]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'sound-current/for-linus'
authorStephen Rothwell <sfr@canb.auug.org.au>
Wed, 4 Nov 2015 22:59:35 +0000 (09:59 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Wed, 4 Nov 2015 22:59:35 +0000 (09:59 +1100)
2809 files changed:
Documentation/DocBook/80211.tmpl
Documentation/IRQ-domain.txt
Documentation/RCU/stallwarn.txt
Documentation/RCU/torture.txt
Documentation/RCU/trace.txt
Documentation/RCU/whatisRCU.txt
Documentation/arm/SA1100/Victor [deleted file]
Documentation/arm/memory.txt
Documentation/arm/uefi.txt
Documentation/arm64/booting.txt
Documentation/atomic_ops.txt
Documentation/devicetree/bindings/arm/gic.txt
Documentation/devicetree/bindings/arm/twd.txt
Documentation/devicetree/bindings/edac/apm-xgene-edac.txt
Documentation/devicetree/bindings/gpio/gpio-msm.txt [deleted file]
Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
Documentation/devicetree/bindings/gpio/gpio-zynq.txt
Documentation/devicetree/bindings/gpio/gpio.txt
Documentation/devicetree/bindings/gpio/netxbig-gpio-ext.txt [new file with mode: 0644]
Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt
Documentation/devicetree/bindings/leds/leds-aat1290.txt
Documentation/devicetree/bindings/leds/leds-bcm6328.txt
Documentation/devicetree/bindings/leds/leds-netxbig.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mmc/fsl-esdhc.txt
Documentation/devicetree/bindings/mmc/mmc.txt
Documentation/devicetree/bindings/mmc/mtk-sd.txt
Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
Documentation/devicetree/bindings/net/apm-xgene-enet.txt
Documentation/devicetree/bindings/net/brcm,iproc-mdio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/can/sun4i_can.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/cpsw.txt
Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/hisilicon-hns-nic.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt
Documentation/devicetree/bindings/net/nfc/st-nci-i2c.txt
Documentation/devicetree/bindings/net/nfc/st-nci-spi.txt
Documentation/devicetree/bindings/net/renesas,ravb.txt
Documentation/devicetree/bindings/net/smsc-lan87xx.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/pci-msi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/atmel,at91-pio4-pinctrl.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pinctrl/berlin,pinctrl.txt
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt
Documentation/devicetree/bindings/pinctrl/fsl,imx7d-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/renesas,pfc-pinctrl.txt
Documentation/devicetree/bindings/rng/samsung,exynos-rng4.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rng/st,rng.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rng/st,stm32-rng.txt [new file with mode: 0644]
Documentation/edac.txt
Documentation/features/vm/THP/arch-support.txt
Documentation/features/vm/pte_special/arch-support.txt
Documentation/filesystems/nfs/nfsroot.txt
Documentation/filesystems/proc.txt
Documentation/gpio/driver.txt
Documentation/hw_random.txt
Documentation/hwmon/lm75
Documentation/hwmon/max31790 [new file with mode: 0644]
Documentation/kernel-parameters.txt
Documentation/locking/lockstat.txt
Documentation/locking/locktorture.txt
Documentation/memory-barriers.txt
Documentation/mmc/mmc-dev-attrs.txt
Documentation/networking/ieee802154.txt
Documentation/networking/ip-sysctl.txt
Documentation/networking/ipvs-sysctl.txt
Documentation/networking/l2tp.txt
Documentation/networking/switchdev.txt
Documentation/networking/vrf.txt
MAINTAINERS
Makefile
arch/alpha/include/asm/atomic.h
arch/arc/Kconfig
arch/arc/boot/dts/axc001.dtsi
arch/arc/boot/dts/axc003.dtsi
arch/arc/boot/dts/axc003_idu.dtsi
arch/arc/boot/dts/nsim_hs.dts
arch/arc/boot/dts/skeleton.dtsi
arch/arc/boot/dts/vdk_axc003.dtsi
arch/arc/boot/dts/vdk_axc003_idu.dtsi
arch/arc/configs/axs101_defconfig
arch/arc/configs/axs103_defconfig
arch/arc/configs/axs103_smp_defconfig
arch/arc/include/asm/arcregs.h
arch/arc/include/asm/atomic.h
arch/arc/include/asm/cache.h
arch/arc/include/asm/cacheflush.h
arch/arc/include/asm/entry-compact.h
arch/arc/include/asm/highmem.h [new file with mode: 0644]
arch/arc/include/asm/hugepage.h [new file with mode: 0644]
arch/arc/include/asm/irq.h
arch/arc/include/asm/irqflags-compact.h
arch/arc/include/asm/kmap_types.h [new file with mode: 0644]
arch/arc/include/asm/mach_desc.h
arch/arc/include/asm/mcip.h
arch/arc/include/asm/mmu.h
arch/arc/include/asm/page.h
arch/arc/include/asm/pgalloc.h
arch/arc/include/asm/pgtable.h
arch/arc/include/asm/processor.h
arch/arc/include/asm/setup.h
arch/arc/include/asm/smp.h
arch/arc/include/asm/tlbflush.h
arch/arc/include/uapi/asm/page.h
arch/arc/kernel/entry-arcv2.S
arch/arc/kernel/entry-compact.S
arch/arc/kernel/head.S
arch/arc/kernel/intc-compact.c
arch/arc/kernel/irq.c
arch/arc/kernel/mcip.c
arch/arc/kernel/setup.c
arch/arc/kernel/smp.c
arch/arc/kernel/time.c
arch/arc/kernel/vmlinux.lds.S
arch/arc/mm/Makefile
arch/arc/mm/cache.c
arch/arc/mm/fault.c
arch/arc/mm/highmem.c [new file with mode: 0644]
arch/arc/mm/init.c
arch/arc/mm/tlb.c
arch/arc/mm/tlbex.S
arch/arc/plat-axs10x/axs10x.c
arch/arc/plat-sim/platform.c
arch/arm/Kconfig
arch/arm/boot/dts/am4372.dtsi
arch/arm/boot/dts/dra7.dtsi
arch/arm/boot/dts/emev2-kzm9d.dts
arch/arm/boot/dts/kirkwood-net5big.dts
arch/arm/boot/dts/kirkwood-netxbig.dtsi
arch/arm/boot/dts/rk3288-veyron-sdmmc.dtsi
arch/arm/boot/dts/rk3288-veyron.dtsi
arch/arm/boot/dts/rk3288.dtsi
arch/arm/boot/dts/sama5d2.dtsi
arch/arm/boot/dts/ste-snowball.dts
arch/arm/boot/dts/stih407-family.dtsi
arch/arm/boot/dts/stm32f429.dtsi
arch/arm/configs/exynos_defconfig
arch/arm/configs/hisi_defconfig
arch/arm/configs/lpc18xx_defconfig
arch/arm/include/asm/arch_gicv3.h [new file with mode: 0644]
arch/arm/include/asm/atomic.h
arch/arm/include/asm/cmpxchg.h
arch/arm/include/asm/irqflags.h
arch/arm/include/asm/mach/arch.h
arch/arm/include/asm/memory.h
arch/arm/include/asm/pgtable.h
arch/arm/include/asm/smp.h
arch/arm/include/asm/unistd.h
arch/arm/kernel/devtree.c
arch/arm/kernel/entry-armv.S
arch/arm/kernel/hw_breakpoint.c
arch/arm/kernel/kgdb.c
arch/arm/kernel/smp.c
arch/arm/kernel/smp_twd.c
arch/arm/kernel/traps.c
arch/arm/lib/clear_user.S
arch/arm/mach-exynos/suspend.c
arch/arm/mach-gemini/board-nas4220b.c
arch/arm/mach-gemini/board-wbd111.c
arch/arm/mach-gemini/board-wbd222.c
arch/arm/mach-imx/gpc.c
arch/arm/mach-mvebu/Kconfig
arch/arm/mach-mvebu/Makefile
arch/arm/mach-mvebu/board.h [deleted file]
arch/arm/mach-mvebu/kirkwood.c
arch/arm/mach-mvebu/netxbig.c [deleted file]
arch/arm/mach-omap2/omap-wakeupgen.c
arch/arm/mm/Kconfig
arch/arm/mm/dma-mapping.c
arch/arm/mm/fault.c
arch/arm/mm/fault.h
arch/arm/mm/mmu.c
arch/arm/net/bpf_jit_32.c
arch/arm/net/bpf_jit_32.h
arch/arm/vdso/vdsomunge.c
arch/arm64/Kconfig
arch/arm64/boot/dts/apm/apm-storm.dtsi
arch/arm64/boot/dts/arm/juno-motherboard.dtsi
arch/arm64/boot/dts/hisilicon/hip05_hns.dtsi [new file with mode: 0644]
arch/arm64/include/asm/acpi.h
arch/arm64/include/asm/arch_gicv3.h [new file with mode: 0644]
arch/arm64/include/asm/atomic.h
arch/arm64/include/asm/cpufeature.h
arch/arm64/include/asm/cputype.h
arch/arm64/include/asm/memory.h
arch/arm64/include/asm/pgtable.h
arch/arm64/kernel/acpi.c
arch/arm64/kernel/armv8_deprecated.c
arch/arm64/kernel/cpu_errata.c
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/efi-stub.c
arch/arm64/kernel/efi.c
arch/arm64/kernel/head.S
arch/arm64/kernel/stacktrace.c
arch/arm64/kernel/suspend.c
arch/arm64/kvm/Kconfig
arch/arm64/mm/proc.S
arch/arm64/net/bpf_jit_comp.c
arch/avr32/boards/atngw100/mrmt.c
arch/avr32/include/asm/atomic.h
arch/c6x/platforms/megamod-pic.c
arch/cris/Kconfig
arch/cris/arch-v10/kernel/head.S
arch/cris/arch-v10/kernel/kgdb.c
arch/cris/arch-v10/mm/init.c
arch/cris/arch-v32/Kconfig
arch/cris/arch-v32/drivers/Kconfig
arch/cris/arch-v32/drivers/Makefile
arch/cris/arch-v32/drivers/axisflashmap.c
arch/cris/arch-v32/drivers/i2c.c [deleted file]
arch/cris/arch-v32/drivers/i2c.h [deleted file]
arch/cris/arch-v32/drivers/mach-a3/Makefile
arch/cris/arch-v32/drivers/mach-a3/gpio.c [deleted file]
arch/cris/arch-v32/drivers/mach-fs/Makefile
arch/cris/arch-v32/drivers/mach-fs/gpio.c [deleted file]
arch/cris/arch-v32/kernel/crisksyms.c
arch/cris/arch-v32/kernel/debugport.c
arch/cris/arch-v32/kernel/head.S
arch/cris/arch-v32/kernel/irq.c
arch/cris/arch-v32/kernel/kgdb.c
arch/cris/arch-v32/kernel/setup.c
arch/cris/arch-v32/mach-a3/Makefile
arch/cris/arch-v32/mach-a3/io.c [deleted file]
arch/cris/arch-v32/mach-fs/Kconfig
arch/cris/arch-v32/mach-fs/Makefile
arch/cris/arch-v32/mach-fs/io.c [deleted file]
arch/cris/boot/dts/artpec3.dtsi [new file with mode: 0644]
arch/cris/boot/dts/dev88.dts
arch/cris/boot/dts/etraxfs.dtsi
arch/cris/boot/dts/include/dt-bindings [new symlink]
arch/cris/boot/dts/p1343.dts [new file with mode: 0644]
arch/cris/boot/rescue/head_v10.S
arch/cris/include/arch-v32/arch/io.h [deleted file]
arch/cris/include/arch-v32/arch/irq.h
arch/cris/include/asm/eshlibld.h
arch/cris/include/asm/io.h
arch/cris/include/uapi/asm/etraxgpio.h
arch/cris/kernel/crisksyms.c
arch/cris/kernel/time.c
arch/frv/include/asm/atomic.h
arch/h8300/include/asm/atomic.h
arch/hexagon/include/asm/atomic.h
arch/ia64/include/asm/atomic.h
arch/ia64/include/asm/unistd.h
arch/ia64/include/uapi/asm/unistd.h
arch/ia64/kernel/entry.S
arch/m32r/include/asm/atomic.h
arch/m68k/include/asm/atomic.h
arch/m68k/mac/psc.c
arch/m68k/sun3/idprom.c
arch/metag/include/asm/atomic_lnkget.h
arch/metag/include/asm/atomic_lock1.h
arch/mips/bcm47xx/Kconfig
arch/mips/cavium-octeon/octeon-irq.c
arch/mips/configs/pistachio_defconfig
arch/mips/include/asm/atomic.h
arch/mips/mti-sead3/Makefile
arch/mips/net/bpf_jit.c
arch/mn10300/include/asm/atomic.h
arch/parisc/include/asm/atomic.h
arch/powerpc/boot/dts/fsl/p1022si-post.dtsi
arch/powerpc/kernel/dma.c
arch/powerpc/net/bpf_jit_comp.c
arch/powerpc/perf/core-book3s.c
arch/powerpc/perf/hv-24x7.c
arch/powerpc/perf/power8-pmu.c
arch/powerpc/platforms/cell/axon_msi.c
arch/powerpc/platforms/cell/spider-pic.c
arch/powerpc/platforms/pasemi/msi.c
arch/powerpc/platforms/powernv/opal-irqchip.c
arch/powerpc/sysdev/ehv_pic.c
arch/powerpc/sysdev/fsl_msi.c
arch/powerpc/sysdev/i8259.c
arch/powerpc/sysdev/ipic.c
arch/powerpc/sysdev/mpic.c
arch/powerpc/sysdev/mpic_msi.c
arch/powerpc/sysdev/qe_lib/qe_ic.c
arch/s390/crypto/sha.h
arch/s390/kernel/perf_cpum_cf.c
arch/s390/net/bpf_jit_comp.c
arch/sh/include/asm/atomic.h
arch/sparc/include/asm/atomic_64.h
arch/sparc/include/asm/topology_64.h
arch/sparc/include/uapi/asm/asi.h
arch/sparc/kernel/iommu.c
arch/sparc/kernel/ldc.c
arch/sparc/kernel/pci_sun4v.c
arch/sparc/kernel/perf_event.c
arch/sparc/kernel/unaligned_64.c
arch/sparc/lib/VISsave.S
arch/sparc/mm/init_64.c
arch/sparc/net/bpf_jit_comp.c
arch/tile/include/asm/atomic.h
arch/tile/include/asm/atomic_64.h
arch/x86/Kconfig
arch/x86/Kconfig.cpu
arch/x86/Kconfig.debug
arch/x86/Makefile
arch/x86/boot/compressed/eboot.c
arch/x86/boot/header.S
arch/x86/crypto/Makefile
arch/x86/crypto/camellia_aesni_avx2_glue.c
arch/x86/crypto/camellia_aesni_avx_glue.c
arch/x86/crypto/cast5_avx_glue.c
arch/x86/crypto/cast6_avx_glue.c
arch/x86/crypto/chacha20_glue.c
arch/x86/crypto/crc32c-pcl-intel-asm_64.S
arch/x86/crypto/poly1305_glue.c
arch/x86/crypto/serpent_avx2_glue.c
arch/x86/crypto/serpent_avx_glue.c
arch/x86/crypto/sha1_ni_asm.S [new file with mode: 0644]
arch/x86/crypto/sha1_ssse3_glue.c
arch/x86/crypto/sha256_ni_asm.S [new file with mode: 0644]
arch/x86/crypto/sha256_ssse3_glue.c
arch/x86/crypto/sha512_ssse3_glue.c
arch/x86/crypto/twofish_avx_glue.c
arch/x86/entry/common.c
arch/x86/entry/entry_32.S
arch/x86/entry/entry_64.S
arch/x86/entry/entry_64_compat.S
arch/x86/entry/syscall_32.c
arch/x86/entry/syscall_64.c
arch/x86/entry/syscalls/syscall_32.tbl
arch/x86/entry/vdso/Makefile
arch/x86/entry/vdso/vdso2c.c
arch/x86/entry/vdso/vdso32-setup.c
arch/x86/entry/vdso/vdso32/int80.S [deleted file]
arch/x86/entry/vdso/vdso32/syscall.S [deleted file]
arch/x86/entry/vdso/vdso32/sysenter.S [deleted file]
arch/x86/entry/vdso/vdso32/system_call.S [new file with mode: 0644]
arch/x86/entry/vdso/vdso32/vclock_gettime.c
arch/x86/entry/vdso/vma.c
arch/x86/entry/vsyscall/vsyscall_64.c
arch/x86/ia32/ia32_signal.c
arch/x86/include/asm/acpi.h
arch/x86/include/asm/amd_nb.h
arch/x86/include/asm/apic.h
arch/x86/include/asm/atomic.h
arch/x86/include/asm/atomic64_64.h
arch/x86/include/asm/cpufeature.h
arch/x86/include/asm/dwarf2.h [new file with mode: 0644]
arch/x86/include/asm/efi.h
arch/x86/include/asm/elf.h
arch/x86/include/asm/fpu/signal.h
arch/x86/include/asm/fpu/types.h
arch/x86/include/asm/fpu/xstate.h
arch/x86/include/asm/hpet.h
arch/x86/include/asm/ia32.h
arch/x86/include/asm/kdebug.h
arch/x86/include/asm/mce.h
arch/x86/include/asm/microcode.h
arch/x86/include/asm/microcode_amd.h
arch/x86/include/asm/microcode_intel.h
arch/x86/include/asm/numachip/numachip.h
arch/x86/include/asm/numachip/numachip_csr.h
arch/x86/include/asm/page_64_types.h
arch/x86/include/asm/page_types.h
arch/x86/include/asm/pgtable.h
arch/x86/include/asm/pgtable_types.h
arch/x86/include/asm/preempt.h
arch/x86/include/asm/processor.h
arch/x86/include/asm/sigcontext.h
arch/x86/include/asm/sigframe.h
arch/x86/include/asm/signal.h
arch/x86/include/asm/switch_to.h
arch/x86/include/asm/syscall.h
arch/x86/include/asm/thread_info.h
arch/x86/include/asm/trace/mpx.h
arch/x86/include/asm/uaccess.h
arch/x86/include/asm/uv/uv_hub.h
arch/x86/include/asm/vdso.h
arch/x86/include/uapi/asm/mce.h
arch/x86/include/uapi/asm/sigcontext.h
arch/x86/include/uapi/asm/sigcontext32.h
arch/x86/kernel/apic/apic.c
arch/x86/kernel/apic/apic_numachip.c
arch/x86/kernel/apic/io_apic.c
arch/x86/kernel/asm-offsets.c
arch/x86/kernel/cpu/Makefile
arch/x86/kernel/cpu/common.c
arch/x86/kernel/cpu/intel_cacheinfo.c
arch/x86/kernel/cpu/mcheck/mce.c
arch/x86/kernel/cpu/mcheck/therm_throt.c
arch/x86/kernel/cpu/microcode/Makefile
arch/x86/kernel/cpu/microcode/amd.c
arch/x86/kernel/cpu/microcode/amd_early.c [deleted file]
arch/x86/kernel/cpu/microcode/core.c
arch/x86/kernel/cpu/microcode/core_early.c [deleted file]
arch/x86/kernel/cpu/microcode/intel.c
arch/x86/kernel/cpu/microcode/intel_early.c [deleted file]
arch/x86/kernel/cpu/microcode/intel_lib.c
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event.h
arch/x86/kernel/cpu/perf_event_intel_bts.c
arch/x86/kernel/cpu/perf_event_intel_cstate.c [new file with mode: 0644]
arch/x86/kernel/cpu/perf_event_intel_ds.c
arch/x86/kernel/cpu/perf_event_intel_lbr.c
arch/x86/kernel/cpu/perf_event_intel_pt.c
arch/x86/kernel/cpu/perf_event_intel_uncore.c
arch/x86/kernel/cpu/perf_event_intel_uncore.h
arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c
arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c
arch/x86/kernel/crash.c
arch/x86/kernel/e820.c
arch/x86/kernel/early-quirks.c
arch/x86/kernel/early_printk.c
arch/x86/kernel/fpu/init.c
arch/x86/kernel/fpu/regset.c
arch/x86/kernel/fpu/signal.c
arch/x86/kernel/fpu/xstate.c
arch/x86/kernel/head_32.S
arch/x86/kernel/hpet.c
arch/x86/kernel/irq_64.c
arch/x86/kernel/kgdb.c
arch/x86/kernel/pci-dma.c
arch/x86/kernel/process.c
arch/x86/kernel/process_32.c
arch/x86/kernel/process_64.c
arch/x86/kernel/quirks.c
arch/x86/kernel/setup.c
arch/x86/kernel/signal.c
arch/x86/kernel/traps.c
arch/x86/kernel/tsc.c
arch/x86/kvm/cpuid.c
arch/x86/kvm/x86.c
arch/x86/kvm/x86.h
arch/x86/lib/x86-opcode-map.txt
arch/x86/math-emu/fpu_aux.c
arch/x86/math-emu/fpu_emu.h
arch/x86/math-emu/fpu_entry.c
arch/x86/math-emu/fpu_proto.h
arch/x86/math-emu/load_store.c
arch/x86/math-emu/reg_compare.c
arch/x86/mm/Makefile
arch/x86/mm/dump_pagetables.c
arch/x86/mm/gup.c
arch/x86/mm/init.c
arch/x86/mm/init_32.c
arch/x86/mm/init_64.c
arch/x86/mm/mpx.c
arch/x86/mm/pageattr.c
arch/x86/net/bpf_jit_comp.c
arch/x86/platform/efi/efi-bgrt.c
arch/x86/platform/efi/efi.c
arch/x86/platform/intel-mid/intel-mid.c
arch/x86/platform/intel-mid/sfi.c
arch/x86/platform/uv/uv_nmi.c
arch/x86/ras/Kconfig
arch/x86/ras/mce_amd_inj.c
arch/x86/um/asm/syscall.h
arch/x86/um/sys_call_table_32.c
arch/x86/um/sys_call_table_64.c
arch/x86/xen/setup.c
arch/xtensa/include/asm/atomic.h
block/blk-lib.c
crypto/Kconfig
crypto/Makefile
crypto/ablkcipher.c
crypto/akcipher.c
crypto/algapi.c
crypto/api.c
crypto/asymmetric_keys/pkcs7_verify.c
crypto/asymmetric_keys/x509_cert_parser.c
crypto/asymmetric_keys/x509_public_key.c
crypto/crypto_user.c
crypto/jitterentropy-kcapi.c
crypto/keywrap.c [new file with mode: 0644]
crypto/rsa.c
crypto/rsa_helper.c
crypto/rsakey.asn1 [deleted file]
crypto/rsaprivkey.asn1 [new file with mode: 0644]
crypto/rsapubkey.asn1 [new file with mode: 0644]
crypto/skcipher.c
crypto/tcrypt.c
crypto/testmgr.c
crypto/testmgr.h
drivers/acpi/apei/ghes.c
drivers/acpi/gsi.c
drivers/atm/iphase.c
drivers/base/dd.c
drivers/base/pinctrl.c
drivers/base/platform-msi.c
drivers/base/regmap/internal.h
drivers/base/regmap/regmap-debugfs.c
drivers/base/regmap/regmap-irq.c
drivers/base/regmap/regmap.c
drivers/bcma/main.c
drivers/block/rbd.c
drivers/bluetooth/Kconfig
drivers/bluetooth/ath3k.c
drivers/bluetooth/bfusb.c
drivers/bluetooth/bluecard_cs.c
drivers/bluetooth/bpa10x.c
drivers/bluetooth/bt3c_cs.c
drivers/bluetooth/btbcm.c
drivers/bluetooth/btintel.c
drivers/bluetooth/btintel.h
drivers/bluetooth/btmrvl_main.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btsdio.c
drivers/bluetooth/btuart_cs.c
drivers/bluetooth/btusb.c
drivers/bluetooth/btwilink.c
drivers/bluetooth/dtl1_cs.c
drivers/bluetooth/hci_ath.c
drivers/bluetooth/hci_bcm.c
drivers/bluetooth/hci_h4.c
drivers/bluetooth/hci_h5.c
drivers/bluetooth/hci_intel.c
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_qca.c
drivers/bluetooth/hci_uart.h
drivers/bluetooth/hci_vhci.c
drivers/char/hw_random/Kconfig
drivers/char/hw_random/Makefile
drivers/char/hw_random/core.c
drivers/char/hw_random/exynos-rng.c
drivers/char/hw_random/mxc-rnga.c
drivers/char/hw_random/octeon-rng.c
drivers/char/hw_random/pasemi-rng.c
drivers/char/hw_random/ppc4xx-rng.c
drivers/char/hw_random/st-rng.c [new file with mode: 0644]
drivers/char/hw_random/stm32-rng.c [new file with mode: 0644]
drivers/clk/clkdev.c
drivers/clk/rockchip/clk-mmc-phase.c
drivers/clocksource/Kconfig
drivers/clocksource/Makefile
drivers/clocksource/arm_global_timer.c
drivers/clocksource/em_sti.c
drivers/clocksource/exynos_mct.c
drivers/clocksource/fsl_ftm_timer.c
drivers/clocksource/h8300_timer16.c
drivers/clocksource/h8300_timer8.c
drivers/clocksource/h8300_tpu.c
drivers/clocksource/mtk_timer.c
drivers/clocksource/numachip.c [new file with mode: 0644]
drivers/clocksource/samsung_pwm_timer.c
drivers/clocksource/sh_cmt.c
drivers/clocksource/sh_mtu2.c
drivers/clocksource/tango_xtal.c [new file with mode: 0644]
drivers/clocksource/time-armada-370-xp.c
drivers/clocksource/time-pistachio.c
drivers/clocksource/timer-digicolor.c
drivers/clocksource/timer-imx-gpt.c
drivers/clocksource/timer-prima2.c
drivers/clocksource/vf_pit_timer.c
drivers/crypto/Kconfig
drivers/crypto/amcc/crypto4xx_core.c
drivers/crypto/atmel-aes.c
drivers/crypto/atmel-sha.c
drivers/crypto/atmel-tdes.c
drivers/crypto/bfin_crc.c
drivers/crypto/caam/caamalg.c
drivers/crypto/caam/caamhash.c
drivers/crypto/caam/desc.h
drivers/crypto/caam/sg_sw_sec4.h
drivers/crypto/ccp/Kconfig
drivers/crypto/ccp/ccp-crypto-aes-cmac.c
drivers/crypto/ccp/ccp-crypto-main.c
drivers/crypto/ccp/ccp-crypto-sha.c
drivers/crypto/ccp/ccp-ops.c
drivers/crypto/ccp/ccp-pci.c
drivers/crypto/ccp/ccp-platform.c
drivers/crypto/marvell/cesa.h
drivers/crypto/marvell/cipher.c
drivers/crypto/marvell/hash.c
drivers/crypto/marvell/tdma.c
drivers/crypto/n2_core.c
drivers/crypto/nx/nx-842-powernv.c
drivers/crypto/nx/nx-842-pseries.c
drivers/crypto/picoxcell_crypto.c
drivers/crypto/qat/qat_common/Makefile
drivers/crypto/qat/qat_common/adf_common_drv.h
drivers/crypto/qat/qat_common/adf_ctl_drv.c
drivers/crypto/qat/qat_common/adf_init.c
drivers/crypto/qat/qat_common/adf_sriov.c
drivers/crypto/qat/qat_common/qat_algs.c
drivers/crypto/qat/qat_common/qat_asym_algs.c
drivers/crypto/qat/qat_common/qat_crypto.c
drivers/crypto/qat/qat_common/qat_hal.c
drivers/crypto/qat/qat_common/qat_rsakey.asn1 [deleted file]
drivers/crypto/qat/qat_common/qat_rsaprivkey.asn1 [new file with mode: 0644]
drivers/crypto/qat/qat_common/qat_rsapubkey.asn1 [new file with mode: 0644]
drivers/crypto/qce/ablkcipher.c
drivers/crypto/qce/cipher.h
drivers/crypto/qce/dma.c
drivers/crypto/qce/dma.h
drivers/crypto/qce/sha.c
drivers/crypto/qce/sha.h
drivers/crypto/sahara.c
drivers/crypto/talitos.c
drivers/crypto/ux500/cryp/cryp_core.c
drivers/crypto/ux500/hash/hash_core.c
drivers/edac/Makefile
drivers/edac/altera_edac.c
drivers/edac/altera_edac.h
drivers/edac/amd64_edac.c
drivers/edac/amd64_edac.h
drivers/edac/debugfs.c [new file with mode: 0644]
drivers/edac/edac_core.h
drivers/edac/edac_mc.c
drivers/edac/edac_mc_sysfs.c
drivers/edac/edac_module.h
drivers/edac/ghes_edac.c
drivers/edac/i5100_edac.c
drivers/edac/ppc4xx_edac.c
drivers/edac/sb_edac.c
drivers/edac/xgene_edac.c
drivers/firmware/efi/Kconfig
drivers/firmware/efi/Makefile
drivers/firmware/efi/efi-pstore.c
drivers/firmware/efi/efi.c
drivers/firmware/efi/esrt.c
drivers/firmware/efi/fake_mem.c [new file with mode: 0644]
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-104-idio-16.c [new file with mode: 0644]
drivers/gpio/gpio-altera.c
drivers/gpio/gpio-amdpt.c [new file with mode: 0644]
drivers/gpio/gpio-arizona.c
drivers/gpio/gpio-ath79.c
drivers/gpio/gpio-etraxfs.c
drivers/gpio/gpio-generic.c
drivers/gpio/gpio-it87.c [new file with mode: 0644]
drivers/gpio/gpio-it8761e.c [deleted file]
drivers/gpio/gpio-lpc18xx.c
drivers/gpio/gpio-max730x.c
drivers/gpio/gpio-moxart.c
drivers/gpio/gpio-msm-v2.c [deleted file]
drivers/gpio/gpio-mvebu.c
drivers/gpio/gpio-omap.c
drivers/gpio/gpio-pca953x.c
drivers/gpio/gpio-pl061.c
drivers/gpio/gpio-sodaville.c
drivers/gpio/gpio-sx150x.c
drivers/gpio/gpio-tb10x.c
drivers/gpio/gpio-tz1090-pdc.c
drivers/gpio/gpio-vf610.c
drivers/gpio/gpio-xlp.c
drivers/gpio/gpio-zx.c
drivers/gpio/gpio-zynq.c
drivers/gpio/gpiolib-acpi.c
drivers/gpio/gpiolib-legacy.c
drivers/gpio/gpiolib-of.c
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h
drivers/gpu/drm/amd/amdgpu/amdgpu.h
drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/radeon/atombios_encoders.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_encoders.c
drivers/gpu/drm/radeon/radeon_legacy_encoders.c
drivers/gpu/drm/radeon/radeon_pm.c
drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/abx500.c
drivers/hwmon/coretemp.c
drivers/hwmon/fam15h_power.c
drivers/hwmon/ibmpowernv.c
drivers/hwmon/ina2xx.c
drivers/hwmon/lm75.c
drivers/hwmon/max31790.c [new file with mode: 0644]
drivers/hwmon/nct6775.c
drivers/i2c/busses/i2c-mv64xxx.c
drivers/i2c/busses/i2c-pnx.c
drivers/ide/pdc202xx_new.c
drivers/infiniband/hw/nes/nes_nic.c
drivers/input/mouse/alps.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/lpc32xx_ts.c
drivers/iommu/amd_iommu.c
drivers/iommu/amd_iommu_types.h
drivers/iommu/amd_iommu_v2.c
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/alphascale_asm9260-icoll.h [new file with mode: 0644]
drivers/irqchip/exynos-combiner.c
drivers/irqchip/irq-armada-370-xp.c
drivers/irqchip/irq-atmel-aic-common.c
drivers/irqchip/irq-atmel-aic5.c
drivers/irqchip/irq-crossbar.c
drivers/irqchip/irq-gic-common.c
drivers/irqchip/irq-gic-common.h
drivers/irqchip/irq-gic-v2m.c
drivers/irqchip/irq-gic-v3-its-pci-msi.c
drivers/irqchip/irq-gic-v3-its-platform-msi.c
drivers/irqchip/irq-gic-v3-its.c
drivers/irqchip/irq-gic-v3.c
drivers/irqchip/irq-gic.c
drivers/irqchip/irq-hip04.c
drivers/irqchip/irq-i8259.c
drivers/irqchip/irq-imx-gpcv2.c
drivers/irqchip/irq-mtk-sysirq.c
drivers/irqchip/irq-mxs.c
drivers/irqchip/irq-nvic.c
drivers/irqchip/irq-renesas-intc-irqpin.c
drivers/irqchip/irq-renesas-irqc.c
drivers/irqchip/irq-s3c24xx.c
drivers/irqchip/irq-sunxi-nmi.c
drivers/irqchip/irq-tegra.c
drivers/irqchip/irq-vf610-mscm-ir.c
drivers/isdn/hisax/hfc4s8s_l1.c
drivers/isdn/hisax/isdnl2.c
drivers/isdn/i4l/isdn_ppp.c
drivers/isdn/mISDN/dsp_pipeline.c
drivers/isdn/mISDN/layer2.c
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/led-class.c
drivers/leds/led-core.c
drivers/leds/leds-88pm860x.c
drivers/leds/leds-bcm6328.c
drivers/leds/leds-bcm6358.c
drivers/leds/leds-cobalt-qube.c
drivers/leds/leds-gpio.c
drivers/leds/leds-hp6xx.c
drivers/leds/leds-ipaq-micro.c
drivers/leds/leds-locomo.c
drivers/leds/leds-menf21bmc.c
drivers/leds/leds-net48xx.c
drivers/leds/leds-netxbig.c
drivers/leds/leds-ot200.c
drivers/leds/leds-powernv.c
drivers/leds/leds-sead3.c [moved from arch/mips/mti-sead3/leds-sead3.c with 99% similarity]
drivers/leds/leds-wrap.c
drivers/leds/leds.h
drivers/leds/trigger/ledtrig-heartbeat.c
drivers/md/md.c
drivers/md/raid1.c
drivers/md/raid10.c
drivers/md/raid5.c
drivers/mmc/card/block.c
drivers/mmc/core/Kconfig
drivers/mmc/core/core.c
drivers/mmc/core/core.h
drivers/mmc/core/debugfs.c
drivers/mmc/core/host.c
drivers/mmc/core/mmc.c
drivers/mmc/core/mmc_ops.c
drivers/mmc/core/mmc_ops.h
drivers/mmc/core/pwrseq_emmc.c
drivers/mmc/core/pwrseq_simple.c
drivers/mmc/core/quirks.c
drivers/mmc/core/sd.c
drivers/mmc/core/sdio.c
drivers/mmc/core/sdio_irq.c
drivers/mmc/core/sdio_ops.h
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/dw_mmc-exynos.c
drivers/mmc/host/dw_mmc-pltfm.c
drivers/mmc/host/dw_mmc-rockchip.c
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/dw_mmc.h
drivers/mmc/host/mmc_spi.c
drivers/mmc/host/moxart-mmc.c
drivers/mmc/host/mtk-sd.c
drivers/mmc/host/omap.c
drivers/mmc/host/sdhci-acpi.c
drivers/mmc/host/sdhci-bcm-kona.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-esdhc.h
drivers/mmc/host/sdhci-msm.c
drivers/mmc/host/sdhci-of-at91.c
drivers/mmc/host/sdhci-of-esdhc.c
drivers/mmc/host/sdhci-pci-core.c [moved from drivers/mmc/host/sdhci-pci.c with 96% similarity]
drivers/mmc/host/sdhci-pci-o2micro.c
drivers/mmc/host/sdhci-pci-o2micro.h
drivers/mmc/host/sdhci-pci.h
drivers/mmc/host/sdhci-pltfm.c
drivers/mmc/host/sdhci-sirf.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sunxi-mmc.c
drivers/mmc/host/vub300.c
drivers/mmc/host/wbsd.c
drivers/net/Kconfig
drivers/net/arcnet/Kconfig
drivers/net/arcnet/arc-rawmode.c
drivers/net/arcnet/arc-rimi.c
drivers/net/arcnet/arcdevice.h [moved from include/linux/arcdevice.h with 75% similarity]
drivers/net/arcnet/arcnet.c
drivers/net/arcnet/capmode.c
drivers/net/arcnet/com20020-isa.c
drivers/net/arcnet/com20020-pci.c
drivers/net/arcnet/com20020.c
drivers/net/arcnet/com20020.h [moved from include/linux/com20020.h with 61% similarity]
drivers/net/arcnet/com20020_cs.c
drivers/net/arcnet/com9026.h [new file with mode: 0644]
drivers/net/arcnet/com90io.c
drivers/net/arcnet/com90xx.c
drivers/net/arcnet/rfc1051.c
drivers/net/arcnet/rfc1201.c
drivers/net/bonding/bond_3ad.c
drivers/net/bonding/bond_main.c
drivers/net/can/Kconfig
drivers/net/can/Makefile
drivers/net/can/at91_can.c
drivers/net/can/dev.c
drivers/net/can/flexcan.c
drivers/net/can/sun4i_can.c [new file with mode: 0644]
drivers/net/dsa/bcm_sf2.c
drivers/net/dsa/bcm_sf2.h
drivers/net/dsa/bcm_sf2_regs.h
drivers/net/dsa/mv88e6060.c
drivers/net/dsa/mv88e6123_61_65.c
drivers/net/dsa/mv88e6131.c
drivers/net/dsa/mv88e6171.c
drivers/net/dsa/mv88e6352.c
drivers/net/dsa/mv88e6xxx.c
drivers/net/dsa/mv88e6xxx.h
drivers/net/dummy.c
drivers/net/ethernet/8390/Kconfig
drivers/net/ethernet/8390/mac8390.c
drivers/net/ethernet/aeroflex/greth.c
drivers/net/ethernet/allwinner/sun4i-emac.c
drivers/net/ethernet/amd/7990.c
drivers/net/ethernet/amd/Kconfig
drivers/net/ethernet/amd/au1000_eth.c
drivers/net/ethernet/amd/xgbe/xgbe-dev.c
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
drivers/net/ethernet/amd/xgbe/xgbe-main.c
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
drivers/net/ethernet/amd/xgbe/xgbe.h
drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
drivers/net/ethernet/apm/xgene/xgene_enet_main.c
drivers/net/ethernet/apm/xgene/xgene_enet_main.h
drivers/net/ethernet/apple/Kconfig
drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c
drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c
drivers/net/ethernet/atheros/atlx/atl1.c
drivers/net/ethernet/atheros/atlx/atl2.c
drivers/net/ethernet/broadcom/Kconfig
drivers/net/ethernet/broadcom/Makefile
drivers/net/ethernet/broadcom/bcm63xx_enet.c
drivers/net/ethernet/broadcom/bcmsysport.c
drivers/net/ethernet/broadcom/bnx2.c
drivers/net/ethernet/broadcom/bnx2.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnxt/Makefile [new file with mode: 0644]
drivers/net/ethernet/broadcom/bnxt/bnxt.c [new file with mode: 0644]
drivers/net/ethernet/broadcom/bnxt/bnxt.h [new file with mode: 0644]
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c [new file with mode: 0644]
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h [new file with mode: 0644]
drivers/net/ethernet/broadcom/bnxt/bnxt_fw_hdr.h [new file with mode: 0644]
drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h [new file with mode: 0644]
drivers/net/ethernet/broadcom/bnxt/bnxt_nvm_defs.h [new file with mode: 0644]
drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c [new file with mode: 0644]
drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h [new file with mode: 0644]
drivers/net/ethernet/broadcom/genet/bcmgenet.c
drivers/net/ethernet/broadcom/genet/bcmgenet.h
drivers/net/ethernet/broadcom/genet/bcmmii.c
drivers/net/ethernet/cavium/Kconfig
drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
drivers/net/ethernet/cavium/thunder/nic_main.c
drivers/net/ethernet/cavium/thunder/nic_reg.h
drivers/net/ethernet/cavium/thunder/nicvf_main.c
drivers/net/ethernet/cavium/thunder/thunder_bgx.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
drivers/net/ethernet/cisco/enic/enic.h
drivers/net/ethernet/cisco/enic/enic_main.c
drivers/net/ethernet/cisco/enic/vnic_dev.c
drivers/net/ethernet/cisco/enic/vnic_dev.h
drivers/net/ethernet/dec/tulip/de2104x.c
drivers/net/ethernet/emulex/benet/be_ethtool.c
drivers/net/ethernet/ethoc.c
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/freescale/fec_ptp.c
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/freescale/gianfar.h
drivers/net/ethernet/freescale/gianfar_ethtool.c
drivers/net/ethernet/freescale/ucc_geth_ethtool.c
drivers/net/ethernet/hisilicon/Kconfig
drivers/net/ethernet/hisilicon/Makefile
drivers/net/ethernet/hisilicon/hip04_mdio.c [deleted file]
drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
drivers/net/ethernet/hisilicon/hns/Makefile [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hnae.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hnae.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_enet.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_enet.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns/hns_ethtool.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns_mdio.c [new file with mode: 0644]
drivers/net/ethernet/ibm/emac/core.c
drivers/net/ethernet/ibm/emac/core.h
drivers/net/ethernet/intel/e1000/e1000_ethtool.c
drivers/net/ethernet/intel/e1000/e1000_hw.c
drivers/net/ethernet/intel/e1000/e1000_main.c
drivers/net/ethernet/intel/e1000e/ethtool.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/fm10k/fm10k.h
drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
drivers/net/ethernet/intel/fm10k/fm10k_iov.c
drivers/net/ethernet/intel/fm10k/fm10k_main.c
drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
drivers/net/ethernet/intel/fm10k/fm10k_pci.c
drivers/net/ethernet/intel/fm10k/fm10k_pf.c
drivers/net/ethernet/intel/fm10k/fm10k_type.h
drivers/net/ethernet/intel/fm10k/fm10k_vf.c
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_adminq.c
drivers/net/ethernet/intel/i40e/i40e_adminq.h
drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
drivers/net/ethernet/intel/i40e/i40e_common.c
drivers/net/ethernet/intel/i40e/i40e_dcb.c
drivers/net/ethernet/intel/i40e/i40e_dcb.h
drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c
drivers/net/ethernet/intel/i40e/i40e_debugfs.c
drivers/net/ethernet/intel/i40e/i40e_devids.h [new file with mode: 0644]
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_fcoe.c
drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_nvm.c
drivers/net/ethernet/intel/i40e/i40e_prototype.h
drivers/net/ethernet/intel/i40e/i40e_ptp.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40e/i40e_txrx.h
drivers/net/ethernet/intel/i40e/i40e_type.h
drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
drivers/net/ethernet/intel/i40evf/i40e_adminq.c
drivers/net/ethernet/intel/i40evf/i40e_adminq.h
drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
drivers/net/ethernet/intel/i40evf/i40e_common.c
drivers/net/ethernet/intel/i40evf/i40e_devids.h [new file with mode: 0644]
drivers/net/ethernet/intel/i40evf/i40e_prototype.h
drivers/net/ethernet/intel/i40evf/i40e_txrx.c
drivers/net/ethernet/intel/i40evf/i40e_txrx.h
drivers/net/ethernet/intel/i40evf/i40e_type.h
drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
drivers/net/ethernet/intel/i40evf/i40evf.h
drivers/net/ethernet/intel/i40evf/i40evf_main.c
drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
drivers/net/ethernet/intel/igb/igb.h
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igb/igb_ptp.c
drivers/net/ethernet/intel/igbvf/ethtool.c
drivers/net/ethernet/intel/igbvf/netdev.c
drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
drivers/net/ethernet/intel/ixgbevf/mbx.h
drivers/net/ethernet/intel/ixgbevf/vf.c
drivers/net/ethernet/intel/ixgbevf/vf.h
drivers/net/ethernet/marvell/mv643xx_eth.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/sky2.c
drivers/net/ethernet/mellanox/mlx4/cmd.c
drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
drivers/net/ethernet/mellanox/mlx4/en_netdev.c
drivers/net/ethernet/mellanox/mlx4/en_tx.c
drivers/net/ethernet/mellanox/mlx4/eq.c
drivers/net/ethernet/mellanox/mlx4/fw.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx4/mlx4.h
drivers/net/ethernet/mellanox/mlx4/mr.c
drivers/net/ethernet/mellanox/mlx4/qp.c
drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
drivers/net/ethernet/mellanox/mlx5/core/cmd.c
drivers/net/ethernet/mellanox/mlx5/core/cq.c
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
drivers/net/ethernet/mellanox/mlx5/core/eq.c
drivers/net/ethernet/mellanox/mlx5/core/health.c
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
drivers/net/ethernet/mellanox/mlx5/core/mr.c
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/mellanox/mlx5/core/port.c
drivers/net/ethernet/mellanox/mlx5/core/qp.c
drivers/net/ethernet/mellanox/mlx5/core/srq.c
drivers/net/ethernet/mellanox/mlx5/core/transobj.c
drivers/net/ethernet/mellanox/mlxsw/Kconfig
drivers/net/ethernet/mellanox/mlxsw/Makefile
drivers/net/ethernet/mellanox/mlxsw/cmd.h
drivers/net/ethernet/mellanox/mlxsw/core.c
drivers/net/ethernet/mellanox/mlxsw/core.h
drivers/net/ethernet/mellanox/mlxsw/item.h
drivers/net/ethernet/mellanox/mlxsw/pci.c
drivers/net/ethernet/mellanox/mlxsw/pci.h
drivers/net/ethernet/mellanox/mlxsw/reg.h
drivers/net/ethernet/mellanox/mlxsw/spectrum.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxsw/spectrum.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxsw/switchx2.c
drivers/net/ethernet/mellanox/mlxsw/txheader.h
drivers/net/ethernet/microchip/Kconfig
drivers/net/ethernet/microchip/Makefile
drivers/net/ethernet/microchip/encx24j600-regmap.c [new file with mode: 0644]
drivers/net/ethernet/microchip/encx24j600.c [new file with mode: 0644]
drivers/net/ethernet/microchip/encx24j600_hw.h [new file with mode: 0644]
drivers/net/ethernet/neterion/s2io.c
drivers/net/ethernet/neterion/vxge/vxge-ethtool.c
drivers/net/ethernet/nvidia/forcedeth.c
drivers/net/ethernet/octeon/octeon_mgmt.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c
drivers/net/ethernet/qlogic/Kconfig
drivers/net/ethernet/qlogic/Makefile
drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c
drivers/net/ethernet/qlogic/qed/Makefile [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_cxt.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_cxt.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_dev.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_dev_api.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_hsi.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_hw.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_hw.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_init_ops.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_init_ops.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_int.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_int.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_l2.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_main.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_mcp.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_mcp.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_reg_addr.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_sp.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_sp_commands.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_spq.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qede/Makefile [new file with mode: 0644]
drivers/net/ethernet/qlogic/qede/qede.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qede/qede_ethtool.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qede/qede_main.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qla3xxx.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c
drivers/net/ethernet/realtek/8139cp.c
drivers/net/ethernet/realtek/8139too.c
drivers/net/ethernet/renesas/ravb.h
drivers/net/ethernet/renesas/ravb_main.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/renesas/sh_eth.h
drivers/net/ethernet/rocker/rocker.c
drivers/net/ethernet/sfc/ef10.c
drivers/net/ethernet/sfc/efx.c
drivers/net/ethernet/sfc/farch.c
drivers/net/ethernet/sfc/mcdi.c
drivers/net/ethernet/sfc/net_driver.h
drivers/net/ethernet/sfc/ptp.c
drivers/net/ethernet/sfc/tx.c
drivers/net/ethernet/smsc/smc91x.c
drivers/net/ethernet/smsc/smc91x.h
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/sun/cassini.c
drivers/net/ethernet/tehuti/tehuti.c
drivers/net/ethernet/ti/cpmac.c
drivers/net/ethernet/ti/cpsw-common.c
drivers/net/ethernet/ti/cpsw-phy-sel.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpsw.h
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/ethernet/ti/netcp_ethss.c
drivers/net/ethernet/ti/tlan.c
drivers/net/ethernet/xilinx/ll_temac_mdio.c
drivers/net/ethernet/xilinx/xilinx_axienet_main.c
drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
drivers/net/fjes/fjes_ethtool.c
drivers/net/geneve.c
drivers/net/ieee802154/Kconfig
drivers/net/ieee802154/at86rf230.c
drivers/net/ieee802154/at86rf230.h
drivers/net/ieee802154/atusb.c
drivers/net/ieee802154/mrf24j40.c
drivers/net/ipvlan/ipvlan_core.c
drivers/net/irda/pxaficp_ir.c
drivers/net/macvlan.c
drivers/net/macvtap.c
drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/aquantia.c
drivers/net/phy/bcm-cygnus.c [new file with mode: 0644]
drivers/net/phy/bcm-phy-lib.c [new file with mode: 0644]
drivers/net/phy/bcm-phy-lib.h [new file with mode: 0644]
drivers/net/phy/bcm63xx.c
drivers/net/phy/bcm7xxx.c
drivers/net/phy/broadcom.c
drivers/net/phy/dp83640.c
drivers/net/phy/dp83848.c [new file with mode: 0644]
drivers/net/phy/mdio-bcm-iproc.c [new file with mode: 0644]
drivers/net/phy/mdio-gpio.c
drivers/net/phy/mdio-mux-mmioreg.c
drivers/net/phy/mdio-mux.c
drivers/net/phy/mdio_bus.c
drivers/net/phy/micrel.c
drivers/net/phy/phy_device.c
drivers/net/phy/smsc.c
drivers/net/phy/teranetics.c
drivers/net/ppp/ppp_generic.c
drivers/net/ppp/pppoe.c
drivers/net/ppp/pptp.c
drivers/net/slip/slhc.c
drivers/net/slip/slip.c
drivers/net/tun.c
drivers/net/usb/Kconfig
drivers/net/usb/asix.h
drivers/net/usb/asix_common.c
drivers/net/usb/cdc-phonet.c
drivers/net/usb/cdc_ether.c
drivers/net/usb/cdc_mbim.c
drivers/net/usb/cdc_ncm.c
drivers/net/usb/dm9601.c
drivers/net/usb/lan78xx.c
drivers/net/usb/lan78xx.h
drivers/net/usb/mcs7830.c
drivers/net/usb/qmi_wwan.c
drivers/net/usb/smsc75xx.c
drivers/net/usb/smsc95xx.c
drivers/net/usb/sr9800.c
drivers/net/usb/usbnet.c
drivers/net/vmxnet3/vmxnet3_ethtool.c
drivers/net/vmxnet3/vmxnet3_int.h
drivers/net/vrf.c
drivers/net/vxlan.c
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/airo.c
drivers/net/wireless/ath/ath10k/bmi.h
drivers/net/wireless/ath/ath10k/ce.c
drivers/net/wireless/ath/ath10k/ce.h
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/debug.h
drivers/net/wireless/ath/ath10k/hif.h
drivers/net/wireless/ath/ath10k/htc.c
drivers/net/wireless/ath/ath10k/htc.h
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/htt_tx.c
drivers/net/wireless/ath/ath10k/hw.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/pci.c
drivers/net/wireless/ath/ath10k/pci.h
drivers/net/wireless/ath/ath10k/targaddrs.h
drivers/net/wireless/ath/ath10k/testmode.c
drivers/net/wireless/ath/ath10k/thermal.c
drivers/net/wireless/ath/ath10k/txrx.c
drivers/net/wireless/ath/ath10k/wmi-ops.h
drivers/net/wireless/ath/ath10k/wmi-tlv.c
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/core.h
drivers/net/wireless/ath/ath6kl/debug.c
drivers/net/wireless/ath/ath6kl/debug.h
drivers/net/wireless/ath/ath6kl/htc_mbox.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath9k/ar9002_phy.h
drivers/net/wireless/ath/ath9k/ar9003_calib.c
drivers/net/wireless/ath/ath9k/ar9003_hw.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/common-debug.c
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/hif_usb.c
drivers/net/wireless/ath/ath9k/hif_usb.h
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/ath/carl9170/rx.c
drivers/net/wireless/ath/dfs_pattern_detector.c
drivers/net/wireless/ath/dfs_pattern_detector.h
drivers/net/wireless/ath/dfs_pri_detector.c
drivers/net/wireless/ath/wcn36xx/dxe.c
drivers/net/wireless/ath/wcn36xx/dxe.h
drivers/net/wireless/ath/wcn36xx/main.c
drivers/net/wireless/ath/wil6210/Kconfig
drivers/net/wireless/ath/wil6210/Makefile
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/interrupt.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/pcie_bus.c
drivers/net/wireless/ath/wil6210/pmc.c
drivers/net/wireless/ath/wil6210/rx_reorder.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/txrx.h
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wil_crash_dump.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/b43/Kconfig
drivers/net/wireless/b43/Makefile
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/pcmcia.c [deleted file]
drivers/net/wireless/b43/pcmcia.h [deleted file]
drivers/net/wireless/brcm80211/Kconfig
drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
drivers/net/wireless/brcm80211/brcmfmac/bus.h
drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
drivers/net/wireless/brcm80211/brcmfmac/chip.c
drivers/net/wireless/brcm80211/brcmfmac/chip.h
drivers/net/wireless/brcm80211/brcmfmac/common.h
drivers/net/wireless/brcm80211/brcmfmac/core.c
drivers/net/wireless/brcm80211/brcmfmac/core.h
drivers/net/wireless/brcm80211/brcmfmac/debug.c
drivers/net/wireless/brcm80211/brcmfmac/debug.h
drivers/net/wireless/brcm80211/brcmfmac/feature.c
drivers/net/wireless/brcm80211/brcmfmac/firmware.c
drivers/net/wireless/brcm80211/brcmfmac/flowring.c
drivers/net/wireless/brcm80211/brcmfmac/flowring.h
drivers/net/wireless/brcm80211/brcmfmac/fweh.c
drivers/net/wireless/brcm80211/brcmfmac/fweh.h
drivers/net/wireless/brcm80211/brcmfmac/fwil.h
drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
drivers/net/wireless/brcm80211/brcmfmac/p2p.c
drivers/net/wireless/brcm80211/brcmfmac/p2p.h
drivers/net/wireless/brcm80211/brcmfmac/pcie.c
drivers/net/wireless/brcm80211/brcmfmac/proto.h
drivers/net/wireless/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
drivers/net/wireless/cw1200/sta.c
drivers/net/wireless/cw1200/sta.h
drivers/net/wireless/ipw2x00/ipw2200.c
drivers/net/wireless/ipw2x00/libipw_rx.c
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/4965.h
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/iwl-8000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-io.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/iwl-trans.c
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/constants.h
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/nvm.c
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/tof.c
drivers/net/wireless/iwlwifi/mvm/tof.h
drivers/net/wireless/iwlwifi/mvm/tt.c
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mediatek/mt7601u/main.c
drivers/net/wireless/mwifiex/11n_aggr.c
drivers/net/wireless/mwifiex/11n_rxreorder.c
drivers/net/wireless/mwifiex/Kconfig
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/debugfs.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/scan.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/sta_tx.c
drivers/net/wireless/mwifiex/tdls.c
drivers/net/wireless/mwifiex/txrx.c
drivers/net/wireless/mwifiex/uap_cmd.c
drivers/net/wireless/mwifiex/uap_event.c
drivers/net/wireless/mwifiex/uap_txrx.c
drivers/net/wireless/mwifiex/usb.c
drivers/net/wireless/mwifiex/usb.h
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/orinoco/cfg.c
drivers/net/wireless/orinoco/orinoco_usb.c
drivers/net/wireless/realtek/Makefile [new file with mode: 0644]
drivers/net/wireless/realtek/rtl818x/Kconfig [moved from drivers/net/wireless/rtl818x/Kconfig with 100% similarity]
drivers/net/wireless/realtek/rtl818x/Makefile [moved from drivers/net/wireless/rtl818x/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/Makefile [moved from drivers/net/wireless/rtl818x/rtl8180/Makefile with 69% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c [moved from drivers/net/wireless/rtl818x/rtl8180/dev.c with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/grf5101.c [moved from drivers/net/wireless/rtl818x/rtl8180/grf5101.c with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/grf5101.h [moved from drivers/net/wireless/rtl818x/rtl8180/grf5101.h with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/max2820.c [moved from drivers/net/wireless/rtl818x/rtl8180/max2820.c with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/max2820.h [moved from drivers/net/wireless/rtl818x/rtl8180/max2820.h with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/rtl8180.h [moved from drivers/net/wireless/rtl818x/rtl8180/rtl8180.h with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/rtl8225.c [moved from drivers/net/wireless/rtl818x/rtl8180/rtl8225.c with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/rtl8225.h [moved from drivers/net/wireless/rtl818x/rtl8180/rtl8225.h with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/rtl8225se.c [moved from drivers/net/wireless/rtl818x/rtl8180/rtl8225se.c with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/rtl8225se.h [moved from drivers/net/wireless/rtl818x/rtl8180/rtl8225se.h with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/sa2400.c [moved from drivers/net/wireless/rtl818x/rtl8180/sa2400.c with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8180/sa2400.h [moved from drivers/net/wireless/rtl818x/rtl8180/sa2400.h with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8187/Makefile [moved from drivers/net/wireless/rtl818x/rtl8187/Makefile with 62% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c [moved from drivers/net/wireless/rtl818x/rtl8187/dev.c with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8187/leds.c [moved from drivers/net/wireless/rtl818x/rtl8187/leds.c with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8187/leds.h [moved from drivers/net/wireless/rtl818x/rtl8187/leds.h with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8187/rfkill.c [moved from drivers/net/wireless/rtl818x/rtl8187/rfkill.c with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8187/rfkill.h [moved from drivers/net/wireless/rtl818x/rtl8187/rfkill.h with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8187/rtl8187.h [moved from drivers/net/wireless/rtl818x/rtl8187/rtl8187.h with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8187/rtl8225.c [moved from drivers/net/wireless/rtl818x/rtl8187/rtl8225.c with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl8187/rtl8225.h [moved from drivers/net/wireless/rtl818x/rtl8187/rtl8225.h with 100% similarity]
drivers/net/wireless/realtek/rtl818x/rtl818x.h [moved from drivers/net/wireless/rtl818x/rtl818x.h with 100% similarity]
drivers/net/wireless/realtek/rtl8xxxu/Kconfig [new file with mode: 0644]
drivers/net/wireless/realtek/rtl8xxxu/Makefile [new file with mode: 0644]
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c [new file with mode: 0644]
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h [new file with mode: 0644]
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h [new file with mode: 0644]
drivers/net/wireless/realtek/rtlwifi/Kconfig [moved from drivers/net/wireless/rtlwifi/Kconfig with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/Makefile [moved from drivers/net/wireless/rtlwifi/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/base.c [moved from drivers/net/wireless/rtlwifi/base.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/base.h [moved from drivers/net/wireless/rtlwifi/base.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/Makefile [moved from drivers/net/wireless/rtlwifi/btcoexist/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbt_precomp.h [moved from drivers/net/wireless/rtlwifi/btcoexist/halbt_precomp.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtc8192e2ant.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.h [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtc8192e2ant.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b1ant.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.h [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b1ant.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtc8821a1ant.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtc8821a1ant.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtc8821a2ant.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtc8821a2ant.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h [moved from drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c [moved from drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.h [moved from drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/cam.c [moved from drivers/net/wireless/rtlwifi/cam.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/cam.h [moved from drivers/net/wireless/rtlwifi/cam.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/core.c [moved from drivers/net/wireless/rtlwifi/core.c with 99% similarity]
drivers/net/wireless/realtek/rtlwifi/core.h [moved from drivers/net/wireless/rtlwifi/core.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/debug.c [moved from drivers/net/wireless/rtlwifi/debug.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/debug.h [moved from drivers/net/wireless/rtlwifi/debug.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/efuse.c [moved from drivers/net/wireless/rtlwifi/efuse.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/efuse.h [moved from drivers/net/wireless/rtlwifi/efuse.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/pci.c [moved from drivers/net/wireless/rtlwifi/pci.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/pci.h [moved from drivers/net/wireless/rtlwifi/pci.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/ps.c [moved from drivers/net/wireless/rtlwifi/ps.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/ps.h [moved from drivers/net/wireless/rtlwifi/ps.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/pwrseqcmd.h [moved from drivers/net/wireless/rtlwifi/pwrseqcmd.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rc.c [moved from drivers/net/wireless/rtlwifi/rc.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rc.h [moved from drivers/net/wireless/rtlwifi/rc.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/regd.c [moved from drivers/net/wireless/rtlwifi/regd.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/regd.h [moved from drivers/net/wireless/rtlwifi/regd.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/Makefile [moved from drivers/net/wireless/rtlwifi/rtl8188ee/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/def.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/def.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/dm.c [moved from drivers/net/wireless/rtlwifi/rtl8188ee/dm.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/dm.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/dm.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c [moved from drivers/net/wireless/rtlwifi/rtl8188ee/fw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/fw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c [moved from drivers/net/wireless/rtlwifi/rtl8188ee/hw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/hw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.c [moved from drivers/net/wireless/rtlwifi/rtl8188ee/led.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/led.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/phy.c [moved from drivers/net/wireless/rtlwifi/rtl8188ee/phy.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/phy.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/phy.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/pwrseq.c [moved from drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/pwrseq.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/reg.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/reg.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/rf.c [moved from drivers/net/wireless/rtlwifi/rtl8188ee/rf.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/rf.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/rf.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c [moved from drivers/net/wireless/rtlwifi/rtl8188ee/sw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/sw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/table.c [moved from drivers/net/wireless/rtlwifi/rtl8188ee/table.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/table.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/table.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c [moved from drivers/net/wireless/rtlwifi/rtl8188ee/trx.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.h [moved from drivers/net/wireless/rtlwifi/rtl8188ee/trx.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192c/Makefile [moved from drivers/net/wireless/rtlwifi/rtl8192c/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c [moved from drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.h [moved from drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.c [moved from drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.h [moved from drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192c/main.c [moved from drivers/net/wireless/rtlwifi/rtl8192c/main.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.c [moved from drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.h [moved from drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/Makefile [moved from drivers/net/wireless/rtlwifi/rtl8192ce/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/def.h [moved from drivers/net/wireless/rtlwifi/rtl8192ce/def.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.c [moved from drivers/net/wireless/rtlwifi/rtl8192ce/dm.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.h [moved from drivers/net/wireless/rtlwifi/rtl8192ce/dm.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c [moved from drivers/net/wireless/rtlwifi/rtl8192ce/hw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.h [moved from drivers/net/wireless/rtlwifi/rtl8192ce/hw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.c [moved from drivers/net/wireless/rtlwifi/rtl8192ce/led.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.h [moved from drivers/net/wireless/rtlwifi/rtl8192ce/led.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.c [moved from drivers/net/wireless/rtlwifi/rtl8192ce/phy.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.h [moved from drivers/net/wireless/rtlwifi/rtl8192ce/phy.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/reg.h [moved from drivers/net/wireless/rtlwifi/rtl8192ce/reg.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.c [moved from drivers/net/wireless/rtlwifi/rtl8192ce/rf.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.h [moved from drivers/net/wireless/rtlwifi/rtl8192ce/rf.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c [moved from drivers/net/wireless/rtlwifi/rtl8192ce/sw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.h [moved from drivers/net/wireless/rtlwifi/rtl8192ce/sw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.c [moved from drivers/net/wireless/rtlwifi/rtl8192ce/table.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.h [moved from drivers/net/wireless/rtlwifi/rtl8192ce/table.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c [moved from drivers/net/wireless/rtlwifi/rtl8192ce/trx.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.h [moved from drivers/net/wireless/rtlwifi/rtl8192ce/trx.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/Makefile [moved from drivers/net/wireless/rtlwifi/rtl8192cu/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h [moved from drivers/net/wireless/rtlwifi/rtl8192cu/def.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.c [moved from drivers/net/wireless/rtlwifi/rtl8192cu/dm.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.h [moved from drivers/net/wireless/rtlwifi/rtl8192cu/dm.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c [moved from drivers/net/wireless/rtlwifi/rtl8192cu/hw.c with 99% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.h [moved from drivers/net/wireless/rtlwifi/rtl8192cu/hw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.c [moved from drivers/net/wireless/rtlwifi/rtl8192cu/led.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.h [moved from drivers/net/wireless/rtlwifi/rtl8192cu/led.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c [moved from drivers/net/wireless/rtlwifi/rtl8192cu/mac.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.h [moved from drivers/net/wireless/rtlwifi/rtl8192cu/mac.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.c [moved from drivers/net/wireless/rtlwifi/rtl8192cu/phy.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.h [moved from drivers/net/wireless/rtlwifi/rtl8192cu/phy.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/reg.h [moved from drivers/net/wireless/rtlwifi/rtl8192cu/reg.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c [moved from drivers/net/wireless/rtlwifi/rtl8192cu/rf.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.h [moved from drivers/net/wireless/rtlwifi/rtl8192cu/rf.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c [moved from drivers/net/wireless/rtlwifi/rtl8192cu/sw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.h [moved from drivers/net/wireless/rtlwifi/rtl8192cu/sw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.c [moved from drivers/net/wireless/rtlwifi/rtl8192cu/table.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.h [moved from drivers/net/wireless/rtlwifi/rtl8192cu/table.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c [moved from drivers/net/wireless/rtlwifi/rtl8192cu/trx.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h [moved from drivers/net/wireless/rtlwifi/rtl8192cu/trx.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/Makefile [moved from drivers/net/wireless/rtlwifi/rtl8192de/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/def.h [moved from drivers/net/wireless/rtlwifi/rtl8192de/def.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.c [moved from drivers/net/wireless/rtlwifi/rtl8192de/dm.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.h [moved from drivers/net/wireless/rtlwifi/rtl8192de/dm.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c [moved from drivers/net/wireless/rtlwifi/rtl8192de/fw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.h [moved from drivers/net/wireless/rtlwifi/rtl8192de/fw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c [moved from drivers/net/wireless/rtlwifi/rtl8192de/hw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.h [moved from drivers/net/wireless/rtlwifi/rtl8192de/hw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.c [moved from drivers/net/wireless/rtlwifi/rtl8192de/led.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.h [moved from drivers/net/wireless/rtlwifi/rtl8192de/led.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c [moved from drivers/net/wireless/rtlwifi/rtl8192de/phy.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h [moved from drivers/net/wireless/rtlwifi/rtl8192de/phy.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/reg.h [moved from drivers/net/wireless/rtlwifi/rtl8192de/reg.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.c [moved from drivers/net/wireless/rtlwifi/rtl8192de/rf.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.h [moved from drivers/net/wireless/rtlwifi/rtl8192de/rf.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c [moved from drivers/net/wireless/rtlwifi/rtl8192de/sw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.h [moved from drivers/net/wireless/rtlwifi/rtl8192de/sw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.c [moved from drivers/net/wireless/rtlwifi/rtl8192de/table.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.h [moved from drivers/net/wireless/rtlwifi/rtl8192de/table.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c [moved from drivers/net/wireless/rtlwifi/rtl8192de/trx.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h [moved from drivers/net/wireless/rtlwifi/rtl8192de/trx.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/Makefile [moved from drivers/net/wireless/rtlwifi/rtl8192ee/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/def.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/def.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/dm.c [moved from drivers/net/wireless/rtlwifi/rtl8192ee/dm.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/dm.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/dm.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.c [moved from drivers/net/wireless/rtlwifi/rtl8192ee/fw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/fw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c [moved from drivers/net/wireless/rtlwifi/rtl8192ee/hw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/hw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/led.c [moved from drivers/net/wireless/rtlwifi/rtl8192ee/led.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/led.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/led.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.c [moved from drivers/net/wireless/rtlwifi/rtl8192ee/phy.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/phy.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/pwrseq.c [moved from drivers/net/wireless/rtlwifi/rtl8192ee/pwrseq.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/pwrseq.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/pwrseq.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/reg.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/reg.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/rf.c [moved from drivers/net/wireless/rtlwifi/rtl8192ee/rf.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/rf.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/rf.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c [moved from drivers/net/wireless/rtlwifi/rtl8192ee/sw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/sw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/table.c [moved from drivers/net/wireless/rtlwifi/rtl8192ee/table.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/table.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/table.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c [moved from drivers/net/wireless/rtlwifi/rtl8192ee/trx.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.h [moved from drivers/net/wireless/rtlwifi/rtl8192ee/trx.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/Makefile [moved from drivers/net/wireless/rtlwifi/rtl8192se/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/def.h [moved from drivers/net/wireless/rtlwifi/rtl8192se/def.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.c [moved from drivers/net/wireless/rtlwifi/rtl8192se/dm.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.h [moved from drivers/net/wireless/rtlwifi/rtl8192se/dm.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.c [moved from drivers/net/wireless/rtlwifi/rtl8192se/fw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.h [moved from drivers/net/wireless/rtlwifi/rtl8192se/fw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c [moved from drivers/net/wireless/rtlwifi/rtl8192se/hw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.h [moved from drivers/net/wireless/rtlwifi/rtl8192se/hw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.c [moved from drivers/net/wireless/rtlwifi/rtl8192se/led.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.h [moved from drivers/net/wireless/rtlwifi/rtl8192se/led.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c [moved from drivers/net/wireless/rtlwifi/rtl8192se/phy.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.h [moved from drivers/net/wireless/rtlwifi/rtl8192se/phy.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/reg.h [moved from drivers/net/wireless/rtlwifi/rtl8192se/reg.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.c [moved from drivers/net/wireless/rtlwifi/rtl8192se/rf.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.h [moved from drivers/net/wireless/rtlwifi/rtl8192se/rf.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c [moved from drivers/net/wireless/rtlwifi/rtl8192se/sw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.h [moved from drivers/net/wireless/rtlwifi/rtl8192se/sw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.c [moved from drivers/net/wireless/rtlwifi/rtl8192se/table.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.h [moved from drivers/net/wireless/rtlwifi/rtl8192se/table.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c [moved from drivers/net/wireless/rtlwifi/rtl8192se/trx.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.h [moved from drivers/net/wireless/rtlwifi/rtl8192se/trx.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/Makefile [moved from drivers/net/wireless/rtlwifi/rtl8723ae/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/btc.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/btc.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/def.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/def.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/dm.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/dm.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/dm.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/dm.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/fw.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/fw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/fw.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/fw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hal_bt_coexist.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hal_bt_coexist.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hal_btc.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hal_btc.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hw.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/hw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hw.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/hw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/led.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/led.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/phy.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/phy.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/phy.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/phy.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/pwrseq.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/pwrseq.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/reg.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/reg.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/rf.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/rf.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/rf.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/rf.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/sw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/sw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/table.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/table.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/table.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/table.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c [moved from drivers/net/wireless/rtlwifi/rtl8723ae/trx.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.h [moved from drivers/net/wireless/rtlwifi/rtl8723ae/trx.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/Makefile [moved from drivers/net/wireless/rtlwifi/rtl8723be/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/def.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/def.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/dm.c [moved from drivers/net/wireless/rtlwifi/rtl8723be/dm.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/dm.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/dm.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.c [moved from drivers/net/wireless/rtlwifi/rtl8723be/fw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/fw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c [moved from drivers/net/wireless/rtlwifi/rtl8723be/hw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/hw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/led.c [moved from drivers/net/wireless/rtlwifi/rtl8723be/led.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/led.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/led.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/phy.c [moved from drivers/net/wireless/rtlwifi/rtl8723be/phy.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/phy.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/phy.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/pwrseq.c [moved from drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/pwrseq.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/reg.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/reg.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/rf.c [moved from drivers/net/wireless/rtlwifi/rtl8723be/rf.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/rf.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/rf.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c [moved from drivers/net/wireless/rtlwifi/rtl8723be/sw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/sw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/table.c [moved from drivers/net/wireless/rtlwifi/rtl8723be/table.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/table.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/table.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c [moved from drivers/net/wireless/rtlwifi/rtl8723be/trx.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.h [moved from drivers/net/wireless/rtlwifi/rtl8723be/trx.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723com/Makefile [moved from drivers/net/wireless/rtlwifi/rtl8723com/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723com/dm_common.c [moved from drivers/net/wireless/rtlwifi/rtl8723com/dm_common.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723com/dm_common.h [moved from drivers/net/wireless/rtlwifi/rtl8723com/dm_common.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c [moved from drivers/net/wireless/rtlwifi/rtl8723com/fw_common.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.h [moved from drivers/net/wireless/rtlwifi/rtl8723com/fw_common.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723com/main.c [moved from drivers/net/wireless/rtlwifi/rtl8723com/main.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723com/phy_common.c [moved from drivers/net/wireless/rtlwifi/rtl8723com/phy_common.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8723com/phy_common.h [moved from drivers/net/wireless/rtlwifi/rtl8723com/phy_common.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/Makefile [moved from drivers/net/wireless/rtlwifi/rtl8821ae/Makefile with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/def.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/def.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c [moved from drivers/net/wireless/rtlwifi/rtl8821ae/dm.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/dm.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c [moved from drivers/net/wireless/rtlwifi/rtl8821ae/fw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/fw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c [moved from drivers/net/wireless/rtlwifi/rtl8821ae/hw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/hw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/led.c [moved from drivers/net/wireless/rtlwifi/rtl8821ae/led.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/led.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/led.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c [moved from drivers/net/wireless/rtlwifi/rtl8821ae/phy.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/phy.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/pwrseq.c [moved from drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/pwrseq.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/reg.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/rf.c [moved from drivers/net/wireless/rtlwifi/rtl8821ae/rf.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/rf.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/rf.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c [moved from drivers/net/wireless/rtlwifi/rtl8821ae/sw.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/sw.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c [moved from drivers/net/wireless/rtlwifi/rtl8821ae/table.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/table.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c [moved from drivers/net/wireless/rtlwifi/rtl8821ae/trx.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h [moved from drivers/net/wireless/rtlwifi/rtl8821ae/trx.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/stats.c [moved from drivers/net/wireless/rtlwifi/stats.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/stats.h [moved from drivers/net/wireless/rtlwifi/stats.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/usb.c [moved from drivers/net/wireless/rtlwifi/usb.c with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/usb.h [moved from drivers/net/wireless/rtlwifi/usb.h with 100% similarity]
drivers/net/wireless/realtek/rtlwifi/wifi.h [moved from drivers/net/wireless/rtlwifi/wifi.h with 100% similarity]
drivers/net/wireless/rndis_wlan.c
drivers/net/wireless/rsi/rsi_91x_mac80211.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800lib.h
drivers/net/wireless/rt2x00/rt2x00config.c
drivers/net/wireless/ti/wl12xx/scan.c
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/scan.c
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/xen-netfront.c
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/fdp/Kconfig [new file with mode: 0644]
drivers/nfc/fdp/Makefile [new file with mode: 0644]
drivers/nfc/fdp/fdp.c [new file with mode: 0644]
drivers/nfc/fdp/fdp.h [new file with mode: 0644]
drivers/nfc/fdp/i2c.c [new file with mode: 0644]
drivers/nfc/microread/Kconfig
drivers/nfc/nfcmrvl/Kconfig
drivers/nfc/nfcmrvl/Makefile
drivers/nfc/nfcmrvl/fw_dnld.c [new file with mode: 0644]
drivers/nfc/nfcmrvl/fw_dnld.h [new file with mode: 0644]
drivers/nfc/nfcmrvl/i2c.c [new file with mode: 0644]
drivers/nfc/nfcmrvl/main.c
drivers/nfc/nfcmrvl/nfcmrvl.h
drivers/nfc/nfcmrvl/spi.c [new file with mode: 0644]
drivers/nfc/nfcmrvl/uart.c
drivers/nfc/nfcmrvl/usb.c
drivers/nfc/nfcsim.c
drivers/nfc/nfcwilink.c
drivers/nfc/nxp-nci/core.c
drivers/nfc/nxp-nci/i2c.c
drivers/nfc/nxp-nci/nxp-nci.h
drivers/nfc/pn533.c
drivers/nfc/pn544/Kconfig
drivers/nfc/s3fwrn5/Kconfig
drivers/nfc/s3fwrn5/Makefile
drivers/nfc/s3fwrn5/i2c.c
drivers/nfc/s3fwrn5/nci.c
drivers/nfc/s3fwrn5/nci.h
drivers/nfc/st-nci/Makefile
drivers/nfc/st-nci/core.c
drivers/nfc/st-nci/i2c.c
drivers/nfc/st-nci/ndlc.c
drivers/nfc/st-nci/ndlc.h
drivers/nfc/st-nci/se.c [moved from drivers/nfc/st-nci/st-nci_se.c with 82% similarity]
drivers/nfc/st-nci/spi.c
drivers/nfc/st-nci/st-nci.h
drivers/nfc/st-nci/st-nci_se.h [deleted file]
drivers/nfc/st-nci/vendor_cmds.c [new file with mode: 0644]
drivers/nfc/st21nfca/Makefile
drivers/nfc/st21nfca/core.c [moved from drivers/nfc/st21nfca/st21nfca.c with 97% similarity]
drivers/nfc/st21nfca/dep.c [moved from drivers/nfc/st21nfca/st21nfca_dep.c with 99% similarity]
drivers/nfc/st21nfca/i2c.c
drivers/nfc/st21nfca/se.c [moved from drivers/nfc/st21nfca/st21nfca_se.c with 96% similarity]
drivers/nfc/st21nfca/st21nfca.h
drivers/nfc/st21nfca/st21nfca_dep.h [deleted file]
drivers/nfc/st21nfca/st21nfca_se.h [deleted file]
drivers/nfc/st21nfca/vendor_cmds.c [new file with mode: 0644]
drivers/nfc/trf7970a.c
drivers/of/irq.c
drivers/pci/host/pci-xgene-msi.c
drivers/pci/msi.c
drivers/pci/of.c
drivers/pci/pci-sysfs.c
drivers/pci/probe.c
drivers/phy/phy-rcar-gen2.c
drivers/pinctrl/Kconfig
drivers/pinctrl/Makefile
drivers/pinctrl/bcm/pinctrl-bcm2835.c
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c
drivers/pinctrl/berlin/Kconfig
drivers/pinctrl/berlin/Makefile
drivers/pinctrl/berlin/berlin-bg2.c
drivers/pinctrl/berlin/berlin-bg2cd.c
drivers/pinctrl/berlin/berlin-bg2q.c
drivers/pinctrl/berlin/berlin-bg4ct.c [new file with mode: 0644]
drivers/pinctrl/berlin/berlin.c
drivers/pinctrl/berlin/berlin.h
drivers/pinctrl/core.c
drivers/pinctrl/freescale/pinctrl-imx.c
drivers/pinctrl/freescale/pinctrl-imx.h
drivers/pinctrl/freescale/pinctrl-imx7d.c
drivers/pinctrl/freescale/pinctrl-mxs.c
drivers/pinctrl/intel/Kconfig
drivers/pinctrl/intel/Makefile
drivers/pinctrl/intel/pinctrl-baytrail.c
drivers/pinctrl/intel/pinctrl-broxton.c [new file with mode: 0644]
drivers/pinctrl/intel/pinctrl-cherryview.c
drivers/pinctrl/intel/pinctrl-intel.c
drivers/pinctrl/mediatek/pinctrl-mtk-common.c
drivers/pinctrl/nomadik/pinctrl-abx500.c
drivers/pinctrl/nomadik/pinctrl-nomadik.c
drivers/pinctrl/pinconf-generic.c
drivers/pinctrl/pinconf.c
drivers/pinctrl/pinctrl-adi2.c
drivers/pinctrl/pinctrl-as3722.c
drivers/pinctrl/pinctrl-at91-pio4.c [new file with mode: 0644]
drivers/pinctrl/pinctrl-at91.c
drivers/pinctrl/pinctrl-coh901.c
drivers/pinctrl/pinctrl-digicolor.c
drivers/pinctrl/pinctrl-pistachio.c
drivers/pinctrl/pinctrl-rockchip.c
drivers/pinctrl/pinctrl-st.c
drivers/pinctrl/pinctrl-tegra-xusb.c
drivers/pinctrl/pinctrl-tz1090-pdc.c
drivers/pinctrl/pinctrl-tz1090.c
drivers/pinctrl/pinctrl-xway.c
drivers/pinctrl/pinctrl-zynq.c
drivers/pinctrl/qcom/pinctrl-msm.c
drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c
drivers/pinctrl/samsung/pinctrl-exynos5440.c
drivers/pinctrl/samsung/pinctrl-samsung.c
drivers/pinctrl/sh-pfc/Kconfig
drivers/pinctrl/sh-pfc/Makefile
drivers/pinctrl/sh-pfc/core.c
drivers/pinctrl/sh-pfc/core.h
drivers/pinctrl/sh-pfc/gpio.c
drivers/pinctrl/sh-pfc/pfc-emev2.c
drivers/pinctrl/sh-pfc/pfc-r8a73a4.c
drivers/pinctrl/sh-pfc/pfc-r8a7740.c
drivers/pinctrl/sh-pfc/pfc-r8a7778.c
drivers/pinctrl/sh-pfc/pfc-r8a7779.c
drivers/pinctrl/sh-pfc/pfc-r8a7790.c
drivers/pinctrl/sh-pfc/pfc-r8a7791.c
drivers/pinctrl/sh-pfc/pfc-r8a7794.c
drivers/pinctrl/sh-pfc/pfc-r8a7795.c [new file with mode: 0644]
drivers/pinctrl/sh-pfc/pfc-sh7203.c
drivers/pinctrl/sh-pfc/pfc-sh7264.c
drivers/pinctrl/sh-pfc/pfc-sh7269.c
drivers/pinctrl/sh-pfc/pfc-sh73a0.c
drivers/pinctrl/sh-pfc/pfc-sh7720.c
drivers/pinctrl/sh-pfc/pfc-sh7722.c
drivers/pinctrl/sh-pfc/pfc-sh7723.c
drivers/pinctrl/sh-pfc/pfc-sh7724.c
drivers/pinctrl/sh-pfc/pfc-sh7734.c
drivers/pinctrl/sh-pfc/pfc-sh7757.c
drivers/pinctrl/sh-pfc/pfc-sh7785.c
drivers/pinctrl/sh-pfc/pfc-sh7786.c
drivers/pinctrl/sh-pfc/pfc-shx3.c
drivers/pinctrl/sh-pfc/sh_pfc.h
drivers/pinctrl/sirf/pinctrl-atlas7.c
drivers/pinctrl/sunxi/Kconfig
drivers/pinctrl/sunxi/Makefile
drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c
drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c [new file with mode: 0644]
drivers/pinctrl/sunxi/pinctrl-sunxi.c
drivers/pinctrl/uniphier/Kconfig
drivers/pinctrl/uniphier/Makefile
drivers/pinctrl/uniphier/pinctrl-ph1-ld4.c
drivers/pinctrl/uniphier/pinctrl-ph1-ld6b.c
drivers/pinctrl/uniphier/pinctrl-ph1-pro4.c
drivers/pinctrl/uniphier/pinctrl-ph1-pro5.c
drivers/pinctrl/uniphier/pinctrl-ph1-sld8.c
drivers/pinctrl/uniphier/pinctrl-proxstream2.c
drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
drivers/pinctrl/vt8500/pinctrl-wmt.c
drivers/pps/kapi.c
drivers/s390/char/monreader.c
drivers/s390/char/vmlogrdr.c
drivers/s390/cio/qdio_main.c
drivers/s390/net/netiucv.c
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_core_sys.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l2_sys.c
drivers/s390/net/qeth_l3_main.c
drivers/s390/net/smsgiucv.c
drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
drivers/scsi/mvsas/mv_sas.c
drivers/scsi/scsi_dh.c
drivers/scsi/scsi_priv.h
drivers/scsi/scsi_sysfs.c
drivers/spmi/spmi-pmic-arb.c
drivers/ssb/Kconfig
drivers/ssb/Makefile
drivers/ssb/bridge_pcmcia_80211.c [new file with mode: 0644]
drivers/ssb/host_soc.c [new file with mode: 0644]
drivers/ssb/main.c
drivers/ssb/pcmcia.c
drivers/ssb/sdio.c
drivers/ssb/ssb_private.h
drivers/staging/speakup/selection.c
drivers/tty/hvc/hvc_iucv.c
drivers/usb/musb/omap2430.c
drivers/usb/renesas_usbhs/rcar2.c
drivers/vhost/vhost.h
drivers/video/fbdev/efifb.c
fs/file.c
fs/fs-writeback.c
fs/overlayfs/copy_up.c
fs/overlayfs/inode.c
fs/overlayfs/super.c
fs/proc/array.c
fs/proc/base.c
fs/proc/meminfo.c
include/asm-generic/atomic-long.h
include/asm-generic/atomic.h
include/asm-generic/mutex-dec.h
include/asm-generic/mutex-xchg.h
include/asm-generic/pgtable.h
include/asm-generic/preempt.h
include/asm-generic/qrwlock_types.h
include/asm-generic/rwsem.h
include/crypto/akcipher.h
include/crypto/hash.h
include/crypto/internal/rsa.h
include/dt-bindings/gpio/gpio.h
include/dt-bindings/leds/leds-netxbig.h [new file with mode: 0644]
include/kvm/arm_vgic.h
include/linux/acpi.h
include/linux/amba/bus.h
include/linux/atomic.h
include/linux/bcma/bcma.h
include/linux/blk-cgroup.h
include/linux/bpf.h
include/linux/brcmphy.h
include/linux/can/dev.h
include/linux/can/led.h
include/linux/compiler.h
include/linux/cpu.h
include/linux/dccp.h
include/linux/edac.h
include/linux/efi.h
include/linux/fdtable.h
include/linux/filter.h
include/linux/fwnode.h
include/linux/genetlink.h
include/linux/gpio/consumer.h
include/linux/gpio/driver.h
include/linux/ieee80211.h
include/linux/ieee802154.h
include/linux/if_bridge.h
include/linux/if_link.h
include/linux/igmp.h
include/linux/inetdevice.h
include/linux/init_task.h
include/linux/interrupt.h
include/linux/iommu-common.h
include/linux/ipv6.h
include/linux/irq.h
include/linux/irqchip/arm-gic-v3.h
include/linux/irqchip/arm-gic.h
include/linux/irqdomain.h
include/linux/irqreturn.h
include/linux/leds.h
include/linux/list.h
include/linux/list_bl.h
include/linux/list_nulls.h
include/linux/mlx4/device.h
include/linux/mlx5/device.h
include/linux/mlx5/driver.h
include/linux/mmc/card.h
include/linux/mmc/core.h
include/linux/mmc/dw_mmc.h
include/linux/mmc/host.h
include/linux/mpi.h
include/linux/msi.h
include/linux/net.h
include/linux/netdev_features.h
include/linux/netdevice.h
include/linux/netfilter.h
include/linux/netfilter/nfnetlink.h
include/linux/netfilter/x_tables.h
include/linux/netfilter_arp/arp_tables.h
include/linux/netfilter_bridge.h
include/linux/netfilter_bridge/ebtables.h
include/linux/netfilter_ingress.h
include/linux/netfilter_ipv4.h
include/linux/netfilter_ipv4/ip_tables.h
include/linux/netfilter_ipv6.h
include/linux/netfilter_ipv6/ip6_tables.h
include/linux/of_gpio.h
include/linux/of_irq.h
include/linux/omap-dma.h
include/linux/once.h [new file with mode: 0644]
include/linux/percpu-rwsem.h
include/linux/perf_event.h
include/linux/phy.h
include/linux/pinctrl/devinfo.h
include/linux/pinctrl/pinconf-generic.h
include/linux/pinctrl/pinctrl-state.h
include/linux/platform_data/atmel.h
include/linux/platform_data/leds-kirkwood-netxbig.h
include/linux/platform_data/mdio-gpio.h [moved from include/linux/mdio-gpio.h with 100% similarity]
include/linux/platform_data/nfcmrvl.h
include/linux/platform_data/st-nci.h
include/linux/pps_kernel.h
include/linux/preempt.h
include/linux/ptp_classify.h
include/linux/qed/common_hsi.h [new file with mode: 0644]
include/linux/qed/eth_common.h [new file with mode: 0644]
include/linux/qed/qed_chain.h [new file with mode: 0644]
include/linux/qed/qed_eth_if.h [new file with mode: 0644]
include/linux/qed/qed_if.h [new file with mode: 0644]
include/linux/random.h
include/linux/rcu_sync.h [new file with mode: 0644]
include/linux/rculist.h
include/linux/rcupdate.h
include/linux/rcutiny.h
include/linux/rcutree.h
include/linux/regmap.h
include/linux/rtnetlink.h
include/linux/sched.h
include/linux/sched/deadline.h
include/linux/seccomp.h
include/linux/skbuff.h
include/linux/smpboot.h
include/linux/srcu.h
include/linux/stop_machine.h
include/linux/tcp.h
include/linux/timekeeping.h
include/linux/timex.h
include/linux/usb/cdc.h [new file with mode: 0644]
include/linux/vmalloc.h
include/net/6lowpan.h
include/net/addrconf.h
include/net/af_ieee802154.h
include/net/af_vsock.h
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/hci_mon.h
include/net/bond_3ad.h
include/net/cfg80211.h
include/net/cfg802154.h
include/net/dn_neigh.h
include/net/dsa.h
include/net/dst.h
include/net/dst_metadata.h
include/net/dst_ops.h
include/net/ethoc.h
include/net/flow.h
include/net/genetlink.h
include/net/ieee802154_netdev.h
include/net/inet6_connection_sock.h
include/net/inet_common.h
include/net/inet_connection_sock.h
include/net/inet_frag.h
include/net/inet_hashtables.h
include/net/inet_sock.h
include/net/inet_timewait_sock.h
include/net/ip.h
include/net/ip6_route.h
include/net/ip6_tunnel.h
include/net/ip_fib.h
include/net/ip_vs.h
include/net/ipv6.h
include/net/iucv/iucv.h
include/net/l3mdev.h [new file with mode: 0644]
include/net/lwtunnel.h
include/net/mac80211.h
include/net/mac802154.h
include/net/mpls_iptunnel.h
include/net/ndisc.h
include/net/netfilter/br_netfilter.h
include/net/netfilter/ipv4/nf_dup_ipv4.h
include/net/netfilter/ipv4/nf_reject.h
include/net/netfilter/ipv6/nf_defrag_ipv6.h
include/net/netfilter/ipv6/nf_dup_ipv6.h
include/net/netfilter/nf_conntrack.h
include/net/netfilter/nf_conntrack_core.h
include/net/netfilter/nf_conntrack_l4proto.h
include/net/netfilter/nf_conntrack_timeout.h
include/net/netfilter/nf_nat_core.h
include/net/netfilter/nf_nat_l3proto.h
include/net/netfilter/nf_queue.h
include/net/netfilter/nf_tables.h
include/net/netfilter/nf_tables_ipv4.h
include/net/netfilter/nf_tables_ipv6.h
include/net/netfilter/nfnetlink_queue.h [deleted file]
include/net/netlink.h
include/net/nfc/nci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
include/net/nl802154.h
include/net/request_sock.h
include/net/route.h
include/net/rtnetlink.h
include/net/sch_generic.h
include/net/sock.h
include/net/switchdev.h
include/net/tc_act/tc_connmark.h
include/net/tcp.h
include/net/tso.h
include/net/vrf.h [deleted file]
include/net/vxlan.h
include/net/xfrm.h
include/trace/events/sched.h
include/uapi/linux/Kbuild
include/uapi/linux/atm_zatm.h
include/uapi/linux/bpf.h
include/uapi/linux/can/bcm.h
include/uapi/linux/if_arcnet.h
include/uapi/linux/if_bridge.h
include/uapi/linux/if_link.h
include/uapi/linux/magic.h
include/uapi/linux/mmc/ioctl.h
include/uapi/linux/netfilter/nfnetlink_log.h
include/uapi/linux/netlink.h
include/uapi/linux/nfc.h
include/uapi/linux/nl80211.h
include/uapi/linux/openvswitch.h
include/uapi/linux/perf_event.h
include/uapi/linux/pkt_cls.h
include/uapi/linux/ptrace.h
include/uapi/linux/rtnetlink.h
include/uapi/linux/screen_info.h
include/uapi/linux/usb/cdc.h
kernel/bpf/Makefile
kernel/bpf/arraymap.c
kernel/bpf/core.c
kernel/bpf/hashtab.c
kernel/bpf/helpers.c
kernel/bpf/inode.c [new file with mode: 0644]
kernel/bpf/syscall.c
kernel/bpf/verifier.c
kernel/cpu.c
kernel/events/core.c
kernel/events/ring_buffer.c
kernel/exit.c
kernel/fork.c
kernel/futex.c
kernel/irq/Kconfig
kernel/irq/Makefile
kernel/irq/chip.c
kernel/irq/cpuhotplug.c [new file with mode: 0644]
kernel/irq/handle.c
kernel/irq/internals.h
kernel/irq/irqdomain.c
kernel/irq/manage.c
kernel/irq/msi.c
kernel/irq/proc.c
kernel/irq/settings.h
kernel/kexec_core.c
kernel/locking/locktorture.c
kernel/locking/mcs_spinlock.h
kernel/locking/mutex.c
kernel/locking/osq_lock.c
kernel/locking/percpu-rwsem.c
kernel/locking/qrwlock.c
kernel/locking/qspinlock_paravirt.h
kernel/locking/rtmutex.c
kernel/locking/rwsem-xadd.c
kernel/memremap.c
kernel/module.c
kernel/ptrace.c
kernel/rcu/Makefile
kernel/rcu/rcutorture.c
kernel/rcu/srcu.c
kernel/rcu/sync.c [new file with mode: 0644]
kernel/rcu/tiny.c
kernel/rcu/tree.c
kernel/rcu/tree.h
kernel/rcu/tree_plugin.h
kernel/rcu/tree_trace.c
kernel/rcu/update.c
kernel/sched/core.c
kernel/sched/cpudeadline.c
kernel/sched/cpudeadline.h
kernel/sched/fair.c
kernel/sched/features.h
kernel/sched/rt.c
kernel/sched/sched.h
kernel/seccomp.c
kernel/smpboot.c
kernel/stop_machine.c
kernel/sysctl.c
kernel/time/clocksource.c
kernel/time/hrtimer.c
kernel/time/ntp.c
kernel/time/ntp_internal.h
kernel/time/posix-cpu-timers.c
kernel/time/timeconst.bc
kernel/time/timekeeping.c
kernel/time/timer.c
kernel/torture.c
kernel/trace/bpf_trace.c
kernel/trace/ftrace.c
kernel/trace/trace_sched_switch.c
kernel/trace/trace_sched_wakeup.c
lib/842/842.h
lib/842/842_compress.c
lib/842/842_decompress.c
lib/Makefile
lib/iommu-common.c
lib/mpi/mpicoder.c
lib/nmi_backtrace.c
lib/once.c [new file with mode: 0644]
lib/random32.c
mm/huge_memory.c
mm/pgtable-generic.c
mm/vmalloc.c
net/6lowpan/core.c
net/6lowpan/iphc.c
net/6lowpan/nhc.c
net/6lowpan/nhc.h
net/6lowpan/nhc_udp.c
net/8021q/vlan_core.c
net/Kconfig
net/Makefile
net/bluetooth/6lowpan.c
net/bluetooth/af_bluetooth.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_request.c
net/bluetooth/hci_request.h
net/bluetooth/hci_sock.c
net/bluetooth/hidp/core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/lib.c
net/bluetooth/mgmt.c
net/bluetooth/sco.c
net/bluetooth/smp.c
net/bluetooth/smp.h
net/bridge/br_device.c
net/bridge/br_fdb.c
net/bridge/br_forward.c
net/bridge/br_if.c
net/bridge/br_input.c
net/bridge/br_ioctl.c
net/bridge/br_mdb.c
net/bridge/br_multicast.c
net/bridge/br_netfilter_hooks.c
net/bridge/br_netfilter_ipv6.c
net/bridge/br_netlink.c
net/bridge/br_private.h
net/bridge/br_stp.c
net/bridge/br_stp_bpdu.c
net/bridge/br_stp_if.c
net/bridge/br_sysfs_br.c
net/bridge/br_vlan.c
net/bridge/netfilter/ebt_log.c
net/bridge/netfilter/ebt_nflog.c
net/bridge/netfilter/ebtable_broute.c
net/bridge/netfilter/ebtable_filter.c
net/bridge/netfilter/ebtable_nat.c
net/bridge/netfilter/ebtables.c
net/bridge/netfilter/nf_tables_bridge.c
net/bridge/netfilter/nft_reject_bridge.c
net/can/bcm.c
net/core/dev.c
net/core/dst.c
net/core/filter.c
net/core/lwtunnel.c
net/core/neighbour.c
net/core/net-sysfs.c
net/core/netpoll.c
net/core/ptp_classifier.c
net/core/request_sock.c
net/core/rtnetlink.c
net/core/sock.c
net/core/sock_diag.c
net/core/tso.c
net/core/utils.c
net/dcb/dcbnl.c
net/dccp/dccp.h
net/dccp/ipv4.c
net/dccp/ipv6.c
net/dccp/minisocks.c
net/dccp/output.c
net/dccp/probe.c
net/decnet/dn_neigh.c
net/decnet/dn_nsp_in.c
net/decnet/dn_nsp_out.c
net/decnet/dn_route.c
net/decnet/netfilter/dn_rtmsg.c
net/dsa/dsa.c
net/dsa/slave.c
net/ethernet/eth.c
net/ieee802154/6lowpan/6lowpan_i.h
net/ieee802154/6lowpan/core.c
net/ieee802154/6lowpan/reassembly.c
net/ieee802154/6lowpan/rx.c
net/ieee802154/6lowpan/tx.c
net/ieee802154/Kconfig
net/ieee802154/core.c
net/ieee802154/core.h
net/ieee802154/header_ops.c
net/ieee802154/nl802154.c
net/ieee802154/rdev-ops.h
net/ieee802154/socket.c
net/ipv4/Makefile
net/ipv4/af_inet.c
net/ipv4/arp.c
net/ipv4/devinet.c
net/ipv4/fib_frontend.c
net/ipv4/fib_semantics.c
net/ipv4/fib_trie.c
net/ipv4/gre_offload.c
net/ipv4/icmp.c
net/ipv4/igmp.c
net/ipv4/inet_connection_sock.c
net/ipv4/inet_diag.c
net/ipv4/inet_fragment.c
net/ipv4/inet_hashtables.c
net/ipv4/ip_forward.c
net/ipv4/ip_fragment.c
net/ipv4/ip_gre.c
net/ipv4/ip_input.c
net/ipv4/ip_output.c
net/ipv4/ip_tunnel_core.c
net/ipv4/ip_vti.c
net/ipv4/ipconfig.c
net/ipv4/ipmr.c
net/ipv4/netfilter.c
net/ipv4/netfilter/Kconfig
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/arptable_filter.c
net/ipv4/netfilter/ip_tables.c
net/ipv4/netfilter/ipt_CLUSTERIP.c
net/ipv4/netfilter/ipt_REJECT.c
net/ipv4/netfilter/ipt_SYNPROXY.c
net/ipv4/netfilter/ipt_ah.c
net/ipv4/netfilter/ipt_rpfilter.c
net/ipv4/netfilter/iptable_filter.c
net/ipv4/netfilter/iptable_mangle.c
net/ipv4/netfilter/iptable_nat.c
net/ipv4/netfilter/iptable_raw.c
net/ipv4/netfilter/iptable_security.c
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
net/ipv4/netfilter/nf_conntrack_proto_icmp.c
net/ipv4/netfilter/nf_defrag_ipv4.c
net/ipv4/netfilter/nf_dup_ipv4.c
net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
net/ipv4/netfilter/nf_nat_snmp_basic.c
net/ipv4/netfilter/nf_reject_ipv4.c
net/ipv4/netfilter/nf_tables_arp.c
net/ipv4/netfilter/nf_tables_ipv4.c
net/ipv4/netfilter/nft_chain_nat_ipv4.c
net/ipv4/netfilter/nft_chain_route_ipv4.c
net/ipv4/netfilter/nft_dup_ipv4.c
net/ipv4/netfilter/nft_masq_ipv4.c
net/ipv4/netfilter/nft_redir_ipv4.c
net/ipv4/netfilter/nft_reject_ipv4.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipv4/syncookies.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_cong.c
net/ipv4/tcp_dctcp.c
net/ipv4/tcp_fastopen.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_minisocks.c
net/ipv4/tcp_output.c
net/ipv4/tcp_recovery.c [new file with mode: 0644]
net/ipv4/tcp_timer.c
net/ipv4/udp.c
net/ipv4/xfrm4_input.c
net/ipv4/xfrm4_output.c
net/ipv4/xfrm4_policy.c
net/ipv6/addrconf.c
net/ipv6/datagram.c
net/ipv6/fib6_rules.c
net/ipv6/icmp.c
net/ipv6/ila.c
net/ipv6/inet6_connection_sock.c
net/ipv6/inet6_hashtables.c
net/ipv6/ip6_fib.c
net/ipv6/ip6_input.c
net/ipv6/ip6_offload.c
net/ipv6/ip6_output.c
net/ipv6/ip6_vti.c
net/ipv6/ip6mr.c
net/ipv6/mcast.c
net/ipv6/mip6.c
net/ipv6/ndisc.c
net/ipv6/netfilter.c
net/ipv6/netfilter/Kconfig
net/ipv6/netfilter/ip6_tables.c
net/ipv6/netfilter/ip6t_REJECT.c
net/ipv6/netfilter/ip6t_SYNPROXY.c
net/ipv6/netfilter/ip6t_rpfilter.c
net/ipv6/netfilter/ip6table_filter.c
net/ipv6/netfilter/ip6table_mangle.c
net/ipv6/netfilter/ip6table_nat.c
net/ipv6/netfilter/ip6table_raw.c
net/ipv6/netfilter/ip6table_security.c
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
net/ipv6/netfilter/nf_dup_ipv6.c
net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
net/ipv6/netfilter/nf_nat_masquerade_ipv6.c
net/ipv6/netfilter/nf_reject_ipv6.c
net/ipv6/netfilter/nf_tables_ipv6.c
net/ipv6/netfilter/nft_chain_nat_ipv6.c
net/ipv6/netfilter/nft_chain_route_ipv6.c
net/ipv6/netfilter/nft_dup_ipv6.c
net/ipv6/netfilter/nft_redir_ipv6.c
net/ipv6/netfilter/nft_reject_ipv6.c
net/ipv6/output_core.c
net/ipv6/raw.c
net/ipv6/reassembly.c
net/ipv6/route.c
net/ipv6/sit.c
net/ipv6/syncookies.c
net/ipv6/tcp_ipv6.c
net/ipv6/tunnel6.c
net/ipv6/udp.c
net/ipv6/xfrm6_input.c
net/ipv6/xfrm6_output.c
net/ipv6/xfrm6_policy.c
net/irda/af_irda.c
net/irda/irlmp.c
net/iucv/af_iucv.c
net/iucv/iucv.c
net/key/af_key.c
net/l2tp/l2tp_core.h
net/l2tp/l2tp_eth.c
net/l2tp/l2tp_ip.c
net/l2tp/l2tp_ip6.c
net/l2tp/l2tp_netlink.c
net/l2tp/l2tp_ppp.c
net/l3mdev/Kconfig [new file with mode: 0644]
net/l3mdev/Makefile [new file with mode: 0644]
net/l3mdev/l3mdev.c [new file with mode: 0644]
net/mac80211/Makefile
net/mac80211/agg-rx.c
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/cfg.h [deleted file]
net/mac80211/debugfs.c
net/mac80211/debugfs_key.c
net/mac80211/debugfs_netdev.c
net/mac80211/debugfs_sta.c
net/mac80211/driver-ops.c
net/mac80211/driver-ops.h
net/mac80211/ethtool.c
net/mac80211/event.c [deleted file]
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_hwmp.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/ocb.c
net/mac80211/offchannel.c
net/mac80211/pm.c
net/mac80211/rate.c
net/mac80211/rc80211_minstrel_debugfs.c
net/mac80211/rc80211_minstrel_ht_debugfs.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tdls.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wpa.c
net/mac802154/cfg.c
net/mac802154/iface.c
net/mac802154/llsec.c
net/mac802154/rx.c
net/mac802154/tx.c
net/mpls/af_mpls.c
net/mpls/internal.h
net/mpls/mpls_iptunnel.c
net/netfilter/Kconfig
net/netfilter/Makefile
net/netfilter/core.c
net/netfilter/ipset/ip_set_core.c
net/netfilter/ipset/ip_set_list_set.c
net/netfilter/ipvs/ip_vs_app.c
net/netfilter/ipvs/ip_vs_conn.c
net/netfilter/ipvs/ip_vs_core.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/ipvs/ip_vs_est.c
net/netfilter/ipvs/ip_vs_ftp.c
net/netfilter/ipvs/ip_vs_lblc.c
net/netfilter/ipvs/ip_vs_lblcr.c
net/netfilter/ipvs/ip_vs_nfct.c
net/netfilter/ipvs/ip_vs_pe_sip.c
net/netfilter/ipvs/ip_vs_proto.c
net/netfilter/ipvs/ip_vs_proto_ah_esp.c
net/netfilter/ipvs/ip_vs_proto_sctp.c
net/netfilter/ipvs/ip_vs_proto_tcp.c
net/netfilter/ipvs/ip_vs_proto_udp.c
net/netfilter/ipvs/ip_vs_sh.c
net/netfilter/ipvs/ip_vs_sync.c
net/netfilter/ipvs/ip_vs_xmit.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nf_conntrack_proto_dccp.c
net/netfilter/nf_conntrack_proto_generic.c
net/netfilter/nf_conntrack_proto_gre.c
net/netfilter/nf_conntrack_proto_sctp.c
net/netfilter/nf_conntrack_proto_tcp.c
net/netfilter/nf_conntrack_proto_udp.c
net/netfilter/nf_conntrack_proto_udplite.c
net/netfilter/nf_nat_core.c
net/netfilter/nf_queue.c
net/netfilter/nf_tables_api.c
net/netfilter/nf_tables_core.c
net/netfilter/nf_tables_netdev.c
net/netfilter/nfnetlink.c
net/netfilter/nfnetlink_cttimeout.c
net/netfilter/nfnetlink_log.c
net/netfilter/nfnetlink_queue.c [moved from net/netfilter/nfnetlink_queue_core.c with 95% similarity]
net/netfilter/nfnetlink_queue_ct.c [deleted file]
net/netfilter/nft_log.c
net/netfilter/nft_meta.c
net/netfilter/nft_queue.c
net/netfilter/nft_reject_inet.c
net/netfilter/x_tables.c
net/netfilter/xt_CT.c
net/netfilter/xt_LOG.c
net/netfilter/xt_NFLOG.c
net/netfilter/xt_TCPMSS.c
net/netfilter/xt_TEE.c
net/netfilter/xt_TPROXY.c
net/netfilter/xt_addrtype.c
net/netfilter/xt_connlimit.c
net/netfilter/xt_ipvs.c
net/netfilter/xt_osf.c
net/netfilter/xt_recent.c
net/netfilter/xt_socket.c
net/netlink/af_netlink.c
net/netlink/genetlink.c
net/nfc/core.c
net/nfc/digital_core.c
net/nfc/hci/core.c
net/nfc/hci/llc.c
net/nfc/nci/Kconfig
net/nfc/nci/Makefile
net/nfc/nci/core.c
net/nfc/nci/data.c
net/nfc/nci/hci.c
net/nfc/nci/ntf.c
net/nfc/nci/rsp.c
net/nfc/nci/spi.c
net/nfc/netlink.c
net/nfc/nfc.h
net/nfc/rawsock.c
net/openvswitch/actions.c
net/openvswitch/conntrack.c
net/openvswitch/conntrack.h
net/openvswitch/datapath.c
net/openvswitch/datapath.h
net/openvswitch/flow.c
net/openvswitch/flow.h
net/openvswitch/flow_netlink.c
net/openvswitch/flow_netlink.h
net/openvswitch/flow_table.c
net/openvswitch/vport-geneve.c
net/openvswitch/vport-gre.c
net/openvswitch/vport-internal_dev.c
net/openvswitch/vport-netdev.c
net/openvswitch/vport-netdev.h
net/openvswitch/vport-vxlan.c
net/openvswitch/vport.c
net/openvswitch/vport.h
net/packet/af_packet.c
net/rds/af_rds.c
net/rds/bind.c
net/rds/connection.c
net/rds/ib.c
net/rds/ib.h
net/rds/ib_cm.c
net/rds/ib_rdma.c
net/rds/ib_recv.c
net/rds/ib_send.c
net/rds/ib_stats.c
net/rds/iw_rdma.c
net/rds/rds.h
net/rds/send.c
net/rds/tcp.c
net/rds/tcp_listen.c
net/rds/tcp_recv.c
net/rds/tcp_send.c
net/rds/threads.c
net/rxrpc/ar-connection.c
net/rxrpc/ar-internal.h
net/rxrpc/ar-transport.c
net/sched/act_bpf.c
net/sched/act_connmark.c
net/sched/act_ipt.c
net/sched/cls_bpf.c
net/sched/em_ipset.c
net/sched/sch_blackhole.c
net/sched/sch_choke.c
net/sched/sch_dsmark.c
net/sched/sch_fq.c
net/sctp/sm_make_chunk.c
net/sctp/sm_statefuns.c
net/switchdev/switchdev.c
net/sysctl_net.c
net/tipc/bcast.c
net/tipc/bcast.h
net/tipc/bearer.c
net/tipc/bearer.h
net/tipc/core.c
net/tipc/core.h
net/tipc/discover.c
net/tipc/link.c
net/tipc/link.h
net/tipc/msg.c
net/tipc/msg.h
net/tipc/name_distr.c
net/tipc/net.c
net/tipc/node.c
net/tipc/node.h
net/tipc/socket.c
net/tipc/udp_media.c
net/unix/af_unix.c
net/vmw_vsock/af_vsock.c
net/vmw_vsock/vmci_transport.c
net/vmw_vsock/vmci_transport.h
net/wireless/Kconfig
net/wireless/core.c
net/wireless/core.h
net/wireless/nl80211.c
net/wireless/reg.c
net/wireless/scan.c
net/wireless/trace.h
net/xfrm/xfrm_input.c
net/xfrm/xfrm_output.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_user.c
samples/bpf/Makefile
samples/bpf/bpf_helpers.h
samples/bpf/fds_example.c [new file with mode: 0644]
samples/bpf/libbpf.c
samples/bpf/libbpf.h
samples/bpf/tcbpf1_kern.c
samples/bpf/test_verifier.c
samples/bpf/trace_output_kern.c [new file with mode: 0644]
samples/bpf/trace_output_user.c [new file with mode: 0644]
security/selinux/hooks.c
security/smack/smack_netfilter.c
tools/build/.gitignore [new file with mode: 0644]
tools/build/Build [new file with mode: 0644]
tools/build/Build.include
tools/build/Documentation/Build.txt
tools/build/Makefile [new file with mode: 0644]
tools/build/Makefile.build
tools/build/Makefile.feature
tools/build/Makefile.include [new file with mode: 0644]
tools/build/feature/Makefile
tools/build/fixdep.c [new file with mode: 0644]
tools/build/tests/ex/Build
tools/build/tests/ex/Makefile
tools/build/tests/ex/ex.c
tools/build/tests/ex/inc.c [new file with mode: 0644]
tools/build/tests/run.sh
tools/include/linux/compiler.h
tools/include/linux/err.h [new file with mode: 0644]
tools/include/linux/filter.h [new file with mode: 0644]
tools/lib/api/Build
tools/lib/api/Makefile
tools/lib/api/cpu.c [new file with mode: 0644]
tools/lib/api/cpu.h [new file with mode: 0644]
tools/lib/api/fs/Build
tools/lib/api/fs/debugfs.c [deleted file]
tools/lib/api/fs/debugfs.h [deleted file]
tools/lib/api/fs/findfs.c [deleted file]
tools/lib/api/fs/findfs.h [deleted file]
tools/lib/api/fs/fs.c
tools/lib/api/fs/fs.h
tools/lib/api/fs/tracefs.c [deleted file]
tools/lib/api/fs/tracefs.h [deleted file]
tools/lib/api/fs/tracing_path.c [new file with mode: 0644]
tools/lib/api/fs/tracing_path.h [new file with mode: 0644]
tools/lib/bpf/Makefile
tools/lib/lockdep/Makefile
tools/lib/symbol/kallsyms.c
tools/lib/symbol/kallsyms.h
tools/lib/traceevent/event-parse.c
tools/lib/traceevent/event-parse.h
tools/lib/traceevent/plugin_kvm.c
tools/net/bpf_jit_disasm.c
tools/perf/Documentation/intel-pt.txt
tools/perf/Documentation/itrace.txt
tools/perf/Documentation/perf-bench.txt
tools/perf/Documentation/perf-inject.txt
tools/perf/Documentation/perf-list.txt
tools/perf/Documentation/perf-record.txt
tools/perf/Documentation/perf-report.txt
tools/perf/Documentation/perf-script.txt
tools/perf/Documentation/perf-stat.txt
tools/perf/Documentation/perf-top.txt
tools/perf/Documentation/perf.txt
tools/perf/MANIFEST
tools/perf/Makefile.perf
tools/perf/arch/common.c
tools/perf/arch/common.h
tools/perf/arch/x86/Build
tools/perf/arch/x86/Makefile
tools/perf/arch/x86/include/arch-tests.h [new file with mode: 0644]
tools/perf/arch/x86/tests/Build
tools/perf/arch/x86/tests/arch-tests.c [new file with mode: 0644]
tools/perf/arch/x86/tests/dwarf-unwind.c
tools/perf/arch/x86/tests/gen-insn-x86-dat.awk [new file with mode: 0644]
tools/perf/arch/x86/tests/gen-insn-x86-dat.sh [new file with mode: 0755]
tools/perf/arch/x86/tests/insn-x86-dat-32.c [new file with mode: 0644]
tools/perf/arch/x86/tests/insn-x86-dat-64.c [new file with mode: 0644]
tools/perf/arch/x86/tests/insn-x86-dat-src.c [new file with mode: 0644]
tools/perf/arch/x86/tests/insn-x86.c [new file with mode: 0644]
tools/perf/arch/x86/tests/intel-cqm.c [new file with mode: 0644]
tools/perf/arch/x86/tests/perf-time-to-tsc.c [moved from tools/perf/tests/perf-time-to-tsc.c with 98% similarity]
tools/perf/arch/x86/tests/rdpmc.c [moved from tools/perf/tests/rdpmc.c with 97% similarity]
tools/perf/arch/x86/util/dwarf-regs.c
tools/perf/arch/x86/util/intel-pt.c
tools/perf/bench/Build
tools/perf/bench/mem-functions.c [new file with mode: 0644]
tools/perf/bench/mem-memcpy.c [deleted file]
tools/perf/bench/numa.c
tools/perf/bench/sched-messaging.c
tools/perf/builtin-annotate.c
tools/perf/builtin-bench.c
tools/perf/builtin-evlist.c
tools/perf/builtin-help.c
tools/perf/builtin-inject.c
tools/perf/builtin-kmem.c
tools/perf/builtin-kvm.c
tools/perf/builtin-list.c
tools/perf/builtin-probe.c
tools/perf/builtin-record.c
tools/perf/builtin-report.c
tools/perf/builtin-sched.c
tools/perf/builtin-script.c
tools/perf/builtin-stat.c
tools/perf/builtin-top.c
tools/perf/builtin-trace.c
tools/perf/config/Makefile
tools/perf/perf.c
tools/perf/python/twatch.py
tools/perf/scripts/python/export-to-postgresql.py
tools/perf/tests/Build
tools/perf/tests/bpf-script-example.c [new file with mode: 0644]
tools/perf/tests/builtin-test.c
tools/perf/tests/code-reading.c
tools/perf/tests/dwarf-unwind.c
tools/perf/tests/evsel-tp-sched.c
tools/perf/tests/hists_filter.c
tools/perf/tests/make
tools/perf/tests/mmap-basic.c
tools/perf/tests/openat-syscall-all-cpus.c
tools/perf/tests/openat-syscall-tp-fields.c
tools/perf/tests/openat-syscall.c
tools/perf/tests/parse-events.c
tools/perf/tests/tests.h
tools/perf/tests/topology.c [new file with mode: 0644]
tools/perf/tests/vmlinux-kallsyms.c
tools/perf/trace/strace/groups/file
tools/perf/ui/browser.c
tools/perf/ui/browser.h
tools/perf/ui/browsers/annotate.c
tools/perf/ui/browsers/hists.c
tools/perf/ui/browsers/map.c
tools/perf/ui/browsers/scripts.c
tools/perf/ui/hist.c
tools/perf/ui/tui/setup.c
tools/perf/util/Build
tools/perf/util/annotate.c
tools/perf/util/annotate.h
tools/perf/util/auxtrace.c
tools/perf/util/auxtrace.h
tools/perf/util/bpf-loader.c [new file with mode: 0644]
tools/perf/util/bpf-loader.h [new file with mode: 0644]
tools/perf/util/callchain.c
tools/perf/util/callchain.h
tools/perf/util/cpumap.c
tools/perf/util/cpumap.h
tools/perf/util/env.c [new file with mode: 0644]
tools/perf/util/env.h [new file with mode: 0644]
tools/perf/util/event.c
tools/perf/util/event.h
tools/perf/util/evlist.c
tools/perf/util/evlist.h
tools/perf/util/evsel.c
tools/perf/util/evsel.h
tools/perf/util/header.c
tools/perf/util/header.h
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/include/dwarf-regs.h
tools/perf/util/intel-pt-decoder/Build
tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
tools/perf/util/intel-pt-decoder/intel-pt-log.c
tools/perf/util/intel-pt-decoder/intel-pt-log.h
tools/perf/util/intel-pt-decoder/x86-opcode-map.txt
tools/perf/util/intel-pt.c
tools/perf/util/machine.c
tools/perf/util/machine.h
tools/perf/util/map.c
tools/perf/util/map.h
tools/perf/util/parse-branch-options.c
tools/perf/util/parse-events.c
tools/perf/util/parse-events.h
tools/perf/util/parse-events.l
tools/perf/util/parse-events.y
tools/perf/util/parse-options.c
tools/perf/util/parse-options.h
tools/perf/util/pmu.c
tools/perf/util/probe-event.c
tools/perf/util/probe-event.h
tools/perf/util/probe-file.c
tools/perf/util/probe-file.h
tools/perf/util/probe-finder.c
tools/perf/util/python.c
tools/perf/util/scripting-engines/trace-event-perl.c
tools/perf/util/scripting-engines/trace-event-python.c
tools/perf/util/session.c
tools/perf/util/session.h
tools/perf/util/sort.c
tools/perf/util/sort.h
tools/perf/util/srcline.c
tools/perf/util/stat.c
tools/perf/util/stat.h
tools/perf/util/strbuf.c
tools/perf/util/strbuf.h
tools/perf/util/symbol-minimal.c
tools/perf/util/symbol.c
tools/perf/util/symbol.h
tools/perf/util/trace-event-info.c
tools/perf/util/trace-event.c
tools/perf/util/trace-event.h
tools/perf/util/unwind-libunwind.c
tools/perf/util/usage.c
tools/perf/util/util.c
tools/perf/util/util.h
tools/testing/selftests/rcutorture/bin/kvm.sh
tools/testing/selftests/rcutorture/configs/lock/CFLIST
tools/testing/selftests/rcutorture/configs/lock/LOCK05 [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/lock/LOCK05.boot [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/lock/LOCK06 [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/lock/LOCK06.boot [new file with mode: 0644]
tools/testing/selftests/timers/Makefile
tools/testing/selftests/timers/adjtick.c [new file with mode: 0644]
tools/testing/selftests/x86/Makefile
tools/testing/selftests/x86/entry_from_vm86.c
tools/testing/selftests/x86/ptrace_syscall.c [new file with mode: 0644]
tools/testing/selftests/x86/raw_syscall_helper_32.S [new file with mode: 0644]
tools/testing/selftests/x86/test_FCMOV.c [new file with mode: 0644]
tools/testing/selftests/x86/test_FCOMI.c [new file with mode: 0644]
tools/testing/selftests/x86/test_FISTTP.c [new file with mode: 0644]
tools/testing/selftests/x86/test_syscall_vdso.c [new file with mode: 0644]
tools/testing/selftests/x86/thunks_32.S [new file with mode: 0644]
tools/testing/selftests/x86/unwind_vdso.c [new file with mode: 0644]
tools/vm/page-types.c
virt/kvm/arm/vgic.c

index aac9357d4866bda01fcf2a0e532a5068b5d4bfbf..f9b9ad7894f580b9d6add880f9bfa4a702e35153 100644 (file)
 !Finclude/net/cfg80211.h cfg80211_scan_request
 !Finclude/net/cfg80211.h cfg80211_scan_done
 !Finclude/net/cfg80211.h cfg80211_bss
-!Finclude/net/cfg80211.h cfg80211_inform_bss_width_frame
-!Finclude/net/cfg80211.h cfg80211_inform_bss_width
+!Finclude/net/cfg80211.h cfg80211_inform_bss
+!Finclude/net/cfg80211.h cfg80211_inform_bss_frame_data
+!Finclude/net/cfg80211.h cfg80211_inform_bss_data
 !Finclude/net/cfg80211.h cfg80211_unlink_bss
 !Finclude/net/cfg80211.h cfg80211_find_ie
 !Finclude/net/cfg80211.h ieee80211_bss_get_ie
index 3a8e15cba816a4ea16fb0208518046214ebff1e6..8d990bde8693fdb85d4f4e67b5d9aaa155418990 100644 (file)
@@ -32,9 +32,9 @@ top of the irq_alloc_desc*() API.  An irq_domain to manage mapping is
 preferred over interrupt controller drivers open coding their own
 reverse mapping scheme.
 
-irq_domain also implements translation from Device Tree interrupt
-specifiers to hwirq numbers, and can be easily extended to support
-other IRQ topology data sources.
+irq_domain also implements translation from an abstract irq_fwspec
+structure to hwirq numbers (Device Tree and ACPI GSI so far), and can
+be easily extended to support other IRQ topology data sources.
 
 === irq_domain usage ===
 An interrupt controller driver creates and registers an irq_domain by
@@ -184,7 +184,7 @@ There are four major interfaces to use hierarchy irq_domain:
    related resources associated with these interrupts.
 3) irq_domain_activate_irq(): activate interrupt controller hardware to
    deliver the interrupt.
-3) irq_domain_deactivate_irq(): deactivate interrupt controller hardware
+4) irq_domain_deactivate_irq(): deactivate interrupt controller hardware
    to stop delivering the interrupt.
 
 Following changes are needed to support hierarchy irq_domain.
index efb9454875ab7453255532887225ad0cb2d5d2fd..0f7fb4298e7e0b657a7df678ee0852de7ab7887d 100644 (file)
@@ -205,6 +205,13 @@ o  For !CONFIG_PREEMPT kernels, a CPU looping anywhere in the
        behavior, you might need to replace some of the cond_resched()
        calls with calls to cond_resched_rcu_qs().
 
+o      Booting Linux using a console connection that is too slow to
+       keep up with the boot-time console-message rate.  For example,
+       a 115Kbaud serial console can be -way- too slow to keep up
+       with boot-time message rates, and will frequently result in
+       RCU CPU stall warning messages.  Especially if you have added
+       debug printk()s.
+
 o      Anything that prevents RCU's grace-period kthreads from running.
        This can result in the "All QSes seen" console-log message.
        This message will include information on when the kthread last
index dac02a6219b1cae3c657512b293b957627cec830..118e7c176ce76aaca534c50fe7fe01da1cb3b710 100644 (file)
@@ -166,40 +166,27 @@ test_no_idle_hz   Whether or not to test the ability of RCU to operate in
 
 torture_type   The type of RCU to test, with string values as follows:
 
-               "rcu":  rcu_read_lock(), rcu_read_unlock() and call_rcu().
-
-               "rcu_sync":  rcu_read_lock(), rcu_read_unlock(), and
-                       synchronize_rcu().
-
-               "rcu_expedited": rcu_read_lock(), rcu_read_unlock(), and
-                       synchronize_rcu_expedited().
+               "rcu":  rcu_read_lock(), rcu_read_unlock() and call_rcu(),
+                       along with expedited, synchronous, and polling
+                       variants.
 
                "rcu_bh": rcu_read_lock_bh(), rcu_read_unlock_bh(), and
-                       call_rcu_bh().
-
-               "rcu_bh_sync": rcu_read_lock_bh(), rcu_read_unlock_bh(),
-                       and synchronize_rcu_bh().
+                       call_rcu_bh(), along with expedited and synchronous
+                       variants.
 
-               "rcu_bh_expedited": rcu_read_lock_bh(), rcu_read_unlock_bh(),
-                       and synchronize_rcu_bh_expedited().
+               "rcu_busted": This tests an intentionally incorrect version
+                       of RCU in order to help test rcutorture itself.
 
                "srcu": srcu_read_lock(), srcu_read_unlock() and
-                       call_srcu().
-
-               "srcu_sync": srcu_read_lock(), srcu_read_unlock() and
-                       synchronize_srcu().
-
-               "srcu_expedited": srcu_read_lock(), srcu_read_unlock() and
-                       synchronize_srcu_expedited().
+                       call_srcu(), along with expedited and
+                       synchronous variants.
 
                "sched": preempt_disable(), preempt_enable(), and
-                       call_rcu_sched().
-
-               "sched_sync": preempt_disable(), preempt_enable(), and
-                       synchronize_sched().
+                       call_rcu_sched(), along with expedited,
+                       synchronous, and polling variants.
 
-               "sched_expedited": preempt_disable(), preempt_enable(), and
-                       synchronize_sched_expedited().
+               "tasks": voluntary context switch and call_rcu_tasks(),
+                       along with expedited and synchronous variants.
 
                Defaults to "rcu".
 
index 97f17e9decdaeced345689e9eb410a79f642b10d..ec6998b1b6d04f3139ed6c066537cc059c89838d 100644 (file)
@@ -56,14 +56,14 @@ rcuboost:
 
 The output of "cat rcu/rcu_preempt/rcudata" looks as follows:
 
-  0!c=30455 g=30456 pq=1/0 qp=1 dt=126535/140000000000000/0 df=2002 of=4 ql=0/0 qs=N... b=10 ci=74572 nci=0 co=1131 ca=716
-  1!c=30719 g=30720 pq=1/0 qp=0 dt=132007/140000000000000/0 df=1874 of=10 ql=0/0 qs=N... b=10 ci=123209 nci=0 co=685 ca=982
-  2!c=30150 g=30151 pq=1/1 qp=1 dt=138537/140000000000000/0 df=1707 of=8 ql=0/0 qs=N... b=10 ci=80132 nci=0 co=1328 ca=1458
-  3 c=31249 g=31250 pq=1/1 qp=0 dt=107255/140000000000000/0 df=1749 of=6 ql=0/450 qs=NRW. b=10 ci=151700 nci=0 co=509 ca=622
-  4!c=29502 g=29503 pq=1/0 qp=1 dt=83647/140000000000000/0 df=965 of=5 ql=0/0 qs=N... b=10 ci=65643 nci=0 co=1373 ca=1521
-  5 c=31201 g=31202 pq=1/0 qp=1 dt=70422/0/0 df=535 of=7 ql=0/0 qs=.... b=10 ci=58500 nci=0 co=764 ca=698
-  6!c=30253 g=30254 pq=1/0 qp=1 dt=95363/140000000000000/0 df=780 of=5 ql=0/0 qs=N... b=10 ci=100607 nci=0 co=1414 ca=1353
-  7 c=31178 g=31178 pq=1/0 qp=0 dt=91536/0/0 df=547 of=4 ql=0/0 qs=.... b=10 ci=109819 nci=0 co=1115 ca=969
+  0!c=30455 g=30456 cnq=1/0:1 dt=126535/140000000000000/0 df=2002 of=4 ql=0/0 qs=N... b=10 ci=74572 nci=0 co=1131 ca=716
+  1!c=30719 g=30720 cnq=1/0:0 dt=132007/140000000000000/0 df=1874 of=10 ql=0/0 qs=N... b=10 ci=123209 nci=0 co=685 ca=982
+  2!c=30150 g=30151 cnq=1/1:1 dt=138537/140000000000000/0 df=1707 of=8 ql=0/0 qs=N... b=10 ci=80132 nci=0 co=1328 ca=1458
+  3 c=31249 g=31250 cnq=1/1:0 dt=107255/140000000000000/0 df=1749 of=6 ql=0/450 qs=NRW. b=10 ci=151700 nci=0 co=509 ca=622
+  4!c=29502 g=29503 cnq=1/0:1 dt=83647/140000000000000/0 df=965 of=5 ql=0/0 qs=N... b=10 ci=65643 nci=0 co=1373 ca=1521
+  5 c=31201 g=31202 cnq=1/0:1 dt=70422/0/0 df=535 of=7 ql=0/0 qs=.... b=10 ci=58500 nci=0 co=764 ca=698
+  6!c=30253 g=30254 cnq=1/0:1 dt=95363/140000000000000/0 df=780 of=5 ql=0/0 qs=N... b=10 ci=100607 nci=0 co=1414 ca=1353
+  7 c=31178 g=31178 cnq=1/0:0 dt=91536/0/0 df=547 of=4 ql=0/0 qs=.... b=10 ci=109819 nci=0 co=1115 ca=969
 
 This file has one line per CPU, or eight for this 8-CPU system.
 The fields are as follows:
@@ -188,14 +188,14 @@ o "ca" is the number of RCU callbacks that have been adopted by this
 Kernels compiled with CONFIG_RCU_BOOST=y display the following from
 /debug/rcu/rcu_preempt/rcudata:
 
-  0!c=12865 g=12866 pq=1/0 qp=1 dt=83113/140000000000000/0 df=288 of=11 ql=0/0 qs=N... kt=0/O ktl=944 b=10 ci=60709 nci=0 co=748 ca=871
-  1 c=14407 g=14408 pq=1/0 qp=0 dt=100679/140000000000000/0 df=378 of=7 ql=0/119 qs=NRW. kt=0/W ktl=9b6 b=10 ci=109740 nci=0 co=589 ca=485
-  2 c=14407 g=14408 pq=1/0 qp=0 dt=105486/0/0 df=90 of=9 ql=0/89 qs=NRW. kt=0/W ktl=c0c b=10 ci=83113 nci=0 co=533 ca=490
-  3 c=14407 g=14408 pq=1/0 qp=0 dt=107138/0/0 df=142 of=8 ql=0/188 qs=NRW. kt=0/W ktl=b96 b=10 ci=121114 nci=0 co=426 ca=290
-  4 c=14405 g=14406 pq=1/0 qp=1 dt=50238/0/0 df=706 of=7 ql=0/0 qs=.... kt=0/W ktl=812 b=10 ci=34929 nci=0 co=643 ca=114
-  5!c=14168 g=14169 pq=1/0 qp=0 dt=45465/140000000000000/0 df=161 of=11 ql=0/0 qs=N... kt=0/O ktl=b4d b=10 ci=47712 nci=0 co=677 ca=722
-  6 c=14404 g=14405 pq=1/0 qp=0 dt=59454/0/0 df=94 of=6 ql=0/0 qs=.... kt=0/W ktl=e57 b=10 ci=55597 nci=0 co=701 ca=811
-  7 c=14407 g=14408 pq=1/0 qp=1 dt=68850/0/0 df=31 of=8 ql=0/0 qs=.... kt=0/W ktl=14bd b=10 ci=77475 nci=0 co=508 ca=1042
+  0!c=12865 g=12866 cnq=1/0:1 dt=83113/140000000000000/0 df=288 of=11 ql=0/0 qs=N... kt=0/O ktl=944 b=10 ci=60709 nci=0 co=748 ca=871
+  1 c=14407 g=14408 cnq=1/0:0 dt=100679/140000000000000/0 df=378 of=7 ql=0/119 qs=NRW. kt=0/W ktl=9b6 b=10 ci=109740 nci=0 co=589 ca=485
+  2 c=14407 g=14408 cnq=1/0:0 dt=105486/0/0 df=90 of=9 ql=0/89 qs=NRW. kt=0/W ktl=c0c b=10 ci=83113 nci=0 co=533 ca=490
+  3 c=14407 g=14408 cnq=1/0:0 dt=107138/0/0 df=142 of=8 ql=0/188 qs=NRW. kt=0/W ktl=b96 b=10 ci=121114 nci=0 co=426 ca=290
+  4 c=14405 g=14406 cnq=1/0:1 dt=50238/0/0 df=706 of=7 ql=0/0 qs=.... kt=0/W ktl=812 b=10 ci=34929 nci=0 co=643 ca=114
+  5!c=14168 g=14169 cnq=1/0:0 dt=45465/140000000000000/0 df=161 of=11 ql=0/0 qs=N... kt=0/O ktl=b4d b=10 ci=47712 nci=0 co=677 ca=722
+  6 c=14404 g=14405 cnq=1/0:0 dt=59454/0/0 df=94 of=6 ql=0/0 qs=.... kt=0/W ktl=e57 b=10 ci=55597 nci=0 co=701 ca=811
+  7 c=14407 g=14408 cnq=1/0:1 dt=68850/0/0 df=31 of=8 ql=0/0 qs=.... kt=0/W ktl=14bd b=10 ci=77475 nci=0 co=508 ca=1042
 
 This is similar to the output discussed above, but contains the following
 additional fields:
index adc2184009c5ecec9ba4615291ca8998743b48e2..dc49c6712b17ff4968d3c4fdf2b304e0292fb5be 100644 (file)
@@ -364,7 +364,7 @@ uses of RCU may be found in listRCU.txt, arrayRCU.txt, and NMI-RCU.txt.
        };
        DEFINE_SPINLOCK(foo_mutex);
 
-       struct foo *gbl_foo;
+       struct foo __rcu *gbl_foo;
 
        /*
         * Create a new struct foo that is the same as the one currently
@@ -386,7 +386,7 @@ uses of RCU may be found in listRCU.txt, arrayRCU.txt, and NMI-RCU.txt.
 
                new_fp = kmalloc(sizeof(*new_fp), GFP_KERNEL);
                spin_lock(&foo_mutex);
-               old_fp = gbl_foo;
+               old_fp = rcu_dereference_protected(gbl_foo, lockdep_is_held(&foo_mutex));
                *new_fp = *old_fp;
                new_fp->a = new_a;
                rcu_assign_pointer(gbl_foo, new_fp);
@@ -487,7 +487,7 @@ The foo_update_a() function might then be written as follows:
 
                new_fp = kmalloc(sizeof(*new_fp), GFP_KERNEL);
                spin_lock(&foo_mutex);
-               old_fp = gbl_foo;
+               old_fp = rcu_dereference_protected(gbl_foo, lockdep_is_held(&foo_mutex));
                *new_fp = *old_fp;
                new_fp->a = new_a;
                rcu_assign_pointer(gbl_foo, new_fp);
diff --git a/Documentation/arm/SA1100/Victor b/Documentation/arm/SA1100/Victor
deleted file mode 100644 (file)
index 9cff415..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-Victor is known as a "digital talking book player" manufactured by
-VisuAide, Inc. to be used by blind people.
-
-For more information related to Victor, see:
-
-       http://www.humanware.com/en-usa/products
-
-Of course Victor is using Linux as its main operating system.
-The Victor implementation for Linux is maintained by Nicolas Pitre:
-
-       nico@visuaide.com
-       nico@fluxnic.net
-
-For any comments, please feel free to contact me through the above
-addresses.
-
index 4178ebda6e665c1c2e9dac6451e2137acfd41eb1..546a39048eb0d6893a6d2fa81fe309bb5555ddbc 100644 (file)
@@ -54,7 +54,7 @@ VMALLOC_START VMALLOC_END-1   vmalloc() / ioremap() space.
                                located here through iotable_init().
                                VMALLOC_START is based upon the value
                                of the high_memory variable, and VMALLOC_END
-                               is equal to 0xff000000.
+                               is equal to 0xff800000.
 
 PAGE_OFFSET    high_memory-1   Kernel direct-mapped RAM region.
                                This maps the platforms RAM, and typically
index d60030a1b909bc68dbe7e6d275064cafbe3fdf4e..7b3fdfe0f7ba37a7ff6a0e46cfec18e1fcfbe68e 100644 (file)
@@ -60,5 +60,3 @@ linux,uefi-mmap-desc-ver  | 32-bit | Version of the mmap descriptor format.
 --------------------------------------------------------------------------------
 linux,uefi-stub-kern-ver  | string | Copy of linux_banner from build.
 --------------------------------------------------------------------------------
-
-For verbose debug messages, specify 'uefi_debug' on the kernel command line.
index 7d9d3c2286b282d96f03cd1545be5780e998002e..369a4f48eb0dac3c80d5f22054fc4b02f1006f28 100644 (file)
@@ -173,13 +173,22 @@ Before jumping into the kernel, the following conditions must be met:
   the kernel image will be entered must be initialised by software at a
   higher exception level to prevent execution in an UNKNOWN state.
 
-  For systems with a GICv3 interrupt controller:
+  For systems with a GICv3 interrupt controller to be used in v3 mode:
   - If EL3 is present:
     ICC_SRE_EL3.Enable (bit 3) must be initialiased to 0b1.
     ICC_SRE_EL3.SRE (bit 0) must be initialised to 0b1.
   - If the kernel is entered at EL1:
     ICC.SRE_EL2.Enable (bit 3) must be initialised to 0b1
     ICC_SRE_EL2.SRE (bit 0) must be initialised to 0b1.
+  - The DT or ACPI tables must describe a GICv3 interrupt controller.
+
+  For systems with a GICv3 interrupt controller to be used in
+  compatibility (v2) mode:
+  - If EL3 is present:
+    ICC_SRE_EL3.SRE (bit 0) must be initialised to 0b0.
+  - If the kernel is entered at EL1:
+    ICC_SRE_EL2.SRE (bit 0) must be initialised to 0b0.
+  - The DT or ACPI tables must describe a GICv2 interrupt controller.
 
 The requirements described above for CPU mode, caches, MMUs, architected
 timers, coherency and system registers apply to all CPUs.  All CPUs must
index b19fc34efdb17921af43bda0000b13dc82640451..c9d1cacb439590a02dbfb1282cbe0d3abe3a9b0d 100644 (file)
@@ -542,6 +542,10 @@ The routines xchg() and cmpxchg() must provide the same exact
 memory-barrier semantics as the atomic and bit operations returning
 values.
 
+Note: If someone wants to use xchg(), cmpxchg() and their variants,
+linux/atomic.h should be included rather than asm/cmpxchg.h, unless
+the code is in arch/* and can take care of itself.
+
 Spinlocks and rwlocks have memory barrier expectations as well.
 The rule to follow is simple:
 
index 2da059a4790cb3c62365bedae458c5565589b225..cc56021eb60babea6c580bfbe594aca3c17dfce5 100644 (file)
@@ -11,13 +11,14 @@ have PPIs or SGIs.
 Main node required properties:
 
 - compatible : should be one of:
-       "arm,gic-400"
+       "arm,arm1176jzf-devchip-gic"
+       "arm,arm11mp-gic"
        "arm,cortex-a15-gic"
-       "arm,cortex-a9-gic"
        "arm,cortex-a7-gic"
-       "arm,arm11mp-gic"
+       "arm,cortex-a9-gic"
+       "arm,gic-400"
+       "arm,pl390"
        "brcm,brahma-b15-gic"
-       "arm,arm1176jzf-devchip-gic"
        "qcom,msm-8660-qgic"
        "qcom,msm-qgic2"
 - interrupt-controller : Identifies the node as an interrupt controller
@@ -58,6 +59,21 @@ Optional
   regions, used when the GIC doesn't have banked registers. The offset is
   cpu-offset * cpu-nr.
 
+- clocks        : List of phandle and clock-specific pairs, one for each entry
+  in clock-names.
+- clock-names   : List of names for the GIC clock input(s). Valid clock names
+  depend on the GIC variant:
+       "ic_clk" (for "arm,arm11mp-gic")
+       "PERIPHCLKEN" (for "arm,cortex-a15-gic")
+       "PERIPHCLK", "PERIPHCLKEN" (for "arm,cortex-a9-gic")
+       "clk" (for "arm,gic-400")
+       "gclk" (for "arm,pl390")
+
+- power-domains : A phandle and PM domain specifier as defined by bindings of
+                 the power controller specified by phandle, used when the GIC
+                 is part of a Power or Clock Domain.
+
+
 Example:
 
        intc: interrupt-controller@fff11000 {
index 75b8610939faf819100c1e4fc7089b8542b753a2..383ea19c2bf0073e3a09ec4c254db28f17f28738 100644 (file)
@@ -19,6 +19,11 @@ interrupts.
 - reg : Specify the base address and the size of the TWD timer
        register window.
 
+Optional
+
+- always-on : a boolean property. If present, the timer is powered through
+  an always-on power domain, therefore it never loses context.
+
 Example:
 
        twd-timer@2c000600 {
index 78edb80002c814278d5d5dfe9a60c4812fe45e64..78e2a31c58d0735b40254e592e1fa7c73983d571 100644 (file)
@@ -5,6 +5,8 @@ The follow error types are supported:
 
   memory controller    - Memory controller
   PMD (L1/L2)          - Processor module unit (PMD) L1/L2 cache
+  L3                   - L3 cache controller
+  SoC                  - SoC IP's such as Ethernet, SATA, and etc
 
 The following section describes the EDAC DT node binding.
 
@@ -30,6 +32,17 @@ Required properties for PMD subnode:
 - reg                  : First resource shall be the PMD resource.
 - pmd-controller       : Instance number of the PMD controller.
 
+Required properties for L3 subnode:
+- compatible           : Shall be "apm,xgene-edac-l3" or
+                          "apm,xgene-edac-l3-v2".
+- reg                  : First resource shall be the L3 EDAC resource.
+
+Required properties for SoC subnode:
+- compatible           : Shall be "apm,xgene-edac-soc-v1" for revision 1 or
+                          "apm,xgene-edac-l3-soc" for general value reporting
+                          only.
+- reg                  : First resource shall be the SoC EDAC resource.
+
 Example:
        csw: csw@7e200000 {
                compatible = "apm,xgene-csw", "syscon";
@@ -76,4 +89,14 @@ Example:
                        reg = <0x0 0x7c000000 0x0 0x200000>;
                        pmd-controller = <0>;
                };
+
+               edacl3@7e600000 {
+                       compatible = "apm,xgene-edac-l3";
+                       reg = <0x0 0x7e600000 0x0 0x1000>;
+               };
+
+               edacsoc@7e930000 {
+                       compatible = "apm,xgene-edac-soc-v1";
+                       reg = <0x0 0x7e930000 0x0 0x1000>;
+               };
        };
diff --git a/Documentation/devicetree/bindings/gpio/gpio-msm.txt b/Documentation/devicetree/bindings/gpio/gpio-msm.txt
deleted file mode 100644 (file)
index ac20e68..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-MSM GPIO controller bindings
-
-Required properties:
-- compatible:
-  - "qcom,msm-gpio" for MSM controllers
-- #gpio-cells : Should be two.
-  - first cell is the pin number
-  - second cell is used to specify optional parameters (unused)
-- gpio-controller : Marks the device node as a GPIO controller.
-- #interrupt-cells : Should be 2.
-- interrupt-controller: Mark the device node as an interrupt controller
-- interrupts : Specify the TLMM summary interrupt number
-- ngpio : Specify the number of MSM GPIOs
-
-Example:
-
-       msmgpio: gpio@fd510000 {
-               compatible = "qcom,msm-gpio";
-               gpio-controller;
-               #gpio-cells = <2>;
-               interrupt-controller;
-               #interrupt-cells = <2>;
-               reg = <0xfd510000 0x4000>;
-               interrupts = <0 208 0>;
-               ngpio = <150>;
-       };
index b9a42f294dd01c63f97831c7487881aebd7d5d8f..13df9933f4cda74da94f2563675e6cd37ac97375 100644 (file)
@@ -24,6 +24,7 @@ Required properties:
        ti,tca6408
        ti,tca6416
        ti,tca6424
+       ti,tca9539
        exar,xra1202
 
 Example:
index db4c6a663c031280256d408e6a9f4bd15082a94b..7b542657f259577bc4e5fd5aa0b882053855bfad 100644 (file)
@@ -12,6 +12,13 @@ Required properties:
 - interrupts           : Interrupt specifier (see interrupt bindings for
                          details)
 - interrupt-parent     : Must be core interrupt controller
+- interrupt-controller : Marks the device node as an interrupt controller.
+- #interrupt-cells     : Should be 2.  The first cell is the GPIO number.
+                         The second cell bits[3:0] is used to specify trigger type and level flags:
+                             1 = low-to-high edge triggered.
+                             2 = high-to-low edge triggered.
+                             4 = active high level-sensitive.
+                             8 = active low level-sensitive.
 - reg                  : Address and length of the register set for the device
 
 Example:
@@ -22,5 +29,7 @@ Example:
                gpio-controller;
                interrupt-parent = <&intc>;
                interrupts = <0 20 4>;
+               interrupt-controller;
+               #interrupt-cells = <2>;
                reg = <0xe000a000 0x1000>;
        };
index 82d40e2505f641be635dd8769b0051508bcf0120..069cdf6f9dace228fe8fd97dc63c2d861273fc03 100644 (file)
@@ -54,9 +54,13 @@ only uses one.
 
 gpio-specifier may encode: bank, pin position inside the bank,
 whether pin is open-drain and whether pin is logically inverted.
+
 Exact meaning of each specifier cell is controller specific, and must
-be documented in the device tree binding for the device. Use the macros
-defined in include/dt-bindings/gpio/gpio.h whenever possible:
+be documented in the device tree binding for the device.
+
+Most controllers are however specifying a generic flag bitfield
+in the last cell, so for these, use the macros defined in
+include/dt-bindings/gpio/gpio.h whenever possible:
 
 Example of a node using GPIOs:
 
@@ -67,6 +71,15 @@ Example of a node using GPIOs:
 GPIO_ACTIVE_HIGH is 0, so in this example gpio-specifier is "18 0" and encodes
 GPIO pin number, and GPIO flags as accepted by the "qe_pio_e" gpio-controller.
 
+Optional standard bitfield specifiers for the last cell:
+
+- Bit 0: 0 means active high, 1 means active low
+- Bit 1: 1 means single-ended wiring, see:
+           https://en.wikipedia.org/wiki/Single-ended_triode
+          When used with active-low, this means open drain/collector, see:
+           https://en.wikipedia.org/wiki/Open_collector
+          When used with active-high, this means open source/emitter
+
 1.1) GPIO specifier best practices
 ----------------------------------
 
@@ -118,6 +131,30 @@ Every GPIO controller node must contain both an empty "gpio-controller"
 property, and a #gpio-cells integer property, which indicates the number of
 cells in a gpio-specifier.
 
+Optionally, a GPIO controller may have a "ngpios" property. This property
+indicates the number of in-use slots of available slots for GPIOs. The
+typical example is something like this: the hardware register is 32 bits
+wide, but only 18 of the bits have a physical counterpart. The driver is
+generally written so that all 32 bits can be used, but the IP block is reused
+in a lot of designs, some using all 32 bits, some using 18 and some using
+12. In this case, setting "ngpios = <18>;" informs the driver that only the
+first 18 GPIOs, at local offset 0 .. 17, are in use.
+
+If these GPIOs do not happen to be the first N GPIOs at offset 0...N-1, an
+additional bitmask is needed to specify which GPIOs are actually in use,
+and which are dummies. The bindings for this case has not yet been
+specified, but should be specified if/when such hardware appears.
+
+Example:
+
+gpio-controller@00000000 {
+       compatible = "foo";
+       reg = <0x00000000 0x1000>;
+       gpio-controller;
+       #gpio-cells = <2>;
+       ngpios = <18>;
+}
+
 The GPIO chip may contain GPIO hog definitions. GPIO hogging is a mechanism
 providing automatic GPIO request and configuration as part of the
 gpio-controller's driver probe function.
diff --git a/Documentation/devicetree/bindings/gpio/netxbig-gpio-ext.txt b/Documentation/devicetree/bindings/gpio/netxbig-gpio-ext.txt
new file mode 100644 (file)
index 0000000..50ec2e6
--- /dev/null
@@ -0,0 +1,22 @@
+Binding for the GPIO extension bus found on some LaCie/Seagate boards
+(Example: 2Big/5Big Network v2, 2Big NAS).
+
+Required properties:
+- compatible: "lacie,netxbig-gpio-ext".
+- addr-gpios: GPIOs representing the address register (LSB -> MSB).
+- data-gpios: GPIOs representing the data register (LSB -> MSB).
+- enable-gpio: latches the new configuration (address, data) on raising edge.
+
+Example:
+
+netxbig_gpio_ext: netxbig-gpio-ext {
+       compatible = "lacie,netxbig-gpio-ext";
+
+       addr-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH
+                     &gpio1 16 GPIO_ACTIVE_HIGH
+                     &gpio1 17 GPIO_ACTIVE_HIGH>;
+       data-gpios = <&gpio1 12 GPIO_ACTIVE_HIGH
+                     &gpio1 13 GPIO_ACTIVE_HIGH
+                     &gpio1 14 GPIO_ACTIVE_HIGH>;
+       enable-gpio = <&gpio0 29 GPIO_ACTIVE_HIGH>;
+};
index 63633bdea7e40ea84aca7954a2d671a5d9163ad8..ae5054c27c996564b9675fac7d245bd6af28b0bc 100644 (file)
@@ -10,6 +10,7 @@ Required properties:
     - "renesas,irqc-r8a7792" (R-Car V2H)
     - "renesas,irqc-r8a7793" (R-Car M2-N)
     - "renesas,irqc-r8a7794" (R-Car E2)
+    - "renesas,intc-ex-r8a7795" (R-Car H3)
 - #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
   interrupts.txt in this directory
 - clocks: Must contain a reference to the functional clock.
index c05ed91a4e42803abccc9769848a3d3fb16917b7..85c0c58617f6da50d3ac0f7f4177df3d9aeb68ab 100644 (file)
@@ -27,9 +27,9 @@ Required properties of the LED child node:
 - flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
                        Maximum flash LED supply current can be calculated using
                        following formula: I = 1A * 162kohm / Rset.
-- flash-timeout-us : see Documentation/devicetree/bindings/leds/common.txt
-                     Maximum flash timeout can be calculated using following
-                     formula: T = 8.82 * 10^9 * Ct.
+- flash-max-timeout-us : see Documentation/devicetree/bindings/leds/common.txt
+                         Maximum flash timeout can be calculated using following
+                         formula: T = 8.82 * 10^9 * Ct.
 
 Optional properties of the LED child node:
 - label : see Documentation/devicetree/bindings/leds/common.txt
@@ -54,7 +54,7 @@ aat1290 {
                label = "aat1290-flash";
                led-max-microamp = <520833>;
                flash-max-microamp = <1012500>;
-               flash-timeout-us = <1940000>;
+               flash-max-timeout-us = <1940000>;
        };
 };
 
index f9e36adc0ebf9715594d2f298d86f221a30bd83b..3f48c1eaf0857799c6dd815340e83b0dc301f100 100644 (file)
@@ -29,6 +29,14 @@ Required properties:
 Optional properties:
   - brcm,serial-leds : Boolean, enables Serial LEDs.
     Default : false
+  - brcm,serial-mux : Boolean, enables Serial LEDs multiplexing.
+    Default : false
+  - brcm,serial-clk-low : Boolean, makes clock signal active low.
+    Default : false
+  - brcm,serial-dat-low : Boolean, makes data signal active low.
+    Default : false
+  - brcm,serial-shift-inv : Boolean, inverts Serial LEDs shift direction.
+    Default : false
 
 Each LED is represented as a sub-node of the brcm,bcm6328-leds device.
 
@@ -110,6 +118,8 @@ Scenario 2 : BCM63268 with Serial/GPHY0 LEDs
                #size-cells = <0>;
                reg = <0x10001900 0x24>;
                brcm,serial-leds;
+               brcm,serial-dat-low;
+               brcm,serial-shift-inv;
 
                gphy0_spd0@0 {
                        reg = <0>;
diff --git a/Documentation/devicetree/bindings/leds/leds-netxbig.txt b/Documentation/devicetree/bindings/leds/leds-netxbig.txt
new file mode 100644 (file)
index 0000000..5ef92a2
--- /dev/null
@@ -0,0 +1,92 @@
+Binding for the CPLD LEDs (GPIO extension bus) found on some LaCie/Seagate
+boards (Example: 2Big/5Big Network v2, 2Big NAS).
+
+Required properties:
+- compatible: "lacie,netxbig-leds".
+- gpio-ext: Phandle for the gpio-ext bus.
+
+Optional properties:
+- timers: Timer array. Each timer entry is represented by three integers:
+  Mode (gpio-ext bus), delay_on and delay_off.
+
+Each LED is represented as a sub-node of the netxbig-leds device.
+
+Required sub-node properties:
+- mode-addr: Mode register address on gpio-ext bus.
+- mode-val: Mode to value mapping. Each entry is represented by two integers:
+  A mode and the corresponding value on the gpio-ext bus.
+- bright-addr: Brightness register address on gpio-ext bus.
+- max-brightness: Maximum brightness value.
+
+Optional sub-node properties:
+- label: Name for this LED. If omitted, the label is taken from the node name.
+- linux,default-trigger: Trigger assigned to the LED.
+
+Example:
+
+netxbig-leds {
+       compatible = "lacie,netxbig-leds";
+
+       gpio-ext = &gpio_ext;
+
+       timers = <NETXBIG_LED_TIMER1 500 500
+                 NETXBIG_LED_TIMER2 500 1000>;
+
+       blue-power {
+               label = "netxbig:blue:power";
+               mode-addr = <0>;
+               mode-val = <NETXBIG_LED_OFF 0
+                           NETXBIG_LED_ON 1
+                           NETXBIG_LED_TIMER1 3
+                           NETXBIG_LED_TIMER2 7>;
+               bright-addr = <1>;
+               max-brightness = <7>;
+       };
+       red-power {
+               label = "netxbig:red:power";
+               mode-addr = <0>;
+               mode-val = <NETXBIG_LED_OFF 0
+                           NETXBIG_LED_ON 2
+                           NETXBIG_LED_TIMER1 4>;
+               bright-addr = <1>;
+               max-brightness = <7>;
+       };
+       blue-sata0 {
+               label = "netxbig:blue:sata0";
+               mode-addr = <3>;
+               mode-val = <NETXBIG_LED_OFF 0
+                           NETXBIG_LED_ON 7
+                           NETXBIG_LED_SATA 1
+                           NETXBIG_LED_TIMER1 3>;
+               bright-addr = <2>;
+               max-brightness = <7>;
+       };
+       red-sata0 {
+               label = "netxbig:red:sata0";
+               mode-addr = <3>;
+               mode-val = <NETXBIG_LED_OFF 0
+                           NETXBIG_LED_ON 2
+                           NETXBIG_LED_TIMER1 4>;
+               bright-addr = <2>;
+               max-brightness = <7>;
+       };
+       blue-sata1 {
+               label = "netxbig:blue:sata1";
+               mode-addr = <4>;
+               mode-val = <NETXBIG_LED_OFF 0
+                           NETXBIG_LED_ON 7
+                           NETXBIG_LED_SATA 1
+                           NETXBIG_LED_TIMER1 3>;
+               bright-addr = <2>;
+               max-brightness = <7>;
+       };
+       red-sata1 {
+               label = "netxbig:red:sata1";
+               mode-addr = <4>;
+               mode-val = <NETXBIG_LED_OFF 0
+                           NETXBIG_LED_ON 2
+                           NETXBIG_LED_TIMER1 4>;
+               bright-addr = <2>;
+               max-brightness = <7>;
+       };
+};
index b7943f3f999546cbaa1ec4170299c9221030427d..dedfb02c744a9b03277cbe42e95237eb3c38b24b 100644 (file)
@@ -22,6 +22,8 @@ Optional properties:
   - voltage-ranges : two cells are required, first cell specifies minimum
     slot voltage (mV), second cell specifies maximum slot voltage (mV).
     Several ranges could be specified.
+  - little-endian : If the host controller is little-endian mode, specify
+    this property. The default endian mode is big-endian.
 
 Example:
 
index 0384fc3f64e83f954183afca1c899fe65966a481..f693baf87264d6525b105c0eb244730111b47820 100644 (file)
@@ -37,6 +37,7 @@ Optional properties:
 - sd-uhs-sdr104: SD UHS SDR104 speed is supported
 - sd-uhs-ddr50: SD UHS DDR50 speed is supported
 - cap-power-off-card: powering off the card is safe
+- cap-mmc-hw-reset: eMMC hardware reset is supported
 - cap-sdio-irq: enable SDIO IRQ signalling on this interface
 - full-pwr-cycle: full power cycle of the card is supported
 - mmc-ddr-1_8v: eMMC high-speed DDR mode(1.8V I/O) is supported
index a1adfa495ad3d7ce60ea17da34fd3d9e22d4beaf..0120c7f1109cb2cf830b002f9e77e774ddbdef79 100644 (file)
@@ -17,6 +17,11 @@ Required properties:
 - vmmc-supply: power to the Core
 - vqmmc-supply: power to the IO
 
+Optional properties:
+- assigned-clocks: PLL of the source clock
+- assigned-clock-parents: parent of source clock, used for HS400 mode to get 400Mhz source clock
+- hs400-ds-delay: HS400 DS delay setting
+
 Examples:
 mmc0: mmc@11230000 {
        compatible = "mediatek,mt8173-mmc", "mediatek,mt8135-mmc";
@@ -24,9 +29,13 @@ mmc0: mmc@11230000 {
        interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_LOW>;
        vmmc-supply = <&mt6397_vemc_3v3_reg>;
        vqmmc-supply = <&mt6397_vio18_reg>;
-       clocks = <&pericfg CLK_PERI_MSDC30_0>, <&topckgen CLK_TOP_MSDC50_0_H_SEL>;
+       clocks = <&pericfg CLK_PERI_MSDC30_0>,
+                <&topckgen CLK_TOP_MSDC50_0_H_SEL>;
        clock-names = "source", "hclk";
        pinctrl-names = "default", "state_uhs";
        pinctrl-0 = <&mmc0_pins_default>;
        pinctrl-1 = <&mmc0_pins_uhs>;
+       assigned-clocks = <&topckgen CLK_TOP_MSDC50_0_SEL>;
+       assigned-clock-parents = <&topckgen CLK_TOP_MSDCPLL_D2>;
+       hs400-ds-delay = <0x14015>;
 };
index d38942f6c5ae8ef1b43272bf21ab010145a7fe21..cae29eb5733d8a529d2ec2c3606e4c2255a01884 100644 (file)
@@ -6,11 +6,12 @@ and the properties used by the MMCIF device.
 
 Required properties:
 
-- compatible: must contain one of the following
+- compatible: should be "renesas,mmcif-<soctype>", "renesas,sh-mmcif" as a
+  fallback. Examples with <soctype> are:
        - "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs
        - "renesas,mmcif-r8a7790" for the MMCIF found in r8a7790 SoCs
        - "renesas,mmcif-r8a7791" for the MMCIF found in r8a7791 SoCs
-       - "renesas,sh-mmcif" for the generic MMCIF
+       - "renesas,mmcif-r8a7794" for the MMCIF found in r8a7794 SoCs
 
 - clocks: reference to the functional clock
 
index c327c2d6f23d7f043799fd9bd4e1e9d94a7b4322..3dc13b68fc3ffb6dd136038f9116a96fbde996c7 100644 (file)
@@ -14,6 +14,19 @@ Required Properties:
                                                        before RK3288
        - "rockchip,rk3288-dw-mshc": for Rockchip RK3288
 
+Optional Properties:
+* clocks: from common clock binding: if ciu_drive and ciu_sample are
+  specified in clock-names, should contain handles to these clocks.
+
+* clock-names: Apart from the clock-names described in synopsys-dw-mshc.txt
+  two more clocks "ciu-drive" and "ciu-sample" are supported. They are used
+  to control the clock phases, "ciu-sample" is required for tuning high-
+  speed modes.
+
+* rockchip,default-sample-phase: The default phase to set ciu_sample at
+  probing, low speeds or in case where all phases work at tuning time.
+  If not specified 0 deg will be used.
+
 Example:
 
        rkdwmmc0@12200000 {
index 346c6095a6155138ad01f2d39374a8ed97224d12..8636f5ae97e5157ba868e4ddc8bf505465e7bcb3 100644 (file)
@@ -75,6 +75,12 @@ Optional properties:
 * vmmc-supply: The phandle to the regulator to use for vmmc.  If this is
   specified we'll defer probe until we can find this regulator.
 
+* dmas: List of DMA specifiers with the controller specific format as described
+  in the generic DMA client binding. Refer to dma.txt for details.
+
+* dma-names: request names for generic DMA client binding. Must be "rx-tx".
+  Refer to dma.txt for details.
+
 Aliases:
 
 - All the MSHC controller nodes should be represented in the aliases node using
@@ -95,6 +101,23 @@ board specific portions as listed below.
                #size-cells = <0>;
        };
 
+[board specific internal DMA resources]
+
+       dwmmc0@12200000 {
+               clock-frequency = <400000000>;
+               clock-freq-min-max = <400000 200000000>;
+               num-slots = <1>;
+               broken-cd;
+               fifo-depth = <0x80>;
+               card-detect-delay = <200>;
+               vmmc-supply = <&buck8>;
+               bus-width = <8>;
+               cap-mmc-highspeed;
+               cap-sd-highspeed;
+       };
+
+[board specific generic DMA request binding]
+
        dwmmc0@12200000 {
                clock-frequency = <400000000>;
                clock-freq-min-max = <400000 200000000>;
@@ -106,4 +129,6 @@ board specific portions as listed below.
                bus-width = <8>;
                cap-mmc-highspeed;
                cap-sd-highspeed;
+               dmas = <&pdma 12>;
+               dma-names = "rx-tx";
        };
index f55aa280d34f9fae6e25170f41a06e5c13104f06..078060a97f95e677789fc7f6fd5f00904424d66b 100644 (file)
@@ -37,6 +37,14 @@ Required properties for ethernet interfaces that have external PHY:
 
 Optional properties:
 - status: Should be "ok" or "disabled" for enabled/disabled. Default is "ok".
+- tx-delay: Delay value for RGMII bridge TX clock.
+           Valid values are between 0 to 7, that maps to
+           417, 717, 1020, 1321, 1611, 1913, 2215, 2514 ps
+           Default value is 4, which corresponds to 1611 ps
+- rx-delay: Delay value for RGMII bridge RX clock.
+           Valid values are between 0 to 7, that maps to
+           273, 589, 899, 1222, 1480, 1806, 2147, 2464 ps
+           Default value is 2, which corresponds to 899 ps
 
 Example:
        menetclk: menetclk {
@@ -72,5 +80,7 @@ Example:
 
 /* Board-specific peripheral configurations */
 &menet {
+       tx-delay = <4>;
+       rx-delay = <2>;
         status = "ok";
 };
diff --git a/Documentation/devicetree/bindings/net/brcm,iproc-mdio.txt b/Documentation/devicetree/bindings/net/brcm,iproc-mdio.txt
new file mode 100644 (file)
index 0000000..8ba9ed1
--- /dev/null
@@ -0,0 +1,23 @@
+* Broadcom iProc MDIO bus controller
+
+Required properties:
+- compatible: should be "brcm,iproc-mdio"
+- reg: address and length of the register set for the MDIO interface
+- #size-cells: must be 1
+- #address-cells: must be 0
+
+Child nodes of this MDIO bus controller node are standard Ethernet PHY device
+nodes as described in Documentation/devicetree/bindings/net/phy.txt
+
+Example:
+
+mdio@18002000 {
+       compatible = "brcm,iproc-mdio";
+       reg = <0x18002000 0x8>;
+       #size-cells = <1>;
+       #address-cells = <0>;
+
+       enet-gphy@0 {
+               reg = <0>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/net/can/sun4i_can.txt b/Documentation/devicetree/bindings/net/can/sun4i_can.txt
new file mode 100644 (file)
index 0000000..84ed190
--- /dev/null
@@ -0,0 +1,36 @@
+Allwinner A10/A20 CAN controller Device Tree Bindings
+-----------------------------------------------------
+
+Required properties:
+- compatible: "allwinner,sun4i-a10-can"
+- reg: physical base address and size of the Allwinner A10/A20 CAN register map.
+- interrupts: interrupt specifier for the sole interrupt.
+- clock: phandle and clock specifier.
+
+Example
+-------
+
+SoC common .dtsi file:
+
+       can0_pins_a: can0@0 {
+               allwinner,pins = "PH20","PH21";
+               allwinner,function = "can";
+               allwinner,drive = <0>;
+               allwinner,pull = <0>;
+       };
+...
+       can0: can@01c2bc00 {
+               compatible = "allwinner,sun4i-a10-can";
+               reg = <0x01c2bc00 0x400>;
+               interrupts = <0 26 4>;
+               clocks = <&apb1_gates 4>;
+               status = "disabled";
+       };
+
+Board specific .dts file:
+
+       can0: can@01c2bc00 {
+               pinctrl-names = "default";
+               pinctrl-0 = <&can0_pins_a>;
+               status = "okay";
+       };
index a9df21aaa1548a64da26c74dfbbef13e652cf3da..4efca560adda4b22f0498f123f053650e4427da5 100644 (file)
@@ -30,6 +30,13 @@ Optional properties:
 - dual_emac            : Specifies Switch to act as Dual EMAC
 - syscon               : Phandle to the system control device node, which is
                          the control module device of the am33x
+- mode-gpios           : Should be added if one/multiple gpio lines are
+                         required to be driven so that cpsw data lines
+                         can be connected to the phy via selective mux.
+                         For example in dra72x-evm, pcf gpio has to be
+                         driven low so that cpsw slave 0 and phy data
+                         lines are connected via mux.
+
 
 Slave Properties:
 Required properties:
@@ -39,6 +46,7 @@ Required properties:
 Optional properties:
 - dual_emac_res_vlan   : Specifies VID to be used to segregate the ports
 - mac-address          : See ethernet.txt file in the same directory
+- phy-handle           : See ethernet.txt file in the same directory
 
 Note: "ti,hwmods" field is used to fetch the base address and irq
 resources from TI, omap hwmod data base during device registration.
index 1e97532a0b792603477ce3e63c5a415c025dc8b0..db74f0dc290c0835b0194d7de57215bc9b74bdc5 100644 (file)
@@ -57,6 +57,10 @@ Properties:
     "rgmii-id", as all other connection types are detected by hardware.
   - fsl,magic-packet : If present, indicates that the hardware supports
     waking up via magic packet.
+  - fsl,wake-on-filer : If present, indicates that the hardware supports
+    waking up by Filer General Purpose Interrupt (FGPI) asserted on the
+    Rx int line.  This is an advanced power management capability allowing
+    certain packet types (user) defined by filer rules to wake up the system.
   - bd-stash : If present, indicates that the hardware supports stashing
     buffer descriptors in the L2.
   - rx-stash-len : Denotes the number of bytes of a received buffer to stash
index 988fc694b663fdde71b23e73051085f0c034c6b5..d1df8a00e1f3b7828cfb66d2706dd81316343384 100644 (file)
@@ -32,13 +32,13 @@ Required properties:
 
 Required properties:
 
-- compatible: should be "hisilicon,hip04-mdio".
+- compatible: should be "hisilicon,mdio".
 - Inherits from MDIO bus node binding [2]
 [2] Documentation/devicetree/bindings/net/phy.txt
 
 Example:
        mdio {
-               compatible = "hisilicon,hip04-mdio";
+               compatible = "hisilicon,mdio";
                reg = <0x28f1000 0x1000>;
                #address-cells = <1>;
                #size-cells = <0>;
diff --git a/Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt b/Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt
new file mode 100644 (file)
index 0000000..80411b2
--- /dev/null
@@ -0,0 +1,49 @@
+Hisilicon DSA Fabric device controller
+
+Required properties:
+- compatible: should be "hisilicon,hns-dsaf-v1" or "hisilicon,hns-dsaf-v2".
+  "hisilicon,hns-dsaf-v1" is for hip05.
+  "hisilicon,hns-dsaf-v2" is for Hi1610 and Hi1612.
+- dsa-name: dsa fabric name who provide this interface.
+  should be "dsafX", X is the dsaf id.
+- mode: dsa fabric mode string. only support one of dsaf modes like these:
+               "2port-64vf",
+               "6port-16rss",
+               "6port-16vf".
+- interrupt-parent: the interrupt parent of this device.
+- interrupts: should contain the DSA Fabric and rcb interrupt.
+- reg: specifies base physical address(es) and size of the device registers.
+  The first region is external interface control register base and size.
+  The second region is SerDes base register and size.
+  The third region is the PPE register base and size.
+  The fourth region is dsa fabric base register and size.
+  The fifth region is cpld base register and size, it is not required if do not use cpld.
+- phy-handle: phy handle of physicl port, 0 if not any phy device. see ethernet.txt [1].
+- buf-size: rx buffer size, should be 16-1024.
+- desc-num: number of description in TX and RX queue, should be 512, 1024, 2048 or 4096.
+
+[1] Documentation/devicetree/bindings/net/phy.txt
+
+Example:
+
+dsa: dsa@c7000000 {
+       compatible = "hisilicon,hns-dsaf-v1";
+       dsa_name = "dsaf0";
+       mode = "6port-16rss";
+       interrupt-parent = <&mbigen_dsa>;
+       reg = <0x0 0xC0000000 0x0 0x420000
+              0x0 0xC2000000 0x0 0x300000
+              0x0 0xc5000000 0x0 0x890000
+              0x0 0xc7000000 0x0 0x60000>;
+       phy-handle = <0 0 0 0 &soc0_phy4 &soc0_phy5 0 0>;
+       interrupts = <131 4>,<132 4>, <133 4>,<134 4>,
+                    <135 4>,<136 4>, <137 4>,<138 4>,
+                    <139 4>,<140 4>, <141 4>,<142 4>,
+                    <143 4>,<144 4>, <145 4>,<146 4>,
+                    <147 4>,<148 4>, <384 1>,<385 1>,
+                    <386 1>,<387 1>, <388 1>,<389 1>,
+                    <390 1>,<391 1>,
+       buf-size = <4096>;
+       desc-num = <1024>;
+       dma-coherent;
+};
diff --git a/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt b/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt
new file mode 100644 (file)
index 0000000..9c23fdf
--- /dev/null
@@ -0,0 +1,22 @@
+Hisilicon MDIO bus controller
+
+Properties:
+- compatible: "hisilicon,mdio","hisilicon,hns-mdio".
+- reg: The base address of the MDIO bus controller register bank.
+- #address-cells: Must be <1>.
+- #size-cells: Must be <0>.  MDIO addresses have no size component.
+
+Typically an MDIO bus might have several children.
+
+Example:
+         mdio@803c0000 {
+                   #address-cells = <1>;
+                   #size-cells = <0>;
+                   compatible = "hisilicon,hns-mdio","hisilicon,mdio";
+                   reg = <0x0 0x803c0000 0x0 0x10000>;
+
+                   ethernet-phy@0 {
+                            ...
+                            reg = <0>;
+                   };
+         };
diff --git a/Documentation/devicetree/bindings/net/hisilicon-hns-nic.txt b/Documentation/devicetree/bindings/net/hisilicon-hns-nic.txt
new file mode 100644 (file)
index 0000000..41d19be
--- /dev/null
@@ -0,0 +1,47 @@
+Hisilicon Network Subsystem NIC controller
+
+Required properties:
+- compatible: "hisilicon,hns-nic-v1" or "hisilicon,hns-nic-v2".
+  "hisilicon,hns-nic-v1" is for hip05.
+  "hisilicon,hns-nic-v2" is for Hi1610 and Hi1612.
+- ae-name: accelerator name who provides this interface,
+  is simply a name referring to the name of name in the accelerator node.
+- port-id: is the index of port provided by DSAF (the accelerator). DSAF can
+  connect to 8 PHYs. Port 0 to 1 are both used for adminstration purpose. They
+  are called debug ports.
+
+  The remaining 6 PHYs are taken according to the mode of DSAF.
+
+  In NIC mode of DSAF, all 6 PHYs are taken as ethernet ports to the CPU. The
+  port-id can be 2 to 7. Here is the diagram:
+            +-----+---------------+
+            |            CPU      |
+            +-+-+-+---+-+-+-+-+-+-+
+              | |     | | | | | |
+             debug       service
+             port         port
+             (0,1)       (2-7)
+
+  In Switch mode of DSAF, all 6 PHYs are taken as physical ports connect to a
+  LAN Switch while the CPU side assume itself have one single NIC connect to
+  this switch. In this case, the port-id will be 2 only.
+            +-----+---------------+
+            |            CPU      |
+            +-+-+-+---+-+-+-+-+-+-+
+              | |   service| port(2)
+             debug   +------------+
+             port    |   switch   |
+             (0,1)   +-+-+-+-+-+-++
+                       | | | | | |
+                      external port
+
+- local-mac-address: mac addr of the ethernet interface
+
+Example:
+
+       ethernet@0{
+               compatible = "hisilicon,hns-nic-v1";
+               ae-name = "dsaf0";
+               port-id = <0>;
+               local-mac-address = [a2 14 e4 4b 56 76];
+       };
diff --git a/Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt b/Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt
new file mode 100644 (file)
index 0000000..a4ed2ef
--- /dev/null
@@ -0,0 +1,20 @@
+* MRF24J40 IEEE 802.15.4 *
+
+Required properties:
+  - compatible:                should be "microchip,mrf24j40", "microchip,mrf24j40ma",
+                       or "microchip,mrf24j40mc" depends on your transceiver
+                       board
+  - spi-max-frequency: maximal bus speed, should be set something under or equal
+                       10000000
+  - reg:               the chipselect index
+  - interrupts:                the interrupt generated by the device.
+
+Example:
+
+       mrf24j40ma@0 {
+               compatible = "microchip,mrf24j40ma";
+               spi-max-frequency = <8500000>;
+               reg = <0>;
+               interrupts = <19 8>;
+               interrupt-parent = <&gpio3>;
+       };
index 7c4a0cc370cf42386ed5e8a979b631280f3d81a5..76df9173825a00e39cca99a9a8c35bc51f2b57b8 100644 (file)
@@ -1,7 +1,10 @@
 * Marvell International Ltd. NCI NFC Controller
 
 Required properties:
-- compatible: Should be "mrvl,nfc-uart".
+- compatible: Should be:
+  - "marvell,nfc-uart" or "mrvl,nfc-uart" for UART devices
+  - "marvell,nfc-i2c" for I2C devices
+  - "marvell,nfc-spi" for SPI devices
 
 Optional SoC specific properties:
 - pinctrl-names: Contains only one value - "default".
@@ -13,13 +16,19 @@ Optional UART-based chip specific properties:
 - flow-control: Specifies that the chip is using RTS/CTS.
 - break-control: Specifies that the chip needs specific break management.
 
+Optional I2C-based chip specific properties:
+- i2c-int-falling: Specifies that the chip read event shall be trigged on
+                  falling edge.
+- i2c-int-rising: Specifies that the chip read event shall be trigged on
+                 rising edge.
+
 Example (for ARM-based BeagleBoard Black with 88W8887 on UART5):
 
 &uart5 {
        status = "okay";
 
        nfcmrvluart: nfcmrvluart@5 {
-               compatible = "mrvl,nfc-uart";
+               compatible = "marvell,nfc-uart";
 
                reset-n-io = <&gpio3 16 0>;
 
@@ -27,3 +36,51 @@ Example (for ARM-based BeagleBoard Black with 88W8887 on UART5):
                flow-control;
         }
 };
+
+
+Example (for ARM-based BeagleBoard Black with 88W8887 on I2C1):
+
+&i2c1 {
+       status = "okay";
+       clock-frequency = <400000>;
+
+       nfcmrvli2c0: i2c@1 {
+               compatible = "marvell,nfc-i2c";
+
+               reg = <0x8>;
+
+               /* I2C INT configuration */
+               interrupt-parent = <&gpio3>;
+               interrupts = <21 0>;
+
+               /* I2C INT trigger configuration */
+               i2c-int-rising;
+
+               /* Reset IO */
+               reset-n-io = <&gpio3 19 0>;
+       };
+};
+
+
+Example (for ARM-based BeagleBoard Black on SPI0):
+
+&spi0 {
+
+       mrvlnfcspi0: spi@0 {
+               compatible = "marvell,nfc-spi";
+
+               reg = <0>;
+
+               /* SPI Bus configuration */
+               spi-max-frequency = <3000000>;
+               spi-cpha;
+               spi-cpol;
+
+               /* SPI INT configuration */
+               interrupt-parent = <&gpio1>;
+               interrupts = <17 0>;
+
+               /* Reset IO */
+                       reset-n-io = <&gpio3 19 0>;
+       };
+};
index d707588ed734969537f4bcf5d64956315704ed47..263732e8879f1ecd249f7427c78583b40a24c913 100644 (file)
@@ -11,6 +11,10 @@ Required properties:
 Optional SoC Specific Properties:
 - pinctrl-names: Contains only one value - "default".
 - pintctrl-0: Specifies the pin control groups used for this controller.
+- ese-present: Specifies that an ese is physically connected to the nfc
+controller.
+- uicc-present: Specifies that the uicc swp signal can be physically
+connected to the nfc controller.
 
 Example (for ARM-based BeagleBoard xM with ST21NFCB on I2C2):
 
@@ -29,5 +33,8 @@ Example (for ARM-based BeagleBoard xM with ST21NFCB on I2C2):
                interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
 
                reset-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
+
+               ese-present;
+               uicc-present;
        };
 };
index 525681b6dc39e8aff6332a1f2afe7061369a9c1c..711ca85a363d2e501cb2c936d4b16473aee30f30 100644 (file)
@@ -2,7 +2,7 @@
 
 Required properties:
 - compatible: Should be "st,st21nfcb-spi"
-- spi-max-frequency: Maximum SPI frequency (<= 10000000).
+- spi-max-frequency: Maximum SPI frequency (<= 4000000).
 - interrupt-parent: phandle for the interrupt gpio controller
 - interrupts: GPIO interrupt to which the chip is connected
 - reset-gpios: Output GPIO pin used to reset the ST21NFCB
@@ -10,6 +10,10 @@ Required properties:
 Optional SoC Specific Properties:
 - pinctrl-names: Contains only one value - "default".
 - pintctrl-0: Specifies the pin control groups used for this controller.
+- ese-present: Specifies that an ese is physically connected to the nfc
+controller.
+- uicc-present: Specifies that the uicc swp signal can be physically
+connected to the nfc controller.
 
 Example (for ARM-based BeagleBoard xM with ST21NFCB on SPI4):
 
@@ -27,5 +31,8 @@ Example (for ARM-based BeagleBoard xM with ST21NFCB on SPI4):
                interrupts = <2 IRQ_TYPE_EDGE_RISING>;
 
                reset-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
+
+               ese-present;
+               uicc-present;
        };
 };
index 1fd8831437bf599b0a129ee769dd770ea0094693..b486f3f5f6a3264037c07d05d335b1737e8d455e 100644 (file)
@@ -6,8 +6,12 @@ interface contains.
 Required properties:
 - compatible: "renesas,etheravb-r8a7790" if the device is a part of R8A7790 SoC.
              "renesas,etheravb-r8a7794" if the device is a part of R8A7794 SoC.
+             "renesas,etheravb-r8a7795" if the device is a part of R8A7795 SoC.
 - reg: offset and length of (1) the register block and (2) the stream buffer.
-- interrupts: interrupt specifier for the sole interrupt.
+- interrupts: A list of interrupt-specifiers, one for each entry in
+             interrupt-names.
+             If interrupt-names is not present, an interrupt specifier
+             for a single muxed interrupt.
 - phy-mode: see ethernet.txt file in the same directory.
 - phy-handle: see ethernet.txt file in the same directory.
 - #address-cells: number of address cells for the MDIO bus, must be equal to 1.
@@ -18,6 +22,12 @@ Required properties:
 Optional properties:
 - interrupt-parent: the phandle for the interrupt controller that services
                    interrupts for this device.
+- interrupt-names: A list of interrupt names.
+                  For the R8A7795 SoC this property is mandatory;
+                  it should include one entry per channel, named "ch%u",
+                  where %u is the channel number ranging from 0 to 24.
+                  For other SoCs this property is optional; if present
+                  it should contain "mux" for a single muxed interrupt.
 - pinctrl-names: pin configuration state name ("default").
 - renesas,no-ether-link: boolean, specify when a board does not provide a proper
                         AVB_LINK signal.
@@ -27,13 +37,46 @@ Optional properties:
 Example:
 
        ethernet@e6800000 {
-               compatible = "renesas,etheravb-r8a7790";
-               reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>;
+               compatible = "renesas,etheravb-r8a7795";
+               reg = <0 0xe6800000 0 0x800>, <0 0xe6a00000 0 0x10000>;
                interrupt-parent = <&gic>;
-               interrupts = <0 163 IRQ_TYPE_LEVEL_HIGH>;
-               clocks = <&mstp8_clks R8A7790_CLK_ETHERAVB>;
-               phy-mode = "rmii";
+               interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>;
+               interrupt-names = "ch0", "ch1", "ch2", "ch3",
+                                 "ch4", "ch5", "ch6", "ch7",
+                                 "ch8", "ch9", "ch10", "ch11",
+                                 "ch12", "ch13", "ch14", "ch15",
+                                 "ch16", "ch17", "ch18", "ch19",
+                                 "ch20", "ch21", "ch22", "ch23",
+                                 "ch24";
+               clocks = <&mstp8_clks R8A7795_CLK_ETHERAVB>;
+               power-domains = <&cpg_clocks>;
+               phy-mode = "rgmii-id";
                phy-handle = <&phy0>;
+
                pinctrl-0 = <&ether_pins>;
                pinctrl-names = "default";
                renesas,no-ether-link;
@@ -41,8 +84,20 @@ Example:
                #size-cells = <0>;
 
                phy0: ethernet-phy@0 {
+                       rxc-skew-ps = <900>;
+                       rxdv-skew-ps = <0>;
+                       rxd0-skew-ps = <0>;
+                       rxd1-skew-ps = <0>;
+                       rxd2-skew-ps = <0>;
+                       rxd3-skew-ps = <0>;
+                       txc-skew-ps = <900>;
+                       txen-skew-ps = <0>;
+                       txd0-skew-ps = <0>;
+                       txd1-skew-ps = <0>;
+                       txd2-skew-ps = <0>;
+                       txd3-skew-ps = <0>;
                        reg = <0>;
                        interrupt-parent = <&gpio2>;
-                       interrupts = <15 IRQ_TYPE_LEVEL_LOW>;
+                       interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
                };
        };
diff --git a/Documentation/devicetree/bindings/net/smsc-lan87xx.txt b/Documentation/devicetree/bindings/net/smsc-lan87xx.txt
new file mode 100644 (file)
index 0000000..974edd5
--- /dev/null
@@ -0,0 +1,24 @@
+SMSC LAN87xx Ethernet PHY
+
+Some boards require special tuning values. Configure them
+through an Ethernet OF device node.
+
+Optional properties:
+
+- smsc,disable-energy-detect:
+  If set, do not enable energy detect mode for the SMSC phy.
+  default: enable energy detect mode
+
+Examples:
+smsc phy with disabled energy detect mode on an am335x based board.
+&davinci_mdio {
+       pinctrl-names = "default", "sleep";
+       pinctrl-0 = <&davinci_mdio_default>;
+       pinctrl-1 = <&davinci_mdio_sleep>;
+       status = "okay";
+
+       ethernetphy0: ethernet-phy@0 {
+               reg = <0>;
+               smsc,disable-energy-detect;
+       };
+};
diff --git a/Documentation/devicetree/bindings/pci/pci-msi.txt b/Documentation/devicetree/bindings/pci/pci-msi.txt
new file mode 100644 (file)
index 0000000..9b3cc81
--- /dev/null
@@ -0,0 +1,220 @@
+This document describes the generic device tree binding for describing the
+relationship between PCI devices and MSI controllers.
+
+Each PCI device under a root complex is uniquely identified by its Requester ID
+(AKA RID). A Requester ID is a triplet of a Bus number, Device number, and
+Function number.
+
+For the purpose of this document, when treated as a numeric value, a RID is
+formatted such that:
+
+* Bits [15:8] are the Bus number.
+* Bits [7:3] are the Device number.
+* Bits [2:0] are the Function number.
+* Any other bits required for padding must be zero.
+
+MSIs may be distinguished in part through the use of sideband data accompanying
+writes. In the case of PCI devices, this sideband data may be derived from the
+Requester ID. A mechanism is required to associate a device with both the MSI
+controllers it can address, and the sideband data that will be associated with
+its writes to those controllers.
+
+For generic MSI bindings, see
+Documentation/devicetree/bindings/interrupt-controller/msi.txt.
+
+
+PCI root complex
+================
+
+Optional properties
+-------------------
+
+- msi-map: Maps a Requester ID to an MSI controller and associated
+  msi-specifier data. The property is an arbitrary number of tuples of
+  (rid-base,msi-controller,msi-base,length), where:
+
+  * rid-base is a single cell describing the first RID matched by the entry.
+
+  * msi-controller is a single phandle to an MSI controller
+
+  * msi-base is an msi-specifier describing the msi-specifier produced for the
+    first RID matched by the entry.
+
+  * length is a single cell describing how many consecutive RIDs are matched
+    following the rid-base.
+
+  Any RID r in the interval [rid-base, rid-base + length) is associated with
+  the listed msi-controller, with the msi-specifier (r - rid-base + msi-base).
+
+- msi-map-mask: A mask to be applied to each Requester ID prior to being mapped
+  to an msi-specifier per the msi-map property.
+
+- msi-parent: Describes the MSI parent of the root complex itself. Where
+  the root complex and MSI controller do not pass sideband data with MSI
+  writes, this property may be used to describe the MSI controller(s)
+  used by PCI devices under the root complex, if defined as such in the
+  binding for the root complex.
+
+
+Example (1)
+===========
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       msi: msi-controller@a {
+               reg = <0xa 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       pci: pci@f {
+               reg = <0xf 0x1>;
+               compatible = "vendor,pcie-root-complex";
+               device_type = "pci";
+
+               /*
+                * The sideband data provided to the MSI controller is
+                * the RID, identity-mapped.
+                */
+               msi-map = <0x0 &msi_a 0x0 0x10000>,
+       };
+};
+
+
+Example (2)
+===========
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       msi: msi-controller@a {
+               reg = <0xa 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       pci: pci@f {
+               reg = <0xf 0x1>;
+               compatible = "vendor,pcie-root-complex";
+               device_type = "pci";
+
+               /*
+                * The sideband data provided to the MSI controller is
+                * the RID, masked to only the device and function bits.
+                */
+               msi-map = <0x0 &msi_a 0x0 0x100>,
+               msi-map-mask = <0xff>
+       };
+};
+
+
+Example (3)
+===========
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       msi: msi-controller@a {
+               reg = <0xa 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       pci: pci@f {
+               reg = <0xf 0x1>;
+               compatible = "vendor,pcie-root-complex";
+               device_type = "pci";
+
+               /*
+                * The sideband data provided to the MSI controller is
+                * the RID, but the high bit of the bus number is
+                * ignored.
+                */
+               msi-map = <0x0000 &msi 0x0000 0x8000>,
+                         <0x8000 &msi 0x0000 0x8000>;
+       };
+};
+
+
+Example (4)
+===========
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       msi: msi-controller@a {
+               reg = <0xa 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       pci: pci@f {
+               reg = <0xf 0x1>;
+               compatible = "vendor,pcie-root-complex";
+               device_type = "pci";
+
+               /*
+                * The sideband data provided to the MSI controller is
+                * the RID, but the high bit of the bus number is
+                * negated.
+                */
+               msi-map = <0x0000 &msi 0x8000 0x8000>,
+                         <0x8000 &msi 0x0000 0x8000>;
+       };
+};
+
+
+Example (5)
+===========
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       msi_a: msi-controller@a {
+               reg = <0xa 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       msi_b: msi-controller@b {
+               reg = <0xb 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       msi_c: msi-controller@c {
+               reg = <0xc 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       pci: pci@c {
+               reg = <0xf 0x1>;
+               compatible = "vendor,pcie-root-complex";
+               device_type = "pci";
+
+               /*
+                * The sideband data provided to MSI controller a is the
+                * RID, but the high bit of the bus number is negated.
+                * The sideband data provided to MSI controller b is the
+                * RID, identity-mapped.
+                * MSI controller c is not addressable.
+                */
+               msi-map = <0x0000 &msi_a 0x8000 0x08000>,
+                         <0x8000 &msi_a 0x0000 0x08000>,
+                         <0x0000 &msi_b 0x0000 0x10000>;
+       };
+};
index 3c821cda1ad05940f3ff32947f7b74c2e990e528..b321b26780dc0697e54f0821289126c2a5c6e801 100644 (file)
@@ -17,6 +17,7 @@ Required properties:
   "allwinner,sun8i-a23-pinctrl"
   "allwinner,sun8i-a23-r-pinctrl"
   "allwinner,sun8i-a33-pinctrl"
+  "allwinner,sun8i-a83t-pinctrl"
 
 - reg: Should contain the register physical address and length for the
   pin controller.
diff --git a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pio4-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pio4-pinctrl.txt
new file mode 100644 (file)
index 0000000..61ac757
--- /dev/null
@@ -0,0 +1,90 @@
+* Atmel PIO4 Controller
+
+The Atmel PIO4 controller is used to select the function of a pin and to
+configure it.
+
+Required properties:
+- compatible: "atmel,sama5d2-pinctrl".
+- reg: base address and length of the PIO controller.
+- interrupts: interrupt outputs from the controller, one for each bank.
+- interrupt-controller: mark the device node as an interrupt controller.
+- #interrupt-cells: should be two.
+- gpio-controller: mark the device node as a gpio controller.
+- #gpio-cells: should be two.
+
+Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
+a general description of GPIO and interrupt bindings.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices.
+
+Subnode format
+Each node (or subnode) will list the pins it needs and how to configured these
+pins.
+
+       node {
+               pinmux = <PIN_NUMBER_PINMUX>;
+               GENERIC_PINCONFIG;
+       };
+
+Required properties:
+- pinmux: integer array. Each integer represents a pin number plus mux and
+ioset settings. Use the macros from boot/dts/<soc>-pinfunc.h file to get the
+right representation of the pin.
+
+Optional properties:
+- GENERIC_PINCONFIG: generic pinconfig options to use, bias-disable,
+bias-pull-down, bias-pull-up, drive-open-drain, input-schmitt-enable,
+input-debounce, output-low, output-high.
+
+Example:
+
+#include <sama5d2-pinfunc.h>
+
+...
+{
+       pioA: pinctrl@fc038000 {
+               compatible = "atmel,sama5d2-pinctrl";
+               reg = <0xfc038000 0x600>;
+               interrupts = <18 IRQ_TYPE_LEVEL_HIGH 7>,
+                            <68 IRQ_TYPE_LEVEL_HIGH 7>,
+                            <69 IRQ_TYPE_LEVEL_HIGH 7>,
+                            <70 IRQ_TYPE_LEVEL_HIGH 7>;
+               interrupt-controller;
+               #interrupt-cells = <2>;
+               gpio-controller;
+               #gpio-cells = <2>;
+               clocks = <&pioA_clk>;
+
+               pinctrl_i2c0_default: i2c0_default {
+                       pinmux = <PIN_PD21__TWD0>,
+                                <PIN_PD22__TWCK0>;
+                       bias-disable;
+               };
+
+               pinctrl_led_gpio_default: led_gpio_default {
+                       pinmux = <PIN_PB0>,
+                                <PIN_PB5>;
+                       bias-pull-up;
+               };
+
+               pinctrl_sdmmc1_default: sdmmc1_default {
+                       cmd_data {
+                               pinmux = <PIN_PA28__SDMMC1_CMD>,
+                                        <PIN_PA18__SDMMC1_DAT0>,
+                                        <PIN_PA19__SDMMC1_DAT1>,
+                                        <PIN_PA20__SDMMC1_DAT2>,
+                                        <PIN_PA21__SDMMC1_DAT3>;
+                               bias-pull-up;
+                       };
+
+                       ck_cd {
+                               pinmux = <PIN_PA22__SDMMC1_CK>,
+                                        <PIN_PA30__SDMMC1_CD>;
+                               bias-disable;
+                       };
+               };
+               ...
+       };
+};
+...
index a8bb5e26019c6f69ea5ef49a8e0bad95ae806bda..f8fa28ce163e8f9a6661fcce8a7bad839555936a 100644 (file)
@@ -20,7 +20,10 @@ Required properties:
        "marvell,berlin2cd-soc-pinctrl",
        "marvell,berlin2cd-system-pinctrl",
        "marvell,berlin2q-soc-pinctrl",
-       "marvell,berlin2q-system-pinctrl"
+       "marvell,berlin2q-system-pinctrl",
+       "marvell,berlin4ct-avio-pinctrl",
+       "marvell,berlin4ct-soc-pinctrl",
+       "marvell,berlin4ct-system-pinctrl"
 
 Required subnode-properties:
 - groups: a list of strings describing the group names.
index 6540ca56be5edac956abf45bb87392ea169b584e..16589fb6f42059c4d3710d87cdf0e7bb37c323e1 100644 (file)
@@ -3,8 +3,8 @@ Broadcom Cygnus GPIO/PINCONF Controller
 Required properties:
 
 - compatible:
-    Must be "brcm,cygnus-ccm-gpio", "brcm,cygnus-asiu-gpio", or
-    "brcm,cygnus-crmu-gpio"
+    Must be "brcm,cygnus-ccm-gpio", "brcm,cygnus-asiu-gpio",
+    "brcm,cygnus-crmu-gpio" or "brcm,iproc-gpio"
 
 - reg:
     Define the base and range of the I/O address space that contains the Cygnus
@@ -26,9 +26,13 @@ Optional properties:
 - interrupt-controller:
     Specifies that the node is an interrupt controller
 
-- pinmux:
-    Specifies the phandle to the IOMUX device, where pins can be individually
-muxed to GPIO
+- gpio-ranges:
+    Specifies the mapping between gpio controller and pin-controllers pins.
+    This requires 4 fields in cells defined as -
+    1. Phandle of pin-controller.
+    2. GPIO base pin offset.
+    3  Pin-control base pin offset.
+    4. number of gpio pins which are linearly mapped from pin base.
 
 Supported generic PINCONF properties in child nodes:
 
@@ -78,6 +82,8 @@ Example:
                gpio-controller;
                interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
                interrupt-controller;
+               gpio-ranges = <&pinctrl 0 42 1>,
+                               <&pinctrl 1 44 3>;
        };
 
        /*
index 8bbf25d58656d2f09ae9b1a779050e2ff87fda65..457b2c68d47b6caa817acc7a8e048b69ff5eb4f5 100644 (file)
@@ -1,16 +1,42 @@
 * Freescale i.MX7 Dual IOMUX Controller
 
+iMX7D supports two iomuxc controllers, fsl,imx7d-iomuxc controller is similar
+as previous iMX SoC generation and fsl,imx7d-iomuxc-lpsr which provides low
+power state retention capabilities on gpios that are part of iomuxc-lpsr
+(GPIO1_IO7..GPIO1_IO0). While iomuxc-lpsr provides its own set of registers for
+mux and pad control settings, it shares the input select register from main
+iomuxc controller for daisy chain settings, the fsl,input-sel property extends
+fsl,imx-pinctrl driver to support iomuxc-lpsr controller.
+
+iomuxc_lpsr: iomuxc-lpsr@302c0000 {
+       compatible = "fsl,imx7d-iomuxc-lpsr";
+       reg = <0x302c0000 0x10000>;
+       fsl,input-sel = <&iomuxc>;
+};
+
+iomuxc: iomuxc@30330000 {
+       compatible = "fsl,imx7d-iomuxc";
+       reg = <0x30330000 0x10000>;
+};
+
+Pheriparials using pads from iomuxc-lpsr support low state retention power
+state, under LPSR mode GPIO's state of pads are retain.
+
 Please refer to fsl,imx-pinctrl.txt in this directory for common binding part
 and usage.
 
 Required properties:
-- compatible: "fsl,imx7d-iomuxc"
+- compatible: "fsl,imx7d-iomuxc" for main IOMUXC controller, or
+  "fsl,imx7d-iomuxc-lpsr" for Low Power State Retention IOMUXC controller.
 - fsl,pins: each entry consists of 6 integers and represents the mux and config
   setting for one pin.  The first 5 integers <mux_reg conf_reg input_reg mux_val
   input_val> are specified using a PIN_FUNC_ID macro, which can be found in
   imx7d-pinfunc.h under device tree source folder.  The last integer CONFIG is
   the pad setting value like pull-up on this pin.  Please refer to i.MX7 Dual
   Reference Manual for detailed CONFIG settings.
+- fsl,input-sel: required property for iomuxc-lpsr controller, this property is
+  a phandle for main iomuxc controller which shares the input select register for
+  daisy chain settings.
 
 CONFIG bits definition:
 PAD_CTL_PUS_100K_DOWN           (0 << 5)
@@ -25,3 +51,38 @@ PAD_CTL_DSE_X1                  (0 << 0)
 PAD_CTL_DSE_X2                  (1 << 0)
 PAD_CTL_DSE_X3                  (2 << 0)
 PAD_CTL_DSE_X4                  (3 << 0)
+
+Examples:
+While iomuxc-lpsr is intended to be used by dedicated peripherals to take
+advantages of LPSR power mode, is also possible that an IP to use pads from
+any of the iomux controllers. For example the I2C1 IP can use SCL pad from
+iomuxc-lpsr controller and SDA pad from iomuxc controller as:
+
+i2c1: i2c@30a20000 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_i2c1_1 &pinctrl_i2c1_2>;
+       status = "okay";
+};
+
+iomuxc-lpsr@302c0000 {
+       compatible = "fsl,imx7d-iomuxc-lpsr";
+       reg = <0x302c0000 0x10000>;
+       fsl,input-sel = <&iomuxc>;
+
+       pinctrl_i2c1_1: i2c1grp-1 {
+               fsl,pins = <
+                       MX7D_PAD_GPIO1_IO04__I2C1_SCL 0x4000007f
+               >;
+       };
+};
+
+iomuxc@30330000 {
+       compatible = "fsl,imx7d-iomuxc";
+       reg = <0x30330000 0x10000>;
+
+       pinctrl_i2c1_2: i2c1grp-2 {
+               fsl,pins = <
+                       MX7D_PAD_I2C1_SDA__I2C1_SDA 0x4000007f
+               >;
+       };
+};
index 9496934528bdef505a373f9946a2dbe0570e8e92..ffadb7a371f6e94ee1422e04cf2598c3b5d5ec31 100644 (file)
@@ -19,6 +19,7 @@ Required Properties:
     - "renesas,pfc-r8a7791": for R8A7791 (R-Car M2-W) compatible pin-controller.
     - "renesas,pfc-r8a7793": for R8A7793 (R-Car M2-N) compatible pin-controller.
     - "renesas,pfc-r8a7794": for R8A7794 (R-Car E2) compatible pin-controller.
+    - "renesas,pfc-r8a7795": for R8A7795 (R-Car H3) compatible pin-controller.
     - "renesas,pfc-sh73a0": for SH73A0 (SH-Mobile AG5) compatible pin-controller.
 
   - reg: Base address and length of each memory resource used by the pin
diff --git a/Documentation/devicetree/bindings/rng/samsung,exynos-rng4.txt b/Documentation/devicetree/bindings/rng/samsung,exynos-rng4.txt
new file mode 100644 (file)
index 0000000..4ca8dd4
--- /dev/null
@@ -0,0 +1,17 @@
+Exynos Pseudo Random Number Generator
+
+Required properties:
+
+- compatible  : Should be "samsung,exynos4-rng".
+- reg         : Specifies base physical address and size of the registers map.
+- clocks      : Phandle to clock-controller plus clock-specifier pair.
+- clock-names : "secss" as a clock name.
+
+Example:
+
+       rng@10830400 {
+               compatible = "samsung,exynos4-rng";
+               reg = <0x10830400 0x200>;
+               clocks = <&clock CLK_SSS>;
+               clock-names = "secss";
+       };
diff --git a/Documentation/devicetree/bindings/rng/st,rng.txt b/Documentation/devicetree/bindings/rng/st,rng.txt
new file mode 100644 (file)
index 0000000..35734bc
--- /dev/null
@@ -0,0 +1,15 @@
+STMicroelectronics HW Random Number Generator
+----------------------------------------------
+
+Required parameters:
+compatible     : Should be "st,rng"
+reg            : Base address and size of IP's register map.
+clocks         : Phandle to device's clock (See: ../clocks/clock-bindings.txt)
+
+Example:
+
+rng@fee80000 {
+       compatible      = "st,rng";
+       reg             = <0xfee80000 0x1000>;
+       clocks          = <&clk_sysin>;
+}
diff --git a/Documentation/devicetree/bindings/rng/st,stm32-rng.txt b/Documentation/devicetree/bindings/rng/st,stm32-rng.txt
new file mode 100644 (file)
index 0000000..47f0417
--- /dev/null
@@ -0,0 +1,21 @@
+STMicroelectronics STM32 HW RNG
+===============================
+
+The STM32 hardware random number generator is a simple fixed purpose IP and
+is fully separated from other crypto functions.
+
+Required properties:
+
+- compatible : Should be "st,stm32-rng"
+- reg : Should be register base and length as documented in the datasheet
+- interrupts : The designated IRQ line for the RNG
+- clocks : The clock needed to enable the RNG
+
+Example:
+
+       rng: rng@50060800 {
+               compatible = "st,stm32-rng";
+               reg = <0x50060800 0x400>;
+               interrupts = <80>;
+               clocks = <&rcc 0 38>;
+       };
index 0cf27a3544a5744f39c232c75039a37ca079c2cd..80841a2d640cf5aeca8e16db4ebba5d224c5a008 100644 (file)
@@ -744,6 +744,52 @@ exports one
    possible that some errors could be lost. With rdimm's, they display the
    contents of the registers
 
+AMD64_EDAC REFERENCE DOCUMENTS USED
+-----------------------------------
+amd64_edac module is based on the following documents
+(available from http://support.amd.com/en-us/search/tech-docs):
+
+1. Title:  BIOS and Kernel Developer's Guide for AMD Athlon 64 and AMD
+          Opteron Processors
+   AMD publication #: 26094
+   Revision: 3.26
+   Link: http://support.amd.com/TechDocs/26094.PDF
+
+2. Title:  BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh
+          Processors
+   AMD publication #: 32559
+   Revision: 3.00
+   Issue Date: May 2006
+   Link: http://support.amd.com/TechDocs/32559.pdf
+
+3. Title:  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 10h
+          Processors
+   AMD publication #: 31116
+   Revision: 3.00
+   Issue Date: September 07, 2007
+   Link: http://support.amd.com/TechDocs/31116.pdf
+
+4. Title: BIOS and Kernel Developer's Guide (BKDG) for AMD Family 15h
+         Models 30h-3Fh Processors
+   AMD publication #: 49125
+   Revision: 3.06
+   Issue Date: 2/12/2015 (latest release)
+   Link: http://support.amd.com/TechDocs/49125_15h_Models_30h-3Fh_BKDG.pdf
+
+5. Title: BIOS and Kernel Developer's Guide (BKDG) for AMD Family 15h
+         Models 60h-6Fh Processors
+   AMD publication #: 50742
+   Revision: 3.01
+   Issue Date: 7/23/2015 (latest release)
+   Link: http://support.amd.com/TechDocs/50742_15h_Models_60h-6Fh_BKDG.pdf
+
+6. Title: BIOS and Kernel Developer's Guide (BKDG) for AMD Family 16h
+         Models 00h-0Fh Processors
+   AMD publication #: 48751
+   Revision: 3.03
+   Issue Date: 2/23/2015 (latest release)
+   Link: http://support.amd.com/TechDocs/48751_16h_bkdg.pdf
+
 CREDITS:
 ========
 
index df384e3e845f7b22f121427c6a0c2d352d7648ce..523f8307b9cd574ee22961d4fd27e618f5fe5b05 100644 (file)
@@ -7,7 +7,7 @@
     |         arch |status|
     -----------------------
     |       alpha: | TODO |
-    |         arc: |  ..  |
+    |         arc: |  ok  |
     |         arm: |  ok  |
     |       arm64: |  ok  |
     |       avr32: |  ..  |
index aaaa21db6226eaec28873ee7c820cab43c367cfb..3de5434c857c8909deac9f8c1770c56723c31ff8 100644 (file)
@@ -7,7 +7,7 @@
     |         arch |status|
     -----------------------
     |       alpha: | TODO |
-    |         arc: | TODO |
+    |         arc: |  ok  |
     |         arm: |  ok  |
     |       arm64: |  ok  |
     |       avr32: | TODO |
index 2d66ed688125f894bae223c7e50fa85327ade534..bb5ab6de5924de19b8562d33b66bba8372fa72a3 100644 (file)
@@ -157,6 +157,9 @@ ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:
                  both:        use both BOOTP and RARP but not DHCP
                               (old option kept for backwards compatibility)
 
+               if dhcp is used, the client identifier can be used by following
+               format "ip=dhcp,client-id-type,client-id-value"
+
                 Default: any
 
   <dns0-ip>    IP address of first nameserver.
index d411ca63c8b6ce1f00ba624ace1fe5e0045da554..3a9d65c912e780977c12102d7719a0374241b962 100644 (file)
@@ -140,7 +140,8 @@ Table 1-1: Process specific entries in /proc
  stat          Process status
  statm         Process memory status information
  status                Process status in human readable form
- wchan         If CONFIG_KALLSYMS is set, a pre-decoded wchan
+ wchan         Present with CONFIG_KALLSYMS=y: it shows the kernel function
+               symbol the task is blocked in - or "0" if not blocked.
  pagemap       Page table
  stack         Report full stack trace, enable via CONFIG_STACKTRACE
  smaps         a extension based on maps, showing the memory consumption of
@@ -310,7 +311,7 @@ Table 1-4: Contents of the stat files (as of 2.6.30-rc7)
   blocked       bitmap of blocked signals
   sigign        bitmap of ignored signals
   sigcatch      bitmap of caught signals
-  wchan         address where process went to sleep
+  0            (place holder, used to be the wchan address, use /proc/PID/wchan instead)
   0             (place holder)
   0             (place holder)
   exit_signal   signal to send to parent thread on exit
index 90d0f6aba7a6848a3043ff7a7de3913118a98461..12a61948ec917ff2af9787a59f82336994acfde7 100644 (file)
@@ -62,6 +62,11 @@ Any debugfs dump method should normally ignore signals which haven't been
 requested as GPIOs. They can use gpiochip_is_requested(), which returns either
 NULL or the label associated with that GPIO when it was requested.
 
+RT_FULL: GPIO driver should not use spinlock_t or any sleepable APIs
+(like PM runtime) in its gpio_chip implementation (.get/.set and direction
+control callbacks) if it is expected to call GPIO APIs from atomic context
+on -RT (inside hard IRQ handlers and similar contexts). Normally this should
+not be required.
 
 GPIO drivers providing IRQs
 ---------------------------
@@ -73,6 +78,13 @@ The IRQ portions of the GPIO block are implemented using an irqchip, using
 the header <linux/irq.h>. So basically such a driver is utilizing two sub-
 systems simultaneously: gpio and irq.
 
+RT_FULL: GPIO driver should not use spinlock_t or any sleepable APIs
+(like PM runtime) as part of its irq_chip implementation on -RT.
+- spinlock_t should be replaced with raw_spinlock_t [1].
+- If sleepable APIs have to be used, these can be done from the .irq_bus_lock()
+  and .irq_bus_unlock() callbacks, as these are the only slowpath callbacks
+  on an irqchip. Create the callbacks if needed [2].
+
 GPIO irqchips usually fall in one of two categories:
 
 * CHAINED GPIO irqchips: these are usually the type that is embedded on
@@ -93,6 +105,38 @@ GPIO irqchips usually fall in one of two categories:
   Chained GPIO irqchips typically can NOT set the .can_sleep flag on
   struct gpio_chip, as everything happens directly in the callbacks.
 
+  RT_FULL: Note, chained IRQ handlers will not be forced threaded on -RT.
+  As result, spinlock_t or any sleepable APIs (like PM runtime) can't be used
+  in chained IRQ handler.
+  if required (and if it can't be converted to the nested threaded GPIO irqchip)
+  - chained IRQ handler can be converted to generic irq handler and this way
+  it will be threaded IRQ handler on -RT and hard IRQ handler on non-RT
+  (for example, see [3]).
+  Know W/A: The generic_handle_irq() is expected to be called with IRQ disabled,
+  so IRQ core will complain if it will be called from IRQ handler wich is forced
+  thread. The "fake?" raw lock can be used to W/A this problem:
+
+       raw_spinlock_t wa_lock;
+       static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
+               unsigned long wa_lock_flags;
+               raw_spin_lock_irqsave(&bank->wa_lock, wa_lock_flags);
+               generic_handle_irq(irq_find_mapping(bank->chip.irqdomain, bit));
+               raw_spin_unlock_irqrestore(&bank->wa_lock, wa_lock_flags);
+
+* GENERIC CHAINED GPIO irqchips: these are the same as "CHAINED GPIO irqchips",
+  but chained IRQ handlers are not used. Instead GPIO IRQs dispatching is
+  performed by generic IRQ handler which is configured using request_irq().
+  The GPIO irqchip will then end up calling something like this sequence in
+  its interrupt handler:
+
+  static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
+       for each detected GPIO IRQ
+               generic_handle_irq(...);
+
+  RT_FULL: Such kind of handlers will be forced threaded on -RT, as result IRQ
+  core will complain that generic_handle_irq() is called with IRQ enabled and
+  the same W/A as for "CHAINED GPIO irqchips" can be applied.
+
 * NESTED THREADED GPIO irqchips: these are off-chip GPIO expanders and any
   other GPIO irqchip residing on the other side of a sleeping bus. Of course
   such drivers that need slow bus traffic to read out IRQ status and similar,
@@ -133,6 +177,13 @@ To use the helpers please keep the following in mind:
   the irqchip can initialize. E.g. .dev and .can_sleep shall be set up
   properly.
 
+- Nominally set all handlers to handle_bad_irq() in the setup call and pass
+  handle_bad_irq() as flow handler parameter in gpiochip_irqchip_add() if it is
+  expected for GPIO driver that irqchip .set_type() callback have to be called
+  before using/enabling GPIO IRQ. Then set the handler to handle_level_irq()
+  and/or handle_edge_irq() in the irqchip .set_type() callback depending on
+  what your controller supports.
+
 It is legal for any IRQ consumer to request an IRQ from any irqchip no matter
 if that is a combined GPIO+IRQ driver. The basic premise is that gpio_chip and
 irq_chip are orthogonal, and offering their services independent of each
@@ -169,6 +220,31 @@ When implementing an irqchip inside a GPIO driver, these two functions should
 typically be called in the .startup() and .shutdown() callbacks from the
 irqchip.
 
+Real-Time compliance for GPIO IRQ chips
+---------------------------------------
+
+Any provider of irqchips needs to be carefully tailored to support Real Time
+preemption. It is desireable that all irqchips in the GPIO subsystem keep this
+in mind and does the proper testing to assure they are real time-enabled.
+So, pay attention on above " RT_FULL:" notes, please.
+The following is a checklist to follow when preparing a driver for real
+time-compliance:
+
+- ensure spinlock_t is not used as part irq_chip implementation;
+- ensure that sleepable APIs are not used as part irq_chip implementation.
+  If sleepable APIs have to be used, these can be done from the .irq_bus_lock()
+  and .irq_bus_unlock() callbacks;
+- Chained GPIO irqchips: ensure spinlock_t or any sleepable APIs are not used
+  from chained IRQ handler;
+- Generic chained GPIO irqchips: take care about generic_handle_irq() calls and
+  apply corresponding W/A;
+- Chained GPIO irqchips: get rid of chained IRQ handler and use generic irq
+  handler if possible :)
+- regmap_mmio: Sry, but you are in trouble :( if MMIO regmap is used as for
+  GPIO IRQ chip implementation;
+- Test your driver with the appropriate in-kernel real time test cases for both
+  level and edge IRQs.
+
 
 Requesting self-owned GPIO pins
 -------------------------------
@@ -190,3 +266,7 @@ gpiochip_free_own_desc().
 These functions must be used with care since they do not affect module use
 count. Do not use the functions to request gpio descriptors not owned by the
 calling driver.
+
+[1] http://www.spinics.net/lists/linux-omap/msg120425.html
+[2] https://lkml.org/lkml/2015/9/25/494
+[3] https://lkml.org/lkml/2015/9/25/495
index 026e237bbc875ac0401cffaf33376e784da9a0b2..fce1634907d0adbd3441d2d37a102d77dc967349 100644 (file)
@@ -3,7 +3,7 @@ Introduction:
        The hw_random framework is software that makes use of a
        special hardware feature on your CPU or motherboard,
        a Random Number Generator (RNG).  The software has two parts:
-       a core providing the /dev/hw_random character device and its
+       a core providing the /dev/hwrng character device and its
        sysfs support, plus a hardware-specific driver that plugs
        into that core.
 
@@ -14,7 +14,7 @@ Introduction:
 
                http://sourceforge.net/projects/gkernel/
 
-       Those tools use /dev/hw_random to fill the kernel entropy pool,
+       Those tools use /dev/hwrng to fill the kernel entropy pool,
        which is used internally and exported by the /dev/urandom and
        /dev/random special files.
 
@@ -32,13 +32,13 @@ Theory of operation:
        The rng-tools package uses such tests in "rngd", and lets you
        run them by hand with a "rngtest" utility.
 
-       /dev/hw_random is char device major 10, minor 183.
+       /dev/hwrng is char device major 10, minor 183.
 
        CLASS DEVICE.  There is a /sys/class/misc/hw_random node with
        two unique attributes, "rng_available" and "rng_current".  The
        "rng_available" attribute lists the hardware-specific drivers
        available, while "rng_current" lists the one which is currently
-       connected to /dev/hw_random.  If your system has more than one
+       connected to /dev/hwrng.  If your system has more than one
        RNG available, you may change the one used by writing a name from
        the list in "rng_available" into "rng_current".
 
index 67691a0aa41db82387369957de586bd4a6a2c6df..ac95edfcd907ccddcf2f7be7cf34944b0895ea94 100644 (file)
@@ -42,8 +42,8 @@ Supported chips:
     Addresses scanned: none
     Datasheet: Publicly available at the ST website
                http://www.st.com/internet/analog/product/121769.jsp
-  * Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP175, TMP275
-    Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp275'
+  * Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP75C, TMP175, TMP275
+    Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp75c', 'tmp275'
     Addresses scanned: none
     Datasheet: Publicly available at the Texas Instruments website
                http://www.ti.com/product/tmp100
@@ -51,6 +51,7 @@ Supported chips:
                http://www.ti.com/product/tmp105
                http://www.ti.com/product/tmp112
                http://www.ti.com/product/tmp75
+               http://www.ti.com/product/tmp75c
                http://www.ti.com/product/tmp175
                http://www.ti.com/product/tmp275
   * NXP LM75B
diff --git a/Documentation/hwmon/max31790 b/Documentation/hwmon/max31790
new file mode 100644 (file)
index 0000000..855e624
--- /dev/null
@@ -0,0 +1,37 @@
+Kernel driver max31790
+======================
+
+Supported chips:
+  * Maxim MAX31790
+    Prefix: 'max31790'
+    Addresses scanned: -
+    Datasheet: http://pdfserv.maximintegrated.com/en/ds/MAX31790.pdf
+
+Author: Il Han <corone.il.han@gmail.com>
+
+
+Description
+-----------
+
+This driver implements support for the Maxim MAX31790 chip.
+
+The MAX31790 controls the speeds of up to six fans using six independent
+PWM outputs. The desired fan speeds (or PWM duty cycles) are written
+through the I2C interface. The outputs drive "4-wire" fans directly,
+or can be used to modulate the fan's power terminals using an external
+pass transistor.
+
+Tachometer inputs monitor fan tachometer logic outputs for precise (+/-1%)
+monitoring and control of fan RPM as well as detection of fan failure.
+Six pins are dedicated tachometer inputs. Any of the six PWM outputs can
+also be configured to serve as tachometer inputs.
+
+
+Sysfs entries
+-------------
+
+fan[1-12]_input    RO  fan tachometer speed in RPM
+fan[1-12]_fault    RO  fan experienced fault
+fan[1-6]_target    RW  desired fan speed in RPM
+pwm[1-6]_enable    RW  regulator mode, 0=disabled, 1=manual mode, 2=rpm mode
+pwm[1-6]           RW  fan target duty cycle (0-255)
index 22a4b687ea5b4b3cb9d576bfeffaed813256a795..046832ef14ce136d4192e6cf7b39fb54de75c522 100644 (file)
@@ -1094,6 +1094,21 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        you are really sure that your UEFI does sane gc and
                        fulfills the spec otherwise your board may brick.
 
+       efi_fake_mem=   nn[KMG]@ss[KMG]:aa[,nn[KMG]@ss[KMG]:aa,..] [EFI; X86]
+                       Add arbitrary attribute to specific memory range by
+                       updating original EFI memory map.
+                       Region of memory which aa attribute is added to is
+                       from ss to ss+nn.
+                       If efi_fake_mem=2G@4G:0x10000,2G@0x10a0000000:0x10000
+                       is specified, EFI_MEMORY_MORE_RELIABLE(0x10000)
+                       attribute is added to range 0x100000000-0x180000000 and
+                       0x10a0000000-0x1120000000.
+
+                       Using this parameter you can do debugging of EFI memmap
+                       related feature. For example, you can do debugging of
+                       Address Range Mirroring feature even if your box
+                       doesn't support it.
+
        eisa_irq_edge=  [PARISC,HW]
                        See header of drivers/parisc/eisa.c.
 
@@ -3074,9 +3089,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        cache-to-cache transfer latencies.
 
        rcutree.rcu_fanout_leaf= [KNL]
-                       Increase the number of CPUs assigned to each
-                       leaf rcu_node structure.  Useful for very large
-                       systems.
+                       Change the number of CPUs assigned to each
+                       leaf rcu_node structure.  Useful for very
+                       large systems, which will choose the value 64,
+                       and for NUMA systems with large remote-access
+                       latencies, which will choose a value aligned
+                       with the appropriate hardware boundaries.
 
        rcutree.jiffies_till_sched_qs= [KNL]
                        Set required age in jiffies for a
index 568bbbacee91a581e1bb4be9bd54ed7e73d830e1..5786ad2cd5e63eec344628ec76c432a324d17a1f 100644 (file)
@@ -12,7 +12,7 @@ Because things like lock contention can severely impact performance.
 - HOW
 
 Lockdep already has hooks in the lock functions and maps lock instances to
-lock classes. We build on that (see Documentation/lokcing/lockdep-design.txt).
+lock classes. We build on that (see Documentation/locking/lockdep-design.txt).
 The graph below shows the relation between the lock functions and the various
 hooks therein.
 
index 619f2bb136a545f1932609f3b93b898145f6a5cc..a2ef3a929bf189bb43f7425f8e5e8a5564d23833 100644 (file)
@@ -52,6 +52,9 @@ torture_type    Type of lock to torture. By default, only spinlocks will
 
                     o "mutex_lock": mutex_lock() and mutex_unlock() pairs.
 
+                    o "rtmutex_lock": rtmutex_lock() and rtmutex_unlock()
+                                      pairs. Kernel must have CONFIG_RT_MUTEX=y.
+
                     o "rwsem_lock": read/write down() and up() semaphore pairs.
 
 torture_runnable  Start locktorture at boot time in the case where the
index 2ba8461b0631de759fefd2a12918a6c4f4ee7562..aef9487303d023cd00d27dceadb441c78f12413f 100644 (file)
@@ -617,16 +617,16 @@ case what's actually required is:
 However, stores are not speculated.  This means that ordering -is- provided
 for load-store control dependencies, as in the following example:
 
-       q = READ_ONCE_CTRL(a);
+       q = READ_ONCE(a);
        if (q) {
                WRITE_ONCE(b, p);
        }
 
 Control dependencies pair normally with other types of barriers.  That
-said, please note that READ_ONCE_CTRL() is not optional!  Without the
-READ_ONCE_CTRL(), the compiler might combine the load from 'a' with
-other loads from 'a', and the store to 'b' with other stores to 'b',
-with possible highly counterintuitive effects on ordering.
+said, please note that READ_ONCE() is not optional! Without the
+READ_ONCE(), the compiler might combine the load from 'a' with other
+loads from 'a', and the store to 'b' with other stores to 'b', with
+possible highly counterintuitive effects on ordering.
 
 Worse yet, if the compiler is able to prove (say) that the value of
 variable 'a' is always non-zero, it would be well within its rights
@@ -636,15 +636,12 @@ as follows:
        q = a;
        b = p;  /* BUG: Compiler and CPU can both reorder!!! */
 
-Finally, the READ_ONCE_CTRL() includes an smp_read_barrier_depends()
-that DEC Alpha needs in order to respect control depedencies.
-
-So don't leave out the READ_ONCE_CTRL().
+So don't leave out the READ_ONCE().
 
 It is tempting to try to enforce ordering on identical stores on both
 branches of the "if" statement as follows:
 
-       q = READ_ONCE_CTRL(a);
+       q = READ_ONCE(a);
        if (q) {
                barrier();
                WRITE_ONCE(b, p);
@@ -658,7 +655,7 @@ branches of the "if" statement as follows:
 Unfortunately, current compilers will transform this as follows at high
 optimization levels:
 
-       q = READ_ONCE_CTRL(a);
+       q = READ_ONCE(a);
        barrier();
        WRITE_ONCE(b, p);  /* BUG: No ordering vs. load from a!!! */
        if (q) {
@@ -688,7 +685,7 @@ memory barriers, for example, smp_store_release():
 In contrast, without explicit memory barriers, two-legged-if control
 ordering is guaranteed only when the stores differ, for example:
 
-       q = READ_ONCE_CTRL(a);
+       q = READ_ONCE(a);
        if (q) {
                WRITE_ONCE(b, p);
                do_something();
@@ -697,14 +694,14 @@ ordering is guaranteed only when the stores differ, for example:
                do_something_else();
        }
 
-The initial READ_ONCE_CTRL() is still required to prevent the compiler
-from proving the value of 'a'.
+The initial READ_ONCE() is still required to prevent the compiler from
+proving the value of 'a'.
 
 In addition, you need to be careful what you do with the local variable 'q',
 otherwise the compiler might be able to guess the value and again remove
 the needed conditional.  For example:
 
-       q = READ_ONCE_CTRL(a);
+       q = READ_ONCE(a);
        if (q % MAX) {
                WRITE_ONCE(b, p);
                do_something();
@@ -717,7 +714,7 @@ If MAX is defined to be 1, then the compiler knows that (q % MAX) is
 equal to zero, in which case the compiler is within its rights to
 transform the above code into the following:
 
-       q = READ_ONCE_CTRL(a);
+       q = READ_ONCE(a);
        WRITE_ONCE(b, p);
        do_something_else();
 
@@ -728,7 +725,7 @@ is gone, and the barrier won't bring it back.  Therefore, if you are
 relying on this ordering, you should make sure that MAX is greater than
 one, perhaps as follows:
 
-       q = READ_ONCE_CTRL(a);
+       q = READ_ONCE(a);
        BUILD_BUG_ON(MAX <= 1); /* Order load from a with store to b. */
        if (q % MAX) {
                WRITE_ONCE(b, p);
@@ -745,7 +742,7 @@ of the 'if' statement.
 You must also be careful not to rely too much on boolean short-circuit
 evaluation.  Consider this example:
 
-       q = READ_ONCE_CTRL(a);
+       q = READ_ONCE(a);
        if (q || 1 > 0)
                WRITE_ONCE(b, 1);
 
@@ -753,7 +750,7 @@ Because the first condition cannot fault and the second condition is
 always true, the compiler can transform this example as following,
 defeating control dependency:
 
-       q = READ_ONCE_CTRL(a);
+       q = READ_ONCE(a);
        WRITE_ONCE(b, 1);
 
 This example underscores the need to ensure that the compiler cannot
@@ -767,7 +764,7 @@ x and y both being zero:
 
        CPU 0                     CPU 1
        =======================   =======================
-       r1 = READ_ONCE_CTRL(x);   r2 = READ_ONCE_CTRL(y);
+       r1 = READ_ONCE(x);        r2 = READ_ONCE(y);
        if (r1 > 0)               if (r2 > 0)
          WRITE_ONCE(y, 1);         WRITE_ONCE(x, 1);
 
@@ -796,11 +793,6 @@ site: https://www.cl.cam.ac.uk/~pes20/ppcmem/index.html.
 
 In summary:
 
-  (*) Control dependencies must be headed by READ_ONCE_CTRL().
-      Or, as a much less preferable alternative, interpose
-      smp_read_barrier_depends() between a READ_ONCE() and the
-      control-dependent write.
-
   (*) Control dependencies can order prior loads against later stores.
       However, they do -not- guarantee any other sort of ordering:
       Not prior loads against later loads, nor prior stores against
@@ -816,14 +808,13 @@ In summary:
       between the prior load and the subsequent store, and this
       conditional must involve the prior load.  If the compiler is able
       to optimize the conditional away, it will have also optimized
-      away the ordering.  Careful use of READ_ONCE_CTRL() READ_ONCE(),
-      and WRITE_ONCE() can help to preserve the needed conditional.
+      away the ordering.  Careful use of READ_ONCE() and WRITE_ONCE()
+      can help to preserve the needed conditional.
 
   (*) Control dependencies require that the compiler avoid reordering the
-      dependency into nonexistence.  Careful use of READ_ONCE_CTRL()
-      or smp_read_barrier_depends() can help to preserve your control
-      dependency.  Please see the Compiler Barrier section for more
-      information.
+      dependency into nonexistence.  Careful use of READ_ONCE() or
+      atomic{,64}_read() can help to preserve your control dependency.
+      Please see the Compiler Barrier section for more information.
 
   (*) Control dependencies pair normally with other types of barriers.
 
@@ -1710,6 +1701,17 @@ There are some more advanced barrier functions:
      operations" subsection for information on where to use these.
 
 
+ (*) lockless_dereference();
+     This can be thought of as a pointer-fetch wrapper around the
+     smp_read_barrier_depends() data-dependency barrier.
+
+     This is also similar to rcu_dereference(), but in cases where
+     object lifetime is handled by some mechanism other than RCU, for
+     example, when the objects removed only when the system goes down.
+     In addition, lockless_dereference() is used in some data structures
+     that can be used both with and without RCU.
+
+
  (*) dma_wmb();
  (*) dma_rmb();
 
@@ -1789,7 +1791,6 @@ The Linux kernel has a number of locking constructs:
  (*) mutexes
  (*) semaphores
  (*) R/W semaphores
- (*) RCU
 
 In all cases there are variants on "ACQUIRE" operations and "RELEASE" operations
 for each construct.  These operations all imply certain barriers:
index 189bab09255a929089ffd031d10c71386964b69e..caa555706f8931dddf8cab3c88763b42d3d2c4d8 100644 (file)
@@ -72,13 +72,3 @@ Note on raw_rpmb_size_mult:
        "raw_rpmb_size_mult" is a mutliple of 128kB block.
        RPMB size in byte is calculated by using the following equation:
        RPMB partition size = 128kB x raw_rpmb_size_mult
-
-SD/MMC/SDIO Clock Gating Attribute
-==================================
-
-Read and write access is provided to following attribute.
-This attribute appears only if CONFIG_MMC_CLKGATE is enabled.
-
-       clkgate_delay   Tune the clock gating delay with desired value in milliseconds.
-
-echo <desired delay> > /sys/class/mmc_host/mmcX/clkgate_delay
index 1700756af0570bf75b6422600beb22c935072575..aa69ccc481dbd75a360f44f7ffba414407cddfed 100644 (file)
@@ -7,11 +7,11 @@ Introduction
 The IEEE 802.15.4 working group focuses on standardization of bottom
 two layers: Medium Access Control (MAC) and Physical (PHY). And there
 are mainly two options available for upper layers:
- - ZigBee - proprietary protocol from ZigBee Alliance
- - 6LowPAN - IPv6 networking over low rate personal area networks
+ - ZigBee - proprietary protocol from the ZigBee Alliance
+ - 6LoWPAN - IPv6 networking over low rate personal area networks
 
-The Linux-ZigBee project goal is to provide complete implementation
-of IEEE 802.15.4 and 6LoWPAN protocols. IEEE 802.15.4 is a stack
+The linux-wpan project goal is to provide a complete implementation
+of the IEEE 802.15.4 and 6LoWPAN protocols. IEEE 802.15.4 is a stack
 of protocols for organizing Low-Rate Wireless Personal Area Networks.
 
 The stack is composed of three main parts:
index ebe94f2cab9811e62f0ea1d64c0d6c75838d0367..05915be862356cf75230851920af5d4738b095b5 100644 (file)
@@ -384,6 +384,14 @@ tcp_mem - vector of 3 INTEGERs: min, pressure, max
        Defaults are calculated at boot time from amount of available
        memory.
 
+tcp_min_rtt_wlen - INTEGER
+       The window length of the windowed min filter to track the minimum RTT.
+       A shorter window lets a flow more quickly pick up new (higher)
+       minimum RTT when it is moved to a longer path (e.g., due to traffic
+       engineering). A longer window makes the filter more resistant to RTT
+       inflations such as transient congestion. The unit is seconds.
+       Default: 300
+
 tcp_moderate_rcvbuf - BOOLEAN
        If set, TCP performs receive buffer auto-tuning, attempting to
        automatically size the buffer (no greater than tcp_rmem[2]) to
@@ -425,6 +433,15 @@ tcp_orphan_retries - INTEGER
        you should think about lowering this value, such sockets
        may consume significant resources. Cf. tcp_max_orphans.
 
+tcp_recovery - INTEGER
+       This value is a bitmap to enable various experimental loss recovery
+       features.
+
+       RACK: 0x1 enables the RACK loss detection for fast detection of lost
+             retransmissions and tail drops.
+
+       Default: 0x1
+
 tcp_reordering - INTEGER
        Initial reordering level of packets in a TCP stream.
        TCP stack can then dynamically adjust flow reordering level
@@ -1199,7 +1216,8 @@ tag - INTEGER
 xfrm4_gc_thresh - INTEGER
        The threshold at which we will start garbage collecting for IPv4
        destination cache entries.  At twice this value the system will
-       refuse new allocations.
+       refuse new allocations. The value must be set below the flowcache
+       limit (4096 * number of online cpus) to take effect.
 
 igmp_link_local_mcast_reports - BOOLEAN
        Enable IGMP reports for link local multicast groups in the
@@ -1645,7 +1663,8 @@ ratelimit - INTEGER
 xfrm6_gc_thresh - INTEGER
        The threshold at which we will start garbage collecting for IPv6
        destination cache entries.  At twice this value the system will
-       refuse new allocations.
+       refuse new allocations. The value must be set below the flowcache
+       limit (4096 * number of online cpus) to take effect.
 
 
 IPv6 Update by:
index 3ba709531adba970595251fa73d6d471ed14c5c1..e6b1c025fdd89362a40e7aa676859a3a83646e7a 100644 (file)
@@ -157,6 +157,16 @@ expire_quiescent_template - BOOLEAN
        persistence template if it is to be used to schedule a new
        connection and the destination server is quiescent.
 
+ignore_tunneled - BOOLEAN
+       0 - disabled (default)
+       not 0 - enabled
+
+       If set, ipvs will set the ipvs_property on all packets which are of
+       unrecognized protocols.  This prevents us from routing tunneled
+       protocols like ipip, which is useful to prevent rescheduling
+       packets that have been tunneled to the ipvs host (i.e. to prevent
+       ipvs routing loops when ipvs is also acting as a real server).
+
 nat_icmp_send - BOOLEAN
         0 - disabled (default)
         not 0 - enabled
index c74434de2fa50a5873c9cef9900bc47fd6431839..4650a00ed012b38864799e2716721c359f218588 100644 (file)
@@ -213,15 +213,12 @@ To create an L2TPv3 ethernet pseudowire between local host 192.168.1.1
 and peer 192.168.1.2, using IP addresses 10.5.1.1 and 10.5.1.2 for the
 tunnel endpoints:-
 
-# modprobe l2tp_eth
-# modprobe l2tp_netlink
-
 # ip l2tp add tunnel tunnel_id 1 peer_tunnel_id 1 udp_sport 5000 \
   udp_dport 5000 encap udp local 192.168.1.1 remote 192.168.1.2
 # ip l2tp add session tunnel_id 1 session_id 1 peer_session_id 1
-# ifconfig -a
+# ip -s -d show dev l2tpeth0
 # ip addr add 10.5.1.2/32 peer 10.5.1.1/32 dev l2tpeth0
-# ifconfig l2tpeth0 up
+# ip li set dev l2tpeth0 up
 
 Choose IP addresses to be the address of a local IP interface and that
 of the remote system. The IP addresses of the l2tpeth0 interface can be
index 476df0496686d50b16fa73126cc4fdf52ac004f7..91994134efca9c92635eb5fe8503e5d5b63ae4b0 100644 (file)
@@ -115,7 +115,7 @@ Switch ID
 ^^^^^^^^^
 
 The switchdev driver must implement the switchdev op switchdev_port_attr_get
-for SWITCHDEV_ATTR_PORT_PARENT_ID for each port netdev, returning the same
+for SWITCHDEV_ATTR_ID_PORT_PARENT_ID for each port netdev, returning the same
 physical ID for each port of a switch.  The ID must be unique between switches
 on the same system.  The ID does not need to be unique between switches on
 different systems.
@@ -178,7 +178,7 @@ entries are installed, for example, using iproute2 bridge cmd:
        bridge fdb add ADDR dev DEV [vlan VID] [self]
 
 The driver should use the helper switchdev_port_fdb_xxx ops for ndo_fdb_xxx
-ops, and handle add/delete/dump of SWITCHDEV_OBJ_PORT_FDB object using
+ops, and handle add/delete/dump of SWITCHDEV_OBJ_ID_PORT_FDB object using
 switchdev_port_obj_xxx ops.
 
 XXX: what should be done if offloading this rule to hardware fails (for
@@ -233,26 +233,27 @@ the bridge's FDB.  It's possible, but not optimal, to enable learning on the
 device port and on the bridge port, and disable learning_sync.
 
 To support learning and learning_sync port attributes, the driver implements
-switchdev op switchdev_port_attr_get/set for SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS.
-The driver should initialize the attributes to the hardware defaults.
+switchdev op switchdev_port_attr_get/set for
+SWITCHDEV_ATTR_PORT_ID_BRIDGE_FLAGS. The driver should initialize the attributes
+to the hardware defaults.
 
 FDB Ageing
 ^^^^^^^^^^
 
-There are two FDB ageing models supported: 1) ageing by the device, and 2)
-ageing by the kernel.  Ageing by the device is preferred if many FDB entries
-are supported.  The driver calls call_switchdev_notifiers(SWITCHDEV_FDB_DEL,
-...) to age out the FDB entry.  In this model, ageing by the kernel should be
-turned off.  XXX: how to turn off ageing in kernel on a per-port basis or
-otherwise prevent the kernel from ageing out the FDB entry?
-
-In the kernel ageing model, the standard bridge ageing mechanism is used to age
-out stale FDB entries.  To keep an FDB entry "alive", the driver should refresh
-the FDB entry by calling call_switchdev_notifiers(SWITCHDEV_FDB_ADD, ...).  The
+The bridge will skip ageing FDB entries marked with NTF_EXT_LEARNED and it is
+the responsibility of the port driver/device to age out these entries.  If the
+port device supports ageing, when the FDB entry expires, it will notify the
+driver which in turn will notify the bridge with SWITCHDEV_FDB_DEL.  If the
+device does not support ageing, the driver can simulate ageing using a
+garbage collection timer to monitor FBD entries.  Expired entries will be
+notified to the bridge using SWITCHDEV_FDB_DEL.  See rocker driver for
+example of driver running ageing timer.
+
+To keep an NTF_EXT_LEARNED entry "alive", the driver should refresh the FDB
+entry by calling call_switchdev_notifiers(SWITCHDEV_FDB_ADD, ...).  The
 notification will reset the FDB entry's last-used time to now.  The driver
 should rate limit refresh notifications, for example, no more than once a
-second.  If the FDB entry expires, fdb_delete is called to remove entry from
-the device.
+second.  (The last-used time is visible using the bridge -s fdb option).
 
 STP State Change on Port
 ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -260,7 +261,7 @@ STP State Change on Port
 Internally or with a third-party STP protocol implementation (e.g. mstpd), the
 bridge driver maintains the STP state for ports, and will notify the switch
 driver of STP state change on a port using the switchdev op
-switchdev_attr_port_set for SWITCHDEV_ATTR_PORT_STP_UPDATE.
+switchdev_attr_port_set for SWITCHDEV_ATTR_PORT_ID_STP_UPDATE.
 
 State is one of BR_STATE_*.  The switch driver can use STP state updates to
 update ingress packet filter list for the port.  For example, if port is
@@ -277,8 +278,8 @@ Flooding L2 domain
 For a given L2 VLAN domain, the switch device should flood multicast/broadcast
 and unknown unicast packets to all ports in domain, if allowed by port's
 current STP state.  The switch driver, knowing which ports are within which
-vlan L2 domain, can program the switch device for flooding.  The packet should
-also be sent to the port netdev for processing by the bridge driver.  The
+vlan L2 domain, can program the switch device for flooding.  The packet may
+be sent to the port netdev for processing by the bridge driver.  The
 bridge should not reflood the packet to the same ports the device flooded,
 otherwise there will be duplicate packets on the wire.
 
@@ -297,6 +298,9 @@ packets up to the bridge driver for flooding.  This is not ideal as the number
 of ports scale in the L2 domain as the device is much more efficient at
 flooding packets that software.
 
+If supported by the device, flood control can be offloaded to it, preventing
+certain netdevs from flooding unicast traffic for which there is no FDB entry.
+
 IGMP Snooping
 ^^^^^^^^^^^^^
 
@@ -316,9 +320,9 @@ SWITCHDEV_OBJ_IPV[4|6]_FIB object using switchdev_port_obj_xxx ops.
 switchdev_port_obj_add is used for both adding a new FIB entry to the device,
 or modifying an existing entry on the device.
 
-XXX: Currently, only SWITCHDEV_OBJ_IPV4_FIB objects are supported.
+XXX: Currently, only SWITCHDEV_OBJ_ID_IPV4_FIB objects are supported.
 
-SWITCHDEV_OBJ_IPV4_FIB object passes:
+SWITCHDEV_OBJ_ID_IPV4_FIB object passes:
 
        struct switchdev_obj_ipv4_fib {         /* IPV4_FIB */
                u32 dst;
@@ -369,3 +373,22 @@ The driver can monitor for updates to arp_tbl using the netevent notifier
 NETEVENT_NEIGH_UPDATE.  The device can be programmed with resolved nexthops
 for the routes as arp_tbl updates.  The driver implements ndo_neigh_destroy
 to know when arp_tbl neighbor entries are purged from the port.
+
+Transaction item queue
+^^^^^^^^^^^^^^^^^^^^^^
+
+For switchdev ops attr_set and obj_add, there is a 2 phase transaction model
+used. First phase is to "prepare" anything needed, including various checks,
+memory allocation, etc. The goal is to handle the stuff that is not unlikely
+to fail here. The second phase is to "commit" the actual changes.
+
+Switchdev provides an inftrastructure for sharing items (for example memory
+allocations) between the two phases.
+
+The object created by a driver in "prepare" phase and it is queued up by:
+switchdev_trans_item_enqueue()
+During the "commit" phase, the driver gets the object by:
+switchdev_trans_item_dequeue()
+
+If a transaction is aborted during "prepare" phase, switchdev code will handle
+cleanup of the queued-up objects.
index 031ef4a634850b8cf4582e5d151fa8080dfa429c..d52aa10cfe911c88b47927c25cfd8ef596c65986 100644 (file)
@@ -90,7 +90,304 @@ or to specify the output device using cmsg and IP_PKTINFO.
 
 Limitations
 -----------
-VRF device currently only works for IPv4. Support for IPv6 is under development.
-
 Index of original ingress interface is not available via cmsg. Will address
 soon.
+
+################################################################################
+
+Using iproute2 for VRFs
+=======================
+VRF devices do *not* have to start with 'vrf-'. That is a convention used here
+for emphasis of the device type, similar to use of 'br' in bridge names.
+
+1. Create a VRF
+
+   To instantiate a VRF device and associate it with a table:
+       $ ip link add dev NAME type vrf table ID
+
+   Remember to add the ip rules as well:
+       $ ip ru add oif NAME table 10
+       $ ip ru add iif NAME table 10
+       $ ip -6 ru add oif NAME table 10
+       $ ip -6 ru add iif NAME table 10
+
+   Without the rules route lookups are not directed to the table.
+
+   For example:
+   $ ip link add dev vrf-blue type vrf table 10
+   $ ip ru add pref 200 oif vrf-blue table 10
+   $ ip ru add pref 200 iif vrf-blue table 10
+   $ ip -6 ru add pref 200 oif vrf-blue table 10
+   $ ip -6 ru add pref 200 iif vrf-blue table 10
+
+
+2. List VRFs
+
+   To list VRFs that have been created:
+       $ ip [-d] link show type vrf
+         NOTE: The -d option is needed to show the table id
+
+   For example:
+   $ ip -d link show type vrf
+   11: vrf-mgmt: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
+       link/ether 72:b3:ba:91:e2:24 brd ff:ff:ff:ff:ff:ff promiscuity 0
+       vrf table 1 addrgenmode eui64
+   12: vrf-red: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
+       link/ether b6:6f:6e:f6:da:73 brd ff:ff:ff:ff:ff:ff promiscuity 0
+       vrf table 10 addrgenmode eui64
+   13: vrf-blue: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
+       link/ether 36:62:e8:7d:bb:8c brd ff:ff:ff:ff:ff:ff promiscuity 0
+       vrf table 66 addrgenmode eui64
+   14: vrf-green: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
+       link/ether e6:28:b8:63:70:bb brd ff:ff:ff:ff:ff:ff promiscuity 0
+       vrf table 81 addrgenmode eui64
+
+
+   Or in brief output:
+
+   $ ip -br link show type vrf
+   vrf-mgmt         UP             72:b3:ba:91:e2:24 <NOARP,MASTER,UP,LOWER_UP>
+   vrf-red          UP             b6:6f:6e:f6:da:73 <NOARP,MASTER,UP,LOWER_UP>
+   vrf-blue         UP             36:62:e8:7d:bb:8c <NOARP,MASTER,UP,LOWER_UP>
+   vrf-green        UP             e6:28:b8:63:70:bb <NOARP,MASTER,UP,LOWER_UP>
+
+
+3. Assign a Network Interface to a VRF
+
+   Network interfaces are assigned to a VRF by enslaving the netdevice to a
+   VRF device:
+       $ ip link set dev NAME master VRF-NAME
+
+   On enslavement connected and local routes are automatically moved to the
+   table associated with the VRF device.
+
+   For example:
+   $ ip link set dev eth0 master vrf-mgmt
+
+
+4. Show Devices Assigned to a VRF
+
+   To show devices that have been assigned to a specific VRF add the master
+   option to the ip command:
+       $ ip link show master VRF-NAME
+
+   For example:
+   $ ip link show master vrf-red
+   3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master vrf-red state UP mode DEFAULT group default qlen 1000
+       link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff
+   4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master vrf-red state UP mode DEFAULT group default qlen 1000
+       link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff
+   7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master vrf-red state DOWN mode DEFAULT group default qlen 1000
+       link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
+
+
+   Or using the brief output:
+   $ ip -br link show master vrf-red
+   eth1             UP             02:00:00:00:02:02 <BROADCAST,MULTICAST,UP,LOWER_UP>
+   eth2             UP             02:00:00:00:02:03 <BROADCAST,MULTICAST,UP,LOWER_UP>
+   eth5             DOWN           02:00:00:00:02:06 <BROADCAST,MULTICAST>
+
+
+5. Show Neighbor Entries for a VRF
+
+   To list neighbor entries associated with devices enslaved to a VRF device
+   add the master option to the ip command:
+       $ ip [-6] neigh show master VRF-NAME
+
+   For example:
+   $  ip neigh show master vrf-red
+   10.2.1.254 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE
+   10.2.2.254 dev eth2 lladdr 5e:54:01:6a:ee:80 REACHABLE
+
+    $ ip -6 neigh show master vrf-red
+    2002:1::64 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE
+
+
+6. Show Addresses for a VRF
+
+   To show addresses for interfaces associated with a VRF add the master
+   option to the ip command:
+       $ ip addr show master VRF-NAME
+
+   For example:
+   $ ip addr show master vrf-red
+   3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master vrf-red state UP group default qlen 1000
+       link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff
+       inet 10.2.1.2/24 brd 10.2.1.255 scope global eth1
+          valid_lft forever preferred_lft forever
+       inet6 2002:1::2/120 scope global
+          valid_lft forever preferred_lft forever
+       inet6 fe80::ff:fe00:202/64 scope link
+          valid_lft forever preferred_lft forever
+   4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master vrf-red state UP group default qlen 1000
+       link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff
+       inet 10.2.2.2/24 brd 10.2.2.255 scope global eth2
+          valid_lft forever preferred_lft forever
+       inet6 2002:2::2/120 scope global
+          valid_lft forever preferred_lft forever
+       inet6 fe80::ff:fe00:203/64 scope link
+          valid_lft forever preferred_lft forever
+   7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master vrf-red state DOWN group default qlen 1000
+       link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
+
+   Or in brief format:
+   $ ip -br addr show master vrf-red
+   eth1             UP             10.2.1.2/24 2002:1::2/120 fe80::ff:fe00:202/64
+   eth2             UP             10.2.2.2/24 2002:2::2/120 fe80::ff:fe00:203/64
+   eth5             DOWN
+
+
+7. Show Routes for a VRF
+
+   To show routes for a VRF use the ip command to display the table associated
+   with the VRF device:
+       $ ip [-6] route show table ID
+
+   For example:
+   $ ip route show table vrf-red
+   prohibit default
+   broadcast 10.2.1.0 dev eth1  proto kernel  scope link  src 10.2.1.2
+   10.2.1.0/24 dev eth1  proto kernel  scope link  src 10.2.1.2
+   local 10.2.1.2 dev eth1  proto kernel  scope host  src 10.2.1.2
+   broadcast 10.2.1.255 dev eth1  proto kernel  scope link  src 10.2.1.2
+   broadcast 10.2.2.0 dev eth2  proto kernel  scope link  src 10.2.2.2
+   10.2.2.0/24 dev eth2  proto kernel  scope link  src 10.2.2.2
+   local 10.2.2.2 dev eth2  proto kernel  scope host  src 10.2.2.2
+   broadcast 10.2.2.255 dev eth2  proto kernel  scope link  src 10.2.2.2
+
+   $ ip -6 route show table vrf-red
+   local 2002:1:: dev lo  proto none  metric 0  pref medium
+   local 2002:1::2 dev lo  proto none  metric 0  pref medium
+   2002:1::/120 dev eth1  proto kernel  metric 256  pref medium
+   local 2002:2:: dev lo  proto none  metric 0  pref medium
+   local 2002:2::2 dev lo  proto none  metric 0  pref medium
+   2002:2::/120 dev eth2  proto kernel  metric 256  pref medium
+   local fe80:: dev lo  proto none  metric 0  pref medium
+   local fe80:: dev lo  proto none  metric 0  pref medium
+   local fe80::ff:fe00:202 dev lo  proto none  metric 0  pref medium
+   local fe80::ff:fe00:203 dev lo  proto none  metric 0  pref medium
+   fe80::/64 dev eth1  proto kernel  metric 256  pref medium
+   fe80::/64 dev eth2  proto kernel  metric 256  pref medium
+   ff00::/8 dev vrf-red  metric 256  pref medium
+   ff00::/8 dev eth1  metric 256  pref medium
+   ff00::/8 dev eth2  metric 256  pref medium
+
+
+8. Route Lookup for a VRF
+
+   A test route lookup can be done for a VRF by adding the oif option to ip:
+       $ ip [-6] route get oif VRF-NAME ADDRESS
+
+   For example:
+   $ ip route get 10.2.1.40 oif vrf-red
+   10.2.1.40 dev eth1  table vrf-red  src 10.2.1.2
+       cache
+
+   $ ip -6 route get 2002:1::32 oif vrf-red
+   2002:1::32 from :: dev eth1  table vrf-red  proto kernel  src 2002:1::2  metric 256  pref medium
+
+
+9. Removing Network Interface from a VRF
+
+   Network interfaces are removed from a VRF by breaking the enslavement to
+   the VRF device:
+       $ ip link set dev NAME nomaster
+
+   Connected routes are moved back to the default table and local entries are
+   moved to the local table.
+
+   For example:
+   $ ip link set dev eth0 nomaster
+
+--------------------------------------------------------------------------------
+
+Commands used in this example:
+
+cat >> /etc/iproute2/rt_tables <<EOF
+1  vrf-mgmt
+10 vrf-red
+66 vrf-blue
+81 vrf-green
+EOF
+
+function vrf_create
+{
+    VRF=$1
+    TBID=$2
+    # create VRF device
+    ip link add vrf-${VRF} type vrf table ${TBID}
+
+    # add rules that direct lookups to vrf table
+    ip ru add pref 200 oif vrf-${VRF} table ${TBID}
+    ip ru add pref 200 iif vrf-${VRF} table ${TBID}
+    ip -6 ru add pref 200 oif vrf-${VRF} table ${TBID}
+    ip -6 ru add pref 200 iif vrf-${VRF} table ${TBID}
+
+    if [ "${VRF}" != "mgmt" ]; then
+        ip route add table ${TBID} prohibit default
+    fi
+    ip link set dev vrf-${VRF} up
+    ip link set dev vrf-${VRF} state up
+}
+
+vrf_create mgmt 1
+ip link set dev eth0 master vrf-mgmt
+
+vrf_create red 10
+ip link set dev eth1 master vrf-red
+ip link set dev eth2 master vrf-red
+ip link set dev eth5 master vrf-red
+
+vrf_create blue 66
+ip link set dev eth3 master vrf-blue
+
+vrf_create green 81
+ip link set dev eth4 master vrf-green
+
+
+Interface addresses from /etc/network/interfaces:
+auto eth0
+iface eth0 inet static
+      address 10.0.0.2
+      netmask 255.255.255.0
+      gateway 10.0.0.254
+
+iface eth0 inet6 static
+      address 2000:1::2
+      netmask 120
+
+auto eth1
+iface eth1 inet static
+      address 10.2.1.2
+      netmask 255.255.255.0
+
+iface eth1 inet6 static
+      address 2002:1::2
+      netmask 120
+
+auto eth2
+iface eth2 inet static
+      address 10.2.2.2
+      netmask 255.255.255.0
+
+iface eth2 inet6 static
+      address 2002:2::2
+      netmask 120
+
+auto eth3
+iface eth3 inet static
+      address 10.2.3.2
+      netmask 255.255.255.0
+
+iface eth3 inet6 static
+      address 2002:3::2
+      netmask 120
+
+auto eth4
+iface eth4 inet static
+      address 10.2.4.2
+      netmask 255.255.255.0
+
+iface eth4 inet6 static
+      address 2002:4::2
+      netmask 120
index cb1a432a40812a54d4073d3873de40e1c13f7859..a17f58cb65b79ad2bcd2668ae384a8da5b85f770 100644 (file)
@@ -240,6 +240,12 @@ L: lm-sensors@lm-sensors.org
 S:     Maintained
 F:     drivers/hwmon/abituguru3.c
 
+ACCES 104-IDIO-16 GPIO DRIVER
+M:     "William Breathitt Gray" <vilhelm.gray@gmail.com>
+L:     linux-gpio@vger.kernel.org
+S:     Maintained
+F:     drivers/gpio/gpio-104-idio-16.c
+
 ACENIC DRIVER
 M:     Jes Sorensen <jes@trained-monkey.org>
 L:     linux-acenic@sunsite.dk
@@ -654,11 +660,6 @@ F: drivers/gpu/drm/radeon/radeon_kfd.c
 F:     drivers/gpu/drm/radeon/radeon_kfd.h
 F:     include/uapi/linux/kfd_ioctl.h
 
-AMD MICROCODE UPDATE SUPPORT
-M:     Borislav Petkov <bp@alien8.de>
-S:     Maintained
-F:     arch/x86/kernel/cpu/microcode/amd*
-
 AMD XGBE DRIVER
 M:     Tom Lendacky <thomas.lendacky@amd.com>
 L:     netdev@vger.kernel.org
@@ -1528,6 +1529,7 @@ W:        http://www.stlinux.com
 S:     Maintained
 F:     arch/arm/mach-sti/
 F:     arch/arm/boot/dts/sti*
+F:     drivers/char/hw_random/st-rng.c
 F:     drivers/clocksource/arm_global_timer.c
 F:     drivers/clocksource/clksrc_st_lpc.c
 F:     drivers/i2c/busses/i2c-st.c
@@ -1780,6 +1782,14 @@ S:       Supported
 F:     Documentation/aoe/
 F:     drivers/block/aoe/
 
+ATHEROS 71XX/9XXX GPIO DRIVER
+M:     Alban Bedel <albeu@free.fr>
+W:     https://github.com/AlbanBedel/linux
+T:     git git://github.com/AlbanBedel/linux
+S:     Maintained
+F:     drivers/gpio/gpio-ath79.c
+F:     Documentation/devicetree/bindings/gpio/gpio-ath79.txt
+
 ATHEROS ATH GENERIC UTILITIES
 M:     "Luis R. Rodriguez" <mcgrof@do-not-panic.com>
 L:     linux-wireless@vger.kernel.org
@@ -4429,6 +4439,14 @@ L:       linuxppc-dev@lists.ozlabs.org
 S:     Maintained
 F:     drivers/net/ethernet/freescale/ucc_geth*
 
+FREESCALE eTSEC ETHERNET DRIVER (GIANFAR)
+M:     Claudiu Manoil <claudiu.manoil@freescale.com>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     drivers/net/ethernet/freescale/gianfar*
+X:     drivers/net/ethernet/freescale/gianfar_ptp.c
+F:     Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
+
 FREESCALE QUICC ENGINE UCC UART DRIVER
 M:     Timur Tabi <timur@tabi.org>
 L:     linuxppc-dev@lists.ozlabs.org
@@ -5447,12 +5465,6 @@ W:       https://01.org/linux-acpi
 S:     Supported
 F:     drivers/platform/x86/intel_menlow.c
 
-INTEL IA32 MICROCODE UPDATE SUPPORT
-M:     Borislav Petkov <bp@alien8.de>
-S:     Maintained
-F:     arch/x86/kernel/cpu/microcode/core*
-F:     arch/x86/kernel/cpu/microcode/intel*
-
 INTEL I/OAT DMA DRIVER
 M:     Dave Jiang <dave.jiang@intel.com>
 R:     Dan Williams <dan.j.williams@intel.com>
@@ -5563,7 +5575,7 @@ F:        drivers/net/wireless/iwlegacy/
 INTEL WIRELESS WIFI LINK (iwlwifi)
 M:     Johannes Berg <johannes.berg@intel.com>
 M:     Emmanuel Grumbach <emmanuel.grumbach@intel.com>
-M:     Intel Linux Wireless <ilw@linux.intel.com>
+M:     Intel Linux Wireless <linuxwifi@intel.com>
 L:     linux-wireless@vger.kernel.org
 W:     http://intellinuxwireless.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git
@@ -6110,6 +6122,13 @@ F:       Documentation/auxdisplay/ks0108
 F:     drivers/auxdisplay/ks0108.c
 F:     include/linux/ks0108.h
 
+L3MDEV
+M:     David Ahern <dsa@cumulusnetworks.com>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     net/l3mdev
+F:     include/net/l3mdev.h
+
 LAPB module
 L:     linux-x25@vger.kernel.org
 S:     Orphan
@@ -6577,6 +6596,13 @@ M:       Guenter Roeck <linux@roeck-us.net>
 S:     Maintained
 F:     drivers/net/dsa/mv88e6352.c
 
+MARVELL CRYPTO DRIVER
+M:     Boris Brezillon <boris.brezillon@free-electrons.com>
+M:     Arnaud Ebalard <arno@natisbad.org>
+F:     drivers/crypto/marvell/
+S:     Maintained
+L:     linux-crypto@vger.kernel.org
+
 MARVELL GIGABIT ETHERNET DRIVERS (skge/sky2)
 M:     Mirko Lindner <mlindner@marvell.com>
 M:     Stephen Hemminger <stephen@networkplumber.org>
@@ -6987,6 +7013,7 @@ M:        Alan Ott <alan@signal11.us>
 L:     linux-wpan@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ieee802154/mrf24j40.c
+F:     Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt
 
 MSI LAPTOP SUPPORT
 M:     "Lee, Chun-Yi" <jlee@suse.com>
@@ -7321,7 +7348,6 @@ S:        Odd Fixes
 F:     drivers/net/
 F:     include/linux/if_*
 F:     include/linux/netdevice.h
-F:     include/linux/arcdevice.h
 F:     include/linux/etherdevice.h
 F:     include/linux/fcdevice.h
 F:     include/linux/fddidevice.h
@@ -8174,6 +8200,13 @@ L:       linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     drivers/pinctrl/pinctrl-at91.*
 
+PIN CONTROLLER - ATMEL AT91 PIO4
+M:     Ludovic Desroches <ludovic.desroches@atmel.com>
+L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L:     linux-gpio@vger.kernel.org
+S:     Supported
+F:     drivers/pinctrl/pinctrl-at91-pio4.*
+
 PIN CONTROLLER - INTEL
 M:     Mika Westerberg <mika.westerberg@linux.intel.com>
 M:     Heikki Krogerus <heikki.krogerus@linux.intel.com>
@@ -8535,6 +8568,16 @@ L:       netdev@vger.kernel.org
 S:     Supported
 F:     drivers/net/ethernet/qlogic/qlge/
 
+QLOGIC QL4xxx ETHERNET DRIVER
+M:     Yuval Mintz <Yuval.Mintz@qlogic.com>
+M:     Ariel Elior <Ariel.Elior@qlogic.com>
+M:     everest-linux-l2@qlogic.com
+L:     netdev@vger.kernel.org
+S:     Supported
+F:     drivers/net/ethernet/qlogic/qed/
+F:     include/linux/qed/
+F:     drivers/net/ethernet/qlogic/qede/
+
 QNX4 FILESYSTEM
 M:     Anders Larsen <al@alarsen.net>
 W:     http://www.alarsen.net/linux/qnx4fs/
@@ -8886,6 +8929,13 @@ S:       Maintained
 F:     drivers/net/wireless/rtlwifi/
 F:     drivers/net/wireless/rtlwifi/rtl8192ce/
 
+RTL8XXXU WIRELESS DRIVER (rtl8xxxu)
+M:     Jes Sorensen <Jes.Sorensen@redhat.com>
+L:     linux-wireless@vger.kernel.org
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jes/linux.git rtl8723au-mac80211
+S:     Maintained
+F:     drivers/net/wireless/realtek/rtl8xxxu/
+
 S3 SAVAGE FRAMEBUFFER DRIVER
 M:     Antonino Daplas <adaplas@gmail.com>
 L:     linux-fbdev@vger.kernel.org
@@ -10093,6 +10143,7 @@ F:      include/net/switchdev.h
 
 SYNOPSYS ARC ARCHITECTURE
 M:     Vineet Gupta <vgupta@synopsys.com>
+L:     linux-snps-arc@lists.infraded.org
 S:     Supported
 F:     arch/arc/
 F:     Documentation/devicetree/bindings/arc/*
@@ -11289,7 +11340,6 @@ M:      Shrijeet Mukherjee <shm@cumulusnetworks.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/vrf.c
-F:     include/net/vrf.h
 F:     Documentation/networking/vrf.txt
 
 VT1211 HARDWARE MONITOR DRIVER
@@ -11476,6 +11526,11 @@ L:     linux-edac@vger.kernel.org
 S:     Maintained
 F:     arch/x86/kernel/cpu/mcheck/*
 
+X86 MICROCODE UPDATE SUPPORT
+M:     Borislav Petkov <bp@alien8.de>
+S:     Maintained
+F:     arch/x86/kernel/cpu/microcode/*
+
 X86 VDSO
 M:     Andy Lutomirski <luto@amacapital.net>
 L:     linux-kernel@vger.kernel.org
index 431067a41fcfe5df67e3799ad0a6c7880030d41d..d5b37391195f80866ad2b831a417039b3ee230cf 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 4
 PATCHLEVEL = 3
 SUBLEVEL = 0
-EXTRAVERSION = -rc7
+EXTRAVERSION =
 NAME = Blurry Fish Butt
 
 # *DOCUMENTATION*
index e8c95609842436771f6c7c024c7b2830706987e8..572b228c44c7a80aec6eed925715c8d2ebacb00d 100644 (file)
 #define ATOMIC_INIT(i)         { (i) }
 #define ATOMIC64_INIT(i)       { (i) }
 
-#define atomic_read(v)         ACCESS_ONCE((v)->counter)
-#define atomic64_read(v)       ACCESS_ONCE((v)->counter)
+#define atomic_read(v)         READ_ONCE((v)->counter)
+#define atomic64_read(v)       READ_ONCE((v)->counter)
 
-#define atomic_set(v,i)                ((v)->counter = (i))
-#define atomic64_set(v,i)      ((v)->counter = (i))
+#define atomic_set(v,i)                WRITE_ONCE((v)->counter, (i))
+#define atomic64_set(v,i)      WRITE_ONCE((v)->counter, (i))
 
 /*
  * To get proper branch prediction for the main line, we must branch
index 78c0621d581940f3f765e75c5218a7cdc9a4ae5f..2c2ac3f3ff80370bd6bad9fffc9dfb8c8f6931e8 100644 (file)
@@ -76,6 +76,10 @@ config STACKTRACE_SUPPORT
 config HAVE_LATENCYTOP_SUPPORT
        def_bool y
 
+config HAVE_ARCH_TRANSPARENT_HUGEPAGE
+       def_bool y
+       depends on ARC_MMU_V4
+
 source "init/Kconfig"
 source "kernel/Kconfig.freezer"
 
@@ -190,6 +194,16 @@ config NR_CPUS
        range 2 4096
        default "4"
 
+config ARC_SMP_HALT_ON_RESET
+       bool "Enable Halt-on-reset boot mode"
+       default y if ARC_UBOOT_SUPPORT
+       help
+         In SMP configuration cores can be configured as Halt-on-reset
+         or they could all start at same time. For Halt-on-reset, non
+         masters are parked until Master kicks them so they can start of
+         at designated entry point. For other case, all jump to common
+         entry point and spin wait for Master's signal.
+
 endif  #SMP
 
 menuconfig ARC_CACHE
@@ -278,6 +292,8 @@ choice
        default ARC_MMU_V2 if ARC_CPU_750D
        default ARC_MMU_V4 if ARC_CPU_HS
 
+if ISA_ARCOMPACT
+
 config ARC_MMU_V1
        bool "MMU v1"
        help
@@ -297,6 +313,8 @@ config ARC_MMU_V3
          Variable Page size (1k-16k), var JTLB size 128 x (2 or 4)
          Shared Address Spaces (SASID)
 
+endif
+
 config ARC_MMU_V4
        bool "MMU v4"
        depends on ISA_ARCV2
@@ -428,6 +446,28 @@ config LINUX_LINK_BASE
          Linux needs to be scooted a bit.
          If you don't know what the above means, leave this setting alone.
 
+config HIGHMEM
+       bool "High Memory Support"
+       help
+         With ARC 2G:2G address split, only upper 2G is directly addressable by
+         kernel. Enable this to potentially allow access to rest of 2G and PAE
+         in future
+
+config ARC_HAS_PAE40
+       bool "Support for the 40-bit Physical Address Extension"
+       default n
+       depends on ISA_ARCV2
+       select HIGHMEM
+       help
+         Enable access to physical memory beyond 4G, only supported on
+         ARC cores with 40 bit Physical Addressing support
+
+config ARCH_PHYS_ADDR_T_64BIT
+       def_bool ARC_HAS_PAE40
+
+config ARCH_DMA_ADDR_T_64BIT
+       bool
+
 config ARC_CURR_IN_REG
        bool "Dedicate Register r25 for current_task pointer"
        default y
index a5e2726a067e6dffde2a552956ee82a7adaf88e1..420dcfde289fe8fe3e99193df1876d399b0dce04 100644 (file)
@@ -95,6 +95,6 @@
                #size-cells = <1>;
                ranges = <0x00000000 0x80000000 0x40000000>;
                device_type = "memory";
-               reg = <0x00000000 0x20000000>;  /* 512MiB */
+               reg = <0x80000000 0x20000000>;  /* 512MiB */
        };
 };
index 846481f37eef08bd8911859009fddf390974b193..f90fadf7f94e5e51f551d9e914aaad728aa5bc84 100644 (file)
@@ -98,6 +98,6 @@
                #size-cells = <1>;
                ranges = <0x00000000 0x80000000 0x40000000>;
                device_type = "memory";
-               reg = <0x00000000 0x20000000>;  /* 512MiB */
+               reg = <0x80000000 0x20000000>;  /* 512MiB */
        };
 };
index 2f0b33257db2e2ecf4749bee0d2f5ed3260dc3a3..06a9f294a2e600a4aa7f8f7b18316ceb60d87ef0 100644 (file)
                #size-cells = <1>;
                ranges = <0x00000000 0x80000000 0x40000000>;
                device_type = "memory";
-               reg = <0x00000000 0x20000000>;  /* 512MiB */
+               reg = <0x80000000 0x20000000>;  /* 512MiB */
        };
 };
index 911f069e0540500d60987e3b3bcce3abde2a48d3..b0eb0e7fe21d8a66b1d0ef5267f24dd6a1f8f254 100644 (file)
 
 / {
        compatible = "snps,nsim_hs";
+       #address-cells = <2>;
+       #size-cells = <2>;
        interrupt-parent = <&core_intc>;
 
+       memory {
+               device_type = "memory";
+               reg = <0x0 0x80000000 0x0 0x40000000    /* 1 GB low mem */
+                      0x1 0x00000000 0x0 0x40000000>;  /* 1 GB highmem */
+       };
+
        chosen {
                bootargs = "earlycon=arc_uart,mmio32,0xc0fc1000,115200n8 console=ttyARC0,115200n8";
        };
@@ -26,8 +34,8 @@
                #address-cells = <1>;
                #size-cells = <1>;
 
-               /* child and parent address space 1:1 mapped */
-               ranges;
+               /* only perip space at end of low mem accessible */
+               ranges = <0x80000000 0x0 0x80000000 0x80000000>;
 
                core_intc: core-interrupt-controller {
                        compatible = "snps,archs-intc";
index a870bdd5e404ca386bb2d8d5efd6b454677f0eae..296d371a335c8374b98efba984d95b705b3f343b 100644 (file)
@@ -32,6 +32,6 @@
 
        memory {
                device_type = "memory";
-               reg = <0x00000000 0x10000000>;  /* 256M */
+               reg = <0x80000000 0x10000000>;  /* 256M */
        };
 };
index 9393fd902f0d401a475db4f97646ee95e36220bb..84226bd48baf070a2ab5bea8cc0741ebee1251e9 100644 (file)
@@ -56,6 +56,6 @@
                #size-cells = <1>;
                ranges = <0x00000000 0x80000000 0x40000000>;
                device_type = "memory";
-               reg = <0x00000000 0x20000000>;  /* 512MiB */
+               reg = <0x80000000 0x20000000>;  /* 512MiB */
        };
 };
index 9bee8ed09eb03cd3e73997c5358d0791634c5add..31f0fb5fc91dec885a598a1769f7e167cbd8a780 100644 (file)
@@ -71,6 +71,6 @@
                #size-cells = <1>;
                ranges = <0x00000000 0x80000000 0x40000000>;
                device_type = "memory";
-               reg = <0x00000000 0x20000000>;  /* 512MiB */
+               reg = <0x80000000 0x20000000>;  /* 512MiB */
        };
 };
index 562dac6a7f7824f4469678999ffed77809a19a1d..c92c0ef1e9d290b2db437001338cf477c1df9513 100644 (file)
@@ -89,7 +89,6 @@ CONFIG_MMC=y
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_MMC_DW=y
-CONFIG_MMC_DW_IDMAC=y
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_EXT3_FS=y
 CONFIG_EXT4_FS=y
index 83a6d8d5cc589b02852a048d42543f027d6f9cae..cfac24e0e7b6565a48c3a22f6251e8992fbb84bb 100644 (file)
@@ -95,7 +95,6 @@ CONFIG_MMC=y
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_MMC_DW=y
-CONFIG_MMC_DW_IDMAC=y
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_EXT3_FS=y
 CONFIG_EXT4_FS=y
index f1e1c84e0dda25a6a7cc95dc34a4de542863babb..9922a118a15a3d28c99c85f1c67407142e75fb86 100644 (file)
@@ -96,7 +96,6 @@ CONFIG_MMC=y
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_MMC_DW=y
-CONFIG_MMC_DW_IDMAC=y
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_EXT3_FS=y
 CONFIG_EXT4_FS=y
index d8023bc8d1ad687c26a57ee6873509d0d1570222..7fac7d85ed6a32bb1abaa4f8e36c5d243cec06c0 100644 (file)
 
 /* gcc builtin sr needs reg param to be long immediate */
 #define write_aux_reg(reg_immed, val)          \
-               __builtin_arc_sr((unsigned int)val, reg_immed)
+               __builtin_arc_sr((unsigned int)(val), reg_immed)
 
 #else
 
@@ -327,8 +327,8 @@ struct bcr_generic {
  */
 
 struct cpuinfo_arc_mmu {
-       unsigned int ver:4, pg_sz_k:8, s_pg_sz_m:8, u_dtlb:6, u_itlb:6;
-       unsigned int num_tlb:16, sets:12, ways:4;
+       unsigned int ver:4, pg_sz_k:8, s_pg_sz_m:8, pad:10, sasid:1, pae:1;
+       unsigned int sets:12, ways:4, u_dtlb:8, u_itlb:8;
 };
 
 struct cpuinfo_arc_cache {
index c3ecda023e3a52a0aa0bd04cb5ba7b69c6f856a6..7730d302cadb5620feff02b2fbe5ebebf1f3e53d 100644 (file)
 #include <asm/barrier.h>
 #include <asm/smp.h>
 
-#define atomic_read(v)  ((v)->counter)
+#define atomic_read(v)  READ_ONCE((v)->counter)
 
 #ifdef CONFIG_ARC_HAS_LLSC
 
-#define atomic_set(v, i) (((v)->counter) = (i))
+#define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
 
 #ifdef CONFIG_ARC_STAR_9000923308
 
@@ -107,7 +107,7 @@ static inline int atomic_##op##_return(int i, atomic_t *v)          \
 #ifndef CONFIG_SMP
 
  /* violating atomic_xxx API locking protocol in UP for optimization sake */
-#define atomic_set(v, i) (((v)->counter) = (i))
+#define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
 
 #else
 
@@ -125,7 +125,7 @@ static inline void atomic_set(atomic_t *v, int i)
        unsigned long flags;
 
        atomic_ops_lock(flags);
-       v->counter = i;
+       WRITE_ONCE(v->counter, i);
        atomic_ops_unlock(flags);
 }
 
index e23ea6e7633a6e8cefbc31be639ac073a673b5f3..abf06e81c9290f6eafc441b563061e3389032922 100644 (file)
@@ -65,6 +65,7 @@ extern int ioc_exists;
 #if defined(CONFIG_ARC_MMU_V3) || defined(CONFIG_ARC_MMU_V4)
 #define ARC_REG_IC_PTAG                0x1E
 #endif
+#define ARC_REG_IC_PTAG_HI     0x1F
 
 /* Bit val in IC_CTRL */
 #define IC_CTRL_CACHE_DISABLE   0x1
@@ -77,6 +78,7 @@ extern int ioc_exists;
 #define ARC_REG_DC_FLSH                0x4B
 #define ARC_REG_DC_FLDL                0x4C
 #define ARC_REG_DC_PTAG                0x5C
+#define ARC_REG_DC_PTAG_HI     0x5F
 
 /* Bit val in DC_CTRL */
 #define DC_CTRL_INV_MODE_FLUSH  0x40
index 0992d3dbcc65f66e4e97925703ec9dc113a7b9a4..fbe3587c4f36f1add284667fd8a04edbe90ea2ef 100644 (file)
 
 void flush_cache_all(void);
 
-void flush_icache_range(unsigned long start, unsigned long end);
-void __sync_icache_dcache(unsigned long paddr, unsigned long vaddr, int len);
-void __inv_icache_page(unsigned long paddr, unsigned long vaddr);
-void __flush_dcache_page(unsigned long paddr, unsigned long vaddr);
+void flush_icache_range(unsigned long kstart, unsigned long kend);
+void __sync_icache_dcache(phys_addr_t paddr, unsigned long vaddr, int len);
+void __inv_icache_page(phys_addr_t paddr, unsigned long vaddr);
+void __flush_dcache_page(phys_addr_t paddr, unsigned long vaddr);
 
 #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
 
index 415443c2a8c4297d235796f8af4a71d2d07002fd..1aff3be9107563ca620072a49abf9e9925d4ec6b 100644 (file)
 
 .macro FAKE_RET_FROM_EXCPN
 
-       ld  r9, [sp, PT_status32]
-       bic r9, r9, (STATUS_U_MASK|STATUS_DE_MASK)
-       bset  r9, r9, STATUS_L_BIT
-       sr  r9, [erstatus]
-       mov r9, 55f
-       sr  r9, [eret]
-
+       lr      r9, [status32]
+       bclr    r9, r9, STATUS_AE_BIT
+       or      r9, r9, (STATUS_E1_MASK|STATUS_E2_MASK)
+       sr      r9, [erstatus]
+       mov     r9, 55f
+       sr      r9, [eret]
        rtie
 55:
 .endm
diff --git a/arch/arc/include/asm/highmem.h b/arch/arc/include/asm/highmem.h
new file mode 100644 (file)
index 0000000..b1585c9
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Synopsys, Inc. (www.synopsys.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 _ASM_HIGHMEM_H
+#define _ASM_HIGHMEM_H
+
+#ifdef CONFIG_HIGHMEM
+
+#include <uapi/asm/page.h>
+#include <asm/kmap_types.h>
+
+/* start after vmalloc area */
+#define FIXMAP_BASE            (PAGE_OFFSET - FIXMAP_SIZE - PKMAP_SIZE)
+#define FIXMAP_SIZE            PGDIR_SIZE      /* only 1 PGD worth */
+#define KM_TYPE_NR             ((FIXMAP_SIZE >> PAGE_SHIFT)/NR_CPUS)
+#define FIXMAP_ADDR(nr)                (FIXMAP_BASE + ((nr) << PAGE_SHIFT))
+
+/* start after fixmap area */
+#define PKMAP_BASE             (FIXMAP_BASE + FIXMAP_SIZE)
+#define PKMAP_SIZE             PGDIR_SIZE
+#define LAST_PKMAP             (PKMAP_SIZE >> PAGE_SHIFT)
+#define LAST_PKMAP_MASK                (LAST_PKMAP - 1)
+#define PKMAP_ADDR(nr)         (PKMAP_BASE + ((nr) << PAGE_SHIFT))
+#define PKMAP_NR(virt)         (((virt) - PKMAP_BASE) >> PAGE_SHIFT)
+
+#define kmap_prot              PAGE_KERNEL
+
+
+#include <asm/cacheflush.h>
+
+extern void *kmap(struct page *page);
+extern void *kmap_high(struct page *page);
+extern void *kmap_atomic(struct page *page);
+extern void __kunmap_atomic(void *kvaddr);
+extern void kunmap_high(struct page *page);
+
+extern void kmap_init(void);
+
+static inline void flush_cache_kmaps(void)
+{
+       flush_cache_all();
+}
+
+static inline void kunmap(struct page *page)
+{
+       BUG_ON(in_interrupt());
+       if (!PageHighMem(page))
+               return;
+       kunmap_high(page);
+}
+
+
+#endif
+
+#endif
diff --git a/arch/arc/include/asm/hugepage.h b/arch/arc/include/asm/hugepage.h
new file mode 100644 (file)
index 0000000..c5094de
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013-15 Synopsys, Inc. (www.synopsys.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 _ASM_ARC_HUGEPAGE_H
+#define _ASM_ARC_HUGEPAGE_H
+
+#include <linux/types.h>
+#include <asm-generic/pgtable-nopmd.h>
+
+static inline pte_t pmd_pte(pmd_t pmd)
+{
+       return __pte(pmd_val(pmd));
+}
+
+static inline pmd_t pte_pmd(pte_t pte)
+{
+       return __pmd(pte_val(pte));
+}
+
+#define pmd_wrprotect(pmd)     pte_pmd(pte_wrprotect(pmd_pte(pmd)))
+#define pmd_mkwrite(pmd)       pte_pmd(pte_mkwrite(pmd_pte(pmd)))
+#define pmd_mkdirty(pmd)       pte_pmd(pte_mkdirty(pmd_pte(pmd)))
+#define pmd_mkold(pmd)         pte_pmd(pte_mkold(pmd_pte(pmd)))
+#define pmd_mkyoung(pmd)       pte_pmd(pte_mkyoung(pmd_pte(pmd)))
+#define pmd_mkhuge(pmd)                pte_pmd(pte_mkhuge(pmd_pte(pmd)))
+#define pmd_mknotpresent(pmd)  pte_pmd(pte_mknotpresent(pmd_pte(pmd)))
+#define pmd_mksplitting(pmd)   pte_pmd(pte_mkspecial(pmd_pte(pmd)))
+#define pmd_mkclean(pmd)       pte_pmd(pte_mkclean(pmd_pte(pmd)))
+
+#define pmd_write(pmd)         pte_write(pmd_pte(pmd))
+#define pmd_young(pmd)         pte_young(pmd_pte(pmd))
+#define pmd_pfn(pmd)           pte_pfn(pmd_pte(pmd))
+#define pmd_dirty(pmd)         pte_dirty(pmd_pte(pmd))
+#define pmd_special(pmd)       pte_special(pmd_pte(pmd))
+
+#define mk_pmd(page, prot)     pte_pmd(mk_pte(page, prot))
+
+#define pmd_trans_huge(pmd)    (pmd_val(pmd) & _PAGE_HW_SZ)
+#define pmd_trans_splitting(pmd)       (pmd_trans_huge(pmd) && pmd_special(pmd))
+
+#define pfn_pmd(pfn, prot)     (__pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot)))
+
+static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
+{
+        /*
+         * open-coded pte_modify() with additional retaining of HW_SZ bit
+         * so that pmd_trans_huge() remains true for this PMD
+         */
+        return __pmd((pmd_val(pmd) & (_PAGE_CHG_MASK | _PAGE_HW_SZ)) | pgprot_val(newprot));
+}
+
+static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr,
+                             pmd_t *pmdp, pmd_t pmd)
+{
+       *pmdp = pmd;
+}
+
+extern void update_mmu_cache_pmd(struct vm_area_struct *vma, unsigned long addr,
+                                pmd_t *pmd);
+
+#define has_transparent_hugepage() 1
+
+/* Generic variants assume pgtable_t is struct page *, hence need for these */
+#define __HAVE_ARCH_PGTABLE_DEPOSIT
+extern void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
+                                      pgtable_t pgtable);
+
+#define __HAVE_ARCH_PGTABLE_WITHDRAW
+extern pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
+
+#define __HAVE_ARCH_FLUSH_PMD_TLB_RANGE
+extern void flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
+                               unsigned long end);
+
+#endif
index bc51036373261c6068292830adae89a80b5d0c9c..4fd7d62a6e30aa513b9e04f6ee881c219b96da96 100644 (file)
@@ -16,6 +16,7 @@
 #ifdef CONFIG_ISA_ARCOMPACT
 #define TIMER0_IRQ      3
 #define TIMER1_IRQ      4
+#define IPI_IRQ                (NR_CPU_IRQS-1) /* dummy to enable SMP build for up hardware */
 #else
 #define TIMER0_IRQ      16
 #define TIMER1_IRQ      17
index aa805575c320ae6be1cfcd28e6c86f60d5f17ba1..d8c608174617783496b8855bc6ed19de9b6f67cd 100644 (file)
 #define STATUS_E2_BIT          2       /* Int 2 enable */
 #define STATUS_A1_BIT          3       /* Int 1 active */
 #define STATUS_A2_BIT          4       /* Int 2 active */
+#define STATUS_AE_BIT          5       /* Exception active */
 
 #define STATUS_E1_MASK         (1<<STATUS_E1_BIT)
 #define STATUS_E2_MASK         (1<<STATUS_E2_BIT)
 #define STATUS_A1_MASK         (1<<STATUS_A1_BIT)
 #define STATUS_A2_MASK         (1<<STATUS_A2_BIT)
+#define STATUS_AE_MASK         (1<<STATUS_AE_BIT)
 #define STATUS_IE_MASK         (STATUS_E1_MASK | STATUS_E2_MASK)
 
 /* Other Interrupt Handling related Aux regs */
@@ -91,7 +93,19 @@ static inline void arch_local_irq_restore(unsigned long flags)
 /*
  * Unconditionally Enable IRQs
  */
-extern void arch_local_irq_enable(void);
+static inline void arch_local_irq_enable(void)
+{
+       unsigned long temp;
+
+       __asm__ __volatile__(
+       "       lr   %0, [status32]     \n"
+       "       or   %0, %0, %1         \n"
+       "       flag %0                 \n"
+       : "=&r"(temp)
+       : "n"((STATUS_E1_MASK | STATUS_E2_MASK))
+       : "cc", "memory");
+}
+
 
 /*
  * Unconditionally Disable IRQs
diff --git a/arch/arc/include/asm/kmap_types.h b/arch/arc/include/asm/kmap_types.h
new file mode 100644 (file)
index 0000000..f0d7f6a
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2015 Synopsys, Inc. (www.synopsys.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 _ASM_KMAP_TYPES_H
+#define _ASM_KMAP_TYPES_H
+
+/*
+ * We primarily need to define KM_TYPE_NR here but that in turn
+ * is a function of PGDIR_SIZE etc.
+ * To avoid circular deps issue, put everything in asm/highmem.h
+ */
+#endif
index e8993a2be6c238a11e557986249773148a6eef7d..6ff657a904b61e700421b60423991273df93e1e1 100644 (file)
  * @dt_compat:         Array of device tree 'compatible' strings
  *                     (XXX: although only 1st entry is looked at)
  * @init_early:                Very early callback [called from setup_arch()]
- * @init_irq:          setup external IRQ controllers [called from init_IRQ()]
- * @init_smp:          for each CPU (e.g. setup IPI)
+ * @init_cpu_smp:      for each CPU as it is coming up (SMP as well as UP)
  *                     [(M):init_IRQ(), (o):start_kernel_secondary()]
- * @init_time:         platform specific clocksource/clockevent registration
- *                     [called from time_init()]
  * @init_machine:      arch initcall level callback (e.g. populate static
  *                     platform devices or parse Devicetree)
  * @init_late:         Late initcall level callback
 struct machine_desc {
        const char              *name;
        const char              **dt_compat;
-
        void                    (*init_early)(void);
-       void                    (*init_irq)(void);
 #ifdef CONFIG_SMP
-       void                    (*init_smp)(unsigned int);
+       void                    (*init_cpu_smp)(unsigned int);
 #endif
-       void                    (*init_time)(void);
        void                    (*init_machine)(void);
        void                    (*init_late)(void);
 
index 52c11f0bb0e5b5afbeacda75ef9c014032de4fb4..46f4e5351b2a56e96d440a27201fc612d2e9c195 100644 (file)
@@ -86,9 +86,6 @@ static inline void __mcip_cmd_data(unsigned int cmd, unsigned int param,
        __mcip_cmd(cmd, param);
 }
 
-extern void mcip_init_early_smp(void);
-extern void mcip_init_smp(unsigned int cpu);
-
 #endif
 
 #endif
index 0f9c3eb5327e4494f4a310e62e194c4457c08bea..b144d7ca7d2076d7f5a41b9bfa179f0f64d21d83 100644 (file)
@@ -24,6 +24,7 @@
 #if (CONFIG_ARC_MMU_VER < 4)
 #define ARC_REG_TLBPD0         0x405
 #define ARC_REG_TLBPD1         0x406
+#define ARC_REG_TLBPD1HI       0       /* Dummy: allows code sharing with ARC700 */
 #define ARC_REG_TLBINDEX       0x407
 #define ARC_REG_TLBCOMMAND     0x408
 #define ARC_REG_PID            0x409
@@ -31,6 +32,7 @@
 #else
 #define ARC_REG_TLBPD0         0x460
 #define ARC_REG_TLBPD1         0x461
+#define ARC_REG_TLBPD1HI       0x463
 #define ARC_REG_TLBINDEX       0x464
 #define ARC_REG_TLBCOMMAND     0x465
 #define ARC_REG_PID            0x468
@@ -83,6 +85,11 @@ void arc_mmu_init(void);
 extern char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len);
 void read_decode_mmu_bcr(void);
 
+static inline int is_pae40_enabled(void)
+{
+       return IS_ENABLED(CONFIG_ARC_HAS_PAE40);
+}
+
 #endif /* !__ASSEMBLY__ */
 
 #endif
index 9c8aa41e45c2248b0ee39a23c3802e4f7425d358..429957f1c2365566006d674bb37311cddb97cc00 100644 (file)
@@ -43,7 +43,6 @@ typedef struct {
 typedef struct {
        unsigned long pgprot;
 } pgprot_t;
-typedef unsigned long pgtable_t;
 
 #define pte_val(x)      ((x).pte)
 #define pgd_val(x)      ((x).pgd)
@@ -57,20 +56,26 @@ typedef unsigned long pgtable_t;
 
 #else /* !STRICT_MM_TYPECHECKS */
 
+#ifdef CONFIG_ARC_HAS_PAE40
+typedef unsigned long long pte_t;
+#else
 typedef unsigned long pte_t;
+#endif
 typedef unsigned long pgd_t;
 typedef unsigned long pgprot_t;
-typedef unsigned long pgtable_t;
 
 #define pte_val(x)     (x)
 #define pgd_val(x)     (x)
 #define pgprot_val(x)  (x)
 #define __pte(x)       (x)
+#define __pgd(x)       (x)
 #define __pgprot(x)    (x)
 #define pte_pgprot(x)  (x)
 
 #endif
 
+typedef pte_t * pgtable_t;
+
 #define ARCH_PFN_OFFSET     (CONFIG_LINUX_LINK_BASE >> PAGE_SHIFT)
 
 #define pfn_valid(pfn)      (((pfn) - ARCH_PFN_OFFSET) < max_mapnr)
index 81208bfd9dcbc460c9875ad509bb0cb11348c07e..86ed671286df377bb6231013724737a135a86d69 100644 (file)
@@ -49,7 +49,7 @@ pmd_populate(struct mm_struct *mm, pmd_t *pmd, pgtable_t ptep)
 
 static inline int __get_order_pgd(void)
 {
-       return get_order(PTRS_PER_PGD * 4);
+       return get_order(PTRS_PER_PGD * sizeof(pgd_t));
 }
 
 static inline pgd_t *pgd_alloc(struct mm_struct *mm)
@@ -87,7 +87,7 @@ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
 
 static inline int __get_order_pte(void)
 {
-       return get_order(PTRS_PER_PTE * 4);
+       return get_order(PTRS_PER_PTE * sizeof(pte_t));
 }
 
 static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
@@ -107,10 +107,10 @@ pte_alloc_one(struct mm_struct *mm, unsigned long address)
        pgtable_t pte_pg;
        struct page *page;
 
-       pte_pg = __get_free_pages(GFP_KERNEL | __GFP_REPEAT, __get_order_pte());
+       pte_pg = (pgtable_t)__get_free_pages(GFP_KERNEL | __GFP_REPEAT, __get_order_pte());
        if (!pte_pg)
                return 0;
-       memzero((void *)pte_pg, PTRS_PER_PTE * 4);
+       memzero((void *)pte_pg, PTRS_PER_PTE * sizeof(pte_t));
        page = virt_to_page(pte_pg);
        if (!pgtable_page_ctor(page)) {
                __free_page(page);
@@ -128,12 +128,12 @@ static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
 static inline void pte_free(struct mm_struct *mm, pgtable_t ptep)
 {
        pgtable_page_dtor(virt_to_page(ptep));
-       free_pages(ptep, __get_order_pte());
+       free_pages((unsigned long)ptep, __get_order_pte());
 }
 
 #define __pte_free_tlb(tlb, pte, addr)  pte_free((tlb)->mm, pte)
 
 #define check_pgt_cache()   do { } while (0)
-#define pmd_pgtable(pmd) pmd_page_vaddr(pmd)
+#define pmd_pgtable(pmd)       ((pgtable_t) pmd_page_vaddr(pmd))
 
 #endif /* _ASM_ARC_PGALLOC_H */
index 1281718802f7c8e4d3f71bdf50b3affd57fd66d6..57af2f05ae8459e2ee2427d231370269894ba95f 100644 (file)
@@ -38,6 +38,7 @@
 #include <asm/page.h>
 #include <asm/mmu.h>
 #include <asm-generic/pgtable-nopmd.h>
+#include <linux/const.h>
 
 /**************************************************************************
  * Page Table Flags
@@ -60,7 +61,8 @@
 #define _PAGE_EXECUTE       (1<<3)     /* Page has user execute perm (H) */
 #define _PAGE_WRITE         (1<<4)     /* Page has user write perm (H) */
 #define _PAGE_READ          (1<<5)     /* Page has user read perm (H) */
-#define _PAGE_MODIFIED      (1<<6)     /* Page modified (dirty) (S) */
+#define _PAGE_DIRTY         (1<<6)     /* Page modified (dirty) (S) */
+#define _PAGE_SPECIAL       (1<<7)
 #define _PAGE_GLOBAL        (1<<8)     /* Page is global (H) */
 #define _PAGE_PRESENT       (1<<10)    /* TLB entry is valid (H) */
 
@@ -71,7 +73,8 @@
 #define _PAGE_WRITE         (1<<2)     /* Page has user write perm (H) */
 #define _PAGE_READ          (1<<3)     /* Page has user read perm (H) */
 #define _PAGE_ACCESSED      (1<<4)     /* Page is accessed (S) */
-#define _PAGE_MODIFIED      (1<<5)     /* Page modified (dirty) (S) */
+#define _PAGE_DIRTY         (1<<5)     /* Page modified (dirty) (S) */
+#define _PAGE_SPECIAL       (1<<6)
 
 #if (CONFIG_ARC_MMU_VER >= 4)
 #define _PAGE_WTHRU         (1<<7)     /* Page cache mode write-thru (H) */
 #define _PAGE_PRESENT       (1<<9)     /* TLB entry is valid (H) */
 
 #if (CONFIG_ARC_MMU_VER >= 4)
-#define _PAGE_SZ            (1<<10)    /* Page Size indicator (H) */
+#define _PAGE_HW_SZ         (1<<10)    /* Page Size indicator (H): 0 normal, 1 super */
 #endif
 
 #define _PAGE_SHARED_CODE   (1<<11)    /* Shared Code page with cmn vaddr
                                           usable for shared TLB entries (H) */
+
+#define _PAGE_UNUSED_BIT    (1<<12)
 #endif
 
 /* vmalloc permissions */
 #define _K_PAGE_PERMS  (_PAGE_EXECUTE | _PAGE_WRITE | _PAGE_READ | \
                        _PAGE_GLOBAL | _PAGE_PRESENT)
 
-#ifdef CONFIG_ARC_CACHE_PAGES
-#define _PAGE_DEF_CACHEABLE _PAGE_CACHEABLE
-#else
-#define _PAGE_DEF_CACHEABLE (0)
+#ifndef CONFIG_ARC_CACHE_PAGES
+#undef _PAGE_CACHEABLE
+#define _PAGE_CACHEABLE 0
 #endif
 
-/* Helper for every "user" page
- * -kernel can R/W/X
- * -by default cached, unless config otherwise
- * -present in memory
- */
-#define ___DEF (_PAGE_PRESENT | _PAGE_DEF_CACHEABLE)
+#ifndef _PAGE_HW_SZ
+#define _PAGE_HW_SZ    0
+#endif
+
+/* Defaults for every user page */
+#define ___DEF (_PAGE_PRESENT | _PAGE_CACHEABLE)
 
 /* Set of bits not changed in pte_modify */
-#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_MODIFIED)
+#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
 
 /* More Abbrevaited helpers */
 #define PAGE_U_NONE     __pgprot(___DEF)
  * user vaddr space - visible in all addr spaces, but kernel mode only
  * Thus Global, all-kernel-access, no-user-access, cached
  */
-#define PAGE_KERNEL          __pgprot(_K_PAGE_PERMS | _PAGE_DEF_CACHEABLE)
+#define PAGE_KERNEL          __pgprot(_K_PAGE_PERMS | _PAGE_CACHEABLE)
 
 /* ioremap */
 #define PAGE_KERNEL_NO_CACHE __pgprot(_K_PAGE_PERMS)
 
 /* Masks for actual TLB "PD"s */
-#define PTE_BITS_IN_PD0                (_PAGE_GLOBAL | _PAGE_PRESENT)
+#define PTE_BITS_IN_PD0                (_PAGE_GLOBAL | _PAGE_PRESENT | _PAGE_HW_SZ)
 #define PTE_BITS_RWX           (_PAGE_EXECUTE | _PAGE_WRITE | _PAGE_READ)
+
+#ifdef CONFIG_ARC_HAS_PAE40
+#define PTE_BITS_NON_RWX_IN_PD1        (0xff00000000 | PAGE_MASK | _PAGE_CACHEABLE)
+#else
 #define PTE_BITS_NON_RWX_IN_PD1        (PAGE_MASK | _PAGE_CACHEABLE)
+#endif
 
 /**************************************************************************
  * Mapping of vm_flags (Generic VM) to PTE flags (arch specific)
 
 /* Optimal Sizing of Pg Tbl - based on MMU page size */
 #if defined(CONFIG_ARC_PAGE_SIZE_8K)
-#define BITS_FOR_PTE   8
+#define BITS_FOR_PTE   8               /* 11:8:13 */
 #elif defined(CONFIG_ARC_PAGE_SIZE_16K)
-#define BITS_FOR_PTE   8
+#define BITS_FOR_PTE   8               /* 10:8:14 */
 #elif defined(CONFIG_ARC_PAGE_SIZE_4K)
-#define BITS_FOR_PTE   9
+#define BITS_FOR_PTE   9               /* 11:9:12 */
 #endif
 
 #define BITS_FOR_PGD   (32 - BITS_FOR_PTE - BITS_IN_PAGE)
 
-#define PGDIR_SHIFT    (BITS_FOR_PTE + BITS_IN_PAGE)
+#define PGDIR_SHIFT    (32 - BITS_FOR_PGD)
 #define PGDIR_SIZE     (1UL << PGDIR_SHIFT)    /* vaddr span, not PDG sz */
 #define PGDIR_MASK     (~(PGDIR_SIZE-1))
 
-#ifdef __ASSEMBLY__
-#define        PTRS_PER_PTE    (1 << BITS_FOR_PTE)
-#define        PTRS_PER_PGD    (1 << BITS_FOR_PGD)
-#else
-#define        PTRS_PER_PTE    (1UL << BITS_FOR_PTE)
-#define        PTRS_PER_PGD    (1UL << BITS_FOR_PGD)
-#endif
+#define        PTRS_PER_PTE    _BITUL(BITS_FOR_PTE)
+#define        PTRS_PER_PGD    _BITUL(BITS_FOR_PGD)
+
 /*
  * Number of entries a user land program use.
  * TASK_SIZE is the maximum vaddr that can be used by a userland program.
@@ -270,15 +275,10 @@ static inline void pmd_set(pmd_t *pmdp, pte_t *ptep)
                (unsigned long)(((pte_val(x) - CONFIG_LINUX_LINK_BASE) >> \
                                PAGE_SHIFT)))
 
-#define mk_pte(page, pgprot)                                           \
-({                                                                     \
-       pte_t pte;                                                      \
-       pte_val(pte) = __pa(page_address(page)) + pgprot_val(pgprot);   \
-       pte;                                                            \
-})
-
+#define mk_pte(page, prot)     pfn_pte(page_to_pfn(page), prot)
 #define pte_pfn(pte)           (pte_val(pte) >> PAGE_SHIFT)
-#define pfn_pte(pfn, prot)     (__pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)))
+#define pfn_pte(pfn, prot)     (__pte(((pte_t)(pfn) << PAGE_SHIFT) | \
+                                pgprot_val(prot)))
 #define __pte_index(addr)      (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
 
 /*
@@ -295,23 +295,26 @@ static inline void pmd_set(pmd_t *pmdp, pte_t *ptep)
 /* Zoo of pte_xxx function */
 #define pte_read(pte)          (pte_val(pte) & _PAGE_READ)
 #define pte_write(pte)         (pte_val(pte) & _PAGE_WRITE)
-#define pte_dirty(pte)         (pte_val(pte) & _PAGE_MODIFIED)
+#define pte_dirty(pte)         (pte_val(pte) & _PAGE_DIRTY)
 #define pte_young(pte)         (pte_val(pte) & _PAGE_ACCESSED)
-#define pte_special(pte)       (0)
+#define pte_special(pte)       (pte_val(pte) & _PAGE_SPECIAL)
 
 #define PTE_BIT_FUNC(fn, op) \
        static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; }
 
+PTE_BIT_FUNC(mknotpresent,     &= ~(_PAGE_PRESENT));
 PTE_BIT_FUNC(wrprotect,        &= ~(_PAGE_WRITE));
 PTE_BIT_FUNC(mkwrite,  |= (_PAGE_WRITE));
-PTE_BIT_FUNC(mkclean,  &= ~(_PAGE_MODIFIED));
-PTE_BIT_FUNC(mkdirty,  |= (_PAGE_MODIFIED));
+PTE_BIT_FUNC(mkclean,  &= ~(_PAGE_DIRTY));
+PTE_BIT_FUNC(mkdirty,  |= (_PAGE_DIRTY));
 PTE_BIT_FUNC(mkold,    &= ~(_PAGE_ACCESSED));
 PTE_BIT_FUNC(mkyoung,  |= (_PAGE_ACCESSED));
 PTE_BIT_FUNC(exprotect,        &= ~(_PAGE_EXECUTE));
 PTE_BIT_FUNC(mkexec,   |= (_PAGE_EXECUTE));
+PTE_BIT_FUNC(mkspecial,        |= (_PAGE_SPECIAL));
+PTE_BIT_FUNC(mkhuge,   |= (_PAGE_HW_SZ));
 
-static inline pte_t pte_mkspecial(pte_t pte) { return pte; }
+#define __HAVE_ARCH_PTE_SPECIAL
 
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
@@ -357,7 +360,6 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
 #define pgd_offset_fast(mm, addr)      pgd_offset(mm, addr)
 #endif
 
-extern void paging_init(void);
 extern pgd_t swapper_pg_dir[] __aligned(PAGE_SIZE);
 void update_mmu_cache(struct vm_area_struct *vma, unsigned long address,
                      pte_t *ptep);
@@ -383,6 +385,10 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address,
  * remap a physical page `pfn' of size `size' with page protection `prot'
  * into virtual address `from'
  */
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+#include <asm/hugepage.h>
+#endif
+
 #include <asm-generic/pgtable.h>
 
 /* to cope with aliasing VIPT cache */
index ee682d8e0213c5c6c2fac2d70f39dff23b15854a..44545354e9e85616b703f531787ed74def265bf5 100644 (file)
@@ -114,7 +114,12 @@ extern unsigned int get_wchan(struct task_struct *p);
  * -----------------------------------------------------------------------------
  */
 #define VMALLOC_START  0x70000000
-#define VMALLOC_SIZE   (PAGE_OFFSET - VMALLOC_START)
+
+/*
+ * 1 PGDIR_SIZE each for fixmap/pkmap, 2 PGDIR_SIZE gutter
+ * See asm/highmem.h for details
+ */
+#define VMALLOC_SIZE   (PAGE_OFFSET - VMALLOC_START - PGDIR_SIZE * 4)
 #define VMALLOC_END    (VMALLOC_START + VMALLOC_SIZE)
 
 #define USER_KERNEL_GUTTER    0x10000000
index 6e3ef5ba4f74adca5d4fd1ed78440579166b4b64..307846691be6ad7bcad8abcdc217af84ddec5609 100644 (file)
@@ -33,4 +33,11 @@ extern int root_mountflags, end_mem;
 void setup_processor(void);
 void __init setup_arch_memory(void);
 
+/* Helpers used in arc_*_mumbojumbo routines */
+#define IS_AVAIL1(v, s)                ((v) ? s : "")
+#define IS_DISABLED_RUN(v)     ((v) ? "" : "(disabled) ")
+#define IS_USED_RUN(v)         ((v) ? "" : "(not used) ")
+#define IS_USED_CFG(cfg)       IS_USED_RUN(IS_ENABLED(cfg))
+#define IS_AVAIL2(v, s, cfg)   IS_AVAIL1(v, s), IS_AVAIL1(v, IS_USED_CFG(cfg))
+
 #endif /* __ASMARC_SETUP_H */
index 3845b9e94f69b0ee5dbda11fe9467c3d4b517aed..133c867d15af0a627576d9faf89c0569220a6f47 100644 (file)
@@ -45,12 +45,19 @@ extern int smp_ipi_irq_setup(int cpu, int irq);
  * struct plat_smp_ops - SMP callbacks provided by platform to ARC SMP
  *
  * @info:              SoC SMP specific info for /proc/cpuinfo etc
+ * @init_early_smp:    A SMP specific h/w block can init itself
+ *                     Could be common across platforms so not covered by
+ *                     mach_desc->init_early()
+ * @init_irq_cpu:      Called for each core so SMP h/w block driver can do
+ *                     any needed setup per cpu (e.g. IPI request)
  * @cpu_kick:          For Master to kickstart a cpu (optionally at a PC)
  * @ipi_send:          To send IPI to a @cpu
  * @ips_clear:         To clear IPI received at @irq
  */
 struct plat_smp_ops {
        const char      *info;
+       void            (*init_early_smp)(void);
+       void            (*init_irq_cpu)(int cpu);
        void            (*cpu_kick)(int cpu, unsigned long pc);
        void            (*ipi_send)(int cpu);
        void            (*ipi_clear)(int irq);
index 71c7b2e4b8745002083e71fd19ae28305d62972e..1fe9c8c80280b9c53006a695994548aa6296cd56 100644 (file)
@@ -17,6 +17,8 @@ void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
 void local_flush_tlb_kernel_range(unsigned long start, unsigned long end);
 void local_flush_tlb_range(struct vm_area_struct *vma,
                           unsigned long start, unsigned long end);
+void local_flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
+                              unsigned long end);
 
 #ifndef CONFIG_SMP
 #define flush_tlb_range(vma, s, e)     local_flush_tlb_range(vma, s, e)
@@ -24,6 +26,7 @@ void local_flush_tlb_range(struct vm_area_struct *vma,
 #define flush_tlb_kernel_range(s, e)   local_flush_tlb_kernel_range(s, e)
 #define flush_tlb_all()                        local_flush_tlb_all()
 #define flush_tlb_mm(mm)               local_flush_tlb_mm(mm)
+#define flush_pmd_tlb_range(vma, s, e) local_flush_pmd_tlb_range(vma, s, e)
 #else
 extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
                                                         unsigned long end);
@@ -31,5 +34,7 @@ extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
 extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
 extern void flush_tlb_all(void);
 extern void flush_tlb_mm(struct mm_struct *mm);
+extern void flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
+
 #endif /* CONFIG_SMP */
 #endif
index 9d129a2a1351951465b22fd020f568abe1843b7a..059aff38f10ab46892470a87e60ddd59e627d710 100644 (file)
@@ -9,6 +9,8 @@
 #ifndef _UAPI__ASM_ARC_PAGE_H
 #define _UAPI__ASM_ARC_PAGE_H
 
+#include <linux/const.h>
+
 /* PAGE_SHIFT determines the page size */
 #if defined(CONFIG_ARC_PAGE_SIZE_16K)
 #define PAGE_SHIFT 14
 #define PAGE_SHIFT 13
 #endif
 
-#ifdef __ASSEMBLY__
-#define PAGE_SIZE      (1 << PAGE_SHIFT)
-#define PAGE_OFFSET    (0x80000000)
-#else
-#define PAGE_SIZE      (1UL << PAGE_SHIFT)     /* Default 8K */
-#define PAGE_OFFSET    (0x80000000UL)          /* Kernel starts at 2G onwards */
-#endif
+#define PAGE_SIZE      _BITUL(PAGE_SHIFT)      /* Default 8K */
+#define PAGE_OFFSET    _AC(0x80000000, UL)     /* Kernel starts at 2G onwrds */
 
 #define PAGE_MASK      (~(PAGE_SIZE-1))
 
index 8fa76567e40299e0449b01d4d6dae6e7b8918807..445e63a10754fbdb0be82663af03f489282cfce5 100644 (file)
@@ -24,7 +24,7 @@
        .align 4
 
 # Initial 16 slots are Exception Vectors
-VECTOR stext                   ; Restart Vector (jump to entry point)
+VECTOR res_service             ; Reset Vector
 VECTOR mem_service             ; Mem exception
 VECTOR instr_service           ; Instrn Error
 VECTOR EV_MachineCheck         ; Fatal Machine check
index 15d457b4403ae4a9ab55f455fd7e2ad1492e780c..59f52035b4ea34a582b50b80e5db48893c1dc7bf 100644 (file)
@@ -86,7 +86,7 @@
  */
 
 ; ********* Critical System Events **********************
-VECTOR   res_service             ; 0x0, Restart Vector  (0x0)
+VECTOR   res_service             ; 0x0, Reset Vector   (0x0)
 VECTOR   mem_service             ; 0x8, Mem exception   (0x1)
 VECTOR   instr_service           ; 0x10, Instrn Error   (0x2)
 
@@ -155,13 +155,9 @@ int2_saved_reg:
 ; ---------------------------------------------
        .section .text, "ax",@progbits
 
-res_service:           ; processor restart
-       flag    0x1     ; not implemented
-       nop
-       nop
 
-reserved:              ; processor restart
-       rtie            ; jump to processor initializations
+reserved:
+       flag 1          ; Unexpected event, halt
 
 ;##################### Interrupt Handling ##############################
 
@@ -175,12 +171,25 @@ ENTRY(handle_interrupt_level2)
 
        ;------------------------------------------------------
        ; if L2 IRQ interrupted a L1 ISR, disable preemption
+       ;
+       ; This is to avoid a potential L1-L2-L1 scenario
+       ;  -L1 IRQ taken
+       ;  -L2 interrupts L1 (before L1 ISR could run)
+       ;  -preemption off IRQ, user task in syscall picked to run
+       ;  -RTIE to userspace
+       ;       Returns from L2 context fine
+       ;       But both L1 and L2 re-enabled, so another L1 can be taken
+       ;       while prev L1 is still unserviced
+       ;
        ;------------------------------------------------------
 
+       ; L2 interrupting L1 implies both L2 and L1 active
+       ; However both A2 and A1 are NOT set in STATUS32, thus
+       ; need to check STATUS32_L2 to determine if L1 was active
+
        ld r9, [sp, PT_status32]        ; get statu32_l2 (saved in pt_regs)
        bbit0 r9, STATUS_A1_BIT, 1f     ; L1 not active when L2 IRQ, so normal
 
-       ; A1 is set in status32_l2
        ; bump thread_info->preempt_count (Disable preemption)
        GET_CURR_THR_INFO_FROM_SP   r10
        ld      r9, [r10, THREAD_INFO_PREEMPT_COUNT]
@@ -320,11 +329,10 @@ END(call_do_page_fault)
        ; Note that we use realtime STATUS32 (not pt_regs->status32) to
        ; decide that.
 
-       ; if Returning from Exception
-       btst   r10, STATUS_AE_BIT
-       bnz    .Lexcep_ret
+       and.f   0, r10, (STATUS_A1_MASK|STATUS_A2_MASK)
+       bz      .Lexcep_or_pure_K_ret
 
-       ; Not Exception so maybe Interrupts (Level 1 or 2)
+       ; Returning from Interrupts (Level 1 or 2)
 
 #ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS
 
@@ -365,8 +373,7 @@ END(call_do_page_fault)
        st      r9, [r10, THREAD_INFO_PREEMPT_COUNT]
 
 149:
-       ;return from level 2
-       INTERRUPT_EPILOGUE 2
+       INTERRUPT_EPILOGUE 2    ; return from level 2 interrupt
 debug_marker_l2:
        rtie
 
@@ -374,15 +381,11 @@ not_level2_interrupt:
 
 #endif
 
-       bbit0  r10, STATUS_A1_BIT, .Lpure_k_mode_ret
-
-       ;return from level 1
-       INTERRUPT_EPILOGUE 1
+       INTERRUPT_EPILOGUE 1    ; return from level 1 interrupt
 debug_marker_l1:
        rtie
 
-.Lexcep_ret:
-.Lpure_k_mode_ret:
+.Lexcep_or_pure_K_ret:
 
        ;this case is for syscalls or Exceptions or pure kernel mode
 
index 812f95e6ae6946d56550cbd80a0baaa8a6e94cc0..689dd867fdff53eeafa0d01d980ecf425f66a759 100644 (file)
 .endm
 
        .section .init.text, "ax",@progbits
-       .type stext, @function
-       .globl stext
-stext:
-       ;-------------------------------------------------------------------
-       ; Don't clobber r0-r2 yet. It might have bootloader provided info
-       ;-------------------------------------------------------------------
+
+;----------------------------------------------------------------
+; Default Reset Handler (jumped into from Reset vector)
+; - Don't clobber r0,r1,r2 as they might have u-boot provided args
+; - Platforms can override this weak version if needed
+;----------------------------------------------------------------
+WEAK(res_service)
+       j       stext
+END(res_service)
+
+;----------------------------------------------------------------
+; Kernel Entry point
+;----------------------------------------------------------------
+ENTRY(stext)
 
        CPU_EARLY_SETUP
 
 #ifdef CONFIG_SMP
-       ; Ensure Boot (Master) proceeds. Others wait in platform dependent way
-       ;       IDENTITY Reg [ 3  2  1  0 ]
-       ;       (cpu-id)             ^^^        => Zero for UP ARC700
-       ;                                       => #Core-ID if SMP (Master 0)
-       ; Note that non-boot CPUs might not land here if halt-on-reset and
-       ; instead breath life from @first_lines_of_secondary, but we still
-       ; need to make sure only boot cpu takes this path.
        GET_CPU_ID  r5
        cmp     r5, 0
-       mov.ne  r0, r5
-       jne     arc_platform_smp_wait_to_boot
+       mov.nz  r0, r5
+#ifdef CONFIG_ARC_SMP_HALT_ON_RESET
+       ; Non-Master can proceed as system would be booted sufficiently
+       jnz     first_lines_of_secondary
+#else
+       ; Non-Masters wait for Master to boot enough and bring them up
+       jnz     arc_platform_smp_wait_to_boot
 #endif
+       ; Master falls thru
+#endif
+
        ; Clear BSS before updating any globals
        ; XXX: use ZOL here
        mov     r5, __bss_start
@@ -102,18 +111,14 @@ stext:
        GET_TSK_STACK_BASE r9, sp       ; r9 = tsk, sp = stack base(output)
 
        j       start_kernel    ; "C" entry point
+END(stext)
 
 #ifdef CONFIG_SMP
 ;----------------------------------------------------------------
 ;     First lines of code run by secondary before jumping to 'C'
 ;----------------------------------------------------------------
        .section .text, "ax",@progbits
-       .type first_lines_of_secondary, @function
-       .globl first_lines_of_secondary
-
-first_lines_of_secondary:
-
-       CPU_EARLY_SETUP
+ENTRY(first_lines_of_secondary)
 
        ; setup per-cpu idle task as "current" on this CPU
        ld      r0, [@secondary_idle_tsk]
@@ -126,5 +131,5 @@ first_lines_of_secondary:
        GET_TSK_STACK_BASE r0, sp
 
        j       start_kernel_secondary
-
+END(first_lines_of_secondary)
 #endif
index 039fac30b5c1f2fca837c6f9cc11de0e0c56c35d..06bcedf19b622b4ef26503660349bc07a8a52d2e 100644 (file)
@@ -79,17 +79,16 @@ static struct irq_chip onchip_intc = {
 static int arc_intc_domain_map(struct irq_domain *d, unsigned int irq,
                               irq_hw_number_t hw)
 {
-       /*
-        * XXX: the IPI IRQ needs to be handled like TIMER too. However ARC core
-        *      code doesn't own it (like TIMER0). ISS IDU / ezchip define it
-        *      in platform header which can't be included here as it goes
-        *      against multi-platform image philisophy
-        */
-       if (irq == TIMER0_IRQ)
+       switch (irq) {
+       case TIMER0_IRQ:
+#ifdef CONFIG_SMP
+       case IPI_IRQ:
+#endif
                irq_set_chip_and_handler(irq, &onchip_intc, handle_percpu_irq);
-       else
+               break;
+       default:
                irq_set_chip_and_handler(irq, &onchip_intc, handle_level_irq);
-
+       }
        return 0;
 }
 
@@ -148,78 +147,15 @@ IRQCHIP_DECLARE(arc_intc, "snps,arc700-intc", init_onchip_IRQ);
 
 void arch_local_irq_enable(void)
 {
-
        unsigned long flags = arch_local_save_flags();
 
-       /* Allow both L1 and L2 at the onset */
-       flags |= (STATUS_E1_MASK | STATUS_E2_MASK);
-
-       /* Called from hard ISR (between irq_enter and irq_exit) */
-       if (in_irq()) {
-
-               /* If in L2 ISR, don't re-enable any further IRQs as this can
-                * cause IRQ priorities to get upside down. e.g. it could allow
-                * L1 be taken while in L2 hard ISR which is wrong not only in
-                * theory, it can also cause the dreaded L1-L2-L1 scenario
-                */
-               if (flags & STATUS_A2_MASK)
-                       flags &= ~(STATUS_E1_MASK | STATUS_E2_MASK);
-
-               /* Even if in L1 ISR, allowe Higher prio L2 IRQs */
-               else if (flags & STATUS_A1_MASK)
-                       flags &= ~(STATUS_E1_MASK);
-       }
-
-       /* called from soft IRQ, ideally we want to re-enable all levels */
-
-       else if (in_softirq()) {
-
-               /* However if this is case of L1 interrupted by L2,
-                * re-enabling both may cause whaco L1-L2-L1 scenario
-                * because ARC700 allows level 1 to interrupt an active L2 ISR
-                * Thus we disable both
-                * However some code, executing in soft ISR wants some IRQs
-                * to be enabled so we re-enable L2 only
-                *
-                * How do we determine L1 intr by L2
-                *  -A2 is set (means in L2 ISR)
-                *  -E1 is set in this ISR's pt_regs->status32 which is
-                *      saved copy of status32_l2 when l2 ISR happened
-                */
-               struct pt_regs *pt = get_irq_regs();
-
-               if ((flags & STATUS_A2_MASK) && pt &&
-                   (pt->status32 & STATUS_A1_MASK)) {
-                       /*flags &= ~(STATUS_E1_MASK | STATUS_E2_MASK); */
-                       flags &= ~(STATUS_E1_MASK);
-               }
-       }
+       if (flags & STATUS_A2_MASK)
+               flags |= STATUS_E2_MASK;
+       else if (flags & STATUS_A1_MASK)
+               flags |= STATUS_E1_MASK;
 
        arch_local_irq_restore(flags);
 }
 
-#else /* ! CONFIG_ARC_COMPACT_IRQ_LEVELS */
-
-/*
- * Simpler version for only 1 level of interrupt
- * Here we only Worry about Level 1 Bits
- */
-void arch_local_irq_enable(void)
-{
-       unsigned long flags;
-
-       /*
-        * ARC IDE Drivers tries to re-enable interrupts from hard-isr
-        * context which is simply wrong
-        */
-       if (in_irq()) {
-               WARN_ONCE(1, "IRQ enabled from hard-isr");
-               return;
-       }
-
-       flags = arch_local_save_flags();
-       flags |= (STATUS_E1_MASK | STATUS_E2_MASK);
-       arch_local_irq_restore(flags);
-}
-#endif
 EXPORT_SYMBOL(arch_local_irq_enable);
+#endif
index 2989a7bcf8a863709734d7f5343bb16c789089f2..2ee226546c6a821f739a079326ed92ad52fe16b8 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/interrupt.h>
 #include <linux/irqchip.h>
 #include <asm/mach_desc.h>
+#include <asm/smp.h>
 
 /*
  * Late Interrupt system init called from start_kernel for Boot CPU only
  */
 void __init init_IRQ(void)
 {
-       /* Any external intc can be setup here */
-       if (machine_desc->init_irq)
-               machine_desc->init_irq();
-
-       /* process the entire interrupt tree in one go */
+       /*
+        * process the entire interrupt tree in one go
+        * Any external intc will be setup provided DT chains them
+        * properly
+        */
        irqchip_init();
 
 #ifdef CONFIG_SMP
-       /* Master CPU can initialize it's side of IPI */
-       if (machine_desc->init_smp)
-               machine_desc->init_smp(smp_processor_id());
+       /* a SMP H/w block could do IPI IRQ request here */
+       if (plat_smp_ops.init_irq_cpu)
+               plat_smp_ops.init_irq_cpu(smp_processor_id());
+
+       if (machine_desc->init_cpu_smp)
+               machine_desc->init_cpu_smp(smp_processor_id());
 #endif
 }
 
index 4ffd1855f1bdc586c5f258f376a109ea11646a30..74a9b074ac3e4e64d97ef8e069f73a9104531682 100644 (file)
 #include <linux/irq.h>
 #include <linux/spinlock.h>
 #include <asm/mcip.h>
+#include <asm/setup.h>
 
 static char smp_cpuinfo_buf[128];
 static int idu_detected;
 
 static DEFINE_RAW_SPINLOCK(mcip_lock);
 
-/*
- * Any SMP specific init any CPU does when it comes up.
- * Here we setup the CPU to enable Inter-Processor-Interrupts
- * Called for each CPU
- * -Master      : init_IRQ()
- * -Other(s)    : start_kernel_secondary()
- */
-void mcip_init_smp(unsigned int cpu)
+static void mcip_setup_per_cpu(int cpu)
 {
        smp_ipi_irq_setup(cpu, IPI_IRQ);
 }
@@ -96,34 +90,8 @@ static void mcip_ipi_clear(int irq)
 #endif
 }
 
-volatile int wake_flag;
-
-static void mcip_wakeup_cpu(int cpu, unsigned long pc)
-{
-       BUG_ON(cpu == 0);
-       wake_flag = cpu;
-}
-
-void arc_platform_smp_wait_to_boot(int cpu)
+static void mcip_probe_n_setup(void)
 {
-       while (wake_flag != cpu)
-               ;
-
-       wake_flag = 0;
-       __asm__ __volatile__("j @first_lines_of_secondary       \n");
-}
-
-struct plat_smp_ops plat_smp_ops = {
-       .info           = smp_cpuinfo_buf,
-       .cpu_kick       = mcip_wakeup_cpu,
-       .ipi_send       = mcip_ipi_send,
-       .ipi_clear      = mcip_ipi_clear,
-};
-
-void mcip_init_early_smp(void)
-{
-#define IS_AVAIL1(var, str)    ((var) ? str : "")
-
        struct mcip_bcr {
 #ifdef CONFIG_CPU_BIG_ENDIAN
                unsigned int pad3:8,
@@ -161,6 +129,14 @@ void mcip_init_early_smp(void)
                panic("kernel trying to use non-existent GRTC\n");
 }
 
+struct plat_smp_ops plat_smp_ops = {
+       .info           = smp_cpuinfo_buf,
+       .init_early_smp = mcip_probe_n_setup,
+       .init_irq_cpu   = mcip_setup_per_cpu,
+       .ipi_send       = mcip_ipi_send,
+       .ipi_clear      = mcip_ipi_clear,
+};
+
 /***************************************************************************
  * ARCv2 Interrupt Distribution Unit (IDU)
  *
index cabde9dc0696479cc3a4d3074fd526cf89c85182..c33e77c0ad3e9eb60367b6c2510c4e35c60d3b52 100644 (file)
@@ -160,10 +160,6 @@ static const struct cpuinfo_data arc_cpu_tbl[] = {
        { {0x00, NULL           } }
 };
 
-#define IS_AVAIL1(v, s)                ((v) ? s : "")
-#define IS_USED_RUN(v)         ((v) ? "" : "(not used) ")
-#define IS_USED_CFG(cfg)       IS_USED_RUN(IS_ENABLED(cfg))
-#define IS_AVAIL2(v, s, cfg)   IS_AVAIL1(v, s), IS_AVAIL1(v, IS_USED_CFG(cfg))
 
 static char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len)
 {
@@ -415,8 +411,9 @@ void __init setup_arch(char **cmdline_p)
        if (machine_desc->init_early)
                machine_desc->init_early();
 
-       setup_processor();
        smp_init_cpus();
+
+       setup_processor();
        setup_arch_memory();
 
        /* copy flat DT out of .init and then unflatten it */
index be13d12420bad642c5141a58fdc82d5798204b59..580587805fa302d0d28f7adc89434b920b3be651 100644 (file)
@@ -42,8 +42,13 @@ void __init smp_prepare_boot_cpu(void)
 }
 
 /*
- * Initialise the CPU possible map early - this describes the CPUs
- * which may be present or become present in the system.
+ * Called from setup_arch() before calling setup_processor()
+ *
+ * - Initialise the CPU possible map early - this describes the CPUs
+ *   which may be present or become present in the system.
+ * - Call early smp init hook. This can initialize a specific multi-core
+ *   IP which is say common to several platforms (hence not part of
+ *   platform specific int_early() hook)
  */
 void __init smp_init_cpus(void)
 {
@@ -51,6 +56,9 @@ void __init smp_init_cpus(void)
 
        for (i = 0; i < NR_CPUS; i++)
                set_cpu_possible(i, true);
+
+       if (plat_smp_ops.init_early_smp)
+               plat_smp_ops.init_early_smp();
 }
 
 /* called from init ( ) =>  process 1 */
@@ -72,35 +80,29 @@ void __init smp_cpus_done(unsigned int max_cpus)
 }
 
 /*
- * After power-up, a non Master CPU needs to wait for Master to kick start it
- *
- * The default implementation halts
- *
- * This relies on platform specific support allowing Master to directly set
- * this CPU's PC (to be @first_lines_of_secondary() and kick start it.
- *
- * In lack of such h/w assist, platforms can override this function
- *   - make this function busy-spin on a token, eventually set by Master
- *     (from arc_platform_smp_wakeup_cpu())
- *   - Once token is available, jump to @first_lines_of_secondary
- *     (using inline asm).
- *
- * Alert: can NOT use stack here as it has not been determined/setup for CPU.
- *        If it turns out to be elaborate, it's better to code it in assembly
- *
+ * Default smp boot helper for Run-on-reset case where all cores start off
+ * together. Non-masters need to wait for Master to start running.
+ * This is implemented using a flag in memory, which Non-masters spin-wait on.
+ * Master sets it to cpu-id of core to "ungate" it.
  */
-void __weak arc_platform_smp_wait_to_boot(int cpu)
+static volatile int wake_flag;
+
+static void arc_default_smp_cpu_kick(int cpu, unsigned long pc)
 {
-       /*
-        * As a hack for debugging - since debugger will single-step over the
-        * FLAG insn - wrap the halt itself it in a self loop
-        */
-       __asm__ __volatile__(
-       "1:             \n"
-       "       flag 1  \n"
-       "       b 1b    \n");
+       BUG_ON(cpu == 0);
+       wake_flag = cpu;
+}
+
+void arc_platform_smp_wait_to_boot(int cpu)
+{
+       while (wake_flag != cpu)
+               ;
+
+       wake_flag = 0;
+       __asm__ __volatile__("j @first_lines_of_secondary       \n");
 }
 
+
 const char *arc_platform_smp_cpuinfo(void)
 {
        return plat_smp_ops.info ? : "";
@@ -129,8 +131,12 @@ void start_kernel_secondary(void)
 
        pr_info("## CPU%u LIVE ##: Executing Code...\n", cpu);
 
-       if (machine_desc->init_smp)
-               machine_desc->init_smp(cpu);
+       /* Some SMP H/w setup - for each cpu */
+       if (plat_smp_ops.init_irq_cpu)
+               plat_smp_ops.init_irq_cpu(cpu);
+
+       if (machine_desc->init_cpu_smp)
+               machine_desc->init_cpu_smp(cpu);
 
        arc_local_timer_setup();
 
@@ -161,6 +167,8 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
        if (plat_smp_ops.cpu_kick)
                plat_smp_ops.cpu_kick(cpu,
                                (unsigned long)first_lines_of_secondary);
+       else
+               arc_default_smp_cpu_kick(cpu, (unsigned long)NULL);
 
        /* wait for 1 sec after kicking the secondary */
        wait_till = jiffies + HZ;
index 4294761a2b3e7ad3b36f5eca5bc26490e31ed61f..dfad287f1db1c6b55b86faacc0b40d2472636795 100644 (file)
@@ -285,7 +285,4 @@ void __init time_init(void)
 
        /* sets up the periodic event timer */
        arc_local_timer_setup();
-
-       if (machine_desc->init_time)
-               machine_desc->init_time();
 }
index dd35bde39f6938e483b2cfd2a2e39a1cf2148c71..894e696bddaa3ca3fab6bdca6ed4f7ac451bb290 100644 (file)
@@ -12,7 +12,7 @@
 #include <asm/thread_info.h>
 
 OUTPUT_ARCH(arc)
-ENTRY(_stext)
+ENTRY(res_service)
 
 #ifdef CONFIG_CPU_BIG_ENDIAN
 jiffies = jiffies_64 + 4;
index 7beb941556c3f73567b8174b6dc1cd15c2ef2d49..3703a4969349186bbf726965cd528db80e2660b5 100644 (file)
@@ -8,3 +8,4 @@
 
 obj-y  := extable.o ioremap.o dma.o fault.o init.o
 obj-y  += tlb.o tlbex.o cache.o mmap.o
+obj-$(CONFIG_HIGHMEM)  += highmem.o
index 0d1a6e96839fbfc6636f324f6d05fb483d97dcea..ff7ff6cbb8112408c05a38a2f8e001265d5d3726 100644 (file)
@@ -25,7 +25,7 @@ static int l2_line_sz;
 int ioc_exists;
 volatile int slc_enable = 1, ioc_enable = 1;
 
-void (*_cache_line_loop_ic_fn)(unsigned long paddr, unsigned long vaddr,
+void (*_cache_line_loop_ic_fn)(phys_addr_t paddr, unsigned long vaddr,
                               unsigned long sz, const int cacheop);
 
 void (*__dma_cache_wback_inv)(unsigned long start, unsigned long sz);
@@ -37,7 +37,6 @@ char *arc_cache_mumbojumbo(int c, char *buf, int len)
        int n = 0;
        struct cpuinfo_arc_cache *p;
 
-#define IS_USED_RUN(v)         ((v) ? "" : "(disabled) ")
 #define PR_CACHE(p, cfg, str)                                          \
        if (!(p)->ver)                                                  \
                n += scnprintf(buf + n, len - n, str"\t\t: N/A\n");     \
@@ -47,7 +46,7 @@ char *arc_cache_mumbojumbo(int c, char *buf, int len)
                        (p)->sz_k, (p)->assoc, (p)->line_len,           \
                        (p)->vipt ? "VIPT" : "PIPT",                    \
                        (p)->alias ? " aliasing" : "",                  \
-                       IS_ENABLED(cfg) ? "" : " (not used)");
+                       IS_USED_CFG(cfg));
 
        PR_CACHE(&cpuinfo_arc700[c].icache, CONFIG_ARC_HAS_ICACHE, "I-Cache");
        PR_CACHE(&cpuinfo_arc700[c].dcache, CONFIG_ARC_HAS_DCACHE, "D-Cache");
@@ -63,7 +62,7 @@ char *arc_cache_mumbojumbo(int c, char *buf, int len)
 
        if (ioc_exists)
                n += scnprintf(buf + n, len - n, "IOC\t\t:%s\n",
-                               IS_USED_RUN(ioc_enable));
+                               IS_DISABLED_RUN(ioc_enable));
 
        return buf;
 }
@@ -217,7 +216,7 @@ slc_chk:
  */
 
 static inline
-void __cache_line_loop_v2(unsigned long paddr, unsigned long vaddr,
+void __cache_line_loop_v2(phys_addr_t paddr, unsigned long vaddr,
                          unsigned long sz, const int op)
 {
        unsigned int aux_cmd;
@@ -254,8 +253,12 @@ void __cache_line_loop_v2(unsigned long paddr, unsigned long vaddr,
        }
 }
 
+/*
+ * For ARC700 MMUv3 I-cache and D-cache flushes
+ * Also reused for HS38 aliasing I-cache configuration
+ */
 static inline
-void __cache_line_loop_v3(unsigned long paddr, unsigned long vaddr,
+void __cache_line_loop_v3(phys_addr_t paddr, unsigned long vaddr,
                          unsigned long sz, const int op)
 {
        unsigned int aux_cmd, aux_tag;
@@ -290,6 +293,16 @@ void __cache_line_loop_v3(unsigned long paddr, unsigned long vaddr,
        if (full_page)
                write_aux_reg(aux_tag, paddr);
 
+       /*
+        * This is technically for MMU v4, using the MMU v3 programming model
+        * Special work for HS38 aliasing I-cache configuratino with PAE40
+        *   - upper 8 bits of paddr need to be written into PTAG_HI
+        *   - (and needs to be written before the lower 32 bits)
+        * Note that PTAG_HI is hoisted outside the line loop
+        */
+       if (is_pae40_enabled() && op == OP_INV_IC)
+               write_aux_reg(ARC_REG_IC_PTAG_HI, (u64)paddr >> 32);
+
        while (num_lines-- > 0) {
                if (!full_page) {
                        write_aux_reg(aux_tag, paddr);
@@ -302,14 +315,20 @@ void __cache_line_loop_v3(unsigned long paddr, unsigned long vaddr,
 }
 
 /*
- * In HS38x (MMU v4), although icache is VIPT, only paddr is needed for cache
- * maintenance ops (in IVIL reg), as long as icache doesn't alias.
+ * In HS38x (MMU v4), I-cache is VIPT (can alias), D-cache is PIPT
+ * Here's how cache ops are implemented
+ *
+ *  - D-cache: only paddr needed (in DC_IVDL/DC_FLDL)
+ *  - I-cache Non Aliasing: Despite VIPT, only paddr needed (in IC_IVIL)
+ *  - I-cache Aliasing: Both vaddr and paddr needed (in IC_IVIL, IC_PTAG
+ *    respectively, similar to MMU v3 programming model, hence
+ *    __cache_line_loop_v3() is used)
  *
- * For Aliasing icache, vaddr is also needed (in IVIL), while paddr is
- * specified in PTAG (similar to MMU v3)
+ * If PAE40 is enabled, independent of aliasing considerations, the higher bits
+ * needs to be written into PTAG_HI
  */
 static inline
-void __cache_line_loop_v4(unsigned long paddr, unsigned long vaddr,
+void __cache_line_loop_v4(phys_addr_t paddr, unsigned long vaddr,
                          unsigned long sz, const int cacheop)
 {
        unsigned int aux_cmd;
@@ -336,6 +355,22 @@ void __cache_line_loop_v4(unsigned long paddr, unsigned long vaddr,
 
        num_lines = DIV_ROUND_UP(sz, L1_CACHE_BYTES);
 
+       /*
+        * For HS38 PAE40 configuration
+        *   - upper 8 bits of paddr need to be written into PTAG_HI
+        *   - (and needs to be written before the lower 32 bits)
+        */
+       if (is_pae40_enabled()) {
+               if (cacheop == OP_INV_IC)
+                       /*
+                        * Non aliasing I-cache in HS38,
+                        * aliasing I-cache handled in __cache_line_loop_v3()
+                        */
+                       write_aux_reg(ARC_REG_IC_PTAG_HI, (u64)paddr >> 32);
+               else
+                       write_aux_reg(ARC_REG_DC_PTAG_HI, (u64)paddr >> 32);
+       }
+
        while (num_lines-- > 0) {
                write_aux_reg(aux_cmd, paddr);
                paddr += L1_CACHE_BYTES;
@@ -413,7 +448,7 @@ static inline void __dc_entire_op(const int op)
 /*
  * D-Cache Line ops: Per Line INV (discard or wback+discard) or FLUSH (wback)
  */
-static inline void __dc_line_op(unsigned long paddr, unsigned long vaddr,
+static inline void __dc_line_op(phys_addr_t paddr, unsigned long vaddr,
                                unsigned long sz, const int op)
 {
        unsigned long flags;
@@ -446,7 +481,7 @@ static inline void __ic_entire_inv(void)
 }
 
 static inline void
-__ic_line_inv_vaddr_local(unsigned long paddr, unsigned long vaddr,
+__ic_line_inv_vaddr_local(phys_addr_t paddr, unsigned long vaddr,
                          unsigned long sz)
 {
        unsigned long flags;
@@ -463,7 +498,7 @@ __ic_line_inv_vaddr_local(unsigned long paddr, unsigned long vaddr,
 #else
 
 struct ic_inv_args {
-       unsigned long paddr, vaddr;
+       phys_addr_t paddr, vaddr;
        int sz;
 };
 
@@ -474,7 +509,7 @@ static void __ic_line_inv_vaddr_helper(void *info)
         __ic_line_inv_vaddr_local(ic_inv->paddr, ic_inv->vaddr, ic_inv->sz);
 }
 
-static void __ic_line_inv_vaddr(unsigned long paddr, unsigned long vaddr,
+static void __ic_line_inv_vaddr(phys_addr_t paddr, unsigned long vaddr,
                                unsigned long sz)
 {
        struct ic_inv_args ic_inv = {
@@ -495,7 +530,7 @@ static void __ic_line_inv_vaddr(unsigned long paddr, unsigned long vaddr,
 
 #endif /* CONFIG_ARC_HAS_ICACHE */
 
-noinline void slc_op(unsigned long paddr, unsigned long sz, const int op)
+noinline void slc_op(phys_addr_t paddr, unsigned long sz, const int op)
 {
 #ifdef CONFIG_ISA_ARCV2
        /*
@@ -585,7 +620,7 @@ void flush_dcache_page(struct page *page)
        } else if (page_mapped(page)) {
 
                /* kernel reading from page with U-mapping */
-               unsigned long paddr = (unsigned long)page_address(page);
+               phys_addr_t paddr = (unsigned long)page_address(page);
                unsigned long vaddr = page->index << PAGE_CACHE_SHIFT;
 
                if (addr_not_cache_congruent(paddr, vaddr))
@@ -733,14 +768,14 @@ EXPORT_SYMBOL(flush_icache_range);
  *    builtin kernel page will not have any virtual mappings.
  *    kprobe on loadable module will be kernel vaddr.
  */
-void __sync_icache_dcache(unsigned long paddr, unsigned long vaddr, int len)
+void __sync_icache_dcache(phys_addr_t paddr, unsigned long vaddr, int len)
 {
        __dc_line_op(paddr, vaddr, len, OP_FLUSH_N_INV);
        __ic_line_inv_vaddr(paddr, vaddr, len);
 }
 
 /* wrapper to compile time eliminate alignment checks in flush loop */
-void __inv_icache_page(unsigned long paddr, unsigned long vaddr)
+void __inv_icache_page(phys_addr_t paddr, unsigned long vaddr)
 {
        __ic_line_inv_vaddr(paddr, vaddr, PAGE_SIZE);
 }
@@ -749,7 +784,7 @@ void __inv_icache_page(unsigned long paddr, unsigned long vaddr)
  * wrapper to clearout kernel or userspace mappings of a page
  * For kernel mappings @vaddr == @paddr
  */
-void __flush_dcache_page(unsigned long paddr, unsigned long vaddr)
+void __flush_dcache_page(phys_addr_t paddr, unsigned long vaddr)
 {
        __dc_line_op(paddr, vaddr & PAGE_MASK, PAGE_SIZE, OP_FLUSH_N_INV);
 }
@@ -807,8 +842,8 @@ void flush_anon_page(struct vm_area_struct *vma, struct page *page,
 void copy_user_highpage(struct page *to, struct page *from,
        unsigned long u_vaddr, struct vm_area_struct *vma)
 {
-       unsigned long kfrom = (unsigned long)page_address(from);
-       unsigned long kto = (unsigned long)page_address(to);
+       void *kfrom = kmap_atomic(from);
+       void *kto = kmap_atomic(to);
        int clean_src_k_mappings = 0;
 
        /*
@@ -818,13 +853,16 @@ void copy_user_highpage(struct page *to, struct page *from,
         *
         * Note that while @u_vaddr refers to DST page's userspace vaddr, it is
         * equally valid for SRC page as well
+        *
+        * For !VIPT cache, all of this gets compiled out as
+        * addr_not_cache_congruent() is 0
         */
        if (page_mapped(from) && addr_not_cache_congruent(kfrom, u_vaddr)) {
-               __flush_dcache_page(kfrom, u_vaddr);
+               __flush_dcache_page((unsigned long)kfrom, u_vaddr);
                clean_src_k_mappings = 1;
        }
 
-       copy_page((void *)kto, (void *)kfrom);
+       copy_page(kto, kfrom);
 
        /*
         * Mark DST page K-mapping as dirty for a later finalization by
@@ -841,11 +879,14 @@ void copy_user_highpage(struct page *to, struct page *from,
         * sync the kernel mapping back to physical page
         */
        if (clean_src_k_mappings) {
-               __flush_dcache_page(kfrom, kfrom);
+               __flush_dcache_page((unsigned long)kfrom, (unsigned long)kfrom);
                set_bit(PG_dc_clean, &from->flags);
        } else {
                clear_bit(PG_dc_clean, &from->flags);
        }
+
+       kunmap_atomic(kto);
+       kunmap_atomic(kfrom);
 }
 
 void clear_user_page(void *to, unsigned long u_vaddr, struct page *page)
index d948e4e9d89c4ebe7e5676f449c9377b4fbe3535..af63f4a13e605eda26bcb7de54595bda5ecad997 100644 (file)
 #include <asm/pgalloc.h>
 #include <asm/mmu.h>
 
-static int handle_vmalloc_fault(unsigned long address)
+/*
+ * kernel virtual address is required to implement vmalloc/pkmap/fixmap
+ * Refer to asm/processor.h for System Memory Map
+ *
+ * It simply copies the PMD entry (pointer to 2nd level page table or hugepage)
+ * from swapper pgdir to task pgdir. The 2nd level table/page is thus shared
+ */
+noinline static int handle_kernel_vaddr_fault(unsigned long address)
 {
        /*
         * Synchronize this task's top level page-table
@@ -72,8 +79,8 @@ void do_page_fault(unsigned long address, struct pt_regs *regs)
         * only copy the information from the master page table,
         * nothing more.
         */
-       if (address >= VMALLOC_START && address <= VMALLOC_END) {
-               ret = handle_vmalloc_fault(address);
+       if (address >= VMALLOC_START) {
+               ret = handle_kernel_vaddr_fault(address);
                if (unlikely(ret))
                        goto bad_area_nosemaphore;
                else
diff --git a/arch/arc/mm/highmem.c b/arch/arc/mm/highmem.c
new file mode 100644 (file)
index 0000000..065ee6b
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2015 Synopsys, Inc. (www.synopsys.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/bootmem.h>
+#include <linux/export.h>
+#include <linux/highmem.h>
+#include <asm/processor.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+
+/*
+ * HIGHMEM API:
+ *
+ * kmap() API provides sleep semantics hence refered to as "permanent maps"
+ * It allows mapping LAST_PKMAP pages, using @last_pkmap_nr as the cursor
+ * for book-keeping
+ *
+ * kmap_atomic() can't sleep (calls pagefault_disable()), thus it provides
+ * shortlived ala "temporary mappings" which historically were implemented as
+ * fixmaps (compile time addr etc). Their book-keeping is done per cpu.
+ *
+ *     Both these facts combined (preemption disabled and per-cpu allocation)
+ *     means the total number of concurrent fixmaps will be limited to max
+ *     such allocations in a single control path. Thus KM_TYPE_NR (another
+ *     historic relic) is a small'ish number which caps max percpu fixmaps
+ *
+ * ARC HIGHMEM Details
+ *
+ * - the kernel vaddr space from 0x7z to 0x8z (currently used by vmalloc/module)
+ *   is now shared between vmalloc and kmap (non overlapping though)
+ *
+ * - Both fixmap/pkmap use a dedicated page table each, hooked up to swapper PGD
+ *   This means each only has 1 PGDIR_SIZE worth of kvaddr mappings, which means
+ *   2M of kvaddr space for typical config (8K page and 11:8:13 traversal split)
+ *
+ * - fixmap anyhow needs a limited number of mappings. So 2M kvaddr == 256 PTE
+ *   slots across NR_CPUS would be more than sufficient (generic code defines
+ *   KM_TYPE_NR as 20).
+ *
+ * - pkmap being preemptible, in theory could do with more than 256 concurrent
+ *   mappings. However, generic pkmap code: map_new_virtual(), doesn't traverse
+ *   the PGD and only works with a single page table @pkmap_page_table, hence
+ *   sets the limit
+ */
+
+extern pte_t * pkmap_page_table;
+static pte_t * fixmap_page_table;
+
+void *kmap(struct page *page)
+{
+       BUG_ON(in_interrupt());
+       if (!PageHighMem(page))
+               return page_address(page);
+
+       return kmap_high(page);
+}
+
+void *kmap_atomic(struct page *page)
+{
+       int idx, cpu_idx;
+       unsigned long vaddr;
+
+       preempt_disable();
+       pagefault_disable();
+       if (!PageHighMem(page))
+               return page_address(page);
+
+       cpu_idx = kmap_atomic_idx_push();
+       idx = cpu_idx + KM_TYPE_NR * smp_processor_id();
+       vaddr = FIXMAP_ADDR(idx);
+
+       set_pte_at(&init_mm, vaddr, fixmap_page_table + idx,
+                  mk_pte(page, kmap_prot));
+
+       return (void *)vaddr;
+}
+EXPORT_SYMBOL(kmap_atomic);
+
+void __kunmap_atomic(void *kv)
+{
+       unsigned long kvaddr = (unsigned long)kv;
+
+       if (kvaddr >= FIXMAP_BASE && kvaddr < (FIXMAP_BASE + FIXMAP_SIZE)) {
+
+               /*
+                * Because preemption is disabled, this vaddr can be associated
+                * with the current allocated index.
+                * But in case of multiple live kmap_atomic(), it still relies on
+                * callers to unmap in right order.
+                */
+               int cpu_idx = kmap_atomic_idx();
+               int idx = cpu_idx + KM_TYPE_NR * smp_processor_id();
+
+               WARN_ON(kvaddr != FIXMAP_ADDR(idx));
+
+               pte_clear(&init_mm, kvaddr, fixmap_page_table + idx);
+               local_flush_tlb_kernel_range(kvaddr, kvaddr + PAGE_SIZE);
+
+               kmap_atomic_idx_pop();
+       }
+
+       pagefault_enable();
+       preempt_enable();
+}
+EXPORT_SYMBOL(__kunmap_atomic);
+
+noinline pte_t *alloc_kmap_pgtable(unsigned long kvaddr)
+{
+       pgd_t *pgd_k;
+       pud_t *pud_k;
+       pmd_t *pmd_k;
+       pte_t *pte_k;
+
+       pgd_k = pgd_offset_k(kvaddr);
+       pud_k = pud_offset(pgd_k, kvaddr);
+       pmd_k = pmd_offset(pud_k, kvaddr);
+
+       pte_k = (pte_t *)alloc_bootmem_low_pages(PAGE_SIZE);
+       pmd_populate_kernel(&init_mm, pmd_k, pte_k);
+       return pte_k;
+}
+
+void kmap_init(void)
+{
+       /* Due to recursive include hell, we can't do this in processor.h */
+       BUILD_BUG_ON(PAGE_OFFSET < (VMALLOC_END + FIXMAP_SIZE + PKMAP_SIZE));
+
+       BUILD_BUG_ON(KM_TYPE_NR > PTRS_PER_PTE);
+       pkmap_page_table = alloc_kmap_pgtable(PKMAP_BASE);
+
+       BUILD_BUG_ON(LAST_PKMAP > PTRS_PER_PTE);
+       fixmap_page_table = alloc_kmap_pgtable(FIXMAP_BASE);
+}
index d44eedd8c3220e6923b26ea8d10fc6f0f84005f3..a9305b5a2cd4ba091f7ae18914f6ef5e64d5236c 100644 (file)
@@ -15,6 +15,7 @@
 #endif
 #include <linux/swap.h>
 #include <linux/module.h>
+#include <linux/highmem.h>
 #include <asm/page.h>
 #include <asm/pgalloc.h>
 #include <asm/sections.h>
@@ -24,16 +25,22 @@ pgd_t swapper_pg_dir[PTRS_PER_PGD] __aligned(PAGE_SIZE);
 char empty_zero_page[PAGE_SIZE] __aligned(PAGE_SIZE);
 EXPORT_SYMBOL(empty_zero_page);
 
-/* Default tot mem from .config */
-static unsigned long arc_mem_sz = 0x20000000;  /* some default */
+static const unsigned long low_mem_start = CONFIG_LINUX_LINK_BASE;
+static unsigned long low_mem_sz;
+
+#ifdef CONFIG_HIGHMEM
+static unsigned long min_high_pfn;
+static u64 high_mem_start;
+static u64 high_mem_sz;
+#endif
 
 /* User can over-ride above with "mem=nnn[KkMm]" in cmdline */
 static int __init setup_mem_sz(char *str)
 {
-       arc_mem_sz = memparse(str, NULL) & PAGE_MASK;
+       low_mem_sz = memparse(str, NULL) & PAGE_MASK;
 
        /* early console might not be setup yet - it will show up later */
-       pr_info("\"mem=%s\": mem sz set to %ldM\n", str, TO_MB(arc_mem_sz));
+       pr_info("\"mem=%s\": mem sz set to %ldM\n", str, TO_MB(low_mem_sz));
 
        return 0;
 }
@@ -41,8 +48,22 @@ early_param("mem", setup_mem_sz);
 
 void __init early_init_dt_add_memory_arch(u64 base, u64 size)
 {
-       arc_mem_sz = size & PAGE_MASK;
-       pr_info("Memory size set via devicetree %ldM\n", TO_MB(arc_mem_sz));
+       int in_use = 0;
+
+       if (!low_mem_sz) {
+               BUG_ON(base != low_mem_start);
+               low_mem_sz = size;
+               in_use = 1;
+       } else {
+#ifdef CONFIG_HIGHMEM
+               high_mem_start = base;
+               high_mem_sz = size;
+               in_use = 1;
+#endif
+       }
+
+       pr_info("Memory @ %llx [%lldM] %s\n",
+               base, TO_MB(size), !in_use ? "Not used":"");
 }
 
 #ifdef CONFIG_BLK_DEV_INITRD
@@ -72,46 +93,62 @@ early_param("initrd", early_initrd);
 void __init setup_arch_memory(void)
 {
        unsigned long zones_size[MAX_NR_ZONES];
-       unsigned long end_mem = CONFIG_LINUX_LINK_BASE + arc_mem_sz;
+       unsigned long zones_holes[MAX_NR_ZONES];
 
        init_mm.start_code = (unsigned long)_text;
        init_mm.end_code = (unsigned long)_etext;
        init_mm.end_data = (unsigned long)_edata;
        init_mm.brk = (unsigned long)_end;
 
-       /*
-        * We do it here, so that memory is correctly instantiated
-        * even if "mem=xxx" cmline over-ride is given and/or
-        * DT has memory node. Each causes an update to @arc_mem_sz
-        * and we finally add memory one here
-        */
-       memblock_add(CONFIG_LINUX_LINK_BASE, arc_mem_sz);
-
-       /*------------- externs in mm need setting up ---------------*/
-
        /* first page of system - kernel .vector starts here */
        min_low_pfn = ARCH_PFN_OFFSET;
 
-       /* Last usable page of low mem (no HIGHMEM yet for ARC port) */
-       max_low_pfn = max_pfn = PFN_DOWN(end_mem);
+       /* Last usable page of low mem */
+       max_low_pfn = max_pfn = PFN_DOWN(low_mem_start + low_mem_sz);
 
-       max_mapnr = max_low_pfn - min_low_pfn;
+#ifdef CONFIG_HIGHMEM
+       min_high_pfn = PFN_DOWN(high_mem_start);
+       max_pfn = PFN_DOWN(high_mem_start + high_mem_sz);
+#endif
+
+       max_mapnr = max_pfn - min_low_pfn;
 
-       /*------------- reserve kernel image -----------------------*/
-       memblock_reserve(CONFIG_LINUX_LINK_BASE,
-                        __pa(_end) - CONFIG_LINUX_LINK_BASE);
+       /*------------- bootmem allocator setup -----------------------*/
+
+       /*
+        * seed the bootmem allocator after any DT memory node parsing or
+        * "mem=xxx" cmdline overrides have potentially updated @arc_mem_sz
+        *
+        * Only low mem is added, otherwise we have crashes when allocating
+        * mem_map[] itself. NO_BOOTMEM allocates mem_map[] at the end of
+        * avail memory, ending in highmem with a > 32-bit address. However
+        * it then tries to memset it with a truncaed 32-bit handle, causing
+        * the crash
+        */
+
+       memblock_add(low_mem_start, low_mem_sz);
+       memblock_reserve(low_mem_start, __pa(_end) - low_mem_start);
 
 #ifdef CONFIG_BLK_DEV_INITRD
-       /*------------- reserve initrd image -----------------------*/
        if (initrd_start)
                memblock_reserve(__pa(initrd_start), initrd_end - initrd_start);
 #endif
 
        memblock_dump_all();
 
-       /*-------------- node setup --------------------------------*/
+       /*----------------- node/zones setup --------------------------*/
        memset(zones_size, 0, sizeof(zones_size));
-       zones_size[ZONE_NORMAL] = max_mapnr;
+       memset(zones_holes, 0, sizeof(zones_holes));
+
+       zones_size[ZONE_NORMAL] = max_low_pfn - min_low_pfn;
+       zones_holes[ZONE_NORMAL] = 0;
+
+#ifdef CONFIG_HIGHMEM
+       zones_size[ZONE_HIGHMEM] = max_pfn - max_low_pfn;
+
+       /* This handles the peripheral address space hole */
+       zones_holes[ZONE_HIGHMEM] = min_high_pfn - max_low_pfn;
+#endif
 
        /*
         * We can't use the helper free_area_init(zones[]) because it uses
@@ -122,9 +159,12 @@ void __init setup_arch_memory(void)
        free_area_init_node(0,                  /* node-id */
                            zones_size,         /* num pages per zone */
                            min_low_pfn,        /* first pfn of node */
-                           NULL);              /* NO holes */
+                           zones_holes);       /* holes */
 
-       high_memory = (void *)end_mem;
+#ifdef CONFIG_HIGHMEM
+       high_memory = (void *)(min_high_pfn << PAGE_SHIFT);
+       kmap_init();
+#endif
 }
 
 /*
@@ -135,6 +175,14 @@ void __init setup_arch_memory(void)
  */
 void __init mem_init(void)
 {
+#ifdef CONFIG_HIGHMEM
+       unsigned long tmp;
+
+       reset_all_zones_managed_pages();
+       for (tmp = min_high_pfn; tmp < max_pfn; tmp++)
+               free_highmem_page(pfn_to_page(tmp));
+#endif
+
        free_all_bootmem();
        mem_init_print_info(NULL);
 }
index 2c7ce8bb74758c127673582426f214a0ee0d0af7..0ee7398468476f57b301bde2fa7c7e13735bb3fb 100644 (file)
@@ -109,6 +109,10 @@ DEFINE_PER_CPU(unsigned int, asid_cache) = MM_CTXT_FIRST_CYCLE;
 static inline void __tlb_entry_erase(void)
 {
        write_aux_reg(ARC_REG_TLBPD1, 0);
+
+       if (is_pae40_enabled())
+               write_aux_reg(ARC_REG_TLBPD1HI, 0);
+
        write_aux_reg(ARC_REG_TLBPD0, 0);
        write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite);
 }
@@ -182,7 +186,7 @@ static void utlb_invalidate(void)
 
 }
 
-static void tlb_entry_insert(unsigned int pd0, unsigned int pd1)
+static void tlb_entry_insert(unsigned int pd0, pte_t pd1)
 {
        unsigned int idx;
 
@@ -225,10 +229,14 @@ static void tlb_entry_erase(unsigned int vaddr_n_asid)
        write_aux_reg(ARC_REG_TLBCOMMAND, TLBDeleteEntry);
 }
 
-static void tlb_entry_insert(unsigned int pd0, unsigned int pd1)
+static void tlb_entry_insert(unsigned int pd0, pte_t pd1)
 {
        write_aux_reg(ARC_REG_TLBPD0, pd0);
        write_aux_reg(ARC_REG_TLBPD1, pd1);
+
+       if (is_pae40_enabled())
+               write_aux_reg(ARC_REG_TLBPD1HI, (u64)pd1 >> 32);
+
        write_aux_reg(ARC_REG_TLBCOMMAND, TLBInsertEntry);
 }
 
@@ -240,22 +248,39 @@ static void tlb_entry_insert(unsigned int pd0, unsigned int pd1)
 
 noinline void local_flush_tlb_all(void)
 {
+       struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu;
        unsigned long flags;
        unsigned int entry;
-       struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu;
+       int num_tlb = mmu->sets * mmu->ways;
 
        local_irq_save(flags);
 
        /* Load PD0 and PD1 with template for a Blank Entry */
        write_aux_reg(ARC_REG_TLBPD1, 0);
+
+       if (is_pae40_enabled())
+               write_aux_reg(ARC_REG_TLBPD1HI, 0);
+
        write_aux_reg(ARC_REG_TLBPD0, 0);
 
-       for (entry = 0; entry < mmu->num_tlb; entry++) {
+       for (entry = 0; entry < num_tlb; entry++) {
                /* write this entry to the TLB */
                write_aux_reg(ARC_REG_TLBINDEX, entry);
                write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite);
        }
 
+       if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
+               const int stlb_idx = 0x800;
+
+               /* Blank sTLB entry */
+               write_aux_reg(ARC_REG_TLBPD0, _PAGE_HW_SZ);
+
+               for (entry = stlb_idx; entry < stlb_idx + 16; entry++) {
+                       write_aux_reg(ARC_REG_TLBINDEX, entry);
+                       write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite);
+               }
+       }
+
        utlb_invalidate();
 
        local_irq_restore(flags);
@@ -409,6 +434,15 @@ static inline void ipi_flush_tlb_range(void *arg)
        local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
 }
 
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+static inline void ipi_flush_pmd_tlb_range(void *arg)
+{
+       struct tlb_args *ta = arg;
+
+       local_flush_pmd_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
+}
+#endif
+
 static inline void ipi_flush_tlb_kernel_range(void *arg)
 {
        struct tlb_args *ta = (struct tlb_args *)arg;
@@ -449,6 +483,20 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
        on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_range, &ta, 1);
 }
 
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+void flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
+                        unsigned long end)
+{
+       struct tlb_args ta = {
+               .ta_vma = vma,
+               .ta_start = start,
+               .ta_end = end
+       };
+
+       on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_pmd_tlb_range, &ta, 1);
+}
+#endif
+
 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
 {
        struct tlb_args ta = {
@@ -463,11 +511,12 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end)
 /*
  * Routine to create a TLB entry
  */
-void create_tlb(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
+void create_tlb(struct vm_area_struct *vma, unsigned long vaddr, pte_t *ptep)
 {
        unsigned long flags;
        unsigned int asid_or_sasid, rwx;
-       unsigned long pd0, pd1;
+       unsigned long pd0;
+       pte_t pd1;
 
        /*
         * create_tlb() assumes that current->mm == vma->mm, since
@@ -499,9 +548,9 @@ void create_tlb(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
 
        local_irq_save(flags);
 
-       tlb_paranoid_check(asid_mm(vma->vm_mm, smp_processor_id()), address);
+       tlb_paranoid_check(asid_mm(vma->vm_mm, smp_processor_id()), vaddr);
 
-       address &= PAGE_MASK;
+       vaddr &= PAGE_MASK;
 
        /* update this PTE credentials */
        pte_val(*ptep) |= (_PAGE_PRESENT | _PAGE_ACCESSED);
@@ -511,7 +560,7 @@ void create_tlb(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
        /* ASID for this task */
        asid_or_sasid = read_aux_reg(ARC_REG_PID) & 0xff;
 
-       pd0 = address | asid_or_sasid | (pte_val(*ptep) & PTE_BITS_IN_PD0);
+       pd0 = vaddr | asid_or_sasid | (pte_val(*ptep) & PTE_BITS_IN_PD0);
 
        /*
         * ARC MMU provides fully orthogonal access bits for K/U mode,
@@ -547,7 +596,7 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddr_unaligned,
                      pte_t *ptep)
 {
        unsigned long vaddr = vaddr_unaligned & PAGE_MASK;
-       unsigned long paddr = pte_val(*ptep) & PAGE_MASK;
+       phys_addr_t paddr = pte_val(*ptep) & PAGE_MASK;
        struct page *page = pfn_to_page(pte_pfn(*ptep));
 
        create_tlb(vma, vaddr, ptep);
@@ -580,6 +629,95 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddr_unaligned,
        }
 }
 
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+
+/*
+ * MMUv4 in HS38x cores supports Super Pages which are basis for Linux THP
+ * support.
+ *
+ * Normal and Super pages can co-exist (ofcourse not overlap) in TLB with a
+ * new bit "SZ" in TLB page desciptor to distinguish between them.
+ * Super Page size is configurable in hardware (4K to 16M), but fixed once
+ * RTL builds.
+ *
+ * The exact THP size a Linx configuration will support is a function of:
+ *  - MMU page size (typical 8K, RTL fixed)
+ *  - software page walker address split between PGD:PTE:PFN (typical
+ *    11:8:13, but can be changed with 1 line)
+ * So for above default, THP size supported is 8K * (2^8) = 2M
+ *
+ * Default Page Walker is 2 levels, PGD:PTE:PFN, which in THP regime
+ * reduces to 1 level (as PTE is folded into PGD and canonically referred
+ * to as PMD).
+ * Thus THP PMD accessors are implemented in terms of PTE (just like sparc)
+ */
+
+void update_mmu_cache_pmd(struct vm_area_struct *vma, unsigned long addr,
+                                pmd_t *pmd)
+{
+       pte_t pte = __pte(pmd_val(*pmd));
+       update_mmu_cache(vma, addr, &pte);
+}
+
+void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
+                               pgtable_t pgtable)
+{
+       struct list_head *lh = (struct list_head *) pgtable;
+
+       assert_spin_locked(&mm->page_table_lock);
+
+       /* FIFO */
+       if (!pmd_huge_pte(mm, pmdp))
+               INIT_LIST_HEAD(lh);
+       else
+               list_add(lh, (struct list_head *) pmd_huge_pte(mm, pmdp));
+       pmd_huge_pte(mm, pmdp) = pgtable;
+}
+
+pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
+{
+       struct list_head *lh;
+       pgtable_t pgtable;
+
+       assert_spin_locked(&mm->page_table_lock);
+
+       pgtable = pmd_huge_pte(mm, pmdp);
+       lh = (struct list_head *) pgtable;
+       if (list_empty(lh))
+               pmd_huge_pte(mm, pmdp) = NULL;
+       else {
+               pmd_huge_pte(mm, pmdp) = (pgtable_t) lh->next;
+               list_del(lh);
+       }
+
+       pte_val(pgtable[0]) = 0;
+       pte_val(pgtable[1]) = 0;
+
+       return pgtable;
+}
+
+void local_flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
+                              unsigned long end)
+{
+       unsigned int cpu;
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+       cpu = smp_processor_id();
+
+       if (likely(asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID)) {
+               unsigned int asid = hw_pid(vma->vm_mm, cpu);
+
+               /* No need to loop here: this will always be for 1 Huge Page */
+               tlb_entry_erase(start | _PAGE_HW_SZ | asid);
+       }
+
+       local_irq_restore(flags);
+}
+
+#endif
+
 /* Read the Cache Build Confuration Registers, Decode them and save into
  * the cpuinfo structure for later use.
  * No Validation is done here, simply read/convert the BCRs
@@ -598,10 +736,10 @@ void read_decode_mmu_bcr(void)
 
        struct bcr_mmu_3 {
 #ifdef CONFIG_CPU_BIG_ENDIAN
-       unsigned int ver:8, ways:4, sets:4, osm:1, reserv:3, pg_sz:4,
+       unsigned int ver:8, ways:4, sets:4, res:3, sasid:1, pg_sz:4,
                     u_itlb:4, u_dtlb:4;
 #else
-       unsigned int u_dtlb:4, u_itlb:4, pg_sz:4, reserv:3, osm:1, sets:4,
+       unsigned int u_dtlb:4, u_itlb:4, pg_sz:4, sasid:1, res:3, sets:4,
                     ways:4, ver:8;
 #endif
        } *mmu3;
@@ -622,7 +760,7 @@ void read_decode_mmu_bcr(void)
 
        if (mmu->ver <= 2) {
                mmu2 = (struct bcr_mmu_1_2 *)&tmp;
-               mmu->pg_sz_k = TO_KB(PAGE_SIZE);
+               mmu->pg_sz_k = TO_KB(0x2000);
                mmu->sets = 1 << mmu2->sets;
                mmu->ways = 1 << mmu2->ways;
                mmu->u_dtlb = mmu2->u_dtlb;
@@ -634,6 +772,7 @@ void read_decode_mmu_bcr(void)
                mmu->ways = 1 << mmu3->ways;
                mmu->u_dtlb = mmu3->u_dtlb;
                mmu->u_itlb = mmu3->u_itlb;
+               mmu->sasid = mmu3->sasid;
        } else {
                mmu4 = (struct bcr_mmu_4 *)&tmp;
                mmu->pg_sz_k = 1 << (mmu4->sz0 - 1);
@@ -642,9 +781,9 @@ void read_decode_mmu_bcr(void)
                mmu->ways = mmu4->n_ways * 2;
                mmu->u_dtlb = mmu4->u_dtlb * 4;
                mmu->u_itlb = mmu4->u_itlb * 4;
+               mmu->sasid = mmu4->sasid;
+               mmu->pae = mmu4->pae;
        }
-
-       mmu->num_tlb = mmu->sets * mmu->ways;
 }
 
 char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len)
@@ -655,14 +794,15 @@ char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len)
 
        if (p_mmu->s_pg_sz_m)
                scnprintf(super_pg, 64, "%dM Super Page%s, ",
-                         p_mmu->s_pg_sz_m, " (not used)");
+                         p_mmu->s_pg_sz_m,
+                         IS_USED_CFG(CONFIG_TRANSPARENT_HUGEPAGE));
 
        n += scnprintf(buf + n, len - n,
-                     "MMU [v%x]\t: %dk PAGE, %sJTLB %d (%dx%d), uDTLB %d, uITLB %d %s\n",
+                     "MMU [v%x]\t: %dk PAGE, %sJTLB %d (%dx%d), uDTLB %d, uITLB %d %s%s\n",
                       p_mmu->ver, p_mmu->pg_sz_k, super_pg,
-                      p_mmu->num_tlb, p_mmu->sets, p_mmu->ways,
+                      p_mmu->sets * p_mmu->ways, p_mmu->sets, p_mmu->ways,
                       p_mmu->u_dtlb, p_mmu->u_itlb,
-                      IS_ENABLED(CONFIG_ARC_MMU_SASID) ? ",SASID" : "");
+                      IS_AVAIL2(p_mmu->pae, "PAE40 ", CONFIG_ARC_HAS_PAE40));
 
        return buf;
 }
@@ -690,6 +830,14 @@ void arc_mmu_init(void)
        if (mmu->pg_sz_k != TO_KB(PAGE_SIZE))
                panic("MMU pg size != PAGE_SIZE (%luk)\n", TO_KB(PAGE_SIZE));
 
+       if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
+           mmu->s_pg_sz_m != TO_MB(HPAGE_PMD_SIZE))
+               panic("MMU Super pg size != Linux HPAGE_PMD_SIZE (%luM)\n",
+                     (unsigned long)TO_MB(HPAGE_PMD_SIZE));
+
+       if (IS_ENABLED(CONFIG_ARC_HAS_PAE40) && !mmu->pae)
+               panic("Hardware doesn't support PAE40\n");
+
        /* Enable the MMU */
        write_aux_reg(ARC_REG_PID, MMU_ENABLE);
 
@@ -725,15 +873,15 @@ void arc_mmu_init(void)
  *      the duplicate one.
  * -Knob to be verbose abt it.(TODO: hook them up to debugfs)
  */
-volatile int dup_pd_verbose = 1;/* Be slient abt it or complain (default) */
+volatile int dup_pd_silent; /* Be slient abt it or complain (default) */
 
 void do_tlb_overlap_fault(unsigned long cause, unsigned long address,
                          struct pt_regs *regs)
 {
-       int set, way, n;
-       unsigned long flags, is_valid;
        struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu;
-       unsigned int pd0[mmu->ways], pd1[mmu->ways];
+       unsigned int pd0[mmu->ways];
+       unsigned long flags;
+       int set;
 
        local_irq_save(flags);
 
@@ -743,14 +891,16 @@ void do_tlb_overlap_fault(unsigned long cause, unsigned long address,
        /* loop thru all sets of TLB */
        for (set = 0; set < mmu->sets; set++) {
 
+               int is_valid, way;
+
                /* read out all the ways of current set */
                for (way = 0, is_valid = 0; way < mmu->ways; way++) {
                        write_aux_reg(ARC_REG_TLBINDEX,
                                          SET_WAY_TO_IDX(mmu, set, way));
                        write_aux_reg(ARC_REG_TLBCOMMAND, TLBRead);
                        pd0[way] = read_aux_reg(ARC_REG_TLBPD0);
-                       pd1[way] = read_aux_reg(ARC_REG_TLBPD1);
                        is_valid |= pd0[way] & _PAGE_PRESENT;
+                       pd0[way] &= PAGE_MASK;
                }
 
                /* If all the WAYS in SET are empty, skip to next SET */
@@ -759,30 +909,28 @@ void do_tlb_overlap_fault(unsigned long cause, unsigned long address,
 
                /* Scan the set for duplicate ways: needs a nested loop */
                for (way = 0; way < mmu->ways - 1; way++) {
+
+                       int n;
+
                        if (!pd0[way])
                                continue;
 
                        for (n = way + 1; n < mmu->ways; n++) {
-                               if ((pd0[way] & PAGE_MASK) ==
-                                   (pd0[n] & PAGE_MASK)) {
-
-                                       if (dup_pd_verbose) {
-                                               pr_info("Duplicate PD's @"
-                                                       "[%d:%d]/[%d:%d]\n",
-                                                    set, way, set, n);
-                                               pr_info("TLBPD0[%u]: %08x\n",
-                                                    way, pd0[way]);
-                                       }
-
-                                       /*
-                                        * clear entry @way and not @n. This is
-                                        * critical to our optimised loop
-                                        */
-                                       pd0[way] = pd1[way] = 0;
-                                       write_aux_reg(ARC_REG_TLBINDEX,
+                               if (pd0[way] != pd0[n])
+                                       continue;
+
+                               if (!dup_pd_silent)
+                                       pr_info("Dup TLB PD0 %08x @ set %d ways %d,%d\n",
+                                               pd0[way], set, way, n);
+
+                               /*
+                                * clear entry @way and not @n.
+                                * This is critical to our optimised loop
+                                */
+                               pd0[way] = 0;
+                               write_aux_reg(ARC_REG_TLBINDEX,
                                                SET_WAY_TO_IDX(mmu, set, way));
-                                       __tlb_entry_erase();
-                               }
+                               __tlb_entry_erase();
                        }
                }
        }
index f6f4c3cb505d1341a8c24b1172a6a89f71e6fc26..63860adc4814083dd5365d83ffd790fa6d390ab4 100644 (file)
@@ -205,20 +205,38 @@ ex_saved_reg1:
 #endif
 
        lsr     r0, r2, PGDIR_SHIFT     ; Bits for indexing into PGD
-       ld.as   r1, [r1, r0]            ; PGD entry corresp to faulting addr
-       and.f   r1, r1, PAGE_MASK       ; Ignoring protection and other flags
-       ;   contains Ptr to Page Table
-       bz.d    do_slow_path_pf         ; if no Page Table, do page fault
+       ld.as   r3, [r1, r0]            ; PGD entry corresp to faulting addr
+       tst     r3, r3
+       bz      do_slow_path_pf         ; if no Page Table, do page fault
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       and.f   0, r3, _PAGE_HW_SZ      ; Is this Huge PMD (thp)
+       add2.nz r1, r1, r0
+       bnz.d   2f              ; YES: PGD == PMD has THP PTE: stop pgd walk
+       mov.nz  r0, r3
+
+#endif
+       and     r1, r3, PAGE_MASK
 
        ; Get the PTE entry: The idea is
        ; (1) x = addr >> PAGE_SHIFT    -> masks page-off bits from @fault-addr
        ; (2) y = x & (PTRS_PER_PTE - 1) -> to get index
-       ; (3) z = pgtbl[y]
-       ; To avoid the multiply by in end, we do the -2, <<2 below
+       ; (3) z = (pgtbl + y * 4)
+
+#ifdef CONFIG_ARC_HAS_PAE40
+#define PTE_SIZE_LOG   3       /* 8 == 2 ^ 3 */
+#else
+#define PTE_SIZE_LOG   2       /* 4 == 2 ^ 2 */
+#endif
+
+       ; multiply in step (3) above avoided by shifting lesser in step (1)
+       lsr     r0, r2, ( PAGE_SHIFT - PTE_SIZE_LOG )
+       and     r0, r0, ( (PTRS_PER_PTE - 1) << PTE_SIZE_LOG )
+       ld.aw   r0, [r1, r0]            ; r0: PTE (lower word only for PAE40)
+                                       ; r1: PTE ptr
+
+2:
 
-       lsr     r0, r2, (PAGE_SHIFT - 2)
-       and     r0, r0, ( (PTRS_PER_PTE - 1) << 2)
-       ld.aw   r0, [r1, r0]            ; get PTE and PTE ptr for fault addr
 #ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
        and.f 0, r0, _PAGE_PRESENT
        bz   1f
@@ -233,18 +251,23 @@ ex_saved_reg1:
 ;-----------------------------------------------------------------
 ; Convert Linux PTE entry into TLB entry
 ; A one-word PTE entry is programmed as two-word TLB Entry [PD0:PD1] in mmu
+;    (for PAE40, two-words PTE, while three-word TLB Entry [PD0:PD1:PD1HI])
 ; IN: r0 = PTE, r1 = ptr to PTE
 
 .macro CONV_PTE_TO_TLB
-       and    r3, r0, PTE_BITS_RWX     ;       r w x
-       lsl    r2, r3, 3                ; r w x 0 0 0 (GLOBAL, kernel only)
+       and    r3, r0, PTE_BITS_RWX     ;          r  w  x
+       lsl    r2, r3, 3                ; Kr Kw Kx 0  0  0 (GLOBAL, kernel only)
        and.f  0,  r0, _PAGE_GLOBAL
-       or.z   r2, r2, r3               ; r w x r w x (!GLOBAL, user page)
+       or.z   r2, r2, r3               ; Kr Kw Kx Ur Uw Ux (!GLOBAL, user page)
 
        and r3, r0, PTE_BITS_NON_RWX_IN_PD1 ; Extract PFN+cache bits from PTE
        or  r3, r3, r2
 
-       sr  r3, [ARC_REG_TLBPD1]        ; these go in PD1
+       sr  r3, [ARC_REG_TLBPD1]        ; paddr[31..13] | Kr Kw Kx Ur Uw Ux | C
+#ifdef CONFIG_ARC_HAS_PAE40
+       ld      r3, [r1, 4]             ; paddr[39..32]
+       sr      r3, [ARC_REG_TLBPD1HI]
+#endif
 
        and r2, r0, PTE_BITS_IN_PD0 ; Extract other PTE flags: (V)alid, (G)lb
 
@@ -365,7 +388,7 @@ ENTRY(EV_TLBMissD)
        lr      r3, [ecr]
        or      r0, r0, _PAGE_ACCESSED        ; Accessed bit always
        btst_s  r3,  ECR_C_BIT_DTLB_ST_MISS   ; See if it was a Write Access ?
-       or.nz   r0, r0, _PAGE_MODIFIED        ; if Write, set Dirty bit as well
+       or.nz   r0, r0, _PAGE_DIRTY           ; if Write, set Dirty bit as well
        st_s    r0, [r1]                      ; Write back PTE
 
        CONV_PTE_TO_TLB
index 0a77b19e1df8db1d37af0346e36a4b53254ca272..1b0f0f458a2bde2438802f431f63748779245c82 100644 (file)
@@ -455,11 +455,6 @@ static void __init axs103_early_init(void)
        axs10x_print_board_ver(AXC003_CREG + 4088, "AXC003 CPU Card");
 
        axs10x_early_init();
-
-#ifdef CONFIG_ARC_MCIP
-       /* No Hardware init, but filling the smp ops callbacks */
-       mcip_init_early_smp();
-#endif
 }
 #endif
 
@@ -487,9 +482,6 @@ static const char *axs103_compat[] __initconst = {
 MACHINE_START(AXS103, "axs103")
        .dt_compat      = axs103_compat,
        .init_early     = axs103_early_init,
-#ifdef CONFIG_ARC_MCIP
-       .init_smp       = mcip_init_smp,
-#endif
 MACHINE_END
 
 /*
index d9e35b4a2f0861bd9e0bd9ad38360e959029d62c..dde692812bc16ac70bc3bfba24fc14ba71d158a2 100644 (file)
@@ -30,8 +30,4 @@ static const char *simulation_compat[] __initconst = {
 
 MACHINE_START(SIMULATION, "simulation")
        .dt_compat      = simulation_compat,
-#ifdef CONFIG_ARC_MCIP
-       .init_early     = mcip_init_early_smp,
-       .init_smp       = mcip_init_smp,
-#endif
 MACHINE_END
index 72ad724c67ae94cd6682ec15f3834966dd7028c0..f1ed1109f4889e006e9df4c6110be001841c0f82 100644 (file)
@@ -645,6 +645,7 @@ config ARCH_SHMOBILE_LEGACY
 
 config ARCH_RPC
        bool "RiscPC"
+       depends on MMU
        select ARCH_ACORN
        select ARCH_MAY_HAVE_PC_FDC
        select ARCH_SPARSEMEM_ENABLE
@@ -819,6 +820,7 @@ config ARCH_VIRT
        bool "Dummy Virtual Machine" if ARCH_MULTI_V7
        select ARM_AMBA
        select ARM_GIC
+       select ARM_GIC_V3
        select ARM_PSCI
        select HAVE_ARM_ARCH_TIMER
 
@@ -1410,7 +1412,6 @@ config HAVE_ARM_ARCH_TIMER
 
 config HAVE_ARM_TWD
        bool
-       depends on SMP
        select CLKSRC_OF if OF
        help
          This options enables support for the ARM timer and watchdog unit
@@ -1470,6 +1471,8 @@ choice
 
        config VMSPLIT_3G
                bool "3G/1G user/kernel split"
+       config VMSPLIT_3G_OPT
+               bool "3G/1G user/kernel split (for full 1G low memory)"
        config VMSPLIT_2G
                bool "2G/2G user/kernel split"
        config VMSPLIT_1G
@@ -1481,6 +1484,7 @@ config PAGE_OFFSET
        default PHYS_OFFSET if !MMU
        default 0x40000000 if VMSPLIT_1G
        default 0x80000000 if VMSPLIT_2G
+       default 0xB0000000 if VMSPLIT_3G_OPT
        default 0xC0000000
 
 config NR_CPUS
@@ -1695,8 +1699,9 @@ config HIGHMEM
          If unsure, say n.
 
 config HIGHPTE
-       bool "Allocate 2nd-level pagetables from highmem"
+       bool "Allocate 2nd-level pagetables from highmem" if EXPERT
        depends on HIGHMEM
+       default y
        help
          The VM uses one page of physical memory for each page table.
          For systems with a lot of processes, this can use a lot of
index 0447c04a40cc439d90296c0f9d70b541eedf18b8..d83ff9c9701e36d5d837c87ce292d89c421c4a70 100644 (file)
                        cpts_clock_mult = <0x80000000>;
                        cpts_clock_shift = <29>;
                        ranges;
+                       syscon = <&scm_conf>;
 
                        davinci_mdio: mdio@4a101000 {
                                compatible = "ti,am4372-mdio","ti,davinci_mdio";
index e289c706d27d50d5597f57a10b7ecd2891523d30..8fedddc35999eef934131943c5696ea3af1a33ed 100644 (file)
                                     <GIC_SPI 336 IRQ_TYPE_LEVEL_HIGH>,
                                     <GIC_SPI 337 IRQ_TYPE_LEVEL_HIGH>;
                        ranges;
+                       syscon = <&scm_conf>;
                        status = "disabled";
 
                        davinci_mdio: mdio@48485000 {
index 955c24ee4a8cbfea89248c2efcb93d9536c0221b..8c24975e8f9d66387b23d9eeb38ab96142609421 100644 (file)
 
                button@1 {
                        debounce_interval = <50>;
-                       wakeup = <1>;
+                       wakeup-source;
                        label = "DSW2-1";
                        linux,code = <KEY_1>;
                        gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
                };
                button@2 {
                        debounce_interval = <50>;
-                       wakeup = <1>;
+                       wakeup-source;
                        label = "DSW2-2";
                        linux,code = <KEY_2>;
                        gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
                };
                button@3 {
                        debounce_interval = <50>;
-                       wakeup = <1>;
+                       wakeup-source;
                        label = "DSW2-3";
                        linux,code = <KEY_3>;
                        gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>;
                };
                button@4 {
                        debounce_interval = <50>;
-                       wakeup = <1>;
+                       wakeup-source;
                        label = "DSW2-4";
                        linux,code = <KEY_4>;
                        gpios = <&gpio0 17 GPIO_ACTIVE_HIGH>;
index 36155b749d9fcbbf2bb579e0af5cccd066234867..d2d44df9c8c0b867242725f7c53d918e99379480 100644 (file)
                         clock-frequency = <32768>;
               };
        };
+
+       netxbig-leds {
+               blue-sata2 {
+                       label = "netxbig:blue:sata2";
+                       mode-addr = <5>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 7
+                                   NETXBIG_LED_SATA 1
+                                   NETXBIG_LED_TIMER1 3>;
+                       bright-addr = <2>;
+                       max-brightness = <7>;
+               };
+               red-sata2 {
+                       label = "netxbig:red:sata2";
+                       mode-addr = <5>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 2
+                                   NETXBIG_LED_TIMER1 4>;
+                       bright-addr = <2>;
+                       max-brightness = <7>;
+               };
+               blue-sata3 {
+                       label = "netxbig:blue:sata3";
+                       mode-addr = <6>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 7
+                                   NETXBIG_LED_SATA 1
+                                   NETXBIG_LED_TIMER1 3>;
+                       bright-addr = <2>;
+                       max-brightness = <7>;
+               };
+               red-sata3 {
+                       label = "netxbig:red:sata3";
+                       mode-addr = <6>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 2
+                                   NETXBIG_LED_TIMER1 4>;
+                       bright-addr = <2>;
+                       max-brightness = <7>;
+               };
+               blue-sata4 {
+                       label = "netxbig:blue:sata4";
+                       mode-addr = <7>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 7
+                                   NETXBIG_LED_SATA 1
+                                   NETXBIG_LED_TIMER1 3>;
+                       bright-addr = <2>;
+                       max-brightness = <7>;
+               };
+               red-sata4 {
+                       label = "netxbig:red:sata4";
+                       mode-addr = <7>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 2
+                                   NETXBIG_LED_TIMER1 4>;
+                       bright-addr = <2>;
+                       max-brightness = <7>;
+               };
+       };
 };
 
 &mdio {
index 1508b12147df35804837bf2ec9b8882a1681ef09..62515a8b99b995a4a7ae2f2e7d2a42149f99bec1 100644 (file)
@@ -13,6 +13,7 @@
  * warranty of any kind, whether express or implied.
 */
 
+#include <dt-bindings/leds/leds-netxbig.h>
 #include "kirkwood.dtsi"
 #include "kirkwood-6281.dtsi"
 
                        gpio = <&gpio0 16 GPIO_ACTIVE_HIGH>;
                };
        };
+
+       netxbig_gpio_ext: netxbig-gpio-ext {
+               compatible = "lacie,netxbig-gpio-ext";
+
+               addr-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH
+                             &gpio1 16 GPIO_ACTIVE_HIGH
+                             &gpio1 17 GPIO_ACTIVE_HIGH>;
+               data-gpios = <&gpio1 12 GPIO_ACTIVE_HIGH
+                             &gpio1 13 GPIO_ACTIVE_HIGH
+                             &gpio1 14 GPIO_ACTIVE_HIGH>;
+               enable-gpio = <&gpio0 29 GPIO_ACTIVE_HIGH>;
+       };
+
+       netxbig-leds {
+               compatible = "lacie,netxbig-leds";
+
+               gpio-ext = <&netxbig_gpio_ext>;
+
+               timers = <NETXBIG_LED_TIMER1 500 500
+                         NETXBIG_LED_TIMER2 500 1000>;
+
+               blue-power {
+                       label = "netxbig:blue:power";
+                       mode-addr = <0>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 1
+                                   NETXBIG_LED_TIMER1 3
+                                   NETXBIG_LED_TIMER2 7>;
+                       bright-addr = <1>;
+                       max-brightness = <7>;
+               };
+               red-power {
+                       label = "netxbig:red:power";
+                       mode-addr = <0>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 2
+                                   NETXBIG_LED_TIMER1 4>;
+                       bright-addr = <1>;
+                       max-brightness = <7>;
+               };
+               blue-sata0 {
+                       label = "netxbig:blue:sata0";
+                       mode-addr = <3>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 7
+                                   NETXBIG_LED_SATA 1
+                                   NETXBIG_LED_TIMER1 3>;
+                       bright-addr = <2>;
+                       max-brightness = <7>;
+               };
+               red-sata0 {
+                       label = "netxbig:red:sata0";
+                       mode-addr = <3>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 2
+                                   NETXBIG_LED_TIMER1 4>;
+                       bright-addr = <2>;
+                       max-brightness = <7>;
+               };
+               blue-sata1 {
+                       label = "netxbig:blue:sata1";
+                       mode-addr = <4>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 7
+                                   NETXBIG_LED_SATA 1
+                                   NETXBIG_LED_TIMER1 3>;
+                       bright-addr = <2>;
+                       max-brightness = <7>;
+               };
+               red-sata1 {
+                       label = "netxbig:red:sata1";
+                       mode-addr = <4>;
+                       mode-val = <NETXBIG_LED_OFF 0
+                                   NETXBIG_LED_ON 2
+                                   NETXBIG_LED_TIMER1 4>;
+                       bright-addr = <2>;
+                       max-brightness = <7>;
+               };
+       };
 };
 
 &mdio {
index b5334ecff13cdf1b9d7bfd0649f6737c56ce09dd..fec076eb7aef8f766c939e8b91dca6ef72595bb3 100644 (file)
@@ -90,7 +90,7 @@
        regulators {
                vccio_sd: LDO_REG4 {
                        regulator-name = "vccio_sd";
-                       regulator-min-microvolt = <3300000>;
+                       regulator-min-microvolt = <1800000>;
                        regulator-max-microvolt = <3300000>;
                        regulator-state-mem {
                                regulator-off-in-suspend;
        cap-sd-highspeed;
        card-detect-delay = <200>;
        cd-gpios = <&gpio7 5 GPIO_ACTIVE_LOW>;
+       rockchip,default-sample-phase = <90>;
        num-slots = <1>;
+       sd-uhs-sdr12;
+       sd-uhs-sdr25;
+       sd-uhs-sdr50;
+       sd-uhs-sdr104;
        vmmc-supply = <&vcc33_sd>;
        vqmmc-supply = <&vccio_sd>;
 };
index 275c78ccc0f3e1e378983871f0c71df91ab1dd89..860cea0a7613166d64bfc5924a14af5a1dab8aeb 100644 (file)
        broken-cd;
        bus-width = <8>;
        cap-mmc-highspeed;
+       rockchip,default-sample-phase = <158>;
        disable-wp;
+       mmc-hs200-1_8v;
        mmc-pwrseq = <&emmc_pwrseq>;
        non-removable;
        num-slots = <1>;
        num-slots = <1>;
        pinctrl-names = "default";
        pinctrl-0 = <&sdio0_clk &sdio0_cmd &sdio0_bus4>;
+       sd-uhs-sdr12;
+       sd-uhs-sdr25;
+       sd-uhs-sdr50;
+       sd-uhs-sdr104;
        vmmc-supply = <&vcc33_sys>;
        vqmmc-supply = <&vcc18_wl>;
 };
index 906e938fb6bfc70d9ec7948171ad425f34ae11f3..4e7c6b7392afdb70974078c80154b10a052fb024 100644 (file)
        sdmmc: dwmmc@ff0c0000 {
                compatible = "rockchip,rk3288-dw-mshc";
                clock-freq-min-max = <400000 150000000>;
-               clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>;
-               clock-names = "biu", "ciu";
+               clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
+                        <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
+               clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
                fifo-depth = <0x100>;
                interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
                reg = <0xff0c0000 0x4000>;
        sdio0: dwmmc@ff0d0000 {
                compatible = "rockchip,rk3288-dw-mshc";
                clock-freq-min-max = <400000 150000000>;
-               clocks = <&cru HCLK_SDIO0>, <&cru SCLK_SDIO0>;
-               clock-names = "biu", "ciu";
+               clocks = <&cru HCLK_SDIO0>, <&cru SCLK_SDIO0>,
+                        <&cru SCLK_SDIO0_DRV>, <&cru SCLK_SDIO0_SAMPLE>;
+               clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
                fifo-depth = <0x100>;
                interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
                reg = <0xff0d0000 0x4000>;
        sdio1: dwmmc@ff0e0000 {
                compatible = "rockchip,rk3288-dw-mshc";
                clock-freq-min-max = <400000 150000000>;
-               clocks = <&cru HCLK_SDIO1>, <&cru SCLK_SDIO1>;
-               clock-names = "biu", "ciu";
+               clocks = <&cru HCLK_SDIO1>, <&cru SCLK_SDIO1>,
+                        <&cru SCLK_SDIO1_DRV>, <&cru SCLK_SDIO1_SAMPLE>;
+               clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
                fifo-depth = <0x100>;
                interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
                reg = <0xff0e0000 0x4000>;
        emmc: dwmmc@ff0f0000 {
                compatible = "rockchip,rk3288-dw-mshc";
                clock-freq-min-max = <400000 150000000>;
-               clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>;
-               clock-names = "biu", "ciu";
+               clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>,
+                        <&cru SCLK_EMMC_DRV>, <&cru SCLK_EMMC_SAMPLE>;
+               clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
                fifo-depth = <0x100>;
                interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
                reg = <0xff0f0000 0x4000>;
index 034cd48ae28b49b8f29153818abba1dc0187fe0a..cc05cde0f9a4145436f5b3807d78e056b140094e 100644 (file)
                                clocks = <&twi1_clk>;
                                status = "disabled";
                        };
+
+                       pioA: pinctrl@fc038000 {
+                               compatible = "atmel,sama5d2-pinctrl";
+                               reg = <0xfc038000 0x600>;
+                               interrupts = <18 IRQ_TYPE_LEVEL_HIGH 7>,
+                                            <68 IRQ_TYPE_LEVEL_HIGH 7>,
+                                            <69 IRQ_TYPE_LEVEL_HIGH 7>,
+                                            <70 IRQ_TYPE_LEVEL_HIGH 7>;
+                               interrupt-controller;
+                               #interrupt-cells = <2>;
+                               gpio-controller;
+                               #gpio-cells = <2>;
+                               clocks = <&pioA_clk>;
+                       };
                };
        };
 };
index 32a5ccb14e7ebfbaec118ceed1f3bc14c2b646df..e80e42163883610005287282d9673c80377b6ce4 100644 (file)
 
                button@1 {
                        debounce_interval = <50>;
-                       wakeup = <1>;
+                       wakeup-source;
                        linux,code = <2>;
                        label = "userpb";
                        gpios = <&gpio1 0 0x4>;
                };
                button@2 {
                        debounce_interval = <50>;
-                       wakeup = <1>;
+                       wakeup-source;
                        linux,code = <3>;
                        label = "extkb1";
                        gpios = <&gpio4 23 0x4>;
                };
                button@3 {
                        debounce_interval = <50>;
-                       wakeup = <1>;
+                       wakeup-source;
                        linux,code = <4>;
                        label = "extkb2";
                        gpios = <&gpio4 24 0x4>;
                };
                button@4 {
                        debounce_interval = <50>;
-                       wakeup = <1>;
+                       wakeup-source;
                        linux,code = <5>;
                        label = "extkb3";
                        gpios = <&gpio5 1 0x4>;
                };
                button@5 {
                        debounce_interval = <50>;
-                       wakeup = <1>;
+                       wakeup-source;
                        linux,code = <6>;
                        label = "extkb4";
                        gpios = <&gpio5 2 0x4>;
index ae0527754000e6f8752b4f12cced4d8c68c165d3..0c24fcb0357703df59ca41588a9061f1da4f4bd0 100644 (file)
                        clocks          = <&clk_sysin>;
                        st,pwm-num-chan = <4>;
                };
+
+               rng10: rng@08a89000 {
+                       compatible      = "st,rng";
+                       reg             = <0x08a89000 0x1000>;
+                       clocks          = <&clk_sysin>;
+                       status          = "okay";
+               };
+
+               rng11: rng@08a8a000 {
+                       compatible      = "st,rng";
+                       reg             = <0x08a8a000 0x1000>;
+                       clocks          = <&clk_sysin>;
+                       status          = "okay";
+               };
        };
 };
index d78a4815da8f3ef9605d6aad4f463a0feb3d920d..5e1e234e8c0a183fe3f7d056ff75290eb5d43933 100644 (file)
                        reg = <0x40023800 0x400>;
                        clocks = <&clk_hse>;
                };
+
+               rng: rng@50060800 {
+                       compatible = "st,stm32-rng";
+                       reg = <0x50060800 0x400>;
+                       interrupts = <80>;
+                       clocks = <&rcc 0 38>;
+               };
        };
 };
 
index 1ff2bfa2e183f45087571875197888e81e3cc8ad..13ba48c4b03b0e0095cf5cc1a40bb9d26da77f92 100644 (file)
@@ -166,7 +166,6 @@ CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_S3C=y
 CONFIG_MMC_SDHCI_S3C_DMA=y
 CONFIG_MMC_DW=y
-CONFIG_MMC_DW_IDMAC=y
 CONFIG_MMC_DW_EXYNOS=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_MAX77686=y
index 5997dbc69822af26b1c7cbd86679ae2488529de8..b2e340b272eea44362f6a494cc23667a4537241a 100644 (file)
@@ -69,7 +69,6 @@ CONFIG_NOP_USB_XCEIV=y
 CONFIG_MMC=y
 CONFIG_RTC_CLASS=y
 CONFIG_MMC_DW=y
-CONFIG_MMC_DW_IDMAC=y
 CONFIG_MMC_DW_PLTFM=y
 CONFIG_RTC_DRV_PL031=y
 CONFIG_DMADEVICES=y
index 1c47f86c3970ae926b169e00c642fa853f9f365d..b7e8cdab51f97ab4689a46fee6f8ad82a827bf7d 100644 (file)
@@ -119,7 +119,6 @@ CONFIG_USB_EHCI_HCD=y
 CONFIG_USB_EHCI_ROOT_HUB_TT=y
 CONFIG_MMC=y
 CONFIG_MMC_DW=y
-CONFIG_MMC_DW_IDMAC=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
 CONFIG_LEDS_PCA9532=y
diff --git a/arch/arm/include/asm/arch_gicv3.h b/arch/arm/include/asm/arch_gicv3.h
new file mode 100644 (file)
index 0000000..6607d97
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * arch/arm/include/asm/arch_gicv3.h
+ *
+ * Copyright (C) 2015 ARM Ltd.
+ *
+ * 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.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_ARCH_GICV3_H
+#define __ASM_ARCH_GICV3_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/io.h>
+
+#define __ACCESS_CP15(CRn, Op1, CRm, Op2)      p15, Op1, %0, CRn, CRm, Op2
+#define __ACCESS_CP15_64(Op1, CRm)             p15, Op1, %Q0, %R0, CRm
+
+#define ICC_EOIR1                      __ACCESS_CP15(c12, 0, c12, 1)
+#define ICC_DIR                                __ACCESS_CP15(c12, 0, c11, 1)
+#define ICC_IAR1                       __ACCESS_CP15(c12, 0, c12, 0)
+#define ICC_SGI1R                      __ACCESS_CP15_64(0, c12)
+#define ICC_PMR                                __ACCESS_CP15(c4, 0, c6, 0)
+#define ICC_CTLR                       __ACCESS_CP15(c12, 0, c12, 4)
+#define ICC_SRE                                __ACCESS_CP15(c12, 0, c12, 5)
+#define ICC_IGRPEN1                    __ACCESS_CP15(c12, 0, c12, 7)
+
+#define ICC_HSRE                       __ACCESS_CP15(c12, 4, c9, 5)
+
+#define ICH_VSEIR                      __ACCESS_CP15(c12, 4, c9, 4)
+#define ICH_HCR                                __ACCESS_CP15(c12, 4, c11, 0)
+#define ICH_VTR                                __ACCESS_CP15(c12, 4, c11, 1)
+#define ICH_MISR                       __ACCESS_CP15(c12, 4, c11, 2)
+#define ICH_EISR                       __ACCESS_CP15(c12, 4, c11, 3)
+#define ICH_ELSR                       __ACCESS_CP15(c12, 4, c11, 5)
+#define ICH_VMCR                       __ACCESS_CP15(c12, 4, c11, 7)
+
+#define __LR0(x)                       __ACCESS_CP15(c12, 4, c12, x)
+#define __LR8(x)                       __ACCESS_CP15(c12, 4, c13, x)
+
+#define ICH_LR0                                __LR0(0)
+#define ICH_LR1                                __LR0(1)
+#define ICH_LR2                                __LR0(2)
+#define ICH_LR3                                __LR0(3)
+#define ICH_LR4                                __LR0(4)
+#define ICH_LR5                                __LR0(5)
+#define ICH_LR6                                __LR0(6)
+#define ICH_LR7                                __LR0(7)
+#define ICH_LR8                                __LR8(0)
+#define ICH_LR9                                __LR8(1)
+#define ICH_LR10                       __LR8(2)
+#define ICH_LR11                       __LR8(3)
+#define ICH_LR12                       __LR8(4)
+#define ICH_LR13                       __LR8(5)
+#define ICH_LR14                       __LR8(6)
+#define ICH_LR15                       __LR8(7)
+
+/* LR top half */
+#define __LRC0(x)                      __ACCESS_CP15(c12, 4, c14, x)
+#define __LRC8(x)                      __ACCESS_CP15(c12, 4, c15, x)
+
+#define ICH_LRC0                       __LRC0(0)
+#define ICH_LRC1                       __LRC0(1)
+#define ICH_LRC2                       __LRC0(2)
+#define ICH_LRC3                       __LRC0(3)
+#define ICH_LRC4                       __LRC0(4)
+#define ICH_LRC5                       __LRC0(5)
+#define ICH_LRC6                       __LRC0(6)
+#define ICH_LRC7                       __LRC0(7)
+#define ICH_LRC8                       __LRC8(0)
+#define ICH_LRC9                       __LRC8(1)
+#define ICH_LRC10                      __LRC8(2)
+#define ICH_LRC11                      __LRC8(3)
+#define ICH_LRC12                      __LRC8(4)
+#define ICH_LRC13                      __LRC8(5)
+#define ICH_LRC14                      __LRC8(6)
+#define ICH_LRC15                      __LRC8(7)
+
+#define __AP0Rx(x)                     __ACCESS_CP15(c12, 4, c8, x)
+#define ICH_AP0R0                      __AP0Rx(0)
+#define ICH_AP0R1                      __AP0Rx(1)
+#define ICH_AP0R2                      __AP0Rx(2)
+#define ICH_AP0R3                      __AP0Rx(3)
+
+#define __AP1Rx(x)                     __ACCESS_CP15(c12, 4, c9, x)
+#define ICH_AP1R0                      __AP1Rx(0)
+#define ICH_AP1R1                      __AP1Rx(1)
+#define ICH_AP1R2                      __AP1Rx(2)
+#define ICH_AP1R3                      __AP1Rx(3)
+
+/* Low-level accessors */
+
+static inline void gic_write_eoir(u32 irq)
+{
+       asm volatile("mcr " __stringify(ICC_EOIR1) : : "r" (irq));
+       isb();
+}
+
+static inline void gic_write_dir(u32 val)
+{
+       asm volatile("mcr " __stringify(ICC_DIR) : : "r" (val));
+       isb();
+}
+
+static inline u32 gic_read_iar(void)
+{
+       u32 irqstat;
+
+       asm volatile("mrc " __stringify(ICC_IAR1) : "=r" (irqstat));
+       return irqstat;
+}
+
+static inline void gic_write_pmr(u32 val)
+{
+       asm volatile("mcr " __stringify(ICC_PMR) : : "r" (val));
+}
+
+static inline void gic_write_ctlr(u32 val)
+{
+       asm volatile("mcr " __stringify(ICC_CTLR) : : "r" (val));
+       isb();
+}
+
+static inline void gic_write_grpen1(u32 val)
+{
+       asm volatile("mcr " __stringify(ICC_IGRPEN1) : : "r" (val));
+       isb();
+}
+
+static inline void gic_write_sgi1r(u64 val)
+{
+       asm volatile("mcrr " __stringify(ICC_SGI1R) : : "r" (val));
+}
+
+static inline u32 gic_read_sre(void)
+{
+       u32 val;
+
+       asm volatile("mrc " __stringify(ICC_SRE) : "=r" (val));
+       return val;
+}
+
+static inline void gic_write_sre(u32 val)
+{
+       asm volatile("mcr " __stringify(ICC_SRE) : : "r" (val));
+       isb();
+}
+
+/*
+ * Even in 32bit systems that use LPAE, there is no guarantee that the I/O
+ * interface provides true 64bit atomic accesses, so using strd/ldrd doesn't
+ * make much sense.
+ * Moreover, 64bit I/O emulation is extremely difficult to implement on
+ * AArch32, since the syndrome register doesn't provide any information for
+ * them.
+ * Consequently, the following IO helpers use 32bit accesses.
+ *
+ * There are only two registers that need 64bit accesses in this driver:
+ * - GICD_IROUTERn, contain the affinity values associated to each interrupt.
+ *   The upper-word (aff3) will always be 0, so there is no need for a lock.
+ * - GICR_TYPER is an ID register and doesn't need atomicity.
+ */
+static inline void gic_write_irouter(u64 val, volatile void __iomem *addr)
+{
+       writel_relaxed((u32)val, addr);
+       writel_relaxed((u32)(val >> 32), addr + 4);
+}
+
+static inline u64 gic_read_typer(const volatile void __iomem *addr)
+{
+       u64 val;
+
+       val = readl_relaxed(addr);
+       val |= (u64)readl_relaxed(addr + 4) << 32;
+       return val;
+}
+
+#endif /* !__ASSEMBLY__ */
+#endif /* !__ASM_ARCH_GICV3_H */
index fe3ef397f5a407073e6a737cc12fbbbcdbe714c1..9e10c4567eb4dd3c898486f5fc34777c3760adc9 100644 (file)
@@ -27,8 +27,8 @@
  * strex/ldrex monitor on some implementations. The reason we can use it for
  * atomic_set() is the clrex or dummy strex done on every exception return.
  */
-#define atomic_read(v) ACCESS_ONCE((v)->counter)
-#define atomic_set(v,i)        (((v)->counter) = (i))
+#define atomic_read(v) READ_ONCE((v)->counter)
+#define atomic_set(v,i)        WRITE_ONCE(((v)->counter), (i))
 
 #if __LINUX_ARM_ARCH__ >= 6
 
@@ -210,8 +210,8 @@ ATOMIC_OP(xor, ^=, eor)
 
 #define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0)
 #define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0)
-#define atomic_inc_return(v)    (atomic_add_return(1, v))
-#define atomic_dec_return(v)    (atomic_sub_return(1, v))
+#define atomic_inc_return_relaxed(v)    (atomic_add_return_relaxed(1, v))
+#define atomic_dec_return_relaxed(v)    (atomic_sub_return_relaxed(1, v))
 #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
 
 #define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)
@@ -442,11 +442,11 @@ static inline int atomic64_add_unless(atomic64_t *v, long long a, long long u)
 
 #define atomic64_add_negative(a, v)    (atomic64_add_return((a), (v)) < 0)
 #define atomic64_inc(v)                        atomic64_add(1LL, (v))
-#define atomic64_inc_return(v)         atomic64_add_return(1LL, (v))
+#define atomic64_inc_return_relaxed(v) atomic64_add_return_relaxed(1LL, (v))
 #define atomic64_inc_and_test(v)       (atomic64_inc_return(v) == 0)
 #define atomic64_sub_and_test(a, v)    (atomic64_sub_return((a), (v)) == 0)
 #define atomic64_dec(v)                        atomic64_sub(1LL, (v))
-#define atomic64_dec_return(v)         atomic64_sub_return(1LL, (v))
+#define atomic64_dec_return_relaxed(v) atomic64_sub_return_relaxed(1LL, (v))
 #define atomic64_dec_and_test(v)       (atomic64_dec_return((v)) == 0)
 #define atomic64_inc_not_zero(v)       atomic64_add_unless((v), 1LL, 0LL)
 
index 916a2744d5c66da7c442ed9fec56c01261ff2eb4..97882f9bad12938780b3455d5ae8f8b184d5f5be 100644 (file)
@@ -39,6 +39,7 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size
 
        switch (size) {
 #if __LINUX_ARM_ARCH__ >= 6
+#ifndef CONFIG_CPU_V6 /* MIN ARCH >= V6K */
        case 1:
                asm volatile("@ __xchg1\n"
                "1:     ldrexb  %0, [%3]\n"
@@ -49,6 +50,17 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size
                        : "r" (x), "r" (ptr)
                        : "memory", "cc");
                break;
+       case 2:
+               asm volatile("@ __xchg2\n"
+               "1:     ldrexh  %0, [%3]\n"
+               "       strexh  %1, %2, [%3]\n"
+               "       teq     %1, #0\n"
+               "       bne     1b"
+                       : "=&r" (ret), "=&r" (tmp)
+                       : "r" (x), "r" (ptr)
+                       : "memory", "cc");
+               break;
+#endif
        case 4:
                asm volatile("@ __xchg4\n"
                "1:     ldrex   %0, [%3]\n"
index 43908146a5cf05c473967d603247a792f73b5663..e6b70d9d084ea5d369c237f9a3f81c92e331a429 100644 (file)
@@ -54,6 +54,14 @@ static inline void arch_local_irq_disable(void)
 
 #define local_fiq_enable()  __asm__("cpsie f   @ __stf" : : : "memory", "cc")
 #define local_fiq_disable() __asm__("cpsid f   @ __clf" : : : "memory", "cc")
+
+#ifndef CONFIG_CPU_V7M
+#define local_abt_enable()  __asm__("cpsie a   @ __sta" : : : "memory", "cc")
+#define local_abt_disable() __asm__("cpsid a   @ __cla" : : : "memory", "cc")
+#else
+#define local_abt_enable()     do { } while (0)
+#define local_abt_disable()    do { } while (0)
+#endif
 #else
 
 /*
@@ -136,6 +144,8 @@ static inline void arch_local_irq_disable(void)
        : "memory", "cc");                                      \
        })
 
+#define local_abt_enable()     do { } while (0)
+#define local_abt_disable()    do { } while (0)
 #endif
 
 /*
index cb3a40717edd045042f97d1f1d7a81ba658dca3f..5c1ad11aa39264aee7e9210cbf6747adab443930 100644 (file)
@@ -47,7 +47,7 @@ struct machine_desc {
        unsigned                l2c_aux_val;    /* L2 cache aux value   */
        unsigned                l2c_aux_mask;   /* L2 cache aux mask    */
        void                    (*l2c_write_sec)(unsigned long, unsigned);
-       struct smp_operations   *smp;           /* SMP operations       */
+       const struct smp_operations     *smp;   /* SMP operations       */
        bool                    (*smp_init)(void);
        void                    (*fixup)(struct tag *, char **);
        void                    (*dt_fixup)(void);
index 98d58bb04ac57853910860ae5f124c1fd1cb14bf..c79b57bf71c40d1fc2083f67b198377936e0002f 100644 (file)
  */
 #define XIP_VIRT_ADDR(physaddr)  (MODULES_VADDR + ((physaddr) & 0x000fffff))
 
+#if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE)
 /*
  * Allow 16MB-aligned ioremap pages
  */
 #define IOREMAP_MAX_ORDER      24
+#endif
 
 #else /* CONFIG_MMU */
 
index f40354198bad4078717ac8933966d511c6dc130b..348caabb7625ee7b12fc0ddd8e2546b22f338b1c 100644 (file)
@@ -43,7 +43,7 @@
  */
 #define VMALLOC_OFFSET         (8*1024*1024)
 #define VMALLOC_START          (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
-#define VMALLOC_END            0xff000000UL
+#define VMALLOC_END            0xff800000UL
 
 #define LIBRARY_TEXT_START     0x0c000000
 
index ef356659b4f43e6125443e7edd67aa1706bc8d2e..3d6dc8b460e4b4ea6b32d66f3ec0004378e53d9f 100644 (file)
@@ -112,7 +112,7 @@ struct smp_operations {
 
 struct of_cpu_method {
        const char *method;
-       struct smp_operations *ops;
+       const struct smp_operations *ops;
 };
 
 #define CPU_METHOD_OF_DECLARE(name, _method, _ops)                     \
@@ -122,6 +122,6 @@ struct of_cpu_method {
 /*
  * set platform specific SMP operations
  */
-extern void smp_set_ops(struct smp_operations *);
+extern void smp_set_ops(const struct smp_operations *);
 
 #endif /* ifndef __ASM_ARM_SMP_H */
index 7cba573c2cc9541a23ff2e0675a701117f7cf269..7b84657fba3577ea3d29ecf1b93e3267feecbb59 100644 (file)
  */
 #define __NR_syscalls  (392)
 
-/*
- * *NOTE*: This is a ghost syscall private to the kernel.  Only the
- * __kuser_cmpxchg code in entry-armv.S should be aware of its
- * existence.  Don't ever use this from user code.
- */
-#define __ARM_NR_cmpxchg               (__ARM_NR_BASE+0x00fff0)
-
 #define __ARCH_WANT_STAT64
 #define __ARCH_WANT_SYS_GETHOSTNAME
 #define __ARCH_WANT_SYS_PAUSE
index 11c54de9f8cfa1e6227ea38dcfddddf2f67ee403..65addcbf5b308acf5dc584419b90f6c65b28d343 100644 (file)
@@ -101,6 +101,7 @@ void __init arm_dt_init_cpu_maps(void)
                if (of_property_read_u32(cpu, "reg", &hwid)) {
                        pr_debug(" * %s missing reg property\n",
                                     cpu->full_name);
+                       of_node_put(cpu);
                        return;
                }
 
@@ -108,8 +109,10 @@ void __init arm_dt_init_cpu_maps(void)
                 * 8 MSBs must be set to 0 in the DT since the reg property
                 * defines the MPIDR[23:0].
                 */
-               if (hwid & ~MPIDR_HWID_BITMASK)
+               if (hwid & ~MPIDR_HWID_BITMASK) {
+                       of_node_put(cpu);
                        return;
+               }
 
                /*
                 * Duplicate MPIDRs are a recipe for disaster.
@@ -119,9 +122,11 @@ void __init arm_dt_init_cpu_maps(void)
                 * to avoid matching valid MPIDR[23:0] values.
                 */
                for (j = 0; j < cpuidx; j++)
-                       if (WARN(tmp_map[j] == hwid, "Duplicate /cpu reg "
-                                                    "properties in the DT\n"))
+                       if (WARN(tmp_map[j] == hwid,
+                                "Duplicate /cpu reg properties in the DT\n")) {
+                               of_node_put(cpu);
                                return;
+                       }
 
                /*
                 * Build a stashed array of MPIDR values. Numbering scheme
@@ -143,6 +148,7 @@ void __init arm_dt_init_cpu_maps(void)
                                               "max cores %u, capping them\n",
                                               cpuidx, nr_cpu_ids)) {
                        cpuidx = nr_cpu_ids;
+                       of_node_put(cpu);
                        break;
                }
 
index 3e1c26eb32b43e13a5fa3e70b2d09f91a07ebd4e..3ce377f7251f3429668c2e2b563fcd8062c991ae 100644 (file)
@@ -427,8 +427,7 @@ ENDPROC(__fiq_abt)
        .endm
 
        .macro  kuser_cmpxchg_check
-#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS) && \
-    !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
+#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS)
 #ifndef CONFIG_MMU
 #warning "NPTL on non MMU needs fixing"
 #else
@@ -859,20 +858,7 @@ __kuser_helper_start:
 
 __kuser_cmpxchg64:                             @ 0xffff0f60
 
-#if defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
-
-       /*
-        * Poor you.  No fast solution possible...
-        * The kernel itself must perform the operation.
-        * A special ghost syscall is used for that (see traps.c).
-        */
-       stmfd   sp!, {r7, lr}
-       ldr     r7, 1f                  @ it's 20 bits
-       swi     __ARM_NR_cmpxchg64
-       ldmfd   sp!, {r7, pc}
-1:     .word   __ARM_NR_cmpxchg64
-
-#elif defined(CONFIG_CPU_32v6K)
+#if defined(CONFIG_CPU_32v6K)
 
        stmfd   sp!, {r4, r5, r6, r7}
        ldrd    r4, r5, [r0]                    @ load old val
@@ -948,20 +934,7 @@ __kuser_memory_barrier:                            @ 0xffff0fa0
 
 __kuser_cmpxchg:                               @ 0xffff0fc0
 
-#if defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
-
-       /*
-        * Poor you.  No fast solution possible...
-        * The kernel itself must perform the operation.
-        * A special ghost syscall is used for that (see traps.c).
-        */
-       stmfd   sp!, {r7, lr}
-       ldr     r7, 1f                  @ it's 20 bits
-       swi     __ARM_NR_cmpxchg
-       ldmfd   sp!, {r7, pc}
-1:     .word   __ARM_NR_cmpxchg
-
-#elif __LINUX_ARM_ARCH__ < 6
+#if __LINUX_ARM_ARCH__ < 6
 
 #ifdef CONFIG_MMU
 
index dc7d0a95bd3651c2949454027a3b6c47dd77863c..6284779d64ee6394dc11b38cc32e24cefac276b1 100644 (file)
@@ -35,7 +35,6 @@
 #include <asm/cputype.h>
 #include <asm/current.h>
 #include <asm/hw_breakpoint.h>
-#include <asm/kdebug.h>
 #include <asm/traps.h>
 
 /* Breakpoint currently in use for each BRP. */
index fd9eefce0a7b8d66e019b048e4ee24753da7acc0..9232caee70604c686e07b7aef4c21598c7196fa4 100644 (file)
@@ -74,7 +74,7 @@ int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
 void
 sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
 {
-       struct pt_regs *thread_regs;
+       struct thread_info *ti;
        int regno;
 
        /* Just making sure... */
@@ -86,24 +86,17 @@ sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
                gdb_regs[regno] = 0;
 
        /* Otherwise, we have only some registers from switch_to() */
-       thread_regs             = task_pt_regs(task);
-       gdb_regs[_R0]           = thread_regs->ARM_r0;
-       gdb_regs[_R1]           = thread_regs->ARM_r1;
-       gdb_regs[_R2]           = thread_regs->ARM_r2;
-       gdb_regs[_R3]           = thread_regs->ARM_r3;
-       gdb_regs[_R4]           = thread_regs->ARM_r4;
-       gdb_regs[_R5]           = thread_regs->ARM_r5;
-       gdb_regs[_R6]           = thread_regs->ARM_r6;
-       gdb_regs[_R7]           = thread_regs->ARM_r7;
-       gdb_regs[_R8]           = thread_regs->ARM_r8;
-       gdb_regs[_R9]           = thread_regs->ARM_r9;
-       gdb_regs[_R10]          = thread_regs->ARM_r10;
-       gdb_regs[_FP]           = thread_regs->ARM_fp;
-       gdb_regs[_IP]           = thread_regs->ARM_ip;
-       gdb_regs[_SPT]          = thread_regs->ARM_sp;
-       gdb_regs[_LR]           = thread_regs->ARM_lr;
-       gdb_regs[_PC]           = thread_regs->ARM_pc;
-       gdb_regs[_CPSR]         = thread_regs->ARM_cpsr;
+       ti                      = task_thread_info(task);
+       gdb_regs[_R4]           = ti->cpu_context.r4;
+       gdb_regs[_R5]           = ti->cpu_context.r5;
+       gdb_regs[_R6]           = ti->cpu_context.r6;
+       gdb_regs[_R7]           = ti->cpu_context.r7;
+       gdb_regs[_R8]           = ti->cpu_context.r8;
+       gdb_regs[_R9]           = ti->cpu_context.r9;
+       gdb_regs[_R10]          = ti->cpu_context.sl;
+       gdb_regs[_FP]           = ti->cpu_context.fp;
+       gdb_regs[_SPT]          = ti->cpu_context.sp;
+       gdb_regs[_PC]           = ti->cpu_context.pc;
 }
 
 void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
index 48185a773852d4ec501702ae3a6471d38b03ce10..b26361355daeb39b61c238333c20dd2d68ec4a61 100644 (file)
@@ -80,7 +80,7 @@ static DECLARE_COMPLETION(cpu_running);
 
 static struct smp_operations smp_ops;
 
-void __init smp_set_ops(struct smp_operations *ops)
+void __init smp_set_ops(const struct smp_operations *ops)
 {
        if (ops)
                smp_ops = *ops;
@@ -400,6 +400,7 @@ asmlinkage void secondary_start_kernel(void)
 
        local_irq_enable();
        local_fiq_enable();
+       local_abt_enable();
 
        /*
         * OK, it's off to the idle thread for us
@@ -748,6 +749,15 @@ core_initcall(register_cpufreq_notifier);
 
 static void raise_nmi(cpumask_t *mask)
 {
+       /*
+        * Generate the backtrace directly if we are running in a calling
+        * context that is not preemptible by the backtrace IPI. Note
+        * that nmi_cpu_backtrace() automatically removes the current cpu
+        * from mask.
+        */
+       if (cpumask_test_cpu(smp_processor_id(), mask) && irqs_disabled())
+               nmi_cpu_backtrace(NULL);
+
        smp_cross_call(mask, IPI_CPU_BACKTRACE);
 }
 
index e9035cda148563e9231ccf184d141930990f6662..1bfa7a7f55336119bfb647eb1e419fe81c086a05 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/of_irq.h>
 #include <linux/of_address.h>
 
-#include <asm/smp_plat.h>
 #include <asm/smp_twd.h>
 
 /* set up by the platform code */
@@ -34,6 +33,8 @@ static unsigned long twd_timer_rate;
 static DEFINE_PER_CPU(bool, percpu_setup_called);
 
 static struct clock_event_device __percpu *twd_evt;
+static unsigned int twd_features =
+               CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
 static int twd_ppi;
 
 static int twd_shutdown(struct clock_event_device *clk)
@@ -294,8 +295,7 @@ static void twd_timer_setup(void)
        writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
 
        clk->name = "local_timer";
-       clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
-                       CLOCK_EVT_FEAT_C3STOP;
+       clk->features = twd_features;
        clk->rating = 350;
        clk->set_state_shutdown = twd_shutdown;
        clk->set_state_periodic = twd_set_periodic;
@@ -350,6 +350,8 @@ static int __init twd_local_timer_common_register(struct device_node *np)
                goto out_irq;
 
        twd_get_clock(np);
+       if (!of_property_read_bool(np, "always-on"))
+               twd_features |= CLOCK_EVT_FEAT_C3STOP;
 
        /*
         * Immediately configure the timer on the boot CPU, unless we need
@@ -392,9 +394,6 @@ static void __init twd_local_timer_of_register(struct device_node *np)
 {
        int err;
 
-       if (!is_smp() || !setup_max_cpus)
-               return;
-
        twd_ppi = irq_of_parse_and_map(np, 0);
        if (!twd_ppi) {
                err = -EINVAL;
index 969f9d9e665f4d49b2951ed65cfde9c279e1aa55..bc698383e82253a47427885359e07e22daa24179 100644 (file)
@@ -625,58 +625,6 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
                set_tls(regs->ARM_r0);
                return 0;
 
-#ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG
-       /*
-        * Atomically store r1 in *r2 if *r2 is equal to r0 for user space.
-        * Return zero in r0 if *MEM was changed or non-zero if no exchange
-        * happened.  Also set the user C flag accordingly.
-        * If access permissions have to be fixed up then non-zero is
-        * returned and the operation has to be re-attempted.
-        *
-        * *NOTE*: This is a ghost syscall private to the kernel.  Only the
-        * __kuser_cmpxchg code in entry-armv.S should be aware of its
-        * existence.  Don't ever use this from user code.
-        */
-       case NR(cmpxchg):
-       for (;;) {
-               extern void do_DataAbort(unsigned long addr, unsigned int fsr,
-                                        struct pt_regs *regs);
-               unsigned long val;
-               unsigned long addr = regs->ARM_r2;
-               struct mm_struct *mm = current->mm;
-               pgd_t *pgd; pmd_t *pmd; pte_t *pte;
-               spinlock_t *ptl;
-
-               regs->ARM_cpsr &= ~PSR_C_BIT;
-               down_read(&mm->mmap_sem);
-               pgd = pgd_offset(mm, addr);
-               if (!pgd_present(*pgd))
-                       goto bad_access;
-               pmd = pmd_offset(pgd, addr);
-               if (!pmd_present(*pmd))
-                       goto bad_access;
-               pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
-               if (!pte_present(*pte) || !pte_write(*pte) || !pte_dirty(*pte)) {
-                       pte_unmap_unlock(pte, ptl);
-                       goto bad_access;
-               }
-               val = *(unsigned long *)addr;
-               val -= regs->ARM_r0;
-               if (val == 0) {
-                       *(unsigned long *)addr = regs->ARM_r1;
-                       regs->ARM_cpsr |= PSR_C_BIT;
-               }
-               pte_unmap_unlock(pte, ptl);
-               up_read(&mm->mmap_sem);
-               return val;
-
-               bad_access:
-               up_read(&mm->mmap_sem);
-               /* simulate a write access fault */
-               do_DataAbort(addr, 15 + (1 << 11), regs);
-       }
-#endif
-
        default:
                /* Calls 9f00xx..9f07ff are defined to return -ENOSYS
                   if not implemented, rather than raising SIGILL.  This
index 970d6c0437743cda6a78620e1439eccb91398da2..e936352ccb0013e040fcd9b22bda1c583cfff361 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/unwind.h>
 
                .text
 
@@ -20,6 +21,8 @@
  */
 ENTRY(__clear_user_std)
 WEAK(arm_clear_user)
+UNWIND(.fnstart)
+UNWIND(.save {r1, lr})
                stmfd   sp!, {r1, lr}
                mov     r2, #0
                cmp     r1, #4
@@ -44,6 +47,7 @@ WEAK(arm_clear_user)
 USER(          strnebt r2, [r0])
                mov     r0, #0
                ldmfd   sp!, {r1, pc}
+UNWIND(.fnend)
 ENDPROC(arm_clear_user)
 ENDPROC(__clear_user_std)
 
index e00eb39453a41ff3cf0090a6db53ee80a2afc7dc..5a7e47ceec91f7a6486821cc0a4b93e7d51257a9 100644 (file)
@@ -177,54 +177,57 @@ static struct irq_chip exynos_pmu_chip = {
 #endif
 };
 
-static int exynos_pmu_domain_xlate(struct irq_domain *domain,
-                                  struct device_node *controller,
-                                  const u32 *intspec,
-                                  unsigned int intsize,
-                                  unsigned long *out_hwirq,
-                                  unsigned int *out_type)
+static int exynos_pmu_domain_translate(struct irq_domain *d,
+                                      struct irq_fwspec *fwspec,
+                                      unsigned long *hwirq,
+                                      unsigned int *type)
 {
-       if (domain->of_node != controller)
-               return -EINVAL; /* Shouldn't happen, really... */
-       if (intsize != 3)
-               return -EINVAL; /* Not GIC compliant */
-       if (intspec[0] != 0)
-               return -EINVAL; /* No PPI should point to this domain */
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
+
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static int exynos_pmu_domain_alloc(struct irq_domain *domain,
                                   unsigned int virq,
                                   unsigned int nr_irqs, void *data)
 {
-       struct of_phandle_args *args = data;
-       struct of_phandle_args parent_args;
+       struct irq_fwspec *fwspec = data;
+       struct irq_fwspec parent_fwspec;
        irq_hw_number_t hwirq;
        int i;
 
-       if (args->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL; /* Not GIC compliant */
-       if (args->args[0] != 0)
+       if (fwspec->param[0] != 0)
                return -EINVAL; /* No PPI should point to this domain */
 
-       hwirq = args->args[1];
+       hwirq = fwspec->param[1];
 
        for (i = 0; i < nr_irqs; i++)
                irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
                                              &exynos_pmu_chip, NULL);
 
-       parent_args = *args;
-       parent_args.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args);
+       parent_fwspec = *fwspec;
+       parent_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+                                           &parent_fwspec);
 }
 
 static const struct irq_domain_ops exynos_pmu_domain_ops = {
-       .xlate  = exynos_pmu_domain_xlate,
-       .alloc  = exynos_pmu_domain_alloc,
-       .free   = irq_domain_free_irqs_common,
+       .translate      = exynos_pmu_domain_translate,
+       .alloc          = exynos_pmu_domain_alloc,
+       .free           = irq_domain_free_irqs_common,
 };
 
 static int __init exynos_pmu_irq_init(struct device_node *node,
index ca8a25bb35217404247bc9163529457289187bdf..18b12796acf95f74ca90e1f9e80f31dd325a3065 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/leds.h>
 #include <linux/input.h>
 #include <linux/gpio_keys.h>
-#include <linux/mdio-gpio.h>
 #include <linux/io.h>
 
 #include <asm/setup.h>
index 418188cd1712fe7f6247a8b97fb423e358f4891d..14c56f3f0ec225032a067ad46c08713734a605fa 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/input.h>
 #include <linux/skbuff.h>
 #include <linux/gpio_keys.h>
-#include <linux/mdio-gpio.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <asm/mach-types.h>
index 266b265090cd222034bb6cfd3c1feb64f05ed058..6070282ce24308c100cdfcb30a181283cf0aafa9 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/input.h>
 #include <linux/skbuff.h>
 #include <linux/gpio_keys.h>
-#include <linux/mdio-gpio.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <asm/mach-types.h>
index 8c4467fad8370c73374ff104209b779e580852e1..10bf7159b27def3adf90403541fed2214b6bac4e 100644 (file)
@@ -181,40 +181,42 @@ static struct irq_chip imx_gpc_chip = {
 #endif
 };
 
-static int imx_gpc_domain_xlate(struct irq_domain *domain,
-                               struct device_node *controller,
-                               const u32 *intspec,
-                               unsigned int intsize,
-                               unsigned long *out_hwirq,
-                               unsigned int *out_type)
+static int imx_gpc_domain_translate(struct irq_domain *d,
+                                   struct irq_fwspec *fwspec,
+                                   unsigned long *hwirq,
+                                   unsigned int *type)
 {
-       if (domain->of_node != controller)
-               return -EINVAL; /* Shouldn't happen, really... */
-       if (intsize != 3)
-               return -EINVAL; /* Not GIC compliant */
-       if (intspec[0] != 0)
-               return -EINVAL; /* No PPI should point to this domain */
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
+
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static int imx_gpc_domain_alloc(struct irq_domain *domain,
                                  unsigned int irq,
                                  unsigned int nr_irqs, void *data)
 {
-       struct of_phandle_args *args = data;
-       struct of_phandle_args parent_args;
+       struct irq_fwspec *fwspec = data;
+       struct irq_fwspec parent_fwspec;
        irq_hw_number_t hwirq;
        int i;
 
-       if (args->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL; /* Not GIC compliant */
-       if (args->args[0] != 0)
+       if (fwspec->param[0] != 0)
                return -EINVAL; /* No PPI should point to this domain */
 
-       hwirq = args->args[1];
+       hwirq = fwspec->param[1];
        if (hwirq >= GPC_MAX_IRQS)
                return -EINVAL; /* Can't deal with this */
 
@@ -222,15 +224,16 @@ static int imx_gpc_domain_alloc(struct irq_domain *domain,
                irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i,
                                              &imx_gpc_chip, NULL);
 
-       parent_args = *args;
-       parent_args.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs, &parent_args);
+       parent_fwspec = *fwspec;
+       parent_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,
+                                           &parent_fwspec);
 }
 
 static const struct irq_domain_ops imx_gpc_domain_ops = {
-       .xlate  = imx_gpc_domain_xlate,
-       .alloc  = imx_gpc_domain_alloc,
-       .free   = irq_domain_free_irqs_common,
+       .translate      = imx_gpc_domain_translate,
+       .alloc          = imx_gpc_domain_alloc,
+       .free           = irq_domain_free_irqs_common,
 };
 
 static int __init imx_gpc_init(struct device_node *node,
index c86a5a0aefac7bad66c45b94157cdb6c2b8e8b28..e20fc4178b15b029f098690f0bc9420be170c141 100644 (file)
@@ -117,11 +117,4 @@ config MACH_KIRKWOOD
          Say 'Y' here if you want your kernel to support boards based
          on the Marvell Kirkwood device tree.
 
-config MACH_NETXBIG
-       bool "LaCie 2Big and 5Big Network v2"
-       depends on MACH_KIRKWOOD
-       help
-         Say 'Y' here if you want your kernel to support the
-         LaCie 2Big and 5Big Network v2
-
 endif
index b4f01497ce0bba1db1bf865b8a850714feca3082..ecf9e0c3b107808752fe49af062b8758e1784986 100644 (file)
@@ -13,4 +13,3 @@ endif
 
 obj-$(CONFIG_MACH_DOVE)                 += dove.o
 obj-$(CONFIG_MACH_KIRKWOOD)     += kirkwood.o kirkwood-pm.o
-obj-$(CONFIG_MACH_NETXBIG)      += netxbig.o
diff --git a/arch/arm/mach-mvebu/board.h b/arch/arm/mach-mvebu/board.h
deleted file mode 100644 (file)
index 98e32cc..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Board functions for Marvell System On Chip
- *
- * Copyright (C) 2014
- *
- * Andrew Lunn <andrew@lunn.ch>
- *
- * 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.
- */
-
-#ifndef __ARCH_MVEBU_BOARD_H
-#define __ARCH_MVEBU_BOARD_H
-
-#ifdef CONFIG_MACH_NETXBIG
-void netxbig_init(void);
-#else
-static inline void netxbig_init(void) {};
-#endif
-#endif
index 925f75f54268ee5d4b5b9093ad48b6b1e290ad0a..f9d8e1ea7183a510cb3920056d990407d3ffb0ac 100644 (file)
@@ -25,7 +25,6 @@
 #include "kirkwood.h"
 #include "kirkwood-pm.h"
 #include "common.h"
-#include "board.h"
 
 static struct resource kirkwood_cpufreq_resources[] = {
        [0] = {
@@ -180,9 +179,6 @@ static void __init kirkwood_dt_init(void)
        kirkwood_pm_init();
        kirkwood_dt_eth_fixup();
 
-       if (of_machine_is_compatible("lacie,netxbig"))
-               netxbig_init();
-
        of_platform_populate(NULL, of_default_bus_match_table, auxdata, NULL);
 }
 
diff --git a/arch/arm/mach-mvebu/netxbig.c b/arch/arm/mach-mvebu/netxbig.c
deleted file mode 100644 (file)
index 94b11b6..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * arch/arm/mach-mvbu/board-netxbig.c
- *
- * LaCie 2Big and 5Big Network v2 board setup
- *
- * Copyright (C) 2010 Simon Guinot <sguinot@lacie.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
- * 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/kernel.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/leds-kirkwood-netxbig.h>
-#include "common.h"
-
-/*****************************************************************************
- * GPIO extension LEDs
- ****************************************************************************/
-
-/*
- * The LEDs are controlled by a CPLD and can be configured through a GPIO
- * extension bus:
- *
- * - address register : bit [0-2] -> GPIO [47-49]
- * - data register    : bit [0-2] -> GPIO [44-46]
- * - enable register  : GPIO 29
- */
-
-static int netxbig_v2_gpio_ext_addr[] = { 47, 48, 49 };
-static int netxbig_v2_gpio_ext_data[] = { 44, 45, 46 };
-
-static struct netxbig_gpio_ext netxbig_v2_gpio_ext = {
-       .addr           = netxbig_v2_gpio_ext_addr,
-       .num_addr       = ARRAY_SIZE(netxbig_v2_gpio_ext_addr),
-       .data           = netxbig_v2_gpio_ext_data,
-       .num_data       = ARRAY_SIZE(netxbig_v2_gpio_ext_data),
-       .enable         = 29,
-};
-
-/*
- * Address register selection:
- *
- * addr | register
- * ----------------------------
- *   0  | front LED
- *   1  | front LED brightness
- *   2  | SATA LED brightness
- *   3  | SATA0 LED
- *   4  | SATA1 LED
- *   5  | SATA2 LED
- *   6  | SATA3 LED
- *   7  | SATA4 LED
- *
- * Data register configuration:
- *
- * data | LED brightness
- * -------------------------------------------------
- *   0  | min (off)
- *   -  | -
- *   7  | max
- *
- * data | front LED mode
- * -------------------------------------------------
- *   0  | fix off
- *   1  | fix blue on
- *   2  | fix red on
- *   3  | blink blue on=1 sec and blue off=1 sec
- *   4  | blink red on=1 sec and red off=1 sec
- *   5  | blink blue on=2.5 sec and red on=0.5 sec
- *   6  | blink blue on=1 sec and red on=1 sec
- *   7  | blink blue on=0.5 sec and blue off=2.5 sec
- *
- * data | SATA LED mode
- * -------------------------------------------------
- *   0  | fix off
- *   1  | SATA activity blink
- *   2  | fix red on
- *   3  | blink blue on=1 sec and blue off=1 sec
- *   4  | blink red on=1 sec and red off=1 sec
- *   5  | blink blue on=2.5 sec and red on=0.5 sec
- *   6  | blink blue on=1 sec and red on=1 sec
- *   7  | fix blue on
- */
-
-static int netxbig_v2_red_mled[NETXBIG_LED_MODE_NUM] = {
-       [NETXBIG_LED_OFF]       = 0,
-       [NETXBIG_LED_ON]        = 2,
-       [NETXBIG_LED_SATA]      = NETXBIG_LED_INVALID_MODE,
-       [NETXBIG_LED_TIMER1]    = 4,
-       [NETXBIG_LED_TIMER2]    = NETXBIG_LED_INVALID_MODE,
-};
-
-static int netxbig_v2_blue_pwr_mled[NETXBIG_LED_MODE_NUM] = {
-       [NETXBIG_LED_OFF]       = 0,
-       [NETXBIG_LED_ON]        = 1,
-       [NETXBIG_LED_SATA]      = NETXBIG_LED_INVALID_MODE,
-       [NETXBIG_LED_TIMER1]    = 3,
-       [NETXBIG_LED_TIMER2]    = 7,
-};
-
-static int netxbig_v2_blue_sata_mled[NETXBIG_LED_MODE_NUM] = {
-       [NETXBIG_LED_OFF]       = 0,
-       [NETXBIG_LED_ON]        = 7,
-       [NETXBIG_LED_SATA]      = 1,
-       [NETXBIG_LED_TIMER1]    = 3,
-       [NETXBIG_LED_TIMER2]    = NETXBIG_LED_INVALID_MODE,
-};
-
-static struct netxbig_led_timer netxbig_v2_led_timer[] = {
-       [0] = {
-               .delay_on       = 500,
-               .delay_off      = 500,
-               .mode           = NETXBIG_LED_TIMER1,
-       },
-       [1] = {
-               .delay_on       = 500,
-               .delay_off      = 1000,
-               .mode           = NETXBIG_LED_TIMER2,
-       },
-};
-
-#define NETXBIG_LED(_name, maddr, mval, baddr)                 \
-       { .name         = _name,                                \
-         .mode_addr    = maddr,                                \
-         .mode_val     = mval,                                 \
-         .bright_addr  = baddr }
-
-static struct netxbig_led net2big_v2_leds_ctrl[] = {
-       NETXBIG_LED("net2big-v2:blue:power", 0, netxbig_v2_blue_pwr_mled,  1),
-       NETXBIG_LED("net2big-v2:red:power",  0, netxbig_v2_red_mled,       1),
-       NETXBIG_LED("net2big-v2:blue:sata0", 3, netxbig_v2_blue_sata_mled, 2),
-       NETXBIG_LED("net2big-v2:red:sata0",  3, netxbig_v2_red_mled,       2),
-       NETXBIG_LED("net2big-v2:blue:sata1", 4, netxbig_v2_blue_sata_mled, 2),
-       NETXBIG_LED("net2big-v2:red:sata1",  4, netxbig_v2_red_mled,       2),
-};
-
-static struct netxbig_led_platform_data net2big_v2_leds_data = {
-       .gpio_ext       = &netxbig_v2_gpio_ext,
-       .timer          = netxbig_v2_led_timer,
-       .num_timer      = ARRAY_SIZE(netxbig_v2_led_timer),
-       .leds           = net2big_v2_leds_ctrl,
-       .num_leds       = ARRAY_SIZE(net2big_v2_leds_ctrl),
-};
-
-static struct netxbig_led net5big_v2_leds_ctrl[] = {
-       NETXBIG_LED("net5big-v2:blue:power", 0, netxbig_v2_blue_pwr_mled,  1),
-       NETXBIG_LED("net5big-v2:red:power",  0, netxbig_v2_red_mled,       1),
-       NETXBIG_LED("net5big-v2:blue:sata0", 3, netxbig_v2_blue_sata_mled, 2),
-       NETXBIG_LED("net5big-v2:red:sata0",  3, netxbig_v2_red_mled,       2),
-       NETXBIG_LED("net5big-v2:blue:sata1", 4, netxbig_v2_blue_sata_mled, 2),
-       NETXBIG_LED("net5big-v2:red:sata1",  4, netxbig_v2_red_mled,       2),
-       NETXBIG_LED("net5big-v2:blue:sata2", 5, netxbig_v2_blue_sata_mled, 2),
-       NETXBIG_LED("net5big-v2:red:sata2",  5, netxbig_v2_red_mled,       2),
-       NETXBIG_LED("net5big-v2:blue:sata3", 6, netxbig_v2_blue_sata_mled, 2),
-       NETXBIG_LED("net5big-v2:red:sata3",  6, netxbig_v2_red_mled,       2),
-       NETXBIG_LED("net5big-v2:blue:sata4", 7, netxbig_v2_blue_sata_mled, 2),
-       NETXBIG_LED("net5big-v2:red:sata4",  7, netxbig_v2_red_mled,       2),
-};
-
-static struct netxbig_led_platform_data net5big_v2_leds_data = {
-       .gpio_ext       = &netxbig_v2_gpio_ext,
-       .timer          = netxbig_v2_led_timer,
-       .num_timer      = ARRAY_SIZE(netxbig_v2_led_timer),
-       .leds           = net5big_v2_leds_ctrl,
-       .num_leds       = ARRAY_SIZE(net5big_v2_leds_ctrl),
-};
-
-static struct platform_device netxbig_v2_leds = {
-       .name           = "leds-netxbig",
-       .id             = -1,
-       .dev            = {
-               .platform_data  = &net2big_v2_leds_data,
-       },
-};
-
-void __init netxbig_init(void)
-{
-
-       if (of_machine_is_compatible("lacie,net5big_v2"))
-               netxbig_v2_leds.dev.platform_data = &net5big_v2_leds_data;
-       platform_device_register(&netxbig_v2_leds);
-}
index e1d2e991d17a31fc15f1c44616c9b4b7f9de3a84..db7e0bab3587cb975ea1ecb5c89e10956fcc7214 100644 (file)
@@ -399,40 +399,42 @@ static struct irq_chip wakeupgen_chip = {
 #endif
 };
 
-static int wakeupgen_domain_xlate(struct irq_domain *domain,
-                                 struct device_node *controller,
-                                 const u32 *intspec,
-                                 unsigned int intsize,
-                                 unsigned long *out_hwirq,
-                                 unsigned int *out_type)
+static int wakeupgen_domain_translate(struct irq_domain *d,
+                                     struct irq_fwspec *fwspec,
+                                     unsigned long *hwirq,
+                                     unsigned int *type)
 {
-       if (domain->of_node != controller)
-               return -EINVAL; /* Shouldn't happen, really... */
-       if (intsize != 3)
-               return -EINVAL; /* Not GIC compliant */
-       if (intspec[0] != 0)
-               return -EINVAL; /* No PPI should point to this domain */
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
+
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static int wakeupgen_domain_alloc(struct irq_domain *domain,
                                  unsigned int virq,
                                  unsigned int nr_irqs, void *data)
 {
-       struct of_phandle_args *args = data;
-       struct of_phandle_args parent_args;
+       struct irq_fwspec *fwspec = data;
+       struct irq_fwspec parent_fwspec;
        irq_hw_number_t hwirq;
        int i;
 
-       if (args->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL; /* Not GIC compliant */
-       if (args->args[0] != 0)
+       if (fwspec->param[0] != 0)
                return -EINVAL; /* No PPI should point to this domain */
 
-       hwirq = args->args[1];
+       hwirq = fwspec->param[1];
        if (hwirq >= MAX_IRQS)
                return -EINVAL; /* Can't deal with this */
 
@@ -440,15 +442,16 @@ static int wakeupgen_domain_alloc(struct irq_domain *domain,
                irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
                                              &wakeupgen_chip, NULL);
 
-       parent_args = *args;
-       parent_args.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args);
+       parent_fwspec = *fwspec;
+       parent_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+                                           &parent_fwspec);
 }
 
 static const struct irq_domain_ops wakeupgen_domain_ops = {
-       .xlate  = wakeupgen_domain_xlate,
-       .alloc  = wakeupgen_domain_alloc,
-       .free   = irq_domain_free_irqs_common,
+       .translate      = wakeupgen_domain_translate,
+       .alloc          = wakeupgen_domain_alloc,
+       .free           = irq_domain_free_irqs_common,
 };
 
 /*
index df7537f12469a15669b89aba42bd96445e789305..c21941349b3ef761680d4b2c9b58ee7cf4008f96 100644 (file)
@@ -419,28 +419,24 @@ config CPU_THUMBONLY
 config CPU_32v3
        bool
        select CPU_USE_DOMAINS if MMU
-       select NEEDS_SYSCALL_FOR_CMPXCHG if SMP
        select NEED_KUSER_HELPERS
        select TLS_REG_EMUL if SMP || !MMU
 
 config CPU_32v4
        bool
        select CPU_USE_DOMAINS if MMU
-       select NEEDS_SYSCALL_FOR_CMPXCHG if SMP
        select NEED_KUSER_HELPERS
        select TLS_REG_EMUL if SMP || !MMU
 
 config CPU_32v4T
        bool
        select CPU_USE_DOMAINS if MMU
-       select NEEDS_SYSCALL_FOR_CMPXCHG if SMP
        select NEED_KUSER_HELPERS
        select TLS_REG_EMUL if SMP || !MMU
 
 config CPU_32v5
        bool
        select CPU_USE_DOMAINS if MMU
-       select NEEDS_SYSCALL_FOR_CMPXCHG if SMP
        select NEED_KUSER_HELPERS
        select TLS_REG_EMUL if SMP || !MMU
 
@@ -805,14 +801,6 @@ config TLS_REG_EMUL
          a few prototypes like that in existence) and therefore access to
          that required register must be emulated.
 
-config NEEDS_SYSCALL_FOR_CMPXCHG
-       bool
-       select NEED_KUSER_HELPERS
-       help
-         SMP on a pre-ARMv6 processor?  Well OK then.
-         Forget about fast user space cmpxchg support.
-         It is just not possible.
-
 config NEED_KUSER_HELPERS
        bool
 
index 1a7815e5421b6b1d3ad774b28915b84b52726136..ad4eb2d26e1697fc6a16f47a8805e532e198a693 100644 (file)
@@ -1407,12 +1407,19 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
        unsigned long uaddr = vma->vm_start;
        unsigned long usize = vma->vm_end - vma->vm_start;
        struct page **pages = __iommu_get_pages(cpu_addr, attrs);
+       unsigned long nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
+       unsigned long off = vma->vm_pgoff;
 
        vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot);
 
        if (!pages)
                return -ENXIO;
 
+       if (off >= nr_pages || (usize >> PAGE_SHIFT) > nr_pages - off)
+               return -ENXIO;
+
+       pages += off;
+
        do {
                int ret = vm_insert_page(vma, uaddr, *pages++);
                if (ret) {
index 0d629b8f973fc2ca63aacb59e5baaf718b194543..daafcf121ce082aa0a0fbb43be3e3712b2942f3e 100644 (file)
@@ -593,6 +593,28 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
        arm_notify_die("", regs, &info, ifsr, 0);
 }
 
+/*
+ * Abort handler to be used only during first unmasking of asynchronous aborts
+ * on the boot CPU. This makes sure that the machine will not die if the
+ * firmware/bootloader left an imprecise abort pending for us to trip over.
+ */
+static int __init early_abort_handler(unsigned long addr, unsigned int fsr,
+                                     struct pt_regs *regs)
+{
+       pr_warn("Hit pending asynchronous external abort (FSR=0x%08x) during "
+               "first unmask, this is most likely caused by a "
+               "firmware/bootloader bug.\n", fsr);
+
+       return 0;
+}
+
+void __init early_abt_enable(void)
+{
+       fsr_info[22].fn = early_abort_handler;
+       local_abt_enable();
+       fsr_info[22].fn = do_bad;
+}
+
 #ifndef CONFIG_ARM_LPAE
 static int __init exceptions_init(void)
 {
index cf08bdfbe0d6b6168970e7962341dd407c2823fd..05ec5e0df32d7bad17f4564a74ad3fcbed88ee9c 100644 (file)
@@ -24,5 +24,6 @@ static inline int fsr_fs(unsigned int fsr)
 
 void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
 unsigned long search_exception_table(unsigned long addr);
+void early_abt_enable(void);
 
 #endif /* __ARCH_ARM_FAULT_H */
index 7cd15143a507740155ad6dbe01e3dbef371111fe..4867f5daf82c99bdf4ee64ebb6b61613cd792a3e 100644 (file)
@@ -38,6 +38,7 @@
 #include <asm/mach/pci.h>
 #include <asm/fixmap.h>
 
+#include "fault.h"
 #include "mm.h"
 #include "tcm.h"
 
@@ -1363,6 +1364,9 @@ static void __init devicemaps_init(const struct machine_desc *mdesc)
         */
        local_flush_tlb_all();
        flush_cache_all();
+
+       /* Enable asynchronous aborts */
+       early_abt_enable();
 }
 
 static void __init kmap_init(void)
index b8efb8cd1f73ee1cb7de196ae88d9173203c4d2a..2f4b14cfddb4762e88818464dcd6c57d5eff2a5c 100644 (file)
@@ -125,7 +125,7 @@ static u64 jit_get_skb_w(struct sk_buff *skb, int offset)
 }
 
 /*
- * Wrapper that handles both OABI and EABI and assures Thumb2 interworking
+ * Wrappers which handle both OABI and EABI and assures Thumb2 interworking
  * (where the assembly routines like __aeabi_uidiv could cause problems).
  */
 static u32 jit_udiv(u32 dividend, u32 divisor)
@@ -133,6 +133,11 @@ static u32 jit_udiv(u32 dividend, u32 divisor)
        return dividend / divisor;
 }
 
+static u32 jit_mod(u32 dividend, u32 divisor)
+{
+       return dividend % divisor;
+}
+
 static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx)
 {
        inst |= (cond << 28);
@@ -471,11 +476,17 @@ static inline void emit_blx_r(u8 tgt_reg, struct jit_ctx *ctx)
 #endif
 }
 
-static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx)
+static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx,
+                               int bpf_op)
 {
 #if __LINUX_ARM_ARCH__ == 7
        if (elf_hwcap & HWCAP_IDIVA) {
-               emit(ARM_UDIV(rd, rm, rn), ctx);
+               if (bpf_op == BPF_DIV)
+                       emit(ARM_UDIV(rd, rm, rn), ctx);
+               else {
+                       emit(ARM_UDIV(ARM_R3, rm, rn), ctx);
+                       emit(ARM_MLS(rd, rn, ARM_R3, rm), ctx);
+               }
                return;
        }
 #endif
@@ -496,7 +507,8 @@ static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx)
                emit(ARM_MOV_R(ARM_R0, rm), ctx);
 
        ctx->seen |= SEEN_CALL;
-       emit_mov_i(ARM_R3, (u32)jit_udiv, ctx);
+       emit_mov_i(ARM_R3, bpf_op == BPF_DIV ? (u32)jit_udiv : (u32)jit_mod,
+                  ctx);
        emit_blx_r(ARM_R3, ctx);
 
        if (rd != ARM_R0)
@@ -698,13 +710,27 @@ load_ind:
                        if (k == 1)
                                break;
                        emit_mov_i(r_scratch, k, ctx);
-                       emit_udiv(r_A, r_A, r_scratch, ctx);
+                       emit_udivmod(r_A, r_A, r_scratch, ctx, BPF_DIV);
                        break;
                case BPF_ALU | BPF_DIV | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_CMP_I(r_X, 0), ctx);
                        emit_err_ret(ARM_COND_EQ, ctx);
-                       emit_udiv(r_A, r_A, r_X, ctx);
+                       emit_udivmod(r_A, r_A, r_X, ctx, BPF_DIV);
+                       break;
+               case BPF_ALU | BPF_MOD | BPF_K:
+                       if (k == 1) {
+                               emit_mov_i(r_A, 0, ctx);
+                               break;
+                       }
+                       emit_mov_i(r_scratch, k, ctx);
+                       emit_udivmod(r_A, r_A, r_scratch, ctx, BPF_MOD);
+                       break;
+               case BPF_ALU | BPF_MOD | BPF_X:
+                       update_on_xread(ctx);
+                       emit(ARM_CMP_I(r_X, 0), ctx);
+                       emit_err_ret(ARM_COND_EQ, ctx);
+                       emit_udivmod(r_A, r_A, r_X, ctx, BPF_MOD);
                        break;
                case BPF_ALU | BPF_OR | BPF_K:
                        /* A |= K */
@@ -1048,7 +1074,7 @@ void bpf_jit_compile(struct bpf_prog *fp)
 
        set_memory_ro((unsigned long)header, header->pages);
        fp->bpf_func = (void *)ctx.target;
-       fp->jited = true;
+       fp->jited = 1;
 out:
        kfree(ctx.offsets);
        return;
index 4b17d5ab652a4029e7cc73165075a75068e0ef6c..c46fca2972f750ad434d202716a67929bd73f4c4 100644 (file)
 
 #define ARM_INST_UMULL         0x00800090
 
+#define ARM_INST_MLS           0x00600090
+
 /*
  * Use a suitable undefined instruction to use for ARM/Thumb2 faulting.
  * We need to be careful not to conflict with those used by other modules
 #define ARM_UMULL(rd_lo, rd_hi, rn, rm)        (ARM_INST_UMULL | (rd_hi) << 16 \
                                         | (rd_lo) << 12 | (rm) << 8 | rn)
 
+#define ARM_MLS(rd, rn, rm, ra)        (ARM_INST_MLS | (rd) << 16 | (rn) | (rm) << 8 \
+                                | (ra) << 12)
+
 #endif /* PFILTER_OPCODES_ARM_H */
index aedec81d11988181e3e6f0631113fd01e4737ac8..f6455273b2f8768a303db0ccc4a4e875df08eb71 100644 (file)
@@ -45,7 +45,6 @@
  * it does.
  */
 
-#include <byteswap.h>
 #include <elf.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <unistd.h>
 
+#define swab16(x) \
+       ((((x) & 0x00ff) << 8) | \
+        (((x) & 0xff00) >> 8))
+
+#define swab32(x) \
+       ((((x) & 0x000000ff) << 24) | \
+        (((x) & 0x0000ff00) <<  8) | \
+        (((x) & 0x00ff0000) >>  8) | \
+        (((x) & 0xff000000) >> 24))
+
 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
 #define HOST_ORDER ELFDATA2LSB
 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
@@ -104,17 +113,17 @@ static void cleanup(void)
 
 static Elf32_Word read_elf_word(Elf32_Word word, bool swap)
 {
-       return swap ? bswap_32(word) : word;
+       return swap ? swab32(word) : word;
 }
 
 static Elf32_Half read_elf_half(Elf32_Half half, bool swap)
 {
-       return swap ? bswap_16(half) : half;
+       return swap ? swab16(half) : half;
 }
 
 static void write_elf_word(Elf32_Word val, Elf32_Word *dst, bool swap)
 {
-       *dst = swap ? bswap_32(val) : val;
+       *dst = swap ? swab32(val) : val;
 }
 
 int main(int argc, char **argv)
index 07d1811aa03fcd1ecd5ee7c260688a59f7ab97e4..440d906429deab9c1daf0de497ead020378891af 100644 (file)
@@ -348,6 +348,33 @@ config ARM64_ERRATUM_843419
 
          If unsure, say Y.
 
+config CAVIUM_ERRATUM_22375
+       bool "Cavium erratum 22375, 24313"
+       default y
+       help
+         Enable workaround for erratum 22375, 24313.
+
+         This implements two gicv3-its errata workarounds for ThunderX. Both
+         with small impact affecting only ITS table allocation.
+
+           erratum 22375: only alloc 8MB table size
+           erratum 24313: ignore memory access type
+
+         The fixes are in ITS initialization and basically ignore memory access
+         type and table size provided by the TYPER and BASER registers.
+
+         If unsure, say Y.
+
+config CAVIUM_ERRATUM_23154
+       bool "Cavium erratum 23154: Access to ICC_IAR1_EL1 is not sync'ed"
+       default y
+       help
+         The gicv3 of ThunderX requires a modified version for
+         reading the IAR status to ensure data synchronization
+         (access to icc_iar1_el1 is not sync'ed before and after).
+
+         If unsure, say Y.
+
 endmenu
 
 
index d831bc2ac204b9ab19b98859f135c0386864db35..d6c9630a5c20a817e840e8f5ed8e00c3a25b3c38 100644 (file)
                                clock-output-names = "xge0clk";
                        };
 
+                       xge1clk: xge1clk@1f62c000 {
+                               compatible = "apm,xgene-device-clock";
+                               status = "disabled";
+                               #clock-cells = <1>;
+                               clocks = <&socplldiv2 0>;
+                               reg = <0x0 0x1f62c000 0x0 0x1000>;
+                               reg-names = "csr-reg";
+                               csr-mask = <0x3>;
+                               clock-output-names = "xge1clk";
+                       };
+
                        sataphy1clk: sataphy1clk@1f21c000 {
                                compatible = "apm,xgene-device-clock";
                                #clock-cells = <1>;
                                reg = <0x0 0x7c600000 0x0 0x200000>;
                                pmd-controller = <3>;
                        };
+
+                       edacl3@7e600000 {
+                               compatible = "apm,xgene-edac-l3";
+                               reg = <0x0 0x7e600000 0x0 0x1000>;
+                       };
+
+                       edacsoc@7e930000 {
+                               compatible = "apm,xgene-edac-soc-v1";
+                               reg = <0x0 0x7e930000 0x0 0x1000>;
+                       };
                };
 
                pcie0: pcie@1f2b0000 {
                        phy-connection-type = "xgmii";
                };
 
+               xgenet1: ethernet@1f620000 {
+                       compatible = "apm,xgene1-xgenet";
+                       status = "disabled";
+                       reg = <0x0 0x1f620000 0x0 0xd100>,
+                             <0x0 0x1f600000 0x0 0Xc300>,
+                             <0x0 0x18000000 0x0 0X8000>;
+                       reg-names = "enet_csr", "ring_csr", "ring_cmd";
+                       interrupts = <0x0 0x6C 0x4>,
+                                    <0x0 0x6D 0x4>;
+                       port-id = <1>;
+                       dma-coherent;
+                       clocks = <&xge1clk 0>;
+                       /* mac address will be overwritten by the bootloader */
+                       local-mac-address = [00 00 00 00 00 00];
+                       phy-connection-type = "xgmii";
+               };
+
                rng: rng@10520000 {
                        compatible = "apm,xgene-rng";
                        reg = <0x0 0x10520000 0x0 0x100>;
index 637e046f0e367dd23fbe391dc1542dd97a315504..3c386680357ebc2af6b0b734e26cdb1165194020 100644 (file)
 
                                button@1 {
                                        debounce_interval = <50>;
-                                       wakeup = <1>;
+                                       wakeup-source;
                                        linux,code = <116>;
                                        label = "POWER";
                                        gpios = <&iofpga_gpio0 0 0x4>;
                                };
                                button@2 {
                                        debounce_interval = <50>;
-                                       wakeup = <1>;
+                                       wakeup-source;
                                        linux,code = <102>;
                                        label = "HOME";
                                        gpios = <&iofpga_gpio0 1 0x4>;
                                };
                                button@3 {
                                        debounce_interval = <50>;
-                                       wakeup = <1>;
+                                       wakeup-source;
                                        linux,code = <152>;
                                        label = "RLOCK";
                                        gpios = <&iofpga_gpio0 2 0x4>;
                                };
                                button@4 {
                                        debounce_interval = <50>;
-                                       wakeup = <1>;
+                                       wakeup-source;
                                        linux,code = <115>;
                                        label = "VOL+";
                                        gpios = <&iofpga_gpio0 3 0x4>;
                                };
                                button@5 {
                                        debounce_interval = <50>;
-                                       wakeup = <1>;
+                                       wakeup-source;
                                        linux,code = <114>;
                                        label = "VOL-";
                                        gpios = <&iofpga_gpio0 4 0x4>;
                                };
                                button@6 {
                                        debounce_interval = <50>;
-                                       wakeup = <1>;
+                                       wakeup-source;
                                        linux,code = <99>;
                                        label = "NMI";
                                        gpios = <&iofpga_gpio0 5 0x4>;
diff --git a/arch/arm64/boot/dts/hisilicon/hip05_hns.dtsi b/arch/arm64/boot/dts/hisilicon/hip05_hns.dtsi
new file mode 100644 (file)
index 0000000..606dd5a
--- /dev/null
@@ -0,0 +1,191 @@
+soc0: soc@000000000 {
+       #address-cells = <2>;
+       #size-cells = <2>;
+       device_type = "soc";
+       compatible = "simple-bus";
+       ranges = <0x0 0x0 0x0 0x0 0x1 0x0>;
+       chip-id = <0>;
+
+       soc0_mdio0: mdio@803c0000 {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               compatible = "hisilicon,hns-mdio";
+               reg = <0x0 0x803c0000 0x0 0x10000
+                      0x0 0x80000000 0x0 0x10000>;
+
+               soc0_phy0: ethernet-phy@0 {
+                       reg = <0x0>;
+                       compatible = "ethernet-phy-ieee802.3-c22";
+               };
+               soc0_phy1: ethernet-phy@1 {
+                       reg = <0x1>;
+                       compatible = "ethernet-phy-ieee802.3-c22";
+               };
+       };
+
+       dsa: dsa@c7000000 {
+               compatible = "hisilicon,hns-dsaf-v1";
+               dsa_name = "dsaf0";
+               mode = "6port-16rss";
+               interrupt-parent = <&mbigen_dsa>;
+
+               reg = <0x0 0xC0000000 0x0 0x420000
+                      0x0 0xC2000000 0x0 0x300000
+                      0x0 0xc5000000 0x0 0x890000
+                      0x0 0xc7000000 0x0 0x60000
+                      >;
+
+               phy-handle = <0 0 0 0 &soc0_phy0 &soc0_phy1 0 0>;
+               interrupts = <
+                       /* [14] ge fifo err 8 / xge 6**/
+                       149 0x4 150 0x4 151 0x4 152 0x4
+                       153 0x4 154 0x4  26 0x4 27 0x4
+                       155 0x4 156 0x4 157 0x4 158 0x4 159 0x4 160 0x4
+                       /* [12] rcb com 4*3**/
+                       0x6 0x4 0x7 0x4 0x8 0x4 0x9 0x4
+                        16 0x4  17 0x4  18 0x4  19 0x4
+                        22 0x4  23 0x4  24 0x4  25 0x4
+                       /* [8] ppe tnl 0-7***/
+                       0x0 0x4 0x1 0x4 0x2 0x4 0x3 0x4
+                       0x4 0x4 0x5 0x4 12 0x4 13 0x4
+                       /* [21] dsaf event int 3+18**/
+                        128 0x4  129 0x4  130 0x4
+                       0x83 0x4 0x84 0x4 0x85 0x4 0x86 0x4 0x87 0x4 0x88 0x4
+                       0x89 0x4 0x8a 0x4 0x8b 0x4 0x8c 0x4 0x8d 0x4 0x8e 0x4
+                       0x8f 0x4 0x90 0x4 0x91 0x4 0x92 0x4 0x93 0x4 0x94 0x4
+                       /* [4] debug rcb 2*2*/
+                       0xe 0x1 0xf 0x1 0x14 0x1 0x15 0x1
+                       /* [256] sevice rcb 2*128*/
+                       0x180 0x1 0x181 0x1 0x182 0x1 0x183 0x1
+                       0x184 0x1 0x185 0x1 0x186 0x1 0x187 0x1
+                       0x188 0x1 0x189 0x1 0x18a 0x1 0x18b 0x1
+                       0x18c 0x1 0x18d 0x1 0x18e 0x1 0x18f 0x1
+                       0x190 0x1 0x191 0x1 0x192 0x1 0x193 0x1
+                       0x194 0x1 0x195 0x1 0x196 0x1 0x197 0x1
+                       0x198 0x1 0x199 0x1 0x19a 0x1 0x19b 0x1
+                       0x19c 0x1 0x19d 0x1 0x19e 0x1 0x19f 0x1
+                       0x1a0 0x1 0x1a1 0x1 0x1a2 0x1 0x1a3 0x1
+                       0x1a4 0x1 0x1a5 0x1 0x1a6 0x1 0x1a7 0x1
+                       0x1a8 0x1 0x1a9 0x1 0x1aa 0x1 0x1ab 0x1
+                       0x1ac 0x1 0x1ad 0x1 0x1ae 0x1 0x1af 0x1
+                       0x1b0 0x1 0x1b1 0x1 0x1b2 0x1 0x1b3 0x1
+                       0x1b4 0x1 0x1b5 0x1 0x1b6 0x1 0x1b7 0x1
+                       0x1b8 0x1 0x1b9 0x1 0x1ba 0x1 0x1bb 0x1
+                       0x1bc 0x1 0x1bd 0x1 0x1be 0x1 0x1bf 0x1
+                       0x1c0 0x1 0x1c1 0x1 0x1c2 0x1 0x1c3 0x1
+                       0x1c4 0x1 0x1c5 0x1 0x1c6 0x1 0x1c7 0x1
+                       0x1c8 0x1 0x1c9 0x1 0x1ca 0x1 0x1cb 0x1
+                       0x1cc 0x1 0x1cd 0x1 0x1ce 0x1 0x1cf 0x1
+                       0x1d0 0x1 0x1d1 0x1 0x1d2 0x1 0x1d3 0x1
+                       0x1d4 0x1 0x1d5 0x1 0x1d6 0x1 0x1d7 0x1
+                       0x1d8 0x1 0x1d9 0x1 0x1da 0x1 0x1db 0x1
+                       0x1dc 0x1 0x1dd 0x1 0x1de 0x1 0x1df 0x1
+                       0x1e0 0x1 0x1e1 0x1 0x1e2 0x1 0x1e3 0x1
+                       0x1e4 0x1 0x1e5 0x1 0x1e6 0x1 0x1e7 0x1
+                       0x1e8 0x1 0x1e9 0x1 0x1ea 0x1 0x1eb 0x1
+                       0x1ec 0x1 0x1ed 0x1 0x1ee 0x1 0x1ef 0x1
+                       0x1f0 0x1 0x1f1 0x1 0x1f2 0x1 0x1f3 0x1
+                       0x1f4 0x1 0x1f5 0x1 0x1f6 0x1 0x1f7 0x1
+                       0x1f8 0x1 0x1f9 0x1 0x1fa 0x1 0x1fb 0x1
+                       0x1fc 0x1 0x1fd 0x1 0x1fe 0x1 0x1ff 0x1
+                       0x200 0x1 0x201 0x1 0x202 0x1 0x203 0x1
+                       0x204 0x1 0x205 0x1 0x206 0x1 0x207 0x1
+                       0x208 0x1 0x209 0x1 0x20a 0x1 0x20b 0x1
+                       0x20c 0x1 0x20d 0x1 0x20e 0x1 0x20f 0x1
+                       0x210 0x1 0x211 0x1 0x212 0x1 0x213 0x1
+                       0x214 0x1 0x215 0x1 0x216 0x1 0x217 0x1
+                       0x218 0x1 0x219 0x1 0x21a 0x1 0x21b 0x1
+                       0x21c 0x1 0x21d 0x1 0x21e 0x1 0x21f 0x1
+                       0x220 0x1 0x221 0x1 0x222 0x1 0x223 0x1
+                       0x224 0x1 0x225 0x1 0x226 0x1 0x227 0x1
+                       0x228 0x1 0x229 0x1 0x22a 0x1 0x22b 0x1
+                       0x22c 0x1 0x22d 0x1 0x22e 0x1 0x22f 0x1
+                       0x230 0x1 0x231 0x1 0x232 0x1 0x233 0x1
+                       0x234 0x1 0x235 0x1 0x236 0x1 0x237 0x1
+                       0x238 0x1 0x239 0x1 0x23a 0x1 0x23b 0x1
+                       0x23c 0x1 0x23d 0x1 0x23e 0x1 0x23f 0x1
+                       0x240 0x1 0x241 0x1 0x242 0x1 0x243 0x1
+                       0x244 0x1 0x245 0x1 0x246 0x1 0x247 0x1
+                       0x248 0x1 0x249 0x1 0x24a 0x1 0x24b 0x1
+                       0x24c 0x1 0x24d 0x1 0x24e 0x1 0x24f 0x1
+                       0x250 0x1 0x251 0x1 0x252 0x1 0x253 0x1
+                       0x254 0x1 0x255 0x1 0x256 0x1 0x257 0x1
+                       0x258 0x1 0x259 0x1 0x25a 0x1 0x25b 0x1
+                       0x25c 0x1 0x25d 0x1 0x25e 0x1 0x25f 0x1
+                       0x260 0x1 0x261 0x1 0x262 0x1 0x263 0x1
+                       0x264 0x1 0x265 0x1 0x266 0x1 0x267 0x1
+                       0x268 0x1 0x269 0x1 0x26a 0x1 0x26b 0x1
+                       0x26c 0x1 0x26d 0x1 0x26e 0x1 0x26f 0x1
+                       0x270 0x1 0x271 0x1 0x272 0x1 0x273 0x1
+                       0x274 0x1 0x275 0x1 0x276 0x1 0x277 0x1
+                       0x278 0x1 0x279 0x1 0x27a 0x1 0x27b 0x1
+                       0x27c 0x1 0x27d 0x1 0x27e 0x1 0x27f 0x1>;
+               buf-size = <4096>;
+               desc-num = <1024>;
+               dma-coherent;
+       };
+
+       eth0: ethernet@0{
+               compatible = "hisilicon,hns-nic-v1";
+               ae-name = "dsaf0";
+               port-id = <0>;
+               local-mac-address = [00 00 00 01 00 58];
+               status = "disabled";
+               dma-coherent;
+       };
+       eth1: ethernet@1{
+               compatible = "hisilicon,hns-nic-v1";
+               ae-name = "dsaf0";
+               port-id = <1>;
+               status = "disabled";
+               dma-coherent;
+       };
+       eth2: ethernet@2{
+               compatible = "hisilicon,hns-nic-v1";
+               ae-name = "dsaf0";
+               port-id = <2>;
+               local-mac-address = [00 00 00 01 00 5a];
+               status = "disabled";
+               dma-coherent;
+       };
+       eth3: ethernet@3{
+               compatible = "hisilicon,hns-nic-v1";
+               ae-name = "dsaf0";
+               port-id = <3>;
+               local-mac-address = [00 00 00 01 00 5b];
+               status = "disabled";
+               dma-coherent;
+       };
+       eth4: ethernet@4{
+               compatible = "hisilicon,hns-nic-v1";
+               ae-name = "dsaf0";
+               port-id = <4>;
+               local-mac-address = [00 00 00 01 00 5c];
+               status = "disabled";
+               dma-coherent;
+       };
+       eth5: ethernet@5{
+               compatible = "hisilicon,hns-nic-v1";
+               ae-name = "dsaf0";
+               port-id = <5>;
+               local-mac-address = [00 00 00 01 00 5d];
+               status = "disabled";
+               dma-coherent;
+       };
+       eth6: ethernet@6{
+               compatible = "hisilicon,hns-nic-v1";
+               ae-name = "dsaf0";
+               port-id = <6>;
+               local-mac-address = [00 00 00 01 00 5e];
+               status = "disabled";
+               dma-coherent;
+       };
+       eth7: ethernet@7{
+               compatible = "hisilicon,hns-nic-v1";
+               ae-name = "dsaf0";
+               port-id = <7>;
+               local-mac-address = [00 00 00 01 00 5f];
+               status = "disabled";
+               dma-coherent;
+       };
+};
index 208cec08a74fa09973473adca3c69dfbcdb6111c..5f8a38dee2744d6655bb6eceb3cabf6917c80ad4 100644 (file)
@@ -92,4 +92,9 @@ static inline const char *acpi_get_enable_method(int cpu)
 {
        return acpi_psci_present() ? "psci" : NULL;
 }
+
+#ifdef CONFIG_ACPI_APEI
+pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr);
+#endif
+
 #endif /*_ASM_ACPI_H*/
diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h
new file mode 100644 (file)
index 0000000..030cdcb
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * arch/arm64/include/asm/arch_gicv3.h
+ *
+ * Copyright (C) 2015 ARM Ltd.
+ *
+ * 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.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_ARCH_GICV3_H
+#define __ASM_ARCH_GICV3_H
+
+#include <asm/sysreg.h>
+
+#define ICC_EOIR1_EL1                  sys_reg(3, 0, 12, 12, 1)
+#define ICC_DIR_EL1                    sys_reg(3, 0, 12, 11, 1)
+#define ICC_IAR1_EL1                   sys_reg(3, 0, 12, 12, 0)
+#define ICC_SGI1R_EL1                  sys_reg(3, 0, 12, 11, 5)
+#define ICC_PMR_EL1                    sys_reg(3, 0, 4, 6, 0)
+#define ICC_CTLR_EL1                   sys_reg(3, 0, 12, 12, 4)
+#define ICC_SRE_EL1                    sys_reg(3, 0, 12, 12, 5)
+#define ICC_GRPEN1_EL1                 sys_reg(3, 0, 12, 12, 7)
+
+#define ICC_SRE_EL2                    sys_reg(3, 4, 12, 9, 5)
+
+/*
+ * System register definitions
+ */
+#define ICH_VSEIR_EL2                  sys_reg(3, 4, 12, 9, 4)
+#define ICH_HCR_EL2                    sys_reg(3, 4, 12, 11, 0)
+#define ICH_VTR_EL2                    sys_reg(3, 4, 12, 11, 1)
+#define ICH_MISR_EL2                   sys_reg(3, 4, 12, 11, 2)
+#define ICH_EISR_EL2                   sys_reg(3, 4, 12, 11, 3)
+#define ICH_ELSR_EL2                   sys_reg(3, 4, 12, 11, 5)
+#define ICH_VMCR_EL2                   sys_reg(3, 4, 12, 11, 7)
+
+#define __LR0_EL2(x)                   sys_reg(3, 4, 12, 12, x)
+#define __LR8_EL2(x)                   sys_reg(3, 4, 12, 13, x)
+
+#define ICH_LR0_EL2                    __LR0_EL2(0)
+#define ICH_LR1_EL2                    __LR0_EL2(1)
+#define ICH_LR2_EL2                    __LR0_EL2(2)
+#define ICH_LR3_EL2                    __LR0_EL2(3)
+#define ICH_LR4_EL2                    __LR0_EL2(4)
+#define ICH_LR5_EL2                    __LR0_EL2(5)
+#define ICH_LR6_EL2                    __LR0_EL2(6)
+#define ICH_LR7_EL2                    __LR0_EL2(7)
+#define ICH_LR8_EL2                    __LR8_EL2(0)
+#define ICH_LR9_EL2                    __LR8_EL2(1)
+#define ICH_LR10_EL2                   __LR8_EL2(2)
+#define ICH_LR11_EL2                   __LR8_EL2(3)
+#define ICH_LR12_EL2                   __LR8_EL2(4)
+#define ICH_LR13_EL2                   __LR8_EL2(5)
+#define ICH_LR14_EL2                   __LR8_EL2(6)
+#define ICH_LR15_EL2                   __LR8_EL2(7)
+
+#define __AP0Rx_EL2(x)                 sys_reg(3, 4, 12, 8, x)
+#define ICH_AP0R0_EL2                  __AP0Rx_EL2(0)
+#define ICH_AP0R1_EL2                  __AP0Rx_EL2(1)
+#define ICH_AP0R2_EL2                  __AP0Rx_EL2(2)
+#define ICH_AP0R3_EL2                  __AP0Rx_EL2(3)
+
+#define __AP1Rx_EL2(x)                 sys_reg(3, 4, 12, 9, x)
+#define ICH_AP1R0_EL2                  __AP1Rx_EL2(0)
+#define ICH_AP1R1_EL2                  __AP1Rx_EL2(1)
+#define ICH_AP1R2_EL2                  __AP1Rx_EL2(2)
+#define ICH_AP1R3_EL2                  __AP1Rx_EL2(3)
+
+#ifndef __ASSEMBLY__
+
+#include <linux/stringify.h>
+
+/*
+ * Low-level accessors
+ *
+ * These system registers are 32 bits, but we make sure that the compiler
+ * sets the GP register's most significant bits to 0 with an explicit cast.
+ */
+
+static inline void gic_write_eoir(u32 irq)
+{
+       asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" ((u64)irq));
+       isb();
+}
+
+static inline void gic_write_dir(u32 irq)
+{
+       asm volatile("msr_s " __stringify(ICC_DIR_EL1) ", %0" : : "r" ((u64)irq));
+       isb();
+}
+
+static inline u64 gic_read_iar_common(void)
+{
+       u64 irqstat;
+
+       asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat));
+       return irqstat;
+}
+
+/*
+ * Cavium ThunderX erratum 23154
+ *
+ * The gicv3 of ThunderX requires a modified version for reading the
+ * IAR status to ensure data synchronization (access to icc_iar1_el1
+ * is not sync'ed before and after).
+ */
+static inline u64 gic_read_iar_cavium_thunderx(void)
+{
+       u64 irqstat;
+
+       asm volatile(
+               "nop;nop;nop;nop\n\t"
+               "nop;nop;nop;nop\n\t"
+               "mrs_s %0, " __stringify(ICC_IAR1_EL1) "\n\t"
+               "nop;nop;nop;nop"
+               : "=r" (irqstat));
+       mb();
+
+       return irqstat;
+}
+
+static inline void gic_write_pmr(u32 val)
+{
+       asm volatile("msr_s " __stringify(ICC_PMR_EL1) ", %0" : : "r" ((u64)val));
+}
+
+static inline void gic_write_ctlr(u32 val)
+{
+       asm volatile("msr_s " __stringify(ICC_CTLR_EL1) ", %0" : : "r" ((u64)val));
+       isb();
+}
+
+static inline void gic_write_grpen1(u32 val)
+{
+       asm volatile("msr_s " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" ((u64)val));
+       isb();
+}
+
+static inline void gic_write_sgi1r(u64 val)
+{
+       asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val));
+}
+
+static inline u32 gic_read_sre(void)
+{
+       u64 val;
+
+       asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val));
+       return val;
+}
+
+static inline void gic_write_sre(u32 val)
+{
+       asm volatile("msr_s " __stringify(ICC_SRE_EL1) ", %0" : : "r" ((u64)val));
+       isb();
+}
+
+#define gic_read_typer(c)              readq_relaxed(c)
+#define gic_write_irouter(v, c)                writeq_relaxed(v, c)
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ASM_ARCH_GICV3_H */
index 35a67783cfa088d4166de9ff7ed6f993b899c09b..1e247ac2601af41012823f0a639e2f9393af6d0b 100644 (file)
@@ -54,7 +54,7 @@
 #define ATOMIC_INIT(i) { (i) }
 
 #define atomic_read(v)                 READ_ONCE((v)->counter)
-#define atomic_set(v, i)               (((v)->counter) = (i))
+#define atomic_set(v, i)               WRITE_ONCE(((v)->counter), (i))
 #define atomic_xchg(v, new)            xchg(&((v)->counter), (new))
 #define atomic_cmpxchg(v, old, new)    cmpxchg(&((v)->counter), (old), (new))
 
index 171570702bb801431f141f46238eb0d9e1895fe5..dbc78d2b8cc6bbd43af2bf4781b6fffaa54a4fe1 100644 (file)
@@ -27,8 +27,9 @@
 #define ARM64_HAS_SYSREG_GIC_CPUIF             3
 #define ARM64_HAS_PAN                          4
 #define ARM64_HAS_LSE_ATOMICS                  5
+#define ARM64_WORKAROUND_CAVIUM_23154          6
 
-#define ARM64_NCAPS                            6
+#define ARM64_NCAPS                            7
 
 #ifndef __ASSEMBLY__
 
index ee6403df9fe4c1f32e9a7b30172a7a141056ec40..100a3d1b17c854d6c1a2c465b56ad8f101ab5efa 100644 (file)
        (0xf                    << MIDR_ARCHITECTURE_SHIFT) | \
        ((partnum)              << MIDR_PARTNUM_SHIFT))
 
-#define ARM_CPU_IMP_ARM                0x41
-#define ARM_CPU_IMP_APM                0x50
+#define ARM_CPU_IMP_ARM                        0x41
+#define ARM_CPU_IMP_APM                        0x50
+#define ARM_CPU_IMP_CAVIUM             0x43
 
-#define ARM_CPU_PART_AEM_V8    0xD0F
-#define ARM_CPU_PART_FOUNDATION        0xD00
-#define ARM_CPU_PART_CORTEX_A57        0xD07
-#define ARM_CPU_PART_CORTEX_A53        0xD03
+#define ARM_CPU_PART_AEM_V8            0xD0F
+#define ARM_CPU_PART_FOUNDATION                0xD00
+#define ARM_CPU_PART_CORTEX_A57                0xD07
+#define ARM_CPU_PART_CORTEX_A53                0xD03
 
-#define APM_CPU_PART_POTENZA   0x000
+#define APM_CPU_PART_POTENZA           0x000
+
+#define CAVIUM_CPU_PART_THUNDERX       0x0A1
 
 #define ID_AA64MMFR0_BIGENDEL0_SHIFT   16
 #define ID_AA64MMFR0_BIGENDEL0_MASK    (0xf << ID_AA64MMFR0_BIGENDEL0_SHIFT)
index 6b4c3ad75a2a99b0760a38436b7d159bfe3209c3..67027c611dbd51e66b72492943394ff317c3998a 100644 (file)
@@ -94,6 +94,7 @@
 #define MT_DEVICE_GRE          2
 #define MT_NORMAL_NC           3
 #define MT_NORMAL              4
+#define MT_NORMAL_WT           5
 
 /*
  * Memory types for Stage-2 translation
index 26b066690593cd6304e81fdd24465a25ace2f396..571ca0ed4f0565378131b25f317ef8b2f819c3b2 100644 (file)
@@ -60,8 +60,10 @@ extern void __pgd_error(const char *file, int line, unsigned long val);
 #define PROT_DEFAULT           (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
 #define PROT_SECT_DEFAULT      (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S)
 
+#define PROT_DEVICE_nGnRnE     (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_DEVICE_nGnRnE))
 #define PROT_DEVICE_nGnRE      (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_DEVICE_nGnRE))
 #define PROT_NORMAL_NC         (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL_NC))
+#define PROT_NORMAL_WT         (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL_WT))
 #define PROT_NORMAL            (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL))
 
 #define PROT_SECT_DEVICE_nGnRE (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE))
index 19de7537e7d32f407f3f75d5f295ab5f3c7c4291..137d537ddceb8001f15d9daa18c95631b561a085 100644 (file)
 #include <asm/cpu_ops.h>
 #include <asm/smp_plat.h>
 
+#ifdef CONFIG_ACPI_APEI
+# include <linux/efi.h>
+# include <asm/pgtable.h>
+#endif
+
 int acpi_noirq = 1;            /* skip ACPI IRQ initialization */
 int acpi_disabled = 1;
 EXPORT_SYMBOL(acpi_disabled);
@@ -230,3 +235,27 @@ void __init acpi_gic_init(void)
 
        early_acpi_os_unmap_memory((char *)table, tbl_size);
 }
+
+#ifdef CONFIG_ACPI_APEI
+pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr)
+{
+       /*
+        * According to "Table 8 Map: EFI memory types to AArch64 memory
+        * types" of UEFI 2.5 section 2.3.6.1, each EFI memory type is
+        * mapped to a corresponding MAIR attribute encoding.
+        * The EFI memory attribute advises all possible capabilities
+        * of a memory region. We use the most efficient capability.
+        */
+
+       u64 attr;
+
+       attr = efi_mem_attributes(addr);
+       if (attr & EFI_MEMORY_WB)
+               return PAGE_KERNEL;
+       if (attr & EFI_MEMORY_WT)
+               return __pgprot(PROT_NORMAL_WT);
+       if (attr & EFI_MEMORY_WC)
+               return __pgprot(PROT_NORMAL_NC);
+       return __pgprot(PROT_DEVICE_nGnRnE);
+}
+#endif
index bcee7abac68ebb5bcbcde829039113e1e76d5a65..937f5e58a4d340a27234c76b5a84fedcf9aa6373 100644 (file)
@@ -284,21 +284,23 @@ static void register_insn_emulation_sysctl(struct ctl_table *table)
        __asm__ __volatile__(                                   \
        ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN,    \
                    CONFIG_ARM64_PAN)                           \
-       "       mov             %w2, %w1\n"                     \
-       "0:     ldxr"B"         %w1, [%3]\n"                    \
-       "1:     stxr"B"         %w0, %w2, [%3]\n"               \
+       "0:     ldxr"B"         %w2, [%3]\n"                    \
+       "1:     stxr"B"         %w0, %w1, [%3]\n"               \
        "       cbz             %w0, 2f\n"                      \
        "       mov             %w0, %w4\n"                     \
+       "       b               3f\n"                           \
        "2:\n"                                                  \
+       "       mov             %w1, %w2\n"                     \
+       "3:\n"                                                  \
        "       .pushsection     .fixup,\"ax\"\n"               \
        "       .align          2\n"                            \
-       "3:     mov             %w0, %w5\n"                     \
-       "       b               2b\n"                           \
+       "4:     mov             %w0, %w5\n"                     \
+       "       b               3b\n"                           \
        "       .popsection"                                    \
        "       .pushsection     __ex_table,\"a\"\n"            \
        "       .align          3\n"                            \
-       "       .quad           0b, 3b\n"                       \
-       "       .quad           1b, 3b\n"                       \
+       "       .quad           0b, 4b\n"                       \
+       "       .quad           1b, 4b\n"                       \
        "       .popsection\n"                                  \
        ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN,    \
                CONFIG_ARM64_PAN)                               \
index 6ffd914385609d0aab875813e4e9e8b690976421..574450c257a4d038e17d4eb8a47b954b20b2b9bb 100644 (file)
@@ -23,6 +23,7 @@
 
 #define MIDR_CORTEX_A53 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53)
 #define MIDR_CORTEX_A57 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57)
+#define MIDR_THUNDERX  MIDR_CPU_PART(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX)
 
 #define CPU_MODEL_MASK (MIDR_IMPLEMENTOR_MASK | MIDR_PARTNUM_MASK | \
                        MIDR_ARCHITECTURE_MASK)
@@ -81,6 +82,14 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
                .capability = ARM64_WORKAROUND_845719,
                MIDR_RANGE(MIDR_CORTEX_A53, 0x00, 0x04),
        },
+#endif
+#ifdef CONFIG_CAVIUM_ERRATUM_23154
+       {
+       /* Cavium ThunderX, pass 1.x */
+               .desc = "Cavium erratum 23154",
+               .capability = ARM64_WORKAROUND_CAVIUM_23154,
+               MIDR_RANGE(MIDR_THUNDERX, 0x00, 0x01),
+       },
 #endif
        {
        }
index 3c9aed32f70b2113773f671053a0f6ac87632b4a..305f30dc9e633fe86947621e54d802744e59df52 100644 (file)
@@ -23,6 +23,8 @@
 #include <asm/cpufeature.h>
 #include <asm/processor.h>
 
+#include <linux/irqchip/arm-gic-v3.h>
+
 static bool
 feature_matches(u64 reg, const struct arm64_cpu_capabilities *entry)
 {
@@ -45,11 +47,26 @@ __ID_FEAT_CHK(id_aa64pfr0);
 __ID_FEAT_CHK(id_aa64mmfr1);
 __ID_FEAT_CHK(id_aa64isar0);
 
+static bool has_useable_gicv3_cpuif(const struct arm64_cpu_capabilities *entry)
+{
+       bool has_sre;
+
+       if (!has_id_aa64pfr0_feature(entry))
+               return false;
+
+       has_sre = gic_enable_sre();
+       if (!has_sre)
+               pr_warn_once("%s present but disabled by higher exception level\n",
+                            entry->desc);
+
+       return has_sre;
+}
+
 static const struct arm64_cpu_capabilities arm64_features[] = {
        {
                .desc = "GIC system register CPU interface",
                .capability = ARM64_HAS_SYSREG_GIC_CPUIF,
-               .matches = has_id_aa64pfr0_feature,
+               .matches = has_useable_gicv3_cpuif,
                .field_pos = 24,
                .min_field_value = 1,
        },
index 816120ece6bcecfbcbc1c25be51324c5197b65cd..78dfbd34b6bffd2fa36312da89dc6ca43f036c3c 100644 (file)
@@ -25,10 +25,20 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg,
        unsigned long kernel_size, kernel_memsize = 0;
        unsigned long nr_pages;
        void *old_image_addr = (void *)*image_addr;
+       unsigned long preferred_offset;
+
+       /*
+        * The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond
+        * a 2 MB aligned base, which itself may be lower than dram_base, as
+        * long as the resulting offset equals or exceeds it.
+        */
+       preferred_offset = round_down(dram_base, SZ_2M) + TEXT_OFFSET;
+       if (preferred_offset < dram_base)
+               preferred_offset += SZ_2M;
 
        /* Relocate the image, if required. */
        kernel_size = _edata - _text;
-       if (*image_addr != (dram_base + TEXT_OFFSET)) {
+       if (*image_addr != preferred_offset) {
                kernel_memsize = kernel_size + (_end - _edata);
 
                /*
@@ -42,7 +52,7 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg,
                 * Mustang), we can still place the kernel at the address
                 * 'dram_base + TEXT_OFFSET'.
                 */
-               *image_addr = *reserve_addr = dram_base + TEXT_OFFSET;
+               *image_addr = *reserve_addr = preferred_offset;
                nr_pages = round_up(kernel_memsize, EFI_ALLOC_ALIGN) /
                           EFI_PAGE_SIZE;
                status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS,
index 13671a9cf0167057d583f1c2dddca54ee94658f8..61eb1d17586a859a1fcc1d0d0fd653028d736b11 100644 (file)
@@ -51,15 +51,6 @@ static struct mm_struct efi_mm = {
        INIT_MM_CONTEXT(efi_mm)
 };
 
-static int uefi_debug __initdata;
-static int __init uefi_debug_setup(char *str)
-{
-       uefi_debug = 1;
-
-       return 0;
-}
-early_param("uefi_debug", uefi_debug_setup);
-
 static int __init is_normal_ram(efi_memory_desc_t *md)
 {
        if (md->attribute & EFI_MEMORY_WB)
@@ -171,14 +162,14 @@ static __init void reserve_regions(void)
        efi_memory_desc_t *md;
        u64 paddr, npages, size;
 
-       if (uefi_debug)
+       if (efi_enabled(EFI_DBG))
                pr_info("Processing EFI memory map:\n");
 
        for_each_efi_memory_desc(&memmap, md) {
                paddr = md->phys_addr;
                npages = md->num_pages;
 
-               if (uefi_debug) {
+               if (efi_enabled(EFI_DBG)) {
                        char buf[64];
 
                        pr_info("  0x%012llx-0x%012llx %s",
@@ -194,11 +185,11 @@ static __init void reserve_regions(void)
 
                if (is_reserve_region(md)) {
                        memblock_reserve(paddr, size);
-                       if (uefi_debug)
+                       if (efi_enabled(EFI_DBG))
                                pr_cont("*");
                }
 
-               if (uefi_debug)
+               if (efi_enabled(EFI_DBG))
                        pr_cont("\n");
        }
 
@@ -210,14 +201,14 @@ void __init efi_init(void)
        struct efi_fdt_params params;
 
        /* Grab UEFI information placed in FDT by stub */
-       if (!efi_get_fdt_params(&params, uefi_debug))
+       if (!efi_get_fdt_params(&params))
                return;
 
        efi_system_table = params.system_table;
 
        memblock_reserve(params.mmap & PAGE_MASK,
                         PAGE_ALIGN(params.mmap_size + (params.mmap & ~PAGE_MASK)));
-       memmap.phys_map = (void *)params.mmap;
+       memmap.phys_map = params.mmap;
        memmap.map = early_memremap(params.mmap, params.mmap_size);
        memmap.map_end = memmap.map + params.mmap_size;
        memmap.desc_size = params.desc_size;
@@ -291,7 +282,7 @@ static int __init arm64_enable_runtime_services(void)
        pr_info("Remapping and enabling EFI services.\n");
 
        mapsize = memmap.map_end - memmap.map;
-       memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map,
+       memmap.map = (__force void *)ioremap_cache(memmap.phys_map,
                                                   mapsize);
        if (!memmap.map) {
                pr_err("Failed to remap EFI memory map\n");
index 90d09eddd5b27368e358efd44ab552db8330d39c..351a4de1b1e26e07e3b1920d06b199e778042811 100644 (file)
@@ -498,6 +498,8 @@ CPU_LE(     bic     x0, x0, #(3 << 24)      )       // Clear the EE and E0E bits for EL1
        orr     x0, x0, #ICC_SRE_EL2_ENABLE     // Set ICC_SRE_EL2.Enable==1
        msr_s   ICC_SRE_EL2, x0
        isb                                     // Make sure SRE is now set
+       mrs_s   x0, ICC_SRE_EL2                 // Read SRE back,
+       tbz     x0, #0, 3f                      // and check that it sticks
        msr_s   ICH_HCR_EL2, xzr                // Reset ICC_HCR_EL2 to defaults
 
 3:
index 407991bf79f5116eed6d5aa4aa2f1314ecabe0fc..ccb6078ed9f20fb55132deb48504df3a3134a784 100644 (file)
@@ -48,11 +48,7 @@ int notrace unwind_frame(struct stackframe *frame)
 
        frame->sp = fp + 0x10;
        frame->fp = *(unsigned long *)(fp);
-       /*
-        * -4 here because we care about the PC at time of bl,
-        * not where the return will go.
-        */
-       frame->pc = *(unsigned long *)(fp + 8) - 4;
+       frame->pc = *(unsigned long *)(fp + 8);
 
        return 0;
 }
index 8297d502217e13010bc891e7d68b5f7279ea62b9..44ca4143b013227ebf9eb40eafd721454ab45074 100644 (file)
@@ -80,17 +80,21 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
        if (ret == 0) {
                /*
                 * We are resuming from reset with TTBR0_EL1 set to the
-                * idmap to enable the MMU; restore the active_mm mappings in
-                * TTBR0_EL1 unless the active_mm == &init_mm, in which case
-                * the thread entered cpu_suspend with TTBR0_EL1 set to
-                * reserved TTBR0 page tables and should be restored as such.
+                * idmap to enable the MMU; set the TTBR0 to the reserved
+                * page tables to prevent speculative TLB allocations, flush
+                * the local tlb and set the default tcr_el1.t0sz so that
+                * the TTBR0 address space set-up is properly restored.
+                * If the current active_mm != &init_mm we entered cpu_suspend
+                * with mappings in TTBR0 that must be restored, so we switch
+                * them back to complete the address space configuration
+                * restoration before returning.
                 */
-               if (mm == &init_mm)
-                       cpu_set_reserved_ttbr0();
-               else
-                       cpu_switch_mm(mm->pgd, mm);
-
+               cpu_set_reserved_ttbr0();
                flush_tlb_all();
+               cpu_set_default_tcr_t0sz();
+
+               if (mm != &init_mm)
+                       cpu_switch_mm(mm->pgd, mm);
 
                /*
                 * Restore per-cpu offset before any kernel
index 5c7e920e486132c5257255ff843d81a6616670b1..ff5292c6277c4764734a1a1769af06347ccb8c4b 100644 (file)
@@ -16,6 +16,9 @@ menuconfig VIRTUALIZATION
 
 if VIRTUALIZATION
 
+config KVM_ARM_VGIC_V3
+       bool
+
 config KVM
        bool "Kernel-based Virtual Machine (KVM) support"
        depends on OF
@@ -31,6 +34,7 @@ config KVM
        select KVM_VFIO
        select HAVE_KVM_EVENTFD
        select HAVE_KVM_IRQFD
+       select KVM_ARM_VGIC_V3
        ---help---
          Support hosting virtualized guest machines.
 
index e4ee7bd8830aed60b9b0b493c4c4dc93863d419c..7783ff05f74cc262c643b1a96932a717e47445c5 100644 (file)
@@ -163,12 +163,14 @@ ENTRY(__cpu_setup)
         *   DEVICE_GRE         010     00001100
         *   NORMAL_NC          011     01000100
         *   NORMAL             100     11111111
+        *   NORMAL_WT          101     10111011
         */
        ldr     x5, =MAIR(0x00, MT_DEVICE_nGnRnE) | \
                     MAIR(0x04, MT_DEVICE_nGnRE) | \
                     MAIR(0x0c, MT_DEVICE_GRE) | \
                     MAIR(0x44, MT_NORMAL_NC) | \
-                    MAIR(0xff, MT_NORMAL)
+                    MAIR(0xff, MT_NORMAL) | \
+                    MAIR(0xbb, MT_NORMAL_WT)
        msr     mair_el1, x5
        /*
         * Prepare SCTLR
index c047598b09e051cfdad2b7e1f24c21bb6e6153f8..a44e5293c6f58adb288e9c0d0549fcbe26c98daa 100644 (file)
@@ -744,7 +744,7 @@ void bpf_int_jit_compile(struct bpf_prog *prog)
 
        set_memory_ro((unsigned long)header, header->pages);
        prog->bpf_func = (void *)ctx.image;
-       prog->jited = true;
+       prog->jited = 1;
 out:
        kfree(ctx.offset);
 }
index 91146b416cdba5b5d76946db679849b649545742..99b0a798495077a8aeddce9957f657581a9f92c4 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/leds_pwm.h>
 #include <linux/input.h>
 #include <linux/gpio_keys.h>
-#include <linux/atmel_serial.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
 
index 97c9bdf8340962209dfe09354175acb1eead01f8..d74fd8ce980aeb327b831fe8c7b4d862b6421524 100644 (file)
@@ -19,8 +19,8 @@
 
 #define ATOMIC_INIT(i)  { (i) }
 
-#define atomic_read(v)         ACCESS_ONCE((v)->counter)
-#define atomic_set(v, i)       (((v)->counter) = i)
+#define atomic_read(v)         READ_ONCE((v)->counter)
+#define atomic_set(v, i)       WRITE_ONCE(((v)->counter), (i))
 
 #define ATOMIC_OP_RETURN(op, asm_op, asm_con)                          \
 static inline int __atomic_##op##_return(int i, atomic_t *v)           \
index ddcb45d7dfa7dcb4f16408ec8eaca6eeae03566d..43afc03e41251d45c2ccd0f0a00656ad5e368fe9 100644 (file)
@@ -178,7 +178,7 @@ static void __init set_megamod_mux(struct megamod_pic *pic, int src, int output)
 static void __init parse_priority_map(struct megamod_pic *pic,
                                      int *mapping, int size)
 {
-       struct device_node *np = pic->irqhost->of_node;
+       struct device_node *np = irq_domain_get_of_node(pic->irqhost);
        const __be32 *map;
        int i, maplen;
        u32 val;
index 8da5653bd8958605e1dbb2e3e0335aaa24abc9dc..e086f9e937280ec2b6676065a6e3b19bbfcc5ba1 100644 (file)
@@ -57,7 +57,6 @@ config CRIS
        select ARCH_WANT_IPC_PARSE_VERSION
        select GENERIC_IRQ_SHOW
        select GENERIC_IOMAP
-       select GENERIC_CMOS_UPDATE
        select MODULES_USE_ELF_RELA
        select CLONE_BACKWARDS2
        select OLD_SIGSUSPEND
index 4a146e1749c93ce2b2e9d35cec6c167bf4815232..a4877a4217560b5074ca1421377913286a646255 100644 (file)
@@ -354,63 +354,6 @@ no_command_line:
        blo     1b
        nop
 
-#ifdef CONFIG_BLK_DEV_ETRAXIDE
-       ;; disable ATA before enabling it in genconfig below
-       moveq   0,$r0
-       move.d  $r0,[R_ATA_CTRL_DATA]
-       move.d  $r0,[R_ATA_TRANSFER_CNT]
-       move.d  $r0,[R_ATA_CONFIG]
-#if 0
-       move.d  R_PORT_G_DATA, $r1
-       move.d  $r0, [$r1]; assert ATA bus-reset
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       move.d  0x08000000,$r0
-       move.d  $r0,[$r1]
-#endif
-#endif
-
-#ifdef CONFIG_JULIETTE
-       ;; configure external DMA channel 0 before enabling it in genconfig
-
-       moveq   0,$r0
-       move.d  $r0,[R_EXT_DMA_0_ADDR]
-       ; cnt enable, word size, output, stop, size 0
-       move.d    IO_STATE (R_EXT_DMA_0_CMD, cnt, enable)       \
-               | IO_STATE (R_EXT_DMA_0_CMD, rqpol, ahigh)      \
-               | IO_STATE (R_EXT_DMA_0_CMD, apol, ahigh)       \
-               | IO_STATE (R_EXT_DMA_0_CMD, rq_ack, burst)     \
-               | IO_STATE (R_EXT_DMA_0_CMD, wid, word)         \
-               | IO_STATE (R_EXT_DMA_0_CMD, dir, output)       \
-               | IO_STATE (R_EXT_DMA_0_CMD, run, stop)         \
-               | IO_FIELD (R_EXT_DMA_0_CMD, trf_count, 0),$r0
-       move.d  $r0,[R_EXT_DMA_0_CMD]
-
-       ;; reset dma4 and wait for completion
-
-       moveq   IO_STATE (R_DMA_CH4_CMD, cmd, reset),$r0
-       move.b  $r0,[R_DMA_CH4_CMD]
-1:     move.b  [R_DMA_CH4_CMD],$r0
-       and.b   IO_MASK (R_DMA_CH4_CMD, cmd),$r0
-       cmp.b   IO_STATE (R_DMA_CH4_CMD, cmd, reset),$r0
-       beq     1b
-       nop
-
-       ;; reset dma5 and wait for completion
-
-       moveq   IO_STATE (R_DMA_CH5_CMD, cmd, reset),$r0
-       move.b  $r0,[R_DMA_CH5_CMD]
-1:     move.b  [R_DMA_CH5_CMD],$r0
-       and.b   IO_MASK (R_DMA_CH5_CMD, cmd),$r0
-       cmp.b   IO_STATE (R_DMA_CH5_CMD, cmd, reset),$r0
-       beq     1b
-       nop
-#endif
-
        ;; Etrax product HW genconfig setup
 
        moveq   0,$r0
@@ -447,21 +390,6 @@ no_command_line:
                | IO_STATE (R_GEN_CONFIG, dma9, usb),$r0
 
 
-#if defined(CONFIG_ETRAX_DEF_R_PORT_G0_DIR_OUT)
-        or.d      IO_STATE (R_GEN_CONFIG, g0dir, out),$r0
-#endif
-
-#if defined(CONFIG_ETRAX_DEF_R_PORT_G8_15_DIR_OUT)
-        or.d      IO_STATE (R_GEN_CONFIG, g8_15dir, out),$r0
-#endif
-#if defined(CONFIG_ETRAX_DEF_R_PORT_G16_23_DIR_OUT)
-       or.d      IO_STATE (R_GEN_CONFIG, g16_23dir, out),$r0
-#endif
-
-#if defined(CONFIG_ETRAX_DEF_R_PORT_G24_DIR_OUT)
-       or.d      IO_STATE (R_GEN_CONFIG, g24dir, out),$r0
-#endif
-
        move.d  $r0,[genconfig_shadow] ; init a shadow register of R_GEN_CONFIG
 
        move.d  $r0,[R_GEN_CONFIG]
@@ -500,19 +428,9 @@ no_command_line:
        ;; including their shadow registers
 
        move.b  CONFIG_ETRAX_DEF_R_PORT_PA_DIR,$r0
-#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PA7)
-       or.b    IO_STATE (R_PORT_PA_DIR, dir7, output),$r0
-#endif
        move.b  $r0,[port_pa_dir_shadow]
        move.b  $r0,[R_PORT_PA_DIR]
        move.b  CONFIG_ETRAX_DEF_R_PORT_PA_DATA,$r0
-#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PA7)
-#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
-       and.b   ~(1 << 7),$r0
-#else
-       or.b    (1 << 7),$r0
-#endif
-#endif
        move.b  $r0,[port_pa_data_shadow]
        move.b  $r0,[R_PORT_PA_DATA]
 
@@ -520,19 +438,9 @@ no_command_line:
        move.b  $r0,[port_pb_config_shadow]
        move.b  $r0,[R_PORT_PB_CONFIG]
        move.b  CONFIG_ETRAX_DEF_R_PORT_PB_DIR,$r0
-#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PB5)
-       or.b    IO_STATE (R_PORT_PB_DIR, dir5, output),$r0
-#endif
        move.b  $r0,[port_pb_dir_shadow]
        move.b  $r0,[R_PORT_PB_DIR]
        move.b  CONFIG_ETRAX_DEF_R_PORT_PB_DATA,$r0
-#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PB5)
-#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
-       and.b   ~(1 << 5),$r0
-#else
-       or.b    (1 << 5),$r0
-#endif
-#endif
        move.b  $r0,[port_pb_data_shadow]
        move.b  $r0,[R_PORT_PB_DATA]
 
@@ -541,20 +449,6 @@ no_command_line:
        move.d  $r0, [R_PORT_PB_I2C]
 
        moveq   0,$r0
-#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_G10)
-#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
-       and.d   ~(1 << 10),$r0
-#else
-       or.d    (1 << 10),$r0
-#endif
-#endif
-#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_G11)
-#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
-       and.d   ~(1 << 11),$r0
-#else
-       or.d    (1 << 11),$r0
-#endif
-#endif
        move.d  $r0,[port_g_data_shadow]
        move.d  $r0,[R_PORT_G_DATA]
 
index 22d846bfc570c6add486d620a9c1430bbd11a46a..ed71ade93a73aea9a3be5a23b74ef87adcd9fa9d 100644 (file)
@@ -275,7 +275,7 @@ static char remcomOutBuffer[BUFMAX];
 /* Error and warning messages. */
 enum error_type
 {
-       SUCCESS, E01, E02, E03, E04, E05, E06, E07
+       SUCCESS, E01, E02, E03, E04, E05, E06, E07, E08
 };
 static char *error_message[] =
 {
@@ -286,7 +286,8 @@ static char *error_message[] =
        "E04 The command is not supported - [s,C,S,!,R,d,r] - internal error.",
        "E05 Change register content - P - the register is not implemented..",
        "E06 Change memory content - M - internal error.",
-       "E07 Change register content - P - the register is not stored on the stack"
+       "E07 Change register content - P - the register is not stored on the stack",
+       "E08 Invalid parameter"
 };
 /********************************* Register image ****************************/
 /* Use the order of registers as defined in "AXIS ETRAX CRIS Programmer's
@@ -351,7 +352,7 @@ char internal_stack[INTERNAL_STACK_SIZE];
    breakpoint to be handled. A static breakpoint uses the content of register
    BRP as it is whereas a dynamic breakpoint requires subtraction with 2
    in order to execute the instruction. The first breakpoint is static. */
-static unsigned char is_dyn_brkp = 0;
+static unsigned char __used is_dyn_brkp;
 
 /********************************* String library ****************************/
 /* Single-step over library functions creates trap loops. */
@@ -413,18 +414,6 @@ gdb_cris_strtol (const char *s, char **endptr, int base)
 }
 
 /********************************** Packet I/O ******************************/
-/* Returns the integer equivalent of a hexadecimal character. */
-static int
-hex (char ch)
-{
-       if ((ch >= 'a') && (ch <= 'f'))
-               return (ch - 'a' + 10);
-       if ((ch >= '0') && (ch <= '9'))
-               return (ch - '0');
-       if ((ch >= 'A') && (ch <= 'F'))
-               return (ch - 'A' + 10);
-       return (-1);
-}
 
 /* Convert the memory, pointed to by mem into hexadecimal representation.
    Put the result in buf, and return a pointer to the last character
@@ -455,22 +444,6 @@ mem2hex(char *buf, unsigned char *mem, int count)
        return (buf);
 }
 
-/* Convert the array, in hexadecimal representation, pointed to by buf into
-   binary representation. Put the result in mem, and return a pointer to
-   the character after the last byte written. */
-static unsigned char*
-hex2mem (unsigned char *mem, char *buf, int count)
-{
-       int i;
-       unsigned char ch;
-       for (i = 0; i < count; i++) {
-               ch = hex (*buf++) << 4;
-               ch = ch + hex (*buf++);
-               *mem++ = ch;
-       }
-       return (mem);
-}
-
 /* Put the content of the array, in binary representation, pointed to by buf
    into memory pointed to by mem, and return a pointer to the character after
    the last byte written.
@@ -524,8 +497,8 @@ getpacket (char *buffer)
                buffer[count] = '\0';
                
                if (ch == '#') {
-                       xmitcsum = hex (getDebugChar ()) << 4;
-                       xmitcsum += hex (getDebugChar ());
+                       xmitcsum = hex_to_bin(getDebugChar()) << 4;
+                       xmitcsum += hex_to_bin(getDebugChar());
                        if (checksum != xmitcsum) {
                                /* Wrong checksum */
                                putDebugChar ('-');
@@ -599,7 +572,7 @@ putDebugString (const unsigned char *str, int length)
 
 /********************************* Register image ****************************/
 /* Write a value to a specified register in the register image of the current
-   thread. Returns status code SUCCESS, E02 or E05. */
+   thread. Returns status code SUCCESS, E02, E05 or E08. */
 static int
 write_register (int regno, char *val)
 {
@@ -608,8 +581,9 @@ write_register (int regno, char *val)
 
         if (regno >= R0 && regno <= PC) {
                /* 32-bit register with simple offset. */
-               hex2mem ((unsigned char *)current_reg + regno * sizeof(unsigned int),
-                        val, sizeof(unsigned int));
+               if (hex2bin((unsigned char *)current_reg + regno * sizeof(unsigned int),
+                           val, sizeof(unsigned int)))
+                       status = E08;
        }
         else if (regno == P0 || regno == VR || regno == P4 || regno == P8) {
                /* Do not support read-only registers. */
@@ -618,13 +592,15 @@ write_register (int regno, char *val)
         else if (regno == CCR) {
                /* 16 bit register with complex offset. (P4 is read-only, P6 is not implemented, 
                    and P7 (MOF) is 32 bits in ETRAX 100LX. */
-               hex2mem ((unsigned char *)&(current_reg->ccr) + (regno-CCR) * sizeof(unsigned short),
-                        val, sizeof(unsigned short));
+               if (hex2bin((unsigned char *)&(current_reg->ccr) + (regno-CCR) * sizeof(unsigned short),
+                           val, sizeof(unsigned short)))
+                       status = E08;
        }
        else if (regno >= MOF && regno <= USP) {
                /* 32 bit register with complex offset.  (P8 has been taken care of.) */
-               hex2mem ((unsigned char *)&(current_reg->ibr) + (regno-IBR) * sizeof(unsigned int),
-                        val, sizeof(unsigned int));
+               if (hex2bin((unsigned char *)&(current_reg->ibr) + (regno-IBR) * sizeof(unsigned int),
+                           val, sizeof(unsigned int)))
+                       status = E08;
        } 
         else {
                /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */
@@ -759,9 +735,11 @@ handle_exception (int sigval)
                                /* Write registers. GXX..XX
                                   Each byte of register data  is described by two hex digits.
                                   Success: OK
-                                  Failure: void. */
-                               hex2mem((char *)&cris_reg, &remcomInBuffer[1], sizeof(registers));
-                               gdb_cris_strcpy (remcomOutBuffer, "OK");
+                                  Failure: E08. */
+                               if (hex2bin((char *)&cris_reg, &remcomInBuffer[1], sizeof(registers)))
+                                       gdb_cris_strcpy (remcomOutBuffer, error_message[E08]);
+                               else
+                                       gdb_cris_strcpy (remcomOutBuffer, "OK");
                                break;
                                
                        case 'P':
@@ -771,7 +749,7 @@ handle_exception (int sigval)
                                   for each byte in the register (target byte order). P1f=11223344 means
                                   set register 31 to 44332211.
                                   Success: OK
-                                  Failure: E02, E05 */
+                                  Failure: E02, E05, E08 */
                                {
                                        char *suffix;
                                        int regno = gdb_cris_strtol (&remcomInBuffer[1], &suffix, 16);
@@ -791,6 +769,10 @@ handle_exception (int sigval)
                                                        /* Do not support non-existing registers on the stack. */
                                                        gdb_cris_strcpy (remcomOutBuffer, error_message[E07]);
                                                        break;
+                                               case E08:
+                                                       /* Invalid parameter. */
+                                                       gdb_cris_strcpy (remcomOutBuffer, error_message[E08]);
+                                                       break;
                                                default:
                                                        /* Valid register number. */
                                                        gdb_cris_strcpy (remcomOutBuffer, "OK");
@@ -826,7 +808,7 @@ handle_exception (int sigval)
                                   AA..AA is the start address,  LLLL is the number of bytes, and
                                   XX..XX is the hexadecimal data.
                                   Success: OK
-                                  Failure: void. */
+                                  Failure: E08. */
                                {
                                        char *lenptr;
                                        char *dataptr;
@@ -835,14 +817,15 @@ handle_exception (int sigval)
                                        int length = gdb_cris_strtol(lenptr+1, &dataptr, 16);
                                        if (*lenptr == ',' && *dataptr == ':') {
                                                if (remcomInBuffer[0] == 'M') {
-                                                       hex2mem(addr, dataptr + 1, length);
-                                               }
-                                               else /* X */ {
+                                                       if (hex2bin(addr, dataptr + 1, length))
+                                                               gdb_cris_strcpy (remcomOutBuffer, error_message[E08]);
+                                                       else
+                                                               gdb_cris_strcpy (remcomOutBuffer, "OK");
+                                               } else /* X */ {
                                                        bin2mem(addr, dataptr + 1, length);
+                                                       gdb_cris_strcpy (remcomOutBuffer, "OK");
                                                }
-                                               gdb_cris_strcpy (remcomOutBuffer, "OK");
-                                       }
-                                       else {
+                                       } else {
                                                gdb_cris_strcpy (remcomOutBuffer, error_message[E06]);
                                        }
                                }
@@ -970,7 +953,7 @@ asm ("\n"
 "  move     $ibr,[cris_reg+0x4E]  ; P9,\n"
 "  move     $irp,[cris_reg+0x52]  ; P10,\n"
 "  move     $srp,[cris_reg+0x56]  ; P11,\n"
-"  move     $dtp0,[cris_reg+0x5A] ; P12, register BAR, assembler might not know BAR\n"
+"  move     $bar,[cris_reg+0x5A]  ; P12,\n"
 "                            ; P13, register DCCR already saved\n"
 ";; Due to the old assembler-versions BRP might not be recognized\n"
 "  .word 0xE670              ; move brp,r0\n"
@@ -1063,7 +1046,7 @@ asm ("\n"
 "  move     $ibr,[cris_reg+0x4E]  ; P9,\n"
 "  move     $irp,[cris_reg+0x52]  ; P10,\n"
 "  move     $srp,[cris_reg+0x56]  ; P11,\n"
-"  move     $dtp0,[cris_reg+0x5A] ; P12, register BAR, assembler might not know BAR\n"
+"  move     $bar,[cris_reg+0x5A]  ; P12,\n"
 "                            ; P13, register DCCR already saved\n"
 ";; Due to the old assembler-versions BRP might not be recognized\n"
 "  .word 0xE670              ; move brp,r0\n"
index e7f8066105aa39e9e81505b762393492261329cc..85e3f1b1f3aca848ed20b80270e915e79428e1a2 100644 (file)
@@ -68,14 +68,10 @@ paging_init(void)
 
        *R_MMU_KSEG = ( IO_STATE(R_MMU_KSEG, seg_f, seg  ) |  /* bootrom */
                        IO_STATE(R_MMU_KSEG, seg_e, page ) |
-                       IO_STATE(R_MMU_KSEG, seg_d, page ) | 
-                       IO_STATE(R_MMU_KSEG, seg_c, page ) |   
+                       IO_STATE(R_MMU_KSEG, seg_d, page ) |
+                       IO_STATE(R_MMU_KSEG, seg_c, page ) |
                        IO_STATE(R_MMU_KSEG, seg_b, seg  ) |  /* kernel reg area */
-#ifdef CONFIG_JULIETTE
-                       IO_STATE(R_MMU_KSEG, seg_a, seg  ) |  /* ARTPEC etc. */
-#else
                        IO_STATE(R_MMU_KSEG, seg_a, page ) |
-#endif
                        IO_STATE(R_MMU_KSEG, seg_9, seg  ) |  /* LED's on some boards */
                        IO_STATE(R_MMU_KSEG, seg_8, seg  ) |  /* CSE0/1, flash and I/O */
                        IO_STATE(R_MMU_KSEG, seg_7, page ) |  /* kernel vmalloc area */
@@ -92,14 +88,10 @@ paging_init(void)
                            IO_FIELD(R_MMU_KBASE_HI, base_d, 0x0 ) |
                            IO_FIELD(R_MMU_KBASE_HI, base_c, 0x0 ) |
                            IO_FIELD(R_MMU_KBASE_HI, base_b, 0xb ) |
-#ifdef CONFIG_JULIETTE
-                           IO_FIELD(R_MMU_KBASE_HI, base_a, 0xa ) |
-#else
                            IO_FIELD(R_MMU_KBASE_HI, base_a, 0x0 ) |
-#endif
                            IO_FIELD(R_MMU_KBASE_HI, base_9, 0x9 ) |
                            IO_FIELD(R_MMU_KBASE_HI, base_8, 0x8 ) );
-       
+
        *R_MMU_KBASE_LO = ( IO_FIELD(R_MMU_KBASE_LO, base_7, 0x0 ) |
                            IO_FIELD(R_MMU_KBASE_LO, base_6, 0x4 ) |
                            IO_FIELD(R_MMU_KBASE_LO, base_5, 0x0 ) |
index 21bbd93be34f3732986d228d5139df43182058cb..17dbe03af5f4d1f61e4e60c24d48d284849a78f5 100644 (file)
@@ -10,95 +10,6 @@ config ETRAX_DRAM_VIRTUAL_BASE
        depends on ETRAX_ARCH_V32
        default "c0000000"
 
-choice
-       prompt "Nbr of Ethernet LED groups"
-       depends on ETRAX_ARCH_V32
-       default ETRAX_NBR_LED_GRP_ONE
-       help
-         Select how many Ethernet LED groups that can be used. Usually one per Ethernet
-         interface is a good choice.
-
-config ETRAX_NBR_LED_GRP_ZERO
-       bool "Use zero LED groups"
-       help
-         Select this if you do not want any Ethernet LEDs.
-
-config ETRAX_NBR_LED_GRP_ONE
-       bool "Use one LED group"
-       help
-         Select this if you want one Ethernet LED group. This LED group
-         can be used for one or more Ethernet interfaces. However, it is
-         recommended that each Ethernet interface use a dedicated LED group.
-
-config ETRAX_NBR_LED_GRP_TWO
-       bool "Use two LED groups"
-       help
-         Select this if you want two Ethernet LED groups. This is the
-         best choice if you have more than one Ethernet interface and
-         would like to have separate LEDs for the interfaces.
-
-endchoice
-
-config ETRAX_LED_G_NET0
-       string "Ethernet LED group 0 green LED bit"
-       depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO)
-       default "PA3"
-       help
-         Bit to use for the green LED in Ethernet LED group 0.
-
-config ETRAX_LED_R_NET0
-       string "Ethernet LED group 0 red LED bit"
-       depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO)
-       default "PA4"
-       help
-         Bit to use for the red LED in Ethernet LED group 0.
-
-config ETRAX_LED_G_NET1
-       string "Ethernet group 1 green LED bit"
-       depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO
-       default ""
-       help
-         Bit to use for the green LED in Ethernet LED group 1.
-
-config ETRAX_LED_R_NET1
-       string "Ethernet group 1 red LED bit"
-       depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO
-       default ""
-       help
-         Bit to use for the red LED in Ethernet LED group 1.
-
-config ETRAX_V32_LED2G
-       string "Second green LED bit"
-       depends on ETRAX_ARCH_V32
-       default "PA5"
-       help
-         Bit to use for the first green LED (status LED).
-         Most Axis products use bit A5 here.
-
-config ETRAX_V32_LED2R
-       string "Second red LED bit"
-       depends on ETRAX_ARCH_V32
-       default "PA6"
-       help
-         Bit to use for the first red LED (network LED).
-         Most Axis products use bit A6 here.
-
-config ETRAX_V32_LED3G
-       string "Third green LED bit"
-       depends on ETRAX_ARCH_V32
-       default "PA7"
-       help
-         Bit to use for the first green LED (drive/power LED).
-         Most Axis products use bit A7 here.
-
-config ETRAX_V32_LED3R
-       string "Third red LED bit"
-       depends on ETRAX_ARCH_V32
-       default "PA7"
-       help
-         Bit to use for the first red LED (drive/power LED).
-         Most Axis products use bit A7 here.
-
 choice
        prompt "Kernel GDB port"
        depends on ETRAX_KGDB
index e6c523cc40bc824bd1c83735873b25426f0a46bc..2735eb7671a5ad9203251fe88726145b3a33a92a 100644 (file)
@@ -149,173 +149,6 @@ config ETRAX_NANDBOOT
          Say Y if your boot code, kernel and root file system is in
          NAND flash. Say N if they are in NOR flash.
 
-config ETRAX_I2C
-       bool "I2C driver"
-       depends on ETRAX_ARCH_V32
-       help
-         This option enables the I2C driver used by e.g. the RTC driver.
-
-config ETRAX_V32_I2C_DATA_PORT
-       string "I2C data pin"
-       depends on ETRAX_I2C
-       help
-         The pin to use for I2C data.
-
-config ETRAX_V32_I2C_CLK_PORT
-       string "I2C clock pin"
-       depends on ETRAX_I2C
-       help
-         The pin to use for I2C clock.
-
-config ETRAX_GPIO
-       bool "GPIO support"
-       depends on ETRAX_ARCH_V32
-       ---help---
-         Enables the ETRAX general port device (major 120, minors 0-4).
-         You can use this driver to access the general port bits. It supports
-         these ioctl's:
-         #include <linux/etraxgpio.h>
-         fd = open("/dev/gpioa", O_RDWR); // or /dev/gpiob
-         ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), bits_to_set);
-         ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), bits_to_clear);
-         err = ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_READ_INBITS), &val);
-         Remember that you need to setup the port directions appropriately in
-         the General configuration.
-
-config ETRAX_VIRTUAL_GPIO
-       bool "Virtual GPIO support"
-       depends on ETRAX_GPIO
-       help
-         Enables the virtual Etrax general port device (major 120, minor 6).
-         It uses an I/O expander for the I2C-bus.
-
-config ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN
-       int "Virtual GPIO interrupt pin on PA pin"
-       range 0 7
-       depends on ETRAX_VIRTUAL_GPIO
-       help
-         The pin to use on PA for virtual gpio interrupt.
-
-config ETRAX_PA_CHANGEABLE_DIR
-       hex "PA user changeable dir mask"
-       depends on ETRAX_GPIO
-       default "0x00" if ETRAXFS
-       default "0x00000000" if !ETRAXFS
-       help
-         This is a bitmask with information of what bits in PA that a
-         user can change direction on using ioctl's.
-         Bit set = changeable.
-         You probably want 0 here, but it depends on your hardware.
-
-config ETRAX_PA_CHANGEABLE_BITS
-       hex "PA user changeable bits mask"
-       depends on ETRAX_GPIO
-       default "0x00" if ETRAXFS
-       default "0x00000000" if !ETRAXFS
-       help
-         This is a bitmask with information of what bits in PA
-         that a user can change the value on using ioctl's.
-         Bit set = changeable.
-
-config ETRAX_PB_CHANGEABLE_DIR
-       hex "PB user changeable dir mask"
-       depends on ETRAX_GPIO
-       default "0x00000" if ETRAXFS
-       default "0x00000000" if !ETRAXFS
-       help
-         This is a bitmask with information of what bits in PB
-         that a user can change direction on using ioctl's.
-         Bit set = changeable.
-         You probably want 0 here, but it depends on your hardware.
-
-config ETRAX_PB_CHANGEABLE_BITS
-       hex "PB user changeable bits mask"
-       depends on ETRAX_GPIO
-       default "0x00000" if ETRAXFS
-       default "0x00000000" if !ETRAXFS
-       help
-         This is a bitmask with information of what bits in PB
-         that a user can change the value on using ioctl's.
-         Bit set = changeable.
-
-config ETRAX_PC_CHANGEABLE_DIR
-       hex "PC user changeable dir mask"
-       depends on ETRAX_GPIO
-       default "0x00000" if ETRAXFS
-       default "0x00000000" if !ETRAXFS
-       help
-         This is a bitmask with information of what bits in PC
-         that a user can change direction on using ioctl's.
-         Bit set = changeable.
-         You probably want 0 here, but it depends on your hardware.
-
-config ETRAX_PC_CHANGEABLE_BITS
-       hex "PC user changeable bits mask"
-       depends on ETRAX_GPIO
-       default "0x00000" if ETRAXFS
-       default "0x00000000" if !ETRAXFS
-       help
-         This is a bitmask with information of what bits in PC
-         that a user can change the value on using ioctl's.
-         Bit set = changeable.
-
-config ETRAX_PD_CHANGEABLE_DIR
-       hex "PD user changeable dir mask"
-       depends on ETRAX_GPIO && ETRAXFS
-       default "0x00000"
-       help
-         This is a bitmask with information of what bits in PD
-         that a user can change direction on using ioctl's.
-         Bit set = changeable.
-         You probably want 0x00000 here, but it depends on your hardware.
-
-config ETRAX_PD_CHANGEABLE_BITS
-       hex "PD user changeable bits mask"
-       depends on ETRAX_GPIO && ETRAXFS
-       default "0x00000"
-       help
-         This is a bitmask (18 bits) with information of what bits in PD
-         that a user can change the value on using ioctl's.
-         Bit set = changeable.
-
-config ETRAX_PE_CHANGEABLE_DIR
-       hex "PE user changeable dir mask"
-       depends on ETRAX_GPIO && ETRAXFS
-       default "0x00000"
-       help
-         This is a bitmask (18 bits) with information of what bits in PE
-         that a user can change direction on using ioctl's.
-         Bit set = changeable.
-         You probably want 0x00000 here, but it depends on your hardware.
-
-config ETRAX_PE_CHANGEABLE_BITS
-       hex "PE user changeable bits mask"
-       depends on ETRAX_GPIO && ETRAXFS
-       default "0x00000"
-       help
-         This is a bitmask (18 bits) with information of what bits in PE
-         that a user can change the value on using ioctl's.
-         Bit set = changeable.
-
-config ETRAX_PV_CHANGEABLE_DIR
-       hex "PV user changeable dir mask"
-       depends on ETRAX_VIRTUAL_GPIO
-       default "0x0000"
-       help
-         This is a bitmask (16 bits) with information of what bits in PV
-         that a user can change direction on using ioctl's.
-         Bit set = changeable.
-         You probably want 0x0000 here, but it depends on your hardware.
-
-config ETRAX_PV_CHANGEABLE_BITS
-       hex "PV user changeable bits mask"
-       depends on ETRAX_VIRTUAL_GPIO
-       default "0x0000"
-       help
-         This is a bitmask (16 bits) with information of what bits in PV
-         that a user can change the value on using ioctl's.
-         Bit set = changeable.
-
 config ETRAX_CARDBUS
         bool "Cardbus support"
         depends on ETRAX_ARCH_V32
index 15fbfefced2c43f6998198162a069aa7070073c1..b5a75fdce77bef639d65ce62ec824059fc541af5 100644 (file)
@@ -7,6 +7,5 @@ obj-$(CONFIG_ETRAX_AXISFLASHMAP)        += axisflashmap.o
 obj-$(CONFIG_ETRAXFS)                   += mach-fs/
 obj-$(CONFIG_CRIS_MACH_ARTPEC3)         += mach-a3/
 obj-$(CONFIG_ETRAX_IOP_FW_LOAD)         += iop_fw_load.o
-obj-$(CONFIG_ETRAX_I2C)                        += i2c.o
 obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o
 obj-$(CONFIG_PCI)                      += pci/
index 5387424683ccce2b26e912474a0e2974e6abc558..c6309a182f467e3cdea78d5545f671bf3d91103a 100644 (file)
@@ -361,7 +361,7 @@ static int __init init_axis_flash(void)
 
 #if 0 /* Dump flash memory so we can see what is going on */
        if (main_mtd) {
-               int sectoraddr, i;
+               int sectoraddr;
                for (sectoraddr = 0; sectoraddr < 2*65536+4096;
                                sectoraddr += PAGESIZE) {
                        main_mtd->read(main_mtd, sectoraddr, PAGESIZE, &len,
@@ -369,21 +369,7 @@ static int __init init_axis_flash(void)
                        printk(KERN_INFO
                               "Sector at %d (length %d):\n",
                               sectoraddr, len);
-                       for (i = 0; i < PAGESIZE; i += 16) {
-                               printk(KERN_INFO
-                                      "%02x %02x %02x %02x "
-                                      "%02x %02x %02x %02x "
-                                      "%02x %02x %02x %02x "
-                                      "%02x %02x %02x %02x\n",
-                                      page[i] & 255, page[i+1] & 255,
-                                      page[i+2] & 255, page[i+3] & 255,
-                                      page[i+4] & 255, page[i+5] & 255,
-                                      page[i+6] & 255, page[i+7] & 255,
-                                      page[i+8] & 255, page[i+9] & 255,
-                                      page[i+10] & 255, page[i+11] & 255,
-                                      page[i+12] & 255, page[i+13] & 255,
-                                      page[i+14] & 255, page[i+15] & 255);
-                       }
+                       print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, page, PAGESIZE, false);
                }
        }
 #endif
@@ -417,25 +403,11 @@ static int __init init_axis_flash(void)
 
 #if 0 /* Dump partition table so we can see what is going on */
                printk(KERN_INFO
-                      "axisflashmap: flash read %d bytes at 0x%08x, data: "
-                      "%02x %02x %02x %02x %02x %02x %02x %02x\n",
-                      len, CONFIG_ETRAX_PTABLE_SECTOR,
-                      page[0] & 255, page[1] & 255,
-                      page[2] & 255, page[3] & 255,
-                      page[4] & 255, page[5] & 255,
-                      page[6] & 255, page[7] & 255);
+                      "axisflashmap: flash read %d bytes at 0x%08x, data: %8ph\n",
+                      len, CONFIG_ETRAX_PTABLE_SECTOR, page);
                printk(KERN_INFO
-                      "axisflashmap: partition table offset %d, data: "
-                      "%02x %02x %02x %02x %02x %02x %02x %02x\n",
-                      PARTITION_TABLE_OFFSET,
-                      page[PARTITION_TABLE_OFFSET+0] & 255,
-                      page[PARTITION_TABLE_OFFSET+1] & 255,
-                      page[PARTITION_TABLE_OFFSET+2] & 255,
-                      page[PARTITION_TABLE_OFFSET+3] & 255,
-                      page[PARTITION_TABLE_OFFSET+4] & 255,
-                      page[PARTITION_TABLE_OFFSET+5] & 255,
-                      page[PARTITION_TABLE_OFFSET+6] & 255,
-                      page[PARTITION_TABLE_OFFSET+7] & 255);
+                      "axisflashmap: partition table offset %d, data: %8ph\n",
+                      PARTITION_TABLE_OFFSET, page + PARTITION_TABLE_OFFSET);
 #endif
        }
 
diff --git a/arch/cris/arch-v32/drivers/i2c.c b/arch/cris/arch-v32/drivers/i2c.c
deleted file mode 100644 (file)
index 3b2c82c..0000000
+++ /dev/null
@@ -1,751 +0,0 @@
-/*!***************************************************************************
-*!
-*! FILE NAME  : i2c.c
-*!
-*! DESCRIPTION: implements an interface for IIC/I2C, both directly from other
-*!              kernel modules (i2c_writereg/readreg) and from userspace using
-*!              ioctl()'s
-*!
-*! Nov 30 1998  Torbjorn Eliasson  Initial version.
-*!              Bjorn Wesen        Elinux kernel version.
-*! Jan 14 2000  Johan Adolfsson    Fixed PB shadow register stuff -
-*!                                 don't use PB_I2C if DS1302 uses same bits,
-*!                                 use PB.
-*| June 23 2003 Pieter Grimmerink  Added 'i2c_sendnack'. i2c_readreg now
-*|                                 generates nack on last received byte,
-*|                                 instead of ack.
-*|                                 i2c_getack changed data level while clock
-*|                                 was high, causing DS75 to see  a stop condition
-*!
-*! ---------------------------------------------------------------------------
-*!
-*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN
-*!
-*!***************************************************************************/
-
-/****************** INCLUDE FILES SECTION ***********************************/
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/mutex.h>
-
-#include <asm/etraxi2c.h>
-
-#include <asm/io.h>
-#include <asm/delay.h>
-
-#include "i2c.h"
-
-/****************** I2C DEFINITION SECTION *************************/
-
-#define D(x)
-
-#define I2C_MAJOR 123  /* LOCAL/EXPERIMENTAL */
-static DEFINE_MUTEX(i2c_mutex);
-static const char i2c_name[] = "i2c";
-
-#define CLOCK_LOW_TIME            8
-#define CLOCK_HIGH_TIME           8
-#define START_CONDITION_HOLD_TIME 8
-#define STOP_CONDITION_HOLD_TIME  8
-#define ENABLE_OUTPUT 0x01
-#define ENABLE_INPUT 0x00
-#define I2C_CLOCK_HIGH 1
-#define I2C_CLOCK_LOW 0
-#define I2C_DATA_HIGH 1
-#define I2C_DATA_LOW 0
-
-#define i2c_enable()
-#define i2c_disable()
-
-/* enable or disable output-enable, to select output or input on the i2c bus */
-
-#define i2c_dir_out() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_out)
-#define i2c_dir_in() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_in)
-
-/* control the i2c clock and data signals */
-
-#define i2c_clk(x) crisv32_io_set(&cris_i2c_clk, x)
-#define i2c_data(x) crisv32_io_set(&cris_i2c_data, x)
-
-/* read a bit from the i2c interface */
-
-#define i2c_getbit() crisv32_io_rd(&cris_i2c_data)
-
-#define i2c_delay(usecs) udelay(usecs)
-
-static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */
-
-/****************** VARIABLE SECTION ************************************/
-
-static struct crisv32_iopin cris_i2c_clk;
-static struct crisv32_iopin cris_i2c_data;
-
-/****************** FUNCTION DEFINITION SECTION *************************/
-
-
-/* generate i2c start condition */
-
-void
-i2c_start(void)
-{
-       /*
-        * SCL=1 SDA=1
-        */
-       i2c_dir_out();
-       i2c_delay(CLOCK_HIGH_TIME/6);
-       i2c_data(I2C_DATA_HIGH);
-       i2c_clk(I2C_CLOCK_HIGH);
-       i2c_delay(CLOCK_HIGH_TIME);
-       /*
-        * SCL=1 SDA=0
-        */
-       i2c_data(I2C_DATA_LOW);
-       i2c_delay(START_CONDITION_HOLD_TIME);
-       /*
-        * SCL=0 SDA=0
-        */
-       i2c_clk(I2C_CLOCK_LOW);
-       i2c_delay(CLOCK_LOW_TIME);
-}
-
-/* generate i2c stop condition */
-
-void
-i2c_stop(void)
-{
-       i2c_dir_out();
-
-       /*
-        * SCL=0 SDA=0
-        */
-       i2c_clk(I2C_CLOCK_LOW);
-       i2c_data(I2C_DATA_LOW);
-       i2c_delay(CLOCK_LOW_TIME*2);
-       /*
-        * SCL=1 SDA=0
-        */
-       i2c_clk(I2C_CLOCK_HIGH);
-       i2c_delay(CLOCK_HIGH_TIME*2);
-       /*
-        * SCL=1 SDA=1
-        */
-       i2c_data(I2C_DATA_HIGH);
-       i2c_delay(STOP_CONDITION_HOLD_TIME);
-
-       i2c_dir_in();
-}
-
-/* write a byte to the i2c interface */
-
-void
-i2c_outbyte(unsigned char x)
-{
-       int i;
-
-       i2c_dir_out();
-
-       for (i = 0; i < 8; i++) {
-               if (x & 0x80) {
-                       i2c_data(I2C_DATA_HIGH);
-               } else {
-                       i2c_data(I2C_DATA_LOW);
-               }
-
-               i2c_delay(CLOCK_LOW_TIME/2);
-               i2c_clk(I2C_CLOCK_HIGH);
-               i2c_delay(CLOCK_HIGH_TIME);
-               i2c_clk(I2C_CLOCK_LOW);
-               i2c_delay(CLOCK_LOW_TIME/2);
-               x <<= 1;
-       }
-       i2c_data(I2C_DATA_LOW);
-       i2c_delay(CLOCK_LOW_TIME/2);
-
-       /*
-        * enable input
-        */
-       i2c_dir_in();
-}
-
-/* read a byte from the i2c interface */
-
-unsigned char
-i2c_inbyte(void)
-{
-       unsigned char aBitByte = 0;
-       int i;
-
-       /* Switch off I2C to get bit */
-       i2c_disable();
-       i2c_dir_in();
-       i2c_delay(CLOCK_HIGH_TIME/2);
-
-       /* Get bit */
-       aBitByte |= i2c_getbit();
-
-       /* Enable I2C */
-       i2c_enable();
-       i2c_delay(CLOCK_LOW_TIME/2);
-
-       for (i = 1; i < 8; i++) {
-               aBitByte <<= 1;
-               /* Clock pulse */
-               i2c_clk(I2C_CLOCK_HIGH);
-               i2c_delay(CLOCK_HIGH_TIME);
-               i2c_clk(I2C_CLOCK_LOW);
-               i2c_delay(CLOCK_LOW_TIME);
-
-               /* Switch off I2C to get bit */
-               i2c_disable();
-               i2c_dir_in();
-               i2c_delay(CLOCK_HIGH_TIME/2);
-
-               /* Get bit */
-               aBitByte |= i2c_getbit();
-
-               /* Enable I2C */
-               i2c_enable();
-               i2c_delay(CLOCK_LOW_TIME/2);
-       }
-       i2c_clk(I2C_CLOCK_HIGH);
-       i2c_delay(CLOCK_HIGH_TIME);
-
-       /*
-        * we leave the clock low, getbyte is usually followed
-        * by sendack/nack, they assume the clock to be low
-        */
-       i2c_clk(I2C_CLOCK_LOW);
-       return aBitByte;
-}
-
-/*#---------------------------------------------------------------------------
-*#
-*# FUNCTION NAME: i2c_getack
-*#
-*# DESCRIPTION  : checks if ack was received from ic2
-*#
-*#--------------------------------------------------------------------------*/
-
-int
-i2c_getack(void)
-{
-       int ack = 1;
-       /*
-        * enable output
-        */
-       i2c_dir_out();
-       /*
-        * Release data bus by setting
-        * data high
-        */
-       i2c_data(I2C_DATA_HIGH);
-       /*
-        * enable input
-        */
-       i2c_dir_in();
-       i2c_delay(CLOCK_HIGH_TIME/4);
-       /*
-        * generate ACK clock pulse
-        */
-       i2c_clk(I2C_CLOCK_HIGH);
-#if 0
-       /*
-        * Use PORT PB instead of I2C
-        * for input. (I2C not working)
-        */
-       i2c_clk(1);
-       i2c_data(1);
-       /*
-        * switch off I2C
-        */
-       i2c_data(1);
-       i2c_disable();
-       i2c_dir_in();
-#endif
-
-       /*
-        * now wait for ack
-        */
-       i2c_delay(CLOCK_HIGH_TIME/2);
-       /*
-        * check for ack
-        */
-       if (i2c_getbit())
-               ack = 0;
-       i2c_delay(CLOCK_HIGH_TIME/2);
-       if (!ack) {
-               if (!i2c_getbit()) /* receiver pulld SDA low */
-                       ack = 1;
-               i2c_delay(CLOCK_HIGH_TIME/2);
-       }
-
-   /*
-    * our clock is high now, make sure data is low
-    * before we enable our output. If we keep data high
-    * and enable output, we would generate a stop condition.
-    */
-#if 0
-   i2c_data(I2C_DATA_LOW);
-
-       /*
-        * end clock pulse
-        */
-       i2c_enable();
-       i2c_dir_out();
-#endif
-       i2c_clk(I2C_CLOCK_LOW);
-       i2c_delay(CLOCK_HIGH_TIME/4);
-       /*
-        * enable output
-        */
-       i2c_dir_out();
-       /*
-        * remove ACK clock pulse
-        */
-       i2c_data(I2C_DATA_HIGH);
-       i2c_delay(CLOCK_LOW_TIME/2);
-       return ack;
-}
-
-/*#---------------------------------------------------------------------------
-*#
-*# FUNCTION NAME: I2C::sendAck
-*#
-*# DESCRIPTION  : Send ACK on received data
-*#
-*#--------------------------------------------------------------------------*/
-void
-i2c_sendack(void)
-{
-       /*
-        * enable output
-        */
-       i2c_delay(CLOCK_LOW_TIME);
-       i2c_dir_out();
-       /*
-        * set ack pulse high
-        */
-       i2c_data(I2C_DATA_LOW);
-       /*
-        * generate clock pulse
-        */
-       i2c_delay(CLOCK_HIGH_TIME/6);
-       i2c_clk(I2C_CLOCK_HIGH);
-       i2c_delay(CLOCK_HIGH_TIME);
-       i2c_clk(I2C_CLOCK_LOW);
-       i2c_delay(CLOCK_LOW_TIME/6);
-       /*
-        * reset data out
-        */
-       i2c_data(I2C_DATA_HIGH);
-       i2c_delay(CLOCK_LOW_TIME);
-
-       i2c_dir_in();
-}
-
-/*#---------------------------------------------------------------------------
-*#
-*# FUNCTION NAME: i2c_sendnack
-*#
-*# DESCRIPTION  : Sends NACK on received data
-*#
-*#--------------------------------------------------------------------------*/
-void
-i2c_sendnack(void)
-{
-       /*
-        * enable output
-        */
-       i2c_delay(CLOCK_LOW_TIME);
-       i2c_dir_out();
-       /*
-        * set data high
-        */
-       i2c_data(I2C_DATA_HIGH);
-       /*
-        * generate clock pulse
-        */
-       i2c_delay(CLOCK_HIGH_TIME/6);
-       i2c_clk(I2C_CLOCK_HIGH);
-       i2c_delay(CLOCK_HIGH_TIME);
-       i2c_clk(I2C_CLOCK_LOW);
-       i2c_delay(CLOCK_LOW_TIME);
-
-       i2c_dir_in();
-}
-
-/*#---------------------------------------------------------------------------
-*#
-*# FUNCTION NAME: i2c_write
-*#
-*# DESCRIPTION  : Writes a value to an I2C device
-*#
-*#--------------------------------------------------------------------------*/
-int
-i2c_write(unsigned char theSlave, void *data, size_t nbytes)
-{
-       int error, cntr = 3;
-       unsigned char bytes_wrote = 0;
-       unsigned char value;
-       unsigned long flags;
-
-       spin_lock_irqsave(&i2c_lock, flags);
-
-       do {
-               error = 0;
-
-               i2c_start();
-               /*
-                * send slave address
-                */
-               i2c_outbyte((theSlave & 0xfe));
-               /*
-                * wait for ack
-                */
-               if (!i2c_getack())
-                       error = 1;
-               /*
-                * send data
-                */
-               for (bytes_wrote = 0; bytes_wrote < nbytes; bytes_wrote++) {
-                       memcpy(&value, data + bytes_wrote, sizeof value);
-                       i2c_outbyte(value);
-                       /*
-                        * now it's time to wait for ack
-                        */
-                       if (!i2c_getack())
-                               error |= 4;
-               }
-               /*
-                * end byte stream
-                */
-               i2c_stop();
-
-       } while (error && cntr--);
-
-       i2c_delay(CLOCK_LOW_TIME);
-
-       spin_unlock_irqrestore(&i2c_lock, flags);
-
-       return -error;
-}
-
-/*#---------------------------------------------------------------------------
-*#
-*# FUNCTION NAME: i2c_read
-*#
-*# DESCRIPTION  : Reads a value from an I2C device
-*#
-*#--------------------------------------------------------------------------*/
-int
-i2c_read(unsigned char theSlave, void *data, size_t nbytes)
-{
-       unsigned char b = 0;
-       unsigned char bytes_read = 0;
-       int error, cntr = 3;
-       unsigned long flags;
-
-       spin_lock_irqsave(&i2c_lock, flags);
-
-       do {
-               error = 0;
-               memset(data, 0, nbytes);
-               /*
-                * generate start condition
-                */
-               i2c_start();
-               /*
-                * send slave address
-                */
-               i2c_outbyte((theSlave | 0x01));
-               /*
-                * wait for ack
-                */
-               if (!i2c_getack())
-                       error = 1;
-               /*
-                * fetch data
-                */
-               for (bytes_read = 0; bytes_read < nbytes; bytes_read++) {
-                       b = i2c_inbyte();
-                       memcpy(data + bytes_read, &b, sizeof b);
-
-                       if (bytes_read < (nbytes - 1))
-                               i2c_sendack();
-               }
-               /*
-                * last received byte needs to be nacked
-                * instead of acked
-                */
-               i2c_sendnack();
-               /*
-                * end sequence
-                */
-               i2c_stop();
-       } while (error && cntr--);
-
-       spin_unlock_irqrestore(&i2c_lock, flags);
-
-       return -error;
-}
-
-/*#---------------------------------------------------------------------------
-*#
-*# FUNCTION NAME: i2c_writereg
-*#
-*# DESCRIPTION  : Writes a value to an I2C device
-*#
-*#--------------------------------------------------------------------------*/
-int
-i2c_writereg(unsigned char theSlave, unsigned char theReg,
-            unsigned char theValue)
-{
-       int error, cntr = 3;
-       unsigned long flags;
-
-       spin_lock_irqsave(&i2c_lock, flags);
-
-       do {
-               error = 0;
-
-               i2c_start();
-               /*
-                * send slave address
-                */
-               i2c_outbyte((theSlave & 0xfe));
-               /*
-                * wait for ack
-                */
-               if(!i2c_getack())
-                       error = 1;
-               /*
-                * now select register
-                */
-               i2c_dir_out();
-               i2c_outbyte(theReg);
-               /*
-                * now it's time to wait for ack
-                */
-               if(!i2c_getack())
-                       error |= 2;
-               /*
-                * send register register data
-                */
-               i2c_outbyte(theValue);
-               /*
-                * now it's time to wait for ack
-                */
-               if(!i2c_getack())
-                       error |= 4;
-               /*
-                * end byte stream
-                */
-               i2c_stop();
-       } while(error && cntr--);
-
-       i2c_delay(CLOCK_LOW_TIME);
-
-       spin_unlock_irqrestore(&i2c_lock, flags);
-
-       return -error;
-}
-
-/*#---------------------------------------------------------------------------
-*#
-*# FUNCTION NAME: i2c_readreg
-*#
-*# DESCRIPTION  : Reads a value from the decoder registers.
-*#
-*#--------------------------------------------------------------------------*/
-unsigned char
-i2c_readreg(unsigned char theSlave, unsigned char theReg)
-{
-       unsigned char b = 0;
-       int error, cntr = 3;
-       unsigned long flags;
-
-       spin_lock_irqsave(&i2c_lock, flags);
-
-       do {
-               error = 0;
-               /*
-                * generate start condition
-                */
-               i2c_start();
-
-               /*
-                * send slave address
-                */
-               i2c_outbyte((theSlave & 0xfe));
-               /*
-                * wait for ack
-                */
-               if(!i2c_getack())
-                       error = 1;
-               /*
-                * now select register
-                */
-               i2c_dir_out();
-               i2c_outbyte(theReg);
-               /*
-                * now it's time to wait for ack
-                */
-               if(!i2c_getack())
-                       error |= 2;
-               /*
-                * repeat start condition
-                */
-               i2c_delay(CLOCK_LOW_TIME);
-               i2c_start();
-               /*
-                * send slave address
-                */
-               i2c_outbyte(theSlave | 0x01);
-               /*
-                * wait for ack
-                */
-               if(!i2c_getack())
-                       error |= 4;
-               /*
-                * fetch register
-                */
-               b = i2c_inbyte();
-               /*
-                * last received byte needs to be nacked
-                * instead of acked
-                */
-               i2c_sendnack();
-               /*
-                * end sequence
-                */
-               i2c_stop();
-
-       } while(error && cntr--);
-
-       spin_unlock_irqrestore(&i2c_lock, flags);
-
-       return b;
-}
-
-static int
-i2c_open(struct inode *inode, struct file *filp)
-{
-       return 0;
-}
-
-static int
-i2c_release(struct inode *inode, struct file *filp)
-{
-       return 0;
-}
-
-/* Main device API. ioctl's to write or read to/from i2c registers.
- */
-
-static long
-i2c_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       int ret;
-       if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) {
-               return -ENOTTY;
-       }
-
-       switch (_IOC_NR(cmd)) {
-               case I2C_WRITEREG:
-                       /* write to an i2c slave */
-                       D(printk("i2cw %d %d %d\n",
-                                I2C_ARGSLAVE(arg),
-                                I2C_ARGREG(arg),
-                                I2C_ARGVALUE(arg)));
-
-                       mutex_lock(&i2c_mutex);
-                       ret = i2c_writereg(I2C_ARGSLAVE(arg),
-                                           I2C_ARGREG(arg),
-                                           I2C_ARGVALUE(arg));
-                       mutex_unlock(&i2c_mutex);
-                       return ret;
-
-               case I2C_READREG:
-               {
-                       unsigned char val;
-                       /* read from an i2c slave */
-                       D(printk("i2cr %d %d ",
-                               I2C_ARGSLAVE(arg),
-                               I2C_ARGREG(arg)));
-                       mutex_lock(&i2c_mutex);
-                       val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg));
-                       mutex_unlock(&i2c_mutex);
-                       D(printk("= %d\n", val));
-                       return val;
-               }
-               default:
-                       return -EINVAL;
-
-       }
-
-       return 0;
-}
-
-static const struct file_operations i2c_fops = {
-       .owner          = THIS_MODULE,
-       .unlocked_ioctl = i2c_ioctl,
-       .open           = i2c_open,
-       .release        = i2c_release,
-       .llseek         = noop_llseek,
-};
-
-static int __init i2c_init(void)
-{
-       static int res;
-       static int first = 1;
-
-       if (!first)
-               return res;
-
-       first = 0;
-
-       /* Setup and enable the DATA and CLK pins */
-
-       res = crisv32_io_get_name(&cris_i2c_data,
-               CONFIG_ETRAX_V32_I2C_DATA_PORT);
-       if (res < 0)
-               return res;
-
-       res = crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_V32_I2C_CLK_PORT);
-       crisv32_io_set_dir(&cris_i2c_clk, crisv32_io_dir_out);
-
-       return res;
-}
-
-
-static int __init i2c_register(void)
-{
-       int res;
-
-       res = i2c_init();
-       if (res < 0)
-               return res;
-
-       /* register char device */
-
-       res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops);
-       if (res < 0) {
-               printk(KERN_ERR "i2c: couldn't get a major number.\n");
-               return res;
-       }
-
-       printk(KERN_INFO
-               "I2C driver v2.2, (c) 1999-2007 Axis Communications AB\n");
-
-       return 0;
-}
-/* this makes sure that i2c_init is called during boot */
-module_init(i2c_register);
-
-/****************** END OF FILE i2c.c ********************************/
diff --git a/arch/cris/arch-v32/drivers/i2c.h b/arch/cris/arch-v32/drivers/i2c.h
deleted file mode 100644 (file)
index d9cc856..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-
-#include <linux/init.h>
-
-/* High level I2C actions */
-int i2c_write(unsigned char theSlave, void *data, size_t nbytes);
-int i2c_read(unsigned char theSlave, void *data, size_t nbytes);
-int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
-unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg);
-
-/* Low level I2C */
-void i2c_start(void);
-void i2c_stop(void);
-void i2c_outbyte(unsigned char x);
-unsigned char i2c_inbyte(void);
-int i2c_getack(void);
-void i2c_sendack(void);
index 5c6d2a2a080ee4bbbbeca7c6bef44899389179c5..59028d0b981c094dd67a3570ed583d6bfa12ed75 100644 (file)
@@ -3,4 +3,3 @@
 #
 
 obj-$(CONFIG_ETRAX_NANDFLASH)   += nandflash.o
-obj-$(CONFIG_ETRAX_GPIO)        += gpio.o
diff --git a/arch/cris/arch-v32/drivers/mach-a3/gpio.c b/arch/cris/arch-v32/drivers/mach-a3/gpio.c
deleted file mode 100644 (file)
index c92e1da..0000000
+++ /dev/null
@@ -1,999 +0,0 @@
-/*
- * Artec-3 general port I/O device
- *
- * Copyright (c) 2007 Axis Communications AB
- *
- * Authors:    Bjorn Wesen      (initial version)
- *             Ola Knutsson     (LED handling)
- *             Johan Adolfsson  (read/set directions, write, port G,
- *                               port to ETRAX FS.
- *             Ricard Wanderlof (PWM for Artpec-3)
- *
- */
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/string.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-
-#include <asm/etraxgpio.h>
-#include <hwregs/reg_map.h>
-#include <hwregs/reg_rdwr.h>
-#include <hwregs/gio_defs.h>
-#include <hwregs/intr_vect_defs.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <mach/pinmux.h>
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-#include "../i2c.h"
-
-#define VIRT_I2C_ADDR 0x40
-#endif
-
-/* The following gio ports on ARTPEC-3 is available:
- * pa 32 bits
- * pb 32 bits
- * pc 16 bits
- * each port has a rw_px_dout, r_px_din and rw_px_oe register.
- */
-
-#define GPIO_MAJOR 120  /* experimental MAJOR number */
-
-#define I2C_INTERRUPT_BITS 0x300 /* i2c0_done and i2c1_done bits */
-
-#define D(x)
-
-#if 0
-static int dp_cnt;
-#define DP(x) \
-       do { \
-               dp_cnt++; \
-               if (dp_cnt % 1000 == 0) \
-                       x; \
-       } while (0)
-#else
-#define DP(x)
-#endif
-
-static DEFINE_MUTEX(gpio_mutex);
-static char gpio_name[] = "etrax gpio";
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-static int virtual_gpio_ioctl(struct file *file, unsigned int cmd,
-                             unsigned long arg);
-#endif
-static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
-static ssize_t gpio_write(struct file *file, const char __user *buf,
-       size_t count, loff_t *off);
-static int gpio_open(struct inode *inode, struct file *filp);
-static int gpio_release(struct inode *inode, struct file *filp);
-static unsigned int gpio_poll(struct file *filp,
-       struct poll_table_struct *wait);
-
-/* private data per open() of this driver */
-
-struct gpio_private {
-       struct gpio_private *next;
-       /* The IO_CFG_WRITE_MODE_VALUE only support 8 bits: */
-       unsigned char clk_mask;
-       unsigned char data_mask;
-       unsigned char write_msb;
-       unsigned char pad1;
-       /* These fields are generic */
-       unsigned long highalarm, lowalarm;
-       wait_queue_head_t alarm_wq;
-       int minor;
-};
-
-static void gpio_set_alarm(struct gpio_private *priv);
-static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
-static int gpio_pwm_ioctl(struct gpio_private *priv, unsigned int cmd,
-       unsigned long arg);
-
-
-/* linked list of alarms to check for */
-
-static struct gpio_private *alarmlist;
-
-static int wanted_interrupts;
-
-static DEFINE_SPINLOCK(gpio_lock);
-
-#define NUM_PORTS (GPIO_MINOR_LAST+1)
-#define GIO_REG_RD_ADDR(reg) \
-       (unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg)
-#define GIO_REG_WR_ADDR(reg) \
-       (unsigned long *)(regi_gio + REG_WR_ADDR_gio_##reg)
-static unsigned long led_dummy;
-static unsigned long port_d_dummy;     /* Only input on Artpec-3 */
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-static unsigned long port_e_dummy;     /* Non existent on Artpec-3 */
-static unsigned long virtual_dummy;
-static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE;
-static unsigned short cached_virtual_gpio_read;
-#endif
-
-static unsigned long *data_out[NUM_PORTS] = {
-       GIO_REG_WR_ADDR(rw_pa_dout),
-       GIO_REG_WR_ADDR(rw_pb_dout),
-       &led_dummy,
-       GIO_REG_WR_ADDR(rw_pc_dout),
-       &port_d_dummy,
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       &port_e_dummy,
-       &virtual_dummy,
-#endif
-};
-
-static unsigned long *data_in[NUM_PORTS] = {
-       GIO_REG_RD_ADDR(r_pa_din),
-       GIO_REG_RD_ADDR(r_pb_din),
-       &led_dummy,
-       GIO_REG_RD_ADDR(r_pc_din),
-       GIO_REG_RD_ADDR(r_pd_din),
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       &port_e_dummy,
-       &virtual_dummy,
-#endif
-};
-
-static unsigned long changeable_dir[NUM_PORTS] = {
-       CONFIG_ETRAX_PA_CHANGEABLE_DIR,
-       CONFIG_ETRAX_PB_CHANGEABLE_DIR,
-       0,
-       CONFIG_ETRAX_PC_CHANGEABLE_DIR,
-       0,
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       0,
-       CONFIG_ETRAX_PV_CHANGEABLE_DIR,
-#endif
-};
-
-static unsigned long changeable_bits[NUM_PORTS] = {
-       CONFIG_ETRAX_PA_CHANGEABLE_BITS,
-       CONFIG_ETRAX_PB_CHANGEABLE_BITS,
-       0,
-       CONFIG_ETRAX_PC_CHANGEABLE_BITS,
-       0,
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       0,
-       CONFIG_ETRAX_PV_CHANGEABLE_BITS,
-#endif
-};
-
-static unsigned long *dir_oe[NUM_PORTS] = {
-       GIO_REG_WR_ADDR(rw_pa_oe),
-       GIO_REG_WR_ADDR(rw_pb_oe),
-       &led_dummy,
-       GIO_REG_WR_ADDR(rw_pc_oe),
-       &port_d_dummy,
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       &port_e_dummy,
-       &virtual_rw_pv_oe,
-#endif
-};
-
-static void gpio_set_alarm(struct gpio_private *priv)
-{
-       int bit;
-       int intr_cfg;
-       int mask;
-       int pins;
-       unsigned long flags;
-
-       spin_lock_irqsave(&gpio_lock, flags);
-       intr_cfg = REG_RD_INT(gio, regi_gio, rw_intr_cfg);
-       pins = REG_RD_INT(gio, regi_gio, rw_intr_pins);
-       mask = REG_RD_INT(gio, regi_gio, rw_intr_mask) & I2C_INTERRUPT_BITS;
-
-       for (bit = 0; bit < 32; bit++) {
-               int intr = bit % 8;
-               int pin = bit / 8;
-               if (priv->minor < GPIO_MINOR_LEDS)
-                       pin += priv->minor * 4;
-               else
-                       pin += (priv->minor - 1) * 4;
-
-               if (priv->highalarm & (1<<bit)) {
-                       intr_cfg |= (regk_gio_hi << (intr * 3));
-                       mask |= 1 << intr;
-                       wanted_interrupts = mask & 0xff;
-                       pins |= pin << (intr * 4);
-               } else if (priv->lowalarm & (1<<bit)) {
-                       intr_cfg |= (regk_gio_lo << (intr * 3));
-                       mask |= 1 << intr;
-                       wanted_interrupts = mask & 0xff;
-                       pins |= pin << (intr * 4);
-               }
-       }
-
-       REG_WR_INT(gio, regi_gio, rw_intr_cfg, intr_cfg);
-       REG_WR_INT(gio, regi_gio, rw_intr_pins, pins);
-       REG_WR_INT(gio, regi_gio, rw_intr_mask, mask);
-
-       spin_unlock_irqrestore(&gpio_lock, flags);
-}
-
-static unsigned int gpio_poll(struct file *file, struct poll_table_struct *wait)
-{
-       unsigned int mask = 0;
-       struct gpio_private *priv = file->private_data;
-       unsigned long data;
-       unsigned long tmp;
-
-       if (priv->minor >= GPIO_MINOR_PWM0 &&
-           priv->minor <= GPIO_MINOR_LAST_PWM)
-               return 0;
-
-       poll_wait(file, &priv->alarm_wq, wait);
-       if (priv->minor <= GPIO_MINOR_D) {
-               data = readl(data_in[priv->minor]);
-               REG_WR_INT(gio, regi_gio, rw_ack_intr, wanted_interrupts);
-               tmp = REG_RD_INT(gio, regi_gio, rw_intr_mask);
-               tmp &= I2C_INTERRUPT_BITS;
-               tmp |= wanted_interrupts;
-               REG_WR_INT(gio, regi_gio, rw_intr_mask, tmp);
-       } else
-               return 0;
-
-       if ((data & priv->highalarm) || (~data & priv->lowalarm))
-               mask = POLLIN|POLLRDNORM;
-
-       DP(printk(KERN_DEBUG "gpio_poll ready: mask 0x%08X\n", mask));
-       return mask;
-}
-
-static irqreturn_t gpio_interrupt(int irq, void *dev_id)
-{
-       reg_gio_rw_intr_mask intr_mask;
-       reg_gio_r_masked_intr masked_intr;
-       reg_gio_rw_ack_intr ack_intr;
-       unsigned long flags;
-       unsigned long tmp;
-       unsigned long tmp2;
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       unsigned char enable_gpiov_ack = 0;
-#endif
-
-       /* Find what PA interrupts are active */
-       masked_intr = REG_RD(gio, regi_gio, r_masked_intr);
-       tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr);
-
-       /* Find those that we have enabled */
-       spin_lock_irqsave(&gpio_lock, flags);
-       tmp &= wanted_interrupts;
-       spin_unlock_irqrestore(&gpio_lock, flags);
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       /* Something changed on virtual GPIO. Interrupt is acked by
-        * reading the device.
-        */
-       if (tmp & (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN)) {
-               i2c_read(VIRT_I2C_ADDR, (void *)&cached_virtual_gpio_read,
-                       sizeof(cached_virtual_gpio_read));
-               enable_gpiov_ack = 1;
-       }
-#endif
-
-       /* Ack them */
-       ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp);
-       REG_WR(gio, regi_gio, rw_ack_intr, ack_intr);
-
-       /* Disable those interrupts.. */
-       intr_mask = REG_RD(gio, regi_gio, rw_intr_mask);
-       tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask);
-       tmp2 &= ~tmp;
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       /* Do not disable interrupt on virtual GPIO. Changes on virtual
-        * pins are only noticed by an interrupt.
-        */
-       if (enable_gpiov_ack)
-               tmp2 |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
-#endif
-       intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2);
-       REG_WR(gio, regi_gio, rw_intr_mask, intr_mask);
-
-       return IRQ_RETVAL(tmp);
-}
-
-static void gpio_write_bit(unsigned long *port, unsigned char data, int bit,
-       unsigned char clk_mask, unsigned char data_mask)
-{
-       unsigned long shadow = readl(port) & ~clk_mask;
-       writel(shadow, port);
-       if (data & 1 << bit)
-               shadow |= data_mask;
-       else
-               shadow &= ~data_mask;
-       writel(shadow, port);
-       /* For FPGA: min 5.0ns (DCC) before CCLK high */
-       shadow |= clk_mask;
-       writel(shadow, port);
-}
-
-static void gpio_write_byte(struct gpio_private *priv, unsigned long *port,
-               unsigned char data)
-{
-       int i;
-
-       if (priv->write_msb)
-               for (i = 7; i >= 0; i--)
-                       gpio_write_bit(port, data, i, priv->clk_mask,
-                               priv->data_mask);
-       else
-               for (i = 0; i <= 7; i++)
-                       gpio_write_bit(port, data, i, priv->clk_mask,
-                               priv->data_mask);
-}
-
-
-static ssize_t gpio_write(struct file *file, const char __user *buf,
-       size_t count, loff_t *off)
-{
-       struct gpio_private *priv = file->private_data;
-       unsigned long flags;
-       ssize_t retval = count;
-       /* Only bits 0-7 may be used for write operations but allow all
-          devices except leds... */
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       if (priv->minor == GPIO_MINOR_V)
-               return -EFAULT;
-#endif
-       if (priv->minor == GPIO_MINOR_LEDS)
-               return -EFAULT;
-
-       if (priv->minor >= GPIO_MINOR_PWM0 &&
-           priv->minor <= GPIO_MINOR_LAST_PWM)
-               return -EFAULT;
-
-       if (!access_ok(VERIFY_READ, buf, count))
-               return -EFAULT;
-
-       /* It must have been configured using the IO_CFG_WRITE_MODE */
-       /* Perhaps a better error code? */
-       if (priv->clk_mask == 0 || priv->data_mask == 0)
-               return -EPERM;
-
-       D(printk(KERN_DEBUG "gpio_write: %lu to data 0x%02X clk 0x%02X "
-               "msb: %i\n",
-               count, priv->data_mask, priv->clk_mask, priv->write_msb));
-
-       spin_lock_irqsave(&gpio_lock, flags);
-
-       while (count--)
-               gpio_write_byte(priv, data_out[priv->minor], *buf++);
-
-       spin_unlock_irqrestore(&gpio_lock, flags);
-       return retval;
-}
-
-static int gpio_open(struct inode *inode, struct file *filp)
-{
-       struct gpio_private *priv;
-       int p = iminor(inode);
-
-       if (p > GPIO_MINOR_LAST_PWM ||
-           (p > GPIO_MINOR_LAST && p < GPIO_MINOR_PWM0))
-               return -EINVAL;
-
-       priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL);
-
-       if (!priv)
-               return -ENOMEM;
-
-       mutex_lock(&gpio_mutex);
-       memset(priv, 0, sizeof(*priv));
-
-       priv->minor = p;
-       filp->private_data = priv;
-
-       /* initialize the io/alarm struct, not for PWM ports though  */
-       if (p <= GPIO_MINOR_LAST) {
-
-               priv->clk_mask = 0;
-               priv->data_mask = 0;
-               priv->highalarm = 0;
-               priv->lowalarm = 0;
-
-               init_waitqueue_head(&priv->alarm_wq);
-
-               /* link it into our alarmlist */
-               spin_lock_irq(&gpio_lock);
-               priv->next = alarmlist;
-               alarmlist = priv;
-               spin_unlock_irq(&gpio_lock);
-       }
-
-       mutex_unlock(&gpio_mutex);
-       return 0;
-}
-
-static int gpio_release(struct inode *inode, struct file *filp)
-{
-       struct gpio_private *p;
-       struct gpio_private *todel;
-       /* local copies while updating them: */
-       unsigned long a_high, a_low;
-
-       /* prepare to free private structure */
-       todel = filp->private_data;
-
-       /* unlink from alarmlist - only for non-PWM ports though */
-       if (todel->minor <= GPIO_MINOR_LAST) {
-               spin_lock_irq(&gpio_lock);
-               p = alarmlist;
-
-               if (p == todel)
-                       alarmlist = todel->next;
-                else {
-                       while (p->next != todel)
-                               p = p->next;
-                       p->next = todel->next;
-               }
-
-               /* Check if there are still any alarms set */
-               p = alarmlist;
-               a_high = 0;
-               a_low = 0;
-               while (p) {
-                       if (p->minor == GPIO_MINOR_A) {
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-                               p->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
-#endif
-                               a_high |= p->highalarm;
-                               a_low |= p->lowalarm;
-                       }
-
-                       p = p->next;
-               }
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       /* Variable 'a_low' needs to be set here again
-        * to ensure that interrupt for virtual GPIO is handled.
-        */
-               a_low |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
-#endif
-
-               spin_unlock_irq(&gpio_lock);
-       }
-       kfree(todel);
-
-       return 0;
-}
-
-/* Main device API. ioctl's to read/set/clear bits, as well as to
- * set alarms to wait for using a subsequent select().
- */
-
-inline unsigned long setget_input(struct gpio_private *priv, unsigned long arg)
-{
-       /* Set direction 0=unchanged 1=input,
-        * return mask with 1=input
-        */
-       unsigned long flags;
-       unsigned long dir_shadow;
-
-       spin_lock_irqsave(&gpio_lock, flags);
-
-       dir_shadow = readl(dir_oe[priv->minor]) &
-               ~(arg & changeable_dir[priv->minor]);
-       writel(dir_shadow, dir_oe[priv->minor]);
-
-       spin_unlock_irqrestore(&gpio_lock, flags);
-
-       if (priv->minor == GPIO_MINOR_C)
-               dir_shadow ^= 0xFFFF;           /* Only 16 bits */
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       else if (priv->minor == GPIO_MINOR_V)
-               dir_shadow ^= 0xFFFF;           /* Only 16 bits */
-#endif
-       else
-               dir_shadow ^= 0xFFFFFFFF;       /* PA, PB and PD 32 bits */
-
-       return dir_shadow;
-
-} /* setget_input */
-
-static inline unsigned long setget_output(struct gpio_private *priv,
-       unsigned long arg)
-{
-       unsigned long flags;
-       unsigned long dir_shadow;
-
-       spin_lock_irqsave(&gpio_lock, flags);
-
-       dir_shadow = readl(dir_oe[priv->minor]) |
-               (arg & changeable_dir[priv->minor]);
-       writel(dir_shadow, dir_oe[priv->minor]);
-
-       spin_unlock_irqrestore(&gpio_lock, flags);
-       return dir_shadow;
-} /* setget_output */
-
-static long gpio_ioctl_unlocked(struct file *file,
-       unsigned int cmd, unsigned long arg)
-{
-       unsigned long flags;
-       unsigned long val;
-       unsigned long shadow;
-       struct gpio_private *priv = file->private_data;
-
-       if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE)
-               return -ENOTTY;
-
-       /* Check for special ioctl handlers first */
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       if (priv->minor == GPIO_MINOR_V)
-               return virtual_gpio_ioctl(file, cmd, arg);
-#endif
-
-       if (priv->minor == GPIO_MINOR_LEDS)
-               return gpio_leds_ioctl(cmd, arg);
-
-       if (priv->minor >= GPIO_MINOR_PWM0 &&
-           priv->minor <= GPIO_MINOR_LAST_PWM)
-               return gpio_pwm_ioctl(priv, cmd, arg);
-
-       switch (_IOC_NR(cmd)) {
-       case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
-               /* Read the port. */
-               return readl(data_in[priv->minor]);
-       case IO_SETBITS:
-               spin_lock_irqsave(&gpio_lock, flags);
-               /* Set changeable bits with a 1 in arg. */
-               shadow = readl(data_out[priv->minor]) |
-                       (arg & changeable_bits[priv->minor]);
-               writel(shadow, data_out[priv->minor]);
-               spin_unlock_irqrestore(&gpio_lock, flags);
-               break;
-       case IO_CLRBITS:
-               spin_lock_irqsave(&gpio_lock, flags);
-               /* Clear changeable bits with a 1 in arg. */
-               shadow = readl(data_out[priv->minor]) &
-                       ~(arg & changeable_bits[priv->minor]);
-               writel(shadow, data_out[priv->minor]);
-               spin_unlock_irqrestore(&gpio_lock, flags);
-               break;
-       case IO_HIGHALARM:
-               /* Set alarm when bits with 1 in arg go high. */
-               priv->highalarm |= arg;
-               gpio_set_alarm(priv);
-               break;
-       case IO_LOWALARM:
-               /* Set alarm when bits with 1 in arg go low. */
-               priv->lowalarm |= arg;
-               gpio_set_alarm(priv);
-               break;
-       case IO_CLRALARM:
-               /* Clear alarm for bits with 1 in arg. */
-               priv->highalarm &= ~arg;
-               priv->lowalarm  &= ~arg;
-               gpio_set_alarm(priv);
-               break;
-       case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
-               /* Read direction 0=input 1=output */
-               return readl(dir_oe[priv->minor]);
-
-       case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
-               /* Set direction 0=unchanged 1=input,
-                * return mask with 1=input
-                */
-               return setget_input(priv, arg);
-
-       case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
-               /* Set direction 0=unchanged 1=output,
-                * return mask with 1=output
-                */
-               return setget_output(priv, arg);
-
-       case IO_CFG_WRITE_MODE:
-       {
-               int res = -EPERM;
-               unsigned long dir_shadow, clk_mask, data_mask, write_msb;
-
-               clk_mask = arg & 0xFF;
-               data_mask = (arg >> 8) & 0xFF;
-               write_msb = (arg >> 16) & 0x01;
-
-               /* Check if we're allowed to change the bits and
-                * the direction is correct
-                */
-               spin_lock_irqsave(&gpio_lock, flags);
-               dir_shadow = readl(dir_oe[priv->minor]);
-               if ((clk_mask & changeable_bits[priv->minor]) &&
-                   (data_mask & changeable_bits[priv->minor]) &&
-                   (clk_mask & dir_shadow) &&
-                   (data_mask & dir_shadow)) {
-                       priv->clk_mask = clk_mask;
-                       priv->data_mask = data_mask;
-                       priv->write_msb = write_msb;
-                       res = 0;
-               }
-               spin_unlock_irqrestore(&gpio_lock, flags);
-
-               return res;
-       }
-       case IO_READ_INBITS:
-               /* *arg is result of reading the input pins */
-               val = readl(data_in[priv->minor]);
-               if (copy_to_user((void __user *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               return 0;
-       case IO_READ_OUTBITS:
-                /* *arg is result of reading the output shadow */
-               val = *data_out[priv->minor];
-               if (copy_to_user((void __user *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               break;
-       case IO_SETGET_INPUT:
-               /* bits set in *arg is set to input,
-                * *arg updated with current input pins.
-                */
-               if (copy_from_user(&val, (void __user *)arg, sizeof(val)))
-                       return -EFAULT;
-               val = setget_input(priv, val);
-               if (copy_to_user((void __user *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               break;
-       case IO_SETGET_OUTPUT:
-               /* bits set in *arg is set to output,
-                * *arg updated with current output pins.
-                */
-               if (copy_from_user(&val, (void __user *)arg, sizeof(val)))
-                       return -EFAULT;
-               val = setget_output(priv, val);
-               if (copy_to_user((void __user *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               break;
-       default:
-               return -EINVAL;
-       } /* switch */
-
-       return 0;
-}
-
-static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       long ret;
-
-       mutex_lock(&gpio_mutex);
-       ret = gpio_ioctl_unlocked(file, cmd, arg);
-       mutex_unlock(&gpio_mutex);
-
-       return ret;
-}
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-static int virtual_gpio_ioctl(struct file *file, unsigned int cmd,
-       unsigned long arg)
-{
-       unsigned long flags;
-       unsigned short val;
-       unsigned short shadow;
-       struct gpio_private *priv = file->private_data;
-
-       switch (_IOC_NR(cmd)) {
-       case IO_SETBITS:
-               spin_lock_irqsave(&gpio_lock, flags);
-               /* Set changeable bits with a 1 in arg. */
-               i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
-               shadow |= ~readl(dir_oe[priv->minor]) |
-                       (arg & changeable_bits[priv->minor]);
-               i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
-               spin_unlock_irqrestore(&gpio_lock, flags);
-               break;
-       case IO_CLRBITS:
-               spin_lock_irqsave(&gpio_lock, flags);
-               /* Clear changeable bits with a 1 in arg. */
-               i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
-               shadow |= ~readl(dir_oe[priv->minor]) &
-                       ~(arg & changeable_bits[priv->minor]);
-               i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
-               spin_unlock_irqrestore(&gpio_lock, flags);
-               break;
-       case IO_HIGHALARM:
-               /* Set alarm when bits with 1 in arg go high. */
-               priv->highalarm |= arg;
-               break;
-       case IO_LOWALARM:
-               /* Set alarm when bits with 1 in arg go low. */
-               priv->lowalarm |= arg;
-               break;
-       case IO_CLRALARM:
-               /* Clear alarm for bits with 1 in arg. */
-               priv->highalarm &= ~arg;
-               priv->lowalarm  &= ~arg;
-               break;
-       case IO_CFG_WRITE_MODE:
-       {
-               unsigned long dir_shadow;
-               dir_shadow = readl(dir_oe[priv->minor]);
-
-               priv->clk_mask = arg & 0xFF;
-               priv->data_mask = (arg >> 8) & 0xFF;
-               priv->write_msb = (arg >> 16) & 0x01;
-               /* Check if we're allowed to change the bits and
-                * the direction is correct
-                */
-               if (!((priv->clk_mask & changeable_bits[priv->minor]) &&
-                     (priv->data_mask & changeable_bits[priv->minor]) &&
-                     (priv->clk_mask & dir_shadow) &&
-                     (priv->data_mask & dir_shadow))) {
-                       priv->clk_mask = 0;
-                       priv->data_mask = 0;
-                       return -EPERM;
-               }
-               break;
-       }
-       case IO_READ_INBITS:
-               /* *arg is result of reading the input pins */
-               val = cached_virtual_gpio_read & ~readl(dir_oe[priv->minor]);
-               if (copy_to_user((void __user *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               return 0;
-
-       case IO_READ_OUTBITS:
-                /* *arg is result of reading the output shadow */
-               i2c_read(VIRT_I2C_ADDR, (void *)&val, sizeof(val));
-               val &= readl(dir_oe[priv->minor]);
-               if (copy_to_user((void __user *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               break;
-       case IO_SETGET_INPUT:
-       {
-               /* bits set in *arg is set to input,
-                * *arg updated with current input pins.
-                */
-               unsigned short input_mask = ~readl(dir_oe[priv->minor]);
-               if (copy_from_user(&val, (void __user *)arg, sizeof(val)))
-                       return -EFAULT;
-               val = setget_input(priv, val);
-               if (copy_to_user((void __user *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               if ((input_mask & val) != input_mask) {
-                       /* Input pins changed. All ports desired as input
-                        * should be set to logic 1.
-                        */
-                       unsigned short change = input_mask ^ val;
-                       i2c_read(VIRT_I2C_ADDR, (void *)&shadow,
-                               sizeof(shadow));
-                       shadow &= ~change;
-                       shadow |= val;
-                       i2c_write(VIRT_I2C_ADDR, (void *)&shadow,
-                               sizeof(shadow));
-               }
-               break;
-       }
-       case IO_SETGET_OUTPUT:
-               /* bits set in *arg is set to output,
-                * *arg updated with current output pins.
-                */
-               if (copy_from_user(&val, (void __user *)arg, sizeof(val)))
-                       return -EFAULT;
-               val = setget_output(priv, val);
-               if (copy_to_user((void __user *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               break;
-       default:
-               return -EINVAL;
-       } /* switch */
-       return 0;
-}
-#endif /* CONFIG_ETRAX_VIRTUAL_GPIO */
-
-static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
-{
-       unsigned char green;
-       unsigned char red;
-
-       switch (_IOC_NR(cmd)) {
-       case IO_LEDACTIVE_SET:
-               green = ((unsigned char) arg) & 1;
-               red   = (((unsigned char) arg) >> 1) & 1;
-               CRIS_LED_ACTIVE_SET_G(green);
-               CRIS_LED_ACTIVE_SET_R(red);
-               break;
-
-       default:
-               return -EINVAL;
-       } /* switch */
-
-       return 0;
-}
-
-static int gpio_pwm_set_mode(unsigned long arg, int pwm_port)
-{
-       int pinmux_pwm = pinmux_pwm0 + pwm_port;
-       int mode;
-       reg_gio_rw_pwm0_ctrl rw_pwm_ctrl = {
-               .ccd_val = 0,
-               .ccd_override = regk_gio_no,
-               .mode = regk_gio_no
-       };
-       int allocstatus;
-
-       if (get_user(mode, &((struct io_pwm_set_mode *) arg)->mode))
-               return -EFAULT;
-       rw_pwm_ctrl.mode = mode;
-       if (mode != PWM_OFF)
-               allocstatus = crisv32_pinmux_alloc_fixed(pinmux_pwm);
-       else
-               allocstatus = crisv32_pinmux_dealloc_fixed(pinmux_pwm);
-       if (allocstatus)
-               return allocstatus;
-       REG_WRITE(reg_gio_rw_pwm0_ctrl, REG_ADDR(gio, regi_gio, rw_pwm0_ctrl) +
-               12 * pwm_port, rw_pwm_ctrl);
-       return 0;
-}
-
-static int gpio_pwm_set_period(unsigned long arg, int pwm_port)
-{
-       struct io_pwm_set_period periods;
-       reg_gio_rw_pwm0_var rw_pwm_widths;
-
-       if (copy_from_user(&periods, (void __user *)arg, sizeof(periods)))
-               return -EFAULT;
-       if (periods.lo > 8191 || periods.hi > 8191)
-               return -EINVAL;
-       rw_pwm_widths.lo = periods.lo;
-       rw_pwm_widths.hi = periods.hi;
-       REG_WRITE(reg_gio_rw_pwm0_var, REG_ADDR(gio, regi_gio, rw_pwm0_var) +
-               12 * pwm_port, rw_pwm_widths);
-       return 0;
-}
-
-static int gpio_pwm_set_duty(unsigned long arg, int pwm_port)
-{
-       unsigned int duty;
-       reg_gio_rw_pwm0_data rw_pwm_duty;
-
-       if (get_user(duty, &((struct io_pwm_set_duty *) arg)->duty))
-               return -EFAULT;
-       if (duty > 255)
-               return -EINVAL;
-       rw_pwm_duty.data = duty;
-       REG_WRITE(reg_gio_rw_pwm0_data, REG_ADDR(gio, regi_gio, rw_pwm0_data) +
-               12 * pwm_port, rw_pwm_duty);
-       return 0;
-}
-
-static int gpio_pwm_ioctl(struct gpio_private *priv, unsigned int cmd,
-       unsigned long arg)
-{
-       int pwm_port = priv->minor - GPIO_MINOR_PWM0;
-
-       switch (_IOC_NR(cmd)) {
-       case IO_PWM_SET_MODE:
-               return gpio_pwm_set_mode(arg, pwm_port);
-       case IO_PWM_SET_PERIOD:
-               return gpio_pwm_set_period(arg, pwm_port);
-       case IO_PWM_SET_DUTY:
-               return gpio_pwm_set_duty(arg, pwm_port);
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static const struct file_operations gpio_fops = {
-       .owner          = THIS_MODULE,
-       .poll           = gpio_poll,
-       .unlocked_ioctl = gpio_ioctl,
-       .write          = gpio_write,
-       .open           = gpio_open,
-       .release        = gpio_release,
-       .llseek         = noop_llseek,
-};
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-static void __init virtual_gpio_init(void)
-{
-       reg_gio_rw_intr_cfg intr_cfg;
-       reg_gio_rw_intr_mask intr_mask;
-       unsigned short shadow;
-
-       shadow = ~virtual_rw_pv_oe; /* Input ports should be set to logic 1 */
-       shadow |= CONFIG_ETRAX_DEF_GIO_PV_OUT;
-       i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
-
-       /* Set interrupt mask and on what state the interrupt shall trigger.
-        * For virtual gpio the interrupt shall trigger on logic '0'.
-        */
-       intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg);
-       intr_mask = REG_RD(gio, regi_gio, rw_intr_mask);
-
-       switch (CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN) {
-       case 0:
-               intr_cfg.pa0 = regk_gio_lo;
-               intr_mask.pa0 = regk_gio_yes;
-               break;
-       case 1:
-               intr_cfg.pa1 = regk_gio_lo;
-               intr_mask.pa1 = regk_gio_yes;
-               break;
-       case 2:
-               intr_cfg.pa2 = regk_gio_lo;
-               intr_mask.pa2 = regk_gio_yes;
-               break;
-       case 3:
-               intr_cfg.pa3 = regk_gio_lo;
-               intr_mask.pa3 = regk_gio_yes;
-               break;
-       case 4:
-               intr_cfg.pa4 = regk_gio_lo;
-               intr_mask.pa4 = regk_gio_yes;
-               break;
-       case 5:
-               intr_cfg.pa5 = regk_gio_lo;
-               intr_mask.pa5 = regk_gio_yes;
-               break;
-       case 6:
-               intr_cfg.pa6 = regk_gio_lo;
-               intr_mask.pa6 = regk_gio_yes;
-               break;
-       case 7:
-               intr_cfg.pa7 = regk_gio_lo;
-               intr_mask.pa7 = regk_gio_yes;
-               break;
-       }
-
-       REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg);
-       REG_WR(gio, regi_gio, rw_intr_mask, intr_mask);
-}
-#endif
-
-/* main driver initialization routine, called from mem.c */
-
-static int __init gpio_init(void)
-{
-       int res, res2;
-
-       printk(KERN_INFO "ETRAX FS GPIO driver v2.7, (c) 2003-2008 "
-               "Axis Communications AB\n");
-
-       /* do the formalities */
-
-       res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
-       if (res < 0) {
-               printk(KERN_ERR "gpio: couldn't get a major number.\n");
-               return res;
-       }
-
-       /* Clear all leds */
-       CRIS_LED_NETWORK_GRP0_SET(0);
-       CRIS_LED_NETWORK_GRP1_SET(0);
-       CRIS_LED_ACTIVE_SET(0);
-       CRIS_LED_DISK_READ(0);
-       CRIS_LED_DISK_WRITE(0);
-
-       res2 = request_irq(GIO_INTR_VECT, gpio_interrupt,
-               IRQF_SHARED, "gpio", &alarmlist);
-       if (res2) {
-               printk(KERN_ERR "err: irq for gpio\n");
-               return res2;
-       }
-
-       /* No IRQs by default. */
-       REG_WR_INT(gio, regi_gio, rw_intr_pins, 0);
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       virtual_gpio_init();
-#endif
-
-       return res;
-}
-
-/* this makes sure that gpio_init is called during kernel boot */
-
-module_init(gpio_init);
index 5c6d2a2a080ee4bbbbeca7c6bef44899389179c5..59028d0b981c094dd67a3570ed583d6bfa12ed75 100644 (file)
@@ -3,4 +3,3 @@
 #
 
 obj-$(CONFIG_ETRAX_NANDFLASH)   += nandflash.o
-obj-$(CONFIG_ETRAX_GPIO)        += gpio.o
diff --git a/arch/cris/arch-v32/drivers/mach-fs/gpio.c b/arch/cris/arch-v32/drivers/mach-fs/gpio.c
deleted file mode 100644 (file)
index 72968fb..0000000
+++ /dev/null
@@ -1,978 +0,0 @@
-/*
- * ETRAX CRISv32 general port I/O device
- *
- * Copyright (c) 1999-2006 Axis Communications AB
- *
- * Authors:    Bjorn Wesen      (initial version)
- *             Ola Knutsson     (LED handling)
- *             Johan Adolfsson  (read/set directions, write, port G,
- *                               port to ETRAX FS.
- *
- */
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/string.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-
-#include <asm/etraxgpio.h>
-#include <hwregs/reg_map.h>
-#include <hwregs/reg_rdwr.h>
-#include <hwregs/gio_defs.h>
-#include <hwregs/intr_vect_defs.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-#include "../i2c.h"
-
-#define VIRT_I2C_ADDR 0x40
-#endif
-
-/* The following gio ports on ETRAX FS is available:
- * pa  8 bits, supports interrupts off, hi, low, set, posedge, negedge anyedge
- * pb 18 bits
- * pc 18 bits
- * pd 18 bits
- * pe 18 bits
- * each port has a rw_px_dout, r_px_din and rw_px_oe register.
- */
-
-#define GPIO_MAJOR 120  /* experimental MAJOR number */
-
-#define D(x)
-
-#if 0
-static int dp_cnt;
-#define DP(x) \
-       do { \
-               dp_cnt++; \
-               if (dp_cnt % 1000 == 0) \
-                       x; \
-       } while (0)
-#else
-#define DP(x)
-#endif
-
-static DEFINE_MUTEX(gpio_mutex);
-static char gpio_name[] = "etrax gpio";
-
-#if 0
-static wait_queue_head_t *gpio_wq;
-#endif
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-static int virtual_gpio_ioctl(struct file *file, unsigned int cmd,
-       unsigned long arg);
-#endif
-static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
-static ssize_t gpio_write(struct file *file, const char *buf, size_t count,
-       loff_t *off);
-static int gpio_open(struct inode *inode, struct file *filp);
-static int gpio_release(struct inode *inode, struct file *filp);
-static unsigned int gpio_poll(struct file *filp,
-       struct poll_table_struct *wait);
-
-/* private data per open() of this driver */
-
-struct gpio_private {
-       struct gpio_private *next;
-       /* The IO_CFG_WRITE_MODE_VALUE only support 8 bits: */
-       unsigned char clk_mask;
-       unsigned char data_mask;
-       unsigned char write_msb;
-       unsigned char pad1;
-       /* These fields are generic */
-       unsigned long highalarm, lowalarm;
-       wait_queue_head_t alarm_wq;
-       int minor;
-};
-
-/* linked list of alarms to check for */
-
-static struct gpio_private *alarmlist;
-
-static int gpio_some_alarms; /* Set if someone uses alarm */
-static unsigned long gpio_pa_high_alarms;
-static unsigned long gpio_pa_low_alarms;
-
-static DEFINE_SPINLOCK(alarm_lock);
-
-#define NUM_PORTS (GPIO_MINOR_LAST+1)
-#define GIO_REG_RD_ADDR(reg) \
-       (volatile unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg)
-#define GIO_REG_WR_ADDR(reg) \
-       (volatile unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg)
-unsigned long led_dummy;
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-static unsigned long virtual_dummy;
-static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE;
-static unsigned short cached_virtual_gpio_read;
-#endif
-
-static volatile unsigned long *data_out[NUM_PORTS] = {
-       GIO_REG_WR_ADDR(rw_pa_dout),
-       GIO_REG_WR_ADDR(rw_pb_dout),
-       &led_dummy,
-       GIO_REG_WR_ADDR(rw_pc_dout),
-       GIO_REG_WR_ADDR(rw_pd_dout),
-       GIO_REG_WR_ADDR(rw_pe_dout),
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       &virtual_dummy,
-#endif
-};
-
-static volatile unsigned long *data_in[NUM_PORTS] = {
-       GIO_REG_RD_ADDR(r_pa_din),
-       GIO_REG_RD_ADDR(r_pb_din),
-       &led_dummy,
-       GIO_REG_RD_ADDR(r_pc_din),
-       GIO_REG_RD_ADDR(r_pd_din),
-       GIO_REG_RD_ADDR(r_pe_din),
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       &virtual_dummy,
-#endif
-};
-
-static unsigned long changeable_dir[NUM_PORTS] = {
-       CONFIG_ETRAX_PA_CHANGEABLE_DIR,
-       CONFIG_ETRAX_PB_CHANGEABLE_DIR,
-       0,
-       CONFIG_ETRAX_PC_CHANGEABLE_DIR,
-       CONFIG_ETRAX_PD_CHANGEABLE_DIR,
-       CONFIG_ETRAX_PE_CHANGEABLE_DIR,
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       CONFIG_ETRAX_PV_CHANGEABLE_DIR,
-#endif
-};
-
-static unsigned long changeable_bits[NUM_PORTS] = {
-       CONFIG_ETRAX_PA_CHANGEABLE_BITS,
-       CONFIG_ETRAX_PB_CHANGEABLE_BITS,
-       0,
-       CONFIG_ETRAX_PC_CHANGEABLE_BITS,
-       CONFIG_ETRAX_PD_CHANGEABLE_BITS,
-       CONFIG_ETRAX_PE_CHANGEABLE_BITS,
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       CONFIG_ETRAX_PV_CHANGEABLE_BITS,
-#endif
-};
-
-static volatile unsigned long *dir_oe[NUM_PORTS] = {
-       GIO_REG_WR_ADDR(rw_pa_oe),
-       GIO_REG_WR_ADDR(rw_pb_oe),
-       &led_dummy,
-       GIO_REG_WR_ADDR(rw_pc_oe),
-       GIO_REG_WR_ADDR(rw_pd_oe),
-       GIO_REG_WR_ADDR(rw_pe_oe),
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       &virtual_rw_pv_oe,
-#endif
-};
-
-
-
-static unsigned int gpio_poll(struct file *file, struct poll_table_struct *wait)
-{
-       unsigned int mask = 0;
-       struct gpio_private *priv = file->private_data;
-       unsigned long data;
-       poll_wait(file, &priv->alarm_wq, wait);
-       if (priv->minor == GPIO_MINOR_A) {
-               reg_gio_rw_intr_cfg intr_cfg;
-               unsigned long tmp;
-               unsigned long flags;
-
-               local_irq_save(flags);
-               data = REG_TYPE_CONV(unsigned long, reg_gio_r_pa_din,
-                       REG_RD(gio, regi_gio, r_pa_din));
-               /* PA has support for interrupt
-                * lets activate high for those low and with highalarm set
-                */
-               intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg);
-
-               tmp = ~data & priv->highalarm & 0xFF;
-               if (tmp & (1 << 0))
-                       intr_cfg.pa0 = regk_gio_hi;
-               if (tmp & (1 << 1))
-                       intr_cfg.pa1 = regk_gio_hi;
-               if (tmp & (1 << 2))
-                       intr_cfg.pa2 = regk_gio_hi;
-               if (tmp & (1 << 3))
-                       intr_cfg.pa3 = regk_gio_hi;
-               if (tmp & (1 << 4))
-                       intr_cfg.pa4 = regk_gio_hi;
-               if (tmp & (1 << 5))
-                       intr_cfg.pa5 = regk_gio_hi;
-               if (tmp & (1 << 6))
-                       intr_cfg.pa6 = regk_gio_hi;
-               if (tmp & (1 << 7))
-                       intr_cfg.pa7 = regk_gio_hi;
-               /*
-                * lets activate low for those high and with lowalarm set
-                */
-               tmp = data & priv->lowalarm & 0xFF;
-               if (tmp & (1 << 0))
-                       intr_cfg.pa0 = regk_gio_lo;
-               if (tmp & (1 << 1))
-                       intr_cfg.pa1 = regk_gio_lo;
-               if (tmp & (1 << 2))
-                       intr_cfg.pa2 = regk_gio_lo;
-               if (tmp & (1 << 3))
-                       intr_cfg.pa3 = regk_gio_lo;
-               if (tmp & (1 << 4))
-                       intr_cfg.pa4 = regk_gio_lo;
-               if (tmp & (1 << 5))
-                       intr_cfg.pa5 = regk_gio_lo;
-               if (tmp & (1 << 6))
-                       intr_cfg.pa6 = regk_gio_lo;
-               if (tmp & (1 << 7))
-                       intr_cfg.pa7 = regk_gio_lo;
-
-               REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg);
-               local_irq_restore(flags);
-       } else if (priv->minor <= GPIO_MINOR_E)
-               data = *data_in[priv->minor];
-       else
-               return 0;
-
-       if ((data & priv->highalarm) || (~data & priv->lowalarm))
-               mask = POLLIN|POLLRDNORM;
-
-       DP(printk(KERN_DEBUG "gpio_poll ready: mask 0x%08X\n", mask));
-       return mask;
-}
-
-int etrax_gpio_wake_up_check(void)
-{
-       struct gpio_private *priv;
-       unsigned long data = 0;
-       unsigned long flags;
-       int ret = 0;
-       spin_lock_irqsave(&alarm_lock, flags);
-       priv = alarmlist;
-       while (priv) {
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-               if (priv->minor == GPIO_MINOR_V)
-                       data = (unsigned long)cached_virtual_gpio_read;
-               else {
-                       data = *data_in[priv->minor];
-                       if (priv->minor == GPIO_MINOR_A)
-                               priv->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
-               }
-#else
-               data = *data_in[priv->minor];
-#endif
-               if ((data & priv->highalarm) ||
-                   (~data & priv->lowalarm)) {
-                       DP(printk(KERN_DEBUG
-                               "etrax_gpio_wake_up_check %i\n", priv->minor));
-                       wake_up_interruptible(&priv->alarm_wq);
-                       ret = 1;
-               }
-               priv = priv->next;
-       }
-       spin_unlock_irqrestore(&alarm_lock, flags);
-       return ret;
-}
-
-static irqreturn_t
-gpio_poll_timer_interrupt(int irq, void *dev_id)
-{
-       if (gpio_some_alarms)
-               return IRQ_RETVAL(etrax_gpio_wake_up_check());
-       return IRQ_NONE;
-}
-
-static irqreturn_t
-gpio_pa_interrupt(int irq, void *dev_id)
-{
-       reg_gio_rw_intr_mask intr_mask;
-       reg_gio_r_masked_intr masked_intr;
-       reg_gio_rw_ack_intr ack_intr;
-       unsigned long tmp;
-       unsigned long tmp2;
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       unsigned char enable_gpiov_ack = 0;
-#endif
-
-       /* Find what PA interrupts are active */
-       masked_intr = REG_RD(gio, regi_gio, r_masked_intr);
-       tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr);
-
-       /* Find those that we have enabled */
-       spin_lock(&alarm_lock);
-       tmp &= (gpio_pa_high_alarms | gpio_pa_low_alarms);
-       spin_unlock(&alarm_lock);
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       /* Something changed on virtual GPIO. Interrupt is acked by
-        * reading the device.
-        */
-       if (tmp & (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN)) {
-               i2c_read(VIRT_I2C_ADDR, (void *)&cached_virtual_gpio_read,
-                       sizeof(cached_virtual_gpio_read));
-               enable_gpiov_ack = 1;
-       }
-#endif
-
-       /* Ack them */
-       ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp);
-       REG_WR(gio, regi_gio, rw_ack_intr, ack_intr);
-
-       /* Disable those interrupts.. */
-       intr_mask = REG_RD(gio, regi_gio, rw_intr_mask);
-       tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask);
-       tmp2 &= ~tmp;
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       /* Do not disable interrupt on virtual GPIO. Changes on virtual
-        * pins are only noticed by an interrupt.
-        */
-       if (enable_gpiov_ack)
-               tmp2 |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
-#endif
-       intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2);
-       REG_WR(gio, regi_gio, rw_intr_mask, intr_mask);
-
-       if (gpio_some_alarms)
-               return IRQ_RETVAL(etrax_gpio_wake_up_check());
-       return IRQ_NONE;
-}
-
-
-static ssize_t gpio_write(struct file *file, const char *buf, size_t count,
-       loff_t *off)
-{
-       struct gpio_private *priv = file->private_data;
-       unsigned char data, clk_mask, data_mask, write_msb;
-       unsigned long flags;
-       unsigned long shadow;
-       volatile unsigned long *port;
-       ssize_t retval = count;
-       /* Only bits 0-7 may be used for write operations but allow all
-          devices except leds... */
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       if (priv->minor == GPIO_MINOR_V)
-               return -EFAULT;
-#endif
-       if (priv->minor == GPIO_MINOR_LEDS)
-               return -EFAULT;
-
-       if (!access_ok(VERIFY_READ, buf, count))
-               return -EFAULT;
-       clk_mask = priv->clk_mask;
-       data_mask = priv->data_mask;
-       /* It must have been configured using the IO_CFG_WRITE_MODE */
-       /* Perhaps a better error code? */
-       if (clk_mask == 0 || data_mask == 0)
-               return -EPERM;
-       write_msb = priv->write_msb;
-       D(printk(KERN_DEBUG "gpio_write: %lu to data 0x%02X clk 0x%02X "
-               "msb: %i\n", count, data_mask, clk_mask, write_msb));
-       port = data_out[priv->minor];
-
-       while (count--) {
-               int i;
-               data = *buf++;
-               if (priv->write_msb) {
-                       for (i = 7; i >= 0; i--) {
-                               local_irq_save(flags);
-                               shadow = *port;
-                               *port = shadow &= ~clk_mask;
-                               if (data & 1<<i)
-                                       *port = shadow |= data_mask;
-                               else
-                                       *port = shadow &= ~data_mask;
-                       /* For FPGA: min 5.0ns (DCC) before CCLK high */
-                               *port = shadow |= clk_mask;
-                               local_irq_restore(flags);
-                       }
-               } else {
-                       for (i = 0; i <= 7; i++) {
-                               local_irq_save(flags);
-                               shadow = *port;
-                               *port = shadow &= ~clk_mask;
-                               if (data & 1<<i)
-                                       *port = shadow |= data_mask;
-                               else
-                                       *port = shadow &= ~data_mask;
-                       /* For FPGA: min 5.0ns (DCC) before CCLK high */
-                               *port = shadow |= clk_mask;
-                               local_irq_restore(flags);
-                       }
-               }
-       }
-       return retval;
-}
-
-
-
-static int
-gpio_open(struct inode *inode, struct file *filp)
-{
-       struct gpio_private *priv;
-       int p = iminor(inode);
-
-       if (p > GPIO_MINOR_LAST)
-               return -EINVAL;
-
-       priv = kzalloc(sizeof(struct gpio_private), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       mutex_lock(&gpio_mutex);
-
-       priv->minor = p;
-
-       /* initialize the io/alarm struct */
-
-       priv->clk_mask = 0;
-       priv->data_mask = 0;
-       priv->highalarm = 0;
-       priv->lowalarm = 0;
-       init_waitqueue_head(&priv->alarm_wq);
-
-       filp->private_data = (void *)priv;
-
-       /* link it into our alarmlist */
-       spin_lock_irq(&alarm_lock);
-       priv->next = alarmlist;
-       alarmlist = priv;
-       spin_unlock_irq(&alarm_lock);
-
-       mutex_unlock(&gpio_mutex);
-       return 0;
-}
-
-static int
-gpio_release(struct inode *inode, struct file *filp)
-{
-       struct gpio_private *p;
-       struct gpio_private *todel;
-       /* local copies while updating them: */
-       unsigned long a_high, a_low;
-       unsigned long some_alarms;
-
-       /* unlink from alarmlist and free the private structure */
-
-       spin_lock_irq(&alarm_lock);
-       p = alarmlist;
-       todel = filp->private_data;
-
-       if (p == todel) {
-               alarmlist = todel->next;
-       } else {
-               while (p->next != todel)
-                       p = p->next;
-               p->next = todel->next;
-       }
-
-       kfree(todel);
-       /* Check if there are still any alarms set */
-       p = alarmlist;
-       some_alarms = 0;
-       a_high = 0;
-       a_low = 0;
-       while (p) {
-               if (p->minor == GPIO_MINOR_A) {
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-                       p->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
-#endif
-                       a_high |= p->highalarm;
-                       a_low |= p->lowalarm;
-               }
-
-               if (p->highalarm | p->lowalarm)
-                       some_alarms = 1;
-               p = p->next;
-       }
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       /* Variables 'some_alarms' and 'a_low' needs to be set here again
-        * to ensure that interrupt for virtual GPIO is handled.
-        */
-       some_alarms = 1;
-       a_low |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
-#endif
-
-       gpio_some_alarms = some_alarms;
-       gpio_pa_high_alarms = a_high;
-       gpio_pa_low_alarms = a_low;
-       spin_unlock_irq(&alarm_lock);
-
-       return 0;
-}
-
-/* Main device API. ioctl's to read/set/clear bits, as well as to
- * set alarms to wait for using a subsequent select().
- */
-
-inline unsigned long setget_input(struct gpio_private *priv, unsigned long arg)
-{
-       /* Set direction 0=unchanged 1=input,
-        * return mask with 1=input
-        */
-       unsigned long flags;
-       unsigned long dir_shadow;
-
-       local_irq_save(flags);
-       dir_shadow = *dir_oe[priv->minor];
-       dir_shadow &= ~(arg & changeable_dir[priv->minor]);
-       *dir_oe[priv->minor] = dir_shadow;
-       local_irq_restore(flags);
-
-       if (priv->minor == GPIO_MINOR_A)
-               dir_shadow ^= 0xFF;    /* Only 8 bits */
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       else if (priv->minor == GPIO_MINOR_V)
-               dir_shadow ^= 0xFFFF;  /* Only 16 bits */
-#endif
-       else
-               dir_shadow ^= 0x3FFFF; /* Only 18 bits */
-       return dir_shadow;
-
-} /* setget_input */
-
-inline unsigned long setget_output(struct gpio_private *priv, unsigned long arg)
-{
-       unsigned long flags;
-       unsigned long dir_shadow;
-
-       local_irq_save(flags);
-       dir_shadow = *dir_oe[priv->minor];
-       dir_shadow |=  (arg & changeable_dir[priv->minor]);
-       *dir_oe[priv->minor] = dir_shadow;
-       local_irq_restore(flags);
-       return dir_shadow;
-} /* setget_output */
-
-static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
-
-static int
-gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       unsigned long flags;
-       unsigned long val;
-       unsigned long shadow;
-       struct gpio_private *priv = file->private_data;
-       if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE)
-               return -EINVAL;
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       if (priv->minor == GPIO_MINOR_V)
-               return virtual_gpio_ioctl(file, cmd, arg);
-#endif
-
-       switch (_IOC_NR(cmd)) {
-       case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
-               /* Read the port. */
-               return *data_in[priv->minor];
-               break;
-       case IO_SETBITS:
-               local_irq_save(flags);
-               /* Set changeable bits with a 1 in arg. */
-               shadow = *data_out[priv->minor];
-               shadow |=  (arg & changeable_bits[priv->minor]);
-               *data_out[priv->minor] = shadow;
-               local_irq_restore(flags);
-               break;
-       case IO_CLRBITS:
-               local_irq_save(flags);
-               /* Clear changeable bits with a 1 in arg. */
-               shadow = *data_out[priv->minor];
-               shadow &=  ~(arg & changeable_bits[priv->minor]);
-               *data_out[priv->minor] = shadow;
-               local_irq_restore(flags);
-               break;
-       case IO_HIGHALARM:
-               /* Set alarm when bits with 1 in arg go high. */
-               priv->highalarm |= arg;
-               spin_lock_irqsave(&alarm_lock, flags);
-               gpio_some_alarms = 1;
-               if (priv->minor == GPIO_MINOR_A)
-                       gpio_pa_high_alarms |= arg;
-               spin_unlock_irqrestore(&alarm_lock, flags);
-               break;
-       case IO_LOWALARM:
-               /* Set alarm when bits with 1 in arg go low. */
-               priv->lowalarm |= arg;
-               spin_lock_irqsave(&alarm_lock, flags);
-               gpio_some_alarms = 1;
-               if (priv->minor == GPIO_MINOR_A)
-                       gpio_pa_low_alarms |= arg;
-               spin_unlock_irqrestore(&alarm_lock, flags);
-               break;
-       case IO_CLRALARM:
-               /* Clear alarm for bits with 1 in arg. */
-               priv->highalarm &= ~arg;
-               priv->lowalarm  &= ~arg;
-               spin_lock_irqsave(&alarm_lock, flags);
-               if (priv->minor == GPIO_MINOR_A) {
-                       if (gpio_pa_high_alarms & arg ||
-                           gpio_pa_low_alarms & arg)
-                               /* Must update the gpio_pa_*alarms masks */
-                               ;
-               }
-               spin_unlock_irqrestore(&alarm_lock, flags);
-               break;
-       case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
-               /* Read direction 0=input 1=output */
-               return *dir_oe[priv->minor];
-       case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
-               /* Set direction 0=unchanged 1=input,
-                * return mask with 1=input
-                */
-               return setget_input(priv, arg);
-               break;
-       case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
-               /* Set direction 0=unchanged 1=output,
-                * return mask with 1=output
-                */
-               return setget_output(priv, arg);
-
-       case IO_CFG_WRITE_MODE:
-       {
-               unsigned long dir_shadow;
-               dir_shadow = *dir_oe[priv->minor];
-
-               priv->clk_mask = arg & 0xFF;
-               priv->data_mask = (arg >> 8) & 0xFF;
-               priv->write_msb = (arg >> 16) & 0x01;
-               /* Check if we're allowed to change the bits and
-                * the direction is correct
-                */
-               if (!((priv->clk_mask & changeable_bits[priv->minor]) &&
-                     (priv->data_mask & changeable_bits[priv->minor]) &&
-                     (priv->clk_mask & dir_shadow) &&
-                     (priv->data_mask & dir_shadow))) {
-                       priv->clk_mask = 0;
-                       priv->data_mask = 0;
-                       return -EPERM;
-               }
-               break;
-       }
-       case IO_READ_INBITS:
-               /* *arg is result of reading the input pins */
-               val = *data_in[priv->minor];
-               if (copy_to_user((unsigned long *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               return 0;
-               break;
-       case IO_READ_OUTBITS:
-                /* *arg is result of reading the output shadow */
-               val = *data_out[priv->minor];
-               if (copy_to_user((unsigned long *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               break;
-       case IO_SETGET_INPUT:
-               /* bits set in *arg is set to input,
-                * *arg updated with current input pins.
-                */
-               if (copy_from_user(&val, (unsigned long *)arg, sizeof(val)))
-                       return -EFAULT;
-               val = setget_input(priv, val);
-               if (copy_to_user((unsigned long *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               break;
-       case IO_SETGET_OUTPUT:
-               /* bits set in *arg is set to output,
-                * *arg updated with current output pins.
-                */
-               if (copy_from_user(&val, (unsigned long *)arg, sizeof(val)))
-                       return -EFAULT;
-               val = setget_output(priv, val);
-               if (copy_to_user((unsigned long *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               break;
-       default:
-               if (priv->minor == GPIO_MINOR_LEDS)
-                       return gpio_leds_ioctl(cmd, arg);
-               else
-                       return -EINVAL;
-       } /* switch */
-
-       return 0;
-}
-
-static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       long ret;
-
-       mutex_lock(&gpio_mutex);
-       ret = gpio_ioctl_unlocked(file, cmd, arg);
-       mutex_unlock(&gpio_mutex);
-
-       return ret;
-}
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-static int
-virtual_gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       unsigned long flags;
-       unsigned short val;
-       unsigned short shadow;
-       struct gpio_private *priv = file->private_data;
-
-       switch (_IOC_NR(cmd)) {
-       case IO_SETBITS:
-               local_irq_save(flags);
-               /* Set changeable bits with a 1 in arg. */
-               i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
-               shadow |= ~*dir_oe[priv->minor];
-               shadow |= (arg & changeable_bits[priv->minor]);
-               i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
-               local_irq_restore(flags);
-               break;
-       case IO_CLRBITS:
-               local_irq_save(flags);
-               /* Clear changeable bits with a 1 in arg. */
-               i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
-               shadow |= ~*dir_oe[priv->minor];
-               shadow &= ~(arg & changeable_bits[priv->minor]);
-               i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
-               local_irq_restore(flags);
-               break;
-       case IO_HIGHALARM:
-               /* Set alarm when bits with 1 in arg go high. */
-               priv->highalarm |= arg;
-               spin_lock(&alarm_lock);
-               gpio_some_alarms = 1;
-               spin_unlock(&alarm_lock);
-               break;
-       case IO_LOWALARM:
-               /* Set alarm when bits with 1 in arg go low. */
-               priv->lowalarm |= arg;
-               spin_lock(&alarm_lock);
-               gpio_some_alarms = 1;
-               spin_unlock(&alarm_lock);
-               break;
-       case IO_CLRALARM:
-               /* Clear alarm for bits with 1 in arg. */
-               priv->highalarm &= ~arg;
-               priv->lowalarm  &= ~arg;
-               spin_lock(&alarm_lock);
-               spin_unlock(&alarm_lock);
-               break;
-       case IO_CFG_WRITE_MODE:
-       {
-               unsigned long dir_shadow;
-               dir_shadow = *dir_oe[priv->minor];
-
-               priv->clk_mask = arg & 0xFF;
-               priv->data_mask = (arg >> 8) & 0xFF;
-               priv->write_msb = (arg >> 16) & 0x01;
-               /* Check if we're allowed to change the bits and
-                * the direction is correct
-                */
-               if (!((priv->clk_mask & changeable_bits[priv->minor]) &&
-                     (priv->data_mask & changeable_bits[priv->minor]) &&
-                     (priv->clk_mask & dir_shadow) &&
-                     (priv->data_mask & dir_shadow))) {
-                       priv->clk_mask = 0;
-                       priv->data_mask = 0;
-                       return -EPERM;
-               }
-               break;
-       }
-       case IO_READ_INBITS:
-               /* *arg is result of reading the input pins */
-               val = cached_virtual_gpio_read;
-               val &= ~*dir_oe[priv->minor];
-               if (copy_to_user((unsigned long *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               return 0;
-               break;
-       case IO_READ_OUTBITS:
-                /* *arg is result of reading the output shadow */
-               i2c_read(VIRT_I2C_ADDR, (void *)&val, sizeof(val));
-               val &= *dir_oe[priv->minor];
-               if (copy_to_user((unsigned long *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               break;
-       case IO_SETGET_INPUT:
-       {
-               /* bits set in *arg is set to input,
-                * *arg updated with current input pins.
-                */
-               unsigned short input_mask = ~*dir_oe[priv->minor];
-               if (copy_from_user(&val, (unsigned long *)arg, sizeof(val)))
-                       return -EFAULT;
-               val = setget_input(priv, val);
-               if (copy_to_user((unsigned long *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               if ((input_mask & val) != input_mask) {
-                       /* Input pins changed. All ports desired as input
-                        * should be set to logic 1.
-                        */
-                       unsigned short change = input_mask ^ val;
-                       i2c_read(VIRT_I2C_ADDR, (void *)&shadow,
-                               sizeof(shadow));
-                       shadow &= ~change;
-                       shadow |= val;
-                       i2c_write(VIRT_I2C_ADDR, (void *)&shadow,
-                               sizeof(shadow));
-               }
-               break;
-       }
-       case IO_SETGET_OUTPUT:
-               /* bits set in *arg is set to output,
-                * *arg updated with current output pins.
-                */
-               if (copy_from_user(&val, (unsigned long *)arg, sizeof(val)))
-                       return -EFAULT;
-               val = setget_output(priv, val);
-               if (copy_to_user((unsigned long *)arg, &val, sizeof(val)))
-                       return -EFAULT;
-               break;
-       default:
-               return -EINVAL;
-       } /* switch */
-  return 0;
-}
-#endif /* CONFIG_ETRAX_VIRTUAL_GPIO */
-
-static int
-gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
-{
-       unsigned char green;
-       unsigned char red;
-
-       switch (_IOC_NR(cmd)) {
-       case IO_LEDACTIVE_SET:
-               green = ((unsigned char) arg) & 1;
-               red   = (((unsigned char) arg) >> 1) & 1;
-               CRIS_LED_ACTIVE_SET_G(green);
-               CRIS_LED_ACTIVE_SET_R(red);
-               break;
-
-       default:
-               return -EINVAL;
-       } /* switch */
-
-       return 0;
-}
-
-static const struct file_operations gpio_fops = {
-       .owner          = THIS_MODULE,
-       .poll           = gpio_poll,
-       .unlocked_ioctl = gpio_ioctl,
-       .write          = gpio_write,
-       .open           = gpio_open,
-       .release        = gpio_release,
-       .llseek         = noop_llseek,
-};
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-static void
-virtual_gpio_init(void)
-{
-       reg_gio_rw_intr_cfg intr_cfg;
-       reg_gio_rw_intr_mask intr_mask;
-       unsigned short shadow;
-
-       shadow = ~virtual_rw_pv_oe; /* Input ports should be set to logic 1 */
-       shadow |= CONFIG_ETRAX_DEF_GIO_PV_OUT;
-       i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
-
-       /* Set interrupt mask and on what state the interrupt shall trigger.
-        * For virtual gpio the interrupt shall trigger on logic '0'.
-        */
-       intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg);
-       intr_mask = REG_RD(gio, regi_gio, rw_intr_mask);
-
-       switch (CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN) {
-       case 0:
-               intr_cfg.pa0 = regk_gio_lo;
-               intr_mask.pa0 = regk_gio_yes;
-               break;
-       case 1:
-               intr_cfg.pa1 = regk_gio_lo;
-               intr_mask.pa1 = regk_gio_yes;
-               break;
-       case 2:
-               intr_cfg.pa2 = regk_gio_lo;
-               intr_mask.pa2 = regk_gio_yes;
-               break;
-       case 3:
-               intr_cfg.pa3 = regk_gio_lo;
-               intr_mask.pa3 = regk_gio_yes;
-               break;
-       case 4:
-               intr_cfg.pa4 = regk_gio_lo;
-               intr_mask.pa4 = regk_gio_yes;
-               break;
-       case 5:
-               intr_cfg.pa5 = regk_gio_lo;
-               intr_mask.pa5 = regk_gio_yes;
-               break;
-       case 6:
-               intr_cfg.pa6 = regk_gio_lo;
-               intr_mask.pa6 = regk_gio_yes;
-               break;
-       case 7:
-               intr_cfg.pa7 = regk_gio_lo;
-               intr_mask.pa7 = regk_gio_yes;
-       break;
-       }
-
-       REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg);
-       REG_WR(gio, regi_gio, rw_intr_mask, intr_mask);
-
-       gpio_pa_low_alarms |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
-       gpio_some_alarms = 1;
-}
-#endif
-
-/* main driver initialization routine, called from mem.c */
-
-static __init int
-gpio_init(void)
-{
-       int res;
-
-       /* do the formalities */
-
-       res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
-       if (res < 0) {
-               printk(KERN_ERR "gpio: couldn't get a major number.\n");
-               return res;
-       }
-
-       /* Clear all leds */
-       CRIS_LED_NETWORK_GRP0_SET(0);
-       CRIS_LED_NETWORK_GRP1_SET(0);
-       CRIS_LED_ACTIVE_SET(0);
-       CRIS_LED_DISK_READ(0);
-       CRIS_LED_DISK_WRITE(0);
-
-       printk(KERN_INFO "ETRAX FS GPIO driver v2.5, (c) 2003-2007 "
-               "Axis Communications AB\n");
-       /* We call etrax_gpio_wake_up_check() from timer interrupt */
-       if (request_irq(TIMER0_INTR_VECT, gpio_poll_timer_interrupt,
-                       IRQF_SHARED, "gpio poll", &alarmlist))
-               printk(KERN_ERR "timer0 irq for gpio\n");
-
-       if (request_irq(GIO_INTR_VECT, gpio_pa_interrupt,
-                       IRQF_SHARED, "gpio PA", &alarmlist))
-               printk(KERN_ERR "PA irq for gpio\n");
-
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       virtual_gpio_init();
-#endif
-
-       return res;
-}
-
-/* this makes sure that gpio_init is called during kernel boot */
-
-module_init(gpio_init);
index bde8d1a10cadd31edb5b0120b0bc3feab73e07f9..b0566350a840319d0e8e4d603e0e892792510d36 100644 (file)
@@ -3,7 +3,6 @@
 #include <arch/dma.h>
 #include <arch/intmem.h>
 #include <mach/pinmux.h>
-#include <arch/io.h>
 
 /* Functions for allocating DMA channels */
 EXPORT_SYMBOL(crisv32_request_dma);
@@ -20,8 +19,6 @@ EXPORT_SYMBOL(crisv32_pinmux_alloc);
 EXPORT_SYMBOL(crisv32_pinmux_alloc_fixed);
 EXPORT_SYMBOL(crisv32_pinmux_dealloc);
 EXPORT_SYMBOL(crisv32_pinmux_dealloc_fixed);
-EXPORT_SYMBOL(crisv32_io_get_name);
-EXPORT_SYMBOL(crisv32_io_get);
 
 /* Functions masking/unmasking interrupts */
 EXPORT_SYMBOL(crisv32_mask_irq);
index 02e33ebe51ec50b9a71d5cd1205df1168a5fe848..d2f3f9c37102365eaed4c6e07bda0fd6cccf2bf1 100644 (file)
@@ -77,8 +77,6 @@ static struct dbg_port *port =
        &ports[2];
 #elif defined(CONFIG_ETRAX_DEBUG_PORT3)
        &ports[3];
-#elif defined(CONFIG_ETRAX_DEBUG_PORT4)
-       &ports[4];
 #else
        NULL;
 #endif
index 74a66e0e3777388a9a05a92ba83fbcce17a6c7f9..ea6366800df7bb300504ba36f48b77d5b3792828 100644 (file)
@@ -292,11 +292,7 @@ _no_romfs_in_flash:
        ;; For cramfs, partition starts with magic and length.
        ;; For jffs2, a jhead is prepended which contains with magic and length.
        ;; The jhead is not part of the jffs2 partition however.
-#ifndef CONFIG_ETRAXFS_SIM
        move.d  __bss_start, $r0
-#else
-       move.d  __end, $r0
-#endif
        move.d  [$r0], $r1
        cmp.d   CRAMFS_MAGIC, $r1 ; cramfs magic?
        beq     2f                ; yes, jump
index 6a881e0e92b43990dd14a7b7e06edeac9541dcb6..6de8db67cb097835f0812cac6845a8bdfda23a87 100644 (file)
@@ -37,7 +37,7 @@
 #define IGNOREMASK (1 << (SER0_INTR_VECT - FIRST_IRQ))
 #elif defined(CONFIG_ETRAX_KGDB_PORT1)
 #define IGNOREMASK (1 << (SER1_INTR_VECT - FIRST_IRQ))
-#elif defined(CONFIG_ETRAX_KGB_PORT2)
+#elif defined(CONFIG_ETRAX_KGDB_PORT2)
 #define IGNOREMASK (1 << (SER2_INTR_VECT - FIRST_IRQ))
 #elif defined(CONFIG_ETRAX_KGDB_PORT3)
 #define IGNOREMASK (1 << (SER3_INTR_VECT - FIRST_IRQ))
@@ -464,14 +464,14 @@ init_IRQ(void)
                etrax_irv->v[i] = weird_irq;
 
        np = of_find_compatible_node(NULL, NULL, "axis,crisv32-intc");
-       domain = irq_domain_add_legacy(np, NR_IRQS - FIRST_IRQ,
+       domain = irq_domain_add_legacy(np, NBR_INTR_VECT - FIRST_IRQ,
                                       FIRST_IRQ, FIRST_IRQ,
                                       &crisv32_irq_ops, NULL);
        BUG_ON(!domain);
        irq_set_default_host(domain);
        of_node_put(np);
 
-       for (i = FIRST_IRQ, j = 0; j < NR_IRQS; i++, j++) {
+       for (i = FIRST_IRQ, j = 0; j < NBR_INTR_VECT; i++, j++) {
                set_exception_vector(i, interrupt[j]);
        }
 
index b06813aeb120147b82502029fc3b21d7c9204fd5..e0fdea706ecad9d10e4a87f5191e9a64b2628659 100644 (file)
@@ -384,19 +384,11 @@ int getDebugChar(void);
 /* Serial port, writes one character. ETRAX 100 specific. from debugport.c */
 void putDebugChar(int val);
 
-/* Returns the integer equivalent of a hexadecimal character. */
-static int hex(char ch);
-
 /* Convert the memory, pointed to by mem into hexadecimal representation.
    Put the result in buf, and return a pointer to the last character
    in buf (null). */
 static char *mem2hex(char *buf, unsigned char *mem, int count);
 
-/* Convert the array, in hexadecimal representation, pointed to by buf into
-   binary representation. Put the result in mem, and return a pointer to
-   the character after the last byte written. */
-static unsigned char *hex2mem(unsigned char *mem, char *buf, int count);
-
 /* Put the content of the array, in binary representation, pointed to by buf
    into memory pointed to by mem, and return a pointer to
    the character after the last byte written. */
@@ -449,7 +441,7 @@ static char output_buffer[BUFMAX];
 /* Error and warning messages. */
 enum error_type
 {
-       SUCCESS, E01, E02, E03, E04, E05, E06,
+       SUCCESS, E01, E02, E03, E04, E05, E06, E07, E08
 };
 
 static char *error_message[] =
@@ -461,6 +453,8 @@ static char *error_message[] =
        "E04 The command is not supported - [s,C,S,!,R,d,r] - internal error.",
        "E05 Change register content - P - the register is not implemented..",
        "E06 Change memory content - M - internal error.",
+       "E07 Change register content - P - the register is not stored on the stack",
+       "E08 Invalid parameter"
 };
 
 /********************************** Breakpoint *******************************/
@@ -539,7 +533,7 @@ gdb_cris_strtol(const char *s, char **endptr, int base)
 /********************************* Register image ****************************/
 
 /* Write a value to a specified register in the register image of the current
-   thread. Returns status code SUCCESS, E02 or E05. */
+   thread. Returns status code SUCCESS, E02, E05 or E08. */
 static int
 write_register(int regno, char *val)
 {
@@ -547,8 +541,9 @@ write_register(int regno, char *val)
 
         if (regno >= R0 && regno <= ACR) {
                /* Consecutive 32-bit registers. */
-               hex2mem((unsigned char *)&reg.r0 + (regno - R0) * sizeof(unsigned int),
-                       val, sizeof(unsigned int));
+               if (hex2bin((unsigned char *)&reg.r0 + (regno - R0) * sizeof(unsigned int),
+                           val, sizeof(unsigned int)))
+                       status = E08;
 
        } else if (regno == BZ || regno == VR || regno == WZ || regno == DZ) {
                /* Read-only registers. */
@@ -557,16 +552,19 @@ write_register(int regno, char *val)
        } else if (regno == PID) {
                /* 32-bit register. (Even though we already checked SRS and WZ, we cannot
                   combine this with the EXS - SPC write since SRS and WZ have different size.) */
-               hex2mem((unsigned char *)&reg.pid, val, sizeof(unsigned int));
+               if (hex2bin((unsigned char *)&reg.pid, val, sizeof(unsigned int)))
+                       status = E08;
 
        } else if (regno == SRS) {
                /* 8-bit register. */
-               hex2mem((unsigned char *)&reg.srs, val, sizeof(unsigned char));
+               if (hex2bin((unsigned char *)&reg.srs, val, sizeof(unsigned char)))
+                       status = E08;
 
        } else if (regno >= EXS && regno <= SPC) {
                /* Consecutive 32-bit registers. */
-               hex2mem((unsigned char *)&reg.exs + (regno - EXS) * sizeof(unsigned int),
-                        val, sizeof(unsigned int));
+               if (hex2bin((unsigned char *)&reg.exs + (regno - EXS) * sizeof(unsigned int),
+                           val, sizeof(unsigned int)))
+                       status = E08;
 
        } else if (regno == PC) {
                /* Pseudo-register. Treat as read-only. */
@@ -574,7 +572,9 @@ write_register(int regno, char *val)
 
        } else if (regno >= S0 && regno <= S15) {
                /* 32-bit registers. */
-               hex2mem((unsigned char *)&sreg.s0_0 + (reg.srs * 16 * sizeof(unsigned int)) + (regno - S0) * sizeof(unsigned int), val, sizeof(unsigned int));
+               if (hex2bin((unsigned char *)&sreg.s0_0 + (reg.srs * 16 * sizeof(unsigned int)) + (regno - S0) * sizeof(unsigned int),
+                          val, sizeof(unsigned int)))
+                       status = E08;
        } else {
                /* Non-existing register. */
                status = E05;
@@ -630,19 +630,6 @@ read_register(char regno, unsigned int *valptr)
 }
 
 /********************************** Packet I/O ******************************/
-/* Returns the integer equivalent of a hexadecimal character. */
-static int
-hex(char ch)
-{
-       if ((ch >= 'a') && (ch <= 'f'))
-               return (ch - 'a' + 10);
-       if ((ch >= '0') && (ch <= '9'))
-               return (ch - '0');
-       if ((ch >= 'A') && (ch <= 'F'))
-               return (ch - 'A' + 10);
-       return -1;
-}
-
 /* Convert the memory, pointed to by mem into hexadecimal representation.
    Put the result in buf, and return a pointer to the last character
    in buf (null). */
@@ -689,22 +676,6 @@ mem2hex_nbo(char *buf, unsigned char *mem, int count)
        return buf;
 }
 
-/* Convert the array, in hexadecimal representation, pointed to by buf into
-   binary representation. Put the result in mem, and return a pointer to
-   the character after the last byte written. */
-static unsigned char*
-hex2mem(unsigned char *mem, char *buf, int count)
-{
-       int i;
-       unsigned char ch;
-       for (i = 0; i < count; i++) {
-               ch = hex (*buf++) << 4;
-               ch = ch + hex (*buf++);
-               *mem++ = ch;
-       }
-       return mem;
-}
-
 /* Put the content of the array, in binary representation, pointed to by buf
    into memory pointed to by mem, and return a pointer to the character after
    the last byte written.
@@ -763,8 +734,8 @@ getpacket(char *buffer)
                buffer[count] = 0;
 
                if (ch == '#') {
-                       xmitcsum = hex(getDebugChar()) << 4;
-                       xmitcsum += hex(getDebugChar());
+                       xmitcsum = hex_to_bin(getDebugChar()) << 4;
+                       xmitcsum += hex_to_bin(getDebugChar());
                        if (checksum != xmitcsum) {
                                /* Wrong checksum */
                                putDebugChar('-');
@@ -1304,14 +1275,17 @@ handle_exception(int sigval)
                                /* Write registers. GXX..XX
                                   Each byte of register data  is described by two hex digits.
                                   Success: OK
-                                  Failure: void. */
+                                  Failure: E08. */
                                /* General and special registers. */
-                               hex2mem((char *)&reg, &input_buffer[1], sizeof(registers));
+                               if (hex2bin((char *)&reg, &input_buffer[1], sizeof(registers)))
+                                       gdb_cris_strcpy(output_buffer, error_message[E08]);
                                /* Support registers. */
-                               hex2mem((char *)&sreg + (reg.srs * 16 * sizeof(unsigned int)),
+                               else if (hex2bin((char *)&sreg + (reg.srs * 16 * sizeof(unsigned int)),
                                        &input_buffer[1] + sizeof(registers),
-                                       16 * sizeof(unsigned int));
-                               gdb_cris_strcpy(output_buffer, "OK");
+                                       16 * sizeof(unsigned int)))
+                                       gdb_cris_strcpy(output_buffer, error_message[E08]);
+                               else
+                                       gdb_cris_strcpy(output_buffer, "OK");
                                break;
 
                        case 'P':
@@ -1338,6 +1312,10 @@ handle_exception(int sigval)
                                                        /* Do not support non-existing registers. */
                                                        gdb_cris_strcpy(output_buffer, error_message[E05]);
                                                        break;
+                                               case E08:
+                                                       /* Invalid parameter. */
+                                                       gdb_cris_strcpy(output_buffer, error_message[E08]);
+                                                       break;
                                                default:
                                                        /* Valid register number. */
                                                        gdb_cris_strcpy(output_buffer, "OK");
@@ -1380,7 +1358,7 @@ handle_exception(int sigval)
                                   AA..AA is the start address,  LLLL is the number of bytes, and
                                   XX..XX is the hexadecimal data.
                                   Success: OK
-                                  Failure: void. */
+                                  Failure: E08. */
                                {
                                        char *lenptr;
                                        char *dataptr;
@@ -1389,13 +1367,15 @@ handle_exception(int sigval)
                                        int len = gdb_cris_strtol(lenptr+1, &dataptr, 16);
                                        if (*lenptr == ',' && *dataptr == ':') {
                                                if (input_buffer[0] == 'M') {
-                                                       hex2mem(addr, dataptr + 1, len);
+                                                       if (hex2bin(addr, dataptr + 1, len))
+                                                               gdb_cris_strcpy(output_buffer, error_message[E08]);
+                                                       else
+                                                               gdb_cris_strcpy(output_buffer, "OK");
                                                } else /* X */ {
                                                        bin2mem(addr, dataptr + 1, len);
+                                                       gdb_cris_strcpy(output_buffer, "OK");
                                                }
-                                               gdb_cris_strcpy(output_buffer, "OK");
-                                       }
-                                       else {
+                                       } else {
                                                gdb_cris_strcpy(output_buffer, error_message[E06]);
                                        }
                                }
index cd1865d68b2e031d55149c6e29c68bc2041de5a4..fe50287aa928c415bc98dd359eff13bcaaba76b6 100644 (file)
@@ -128,10 +128,6 @@ static struct i2c_board_info __initdata i2c_info[] = {
        {I2C_BOARD_INFO("tmp100", 0x4E)},
 #ifdef CONFIG_RTC_DRV_PCF8563
        {I2C_BOARD_INFO("pcf8563", 0x51)},
-#endif
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       {I2C_BOARD_INFO("vgpio", 0x20)},
-       {I2C_BOARD_INFO("vgpio", 0x21)},
 #endif
        {I2C_BOARD_INFO("pca9536", 0x41)},
        {I2C_BOARD_INFO("fnp300", 0x40)},
@@ -146,10 +142,6 @@ static struct i2c_board_info __initdata i2c_info2[] = {
        {I2C_BOARD_INFO("tmp100", 0x4C)},
        {I2C_BOARD_INFO("tmp100", 0x4D)},
        {I2C_BOARD_INFO("tmp100", 0x4E)},
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-       {I2C_BOARD_INFO("vgpio", 0x20)},
-       {I2C_BOARD_INFO("vgpio", 0x21)},
-#endif
        {I2C_BOARD_INFO("pca9536", 0x41)},
        {I2C_BOARD_INFO("fnp300", 0x40)},
        {I2C_BOARD_INFO("fnp300", 0x42)},
index 18a227196a41b75e0fb84929976182ed4480e7a2..0cc6eebacbed7dd5b3b1470a15b3abc612de78ab 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for the linux kernel.
 #
 
-obj-y   := dma.o pinmux.o io.o arbiter.o
+obj-y   := dma.o pinmux.o arbiter.o
 
 clean:
 
diff --git a/arch/cris/arch-v32/mach-a3/io.c b/arch/cris/arch-v32/mach-a3/io.c
deleted file mode 100644 (file)
index 090ceb9..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Helper functions for I/O pins.
- *
- * Copyright (c) 2005-2007 Axis Communications AB.
- */
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/ctype.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/io.h>
-#include <mach/pinmux.h>
-#include <hwregs/gio_defs.h>
-
-struct crisv32_ioport crisv32_ioports[] = {
-       {
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_oe),
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_dout),
-               (unsigned long *)REG_ADDR(gio, regi_gio, r_pa_din),
-               32
-       },
-       {
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_oe),
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_dout),
-               (unsigned long *)REG_ADDR(gio, regi_gio, r_pb_din),
-               32
-       },
-       {
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_oe),
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_dout),
-               (unsigned long *)REG_ADDR(gio, regi_gio, r_pc_din),
-               16
-       },
-};
-
-#define NBR_OF_PORTS ARRAY_SIZE(crisv32_ioports)
-
-struct crisv32_iopin crisv32_led_net0_green;
-struct crisv32_iopin crisv32_led_net0_red;
-struct crisv32_iopin crisv32_led2_green;
-struct crisv32_iopin crisv32_led2_red;
-struct crisv32_iopin crisv32_led3_green;
-struct crisv32_iopin crisv32_led3_red;
-
-/* Dummy port used when green LED and red LED is on the same bit */
-static unsigned long io_dummy;
-static struct crisv32_ioport dummy_port = {
-       &io_dummy,
-       &io_dummy,
-       &io_dummy,
-       32
-};
-static struct crisv32_iopin dummy_led = {
-       &dummy_port,
-       0
-};
-
-static int __init crisv32_io_init(void)
-{
-       int ret = 0;
-
-       u32 i;
-
-       /* Locks *should* be dynamically initialized. */
-       for (i = 0; i < ARRAY_SIZE(crisv32_ioports); i++)
-               spin_lock_init(&crisv32_ioports[i].lock);
-       spin_lock_init(&dummy_port.lock);
-
-       /* Initialize LEDs */
-#if (defined(CONFIG_ETRAX_NBR_LED_GRP_ONE) || defined(CONFIG_ETRAX_NBR_LED_GRP_TWO))
-       ret += crisv32_io_get_name(&crisv32_led_net0_green,
-               CONFIG_ETRAX_LED_G_NET0);
-       crisv32_io_set_dir(&crisv32_led_net0_green, crisv32_io_dir_out);
-       if (strcmp(CONFIG_ETRAX_LED_G_NET0, CONFIG_ETRAX_LED_R_NET0)) {
-               ret += crisv32_io_get_name(&crisv32_led_net0_red,
-                       CONFIG_ETRAX_LED_R_NET0);
-               crisv32_io_set_dir(&crisv32_led_net0_red, crisv32_io_dir_out);
-       } else
-               crisv32_led_net0_red = dummy_led;
-#endif
-
-       ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_V32_LED2G);
-       ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_V32_LED2R);
-       ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_V32_LED3G);
-       ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_V32_LED3R);
-
-       crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out);
-       crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out);
-       crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out);
-       crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out);
-
-       return ret;
-}
-
-__initcall(crisv32_io_init);
-
-int crisv32_io_get(struct crisv32_iopin *iopin,
-       unsigned int port, unsigned int pin)
-{
-       if (port > NBR_OF_PORTS)
-               return -EINVAL;
-       if (port > crisv32_ioports[port].pin_count)
-               return -EINVAL;
-
-       iopin->bit = 1 << pin;
-       iopin->port = &crisv32_ioports[port];
-
-       if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio))
-               return -EIO;
-
-       return 0;
-}
-
-int crisv32_io_get_name(struct crisv32_iopin *iopin, const char *name)
-{
-       int port;
-       int pin;
-
-       if (toupper(*name) == 'P')
-               name++;
-
-       if (toupper(*name) < 'A' || toupper(*name) > 'E')
-               return -EINVAL;
-
-       port = toupper(*name) - 'A';
-       name++;
-       pin = simple_strtoul(name, NULL, 10);
-
-       if (pin < 0 || pin > crisv32_ioports[port].pin_count)
-               return -EINVAL;
-
-       iopin->bit = 1 << pin;
-       iopin->port = &crisv32_ioports[port];
-
-       if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio))
-               return -EIO;
-
-       return 0;
-}
-
-#ifdef CONFIG_PCI
-/* PCI I/O access stuff */
-struct cris_io_operations *cris_iops = NULL;
-EXPORT_SYMBOL(cris_iops);
-#endif
-
index 774de82abef6a5453ccc6abcf5a47b83805da5e8..7d1ab972bc0f91600719ffe844969538ca603c20 100644 (file)
@@ -192,25 +192,6 @@ config ETRAX_DEF_GIO_PE_OUT
          Configures the initial data for the general port E bits.  Most
          products should use 00000 here.
 
-config ETRAX_DEF_GIO_PV_OE
-       hex "GIO_PV_OE"
-       depends on ETRAX_VIRTUAL_GPIO
-       default "0000"
-       help
-         Configures the direction of virtual general port V bits. 1 is out,
-         0 is in. This is often totally different depending on the product
-         used. These bits are used for all kinds of stuff. If you don't know
-         what to use, it is always safe to put all as inputs, although
-         floating inputs isn't good.
-
-config ETRAX_DEF_GIO_PV_OUT
-       hex "GIO_PV_OUT"
-       depends on ETRAX_VIRTUAL_GPIO
-       default "0000"
-       help
-         Configures the initial data for the virtual general port V bits.
-         Most products should use 0000 here.
-
 endmenu
 
 endif
index 18a227196a41b75e0fb84929976182ed4480e7a2..0cc6eebacbed7dd5b3b1470a15b3abc612de78ab 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for the linux kernel.
 #
 
-obj-y   := dma.o pinmux.o io.o arbiter.o
+obj-y   := dma.o pinmux.o arbiter.o
 
 clean:
 
diff --git a/arch/cris/arch-v32/mach-fs/io.c b/arch/cris/arch-v32/mach-fs/io.c
deleted file mode 100644 (file)
index a695866..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Helper functions for I/O pins.
- *
- * Copyright (c) 2004-2007 Axis Communications AB.
- */
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/ctype.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/io.h>
-#include <mach/pinmux.h>
-#include <hwregs/gio_defs.h>
-
-#ifndef DEBUG
-#define DEBUG(x)
-#endif
-
-struct crisv32_ioport crisv32_ioports[] = {
-       {
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_oe),
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_dout),
-               (unsigned long *)REG_ADDR(gio, regi_gio, r_pa_din),
-               8
-       },
-       {
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_oe),
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_dout),
-               (unsigned long *)REG_ADDR(gio, regi_gio, r_pb_din),
-               18
-       },
-       {
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_oe),
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_dout),
-               (unsigned long *)REG_ADDR(gio, regi_gio, r_pc_din),
-               18
-       },
-       {
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pd_oe),
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pd_dout),
-               (unsigned long *)REG_ADDR(gio, regi_gio, r_pd_din),
-               18
-       },
-       {
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pe_oe),
-               (unsigned long *)REG_ADDR(gio, regi_gio, rw_pe_dout),
-               (unsigned long *)REG_ADDR(gio, regi_gio, r_pe_din),
-               18
-       }
-};
-
-#define NBR_OF_PORTS ARRAY_SIZE(crisv32_ioports)
-
-struct crisv32_iopin crisv32_led_net0_green;
-struct crisv32_iopin crisv32_led_net0_red;
-struct crisv32_iopin crisv32_led_net1_green;
-struct crisv32_iopin crisv32_led_net1_red;
-struct crisv32_iopin crisv32_led2_green;
-struct crisv32_iopin crisv32_led2_red;
-struct crisv32_iopin crisv32_led3_green;
-struct crisv32_iopin crisv32_led3_red;
-
-/* Dummy port used when green LED and red LED is on the same bit */
-static unsigned long io_dummy;
-static struct crisv32_ioport dummy_port = {
-       &io_dummy,
-       &io_dummy,
-       &io_dummy,
-       18
-};
-static struct crisv32_iopin dummy_led = {
-       &dummy_port,
-       0
-};
-
-static int __init crisv32_io_init(void)
-{
-       int ret = 0;
-
-       u32 i;
-
-       /* Locks *should* be dynamically initialized. */
-       for (i = 0; i < ARRAY_SIZE(crisv32_ioports); i++)
-               spin_lock_init(&crisv32_ioports[i].lock);
-       spin_lock_init(&dummy_port.lock);
-
-       /* Initialize LEDs */
-#if (defined(CONFIG_ETRAX_NBR_LED_GRP_ONE) || defined(CONFIG_ETRAX_NBR_LED_GRP_TWO))
-       ret +=
-           crisv32_io_get_name(&crisv32_led_net0_green,
-                               CONFIG_ETRAX_LED_G_NET0);
-       crisv32_io_set_dir(&crisv32_led_net0_green, crisv32_io_dir_out);
-       if (strcmp(CONFIG_ETRAX_LED_G_NET0, CONFIG_ETRAX_LED_R_NET0)) {
-               ret +=
-                   crisv32_io_get_name(&crisv32_led_net0_red,
-                                       CONFIG_ETRAX_LED_R_NET0);
-               crisv32_io_set_dir(&crisv32_led_net0_red, crisv32_io_dir_out);
-       } else
-               crisv32_led_net0_red = dummy_led;
-#endif
-
-#ifdef CONFIG_ETRAX_NBR_LED_GRP_TWO
-       ret +=
-           crisv32_io_get_name(&crisv32_led_net1_green,
-                               CONFIG_ETRAX_LED_G_NET1);
-       crisv32_io_set_dir(&crisv32_led_net1_green, crisv32_io_dir_out);
-       if (strcmp(CONFIG_ETRAX_LED_G_NET1, CONFIG_ETRAX_LED_R_NET1)) {
-               crisv32_io_get_name(&crisv32_led_net1_red,
-                                   CONFIG_ETRAX_LED_R_NET1);
-               crisv32_io_set_dir(&crisv32_led_net1_red, crisv32_io_dir_out);
-       } else
-               crisv32_led_net1_red = dummy_led;
-#endif
-
-       ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_V32_LED2G);
-       ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_V32_LED2R);
-       ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_V32_LED3G);
-       ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_V32_LED3R);
-
-       crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out);
-       crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out);
-       crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out);
-       crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out);
-
-       return ret;
-}
-
-__initcall(crisv32_io_init);
-
-int crisv32_io_get(struct crisv32_iopin *iopin,
-                  unsigned int port, unsigned int pin)
-{
-       if (port > NBR_OF_PORTS)
-               return -EINVAL;
-       if (port > crisv32_ioports[port].pin_count)
-               return -EINVAL;
-
-       iopin->bit = 1 << pin;
-       iopin->port = &crisv32_ioports[port];
-
-       /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */
-       /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */
-       if (port != 0 && crisv32_pinmux_alloc(port - 1, pin, pin, pinmux_gpio))
-               return -EIO;
-       DEBUG(printk(KERN_DEBUG "crisv32_io_get: Allocated pin %d on port %d\n",
-               pin, port));
-
-       return 0;
-}
-
-int crisv32_io_get_name(struct crisv32_iopin *iopin, const char *name)
-{
-       int port;
-       int pin;
-
-       if (toupper(*name) == 'P')
-               name++;
-
-       if (toupper(*name) < 'A' || toupper(*name) > 'E')
-               return -EINVAL;
-
-       port = toupper(*name) - 'A';
-       name++;
-       pin = simple_strtoul(name, NULL, 10);
-
-       if (pin < 0 || pin > crisv32_ioports[port].pin_count)
-               return -EINVAL;
-
-       iopin->bit = 1 << pin;
-       iopin->port = &crisv32_ioports[port];
-
-       /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */
-       /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */
-       if (port != 0 && crisv32_pinmux_alloc(port - 1, pin, pin, pinmux_gpio))
-               return -EIO;
-
-       DEBUG(printk(KERN_DEBUG
-               "crisv32_io_get_name: Allocated pin %d on port %d\n",
-               pin, port));
-
-       return 0;
-}
-
-#ifdef CONFIG_PCI
-/* PCI I/O access stuff */
-struct cris_io_operations *cris_iops = NULL;
-EXPORT_SYMBOL(cris_iops);
-#endif
diff --git a/arch/cris/boot/dts/artpec3.dtsi b/arch/cris/boot/dts/artpec3.dtsi
new file mode 100644 (file)
index 0000000..be15be6
--- /dev/null
@@ -0,0 +1,46 @@
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       interrupt-parent = <&intc>;
+
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               cpu@0 {
+                       device_type = "cpu";
+                       model = "axis,crisv32";
+                       reg = <0>;
+               };
+       };
+
+       soc {
+               compatible = "simple-bus";
+               model = "artpec3";
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               intc: interrupt-controller {
+                       compatible = "axis,crisv32-intc";
+                       reg = <0xb002a000 0x1000>;
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+               };
+
+               gio: gpio@b0020000 {
+                       compatible = "axis,artpec3-gio";
+                       reg = <0xb0020000 0x1000>;
+                       interrupts = <61>;
+                       gpio-controller;
+                       #gpio-cells = <3>;
+               };
+
+               serial@b003e000 {
+                       compatible = "axis,etraxfs-uart";
+                       reg = <0xb003e000 0x1000>;
+                       interrupts = <64>;
+                       status = "disabled";
+               };
+       };
+};
index 4fa5a3f9d0ec1ec30a52a91b22c18377888f0c4f..b9a230d108748d97fcc2f81f2b6b6db0d97dfd0a 100644 (file)
@@ -1,5 +1,7 @@
 /dts-v1/;
 
+#include <dt-bindings/gpio/gpio.h>
+
 /include/ "etraxfs.dtsi"
 
 / {
                        status = "okay";
                };
        };
+
+       spi {
+               compatible = "spi-gpio";
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               gpio-sck = <&gio 1 0 0xd>;
+               gpio-miso = <&gio 4 0 0xd>;
+               gpio-mosi = <&gio 0 0 0xd>;
+               cs-gpios = <&gio 3 0 0xd>;
+               num-chipselects = <1>;
+
+               temp-sensor@0 {
+                       compatible = "ti,lm70";
+                       reg = <0>;
+
+                       spi-max-frequency = <100000>;
+               };
+       };
+
+       i2c {
+               compatible = "i2c-gpio";
+               gpios = <&gio 5 0 0xd>, <&gio 6 0 0xd>;
+               i2c-gpio,delay-us = <2>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               rtc@51 {
+                       compatible = "nxp,pcf8563";
+                       reg = <0x51>;
+               };
+       };
+
+       leds {
+               compatible = "gpio-leds";
+
+               network {
+                       label = "network";
+                       gpios = <&gio 2 GPIO_ACTIVE_LOW 0xa>;
+               };
+
+               status {
+                       label = "status";
+                       gpios = <&gio 3 GPIO_ACTIVE_LOW 0xa>;
+                       linux,default-trigger = "heartbeat";
+               };
+       };
 };
index 909bcedc356580da301ad06da0c3adbbe0755c99..bf1b8582d4d803408411c59e779b46ea22f8dba8 100644 (file)
                        #interrupt-cells = <1>;
                };
 
+               gio: gpio@b001a000 {
+                       compatible = "axis,etraxfs-gio";
+                       reg = <0xb001a000 0x1000>;
+                       interrupts = <50>;
+                       gpio-controller;
+                       #gpio-cells = <3>;
+               };
+
                serial@b00260000 {
                        compatible = "axis,etraxfs-uart";
                        reg = <0xb0026000 0x1000>;
diff --git a/arch/cris/boot/dts/include/dt-bindings b/arch/cris/boot/dts/include/dt-bindings
new file mode 120000 (symlink)
index 0000000..08c00e4
--- /dev/null
@@ -0,0 +1 @@
+../../../../../include/dt-bindings
\ No newline at end of file
diff --git a/arch/cris/boot/dts/p1343.dts b/arch/cris/boot/dts/p1343.dts
new file mode 100644 (file)
index 0000000..fab7bdb
--- /dev/null
@@ -0,0 +1,76 @@
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/include/ "artpec3.dtsi"
+
+/ {
+       model = "Axis P1343 Network Camera";
+       compatible = "axis,p1343";
+
+       aliases {
+               serial0 = &uart0;
+       };
+
+       soc {
+               uart0: serial@b003e000 {
+                       status = "okay";
+               };
+       };
+
+       i2c {
+               compatible = "i2c-gpio";
+               gpios = <&gio 3 0 0xa>, <&gio 2 0 0xa>;
+               i2c-gpio,delay-us = <2>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               rtc@51 {
+                       compatible = "nxp,pcf8563";
+                       reg = <0x51>;
+               };
+       };
+
+       leds {
+               compatible = "gpio-leds";
+
+               status_green {
+                       label = "status:green";
+                       gpios = <&gio 0 GPIO_ACTIVE_LOW 0xc>;
+                       linux,default-trigger = "heartbeat";
+               };
+
+               status_red {
+                       label = "status:red";
+                       gpios = <&gio 1 GPIO_ACTIVE_LOW 0xc>;
+               };
+
+               network_green {
+                       label = "network:green";
+                       gpios = <&gio 2 GPIO_ACTIVE_LOW 0xc>;
+               };
+
+               network_red {
+                       label = "network:red";
+                       gpios = <&gio 3 GPIO_ACTIVE_LOW 0xc>;
+               };
+
+               power_red {
+                       label = "power:red";
+                       gpios = <&gio 4 GPIO_ACTIVE_LOW 0xc>;
+               };
+       };
+
+       gpio_keys {
+               compatible = "gpio-keys";
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               activity-button@0 {
+                       label = "Activity Button";
+                       linux,code = <KEY_FN>;
+                       gpios = <&gio 13 GPIO_ACTIVE_LOW 0xd>;
+               };
+       };
+};
index af55df0994b38580439c29310e8cc1495474e235..1c05492f3eb20a2c219e7e6ce5f1a58d48dfc882 100644 (file)
@@ -281,9 +281,6 @@ wait_ser:
 #ifdef CONFIG_ETRAX_PB_LEDS
        move.b  $r2, [R_PORT_PB_DATA]
 #endif
-#ifdef CONFIG_ETRAX_90000000_LEDS
-       move.b  $r2, [0x90000000]
-#endif
 #endif
 
        ;; check if we got something on the serial port
diff --git a/arch/cris/include/arch-v32/arch/io.h b/arch/cris/include/arch-v32/arch/io.h
deleted file mode 100644 (file)
index adc5484..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-#ifndef _ASM_ARCH_CRIS_IO_H
-#define _ASM_ARCH_CRIS_IO_H
-
-#include <linux/spinlock.h>
-#include <hwregs/reg_map.h>
-#include <hwregs/reg_rdwr.h>
-#include <hwregs/gio_defs.h>
-
-enum crisv32_io_dir
-{
-  crisv32_io_dir_in = 0,
-  crisv32_io_dir_out = 1
-};
-
-struct crisv32_ioport
-{
-  volatile unsigned long *oe;
-  volatile unsigned long *data;
-  volatile unsigned long *data_in;
-  unsigned int pin_count;
-  spinlock_t lock;
-};
-
-struct crisv32_iopin
-{
-  struct crisv32_ioport* port;
-  int bit;
-};
-
-extern struct crisv32_ioport crisv32_ioports[];
-
-extern struct crisv32_iopin crisv32_led1_green;
-extern struct crisv32_iopin crisv32_led1_red;
-extern struct crisv32_iopin crisv32_led2_green;
-extern struct crisv32_iopin crisv32_led2_red;
-extern struct crisv32_iopin crisv32_led3_green;
-extern struct crisv32_iopin crisv32_led3_red;
-
-extern struct crisv32_iopin crisv32_led_net0_green;
-extern struct crisv32_iopin crisv32_led_net0_red;
-extern struct crisv32_iopin crisv32_led_net1_green;
-extern struct crisv32_iopin crisv32_led_net1_red;
-
-static inline void crisv32_io_set(struct crisv32_iopin *iopin, int val)
-{
-       unsigned long flags;
-       spin_lock_irqsave(&iopin->port->lock, flags);
-
-       if (iopin->port->data) {
-               if (val)
-                       *iopin->port->data |= iopin->bit;
-               else
-                       *iopin->port->data &= ~iopin->bit;
-       }
-
-       spin_unlock_irqrestore(&iopin->port->lock, flags);
-}
-
-static inline void crisv32_io_set_dir(struct crisv32_iopin* iopin,
-                              enum crisv32_io_dir dir)
-{
-       unsigned long flags;
-       spin_lock_irqsave(&iopin->port->lock, flags);
-
-       if (iopin->port->oe) {
-               if (dir == crisv32_io_dir_in)
-                       *iopin->port->oe &= ~iopin->bit;
-               else
-                       *iopin->port->oe |= iopin->bit;
-       }
-
-       spin_unlock_irqrestore(&iopin->port->lock, flags);
-}
-
-static inline int crisv32_io_rd(struct crisv32_iopin* iopin)
-{
-       return ((*iopin->port->data_in & iopin->bit) ? 1 : 0);
-}
-
-int crisv32_io_get(struct crisv32_iopin* iopin,
-                   unsigned int port, unsigned int pin);
-int crisv32_io_get_name(struct crisv32_iopin* iopin,
-                       const char *name);
-
-#define CRIS_LED_OFF    0x00
-#define CRIS_LED_GREEN  0x01
-#define CRIS_LED_RED    0x02
-#define CRIS_LED_ORANGE (CRIS_LED_GREEN | CRIS_LED_RED)
-
-#if (defined(CONFIG_ETRAX_NBR_LED_GRP_ONE) || defined(CONFIG_ETRAX_NBR_LED_GRP_TWO))
-#define CRIS_LED_NETWORK_GRP0_SET(x)                          \
-       do {                                             \
-               CRIS_LED_NETWORK_GRP0_SET_G((x) & CRIS_LED_GREEN); \
-               CRIS_LED_NETWORK_GRP0_SET_R((x) & CRIS_LED_RED);   \
-       } while (0)
-#else
-#define CRIS_LED_NETWORK_GRP0_SET(x) while (0) {}
-#endif
-
-#define CRIS_LED_NETWORK_GRP0_SET_G(x) \
-       crisv32_io_set(&crisv32_led_net0_green, !(x));
-
-#define CRIS_LED_NETWORK_GRP0_SET_R(x) \
-       crisv32_io_set(&crisv32_led_net0_red, !(x));
-
-#if defined(CONFIG_ETRAX_NBR_LED_GRP_TWO)
-#define CRIS_LED_NETWORK_GRP1_SET(x)                          \
-       do {                                             \
-               CRIS_LED_NETWORK_GRP1_SET_G((x) & CRIS_LED_GREEN); \
-               CRIS_LED_NETWORK_GRP1_SET_R((x) & CRIS_LED_RED);   \
-       } while (0)
-#else
-#define CRIS_LED_NETWORK_GRP1_SET(x) while (0) {}
-#endif
-
-#define CRIS_LED_NETWORK_GRP1_SET_G(x) \
-       crisv32_io_set(&crisv32_led_net1_green, !(x));
-
-#define CRIS_LED_NETWORK_GRP1_SET_R(x) \
-       crisv32_io_set(&crisv32_led_net1_red, !(x));
-
-#define CRIS_LED_ACTIVE_SET(x)                           \
-       do {                                        \
-               CRIS_LED_ACTIVE_SET_G((x) & CRIS_LED_GREEN);  \
-               CRIS_LED_ACTIVE_SET_R((x) & CRIS_LED_RED);    \
-       } while (0)
-
-#define CRIS_LED_ACTIVE_SET_G(x) \
-       crisv32_io_set(&crisv32_led2_green, !(x));
-#define CRIS_LED_ACTIVE_SET_R(x) \
-       crisv32_io_set(&crisv32_led2_red, !(x));
-#define CRIS_LED_DISK_WRITE(x) \
-         do{\
-               crisv32_io_set(&crisv32_led3_green, !(x)); \
-               crisv32_io_set(&crisv32_led3_red, !(x));   \
-        }while(0)
-#define CRIS_LED_DISK_READ(x) \
-       crisv32_io_set(&crisv32_led3_green, !(x));
-
-#endif
index 0c1b4d3a34e749b222c1d0ec0af3a56a4b80b952..8270a1bbfdb6d0a7468f39f52884792281b5f2d3 100644 (file)
@@ -4,7 +4,7 @@
 #include <hwregs/intr_vect.h>
 
 /* Number of non-cpu interrupts. */
-#define NR_IRQS NBR_INTR_VECT /* Exceptions + IRQs */
+#define NR_IRQS (NBR_INTR_VECT + 256) /* Exceptions + IRQs */
 #define FIRST_IRQ 0x31 /* Exception number for first IRQ */
 #define NR_REAL_IRQS (NBR_INTR_VECT - FIRST_IRQ) /* IRQs */
 #if NR_REAL_IRQS > 32
index 10ce36cf79a911f6ae4d55e7eac35f812710b50c..70aa448256b0ba69828e487ab4ab345f7d07f5c1 100644 (file)
@@ -45,8 +45,7 @@
    assumed that we want to share code when debugging (exposes more
    trouble). */
 #ifndef SHARE_LIB_CORE
-# if (defined(__KERNEL__) || !defined(RELOC_DEBUG)) \
-     && !defined(CONFIG_SHARE_SHLIB_CORE)
+# if (defined(__KERNEL__) || !defined(RELOC_DEBUG))
 #  define SHARE_LIB_CORE 0
 # else
 #  define SHARE_LIB_CORE 1
index 752a3f45df60cd475b51b21bb567b9bd48f90581..cce8664d5dd67b105866d476b99e1123de0b8c8b 100644 (file)
@@ -2,7 +2,9 @@
 #define _ASM_CRIS_IO_H
 
 #include <asm/page.h>   /* for __va, __pa */
+#ifdef CONFIG_ETRAX_ARCH_V10
 #include <arch/io.h>
+#endif
 #include <asm-generic/iomap.h>
 #include <linux/kernel.h>
 
index 461c089db765af02188d880741b505a7c781dddc..c6e7d57c8b248dff6d7b0cf57d113c98499852b1 100644 (file)
  *       g1-g7 and g25-g31 is both input and outputs but on different pins
  *       Also note that some bits change pins depending on what interfaces
  *       are enabled.
- *
- * For ETRAX FS (CONFIG_ETRAXFS):
- * /dev/gpioa  minor 0,  8 bit GPIO, each bit can change direction
- * /dev/gpiob  minor 1, 18 bit GPIO, each bit can change direction
- * /dev/gpioc  minor 3, 18 bit GPIO, each bit can change direction
- * /dev/gpiod  minor 4, 18 bit GPIO, each bit can change direction
- * /dev/gpioe  minor 5, 18 bit GPIO, each bit can change direction
- * /dev/leds   minor 2, Access to leds depending on kernelconfig
- *
- * For ARTPEC-3 (CONFIG_CRIS_MACH_ARTPEC3):
- * /dev/gpioa  minor 0, 32 bit GPIO, each bit can change direction
- * /dev/gpiob  minor 1, 32 bit GPIO, each bit can change direction
- * /dev/gpioc  minor 3, 16 bit GPIO, each bit can change direction
- * /dev/gpiod  minor 4, 32 bit GPIO, input only
- * /dev/leds   minor 2, Access to leds depending on kernelconfig
- * /dev/pwm0   minor 16, PWM channel 0 on PA30
- * /dev/pwm1   minor 17, PWM channel 1 on PA31
- * /dev/pwm2   minor 18, PWM channel 2 on PB26
- * /dev/ppwm   minor 19, PPWM channel
- *
  */
 #ifndef _ASM_ETRAXGPIO_H
 #define _ASM_ETRAXGPIO_H
 #define ETRAXGPIO_IOCTYPE 43
 
 /* etraxgpio _IOC_TYPE, bits 8 to 15 in ioctl cmd */
-#ifdef CONFIG_ETRAX_ARCH_V10
 #define GPIO_MINOR_A 0
 #define GPIO_MINOR_B 1
 #define GPIO_MINOR_LEDS 2
 #define GPIO_MINOR_G 3
 #define GPIO_MINOR_LAST 3
 #define GPIO_MINOR_LAST_REAL GPIO_MINOR_LAST
-#endif
-
-#ifdef CONFIG_ETRAXFS
-#define GPIO_MINOR_A 0
-#define GPIO_MINOR_B 1
-#define GPIO_MINOR_LEDS 2
-#define GPIO_MINOR_C 3
-#define GPIO_MINOR_D 4
-#define GPIO_MINOR_E 5
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-#define GPIO_MINOR_V 6
-#define GPIO_MINOR_LAST 6
-#else
-#define GPIO_MINOR_LAST 5
-#endif
-#define GPIO_MINOR_LAST_REAL GPIO_MINOR_LAST
-#endif
-
-#ifdef CONFIG_CRIS_MACH_ARTPEC3
-#define GPIO_MINOR_A 0
-#define GPIO_MINOR_B 1
-#define GPIO_MINOR_LEDS 2
-#define GPIO_MINOR_C 3
-#define GPIO_MINOR_D 4
-#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
-#define GPIO_MINOR_V 6
-#define GPIO_MINOR_LAST 6
-#else
-#define GPIO_MINOR_LAST 4
-#endif
-#define GPIO_MINOR_FIRST_PWM 16
-#define GPIO_MINOR_PWM0 (GPIO_MINOR_FIRST_PWM+0)
-#define GPIO_MINOR_PWM1 (GPIO_MINOR_FIRST_PWM+1)
-#define GPIO_MINOR_PWM2 (GPIO_MINOR_FIRST_PWM+2)
-#define GPIO_MINOR_PPWM (GPIO_MINOR_FIRST_PWM+3)
-#define GPIO_MINOR_LAST_PWM GPIO_MINOR_PPWM
-#define GPIO_MINOR_LAST_REAL GPIO_MINOR_LAST_PWM
-#endif
-
 
 
 /* supported ioctl _IOC_NR's */
 #define IO_SETGET_OUTPUT 0x13 /* bits set in *arg is set to output, */
                              /* *arg updated with current output pins. */
 
-/* The following ioctl's are applicable to the PWM channels only */
-
-#define IO_PWM_SET_MODE     0x20
-
-enum io_pwm_mode {
-       PWM_OFF = 0,            /* disabled, deallocated */
-       PWM_STANDARD = 1,       /* 390 kHz, duty cycle 0..255/256 */
-       PWM_FAST = 2,           /* variable freq, w/ 10ns active pulse len */
-       PWM_VARFREQ = 3,        /* individually configurable high/low periods */
-       PWM_SOFT = 4            /* software generated */
-};
-
-struct io_pwm_set_mode {
-       enum io_pwm_mode mode;
-};
-
-/* Only for mode PWM_VARFREQ. Period lo/high set in increments of 10ns
- * from 10ns (value = 0) to 81920ns (value = 8191)
- * (Resulting frequencies range from 50 MHz (10ns + 10ns) down to
- * 6.1 kHz (81920ns + 81920ns) at 50% duty cycle, to 12.2 kHz at min/max duty
- * cycle (81920 + 10ns or 10ns + 81920ns, respectively).)
- */
-#define IO_PWM_SET_PERIOD   0x21
-
-struct io_pwm_set_period {
-       unsigned int lo;                /* 0..8191 */
-       unsigned int hi;                /* 0..8191 */
-};
-
-/* Only for modes PWM_STANDARD and PWM_FAST.
- * For PWM_STANDARD, set duty cycle of 390 kHz PWM output signal, from
- * 0 (value = 0) to 255/256 (value = 255).
- * For PWM_FAST, set duty cycle of PWM output signal from
- * 0% (value = 0) to 100% (value = 255). Output signal in this mode
- * is a 10ns pulse surrounded by a high or low level depending on duty
- * cycle (except for 0% and 100% which result in a constant output).
- * Resulting output frequency varies from 50 MHz at 50% duty cycle,
- * down to 390 kHz at min/max duty cycle.
- */
-#define IO_PWM_SET_DUTY     0x22
-
-struct io_pwm_set_duty {
-       int duty;               /* 0..255 */
-};
-
-/* Returns information about the latest PWM pulse.
- * lo: Length of the latest low period, in units of 10ns.
- * hi: Length of the latest high period, in units of 10ns.
- * cnt: Time since last detected edge, in units of 10ns.
- *
- * The input source to PWM is decied by IO_PWM_SET_INPUT_SRC.
- *
- * NOTE: All PWM devices is connected to the same input source.
- */
-#define IO_PWM_GET_PERIOD   0x23
-
-struct io_pwm_get_period {
-       unsigned int lo;
-       unsigned int hi;
-       unsigned int cnt;
-};
-
-/* Sets the input source for the PWM input. For the src value see the
- * register description for gio:rw_pwm_in_cfg.
- *
- * NOTE: All PWM devices is connected to the same input source.
- */
-#define IO_PWM_SET_INPUT_SRC   0x24
-struct io_pwm_set_input_src {
-       unsigned int src;       /* 0..7 */
-};
-
-/* Sets the duty cycles in steps of 1/256, 0 = 0%, 255 = 100% duty cycle */
-#define IO_PPWM_SET_DUTY     0x25
-
-struct io_ppwm_set_duty {
-       int duty;               /* 0..255 */
-};
-
-/* Configuraton struct for the IO_PWMCLK_SET_CONFIG ioctl to configure
- * PWM capable gpio pins:
- */
-#define IO_PWMCLK_SETGET_CONFIG 0x26
-struct gpio_pwmclk_conf {
-  unsigned int gpiopin; /* The pin number based on the opened device */
-  unsigned int baseclk; /* The base clock to use, or sw will select one close*/
-  unsigned int low;     /* The number of low periods of the baseclk */
-  unsigned int high;    /* The number of high periods of the baseclk */
-};
-
-/* Examples:
- * To get a symmetric 12 MHz clock without knowing anything about the hardware:
- * baseclk = 12000000, low = 0, high = 0
- * To just get info of current setting:
- * baseclk = 0, low = 0, high = 0, the values will be updated by driver.
- */
-
 #endif
index e704f81f85cc7e9642a4ca64b88c180e70ef0f5d..31b4bd288cada7755c112eafeeab386ba6f49e95 100644 (file)
@@ -18,7 +18,6 @@
 #include <asm/pgtable.h>
 #include <asm/fasttimer.h>
 
-extern unsigned long get_cmos_time(void);
 extern void __Udiv(void);
 extern void __Umod(void);
 extern void __Div(void);
@@ -30,7 +29,6 @@ extern void __negdi2(void);
 extern void iounmap(volatile void * __iomem);
 
 /* Platform dependent support */
-EXPORT_SYMBOL(get_cmos_time);
 EXPORT_SYMBOL(loops_per_usec);
 
 /* Math functions */
index 7780d379522f49bcebddc54ddfc57f7d1d6d1e34..2dda6da7152159bb62663ff8b9d2158bdd75ce48 100644 (file)
 extern unsigned long loops_per_jiffy; /* init/main.c */
 unsigned long loops_per_usec;
 
-int set_rtc_mmss(unsigned long nowtime)
-{
-       D(printk(KERN_DEBUG "set_rtc_mmss(%lu)\n", nowtime));
-       return 0;
-}
-
-/* grab the time from the RTC chip */
-unsigned long get_cmos_time(void)
-{
-       return 0;
-}
-
-
-int update_persistent_clock(struct timespec now)
-{
-       return set_rtc_mmss(now.tv_sec);
-}
-
-void read_persistent_clock(struct timespec *ts)
-{
-       ts->tv_sec = 0;
-       ts->tv_nsec = 0;
-}
-
-
 extern void cris_profile_sample(struct pt_regs* regs);
 
 void
index 0da689def4cc66feec44f0e84e7be1c7c095817b..64f02d451aa8c915c81eebbd4c34f5808449a97b 100644 (file)
@@ -32,8 +32,8 @@
  */
 
 #define ATOMIC_INIT(i)         { (i) }
-#define atomic_read(v)         ACCESS_ONCE((v)->counter)
-#define atomic_set(v, i)       (((v)->counter) = (i))
+#define atomic_read(v)         READ_ONCE((v)->counter)
+#define atomic_set(v, i)       WRITE_ONCE(((v)->counter), (i))
 
 static inline int atomic_inc_return(atomic_t *v)
 {
index 702ee539f87da5e0e397d091a9bd56218c4173d4..4435a445ae7ec7f8d5fa88edc649d34f6dfdd257 100644 (file)
@@ -11,8 +11,8 @@
 
 #define ATOMIC_INIT(i) { (i) }
 
-#define atomic_read(v)         ACCESS_ONCE((v)->counter)
-#define atomic_set(v, i)       (((v)->counter) = i)
+#define atomic_read(v)         READ_ONCE((v)->counter)
+#define atomic_set(v, i)       WRITE_ONCE(((v)->counter), (i))
 
 #include <linux/kernel.h>
 
index 811d61f6422da107e9c4da9e03d02669eff1079b..55696c4100d468208c757f30be47806c877dfab9 100644 (file)
@@ -48,7 +48,7 @@ static inline void atomic_set(atomic_t *v, int new)
  *
  * Assumes all word reads on our architecture are atomic.
  */
-#define atomic_read(v)         ((v)->counter)
+#define atomic_read(v)         READ_ONCE((v)->counter)
 
 /**
  * atomic_xchg - atomic
index be4beeb77d57c2d2ca3a20de5ec61cfb18de38ad..8dfb5f6f6c352c45126656216edb228e15026be9 100644 (file)
 #define ATOMIC_INIT(i)         { (i) }
 #define ATOMIC64_INIT(i)       { (i) }
 
-#define atomic_read(v)         ACCESS_ONCE((v)->counter)
-#define atomic64_read(v)       ACCESS_ONCE((v)->counter)
+#define atomic_read(v)         READ_ONCE((v)->counter)
+#define atomic64_read(v)       READ_ONCE((v)->counter)
 
-#define atomic_set(v,i)                (((v)->counter) = (i))
-#define atomic64_set(v,i)      (((v)->counter) = (i))
+#define atomic_set(v,i)                WRITE_ONCE(((v)->counter), (i))
+#define atomic64_set(v,i)      WRITE_ONCE(((v)->counter), (i))
 
 #define ATOMIC_OP(op, c_op)                                            \
 static __inline__ int                                                  \
index 99c96a5e6016b50a951ba8dfccf609cb06962232..db73390568c8dd873fc045cd35773714bd35f8e1 100644 (file)
@@ -11,7 +11,7 @@
 
 
 
-#define NR_syscalls                    321 /* length of syscall table */
+#define NR_syscalls                    322 /* length of syscall table */
 
 /*
  * The following defines stop scripts/checksyscalls.sh from complaining about
index 98e94e19a5a0870fc71b34f85310f475de85697b..9038726e7d26777bf8f704f101ff86090e4d5059 100644 (file)
 #define __NR_execveat                  1342
 #define __NR_userfaultfd               1343
 #define __NR_membarrier                        1344
+#define __NR_kcmp                      1345
 
 #endif /* _UAPI_ASM_IA64_UNISTD_H */
index 37cc7a65cd3ee1fc3304d862776f438db4158b4d..dcd97f84d065435a8eca5c041f33bb67c3912797 100644 (file)
@@ -1770,5 +1770,6 @@ sys_call_table:
        data8 sys_execveat
        data8 sys_userfaultfd
        data8 sys_membarrier
+       data8 sys_kcmp                          // 1345
 
        .org sys_call_table + 8*NR_syscalls     // guard against failures to increase NR_syscalls
index 025e2a1704936250bb0403f619f70f4897826304..ea35160d632bfc073e15c912262d2190d05371b7 100644 (file)
@@ -28,7 +28,7 @@
  *
  * Atomically reads the value of @v.
  */
-#define atomic_read(v) ACCESS_ONCE((v)->counter)
+#define atomic_read(v) READ_ONCE((v)->counter)
 
 /**
  * atomic_set - set atomic variable
@@ -37,7 +37,7 @@
  *
  * Atomically sets the value of @v to @i.
  */
-#define atomic_set(v,i)        (((v)->counter) = (i))
+#define atomic_set(v,i)        WRITE_ONCE(((v)->counter), (i))
 
 #ifdef CONFIG_CHIP_M32700_TS1
 #define __ATOMIC_CLOBBER       , "r4"
index 039fac120cc0ebe31d39264ea985961e184aaf3e..4858178260f90435810c8201e220d0bee16e446e 100644 (file)
@@ -17,8 +17,8 @@
 
 #define ATOMIC_INIT(i) { (i) }
 
-#define atomic_read(v)         ACCESS_ONCE((v)->counter)
-#define atomic_set(v, i)       (((v)->counter) = i)
+#define atomic_read(v)         READ_ONCE((v)->counter)
+#define atomic_set(v, i)       WRITE_ONCE(((v)->counter), (i))
 
 /*
  * The ColdFire parts cannot do some immediate to memory operations,
index cd38f29955c87421e8142c8d4ca9e065111b57c3..2290c0cae48beb8ab9fb22a74327bf94306c9df0 100644 (file)
@@ -29,6 +29,7 @@
 
 int psc_present;
 volatile __u8 *psc;
+EXPORT_SYMBOL_GPL(psc);
 
 /*
  * Debugging dump, used in various places to see what's going on.
index c86ac37d198349bceea1ea94aa340668402cacf0..cfe9aa4223431764f04f8f6fd29f4f00c3cb336c 100644 (file)
@@ -125,8 +125,5 @@ void __init idprom_init(void)
 
        display_system_type(idprom->id_machtype);
 
-       printk("Ethernet address: %x:%x:%x:%x:%x:%x\n",
-                   idprom->id_ethaddr[0], idprom->id_ethaddr[1],
-                   idprom->id_ethaddr[2], idprom->id_ethaddr[3],
-                   idprom->id_ethaddr[4], idprom->id_ethaddr[5]);
+       printk("Ethernet address: %pM\n", idprom->id_ethaddr);
 }
index 21c4c268b86c6e87ce0342487b3ab4ebad618d59..a62581815624787a57881f50057e57a9fb2957cc 100644 (file)
@@ -3,7 +3,7 @@
 
 #define ATOMIC_INIT(i) { (i) }
 
-#define atomic_set(v, i)               ((v)->counter = (i))
+#define atomic_set(v, i)               WRITE_ONCE((v)->counter, (i))
 
 #include <linux/compiler.h>
 
index f8efe380fe8b334ef87cb91e905a52bfa1da2b48..0295d9b8d5bf2730dc9fe3fed7ab523444b2354e 100644 (file)
@@ -10,7 +10,7 @@
 
 static inline int atomic_read(const atomic_t *v)
 {
-       return (v)->counter;
+       return READ_ONCE((v)->counter);
 }
 
 /*
index 51ed599cc8942130b1c36595239ad115b6e56a8b..e970fd9cf7693fcc23dc5cedf508c63c20d63882 100644 (file)
@@ -4,6 +4,7 @@ config BCM47XX_SSB
        bool "SSB Support for Broadcom BCM47XX"
        select SYS_HAS_CPU_BMIPS32_3300
        select SSB
+       select SSB_HOST_SOC
        select SSB_DRIVER_MIPS
        select SSB_DRIVER_EXTIF
        select SSB_EMBEDDED
index 0352bc8d56b316588dbca209462700c4ffa03aa3..4f9eb05768840f93006384959f0a7dfffdb7caf2 100644 (file)
@@ -1094,7 +1094,7 @@ static int octeon_irq_gpio_xlat(struct irq_domain *d,
        unsigned int pin;
        unsigned int trigger;
 
-       if (d->of_node != node)
+       if (irq_domain_get_of_node(d) != node)
                return -EINVAL;
 
        if (intsize < 2)
@@ -2163,7 +2163,7 @@ static int octeon_irq_cib_map(struct irq_domain *d,
 
        if (hw >= host_data->max_bits) {
                pr_err("ERROR: %s mapping %u is to big!\n",
-                      d->of_node->name, (unsigned)hw);
+                      irq_domain_get_of_node(d)->name, (unsigned)hw);
                return -EINVAL;
        }
 
index 642b50946943ccebd0fc24b9e4b6cc06c4a90d63..8b7429127a1d18c192677e7b04d7d9c68e6e9875 100644 (file)
@@ -257,7 +257,6 @@ CONFIG_MMC=y
 CONFIG_MMC_BLOCK_MINORS=16
 CONFIG_MMC_TEST=m
 CONFIG_MMC_DW=y
-CONFIG_MMC_DW_IDMAC=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
 CONFIG_RTC_CLASS=y
index 4c42fd9af7778462d18bea0724da19d2cdb35d4b..f82d3af07931656a1f295b07972d6717df89a30d 100644 (file)
@@ -30,7 +30,7 @@
  *
  * Atomically reads the value of @v.
  */
-#define atomic_read(v)         ACCESS_ONCE((v)->counter)
+#define atomic_read(v)         READ_ONCE((v)->counter)
 
 /*
  * atomic_set - set atomic variable
@@ -39,7 +39,7 @@
  *
  * Atomically sets the value of @v to @i.
  */
-#define atomic_set(v, i)               ((v)->counter = (i))
+#define atomic_set(v, i)       WRITE_ONCE((v)->counter, (i))
 
 #define ATOMIC_OP(op, c_op, asm_op)                                          \
 static __inline__ void atomic_##op(int i, atomic_t * v)                              \
@@ -315,14 +315,14 @@ static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
  * @v: pointer of type atomic64_t
  *
  */
-#define atomic64_read(v)       ACCESS_ONCE((v)->counter)
+#define atomic64_read(v)       READ_ONCE((v)->counter)
 
 /*
  * atomic64_set - set atomic variable
  * @v: pointer of type atomic64_t
  * @i: required value
  */
-#define atomic64_set(v, i)     ((v)->counter = (i))
+#define atomic64_set(v, i)     WRITE_ONCE((v)->counter, (i))
 
 #define ATOMIC64_OP(op, c_op, asm_op)                                        \
 static __inline__ void atomic64_##op(long i, atomic64_t * v)                 \
index 2e52cbd20cebac71a1dc50482d48983df00f15e8..7a584e0bf93332073591ab2a179b37705f20230c 100644 (file)
@@ -12,6 +12,4 @@ obj-y                         := sead3-lcd.o sead3-display.o sead3-init.o \
                                   sead3-int.o sead3-platform.o sead3-reset.o \
                                   sead3-setup.o sead3-time.o
 
-obj-y                          += leds-sead3.o
-
 obj-$(CONFIG_EARLY_PRINTK)     += sead3-console.o
index 0c4a133f6216012c303e2b4105214c422a327976..77cb27309db27f781ec9862037074d39e928401a 100644 (file)
@@ -1251,7 +1251,7 @@ void bpf_jit_compile(struct bpf_prog *fp)
                bpf_jit_dump(fp->len, alloc_size, 2, ctx.target);
 
        fp->bpf_func = (void *)ctx.target;
-       fp->jited = true;
+       fp->jited = 1;
 
 out:
        kfree(ctx.offsets);
index 375e59140c9cef2f8f9fe36962ac3ddca399d994..ce318d5ab23b06484e063d59e564bef6a2673700 100644 (file)
@@ -34,7 +34,7 @@
  *
  * Atomically reads the value of @v.  Note that the guaranteed
  */
-#define atomic_read(v) (ACCESS_ONCE((v)->counter))
+#define atomic_read(v) READ_ONCE((v)->counter)
 
 /**
  * atomic_set - set atomic variable
@@ -43,7 +43,7 @@
  *
  * Atomically sets the value of @v to @i.  Note that the guaranteed
  */
-#define atomic_set(v, i) (((v)->counter) = (i))
+#define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
 
 #define ATOMIC_OP(op)                                                  \
 static inline void atomic_##op(int i, atomic_t *v)                     \
index 2536965d00eae4c4a8c928a65b78471628cebf3d..1d109990a02242aafb76c19da65a2f74d34367b9 100644 (file)
@@ -67,7 +67,7 @@ static __inline__ void atomic_set(atomic_t *v, int i)
 
 static __inline__ int atomic_read(const atomic_t *v)
 {
-       return ACCESS_ONCE((v)->counter);
+       return READ_ONCE((v)->counter);
 }
 
 /* exported interface */
index 426bf4103b9ee6df7a1df5f68fad5d1d0d3050a7..5f51b7bfc064048b14f1f30eafd287522c25e978 100644 (file)
 
 /include/ "pq3-etsec2-0.dtsi"
        enet0: enet0_grp2: ethernet@b0000 {
+               fsl,wake-on-filer;
        };
 
 /include/ "pq3-etsec2-1.dtsi"
        enet1: enet1_grp2: ethernet@b1000 {
+               fsl,wake-on-filer;
        };
 
        global-utilities@e0000 {
index 59503ed98e5fcd5bdb783b23a074eadf4ac3fafa..3f1472a78f393434e2b77df6ab2e24587566befe 100644 (file)
@@ -303,7 +303,7 @@ int dma_set_coherent_mask(struct device *dev, u64 mask)
        dev->coherent_dma_mask = mask;
        return 0;
 }
-EXPORT_SYMBOL_GPL(dma_set_coherent_mask);
+EXPORT_SYMBOL(dma_set_coherent_mask);
 
 #define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16)
 
index 17cea18a09d32f103aa453c645a7324739c58ffe..04782164ee67d8a570bcb4b4ee166dc6809753de 100644 (file)
@@ -679,7 +679,7 @@ void bpf_jit_compile(struct bpf_prog *fp)
                ((u64 *)image)[1] = local_paca->kernel_toc;
 #endif
                fp->bpf_func = (void *)image;
-               fp->jited = true;
+               fp->jited = 1;
        }
 out:
        kfree(addrs);
index b0382f3f1095589a599c92d1a6eb25904a988304..d1e65ce545b31f427629853a3803a6b0b4af0d9b 100644 (file)
@@ -48,7 +48,7 @@ struct cpu_hw_events {
        unsigned long amasks[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES];
        unsigned long avalues[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES];
 
-       unsigned int group_flag;
+       unsigned int txn_flags;
        int n_txn_start;
 
        /* BHRB bits */
@@ -1441,7 +1441,7 @@ static int power_pmu_add(struct perf_event *event, int ef_flags)
         * skip the schedulability test here, it will be performed
         * at commit time(->commit_txn) as a whole
         */
-       if (cpuhw->group_flag & PERF_EVENT_TXN)
+       if (cpuhw->txn_flags & PERF_PMU_TXN_ADD)
                goto nocheck;
 
        if (check_excludes(cpuhw->event, cpuhw->flags, n0, 1))
@@ -1586,13 +1586,22 @@ static void power_pmu_stop(struct perf_event *event, int ef_flags)
  * Start group events scheduling transaction
  * Set the flag to make pmu::enable() not perform the
  * schedulability test, it will be performed at commit time
+ *
+ * We only support PERF_PMU_TXN_ADD transactions. Save the
+ * transaction flags but otherwise ignore non-PERF_PMU_TXN_ADD
+ * transactions.
  */
-static void power_pmu_start_txn(struct pmu *pmu)
+static void power_pmu_start_txn(struct pmu *pmu, unsigned int txn_flags)
 {
        struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
 
+       WARN_ON_ONCE(cpuhw->txn_flags);         /* txn already in flight */
+
+       cpuhw->txn_flags = txn_flags;
+       if (txn_flags & ~PERF_PMU_TXN_ADD)
+               return;
+
        perf_pmu_disable(pmu);
-       cpuhw->group_flag |= PERF_EVENT_TXN;
        cpuhw->n_txn_start = cpuhw->n_events;
 }
 
@@ -1604,8 +1613,15 @@ static void power_pmu_start_txn(struct pmu *pmu)
 static void power_pmu_cancel_txn(struct pmu *pmu)
 {
        struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
+       unsigned int txn_flags;
+
+       WARN_ON_ONCE(!cpuhw->txn_flags);        /* no txn in flight */
+
+       txn_flags = cpuhw->txn_flags;
+       cpuhw->txn_flags = 0;
+       if (txn_flags & ~PERF_PMU_TXN_ADD)
+               return;
 
-       cpuhw->group_flag &= ~PERF_EVENT_TXN;
        perf_pmu_enable(pmu);
 }
 
@@ -1621,7 +1637,15 @@ static int power_pmu_commit_txn(struct pmu *pmu)
 
        if (!ppmu)
                return -EAGAIN;
+
        cpuhw = this_cpu_ptr(&cpu_hw_events);
+       WARN_ON_ONCE(!cpuhw->txn_flags);        /* no txn in flight */
+
+       if (cpuhw->txn_flags & ~PERF_PMU_TXN_ADD) {
+               cpuhw->txn_flags = 0;
+               return 0;
+       }
+
        n = cpuhw->n_events;
        if (check_excludes(cpuhw->event, cpuhw->flags, 0, n))
                return -EAGAIN;
@@ -1632,7 +1656,7 @@ static int power_pmu_commit_txn(struct pmu *pmu)
        for (i = cpuhw->n_txn_start; i < n; ++i)
                cpuhw->event[i]->hw.config = cpuhw->events[i];
 
-       cpuhw->group_flag &= ~PERF_EVENT_TXN;
+       cpuhw->txn_flags = 0;
        perf_pmu_enable(pmu);
        return 0;
 }
index 527c8b98e97ee5cf8d8564dc166e05e92ac6ee85..9f9dfda9ed2c13a927f6c863759a2e780a5e8941 100644 (file)
@@ -142,6 +142,15 @@ static struct attribute_group event_long_desc_group = {
 
 static struct kmem_cache *hv_page_cache;
 
+DEFINE_PER_CPU(int, hv_24x7_txn_flags);
+DEFINE_PER_CPU(int, hv_24x7_txn_err);
+
+struct hv_24x7_hw {
+       struct perf_event *events[255];
+};
+
+DEFINE_PER_CPU(struct hv_24x7_hw, hv_24x7_hw);
+
 /*
  * request_buffer and result_buffer are not required to be 4k aligned,
  * but are not allowed to cross any 4k boundary. Aligning them to 4k is
@@ -1231,9 +1240,48 @@ static void update_event_count(struct perf_event *event, u64 now)
 static void h_24x7_event_read(struct perf_event *event)
 {
        u64 now;
+       struct hv_24x7_request_buffer *request_buffer;
+       struct hv_24x7_hw *h24x7hw;
+       int txn_flags;
+
+       txn_flags = __this_cpu_read(hv_24x7_txn_flags);
+
+       /*
+        * If in a READ transaction, add this counter to the list of
+        * counters to read during the next HCALL (i.e commit_txn()).
+        * If not in a READ transaction, go ahead and make the HCALL
+        * to read this counter by itself.
+        */
+
+       if (txn_flags & PERF_PMU_TXN_READ) {
+               int i;
+               int ret;
 
-       now = h_24x7_get_value(event);
-       update_event_count(event, now);
+               if (__this_cpu_read(hv_24x7_txn_err))
+                       return;
+
+               request_buffer = (void *)get_cpu_var(hv_24x7_reqb);
+
+               ret = add_event_to_24x7_request(event, request_buffer);
+               if (ret) {
+                       __this_cpu_write(hv_24x7_txn_err, ret);
+               } else {
+                       /*
+                        * Assoicate the event with the HCALL request index,
+                        * so ->commit_txn() can quickly find/update count.
+                        */
+                       i = request_buffer->num_requests - 1;
+
+                       h24x7hw = &get_cpu_var(hv_24x7_hw);
+                       h24x7hw->events[i] = event;
+                       put_cpu_var(h24x7hw);
+               }
+
+               put_cpu_var(hv_24x7_reqb);
+       } else {
+               now = h_24x7_get_value(event);
+               update_event_count(event, now);
+       }
 }
 
 static void h_24x7_event_start(struct perf_event *event, int flags)
@@ -1255,6 +1303,117 @@ static int h_24x7_event_add(struct perf_event *event, int flags)
        return 0;
 }
 
+/*
+ * 24x7 counters only support READ transactions. They are
+ * always counting and dont need/support ADD transactions.
+ * Cache the flags, but otherwise ignore transactions that
+ * are not PERF_PMU_TXN_READ.
+ */
+static void h_24x7_event_start_txn(struct pmu *pmu, unsigned int flags)
+{
+       struct hv_24x7_request_buffer *request_buffer;
+       struct hv_24x7_data_result_buffer *result_buffer;
+
+       /* We should not be called if we are already in a txn */
+       WARN_ON_ONCE(__this_cpu_read(hv_24x7_txn_flags));
+
+       __this_cpu_write(hv_24x7_txn_flags, flags);
+       if (flags & ~PERF_PMU_TXN_READ)
+               return;
+
+       request_buffer = (void *)get_cpu_var(hv_24x7_reqb);
+       result_buffer = (void *)get_cpu_var(hv_24x7_resb);
+
+       init_24x7_request(request_buffer, result_buffer);
+
+       put_cpu_var(hv_24x7_resb);
+       put_cpu_var(hv_24x7_reqb);
+}
+
+/*
+ * Clean up transaction state.
+ *
+ * NOTE: Ignore state of request and result buffers for now.
+ *      We will initialize them during the next read/txn.
+ */
+static void reset_txn(void)
+{
+       __this_cpu_write(hv_24x7_txn_flags, 0);
+       __this_cpu_write(hv_24x7_txn_err, 0);
+}
+
+/*
+ * 24x7 counters only support READ transactions. They are always counting
+ * and dont need/support ADD transactions. Clear ->txn_flags but otherwise
+ * ignore transactions that are not of type PERF_PMU_TXN_READ.
+ *
+ * For READ transactions, submit all pending 24x7 requests (i.e requests
+ * that were queued by h_24x7_event_read()), to the hypervisor and update
+ * the event counts.
+ */
+static int h_24x7_event_commit_txn(struct pmu *pmu)
+{
+       struct hv_24x7_request_buffer *request_buffer;
+       struct hv_24x7_data_result_buffer *result_buffer;
+       struct hv_24x7_result *resb;
+       struct perf_event *event;
+       u64 count;
+       int i, ret, txn_flags;
+       struct hv_24x7_hw *h24x7hw;
+
+       txn_flags = __this_cpu_read(hv_24x7_txn_flags);
+       WARN_ON_ONCE(!txn_flags);
+
+       ret = 0;
+       if (txn_flags & ~PERF_PMU_TXN_READ)
+               goto out;
+
+       ret = __this_cpu_read(hv_24x7_txn_err);
+       if (ret)
+               goto out;
+
+       request_buffer = (void *)get_cpu_var(hv_24x7_reqb);
+       result_buffer = (void *)get_cpu_var(hv_24x7_resb);
+
+       ret = make_24x7_request(request_buffer, result_buffer);
+       if (ret) {
+               log_24x7_hcall(request_buffer, result_buffer, ret);
+               goto put_reqb;
+       }
+
+       h24x7hw = &get_cpu_var(hv_24x7_hw);
+
+       /* Update event counts from hcall */
+       for (i = 0; i < request_buffer->num_requests; i++) {
+               resb = &result_buffer->results[i];
+               count = be64_to_cpu(resb->elements[0].element_data[0]);
+               event = h24x7hw->events[i];
+               h24x7hw->events[i] = NULL;
+               update_event_count(event, count);
+       }
+
+       put_cpu_var(hv_24x7_hw);
+
+put_reqb:
+       put_cpu_var(hv_24x7_resb);
+       put_cpu_var(hv_24x7_reqb);
+out:
+       reset_txn();
+       return ret;
+}
+
+/*
+ * 24x7 counters only support READ transactions. They are always counting
+ * and dont need/support ADD transactions. However, regardless of type
+ * of transaction, all we need to do is cleanup, so we don't have to check
+ * the type of transaction.
+ */
+static void h_24x7_event_cancel_txn(struct pmu *pmu)
+{
+       WARN_ON_ONCE(!__this_cpu_read(hv_24x7_txn_flags));
+       reset_txn();
+}
+
 static struct pmu h_24x7_pmu = {
        .task_ctx_nr = perf_invalid_context,
 
@@ -1266,6 +1425,9 @@ static struct pmu h_24x7_pmu = {
        .start       = h_24x7_event_start,
        .stop        = h_24x7_event_stop,
        .read        = h_24x7_event_read,
+       .start_txn   = h_24x7_event_start_txn,
+       .commit_txn  = h_24x7_event_commit_txn,
+       .cancel_txn  = h_24x7_event_cancel_txn,
 };
 
 static int hv_24x7_init(void)
index 396351db601b42906c83a8dbf592e40b47db376a..7d5e295255b7b25934d51792fb6876c16462d420 100644 (file)
@@ -676,6 +676,9 @@ static u64 power8_bhrb_filter_map(u64 branch_sample_type)
        if (branch_sample_type & PERF_SAMPLE_BRANCH_IND_CALL)
                return -1;
 
+       if (branch_sample_type & PERF_SAMPLE_BRANCH_CALL)
+               return -1;
+
        if (branch_sample_type & PERF_SAMPLE_BRANCH_ANY_CALL) {
                pmu_bhrb_filter |= POWER8_MMCRA_IFM1;
                return pmu_bhrb_filter;
index e0e68a1c0d3c20dd5d618e62cdccba2e429af475..aed7714495c10fd7fa319675f12f4c7baacdd7ed 100644 (file)
@@ -327,7 +327,7 @@ static void axon_msi_shutdown(struct platform_device *device)
        u32 tmp;
 
        pr_devel("axon_msi: disabling %s\n",
-                 msic->irq_domain->of_node->full_name);
+                irq_domain_get_of_node(msic->irq_domain)->full_name);
        tmp  = dcr_read(msic->dcr_host, MSIC_CTRL_REG);
        tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
        msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
index 9d27de62dc62755c4800d46bcc8b4409a81a5115..54ee5743cb724c9b2a28e77607d095da38065d05 100644 (file)
@@ -231,20 +231,23 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic)
        const u32 *imap, *tmp;
        int imaplen, intsize, unit;
        struct device_node *iic;
+       struct device_node *of_node;
+
+       of_node = irq_domain_get_of_node(pic->host);
 
        /* First, we check whether we have a real "interrupts" in the device
         * tree in case the device-tree is ever fixed
         */
-       virq = irq_of_parse_and_map(pic->host->of_node, 0);
+       virq = irq_of_parse_and_map(of_node, 0);
        if (virq)
                return virq;
 
        /* Now do the horrible hacks */
-       tmp = of_get_property(pic->host->of_node, "#interrupt-cells", NULL);
+       tmp = of_get_property(of_node, "#interrupt-cells", NULL);
        if (tmp == NULL)
                return NO_IRQ;
        intsize = *tmp;
-       imap = of_get_property(pic->host->of_node, "interrupt-map", &imaplen);
+       imap = of_get_property(of_node, "interrupt-map", &imaplen);
        if (imap == NULL || imaplen < (intsize + 1))
                return NO_IRQ;
        iic = of_find_node_by_phandle(imap[intsize]);
index b304a9fe55cc410e2bcbd9693a84bce1d2bf5365..d9af76342d9980791a6dce16db4c2ce394d9c386 100644 (file)
@@ -144,9 +144,11 @@ int mpic_pasemi_msi_init(struct mpic *mpic)
 {
        int rc;
        struct pci_controller *phb;
+       struct device_node *of_node;
 
-       if (!mpic->irqhost->of_node ||
-           !of_device_is_compatible(mpic->irqhost->of_node,
+       of_node = irq_domain_get_of_node(mpic->irqhost);
+       if (!of_node ||
+           !of_device_is_compatible(of_node,
                                     "pasemi,pwrficient-openpic"))
                return -ENODEV;
 
index 2c91ee7800b90e09edb93a35aa162da74071ba8c..6ccfb6c1c707b40e9fd6ba4cb202d70c1faf57cb 100644 (file)
@@ -137,7 +137,7 @@ static void opal_handle_irq_work(struct irq_work *work)
 static int opal_event_match(struct irq_domain *h, struct device_node *node,
                            enum irq_domain_bus_token bus_token)
 {
-       return h->of_node == node;
+       return irq_domain_get_of_node(h) == node;
 }
 
 static int opal_event_xlate(struct irq_domain *h, struct device_node *np,
index eca0b00794fa567edb3edc2d12650328b4dec486..bffcc7a486a11c9821ded7f18178c336aa58b9b7 100644 (file)
@@ -181,7 +181,8 @@ static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node,
                              enum irq_domain_bus_token bus_token)
 {
        /* Exact match, unless ehv_pic node is NULL */
-       return h->of_node == NULL || h->of_node == node;
+       struct device_node *of_node = irq_domain_get_of_node(h);
+       return of_node == NULL || of_node == node;
 }
 
 static int ehv_pic_host_map(struct irq_domain *h, unsigned int virq,
index 48a576aa47b92455e97a3f437b5cc18b6007f91a..3a2be3676f4353eb6b4e3b237334cd0b3903d37f 100644 (file)
@@ -110,7 +110,7 @@ static int fsl_msi_init_allocator(struct fsl_msi *msi_data)
        int rc, hwirq;
 
        rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS_MAX,
-                             msi_data->irqhost->of_node);
+                             irq_domain_get_of_node(msi_data->irqhost));
        if (rc)
                return rc;
 
index e1a9c2c2d5d357c7e88083d60a6f89c0d04219c2..6f99ed3967fdef3ad382a03758d361e1ee3223db 100644 (file)
@@ -165,7 +165,8 @@ static struct resource pic_edgectrl_iores = {
 static int i8259_host_match(struct irq_domain *h, struct device_node *node,
                            enum irq_domain_bus_token bus_token)
 {
-       return h->of_node == NULL || h->of_node == node;
+       struct device_node *of_node = irq_domain_get_of_node(h);
+       return of_node == NULL || of_node == node;
 }
 
 static int i8259_host_map(struct irq_domain *h, unsigned int virq,
index b1297ab1599b46ef24457ec890b28b69ba4dc0bf..f76ee39cb3377a0a6788ad4fcd31fe3c4614743f 100644 (file)
@@ -675,7 +675,8 @@ static int ipic_host_match(struct irq_domain *h, struct device_node *node,
                           enum irq_domain_bus_token bus_token)
 {
        /* Exact match, unless ipic node is NULL */
-       return h->of_node == NULL || h->of_node == node;
+       struct device_node *of_node = irq_domain_get_of_node(h);
+       return of_node == NULL || of_node == node;
 }
 
 static int ipic_host_map(struct irq_domain *h, unsigned int virq,
index 537e5db85a060518928ee812cdab8dd00a311b06..cecd1156c1852a71b6f0432c77fd1410eda767d1 100644 (file)
@@ -1011,7 +1011,8 @@ static int mpic_host_match(struct irq_domain *h, struct device_node *node,
                           enum irq_domain_bus_token bus_token)
 {
        /* Exact match, unless mpic node is NULL */
-       return h->of_node == NULL || h->of_node == node;
+       struct device_node *of_node = irq_domain_get_of_node(h);
+       return of_node == NULL || of_node == node;
 }
 
 static int mpic_host_map(struct irq_domain *h, unsigned int virq,
index 7dc39f35a4ccdb0d9f213515ccb5b850de3e1b4e..1d48a5385905a8ce96290f10064546a39efed403 100644 (file)
@@ -84,7 +84,7 @@ int mpic_msi_init_allocator(struct mpic *mpic)
        int rc;
 
        rc = msi_bitmap_alloc(&mpic->msi_bitmap, mpic->num_sources,
-                             mpic->irqhost->of_node);
+                             irq_domain_get_of_node(mpic->irqhost));
        if (rc)
                return rc;
 
index fbcc1f855a7f2ea15937d02f0b0cb4305af5034a..ef36f16f9f6fbc9bdfd0c02e6df29e77fc83817c 100644 (file)
@@ -248,7 +248,8 @@ static int qe_ic_host_match(struct irq_domain *h, struct device_node *node,
                            enum irq_domain_bus_token bus_token)
 {
        /* Exact match, unless qe_ic node is NULL */
-       return h->of_node == NULL || h->of_node == node;
+       struct device_node *of_node = irq_domain_get_of_node(h);
+       return of_node == NULL || of_node == node;
 }
 
 static int qe_ic_host_map(struct irq_domain *h, unsigned int virq,
index f4e9dc71675f7e5d572c2e6d97312b097e5d89a2..10f2007900790919f41ac6f3d3bd425c9decebb6 100644 (file)
@@ -19,7 +19,7 @@
 #include <crypto/sha.h>
 
 /* must be big enough for the largest SHA variant */
-#define SHA_MAX_STATE_SIZE     16
+#define SHA_MAX_STATE_SIZE     (SHA512_DIGEST_SIZE / 4)
 #define SHA_MAX_BLOCK_SIZE      SHA512_BLOCK_SIZE
 
 struct s390_sha_ctx {
index a9563409c36ea7bc37bfb405411b9c6ac70c2a50..929c147e07b40c19370e621a4ba96e99c13a994e 100644 (file)
@@ -72,6 +72,7 @@ struct cpu_hw_events {
        atomic_t                ctr_set[CPUMF_CTR_SET_MAX];
        u64                     state, tx_state;
        unsigned int            flags;
+       unsigned int            txn_flags;
 };
 static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
        .ctr_set = {
@@ -82,6 +83,7 @@ static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
        },
        .state = 0,
        .flags = 0,
+       .txn_flags = 0,
 };
 
 static int get_counter_set(u64 event)
@@ -538,7 +540,7 @@ static int cpumf_pmu_add(struct perf_event *event, int flags)
         * For group events transaction, the authorization check is
         * done in cpumf_pmu_commit_txn().
         */
-       if (!(cpuhw->flags & PERF_EVENT_TXN))
+       if (!(cpuhw->txn_flags & PERF_PMU_TXN_ADD))
                if (validate_ctr_auth(&event->hw))
                        return -ENOENT;
 
@@ -576,13 +578,22 @@ static void cpumf_pmu_del(struct perf_event *event, int flags)
 /*
  * Start group events scheduling transaction.
  * Set flags to perform a single test at commit time.
+ *
+ * We only support PERF_PMU_TXN_ADD transactions. Save the
+ * transaction flags but otherwise ignore non-PERF_PMU_TXN_ADD
+ * transactions.
  */
-static void cpumf_pmu_start_txn(struct pmu *pmu)
+static void cpumf_pmu_start_txn(struct pmu *pmu, unsigned int txn_flags)
 {
        struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
 
+       WARN_ON_ONCE(cpuhw->txn_flags);         /* txn already in flight */
+
+       cpuhw->txn_flags = txn_flags;
+       if (txn_flags & ~PERF_PMU_TXN_ADD)
+               return;
+
        perf_pmu_disable(pmu);
-       cpuhw->flags |= PERF_EVENT_TXN;
        cpuhw->tx_state = cpuhw->state;
 }
 
@@ -593,11 +604,18 @@ static void cpumf_pmu_start_txn(struct pmu *pmu)
  */
 static void cpumf_pmu_cancel_txn(struct pmu *pmu)
 {
+       unsigned int txn_flags;
        struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
 
+       WARN_ON_ONCE(!cpuhw->txn_flags);        /* no txn in flight */
+
+       txn_flags = cpuhw->txn_flags;
+       cpuhw->txn_flags = 0;
+       if (txn_flags & ~PERF_PMU_TXN_ADD)
+               return;
+
        WARN_ON(cpuhw->tx_state != cpuhw->state);
 
-       cpuhw->flags &= ~PERF_EVENT_TXN;
        perf_pmu_enable(pmu);
 }
 
@@ -611,13 +629,20 @@ static int cpumf_pmu_commit_txn(struct pmu *pmu)
        struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
        u64 state;
 
+       WARN_ON_ONCE(!cpuhw->txn_flags);        /* no txn in flight */
+
+       if (cpuhw->txn_flags & ~PERF_PMU_TXN_ADD) {
+               cpuhw->txn_flags = 0;
+               return 0;
+       }
+
        /* check if the updated state can be scheduled */
        state = cpuhw->state & ~((1 << CPUMF_LCCTL_ENABLE_SHIFT) - 1);
        state >>= CPUMF_LCCTL_ENABLE_SHIFT;
        if ((state & cpuhw->info.auth_ctl) != state)
                return -ENOENT;
 
-       cpuhw->flags &= ~PERF_EVENT_TXN;
+       cpuhw->txn_flags = 0;
        perf_pmu_enable(pmu);
        return 0;
 }
index eeda051442c3d0de0bec154b9aa3f15fe85e6de8..9a0c4c22e53670b1d813f3ddfb328b76c8e06c78 100644 (file)
@@ -1310,7 +1310,7 @@ void bpf_int_jit_compile(struct bpf_prog *fp)
        if (jit.prg_buf) {
                set_memory_ro((unsigned long)header, header->pages);
                fp->bpf_func = (void *) jit.prg_buf;
-               fp->jited = true;
+               fp->jited = 1;
        }
 free_addrs:
        kfree(jit.addrs);
index 05b9f74ce2d544d3f9d7bede26cdc57c04a54e2c..c399e1c55685178d149249ec4e3c8c0178e01829 100644 (file)
@@ -14,8 +14,8 @@
 
 #define ATOMIC_INIT(i) { (i) }
 
-#define atomic_read(v)         ACCESS_ONCE((v)->counter)
-#define atomic_set(v,i)                ((v)->counter = (i))
+#define atomic_read(v)         READ_ONCE((v)->counter)
+#define atomic_set(v,i)                WRITE_ONCE((v)->counter, (i))
 
 #if defined(CONFIG_GUSA_RB)
 #include <asm/atomic-grb.h>
index 917084ace49dee70975f195a6c53d1d5c24af4ad..f2fbf9e16fafca66c4aa01145645f1748775cde2 100644 (file)
 #define ATOMIC_INIT(i)         { (i) }
 #define ATOMIC64_INIT(i)       { (i) }
 
-#define atomic_read(v)         ACCESS_ONCE((v)->counter)
-#define atomic64_read(v)       ACCESS_ONCE((v)->counter)
+#define atomic_read(v)         READ_ONCE((v)->counter)
+#define atomic64_read(v)       READ_ONCE((v)->counter)
 
-#define atomic_set(v, i)       (((v)->counter) = i)
-#define atomic64_set(v, i)     (((v)->counter) = i)
+#define atomic_set(v, i)       WRITE_ONCE(((v)->counter), (i))
+#define atomic64_set(v, i)     WRITE_ONCE(((v)->counter), (i))
 
 #define ATOMIC_OP(op)                                                  \
 void atomic_##op(int, atomic_t *);                                     \
index 01d17046225a8aa084b2a8f9d70a235bcab681f8..bec481aaca1635999f8889a52c4dde68bc109014 100644 (file)
@@ -31,6 +31,9 @@ static inline int pcibus_to_node(struct pci_bus *pbus)
         cpu_all_mask : \
         cpumask_of_node(pcibus_to_node(bus)))
 
+int __node_distance(int, int);
+#define node_distance(a, b) __node_distance(a, b)
+
 #else /* CONFIG_NUMA */
 
 #include <asm-generic/topology.h>
index aace6f31371638384bfc009e80f10d8719376dbf..7ad7203deaec0388629eccbeb965e11b046ae6af 100644 (file)
                                      * Most-Recently-Used, primary,
                                      * implicit
                                      */
-#define ASI_ST_BLKINIT_MRU_S   0xf2 /* (NG4) init-store, twin load,
+#define ASI_ST_BLKINIT_MRU_S   0xf3 /* (NG4) init-store, twin load,
                                      * Most-Recently-Used, secondary,
                                      * implicit
                                      */
index 5320689c06e97a5731825b5c2b45d955ef340af3..37686828c3d9818a9decfe1b6a396e20161e3d43 100644 (file)
@@ -161,7 +161,7 @@ static inline iopte_t *alloc_npages(struct device *dev,
 
        entry = iommu_tbl_range_alloc(dev, &iommu->tbl, npages, NULL,
                                      (unsigned long)(-1), 0);
-       if (unlikely(entry == DMA_ERROR_CODE))
+       if (unlikely(entry == IOMMU_ERROR_CODE))
                return NULL;
 
        return iommu->page_table + entry;
@@ -253,7 +253,7 @@ static void dma_4u_free_coherent(struct device *dev, size_t size,
        npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT;
        iommu = dev->archdata.iommu;
 
-       iommu_tbl_range_free(&iommu->tbl, dvma, npages, DMA_ERROR_CODE);
+       iommu_tbl_range_free(&iommu->tbl, dvma, npages, IOMMU_ERROR_CODE);
 
        order = get_order(size);
        if (order < 10)
@@ -426,7 +426,7 @@ static void dma_4u_unmap_page(struct device *dev, dma_addr_t bus_addr,
        iommu_free_ctx(iommu, ctx);
        spin_unlock_irqrestore(&iommu->lock, flags);
 
-       iommu_tbl_range_free(&iommu->tbl, bus_addr, npages, DMA_ERROR_CODE);
+       iommu_tbl_range_free(&iommu->tbl, bus_addr, npages, IOMMU_ERROR_CODE);
 }
 
 static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist,
@@ -492,7 +492,7 @@ static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist,
                                              &handle, (unsigned long)(-1), 0);
 
                /* Handle failure */
-               if (unlikely(entry == DMA_ERROR_CODE)) {
+               if (unlikely(entry == IOMMU_ERROR_CODE)) {
                        if (printk_ratelimit())
                                printk(KERN_INFO "iommu_alloc failed, iommu %p paddr %lx"
                                       " npages %lx\n", iommu, paddr, npages);
@@ -571,7 +571,7 @@ iommu_map_failed:
                                iopte_make_dummy(iommu, base + j);
 
                        iommu_tbl_range_free(&iommu->tbl, vaddr, npages,
-                                            DMA_ERROR_CODE);
+                                            IOMMU_ERROR_CODE);
 
                        s->dma_address = DMA_ERROR_CODE;
                        s->dma_length = 0;
@@ -648,7 +648,7 @@ static void dma_4u_unmap_sg(struct device *dev, struct scatterlist *sglist,
                        iopte_make_dummy(iommu, base + i);
 
                iommu_tbl_range_free(&iommu->tbl, dma_handle, npages,
-                                    DMA_ERROR_CODE);
+                                    IOMMU_ERROR_CODE);
                sg = sg_next(sg);
        }
 
index 1ae5eb1bb045130f05c8edc4573dbcd75e00475f..59d5038664317d962a73ea2ee9d6133bb5d44904 100644 (file)
@@ -1953,7 +1953,7 @@ static struct ldc_mtable_entry *alloc_npages(struct ldc_iommu *iommu,
 
        entry = iommu_tbl_range_alloc(NULL, &iommu->iommu_map_table,
                                      npages, NULL, (unsigned long)-1, 0);
-       if (unlikely(entry < 0))
+       if (unlikely(entry == IOMMU_ERROR_CODE))
                return NULL;
 
        return iommu->page_table + entry;
index d2fe57dad433ea9409c52a3aaf8ae9efa2113032..836e8cef47e2aa4e57540169797cb2cb0b64af3f 100644 (file)
@@ -159,7 +159,7 @@ static void *dma_4v_alloc_coherent(struct device *dev, size_t size,
        entry = iommu_tbl_range_alloc(dev, &iommu->tbl, npages, NULL,
                                      (unsigned long)(-1), 0);
 
-       if (unlikely(entry == DMA_ERROR_CODE))
+       if (unlikely(entry == IOMMU_ERROR_CODE))
                goto range_alloc_fail;
 
        *dma_addrp = (iommu->tbl.table_map_base + (entry << IO_PAGE_SHIFT));
@@ -187,7 +187,7 @@ static void *dma_4v_alloc_coherent(struct device *dev, size_t size,
        return ret;
 
 iommu_map_fail:
-       iommu_tbl_range_free(&iommu->tbl, *dma_addrp, npages, DMA_ERROR_CODE);
+       iommu_tbl_range_free(&iommu->tbl, *dma_addrp, npages, IOMMU_ERROR_CODE);
 
 range_alloc_fail:
        free_pages(first_page, order);
@@ -226,7 +226,7 @@ static void dma_4v_free_coherent(struct device *dev, size_t size, void *cpu,
        devhandle = pbm->devhandle;
        entry = ((dvma - iommu->tbl.table_map_base) >> IO_PAGE_SHIFT);
        dma_4v_iommu_demap(&devhandle, entry, npages);
-       iommu_tbl_range_free(&iommu->tbl, dvma, npages, DMA_ERROR_CODE);
+       iommu_tbl_range_free(&iommu->tbl, dvma, npages, IOMMU_ERROR_CODE);
        order = get_order(size);
        if (order < 10)
                free_pages((unsigned long)cpu, order);
@@ -256,7 +256,7 @@ static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page,
        entry = iommu_tbl_range_alloc(dev, &iommu->tbl, npages, NULL,
                                      (unsigned long)(-1), 0);
 
-       if (unlikely(entry == DMA_ERROR_CODE))
+       if (unlikely(entry == IOMMU_ERROR_CODE))
                goto bad;
 
        bus_addr = (iommu->tbl.table_map_base + (entry << IO_PAGE_SHIFT));
@@ -288,7 +288,7 @@ bad:
        return DMA_ERROR_CODE;
 
 iommu_map_fail:
-       iommu_tbl_range_free(&iommu->tbl, bus_addr, npages, DMA_ERROR_CODE);
+       iommu_tbl_range_free(&iommu->tbl, bus_addr, npages, IOMMU_ERROR_CODE);
        return DMA_ERROR_CODE;
 }
 
@@ -317,7 +317,7 @@ static void dma_4v_unmap_page(struct device *dev, dma_addr_t bus_addr,
        bus_addr &= IO_PAGE_MASK;
        entry = (bus_addr - iommu->tbl.table_map_base) >> IO_PAGE_SHIFT;
        dma_4v_iommu_demap(&devhandle, entry, npages);
-       iommu_tbl_range_free(&iommu->tbl, bus_addr, npages, DMA_ERROR_CODE);
+       iommu_tbl_range_free(&iommu->tbl, bus_addr, npages, IOMMU_ERROR_CODE);
 }
 
 static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
@@ -376,7 +376,7 @@ static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
                                              &handle, (unsigned long)(-1), 0);
 
                /* Handle failure */
-               if (unlikely(entry == DMA_ERROR_CODE)) {
+               if (unlikely(entry == IOMMU_ERROR_CODE)) {
                        if (printk_ratelimit())
                                printk(KERN_INFO "iommu_alloc failed, iommu %p paddr %lx"
                                       " npages %lx\n", iommu, paddr, npages);
@@ -451,7 +451,7 @@ iommu_map_failed:
                        npages = iommu_num_pages(s->dma_address, s->dma_length,
                                                 IO_PAGE_SIZE);
                        iommu_tbl_range_free(&iommu->tbl, vaddr, npages,
-                                            DMA_ERROR_CODE);
+                                            IOMMU_ERROR_CODE);
                        /* XXX demap? XXX */
                        s->dma_address = DMA_ERROR_CODE;
                        s->dma_length = 0;
@@ -496,7 +496,7 @@ static void dma_4v_unmap_sg(struct device *dev, struct scatterlist *sglist,
                entry = ((dma_handle - tbl->table_map_base) >> shift);
                dma_4v_iommu_demap(&devhandle, entry, npages);
                iommu_tbl_range_free(&iommu->tbl, dma_handle, npages,
-                                    DMA_ERROR_CODE);
+                                    IOMMU_ERROR_CODE);
                sg = sg_next(sg);
        }
 
index 689db65f85294a95051d08e4f7846151ccc002de..b0da5aedb336c643baf9163c010c6c679ce6bbdc 100644 (file)
@@ -108,7 +108,7 @@ struct cpu_hw_events {
        /* Enabled/disable state.  */
        int                     enabled;
 
-       unsigned int            group_flag;
+       unsigned int            txn_flags;
 };
 static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { .enabled = 1, };
 
@@ -1379,7 +1379,7 @@ static int sparc_pmu_add(struct perf_event *event, int ef_flags)
         * skip the schedulability test here, it will be performed
         * at commit time(->commit_txn) as a whole
         */
-       if (cpuc->group_flag & PERF_EVENT_TXN)
+       if (cpuc->txn_flags & PERF_PMU_TXN_ADD)
                goto nocheck;
 
        if (check_excludes(cpuc->event, n0, 1))
@@ -1494,12 +1494,17 @@ static int sparc_pmu_event_init(struct perf_event *event)
  * Set the flag to make pmu::enable() not perform the
  * schedulability test, it will be performed at commit time
  */
-static void sparc_pmu_start_txn(struct pmu *pmu)
+static void sparc_pmu_start_txn(struct pmu *pmu, unsigned int txn_flags)
 {
        struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
 
+       WARN_ON_ONCE(cpuhw->txn_flags);         /* txn already in flight */
+
+       cpuhw->txn_flags = txn_flags;
+       if (txn_flags & ~PERF_PMU_TXN_ADD)
+               return;
+
        perf_pmu_disable(pmu);
-       cpuhw->group_flag |= PERF_EVENT_TXN;
 }
 
 /*
@@ -1510,8 +1515,15 @@ static void sparc_pmu_start_txn(struct pmu *pmu)
 static void sparc_pmu_cancel_txn(struct pmu *pmu)
 {
        struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
+       unsigned int txn_flags;
+
+       WARN_ON_ONCE(!cpuhw->txn_flags);        /* no txn in flight */
+
+       txn_flags = cpuhw->txn_flags;
+       cpuhw->txn_flags = 0;
+       if (txn_flags & ~PERF_PMU_TXN_ADD)
+               return;
 
-       cpuhw->group_flag &= ~PERF_EVENT_TXN;
        perf_pmu_enable(pmu);
 }
 
@@ -1528,14 +1540,20 @@ static int sparc_pmu_commit_txn(struct pmu *pmu)
        if (!sparc_pmu)
                return -EINVAL;
 
-       cpuc = this_cpu_ptr(&cpu_hw_events);
+       WARN_ON_ONCE(!cpuc->txn_flags); /* no txn in flight */
+
+       if (cpuc->txn_flags & ~PERF_PMU_TXN_ADD) {
+               cpuc->txn_flags = 0;
+               return 0;
+       }
+
        n = cpuc->n_events;
        if (check_excludes(cpuc->event, 0, n))
                return -EINVAL;
        if (sparc_check_constraints(cpuc->event, cpuc->events, n))
                return -EAGAIN;
 
-       cpuc->group_flag &= ~PERF_EVENT_TXN;
+       cpuc->txn_flags = 0;
        perf_pmu_enable(pmu);
        return 0;
 }
index 62098a89bbbf5b87fe1fc640e7699d380efb5028..d89e97b374cf4dab95cc72ee073263c472e8eaff 100644 (file)
@@ -436,24 +436,26 @@ extern void sun4v_data_access_exception(struct pt_regs *regs,
 int handle_ldf_stq(u32 insn, struct pt_regs *regs)
 {
        unsigned long addr = compute_effective_address(regs, insn, 0);
-       int freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
+       int freg;
        struct fpustate *f = FPUSTATE;
        int asi = decode_asi(insn, regs);
-       int flag = (freg < 32) ? FPRS_DL : FPRS_DU;
+       int flag;
 
        perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
 
        save_and_clear_fpu();
        current_thread_info()->xfsr[0] &= ~0x1c000;
-       if (freg & 3) {
-               current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
-               do_fpother(regs);
-               return 0;
-       }
        if (insn & 0x200000) {
                /* STQ */
                u64 first = 0, second = 0;
                
+               freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
+               flag = (freg < 32) ? FPRS_DL : FPRS_DU;
+               if (freg & 3) {
+                       current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
+                       do_fpother(regs);
+                       return 0;
+               }
                if (current_thread_info()->fpsaved[0] & flag) {
                        first = *(u64 *)&f->regs[freg];
                        second = *(u64 *)&f->regs[freg+2];
@@ -513,6 +515,12 @@ int handle_ldf_stq(u32 insn, struct pt_regs *regs)
                case 0x100000: size = 4; break;
                default: size = 2; break;
                }
+               if (size == 1)
+                       freg = (insn >> 25) & 0x1f;
+               else
+                       freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
+               flag = (freg < 32) ? FPRS_DL : FPRS_DU;
+
                for (i = 0; i < size; i++)
                        data[i] = 0;
                
index a063d84336d6384a03d7ddd8a5143f0909801ed8..62c2647bd5cefaa832edf622c641bd0f3c17e8dc 100644 (file)
@@ -6,24 +6,23 @@
  * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
  */
 
+#include <linux/linkage.h>
+
 #include <asm/asi.h>
 #include <asm/page.h>
 #include <asm/ptrace.h>
 #include <asm/visasm.h>
 #include <asm/thread_info.h>
 
-       .text
-       .globl          VISenter, VISenterhalf
-
        /* On entry: %o5=current FPRS value, %g7 is callers address */
        /* May clobber %o5, %g1, %g2, %g3, %g7, %icc, %xcc */
 
        /* Nothing special need be done here to handle pre-emption, this
         * FPU save/restore mechanism is already preemption safe.
         */
-
+       .text
        .align          32
-VISenter:
+ENTRY(VISenter)
        ldub            [%g6 + TI_FPDEPTH], %g1
        brnz,a,pn       %g1, 1f
         cmp            %g1, 1
@@ -79,3 +78,4 @@ vis1: ldub            [%g6 + TI_FPSAVED], %g3
        .align          32
 80:    jmpl            %g7 + %g0, %g0
         nop
+ENDPROC(VISenter)
index 4ac88b7575147fb50443902fb15e1dd7a007bc84..3025bd57f7abbabe83b3f9a8e753d39aeb76c251 100644 (file)
@@ -93,6 +93,8 @@ static unsigned long cpu_pgsz_mask;
 static struct linux_prom64_registers pavail[MAX_BANKS];
 static int pavail_ents;
 
+u64 numa_latency[MAX_NUMNODES][MAX_NUMNODES];
+
 static int cmp_p64(const void *a, const void *b)
 {
        const struct linux_prom64_registers *x = a, *y = b;
@@ -1157,6 +1159,48 @@ static struct mdesc_mlgroup * __init find_mlgroup(u64 node)
        return NULL;
 }
 
+int __node_distance(int from, int to)
+{
+       if ((from >= MAX_NUMNODES) || (to >= MAX_NUMNODES)) {
+               pr_warn("Returning default NUMA distance value for %d->%d\n",
+                       from, to);
+               return (from == to) ? LOCAL_DISTANCE : REMOTE_DISTANCE;
+       }
+       return numa_latency[from][to];
+}
+
+static int find_best_numa_node_for_mlgroup(struct mdesc_mlgroup *grp)
+{
+       int i;
+
+       for (i = 0; i < MAX_NUMNODES; i++) {
+               struct node_mem_mask *n = &node_masks[i];
+
+               if ((grp->mask == n->mask) && (grp->match == n->val))
+                       break;
+       }
+       return i;
+}
+
+static void find_numa_latencies_for_group(struct mdesc_handle *md, u64 grp,
+                                         int index)
+{
+       u64 arc;
+
+       mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_FWD) {
+               int tnode;
+               u64 target = mdesc_arc_target(md, arc);
+               struct mdesc_mlgroup *m = find_mlgroup(target);
+
+               if (!m)
+                       continue;
+               tnode = find_best_numa_node_for_mlgroup(m);
+               if (tnode == MAX_NUMNODES)
+                       continue;
+               numa_latency[index][tnode] = m->latency;
+       }
+}
+
 static int __init numa_attach_mlgroup(struct mdesc_handle *md, u64 grp,
                                      int index)
 {
@@ -1220,9 +1264,16 @@ static int __init numa_parse_mdesc_group(struct mdesc_handle *md, u64 grp,
 static int __init numa_parse_mdesc(void)
 {
        struct mdesc_handle *md = mdesc_grab();
-       int i, err, count;
+       int i, j, err, count;
        u64 node;
 
+       /* Some sane defaults for numa latency values */
+       for (i = 0; i < MAX_NUMNODES; i++) {
+               for (j = 0; j < MAX_NUMNODES; j++)
+                       numa_latency[i][j] = (i == j) ?
+                               LOCAL_DISTANCE : REMOTE_DISTANCE;
+       }
+
        node = mdesc_node_by_name(md, MDESC_NODE_NULL, "latency-groups");
        if (node == MDESC_NODE_NULL) {
                mdesc_release(md);
@@ -1245,6 +1296,23 @@ static int __init numa_parse_mdesc(void)
                count++;
        }
 
+       count = 0;
+       mdesc_for_each_node_by_name(md, node, "group") {
+               find_numa_latencies_for_group(md, node, count);
+               count++;
+       }
+
+       /* Normalize numa latency matrix according to ACPI SLIT spec. */
+       for (i = 0; i < MAX_NUMNODES; i++) {
+               u64 self_latency = numa_latency[i][i];
+
+               for (j = 0; j < MAX_NUMNODES; j++) {
+                       numa_latency[i][j] =
+                               (numa_latency[i][j] * LOCAL_DISTANCE) /
+                               self_latency;
+               }
+       }
+
        add_node_ranges();
 
        for (i = 0; i < num_node_masks; i++) {
index f8b9f71b9a2b631816df61ff9b95657786e7cd51..22564f5f23647a6f54752052091d2c74c194bccf 100644 (file)
@@ -812,7 +812,7 @@ cond_branch:                        f_offset = addrs[i + filter[i].jf];
        if (image) {
                bpf_flush_icache(image, image + proglen);
                fp->bpf_func = (void *)image;
-               fp->jited = true;
+               fp->jited = 1;
        }
 out:
        kfree(addrs);
index 70979846076332bf7771d8517afdcc3415518b17..9fc0107a9c5e557f226b62da9a5e086a4f7b4272 100644 (file)
@@ -34,7 +34,7 @@
  */
 static inline int atomic_read(const atomic_t *v)
 {
-       return ACCESS_ONCE(v->counter);
+       return READ_ONCE(v->counter);
 }
 
 /**
index 096a56d6ead429a2a0c06fd282a52e18a1d76a47..51cabc26e387c32fd368d8058563eae117782ff2 100644 (file)
@@ -24,7 +24,7 @@
 
 /* First, the 32-bit atomic ops that are "real" on our 64-bit platform. */
 
-#define atomic_set(v, i) ((v)->counter = (i))
+#define atomic_set(v, i) WRITE_ONCE((v)->counter, (i))
 
 /*
  * The smp_mb() operations throughout are to support the fact that
@@ -82,8 +82,8 @@ static inline void atomic_xor(int i, atomic_t *v)
 
 #define ATOMIC64_INIT(i)       { (i) }
 
-#define atomic64_read(v)               ((v)->counter)
-#define atomic64_set(v, i) ((v)->counter = (i))
+#define atomic64_read(v)       READ_ONCE((v)->counter)
+#define atomic64_set(v, i)     WRITE_ONCE((v)->counter, (i))
 
 static inline void atomic64_add(long i, atomic64_t *v)
 {
index 96d058a871007e7b119bd3a6d3ff14eb9a5a61eb..db3622f22b618303a8bc9c3c2f34762f400dbafb 100644 (file)
@@ -1123,8 +1123,10 @@ config X86_REBOOTFIXUPS
          Say N otherwise.
 
 config MICROCODE
-       tristate "CPU microcode loading support"
+       bool "CPU microcode loading support"
+       default y
        depends on CPU_SUP_AMD || CPU_SUP_INTEL
+       depends on BLK_DEV_INITRD
        select FW_LOADER
        ---help---
 
@@ -1166,24 +1168,6 @@ config MICROCODE_OLD_INTERFACE
        def_bool y
        depends on MICROCODE
 
-config MICROCODE_INTEL_EARLY
-       bool
-
-config MICROCODE_AMD_EARLY
-       bool
-
-config MICROCODE_EARLY
-       bool "Early load microcode"
-       depends on MICROCODE=y && BLK_DEV_INITRD
-       select MICROCODE_INTEL_EARLY if MICROCODE_INTEL
-       select MICROCODE_AMD_EARLY if MICROCODE_AMD
-       default y
-       help
-         This option provides functionality to read additional microcode data
-         at the beginning of initrd image. The data tells kernel to load
-         microcode to CPU's as early as possible. No functional change if no
-         microcode data is glued to the initrd, therefore it's safe to say Y.
-
 config X86_MSR
        tristate "/dev/cpu/*/msr - Model-specific register support"
        ---help---
@@ -2043,6 +2027,55 @@ config COMPAT_VDSO
          If unsure, say N: if you are compiling your own kernel, you
          are unlikely to be using a buggy version of glibc.
 
+choice
+       prompt "vsyscall table for legacy applications"
+       depends on X86_64
+       default LEGACY_VSYSCALL_EMULATE
+       help
+         Legacy user code that does not know how to find the vDSO expects
+         to be able to issue three syscalls by calling fixed addresses in
+         kernel space. Since this location is not randomized with ASLR,
+         it can be used to assist security vulnerability exploitation.
+
+         This setting can be changed at boot time via the kernel command
+         line parameter vsyscall=[native|emulate|none].
+
+         On a system with recent enough glibc (2.14 or newer) and no
+         static binaries, you can say None without a performance penalty
+         to improve security.
+
+         If unsure, select "Emulate".
+
+       config LEGACY_VSYSCALL_NATIVE
+               bool "Native"
+               help
+                 Actual executable code is located in the fixed vsyscall
+                 address mapping, implementing time() efficiently. Since
+                 this makes the mapping executable, it can be used during
+                 security vulnerability exploitation (traditionally as
+                 ROP gadgets). This configuration is not recommended.
+
+       config LEGACY_VSYSCALL_EMULATE
+               bool "Emulate"
+               help
+                 The kernel traps and emulates calls into the fixed
+                 vsyscall address mapping. This makes the mapping
+                 non-executable, but it still contains known contents,
+                 which could be used in certain rare security vulnerability
+                 exploits. This configuration is recommended when userspace
+                 still uses the vsyscall area.
+
+       config LEGACY_VSYSCALL_NONE
+               bool "None"
+               help
+                 There will be no vsyscall mapping at all. This will
+                 eliminate any risk of ASLR bypass due to the vsyscall
+                 fixed address mapping. Attempts to use the vsyscalls
+                 will be reported to dmesg, so that either old or
+                 malicious userspace programs can be identified.
+
+endchoice
+
 config CMDLINE_BOOL
        bool "Built-in kernel command line"
        ---help---
index 6983314c8b377cfdf8c4206d626766c7dd1fbcc2..3ba5ff2f2d082b3653477141ebbb4409c119f54f 100644 (file)
@@ -3,10 +3,6 @@ choice
        prompt "Processor family"
        default M686 if X86_32
        default GENERIC_CPU if X86_64
-
-config M486
-       bool "486"
-       depends on X86_32
        ---help---
          This is the processor type of your CPU. This information is
          used for optimizing purposes. In order to compile a kernel
@@ -23,9 +19,9 @@ config M486
 
          Here are the settings recommended for greatest speed:
          - "486" for the AMD/Cyrix/IBM/Intel 486DX/DX2/DX4 or
-         SL/SLC/SLC2/SLC3/SX/SX2 and UMC U5D or U5S.
+           SL/SLC/SLC2/SLC3/SX/SX2 and UMC U5D or U5S.
          - "586" for generic Pentium CPUs lacking the TSC
-         (time stamp counter) register.
+           (time stamp counter) register.
          - "Pentium-Classic" for the Intel Pentium.
          - "Pentium-MMX" for the Intel Pentium MMX.
          - "Pentium-Pro" for the Intel Pentium Pro.
@@ -34,17 +30,31 @@ config M486
          - "Pentium-4" for the Intel Pentium 4 or P4-based Celeron.
          - "K6" for the AMD K6, K6-II and K6-III (aka K6-3D).
          - "Athlon" for the AMD K7 family (Athlon/Duron/Thunderbird).
+         - "Opteron/Athlon64/Hammer/K8" for all K8 and newer AMD CPUs.
          - "Crusoe" for the Transmeta Crusoe series.
          - "Efficeon" for the Transmeta Efficeon series.
          - "Winchip-C6" for original IDT Winchip.
          - "Winchip-2" for IDT Winchips with 3dNow! capabilities.
+         - "AMD Elan" for the 32-bit AMD Elan embedded CPU.
          - "GeodeGX1" for Geode GX1 (Cyrix MediaGX).
          - "Geode GX/LX" For AMD Geode GX and LX processors.
          - "CyrixIII/VIA C3" for VIA Cyrix III or VIA C3.
          - "VIA C3-2" for VIA C3-2 "Nehemiah" (model 9 and above).
          - "VIA C7" for VIA C7.
+         - "Intel P4" for the Pentium 4/Netburst microarchitecture.
+         - "Core 2/newer Xeon" for all core2 and newer Intel CPUs.
+         - "Intel Atom" for the Atom-microarchitecture CPUs.
+         - "Generic-x86-64" for a kernel which runs on any x86-64 CPU.
+
+         See each option's help text for additional details. If you don't know
+         what to do, choose "486".
 
-         If you don't know what to do, choose "486".
+config M486
+       bool "486"
+       depends on X86_32
+       ---help---
+         Select this for an 486-class CPU such as AMD/Cyrix/IBM/Intel
+         486DX/DX2/DX4 or SL/SLC/SLC2/SLC3/SX/SX2 and UMC U5D or U5S.
 
 config M586
        bool "586/K5/5x86/6x86/6x86MX"
index d8c0d3266173d96f84ac543885ba0bd63c0c3e47..3e0baf726eef56f04765d26349e34b719fbd1e9f 100644 (file)
@@ -65,10 +65,14 @@ config EARLY_PRINTK_EFI
          This is useful for kernel debugging when your machine crashes very
          early before the console code is initialized.
 
+config X86_PTDUMP_CORE
+       def_bool n
+
 config X86_PTDUMP
        bool "Export kernel pagetable layout to userspace via debugfs"
        depends on DEBUG_KERNEL
        select DEBUG_FS
+       select X86_PTDUMP_CORE
        ---help---
          Say Y here if you want to show the kernel pagetable layout in a
          debugfs file. This information is only useful for kernel developers
@@ -79,7 +83,8 @@ config X86_PTDUMP
 
 config EFI_PGT_DUMP
        bool "Dump the EFI pagetable"
-       depends on EFI && X86_PTDUMP
+       depends on EFI
+       select X86_PTDUMP_CORE
        ---help---
          Enable this if you want to dump the EFI page table before
          enabling virtual mode. This can be used to debug miscellaneous
@@ -105,6 +110,35 @@ config DEBUG_RODATA_TEST
          feature as well as for the change_page_attr() infrastructure.
          If in doubt, say "N"
 
+config DEBUG_WX
+       bool "Warn on W+X mappings at boot"
+       depends on DEBUG_RODATA
+       default y
+       select X86_PTDUMP_CORE
+       ---help---
+         Generate a warning if any W+X mappings are found at boot.
+
+         This is useful for discovering cases where the kernel is leaving
+         W+X mappings after applying NX, as such mappings are a security risk.
+
+         Look for a message in dmesg output like this:
+
+           x86/mm: Checked W+X mappings: passed, no W+X pages found.
+
+         or like this, if the check failed:
+
+           x86/mm: Checked W+X mappings: FAILED, <N> W+X pages found.
+
+         Note that even if the check fails, your kernel is possibly
+         still fine, as W+X mappings are not a security hole in
+         themselves, what they do is that they make the exploitation
+         of other unfixed kernel bugs easier.
+
+         There is no runtime or memory usage effect of this option
+         once the kernel has booted up - it's a one time check.
+
+         If in doubt, say "Y".
+
 config DEBUG_SET_MODULE_RONX
        bool "Set loadable kernel module data as NX and text as RO"
        depends on MODULES
index 747860c696e1d5a4f7670c3b7b8d6127d2fdeb9f..4086abca0b32345c92207fa75468cc98ebe97da4 100644 (file)
@@ -159,15 +159,23 @@ endif
 sp-$(CONFIG_X86_32) := esp
 sp-$(CONFIG_X86_64) := rsp
 
+# do binutils support CFI?
+cfi := $(call as-instr,.cfi_startproc\n.cfi_rel_offset $(sp-y)$(comma)0\n.cfi_endproc,-DCONFIG_AS_CFI=1)
+# is .cfi_signal_frame supported too?
+cfi-sigframe := $(call as-instr,.cfi_startproc\n.cfi_signal_frame\n.cfi_endproc,-DCONFIG_AS_CFI_SIGNAL_FRAME=1)
+cfi-sections := $(call as-instr,.cfi_sections .debug_frame,-DCONFIG_AS_CFI_SECTIONS=1)
+
 # does binutils support specific instructions?
 asinstr := $(call as-instr,fxsaveq (%rax),-DCONFIG_AS_FXSAVEQ=1)
 asinstr += $(call as-instr,pshufb %xmm0$(comma)%xmm0,-DCONFIG_AS_SSSE3=1)
 asinstr += $(call as-instr,crc32l %eax$(comma)%eax,-DCONFIG_AS_CRC32=1)
 avx_instr := $(call as-instr,vxorps %ymm0$(comma)%ymm1$(comma)%ymm2,-DCONFIG_AS_AVX=1)
 avx2_instr :=$(call as-instr,vpbroadcastb %xmm0$(comma)%ymm1,-DCONFIG_AS_AVX2=1)
+sha1_ni_instr :=$(call as-instr,sha1msg1 %xmm0$(comma)%xmm1,-DCONFIG_AS_SHA1_NI=1)
+sha256_ni_instr :=$(call as-instr,sha256msg1 %xmm0$(comma)%xmm1,-DCONFIG_AS_SHA256_NI=1)
 
-KBUILD_AFLAGS += $(asinstr) $(avx_instr) $(avx2_instr)
-KBUILD_CFLAGS += $(asinstr) $(avx_instr) $(avx2_instr)
+KBUILD_AFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr) $(avx_instr) $(avx2_instr) $(sha1_ni_instr) $(sha256_ni_instr)
+KBUILD_CFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr) $(avx_instr) $(avx2_instr) $(sha1_ni_instr) $(sha256_ni_instr)
 
 LDFLAGS := -m elf_$(UTS_MACHINE)
 
index db51c1f27446157001cedc87bdd38b551e2f1844..583d539a41977a2c7397b9fd38e0839d3c41dbb9 100644 (file)
@@ -624,7 +624,7 @@ setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
 static efi_status_t
 __gop_query32(struct efi_graphics_output_protocol_32 *gop32,
              struct efi_graphics_output_mode_info **info,
-             unsigned long *size, u32 *fb_base)
+             unsigned long *size, u64 *fb_base)
 {
        struct efi_graphics_output_protocol_mode_32 *mode;
        efi_status_t status;
@@ -650,7 +650,8 @@ setup_gop32(struct screen_info *si, efi_guid_t *proto,
        unsigned long nr_gops;
        u16 width, height;
        u32 pixels_per_scan_line;
-       u32 fb_base;
+       u32 ext_lfb_base;
+       u64 fb_base;
        struct efi_pixel_bitmask pixel_info;
        int pixel_format;
        efi_status_t status;
@@ -667,7 +668,7 @@ setup_gop32(struct screen_info *si, efi_guid_t *proto,
                bool conout_found = false;
                void *dummy = NULL;
                u32 h = handles[i];
-               u32 current_fb_base;
+               u64 current_fb_base;
 
                status = efi_call_early(handle_protocol, h,
                                        proto, (void **)&gop32);
@@ -715,6 +716,13 @@ setup_gop32(struct screen_info *si, efi_guid_t *proto,
        si->lfb_width = width;
        si->lfb_height = height;
        si->lfb_base = fb_base;
+
+       ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
+       if (ext_lfb_base) {
+               si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
+               si->ext_lfb_base = ext_lfb_base;
+       }
+
        si->pages = 1;
 
        setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
@@ -729,7 +737,7 @@ out:
 static efi_status_t
 __gop_query64(struct efi_graphics_output_protocol_64 *gop64,
              struct efi_graphics_output_mode_info **info,
-             unsigned long *size, u32 *fb_base)
+             unsigned long *size, u64 *fb_base)
 {
        struct efi_graphics_output_protocol_mode_64 *mode;
        efi_status_t status;
@@ -755,7 +763,8 @@ setup_gop64(struct screen_info *si, efi_guid_t *proto,
        unsigned long nr_gops;
        u16 width, height;
        u32 pixels_per_scan_line;
-       u32 fb_base;
+       u32 ext_lfb_base;
+       u64 fb_base;
        struct efi_pixel_bitmask pixel_info;
        int pixel_format;
        efi_status_t status;
@@ -772,7 +781,7 @@ setup_gop64(struct screen_info *si, efi_guid_t *proto,
                bool conout_found = false;
                void *dummy = NULL;
                u64 h = handles[i];
-               u32 current_fb_base;
+               u64 current_fb_base;
 
                status = efi_call_early(handle_protocol, h,
                                        proto, (void **)&gop64);
@@ -820,6 +829,13 @@ setup_gop64(struct screen_info *si, efi_guid_t *proto,
        si->lfb_width = width;
        si->lfb_height = height;
        si->lfb_base = fb_base;
+
+       ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
+       if (ext_lfb_base) {
+               si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
+               si->ext_lfb_base = ext_lfb_base;
+       }
+
        si->pages = 1;
 
        setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
index 2d6b309c8e9a12ac67ddf9d9cb429cb1fe8a7eae..6236b9ec4b764cfd988fb47f08cc000a653fbed4 100644 (file)
@@ -154,7 +154,7 @@ extra_header_fields:
 #else
        .quad   0                               # ImageBase
 #endif
-       .long   CONFIG_PHYSICAL_ALIGN           # SectionAlignment
+       .long   0x20                            # SectionAlignment
        .long   0x20                            # FileAlignment
        .word   0                               # MajorOperatingSystemVersion
        .word   0                               # MinorOperatingSystemVersion
index 9a2838cf05916a132ee573ad4c0061138fda716a..b9b912a44d61b7d988c07b13e8dcafe0e14ae57a 100644 (file)
@@ -5,6 +5,8 @@
 avx_supported := $(call as-instr,vpxor %xmm0$(comma)%xmm0$(comma)%xmm0,yes,no)
 avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\
                                $(comma)4)$(comma)%ymm2,yes,no)
+sha1_ni_supported :=$(call as-instr,sha1msg1 %xmm0$(comma)%xmm1,yes,no)
+sha256_ni_supported :=$(call as-instr,sha256msg1 %xmm0$(comma)%xmm1,yes,no)
 
 obj-$(CONFIG_CRYPTO_GLUE_HELPER_X86) += glue_helper.o
 
@@ -91,9 +93,15 @@ ifeq ($(avx2_supported),yes)
 sha1-ssse3-y += sha1_avx2_x86_64_asm.o
 poly1305-x86_64-y += poly1305-avx2-x86_64.o
 endif
+ifeq ($(sha1_ni_supported),yes)
+sha1-ssse3-y += sha1_ni_asm.o
+endif
 crc32c-intel-y := crc32c-intel_glue.o
 crc32c-intel-$(CONFIG_64BIT) += crc32c-pcl-intel-asm_64.o
 crc32-pclmul-y := crc32-pclmul_asm.o crc32-pclmul_glue.o
 sha256-ssse3-y := sha256-ssse3-asm.o sha256-avx-asm.o sha256-avx2-asm.o sha256_ssse3_glue.o
+ifeq ($(sha256_ni_supported),yes)
+sha256-ssse3-y += sha256_ni_asm.o
+endif
 sha512-ssse3-y := sha512-ssse3-asm.o sha512-avx-asm.o sha512-avx2-asm.o sha512_ssse3_glue.o
 crct10dif-pclmul-y := crct10dif-pcl-asm_64.o crct10dif-pclmul_glue.o
index 4c65c70e628bb2776c17c106973b2f2b668890fb..d844569245633845da1fe485dca9c44043be680f 100644 (file)
@@ -567,7 +567,8 @@ static int __init camellia_aesni_init(void)
                return -ENODEV;
        }
 
-       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+       if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
+                               &feature_name)) {
                pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
index bacaa13acac544e037571bd292e91f5239256edc..93d8f295784e399c2fc363d8b5602fede9039529 100644 (file)
@@ -559,7 +559,8 @@ static int __init camellia_aesni_init(void)
                return -ENODEV;
        }
 
-       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+       if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
+                               &feature_name)) {
                pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
index be00aa48b2b5e3044ea8a397b0df37428e409447..8648158f39166f1cf1603fc7f139af629e163bde 100644 (file)
@@ -469,7 +469,8 @@ static int __init cast5_init(void)
 {
        const char *feature_name;
 
-       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+       if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
+                               &feature_name)) {
                pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
index 5dbba72242217541a92056068378abd86e67c378..fca459578c355d8104654bad9d06e88c136e2f61 100644 (file)
@@ -591,7 +591,8 @@ static int __init cast6_init(void)
 {
        const char *feature_name;
 
-       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+       if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
+                               &feature_name)) {
                pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
index effe2160b7c535acc3468e7371744ab318c6a8c2..722bacea040e71f4cae3769e38605e51a6fd8f82 100644 (file)
@@ -130,7 +130,7 @@ static int __init chacha20_simd_mod_init(void)
 
 #ifdef CONFIG_AS_AVX2
        chacha20_use_avx2 = cpu_has_avx && cpu_has_avx2 &&
-                           cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, NULL);
+                           cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL);
 #endif
        return crypto_register_alg(&alg);
 }
index 225be06edc80982f9509e25af09e1ac0a0232237..4fe27e07419486055ca7bc5f9108dbe05639df5c 100644 (file)
@@ -330,7 +330,7 @@ ENDPROC(crc_pcl)
        ## PCLMULQDQ tables
        ## Table is 128 entries x 2 words (8 bytes) each
        ################################################################
-.section       .rotata, "a", %progbits
+.section       .rodata, "a", %progbits
 .align 8
 K_table:
        .long 0x493c7d27, 0x00000001
index f7170d764f32c307c827d33e8c40c5fbd95acd4d..4264a3d595894b1a8e8bbba635f477878b0461dd 100644 (file)
@@ -184,7 +184,7 @@ static int __init poly1305_simd_mod_init(void)
 
 #ifdef CONFIG_AS_AVX2
        poly1305_use_avx2 = cpu_has_avx && cpu_has_avx2 &&
-                           cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, NULL);
+                           cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL);
        alg.descsize = sizeof(struct poly1305_simd_desc_ctx);
        if (poly1305_use_avx2)
                alg.descsize += 10 * sizeof(u32);
index 7d838dc4d888f30010dfba7c21561a16bfe4e3d0..6d198342e2de4951635c46aa3486c88cbc793274 100644 (file)
@@ -542,7 +542,8 @@ static int __init init(void)
                pr_info("AVX2 instructions are not detected.\n");
                return -ENODEV;
        }
-       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+       if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
+                               &feature_name)) {
                pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
index da7dafc9b16d5cca124e4f6d01d4462f0cb92b15..5dc37026c7ce6d78407cd04cea618a10d14174a8 100644 (file)
@@ -597,7 +597,8 @@ static int __init serpent_init(void)
 {
        const char *feature_name;
 
-       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+       if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM,
+                               &feature_name)) {
                pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
diff --git a/arch/x86/crypto/sha1_ni_asm.S b/arch/x86/crypto/sha1_ni_asm.S
new file mode 100644 (file)
index 0000000..874a651
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Intel SHA Extensions optimized implementation of a SHA-1 update function
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * 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.
+ *
+ * Contact Information:
+ *     Sean Gulley <sean.m.gulley@intel.com>
+ *     Tim Chen <tim.c.chen@linux.intel.com>
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of Intel Corporation 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER 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/linkage.h>
+
+#define DIGEST_PTR     %rdi    /* 1st arg */
+#define DATA_PTR       %rsi    /* 2nd arg */
+#define NUM_BLKS       %rdx    /* 3rd arg */
+
+#define RSPSAVE                %rax
+
+/* gcc conversion */
+#define FRAME_SIZE     32      /* space for 2x16 bytes */
+
+#define ABCD           %xmm0
+#define E0             %xmm1   /* Need two E's b/c they ping pong */
+#define E1             %xmm2
+#define MSG0           %xmm3
+#define MSG1           %xmm4
+#define MSG2           %xmm5
+#define MSG3           %xmm6
+#define SHUF_MASK      %xmm7
+
+
+/*
+ * Intel SHA Extensions optimized implementation of a SHA-1 update function
+ *
+ * The function takes a pointer to the current hash values, a pointer to the
+ * input data, and a number of 64 byte blocks to process.  Once all blocks have
+ * been processed, the digest pointer is  updated with the resulting hash value.
+ * The function only processes complete blocks, there is no functionality to
+ * store partial blocks. All message padding and hash value initialization must
+ * be done outside the update function.
+ *
+ * The indented lines in the loop are instructions related to rounds processing.
+ * The non-indented lines are instructions related to the message schedule.
+ *
+ * void sha1_ni_transform(uint32_t *digest, const void *data,
+               uint32_t numBlocks)
+ * digest : pointer to digest
+ * data: pointer to input data
+ * numBlocks: Number of blocks to process
+ */
+.text
+.align 32
+ENTRY(sha1_ni_transform)
+       mov             %rsp, RSPSAVE
+       sub             $FRAME_SIZE, %rsp
+       and             $~0xF, %rsp
+
+       shl             $6, NUM_BLKS            /* convert to bytes */
+       jz              .Ldone_hash
+       add             DATA_PTR, NUM_BLKS      /* pointer to end of data */
+
+       /* load initial hash values */
+       pinsrd          $3, 1*16(DIGEST_PTR), E0
+       movdqu          0*16(DIGEST_PTR), ABCD
+       pand            UPPER_WORD_MASK(%rip), E0
+       pshufd          $0x1B, ABCD, ABCD
+
+       movdqa          PSHUFFLE_BYTE_FLIP_MASK(%rip), SHUF_MASK
+
+.Lloop0:
+       /* Save hash values for addition after rounds */
+       movdqa          E0, (0*16)(%rsp)
+       movdqa          ABCD, (1*16)(%rsp)
+
+       /* Rounds 0-3 */
+       movdqu          0*16(DATA_PTR), MSG0
+       pshufb          SHUF_MASK, MSG0
+               paddd           MSG0, E0
+               movdqa          ABCD, E1
+               sha1rnds4       $0, E0, ABCD
+
+       /* Rounds 4-7 */
+       movdqu          1*16(DATA_PTR), MSG1
+       pshufb          SHUF_MASK, MSG1
+               sha1nexte       MSG1, E1
+               movdqa          ABCD, E0
+               sha1rnds4       $0, E1, ABCD
+       sha1msg1        MSG1, MSG0
+
+       /* Rounds 8-11 */
+       movdqu          2*16(DATA_PTR), MSG2
+       pshufb          SHUF_MASK, MSG2
+               sha1nexte       MSG2, E0
+               movdqa          ABCD, E1
+               sha1rnds4       $0, E0, ABCD
+       sha1msg1        MSG2, MSG1
+       pxor            MSG2, MSG0
+
+       /* Rounds 12-15 */
+       movdqu          3*16(DATA_PTR), MSG3
+       pshufb          SHUF_MASK, MSG3
+               sha1nexte       MSG3, E1
+               movdqa          ABCD, E0
+       sha1msg2        MSG3, MSG0
+               sha1rnds4       $0, E1, ABCD
+       sha1msg1        MSG3, MSG2
+       pxor            MSG3, MSG1
+
+       /* Rounds 16-19 */
+               sha1nexte       MSG0, E0
+               movdqa          ABCD, E1
+       sha1msg2        MSG0, MSG1
+               sha1rnds4       $0, E0, ABCD
+       sha1msg1        MSG0, MSG3
+       pxor            MSG0, MSG2
+
+       /* Rounds 20-23 */
+               sha1nexte       MSG1, E1
+               movdqa          ABCD, E0
+       sha1msg2        MSG1, MSG2
+               sha1rnds4       $1, E1, ABCD
+       sha1msg1        MSG1, MSG0
+       pxor            MSG1, MSG3
+
+       /* Rounds 24-27 */
+               sha1nexte       MSG2, E0
+               movdqa          ABCD, E1
+       sha1msg2        MSG2, MSG3
+               sha1rnds4       $1, E0, ABCD
+       sha1msg1        MSG2, MSG1
+       pxor            MSG2, MSG0
+
+       /* Rounds 28-31 */
+               sha1nexte       MSG3, E1
+               movdqa          ABCD, E0
+       sha1msg2        MSG3, MSG0
+               sha1rnds4       $1, E1, ABCD
+       sha1msg1        MSG3, MSG2
+       pxor            MSG3, MSG1
+
+       /* Rounds 32-35 */
+               sha1nexte       MSG0, E0
+               movdqa          ABCD, E1
+       sha1msg2        MSG0, MSG1
+               sha1rnds4       $1, E0, ABCD
+       sha1msg1        MSG0, MSG3
+       pxor            MSG0, MSG2
+
+       /* Rounds 36-39 */
+               sha1nexte       MSG1, E1
+               movdqa          ABCD, E0
+       sha1msg2        MSG1, MSG2
+               sha1rnds4       $1, E1, ABCD
+       sha1msg1        MSG1, MSG0
+       pxor            MSG1, MSG3
+
+       /* Rounds 40-43 */
+               sha1nexte       MSG2, E0
+               movdqa          ABCD, E1
+       sha1msg2        MSG2, MSG3
+               sha1rnds4       $2, E0, ABCD
+       sha1msg1        MSG2, MSG1
+       pxor            MSG2, MSG0
+
+       /* Rounds 44-47 */
+               sha1nexte       MSG3, E1
+               movdqa          ABCD, E0
+       sha1msg2        MSG3, MSG0
+               sha1rnds4       $2, E1, ABCD
+       sha1msg1        MSG3, MSG2
+       pxor            MSG3, MSG1
+
+       /* Rounds 48-51 */
+               sha1nexte       MSG0, E0
+               movdqa          ABCD, E1
+       sha1msg2        MSG0, MSG1
+               sha1rnds4       $2, E0, ABCD
+       sha1msg1        MSG0, MSG3
+       pxor            MSG0, MSG2
+
+       /* Rounds 52-55 */
+               sha1nexte       MSG1, E1
+               movdqa          ABCD, E0
+       sha1msg2        MSG1, MSG2
+               sha1rnds4       $2, E1, ABCD
+       sha1msg1        MSG1, MSG0
+       pxor            MSG1, MSG3
+
+       /* Rounds 56-59 */
+               sha1nexte       MSG2, E0
+               movdqa          ABCD, E1
+       sha1msg2        MSG2, MSG3
+               sha1rnds4       $2, E0, ABCD
+       sha1msg1        MSG2, MSG1
+       pxor            MSG2, MSG0
+
+       /* Rounds 60-63 */
+               sha1nexte       MSG3, E1
+               movdqa          ABCD, E0
+       sha1msg2        MSG3, MSG0
+               sha1rnds4       $3, E1, ABCD
+       sha1msg1        MSG3, MSG2
+       pxor            MSG3, MSG1
+
+       /* Rounds 64-67 */
+               sha1nexte       MSG0, E0
+               movdqa          ABCD, E1
+       sha1msg2        MSG0, MSG1
+               sha1rnds4       $3, E0, ABCD
+       sha1msg1        MSG0, MSG3
+       pxor            MSG0, MSG2
+
+       /* Rounds 68-71 */
+               sha1nexte       MSG1, E1
+               movdqa          ABCD, E0
+       sha1msg2        MSG1, MSG2
+               sha1rnds4       $3, E1, ABCD
+       pxor            MSG1, MSG3
+
+       /* Rounds 72-75 */
+               sha1nexte       MSG2, E0
+               movdqa          ABCD, E1
+       sha1msg2        MSG2, MSG3
+               sha1rnds4       $3, E0, ABCD
+
+       /* Rounds 76-79 */
+               sha1nexte       MSG3, E1
+               movdqa          ABCD, E0
+               sha1rnds4       $3, E1, ABCD
+
+       /* Add current hash values with previously saved */
+       sha1nexte       (0*16)(%rsp), E0
+       paddd           (1*16)(%rsp), ABCD
+
+       /* Increment data pointer and loop if more to process */
+       add             $64, DATA_PTR
+       cmp             NUM_BLKS, DATA_PTR
+       jne             .Lloop0
+
+       /* Write hash values back in the correct order */
+       pshufd          $0x1B, ABCD, ABCD
+       movdqu          ABCD, 0*16(DIGEST_PTR)
+       pextrd          $3, E0, 1*16(DIGEST_PTR)
+
+.Ldone_hash:
+       mov             RSPSAVE, %rsp
+
+       ret
+ENDPROC(sha1_ni_transform)
+
+.data
+
+.align 64
+PSHUFFLE_BYTE_FLIP_MASK:
+       .octa 0x000102030405060708090a0b0c0d0e0f
+UPPER_WORD_MASK:
+       .octa 0xFFFFFFFF000000000000000000000000
index 7c48e8b20848e5c890825f25db91c63616dce65d..dd14616b773970d13c2886f255c0f76b4eb58450 100644 (file)
 #include <crypto/sha1_base.h>
 #include <asm/fpu/api.h>
 
+typedef void (sha1_transform_fn)(u32 *digest, const char *data,
+                               unsigned int rounds);
 
-asmlinkage void sha1_transform_ssse3(u32 *digest, const char *data,
-                                    unsigned int rounds);
-#ifdef CONFIG_AS_AVX
-asmlinkage void sha1_transform_avx(u32 *digest, const char *data,
-                                  unsigned int rounds);
-#endif
-#ifdef CONFIG_AS_AVX2
-#define SHA1_AVX2_BLOCK_OPTSIZE        4       /* optimal 4*64 bytes of SHA1 blocks */
-
-asmlinkage void sha1_transform_avx2(u32 *digest, const char *data,
-                                   unsigned int rounds);
-#endif
-
-static void (*sha1_transform_asm)(u32 *, const char *, unsigned int);
-
-static int sha1_ssse3_update(struct shash_desc *desc, const u8 *data,
-                            unsigned int len)
+static int sha1_update(struct shash_desc *desc, const u8 *data,
+                            unsigned int len, sha1_transform_fn *sha1_xform)
 {
        struct sha1_state *sctx = shash_desc_ctx(desc);
 
@@ -61,14 +48,14 @@ static int sha1_ssse3_update(struct shash_desc *desc, const u8 *data,
 
        kernel_fpu_begin();
        sha1_base_do_update(desc, data, len,
-                           (sha1_block_fn *)sha1_transform_asm);
+                           (sha1_block_fn *)sha1_xform);
        kernel_fpu_end();
 
        return 0;
 }
 
-static int sha1_ssse3_finup(struct shash_desc *desc, const u8 *data,
-                             unsigned int len, u8 *out)
+static int sha1_finup(struct shash_desc *desc, const u8 *data,
+                     unsigned int len, u8 *out, sha1_transform_fn *sha1_xform)
 {
        if (!irq_fpu_usable())
                return crypto_sha1_finup(desc, data, len, out);
@@ -76,32 +63,37 @@ static int sha1_ssse3_finup(struct shash_desc *desc, const u8 *data,
        kernel_fpu_begin();
        if (len)
                sha1_base_do_update(desc, data, len,
-                                   (sha1_block_fn *)sha1_transform_asm);
-       sha1_base_do_finalize(desc, (sha1_block_fn *)sha1_transform_asm);
+                                   (sha1_block_fn *)sha1_xform);
+       sha1_base_do_finalize(desc, (sha1_block_fn *)sha1_xform);
        kernel_fpu_end();
 
        return sha1_base_finish(desc, out);
 }
 
-/* Add padding and return the message digest. */
-static int sha1_ssse3_final(struct shash_desc *desc, u8 *out)
+asmlinkage void sha1_transform_ssse3(u32 *digest, const char *data,
+                                    unsigned int rounds);
+
+static int sha1_ssse3_update(struct shash_desc *desc, const u8 *data,
+                            unsigned int len)
 {
-       return sha1_ssse3_finup(desc, NULL, 0, out);
+       return sha1_update(desc, data, len,
+                       (sha1_transform_fn *) sha1_transform_ssse3);
 }
 
-#ifdef CONFIG_AS_AVX2
-static void sha1_apply_transform_avx2(u32 *digest, const char *data,
-                               unsigned int rounds)
+static int sha1_ssse3_finup(struct shash_desc *desc, const u8 *data,
+                             unsigned int len, u8 *out)
 {
-       /* Select the optimal transform based on data block size */
-       if (rounds >= SHA1_AVX2_BLOCK_OPTSIZE)
-               sha1_transform_avx2(digest, data, rounds);
-       else
-               sha1_transform_avx(digest, data, rounds);
+       return sha1_finup(desc, data, len, out,
+                       (sha1_transform_fn *) sha1_transform_ssse3);
+}
+
+/* Add padding and return the message digest. */
+static int sha1_ssse3_final(struct shash_desc *desc, u8 *out)
+{
+       return sha1_ssse3_finup(desc, NULL, 0, out);
 }
-#endif
 
-static struct shash_alg alg = {
+static struct shash_alg sha1_ssse3_alg = {
        .digestsize     =       SHA1_DIGEST_SIZE,
        .init           =       sha1_base_init,
        .update         =       sha1_ssse3_update,
@@ -110,7 +102,7 @@ static struct shash_alg alg = {
        .descsize       =       sizeof(struct sha1_state),
        .base           =       {
                .cra_name       =       "sha1",
-               .cra_driver_name      "sha1-ssse3",
+               .cra_driver_name =      "sha1-ssse3",
                .cra_priority   =       150,
                .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
                .cra_blocksize  =       SHA1_BLOCK_SIZE,
@@ -118,10 +110,62 @@ static struct shash_alg alg = {
        }
 };
 
+static int register_sha1_ssse3(void)
+{
+       if (boot_cpu_has(X86_FEATURE_SSSE3))
+               return crypto_register_shash(&sha1_ssse3_alg);
+       return 0;
+}
+
+static void unregister_sha1_ssse3(void)
+{
+       if (boot_cpu_has(X86_FEATURE_SSSE3))
+               crypto_unregister_shash(&sha1_ssse3_alg);
+}
+
 #ifdef CONFIG_AS_AVX
-static bool __init avx_usable(void)
+asmlinkage void sha1_transform_avx(u32 *digest, const char *data,
+                                  unsigned int rounds);
+
+static int sha1_avx_update(struct shash_desc *desc, const u8 *data,
+                            unsigned int len)
 {
-       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, NULL)) {
+       return sha1_update(desc, data, len,
+                       (sha1_transform_fn *) sha1_transform_avx);
+}
+
+static int sha1_avx_finup(struct shash_desc *desc, const u8 *data,
+                             unsigned int len, u8 *out)
+{
+       return sha1_finup(desc, data, len, out,
+                       (sha1_transform_fn *) sha1_transform_avx);
+}
+
+static int sha1_avx_final(struct shash_desc *desc, u8 *out)
+{
+       return sha1_avx_finup(desc, NULL, 0, out);
+}
+
+static struct shash_alg sha1_avx_alg = {
+       .digestsize     =       SHA1_DIGEST_SIZE,
+       .init           =       sha1_base_init,
+       .update         =       sha1_avx_update,
+       .final          =       sha1_avx_final,
+       .finup          =       sha1_avx_finup,
+       .descsize       =       sizeof(struct sha1_state),
+       .base           =       {
+               .cra_name       =       "sha1",
+               .cra_driver_name =      "sha1-avx",
+               .cra_priority   =       160,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA1_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
+};
+
+static bool avx_usable(void)
+{
+       if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL)) {
                if (cpu_has_avx)
                        pr_info("AVX detected but unusable.\n");
                return false;
@@ -130,55 +174,197 @@ static bool __init avx_usable(void)
        return true;
 }
 
-#ifdef CONFIG_AS_AVX2
-static bool __init avx2_usable(void)
+static int register_sha1_avx(void)
+{
+       if (avx_usable())
+               return crypto_register_shash(&sha1_avx_alg);
+       return 0;
+}
+
+static void unregister_sha1_avx(void)
 {
-       if (avx_usable() && cpu_has_avx2 && boot_cpu_has(X86_FEATURE_BMI1) &&
-           boot_cpu_has(X86_FEATURE_BMI2))
+       if (avx_usable())
+               crypto_unregister_shash(&sha1_avx_alg);
+}
+
+#else  /* CONFIG_AS_AVX */
+static inline int register_sha1_avx(void) { return 0; }
+static inline void unregister_sha1_avx(void) { }
+#endif /* CONFIG_AS_AVX */
+
+
+#if defined(CONFIG_AS_AVX2) && (CONFIG_AS_AVX)
+#define SHA1_AVX2_BLOCK_OPTSIZE        4       /* optimal 4*64 bytes of SHA1 blocks */
+
+asmlinkage void sha1_transform_avx2(u32 *digest, const char *data,
+                                   unsigned int rounds);
+
+static bool avx2_usable(void)
+{
+       if (avx_usable() && boot_cpu_has(X86_FEATURE_AVX2)
+               && boot_cpu_has(X86_FEATURE_BMI1)
+               && boot_cpu_has(X86_FEATURE_BMI2))
                return true;
 
        return false;
 }
+
+static void sha1_apply_transform_avx2(u32 *digest, const char *data,
+                               unsigned int rounds)
+{
+       /* Select the optimal transform based on data block size */
+       if (rounds >= SHA1_AVX2_BLOCK_OPTSIZE)
+               sha1_transform_avx2(digest, data, rounds);
+       else
+               sha1_transform_avx(digest, data, rounds);
+}
+
+static int sha1_avx2_update(struct shash_desc *desc, const u8 *data,
+                            unsigned int len)
+{
+       return sha1_update(desc, data, len,
+               (sha1_transform_fn *) sha1_apply_transform_avx2);
+}
+
+static int sha1_avx2_finup(struct shash_desc *desc, const u8 *data,
+                             unsigned int len, u8 *out)
+{
+       return sha1_finup(desc, data, len, out,
+               (sha1_transform_fn *) sha1_apply_transform_avx2);
+}
+
+static int sha1_avx2_final(struct shash_desc *desc, u8 *out)
+{
+       return sha1_avx2_finup(desc, NULL, 0, out);
+}
+
+static struct shash_alg sha1_avx2_alg = {
+       .digestsize     =       SHA1_DIGEST_SIZE,
+       .init           =       sha1_base_init,
+       .update         =       sha1_avx2_update,
+       .final          =       sha1_avx2_final,
+       .finup          =       sha1_avx2_finup,
+       .descsize       =       sizeof(struct sha1_state),
+       .base           =       {
+               .cra_name       =       "sha1",
+               .cra_driver_name =      "sha1-avx2",
+               .cra_priority   =       170,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA1_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
+};
+
+static int register_sha1_avx2(void)
+{
+       if (avx2_usable())
+               return crypto_register_shash(&sha1_avx2_alg);
+       return 0;
+}
+
+static void unregister_sha1_avx2(void)
+{
+       if (avx2_usable())
+               crypto_unregister_shash(&sha1_avx2_alg);
+}
+
+#else
+static inline int register_sha1_avx2(void) { return 0; }
+static inline void unregister_sha1_avx2(void) { }
 #endif
+
+#ifdef CONFIG_AS_SHA1_NI
+asmlinkage void sha1_ni_transform(u32 *digest, const char *data,
+                                  unsigned int rounds);
+
+static int sha1_ni_update(struct shash_desc *desc, const u8 *data,
+                            unsigned int len)
+{
+       return sha1_update(desc, data, len,
+               (sha1_transform_fn *) sha1_ni_transform);
+}
+
+static int sha1_ni_finup(struct shash_desc *desc, const u8 *data,
+                             unsigned int len, u8 *out)
+{
+       return sha1_finup(desc, data, len, out,
+               (sha1_transform_fn *) sha1_ni_transform);
+}
+
+static int sha1_ni_final(struct shash_desc *desc, u8 *out)
+{
+       return sha1_ni_finup(desc, NULL, 0, out);
+}
+
+static struct shash_alg sha1_ni_alg = {
+       .digestsize     =       SHA1_DIGEST_SIZE,
+       .init           =       sha1_base_init,
+       .update         =       sha1_ni_update,
+       .final          =       sha1_ni_final,
+       .finup          =       sha1_ni_finup,
+       .descsize       =       sizeof(struct sha1_state),
+       .base           =       {
+               .cra_name       =       "sha1",
+               .cra_driver_name =      "sha1-ni",
+               .cra_priority   =       250,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA1_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
+};
+
+static int register_sha1_ni(void)
+{
+       if (boot_cpu_has(X86_FEATURE_SHA_NI))
+               return crypto_register_shash(&sha1_ni_alg);
+       return 0;
+}
+
+static void unregister_sha1_ni(void)
+{
+       if (boot_cpu_has(X86_FEATURE_SHA_NI))
+               crypto_unregister_shash(&sha1_ni_alg);
+}
+
+#else
+static inline int register_sha1_ni(void) { return 0; }
+static inline void unregister_sha1_ni(void) { }
 #endif
 
 static int __init sha1_ssse3_mod_init(void)
 {
-       char *algo_name;
+       if (register_sha1_ssse3())
+               goto fail;
 
-       /* test for SSSE3 first */
-       if (cpu_has_ssse3) {
-               sha1_transform_asm = sha1_transform_ssse3;
-               algo_name = "SSSE3";
+       if (register_sha1_avx()) {
+               unregister_sha1_ssse3();
+               goto fail;
        }
 
-#ifdef CONFIG_AS_AVX
-       /* allow AVX to override SSSE3, it's a little faster */
-       if (avx_usable()) {
-               sha1_transform_asm = sha1_transform_avx;
-               algo_name = "AVX";
-#ifdef CONFIG_AS_AVX2
-               /* allow AVX2 to override AVX, it's a little faster */
-               if (avx2_usable()) {
-                       sha1_transform_asm = sha1_apply_transform_avx2;
-                       algo_name = "AVX2";
-               }
-#endif
+       if (register_sha1_avx2()) {
+               unregister_sha1_avx();
+               unregister_sha1_ssse3();
+               goto fail;
        }
-#endif
 
-       if (sha1_transform_asm) {
-               pr_info("Using %s optimized SHA-1 implementation\n", algo_name);
-               return crypto_register_shash(&alg);
+       if (register_sha1_ni()) {
+               unregister_sha1_avx2();
+               unregister_sha1_avx();
+               unregister_sha1_ssse3();
+               goto fail;
        }
-       pr_info("Neither AVX nor AVX2 nor SSSE3 is available/usable.\n");
 
+       return 0;
+fail:
        return -ENODEV;
 }
 
 static void __exit sha1_ssse3_mod_fini(void)
 {
-       crypto_unregister_shash(&alg);
+       unregister_sha1_ni();
+       unregister_sha1_avx2();
+       unregister_sha1_avx();
+       unregister_sha1_ssse3();
 }
 
 module_init(sha1_ssse3_mod_init);
diff --git a/arch/x86/crypto/sha256_ni_asm.S b/arch/x86/crypto/sha256_ni_asm.S
new file mode 100644 (file)
index 0000000..748cdf2
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Intel SHA Extensions optimized implementation of a SHA-256 update function
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * 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.
+ *
+ * Contact Information:
+ *     Sean Gulley <sean.m.gulley@intel.com>
+ *     Tim Chen <tim.c.chen@linux.intel.com>
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of Intel Corporation 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER 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/linkage.h>
+
+#define DIGEST_PTR     %rdi    /* 1st arg */
+#define DATA_PTR       %rsi    /* 2nd arg */
+#define NUM_BLKS       %rdx    /* 3rd arg */
+
+#define SHA256CONSTANTS        %rax
+
+#define MSG            %xmm0
+#define STATE0         %xmm1
+#define STATE1         %xmm2
+#define MSGTMP0                %xmm3
+#define MSGTMP1                %xmm4
+#define MSGTMP2                %xmm5
+#define MSGTMP3                %xmm6
+#define MSGTMP4                %xmm7
+
+#define SHUF_MASK      %xmm8
+
+#define ABEF_SAVE      %xmm9
+#define CDGH_SAVE      %xmm10
+
+/*
+ * Intel SHA Extensions optimized implementation of a SHA-256 update function
+ *
+ * The function takes a pointer to the current hash values, a pointer to the
+ * input data, and a number of 64 byte blocks to process.  Once all blocks have
+ * been processed, the digest pointer is  updated with the resulting hash value.
+ * The function only processes complete blocks, there is no functionality to
+ * store partial blocks.  All message padding and hash value initialization must
+ * be done outside the update function.
+ *
+ * The indented lines in the loop are instructions related to rounds processing.
+ * The non-indented lines are instructions related to the message schedule.
+ *
+ * void sha256_ni_transform(uint32_t *digest, const void *data,
+               uint32_t numBlocks);
+ * digest : pointer to digest
+ * data: pointer to input data
+ * numBlocks: Number of blocks to process
+ */
+
+.text
+.align 32
+ENTRY(sha256_ni_transform)
+
+       shl             $6, NUM_BLKS            /*  convert to bytes */
+       jz              .Ldone_hash
+       add             DATA_PTR, NUM_BLKS      /* pointer to end of data */
+
+       /*
+        * load initial hash values
+        * Need to reorder these appropriately
+        * DCBA, HGFE -> ABEF, CDGH
+        */
+       movdqu          0*16(DIGEST_PTR), STATE0
+       movdqu          1*16(DIGEST_PTR), STATE1
+
+       pshufd          $0xB1, STATE0,  STATE0          /* CDAB */
+       pshufd          $0x1B, STATE1,  STATE1          /* EFGH */
+       movdqa          STATE0, MSGTMP4
+       palignr         $8, STATE1,  STATE0             /* ABEF */
+       pblendw         $0xF0, MSGTMP4, STATE1          /* CDGH */
+
+       movdqa          PSHUFFLE_BYTE_FLIP_MASK(%rip), SHUF_MASK
+       lea             K256(%rip), SHA256CONSTANTS
+
+.Lloop0:
+       /* Save hash values for addition after rounds */
+       movdqa          STATE0, ABEF_SAVE
+       movdqa          STATE1, CDGH_SAVE
+
+       /* Rounds 0-3 */
+       movdqu          0*16(DATA_PTR), MSG
+       pshufb          SHUF_MASK, MSG
+       movdqa          MSG, MSGTMP0
+               paddd           0*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+
+       /* Rounds 4-7 */
+       movdqu          1*16(DATA_PTR), MSG
+       pshufb          SHUF_MASK, MSG
+       movdqa          MSG, MSGTMP1
+               paddd           1*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP1, MSGTMP0
+
+       /* Rounds 8-11 */
+       movdqu          2*16(DATA_PTR), MSG
+       pshufb          SHUF_MASK, MSG
+       movdqa          MSG, MSGTMP2
+               paddd           2*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP2, MSGTMP1
+
+       /* Rounds 12-15 */
+       movdqu          3*16(DATA_PTR), MSG
+       pshufb          SHUF_MASK, MSG
+       movdqa          MSG, MSGTMP3
+               paddd           3*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP3, MSGTMP4
+       palignr         $4, MSGTMP2, MSGTMP4
+       paddd           MSGTMP4, MSGTMP0
+       sha256msg2      MSGTMP3, MSGTMP0
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP3, MSGTMP2
+
+       /* Rounds 16-19 */
+       movdqa          MSGTMP0, MSG
+               paddd           4*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP0, MSGTMP4
+       palignr         $4, MSGTMP3, MSGTMP4
+       paddd           MSGTMP4, MSGTMP1
+       sha256msg2      MSGTMP0, MSGTMP1
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP0, MSGTMP3
+
+       /* Rounds 20-23 */
+       movdqa          MSGTMP1, MSG
+               paddd           5*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP1, MSGTMP4
+       palignr         $4, MSGTMP0, MSGTMP4
+       paddd           MSGTMP4, MSGTMP2
+       sha256msg2      MSGTMP1, MSGTMP2
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP1, MSGTMP0
+
+       /* Rounds 24-27 */
+       movdqa          MSGTMP2, MSG
+               paddd           6*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP2, MSGTMP4
+       palignr         $4, MSGTMP1, MSGTMP4
+       paddd           MSGTMP4, MSGTMP3
+       sha256msg2      MSGTMP2, MSGTMP3
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP2, MSGTMP1
+
+       /* Rounds 28-31 */
+       movdqa          MSGTMP3, MSG
+               paddd           7*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP3, MSGTMP4
+       palignr         $4, MSGTMP2, MSGTMP4
+       paddd           MSGTMP4, MSGTMP0
+       sha256msg2      MSGTMP3, MSGTMP0
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP3, MSGTMP2
+
+       /* Rounds 32-35 */
+       movdqa          MSGTMP0, MSG
+               paddd           8*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP0, MSGTMP4
+       palignr         $4, MSGTMP3, MSGTMP4
+       paddd           MSGTMP4, MSGTMP1
+       sha256msg2      MSGTMP0, MSGTMP1
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP0, MSGTMP3
+
+       /* Rounds 36-39 */
+       movdqa          MSGTMP1, MSG
+               paddd           9*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP1, MSGTMP4
+       palignr         $4, MSGTMP0, MSGTMP4
+       paddd           MSGTMP4, MSGTMP2
+       sha256msg2      MSGTMP1, MSGTMP2
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP1, MSGTMP0
+
+       /* Rounds 40-43 */
+       movdqa          MSGTMP2, MSG
+               paddd           10*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP2, MSGTMP4
+       palignr         $4, MSGTMP1, MSGTMP4
+       paddd           MSGTMP4, MSGTMP3
+       sha256msg2      MSGTMP2, MSGTMP3
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP2, MSGTMP1
+
+       /* Rounds 44-47 */
+       movdqa          MSGTMP3, MSG
+               paddd           11*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP3, MSGTMP4
+       palignr         $4, MSGTMP2, MSGTMP4
+       paddd           MSGTMP4, MSGTMP0
+       sha256msg2      MSGTMP3, MSGTMP0
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP3, MSGTMP2
+
+       /* Rounds 48-51 */
+       movdqa          MSGTMP0, MSG
+               paddd           12*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP0, MSGTMP4
+       palignr         $4, MSGTMP3, MSGTMP4
+       paddd           MSGTMP4, MSGTMP1
+       sha256msg2      MSGTMP0, MSGTMP1
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+       sha256msg1      MSGTMP0, MSGTMP3
+
+       /* Rounds 52-55 */
+       movdqa          MSGTMP1, MSG
+               paddd           13*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP1, MSGTMP4
+       palignr         $4, MSGTMP0, MSGTMP4
+       paddd           MSGTMP4, MSGTMP2
+       sha256msg2      MSGTMP1, MSGTMP2
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+
+       /* Rounds 56-59 */
+       movdqa          MSGTMP2, MSG
+               paddd           14*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+       movdqa          MSGTMP2, MSGTMP4
+       palignr         $4, MSGTMP1, MSGTMP4
+       paddd           MSGTMP4, MSGTMP3
+       sha256msg2      MSGTMP2, MSGTMP3
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+
+       /* Rounds 60-63 */
+       movdqa          MSGTMP3, MSG
+               paddd           15*16(SHA256CONSTANTS), MSG
+               sha256rnds2     STATE0, STATE1
+               pshufd          $0x0E, MSG, MSG
+               sha256rnds2     STATE1, STATE0
+
+       /* Add current hash values with previously saved */
+       paddd           ABEF_SAVE, STATE0
+       paddd           CDGH_SAVE, STATE1
+
+       /* Increment data pointer and loop if more to process */
+       add             $64, DATA_PTR
+       cmp             NUM_BLKS, DATA_PTR
+       jne             .Lloop0
+
+       /* Write hash values back in the correct order */
+       pshufd          $0x1B, STATE0,  STATE0          /* FEBA */
+       pshufd          $0xB1, STATE1,  STATE1          /* DCHG */
+       movdqa          STATE0, MSGTMP4
+       pblendw         $0xF0, STATE1,  STATE0          /* DCBA */
+       palignr         $8, MSGTMP4, STATE1             /* HGFE */
+
+       movdqu          STATE0, 0*16(DIGEST_PTR)
+       movdqu          STATE1, 1*16(DIGEST_PTR)
+
+.Ldone_hash:
+
+       ret
+ENDPROC(sha256_ni_transform)
+
+.data
+.align 64
+K256:
+       .long   0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+       .long   0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+       .long   0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+       .long   0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+       .long   0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+       .long   0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+       .long   0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+       .long   0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+       .long   0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+       .long   0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+       .long   0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+       .long   0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+       .long   0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+       .long   0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+       .long   0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+       .long   0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+
+PSHUFFLE_BYTE_FLIP_MASK:
+       .octa 0x0c0d0e0f08090a0b0405060700010203
index f8097fc0d1d1b56eb6d48389ac2778145897cfb2..5f4d6086dc5913be7d680882ab94d2286ad3e230 100644 (file)
 
 asmlinkage void sha256_transform_ssse3(u32 *digest, const char *data,
                                       u64 rounds);
-#ifdef CONFIG_AS_AVX
-asmlinkage void sha256_transform_avx(u32 *digest, const char *data,
-                                    u64 rounds);
-#endif
-#ifdef CONFIG_AS_AVX2
-asmlinkage void sha256_transform_rorx(u32 *digest, const char *data,
-                                     u64 rounds);
-#endif
-
-static void (*sha256_transform_asm)(u32 *, const char *, u64);
+typedef void (sha256_transform_fn)(u32 *digest, const char *data, u64 rounds);
 
-static int sha256_ssse3_update(struct shash_desc *desc, const u8 *data,
-                            unsigned int len)
+static int sha256_update(struct shash_desc *desc, const u8 *data,
+                        unsigned int len, sha256_transform_fn *sha256_xform)
 {
        struct sha256_state *sctx = shash_desc_ctx(desc);
 
@@ -67,14 +58,14 @@ static int sha256_ssse3_update(struct shash_desc *desc, const u8 *data,
 
        kernel_fpu_begin();
        sha256_base_do_update(desc, data, len,
-                             (sha256_block_fn *)sha256_transform_asm);
+                             (sha256_block_fn *)sha256_xform);
        kernel_fpu_end();
 
        return 0;
 }
 
-static int sha256_ssse3_finup(struct shash_desc *desc, const u8 *data,
-                             unsigned int len, u8 *out)
+static int sha256_finup(struct shash_desc *desc, const u8 *data,
+             unsigned int len, u8 *out, sha256_transform_fn *sha256_xform)
 {
        if (!irq_fpu_usable())
                return crypto_sha256_finup(desc, data, len, out);
@@ -82,20 +73,32 @@ static int sha256_ssse3_finup(struct shash_desc *desc, const u8 *data,
        kernel_fpu_begin();
        if (len)
                sha256_base_do_update(desc, data, len,
-                                     (sha256_block_fn *)sha256_transform_asm);
-       sha256_base_do_finalize(desc, (sha256_block_fn *)sha256_transform_asm);
+                                     (sha256_block_fn *)sha256_xform);
+       sha256_base_do_finalize(desc, (sha256_block_fn *)sha256_xform);
        kernel_fpu_end();
 
        return sha256_base_finish(desc, out);
 }
 
+static int sha256_ssse3_update(struct shash_desc *desc, const u8 *data,
+                        unsigned int len)
+{
+       return sha256_update(desc, data, len, sha256_transform_ssse3);
+}
+
+static int sha256_ssse3_finup(struct shash_desc *desc, const u8 *data,
+             unsigned int len, u8 *out)
+{
+       return sha256_finup(desc, data, len, out, sha256_transform_ssse3);
+}
+
 /* Add padding and return the message digest. */
 static int sha256_ssse3_final(struct shash_desc *desc, u8 *out)
 {
        return sha256_ssse3_finup(desc, NULL, 0, out);
 }
 
-static struct shash_alg algs[] = { {
+static struct shash_alg sha256_ssse3_algs[] = { {
        .digestsize     =       SHA256_DIGEST_SIZE,
        .init           =       sha256_base_init,
        .update         =       sha256_ssse3_update,
@@ -127,10 +130,77 @@ static struct shash_alg algs[] = { {
        }
 } };
 
+static int register_sha256_ssse3(void)
+{
+       if (boot_cpu_has(X86_FEATURE_SSSE3))
+               return crypto_register_shashes(sha256_ssse3_algs,
+                               ARRAY_SIZE(sha256_ssse3_algs));
+       return 0;
+}
+
+static void unregister_sha256_ssse3(void)
+{
+       if (boot_cpu_has(X86_FEATURE_SSSE3))
+               crypto_unregister_shashes(sha256_ssse3_algs,
+                               ARRAY_SIZE(sha256_ssse3_algs));
+}
+
 #ifdef CONFIG_AS_AVX
-static bool __init avx_usable(void)
+asmlinkage void sha256_transform_avx(u32 *digest, const char *data,
+                                    u64 rounds);
+
+static int sha256_avx_update(struct shash_desc *desc, const u8 *data,
+                        unsigned int len)
+{
+       return sha256_update(desc, data, len, sha256_transform_avx);
+}
+
+static int sha256_avx_finup(struct shash_desc *desc, const u8 *data,
+                     unsigned int len, u8 *out)
+{
+       return sha256_finup(desc, data, len, out, sha256_transform_avx);
+}
+
+static int sha256_avx_final(struct shash_desc *desc, u8 *out)
+{
+       return sha256_avx_finup(desc, NULL, 0, out);
+}
+
+static struct shash_alg sha256_avx_algs[] = { {
+       .digestsize     =       SHA256_DIGEST_SIZE,
+       .init           =       sha256_base_init,
+       .update         =       sha256_avx_update,
+       .final          =       sha256_avx_final,
+       .finup          =       sha256_avx_finup,
+       .descsize       =       sizeof(struct sha256_state),
+       .base           =       {
+               .cra_name       =       "sha256",
+               .cra_driver_name =      "sha256-avx",
+               .cra_priority   =       160,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA256_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
+}, {
+       .digestsize     =       SHA224_DIGEST_SIZE,
+       .init           =       sha224_base_init,
+       .update         =       sha256_avx_update,
+       .final          =       sha256_avx_final,
+       .finup          =       sha256_avx_finup,
+       .descsize       =       sizeof(struct sha256_state),
+       .base           =       {
+               .cra_name       =       "sha224",
+               .cra_driver_name =      "sha224-avx",
+               .cra_priority   =       160,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA224_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
+} };
+
+static bool avx_usable(void)
 {
-       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, NULL)) {
+       if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL)) {
                if (cpu_has_avx)
                        pr_info("AVX detected but unusable.\n");
                return false;
@@ -138,47 +208,216 @@ static bool __init avx_usable(void)
 
        return true;
 }
-#endif
 
-static int __init sha256_ssse3_mod_init(void)
+static int register_sha256_avx(void)
 {
-       /* test for SSSE3 first */
-       if (cpu_has_ssse3)
-               sha256_transform_asm = sha256_transform_ssse3;
+       if (avx_usable())
+               return crypto_register_shashes(sha256_avx_algs,
+                               ARRAY_SIZE(sha256_avx_algs));
+       return 0;
+}
 
-#ifdef CONFIG_AS_AVX
-       /* allow AVX to override SSSE3, it's a little faster */
-       if (avx_usable()) {
-#ifdef CONFIG_AS_AVX2
-               if (boot_cpu_has(X86_FEATURE_AVX2) && boot_cpu_has(X86_FEATURE_BMI2))
-                       sha256_transform_asm = sha256_transform_rorx;
-               else
+static void unregister_sha256_avx(void)
+{
+       if (avx_usable())
+               crypto_unregister_shashes(sha256_avx_algs,
+                               ARRAY_SIZE(sha256_avx_algs));
+}
+
+#else
+static inline int register_sha256_avx(void) { return 0; }
+static inline void unregister_sha256_avx(void) { }
 #endif
-                       sha256_transform_asm = sha256_transform_avx;
+
+#if defined(CONFIG_AS_AVX2) && defined(CONFIG_AS_AVX)
+asmlinkage void sha256_transform_rorx(u32 *digest, const char *data,
+                                     u64 rounds);
+
+static int sha256_avx2_update(struct shash_desc *desc, const u8 *data,
+                        unsigned int len)
+{
+       return sha256_update(desc, data, len, sha256_transform_rorx);
+}
+
+static int sha256_avx2_finup(struct shash_desc *desc, const u8 *data,
+                     unsigned int len, u8 *out)
+{
+       return sha256_finup(desc, data, len, out, sha256_transform_rorx);
+}
+
+static int sha256_avx2_final(struct shash_desc *desc, u8 *out)
+{
+       return sha256_avx2_finup(desc, NULL, 0, out);
+}
+
+static struct shash_alg sha256_avx2_algs[] = { {
+       .digestsize     =       SHA256_DIGEST_SIZE,
+       .init           =       sha256_base_init,
+       .update         =       sha256_avx2_update,
+       .final          =       sha256_avx2_final,
+       .finup          =       sha256_avx2_finup,
+       .descsize       =       sizeof(struct sha256_state),
+       .base           =       {
+               .cra_name       =       "sha256",
+               .cra_driver_name =      "sha256-avx2",
+               .cra_priority   =       170,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA256_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
        }
-#endif
+}, {
+       .digestsize     =       SHA224_DIGEST_SIZE,
+       .init           =       sha224_base_init,
+       .update         =       sha256_avx2_update,
+       .final          =       sha256_avx2_final,
+       .finup          =       sha256_avx2_finup,
+       .descsize       =       sizeof(struct sha256_state),
+       .base           =       {
+               .cra_name       =       "sha224",
+               .cra_driver_name =      "sha224-avx2",
+               .cra_priority   =       170,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA224_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
+} };
 
-       if (sha256_transform_asm) {
-#ifdef CONFIG_AS_AVX
-               if (sha256_transform_asm == sha256_transform_avx)
-                       pr_info("Using AVX optimized SHA-256 implementation\n");
-#ifdef CONFIG_AS_AVX2
-               else if (sha256_transform_asm == sha256_transform_rorx)
-                       pr_info("Using AVX2 optimized SHA-256 implementation\n");
+static bool avx2_usable(void)
+{
+       if (avx_usable() && boot_cpu_has(X86_FEATURE_AVX2) &&
+                   boot_cpu_has(X86_FEATURE_BMI2))
+               return true;
+
+       return false;
+}
+
+static int register_sha256_avx2(void)
+{
+       if (avx2_usable())
+               return crypto_register_shashes(sha256_avx2_algs,
+                               ARRAY_SIZE(sha256_avx2_algs));
+       return 0;
+}
+
+static void unregister_sha256_avx2(void)
+{
+       if (avx2_usable())
+               crypto_unregister_shashes(sha256_avx2_algs,
+                               ARRAY_SIZE(sha256_avx2_algs));
+}
+
+#else
+static inline int register_sha256_avx2(void) { return 0; }
+static inline void unregister_sha256_avx2(void) { }
 #endif
-               else
+
+#ifdef CONFIG_AS_SHA256_NI
+asmlinkage void sha256_ni_transform(u32 *digest, const char *data,
+                                  u64 rounds); /*unsigned int rounds);*/
+
+static int sha256_ni_update(struct shash_desc *desc, const u8 *data,
+                        unsigned int len)
+{
+       return sha256_update(desc, data, len, sha256_ni_transform);
+}
+
+static int sha256_ni_finup(struct shash_desc *desc, const u8 *data,
+                     unsigned int len, u8 *out)
+{
+       return sha256_finup(desc, data, len, out, sha256_ni_transform);
+}
+
+static int sha256_ni_final(struct shash_desc *desc, u8 *out)
+{
+       return sha256_ni_finup(desc, NULL, 0, out);
+}
+
+static struct shash_alg sha256_ni_algs[] = { {
+       .digestsize     =       SHA256_DIGEST_SIZE,
+       .init           =       sha256_base_init,
+       .update         =       sha256_ni_update,
+       .final          =       sha256_ni_final,
+       .finup          =       sha256_ni_finup,
+       .descsize       =       sizeof(struct sha256_state),
+       .base           =       {
+               .cra_name       =       "sha256",
+               .cra_driver_name =      "sha256-ni",
+               .cra_priority   =       250,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA256_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
+}, {
+       .digestsize     =       SHA224_DIGEST_SIZE,
+       .init           =       sha224_base_init,
+       .update         =       sha256_ni_update,
+       .final          =       sha256_ni_final,
+       .finup          =       sha256_ni_finup,
+       .descsize       =       sizeof(struct sha256_state),
+       .base           =       {
+               .cra_name       =       "sha224",
+               .cra_driver_name =      "sha224-ni",
+               .cra_priority   =       250,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA224_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
+} };
+
+static int register_sha256_ni(void)
+{
+       if (boot_cpu_has(X86_FEATURE_SHA_NI))
+               return crypto_register_shashes(sha256_ni_algs,
+                               ARRAY_SIZE(sha256_ni_algs));
+       return 0;
+}
+
+static void unregister_sha256_ni(void)
+{
+       if (boot_cpu_has(X86_FEATURE_SHA_NI))
+               crypto_unregister_shashes(sha256_ni_algs,
+                               ARRAY_SIZE(sha256_ni_algs));
+}
+
+#else
+static inline int register_sha256_ni(void) { return 0; }
+static inline void unregister_sha256_ni(void) { }
 #endif
-                       pr_info("Using SSSE3 optimized SHA-256 implementation\n");
-               return crypto_register_shashes(algs, ARRAY_SIZE(algs));
+
+static int __init sha256_ssse3_mod_init(void)
+{
+       if (register_sha256_ssse3())
+               goto fail;
+
+       if (register_sha256_avx()) {
+               unregister_sha256_ssse3();
+               goto fail;
        }
-       pr_info("Neither AVX nor SSSE3 is available/usable.\n");
 
+       if (register_sha256_avx2()) {
+               unregister_sha256_avx();
+               unregister_sha256_ssse3();
+               goto fail;
+       }
+
+       if (register_sha256_ni()) {
+               unregister_sha256_avx2();
+               unregister_sha256_avx();
+               unregister_sha256_ssse3();
+               goto fail;
+       }
+
+       return 0;
+fail:
        return -ENODEV;
 }
 
 static void __exit sha256_ssse3_mod_fini(void)
 {
-       crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
+       unregister_sha256_ni();
+       unregister_sha256_avx2();
+       unregister_sha256_avx();
+       unregister_sha256_ssse3();
 }
 
 module_init(sha256_ssse3_mod_init);
index 2edad7b81870154055e86694142a66de7fbc48ff..34e5083d6f36540e967dc755384012ca35afd714 100644 (file)
 
 asmlinkage void sha512_transform_ssse3(u64 *digest, const char *data,
                                       u64 rounds);
-#ifdef CONFIG_AS_AVX
-asmlinkage void sha512_transform_avx(u64 *digest, const char *data,
-                                    u64 rounds);
-#endif
-#ifdef CONFIG_AS_AVX2
-asmlinkage void sha512_transform_rorx(u64 *digest, const char *data,
-                                     u64 rounds);
-#endif
 
-static void (*sha512_transform_asm)(u64 *, const char *, u64);
+typedef void (sha512_transform_fn)(u64 *digest, const char *data, u64 rounds);
 
-static int sha512_ssse3_update(struct shash_desc *desc, const u8 *data,
-                              unsigned int len)
+static int sha512_update(struct shash_desc *desc, const u8 *data,
+                      unsigned int len, sha512_transform_fn *sha512_xform)
 {
        struct sha512_state *sctx = shash_desc_ctx(desc);
 
@@ -66,14 +58,14 @@ static int sha512_ssse3_update(struct shash_desc *desc, const u8 *data,
 
        kernel_fpu_begin();
        sha512_base_do_update(desc, data, len,
-                             (sha512_block_fn *)sha512_transform_asm);
+                             (sha512_block_fn *)sha512_xform);
        kernel_fpu_end();
 
        return 0;
 }
 
-static int sha512_ssse3_finup(struct shash_desc *desc, const u8 *data,
-                             unsigned int len, u8 *out)
+static int sha512_finup(struct shash_desc *desc, const u8 *data,
+             unsigned int len, u8 *out, sha512_transform_fn *sha512_xform)
 {
        if (!irq_fpu_usable())
                return crypto_sha512_finup(desc, data, len, out);
@@ -81,20 +73,32 @@ static int sha512_ssse3_finup(struct shash_desc *desc, const u8 *data,
        kernel_fpu_begin();
        if (len)
                sha512_base_do_update(desc, data, len,
-                                     (sha512_block_fn *)sha512_transform_asm);
-       sha512_base_do_finalize(desc, (sha512_block_fn *)sha512_transform_asm);
+                                     (sha512_block_fn *)sha512_xform);
+       sha512_base_do_finalize(desc, (sha512_block_fn *)sha512_xform);
        kernel_fpu_end();
 
        return sha512_base_finish(desc, out);
 }
 
+static int sha512_ssse3_update(struct shash_desc *desc, const u8 *data,
+                      unsigned int len)
+{
+       return sha512_update(desc, data, len, sha512_transform_ssse3);
+}
+
+static int sha512_ssse3_finup(struct shash_desc *desc, const u8 *data,
+             unsigned int len, u8 *out)
+{
+       return sha512_finup(desc, data, len, out, sha512_transform_ssse3);
+}
+
 /* Add padding and return the message digest. */
 static int sha512_ssse3_final(struct shash_desc *desc, u8 *out)
 {
        return sha512_ssse3_finup(desc, NULL, 0, out);
 }
 
-static struct shash_alg algs[] = { {
+static struct shash_alg sha512_ssse3_algs[] = { {
        .digestsize     =       SHA512_DIGEST_SIZE,
        .init           =       sha512_base_init,
        .update         =       sha512_ssse3_update,
@@ -126,10 +130,27 @@ static struct shash_alg algs[] = { {
        }
 } };
 
+static int register_sha512_ssse3(void)
+{
+       if (boot_cpu_has(X86_FEATURE_SSSE3))
+               return crypto_register_shashes(sha512_ssse3_algs,
+                       ARRAY_SIZE(sha512_ssse3_algs));
+       return 0;
+}
+
+static void unregister_sha512_ssse3(void)
+{
+       if (boot_cpu_has(X86_FEATURE_SSSE3))
+               crypto_unregister_shashes(sha512_ssse3_algs,
+                       ARRAY_SIZE(sha512_ssse3_algs));
+}
+
 #ifdef CONFIG_AS_AVX
-static bool __init avx_usable(void)
+asmlinkage void sha512_transform_avx(u64 *digest, const char *data,
+                                    u64 rounds);
+static bool avx_usable(void)
 {
-       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, NULL)) {
+       if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL)) {
                if (cpu_has_avx)
                        pr_info("AVX detected but unusable.\n");
                return false;
@@ -137,47 +158,185 @@ static bool __init avx_usable(void)
 
        return true;
 }
-#endif
 
-static int __init sha512_ssse3_mod_init(void)
+static int sha512_avx_update(struct shash_desc *desc, const u8 *data,
+                      unsigned int len)
 {
-       /* test for SSSE3 first */
-       if (cpu_has_ssse3)
-               sha512_transform_asm = sha512_transform_ssse3;
+       return sha512_update(desc, data, len, sha512_transform_avx);
+}
 
-#ifdef CONFIG_AS_AVX
-       /* allow AVX to override SSSE3, it's a little faster */
-       if (avx_usable()) {
-#ifdef CONFIG_AS_AVX2
-               if (boot_cpu_has(X86_FEATURE_AVX2))
-                       sha512_transform_asm = sha512_transform_rorx;
-               else
-#endif
-                       sha512_transform_asm = sha512_transform_avx;
+static int sha512_avx_finup(struct shash_desc *desc, const u8 *data,
+             unsigned int len, u8 *out)
+{
+       return sha512_finup(desc, data, len, out, sha512_transform_avx);
+}
+
+/* Add padding and return the message digest. */
+static int sha512_avx_final(struct shash_desc *desc, u8 *out)
+{
+       return sha512_avx_finup(desc, NULL, 0, out);
+}
+
+static struct shash_alg sha512_avx_algs[] = { {
+       .digestsize     =       SHA512_DIGEST_SIZE,
+       .init           =       sha512_base_init,
+       .update         =       sha512_avx_update,
+       .final          =       sha512_avx_final,
+       .finup          =       sha512_avx_finup,
+       .descsize       =       sizeof(struct sha512_state),
+       .base           =       {
+               .cra_name       =       "sha512",
+               .cra_driver_name =      "sha512-avx",
+               .cra_priority   =       160,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA512_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
        }
-#endif
+},  {
+       .digestsize     =       SHA384_DIGEST_SIZE,
+       .init           =       sha384_base_init,
+       .update         =       sha512_avx_update,
+       .final          =       sha512_avx_final,
+       .finup          =       sha512_avx_finup,
+       .descsize       =       sizeof(struct sha512_state),
+       .base           =       {
+               .cra_name       =       "sha384",
+               .cra_driver_name =      "sha384-avx",
+               .cra_priority   =       160,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA384_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
+} };
 
-       if (sha512_transform_asm) {
-#ifdef CONFIG_AS_AVX
-               if (sha512_transform_asm == sha512_transform_avx)
-                       pr_info("Using AVX optimized SHA-512 implementation\n");
-#ifdef CONFIG_AS_AVX2
-               else if (sha512_transform_asm == sha512_transform_rorx)
-                       pr_info("Using AVX2 optimized SHA-512 implementation\n");
+static int register_sha512_avx(void)
+{
+       if (avx_usable())
+               return crypto_register_shashes(sha512_avx_algs,
+                       ARRAY_SIZE(sha512_avx_algs));
+       return 0;
+}
+
+static void unregister_sha512_avx(void)
+{
+       if (avx_usable())
+               crypto_unregister_shashes(sha512_avx_algs,
+                       ARRAY_SIZE(sha512_avx_algs));
+}
+#else
+static inline int register_sha512_avx(void) { return 0; }
+static inline void unregister_sha512_avx(void) { }
 #endif
-               else
+
+#if defined(CONFIG_AS_AVX2) && defined(CONFIG_AS_AVX)
+asmlinkage void sha512_transform_rorx(u64 *digest, const char *data,
+                                     u64 rounds);
+
+static int sha512_avx2_update(struct shash_desc *desc, const u8 *data,
+                      unsigned int len)
+{
+       return sha512_update(desc, data, len, sha512_transform_rorx);
+}
+
+static int sha512_avx2_finup(struct shash_desc *desc, const u8 *data,
+             unsigned int len, u8 *out)
+{
+       return sha512_finup(desc, data, len, out, sha512_transform_rorx);
+}
+
+/* Add padding and return the message digest. */
+static int sha512_avx2_final(struct shash_desc *desc, u8 *out)
+{
+       return sha512_avx2_finup(desc, NULL, 0, out);
+}
+
+static struct shash_alg sha512_avx2_algs[] = { {
+       .digestsize     =       SHA512_DIGEST_SIZE,
+       .init           =       sha512_base_init,
+       .update         =       sha512_avx2_update,
+       .final          =       sha512_avx2_final,
+       .finup          =       sha512_avx2_finup,
+       .descsize       =       sizeof(struct sha512_state),
+       .base           =       {
+               .cra_name       =       "sha512",
+               .cra_driver_name =      "sha512-avx2",
+               .cra_priority   =       170,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA512_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
+},  {
+       .digestsize     =       SHA384_DIGEST_SIZE,
+       .init           =       sha384_base_init,
+       .update         =       sha512_avx2_update,
+       .final          =       sha512_avx2_final,
+       .finup          =       sha512_avx2_finup,
+       .descsize       =       sizeof(struct sha512_state),
+       .base           =       {
+               .cra_name       =       "sha384",
+               .cra_driver_name =      "sha384-avx2",
+               .cra_priority   =       170,
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA384_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
+} };
+
+static bool avx2_usable(void)
+{
+       if (avx_usable() && boot_cpu_has(X86_FEATURE_AVX2) &&
+                   boot_cpu_has(X86_FEATURE_BMI2))
+               return true;
+
+       return false;
+}
+
+static int register_sha512_avx2(void)
+{
+       if (avx2_usable())
+               return crypto_register_shashes(sha512_avx2_algs,
+                       ARRAY_SIZE(sha512_avx2_algs));
+       return 0;
+}
+
+static void unregister_sha512_avx2(void)
+{
+       if (avx2_usable())
+               crypto_unregister_shashes(sha512_avx2_algs,
+                       ARRAY_SIZE(sha512_avx2_algs));
+}
+#else
+static inline int register_sha512_avx2(void) { return 0; }
+static inline void unregister_sha512_avx2(void) { }
 #endif
-                       pr_info("Using SSSE3 optimized SHA-512 implementation\n");
-               return crypto_register_shashes(algs, ARRAY_SIZE(algs));
+
+static int __init sha512_ssse3_mod_init(void)
+{
+
+       if (register_sha512_ssse3())
+               goto fail;
+
+       if (register_sha512_avx()) {
+               unregister_sha512_ssse3();
+               goto fail;
        }
-       pr_info("Neither AVX nor SSSE3 is available/usable.\n");
 
+       if (register_sha512_avx2()) {
+               unregister_sha512_avx();
+               unregister_sha512_ssse3();
+               goto fail;
+       }
+
+       return 0;
+fail:
        return -ENODEV;
 }
 
 static void __exit sha512_ssse3_mod_fini(void)
 {
-       crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
+       unregister_sha512_avx2();
+       unregister_sha512_avx();
+       unregister_sha512_ssse3();
 }
 
 module_init(sha512_ssse3_mod_init);
index c2bd0ce718eee505272249386dffef3cc417dad8..b7a3904b953c6146f543a164e6456c1893102a92 100644 (file)
@@ -558,7 +558,7 @@ static int __init twofish_init(void)
 {
        const char *feature_name;
 
-       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+       if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, &feature_name)) {
                pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
index 80dcc9261ca31a41b9db930cfb66a9f07b5340d4..a89fdbc1f0beb7e7198c7a625767a2cfe32ca9e3 100644 (file)
 
 #include <asm/desc.h>
 #include <asm/traps.h>
+#include <asm/vdso.h>
+#include <asm/uaccess.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/syscalls.h>
 
+static struct thread_info *pt_regs_to_thread_info(struct pt_regs *regs)
+{
+       unsigned long top_of_stack =
+               (unsigned long)(regs + 1) + TOP_OF_KERNEL_STACK_PADDING;
+       return (struct thread_info *)(top_of_stack - THREAD_SIZE);
+}
+
 #ifdef CONFIG_CONTEXT_TRACKING
 /* Called on entry from user mode with IRQs off. */
 __visible void enter_from_user_mode(void)
@@ -66,13 +75,14 @@ static void do_audit_syscall_entry(struct pt_regs *regs, u32 arch)
  */
 unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch)
 {
+       struct thread_info *ti = pt_regs_to_thread_info(regs);
        unsigned long ret = 0;
        u32 work;
 
-       BUG_ON(regs != task_pt_regs(current));
+       if (IS_ENABLED(CONFIG_DEBUG_ENTRY))
+               BUG_ON(regs != task_pt_regs(current));
 
-       work = ACCESS_ONCE(current_thread_info()->flags) &
-               _TIF_WORK_SYSCALL_ENTRY;
+       work = ACCESS_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY;
 
 #ifdef CONFIG_CONTEXT_TRACKING
        /*
@@ -154,11 +164,12 @@ unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch)
 long syscall_trace_enter_phase2(struct pt_regs *regs, u32 arch,
                                unsigned long phase1_result)
 {
+       struct thread_info *ti = pt_regs_to_thread_info(regs);
        long ret = 0;
-       u32 work = ACCESS_ONCE(current_thread_info()->flags) &
-               _TIF_WORK_SYSCALL_ENTRY;
+       u32 work = ACCESS_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY;
 
-       BUG_ON(regs != task_pt_regs(current));
+       if (IS_ENABLED(CONFIG_DEBUG_ENTRY))
+               BUG_ON(regs != task_pt_regs(current));
 
        /*
         * If we stepped into a sysenter/syscall insn, it trapped in
@@ -207,19 +218,12 @@ long syscall_trace_enter(struct pt_regs *regs)
                return syscall_trace_enter_phase2(regs, arch, phase1_result);
 }
 
-static struct thread_info *pt_regs_to_thread_info(struct pt_regs *regs)
-{
-       unsigned long top_of_stack =
-               (unsigned long)(regs + 1) + TOP_OF_KERNEL_STACK_PADDING;
-       return (struct thread_info *)(top_of_stack - THREAD_SIZE);
-}
+#define EXIT_TO_USERMODE_LOOP_FLAGS                            \
+       (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE |   \
+        _TIF_NEED_RESCHED | _TIF_USER_RETURN_NOTIFY)
 
-/* Called with IRQs disabled. */
-__visible void prepare_exit_to_usermode(struct pt_regs *regs)
+static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags)
 {
-       if (WARN_ON(!irqs_disabled()))
-               local_irq_disable();
-
        /*
         * In order to return to user mode, we need to have IRQs off with
         * none of _TIF_SIGPENDING, _TIF_NOTIFY_RESUME, _TIF_USER_RETURN_NOTIFY,
@@ -229,14 +233,6 @@ __visible void prepare_exit_to_usermode(struct pt_regs *regs)
         * work to clear some of the flags can sleep.
         */
        while (true) {
-               u32 cached_flags =
-                       READ_ONCE(pt_regs_to_thread_info(regs)->flags);
-
-               if (!(cached_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME |
-                                     _TIF_UPROBE | _TIF_NEED_RESCHED |
-                                     _TIF_USER_RETURN_NOTIFY)))
-                       break;
-
                /* We have work to do. */
                local_irq_enable();
 
@@ -260,50 +256,81 @@ __visible void prepare_exit_to_usermode(struct pt_regs *regs)
 
                /* Disable IRQs and retry */
                local_irq_disable();
+
+               cached_flags = READ_ONCE(pt_regs_to_thread_info(regs)->flags);
+
+               if (!(cached_flags & EXIT_TO_USERMODE_LOOP_FLAGS))
+                       break;
+
        }
+}
+
+/* Called with IRQs disabled. */
+__visible inline void prepare_exit_to_usermode(struct pt_regs *regs)
+{
+       u32 cached_flags;
+
+       if (IS_ENABLED(CONFIG_PROVE_LOCKING) && WARN_ON(!irqs_disabled()))
+               local_irq_disable();
+
+       lockdep_sys_exit();
+
+       cached_flags =
+               READ_ONCE(pt_regs_to_thread_info(regs)->flags);
+
+       if (unlikely(cached_flags & EXIT_TO_USERMODE_LOOP_FLAGS))
+               exit_to_usermode_loop(regs, cached_flags);
 
        user_enter();
 }
 
+#define SYSCALL_EXIT_WORK_FLAGS                                \
+       (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT |      \
+        _TIF_SINGLESTEP | _TIF_SYSCALL_TRACEPOINT)
+
+static void syscall_slow_exit_work(struct pt_regs *regs, u32 cached_flags)
+{
+       bool step;
+
+       audit_syscall_exit(regs);
+
+       if (cached_flags & _TIF_SYSCALL_TRACEPOINT)
+               trace_sys_exit(regs, regs->ax);
+
+       /*
+        * If TIF_SYSCALL_EMU is set, we only get here because of
+        * TIF_SINGLESTEP (i.e. this is PTRACE_SYSEMU_SINGLESTEP).
+        * We already reported this syscall instruction in
+        * syscall_trace_enter().
+        */
+       step = unlikely(
+               (cached_flags & (_TIF_SINGLESTEP | _TIF_SYSCALL_EMU))
+               == _TIF_SINGLESTEP);
+       if (step || cached_flags & _TIF_SYSCALL_TRACE)
+               tracehook_report_syscall_exit(regs, step);
+}
+
 /*
  * Called with IRQs on and fully valid regs.  Returns with IRQs off in a
  * state such that we can immediately switch to user mode.
  */
-__visible void syscall_return_slowpath(struct pt_regs *regs)
+__visible inline void syscall_return_slowpath(struct pt_regs *regs)
 {
        struct thread_info *ti = pt_regs_to_thread_info(regs);
        u32 cached_flags = READ_ONCE(ti->flags);
-       bool step;
 
        CT_WARN_ON(ct_state() != CONTEXT_KERNEL);
 
-       if (WARN(irqs_disabled(), "syscall %ld left IRQs disabled",
-                regs->orig_ax))
+       if (IS_ENABLED(CONFIG_PROVE_LOCKING) &&
+           WARN(irqs_disabled(), "syscall %ld left IRQs disabled", regs->orig_ax))
                local_irq_enable();
 
        /*
         * First do one-time work.  If these work items are enabled, we
         * want to run them exactly once per syscall exit with IRQs on.
         */
-       if (cached_flags & (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT |
-                           _TIF_SINGLESTEP | _TIF_SYSCALL_TRACEPOINT)) {
-               audit_syscall_exit(regs);
-
-               if (cached_flags & _TIF_SYSCALL_TRACEPOINT)
-                       trace_sys_exit(regs, regs->ax);
-
-               /*
-                * If TIF_SYSCALL_EMU is set, we only get here because of
-                * TIF_SINGLESTEP (i.e. this is PTRACE_SYSEMU_SINGLESTEP).
-                * We already reported this syscall instruction in
-                * syscall_trace_enter().
-                */
-               step = unlikely(
-                       (cached_flags & (_TIF_SINGLESTEP | _TIF_SYSCALL_EMU))
-                       == _TIF_SINGLESTEP);
-               if (step || cached_flags & _TIF_SYSCALL_TRACE)
-                       tracehook_report_syscall_exit(regs, step);
-       }
+       if (unlikely(cached_flags & SYSCALL_EXIT_WORK_FLAGS))
+               syscall_slow_exit_work(regs, cached_flags);
 
 #ifdef CONFIG_COMPAT
        /*
@@ -316,3 +343,144 @@ __visible void syscall_return_slowpath(struct pt_regs *regs)
        local_irq_disable();
        prepare_exit_to_usermode(regs);
 }
+
+#if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
+/*
+ * Does a 32-bit syscall.  Called with IRQs on and does all entry and
+ * exit work and returns with IRQs off.  This function is extremely hot
+ * in workloads that use it, and it's usually called from
+ * do_fast_syscall_32, so forcibly inline it to improve performance.
+ */
+#ifdef CONFIG_X86_32
+/* 32-bit kernels use a trap gate for INT80, and the asm code calls here. */
+__visible
+#else
+/* 64-bit kernels use do_syscall_32_irqs_off() instead. */
+static
+#endif
+__always_inline void do_syscall_32_irqs_on(struct pt_regs *regs)
+{
+       struct thread_info *ti = pt_regs_to_thread_info(regs);
+       unsigned int nr = (unsigned int)regs->orig_ax;
+
+#ifdef CONFIG_IA32_EMULATION
+       ti->status |= TS_COMPAT;
+#endif
+
+       if (READ_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY) {
+               /*
+                * Subtlety here: if ptrace pokes something larger than
+                * 2^32-1 into orig_ax, this truncates it.  This may or
+                * may not be necessary, but it matches the old asm
+                * behavior.
+                */
+               nr = syscall_trace_enter(regs);
+       }
+
+       if (likely(nr < IA32_NR_syscalls)) {
+               /*
+                * It's possible that a 32-bit syscall implementation
+                * takes a 64-bit parameter but nonetheless assumes that
+                * the high bits are zero.  Make sure we zero-extend all
+                * of the args.
+                */
+               regs->ax = ia32_sys_call_table[nr](
+                       (unsigned int)regs->bx, (unsigned int)regs->cx,
+                       (unsigned int)regs->dx, (unsigned int)regs->si,
+                       (unsigned int)regs->di, (unsigned int)regs->bp);
+       }
+
+       syscall_return_slowpath(regs);
+}
+
+#ifdef CONFIG_X86_64
+/* Handles INT80 on 64-bit kernels */
+__visible void do_syscall_32_irqs_off(struct pt_regs *regs)
+{
+       local_irq_enable();
+       do_syscall_32_irqs_on(regs);
+}
+#endif
+
+/* Returns 0 to return using IRET or 1 to return using SYSEXIT/SYSRETL. */
+__visible long do_fast_syscall_32(struct pt_regs *regs)
+{
+       /*
+        * Called using the internal vDSO SYSENTER/SYSCALL32 calling
+        * convention.  Adjust regs so it looks like we entered using int80.
+        */
+
+       unsigned long landing_pad = (unsigned long)current->mm->context.vdso +
+               vdso_image_32.sym_int80_landing_pad;
+
+       /*
+        * SYSENTER loses EIP, and even SYSCALL32 needs us to skip forward
+        * so that 'regs->ip -= 2' lands back on an int $0x80 instruction.
+        * Fix it up.
+        */
+       regs->ip = landing_pad;
+
+       /*
+        * Fetch ECX from where the vDSO stashed it.
+        *
+        * WARNING: We are in CONTEXT_USER and RCU isn't paying attention!
+        */
+       local_irq_enable();
+       if (
+#ifdef CONFIG_X86_64
+               /*
+                * Micro-optimization: the pointer we're following is explicitly
+                * 32 bits, so it can't be out of range.
+                */
+               __get_user(*(u32 *)&regs->cx,
+                           (u32 __user __force *)(unsigned long)(u32)regs->sp)
+#else
+               get_user(*(u32 *)&regs->cx,
+                        (u32 __user __force *)(unsigned long)(u32)regs->sp)
+#endif
+               ) {
+
+               /* User code screwed up. */
+               local_irq_disable();
+               regs->ax = -EFAULT;
+#ifdef CONFIG_CONTEXT_TRACKING
+               enter_from_user_mode();
+#endif
+               prepare_exit_to_usermode(regs);
+               return 0;       /* Keep it simple: use IRET. */
+       }
+
+       /* Now this is just like a normal syscall. */
+       do_syscall_32_irqs_on(regs);
+
+#ifdef CONFIG_X86_64
+       /*
+        * Opportunistic SYSRETL: if possible, try to return using SYSRETL.
+        * SYSRETL is available on all 64-bit CPUs, so we don't need to
+        * bother with SYSEXIT.
+        *
+        * Unlike 64-bit opportunistic SYSRET, we can't check that CX == IP,
+        * because the ECX fixup above will ensure that this is essentially
+        * never the case.
+        */
+       return regs->cs == __USER32_CS && regs->ss == __USER_DS &&
+               regs->ip == landing_pad &&
+               (regs->flags & (X86_EFLAGS_RF | X86_EFLAGS_TF)) == 0;
+#else
+       /*
+        * Opportunistic SYSEXIT: if possible, try to return using SYSEXIT.
+        *
+        * Unlike 64-bit opportunistic SYSRET, we can't check that CX == IP,
+        * because the ECX fixup above will ensure that this is essentially
+        * never the case.
+        *
+        * We don't allow syscalls at all from VM86 mode, but we still
+        * need to check VM, because we might be returning from sys_vm86.
+        */
+       return static_cpu_has(X86_FEATURE_SEP) &&
+               regs->cs == __USER_CS && regs->ss == __USER_DS &&
+               regs->ip == landing_pad &&
+               (regs->flags & (X86_EFLAGS_RF | X86_EFLAGS_TF | X86_EFLAGS_VM)) == 0;
+#endif
+}
+#endif
index b2909bf8cf7029b17f6457edcd86b6e04395e94b..3eb572ed3d7ad438d8dfd1627b5b4121314c9f67 100644 (file)
@@ -3,7 +3,7 @@
  *
  * entry_32.S contains the system-call and low-level fault and trap handling routines.
  *
- * Stack layout in 'syscall_exit':
+ * Stack layout while running C code:
  *     ptrace needs to have all registers on the stack.
  *     If the order here is changed, it needs to be
  *     updated in fork.c:copy_process(), signal.c:do_signal(),
 
 #endif /* CONFIG_X86_32_LAZY_GS */
 
-.macro SAVE_ALL
+.macro SAVE_ALL pt_regs_ax=%eax
        cld
        PUSH_GS
        pushl   %fs
        pushl   %es
        pushl   %ds
-       pushl   %eax
+       pushl   \pt_regs_ax
        pushl   %ebp
        pushl   %edi
        pushl   %esi
@@ -211,7 +211,11 @@ ENTRY(ret_from_fork)
        popl    %eax
        pushl   $0x0202                         # Reset kernel eflags
        popfl
-       jmp     syscall_exit
+
+       /* When we fork, we trace the syscall return in the child, too. */
+       movl    %esp, %eax
+       call    syscall_return_slowpath
+       jmp     restore_all
 END(ret_from_fork)
 
 ENTRY(ret_from_kernel_thread)
@@ -224,7 +228,15 @@ ENTRY(ret_from_kernel_thread)
        movl    PT_EBP(%esp), %eax
        call    *PT_EBX(%esp)
        movl    $0, PT_EAX(%esp)
-       jmp     syscall_exit
+
+       /*
+        * Kernel threads return to userspace as if returning from a syscall.
+        * We should check whether anything actually uses this path and, if so,
+        * consider switching it over to ret_from_fork.
+        */
+       movl    %esp, %eax
+       call    syscall_return_slowpath
+       jmp     restore_all
 ENDPROC(ret_from_kernel_thread)
 
 /*
@@ -255,7 +267,6 @@ ret_from_intr:
        jb      resume_kernel                   # not returning to v8086 or userspace
 
 ENTRY(resume_userspace)
-       LOCKDEP_SYS_EXIT
        DISABLE_INTERRUPTS(CLBR_ANY)
        TRACE_IRQS_OFF
        movl    %esp, %eax
@@ -276,76 +287,47 @@ need_resched:
 END(resume_kernel)
 #endif
 
-/*
- * SYSENTER_RETURN points to after the SYSENTER instruction
- * in the vsyscall page.  See vsyscall-sysentry.S, which defines
- * the symbol.
- */
-
        # SYSENTER  call handler stub
 ENTRY(entry_SYSENTER_32)
        movl    TSS_sysenter_sp0(%esp), %esp
 sysenter_past_esp:
+       pushl   $__USER_DS              /* pt_regs->ss */
+       pushl   %ecx                    /* pt_regs->cx */
+       pushfl                          /* pt_regs->flags (except IF = 0) */
+       orl     $X86_EFLAGS_IF, (%esp)  /* Fix IF */
+       pushl   $__USER_CS              /* pt_regs->cs */
+       pushl   $0                      /* pt_regs->ip = 0 (placeholder) */
+       pushl   %eax                    /* pt_regs->orig_ax */
+       SAVE_ALL pt_regs_ax=$-ENOSYS    /* save rest */
+
        /*
-        * Interrupts are disabled here, but we can't trace it until
-        * enough kernel state to call TRACE_IRQS_OFF can be called - but
-        * we immediately enable interrupts at that point anyway.
-        */
-       pushl   $__USER_DS
-       pushl   %ebp
-       pushfl
-       orl     $X86_EFLAGS_IF, (%esp)
-       pushl   $__USER_CS
-       /*
-        * Push current_thread_info()->sysenter_return to the stack.
-        * A tiny bit of offset fixup is necessary: TI_sysenter_return
-        * is relative to thread_info, which is at the bottom of the
-        * kernel stack page.  4*4 means the 4 words pushed above;
-        * TOP_OF_KERNEL_STACK_PADDING takes us to the top of the stack;
-        * and THREAD_SIZE takes us to the bottom.
+        * User mode is traced as though IRQs are on, and SYSENTER
+        * turned them off.
         */
-       pushl   ((TI_sysenter_return) - THREAD_SIZE + TOP_OF_KERNEL_STACK_PADDING + 4*4)(%esp)
-
-       pushl   %eax
-       SAVE_ALL
-       ENABLE_INTERRUPTS(CLBR_NONE)
-
-/*
- * Load the potential sixth argument from user stack.
- * Careful about security.
- */
-       cmpl    $__PAGE_OFFSET-3, %ebp
-       jae     syscall_fault
-       ASM_STAC
-1:     movl    (%ebp), %ebp
-       ASM_CLAC
-       movl    %ebp, PT_EBP(%esp)
-       _ASM_EXTABLE(1b, syscall_fault)
+       TRACE_IRQS_OFF
 
-       GET_THREAD_INFO(%ebp)
+       movl    %esp, %eax
+       call    do_fast_syscall_32
+       testl   %eax, %eax
+       jz      .Lsyscall_32_done
 
-       testl   $_TIF_WORK_SYSCALL_ENTRY, TI_flags(%ebp)
-       jnz     syscall_trace_entry
-sysenter_do_call:
-       cmpl    $(NR_syscalls), %eax
-       jae     sysenter_badsys
-       call    *sys_call_table(, %eax, 4)
-sysenter_after_call:
-       movl    %eax, PT_EAX(%esp)
-       LOCKDEP_SYS_EXIT
-       DISABLE_INTERRUPTS(CLBR_ANY)
-       TRACE_IRQS_OFF
-       movl    TI_flags(%ebp), %ecx
-       testl   $_TIF_ALLWORK_MASK, %ecx
-       jnz     syscall_exit_work_irqs_off
-sysenter_exit:
-/* if something modifies registers it must also disable sysexit */
-       movl    PT_EIP(%esp), %edx
-       movl    PT_OLDESP(%esp), %ecx
-       xorl    %ebp, %ebp
-       TRACE_IRQS_ON
+/* Opportunistic SYSEXIT */
+       TRACE_IRQS_ON                   /* User mode traces as IRQs on. */
+       movl    PT_EIP(%esp), %edx      /* pt_regs->ip */
+       movl    PT_OLDESP(%esp), %ecx   /* pt_regs->sp */
 1:     mov     PT_FS(%esp), %fs
        PTGS_TO_GS
+       popl    %ebx                    /* pt_regs->bx */
+       addl    $2*4, %esp              /* skip pt_regs->cx and pt_regs->dx */
+       popl    %esi                    /* pt_regs->si */
+       popl    %edi                    /* pt_regs->di */
+       popl    %ebp                    /* pt_regs->bp */
+       popl    %eax                    /* pt_regs->ax */
+
+       /*
+        * Return back to the vDSO, which will pop ecx and edx.
+        * Don't bother with DS and ES (they already contain __USER_DS).
+        */
        ENABLE_INTERRUPTS_SYSEXIT
 
 .pushsection .fixup, "ax"
@@ -359,21 +341,18 @@ ENDPROC(entry_SYSENTER_32)
        # system call handler stub
 ENTRY(entry_INT80_32)
        ASM_CLAC
-       pushl   %eax                            # save orig_eax
-       SAVE_ALL
-       GET_THREAD_INFO(%ebp)
-                                               # system call tracing in operation / emulation
-       testl   $_TIF_WORK_SYSCALL_ENTRY, TI_flags(%ebp)
-       jnz     syscall_trace_entry
-       cmpl    $(NR_syscalls), %eax
-       jae     syscall_badsys
-syscall_call:
-       call    *sys_call_table(, %eax, 4)
-syscall_after_call:
-       movl    %eax, PT_EAX(%esp)              # store the return value
-syscall_exit:
-       LOCKDEP_SYS_EXIT
-       jmp     syscall_exit_work
+       pushl   %eax                    /* pt_regs->orig_ax */
+       SAVE_ALL pt_regs_ax=$-ENOSYS    /* save rest */
+
+       /*
+        * User mode is traced as though IRQs are on.  Unlike the 64-bit
+        * case, INT80 is a trap gate on 32-bit kernels, so interrupts
+        * are already on (unless user code is messing around with iopl).
+        */
+
+       movl    %esp, %eax
+       call    do_syscall_32_irqs_on
+.Lsyscall_32_done:
 
 restore_all:
        TRACE_IRQS_IRET
@@ -450,47 +429,6 @@ ldt_ss:
 #endif
 ENDPROC(entry_INT80_32)
 
-       # perform syscall exit tracing
-       ALIGN
-syscall_trace_entry:
-       movl    $-ENOSYS, PT_EAX(%esp)
-       movl    %esp, %eax
-       call    syscall_trace_enter
-       /* What it returned is what we'll actually use.  */
-       cmpl    $(NR_syscalls), %eax
-       jnae    syscall_call
-       jmp     syscall_exit
-END(syscall_trace_entry)
-
-       # perform syscall exit tracing
-       ALIGN
-syscall_exit_work_irqs_off:
-       TRACE_IRQS_ON
-       ENABLE_INTERRUPTS(CLBR_ANY)
-
-syscall_exit_work:
-       movl    %esp, %eax
-       call    syscall_return_slowpath
-       jmp     restore_all
-END(syscall_exit_work)
-
-syscall_fault:
-       ASM_CLAC
-       GET_THREAD_INFO(%ebp)
-       movl    $-EFAULT, PT_EAX(%esp)
-       jmp     resume_userspace
-END(syscall_fault)
-
-syscall_badsys:
-       movl    $-ENOSYS, %eax
-       jmp     syscall_after_call
-END(syscall_badsys)
-
-sysenter_badsys:
-       movl    $-ENOSYS, %eax
-       jmp     sysenter_after_call
-END(sysenter_badsys)
-
 .macro FIXUP_ESPFIX_STACK
 /*
  * Switch back for ESPFIX stack to the normal zerobased stack
index 055a01de7c8da6e052cfdebe0be8447b14933c87..53616ca0324440d6cba482c3855433b5a63b4ef9 100644 (file)
@@ -391,20 +391,16 @@ GLOBAL(stub_execveat)
        jmp     return_from_execve
 END(stub_execveat)
 
-#if defined(CONFIG_X86_X32_ABI) || defined(CONFIG_IA32_EMULATION)
+#if defined(CONFIG_X86_X32_ABI)
        .align  8
 GLOBAL(stub_x32_execve)
-GLOBAL(stub32_execve)
        call    compat_sys_execve
        jmp     return_from_execve
-END(stub32_execve)
 END(stub_x32_execve)
        .align  8
 GLOBAL(stub_x32_execveat)
-GLOBAL(stub32_execveat)
        call    compat_sys_execveat
        jmp     return_from_execve
-END(stub32_execveat)
 END(stub_x32_execveat)
 #endif
 
@@ -557,7 +553,6 @@ ret_from_intr:
        jz      retint_kernel
 
        /* Interrupt came from user space */
-       LOCKDEP_SYS_EXIT_IRQ
 GLOBAL(retint_user)
        mov     %rsp,%rdi
        call    prepare_exit_to_usermode
@@ -587,7 +582,7 @@ retint_kernel:
  * At this label, code paths which return to kernel and to user,
  * which come from interrupts/exception and from syscalls, merge.
  */
-restore_regs_and_iret:
+GLOBAL(restore_regs_and_iret)
        RESTORE_EXTRA_REGS
 restore_c_regs_and_iret:
        RESTORE_C_REGS
index a9360d40fb7fd5f518077cc2d9aaa63cfd4853aa..c3201830a85ee8dcddabb0cab864545d99d47fd3 100644 (file)
 #include <linux/linkage.h>
 #include <linux/err.h>
 
-/* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this.  */
-#include <linux/elf-em.h>
-#define AUDIT_ARCH_I386                (EM_386|__AUDIT_ARCH_LE)
-#define __AUDIT_ARCH_LE                0x40000000
-
-#ifndef CONFIG_AUDITSYSCALL
-# define sysexit_audit         ia32_ret_from_sys_call_irqs_off
-# define sysretl_audit         ia32_ret_from_sys_call_irqs_off
-#endif
-
        .section .entry.text, "ax"
 
 #ifdef CONFIG_PARAVIRT
@@ -58,219 +48,87 @@ ENDPROC(native_usergs_sysret32)
  * with the int 0x80 path.
  */
 ENTRY(entry_SYSENTER_compat)
-       /*
-        * Interrupts are off on entry.
-        * We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON,
-        * it is too small to ever cause noticeable irq latency.
-        */
+       /* Interrupts are off on entry. */
        SWAPGS_UNSAFE_STACK
        movq    PER_CPU_VAR(cpu_current_top_of_stack), %rsp
-       ENABLE_INTERRUPTS(CLBR_NONE)
 
-       /* Zero-extending 32-bit regs, do not remove */
-       movl    %ebp, %ebp
+       /*
+        * User tracing code (ptrace or signal handlers) might assume that
+        * the saved RAX contains a 32-bit number when we're invoking a 32-bit
+        * syscall.  Just in case the high bits are nonzero, zero-extend
+        * the syscall number.  (This could almost certainly be deleted
+        * with no ill effects.)
+        */
        movl    %eax, %eax
 
-       movl    ASM_THREAD_INFO(TI_sysenter_return, %rsp, 0), %r10d
-
        /* Construct struct pt_regs on stack */
        pushq   $__USER32_DS            /* pt_regs->ss */
-       pushq   %rbp                    /* pt_regs->sp */
-       pushfq                          /* pt_regs->flags */
+       pushq   %rcx                    /* pt_regs->sp */
+
+       /*
+        * Push flags.  This is nasty.  First, interrupts are currently
+        * off, but we need pt_regs->flags to have IF set.  Second, even
+        * if TF was set when SYSENTER started, it's clear by now.  We fix
+        * that later using TIF_SINGLESTEP.
+        */
+       pushfq                          /* pt_regs->flags (except IF = 0) */
+       orl     $X86_EFLAGS_IF, (%rsp)  /* Fix saved flags */
+       ASM_CLAC                        /* Clear AC after saving FLAGS */
+
        pushq   $__USER32_CS            /* pt_regs->cs */
-       pushq   %r10                    /* pt_regs->ip = thread_info->sysenter_return */
+       xorq    %r8,%r8
+       pushq   %r8                     /* pt_regs->ip = 0 (placeholder) */
        pushq   %rax                    /* pt_regs->orig_ax */
        pushq   %rdi                    /* pt_regs->di */
        pushq   %rsi                    /* pt_regs->si */
        pushq   %rdx                    /* pt_regs->dx */
-       pushq   %rcx                    /* pt_regs->cx */
+       pushq   %rcx                    /* pt_regs->cx (will be overwritten) */
        pushq   $-ENOSYS                /* pt_regs->ax */
+       pushq   %r8                     /* pt_regs->r8  = 0 */
+       pushq   %r8                     /* pt_regs->r9  = 0 */
+       pushq   %r8                     /* pt_regs->r10 = 0 */
+       pushq   %r8                     /* pt_regs->r11 = 0 */
+       pushq   %rbx                    /* pt_regs->rbx */
+       pushq   %rbp                    /* pt_regs->rbp */
+       pushq   %r8                     /* pt_regs->r12 = 0 */
+       pushq   %r8                     /* pt_regs->r13 = 0 */
+       pushq   %r8                     /* pt_regs->r14 = 0 */
+       pushq   %r8                     /* pt_regs->r15 = 0 */
        cld
-       sub     $(10*8), %rsp /* pt_regs->r8-11, bp, bx, r12-15 not saved */
-
-       /*
-        * no need to do an access_ok check here because rbp has been
-        * 32-bit zero extended
-        */
-       ASM_STAC
-1:     movl    (%rbp), %ebp
-       _ASM_EXTABLE(1b, ia32_badarg)
-       ASM_CLAC
 
        /*
         * Sysenter doesn't filter flags, so we need to clear NT
         * ourselves.  To save a few cycles, we can check whether
         * NT was set instead of doing an unconditional popfq.
+        * This needs to happen before enabling interrupts so that
+        * we don't get preempted with NT set.
+        *
+        * NB.: sysenter_fix_flags is a label with the code under it moved
+        * out-of-line as an optimization: NT is unlikely to be set in the
+        * majority of the cases and instead of polluting the I$ unnecessarily,
+        * we're keeping that code behind a branch which will predict as
+        * not-taken and therefore its instructions won't be fetched.
         */
        testl   $X86_EFLAGS_NT, EFLAGS(%rsp)
        jnz     sysenter_fix_flags
 sysenter_flags_fixed:
 
-       orl     $TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
-       testl   $_TIF_WORK_SYSCALL_ENTRY, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz     sysenter_tracesys
-
-sysenter_do_call:
-       /* 32-bit syscall -> 64-bit C ABI argument conversion */
-       movl    %edi, %r8d              /* arg5 */
-       movl    %ebp, %r9d              /* arg6 */
-       xchg    %ecx, %esi              /* rsi:arg2, rcx:arg4 */
-       movl    %ebx, %edi              /* arg1 */
-       movl    %edx, %edx              /* arg3 (zero extension) */
-sysenter_dispatch:
-       cmpq    $(IA32_NR_syscalls-1), %rax
-       ja      1f
-       call    *ia32_sys_call_table(, %rax, 8)
-       movq    %rax, RAX(%rsp)
-1:
-       DISABLE_INTERRUPTS(CLBR_NONE)
-       TRACE_IRQS_OFF
-       testl   $_TIF_ALLWORK_MASK, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz     sysexit_audit
-sysexit_from_sys_call:
        /*
-        * NB: SYSEXIT is not obviously safe for 64-bit kernels -- an
-        * NMI between STI and SYSEXIT has poorly specified behavior,
-        * and and NMI followed by an IRQ with usergs is fatal.  So
-        * we just pretend we're using SYSEXIT but we really use
-        * SYSRETL instead.
-        *
-        * This code path is still called 'sysexit' because it pairs
-        * with 'sysenter' and it uses the SYSENTER calling convention.
+        * User mode is traced as though IRQs are on, and SYSENTER
+        * turned them off.
         */
-       andl    $~TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
-       movl    RIP(%rsp), %ecx         /* User %eip */
-       movq    RAX(%rsp), %rax
-       movl    RSI(%rsp), %esi
-       movl    RDI(%rsp), %edi
-       xorl    %edx, %edx              /* Do not leak kernel information */
-       xorq    %r8, %r8
-       xorq    %r9, %r9
-       xorq    %r10, %r10
-       movl    EFLAGS(%rsp), %r11d     /* User eflags */
-       TRACE_IRQS_ON
-
-       /*
-        * SYSRETL works even on Intel CPUs.  Use it in preference to SYSEXIT,
-        * since it avoids a dicey window with interrupts enabled.
-        */
-       movl    RSP(%rsp), %esp
-
-       /*
-        * USERGS_SYSRET32 does:
-        *  gsbase = user's gs base
-        *  eip = ecx
-        *  rflags = r11
-        *  cs = __USER32_CS
-        *  ss = __USER_DS
-        *
-        * The prologue set RIP(%rsp) to VDSO32_SYSENTER_RETURN, which does:
-        *
-        *  pop %ebp
-        *  pop %edx
-        *  pop %ecx
-        *
-        * Therefore, we invoke SYSRETL with EDX and R8-R10 zeroed to
-        * avoid info leaks.  R11 ends up with VDSO32_SYSENTER_RETURN's
-        * address (already known to user code), and R12-R15 are
-        * callee-saved and therefore don't contain any interesting
-        * kernel data.
-        */
-       USERGS_SYSRET32
-
-#ifdef CONFIG_AUDITSYSCALL
-       .macro auditsys_entry_common
-       /*
-        * At this point, registers hold syscall args in the 32-bit syscall ABI:
-        * EAX is syscall number, the 6 args are in EBX,ECX,EDX,ESI,EDI,EBP.
-        *
-        * We want to pass them to __audit_syscall_entry(), which is a 64-bit
-        * C function with 5 parameters, so shuffle them to match what
-        * the function expects: RDI,RSI,RDX,RCX,R8.
-        */
-       movl    %esi, %r8d              /* arg5 (R8 ) <= 4th syscall arg (ESI) */
-       xchg    %ecx, %edx              /* arg4 (RCX) <= 3rd syscall arg (EDX) */
-                                       /* arg3 (RDX) <= 2nd syscall arg (ECX) */
-       movl    %ebx, %esi              /* arg2 (RSI) <= 1st syscall arg (EBX) */
-       movl    %eax, %edi              /* arg1 (RDI) <= syscall number  (EAX) */
-       call    __audit_syscall_entry
-
-       /*
-        * We are going to jump back to the syscall dispatch code.
-        * Prepare syscall args as required by the 64-bit C ABI.
-        * Registers clobbered by __audit_syscall_entry() are
-        * loaded from pt_regs on stack:
-        */
-       movl    ORIG_RAX(%rsp), %eax    /* syscall number */
-       movl    %ebx, %edi              /* arg1 */
-       movl    RCX(%rsp), %esi         /* arg2 */
-       movl    RDX(%rsp), %edx         /* arg3 */
-       movl    RSI(%rsp), %ecx         /* arg4 */
-       movl    RDI(%rsp), %r8d         /* arg5 */
-       .endm
-
-       .macro auditsys_exit exit
-       TRACE_IRQS_ON
-       ENABLE_INTERRUPTS(CLBR_NONE)
-       testl   $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT), ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz     ia32_ret_from_sys_call
-       movl    %eax, %esi              /* second arg, syscall return value */
-       cmpl    $-MAX_ERRNO, %eax       /* is it an error ? */
-       jbe     1f
-       movslq  %eax, %rsi              /* if error sign extend to 64 bits */
-1:     setbe   %al                     /* 1 if error, 0 if not */
-       movzbl  %al, %edi               /* zero-extend that into %edi */
-       call    __audit_syscall_exit
-       movl    $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT), %edi
-       DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF
-       testl   %edi, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jz      \exit
-       xorl    %eax, %eax              /* Do not leak kernel information */
-       movq    %rax, R11(%rsp)
-       movq    %rax, R10(%rsp)
-       movq    %rax, R9(%rsp)
-       movq    %rax, R8(%rsp)
-       jmp     int_ret_from_sys_call_irqs_off
-       .endm
 
-sysenter_auditsys:
-       auditsys_entry_common
-       movl    %ebp, %r9d              /* reload 6th syscall arg */
-       jmp     sysenter_dispatch
-
-sysexit_audit:
-       auditsys_exit sysexit_from_sys_call
-#endif
+       movq    %rsp, %rdi
+       call    do_fast_syscall_32
+       testl   %eax, %eax
+       jz      .Lsyscall_32_done
+       jmp     sysret32_from_system_call
 
 sysenter_fix_flags:
-       pushq   $(X86_EFLAGS_IF|X86_EFLAGS_FIXED)
+       pushq   $X86_EFLAGS_FIXED
        popfq
        jmp     sysenter_flags_fixed
-
-sysenter_tracesys:
-#ifdef CONFIG_AUDITSYSCALL
-       testl   $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT), ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jz      sysenter_auditsys
-#endif
-       SAVE_EXTRA_REGS
-       xorl    %eax, %eax              /* Do not leak kernel information */
-       movq    %rax, R11(%rsp)
-       movq    %rax, R10(%rsp)
-       movq    %rax, R9(%rsp)
-       movq    %rax, R8(%rsp)
-       movq    %rsp, %rdi              /* &pt_regs -> arg1 */
-       call    syscall_trace_enter
-
-       /* Reload arg registers from stack. (see sysenter_tracesys) */
-       movl    RCX(%rsp), %ecx
-       movl    RDX(%rsp), %edx
-       movl    RSI(%rsp), %esi
-       movl    RDI(%rsp), %edi
-       movl    %eax, %eax              /* zero extension */
-
-       RESTORE_EXTRA_REGS
-       jmp     sysenter_do_call
 ENDPROC(entry_SYSENTER_compat)
 
 /*
@@ -298,21 +156,14 @@ ENDPROC(entry_SYSENTER_compat)
  * edi  arg5
  * esp  user stack
  * 0(%esp) arg6
- *
- * This is purely a fast path. For anything complicated we use the int 0x80
- * path below. We set up a complete hardware stack frame to share code
- * with the int 0x80 path.
  */
 ENTRY(entry_SYSCALL_compat)
-       /*
-        * Interrupts are off on entry.
-        * We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON,
-        * it is too small to ever cause noticeable irq latency.
-        */
+       /* Interrupts are off on entry. */
        SWAPGS_UNSAFE_STACK
+
+       /* Stash user ESP and switch to the kernel stack. */
        movl    %esp, %r8d
        movq    PER_CPU_VAR(cpu_current_top_of_stack), %rsp
-       ENABLE_INTERRUPTS(CLBR_NONE)
 
        /* Zero-extending 32-bit regs, do not remove */
        movl    %eax, %eax
@@ -327,162 +178,67 @@ ENTRY(entry_SYSCALL_compat)
        pushq   %rdi                    /* pt_regs->di */
        pushq   %rsi                    /* pt_regs->si */
        pushq   %rdx                    /* pt_regs->dx */
-       pushq   %rbp                    /* pt_regs->cx */
-       movl    %ebp, %ecx
+       pushq   %rcx                    /* pt_regs->cx (will be overwritten) */
        pushq   $-ENOSYS                /* pt_regs->ax */
-       sub     $(10*8), %rsp           /* pt_regs->r8-11, bp, bx, r12-15 not saved */
+       xorq    %r8,%r8
+       pushq   %r8                     /* pt_regs->r8  = 0 */
+       pushq   %r8                     /* pt_regs->r9  = 0 */
+       pushq   %r8                     /* pt_regs->r10 = 0 */
+       pushq   %r8                     /* pt_regs->r11 = 0 */
+       pushq   %rbx                    /* pt_regs->rbx */
+       pushq   %rbp                    /* pt_regs->rbp */
+       pushq   %r8                     /* pt_regs->r12 = 0 */
+       pushq   %r8                     /* pt_regs->r13 = 0 */
+       pushq   %r8                     /* pt_regs->r14 = 0 */
+       pushq   %r8                     /* pt_regs->r15 = 0 */
 
        /*
-        * No need to do an access_ok check here because r8 has been
-        * 32-bit zero extended:
+        * User mode is traced as though IRQs are on, and SYSENTER
+        * turned them off.
         */
-       ASM_STAC
-1:     movl    (%r8), %r9d
-       _ASM_EXTABLE(1b, ia32_badarg)
-       ASM_CLAC
-       orl     $TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
-       testl   $_TIF_WORK_SYSCALL_ENTRY, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz     cstar_tracesys
-
-cstar_do_call:
-       /* 32-bit syscall -> 64-bit C ABI argument conversion */
-       movl    %edi, %r8d              /* arg5 */
-       /* r9 already loaded */         /* arg6 */
-       xchg    %ecx, %esi              /* rsi:arg2, rcx:arg4 */
-       movl    %ebx, %edi              /* arg1 */
-       movl    %edx, %edx              /* arg3 (zero extension) */
-
-cstar_dispatch:
-       cmpq    $(IA32_NR_syscalls-1), %rax
-       ja      1f
-
-       call    *ia32_sys_call_table(, %rax, 8)
-       movq    %rax, RAX(%rsp)
-1:
-       DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF
-       testl   $_TIF_ALLWORK_MASK, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz     sysretl_audit
 
-sysretl_from_sys_call:
-       andl    $~TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
-       movl    RDX(%rsp), %edx
-       movl    RSI(%rsp), %esi
-       movl    RDI(%rsp), %edi
-       movl    RIP(%rsp), %ecx
-       movl    EFLAGS(%rsp), %r11d
-       movq    RAX(%rsp), %rax
-       xorq    %r10, %r10
-       xorq    %r9, %r9
-       xorq    %r8, %r8
-       TRACE_IRQS_ON
-       movl    RSP(%rsp), %esp
-       /*
-        * 64-bit->32-bit SYSRET restores eip from ecx,
-        * eflags from r11 (but RF and VM bits are forced to 0),
-        * cs and ss are loaded from MSRs.
-        * (Note: 32-bit->32-bit SYSRET is different: since r11
-        * does not exist, it merely sets eflags.IF=1).
+       movq    %rsp, %rdi
+       call    do_fast_syscall_32
+       testl   %eax, %eax
+       jz      .Lsyscall_32_done
+
+       /* Opportunistic SYSRET */
+sysret32_from_system_call:
+       TRACE_IRQS_ON                   /* User mode traces as IRQs on. */
+       movq    RBX(%rsp), %rbx         /* pt_regs->rbx */
+       movq    RBP(%rsp), %rbp         /* pt_regs->rbp */
+       movq    EFLAGS(%rsp), %r11      /* pt_regs->flags (in r11) */
+       movq    RIP(%rsp), %rcx         /* pt_regs->ip (in rcx) */
+       addq    $RAX, %rsp              /* Skip r8-r15 */
+       popq    %rax                    /* pt_regs->rax */
+       popq    %rdx                    /* Skip pt_regs->cx */
+       popq    %rdx                    /* pt_regs->dx */
+       popq    %rsi                    /* pt_regs->si */
+       popq    %rdi                    /* pt_regs->di */
+
+        /*
+         * USERGS_SYSRET32 does:
+         *  GSBASE = user's GS base
+         *  EIP = ECX
+         *  RFLAGS = R11
+         *  CS = __USER32_CS
+         *  SS = __USER_DS
+         *
+        * ECX will not match pt_regs->cx, but we're returning to a vDSO
+        * trampoline that will fix up RCX, so this is okay.
         *
-        * NB: On AMD CPUs with the X86_BUG_SYSRET_SS_ATTRS bug, the ss
-        * descriptor is not reinitialized.  This means that we must
-        * avoid SYSRET with SS == NULL, which could happen if we schedule,
-        * exit the kernel, and re-enter using an interrupt vector.  (All
-        * interrupt entries on x86_64 set SS to NULL.)  We prevent that
-        * from happening by reloading SS in __switch_to.
-        */
-       USERGS_SYSRET32
-
-#ifdef CONFIG_AUDITSYSCALL
-cstar_auditsys:
-       movl    %r9d, R9(%rsp)          /* register to be clobbered by call */
-       auditsys_entry_common
-       movl    R9(%rsp), %r9d          /* reload 6th syscall arg */
-       jmp     cstar_dispatch
-
-sysretl_audit:
-       auditsys_exit sysretl_from_sys_call
-#endif
-
-cstar_tracesys:
-#ifdef CONFIG_AUDITSYSCALL
-       testl   $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT), ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jz      cstar_auditsys
-#endif
-       xchgl   %r9d, %ebp
-       SAVE_EXTRA_REGS
-       xorl    %eax, %eax              /* Do not leak kernel information */
-       movq    %rax, R11(%rsp)
-       movq    %rax, R10(%rsp)
-       movq    %r9, R9(%rsp)
-       movq    %rax, R8(%rsp)
-       movq    %rsp, %rdi              /* &pt_regs -> arg1 */
-       call    syscall_trace_enter
-       movl    R9(%rsp), %r9d
-
-       /* Reload arg registers from stack. (see sysenter_tracesys) */
-       movl    RCX(%rsp), %ecx
-       movl    RDX(%rsp), %edx
-       movl    RSI(%rsp), %esi
-       movl    RDI(%rsp), %edi
-       movl    %eax, %eax              /* zero extension */
-
-       RESTORE_EXTRA_REGS
-       xchgl   %ebp, %r9d
-       jmp     cstar_do_call
+        * R12-R15 are callee-saved, so they contain whatever was in them
+        * when the system call started, which is already known to user
+        * code.  We zero R8-R10 to avoid info leaks.
+         */
+       xorq    %r8, %r8
+       xorq    %r9, %r9
+       xorq    %r10, %r10
+       movq    RSP-ORIG_RAX(%rsp), %rsp
+        USERGS_SYSRET32
 END(entry_SYSCALL_compat)
 
-ia32_badarg:
-       /*
-        * So far, we've entered kernel mode, set AC, turned on IRQs, and
-        * saved C regs except r8-r11.  We haven't done any of the other
-        * standard entry work, though.  We want to bail, but we shouldn't
-        * treat this as a syscall entry since we don't even know what the
-        * args are.  Instead, treat this as a non-syscall entry, finish
-        * the entry work, and immediately exit after setting AX = -EFAULT.
-        *
-        * We're really just being polite here.  Killing the task outright
-        * would be a reasonable action, too.  Given that the only valid
-        * way to have gotten here is through the vDSO, and we already know
-        * that the stack pointer is bad, the task isn't going to survive
-        * for long no matter what we do.
-        */
-
-       ASM_CLAC                        /* undo STAC */
-       movq    $-EFAULT, RAX(%rsp)     /* return -EFAULT if possible */
-
-       /* Fill in the rest of pt_regs */
-       xorl    %eax, %eax
-       movq    %rax, R11(%rsp)
-       movq    %rax, R10(%rsp)
-       movq    %rax, R9(%rsp)
-       movq    %rax, R8(%rsp)
-       SAVE_EXTRA_REGS
-
-       /* Turn IRQs back off. */
-       DISABLE_INTERRUPTS(CLBR_NONE)
-       TRACE_IRQS_OFF
-
-       /* Now finish entering normal kernel mode. */
-#ifdef CONFIG_CONTEXT_TRACKING
-       call enter_from_user_mode
-#endif
-
-       /* And exit again. */
-       jmp retint_user
-
-ia32_ret_from_sys_call_irqs_off:
-       TRACE_IRQS_ON
-       ENABLE_INTERRUPTS(CLBR_NONE)
-
-ia32_ret_from_sys_call:
-       xorl    %eax, %eax              /* Do not leak kernel information */
-       movq    %rax, R11(%rsp)
-       movq    %rax, R10(%rsp)
-       movq    %rax, R9(%rsp)
-       movq    %rax, R8(%rsp)
-       jmp     int_ret_from_sys_call
-
 /*
  * Emulated IA32 system calls via int 0x80.
  *
@@ -507,14 +263,17 @@ ia32_ret_from_sys_call:
 ENTRY(entry_INT80_compat)
        /*
         * Interrupts are off on entry.
-        * We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON,
-        * it is too small to ever cause noticeable irq latency.
         */
        PARAVIRT_ADJUST_EXCEPTION_FRAME
        SWAPGS
-       ENABLE_INTERRUPTS(CLBR_NONE)
 
-       /* Zero-extending 32-bit regs, do not remove */
+       /*
+        * User tracing code (ptrace or signal handlers) might assume that
+        * the saved RAX contains a 32-bit number when we're invoking a 32-bit
+        * syscall.  Just in case the high bits are nonzero, zero-extend
+        * the syscall number.  (This could almost certainly be deleted
+        * with no ill effects.)
+        */
        movl    %eax, %eax
 
        /* Construct struct pt_regs on stack (iret frame is already on stack) */
@@ -524,67 +283,37 @@ ENTRY(entry_INT80_compat)
        pushq   %rdx                    /* pt_regs->dx */
        pushq   %rcx                    /* pt_regs->cx */
        pushq   $-ENOSYS                /* pt_regs->ax */
-       pushq   $0                      /* pt_regs->r8 */
-       pushq   $0                      /* pt_regs->r9 */
-       pushq   $0                      /* pt_regs->r10 */
-       pushq   $0                      /* pt_regs->r11 */
+       xorq    %r8,%r8
+       pushq   %r8                     /* pt_regs->r8  = 0 */
+       pushq   %r8                     /* pt_regs->r9  = 0 */
+       pushq   %r8                     /* pt_regs->r10 = 0 */
+       pushq   %r8                     /* pt_regs->r11 = 0 */
+       pushq   %rbx                    /* pt_regs->rbx */
+       pushq   %rbp                    /* pt_regs->rbp */
+       pushq   %r12                    /* pt_regs->r12 */
+       pushq   %r13                    /* pt_regs->r13 */
+       pushq   %r14                    /* pt_regs->r14 */
+       pushq   %r15                    /* pt_regs->r15 */
        cld
-       sub     $(6*8), %rsp /* pt_regs->bp, bx, r12-15 not saved */
-
-       orl     $TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
-       testl   $_TIF_WORK_SYSCALL_ENTRY, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz     ia32_tracesys
-
-ia32_do_call:
-       /* 32-bit syscall -> 64-bit C ABI argument conversion */
-       movl    %edi, %r8d              /* arg5 */
-       movl    %ebp, %r9d              /* arg6 */
-       xchg    %ecx, %esi              /* rsi:arg2, rcx:arg4 */
-       movl    %ebx, %edi              /* arg1 */
-       movl    %edx, %edx              /* arg3 (zero extension) */
-       cmpq    $(IA32_NR_syscalls-1), %rax
-       ja      1f
 
-       call    *ia32_sys_call_table(, %rax, 8)
-       movq    %rax, RAX(%rsp)
-1:
-       jmp     int_ret_from_sys_call
-
-ia32_tracesys:
-       SAVE_EXTRA_REGS
-       movq    %rsp, %rdi                      /* &pt_regs -> arg1 */
-       call    syscall_trace_enter
        /*
-        * Reload arg registers from stack in case ptrace changed them.
-        * Don't reload %eax because syscall_trace_enter() returned
-        * the %rax value we should see.  But do truncate it to 32 bits.
-        * If it's -1 to make us punt the syscall, then (u32)-1 is still
-        * an appropriately invalid value.
+        * User mode is traced as though IRQs are on, and the interrupt
+        * gate turned them off.
         */
-       movl    RCX(%rsp), %ecx
-       movl    RDX(%rsp), %edx
-       movl    RSI(%rsp), %esi
-       movl    RDI(%rsp), %edi
-       movl    %eax, %eax              /* zero extension */
-       RESTORE_EXTRA_REGS
-       jmp     ia32_do_call
-END(entry_INT80_compat)
+       TRACE_IRQS_OFF
 
-       .macro PTREGSCALL label, func
-       ALIGN
-GLOBAL(\label)
-       leaq    \func(%rip), %rax
-       jmp     ia32_ptregs_common
-       .endm
+       movq    %rsp, %rdi
+       call    do_syscall_32_irqs_off
+.Lsyscall_32_done:
 
-       PTREGSCALL stub32_rt_sigreturn, sys32_rt_sigreturn
-       PTREGSCALL stub32_sigreturn,    sys32_sigreturn
-       PTREGSCALL stub32_fork,         sys_fork
-       PTREGSCALL stub32_vfork,        sys_vfork
+       /* Go back to user mode. */
+       TRACE_IRQS_ON
+       SWAPGS
+       jmp     restore_regs_and_iret
+END(entry_INT80_compat)
 
        ALIGN
 GLOBAL(stub32_clone)
-       leaq    sys_clone(%rip), %rax
        /*
         * The 32-bit clone ABI is: clone(..., int tls_val, int *child_tidptr).
         * The 64-bit clone ABI is: clone(..., int *child_tidptr, int tls_val).
@@ -593,12 +322,4 @@ GLOBAL(stub32_clone)
         * so we need to swap arguments here before calling it:
         */
        xchg    %r8, %rcx
-       jmp     ia32_ptregs_common
-
-       ALIGN
-ia32_ptregs_common:
-       SAVE_EXTRA_REGS 8
-       call    *%rax
-       RESTORE_EXTRA_REGS 8
-       ret
-END(ia32_ptregs_common)
+       jmp     sys_clone
index 8ea34f94e973c41322625743f92fe16bc0ed35fa..9a6649857106a0e0bc6bfe8ad0074562e394a5d1 100644 (file)
@@ -4,24 +4,21 @@
 #include <linux/sys.h>
 #include <linux/cache.h>
 #include <asm/asm-offsets.h>
+#include <asm/syscall.h>
 
 #ifdef CONFIG_IA32_EMULATION
 #define SYM(sym, compat) compat
 #else
 #define SYM(sym, compat) sym
-#define ia32_sys_call_table sys_call_table
-#define __NR_syscall_compat_max __NR_syscall_max
 #endif
 
-#define __SYSCALL_I386(nr, sym, compat) extern asmlinkage void SYM(sym, compat)(void) ;
+#define __SYSCALL_I386(nr, sym, compat) extern asmlinkage long SYM(sym, compat)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) ;
 #include <asm/syscalls_32.h>
 #undef __SYSCALL_I386
 
 #define __SYSCALL_I386(nr, sym, compat) [nr] = SYM(sym, compat),
 
-typedef asmlinkage void (*sys_call_ptr_t)(void);
-
-extern asmlinkage void sys_ni_syscall(void);
+extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
 
 __visible const sys_call_ptr_t ia32_sys_call_table[__NR_syscall_compat_max+1] = {
        /*
index 4ac730b37f0bf4de7ef67fd7f869d9f756e207ed..41283d22be7a92db23e46fb427593a2e878ee37d 100644 (file)
 # define __SYSCALL_X32(nr, sym, compat) /* nothing */
 #endif
 
-#define __SYSCALL_64(nr, sym, compat) extern asmlinkage void sym(void) ;
+#define __SYSCALL_64(nr, sym, compat) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) ;
 #include <asm/syscalls_64.h>
 #undef __SYSCALL_64
 
 #define __SYSCALL_64(nr, sym, compat) [nr] = sym,
 
-extern void sys_ni_syscall(void);
+extern long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
 
 asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
        /*
index 7663c455b9f650f67292e5a2c4664606fb1dbacb..caa2c712d1e70c5895d92cf856b91a02d9123083 100644 (file)
@@ -8,7 +8,7 @@
 #
 0      i386    restart_syscall         sys_restart_syscall
 1      i386    exit                    sys_exit
-2      i386    fork                    sys_fork                        stub32_fork
+2      i386    fork                    sys_fork                        sys_fork
 3      i386    read                    sys_read
 4      i386    write                   sys_write
 5      i386    open                    sys_open                        compat_sys_open
@@ -17,7 +17,7 @@
 8      i386    creat                   sys_creat
 9      i386    link                    sys_link
 10     i386    unlink                  sys_unlink
-11     i386    execve                  sys_execve                      stub32_execve
+11     i386    execve                  sys_execve                      compat_sys_execve
 12     i386    chdir                   sys_chdir
 13     i386    time                    sys_time                        compat_sys_time
 14     i386    mknod                   sys_mknod
 116    i386    sysinfo                 sys_sysinfo                     compat_sys_sysinfo
 117    i386    ipc                     sys_ipc                         compat_sys_ipc
 118    i386    fsync                   sys_fsync
-119    i386    sigreturn               sys_sigreturn                   stub32_sigreturn
+119    i386    sigreturn               sys_sigreturn                   sys32_sigreturn
 120    i386    clone                   sys_clone                       stub32_clone
 121    i386    setdomainname           sys_setdomainname
 122    i386    uname                   sys_newuname
 170    i386    setresgid               sys_setresgid16
 171    i386    getresgid               sys_getresgid16
 172    i386    prctl                   sys_prctl
-173    i386    rt_sigreturn            sys_rt_sigreturn                stub32_rt_sigreturn
+173    i386    rt_sigreturn            sys_rt_sigreturn                sys32_rt_sigreturn
 174    i386    rt_sigaction            sys_rt_sigaction                compat_sys_rt_sigaction
 175    i386    rt_sigprocmask          sys_rt_sigprocmask
 176    i386    rt_sigpending           sys_rt_sigpending               compat_sys_rt_sigpending
 187    i386    sendfile                sys_sendfile                    compat_sys_sendfile
 188    i386    getpmsg
 189    i386    putpmsg
-190    i386    vfork                   sys_vfork                       stub32_vfork
+190    i386    vfork                   sys_vfork                       sys_vfork
 191    i386    ugetrlimit              sys_getrlimit                   compat_sys_getrlimit
 192    i386    mmap2                   sys_mmap_pgoff
 193    i386    truncate64              sys_truncate64                  sys32_truncate64
 355    i386    getrandom               sys_getrandom
 356    i386    memfd_create            sys_memfd_create
 357    i386    bpf                     sys_bpf
-358    i386    execveat                sys_execveat                    stub32_execveat
+358    i386    execveat                sys_execveat                    compat_sys_execveat
 359    i386    socket                  sys_socket
 360    i386    socketpair              sys_socketpair
 361    i386    bind                    sys_bind
index a3d0767a6b294fb1218c305679589e8625f50a97..265c0ed6811800e3b03550cfded0c0cc743794f0 100644 (file)
@@ -19,9 +19,7 @@ obj-y                         += vma.o
 # vDSO images to build
 vdso_img-$(VDSO64-y)           += 64
 vdso_img-$(VDSOX32-y)          += x32
-vdso_img-$(VDSO32-y)           += 32-int80
-vdso_img-$(CONFIG_IA32_EMULATION)      += 32-syscall
-vdso_img-$(VDSO32-y)           += 32-sysenter
+vdso_img-$(VDSO32-y)           += 32
 
 obj-$(VDSO32-y)                        += vdso32-setup.o
 
@@ -69,7 +67,7 @@ $(obj)/vdso-image-%.c: $(obj)/vdso%.so.dbg $(obj)/vdso%.so $(obj)/vdso2c FORCE
 CFL := $(PROFILING) -mcmodel=small -fPIC -O2 -fasynchronous-unwind-tables -m64 \
        $(filter -g%,$(KBUILD_CFLAGS)) $(call cc-option, -fno-stack-protector) \
        -fno-omit-frame-pointer -foptimize-sibling-calls \
-       -DDISABLE_BRANCH_PROFILING
+       -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO
 
 $(vobjs): KBUILD_CFLAGS += $(CFL)
 
@@ -122,15 +120,6 @@ $(obj)/%.so: $(obj)/%.so.dbg
 $(obj)/vdsox32.so.dbg: $(src)/vdsox32.lds $(vobjx32s) FORCE
        $(call if_changed,vdso)
 
-#
-# Build multiple 32-bit vDSO images to choose from at boot time.
-#
-vdso32.so-$(VDSO32-y)          += int80
-vdso32.so-$(CONFIG_IA32_EMULATION)     += syscall
-vdso32.so-$(VDSO32-y)          += sysenter
-
-vdso32-images                  = $(vdso32.so-y:%=vdso32-%.so)
-
 CPPFLAGS_vdso32.lds = $(CPPFLAGS_vdso.lds)
 VDSO_LDFLAGS_vdso32.lds = -m32 -Wl,-m,elf_i386 -Wl,-soname=linux-gate.so.1
 
@@ -139,14 +128,12 @@ VDSO_LDFLAGS_vdso32.lds = -m32 -Wl,-m,elf_i386 -Wl,-soname=linux-gate.so.1
 override obj-dirs = $(dir $(obj)) $(obj)/vdso32/
 
 targets += vdso32/vdso32.lds
-targets += vdso32/note.o vdso32/vclock_gettime.o $(vdso32.so-y:%=vdso32/%.o)
+targets += vdso32/note.o vdso32/vclock_gettime.o vdso32/system_call.o
 targets += vdso32/vclock_gettime.o
 
-$(obj)/vdso32.o: $(vdso32-images:%=$(obj)/%)
-
-KBUILD_AFLAGS_32 := $(filter-out -m64,$(KBUILD_AFLAGS))
-$(vdso32-images:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_32)
-$(vdso32-images:%=$(obj)/%.dbg): asflags-$(CONFIG_X86_64) += -m32
+KBUILD_AFLAGS_32 := $(filter-out -m64,$(KBUILD_AFLAGS)) -DBUILD_VDSO
+$(obj)/vdso32.so.dbg: KBUILD_AFLAGS = $(KBUILD_AFLAGS_32)
+$(obj)/vdso32.so.dbg: asflags-$(CONFIG_X86_64) += -m32
 
 KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS))
 KBUILD_CFLAGS_32 := $(filter-out -mcmodel=kernel,$(KBUILD_CFLAGS_32))
@@ -157,13 +144,13 @@ KBUILD_CFLAGS_32 += $(call cc-option, -fno-stack-protector)
 KBUILD_CFLAGS_32 += $(call cc-option, -foptimize-sibling-calls)
 KBUILD_CFLAGS_32 += -fno-omit-frame-pointer
 KBUILD_CFLAGS_32 += -DDISABLE_BRANCH_PROFILING
-$(vdso32-images:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
+$(obj)/vdso32.so.dbg: KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
 
-$(vdso32-images:%=$(obj)/%.dbg): $(obj)/vdso32-%.so.dbg: FORCE \
-                                $(obj)/vdso32/vdso32.lds \
-                                $(obj)/vdso32/vclock_gettime.o \
-                                $(obj)/vdso32/note.o \
-                                $(obj)/vdso32/%.o
+$(obj)/vdso32.so.dbg: FORCE \
+                     $(obj)/vdso32/vdso32.lds \
+                     $(obj)/vdso32/vclock_gettime.o \
+                     $(obj)/vdso32/note.o \
+                     $(obj)/vdso32/system_call.o
        $(call if_changed,vdso)
 
 #
@@ -206,4 +193,4 @@ $(vdso_img_insttargets): install_%: $(obj)/%.dbg $(MODLIB)/vdso FORCE
 PHONY += vdso_install $(vdso_img_insttargets)
 vdso_install: $(vdso_img_insttargets) FORCE
 
-clean-files := vdso32-syscall* vdso32-sysenter* vdso32-int80* vdso64* vdso-image-*.c vdsox32.so*
+clean-files := vdso32.so vdso32.so.dbg vdso64* vdso-image-*.c vdsox32.so*
index 8627db24a7f6a25993bc3cb5a53157cab7c7df1a..785d9922b106317e9285dbcde43417417f4911fb 100644 (file)
@@ -98,10 +98,10 @@ struct vdso_sym required_syms[] = {
                "VDSO_FAKE_SECTION_TABLE_END", false
        },
        {"VDSO32_NOTE_MASK", true},
-       {"VDSO32_SYSENTER_RETURN", true},
        {"__kernel_vsyscall", true},
        {"__kernel_sigreturn", true},
        {"__kernel_rt_sigreturn", true},
+       {"int80_landing_pad", true},
 };
 
 __attribute__((format(printf, 1, 2))) __attribute__((noreturn))
index e904c270573bf58054d5fd83f2b9c77b0ee81347..08a317a9ae4b582974ec4af4842461bddb762121 100644 (file)
@@ -48,35 +48,9 @@ __setup("vdso32=", vdso32_setup);
 __setup_param("vdso=", vdso_setup, vdso32_setup, 0);
 #endif
 
-#ifdef CONFIG_X86_64
-
-#define        vdso32_sysenter()       (boot_cpu_has(X86_FEATURE_SYSENTER32))
-#define        vdso32_syscall()        (boot_cpu_has(X86_FEATURE_SYSCALL32))
-
-#else  /* CONFIG_X86_32 */
-
-#define vdso32_sysenter()      (boot_cpu_has(X86_FEATURE_SEP))
-#define vdso32_syscall()       (0)
-
-#endif /* CONFIG_X86_64 */
-
-#if defined(CONFIG_X86_32) || defined(CONFIG_COMPAT)
-const struct vdso_image *selected_vdso32;
-#endif
-
 int __init sysenter_setup(void)
 {
-#ifdef CONFIG_COMPAT
-       if (vdso32_syscall())
-               selected_vdso32 = &vdso_image_32_syscall;
-       else
-#endif
-       if (vdso32_sysenter())
-               selected_vdso32 = &vdso_image_32_sysenter;
-       else
-               selected_vdso32 = &vdso_image_32_int80;
-
-       init_vdso_image(selected_vdso32);
+       init_vdso_image(&vdso_image_32);
 
        return 0;
 }
diff --git a/arch/x86/entry/vdso/vdso32/int80.S b/arch/x86/entry/vdso/vdso32/int80.S
deleted file mode 100644 (file)
index b15b7c0..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Code for the vDSO.  This version uses the old int $0x80 method.
- *
- * First get the common code for the sigreturn entry points.
- * This must come first.
- */
-#include "sigreturn.S"
-
-       .text
-       .globl __kernel_vsyscall
-       .type __kernel_vsyscall,@function
-       ALIGN
-__kernel_vsyscall:
-.LSTART_vsyscall:
-       int $0x80
-       ret
-.LEND_vsyscall:
-       .size __kernel_vsyscall,.-.LSTART_vsyscall
-       .previous
-
-       .section .eh_frame,"a",@progbits
-.LSTARTFRAMEDLSI:
-       .long .LENDCIEDLSI-.LSTARTCIEDLSI
-.LSTARTCIEDLSI:
-       .long 0                 /* CIE ID */
-       .byte 1                 /* Version number */
-       .string "zR"            /* NUL-terminated augmentation string */
-       .uleb128 1              /* Code alignment factor */
-       .sleb128 -4             /* Data alignment factor */
-       .byte 8                 /* Return address register column */
-       .uleb128 1              /* Augmentation value length */
-       .byte 0x1b              /* DW_EH_PE_pcrel|DW_EH_PE_sdata4. */
-       .byte 0x0c              /* DW_CFA_def_cfa */
-       .uleb128 4
-       .uleb128 4
-       .byte 0x88              /* DW_CFA_offset, column 0x8 */
-       .uleb128 1
-       .align 4
-.LENDCIEDLSI:
-       .long .LENDFDEDLSI-.LSTARTFDEDLSI /* Length FDE */
-.LSTARTFDEDLSI:
-       .long .LSTARTFDEDLSI-.LSTARTFRAMEDLSI /* CIE pointer */
-       .long .LSTART_vsyscall-.        /* PC-relative start address */
-       .long .LEND_vsyscall-.LSTART_vsyscall
-       .uleb128 0
-       .align 4
-.LENDFDEDLSI:
-       .previous
-
-       /*
-        * Pad out the segment to match the size of the sysenter.S version.
-        */
-VDSO32_vsyscall_eh_frame_size = 0x40
-       .section .data,"aw",@progbits
-       .space VDSO32_vsyscall_eh_frame_size-(.LENDFDEDLSI-.LSTARTFRAMEDLSI), 0
-       .previous
diff --git a/arch/x86/entry/vdso/vdso32/syscall.S b/arch/x86/entry/vdso/vdso32/syscall.S
deleted file mode 100644 (file)
index 6b286bb..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Code for the vDSO.  This version uses the syscall instruction.
- *
- * First get the common code for the sigreturn entry points.
- * This must come first.
- */
-#define SYSCALL_ENTER_KERNEL   syscall
-#include "sigreturn.S"
-
-#include <asm/segment.h>
-
-       .text
-       .globl __kernel_vsyscall
-       .type __kernel_vsyscall,@function
-       ALIGN
-__kernel_vsyscall:
-.LSTART_vsyscall:
-       push    %ebp
-.Lpush_ebp:
-       movl    %ecx, %ebp
-       syscall
-       movl    %ebp, %ecx
-       popl    %ebp
-.Lpop_ebp:
-       ret
-.LEND_vsyscall:
-       .size __kernel_vsyscall,.-.LSTART_vsyscall
-
-       .section .eh_frame,"a",@progbits
-.LSTARTFRAME:
-       .long .LENDCIE-.LSTARTCIE
-.LSTARTCIE:
-       .long 0                 /* CIE ID */
-       .byte 1                 /* Version number */
-       .string "zR"            /* NUL-terminated augmentation string */
-       .uleb128 1              /* Code alignment factor */
-       .sleb128 -4             /* Data alignment factor */
-       .byte 8                 /* Return address register column */
-       .uleb128 1              /* Augmentation value length */
-       .byte 0x1b              /* DW_EH_PE_pcrel|DW_EH_PE_sdata4. */
-       .byte 0x0c              /* DW_CFA_def_cfa */
-       .uleb128 4
-       .uleb128 4
-       .byte 0x88              /* DW_CFA_offset, column 0x8 */
-       .uleb128 1
-       .align 4
-.LENDCIE:
-
-       .long .LENDFDE1-.LSTARTFDE1     /* Length FDE */
-.LSTARTFDE1:
-       .long .LSTARTFDE1-.LSTARTFRAME  /* CIE pointer */
-       .long .LSTART_vsyscall-.        /* PC-relative start address */
-       .long .LEND_vsyscall-.LSTART_vsyscall
-       .uleb128 0                      /* Augmentation length */
-       /* What follows are the instructions for the table generation.
-          We have to record all changes of the stack pointer.  */
-       .byte 0x40 + .Lpush_ebp-.LSTART_vsyscall /* DW_CFA_advance_loc */
-       .byte 0x0e              /* DW_CFA_def_cfa_offset */
-       .uleb128 8
-       .byte 0x85, 0x02        /* DW_CFA_offset %ebp -8 */
-       .byte 0x40 + .Lpop_ebp-.Lpush_ebp /* DW_CFA_advance_loc */
-       .byte 0xc5              /* DW_CFA_restore %ebp */
-       .byte 0x0e              /* DW_CFA_def_cfa_offset */
-       .uleb128 4
-       .align 4
-.LENDFDE1:
-       .previous
-
-       /*
-        * Pad out the segment to match the size of the sysenter.S version.
-        */
-VDSO32_vsyscall_eh_frame_size = 0x40
-       .section .data,"aw",@progbits
-       .space VDSO32_vsyscall_eh_frame_size-(.LENDFDE1-.LSTARTFRAME), 0
-       .previous
diff --git a/arch/x86/entry/vdso/vdso32/sysenter.S b/arch/x86/entry/vdso/vdso32/sysenter.S
deleted file mode 100644 (file)
index e354bce..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Code for the vDSO.  This version uses the sysenter instruction.
- *
- * First get the common code for the sigreturn entry points.
- * This must come first.
- */
-#include "sigreturn.S"
-
-/*
- * The caller puts arg2 in %ecx, which gets pushed. The kernel will use
- * %ecx itself for arg2. The pushing is because the sysexit instruction
- * (found in entry.S) requires that we clobber %ecx with the desired %esp.
- * User code might expect that %ecx is unclobbered though, as it would be
- * for returning via the iret instruction, so we must push and pop.
- *
- * The caller puts arg3 in %edx, which the sysexit instruction requires
- * for %eip. Thus, exactly as for arg2, we must push and pop.
- *
- * Arg6 is different. The caller puts arg6 in %ebp. Since the sysenter
- * instruction clobbers %esp, the user's %esp won't even survive entry
- * into the kernel. We store %esp in %ebp. Code in entry.S must fetch
- * arg6 from the stack.
- *
- * You can not use this vsyscall for the clone() syscall because the
- * three words on the parent stack do not get copied to the child.
- */
-       .text
-       .globl __kernel_vsyscall
-       .type __kernel_vsyscall,@function
-       ALIGN
-__kernel_vsyscall:
-.LSTART_vsyscall:
-       push %ecx
-.Lpush_ecx:
-       push %edx
-.Lpush_edx:
-       push %ebp
-.Lenter_kernel:
-       movl %esp,%ebp
-       sysenter
-
-       /* 7: align return point with nop's to make disassembly easier */
-       .space 7,0x90
-
-       /* 14: System call restart point is here! (SYSENTER_RETURN-2) */
-       int $0x80
-       /* 16: System call normal return point is here! */
-VDSO32_SYSENTER_RETURN:        /* Symbol used by sysenter.c via vdso32-syms.h */
-       pop %ebp
-.Lpop_ebp:
-       pop %edx
-.Lpop_edx:
-       pop %ecx
-.Lpop_ecx:
-       ret
-.LEND_vsyscall:
-       .size __kernel_vsyscall,.-.LSTART_vsyscall
-       .previous
-
-       .section .eh_frame,"a",@progbits
-.LSTARTFRAMEDLSI:
-       .long .LENDCIEDLSI-.LSTARTCIEDLSI
-.LSTARTCIEDLSI:
-       .long 0                 /* CIE ID */
-       .byte 1                 /* Version number */
-       .string "zR"            /* NUL-terminated augmentation string */
-       .uleb128 1              /* Code alignment factor */
-       .sleb128 -4             /* Data alignment factor */
-       .byte 8                 /* Return address register column */
-       .uleb128 1              /* Augmentation value length */
-       .byte 0x1b              /* DW_EH_PE_pcrel|DW_EH_PE_sdata4. */
-       .byte 0x0c              /* DW_CFA_def_cfa */
-       .uleb128 4
-       .uleb128 4
-       .byte 0x88              /* DW_CFA_offset, column 0x8 */
-       .uleb128 1
-       .align 4
-.LENDCIEDLSI:
-       .long .LENDFDEDLSI-.LSTARTFDEDLSI /* Length FDE */
-.LSTARTFDEDLSI:
-       .long .LSTARTFDEDLSI-.LSTARTFRAMEDLSI /* CIE pointer */
-       .long .LSTART_vsyscall-.        /* PC-relative start address */
-       .long .LEND_vsyscall-.LSTART_vsyscall
-       .uleb128 0
-       /* What follows are the instructions for the table generation.
-          We have to record all changes of the stack pointer.  */
-       .byte 0x40 + (.Lpush_ecx-.LSTART_vsyscall) /* DW_CFA_advance_loc */
-       .byte 0x0e              /* DW_CFA_def_cfa_offset */
-       .byte 0x08              /* RA at offset 8 now */
-       .byte 0x40 + (.Lpush_edx-.Lpush_ecx) /* DW_CFA_advance_loc */
-       .byte 0x0e              /* DW_CFA_def_cfa_offset */
-       .byte 0x0c              /* RA at offset 12 now */
-       .byte 0x40 + (.Lenter_kernel-.Lpush_edx) /* DW_CFA_advance_loc */
-       .byte 0x0e              /* DW_CFA_def_cfa_offset */
-       .byte 0x10              /* RA at offset 16 now */
-       .byte 0x85, 0x04        /* DW_CFA_offset %ebp -16 */
-       /* Finally the epilogue.  */
-       .byte 0x40 + (.Lpop_ebp-.Lenter_kernel) /* DW_CFA_advance_loc */
-       .byte 0x0e              /* DW_CFA_def_cfa_offset */
-       .byte 0x0c              /* RA at offset 12 now */
-       .byte 0xc5              /* DW_CFA_restore %ebp */
-       .byte 0x40 + (.Lpop_edx-.Lpop_ebp) /* DW_CFA_advance_loc */
-       .byte 0x0e              /* DW_CFA_def_cfa_offset */
-       .byte 0x08              /* RA at offset 8 now */
-       .byte 0x40 + (.Lpop_ecx-.Lpop_edx) /* DW_CFA_advance_loc */
-       .byte 0x0e              /* DW_CFA_def_cfa_offset */
-       .byte 0x04              /* RA at offset 4 now */
-       .align 4
-.LENDFDEDLSI:
-       .previous
-
-       /*
-        * Emit a symbol with the size of this .eh_frame data,
-        * to verify it matches the other versions.
-        */
-VDSO32_vsyscall_eh_frame_size = (.LENDFDEDLSI-.LSTARTFRAMEDLSI)
diff --git a/arch/x86/entry/vdso/vdso32/system_call.S b/arch/x86/entry/vdso/vdso32/system_call.S
new file mode 100644 (file)
index 0000000..93bd845
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Code for the vDSO.  This version uses the old int $0x80 method.
+*/
+
+#include <asm/dwarf2.h>
+#include <asm/cpufeature.h>
+#include <asm/alternative-asm.h>
+
+/*
+ * First get the common code for the sigreturn entry points.
+ * This must come first.
+ */
+#include "sigreturn.S"
+
+       .text
+       .globl __kernel_vsyscall
+       .type __kernel_vsyscall,@function
+       ALIGN
+__kernel_vsyscall:
+       CFI_STARTPROC
+       /*
+        * Reshuffle regs so that all of any of the entry instructions
+        * will preserve enough state.
+        */
+       pushl   %edx
+       CFI_ADJUST_CFA_OFFSET   4
+       CFI_REL_OFFSET          edx, 0
+       pushl   %ecx
+       CFI_ADJUST_CFA_OFFSET   4
+       CFI_REL_OFFSET          ecx, 0
+       movl    %esp, %ecx
+
+#ifdef CONFIG_X86_64
+       /* If SYSENTER (Intel) or SYSCALL32 (AMD) is available, use it. */
+       ALTERNATIVE_2 "", "sysenter", X86_FEATURE_SYSENTER32, \
+                         "syscall",  X86_FEATURE_SYSCALL32
+#else
+       ALTERNATIVE "", "sysenter", X86_FEATURE_SEP
+#endif
+
+       /* Enter using int $0x80 */
+       movl    (%esp), %ecx
+       int     $0x80
+GLOBAL(int80_landing_pad)
+
+       /* Restore ECX and EDX in case they were clobbered. */
+       popl    %ecx
+       CFI_RESTORE             ecx
+       CFI_ADJUST_CFA_OFFSET   -4
+       popl    %edx
+       CFI_RESTORE             edx
+       CFI_ADJUST_CFA_OFFSET   -4
+       ret
+       CFI_ENDPROC
+
+       .size __kernel_vsyscall,.-__kernel_vsyscall
+       .previous
index 175cc72c0f6861e8b2bb395e5eb8f96f3b391eeb..87a86e017f0e880c7e4d1adc431b6fe9a2ee9a17 100644 (file)
  */
 #undef CONFIG_64BIT
 #undef CONFIG_X86_64
+#undef CONFIG_PGTABLE_LEVELS
 #undef CONFIG_ILLEGAL_POINTER_VALUE
 #undef CONFIG_SPARSEMEM_VMEMMAP
 #undef CONFIG_NR_CPUS
 
 #define CONFIG_X86_32 1
+#define CONFIG_PGTABLE_LEVELS 2
 #define CONFIG_PAGE_OFFSET 0
 #define CONFIG_ILLEGAL_POINTER_VALUE 0
 #define CONFIG_NR_CPUS 1
index 434543145d7889f3fbedd2b28e54df9212346af9..64df47148160239af2442b93ff4c32de941aa362 100644 (file)
@@ -180,21 +180,10 @@ up_fail:
 #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
 static int load_vdso32(void)
 {
-       int ret;
-
        if (vdso32_enabled != 1)  /* Other values all mean "disabled" */
                return 0;
 
-       ret = map_vdso(selected_vdso32, false);
-       if (ret)
-               return ret;
-
-       if (selected_vdso32->sym_VDSO32_SYSENTER_RETURN)
-               current_thread_info()->sysenter_return =
-                       current->mm->context.vdso +
-                       selected_vdso32->sym_VDSO32_SYSENTER_RETURN;
-
-       return 0;
+       return map_vdso(&vdso_image_32, false);
 }
 #endif
 
index b160c0c6baed54c38cc0efbf15ff67075ec869c3..174c2549939d6d184b3c0e01e9d08e23356dacbe 100644 (file)
 #define CREATE_TRACE_POINTS
 #include "vsyscall_trace.h"
 
-static enum { EMULATE, NATIVE, NONE } vsyscall_mode = EMULATE;
+static enum { EMULATE, NATIVE, NONE } vsyscall_mode =
+#if defined(CONFIG_LEGACY_VSYSCALL_NATIVE)
+       NATIVE;
+#elif defined(CONFIG_LEGACY_VSYSCALL_NONE)
+       NONE;
+#else
+       EMULATE;
+#endif
 
 static int __init vsyscall_setup(char *str)
 {
index a0a19b7ba22dc242d2bb957b71aa617e6c75104e..0552884da18db24c7910fa8166a1385f56b6d4a9 100644 (file)
@@ -26,7 +26,7 @@
 #include <asm/ptrace.h>
 #include <asm/ia32_unistd.h>
 #include <asm/user32.h>
-#include <asm/sigcontext32.h>
+#include <uapi/asm/sigcontext.h>
 #include <asm/proto.h>
 #include <asm/vdso.h>
 #include <asm/sigframe.h>
@@ -68,7 +68,7 @@
 }
 
 static int ia32_restore_sigcontext(struct pt_regs *regs,
-                                  struct sigcontext_ia32 __user *sc)
+                                  struct sigcontext_32 __user *sc)
 {
        unsigned int tmpflags, err = 0;
        void __user *buf;
@@ -170,7 +170,7 @@ badframe:
  * Set up a signal frame.
  */
 
-static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
+static int ia32_setup_sigcontext(struct sigcontext_32 __user *sc,
                                 void __user *fpstate,
                                 struct pt_regs *regs, unsigned int mask)
 {
@@ -234,7 +234,7 @@ static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
                unsigned long fx_aligned, math_size;
 
                sp = fpu__alloc_mathframe(sp, 1, &fx_aligned, &math_size);
-               *fpstate = (struct _fpstate_ia32 __user *) sp;
+               *fpstate = (struct _fpstate_32 __user *) sp;
                if (copy_fpstate_to_sigframe(*fpstate, (void __user *)fx_aligned,
                                    math_size) < 0)
                        return (void __user *) -1L;
@@ -289,7 +289,7 @@ int ia32_setup_frame(int sig, struct ksignal *ksig,
                /* Return stub is in 32bit vsyscall page */
                if (current->mm->context.vdso)
                        restorer = current->mm->context.vdso +
-                               selected_vdso32->sym___kernel_sigreturn;
+                               vdso_image_32.sym___kernel_sigreturn;
                else
                        restorer = &frame->retcode;
        }
@@ -368,7 +368,7 @@ int ia32_setup_rt_frame(int sig, struct ksignal *ksig,
                        restorer = ksig->ka.sa.sa_restorer;
                else
                        restorer = current->mm->context.vdso +
-                               selected_vdso32->sym___kernel_rt_sigreturn;
+                               vdso_image_32.sym___kernel_rt_sigreturn;
                put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
 
                /*
index 3a45668f6dc38312bc9f5761214f076a144d00d4..94c18ebfd68cafaec66c6fc0b8c31ccdf37641fb 100644 (file)
 #include <asm/mpspec.h>
 #include <asm/realmode.h>
 
+#ifdef CONFIG_ACPI_APEI
+# include <asm/pgtable_types.h>
+#endif
+
 #ifdef CONFIG_ACPI
 extern int acpi_lapic;
 extern int acpi_ioapic;
@@ -147,4 +151,23 @@ extern int x86_acpi_numa_init(void);
 
 #define acpi_unlazy_tlb(x)     leave_mm(x)
 
+#ifdef CONFIG_ACPI_APEI
+static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr)
+{
+       /*
+        * We currently have no way to look up the EFI memory map
+        * attributes for a region in a consistent way, because the
+        * memmap is discarded after efi_free_boot_services(). So if
+        * you call efi_mem_attributes() during boot and at runtime,
+        * you could theoretically see different attributes.
+        *
+        * Since we are yet to see any x86 platforms that require
+        * anything other than PAGE_KERNEL (some arm64 platforms
+        * require the equivalent of PAGE_KERNEL_NOCACHE), return that
+        * until we know differently.
+        */
+        return PAGE_KERNEL;
+}
+#endif
+
 #endif /* _ASM_X86_ACPI_H */
index 1a5da2e63aeeebc062bd0f5c08b36e4bda32707d..3c56ef1ae068e8c52b829bab6719d47fba0ef8bd 100644 (file)
@@ -81,7 +81,7 @@ static inline struct amd_northbridge *node_to_amd_nb(int node)
        return (node < amd_northbridges.num) ? &amd_northbridges.nb[node] : NULL;
 }
 
-static inline u16 amd_get_node_id(struct pci_dev *pdev)
+static inline u16 amd_pci_dev_to_node_id(struct pci_dev *pdev)
 {
        struct pci_dev *misc;
        int i;
index ebf6d5e5668c98b823d4e6b8fe508e4d1b8e4995..a30316bf801ab9da14753425de67670f71feece0 100644 (file)
@@ -115,6 +115,59 @@ static inline bool apic_is_x2apic_enabled(void)
        return msr & X2APIC_ENABLE;
 }
 
+extern void enable_IR_x2apic(void);
+
+extern int get_physical_broadcast(void);
+
+extern int lapic_get_maxlvt(void);
+extern void clear_local_APIC(void);
+extern void disconnect_bsp_APIC(int virt_wire_setup);
+extern void disable_local_APIC(void);
+extern void lapic_shutdown(void);
+extern void sync_Arb_IDs(void);
+extern void init_bsp_APIC(void);
+extern void setup_local_APIC(void);
+extern void init_apic_mappings(void);
+void register_lapic_address(unsigned long address);
+extern void setup_boot_APIC_clock(void);
+extern void setup_secondary_APIC_clock(void);
+extern int APIC_init_uniprocessor(void);
+
+#ifdef CONFIG_X86_64
+static inline int apic_force_enable(unsigned long addr)
+{
+       return -1;
+}
+#else
+extern int apic_force_enable(unsigned long addr);
+#endif
+
+extern int apic_bsp_setup(bool upmode);
+extern void apic_ap_setup(void);
+
+/*
+ * On 32bit this is mach-xxx local
+ */
+#ifdef CONFIG_X86_64
+extern int apic_is_clustered_box(void);
+#else
+static inline int apic_is_clustered_box(void)
+{
+       return 0;
+}
+#endif
+
+extern int setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask);
+
+#else /* !CONFIG_X86_LOCAL_APIC */
+static inline void lapic_shutdown(void) { }
+#define local_apic_timer_c2_ok         1
+static inline void init_apic_mappings(void) { }
+static inline void disable_local_APIC(void) { }
+# define setup_boot_APIC_clock x86_init_noop
+# define setup_secondary_APIC_clock x86_init_noop
+#endif /* !CONFIG_X86_LOCAL_APIC */
+
 #ifdef CONFIG_X86_X2APIC
 /*
  * Make previous memory operations globally visible before
@@ -186,67 +239,14 @@ static inline int x2apic_enabled(void)
 }
 
 #define x2apic_supported()     (cpu_has_x2apic)
-#else
+#else /* !CONFIG_X86_X2APIC */
 static inline void check_x2apic(void) { }
 static inline void x2apic_setup(void) { }
 static inline int x2apic_enabled(void) { return 0; }
 
 #define x2apic_mode            (0)
 #define        x2apic_supported()      (0)
-#endif
-
-extern void enable_IR_x2apic(void);
-
-extern int get_physical_broadcast(void);
-
-extern int lapic_get_maxlvt(void);
-extern void clear_local_APIC(void);
-extern void disconnect_bsp_APIC(int virt_wire_setup);
-extern void disable_local_APIC(void);
-extern void lapic_shutdown(void);
-extern void sync_Arb_IDs(void);
-extern void init_bsp_APIC(void);
-extern void setup_local_APIC(void);
-extern void init_apic_mappings(void);
-void register_lapic_address(unsigned long address);
-extern void setup_boot_APIC_clock(void);
-extern void setup_secondary_APIC_clock(void);
-extern int APIC_init_uniprocessor(void);
-
-#ifdef CONFIG_X86_64
-static inline int apic_force_enable(unsigned long addr)
-{
-       return -1;
-}
-#else
-extern int apic_force_enable(unsigned long addr);
-#endif
-
-extern int apic_bsp_setup(bool upmode);
-extern void apic_ap_setup(void);
-
-/*
- * On 32bit this is mach-xxx local
- */
-#ifdef CONFIG_X86_64
-extern int apic_is_clustered_box(void);
-#else
-static inline int apic_is_clustered_box(void)
-{
-       return 0;
-}
-#endif
-
-extern int setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask);
-
-#else /* !CONFIG_X86_LOCAL_APIC */
-static inline void lapic_shutdown(void) { }
-#define local_apic_timer_c2_ok         1
-static inline void init_apic_mappings(void) { }
-static inline void disable_local_APIC(void) { }
-# define setup_boot_APIC_clock x86_init_noop
-# define setup_secondary_APIC_clock x86_init_noop
-#endif /* !CONFIG_X86_LOCAL_APIC */
+#endif /* !CONFIG_X86_X2APIC */
 
 #ifdef CONFIG_X86_64
 #define        SET_APIC_ID(x)          (apic->set_apic_id(x))
index fb52aa644aabb4ed8ff15eb26dca1944648786c0..ae5fb83e6d91c9bf459b02f143a9b583a4515ed7 100644 (file)
@@ -24,7 +24,7 @@
  */
 static __always_inline int atomic_read(const atomic_t *v)
 {
-       return ACCESS_ONCE((v)->counter);
+       return READ_ONCE((v)->counter);
 }
 
 /**
@@ -36,7 +36,7 @@ static __always_inline int atomic_read(const atomic_t *v)
  */
 static __always_inline void atomic_set(atomic_t *v, int i)
 {
-       v->counter = i;
+       WRITE_ONCE(v->counter, i);
 }
 
 /**
index 50e33eff58de7fde09c770e229c9a30fccf49d03..037351022f5483c99a3f0a42dd4646f1eb0a3055 100644 (file)
@@ -18,7 +18,7 @@
  */
 static inline long atomic64_read(const atomic64_t *v)
 {
-       return ACCESS_ONCE((v)->counter);
+       return READ_ONCE((v)->counter);
 }
 
 /**
@@ -30,7 +30,7 @@ static inline long atomic64_read(const atomic64_t *v)
  */
 static inline void atomic64_set(atomic64_t *v, long i)
 {
-       v->counter = i;
+       WRITE_ONCE(v->counter, i);
 }
 
 /**
index 9727b3b48bd174c8ae8297bd94e897484375618a..e4f8010f22e04d2f261bb73bc7745f68f36a392f 100644 (file)
@@ -12,7 +12,7 @@
 #include <asm/disabled-features.h>
 #endif
 
-#define NCAPINTS       13      /* N 32-bit words worth of info */
+#define NCAPINTS       14      /* N 32-bit words worth of info */
 #define NBUGINTS       1       /* N 32-bit bug flags */
 
 /*
 /* Intel-defined CPU QoS Sub-leaf, CPUID level 0x0000000F:1 (edx), word 12 */
 #define X86_FEATURE_CQM_OCCUP_LLC (12*32+ 0) /* LLC occupancy monitoring if 1 */
 
+/* AMD-defined CPU features, CPUID level 0x80000008 (ebx), word 13 */
+#define X86_FEATURE_CLZERO     (13*32+0) /* CLZERO instruction */
+
 /*
  * BUG word(s)
  */
diff --git a/arch/x86/include/asm/dwarf2.h b/arch/x86/include/asm/dwarf2.h
new file mode 100644 (file)
index 0000000..b7a1ab8
--- /dev/null
@@ -0,0 +1,84 @@
+#ifndef _ASM_X86_DWARF2_H
+#define _ASM_X86_DWARF2_H
+
+#ifndef __ASSEMBLY__
+#warning "asm/dwarf2.h should be only included in pure assembly files"
+#endif
+
+/*
+ * Macros for dwarf2 CFI unwind table entries.
+ * See "as.info" for details on these pseudo ops. Unfortunately
+ * they are only supported in very new binutils, so define them
+ * away for older version.
+ */
+
+#ifdef CONFIG_AS_CFI
+
+#define CFI_STARTPROC          .cfi_startproc
+#define CFI_ENDPROC            .cfi_endproc
+#define CFI_DEF_CFA            .cfi_def_cfa
+#define CFI_DEF_CFA_REGISTER   .cfi_def_cfa_register
+#define CFI_DEF_CFA_OFFSET     .cfi_def_cfa_offset
+#define CFI_ADJUST_CFA_OFFSET  .cfi_adjust_cfa_offset
+#define CFI_OFFSET             .cfi_offset
+#define CFI_REL_OFFSET         .cfi_rel_offset
+#define CFI_REGISTER           .cfi_register
+#define CFI_RESTORE            .cfi_restore
+#define CFI_REMEMBER_STATE     .cfi_remember_state
+#define CFI_RESTORE_STATE      .cfi_restore_state
+#define CFI_UNDEFINED          .cfi_undefined
+#define CFI_ESCAPE             .cfi_escape
+
+#ifdef CONFIG_AS_CFI_SIGNAL_FRAME
+#define CFI_SIGNAL_FRAME       .cfi_signal_frame
+#else
+#define CFI_SIGNAL_FRAME
+#endif
+
+#if defined(CONFIG_AS_CFI_SECTIONS) && defined(__ASSEMBLY__)
+#ifndef BUILD_VDSO
+       /*
+        * Emit CFI data in .debug_frame sections, not .eh_frame sections.
+        * The latter we currently just discard since we don't do DWARF
+        * unwinding at runtime.  So only the offline DWARF information is
+        * useful to anyone.  Note we should not use this directive if
+        * vmlinux.lds.S gets changed so it doesn't discard .eh_frame.
+        */
+       .cfi_sections .debug_frame
+#else
+        /*
+         * For the vDSO, emit both runtime unwind information and debug
+         * symbols for the .dbg file.
+         */
+       .cfi_sections .eh_frame, .debug_frame
+#endif
+#endif
+
+#else
+
+/*
+ * Due to the structure of pre-exisiting code, don't use assembler line
+ * comment character # to ignore the arguments. Instead, use a dummy macro.
+ */
+.macro cfi_ignore a=0, b=0, c=0, d=0
+.endm
+
+#define CFI_STARTPROC          cfi_ignore
+#define CFI_ENDPROC            cfi_ignore
+#define CFI_DEF_CFA            cfi_ignore
+#define CFI_DEF_CFA_REGISTER   cfi_ignore
+#define CFI_DEF_CFA_OFFSET     cfi_ignore
+#define CFI_ADJUST_CFA_OFFSET  cfi_ignore
+#define CFI_OFFSET             cfi_ignore
+#define CFI_REL_OFFSET         cfi_ignore
+#define CFI_REGISTER           cfi_ignore
+#define CFI_RESTORE            cfi_ignore
+#define CFI_REMEMBER_STATE     cfi_ignore
+#define CFI_RESTORE_STATE      cfi_ignore
+#define CFI_UNDEFINED          cfi_ignore
+#define CFI_ESCAPE             cfi_ignore
+#define CFI_SIGNAL_FRAME       cfi_ignore
+
+#endif
+
+#endif /* _ASM_X86_DWARF2_H */
index ae68be92f75587ce2bfdc2d09f18711ef9cd9c8a..0010c78c4998cf0702299ea2f8a9229e09bb6438 100644 (file)
@@ -105,6 +105,7 @@ extern void __init efi_set_executable(efi_memory_desc_t *md, bool executable);
 extern int __init efi_memblock_x86_reserve_range(void);
 extern pgd_t * __init efi_call_phys_prolog(void);
 extern void __init efi_call_phys_epilog(pgd_t *save_pgd);
+extern void __init efi_print_memmap(void);
 extern void __init efi_unmap_memmap(void);
 extern void __init efi_memory_uc(u64 addr, unsigned long size);
 extern void __init efi_map_region(efi_memory_desc_t *md);
index 141c561f46649209915158211c31b038ee061ac8..1514753fd43553e079696712b48a8d08b6966e98 100644 (file)
@@ -171,11 +171,11 @@ do {                                              \
 static inline void elf_common_init(struct thread_struct *t,
                                   struct pt_regs *regs, const u16 ds)
 {
-       /* Commented-out registers are cleared in stub_execve */
-       /*regs->ax = regs->bx =*/ regs->cx = regs->dx = 0;
-       regs->si = regs->di /*= regs->bp*/ = 0;
+       /* ax gets execve's return value. */
+       /*regs->ax = */ regs->bx = regs->cx = regs->dx = 0;
+       regs->si = regs->di = regs->bp = 0;
        regs->r8 = regs->r9 = regs->r10 = regs->r11 = 0;
-       /*regs->r12 = regs->r13 = regs->r14 = regs->r15 = 0;*/
+       regs->r12 = regs->r13 = regs->r14 = regs->r15 = 0;
        t->fs = t->gs = 0;
        t->fsindex = t->gsindex = 0;
        t->ds = t->es = ds;
@@ -328,7 +328,7 @@ else                                                                        \
 
 #define VDSO_ENTRY                                                     \
        ((unsigned long)current->mm->context.vdso +                     \
-        selected_vdso32->sym___kernel_vsyscall)
+        vdso_image_32.sym___kernel_vsyscall)
 
 struct linux_binprm;
 
index 7358e9d61f1e70ebb069048d5120e5a377895ba4..0e970d00dfcd6754167f645e18ca64a6e94ec94d 100644 (file)
@@ -5,7 +5,7 @@
 #define _ASM_X86_FPU_SIGNAL_H
 
 #ifdef CONFIG_X86_64
-# include <asm/sigcontext32.h>
+# include <uapi/asm/sigcontext.h>
 # include <asm/user32.h>
 struct ksignal;
 int ia32_setup_rt_frame(int sig, struct ksignal *ksig,
index c49c5173158e743b985c4e29f09f040811db6a48..1c6f6ac52ad0a2cfe44db5eab9825b59408cde01 100644 (file)
@@ -95,63 +95,122 @@ struct swregs_state {
 /*
  * List of XSAVE features Linux knows about:
  */
-enum xfeature_bit {
-       XSTATE_BIT_FP,
-       XSTATE_BIT_SSE,
-       XSTATE_BIT_YMM,
-       XSTATE_BIT_BNDREGS,
-       XSTATE_BIT_BNDCSR,
-       XSTATE_BIT_OPMASK,
-       XSTATE_BIT_ZMM_Hi256,
-       XSTATE_BIT_Hi16_ZMM,
-
-       XFEATURES_NR_MAX,
+enum xfeature {
+       XFEATURE_FP,
+       XFEATURE_SSE,
+       /*
+        * Values above here are "legacy states".
+        * Those below are "extended states".
+        */
+       XFEATURE_YMM,
+       XFEATURE_BNDREGS,
+       XFEATURE_BNDCSR,
+       XFEATURE_OPMASK,
+       XFEATURE_ZMM_Hi256,
+       XFEATURE_Hi16_ZMM,
+
+       XFEATURE_MAX,
 };
 
-#define XSTATE_FP              (1 << XSTATE_BIT_FP)
-#define XSTATE_SSE             (1 << XSTATE_BIT_SSE)
-#define XSTATE_YMM             (1 << XSTATE_BIT_YMM)
-#define XSTATE_BNDREGS         (1 << XSTATE_BIT_BNDREGS)
-#define XSTATE_BNDCSR          (1 << XSTATE_BIT_BNDCSR)
-#define XSTATE_OPMASK          (1 << XSTATE_BIT_OPMASK)
-#define XSTATE_ZMM_Hi256       (1 << XSTATE_BIT_ZMM_Hi256)
-#define XSTATE_Hi16_ZMM                (1 << XSTATE_BIT_Hi16_ZMM)
+#define XFEATURE_MASK_FP               (1 << XFEATURE_FP)
+#define XFEATURE_MASK_SSE              (1 << XFEATURE_SSE)
+#define XFEATURE_MASK_YMM              (1 << XFEATURE_YMM)
+#define XFEATURE_MASK_BNDREGS          (1 << XFEATURE_BNDREGS)
+#define XFEATURE_MASK_BNDCSR           (1 << XFEATURE_BNDCSR)
+#define XFEATURE_MASK_OPMASK           (1 << XFEATURE_OPMASK)
+#define XFEATURE_MASK_ZMM_Hi256                (1 << XFEATURE_ZMM_Hi256)
+#define XFEATURE_MASK_Hi16_ZMM         (1 << XFEATURE_Hi16_ZMM)
+
+#define XFEATURE_MASK_FPSSE            (XFEATURE_MASK_FP | XFEATURE_MASK_SSE)
+#define XFEATURE_MASK_AVX512           (XFEATURE_MASK_OPMASK \
+                                        | XFEATURE_MASK_ZMM_Hi256 \
+                                        | XFEATURE_MASK_Hi16_ZMM)
+
+#define FIRST_EXTENDED_XFEATURE        XFEATURE_YMM
 
-#define XSTATE_FPSSE           (XSTATE_FP | XSTATE_SSE)
-#define XSTATE_AVX512          (XSTATE_OPMASK | XSTATE_ZMM_Hi256 | XSTATE_Hi16_ZMM)
+struct reg_128_bit {
+       u8      regbytes[128/8];
+};
+struct reg_256_bit {
+       u8      regbytes[256/8];
+};
+struct reg_512_bit {
+       u8      regbytes[512/8];
+};
 
 /*
+ * State component 2:
+ *
  * There are 16x 256-bit AVX registers named YMM0-YMM15.
  * The low 128 bits are aliased to the 16 SSE registers (XMM0-XMM15)
- * and are stored in 'struct fxregs_state::xmm_space[]'.
+ * and are stored in 'struct fxregs_state::xmm_space[]' in the
+ * "legacy" area.
  *
- * The high 128 bits are stored here:
- *    16x 128 bits == 256 bytes.
+ * The high 128 bits are stored here.
  */
 struct ymmh_struct {
-       u8                              ymmh_space[256];
-};
-
-/* We don't support LWP yet: */
-struct lwp_struct {
-       u8                              reserved[128];
-};
+       struct reg_128_bit              hi_ymm[16];
+} __packed;
 
 /* Intel MPX support: */
-struct bndreg {
+
+struct mpx_bndreg {
        u64                             lower_bound;
        u64                             upper_bound;
 } __packed;
+/*
+ * State component 3 is used for the 4 128-bit bounds registers
+ */
+struct mpx_bndreg_state {
+       struct mpx_bndreg               bndreg[4];
+} __packed;
 
-struct bndcsr {
+/*
+ * State component 4 is used for the 64-bit user-mode MPX
+ * configuration register BNDCFGU and the 64-bit MPX status
+ * register BNDSTATUS.  We call the pair "BNDCSR".
+ */
+struct mpx_bndcsr {
        u64                             bndcfgu;
        u64                             bndstatus;
 } __packed;
 
-struct mpx_struct {
-       struct bndreg                   bndreg[4];
-       struct bndcsr                   bndcsr;
-};
+/*
+ * The BNDCSR state is padded out to be 64-bytes in size.
+ */
+struct mpx_bndcsr_state {
+       union {
+               struct mpx_bndcsr               bndcsr;
+               u8                              pad_to_64_bytes[64];
+       };
+} __packed;
+
+/* AVX-512 Components: */
+
+/*
+ * State component 5 is used for the 8 64-bit opmask registers
+ * k0-k7 (opmask state).
+ */
+struct avx_512_opmask_state {
+       u64                             opmask_reg[8];
+} __packed;
+
+/*
+ * State component 6 is used for the upper 256 bits of the
+ * registers ZMM0-ZMM15. These 16 256-bit values are denoted
+ * ZMM0_H-ZMM15_H (ZMM_Hi256 state).
+ */
+struct avx_512_zmm_uppers_state {
+       struct reg_256_bit              zmm_upper[16];
+} __packed;
+
+/*
+ * State component 7 is used for the 16 512-bit registers
+ * ZMM16-ZMM31 (Hi16_ZMM state).
+ */
+struct avx_512_hi16_state {
+       struct reg_512_bit              hi16_zmm[16];
+} __packed;
 
 struct xstate_header {
        u64                             xfeatures;
@@ -159,22 +218,19 @@ struct xstate_header {
        u64                             reserved[6];
 } __attribute__((packed));
 
-/* New processor state extensions should be added here: */
-#define XSTATE_RESERVE                 (sizeof(struct ymmh_struct) + \
-                                        sizeof(struct lwp_struct)  + \
-                                        sizeof(struct mpx_struct)  )
 /*
  * This is our most modern FPU state format, as saved by the XSAVE
  * and restored by the XRSTOR instructions.
  *
  * It consists of a legacy fxregs portion, an xstate header and
- * subsequent fixed size areas as defined by the xstate header.
- * Not all CPUs support all the extensions.
+ * subsequent areas as defined by the xstate header.  Not all CPUs
+ * support all the extensions, so the size of the extended area
+ * can vary quite a bit between CPUs.
  */
 struct xregs_state {
        struct fxregs_state             i387;
        struct xstate_header            header;
-       u8                              __reserved[XSTATE_RESERVE];
+       u8                              extended_state_area[0];
 } __attribute__ ((packed, aligned (64)));
 
 /*
@@ -182,7 +238,9 @@ struct xregs_state {
  * put together, so that we can pick the right one runtime.
  *
  * The size of the structure is determined by the largest
- * member - which is the xsave area:
+ * member - which is the xsave area.  The padding is there
+ * to ensure that statically-allocated task_structs (just
+ * the init_task today) have enough space.
  */
 union fpregs_state {
        struct fregs_state              fsave;
index 4656b25bb9a7b4a7fa719213451324b2d3f6ebec..3a6c89b7030757e707a77be75a5a4d3a30e2982f 100644 (file)
@@ -6,7 +6,7 @@
 #include <linux/uaccess.h>
 
 /* Bit 63 of XCR0 is reserved for future expansion */
-#define XSTATE_EXTEND_MASK     (~(XSTATE_FPSSE | (1ULL << 63)))
+#define XFEATURE_MASK_EXTEND   (~(XFEATURE_MASK_FPSSE | (1ULL << 63)))
 
 #define XSTATE_CPUID           0x0000000d
 
 #define XSAVE_YMM_OFFSET    (XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET)
 
 /* Supported features which support lazy state saving */
-#define XSTATE_LAZY    (XSTATE_FP | XSTATE_SSE | XSTATE_YMM                  \
-                       | XSTATE_OPMASK | XSTATE_ZMM_Hi256 | XSTATE_Hi16_ZMM)
+#define XFEATURE_MASK_LAZY     (XFEATURE_MASK_FP | \
+                                XFEATURE_MASK_SSE | \
+                                XFEATURE_MASK_YMM | \
+                                XFEATURE_MASK_OPMASK | \
+                                XFEATURE_MASK_ZMM_Hi256 | \
+                                XFEATURE_MASK_Hi16_ZMM)
 
 /* Supported features which require eager state saving */
-#define XSTATE_EAGER   (XSTATE_BNDREGS | XSTATE_BNDCSR)
+#define XFEATURE_MASK_EAGER    (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR)
 
 /* All currently supported features */
-#define XCNTXT_MASK    (XSTATE_LAZY | XSTATE_EAGER)
+#define XCNTXT_MASK    (XFEATURE_MASK_LAZY | XFEATURE_MASK_EAGER)
 
 #ifdef CONFIG_X86_64
 #define REX_PREFIX     "0x48, "
@@ -40,6 +44,7 @@ extern u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS];
 
 extern void update_regset_xstate_info(unsigned int size, u64 xstate_mask);
 
+void fpu__xstate_clear_all_cpu_caps(void);
 void *get_xsave_addr(struct xregs_state *xsave, int xstate);
 const void *get_xsave_field_ptr(int xstate_field);
 
index 5fa9fb0f8809902a8e15f6f8fa1d245d379a6a16..cc285ec4b2c1e94906901b3462d9655b4df28cf1 100644 (file)
 /* hpet memory map physical address */
 extern unsigned long hpet_address;
 extern unsigned long force_hpet_address;
-extern int boot_hpet_disable;
+extern bool boot_hpet_disable;
 extern u8 hpet_blockid;
-extern int hpet_force_user;
-extern u8 hpet_msi_disable;
+extern bool hpet_force_user;
+extern bool hpet_msi_disable;
 extern int is_hpet_enabled(void);
 extern int hpet_enable(void);
 extern void hpet_disable(void);
index 28019765442e6092e8131f8ed01e43d783e0152c..a9bdf5569ab38a5e3a15d5f8bb0dc09b1d1e40ff 100644 (file)
@@ -10,7 +10,7 @@
  * 32 bit structures for IA32 support.
  */
 
-#include <asm/sigcontext32.h>
+#include <uapi/asm/sigcontext.h>
 
 /* signal.h */
 
@@ -18,7 +18,7 @@ struct ucontext_ia32 {
        unsigned int      uc_flags;
        unsigned int      uc_link;
        compat_stack_t    uc_stack;
-       struct sigcontext_ia32 uc_mcontext;
+       struct sigcontext_32 uc_mcontext;
        compat_sigset_t   uc_sigmask;   /* mask last for extensibility */
 };
 
index b130d59406fb12ab3a75d5a2a8631b202be50ab3..e5f5dc9787d5fa64beac6bca4a4854c05d03dc4a 100644 (file)
@@ -29,11 +29,5 @@ extern void show_trace(struct task_struct *t, struct pt_regs *regs,
 extern void __show_regs(struct pt_regs *regs, int all);
 extern unsigned long oops_begin(void);
 extern void oops_end(unsigned long, struct pt_regs *, int signr);
-#ifdef CONFIG_KEXEC_CORE
-extern int in_crash_kexec;
-#else
-/* no crash dump is ever in progress if no crash kernel can be kexec'd */
-#define in_crash_kexec 0
-#endif
 
 #endif /* _ASM_X86_KDEBUG_H */
index 2dbc0bf2b9f3f7848d10025bb4bcfcf6d36a4d2d..2ea4527e462f133398f178a885a4b920892cd3f5 100644 (file)
@@ -123,19 +123,27 @@ struct mca_config {
 };
 
 struct mce_vendor_flags {
-                       /*
-                        * overflow recovery cpuid bit indicates that overflow
-                        * conditions are not fatal
-                        */
-       __u64           overflow_recov  : 1,
-
-                       /*
-                        * SUCCOR stands for S/W UnCorrectable error COntainment
-                        * and Recovery. It indicates support for data poisoning
-                        * in HW and deferred error interrupts.
-                        */
-                       succor          : 1,
-                       __reserved_0    : 62;
+       /*
+        * Indicates that overflow conditions are not fatal, when set.
+        */
+       __u64 overflow_recov    : 1,
+
+       /*
+        * (AMD) SUCCOR stands for S/W UnCorrectable error COntainment and
+        * Recovery. It indicates support for data poisoning in HW and deferred
+        * error interrupts.
+        */
+             succor            : 1,
+
+       /*
+        * (AMD) SMCA: This bit indicates support for Scalable MCA which expands
+        * the register space for each MCA bank and also increases number of
+        * banks. Also, to accommodate the new banks and registers, the MCA
+        * register space is moved to a new MSR range.
+        */
+             smca              : 1,
+
+             __reserved_0      : 61;
 };
 extern struct mce_vendor_flags mce_flags;
 
index 9e6278c7140eac3cac2e5841002300c3b903e457..34e62b1dcfce46a606fbe78d7d55131513d859d3 100644 (file)
@@ -27,7 +27,6 @@ struct cpu_signature {
 struct device;
 
 enum ucode_state { UCODE_ERROR, UCODE_OK, UCODE_NFOUND };
-extern bool dis_ucode_ldr;
 
 struct microcode_ops {
        enum ucode_state (*request_microcode_user) (int cpu,
@@ -55,6 +54,12 @@ struct ucode_cpu_info {
 };
 extern struct ucode_cpu_info ucode_cpu_info[];
 
+#ifdef CONFIG_MICROCODE
+int __init microcode_init(void);
+#else
+static inline int __init microcode_init(void)  { return 0; };
+#endif
+
 #ifdef CONFIG_MICROCODE_INTEL
 extern struct microcode_ops * __init init_intel_microcode(void);
 #else
@@ -75,7 +80,6 @@ static inline struct microcode_ops * __init init_amd_microcode(void)
 static inline void __exit exit_amd_microcode(void) {}
 #endif
 
-#ifdef CONFIG_MICROCODE_EARLY
 #define MAX_UCODE_COUNT 128
 
 #define QCHAR(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
@@ -150,22 +154,18 @@ static inline unsigned int x86_model(unsigned int sig)
        return model;
 }
 
+#ifdef CONFIG_MICROCODE
 extern void __init load_ucode_bsp(void);
 extern void load_ucode_ap(void);
 extern int __init save_microcode_in_initrd(void);
 void reload_early_microcode(void);
 extern bool get_builtin_firmware(struct cpio_data *cd, const char *name);
 #else
-static inline void __init load_ucode_bsp(void) {}
-static inline void load_ucode_ap(void) {}
-static inline int __init save_microcode_in_initrd(void)
-{
-       return 0;
-}
-static inline void reload_early_microcode(void) {}
-static inline bool get_builtin_firmware(struct cpio_data *cd, const char *name)
-{
-       return false;
-}
+static inline void __init load_ucode_bsp(void)                 { }
+static inline void load_ucode_ap(void)                         { }
+static inline int __init save_microcode_in_initrd(void)                { return 0; }
+static inline void reload_early_microcode(void)                        { }
+static inline bool
+get_builtin_firmware(struct cpio_data *cd, const char *name)   { return false; }
 #endif
 #endif /* _ASM_X86_MICROCODE_H */
index ac6d328977a67e4fd0ae5674e2d7e8a30539ff92..adfc847a395ec2d94e4d46993379a0a7a77f59e0 100644 (file)
@@ -64,7 +64,7 @@ extern enum ucode_state load_microcode_amd(int cpu, u8 family, const u8 *data, s
 #define PATCH_MAX_SIZE PAGE_SIZE
 extern u8 amd_ucode_patch[PATCH_MAX_SIZE];
 
-#ifdef CONFIG_MICROCODE_AMD_EARLY
+#ifdef CONFIG_MICROCODE_AMD
 extern void __init load_ucode_amd_bsp(unsigned int family);
 extern void load_ucode_amd_ap(void);
 extern int __init save_microcode_in_initrd_amd(void);
@@ -76,4 +76,5 @@ static inline int __init save_microcode_in_initrd_amd(void) { return -EINVAL; }
 void reload_ucode_amd(void) {}
 #endif
 
+extern bool check_current_patch_level(u32 *rev, bool early);
 #endif /* _ASM_X86_MICROCODE_AMD_H */
index 7991c606125d01b137a6f0e1ed16f1e8c1d86015..8559b0102ea1f14dfc840c28df6e1fcc0f813965 100644 (file)
@@ -57,7 +57,7 @@ extern int has_newer_microcode(void *mc, unsigned int csig, int cpf, int rev);
 extern int microcode_sanity_check(void *mc, int print_err);
 extern int find_matching_signature(void *mc, unsigned int csig, int cpf);
 
-#ifdef CONFIG_MICROCODE_INTEL_EARLY
+#ifdef CONFIG_MICROCODE_INTEL
 extern void __init load_ucode_intel_bsp(void);
 extern void load_ucode_intel_ap(void);
 extern void show_ucode_info_early(void);
@@ -71,13 +71,9 @@ static inline int __init save_microcode_in_initrd_intel(void) { return -EINVAL;
 static inline void reload_ucode_intel(void) {}
 #endif
 
-#if defined(CONFIG_MICROCODE_INTEL_EARLY) && defined(CONFIG_HOTPLUG_CPU)
+#ifdef CONFIG_HOTPLUG_CPU
 extern int save_mc_for_early(u8 *mc);
 #else
-static inline int save_mc_for_early(u8 *mc)
-{
-       return 0;
-}
+static inline int save_mc_for_early(u8 *mc) { return 0; }
 #endif
-
 #endif /* _ASM_X86_MICROCODE_INTEL_H */
index 1c6f7f6212c14066e7522162e16494f2f7feb437..c64373a2d731b2bddfde9656d7f5c1ddc67d676d 100644 (file)
@@ -14,6 +14,7 @@
 #ifndef _ASM_X86_NUMACHIP_NUMACHIP_H
 #define _ASM_X86_NUMACHIP_NUMACHIP_H
 
+extern u8 numachip_system;
 extern int __init pci_numachip_init(void);
 
 #endif /* _ASM_X86_NUMACHIP_NUMACHIP_H */
index 660f843df928c12b1afc0816ae412e86563b1952..29719eecdc2e6119647ae2dc4d5431cea86aa5ac 100644 (file)
 #ifndef _ASM_X86_NUMACHIP_NUMACHIP_CSR_H
 #define _ASM_X86_NUMACHIP_NUMACHIP_CSR_H
 
-#include <linux/numa.h>
-#include <linux/percpu.h>
+#include <linux/smp.h>
 #include <linux/io.h>
-#include <linux/swab.h>
-#include <asm/types.h>
-#include <asm/processor.h>
 
 #define CSR_NODE_SHIFT         16
 #define CSR_NODE_BITS(p)       (((unsigned long)(p)) << CSR_NODE_SHIFT)
 
 /* 32K CSR space, b15 indicates geo/non-geo */
 #define CSR_OFFSET_MASK        0x7fffUL
-
-/* Global CSR space covers all 4K possible nodes with 64K CSR space per node */
-#define NUMACHIP_GCSR_BASE     0x3fff00000000ULL
-#define NUMACHIP_GCSR_LIM      0x3fff0fffffffULL
-#define NUMACHIP_GCSR_SIZE     (NUMACHIP_GCSR_LIM - NUMACHIP_GCSR_BASE + 1)
+#define CSR_G0_NODE_IDS (0x008 + (0 << 12))
+#define CSR_G3_EXT_IRQ_GEN (0x030 + (3 << 12))
 
 /*
  * Local CSR space starts in global CSR space with "nodeid" = 0xfff0, however
 #define NUMACHIP_LCSR_BASE     0x3ffffe000000ULL
 #define NUMACHIP_LCSR_LIM      0x3fffffffffffULL
 #define NUMACHIP_LCSR_SIZE     (NUMACHIP_LCSR_LIM - NUMACHIP_LCSR_BASE + 1)
-
-static inline void *gcsr_address(int node, unsigned long offset)
-{
-       return __va(NUMACHIP_GCSR_BASE | (1UL << 15) |
-               CSR_NODE_BITS(node & CSR_NODE_MASK) | (offset & CSR_OFFSET_MASK));
-}
+#define NUMACHIP_LAPIC_BITS    8
 
 static inline void *lcsr_address(unsigned long offset)
 {
@@ -54,114 +42,57 @@ static inline void *lcsr_address(unsigned long offset)
                CSR_NODE_BITS(0xfff0) | (offset & CSR_OFFSET_MASK));
 }
 
-static inline unsigned int read_gcsr(int node, unsigned long offset)
+static inline unsigned int read_lcsr(unsigned long offset)
 {
-       return swab32(readl(gcsr_address(node, offset)));
+       return swab32(readl(lcsr_address(offset)));
 }
 
-static inline void write_gcsr(int node, unsigned long offset, unsigned int val)
+static inline void write_lcsr(unsigned long offset, unsigned int val)
 {
-       writel(swab32(val), gcsr_address(node, offset));
+       writel(swab32(val), lcsr_address(offset));
 }
 
-static inline unsigned int read_lcsr(unsigned long offset)
+/*
+ * On NumaChip2, local CSR space is 16MB and starts at fixed offset below 4G
+ */
+
+#define NUMACHIP2_LCSR_BASE       0xf0000000UL
+#define NUMACHIP2_LCSR_SIZE       0x1000000UL
+#define NUMACHIP2_APIC_ICR        0x100000
+#define NUMACHIP2_TIMER_DEADLINE  0x200000
+#define NUMACHIP2_TIMER_INT       0x200008
+#define NUMACHIP2_TIMER_NOW       0x200018
+#define NUMACHIP2_TIMER_RESET     0x200020
+
+static inline void __iomem *numachip2_lcsr_address(unsigned long offset)
 {
-       return swab32(readl(lcsr_address(offset)));
+       return (void __iomem *)__va(NUMACHIP2_LCSR_BASE |
+               (offset & (NUMACHIP2_LCSR_SIZE - 1)));
 }
 
-static inline void write_lcsr(unsigned long offset, unsigned int val)
+static inline u32 numachip2_read32_lcsr(unsigned long offset)
 {
-       writel(swab32(val), lcsr_address(offset));
+       return readl(numachip2_lcsr_address(offset));
 }
 
-/* ========================================================================= */
-/*                   CSR_G0_STATE_CLEAR                                      */
-/* ========================================================================= */
-
-#define CSR_G0_STATE_CLEAR (0x000 + (0 << 12))
-union numachip_csr_g0_state_clear {
-       unsigned int v;
-       struct numachip_csr_g0_state_clear_s {
-               unsigned int _state:2;
-               unsigned int _rsvd_2_6:5;
-               unsigned int _lost:1;
-               unsigned int _rsvd_8_31:24;
-       } s;
-};
-
-/* ========================================================================= */
-/*                   CSR_G0_NODE_IDS                                         */
-/* ========================================================================= */
+static inline u64 numachip2_read64_lcsr(unsigned long offset)
+{
+       return readq(numachip2_lcsr_address(offset));
+}
 
-#define CSR_G0_NODE_IDS (0x008 + (0 << 12))
-union numachip_csr_g0_node_ids {
-       unsigned int v;
-       struct numachip_csr_g0_node_ids_s {
-               unsigned int _initialid:16;
-               unsigned int _nodeid:12;
-               unsigned int _rsvd_28_31:4;
-       } s;
-};
-
-/* ========================================================================= */
-/*                   CSR_G3_EXT_IRQ_GEN                                      */
-/* ========================================================================= */
+static inline void numachip2_write32_lcsr(unsigned long offset, u32 val)
+{
+       writel(val, numachip2_lcsr_address(offset));
+}
 
-#define CSR_G3_EXT_IRQ_GEN (0x030 + (3 << 12))
-union numachip_csr_g3_ext_irq_gen {
-       unsigned int v;
-       struct numachip_csr_g3_ext_irq_gen_s {
-               unsigned int _vector:8;
-               unsigned int _msgtype:3;
-               unsigned int _index:5;
-               unsigned int _destination_apic_id:16;
-       } s;
-};
-
-/* ========================================================================= */
-/*                   CSR_G3_EXT_IRQ_STATUS                                   */
-/* ========================================================================= */
-
-#define CSR_G3_EXT_IRQ_STATUS (0x034 + (3 << 12))
-union numachip_csr_g3_ext_irq_status {
-       unsigned int v;
-       struct numachip_csr_g3_ext_irq_status_s {
-               unsigned int _result:32;
-       } s;
-};
-
-/* ========================================================================= */
-/*                   CSR_G3_EXT_IRQ_DEST                                     */
-/* ========================================================================= */
-
-#define CSR_G3_EXT_IRQ_DEST (0x038 + (3 << 12))
-union numachip_csr_g3_ext_irq_dest {
-       unsigned int v;
-       struct numachip_csr_g3_ext_irq_dest_s {
-               unsigned int _irq:8;
-               unsigned int _rsvd_8_31:24;
-       } s;
-};
-
-/* ========================================================================= */
-/*                   CSR_G3_NC_ATT_MAP_SELECT                                */
-/* ========================================================================= */
-
-#define CSR_G3_NC_ATT_MAP_SELECT (0x7fc + (3 << 12))
-union numachip_csr_g3_nc_att_map_select {
-       unsigned int v;
-       struct numachip_csr_g3_nc_att_map_select_s {
-               unsigned int _upper_address_bits:4;
-               unsigned int _select_ram:4;
-               unsigned int _rsvd_8_31:24;
-       } s;
-};
-
-/* ========================================================================= */
-/*                   CSR_G3_NC_ATT_MAP_SELECT_0-255                          */
-/* ========================================================================= */
-
-#define CSR_G3_NC_ATT_MAP_SELECT_0 (0x800 + (3 << 12))
+static inline void numachip2_write64_lcsr(unsigned long offset, u64 val)
+{
+       writeq(val, numachip2_lcsr_address(offset));
+}
 
-#endif /* _ASM_X86_NUMACHIP_NUMACHIP_CSR_H */
+static inline unsigned int numachip2_timer(void)
+{
+       return (smp_processor_id() % 48) << 6;
+}
 
+#endif /* _ASM_X86_NUMACHIP_NUMACHIP_CSR_H */
index 4edd53b79a81aa3054df42137dbfc854ffe124ff..4928cf0d5af0fefef7240a6112c67f19e42059d3 100644 (file)
@@ -26,9 +26,6 @@
 #define MCE_STACK 4
 #define N_EXCEPTION_STACKS 4  /* hw limit: 7 */
 
-#define PUD_PAGE_SIZE          (_AC(1, UL) << PUD_SHIFT)
-#define PUD_PAGE_MASK          (~(PUD_PAGE_SIZE-1))
-
 /*
  * Set __PAGE_OFFSET to the most negative possible address +
  * PGDIR_SIZE*16 (pgd slot 272).  The gap is to allow a space for a
index c7c712f2648b001b84f06d7ef683239fd09dd2cc..c5b7fb2774d08e17bad6966933e2d753c6de92ec 100644 (file)
@@ -20,6 +20,9 @@
 #define PMD_PAGE_SIZE          (_AC(1, UL) << PMD_SHIFT)
 #define PMD_PAGE_MASK          (~(PMD_PAGE_SIZE-1))
 
+#define PUD_PAGE_SIZE          (_AC(1, UL) << PUD_SHIFT)
+#define PUD_PAGE_MASK          (~(PUD_PAGE_SIZE-1))
+
 #define HPAGE_SHIFT            PMD_SHIFT
 #define HPAGE_SIZE             (_AC(1,UL) << HPAGE_SHIFT)
 #define HPAGE_MASK             (~(HPAGE_SIZE - 1))
index 867da5bbb4a33df1d268d3f282cda30d0fdf671d..c0b41f111a9a4d19eac4b05723b33e6fcd2893c1 100644 (file)
 #include <asm/x86_init.h>
 
 void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd);
+void ptdump_walk_pgd_level_checkwx(void);
+
+#ifdef CONFIG_DEBUG_WX
+#define debug_checkwx() ptdump_walk_pgd_level_checkwx()
+#else
+#define debug_checkwx() do { } while (0)
+#endif
 
 /*
  * ZERO_PAGE is a global shared page that is always zero: used
@@ -142,12 +149,12 @@ static inline unsigned long pte_pfn(pte_t pte)
 
 static inline unsigned long pmd_pfn(pmd_t pmd)
 {
-       return (pmd_val(pmd) & PTE_PFN_MASK) >> PAGE_SHIFT;
+       return (pmd_val(pmd) & pmd_pfn_mask(pmd)) >> PAGE_SHIFT;
 }
 
 static inline unsigned long pud_pfn(pud_t pud)
 {
-       return (pud_val(pud) & PTE_PFN_MASK) >> PAGE_SHIFT;
+       return (pud_val(pud) & pud_pfn_mask(pud)) >> PAGE_SHIFT;
 }
 
 #define pte_page(pte)  pfn_to_page(pte_pfn(pte))
@@ -379,7 +386,9 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
        return __pgprot(preservebits | addbits);
 }
 
-#define pte_pgprot(x) __pgprot(pte_flags(x) & PTE_FLAGS_MASK)
+#define pte_pgprot(x) __pgprot(pte_flags(x))
+#define pmd_pgprot(x) __pgprot(pmd_flags(x))
+#define pud_pgprot(x) __pgprot(pud_flags(x))
 
 #define canon_pgprot(p) __pgprot(massage_pgprot(p))
 
@@ -502,14 +511,15 @@ static inline int pmd_none(pmd_t pmd)
 
 static inline unsigned long pmd_page_vaddr(pmd_t pmd)
 {
-       return (unsigned long)__va(pmd_val(pmd) & PTE_PFN_MASK);
+       return (unsigned long)__va(pmd_val(pmd) & pmd_pfn_mask(pmd));
 }
 
 /*
  * Currently stuck as a macro due to indirect forward reference to
  * linux/mmzone.h's __section_mem_map_addr() definition:
  */
-#define pmd_page(pmd)  pfn_to_page((pmd_val(pmd) & PTE_PFN_MASK) >> PAGE_SHIFT)
+#define pmd_page(pmd)          \
+       pfn_to_page((pmd_val(pmd) & pmd_pfn_mask(pmd)) >> PAGE_SHIFT)
 
 /*
  * the pmd page can be thought of an array like this: pmd_t[PTRS_PER_PMD]
@@ -570,14 +580,15 @@ static inline int pud_present(pud_t pud)
 
 static inline unsigned long pud_page_vaddr(pud_t pud)
 {
-       return (unsigned long)__va((unsigned long)pud_val(pud) & PTE_PFN_MASK);
+       return (unsigned long)__va(pud_val(pud) & pud_pfn_mask(pud));
 }
 
 /*
  * Currently stuck as a macro due to indirect forward reference to
  * linux/mmzone.h's __section_mem_map_addr() definition:
  */
-#define pud_page(pud)          pfn_to_page(pud_val(pud) >> PAGE_SHIFT)
+#define pud_page(pud)          \
+       pfn_to_page((pud_val(pud) & pud_pfn_mask(pud)) >> PAGE_SHIFT)
 
 /* Find an entry in the second-level page table.. */
 static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address)
index 13f310bfc09a754bfa3c69305476421e7dd67706..dd5b0aa9dd2f93a01b554029ebc243aa56ae91d3 100644 (file)
@@ -209,10 +209,10 @@ enum page_cache_mode {
 
 #include <linux/types.h>
 
-/* PTE_PFN_MASK extracts the PFN from a (pte|pmd|pud|pgd)val_t */
+/* Extracts the PFN from a (pte|pmd|pud|pgd)val_t of a 4KB page */
 #define PTE_PFN_MASK           ((pteval_t)PHYSICAL_PAGE_MASK)
 
-/* PTE_FLAGS_MASK extracts the flags from a (pte|pmd|pud|pgd)val_t */
+/* Extracts the flags from a (pte|pmd|pud|pgd)val_t of a 4KB page */
 #define PTE_FLAGS_MASK         (~PTE_PFN_MASK)
 
 typedef struct pgprot { pgprotval_t pgprot; } pgprot_t;
@@ -276,14 +276,46 @@ static inline pmdval_t native_pmd_val(pmd_t pmd)
 }
 #endif
 
+static inline pudval_t pud_pfn_mask(pud_t pud)
+{
+       if (native_pud_val(pud) & _PAGE_PSE)
+               return PUD_PAGE_MASK & PHYSICAL_PAGE_MASK;
+       else
+               return PTE_PFN_MASK;
+}
+
+static inline pudval_t pud_flags_mask(pud_t pud)
+{
+       if (native_pud_val(pud) & _PAGE_PSE)
+               return ~(PUD_PAGE_MASK & (pudval_t)PHYSICAL_PAGE_MASK);
+       else
+               return ~PTE_PFN_MASK;
+}
+
 static inline pudval_t pud_flags(pud_t pud)
 {
-       return native_pud_val(pud) & PTE_FLAGS_MASK;
+       return native_pud_val(pud) & pud_flags_mask(pud);
+}
+
+static inline pmdval_t pmd_pfn_mask(pmd_t pmd)
+{
+       if (native_pmd_val(pmd) & _PAGE_PSE)
+               return PMD_PAGE_MASK & PHYSICAL_PAGE_MASK;
+       else
+               return PTE_PFN_MASK;
+}
+
+static inline pmdval_t pmd_flags_mask(pmd_t pmd)
+{
+       if (native_pmd_val(pmd) & _PAGE_PSE)
+               return ~(PMD_PAGE_MASK & (pmdval_t)PHYSICAL_PAGE_MASK);
+       else
+               return ~PTE_PFN_MASK;
 }
 
 static inline pmdval_t pmd_flags(pmd_t pmd)
 {
-       return native_pmd_val(pmd) & PTE_FLAGS_MASK;
+       return native_pmd_val(pmd) & pmd_flags_mask(pmd);
 }
 
 static inline pte_t native_make_pte(pteval_t val)
index b12f81022a6b2c574f9c037e616f90373502c24e..01bcde84d3e40fb36e16625f0c1024f1291bb9ae 100644 (file)
@@ -30,12 +30,9 @@ static __always_inline void preempt_count_set(int pc)
 /*
  * must be macros to avoid header recursion hell
  */
-#define init_task_preempt_count(p) do { \
-       task_thread_info(p)->saved_preempt_count = PREEMPT_DISABLED; \
-} while (0)
+#define init_task_preempt_count(p) do { } while (0)
 
 #define init_idle_preempt_count(p, cpu) do { \
-       task_thread_info(p)->saved_preempt_count = PREEMPT_ENABLED; \
        per_cpu(__preempt_count, (cpu)) = PREEMPT_ENABLED; \
 } while (0)
 
index 19577dd325fa04443089897863cf59944a78354b..67522256c7ffaf610aa70ef885bd8df584d1bbbd 100644 (file)
@@ -11,7 +11,7 @@ struct vm86;
 #include <asm/math_emu.h>
 #include <asm/segment.h>
 #include <asm/types.h>
-#include <asm/sigcontext.h>
+#include <uapi/asm/sigcontext.h>
 #include <asm/current.h>
 #include <asm/cpufeature.h>
 #include <asm/page.h>
@@ -556,12 +556,12 @@ static inline unsigned int cpuid_edx(unsigned int op)
 }
 
 /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */
-static inline void rep_nop(void)
+static __always_inline void rep_nop(void)
 {
        asm volatile("rep; nop" ::: "memory");
 }
 
-static inline void cpu_relax(void)
+static __always_inline void cpu_relax(void)
 {
        rep_nop();
 }
index 9dfce4e0417d92adc623d32ff93f67109316b451..e6cd2c489dbb9fe87d1c6c331815bd5cac963e13 100644 (file)
@@ -1,79 +1,8 @@
 #ifndef _ASM_X86_SIGCONTEXT_H
 #define _ASM_X86_SIGCONTEXT_H
 
-#include <uapi/asm/sigcontext.h>
-
-#ifdef __i386__
-struct sigcontext {
-       unsigned short gs, __gsh;
-       unsigned short fs, __fsh;
-       unsigned short es, __esh;
-       unsigned short ds, __dsh;
-       unsigned long di;
-       unsigned long si;
-       unsigned long bp;
-       unsigned long sp;
-       unsigned long bx;
-       unsigned long dx;
-       unsigned long cx;
-       unsigned long ax;
-       unsigned long trapno;
-       unsigned long err;
-       unsigned long ip;
-       unsigned short cs, __csh;
-       unsigned long flags;
-       unsigned long sp_at_signal;
-       unsigned short ss, __ssh;
+/* This is a legacy header - all kernel code includes <uapi/asm/sigcontext.h> directly. */
 
-       /*
-        * fpstate is really (struct _fpstate *) or (struct _xstate *)
-        * depending on the FP_XSTATE_MAGIC1 encoded in the SW reserved
-        * bytes of (struct _fpstate) and FP_XSTATE_MAGIC2 present at the end
-        * of extended memory layout. See comments at the definition of
-        * (struct _fpx_sw_bytes)
-        */
-       void __user *fpstate;           /* zero when no FPU/extended context */
-       unsigned long oldmask;
-       unsigned long cr2;
-};
-#else /* __i386__ */
-struct sigcontext {
-       unsigned long r8;
-       unsigned long r9;
-       unsigned long r10;
-       unsigned long r11;
-       unsigned long r12;
-       unsigned long r13;
-       unsigned long r14;
-       unsigned long r15;
-       unsigned long di;
-       unsigned long si;
-       unsigned long bp;
-       unsigned long bx;
-       unsigned long dx;
-       unsigned long ax;
-       unsigned long cx;
-       unsigned long sp;
-       unsigned long ip;
-       unsigned long flags;
-       unsigned short cs;
-       unsigned short gs;
-       unsigned short fs;
-       unsigned short __pad0;
-       unsigned long err;
-       unsigned long trapno;
-       unsigned long oldmask;
-       unsigned long cr2;
+#include <uapi/asm/sigcontext.h>
 
-       /*
-        * fpstate is really (struct _fpstate *) or (struct _xstate *)
-        * depending on the FP_XSTATE_MAGIC1 encoded in the SW reserved
-        * bytes of (struct _fpstate) and FP_XSTATE_MAGIC2 present at the end
-        * of extended memory layout. See comments at the definition of
-        * (struct _fpx_sw_bytes)
-        */
-       void __user *fpstate;           /* zero when no FPU/extended context */
-       unsigned long reserved1[8];
-};
-#endif /* !__i386__ */
 #endif /* _ASM_X86_SIGCONTEXT_H */
index 1f3175bb994eb3480befe74ad1476c7df42ca02c..34edd1650baed6832decb9dc97a9cbe32be5a3d0 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _ASM_X86_SIGFRAME_H
 #define _ASM_X86_SIGFRAME_H
 
-#include <asm/sigcontext.h>
+#include <uapi/asm/sigcontext.h>
 #include <asm/siginfo.h>
 #include <asm/ucontext.h>
 #include <linux/compat.h>
@@ -9,8 +9,6 @@
 #ifdef CONFIG_X86_32
 #define sigframe_ia32          sigframe
 #define rt_sigframe_ia32       rt_sigframe
-#define sigcontext_ia32                sigcontext
-#define _fpstate_ia32          _fpstate
 #define ucontext_ia32          ucontext
 #else /* !CONFIG_X86_32 */
 
@@ -24,7 +22,7 @@
 struct sigframe_ia32 {
        u32 pretcode;
        int sig;
-       struct sigcontext_ia32 sc;
+       struct sigcontext_32 sc;
        /*
         * fpstate is unused. fpstate is moved/allocated after
         * retcode[] below. This movement allows to have the FP state and the
@@ -33,7 +31,7 @@ struct sigframe_ia32 {
         * the offset of extramask[] in the sigframe and thus prevent any
         * legacy application accessing/modifying it.
         */
-       struct _fpstate_ia32 fpstate_unused;
+       struct _fpstate_32 fpstate_unused;
 #ifdef CONFIG_IA32_EMULATION
        unsigned int extramask[_COMPAT_NSIG_WORDS-1];
 #else /* !CONFIG_IA32_EMULATION */
index c481be78fcf14b50388ef37d58e1af930bc23677..2138c9ae19eeb9ffdede173ecbe144a74ab38a7e 100644 (file)
@@ -34,7 +34,7 @@ extern void do_signal(struct pt_regs *regs);
 
 #define __ARCH_HAS_SA_RESTORER
 
-#include <asm/sigcontext.h>
+#include <uapi/asm/sigcontext.h>
 
 #ifdef __i386__
 
index d7f3b3b78ac313ca8a871d3a53b3d118850a657d..751bf4b7bf114da12231a56f4217c2583ddeafb2 100644 (file)
@@ -79,12 +79,12 @@ do {                                                                        \
 #else /* CONFIG_X86_32 */
 
 /* frame pointer must be last for get_wchan */
-#define SAVE_CONTEXT    "pushf ; pushq %%rbp ; movq %%rsi,%%rbp\n\t"
-#define RESTORE_CONTEXT "movq %%rbp,%%rsi ; popq %%rbp ; popf\t"
+#define SAVE_CONTEXT    "pushq %%rbp ; movq %%rsi,%%rbp\n\t"
+#define RESTORE_CONTEXT "movq %%rbp,%%rsi ; popq %%rbp\t"
 
 #define __EXTRA_CLOBBER  \
        , "rcx", "rbx", "rdx", "r8", "r9", "r10", "r11", \
-         "r12", "r13", "r14", "r15"
+         "r12", "r13", "r14", "r15", "flags"
 
 #ifdef CONFIG_CC_STACKPROTECTOR
 #define __switch_canary                                                          \
@@ -100,7 +100,11 @@ do {                                                                       \
 #define __switch_canary_iparam
 #endif /* CC_STACKPROTECTOR */
 
-/* Save restore flags to clear handle leaking NT */
+/*
+ * There is no need to save or restore flags, because flags are always
+ * clean in kernel mode, with the possible exception of IOPL.  Kernel IOPL
+ * has no effect.
+ */
 #define switch_to(prev, next, last) \
        asm volatile(SAVE_CONTEXT                                         \
             "movq %%rsp,%P[threadrsp](%[prev])\n\t" /* save RSP */       \
index d6a756ae04c8b999ed379159aa03029b17a38139..999b7cd2e78c7ffe21526fd2b9715b3486216907 100644 (file)
 #include <asm/thread_info.h>   /* for TS_COMPAT */
 #include <asm/unistd.h>
 
-typedef void (*sys_call_ptr_t)(void);
+typedef asmlinkage long (*sys_call_ptr_t)(unsigned long, unsigned long,
+                                         unsigned long, unsigned long,
+                                         unsigned long, unsigned long);
 extern const sys_call_ptr_t sys_call_table[];
 
+#if defined(CONFIG_X86_32)
+#define ia32_sys_call_table sys_call_table
+#define __NR_syscall_compat_max __NR_syscall_max
+#define IA32_NR_syscalls NR_syscalls
+#endif
+
+#if defined(CONFIG_IA32_EMULATION)
+extern const sys_call_ptr_t ia32_sys_call_table[];
+#endif
+
 /*
  * Only the low 32 bits of orig_ax are meaningful, so we return int.
  * This importantly ignores the high bits on 64-bit, so comparisons
index 8afdc3e44247c3a53cc5513ddb0d8f2f84340a4c..c7b551028740f18a5360070a6ccb5dc9ad714c5e 100644 (file)
@@ -57,9 +57,7 @@ struct thread_info {
        __u32                   flags;          /* low level flags */
        __u32                   status;         /* thread synchronous flags */
        __u32                   cpu;            /* current CPU */
-       int                     saved_preempt_count;
        mm_segment_t            addr_limit;
-       void __user             *sysenter_return;
        unsigned int            sig_on_uaccess_error:1;
        unsigned int            uaccess_err:1;  /* uaccess failed */
 };
@@ -69,7 +67,6 @@ struct thread_info {
        .task           = &tsk,                 \
        .flags          = 0,                    \
        .cpu            = 0,                    \
-       .saved_preempt_count = INIT_PREEMPT_COUNT,      \
        .addr_limit     = KERNEL_DS,            \
 }
 
index 173dd3ba108cb1200e83acd33e9deab854d62e54..0f492fc50bcec34368a1869995496b64a476cd39 100644 (file)
@@ -11,7 +11,7 @@
 TRACE_EVENT(mpx_bounds_register_exception,
 
        TP_PROTO(void *addr_referenced,
-                const struct bndreg *bndreg),
+                const struct mpx_bndreg *bndreg),
        TP_ARGS(addr_referenced, bndreg),
 
        TP_STRUCT__entry(
@@ -44,7 +44,7 @@ TRACE_EVENT(mpx_bounds_register_exception,
 
 TRACE_EVENT(bounds_exception_mpx,
 
-       TP_PROTO(const struct bndcsr *bndcsr),
+       TP_PROTO(const struct mpx_bndcsr *bndcsr),
        TP_ARGS(bndcsr),
 
        TP_STRUCT__entry(
@@ -116,7 +116,8 @@ TRACE_EVENT(mpx_new_bounds_table,
 /*
  * This gets used outside of MPX-specific code, so we need a stub.
  */
-static inline void trace_bounds_exception_mpx(const struct bndcsr *bndcsr)
+static inline
+void trace_bounds_exception_mpx(const struct mpx_bndcsr *bndcsr)
 {
 }
 
index a8df874f3e8825b0ea6bde91909b8b3d7b380ba7..09b1b0ab94b7653f7ed7019eb7e35b518582f825 100644 (file)
@@ -51,13 +51,13 @@ static inline bool __chk_range_not_ok(unsigned long addr, unsigned long size, un
         * limit, not add it to the address).
         */
        if (__builtin_constant_p(size))
-               return addr > limit - size;
+               return unlikely(addr > limit - size);
 
        /* Arbitrary sizes? Be careful about overflow */
        addr += size;
-       if (addr < size)
+       if (unlikely(addr < size))
                return true;
-       return addr > limit;
+       return unlikely(addr > limit);
 }
 
 #define __range_not_ok(addr, size, limit)                              \
@@ -182,7 +182,7 @@ __typeof__(__builtin_choose_expr(sizeof(x) > sizeof(0UL), 0ULL, 0UL))
                     : "=a" (__ret_gu), "=r" (__val_gu)                 \
                     : "0" (ptr), "i" (sizeof(*(ptr))));                \
        (x) = (__force __typeof__(*(ptr))) __val_gu;                    \
-       __ret_gu;                                                       \
+       __builtin_expect(__ret_gu, 0);                                  \
 })
 
 #define __put_user_x(size, x, ptr, __ret_pu)                   \
@@ -278,7 +278,7 @@ extern void __put_user_8(void);
                __put_user_x(X, __pu_val, ptr, __ret_pu);       \
                break;                                          \
        }                                                       \
-       __ret_pu;                                               \
+       __builtin_expect(__ret_pu, 0);                          \
 })
 
 #define __put_user_size(x, ptr, size, retval, errret)                  \
@@ -401,7 +401,7 @@ do {                                                                        \
 ({                                                             \
        int __pu_err;                                           \
        __put_user_size((x), (ptr), (size), __pu_err, -EFAULT); \
-       __pu_err;                                               \
+       __builtin_expect(__pu_err, 0);                          \
 })
 
 #define __get_user_nocheck(x, ptr, size)                               \
@@ -410,7 +410,7 @@ do {                                                                        \
        unsigned long __gu_val;                                         \
        __get_user_size(__gu_val, (ptr), (size), __gu_err, -EFAULT);    \
        (x) = (__force __typeof__(*(ptr)))__gu_val;                     \
-       __gu_err;                                                       \
+       __builtin_expect(__gu_err, 0);                                  \
 })
 
 /* FIXME: this hack is definitely wrong -AK */
index a00ad8f2a6578f61db3e322013b93888fbef4afa..ea7074784cc4717935e579610b814971127f6102 100644 (file)
@@ -609,7 +609,7 @@ struct uv_cpu_nmi_s {
 
 DECLARE_PER_CPU(struct uv_cpu_nmi_s, uv_cpu_nmi);
 
-#define uv_hub_nmi                     (uv_cpu_nmi.hub)
+#define uv_hub_nmi                     this_cpu_read(uv_cpu_nmi.hub)
 #define uv_cpu_nmi_per(cpu)            (per_cpu(uv_cpu_nmi, cpu))
 #define uv_hub_nmi_per(cpu)            (uv_cpu_nmi_per(cpu).hub)
 
index 8021bd28c0f13277a79cdfce15c7b842cb0dcd7e..756de9190aecad5d7939ee7189703a9177f4da25 100644 (file)
@@ -26,7 +26,7 @@ struct vdso_image {
        long sym___kernel_sigreturn;
        long sym___kernel_rt_sigreturn;
        long sym___kernel_vsyscall;
-       long sym_VDSO32_SYSENTER_RETURN;
+       long sym_int80_landing_pad;
 };
 
 #ifdef CONFIG_X86_64
@@ -38,13 +38,7 @@ extern const struct vdso_image vdso_image_x32;
 #endif
 
 #if defined CONFIG_X86_32 || defined CONFIG_COMPAT
-extern const struct vdso_image vdso_image_32_int80;
-#ifdef CONFIG_COMPAT
-extern const struct vdso_image vdso_image_32_syscall;
-#endif
-extern const struct vdso_image vdso_image_32_sysenter;
-
-extern const struct vdso_image *selected_vdso32;
+extern const struct vdso_image vdso_image_32;
 #endif
 
 extern void __init init_vdso_image(const struct vdso_image *image);
index 76880ede9a359b3d4f433aff9beaaea6a17543db..03429da2fa80145bbe11e56587e2c7868f467d5d 100644 (file)
@@ -2,7 +2,7 @@
 #define _UAPI_ASM_X86_MCE_H
 
 #include <linux/types.h>
-#include <asm/ioctls.h>
+#include <linux/ioctl.h>
 
 /* Fields are zero when not available */
 struct mce {
index 40836a9a7250c99a16dc5969057177f66aa57186..d485232f1e9f9a1b0f0ba4d44bf8382c39175757 100644 (file)
 #ifndef _UAPI_ASM_X86_SIGCONTEXT_H
 #define _UAPI_ASM_X86_SIGCONTEXT_H
 
+/*
+ * Linux signal context definitions. The sigcontext includes a complex
+ * hierarchy of CPU and FPU state, available to user-space (on the stack) when
+ * a signal handler is executed.
+ *
+ * As over the years this ABI grew from its very simple roots towards
+ * supporting more and more CPU state organically, some of the details (which
+ * were rather clever hacks back in the days) became a bit quirky by today.
+ *
+ * The current ABI includes flexible provisions for future extensions, so we
+ * won't have to grow new quirks for quite some time. Promise!
+ */
+
 #include <linux/compiler.h>
 #include <linux/types.h>
 
-#define FP_XSTATE_MAGIC1       0x46505853U
-#define FP_XSTATE_MAGIC2       0x46505845U
-#define FP_XSTATE_MAGIC2_SIZE  sizeof(FP_XSTATE_MAGIC2)
+#define FP_XSTATE_MAGIC1               0x46505853U
+#define FP_XSTATE_MAGIC2               0x46505845U
+#define FP_XSTATE_MAGIC2_SIZE          sizeof(FP_XSTATE_MAGIC2)
 
 /*
- * bytes 464..511 in the current 512byte layout of fxsave/fxrstor frame
- * are reserved for SW usage. On cpu's supporting xsave/xrstor, these bytes
- * are used to extended the fpstate pointer in the sigcontext, which now
- * includes the extended state information along with fpstate information.
+ * Bytes 464..511 in the current 512-byte layout of the FXSAVE/FXRSTOR frame
+ * are reserved for SW usage. On CPUs supporting XSAVE/XRSTOR, these bytes are
+ * used to extend the fpstate pointer in the sigcontext, which now includes the
+ * extended state information along with fpstate information.
+ *
+ * If sw_reserved.magic1 == FP_XSTATE_MAGIC1 then there's a
+ * sw_reserved.extended_size bytes large extended context area present. (The
+ * last 32-bit word of this extended area (at the
+ * fpstate+extended_size-FP_XSTATE_MAGIC2_SIZE address) is set to
+ * FP_XSTATE_MAGIC2 so that you can sanity check your size calculations.)
  *
- * Presence of FP_XSTATE_MAGIC1 at the beginning of this SW reserved
- * area and FP_XSTATE_MAGIC2 at the end of memory layout
- * (extended_size - FP_XSTATE_MAGIC2_SIZE) indicates the presence of the
- * extended state information in the memory layout pointed by the fpstate
- * pointer in sigcontext.
+ * This extended area typically grows with newer CPUs that have larger and
+ * larger XSAVE areas.
  */
 struct _fpx_sw_bytes {
-       __u32 magic1;           /* FP_XSTATE_MAGIC1 */
-       __u32 extended_size;    /* total size of the layout referred by
-                                * fpstate pointer in the sigcontext.
-                                */
-       __u64 xfeatures;
-                               /* feature bit mask (including fp/sse/extended
-                                * state) that is present in the memory
-                                * layout.
-                                */
-       __u32 xstate_size;      /* actual xsave state size, based on the
-                                * features saved in the layout.
-                                * 'extended_size' will be greater than
-                                * 'xstate_size'.
-                                */
-       __u32 padding[7];       /*  for future use. */
+       /*
+        * If set to FP_XSTATE_MAGIC1 then this is an xstate context.
+        * 0 if a legacy frame.
+        */
+       __u32                           magic1;
+
+       /*
+        * Total size of the fpstate area:
+        *
+        *  - if magic1 == 0 then it's sizeof(struct _fpstate)
+        *  - if magic1 == FP_XSTATE_MAGIC1 then it's sizeof(struct _xstate)
+        *    plus extensions (if any)
+        */
+       __u32                           extended_size;
+
+       /*
+        * Feature bit mask (including FP/SSE/extended state) that is present
+        * in the memory layout:
+        */
+       __u64                           xfeatures;
+
+       /*
+        * Actual XSAVE state size, based on the xfeatures saved in the layout.
+        * 'extended_size' is greater than 'xstate_size':
+        */
+       __u32                           xstate_size;
+
+       /* For future use: */
+       __u32                           padding[7];
 };
 
-#ifdef __i386__
 /*
- * As documented in the iBCS2 standard..
- *
- * The first part of "struct _fpstate" is just the normal i387
- * hardware setup, the extra "status" word is used to save the
- * coprocessor status word before entering the handler.
+ * As documented in the iBCS2 standard:
  *
- * Pentium III FXSR, SSE support
- *     Gareth Hughes <gareth@valinux.com>, May 2000
+ * The first part of "struct _fpstate" is just the normal i387 hardware setup,
+ * the extra "status" word is used to save the coprocessor status word before
+ * entering the handler.
  *
- * The FPU state data structure has had to grow to accommodate the
- * extended FPU state required by the Streaming SIMD Extensions.
- * There is no documented standard to accomplish this at the moment.
+ * The FPU state data structure has had to grow to accommodate the extended FPU
+ * state required by the Streaming SIMD Extensions.  There is no documented
+ * standard to accomplish this at the moment.
  */
+
+/* 10-byte legacy floating point register: */
 struct _fpreg {
-       unsigned short significand[4];
-       unsigned short exponent;
+       __u16                           significand[4];
+       __u16                           exponent;
 };
 
+/* 16-byte floating point register: */
 struct _fpxreg {
-       unsigned short significand[4];
-       unsigned short exponent;
-       unsigned short padding[3];
+       __u16                           significand[4];
+       __u16                           exponent;
+       __u16                           padding[3];
 };
 
+/* 16-byte XMM register: */
 struct _xmmreg {
-       unsigned long element[4];
+       __u32                           element[4];
 };
 
-struct _fpstate {
-       /* Regular FPU environment */
-       unsigned long   cw;
-       unsigned long   sw;
-       unsigned long   tag;
-       unsigned long   ipoff;
-       unsigned long   cssel;
-       unsigned long   dataoff;
-       unsigned long   datasel;
-       struct _fpreg   _st[8];
-       unsigned short  status;
-       unsigned short  magic;          /* 0xffff = regular FPU data only */
+#define X86_FXSR_MAGIC                 0x0000
+
+/*
+ * The 32-bit FPU frame:
+ */
+struct _fpstate_32 {
+       /* Legacy FPU environment: */
+       __u32                           cw;
+       __u32                           sw;
+       __u32                           tag;
+       __u32                           ipoff;
+       __u32                           cssel;
+       __u32                           dataoff;
+       __u32                           datasel;
+       struct _fpreg                   _st[8];
+       __u16                           status;
+       __u16                           magic;          /* 0xffff: regular FPU data only */
+                                                       /* 0x0000: FXSR FPU data */
 
        /* FXSR FPU environment */
-       unsigned long   _fxsr_env[6];   /* FXSR FPU env is ignored */
-       unsigned long   mxcsr;
-       unsigned long   reserved;
-       struct _fpxreg  _fxsr_st[8];    /* FXSR FPU reg data is ignored */
-       struct _xmmreg  _xmm[8];
-       unsigned long   padding1[44];
+       __u32                           _fxsr_env[6];   /* FXSR FPU env is ignored */
+       __u32                           mxcsr;
+       __u32                           reserved;
+       struct _fpxreg                  _fxsr_st[8];    /* FXSR FPU reg data is ignored */
+       struct _xmmreg                  _xmm[8];        /* First 8 XMM registers */
+       union {
+               __u32                   padding1[44];   /* Second 8 XMM registers plus padding */
+               __u32                   padding[44];    /* Alias name for old user-space */
+       };
 
        union {
-               unsigned long   padding2[12];
-               struct _fpx_sw_bytes sw_reserved; /* represents the extended
-                                                  * state info */
+               __u32                   padding2[12];
+               struct _fpx_sw_bytes    sw_reserved;    /* Potential extended state is encoded here */
        };
 };
 
-#define X86_FXSR_MAGIC         0x0000
-
-#ifndef __KERNEL__
 /*
- * User-space might still rely on the old definition:
+ * The 64-bit FPU frame. (FXSAVE format and later)
+ *
+ * Note1: If sw_reserved.magic1 == FP_XSTATE_MAGIC1 then the structure is
+ *        larger: 'struct _xstate'. Note that 'struct _xstate' embedds
+ *        'struct _fpstate' so that you can always assume the _fpstate portion
+ *        exists so that you can check the magic value.
+ *
+ * Note2: Reserved fields may someday contain valuable data. Always
+ *       save/restore them when you change signal frames.
  */
-struct sigcontext {
-       unsigned short gs, __gsh;
-       unsigned short fs, __fsh;
-       unsigned short es, __esh;
-       unsigned short ds, __dsh;
-       unsigned long edi;
-       unsigned long esi;
-       unsigned long ebp;
-       unsigned long esp;
-       unsigned long ebx;
-       unsigned long edx;
-       unsigned long ecx;
-       unsigned long eax;
-       unsigned long trapno;
-       unsigned long err;
-       unsigned long eip;
-       unsigned short cs, __csh;
-       unsigned long eflags;
-       unsigned long esp_at_signal;
-       unsigned short ss, __ssh;
-       struct _fpstate __user *fpstate;
-       unsigned long oldmask;
-       unsigned long cr2;
-};
-#endif /* !__KERNEL__ */
-
-#else /* __i386__ */
-
-/* FXSAVE frame */
-/* Note: reserved1/2 may someday contain valuable data. Always save/restore
-   them when you change signal frames. */
-struct _fpstate {
-       __u16   cwd;
-       __u16   swd;
-       __u16   twd;            /* Note this is not the same as the
-                                  32bit/x87/FSAVE twd */
-       __u16   fop;
-       __u64   rip;
-       __u64   rdp;
-       __u32   mxcsr;
-       __u32   mxcsr_mask;
-       __u32   st_space[32];   /* 8*16 bytes for each FP-reg */
-       __u32   xmm_space[64];  /* 16*16 bytes for each XMM-reg  */
-       __u32   reserved2[12];
+struct _fpstate_64 {
+       __u16                           cwd;
+       __u16                           swd;
+       /* Note this is not the same as the 32-bit/x87/FSAVE twd: */
+       __u16                           twd;
+       __u16                           fop;
+       __u64                           rip;
+       __u64                           rdp;
+       __u32                           mxcsr;
+       __u32                           mxcsr_mask;
+       __u32                           st_space[32];   /*  8x  FP registers, 16 bytes each */
+       __u32                           xmm_space[64];  /* 16x XMM registers, 16 bytes each */
+       __u32                           reserved2[12];
        union {
-               __u32   reserved3[12];
-               struct _fpx_sw_bytes sw_reserved; /* represents the extended
-                                                  * state information */
+               __u32                   reserved3[12];
+               struct _fpx_sw_bytes    sw_reserved;    /* Potential extended state is encoded here */
        };
 };
 
-#ifndef __KERNEL__
-/*
- * User-space might still rely on the old definition:
- */
-struct sigcontext {
-       __u64 r8;
-       __u64 r9;
-       __u64 r10;
-       __u64 r11;
-       __u64 r12;
-       __u64 r13;
-       __u64 r14;
-       __u64 r15;
-       __u64 rdi;
-       __u64 rsi;
-       __u64 rbp;
-       __u64 rbx;
-       __u64 rdx;
-       __u64 rax;
-       __u64 rcx;
-       __u64 rsp;
-       __u64 rip;
-       __u64 eflags;           /* RFLAGS */
-       __u16 cs;
-       __u16 gs;
-       __u16 fs;
-       __u16 __pad0;
-       __u64 err;
-       __u64 trapno;
-       __u64 oldmask;
-       __u64 cr2;
-       struct _fpstate __user *fpstate;        /* zero when no FPU context */
-#ifdef __ILP32__
-       __u32 __fpstate_pad;
+#ifdef __i386__
+# define _fpstate _fpstate_32
+#else
+# define _fpstate _fpstate_64
 #endif
-       __u64 reserved1[8];
-};
-#endif /* !__KERNEL__ */
-
-#endif /* !__i386__ */
 
 struct _header {
-       __u64 xfeatures;
-       __u64 reserved1[2];
-       __u64 reserved2[5];
+       __u64                           xfeatures;
+       __u64                           reserved1[2];
+       __u64                           reserved2[5];
 };
 
 struct _ymmh_state {
-       /* 16 * 16 bytes for each YMMH-reg */
-       __u32 ymmh_space[64];
+       /* 16x YMM registers, 16 bytes each: */
+       __u32                           ymmh_space[64];
 };
 
 /*
- * Extended state pointed by the fpstate pointer in the sigcontext.
- * In addition to the fpstate, information encoded in the xstate_hdr
- * indicates the presence of other extended state information
- * supported by the processor and OS.
+ * Extended state pointed to by sigcontext::fpstate.
+ *
+ * In addition to the fpstate, information encoded in _xstate::xstate_hdr
+ * indicates the presence of other extended state information supported
+ * by the CPU and kernel:
  */
 struct _xstate {
-       struct _fpstate fpstate;
-       struct _header xstate_hdr;
-       struct _ymmh_state ymmh;
-       /* new processor state extensions go here */
+       struct _fpstate                 fpstate;
+       struct _header                  xstate_hdr;
+       struct _ymmh_state              ymmh;
+       /* New processor state extensions go here: */
+};
+
+/*
+ * The 32-bit signal frame:
+ */
+struct sigcontext_32 {
+       __u16                           gs, __gsh;
+       __u16                           fs, __fsh;
+       __u16                           es, __esh;
+       __u16                           ds, __dsh;
+       __u32                           di;
+       __u32                           si;
+       __u32                           bp;
+       __u32                           sp;
+       __u32                           bx;
+       __u32                           dx;
+       __u32                           cx;
+       __u32                           ax;
+       __u32                           trapno;
+       __u32                           err;
+       __u32                           ip;
+       __u16                           cs, __csh;
+       __u32                           flags;
+       __u32                           sp_at_signal;
+       __u16                           ss, __ssh;
+
+       /*
+        * fpstate is really (struct _fpstate *) or (struct _xstate *)
+        * depending on the FP_XSTATE_MAGIC1 encoded in the SW reserved
+        * bytes of (struct _fpstate) and FP_XSTATE_MAGIC2 present at the end
+        * of extended memory layout. See comments at the definition of
+        * (struct _fpx_sw_bytes)
+        */
+       __u32                           fpstate; /* Zero when no FPU/extended context */
+       __u32                           oldmask;
+       __u32                           cr2;
+};
+
+/*
+ * The 64-bit signal frame:
+ */
+struct sigcontext_64 {
+       __u64                           r8;
+       __u64                           r9;
+       __u64                           r10;
+       __u64                           r11;
+       __u64                           r12;
+       __u64                           r13;
+       __u64                           r14;
+       __u64                           r15;
+       __u64                           di;
+       __u64                           si;
+       __u64                           bp;
+       __u64                           bx;
+       __u64                           dx;
+       __u64                           ax;
+       __u64                           cx;
+       __u64                           sp;
+       __u64                           ip;
+       __u64                           flags;
+       __u16                           cs;
+       __u16                           gs;
+       __u16                           fs;
+       __u16                           __pad0;
+       __u64                           err;
+       __u64                           trapno;
+       __u64                           oldmask;
+       __u64                           cr2;
+
+       /*
+        * fpstate is really (struct _fpstate *) or (struct _xstate *)
+        * depending on the FP_XSTATE_MAGIC1 encoded in the SW reserved
+        * bytes of (struct _fpstate) and FP_XSTATE_MAGIC2 present at the end
+        * of extended memory layout. See comments at the definition of
+        * (struct _fpx_sw_bytes)
+        */
+       __u64                           fpstate; /* Zero when no FPU/extended context */
+       __u64                           reserved1[8];
+};
+
+/*
+ * Create the real 'struct sigcontext' type:
+ */
+#ifdef __KERNEL__
+# ifdef __i386__
+#  define sigcontext sigcontext_32
+# else
+#  define sigcontext sigcontext_64
+# endif
+#endif
+
+/*
+ * The old user-space sigcontext definition, just in case user-space still
+ * relies on it. The kernel definition (in asm/sigcontext.h) has unified
+ * field names but otherwise the same layout.
+ */
+#ifndef __KERNEL__
+
+#define _fpstate_ia32                  _fpstate_32
+#define sigcontext_ia32                        sigcontext_32
+
+
+# ifdef __i386__
+struct sigcontext {
+       __u16                           gs, __gsh;
+       __u16                           fs, __fsh;
+       __u16                           es, __esh;
+       __u16                           ds, __dsh;
+       __u32                           edi;
+       __u32                           esi;
+       __u32                           ebp;
+       __u32                           esp;
+       __u32                           ebx;
+       __u32                           edx;
+       __u32                           ecx;
+       __u32                           eax;
+       __u32                           trapno;
+       __u32                           err;
+       __u32                           eip;
+       __u16                           cs, __csh;
+       __u32                           eflags;
+       __u32                           esp_at_signal;
+       __u16                           ss, __ssh;
+       struct _fpstate __user          *fpstate;
+       __u32                           oldmask;
+       __u32                           cr2;
 };
+# else /* __x86_64__: */
+struct sigcontext {
+       __u64                           r8;
+       __u64                           r9;
+       __u64                           r10;
+       __u64                           r11;
+       __u64                           r12;
+       __u64                           r13;
+       __u64                           r14;
+       __u64                           r15;
+       __u64                           rdi;
+       __u64                           rsi;
+       __u64                           rbp;
+       __u64                           rbx;
+       __u64                           rdx;
+       __u64                           rax;
+       __u64                           rcx;
+       __u64                           rsp;
+       __u64                           rip;
+       __u64                           eflags;         /* RFLAGS */
+       __u16                           cs;
+       __u16                           gs;
+       __u16                           fs;
+       __u16                           __pad0;
+       __u64                           err;
+       __u64                           trapno;
+       __u64                           oldmask;
+       __u64                           cr2;
+       struct _fpstate __user          *fpstate;       /* Zero when no FPU context */
+#  ifdef __ILP32__
+       __u32                           __fpstate_pad;
+#  endif
+       __u64                           reserved1[8];
+};
+# endif /* __x86_64__ */
+#endif /* !__KERNEL__ */
 
 #endif /* _UAPI_ASM_X86_SIGCONTEXT_H */
index ad1478c4ae1297b58ff8f1be6bece331769bb3b3..a92b0f0dc09e70ec4f35f26fd25f6063cd6ce56f 100644 (file)
@@ -1,77 +1,8 @@
 #ifndef _ASM_X86_SIGCONTEXT32_H
 #define _ASM_X86_SIGCONTEXT32_H
 
-#include <linux/types.h>
+/* This is a legacy file - all the type definitions are in sigcontext.h: */
 
-/* signal context for 32bit programs. */
-
-#define X86_FXSR_MAGIC         0x0000
-
-struct _fpreg {
-       unsigned short significand[4];
-       unsigned short exponent;
-};
-
-struct _fpxreg {
-       unsigned short significand[4];
-       unsigned short exponent;
-       unsigned short padding[3];
-};
-
-struct _xmmreg {
-       __u32   element[4];
-};
-
-/* FSAVE frame with extensions */
-struct _fpstate_ia32 {
-       /* Regular FPU environment */
-       __u32   cw;
-       __u32   sw;
-       __u32   tag;    /* not compatible to 64bit twd */
-       __u32   ipoff;
-       __u32   cssel;
-       __u32   dataoff;
-       __u32   datasel;
-       struct _fpreg   _st[8];
-       unsigned short  status;
-       unsigned short  magic;          /* 0xffff = regular FPU data only */
-
-       /* FXSR FPU environment */
-       __u32   _fxsr_env[6];
-       __u32   mxcsr;
-       __u32   reserved;
-       struct _fpxreg  _fxsr_st[8];
-       struct _xmmreg  _xmm[8];        /* It's actually 16 */
-       __u32   padding[44];
-       union {
-               __u32 padding2[12];
-               struct _fpx_sw_bytes sw_reserved;
-       };
-};
-
-struct sigcontext_ia32 {
-       unsigned short gs, __gsh;
-       unsigned short fs, __fsh;
-       unsigned short es, __esh;
-       unsigned short ds, __dsh;
-       unsigned int di;
-       unsigned int si;
-       unsigned int bp;
-       unsigned int sp;
-       unsigned int bx;
-       unsigned int dx;
-       unsigned int cx;
-       unsigned int ax;
-       unsigned int trapno;
-       unsigned int err;
-       unsigned int ip;
-       unsigned short cs, __csh;
-       unsigned int flags;
-       unsigned int sp_at_signal;
-       unsigned short ss, __ssh;
-       unsigned int fpstate;           /* really (struct _fpstate_ia32 *) */
-       unsigned int oldmask;
-       unsigned int cr2;
-};
+#include <asm/sigcontext.h>
 
 #endif /* _ASM_X86_SIGCONTEXT32_H */
index 24e94ce454e2363e6ad14ae2e4cff6c4ac492ab4..2f69e3b184f62c2f7e87c6956bc58fbadb711965 100644 (file)
@@ -1431,7 +1431,7 @@ enum {
 };
 static int x2apic_state;
 
-static inline void __x2apic_disable(void)
+static void __x2apic_disable(void)
 {
        u64 msr;
 
@@ -1447,7 +1447,7 @@ static inline void __x2apic_disable(void)
        printk_once(KERN_INFO "x2apic disabled\n");
 }
 
-static inline void __x2apic_enable(void)
+static void __x2apic_enable(void)
 {
        u64 msr;
 
@@ -1807,7 +1807,7 @@ int apic_version[MAX_LOCAL_APIC];
 /*
  * This interrupt should _never_ happen with our APIC/SMP architecture
  */
-static inline void __smp_spurious_interrupt(u8 vector)
+static void __smp_spurious_interrupt(u8 vector)
 {
        u32 v;
 
@@ -1848,7 +1848,7 @@ __visible void smp_trace_spurious_interrupt(struct pt_regs *regs)
 /*
  * This interrupt should never happen with our APIC/SMP architecture
  */
-static inline void __smp_error_interrupt(struct pt_regs *regs)
+static void __smp_error_interrupt(struct pt_regs *regs)
 {
        u32 v;
        u32 i = 0;
index b548fd3b764bd10f6f3b1240b2c774c07e7d6275..38dd5efdd04c33aa58b2b20b19596bcd12e3e4af 100644 (file)
  *
  */
 
-#include <linux/errno.h>
-#include <linux/threads.h>
-#include <linux/cpumask.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/ctype.h>
 #include <linux/init.h>
-#include <linux/hardirq.h>
-#include <linux/delay.h>
 
 #include <asm/numachip/numachip.h>
 #include <asm/numachip/numachip_csr.h>
-#include <asm/smp.h>
-#include <asm/apic.h>
 #include <asm/ipi.h>
 #include <asm/apic_flat_64.h>
 #include <asm/pgtable.h>
+#include <asm/pci_x86.h>
 
-static int numachip_system __read_mostly;
+u8 numachip_system __read_mostly;
+static const struct apic apic_numachip1;
+static const struct apic apic_numachip2;
+static void (*numachip_apic_icr_write)(int apicid, unsigned int val) __read_mostly;
 
-static const struct apic apic_numachip;
-
-static unsigned int get_apic_id(unsigned long x)
+static unsigned int numachip1_get_apic_id(unsigned long x)
 {
        unsigned long value;
        unsigned int id = (x >> 24) & 0xff;
@@ -47,7 +38,7 @@ static unsigned int get_apic_id(unsigned long x)
        return id;
 }
 
-static unsigned long set_apic_id(unsigned int id)
+static unsigned long numachip1_set_apic_id(unsigned int id)
 {
        unsigned long x;
 
@@ -55,9 +46,17 @@ static unsigned long set_apic_id(unsigned int id)
        return x;
 }
 
-static unsigned int read_xapic_id(void)
+static unsigned int numachip2_get_apic_id(unsigned long x)
+{
+       u64 mcfg;
+
+       rdmsrl(MSR_FAM10H_MMIO_CONF_BASE, mcfg);
+       return ((mcfg >> (28 - 8)) & 0xfff00) | (x >> 24);
+}
+
+static unsigned long numachip2_set_apic_id(unsigned int id)
 {
-       return get_apic_id(apic_read(APIC_ID));
+       return id << 24;
 }
 
 static int numachip_apic_id_valid(int apicid)
@@ -68,7 +67,7 @@ static int numachip_apic_id_valid(int apicid)
 
 static int numachip_apic_id_registered(void)
 {
-       return physid_isset(read_xapic_id(), phys_cpu_present_map);
+       return 1;
 }
 
 static int numachip_phys_pkg_id(int initial_apic_id, int index_msb)
@@ -76,36 +75,48 @@ static int numachip_phys_pkg_id(int initial_apic_id, int index_msb)
        return initial_apic_id >> index_msb;
 }
 
-static int numachip_wakeup_secondary(int phys_apicid, unsigned long start_rip)
+static void numachip1_apic_icr_write(int apicid, unsigned int val)
 {
-       union numachip_csr_g3_ext_irq_gen int_gen;
-
-       int_gen.s._destination_apic_id = phys_apicid;
-       int_gen.s._vector = 0;
-       int_gen.s._msgtype = APIC_DM_INIT >> 8;
-       int_gen.s._index = 0;
-
-       write_lcsr(CSR_G3_EXT_IRQ_GEN, int_gen.v);
+       write_lcsr(CSR_G3_EXT_IRQ_GEN, (apicid << 16) | val);
+}
 
-       int_gen.s._msgtype = APIC_DM_STARTUP >> 8;
-       int_gen.s._vector = start_rip >> 12;
+static void numachip2_apic_icr_write(int apicid, unsigned int val)
+{
+       numachip2_write32_lcsr(NUMACHIP2_APIC_ICR, (apicid << 12) | val);
+}
 
-       write_lcsr(CSR_G3_EXT_IRQ_GEN, int_gen.v);
+static int numachip_wakeup_secondary(int phys_apicid, unsigned long start_rip)
+{
+       numachip_apic_icr_write(phys_apicid, APIC_DM_INIT);
+       numachip_apic_icr_write(phys_apicid, APIC_DM_STARTUP |
+               (start_rip >> 12));
 
        return 0;
 }
 
 static void numachip_send_IPI_one(int cpu, int vector)
 {
-       union numachip_csr_g3_ext_irq_gen int_gen;
-       int apicid = per_cpu(x86_cpu_to_apicid, cpu);
-
-       int_gen.s._destination_apic_id = apicid;
-       int_gen.s._vector = vector;
-       int_gen.s._msgtype = (vector == NMI_VECTOR ? APIC_DM_NMI : APIC_DM_FIXED) >> 8;
-       int_gen.s._index = 0;
+       int local_apicid, apicid = per_cpu(x86_cpu_to_apicid, cpu);
+       unsigned int dmode;
+
+       preempt_disable();
+       local_apicid = __this_cpu_read(x86_cpu_to_apicid);
+
+       /* Send via local APIC where non-local part matches */
+       if (!((apicid ^ local_apicid) >> NUMACHIP_LAPIC_BITS)) {
+               unsigned long flags;
+
+               local_irq_save(flags);
+               __default_send_IPI_dest_field(apicid, vector,
+                       APIC_DEST_PHYSICAL);
+               local_irq_restore(flags);
+               preempt_enable();
+               return;
+       }
+       preempt_enable();
 
-       write_lcsr(CSR_G3_EXT_IRQ_GEN, int_gen.v);
+       dmode = (vector == NMI_VECTOR) ? APIC_DM_NMI : APIC_DM_FIXED;
+       numachip_apic_icr_write(apicid, dmode | vector);
 }
 
 static void numachip_send_IPI_mask(const struct cpumask *mask, int vector)
@@ -149,9 +160,14 @@ static void numachip_send_IPI_self(int vector)
        apic_write(APIC_SELF_IPI, vector);
 }
 
-static int __init numachip_probe(void)
+static int __init numachip1_probe(void)
 {
-       return apic == &apic_numachip;
+       return apic == &apic_numachip1;
+}
+
+static int __init numachip2_probe(void)
+{
+       return apic == &apic_numachip2;
 }
 
 static void fixup_cpu_id(struct cpuinfo_x86 *c, int node)
@@ -172,34 +188,118 @@ static void fixup_cpu_id(struct cpuinfo_x86 *c, int node)
 
 static int __init numachip_system_init(void)
 {
-       if (!numachip_system)
+       /* Map the LCSR area and set up the apic_icr_write function */
+       switch (numachip_system) {
+       case 1:
+               init_extra_mapping_uc(NUMACHIP_LCSR_BASE, NUMACHIP_LCSR_SIZE);
+               numachip_apic_icr_write = numachip1_apic_icr_write;
+               x86_init.pci.arch_init = pci_numachip_init;
+               break;
+       case 2:
+               init_extra_mapping_uc(NUMACHIP2_LCSR_BASE, NUMACHIP2_LCSR_SIZE);
+               numachip_apic_icr_write = numachip2_apic_icr_write;
+
+               /* Use MCFG config cycles rather than locked CF8 cycles */
+               raw_pci_ops = &pci_mmcfg;
+               break;
+       default:
                return 0;
-
-       init_extra_mapping_uc(NUMACHIP_LCSR_BASE, NUMACHIP_LCSR_SIZE);
-       init_extra_mapping_uc(NUMACHIP_GCSR_BASE, NUMACHIP_GCSR_SIZE);
+       }
 
        x86_cpuinit.fixup_cpu_id = fixup_cpu_id;
-       x86_init.pci.arch_init = pci_numachip_init;
 
        return 0;
 }
 early_initcall(numachip_system_init);
 
-static int numachip_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+static int numachip1_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
 {
-       if (!strncmp(oem_id, "NUMASC", 6)) {
-               numachip_system = 1;
-               return 1;
-       }
+       if ((strncmp(oem_id, "NUMASC", 6) != 0) ||
+           (strncmp(oem_table_id, "NCONNECT", 8) != 0))
+               return 0;
 
-       return 0;
+       numachip_system = 1;
+
+       return 1;
+}
+
+static int numachip2_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+{
+       if ((strncmp(oem_id, "NUMASC", 6) != 0) ||
+           (strncmp(oem_table_id, "NCONECT2", 8) != 0))
+               return 0;
+
+       numachip_system = 2;
+
+       return 1;
+}
+
+/* APIC IPIs are queued */
+static void numachip_apic_wait_icr_idle(void)
+{
 }
 
-static const struct apic apic_numachip __refconst = {
+/* APIC NMI IPIs are queued */
+static u32 numachip_safe_apic_wait_icr_idle(void)
+{
+       return 0;
+}
 
+static const struct apic apic_numachip1 __refconst = {
        .name                           = "NumaConnect system",
-       .probe                          = numachip_probe,
-       .acpi_madt_oem_check            = numachip_acpi_madt_oem_check,
+       .probe                          = numachip1_probe,
+       .acpi_madt_oem_check            = numachip1_acpi_madt_oem_check,
+       .apic_id_valid                  = numachip_apic_id_valid,
+       .apic_id_registered             = numachip_apic_id_registered,
+
+       .irq_delivery_mode              = dest_Fixed,
+       .irq_dest_mode                  = 0, /* physical */
+
+       .target_cpus                    = online_target_cpus,
+       .disable_esr                    = 0,
+       .dest_logical                   = 0,
+       .check_apicid_used              = NULL,
+
+       .vector_allocation_domain       = default_vector_allocation_domain,
+       .init_apic_ldr                  = flat_init_apic_ldr,
+
+       .ioapic_phys_id_map             = NULL,
+       .setup_apic_routing             = NULL,
+       .cpu_present_to_apicid          = default_cpu_present_to_apicid,
+       .apicid_to_cpu_present          = NULL,
+       .check_phys_apicid_present      = default_check_phys_apicid_present,
+       .phys_pkg_id                    = numachip_phys_pkg_id,
+
+       .get_apic_id                    = numachip1_get_apic_id,
+       .set_apic_id                    = numachip1_set_apic_id,
+       .apic_id_mask                   = 0xffU << 24,
+
+       .cpu_mask_to_apicid_and         = default_cpu_mask_to_apicid_and,
+
+       .send_IPI_mask                  = numachip_send_IPI_mask,
+       .send_IPI_mask_allbutself       = numachip_send_IPI_mask_allbutself,
+       .send_IPI_allbutself            = numachip_send_IPI_allbutself,
+       .send_IPI_all                   = numachip_send_IPI_all,
+       .send_IPI_self                  = numachip_send_IPI_self,
+
+       .wakeup_secondary_cpu           = numachip_wakeup_secondary,
+       .inquire_remote_apic            = NULL, /* REMRD not supported */
+
+       .read                           = native_apic_mem_read,
+       .write                          = native_apic_mem_write,
+       .eoi_write                      = native_apic_mem_write,
+       .icr_read                       = native_apic_icr_read,
+       .icr_write                      = native_apic_icr_write,
+       .wait_icr_idle                  = numachip_apic_wait_icr_idle,
+       .safe_wait_icr_idle             = numachip_safe_apic_wait_icr_idle,
+};
+
+apic_driver(apic_numachip1);
+
+static const struct apic apic_numachip2 __refconst = {
+       .name                           = "NumaConnect2 system",
+       .probe                          = numachip2_probe,
+       .acpi_madt_oem_check            = numachip2_acpi_madt_oem_check,
        .apic_id_valid                  = numachip_apic_id_valid,
        .apic_id_registered             = numachip_apic_id_registered,
 
@@ -221,8 +321,8 @@ static const struct apic apic_numachip __refconst = {
        .check_phys_apicid_present      = default_check_phys_apicid_present,
        .phys_pkg_id                    = numachip_phys_pkg_id,
 
-       .get_apic_id                    = get_apic_id,
-       .set_apic_id                    = set_apic_id,
+       .get_apic_id                    = numachip2_get_apic_id,
+       .set_apic_id                    = numachip2_set_apic_id,
        .apic_id_mask                   = 0xffU << 24,
 
        .cpu_mask_to_apicid_and         = default_cpu_mask_to_apicid_and,
@@ -241,8 +341,8 @@ static const struct apic apic_numachip __refconst = {
        .eoi_write                      = native_apic_mem_write,
        .icr_read                       = native_apic_icr_read,
        .icr_write                      = native_apic_icr_write,
-       .wait_icr_idle                  = native_apic_wait_icr_idle,
-       .safe_wait_icr_idle             = native_safe_apic_wait_icr_idle,
+       .wait_icr_idle                  = numachip_apic_wait_icr_idle,
+       .safe_wait_icr_idle             = numachip_safe_apic_wait_icr_idle,
 };
-apic_driver(apic_numachip);
 
+apic_driver(apic_numachip2);
index bb6bfc01cb82dd6ffbe4aa49e92020b2600584d6..f25321894ad294278c20d5ba7b4076ce2353a8fb 100644 (file)
@@ -529,7 +529,7 @@ static void __eoi_ioapic_pin(int apic, int pin, int vector)
        }
 }
 
-void eoi_ioapic_pin(int vector, struct mp_chip_data *data)
+static void eoi_ioapic_pin(int vector, struct mp_chip_data *data)
 {
        unsigned long flags;
        struct irq_pin_list *entry;
@@ -2547,7 +2547,9 @@ void __init setup_ioapic_dest(void)
                        mask = apic->target_cpus();
 
                chip = irq_data_get_irq_chip(idata);
-               chip->irq_set_affinity(idata, mask, false);
+               /* Might be lapic_chip for irq 0 */
+               if (chip->irq_set_affinity)
+                       chip->irq_set_affinity(idata, mask, false);
        }
 }
 #endif
index 8e3d22a1af94094b749abca95187b073403d628c..439df975bc7aed0861581381476eeb933bc086de 100644 (file)
@@ -43,18 +43,15 @@ void common(void) {
 
 #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
        BLANK();
-       OFFSET(IA32_SIGCONTEXT_ax, sigcontext_ia32, ax);
-       OFFSET(IA32_SIGCONTEXT_bx, sigcontext_ia32, bx);
-       OFFSET(IA32_SIGCONTEXT_cx, sigcontext_ia32, cx);
-       OFFSET(IA32_SIGCONTEXT_dx, sigcontext_ia32, dx);
-       OFFSET(IA32_SIGCONTEXT_si, sigcontext_ia32, si);
-       OFFSET(IA32_SIGCONTEXT_di, sigcontext_ia32, di);
-       OFFSET(IA32_SIGCONTEXT_bp, sigcontext_ia32, bp);
-       OFFSET(IA32_SIGCONTEXT_sp, sigcontext_ia32, sp);
-       OFFSET(IA32_SIGCONTEXT_ip, sigcontext_ia32, ip);
-
-       BLANK();
-       OFFSET(TI_sysenter_return, thread_info, sysenter_return);
+       OFFSET(IA32_SIGCONTEXT_ax, sigcontext_32, ax);
+       OFFSET(IA32_SIGCONTEXT_bx, sigcontext_32, bx);
+       OFFSET(IA32_SIGCONTEXT_cx, sigcontext_32, cx);
+       OFFSET(IA32_SIGCONTEXT_dx, sigcontext_32, dx);
+       OFFSET(IA32_SIGCONTEXT_si, sigcontext_32, si);
+       OFFSET(IA32_SIGCONTEXT_di, sigcontext_32, di);
+       OFFSET(IA32_SIGCONTEXT_bp, sigcontext_32, bp);
+       OFFSET(IA32_SIGCONTEXT_sp, sigcontext_32, sp);
+       OFFSET(IA32_SIGCONTEXT_ip, sigcontext_32, ip);
 
        BLANK();
        OFFSET(IA32_RT_SIGFRAME_sigcontext, rt_sigframe_ia32, uc.uc_mcontext);
index 4eb065c6bed26b38a2804ffcab57f9e09274a7ec..58031303e30488c540d609f95d0693918c09fa64 100644 (file)
@@ -41,6 +41,7 @@ obj-$(CONFIG_CPU_SUP_INTEL)           += perf_event_p6.o perf_event_knc.o perf_event_p4.o
 obj-$(CONFIG_CPU_SUP_INTEL)            += perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o
 obj-$(CONFIG_CPU_SUP_INTEL)            += perf_event_intel_rapl.o perf_event_intel_cqm.o
 obj-$(CONFIG_CPU_SUP_INTEL)            += perf_event_intel_pt.o perf_event_intel_bts.o
+obj-$(CONFIG_CPU_SUP_INTEL)            += perf_event_intel_cstate.o
 
 obj-$(CONFIG_PERF_EVENTS_INTEL_UNCORE) += perf_event_intel_uncore.o \
                                           perf_event_intel_uncore_snb.o \
index de22ea7ff82f93ea1a8d4a204f10a7c6e8fdef76..4ddd780aeac90ab353134453316dc18108c8b308 100644 (file)
@@ -670,6 +670,7 @@ void get_cpu_cap(struct cpuinfo_x86 *c)
 
                c->x86_virt_bits = (eax >> 8) & 0xff;
                c->x86_phys_bits = eax & 0xff;
+               c->x86_capability[13] = cpuid_ebx(0x80000008);
        }
 #ifdef CONFIG_X86_32
        else if (cpu_has(c, X86_FEATURE_PAE) || cpu_has(c, X86_FEATURE_PSE36))
index be4febc58b9443372e4df5037e357789de5184fe..e38d338a64475a82feb198c7169e54b8d6d7100a 100644 (file)
@@ -157,7 +157,7 @@ struct _cpuid4_info_regs {
        struct amd_northbridge *nb;
 };
 
-unsigned short                 num_cache_leaves;
+static unsigned short num_cache_leaves;
 
 /* AMD doesn't have CPUID4. Emulate it here to report the same
    information to the user.  This makes some assumptions about the machine:
@@ -326,7 +326,7 @@ static void amd_calc_l3_indices(struct amd_northbridge *nb)
  *
  * @returns: the disabled index if used or negative value if slot free.
  */
-int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned slot)
+static int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned slot)
 {
        unsigned int reg = 0;
 
@@ -403,8 +403,8 @@ static void amd_l3_disable_index(struct amd_northbridge *nb, int cpu,
  *
  * @return: 0 on success, error status on failure
  */
-int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu, unsigned slot,
-                           unsigned long index)
+static int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu,
+                           unsigned slot, unsigned long index)
 {
        int ret = 0;
 
index 9d014b82a124c82fdfbd6c952f03c7624d99972f..c5b0d562dbf55064685c78b5d0fa6280748086d1 100644 (file)
@@ -1586,6 +1586,8 @@ static int __mcheck_cpu_ancient_init(struct cpuinfo_x86 *c)
                winchip_mcheck_init(c);
                return 1;
                break;
+       default:
+               return 0;
        }
 
        return 0;
@@ -1605,6 +1607,8 @@ static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c)
                mce_amd_feature_init(c);
                mce_flags.overflow_recov = !!(ebx & BIT(0));
                mce_flags.succor         = !!(ebx & BIT(1));
+               mce_flags.smca           = !!(ebx & BIT(3));
+
                break;
                }
 
@@ -2042,7 +2046,7 @@ int __init mcheck_init(void)
  * Disable machine checks on suspend and shutdown. We can't really handle
  * them later.
  */
-static int mce_disable_error_reporting(void)
+static void mce_disable_error_reporting(void)
 {
        int i;
 
@@ -2052,17 +2056,32 @@ static int mce_disable_error_reporting(void)
                if (b->init)
                        wrmsrl(MSR_IA32_MCx_CTL(i), 0);
        }
-       return 0;
+       return;
+}
+
+static void vendor_disable_error_reporting(void)
+{
+       /*
+        * Don't clear on Intel CPUs. Some of these MSRs are socket-wide.
+        * Disabling them for just a single offlined CPU is bad, since it will
+        * inhibit reporting for all shared resources on the socket like the
+        * last level cache (LLC), the integrated memory controller (iMC), etc.
+        */
+       if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
+               return;
+
+       mce_disable_error_reporting();
 }
 
 static int mce_syscore_suspend(void)
 {
-       return mce_disable_error_reporting();
+       vendor_disable_error_reporting();
+       return 0;
 }
 
 static void mce_syscore_shutdown(void)
 {
-       mce_disable_error_reporting();
+       vendor_disable_error_reporting();
 }
 
 /*
@@ -2342,19 +2361,14 @@ static void mce_device_remove(unsigned int cpu)
 static void mce_disable_cpu(void *h)
 {
        unsigned long action = *(unsigned long *)h;
-       int i;
 
        if (!mce_available(raw_cpu_ptr(&cpu_info)))
                return;
 
        if (!(action & CPU_TASKS_FROZEN))
                cmci_clear();
-       for (i = 0; i < mca_cfg.banks; i++) {
-               struct mce_bank *b = &mce_banks[i];
 
-               if (b->init)
-                       wrmsrl(MSR_IA32_MCx_CTL(i), 0);
-       }
+       vendor_disable_error_reporting();
 }
 
 static void mce_reenable_cpu(void *h)
index 1af51b1586d7f7ae0bfa65ec377634b2d8fa39c2..2c5aaf8c2e2f3dcc94d348dfe91da6d7d5000ae9 100644 (file)
@@ -503,14 +503,6 @@ void intel_init_thermal(struct cpuinfo_x86 *c)
                return;
        }
 
-       /* Check whether a vector already exists */
-       if (h & APIC_VECTOR_MASK) {
-               printk(KERN_DEBUG
-                      "CPU%d: Thermal LVT vector (%#x) already installed\n",
-                      cpu, (h & APIC_VECTOR_MASK));
-               return;
-       }
-
        /* early Pentium M models use different method for enabling TM2 */
        if (cpu_has(c, X86_FEATURE_TM2)) {
                if (c->x86 == 6 && (c->x86_model == 9 || c->x86_model == 13)) {
index 285c85427c323c8e33abc8a4dfc0436123143de7..220b1a50851345632c3f5c1ca4f0e3cbb40d02a0 100644 (file)
@@ -2,6 +2,3 @@ microcode-y                             := core.o
 obj-$(CONFIG_MICROCODE)                        += microcode.o
 microcode-$(CONFIG_MICROCODE_INTEL)    += intel.o intel_lib.o
 microcode-$(CONFIG_MICROCODE_AMD)      += amd.o
-obj-$(CONFIG_MICROCODE_EARLY)          += core_early.o
-obj-$(CONFIG_MICROCODE_INTEL_EARLY)    += intel_early.o
-obj-$(CONFIG_MICROCODE_AMD_EARLY)      += amd_early.o
index 12829c3ced3c549c982a3072f825c2ed1b1c7758..2233f8a766156891a52b9a7658f04efeaf4f86c8 100644 (file)
@@ -1,5 +1,9 @@
 /*
  *  AMD CPU Microcode Update Driver for Linux
+ *
+ *  This driver allows to upgrade microcode on F10h AMD
+ *  CPUs and later.
+ *
  *  Copyright (C) 2008-2011 Advanced Micro Devices Inc.
  *
  *  Author: Peter Oruba <peter.oruba@amd.com>
  *  Based on work by:
  *  Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
  *
- *  Maintainers:
- *  Andreas Herrmann <herrmann.der.user@googlemail.com>
- *  Borislav Petkov <bp@alien8.de>
+ *  early loader:
+ *  Copyright (C) 2013 Advanced Micro Devices, Inc.
  *
- *  This driver allows to upgrade microcode on F10h AMD
- *  CPUs and later.
+ *  Author: Jacob Shin <jacob.shin@amd.com>
+ *  Fixes: Borislav Petkov <bp@suse.de>
  *
  *  Licensed under the terms of the GNU General Public
  *  License version 2. See file COPYING for details.
  */
+#define pr_fmt(fmt) "microcode: " fmt
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
+#include <linux/earlycpio.h>
 #include <linux/firmware.h>
 #include <linux/uaccess.h>
 #include <linux/vmalloc.h>
+#include <linux/initrd.h>
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/pci.h>
 
+#include <asm/microcode_amd.h>
 #include <asm/microcode.h>
 #include <asm/processor.h>
+#include <asm/setup.h>
+#include <asm/cpu.h>
 #include <asm/msr.h>
-#include <asm/microcode_amd.h>
-
-MODULE_DESCRIPTION("AMD Microcode Update Driver");
-MODULE_AUTHOR("Peter Oruba");
-MODULE_LICENSE("GPL v2");
 
 static struct equiv_cpu_entry *equiv_cpu_table;
 
@@ -47,6 +48,432 @@ struct ucode_patch {
 
 static LIST_HEAD(pcache);
 
+/*
+ * This points to the current valid container of microcode patches which we will
+ * save from the initrd before jettisoning its contents.
+ */
+static u8 *container;
+static size_t container_size;
+
+static u32 ucode_new_rev;
+u8 amd_ucode_patch[PATCH_MAX_SIZE];
+static u16 this_equiv_id;
+
+static struct cpio_data ucode_cpio;
+
+/*
+ * Microcode patch container file is prepended to the initrd in cpio format.
+ * See Documentation/x86/early-microcode.txt
+ */
+static __initdata char ucode_path[] = "kernel/x86/microcode/AuthenticAMD.bin";
+
+static struct cpio_data __init find_ucode_in_initrd(void)
+{
+       long offset = 0;
+       char *path;
+       void *start;
+       size_t size;
+
+#ifdef CONFIG_X86_32
+       struct boot_params *p;
+
+       /*
+        * On 32-bit, early load occurs before paging is turned on so we need
+        * to use physical addresses.
+        */
+       p       = (struct boot_params *)__pa_nodebug(&boot_params);
+       path    = (char *)__pa_nodebug(ucode_path);
+       start   = (void *)p->hdr.ramdisk_image;
+       size    = p->hdr.ramdisk_size;
+#else
+       path    = ucode_path;
+       start   = (void *)(boot_params.hdr.ramdisk_image + PAGE_OFFSET);
+       size    = boot_params.hdr.ramdisk_size;
+#endif
+
+       return find_cpio_data(path, start, size, &offset);
+}
+
+static size_t compute_container_size(u8 *data, u32 total_size)
+{
+       size_t size = 0;
+       u32 *header = (u32 *)data;
+
+       if (header[0] != UCODE_MAGIC ||
+           header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */
+           header[2] == 0)                            /* size */
+               return size;
+
+       size = header[2] + CONTAINER_HDR_SZ;
+       total_size -= size;
+       data += size;
+
+       while (total_size) {
+               u16 patch_size;
+
+               header = (u32 *)data;
+
+               if (header[0] != UCODE_UCODE_TYPE)
+                       break;
+
+               /*
+                * Sanity-check patch size.
+                */
+               patch_size = header[1];
+               if (patch_size > PATCH_MAX_SIZE)
+                       break;
+
+               size       += patch_size + SECTION_HDR_SIZE;
+               data       += patch_size + SECTION_HDR_SIZE;
+               total_size -= patch_size + SECTION_HDR_SIZE;
+       }
+
+       return size;
+}
+
+/*
+ * Early load occurs before we can vmalloc(). So we look for the microcode
+ * patch container file in initrd, traverse equivalent cpu table, look for a
+ * matching microcode patch, and update, all in initrd memory in place.
+ * When vmalloc() is available for use later -- on 64-bit during first AP load,
+ * and on 32-bit during save_microcode_in_initrd_amd() -- we can call
+ * load_microcode_amd() to save equivalent cpu table and microcode patches in
+ * kernel heap memory.
+ */
+static void apply_ucode_in_initrd(void *ucode, size_t size, bool save_patch)
+{
+       struct equiv_cpu_entry *eq;
+       size_t *cont_sz;
+       u32 *header;
+       u8  *data, **cont;
+       u8 (*patch)[PATCH_MAX_SIZE];
+       u16 eq_id = 0;
+       int offset, left;
+       u32 rev, eax, ebx, ecx, edx;
+       u32 *new_rev;
+
+#ifdef CONFIG_X86_32
+       new_rev = (u32 *)__pa_nodebug(&ucode_new_rev);
+       cont_sz = (size_t *)__pa_nodebug(&container_size);
+       cont    = (u8 **)__pa_nodebug(&container);
+       patch   = (u8 (*)[PATCH_MAX_SIZE])__pa_nodebug(&amd_ucode_patch);
+#else
+       new_rev = &ucode_new_rev;
+       cont_sz = &container_size;
+       cont    = &container;
+       patch   = &amd_ucode_patch;
+#endif
+
+       data   = ucode;
+       left   = size;
+       header = (u32 *)data;
+
+       /* find equiv cpu table */
+       if (header[0] != UCODE_MAGIC ||
+           header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */
+           header[2] == 0)                            /* size */
+               return;
+
+       eax = 0x00000001;
+       ecx = 0;
+       native_cpuid(&eax, &ebx, &ecx, &edx);
+
+       while (left > 0) {
+               eq = (struct equiv_cpu_entry *)(data + CONTAINER_HDR_SZ);
+
+               *cont = data;
+
+               /* Advance past the container header */
+               offset = header[2] + CONTAINER_HDR_SZ;
+               data  += offset;
+               left  -= offset;
+
+               eq_id = find_equiv_id(eq, eax);
+               if (eq_id) {
+                       this_equiv_id = eq_id;
+                       *cont_sz = compute_container_size(*cont, left + offset);
+
+                       /*
+                        * truncate how much we need to iterate over in the
+                        * ucode update loop below
+                        */
+                       left = *cont_sz - offset;
+                       break;
+               }
+
+               /*
+                * support multiple container files appended together. if this
+                * one does not have a matching equivalent cpu entry, we fast
+                * forward to the next container file.
+                */
+               while (left > 0) {
+                       header = (u32 *)data;
+                       if (header[0] == UCODE_MAGIC &&
+                           header[1] == UCODE_EQUIV_CPU_TABLE_TYPE)
+                               break;
+
+                       offset = header[1] + SECTION_HDR_SIZE;
+                       data  += offset;
+                       left  -= offset;
+               }
+
+               /* mark where the next microcode container file starts */
+               offset    = data - (u8 *)ucode;
+               ucode     = data;
+       }
+
+       if (!eq_id) {
+               *cont = NULL;
+               *cont_sz = 0;
+               return;
+       }
+
+       if (check_current_patch_level(&rev, true))
+               return;
+
+       while (left > 0) {
+               struct microcode_amd *mc;
+
+               header = (u32 *)data;
+               if (header[0] != UCODE_UCODE_TYPE || /* type */
+                   header[1] == 0)                  /* size */
+                       break;
+
+               mc = (struct microcode_amd *)(data + SECTION_HDR_SIZE);
+
+               if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id) {
+
+                       if (!__apply_microcode_amd(mc)) {
+                               rev = mc->hdr.patch_id;
+                               *new_rev = rev;
+
+                               if (save_patch)
+                                       memcpy(patch, mc,
+                                              min_t(u32, header[1], PATCH_MAX_SIZE));
+                       }
+               }
+
+               offset  = header[1] + SECTION_HDR_SIZE;
+               data   += offset;
+               left   -= offset;
+       }
+}
+
+static bool __init load_builtin_amd_microcode(struct cpio_data *cp,
+                                             unsigned int family)
+{
+#ifdef CONFIG_X86_64
+       char fw_name[36] = "amd-ucode/microcode_amd.bin";
+
+       if (family >= 0x15)
+               snprintf(fw_name, sizeof(fw_name),
+                        "amd-ucode/microcode_amd_fam%.2xh.bin", family);
+
+       return get_builtin_firmware(cp, fw_name);
+#else
+       return false;
+#endif
+}
+
+void __init load_ucode_amd_bsp(unsigned int family)
+{
+       struct cpio_data cp;
+       void **data;
+       size_t *size;
+
+#ifdef CONFIG_X86_32
+       data =  (void **)__pa_nodebug(&ucode_cpio.data);
+       size = (size_t *)__pa_nodebug(&ucode_cpio.size);
+#else
+       data = &ucode_cpio.data;
+       size = &ucode_cpio.size;
+#endif
+
+       cp = find_ucode_in_initrd();
+       if (!cp.data) {
+               if (!load_builtin_amd_microcode(&cp, family))
+                       return;
+       }
+
+       *data = cp.data;
+       *size = cp.size;
+
+       apply_ucode_in_initrd(cp.data, cp.size, true);
+}
+
+#ifdef CONFIG_X86_32
+/*
+ * On 32-bit, since AP's early load occurs before paging is turned on, we
+ * cannot traverse cpu_equiv_table and pcache in kernel heap memory. So during
+ * cold boot, AP will apply_ucode_in_initrd() just like the BSP. During
+ * save_microcode_in_initrd_amd() BSP's patch is copied to amd_ucode_patch,
+ * which is used upon resume from suspend.
+ */
+void load_ucode_amd_ap(void)
+{
+       struct microcode_amd *mc;
+       size_t *usize;
+       void **ucode;
+
+       mc = (struct microcode_amd *)__pa_nodebug(amd_ucode_patch);
+       if (mc->hdr.patch_id && mc->hdr.processor_rev_id) {
+               __apply_microcode_amd(mc);
+               return;
+       }
+
+       ucode = (void *)__pa_nodebug(&container);
+       usize = (size_t *)__pa_nodebug(&container_size);
+
+       if (!*ucode || !*usize)
+               return;
+
+       apply_ucode_in_initrd(*ucode, *usize, false);
+}
+
+static void __init collect_cpu_sig_on_bsp(void *arg)
+{
+       unsigned int cpu = smp_processor_id();
+       struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+
+       uci->cpu_sig.sig = cpuid_eax(0x00000001);
+}
+
+static void __init get_bsp_sig(void)
+{
+       unsigned int bsp = boot_cpu_data.cpu_index;
+       struct ucode_cpu_info *uci = ucode_cpu_info + bsp;
+
+       if (!uci->cpu_sig.sig)
+               smp_call_function_single(bsp, collect_cpu_sig_on_bsp, NULL, 1);
+}
+#else
+void load_ucode_amd_ap(void)
+{
+       unsigned int cpu = smp_processor_id();
+       struct equiv_cpu_entry *eq;
+       struct microcode_amd *mc;
+       u32 rev, eax;
+       u16 eq_id;
+
+       /* Exit if called on the BSP. */
+       if (!cpu)
+               return;
+
+       if (!container)
+               return;
+
+       /*
+        * 64-bit runs with paging enabled, thus early==false.
+        */
+       if (check_current_patch_level(&rev, false))
+               return;
+
+       eax = cpuid_eax(0x00000001);
+       eq  = (struct equiv_cpu_entry *)(container + CONTAINER_HDR_SZ);
+
+       eq_id = find_equiv_id(eq, eax);
+       if (!eq_id)
+               return;
+
+       if (eq_id == this_equiv_id) {
+               mc = (struct microcode_amd *)amd_ucode_patch;
+
+               if (mc && rev < mc->hdr.patch_id) {
+                       if (!__apply_microcode_amd(mc))
+                               ucode_new_rev = mc->hdr.patch_id;
+               }
+
+       } else {
+               if (!ucode_cpio.data)
+                       return;
+
+               /*
+                * AP has a different equivalence ID than BSP, looks like
+                * mixed-steppings silicon so go through the ucode blob anew.
+                */
+               apply_ucode_in_initrd(ucode_cpio.data, ucode_cpio.size, false);
+       }
+}
+#endif
+
+int __init save_microcode_in_initrd_amd(void)
+{
+       unsigned long cont;
+       int retval = 0;
+       enum ucode_state ret;
+       u8 *cont_va;
+       u32 eax;
+
+       if (!container)
+               return -EINVAL;
+
+#ifdef CONFIG_X86_32
+       get_bsp_sig();
+       cont    = (unsigned long)container;
+       cont_va = __va(container);
+#else
+       /*
+        * We need the physical address of the container for both bitness since
+        * boot_params.hdr.ramdisk_image is a physical address.
+        */
+       cont    = __pa(container);
+       cont_va = container;
+#endif
+
+       /*
+        * Take into account the fact that the ramdisk might get relocated and
+        * therefore we need to recompute the container's position in virtual
+        * memory space.
+        */
+       if (relocated_ramdisk)
+               container = (u8 *)(__va(relocated_ramdisk) +
+                            (cont - boot_params.hdr.ramdisk_image));
+       else
+               container = cont_va;
+
+       if (ucode_new_rev)
+               pr_info("microcode: updated early to new patch_level=0x%08x\n",
+                       ucode_new_rev);
+
+       eax   = cpuid_eax(0x00000001);
+       eax   = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
+
+       ret = load_microcode_amd(smp_processor_id(), eax, container, container_size);
+       if (ret != UCODE_OK)
+               retval = -EINVAL;
+
+       /*
+        * This will be freed any msec now, stash patches for the current
+        * family and switch to patch cache for cpu hotplug, etc later.
+        */
+       container = NULL;
+       container_size = 0;
+
+       return retval;
+}
+
+void reload_ucode_amd(void)
+{
+       struct microcode_amd *mc;
+       u32 rev;
+
+       /*
+        * early==false because this is a syscore ->resume path and by
+        * that time paging is long enabled.
+        */
+       if (check_current_patch_level(&rev, false))
+               return;
+
+       mc = (struct microcode_amd *)amd_ucode_patch;
+
+       if (mc && rev < mc->hdr.patch_id) {
+               if (!__apply_microcode_amd(mc)) {
+                       ucode_new_rev = mc->hdr.patch_id;
+                       pr_info("microcode: reload patch_level=0x%08x\n",
+                               ucode_new_rev);
+               }
+       }
+}
 static u16 __find_equiv_id(unsigned int cpu)
 {
        struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
@@ -177,6 +604,53 @@ static unsigned int verify_patch_size(u8 family, u32 patch_size,
        return patch_size;
 }
 
+/*
+ * Those patch levels cannot be updated to newer ones and thus should be final.
+ */
+static u32 final_levels[] = {
+       0x01000098,
+       0x0100009f,
+       0x010000af,
+       0, /* T-101 terminator */
+};
+
+/*
+ * Check the current patch level on this CPU.
+ *
+ * @rev: Use it to return the patch level. It is set to 0 in the case of
+ * error.
+ *
+ * Returns:
+ *  - true: if update should stop
+ *  - false: otherwise
+ */
+bool check_current_patch_level(u32 *rev, bool early)
+{
+       u32 lvl, dummy, i;
+       bool ret = false;
+       u32 *levels;
+
+       native_rdmsr(MSR_AMD64_PATCH_LEVEL, lvl, dummy);
+
+       if (IS_ENABLED(CONFIG_X86_32) && early)
+               levels = (u32 *)__pa_nodebug(&final_levels);
+       else
+               levels = final_levels;
+
+       for (i = 0; levels[i]; i++) {
+               if (lvl == levels[i]) {
+                       lvl = 0;
+                       ret = true;
+                       break;
+               }
+       }
+
+       if (rev)
+               *rev = lvl;
+
+       return ret;
+}
+
 int __apply_microcode_amd(struct microcode_amd *mc_amd)
 {
        u32 rev, dummy;
@@ -197,7 +671,7 @@ int apply_microcode_amd(int cpu)
        struct microcode_amd *mc_amd;
        struct ucode_cpu_info *uci;
        struct ucode_patch *p;
-       u32 rev, dummy;
+       u32 rev;
 
        BUG_ON(raw_smp_processor_id() != cpu);
 
@@ -210,7 +684,8 @@ int apply_microcode_amd(int cpu)
        mc_amd  = p->data;
        uci->mc = p->data;
 
-       rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
+       if (check_current_patch_level(&rev, false))
+               return -1;
 
        /* need to apply patch? */
        if (rev >= mc_amd->hdr.patch_id) {
@@ -387,7 +862,7 @@ enum ucode_state load_microcode_amd(int cpu, u8 family, const u8 *data, size_t s
        if (ret != UCODE_OK)
                cleanup();
 
-#if defined(CONFIG_MICROCODE_AMD_EARLY) && defined(CONFIG_X86_32)
+#ifdef CONFIG_X86_32
        /* save BSP's matching patch for early load */
        if (cpu_data(cpu).cpu_index == boot_cpu_data.cpu_index) {
                struct ucode_patch *p = find_patch(cpu);
@@ -475,7 +950,7 @@ static struct microcode_ops microcode_amd_ops = {
 
 struct microcode_ops * __init init_amd_microcode(void)
 {
-       struct cpuinfo_x86 *c = &cpu_data(0);
+       struct cpuinfo_x86 *c = &boot_cpu_data;
 
        if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) {
                pr_warning("AMD CPU family 0x%x not supported\n", c->x86);
diff --git a/arch/x86/kernel/cpu/microcode/amd_early.c b/arch/x86/kernel/cpu/microcode/amd_early.c
deleted file mode 100644 (file)
index e8a215a..0000000
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * Copyright (C) 2013 Advanced Micro Devices, Inc.
- *
- * Author: Jacob Shin <jacob.shin@amd.com>
- * Fixes: Borislav Petkov <bp@suse.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/earlycpio.h>
-#include <linux/initrd.h>
-
-#include <asm/cpu.h>
-#include <asm/setup.h>
-#include <asm/microcode_amd.h>
-
-/*
- * This points to the current valid container of microcode patches which we will
- * save from the initrd before jettisoning its contents.
- */
-static u8 *container;
-static size_t container_size;
-
-static u32 ucode_new_rev;
-u8 amd_ucode_patch[PATCH_MAX_SIZE];
-static u16 this_equiv_id;
-
-static struct cpio_data ucode_cpio;
-
-/*
- * Microcode patch container file is prepended to the initrd in cpio format.
- * See Documentation/x86/early-microcode.txt
- */
-static __initdata char ucode_path[] = "kernel/x86/microcode/AuthenticAMD.bin";
-
-static struct cpio_data __init find_ucode_in_initrd(void)
-{
-       long offset = 0;
-       char *path;
-       void *start;
-       size_t size;
-
-#ifdef CONFIG_X86_32
-       struct boot_params *p;
-
-       /*
-        * On 32-bit, early load occurs before paging is turned on so we need
-        * to use physical addresses.
-        */
-       p       = (struct boot_params *)__pa_nodebug(&boot_params);
-       path    = (char *)__pa_nodebug(ucode_path);
-       start   = (void *)p->hdr.ramdisk_image;
-       size    = p->hdr.ramdisk_size;
-#else
-       path    = ucode_path;
-       start   = (void *)(boot_params.hdr.ramdisk_image + PAGE_OFFSET);
-       size    = boot_params.hdr.ramdisk_size;
-#endif
-
-       return find_cpio_data(path, start, size, &offset);
-}
-
-static size_t compute_container_size(u8 *data, u32 total_size)
-{
-       size_t size = 0;
-       u32 *header = (u32 *)data;
-
-       if (header[0] != UCODE_MAGIC ||
-           header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */
-           header[2] == 0)                            /* size */
-               return size;
-
-       size = header[2] + CONTAINER_HDR_SZ;
-       total_size -= size;
-       data += size;
-
-       while (total_size) {
-               u16 patch_size;
-
-               header = (u32 *)data;
-
-               if (header[0] != UCODE_UCODE_TYPE)
-                       break;
-
-               /*
-                * Sanity-check patch size.
-                */
-               patch_size = header[1];
-               if (patch_size > PATCH_MAX_SIZE)
-                       break;
-
-               size       += patch_size + SECTION_HDR_SIZE;
-               data       += patch_size + SECTION_HDR_SIZE;
-               total_size -= patch_size + SECTION_HDR_SIZE;
-       }
-
-       return size;
-}
-
-/*
- * Early load occurs before we can vmalloc(). So we look for the microcode
- * patch container file in initrd, traverse equivalent cpu table, look for a
- * matching microcode patch, and update, all in initrd memory in place.
- * When vmalloc() is available for use later -- on 64-bit during first AP load,
- * and on 32-bit during save_microcode_in_initrd_amd() -- we can call
- * load_microcode_amd() to save equivalent cpu table and microcode patches in
- * kernel heap memory.
- */
-static void apply_ucode_in_initrd(void *ucode, size_t size, bool save_patch)
-{
-       struct equiv_cpu_entry *eq;
-       size_t *cont_sz;
-       u32 *header;
-       u8  *data, **cont;
-       u8 (*patch)[PATCH_MAX_SIZE];
-       u16 eq_id = 0;
-       int offset, left;
-       u32 rev, eax, ebx, ecx, edx;
-       u32 *new_rev;
-
-#ifdef CONFIG_X86_32
-       new_rev = (u32 *)__pa_nodebug(&ucode_new_rev);
-       cont_sz = (size_t *)__pa_nodebug(&container_size);
-       cont    = (u8 **)__pa_nodebug(&container);
-       patch   = (u8 (*)[PATCH_MAX_SIZE])__pa_nodebug(&amd_ucode_patch);
-#else
-       new_rev = &ucode_new_rev;
-       cont_sz = &container_size;
-       cont    = &container;
-       patch   = &amd_ucode_patch;
-#endif
-
-       data   = ucode;
-       left   = size;
-       header = (u32 *)data;
-
-       /* find equiv cpu table */
-       if (header[0] != UCODE_MAGIC ||
-           header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */
-           header[2] == 0)                            /* size */
-               return;
-
-       eax = 0x00000001;
-       ecx = 0;
-       native_cpuid(&eax, &ebx, &ecx, &edx);
-
-       while (left > 0) {
-               eq = (struct equiv_cpu_entry *)(data + CONTAINER_HDR_SZ);
-
-               *cont = data;
-
-               /* Advance past the container header */
-               offset = header[2] + CONTAINER_HDR_SZ;
-               data  += offset;
-               left  -= offset;
-
-               eq_id = find_equiv_id(eq, eax);
-               if (eq_id) {
-                       this_equiv_id = eq_id;
-                       *cont_sz = compute_container_size(*cont, left + offset);
-
-                       /*
-                        * truncate how much we need to iterate over in the
-                        * ucode update loop below
-                        */
-                       left = *cont_sz - offset;
-                       break;
-               }
-
-               /*
-                * support multiple container files appended together. if this
-                * one does not have a matching equivalent cpu entry, we fast
-                * forward to the next container file.
-                */
-               while (left > 0) {
-                       header = (u32 *)data;
-                       if (header[0] == UCODE_MAGIC &&
-                           header[1] == UCODE_EQUIV_CPU_TABLE_TYPE)
-                               break;
-
-                       offset = header[1] + SECTION_HDR_SIZE;
-                       data  += offset;
-                       left  -= offset;
-               }
-
-               /* mark where the next microcode container file starts */
-               offset    = data - (u8 *)ucode;
-               ucode     = data;
-       }
-
-       if (!eq_id) {
-               *cont = NULL;
-               *cont_sz = 0;
-               return;
-       }
-
-       /* find ucode and update if needed */
-
-       native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);
-
-       while (left > 0) {
-               struct microcode_amd *mc;
-
-               header = (u32 *)data;
-               if (header[0] != UCODE_UCODE_TYPE || /* type */
-                   header[1] == 0)                  /* size */
-                       break;
-
-               mc = (struct microcode_amd *)(data + SECTION_HDR_SIZE);
-
-               if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id) {
-
-                       if (!__apply_microcode_amd(mc)) {
-                               rev = mc->hdr.patch_id;
-                               *new_rev = rev;
-
-                               if (save_patch)
-                                       memcpy(patch, mc,
-                                              min_t(u32, header[1], PATCH_MAX_SIZE));
-                       }
-               }
-
-               offset  = header[1] + SECTION_HDR_SIZE;
-               data   += offset;
-               left   -= offset;
-       }
-}
-
-static bool __init load_builtin_amd_microcode(struct cpio_data *cp,
-                                             unsigned int family)
-{
-#ifdef CONFIG_X86_64
-       char fw_name[36] = "amd-ucode/microcode_amd.bin";
-
-       if (family >= 0x15)
-               snprintf(fw_name, sizeof(fw_name),
-                        "amd-ucode/microcode_amd_fam%.2xh.bin", family);
-
-       return get_builtin_firmware(cp, fw_name);
-#else
-       return false;
-#endif
-}
-
-void __init load_ucode_amd_bsp(unsigned int family)
-{
-       struct cpio_data cp;
-       void **data;
-       size_t *size;
-
-#ifdef CONFIG_X86_32
-       data =  (void **)__pa_nodebug(&ucode_cpio.data);
-       size = (size_t *)__pa_nodebug(&ucode_cpio.size);
-#else
-       data = &ucode_cpio.data;
-       size = &ucode_cpio.size;
-#endif
-
-       cp = find_ucode_in_initrd();
-       if (!cp.data) {
-               if (!load_builtin_amd_microcode(&cp, family))
-                       return;
-       }
-
-       *data = cp.data;
-       *size = cp.size;
-
-       apply_ucode_in_initrd(cp.data, cp.size, true);
-}
-
-#ifdef CONFIG_X86_32
-/*
- * On 32-bit, since AP's early load occurs before paging is turned on, we
- * cannot traverse cpu_equiv_table and pcache in kernel heap memory. So during
- * cold boot, AP will apply_ucode_in_initrd() just like the BSP. During
- * save_microcode_in_initrd_amd() BSP's patch is copied to amd_ucode_patch,
- * which is used upon resume from suspend.
- */
-void load_ucode_amd_ap(void)
-{
-       struct microcode_amd *mc;
-       size_t *usize;
-       void **ucode;
-
-       mc = (struct microcode_amd *)__pa_nodebug(amd_ucode_patch);
-       if (mc->hdr.patch_id && mc->hdr.processor_rev_id) {
-               __apply_microcode_amd(mc);
-               return;
-       }
-
-       ucode = (void *)__pa_nodebug(&container);
-       usize = (size_t *)__pa_nodebug(&container_size);
-
-       if (!*ucode || !*usize)
-               return;
-
-       apply_ucode_in_initrd(*ucode, *usize, false);
-}
-
-static void __init collect_cpu_sig_on_bsp(void *arg)
-{
-       unsigned int cpu = smp_processor_id();
-       struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
-
-       uci->cpu_sig.sig = cpuid_eax(0x00000001);
-}
-
-static void __init get_bsp_sig(void)
-{
-       unsigned int bsp = boot_cpu_data.cpu_index;
-       struct ucode_cpu_info *uci = ucode_cpu_info + bsp;
-
-       if (!uci->cpu_sig.sig)
-               smp_call_function_single(bsp, collect_cpu_sig_on_bsp, NULL, 1);
-}
-#else
-void load_ucode_amd_ap(void)
-{
-       unsigned int cpu = smp_processor_id();
-       struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
-       struct equiv_cpu_entry *eq;
-       struct microcode_amd *mc;
-       u32 rev, eax;
-       u16 eq_id;
-
-       /* Exit if called on the BSP. */
-       if (!cpu)
-               return;
-
-       if (!container)
-               return;
-
-       rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);
-
-       uci->cpu_sig.rev = rev;
-       uci->cpu_sig.sig = eax;
-
-       eax = cpuid_eax(0x00000001);
-       eq  = (struct equiv_cpu_entry *)(container + CONTAINER_HDR_SZ);
-
-       eq_id = find_equiv_id(eq, eax);
-       if (!eq_id)
-               return;
-
-       if (eq_id == this_equiv_id) {
-               mc = (struct microcode_amd *)amd_ucode_patch;
-
-               if (mc && rev < mc->hdr.patch_id) {
-                       if (!__apply_microcode_amd(mc))
-                               ucode_new_rev = mc->hdr.patch_id;
-               }
-
-       } else {
-               if (!ucode_cpio.data)
-                       return;
-
-               /*
-                * AP has a different equivalence ID than BSP, looks like
-                * mixed-steppings silicon so go through the ucode blob anew.
-                */
-               apply_ucode_in_initrd(ucode_cpio.data, ucode_cpio.size, false);
-       }
-}
-#endif
-
-int __init save_microcode_in_initrd_amd(void)
-{
-       unsigned long cont;
-       int retval = 0;
-       enum ucode_state ret;
-       u8 *cont_va;
-       u32 eax;
-
-       if (!container)
-               return -EINVAL;
-
-#ifdef CONFIG_X86_32
-       get_bsp_sig();
-       cont    = (unsigned long)container;
-       cont_va = __va(container);
-#else
-       /*
-        * We need the physical address of the container for both bitness since
-        * boot_params.hdr.ramdisk_image is a physical address.
-        */
-       cont    = __pa(container);
-       cont_va = container;
-#endif
-
-       /*
-        * Take into account the fact that the ramdisk might get relocated and
-        * therefore we need to recompute the container's position in virtual
-        * memory space.
-        */
-       if (relocated_ramdisk)
-               container = (u8 *)(__va(relocated_ramdisk) +
-                            (cont - boot_params.hdr.ramdisk_image));
-       else
-               container = cont_va;
-
-       if (ucode_new_rev)
-               pr_info("microcode: updated early to new patch_level=0x%08x\n",
-                       ucode_new_rev);
-
-       eax   = cpuid_eax(0x00000001);
-       eax   = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
-
-       ret = load_microcode_amd(smp_processor_id(), eax, container, container_size);
-       if (ret != UCODE_OK)
-               retval = -EINVAL;
-
-       /*
-        * This will be freed any msec now, stash patches for the current
-        * family and switch to patch cache for cpu hotplug, etc later.
-        */
-       container = NULL;
-       container_size = 0;
-
-       return retval;
-}
-
-void reload_ucode_amd(void)
-{
-       struct microcode_amd *mc;
-       u32 rev, eax;
-
-       rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);
-
-       mc = (struct microcode_amd *)amd_ucode_patch;
-
-       if (mc && rev < mc->hdr.patch_id) {
-               if (!__apply_microcode_amd(mc)) {
-                       ucode_new_rev = mc->hdr.patch_id;
-                       pr_info("microcode: reload patch_level=0x%08x\n",
-                               ucode_new_rev);
-               }
-       }
-}
index 9e3f3c7dd5d76124b12b167dc5c4e59129256b63..7fc27f1cca586a1752d95fa73c612db03db78437 100644 (file)
@@ -5,6 +5,12 @@
  *           2006      Shaohua Li <shaohua.li@intel.com>
  *           2013-2015 Borislav Petkov <bp@alien8.de>
  *
+ * X86 CPU microcode early update for Linux:
+ *
+ *     Copyright (C) 2012 Fenghua Yu <fenghua.yu@intel.com>
+ *                        H Peter Anvin" <hpa@zytor.com>
+ *               (C) 2015 Borislav Petkov <bp@alien8.de>
+ *
  * This driver allows to upgrade microcode on x86 processors.
  *
  * This program is free software; you can redistribute it and/or
  * 2 of the License, or (at your option) any later version.
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define pr_fmt(fmt) "microcode: " fmt
 
 #include <linux/platform_device.h>
+#include <linux/syscore_ops.h>
 #include <linux/miscdevice.h>
 #include <linux/capability.h>
+#include <linux/firmware.h>
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/cpu.h>
 #include <linux/fs.h>
 #include <linux/mm.h>
-#include <linux/syscore_ops.h>
 
-#include <asm/microcode.h>
-#include <asm/processor.h>
+#include <asm/microcode_intel.h>
 #include <asm/cpu_device_id.h>
+#include <asm/microcode_amd.h>
 #include <asm/perf_event.h>
+#include <asm/microcode.h>
+#include <asm/processor.h>
+#include <asm/cmdline.h>
 
-MODULE_DESCRIPTION("Microcode Update Driver");
-MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
-MODULE_LICENSE("GPL");
-
-#define MICROCODE_VERSION      "2.00"
+#define MICROCODE_VERSION      "2.01"
 
 static struct microcode_ops    *microcode_ops;
 
-bool dis_ucode_ldr;
-module_param(dis_ucode_ldr, bool, 0);
+static bool dis_ucode_ldr;
+
+static int __init disable_loader(char *str)
+{
+       dis_ucode_ldr = true;
+       return 1;
+}
+__setup("dis_ucode_ldr", disable_loader);
 
 /*
  * Synchronization.
@@ -68,6 +79,150 @@ struct cpu_info_ctx {
        int                     err;
 };
 
+static bool __init check_loader_disabled_bsp(void)
+{
+#ifdef CONFIG_X86_32
+       const char *cmdline = (const char *)__pa_nodebug(boot_command_line);
+       const char *opt     = "dis_ucode_ldr";
+       const char *option  = (const char *)__pa_nodebug(opt);
+       bool *res = (bool *)__pa_nodebug(&dis_ucode_ldr);
+
+#else /* CONFIG_X86_64 */
+       const char *cmdline = boot_command_line;
+       const char *option  = "dis_ucode_ldr";
+       bool *res = &dis_ucode_ldr;
+#endif
+
+       if (cmdline_find_option_bool(cmdline, option))
+               *res = true;
+
+       return *res;
+}
+
+extern struct builtin_fw __start_builtin_fw[];
+extern struct builtin_fw __end_builtin_fw[];
+
+bool get_builtin_firmware(struct cpio_data *cd, const char *name)
+{
+#ifdef CONFIG_FW_LOADER
+       struct builtin_fw *b_fw;
+
+       for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
+               if (!strcmp(name, b_fw->name)) {
+                       cd->size = b_fw->size;
+                       cd->data = b_fw->data;
+                       return true;
+               }
+       }
+#endif
+       return false;
+}
+
+void __init load_ucode_bsp(void)
+{
+       int vendor;
+       unsigned int family;
+
+       if (check_loader_disabled_bsp())
+               return;
+
+       if (!have_cpuid_p())
+               return;
+
+       vendor = x86_vendor();
+       family = x86_family();
+
+       switch (vendor) {
+       case X86_VENDOR_INTEL:
+               if (family >= 6)
+                       load_ucode_intel_bsp();
+               break;
+       case X86_VENDOR_AMD:
+               if (family >= 0x10)
+                       load_ucode_amd_bsp(family);
+               break;
+       default:
+               break;
+       }
+}
+
+static bool check_loader_disabled_ap(void)
+{
+#ifdef CONFIG_X86_32
+       return *((bool *)__pa_nodebug(&dis_ucode_ldr));
+#else
+       return dis_ucode_ldr;
+#endif
+}
+
+void load_ucode_ap(void)
+{
+       int vendor, family;
+
+       if (check_loader_disabled_ap())
+               return;
+
+       if (!have_cpuid_p())
+               return;
+
+       vendor = x86_vendor();
+       family = x86_family();
+
+       switch (vendor) {
+       case X86_VENDOR_INTEL:
+               if (family >= 6)
+                       load_ucode_intel_ap();
+               break;
+       case X86_VENDOR_AMD:
+               if (family >= 0x10)
+                       load_ucode_amd_ap();
+               break;
+       default:
+               break;
+       }
+}
+
+int __init save_microcode_in_initrd(void)
+{
+       struct cpuinfo_x86 *c = &boot_cpu_data;
+
+       switch (c->x86_vendor) {
+       case X86_VENDOR_INTEL:
+               if (c->x86 >= 6)
+                       save_microcode_in_initrd_intel();
+               break;
+       case X86_VENDOR_AMD:
+               if (c->x86 >= 0x10)
+                       save_microcode_in_initrd_amd();
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+void reload_early_microcode(void)
+{
+       int vendor, family;
+
+       vendor = x86_vendor();
+       family = x86_family();
+
+       switch (vendor) {
+       case X86_VENDOR_INTEL:
+               if (family >= 6)
+                       reload_ucode_intel();
+               break;
+       case X86_VENDOR_AMD:
+               if (family >= 0x10)
+                       reload_ucode_amd();
+               break;
+       default:
+               break;
+       }
+}
+
 static void collect_cpu_info_local(void *arg)
 {
        struct cpu_info_ctx *ctx = arg;
@@ -210,9 +365,6 @@ static void __exit microcode_dev_exit(void)
 {
        misc_deregister(&microcode_dev);
 }
-
-MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
-MODULE_ALIAS("devname:cpu/microcode");
 #else
 #define microcode_dev_init()   0
 #define microcode_dev_exit()   do { } while (0)
@@ -463,20 +615,6 @@ static struct notifier_block mc_cpu_notifier = {
        .notifier_call  = mc_cpu_callback,
 };
 
-#ifdef MODULE
-/* Autoload on Intel and AMD systems */
-static const struct x86_cpu_id __initconst microcode_id[] = {
-#ifdef CONFIG_MICROCODE_INTEL
-       { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, },
-#endif
-#ifdef CONFIG_MICROCODE_AMD
-       { X86_VENDOR_AMD, X86_FAMILY_ANY, X86_MODEL_ANY, },
-#endif
-       {}
-};
-MODULE_DEVICE_TABLE(x86cpu, microcode_id);
-#endif
-
 static struct attribute *cpu_root_microcode_attrs[] = {
        &dev_attr_reload.attr,
        NULL
@@ -487,9 +625,9 @@ static struct attribute_group cpu_root_microcode_group = {
        .attrs = cpu_root_microcode_attrs,
 };
 
-static int __init microcode_init(void)
+int __init microcode_init(void)
 {
-       struct cpuinfo_x86 *c = &cpu_data(0);
+       struct cpuinfo_x86 *c = &boot_cpu_data;
        int error;
 
        if (paravirt_enabled() || dis_ucode_ldr)
@@ -560,35 +698,3 @@ static int __init microcode_init(void)
        return error;
 
 }
-module_init(microcode_init);
-
-static void __exit microcode_exit(void)
-{
-       struct cpuinfo_x86 *c = &cpu_data(0);
-
-       microcode_dev_exit();
-
-       unregister_hotcpu_notifier(&mc_cpu_notifier);
-       unregister_syscore_ops(&mc_syscore_ops);
-
-       sysfs_remove_group(&cpu_subsys.dev_root->kobj,
-                          &cpu_root_microcode_group);
-
-       get_online_cpus();
-       mutex_lock(&microcode_mutex);
-
-       subsys_interface_unregister(&mc_cpu_interface);
-
-       mutex_unlock(&microcode_mutex);
-       put_online_cpus();
-
-       platform_device_unregister(microcode_pdev);
-
-       microcode_ops = NULL;
-
-       if (c->x86_vendor == X86_VENDOR_AMD)
-               exit_amd_microcode();
-
-       pr_info("Microcode Update Driver: v" MICROCODE_VERSION " removed.\n");
-}
-module_exit(microcode_exit);
diff --git a/arch/x86/kernel/cpu/microcode/core_early.c b/arch/x86/kernel/cpu/microcode/core_early.c
deleted file mode 100644 (file)
index 8ebc421..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- *     X86 CPU microcode early update for Linux
- *
- *     Copyright (C) 2012 Fenghua Yu <fenghua.yu@intel.com>
- *                        H Peter Anvin" <hpa@zytor.com>
- *               (C) 2015 Borislav Petkov <bp@alien8.de>
- *
- *     This driver allows to early upgrade microcode on Intel processors
- *     belonging to IA-32 family - PentiumPro, Pentium II,
- *     Pentium III, Xeon, Pentium 4, etc.
- *
- *     Reference: Section 9.11 of Volume 3, IA-32 Intel Architecture
- *     Software Developer's Manual.
- *
- *     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/module.h>
-#include <linux/firmware.h>
-#include <asm/microcode.h>
-#include <asm/microcode_intel.h>
-#include <asm/microcode_amd.h>
-#include <asm/processor.h>
-#include <asm/cmdline.h>
-
-static bool __init check_loader_disabled_bsp(void)
-{
-#ifdef CONFIG_X86_32
-       const char *cmdline = (const char *)__pa_nodebug(boot_command_line);
-       const char *opt     = "dis_ucode_ldr";
-       const char *option  = (const char *)__pa_nodebug(opt);
-       bool *res = (bool *)__pa_nodebug(&dis_ucode_ldr);
-
-#else /* CONFIG_X86_64 */
-       const char *cmdline = boot_command_line;
-       const char *option  = "dis_ucode_ldr";
-       bool *res = &dis_ucode_ldr;
-#endif
-
-       if (cmdline_find_option_bool(cmdline, option))
-               *res = true;
-
-       return *res;
-}
-
-extern struct builtin_fw __start_builtin_fw[];
-extern struct builtin_fw __end_builtin_fw[];
-
-bool get_builtin_firmware(struct cpio_data *cd, const char *name)
-{
-#ifdef CONFIG_FW_LOADER
-       struct builtin_fw *b_fw;
-
-       for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
-               if (!strcmp(name, b_fw->name)) {
-                       cd->size = b_fw->size;
-                       cd->data = b_fw->data;
-                       return true;
-               }
-       }
-#endif
-       return false;
-}
-
-void __init load_ucode_bsp(void)
-{
-       int vendor;
-       unsigned int family;
-
-       if (check_loader_disabled_bsp())
-               return;
-
-       if (!have_cpuid_p())
-               return;
-
-       vendor = x86_vendor();
-       family = x86_family();
-
-       switch (vendor) {
-       case X86_VENDOR_INTEL:
-               if (family >= 6)
-                       load_ucode_intel_bsp();
-               break;
-       case X86_VENDOR_AMD:
-               if (family >= 0x10)
-                       load_ucode_amd_bsp(family);
-               break;
-       default:
-               break;
-       }
-}
-
-static bool check_loader_disabled_ap(void)
-{
-#ifdef CONFIG_X86_32
-       return *((bool *)__pa_nodebug(&dis_ucode_ldr));
-#else
-       return dis_ucode_ldr;
-#endif
-}
-
-void load_ucode_ap(void)
-{
-       int vendor, family;
-
-       if (check_loader_disabled_ap())
-               return;
-
-       if (!have_cpuid_p())
-               return;
-
-       vendor = x86_vendor();
-       family = x86_family();
-
-       switch (vendor) {
-       case X86_VENDOR_INTEL:
-               if (family >= 6)
-                       load_ucode_intel_ap();
-               break;
-       case X86_VENDOR_AMD:
-               if (family >= 0x10)
-                       load_ucode_amd_ap();
-               break;
-       default:
-               break;
-       }
-}
-
-int __init save_microcode_in_initrd(void)
-{
-       struct cpuinfo_x86 *c = &boot_cpu_data;
-
-       switch (c->x86_vendor) {
-       case X86_VENDOR_INTEL:
-               if (c->x86 >= 6)
-                       save_microcode_in_initrd_intel();
-               break;
-       case X86_VENDOR_AMD:
-               if (c->x86 >= 0x10)
-                       save_microcode_in_initrd_amd();
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-void reload_early_microcode(void)
-{
-       int vendor, family;
-
-       vendor = x86_vendor();
-       family = x86_family();
-
-       switch (vendor) {
-       case X86_VENDOR_INTEL:
-               if (family >= 6)
-                       reload_ucode_intel();
-               break;
-       case X86_VENDOR_AMD:
-               if (family >= 0x10)
-                       reload_ucode_amd();
-               break;
-       default:
-               break;
-       }
-}
index 969dc17eb1b4b86775d5496bb6ebe9ba67110b4c..ce47402eb2f90a70f44b9902687133da3acbad47 100644 (file)
  * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
  *              2006 Shaohua Li <shaohua.li@intel.com>
  *
+ * Intel CPU microcode early update for Linux
+ *
+ * Copyright (C) 2012 Fenghua Yu <fenghua.yu@intel.com>
+ *                   H Peter Anvin" <hpa@zytor.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 the Free Software Foundation; either version
  * 2 of the License, or (at your option) any later version.
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+/*
+ * This needs to be before all headers so that pr_debug in printk.h doesn't turn
+ * printk calls into no_printk().
+ *
+ *#define DEBUG
+ */
+#define pr_fmt(fmt) "microcode: " fmt
 
+#include <linux/earlycpio.h>
 #include <linux/firmware.h>
 #include <linux/uaccess.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/vmalloc.h>
+#include <linux/initrd.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
 
 #include <asm/microcode_intel.h>
 #include <asm/processor.h>
+#include <asm/tlbflush.h>
+#include <asm/setup.h>
 #include <asm/msr.h>
 
-MODULE_DESCRIPTION("Microcode Update Driver");
-MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
-MODULE_LICENSE("GPL");
+static unsigned long mc_saved_in_initrd[MAX_UCODE_COUNT];
+static struct mc_saved_data {
+       unsigned int mc_saved_count;
+       struct microcode_intel **mc_saved;
+} mc_saved_data;
+
+static enum ucode_state
+load_microcode_early(struct microcode_intel **saved,
+                    unsigned int num_saved, struct ucode_cpu_info *uci)
+{
+       struct microcode_intel *ucode_ptr, *new_mc = NULL;
+       struct microcode_header_intel *mc_hdr;
+       int new_rev, ret, i;
+
+       new_rev = uci->cpu_sig.rev;
+
+       for (i = 0; i < num_saved; i++) {
+               ucode_ptr = saved[i];
+               mc_hdr    = (struct microcode_header_intel *)ucode_ptr;
+
+               ret = has_newer_microcode(ucode_ptr,
+                                         uci->cpu_sig.sig,
+                                         uci->cpu_sig.pf,
+                                         new_rev);
+               if (!ret)
+                       continue;
+
+               new_rev = mc_hdr->rev;
+               new_mc  = ucode_ptr;
+       }
+
+       if (!new_mc)
+               return UCODE_NFOUND;
+
+       uci->mc = (struct microcode_intel *)new_mc;
+       return UCODE_OK;
+}
+
+static inline void
+copy_initrd_ptrs(struct microcode_intel **mc_saved, unsigned long *initrd,
+                 unsigned long off, int num_saved)
+{
+       int i;
+
+       for (i = 0; i < num_saved; i++)
+               mc_saved[i] = (struct microcode_intel *)(initrd[i] + off);
+}
+
+#ifdef CONFIG_X86_32
+static void
+microcode_phys(struct microcode_intel **mc_saved_tmp,
+              struct mc_saved_data *mc_saved_data)
+{
+       int i;
+       struct microcode_intel ***mc_saved;
+
+       mc_saved = (struct microcode_intel ***)
+                  __pa_nodebug(&mc_saved_data->mc_saved);
+       for (i = 0; i < mc_saved_data->mc_saved_count; i++) {
+               struct microcode_intel *p;
+
+               p = *(struct microcode_intel **)
+                       __pa_nodebug(mc_saved_data->mc_saved + i);
+               mc_saved_tmp[i] = (struct microcode_intel *)__pa_nodebug(p);
+       }
+}
+#endif
+
+static enum ucode_state
+load_microcode(struct mc_saved_data *mc_saved_data, unsigned long *initrd,
+              unsigned long initrd_start, struct ucode_cpu_info *uci)
+{
+       struct microcode_intel *mc_saved_tmp[MAX_UCODE_COUNT];
+       unsigned int count = mc_saved_data->mc_saved_count;
+
+       if (!mc_saved_data->mc_saved) {
+               copy_initrd_ptrs(mc_saved_tmp, initrd, initrd_start, count);
+
+               return load_microcode_early(mc_saved_tmp, count, uci);
+       } else {
+#ifdef CONFIG_X86_32
+               microcode_phys(mc_saved_tmp, mc_saved_data);
+               return load_microcode_early(mc_saved_tmp, count, uci);
+#else
+               return load_microcode_early(mc_saved_data->mc_saved,
+                                                   count, uci);
+#endif
+       }
+}
+
+/*
+ * Given CPU signature and a microcode patch, this function finds if the
+ * microcode patch has matching family and model with the CPU.
+ */
+static enum ucode_state
+matching_model_microcode(struct microcode_header_intel *mc_header,
+                       unsigned long sig)
+{
+       unsigned int fam, model;
+       unsigned int fam_ucode, model_ucode;
+       struct extended_sigtable *ext_header;
+       unsigned long total_size = get_totalsize(mc_header);
+       unsigned long data_size = get_datasize(mc_header);
+       int ext_sigcount, i;
+       struct extended_signature *ext_sig;
+
+       fam   = __x86_family(sig);
+       model = x86_model(sig);
+
+       fam_ucode   = __x86_family(mc_header->sig);
+       model_ucode = x86_model(mc_header->sig);
+
+       if (fam == fam_ucode && model == model_ucode)
+               return UCODE_OK;
+
+       /* Look for ext. headers: */
+       if (total_size <= data_size + MC_HEADER_SIZE)
+               return UCODE_NFOUND;
+
+       ext_header   = (void *) mc_header + data_size + MC_HEADER_SIZE;
+       ext_sig      = (void *)ext_header + EXT_HEADER_SIZE;
+       ext_sigcount = ext_header->count;
+
+       for (i = 0; i < ext_sigcount; i++) {
+               fam_ucode   = __x86_family(ext_sig->sig);
+               model_ucode = x86_model(ext_sig->sig);
+
+               if (fam == fam_ucode && model == model_ucode)
+                       return UCODE_OK;
+
+               ext_sig++;
+       }
+       return UCODE_NFOUND;
+}
+
+static int
+save_microcode(struct mc_saved_data *mc_saved_data,
+              struct microcode_intel **mc_saved_src,
+              unsigned int mc_saved_count)
+{
+       int i, j;
+       struct microcode_intel **saved_ptr;
+       int ret;
+
+       if (!mc_saved_count)
+               return -EINVAL;
+
+       /*
+        * Copy new microcode data.
+        */
+       saved_ptr = kcalloc(mc_saved_count, sizeof(struct microcode_intel *), GFP_KERNEL);
+       if (!saved_ptr)
+               return -ENOMEM;
+
+       for (i = 0; i < mc_saved_count; i++) {
+               struct microcode_header_intel *mc_hdr;
+               struct microcode_intel *mc;
+               unsigned long size;
+
+               if (!mc_saved_src[i]) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               mc     = mc_saved_src[i];
+               mc_hdr = &mc->hdr;
+               size   = get_totalsize(mc_hdr);
+
+               saved_ptr[i] = kmalloc(size, GFP_KERNEL);
+               if (!saved_ptr[i]) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+
+               memcpy(saved_ptr[i], mc, size);
+       }
+
+       /*
+        * Point to newly saved microcode.
+        */
+       mc_saved_data->mc_saved = saved_ptr;
+       mc_saved_data->mc_saved_count = mc_saved_count;
+
+       return 0;
+
+err:
+       for (j = 0; j <= i; j++)
+               kfree(saved_ptr[j]);
+       kfree(saved_ptr);
+
+       return ret;
+}
+
+/*
+ * A microcode patch in ucode_ptr is saved into mc_saved
+ * - if it has matching signature and newer revision compared to an existing
+ *   patch mc_saved.
+ * - or if it is a newly discovered microcode patch.
+ *
+ * The microcode patch should have matching model with CPU.
+ *
+ * Returns: The updated number @num_saved of saved microcode patches.
+ */
+static unsigned int _save_mc(struct microcode_intel **mc_saved,
+                            u8 *ucode_ptr, unsigned int num_saved)
+{
+       struct microcode_header_intel *mc_hdr, *mc_saved_hdr;
+       unsigned int sig, pf;
+       int found = 0, i;
+
+       mc_hdr = (struct microcode_header_intel *)ucode_ptr;
+
+       for (i = 0; i < num_saved; i++) {
+               mc_saved_hdr = (struct microcode_header_intel *)mc_saved[i];
+               sig          = mc_saved_hdr->sig;
+               pf           = mc_saved_hdr->pf;
+
+               if (!find_matching_signature(ucode_ptr, sig, pf))
+                       continue;
+
+               found = 1;
+
+               if (mc_hdr->rev <= mc_saved_hdr->rev)
+                       continue;
+
+               /*
+                * Found an older ucode saved earlier. Replace it with
+                * this newer one.
+                */
+               mc_saved[i] = (struct microcode_intel *)ucode_ptr;
+               break;
+       }
+
+       /* Newly detected microcode, save it to memory. */
+       if (i >= num_saved && !found)
+               mc_saved[num_saved++] = (struct microcode_intel *)ucode_ptr;
+
+       return num_saved;
+}
+
+/*
+ * Get microcode matching with BSP's model. Only CPUs with the same model as
+ * BSP can stay in the platform.
+ */
+static enum ucode_state __init
+get_matching_model_microcode(int cpu, unsigned long start,
+                            void *data, size_t size,
+                            struct mc_saved_data *mc_saved_data,
+                            unsigned long *mc_saved_in_initrd,
+                            struct ucode_cpu_info *uci)
+{
+       u8 *ucode_ptr = data;
+       unsigned int leftover = size;
+       enum ucode_state state = UCODE_OK;
+       unsigned int mc_size;
+       struct microcode_header_intel *mc_header;
+       struct microcode_intel *mc_saved_tmp[MAX_UCODE_COUNT];
+       unsigned int mc_saved_count = mc_saved_data->mc_saved_count;
+       int i;
+
+       while (leftover && mc_saved_count < ARRAY_SIZE(mc_saved_tmp)) {
+
+               if (leftover < sizeof(mc_header))
+                       break;
+
+               mc_header = (struct microcode_header_intel *)ucode_ptr;
+
+               mc_size = get_totalsize(mc_header);
+               if (!mc_size || mc_size > leftover ||
+                       microcode_sanity_check(ucode_ptr, 0) < 0)
+                       break;
+
+               leftover -= mc_size;
+
+               /*
+                * Since APs with same family and model as the BSP may boot in
+                * the platform, we need to find and save microcode patches
+                * with the same family and model as the BSP.
+                */
+               if (matching_model_microcode(mc_header, uci->cpu_sig.sig) !=
+                        UCODE_OK) {
+                       ucode_ptr += mc_size;
+                       continue;
+               }
+
+               mc_saved_count = _save_mc(mc_saved_tmp, ucode_ptr, mc_saved_count);
+
+               ucode_ptr += mc_size;
+       }
+
+       if (leftover) {
+               state = UCODE_ERROR;
+               goto out;
+       }
+
+       if (mc_saved_count == 0) {
+               state = UCODE_NFOUND;
+               goto out;
+       }
+
+       for (i = 0; i < mc_saved_count; i++)
+               mc_saved_in_initrd[i] = (unsigned long)mc_saved_tmp[i] - start;
+
+       mc_saved_data->mc_saved_count = mc_saved_count;
+out:
+       return state;
+}
+
+static int collect_cpu_info_early(struct ucode_cpu_info *uci)
+{
+       unsigned int val[2];
+       unsigned int family, model;
+       struct cpu_signature csig;
+       unsigned int eax, ebx, ecx, edx;
+
+       csig.sig = 0;
+       csig.pf = 0;
+       csig.rev = 0;
+
+       memset(uci, 0, sizeof(*uci));
+
+       eax = 0x00000001;
+       ecx = 0;
+       native_cpuid(&eax, &ebx, &ecx, &edx);
+       csig.sig = eax;
+
+       family = __x86_family(csig.sig);
+       model  = x86_model(csig.sig);
+
+       if ((model >= 5) || (family > 6)) {
+               /* get processor flags from MSR 0x17 */
+               native_rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
+               csig.pf = 1 << ((val[1] >> 18) & 7);
+       }
+       native_wrmsr(MSR_IA32_UCODE_REV, 0, 0);
+
+       /* As documented in the SDM: Do a CPUID 1 here */
+       sync_core();
+
+       /* get the current revision from MSR 0x8B */
+       native_rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);
+
+       csig.rev = val[1];
+
+       uci->cpu_sig = csig;
+       uci->valid = 1;
+
+       return 0;
+}
+
+static void show_saved_mc(void)
+{
+#ifdef DEBUG
+       int i, j;
+       unsigned int sig, pf, rev, total_size, data_size, date;
+       struct ucode_cpu_info uci;
+
+       if (mc_saved_data.mc_saved_count == 0) {
+               pr_debug("no microcode data saved.\n");
+               return;
+       }
+       pr_debug("Total microcode saved: %d\n", mc_saved_data.mc_saved_count);
+
+       collect_cpu_info_early(&uci);
+
+       sig = uci.cpu_sig.sig;
+       pf = uci.cpu_sig.pf;
+       rev = uci.cpu_sig.rev;
+       pr_debug("CPU: sig=0x%x, pf=0x%x, rev=0x%x\n", sig, pf, rev);
+
+       for (i = 0; i < mc_saved_data.mc_saved_count; i++) {
+               struct microcode_header_intel *mc_saved_header;
+               struct extended_sigtable *ext_header;
+               int ext_sigcount;
+               struct extended_signature *ext_sig;
+
+               mc_saved_header = (struct microcode_header_intel *)
+                                 mc_saved_data.mc_saved[i];
+               sig = mc_saved_header->sig;
+               pf = mc_saved_header->pf;
+               rev = mc_saved_header->rev;
+               total_size = get_totalsize(mc_saved_header);
+               data_size = get_datasize(mc_saved_header);
+               date = mc_saved_header->date;
+
+               pr_debug("mc_saved[%d]: sig=0x%x, pf=0x%x, rev=0x%x, toal size=0x%x, date = %04x-%02x-%02x\n",
+                        i, sig, pf, rev, total_size,
+                        date & 0xffff,
+                        date >> 24,
+                        (date >> 16) & 0xff);
+
+               /* Look for ext. headers: */
+               if (total_size <= data_size + MC_HEADER_SIZE)
+                       continue;
+
+               ext_header = (void *) mc_saved_header + data_size + MC_HEADER_SIZE;
+               ext_sigcount = ext_header->count;
+               ext_sig = (void *)ext_header + EXT_HEADER_SIZE;
+
+               for (j = 0; j < ext_sigcount; j++) {
+                       sig = ext_sig->sig;
+                       pf = ext_sig->pf;
+
+                       pr_debug("\tExtended[%d]: sig=0x%x, pf=0x%x\n",
+                                j, sig, pf);
+
+                       ext_sig++;
+               }
+
+       }
+#endif
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static DEFINE_MUTEX(x86_cpu_microcode_mutex);
+/*
+ * Save this mc into mc_saved_data. So it will be loaded early when a CPU is
+ * hot added or resumes.
+ *
+ * Please make sure this mc should be a valid microcode patch before calling
+ * this function.
+ */
+int save_mc_for_early(u8 *mc)
+{
+       struct microcode_intel *mc_saved_tmp[MAX_UCODE_COUNT];
+       unsigned int mc_saved_count_init;
+       unsigned int mc_saved_count;
+       struct microcode_intel **mc_saved;
+       int ret = 0;
+       int i;
+
+       /*
+        * Hold hotplug lock so mc_saved_data is not accessed by a CPU in
+        * hotplug.
+        */
+       mutex_lock(&x86_cpu_microcode_mutex);
+
+       mc_saved_count_init = mc_saved_data.mc_saved_count;
+       mc_saved_count = mc_saved_data.mc_saved_count;
+       mc_saved = mc_saved_data.mc_saved;
+
+       if (mc_saved && mc_saved_count)
+               memcpy(mc_saved_tmp, mc_saved,
+                      mc_saved_count * sizeof(struct microcode_intel *));
+       /*
+        * Save the microcode patch mc in mc_save_tmp structure if it's a newer
+        * version.
+        */
+       mc_saved_count = _save_mc(mc_saved_tmp, mc, mc_saved_count);
+
+       /*
+        * Save the mc_save_tmp in global mc_saved_data.
+        */
+       ret = save_microcode(&mc_saved_data, mc_saved_tmp, mc_saved_count);
+       if (ret) {
+               pr_err("Cannot save microcode patch.\n");
+               goto out;
+       }
+
+       show_saved_mc();
+
+       /*
+        * Free old saved microcode data.
+        */
+       if (mc_saved) {
+               for (i = 0; i < mc_saved_count_init; i++)
+                       kfree(mc_saved[i]);
+               kfree(mc_saved);
+       }
+
+out:
+       mutex_unlock(&x86_cpu_microcode_mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(save_mc_for_early);
+#endif
+
+static bool __init load_builtin_intel_microcode(struct cpio_data *cp)
+{
+#ifdef CONFIG_X86_64
+       unsigned int eax = 0x00000001, ebx, ecx = 0, edx;
+       unsigned int family, model, stepping;
+       char name[30];
+
+       native_cpuid(&eax, &ebx, &ecx, &edx);
+
+       family   = __x86_family(eax);
+       model    = x86_model(eax);
+       stepping = eax & 0xf;
+
+       sprintf(name, "intel-ucode/%02x-%02x-%02x", family, model, stepping);
+
+       return get_builtin_firmware(cp, name);
+#else
+       return false;
+#endif
+}
+
+static __initdata char ucode_name[] = "kernel/x86/microcode/GenuineIntel.bin";
+static __init enum ucode_state
+scan_microcode(struct mc_saved_data *mc_saved_data, unsigned long *initrd,
+              unsigned long start, unsigned long size,
+              struct ucode_cpu_info *uci)
+{
+       struct cpio_data cd;
+       long offset = 0;
+#ifdef CONFIG_X86_32
+       char *p = (char *)__pa_nodebug(ucode_name);
+#else
+       char *p = ucode_name;
+#endif
+
+       cd.data = NULL;
+       cd.size = 0;
+
+       cd = find_cpio_data(p, (void *)start, size, &offset);
+       if (!cd.data) {
+               if (!load_builtin_intel_microcode(&cd))
+                       return UCODE_ERROR;
+       }
+
+       return get_matching_model_microcode(0, start, cd.data, cd.size,
+                                           mc_saved_data, initrd, uci);
+}
+
+/*
+ * Print ucode update info.
+ */
+static void
+print_ucode_info(struct ucode_cpu_info *uci, unsigned int date)
+{
+       int cpu = smp_processor_id();
+
+       pr_info("CPU%d microcode updated early to revision 0x%x, date = %04x-%02x-%02x\n",
+               cpu,
+               uci->cpu_sig.rev,
+               date & 0xffff,
+               date >> 24,
+               (date >> 16) & 0xff);
+}
+
+#ifdef CONFIG_X86_32
+
+static int delay_ucode_info;
+static int current_mc_date;
+
+/*
+ * Print early updated ucode info after printk works. This is delayed info dump.
+ */
+void show_ucode_info_early(void)
+{
+       struct ucode_cpu_info uci;
+
+       if (delay_ucode_info) {
+               collect_cpu_info_early(&uci);
+               print_ucode_info(&uci, current_mc_date);
+               delay_ucode_info = 0;
+       }
+}
+
+/*
+ * At this point, we can not call printk() yet. Keep microcode patch number in
+ * mc_saved_data.mc_saved and delay printing microcode info in
+ * show_ucode_info_early() until printk() works.
+ */
+static void print_ucode(struct ucode_cpu_info *uci)
+{
+       struct microcode_intel *mc_intel;
+       int *delay_ucode_info_p;
+       int *current_mc_date_p;
+
+       mc_intel = uci->mc;
+       if (mc_intel == NULL)
+               return;
+
+       delay_ucode_info_p = (int *)__pa_nodebug(&delay_ucode_info);
+       current_mc_date_p = (int *)__pa_nodebug(&current_mc_date);
+
+       *delay_ucode_info_p = 1;
+       *current_mc_date_p = mc_intel->hdr.date;
+}
+#else
+
+/*
+ * Flush global tlb. We only do this in x86_64 where paging has been enabled
+ * already and PGE should be enabled as well.
+ */
+static inline void flush_tlb_early(void)
+{
+       __native_flush_tlb_global_irq_disabled();
+}
+
+static inline void print_ucode(struct ucode_cpu_info *uci)
+{
+       struct microcode_intel *mc_intel;
+
+       mc_intel = uci->mc;
+       if (mc_intel == NULL)
+               return;
+
+       print_ucode_info(uci, mc_intel->hdr.date);
+}
+#endif
+
+static int apply_microcode_early(struct ucode_cpu_info *uci, bool early)
+{
+       struct microcode_intel *mc_intel;
+       unsigned int val[2];
+
+       mc_intel = uci->mc;
+       if (mc_intel == NULL)
+               return 0;
+
+       /* write microcode via MSR 0x79 */
+       native_wrmsr(MSR_IA32_UCODE_WRITE,
+             (unsigned long) mc_intel->bits,
+             (unsigned long) mc_intel->bits >> 16 >> 16);
+       native_wrmsr(MSR_IA32_UCODE_REV, 0, 0);
+
+       /* As documented in the SDM: Do a CPUID 1 here */
+       sync_core();
+
+       /* get the current revision from MSR 0x8B */
+       native_rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);
+       if (val[1] != mc_intel->hdr.rev)
+               return -1;
+
+#ifdef CONFIG_X86_64
+       /* Flush global tlb. This is precaution. */
+       flush_tlb_early();
+#endif
+       uci->cpu_sig.rev = val[1];
+
+       if (early)
+               print_ucode(uci);
+       else
+               print_ucode_info(uci, mc_intel->hdr.date);
+
+       return 0;
+}
+
+/*
+ * This function converts microcode patch offsets previously stored in
+ * mc_saved_in_initrd to pointers and stores the pointers in mc_saved_data.
+ */
+int __init save_microcode_in_initrd_intel(void)
+{
+       unsigned int count = mc_saved_data.mc_saved_count;
+       struct microcode_intel *mc_saved[MAX_UCODE_COUNT];
+       int ret = 0;
+
+       if (count == 0)
+               return ret;
+
+       copy_initrd_ptrs(mc_saved, mc_saved_in_initrd, initrd_start, count);
+       ret = save_microcode(&mc_saved_data, mc_saved, count);
+       if (ret)
+               pr_err("Cannot save microcode patches from initrd.\n");
+
+       show_saved_mc();
+
+       return ret;
+}
+
+static void __init
+_load_ucode_intel_bsp(struct mc_saved_data *mc_saved_data,
+                     unsigned long *initrd,
+                     unsigned long start, unsigned long size)
+{
+       struct ucode_cpu_info uci;
+       enum ucode_state ret;
+
+       collect_cpu_info_early(&uci);
+
+       ret = scan_microcode(mc_saved_data, initrd, start, size, &uci);
+       if (ret != UCODE_OK)
+               return;
+
+       ret = load_microcode(mc_saved_data, initrd, start, &uci);
+       if (ret != UCODE_OK)
+               return;
+
+       apply_microcode_early(&uci, true);
+}
+
+void __init load_ucode_intel_bsp(void)
+{
+       u64 start, size;
+#ifdef CONFIG_X86_32
+       struct boot_params *p;
+
+       p       = (struct boot_params *)__pa_nodebug(&boot_params);
+       start   = p->hdr.ramdisk_image;
+       size    = p->hdr.ramdisk_size;
+
+       _load_ucode_intel_bsp(
+                       (struct mc_saved_data *)__pa_nodebug(&mc_saved_data),
+                       (unsigned long *)__pa_nodebug(&mc_saved_in_initrd),
+                       start, size);
+#else
+       start   = boot_params.hdr.ramdisk_image + PAGE_OFFSET;
+       size    = boot_params.hdr.ramdisk_size;
+
+       _load_ucode_intel_bsp(&mc_saved_data, mc_saved_in_initrd, start, size);
+#endif
+}
+
+void load_ucode_intel_ap(void)
+{
+       struct mc_saved_data *mc_saved_data_p;
+       struct ucode_cpu_info uci;
+       unsigned long *mc_saved_in_initrd_p;
+       unsigned long initrd_start_addr;
+       enum ucode_state ret;
+#ifdef CONFIG_X86_32
+       unsigned long *initrd_start_p;
+
+       mc_saved_in_initrd_p =
+               (unsigned long *)__pa_nodebug(mc_saved_in_initrd);
+       mc_saved_data_p = (struct mc_saved_data *)__pa_nodebug(&mc_saved_data);
+       initrd_start_p = (unsigned long *)__pa_nodebug(&initrd_start);
+       initrd_start_addr = (unsigned long)__pa_nodebug(*initrd_start_p);
+#else
+       mc_saved_data_p = &mc_saved_data;
+       mc_saved_in_initrd_p = mc_saved_in_initrd;
+       initrd_start_addr = initrd_start;
+#endif
+
+       /*
+        * If there is no valid ucode previously saved in memory, no need to
+        * update ucode on this AP.
+        */
+       if (mc_saved_data_p->mc_saved_count == 0)
+               return;
+
+       collect_cpu_info_early(&uci);
+       ret = load_microcode(mc_saved_data_p, mc_saved_in_initrd_p,
+                            initrd_start_addr, &uci);
+
+       if (ret != UCODE_OK)
+               return;
+
+       apply_microcode_early(&uci, true);
+}
+
+void reload_ucode_intel(void)
+{
+       struct ucode_cpu_info uci;
+       enum ucode_state ret;
+
+       if (!mc_saved_data.mc_saved_count)
+               return;
+
+       collect_cpu_info_early(&uci);
+
+       ret = load_microcode_early(mc_saved_data.mc_saved,
+                                  mc_saved_data.mc_saved_count, &uci);
+       if (ret != UCODE_OK)
+               return;
+
+       apply_microcode_early(&uci, false);
+}
 
 static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
 {
@@ -264,7 +1041,7 @@ static struct microcode_ops microcode_intel_ops = {
 
 struct microcode_ops * __init init_intel_microcode(void)
 {
-       struct cpuinfo_x86 *c = &cpu_data(0);
+       struct cpuinfo_x86 *c = &boot_cpu_data;
 
        if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 ||
            cpu_has(c, X86_FEATURE_IA64)) {
diff --git a/arch/x86/kernel/cpu/microcode/intel_early.c b/arch/x86/kernel/cpu/microcode/intel_early.c
deleted file mode 100644 (file)
index 37ea89c..0000000
+++ /dev/null
@@ -1,808 +0,0 @@
-/*
- *     Intel CPU microcode early update for Linux
- *
- *     Copyright (C) 2012 Fenghua Yu <fenghua.yu@intel.com>
- *                        H Peter Anvin" <hpa@zytor.com>
- *
- *     This allows to early upgrade microcode on Intel processors
- *     belonging to IA-32 family - PentiumPro, Pentium II,
- *     Pentium III, Xeon, Pentium 4, etc.
- *
- *     Reference: Section 9.11 of Volume 3, IA-32 Intel Architecture
- *     Software Developer's Manual.
- *
- *     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 needs to be before all headers so that pr_debug in printk.h doesn't turn
- * printk calls into no_printk().
- *
- *#define DEBUG
- */
-
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/earlycpio.h>
-#include <linux/initrd.h>
-#include <linux/cpu.h>
-#include <asm/msr.h>
-#include <asm/microcode_intel.h>
-#include <asm/processor.h>
-#include <asm/tlbflush.h>
-#include <asm/setup.h>
-
-#undef pr_fmt
-#define pr_fmt(fmt)    "microcode: " fmt
-
-static unsigned long mc_saved_in_initrd[MAX_UCODE_COUNT];
-static struct mc_saved_data {
-       unsigned int mc_saved_count;
-       struct microcode_intel **mc_saved;
-} mc_saved_data;
-
-static enum ucode_state
-load_microcode_early(struct microcode_intel **saved,
-                    unsigned int num_saved, struct ucode_cpu_info *uci)
-{
-       struct microcode_intel *ucode_ptr, *new_mc = NULL;
-       struct microcode_header_intel *mc_hdr;
-       int new_rev, ret, i;
-
-       new_rev = uci->cpu_sig.rev;
-
-       for (i = 0; i < num_saved; i++) {
-               ucode_ptr = saved[i];
-               mc_hdr    = (struct microcode_header_intel *)ucode_ptr;
-
-               ret = has_newer_microcode(ucode_ptr,
-                                         uci->cpu_sig.sig,
-                                         uci->cpu_sig.pf,
-                                         new_rev);
-               if (!ret)
-                       continue;
-
-               new_rev = mc_hdr->rev;
-               new_mc  = ucode_ptr;
-       }
-
-       if (!new_mc)
-               return UCODE_NFOUND;
-
-       uci->mc = (struct microcode_intel *)new_mc;
-       return UCODE_OK;
-}
-
-static inline void
-copy_initrd_ptrs(struct microcode_intel **mc_saved, unsigned long *initrd,
-                 unsigned long off, int num_saved)
-{
-       int i;
-
-       for (i = 0; i < num_saved; i++)
-               mc_saved[i] = (struct microcode_intel *)(initrd[i] + off);
-}
-
-#ifdef CONFIG_X86_32
-static void
-microcode_phys(struct microcode_intel **mc_saved_tmp,
-              struct mc_saved_data *mc_saved_data)
-{
-       int i;
-       struct microcode_intel ***mc_saved;
-
-       mc_saved = (struct microcode_intel ***)
-                  __pa_nodebug(&mc_saved_data->mc_saved);
-       for (i = 0; i < mc_saved_data->mc_saved_count; i++) {
-               struct microcode_intel *p;
-
-               p = *(struct microcode_intel **)
-                       __pa_nodebug(mc_saved_data->mc_saved + i);
-               mc_saved_tmp[i] = (struct microcode_intel *)__pa_nodebug(p);
-       }
-}
-#endif
-
-static enum ucode_state
-load_microcode(struct mc_saved_data *mc_saved_data, unsigned long *initrd,
-              unsigned long initrd_start, struct ucode_cpu_info *uci)
-{
-       struct microcode_intel *mc_saved_tmp[MAX_UCODE_COUNT];
-       unsigned int count = mc_saved_data->mc_saved_count;
-
-       if (!mc_saved_data->mc_saved) {
-               copy_initrd_ptrs(mc_saved_tmp, initrd, initrd_start, count);
-
-               return load_microcode_early(mc_saved_tmp, count, uci);
-       } else {
-#ifdef CONFIG_X86_32
-               microcode_phys(mc_saved_tmp, mc_saved_data);
-               return load_microcode_early(mc_saved_tmp, count, uci);
-#else
-               return load_microcode_early(mc_saved_data->mc_saved,
-                                                   count, uci);
-#endif
-       }
-}
-
-/*
- * Given CPU signature and a microcode patch, this function finds if the
- * microcode patch has matching family and model with the CPU.
- */
-static enum ucode_state
-matching_model_microcode(struct microcode_header_intel *mc_header,
-                       unsigned long sig)
-{
-       unsigned int fam, model;
-       unsigned int fam_ucode, model_ucode;
-       struct extended_sigtable *ext_header;
-       unsigned long total_size = get_totalsize(mc_header);
-       unsigned long data_size = get_datasize(mc_header);
-       int ext_sigcount, i;
-       struct extended_signature *ext_sig;
-
-       fam   = __x86_family(sig);
-       model = x86_model(sig);
-
-       fam_ucode   = __x86_family(mc_header->sig);
-       model_ucode = x86_model(mc_header->sig);
-
-       if (fam == fam_ucode && model == model_ucode)
-               return UCODE_OK;
-
-       /* Look for ext. headers: */
-       if (total_size <= data_size + MC_HEADER_SIZE)
-               return UCODE_NFOUND;
-
-       ext_header   = (void *) mc_header + data_size + MC_HEADER_SIZE;
-       ext_sig      = (void *)ext_header + EXT_HEADER_SIZE;
-       ext_sigcount = ext_header->count;
-
-       for (i = 0; i < ext_sigcount; i++) {
-               fam_ucode   = __x86_family(ext_sig->sig);
-               model_ucode = x86_model(ext_sig->sig);
-
-               if (fam == fam_ucode && model == model_ucode)
-                       return UCODE_OK;
-
-               ext_sig++;
-       }
-       return UCODE_NFOUND;
-}
-
-static int
-save_microcode(struct mc_saved_data *mc_saved_data,
-              struct microcode_intel **mc_saved_src,
-              unsigned int mc_saved_count)
-{
-       int i, j;
-       struct microcode_intel **saved_ptr;
-       int ret;
-
-       if (!mc_saved_count)
-               return -EINVAL;
-
-       /*
-        * Copy new microcode data.
-        */
-       saved_ptr = kcalloc(mc_saved_count, sizeof(struct microcode_intel *), GFP_KERNEL);
-       if (!saved_ptr)
-               return -ENOMEM;
-
-       for (i = 0; i < mc_saved_count; i++) {
-               struct microcode_header_intel *mc_hdr;
-               struct microcode_intel *mc;
-               unsigned long size;
-
-               if (!mc_saved_src[i]) {
-                       ret = -EINVAL;
-                       goto err;
-               }
-
-               mc     = mc_saved_src[i];
-               mc_hdr = &mc->hdr;
-               size   = get_totalsize(mc_hdr);
-
-               saved_ptr[i] = kmalloc(size, GFP_KERNEL);
-               if (!saved_ptr[i]) {
-                       ret = -ENOMEM;
-                       goto err;
-               }
-
-               memcpy(saved_ptr[i], mc, size);
-       }
-
-       /*
-        * Point to newly saved microcode.
-        */
-       mc_saved_data->mc_saved = saved_ptr;
-       mc_saved_data->mc_saved_count = mc_saved_count;
-
-       return 0;
-
-err:
-       for (j = 0; j <= i; j++)
-               kfree(saved_ptr[j]);
-       kfree(saved_ptr);
-
-       return ret;
-}
-
-/*
- * A microcode patch in ucode_ptr is saved into mc_saved
- * - if it has matching signature and newer revision compared to an existing
- *   patch mc_saved.
- * - or if it is a newly discovered microcode patch.
- *
- * The microcode patch should have matching model with CPU.
- *
- * Returns: The updated number @num_saved of saved microcode patches.
- */
-static unsigned int _save_mc(struct microcode_intel **mc_saved,
-                            u8 *ucode_ptr, unsigned int num_saved)
-{
-       struct microcode_header_intel *mc_hdr, *mc_saved_hdr;
-       unsigned int sig, pf;
-       int found = 0, i;
-
-       mc_hdr = (struct microcode_header_intel *)ucode_ptr;
-
-       for (i = 0; i < num_saved; i++) {
-               mc_saved_hdr = (struct microcode_header_intel *)mc_saved[i];
-               sig          = mc_saved_hdr->sig;
-               pf           = mc_saved_hdr->pf;
-
-               if (!find_matching_signature(ucode_ptr, sig, pf))
-                       continue;
-
-               found = 1;
-
-               if (mc_hdr->rev <= mc_saved_hdr->rev)
-                       continue;
-
-               /*
-                * Found an older ucode saved earlier. Replace it with
-                * this newer one.
-                */
-               mc_saved[i] = (struct microcode_intel *)ucode_ptr;
-               break;
-       }
-
-       /* Newly detected microcode, save it to memory. */
-       if (i >= num_saved && !found)
-               mc_saved[num_saved++] = (struct microcode_intel *)ucode_ptr;
-
-       return num_saved;
-}
-
-/*
- * Get microcode matching with BSP's model. Only CPUs with the same model as
- * BSP can stay in the platform.
- */
-static enum ucode_state __init
-get_matching_model_microcode(int cpu, unsigned long start,
-                            void *data, size_t size,
-                            struct mc_saved_data *mc_saved_data,
-                            unsigned long *mc_saved_in_initrd,
-                            struct ucode_cpu_info *uci)
-{
-       u8 *ucode_ptr = data;
-       unsigned int leftover = size;
-       enum ucode_state state = UCODE_OK;
-       unsigned int mc_size;
-       struct microcode_header_intel *mc_header;
-       struct microcode_intel *mc_saved_tmp[MAX_UCODE_COUNT];
-       unsigned int mc_saved_count = mc_saved_data->mc_saved_count;
-       int i;
-
-       while (leftover && mc_saved_count < ARRAY_SIZE(mc_saved_tmp)) {
-
-               if (leftover < sizeof(mc_header))
-                       break;
-
-               mc_header = (struct microcode_header_intel *)ucode_ptr;
-
-               mc_size = get_totalsize(mc_header);
-               if (!mc_size || mc_size > leftover ||
-                       microcode_sanity_check(ucode_ptr, 0) < 0)
-                       break;
-
-               leftover -= mc_size;
-
-               /*
-                * Since APs with same family and model as the BSP may boot in
-                * the platform, we need to find and save microcode patches
-                * with the same family and model as the BSP.
-                */
-               if (matching_model_microcode(mc_header, uci->cpu_sig.sig) !=
-                        UCODE_OK) {
-                       ucode_ptr += mc_size;
-                       continue;
-               }
-
-               mc_saved_count = _save_mc(mc_saved_tmp, ucode_ptr, mc_saved_count);
-
-               ucode_ptr += mc_size;
-       }
-
-       if (leftover) {
-               state = UCODE_ERROR;
-               goto out;
-       }
-
-       if (mc_saved_count == 0) {
-               state = UCODE_NFOUND;
-               goto out;
-       }
-
-       for (i = 0; i < mc_saved_count; i++)
-               mc_saved_in_initrd[i] = (unsigned long)mc_saved_tmp[i] - start;
-
-       mc_saved_data->mc_saved_count = mc_saved_count;
-out:
-       return state;
-}
-
-static int collect_cpu_info_early(struct ucode_cpu_info *uci)
-{
-       unsigned int val[2];
-       unsigned int family, model;
-       struct cpu_signature csig;
-       unsigned int eax, ebx, ecx, edx;
-
-       csig.sig = 0;
-       csig.pf = 0;
-       csig.rev = 0;
-
-       memset(uci, 0, sizeof(*uci));
-
-       eax = 0x00000001;
-       ecx = 0;
-       native_cpuid(&eax, &ebx, &ecx, &edx);
-       csig.sig = eax;
-
-       family = __x86_family(csig.sig);
-       model  = x86_model(csig.sig);
-
-       if ((model >= 5) || (family > 6)) {
-               /* get processor flags from MSR 0x17 */
-               native_rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
-               csig.pf = 1 << ((val[1] >> 18) & 7);
-       }
-       native_wrmsr(MSR_IA32_UCODE_REV, 0, 0);
-
-       /* As documented in the SDM: Do a CPUID 1 here */
-       sync_core();
-
-       /* get the current revision from MSR 0x8B */
-       native_rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);
-
-       csig.rev = val[1];
-
-       uci->cpu_sig = csig;
-       uci->valid = 1;
-
-       return 0;
-}
-
-#ifdef DEBUG
-static void show_saved_mc(void)
-{
-       int i, j;
-       unsigned int sig, pf, rev, total_size, data_size, date;
-       struct ucode_cpu_info uci;
-
-       if (mc_saved_data.mc_saved_count == 0) {
-               pr_debug("no microcode data saved.\n");
-               return;
-       }
-       pr_debug("Total microcode saved: %d\n", mc_saved_data.mc_saved_count);
-
-       collect_cpu_info_early(&uci);
-
-       sig = uci.cpu_sig.sig;
-       pf = uci.cpu_sig.pf;
-       rev = uci.cpu_sig.rev;
-       pr_debug("CPU: sig=0x%x, pf=0x%x, rev=0x%x\n", sig, pf, rev);
-
-       for (i = 0; i < mc_saved_data.mc_saved_count; i++) {
-               struct microcode_header_intel *mc_saved_header;
-               struct extended_sigtable *ext_header;
-               int ext_sigcount;
-               struct extended_signature *ext_sig;
-
-               mc_saved_header = (struct microcode_header_intel *)
-                                 mc_saved_data.mc_saved[i];
-               sig = mc_saved_header->sig;
-               pf = mc_saved_header->pf;
-               rev = mc_saved_header->rev;
-               total_size = get_totalsize(mc_saved_header);
-               data_size = get_datasize(mc_saved_header);
-               date = mc_saved_header->date;
-
-               pr_debug("mc_saved[%d]: sig=0x%x, pf=0x%x, rev=0x%x, toal size=0x%x, date = %04x-%02x-%02x\n",
-                        i, sig, pf, rev, total_size,
-                        date & 0xffff,
-                        date >> 24,
-                        (date >> 16) & 0xff);
-
-               /* Look for ext. headers: */
-               if (total_size <= data_size + MC_HEADER_SIZE)
-                       continue;
-
-               ext_header = (void *) mc_saved_header + data_size + MC_HEADER_SIZE;
-               ext_sigcount = ext_header->count;
-               ext_sig = (void *)ext_header + EXT_HEADER_SIZE;
-
-               for (j = 0; j < ext_sigcount; j++) {
-                       sig = ext_sig->sig;
-                       pf = ext_sig->pf;
-
-                       pr_debug("\tExtended[%d]: sig=0x%x, pf=0x%x\n",
-                                j, sig, pf);
-
-                       ext_sig++;
-               }
-
-       }
-}
-#else
-static inline void show_saved_mc(void)
-{
-}
-#endif
-
-#if defined(CONFIG_MICROCODE_INTEL_EARLY) && defined(CONFIG_HOTPLUG_CPU)
-static DEFINE_MUTEX(x86_cpu_microcode_mutex);
-/*
- * Save this mc into mc_saved_data. So it will be loaded early when a CPU is
- * hot added or resumes.
- *
- * Please make sure this mc should be a valid microcode patch before calling
- * this function.
- */
-int save_mc_for_early(u8 *mc)
-{
-       struct microcode_intel *mc_saved_tmp[MAX_UCODE_COUNT];
-       unsigned int mc_saved_count_init;
-       unsigned int mc_saved_count;
-       struct microcode_intel **mc_saved;
-       int ret = 0;
-       int i;
-
-       /*
-        * Hold hotplug lock so mc_saved_data is not accessed by a CPU in
-        * hotplug.
-        */
-       mutex_lock(&x86_cpu_microcode_mutex);
-
-       mc_saved_count_init = mc_saved_data.mc_saved_count;
-       mc_saved_count = mc_saved_data.mc_saved_count;
-       mc_saved = mc_saved_data.mc_saved;
-
-       if (mc_saved && mc_saved_count)
-               memcpy(mc_saved_tmp, mc_saved,
-                      mc_saved_count * sizeof(struct microcode_intel *));
-       /*
-        * Save the microcode patch mc in mc_save_tmp structure if it's a newer
-        * version.
-        */
-       mc_saved_count = _save_mc(mc_saved_tmp, mc, mc_saved_count);
-
-       /*
-        * Save the mc_save_tmp in global mc_saved_data.
-        */
-       ret = save_microcode(&mc_saved_data, mc_saved_tmp, mc_saved_count);
-       if (ret) {
-               pr_err("Cannot save microcode patch.\n");
-               goto out;
-       }
-
-       show_saved_mc();
-
-       /*
-        * Free old saved microcode data.
-        */
-       if (mc_saved) {
-               for (i = 0; i < mc_saved_count_init; i++)
-                       kfree(mc_saved[i]);
-               kfree(mc_saved);
-       }
-
-out:
-       mutex_unlock(&x86_cpu_microcode_mutex);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(save_mc_for_early);
-#endif
-
-static bool __init load_builtin_intel_microcode(struct cpio_data *cp)
-{
-#ifdef CONFIG_X86_64
-       unsigned int eax = 0x00000001, ebx, ecx = 0, edx;
-       unsigned int family, model, stepping;
-       char name[30];
-
-       native_cpuid(&eax, &ebx, &ecx, &edx);
-
-       family   = __x86_family(eax);
-       model    = x86_model(eax);
-       stepping = eax & 0xf;
-
-       sprintf(name, "intel-ucode/%02x-%02x-%02x", family, model, stepping);
-
-       return get_builtin_firmware(cp, name);
-#else
-       return false;
-#endif
-}
-
-static __initdata char ucode_name[] = "kernel/x86/microcode/GenuineIntel.bin";
-static __init enum ucode_state
-scan_microcode(struct mc_saved_data *mc_saved_data, unsigned long *initrd,
-              unsigned long start, unsigned long size,
-              struct ucode_cpu_info *uci)
-{
-       struct cpio_data cd;
-       long offset = 0;
-#ifdef CONFIG_X86_32
-       char *p = (char *)__pa_nodebug(ucode_name);
-#else
-       char *p = ucode_name;
-#endif
-
-       cd.data = NULL;
-       cd.size = 0;
-
-       cd = find_cpio_data(p, (void *)start, size, &offset);
-       if (!cd.data) {
-               if (!load_builtin_intel_microcode(&cd))
-                       return UCODE_ERROR;
-       }
-
-       return get_matching_model_microcode(0, start, cd.data, cd.size,
-                                           mc_saved_data, initrd, uci);
-}
-
-/*
- * Print ucode update info.
- */
-static void
-print_ucode_info(struct ucode_cpu_info *uci, unsigned int date)
-{
-       int cpu = smp_processor_id();
-
-       pr_info("CPU%d microcode updated early to revision 0x%x, date = %04x-%02x-%02x\n",
-               cpu,
-               uci->cpu_sig.rev,
-               date & 0xffff,
-               date >> 24,
-               (date >> 16) & 0xff);
-}
-
-#ifdef CONFIG_X86_32
-
-static int delay_ucode_info;
-static int current_mc_date;
-
-/*
- * Print early updated ucode info after printk works. This is delayed info dump.
- */
-void show_ucode_info_early(void)
-{
-       struct ucode_cpu_info uci;
-
-       if (delay_ucode_info) {
-               collect_cpu_info_early(&uci);
-               print_ucode_info(&uci, current_mc_date);
-               delay_ucode_info = 0;
-       }
-}
-
-/*
- * At this point, we can not call printk() yet. Keep microcode patch number in
- * mc_saved_data.mc_saved and delay printing microcode info in
- * show_ucode_info_early() until printk() works.
- */
-static void print_ucode(struct ucode_cpu_info *uci)
-{
-       struct microcode_intel *mc_intel;
-       int *delay_ucode_info_p;
-       int *current_mc_date_p;
-
-       mc_intel = uci->mc;
-       if (mc_intel == NULL)
-               return;
-
-       delay_ucode_info_p = (int *)__pa_nodebug(&delay_ucode_info);
-       current_mc_date_p = (int *)__pa_nodebug(&current_mc_date);
-
-       *delay_ucode_info_p = 1;
-       *current_mc_date_p = mc_intel->hdr.date;
-}
-#else
-
-/*
- * Flush global tlb. We only do this in x86_64 where paging has been enabled
- * already and PGE should be enabled as well.
- */
-static inline void flush_tlb_early(void)
-{
-       __native_flush_tlb_global_irq_disabled();
-}
-
-static inline void print_ucode(struct ucode_cpu_info *uci)
-{
-       struct microcode_intel *mc_intel;
-
-       mc_intel = uci->mc;
-       if (mc_intel == NULL)
-               return;
-
-       print_ucode_info(uci, mc_intel->hdr.date);
-}
-#endif
-
-static int apply_microcode_early(struct ucode_cpu_info *uci, bool early)
-{
-       struct microcode_intel *mc_intel;
-       unsigned int val[2];
-
-       mc_intel = uci->mc;
-       if (mc_intel == NULL)
-               return 0;
-
-       /* write microcode via MSR 0x79 */
-       native_wrmsr(MSR_IA32_UCODE_WRITE,
-             (unsigned long) mc_intel->bits,
-             (unsigned long) mc_intel->bits >> 16 >> 16);
-       native_wrmsr(MSR_IA32_UCODE_REV, 0, 0);
-
-       /* As documented in the SDM: Do a CPUID 1 here */
-       sync_core();
-
-       /* get the current revision from MSR 0x8B */
-       native_rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);
-       if (val[1] != mc_intel->hdr.rev)
-               return -1;
-
-#ifdef CONFIG_X86_64
-       /* Flush global tlb. This is precaution. */
-       flush_tlb_early();
-#endif
-       uci->cpu_sig.rev = val[1];
-
-       if (early)
-               print_ucode(uci);
-       else
-               print_ucode_info(uci, mc_intel->hdr.date);
-
-       return 0;
-}
-
-/*
- * This function converts microcode patch offsets previously stored in
- * mc_saved_in_initrd to pointers and stores the pointers in mc_saved_data.
- */
-int __init save_microcode_in_initrd_intel(void)
-{
-       unsigned int count = mc_saved_data.mc_saved_count;
-       struct microcode_intel *mc_saved[MAX_UCODE_COUNT];
-       int ret = 0;
-
-       if (count == 0)
-               return ret;
-
-       copy_initrd_ptrs(mc_saved, mc_saved_in_initrd, initrd_start, count);
-       ret = save_microcode(&mc_saved_data, mc_saved, count);
-       if (ret)
-               pr_err("Cannot save microcode patches from initrd.\n");
-
-       show_saved_mc();
-
-       return ret;
-}
-
-static void __init
-_load_ucode_intel_bsp(struct mc_saved_data *mc_saved_data,
-                     unsigned long *initrd,
-                     unsigned long start, unsigned long size)
-{
-       struct ucode_cpu_info uci;
-       enum ucode_state ret;
-
-       collect_cpu_info_early(&uci);
-
-       ret = scan_microcode(mc_saved_data, initrd, start, size, &uci);
-       if (ret != UCODE_OK)
-               return;
-
-       ret = load_microcode(mc_saved_data, initrd, start, &uci);
-       if (ret != UCODE_OK)
-               return;
-
-       apply_microcode_early(&uci, true);
-}
-
-void __init load_ucode_intel_bsp(void)
-{
-       u64 start, size;
-#ifdef CONFIG_X86_32
-       struct boot_params *p;
-
-       p       = (struct boot_params *)__pa_nodebug(&boot_params);
-       start   = p->hdr.ramdisk_image;
-       size    = p->hdr.ramdisk_size;
-
-       _load_ucode_intel_bsp(
-                       (struct mc_saved_data *)__pa_nodebug(&mc_saved_data),
-                       (unsigned long *)__pa_nodebug(&mc_saved_in_initrd),
-                       start, size);
-#else
-       start   = boot_params.hdr.ramdisk_image + PAGE_OFFSET;
-       size    = boot_params.hdr.ramdisk_size;
-
-       _load_ucode_intel_bsp(&mc_saved_data, mc_saved_in_initrd, start, size);
-#endif
-}
-
-void load_ucode_intel_ap(void)
-{
-       struct mc_saved_data *mc_saved_data_p;
-       struct ucode_cpu_info uci;
-       unsigned long *mc_saved_in_initrd_p;
-       unsigned long initrd_start_addr;
-       enum ucode_state ret;
-#ifdef CONFIG_X86_32
-       unsigned long *initrd_start_p;
-
-       mc_saved_in_initrd_p =
-               (unsigned long *)__pa_nodebug(mc_saved_in_initrd);
-       mc_saved_data_p = (struct mc_saved_data *)__pa_nodebug(&mc_saved_data);
-       initrd_start_p = (unsigned long *)__pa_nodebug(&initrd_start);
-       initrd_start_addr = (unsigned long)__pa_nodebug(*initrd_start_p);
-#else
-       mc_saved_data_p = &mc_saved_data;
-       mc_saved_in_initrd_p = mc_saved_in_initrd;
-       initrd_start_addr = initrd_start;
-#endif
-
-       /*
-        * If there is no valid ucode previously saved in memory, no need to
-        * update ucode on this AP.
-        */
-       if (mc_saved_data_p->mc_saved_count == 0)
-               return;
-
-       collect_cpu_info_early(&uci);
-       ret = load_microcode(mc_saved_data_p, mc_saved_in_initrd_p,
-                            initrd_start_addr, &uci);
-
-       if (ret != UCODE_OK)
-               return;
-
-       apply_microcode_early(&uci, true);
-}
-
-void reload_ucode_intel(void)
-{
-       struct ucode_cpu_info uci;
-       enum ucode_state ret;
-
-       if (!mc_saved_data.mc_saved_count)
-               return;
-
-       collect_cpu_info_early(&uci);
-
-       ret = load_microcode_early(mc_saved_data.mc_saved,
-                                  mc_saved_data.mc_saved_count, &uci);
-       if (ret != UCODE_OK)
-               return;
-
-       apply_microcode_early(&uci, false);
-}
index 1883d252ff7d60ce7707a8108283144de64ce14d..b96896bcbdaf1ac88d4dcfa2a7ce3a98d291ccad 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/firmware.h>
 #include <linux/uaccess.h>
 #include <linux/kernel.h>
-#include <linux/module.h>
 
 #include <asm/microcode_intel.h>
 #include <asm/processor.h>
index 66dd3fe99b82d0e54e1df27255d27e4accc69547..4562cf070c279d5edeb0e18ae94c8bff94166dd8 100644 (file)
@@ -1175,7 +1175,7 @@ static int x86_pmu_add(struct perf_event *event, int flags)
         * skip the schedulability test here, it will be performed
         * at commit time (->commit_txn) as a whole.
         */
-       if (cpuc->group_flag & PERF_EVENT_TXN)
+       if (cpuc->txn_flags & PERF_PMU_TXN_ADD)
                goto done_collect;
 
        ret = x86_pmu.schedule_events(cpuc, n, assign);
@@ -1326,7 +1326,7 @@ static void x86_pmu_del(struct perf_event *event, int flags)
         * XXX assumes any ->del() called during a TXN will only be on
         * an event added during that same TXN.
         */
-       if (cpuc->group_flag & PERF_EVENT_TXN)
+       if (cpuc->txn_flags & PERF_PMU_TXN_ADD)
                return;
 
        /*
@@ -1748,11 +1748,22 @@ static inline void x86_pmu_read(struct perf_event *event)
  * Start group events scheduling transaction
  * Set the flag to make pmu::enable() not perform the
  * schedulability test, it will be performed at commit time
+ *
+ * We only support PERF_PMU_TXN_ADD transactions. Save the
+ * transaction flags but otherwise ignore non-PERF_PMU_TXN_ADD
+ * transactions.
  */
-static void x86_pmu_start_txn(struct pmu *pmu)
+static void x86_pmu_start_txn(struct pmu *pmu, unsigned int txn_flags)
 {
+       struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+       WARN_ON_ONCE(cpuc->txn_flags);          /* txn already in flight */
+
+       cpuc->txn_flags = txn_flags;
+       if (txn_flags & ~PERF_PMU_TXN_ADD)
+               return;
+
        perf_pmu_disable(pmu);
-       __this_cpu_or(cpu_hw_events.group_flag, PERF_EVENT_TXN);
        __this_cpu_write(cpu_hw_events.n_txn, 0);
 }
 
@@ -1763,7 +1774,16 @@ static void x86_pmu_start_txn(struct pmu *pmu)
  */
 static void x86_pmu_cancel_txn(struct pmu *pmu)
 {
-       __this_cpu_and(cpu_hw_events.group_flag, ~PERF_EVENT_TXN);
+       unsigned int txn_flags;
+       struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+       WARN_ON_ONCE(!cpuc->txn_flags); /* no txn in flight */
+
+       txn_flags = cpuc->txn_flags;
+       cpuc->txn_flags = 0;
+       if (txn_flags & ~PERF_PMU_TXN_ADD)
+               return;
+
        /*
         * Truncate collected array by the number of events added in this
         * transaction. See x86_pmu_add() and x86_pmu_*_txn().
@@ -1786,6 +1806,13 @@ static int x86_pmu_commit_txn(struct pmu *pmu)
        int assign[X86_PMC_IDX_MAX];
        int n, ret;
 
+       WARN_ON_ONCE(!cpuc->txn_flags); /* no txn in flight */
+
+       if (cpuc->txn_flags & ~PERF_PMU_TXN_ADD) {
+               cpuc->txn_flags = 0;
+               return 0;
+       }
+
        n = cpuc->n_events;
 
        if (!x86_pmu_initialized())
@@ -1801,7 +1828,7 @@ static int x86_pmu_commit_txn(struct pmu *pmu)
         */
        memcpy(cpuc->assign, assign, n*sizeof(int));
 
-       cpuc->group_flag &= ~PERF_EVENT_TXN;
+       cpuc->txn_flags = 0;
        perf_pmu_enable(pmu);
        return 0;
 }
index 165be83a7fa48a105fe67c67791e19d0b92c22ad..499f533dd3ccbd22bb84423649e031f2759080ca 100644 (file)
@@ -196,7 +196,7 @@ struct cpu_hw_events {
 
        int                     n_excl; /* the number of exclusive events */
 
-       unsigned int            group_flag;
+       unsigned int            txn_flags;
        int                     is_fake;
 
        /*
index d1c0f254afbeefe61fcfaeeb7625664d7d352918..2cad71d1b14cfb36df83674dac992f51c37db63a 100644 (file)
@@ -495,6 +495,19 @@ static int bts_event_init(struct perf_event *event)
        if (x86_add_exclusive(x86_lbr_exclusive_bts))
                return -EBUSY;
 
+       /*
+        * BTS leaks kernel addresses even when CPL0 tracing is
+        * disabled, so disallow intel_bts driver for unprivileged
+        * users on paranoid systems since it provides trace data
+        * to the user in a zero-copy fashion.
+        *
+        * Note that the default paranoia setting permits unprivileged
+        * users to profile the kernel.
+        */
+       if (event->attr.exclude_kernel && perf_paranoid_kernel() &&
+           !capable(CAP_SYS_ADMIN))
+               return -EACCES;
+
        ret = x86_reserve_hardware();
        if (ret) {
                x86_del_exclusive(x86_lbr_exclusive_bts);
diff --git a/arch/x86/kernel/cpu/perf_event_intel_cstate.c b/arch/x86/kernel/cpu/perf_event_intel_cstate.c
new file mode 100644 (file)
index 0000000..75a38b5
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * perf_event_intel_cstate.c: support cstate residency counters
+ *
+ * Copyright (C) 2015, Intel Corp.
+ * Author: Kan Liang (kan.liang@intel.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ */
+
+/*
+ * This file export cstate related free running (read-only) counters
+ * for perf. These counters may be use simultaneously by other tools,
+ * such as turbostat. However, it still make sense to implement them
+ * in perf. Because we can conveniently collect them together with
+ * other events, and allow to use them from tools without special MSR
+ * access code.
+ *
+ * The events only support system-wide mode counting. There is no
+ * sampling support because it is not supported by the hardware.
+ *
+ * According to counters' scope and category, two PMUs are registered
+ * with the perf_event core subsystem.
+ *  - 'cstate_core': The counter is available for each physical core.
+ *    The counters include CORE_C*_RESIDENCY.
+ *  - 'cstate_pkg': The counter is available for each physical package.
+ *    The counters include PKG_C*_RESIDENCY.
+ *
+ * All of these counters are specified in the Intel® 64 and IA-32
+ * Architectures Software Developer.s Manual Vol3b.
+ *
+ * Model specific counters:
+ *     MSR_CORE_C1_RES: CORE C1 Residency Counter
+ *                      perf code: 0x00
+ *                      Available model: SLM,AMT
+ *                      Scope: Core (each processor core has a MSR)
+ *     MSR_CORE_C3_RESIDENCY: CORE C3 Residency Counter
+ *                            perf code: 0x01
+ *                            Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL
+ *                            Scope: Core
+ *     MSR_CORE_C6_RESIDENCY: CORE C6 Residency Counter
+ *                            perf code: 0x02
+ *                            Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW,SKL
+ *                            Scope: Core
+ *     MSR_CORE_C7_RESIDENCY: CORE C7 Residency Counter
+ *                            perf code: 0x03
+ *                            Available model: SNB,IVB,HSW,BDW,SKL
+ *                            Scope: Core
+ *     MSR_PKG_C2_RESIDENCY:  Package C2 Residency Counter.
+ *                            perf code: 0x00
+ *                            Available model: SNB,IVB,HSW,BDW,SKL
+ *                            Scope: Package (physical package)
+ *     MSR_PKG_C3_RESIDENCY:  Package C3 Residency Counter.
+ *                            perf code: 0x01
+ *                            Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL
+ *                            Scope: Package (physical package)
+ *     MSR_PKG_C6_RESIDENCY:  Package C6 Residency Counter.
+ *                            perf code: 0x02
+ *                            Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW,SKL
+ *                            Scope: Package (physical package)
+ *     MSR_PKG_C7_RESIDENCY:  Package C7 Residency Counter.
+ *                            perf code: 0x03
+ *                            Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL
+ *                            Scope: Package (physical package)
+ *     MSR_PKG_C8_RESIDENCY:  Package C8 Residency Counter.
+ *                            perf code: 0x04
+ *                            Available model: HSW ULT only
+ *                            Scope: Package (physical package)
+ *     MSR_PKG_C9_RESIDENCY:  Package C9 Residency Counter.
+ *                            perf code: 0x05
+ *                            Available model: HSW ULT only
+ *                            Scope: Package (physical package)
+ *     MSR_PKG_C10_RESIDENCY: Package C10 Residency Counter.
+ *                            perf code: 0x06
+ *                            Available model: HSW ULT only
+ *                            Scope: Package (physical package)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/perf_event.h>
+#include <asm/cpu_device_id.h>
+#include "perf_event.h"
+
+#define DEFINE_CSTATE_FORMAT_ATTR(_var, _name, _format)                \
+static ssize_t __cstate_##_var##_show(struct kobject *kobj,    \
+                               struct kobj_attribute *attr,    \
+                               char *page)                     \
+{                                                              \
+       BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE);             \
+       return sprintf(page, _format "\n");                     \
+}                                                              \
+static struct kobj_attribute format_attr_##_var =              \
+       __ATTR(_name, 0444, __cstate_##_var##_show, NULL)
+
+static ssize_t cstate_get_attr_cpumask(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *buf);
+
+struct perf_cstate_msr {
+       u64     msr;
+       struct  perf_pmu_events_attr *attr;
+       bool    (*test)(int idx);
+};
+
+
+/* cstate_core PMU */
+
+static struct pmu cstate_core_pmu;
+static bool has_cstate_core;
+
+enum perf_cstate_core_id {
+       /*
+        * cstate_core events
+        */
+       PERF_CSTATE_CORE_C1_RES = 0,
+       PERF_CSTATE_CORE_C3_RES,
+       PERF_CSTATE_CORE_C6_RES,
+       PERF_CSTATE_CORE_C7_RES,
+
+       PERF_CSTATE_CORE_EVENT_MAX,
+};
+
+bool test_core(int idx)
+{
+       if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ||
+           boot_cpu_data.x86 != 6)
+               return false;
+
+       switch (boot_cpu_data.x86_model) {
+       case 30: /* 45nm Nehalem    */
+       case 26: /* 45nm Nehalem-EP */
+       case 46: /* 45nm Nehalem-EX */
+
+       case 37: /* 32nm Westmere    */
+       case 44: /* 32nm Westmere-EP */
+       case 47: /* 32nm Westmere-EX */
+               if (idx == PERF_CSTATE_CORE_C3_RES ||
+                   idx == PERF_CSTATE_CORE_C6_RES)
+                       return true;
+               break;
+       case 42: /* 32nm SandyBridge         */
+       case 45: /* 32nm SandyBridge-E/EN/EP */
+
+       case 58: /* 22nm IvyBridge       */
+       case 62: /* 22nm IvyBridge-EP/EX */
+
+       case 60: /* 22nm Haswell Core */
+       case 63: /* 22nm Haswell Server */
+       case 69: /* 22nm Haswell ULT */
+       case 70: /* 22nm Haswell + GT3e (Intel Iris Pro graphics) */
+
+       case 61: /* 14nm Broadwell Core-M */
+       case 86: /* 14nm Broadwell Xeon D */
+       case 71: /* 14nm Broadwell + GT3e (Intel Iris Pro graphics) */
+       case 79: /* 14nm Broadwell Server */
+
+       case 78: /* 14nm Skylake Mobile */
+       case 94: /* 14nm Skylake Desktop */
+               if (idx == PERF_CSTATE_CORE_C3_RES ||
+                   idx == PERF_CSTATE_CORE_C6_RES ||
+                   idx == PERF_CSTATE_CORE_C7_RES)
+                       return true;
+               break;
+       case 55: /* 22nm Atom "Silvermont"                */
+       case 77: /* 22nm Atom "Silvermont Avoton/Rangely" */
+       case 76: /* 14nm Atom "Airmont"                   */
+               if (idx == PERF_CSTATE_CORE_C1_RES ||
+                   idx == PERF_CSTATE_CORE_C6_RES)
+                       return true;
+               break;
+       }
+
+       return false;
+}
+
+PMU_EVENT_ATTR_STRING(c1-residency, evattr_cstate_core_c1, "event=0x00");
+PMU_EVENT_ATTR_STRING(c3-residency, evattr_cstate_core_c3, "event=0x01");
+PMU_EVENT_ATTR_STRING(c6-residency, evattr_cstate_core_c6, "event=0x02");
+PMU_EVENT_ATTR_STRING(c7-residency, evattr_cstate_core_c7, "event=0x03");
+
+static struct perf_cstate_msr core_msr[] = {
+       [PERF_CSTATE_CORE_C1_RES] = { MSR_CORE_C1_RES,          &evattr_cstate_core_c1, test_core, },
+       [PERF_CSTATE_CORE_C3_RES] = { MSR_CORE_C3_RESIDENCY,    &evattr_cstate_core_c3, test_core, },
+       [PERF_CSTATE_CORE_C6_RES] = { MSR_CORE_C6_RESIDENCY,    &evattr_cstate_core_c6, test_core, },
+       [PERF_CSTATE_CORE_C7_RES] = { MSR_CORE_C7_RESIDENCY,    &evattr_cstate_core_c7, test_core, },
+};
+
+static struct attribute *core_events_attrs[PERF_CSTATE_CORE_EVENT_MAX + 1] = {
+       NULL,
+};
+
+static struct attribute_group core_events_attr_group = {
+       .name = "events",
+       .attrs = core_events_attrs,
+};
+
+DEFINE_CSTATE_FORMAT_ATTR(core_event, event, "config:0-63");
+static struct attribute *core_format_attrs[] = {
+       &format_attr_core_event.attr,
+       NULL,
+};
+
+static struct attribute_group core_format_attr_group = {
+       .name = "format",
+       .attrs = core_format_attrs,
+};
+
+static cpumask_t cstate_core_cpu_mask;
+static DEVICE_ATTR(cpumask, S_IRUGO, cstate_get_attr_cpumask, NULL);
+
+static struct attribute *cstate_cpumask_attrs[] = {
+       &dev_attr_cpumask.attr,
+       NULL,
+};
+
+static struct attribute_group cpumask_attr_group = {
+       .attrs = cstate_cpumask_attrs,
+};
+
+static const struct attribute_group *core_attr_groups[] = {
+       &core_events_attr_group,
+       &core_format_attr_group,
+       &cpumask_attr_group,
+       NULL,
+};
+
+/* cstate_core PMU end */
+
+
+/* cstate_pkg PMU */
+
+static struct pmu cstate_pkg_pmu;
+static bool has_cstate_pkg;
+
+enum perf_cstate_pkg_id {
+       /*
+        * cstate_pkg events
+        */
+       PERF_CSTATE_PKG_C2_RES = 0,
+       PERF_CSTATE_PKG_C3_RES,
+       PERF_CSTATE_PKG_C6_RES,
+       PERF_CSTATE_PKG_C7_RES,
+       PERF_CSTATE_PKG_C8_RES,
+       PERF_CSTATE_PKG_C9_RES,
+       PERF_CSTATE_PKG_C10_RES,
+
+       PERF_CSTATE_PKG_EVENT_MAX,
+};
+
+bool test_pkg(int idx)
+{
+       if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ||
+           boot_cpu_data.x86 != 6)
+               return false;
+
+       switch (boot_cpu_data.x86_model) {
+       case 30: /* 45nm Nehalem    */
+       case 26: /* 45nm Nehalem-EP */
+       case 46: /* 45nm Nehalem-EX */
+
+       case 37: /* 32nm Westmere    */
+       case 44: /* 32nm Westmere-EP */
+       case 47: /* 32nm Westmere-EX */
+               if (idx == PERF_CSTATE_CORE_C3_RES ||
+                   idx == PERF_CSTATE_CORE_C6_RES ||
+                   idx == PERF_CSTATE_CORE_C7_RES)
+                       return true;
+               break;
+       case 42: /* 32nm SandyBridge         */
+       case 45: /* 32nm SandyBridge-E/EN/EP */
+
+       case 58: /* 22nm IvyBridge       */
+       case 62: /* 22nm IvyBridge-EP/EX */
+
+       case 60: /* 22nm Haswell Core */
+       case 63: /* 22nm Haswell Server */
+       case 70: /* 22nm Haswell + GT3e (Intel Iris Pro graphics) */
+
+       case 61: /* 14nm Broadwell Core-M */
+       case 86: /* 14nm Broadwell Xeon D */
+       case 71: /* 14nm Broadwell + GT3e (Intel Iris Pro graphics) */
+       case 79: /* 14nm Broadwell Server */
+
+       case 78: /* 14nm Skylake Mobile */
+       case 94: /* 14nm Skylake Desktop */
+               if (idx == PERF_CSTATE_PKG_C2_RES ||
+                   idx == PERF_CSTATE_PKG_C3_RES ||
+                   idx == PERF_CSTATE_PKG_C6_RES ||
+                   idx == PERF_CSTATE_PKG_C7_RES)
+                       return true;
+               break;
+       case 55: /* 22nm Atom "Silvermont"                */
+       case 77: /* 22nm Atom "Silvermont Avoton/Rangely" */
+       case 76: /* 14nm Atom "Airmont"                   */
+               if (idx == PERF_CSTATE_CORE_C6_RES)
+                       return true;
+               break;
+       case 69: /* 22nm Haswell ULT */
+               if (idx == PERF_CSTATE_PKG_C2_RES ||
+                   idx == PERF_CSTATE_PKG_C3_RES ||
+                   idx == PERF_CSTATE_PKG_C6_RES ||
+                   idx == PERF_CSTATE_PKG_C7_RES ||
+                   idx == PERF_CSTATE_PKG_C8_RES ||
+                   idx == PERF_CSTATE_PKG_C9_RES ||
+                   idx == PERF_CSTATE_PKG_C10_RES)
+                       return true;
+               break;
+       }
+
+       return false;
+}
+
+PMU_EVENT_ATTR_STRING(c2-residency, evattr_cstate_pkg_c2, "event=0x00");
+PMU_EVENT_ATTR_STRING(c3-residency, evattr_cstate_pkg_c3, "event=0x01");
+PMU_EVENT_ATTR_STRING(c6-residency, evattr_cstate_pkg_c6, "event=0x02");
+PMU_EVENT_ATTR_STRING(c7-residency, evattr_cstate_pkg_c7, "event=0x03");
+PMU_EVENT_ATTR_STRING(c8-residency, evattr_cstate_pkg_c8, "event=0x04");
+PMU_EVENT_ATTR_STRING(c9-residency, evattr_cstate_pkg_c9, "event=0x05");
+PMU_EVENT_ATTR_STRING(c10-residency, evattr_cstate_pkg_c10, "event=0x06");
+
+static struct perf_cstate_msr pkg_msr[] = {
+       [PERF_CSTATE_PKG_C2_RES] = { MSR_PKG_C2_RESIDENCY,      &evattr_cstate_pkg_c2,  test_pkg, },
+       [PERF_CSTATE_PKG_C3_RES] = { MSR_PKG_C3_RESIDENCY,      &evattr_cstate_pkg_c3,  test_pkg, },
+       [PERF_CSTATE_PKG_C6_RES] = { MSR_PKG_C6_RESIDENCY,      &evattr_cstate_pkg_c6,  test_pkg, },
+       [PERF_CSTATE_PKG_C7_RES] = { MSR_PKG_C7_RESIDENCY,      &evattr_cstate_pkg_c7,  test_pkg, },
+       [PERF_CSTATE_PKG_C8_RES] = { MSR_PKG_C8_RESIDENCY,      &evattr_cstate_pkg_c8,  test_pkg, },
+       [PERF_CSTATE_PKG_C9_RES] = { MSR_PKG_C9_RESIDENCY,      &evattr_cstate_pkg_c9,  test_pkg, },
+       [PERF_CSTATE_PKG_C10_RES] = { MSR_PKG_C10_RESIDENCY,    &evattr_cstate_pkg_c10, test_pkg, },
+};
+
+static struct attribute *pkg_events_attrs[PERF_CSTATE_PKG_EVENT_MAX + 1] = {
+       NULL,
+};
+
+static struct attribute_group pkg_events_attr_group = {
+       .name = "events",
+       .attrs = pkg_events_attrs,
+};
+
+DEFINE_CSTATE_FORMAT_ATTR(pkg_event, event, "config:0-63");
+static struct attribute *pkg_format_attrs[] = {
+       &format_attr_pkg_event.attr,
+       NULL,
+};
+static struct attribute_group pkg_format_attr_group = {
+       .name = "format",
+       .attrs = pkg_format_attrs,
+};
+
+static cpumask_t cstate_pkg_cpu_mask;
+
+static const struct attribute_group *pkg_attr_groups[] = {
+       &pkg_events_attr_group,
+       &pkg_format_attr_group,
+       &cpumask_attr_group,
+       NULL,
+};
+
+/* cstate_pkg PMU end*/
+
+static ssize_t cstate_get_attr_cpumask(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct pmu *pmu = dev_get_drvdata(dev);
+
+       if (pmu == &cstate_core_pmu)
+               return cpumap_print_to_pagebuf(true, buf, &cstate_core_cpu_mask);
+       else if (pmu == &cstate_pkg_pmu)
+               return cpumap_print_to_pagebuf(true, buf, &cstate_pkg_cpu_mask);
+       else
+               return 0;
+}
+
+static int cstate_pmu_event_init(struct perf_event *event)
+{
+       u64 cfg = event->attr.config;
+       int ret = 0;
+
+       if (event->attr.type != event->pmu->type)
+               return -ENOENT;
+
+       /* unsupported modes and filters */
+       if (event->attr.exclude_user   ||
+           event->attr.exclude_kernel ||
+           event->attr.exclude_hv     ||
+           event->attr.exclude_idle   ||
+           event->attr.exclude_host   ||
+           event->attr.exclude_guest  ||
+           event->attr.sample_period) /* no sampling */
+               return -EINVAL;
+
+       if (event->pmu == &cstate_core_pmu) {
+               if (cfg >= PERF_CSTATE_CORE_EVENT_MAX)
+                       return -EINVAL;
+               if (!core_msr[cfg].attr)
+                       return -EINVAL;
+               event->hw.event_base = core_msr[cfg].msr;
+       } else if (event->pmu == &cstate_pkg_pmu) {
+               if (cfg >= PERF_CSTATE_PKG_EVENT_MAX)
+                       return -EINVAL;
+               if (!pkg_msr[cfg].attr)
+                       return -EINVAL;
+               event->hw.event_base = pkg_msr[cfg].msr;
+       } else
+               return -ENOENT;
+
+       /* must be done before validate_group */
+       event->hw.config = cfg;
+       event->hw.idx = -1;
+
+       return ret;
+}
+
+static inline u64 cstate_pmu_read_counter(struct perf_event *event)
+{
+       u64 val;
+
+       rdmsrl(event->hw.event_base, val);
+       return val;
+}
+
+static void cstate_pmu_event_update(struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       u64 prev_raw_count, new_raw_count;
+
+again:
+       prev_raw_count = local64_read(&hwc->prev_count);
+       new_raw_count = cstate_pmu_read_counter(event);
+
+       if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+                           new_raw_count) != prev_raw_count)
+               goto again;
+
+       local64_add(new_raw_count - prev_raw_count, &event->count);
+}
+
+static void cstate_pmu_event_start(struct perf_event *event, int mode)
+{
+       local64_set(&event->hw.prev_count, cstate_pmu_read_counter(event));
+}
+
+static void cstate_pmu_event_stop(struct perf_event *event, int mode)
+{
+       cstate_pmu_event_update(event);
+}
+
+static void cstate_pmu_event_del(struct perf_event *event, int mode)
+{
+       cstate_pmu_event_stop(event, PERF_EF_UPDATE);
+}
+
+static int cstate_pmu_event_add(struct perf_event *event, int mode)
+{
+       if (mode & PERF_EF_START)
+               cstate_pmu_event_start(event, mode);
+
+       return 0;
+}
+
+static void cstate_cpu_exit(int cpu)
+{
+       int i, id, target;
+
+       /* cpu exit for cstate core */
+       if (has_cstate_core) {
+               id = topology_core_id(cpu);
+               target = -1;
+
+               for_each_online_cpu(i) {
+                       if (i == cpu)
+                               continue;
+                       if (id == topology_core_id(i)) {
+                               target = i;
+                               break;
+                       }
+               }
+               if (cpumask_test_and_clear_cpu(cpu, &cstate_core_cpu_mask) && target >= 0)
+                       cpumask_set_cpu(target, &cstate_core_cpu_mask);
+               WARN_ON(cpumask_empty(&cstate_core_cpu_mask));
+               if (target >= 0)
+                       perf_pmu_migrate_context(&cstate_core_pmu, cpu, target);
+       }
+
+       /* cpu exit for cstate pkg */
+       if (has_cstate_pkg) {
+               id = topology_physical_package_id(cpu);
+               target = -1;
+
+               for_each_online_cpu(i) {
+                       if (i == cpu)
+                               continue;
+                       if (id == topology_physical_package_id(i)) {
+                               target = i;
+                               break;
+                       }
+               }
+               if (cpumask_test_and_clear_cpu(cpu, &cstate_pkg_cpu_mask) && target >= 0)
+                       cpumask_set_cpu(target, &cstate_pkg_cpu_mask);
+               WARN_ON(cpumask_empty(&cstate_pkg_cpu_mask));
+               if (target >= 0)
+                       perf_pmu_migrate_context(&cstate_pkg_pmu, cpu, target);
+       }
+}
+
+static void cstate_cpu_init(int cpu)
+{
+       int i, id;
+
+       /* cpu init for cstate core */
+       if (has_cstate_core) {
+               id = topology_core_id(cpu);
+               for_each_cpu(i, &cstate_core_cpu_mask) {
+                       if (id == topology_core_id(i))
+                               break;
+               }
+               if (i >= nr_cpu_ids)
+                       cpumask_set_cpu(cpu, &cstate_core_cpu_mask);
+       }
+
+       /* cpu init for cstate pkg */
+       if (has_cstate_pkg) {
+               id = topology_physical_package_id(cpu);
+               for_each_cpu(i, &cstate_pkg_cpu_mask) {
+                       if (id == topology_physical_package_id(i))
+                               break;
+               }
+               if (i >= nr_cpu_ids)
+                       cpumask_set_cpu(cpu, &cstate_pkg_cpu_mask);
+       }
+}
+
+static int cstate_cpu_notifier(struct notifier_block *self,
+                                 unsigned long action, void *hcpu)
+{
+       unsigned int cpu = (long)hcpu;
+
+       switch (action & ~CPU_TASKS_FROZEN) {
+       case CPU_UP_PREPARE:
+               break;
+       case CPU_STARTING:
+               cstate_cpu_init(cpu);
+               break;
+       case CPU_UP_CANCELED:
+       case CPU_DYING:
+               break;
+       case CPU_ONLINE:
+       case CPU_DEAD:
+               break;
+       case CPU_DOWN_PREPARE:
+               cstate_cpu_exit(cpu);
+               break;
+       default:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+/*
+ * Probe the cstate events and insert the available one into sysfs attrs
+ * Return false if there is no available events.
+ */
+static bool cstate_probe_msr(struct perf_cstate_msr *msr,
+                            struct attribute   **events_attrs,
+                            int max_event_nr)
+{
+       int i, j = 0;
+       u64 val;
+
+       /* Probe the cstate events. */
+       for (i = 0; i < max_event_nr; i++) {
+               if (!msr[i].test(i) || rdmsrl_safe(msr[i].msr, &val))
+                       msr[i].attr = NULL;
+       }
+
+       /* List remaining events in the sysfs attrs. */
+       for (i = 0; i < max_event_nr; i++) {
+               if (msr[i].attr)
+                       events_attrs[j++] = &msr[i].attr->attr.attr;
+       }
+       events_attrs[j] = NULL;
+
+       return (j > 0) ? true : false;
+}
+
+static int __init cstate_init(void)
+{
+       /* SLM has different MSR for PKG C6 */
+       switch (boot_cpu_data.x86_model) {
+       case 55:
+       case 76:
+       case 77:
+               pkg_msr[PERF_CSTATE_PKG_C6_RES].msr = MSR_PKG_C7_RESIDENCY;
+       }
+
+       if (cstate_probe_msr(core_msr, core_events_attrs, PERF_CSTATE_CORE_EVENT_MAX))
+               has_cstate_core = true;
+
+       if (cstate_probe_msr(pkg_msr, pkg_events_attrs, PERF_CSTATE_PKG_EVENT_MAX))
+               has_cstate_pkg = true;
+
+       return (has_cstate_core || has_cstate_pkg) ? 0 : -ENODEV;
+}
+
+static void __init cstate_cpumask_init(void)
+{
+       int cpu;
+
+       cpu_notifier_register_begin();
+
+       for_each_online_cpu(cpu)
+               cstate_cpu_init(cpu);
+
+       __perf_cpu_notifier(cstate_cpu_notifier);
+
+       cpu_notifier_register_done();
+}
+
+static struct pmu cstate_core_pmu = {
+       .attr_groups    = core_attr_groups,
+       .name           = "cstate_core",
+       .task_ctx_nr    = perf_invalid_context,
+       .event_init     = cstate_pmu_event_init,
+       .add            = cstate_pmu_event_add, /* must have */
+       .del            = cstate_pmu_event_del, /* must have */
+       .start          = cstate_pmu_event_start,
+       .stop           = cstate_pmu_event_stop,
+       .read           = cstate_pmu_event_update,
+       .capabilities   = PERF_PMU_CAP_NO_INTERRUPT,
+};
+
+static struct pmu cstate_pkg_pmu = {
+       .attr_groups    = pkg_attr_groups,
+       .name           = "cstate_pkg",
+       .task_ctx_nr    = perf_invalid_context,
+       .event_init     = cstate_pmu_event_init,
+       .add            = cstate_pmu_event_add, /* must have */
+       .del            = cstate_pmu_event_del, /* must have */
+       .start          = cstate_pmu_event_start,
+       .stop           = cstate_pmu_event_stop,
+       .read           = cstate_pmu_event_update,
+       .capabilities   = PERF_PMU_CAP_NO_INTERRUPT,
+};
+
+static void __init cstate_pmus_register(void)
+{
+       int err;
+
+       if (has_cstate_core) {
+               err = perf_pmu_register(&cstate_core_pmu, cstate_core_pmu.name, -1);
+               if (WARN_ON(err))
+                       pr_info("Failed to register PMU %s error %d\n",
+                               cstate_core_pmu.name, err);
+       }
+
+       if (has_cstate_pkg) {
+               err = perf_pmu_register(&cstate_pkg_pmu, cstate_pkg_pmu.name, -1);
+               if (WARN_ON(err))
+                       pr_info("Failed to register PMU %s error %d\n",
+                               cstate_pkg_pmu.name, err);
+       }
+}
+
+static int __init cstate_pmu_init(void)
+{
+       int err;
+
+       if (cpu_has_hypervisor)
+               return -ENODEV;
+
+       err = cstate_init();
+       if (err)
+               return err;
+
+       cstate_cpumask_init();
+
+       cstate_pmus_register();
+
+       return 0;
+}
+
+device_initcall(cstate_pmu_init);
index 84f236ab96b03459907662de8842958a3645d011..5db1c7755548e2ad472b2fbe5541125a45d90376 100644 (file)
@@ -510,10 +510,11 @@ int intel_pmu_drain_bts_buffer(void)
                u64     flags;
        };
        struct perf_event *event = cpuc->events[INTEL_PMC_IDX_FIXED_BTS];
-       struct bts_record *at, *top;
+       struct bts_record *at, *base, *top;
        struct perf_output_handle handle;
        struct perf_event_header header;
        struct perf_sample_data data;
+       unsigned long skip = 0;
        struct pt_regs regs;
 
        if (!event)
@@ -522,10 +523,10 @@ int intel_pmu_drain_bts_buffer(void)
        if (!x86_pmu.bts_active)
                return 0;
 
-       at  = (struct bts_record *)(unsigned long)ds->bts_buffer_base;
-       top = (struct bts_record *)(unsigned long)ds->bts_index;
+       base = (struct bts_record *)(unsigned long)ds->bts_buffer_base;
+       top  = (struct bts_record *)(unsigned long)ds->bts_index;
 
-       if (top <= at)
+       if (top <= base)
                return 0;
 
        memset(&regs, 0, sizeof(regs));
@@ -534,6 +535,27 @@ int intel_pmu_drain_bts_buffer(void)
 
        perf_sample_data_init(&data, 0, event->hw.last_period);
 
+       /*
+        * BTS leaks kernel addresses in branches across the cpl boundary,
+        * such as traps or system calls, so unless the user is asking for
+        * kernel tracing (and right now it's not possible), we'd need to
+        * filter them out. But first we need to count how many of those we
+        * have in the current batch. This is an extra O(n) pass, however,
+        * it's much faster than the other one especially considering that
+        * n <= 2560 (BTS_BUFFER_SIZE / BTS_RECORD_SIZE * 15/16; see the
+        * alloc_bts_buffer()).
+        */
+       for (at = base; at < top; at++) {
+               /*
+                * Note that right now *this* BTS code only works if
+                * attr::exclude_kernel is set, but let's keep this extra
+                * check here in case that changes.
+                */
+               if (event->attr.exclude_kernel &&
+                   (kernel_ip(at->from) || kernel_ip(at->to)))
+                       skip++;
+       }
+
        /*
         * Prepare a generic sample, i.e. fill in the invariant fields.
         * We will overwrite the from and to address before we output
@@ -541,10 +563,16 @@ int intel_pmu_drain_bts_buffer(void)
         */
        perf_prepare_sample(&header, &data, event, &regs);
 
-       if (perf_output_begin(&handle, event, header.size * (top - at)))
+       if (perf_output_begin(&handle, event, header.size *
+                             (top - base - skip)))
                return 1;
 
-       for (; at < top; at++) {
+       for (at = base; at < top; at++) {
+               /* Filter out any records that contain kernel addresses. */
+               if (event->attr.exclude_kernel &&
+                   (kernel_ip(at->from) || kernel_ip(at->to)))
+                       continue;
+
                data.ip         = at->from;
                data.addr       = at->to;
 
index b2c9475b7ff24af15c08a68f1847eca336bbec97..bfd0b717e944ce012b7a0abe19c89f5294d284e1 100644 (file)
@@ -151,10 +151,10 @@ static void __intel_pmu_lbr_enable(bool pmi)
         * No need to reprogram LBR_SELECT in a PMI, as it
         * did not change.
         */
-       if (cpuc->lbr_sel && !pmi) {
+       if (cpuc->lbr_sel)
                lbr_select = cpuc->lbr_sel->config;
+       if (!pmi)
                wrmsrl(MSR_LBR_SELECT, lbr_select);
-       }
 
        rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
        orig_debugctl = debugctl;
@@ -555,6 +555,8 @@ static int intel_pmu_setup_sw_lbr_filter(struct perf_event *event)
        if (br_type & PERF_SAMPLE_BRANCH_IND_JUMP)
                mask |= X86_BR_IND_JMP;
 
+       if (br_type & PERF_SAMPLE_BRANCH_CALL)
+               mask |= X86_BR_CALL | X86_BR_ZERO_CALL;
        /*
         * stash actual user request into reg, it may
         * be used by fixup code for some CPU
@@ -890,6 +892,7 @@ static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
        [PERF_SAMPLE_BRANCH_IND_CALL_SHIFT]     = LBR_IND_CALL,
        [PERF_SAMPLE_BRANCH_COND_SHIFT]         = LBR_JCC,
        [PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT]     = LBR_IND_JMP,
+       [PERF_SAMPLE_BRANCH_CALL_SHIFT]         = LBR_REL_CALL,
 };
 
 static const int hsw_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
@@ -905,6 +908,7 @@ static const int hsw_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
        [PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT]   = LBR_REL_CALL | LBR_IND_CALL
                                                | LBR_RETURN | LBR_CALL_STACK,
        [PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT]     = LBR_IND_JMP,
+       [PERF_SAMPLE_BRANCH_CALL_SHIFT]         = LBR_REL_CALL,
 };
 
 /* core */
index 42169283448b9c5af7a75175ad18d02ec0079612..868e1194337f6cf93cd1a8048480a60aa7acf3e3 100644 (file)
@@ -139,9 +139,6 @@ static int __init pt_pmu_hw_init(void)
        long i;
 
        attrs = NULL;
-       ret = -ENODEV;
-       if (!test_cpu_cap(&boot_cpu_data, X86_FEATURE_INTEL_PT))
-               goto fail;
 
        for (i = 0; i < PT_CPUID_LEAVES; i++) {
                cpuid_count(20, i,
@@ -1130,6 +1127,10 @@ static __init int pt_init(void)
        int ret, cpu, prior_warn = 0;
 
        BUILD_BUG_ON(sizeof(struct topa) > PAGE_SIZE);
+
+       if (!test_cpu_cap(&boot_cpu_data, X86_FEATURE_INTEL_PT))
+               return -ENODEV;
+
        get_online_cpus();
        for_each_online_cpu(cpu) {
                u64 ctl;
index 560e5255b15e870403728c3411c1b07246781cb7..61215a69b03d928d83dc6e86950bd5ad1b2db68c 100644 (file)
@@ -7,7 +7,8 @@ struct intel_uncore_type **uncore_pci_uncores = empty_uncore;
 static bool pcidrv_registered;
 struct pci_driver *uncore_pci_driver;
 /* pci bus to socket mapping */
-int uncore_pcibus_to_physid[256] = { [0 ... 255] = -1, };
+DEFINE_RAW_SPINLOCK(pci2phy_map_lock);
+struct list_head pci2phy_map_head = LIST_HEAD_INIT(pci2phy_map_head);
 struct pci_dev *uncore_extra_pci_dev[UNCORE_SOCKET_MAX][UNCORE_EXTRA_PCI_DEV_MAX];
 
 static DEFINE_RAW_SPINLOCK(uncore_box_lock);
@@ -20,6 +21,59 @@ static struct event_constraint uncore_constraint_fixed =
 struct event_constraint uncore_constraint_empty =
        EVENT_CONSTRAINT(0, 0, 0);
 
+int uncore_pcibus_to_physid(struct pci_bus *bus)
+{
+       struct pci2phy_map *map;
+       int phys_id = -1;
+
+       raw_spin_lock(&pci2phy_map_lock);
+       list_for_each_entry(map, &pci2phy_map_head, list) {
+               if (map->segment == pci_domain_nr(bus)) {
+                       phys_id = map->pbus_to_physid[bus->number];
+                       break;
+               }
+       }
+       raw_spin_unlock(&pci2phy_map_lock);
+
+       return phys_id;
+}
+
+struct pci2phy_map *__find_pci2phy_map(int segment)
+{
+       struct pci2phy_map *map, *alloc = NULL;
+       int i;
+
+       lockdep_assert_held(&pci2phy_map_lock);
+
+lookup:
+       list_for_each_entry(map, &pci2phy_map_head, list) {
+               if (map->segment == segment)
+                       goto end;
+       }
+
+       if (!alloc) {
+               raw_spin_unlock(&pci2phy_map_lock);
+               alloc = kmalloc(sizeof(struct pci2phy_map), GFP_KERNEL);
+               raw_spin_lock(&pci2phy_map_lock);
+
+               if (!alloc)
+                       return NULL;
+
+               goto lookup;
+       }
+
+       map = alloc;
+       alloc = NULL;
+       map->segment = segment;
+       for (i = 0; i < 256; i++)
+               map->pbus_to_physid[i] = -1;
+       list_add_tail(&map->list, &pci2phy_map_head);
+
+end:
+       kfree(alloc);
+       return map;
+}
+
 ssize_t uncore_event_show(struct kobject *kobj,
                          struct kobj_attribute *attr, char *buf)
 {
@@ -809,7 +863,7 @@ static int uncore_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id
        int phys_id;
        bool first_box = false;
 
-       phys_id = uncore_pcibus_to_physid[pdev->bus->number];
+       phys_id = uncore_pcibus_to_physid(pdev->bus);
        if (phys_id < 0)
                return -ENODEV;
 
@@ -856,9 +910,10 @@ static void uncore_pci_remove(struct pci_dev *pdev)
 {
        struct intel_uncore_box *box = pci_get_drvdata(pdev);
        struct intel_uncore_pmu *pmu;
-       int i, cpu, phys_id = uncore_pcibus_to_physid[pdev->bus->number];
+       int i, cpu, phys_id;
        bool last_box = false;
 
+       phys_id = uncore_pcibus_to_physid(pdev->bus);
        box = pci_get_drvdata(pdev);
        if (!box) {
                for (i = 0; i < UNCORE_EXTRA_PCI_DEV_MAX; i++) {
index 72c54c2e5b1afdd2102b39b1d53e4ee4de57518d..2f0a4a98e16bee4bcadde4591daab3be6e7c08cf 100644 (file)
@@ -117,6 +117,15 @@ struct uncore_event_desc {
        const char *config;
 };
 
+struct pci2phy_map {
+       struct list_head list;
+       int segment;
+       int pbus_to_physid[256];
+};
+
+int uncore_pcibus_to_physid(struct pci_bus *bus);
+struct pci2phy_map *__find_pci2phy_map(int segment);
+
 ssize_t uncore_event_show(struct kobject *kobj,
                          struct kobj_attribute *attr, char *buf);
 
@@ -317,7 +326,8 @@ u64 uncore_shared_reg_config(struct intel_uncore_box *box, int idx);
 extern struct intel_uncore_type **uncore_msr_uncores;
 extern struct intel_uncore_type **uncore_pci_uncores;
 extern struct pci_driver *uncore_pci_driver;
-extern int uncore_pcibus_to_physid[256];
+extern raw_spinlock_t pci2phy_map_lock;
+extern struct list_head pci2phy_map_head;
 extern struct pci_dev *uncore_extra_pci_dev[UNCORE_SOCKET_MAX][UNCORE_EXTRA_PCI_DEV_MAX];
 extern struct event_constraint uncore_constraint_empty;
 
index f78574b3cb55bafeb4d0a712c688b652fe770f72..845256158a10d79fdcc9ebd4946ca20c1b468277 100644 (file)
@@ -420,15 +420,25 @@ static void snb_uncore_imc_event_del(struct perf_event *event, int flags)
 static int snb_pci2phy_map_init(int devid)
 {
        struct pci_dev *dev = NULL;
-       int bus;
+       struct pci2phy_map *map;
+       int bus, segment;
 
        dev = pci_get_device(PCI_VENDOR_ID_INTEL, devid, dev);
        if (!dev)
                return -ENOTTY;
 
        bus = dev->bus->number;
-
-       uncore_pcibus_to_physid[bus] = 0;
+       segment = pci_domain_nr(dev->bus);
+
+       raw_spin_lock(&pci2phy_map_lock);
+       map = __find_pci2phy_map(segment);
+       if (!map) {
+               raw_spin_unlock(&pci2phy_map_lock);
+               pci_dev_put(dev);
+               return -ENOMEM;
+       }
+       map->pbus_to_physid[bus] = 0;
+       raw_spin_unlock(&pci2phy_map_lock);
 
        pci_dev_put(dev);
 
index 694510a887dc34d82ea608eaa07599f4a26dd726..f0f4fcba252e1545fc21d8d215329a631c471aac 100644 (file)
@@ -1087,7 +1087,8 @@ static struct pci_driver snbep_uncore_pci_driver = {
 static int snbep_pci2phy_map_init(int devid)
 {
        struct pci_dev *ubox_dev = NULL;
-       int i, bus, nodeid;
+       int i, bus, nodeid, segment;
+       struct pci2phy_map *map;
        int err = 0;
        u32 config = 0;
 
@@ -1106,16 +1107,27 @@ static int snbep_pci2phy_map_init(int devid)
                err = pci_read_config_dword(ubox_dev, 0x54, &config);
                if (err)
                        break;
+
+               segment = pci_domain_nr(ubox_dev->bus);
+               raw_spin_lock(&pci2phy_map_lock);
+               map = __find_pci2phy_map(segment);
+               if (!map) {
+                       raw_spin_unlock(&pci2phy_map_lock);
+                       err = -ENOMEM;
+                       break;
+               }
+
                /*
                 * every three bits in the Node ID mapping register maps
                 * to a particular node.
                 */
                for (i = 0; i < 8; i++) {
                        if (nodeid == ((config >> (3 * i)) & 0x7)) {
-                               uncore_pcibus_to_physid[bus] = i;
+                               map->pbus_to_physid[bus] = i;
                                break;
                        }
                }
+               raw_spin_unlock(&pci2phy_map_lock);
        }
 
        if (!err) {
@@ -1123,13 +1135,17 @@ static int snbep_pci2phy_map_init(int devid)
                 * For PCI bus with no UBOX device, find the next bus
                 * that has UBOX device and use its mapping.
                 */
-               i = -1;
-               for (bus = 255; bus >= 0; bus--) {
-                       if (uncore_pcibus_to_physid[bus] >= 0)
-                               i = uncore_pcibus_to_physid[bus];
-                       else
-                               uncore_pcibus_to_physid[bus] = i;
+               raw_spin_lock(&pci2phy_map_lock);
+               list_for_each_entry(map, &pci2phy_map_head, list) {
+                       i = -1;
+                       for (bus = 255; bus >= 0; bus--) {
+                               if (map->pbus_to_physid[bus] >= 0)
+                                       i = map->pbus_to_physid[bus];
+                               else
+                                       map->pbus_to_physid[bus] = i;
+                       }
                }
+               raw_spin_unlock(&pci2phy_map_lock);
        }
 
        pci_dev_put(ubox_dev);
@@ -2444,7 +2460,7 @@ static struct intel_uncore_type *bdx_pci_uncores[] = {
        NULL,
 };
 
-static DEFINE_PCI_DEVICE_TABLE(bdx_uncore_pci_ids) = {
+static const struct pci_device_id bdx_uncore_pci_ids[] = {
        { /* Home Agent 0 */
                PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f30),
                .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_HA, 0),
index 74ca2fe7a0b3a60d7fc6c71c2d5dda30fcf3e767..2c1910f6717ed5278fc6d85d25c92360664feb68 100644 (file)
@@ -75,8 +75,6 @@ struct crash_memmap_data {
        unsigned int type;
 };
 
-int in_crash_kexec;
-
 /*
  * This is used to VMCLEAR all VMCSs loaded on the
  * processor. And when loading kvm_intel module, the
@@ -132,7 +130,6 @@ static void kdump_nmi_callback(int cpu, struct pt_regs *regs)
 
 static void kdump_nmi_shootdown_cpus(void)
 {
-       in_crash_kexec = 1;
        nmi_shootdown_cpus(kdump_nmi_callback);
 
        disable_local_APIC();
index a102564d08eb3dd6157fab6b7af8fcf0da4d7c72..569c1e4f96feb897956a64e35bd8fccb07dca6c6 100644 (file)
@@ -911,7 +911,7 @@ void __init finish_e820_parsing(void)
        }
 }
 
-static inline const char *e820_type_to_string(int e820_type)
+static const char *e820_type_to_string(int e820_type)
 {
        switch (e820_type) {
        case E820_RESERVED_KERN:
index 9f9cc682e561e2427152314b544dd387b0c27922..db9a675e751b0bf1401af295a40b1e7a314bab6a 100644 (file)
@@ -584,7 +584,7 @@ static void __init intel_graphics_stolen(int num, int slot, int func)
 static void __init force_disable_hpet(int num, int slot, int func)
 {
 #ifdef CONFIG_HPET_TIMER
-       boot_hpet_disable = 1;
+       boot_hpet_disable = true;
        pr_info("x86/hpet: Will disable the HPET for this platform because it's not reliable\n");
 #endif
 }
index eec40f595ab97ee74b83002a21e09a0d2c032cbb..21bf92490a7b9c8897fef3ccb3846ddd82875a70 100644 (file)
@@ -195,14 +195,14 @@ static __init void early_serial_init(char *s)
 #ifdef CONFIG_PCI
 static void mem32_serial_out(unsigned long addr, int offset, int value)
 {
-       u32 *vaddr = (u32 *)addr;
+       u32 __iomem *vaddr = (u32 __iomem *)addr;
        /* shift implied by pointer type */
        writel(value, vaddr + offset);
 }
 
 static unsigned int mem32_serial_in(unsigned long addr, int offset)
 {
-       u32 *vaddr = (u32 *)addr;
+       u32 __iomem *vaddr = (u32 __iomem *)addr;
        /* shift implied by pointer type */
        return readl(vaddr + offset);
 }
@@ -316,7 +316,7 @@ static struct console early_serial_console = {
        .index =        -1,
 };
 
-static inline void early_console_register(struct console *con, int keep_early)
+static void early_console_register(struct console *con, int keep_early)
 {
        if (con->index != -1) {
                printk(KERN_CRIT "ERROR: earlyprintk= %s already used\n",
index d14e9ac3235a1ac73174ffb95b2990d5163d2933..be39b5fde4b9619a566eb5deee4ee9456a3f5cc5 100644 (file)
@@ -290,11 +290,11 @@ static void __init fpu__init_system_ctx_switch(void)
        if (cpu_has_xsaveopt && eagerfpu != DISABLE)
                eagerfpu = ENABLE;
 
-       if (xfeatures_mask & XSTATE_EAGER) {
+       if (xfeatures_mask & XFEATURE_MASK_EAGER) {
                if (eagerfpu == DISABLE) {
                        pr_err("x86/fpu: eagerfpu switching disabled, disabling the following xstate features: 0x%llx.\n",
-                              xfeatures_mask & XSTATE_EAGER);
-                       xfeatures_mask &= ~XSTATE_EAGER;
+                              xfeatures_mask & XFEATURE_MASK_EAGER);
+                       xfeatures_mask &= ~XFEATURE_MASK_EAGER;
                } else {
                        eagerfpu = ENABLE;
                }
@@ -354,17 +354,7 @@ static int __init x86_noxsave_setup(char *s)
        if (strlen(s))
                return 0;
 
-       setup_clear_cpu_cap(X86_FEATURE_XSAVE);
-       setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
-       setup_clear_cpu_cap(X86_FEATURE_XSAVEC);
-       setup_clear_cpu_cap(X86_FEATURE_XSAVES);
-       setup_clear_cpu_cap(X86_FEATURE_AVX);
-       setup_clear_cpu_cap(X86_FEATURE_AVX2);
-       setup_clear_cpu_cap(X86_FEATURE_AVX512F);
-       setup_clear_cpu_cap(X86_FEATURE_AVX512PF);
-       setup_clear_cpu_cap(X86_FEATURE_AVX512ER);
-       setup_clear_cpu_cap(X86_FEATURE_AVX512CD);
-       setup_clear_cpu_cap(X86_FEATURE_MPX);
+       fpu__xstate_clear_all_cpu_caps();
 
        return 1;
 }
index dc60810c1c74c6180d47149e93994957f5d3a9ad..0bc3490420c55e1fcbecb458d8442766b901aab2 100644 (file)
@@ -66,7 +66,7 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
         * presence of FP and SSE state.
         */
        if (cpu_has_xsave)
-               fpu->state.xsave.header.xfeatures |= XSTATE_FPSSE;
+               fpu->state.xsave.header.xfeatures |= XFEATURE_MASK_FPSSE;
 
        return ret;
 }
@@ -326,7 +326,7 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset,
         * presence of FP.
         */
        if (cpu_has_xsave)
-               fpu->state.xsave.header.xfeatures |= XSTATE_FP;
+               fpu->state.xsave.header.xfeatures |= XFEATURE_MASK_FP;
        return ret;
 }
 
index 50ec9af1bd5189d9c51de284a383103842d9d60a..ef29b742cea79a99c8e653ea91ae83e82b08ba65 100644 (file)
@@ -56,7 +56,7 @@ static inline int save_fsave_header(struct task_struct *tsk, void __user *buf)
        if (use_fxsr()) {
                struct xregs_state *xsave = &tsk->thread.fpu.state.xsave;
                struct user_i387_ia32_struct env;
-               struct _fpstate_ia32 __user *fp = buf;
+               struct _fpstate_32 __user *fp = buf;
 
                convert_from_fxsr(&env, tsk);
 
@@ -107,7 +107,7 @@ static inline int save_xstate_epilog(void __user *buf, int ia32_frame)
         * header as well as change any contents in the memory layout.
         * xrestore as part of sigreturn will capture all the changes.
         */
-       xfeatures |= XSTATE_FPSSE;
+       xfeatures |= XFEATURE_MASK_FPSSE;
 
        err |= __put_user(xfeatures, (__u32 *)&x->header.xfeatures);
 
@@ -165,7 +165,7 @@ int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size)
        if (!static_cpu_has(X86_FEATURE_FPU))
                return fpregs_soft_get(current, NULL, 0,
                        sizeof(struct user_i387_ia32_struct), NULL,
-                       (struct _fpstate_ia32 __user *) buf) ? -1 : 1;
+                       (struct _fpstate_32 __user *) buf) ? -1 : 1;
 
        if (fpregs_active()) {
                /* Save the live register state to the user directly. */
@@ -207,7 +207,7 @@ sanitize_restored_xstate(struct task_struct *tsk,
                 * layout and not enabled by the OS.
                 */
                if (fx_only)
-                       header->xfeatures = XSTATE_FPSSE;
+                       header->xfeatures = XFEATURE_MASK_FPSSE;
                else
                        header->xfeatures &= (xfeatures_mask & xfeatures);
        }
@@ -230,7 +230,7 @@ static inline int copy_user_to_fpregs_zeroing(void __user *buf, u64 xbv, int fx_
 {
        if (use_xsave()) {
                if ((unsigned long)buf % 64 || fx_only) {
-                       u64 init_bv = xfeatures_mask & ~XSTATE_FPSSE;
+                       u64 init_bv = xfeatures_mask & ~XFEATURE_MASK_FPSSE;
                        copy_kernel_to_xregs(&init_fpstate.xsave, init_bv);
                        return copy_user_to_fxregs(buf);
                } else {
index 62fc001c7846d001490377485f445d4b598ebb64..6454f2731b5666e49aeda8f87d96078d564de2f2 100644 (file)
@@ -31,12 +31,28 @@ static const char *xfeature_names[] =
  */
 u64 xfeatures_mask __read_mostly;
 
-static unsigned int xstate_offsets[XFEATURES_NR_MAX] = { [ 0 ... XFEATURES_NR_MAX - 1] = -1};
-static unsigned int xstate_sizes[XFEATURES_NR_MAX]   = { [ 0 ... XFEATURES_NR_MAX - 1] = -1};
+static unsigned int xstate_offsets[XFEATURE_MAX] = { [ 0 ... XFEATURE_MAX - 1] = -1};
+static unsigned int xstate_sizes[XFEATURE_MAX]   = { [ 0 ... XFEATURE_MAX - 1] = -1};
 static unsigned int xstate_comp_offsets[sizeof(xfeatures_mask)*8];
 
-/* The number of supported xfeatures in xfeatures_mask: */
-static unsigned int xfeatures_nr;
+/*
+ * Clear all of the X86_FEATURE_* bits that are unavailable
+ * when the CPU has no XSAVE support.
+ */
+void fpu__xstate_clear_all_cpu_caps(void)
+{
+       setup_clear_cpu_cap(X86_FEATURE_XSAVE);
+       setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
+       setup_clear_cpu_cap(X86_FEATURE_XSAVEC);
+       setup_clear_cpu_cap(X86_FEATURE_XSAVES);
+       setup_clear_cpu_cap(X86_FEATURE_AVX);
+       setup_clear_cpu_cap(X86_FEATURE_AVX2);
+       setup_clear_cpu_cap(X86_FEATURE_AVX512F);
+       setup_clear_cpu_cap(X86_FEATURE_AVX512PF);
+       setup_clear_cpu_cap(X86_FEATURE_AVX512ER);
+       setup_clear_cpu_cap(X86_FEATURE_AVX512CD);
+       setup_clear_cpu_cap(X86_FEATURE_MPX);
+}
 
 /*
  * Return whether the system supports a given xfeature.
@@ -53,7 +69,7 @@ int cpu_has_xfeatures(u64 xfeatures_needed, const char **feature_name)
                /*
                 * So we use FLS here to be able to print the most advanced
                 * feature that was requested but is missing. So if a driver
-                * asks about "XSTATE_SSE | XSTATE_YMM" we'll print the
+                * asks about "XFEATURE_MASK_SSE | XFEATURE_MASK_YMM" we'll print the
                 * missing AVX feature - this is the most informative message
                 * to users:
                 */
@@ -112,7 +128,7 @@ void fpstate_sanitize_xstate(struct fpu *fpu)
        /*
         * FP is in init state
         */
-       if (!(xfeatures & XSTATE_FP)) {
+       if (!(xfeatures & XFEATURE_MASK_FP)) {
                fx->cwd = 0x37f;
                fx->swd = 0;
                fx->twd = 0;
@@ -125,7 +141,7 @@ void fpstate_sanitize_xstate(struct fpu *fpu)
        /*
         * SSE is in init state
         */
-       if (!(xfeatures & XSTATE_SSE))
+       if (!(xfeatures & XFEATURE_MASK_SSE))
                memset(&fx->xmm_space[0], 0, 256);
 
        /*
@@ -168,26 +184,44 @@ void fpu__init_cpu_xstate(void)
        xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask);
 }
 
+/*
+ * Note that in the future we will likely need a pair of
+ * functions here: one for user xstates and the other for
+ * system xstates.  For now, they are the same.
+ */
+static int xfeature_enabled(enum xfeature xfeature)
+{
+       return !!(xfeatures_mask & (1UL << xfeature));
+}
+
 /*
  * Record the offsets and sizes of various xstates contained
  * in the XSAVE state memory layout.
- *
- * ( Note that certain features might be non-present, for them
- *   we'll have 0 offset and 0 size. )
  */
 static void __init setup_xstate_features(void)
 {
-       u32 eax, ebx, ecx, edx, leaf;
-
-       xfeatures_nr = fls64(xfeatures_mask);
-
-       for (leaf = 2; leaf < xfeatures_nr; leaf++) {
-               cpuid_count(XSTATE_CPUID, leaf, &eax, &ebx, &ecx, &edx);
-
-               xstate_offsets[leaf] = ebx;
-               xstate_sizes[leaf] = eax;
+       u32 eax, ebx, ecx, edx, i;
+       /* start at the beginnning of the "extended state" */
+       unsigned int last_good_offset = offsetof(struct xregs_state,
+                                                extended_state_area);
+
+       for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
+               if (!xfeature_enabled(i))
+                       continue;
+
+               cpuid_count(XSTATE_CPUID, i, &eax, &ebx, &ecx, &edx);
+               xstate_offsets[i] = ebx;
+               xstate_sizes[i] = eax;
+               /*
+                * In our xstate size checks, we assume that the
+                * highest-numbered xstate feature has the
+                * highest offset in the buffer.  Ensure it does.
+                */
+               WARN_ONCE(last_good_offset > xstate_offsets[i],
+                       "x86/fpu: misordered xstate at %d\n", last_good_offset);
+               last_good_offset = xstate_offsets[i];
 
-               printk(KERN_INFO "x86/fpu: xstate_offset[%d]: %04x, xstate_sizes[%d]: %04x\n", leaf, ebx, leaf, eax);
+               printk(KERN_INFO "x86/fpu: xstate_offset[%d]: %4d, xstate_sizes[%d]: %4d\n", i, ebx, i, eax);
        }
 }
 
@@ -204,14 +238,14 @@ static void __init print_xstate_feature(u64 xstate_mask)
  */
 static void __init print_xstate_features(void)
 {
-       print_xstate_feature(XSTATE_FP);
-       print_xstate_feature(XSTATE_SSE);
-       print_xstate_feature(XSTATE_YMM);
-       print_xstate_feature(XSTATE_BNDREGS);
-       print_xstate_feature(XSTATE_BNDCSR);
-       print_xstate_feature(XSTATE_OPMASK);
-       print_xstate_feature(XSTATE_ZMM_Hi256);
-       print_xstate_feature(XSTATE_Hi16_ZMM);
+       print_xstate_feature(XFEATURE_MASK_FP);
+       print_xstate_feature(XFEATURE_MASK_SSE);
+       print_xstate_feature(XFEATURE_MASK_YMM);
+       print_xstate_feature(XFEATURE_MASK_BNDREGS);
+       print_xstate_feature(XFEATURE_MASK_BNDCSR);
+       print_xstate_feature(XFEATURE_MASK_OPMASK);
+       print_xstate_feature(XFEATURE_MASK_ZMM_Hi256);
+       print_xstate_feature(XFEATURE_MASK_Hi16_ZMM);
 }
 
 /*
@@ -233,8 +267,8 @@ static void __init setup_xstate_comp(void)
        xstate_comp_offsets[1] = offsetof(struct fxregs_state, xmm_space);
 
        if (!cpu_has_xsaves) {
-               for (i = 2; i < xfeatures_nr; i++) {
-                       if (test_bit(i, (unsigned long *)&xfeatures_mask)) {
+               for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
+                       if (xfeature_enabled(i)) {
                                xstate_comp_offsets[i] = xstate_offsets[i];
                                xstate_comp_sizes[i] = xstate_sizes[i];
                        }
@@ -242,15 +276,16 @@ static void __init setup_xstate_comp(void)
                return;
        }
 
-       xstate_comp_offsets[2] = FXSAVE_SIZE + XSAVE_HDR_SIZE;
+       xstate_comp_offsets[FIRST_EXTENDED_XFEATURE] =
+               FXSAVE_SIZE + XSAVE_HDR_SIZE;
 
-       for (i = 2; i < xfeatures_nr; i++) {
-               if (test_bit(i, (unsigned long *)&xfeatures_mask))
+       for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
+               if (xfeature_enabled(i))
                        xstate_comp_sizes[i] = xstate_sizes[i];
                else
                        xstate_comp_sizes[i] = 0;
 
-               if (i > 2)
+               if (i > FIRST_EXTENDED_XFEATURE)
                        xstate_comp_offsets[i] = xstate_comp_offsets[i-1]
                                        + xstate_comp_sizes[i-1];
 
@@ -290,27 +325,280 @@ static void __init setup_init_fpu_buf(void)
        copy_xregs_to_kernel_booting(&init_fpstate.xsave);
 }
 
+static int xfeature_is_supervisor(int xfeature_nr)
+{
+       /*
+        * We currently do not support supervisor states, but if
+        * we did, we could find out like this.
+        *
+        * SDM says: If state component i is a user state component,
+        * ECX[0] return 0; if state component i is a supervisor
+        * state component, ECX[0] returns 1.
+       u32 eax, ebx, ecx, edx;
+       cpuid_count(XSTATE_CPUID, xfeature_nr, &eax, &ebx, &ecx, &edx;
+       return !!(ecx & 1);
+       */
+       return 0;
+}
+/*
+static int xfeature_is_user(int xfeature_nr)
+{
+       return !xfeature_is_supervisor(xfeature_nr);
+}
+*/
+
+/*
+ * This check is important because it is easy to get XSTATE_*
+ * confused with XSTATE_BIT_*.
+ */
+#define CHECK_XFEATURE(nr) do {                \
+       WARN_ON(nr < FIRST_EXTENDED_XFEATURE);  \
+       WARN_ON(nr >= XFEATURE_MAX);    \
+} while (0)
+
+/*
+ * We could cache this like xstate_size[], but we only use
+ * it here, so it would be a waste of space.
+ */
+static int xfeature_is_aligned(int xfeature_nr)
+{
+       u32 eax, ebx, ecx, edx;
+
+       CHECK_XFEATURE(xfeature_nr);
+       cpuid_count(XSTATE_CPUID, xfeature_nr, &eax, &ebx, &ecx, &edx);
+       /*
+        * The value returned by ECX[1] indicates the alignment
+        * of state component i when the compacted format
+        * of the extended region of an XSAVE area is used
+        */
+       return !!(ecx & 2);
+}
+
+static int xfeature_uncompacted_offset(int xfeature_nr)
+{
+       u32 eax, ebx, ecx, edx;
+
+       CHECK_XFEATURE(xfeature_nr);
+       cpuid_count(XSTATE_CPUID, xfeature_nr, &eax, &ebx, &ecx, &edx);
+       return ebx;
+}
+
+static int xfeature_size(int xfeature_nr)
+{
+       u32 eax, ebx, ecx, edx;
+
+       CHECK_XFEATURE(xfeature_nr);
+       cpuid_count(XSTATE_CPUID, xfeature_nr, &eax, &ebx, &ecx, &edx);
+       return eax;
+}
+
+/*
+ * 'XSAVES' implies two different things:
+ * 1. saving of supervisor/system state
+ * 2. using the compacted format
+ *
+ * Use this function when dealing with the compacted format so
+ * that it is obvious which aspect of 'XSAVES' is being handled
+ * by the calling code.
+ */
+static int using_compacted_format(void)
+{
+       return cpu_has_xsaves;
+}
+
+static void __xstate_dump_leaves(void)
+{
+       int i;
+       u32 eax, ebx, ecx, edx;
+       static int should_dump = 1;
+
+       if (!should_dump)
+               return;
+       should_dump = 0;
+       /*
+        * Dump out a few leaves past the ones that we support
+        * just in case there are some goodies up there
+        */
+       for (i = 0; i < XFEATURE_MAX + 10; i++) {
+               cpuid_count(XSTATE_CPUID, i, &eax, &ebx, &ecx, &edx);
+               pr_warn("CPUID[%02x, %02x]: eax=%08x ebx=%08x ecx=%08x edx=%08x\n",
+                       XSTATE_CPUID, i, eax, ebx, ecx, edx);
+       }
+}
+
+#define XSTATE_WARN_ON(x) do {                                                 \
+       if (WARN_ONCE(x, "XSAVE consistency problem, dumping leaves")) {        \
+               __xstate_dump_leaves();                                         \
+       }                                                                       \
+} while (0)
+
+#define XCHECK_SZ(sz, nr, nr_macro, __struct) do {                     \
+       if ((nr == nr_macro) &&                                         \
+           WARN_ONCE(sz != sizeof(__struct),                           \
+               "%s: struct is %zu bytes, cpu state %d bytes\n",        \
+               __stringify(nr_macro), sizeof(__struct), sz)) {         \
+               __xstate_dump_leaves();                                 \
+       }                                                               \
+} while (0)
+
+/*
+ * We have a C struct for each 'xstate'.  We need to ensure
+ * that our software representation matches what the CPU
+ * tells us about the state's size.
+ */
+static void check_xstate_against_struct(int nr)
+{
+       /*
+        * Ask the CPU for the size of the state.
+        */
+       int sz = xfeature_size(nr);
+       /*
+        * Match each CPU state with the corresponding software
+        * structure.
+        */
+       XCHECK_SZ(sz, nr, XFEATURE_YMM,       struct ymmh_struct);
+       XCHECK_SZ(sz, nr, XFEATURE_BNDREGS,   struct mpx_bndreg_state);
+       XCHECK_SZ(sz, nr, XFEATURE_BNDCSR,    struct mpx_bndcsr_state);
+       XCHECK_SZ(sz, nr, XFEATURE_OPMASK,    struct avx_512_opmask_state);
+       XCHECK_SZ(sz, nr, XFEATURE_ZMM_Hi256, struct avx_512_zmm_uppers_state);
+       XCHECK_SZ(sz, nr, XFEATURE_Hi16_ZMM,  struct avx_512_hi16_state);
+
+       /*
+        * Make *SURE* to add any feature numbers in below if
+        * there are "holes" in the xsave state component
+        * numbers.
+        */
+       if ((nr < XFEATURE_YMM) ||
+           (nr >= XFEATURE_MAX)) {
+               WARN_ONCE(1, "no structure for xstate: %d\n", nr);
+               XSTATE_WARN_ON(1);
+       }
+}
+
+/*
+ * This essentially double-checks what the cpu told us about
+ * how large the XSAVE buffer needs to be.  We are recalculating
+ * it to be safe.
+ */
+static void do_extra_xstate_size_checks(void)
+{
+       int paranoid_xstate_size = FXSAVE_SIZE + XSAVE_HDR_SIZE;
+       int i;
+
+       for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
+               if (!xfeature_enabled(i))
+                       continue;
+
+               check_xstate_against_struct(i);
+               /*
+                * Supervisor state components can be managed only by
+                * XSAVES, which is compacted-format only.
+                */
+               if (!using_compacted_format())
+                       XSTATE_WARN_ON(xfeature_is_supervisor(i));
+
+               /* Align from the end of the previous feature */
+               if (xfeature_is_aligned(i))
+                       paranoid_xstate_size = ALIGN(paranoid_xstate_size, 64);
+               /*
+                * The offset of a given state in the non-compacted
+                * format is given to us in a CPUID leaf.  We check
+                * them for being ordered (increasing offsets) in
+                * setup_xstate_features().
+                */
+               if (!using_compacted_format())
+                       paranoid_xstate_size = xfeature_uncompacted_offset(i);
+               /*
+                * The compacted-format offset always depends on where
+                * the previous state ended.
+                */
+               paranoid_xstate_size += xfeature_size(i);
+       }
+       XSTATE_WARN_ON(paranoid_xstate_size != xstate_size);
+}
+
 /*
  * Calculate total size of enabled xstates in XCR0/xfeatures_mask.
+ *
+ * Note the SDM's wording here.  "sub-function 0" only enumerates
+ * the size of the *user* states.  If we use it to size a buffer
+ * that we use 'XSAVES' on, we could potentially overflow the
+ * buffer because 'XSAVES' saves system states too.
+ *
+ * Note that we do not currently set any bits on IA32_XSS so
+ * 'XCR0 | IA32_XSS == XCR0' for now.
  */
-static void __init init_xstate_size(void)
+static unsigned int __init calculate_xstate_size(void)
 {
        unsigned int eax, ebx, ecx, edx;
-       int i;
+       unsigned int calculated_xstate_size;
 
        if (!cpu_has_xsaves) {
+               /*
+                * - CPUID function 0DH, sub-function 0:
+                *    EBX enumerates the size (in bytes) required by
+                *    the XSAVE instruction for an XSAVE area
+                *    containing all the *user* state components
+                *    corresponding to bits currently set in XCR0.
+                */
                cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx);
-               xstate_size = ebx;
-               return;
+               calculated_xstate_size = ebx;
+       } else {
+               /*
+                * - CPUID function 0DH, sub-function 1:
+                *    EBX enumerates the size (in bytes) required by
+                *    the XSAVES instruction for an XSAVE area
+                *    containing all the state components
+                *    corresponding to bits currently set in
+                *    XCR0 | IA32_XSS.
+                */
+               cpuid_count(XSTATE_CPUID, 1, &eax, &ebx, &ecx, &edx);
+               calculated_xstate_size = ebx;
        }
+       return calculated_xstate_size;
+}
 
-       xstate_size = FXSAVE_SIZE + XSAVE_HDR_SIZE;
-       for (i = 2; i < 64; i++) {
-               if (test_bit(i, (unsigned long *)&xfeatures_mask)) {
-                       cpuid_count(XSTATE_CPUID, i, &eax, &ebx, &ecx, &edx);
-                       xstate_size += eax;
-               }
-       }
+/*
+ * Will the runtime-enumerated 'xstate_size' fit in the init
+ * task's statically-allocated buffer?
+ */
+static bool is_supported_xstate_size(unsigned int test_xstate_size)
+{
+       if (test_xstate_size <= sizeof(union fpregs_state))
+               return true;
+
+       pr_warn("x86/fpu: xstate buffer too small (%zu < %d), disabling xsave\n",
+                       sizeof(union fpregs_state), test_xstate_size);
+       return false;
+}
+
+static int init_xstate_size(void)
+{
+       /* Recompute the context size for enabled features: */
+       unsigned int possible_xstate_size = calculate_xstate_size();
+
+       /* Ensure we have the space to store all enabled: */
+       if (!is_supported_xstate_size(possible_xstate_size))
+               return -EINVAL;
+
+       /*
+        * The size is OK, we are definitely going to use xsave,
+        * make it known to the world that we need more space.
+        */
+       xstate_size = possible_xstate_size;
+       do_extra_xstate_size_checks();
+       return 0;
+}
+
+/*
+ * We enabled the XSAVE hardware, but something went wrong and
+ * we can not use it.  Disable it.
+ */
+static void fpu__init_disable_system_xstate(void)
+{
+       xfeatures_mask = 0;
+       cr4_clear_bits(X86_CR4_OSXSAVE);
+       fpu__xstate_clear_all_cpu_caps();
 }
 
 /*
@@ -321,6 +609,7 @@ void __init fpu__init_system_xstate(void)
 {
        unsigned int eax, ebx, ecx, edx;
        static int on_boot_cpu = 1;
+       int err;
 
        WARN_ON_FPU(!on_boot_cpu);
        on_boot_cpu = 0;
@@ -338,7 +627,7 @@ void __init fpu__init_system_xstate(void)
        cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx);
        xfeatures_mask = eax + ((u64)edx << 32);
 
-       if ((xfeatures_mask & XSTATE_FPSSE) != XSTATE_FPSSE) {
+       if ((xfeatures_mask & XFEATURE_MASK_FPSSE) != XFEATURE_MASK_FPSSE) {
                pr_err("x86/fpu: FP/SSE not present amongst the CPU's xstate features: 0x%llx.\n", xfeatures_mask);
                BUG();
        }
@@ -348,16 +637,19 @@ void __init fpu__init_system_xstate(void)
 
        /* Enable xstate instructions to be able to continue with initialization: */
        fpu__init_cpu_xstate();
-
-       /* Recompute the context size for enabled features: */
-       init_xstate_size();
+       err = init_xstate_size();
+       if (err) {
+               /* something went wrong, boot without any XSAVE support */
+               fpu__init_disable_system_xstate();
+               return;
+       }
 
        update_regset_xstate_info(xstate_size, xfeatures_mask);
        fpu__init_prepare_fx_sw_frame();
        setup_init_fpu_buf();
        setup_xstate_comp();
 
-       pr_info("x86/fpu: Enabled xstate features 0x%llx, context size is 0x%x bytes, using '%s' format.\n",
+       pr_info("x86/fpu: Enabled xstate features 0x%llx, context size is %d bytes, using '%s' format.\n",
                xfeatures_mask,
                xstate_size,
                cpu_has_xsaves ? "compacted" : "standard");
@@ -388,7 +680,7 @@ void fpu__resume_cpu(void)
  * Inputs:
  *     xstate: the thread's storage area for all FPU data
  *     xstate_feature: state which is defined in xsave.h (e.g.
- *     XSTATE_FP, XSTATE_SSE, etc...)
+ *     XFEATURE_MASK_FP, XFEATURE_MASK_SSE, etc...)
  * Output:
  *     address of the state in the xsave area, or NULL if the
  *     field is not present in the xsave buffer.
@@ -439,8 +731,8 @@ EXPORT_SYMBOL_GPL(get_xsave_addr);
  * Note that this only works on the current task.
  *
  * Inputs:
- *     @xsave_state: state which is defined in xsave.h (e.g. XSTATE_FP,
- *     XSTATE_SSE, etc...)
+ *     @xsave_state: state which is defined in xsave.h (e.g. XFEATURE_MASK_FP,
+ *     XFEATURE_MASK_SSE, etc...)
  * Output:
  *     address of the state in the xsave area or NULL if the state
  *     is not present or is in its 'init state'.
index 0e2d96ffd158d0e5f4c1d355040cd9b285ef84d6..6bc9ae24b6d2a74930701c0c6ea60fe090a3ebbc 100644 (file)
@@ -152,7 +152,7 @@ ENTRY(startup_32)
        movl %eax, pa(olpc_ofw_pgd)
 #endif
 
-#ifdef CONFIG_MICROCODE_EARLY
+#ifdef CONFIG_MICROCODE
        /* Early load ucode on BSP. */
        call load_ucode_bsp
 #endif
@@ -311,12 +311,11 @@ ENTRY(startup_32_smp)
        movl %eax,%ss
        leal -__PAGE_OFFSET(%ecx),%esp
 
-#ifdef CONFIG_MICROCODE_EARLY
+#ifdef CONFIG_MICROCODE
        /* Early load ucode on AP. */
        call load_ucode_ap
 #endif
 
-
 default_entry:
 #define CR0_STATE      (X86_CR0_PE | X86_CR0_MP | X86_CR0_ET | \
                         X86_CR0_NE | X86_CR0_WP | X86_CR0_AM | \
index 88b4da37308115b2189fd811f12a67f1a2c10ff8..b8e6ff5cd5d055892715af36f1e9b72340f7b18c 100644 (file)
  */
 unsigned long                          hpet_address;
 u8                                     hpet_blockid; /* OS timer block num */
-u8                                     hpet_msi_disable;
+bool                                   hpet_msi_disable;
 
 #ifdef CONFIG_PCI_MSI
-static unsigned long                   hpet_num_timers;
+static unsigned int                    hpet_num_timers;
 #endif
 static void __iomem                    *hpet_virt_address;
 
@@ -86,9 +86,9 @@ static inline void hpet_clear_mapping(void)
 /*
  * HPET command line enable / disable
  */
-int boot_hpet_disable;
-int hpet_force_user;
-static int hpet_verbose;
+bool boot_hpet_disable;
+bool hpet_force_user;
+static bool hpet_verbose;
 
 static int __init hpet_setup(char *str)
 {
@@ -98,11 +98,11 @@ static int __init hpet_setup(char *str)
                if (next)
                        *next++ = 0;
                if (!strncmp("disable", str, 7))
-                       boot_hpet_disable = 1;
+                       boot_hpet_disable = true;
                if (!strncmp("force", str, 5))
-                       hpet_force_user = 1;
+                       hpet_force_user = true;
                if (!strncmp("verbose", str, 7))
-                       hpet_verbose = 1;
+                       hpet_verbose = true;
                str = next;
        }
        return 1;
@@ -111,7 +111,7 @@ __setup("hpet=", hpet_setup);
 
 static int __init disable_hpet(char *str)
 {
-       boot_hpet_disable = 1;
+       boot_hpet_disable = true;
        return 1;
 }
 __setup("nohpet", disable_hpet);
@@ -124,7 +124,7 @@ static inline int is_hpet_capable(void)
 /*
  * HPET timer interrupt enable / disable
  */
-static int hpet_legacy_int_enabled;
+static bool hpet_legacy_int_enabled;
 
 /**
  * is_hpet_enabled - check whether the hpet timer interrupt is enabled
@@ -230,7 +230,7 @@ static struct clock_event_device hpet_clockevent;
 
 static void hpet_stop_counter(void)
 {
-       unsigned long cfg = hpet_readl(HPET_CFG);
+       u32 cfg = hpet_readl(HPET_CFG);
        cfg &= ~HPET_CFG_ENABLE;
        hpet_writel(cfg, HPET_CFG);
 }
@@ -272,7 +272,7 @@ static void hpet_enable_legacy_int(void)
 
        cfg |= HPET_CFG_LEGACY;
        hpet_writel(cfg, HPET_CFG);
-       hpet_legacy_int_enabled = 1;
+       hpet_legacy_int_enabled = true;
 }
 
 static void hpet_legacy_clockevent_register(void)
@@ -983,7 +983,7 @@ void hpet_disable(void)
                        cfg = *hpet_boot_cfg;
                else if (hpet_legacy_int_enabled) {
                        cfg &= ~HPET_CFG_LEGACY;
-                       hpet_legacy_int_enabled = 0;
+                       hpet_legacy_int_enabled = false;
                }
                cfg &= ~HPET_CFG_ENABLE;
                hpet_writel(cfg, HPET_CFG);
@@ -1121,8 +1121,7 @@ EXPORT_SYMBOL_GPL(hpet_rtc_timer_init);
 
 static void hpet_disable_rtc_channel(void)
 {
-       unsigned long cfg;
-       cfg = hpet_readl(HPET_T1_CFG);
+       u32 cfg = hpet_readl(HPET_T1_CFG);
        cfg &= ~HPET_TN_ENABLE;
        hpet_writel(cfg, HPET_T1_CFG);
 }
index c767cf2bc80a0b8f44be521f6171c3d8d4fa7b7d..206d0b90a3ab1e800e372ada94b2bd9f8dac4e5b 100644 (file)
@@ -72,7 +72,7 @@ bool handle_irq(struct irq_desc *desc, struct pt_regs *regs)
 {
        stack_overflow_check(regs);
 
-       if (unlikely(IS_ERR_OR_NULL(desc)))
+       if (IS_ERR_OR_NULL(desc))
                return false;
 
        generic_handle_irq_desc(desc);
index d6178d9791db7966e8bc188e3df16c03233c3da3..44256a62702b2c51077fc0b8b82a904ed122b9f6 100644 (file)
@@ -511,26 +511,31 @@ single_step_cont(struct pt_regs *regs, struct die_args *args)
        return NOTIFY_STOP;
 }
 
-static int was_in_debug_nmi[NR_CPUS];
+static DECLARE_BITMAP(was_in_debug_nmi, NR_CPUS);
 
 static int kgdb_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 {
+       int cpu;
+
        switch (cmd) {
        case NMI_LOCAL:
                if (atomic_read(&kgdb_active) != -1) {
                        /* KGDB CPU roundup */
-                       kgdb_nmicallback(raw_smp_processor_id(), regs);
-                       was_in_debug_nmi[raw_smp_processor_id()] = 1;
+                       cpu = raw_smp_processor_id();
+                       kgdb_nmicallback(cpu, regs);
+                       set_bit(cpu, was_in_debug_nmi);
                        touch_nmi_watchdog();
+
                        return NMI_HANDLED;
                }
                break;
 
        case NMI_UNKNOWN:
-               if (was_in_debug_nmi[raw_smp_processor_id()]) {
-                       was_in_debug_nmi[raw_smp_processor_id()] = 0;
+               cpu = raw_smp_processor_id();
+
+               if (__test_and_clear_bit(cpu, was_in_debug_nmi))
                        return NMI_HANDLED;
-               }
+
                break;
        default:
                /* do nothing */
index 1b55de1267cfc4f3032b37c72c14239533fbdbcf..cd99433b8ba17597cbc9e91aba9c40eee7e05e4b 100644 (file)
@@ -131,11 +131,12 @@ void dma_generic_free_coherent(struct device *dev, size_t size, void *vaddr,
 
 bool arch_dma_alloc_attrs(struct device **dev, gfp_t *gfp)
 {
+       if (!*dev)
+               *dev = &x86_dma_fallback_dev;
+
        *gfp &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
        *gfp = dma_alloc_coherent_gfp_flags(*dev, *gfp);
 
-       if (!*dev)
-               *dev = &x86_dma_fallback_dev;
        if (!is_device_dma_capable(*dev))
                return false;
        return true;
index e28db181e4fcb04d19c02e7709e20fabb06f8c29..9f7c21c22477e59462d72e930d79a4c2a238a051 100644 (file)
@@ -84,6 +84,9 @@ EXPORT_SYMBOL_GPL(idle_notifier_unregister);
 int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 {
        memcpy(dst, src, arch_task_struct_size);
+#ifdef CONFIG_VM86
+       dst->thread.vm86 = NULL;
+#endif
 
        return fpu__copy(&dst->thread.fpu, &src->thread.fpu);
 }
index 737527b40e5bf40bb1e757b635cc30994db911bd..9f950917528b332b139afb6fa854c87c8d92c4d0 100644 (file)
@@ -279,14 +279,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
        if (get_kernel_rpl() && unlikely(prev->iopl != next->iopl))
                set_iopl_mask(next->iopl);
 
-       /*
-        * If it were not for PREEMPT_ACTIVE we could guarantee that the
-        * preempt_count of all tasks was equal here and this would not be
-        * needed.
-        */
-       task_thread_info(prev_p)->saved_preempt_count = this_cpu_read(__preempt_count);
-       this_cpu_write(__preempt_count, task_thread_info(next_p)->saved_preempt_count);
-
        /*
         * Now maybe handle debug registers and/or IO bitmaps
         */
index b35921a670b25b03878e3f9ac2e96abfae0e910c..e835d263a33b43ccf7601698cfeadc447ede491e 100644 (file)
@@ -332,7 +332,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
        /*
         * Switch FS and GS.
         *
-        * These are even more complicated than FS and GS: they have
+        * These are even more complicated than DS and ES: they have
         * 64-bit bases are that controlled by arch_prctl.  Those bases
         * only differ from the values in the GDT or LDT if the selector
         * is 0.
@@ -401,14 +401,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
         */
        this_cpu_write(current_task, next_p);
 
-       /*
-        * If it were not for PREEMPT_ACTIVE we could guarantee that the
-        * preempt_count of all tasks was equal here and this would not be
-        * needed.
-        */
-       task_thread_info(prev_p)->saved_preempt_count = this_cpu_read(__preempt_count);
-       this_cpu_write(__preempt_count, task_thread_info(next_p)->saved_preempt_count);
-
        /* Reload esp0 and ss1.  This changes current_thread_info(). */
        load_sp0(tss, next);
 
index 176a0f99d4daa6f2f94d2e505b1452d3a87d02db..cc457ff818ad65c0ea92bb29257d0629141716fa 100644 (file)
@@ -524,7 +524,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E6XX_CU,
  */
 static void force_disable_hpet_msi(struct pci_dev *unused)
 {
-       hpet_msi_disable = 1;
+       hpet_msi_disable = true;
 }
 
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS,
index a3cccbfc5f777e3483cad7da4a5a587bdd56c09c..a1e4da98c8f0db8c838766e9466c2608c124fe98 100644 (file)
 #include <asm/mce.h>
 #include <asm/alternative.h>
 #include <asm/prom.h>
+#include <asm/microcode.h>
 
 /*
  * max_low_pfn_mapped: highest direct mapped pfn under 4GB
@@ -480,34 +481,34 @@ static void __init memblock_x86_reserve_range_setup_data(void)
 
 #ifdef CONFIG_KEXEC_CORE
 
+/* 16M alignment for crash kernel regions */
+#define CRASH_ALIGN            (16 << 20)
+
 /*
  * Keep the crash kernel below this limit.  On 32 bits earlier kernels
  * would limit the kernel to the low 512 MiB due to mapping restrictions.
  * On 64bit, old kexec-tools need to under 896MiB.
  */
 #ifdef CONFIG_X86_32
-# define CRASH_KERNEL_ADDR_LOW_MAX     (512 << 20)
-# define CRASH_KERNEL_ADDR_HIGH_MAX    (512 << 20)
+# define CRASH_ADDR_LOW_MAX    (512 << 20)
+# define CRASH_ADDR_HIGH_MAX   (512 << 20)
 #else
-# define CRASH_KERNEL_ADDR_LOW_MAX     (896UL<<20)
-# define CRASH_KERNEL_ADDR_HIGH_MAX    MAXMEM
+# define CRASH_ADDR_LOW_MAX    (896UL << 20)
+# define CRASH_ADDR_HIGH_MAX   MAXMEM
 #endif
 
-static void __init reserve_crashkernel_low(void)
+static int __init reserve_crashkernel_low(void)
 {
 #ifdef CONFIG_X86_64
-       const unsigned long long alignment = 16<<20;    /* 16M */
-       unsigned long long low_base = 0, low_size = 0;
+       unsigned long long base, low_base = 0, low_size = 0;
        unsigned long total_low_mem;
-       unsigned long long base;
-       bool auto_set = false;
        int ret;
 
-       total_low_mem = memblock_mem_size(1UL<<(32-PAGE_SHIFT));
+       total_low_mem = memblock_mem_size(1UL << (32 - PAGE_SHIFT));
+
        /* crashkernel=Y,low */
-       ret = parse_crashkernel_low(boot_command_line, total_low_mem,
-                                               &low_size, &base);
-       if (ret != 0) {
+       ret = parse_crashkernel_low(boot_command_line, total_low_mem, &low_size, &base);
+       if (ret) {
                /*
                 * two parts from lib/swiotlb.c:
                 * -swiotlb size: user-specified with swiotlb= or default.
@@ -517,52 +518,52 @@ static void __init reserve_crashkernel_low(void)
                 * make sure we allocate enough extra low memory so that we
                 * don't run out of DMA buffers for 32-bit devices.
                 */
-               low_size = max(swiotlb_size_or_default() + (8UL<<20), 256UL<<20);
-               auto_set = true;
+               low_size = max(swiotlb_size_or_default() + (8UL << 20), 256UL << 20);
        } else {
                /* passed with crashkernel=0,low ? */
                if (!low_size)
-                       return;
+                       return 0;
        }
 
-       low_base = memblock_find_in_range(low_size, (1ULL<<32),
-                                       low_size, alignment);
-
+       low_base = memblock_find_in_range(low_size, 1ULL << 32, low_size, CRASH_ALIGN);
        if (!low_base) {
-               if (!auto_set)
-                       pr_info("crashkernel low reservation failed - No suitable area found.\n");
+               pr_err("Cannot reserve %ldMB crashkernel low memory, please try smaller size.\n",
+                      (unsigned long)(low_size >> 20));
+               return -ENOMEM;
+       }
 
-               return;
+       ret = memblock_reserve(low_base, low_size);
+       if (ret) {
+               pr_err("%s: Error reserving crashkernel low memblock.\n", __func__);
+               return ret;
        }
 
-       memblock_reserve(low_base, low_size);
        pr_info("Reserving %ldMB of low memory at %ldMB for crashkernel (System low RAM: %ldMB)\n",
-                       (unsigned long)(low_size >> 20),
-                       (unsigned long)(low_base >> 20),
-                       (unsigned long)(total_low_mem >> 20));
+               (unsigned long)(low_size >> 20),
+               (unsigned long)(low_base >> 20),
+               (unsigned long)(total_low_mem >> 20));
+
        crashk_low_res.start = low_base;
        crashk_low_res.end   = low_base + low_size - 1;
        insert_resource(&iomem_resource, &crashk_low_res);
 #endif
+       return 0;
 }
 
 static void __init reserve_crashkernel(void)
 {
-       const unsigned long long alignment = 16<<20;    /* 16M */
-       unsigned long long total_mem;
-       unsigned long long crash_size, crash_base;
+       unsigned long long crash_size, crash_base, total_mem;
        bool high = false;
        int ret;
 
        total_mem = memblock_phys_mem_size();
 
        /* crashkernel=XM */
-       ret = parse_crashkernel(boot_command_line, total_mem,
-                       &crash_size, &crash_base);
+       ret = parse_crashkernel(boot_command_line, total_mem, &crash_size, &crash_base);
        if (ret != 0 || crash_size <= 0) {
                /* crashkernel=X,high */
                ret = parse_crashkernel_high(boot_command_line, total_mem,
-                               &crash_size, &crash_base);
+                                            &crash_size, &crash_base);
                if (ret != 0 || crash_size <= 0)
                        return;
                high = true;
@@ -573,11 +574,10 @@ static void __init reserve_crashkernel(void)
                /*
                 *  kexec want bzImage is below CRASH_KERNEL_ADDR_MAX
                 */
-               crash_base = memblock_find_in_range(alignment,
-                                       high ? CRASH_KERNEL_ADDR_HIGH_MAX :
-                                              CRASH_KERNEL_ADDR_LOW_MAX,
-                                       crash_size, alignment);
-
+               crash_base = memblock_find_in_range(CRASH_ALIGN,
+                                                   high ? CRASH_ADDR_HIGH_MAX
+                                                        : CRASH_ADDR_LOW_MAX,
+                                                   crash_size, CRASH_ALIGN);
                if (!crash_base) {
                        pr_info("crashkernel reservation failed - No suitable area found.\n");
                        return;
@@ -587,26 +587,32 @@ static void __init reserve_crashkernel(void)
                unsigned long long start;
 
                start = memblock_find_in_range(crash_base,
-                                crash_base + crash_size, crash_size, 1<<20);
+                                              crash_base + crash_size,
+                                              crash_size, 1 << 20);
                if (start != crash_base) {
                        pr_info("crashkernel reservation failed - memory is in use.\n");
                        return;
                }
        }
-       memblock_reserve(crash_base, crash_size);
+       ret = memblock_reserve(crash_base, crash_size);
+       if (ret) {
+               pr_err("%s: Error reserving crashkernel memblock.\n", __func__);
+               return;
+       }
+
+       if (crash_base >= (1ULL << 32) && reserve_crashkernel_low()) {
+               memblock_free(crash_base, crash_size);
+               return;
+       }
 
-       printk(KERN_INFO "Reserving %ldMB of memory at %ldMB "
-                       "for crashkernel (System RAM: %ldMB)\n",
-                       (unsigned long)(crash_size >> 20),
-                       (unsigned long)(crash_base >> 20),
-                       (unsigned long)(total_mem >> 20));
+       pr_info("Reserving %ldMB of memory at %ldMB for crashkernel (System RAM: %ldMB)\n",
+               (unsigned long)(crash_size >> 20),
+               (unsigned long)(crash_base >> 20),
+               (unsigned long)(total_mem >> 20));
 
        crashk_res.start = crash_base;
        crashk_res.end   = crash_base + crash_size - 1;
        insert_resource(&iomem_resource, &crashk_res);
-
-       if (crash_base >= (1ULL<<32))
-               reserve_crashkernel_low();
 }
 #else
 static void __init reserve_crashkernel(void)
@@ -1079,8 +1085,10 @@ void __init setup_arch(char **cmdline_p)
        memblock_set_current_limit(ISA_END_ADDRESS);
        memblock_x86_fill();
 
-       if (efi_enabled(EFI_BOOT))
+       if (efi_enabled(EFI_BOOT)) {
+               efi_fake_memmap();
                efi_find_mirror();
+       }
 
        /*
         * The EFI specification says that boot service code won't be called
@@ -1242,6 +1250,8 @@ void __init setup_arch(char **cmdline_p)
        if (efi_enabled(EFI_BOOT))
                efi_apply_memmap_quirks();
 #endif
+
+       microcode_init();
 }
 
 #ifdef CONFIG_X86_32
index da52e6bb5c7f776d489ee0ebda4d3a7d2cc4f11e..b7ffb7c00075787532ceec10b6aed0028ea01b5c 100644 (file)
@@ -63,6 +63,7 @@
 
 int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
 {
+       unsigned long buf_val;
        void __user *buf;
        unsigned int tmpflags;
        unsigned int err = 0;
@@ -107,7 +108,8 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
                regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
                regs->orig_ax = -1;             /* disable syscall checks */
 
-               get_user_ex(buf, &sc->fpstate);
+               get_user_ex(buf_val, &sc->fpstate);
+               buf = (void __user *)buf_val;
        } get_user_catch(err);
 
        err |= fpu__restore_sig(buf, config_enabled(CONFIG_X86_32));
@@ -196,7 +198,7 @@ static unsigned long align_sigframe(unsigned long sp)
        return sp;
 }
 
-static inline void __user *
+static void __user *
 get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
             void __user **fpstate)
 {
@@ -299,7 +301,7 @@ __setup_frame(int sig, struct ksignal *ksig, sigset_t *set,
 
        if (current->mm->context.vdso)
                restorer = current->mm->context.vdso +
-                       selected_vdso32->sym___kernel_sigreturn;
+                       vdso_image_32.sym___kernel_sigreturn;
        else
                restorer = &frame->retcode;
        if (ksig->ka.sa.sa_flags & SA_RESTORER)
@@ -363,7 +365,7 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig,
 
                /* Set up to return from userspace.  */
                restorer = current->mm->context.vdso +
-                       selected_vdso32->sym___kernel_rt_sigreturn;
+                       vdso_image_32.sym___kernel_rt_sigreturn;
                if (ksig->ka.sa.sa_flags & SA_RESTORER)
                        restorer = ksig->ka.sa.sa_restorer;
                put_user_ex(restorer, &frame->pretcode);
index 346eec73f7db408e95eaf3d682ded2844828d739..ade185a46b1da63cea0be1ce47731e301f9d1d5a 100644 (file)
@@ -361,7 +361,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
 
 dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
 {
-       const struct bndcsr *bndcsr;
+       const struct mpx_bndcsr *bndcsr;
        siginfo_t *info;
 
        RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
@@ -384,7 +384,7 @@ dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
         * which is all zeros which indicates MPX was not
         * responsible for the exception.
         */
-       bndcsr = get_xsave_field_ptr(XSTATE_BNDCSR);
+       bndcsr = get_xsave_field_ptr(XFEATURE_MASK_BNDCSR);
        if (!bndcsr)
                goto exit_trap;
 
index c3f7602cd0386b2fb0a1a7437263f9c71f27d03c..c7c4d9c51e99fe582b71ab1f2e3ca9ffaae2226c 100644 (file)
@@ -168,21 +168,20 @@ static void cyc2ns_write_end(int cpu, struct cyc2ns_data *data)
  *              ns = cycles * cyc2ns_scale / SC
  *
  *      And since SC is a constant power of two, we can convert the div
- *  into a shift.
+ *  into a shift. The larger SC is, the more accurate the conversion, but
+ *  cyc2ns_scale needs to be a 32-bit value so that 32-bit multiplication
+ *  (64-bit result) can be used.
  *
- *  We can use khz divisor instead of mhz to keep a better precision, since
- *  cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits.
+ *  We can use khz divisor instead of mhz to keep a better precision.
  *  (mathieu.desnoyers@polymtl.ca)
  *
  *                      -johnstul@us.ibm.com "math is hard, lets go shopping!"
  */
 
-#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
-
 static void cyc2ns_data_init(struct cyc2ns_data *data)
 {
        data->cyc2ns_mul = 0;
-       data->cyc2ns_shift = CYC2NS_SCALE_FACTOR;
+       data->cyc2ns_shift = 0;
        data->cyc2ns_offset = 0;
        data->__count = 0;
 }
@@ -216,14 +215,14 @@ static inline unsigned long long cycles_2_ns(unsigned long long cyc)
 
        if (likely(data == tail)) {
                ns = data->cyc2ns_offset;
-               ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, CYC2NS_SCALE_FACTOR);
+               ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, data->cyc2ns_shift);
        } else {
                data->__count++;
 
                barrier();
 
                ns = data->cyc2ns_offset;
-               ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, CYC2NS_SCALE_FACTOR);
+               ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, data->cyc2ns_shift);
 
                barrier();
 
@@ -257,12 +256,22 @@ static void set_cyc2ns_scale(unsigned long cpu_khz, int cpu)
         * time function is continuous; see the comment near struct
         * cyc2ns_data.
         */
-       data->cyc2ns_mul =
-               DIV_ROUND_CLOSEST(NSEC_PER_MSEC << CYC2NS_SCALE_FACTOR,
-                                 cpu_khz);
-       data->cyc2ns_shift = CYC2NS_SCALE_FACTOR;
+       clocks_calc_mult_shift(&data->cyc2ns_mul, &data->cyc2ns_shift, cpu_khz,
+                              NSEC_PER_MSEC, 0);
+
+       /*
+        * cyc2ns_shift is exported via arch_perf_update_userpage() where it is
+        * not expected to be greater than 31 due to the original published
+        * conversion algorithm shifting a 32-bit value (now specifies a 64-bit
+        * value) - refer perf_event_mmap_page documentation in perf_event.h.
+        */
+       if (data->cyc2ns_shift == 32) {
+               data->cyc2ns_shift = 31;
+               data->cyc2ns_mul >>= 1;
+       }
+
        data->cyc2ns_offset = ns_now -
-               mul_u64_u32_shr(tsc_now, data->cyc2ns_mul, CYC2NS_SCALE_FACTOR);
+               mul_u64_u32_shr(tsc_now, data->cyc2ns_mul, data->cyc2ns_shift);
 
        cyc2ns_write_end(cpu, data);
 
index 2fbea2544f2437bc0ae50ef00288dc320effd81e..156441bcaac82721dea135eb4f548fa8366a2abc 100644 (file)
@@ -30,7 +30,7 @@ static u32 xstate_required_size(u64 xstate_bv, bool compacted)
        int feature_bit = 0;
        u32 ret = XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET;
 
-       xstate_bv &= XSTATE_EXTEND_MASK;
+       xstate_bv &= XFEATURE_MASK_EXTEND;
        while (xstate_bv) {
                if (xstate_bv & 0x1) {
                        u32 eax, ebx, ecx, edx, offset;
@@ -51,7 +51,7 @@ u64 kvm_supported_xcr0(void)
        u64 xcr0 = KVM_SUPPORTED_XCR0 & host_xcr0;
 
        if (!kvm_x86_ops->mpx_supported())
-               xcr0 &= ~(XSTATE_BNDREGS | XSTATE_BNDCSR);
+               xcr0 &= ~(XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR);
 
        return xcr0;
 }
index 9a9a198303219b6430159af03d4d1e1d898ec6f7..bda65690788ebb5ec27bab1b934532cfd1034a25 100644 (file)
@@ -663,9 +663,9 @@ static int __kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr)
        /* Only support XCR_XFEATURE_ENABLED_MASK(xcr0) now  */
        if (index != XCR_XFEATURE_ENABLED_MASK)
                return 1;
-       if (!(xcr0 & XSTATE_FP))
+       if (!(xcr0 & XFEATURE_MASK_FP))
                return 1;
-       if ((xcr0 & XSTATE_YMM) && !(xcr0 & XSTATE_SSE))
+       if ((xcr0 & XFEATURE_MASK_YMM) && !(xcr0 & XFEATURE_MASK_SSE))
                return 1;
 
        /*
@@ -673,23 +673,24 @@ static int __kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr)
         * saving.  However, xcr0 bit 0 is always set, even if the
         * emulated CPU does not support XSAVE (see fx_init).
         */
-       valid_bits = vcpu->arch.guest_supported_xcr0 | XSTATE_FP;
+       valid_bits = vcpu->arch.guest_supported_xcr0 | XFEATURE_MASK_FP;
        if (xcr0 & ~valid_bits)
                return 1;
 
-       if ((!(xcr0 & XSTATE_BNDREGS)) != (!(xcr0 & XSTATE_BNDCSR)))
+       if ((!(xcr0 & XFEATURE_MASK_BNDREGS)) !=
+           (!(xcr0 & XFEATURE_MASK_BNDCSR)))
                return 1;
 
-       if (xcr0 & XSTATE_AVX512) {
-               if (!(xcr0 & XSTATE_YMM))
+       if (xcr0 & XFEATURE_MASK_AVX512) {
+               if (!(xcr0 & XFEATURE_MASK_YMM))
                        return 1;
-               if ((xcr0 & XSTATE_AVX512) != XSTATE_AVX512)
+               if ((xcr0 & XFEATURE_MASK_AVX512) != XFEATURE_MASK_AVX512)
                        return 1;
        }
        kvm_put_guest_xcr0(vcpu);
        vcpu->arch.xcr0 = xcr0;
 
-       if ((xcr0 ^ old_xcr0) & XSTATE_EXTEND_MASK)
+       if ((xcr0 ^ old_xcr0) & XFEATURE_MASK_EXTEND)
                kvm_update_cpuid(vcpu);
        return 0;
 }
@@ -2905,7 +2906,7 @@ static void fill_xsave(u8 *dest, struct kvm_vcpu *vcpu)
         * Copy each region from the possibly compacted offset to the
         * non-compacted offset.
         */
-       valid = xstate_bv & ~XSTATE_FPSSE;
+       valid = xstate_bv & ~XFEATURE_MASK_FPSSE;
        while (valid) {
                u64 feature = valid & -valid;
                int index = fls64(feature) - 1;
@@ -2943,7 +2944,7 @@ static void load_xsave(struct kvm_vcpu *vcpu, u8 *src)
         * Copy each region from the non-compacted offset to the
         * possibly compacted offset.
         */
-       valid = xstate_bv & ~XSTATE_FPSSE;
+       valid = xstate_bv & ~XFEATURE_MASK_FPSSE;
        while (valid) {
                u64 feature = valid & -valid;
                int index = fls64(feature) - 1;
@@ -2971,7 +2972,7 @@ static void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu,
                        &vcpu->arch.guest_fpu.state.fxsave,
                        sizeof(struct fxregs_state));
                *(u64 *)&guest_xsave->region[XSAVE_HDR_OFFSET / sizeof(u32)] =
-                       XSTATE_FPSSE;
+                       XFEATURE_MASK_FPSSE;
        }
 }
 
@@ -2991,7 +2992,7 @@ static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu,
                        return -EINVAL;
                load_xsave(vcpu, (u8 *)guest_xsave->region);
        } else {
-               if (xstate_bv & ~XSTATE_FPSSE)
+               if (xstate_bv & ~XFEATURE_MASK_FPSSE)
                        return -EINVAL;
                memcpy(&vcpu->arch.guest_fpu.state.fxsave,
                        guest_xsave->region, sizeof(struct fxregs_state));
@@ -7005,7 +7006,7 @@ static void fx_init(struct kvm_vcpu *vcpu)
        /*
         * Ensure guest xcr0 is valid for loading
         */
-       vcpu->arch.xcr0 = XSTATE_FP;
+       vcpu->arch.xcr0 = XFEATURE_MASK_FP;
 
        vcpu->arch.cr0 |= X86_CR0_ET;
 }
index 2f822cd886c26cb631d50c38fb933d5898bcc0c0..f2afa5fe48a6dcbd44f5c573cfe9d16f41b8ad16 100644 (file)
@@ -180,9 +180,9 @@ int kvm_mtrr_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata);
 bool kvm_mtrr_check_gfn_range_consistency(struct kvm_vcpu *vcpu, gfn_t gfn,
                                          int page_num);
 
-#define KVM_SUPPORTED_XCR0     (XSTATE_FP | XSTATE_SSE | XSTATE_YMM \
-                               | XSTATE_BNDREGS | XSTATE_BNDCSR \
-                               | XSTATE_AVX512)
+#define KVM_SUPPORTED_XCR0     (XFEATURE_MASK_FP | XFEATURE_MASK_SSE \
+                               | XFEATURE_MASK_YMM | XFEATURE_MASK_BNDREGS \
+                               | XFEATURE_MASK_BNDCSR | XFEATURE_MASK_AVX512)
 extern u64 host_xcr0;
 
 extern u64 kvm_supported_xcr0(void);
index 816488c0b97e3540a59af5c5f4c001fed1f5ba41..d388de72eacaa353ba14e3340a30ef64b63bcd97 100644 (file)
@@ -353,8 +353,12 @@ AVXcode: 1
 17: vmovhps Mq,Vq (v1) | vmovhpd Mq,Vq (66),(v1)
 18: Grp16 (1A)
 19:
-1a: BNDCL Ev,Gv | BNDCU Ev,Gv | BNDMOV Gv,Ev | BNDLDX Gv,Ev,Gv
-1b: BNDCN Ev,Gv | BNDMOV Ev,Gv | BNDMK Gv,Ev | BNDSTX Ev,GV,Gv
+# Intel SDM opcode map does not list MPX instructions. For now using Gv for
+# bnd registers and Ev for everything else is OK because the instruction
+# decoder does not use the information except as an indication that there is
+# a ModR/M byte.
+1a: BNDCL Gv,Ev (F3) | BNDCU Gv,Ev (F2) | BNDMOV Gv,Ev (66) | BNDLDX Gv,Ev
+1b: BNDCN Gv,Ev (F2) | BNDMOV Ev,Gv (66) | BNDMK Gv,Ev (F3) | BNDSTX Ev,Gv
 1c:
 1d:
 1e:
@@ -732,6 +736,12 @@ bd: vfnmadd231ss/d Vx,Hx,Wx (66),(v),(v1)
 be: vfnmsub231ps/d Vx,Hx,Wx (66),(v)
 bf: vfnmsub231ss/d Vx,Hx,Wx (66),(v),(v1)
 # 0x0f 0x38 0xc0-0xff
+c8: sha1nexte Vdq,Wdq
+c9: sha1msg1 Vdq,Wdq
+ca: sha1msg2 Vdq,Wdq
+cb: sha256rnds2 Vdq,Wdq
+cc: sha256msg1 Vdq,Wdq
+cd: sha256msg2 Vdq,Wdq
 db: VAESIMC Vdq,Wdq (66),(v1)
 dc: VAESENC Vdq,Hdq,Wdq (66),(v1)
 dd: VAESENCLAST Vdq,Hdq,Wdq (66),(v1)
@@ -790,6 +800,7 @@ AVXcode: 3
 61: vpcmpestri Vdq,Wdq,Ib (66),(v1)
 62: vpcmpistrm Vdq,Wdq,Ib (66),(v1)
 63: vpcmpistri Vdq,Wdq,Ib (66),(v1)
+cc: sha1rnds4 Vdq,Wdq,Ib
 df: VAESKEYGEN Vdq,Wdq,Ib (66),(v1)
 f0: RORX Gy,Ey,Ib (F2),(v)
 EndTable
@@ -874,7 +885,7 @@ GrpTable: Grp7
 2: LGDT Ms | XGETBV (000),(11B) | XSETBV (001),(11B) | VMFUNC (100),(11B) | XEND (101)(11B) | XTEST (110)(11B)
 3: LIDT Ms
 4: SMSW Mw/Rv
-5:
+5: rdpkru (110),(11B) | wrpkru (111),(11B)
 6: LMSW Ew
 7: INVLPG Mb | SWAPGS (o64),(000),(11B) | RDTSCP (001),(11B)
 EndTable
@@ -888,6 +899,9 @@ EndTable
 
 GrpTable: Grp9
 1: CMPXCHG8B/16B Mq/Mdq
+3: xrstors
+4: xsavec
+5: xsaves
 6: VMPTRLD Mq | VMCLEAR Mq (66) | VMXON Mq (F3) | RDRAND Rv (11B)
 7: VMPTRST Mq | VMPTRST Mq (F3) | RDSEED Rv (11B)
 EndTable
@@ -932,8 +946,8 @@ GrpTable: Grp15
 3: vstmxcsr Md (v1) | WRGSBASE Ry (F3),(11B)
 4: XSAVE
 5: XRSTOR | lfence (11B)
-6: XSAVEOPT | mfence (11B)
-7: clflush | sfence (11B)
+6: XSAVEOPT | clwb (66) | mfence (11B)
+7: clflush | clflushopt (66) | sfence (11B) | pcommit (66),(11B)
 EndTable
 
 GrpTable: Grp16
index dd76a05729b0106c95ae2bd925ca21a647214fd6..024f6e971174e88df9d96ffb66641f7835b8729d 100644 (file)
@@ -169,6 +169,76 @@ void fxch_i(void)
        fpu_tag_word = tag_word;
 }
 
+static void fcmovCC(void)
+{
+       /* fcmovCC st(i) */
+       int i = FPU_rm;
+       FPU_REG *st0_ptr = &st(0);
+       FPU_REG *sti_ptr = &st(i);
+       long tag_word = fpu_tag_word;
+       int regnr = top & 7;
+       int regnri = (top + i) & 7;
+       u_char sti_tag = (tag_word >> (regnri * 2)) & 3;
+
+       if (sti_tag == TAG_Empty) {
+               FPU_stack_underflow();
+               clear_C1();
+               return;
+       }
+       reg_copy(sti_ptr, st0_ptr);
+       tag_word &= ~(3 << (regnr * 2));
+       tag_word |= (sti_tag << (regnr * 2));
+       fpu_tag_word = tag_word;
+}
+
+void fcmovb(void)
+{
+       if (FPU_EFLAGS & X86_EFLAGS_CF)
+               fcmovCC();
+}
+
+void fcmove(void)
+{
+       if (FPU_EFLAGS & X86_EFLAGS_ZF)
+               fcmovCC();
+}
+
+void fcmovbe(void)
+{
+       if (FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF))
+               fcmovCC();
+}
+
+void fcmovu(void)
+{
+       if (FPU_EFLAGS & X86_EFLAGS_PF)
+               fcmovCC();
+}
+
+void fcmovnb(void)
+{
+       if (!(FPU_EFLAGS & X86_EFLAGS_CF))
+               fcmovCC();
+}
+
+void fcmovne(void)
+{
+       if (!(FPU_EFLAGS & X86_EFLAGS_ZF))
+               fcmovCC();
+}
+
+void fcmovnbe(void)
+{
+       if (!(FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF)))
+               fcmovCC();
+}
+
+void fcmovnu(void)
+{
+       if (!(FPU_EFLAGS & X86_EFLAGS_PF))
+               fcmovCC();
+}
+
 void ffree_(void)
 {
        /* ffree st(i) */
index 4dae511c85ad0dead11962f635cb405d13961551..afbc4d805d66f51b5392056a0f83cc42382c60ad 100644 (file)
@@ -71,7 +71,7 @@
 
 #include "fpu_system.h"
 
-#include <asm/sigcontext.h>    /* for struct _fpstate */
+#include <uapi/asm/sigcontext.h>       /* for struct _fpstate */
 #include <asm/math_emu.h>
 #include <linux/linkage.h>
 
index 3d8f2e421466a8af255eba9602748fee8753a377..e945fedf1de2982c5e58aaf4b3e97a28b4e9457e 100644 (file)
 
 #define __BAD__ FPU_illegal    /* Illegal on an 80486, causes SIGILL */
 
-#ifndef NO_UNDOC_CODE          /* Un-documented FPU op-codes supported by default. */
+/* fcmovCC and f(u)comi(p) are enabled if CPUID(1).EDX(15) "cmov" is set */
 
-/* WARNING: These codes are not documented by Intel in their 80486 manual
-   and may not work on FPU clones or later Intel FPUs. */
-
-/* Changes to support the un-doc codes provided by Linus Torvalds. */
-
-#define _d9_d8_ fstp_i         /* unofficial code (19) */
-#define _dc_d0_ fcom_st                /* unofficial code (14) */
-#define _dc_d8_ fcompst                /* unofficial code (1c) */
-#define _dd_c8_ fxch_i         /* unofficial code (0d) */
-#define _de_d0_ fcompst                /* unofficial code (16) */
-#define _df_c0_ ffreep         /* unofficial code (07) ffree + pop */
-#define _df_c8_ fxch_i         /* unofficial code (0f) */
-#define _df_d0_ fstp_i         /* unofficial code (17) */
-#define _df_d8_ fstp_i         /* unofficial code (1f) */
+/* WARNING: "u" entries are not documented by Intel in their 80486 manual
+   and may not work on FPU clones or later Intel FPUs.
+   Changes to support them provided by Linus Torvalds. */
 
 static FUNC const st_instr_table[64] = {
-       fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
-       fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
-       fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
-       fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
-       fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
-       fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
-       fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
-       fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
+/* Opcode:     d8              d9              da              db */
+/*             dc              dd              de              df */
+/* c0..7 */    fadd__,         fld_i_,         fcmovb,         fcmovnb,
+/* c0..7 */    fadd_i,         ffree_,         faddp_,         ffreep,/*u*/
+/* c8..f */    fmul__,         fxch_i,         fcmove,         fcmovne,
+/* c8..f */    fmul_i,         fxch_i,/*u*/    fmulp_,         fxch_i,/*u*/
+/* d0..7 */    fcom_st,        fp_nop,         fcmovbe,        fcmovnbe,
+/* d0..7 */    fcom_st,/*u*/   fst_i_,         fcompst,/*u*/   fstp_i,/*u*/
+/* d8..f */    fcompst,        fstp_i,/*u*/    fcmovu,         fcmovnu,
+/* d8..f */    fcompst,/*u*/   fstp_i,         fcompp,         fstp_i,/*u*/
+/* e0..7 */    fsub__,         FPU_etc,        __BAD__,        finit_,
+/* e0..7 */    fsubri,         fucom_,         fsubrp,         fstsw_,
+/* e8..f */    fsubr_,         fconst,         fucompp,        fucomi_,
+/* e8..f */    fsub_i,         fucomp,         fsubp_,         fucomip,
+/* f0..7 */    fdiv__,         FPU_triga,      __BAD__,        fcomi_,
+/* f0..7 */    fdivri,         __BAD__,        fdivrp,         fcomip,
+/* f8..f */    fdivr_,         FPU_trigb,      __BAD__,        __BAD__,
+/* f8..f */    fdiv_i,         __BAD__,        fdivp_,         __BAD__,
 };
 
-#else /* Support only documented FPU op-codes */
-
-static FUNC const st_instr_table[64] = {
-       fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
-       fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
-       fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
-       fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
-       fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
-       fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
-       fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
-       fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
-};
-
-#endif /* NO_UNDOC_CODE */
-
 #define _NONE_ 0               /* Take no special action */
 #define _REG0_ 1               /* Need to check for not empty st(0) */
 #define _REGI_ 2               /* Need to check for not empty st(0) and st(rm) */
@@ -94,36 +78,18 @@ static FUNC const st_instr_table[64] = {
 #define _REGIc 0               /* Compare st(0) and st(rm) */
 #define _REGIn 0               /* Uses st(0) and st(rm), but handle checks later */
 
-#ifndef NO_UNDOC_CODE
-
-/* Un-documented FPU op-codes supported by default. (see above) */
-
 static u_char const type_table[64] = {
-       _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
-       _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
-       _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
-       _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
-       _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
-       _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
-       _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
-       _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
+/* Opcode:     d8      d9      da      db      dc      dd      de      df */
+/* c0..7 */    _REGI_, _NONE_, _REGIn, _REGIn, _REGIi, _REGi_, _REGIp, _REGi_,
+/* c8..f */    _REGI_, _REGIn, _REGIn, _REGIn, _REGIi, _REGI_, _REGIp, _REGI_,
+/* d0..7 */    _REGIc, _NONE_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_,
+/* d8..f */    _REGIc, _REG0_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_,
+/* e0..7 */    _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
+/* e8..f */    _REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _REGIc,
+/* f0..7 */    _REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _REGIc,
+/* f8..f */    _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
 };
 
-#else /* Support only documented FPU op-codes */
-
-static u_char const type_table[64] = {
-       _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
-       _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
-       _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
-       _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
-       _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
-       _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
-       _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
-       _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
-};
-
-#endif /* NO_UNDOC_CODE */
-
 #ifdef RE_ENTRANT_CHECKING
 u_char emulating = 0;
 #endif /* RE_ENTRANT_CHECKING */
index 9779df436b7d8c70ed8e5249f508c97f098a6c1b..caff438b9c1dac8c82eaf647ccb23fd7c2fbb580 100644 (file)
@@ -46,6 +46,14 @@ extern void fstsw_(void);
 extern void fp_nop(void);
 extern void fld_i_(void);
 extern void fxch_i(void);
+extern void fcmovb(void);
+extern void fcmove(void);
+extern void fcmovbe(void);
+extern void fcmovu(void);
+extern void fcmovnb(void);
+extern void fcmovne(void);
+extern void fcmovnbe(void);
+extern void fcmovnu(void);
 extern void ffree_(void);
 extern void ffreep(void);
 extern void fst_i_(void);
@@ -108,6 +116,10 @@ extern void fcompp(void);
 extern void fucom_(void);
 extern void fucomp(void);
 extern void fucompp(void);
+extern void fcomi_(void);
+extern void fcomip(void);
+extern void fucomi_(void);
+extern void fucomip(void);
 /* reg_constant.c */
 extern void fconst(void);
 /* reg_ld_str.c */
index 2931ff355218041221033379c18aa7b6c4b9458c..95228ff042c09bad7bce04aadc0eb8425a24714b 100644 (file)
 
 #define pop_0()        { FPU_settag0(TAG_Empty); top++; }
 
+/* index is a 5-bit value: (3-bit FPU_modrm.reg field | opcode[2,1]) */
 static u_char const type_table[32] = {
-       _PUSH_, _PUSH_, _PUSH_, _PUSH_,
-       _null_, _null_, _null_, _null_,
-       _REG0_, _REG0_, _REG0_, _REG0_,
-       _REG0_, _REG0_, _REG0_, _REG0_,
+       _PUSH_, _PUSH_, _PUSH_, _PUSH_, /* /0: d9:fld f32,  db:fild m32,  dd:fld f64,  df:fild m16 */
+       _null_, _REG0_, _REG0_, _REG0_, /* /1: d9:undef,    db,dd,df:fisttp m32/64/16 */
+       _REG0_, _REG0_, _REG0_, _REG0_, /* /2: d9:fst f32,  db:fist m32,  dd:fst f64,  df:fist m16 */
+       _REG0_, _REG0_, _REG0_, _REG0_, /* /3: d9:fstp f32, db:fistp m32, dd:fstp f64, df:fistp m16 */
        _NONE_, _null_, _NONE_, _PUSH_,
        _NONE_, _PUSH_, _null_, _PUSH_,
        _NONE_, _null_, _NONE_, _REG0_,
@@ -45,15 +46,19 @@ static u_char const type_table[32] = {
 };
 
 u_char const data_sizes_16[32] = {
-       4, 4, 8, 2, 0, 0, 0, 0,
-       4, 4, 8, 2, 4, 4, 8, 2,
+       4, 4, 8, 2,
+       0, 4, 8, 2, /* /1: d9:undef, db,dd,df:fisttp */
+       4, 4, 8, 2,
+       4, 4, 8, 2,
        14, 0, 94, 10, 2, 10, 0, 8,
        14, 0, 94, 10, 2, 10, 2, 8
 };
 
 static u_char const data_sizes_32[32] = {
-       4, 4, 8, 2, 0, 0, 0, 0,
-       4, 4, 8, 2, 4, 4, 8, 2,
+       4, 4, 8, 2,
+       0, 4, 8, 2, /* /1: d9:undef, db,dd,df:fisttp */
+       4, 4, 8, 2,
+       4, 4, 8, 2,
        28, 0, 108, 10, 2, 10, 0, 8,
        28, 0, 108, 10, 2, 10, 2, 8
 };
@@ -65,6 +70,7 @@ int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
        FPU_REG *st0_ptr;
        u_char st0_tag = TAG_Empty;     /* This is just to stop a gcc warning. */
        u_char loaded_tag;
+       int sv_cw;
 
        st0_ptr = NULL;         /* Initialized just to stop compiler warnings. */
 
@@ -111,7 +117,8 @@ int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
        }
 
        switch (type) {
-       case 000:               /* fld m32real */
+       /* type is a 5-bit value: (3-bit FPU_modrm.reg field | opcode[2,1]) */
+       case 000:               /* fld m32real (d9 /0) */
                clear_C1();
                loaded_tag =
                    FPU_load_single((float __user *)data_address, &loaded_data);
@@ -123,13 +130,13 @@ int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
                }
                FPU_copy_to_reg0(&loaded_data, loaded_tag);
                break;
-       case 001:               /* fild m32int */
+       case 001:               /* fild m32int (db /0) */
                clear_C1();
                loaded_tag =
                    FPU_load_int32((long __user *)data_address, &loaded_data);
                FPU_copy_to_reg0(&loaded_data, loaded_tag);
                break;
-       case 002:               /* fld m64real */
+       case 002:               /* fld m64real (dd /0) */
                clear_C1();
                loaded_tag =
                    FPU_load_double((double __user *)data_address,
@@ -142,12 +149,44 @@ int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
                }
                FPU_copy_to_reg0(&loaded_data, loaded_tag);
                break;
-       case 003:               /* fild m16int */
+       case 003:               /* fild m16int (df /0) */
                clear_C1();
                loaded_tag =
                    FPU_load_int16((short __user *)data_address, &loaded_data);
                FPU_copy_to_reg0(&loaded_data, loaded_tag);
                break;
+       /* case 004: undefined (d9 /1) */
+       /* fisttp are enabled if CPUID(1).ECX(0) "sse3" is set */
+       case 005:               /* fisttp m32int (db /1) */
+               clear_C1();
+               sv_cw = control_word;
+               control_word |= RC_CHOP;
+               if (FPU_store_int32
+                   (st0_ptr, st0_tag, (long __user *)data_address))
+                       pop_0();        /* pop only if the number was actually stored
+                                          (see the 80486 manual p16-28) */
+               control_word = sv_cw;
+               break;
+       case 006:               /* fisttp m64int (dd /1) */
+               clear_C1();
+               sv_cw = control_word;
+               control_word |= RC_CHOP;
+               if (FPU_store_int64
+                   (st0_ptr, st0_tag, (long long __user *)data_address))
+                       pop_0();        /* pop only if the number was actually stored
+                                          (see the 80486 manual p16-28) */
+               control_word = sv_cw;
+               break;
+       case 007:               /* fisttp m16int (df /1) */
+               clear_C1();
+               sv_cw = control_word;
+               control_word |= RC_CHOP;
+               if (FPU_store_int16
+                   (st0_ptr, st0_tag, (short __user *)data_address))
+                       pop_0();        /* pop only if the number was actually stored
+                                          (see the 80486 manual p16-28) */
+               control_word = sv_cw;
+               break;
        case 010:               /* fst m32real */
                clear_C1();
                FPU_store_single(st0_ptr, st0_tag,
index ecce55fc2e2e1b6d59cc6f94891b47d80925c423..b77360fdbf4a8cc8aa191147208d6acb50609d78 100644 (file)
@@ -249,6 +249,54 @@ static int compare_st_st(int nr)
        return 0;
 }
 
+static int compare_i_st_st(int nr)
+{
+       int f, c;
+       FPU_REG *st_ptr;
+
+       if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
+               FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
+               /* Stack fault */
+               EXCEPTION(EX_StackUnder);
+               return !(control_word & CW_Invalid);
+       }
+
+       partial_status &= ~SW_C0;
+       st_ptr = &st(nr);
+       c = compare(st_ptr, FPU_gettagi(nr));
+       if (c & COMP_NaN) {
+               FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
+               EXCEPTION(EX_Invalid);
+               return !(control_word & CW_Invalid);
+       }
+
+       switch (c & 7) {
+       case COMP_A_lt_B:
+               f = X86_EFLAGS_CF;
+               break;
+       case COMP_A_eq_B:
+               f = X86_EFLAGS_ZF;
+               break;
+       case COMP_A_gt_B:
+               f = 0;
+               break;
+       case COMP_No_Comp:
+               f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
+               break;
+#ifdef PARANOID
+       default:
+               EXCEPTION(EX_INTERNAL | 0x122);
+               f = 0;
+               break;
+#endif /* PARANOID */
+       }
+       FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
+       if (c & COMP_Denormal) {
+               return denormal_operand() < 0;
+       }
+       return 0;
+}
+
 static int compare_u_st_st(int nr)
 {
        int f = 0, c;
@@ -299,6 +347,58 @@ static int compare_u_st_st(int nr)
        return 0;
 }
 
+static int compare_ui_st_st(int nr)
+{
+       int f = 0, c;
+       FPU_REG *st_ptr;
+
+       if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
+               FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
+               /* Stack fault */
+               EXCEPTION(EX_StackUnder);
+               return !(control_word & CW_Invalid);
+       }
+
+       partial_status &= ~SW_C0;
+       st_ptr = &st(nr);
+       c = compare(st_ptr, FPU_gettagi(nr));
+       if (c & COMP_NaN) {
+               FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
+               if (c & COMP_SNaN) {    /* This is the only difference between
+                                          un-ordered and ordinary comparisons */
+                       EXCEPTION(EX_Invalid);
+                       return !(control_word & CW_Invalid);
+               }
+               return 0;
+       }
+
+       switch (c & 7) {
+       case COMP_A_lt_B:
+               f = X86_EFLAGS_CF;
+               break;
+       case COMP_A_eq_B:
+               f = X86_EFLAGS_ZF;
+               break;
+       case COMP_A_gt_B:
+               f = 0;
+               break;
+       case COMP_No_Comp:
+               f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
+               break;
+#ifdef PARANOID
+       default:
+               EXCEPTION(EX_INTERNAL | 0x123);
+               f = 0;
+               break;
+#endif /* PARANOID */
+       }
+       FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
+       if (c & COMP_Denormal) {
+               return denormal_operand() < 0;
+       }
+       return 0;
+}
+
 /*---------------------------------------------------------------------------*/
 
 void fcom_st(void)
@@ -348,3 +448,31 @@ void fucompp(void)
        } else
                FPU_illegal();
 }
+
+/* P6+ compare-to-EFLAGS ops */
+
+void fcomi_(void)
+{
+       /* fcomi st(i) */
+       compare_i_st_st(FPU_rm);
+}
+
+void fcomip(void)
+{
+       /* fcomip st(i) */
+       if (!compare_i_st_st(FPU_rm))
+               FPU_pop();
+}
+
+void fucomi_(void)
+{
+       /* fucomi st(i) */
+       compare_ui_st_st(FPU_rm);
+}
+
+void fucomip(void)
+{
+       /* fucomip st(i) */
+       if (!compare_ui_st_st(FPU_rm))
+               FPU_pop();
+}
index a482d105172b68766b8cdfc89717a1623bc24a23..65c47fda26fc5643d67aed5013de8aa8257aac0a 100644 (file)
@@ -14,7 +14,7 @@ obj-$(CONFIG_SMP)             += tlb.o
 obj-$(CONFIG_X86_32)           += pgtable_32.o iomap_32.o
 
 obj-$(CONFIG_HUGETLB_PAGE)     += hugetlbpage.o
-obj-$(CONFIG_X86_PTDUMP)       += dump_pagetables.o
+obj-$(CONFIG_X86_PTDUMP_CORE)  += dump_pagetables.o
 
 obj-$(CONFIG_HIGHMEM)          += highmem_32.o
 
index f0cedf3395af465bc65c3cadc49f02bbc68fad48..1bf417e9cc1309e3353f08fee9dad6915fb34c0c 100644 (file)
@@ -32,6 +32,8 @@ struct pg_state {
        const struct addr_marker *marker;
        unsigned long lines;
        bool to_dmesg;
+       bool check_wx;
+       unsigned long wx_pages;
 };
 
 struct addr_marker {
@@ -155,7 +157,7 @@ static void printk_prot(struct seq_file *m, pgprot_t prot, int level, bool dmsg)
                        pt_dump_cont_printf(m, dmsg, "    ");
                if ((level == 4 && pr & _PAGE_PAT) ||
                    ((level == 3 || level == 2) && pr & _PAGE_PAT_LARGE))
-                       pt_dump_cont_printf(m, dmsg, "pat ");
+                       pt_dump_cont_printf(m, dmsg, "PAT ");
                else
                        pt_dump_cont_printf(m, dmsg, "    ");
                if (pr & _PAGE_GLOBAL)
@@ -198,8 +200,8 @@ static void note_page(struct seq_file *m, struct pg_state *st,
         * we have now. "break" is either changing perms, levels or
         * address space marker.
         */
-       prot = pgprot_val(new_prot) & PTE_FLAGS_MASK;
-       cur = pgprot_val(st->current_prot) & PTE_FLAGS_MASK;
+       prot = pgprot_val(new_prot);
+       cur = pgprot_val(st->current_prot);
 
        if (!st->level) {
                /* First entry */
@@ -214,6 +216,16 @@ static void note_page(struct seq_file *m, struct pg_state *st,
                const char *unit = units;
                unsigned long delta;
                int width = sizeof(unsigned long) * 2;
+               pgprotval_t pr = pgprot_val(st->current_prot);
+
+               if (st->check_wx && (pr & _PAGE_RW) && !(pr & _PAGE_NX)) {
+                       WARN_ONCE(1,
+                                 "x86/mm: Found insecure W+X mapping at address %p/%pS\n",
+                                 (void *)st->start_address,
+                                 (void *)st->start_address);
+                       st->wx_pages += (st->current_address -
+                                        st->start_address) / PAGE_SIZE;
+               }
 
                /*
                 * Now print the actual finished series
@@ -269,13 +281,13 @@ static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr,
 {
        int i;
        pte_t *start;
+       pgprotval_t prot;
 
        start = (pte_t *) pmd_page_vaddr(addr);
        for (i = 0; i < PTRS_PER_PTE; i++) {
-               pgprot_t prot = pte_pgprot(*start);
-
+               prot = pte_flags(*start);
                st->current_address = normalize_addr(P + i * PTE_LEVEL_MULT);
-               note_page(m, st, prot, 4);
+               note_page(m, st, __pgprot(prot), 4);
                start++;
        }
 }
@@ -287,18 +299,19 @@ static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t addr,
 {
        int i;
        pmd_t *start;
+       pgprotval_t prot;
 
        start = (pmd_t *) pud_page_vaddr(addr);
        for (i = 0; i < PTRS_PER_PMD; i++) {
                st->current_address = normalize_addr(P + i * PMD_LEVEL_MULT);
                if (!pmd_none(*start)) {
-                       pgprotval_t prot = pmd_val(*start) & PTE_FLAGS_MASK;
-
-                       if (pmd_large(*start) || !pmd_present(*start))
+                       if (pmd_large(*start) || !pmd_present(*start)) {
+                               prot = pmd_flags(*start);
                                note_page(m, st, __pgprot(prot), 3);
-                       else
+                       } else {
                                walk_pte_level(m, st, *start,
                                               P + i * PMD_LEVEL_MULT);
+                       }
                } else
                        note_page(m, st, __pgprot(0), 3);
                start++;
@@ -318,19 +331,20 @@ static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
 {
        int i;
        pud_t *start;
+       pgprotval_t prot;
 
        start = (pud_t *) pgd_page_vaddr(addr);
 
        for (i = 0; i < PTRS_PER_PUD; i++) {
                st->current_address = normalize_addr(P + i * PUD_LEVEL_MULT);
                if (!pud_none(*start)) {
-                       pgprotval_t prot = pud_val(*start) & PTE_FLAGS_MASK;
-
-                       if (pud_large(*start) || !pud_present(*start))
+                       if (pud_large(*start) || !pud_present(*start)) {
+                               prot = pud_flags(*start);
                                note_page(m, st, __pgprot(prot), 2);
-                       else
+                       } else {
                                walk_pmd_level(m, st, *start,
                                               P + i * PUD_LEVEL_MULT);
+                       }
                } else
                        note_page(m, st, __pgprot(0), 2);
 
@@ -344,13 +358,15 @@ static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
 #define pgd_none(a)  pud_none(__pud(pgd_val(a)))
 #endif
 
-void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
+static void ptdump_walk_pgd_level_core(struct seq_file *m, pgd_t *pgd,
+                                      bool checkwx)
 {
 #ifdef CONFIG_X86_64
        pgd_t *start = (pgd_t *) &init_level4_pgt;
 #else
        pgd_t *start = swapper_pg_dir;
 #endif
+       pgprotval_t prot;
        int i;
        struct pg_state st = {};
 
@@ -359,16 +375,20 @@ void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
                st.to_dmesg = true;
        }
 
+       st.check_wx = checkwx;
+       if (checkwx)
+               st.wx_pages = 0;
+
        for (i = 0; i < PTRS_PER_PGD; i++) {
                st.current_address = normalize_addr(i * PGD_LEVEL_MULT);
                if (!pgd_none(*start)) {
-                       pgprotval_t prot = pgd_val(*start) & PTE_FLAGS_MASK;
-
-                       if (pgd_large(*start) || !pgd_present(*start))
+                       if (pgd_large(*start) || !pgd_present(*start)) {
+                               prot = pgd_flags(*start);
                                note_page(m, &st, __pgprot(prot), 1);
-                       else
+                       } else {
                                walk_pud_level(m, &st, *start,
                                               i * PGD_LEVEL_MULT);
+                       }
                } else
                        note_page(m, &st, __pgprot(0), 1);
 
@@ -378,8 +398,26 @@ void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
        /* Flush out the last page */
        st.current_address = normalize_addr(PTRS_PER_PGD*PGD_LEVEL_MULT);
        note_page(m, &st, __pgprot(0), 0);
+       if (!checkwx)
+               return;
+       if (st.wx_pages)
+               pr_info("x86/mm: Checked W+X mappings: FAILED, %lu W+X pages found.\n",
+                       st.wx_pages);
+       else
+               pr_info("x86/mm: Checked W+X mappings: passed, no W+X pages found.\n");
 }
 
+void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
+{
+       ptdump_walk_pgd_level_core(m, pgd, false);
+}
+
+void ptdump_walk_pgd_level_checkwx(void)
+{
+       ptdump_walk_pgd_level_core(NULL, NULL, true);
+}
+
+#ifdef CONFIG_X86_PTDUMP
 static int ptdump_show(struct seq_file *m, void *v)
 {
        ptdump_walk_pgd_level(m, NULL);
@@ -397,10 +435,13 @@ static const struct file_operations ptdump_fops = {
        .llseek         = seq_lseek,
        .release        = single_release,
 };
+#endif
 
 static int pt_dump_init(void)
 {
+#ifdef CONFIG_X86_PTDUMP
        struct dentry *pe;
+#endif
 
 #ifdef CONFIG_X86_32
        /* Not a compile-time constant on x86-32 */
@@ -412,10 +453,12 @@ static int pt_dump_init(void)
        address_markers[FIXADDR_START_NR].start_address = FIXADDR_START;
 #endif
 
+#ifdef CONFIG_X86_PTDUMP
        pe = debugfs_create_file("kernel_page_tables", 0600, NULL, NULL,
                                 &ptdump_fops);
        if (!pe)
                return -ENOMEM;
+#endif
 
        return 0;
 }
index 81bf3d2af3eb396e6c1703a7b7f71e5af5b4c14d..ae9a37bf13711460892584e67d02880291168f86 100644 (file)
@@ -118,21 +118,20 @@ static noinline int gup_huge_pmd(pmd_t pmd, unsigned long addr,
                unsigned long end, int write, struct page **pages, int *nr)
 {
        unsigned long mask;
-       pte_t pte = *(pte_t *)&pmd;
        struct page *head, *page;
        int refs;
 
        mask = _PAGE_PRESENT|_PAGE_USER;
        if (write)
                mask |= _PAGE_RW;
-       if ((pte_flags(pte) & mask) != mask)
+       if ((pmd_flags(pmd) & mask) != mask)
                return 0;
        /* hugepages are never "special" */
-       VM_BUG_ON(pte_flags(pte) & _PAGE_SPECIAL);
-       VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
+       VM_BUG_ON(pmd_flags(pmd) & _PAGE_SPECIAL);
+       VM_BUG_ON(!pfn_valid(pmd_pfn(pmd)));
 
        refs = 0;
-       head = pte_page(pte);
+       head = pmd_page(pmd);
        page = head + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
        do {
                VM_BUG_ON_PAGE(compound_head(page) != head, page);
@@ -195,21 +194,20 @@ static noinline int gup_huge_pud(pud_t pud, unsigned long addr,
                unsigned long end, int write, struct page **pages, int *nr)
 {
        unsigned long mask;
-       pte_t pte = *(pte_t *)&pud;
        struct page *head, *page;
        int refs;
 
        mask = _PAGE_PRESENT|_PAGE_USER;
        if (write)
                mask |= _PAGE_RW;
-       if ((pte_flags(pte) & mask) != mask)
+       if ((pud_flags(pud) & mask) != mask)
                return 0;
        /* hugepages are never "special" */
-       VM_BUG_ON(pte_flags(pte) & _PAGE_SPECIAL);
-       VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
+       VM_BUG_ON(pud_flags(pud) & _PAGE_SPECIAL);
+       VM_BUG_ON(!pfn_valid(pud_pfn(pud)));
 
        refs = 0;
-       head = pte_page(pte);
+       head = pud_page(pud);
        page = head + ((addr & ~PUD_MASK) >> PAGE_SHIFT);
        do {
                VM_BUG_ON_PAGE(compound_head(page) != head, page);
index 1d8a83df153af10a150baf44cd7fd85adf0bbcf4..1f37cb2b56a9938784ba64abb4db0e51de0c70b9 100644 (file)
@@ -693,14 +693,12 @@ void free_initmem(void)
 #ifdef CONFIG_BLK_DEV_INITRD
 void __init free_initrd_mem(unsigned long start, unsigned long end)
 {
-#ifdef CONFIG_MICROCODE_EARLY
        /*
         * Remember, initrd memory may contain microcode or other useful things.
         * Before we lose initrd mem, we need to find a place to hold them
         * now that normal virtual memory is enabled.
         */
        save_microcode_in_initrd();
-#endif
 
        /*
         * end could be not aligned, and We can not align that,
index 7562f42914b4adb2df9c23a0e85493e8378d0543..cb4ef3de61f9ae9c95249876965a71a5d7b58cf8 100644 (file)
@@ -957,6 +957,8 @@ void mark_rodata_ro(void)
        set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
 #endif
        mark_nxdata_nx();
+       if (__supported_pte_mask & _PAGE_NX)
+               debug_checkwx();
 }
 #endif
 
index df48430c279b8688996b9f0074c08b1ce139af06..5ed62eff31bd5e7fe2e711a4204afbaad60e2437 100644 (file)
@@ -1150,6 +1150,8 @@ void mark_rodata_ro(void)
        free_init_pages("unused kernel",
                        (unsigned long) __va(__pa_symbol(rodata_end)),
                        (unsigned long) __va(__pa_symbol(_sdata)));
+
+       debug_checkwx();
 }
 
 #endif
index 134948b0926f521afe63f9e8c3679835f72cccee..b0ae85f90f10acecc2898e7d19d06ddb6196db57 100644 (file)
@@ -237,7 +237,8 @@ bad_opcode:
  */
 siginfo_t *mpx_generate_siginfo(struct pt_regs *regs)
 {
-       const struct bndreg *bndregs, *bndreg;
+       const struct mpx_bndreg_state *bndregs;
+       const struct mpx_bndreg *bndreg;
        siginfo_t *info = NULL;
        struct insn insn;
        uint8_t bndregno;
@@ -258,13 +259,13 @@ siginfo_t *mpx_generate_siginfo(struct pt_regs *regs)
                goto err_out;
        }
        /* get bndregs field from current task's xsave area */
-       bndregs = get_xsave_field_ptr(XSTATE_BNDREGS);
+       bndregs = get_xsave_field_ptr(XFEATURE_MASK_BNDREGS);
        if (!bndregs) {
                err = -EINVAL;
                goto err_out;
        }
        /* now go select the individual register in the set of 4 */
-       bndreg = &bndregs[bndregno];
+       bndreg = &bndregs->bndreg[bndregno];
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info) {
@@ -306,7 +307,7 @@ err_out:
 
 static __user void *mpx_get_bounds_dir(void)
 {
-       const struct bndcsr *bndcsr;
+       const struct mpx_bndcsr *bndcsr;
 
        if (!cpu_feature_enabled(X86_FEATURE_MPX))
                return MPX_INVALID_BOUNDS_DIR;
@@ -315,7 +316,7 @@ static __user void *mpx_get_bounds_dir(void)
         * The bounds directory pointer is stored in a register
         * only accessible if we first do an xsave.
         */
-       bndcsr = get_xsave_field_ptr(XSTATE_BNDCSR);
+       bndcsr = get_xsave_field_ptr(XFEATURE_MASK_BNDCSR);
        if (!bndcsr)
                return MPX_INVALID_BOUNDS_DIR;
 
@@ -489,10 +490,10 @@ out_unmap:
 static int do_mpx_bt_fault(void)
 {
        unsigned long bd_entry, bd_base;
-       const struct bndcsr *bndcsr;
+       const struct mpx_bndcsr *bndcsr;
        struct mm_struct *mm = current->mm;
 
-       bndcsr = get_xsave_field_ptr(XSTATE_BNDCSR);
+       bndcsr = get_xsave_field_ptr(XFEATURE_MASK_BNDCSR);
        if (!bndcsr)
                return -EINVAL;
        /*
index 2c44c07923012a58e9f430bf4754910984771556..a3137a4feed15ed0d58189411e4e1ae2d9236e8a 100644 (file)
@@ -414,18 +414,28 @@ pmd_t *lookup_pmd_address(unsigned long address)
 phys_addr_t slow_virt_to_phys(void *__virt_addr)
 {
        unsigned long virt_addr = (unsigned long)__virt_addr;
-       phys_addr_t phys_addr;
-       unsigned long offset;
+       unsigned long phys_addr, offset;
        enum pg_level level;
-       unsigned long pmask;
        pte_t *pte;
 
        pte = lookup_address(virt_addr, &level);
        BUG_ON(!pte);
-       pmask = page_level_mask(level);
-       offset = virt_addr & ~pmask;
-       phys_addr = (phys_addr_t)pte_pfn(*pte) << PAGE_SHIFT;
-       return (phys_addr | offset);
+
+       switch (level) {
+       case PG_LEVEL_1G:
+               phys_addr = pud_pfn(*(pud_t *)pte) << PAGE_SHIFT;
+               offset = virt_addr & ~PUD_PAGE_MASK;
+               break;
+       case PG_LEVEL_2M:
+               phys_addr = pmd_pfn(*(pmd_t *)pte) << PAGE_SHIFT;
+               offset = virt_addr & ~PMD_PAGE_MASK;
+               break;
+       default:
+               phys_addr = pte_pfn(*pte) << PAGE_SHIFT;
+               offset = virt_addr & ~PAGE_MASK;
+       }
+
+       return (phys_addr_t)(phys_addr | offset);
 }
 EXPORT_SYMBOL_GPL(slow_virt_to_phys);
 
@@ -458,7 +468,7 @@ static int
 try_preserve_large_page(pte_t *kpte, unsigned long address,
                        struct cpa_data *cpa)
 {
-       unsigned long nextpage_addr, numpages, pmask, psize, addr, pfn;
+       unsigned long nextpage_addr, numpages, pmask, psize, addr, pfn, old_pfn;
        pte_t new_pte, old_pte, *tmp;
        pgprot_t old_prot, new_prot, req_prot;
        int i, do_split = 1;
@@ -478,17 +488,21 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
 
        switch (level) {
        case PG_LEVEL_2M:
-#ifdef CONFIG_X86_64
+               old_prot = pmd_pgprot(*(pmd_t *)kpte);
+               old_pfn = pmd_pfn(*(pmd_t *)kpte);
+               break;
        case PG_LEVEL_1G:
-#endif
-               psize = page_level_size(level);
-               pmask = page_level_mask(level);
+               old_prot = pud_pgprot(*(pud_t *)kpte);
+               old_pfn = pud_pfn(*(pud_t *)kpte);
                break;
        default:
                do_split = -EINVAL;
                goto out_unlock;
        }
 
+       psize = page_level_size(level);
+       pmask = page_level_mask(level);
+
        /*
         * Calculate the number of pages, which fit into this large
         * page starting at address:
@@ -504,7 +518,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
         * up accordingly.
         */
        old_pte = *kpte;
-       old_prot = req_prot = pgprot_large_2_4k(pte_pgprot(old_pte));
+       req_prot = pgprot_large_2_4k(old_prot);
 
        pgprot_val(req_prot) &= ~pgprot_val(cpa->mask_clr);
        pgprot_val(req_prot) |= pgprot_val(cpa->mask_set);
@@ -530,10 +544,10 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
        req_prot = canon_pgprot(req_prot);
 
        /*
-        * old_pte points to the large page base address. So we need
+        * old_pfn points to the large page base pfn. So we need
         * to add the offset of the virtual address:
         */
-       pfn = pte_pfn(old_pte) + ((address & (psize - 1)) >> PAGE_SHIFT);
+       pfn = old_pfn + ((address & (psize - 1)) >> PAGE_SHIFT);
        cpa->pfn = pfn;
 
        new_prot = static_protections(req_prot, address, pfn);
@@ -544,7 +558,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
         * the pages in the range we try to preserve:
         */
        addr = address & pmask;
-       pfn = pte_pfn(old_pte);
+       pfn = old_pfn;
        for (i = 0; i < (psize >> PAGE_SHIFT); i++, addr += PAGE_SIZE, pfn++) {
                pgprot_t chk_prot = static_protections(req_prot, addr, pfn);
 
@@ -574,7 +588,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
                 * The address is aligned and the number of pages
                 * covers the full page.
                 */
-               new_pte = pfn_pte(pte_pfn(old_pte), new_prot);
+               new_pte = pfn_pte(old_pfn, new_prot);
                __set_pmd_pte(kpte, address, new_pte);
                cpa->flags |= CPA_FLUSHTLB;
                do_split = 0;
@@ -591,7 +605,7 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
                   struct page *base)
 {
        pte_t *pbase = (pte_t *)page_address(base);
-       unsigned long pfn, pfninc = 1;
+       unsigned long ref_pfn, pfn, pfninc = 1;
        unsigned int i, level;
        pte_t *tmp;
        pgprot_t ref_prot;
@@ -608,26 +622,33 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
        }
 
        paravirt_alloc_pte(&init_mm, page_to_pfn(base));
-       ref_prot = pte_pgprot(pte_clrhuge(*kpte));
 
-       /* promote PAT bit to correct position */
-       if (level == PG_LEVEL_2M)
+       switch (level) {
+       case PG_LEVEL_2M:
+               ref_prot = pmd_pgprot(*(pmd_t *)kpte);
+               /* clear PSE and promote PAT bit to correct position */
                ref_prot = pgprot_large_2_4k(ref_prot);
+               ref_pfn = pmd_pfn(*(pmd_t *)kpte);
+               break;
 
-#ifdef CONFIG_X86_64
-       if (level == PG_LEVEL_1G) {
+       case PG_LEVEL_1G:
+               ref_prot = pud_pgprot(*(pud_t *)kpte);
+               ref_pfn = pud_pfn(*(pud_t *)kpte);
                pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT;
+
                /*
-                * Set the PSE flags only if the PRESENT flag is set
+                * Clear the PSE flags if the PRESENT flag is not set
                 * otherwise pmd_present/pmd_huge will return true
                 * even on a non present pmd.
                 */
-               if (pgprot_val(ref_prot) & _PAGE_PRESENT)
-                       pgprot_val(ref_prot) |= _PAGE_PSE;
-               else
+               if (!(pgprot_val(ref_prot) & _PAGE_PRESENT))
                        pgprot_val(ref_prot) &= ~_PAGE_PSE;
+               break;
+
+       default:
+               spin_unlock(&pgd_lock);
+               return 1;
        }
-#endif
 
        /*
         * Set the GLOBAL flags only if the PRESENT flag is set
@@ -643,13 +664,16 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
        /*
         * Get the target pfn from the original entry:
         */
-       pfn = pte_pfn(*kpte);
+       pfn = ref_pfn;
        for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc)
                set_pte(&pbase[i], pfn_pte(pfn, canon_pgprot(ref_prot)));
 
-       if (pfn_range_is_mapped(PFN_DOWN(__pa(address)),
-                               PFN_DOWN(__pa(address)) + 1))
-               split_page_count(level);
+       if (virt_addr_valid(address)) {
+               unsigned long pfn = PFN_DOWN(__pa(address));
+
+               if (pfn_range_is_mapped(pfn, pfn + 1))
+                       split_page_count(level);
+       }
 
        /*
         * Install the new, split up pagetable.
index 70efcd0940f9f34b8649872b5b1ac44853a8e5f0..75991979f667f1b9e0e320fa47852969d1066983 100644 (file)
@@ -1109,7 +1109,7 @@ void bpf_int_jit_compile(struct bpf_prog *prog)
                bpf_flush_icache(header, image + proglen);
                set_memory_ro((unsigned long)header, header->pages);
                prog->bpf_func = (void *)image;
-               prog->jited = true;
+               prog->jited = 1;
        }
 out:
        kfree(addrs);
index d7f997f7c26d2502a1a188583d6817c3672417d1..ea48449b2e63d1428de814d654ba58087dee436a 100644 (file)
@@ -50,11 +50,16 @@ void __init efi_bgrt_init(void)
                       bgrt_tab->version);
                return;
        }
-       if (bgrt_tab->status != 1) {
-               pr_err("Ignoring BGRT: invalid status %u (expected 1)\n",
+       if (bgrt_tab->status & 0xfe) {
+               pr_err("Ignoring BGRT: reserved status bits are non-zero %u\n",
                       bgrt_tab->status);
                return;
        }
+       if (bgrt_tab->status != 1) {
+               pr_debug("Ignoring BGRT: invalid status %u (expected 1)\n",
+                        bgrt_tab->status);
+               return;
+       }
        if (bgrt_tab->image_type != 0) {
                pr_err("Ignoring BGRT: invalid image type %u (expected 0)\n",
                       bgrt_tab->image_type);
index 6a28ded74211145a74bfe677f9619f2d2fb676ae..ad285404ea7f58ac74e998d5658a8c72a9be019d 100644 (file)
@@ -194,7 +194,7 @@ static void __init do_add_efi_memmap(void)
 int __init efi_memblock_x86_reserve_range(void)
 {
        struct efi_info *e = &boot_params.efi_info;
-       unsigned long pmap;
+       phys_addr_t pmap;
 
        if (efi_enabled(EFI_PARAVIRT))
                return 0;
@@ -209,7 +209,7 @@ int __init efi_memblock_x86_reserve_range(void)
 #else
        pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32));
 #endif
-       memmap.phys_map         = (void *)pmap;
+       memmap.phys_map         = pmap;
        memmap.nr_map           = e->efi_memmap_size /
                                  e->efi_memdesc_size;
        memmap.desc_size        = e->efi_memdesc_size;
@@ -222,7 +222,7 @@ int __init efi_memblock_x86_reserve_range(void)
        return 0;
 }
 
-static void __init print_efi_memmap(void)
+void __init efi_print_memmap(void)
 {
 #ifdef EFI_DEBUG
        efi_memory_desc_t *md;
@@ -524,7 +524,7 @@ void __init efi_init(void)
                return;
 
        if (efi_enabled(EFI_DBG))
-               print_efi_memmap();
+               efi_print_memmap();
 
        efi_esrt_init();
 }
@@ -1017,24 +1017,6 @@ u32 efi_mem_type(unsigned long phys_addr)
        return 0;
 }
 
-u64 efi_mem_attributes(unsigned long phys_addr)
-{
-       efi_memory_desc_t *md;
-       void *p;
-
-       if (!efi_enabled(EFI_MEMMAP))
-               return 0;
-
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               md = p;
-               if ((md->phys_addr <= phys_addr) &&
-                   (phys_addr < (md->phys_addr +
-                                 (md->num_pages << EFI_PAGE_SHIFT))))
-                       return md->attribute;
-       }
-       return 0;
-}
-
 static int __init arch_parse_efi_cmdline(char *str)
 {
        if (!str) {
@@ -1044,8 +1026,6 @@ static int __init arch_parse_efi_cmdline(char *str)
 
        if (parse_option_str(str, "old_map"))
                set_bit(EFI_OLD_MEMMAP, &efi.flags);
-       if (parse_option_str(str, "debug"))
-               set_bit(EFI_DBG, &efi.flags);
 
        return 0;
 }
index 01d54ea766c16539d001da113320c56ee8eac1f8..1bbc21e2e4aeab9fadaaa1f21856ab0ab9a4cea1 100644 (file)
@@ -61,7 +61,7 @@
 enum intel_mid_timer_options intel_mid_timer_options;
 
 /* intel_mid_ops to store sub arch ops */
-struct intel_mid_ops *intel_mid_ops;
+static struct intel_mid_ops *intel_mid_ops;
 /* getter function for sub arch ops*/
 static void *(*get_intel_mid_ops[])(void) = INTEL_MID_OPS_INIT;
 enum intel_mid_cpu_type __intel_mid_cpu_chip;
index ce992e8cc06526c4e61f1efb1b852e0f41df9ccf..5ee360a951ce7d59c74738491c8711f2f8fc1446 100644 (file)
@@ -197,10 +197,9 @@ static int __init sfi_parse_gpio(struct sfi_table_header *table)
        num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry);
        pentry = (struct sfi_gpio_table_entry *)sb->pentry;
 
-       gpio_table = kmalloc(num * sizeof(*pentry), GFP_KERNEL);
+       gpio_table = kmemdup(pentry, num * sizeof(*pentry), GFP_KERNEL);
        if (!gpio_table)
                return -1;
-       memcpy(gpio_table, pentry, num * sizeof(*pentry));
        gpio_num_entry = num;
 
        pr_debug("GPIO pin info:\n");
index 5c9f63fa6abf24ed575005d7ffb0c3118a728505..327f21c3bde1183ebe1dcc04eded4ba82e88d8af 100644 (file)
@@ -376,38 +376,42 @@ static void uv_nmi_wait(int master)
                atomic_read(&uv_nmi_cpus_in_nmi), num_online_cpus());
 }
 
+/* Dump Instruction Pointer header */
 static void uv_nmi_dump_cpu_ip_hdr(void)
 {
-       printk(KERN_DEFAULT
-               "\nUV: %4s %6s %-32s %s   (Note: PID 0 not listed)\n",
+       pr_info("\nUV: %4s %6s %-32s %s   (Note: PID 0 not listed)\n",
                "CPU", "PID", "COMMAND", "IP");
 }
 
+/* Dump Instruction Pointer info */
 static void uv_nmi_dump_cpu_ip(int cpu, struct pt_regs *regs)
 {
-       printk(KERN_DEFAULT "UV: %4d %6d %-32.32s ",
-               cpu, current->pid, current->comm);
-
+       pr_info("UV: %4d %6d %-32.32s ", cpu, current->pid, current->comm);
        printk_address(regs->ip);
 }
 
-/* Dump this cpu's state */
+/*
+ * Dump this CPU's state.  If action was set to "kdump" and the crash_kexec
+ * failed, then we provide "dump" as an alternate action.  Action "dump" now
+ * also includes the show "ips" (instruction pointers) action whereas the
+ * action "ips" only displays instruction pointers for the non-idle CPU's.
+ * This is an abbreviated form of the "ps" command.
+ */
 static void uv_nmi_dump_state_cpu(int cpu, struct pt_regs *regs)
 {
        const char *dots = " ................................. ";
 
-       if (uv_nmi_action_is("ips")) {
-               if (cpu == 0)
-                       uv_nmi_dump_cpu_ip_hdr();
+       if (cpu == 0)
+               uv_nmi_dump_cpu_ip_hdr();
 
-               if (current->pid != 0)
-                       uv_nmi_dump_cpu_ip(cpu, regs);
+       if (current->pid != 0 || !uv_nmi_action_is("ips"))
+               uv_nmi_dump_cpu_ip(cpu, regs);
 
-       } else if (uv_nmi_action_is("dump")) {
-               printk(KERN_DEFAULT
-                       "UV:%sNMI process trace for CPU %d\n", dots, cpu);
+       if (uv_nmi_action_is("dump")) {
+               pr_info("UV:%sNMI process trace for CPU %d\n", dots, cpu);
                show_regs(regs);
        }
+
        this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_DUMP_DONE);
 }
 
@@ -469,8 +473,7 @@ static void uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master)
                                uv_nmi_trigger_dump(tcpu);
                }
                if (ignored)
-                       printk(KERN_DEFAULT "UV: %d CPUs ignored NMI\n",
-                               ignored);
+                       pr_alert("UV: %d CPUs ignored NMI\n", ignored);
 
                console_loglevel = saved_console_loglevel;
                pr_alert("UV: process trace complete\n");
@@ -492,8 +495,9 @@ static void uv_nmi_touch_watchdogs(void)
        touch_nmi_watchdog();
 }
 
-#if defined(CONFIG_KEXEC_CORE)
 static atomic_t uv_nmi_kexec_failed;
+
+#if defined(CONFIG_KEXEC_CORE)
 static void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs)
 {
        /* Call crash to dump system state */
@@ -502,10 +506,9 @@ static void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs)
                crash_kexec(regs);
 
                pr_emerg("UV: crash_kexec unexpectedly returned, ");
+               atomic_set(&uv_nmi_kexec_failed, 1);
                if (!kexec_crash_image) {
                        pr_cont("crash kernel not loaded\n");
-                       atomic_set(&uv_nmi_kexec_failed, 1);
-                       uv_nmi_sync_exit(1);
                        return;
                }
                pr_cont("kexec busy, stalling cpus while waiting\n");
@@ -514,9 +517,6 @@ static void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs)
        /* If crash exec fails the slaves should return, otherwise stall */
        while (atomic_read(&uv_nmi_kexec_failed) == 0)
                mdelay(10);
-
-       /* Crash kernel most likely not loaded, return in an orderly fashion */
-       uv_nmi_sync_exit(0);
 }
 
 #else /* !CONFIG_KEXEC_CORE */
@@ -524,6 +524,7 @@ static inline void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs)
 {
        if (master)
                pr_err("UV: NMI kdump: KEXEC not supported in this kernel\n");
+       atomic_set(&uv_nmi_kexec_failed, 1);
 }
 #endif /* !CONFIG_KEXEC_CORE */
 
@@ -613,9 +614,14 @@ int uv_handle_nmi(unsigned int reason, struct pt_regs *regs)
        master = (atomic_read(&uv_nmi_cpu) == cpu);
 
        /* If NMI action is "kdump", then attempt to do it */
-       if (uv_nmi_action_is("kdump"))
+       if (uv_nmi_action_is("kdump")) {
                uv_nmi_kdump(cpu, master, regs);
 
+               /* Unexpected return, revert action to "dump" */
+               if (master)
+                       strncpy(uv_nmi_action, "dump", strlen(uv_nmi_action));
+       }
+
        /* Pause as all cpus enter the NMI handler */
        uv_nmi_wait(master);
 
@@ -640,6 +646,7 @@ int uv_handle_nmi(unsigned int reason, struct pt_regs *regs)
                atomic_set(&uv_nmi_cpus_in_nmi, -1);
                atomic_set(&uv_nmi_cpu, -1);
                atomic_set(&uv_in_nmi, 0);
+               atomic_set(&uv_nmi_kexec_failed, 0);
        }
 
        uv_nmi_touch_watchdogs();
index 10fea5fc821e16f450121a550cc00ea214cc4469..df280da348255de9178c0c98cdb1f66b31f13f49 100644 (file)
@@ -1,11 +1,9 @@
 config AMD_MCE_INJ
        tristate "Simple MCE injection interface for AMD processors"
-       depends on RAS && EDAC_DECODE_MCE && DEBUG_FS
+       depends on RAS && EDAC_DECODE_MCE && DEBUG_FS && AMD_NB
        default n
        help
          This is a simple debugfs interface to inject MCEs and test different
          aspects of the MCE handling code.
 
          WARNING: Do not even assume this interface is staying stable!
-
-
index 17e35b5bf7792a95a89472c3dd48c39bd81f0192..55d38cfa46c2626c6d2f85587da27c05ca3e6bf7 100644 (file)
 #include <linux/cpu.h>
 #include <linux/string.h>
 #include <linux/uaccess.h>
+#include <linux/pci.h>
+
 #include <asm/mce.h>
+#include <asm/amd_nb.h>
+#include <asm/irq_vectors.h>
 
 #include "../kernel/cpu/mcheck/mce-internal.h"
 
@@ -30,16 +34,21 @@ static struct dentry *dfs_inj;
 static u8 n_banks;
 
 #define MAX_FLAG_OPT_SIZE      3
+#define NBCFG                  0x44
 
 enum injection_type {
        SW_INJ = 0,     /* SW injection, simply decode the error */
        HW_INJ,         /* Trigger a #MC */
+       DFR_INT_INJ,    /* Trigger Deferred error interrupt */
+       THR_INT_INJ,    /* Trigger threshold interrupt */
        N_INJ_TYPES,
 };
 
 static const char * const flags_options[] = {
        [SW_INJ] = "sw",
        [HW_INJ] = "hw",
+       [DFR_INT_INJ] = "df",
+       [THR_INT_INJ] = "th",
        NULL
 };
 
@@ -129,12 +138,9 @@ static ssize_t flags_write(struct file *filp, const char __user *ubuf,
 {
        char buf[MAX_FLAG_OPT_SIZE], *__buf;
        int err;
-       size_t ret;
 
        if (cnt > MAX_FLAG_OPT_SIZE)
-               cnt = MAX_FLAG_OPT_SIZE;
-
-       ret = cnt;
+               return -EINVAL;
 
        if (copy_from_user(&buf, ubuf, cnt))
                return -EFAULT;
@@ -150,9 +156,9 @@ static ssize_t flags_write(struct file *filp, const char __user *ubuf,
                return err;
        }
 
-       *ppos += ret;
+       *ppos += cnt;
 
-       return ret;
+       return cnt;
 }
 
 static const struct file_operations flags_fops = {
@@ -185,6 +191,55 @@ static void trigger_mce(void *info)
        asm volatile("int $18");
 }
 
+static void trigger_dfr_int(void *info)
+{
+       asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR));
+}
+
+static void trigger_thr_int(void *info)
+{
+       asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR));
+}
+
+static u32 get_nbc_for_node(int node_id)
+{
+       struct cpuinfo_x86 *c = &boot_cpu_data;
+       u32 cores_per_node;
+
+       cores_per_node = c->x86_max_cores / amd_get_nodes_per_socket();
+
+       return cores_per_node * node_id;
+}
+
+static void toggle_nb_mca_mst_cpu(u16 nid)
+{
+       struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
+       u32 val;
+       int err;
+
+       if (!F3)
+               return;
+
+       err = pci_read_config_dword(F3, NBCFG, &val);
+       if (err) {
+               pr_err("%s: Error reading F%dx%03x.\n",
+                      __func__, PCI_FUNC(F3->devfn), NBCFG);
+               return;
+       }
+
+       if (val & BIT(27))
+               return;
+
+       pr_err("%s: Set D18F3x44[NbMcaToMstCpuEn] which BIOS hasn't done.\n",
+              __func__);
+
+       val |= BIT(27);
+       err = pci_write_config_dword(F3, NBCFG, val);
+       if (err)
+               pr_err("%s: Error writing F%dx%03x.\n",
+                      __func__, PCI_FUNC(F3->devfn), NBCFG);
+}
+
 static void do_inject(void)
 {
        u64 mcg_status = 0;
@@ -205,6 +260,26 @@ static void do_inject(void)
        if (!(i_mce.status & MCI_STATUS_PCC))
                mcg_status |= MCG_STATUS_RIPV;
 
+       /*
+        * Ensure necessary status bits for deferred errors:
+        * - MCx_STATUS[Deferred]: make sure it is a deferred error
+        * - MCx_STATUS[UC] cleared: deferred errors are _not_ UC
+        */
+       if (inj_type == DFR_INT_INJ) {
+               i_mce.status |= MCI_STATUS_DEFERRED;
+               i_mce.status |= (i_mce.status & ~MCI_STATUS_UC);
+       }
+
+       /*
+        * For multi node CPUs, logging and reporting of bank 4 errors happens
+        * only on the node base core. Refer to D18F3x44[NbMcaToMstCpuEn] for
+        * Fam10h and later BKDGs.
+        */
+       if (static_cpu_has(X86_FEATURE_AMD_DCM) && b == 4) {
+               toggle_nb_mca_mst_cpu(amd_get_nb_id(cpu));
+               cpu = get_nbc_for_node(amd_get_nb_id(cpu));
+       }
+
        get_online_cpus();
        if (!cpu_online(cpu))
                goto err;
@@ -225,7 +300,16 @@ static void do_inject(void)
 
        toggle_hw_mce_inject(cpu, false);
 
-       smp_call_function_single(cpu, trigger_mce, NULL, 0);
+       switch (inj_type) {
+       case DFR_INT_INJ:
+               smp_call_function_single(cpu, trigger_dfr_int, NULL, 0);
+               break;
+       case THR_INT_INJ:
+               smp_call_function_single(cpu, trigger_thr_int, NULL, 0);
+               break;
+       default:
+               smp_call_function_single(cpu, trigger_mce, NULL, 0);
+       }
 
 err:
        put_online_cpus();
@@ -290,6 +374,11 @@ static const char readme_msg[] =
 "\t    handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
 "\t    is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
 "\t    before injecting.\n"
+"\t  - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n"
+"\t    error APIC interrupt handler to handle the error if the feature is \n"
+"\t    is present in hardware. \n"
+"\t  - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n"
+"\t    APIC interrupt handler to handle the error. \n"
 "\n";
 
 static ssize_t
index 9fe77b7b5a0ed5b77f05174e8ea87f39cb80e2ed..81d6562ce01d5ce52294691e03555c1b4cf3e10f 100644 (file)
@@ -3,6 +3,10 @@
 
 #include <uapi/linux/audit.h>
 
+typedef asmlinkage long (*sys_call_ptr_t)(unsigned long, unsigned long,
+                                         unsigned long, unsigned long,
+                                         unsigned long, unsigned long);
+
 static inline int syscall_get_arch(void)
 {
 #ifdef CONFIG_X86_32
index bd16d6c370ec9aaeb3328779e277de8129ef61f3..439c0994b69689ade4fa49c6a9ead46e25597880 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/sys.h>
 #include <linux/cache.h>
 #include <generated/user_constants.h>
+#include <asm/syscall.h>
 
 #define __NO_STUBS
 
 
 #define old_mmap sys_old_mmap
 
-#define __SYSCALL_I386(nr, sym, compat) extern asmlinkage void sym(void) ;
+#define __SYSCALL_I386(nr, sym, compat) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) ;
 #include <asm/syscalls_32.h>
 
 #undef __SYSCALL_I386
 #define __SYSCALL_I386(nr, sym, compat) [ nr ] = sym,
 
-typedef asmlinkage void (*sys_call_ptr_t)(void);
-
-extern asmlinkage void sys_ni_syscall(void);
+extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
 
 const sys_call_ptr_t sys_call_table[] ____cacheline_aligned = {
        /*
index a75d8700472a4f52db0211217cf7bbcd4f2dc1f5..b74ea6c2c0e7b2fdb43d2f60231edc8c994333c6 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/sys.h>
 #include <linux/cache.h>
 #include <generated/user_constants.h>
+#include <asm/syscall.h>
 
 #define __NO_STUBS
 
 #define __SYSCALL_COMMON(nr, sym, compat) __SYSCALL_64(nr, sym, compat)
 #define __SYSCALL_X32(nr, sym, compat) /* Not supported */
 
-#define __SYSCALL_64(nr, sym, compat) extern asmlinkage void sym(void) ;
+#define __SYSCALL_64(nr, sym, compat) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) ;
 #include <asm/syscalls_64.h>
 
 #undef __SYSCALL_64
 #define __SYSCALL_64(nr, sym, compat) [ nr ] = sym,
 
-typedef void (*sys_call_ptr_t)(void);
-
-extern void sys_ni_syscall(void);
+extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
 
 const sys_call_ptr_t sys_call_table[] ____cacheline_aligned = {
        /*
index 1c30e4ab1022bda71ff80d841509605ae07034cc..63320b6d35bc45f4292d3d5371e529e28a7e837c 100644 (file)
@@ -965,17 +965,8 @@ char * __init xen_auto_xlated_memory_setup(void)
 static void __init fiddle_vdso(void)
 {
 #ifdef CONFIG_X86_32
-       /*
-        * This could be called before selected_vdso32 is initialized, so
-        * just fiddle with both possible images.  vdso_image_32_syscall
-        * can't be selected, since it only exists on 64-bit systems.
-        */
-       u32 *mask;
-       mask = vdso_image_32_int80.data +
-               vdso_image_32_int80.sym_VDSO32_NOTE_MASK;
-       *mask |= 1 << VDSO_NOTE_NONEGSEG_BIT;
-       mask = vdso_image_32_sysenter.data +
-               vdso_image_32_sysenter.sym_VDSO32_NOTE_MASK;
+       u32 *mask = vdso_image_32.data +
+               vdso_image_32.sym_VDSO32_NOTE_MASK;
        *mask |= 1 << VDSO_NOTE_NONEGSEG_BIT;
 #endif
 }
index 93795d04730387c5207a54807ecb6fe0fc571c50..fd8017ce298afcc54aedae0763e36d71377a02b5 100644 (file)
@@ -47,7 +47,7 @@
  *
  * Atomically reads the value of @v.
  */
-#define atomic_read(v)         ACCESS_ONCE((v)->counter)
+#define atomic_read(v)         READ_ONCE((v)->counter)
 
 /**
  * atomic_set - set atomic variable
@@ -56,7 +56,7 @@
  *
  * Atomically sets the value of @v to @i.
  */
-#define atomic_set(v,i)                ((v)->counter = (i))
+#define atomic_set(v,i)                WRITE_ONCE((v)->counter, (i))
 
 #if XCHAL_HAVE_S32C1I
 #define ATOMIC_OP(op)                                                  \
index bd40292e5009675ef17d6914b67010834cf1beb6..9ebf65379556a0f5730b3934acd2fa78e232e816 100644 (file)
@@ -26,13 +26,6 @@ static void bio_batch_end_io(struct bio *bio)
        bio_put(bio);
 }
 
-/*
- * Ensure that max discard sectors doesn't overflow bi_size and hopefully
- * it is of the proper granularity as long as the granularity is a power
- * of two.
- */
-#define MAX_BIO_SECTORS ((1U << 31) >> 9)
-
 /**
  * blkdev_issue_discard - queue a discard
  * @bdev:      blockdev to issue discard for
@@ -50,6 +43,8 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
        DECLARE_COMPLETION_ONSTACK(wait);
        struct request_queue *q = bdev_get_queue(bdev);
        int type = REQ_WRITE | REQ_DISCARD;
+       unsigned int granularity;
+       int alignment;
        struct bio_batch bb;
        struct bio *bio;
        int ret = 0;
@@ -61,6 +56,10 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
        if (!blk_queue_discard(q))
                return -EOPNOTSUPP;
 
+       /* Zero-sector (unknown) and one-sector granularities are the same.  */
+       granularity = max(q->limits.discard_granularity >> 9, 1U);
+       alignment = (bdev_discard_alignment(bdev) >> 9) % granularity;
+
        if (flags & BLKDEV_DISCARD_SECURE) {
                if (!blk_queue_secdiscard(q))
                        return -EOPNOTSUPP;
@@ -74,7 +73,7 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
        blk_start_plug(&plug);
        while (nr_sects) {
                unsigned int req_sects;
-               sector_t end_sect;
+               sector_t end_sect, tmp;
 
                bio = bio_alloc(gfp_mask, 1);
                if (!bio) {
@@ -82,8 +81,22 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
                        break;
                }
 
-               req_sects = min_t(sector_t, nr_sects, MAX_BIO_SECTORS);
+               /* Make sure bi_size doesn't overflow */
+               req_sects = min_t(sector_t, nr_sects, UINT_MAX >> 9);
+
+               /*
+                * If splitting a request, and the next starting sector would be
+                * misaligned, stop the discard at the previous aligned sector.
+                */
                end_sect = sector + req_sects;
+               tmp = end_sect;
+               if (req_sects < nr_sects &&
+                   sector_div(tmp, granularity) != alignment) {
+                       end_sect = end_sect - alignment;
+                       sector_div(end_sect, granularity);
+                       end_sect = end_sect * granularity + alignment;
+                       req_sects = end_sect - sector;
+               }
 
                bio->bi_iter.bi_sector = sector;
                bio->bi_end_io = bio_batch_end_io;
index 48ee3e175dac258ad7ab1b60c2c76d85f3b9a2d9..7240821137fde371f0e32d9ae8ac25b64ab58862 100644 (file)
@@ -348,6 +348,13 @@ config CRYPTO_XTS
          key size 256, 384 or 512 bits. This implementation currently
          can't handle a sectorsize which is not a multiple of 16 bytes.
 
+config CRYPTO_KEYWRAP
+       tristate "Key wrapping support"
+       select CRYPTO_BLKCIPHER
+       help
+         Support for key wrapping (NIST SP800-38F / RFC3394) without
+         padding.
+
 comment "Hash modes"
 
 config CRYPTO_CMAC
@@ -597,17 +604,18 @@ config CRYPTO_SHA1
          SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2).
 
 config CRYPTO_SHA1_SSSE3
-       tristate "SHA1 digest algorithm (SSSE3/AVX/AVX2)"
+       tristate "SHA1 digest algorithm (SSSE3/AVX/AVX2/SHA-NI)"
        depends on X86 && 64BIT
        select CRYPTO_SHA1
        select CRYPTO_HASH
        help
          SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2) implemented
          using Supplemental SSE3 (SSSE3) instructions or Advanced Vector
-         Extensions (AVX/AVX2), when available.
+         Extensions (AVX/AVX2) or SHA-NI(SHA Extensions New Instructions),
+         when available.
 
 config CRYPTO_SHA256_SSSE3
-       tristate "SHA256 digest algorithm (SSSE3/AVX/AVX2)"
+       tristate "SHA256 digest algorithm (SSSE3/AVX/AVX2/SHA-NI)"
        depends on X86 && 64BIT
        select CRYPTO_SHA256
        select CRYPTO_HASH
@@ -615,7 +623,8 @@ config CRYPTO_SHA256_SSSE3
          SHA-256 secure hash standard (DFIPS 180-2) implemented
          using Supplemental SSE3 (SSSE3) instructions, or Advanced Vector
          Extensions version 1 (AVX1), or Advanced Vector Extensions
-         version 2 (AVX2) instructions, when available.
+         version 2 (AVX2) instructions, or SHA-NI (SHA Extensions New
+         Instructions) when available.
 
 config CRYPTO_SHA512_SSSE3
        tristate "SHA512 digest algorithm (SSSE3/AVX/AVX2)"
index e2c59819b236f61303f87989193e26517d5ad3a9..f7aba923458d3895b057ebdda6a4b39f1c385d02 100644 (file)
@@ -31,10 +31,13 @@ obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o
 obj-$(CONFIG_CRYPTO_PCOMP2) += pcompress.o
 obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o
 
-$(obj)/rsakey-asn1.o: $(obj)/rsakey-asn1.c $(obj)/rsakey-asn1.h
-clean-files += rsakey-asn1.c rsakey-asn1.h
+$(obj)/rsapubkey-asn1.o: $(obj)/rsapubkey-asn1.c $(obj)/rsapubkey-asn1.h
+$(obj)/rsaprivkey-asn1.o: $(obj)/rsaprivkey-asn1.c $(obj)/rsaprivkey-asn1.h
+clean-files += rsapubkey-asn1.c rsapubkey-asn1.h
+clean-files += rsaprivkey-asn1.c rsaprivkey-asn1.h
 
-rsa_generic-y := rsakey-asn1.o
+rsa_generic-y := rsapubkey-asn1.o
+rsa_generic-y += rsaprivkey-asn1.o
 rsa_generic-y += rsa.o
 rsa_generic-y += rsa_helper.o
 obj-$(CONFIG_CRYPTO_RSA) += rsa_generic.o
@@ -67,6 +70,7 @@ obj-$(CONFIG_CRYPTO_CTS) += cts.o
 obj-$(CONFIG_CRYPTO_LRW) += lrw.o
 obj-$(CONFIG_CRYPTO_XTS) += xts.o
 obj-$(CONFIG_CRYPTO_CTR) += ctr.o
+obj-$(CONFIG_CRYPTO_KEYWRAP) += keywrap.o
 obj-$(CONFIG_CRYPTO_GCM) += gcm.o
 obj-$(CONFIG_CRYPTO_CCM) += ccm.o
 obj-$(CONFIG_CRYPTO_CHACHA20POLY1305) += chacha20poly1305.o
index b788f169cc9880d0d1ac2ce4930145d9862b306d..b4ffc5be1a93c1ff0bceb6b9a5cc3dc1b5101d97 100644 (file)
@@ -706,7 +706,7 @@ struct crypto_ablkcipher *crypto_alloc_ablkcipher(const char *alg_name,
 err:
                if (err != -EAGAIN)
                        break;
-               if (signal_pending(current)) {
+               if (fatal_signal_pending(current)) {
                        err = -EINTR;
                        break;
                }
index 528ae6aa9bff7403c1f115e274c59862e9b7e3fc..120ec042ec9eaef432c649db2e5f3ecb7869f86e 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/cryptouser.h>
 #include <net/netlink.h>
 #include <crypto/akcipher.h>
-#include <crypto/public_key.h>
 #include "internal.h"
 
 #ifdef CONFIG_NET
index d130b41dbaea244000c35328d7eba7b463158bc8..59bf491fe3d8606acc4016c53182182615d32a21 100644 (file)
@@ -345,7 +345,7 @@ static void crypto_wait_for_test(struct crypto_larval *larval)
                crypto_alg_tested(larval->alg.cra_driver_name, 0);
        }
 
-       err = wait_for_completion_interruptible(&larval->completion);
+       err = wait_for_completion_killable(&larval->completion);
        WARN_ON(err);
 
 out:
index afe4610afc4b952d585ede5ba9cbd732f5aa3750..bbc147cb5dec87affad82510412459c075056f86 100644 (file)
@@ -172,7 +172,7 @@ static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg)
        struct crypto_larval *larval = (void *)alg;
        long timeout;
 
-       timeout = wait_for_completion_interruptible_timeout(
+       timeout = wait_for_completion_killable_timeout(
                &larval->completion, 60 * HZ);
 
        alg = larval->adult;
@@ -445,7 +445,7 @@ struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask)
 err:
                if (err != -EAGAIN)
                        break;
-               if (signal_pending(current)) {
+               if (fatal_signal_pending(current)) {
                        err = -EINTR;
                        break;
                }
@@ -562,7 +562,7 @@ void *crypto_alloc_tfm(const char *alg_name,
 err:
                if (err != -EAGAIN)
                        break;
-               if (signal_pending(current)) {
+               if (fatal_signal_pending(current)) {
                        err = -EINTR;
                        break;
                }
index d20c0b4b880ed1e6dde6552cfce4c6d85a05b053..325575caf6b452ba89ef84985c13d504e956f86e 100644 (file)
@@ -49,11 +49,12 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
        sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm);
 
        ret = -ENOMEM;
-       digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
+       digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size,
+                        GFP_KERNEL);
        if (!digest)
                goto error_no_desc;
 
-       desc = digest + digest_size;
+       desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc));
        desc->tfm   = tfm;
        desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
 
index af71878dc15bfff17dd34200a08be48633ceea9b..3000ea3b6687451df7b5459e01cbf4215f9e9c85 100644 (file)
@@ -546,9 +546,9 @@ int x509_decode_time(time64_t *_t,  size_t hdrlen,
        if (year < 1970 ||
            mon < 1 || mon > 12 ||
            day < 1 || day > mon_len ||
-           hour < 0 || hour > 23 ||
-           min < 0 || min > 59 ||
-           sec < 0 || sec > 59)
+           hour > 23 ||
+           min > 59 ||
+           sec > 59)
                goto invalid_time;
        
        *_t = mktime64(year, mon, day, hour, min, sec);
index 19709663241223968ee73f3d1847be9e253969f7..68c3c40501ab058e7d0d1a6c13fc630d0d542c9d 100644 (file)
@@ -194,14 +194,15 @@ int x509_get_sig_params(struct x509_certificate *cert)
         * digest storage space.
         */
        ret = -ENOMEM;
-       digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
+       digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size,
+                        GFP_KERNEL);
        if (!digest)
                goto error;
 
        cert->sig.digest = digest;
        cert->sig.digest_size = digest_size;
 
-       desc = digest + digest_size;
+       desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc));
        desc->tfm = tfm;
        desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
 
index d94d99ffe8b9b6cecf2c8348241ea17738e9b457..237f3795cfaaa1f988fadf5b07eefe3c44609091 100644 (file)
@@ -375,7 +375,7 @@ static struct crypto_alg *crypto_user_skcipher_alg(const char *name, u32 type,
                err = PTR_ERR(alg);
                if (err != -EAGAIN)
                        break;
-               if (signal_pending(current)) {
+               if (fatal_signal_pending(current)) {
                        err = -EINTR;
                        break;
                }
index ceea83d13168f648e2ca22f635504992c24ba7f2..597cedd3531c8bb850dfb3fb746ab0f683d927b4 100644 (file)
@@ -98,10 +98,6 @@ void jent_get_nstime(__u64 *out)
         * If random_get_entropy does not return a value (which is possible on,
         * for example, MIPS), invoke __getnstimeofday
         * hoping that there are timers we can work with.
-        *
-        * The list of available timers can be obtained from
-        * /sys/devices/system/clocksource/clocksource0/available_clocksource
-        * and are registered with clocksource_register()
         */
        if ((0 == tmp) &&
           (0 == __getnstimeofday(&ts))) {
diff --git a/crypto/keywrap.c b/crypto/keywrap.c
new file mode 100644 (file)
index 0000000..b1d106c
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * Key Wrapping: RFC3394 / NIST SP800-38F
+ *
+ * Copyright (C) 2015, Stephan Mueller <smueller@chronox.de>
+ *
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU General Public License, in which case the provisions of the GPL2
+ * are required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * 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, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/*
+ * Note for using key wrapping:
+ *
+ *     * The result of the encryption operation is the ciphertext starting
+ *       with the 2nd semiblock. The first semiblock is provided as the IV.
+ *       The IV used to start the encryption operation is the default IV.
+ *
+ *     * The input for the decryption is the first semiblock handed in as an
+ *       IV. The ciphertext is the data starting with the 2nd semiblock. The
+ *       return code of the decryption operation will be EBADMSG in case an
+ *       integrity error occurs.
+ *
+ * To obtain the full result of an encryption as expected by SP800-38F, the
+ * caller must allocate a buffer of plaintext + 8 bytes:
+ *
+ *     unsigned int datalen = ptlen + crypto_skcipher_ivsize(tfm);
+ *     u8 data[datalen];
+ *     u8 *iv = data;
+ *     u8 *pt = data + crypto_skcipher_ivsize(tfm);
+ *             <ensure that pt contains the plaintext of size ptlen>
+ *     sg_init_one(&sg, ptdata, ptlen);
+ *     skcipher_request_set_crypt(req, &sg, &sg, ptlen, iv);
+ *
+ *     ==> After encryption, data now contains full KW result as per SP800-38F.
+ *
+ * In case of decryption, ciphertext now already has the expected length
+ * and must be segmented appropriately:
+ *
+ *     unsigned int datalen = CTLEN;
+ *     u8 data[datalen];
+ *             <ensure that data contains full ciphertext>
+ *     u8 *iv = data;
+ *     u8 *ct = data + crypto_skcipher_ivsize(tfm);
+ *     unsigned int ctlen = datalen - crypto_skcipher_ivsize(tfm);
+ *     sg_init_one(&sg, ctdata, ctlen);
+ *     skcipher_request_set_crypt(req, &sg, &sg, ptlen, iv);
+ *
+ *     ==> After decryption (which hopefully does not return EBADMSG), the ct
+ *     pointer now points to the plaintext of size ctlen.
+ *
+ * Note 2: KWP is not implemented as this would defy in-place operation.
+ *        If somebody wants to wrap non-aligned data, he should simply pad
+ *        the input with zeros to fill it up to the 8 byte boundary.
+ */
+
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/internal/skcipher.h>
+
+struct crypto_kw_ctx {
+       struct crypto_cipher *child;
+};
+
+struct crypto_kw_block {
+#define SEMIBSIZE 8
+       u8 A[SEMIBSIZE];
+       u8 R[SEMIBSIZE];
+};
+
+/* convert 64 bit integer into its string representation */
+static inline void crypto_kw_cpu_to_be64(u64 val, u8 *buf)
+{
+       __be64 *a = (__be64 *)buf;
+
+       *a = cpu_to_be64(val);
+}
+
+/*
+ * Fast forward the SGL to the "end" length minus SEMIBSIZE.
+ * The start in the SGL defined by the fast-forward is returned with
+ * the walk variable
+ */
+static void crypto_kw_scatterlist_ff(struct scatter_walk *walk,
+                                    struct scatterlist *sg,
+                                    unsigned int end)
+{
+       unsigned int skip = 0;
+
+       /* The caller should only operate on full SEMIBLOCKs. */
+       BUG_ON(end < SEMIBSIZE);
+
+       skip = end - SEMIBSIZE;
+       while (sg) {
+               if (sg->length > skip) {
+                       scatterwalk_start(walk, sg);
+                       scatterwalk_advance(walk, skip);
+                       break;
+               } else
+                       skip -= sg->length;
+
+               sg = sg_next(sg);
+       }
+}
+
+static int crypto_kw_decrypt(struct blkcipher_desc *desc,
+                            struct scatterlist *dst, struct scatterlist *src,
+                            unsigned int nbytes)
+{
+       struct crypto_blkcipher *tfm = desc->tfm;
+       struct crypto_kw_ctx *ctx = crypto_blkcipher_ctx(tfm);
+       struct crypto_cipher *child = ctx->child;
+
+       unsigned long alignmask = max_t(unsigned long, SEMIBSIZE,
+                                       crypto_cipher_alignmask(child));
+       unsigned int i;
+
+       u8 blockbuf[sizeof(struct crypto_kw_block) + alignmask];
+       struct crypto_kw_block *block = (struct crypto_kw_block *)
+                                       PTR_ALIGN(blockbuf + 0, alignmask + 1);
+
+       u64 t = 6 * ((nbytes) >> 3);
+       struct scatterlist *lsrc, *ldst;
+       int ret = 0;
+
+       /*
+        * Require at least 2 semiblocks (note, the 3rd semiblock that is
+        * required by SP800-38F is the IV.
+        */
+       if (nbytes < (2 * SEMIBSIZE) || nbytes % SEMIBSIZE)
+               return -EINVAL;
+
+       /* Place the IV into block A */
+       memcpy(block->A, desc->info, SEMIBSIZE);
+
+       /*
+        * src scatterlist is read-only. dst scatterlist is r/w. During the
+        * first loop, lsrc points to src and ldst to dst. For any
+        * subsequent round, the code operates on dst only.
+        */
+       lsrc = src;
+       ldst = dst;
+
+       for (i = 0; i < 6; i++) {
+               u8 tbe_buffer[SEMIBSIZE + alignmask];
+               /* alignment for the crypto_xor and the _to_be64 operation */
+               u8 *tbe = PTR_ALIGN(tbe_buffer + 0, alignmask + 1);
+               unsigned int tmp_nbytes = nbytes;
+               struct scatter_walk src_walk, dst_walk;
+
+               while (tmp_nbytes) {
+                       /* move pointer by tmp_nbytes in the SGL */
+                       crypto_kw_scatterlist_ff(&src_walk, lsrc, tmp_nbytes);
+                       /* get the source block */
+                       scatterwalk_copychunks(block->R, &src_walk, SEMIBSIZE,
+                                              false);
+
+                       /* perform KW operation: get counter as byte string */
+                       crypto_kw_cpu_to_be64(t, tbe);
+                       /* perform KW operation: modify IV with counter */
+                       crypto_xor(block->A, tbe, SEMIBSIZE);
+                       t--;
+                       /* perform KW operation: decrypt block */
+                       crypto_cipher_decrypt_one(child, (u8*)block,
+                                                 (u8*)block);
+
+                       /* move pointer by tmp_nbytes in the SGL */
+                       crypto_kw_scatterlist_ff(&dst_walk, ldst, tmp_nbytes);
+                       /* Copy block->R into place */
+                       scatterwalk_copychunks(block->R, &dst_walk, SEMIBSIZE,
+                                              true);
+
+                       tmp_nbytes -= SEMIBSIZE;
+               }
+
+               /* we now start to operate on the dst SGL only */
+               lsrc = dst;
+               ldst = dst;
+       }
+
+       /* Perform authentication check */
+       if (crypto_memneq("\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6", block->A,
+                         SEMIBSIZE))
+               ret = -EBADMSG;
+
+       memzero_explicit(&block, sizeof(struct crypto_kw_block));
+
+       return ret;
+}
+
+static int crypto_kw_encrypt(struct blkcipher_desc *desc,
+                            struct scatterlist *dst, struct scatterlist *src,
+                            unsigned int nbytes)
+{
+       struct crypto_blkcipher *tfm = desc->tfm;
+       struct crypto_kw_ctx *ctx = crypto_blkcipher_ctx(tfm);
+       struct crypto_cipher *child = ctx->child;
+
+       unsigned long alignmask = max_t(unsigned long, SEMIBSIZE,
+                                       crypto_cipher_alignmask(child));
+       unsigned int i;
+
+       u8 blockbuf[sizeof(struct crypto_kw_block) + alignmask];
+       struct crypto_kw_block *block = (struct crypto_kw_block *)
+                                       PTR_ALIGN(blockbuf + 0, alignmask + 1);
+
+       u64 t = 1;
+       struct scatterlist *lsrc, *ldst;
+
+       /*
+        * Require at least 2 semiblocks (note, the 3rd semiblock that is
+        * required by SP800-38F is the IV that occupies the first semiblock.
+        * This means that the dst memory must be one semiblock larger than src.
+        * Also ensure that the given data is aligned to semiblock.
+        */
+       if (nbytes < (2 * SEMIBSIZE) || nbytes % SEMIBSIZE)
+               return -EINVAL;
+
+       /*
+        * Place the predefined IV into block A -- for encrypt, the caller
+        * does not need to provide an IV, but he needs to fetch the final IV.
+        */
+       memcpy(block->A, "\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6", SEMIBSIZE);
+
+       /*
+        * src scatterlist is read-only. dst scatterlist is r/w. During the
+        * first loop, lsrc points to src and ldst to dst. For any
+        * subsequent round, the code operates on dst only.
+        */
+       lsrc = src;
+       ldst = dst;
+
+       for (i = 0; i < 6; i++) {
+               u8 tbe_buffer[SEMIBSIZE + alignmask];
+               u8 *tbe = PTR_ALIGN(tbe_buffer + 0, alignmask + 1);
+               unsigned int tmp_nbytes = nbytes;
+               struct scatter_walk src_walk, dst_walk;
+
+               scatterwalk_start(&src_walk, lsrc);
+               scatterwalk_start(&dst_walk, ldst);
+
+               while (tmp_nbytes) {
+                       /* get the source block */
+                       scatterwalk_copychunks(block->R, &src_walk, SEMIBSIZE,
+                                              false);
+
+                       /* perform KW operation: encrypt block */
+                       crypto_cipher_encrypt_one(child, (u8 *)block,
+                                                 (u8 *)block);
+                       /* perform KW operation: get counter as byte string */
+                       crypto_kw_cpu_to_be64(t, tbe);
+                       /* perform KW operation: modify IV with counter */
+                       crypto_xor(block->A, tbe, SEMIBSIZE);
+                       t++;
+
+                       /* Copy block->R into place */
+                       scatterwalk_copychunks(block->R, &dst_walk, SEMIBSIZE,
+                                              true);
+
+                       tmp_nbytes -= SEMIBSIZE;
+               }
+
+               /* we now start to operate on the dst SGL only */
+               lsrc = dst;
+               ldst = dst;
+       }
+
+       /* establish the IV for the caller to pick up */
+       memcpy(desc->info, block->A, SEMIBSIZE);
+
+       memzero_explicit(&block, sizeof(struct crypto_kw_block));
+
+       return 0;
+}
+
+static int crypto_kw_setkey(struct crypto_tfm *parent, const u8 *key,
+                           unsigned int keylen)
+{
+       struct crypto_kw_ctx *ctx = crypto_tfm_ctx(parent);
+       struct crypto_cipher *child = ctx->child;
+       int err;
+
+       crypto_cipher_clear_flags(child, CRYPTO_TFM_REQ_MASK);
+       crypto_cipher_set_flags(child, crypto_tfm_get_flags(parent) &
+                                      CRYPTO_TFM_REQ_MASK);
+       err = crypto_cipher_setkey(child, key, keylen);
+       crypto_tfm_set_flags(parent, crypto_cipher_get_flags(child) &
+                                    CRYPTO_TFM_RES_MASK);
+       return err;
+}
+
+static int crypto_kw_init_tfm(struct crypto_tfm *tfm)
+{
+       struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
+       struct crypto_spawn *spawn = crypto_instance_ctx(inst);
+       struct crypto_kw_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct crypto_cipher *cipher;
+
+       cipher = crypto_spawn_cipher(spawn);
+       if (IS_ERR(cipher))
+               return PTR_ERR(cipher);
+
+       ctx->child = cipher;
+       return 0;
+}
+
+static void crypto_kw_exit_tfm(struct crypto_tfm *tfm)
+{
+       struct crypto_kw_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       crypto_free_cipher(ctx->child);
+}
+
+static struct crypto_instance *crypto_kw_alloc(struct rtattr **tb)
+{
+       struct crypto_instance *inst = NULL;
+       struct crypto_alg *alg = NULL;
+       int err;
+
+       err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_BLKCIPHER);
+       if (err)
+               return ERR_PTR(err);
+
+       alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER,
+                                 CRYPTO_ALG_TYPE_MASK);
+       if (IS_ERR(alg))
+               return ERR_CAST(alg);
+
+       inst = ERR_PTR(-EINVAL);
+       /* Section 5.1 requirement for KW */
+       if (alg->cra_blocksize != sizeof(struct crypto_kw_block))
+               goto err;
+
+       inst = crypto_alloc_instance("kw", alg);
+       if (IS_ERR(inst))
+               goto err;
+
+       inst->alg.cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER;
+       inst->alg.cra_priority = alg->cra_priority;
+       inst->alg.cra_blocksize = SEMIBSIZE;
+       inst->alg.cra_alignmask = 0;
+       inst->alg.cra_type = &crypto_blkcipher_type;
+       inst->alg.cra_blkcipher.ivsize = SEMIBSIZE;
+       inst->alg.cra_blkcipher.min_keysize = alg->cra_cipher.cia_min_keysize;
+       inst->alg.cra_blkcipher.max_keysize = alg->cra_cipher.cia_max_keysize;
+
+       inst->alg.cra_ctxsize = sizeof(struct crypto_kw_ctx);
+
+       inst->alg.cra_init = crypto_kw_init_tfm;
+       inst->alg.cra_exit = crypto_kw_exit_tfm;
+
+       inst->alg.cra_blkcipher.setkey = crypto_kw_setkey;
+       inst->alg.cra_blkcipher.encrypt = crypto_kw_encrypt;
+       inst->alg.cra_blkcipher.decrypt = crypto_kw_decrypt;
+
+err:
+       crypto_mod_put(alg);
+       return inst;
+}
+
+static void crypto_kw_free(struct crypto_instance *inst)
+{
+       crypto_drop_spawn(crypto_instance_ctx(inst));
+       kfree(inst);
+}
+
+static struct crypto_template crypto_kw_tmpl = {
+       .name = "kw",
+       .alloc = crypto_kw_alloc,
+       .free = crypto_kw_free,
+       .module = THIS_MODULE,
+};
+
+static int __init crypto_kw_init(void)
+{
+       return crypto_register_template(&crypto_kw_tmpl);
+}
+
+static void __exit crypto_kw_exit(void)
+{
+       crypto_unregister_template(&crypto_kw_tmpl);
+}
+
+module_init(crypto_kw_init);
+module_exit(crypto_kw_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
+MODULE_DESCRIPTION("Key Wrapping (RFC3394 / NIST SP800-38F)");
+MODULE_ALIAS_CRYPTO("kw");
index 466003e1a8cf20b501425ef8049cdc71b3f262bb..1093e041db03b98549a0e7abcd6557683068ba11 100644 (file)
@@ -97,24 +97,21 @@ static int rsa_enc(struct akcipher_request *req)
                goto err_free_c;
        }
 
-       m = mpi_read_raw_data(req->src, req->src_len);
-       if (!m) {
-               ret = -ENOMEM;
+       ret = -ENOMEM;
+       m = mpi_read_raw_from_sgl(req->src, req->src_len);
+       if (!m)
                goto err_free_c;
-       }
 
        ret = _rsa_enc(pkey, c, m);
        if (ret)
                goto err_free_m;
 
-       ret = mpi_read_buffer(c, req->dst, req->dst_len, &req->dst_len, &sign);
+       ret = mpi_write_to_sgl(c, req->dst, &req->dst_len, &sign);
        if (ret)
                goto err_free_m;
 
-       if (sign < 0) {
+       if (sign < 0)
                ret = -EBADMSG;
-               goto err_free_m;
-       }
 
 err_free_m:
        mpi_free(m);
@@ -145,25 +142,21 @@ static int rsa_dec(struct akcipher_request *req)
                goto err_free_m;
        }
 
-       c = mpi_read_raw_data(req->src, req->src_len);
-       if (!c) {
-               ret = -ENOMEM;
+       ret = -ENOMEM;
+       c = mpi_read_raw_from_sgl(req->src, req->src_len);
+       if (!c)
                goto err_free_m;
-       }
 
        ret = _rsa_dec(pkey, m, c);
        if (ret)
                goto err_free_c;
 
-       ret = mpi_read_buffer(m, req->dst, req->dst_len, &req->dst_len, &sign);
+       ret = mpi_write_to_sgl(m, req->dst, &req->dst_len, &sign);
        if (ret)
                goto err_free_c;
 
-       if (sign < 0) {
+       if (sign < 0)
                ret = -EBADMSG;
-               goto err_free_c;
-       }
-
 err_free_c:
        mpi_free(c);
 err_free_m:
@@ -193,24 +186,21 @@ static int rsa_sign(struct akcipher_request *req)
                goto err_free_s;
        }
 
-       m = mpi_read_raw_data(req->src, req->src_len);
-       if (!m) {
-               ret = -ENOMEM;
+       ret = -ENOMEM;
+       m = mpi_read_raw_from_sgl(req->src, req->src_len);
+       if (!m)
                goto err_free_s;
-       }
 
        ret = _rsa_sign(pkey, s, m);
        if (ret)
                goto err_free_m;
 
-       ret = mpi_read_buffer(s, req->dst, req->dst_len, &req->dst_len, &sign);
+       ret = mpi_write_to_sgl(s, req->dst, &req->dst_len, &sign);
        if (ret)
                goto err_free_m;
 
-       if (sign < 0) {
+       if (sign < 0)
                ret = -EBADMSG;
-               goto err_free_m;
-       }
 
 err_free_m:
        mpi_free(m);
@@ -241,7 +231,8 @@ static int rsa_verify(struct akcipher_request *req)
                goto err_free_m;
        }
 
-       s = mpi_read_raw_data(req->src, req->src_len);
+       ret = -ENOMEM;
+       s = mpi_read_raw_from_sgl(req->src, req->src_len);
        if (!s) {
                ret = -ENOMEM;
                goto err_free_m;
@@ -251,14 +242,12 @@ static int rsa_verify(struct akcipher_request *req)
        if (ret)
                goto err_free_s;
 
-       ret = mpi_read_buffer(m, req->dst, req->dst_len, &req->dst_len, &sign);
+       ret = mpi_write_to_sgl(m, req->dst, &req->dst_len, &sign);
        if (ret)
                goto err_free_s;
 
-       if (sign < 0) {
+       if (sign < 0)
                ret = -EBADMSG;
-               goto err_free_s;
-       }
 
 err_free_s:
        mpi_free(s);
@@ -282,13 +271,13 @@ static int rsa_check_key_length(unsigned int len)
        return -EINVAL;
 }
 
-static int rsa_setkey(struct crypto_akcipher *tfm, const void *key,
-                     unsigned int keylen)
+static int rsa_set_pub_key(struct crypto_akcipher *tfm, const void *key,
+                          unsigned int keylen)
 {
        struct rsa_key *pkey = akcipher_tfm_ctx(tfm);
        int ret;
 
-       ret = rsa_parse_key(pkey, key, keylen);
+       ret = rsa_parse_pub_key(pkey, key, keylen);
        if (ret)
                return ret;
 
@@ -299,6 +288,30 @@ static int rsa_setkey(struct crypto_akcipher *tfm, const void *key,
        return ret;
 }
 
+static int rsa_set_priv_key(struct crypto_akcipher *tfm, const void *key,
+                           unsigned int keylen)
+{
+       struct rsa_key *pkey = akcipher_tfm_ctx(tfm);
+       int ret;
+
+       ret = rsa_parse_priv_key(pkey, key, keylen);
+       if (ret)
+               return ret;
+
+       if (rsa_check_key_length(mpi_get_size(pkey->n) << 3)) {
+               rsa_free_key(pkey);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+static int rsa_max_size(struct crypto_akcipher *tfm)
+{
+       struct rsa_key *pkey = akcipher_tfm_ctx(tfm);
+
+       return pkey->n ? mpi_get_size(pkey->n) : -EINVAL;
+}
+
 static void rsa_exit_tfm(struct crypto_akcipher *tfm)
 {
        struct rsa_key *pkey = akcipher_tfm_ctx(tfm);
@@ -311,7 +324,9 @@ static struct akcipher_alg rsa = {
        .decrypt = rsa_dec,
        .sign = rsa_sign,
        .verify = rsa_verify,
-       .setkey = rsa_setkey,
+       .set_priv_key = rsa_set_priv_key,
+       .set_pub_key = rsa_set_pub_key,
+       .max_size = rsa_max_size,
        .exit = rsa_exit_tfm,
        .base = {
                .cra_name = "rsa",
index 8d96ce969b4480601b125a14db850f5edda38ddc..d226f48d090767afb6fc588ac7829b09f9509cf3 100644 (file)
@@ -15,7 +15,8 @@
 #include <linux/err.h>
 #include <linux/fips.h>
 #include <crypto/internal/rsa.h>
-#include "rsakey-asn1.h"
+#include "rsapubkey-asn1.h"
+#include "rsaprivkey-asn1.h"
 
 int rsa_get_n(void *context, size_t hdrlen, unsigned char tag,
              const void *value, size_t vlen)
@@ -94,8 +95,8 @@ void rsa_free_key(struct rsa_key *key)
 EXPORT_SYMBOL_GPL(rsa_free_key);
 
 /**
- * rsa_parse_key() - extracts an rsa key from BER encoded buffer
- *                  and stores it in the provided struct rsa_key
+ * rsa_parse_pub_key() - extracts an rsa public key from BER encoded buffer
+ *                      and stores it in the provided struct rsa_key
  *
  * @rsa_key:   struct rsa_key key representation
  * @key:       key in BER format
@@ -103,13 +104,13 @@ EXPORT_SYMBOL_GPL(rsa_free_key);
  *
  * Return:     0 on success or error code in case of error
  */
-int rsa_parse_key(struct rsa_key *rsa_key, const void *key,
-                 unsigned int key_len)
+int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key,
+                     unsigned int key_len)
 {
        int ret;
 
        free_mpis(rsa_key);
-       ret = asn1_ber_decoder(&rsakey_decoder, rsa_key, key, key_len);
+       ret = asn1_ber_decoder(&rsapubkey_decoder, rsa_key, key, key_len);
        if (ret < 0)
                goto error;
 
@@ -118,4 +119,31 @@ error:
        free_mpis(rsa_key);
        return ret;
 }
-EXPORT_SYMBOL_GPL(rsa_parse_key);
+EXPORT_SYMBOL_GPL(rsa_parse_pub_key);
+
+/**
+ * rsa_parse_pub_key() - extracts an rsa private key from BER encoded buffer
+ *                      and stores it in the provided struct rsa_key
+ *
+ * @rsa_key:   struct rsa_key key representation
+ * @key:       key in BER format
+ * @key_len:   length of key
+ *
+ * Return:     0 on success or error code in case of error
+ */
+int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
+                      unsigned int key_len)
+{
+       int ret;
+
+       free_mpis(rsa_key);
+       ret = asn1_ber_decoder(&rsaprivkey_decoder, rsa_key, key, key_len);
+       if (ret < 0)
+               goto error;
+
+       return 0;
+error:
+       free_mpis(rsa_key);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(rsa_parse_priv_key);
diff --git a/crypto/rsakey.asn1 b/crypto/rsakey.asn1
deleted file mode 100644 (file)
index 3c7b5df..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-RsaKey ::= SEQUENCE {
-       n INTEGER ({ rsa_get_n }),
-       e INTEGER ({ rsa_get_e }),
-       d INTEGER ({ rsa_get_d })
-}
diff --git a/crypto/rsaprivkey.asn1 b/crypto/rsaprivkey.asn1
new file mode 100644 (file)
index 0000000..731aea5
--- /dev/null
@@ -0,0 +1,11 @@
+RsaPrivKey ::= SEQUENCE {
+       version         INTEGER,
+       n               INTEGER ({ rsa_get_n }),
+       e               INTEGER ({ rsa_get_e }),
+       d               INTEGER ({ rsa_get_d }),
+       prime1          INTEGER,
+       prime2          INTEGER,
+       exponent1       INTEGER,
+       exponent2       INTEGER,
+       coefficient     INTEGER
+}
diff --git a/crypto/rsapubkey.asn1 b/crypto/rsapubkey.asn1
new file mode 100644 (file)
index 0000000..725498e
--- /dev/null
@@ -0,0 +1,4 @@
+RsaPubKey ::= SEQUENCE {
+       n INTEGER ({ rsa_get_n }),
+       e INTEGER ({ rsa_get_e })
+}
index dd5fc1bf64476f2e76a652486a8ec93f03836784..7591928be7ca789cfc909b3ea300fb6f2fd2cb6f 100644 (file)
@@ -91,7 +91,7 @@ static void crypto_exit_skcipher_ops_blkcipher(struct crypto_tfm *tfm)
        crypto_free_blkcipher(*ctx);
 }
 
-int crypto_init_skcipher_ops_blkcipher(struct crypto_tfm *tfm)
+static int crypto_init_skcipher_ops_blkcipher(struct crypto_tfm *tfm)
 {
        struct crypto_alg *calg = tfm->__crt_alg;
        struct crypto_skcipher *skcipher = __crypto_skcipher_cast(tfm);
@@ -182,7 +182,7 @@ static void crypto_exit_skcipher_ops_ablkcipher(struct crypto_tfm *tfm)
        crypto_free_ablkcipher(*ctx);
 }
 
-int crypto_init_skcipher_ops_ablkcipher(struct crypto_tfm *tfm)
+static int crypto_init_skcipher_ops_ablkcipher(struct crypto_tfm *tfm)
 {
        struct crypto_alg *calg = tfm->__crt_alg;
        struct crypto_skcipher *skcipher = __crypto_skcipher_cast(tfm);
index 2b00b617daab537dba7657b6fbc569d440db931f..46a4a757d478dc8563e9920ee2ad16736f18faac 100644 (file)
@@ -48,6 +48,8 @@
 #define ENCRYPT 1
 #define DECRYPT 0
 
+#define MAX_DIGEST_SIZE                64
+
 /*
  * return a string with the driver name
  */
@@ -950,7 +952,7 @@ static void test_ahash_speed(const char *algo, unsigned int secs,
        struct tcrypt_result tresult;
        struct ahash_request *req;
        struct crypto_ahash *tfm;
-       static char output[1024];
+       char *output;
        int i, ret;
 
        tfm = crypto_alloc_ahash(algo, 0, 0);
@@ -963,9 +965,9 @@ static void test_ahash_speed(const char *algo, unsigned int secs,
        printk(KERN_INFO "\ntesting speed of async %s (%s)\n", algo,
                        get_driver_name(crypto_ahash, tfm));
 
-       if (crypto_ahash_digestsize(tfm) > sizeof(output)) {
-               pr_err("digestsize(%u) > outputbuffer(%zu)\n",
-                      crypto_ahash_digestsize(tfm), sizeof(output));
+       if (crypto_ahash_digestsize(tfm) > MAX_DIGEST_SIZE) {
+               pr_err("digestsize(%u) > %d\n", crypto_ahash_digestsize(tfm),
+                      MAX_DIGEST_SIZE);
                goto out;
        }
 
@@ -980,6 +982,10 @@ static void test_ahash_speed(const char *algo, unsigned int secs,
        ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
                                   tcrypt_complete, &tresult);
 
+       output = kmalloc(MAX_DIGEST_SIZE, GFP_KERNEL);
+       if (!output)
+               goto out_nomem;
+
        for (i = 0; speed[i].blen != 0; i++) {
                if (speed[i].blen > TVMEMSIZE * PAGE_SIZE) {
                        pr_err("template (%u) too big for tvmem (%lu)\n",
@@ -1006,6 +1012,9 @@ static void test_ahash_speed(const char *algo, unsigned int secs,
                }
        }
 
+       kfree(output);
+
+out_nomem:
        ahash_request_free(req);
 
 out:
index fa18753f5c344de0eba58d9c9c1296255f4178c7..ae8c57fd8bc7f855e4fb73145a1ec72a34540add 100644 (file)
@@ -1034,12 +1034,22 @@ static int __test_skcipher(struct crypto_skcipher *tfm, int enc,
 
                q = data;
                if (memcmp(q, template[i].result, template[i].rlen)) {
-                       pr_err("alg: skcipher%s: Test %d failed on %s for %s\n",
+                       pr_err("alg: skcipher%s: Test %d failed (invalid result) on %s for %s\n",
                               d, j, e, algo);
                        hexdump(q, template[i].rlen);
                        ret = -EINVAL;
                        goto out;
                }
+
+               if (template[i].iv_out &&
+                   memcmp(iv, template[i].iv_out,
+                          crypto_skcipher_ivsize(tfm))) {
+                       pr_err("alg: skcipher%s: Test %d failed (invalid output IV) on %s for %s\n",
+                              d, j, e, algo);
+                       hexdump(iv, crypto_skcipher_ivsize(tfm));
+                       ret = -EINVAL;
+                       goto out;
+               }
        }
 
        j = 0;
@@ -1845,34 +1855,34 @@ static int do_test_rsa(struct crypto_akcipher *tfm,
        struct tcrypt_result result;
        unsigned int out_len_max, out_len = 0;
        int err = -ENOMEM;
+       struct scatterlist src, dst, src_tab[2];
 
        req = akcipher_request_alloc(tfm, GFP_KERNEL);
        if (!req)
                return err;
 
        init_completion(&result.completion);
-       err = crypto_akcipher_setkey(tfm, vecs->key, vecs->key_len);
-       if (err)
-               goto free_req;
 
-       akcipher_request_set_crypt(req, vecs->m, outbuf_enc, vecs->m_size,
-                                  out_len);
-       /* expect this to fail, and update the required buf len */
-       crypto_akcipher_encrypt(req);
-       out_len = req->dst_len;
-       if (!out_len) {
-               err = -EINVAL;
+       if (vecs->public_key_vec)
+               err = crypto_akcipher_set_pub_key(tfm, vecs->key,
+                                                 vecs->key_len);
+       else
+               err = crypto_akcipher_set_priv_key(tfm, vecs->key,
+                                                  vecs->key_len);
+       if (err)
                goto free_req;
-       }
 
-       out_len_max = out_len;
-       err = -ENOMEM;
+       out_len_max = crypto_akcipher_maxsize(tfm);
        outbuf_enc = kzalloc(out_len_max, GFP_KERNEL);
        if (!outbuf_enc)
                goto free_req;
 
-       akcipher_request_set_crypt(req, vecs->m, outbuf_enc, vecs->m_size,
-                                  out_len);
+       sg_init_table(src_tab, 2);
+       sg_set_buf(&src_tab[0], vecs->m, 8);
+       sg_set_buf(&src_tab[1], vecs->m + 8, vecs->m_size - 8);
+       sg_init_one(&dst, outbuf_enc, out_len_max);
+       akcipher_request_set_crypt(req, src_tab, &dst, vecs->m_size,
+                                  out_len_max);
        akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
                                      tcrypt_complete, &result);
 
@@ -1882,13 +1892,13 @@ static int do_test_rsa(struct crypto_akcipher *tfm,
                pr_err("alg: rsa: encrypt test failed. err %d\n", err);
                goto free_all;
        }
-       if (out_len != vecs->c_size) {
+       if (req->dst_len != vecs->c_size) {
                pr_err("alg: rsa: encrypt test failed. Invalid output len\n");
                err = -EINVAL;
                goto free_all;
        }
        /* verify that encrypted message is equal to expected */
-       if (memcmp(vecs->c, outbuf_enc, vecs->c_size)) {
+       if (memcmp(vecs->c, sg_virt(req->dst), vecs->c_size)) {
                pr_err("alg: rsa: encrypt test failed. Invalid output\n");
                err = -EINVAL;
                goto free_all;
@@ -1903,9 +1913,10 @@ static int do_test_rsa(struct crypto_akcipher *tfm,
                err = -ENOMEM;
                goto free_all;
        }
+       sg_init_one(&src, vecs->c, vecs->c_size);
+       sg_init_one(&dst, outbuf_dec, out_len_max);
        init_completion(&result.completion);
-       akcipher_request_set_crypt(req, outbuf_enc, outbuf_dec, vecs->c_size,
-                                  out_len);
+       akcipher_request_set_crypt(req, &src, &dst, vecs->c_size, out_len_max);
 
        /* Run RSA decrypt - m = c^d mod n;*/
        err = wait_async_op(&result, crypto_akcipher_decrypt(req));
@@ -2080,7 +2091,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(md5),ecb(cipher_null))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2096,7 +2106,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha1),cbc(aes))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2110,7 +2119,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha1),cbc(des))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2124,7 +2132,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha1),cbc(des3_ede))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2138,7 +2145,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha1),ecb(cipher_null))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2158,7 +2164,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha224),cbc(des))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2172,7 +2177,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha224),cbc(des3_ede))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2186,7 +2190,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha256),cbc(aes))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2200,7 +2203,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha256),cbc(des))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2214,7 +2216,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha256),cbc(des3_ede))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2228,7 +2229,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha384),cbc(des))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2242,7 +2242,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha384),cbc(des3_ede))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2256,7 +2255,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha512),cbc(aes))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2270,7 +2268,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha512),cbc(des))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -2284,7 +2281,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "authenc(hmac(sha512),cbc(des3_ede))",
                .test = alg_test_aead,
-               .fips_allowed = 1,
                .suite = {
                        .aead = {
                                .enc = {
@@ -3011,7 +3007,6 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "ecb(des)",
                .test = alg_test_skcipher,
-               .fips_allowed = 1,
                .suite = {
                        .cipher = {
                                .enc = {
@@ -3291,6 +3286,22 @@ static const struct alg_test_desc alg_test_descs[] = {
                .alg = "jitterentropy_rng",
                .fips_allowed = 1,
                .test = alg_test_null,
+       }, {
+               .alg = "kw(aes)",
+               .test = alg_test_skcipher,
+               .fips_allowed = 1,
+               .suite = {
+                       .cipher = {
+                               .enc = {
+                                       .vecs = aes_kw_enc_tv_template,
+                                       .count = ARRAY_SIZE(aes_kw_enc_tv_template)
+                               },
+                               .dec = {
+                                       .vecs = aes_kw_dec_tv_template,
+                                       .count = ARRAY_SIZE(aes_kw_dec_tv_template)
+                               }
+                       }
+               }
        }, {
                .alg = "lrw(aes)",
                .test = alg_test_skcipher,
index 64b8a8082645da7ddb69f1dda35f52b4f746a486..da0a8fd765f4ee2574db8ebcc671119e723b2a7a 100644 (file)
@@ -67,6 +67,7 @@ struct hash_testvec {
 struct cipher_testvec {
        char *key;
        char *iv;
+       char *iv_out;
        char *input;
        char *result;
        unsigned short tap[MAX_TAP];
@@ -149,7 +150,8 @@ static struct akcipher_testvec rsa_tv_template[] = {
        {
 #ifndef CONFIG_CRYPTO_FIPS
        .key =
-       "\x30\x81\x88" /* sequence of 136 bytes */
+       "\x30\x81\x9A" /* sequence of 154 bytes */
+       "\x02\x01\x01" /* version - integer of 1 byte */
        "\x02\x41" /* modulus - integer of 65 bytes */
        "\x00\xAA\x36\xAB\xCE\x88\xAC\xFD\xFF\x55\x52\x3C\x7F\xC4\x52\x3F"
        "\x90\xEF\xA0\x0D\xF3\x77\x4A\x25\x9F\x2E\x62\xB4\xC5\xD9\x9C\xB5"
@@ -161,19 +163,25 @@ static struct akcipher_testvec rsa_tv_template[] = {
        "\x0A\x03\x37\x48\x62\x64\x87\x69\x5F\x5F\x30\xBC\x38\xB9\x8B\x44"
        "\xC2\xCD\x2D\xFF\x43\x40\x98\xCD\x20\xD8\xA1\x38\xD0\x90\xBF\x64"
        "\x79\x7C\x3F\xA7\xA2\xCD\xCB\x3C\xD1\xE0\xBD\xBA\x26\x54\xB4\xF9"
-       "\xDF\x8E\x8A\xE5\x9D\x73\x3D\x9F\x33\xB3\x01\x62\x4A\xFD\x1D\x51",
+       "\xDF\x8E\x8A\xE5\x9D\x73\x3D\x9F\x33\xB3\x01\x62\x4A\xFD\x1D\x51"
+       "\x02\x01\x00" /* prime1 - integer of 1 byte */
+       "\x02\x01\x00" /* prime2 - integer of 1 byte */
+       "\x02\x01\x00" /* exponent1 - integer of 1 byte */
+       "\x02\x01\x00" /* exponent2 - integer of 1 byte */
+       "\x02\x01\x00", /* coefficient - integer of 1 byte */
        .m = "\x54\x85\x9b\x34\x2c\x49\xea\x2a",
        .c =
        "\x63\x1c\xcd\x7b\xe1\x7e\xe4\xde\xc9\xa8\x89\xa1\x74\xcb\x3c\x63"
        "\x7d\x24\xec\x83\xc3\x15\xe4\x7f\x73\x05\x34\xd1\xec\x22\xbb\x8a"
        "\x5e\x32\x39\x6d\xc1\x1d\x7d\x50\x3b\x9f\x7a\xad\xf0\x2e\x25\x53"
        "\x9f\x6e\xbd\x4c\x55\x84\x0c\x9b\xcf\x1a\x4b\x51\x1e\x9e\x0c\x06",
-       .key_len = 139,
+       .key_len = 157,
        .m_size = 8,
        .c_size = 64,
        }, {
        .key =
-       "\x30\x82\x01\x0B" /* sequence of 267 bytes */
+       "\x30\x82\x01\x1D" /* sequence of 285 bytes */
+       "\x02\x01\x01" /* version - integer of 1 byte */
        "\x02\x81\x81" /* modulus - integer of 129 bytes */
        "\x00\xBB\xF8\x2F\x09\x06\x82\xCE\x9C\x23\x38\xAC\x2B\x9D\xA8\x71"
        "\xF7\x36\x8D\x07\xEE\xD4\x10\x43\xA4\x40\xD6\xB6\xF0\x74\x54\xF5"
@@ -194,8 +202,13 @@ static struct akcipher_testvec rsa_tv_template[] = {
        "\x44\xE5\x6A\xAF\x68\xC5\x6C\x09\x2C\xD3\x8D\xC3\xBE\xF5\xD2\x0A"
        "\x93\x99\x26\xED\x4F\x74\xA1\x3E\xDD\xFB\xE1\xA1\xCE\xCC\x48\x94"
        "\xAF\x94\x28\xC2\xB7\xB8\x88\x3F\xE4\x46\x3A\x4B\xC8\x5B\x1C\xB3"
-       "\xC1",
-       .key_len = 271,
+       "\xC1"
+       "\x02\x01\x00" /* prime1 - integer of 1 byte */
+       "\x02\x01\x00" /* prime2 - integer of 1 byte */
+       "\x02\x01\x00" /* exponent1 - integer of 1 byte */
+       "\x02\x01\x00" /* exponent2 - integer of 1 byte */
+       "\x02\x01\x00", /* coefficient - integer of 1 byte */
+       .key_len = 289,
        .m = "\x54\x85\x9b\x34\x2c\x49\xea\x2a",
        .c =
        "\x74\x1b\x55\xac\x47\xb5\x08\x0a\x6e\x2b\x2d\xf7\x94\xb8\x8a\x95"
@@ -211,7 +224,8 @@ static struct akcipher_testvec rsa_tv_template[] = {
        }, {
 #endif
        .key =
-       "\x30\x82\x02\x0D" /* sequence of 525 bytes */
+       "\x30\x82\x02\x1F" /* sequence of 543 bytes */
+       "\x02\x01\x01" /* version - integer of 1 byte */
        "\x02\x82\x01\x00" /* modulus - integer of 256 bytes */
        "\xDB\x10\x1A\xC2\xA3\xF1\xDC\xFF\x13\x6B\xED\x44\xDF\xF0\x02\x6D"
        "\x13\xC7\x88\xDA\x70\x6B\x54\xF1\xE8\x27\xDC\xC3\x0F\x99\x6A\xFA"
@@ -246,8 +260,13 @@ static struct akcipher_testvec rsa_tv_template[] = {
        "\x77\xAF\x51\x27\x5B\x5E\x69\xB8\x81\xE6\x11\xC5\x43\x23\x81\x04"
        "\x62\xFF\xE9\x46\xB8\xD8\x44\xDB\xA5\xCC\x31\x54\x34\xCE\x3E\x82"
        "\xD6\xBF\x7A\x0B\x64\x21\x6D\x88\x7E\x5B\x45\x12\x1E\x63\x8D\x49"
-       "\xA7\x1D\xD9\x1E\x06\xCD\xE8\xBA\x2C\x8C\x69\x32\xEA\xBE\x60\x71",
-       .key_len = 529,
+       "\xA7\x1D\xD9\x1E\x06\xCD\xE8\xBA\x2C\x8C\x69\x32\xEA\xBE\x60\x71"
+       "\x02\x01\x00" /* prime1 - integer of 1 byte */
+       "\x02\x01\x00" /* prime2 - integer of 1 byte */
+       "\x02\x01\x00" /* exponent1 - integer of 1 byte */
+       "\x02\x01\x00" /* exponent2 - integer of 1 byte */
+       "\x02\x01\x00", /* coefficient - integer of 1 byte */
+       .key_len = 547,
        .m = "\x54\x85\x9b\x34\x2c\x49\xea\x2a",
        .c =
        "\xb2\x97\x76\xb4\xae\x3e\x38\x3c\x7e\x64\x1f\xcc\xa2\x7f\xf6\xbe"
@@ -23813,6 +23832,46 @@ static struct aead_testvec rfc7539esp_dec_tv_template[] = {
        },
 };
 
+/*
+ * All key wrapping test vectors taken from
+ * http://csrc.nist.gov/groups/STM/cavp/documents/mac/kwtestvectors.zip
+ *
+ * Note: as documented in keywrap.c, the ivout for encryption is the first
+ * semiblock of the ciphertext from the test vector. For decryption, iv is
+ * the first semiblock of the ciphertext.
+ */
+static struct cipher_testvec aes_kw_enc_tv_template[] = {
+       {
+               .key    = "\x75\x75\xda\x3a\x93\x60\x7c\xc2"
+                         "\xbf\xd8\xce\xc7\xaa\xdf\xd9\xa6",
+               .klen   = 16,
+               .input  = "\x42\x13\x6d\x3c\x38\x4a\x3e\xea"
+                         "\xc9\x5a\x06\x6f\xd2\x8f\xed\x3f",
+               .ilen   = 16,
+               .result = "\xf6\x85\x94\x81\x6f\x64\xca\xa3"
+                         "\xf5\x6f\xab\xea\x25\x48\xf5\xfb",
+               .rlen   = 16,
+               .iv_out = "\x03\x1f\x6b\xd7\xe6\x1e\x64\x3d",
+       },
+};
+
+static struct cipher_testvec aes_kw_dec_tv_template[] = {
+       {
+               .key    = "\x80\xaa\x99\x73\x27\xa4\x80\x6b"
+                         "\x6a\x7a\x41\xa5\x2b\x86\xc3\x71"
+                         "\x03\x86\xf9\x32\x78\x6e\xf7\x96"
+                         "\x76\xfa\xfb\x90\xb8\x26\x3c\x5f",
+               .klen   = 32,
+               .input  = "\xd3\x3d\x3d\x97\x7b\xf0\xa9\x15"
+                         "\x59\xf9\x9c\x8a\xcd\x29\x3d\x43",
+               .ilen   = 16,
+               .result = "\x0a\x25\x6b\xa7\x5c\xfa\x03\xaa"
+                         "\xa0\x2b\xa9\x42\x03\xf1\x5b\xaa",
+               .rlen   = 16,
+               .iv     = "\x42\x3c\x96\x0d\x8a\x2a\xc4\xc1",
+       },
+};
+
 /*
  * ANSI X9.31 Continuous Pseudo-Random Number Generator (AES mode)
  * test vectors, taken from Appendix B.2.9 and B.2.10:
index 23981ac1c6c21a895de24f18117aa9c381cee19c..3dd9c462d22afd650b989399ac9c973afc983257 100644 (file)
@@ -157,11 +157,15 @@ static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn)
 
 static void __iomem *ghes_ioremap_pfn_irq(u64 pfn)
 {
-       unsigned long vaddr;
+       unsigned long vaddr, paddr;
+       pgprot_t prot;
 
        vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr);
-       ioremap_page_range(vaddr, vaddr + PAGE_SIZE,
-                          pfn << PAGE_SHIFT, PAGE_KERNEL);
+
+       paddr = pfn << PAGE_SHIFT;
+       prot = arch_apei_get_mem_attribute(paddr);
+
+       ioremap_page_range(vaddr, vaddr + PAGE_SIZE, paddr, prot);
 
        return (void __iomem *)vaddr;
 }
index 38208f2d0e69264f5ad71c66ac65be9719fa70ec..fa4585a6914e912a402ec339d4bd241dd0fadac1 100644 (file)
 #include <linux/acpi.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
+#include <linux/of.h>
 
 enum acpi_irq_model_id acpi_irq_model;
 
+static struct fwnode_handle *acpi_gsi_domain_id;
+
 static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity)
 {
        switch (polarity) {
@@ -45,12 +48,10 @@ static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity)
  */
 int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
 {
-       /*
-        * Only default domain is supported at present, always find
-        * the mapping corresponding to default domain by passing NULL
-        * as irq_domain parameter
-        */
-       *irq = irq_find_mapping(NULL, gsi);
+       struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
+                                                       DOMAIN_BUS_ANY);
+
+       *irq = irq_find_mapping(d, gsi);
        /*
         * *irq == 0 means no mapping, that should
         * be reported as a failure
@@ -72,23 +73,19 @@ EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
 int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
                      int polarity)
 {
-       unsigned int irq;
-       unsigned int irq_type = acpi_gsi_get_irq_type(trigger, polarity);
+       struct irq_fwspec fwspec;
 
-       /*
-        * There is no way at present to look-up the IRQ domain on ACPI,
-        * hence always create mapping referring to the default domain
-        * by passing NULL as irq_domain parameter
-        */
-       irq = irq_create_mapping(NULL, gsi);
-       if (!irq)
+       if (WARN_ON(!acpi_gsi_domain_id)) {
+               pr_warn("GSI: No registered irqchip, giving up\n");
                return -EINVAL;
+       }
 
-       /* Set irq type if specified and different than the current one */
-       if (irq_type != IRQ_TYPE_NONE &&
-               irq_type != irq_get_trigger_type(irq))
-               irq_set_irq_type(irq, irq_type);
-       return irq;
+       fwspec.fwnode = acpi_gsi_domain_id;
+       fwspec.param[0] = gsi;
+       fwspec.param[1] = acpi_gsi_get_irq_type(trigger, polarity);
+       fwspec.param_count = 2;
+
+       return irq_create_fwspec_mapping(&fwspec);
 }
 EXPORT_SYMBOL_GPL(acpi_register_gsi);
 
@@ -98,8 +95,23 @@ EXPORT_SYMBOL_GPL(acpi_register_gsi);
  */
 void acpi_unregister_gsi(u32 gsi)
 {
-       int irq = irq_find_mapping(NULL, gsi);
+       struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
+                                                       DOMAIN_BUS_ANY);
+       int irq = irq_find_mapping(d, gsi);
 
        irq_dispose_mapping(irq);
 }
 EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
+
+/**
+ * acpi_set_irq_model - Setup the GSI irqdomain information
+ * @model: the value assigned to acpi_irq_model
+ * @fwnode: the irq_domain identifier for mapping and looking up
+ *          GSI interrupts
+ */
+void __init acpi_set_irq_model(enum acpi_irq_model_id model,
+                              struct fwnode_handle *fwnode)
+{
+       acpi_irq_model = model;
+       acpi_gsi_domain_id = fwnode;
+}
index 65e65903faa072f5d9f8897bf9052112ad624866..7d00f2994738b45d91b6f3b0231856f35341fcb2 100644 (file)
@@ -112,7 +112,8 @@ static void ia_enque_head_rtn_q (IARTN_Q *que, IARTN_Q * data)
 
 static int ia_enque_rtn_q (IARTN_Q *que, struct desc_tbl_t data) {
    IARTN_Q *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
-   if (!entry) return -1;
+   if (!entry)
+      return -ENOMEM;
    entry->data = data;
    entry->next = NULL;
    if (que->next == NULL) 
@@ -1175,7 +1176,7 @@ static int rx_pkt(struct atm_dev *dev)
         if (!(skb = atm_alloc_charge(vcc, len, GFP_ATOMIC))) {
            if (vcc->vci < 32)
               printk("Drop control packets\n");
-             goto out_free_desc;
+          goto out_free_desc;
         }
        skb_put(skb,len);  
         // pwang_test
index be0eb463912882ddc06b6aeebc443b54f1f84bf7..a641cf3ccad691e76d9665e9defab4ef04e9af4f 100644 (file)
@@ -322,6 +322,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
                        goto probe_failed;
        }
 
+       pinctrl_init_done(dev);
+
        if (dev->pm_domain && dev->pm_domain->sync)
                dev->pm_domain->sync(dev);
 
index 5fb74b43848e21369670b4503fdee13b0d4f255e..0762975927541346de55ca09466fe194f7f1d212 100644 (file)
@@ -42,9 +42,20 @@ int pinctrl_bind_pins(struct device *dev)
                goto cleanup_get;
        }
 
-       ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);
+       dev->pins->init_state = pinctrl_lookup_state(dev->pins->p,
+                                       PINCTRL_STATE_INIT);
+       if (IS_ERR(dev->pins->init_state)) {
+               /* Not supplying this state is perfectly legal */
+               dev_dbg(dev, "no init pinctrl state\n");
+
+               ret = pinctrl_select_state(dev->pins->p,
+                                          dev->pins->default_state);
+       } else {
+               ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);
+       }
+
        if (ret) {
-               dev_dbg(dev, "failed to activate default pinctrl state\n");
+               dev_dbg(dev, "failed to activate initial pinctrl state\n");
                goto cleanup_get;
        }
 
index 134483daac25e6627234a5dac90a60687d4810dd..5df4575b5ba765de181a02701c706a6802d38b13 100644 (file)
@@ -152,7 +152,7 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
 
 /**
  * platform_msi_create_irq_domain - Create a platform MSI interrupt domain
- * @np:                Optional device-tree node of the interrupt controller
+ * @fwnode:            Optional fwnode of the interrupt controller
  * @info:      MSI domain info
  * @parent:    Parent irq domain
  *
@@ -162,7 +162,7 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
  * Returns:
  * A domain pointer or NULL in case of failure.
  */
-struct irq_domain *platform_msi_create_irq_domain(struct device_node *np,
+struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
                                                  struct msi_domain_info *info,
                                                  struct irq_domain *parent)
 {
@@ -173,7 +173,7 @@ struct irq_domain *platform_msi_create_irq_domain(struct device_node *np,
        if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
                platform_msi_update_chip_ops(info);
 
-       domain = msi_create_irq_domain(np, info, parent);
+       domain = msi_create_irq_domain(fwnode, info, parent);
        if (domain)
                domain->bus_token = DOMAIN_BUS_PLATFORM_MSI;
 
index cc557886ab2377a550c1ae529b6ecee23380161f..3250e53473a38957ecd12899612b6acaa5590283 100644 (file)
@@ -59,6 +59,7 @@ struct regmap {
        regmap_lock lock;
        regmap_unlock unlock;
        void *lock_arg; /* This is passed to lock/unlock functions */
+       gfp_t alloc_flags;
 
        struct device *dev; /* Device we do I/O on */
        void *work_buf;     /* Scratch buffer used to format I/O */
@@ -98,6 +99,8 @@ struct regmap {
 
        int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
        int (*reg_write)(void *context, unsigned int reg, unsigned int val);
+       int (*reg_update_bits)(void *context, unsigned int reg,
+                              unsigned int mask, unsigned int val);
 
        bool defer_caching;
 
index 4c55cfbad19e95df8cb67864d78af960c073b4df..3f0a7e262d6982c63dcaf3e81f058b0c471d2880 100644 (file)
@@ -30,7 +30,7 @@ static LIST_HEAD(regmap_debugfs_early_list);
 static DEFINE_MUTEX(regmap_debugfs_early_lock);
 
 /* Calculate the length of a fixed format  */
-static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size)
+static size_t regmap_calc_reg_len(int max_val)
 {
        return snprintf(NULL, 0, "%x", max_val);
 }
@@ -173,8 +173,7 @@ static inline void regmap_calc_tot_len(struct regmap *map,
 {
        /* Calculate the length of a fixed format  */
        if (!map->debugfs_tot_len) {
-               map->debugfs_reg_len = regmap_calc_reg_len(map->max_register,
-                                                          buf, count);
+               map->debugfs_reg_len = regmap_calc_reg_len(map->max_register),
                map->debugfs_val_len = 2 * map->format.val_bytes;
                map->debugfs_tot_len = map->debugfs_reg_len +
                        map->debugfs_val_len + 3;      /* : \n */
@@ -338,6 +337,7 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
        char *buf;
        char *entry;
        int ret;
+       unsigned entry_len;
 
        if (*ppos < 0 || !count)
                return -EINVAL;
@@ -365,18 +365,15 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
        p = 0;
        mutex_lock(&map->cache_lock);
        list_for_each_entry(c, &map->debugfs_off_cache, list) {
-               snprintf(entry, PAGE_SIZE, "%x-%x",
-                        c->base_reg, c->max_reg);
+               entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n",
+                                    c->base_reg, c->max_reg);
                if (p >= *ppos) {
-                       if (buf_pos + 1 + strlen(entry) > count)
+                       if (buf_pos + entry_len > count)
                                break;
-                       snprintf(buf + buf_pos, count - buf_pos,
-                                "%s", entry);
-                       buf_pos += strlen(entry);
-                       buf[buf_pos] = '\n';
-                       buf_pos++;
+                       memcpy(buf + buf_pos, entry, entry_len);
+                       buf_pos += entry_len;
                }
-               p += strlen(entry) + 1;
+               p += entry_len;
        }
        mutex_unlock(&map->cache_lock);
 
@@ -420,7 +417,7 @@ static ssize_t regmap_access_read_file(struct file *file,
                return -ENOMEM;
 
        /* Calculate the length of a fixed format  */
-       reg_len = regmap_calc_reg_len(map->max_register, buf, count);
+       reg_len = regmap_calc_reg_len(map->max_register);
        tot_len = reg_len + 10; /* ': R W V P\n' */
 
        for (i = 0; i <= map->max_register; i += map->reg_stride) {
index 38d1f72d869cf4ceb698067f588f81b96e0102df..8d16db533527362efa638f67b69673607d069327 100644 (file)
@@ -63,6 +63,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
        struct regmap *map = d->map;
        int i, ret;
        u32 reg;
+       u32 unmask_offset;
 
        if (d->chip->runtime_pm) {
                ret = pm_runtime_get_sync(map->dev);
@@ -79,12 +80,28 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
        for (i = 0; i < d->chip->num_regs; i++) {
                reg = d->chip->mask_base +
                        (i * map->reg_stride * d->irq_reg_stride);
-               if (d->chip->mask_invert)
+               if (d->chip->mask_invert) {
                        ret = regmap_update_bits(d->map, reg,
                                         d->mask_buf_def[i], ~d->mask_buf[i]);
-               else
+               } else if (d->chip->unmask_base) {
+                       /* set mask with mask_base register */
+                       ret = regmap_update_bits(d->map, reg,
+                                       d->mask_buf_def[i], ~d->mask_buf[i]);
+                       if (ret < 0)
+                               dev_err(d->map->dev,
+                                       "Failed to sync unmasks in %x\n",
+                                       reg);
+                       unmask_offset = d->chip->unmask_base -
+                                                       d->chip->mask_base;
+                       /* clear mask with unmask_base register */
+                       ret = regmap_update_bits(d->map,
+                                       reg + unmask_offset,
+                                       d->mask_buf_def[i],
+                                       d->mask_buf[i]);
+               } else {
                        ret = regmap_update_bits(d->map, reg,
                                         d->mask_buf_def[i], d->mask_buf[i]);
+               }
                if (ret != 0)
                        dev_err(d->map->dev, "Failed to sync masks in %x\n",
                                reg);
@@ -116,7 +133,11 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
                if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) {
                        reg = d->chip->ack_base +
                                (i * map->reg_stride * d->irq_reg_stride);
-                       ret = regmap_write(map, reg, d->mask_buf[i]);
+                       /* some chips ack by write 0 */
+                       if (d->chip->ack_invert)
+                               ret = regmap_write(map, reg, ~d->mask_buf[i]);
+                       else
+                               ret = regmap_write(map, reg, d->mask_buf[i]);
                        if (ret != 0)
                                dev_err(d->map->dev, "Failed to ack 0x%x: %d\n",
                                        reg, ret);
@@ -339,6 +360,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
        int i;
        int ret = -ENOMEM;
        u32 reg;
+       u32 unmask_offset;
 
        if (chip->num_regs <= 0)
                return -EINVAL;
@@ -420,7 +442,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
                if (chip->mask_invert)
                        ret = regmap_update_bits(map, reg,
                                         d->mask_buf[i], ~d->mask_buf[i]);
-               else
+               else if (d->chip->unmask_base) {
+                       unmask_offset = d->chip->unmask_base -
+                                       d->chip->mask_base;
+                       ret = regmap_update_bits(d->map,
+                                       reg + unmask_offset,
+                                       d->mask_buf[i],
+                                       d->mask_buf[i]);
+               } else
                        ret = regmap_update_bits(map, reg,
                                         d->mask_buf[i], d->mask_buf[i]);
                if (ret != 0) {
@@ -445,7 +474,11 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
                if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) {
                        reg = chip->ack_base +
                                (i * map->reg_stride * d->irq_reg_stride);
-                       ret = regmap_write(map, reg,
+                       if (chip->ack_invert)
+                               ret = regmap_write(map, reg,
+                                       ~(d->status_buf[i] & d->mask_buf[i]));
+                       else
+                               ret = regmap_write(map, reg,
                                        d->status_buf[i] & d->mask_buf[i]);
                        if (ret != 0) {
                                dev_err(map->dev, "Failed to ack 0x%x: %d\n",
index afaf56200674a29517678af763bc9a084659224b..4ac63c0e50c7e6a446847b7a29567834d4a5b073 100644 (file)
@@ -561,6 +561,16 @@ struct regmap *__regmap_init(struct device *dev,
                }
                map->lock_arg = map;
        }
+
+       /*
+        * When we write in fast-paths with regmap_bulk_write() don't allocate
+        * scratch buffers with sleeping allocations.
+        */
+       if ((bus && bus->fast_io) || config->fast_io)
+               map->alloc_flags = GFP_ATOMIC;
+       else
+               map->alloc_flags = GFP_KERNEL;
+
        map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
        map->format.pad_bytes = config->pad_bits / 8;
        map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
@@ -619,6 +629,7 @@ struct regmap *__regmap_init(struct device *dev,
                goto skip_format_initialization;
        } else {
                map->reg_read  = _regmap_bus_read;
+               map->reg_update_bits = bus->reg_update_bits;
        }
 
        reg_endian = regmap_get_reg_endian(bus, config);
@@ -1786,7 +1797,7 @@ out:
                if (!val_count)
                        return -EINVAL;
 
-               wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL);
+               wval = kmemdup(val, val_count * val_bytes, map->alloc_flags);
                if (!wval) {
                        dev_err(map->dev, "Error in memory allocation\n");
                        return -ENOMEM;
@@ -2509,20 +2520,26 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
        int ret;
        unsigned int tmp, orig;
 
-       ret = _regmap_read(map, reg, &orig);
-       if (ret != 0)
-               return ret;
+       if (change)
+               *change = false;
 
-       tmp = orig & ~mask;
-       tmp |= val & mask;
-
-       if (force_write || (tmp != orig)) {
-               ret = _regmap_write(map, reg, tmp);
-               if (change)
+       if (regmap_volatile(map, reg) && map->reg_update_bits) {
+               ret = map->reg_update_bits(map->bus_context, reg, mask, val);
+               if (ret == 0 && change)
                        *change = true;
        } else {
-               if (change)
-                       *change = false;
+               ret = _regmap_read(map, reg, &orig);
+               if (ret != 0)
+                       return ret;
+
+               tmp = orig & ~mask;
+               tmp |= val & mask;
+
+               if (force_write || (tmp != orig)) {
+                       ret = _regmap_write(map, reg, tmp);
+                       if (ret == 0 && change)
+                               *change = true;
+               }
        }
 
        return ret;
index 24882c18fcbe6911c7266aa4dcf42903ffb7dbef..59d8d0d14824d0bbfcb5f20fdb6312261f2c79a2 100644 (file)
@@ -436,13 +436,8 @@ int bcma_bus_register(struct bcma_bus *bus)
        }
 
        dev = bcma_bus_get_host_dev(bus);
-       /* TODO: remove check for IS_BUILTIN(CONFIG_BCMA) check when
-        * of_default_bus_match_table is exported or in some other way
-        * accessible. This is just a temporary workaround.
-        */
-       if (IS_BUILTIN(CONFIG_BCMA) && dev) {
-               of_platform_populate(dev->of_node, of_default_bus_match_table,
-                                    NULL, dev);
+       if (dev) {
+               of_platform_default_populate(dev->of_node, NULL, dev);
        }
 
        /* Cores providing flash access go before SPROM init */
index 6f26cf38c6f9c732bfa3293c1b129919a6e245f3..128e7df5b807222ba2d6f50148cb164919a308f7 100644 (file)
@@ -3780,6 +3780,9 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
        blk_queue_max_discard_sectors(q, segment_size / SECTOR_SIZE);
        q->limits.discard_zeroes_data = 1;
 
+       if (!ceph_test_opt(rbd_dev->rbd_client->client, NOCRC))
+               q->backing_dev_info.capabilities |= BDI_CAP_STABLE_WRITES;
+
        disk->queue = q;
 
        q->queuedata = rbd_dev;
index 0bd88c942a5202845960ddbf62b35e9d1aa9e9d2..ec6af15950622ef2448cfda759fd73bb141286f0 100644 (file)
@@ -4,6 +4,7 @@ menu "Bluetooth device drivers"
 
 config BT_INTEL
        tristate
+       select REGMAP
 
 config BT_BCM
        tristate
@@ -182,7 +183,8 @@ config BT_HCIBCM203X
 
 config BT_HCIBPA10X
        tristate "HCI BPA10x USB driver"
-       depends on USB
+       depends on USB && BT_HCIUART
+       select BT_HCIUART_H4
        help
          Bluetooth HCI BPA10x USB driver.
          This driver provides support for the Digianswer BPA 100/105 Bluetooth
@@ -275,7 +277,7 @@ config BT_MRVL
          The core driver to support Marvell Bluetooth devices.
 
          This driver is required if you want to support
-         Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897.
+         Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897/8997.
 
          Say Y here to compile Marvell Bluetooth driver
          into the kernel or say M to compile it as module.
@@ -289,7 +291,7 @@ config BT_MRVL_SDIO
          The driver for Marvell Bluetooth chipsets with SDIO interface.
 
          This driver is required if you want to use Marvell Bluetooth
-         devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897
+         devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897/SD8997
          chipsets are supported.
 
          Say Y here to compile support for Marvell BT-over-SDIO driver
index e527a3e13939c2f19c74401a5c24edb5c13e97ad..fa893c3ec4087f39382f3dc83b968c9859f41cca 100644 (file)
@@ -93,6 +93,7 @@ static const struct usb_device_id ath3k_table[] = {
        { USB_DEVICE(0x04CA, 0x300f) },
        { USB_DEVICE(0x04CA, 0x3010) },
        { USB_DEVICE(0x0930, 0x0219) },
+       { USB_DEVICE(0x0930, 0x021c) },
        { USB_DEVICE(0x0930, 0x0220) },
        { USB_DEVICE(0x0930, 0x0227) },
        { USB_DEVICE(0x0b05, 0x17d0) },
@@ -104,6 +105,7 @@ static const struct usb_device_id ath3k_table[] = {
        { USB_DEVICE(0x0CF3, 0x311F) },
        { USB_DEVICE(0x0cf3, 0x3121) },
        { USB_DEVICE(0x0CF3, 0x817a) },
+       { USB_DEVICE(0x0CF3, 0x817b) },
        { USB_DEVICE(0x0cf3, 0xe003) },
        { USB_DEVICE(0x0CF3, 0xE004) },
        { USB_DEVICE(0x0CF3, 0xE005) },
@@ -153,6 +155,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
        { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
@@ -164,6 +167,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
        { USB_DEVICE(0x0cf3, 0x311F), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0CF3, 0x817b), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe006), .driver_info = BTUSB_ATH3012 },
index a5c4d0584389713522652c472cfd5c7b5e75dbab..616ec2ac1b22f8d3e77f29848f3cd46cf830a94c 100644 (file)
@@ -422,17 +422,12 @@ static int bfusb_open(struct hci_dev *hdev)
 
        BT_DBG("hdev %p bfusb %p", hdev, data);
 
-       if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        write_lock_irqsave(&data->lock, flags);
 
        err = bfusb_rx_submit(data, NULL);
        if (!err) {
                for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
                        bfusb_rx_submit(data, NULL);
-       } else {
-               clear_bit(HCI_RUNNING, &hdev->flags);
        }
 
        write_unlock_irqrestore(&data->lock, flags);
@@ -458,9 +453,6 @@ static int bfusb_close(struct hci_dev *hdev)
 
        BT_DBG("hdev %p bfusb %p", hdev, data);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        write_lock_irqsave(&data->lock, flags);
        write_unlock_irqrestore(&data->lock, flags);
 
@@ -479,9 +471,6 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
index 35e63aaa6f803e423a39e532adeff717b7279cb3..36fa1c958c74fadbb6bed47eca40a38e48433be0 100644 (file)
@@ -390,7 +390,7 @@ static void bluecard_receive(struct bluecard_info *info,
        for (i = 0; i < len; i++) {
 
                /* Allocate packet */
-               if (info->rx_skb == NULL) {
+               if (!info->rx_skb) {
                        info->rx_state = RECV_WAIT_PACKET_TYPE;
                        info->rx_count = 0;
                        info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
@@ -628,9 +628,6 @@ static int bluecard_hci_open(struct hci_dev *hdev)
        if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
                bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
 
-       if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
-               return 0;
-
        if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
                unsigned int iobase = info->p_dev->resource[0]->start;
 
@@ -646,9 +643,6 @@ static int bluecard_hci_close(struct hci_dev *hdev)
 {
        struct bluecard_info *info = hci_get_drvdata(hdev);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
-               return 0;
-
        bluecard_hci_flush(hdev);
 
        if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
index 8a319913c9a96fd60c6d021c5edd055b83ef0aad..49c397e21b39886ed409461a30479cfcc427772c 100644 (file)
@@ -35,7 +35,9 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-#define VERSION "0.10"
+#include "hci_uart.h"
+
+#define VERSION "0.11"
 
 static const struct usb_device_id bpa10x_table[] = {
        /* Tektronix BPA 100/105 (Digianswer) */
@@ -56,112 +58,6 @@ struct bpa10x_data {
        struct sk_buff *rx_skb[2];
 };
 
-#define HCI_VENDOR_HDR_SIZE 5
-
-struct hci_vendor_hdr {
-       __u8    type;
-       __le16  snum;
-       __le16  dlen;
-} __packed;
-
-static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
-{
-       struct bpa10x_data *data = hci_get_drvdata(hdev);
-
-       BT_DBG("%s queue %d buffer %p count %d", hdev->name,
-                                                       queue, buf, count);
-
-       if (queue < 0 || queue > 1)
-               return -EILSEQ;
-
-       hdev->stat.byte_rx += count;
-
-       while (count) {
-               struct sk_buff *skb = data->rx_skb[queue];
-               struct { __u8 type; int expect; } *scb;
-               int type, len = 0;
-
-               if (!skb) {
-                       /* Start of the frame */
-
-                       type = *((__u8 *) buf);
-                       count--; buf++;
-
-                       switch (type) {
-                       case HCI_EVENT_PKT:
-                               if (count >= HCI_EVENT_HDR_SIZE) {
-                                       struct hci_event_hdr *h = buf;
-                                       len = HCI_EVENT_HDR_SIZE + h->plen;
-                               } else
-                                       return -EILSEQ;
-                               break;
-
-                       case HCI_ACLDATA_PKT:
-                               if (count >= HCI_ACL_HDR_SIZE) {
-                                       struct hci_acl_hdr *h = buf;
-                                       len = HCI_ACL_HDR_SIZE +
-                                                       __le16_to_cpu(h->dlen);
-                               } else
-                                       return -EILSEQ;
-                               break;
-
-                       case HCI_SCODATA_PKT:
-                               if (count >= HCI_SCO_HDR_SIZE) {
-                                       struct hci_sco_hdr *h = buf;
-                                       len = HCI_SCO_HDR_SIZE + h->dlen;
-                               } else
-                                       return -EILSEQ;
-                               break;
-
-                       case HCI_VENDOR_PKT:
-                               if (count >= HCI_VENDOR_HDR_SIZE) {
-                                       struct hci_vendor_hdr *h = buf;
-                                       len = HCI_VENDOR_HDR_SIZE +
-                                                       __le16_to_cpu(h->dlen);
-                               } else
-                                       return -EILSEQ;
-                               break;
-                       }
-
-                       skb = bt_skb_alloc(len, GFP_ATOMIC);
-                       if (!skb) {
-                               BT_ERR("%s no memory for packet", hdev->name);
-                               return -ENOMEM;
-                       }
-
-                       data->rx_skb[queue] = skb;
-
-                       scb = (void *) skb->cb;
-                       scb->type = type;
-                       scb->expect = len;
-               } else {
-                       /* Continuation */
-
-                       scb = (void *) skb->cb;
-                       len = scb->expect;
-               }
-
-               len = min(len, count);
-
-               memcpy(skb_put(skb, len), buf, len);
-
-               scb->expect -= len;
-
-               if (scb->expect == 0) {
-                       /* Complete frame */
-
-                       data->rx_skb[queue] = NULL;
-
-                       bt_cb(skb)->pkt_type = scb->type;
-                       hci_recv_frame(hdev, skb);
-               }
-
-               count -= len; buf += len;
-       }
-
-       return 0;
-}
-
 static void bpa10x_tx_complete(struct urb *urb)
 {
        struct sk_buff *skb = urb->context;
@@ -184,6 +80,22 @@ done:
        kfree_skb(skb);
 }
 
+#define HCI_VENDOR_HDR_SIZE 5
+
+#define HCI_RECV_VENDOR \
+       .type = HCI_VENDOR_PKT, \
+       .hlen = HCI_VENDOR_HDR_SIZE, \
+       .loff = 3, \
+       .lsize = 2, \
+       .maxlen = HCI_MAX_FRAME_SIZE
+
+static const struct h4_recv_pkt bpa10x_recv_pkts[] = {
+       { H4_RECV_ACL,     .recv = hci_recv_frame },
+       { H4_RECV_SCO,     .recv = hci_recv_frame },
+       { H4_RECV_EVENT,   .recv = hci_recv_frame },
+       { HCI_RECV_VENDOR, .recv = hci_recv_diag  },
+};
+
 static void bpa10x_rx_complete(struct urb *urb)
 {
        struct hci_dev *hdev = urb->context;
@@ -197,11 +109,17 @@ static void bpa10x_rx_complete(struct urb *urb)
                return;
 
        if (urb->status == 0) {
-               if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe),
+               bool idx = usb_pipebulk(urb->pipe);
+
+               data->rx_skb[idx] = h4_recv_buf(hdev, data->rx_skb[idx],
                                                urb->transfer_buffer,
-                                               urb->actual_length) < 0) {
+                                               urb->actual_length,
+                                               bpa10x_recv_pkts,
+                                               ARRAY_SIZE(bpa10x_recv_pkts));
+               if (IS_ERR(data->rx_skb[idx])) {
                        BT_ERR("%s corrupted event packet", hdev->name);
                        hdev->stat.err_rx++;
+                       data->rx_skb[idx] = NULL;
                }
        }
 
@@ -304,9 +222,6 @@ static int bpa10x_open(struct hci_dev *hdev)
 
        BT_DBG("%s", hdev->name);
 
-       if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        err = bpa10x_submit_intr_urb(hdev);
        if (err < 0)
                goto error;
@@ -320,8 +235,6 @@ static int bpa10x_open(struct hci_dev *hdev)
 error:
        usb_kill_anchored_urbs(&data->rx_anchor);
 
-       clear_bit(HCI_RUNNING, &hdev->flags);
-
        return err;
 }
 
@@ -331,9 +244,6 @@ static int bpa10x_close(struct hci_dev *hdev)
 
        BT_DBG("%s", hdev->name);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        usb_kill_anchored_urbs(&data->rx_anchor);
 
        return 0;
@@ -350,6 +260,24 @@ static int bpa10x_flush(struct hci_dev *hdev)
        return 0;
 }
 
+static int bpa10x_setup(struct hci_dev *hdev)
+{
+       const u8 req[] = { 0x07 };
+       struct sk_buff *skb;
+
+       BT_DBG("%s", hdev->name);
+
+       /* Read revision string */
+       skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
+
+       kfree_skb(skb);
+       return 0;
+}
+
 static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct bpa10x_data *data = hci_get_drvdata(hdev);
@@ -360,9 +288,6 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s", hdev->name);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        skb->dev = (void *) hdev;
 
        urb = usb_alloc_urb(0, GFP_ATOMIC);
@@ -431,6 +356,25 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
        return 0;
 }
 
+static int bpa10x_set_diag(struct hci_dev *hdev, bool enable)
+{
+       const u8 req[] = { 0x00, enable };
+       struct sk_buff *skb;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!test_bit(HCI_RUNNING, &hdev->flags))
+               return -ENETDOWN;
+
+       /* Enable sniffer operation */
+       skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       kfree_skb(skb);
+       return 0;
+}
+
 static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
        struct bpa10x_data *data;
@@ -465,7 +409,9 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *
        hdev->open     = bpa10x_open;
        hdev->close    = bpa10x_close;
        hdev->flush    = bpa10x_flush;
+       hdev->setup    = bpa10x_setup;
        hdev->send     = bpa10x_send_frame;
+       hdev->set_diag = bpa10x_set_diag;
 
        set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
 
index a00bb82eb7c6d8322f835c8ce5689eae784bc5f2..5803aaed958f9518bdf9b6683ae64aa24be0514e 100644 (file)
@@ -233,7 +233,7 @@ static void bt3c_receive(struct bt3c_info *info)
                info->hdev->stat.byte_rx++;
 
                /* Allocate packet */
-               if (info->rx_skb == NULL) {
+               if (!info->rx_skb) {
                        info->rx_state = RECV_WAIT_PACKET_TYPE;
                        info->rx_count = 0;
                        info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
@@ -270,7 +270,6 @@ static void bt3c_receive(struct bt3c_info *info)
                                /* Unknown packet */
                                BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
                                info->hdev->stat.err_rx++;
-                               clear_bit(HCI_RUNNING, &(info->hdev->flags));
 
                                kfree_skb(info->rx_skb);
                                info->rx_skb = NULL;
@@ -395,17 +394,12 @@ static int bt3c_hci_flush(struct hci_dev *hdev)
 
 static int bt3c_hci_open(struct hci_dev *hdev)
 {
-       set_bit(HCI_RUNNING, &(hdev->flags));
-
        return 0;
 }
 
 
 static int bt3c_hci_close(struct hci_dev *hdev)
 {
-       if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
-               return 0;
-
        bt3c_hci_flush(hdev);
 
        return 0;
@@ -453,7 +447,8 @@ static int bt3c_load_firmware(struct bt3c_info *info,
 {
        char *ptr = (char *) firmware;
        char b[9];
-       unsigned int iobase, size, addr, fcs, tmp;
+       unsigned int iobase, tmp;
+       unsigned long size, addr, fcs;
        int i, err = 0;
 
        iobase = info->p_dev->resource[0]->start;
@@ -478,15 +473,18 @@ static int bt3c_load_firmware(struct bt3c_info *info,
 
                memset(b, 0, sizeof(b));
                memcpy(b, ptr + 2, 2);
-               size = simple_strtoul(b, NULL, 16);
+               if (kstrtoul(b, 16, &size) < 0)
+                       return -EINVAL;
 
                memset(b, 0, sizeof(b));
                memcpy(b, ptr + 4, 8);
-               addr = simple_strtoul(b, NULL, 16);
+               if (kstrtoul(b, 16, &addr) < 0)
+                       return -EINVAL;
 
                memset(b, 0, sizeof(b));
                memcpy(b, ptr + (size * 2) + 2, 2);
-               fcs = simple_strtoul(b, NULL, 16);
+               if (kstrtoul(b, 16, &fcs) < 0)
+                       return -EINVAL;
 
                memset(b, 0, sizeof(b));
                for (tmp = 0, i = 0; i < size; i++) {
index 02ed816a18f9a2b652bbd0d179d150f50cad0294..0b697946e9bc7d91abf76b0dbfa7384fec4ce9b8 100644 (file)
@@ -181,6 +181,27 @@ static int btbcm_reset(struct hci_dev *hdev)
        return 0;
 }
 
+static struct sk_buff *btbcm_read_local_name(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s: BCM: Reading local name failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               return skb;
+       }
+
+       if (skb->len != sizeof(struct hci_rp_read_local_name)) {
+               BT_ERR("%s: BCM: Local name length mismatch", hdev->name);
+               kfree_skb(skb);
+               return ERR_PTR(-EIO);
+       }
+
+       return skb;
+}
+
 static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev)
 {
        struct sk_buff *skb;
@@ -302,7 +323,7 @@ int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len)
        }
 
        BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
-               hw_name ? : "BCM", (subver & 0x7000) >> 13,
+               hw_name ? : "BCM", (subver & 0xe000) >> 13,
                (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
 
        return 0;
@@ -332,7 +353,7 @@ int btbcm_finalize(struct hci_dev *hdev)
        kfree_skb(skb);
 
        BT_INFO("%s: BCM (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
-               (subver & 0x7000) >> 13, (subver & 0x1f00) >> 8,
+               (subver & 0xe000) >> 13, (subver & 0x1f00) >> 8,
                (subver & 0x00ff), rev & 0x0fff);
 
        btbcm_check_bdaddr(hdev);
@@ -393,6 +414,14 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
        BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
        kfree_skb(skb);
 
+       /* Read Local Name */
+       skb = btbcm_read_local_name(hdev);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
+       kfree_skb(skb);
+
        switch ((rev & 0xf000) >> 12) {
        case 0:
        case 3:
@@ -432,7 +461,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
        }
 
        BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
-               hw_name ? : "BCM", (subver & 0x7000) >> 13,
+               hw_name ? : "BCM", (subver & 0xe000) >> 13,
                (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
 
        err = request_firmware(&fw, fw_name, &hdev->dev);
@@ -461,9 +490,17 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
        kfree_skb(skb);
 
        BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
-               hw_name ? : "BCM", (subver & 0x7000) >> 13,
+               hw_name ? : "BCM", (subver & 0xe000) >> 13,
                (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
 
+       /* Read Local Name */
+       skb = btbcm_read_local_name(hdev);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
+       kfree_skb(skb);
+
        btbcm_check_bdaddr(hdev);
 
        set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
@@ -475,12 +512,34 @@ EXPORT_SYMBOL_GPL(btbcm_setup_patchram);
 int btbcm_setup_apple(struct hci_dev *hdev)
 {
        struct sk_buff *skb;
+       int err;
+
+       /* Reset */
+       err = btbcm_reset(hdev);
+       if (err)
+               return err;
 
        /* Read Verbose Config Version Info */
        skb = btbcm_read_verbose_config(hdev);
        if (!IS_ERR(skb)) {
-               BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1],
-                       get_unaligned_le16(skb->data + 5));
+               BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name,
+                       skb->data[1], get_unaligned_le16(skb->data + 5));
+               kfree_skb(skb);
+       }
+
+       /* Read USB Product Info */
+       skb = btbcm_read_usb_product(hdev);
+       if (!IS_ERR(skb)) {
+               BT_INFO("%s: BCM: product %4.4x:%4.4x", hdev->name,
+                       get_unaligned_le16(skb->data + 1),
+                       get_unaligned_le16(skb->data + 3));
+               kfree_skb(skb);
+       }
+
+       /* Read Local Name */
+       skb = btbcm_read_local_name(hdev);
+       if (!IS_ERR(skb)) {
+               BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
                kfree_skb(skb);
        }
 
index 048423fd83bf8cc5b17df1bff18d90dea826b877..1f13e617bf560d7dde4c30af30e7b6ded180f1c4 100644 (file)
@@ -22,6 +22,8 @@
  */
 
 #include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -89,6 +91,75 @@ int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
 }
 EXPORT_SYMBOL_GPL(btintel_set_bdaddr);
 
+int btintel_set_diag(struct hci_dev *hdev, bool enable)
+{
+       struct sk_buff *skb;
+       u8 param[3];
+       int err;
+
+       if (enable) {
+               param[0] = 0x03;
+               param[1] = 0x03;
+               param[2] = 0x03;
+       } else {
+               param[0] = 0x00;
+               param[1] = 0x00;
+               param[2] = 0x00;
+       }
+
+       skb = __hci_cmd_sync(hdev, 0xfc43, 3, param, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               if (err == -ENODATA)
+                       goto done;
+               BT_ERR("%s: Changing Intel diagnostic mode failed (%d)",
+                      hdev->name, err);
+               return err;
+       }
+       kfree_skb(skb);
+
+done:
+       btintel_set_event_mask(hdev, enable);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_set_diag);
+
+int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable)
+{
+       struct sk_buff *skb;
+       u8 param[2];
+       int err;
+
+       param[0] = 0x01;
+       param[1] = 0x00;
+
+       skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               BT_ERR("%s: Entering Intel manufacturer mode failed (%d)",
+                      hdev->name, err);
+               return PTR_ERR(skb);
+       }
+       kfree_skb(skb);
+
+       err = btintel_set_diag(hdev, enable);
+
+       param[0] = 0x00;
+       param[1] = 0x00;
+
+       skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)",
+                      hdev->name, err);
+               return PTR_ERR(skb);
+       }
+       kfree_skb(skb);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(btintel_set_diag_mfg);
+
 void btintel_hw_error(struct hci_dev *hdev, u8 code)
 {
        struct sk_buff *skb;
@@ -169,6 +240,304 @@ int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
 }
 EXPORT_SYMBOL_GPL(btintel_secure_send);
 
+int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name)
+{
+       const struct firmware *fw;
+       struct sk_buff *skb;
+       const u8 *fw_ptr;
+       int err;
+
+       err = request_firmware_direct(&fw, ddc_name, &hdev->dev);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to load Intel DDC file %s (%d)",
+                          ddc_name, err);
+               return err;
+       }
+
+       bt_dev_info(hdev, "Found Intel DDC parameters: %s", ddc_name);
+
+       fw_ptr = fw->data;
+
+       /* DDC file contains one or more DDC structure which has
+        * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2).
+        */
+       while (fw->size > fw_ptr - fw->data) {
+               u8 cmd_plen = fw_ptr[0] + sizeof(u8);
+
+               skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr,
+                                    HCI_INIT_TIMEOUT);
+               if (IS_ERR(skb)) {
+                       bt_dev_err(hdev, "Failed to send Intel_Write_DDC (%ld)",
+                                  PTR_ERR(skb));
+                       release_firmware(fw);
+                       return PTR_ERR(skb);
+               }
+
+               fw_ptr += cmd_plen;
+               kfree_skb(skb);
+       }
+
+       release_firmware(fw);
+
+       bt_dev_info(hdev, "Applying Intel DDC parameters completed");
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_load_ddc_config);
+
+int btintel_set_event_mask(struct hci_dev *hdev, bool debug)
+{
+       u8 mask[8] = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+       struct sk_buff *skb;
+       int err;
+
+       if (debug)
+               mask[1] |= 0x62;
+
+       skb = __hci_cmd_sync(hdev, 0xfc52, 8, mask, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               BT_ERR("%s: Setting Intel event mask failed (%d)",
+                      hdev->name, err);
+               return err;
+       }
+       kfree_skb(skb);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_set_event_mask);
+
+int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
+{
+       struct sk_buff *skb;
+       u8 param[2];
+       int err;
+
+       param[0] = 0x01;
+       param[1] = 0x00;
+
+       skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               BT_ERR("%s: Entering Intel manufacturer mode failed (%d)",
+                      hdev->name, err);
+               return PTR_ERR(skb);
+       }
+       kfree_skb(skb);
+
+       err = btintel_set_event_mask(hdev, debug);
+
+       param[0] = 0x00;
+       param[1] = 0x00;
+
+       skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)",
+                      hdev->name, err);
+               return PTR_ERR(skb);
+       }
+       kfree_skb(skb);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg);
+
+/* ------- REGMAP IBT SUPPORT ------- */
+
+#define IBT_REG_MODE_8BIT  0x00
+#define IBT_REG_MODE_16BIT 0x01
+#define IBT_REG_MODE_32BIT 0x02
+
+struct regmap_ibt_context {
+       struct hci_dev *hdev;
+       __u16 op_write;
+       __u16 op_read;
+};
+
+struct ibt_cp_reg_access {
+       __le32  addr;
+       __u8    mode;
+       __u8    len;
+       __u8    data[0];
+} __packed;
+
+struct ibt_rp_reg_access {
+       __u8    status;
+       __le32  addr;
+       __u8    data[0];
+} __packed;
+
+static int regmap_ibt_read(void *context, const void *addr, size_t reg_size,
+                          void *val, size_t val_size)
+{
+       struct regmap_ibt_context *ctx = context;
+       struct ibt_cp_reg_access cp;
+       struct ibt_rp_reg_access *rp;
+       struct sk_buff *skb;
+       int err = 0;
+
+       if (reg_size != sizeof(__le32))
+               return -EINVAL;
+
+       switch (val_size) {
+       case 1:
+               cp.mode = IBT_REG_MODE_8BIT;
+               break;
+       case 2:
+               cp.mode = IBT_REG_MODE_16BIT;
+               break;
+       case 4:
+               cp.mode = IBT_REG_MODE_32BIT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* regmap provides a little-endian formatted addr */
+       cp.addr = *(__le32 *)addr;
+       cp.len = val_size;
+
+       bt_dev_dbg(ctx->hdev, "Register (0x%x) read", le32_to_cpu(cp.addr));
+
+       skb = hci_cmd_sync(ctx->hdev, ctx->op_read, sizeof(cp), &cp,
+                          HCI_CMD_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error (%d)",
+                          le32_to_cpu(cp.addr), err);
+               return err;
+       }
+
+       if (skb->len != sizeof(*rp) + val_size) {
+               bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad len",
+                          le32_to_cpu(cp.addr));
+               err = -EINVAL;
+               goto done;
+       }
+
+       rp = (struct ibt_rp_reg_access *)skb->data;
+
+       if (rp->addr != cp.addr) {
+               bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad addr",
+                          le32_to_cpu(rp->addr));
+               err = -EINVAL;
+               goto done;
+       }
+
+       memcpy(val, rp->data, val_size);
+
+done:
+       kfree_skb(skb);
+       return err;
+}
+
+static int regmap_ibt_gather_write(void *context,
+                                  const void *addr, size_t reg_size,
+                                  const void *val, size_t val_size)
+{
+       struct regmap_ibt_context *ctx = context;
+       struct ibt_cp_reg_access *cp;
+       struct sk_buff *skb;
+       int plen = sizeof(*cp) + val_size;
+       u8 mode;
+       int err = 0;
+
+       if (reg_size != sizeof(__le32))
+               return -EINVAL;
+
+       switch (val_size) {
+       case 1:
+               mode = IBT_REG_MODE_8BIT;
+               break;
+       case 2:
+               mode = IBT_REG_MODE_16BIT;
+               break;
+       case 4:
+               mode = IBT_REG_MODE_32BIT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       cp = kmalloc(plen, GFP_KERNEL);
+       if (!cp)
+               return -ENOMEM;
+
+       /* regmap provides a little-endian formatted addr/value */
+       cp->addr = *(__le32 *)addr;
+       cp->mode = mode;
+       cp->len = val_size;
+       memcpy(&cp->data, val, val_size);
+
+       bt_dev_dbg(ctx->hdev, "Register (0x%x) write", le32_to_cpu(cp->addr));
+
+       skb = hci_cmd_sync(ctx->hdev, ctx->op_write, plen, cp, HCI_CMD_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               bt_dev_err(ctx->hdev, "regmap: Register (0x%x) write error (%d)",
+                          le32_to_cpu(cp->addr), err);
+               goto done;
+       }
+       kfree_skb(skb);
+
+done:
+       kfree(cp);
+       return err;
+}
+
+static int regmap_ibt_write(void *context, const void *data, size_t count)
+{
+       /* data contains register+value, since we only support 32bit addr,
+        * minimum data size is 4 bytes.
+        */
+       if (WARN_ONCE(count < 4, "Invalid register access"))
+               return -EINVAL;
+
+       return regmap_ibt_gather_write(context, data, 4, data + 4, count - 4);
+}
+
+static void regmap_ibt_free_context(void *context)
+{
+       kfree(context);
+}
+
+static struct regmap_bus regmap_ibt = {
+       .read = regmap_ibt_read,
+       .write = regmap_ibt_write,
+       .gather_write = regmap_ibt_gather_write,
+       .free_context = regmap_ibt_free_context,
+       .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
+       .val_format_endian_default = REGMAP_ENDIAN_LITTLE,
+};
+
+/* Config is the same for all register regions */
+static const struct regmap_config regmap_ibt_cfg = {
+       .name      = "btintel_regmap",
+       .reg_bits  = 32,
+       .val_bits  = 32,
+};
+
+struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
+                                  u16 opcode_write)
+{
+       struct regmap_ibt_context *ctx;
+
+       bt_dev_info(hdev, "regmap: Init R%x-W%x region", opcode_read,
+                   opcode_write);
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return ERR_PTR(-ENOMEM);
+
+       ctx->op_read = opcode_read;
+       ctx->op_write = opcode_write;
+       ctx->hdev = hdev;
+
+       return regmap_init(&hdev->dev, &regmap_ibt, ctx, &regmap_ibt_cfg);
+}
+EXPORT_SYMBOL_GPL(btintel_regmap_init);
+
 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
 MODULE_VERSION(VERSION);
index b278d14758d592896db033c78af53b6565a6c88f..07e58e05a7fa6771c128c3965632d97413b2ff15 100644 (file)
@@ -73,11 +73,19 @@ struct intel_secure_send_result {
 
 int btintel_check_bdaddr(struct hci_dev *hdev);
 int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
+int btintel_set_diag(struct hci_dev *hdev, bool enable);
+int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable);
 void btintel_hw_error(struct hci_dev *hdev, u8 code);
 
 void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver);
 int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
                        const void *param);
+int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name);
+int btintel_set_event_mask(struct hci_dev *hdev, bool debug);
+int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug);
+
+struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
+                                  u16 opcode_write);
 
 #else
 
@@ -91,11 +99,22 @@ static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdadd
        return -EOPNOTSUPP;
 }
 
+static inline int btintel_set_diag(struct hci_dev *hdev, bool enable)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable)
+{
+       return -EOPNOTSUPP;
+}
+
 static inline void btintel_hw_error(struct hci_dev *hdev, u8 code)
 {
 }
 
-static void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
+static inline void btintel_version_info(struct hci_dev *hdev,
+                                       struct intel_version *ver)
 {
 }
 
@@ -105,4 +124,26 @@ static inline int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type,
        return -EOPNOTSUPP;
 }
 
+static inline int btintel_load_ddc_config(struct hci_dev *hdev,
+                                         const char *ddc_name)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int btintel_set_event_mask(struct hci_dev *hdev, bool debug)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev,
+                                                u16 opcode_read,
+                                                u16 opcode_write)
+{
+       return ERR_PTR(-EINVAL);
+}
 #endif
index de05deb444ce260a13be75af8b96eb8c8922794a..6af917331962409dded11034273af84e98905313 100644 (file)
@@ -184,7 +184,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode,
        }
 
        skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC);
-       if (skb == NULL) {
+       if (!skb) {
                BT_ERR("No free skb");
                return -ENOMEM;
        }
@@ -377,20 +377,6 @@ static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
                return -EINVAL;
        }
 
-       if (skb_headroom(skb) < BTM_HEADER_LEN) {
-               struct sk_buff *tmp = skb;
-
-               skb = skb_realloc_headroom(skb, BTM_HEADER_LEN);
-               if (!skb) {
-                       BT_ERR("Tx Error: realloc_headroom failed %d",
-                               BTM_HEADER_LEN);
-                       skb = tmp;
-                       return -EINVAL;
-               }
-
-               kfree_skb(tmp);
-       }
-
        skb_push(skb, BTM_HEADER_LEN);
 
        /* header type: byte[3]
@@ -450,13 +436,6 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags)) {
-               BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags);
-               print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET,
-                                                       skb->data, skb->len);
-               return -EBUSY;
-       }
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
@@ -491,9 +470,6 @@ static int btmrvl_close(struct hci_dev *hdev)
 {
        struct btmrvl_private *priv = hci_get_drvdata(hdev);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        skb_queue_purge(&priv->adapter->tx_queue);
 
        return 0;
@@ -501,8 +477,6 @@ static int btmrvl_close(struct hci_dev *hdev)
 
 static int btmrvl_open(struct hci_dev *hdev)
 {
-       set_bit(HCI_RUNNING, &hdev->flags);
-
        return 0;
 }
 
@@ -542,14 +516,17 @@ static int btmrvl_check_device_tree(struct btmrvl_private *priv)
                ret = of_property_read_u8_array(dt_node, "btmrvl,cal-data",
                                                cal_data + BT_CAL_HDR_LEN,
                                                BT_CAL_DATA_SIZE);
-               if (ret)
+               if (ret) {
+                       of_node_put(dt_node);
                        return ret;
+               }
 
                BT_DBG("Use cal data from device tree");
                ret = btmrvl_download_cal_data(priv, cal_data,
                                               BT_CAL_DATA_SIZE);
                if (ret) {
                        BT_ERR("Fail to download calibrate data");
+                       of_node_put(dt_node);
                        return ret;
                }
        }
index b9978a7ba0cc9b9883d2bdd95168f81408f6cc60..71ea2a3af29340b09ed1a44ccd3f22f840b8e25f 100644 (file)
@@ -146,6 +146,29 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = {
        .fw_dump_end = 0xea,
 };
 
+static const struct btmrvl_sdio_card_reg btmrvl_reg_8997 = {
+       .cfg = 0x00,
+       .host_int_mask = 0x08,
+       .host_intstatus = 0x0c,
+       .card_status = 0x5c,
+       .sq_read_base_addr_a0 = 0xf8,
+       .sq_read_base_addr_a1 = 0xf9,
+       .card_revision = 0xc8,
+       .card_fw_status0 = 0xe8,
+       .card_fw_status1 = 0xe9,
+       .card_rx_len = 0xea,
+       .card_rx_unit = 0xeb,
+       .io_port_0 = 0xe4,
+       .io_port_1 = 0xe5,
+       .io_port_2 = 0xe6,
+       .int_read_to_clear = true,
+       .host_int_rsr = 0x04,
+       .card_misc_cfg = 0xD8,
+       .fw_dump_ctrl = 0xf0,
+       .fw_dump_start = 0xf1,
+       .fw_dump_end = 0xf8,
+};
+
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
        .helper         = "mrvl/sd8688_helper.bin",
        .firmware       = "mrvl/sd8688.bin",
@@ -191,25 +214,37 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
        .supports_fw_dump = true,
 };
 
+static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = {
+       .helper         = NULL,
+       .firmware       = "mrvl/sd8997_uapsta.bin",
+       .reg            = &btmrvl_reg_8997,
+       .support_pscan_win_report = true,
+       .sd_blksz_fw_dl = 256,
+       .supports_fw_dump = true,
+};
+
 static const struct sdio_device_id btmrvl_sdio_ids[] = {
        /* Marvell SD8688 Bluetooth device */
        { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
-                       .driver_data = (unsigned long) &btmrvl_sdio_sd8688 },
+                       .driver_data = (unsigned long)&btmrvl_sdio_sd8688 },
        /* Marvell SD8787 Bluetooth device */
        { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
-                       .driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
+                       .driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
        /* Marvell SD8787 Bluetooth AMP device */
        { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B),
-                       .driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
+                       .driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
        /* Marvell SD8797 Bluetooth device */
        { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
-                       .driver_data = (unsigned long) &btmrvl_sdio_sd8797 },
+                       .driver_data = (unsigned long)&btmrvl_sdio_sd8797 },
        /* Marvell SD8887 Bluetooth device */
        { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136),
                        .driver_data = (unsigned long)&btmrvl_sdio_sd8887 },
        /* Marvell SD8897 Bluetooth device */
        { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E),
-                       .driver_data = (unsigned long) &btmrvl_sdio_sd8897 },
+                       .driver_data = (unsigned long)&btmrvl_sdio_sd8897 },
+       /* Marvell SD8997 Bluetooth device */
+       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9142),
+                       .driver_data = (unsigned long)&btmrvl_sdio_sd8997 },
 
        { }     /* Terminating entry */
 };
@@ -619,7 +654,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
 
        /* Allocate buffer */
        skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC);
-       if (skb == NULL) {
+       if (!skb) {
                BT_ERR("No free skb");
                ret = -ENOMEM;
                goto exit;
@@ -1278,6 +1313,12 @@ static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv)
 
                if (memory_size == 0) {
                        BT_INFO("Firmware dump finished!");
+                       sdio_writeb(card->func, FW_DUMP_READ_DONE,
+                                   card->reg->fw_dump_ctrl, &ret);
+                       if (ret) {
+                               BT_ERR("SDIO Write MEMDUMP_FINISH ERR");
+                               goto done;
+                       }
                        break;
                }
 
@@ -1616,3 +1657,4 @@ MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
+MODULE_FIRMWARE("mrvl/sd8997_uapsta.bin");
index 83f6437dd91df4014e27084f53ac6b02be5ed68a..7b624423a7e8f790e8448b421d39b1bf91616b23 100644 (file)
@@ -194,21 +194,15 @@ static int btsdio_open(struct hci_dev *hdev)
 
        BT_DBG("%s", hdev->name);
 
-       if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        sdio_claim_host(data->func);
 
        err = sdio_enable_func(data->func);
-       if (err < 0) {
-               clear_bit(HCI_RUNNING, &hdev->flags);
+       if (err < 0)
                goto release;
-       }
 
        err = sdio_claim_irq(data->func, btsdio_interrupt);
        if (err < 0) {
                sdio_disable_func(data->func);
-               clear_bit(HCI_RUNNING, &hdev->flags);
                goto release;
        }
 
@@ -229,9 +223,6 @@ static int btsdio_close(struct hci_dev *hdev)
 
        BT_DBG("%s", hdev->name);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        sdio_claim_host(data->func);
 
        sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL);
@@ -261,9 +252,6 @@ static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s", hdev->name);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
index abb4d2106db464198c328af1069342bd36d64d80..bb8e4025fb9eb8d30d670ce03d9afcc245336e88 100644 (file)
@@ -38,7 +38,7 @@
 #include <linux/serial.h>
 #include <linux/serial_reg.h>
 #include <linux/bitops.h>
-#include <asm/io.h>
+#include <linux/io.h>
 
 #include <pcmcia/cistpl.h>
 #include <pcmcia/ciscode.h>
@@ -188,7 +188,7 @@ static void btuart_receive(struct btuart_info *info)
                info->hdev->stat.byte_rx++;
 
                /* Allocate packet */
-               if (info->rx_skb == NULL) {
+               if (!info->rx_skb) {
                        info->rx_state = RECV_WAIT_PACKET_TYPE;
                        info->rx_count = 0;
                        info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
@@ -223,7 +223,6 @@ static void btuart_receive(struct btuart_info *info)
                                /* Unknown packet */
                                BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
                                info->hdev->stat.err_rx++;
-                               clear_bit(HCI_RUNNING, &(info->hdev->flags));
 
                                kfree_skb(info->rx_skb);
                                info->rx_skb = NULL;
@@ -409,17 +408,12 @@ static int btuart_hci_flush(struct hci_dev *hdev)
 
 static int btuart_hci_open(struct hci_dev *hdev)
 {
-       set_bit(HCI_RUNNING, &(hdev->flags));
-
        return 0;
 }
 
 
 static int btuart_hci_close(struct hci_dev *hdev)
 {
-       if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
-               return 0;
-
        btuart_hci_flush(hdev);
 
        return 0;
index b6aceaf82aa88c7617c4fd8e93506b8461314426..e33dacf5bd98765178ddac60f7d50d742de555ac 100644 (file)
@@ -60,6 +60,8 @@ static struct usb_driver btusb_driver;
 #define BTUSB_QCA_ROME         0x8000
 #define BTUSB_BCM_APPLE                0x10000
 #define BTUSB_REALTEK          0x20000
+#define BTUSB_BCM2045          0x40000
+#define BTUSB_IFNUM_2          0x80000
 
 static const struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
@@ -73,7 +75,7 @@ static const struct usb_device_id btusb_table[] = {
 
        /* Apple-specific (Broadcom) devices */
        { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01),
-         .driver_info = BTUSB_BCM_APPLE },
+         .driver_info = BTUSB_BCM_APPLE | BTUSB_IFNUM_2 },
 
        /* MediaTek MT76x0E */
        { USB_DEVICE(0x0e8d, 0x763f) },
@@ -124,6 +126,9 @@ static const struct usb_device_id btusb_table[] = {
        /* Broadcom BCM20702B0 (Dynex/Insignia) */
        { USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM },
 
+       /* Broadcom BCM43142A0 (Foxconn/Lenovo) */
+       { USB_DEVICE(0x105b, 0xe065), .driver_info = BTUSB_BCM_PATCHRAM },
+
        /* Foxconn - Hon Hai */
        { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01),
          .driver_info = BTUSB_BCM_PATCHRAM },
@@ -164,6 +169,9 @@ static const struct usb_device_id blacklist_table[] = {
        /* Broadcom BCM2033 without firmware */
        { USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
 
+       /* Broadcom BCM2045 devices */
+       { USB_DEVICE(0x0a5c, 0x2045), .driver_info = BTUSB_BCM2045 },
+
        /* Atheros 3011 with sflash firmware */
        { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
        { USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
@@ -195,6 +203,7 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
@@ -206,6 +215,7 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x0cf3, 0x311f), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x817b), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
@@ -341,12 +351,14 @@ static const struct usb_device_id blacklist_table[] = {
 #define BTUSB_FIRMWARE_FAILED  8
 #define BTUSB_BOOTING          9
 #define BTUSB_RESET_RESUME     10
+#define BTUSB_DIAG_RUNNING     11
 
 struct btusb_data {
        struct hci_dev       *hdev;
        struct usb_device    *udev;
        struct usb_interface *intf;
        struct usb_interface *isoc;
+       struct usb_interface *diag;
 
        unsigned long flags;
 
@@ -361,6 +373,7 @@ struct btusb_data {
        struct usb_anchor intr_anchor;
        struct usb_anchor bulk_anchor;
        struct usb_anchor isoc_anchor;
+       struct usb_anchor diag_anchor;
        spinlock_t rxlock;
 
        struct sk_buff *evt_skb;
@@ -372,6 +385,8 @@ struct btusb_data {
        struct usb_endpoint_descriptor *bulk_rx_ep;
        struct usb_endpoint_descriptor *isoc_tx_ep;
        struct usb_endpoint_descriptor *isoc_rx_ep;
+       struct usb_endpoint_descriptor *diag_tx_ep;
+       struct usb_endpoint_descriptor *diag_rx_ep;
 
        __u8 cmdreq_type;
        __u8 cmdreq;
@@ -869,6 +884,92 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
        return err;
 }
 
+static void btusb_diag_complete(struct urb *urb)
+{
+       struct hci_dev *hdev = urb->context;
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       int err;
+
+       BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
+              urb->actual_length);
+
+       if (urb->status == 0) {
+               struct sk_buff *skb;
+
+               skb = bt_skb_alloc(urb->actual_length, GFP_ATOMIC);
+               if (skb) {
+                       memcpy(skb_put(skb, urb->actual_length),
+                              urb->transfer_buffer, urb->actual_length);
+                       hci_recv_diag(hdev, skb);
+               }
+       } else if (urb->status == -ENOENT) {
+               /* Avoid suspend failed when usb_kill_urb */
+               return;
+       }
+
+       if (!test_bit(BTUSB_DIAG_RUNNING, &data->flags))
+               return;
+
+       usb_anchor_urb(urb, &data->diag_anchor);
+       usb_mark_last_busy(data->udev);
+
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (err < 0) {
+               /* -EPERM: urb is being killed;
+                * -ENODEV: device got disconnected */
+               if (err != -EPERM && err != -ENODEV)
+                       BT_ERR("%s urb %p failed to resubmit (%d)",
+                              hdev->name, urb, -err);
+               usb_unanchor_urb(urb);
+       }
+}
+
+static int btusb_submit_diag_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       struct urb *urb;
+       unsigned char *buf;
+       unsigned int pipe;
+       int err, size = HCI_MAX_FRAME_SIZE;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!data->diag_rx_ep)
+               return -ENODEV;
+
+       urb = usb_alloc_urb(0, mem_flags);
+       if (!urb)
+               return -ENOMEM;
+
+       buf = kmalloc(size, mem_flags);
+       if (!buf) {
+               usb_free_urb(urb);
+               return -ENOMEM;
+       }
+
+       pipe = usb_rcvbulkpipe(data->udev, data->diag_rx_ep->bEndpointAddress);
+
+       usb_fill_bulk_urb(urb, data->udev, pipe, buf, size,
+                         btusb_diag_complete, hdev);
+
+       urb->transfer_flags |= URB_FREE_BUFFER;
+
+       usb_mark_last_busy(data->udev);
+       usb_anchor_urb(urb, &data->diag_anchor);
+
+       err = usb_submit_urb(urb, mem_flags);
+       if (err < 0) {
+               if (err != -EPERM && err != -ENODEV)
+                       BT_ERR("%s urb %p submission failed (%d)",
+                              hdev->name, urb, -err);
+               usb_unanchor_urb(urb);
+       }
+
+       usb_free_urb(urb);
+
+       return err;
+}
+
 static void btusb_tx_complete(struct urb *urb)
 {
        struct sk_buff *skb = urb->context;
@@ -940,9 +1041,6 @@ static int btusb_open(struct hci_dev *hdev)
 
        data->intf->needs_remote_wakeup = 1;
 
-       if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-               goto done;
-
        if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
                goto done;
 
@@ -959,13 +1057,17 @@ static int btusb_open(struct hci_dev *hdev)
        set_bit(BTUSB_BULK_RUNNING, &data->flags);
        btusb_submit_bulk_urb(hdev, GFP_KERNEL);
 
+       if (data->diag) {
+               if (!btusb_submit_diag_urb(hdev, GFP_KERNEL))
+                       set_bit(BTUSB_DIAG_RUNNING, &data->flags);
+       }
+
 done:
        usb_autopm_put_interface(data->intf);
        return 0;
 
 failed:
        clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-       clear_bit(HCI_RUNNING, &hdev->flags);
        usb_autopm_put_interface(data->intf);
        return err;
 }
@@ -975,6 +1077,7 @@ static void btusb_stop_traffic(struct btusb_data *data)
        usb_kill_anchored_urbs(&data->intr_anchor);
        usb_kill_anchored_urbs(&data->bulk_anchor);
        usb_kill_anchored_urbs(&data->isoc_anchor);
+       usb_kill_anchored_urbs(&data->diag_anchor);
 }
 
 static int btusb_close(struct hci_dev *hdev)
@@ -984,15 +1087,13 @@ static int btusb_close(struct hci_dev *hdev)
 
        BT_DBG("%s", hdev->name);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        cancel_work_sync(&data->work);
        cancel_work_sync(&data->waker);
 
        clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
        clear_bit(BTUSB_BULK_RUNNING, &data->flags);
        clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+       clear_bit(BTUSB_DIAG_RUNNING, &data->flags);
 
        btusb_stop_traffic(data);
        btusb_free_frags(data);
@@ -1156,9 +1257,6 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s", hdev->name);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                urb = alloc_ctrl_urb(hdev, skb);
@@ -1277,6 +1375,20 @@ static void btusb_work(struct work_struct *work)
                        clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
                        usb_kill_anchored_urbs(&data->isoc_anchor);
 
+                       /* When isochronous alternate setting needs to be
+                        * changed, because SCO connection has been added
+                        * or removed, a packet fragment may be left in the
+                        * reassembling state. This could lead to wrongly
+                        * assembled fragments.
+                        *
+                        * Clear outstanding fragment when selecting a new
+                        * alternate setting.
+                        */
+                       spin_lock(&data->rxlock);
+                       kfree_skb(data->sco_skb);
+                       data->sco_skb = NULL;
+                       spin_unlock(&data->rxlock);
+
                        if (__set_isoc_interface(hdev, new_alts) < 0)
                                return;
                }
@@ -1348,7 +1460,9 @@ static int btusb_setup_csr(struct hci_dev *hdev)
 
        rp = (struct hci_rp_read_local_version *)skb->data;
 
-       if (le16_to_cpu(rp->manufacturer) != 10) {
+       /* Detect controllers which aren't real CSR ones. */
+       if (le16_to_cpu(rp->manufacturer) != 10 ||
+           le16_to_cpu(rp->lmp_subver) == 0x0c5c) {
                /* Clear the reset quirk since this is not an actual
                 * early Bluetooth 1.1 device from CSR.
                 */
@@ -1587,8 +1701,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
                BT_INFO("%s: Intel device is already patched. patch num: %02x",
                        hdev->name, ver->fw_patch_num);
                kfree_skb(skb);
-               btintel_check_bdaddr(hdev);
-               return 0;
+               goto complete;
        }
 
        /* Opens the firmware patch file based on the firmware version read
@@ -1600,8 +1713,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
        fw = btusb_setup_intel_get_fw(hdev, ver);
        if (!fw) {
                kfree_skb(skb);
-               btintel_check_bdaddr(hdev);
-               return 0;
+               goto complete;
        }
        fw_ptr = fw->data;
 
@@ -1674,8 +1786,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
        BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
                hdev->name);
 
-       btintel_check_bdaddr(hdev);
-       return 0;
+       goto complete;
 
 exit_mfg_disable:
        /* Disable the manufacturer mode without reset */
@@ -1690,8 +1801,7 @@ exit_mfg_disable:
 
        BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
 
-       btintel_check_bdaddr(hdev);
-       return 0;
+       goto complete;
 
 exit_mfg_deactivate:
        release_firmware(fw);
@@ -1711,6 +1821,12 @@ exit_mfg_deactivate:
        BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
                hdev->name);
 
+complete:
+       /* Set the event mask for Intel specific vendor events. This enables
+        * a few extra events that are useful during general operation.
+        */
+       btintel_set_event_mask_mfg(hdev, false);
+
        btintel_check_bdaddr(hdev);
        return 0;
 }
@@ -1827,9 +1943,6 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s", hdev->name);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
@@ -2003,6 +2116,15 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
        BT_INFO("%s: Secure boot is %s", hdev->name,
                params->secure_boot ? "enabled" : "disabled");
 
+       BT_INFO("%s: OTP lock is %s", hdev->name,
+               params->otp_lock ? "enabled" : "disabled");
+
+       BT_INFO("%s: API lock is %s", hdev->name,
+               params->api_lock ? "enabled" : "disabled");
+
+       BT_INFO("%s: Debug lock is %s", hdev->name,
+               params->debug_lock ? "enabled" : "disabled");
+
        BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name,
                params->min_fw_build_nn, params->min_fw_build_cw,
                2000 + params->min_fw_build_yy);
@@ -2217,36 +2339,16 @@ done:
         * The device can work without DDC parameters, so even if it fails
         * to load the file, no need to fail the setup.
         */
-       err = request_firmware_direct(&fw, fwname, &hdev->dev);
-       if (err < 0)
-               return 0;
-
-       BT_INFO("%s: Found Intel DDC parameters: %s", hdev->name, fwname);
-
-       fw_ptr = fw->data;
+       btintel_load_ddc_config(hdev, fwname);
 
-       /* DDC file contains one or more DDC structure which has
-        * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2).
+       /* Set the event mask for Intel specific vendor events. This enables
+        * a few extra events that are useful during general operation. It
+        * does not enable any debugging related events.
+        *
+        * The device will function correctly without these events enabled
+        * and thus no need to fail the setup.
         */
-       while (fw->size > fw_ptr - fw->data) {
-               u8 cmd_plen = fw_ptr[0] + sizeof(u8);
-
-               skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr,
-                                    HCI_INIT_TIMEOUT);
-               if (IS_ERR(skb)) {
-                       BT_ERR("%s: Failed to send Intel_Write_DDC (%ld)",
-                              hdev->name, PTR_ERR(skb));
-                       release_firmware(fw);
-                       return PTR_ERR(skb);
-               }
-
-               fw_ptr += cmd_plen;
-               kfree_skb(skb);
-       }
-
-       release_firmware(fw);
-
-       BT_INFO("%s: Applying Intel DDC parameters completed", hdev->name);
+       btintel_set_event_mask(hdev, false);
 
        return 0;
 }
@@ -2573,19 +2675,115 @@ static int btusb_setup_qca(struct hci_dev *hdev)
        return 0;
 }
 
+#ifdef CONFIG_BT_HCIBTUSB_BCM
+static inline int __set_diag_interface(struct hci_dev *hdev)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       struct usb_interface *intf = data->diag;
+       int i;
+
+       if (!data->diag)
+               return -ENODEV;
+
+       data->diag_tx_ep = NULL;
+       data->diag_rx_ep = NULL;
+
+       for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+               struct usb_endpoint_descriptor *ep_desc;
+
+               ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+               if (!data->diag_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
+                       data->diag_tx_ep = ep_desc;
+                       continue;
+               }
+
+               if (!data->diag_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
+                       data->diag_rx_ep = ep_desc;
+                       continue;
+               }
+       }
+
+       if (!data->diag_tx_ep || !data->diag_rx_ep) {
+               BT_ERR("%s invalid diagnostic descriptors", hdev->name);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static struct urb *alloc_diag_urb(struct hci_dev *hdev, bool enable)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       struct sk_buff *skb;
+       struct urb *urb;
+       unsigned int pipe;
+
+       if (!data->diag_tx_ep)
+               return ERR_PTR(-ENODEV);
+
+       urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!urb)
+               return ERR_PTR(-ENOMEM);
+
+       skb = bt_skb_alloc(2, GFP_KERNEL);
+       if (!skb) {
+               usb_free_urb(urb);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       *skb_put(skb, 1) = 0xf0;
+       *skb_put(skb, 1) = enable;
+
+       pipe = usb_sndbulkpipe(data->udev, data->diag_tx_ep->bEndpointAddress);
+
+       usb_fill_bulk_urb(urb, data->udev, pipe,
+                         skb->data, skb->len, btusb_tx_complete, skb);
+
+       skb->dev = (void *)hdev;
+
+       return urb;
+}
+
+static int btusb_bcm_set_diag(struct hci_dev *hdev, bool enable)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       struct urb *urb;
+
+       if (!data->diag)
+               return -ENODEV;
+
+       if (!test_bit(HCI_RUNNING, &hdev->flags))
+               return -ENETDOWN;
+
+       urb = alloc_diag_urb(hdev, enable);
+       if (IS_ERR(urb))
+               return PTR_ERR(urb);
+
+       return submit_or_queue_tx_urb(hdev, urb);
+}
+#endif
+
 static int btusb_probe(struct usb_interface *intf,
                       const struct usb_device_id *id)
 {
        struct usb_endpoint_descriptor *ep_desc;
        struct btusb_data *data;
        struct hci_dev *hdev;
+       unsigned ifnum_base;
        int i, err;
 
        BT_DBG("intf %p id %p", intf, id);
 
        /* interface numbers are hardcoded in the spec */
-       if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
-               return -ENODEV;
+       if (intf->cur_altsetting->desc.bInterfaceNumber != 0) {
+               if (!(id->driver_info & BTUSB_IFNUM_2))
+                       return -ENODEV;
+               if (intf->cur_altsetting->desc.bInterfaceNumber != 2)
+                       return -ENODEV;
+       }
+
+       ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber;
 
        if (!id->driver_info) {
                const struct usb_device_id *match;
@@ -2653,6 +2851,7 @@ static int btusb_probe(struct usb_interface *intf,
        init_usb_anchor(&data->intr_anchor);
        init_usb_anchor(&data->bulk_anchor);
        init_usb_anchor(&data->isoc_anchor);
+       init_usb_anchor(&data->diag_anchor);
        spin_lock_init(&data->rxlock);
 
        if (id->driver_info & BTUSB_INTEL_NEW) {
@@ -2686,33 +2885,53 @@ static int btusb_probe(struct usb_interface *intf,
        hdev->send   = btusb_send_frame;
        hdev->notify = btusb_notify;
 
+       if (id->driver_info & BTUSB_BCM2045)
+               set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
+
        if (id->driver_info & BTUSB_BCM92035)
                hdev->setup = btusb_setup_bcm92035;
 
 #ifdef CONFIG_BT_HCIBTUSB_BCM
        if (id->driver_info & BTUSB_BCM_PATCHRAM) {
+               hdev->manufacturer = 15;
                hdev->setup = btbcm_setup_patchram;
+               hdev->set_diag = btusb_bcm_set_diag;
                hdev->set_bdaddr = btbcm_set_bdaddr;
+
+               /* Broadcom LM_DIAG Interface numbers are hardcoded */
+               data->diag = usb_ifnum_to_if(data->udev, ifnum_base + 2);
        }
 
-       if (id->driver_info & BTUSB_BCM_APPLE)
+       if (id->driver_info & BTUSB_BCM_APPLE) {
+               hdev->manufacturer = 15;
                hdev->setup = btbcm_setup_apple;
+               hdev->set_diag = btusb_bcm_set_diag;
+
+               /* Broadcom LM_DIAG Interface numbers are hardcoded */
+               data->diag = usb_ifnum_to_if(data->udev, ifnum_base + 2);
+       }
 #endif
 
        if (id->driver_info & BTUSB_INTEL) {
+               hdev->manufacturer = 2;
                hdev->setup = btusb_setup_intel;
                hdev->shutdown = btusb_shutdown_intel;
+               hdev->set_diag = btintel_set_diag_mfg;
                hdev->set_bdaddr = btintel_set_bdaddr;
                set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
                set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+               set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
        }
 
        if (id->driver_info & BTUSB_INTEL_NEW) {
+               hdev->manufacturer = 2;
                hdev->send = btusb_send_frame_intel;
                hdev->setup = btusb_setup_intel_new;
                hdev->hw_error = btintel_hw_error;
+               hdev->set_diag = btintel_set_diag;
                hdev->set_bdaddr = btintel_set_bdaddr;
                set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+               set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
        }
 
        if (id->driver_info & BTUSB_MARVELL)
@@ -2723,8 +2942,10 @@ static int btusb_probe(struct usb_interface *intf,
                set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
        }
 
-       if (id->driver_info & BTUSB_INTEL_BOOT)
+       if (id->driver_info & BTUSB_INTEL_BOOT) {
+               hdev->manufacturer = 2;
                set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+       }
 
        if (id->driver_info & BTUSB_ATH3012) {
                hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
@@ -2753,8 +2974,8 @@ static int btusb_probe(struct usb_interface *intf,
                /* AMP controllers do not support SCO packets */
                data->isoc = NULL;
        } else {
-               /* Interface numbers are hardcoded in the specification */
-               data->isoc = usb_ifnum_to_if(data->udev, 1);
+               /* Interface orders are hardcoded in the specification */
+               data->isoc = usb_ifnum_to_if(data->udev, ifnum_base + 1);
        }
 
        if (!reset)
@@ -2782,7 +3003,7 @@ static int btusb_probe(struct usb_interface *intf,
                        set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
 
                /* Fake CSR devices with broken commands */
-               if (bcdDevice <= 0x100)
+               if (bcdDevice <= 0x100 || bcdDevice == 0x134)
                        hdev->setup = btusb_setup_csr;
 
                set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
@@ -2817,6 +3038,16 @@ static int btusb_probe(struct usb_interface *intf,
                }
        }
 
+#ifdef CONFIG_BT_HCIBTUSB_BCM
+       if (data->diag) {
+               if (!usb_driver_claim_interface(&btusb_driver,
+                                               data->diag, data))
+                       __set_diag_interface(hdev);
+               else
+                       data->diag = NULL;
+       }
+#endif
+
        err = hci_register_dev(hdev);
        if (err < 0) {
                hci_free_dev(hdev);
@@ -2844,12 +3075,25 @@ static void btusb_disconnect(struct usb_interface *intf)
        if (data->isoc)
                usb_set_intfdata(data->isoc, NULL);
 
+       if (data->diag)
+               usb_set_intfdata(data->diag, NULL);
+
        hci_unregister_dev(hdev);
 
-       if (intf == data->isoc)
+       if (intf == data->intf) {
+               if (data->isoc)
+                       usb_driver_release_interface(&btusb_driver, data->isoc);
+               if (data->diag)
+                       usb_driver_release_interface(&btusb_driver, data->diag);
+       } else if (intf == data->isoc) {
+               if (data->diag)
+                       usb_driver_release_interface(&btusb_driver, data->diag);
+               usb_driver_release_interface(&btusb_driver, data->intf);
+       } else if (intf == data->diag) {
                usb_driver_release_interface(&btusb_driver, data->intf);
-       else if (data->isoc)
-               usb_driver_release_interface(&btusb_driver, data->isoc);
+               if (data->isoc)
+                       usb_driver_release_interface(&btusb_driver, data->isoc);
+       }
 
        hci_free_dev(hdev);
 }
index 7a722df97343ee6f25fb4c02959b8d64bacc4449..57eb935aedc73b4a782d42f676ec06a0ad5bed5a 100644 (file)
@@ -155,9 +155,6 @@ static int ti_st_open(struct hci_dev *hdev)
 
        BT_DBG("%s %p", hdev->name, hdev);
 
-       if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        /* provide contexts for callbacks from ST */
        hst = hci_get_drvdata(hdev);
 
@@ -181,7 +178,6 @@ static int ti_st_open(struct hci_dev *hdev)
                        goto done;
 
                if (err != -EINPROGRESS) {
-                       clear_bit(HCI_RUNNING, &hdev->flags);
                        BT_ERR("st_register failed %d", err);
                        return err;
                }
@@ -195,7 +191,6 @@ static int ti_st_open(struct hci_dev *hdev)
                        (&hst->wait_reg_completion,
                         msecs_to_jiffies(BT_REGISTER_TIMEOUT));
                if (!timeleft) {
-                       clear_bit(HCI_RUNNING, &hdev->flags);
                        BT_ERR("Timeout(%d sec),didn't get reg "
                                        "completion signal from ST",
                                        BT_REGISTER_TIMEOUT / 1000);
@@ -205,7 +200,6 @@ static int ti_st_open(struct hci_dev *hdev)
                /* Is ST registration callback
                 * called with ERROR status? */
                if (hst->reg_status != 0) {
-                       clear_bit(HCI_RUNNING, &hdev->flags);
                        BT_ERR("ST registration completed with invalid "
                                        "status %d", hst->reg_status);
                        return -EAGAIN;
@@ -215,7 +209,6 @@ done:
                hst->st_write = ti_st_proto[i].write;
                if (!hst->st_write) {
                        BT_ERR("undefined ST write function");
-                       clear_bit(HCI_RUNNING, &hdev->flags);
                        for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
                                /* Undo registration with ST */
                                err = st_unregister(&ti_st_proto[i]);
@@ -236,9 +229,6 @@ static int ti_st_close(struct hci_dev *hdev)
        int err, i;
        struct ti_st *hst = hci_get_drvdata(hdev);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
                err = st_unregister(&ti_st_proto[i]);
                if (err)
@@ -256,9 +246,6 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
        struct ti_st *hst;
        long len;
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        hst = hci_get_drvdata(hdev);
 
        /* Prepend skb with frame type */
index 84135c54ed2e46df5111e2c2797b82fa8db4b143..5026f66fac88d2a7d8726a917ad0fe985be22a6a 100644 (file)
@@ -357,8 +357,6 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst)
 
 static int dtl1_hci_open(struct hci_dev *hdev)
 {
-       set_bit(HCI_RUNNING, &(hdev->flags));
-
        return 0;
 }
 
@@ -376,9 +374,6 @@ static int dtl1_hci_flush(struct hci_dev *hdev)
 
 static int dtl1_hci_close(struct hci_dev *hdev)
 {
-       if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
-               return 0;
-
        dtl1_hci_flush(hdev);
 
        return 0;
index 6da5e4ca13ea6d925807660582be5957c4903752..d776dfd5147811ef37071e68ffd9d9ceaa961c0d 100644 (file)
@@ -243,6 +243,7 @@ static struct sk_buff *ath_dequeue(struct hci_uart *hu)
 static const struct hci_uart_proto athp = {
        .id             = HCI_UART_ATH3K,
        .name           = "ATH3K",
+       .manufacturer   = 69,
        .open           = ath_open,
        .close          = ath_close,
        .flush          = ath_flush,
index 835bfab88ef56acecbf9887d2e13ea912af750cf..cb852cc750b78256cd05fb62eda18dfb28c246aa 100644 (file)
@@ -31,6 +31,9 @@
 #include <linux/clk.h>
 #include <linux/gpio/consumer.h>
 #include <linux/tty.h>
+#include <linux/interrupt.h>
+#include <linux/dmi.h>
+#include <linux/pm_runtime.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include "btbcm.h"
 #include "hci_uart.h"
 
+#define BCM_LM_DIAG_PKT 0x07
+#define BCM_LM_DIAG_SIZE 63
+
+#define BCM_AUTOSUSPEND_DELAY  5000 /* default autosleep delay */
+
 struct bcm_device {
        struct list_head        list;
 
@@ -51,8 +59,10 @@ struct bcm_device {
        bool                    clk_enabled;
 
        u32                     init_speed;
+       int                     irq;
+       u8                      irq_polarity;
 
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
        struct hci_uart         *hu;
        bool                    is_suspended; /* suspend/resume flag */
 #endif
@@ -66,7 +76,7 @@ struct bcm_data {
 };
 
 /* List of BCM BT UART devices */
-static DEFINE_SPINLOCK(bcm_device_lock);
+static DEFINE_MUTEX(bcm_device_lock);
 static LIST_HEAD(bcm_device_list);
 
 static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
@@ -80,7 +90,7 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
 
                clock.type = BCM_UART_CLOCK_48MHZ;
 
-               BT_DBG("%s: Set Controller clock (%d)", hdev->name, clock.type);
+               bt_dev_dbg(hdev, "Set Controller clock (%d)", clock.type);
 
                /* This Broadcom specific command changes the UART's controller
                 * clock for baud rate > 3000000.
@@ -88,15 +98,15 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
                skb = __hci_cmd_sync(hdev, 0xfc45, 1, &clock, HCI_INIT_TIMEOUT);
                if (IS_ERR(skb)) {
                        int err = PTR_ERR(skb);
-                       BT_ERR("%s: BCM: failed to write clock command (%d)",
-                              hdev->name, err);
+                       bt_dev_err(hdev, "BCM: failed to write clock (%d)",
+                                  err);
                        return err;
                }
 
                kfree_skb(skb);
        }
 
-       BT_DBG("%s: Set Controller UART speed to %d bit/s", hdev->name, speed);
+       bt_dev_dbg(hdev, "Set Controller UART speed to %d bit/s", speed);
 
        param.zero = cpu_to_le16(0);
        param.baud_rate = cpu_to_le32(speed);
@@ -108,8 +118,8 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
                             HCI_INIT_TIMEOUT);
        if (IS_ERR(skb)) {
                int err = PTR_ERR(skb);
-               BT_ERR("%s: BCM: failed to write update baudrate command (%d)",
-                      hdev->name, err);
+               bt_dev_err(hdev, "BCM: failed to write update baudrate (%d)",
+                          err);
                return err;
        }
 
@@ -149,12 +159,125 @@ static int bcm_gpio_set_power(struct bcm_device *dev, bool powered)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static irqreturn_t bcm_host_wake(int irq, void *data)
+{
+       struct bcm_device *bdev = data;
+
+       bt_dev_dbg(bdev, "Host wake IRQ");
+
+       pm_runtime_get(&bdev->pdev->dev);
+       pm_runtime_mark_last_busy(&bdev->pdev->dev);
+       pm_runtime_put_autosuspend(&bdev->pdev->dev);
+
+       return IRQ_HANDLED;
+}
+
+static int bcm_request_irq(struct bcm_data *bcm)
+{
+       struct bcm_device *bdev = bcm->dev;
+       int err = 0;
+
+       /* If this is not a platform device, do not enable PM functionalities */
+       mutex_lock(&bcm_device_lock);
+       if (!bcm_device_exists(bdev)) {
+               err = -ENODEV;
+               goto unlock;
+       }
+
+       if (bdev->irq > 0) {
+               err = devm_request_irq(&bdev->pdev->dev, bdev->irq,
+                                      bcm_host_wake, IRQF_TRIGGER_RISING,
+                                      "host_wake", bdev);
+               if (err)
+                       goto unlock;
+
+               device_init_wakeup(&bdev->pdev->dev, true);
+
+               pm_runtime_set_autosuspend_delay(&bdev->pdev->dev,
+                                                BCM_AUTOSUSPEND_DELAY);
+               pm_runtime_use_autosuspend(&bdev->pdev->dev);
+               pm_runtime_set_active(&bdev->pdev->dev);
+               pm_runtime_enable(&bdev->pdev->dev);
+       }
+
+unlock:
+       mutex_unlock(&bcm_device_lock);
+
+       return err;
+}
+
+static const struct bcm_set_sleep_mode default_sleep_params = {
+       .sleep_mode = 1,        /* 0=Disabled, 1=UART, 2=Reserved, 3=USB */
+       .idle_host = 2,         /* idle threshold HOST, in 300ms */
+       .idle_dev = 2,          /* idle threshold device, in 300ms */
+       .bt_wake_active = 1,    /* BT_WAKE active mode: 1 = high, 0 = low */
+       .host_wake_active = 0,  /* HOST_WAKE active mode: 1 = high, 0 = low */
+       .allow_host_sleep = 1,  /* Allow host sleep in SCO flag */
+       .combine_modes = 1,     /* Combine sleep and LPM flag */
+       .tristate_control = 0,  /* Allow tri-state control of UART tx flag */
+       /* Irrelevant USB flags */
+       .usb_auto_sleep = 0,
+       .usb_resume_timeout = 0,
+       .pulsed_host_wake = 0,
+       .break_to_host = 0
+};
+
+static int bcm_setup_sleep(struct hci_uart *hu)
+{
+       struct bcm_data *bcm = hu->priv;
+       struct sk_buff *skb;
+       struct bcm_set_sleep_mode sleep_params = default_sleep_params;
+
+       sleep_params.host_wake_active = !bcm->dev->irq_polarity;
+
+       skb = __hci_cmd_sync(hu->hdev, 0xfc27, sizeof(sleep_params),
+                            &sleep_params, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               int err = PTR_ERR(skb);
+               bt_dev_err(hu->hdev, "Sleep VSC failed (%d)", err);
+               return err;
+       }
+       kfree_skb(skb);
+
+       bt_dev_dbg(hu->hdev, "Set Sleep Parameters VSC succeeded");
+
+       return 0;
+}
+#else
+static inline int bcm_request_irq(struct bcm_data *bcm) { return 0; }
+static inline int bcm_setup_sleep(struct hci_uart *hu) { return 0; }
+#endif
+
+static int bcm_set_diag(struct hci_dev *hdev, bool enable)
+{
+       struct hci_uart *hu = hci_get_drvdata(hdev);
+       struct bcm_data *bcm = hu->priv;
+       struct sk_buff *skb;
+
+       if (!test_bit(HCI_RUNNING, &hdev->flags))
+               return -ENETDOWN;
+
+       skb = bt_skb_alloc(3, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, 1) = BCM_LM_DIAG_PKT;
+       *skb_put(skb, 1) = 0xf0;
+       *skb_put(skb, 1) = enable;
+
+       skb_queue_tail(&bcm->txq, skb);
+       hci_uart_tx_wakeup(hu);
+
+       return 0;
+}
+
 static int bcm_open(struct hci_uart *hu)
 {
        struct bcm_data *bcm;
        struct list_head *p;
 
-       BT_DBG("hu %p", hu);
+       bt_dev_dbg(hu->hdev, "hu %p", hu);
 
        bcm = kzalloc(sizeof(*bcm), GFP_KERNEL);
        if (!bcm)
@@ -164,7 +287,7 @@ static int bcm_open(struct hci_uart *hu)
 
        hu->priv = bcm;
 
-       spin_lock(&bcm_device_lock);
+       mutex_lock(&bcm_device_lock);
        list_for_each(p, &bcm_device_list) {
                struct bcm_device *dev = list_entry(p, struct bcm_device, list);
 
@@ -175,17 +298,15 @@ static int bcm_open(struct hci_uart *hu)
                if (hu->tty->dev->parent == dev->pdev->dev.parent) {
                        bcm->dev = dev;
                        hu->init_speed = dev->init_speed;
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
                        dev->hu = hu;
 #endif
+                       bcm_gpio_set_power(bcm->dev, true);
                        break;
                }
        }
 
-       if (bcm->dev)
-               bcm_gpio_set_power(bcm->dev, true);
-
-       spin_unlock(&bcm_device_lock);
+       mutex_unlock(&bcm_device_lock);
 
        return 0;
 }
@@ -193,18 +314,27 @@ static int bcm_open(struct hci_uart *hu)
 static int bcm_close(struct hci_uart *hu)
 {
        struct bcm_data *bcm = hu->priv;
+       struct bcm_device *bdev = bcm->dev;
 
-       BT_DBG("hu %p", hu);
+       bt_dev_dbg(hu->hdev, "hu %p", hu);
 
        /* Protect bcm->dev against removal of the device or driver */
-       spin_lock(&bcm_device_lock);
-       if (bcm_device_exists(bcm->dev)) {
-               bcm_gpio_set_power(bcm->dev, false);
-#ifdef CONFIG_PM_SLEEP
-               bcm->dev->hu = NULL;
+       mutex_lock(&bcm_device_lock);
+       if (bcm_device_exists(bdev)) {
+               bcm_gpio_set_power(bdev, false);
+#ifdef CONFIG_PM
+               pm_runtime_disable(&bdev->pdev->dev);
+               pm_runtime_set_suspended(&bdev->pdev->dev);
+
+               if (device_can_wakeup(&bdev->pdev->dev)) {
+                       devm_free_irq(&bdev->pdev->dev, bdev->irq, bdev);
+                       device_init_wakeup(&bdev->pdev->dev, false);
+               }
+
+               bdev->hu = NULL;
 #endif
        }
-       spin_unlock(&bcm_device_lock);
+       mutex_unlock(&bcm_device_lock);
 
        skb_queue_purge(&bcm->txq);
        kfree_skb(bcm->rx_skb);
@@ -218,7 +348,7 @@ static int bcm_flush(struct hci_uart *hu)
 {
        struct bcm_data *bcm = hu->priv;
 
-       BT_DBG("hu %p", hu);
+       bt_dev_dbg(hu->hdev, "hu %p", hu);
 
        skb_queue_purge(&bcm->txq);
 
@@ -227,13 +357,15 @@ static int bcm_flush(struct hci_uart *hu)
 
 static int bcm_setup(struct hci_uart *hu)
 {
+       struct bcm_data *bcm = hu->priv;
        char fw_name[64];
        const struct firmware *fw;
        unsigned int speed;
        int err;
 
-       BT_DBG("hu %p", hu);
+       bt_dev_dbg(hu->hdev, "hu %p", hu);
 
+       hu->hdev->set_diag = bcm_set_diag;
        hu->hdev->set_bdaddr = btbcm_set_bdaddr;
 
        err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name));
@@ -242,13 +374,13 @@ static int bcm_setup(struct hci_uart *hu)
 
        err = request_firmware(&fw, fw_name, &hu->hdev->dev);
        if (err < 0) {
-               BT_INFO("%s: BCM: Patch %s not found", hu->hdev->name, fw_name);
+               bt_dev_info(hu->hdev, "BCM: Patch %s not found", fw_name);
                return 0;
        }
 
        err = btbcm_patchram(hu->hdev, fw);
        if (err) {
-               BT_INFO("%s: BCM: Patch failed (%d)", hu->hdev->name, err);
+               bt_dev_info(hu->hdev, "BCM: Patch failed (%d)", err);
                goto finalize;
        }
 
@@ -281,14 +413,28 @@ finalize:
        release_firmware(fw);
 
        err = btbcm_finalize(hu->hdev);
+       if (err)
+               return err;
+
+       err = bcm_request_irq(bcm);
+       if (!err)
+               err = bcm_setup_sleep(hu);
 
        return err;
 }
 
+#define BCM_RECV_LM_DIAG \
+       .type = BCM_LM_DIAG_PKT, \
+       .hlen = BCM_LM_DIAG_SIZE, \
+       .loff = 0, \
+       .lsize = 0, \
+       .maxlen = BCM_LM_DIAG_SIZE
+
 static const struct h4_recv_pkt bcm_recv_pkts[] = {
-       { H4_RECV_ACL,   .recv = hci_recv_frame },
-       { H4_RECV_SCO,   .recv = hci_recv_frame },
-       { H4_RECV_EVENT, .recv = hci_recv_frame },
+       { H4_RECV_ACL,      .recv = hci_recv_frame },
+       { H4_RECV_SCO,      .recv = hci_recv_frame },
+       { H4_RECV_EVENT,    .recv = hci_recv_frame },
+       { BCM_RECV_LM_DIAG, .recv = hci_recv_diag  },
 };
 
 static int bcm_recv(struct hci_uart *hu, const void *data, int count)
@@ -302,9 +448,18 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count)
                                  bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts));
        if (IS_ERR(bcm->rx_skb)) {
                int err = PTR_ERR(bcm->rx_skb);
-               BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+               bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
                bcm->rx_skb = NULL;
                return err;
+       } else if (!bcm->rx_skb) {
+               /* Delay auto-suspend when receiving completed packet */
+               mutex_lock(&bcm_device_lock);
+               if (bcm->dev && bcm_device_exists(bcm->dev)) {
+                       pm_runtime_get(&bcm->dev->pdev->dev);
+                       pm_runtime_mark_last_busy(&bcm->dev->pdev->dev);
+                       pm_runtime_put_autosuspend(&bcm->dev->pdev->dev);
+               }
+               mutex_unlock(&bcm_device_lock);
        }
 
        return count;
@@ -314,7 +469,7 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
 {
        struct bcm_data *bcm = hu->priv;
 
-       BT_DBG("hu %p skb %p", hu, skb);
+       bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb);
 
        /* Prepend skb with frame type */
        memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
@@ -326,39 +481,105 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
 static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
 {
        struct bcm_data *bcm = hu->priv;
+       struct sk_buff *skb = NULL;
+       struct bcm_device *bdev = NULL;
+
+       mutex_lock(&bcm_device_lock);
+
+       if (bcm_device_exists(bcm->dev)) {
+               bdev = bcm->dev;
+               pm_runtime_get_sync(&bdev->pdev->dev);
+               /* Shall be resumed here */
+       }
+
+       skb = skb_dequeue(&bcm->txq);
+
+       if (bdev) {
+               pm_runtime_mark_last_busy(&bdev->pdev->dev);
+               pm_runtime_put_autosuspend(&bdev->pdev->dev);
+       }
+
+       mutex_unlock(&bcm_device_lock);
 
-       return skb_dequeue(&bcm->txq);
+       return skb;
 }
 
-#ifdef CONFIG_PM_SLEEP
-/* Platform suspend callback */
-static int bcm_suspend(struct device *dev)
+#ifdef CONFIG_PM
+static int bcm_suspend_device(struct device *dev)
 {
        struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
 
-       BT_DBG("suspend (%p): is_suspended %d", bdev, bdev->is_suspended);
+       bt_dev_dbg(bdev, "");
 
-       spin_lock(&bcm_device_lock);
-
-       if (!bdev->hu)
-               goto unlock;
-
-       if (!bdev->is_suspended) {
+       if (!bdev->is_suspended && bdev->hu) {
                hci_uart_set_flow_control(bdev->hu, true);
 
-               /* Once this callback returns, driver suspends BT via GPIO */
+               /* Once this returns, driver suspends BT via GPIO */
                bdev->is_suspended = true;
        }
 
        /* Suspend the device */
        if (bdev->device_wakeup) {
                gpiod_set_value(bdev->device_wakeup, false);
-               BT_DBG("suspend, delaying 15 ms");
+               bt_dev_dbg(bdev, "suspend, delaying 15 ms");
                mdelay(15);
        }
 
+       return 0;
+}
+
+static int bcm_resume_device(struct device *dev)
+{
+       struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
+
+       bt_dev_dbg(bdev, "");
+
+       if (bdev->device_wakeup) {
+               gpiod_set_value(bdev->device_wakeup, true);
+               bt_dev_dbg(bdev, "resume, delaying 15 ms");
+               mdelay(15);
+       }
+
+       /* When this executes, the device has woken up already */
+       if (bdev->is_suspended && bdev->hu) {
+               bdev->is_suspended = false;
+
+               hci_uart_set_flow_control(bdev->hu, false);
+       }
+
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+/* Platform suspend callback */
+static int bcm_suspend(struct device *dev)
+{
+       struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
+       int error;
+
+       bt_dev_dbg(bdev, "suspend: is_suspended %d", bdev->is_suspended);
+
+       /* bcm_suspend can be called at any time as long as platform device is
+        * bound, so it should use bcm_device_lock to protect access to hci_uart
+        * and device_wake-up GPIO.
+        */
+       mutex_lock(&bcm_device_lock);
+
+       if (!bdev->hu)
+               goto unlock;
+
+       if (pm_runtime_active(dev))
+               bcm_suspend_device(dev);
+
+       if (device_may_wakeup(&bdev->pdev->dev)) {
+               error = enable_irq_wake(bdev->irq);
+               if (!error)
+                       bt_dev_dbg(bdev, "BCM irq: enabled");
+       }
+
 unlock:
-       spin_unlock(&bcm_device_lock);
+       mutex_unlock(&bcm_device_lock);
 
        return 0;
 }
@@ -368,28 +589,30 @@ static int bcm_resume(struct device *dev)
 {
        struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
 
-       BT_DBG("resume (%p): is_suspended %d", bdev, bdev->is_suspended);
+       bt_dev_dbg(bdev, "resume: is_suspended %d", bdev->is_suspended);
 
-       spin_lock(&bcm_device_lock);
+       /* bcm_resume can be called at any time as long as platform device is
+        * bound, so it should use bcm_device_lock to protect access to hci_uart
+        * and device_wake-up GPIO.
+        */
+       mutex_lock(&bcm_device_lock);
 
        if (!bdev->hu)
                goto unlock;
 
-       if (bdev->device_wakeup) {
-               gpiod_set_value(bdev->device_wakeup, true);
-               BT_DBG("resume, delaying 15 ms");
-               mdelay(15);
+       if (device_may_wakeup(&bdev->pdev->dev)) {
+               disable_irq_wake(bdev->irq);
+               bt_dev_dbg(bdev, "BCM irq: disabled");
        }
 
-       /* When this callback executes, the device has woken up already */
-       if (bdev->is_suspended) {
-               bdev->is_suspended = false;
-
-               hci_uart_set_flow_control(bdev->hu, false);
-       }
+       bcm_resume_device(dev);
 
 unlock:
-       spin_unlock(&bcm_device_lock);
+       mutex_unlock(&bcm_device_lock);
+
+       pm_runtime_disable(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
 
        return 0;
 }
@@ -397,24 +620,59 @@ unlock:
 
 static const struct acpi_gpio_params device_wakeup_gpios = { 0, 0, false };
 static const struct acpi_gpio_params shutdown_gpios = { 1, 0, false };
+static const struct acpi_gpio_params host_wakeup_gpios = { 2, 0, false };
 
 static const struct acpi_gpio_mapping acpi_bcm_default_gpios[] = {
        { "device-wakeup-gpios", &device_wakeup_gpios, 1 },
        { "shutdown-gpios", &shutdown_gpios, 1 },
+       { "host-wakeup-gpios", &host_wakeup_gpios, 1 },
        { },
 };
 
 #ifdef CONFIG_ACPI
+static u8 acpi_active_low = ACPI_ACTIVE_LOW;
+
+/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */
+static const struct dmi_system_id bcm_wrong_irq_dmi_table[] = {
+       {
+               .ident = "Asus T100TA",
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR,
+                                       "ASUSTeK COMPUTER INC."),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
+               },
+               .driver_data = &acpi_active_low,
+       },
+       { }
+};
+
 static int bcm_resource(struct acpi_resource *ares, void *data)
 {
        struct bcm_device *dev = data;
-
-       if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
-               struct acpi_resource_uart_serialbus *sb;
-
+       struct acpi_resource_extended_irq *irq;
+       struct acpi_resource_gpio *gpio;
+       struct acpi_resource_uart_serialbus *sb;
+
+       switch (ares->type) {
+       case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+               irq = &ares->data.extended_irq;
+               dev->irq_polarity = irq->polarity;
+               break;
+
+       case ACPI_RESOURCE_TYPE_GPIO:
+               gpio = &ares->data.gpio;
+               if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT)
+                       dev->irq_polarity = gpio->polarity;
+               break;
+
+       case ACPI_RESOURCE_TYPE_SERIAL_BUS:
                sb = &ares->data.uart_serial_bus;
                if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_UART)
                        dev->init_speed = sb->default_baud_rate;
+               break;
+
+       default:
+               break;
        }
 
        /* Always tell the ACPI core to skip this resource */
@@ -424,15 +682,10 @@ static int bcm_resource(struct acpi_resource *ares, void *data)
 static int bcm_acpi_probe(struct bcm_device *dev)
 {
        struct platform_device *pdev = dev->pdev;
-       const struct acpi_device_id *id;
-       struct acpi_device *adev;
        LIST_HEAD(resources);
+       const struct dmi_system_id *dmi_id;
        int ret;
 
-       id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
-       if (!id)
-               return -ENODEV;
-
        /* Retrieve GPIO data */
        dev->name = dev_name(&pdev->dev);
        ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
@@ -453,6 +706,21 @@ static int bcm_acpi_probe(struct bcm_device *dev)
        if (IS_ERR(dev->shutdown))
                return PTR_ERR(dev->shutdown);
 
+       /* IRQ can be declared in ACPI table as Interrupt or GpioInt */
+       dev->irq = platform_get_irq(pdev, 0);
+       if (dev->irq <= 0) {
+               struct gpio_desc *gpio;
+
+               gpio = devm_gpiod_get_optional(&pdev->dev, "host-wakeup",
+                                              GPIOD_IN);
+               if (IS_ERR(gpio))
+                       return PTR_ERR(gpio);
+
+               dev->irq = gpiod_to_irq(gpio);
+       }
+
+       dev_info(&pdev->dev, "BCM irq: %d\n", dev->irq);
+
        /* Make sure at-least one of the GPIO is defined and that
         * a name is specified for this instance
         */
@@ -462,11 +730,18 @@ static int bcm_acpi_probe(struct bcm_device *dev)
        }
 
        /* Retrieve UART ACPI info */
-       adev = ACPI_COMPANION(&dev->pdev->dev);
-       if (!adev)
-               return 0;
+       ret = acpi_dev_get_resources(ACPI_COMPANION(&dev->pdev->dev),
+                                    &resources, bcm_resource, dev);
+       if (ret < 0)
+               return ret;
+       acpi_dev_free_resource_list(&resources);
 
-       acpi_dev_get_resources(adev, &resources, bcm_resource, dev);
+       dmi_id = dmi_first_match(bcm_wrong_irq_dmi_table);
+       if (dmi_id) {
+               bt_dev_warn(dev, "%s: Overwriting IRQ polarity to active low",
+                           dmi_id->ident);
+               dev->irq_polarity = *(u8 *)dmi_id->driver_data;
+       }
 
        return 0;
 }
@@ -480,7 +755,6 @@ static int bcm_acpi_probe(struct bcm_device *dev)
 static int bcm_probe(struct platform_device *pdev)
 {
        struct bcm_device *dev;
-       struct acpi_device_id *pdata = pdev->dev.platform_data;
        int ret;
 
        dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
@@ -489,24 +763,18 @@ static int bcm_probe(struct platform_device *pdev)
 
        dev->pdev = pdev;
 
-       if (ACPI_HANDLE(&pdev->dev)) {
-               ret = bcm_acpi_probe(dev);
-               if (ret)
-                       return ret;
-       } else if (pdata) {
-               dev->name = pdata->id;
-       } else {
-               return -ENODEV;
-       }
+       ret = bcm_acpi_probe(dev);
+       if (ret)
+               return ret;
 
        platform_set_drvdata(pdev, dev);
 
        dev_info(&pdev->dev, "%s device registered.\n", dev->name);
 
        /* Place this instance on the device list */
-       spin_lock(&bcm_device_lock);
+       mutex_lock(&bcm_device_lock);
        list_add_tail(&dev->list, &bcm_device_list);
-       spin_unlock(&bcm_device_lock);
+       mutex_unlock(&bcm_device_lock);
 
        bcm_gpio_set_power(dev, false);
 
@@ -517,9 +785,9 @@ static int bcm_remove(struct platform_device *pdev)
 {
        struct bcm_device *dev = platform_get_drvdata(pdev);
 
-       spin_lock(&bcm_device_lock);
+       mutex_lock(&bcm_device_lock);
        list_del(&dev->list);
-       spin_unlock(&bcm_device_lock);
+       mutex_unlock(&bcm_device_lock);
 
        acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pdev->dev));
 
@@ -531,6 +799,7 @@ static int bcm_remove(struct platform_device *pdev)
 static const struct hci_uart_proto bcm_proto = {
        .id             = HCI_UART_BCM,
        .name           = "BCM",
+       .manufacturer   = 15,
        .init_speed     = 115200,
        .oper_speed     = 4000000,
        .open           = bcm_open,
@@ -553,7 +822,10 @@ MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
 #endif
 
 /* Platform suspend and resume callbacks */
-static SIMPLE_DEV_PM_OPS(bcm_pm_ops, bcm_suspend, bcm_resume);
+static const struct dev_pm_ops bcm_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(bcm_suspend, bcm_resume)
+       SET_RUNTIME_PM_OPS(bcm_suspend_device, bcm_resume_device, NULL)
+};
 
 static struct platform_driver bcm_driver = {
        .probe = bcm_probe,
index eec3f28e4bb9ffba89498a19e16e3a3320a164f2..a6fce48da0fb382df7e8600aea320e79ef05713c 100644 (file)
@@ -266,3 +266,4 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
 
        return skb;
 }
+EXPORT_SYMBOL_GPL(h4_recv_buf);
index b35b238a0380197fda6ee096e388a6fc09aa11a7..abee2216fdeb263bb6cc1d1052a070825f5926cd 100644 (file)
@@ -128,7 +128,7 @@ static void h5_timed_event(unsigned long arg)
 {
        const unsigned char sync_req[] = { 0x01, 0x7e };
        unsigned char conf_req[] = { 0x03, 0xfc, 0x01 };
-       struct hci_uart *hu = (struct hci_uart *) arg;
+       struct hci_uart *hu = (struct hci_uart *)arg;
        struct h5 *h5 = hu->priv;
        struct sk_buff *skb;
        unsigned long flags;
@@ -210,7 +210,7 @@ static int h5_open(struct hci_uart *hu)
 
        init_timer(&h5->timer);
        h5->timer.function = h5_timed_event;
-       h5->timer.data = (unsigned long) hu;
+       h5->timer.data = (unsigned long)hu;
 
        h5->tx_win = H5_TX_WIN_MAX;
 
@@ -453,7 +453,7 @@ static int h5_rx_pkt_start(struct hci_uart *hu, unsigned char c)
                return -ENOMEM;
        }
 
-       h5->rx_skb->dev = (void *) hu->hdev;
+       h5->rx_skb->dev = (void *)hu->hdev;
 
        return 0;
 }
@@ -696,7 +696,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
        }
 
        skb = skb_dequeue(&h5->unrel);
-       if (skb != NULL) {
+       if (skb) {
                nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
                                      skb->data, skb->len);
                if (nskb) {
@@ -714,7 +714,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
                goto unlock;
 
        skb = skb_dequeue(&h5->rel);
-       if (skb != NULL) {
+       if (skb) {
                nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
                                      skb->data, skb->len);
                if (nskb) {
index cf07d112195620106282fcbaf40059debc17d461..4a414a5a31655a4d3d0a8a0b03749ef73ff14357 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/platform_device.h>
 #include <linux/gpio/consumer.h>
 #include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #define STATE_FIRMWARE_LOADED  2
 #define STATE_FIRMWARE_FAILED  3
 #define STATE_BOOTING          4
+#define STATE_LPM_ENABLED      5
+#define STATE_TX_ACTIVE                6
+#define STATE_SUSPENDED                7
+#define STATE_LPM_TRANSACTION  8
+
+#define HCI_LPM_WAKE_PKT 0xf0
+#define HCI_LPM_PKT 0xf1
+#define HCI_LPM_MAX_SIZE 10
+#define HCI_LPM_HDR_SIZE HCI_EVENT_HDR_SIZE
+
+#define LPM_OP_TX_NOTIFY 0x00
+#define LPM_OP_SUSPEND_ACK 0x02
+#define LPM_OP_RESUME_ACK 0x03
+
+#define LPM_SUSPEND_DELAY_MS 1000
+
+struct hci_lpm_pkt {
+       __u8 opcode;
+       __u8 dlen;
+       __u8 data[0];
+} __packed;
 
 struct intel_device {
        struct list_head list;
        struct platform_device *pdev;
        struct gpio_desc *reset;
+       struct hci_uart *hu;
+       struct mutex hu_lock;
+       int irq;
 };
 
 static LIST_HEAD(intel_device_list);
-static DEFINE_SPINLOCK(intel_device_list_lock);
+static DEFINE_MUTEX(intel_device_list_lock);
 
 struct intel_data {
        struct sk_buff *rx_skb;
        struct sk_buff_head txq;
+       struct work_struct busy_work;
+       struct hci_uart *hu;
        unsigned long flags;
 };
 
@@ -101,24 +129,185 @@ static int intel_wait_booting(struct hci_uart *hu)
                                  msecs_to_jiffies(1000));
 
        if (err == 1) {
-               BT_ERR("%s: Device boot interrupted", hu->hdev->name);
+               bt_dev_err(hu->hdev, "Device boot interrupted");
                return -EINTR;
        }
 
        if (err) {
-               BT_ERR("%s: Device boot timeout", hu->hdev->name);
+               bt_dev_err(hu->hdev, "Device boot timeout");
                return -ETIMEDOUT;
        }
 
        return err;
 }
 
+#ifdef CONFIG_PM
+static int intel_wait_lpm_transaction(struct hci_uart *hu)
+{
+       struct intel_data *intel = hu->priv;
+       int err;
+
+       err = wait_on_bit_timeout(&intel->flags, STATE_LPM_TRANSACTION,
+                                 TASK_INTERRUPTIBLE,
+                                 msecs_to_jiffies(1000));
+
+       if (err == 1) {
+               bt_dev_err(hu->hdev, "LPM transaction interrupted");
+               return -EINTR;
+       }
+
+       if (err) {
+               bt_dev_err(hu->hdev, "LPM transaction timeout");
+               return -ETIMEDOUT;
+       }
+
+       return err;
+}
+
+static int intel_lpm_suspend(struct hci_uart *hu)
+{
+       static const u8 suspend[] = { 0x01, 0x01, 0x01 };
+       struct intel_data *intel = hu->priv;
+       struct sk_buff *skb;
+
+       if (!test_bit(STATE_LPM_ENABLED, &intel->flags) ||
+           test_bit(STATE_SUSPENDED, &intel->flags))
+               return 0;
+
+       if (test_bit(STATE_TX_ACTIVE, &intel->flags))
+               return -EAGAIN;
+
+       bt_dev_dbg(hu->hdev, "Suspending");
+
+       skb = bt_skb_alloc(sizeof(suspend), GFP_KERNEL);
+       if (!skb) {
+               bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet");
+               return -ENOMEM;
+       }
+
+       memcpy(skb_put(skb, sizeof(suspend)), suspend, sizeof(suspend));
+       bt_cb(skb)->pkt_type = HCI_LPM_PKT;
+
+       set_bit(STATE_LPM_TRANSACTION, &intel->flags);
+
+       /* LPM flow is a priority, enqueue packet at list head */
+       skb_queue_head(&intel->txq, skb);
+       hci_uart_tx_wakeup(hu);
+
+       intel_wait_lpm_transaction(hu);
+       /* Even in case of failure, continue and test the suspended flag */
+
+       clear_bit(STATE_LPM_TRANSACTION, &intel->flags);
+
+       if (!test_bit(STATE_SUSPENDED, &intel->flags)) {
+               bt_dev_err(hu->hdev, "Device suspend error");
+               return -EINVAL;
+       }
+
+       bt_dev_dbg(hu->hdev, "Suspended");
+
+       hci_uart_set_flow_control(hu, true);
+
+       return 0;
+}
+
+static int intel_lpm_resume(struct hci_uart *hu)
+{
+       struct intel_data *intel = hu->priv;
+       struct sk_buff *skb;
+
+       if (!test_bit(STATE_LPM_ENABLED, &intel->flags) ||
+           !test_bit(STATE_SUSPENDED, &intel->flags))
+               return 0;
+
+       bt_dev_dbg(hu->hdev, "Resuming");
+
+       hci_uart_set_flow_control(hu, false);
+
+       skb = bt_skb_alloc(0, GFP_KERNEL);
+       if (!skb) {
+               bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet");
+               return -ENOMEM;
+       }
+
+       bt_cb(skb)->pkt_type = HCI_LPM_WAKE_PKT;
+
+       set_bit(STATE_LPM_TRANSACTION, &intel->flags);
+
+       /* LPM flow is a priority, enqueue packet at list head */
+       skb_queue_head(&intel->txq, skb);
+       hci_uart_tx_wakeup(hu);
+
+       intel_wait_lpm_transaction(hu);
+       /* Even in case of failure, continue and test the suspended flag */
+
+       clear_bit(STATE_LPM_TRANSACTION, &intel->flags);
+
+       if (test_bit(STATE_SUSPENDED, &intel->flags)) {
+               bt_dev_err(hu->hdev, "Device resume error");
+               return -EINVAL;
+       }
+
+       bt_dev_dbg(hu->hdev, "Resumed");
+
+       return 0;
+}
+#endif /* CONFIG_PM */
+
+static int intel_lpm_host_wake(struct hci_uart *hu)
+{
+       static const u8 lpm_resume_ack[] = { LPM_OP_RESUME_ACK, 0x00 };
+       struct intel_data *intel = hu->priv;
+       struct sk_buff *skb;
+
+       hci_uart_set_flow_control(hu, false);
+
+       clear_bit(STATE_SUSPENDED, &intel->flags);
+
+       skb = bt_skb_alloc(sizeof(lpm_resume_ack), GFP_KERNEL);
+       if (!skb) {
+               bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet");
+               return -ENOMEM;
+       }
+
+       memcpy(skb_put(skb, sizeof(lpm_resume_ack)), lpm_resume_ack,
+              sizeof(lpm_resume_ack));
+       bt_cb(skb)->pkt_type = HCI_LPM_PKT;
+
+       /* LPM flow is a priority, enqueue packet at list head */
+       skb_queue_head(&intel->txq, skb);
+       hci_uart_tx_wakeup(hu);
+
+       bt_dev_dbg(hu->hdev, "Resumed by controller");
+
+       return 0;
+}
+
+static irqreturn_t intel_irq(int irq, void *dev_id)
+{
+       struct intel_device *idev = dev_id;
+
+       dev_info(&idev->pdev->dev, "hci_intel irq\n");
+
+       mutex_lock(&idev->hu_lock);
+       if (idev->hu)
+               intel_lpm_host_wake(idev->hu);
+       mutex_unlock(&idev->hu_lock);
+
+       /* Host/Controller are now LPM resumed, trigger a new delayed suspend */
+       pm_runtime_get(&idev->pdev->dev);
+       pm_runtime_mark_last_busy(&idev->pdev->dev);
+       pm_runtime_put_autosuspend(&idev->pdev->dev);
+
+       return IRQ_HANDLED;
+}
+
 static int intel_set_power(struct hci_uart *hu, bool powered)
 {
        struct list_head *p;
        int err = -ENODEV;
 
-       spin_lock(&intel_device_list_lock);
+       mutex_lock(&intel_device_list_lock);
 
        list_for_each(p, &intel_device_list) {
                struct intel_device *idev = list_entry(p, struct intel_device,
@@ -139,13 +328,73 @@ static int intel_set_power(struct hci_uart *hu, bool powered)
                        hu, dev_name(&idev->pdev->dev), powered);
 
                gpiod_set_value(idev->reset, powered);
+
+               /* Provide to idev a hu reference which is used to run LPM
+                * transactions (lpm suspend/resume) from PM callbacks.
+                * hu needs to be protected against concurrent removing during
+                * these PM ops.
+                */
+               mutex_lock(&idev->hu_lock);
+               idev->hu = powered ? hu : NULL;
+               mutex_unlock(&idev->hu_lock);
+
+               if (idev->irq < 0)
+                       break;
+
+               if (powered && device_can_wakeup(&idev->pdev->dev)) {
+                       err = devm_request_threaded_irq(&idev->pdev->dev,
+                                                       idev->irq, NULL,
+                                                       intel_irq,
+                                                       IRQF_ONESHOT,
+                                                       "bt-host-wake", idev);
+                       if (err) {
+                               BT_ERR("hu %p, unable to allocate irq-%d",
+                                      hu, idev->irq);
+                               break;
+                       }
+
+                       device_wakeup_enable(&idev->pdev->dev);
+
+                       pm_runtime_set_active(&idev->pdev->dev);
+                       pm_runtime_use_autosuspend(&idev->pdev->dev);
+                       pm_runtime_set_autosuspend_delay(&idev->pdev->dev,
+                                                        LPM_SUSPEND_DELAY_MS);
+                       pm_runtime_enable(&idev->pdev->dev);
+               } else if (!powered && device_may_wakeup(&idev->pdev->dev)) {
+                       devm_free_irq(&idev->pdev->dev, idev->irq, idev);
+                       device_wakeup_disable(&idev->pdev->dev);
+
+                       pm_runtime_disable(&idev->pdev->dev);
+               }
        }
 
-       spin_unlock(&intel_device_list_lock);
+       mutex_unlock(&intel_device_list_lock);
 
        return err;
 }
 
+static void intel_busy_work(struct work_struct *work)
+{
+       struct list_head *p;
+       struct intel_data *intel = container_of(work, struct intel_data,
+                                               busy_work);
+
+       /* Link is busy, delay the suspend */
+       mutex_lock(&intel_device_list_lock);
+       list_for_each(p, &intel_device_list) {
+               struct intel_device *idev = list_entry(p, struct intel_device,
+                                                      list);
+
+               if (intel->hu->tty->dev->parent == idev->pdev->dev.parent) {
+                       pm_runtime_get(&idev->pdev->dev);
+                       pm_runtime_mark_last_busy(&idev->pdev->dev);
+                       pm_runtime_put_autosuspend(&idev->pdev->dev);
+                       break;
+               }
+       }
+       mutex_unlock(&intel_device_list_lock);
+}
+
 static int intel_open(struct hci_uart *hu)
 {
        struct intel_data *intel;
@@ -157,6 +406,9 @@ static int intel_open(struct hci_uart *hu)
                return -ENOMEM;
 
        skb_queue_head_init(&intel->txq);
+       INIT_WORK(&intel->busy_work, intel_busy_work);
+
+       intel->hu = hu;
 
        hu->priv = intel;
 
@@ -172,6 +424,8 @@ static int intel_close(struct hci_uart *hu)
 
        BT_DBG("hu %p", hu);
 
+       cancel_work_sync(&intel->busy_work);
+
        intel_set_power(hu, false);
 
        skb_queue_purge(&intel->txq);
@@ -237,11 +491,11 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed)
        if (err && err != ETIMEDOUT)
                return err;
 
-       BT_INFO("%s: Change controller speed to %d", hdev->name, speed);
+       bt_dev_info(hdev, "Change controller speed to %d", speed);
 
        speed_cmd[3] = intel_convert_speed(speed);
        if (speed_cmd[3] == 0xff) {
-               BT_ERR("%s: Unsupported speed", hdev->name);
+               bt_dev_err(hdev, "Unsupported speed");
                return -EINVAL;
        }
 
@@ -250,16 +504,15 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed)
         */
        skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
        if (IS_ERR(skb)) {
-               BT_ERR("%s: Reading Intel version information failed (%ld)",
-                      hdev->name, PTR_ERR(skb));
+               bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
+                          PTR_ERR(skb));
                return PTR_ERR(skb);
        }
        kfree_skb(skb);
 
        skb = bt_skb_alloc(sizeof(speed_cmd), GFP_KERNEL);
        if (!skb) {
-               BT_ERR("%s: Failed to allocate memory for baudrate packet",
-                      hdev->name);
+               bt_dev_err(hdev, "Failed to alloc memory for baudrate packet");
                return -ENOMEM;
        }
 
@@ -284,11 +537,14 @@ static int intel_setup(struct hci_uart *hu)
 {
        static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01,
                                          0x00, 0x08, 0x04, 0x00 };
+       static const u8 lpm_param[] = { 0x03, 0x07, 0x01, 0x0b };
        struct intel_data *intel = hu->priv;
+       struct intel_device *idev = NULL;
        struct hci_dev *hdev = hu->hdev;
        struct sk_buff *skb;
        struct intel_version *ver;
        struct intel_boot_params *params;
+       struct list_head *p;
        const struct firmware *fw;
        const u8 *fw_ptr;
        char fwname[64];
@@ -299,8 +555,9 @@ static int intel_setup(struct hci_uart *hu)
        int speed_change = 0;
        int err;
 
-       BT_DBG("%s", hdev->name);
+       bt_dev_dbg(hdev, "start intel_setup");
 
+       hu->hdev->set_diag = btintel_set_diag;
        hu->hdev->set_bdaddr = btintel_set_bdaddr;
 
        calltime = ktime_get();
@@ -335,21 +592,21 @@ static int intel_setup(struct hci_uart *hu)
         */
        skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
        if (IS_ERR(skb)) {
-               BT_ERR("%s: Reading Intel version information failed (%ld)",
-                      hdev->name, PTR_ERR(skb));
+               bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
+                          PTR_ERR(skb));
                return PTR_ERR(skb);
        }
 
        if (skb->len != sizeof(*ver)) {
-               BT_ERR("%s: Intel version event size mismatch", hdev->name);
+               bt_dev_err(hdev, "Intel version event size mismatch");
                kfree_skb(skb);
                return -EILSEQ;
        }
 
        ver = (struct intel_version *)skb->data;
        if (ver->status) {
-               BT_ERR("%s: Intel version command failure (%02x)",
-                      hdev->name, ver->status);
+               bt_dev_err(hdev, "Intel version command failure (%02x)",
+                          ver->status);
                err = -bt_to_errno(ver->status);
                kfree_skb(skb);
                return err;
@@ -359,8 +616,8 @@ static int intel_setup(struct hci_uart *hu)
         * for now only accept this single value.
         */
        if (ver->hw_platform != 0x37) {
-               BT_ERR("%s: Unsupported Intel hardware platform (%u)",
-                      hdev->name, ver->hw_platform);
+               bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)",
+                          ver->hw_platform);
                kfree_skb(skb);
                return -EINVAL;
        }
@@ -371,8 +628,8 @@ static int intel_setup(struct hci_uart *hu)
         * when newer hardware variants come along.
         */
        if (ver->hw_variant != 0x0b) {
-               BT_ERR("%s: Unsupported Intel hardware variant (%u)",
-                      hdev->name, ver->hw_variant);
+               bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)",
+                          ver->hw_variant);
                kfree_skb(skb);
                return -EINVAL;
        }
@@ -403,8 +660,8 @@ static int intel_setup(struct hci_uart *hu)
         * choice is to return an error and abort the device initialization.
         */
        if (ver->fw_variant != 0x06) {
-               BT_ERR("%s: Unsupported Intel firmware variant (%u)",
-                      hdev->name, ver->fw_variant);
+               bt_dev_err(hdev, "Unsupported Intel firmware variant (%u)",
+                          ver->fw_variant);
                kfree_skb(skb);
                return -ENODEV;
        }
@@ -416,33 +673,33 @@ static int intel_setup(struct hci_uart *hu)
         */
        skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT);
        if (IS_ERR(skb)) {
-               BT_ERR("%s: Reading Intel boot parameters failed (%ld)",
-                      hdev->name, PTR_ERR(skb));
+               bt_dev_err(hdev, "Reading Intel boot parameters failed (%ld)",
+                          PTR_ERR(skb));
                return PTR_ERR(skb);
        }
 
        if (skb->len != sizeof(*params)) {
-               BT_ERR("%s: Intel boot parameters size mismatch", hdev->name);
+               bt_dev_err(hdev, "Intel boot parameters size mismatch");
                kfree_skb(skb);
                return -EILSEQ;
        }
 
        params = (struct intel_boot_params *)skb->data;
        if (params->status) {
-               BT_ERR("%s: Intel boot parameters command failure (%02x)",
-                      hdev->name, params->status);
+               bt_dev_err(hdev, "Intel boot parameters command failure (%02x)",
+                          params->status);
                err = -bt_to_errno(params->status);
                kfree_skb(skb);
                return err;
        }
 
-       BT_INFO("%s: Device revision is %u", hdev->name,
-               le16_to_cpu(params->dev_revid));
+       bt_dev_info(hdev, "Device revision is %u",
+                   le16_to_cpu(params->dev_revid));
 
-       BT_INFO("%s: Secure boot is %s", hdev->name,
-               params->secure_boot ? "enabled" : "disabled");
+       bt_dev_info(hdev, "Secure boot is %s",
+                   params->secure_boot ? "enabled" : "disabled");
 
-       BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name,
+       bt_dev_info(hdev, "Minimum firmware build %u week %u %u",
                params->min_fw_build_nn, params->min_fw_build_cw,
                2000 + params->min_fw_build_yy);
 
@@ -451,8 +708,8 @@ static int intel_setup(struct hci_uart *hu)
         * that this bootloader does not send them, then abort the setup.
         */
        if (params->limited_cce != 0x00) {
-               BT_ERR("%s: Unsupported Intel firmware loading method (%u)",
-                      hdev->name, params->limited_cce);
+               bt_dev_err(hdev, "Unsupported Intel firmware loading method (%u)",
+                          params->limited_cce);
                kfree_skb(skb);
                return -EINVAL;
        }
@@ -461,7 +718,7 @@ static int intel_setup(struct hci_uart *hu)
         * also be no valid address for the operational firmware.
         */
        if (!bacmp(&params->otp_bdaddr, BDADDR_ANY)) {
-               BT_INFO("%s: No device address configured", hdev->name);
+               bt_dev_info(hdev, "No device address configured");
                set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
        }
 
@@ -476,19 +733,23 @@ static int intel_setup(struct hci_uart *hu)
 
        err = request_firmware(&fw, fwname, &hdev->dev);
        if (err < 0) {
-               BT_ERR("%s: Failed to load Intel firmware file (%d)",
-                      hdev->name, err);
+               bt_dev_err(hdev, "Failed to load Intel firmware file (%d)",
+                          err);
                kfree_skb(skb);
                return err;
        }
 
-       BT_INFO("%s: Found device firmware: %s", hdev->name, fwname);
+       bt_dev_info(hdev, "Found device firmware: %s", fwname);
+
+       /* Save the DDC file name for later */
+       snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.ddc",
+                le16_to_cpu(params->dev_revid));
 
        kfree_skb(skb);
 
        if (fw->size < 644) {
-               BT_ERR("%s: Invalid size of firmware file (%zu)",
-                      hdev->name, fw->size);
+               bt_dev_err(hdev, "Invalid size of firmware file (%zu)",
+                          fw->size);
                err = -EBADF;
                goto done;
        }
@@ -500,8 +761,7 @@ static int intel_setup(struct hci_uart *hu)
         */
        err = btintel_secure_send(hdev, 0x00, 128, fw->data);
        if (err < 0) {
-               BT_ERR("%s: Failed to send firmware header (%d)",
-                      hdev->name, err);
+               bt_dev_err(hdev, "Failed to send firmware header (%d)", err);
                goto done;
        }
 
@@ -510,8 +770,8 @@ static int intel_setup(struct hci_uart *hu)
         */
        err = btintel_secure_send(hdev, 0x03, 256, fw->data + 128);
        if (err < 0) {
-               BT_ERR("%s: Failed to send firmware public key (%d)",
-                      hdev->name, err);
+               bt_dev_err(hdev, "Failed to send firmware public key (%d)",
+                          err);
                goto done;
        }
 
@@ -520,8 +780,8 @@ static int intel_setup(struct hci_uart *hu)
         */
        err = btintel_secure_send(hdev, 0x02, 256, fw->data + 388);
        if (err < 0) {
-               BT_ERR("%s: Failed to send firmware signature (%d)",
-                      hdev->name, err);
+               bt_dev_err(hdev, "Failed to send firmware signature (%d)",
+                          err);
                goto done;
        }
 
@@ -533,8 +793,8 @@ static int intel_setup(struct hci_uart *hu)
 
                frag_len += sizeof(*cmd) + cmd->plen;
 
-               BT_DBG("%s: patching %td/%zu", hdev->name,
-                      (fw_ptr - fw->data), fw->size);
+               bt_dev_dbg(hdev, "Patching %td/%zu", (fw_ptr - fw->data),
+                          fw->size);
 
                /* The parameter length of the secure send command requires
                 * a 4 byte alignment. It happens so that the firmware file
@@ -552,8 +812,8 @@ static int intel_setup(struct hci_uart *hu)
                 */
                err = btintel_secure_send(hdev, 0x01, frag_len, fw_ptr);
                if (err < 0) {
-                       BT_ERR("%s: Failed to send firmware data (%d)",
-                              hdev->name, err);
+                       bt_dev_err(hdev, "Failed to send firmware data (%d)",
+                                  err);
                        goto done;
                }
 
@@ -563,7 +823,7 @@ static int intel_setup(struct hci_uart *hu)
 
        set_bit(STATE_FIRMWARE_LOADED, &intel->flags);
 
-       BT_INFO("%s: Waiting for firmware download to complete", hdev->name);
+       bt_dev_info(hdev, "Waiting for firmware download to complete");
 
        /* Before switching the device into operational mode and with that
         * booting the loaded firmware, wait for the bootloader notification
@@ -580,19 +840,19 @@ static int intel_setup(struct hci_uart *hu)
                                  TASK_INTERRUPTIBLE,
                                  msecs_to_jiffies(5000));
        if (err == 1) {
-               BT_ERR("%s: Firmware loading interrupted", hdev->name);
+               bt_dev_err(hdev, "Firmware loading interrupted");
                err = -EINTR;
                goto done;
        }
 
        if (err) {
-               BT_ERR("%s: Firmware loading timeout", hdev->name);
+               bt_dev_err(hdev, "Firmware loading timeout");
                err = -ETIMEDOUT;
                goto done;
        }
 
        if (test_bit(STATE_FIRMWARE_FAILED, &intel->flags)) {
-               BT_ERR("%s: Firmware loading failed", hdev->name);
+               bt_dev_err(hdev, "Firmware loading failed");
                err = -ENOEXEC;
                goto done;
        }
@@ -601,7 +861,7 @@ static int intel_setup(struct hci_uart *hu)
        delta = ktime_sub(rettime, calltime);
        duration = (unsigned long long) ktime_to_ns(delta) >> 10;
 
-       BT_INFO("%s: Firmware loaded in %llu usecs", hdev->name, duration);
+       bt_dev_info(hdev, "Firmware loaded in %llu usecs", duration);
 
 done:
        release_firmware(fw);
@@ -634,7 +894,7 @@ done:
         * 1 second. However if that happens, then just fail the setup
         * since something went wrong.
         */
-       BT_INFO("%s: Waiting for device to boot", hdev->name);
+       bt_dev_info(hdev, "Waiting for device to boot");
 
        err = intel_wait_booting(hu);
        if (err)
@@ -646,7 +906,39 @@ done:
        delta = ktime_sub(rettime, calltime);
        duration = (unsigned long long) ktime_to_ns(delta) >> 10;
 
-       BT_INFO("%s: Device booted in %llu usecs", hdev->name, duration);
+       bt_dev_info(hdev, "Device booted in %llu usecs", duration);
+
+       /* Enable LPM if matching pdev with wakeup enabled */
+       mutex_lock(&intel_device_list_lock);
+       list_for_each(p, &intel_device_list) {
+               struct intel_device *dev = list_entry(p, struct intel_device,
+                                                     list);
+               if (hu->tty->dev->parent == dev->pdev->dev.parent) {
+                       if (device_may_wakeup(&dev->pdev->dev))
+                               idev = dev;
+                       break;
+               }
+       }
+       mutex_unlock(&intel_device_list_lock);
+
+       if (!idev)
+               goto no_lpm;
+
+       bt_dev_info(hdev, "Enabling LPM");
+
+       skb = __hci_cmd_sync(hdev, 0xfc8b, sizeof(lpm_param), lpm_param,
+                            HCI_CMD_TIMEOUT);
+       if (IS_ERR(skb)) {
+               bt_dev_err(hdev, "Failed to enable LPM");
+               goto no_lpm;
+       }
+       kfree_skb(skb);
+
+       set_bit(STATE_LPM_ENABLED, &intel->flags);
+
+no_lpm:
+       /* Ignore errors, device can work without DDC parameters */
+       btintel_load_ddc_config(hdev, fwname);
 
        skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_CMD_TIMEOUT);
        if (IS_ERR(skb))
@@ -659,7 +951,7 @@ done:
                        return err;
        }
 
-       BT_INFO("%s: Setup complete", hdev->name);
+       bt_dev_info(hdev, "Setup complete");
 
        clear_bit(STATE_BOOTLOADER, &intel->flags);
 
@@ -708,10 +1000,71 @@ recv:
        return hci_recv_frame(hdev, skb);
 }
 
+static void intel_recv_lpm_notify(struct hci_dev *hdev, int value)
+{
+       struct hci_uart *hu = hci_get_drvdata(hdev);
+       struct intel_data *intel = hu->priv;
+
+       bt_dev_dbg(hdev, "TX idle notification (%d)", value);
+
+       if (value) {
+               set_bit(STATE_TX_ACTIVE, &intel->flags);
+               schedule_work(&intel->busy_work);
+       } else {
+               clear_bit(STATE_TX_ACTIVE, &intel->flags);
+       }
+}
+
+static int intel_recv_lpm(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_lpm_pkt *lpm = (void *)skb->data;
+       struct hci_uart *hu = hci_get_drvdata(hdev);
+       struct intel_data *intel = hu->priv;
+
+       switch (lpm->opcode) {
+       case LPM_OP_TX_NOTIFY:
+               if (lpm->dlen < 1) {
+                       bt_dev_err(hu->hdev, "Invalid LPM notification packet");
+                       break;
+               }
+               intel_recv_lpm_notify(hdev, lpm->data[0]);
+               break;
+       case LPM_OP_SUSPEND_ACK:
+               set_bit(STATE_SUSPENDED, &intel->flags);
+               if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags)) {
+                       smp_mb__after_atomic();
+                       wake_up_bit(&intel->flags, STATE_LPM_TRANSACTION);
+               }
+               break;
+       case LPM_OP_RESUME_ACK:
+               clear_bit(STATE_SUSPENDED, &intel->flags);
+               if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags)) {
+                       smp_mb__after_atomic();
+                       wake_up_bit(&intel->flags, STATE_LPM_TRANSACTION);
+               }
+               break;
+       default:
+               bt_dev_err(hdev, "Unknown LPM opcode (%02x)", lpm->opcode);
+               break;
+       }
+
+       kfree_skb(skb);
+
+       return 0;
+}
+
+#define INTEL_RECV_LPM \
+       .type = HCI_LPM_PKT, \
+       .hlen = HCI_LPM_HDR_SIZE, \
+       .loff = 1, \
+       .lsize = 1, \
+       .maxlen = HCI_LPM_MAX_SIZE
+
 static const struct h4_recv_pkt intel_recv_pkts[] = {
-       { H4_RECV_ACL,   .recv = hci_recv_frame },
-       { H4_RECV_SCO,   .recv = hci_recv_frame },
-       { H4_RECV_EVENT, .recv = intel_recv_event },
+       { H4_RECV_ACL,    .recv = hci_recv_frame   },
+       { H4_RECV_SCO,    .recv = hci_recv_frame   },
+       { H4_RECV_EVENT,  .recv = intel_recv_event },
+       { INTEL_RECV_LPM, .recv = intel_recv_lpm   },
 };
 
 static int intel_recv(struct hci_uart *hu, const void *data, int count)
@@ -726,7 +1079,7 @@ static int intel_recv(struct hci_uart *hu, const void *data, int count)
                                    ARRAY_SIZE(intel_recv_pkts));
        if (IS_ERR(intel->rx_skb)) {
                int err = PTR_ERR(intel->rx_skb);
-               BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+               bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
                intel->rx_skb = NULL;
                return err;
        }
@@ -737,9 +1090,27 @@ static int intel_recv(struct hci_uart *hu, const void *data, int count)
 static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
 {
        struct intel_data *intel = hu->priv;
+       struct list_head *p;
 
        BT_DBG("hu %p skb %p", hu, skb);
 
+       /* Be sure our controller is resumed and potential LPM transaction
+        * completed before enqueuing any packet.
+        */
+       mutex_lock(&intel_device_list_lock);
+       list_for_each(p, &intel_device_list) {
+               struct intel_device *idev = list_entry(p, struct intel_device,
+                                                      list);
+
+               if (hu->tty->dev->parent == idev->pdev->dev.parent) {
+                       pm_runtime_get_sync(&idev->pdev->dev);
+                       pm_runtime_mark_last_busy(&idev->pdev->dev);
+                       pm_runtime_put_autosuspend(&idev->pdev->dev);
+                       break;
+               }
+       }
+       mutex_unlock(&intel_device_list_lock);
+
        skb_queue_tail(&intel->txq, skb);
 
        return 0;
@@ -777,6 +1148,7 @@ static struct sk_buff *intel_dequeue(struct hci_uart *hu)
 static const struct hci_uart_proto intel_proto = {
        .id             = HCI_UART_INTEL,
        .name           = "Intel",
+       .manufacturer   = 2,
        .init_speed     = 115200,
        .oper_speed     = 3000000,
        .open           = intel_open,
@@ -795,24 +1167,61 @@ static const struct acpi_device_id intel_acpi_match[] = {
        { },
 };
 MODULE_DEVICE_TABLE(acpi, intel_acpi_match);
+#endif
 
-static int intel_acpi_probe(struct intel_device *idev)
+#ifdef CONFIG_PM
+static int intel_suspend_device(struct device *dev)
 {
-       const struct acpi_device_id *id;
+       struct intel_device *idev = dev_get_drvdata(dev);
 
-       id = acpi_match_device(intel_acpi_match, &idev->pdev->dev);
-       if (!id)
-               return -ENODEV;
+       mutex_lock(&idev->hu_lock);
+       if (idev->hu)
+               intel_lpm_suspend(idev->hu);
+       mutex_unlock(&idev->hu_lock);
 
        return 0;
 }
-#else
-static int intel_acpi_probe(struct intel_device *idev)
+
+static int intel_resume_device(struct device *dev)
+{
+       struct intel_device *idev = dev_get_drvdata(dev);
+
+       mutex_lock(&idev->hu_lock);
+       if (idev->hu)
+               intel_lpm_resume(idev->hu);
+       mutex_unlock(&idev->hu_lock);
+
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int intel_suspend(struct device *dev)
+{
+       struct intel_device *idev = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev))
+               enable_irq_wake(idev->irq);
+
+       return intel_suspend_device(dev);
+}
+
+static int intel_resume(struct device *dev)
 {
-       return -ENODEV;
+       struct intel_device *idev = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev))
+               disable_irq_wake(idev->irq);
+
+       return intel_resume_device(dev);
 }
 #endif
 
+static const struct dev_pm_ops intel_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume)
+       SET_RUNTIME_PM_OPS(intel_suspend_device, intel_resume_device, NULL)
+};
+
 static int intel_probe(struct platform_device *pdev)
 {
        struct intel_device *idev;
@@ -821,15 +1230,9 @@ static int intel_probe(struct platform_device *pdev)
        if (!idev)
                return -ENOMEM;
 
-       idev->pdev = pdev;
+       mutex_init(&idev->hu_lock);
 
-       if (ACPI_HANDLE(&pdev->dev)) {
-               int err = intel_acpi_probe(idev);
-               if (err)
-                       return err;
-       } else {
-               return -ENODEV;
-       }
+       idev->pdev = pdev;
 
        idev->reset = devm_gpiod_get_optional(&pdev->dev, "reset",
                                              GPIOD_OUT_LOW);
@@ -838,14 +1241,40 @@ static int intel_probe(struct platform_device *pdev)
                return PTR_ERR(idev->reset);
        }
 
+       idev->irq = platform_get_irq(pdev, 0);
+       if (idev->irq < 0) {
+               struct gpio_desc *host_wake;
+
+               dev_err(&pdev->dev, "No IRQ, falling back to gpio-irq\n");
+
+               host_wake = devm_gpiod_get_optional(&pdev->dev, "host-wake",
+                                                   GPIOD_IN);
+               if (IS_ERR(host_wake)) {
+                       dev_err(&pdev->dev, "Unable to retrieve IRQ\n");
+                       goto no_irq;
+               }
+
+               idev->irq = gpiod_to_irq(host_wake);
+               if (idev->irq < 0) {
+                       dev_err(&pdev->dev, "No corresponding irq for gpio\n");
+                       goto no_irq;
+               }
+       }
+
+       /* Only enable wake-up/irq when controller is powered */
+       device_set_wakeup_capable(&pdev->dev, true);
+       device_wakeup_disable(&pdev->dev);
+
+no_irq:
        platform_set_drvdata(pdev, idev);
 
        /* Place this instance on the device list */
-       spin_lock(&intel_device_list_lock);
+       mutex_lock(&intel_device_list_lock);
        list_add_tail(&idev->list, &intel_device_list);
-       spin_unlock(&intel_device_list_lock);
+       mutex_unlock(&intel_device_list_lock);
 
-       dev_info(&pdev->dev, "registered.\n");
+       dev_info(&pdev->dev, "registered, gpio(%d)/irq(%d).\n",
+                desc_to_gpio(idev->reset), idev->irq);
 
        return 0;
 }
@@ -854,9 +1283,11 @@ static int intel_remove(struct platform_device *pdev)
 {
        struct intel_device *idev = platform_get_drvdata(pdev);
 
-       spin_lock(&intel_device_list_lock);
+       device_wakeup_disable(&pdev->dev);
+
+       mutex_lock(&intel_device_list_lock);
        list_del(&idev->list);
-       spin_unlock(&intel_device_list_lock);
+       mutex_unlock(&intel_device_list_lock);
 
        dev_info(&pdev->dev, "unregistered.\n");
 
@@ -869,6 +1300,7 @@ static struct platform_driver intel_driver = {
        .driver = {
                .name = "hci_intel",
                .acpi_match_table = ACPI_PTR(intel_acpi_match),
+               .pm = &intel_pm_ops,
        },
 };
 
index 0d5a05a7c1fd1d46a8b77d71b94f97a9df4190b3..96bcec5598c221e4cec2841bbc4eb521d20778c1 100644 (file)
@@ -208,9 +208,6 @@ static int hci_uart_open(struct hci_dev *hdev)
        BT_DBG("%s %p", hdev->name, hdev);
 
        /* Nothing to do for UART driver */
-
-       set_bit(HCI_RUNNING, &hdev->flags);
-
        return 0;
 }
 
@@ -241,9 +238,6 @@ static int hci_uart_close(struct hci_dev *hdev)
 {
        BT_DBG("hdev %p", hdev);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        hci_uart_flush(hdev);
        hdev->flush = NULL;
        return 0;
@@ -254,9 +248,6 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_uart *hu = hci_get_drvdata(hdev);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
 
        hu->proto->enqueue(hu, skb);
@@ -470,8 +461,6 @@ static int hci_uart_tty_open(struct tty_struct *tty)
        INIT_WORK(&hu->init_ready, hci_uart_init_work);
        INIT_WORK(&hu->write_work, hci_uart_write_work);
 
-       spin_lock_init(&hu->rx_lock);
-
        /* Flush any pending characters in the driver and line discipline. */
 
        /* FIXME: why is this needed. Note don't use ldisc_ref here as the
@@ -569,14 +558,14 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
        if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
                return;
 
-       spin_lock(&hu->rx_lock);
+       /* It does not need a lock here as it is already protected by a mutex in
+        * tty caller
+        */
        hu->proto->recv(hu, data, count);
 
        if (hu->hdev)
                hu->hdev->stat.byte_rx += count;
 
-       spin_unlock(&hu->rx_lock);
-
        tty_unthrottle(tty);
 }
 
@@ -598,6 +587,13 @@ static int hci_uart_register_dev(struct hci_uart *hu)
        hdev->bus = HCI_UART;
        hci_set_drvdata(hdev, hu);
 
+       /* Only when vendor specific setup callback is provided, consider
+        * the manufacturer information valid. This avoids filling in the
+        * value for Ericsson when nothing is specified.
+        */
+       if (hu->proto->setup)
+               hdev->manufacturer = hu->proto->manufacturer;
+
        hdev->open  = hci_uart_open;
        hdev->close = hci_uart_close;
        hdev->flush = hci_uart_flush;
index 6b9b91267959f5e2d997386339f05fb55ab69721..77eae64000b398bd1501dae92135b880bd18bcbc 100644 (file)
 #define HCI_IBS_SLEEP_IND      0xFE
 #define HCI_IBS_WAKE_IND       0xFD
 #define HCI_IBS_WAKE_ACK       0xFC
-#define HCI_MAX_IBS_SIZE       10
+#define HCI_MAX_IBS_SIZE       10
 
 /* Controller states */
 #define STATE_IN_BAND_SLEEP_ENABLED    1
 
-#define IBS_WAKE_RETRANS_TIMEOUT_MS    100
-#define IBS_TX_IDLE_TIMEOUT_MS                 2000
+#define IBS_WAKE_RETRANS_TIMEOUT_MS    100
+#define IBS_TX_IDLE_TIMEOUT_MS         2000
 #define BAUDRATE_SETTLE_TIMEOUT_MS     300
 
 /* HCI_IBS transmit side sleep protocol states */
@@ -181,8 +181,8 @@ static void serial_clock_vote(unsigned long vote, struct hci_uart *hu)
                else
                        __serial_clock_off(hu->tty);
 
-               BT_DBG("Vote serial clock %s(%s)", new_vote? "true" : "false",
-                      vote? "true" : "false");
+               BT_DBG("Vote serial clock %s(%s)", new_vote ? "true" : "false",
+                      vote ? "true" : "false");
 
                diff = jiffies_to_msecs(jiffies - qca->vote_last_jif);
 
@@ -347,7 +347,7 @@ static void hci_ibs_wake_retrans_timeout(unsigned long arg)
        struct hci_uart *hu = (struct hci_uart *)arg;
        struct qca_data *qca = hu->priv;
        unsigned long flags, retrans_delay;
-       unsigned long retransmit = 0;
+       bool retransmit = false;
 
        BT_DBG("hu %p wake retransmit timeout in %d state",
                hu, qca->tx_ibs_state);
@@ -358,7 +358,7 @@ static void hci_ibs_wake_retrans_timeout(unsigned long arg)
        switch (qca->tx_ibs_state) {
        case HCI_IBS_TX_WAKING:
                /* No WAKE_ACK, retransmit WAKE */
-               retransmit = 1;
+               retransmit = true;
                if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
                        BT_ERR("Failed to acknowledge device wake up");
                        break;
@@ -821,7 +821,7 @@ static struct sk_buff *qca_dequeue(struct hci_uart *hu)
 
 static uint8_t qca_get_baudrate_value(int speed)
 {
-       switch(speed) {
+       switch (speed) {
        case 9600:
                return QCA_BAUDRATE_9600;
        case 19200:
@@ -947,6 +947,7 @@ static int qca_setup(struct hci_uart *hu)
 static struct hci_uart_proto qca_proto = {
        .id             = HCI_UART_QCA,
        .name           = "QCA",
+       .manufacturer   = 29,
        .init_speed     = 115200,
        .oper_speed     = 3000000,
        .open           = qca_open,
index 495b9ef52bb0fd608519fda39c0b4726c95f3c7f..82c92f1b65b4af7c317c8a0af91723a481ac3874 100644 (file)
@@ -59,6 +59,7 @@ struct hci_uart;
 struct hci_uart_proto {
        unsigned int id;
        const char *name;
+       unsigned int manufacturer;
        unsigned int init_speed;
        unsigned int oper_speed;
        int (*open)(struct hci_uart *hu);
@@ -85,7 +86,6 @@ struct hci_uart {
 
        struct sk_buff          *tx_skb;
        unsigned long           tx_state;
-       spinlock_t              rx_lock;
 
        unsigned int init_speed;
        unsigned int oper_speed;
index 78653db2ef2bca6d6ea085b8aa9054ce74d05e17..ed888e302bc396f000035a63ac310686262fc924 100644 (file)
@@ -55,8 +55,6 @@ struct vhci_data {
 
 static int vhci_open_dev(struct hci_dev *hdev)
 {
-       set_bit(HCI_RUNNING, &hdev->flags);
-
        return 0;
 }
 
@@ -64,9 +62,6 @@ static int vhci_close_dev(struct hci_dev *hdev)
 {
        struct vhci_data *data = hci_get_drvdata(hdev);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        skb_queue_purge(&data->readq);
 
        return 0;
@@ -85,9 +80,6 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct vhci_data *data = hci_get_drvdata(hdev);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
        skb_queue_tail(&data->readq, skb);
 
index f48cf11c655e16f23d5da3317e165911a5f093b0..dbf22719462f9481006b5c874ca99200342b4bee 100644 (file)
@@ -10,7 +10,7 @@ menuconfig HW_RANDOM
 
          To compile this driver as a module, choose M here: the
          module will be called rng-core.  This provides a device
-         that's usually called /dev/hw_random, and which exposes one
+         that's usually called /dev/hwrng, and which exposes one
          of possibly several hardware random number generators.
 
          These hardware random number generators do not feed directly
@@ -346,6 +346,16 @@ config HW_RANDOM_MSM
 
          If unsure, say Y.
 
+config HW_RANDOM_ST
+       tristate "ST Microelectronics HW Random Number Generator support"
+       depends on HW_RANDOM && ARCH_STI
+       ---help---
+         This driver provides kernel-side support for the Random Number
+         Generator hardware found on STi series of SoCs.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st-rng.
+
 config HW_RANDOM_XGENE
        tristate "APM X-Gene True Random Number Generator (TRNG) support"
        depends on HW_RANDOM && ARCH_XGENE
@@ -359,6 +369,18 @@ config HW_RANDOM_XGENE
 
          If unsure, say Y.
 
+config HW_RANDOM_STM32
+       tristate "STMicroelectronics STM32 random number generator"
+       depends on HW_RANDOM && (ARCH_STM32 || COMPILE_TEST)
+       help
+         This driver provides kernel-side support for the Random Number
+         Generator hardware found on STM32 microcontrollers.
+
+         To compile this driver as a module, choose M here: the
+         module will be called stm32-rng.
+
+         If unsure, say N.
+
 endif # HW_RANDOM
 
 config UML_RANDOM
index 055bb01510ad41907e548049323c96a62d6991dd..5ad39763512835f143b0b5ea2a8a549fb59e5d8e 100644 (file)
@@ -30,4 +30,6 @@ obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o
 obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
 obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o
 obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o
+obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o
 obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o
+obj-$(CONFIG_HW_RANDOM_STM32) += stm32-rng.o
index 5643b65cee204d950d842529e0a12123f57e92c0..6f497aa1b27654112bc048f6c207288394015386 100644 (file)
@@ -323,7 +323,7 @@ static ssize_t hwrng_attr_current_store(struct device *dev,
                return -ERESTARTSYS;
        err = -ENODEV;
        list_for_each_entry(rng, &rng_list, list) {
-               if (strcmp(rng->name, buf) == 0) {
+               if (sysfs_streq(rng->name, buf)) {
                        err = 0;
                        if (rng != current_rng)
                                err = set_current_rng(rng);
index dc4701fd814f10aae76bfd852429f89bfe229c46..30cf4623184fbdc39387d5448cd5b4e281754053 100644 (file)
@@ -53,15 +53,11 @@ static void exynos_rng_writel(struct exynos_rng *rng, u32 val, u32 offset)
        __raw_writel(val, rng->mem + offset);
 }
 
-static int exynos_init(struct hwrng *rng)
+static int exynos_rng_configure(struct exynos_rng *exynos_rng)
 {
-       struct exynos_rng *exynos_rng = container_of(rng,
-                                               struct exynos_rng, rng);
        int i;
        int ret = 0;
 
-       pm_runtime_get_sync(exynos_rng->dev);
-
        for (i = 0 ; i < 5 ; i++)
                exynos_rng_writel(exynos_rng, jiffies,
                                EXYNOS_PRNG_SEED_OFFSET + 4*i);
@@ -70,6 +66,17 @@ static int exynos_init(struct hwrng *rng)
                                                 & SEED_SETTING_DONE))
                ret = -EIO;
 
+       return ret;
+}
+
+static int exynos_init(struct hwrng *rng)
+{
+       struct exynos_rng *exynos_rng = container_of(rng,
+                                               struct exynos_rng, rng);
+       int ret = 0;
+
+       pm_runtime_get_sync(exynos_rng->dev);
+       ret = exynos_rng_configure(exynos_rng);
        pm_runtime_put_noidle(exynos_rng->dev);
 
        return ret;
@@ -81,21 +88,24 @@ static int exynos_read(struct hwrng *rng, void *buf,
        struct exynos_rng *exynos_rng = container_of(rng,
                                                struct exynos_rng, rng);
        u32 *data = buf;
+       int retry = 100;
 
        pm_runtime_get_sync(exynos_rng->dev);
 
        exynos_rng_writel(exynos_rng, PRNG_START, 0);
 
        while (!(exynos_rng_readl(exynos_rng,
-                       EXYNOS_PRNG_STATUS_OFFSET) & PRNG_DONE))
+                       EXYNOS_PRNG_STATUS_OFFSET) & PRNG_DONE) && --retry)
                cpu_relax();
+       if (!retry)
+               return -ETIMEDOUT;
 
        exynos_rng_writel(exynos_rng, PRNG_DONE, EXYNOS_PRNG_STATUS_OFFSET);
 
        *data = exynos_rng_readl(exynos_rng, EXYNOS_PRNG_OUT1_OFFSET);
 
        pm_runtime_mark_last_busy(exynos_rng->dev);
-       pm_runtime_autosuspend(exynos_rng->dev);
+       pm_runtime_put_sync_autosuspend(exynos_rng->dev);
 
        return 4;
 }
@@ -152,15 +162,45 @@ static int exynos_rng_runtime_resume(struct device *dev)
 
        return clk_prepare_enable(exynos_rng->clk);
 }
+
+static int exynos_rng_suspend(struct device *dev)
+{
+       return pm_runtime_force_suspend(dev);
+}
+
+static int exynos_rng_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct exynos_rng *exynos_rng = platform_get_drvdata(pdev);
+       int ret;
+
+       ret = pm_runtime_force_resume(dev);
+       if (ret)
+               return ret;
+
+       return exynos_rng_configure(exynos_rng);
+}
 #endif
 
-static UNIVERSAL_DEV_PM_OPS(exynos_rng_pm_ops, exynos_rng_runtime_suspend,
-                                       exynos_rng_runtime_resume, NULL);
+static const struct dev_pm_ops exynos_rng_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(exynos_rng_suspend, exynos_rng_resume)
+       SET_RUNTIME_PM_OPS(exynos_rng_runtime_suspend,
+                          exynos_rng_runtime_resume, NULL)
+};
+
+static const struct of_device_id exynos_rng_dt_match[] = {
+       {
+               .compatible = "samsung,exynos4-rng",
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, exynos_rng_dt_match);
 
 static struct platform_driver exynos_rng_driver = {
        .driver         = {
                .name   = "exynos-rng",
                .pm     = &exynos_rng_pm_ops,
+               .of_match_table = exynos_rng_dt_match,
        },
        .probe          = exynos_rng_probe,
 };
index 6cbb72ec60131ab248237921d733e5f659728ebc..467362262651e61f836258d9783a7672a857c8a0 100644 (file)
@@ -141,12 +141,11 @@ static void mxc_rnga_cleanup(struct hwrng *rng)
 
 static int __init mxc_rnga_probe(struct platform_device *pdev)
 {
-       int err = -ENODEV;
+       int err;
        struct resource *res;
        struct mxc_rng *mxc_rng;
 
-       mxc_rng = devm_kzalloc(&pdev->dev, sizeof(struct mxc_rng),
-                                       GFP_KERNEL);
+       mxc_rng = devm_kzalloc(&pdev->dev, sizeof(*mxc_rng), GFP_KERNEL);
        if (!mxc_rng)
                return -ENOMEM;
 
@@ -160,13 +159,12 @@ static int __init mxc_rnga_probe(struct platform_device *pdev)
        mxc_rng->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(mxc_rng->clk)) {
                dev_err(&pdev->dev, "Could not get rng_clk!\n");
-               err = PTR_ERR(mxc_rng->clk);
-               goto out;
+               return PTR_ERR(mxc_rng->clk);
        }
 
        err = clk_prepare_enable(mxc_rng->clk);
        if (err)
-               goto out;
+               return err;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        mxc_rng->mem = devm_ioremap_resource(&pdev->dev, res);
@@ -181,14 +179,10 @@ static int __init mxc_rnga_probe(struct platform_device *pdev)
                goto err_ioremap;
        }
 
-       dev_info(&pdev->dev, "MXC RNGA Registered.\n");
-
        return 0;
 
 err_ioremap:
        clk_disable_unprepare(mxc_rng->clk);
-
-out:
        return err;
 }
 
index 6234a4a19b5690235b7b12cd916a88006ae888a0..8c78aa0904925728404b49aedda73a212330cb3a 100644 (file)
@@ -96,7 +96,7 @@ static int octeon_rng_probe(struct platform_device *pdev)
        rng->ops = ops;
 
        platform_set_drvdata(pdev, &rng->ops);
-       ret = hwrng_register(&rng->ops);
+       ret = devm_hwrng_register(&pdev->dev, &rng->ops);
        if (ret)
                return -ENOENT;
 
@@ -105,21 +105,11 @@ static int octeon_rng_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int octeon_rng_remove(struct platform_device *pdev)
-{
-       struct hwrng *rng = platform_get_drvdata(pdev);
-
-       hwrng_unregister(rng);
-
-       return 0;
-}
-
 static struct platform_driver octeon_rng_driver = {
        .driver = {
                .name           = "octeon_rng",
        },
        .probe          = octeon_rng_probe,
-       .remove         = octeon_rng_remove,
 };
 
 module_platform_driver(octeon_rng_driver);
index 51cb1d5cc489773072edcaf637091172c55ef1fe..699b7259f5d71e14e79b73f6943f352c0c0cbc18 100644 (file)
@@ -138,6 +138,7 @@ static const struct of_device_id rng_match[] = {
        { .compatible      = "pasemi,pwrficient-rng", },
        { },
 };
+MODULE_DEVICE_TABLE(of, rng_match);
 
 static struct platform_driver rng_driver = {
        .driver = {
index b2cfda0fa93e28535772cd67237ee681885d274f..c0db4387d2e20e6cb75993b8aecd16a5249f47d2 100644 (file)
@@ -129,6 +129,7 @@ static const struct of_device_id ppc4xx_rng_match[] = {
        { .compatible = "amcc,ppc440epx-rng", },
        {},
 };
+MODULE_DEVICE_TABLE(of, ppc4xx_rng_match);
 
 static struct platform_driver ppc4xx_rng_driver = {
        .driver = {
diff --git a/drivers/char/hw_random/st-rng.c b/drivers/char/hw_random/st-rng.c
new file mode 100644 (file)
index 0000000..1d35363
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * ST Random Number Generator Driver ST's Platforms
+ *
+ * Author: Pankaj Dev: <pankaj.dev@st.com>
+ *         Lee Jones <lee.jones@linaro.org>
+ *
+ * Copyright (C) 2015 STMicroelectronics (R&D) Limited
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* Registers */
+#define ST_RNG_STATUS_REG              0x20
+#define ST_RNG_DATA_REG                        0x24
+
+/* Registers fields */
+#define ST_RNG_STATUS_BAD_SEQUENCE     BIT(0)
+#define ST_RNG_STATUS_BAD_ALTERNANCE   BIT(1)
+#define ST_RNG_STATUS_FIFO_FULL                BIT(5)
+
+#define ST_RNG_SAMPLE_SIZE             2 /* 2 Byte (16bit) samples */
+#define ST_RNG_FIFO_DEPTH              4
+#define ST_RNG_FIFO_SIZE               (ST_RNG_FIFO_DEPTH * ST_RNG_SAMPLE_SIZE)
+
+/*
+ * Samples are documented to be available every 0.667us, so in theory
+ * the 4 sample deep FIFO should take 2.668us to fill.  However, during
+ * thorough testing, it became apparent that filling the FIFO actually
+ * takes closer to 12us.  We then multiply by 2 in order to account for
+ * the lack of udelay()'s reliability, suggested by Russell King.
+ */
+#define ST_RNG_FILL_FIFO_TIMEOUT       (12 * 2)
+
+struct st_rng_data {
+       void __iomem    *base;
+       struct clk      *clk;
+       struct hwrng    ops;
+};
+
+static int st_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+       struct st_rng_data *ddata = (struct st_rng_data *)rng->priv;
+       u32 status;
+       int i;
+
+       if (max < sizeof(u16))
+               return -EINVAL;
+
+       /* Wait until FIFO is full - max 4uS*/
+       for (i = 0; i < ST_RNG_FILL_FIFO_TIMEOUT; i++) {
+               status = readl_relaxed(ddata->base + ST_RNG_STATUS_REG);
+               if (status & ST_RNG_STATUS_FIFO_FULL)
+                       break;
+               udelay(1);
+       }
+
+       if (i == ST_RNG_FILL_FIFO_TIMEOUT)
+               return 0;
+
+       for (i = 0; i < ST_RNG_FIFO_SIZE && i < max; i += 2)
+               *(u16 *)(data + i) =
+                       readl_relaxed(ddata->base + ST_RNG_DATA_REG);
+
+       return i;       /* No of bytes read */
+}
+
+static int st_rng_probe(struct platform_device *pdev)
+{
+       struct st_rng_data *ddata;
+       struct resource *res;
+       struct clk *clk;
+       void __iomem *base;
+       int ret;
+
+       ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+       if (!ddata)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       ret = clk_prepare_enable(clk);
+       if (ret)
+               return ret;
+
+       ddata->ops.priv = (unsigned long)ddata;
+       ddata->ops.read = st_rng_read;
+       ddata->ops.name = pdev->name;
+       ddata->base     = base;
+       ddata->clk      = clk;
+
+       dev_set_drvdata(&pdev->dev, ddata);
+
+       ret = hwrng_register(&ddata->ops);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register HW RNG\n");
+               return ret;
+       }
+
+       dev_info(&pdev->dev, "Successfully registered HW RNG\n");
+
+       return 0;
+}
+
+static int st_rng_remove(struct platform_device *pdev)
+{
+       struct st_rng_data *ddata = dev_get_drvdata(&pdev->dev);
+
+       hwrng_unregister(&ddata->ops);
+
+       clk_disable_unprepare(ddata->clk);
+
+       return 0;
+}
+
+static const struct of_device_id st_rng_match[] = {
+       { .compatible = "st,rng" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, st_rng_match);
+
+static struct platform_driver st_rng_driver = {
+       .driver = {
+               .name = "st-hwrandom",
+               .of_match_table = of_match_ptr(st_rng_match),
+       },
+       .probe = st_rng_probe,
+       .remove = st_rng_remove
+};
+
+module_platform_driver(st_rng_driver);
+
+MODULE_AUTHOR("Pankaj Dev <pankaj.dev@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c
new file mode 100644 (file)
index 0000000..92a8106
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2015, Daniel Thompson
+ *
+ * This file 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 file 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/clk.h>
+#include <linux/delay.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#define RNG_CR 0x00
+#define RNG_CR_RNGEN BIT(2)
+
+#define RNG_SR 0x04
+#define RNG_SR_SEIS BIT(6)
+#define RNG_SR_CEIS BIT(5)
+#define RNG_SR_DRDY BIT(0)
+
+#define RNG_DR 0x08
+
+/*
+ * It takes 40 cycles @ 48MHz to generate each random number (e.g. <1us).
+ * At the time of writing STM32 parts max out at ~200MHz meaning a timeout
+ * of 500 leaves us a very comfortable margin for error. The loop to which
+ * the timeout applies takes at least 4 instructions per iteration so the
+ * timeout is enough to take us up to multi-GHz parts!
+ */
+#define RNG_TIMEOUT 500
+
+struct stm32_rng_private {
+       struct hwrng rng;
+       void __iomem *base;
+       struct clk *clk;
+};
+
+static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+       struct stm32_rng_private *priv =
+           container_of(rng, struct stm32_rng_private, rng);
+       u32 sr;
+       int retval = 0;
+
+       pm_runtime_get_sync((struct device *) priv->rng.priv);
+
+       while (max > sizeof(u32)) {
+               sr = readl_relaxed(priv->base + RNG_SR);
+               if (!sr && wait) {
+                       unsigned int timeout = RNG_TIMEOUT;
+
+                       do {
+                               cpu_relax();
+                               sr = readl_relaxed(priv->base + RNG_SR);
+                       } while (!sr && --timeout);
+               }
+
+               /* If error detected or data not ready... */
+               if (sr != RNG_SR_DRDY)
+                       break;
+
+               *(u32 *)data = readl_relaxed(priv->base + RNG_DR);
+
+               retval += sizeof(u32);
+               data += sizeof(u32);
+               max -= sizeof(u32);
+       }
+
+       if (WARN_ONCE(sr & (RNG_SR_SEIS | RNG_SR_CEIS),
+                     "bad RNG status - %x\n", sr))
+               writel_relaxed(0, priv->base + RNG_SR);
+
+       pm_runtime_mark_last_busy((struct device *) priv->rng.priv);
+       pm_runtime_put_sync_autosuspend((struct device *) priv->rng.priv);
+
+       return retval || !wait ? retval : -EIO;
+}
+
+static int stm32_rng_init(struct hwrng *rng)
+{
+       struct stm32_rng_private *priv =
+           container_of(rng, struct stm32_rng_private, rng);
+       int err;
+
+       err = clk_prepare_enable(priv->clk);
+       if (err)
+               return err;
+
+       writel_relaxed(RNG_CR_RNGEN, priv->base + RNG_CR);
+
+       /* clear error indicators */
+       writel_relaxed(0, priv->base + RNG_SR);
+
+       return 0;
+}
+
+static void stm32_rng_cleanup(struct hwrng *rng)
+{
+       struct stm32_rng_private *priv =
+           container_of(rng, struct stm32_rng_private, rng);
+
+       writel_relaxed(0, priv->base + RNG_CR);
+       clk_disable_unprepare(priv->clk);
+}
+
+static int stm32_rng_probe(struct platform_device *ofdev)
+{
+       struct device *dev = &ofdev->dev;
+       struct device_node *np = ofdev->dev.of_node;
+       struct stm32_rng_private *priv;
+       struct resource res;
+       int err;
+
+       priv = devm_kzalloc(dev, sizeof(struct stm32_rng_private), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       err = of_address_to_resource(np, 0, &res);
+       if (err)
+               return err;
+
+       priv->base = devm_ioremap_resource(dev, &res);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       priv->clk = devm_clk_get(&ofdev->dev, NULL);
+       if (IS_ERR(priv->clk))
+               return PTR_ERR(priv->clk);
+
+       dev_set_drvdata(dev, priv);
+
+       priv->rng.name = dev_driver_string(dev),
+#ifndef CONFIG_PM
+       priv->rng.init = stm32_rng_init,
+       priv->rng.cleanup = stm32_rng_cleanup,
+#endif
+       priv->rng.read = stm32_rng_read,
+       priv->rng.priv = (unsigned long) dev;
+
+       pm_runtime_set_autosuspend_delay(dev, 100);
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_enable(dev);
+
+       return devm_hwrng_register(dev, &priv->rng);
+}
+
+#ifdef CONFIG_PM
+static int stm32_rng_runtime_suspend(struct device *dev)
+{
+       struct stm32_rng_private *priv = dev_get_drvdata(dev);
+
+       stm32_rng_cleanup(&priv->rng);
+
+       return 0;
+}
+
+static int stm32_rng_runtime_resume(struct device *dev)
+{
+       struct stm32_rng_private *priv = dev_get_drvdata(dev);
+
+       return stm32_rng_init(&priv->rng);
+}
+#endif
+
+static UNIVERSAL_DEV_PM_OPS(stm32_rng_pm_ops, stm32_rng_runtime_suspend,
+                           stm32_rng_runtime_resume, NULL);
+
+static const struct of_device_id stm32_rng_match[] = {
+       {
+               .compatible = "st,stm32-rng",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, stm32_rng_match);
+
+static struct platform_driver stm32_rng_driver = {
+       .driver = {
+               .name = "stm32-rng",
+               .pm = &stm32_rng_pm_ops,
+               .of_match_table = stm32_rng_match,
+       },
+       .probe = stm32_rng_probe,
+};
+
+module_platform_driver(stm32_rng_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Daniel Thompson <daniel.thompson@linaro.org>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 RNG device driver");
index c0eaf0973bd2bca88e269dbca7f44fd3fcdaf3c7..779b6ff0c7ad4040a680a2155964bd3016363767 100644 (file)
@@ -333,7 +333,8 @@ int clk_add_alias(const char *alias, const char *alias_dev_name,
        if (IS_ERR(r))
                return PTR_ERR(r);
 
-       l = clkdev_create(r, alias, "%s", alias_dev_name);
+       l = clkdev_create(r, alias, alias_dev_name ? "%s" : NULL,
+                         alias_dev_name);
        clk_put(r);
 
        return l ? 0 : -ENODEV;
index 9b613426e968ab6a0e36fdcb52b7c87ac076253b..bc24e5a002e77cad874a0c010fc55046aa66b0f4 100644 (file)
@@ -45,8 +45,8 @@ static unsigned long rockchip_mmc_recalc(struct clk_hw *hw,
 #define PSECS_PER_SEC 1000000000000LL
 
 /*
- * Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to
- * simplify calculations. So 45degs could be anywhere between 33deg and 66deg.
+ * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
+ * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
  */
 #define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
 
@@ -69,7 +69,7 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw)
 
                delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
                delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
-               degrees += delay_num * factor / 10000;
+               degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000);
        }
 
        return degrees % 360;
@@ -82,25 +82,41 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
        u8 nineties, remainder;
        u8 delay_num;
        u32 raw_value;
-       u64 delay;
-
-       /* allow 22 to be 22.5 */
-       degrees++;
-       /* floor to 22.5 increment */
-       degrees -= ((degrees) * 10 % 225) / 10;
+       u32 delay;
 
        nineties = degrees / 90;
-       /* 22.5 multiples */
-       remainder = (degrees % 90) / 22;
-
-       delay = PSECS_PER_SEC;
-       do_div(delay, rate);
-       /* / 360 / 22.5 */
-       do_div(delay, 16);
-       do_div(delay, ROCKCHIP_MMC_DELAY_ELEMENT_PSEC);
-
+       remainder = (degrees % 90);
+
+       /*
+        * Due to the inexact nature of the "fine" delay, we might
+        * actually go non-monotonic.  We don't go _too_ monotonic
+        * though, so we should be OK.  Here are options of how we may
+        * work:
+        *
+        * Ideally we end up with:
+        *   1.0, 2.0, ..., 69.0, 70.0, ...,  89.0, 90.0
+        *
+        * On one extreme (if delay is actually 44ps):
+        *   .73, 1.5, ..., 50.6, 51.3, ...,  65.3, 90.0
+        * The other (if delay is actually 77ps):
+        *   1.3, 2.6, ..., 88.6. 89.8, ..., 114.0, 90
+        *
+        * It's possible we might make a delay that is up to 25
+        * degrees off from what we think we're making.  That's OK
+        * though because we should be REALLY far from any bad range.
+        */
+
+       /*
+        * Convert to delay; do a little extra work to make sure we
+        * don't overflow 32-bit / 64-bit numbers.
+        */
+       delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
        delay *= remainder;
-       delay_num = (u8) min(delay, 255ULL);
+       delay = DIV_ROUND_CLOSEST(delay,
+                       (rate / 1000) * 36 *
+                               (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));
+
+       delay_num = (u8) min_t(u32, delay, 255);
 
        raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
        raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
index a7726db13abbb0e883ec5681fec65a473bc9d29e..50b68bc20720297b3ae4cb3e935c771540b9032f 100644 (file)
@@ -279,6 +279,10 @@ config CLKSRC_MIPS_GIC
        depends on MIPS_GIC
        select CLKSRC_OF
 
+config CLKSRC_TANGO_XTAL
+       bool
+       select CLKSRC_OF
+
 config CLKSRC_PXA
        def_bool y if ARCH_PXA || ARCH_SA1100
        select CLKSRC_OF if OF
index 5c00863c3e33ad8a3061fdf88765cfb4c0770d97..67bc996ca90983dc353453b3015808315e1c8c35 100644 (file)
@@ -56,9 +56,11 @@ obj-$(CONFIG_ARCH_KEYSTONE)          += timer-keystone.o
 obj-$(CONFIG_ARCH_INTEGRATOR_AP)       += timer-integrator-ap.o
 obj-$(CONFIG_CLKSRC_VERSATILE)         += versatile.o
 obj-$(CONFIG_CLKSRC_MIPS_GIC)          += mips-gic-timer.o
+obj-$(CONFIG_CLKSRC_TANGO_XTAL)                += tango_xtal.o
 obj-$(CONFIG_CLKSRC_IMX_GPT)           += timer-imx-gpt.o
 obj-$(CONFIG_ASM9260_TIMER)            += asm9260_timer.o
 obj-$(CONFIG_H8300)                    += h8300_timer8.o
 obj-$(CONFIG_H8300_TMR16)              += h8300_timer16.o
 obj-$(CONFIG_H8300_TPU)                        += h8300_tpu.o
 obj-$(CONFIG_CLKSRC_ST_LPC)            += clksrc_st_lpc.o
+obj-$(CONFIG_X86_NUMACHIP)             += numachip.o
index 29ea50ac366ab9c9399951286beb3ff09984d68c..a2cb6fae92958b7319c55ee20ef21e26268ffbea 100644 (file)
@@ -60,7 +60,7 @@ static struct clock_event_device __percpu *gt_evt;
  *  different to the 32-bit upper value read previously, go back to step 2.
  *  Otherwise the 64-bit timer counter value is correct.
  */
-static u64 gt_counter_read(void)
+static u64 notrace _gt_counter_read(void)
 {
        u64 counter;
        u32 lower;
@@ -79,6 +79,11 @@ static u64 gt_counter_read(void)
        return counter;
 }
 
+static u64 gt_counter_read(void)
+{
+       return _gt_counter_read();
+}
+
 /**
  * To ensure that updates to comparator value register do not set the
  * Interrupt Status Register proceed as follows:
@@ -201,7 +206,7 @@ static struct clocksource gt_clocksource = {
 #ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
 static u64 notrace gt_sched_clock_read(void)
 {
-       return gt_counter_read();
+       return _gt_counter_read();
 }
 #endif
 
index 7a97a34dba701576c7d8b1d143e355cb56abfc9c..19bb1792d647dc8cca312248b5f2c7eef86099dd 100644 (file)
@@ -228,7 +228,6 @@ static int em_sti_register_clocksource(struct em_sti_priv *p)
 {
        struct clocksource *cs = &p->cs;
 
-       memset(cs, 0, sizeof(*cs));
        cs->name = dev_name(&p->pdev->dev);
        cs->rating = 200;
        cs->read = em_sti_clocksource_read;
@@ -285,7 +284,6 @@ static void em_sti_register_clockevent(struct em_sti_priv *p)
 {
        struct clock_event_device *ced = &p->ced;
 
-       memset(ced, 0, sizeof(*ced));
        ced->name = dev_name(&p->pdev->dev);
        ced->features = CLOCK_EVT_FEAT_ONESHOT;
        ced->rating = 200;
index 029f96ab131a20c5ff51ec9f6818f7053e6d4349..ff44082a0827d60ff14a00e40001d5900adc8e92 100644 (file)
@@ -382,24 +382,28 @@ static void exynos4_mct_tick_start(unsigned long cycles,
 static int exynos4_tick_set_next_event(unsigned long cycles,
                                       struct clock_event_device *evt)
 {
-       struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
+       struct mct_clock_event_device *mevt;
 
+       mevt = container_of(evt, struct mct_clock_event_device, evt);
        exynos4_mct_tick_start(cycles, mevt);
-
        return 0;
 }
 
 static int set_state_shutdown(struct clock_event_device *evt)
 {
-       exynos4_mct_tick_stop(this_cpu_ptr(&percpu_mct_tick));
+       struct mct_clock_event_device *mevt;
+
+       mevt = container_of(evt, struct mct_clock_event_device, evt);
+       exynos4_mct_tick_stop(mevt);
        return 0;
 }
 
 static int set_state_periodic(struct clock_event_device *evt)
 {
-       struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
+       struct mct_clock_event_device *mevt;
        unsigned long cycles_per_jiffy;
 
+       mevt = container_of(evt, struct mct_clock_event_device, evt);
        cycles_per_jiffy = (((unsigned long long)NSEC_PER_SEC / HZ * evt->mult)
                            >> evt->shift);
        exynos4_mct_tick_stop(mevt);
index ef434699c80a090ee1bbb0de41b38170bddc188b..10202f1fdfd7b2afc6ccf9cd044cecdc2d3d66a7 100644 (file)
@@ -118,7 +118,7 @@ static inline void ftm_reset_counter(void __iomem *base)
        ftm_writel(0x00, base + FTM_CNT);
 }
 
-static u64 ftm_read_sched_clock(void)
+static u64 notrace ftm_read_sched_clock(void)
 {
        return ftm_readl(priv->clksrc_base + FTM_CNT);
 }
index 82941c1e9e33f0d098ae4b0b7273fa263c3b9ce0..0e076c6fc006b26b52729c9389db7d87a9604ce3 100644 (file)
@@ -153,7 +153,6 @@ static int timer16_setup(struct timer16_priv *p, struct platform_device *pdev)
        int ret, irq;
        unsigned int ch;
 
-       memset(p, 0, sizeof(*p));
        p->pdev = pdev;
 
        res[REG_CH] = platform_get_resource(p->pdev,
index f9b3b7033a970acfdc21c7503ec34326e6042748..44375d8b9bc438878be58699fb95ce8fb624ebc6 100644 (file)
@@ -215,7 +215,6 @@ static int timer8_setup(struct timer8_priv *p,
        int irq;
        int ret;
 
-       memset(p, 0, sizeof(*p));
        p->pdev = pdev;
 
        res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
index 64195fdd78bf78e90a1f3c264a1e88716b6c7f25..5487410bfabb93b0c4768a91870485f6ba77d31f 100644 (file)
@@ -123,7 +123,6 @@ static int __init tpu_setup(struct tpu_priv *p, struct platform_device *pdev)
 {
        struct resource *res[2];
 
-       memset(p, 0, sizeof(*p));
        p->pdev = pdev;
 
        res[CH_L] = platform_get_resource(p->pdev, IORESOURCE_MEM, CH_L);
index 50f0641c65b6f30cd890240aca4f6febd5f4a6e8..fbfc74685e6afba837ae549021f7fe763f7c0de4 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/sched_clock.h>
 #include <linux/slab.h>
 
 #define GPT_IRQ_EN_REG         0x00
@@ -59,6 +60,13 @@ struct mtk_clock_event_device {
        struct clock_event_device dev;
 };
 
+static void __iomem *gpt_sched_reg __read_mostly;
+
+static u64 notrace mtk_read_sched_clock(void)
+{
+       return readl_relaxed(gpt_sched_reg);
+}
+
 static inline struct mtk_clock_event_device *to_mtk_clk(
                                struct clock_event_device *c)
 {
@@ -141,14 +149,6 @@ static irqreturn_t mtk_timer_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static void mtk_timer_global_reset(struct mtk_clock_event_device *evt)
-{
-       /* Disable all interrupts */
-       writel(0x0, evt->gpt_base + GPT_IRQ_EN_REG);
-       /* Acknowledge all interrupts */
-       writel(0x3f, evt->gpt_base + GPT_IRQ_ACK_REG);
-}
-
 static void
 mtk_timer_setup(struct mtk_clock_event_device *evt, u8 timer, u8 option)
 {
@@ -168,6 +168,12 @@ static void mtk_timer_enable_irq(struct mtk_clock_event_device *evt, u8 timer)
 {
        u32 val;
 
+       /* Disable all interrupts */
+       writel(0x0, evt->gpt_base + GPT_IRQ_EN_REG);
+
+       /* Acknowledge all spurious pending interrupts */
+       writel(0x3f, evt->gpt_base + GPT_IRQ_ACK_REG);
+
        val = readl(evt->gpt_base + GPT_IRQ_EN_REG);
        writel(val | GPT_IRQ_ENABLE(timer),
                        evt->gpt_base + GPT_IRQ_EN_REG);
@@ -220,8 +226,6 @@ static void __init mtk_timer_init(struct device_node *node)
        }
        rate = clk_get_rate(clk);
 
-       mtk_timer_global_reset(evt);
-
        if (request_irq(evt->dev.irq, mtk_timer_interrupt,
                        IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) {
                pr_warn("failed to setup irq %d\n", evt->dev.irq);
@@ -234,6 +238,8 @@ static void __init mtk_timer_init(struct device_node *node)
        mtk_timer_setup(evt, GPT_CLK_SRC, TIMER_CTRL_OP_FREERUN);
        clocksource_mmio_init(evt->gpt_base + TIMER_CNT_REG(GPT_CLK_SRC),
                        node->name, rate, 300, 32, clocksource_mmio_readl_up);
+       gpt_sched_reg = evt->gpt_base + TIMER_CNT_REG(GPT_CLK_SRC);
+       sched_clock_register(mtk_read_sched_clock, 32, rate);
 
        /* Configure clock event */
        mtk_timer_setup(evt, GPT_CLK_EVT, TIMER_CTRL_OP_REPEAT);
diff --git a/drivers/clocksource/numachip.c b/drivers/clocksource/numachip.c
new file mode 100644 (file)
index 0000000..4e0f11f
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright (C) 2015 Numascale AS. All rights reserved.
+ *
+ * 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/clockchips.h>
+
+#include <asm/irq.h>
+#include <asm/numachip/numachip.h>
+#include <asm/numachip/numachip_csr.h>
+
+static DEFINE_PER_CPU(struct clock_event_device, numachip2_ced);
+
+static cycles_t numachip2_timer_read(struct clocksource *cs)
+{
+       return numachip2_read64_lcsr(NUMACHIP2_TIMER_NOW);
+}
+
+static struct clocksource numachip2_clocksource = {
+       .name            = "numachip2",
+       .rating          = 295,
+       .read            = numachip2_timer_read,
+       .mask            = CLOCKSOURCE_MASK(64),
+       .flags           = CLOCK_SOURCE_IS_CONTINUOUS,
+       .mult            = 1,
+       .shift           = 0,
+};
+
+static int numachip2_set_next_event(unsigned long delta, struct clock_event_device *ced)
+{
+       numachip2_write64_lcsr(NUMACHIP2_TIMER_DEADLINE + numachip2_timer(),
+               delta);
+       return 0;
+}
+
+static struct clock_event_device numachip2_clockevent = {
+       .name            = "numachip2",
+       .rating          = 400,
+       .set_next_event  = numachip2_set_next_event,
+       .features        = CLOCK_EVT_FEAT_ONESHOT,
+       .mult            = 1,
+       .shift           = 0,
+       .min_delta_ns    = 1250,
+       .max_delta_ns    = LONG_MAX,
+};
+
+static void numachip_timer_interrupt(void)
+{
+       struct clock_event_device *ced = this_cpu_ptr(&numachip2_ced);
+
+       ced->event_handler(ced);
+}
+
+static __init void numachip_timer_each(struct work_struct *work)
+{
+       unsigned local_apicid = __this_cpu_read(x86_cpu_to_apicid) & 0xff;
+       struct clock_event_device *ced = this_cpu_ptr(&numachip2_ced);
+
+       /* Setup IPI vector to local core and relative timing mode */
+       numachip2_write64_lcsr(NUMACHIP2_TIMER_INT + numachip2_timer(),
+               (3 << 22) | (X86_PLATFORM_IPI_VECTOR << 14) |
+               (local_apicid << 6));
+
+       *ced = numachip2_clockevent;
+       ced->cpumask = cpumask_of(smp_processor_id());
+       clockevents_register_device(ced);
+}
+
+static int __init numachip_timer_init(void)
+{
+       if (numachip_system != 2)
+               return -ENODEV;
+
+       /* Reset timer */
+       numachip2_write64_lcsr(NUMACHIP2_TIMER_RESET, 0);
+       clocksource_register_hz(&numachip2_clocksource, NSEC_PER_SEC);
+
+       /* Setup per-cpu clockevents */
+       x86_platform_ipi_callback = numachip_timer_interrupt;
+       schedule_on_each_cpu(&numachip_timer_each);
+
+       return 0;
+}
+
+arch_initcall(numachip_timer_init);
index bc90e13338cc6e961d3d3e4c4bdc611c2dd05436..9502bc4c3f6d9a17ec7616bf743c5bfc26c3f638 100644 (file)
@@ -307,7 +307,7 @@ static void samsung_clocksource_resume(struct clocksource *cs)
        samsung_time_start(pwm.source_id, true);
 }
 
-static cycle_t samsung_clocksource_read(struct clocksource *c)
+static cycle_t notrace samsung_clocksource_read(struct clocksource *c)
 {
        return ~readl_relaxed(pwm.source_reg);
 }
index ba73a6eb8d66e640666b3ee5fb1733ea829ba601..103c49362c68f66f830587a6504e33ad428a1c14 100644 (file)
@@ -962,7 +962,6 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
        unsigned int i;
        int ret;
 
-       memset(cmt, 0, sizeof(*cmt));
        cmt->pdev = pdev;
        raw_spin_lock_init(&cmt->lock);
 
index f1985da8113f481fb870c4b91eeef62b962300ad..53aa7e92a7d77b7efc052466e8904efc110cc2eb 100644 (file)
@@ -280,7 +280,9 @@ static int sh_mtu2_clock_event_shutdown(struct clock_event_device *ced)
 {
        struct sh_mtu2_channel *ch = ced_to_sh_mtu2(ced);
 
-       sh_mtu2_disable(ch);
+       if (clockevent_state_periodic(ced))
+               sh_mtu2_disable(ch);
+
        return 0;
 }
 
diff --git a/drivers/clocksource/tango_xtal.c b/drivers/clocksource/tango_xtal.c
new file mode 100644 (file)
index 0000000..d297b30
--- /dev/null
@@ -0,0 +1,66 @@
+#include <linux/clocksource.h>
+#include <linux/sched_clock.h>
+#include <linux/of_address.h>
+#include <linux/printk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+
+static void __iomem *xtal_in_cnt;
+static struct delay_timer delay_timer;
+
+static unsigned long notrace read_xtal_counter(void)
+{
+       return readl_relaxed(xtal_in_cnt);
+}
+
+static u64 notrace read_sched_clock(void)
+{
+       return read_xtal_counter();
+}
+
+static cycle_t read_clocksource(struct clocksource *cs)
+{
+       return read_xtal_counter();
+}
+
+static struct clocksource tango_xtal = {
+       .name   = "tango-xtal",
+       .rating = 350,
+       .read   = read_clocksource,
+       .mask   = CLOCKSOURCE_MASK(32),
+       .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static void __init tango_clocksource_init(struct device_node *np)
+{
+       struct clk *clk;
+       int xtal_freq, ret;
+
+       xtal_in_cnt = of_iomap(np, 0);
+       if (xtal_in_cnt == NULL) {
+               pr_err("%s: invalid address\n", np->full_name);
+               return;
+       }
+
+       clk = of_clk_get(np, 0);
+       if (IS_ERR(clk)) {
+               pr_err("%s: invalid clock\n", np->full_name);
+               return;
+       }
+
+       xtal_freq = clk_get_rate(clk);
+       delay_timer.freq = xtal_freq;
+       delay_timer.read_current_timer = read_xtal_counter;
+
+       ret = clocksource_register_hz(&tango_xtal, xtal_freq);
+       if (ret != 0) {
+               pr_err("%s: registration failed\n", np->full_name);
+               return;
+       }
+
+       sched_clock_register(read_sched_clock, 32, xtal_freq);
+       register_current_timer_delay(&delay_timer);
+}
+
+CLOCKSOURCE_OF_DECLARE(tango, "sigma,tick-counter", tango_clocksource_init);
index 2162796fd504fb15d9639bfec054a7ece427220b..d93ec3c4f139f2bbcf1ada62efaeed9e6062fb66 100644 (file)
@@ -45,6 +45,8 @@
 #include <linux/percpu.h>
 #include <linux/syscore_ops.h>
 
+#include <asm/delay.h>
+
 /*
  * Timer block registers.
  */
@@ -249,6 +251,15 @@ struct syscore_ops armada_370_xp_timer_syscore_ops = {
        .resume         = armada_370_xp_timer_resume,
 };
 
+static unsigned long armada_370_delay_timer_read(void)
+{
+       return ~readl(timer_base + TIMER0_VAL_OFF);
+}
+
+static struct delay_timer armada_370_delay_timer = {
+       .read_current_timer = armada_370_delay_timer_read,
+};
+
 static void __init armada_370_xp_timer_common_init(struct device_node *np)
 {
        u32 clr = 0, set = 0;
@@ -287,6 +298,9 @@ static void __init armada_370_xp_timer_common_init(struct device_node *np)
                TIMER0_RELOAD_EN | enable_mask,
                TIMER0_RELOAD_EN | enable_mask);
 
+       armada_370_delay_timer.freq = timer_clk;
+       register_current_timer_delay(&armada_370_delay_timer);
+
        /*
         * Set scale and timer for sched_clock.
         */
index 18d4266c2986c65d1f2d3cd55e0d4986dfb9b4d6..bba6799000541d360081b41e4e87ee8d9bf7596a 100644 (file)
@@ -67,7 +67,8 @@ static inline void gpt_writel(void __iomem *base, u32 value, u32 offset,
        writel(value, base + 0x20 * gpt_id + offset);
 }
 
-static cycle_t pistachio_clocksource_read_cycles(struct clocksource *cs)
+static cycle_t notrace
+pistachio_clocksource_read_cycles(struct clocksource *cs)
 {
        struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs);
        u32 counter, overflw;
index e73947f0f86db3c1b9656c9b3418c9151f7f2954..a536eeb634d885fccf5b92f0d0cbeeec7f88baf2 100644 (file)
@@ -143,7 +143,7 @@ static irqreturn_t digicolor_timer_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static u64 digicolor_timer_sched_read(void)
+static u64 notrace digicolor_timer_sched_read(void)
 {
        return ~readl(dc_timer_dev.base + COUNT(TIMER_B));
 }
index 839aba92fc395811e67e8d2ac7ba1ce7727c0bcf..99ec96769dda899817bf1ee41fd836a4e2bbbc53 100644 (file)
@@ -305,13 +305,14 @@ static int __init mxc_clockevent_init(struct imx_timer *imxtm)
        struct irqaction *act = &imxtm->act;
 
        ced->name = "mxc_timer1";
-       ced->features = CLOCK_EVT_FEAT_ONESHOT;
+       ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ;
        ced->set_state_shutdown = mxc_shutdown;
        ced->set_state_oneshot = mxc_set_oneshot;
        ced->tick_resume = mxc_shutdown;
        ced->set_next_event = imxtm->gpt->set_next_event;
        ced->rating = 200;
        ced->cpumask = cpumask_of(0);
+       ced->irq = imxtm->irq;
        clockevents_config_and_register(ced, clk_get_rate(imxtm->clk_per),
                                        0xff, 0xfffffffe);
 
index 78de982cc640bd93a5ea9b834e9c7049061a24a2..2854c663e8b5b978ae463d1d96c218c79da77a5e 100644 (file)
@@ -73,7 +73,7 @@ static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
 }
 
 /* read 64-bit timer counter */
-static cycle_t sirfsoc_timer_read(struct clocksource *cs)
+static cycle_t notrace sirfsoc_timer_read(struct clocksource *cs)
 {
        u64 cycles;
 
index f07ba99321716c02628affeafca70100642a1df2..a0e6c68536a18d8dbc76eb7f239bea31ca24240f 100644 (file)
@@ -52,7 +52,7 @@ static inline void pit_irq_acknowledge(void)
        __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG);
 }
 
-static u64 pit_read_sched_clock(void)
+static u64 notrace pit_read_sched_clock(void)
 {
        return ~__raw_readl(clksrc_base + PITCVAL);
 }
index d234719065a5f2e39c3e81681dc0bd184e803033..2569e043317e14c70eee11617622fe3dfe5a7846 100644 (file)
@@ -420,7 +420,7 @@ config CRYPTO_DEV_CCP
        bool "Support for AMD Cryptographic Coprocessor"
        depends on ((X86 && PCI) || (ARM64 && (OF_ADDRESS || ACPI))) && HAS_IOMEM
        help
-         The AMD Cryptographic Coprocessor provides hardware support
+         The AMD Cryptographic Coprocessor provides hardware offload support
          for encryption, hashing and related operations.
 
 if CRYPTO_DEV_CCP
@@ -429,7 +429,8 @@ endif
 
 config CRYPTO_DEV_MXS_DCP
        tristate "Support for Freescale MXS DCP"
-       depends on ARCH_MXS
+       depends on (ARCH_MXS || ARCH_MXC)
+       select STMP_DEVICE
        select CRYPTO_CBC
        select CRYPTO_ECB
        select CRYPTO_AES
index 192a8fa325c1ccc8c71dd8617d84f509a1b6dfae..58a630e55d5d601d7afc844626a9a7f92ff948ab 100644 (file)
@@ -740,26 +740,6 @@ void crypto4xx_return_pd(struct crypto4xx_device *dev,
        pd_uinfo->state = PD_ENTRY_FREE;
 }
 
-/*
- * derive number of elements in scatterlist
- * Shamlessly copy from talitos.c
- */
-static int get_sg_count(struct scatterlist *sg_list, int nbytes)
-{
-       struct scatterlist *sg = sg_list;
-       int sg_nents = 0;
-
-       while (nbytes) {
-               sg_nents++;
-               if (sg->length > nbytes)
-                       break;
-               nbytes -= sg->length;
-               sg = sg_next(sg);
-       }
-
-       return sg_nents;
-}
-
 static u32 get_next_gd(u32 current)
 {
        if (current != PPC4XX_LAST_GD)
@@ -800,7 +780,7 @@ u32 crypto4xx_build_pd(struct crypto_async_request *req,
        u32 gd_idx = 0;
 
        /* figure how many gd is needed */
-       num_gd = get_sg_count(src, datalen);
+       num_gd = sg_nents_for_len(src, datalen);
        if (num_gd == 1)
                num_gd = 0;
 
@@ -1284,6 +1264,7 @@ static const struct of_device_id crypto4xx_match[] = {
        { .compatible      = "amcc,ppc4xx-crypto",},
        { },
 };
+MODULE_DEVICE_TABLE(of, crypto4xx_match);
 
 static struct platform_driver crypto4xx_driver = {
        .driver = {
index 0f9a9dc06a830f4eb5f9669ba26df7c2f0a95077..fb16d812c8f555493f3a2527c0c551bc6de59139 100644 (file)
@@ -260,7 +260,11 @@ static struct atmel_aes_dev *atmel_aes_find_dev(struct atmel_aes_ctx *ctx)
 
 static int atmel_aes_hw_init(struct atmel_aes_dev *dd)
 {
-       clk_prepare_enable(dd->iclk);
+       int err;
+
+       err = clk_prepare_enable(dd->iclk);
+       if (err)
+               return err;
 
        if (!(dd->flags & AES_FLAGS_INIT)) {
                atmel_aes_write(dd, AES_CR, AES_CR_SWRST);
@@ -1320,7 +1324,6 @@ static int atmel_aes_probe(struct platform_device *pdev)
        struct crypto_platform_data *pdata;
        struct device *dev = &pdev->dev;
        struct resource *aes_res;
-       unsigned long aes_phys_size;
        int err;
 
        pdata = pdev->dev.platform_data;
@@ -1337,7 +1340,7 @@ static int atmel_aes_probe(struct platform_device *pdev)
                goto aes_dd_err;
        }
 
-       aes_dd = kzalloc(sizeof(struct atmel_aes_dev), GFP_KERNEL);
+       aes_dd = devm_kzalloc(&pdev->dev, sizeof(*aes_dd), GFP_KERNEL);
        if (aes_dd == NULL) {
                dev_err(dev, "unable to alloc data struct.\n");
                err = -ENOMEM;
@@ -1368,36 +1371,35 @@ static int atmel_aes_probe(struct platform_device *pdev)
                goto res_err;
        }
        aes_dd->phys_base = aes_res->start;
-       aes_phys_size = resource_size(aes_res);
 
        /* Get the IRQ */
        aes_dd->irq = platform_get_irq(pdev,  0);
        if (aes_dd->irq < 0) {
                dev_err(dev, "no IRQ resource info\n");
                err = aes_dd->irq;
-               goto aes_irq_err;
+               goto res_err;
        }
 
-       err = request_irq(aes_dd->irq, atmel_aes_irq, IRQF_SHARED, "atmel-aes",
-                                               aes_dd);
+       err = devm_request_irq(&pdev->dev, aes_dd->irq, atmel_aes_irq,
+                              IRQF_SHARED, "atmel-aes", aes_dd);
        if (err) {
                dev_err(dev, "unable to request aes irq.\n");
-               goto aes_irq_err;
+               goto res_err;
        }
 
        /* Initializing the clock */
-       aes_dd->iclk = clk_get(&pdev->dev, "aes_clk");
+       aes_dd->iclk = devm_clk_get(&pdev->dev, "aes_clk");
        if (IS_ERR(aes_dd->iclk)) {
                dev_err(dev, "clock initialization failed.\n");
                err = PTR_ERR(aes_dd->iclk);
-               goto clk_err;
+               goto res_err;
        }
 
-       aes_dd->io_base = ioremap(aes_dd->phys_base, aes_phys_size);
+       aes_dd->io_base = devm_ioremap_resource(&pdev->dev, aes_res);
        if (!aes_dd->io_base) {
                dev_err(dev, "can't ioremap\n");
                err = -ENOMEM;
-               goto aes_io_err;
+               goto res_err;
        }
 
        atmel_aes_hw_version_init(aes_dd);
@@ -1434,17 +1436,9 @@ err_algs:
 err_aes_dma:
        atmel_aes_buff_cleanup(aes_dd);
 err_aes_buff:
-       iounmap(aes_dd->io_base);
-aes_io_err:
-       clk_put(aes_dd->iclk);
-clk_err:
-       free_irq(aes_dd->irq, aes_dd);
-aes_irq_err:
 res_err:
        tasklet_kill(&aes_dd->done_task);
        tasklet_kill(&aes_dd->queue_task);
-       kfree(aes_dd);
-       aes_dd = NULL;
 aes_dd_err:
        dev_err(dev, "initialization failed.\n");
 
@@ -1469,16 +1463,6 @@ static int atmel_aes_remove(struct platform_device *pdev)
 
        atmel_aes_dma_cleanup(aes_dd);
 
-       iounmap(aes_dd->io_base);
-
-       clk_put(aes_dd->iclk);
-
-       if (aes_dd->irq > 0)
-               free_irq(aes_dd->irq, aes_dd);
-
-       kfree(aes_dd);
-       aes_dd = NULL;
-
        return 0;
 }
 
index 5b35433c5399b1264712d6b785cacea6abca5c9b..660d8c06540b93bb687e82b33ba53f33d818c4c2 100644 (file)
@@ -794,7 +794,11 @@ static void atmel_sha_finish_req(struct ahash_request *req, int err)
 
 static int atmel_sha_hw_init(struct atmel_sha_dev *dd)
 {
-       clk_prepare_enable(dd->iclk);
+       int err;
+
+       err = clk_prepare_enable(dd->iclk);
+       if (err)
+               return err;
 
        if (!(SHA_FLAGS_INIT & dd->flags)) {
                atmel_sha_write(dd, SHA_CR, SHA_CR_SWRST);
@@ -1345,11 +1349,9 @@ static int atmel_sha_probe(struct platform_device *pdev)
        struct crypto_platform_data     *pdata;
        struct device *dev = &pdev->dev;
        struct resource *sha_res;
-       unsigned long sha_phys_size;
        int err;
 
-       sha_dd = devm_kzalloc(&pdev->dev, sizeof(struct atmel_sha_dev),
-                               GFP_KERNEL);
+       sha_dd = devm_kzalloc(&pdev->dev, sizeof(*sha_dd), GFP_KERNEL);
        if (sha_dd == NULL) {
                dev_err(dev, "unable to alloc data struct.\n");
                err = -ENOMEM;
@@ -1378,7 +1380,6 @@ static int atmel_sha_probe(struct platform_device *pdev)
                goto res_err;
        }
        sha_dd->phys_base = sha_res->start;
-       sha_phys_size = resource_size(sha_res);
 
        /* Get the IRQ */
        sha_dd->irq = platform_get_irq(pdev,  0);
@@ -1388,26 +1389,26 @@ static int atmel_sha_probe(struct platform_device *pdev)
                goto res_err;
        }
 
-       err = request_irq(sha_dd->irq, atmel_sha_irq, IRQF_SHARED, "atmel-sha",
-                                               sha_dd);
+       err = devm_request_irq(&pdev->dev, sha_dd->irq, atmel_sha_irq,
+                              IRQF_SHARED, "atmel-sha", sha_dd);
        if (err) {
                dev_err(dev, "unable to request sha irq.\n");
                goto res_err;
        }
 
        /* Initializing the clock */
-       sha_dd->iclk = clk_get(&pdev->dev, "sha_clk");
+       sha_dd->iclk = devm_clk_get(&pdev->dev, "sha_clk");
        if (IS_ERR(sha_dd->iclk)) {
                dev_err(dev, "clock initialization failed.\n");
                err = PTR_ERR(sha_dd->iclk);
-               goto clk_err;
+               goto res_err;
        }
 
-       sha_dd->io_base = ioremap(sha_dd->phys_base, sha_phys_size);
+       sha_dd->io_base = devm_ioremap_resource(&pdev->dev, sha_res);
        if (!sha_dd->io_base) {
                dev_err(dev, "can't ioremap\n");
                err = -ENOMEM;
-               goto sha_io_err;
+               goto res_err;
        }
 
        atmel_sha_hw_version_init(sha_dd);
@@ -1421,12 +1422,12 @@ static int atmel_sha_probe(struct platform_device *pdev)
                        if (IS_ERR(pdata)) {
                                dev_err(&pdev->dev, "platform data not available\n");
                                err = PTR_ERR(pdata);
-                               goto err_pdata;
+                               goto res_err;
                        }
                }
                if (!pdata->dma_slave) {
                        err = -ENXIO;
-                       goto err_pdata;
+                       goto res_err;
                }
                err = atmel_sha_dma_init(sha_dd, pdata);
                if (err)
@@ -1457,12 +1458,6 @@ err_algs:
        if (sha_dd->caps.has_dma)
                atmel_sha_dma_cleanup(sha_dd);
 err_sha_dma:
-err_pdata:
-       iounmap(sha_dd->io_base);
-sha_io_err:
-       clk_put(sha_dd->iclk);
-clk_err:
-       free_irq(sha_dd->irq, sha_dd);
 res_err:
        tasklet_kill(&sha_dd->done_task);
 sha_dd_err:
index ca2999709eb4cf6fa38ca0096a482659a52c1f21..2c7a628d0375fd43d80f82084b38eed009a8f032 100644 (file)
@@ -218,7 +218,11 @@ static struct atmel_tdes_dev *atmel_tdes_find_dev(struct atmel_tdes_ctx *ctx)
 
 static int atmel_tdes_hw_init(struct atmel_tdes_dev *dd)
 {
-       clk_prepare_enable(dd->iclk);
+       int err;
+
+       err = clk_prepare_enable(dd->iclk);
+       if (err)
+               return err;
 
        if (!(dd->flags & TDES_FLAGS_INIT)) {
                atmel_tdes_write(dd, TDES_CR, TDES_CR_SWRST);
@@ -1355,7 +1359,6 @@ static int atmel_tdes_probe(struct platform_device *pdev)
        struct crypto_platform_data     *pdata;
        struct device *dev = &pdev->dev;
        struct resource *tdes_res;
-       unsigned long tdes_phys_size;
        int err;
 
        tdes_dd = devm_kmalloc(&pdev->dev, sizeof(*tdes_dd), GFP_KERNEL);
@@ -1389,7 +1392,6 @@ static int atmel_tdes_probe(struct platform_device *pdev)
                goto res_err;
        }
        tdes_dd->phys_base = tdes_res->start;
-       tdes_phys_size = resource_size(tdes_res);
 
        /* Get the IRQ */
        tdes_dd->irq = platform_get_irq(pdev,  0);
@@ -1399,26 +1401,26 @@ static int atmel_tdes_probe(struct platform_device *pdev)
                goto res_err;
        }
 
-       err = request_irq(tdes_dd->irq, atmel_tdes_irq, IRQF_SHARED,
-                       "atmel-tdes", tdes_dd);
+       err = devm_request_irq(&pdev->dev, tdes_dd->irq, atmel_tdes_irq,
+                              IRQF_SHARED, "atmel-tdes", tdes_dd);
        if (err) {
                dev_err(dev, "unable to request tdes irq.\n");
-               goto tdes_irq_err;
+               goto res_err;
        }
 
        /* Initializing the clock */
-       tdes_dd->iclk = clk_get(&pdev->dev, "tdes_clk");
+       tdes_dd->iclk = devm_clk_get(&pdev->dev, "tdes_clk");
        if (IS_ERR(tdes_dd->iclk)) {
                dev_err(dev, "clock initialization failed.\n");
                err = PTR_ERR(tdes_dd->iclk);
-               goto clk_err;
+               goto res_err;
        }
 
-       tdes_dd->io_base = ioremap(tdes_dd->phys_base, tdes_phys_size);
+       tdes_dd->io_base = devm_ioremap_resource(&pdev->dev, tdes_res);
        if (!tdes_dd->io_base) {
                dev_err(dev, "can't ioremap\n");
                err = -ENOMEM;
-               goto tdes_io_err;
+               goto res_err;
        }
 
        atmel_tdes_hw_version_init(tdes_dd);
@@ -1474,12 +1476,6 @@ err_tdes_dma:
 err_pdata:
        atmel_tdes_buff_cleanup(tdes_dd);
 err_tdes_buff:
-       iounmap(tdes_dd->io_base);
-tdes_io_err:
-       clk_put(tdes_dd->iclk);
-clk_err:
-       free_irq(tdes_dd->irq, tdes_dd);
-tdes_irq_err:
 res_err:
        tasklet_kill(&tdes_dd->done_task);
        tasklet_kill(&tdes_dd->queue_task);
@@ -1510,13 +1506,6 @@ static int atmel_tdes_remove(struct platform_device *pdev)
 
        atmel_tdes_buff_cleanup(tdes_dd);
 
-       iounmap(tdes_dd->io_base);
-
-       clk_put(tdes_dd->iclk);
-
-       if (tdes_dd->irq >= 0)
-               free_irq(tdes_dd->irq, tdes_dd);
-
        return 0;
 }
 
index 2f0b3337505da4e24664db8918ca9028a8eecd8c..95b73968cf72ff293afce00ae871fd5fc6b37bfd 100644 (file)
@@ -96,26 +96,6 @@ struct bfin_crypto_crc_ctx {
        u32                     key;
 };
 
-
-/*
- * derive number of elements in scatterlist
- */
-static int sg_count(struct scatterlist *sg_list)
-{
-       struct scatterlist *sg = sg_list;
-       int sg_nents = 1;
-
-       if (sg_list == NULL)
-               return 0;
-
-       while (!sg_is_last(sg)) {
-               sg_nents++;
-               sg = sg_next(sg);
-       }
-
-       return sg_nents;
-}
-
 /*
  * get element in scatter list by given index
  */
@@ -160,7 +140,7 @@ static int bfin_crypto_crc_init(struct ahash_request *req)
        }
        spin_unlock_bh(&crc_list.lock);
 
-       if (sg_count(req->src) > CRC_MAX_DMA_DESC) {
+       if (sg_nents(req->src) > CRC_MAX_DMA_DESC) {
                dev_dbg(ctx->crc->dev, "init: requested sg list is too big > %d\n",
                        CRC_MAX_DMA_DESC);
                return -EINVAL;
@@ -376,7 +356,8 @@ static int bfin_crypto_crc_handle_queue(struct bfin_crypto_crc *crc,
                        ctx->sg = req->src;
 
                /* Chop crc buffer size to multiple of 32 bit */
-               nsg = ctx->sg_nents = sg_count(ctx->sg);
+               nsg = sg_nents(ctx->sg);
+               ctx->sg_nents = nsg;
                ctx->sg_buflen = ctx->buflast_len + req->nbytes;
                ctx->bufnext_len = ctx->sg_buflen % 4;
                ctx->sg_buflen &= ~0x3;
index ba79d638f78200a189bda8763f2997222d60709d..ea8189f4b0212cc038f5f4363cf10f5e8a54099a 100644 (file)
@@ -1705,14 +1705,131 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher,
        return ret;
 }
 
+static int xts_ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher,
+                                const u8 *key, unsigned int keylen)
+{
+       struct caam_ctx *ctx = crypto_ablkcipher_ctx(ablkcipher);
+       struct device *jrdev = ctx->jrdev;
+       u32 *key_jump_cmd, *desc;
+       __be64 sector_size = cpu_to_be64(512);
+
+       if (keylen != 2 * AES_MIN_KEY_SIZE  && keylen != 2 * AES_MAX_KEY_SIZE) {
+               crypto_ablkcipher_set_flags(ablkcipher,
+                                           CRYPTO_TFM_RES_BAD_KEY_LEN);
+               dev_err(jrdev, "key size mismatch\n");
+               return -EINVAL;
+       }
+
+       memcpy(ctx->key, key, keylen);
+       ctx->key_dma = dma_map_single(jrdev, ctx->key, keylen, DMA_TO_DEVICE);
+       if (dma_mapping_error(jrdev, ctx->key_dma)) {
+               dev_err(jrdev, "unable to map key i/o memory\n");
+               return -ENOMEM;
+       }
+       ctx->enckeylen = keylen;
+
+       /* xts_ablkcipher_encrypt shared descriptor */
+       desc = ctx->sh_desc_enc;
+       init_sh_desc(desc, HDR_SHARE_SERIAL | HDR_SAVECTX);
+       /* Skip if already shared */
+       key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+                                  JUMP_COND_SHRD);
+
+       /* Load class1 keys only */
+       append_key_as_imm(desc, (void *)ctx->key, ctx->enckeylen,
+                         ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
+
+       /* Load sector size with index 40 bytes (0x28) */
+       append_cmd(desc, CMD_LOAD | IMMEDIATE | LDST_SRCDST_BYTE_CONTEXT |
+                  LDST_CLASS_1_CCB | (0x28 << LDST_OFFSET_SHIFT) | 8);
+       append_data(desc, (void *)&sector_size, 8);
+
+       set_jump_tgt_here(desc, key_jump_cmd);
+
+       /*
+        * create sequence for loading the sector index
+        * Upper 8B of IV - will be used as sector index
+        * Lower 8B of IV - will be discarded
+        */
+       append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT |
+                  LDST_CLASS_1_CCB | (0x20 << LDST_OFFSET_SHIFT) | 8);
+       append_seq_fifo_load(desc, 8, FIFOLD_CLASS_SKIP);
+
+       /* Load operation */
+       append_operation(desc, ctx->class1_alg_type | OP_ALG_AS_INITFINAL |
+                        OP_ALG_ENCRYPT);
+
+       /* Perform operation */
+       ablkcipher_append_src_dst(desc);
+
+       ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc, desc_bytes(desc),
+                                             DMA_TO_DEVICE);
+       if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) {
+               dev_err(jrdev, "unable to map shared descriptor\n");
+               return -ENOMEM;
+       }
+#ifdef DEBUG
+       print_hex_dump(KERN_ERR,
+                      "xts ablkcipher enc shdesc@" __stringify(__LINE__) ": ",
+                      DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1);
+#endif
+
+       /* xts_ablkcipher_decrypt shared descriptor */
+       desc = ctx->sh_desc_dec;
+
+       init_sh_desc(desc, HDR_SHARE_SERIAL | HDR_SAVECTX);
+       /* Skip if already shared */
+       key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+                                  JUMP_COND_SHRD);
+
+       /* Load class1 key only */
+       append_key_as_imm(desc, (void *)ctx->key, ctx->enckeylen,
+                         ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
+
+       /* Load sector size with index 40 bytes (0x28) */
+       append_cmd(desc, CMD_LOAD | IMMEDIATE | LDST_SRCDST_BYTE_CONTEXT |
+                  LDST_CLASS_1_CCB | (0x28 << LDST_OFFSET_SHIFT) | 8);
+       append_data(desc, (void *)&sector_size, 8);
+
+       set_jump_tgt_here(desc, key_jump_cmd);
+
+       /*
+        * create sequence for loading the sector index
+        * Upper 8B of IV - will be used as sector index
+        * Lower 8B of IV - will be discarded
+        */
+       append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT |
+                  LDST_CLASS_1_CCB | (0x20 << LDST_OFFSET_SHIFT) | 8);
+       append_seq_fifo_load(desc, 8, FIFOLD_CLASS_SKIP);
+
+       /* Load operation */
+       append_dec_op1(desc, ctx->class1_alg_type);
+
+       /* Perform operation */
+       ablkcipher_append_src_dst(desc);
+
+       ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc, desc_bytes(desc),
+                                             DMA_TO_DEVICE);
+       if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) {
+               dma_unmap_single(jrdev, ctx->sh_desc_enc_dma,
+                                desc_bytes(ctx->sh_desc_enc), DMA_TO_DEVICE);
+               dev_err(jrdev, "unable to map shared descriptor\n");
+               return -ENOMEM;
+       }
+#ifdef DEBUG
+       print_hex_dump(KERN_ERR,
+                      "xts ablkcipher dec shdesc@" __stringify(__LINE__) ": ",
+                      DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1);
+#endif
+
+       return 0;
+}
+
 /*
  * aead_edesc - s/w-extended aead descriptor
  * @assoc_nents: number of segments in associated data (SPI+Seq) scatterlist
- * @assoc_chained: if source is chained
  * @src_nents: number of segments in input scatterlist
- * @src_chained: if source is chained
  * @dst_nents: number of segments in output scatterlist
- * @dst_chained: if destination is chained
  * @iv_dma: dma address of iv for checking continuity and link table
  * @desc: h/w descriptor (variable length; must not exceed MAX_CAAM_DESCSIZE)
  * @sec4_sg_bytes: length of dma mapped sec4_sg space
@@ -1721,11 +1838,8 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher,
  */
 struct aead_edesc {
        int assoc_nents;
-       bool assoc_chained;
        int src_nents;
-       bool src_chained;
        int dst_nents;
-       bool dst_chained;
        dma_addr_t iv_dma;
        int sec4_sg_bytes;
        dma_addr_t sec4_sg_dma;
@@ -1736,9 +1850,7 @@ struct aead_edesc {
 /*
  * ablkcipher_edesc - s/w-extended ablkcipher descriptor
  * @src_nents: number of segments in input scatterlist
- * @src_chained: if source is chained
  * @dst_nents: number of segments in output scatterlist
- * @dst_chained: if destination is chained
  * @iv_dma: dma address of iv for checking continuity and link table
  * @desc: h/w descriptor (variable length; must not exceed MAX_CAAM_DESCSIZE)
  * @sec4_sg_bytes: length of dma mapped sec4_sg space
@@ -1747,9 +1859,7 @@ struct aead_edesc {
  */
 struct ablkcipher_edesc {
        int src_nents;
-       bool src_chained;
        int dst_nents;
-       bool dst_chained;
        dma_addr_t iv_dma;
        int sec4_sg_bytes;
        dma_addr_t sec4_sg_dma;
@@ -1759,18 +1869,15 @@ struct ablkcipher_edesc {
 
 static void caam_unmap(struct device *dev, struct scatterlist *src,
                       struct scatterlist *dst, int src_nents,
-                      bool src_chained, int dst_nents, bool dst_chained,
+                      int dst_nents,
                       dma_addr_t iv_dma, int ivsize, dma_addr_t sec4_sg_dma,
                       int sec4_sg_bytes)
 {
        if (dst != src) {
-               dma_unmap_sg_chained(dev, src, src_nents ? : 1, DMA_TO_DEVICE,
-                                    src_chained);
-               dma_unmap_sg_chained(dev, dst, dst_nents ? : 1, DMA_FROM_DEVICE,
-                                    dst_chained);
+               dma_unmap_sg(dev, src, src_nents ? : 1, DMA_TO_DEVICE);
+               dma_unmap_sg(dev, dst, dst_nents ? : 1, DMA_FROM_DEVICE);
        } else {
-               dma_unmap_sg_chained(dev, src, src_nents ? : 1,
-                                    DMA_BIDIRECTIONAL, src_chained);
+               dma_unmap_sg(dev, src, src_nents ? : 1, DMA_BIDIRECTIONAL);
        }
 
        if (iv_dma)
@@ -1785,8 +1892,7 @@ static void aead_unmap(struct device *dev,
                       struct aead_request *req)
 {
        caam_unmap(dev, req->src, req->dst,
-                  edesc->src_nents, edesc->src_chained, edesc->dst_nents,
-                  edesc->dst_chained, 0, 0,
+                  edesc->src_nents, edesc->dst_nents, 0, 0,
                   edesc->sec4_sg_dma, edesc->sec4_sg_bytes);
 }
 
@@ -1798,8 +1904,8 @@ static void ablkcipher_unmap(struct device *dev,
        int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
 
        caam_unmap(dev, req->src, req->dst,
-                  edesc->src_nents, edesc->src_chained, edesc->dst_nents,
-                  edesc->dst_chained, edesc->iv_dma, ivsize,
+                  edesc->src_nents, edesc->dst_nents,
+                  edesc->iv_dma, ivsize,
                   edesc->sec4_sg_dma, edesc->sec4_sg_bytes);
 }
 
@@ -2169,22 +2275,18 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
        struct aead_edesc *edesc;
        int sgc;
        bool all_contig = true;
-       bool src_chained = false, dst_chained = false;
        int sec4_sg_index, sec4_sg_len = 0, sec4_sg_bytes;
        unsigned int authsize = ctx->authsize;
 
        if (unlikely(req->dst != req->src)) {
-               src_nents = sg_count(req->src, req->assoclen + req->cryptlen,
-                                    &src_chained);
+               src_nents = sg_count(req->src, req->assoclen + req->cryptlen);
                dst_nents = sg_count(req->dst,
                                     req->assoclen + req->cryptlen +
-                                       (encrypt ? authsize : (-authsize)),
-                                    &dst_chained);
+                                       (encrypt ? authsize : (-authsize)));
        } else {
                src_nents = sg_count(req->src,
                                     req->assoclen + req->cryptlen +
-                                       (encrypt ? authsize : 0),
-                                    &src_chained);
+                                       (encrypt ? authsize : 0));
        }
 
        /* Check if data are contiguous. */
@@ -2207,37 +2309,35 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
        }
 
        if (likely(req->src == req->dst)) {
-               sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1,
-                                        DMA_BIDIRECTIONAL, src_chained);
+               sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+                                DMA_BIDIRECTIONAL);
                if (unlikely(!sgc)) {
                        dev_err(jrdev, "unable to map source\n");
                        kfree(edesc);
                        return ERR_PTR(-ENOMEM);
                }
        } else {
-               sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1,
-                                        DMA_TO_DEVICE, src_chained);
+               sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+                                DMA_TO_DEVICE);
                if (unlikely(!sgc)) {
                        dev_err(jrdev, "unable to map source\n");
                        kfree(edesc);
                        return ERR_PTR(-ENOMEM);
                }
 
-               sgc = dma_map_sg_chained(jrdev, req->dst, dst_nents ? : 1,
-                                        DMA_FROM_DEVICE, dst_chained);
+               sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1,
+                                DMA_FROM_DEVICE);
                if (unlikely(!sgc)) {
                        dev_err(jrdev, "unable to map destination\n");
-                       dma_unmap_sg_chained(jrdev, req->src, src_nents ? : 1,
-                                            DMA_TO_DEVICE, src_chained);
+                       dma_unmap_sg(jrdev, req->src, src_nents ? : 1,
+                                    DMA_TO_DEVICE);
                        kfree(edesc);
                        return ERR_PTR(-ENOMEM);
                }
        }
 
        edesc->src_nents = src_nents;
-       edesc->src_chained = src_chained;
        edesc->dst_nents = dst_nents;
-       edesc->dst_chained = dst_chained;
        edesc->sec4_sg = (void *)edesc + sizeof(struct aead_edesc) +
                         desc_bytes;
        *all_contig_ptr = all_contig;
@@ -2467,22 +2567,21 @@ static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request
        bool iv_contig = false;
        int sgc;
        int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
-       bool src_chained = false, dst_chained = false;
        int sec4_sg_index;
 
-       src_nents = sg_count(req->src, req->nbytes, &src_chained);
+       src_nents = sg_count(req->src, req->nbytes);
 
        if (req->dst != req->src)
-               dst_nents = sg_count(req->dst, req->nbytes, &dst_chained);
+               dst_nents = sg_count(req->dst, req->nbytes);
 
        if (likely(req->src == req->dst)) {
-               sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1,
-                                        DMA_BIDIRECTIONAL, src_chained);
+               sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+                                DMA_BIDIRECTIONAL);
        } else {
-               sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1,
-                                        DMA_TO_DEVICE, src_chained);
-               sgc = dma_map_sg_chained(jrdev, req->dst, dst_nents ? : 1,
-                                        DMA_FROM_DEVICE, dst_chained);
+               sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+                                DMA_TO_DEVICE);
+               sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1,
+                                DMA_FROM_DEVICE);
        }
 
        iv_dma = dma_map_single(jrdev, req->info, ivsize, DMA_TO_DEVICE);
@@ -2511,9 +2610,7 @@ static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request
        }
 
        edesc->src_nents = src_nents;
-       edesc->src_chained = src_chained;
        edesc->dst_nents = dst_nents;
-       edesc->dst_chained = dst_chained;
        edesc->sec4_sg_bytes = sec4_sg_bytes;
        edesc->sec4_sg = (void *)edesc + sizeof(struct ablkcipher_edesc) +
                         desc_bytes;
@@ -2646,22 +2743,21 @@ static struct ablkcipher_edesc *ablkcipher_giv_edesc_alloc(
        bool iv_contig = false;
        int sgc;
        int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
-       bool src_chained = false, dst_chained = false;
        int sec4_sg_index;
 
-       src_nents = sg_count(req->src, req->nbytes, &src_chained);
+       src_nents = sg_count(req->src, req->nbytes);
 
        if (unlikely(req->dst != req->src))
-               dst_nents = sg_count(req->dst, req->nbytes, &dst_chained);
+               dst_nents = sg_count(req->dst, req->nbytes);
 
        if (likely(req->src == req->dst)) {
-               sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1,
-                                        DMA_BIDIRECTIONAL, src_chained);
+               sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+                                DMA_BIDIRECTIONAL);
        } else {
-               sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1,
-                                        DMA_TO_DEVICE, src_chained);
-               sgc = dma_map_sg_chained(jrdev, req->dst, dst_nents ? : 1,
-                                        DMA_FROM_DEVICE, dst_chained);
+               sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+                                DMA_TO_DEVICE);
+               sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1,
+                                DMA_FROM_DEVICE);
        }
 
        /*
@@ -2690,9 +2786,7 @@ static struct ablkcipher_edesc *ablkcipher_giv_edesc_alloc(
        }
 
        edesc->src_nents = src_nents;
-       edesc->src_chained = src_chained;
        edesc->dst_nents = dst_nents;
-       edesc->dst_chained = dst_chained;
        edesc->sec4_sg_bytes = sec4_sg_bytes;
        edesc->sec4_sg = (void *)edesc + sizeof(struct ablkcipher_edesc) +
                         desc_bytes;
@@ -2871,7 +2965,23 @@ static struct caam_alg_template driver_algs[] = {
                        .ivsize = CTR_RFC3686_IV_SIZE,
                        },
                .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_CTR_MOD128,
-       }
+       },
+       {
+               .name = "xts(aes)",
+               .driver_name = "xts-aes-caam",
+               .blocksize = AES_BLOCK_SIZE,
+               .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+               .template_ablkcipher = {
+                       .setkey = xts_ablkcipher_setkey,
+                       .encrypt = ablkcipher_encrypt,
+                       .decrypt = ablkcipher_decrypt,
+                       .geniv = "eseqiv",
+                       .min_keysize = 2 * AES_MIN_KEY_SIZE,
+                       .max_keysize = 2 * AES_MAX_KEY_SIZE,
+                       .ivsize = AES_BLOCK_SIZE,
+                       },
+               .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_XTS,
+       },
 };
 
 static struct caam_aead_alg driver_aeads[] = {
index 94433b9fc200dc5f5322d29819ebfe74f3fd90f1..49106ea42887097a152ec21b7457004eec9f7fbd 100644 (file)
@@ -134,6 +134,15 @@ struct caam_hash_state {
        int current_buf;
 };
 
+struct caam_export_state {
+       u8 buf[CAAM_MAX_HASH_BLOCK_SIZE];
+       u8 caam_ctx[MAX_CTX_LEN];
+       int buflen;
+       int (*update)(struct ahash_request *req);
+       int (*final)(struct ahash_request *req);
+       int (*finup)(struct ahash_request *req);
+};
+
 /* Common job descriptor seq in/out ptr routines */
 
 /* Map state->caam_ctx, and append seq_out_ptr command that points to it */
@@ -181,10 +190,9 @@ static inline dma_addr_t buf_map_to_sec4_sg(struct device *jrdev,
 /* Map req->src and put it in link table */
 static inline void src_map_to_sec4_sg(struct device *jrdev,
                                      struct scatterlist *src, int src_nents,
-                                     struct sec4_sg_entry *sec4_sg,
-                                     bool chained)
+                                     struct sec4_sg_entry *sec4_sg)
 {
-       dma_map_sg_chained(jrdev, src, src_nents, DMA_TO_DEVICE, chained);
+       dma_map_sg(jrdev, src, src_nents, DMA_TO_DEVICE);
        sg_to_sec4_sg_last(src, src_nents, sec4_sg, 0);
 }
 
@@ -585,7 +593,6 @@ badkey:
  * ahash_edesc - s/w-extended ahash descriptor
  * @dst_dma: physical mapped address of req->result
  * @sec4_sg_dma: physical mapped address of h/w link table
- * @chained: if source is chained
  * @src_nents: number of segments in input scatterlist
  * @sec4_sg_bytes: length of dma mapped sec4_sg space
  * @sec4_sg: pointer to h/w link table
@@ -594,7 +601,6 @@ badkey:
 struct ahash_edesc {
        dma_addr_t dst_dma;
        dma_addr_t sec4_sg_dma;
-       bool chained;
        int src_nents;
        int sec4_sg_bytes;
        struct sec4_sg_entry *sec4_sg;
@@ -606,8 +612,7 @@ static inline void ahash_unmap(struct device *dev,
                        struct ahash_request *req, int dst_len)
 {
        if (edesc->src_nents)
-               dma_unmap_sg_chained(dev, req->src, edesc->src_nents,
-                                    DMA_TO_DEVICE, edesc->chained);
+               dma_unmap_sg(dev, req->src, edesc->src_nents, DMA_TO_DEVICE);
        if (edesc->dst_dma)
                dma_unmap_single(dev, edesc->dst_dma, dst_len, DMA_FROM_DEVICE);
 
@@ -788,7 +793,6 @@ static int ahash_update_ctx(struct ahash_request *req)
        dma_addr_t ptr = ctx->sh_desc_update_dma;
        int src_nents, sec4_sg_bytes, sec4_sg_src_index;
        struct ahash_edesc *edesc;
-       bool chained = false;
        int ret = 0;
        int sh_len;
 
@@ -797,8 +801,8 @@ static int ahash_update_ctx(struct ahash_request *req)
        to_hash = in_len - *next_buflen;
 
        if (to_hash) {
-               src_nents = __sg_count(req->src, req->nbytes - (*next_buflen),
-                                      &chained);
+               src_nents = sg_nents_for_len(req->src,
+                                            req->nbytes - (*next_buflen));
                sec4_sg_src_index = 1 + (*buflen ? 1 : 0);
                sec4_sg_bytes = (sec4_sg_src_index + src_nents) *
                                 sizeof(struct sec4_sg_entry);
@@ -816,7 +820,6 @@ static int ahash_update_ctx(struct ahash_request *req)
                }
 
                edesc->src_nents = src_nents;
-               edesc->chained = chained;
                edesc->sec4_sg_bytes = sec4_sg_bytes;
                edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) +
                                 DESC_JOB_IO_LEN;
@@ -829,12 +832,11 @@ static int ahash_update_ctx(struct ahash_request *req)
                state->buf_dma = try_buf_map_to_sec4_sg(jrdev,
                                                        edesc->sec4_sg + 1,
                                                        buf, state->buf_dma,
-                                                       *next_buflen, *buflen);
+                                                       *buflen, last_buflen);
 
                if (src_nents) {
                        src_map_to_sec4_sg(jrdev, req->src, src_nents,
-                                          edesc->sec4_sg + sec4_sg_src_index,
-                                          chained);
+                                          edesc->sec4_sg + sec4_sg_src_index);
                        if (*next_buflen)
                                scatterwalk_map_and_copy(next_buf, req->src,
                                                         to_hash - *buflen,
@@ -996,11 +998,10 @@ static int ahash_finup_ctx(struct ahash_request *req)
        int src_nents;
        int digestsize = crypto_ahash_digestsize(ahash);
        struct ahash_edesc *edesc;
-       bool chained = false;
        int ret = 0;
        int sh_len;
 
-       src_nents = __sg_count(req->src, req->nbytes, &chained);
+       src_nents = sg_nents_for_len(req->src, req->nbytes);
        sec4_sg_src_index = 1 + (buflen ? 1 : 0);
        sec4_sg_bytes = (sec4_sg_src_index + src_nents) *
                         sizeof(struct sec4_sg_entry);
@@ -1018,7 +1019,6 @@ static int ahash_finup_ctx(struct ahash_request *req)
        init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER | HDR_REVERSE);
 
        edesc->src_nents = src_nents;
-       edesc->chained = chained;
        edesc->sec4_sg_bytes = sec4_sg_bytes;
        edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) +
                         DESC_JOB_IO_LEN;
@@ -1033,7 +1033,7 @@ static int ahash_finup_ctx(struct ahash_request *req)
                                                last_buflen);
 
        src_map_to_sec4_sg(jrdev, req->src, src_nents, edesc->sec4_sg +
-                          sec4_sg_src_index, chained);
+                          sec4_sg_src_index);
 
        edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg,
                                            sec4_sg_bytes, DMA_TO_DEVICE);
@@ -1081,14 +1081,12 @@ static int ahash_digest(struct ahash_request *req)
        int src_nents, sec4_sg_bytes;
        dma_addr_t src_dma;
        struct ahash_edesc *edesc;
-       bool chained = false;
        int ret = 0;
        u32 options;
        int sh_len;
 
-       src_nents = sg_count(req->src, req->nbytes, &chained);
-       dma_map_sg_chained(jrdev, req->src, src_nents ? : 1, DMA_TO_DEVICE,
-                          chained);
+       src_nents = sg_count(req->src, req->nbytes);
+       dma_map_sg(jrdev, req->src, src_nents ? : 1, DMA_TO_DEVICE);
        sec4_sg_bytes = src_nents * sizeof(struct sec4_sg_entry);
 
        /* allocate space for base edesc and hw desc commands, link tables */
@@ -1102,7 +1100,6 @@ static int ahash_digest(struct ahash_request *req)
                          DESC_JOB_IO_LEN;
        edesc->sec4_sg_bytes = sec4_sg_bytes;
        edesc->src_nents = src_nents;
-       edesc->chained = chained;
 
        sh_len = desc_len(sh_desc);
        desc = edesc->hw_desc;
@@ -1228,7 +1225,6 @@ static int ahash_update_no_ctx(struct ahash_request *req)
        struct ahash_edesc *edesc;
        u32 *desc, *sh_desc = ctx->sh_desc_update_first;
        dma_addr_t ptr = ctx->sh_desc_update_first_dma;
-       bool chained = false;
        int ret = 0;
        int sh_len;
 
@@ -1236,8 +1232,8 @@ static int ahash_update_no_ctx(struct ahash_request *req)
        to_hash = in_len - *next_buflen;
 
        if (to_hash) {
-               src_nents = __sg_count(req->src, req->nbytes - (*next_buflen),
-                                      &chained);
+               src_nents = sg_nents_for_len(req->src,
+                                            req->nbytes - (*next_buflen));
                sec4_sg_bytes = (1 + src_nents) *
                                sizeof(struct sec4_sg_entry);
 
@@ -1254,7 +1250,6 @@ static int ahash_update_no_ctx(struct ahash_request *req)
                }
 
                edesc->src_nents = src_nents;
-               edesc->chained = chained;
                edesc->sec4_sg_bytes = sec4_sg_bytes;
                edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) +
                                 DESC_JOB_IO_LEN;
@@ -1263,7 +1258,7 @@ static int ahash_update_no_ctx(struct ahash_request *req)
                state->buf_dma = buf_map_to_sec4_sg(jrdev, edesc->sec4_sg,
                                                    buf, *buflen);
                src_map_to_sec4_sg(jrdev, req->src, src_nents,
-                                  edesc->sec4_sg + 1, chained);
+                                  edesc->sec4_sg + 1);
                if (*next_buflen) {
                        scatterwalk_map_and_copy(next_buf, req->src,
                                                 to_hash - *buflen,
@@ -1343,11 +1338,10 @@ static int ahash_finup_no_ctx(struct ahash_request *req)
        int sec4_sg_bytes, sec4_sg_src_index, src_nents;
        int digestsize = crypto_ahash_digestsize(ahash);
        struct ahash_edesc *edesc;
-       bool chained = false;
        int sh_len;
        int ret = 0;
 
-       src_nents = __sg_count(req->src, req->nbytes, &chained);
+       src_nents = sg_nents_for_len(req->src, req->nbytes);
        sec4_sg_src_index = 2;
        sec4_sg_bytes = (sec4_sg_src_index + src_nents) *
                         sizeof(struct sec4_sg_entry);
@@ -1365,7 +1359,6 @@ static int ahash_finup_no_ctx(struct ahash_request *req)
        init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER | HDR_REVERSE);
 
        edesc->src_nents = src_nents;
-       edesc->chained = chained;
        edesc->sec4_sg_bytes = sec4_sg_bytes;
        edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) +
                         DESC_JOB_IO_LEN;
@@ -1374,8 +1367,7 @@ static int ahash_finup_no_ctx(struct ahash_request *req)
                                                state->buf_dma, buflen,
                                                last_buflen);
 
-       src_map_to_sec4_sg(jrdev, req->src, src_nents, edesc->sec4_sg + 1,
-                          chained);
+       src_map_to_sec4_sg(jrdev, req->src, src_nents, edesc->sec4_sg + 1);
 
        edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg,
                                            sec4_sg_bytes, DMA_TO_DEVICE);
@@ -1429,7 +1421,6 @@ static int ahash_update_first(struct ahash_request *req)
        dma_addr_t src_dma;
        u32 options;
        struct ahash_edesc *edesc;
-       bool chained = false;
        int ret = 0;
        int sh_len;
 
@@ -1438,10 +1429,8 @@ static int ahash_update_first(struct ahash_request *req)
        to_hash = req->nbytes - *next_buflen;
 
        if (to_hash) {
-               src_nents = sg_count(req->src, req->nbytes - (*next_buflen),
-                                    &chained);
-               dma_map_sg_chained(jrdev, req->src, src_nents ? : 1,
-                                  DMA_TO_DEVICE, chained);
+               src_nents = sg_count(req->src, req->nbytes - (*next_buflen));
+               dma_map_sg(jrdev, req->src, src_nents ? : 1, DMA_TO_DEVICE);
                sec4_sg_bytes = src_nents * sizeof(struct sec4_sg_entry);
 
                /*
@@ -1457,7 +1446,6 @@ static int ahash_update_first(struct ahash_request *req)
                }
 
                edesc->src_nents = src_nents;
-               edesc->chained = chained;
                edesc->sec4_sg_bytes = sec4_sg_bytes;
                edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) +
                                 DESC_JOB_IO_LEN;
@@ -1574,25 +1562,42 @@ static int ahash_final(struct ahash_request *req)
 
 static int ahash_export(struct ahash_request *req, void *out)
 {
-       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
-       struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash);
        struct caam_hash_state *state = ahash_request_ctx(req);
+       struct caam_export_state *export = out;
+       int len;
+       u8 *buf;
+
+       if (state->current_buf) {
+               buf = state->buf_1;
+               len = state->buflen_1;
+       } else {
+               buf = state->buf_0;
+               len = state->buflen_1;
+       }
+
+       memcpy(export->buf, buf, len);
+       memcpy(export->caam_ctx, state->caam_ctx, sizeof(export->caam_ctx));
+       export->buflen = len;
+       export->update = state->update;
+       export->final = state->final;
+       export->finup = state->finup;
 
-       memcpy(out, ctx, sizeof(struct caam_hash_ctx));
-       memcpy(out + sizeof(struct caam_hash_ctx), state,
-              sizeof(struct caam_hash_state));
        return 0;
 }
 
 static int ahash_import(struct ahash_request *req, const void *in)
 {
-       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
-       struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash);
        struct caam_hash_state *state = ahash_request_ctx(req);
+       const struct caam_export_state *export = in;
+
+       memset(state, 0, sizeof(*state));
+       memcpy(state->buf_0, export->buf, export->buflen);
+       memcpy(state->caam_ctx, export->caam_ctx, sizeof(state->caam_ctx));
+       state->buflen_0 = export->buflen;
+       state->update = export->update;
+       state->final = export->final;
+       state->finup = export->finup;
 
-       memcpy(ctx, in, sizeof(struct caam_hash_ctx));
-       memcpy(state, in + sizeof(struct caam_hash_ctx),
-              sizeof(struct caam_hash_state));
        return 0;
 }
 
@@ -1626,8 +1631,9 @@ static struct caam_hash_template driver_hash[] = {
                        .setkey = ahash_setkey,
                        .halg = {
                                .digestsize = SHA1_DIGEST_SIZE,
-                               },
+                               .statesize = sizeof(struct caam_export_state),
                        },
+               },
                .alg_type = OP_ALG_ALGSEL_SHA1,
                .alg_op = OP_ALG_ALGSEL_SHA1 | OP_ALG_AAI_HMAC,
        }, {
@@ -1647,8 +1653,9 @@ static struct caam_hash_template driver_hash[] = {
                        .setkey = ahash_setkey,
                        .halg = {
                                .digestsize = SHA224_DIGEST_SIZE,
-                               },
+                               .statesize = sizeof(struct caam_export_state),
                        },
+               },
                .alg_type = OP_ALG_ALGSEL_SHA224,
                .alg_op = OP_ALG_ALGSEL_SHA224 | OP_ALG_AAI_HMAC,
        }, {
@@ -1668,8 +1675,9 @@ static struct caam_hash_template driver_hash[] = {
                        .setkey = ahash_setkey,
                        .halg = {
                                .digestsize = SHA256_DIGEST_SIZE,
-                               },
+                               .statesize = sizeof(struct caam_export_state),
                        },
+               },
                .alg_type = OP_ALG_ALGSEL_SHA256,
                .alg_op = OP_ALG_ALGSEL_SHA256 | OP_ALG_AAI_HMAC,
        }, {
@@ -1689,8 +1697,9 @@ static struct caam_hash_template driver_hash[] = {
                        .setkey = ahash_setkey,
                        .halg = {
                                .digestsize = SHA384_DIGEST_SIZE,
-                               },
+                               .statesize = sizeof(struct caam_export_state),
                        },
+               },
                .alg_type = OP_ALG_ALGSEL_SHA384,
                .alg_op = OP_ALG_ALGSEL_SHA384 | OP_ALG_AAI_HMAC,
        }, {
@@ -1710,8 +1719,9 @@ static struct caam_hash_template driver_hash[] = {
                        .setkey = ahash_setkey,
                        .halg = {
                                .digestsize = SHA512_DIGEST_SIZE,
-                               },
+                               .statesize = sizeof(struct caam_export_state),
                        },
+               },
                .alg_type = OP_ALG_ALGSEL_SHA512,
                .alg_op = OP_ALG_ALGSEL_SHA512 | OP_ALG_AAI_HMAC,
        }, {
@@ -1731,8 +1741,9 @@ static struct caam_hash_template driver_hash[] = {
                        .setkey = ahash_setkey,
                        .halg = {
                                .digestsize = MD5_DIGEST_SIZE,
-                               },
+                               .statesize = sizeof(struct caam_export_state),
                        },
+               },
                .alg_type = OP_ALG_ALGSEL_MD5,
                .alg_op = OP_ALG_ALGSEL_MD5 | OP_ALG_AAI_HMAC,
        },
@@ -1952,8 +1963,9 @@ static int __init caam_algapi_hash_init(void)
 
                err = crypto_register_ahash(&t_alg->ahash_alg);
                if (err) {
-                       pr_warn("%s alg registration failed\n",
-                               t_alg->ahash_alg.halg.base.cra_driver_name);
+                       pr_warn("%s alg registration failed: %d\n",
+                               t_alg->ahash_alg.halg.base.cra_driver_name,
+                               err);
                        kfree(t_alg);
                } else
                        list_add_tail(&t_alg->entry, &hash_list);
@@ -1968,8 +1980,9 @@ static int __init caam_algapi_hash_init(void)
 
                err = crypto_register_ahash(&t_alg->ahash_alg);
                if (err) {
-                       pr_warn("%s alg registration failed\n",
-                               t_alg->ahash_alg.halg.base.cra_driver_name);
+                       pr_warn("%s alg registration failed: %d\n",
+                               t_alg->ahash_alg.halg.base.cra_driver_name,
+                               err);
                        kfree(t_alg);
                } else
                        list_add_tail(&t_alg->entry, &hash_list);
index 983d663ef6714c46fd7ee4d724e0e3d1adeb9002..1e93c6af2275b834f460d7a825bf56d4830946f3 100644 (file)
@@ -1492,7 +1492,6 @@ struct sec4_sg_entry {
 #define JUMP_JSL               (1 << JUMP_JSL_SHIFT)
 
 #define JUMP_TYPE_SHIFT                22
-#define JUMP_TYPE_MASK         (0x03 << JUMP_TYPE_SHIFT)
 #define JUMP_TYPE_LOCAL                (0x00 << JUMP_TYPE_SHIFT)
 #define JUMP_TYPE_NONLOCAL     (0x01 << JUMP_TYPE_SHIFT)
 #define JUMP_TYPE_HALT         (0x02 << JUMP_TYPE_SHIFT)
index 18cd6d1f587049a63af53d28225cbbe1fb5a866d..12ec6616e89d130f6b5719acb3c3f054a96018ea 100644 (file)
@@ -69,81 +69,13 @@ static inline struct sec4_sg_entry *sg_to_sec4_sg_len(
        return sec4_sg_ptr - 1;
 }
 
-/* count number of elements in scatterlist */
-static inline int __sg_count(struct scatterlist *sg_list, int nbytes,
-                            bool *chained)
-{
-       struct scatterlist *sg = sg_list;
-       int sg_nents = 0;
-
-       while (nbytes > 0) {
-               sg_nents++;
-               nbytes -= sg->length;
-               if (!sg_is_last(sg) && (sg + 1)->length == 0)
-                       *chained = true;
-               sg = sg_next(sg);
-       }
-
-       return sg_nents;
-}
-
 /* derive number of elements in scatterlist, but return 0 for 1 */
-static inline int sg_count(struct scatterlist *sg_list, int nbytes,
-                            bool *chained)
+static inline int sg_count(struct scatterlist *sg_list, int nbytes)
 {
-       int sg_nents = __sg_count(sg_list, nbytes, chained);
+       int sg_nents = sg_nents_for_len(sg_list, nbytes);
 
        if (likely(sg_nents == 1))
                return 0;
 
        return sg_nents;
 }
-
-static inline void dma_unmap_sg_chained(
-       struct device *dev, struct scatterlist *sg, unsigned int nents,
-       enum dma_data_direction dir, bool chained)
-{
-       if (unlikely(chained)) {
-               int i;
-               struct scatterlist *tsg = sg;
-
-               /*
-                * Use a local copy of the sg pointer to avoid moving the
-                * head of the list pointed to by sg as we walk the list.
-                */
-               for (i = 0; i < nents; i++) {
-                       dma_unmap_sg(dev, tsg, 1, dir);
-                       tsg = sg_next(tsg);
-               }
-       } else if (nents) {
-               dma_unmap_sg(dev, sg, nents, dir);
-       }
-}
-
-static inline int dma_map_sg_chained(
-       struct device *dev, struct scatterlist *sg, unsigned int nents,
-       enum dma_data_direction dir, bool chained)
-{
-       if (unlikely(chained)) {
-               int i;
-               struct scatterlist *tsg = sg;
-
-               /*
-                * Use a local copy of the sg pointer to avoid moving the
-                * head of the list pointed to by sg as we walk the list.
-                */
-               for (i = 0; i < nents; i++) {
-                       if (!dma_map_sg(dev, tsg, 1, dir)) {
-                               dma_unmap_sg_chained(dev, sg, i, dir,
-                                                    chained);
-                               nents = 0;
-                               break;
-                       }
-
-                       tsg = sg_next(tsg);
-               }
-       } else
-               nents = dma_map_sg(dev, sg, nents, dir);
-
-       return nents;
-}
index ae38f6b6cc10c3c97d2f3543aa896a454c1bbd9a..3cd8481065f8af199fba03d366d49cff9bb92703 100644 (file)
@@ -5,12 +5,12 @@ config CRYPTO_DEV_CCP_DD
        select HW_RANDOM
        help
          Provides the interface to use the AMD Cryptographic Coprocessor
-         which can be used to accelerate or offload encryption operations
-         such as SHA, AES and more. If you choose 'M' here, this module
-         will be called ccp.
+         which can be used to offload encryption operations such as SHA,
+         AES and more. If you choose 'M' here, this module will be called
+         ccp.
 
 config CRYPTO_DEV_CCP_CRYPTO
-       tristate "Encryption and hashing acceleration support"
+       tristate "Encryption and hashing offload support"
        depends on CRYPTO_DEV_CCP_DD
        default m
        select CRYPTO_HASH
@@ -18,6 +18,5 @@ config CRYPTO_DEV_CCP_CRYPTO
        select CRYPTO_AUTHENC
        help
          Support for using the cryptographic API with the AMD Cryptographic
-         Coprocessor. This module supports acceleration and offload of SHA
-         and AES algorithms.  If you choose 'M' here, this module will be
-         called ccp_crypto.
+         Coprocessor. This module supports offload of SHA and AES algorithms.
+         If you choose 'M' here, this module will be called ccp_crypto.
index ea7e8446956a62ec33f0a0db8ead012ffbb56787..d89f20c04266b31ad85bf82ccf5a2169c52ba727 100644 (file)
@@ -118,10 +118,19 @@ static int ccp_do_cmac_update(struct ahash_request *req, unsigned int nbytes,
        if (rctx->buf_count) {
                sg_init_one(&rctx->buf_sg, rctx->buf, rctx->buf_count);
                sg = ccp_crypto_sg_table_add(&rctx->data_sg, &rctx->buf_sg);
+               if (!sg) {
+                       ret = -EINVAL;
+                       goto e_free;
+               }
        }
 
-       if (nbytes)
+       if (nbytes) {
                sg = ccp_crypto_sg_table_add(&rctx->data_sg, req->src);
+               if (!sg) {
+                       ret = -EINVAL;
+                       goto e_free;
+               }
+       }
 
        if (need_pad) {
                int pad_length = block_size - (len & (block_size - 1));
@@ -132,6 +141,10 @@ static int ccp_do_cmac_update(struct ahash_request *req, unsigned int nbytes,
                rctx->pad[0] = 0x80;
                sg_init_one(&rctx->pad_sg, rctx->pad, pad_length);
                sg = ccp_crypto_sg_table_add(&rctx->data_sg, &rctx->pad_sg);
+               if (!sg) {
+                       ret = -EINVAL;
+                       goto e_free;
+               }
        }
        if (sg) {
                sg_mark_end(sg);
@@ -162,6 +175,11 @@ static int ccp_do_cmac_update(struct ahash_request *req, unsigned int nbytes,
 
        ret = ccp_crypto_enqueue_request(&req->base, &rctx->cmd);
 
+       return ret;
+
+e_free:
+       sg_free_table(&rctx->data_sg);
+
        return ret;
 }
 
index bdec01ec608f48e49375a0d17149a9d326574f17..e0380e59c361f3624af6d61caaa7efc47a0986ee 100644 (file)
@@ -305,14 +305,16 @@ struct scatterlist *ccp_crypto_sg_table_add(struct sg_table *table,
        for (sg = table->sgl; sg; sg = sg_next(sg))
                if (!sg_page(sg))
                        break;
-       BUG_ON(!sg);
+       if (WARN_ON(!sg))
+               return NULL;
 
        for (; sg && sg_add; sg = sg_next(sg), sg_add = sg_next(sg_add)) {
                sg_set_page(sg, sg_page(sg_add), sg_add->length,
                            sg_add->offset);
                sg_last = sg;
        }
-       BUG_ON(sg_add);
+       if (WARN_ON(sg_add))
+               return NULL;
 
        return sg_last;
 }
index 507b34e0cc195be71879976f2215d8b64d356644..d14b3f28e010897990223fffc8f932fe368a033d 100644 (file)
@@ -107,7 +107,15 @@ static int ccp_do_sha_update(struct ahash_request *req, unsigned int nbytes,
 
                sg_init_one(&rctx->buf_sg, rctx->buf, rctx->buf_count);
                sg = ccp_crypto_sg_table_add(&rctx->data_sg, &rctx->buf_sg);
+               if (!sg) {
+                       ret = -EINVAL;
+                       goto e_free;
+               }
                sg = ccp_crypto_sg_table_add(&rctx->data_sg, req->src);
+               if (!sg) {
+                       ret = -EINVAL;
+                       goto e_free;
+               }
                sg_mark_end(sg);
 
                sg = rctx->data_sg.sgl;
@@ -141,6 +149,11 @@ static int ccp_do_sha_update(struct ahash_request *req, unsigned int nbytes,
 
        ret = ccp_crypto_enqueue_request(&req->base, &rctx->cmd);
 
+       return ret;
+
+e_free:
+       sg_free_table(&rctx->data_sg);
+
        return ret;
 }
 
index d09c6c4af4aabfcba7cdd48e33c6d88ab80b5453..c6e883b296a973628df1a000990342728620ad9b 100644 (file)
@@ -611,15 +611,16 @@ static void ccp_get_dm_area(struct ccp_dm_workarea *wa, unsigned int wa_offset,
                                 1);
 }
 
-static void ccp_reverse_set_dm_area(struct ccp_dm_workarea *wa,
-                                   struct scatterlist *sg,
-                                   unsigned int len, unsigned int se_len,
-                                   bool sign_extend)
+static int ccp_reverse_set_dm_area(struct ccp_dm_workarea *wa,
+                                  struct scatterlist *sg,
+                                  unsigned int len, unsigned int se_len,
+                                  bool sign_extend)
 {
        unsigned int nbytes, sg_offset, dm_offset, ksb_len, i;
        u8 buffer[CCP_REVERSE_BUF_SIZE];
 
-       BUG_ON(se_len > sizeof(buffer));
+       if (WARN_ON(se_len > sizeof(buffer)))
+               return -EINVAL;
 
        sg_offset = len;
        dm_offset = 0;
@@ -642,6 +643,8 @@ static void ccp_reverse_set_dm_area(struct ccp_dm_workarea *wa,
                                       se_len - ksb_len);
                }
        }
+
+       return 0;
 }
 
 static void ccp_reverse_get_dm_area(struct ccp_dm_workarea *wa,
@@ -1606,8 +1609,10 @@ static int ccp_run_rsa_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
        if (ret)
                goto e_ksb;
 
-       ccp_reverse_set_dm_area(&exp, rsa->exp, rsa->exp_len, CCP_KSB_BYTES,
-                               false);
+       ret = ccp_reverse_set_dm_area(&exp, rsa->exp, rsa->exp_len,
+                                     CCP_KSB_BYTES, false);
+       if (ret)
+               goto e_exp;
        ret = ccp_copy_to_ksb(cmd_q, &exp, op.jobid, op.ksb_key,
                              CCP_PASSTHRU_BYTESWAP_NOOP);
        if (ret) {
@@ -1623,11 +1628,15 @@ static int ccp_run_rsa_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
        if (ret)
                goto e_exp;
 
-       ccp_reverse_set_dm_area(&src, rsa->mod, rsa->mod_len, CCP_KSB_BYTES,
-                               false);
+       ret = ccp_reverse_set_dm_area(&src, rsa->mod, rsa->mod_len,
+                                     CCP_KSB_BYTES, false);
+       if (ret)
+               goto e_src;
        src.address += o_len;   /* Adjust the address for the copy operation */
-       ccp_reverse_set_dm_area(&src, rsa->src, rsa->src_len, CCP_KSB_BYTES,
-                               false);
+       ret = ccp_reverse_set_dm_area(&src, rsa->src, rsa->src_len,
+                                     CCP_KSB_BYTES, false);
+       if (ret)
+               goto e_src;
        src.address -= o_len;   /* Reset the address to original value */
 
        /* Prepare the output area for the operation */
@@ -1841,21 +1850,27 @@ static int ccp_run_ecc_mm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
        save = src.address;
 
        /* Copy the ECC modulus */
-       ccp_reverse_set_dm_area(&src, ecc->mod, ecc->mod_len,
-                               CCP_ECC_OPERAND_SIZE, false);
+       ret = ccp_reverse_set_dm_area(&src, ecc->mod, ecc->mod_len,
+                                     CCP_ECC_OPERAND_SIZE, false);
+       if (ret)
+               goto e_src;
        src.address += CCP_ECC_OPERAND_SIZE;
 
        /* Copy the first operand */
-       ccp_reverse_set_dm_area(&src, ecc->u.mm.operand_1,
-                               ecc->u.mm.operand_1_len,
-                               CCP_ECC_OPERAND_SIZE, false);
+       ret = ccp_reverse_set_dm_area(&src, ecc->u.mm.operand_1,
+                                     ecc->u.mm.operand_1_len,
+                                     CCP_ECC_OPERAND_SIZE, false);
+       if (ret)
+               goto e_src;
        src.address += CCP_ECC_OPERAND_SIZE;
 
        if (ecc->function != CCP_ECC_FUNCTION_MINV_384BIT) {
                /* Copy the second operand */
-               ccp_reverse_set_dm_area(&src, ecc->u.mm.operand_2,
-                                       ecc->u.mm.operand_2_len,
-                                       CCP_ECC_OPERAND_SIZE, false);
+               ret = ccp_reverse_set_dm_area(&src, ecc->u.mm.operand_2,
+                                             ecc->u.mm.operand_2_len,
+                                             CCP_ECC_OPERAND_SIZE, false);
+               if (ret)
+                       goto e_src;
                src.address += CCP_ECC_OPERAND_SIZE;
        }
 
@@ -1960,18 +1975,24 @@ static int ccp_run_ecc_pm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
        save = src.address;
 
        /* Copy the ECC modulus */
-       ccp_reverse_set_dm_area(&src, ecc->mod, ecc->mod_len,
-                               CCP_ECC_OPERAND_SIZE, false);
+       ret = ccp_reverse_set_dm_area(&src, ecc->mod, ecc->mod_len,
+                                     CCP_ECC_OPERAND_SIZE, false);
+       if (ret)
+               goto e_src;
        src.address += CCP_ECC_OPERAND_SIZE;
 
        /* Copy the first point X and Y coordinate */
-       ccp_reverse_set_dm_area(&src, ecc->u.pm.point_1.x,
-                               ecc->u.pm.point_1.x_len,
-                               CCP_ECC_OPERAND_SIZE, false);
+       ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.point_1.x,
+                                     ecc->u.pm.point_1.x_len,
+                                     CCP_ECC_OPERAND_SIZE, false);
+       if (ret)
+               goto e_src;
        src.address += CCP_ECC_OPERAND_SIZE;
-       ccp_reverse_set_dm_area(&src, ecc->u.pm.point_1.y,
-                               ecc->u.pm.point_1.y_len,
-                               CCP_ECC_OPERAND_SIZE, false);
+       ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.point_1.y,
+                                     ecc->u.pm.point_1.y_len,
+                                     CCP_ECC_OPERAND_SIZE, false);
+       if (ret)
+               goto e_src;
        src.address += CCP_ECC_OPERAND_SIZE;
 
        /* Set the first point Z coordianate to 1 */
@@ -1980,13 +2001,17 @@ static int ccp_run_ecc_pm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
 
        if (ecc->function == CCP_ECC_FUNCTION_PADD_384BIT) {
                /* Copy the second point X and Y coordinate */
-               ccp_reverse_set_dm_area(&src, ecc->u.pm.point_2.x,
-                                       ecc->u.pm.point_2.x_len,
-                                       CCP_ECC_OPERAND_SIZE, false);
+               ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.point_2.x,
+                                             ecc->u.pm.point_2.x_len,
+                                             CCP_ECC_OPERAND_SIZE, false);
+               if (ret)
+                       goto e_src;
                src.address += CCP_ECC_OPERAND_SIZE;
-               ccp_reverse_set_dm_area(&src, ecc->u.pm.point_2.y,
-                                       ecc->u.pm.point_2.y_len,
-                                       CCP_ECC_OPERAND_SIZE, false);
+               ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.point_2.y,
+                                             ecc->u.pm.point_2.y_len,
+                                             CCP_ECC_OPERAND_SIZE, false);
+               if (ret)
+                       goto e_src;
                src.address += CCP_ECC_OPERAND_SIZE;
 
                /* Set the second point Z coordianate to 1 */
@@ -1994,16 +2019,21 @@ static int ccp_run_ecc_pm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
                src.address += CCP_ECC_OPERAND_SIZE;
        } else {
                /* Copy the Domain "a" parameter */
-               ccp_reverse_set_dm_area(&src, ecc->u.pm.domain_a,
-                                       ecc->u.pm.domain_a_len,
-                                       CCP_ECC_OPERAND_SIZE, false);
+               ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.domain_a,
+                                             ecc->u.pm.domain_a_len,
+                                             CCP_ECC_OPERAND_SIZE, false);
+               if (ret)
+                       goto e_src;
                src.address += CCP_ECC_OPERAND_SIZE;
 
                if (ecc->function == CCP_ECC_FUNCTION_PMUL_384BIT) {
                        /* Copy the scalar value */
-                       ccp_reverse_set_dm_area(&src, ecc->u.pm.scalar,
-                                               ecc->u.pm.scalar_len,
-                                               CCP_ECC_OPERAND_SIZE, false);
+                       ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.scalar,
+                                                     ecc->u.pm.scalar_len,
+                                                     CCP_ECC_OPERAND_SIZE,
+                                                     false);
+                       if (ret)
+                               goto e_src;
                        src.address += CCP_ECC_OPERAND_SIZE;
                }
        }
index af190d4795a89df056483485992c0661f79fd2da..6ade02f04f91cc7fdaafcdfa2c0b8bbe97e198f3 100644 (file)
@@ -319,7 +319,7 @@ static const struct pci_device_id ccp_pci_table[] = {
 MODULE_DEVICE_TABLE(pci, ccp_pci_table);
 
 static struct pci_driver ccp_pci_driver = {
-       .name = "AMD Cryptographic Coprocessor",
+       .name = "ccp",
        .id_table = ccp_pci_table,
        .probe = ccp_pci_probe,
        .remove = ccp_pci_remove,
index bb241c3ab6b9cad5fec5ed22f4a9f96b5d7da551..8b923b7e9389aabd96ee10350c0f0ea9f66556f6 100644 (file)
@@ -29,7 +29,6 @@
 #include "ccp-dev.h"
 
 struct ccp_platform {
-       int use_acpi;
        int coherent;
 };
 
@@ -95,7 +94,6 @@ static int ccp_platform_probe(struct platform_device *pdev)
        struct ccp_device *ccp;
        struct ccp_platform *ccp_platform;
        struct device *dev = &pdev->dev;
-       struct acpi_device *adev = ACPI_COMPANION(dev);
        struct resource *ior;
        int ret;
 
@@ -112,8 +110,6 @@ static int ccp_platform_probe(struct platform_device *pdev)
        ccp->get_irq = ccp_get_irqs;
        ccp->free_irq = ccp_free_irqs;
 
-       ccp_platform->use_acpi = (!adev || acpi_disabled) ? 0 : 1;
-
        ior = ccp_find_mmio_area(ccp);
        ccp->io_map = devm_ioremap_resource(dev, ior);
        if (IS_ERR(ccp->io_map)) {
@@ -229,7 +225,7 @@ MODULE_DEVICE_TABLE(of, ccp_of_match);
 
 static struct platform_driver ccp_platform_driver = {
        .driver = {
-               .name = "AMD Cryptographic Coprocessor",
+               .name = "ccp",
 #ifdef CONFIG_ACPI
                .acpi_match_table = ccp_acpi_match,
 #endif
index bc2a55bc35e4727290319b462008c695b7e2923e..bd985e72520b56428af796e1531cfb8771099a9c 100644 (file)
 
 #define CESA_SA_DESC_MAC_DATA(offset)                                  \
        cpu_to_le32(CESA_SA_DATA_SRAM_OFFSET + (offset))
-#define CESA_SA_DESC_MAC_DATA_MSK              GENMASK(15, 0)
+#define CESA_SA_DESC_MAC_DATA_MSK              cpu_to_le32(GENMASK(15, 0))
 
 #define CESA_SA_DESC_MAC_TOTAL_LEN(total_len)  cpu_to_le32((total_len) << 16)
-#define CESA_SA_DESC_MAC_TOTAL_LEN_MSK         GENMASK(31, 16)
+#define CESA_SA_DESC_MAC_TOTAL_LEN_MSK         cpu_to_le32(GENMASK(31, 16))
 
 #define CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX     0xffff
 
 #define CESA_SA_DESC_MAC_DIGEST(offset)                                        \
        cpu_to_le32(CESA_SA_MAC_DIG_SRAM_OFFSET + (offset))
-#define CESA_SA_DESC_MAC_DIGEST_MSK            GENMASK(15, 0)
+#define CESA_SA_DESC_MAC_DIGEST_MSK            cpu_to_le32(GENMASK(15, 0))
 
 #define CESA_SA_DESC_MAC_FRAG_LEN(frag_len)    cpu_to_le32((frag_len) << 16)
-#define CESA_SA_DESC_MAC_FRAG_LEN_MSK          GENMASK(31, 16)
+#define CESA_SA_DESC_MAC_FRAG_LEN_MSK          cpu_to_le32(GENMASK(31, 16))
 
 #define CESA_SA_DESC_MAC_IV(offset)                                    \
        cpu_to_le32((CESA_SA_MAC_IIV_SRAM_OFFSET + (offset)) |          \
  * to be executed.
  */
 struct mv_cesa_sec_accel_desc {
-       u32 config;
-       u32 enc_p;
-       u32 enc_len;
-       u32 enc_key_p;
-       u32 enc_iv;
-       u32 mac_src_p;
-       u32 mac_digest;
-       u32 mac_iv;
+       __le32 config;
+       __le32 enc_p;
+       __le32 enc_len;
+       __le32 enc_key_p;
+       __le32 enc_iv;
+       __le32 mac_src_p;
+       __le32 mac_digest;
+       __le32 mac_iv;
 };
 
 /**
@@ -293,11 +293,13 @@ struct mv_cesa_op_ctx {
  * operation.
  */
 struct mv_cesa_tdma_desc {
-       u32 byte_cnt;
-       u32 src;
-       u32 dst;
-       u32 next_dma;
-       u32 cur_dma;
+       __le32 byte_cnt;
+       __le32 src;
+       __le32 dst;
+       __le32 next_dma;
+
+       /* Software state */
+       dma_addr_t cur_dma;
        struct mv_cesa_tdma_desc *next;
        union {
                struct mv_cesa_op_ctx *op;
@@ -612,7 +614,8 @@ struct mv_cesa_ahash_req {
        u64 len;
        int src_nents;
        bool last_req;
-       __be32 state[8];
+       bool algo_le;
+       u32 state[8];
 };
 
 /* CESA functions */
@@ -626,7 +629,7 @@ static inline void mv_cesa_update_op_cfg(struct mv_cesa_op_ctx *op,
        op->desc.config |= cpu_to_le32(cfg);
 }
 
-static inline u32 mv_cesa_get_op_cfg(struct mv_cesa_op_ctx *op)
+static inline u32 mv_cesa_get_op_cfg(const struct mv_cesa_op_ctx *op)
 {
        return le32_to_cpu(op->desc.config);
 }
@@ -676,7 +679,7 @@ static inline void mv_cesa_set_int_mask(struct mv_cesa_engine *engine,
        if (int_mask == engine->int_mask)
                return;
 
-       writel(int_mask, engine->regs + CESA_SA_INT_MSK);
+       writel_relaxed(int_mask, engine->regs + CESA_SA_INT_MSK);
        engine->int_mask = int_mask;
 }
 
@@ -685,6 +688,12 @@ static inline u32 mv_cesa_get_int_mask(struct mv_cesa_engine *engine)
        return engine->int_mask;
 }
 
+static inline bool mv_cesa_mac_op_is_first_frag(const struct mv_cesa_op_ctx *op)
+{
+       return (mv_cesa_get_op_cfg(op) & CESA_SA_DESC_CFG_FRAG_MSK) ==
+               CESA_SA_DESC_CFG_FIRST_FRAG;
+}
+
 int mv_cesa_queue_req(struct crypto_async_request *req);
 
 /*
@@ -789,10 +798,8 @@ int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
                                  dma_addr_t dst, dma_addr_t src, u32 size,
                                  u32 flags, gfp_t gfp_flags);
 
-int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain,
-                                u32 flags);
-
-int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, u32 flags);
+int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain, gfp_t flags);
+int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, gfp_t flags);
 
 int mv_cesa_dma_add_op_transfers(struct mv_cesa_tdma_chain *chain,
                                 struct mv_cesa_dma_iter *dma_iter,
index 3df2f4e7adb29a681f18a4f77acdf9df2f5e411b..6edae64bb3873e9a5dac2a0ca9e5f96555bc2695 100644 (file)
@@ -98,14 +98,14 @@ static void mv_cesa_ablkcipher_std_step(struct ablkcipher_request *req)
 
        /* FIXME: only update enc_len field */
        if (!sreq->skip_ctx) {
-               memcpy(engine->sram, &sreq->op, sizeof(sreq->op));
+               memcpy_toio(engine->sram, &sreq->op, sizeof(sreq->op));
                sreq->skip_ctx = true;
        } else {
-               memcpy(engine->sram, &sreq->op, sizeof(sreq->op.desc));
+               memcpy_toio(engine->sram, &sreq->op, sizeof(sreq->op.desc));
        }
 
        mv_cesa_set_int_mask(engine, CESA_SA_INT_ACCEL0_DONE);
-       writel(CESA_SA_CFG_PARA_DIS, engine->regs + CESA_SA_CFG);
+       writel_relaxed(CESA_SA_CFG_PARA_DIS, engine->regs + CESA_SA_CFG);
        writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
 }
 
@@ -145,8 +145,9 @@ static int mv_cesa_ablkcipher_process(struct crypto_async_request *req,
        if (ret)
                return ret;
 
-       memcpy(ablkreq->info, engine->sram + CESA_SA_CRYPT_IV_SRAM_OFFSET,
-              crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(ablkreq)));
+       memcpy_fromio(ablkreq->info,
+                     engine->sram + CESA_SA_CRYPT_IV_SRAM_OFFSET,
+                     crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(ablkreq)));
 
        return 0;
 }
@@ -181,7 +182,7 @@ mv_cesa_ablkcipher_std_prepare(struct ablkcipher_request *req)
        sreq->size = 0;
        sreq->offset = 0;
        mv_cesa_adjust_op(engine, &sreq->op);
-       memcpy(engine->sram, &sreq->op, sizeof(sreq->op));
+       memcpy_toio(engine->sram, &sreq->op, sizeof(sreq->op));
 }
 
 static inline void mv_cesa_ablkcipher_prepare(struct crypto_async_request *req,
index e8d0d712813746bd9360f4726e8cffb18110ae02..6ec55b4a087bf740d80d3fa441d1b25c7a78ec77 100644 (file)
@@ -27,10 +27,10 @@ mv_cesa_ahash_req_iter_init(struct mv_cesa_ahash_dma_iter *iter,
                            struct ahash_request *req)
 {
        struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
-       unsigned int len = req->nbytes;
+       unsigned int len = req->nbytes + creq->cache_ptr;
 
        if (!creq->last_req)
-               len = (len + creq->cache_ptr) & ~CESA_HASH_BLOCK_SIZE_MSK;
+               len &= ~CESA_HASH_BLOCK_SIZE_MSK;
 
        mv_cesa_req_dma_iter_init(&iter->base, len);
        mv_cesa_sg_dma_iter_init(&iter->src, req->src, DMA_TO_DEVICE);
@@ -179,7 +179,6 @@ static int mv_cesa_ahash_pad_len(struct mv_cesa_ahash_req *creq)
 
 static int mv_cesa_ahash_pad_req(struct mv_cesa_ahash_req *creq, u8 *buf)
 {
-       __be64 bits = cpu_to_be64(creq->len << 3);
        unsigned int index, padlen;
 
        buf[0] = 0x80;
@@ -187,7 +186,14 @@ static int mv_cesa_ahash_pad_req(struct mv_cesa_ahash_req *creq, u8 *buf)
        index = creq->len & CESA_HASH_BLOCK_SIZE_MSK;
        padlen = mv_cesa_ahash_pad_len(creq);
        memset(buf + 1, 0, padlen - 1);
-       memcpy(buf + padlen, &bits, sizeof(bits));
+
+       if (creq->algo_le) {
+               __le64 bits = cpu_to_le64(creq->len << 3);
+               memcpy(buf + padlen, &bits, sizeof(bits));
+       } else {
+               __be64 bits = cpu_to_be64(creq->len << 3);
+               memcpy(buf + padlen, &bits, sizeof(bits));
+       }
 
        return padlen + 8;
 }
@@ -203,8 +209,8 @@ static void mv_cesa_ahash_std_step(struct ahash_request *req)
        size_t  len;
 
        if (creq->cache_ptr)
-               memcpy(engine->sram + CESA_SA_DATA_SRAM_OFFSET, creq->cache,
-                      creq->cache_ptr);
+               memcpy_toio(engine->sram + CESA_SA_DATA_SRAM_OFFSET,
+                           creq->cache, creq->cache_ptr);
 
        len = min_t(size_t, req->nbytes + creq->cache_ptr - sreq->offset,
                    CESA_SA_SRAM_PAYLOAD_SIZE);
@@ -245,10 +251,10 @@ static void mv_cesa_ahash_std_step(struct ahash_request *req)
                        if (len + trailerlen > CESA_SA_SRAM_PAYLOAD_SIZE) {
                                len &= CESA_HASH_BLOCK_SIZE_MSK;
                                new_cache_ptr = 64 - trailerlen;
-                               memcpy(creq->cache,
-                                      engine->sram +
-                                      CESA_SA_DATA_SRAM_OFFSET + len,
-                                      new_cache_ptr);
+                               memcpy_fromio(creq->cache,
+                                             engine->sram +
+                                             CESA_SA_DATA_SRAM_OFFSET + len,
+                                             new_cache_ptr);
                        } else {
                                len += mv_cesa_ahash_pad_req(creq,
                                                engine->sram + len +
@@ -266,7 +272,7 @@ static void mv_cesa_ahash_std_step(struct ahash_request *req)
        mv_cesa_update_op_cfg(op, frag_mode, CESA_SA_DESC_CFG_FRAG_MSK);
 
        /* FIXME: only update enc_len field */
-       memcpy(engine->sram, op, sizeof(*op));
+       memcpy_toio(engine->sram, op, sizeof(*op));
 
        if (frag_mode == CESA_SA_DESC_CFG_FIRST_FRAG)
                mv_cesa_update_op_cfg(op, CESA_SA_DESC_CFG_MID_FRAG,
@@ -275,7 +281,7 @@ static void mv_cesa_ahash_std_step(struct ahash_request *req)
        creq->cache_ptr = new_cache_ptr;
 
        mv_cesa_set_int_mask(engine, CESA_SA_INT_ACCEL0_DONE);
-       writel(CESA_SA_CFG_PARA_DIS, engine->regs + CESA_SA_CFG);
+       writel_relaxed(CESA_SA_CFG_PARA_DIS, engine->regs + CESA_SA_CFG);
        writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
 }
 
@@ -306,7 +312,7 @@ static void mv_cesa_ahash_std_prepare(struct ahash_request *req)
 
        sreq->offset = 0;
        mv_cesa_adjust_op(engine, &creq->op_tmpl);
-       memcpy(engine->sram, &creq->op_tmpl, sizeof(creq->op_tmpl));
+       memcpy_toio(engine->sram, &creq->op_tmpl, sizeof(creq->op_tmpl));
 }
 
 static void mv_cesa_ahash_step(struct crypto_async_request *req)
@@ -338,7 +344,7 @@ static int mv_cesa_ahash_process(struct crypto_async_request *req, u32 status)
 
        digsize = crypto_ahash_digestsize(crypto_ahash_reqtfm(ahashreq));
        for (i = 0; i < digsize / 4; i++)
-               creq->state[i] = readl(engine->regs + CESA_IVDIG(i));
+               creq->state[i] = readl_relaxed(engine->regs + CESA_IVDIG(i));
 
        if (creq->cache_ptr)
                sg_pcopy_to_buffer(ahashreq->src, creq->src_nents,
@@ -347,18 +353,21 @@ static int mv_cesa_ahash_process(struct crypto_async_request *req, u32 status)
                                   ahashreq->nbytes - creq->cache_ptr);
 
        if (creq->last_req) {
-               for (i = 0; i < digsize / 4; i++) {
-                       /*
-                        * Hardware provides MD5 digest in a different
-                        * endianness than SHA-1 and SHA-256 ones.
-                        */
-                       if (digsize == MD5_DIGEST_SIZE)
-                               creq->state[i] = cpu_to_le32(creq->state[i]);
-                       else
-                               creq->state[i] = cpu_to_be32(creq->state[i]);
-               }
+               /*
+                * Hardware's MD5 digest is in little endian format, but
+                * SHA in big endian format
+                */
+               if (creq->algo_le) {
+                       __le32 *result = (void *)ahashreq->result;
+
+                       for (i = 0; i < digsize / 4; i++)
+                               result[i] = cpu_to_le32(creq->state[i]);
+               } else {
+                       __be32 *result = (void *)ahashreq->result;
 
-               memcpy(ahashreq->result, creq->state, digsize);
+                       for (i = 0; i < digsize / 4; i++)
+                               result[i] = cpu_to_be32(creq->state[i]);
+               }
        }
 
        return ret;
@@ -381,8 +390,7 @@ static void mv_cesa_ahash_prepare(struct crypto_async_request *req,
 
        digsize = crypto_ahash_digestsize(crypto_ahash_reqtfm(ahashreq));
        for (i = 0; i < digsize / 4; i++)
-               writel(creq->state[i],
-                      engine->regs + CESA_IVDIG(i));
+               writel_relaxed(creq->state[i], engine->regs + CESA_IVDIG(i));
 }
 
 static void mv_cesa_ahash_req_cleanup(struct crypto_async_request *req)
@@ -404,7 +412,7 @@ static const struct mv_cesa_req_ops mv_cesa_ahash_req_ops = {
 };
 
 static int mv_cesa_ahash_init(struct ahash_request *req,
-                             struct mv_cesa_op_ctx *tmpl)
+                             struct mv_cesa_op_ctx *tmpl, bool algo_le)
 {
        struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
 
@@ -418,6 +426,7 @@ static int mv_cesa_ahash_init(struct ahash_request *req,
        mv_cesa_set_mac_op_frag_len(tmpl, 0);
        creq->op_tmpl = *tmpl;
        creq->len = 0;
+       creq->algo_le = algo_le;
 
        return 0;
 }
@@ -462,145 +471,114 @@ static int mv_cesa_ahash_cache_req(struct ahash_request *req, bool *cached)
 }
 
 static struct mv_cesa_op_ctx *
-mv_cesa_ahash_dma_add_cache(struct mv_cesa_tdma_chain *chain,
-                           struct mv_cesa_ahash_dma_iter *dma_iter,
-                           struct mv_cesa_ahash_req *creq,
-                           gfp_t flags)
+mv_cesa_dma_add_frag(struct mv_cesa_tdma_chain *chain,
+                    struct mv_cesa_op_ctx *tmpl, unsigned int frag_len,
+                    gfp_t flags)
 {
-       struct mv_cesa_ahash_dma_req *ahashdreq = &creq->req.dma;
-       struct mv_cesa_op_ctx *op = NULL;
+       struct mv_cesa_op_ctx *op;
        int ret;
 
-       if (!creq->cache_ptr)
-               return NULL;
+       op = mv_cesa_dma_add_op(chain, tmpl, false, flags);
+       if (IS_ERR(op))
+               return op;
 
-       ret = mv_cesa_dma_add_data_transfer(chain,
-                                           CESA_SA_DATA_SRAM_OFFSET,
-                                           ahashdreq->cache_dma,
-                                           creq->cache_ptr,
-                                           CESA_TDMA_DST_IN_SRAM,
-                                           flags);
+       /* Set the operation block fragment length. */
+       mv_cesa_set_mac_op_frag_len(op, frag_len);
+
+       /* Append dummy desc to launch operation */
+       ret = mv_cesa_dma_add_dummy_launch(chain, flags);
        if (ret)
                return ERR_PTR(ret);
 
-       if (!dma_iter->base.op_len) {
-               op = mv_cesa_dma_add_op(chain, &creq->op_tmpl, false, flags);
-               if (IS_ERR(op))
-                       return op;
-
-               mv_cesa_set_mac_op_frag_len(op, creq->cache_ptr);
-
-               /* Add dummy desc to launch crypto operation */
-               ret = mv_cesa_dma_add_dummy_launch(chain, flags);
-               if (ret)
-                       return ERR_PTR(ret);
-       }
+       if (mv_cesa_mac_op_is_first_frag(tmpl))
+               mv_cesa_update_op_cfg(tmpl,
+                                     CESA_SA_DESC_CFG_MID_FRAG,
+                                     CESA_SA_DESC_CFG_FRAG_MSK);
 
        return op;
 }
 
-static struct mv_cesa_op_ctx *
-mv_cesa_ahash_dma_add_data(struct mv_cesa_tdma_chain *chain,
-                          struct mv_cesa_ahash_dma_iter *dma_iter,
-                          struct mv_cesa_ahash_req *creq,
-                          gfp_t flags)
+static int
+mv_cesa_ahash_dma_add_cache(struct mv_cesa_tdma_chain *chain,
+                           struct mv_cesa_ahash_dma_iter *dma_iter,
+                           struct mv_cesa_ahash_req *creq,
+                           gfp_t flags)
 {
-       struct mv_cesa_op_ctx *op;
-       int ret;
-
-       op = mv_cesa_dma_add_op(chain, &creq->op_tmpl, false, flags);
-       if (IS_ERR(op))
-               return op;
-
-       mv_cesa_set_mac_op_frag_len(op, dma_iter->base.op_len);
-
-       if ((mv_cesa_get_op_cfg(&creq->op_tmpl) & CESA_SA_DESC_CFG_FRAG_MSK) ==
-           CESA_SA_DESC_CFG_FIRST_FRAG)
-               mv_cesa_update_op_cfg(&creq->op_tmpl,
-                                     CESA_SA_DESC_CFG_MID_FRAG,
-                                     CESA_SA_DESC_CFG_FRAG_MSK);
-
-       /* Add input transfers */
-       ret = mv_cesa_dma_add_op_transfers(chain, &dma_iter->base,
-                                          &dma_iter->src, flags);
-       if (ret)
-               return ERR_PTR(ret);
+       struct mv_cesa_ahash_dma_req *ahashdreq = &creq->req.dma;
 
-       /* Add dummy desc to launch crypto operation */
-       ret = mv_cesa_dma_add_dummy_launch(chain, flags);
-       if (ret)
-               return ERR_PTR(ret);
+       if (!creq->cache_ptr)
+               return 0;
 
-       return op;
+       return mv_cesa_dma_add_data_transfer(chain,
+                                            CESA_SA_DATA_SRAM_OFFSET,
+                                            ahashdreq->cache_dma,
+                                            creq->cache_ptr,
+                                            CESA_TDMA_DST_IN_SRAM,
+                                            flags);
 }
 
 static struct mv_cesa_op_ctx *
 mv_cesa_ahash_dma_last_req(struct mv_cesa_tdma_chain *chain,
                           struct mv_cesa_ahash_dma_iter *dma_iter,
                           struct mv_cesa_ahash_req *creq,
-                          struct mv_cesa_op_ctx *op,
-                          gfp_t flags)
+                          unsigned int frag_len, gfp_t flags)
 {
        struct mv_cesa_ahash_dma_req *ahashdreq = &creq->req.dma;
        unsigned int len, trailerlen, padoff = 0;
+       struct mv_cesa_op_ctx *op;
        int ret;
 
-       if (!creq->last_req)
-               return op;
-
-       if (op && creq->len <= CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX) {
-               u32 frag = CESA_SA_DESC_CFG_NOT_FRAG;
-
-               if ((mv_cesa_get_op_cfg(op) & CESA_SA_DESC_CFG_FRAG_MSK) !=
-                   CESA_SA_DESC_CFG_FIRST_FRAG)
-                       frag = CESA_SA_DESC_CFG_LAST_FRAG;
+       /*
+        * If the transfer is smaller than our maximum length, and we have
+        * some data outstanding, we can ask the engine to finish the hash.
+        */
+       if (creq->len <= CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX && frag_len) {
+               op = mv_cesa_dma_add_frag(chain, &creq->op_tmpl, frag_len,
+                                         flags);
+               if (IS_ERR(op))
+                       return op;
 
-               mv_cesa_update_op_cfg(op, frag, CESA_SA_DESC_CFG_FRAG_MSK);
+               mv_cesa_set_mac_op_total_len(op, creq->len);
+               mv_cesa_update_op_cfg(op, mv_cesa_mac_op_is_first_frag(op) ?
+                                               CESA_SA_DESC_CFG_NOT_FRAG :
+                                               CESA_SA_DESC_CFG_LAST_FRAG,
+                                     CESA_SA_DESC_CFG_FRAG_MSK);
 
                return op;
        }
 
+       /*
+        * The request is longer than the engine can handle, or we have
+        * no data outstanding. Manually generate the padding, adding it
+        * as a "mid" fragment.
+        */
        ret = mv_cesa_ahash_dma_alloc_padding(ahashdreq, flags);
        if (ret)
                return ERR_PTR(ret);
 
        trailerlen = mv_cesa_ahash_pad_req(creq, ahashdreq->padding);
 
-       if (op) {
-               len = min(CESA_SA_SRAM_PAYLOAD_SIZE - dma_iter->base.op_len,
-                         trailerlen);
-               if (len) {
-                       ret = mv_cesa_dma_add_data_transfer(chain,
+       len = min(CESA_SA_SRAM_PAYLOAD_SIZE - frag_len, trailerlen);
+       if (len) {
+               ret = mv_cesa_dma_add_data_transfer(chain,
                                                CESA_SA_DATA_SRAM_OFFSET +
-                                               dma_iter->base.op_len,
+                                               frag_len,
                                                ahashdreq->padding_dma,
                                                len, CESA_TDMA_DST_IN_SRAM,
                                                flags);
-                       if (ret)
-                               return ERR_PTR(ret);
-
-                       mv_cesa_update_op_cfg(op, CESA_SA_DESC_CFG_MID_FRAG,
-                                             CESA_SA_DESC_CFG_FRAG_MSK);
-                       mv_cesa_set_mac_op_frag_len(op,
-                                       dma_iter->base.op_len + len);
-                       padoff += len;
-               }
-       }
-
-       if (padoff >= trailerlen)
-               return op;
+               if (ret)
+                       return ERR_PTR(ret);
 
-       if ((mv_cesa_get_op_cfg(&creq->op_tmpl) & CESA_SA_DESC_CFG_FRAG_MSK) !=
-           CESA_SA_DESC_CFG_FIRST_FRAG)
-               mv_cesa_update_op_cfg(&creq->op_tmpl,
-                                     CESA_SA_DESC_CFG_MID_FRAG,
-                                     CESA_SA_DESC_CFG_FRAG_MSK);
+               op = mv_cesa_dma_add_frag(chain, &creq->op_tmpl, frag_len + len,
+                                         flags);
+               if (IS_ERR(op))
+                       return op;
 
-       op = mv_cesa_dma_add_op(chain, &creq->op_tmpl, false, flags);
-       if (IS_ERR(op))
-               return op;
+               if (len == trailerlen)
+                       return op;
 
-       mv_cesa_set_mac_op_frag_len(op, trailerlen - padoff);
+               padoff += len;
+       }
 
        ret = mv_cesa_dma_add_data_transfer(chain,
                                            CESA_SA_DATA_SRAM_OFFSET,
@@ -612,12 +590,8 @@ mv_cesa_ahash_dma_last_req(struct mv_cesa_tdma_chain *chain,
        if (ret)
                return ERR_PTR(ret);
 
-       /* Add dummy desc to launch crypto operation */
-       ret = mv_cesa_dma_add_dummy_launch(chain, flags);
-       if (ret)
-               return ERR_PTR(ret);
-
-       return op;
+       return mv_cesa_dma_add_frag(chain, &creq->op_tmpl, trailerlen - padoff,
+                                   flags);
 }
 
 static int mv_cesa_ahash_dma_req_init(struct ahash_request *req)
@@ -627,9 +601,9 @@ static int mv_cesa_ahash_dma_req_init(struct ahash_request *req)
                      GFP_KERNEL : GFP_ATOMIC;
        struct mv_cesa_ahash_dma_req *ahashdreq = &creq->req.dma;
        struct mv_cesa_tdma_req *dreq = &ahashdreq->base;
-       struct mv_cesa_tdma_chain chain;
        struct mv_cesa_ahash_dma_iter iter;
        struct mv_cesa_op_ctx *op = NULL;
+       unsigned int frag_len;
        int ret;
 
        dreq->chain.first = NULL;
@@ -644,29 +618,59 @@ static int mv_cesa_ahash_dma_req_init(struct ahash_request *req)
                }
        }
 
-       mv_cesa_tdma_desc_iter_init(&chain);
+       mv_cesa_tdma_desc_iter_init(&dreq->chain);
        mv_cesa_ahash_req_iter_init(&iter, req);
 
-       op = mv_cesa_ahash_dma_add_cache(&chain, &iter,
-                                        creq, flags);
-       if (IS_ERR(op)) {
-               ret = PTR_ERR(op);
+       /*
+        * Add the cache (left-over data from a previous block) first.
+        * This will never overflow the SRAM size.
+        */
+       ret = mv_cesa_ahash_dma_add_cache(&dreq->chain, &iter, creq, flags);
+       if (ret)
                goto err_free_tdma;
-       }
 
-       do {
-               if (!iter.base.op_len)
-                       break;
+       if (iter.src.sg) {
+               /*
+                * Add all the new data, inserting an operation block and
+                * launch command between each full SRAM block-worth of
+                * data. We intentionally do not add the final op block.
+                */
+               while (true) {
+                       ret = mv_cesa_dma_add_op_transfers(&dreq->chain,
+                                                          &iter.base,
+                                                          &iter.src, flags);
+                       if (ret)
+                               goto err_free_tdma;
+
+                       frag_len = iter.base.op_len;
 
-               op = mv_cesa_ahash_dma_add_data(&chain, &iter,
-                                               creq, flags);
-               if (IS_ERR(op)) {
-                       ret = PTR_ERR(op);
-                       goto err_free_tdma;
+                       if (!mv_cesa_ahash_req_iter_next_op(&iter))
+                               break;
+
+                       op = mv_cesa_dma_add_frag(&dreq->chain, &creq->op_tmpl,
+                                                 frag_len, flags);
+                       if (IS_ERR(op)) {
+                               ret = PTR_ERR(op);
+                               goto err_free_tdma;
+                       }
                }
-       } while (mv_cesa_ahash_req_iter_next_op(&iter));
+       } else {
+               /* Account for the data that was in the cache. */
+               frag_len = iter.base.op_len;
+       }
+
+       /*
+        * At this point, frag_len indicates whether we have any data
+        * outstanding which needs an operation.  Queue up the final
+        * operation, which depends whether this is the final request.
+        */
+       if (creq->last_req)
+               op = mv_cesa_ahash_dma_last_req(&dreq->chain, &iter, creq,
+                                               frag_len, flags);
+       else if (frag_len)
+               op = mv_cesa_dma_add_frag(&dreq->chain, &creq->op_tmpl,
+                                         frag_len, flags);
 
-       op = mv_cesa_ahash_dma_last_req(&chain, &iter, creq, op, flags);
        if (IS_ERR(op)) {
                ret = PTR_ERR(op);
                goto err_free_tdma;
@@ -674,7 +678,7 @@ static int mv_cesa_ahash_dma_req_init(struct ahash_request *req)
 
        if (op) {
                /* Add dummy desc to wait for crypto operation end */
-               ret = mv_cesa_dma_add_dummy_end(&chain, flags);
+               ret = mv_cesa_dma_add_dummy_end(&dreq->chain, flags);
                if (ret)
                        goto err_free_tdma;
        }
@@ -685,8 +689,6 @@ static int mv_cesa_ahash_dma_req_init(struct ahash_request *req)
        else
                creq->cache_ptr = 0;
 
-       dreq->chain = chain;
-
        return 0;
 
 err_free_tdma:
@@ -795,47 +797,50 @@ static int mv_cesa_ahash_finup(struct ahash_request *req)
        return ret;
 }
 
-static int mv_cesa_md5_init(struct ahash_request *req)
+static int mv_cesa_ahash_export(struct ahash_request *req, void *hash,
+                               u64 *len, void *cache)
 {
-       struct mv_cesa_op_ctx tmpl;
-
-       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_MD5);
-
-       mv_cesa_ahash_init(req, &tmpl);
-
-       return 0;
-}
-
-static int mv_cesa_md5_export(struct ahash_request *req, void *out)
-{
-       struct md5_state *out_state = out;
        struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
        struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
        unsigned int digsize = crypto_ahash_digestsize(ahash);
+       unsigned int blocksize;
+
+       blocksize = crypto_ahash_blocksize(ahash);
 
-       out_state->byte_count = creq->len;
-       memcpy(out_state->hash, creq->state, digsize);
-       memset(out_state->block, 0, sizeof(out_state->block));
+       *len = creq->len;
+       memcpy(hash, creq->state, digsize);
+       memset(cache, 0, blocksize);
        if (creq->cache)
-               memcpy(out_state->block, creq->cache, creq->cache_ptr);
+               memcpy(cache, creq->cache, creq->cache_ptr);
 
        return 0;
 }
 
-static int mv_cesa_md5_import(struct ahash_request *req, const void *in)
+static int mv_cesa_ahash_import(struct ahash_request *req, const void *hash,
+                               u64 len, const void *cache)
 {
-       const struct md5_state *in_state = in;
        struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
        struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
        unsigned int digsize = crypto_ahash_digestsize(ahash);
+       unsigned int blocksize;
        unsigned int cache_ptr;
        int ret;
 
-       creq->len = in_state->byte_count;
-       memcpy(creq->state, in_state->hash, digsize);
+       ret = crypto_ahash_init(req);
+       if (ret)
+               return ret;
+
+       blocksize = crypto_ahash_blocksize(ahash);
+       if (len >= blocksize)
+               mv_cesa_update_op_cfg(&creq->op_tmpl,
+                                     CESA_SA_DESC_CFG_MID_FRAG,
+                                     CESA_SA_DESC_CFG_FRAG_MSK);
+
+       creq->len = len;
+       memcpy(creq->state, hash, digsize);
        creq->cache_ptr = 0;
 
-       cache_ptr = creq->len % sizeof(in_state->block);
+       cache_ptr = do_div(len, blocksize);
        if (!cache_ptr)
                return 0;
 
@@ -843,12 +848,39 @@ static int mv_cesa_md5_import(struct ahash_request *req, const void *in)
        if (ret)
                return ret;
 
-       memcpy(creq->cache, in_state->block, cache_ptr);
+       memcpy(creq->cache, cache, cache_ptr);
        creq->cache_ptr = cache_ptr;
 
        return 0;
 }
 
+static int mv_cesa_md5_init(struct ahash_request *req)
+{
+       struct mv_cesa_op_ctx tmpl = { };
+
+       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_MD5);
+
+       mv_cesa_ahash_init(req, &tmpl, true);
+
+       return 0;
+}
+
+static int mv_cesa_md5_export(struct ahash_request *req, void *out)
+{
+       struct md5_state *out_state = out;
+
+       return mv_cesa_ahash_export(req, out_state->hash,
+                                   &out_state->byte_count, out_state->block);
+}
+
+static int mv_cesa_md5_import(struct ahash_request *req, const void *in)
+{
+       const struct md5_state *in_state = in;
+
+       return mv_cesa_ahash_import(req, in_state->hash, in_state->byte_count,
+                                   in_state->block);
+}
+
 static int mv_cesa_md5_digest(struct ahash_request *req)
 {
        int ret;
@@ -870,6 +902,7 @@ struct ahash_alg mv_md5_alg = {
        .import = mv_cesa_md5_import,
        .halg = {
                .digestsize = MD5_DIGEST_SIZE,
+               .statesize = sizeof(struct md5_state),
                .base = {
                        .cra_name = "md5",
                        .cra_driver_name = "mv-md5",
@@ -886,11 +919,11 @@ struct ahash_alg mv_md5_alg = {
 
 static int mv_cesa_sha1_init(struct ahash_request *req)
 {
-       struct mv_cesa_op_ctx tmpl;
+       struct mv_cesa_op_ctx tmpl = { };
 
        mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_SHA1);
 
-       mv_cesa_ahash_init(req, &tmpl);
+       mv_cesa_ahash_init(req, &tmpl, false);
 
        return 0;
 }
@@ -898,44 +931,17 @@ static int mv_cesa_sha1_init(struct ahash_request *req)
 static int mv_cesa_sha1_export(struct ahash_request *req, void *out)
 {
        struct sha1_state *out_state = out;
-       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
-       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
-       unsigned int digsize = crypto_ahash_digestsize(ahash);
-
-       out_state->count = creq->len;
-       memcpy(out_state->state, creq->state, digsize);
-       memset(out_state->buffer, 0, sizeof(out_state->buffer));
-       if (creq->cache)
-               memcpy(out_state->buffer, creq->cache, creq->cache_ptr);
 
-       return 0;
+       return mv_cesa_ahash_export(req, out_state->state, &out_state->count,
+                                   out_state->buffer);
 }
 
 static int mv_cesa_sha1_import(struct ahash_request *req, const void *in)
 {
        const struct sha1_state *in_state = in;
-       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
-       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
-       unsigned int digsize = crypto_ahash_digestsize(ahash);
-       unsigned int cache_ptr;
-       int ret;
 
-       creq->len = in_state->count;
-       memcpy(creq->state, in_state->state, digsize);
-       creq->cache_ptr = 0;
-
-       cache_ptr = creq->len % SHA1_BLOCK_SIZE;
-       if (!cache_ptr)
-               return 0;
-
-       ret = mv_cesa_ahash_alloc_cache(req);
-       if (ret)
-               return ret;
-
-       memcpy(creq->cache, in_state->buffer, cache_ptr);
-       creq->cache_ptr = cache_ptr;
-
-       return 0;
+       return mv_cesa_ahash_import(req, in_state->state, in_state->count,
+                                   in_state->buffer);
 }
 
 static int mv_cesa_sha1_digest(struct ahash_request *req)
@@ -959,6 +965,7 @@ struct ahash_alg mv_sha1_alg = {
        .import = mv_cesa_sha1_import,
        .halg = {
                .digestsize = SHA1_DIGEST_SIZE,
+               .statesize = sizeof(struct sha1_state),
                .base = {
                        .cra_name = "sha1",
                        .cra_driver_name = "mv-sha1",
@@ -975,11 +982,11 @@ struct ahash_alg mv_sha1_alg = {
 
 static int mv_cesa_sha256_init(struct ahash_request *req)
 {
-       struct mv_cesa_op_ctx tmpl;
+       struct mv_cesa_op_ctx tmpl = { };
 
        mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_SHA256);
 
-       mv_cesa_ahash_init(req, &tmpl);
+       mv_cesa_ahash_init(req, &tmpl, false);
 
        return 0;
 }
@@ -998,44 +1005,17 @@ static int mv_cesa_sha256_digest(struct ahash_request *req)
 static int mv_cesa_sha256_export(struct ahash_request *req, void *out)
 {
        struct sha256_state *out_state = out;
-       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
-       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
-       unsigned int ds = crypto_ahash_digestsize(ahash);
 
-       out_state->count = creq->len;
-       memcpy(out_state->state, creq->state, ds);
-       memset(out_state->buf, 0, sizeof(out_state->buf));
-       if (creq->cache)
-               memcpy(out_state->buf, creq->cache, creq->cache_ptr);
-
-       return 0;
+       return mv_cesa_ahash_export(req, out_state->state, &out_state->count,
+                                   out_state->buf);
 }
 
 static int mv_cesa_sha256_import(struct ahash_request *req, const void *in)
 {
        const struct sha256_state *in_state = in;
-       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
-       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
-       unsigned int digsize = crypto_ahash_digestsize(ahash);
-       unsigned int cache_ptr;
-       int ret;
-
-       creq->len = in_state->count;
-       memcpy(creq->state, in_state->state, digsize);
-       creq->cache_ptr = 0;
-
-       cache_ptr = creq->len % SHA256_BLOCK_SIZE;
-       if (!cache_ptr)
-               return 0;
-
-       ret = mv_cesa_ahash_alloc_cache(req);
-       if (ret)
-               return ret;
-
-       memcpy(creq->cache, in_state->buf, cache_ptr);
-       creq->cache_ptr = cache_ptr;
 
-       return 0;
+       return mv_cesa_ahash_import(req, in_state->state, in_state->count,
+                                   in_state->buf);
 }
 
 struct ahash_alg mv_sha256_alg = {
@@ -1048,6 +1028,7 @@ struct ahash_alg mv_sha256_alg = {
        .import = mv_cesa_sha256_import,
        .halg = {
                .digestsize = SHA256_DIGEST_SIZE,
+               .statesize = sizeof(struct sha256_state),
                .base = {
                        .cra_name = "sha256",
                        .cra_driver_name = "mv-sha256",
@@ -1231,12 +1212,12 @@ static int mv_cesa_ahmac_cra_init(struct crypto_tfm *tfm)
 static int mv_cesa_ahmac_md5_init(struct ahash_request *req)
 {
        struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
-       struct mv_cesa_op_ctx tmpl;
+       struct mv_cesa_op_ctx tmpl = { };
 
        mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_HMAC_MD5);
        memcpy(tmpl.ctx.hash.iv, ctx->iv, sizeof(ctx->iv));
 
-       mv_cesa_ahash_init(req, &tmpl);
+       mv_cesa_ahash_init(req, &tmpl, true);
 
        return 0;
 }
@@ -1301,12 +1282,12 @@ struct ahash_alg mv_ahmac_md5_alg = {
 static int mv_cesa_ahmac_sha1_init(struct ahash_request *req)
 {
        struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
-       struct mv_cesa_op_ctx tmpl;
+       struct mv_cesa_op_ctx tmpl = { };
 
        mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_HMAC_SHA1);
        memcpy(tmpl.ctx.hash.iv, ctx->iv, sizeof(ctx->iv));
 
-       mv_cesa_ahash_init(req, &tmpl);
+       mv_cesa_ahash_init(req, &tmpl, false);
 
        return 0;
 }
@@ -1391,12 +1372,12 @@ static int mv_cesa_ahmac_sha256_setkey(struct crypto_ahash *tfm, const u8 *key,
 static int mv_cesa_ahmac_sha256_init(struct ahash_request *req)
 {
        struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
-       struct mv_cesa_op_ctx tmpl;
+       struct mv_cesa_op_ctx tmpl = { };
 
        mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_HMAC_SHA256);
        memcpy(tmpl.ctx.hash.iv, ctx->iv, sizeof(ctx->iv));
 
-       mv_cesa_ahash_init(req, &tmpl);
+       mv_cesa_ahash_init(req, &tmpl, false);
 
        return 0;
 }
index 64a366c501744303984c3bfd5e36a1bf6bbb55c6..76427981275b025dfee52a87e4850981ac159f0f 100644 (file)
@@ -41,18 +41,18 @@ void mv_cesa_dma_step(struct mv_cesa_tdma_req *dreq)
 {
        struct mv_cesa_engine *engine = dreq->base.engine;
 
-       writel(0, engine->regs + CESA_SA_CFG);
+       writel_relaxed(0, engine->regs + CESA_SA_CFG);
 
        mv_cesa_set_int_mask(engine, CESA_SA_INT_ACC0_IDMA_DONE);
-       writel(CESA_TDMA_DST_BURST_128B | CESA_TDMA_SRC_BURST_128B |
-              CESA_TDMA_NO_BYTE_SWAP | CESA_TDMA_EN,
-              engine->regs + CESA_TDMA_CONTROL);
-
-       writel(CESA_SA_CFG_ACT_CH0_IDMA | CESA_SA_CFG_MULTI_PKT |
-              CESA_SA_CFG_CH0_W_IDMA | CESA_SA_CFG_PARA_DIS,
-              engine->regs + CESA_SA_CFG);
-       writel(dreq->chain.first->cur_dma,
-              engine->regs + CESA_TDMA_NEXT_ADDR);
+       writel_relaxed(CESA_TDMA_DST_BURST_128B | CESA_TDMA_SRC_BURST_128B |
+                      CESA_TDMA_NO_BYTE_SWAP | CESA_TDMA_EN,
+                      engine->regs + CESA_TDMA_CONTROL);
+
+       writel_relaxed(CESA_SA_CFG_ACT_CH0_IDMA | CESA_SA_CFG_MULTI_PKT |
+                      CESA_SA_CFG_CH0_W_IDMA | CESA_SA_CFG_PARA_DIS,
+                      engine->regs + CESA_SA_CFG);
+       writel_relaxed(dreq->chain.first->cur_dma,
+                      engine->regs + CESA_TDMA_NEXT_ADDR);
        writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
 }
 
@@ -69,7 +69,7 @@ void mv_cesa_dma_cleanup(struct mv_cesa_tdma_req *dreq)
 
                tdma = tdma->next;
                dma_pool_free(cesa_dev->dma->tdma_desc_pool, old_tdma,
-                             le32_to_cpu(old_tdma->cur_dma));
+                             old_tdma->cur_dma);
        }
 
        dreq->chain.first = NULL;
@@ -105,9 +105,9 @@ mv_cesa_dma_add_desc(struct mv_cesa_tdma_chain *chain, gfp_t flags)
                return ERR_PTR(-ENOMEM);
 
        memset(new_tdma, 0, sizeof(*new_tdma));
-       new_tdma->cur_dma = cpu_to_le32(dma_handle);
+       new_tdma->cur_dma = dma_handle;
        if (chain->last) {
-               chain->last->next_dma = new_tdma->cur_dma;
+               chain->last->next_dma = cpu_to_le32(dma_handle);
                chain->last->next = new_tdma;
        } else {
                chain->first = new_tdma;
@@ -126,6 +126,7 @@ struct mv_cesa_op_ctx *mv_cesa_dma_add_op(struct mv_cesa_tdma_chain *chain,
        struct mv_cesa_tdma_desc *tdma;
        struct mv_cesa_op_ctx *op;
        dma_addr_t dma_handle;
+       unsigned int size;
 
        tdma = mv_cesa_dma_add_desc(chain, flags);
        if (IS_ERR(tdma))
@@ -137,10 +138,12 @@ struct mv_cesa_op_ctx *mv_cesa_dma_add_op(struct mv_cesa_tdma_chain *chain,
 
        *op = *op_templ;
 
+       size = skip_ctx ? sizeof(op->desc) : sizeof(*op);
+
        tdma = chain->last;
        tdma->op = op;
-       tdma->byte_cnt = (skip_ctx ? sizeof(op->desc) : sizeof(*op)) | BIT(31);
-       tdma->src = dma_handle;
+       tdma->byte_cnt = cpu_to_le32(size | BIT(31));
+       tdma->src = cpu_to_le32(dma_handle);
        tdma->flags = CESA_TDMA_DST_IN_SRAM | CESA_TDMA_OP;
 
        return op;
@@ -156,7 +159,7 @@ int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
        if (IS_ERR(tdma))
                return PTR_ERR(tdma);
 
-       tdma->byte_cnt = size | BIT(31);
+       tdma->byte_cnt = cpu_to_le32(size | BIT(31));
        tdma->src = src;
        tdma->dst = dst;
 
@@ -166,8 +169,7 @@ int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
        return 0;
 }
 
-int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain,
-                                u32 flags)
+int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain, gfp_t flags)
 {
        struct mv_cesa_tdma_desc *tdma;
 
@@ -178,7 +180,7 @@ int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain,
        return 0;
 }
 
-int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, u32 flags)
+int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, gfp_t flags)
 {
        struct mv_cesa_tdma_desc *tdma;
 
@@ -186,7 +188,7 @@ int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, u32 flags)
        if (IS_ERR(tdma))
                return PTR_ERR(tdma);
 
-       tdma->byte_cnt = BIT(31);
+       tdma->byte_cnt = cpu_to_le32(BIT(31));
 
        return 0;
 }
index 2e8dab9d4263b000c51b8c8e584cdeb636fe1e14..5450880abb7b97a4f3ba29fb36b384b91e639965 100644 (file)
@@ -34,7 +34,7 @@
 #define DRV_MODULE_VERSION     "0.2"
 #define DRV_MODULE_RELDATE     "July 28, 2011"
 
-static char version[] =
+static const char version[] =
        DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
 
 MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
index 3750e13d872181c425feefafbc259c03024b75e0..9ef51fafdbffaef11b800619818e10ae5f52cc17 100644 (file)
@@ -491,7 +491,7 @@ static int nx842_powernv_compress(const unsigned char *in, unsigned int inlen,
                                  void *wmem)
 {
        return nx842_powernv_function(in, inlen, out, outlenp,
-                                     wmem, CCW_FC_842_COMP_NOCRC);
+                                     wmem, CCW_FC_842_COMP_CRC);
 }
 
 /**
@@ -519,7 +519,7 @@ static int nx842_powernv_decompress(const unsigned char *in, unsigned int inlen,
                                    void *wmem)
 {
        return nx842_powernv_function(in, inlen, out, outlenp,
-                                     wmem, CCW_FC_842_DECOMP_NOCRC);
+                                     wmem, CCW_FC_842_DECOMP_CRC);
 }
 
 static int __init nx842_powernv_probe(struct device_node *dn)
index f4cbde03c6adda03c497f0ce08a358081f78d6d6..cddc6d8b55d9c63d974c0fdc8341518f70afb527 100644 (file)
@@ -234,6 +234,10 @@ static int nx842_validate_result(struct device *dev,
                dev_dbg(dev, "%s: Out of space in output buffer\n",
                                        __func__);
                return -ENOSPC;
+       case 65: /* Calculated CRC doesn't match the passed value */
+               dev_dbg(dev, "%s: CRC mismatch for decompression\n",
+                                       __func__);
+               return -EINVAL;
        case 66: /* Input data contains an illegal template field */
        case 67: /* Template indicates data past the end of the input stream */
                dev_dbg(dev, "%s: Bad data for decompression (code:%d)\n",
@@ -324,7 +328,7 @@ static int nx842_pseries_compress(const unsigned char *in, unsigned int inlen,
        slout.entries = (struct nx842_slentry *)workmem->slout;
 
        /* Init operation */
-       op.flags = NX842_OP_COMPRESS;
+       op.flags = NX842_OP_COMPRESS_CRC;
        csbcpb = &workmem->csbcpb;
        memset(csbcpb, 0, sizeof(*csbcpb));
        op.csbcpb = nx842_get_pa(csbcpb);
@@ -457,7 +461,7 @@ static int nx842_pseries_decompress(const unsigned char *in, unsigned int inlen,
        slout.entries = (struct nx842_slentry *)workmem->slout;
 
        /* Init operation */
-       op.flags = NX842_OP_DECOMPRESS;
+       op.flags = NX842_OP_DECOMPRESS_CRC;
        csbcpb = &workmem->csbcpb;
        memset(csbcpb, 0, sizeof(*csbcpb));
        op.csbcpb = nx842_get_pa(csbcpb);
index da36de26a4dc1074859fc5e5082630adc57897f3..615da961c4d8beac11a9304959e00ce3c49a918c 100644 (file)
@@ -1591,6 +1591,7 @@ static const struct of_device_id spacc_of_id_table[] = {
        { .compatible = "picochip,spacc-l2" },
        {}
 };
+MODULE_DEVICE_TABLE(of, spacc_of_id_table);
 #endif /* CONFIG_OF */
 
 static bool spacc_is_compatible(struct platform_device *pdev,
index df20a9de1c586ef26bb3c5ba513ed052c35d573c..9e9e196c6d51946dead531a2c1d5107304781166 100644 (file)
@@ -1,5 +1,10 @@
-$(obj)/qat_rsakey-asn1.o: $(obj)/qat_rsakey-asn1.c $(obj)/qat_rsakey-asn1.h
-clean-files += qat_rsakey-asn1.c qat_rsakey-asn1.h
+$(obj)/qat_rsapubkey-asn1.o: $(obj)/qat_rsapubkey-asn1.c \
+                            $(obj)/qat_rsapubkey-asn1.h
+$(obj)/qat_rsaprivkey-asn1.o: $(obj)/qat_rsaprivkey-asn1.c \
+                             $(obj)/qat_rsaprivkey-asn1.h
+
+clean-files += qat_rsapubkey-asn1.c qat_rsapubkey-asn1.h
+clean-files += qat_rsaprivkey-asn1.c qat_rsapvivkey-asn1.h
 
 obj-$(CONFIG_CRYPTO_DEV_QAT) += intel_qat.o
 intel_qat-objs := adf_cfg.o \
@@ -13,7 +18,8 @@ intel_qat-objs := adf_cfg.o \
        adf_hw_arbiter.o \
        qat_crypto.o \
        qat_algs.o \
-       qat_rsakey-asn1.o \
+       qat_rsapubkey-asn1.o \
+       qat_rsaprivkey-asn1.o \
        qat_asym_algs.o \
        qat_uclo.o \
        qat_hal.o
index 7836dffc3d4728abbe54dc0fc15af920cff9f84f..3f76bd495bcb5bca0a09ea4f80cf0b05edc10f82 100644 (file)
@@ -163,10 +163,8 @@ struct qat_crypto_instance *qat_crypto_get_instance_node(int node);
 void qat_crypto_put_instance(struct qat_crypto_instance *inst);
 void qat_alg_callback(void *resp);
 void qat_alg_asym_callback(void *resp);
-int qat_algs_init(void);
-void qat_algs_exit(void);
 int qat_algs_register(void);
-int qat_algs_unregister(void);
+void qat_algs_unregister(void);
 int qat_asym_algs_register(void);
 void qat_asym_algs_unregister(void);
 
index cd8a12af8ec53467019683d5ce962947eaa72f57..03856ad280b9349d650a0d06723a9f9da31e7c75 100644 (file)
@@ -463,9 +463,6 @@ static int __init adf_register_ctl_device_driver(void)
 {
        mutex_init(&adf_ctl_lock);
 
-       if (qat_algs_init())
-               goto err_algs_init;
-
        if (adf_chr_drv_create())
                goto err_chr_dev;
 
@@ -482,8 +479,6 @@ err_crypto_register:
 err_aer:
        adf_chr_drv_destroy();
 err_chr_dev:
-       qat_algs_exit();
-err_algs_init:
        mutex_destroy(&adf_ctl_lock);
        return -EFAULT;
 }
@@ -493,7 +488,6 @@ static void __exit adf_unregister_ctl_device_driver(void)
        adf_chr_drv_destroy();
        adf_exit_aer();
        qat_crypto_unregister();
-       qat_algs_exit();
        adf_clean_vf_map(false);
        mutex_destroy(&adf_ctl_lock);
 }
index ac37a89965acb6afa936c4949eb84b81bdca9d74..d873eeecc363c719c6d9eedc223187975be210a7 100644 (file)
@@ -272,12 +272,10 @@ int adf_dev_stop(struct adf_accel_dev *accel_dev)
        clear_bit(ADF_STATUS_STARTING, &accel_dev->status);
        clear_bit(ADF_STATUS_STARTED, &accel_dev->status);
 
-       if (!list_empty(&accel_dev->crypto_list) && qat_algs_unregister())
-               dev_err(&GET_DEV(accel_dev),
-                       "Failed to unregister crypto algs\n");
-
-       if (!list_empty(&accel_dev->crypto_list))
+       if (!list_empty(&accel_dev->crypto_list)) {
+               qat_algs_unregister();
                qat_asym_algs_unregister();
+       }
 
        list_for_each(list_itr, &service_table) {
                service = list_entry(list_itr, struct service_hndl, list);
index 2f77a4a8cecb1b4488947a7591e4ca49bd60d036..1117a8b58280a084396f696e77f14b9899386981 100644 (file)
@@ -244,11 +244,8 @@ int adf_sriov_configure(struct pci_dev *pdev, int numvfs)
                return -EFAULT;
        }
 
-       if (!iommu_present(&pci_bus_type)) {
-               dev_err(&pdev->dev,
-                       "IOMMU must be enabled for SR-IOV to work\n");
-               return -EINVAL;
-       }
+       if (!iommu_present(&pci_bus_type))
+               dev_warn(&pdev->dev, "IOMMU should be enabled for SR-IOV to work correctly\n");
 
        if (accel_dev->pf.vf_info) {
                dev_info(&pdev->dev, "Already enabled for this device\n");
index 2bd913aceaeb5c0370bc428d6c52278d1a4ecd78..59e4c3af15edb10fe46f4e6afb0ad2d18bc92760 100644 (file)
 #include "icp_qat_fw.h"
 #include "icp_qat_fw_la.h"
 
-#define QAT_AES_HW_CONFIG_CBC_ENC(alg) \
-       ICP_QAT_HW_CIPHER_CONFIG_BUILD(ICP_QAT_HW_CIPHER_CBC_MODE, alg, \
+#define QAT_AES_HW_CONFIG_ENC(alg, mode) \
+       ICP_QAT_HW_CIPHER_CONFIG_BUILD(mode, alg, \
                                       ICP_QAT_HW_CIPHER_NO_CONVERT, \
                                       ICP_QAT_HW_CIPHER_ENCRYPT)
 
-#define QAT_AES_HW_CONFIG_CBC_DEC(alg) \
-       ICP_QAT_HW_CIPHER_CONFIG_BUILD(ICP_QAT_HW_CIPHER_CBC_MODE, alg, \
+#define QAT_AES_HW_CONFIG_DEC(alg, mode) \
+       ICP_QAT_HW_CIPHER_CONFIG_BUILD(mode, alg, \
                                       ICP_QAT_HW_CIPHER_KEY_CONVERT, \
                                       ICP_QAT_HW_CIPHER_DECRYPT)
 
@@ -271,7 +271,8 @@ static void qat_alg_init_common_hdr(struct icp_qat_fw_comn_req_hdr *header)
 
 static int qat_alg_aead_init_enc_session(struct crypto_aead *aead_tfm,
                                         int alg,
-                                        struct crypto_authenc_keys *keys)
+                                        struct crypto_authenc_keys *keys,
+                                        int mode)
 {
        struct qat_alg_aead_ctx *ctx = crypto_aead_ctx(aead_tfm);
        unsigned int digestsize = crypto_aead_authsize(aead_tfm);
@@ -288,7 +289,7 @@ static int qat_alg_aead_init_enc_session(struct crypto_aead *aead_tfm,
        struct icp_qat_fw_auth_cd_ctrl_hdr *hash_cd_ctrl = ptr;
 
        /* CD setup */
-       cipher->aes.cipher_config.val = QAT_AES_HW_CONFIG_CBC_ENC(alg);
+       cipher->aes.cipher_config.val = QAT_AES_HW_CONFIG_ENC(alg, mode);
        memcpy(cipher->aes.key, keys->enckey, keys->enckeylen);
        hash->sha.inner_setup.auth_config.config =
                ICP_QAT_HW_AUTH_CONFIG_BUILD(ICP_QAT_HW_AUTH_MODE1,
@@ -351,7 +352,8 @@ static int qat_alg_aead_init_enc_session(struct crypto_aead *aead_tfm,
 
 static int qat_alg_aead_init_dec_session(struct crypto_aead *aead_tfm,
                                         int alg,
-                                        struct crypto_authenc_keys *keys)
+                                        struct crypto_authenc_keys *keys,
+                                        int mode)
 {
        struct qat_alg_aead_ctx *ctx = crypto_aead_ctx(aead_tfm);
        unsigned int digestsize = crypto_aead_authsize(aead_tfm);
@@ -373,7 +375,7 @@ static int qat_alg_aead_init_dec_session(struct crypto_aead *aead_tfm,
                sizeof(struct icp_qat_fw_la_cipher_req_params));
 
        /* CD setup */
-       cipher->aes.cipher_config.val = QAT_AES_HW_CONFIG_CBC_DEC(alg);
+       cipher->aes.cipher_config.val = QAT_AES_HW_CONFIG_DEC(alg, mode);
        memcpy(cipher->aes.key, keys->enckey, keys->enckeylen);
        hash->sha.inner_setup.auth_config.config =
                ICP_QAT_HW_AUTH_CONFIG_BUILD(ICP_QAT_HW_AUTH_MODE1,
@@ -464,7 +466,7 @@ static void qat_alg_ablkcipher_init_com(struct qat_alg_ablkcipher_ctx *ctx,
 
 static void qat_alg_ablkcipher_init_enc(struct qat_alg_ablkcipher_ctx *ctx,
                                        int alg, const uint8_t *key,
-                                       unsigned int keylen)
+                                       unsigned int keylen, int mode)
 {
        struct icp_qat_hw_cipher_algo_blk *enc_cd = ctx->enc_cd;
        struct icp_qat_fw_la_bulk_req *req = &ctx->enc_fw_req;
@@ -472,12 +474,12 @@ static void qat_alg_ablkcipher_init_enc(struct qat_alg_ablkcipher_ctx *ctx,
 
        qat_alg_ablkcipher_init_com(ctx, req, enc_cd, key, keylen);
        cd_pars->u.s.content_desc_addr = ctx->enc_cd_paddr;
-       enc_cd->aes.cipher_config.val = QAT_AES_HW_CONFIG_CBC_ENC(alg);
+       enc_cd->aes.cipher_config.val = QAT_AES_HW_CONFIG_ENC(alg, mode);
 }
 
 static void qat_alg_ablkcipher_init_dec(struct qat_alg_ablkcipher_ctx *ctx,
                                        int alg, const uint8_t *key,
-                                       unsigned int keylen)
+                                       unsigned int keylen, int mode)
 {
        struct icp_qat_hw_cipher_algo_blk *dec_cd = ctx->dec_cd;
        struct icp_qat_fw_la_bulk_req *req = &ctx->dec_fw_req;
@@ -485,29 +487,48 @@ static void qat_alg_ablkcipher_init_dec(struct qat_alg_ablkcipher_ctx *ctx,
 
        qat_alg_ablkcipher_init_com(ctx, req, dec_cd, key, keylen);
        cd_pars->u.s.content_desc_addr = ctx->dec_cd_paddr;
-       dec_cd->aes.cipher_config.val = QAT_AES_HW_CONFIG_CBC_DEC(alg);
+
+       if (mode != ICP_QAT_HW_CIPHER_CTR_MODE)
+               dec_cd->aes.cipher_config.val =
+                                       QAT_AES_HW_CONFIG_DEC(alg, mode);
+       else
+               dec_cd->aes.cipher_config.val =
+                                       QAT_AES_HW_CONFIG_ENC(alg, mode);
 }
 
-static int qat_alg_validate_key(int key_len, int *alg)
+static int qat_alg_validate_key(int key_len, int *alg, int mode)
 {
-       switch (key_len) {
-       case AES_KEYSIZE_128:
-               *alg = ICP_QAT_HW_CIPHER_ALGO_AES128;
-               break;
-       case AES_KEYSIZE_192:
-               *alg = ICP_QAT_HW_CIPHER_ALGO_AES192;
-               break;
-       case AES_KEYSIZE_256:
-               *alg = ICP_QAT_HW_CIPHER_ALGO_AES256;
-               break;
-       default:
-               return -EINVAL;
+       if (mode != ICP_QAT_HW_CIPHER_XTS_MODE) {
+               switch (key_len) {
+               case AES_KEYSIZE_128:
+                       *alg = ICP_QAT_HW_CIPHER_ALGO_AES128;
+                       break;
+               case AES_KEYSIZE_192:
+                       *alg = ICP_QAT_HW_CIPHER_ALGO_AES192;
+                       break;
+               case AES_KEYSIZE_256:
+                       *alg = ICP_QAT_HW_CIPHER_ALGO_AES256;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       } else {
+               switch (key_len) {
+               case AES_KEYSIZE_128 << 1:
+                       *alg = ICP_QAT_HW_CIPHER_ALGO_AES128;
+                       break;
+               case AES_KEYSIZE_256 << 1:
+                       *alg = ICP_QAT_HW_CIPHER_ALGO_AES256;
+                       break;
+               default:
+                       return -EINVAL;
+               }
        }
        return 0;
 }
 
-static int qat_alg_aead_init_sessions(struct crypto_aead *tfm,
-                                     const uint8_t *key, unsigned int keylen)
+static int qat_alg_aead_init_sessions(struct crypto_aead *tfm, const u8 *key,
+                                     unsigned int keylen,  int mode)
 {
        struct crypto_authenc_keys keys;
        int alg;
@@ -515,13 +536,13 @@ static int qat_alg_aead_init_sessions(struct crypto_aead *tfm,
        if (crypto_authenc_extractkeys(&keys, key, keylen))
                goto bad_key;
 
-       if (qat_alg_validate_key(keys.enckeylen, &alg))
+       if (qat_alg_validate_key(keys.enckeylen, &alg, mode))
                goto bad_key;
 
-       if (qat_alg_aead_init_enc_session(tfm, alg, &keys))
+       if (qat_alg_aead_init_enc_session(tfm, alg, &keys, mode))
                goto error;
 
-       if (qat_alg_aead_init_dec_session(tfm, alg, &keys))
+       if (qat_alg_aead_init_dec_session(tfm, alg, &keys, mode))
                goto error;
 
        return 0;
@@ -534,15 +555,16 @@ error:
 
 static int qat_alg_ablkcipher_init_sessions(struct qat_alg_ablkcipher_ctx *ctx,
                                            const uint8_t *key,
-                                           unsigned int keylen)
+                                           unsigned int keylen,
+                                           int mode)
 {
        int alg;
 
-       if (qat_alg_validate_key(keylen, &alg))
+       if (qat_alg_validate_key(keylen, &alg, mode))
                goto bad_key;
 
-       qat_alg_ablkcipher_init_enc(ctx, alg, key, keylen);
-       qat_alg_ablkcipher_init_dec(ctx, alg, key, keylen);
+       qat_alg_ablkcipher_init_enc(ctx, alg, key, keylen, mode);
+       qat_alg_ablkcipher_init_dec(ctx, alg, key, keylen, mode);
        return 0;
 bad_key:
        crypto_tfm_set_flags(ctx->tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
@@ -586,7 +608,8 @@ static int qat_alg_aead_setkey(struct crypto_aead *tfm, const uint8_t *key,
                        goto out_free_enc;
                }
        }
-       if (qat_alg_aead_init_sessions(tfm, key, keylen))
+       if (qat_alg_aead_init_sessions(tfm, key, keylen,
+                                      ICP_QAT_HW_CIPHER_CBC_MODE))
                goto out_free_all;
 
        return 0;
@@ -876,8 +899,8 @@ static int qat_alg_aead_enc(struct aead_request *areq)
 }
 
 static int qat_alg_ablkcipher_setkey(struct crypto_ablkcipher *tfm,
-                                    const uint8_t *key,
-                                    unsigned int keylen)
+                                    const u8 *key, unsigned int keylen,
+                                    int mode)
 {
        struct qat_alg_ablkcipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
        struct device *dev;
@@ -918,7 +941,7 @@ static int qat_alg_ablkcipher_setkey(struct crypto_ablkcipher *tfm,
                }
        }
        spin_unlock(&ctx->lock);
-       if (qat_alg_ablkcipher_init_sessions(ctx, key, keylen))
+       if (qat_alg_ablkcipher_init_sessions(ctx, key, keylen, mode))
                goto out_free_all;
 
        return 0;
@@ -936,6 +959,27 @@ out_free_enc:
        return -ENOMEM;
 }
 
+static int qat_alg_ablkcipher_cbc_setkey(struct crypto_ablkcipher *tfm,
+                                        const u8 *key, unsigned int keylen)
+{
+       return qat_alg_ablkcipher_setkey(tfm, key, keylen,
+                                        ICP_QAT_HW_CIPHER_CBC_MODE);
+}
+
+static int qat_alg_ablkcipher_ctr_setkey(struct crypto_ablkcipher *tfm,
+                                        const u8 *key, unsigned int keylen)
+{
+       return qat_alg_ablkcipher_setkey(tfm, key, keylen,
+                                        ICP_QAT_HW_CIPHER_CTR_MODE);
+}
+
+static int qat_alg_ablkcipher_xts_setkey(struct crypto_ablkcipher *tfm,
+                                        const u8 *key, unsigned int keylen)
+{
+       return qat_alg_ablkcipher_setkey(tfm, key, keylen,
+                                        ICP_QAT_HW_CIPHER_XTS_MODE);
+}
+
 static int qat_alg_ablkcipher_encrypt(struct ablkcipher_request *req)
 {
        struct crypto_ablkcipher *atfm = crypto_ablkcipher_reqtfm(req);
@@ -1171,7 +1215,51 @@ static struct crypto_alg qat_algs[] = { {
        .cra_exit = qat_alg_ablkcipher_exit,
        .cra_u = {
                .ablkcipher = {
-                       .setkey = qat_alg_ablkcipher_setkey,
+                       .setkey = qat_alg_ablkcipher_cbc_setkey,
+                       .decrypt = qat_alg_ablkcipher_decrypt,
+                       .encrypt = qat_alg_ablkcipher_encrypt,
+                       .min_keysize = AES_MIN_KEY_SIZE,
+                       .max_keysize = AES_MAX_KEY_SIZE,
+                       .ivsize = AES_BLOCK_SIZE,
+               },
+       },
+}, {
+       .cra_name = "ctr(aes)",
+       .cra_driver_name = "qat_aes_ctr",
+       .cra_priority = 4001,
+       .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+       .cra_blocksize = AES_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct qat_alg_ablkcipher_ctx),
+       .cra_alignmask = 0,
+       .cra_type = &crypto_ablkcipher_type,
+       .cra_module = THIS_MODULE,
+       .cra_init = qat_alg_ablkcipher_init,
+       .cra_exit = qat_alg_ablkcipher_exit,
+       .cra_u = {
+               .ablkcipher = {
+                       .setkey = qat_alg_ablkcipher_ctr_setkey,
+                       .decrypt = qat_alg_ablkcipher_decrypt,
+                       .encrypt = qat_alg_ablkcipher_encrypt,
+                       .min_keysize = AES_MIN_KEY_SIZE,
+                       .max_keysize = AES_MAX_KEY_SIZE,
+                       .ivsize = AES_BLOCK_SIZE,
+               },
+       },
+}, {
+       .cra_name = "xts(aes)",
+       .cra_driver_name = "qat_aes_xts",
+       .cra_priority = 4001,
+       .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+       .cra_blocksize = AES_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct qat_alg_ablkcipher_ctx),
+       .cra_alignmask = 0,
+       .cra_type = &crypto_ablkcipher_type,
+       .cra_module = THIS_MODULE,
+       .cra_init = qat_alg_ablkcipher_init,
+       .cra_exit = qat_alg_ablkcipher_exit,
+       .cra_u = {
+               .ablkcipher = {
+                       .setkey = qat_alg_ablkcipher_xts_setkey,
                        .decrypt = qat_alg_ablkcipher_decrypt,
                        .encrypt = qat_alg_ablkcipher_encrypt,
                        .min_keysize = AES_MIN_KEY_SIZE,
@@ -1212,7 +1300,7 @@ unreg_algs:
        goto unlock;
 }
 
-int qat_algs_unregister(void)
+void qat_algs_unregister(void)
 {
        mutex_lock(&algs_lock);
        if (--active_devs != 0)
@@ -1223,14 +1311,4 @@ int qat_algs_unregister(void)
 
 unlock:
        mutex_unlock(&algs_lock);
-       return 0;
-}
-
-int qat_algs_init(void)
-{
-       return 0;
-}
-
-void qat_algs_exit(void)
-{
 }
index e87f51023ba4ef2d8db9640c44adb7d4396bd467..51c594fdacdc7bb2b2887ca400182525ebadc1af 100644 (file)
@@ -51,7 +51,9 @@
 #include <crypto/akcipher.h>
 #include <linux/dma-mapping.h>
 #include <linux/fips.h>
-#include "qat_rsakey-asn1.h"
+#include <crypto/scatterwalk.h>
+#include "qat_rsapubkey-asn1.h"
+#include "qat_rsaprivkey-asn1.h"
 #include "icp_qat_fw_pke.h"
 #include "adf_accel_devices.h"
 #include "adf_transport.h"
@@ -106,6 +108,7 @@ struct qat_rsa_request {
        dma_addr_t phy_in;
        dma_addr_t phy_out;
        char *src_align;
+       char *dst_align;
        struct icp_qat_fw_pke_request req;
        struct qat_rsa_ctx *ctx;
        int err;
@@ -118,7 +121,6 @@ static void qat_rsa_cb(struct icp_qat_fw_pke_resp *resp)
        struct device *dev = &GET_DEV(req->ctx->inst->accel_dev);
        int err = ICP_QAT_FW_PKE_RESP_PKE_STAT_GET(
                                resp->pke_resp_hdr.comn_resp_flags);
-       char *ptr = areq->dst;
 
        err = (err == ICP_QAT_FW_COMN_STATUS_FLAG_OK) ? 0 : -EINVAL;
 
@@ -129,24 +131,44 @@ static void qat_rsa_cb(struct icp_qat_fw_pke_resp *resp)
                dma_unmap_single(dev, req->in.enc.m, req->ctx->key_sz,
                                 DMA_TO_DEVICE);
 
-       dma_unmap_single(dev, req->out.enc.c, req->ctx->key_sz,
-                        DMA_FROM_DEVICE);
+       areq->dst_len = req->ctx->key_sz;
+       if (req->dst_align) {
+               char *ptr = req->dst_align;
+
+               while (!(*ptr) && areq->dst_len) {
+                       areq->dst_len--;
+                       ptr++;
+               }
+
+               if (areq->dst_len != req->ctx->key_sz)
+                       memmove(req->dst_align, ptr, areq->dst_len);
+
+               scatterwalk_map_and_copy(req->dst_align, areq->dst, 0,
+                                        areq->dst_len, 1);
+
+               dma_free_coherent(dev, req->ctx->key_sz, req->dst_align,
+                                 req->out.enc.c);
+       } else {
+               char *ptr = sg_virt(areq->dst);
+
+               while (!(*ptr) && areq->dst_len) {
+                       areq->dst_len--;
+                       ptr++;
+               }
+
+               if (sg_virt(areq->dst) != ptr && areq->dst_len)
+                       memmove(sg_virt(areq->dst), ptr, areq->dst_len);
+
+               dma_unmap_single(dev, req->out.enc.c, req->ctx->key_sz,
+                                DMA_FROM_DEVICE);
+       }
+
        dma_unmap_single(dev, req->phy_in, sizeof(struct qat_rsa_input_params),
                         DMA_TO_DEVICE);
        dma_unmap_single(dev, req->phy_out,
                         sizeof(struct qat_rsa_output_params),
                         DMA_TO_DEVICE);
 
-       areq->dst_len = req->ctx->key_sz;
-       /* Need to set the corect length of the output */
-       while (!(*ptr) && areq->dst_len) {
-               areq->dst_len--;
-               ptr++;
-       }
-
-       if (areq->dst_len != req->ctx->key_sz)
-               memmove(areq->dst, ptr, areq->dst_len);
-
        akcipher_request_complete(areq, err);
 }
 
@@ -255,8 +277,16 @@ static int qat_rsa_enc(struct akcipher_request *req)
         * same as modulo n so in case it is different we need to allocate a
         * new buf and copy src data.
         * In other case we just need to map the user provided buffer.
+        * Also need to make sure that it is in contiguous buffer.
         */
-       if (req->src_len < ctx->key_sz) {
+       if (sg_is_last(req->src) && req->src_len == ctx->key_sz) {
+               qat_req->src_align = NULL;
+               qat_req->in.enc.m = dma_map_single(dev, sg_virt(req->src),
+                                                  req->src_len, DMA_TO_DEVICE);
+               if (unlikely(dma_mapping_error(dev, qat_req->in.enc.m)))
+                       return ret;
+
+       } else {
                int shift = ctx->key_sz - req->src_len;
 
                qat_req->src_align = dma_zalloc_coherent(dev, ctx->key_sz,
@@ -265,29 +295,39 @@ static int qat_rsa_enc(struct akcipher_request *req)
                if (unlikely(!qat_req->src_align))
                        return ret;
 
-               memcpy(qat_req->src_align + shift, req->src, req->src_len);
+               scatterwalk_map_and_copy(qat_req->src_align + shift, req->src,
+                                        0, req->src_len, 0);
+       }
+       if (sg_is_last(req->dst) && req->dst_len == ctx->key_sz) {
+               qat_req->dst_align = NULL;
+               qat_req->out.enc.c = dma_map_single(dev, sg_virt(req->dst),
+                                                   req->dst_len,
+                                                   DMA_FROM_DEVICE);
+
+               if (unlikely(dma_mapping_error(dev, qat_req->out.enc.c)))
+                       goto unmap_src;
+
        } else {
-               qat_req->src_align = NULL;
-               qat_req->in.enc.m = dma_map_single(dev, req->src, req->src_len,
-                                          DMA_TO_DEVICE);
+               qat_req->dst_align = dma_zalloc_coherent(dev, ctx->key_sz,
+                                                        &qat_req->out.enc.c,
+                                                        GFP_KERNEL);
+               if (unlikely(!qat_req->dst_align))
+                       goto unmap_src;
+
        }
        qat_req->in.in_tab[3] = 0;
-       qat_req->out.enc.c = dma_map_single(dev, req->dst, req->dst_len,
-                                           DMA_FROM_DEVICE);
        qat_req->out.out_tab[1] = 0;
        qat_req->phy_in = dma_map_single(dev, &qat_req->in.enc.m,
                                         sizeof(struct qat_rsa_input_params),
                                         DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(dev, qat_req->phy_in)))
+               goto unmap_dst;
+
        qat_req->phy_out = dma_map_single(dev, &qat_req->out.enc.c,
                                          sizeof(struct qat_rsa_output_params),
-                                           DMA_TO_DEVICE);
-
-       if (unlikely((!qat_req->src_align &&
-                     dma_mapping_error(dev, qat_req->in.enc.m)) ||
-                    dma_mapping_error(dev, qat_req->out.enc.c) ||
-                    dma_mapping_error(dev, qat_req->phy_in) ||
-                    dma_mapping_error(dev, qat_req->phy_out)))
-               goto unmap;
+                                         DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(dev, qat_req->phy_out)))
+               goto unmap_in_params;
 
        msg->pke_mid.src_data_addr = qat_req->phy_in;
        msg->pke_mid.dest_data_addr = qat_req->phy_out;
@@ -300,7 +340,7 @@ static int qat_rsa_enc(struct akcipher_request *req)
 
        if (!ret)
                return -EINPROGRESS;
-unmap:
+unmap_src:
        if (qat_req->src_align)
                dma_free_coherent(dev, ctx->key_sz, qat_req->src_align,
                                  qat_req->in.enc.m);
@@ -308,9 +348,15 @@ unmap:
                if (!dma_mapping_error(dev, qat_req->in.enc.m))
                        dma_unmap_single(dev, qat_req->in.enc.m, ctx->key_sz,
                                         DMA_TO_DEVICE);
-       if (!dma_mapping_error(dev, qat_req->out.enc.c))
-               dma_unmap_single(dev, qat_req->out.enc.c, ctx->key_sz,
-                                DMA_FROM_DEVICE);
+unmap_dst:
+       if (qat_req->dst_align)
+               dma_free_coherent(dev, ctx->key_sz, qat_req->dst_align,
+                                 qat_req->out.enc.c);
+       else
+               if (!dma_mapping_error(dev, qat_req->out.enc.c))
+                       dma_unmap_single(dev, qat_req->out.enc.c, ctx->key_sz,
+                                        DMA_FROM_DEVICE);
+unmap_in_params:
        if (!dma_mapping_error(dev, qat_req->phy_in))
                dma_unmap_single(dev, qat_req->phy_in,
                                 sizeof(struct qat_rsa_input_params),
@@ -362,8 +408,16 @@ static int qat_rsa_dec(struct akcipher_request *req)
         * same as modulo n so in case it is different we need to allocate a
         * new buf and copy src data.
         * In other case we just need to map the user provided buffer.
+        * Also need to make sure that it is in contiguous buffer.
         */
-       if (req->src_len < ctx->key_sz) {
+       if (sg_is_last(req->src) && req->src_len == ctx->key_sz) {
+               qat_req->src_align = NULL;
+               qat_req->in.dec.c = dma_map_single(dev, sg_virt(req->src),
+                                                  req->dst_len, DMA_TO_DEVICE);
+               if (unlikely(dma_mapping_error(dev, qat_req->in.dec.c)))
+                       return ret;
+
+       } else {
                int shift = ctx->key_sz - req->src_len;
 
                qat_req->src_align = dma_zalloc_coherent(dev, ctx->key_sz,
@@ -372,29 +426,40 @@ static int qat_rsa_dec(struct akcipher_request *req)
                if (unlikely(!qat_req->src_align))
                        return ret;
 
-               memcpy(qat_req->src_align + shift, req->src, req->src_len);
+               scatterwalk_map_and_copy(qat_req->src_align + shift, req->src,
+                                        0, req->src_len, 0);
+       }
+       if (sg_is_last(req->dst) && req->dst_len == ctx->key_sz) {
+               qat_req->dst_align = NULL;
+               qat_req->out.dec.m = dma_map_single(dev, sg_virt(req->dst),
+                                                   req->dst_len,
+                                                   DMA_FROM_DEVICE);
+
+               if (unlikely(dma_mapping_error(dev, qat_req->out.dec.m)))
+                       goto unmap_src;
+
        } else {
-               qat_req->src_align = NULL;
-               qat_req->in.dec.c = dma_map_single(dev, req->src, req->src_len,
-                                                  DMA_TO_DEVICE);
+               qat_req->dst_align = dma_zalloc_coherent(dev, ctx->key_sz,
+                                                        &qat_req->out.dec.m,
+                                                        GFP_KERNEL);
+               if (unlikely(!qat_req->dst_align))
+                       goto unmap_src;
+
        }
+
        qat_req->in.in_tab[3] = 0;
-       qat_req->out.dec.m = dma_map_single(dev, req->dst, req->dst_len,
-                                           DMA_FROM_DEVICE);
        qat_req->out.out_tab[1] = 0;
        qat_req->phy_in = dma_map_single(dev, &qat_req->in.dec.c,
                                         sizeof(struct qat_rsa_input_params),
                                         DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(dev, qat_req->phy_in)))
+               goto unmap_dst;
+
        qat_req->phy_out = dma_map_single(dev, &qat_req->out.dec.m,
                                          sizeof(struct qat_rsa_output_params),
-                                           DMA_TO_DEVICE);
-
-       if (unlikely((!qat_req->src_align &&
-                     dma_mapping_error(dev, qat_req->in.dec.c)) ||
-                    dma_mapping_error(dev, qat_req->out.dec.m) ||
-                    dma_mapping_error(dev, qat_req->phy_in) ||
-                    dma_mapping_error(dev, qat_req->phy_out)))
-               goto unmap;
+                                         DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(dev, qat_req->phy_out)))
+               goto unmap_in_params;
 
        msg->pke_mid.src_data_addr = qat_req->phy_in;
        msg->pke_mid.dest_data_addr = qat_req->phy_out;
@@ -407,7 +472,7 @@ static int qat_rsa_dec(struct akcipher_request *req)
 
        if (!ret)
                return -EINPROGRESS;
-unmap:
+unmap_src:
        if (qat_req->src_align)
                dma_free_coherent(dev, ctx->key_sz, qat_req->src_align,
                                  qat_req->in.dec.c);
@@ -415,9 +480,15 @@ unmap:
                if (!dma_mapping_error(dev, qat_req->in.dec.c))
                        dma_unmap_single(dev, qat_req->in.dec.c, ctx->key_sz,
                                         DMA_TO_DEVICE);
-       if (!dma_mapping_error(dev, qat_req->out.dec.m))
-               dma_unmap_single(dev, qat_req->out.dec.m, ctx->key_sz,
-                                DMA_FROM_DEVICE);
+unmap_dst:
+       if (qat_req->dst_align)
+               dma_free_coherent(dev, ctx->key_sz, qat_req->dst_align,
+                                 qat_req->out.dec.m);
+       else
+               if (!dma_mapping_error(dev, qat_req->out.dec.m))
+                       dma_unmap_single(dev, qat_req->out.dec.m, ctx->key_sz,
+                                        DMA_FROM_DEVICE);
+unmap_in_params:
        if (!dma_mapping_error(dev, qat_req->phy_in))
                dma_unmap_single(dev, qat_req->phy_in,
                                 sizeof(struct qat_rsa_input_params),
@@ -531,7 +602,7 @@ err:
 }
 
 static int qat_rsa_setkey(struct crypto_akcipher *tfm, const void *key,
-                         unsigned int keylen)
+                         unsigned int keylen, bool private)
 {
        struct qat_rsa_ctx *ctx = akcipher_tfm_ctx(tfm);
        struct device *dev = &GET_DEV(ctx->inst->accel_dev);
@@ -550,7 +621,13 @@ static int qat_rsa_setkey(struct crypto_akcipher *tfm, const void *key,
        ctx->n = NULL;
        ctx->e = NULL;
        ctx->d = NULL;
-       ret = asn1_ber_decoder(&qat_rsakey_decoder, ctx, key, keylen);
+
+       if (private)
+               ret = asn1_ber_decoder(&qat_rsaprivkey_decoder, ctx, key,
+                                      keylen);
+       else
+               ret = asn1_ber_decoder(&qat_rsapubkey_decoder, ctx, key,
+                                      keylen);
        if (ret < 0)
                goto free;
 
@@ -559,6 +636,11 @@ static int qat_rsa_setkey(struct crypto_akcipher *tfm, const void *key,
                ret = -EINVAL;
                goto free;
        }
+       if (private && !ctx->d) {
+               /* invalid private key provided */
+               ret = -EINVAL;
+               goto free;
+       }
 
        return 0;
 free:
@@ -579,6 +661,25 @@ free:
        return ret;
 }
 
+static int qat_rsa_setpubkey(struct crypto_akcipher *tfm, const void *key,
+                            unsigned int keylen)
+{
+       return qat_rsa_setkey(tfm, key, keylen, false);
+}
+
+static int qat_rsa_setprivkey(struct crypto_akcipher *tfm, const void *key,
+                             unsigned int keylen)
+{
+       return qat_rsa_setkey(tfm, key, keylen, true);
+}
+
+static int qat_rsa_max_size(struct crypto_akcipher *tfm)
+{
+       struct qat_rsa_ctx *ctx = akcipher_tfm_ctx(tfm);
+
+       return (ctx->n) ? ctx->key_sz : -EINVAL;
+}
+
 static int qat_rsa_init_tfm(struct crypto_akcipher *tfm)
 {
        struct qat_rsa_ctx *ctx = akcipher_tfm_ctx(tfm);
@@ -617,7 +718,9 @@ static struct akcipher_alg rsa = {
        .decrypt = qat_rsa_dec,
        .sign = qat_rsa_dec,
        .verify = qat_rsa_enc,
-       .setkey = qat_rsa_setkey,
+       .set_pub_key = qat_rsa_setpubkey,
+       .set_priv_key = qat_rsa_setprivkey,
+       .max_size = qat_rsa_max_size,
        .init = qat_rsa_init_tfm,
        .exit = qat_rsa_exit_tfm,
        .reqsize = sizeof(struct qat_rsa_request) + 64,
index 07c2f9f9d1fcd8831938e4c10711dd2a6a736acb..9cab15497f0470ee710cc9e1d4df31b43a64b773 100644 (file)
@@ -60,8 +60,8 @@ static struct service_hndl qat_crypto;
 
 void qat_crypto_put_instance(struct qat_crypto_instance *inst)
 {
-       if (atomic_sub_return(1, &inst->refctr) == 0)
-               adf_dev_put(inst->accel_dev);
+       atomic_dec(&inst->refctr);
+       adf_dev_put(inst->accel_dev);
 }
 
 static int qat_crypto_free_instances(struct adf_accel_dev *accel_dev)
@@ -97,49 +97,66 @@ static int qat_crypto_free_instances(struct adf_accel_dev *accel_dev)
 struct qat_crypto_instance *qat_crypto_get_instance_node(int node)
 {
        struct adf_accel_dev *accel_dev = NULL;
-       struct qat_crypto_instance *inst_best = NULL;
+       struct qat_crypto_instance *inst = NULL;
        struct list_head *itr;
        unsigned long best = ~0;
 
        list_for_each(itr, adf_devmgr_get_head()) {
-               accel_dev = list_entry(itr, struct adf_accel_dev, list);
+               struct adf_accel_dev *tmp_dev;
+               unsigned long ctr;
+
+               tmp_dev = list_entry(itr, struct adf_accel_dev, list);
+
+               if ((node == dev_to_node(&GET_DEV(tmp_dev)) ||
+                    dev_to_node(&GET_DEV(tmp_dev)) < 0) &&
+                   adf_dev_started(tmp_dev) &&
+                   !list_empty(&tmp_dev->crypto_list)) {
+                       ctr = atomic_read(&tmp_dev->ref_count);
+                       if (best > ctr) {
+                               accel_dev = tmp_dev;
+                               best = ctr;
+                       }
+               }
+       }
+       if (!accel_dev)
+               pr_info("QAT: Could not find a device on node %d\n", node);
+
+       /* Get any started device */
+       list_for_each(itr, adf_devmgr_get_head()) {
+               struct adf_accel_dev *tmp_dev;
 
-               if ((node == dev_to_node(&GET_DEV(accel_dev)) ||
-                    dev_to_node(&GET_DEV(accel_dev)) < 0) &&
-                   adf_dev_started(accel_dev) &&
-                   !list_empty(&accel_dev->crypto_list))
+               tmp_dev = list_entry(itr, struct adf_accel_dev, list);
+
+               if (adf_dev_started(tmp_dev) &&
+                   !list_empty(&tmp_dev->crypto_list)) {
+                       accel_dev = tmp_dev;
                        break;
-               accel_dev = NULL;
-       }
-       if (!accel_dev) {
-               pr_err("QAT: Could not find a device on node %d\n", node);
-               accel_dev = adf_devmgr_get_first();
+               }
        }
-       if (!accel_dev || !adf_dev_started(accel_dev))
+
+       if (!accel_dev)
                return NULL;
 
+       best = ~0;
        list_for_each(itr, &accel_dev->crypto_list) {
-               struct qat_crypto_instance *inst;
-               unsigned long cur;
-
-               inst = list_entry(itr, struct qat_crypto_instance, list);
-               cur = atomic_read(&inst->refctr);
-               if (best > cur) {
-                       inst_best = inst;
-                       best = cur;
+               struct qat_crypto_instance *tmp_inst;
+               unsigned long ctr;
+
+               tmp_inst = list_entry(itr, struct qat_crypto_instance, list);
+               ctr = atomic_read(&tmp_inst->refctr);
+               if (best > ctr) {
+                       inst = tmp_inst;
+                       best = ctr;
                }
        }
-       if (inst_best) {
-               if (atomic_add_return(1, &inst_best->refctr) == 1) {
-                       if (adf_dev_get(accel_dev)) {
-                               atomic_dec(&inst_best->refctr);
-                               dev_err(&GET_DEV(accel_dev),
-                                       "Could not increment dev refctr\n");
-                               return NULL;
-                       }
+       if (inst) {
+               if (adf_dev_get(accel_dev)) {
+                       dev_err(&GET_DEV(accel_dev), "Could not increment dev refctr\n");
+                       return NULL;
                }
+               atomic_inc(&inst->refctr);
        }
-       return inst_best;
+       return inst;
 }
 
 static int qat_crypto_create_instances(struct adf_accel_dev *accel_dev)
index 8e711d1c308402ad35ce87822ed16f572d07fe5b..380e761801a71f5471df7e35feeaba7eedc6d3b8 100644 (file)
@@ -1034,7 +1034,7 @@ static int qat_hal_concat_micro_code(uint64_t *micro_inst,
                                     unsigned int inst_num, unsigned int size,
                                     unsigned int addr, unsigned int *value)
 {
-       int i, val_indx;
+       int i;
        unsigned int cur_value;
        const uint64_t *inst_arr;
        int fixup_offset;
@@ -1042,8 +1042,7 @@ static int qat_hal_concat_micro_code(uint64_t *micro_inst,
        int orig_num;
 
        orig_num = inst_num;
-       val_indx = 0;
-       cur_value = value[val_indx++];
+       cur_value = value[0];
        inst_arr = inst_4b;
        usize = ARRAY_SIZE(inst_4b);
        fixup_offset = inst_num;
diff --git a/drivers/crypto/qat/qat_common/qat_rsakey.asn1 b/drivers/crypto/qat/qat_common/qat_rsakey.asn1
deleted file mode 100644 (file)
index 97b0e02..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-RsaKey ::= SEQUENCE {
-       n INTEGER ({ qat_rsa_get_n }),
-       e INTEGER ({ qat_rsa_get_e }),
-       d INTEGER ({ qat_rsa_get_d })
-}
diff --git a/drivers/crypto/qat/qat_common/qat_rsaprivkey.asn1 b/drivers/crypto/qat/qat_common/qat_rsaprivkey.asn1
new file mode 100644 (file)
index 0000000..f0066ad
--- /dev/null
@@ -0,0 +1,11 @@
+RsaPrivKey ::= SEQUENCE {
+       version         INTEGER,
+       n               INTEGER ({ qat_rsa_get_n }),
+       e               INTEGER ({ qat_rsa_get_e }),
+       d               INTEGER ({ qat_rsa_get_d }),
+       prime1          INTEGER,
+       prime2          INTEGER,
+       exponent1       INTEGER,
+       exponent2       INTEGER,
+       coefficient     INTEGER
+}
diff --git a/drivers/crypto/qat/qat_common/qat_rsapubkey.asn1 b/drivers/crypto/qat/qat_common/qat_rsapubkey.asn1
new file mode 100644 (file)
index 0000000..bd667b3
--- /dev/null
@@ -0,0 +1,4 @@
+RsaPubKey ::= SEQUENCE {
+       n INTEGER ({ qat_rsa_get_n }),
+       e INTEGER ({ qat_rsa_get_e })
+}
index ad592de475a4295be62c0c59f2af9e4bb2d8e529..2c0d63d48747dcc6fdd9d44a5182144032c2db4f 100644 (file)
@@ -44,10 +44,8 @@ static void qce_ablkcipher_done(void *data)
                        error);
 
        if (diff_dst)
-               qce_unmapsg(qce->dev, rctx->src_sg, rctx->src_nents, dir_src,
-                           rctx->dst_chained);
-       qce_unmapsg(qce->dev, rctx->dst_sg, rctx->dst_nents, dir_dst,
-                   rctx->dst_chained);
+               dma_unmap_sg(qce->dev, rctx->src_sg, rctx->src_nents, dir_src);
+       dma_unmap_sg(qce->dev, rctx->dst_sg, rctx->dst_nents, dir_dst);
 
        sg_free_table(&rctx->dst_tbl);
 
@@ -80,15 +78,11 @@ qce_ablkcipher_async_req_handle(struct crypto_async_request *async_req)
        dir_src = diff_dst ? DMA_TO_DEVICE : DMA_BIDIRECTIONAL;
        dir_dst = diff_dst ? DMA_FROM_DEVICE : DMA_BIDIRECTIONAL;
 
-       rctx->src_nents = qce_countsg(req->src, req->nbytes,
-                                     &rctx->src_chained);
-       if (diff_dst) {
-               rctx->dst_nents = qce_countsg(req->dst, req->nbytes,
-                                             &rctx->dst_chained);
-       } else {
+       rctx->src_nents = sg_nents_for_len(req->src, req->nbytes);
+       if (diff_dst)
+               rctx->dst_nents = sg_nents_for_len(req->dst, req->nbytes);
+       else
                rctx->dst_nents = rctx->src_nents;
-               rctx->dst_chained = rctx->src_chained;
-       }
 
        rctx->dst_nents += 1;
 
@@ -116,14 +110,12 @@ qce_ablkcipher_async_req_handle(struct crypto_async_request *async_req)
        sg_mark_end(sg);
        rctx->dst_sg = rctx->dst_tbl.sgl;
 
-       ret = qce_mapsg(qce->dev, rctx->dst_sg, rctx->dst_nents, dir_dst,
-                       rctx->dst_chained);
+       ret = dma_map_sg(qce->dev, rctx->dst_sg, rctx->dst_nents, dir_dst);
        if (ret < 0)
                goto error_free;
 
        if (diff_dst) {
-               ret = qce_mapsg(qce->dev, req->src, rctx->src_nents, dir_src,
-                               rctx->src_chained);
+               ret = dma_map_sg(qce->dev, req->src, rctx->src_nents, dir_src);
                if (ret < 0)
                        goto error_unmap_dst;
                rctx->src_sg = req->src;
@@ -149,11 +141,9 @@ error_terminate:
        qce_dma_terminate_all(&qce->dma);
 error_unmap_src:
        if (diff_dst)
-               qce_unmapsg(qce->dev, req->src, rctx->src_nents, dir_src,
-                           rctx->src_chained);
+               dma_unmap_sg(qce->dev, req->src, rctx->src_nents, dir_src);
 error_unmap_dst:
-       qce_unmapsg(qce->dev, rctx->dst_sg, rctx->dst_nents, dir_dst,
-                   rctx->dst_chained);
+       dma_unmap_sg(qce->dev, rctx->dst_sg, rctx->dst_nents, dir_dst);
 error_free:
        sg_free_table(&rctx->dst_tbl);
        return ret;
index d5757cfcda2de1a9c8b3da309ff723d9fedc817b..5c6a5f8633e5d39d0f20340617a1df3f8a4137d1 100644 (file)
@@ -32,8 +32,6 @@ struct qce_cipher_ctx {
  * @ivsize: IV size
  * @src_nents: source entries
  * @dst_nents: destination entries
- * @src_chained: is source chained
- * @dst_chained: is destination chained
  * @result_sg: scatterlist used for result buffer
  * @dst_tbl: destination sg table
  * @dst_sg: destination sg pointer table beginning
@@ -47,8 +45,6 @@ struct qce_cipher_reqctx {
        unsigned int ivsize;
        int src_nents;
        int dst_nents;
-       bool src_chained;
-       bool dst_chained;
        struct scatterlist result_sg;
        struct sg_table dst_tbl;
        struct scatterlist *dst_sg;
index 378cb768647f6f7306c94485cc4a5c5347ffe173..4797e795c9b9ccabb665e2a75a8790c2bc304cf3 100644 (file)
@@ -54,58 +54,6 @@ void qce_dma_release(struct qce_dma_data *dma)
        kfree(dma->result_buf);
 }
 
-int qce_mapsg(struct device *dev, struct scatterlist *sg, int nents,
-             enum dma_data_direction dir, bool chained)
-{
-       int err;
-
-       if (chained) {
-               while (sg) {
-                       err = dma_map_sg(dev, sg, 1, dir);
-                       if (!err)
-                               return -EFAULT;
-                       sg = sg_next(sg);
-               }
-       } else {
-               err = dma_map_sg(dev, sg, nents, dir);
-               if (!err)
-                       return -EFAULT;
-       }
-
-       return nents;
-}
-
-void qce_unmapsg(struct device *dev, struct scatterlist *sg, int nents,
-                enum dma_data_direction dir, bool chained)
-{
-       if (chained)
-               while (sg) {
-                       dma_unmap_sg(dev, sg, 1, dir);
-                       sg = sg_next(sg);
-               }
-       else
-               dma_unmap_sg(dev, sg, nents, dir);
-}
-
-int qce_countsg(struct scatterlist *sglist, int nbytes, bool *chained)
-{
-       struct scatterlist *sg = sglist;
-       int nents = 0;
-
-       if (chained)
-               *chained = false;
-
-       while (nbytes > 0 && sg) {
-               nents++;
-               nbytes -= sg->length;
-               if (!sg_is_last(sg) && (sg + 1)->length == 0 && chained)
-                       *chained = true;
-               sg = sg_next(sg);
-       }
-
-       return nents;
-}
-
 struct scatterlist *
 qce_sgtable_add(struct sg_table *sgt, struct scatterlist *new_sgl)
 {
index 65bedb81de0b5f19071fff20a2ff9ee2db4fa18d..130235d17bb4e0b60ea6c78986de0a5bb2320619 100644 (file)
@@ -49,11 +49,6 @@ int qce_dma_prep_sgs(struct qce_dma_data *dma, struct scatterlist *sg_in,
                     dma_async_tx_callback cb, void *cb_param);
 void qce_dma_issue_pending(struct qce_dma_data *dma);
 int qce_dma_terminate_all(struct qce_dma_data *dma);
-int qce_countsg(struct scatterlist *sg_list, int nbytes, bool *chained);
-void qce_unmapsg(struct device *dev, struct scatterlist *sg, int nents,
-                enum dma_data_direction dir, bool chained);
-int qce_mapsg(struct device *dev, struct scatterlist *sg, int nents,
-             enum dma_data_direction dir, bool chained);
 struct scatterlist *
 qce_sgtable_add(struct sg_table *sgt, struct scatterlist *sg_add);
 
index be2f5049256af5e846c49a66da40f420f5e6a6ae..0c9973ec80ebde80513152b7f16af5d50da8daae 100644 (file)
@@ -51,9 +51,8 @@ static void qce_ahash_done(void *data)
        if (error)
                dev_dbg(qce->dev, "ahash dma termination error (%d)\n", error);
 
-       qce_unmapsg(qce->dev, req->src, rctx->src_nents, DMA_TO_DEVICE,
-                   rctx->src_chained);
-       qce_unmapsg(qce->dev, &rctx->result_sg, 1, DMA_FROM_DEVICE, 0);
+       dma_unmap_sg(qce->dev, req->src, rctx->src_nents, DMA_TO_DEVICE);
+       dma_unmap_sg(qce->dev, &rctx->result_sg, 1, DMA_FROM_DEVICE);
 
        memcpy(rctx->digest, result->auth_iv, digestsize);
        if (req->result)
@@ -92,16 +91,14 @@ static int qce_ahash_async_req_handle(struct crypto_async_request *async_req)
                rctx->authklen = AES_KEYSIZE_128;
        }
 
-       rctx->src_nents = qce_countsg(req->src, req->nbytes,
-                                     &rctx->src_chained);
-       ret = qce_mapsg(qce->dev, req->src, rctx->src_nents, DMA_TO_DEVICE,
-                       rctx->src_chained);
+       rctx->src_nents = sg_nents_for_len(req->src, req->nbytes);
+       ret = dma_map_sg(qce->dev, req->src, rctx->src_nents, DMA_TO_DEVICE);
        if (ret < 0)
                return ret;
 
        sg_init_one(&rctx->result_sg, qce->dma.result_buf, QCE_RESULT_BUF_SZ);
 
-       ret = qce_mapsg(qce->dev, &rctx->result_sg, 1, DMA_FROM_DEVICE, 0);
+       ret = dma_map_sg(qce->dev, &rctx->result_sg, 1, DMA_FROM_DEVICE);
        if (ret < 0)
                goto error_unmap_src;
 
@@ -121,10 +118,9 @@ static int qce_ahash_async_req_handle(struct crypto_async_request *async_req)
 error_terminate:
        qce_dma_terminate_all(&qce->dma);
 error_unmap_dst:
-       qce_unmapsg(qce->dev, &rctx->result_sg, 1, DMA_FROM_DEVICE, 0);
+       dma_unmap_sg(qce->dev, &rctx->result_sg, 1, DMA_FROM_DEVICE);
 error_unmap_src:
-       qce_unmapsg(qce->dev, req->src, rctx->src_nents, DMA_TO_DEVICE,
-                   rctx->src_chained);
+       dma_unmap_sg(qce->dev, req->src, rctx->src_nents, DMA_TO_DEVICE);
        return ret;
 }
 
index 286f0d5397f359c6662014499f7462887c5e1290..236bb5e9ae7537792ad4b3b925b46f86a053b507 100644 (file)
@@ -36,7 +36,6 @@ struct qce_sha_ctx {
  * @flags: operation flags
  * @src_orig: original request sg list
  * @nbytes_orig: original request number of bytes
- * @src_chained: is source scatterlist chained
  * @src_nents: source number of entries
  * @byte_count: byte count
  * @count: save count in states during update, import and export
@@ -55,7 +54,6 @@ struct qce_sha_reqctx {
        unsigned long flags;
        struct scatterlist *src_orig;
        unsigned int nbytes_orig;
-       bool src_chained;
        int src_nents;
        __be32 byte_count[2];
        u64 count;
index 820dc3acb28ccfa3a008ccd9e121434ae9fba94d..f68c24a98277a0263c2fa88b79b17c408591a81c 100644 (file)
@@ -173,7 +173,6 @@ struct sahara_aes_reqctx {
  * @sg_in_idx: number of hw links
  * @in_sg: scatterlist for input data
  * @in_sg_chain: scatterlists for chained input data
- * @in_sg_chained: specifies if chained scatterlists are used or not
  * @total: total number of bytes for transfer
  * @last: is this the last block
  * @first: is this the first block
@@ -191,7 +190,6 @@ struct sahara_sha_reqctx {
        unsigned int            sg_in_idx;
        struct scatterlist      *in_sg;
        struct scatterlist      in_sg_chain[2];
-       bool                    in_sg_chained;
        size_t                  total;
        unsigned int            last;
        unsigned int            first;
@@ -274,31 +272,7 @@ static u32 sahara_aes_data_link_hdr(struct sahara_dev *dev)
                        SAHARA_HDR_CHA_SKHA | SAHARA_HDR_PARITY_BIT;
 }
 
-static int sahara_sg_length(struct scatterlist *sg,
-                           unsigned int total)
-{
-       int sg_nb;
-       unsigned int len;
-       struct scatterlist *sg_list;
-
-       sg_nb = 0;
-       sg_list = sg;
-
-       while (total) {
-               len = min(sg_list->length, total);
-
-               sg_nb++;
-               total -= len;
-
-               sg_list = sg_next(sg_list);
-               if (!sg_list)
-                       total = 0;
-       }
-
-       return sg_nb;
-}
-
-static char *sahara_err_src[16] = {
+static const char *sahara_err_src[16] = {
        "No error",
        "Header error",
        "Descriptor length error",
@@ -317,14 +291,14 @@ static char *sahara_err_src[16] = {
        "DMA error"
 };
 
-static char *sahara_err_dmasize[4] = {
+static const char *sahara_err_dmasize[4] = {
        "Byte transfer",
        "Half-word transfer",
        "Word transfer",
        "Reserved"
 };
 
-static char *sahara_err_dmasrc[8] = {
+static const char *sahara_err_dmasrc[8] = {
        "No error",
        "AHB bus error",
        "Internal IP bus error",
@@ -335,7 +309,7 @@ static char *sahara_err_dmasrc[8] = {
        "DMA HW error"
 };
 
-static char *sahara_cha_errsrc[12] = {
+static const char *sahara_cha_errsrc[12] = {
        "Input buffer non-empty",
        "Illegal address",
        "Illegal mode",
@@ -350,7 +324,7 @@ static char *sahara_cha_errsrc[12] = {
        "Reserved"
 };
 
-static char *sahara_cha_err[4] = { "No error", "SKHA", "MDHA", "RNG" };
+static const char *sahara_cha_err[4] = { "No error", "SKHA", "MDHA", "RNG" };
 
 static void sahara_decode_error(struct sahara_dev *dev, unsigned int error)
 {
@@ -380,7 +354,7 @@ static void sahara_decode_error(struct sahara_dev *dev, unsigned int error)
        dev_err(dev->device, "\n");
 }
 
-static char *sahara_state[4] = { "Idle", "Busy", "Error", "HW Fault" };
+static const char *sahara_state[4] = { "Idle", "Busy", "Error", "HW Fault" };
 
 static void sahara_decode_status(struct sahara_dev *dev, unsigned int status)
 {
@@ -502,8 +476,8 @@ static int sahara_hw_descriptor_create(struct sahara_dev *dev)
                idx++;
        }
 
-       dev->nb_in_sg = sahara_sg_length(dev->in_sg, dev->total);
-       dev->nb_out_sg = sahara_sg_length(dev->out_sg, dev->total);
+       dev->nb_in_sg = sg_nents_for_len(dev->in_sg, dev->total);
+       dev->nb_out_sg = sg_nents_for_len(dev->out_sg, dev->total);
        if ((dev->nb_in_sg + dev->nb_out_sg) > SAHARA_MAX_HW_LINK) {
                dev_err(dev->device, "not enough hw links (%d)\n",
                        dev->nb_in_sg + dev->nb_out_sg);
@@ -818,45 +792,26 @@ static int sahara_sha_hw_links_create(struct sahara_dev *dev,
 
        dev->in_sg = rctx->in_sg;
 
-       dev->nb_in_sg = sahara_sg_length(dev->in_sg, rctx->total);
+       dev->nb_in_sg = sg_nents_for_len(dev->in_sg, rctx->total);
        if ((dev->nb_in_sg) > SAHARA_MAX_HW_LINK) {
                dev_err(dev->device, "not enough hw links (%d)\n",
                        dev->nb_in_sg + dev->nb_out_sg);
                return -EINVAL;
        }
 
-       if (rctx->in_sg_chained) {
-               i = start;
-               sg = dev->in_sg;
-               while (sg) {
-                       ret = dma_map_sg(dev->device, sg, 1,
-                                        DMA_TO_DEVICE);
-                       if (!ret)
-                               return -EFAULT;
-
-                       dev->hw_link[i]->len = sg->length;
-                       dev->hw_link[i]->p = sg->dma_address;
+       sg = dev->in_sg;
+       ret = dma_map_sg(dev->device, dev->in_sg, dev->nb_in_sg, DMA_TO_DEVICE);
+       if (!ret)
+               return -EFAULT;
+
+       for (i = start; i < dev->nb_in_sg + start; i++) {
+               dev->hw_link[i]->len = sg->length;
+               dev->hw_link[i]->p = sg->dma_address;
+               if (i == (dev->nb_in_sg + start - 1)) {
+                       dev->hw_link[i]->next = 0;
+               } else {
                        dev->hw_link[i]->next = dev->hw_phys_link[i + 1];
                        sg = sg_next(sg);
-                       i += 1;
-               }
-               dev->hw_link[i-1]->next = 0;
-       } else {
-               sg = dev->in_sg;
-               ret = dma_map_sg(dev->device, dev->in_sg, dev->nb_in_sg,
-                                DMA_TO_DEVICE);
-               if (!ret)
-                       return -EFAULT;
-
-               for (i = start; i < dev->nb_in_sg + start; i++) {
-                       dev->hw_link[i]->len = sg->length;
-                       dev->hw_link[i]->p = sg->dma_address;
-                       if (i == (dev->nb_in_sg + start - 1)) {
-                               dev->hw_link[i]->next = 0;
-                       } else {
-                               dev->hw_link[i]->next = dev->hw_phys_link[i + 1];
-                               sg = sg_next(sg);
-                       }
                }
        }
 
@@ -1004,7 +959,6 @@ static int sahara_sha_prepare_request(struct ahash_request *req)
                rctx->total = req->nbytes + rctx->buf_cnt;
                rctx->in_sg = rctx->in_sg_chain;
 
-               rctx->in_sg_chained = true;
                req->src = rctx->in_sg_chain;
        /* only data from previous operation */
        } else if (rctx->buf_cnt) {
@@ -1015,13 +969,11 @@ static int sahara_sha_prepare_request(struct ahash_request *req)
                /* buf was copied into rembuf above */
                sg_init_one(rctx->in_sg, rctx->rembuf, rctx->buf_cnt);
                rctx->total = rctx->buf_cnt;
-               rctx->in_sg_chained = false;
        /* no data from previous operation */
        } else {
                rctx->in_sg = req->src;
                rctx->total = req->nbytes;
                req->src = rctx->in_sg;
-               rctx->in_sg_chained = false;
        }
 
        /* on next call, we only have the remaining data in the buffer */
@@ -1030,23 +982,6 @@ static int sahara_sha_prepare_request(struct ahash_request *req)
        return -EINPROGRESS;
 }
 
-static void sahara_sha_unmap_sg(struct sahara_dev *dev,
-                               struct sahara_sha_reqctx *rctx)
-{
-       struct scatterlist *sg;
-
-       if (rctx->in_sg_chained) {
-               sg = dev->in_sg;
-               while (sg) {
-                       dma_unmap_sg(dev->device, sg, 1, DMA_TO_DEVICE);
-                       sg = sg_next(sg);
-               }
-       } else {
-               dma_unmap_sg(dev->device, dev->in_sg, dev->nb_in_sg,
-                       DMA_TO_DEVICE);
-       }
-}
-
 static int sahara_sha_process(struct ahash_request *req)
 {
        struct sahara_dev *dev = dev_ptr;
@@ -1086,7 +1021,8 @@ static int sahara_sha_process(struct ahash_request *req)
        }
 
        if (rctx->sg_in_idx)
-               sahara_sha_unmap_sg(dev, rctx);
+               dma_unmap_sg(dev->device, dev->in_sg, dev->nb_in_sg,
+                            DMA_TO_DEVICE);
 
        memcpy(rctx->context, dev->context_base, rctx->context_size);
 
index 3b20a1bce703fdb7bf3725a9a3d5e1a741e34e76..46f531e19ccf07e97af05c221a5bfbb0d060d0a7 100644 (file)
@@ -857,8 +857,6 @@ badkey:
  * talitos_edesc - s/w-extended descriptor
  * @src_nents: number of segments in input scatterlist
  * @dst_nents: number of segments in output scatterlist
- * @src_chained: whether src is chained or not
- * @dst_chained: whether dst is chained or not
  * @icv_ool: whether ICV is out-of-line
  * @iv_dma: dma address of iv for checking continuity and link table
  * @dma_len: length of dma mapped link_tbl space
@@ -874,8 +872,6 @@ badkey:
 struct talitos_edesc {
        int src_nents;
        int dst_nents;
-       bool src_chained;
-       bool dst_chained;
        bool icv_ool;
        dma_addr_t iv_dma;
        int dma_len;
@@ -887,29 +883,6 @@ struct talitos_edesc {
        };
 };
 
-static int talitos_map_sg(struct device *dev, struct scatterlist *sg,
-                         unsigned int nents, enum dma_data_direction dir,
-                         bool chained)
-{
-       if (unlikely(chained))
-               while (sg) {
-                       dma_map_sg(dev, sg, 1, dir);
-                       sg = sg_next(sg);
-               }
-       else
-               dma_map_sg(dev, sg, nents, dir);
-       return nents;
-}
-
-static void talitos_unmap_sg_chain(struct device *dev, struct scatterlist *sg,
-                                  enum dma_data_direction dir)
-{
-       while (sg) {
-               dma_unmap_sg(dev, sg, 1, dir);
-               sg = sg_next(sg);
-       }
-}
-
 static void talitos_sg_unmap(struct device *dev,
                             struct talitos_edesc *edesc,
                             struct scatterlist *src,
@@ -919,24 +892,13 @@ static void talitos_sg_unmap(struct device *dev,
        unsigned int dst_nents = edesc->dst_nents ? : 1;
 
        if (src != dst) {
-               if (edesc->src_chained)
-                       talitos_unmap_sg_chain(dev, src, DMA_TO_DEVICE);
-               else
-                       dma_unmap_sg(dev, src, src_nents, DMA_TO_DEVICE);
+               dma_unmap_sg(dev, src, src_nents, DMA_TO_DEVICE);
 
                if (dst) {
-                       if (edesc->dst_chained)
-                               talitos_unmap_sg_chain(dev, dst,
-                                                      DMA_FROM_DEVICE);
-                       else
-                               dma_unmap_sg(dev, dst, dst_nents,
-                                            DMA_FROM_DEVICE);
+                       dma_unmap_sg(dev, dst, dst_nents, DMA_FROM_DEVICE);
                }
        } else
-               if (edesc->src_chained)
-                       talitos_unmap_sg_chain(dev, src, DMA_BIDIRECTIONAL);
-               else
-                       dma_unmap_sg(dev, src, src_nents, DMA_BIDIRECTIONAL);
+               dma_unmap_sg(dev, src, src_nents, DMA_BIDIRECTIONAL);
 }
 
 static void ipsec_esp_unmap(struct device *dev,
@@ -1118,10 +1080,9 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
        map_single_talitos_ptr(dev, &desc->ptr[0], ctx->authkeylen, &ctx->key,
                               DMA_TO_DEVICE);
 
-       sg_count = talitos_map_sg(dev, areq->src, edesc->src_nents ?: 1,
-                                 (areq->src == areq->dst) ? DMA_BIDIRECTIONAL
-                                                          : DMA_TO_DEVICE,
-                                 edesc->src_chained);
+       sg_count = dma_map_sg(dev, areq->src, edesc->src_nents ?: 1,
+                             (areq->src == areq->dst) ? DMA_BIDIRECTIONAL
+                                                          : DMA_TO_DEVICE);
 
        /* hmac data */
        desc->ptr[1].len = cpu_to_be16(areq->assoclen);
@@ -1185,9 +1146,8 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
        desc->ptr[5].j_extent = authsize;
 
        if (areq->src != areq->dst)
-               sg_count = talitos_map_sg(dev, areq->dst,
-                                         edesc->dst_nents ? : 1,
-                                         DMA_FROM_DEVICE, edesc->dst_chained);
+               sg_count = dma_map_sg(dev, areq->dst, edesc->dst_nents ? : 1,
+                                     DMA_FROM_DEVICE);
 
        edesc->icv_ool = false;
 
@@ -1233,26 +1193,6 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
        return ret;
 }
 
-/*
- * derive number of elements in scatterlist
- */
-static int sg_count(struct scatterlist *sg_list, int nbytes, bool *chained)
-{
-       struct scatterlist *sg = sg_list;
-       int sg_nents = 0;
-
-       *chained = false;
-       while (nbytes > 0 && sg) {
-               sg_nents++;
-               nbytes -= sg->length;
-               if (!sg_is_last(sg) && (sg + 1)->length == 0)
-                       *chained = true;
-               sg = sg_next(sg);
-       }
-
-       return sg_nents;
-}
-
 /*
  * allocate and map the extended descriptor
  */
@@ -1270,7 +1210,6 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
 {
        struct talitos_edesc *edesc;
        int src_nents, dst_nents, alloc_len, dma_len;
-       bool src_chained = false, dst_chained = false;
        dma_addr_t iv_dma = 0;
        gfp_t flags = cryptoflags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL :
                      GFP_ATOMIC;
@@ -1287,18 +1226,16 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
                iv_dma = dma_map_single(dev, iv, ivsize, DMA_TO_DEVICE);
 
        if (!dst || dst == src) {
-               src_nents = sg_count(src, assoclen + cryptlen + authsize,
-                                    &src_chained);
+               src_nents = sg_nents_for_len(src,
+                                            assoclen + cryptlen + authsize);
                src_nents = (src_nents == 1) ? 0 : src_nents;
                dst_nents = dst ? src_nents : 0;
        } else { /* dst && dst != src*/
-               src_nents = sg_count(src, assoclen + cryptlen +
-                                         (encrypt ? 0 : authsize),
-                                    &src_chained);
+               src_nents = sg_nents_for_len(src, assoclen + cryptlen +
+                                                (encrypt ? 0 : authsize));
                src_nents = (src_nents == 1) ? 0 : src_nents;
-               dst_nents = sg_count(dst, assoclen + cryptlen +
-                                         (encrypt ? authsize : 0),
-                                    &dst_chained);
+               dst_nents = sg_nents_for_len(dst, assoclen + cryptlen +
+                                                (encrypt ? authsize : 0));
                dst_nents = (dst_nents == 1) ? 0 : dst_nents;
        }
 
@@ -1332,8 +1269,6 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
 
        edesc->src_nents = src_nents;
        edesc->dst_nents = dst_nents;
-       edesc->src_chained = src_chained;
-       edesc->dst_chained = dst_chained;
        edesc->iv_dma = iv_dma;
        edesc->dma_len = dma_len;
        if (dma_len)
@@ -1518,8 +1453,7 @@ int map_sg_in_talitos_ptr(struct device *dev, struct scatterlist *src,
        } else {
                to_talitos_ptr_extent_clear(ptr, is_sec1);
 
-               sg_count = talitos_map_sg(dev, src, edesc->src_nents ? : 1, dir,
-                                         edesc->src_chained);
+               sg_count = dma_map_sg(dev, src, edesc->src_nents ? : 1, dir);
 
                if (sg_count == 1) {
                        to_talitos_ptr(ptr, sg_dma_address(src), is_sec1);
@@ -1552,8 +1486,7 @@ void map_sg_out_talitos_ptr(struct device *dev, struct scatterlist *dst,
        bool is_sec1 = has_ftr_sec1(priv);
 
        if (dir != DMA_NONE)
-               sg_count = talitos_map_sg(dev, dst, edesc->dst_nents ? : 1,
-                                         dir, edesc->dst_chained);
+               sg_count = dma_map_sg(dev, dst, edesc->dst_nents ? : 1, dir);
 
        to_talitos_ptr_len(ptr, len, is_sec1);
 
@@ -1897,12 +1830,11 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
        unsigned int nbytes_to_hash;
        unsigned int to_hash_later;
        unsigned int nsg;
-       bool chained;
 
        if (!req_ctx->last && (nbytes + req_ctx->nbuf <= blocksize)) {
                /* Buffer up to one whole block */
                sg_copy_to_buffer(areq->src,
-                                 sg_count(areq->src, nbytes, &chained),
+                                 sg_nents_for_len(areq->src, nbytes),
                                  req_ctx->buf + req_ctx->nbuf, nbytes);
                req_ctx->nbuf += nbytes;
                return 0;
@@ -1935,7 +1867,7 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
                req_ctx->psrc = areq->src;
 
        if (to_hash_later) {
-               int nents = sg_count(areq->src, nbytes, &chained);
+               int nents = sg_nents_for_len(areq->src, nbytes);
                sg_pcopy_to_buffer(areq->src, nents,
                                      req_ctx->bufnext,
                                      to_hash_later,
index fded0a5cfcd72ce5d8539ce5626b11e2bbee38d7..4c243c1ffc7f6d32224db88bb4c79d0810d9c87d 100644 (file)
@@ -1414,7 +1414,7 @@ static int ux500_cryp_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
 
        dev_dbg(dev, "[%s]", __func__);
-       device_data = kzalloc(sizeof(struct cryp_device_data), GFP_ATOMIC);
+       device_data = devm_kzalloc(dev, sizeof(*device_data), GFP_ATOMIC);
        if (!device_data) {
                dev_err(dev, "[%s]: kzalloc() failed!", __func__);
                ret = -ENOMEM;
@@ -1435,23 +1435,15 @@ static int ux500_cryp_probe(struct platform_device *pdev)
                dev_err(dev, "[%s]: platform_get_resource() failed",
                                __func__);
                ret = -ENODEV;
-               goto out_kfree;
-       }
-
-       res = request_mem_region(res->start, resource_size(res), pdev->name);
-       if (res == NULL) {
-               dev_err(dev, "[%s]: request_mem_region() failed",
-                               __func__);
-               ret = -EBUSY;
-               goto out_kfree;
+               goto out;
        }
 
        device_data->phybase = res->start;
-       device_data->base = ioremap(res->start, resource_size(res));
+       device_data->base = devm_ioremap_resource(dev, res);
        if (!device_data->base) {
                dev_err(dev, "[%s]: ioremap failed!", __func__);
                ret = -ENOMEM;
-               goto out_free_mem;
+               goto out;
        }
 
        spin_lock_init(&device_data->ctx_lock);
@@ -1463,11 +1455,11 @@ static int ux500_cryp_probe(struct platform_device *pdev)
                dev_err(dev, "[%s]: could not get cryp regulator", __func__);
                ret = PTR_ERR(device_data->pwr_regulator);
                device_data->pwr_regulator = NULL;
-               goto out_unmap;
+               goto out;
        }
 
        /* Enable the clk for CRYP hardware block */
-       device_data->clk = clk_get(&pdev->dev, NULL);
+       device_data->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(device_data->clk)) {
                dev_err(dev, "[%s]: clk_get() failed!", __func__);
                ret = PTR_ERR(device_data->clk);
@@ -1477,7 +1469,7 @@ static int ux500_cryp_probe(struct platform_device *pdev)
        ret = clk_prepare(device_data->clk);
        if (ret) {
                dev_err(dev, "[%s]: clk_prepare() failed!", __func__);
-               goto out_clk;
+               goto out_regulator;
        }
 
        /* Enable device power (and clock) */
@@ -1510,11 +1502,8 @@ static int ux500_cryp_probe(struct platform_device *pdev)
                goto out_power;
        }
 
-       ret = request_irq(res_irq->start,
-                         cryp_interrupt_handler,
-                         0,
-                         "cryp1",
-                         device_data);
+       ret = devm_request_irq(&pdev->dev, res_irq->start,
+                              cryp_interrupt_handler, 0, "cryp1", device_data);
        if (ret) {
                dev_err(dev, "[%s]: Unable to request IRQ", __func__);
                goto out_power;
@@ -1550,28 +1539,15 @@ out_power:
 out_clk_unprepare:
        clk_unprepare(device_data->clk);
 
-out_clk:
-       clk_put(device_data->clk);
-
 out_regulator:
        regulator_put(device_data->pwr_regulator);
 
-out_unmap:
-       iounmap(device_data->base);
-
-out_free_mem:
-       release_mem_region(res->start, resource_size(res));
-
-out_kfree:
-       kfree(device_data);
 out:
        return ret;
 }
 
 static int ux500_cryp_remove(struct platform_device *pdev)
 {
-       struct resource *res = NULL;
-       struct resource *res_irq = NULL;
        struct cryp_device_data *device_data;
 
        dev_dbg(&pdev->dev, "[%s]", __func__);
@@ -1607,37 +1583,18 @@ static int ux500_cryp_remove(struct platform_device *pdev)
        if (list_empty(&driver_data.device_list.k_list))
                cryp_algs_unregister_all();
 
-       res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!res_irq)
-               dev_err(&pdev->dev, "[%s]: IORESOURCE_IRQ, unavailable",
-                       __func__);
-       else {
-               disable_irq(res_irq->start);
-               free_irq(res_irq->start, device_data);
-       }
-
        if (cryp_disable_power(&pdev->dev, device_data, false))
                dev_err(&pdev->dev, "[%s]: cryp_disable_power() failed",
                        __func__);
 
        clk_unprepare(device_data->clk);
-       clk_put(device_data->clk);
        regulator_put(device_data->pwr_regulator);
 
-       iounmap(device_data->base);
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res)
-               release_mem_region(res->start, resource_size(res));
-
-       kfree(device_data);
-
        return 0;
 }
 
 static void ux500_cryp_shutdown(struct platform_device *pdev)
 {
-       struct resource *res_irq = NULL;
        struct cryp_device_data *device_data;
 
        dev_dbg(&pdev->dev, "[%s]", __func__);
@@ -1673,15 +1630,6 @@ static void ux500_cryp_shutdown(struct platform_device *pdev)
        if (list_empty(&driver_data.device_list.k_list))
                cryp_algs_unregister_all();
 
-       res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!res_irq)
-               dev_err(&pdev->dev, "[%s]: IORESOURCE_IRQ, unavailable",
-                       __func__);
-       else {
-               disable_irq(res_irq->start);
-               free_irq(res_irq->start, device_data);
-       }
-
        if (cryp_disable_power(&pdev->dev, device_data, false))
                dev_err(&pdev->dev, "[%s]: cryp_disable_power() failed",
                        __func__);
@@ -1777,6 +1725,7 @@ static const struct of_device_id ux500_cryp_match[] = {
        { .compatible = "stericsson,ux500-cryp" },
        { },
 };
+MODULE_DEVICE_TABLE(of, ux500_cryp_match);
 
 static struct platform_driver cryp_driver = {
        .probe  = ux500_cryp_probe,
index 5f5f360628fc443aace79138d0b63c7f267b9148..f47d112041b2173d0f25c0ce9069729000e04456 100644 (file)
@@ -1657,7 +1657,7 @@ static int ux500_hash_probe(struct platform_device *pdev)
        struct hash_device_data *device_data;
        struct device           *dev = &pdev->dev;
 
-       device_data = kzalloc(sizeof(*device_data), GFP_ATOMIC);
+       device_data = devm_kzalloc(dev, sizeof(*device_data), GFP_ATOMIC);
        if (!device_data) {
                ret = -ENOMEM;
                goto out;
@@ -1670,22 +1670,15 @@ static int ux500_hash_probe(struct platform_device *pdev)
        if (!res) {
                dev_dbg(dev, "%s: platform_get_resource() failed!\n", __func__);
                ret = -ENODEV;
-               goto out_kfree;
-       }
-
-       res = request_mem_region(res->start, resource_size(res), pdev->name);
-       if (res == NULL) {
-               dev_dbg(dev, "%s: request_mem_region() failed!\n", __func__);
-               ret = -EBUSY;
-               goto out_kfree;
+               goto out;
        }
 
        device_data->phybase = res->start;
-       device_data->base = ioremap(res->start, resource_size(res));
+       device_data->base = devm_ioremap_resource(dev, res);
        if (!device_data->base) {
                dev_err(dev, "%s: ioremap() failed!\n", __func__);
                ret = -ENOMEM;
-               goto out_free_mem;
+               goto out;
        }
        spin_lock_init(&device_data->ctx_lock);
        spin_lock_init(&device_data->power_state_lock);
@@ -1696,11 +1689,11 @@ static int ux500_hash_probe(struct platform_device *pdev)
                dev_err(dev, "%s: regulator_get() failed!\n", __func__);
                ret = PTR_ERR(device_data->regulator);
                device_data->regulator = NULL;
-               goto out_unmap;
+               goto out;
        }
 
        /* Enable the clock for HASH1 hardware block */
-       device_data->clk = clk_get(dev, NULL);
+       device_data->clk = devm_clk_get(dev, NULL);
        if (IS_ERR(device_data->clk)) {
                dev_err(dev, "%s: clk_get() failed!\n", __func__);
                ret = PTR_ERR(device_data->clk);
@@ -1710,7 +1703,7 @@ static int ux500_hash_probe(struct platform_device *pdev)
        ret = clk_prepare(device_data->clk);
        if (ret) {
                dev_err(dev, "%s: clk_prepare() failed!\n", __func__);
-               goto out_clk;
+               goto out_regulator;
        }
 
        /* Enable device power (and clock) */
@@ -1752,20 +1745,9 @@ out_power:
 out_clk_unprepare:
        clk_unprepare(device_data->clk);
 
-out_clk:
-       clk_put(device_data->clk);
-
 out_regulator:
        regulator_put(device_data->regulator);
 
-out_unmap:
-       iounmap(device_data->base);
-
-out_free_mem:
-       release_mem_region(res->start, resource_size(res));
-
-out_kfree:
-       kfree(device_data);
 out:
        return ret;
 }
@@ -1776,7 +1758,6 @@ out:
  */
 static int ux500_hash_remove(struct platform_device *pdev)
 {
-       struct resource         *res;
        struct hash_device_data *device_data;
        struct device           *dev = &pdev->dev;
 
@@ -1816,17 +1797,8 @@ static int ux500_hash_remove(struct platform_device *pdev)
                        __func__);
 
        clk_unprepare(device_data->clk);
-       clk_put(device_data->clk);
        regulator_put(device_data->regulator);
 
-       iounmap(device_data->base);
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res)
-               release_mem_region(res->start, resource_size(res));
-
-       kfree(device_data);
-
        return 0;
 }
 
@@ -1836,7 +1808,6 @@ static int ux500_hash_remove(struct platform_device *pdev)
  */
 static void ux500_hash_shutdown(struct platform_device *pdev)
 {
-       struct resource *res = NULL;
        struct hash_device_data *device_data;
 
        device_data = platform_get_drvdata(pdev);
@@ -1870,12 +1841,6 @@ static void ux500_hash_shutdown(struct platform_device *pdev)
        if (list_empty(&driver_data.device_list.k_list))
                ahash_algs_unregister_all(device_data);
 
-       iounmap(device_data->base);
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res)
-               release_mem_region(res->start, resource_size(res));
-
        if (hash_disable_power(device_data, false))
                dev_err(&pdev->dev, "%s: hash_disable_power() failed\n",
                        __func__);
@@ -1958,6 +1923,7 @@ static const struct of_device_id ux500_hash_match[] = {
        { .compatible = "stericsson,ux500-hash" },
        { },
 };
+MODULE_DEVICE_TABLE(of, ux500_hash_match);
 
 static struct platform_driver hash_driver = {
        .probe  = ux500_hash_probe,
index ae3c5f3ce405829fabc9e18e790778b8a5dcf3ea..dbf53e08bdd1a38261fc883bd812e881e4152fd8 100644 (file)
@@ -12,6 +12,8 @@ obj-$(CONFIG_EDAC_MM_EDAC)            += edac_core.o
 edac_core-y    := edac_mc.o edac_device.o edac_mc_sysfs.o
 edac_core-y    += edac_module.o edac_device_sysfs.o
 
+edac_core-$(CONFIG_EDAC_DEBUG)         += debugfs.o
+
 ifdef CONFIG_PCI
 edac_core-y    += edac_pci.o edac_pci_sysfs.o
 endif
index 23ef0917483cadb2c2380795d70c2714aae0ce76..929640981d8a845793ed4c005da23a7a26c0c32b 100644 (file)
@@ -51,11 +51,9 @@ static const struct altr_sdram_prv_data c5_data = {
        .ecc_irq_clr_mask   = (CV_DRAMINTR_INTRCLR | CV_DRAMINTR_INTREN),
        .ecc_cnt_rst_offset = CV_DRAMINTR_OFST,
        .ecc_cnt_rst_mask   = CV_DRAMINTR_INTRCLR,
-#ifdef CONFIG_EDAC_DEBUG
        .ce_ue_trgr_offset  = CV_CTLCFG_OFST,
        .ce_set_mask        = CV_CTLCFG_GEN_SB_ERR,
        .ue_set_mask        = CV_CTLCFG_GEN_DB_ERR,
-#endif
 };
 
 static const struct altr_sdram_prv_data a10_data = {
@@ -72,11 +70,9 @@ static const struct altr_sdram_prv_data a10_data = {
        .ecc_irq_clr_mask   = (A10_INTSTAT_SBEERR | A10_INTSTAT_DBEERR),
        .ecc_cnt_rst_offset = A10_ECCCTRL1_OFST,
        .ecc_cnt_rst_mask   = A10_ECC_CNT_RESET_MASK,
-#ifdef CONFIG_EDAC_DEBUG
        .ce_ue_trgr_offset  = A10_DIAGINTTEST_OFST,
        .ce_set_mask        = A10_DIAGINT_TSERRA_MASK,
        .ue_set_mask        = A10_DIAGINT_TDERRA_MASK,
-#endif
 };
 
 static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id)
@@ -116,7 +112,6 @@ static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id)
        return IRQ_NONE;
 }
 
-#ifdef CONFIG_EDAC_DEBUG
 static ssize_t altr_sdr_mc_err_inject_write(struct file *file,
                                            const char __user *data,
                                            size_t count, loff_t *ppos)
@@ -191,14 +186,15 @@ static const struct file_operations altr_sdr_mc_debug_inject_fops = {
 
 static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
 {
-       if (mci->debugfs)
-               debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
-                                   &altr_sdr_mc_debug_inject_fops);
+       if (!IS_ENABLED(CONFIG_EDAC_DEBUG))
+               return;
+
+       if (!mci->debugfs)
+               return;
+
+       edac_debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
+                                &altr_sdr_mc_debug_inject_fops);
 }
-#else
-static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
-{}
-#endif
 
 /* Get total memory size from Open Firmware DTB */
 static unsigned long get_total_mem(void)
index 7b64dc7c4eb7bc54f505416a2d1c7eb7c234d832..953077d3e4f3c3d43f45a5a415e20426123dabd2 100644 (file)
@@ -30,8 +30,7 @@
 #define CV_CTLCFG_GEN_SB_ERR       0x2000
 #define CV_CTLCFG_GEN_DB_ERR       0x4000
 
-#define CV_CTLCFG_ECC_AUTO_EN     (CV_CTLCFG_ECC_EN | \
-                                  CV_CTLCFG_ECC_CORR_EN)
+#define CV_CTLCFG_ECC_AUTO_EN     (CV_CTLCFG_ECC_EN)
 
 /* SDRAM Controller Address Width Register */
 #define CV_DRAMADDRW_OFST          0x2C
@@ -181,13 +180,11 @@ struct altr_sdram_prv_data {
        int ecc_irq_clr_mask;
        int ecc_cnt_rst_offset;
        int ecc_cnt_rst_mask;
-#ifdef CONFIG_EDAC_DEBUG
        struct edac_dev_sysfs_attribute *eccmgr_sysfs_attr;
        int ecc_enable_mask;
        int ce_set_mask;
        int ue_set_mask;
        int ce_ue_trgr_offset;
-#endif
 };
 
 /* Altera SDRAM Memory Controller data */
index 73aea40a9c89592035e08ecf98292dc9aab9df62..9eee13ef83a560fd0c54153c61bc01a77cd3eb7f 100644 (file)
@@ -173,7 +173,7 @@ static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct,
  * scan the scrub rate mapping table for a close or matching bandwidth value to
  * issue. If requested is too big, then use last maximum value found.
  */
-static int __set_scrub_rate(struct pci_dev *ctl, u32 new_bw, u32 min_rate)
+static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate)
 {
        u32 scrubval;
        int i;
@@ -201,7 +201,14 @@ static int __set_scrub_rate(struct pci_dev *ctl, u32 new_bw, u32 min_rate)
 
        scrubval = scrubrates[i].scrubval;
 
-       pci_write_bits32(ctl, SCRCTRL, scrubval, 0x001F);
+       if (pvt->fam == 0x15 && pvt->model == 0x60) {
+               f15h_select_dct(pvt, 0);
+               pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
+               f15h_select_dct(pvt, 1);
+               pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
+       } else {
+               pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F);
+       }
 
        if (scrubval)
                return scrubrates[i].bandwidth;
@@ -217,11 +224,15 @@ static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
        if (pvt->fam == 0xf)
                min_scrubrate = 0x0;
 
-       /* Erratum #505 */
-       if (pvt->fam == 0x15 && pvt->model < 0x10)
-               f15h_select_dct(pvt, 0);
+       if (pvt->fam == 0x15) {
+               /* Erratum #505 */
+               if (pvt->model < 0x10)
+                       f15h_select_dct(pvt, 0);
 
-       return __set_scrub_rate(pvt->F3, bw, min_scrubrate);
+               if (pvt->model == 0x60)
+                       min_scrubrate = 0x6;
+       }
+       return __set_scrub_rate(pvt, bw, min_scrubrate);
 }
 
 static int get_scrub_rate(struct mem_ctl_info *mci)
@@ -230,11 +241,15 @@ static int get_scrub_rate(struct mem_ctl_info *mci)
        u32 scrubval = 0;
        int i, retval = -EINVAL;
 
-       /* Erratum #505 */
-       if (pvt->fam == 0x15 && pvt->model < 0x10)
-               f15h_select_dct(pvt, 0);
+       if (pvt->fam == 0x15) {
+               /* Erratum #505 */
+               if (pvt->model < 0x10)
+                       f15h_select_dct(pvt, 0);
 
-       amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
+               if (pvt->model == 0x60)
+                       amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval);
+       } else
+               amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
 
        scrubval = scrubval & 0x001F;
 
@@ -2770,7 +2785,7 @@ static int init_one_instance(struct pci_dev *F2)
        struct mem_ctl_info *mci = NULL;
        struct edac_mc_layer layers[2];
        int err = 0, ret;
-       u16 nid = amd_get_node_id(F2);
+       u16 nid = amd_pci_dev_to_node_id(F2);
 
        ret = -ENOMEM;
        pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
@@ -2860,7 +2875,7 @@ err_ret:
 static int probe_one_instance(struct pci_dev *pdev,
                              const struct pci_device_id *mc_type)
 {
-       u16 nid = amd_get_node_id(pdev);
+       u16 nid = amd_pci_dev_to_node_id(pdev);
        struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
        struct ecc_settings *s;
        int ret = 0;
@@ -2910,7 +2925,7 @@ static void remove_one_instance(struct pci_dev *pdev)
 {
        struct mem_ctl_info *mci;
        struct amd64_pvt *pvt;
-       u16 nid = amd_get_node_id(pdev);
+       u16 nid = amd_pci_dev_to_node_id(pdev);
        struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
        struct ecc_settings *s = ecc_stngs[nid];
 
index 4bdec752d33096bfdfa9c5c201c81dba3c217af8..c0f248f3aaf976ef9f5ce3b79c20b9bf8b3b7cbd 100644 (file)
@@ -2,64 +2,10 @@
  * AMD64 class Memory Controller kernel module
  *
  * Copyright (c) 2009 SoftwareBitMaker.
- * Copyright (c) 2009 Advanced Micro Devices, Inc.
+ * Copyright (c) 2009-15 Advanced Micro Devices, Inc.
  *
  * This file may be distributed under the terms of the
  * GNU General Public License.
- *
- *     Originally Written by Thayne Harbaugh
- *
- *      Changes by Douglas "norsk" Thompson  <dougthompson@xmission.com>:
- *             - K8 CPU Revision D and greater support
- *
- *      Changes by Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>:
- *             - Module largely rewritten, with new (and hopefully correct)
- *             code for dealing with node and chip select interleaving,
- *             various code cleanup, and bug fixes
- *             - Added support for memory hoisting using DRAM hole address
- *             register
- *
- *     Changes by Douglas "norsk" Thompson <dougthompson@xmission.com>:
- *             -K8 Rev (1207) revision support added, required Revision
- *             specific mini-driver code to support Rev F as well as
- *             prior revisions
- *
- *     Changes by Douglas "norsk" Thompson <dougthompson@xmission.com>:
- *             -Family 10h revision support added. New PCI Device IDs,
- *             indicating new changes. Actual registers modified
- *             were slight, less than the Rev E to Rev F transition
- *             but changing the PCI Device ID was the proper thing to
- *             do, as it provides for almost automactic family
- *             detection. The mods to Rev F required more family
- *             information detection.
- *
- *     Changes/Fixes by Borislav Petkov <bp@alien8.de>:
- *             - misc fixes and code cleanups
- *
- * This module is based on the following documents
- * (available from http://www.amd.com/):
- *
- *     Title:  BIOS and Kernel Developer's Guide for AMD Athlon 64 and AMD
- *             Opteron Processors
- *     AMD publication #: 26094
- *`    Revision: 3.26
- *
- *     Title:  BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh
- *             Processors
- *     AMD publication #: 32559
- *     Revision: 3.00
- *     Issue Date: May 2006
- *
- *     Title:  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 10h
- *             Processors
- *     AMD publication #: 31116
- *     Revision: 3.00
- *     Issue Date: September 07, 2007
- *
- * Sections in the first 2 documents are no longer in sync with each other.
- * The Family 10h BKDG was totally re-written from scratch with a new
- * presentation model.
- * Therefore, comments that refer to a Document section might be off.
  */
 
 #include <linux/module.h>
 
 #define DCT_SEL_HI                     0x114
 
+#define F15H_M60H_SCRCTRL              0x1C8
+
 /*
  * Function 3 - Misc Control
  */
diff --git a/drivers/edac/debugfs.c b/drivers/edac/debugfs.c
new file mode 100644 (file)
index 0000000..54d2f66
--- /dev/null
@@ -0,0 +1,163 @@
+#include "edac_module.h"
+
+static struct dentry *edac_debugfs;
+
+static ssize_t edac_fake_inject_write(struct file *file,
+                                     const char __user *data,
+                                     size_t count, loff_t *ppos)
+{
+       struct device *dev = file->private_data;
+       struct mem_ctl_info *mci = to_mci(dev);
+       static enum hw_event_mc_err_type type;
+       u16 errcount = mci->fake_inject_count;
+
+       if (!errcount)
+               errcount = 1;
+
+       type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
+                                  : HW_EVENT_ERR_CORRECTED;
+
+       printk(KERN_DEBUG
+              "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
+               errcount,
+               (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
+               errcount > 1 ? "s" : "",
+               mci->fake_inject_layer[0],
+               mci->fake_inject_layer[1],
+               mci->fake_inject_layer[2]
+              );
+       edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
+                            mci->fake_inject_layer[0],
+                            mci->fake_inject_layer[1],
+                            mci->fake_inject_layer[2],
+                            "FAKE ERROR", "for EDAC testing only");
+
+       return count;
+}
+
+static const struct file_operations debug_fake_inject_fops = {
+       .open = simple_open,
+       .write = edac_fake_inject_write,
+       .llseek = generic_file_llseek,
+};
+
+int __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)
+{
+       debugfs_remove(edac_debugfs);
+}
+
+int edac_create_debugfs_nodes(struct mem_ctl_info *mci)
+{
+       struct dentry *d, *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;
+
+       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;
+       }
+
+       d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
+                               &mci->fake_inject_ue);
+       if (!d)
+               goto nomem;
+
+       d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
+                               &mci->fake_inject_count);
+       if (!d)
+               goto nomem;
+
+       d = debugfs_create_file("fake_inject", S_IWUSR, parent,
+                               &mci->dev,
+                               &debug_fake_inject_fops);
+       if (!d)
+               goto nomem;
+
+       mci->debugfs = parent;
+       return 0;
+nomem:
+       edac_debugfs_remove_recursive(mci->debugfs);
+       return -ENOMEM;
+}
+
+/* Create a toplevel dir under EDAC's debugfs hierarchy */
+struct dentry *edac_debugfs_create_dir(const char *dirname)
+{
+       if (!edac_debugfs)
+               return NULL;
+
+       return debugfs_create_dir(dirname, edac_debugfs);
+}
+EXPORT_SYMBOL_GPL(edac_debugfs_create_dir);
+
+/* Create a toplevel dir under EDAC's debugfs hierarchy with parent @parent */
+struct dentry *
+edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent)
+{
+       return debugfs_create_dir(dirname, parent);
+}
+EXPORT_SYMBOL_GPL(edac_debugfs_create_dir_at);
+
+/*
+ * Create a file under EDAC's hierarchy or a sub-hierarchy:
+ *
+ * @name: file name
+ * @mode: file permissions
+ * @parent: parent dentry. If NULL, it becomes the toplevel EDAC dir
+ * @data: private data of caller
+ * @fops: file operations of this file
+ */
+struct dentry *
+edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent,
+                        void *data, const struct file_operations *fops)
+{
+       if (!parent)
+               parent = edac_debugfs;
+
+       return debugfs_create_file(name, mode, parent, data, fops);
+}
+EXPORT_SYMBOL_GPL(edac_debugfs_create_file);
+
+/* Wrapper for debugfs_create_x8() */
+struct dentry *edac_debugfs_create_x8(const char *name, umode_t mode,
+                                      struct dentry *parent, u8 *value)
+{
+       if (!parent)
+               parent = edac_debugfs;
+
+       return debugfs_create_x8(name, mode, parent, value);
+}
+EXPORT_SYMBOL_GPL(edac_debugfs_create_x8);
+
+/* Wrapper for debugfs_create_x16() */
+struct dentry *edac_debugfs_create_x16(const char *name, umode_t mode,
+                                      struct dentry *parent, u16 *value)
+{
+       if (!parent)
+               parent = edac_debugfs;
+
+       return debugfs_create_x16(name, mode, parent, value);
+}
+EXPORT_SYMBOL_GPL(edac_debugfs_create_x16);
index ad42587c3f4d6e60d25ccb49fd9550b3341e6984..4861542163d704fe4f629777b04df791b5c790ea 100644 (file)
@@ -94,6 +94,8 @@ do {                                                                  \
 
 #define edac_dev_name(dev) (dev)->dev_name
 
+#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
+
 /*
  * The following are the structures to provide for a generic
  * or abstract 'edac_device'. This set of structures and the
index 943ed8cf71b946c8dfad8b33b2ee97711443e3ba..77ecd6a4179aaa2e2b6504da395debe6351508d9 100644 (file)
@@ -1302,7 +1302,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
        grain_bits = fls_long(e->grain) + 1;
        trace_mc_event(type, e->msg, e->label, e->error_count,
                       mci->mc_idx, e->top_layer, e->mid_layer, e->low_layer,
-                      PAGES_TO_MiB(e->page_frame_number) | e->offset_in_page,
+                      (e->page_frame_number << PAGE_SHIFT) | e->offset_in_page,
                       grain_bits, e->syndrome, e->other_detail);
 
        edac_raw_mc_handle_error(type, mci, e);
index 33df7d93c857b6fcd65b4b136e3e7eaf0cf1ea62..a75acea0f674ed7ca9101ca7ce8a65e5b0961905 100644 (file)
@@ -229,7 +229,7 @@ static ssize_t channel_dimm_label_show(struct device *dev,
        if (!rank->dimm->label[0])
                return 0;
 
-       return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n",
+       return snprintf(data, sizeof(rank->dimm->label) + 1, "%s\n",
                        rank->dimm->label);
 }
 
@@ -240,14 +240,21 @@ static ssize_t channel_dimm_label_store(struct device *dev,
        struct csrow_info *csrow = to_csrow(dev);
        unsigned chan = to_channel(mattr);
        struct rank_info *rank = csrow->channels[chan];
+       size_t copy_count = count;
 
-       ssize_t max_size = 0;
+       if (count == 0)
+               return -EINVAL;
+
+       if (data[count - 1] == '\0' || data[count - 1] == '\n')
+               copy_count -= 1;
+
+       if (copy_count == 0 || copy_count >= sizeof(rank->dimm->label))
+               return -EINVAL;
 
-       max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
-       strncpy(rank->dimm->label, data, max_size);
-       rank->dimm->label[max_size] = '\0';
+       strncpy(rank->dimm->label, data, copy_count);
+       rank->dimm->label[copy_count] = '\0';
 
-       return max_size;
+       return count;
 }
 
 /* show function for dynamic chX_ce_count attribute */
@@ -485,7 +492,7 @@ static ssize_t dimmdev_label_show(struct device *dev,
        if (!dimm->label[0])
                return 0;
 
-       return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", dimm->label);
+       return snprintf(data, sizeof(dimm->label) + 1, "%s\n", dimm->label);
 }
 
 static ssize_t dimmdev_label_store(struct device *dev,
@@ -494,14 +501,21 @@ static ssize_t dimmdev_label_store(struct device *dev,
                                   size_t count)
 {
        struct dimm_info *dimm = to_dimm(dev);
+       size_t copy_count = count;
 
-       ssize_t max_size = 0;
+       if (count == 0)
+               return -EINVAL;
 
-       max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
-       strncpy(dimm->label, data, max_size);
-       dimm->label[max_size] = '\0';
+       if (data[count - 1] == '\0' || data[count - 1] == '\n')
+               copy_count -= 1;
 
-       return max_size;
+       if (copy_count == 0 || copy_count >= sizeof(dimm->label))
+               return -EINVAL;
+
+       strncpy(dimm->label, data, copy_count);
+       dimm->label[copy_count] = '\0';
+
+       return count;
 }
 
 static ssize_t dimmdev_size_show(struct device *dev,
@@ -785,47 +799,6 @@ static ssize_t mci_max_location_show(struct device *dev,
        return p - data;
 }
 
-#ifdef CONFIG_EDAC_DEBUG
-static ssize_t edac_fake_inject_write(struct file *file,
-                                     const char __user *data,
-                                     size_t count, loff_t *ppos)
-{
-       struct device *dev = file->private_data;
-       struct mem_ctl_info *mci = to_mci(dev);
-       static enum hw_event_mc_err_type type;
-       u16 errcount = mci->fake_inject_count;
-
-       if (!errcount)
-               errcount = 1;
-
-       type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
-                                  : HW_EVENT_ERR_CORRECTED;
-
-       printk(KERN_DEBUG
-              "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
-               errcount,
-               (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
-               errcount > 1 ? "s" : "",
-               mci->fake_inject_layer[0],
-               mci->fake_inject_layer[1],
-               mci->fake_inject_layer[2]
-              );
-       edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
-                            mci->fake_inject_layer[0],
-                            mci->fake_inject_layer[1],
-                            mci->fake_inject_layer[2],
-                            "FAKE ERROR", "for EDAC testing only");
-
-       return count;
-}
-
-static const struct file_operations debug_fake_inject_fops = {
-       .open = simple_open,
-       .write = edac_fake_inject_write,
-       .llseek = generic_file_llseek,
-};
-#endif
-
 /* default Control file */
 static DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
 
@@ -896,71 +869,6 @@ static struct device_type mci_attr_type = {
        .release        = mci_attr_release,
 };
 
-#ifdef CONFIG_EDAC_DEBUG
-static struct dentry *edac_debugfs;
-
-int __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)
-{
-       debugfs_remove(edac_debugfs);
-}
-
-static int edac_create_debug_nodes(struct mem_ctl_info *mci)
-{
-       struct dentry *d, *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;
-
-       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;
-       }
-
-       d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
-                               &mci->fake_inject_ue);
-       if (!d)
-               goto nomem;
-
-       d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
-                               &mci->fake_inject_count);
-       if (!d)
-               goto nomem;
-
-       d = debugfs_create_file("fake_inject", S_IWUSR, parent,
-                               &mci->dev,
-                               &debug_fake_inject_fops);
-       if (!d)
-               goto nomem;
-
-       mci->debugfs = parent;
-       return 0;
-nomem:
-       debugfs_remove(mci->debugfs);
-       return -ENOMEM;
-}
-#endif
-
 /*
  * Create a new Memory Controller kobject instance,
  *     mc<id> under the 'mc' directory
@@ -1039,9 +947,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
                goto fail_unregister_dimm;
 #endif
 
-#ifdef CONFIG_EDAC_DEBUG
-       edac_create_debug_nodes(mci);
-#endif
+       edac_create_debugfs_nodes(mci);
        return 0;
 
 fail_unregister_dimm:
@@ -1070,7 +976,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
        edac_dbg(0, "\n");
 
 #ifdef CONFIG_EDAC_DEBUG
-       debugfs_remove(mci->debugfs);
+       edac_debugfs_remove_recursive(mci->debugfs);
 #endif
 #ifdef CONFIG_EDAC_LEGACY_SYSFS
        edac_delete_csrow_objects(mci);
index 26ecc52e073d8b5dc246aa2897cd59fdf54700ad..b95a48fc723d587dce62116f330b46034fc3ecf1 100644 (file)
@@ -60,15 +60,39 @@ extern void *edac_align_ptr(void **p, unsigned size, int n_elems);
 /*
  * EDAC debugfs functions
  */
+
+#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_exit(void);
+int 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);
+struct dentry *
+edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent,
+                        void *data, const struct file_operations *fops);
+struct dentry *
+edac_debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent, u8 *value);
+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_exit(void) {}
+static inline int edac_debugfs_init(void)                                      { return -ENODEV; }
+static inline void edac_debugfs_exit(void)                                     { }
+static inline int edac_create_debugfs_nodes(struct mem_ctl_info *mci)          { return 0; }
+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; }
+static inline struct dentry *
+edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent,
+                        void *data, const struct file_operations *fops)        { return NULL; }
+static inline struct dentry *
+edac_debugfs_create_x8(const char *name, umode_t mode,
+                      struct dentry *parent, u8 *value)                        { return NULL; }
+static inline struct dentry *
+edac_debugfs_create_x16(const char *name, umode_t mode,
+                      struct dentry *parent, u16 *value)                       { return NULL; }
 #endif
 
 /*
index b24681998740033664331ed4ba5d8ced56af63d0..e3fa4390f8460685c0bd57fe8d52c9c043de719f 100644 (file)
@@ -66,26 +66,6 @@ struct ghes_edac_dimm_fill {
        unsigned count;
 };
 
-char *memory_type[] = {
-       [MEM_EMPTY] = "EMPTY",
-       [MEM_RESERVED] = "RESERVED",
-       [MEM_UNKNOWN] = "UNKNOWN",
-       [MEM_FPM] = "FPM",
-       [MEM_EDO] = "EDO",
-       [MEM_BEDO] = "BEDO",
-       [MEM_SDR] = "SDR",
-       [MEM_RDR] = "RDR",
-       [MEM_DDR] = "DDR",
-       [MEM_RDDR] = "RDDR",
-       [MEM_RMBS] = "RMBS",
-       [MEM_DDR2] = "DDR2",
-       [MEM_FB_DDR2] = "FB_DDR2",
-       [MEM_RDDR2] = "RDDR2",
-       [MEM_XDR] = "XDR",
-       [MEM_DDR3] = "DDR3",
-       [MEM_RDDR3] = "RDDR3",
-};
-
 static void ghes_edac_count_dimms(const struct dmi_header *dh, void *arg)
 {
        int *num_dimm = arg;
@@ -173,7 +153,7 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
 
                if (dimm->nr_pages) {
                        edac_dbg(1, "DIMM%i: %s size = %d MB%s\n",
-                               dimm_fill->count, memory_type[dimm->mtype],
+                               dimm_fill->count, edac_mem_types[dimm->mtype],
                                PAGES_TO_MiB(dimm->nr_pages),
                                (dimm->edac_mode != EDAC_NONE) ? "(ECC)" : "");
                        edac_dbg(2, "\ttype %d, detail 0x%02x, width %d(total %d)\n",
@@ -417,7 +397,7 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
                 "APEI location: %s %s", e->location, e->other_detail);
        trace_mc_event(type, e->msg, e->label, e->error_count,
                       mci->mc_idx, e->top_layer, e->mid_layer, e->low_layer,
-                      PAGES_TO_MiB(e->page_frame_number) | e->offset_in_page,
+                      (e->page_frame_number << PAGE_SHIFT) | e->offset_in_page,
                       grain_bits, e->syndrome, pvt->detail_location);
 
        /* Report the error via EDAC API */
index e9f8a393915a82f54b3c7c63920ccf29244b8e17..40917775dca1c84fa2f84acbea9a988ec94060e3 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/debugfs.h>
 
 #include "edac_core.h"
+#include "edac_module.h"
 
 /* register addresses */
 
@@ -966,25 +967,25 @@ static int i5100_setup_debugfs(struct mem_ctl_info *mci)
        if (!i5100_debugfs)
                return -ENODEV;
 
-       priv->debugfs = debugfs_create_dir(mci->bus->name, i5100_debugfs);
+       priv->debugfs = edac_debugfs_create_dir_at(mci->bus->name, i5100_debugfs);
 
        if (!priv->debugfs)
                return -ENOMEM;
 
-       debugfs_create_x8("inject_channel", S_IRUGO | S_IWUSR, priv->debugfs,
-                       &priv->inject_channel);
-       debugfs_create_x8("inject_hlinesel", S_IRUGO | S_IWUSR, priv->debugfs,
-                       &priv->inject_hlinesel);
-       debugfs_create_x8("inject_deviceptr1", S_IRUGO | S_IWUSR, priv->debugfs,
-                       &priv->inject_deviceptr1);
-       debugfs_create_x8("inject_deviceptr2", S_IRUGO | S_IWUSR, priv->debugfs,
-                       &priv->inject_deviceptr2);
-       debugfs_create_x16("inject_eccmask1", S_IRUGO | S_IWUSR, priv->debugfs,
-                       &priv->inject_eccmask1);
-       debugfs_create_x16("inject_eccmask2", S_IRUGO | S_IWUSR, priv->debugfs,
-                       &priv->inject_eccmask2);
-       debugfs_create_file("inject_enable", S_IWUSR, priv->debugfs,
-                       &mci->dev, &i5100_inject_enable_fops);
+       edac_debugfs_create_x8("inject_channel", S_IRUGO | S_IWUSR, priv->debugfs,
+                               &priv->inject_channel);
+       edac_debugfs_create_x8("inject_hlinesel", S_IRUGO | S_IWUSR, priv->debugfs,
+                               &priv->inject_hlinesel);
+       edac_debugfs_create_x8("inject_deviceptr1", S_IRUGO | S_IWUSR, priv->debugfs,
+                               &priv->inject_deviceptr1);
+       edac_debugfs_create_x8("inject_deviceptr2", S_IRUGO | S_IWUSR, priv->debugfs,
+                               &priv->inject_deviceptr2);
+       edac_debugfs_create_x16("inject_eccmask1", S_IRUGO | S_IWUSR, priv->debugfs,
+                               &priv->inject_eccmask1);
+       edac_debugfs_create_x16("inject_eccmask2", S_IRUGO | S_IWUSR, priv->debugfs,
+                               &priv->inject_eccmask2);
+       edac_debugfs_create_file("inject_enable", S_IWUSR, priv->debugfs,
+                               &mci->dev, &i5100_inject_enable_fops);
 
        return 0;
 
@@ -1189,7 +1190,7 @@ static void i5100_remove_one(struct pci_dev *pdev)
 
        priv = mci->pvt_info;
 
-       debugfs_remove_recursive(priv->debugfs);
+       edac_debugfs_remove_recursive(priv->debugfs);
 
        priv->scrub_enable = 0;
        cancel_delayed_work_sync(&(priv->i5100_scrubbing));
@@ -1223,7 +1224,7 @@ static int __init i5100_init(void)
 {
        int pci_rc;
 
-       i5100_debugfs = debugfs_create_dir("i5100_edac", NULL);
+       i5100_debugfs = edac_debugfs_create_dir_at("i5100_edac", NULL);
 
        pci_rc = pci_register_driver(&i5100_driver);
        return (pci_rc < 0) ? pci_rc : 0;
@@ -1231,7 +1232,7 @@ static int __init i5100_init(void)
 
 static void __exit i5100_exit(void)
 {
-       debugfs_remove(i5100_debugfs);
+       edac_debugfs_remove(i5100_debugfs);
 
        pci_unregister_driver(&i5100_driver);
 }
index 711d8ad74f116ebdcc7fd3833fbc0672c7a6359b..d3a64ba61fa3075beab3001d9270c572c8af9c9c 100644 (file)
@@ -199,6 +199,7 @@ static const struct of_device_id ppc4xx_edac_match[] = {
        },
        { }
 };
+MODULE_DEVICE_TABLE(of, ppc4xx_edac_match);
 
 static struct platform_driver ppc4xx_edac_driver = {
        .probe                  = ppc4xx_edac_probe,
index cf1268ddef0c058982ff45fb0bb0074a06d2f6b7..429309c62699ff9ea536a90b839dbb99b7305ecc 100644 (file)
@@ -1688,6 +1688,7 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci,
 {
        struct sbridge_pvt *pvt = mci->pvt_info;
        struct pci_dev *pdev;
+       u8 saw_chan_mask = 0;
        int i;
 
        for (i = 0; i < sbridge_dev->n_devs; i++) {
@@ -1721,6 +1722,7 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci,
                {
                        int id = pdev->device - PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0;
                        pvt->pci_tad[id] = pdev;
+                       saw_chan_mask |= 1 << id;
                }
                        break;
                case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO:
@@ -1741,10 +1743,8 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci,
            !pvt-> pci_tad || !pvt->pci_ras  || !pvt->pci_ta)
                goto enodev;
 
-       for (i = 0; i < NUM_CHANNELS; i++) {
-               if (!pvt->pci_tad[i])
-                       goto enodev;
-       }
+       if (saw_chan_mask != 0x0f)
+               goto enodev;
        return 0;
 
 enodev:
index ba06904af2e1c2dc9f574caa8d82c4478f3c42df..41f876414a18d759ee2d89c33b5a4791ddcd6f43 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/regmap.h>
 
 #include "edac_core.h"
+#include "edac_module.h"
 
 #define EDAC_MOD_STR                   "xgene_edac"
 
@@ -62,10 +63,12 @@ struct xgene_edac {
        struct regmap           *efuse_map;
        void __iomem            *pcp_csr;
        spinlock_t              lock;
-       struct dentry           *dfs;
+       struct dentry           *dfs;
 
        struct list_head        mcus;
        struct list_head        pmds;
+       struct list_head        l3s;
+       struct list_head        socs;
 
        struct mutex            mc_lock;
        int                     mc_active_mask;
@@ -172,12 +175,12 @@ static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
 {
        if (!IS_ENABLED(CONFIG_EDAC_DEBUG))
                return;
-#ifdef CONFIG_EDAC_DEBUG
+
        if (!mci->debugfs)
                return;
-       debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
-                           &xgene_edac_mc_debug_inject_fops);
-#endif
+
+       edac_debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
+                                &xgene_edac_mc_debug_inject_fops);
 }
 
 static void xgene_edac_mc_check(struct mem_ctl_info *mci)
@@ -536,140 +539,134 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
        pg_f = ctx->pmd_csr + cpu_idx * CPU_CSR_STRIDE + CPU_MEMERR_CPU_PAGE;
 
        val = readl(pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET);
-       if (val) {
-               dev_err(edac_dev->dev,
-                       "CPU%d L1 memory error ICF 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
-                       ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
-                       MEMERR_CPU_ICFESR_ERRWAY_RD(val),
-                       MEMERR_CPU_ICFESR_ERRINDEX_RD(val),
-                       MEMERR_CPU_ICFESR_ERRINFO_RD(val));
-               if (val & MEMERR_CPU_ICFESR_CERR_MASK)
-                       dev_err(edac_dev->dev,
-                               "One or more correctable error\n");
-               if (val & MEMERR_CPU_ICFESR_MULTCERR_MASK)
-                       dev_err(edac_dev->dev, "Multiple correctable error\n");
-               switch (MEMERR_CPU_ICFESR_ERRTYPE_RD(val)) {
-               case 1:
-                       dev_err(edac_dev->dev, "L1 TLB multiple hit\n");
-                       break;
-               case 2:
-                       dev_err(edac_dev->dev, "Way select multiple hit\n");
-                       break;
-               case 3:
-                       dev_err(edac_dev->dev, "Physical tag parity error\n");
-                       break;
-               case 4:
-               case 5:
-                       dev_err(edac_dev->dev, "L1 data parity error\n");
-                       break;
-               case 6:
-                       dev_err(edac_dev->dev, "L1 pre-decode parity error\n");
-                       break;
-               }
+       if (!val)
+               goto chk_lsu;
+       dev_err(edac_dev->dev,
+               "CPU%d L1 memory error ICF 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
+               ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
+               MEMERR_CPU_ICFESR_ERRWAY_RD(val),
+               MEMERR_CPU_ICFESR_ERRINDEX_RD(val),
+               MEMERR_CPU_ICFESR_ERRINFO_RD(val));
+       if (val & MEMERR_CPU_ICFESR_CERR_MASK)
+               dev_err(edac_dev->dev, "One or more correctable error\n");
+       if (val & MEMERR_CPU_ICFESR_MULTCERR_MASK)
+               dev_err(edac_dev->dev, "Multiple correctable error\n");
+       switch (MEMERR_CPU_ICFESR_ERRTYPE_RD(val)) {
+       case 1:
+               dev_err(edac_dev->dev, "L1 TLB multiple hit\n");
+               break;
+       case 2:
+               dev_err(edac_dev->dev, "Way select multiple hit\n");
+               break;
+       case 3:
+               dev_err(edac_dev->dev, "Physical tag parity error\n");
+               break;
+       case 4:
+       case 5:
+               dev_err(edac_dev->dev, "L1 data parity error\n");
+               break;
+       case 6:
+               dev_err(edac_dev->dev, "L1 pre-decode parity error\n");
+               break;
+       }
 
-               /* Clear any HW errors */
-               writel(val, pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET);
+       /* Clear any HW errors */
+       writel(val, pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET);
 
-               if (val & (MEMERR_CPU_ICFESR_CERR_MASK |
-                          MEMERR_CPU_ICFESR_MULTCERR_MASK))
-                       edac_device_handle_ce(edac_dev, 0, 0,
-                                             edac_dev->ctl_name);
-       }
+       if (val & (MEMERR_CPU_ICFESR_CERR_MASK |
+                  MEMERR_CPU_ICFESR_MULTCERR_MASK))
+               edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
 
+chk_lsu:
        val = readl(pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET);
-       if (val) {
+       if (!val)
+               goto chk_mmu;
+       dev_err(edac_dev->dev,
+               "CPU%d memory error LSU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
+               ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
+               MEMERR_CPU_LSUESR_ERRWAY_RD(val),
+               MEMERR_CPU_LSUESR_ERRINDEX_RD(val),
+               MEMERR_CPU_LSUESR_ERRINFO_RD(val));
+       if (val & MEMERR_CPU_LSUESR_CERR_MASK)
+               dev_err(edac_dev->dev, "One or more correctable error\n");
+       if (val & MEMERR_CPU_LSUESR_MULTCERR_MASK)
+               dev_err(edac_dev->dev, "Multiple correctable error\n");
+       switch (MEMERR_CPU_LSUESR_ERRTYPE_RD(val)) {
+       case 0:
+               dev_err(edac_dev->dev, "Load tag error\n");
+               break;
+       case 1:
+               dev_err(edac_dev->dev, "Load data error\n");
+               break;
+       case 2:
+               dev_err(edac_dev->dev, "WSL multihit error\n");
+               break;
+       case 3:
+               dev_err(edac_dev->dev, "Store tag error\n");
+               break;
+       case 4:
                dev_err(edac_dev->dev,
-                       "CPU%d memory error LSU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
-                       ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
-                       MEMERR_CPU_LSUESR_ERRWAY_RD(val),
-                       MEMERR_CPU_LSUESR_ERRINDEX_RD(val),
-                       MEMERR_CPU_LSUESR_ERRINFO_RD(val));
-               if (val & MEMERR_CPU_LSUESR_CERR_MASK)
-                       dev_err(edac_dev->dev,
-                               "One or more correctable error\n");
-               if (val & MEMERR_CPU_LSUESR_MULTCERR_MASK)
-                       dev_err(edac_dev->dev, "Multiple correctable error\n");
-               switch (MEMERR_CPU_LSUESR_ERRTYPE_RD(val)) {
-               case 0:
-                       dev_err(edac_dev->dev, "Load tag error\n");
-                       break;
-               case 1:
-                       dev_err(edac_dev->dev, "Load data error\n");
-                       break;
-               case 2:
-                       dev_err(edac_dev->dev, "WSL multihit error\n");
-                       break;
-               case 3:
-                       dev_err(edac_dev->dev, "Store tag error\n");
-                       break;
-               case 4:
-                       dev_err(edac_dev->dev,
-                               "DTB multihit from load pipeline error\n");
-                       break;
-               case 5:
-                       dev_err(edac_dev->dev,
-                               "DTB multihit from store pipeline error\n");
-                       break;
-               }
+                       "DTB multihit from load pipeline error\n");
+               break;
+       case 5:
+               dev_err(edac_dev->dev,
+                       "DTB multihit from store pipeline error\n");
+               break;
+       }
 
-               /* Clear any HW errors */
-               writel(val, pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET);
+       /* Clear any HW errors */
+       writel(val, pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET);
 
-               if (val & (MEMERR_CPU_LSUESR_CERR_MASK |
-                          MEMERR_CPU_LSUESR_MULTCERR_MASK))
-                       edac_device_handle_ce(edac_dev, 0, 0,
-                                             edac_dev->ctl_name);
-       }
+       if (val & (MEMERR_CPU_LSUESR_CERR_MASK |
+                  MEMERR_CPU_LSUESR_MULTCERR_MASK))
+               edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
 
+chk_mmu:
        val = readl(pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET);
-       if (val) {
-               dev_err(edac_dev->dev,
-                       "CPU%d memory error MMU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X %s\n",
-                       ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
-                       MEMERR_CPU_MMUESR_ERRWAY_RD(val),
-                       MEMERR_CPU_MMUESR_ERRINDEX_RD(val),
-                       MEMERR_CPU_MMUESR_ERRINFO_RD(val),
-                       val & MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK ? "LSU" :
-                                                                    "ICF");
-               if (val & MEMERR_CPU_MMUESR_CERR_MASK)
-                       dev_err(edac_dev->dev,
-                               "One or more correctable error\n");
-               if (val & MEMERR_CPU_MMUESR_MULTCERR_MASK)
-                       dev_err(edac_dev->dev, "Multiple correctable error\n");
-               switch (MEMERR_CPU_MMUESR_ERRTYPE_RD(val)) {
-               case 0:
-                       dev_err(edac_dev->dev, "Stage 1 UTB hit error\n");
-                       break;
-               case 1:
-                       dev_err(edac_dev->dev, "Stage 1 UTB miss error\n");
-                       break;
-               case 2:
-                       dev_err(edac_dev->dev, "Stage 1 UTB allocate error\n");
-                       break;
-               case 3:
-                       dev_err(edac_dev->dev,
-                               "TMO operation single bank error\n");
-                       break;
-               case 4:
-                       dev_err(edac_dev->dev, "Stage 2 UTB error\n");
-                       break;
-               case 5:
-                       dev_err(edac_dev->dev, "Stage 2 UTB miss error\n");
-                       break;
-               case 6:
-                       dev_err(edac_dev->dev, "Stage 2 UTB allocate error\n");
-                       break;
-               case 7:
-                       dev_err(edac_dev->dev,
-                               "TMO operation multiple bank error\n");
-                       break;
-               }
+       if (!val)
+               return;
+       dev_err(edac_dev->dev,
+               "CPU%d memory error MMU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X %s\n",
+               ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
+               MEMERR_CPU_MMUESR_ERRWAY_RD(val),
+               MEMERR_CPU_MMUESR_ERRINDEX_RD(val),
+               MEMERR_CPU_MMUESR_ERRINFO_RD(val),
+               val & MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK ? "LSU" : "ICF");
+       if (val & MEMERR_CPU_MMUESR_CERR_MASK)
+               dev_err(edac_dev->dev, "One or more correctable error\n");
+       if (val & MEMERR_CPU_MMUESR_MULTCERR_MASK)
+               dev_err(edac_dev->dev, "Multiple correctable error\n");
+       switch (MEMERR_CPU_MMUESR_ERRTYPE_RD(val)) {
+       case 0:
+               dev_err(edac_dev->dev, "Stage 1 UTB hit error\n");
+               break;
+       case 1:
+               dev_err(edac_dev->dev, "Stage 1 UTB miss error\n");
+               break;
+       case 2:
+               dev_err(edac_dev->dev, "Stage 1 UTB allocate error\n");
+               break;
+       case 3:
+               dev_err(edac_dev->dev, "TMO operation single bank error\n");
+               break;
+       case 4:
+               dev_err(edac_dev->dev, "Stage 2 UTB error\n");
+               break;
+       case 5:
+               dev_err(edac_dev->dev, "Stage 2 UTB miss error\n");
+               break;
+       case 6:
+               dev_err(edac_dev->dev, "Stage 2 UTB allocate error\n");
+               break;
+       case 7:
+               dev_err(edac_dev->dev, "TMO operation multiple bank error\n");
+               break;
+       }
 
-               /* Clear any HW errors */
-               writel(val, pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET);
+       /* Clear any HW errors */
+       writel(val, pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET);
 
-               edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
-       }
+       edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
 }
 
 static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev)
@@ -684,60 +681,56 @@ static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev)
        /* Check L2 */
        pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE;
        val = readl(pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET);
-       if (val) {
-               val_lo = readl(pg_e + MEMERR_L2C_L2EALR_PAGE_OFFSET);
-               val_hi = readl(pg_e + MEMERR_L2C_L2EAHR_PAGE_OFFSET);
-               dev_err(edac_dev->dev,
-                       "PMD%d memory error L2C L2ESR 0x%08X @ 0x%08X.%08X\n",
-                       ctx->pmd, val, val_hi, val_lo);
-               dev_err(edac_dev->dev,
-                       "ErrSyndrome 0x%02X ErrWay 0x%02X ErrCpu %d ErrGroup 0x%02X ErrAction 0x%02X\n",
-                       MEMERR_L2C_L2ESR_ERRSYN_RD(val),
-                       MEMERR_L2C_L2ESR_ERRWAY_RD(val),
-                       MEMERR_L2C_L2ESR_ERRCPU_RD(val),
-                       MEMERR_L2C_L2ESR_ERRGROUP_RD(val),
-                       MEMERR_L2C_L2ESR_ERRACTION_RD(val));
-
-               if (val & MEMERR_L2C_L2ESR_ERR_MASK)
-                       dev_err(edac_dev->dev,
-                               "One or more correctable error\n");
-               if (val & MEMERR_L2C_L2ESR_MULTICERR_MASK)
-                       dev_err(edac_dev->dev, "Multiple correctable error\n");
-               if (val & MEMERR_L2C_L2ESR_UCERR_MASK)
-                       dev_err(edac_dev->dev,
-                               "One or more uncorrectable error\n");
-               if (val & MEMERR_L2C_L2ESR_MULTUCERR_MASK)
-                       dev_err(edac_dev->dev,
-                               "Multiple uncorrectable error\n");
-
-               switch (MEMERR_L2C_L2ESR_ERRTYPE_RD(val)) {
-               case 0:
-                       dev_err(edac_dev->dev, "Outbound SDB parity error\n");
-                       break;
-               case 1:
-                       dev_err(edac_dev->dev, "Inbound SDB parity error\n");
-                       break;
-               case 2:
-                       dev_err(edac_dev->dev, "Tag ECC error\n");
-                       break;
-               case 3:
-                       dev_err(edac_dev->dev, "Data ECC error\n");
-                       break;
-               }
+       if (!val)
+               goto chk_l2c;
+       val_lo = readl(pg_e + MEMERR_L2C_L2EALR_PAGE_OFFSET);
+       val_hi = readl(pg_e + MEMERR_L2C_L2EAHR_PAGE_OFFSET);
+       dev_err(edac_dev->dev,
+               "PMD%d memory error L2C L2ESR 0x%08X @ 0x%08X.%08X\n",
+               ctx->pmd, val, val_hi, val_lo);
+       dev_err(edac_dev->dev,
+               "ErrSyndrome 0x%02X ErrWay 0x%02X ErrCpu %d ErrGroup 0x%02X ErrAction 0x%02X\n",
+               MEMERR_L2C_L2ESR_ERRSYN_RD(val),
+               MEMERR_L2C_L2ESR_ERRWAY_RD(val),
+               MEMERR_L2C_L2ESR_ERRCPU_RD(val),
+               MEMERR_L2C_L2ESR_ERRGROUP_RD(val),
+               MEMERR_L2C_L2ESR_ERRACTION_RD(val));
+
+       if (val & MEMERR_L2C_L2ESR_ERR_MASK)
+               dev_err(edac_dev->dev, "One or more correctable error\n");
+       if (val & MEMERR_L2C_L2ESR_MULTICERR_MASK)
+               dev_err(edac_dev->dev, "Multiple correctable error\n");
+       if (val & MEMERR_L2C_L2ESR_UCERR_MASK)
+               dev_err(edac_dev->dev, "One or more uncorrectable error\n");
+       if (val & MEMERR_L2C_L2ESR_MULTUCERR_MASK)
+               dev_err(edac_dev->dev, "Multiple uncorrectable error\n");
+
+       switch (MEMERR_L2C_L2ESR_ERRTYPE_RD(val)) {
+       case 0:
+               dev_err(edac_dev->dev, "Outbound SDB parity error\n");
+               break;
+       case 1:
+               dev_err(edac_dev->dev, "Inbound SDB parity error\n");
+               break;
+       case 2:
+               dev_err(edac_dev->dev, "Tag ECC error\n");
+               break;
+       case 3:
+               dev_err(edac_dev->dev, "Data ECC error\n");
+               break;
+       }
 
-               /* Clear any HW errors */
-               writel(val, pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET);
+       /* Clear any HW errors */
+       writel(val, pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET);
 
-               if (val & (MEMERR_L2C_L2ESR_ERR_MASK |
-                          MEMERR_L2C_L2ESR_MULTICERR_MASK))
-                       edac_device_handle_ce(edac_dev, 0, 0,
-                                             edac_dev->ctl_name);
-               if (val & (MEMERR_L2C_L2ESR_UCERR_MASK |
-                          MEMERR_L2C_L2ESR_MULTUCERR_MASK))
-                       edac_device_handle_ue(edac_dev, 0, 0,
-                                             edac_dev->ctl_name);
-       }
+       if (val & (MEMERR_L2C_L2ESR_ERR_MASK |
+                  MEMERR_L2C_L2ESR_MULTICERR_MASK))
+               edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
+       if (val & (MEMERR_L2C_L2ESR_UCERR_MASK |
+                  MEMERR_L2C_L2ESR_MULTUCERR_MASK))
+               edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
 
+chk_l2c:
        /* Check if any memory request timed out on L2 cache */
        pg_d = ctx->pmd_csr + CPU_L2C_PAGE;
        val = readl(pg_d + CPUX_L2C_L2RTOSR_PAGE_OFFSET);
@@ -877,35 +870,25 @@ static const struct file_operations xgene_edac_pmd_debug_inject_fops[] = {
        { }
 };
 
-static void xgene_edac_pmd_create_debugfs_nodes(
-       struct edac_device_ctl_info *edac_dev)
+static void
+xgene_edac_pmd_create_debugfs_nodes(struct edac_device_ctl_info *edac_dev)
 {
        struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
-       struct dentry *edac_debugfs;
-       char name[30];
+       struct dentry *dbgfs_dir;
+       char name[10];
 
-       if (!IS_ENABLED(CONFIG_EDAC_DEBUG))
+       if (!IS_ENABLED(CONFIG_EDAC_DEBUG) || !ctx->edac->dfs)
                return;
 
-       /*
-        * Todo: Switch to common EDAC debug file system for edac device
-        *       when available.
-        */
-       if (!ctx->edac->dfs) {
-               ctx->edac->dfs = debugfs_create_dir(edac_dev->dev->kobj.name,
-                                                   NULL);
-               if (!ctx->edac->dfs)
-                       return;
-       }
-       sprintf(name, "PMD%d", ctx->pmd);
-       edac_debugfs = debugfs_create_dir(name, ctx->edac->dfs);
-       if (!edac_debugfs)
+       snprintf(name, sizeof(name), "PMD%d", ctx->pmd);
+       dbgfs_dir = edac_debugfs_create_dir_at(name, ctx->edac->dfs);
+       if (!dbgfs_dir)
                return;
 
-       debugfs_create_file("l1_inject_ctrl", S_IWUSR, edac_debugfs, edac_dev,
-                           &xgene_edac_pmd_debug_inject_fops[0]);
-       debugfs_create_file("l2_inject_ctrl", S_IWUSR, edac_debugfs, edac_dev,
-                           &xgene_edac_pmd_debug_inject_fops[1]);
+       edac_debugfs_create_file("l1_inject_ctrl", S_IWUSR, dbgfs_dir, edac_dev,
+                                &xgene_edac_pmd_debug_inject_fops[0]);
+       edac_debugfs_create_file("l2_inject_ctrl", S_IWUSR, dbgfs_dir, edac_dev,
+                                &xgene_edac_pmd_debug_inject_fops[1]);
 }
 
 static int xgene_edac_pmd_available(u32 efuse, int pmd)
@@ -941,7 +924,7 @@ static int xgene_edac_pmd_add(struct xgene_edac *edac, struct device_node *np,
                goto err_group;
        }
 
-       sprintf(edac_name, "l2c%d", pmd);
+       snprintf(edac_name, sizeof(edac_name), "l2c%d", pmd);
        edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx),
                                              edac_name, 1, "l2c", 1, 2, NULL,
                                              0, edac_device_alloc_index());
@@ -1016,10 +999,780 @@ static int xgene_edac_pmd_remove(struct xgene_edac_pmd_ctx *pmd)
        return 0;
 }
 
+/* L3 Error device */
+#define L3C_ESR                                (0x0A * 4)
+#define  L3C_ESR_DATATAG_MASK          BIT(9)
+#define  L3C_ESR_MULTIHIT_MASK         BIT(8)
+#define  L3C_ESR_UCEVICT_MASK          BIT(6)
+#define  L3C_ESR_MULTIUCERR_MASK       BIT(5)
+#define  L3C_ESR_MULTICERR_MASK                BIT(4)
+#define  L3C_ESR_UCERR_MASK            BIT(3)
+#define  L3C_ESR_CERR_MASK             BIT(2)
+#define  L3C_ESR_UCERRINTR_MASK                BIT(1)
+#define  L3C_ESR_CERRINTR_MASK         BIT(0)
+#define L3C_ECR                                (0x0B * 4)
+#define  L3C_ECR_UCINTREN              BIT(3)
+#define  L3C_ECR_CINTREN               BIT(2)
+#define  L3C_UCERREN                   BIT(1)
+#define  L3C_CERREN                    BIT(0)
+#define L3C_ELR                                (0x0C * 4)
+#define  L3C_ELR_ERRSYN(src)           ((src & 0xFF800000) >> 23)
+#define  L3C_ELR_ERRWAY(src)           ((src & 0x007E0000) >> 17)
+#define  L3C_ELR_AGENTID(src)          ((src & 0x0001E000) >> 13)
+#define  L3C_ELR_ERRGRP(src)           ((src & 0x00000F00) >> 8)
+#define  L3C_ELR_OPTYPE(src)           ((src & 0x000000F0) >> 4)
+#define  L3C_ELR_PADDRHIGH(src)                (src & 0x0000000F)
+#define L3C_AELR                       (0x0D * 4)
+#define L3C_BELR                       (0x0E * 4)
+#define  L3C_BELR_BANK(src)            (src & 0x0000000F)
+
+struct xgene_edac_dev_ctx {
+       struct list_head        next;
+       struct device           ddev;
+       char                    *name;
+       struct xgene_edac       *edac;
+       struct edac_device_ctl_info *edac_dev;
+       int                     edac_idx;
+       void __iomem            *dev_csr;
+       int                     version;
+};
+
+/*
+ * Version 1 of the L3 controller has broken single bit correctable logic for
+ * certain error syndromes. Log them as uncorrectable in that case.
+ */
+static bool xgene_edac_l3_promote_to_uc_err(u32 l3cesr, u32 l3celr)
+{
+       if (l3cesr & L3C_ESR_DATATAG_MASK) {
+               switch (L3C_ELR_ERRSYN(l3celr)) {
+               case 0x13C:
+               case 0x0B4:
+               case 0x007:
+               case 0x00D:
+               case 0x00E:
+               case 0x019:
+               case 0x01A:
+               case 0x01C:
+               case 0x04E:
+               case 0x041:
+                       return true;
+               }
+       } else if (L3C_ELR_ERRSYN(l3celr) == 9)
+               return true;
+
+       return false;
+}
+
+static void xgene_edac_l3_check(struct edac_device_ctl_info *edac_dev)
+{
+       struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
+       u32 l3cesr;
+       u32 l3celr;
+       u32 l3caelr;
+       u32 l3cbelr;
+
+       l3cesr = readl(ctx->dev_csr + L3C_ESR);
+       if (!(l3cesr & (L3C_ESR_UCERR_MASK | L3C_ESR_CERR_MASK)))
+               return;
+
+       if (l3cesr & L3C_ESR_UCERR_MASK)
+               dev_err(edac_dev->dev, "L3C uncorrectable error\n");
+       if (l3cesr & L3C_ESR_CERR_MASK)
+               dev_warn(edac_dev->dev, "L3C correctable error\n");
+
+       l3celr = readl(ctx->dev_csr + L3C_ELR);
+       l3caelr = readl(ctx->dev_csr + L3C_AELR);
+       l3cbelr = readl(ctx->dev_csr + L3C_BELR);
+       if (l3cesr & L3C_ESR_MULTIHIT_MASK)
+               dev_err(edac_dev->dev, "L3C multiple hit error\n");
+       if (l3cesr & L3C_ESR_UCEVICT_MASK)
+               dev_err(edac_dev->dev,
+                       "L3C dropped eviction of line with error\n");
+       if (l3cesr & L3C_ESR_MULTIUCERR_MASK)
+               dev_err(edac_dev->dev, "L3C multiple uncorrectable error\n");
+       if (l3cesr & L3C_ESR_DATATAG_MASK)
+               dev_err(edac_dev->dev,
+                       "L3C data error syndrome 0x%X group 0x%X\n",
+                       L3C_ELR_ERRSYN(l3celr), L3C_ELR_ERRGRP(l3celr));
+       else
+               dev_err(edac_dev->dev,
+                       "L3C tag error syndrome 0x%X Way of Tag 0x%X Agent ID 0x%X Operation type 0x%X\n",
+                       L3C_ELR_ERRSYN(l3celr), L3C_ELR_ERRWAY(l3celr),
+                       L3C_ELR_AGENTID(l3celr), L3C_ELR_OPTYPE(l3celr));
+       /*
+        * NOTE: Address [41:38] in L3C_ELR_PADDRHIGH(l3celr).
+        *       Address [37:6] in l3caelr. Lower 6 bits are zero.
+        */
+       dev_err(edac_dev->dev, "L3C error address 0x%08X.%08X bank %d\n",
+               L3C_ELR_PADDRHIGH(l3celr) << 6 | (l3caelr >> 26),
+               (l3caelr & 0x3FFFFFFF) << 6, L3C_BELR_BANK(l3cbelr));
+       dev_err(edac_dev->dev,
+               "L3C error status register value 0x%X\n", l3cesr);
+
+       /* Clear L3C error interrupt */
+       writel(0, ctx->dev_csr + L3C_ESR);
+
+       if (ctx->version <= 1 &&
+           xgene_edac_l3_promote_to_uc_err(l3cesr, l3celr)) {
+               edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
+               return;
+       }
+       if (l3cesr & L3C_ESR_CERR_MASK)
+               edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
+       if (l3cesr & L3C_ESR_UCERR_MASK)
+               edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
+}
+
+static void xgene_edac_l3_hw_init(struct edac_device_ctl_info *edac_dev,
+                                 bool enable)
+{
+       struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
+       u32 val;
+
+       val = readl(ctx->dev_csr + L3C_ECR);
+       val |= L3C_UCERREN | L3C_CERREN;
+       /* On disable, we just disable interrupt but keep error enabled */
+       if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
+               if (enable)
+                       val |= L3C_ECR_UCINTREN | L3C_ECR_CINTREN;
+               else
+                       val &= ~(L3C_ECR_UCINTREN | L3C_ECR_CINTREN);
+       }
+       writel(val, ctx->dev_csr + L3C_ECR);
+
+       if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
+               /* Enable/disable L3 error top level interrupt */
+               if (enable) {
+                       xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
+                                              L3C_UNCORR_ERR_MASK);
+                       xgene_edac_pcp_clrbits(ctx->edac, PCPLPERRINTMSK,
+                                              L3C_CORR_ERR_MASK);
+               } else {
+                       xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
+                                              L3C_UNCORR_ERR_MASK);
+                       xgene_edac_pcp_setbits(ctx->edac, PCPLPERRINTMSK,
+                                              L3C_CORR_ERR_MASK);
+               }
+       }
+}
+
+static ssize_t xgene_edac_l3_inject_ctrl_write(struct file *file,
+                                              const char __user *data,
+                                              size_t count, loff_t *ppos)
+{
+       struct edac_device_ctl_info *edac_dev = file->private_data;
+       struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
+
+       /* Generate all errors */
+       writel(0xFFFFFFFF, ctx->dev_csr + L3C_ESR);
+       return count;
+}
+
+static const struct file_operations xgene_edac_l3_debug_inject_fops = {
+       .open = simple_open,
+       .write = xgene_edac_l3_inject_ctrl_write,
+       .llseek = generic_file_llseek
+};
+
+static void
+xgene_edac_l3_create_debugfs_nodes(struct edac_device_ctl_info *edac_dev)
+{
+       struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
+       struct dentry *dbgfs_dir;
+       char name[10];
+
+       if (!IS_ENABLED(CONFIG_EDAC_DEBUG) || !ctx->edac->dfs)
+               return;
+
+       snprintf(name, sizeof(name), "l3c%d", ctx->edac_idx);
+       dbgfs_dir = edac_debugfs_create_dir_at(name, ctx->edac->dfs);
+       if (!dbgfs_dir)
+               return;
+
+       debugfs_create_file("l3_inject_ctrl", S_IWUSR, dbgfs_dir, edac_dev,
+                           &xgene_edac_l3_debug_inject_fops);
+}
+
+static int xgene_edac_l3_add(struct xgene_edac *edac, struct device_node *np,
+                            int version)
+{
+       struct edac_device_ctl_info *edac_dev;
+       struct xgene_edac_dev_ctx *ctx;
+       struct resource res;
+       void __iomem *dev_csr;
+       int edac_idx;
+       int rc = 0;
+
+       if (!devres_open_group(edac->dev, xgene_edac_l3_add, GFP_KERNEL))
+               return -ENOMEM;
+
+       rc = of_address_to_resource(np, 0, &res);
+       if (rc < 0) {
+               dev_err(edac->dev, "no L3 resource address\n");
+               goto err_release_group;
+       }
+       dev_csr = devm_ioremap_resource(edac->dev, &res);
+       if (IS_ERR(dev_csr)) {
+               dev_err(edac->dev,
+                       "devm_ioremap_resource failed for L3 resource address\n");
+               rc = PTR_ERR(dev_csr);
+               goto err_release_group;
+       }
+
+       edac_idx = edac_device_alloc_index();
+       edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx),
+                                             "l3c", 1, "l3c", 1, 0, NULL, 0,
+                                             edac_idx);
+       if (!edac_dev) {
+               rc = -ENOMEM;
+               goto err_release_group;
+       }
+
+       ctx = edac_dev->pvt_info;
+       ctx->dev_csr = dev_csr;
+       ctx->name = "xgene_l3_err";
+       ctx->edac_idx = edac_idx;
+       ctx->edac = edac;
+       ctx->edac_dev = edac_dev;
+       ctx->ddev = *edac->dev;
+       ctx->version = version;
+       edac_dev->dev = &ctx->ddev;
+       edac_dev->ctl_name = ctx->name;
+       edac_dev->dev_name = ctx->name;
+       edac_dev->mod_name = EDAC_MOD_STR;
+
+       if (edac_op_state == EDAC_OPSTATE_POLL)
+               edac_dev->edac_check = xgene_edac_l3_check;
+
+       xgene_edac_l3_create_debugfs_nodes(edac_dev);
+
+       rc = edac_device_add_device(edac_dev);
+       if (rc > 0) {
+               dev_err(edac->dev, "failed edac_device_add_device()\n");
+               rc = -ENOMEM;
+               goto err_ctl_free;
+       }
+
+       if (edac_op_state == EDAC_OPSTATE_INT)
+               edac_dev->op_state = OP_RUNNING_INTERRUPT;
+
+       list_add(&ctx->next, &edac->l3s);
+
+       xgene_edac_l3_hw_init(edac_dev, 1);
+
+       devres_remove_group(edac->dev, xgene_edac_l3_add);
+
+       dev_info(edac->dev, "X-Gene EDAC L3 registered\n");
+       return 0;
+
+err_ctl_free:
+       edac_device_free_ctl_info(edac_dev);
+err_release_group:
+       devres_release_group(edac->dev, xgene_edac_l3_add);
+       return rc;
+}
+
+static int xgene_edac_l3_remove(struct xgene_edac_dev_ctx *l3)
+{
+       struct edac_device_ctl_info *edac_dev = l3->edac_dev;
+
+       xgene_edac_l3_hw_init(edac_dev, 0);
+       edac_device_del_device(l3->edac->dev);
+       edac_device_free_ctl_info(edac_dev);
+       return 0;
+}
+
+/* SoC error device */
+#define IOBAXIS0TRANSERRINTSTS         0x0000
+#define  IOBAXIS0_M_ILLEGAL_ACCESS_MASK        BIT(1)
+#define  IOBAXIS0_ILLEGAL_ACCESS_MASK  BIT(0)
+#define IOBAXIS0TRANSERRINTMSK         0x0004
+#define IOBAXIS0TRANSERRREQINFOL       0x0008
+#define IOBAXIS0TRANSERRREQINFOH       0x000c
+#define  REQTYPE_RD(src)               (((src) & BIT(0)))
+#define  ERRADDRH_RD(src)              (((src) & 0xffc00000) >> 22)
+#define IOBAXIS1TRANSERRINTSTS         0x0010
+#define IOBAXIS1TRANSERRINTMSK         0x0014
+#define IOBAXIS1TRANSERRREQINFOL       0x0018
+#define IOBAXIS1TRANSERRREQINFOH       0x001c
+#define IOBPATRANSERRINTSTS            0x0020
+#define  IOBPA_M_REQIDRAM_CORRUPT_MASK BIT(7)
+#define  IOBPA_REQIDRAM_CORRUPT_MASK   BIT(6)
+#define  IOBPA_M_TRANS_CORRUPT_MASK    BIT(5)
+#define  IOBPA_TRANS_CORRUPT_MASK      BIT(4)
+#define  IOBPA_M_WDATA_CORRUPT_MASK    BIT(3)
+#define  IOBPA_WDATA_CORRUPT_MASK      BIT(2)
+#define  IOBPA_M_RDATA_CORRUPT_MASK    BIT(1)
+#define  IOBPA_RDATA_CORRUPT_MASK      BIT(0)
+#define IOBBATRANSERRINTSTS            0x0030
+#define  M_ILLEGAL_ACCESS_MASK         BIT(15)
+#define  ILLEGAL_ACCESS_MASK           BIT(14)
+#define  M_WIDRAM_CORRUPT_MASK         BIT(13)
+#define  WIDRAM_CORRUPT_MASK           BIT(12)
+#define  M_RIDRAM_CORRUPT_MASK         BIT(11)
+#define  RIDRAM_CORRUPT_MASK           BIT(10)
+#define  M_TRANS_CORRUPT_MASK          BIT(9)
+#define  TRANS_CORRUPT_MASK            BIT(8)
+#define  M_WDATA_CORRUPT_MASK          BIT(7)
+#define  WDATA_CORRUPT_MASK            BIT(6)
+#define  M_RBM_POISONED_REQ_MASK       BIT(5)
+#define  RBM_POISONED_REQ_MASK         BIT(4)
+#define  M_XGIC_POISONED_REQ_MASK      BIT(3)
+#define  XGIC_POISONED_REQ_MASK                BIT(2)
+#define  M_WRERR_RESP_MASK             BIT(1)
+#define  WRERR_RESP_MASK               BIT(0)
+#define IOBBATRANSERRREQINFOL          0x0038
+#define IOBBATRANSERRREQINFOH          0x003c
+#define  REQTYPE_F2_RD(src)            ((src) & BIT(0))
+#define  ERRADDRH_F2_RD(src)           (((src) & 0xffc00000) >> 22)
+#define IOBBATRANSERRCSWREQID          0x0040
+#define XGICTRANSERRINTSTS             0x0050
+#define  M_WR_ACCESS_ERR_MASK          BIT(3)
+#define  WR_ACCESS_ERR_MASK            BIT(2)
+#define  M_RD_ACCESS_ERR_MASK          BIT(1)
+#define  RD_ACCESS_ERR_MASK            BIT(0)
+#define XGICTRANSERRINTMSK             0x0054
+#define XGICTRANSERRREQINFO            0x0058
+#define  REQTYPE_MASK                  BIT(26)
+#define  ERRADDR_RD(src)               ((src) & 0x03ffffff)
+#define GLBL_ERR_STS                   0x0800
+#define  MDED_ERR_MASK                 BIT(3)
+#define  DED_ERR_MASK                  BIT(2)
+#define  MSEC_ERR_MASK                 BIT(1)
+#define  SEC_ERR_MASK                  BIT(0)
+#define GLBL_SEC_ERRL                  0x0810
+#define GLBL_SEC_ERRH                  0x0818
+#define GLBL_MSEC_ERRL                 0x0820
+#define GLBL_MSEC_ERRH                 0x0828
+#define GLBL_DED_ERRL                  0x0830
+#define GLBL_DED_ERRLMASK              0x0834
+#define GLBL_DED_ERRH                  0x0838
+#define GLBL_DED_ERRHMASK              0x083c
+#define GLBL_MDED_ERRL                 0x0840
+#define GLBL_MDED_ERRLMASK             0x0844
+#define GLBL_MDED_ERRH                 0x0848
+#define GLBL_MDED_ERRHMASK             0x084c
+
+static const char * const soc_mem_err_v1[] = {
+       "10GbE0",
+       "10GbE1",
+       "Security",
+       "SATA45",
+       "SATA23/ETH23",
+       "SATA01/ETH01",
+       "USB1",
+       "USB0",
+       "QML",
+       "QM0",
+       "QM1 (XGbE01)",
+       "PCIE4",
+       "PCIE3",
+       "PCIE2",
+       "PCIE1",
+       "PCIE0",
+       "CTX Manager",
+       "OCM",
+       "1GbE",
+       "CLE",
+       "AHBC",
+       "PktDMA",
+       "GFC",
+       "MSLIM",
+       "10GbE2",
+       "10GbE3",
+       "QM2 (XGbE23)",
+       "IOB",
+       "unknown",
+       "unknown",
+       "unknown",
+       "unknown",
+};
+
+static void xgene_edac_iob_gic_report(struct edac_device_ctl_info *edac_dev)
+{
+       struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
+       u32 err_addr_lo;
+       u32 err_addr_hi;
+       u32 reg;
+       u32 info;
+
+       /* GIC transaction error interrupt */
+       reg = readl(ctx->dev_csr + XGICTRANSERRINTSTS);
+       if (!reg)
+               goto chk_iob_err;
+       dev_err(edac_dev->dev, "XGIC transaction error\n");
+       if (reg & RD_ACCESS_ERR_MASK)
+               dev_err(edac_dev->dev, "XGIC read size error\n");
+       if (reg & M_RD_ACCESS_ERR_MASK)
+               dev_err(edac_dev->dev, "Multiple XGIC read size error\n");
+       if (reg & WR_ACCESS_ERR_MASK)
+               dev_err(edac_dev->dev, "XGIC write size error\n");
+       if (reg & M_WR_ACCESS_ERR_MASK)
+               dev_err(edac_dev->dev, "Multiple XGIC write size error\n");
+       info = readl(ctx->dev_csr + XGICTRANSERRREQINFO);
+       dev_err(edac_dev->dev, "XGIC %s access @ 0x%08X (0x%08X)\n",
+               info & REQTYPE_MASK ? "read" : "write", ERRADDR_RD(info),
+               info);
+       writel(reg, ctx->dev_csr + XGICTRANSERRINTSTS);
+
+chk_iob_err:
+       /* IOB memory error */
+       reg = readl(ctx->dev_csr + GLBL_ERR_STS);
+       if (!reg)
+               return;
+       if (reg & SEC_ERR_MASK) {
+               err_addr_lo = readl(ctx->dev_csr + GLBL_SEC_ERRL);
+               err_addr_hi = readl(ctx->dev_csr + GLBL_SEC_ERRH);
+               dev_err(edac_dev->dev,
+                       "IOB single-bit correctable memory at 0x%08X.%08X error\n",
+                       err_addr_lo, err_addr_hi);
+               writel(err_addr_lo, ctx->dev_csr + GLBL_SEC_ERRL);
+               writel(err_addr_hi, ctx->dev_csr + GLBL_SEC_ERRH);
+       }
+       if (reg & MSEC_ERR_MASK) {
+               err_addr_lo = readl(ctx->dev_csr + GLBL_MSEC_ERRL);
+               err_addr_hi = readl(ctx->dev_csr + GLBL_MSEC_ERRH);
+               dev_err(edac_dev->dev,
+                       "IOB multiple single-bit correctable memory at 0x%08X.%08X error\n",
+                       err_addr_lo, err_addr_hi);
+               writel(err_addr_lo, ctx->dev_csr + GLBL_MSEC_ERRL);
+               writel(err_addr_hi, ctx->dev_csr + GLBL_MSEC_ERRH);
+       }
+       if (reg & (SEC_ERR_MASK | MSEC_ERR_MASK))
+               edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
+
+       if (reg & DED_ERR_MASK) {
+               err_addr_lo = readl(ctx->dev_csr + GLBL_DED_ERRL);
+               err_addr_hi = readl(ctx->dev_csr + GLBL_DED_ERRH);
+               dev_err(edac_dev->dev,
+                       "IOB double-bit uncorrectable memory at 0x%08X.%08X error\n",
+                       err_addr_lo, err_addr_hi);
+               writel(err_addr_lo, ctx->dev_csr + GLBL_DED_ERRL);
+               writel(err_addr_hi, ctx->dev_csr + GLBL_DED_ERRH);
+       }
+       if (reg & MDED_ERR_MASK) {
+               err_addr_lo = readl(ctx->dev_csr + GLBL_MDED_ERRL);
+               err_addr_hi = readl(ctx->dev_csr + GLBL_MDED_ERRH);
+               dev_err(edac_dev->dev,
+                       "Multiple IOB double-bit uncorrectable memory at 0x%08X.%08X error\n",
+                       err_addr_lo, err_addr_hi);
+               writel(err_addr_lo, ctx->dev_csr + GLBL_MDED_ERRL);
+               writel(err_addr_hi, ctx->dev_csr + GLBL_MDED_ERRH);
+       }
+       if (reg & (DED_ERR_MASK | MDED_ERR_MASK))
+               edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
+}
+
+static void xgene_edac_rb_report(struct edac_device_ctl_info *edac_dev)
+{
+       struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
+       u32 err_addr_lo;
+       u32 err_addr_hi;
+       u32 reg;
+
+       /* IOB Bridge agent transaction error interrupt */
+       reg = readl(ctx->dev_csr + IOBBATRANSERRINTSTS);
+       if (!reg)
+               return;
+
+       dev_err(edac_dev->dev, "IOB bridge agent (BA) transaction error\n");
+       if (reg & WRERR_RESP_MASK)
+               dev_err(edac_dev->dev, "IOB BA write response error\n");
+       if (reg & M_WRERR_RESP_MASK)
+               dev_err(edac_dev->dev,
+                       "Multiple IOB BA write response error\n");
+       if (reg & XGIC_POISONED_REQ_MASK)
+               dev_err(edac_dev->dev, "IOB BA XGIC poisoned write error\n");
+       if (reg & M_XGIC_POISONED_REQ_MASK)
+               dev_err(edac_dev->dev,
+                       "Multiple IOB BA XGIC poisoned write error\n");
+       if (reg & RBM_POISONED_REQ_MASK)
+               dev_err(edac_dev->dev, "IOB BA RBM poisoned write error\n");
+       if (reg & M_RBM_POISONED_REQ_MASK)
+               dev_err(edac_dev->dev,
+                       "Multiple IOB BA RBM poisoned write error\n");
+       if (reg & WDATA_CORRUPT_MASK)
+               dev_err(edac_dev->dev, "IOB BA write error\n");
+       if (reg & M_WDATA_CORRUPT_MASK)
+               dev_err(edac_dev->dev, "Multiple IOB BA write error\n");
+       if (reg & TRANS_CORRUPT_MASK)
+               dev_err(edac_dev->dev, "IOB BA transaction error\n");
+       if (reg & M_TRANS_CORRUPT_MASK)
+               dev_err(edac_dev->dev, "Multiple IOB BA transaction error\n");
+       if (reg & RIDRAM_CORRUPT_MASK)
+               dev_err(edac_dev->dev,
+                       "IOB BA RDIDRAM read transaction ID error\n");
+       if (reg & M_RIDRAM_CORRUPT_MASK)
+               dev_err(edac_dev->dev,
+                       "Multiple IOB BA RDIDRAM read transaction ID error\n");
+       if (reg & WIDRAM_CORRUPT_MASK)
+               dev_err(edac_dev->dev,
+                       "IOB BA RDIDRAM write transaction ID error\n");
+       if (reg & M_WIDRAM_CORRUPT_MASK)
+               dev_err(edac_dev->dev,
+                       "Multiple IOB BA RDIDRAM write transaction ID error\n");
+       if (reg & ILLEGAL_ACCESS_MASK)
+               dev_err(edac_dev->dev,
+                       "IOB BA XGIC/RB illegal access error\n");
+       if (reg & M_ILLEGAL_ACCESS_MASK)
+               dev_err(edac_dev->dev,
+                       "Multiple IOB BA XGIC/RB illegal access error\n");
+
+       err_addr_lo = readl(ctx->dev_csr + IOBBATRANSERRREQINFOL);
+       err_addr_hi = readl(ctx->dev_csr + IOBBATRANSERRREQINFOH);
+       dev_err(edac_dev->dev, "IOB BA %s access at 0x%02X.%08X (0x%08X)\n",
+               REQTYPE_F2_RD(err_addr_hi) ? "read" : "write",
+               ERRADDRH_F2_RD(err_addr_hi), err_addr_lo, err_addr_hi);
+       if (reg & WRERR_RESP_MASK)
+               dev_err(edac_dev->dev, "IOB BA requestor ID 0x%08X\n",
+                       readl(ctx->dev_csr + IOBBATRANSERRCSWREQID));
+       writel(reg, ctx->dev_csr + IOBBATRANSERRINTSTS);
+}
+
+static void xgene_edac_pa_report(struct edac_device_ctl_info *edac_dev)
+{
+       struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
+       u32 err_addr_lo;
+       u32 err_addr_hi;
+       u32 reg;
+
+       /* IOB Processing agent transaction error interrupt */
+       reg = readl(ctx->dev_csr + IOBPATRANSERRINTSTS);
+       if (!reg)
+               goto chk_iob_axi0;
+       dev_err(edac_dev->dev, "IOB procesing agent (PA) transaction error\n");
+       if (reg & IOBPA_RDATA_CORRUPT_MASK)
+               dev_err(edac_dev->dev, "IOB PA read data RAM error\n");
+       if (reg & IOBPA_M_RDATA_CORRUPT_MASK)
+               dev_err(edac_dev->dev,
+                       "Mutilple IOB PA read data RAM error\n");
+       if (reg & IOBPA_WDATA_CORRUPT_MASK)
+               dev_err(edac_dev->dev, "IOB PA write data RAM error\n");
+       if (reg & IOBPA_M_WDATA_CORRUPT_MASK)
+               dev_err(edac_dev->dev,
+                       "Mutilple IOB PA write data RAM error\n");
+       if (reg & IOBPA_TRANS_CORRUPT_MASK)
+               dev_err(edac_dev->dev, "IOB PA transaction error\n");
+       if (reg & IOBPA_M_TRANS_CORRUPT_MASK)
+               dev_err(edac_dev->dev, "Mutilple IOB PA transaction error\n");
+       if (reg & IOBPA_REQIDRAM_CORRUPT_MASK)
+               dev_err(edac_dev->dev, "IOB PA transaction ID RAM error\n");
+       if (reg & IOBPA_M_REQIDRAM_CORRUPT_MASK)
+               dev_err(edac_dev->dev,
+                       "Multiple IOB PA transaction ID RAM error\n");
+       writel(reg, ctx->dev_csr + IOBPATRANSERRINTSTS);
+
+chk_iob_axi0:
+       /* IOB AXI0 Error */
+       reg = readl(ctx->dev_csr + IOBAXIS0TRANSERRINTSTS);
+       if (!reg)
+               goto chk_iob_axi1;
+       err_addr_lo = readl(ctx->dev_csr + IOBAXIS0TRANSERRREQINFOL);
+       err_addr_hi = readl(ctx->dev_csr + IOBAXIS0TRANSERRREQINFOH);
+       dev_err(edac_dev->dev,
+               "%sAXI slave 0 illegal %s access @ 0x%02X.%08X (0x%08X)\n",
+               reg & IOBAXIS0_M_ILLEGAL_ACCESS_MASK ? "Multiple " : "",
+               REQTYPE_RD(err_addr_hi) ? "read" : "write",
+               ERRADDRH_RD(err_addr_hi), err_addr_lo, err_addr_hi);
+       writel(reg, ctx->dev_csr + IOBAXIS0TRANSERRINTSTS);
+
+chk_iob_axi1:
+       /* IOB AXI1 Error */
+       reg = readl(ctx->dev_csr + IOBAXIS1TRANSERRINTSTS);
+       if (!reg)
+               return;
+       err_addr_lo = readl(ctx->dev_csr + IOBAXIS1TRANSERRREQINFOL);
+       err_addr_hi = readl(ctx->dev_csr + IOBAXIS1TRANSERRREQINFOH);
+       dev_err(edac_dev->dev,
+               "%sAXI slave 1 illegal %s access @ 0x%02X.%08X (0x%08X)\n",
+               reg & IOBAXIS0_M_ILLEGAL_ACCESS_MASK ? "Multiple " : "",
+               REQTYPE_RD(err_addr_hi) ? "read" : "write",
+               ERRADDRH_RD(err_addr_hi), err_addr_lo, err_addr_hi);
+       writel(reg, ctx->dev_csr + IOBAXIS1TRANSERRINTSTS);
+}
+
+static void xgene_edac_soc_check(struct edac_device_ctl_info *edac_dev)
+{
+       struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
+       const char * const *soc_mem_err = NULL;
+       u32 pcp_hp_stat;
+       u32 pcp_lp_stat;
+       u32 reg;
+       int i;
+
+       xgene_edac_pcp_rd(ctx->edac, PCPHPERRINTSTS, &pcp_hp_stat);
+       xgene_edac_pcp_rd(ctx->edac, PCPLPERRINTSTS, &pcp_lp_stat);
+       xgene_edac_pcp_rd(ctx->edac, MEMERRINTSTS, &reg);
+       if (!((pcp_hp_stat & (IOB_PA_ERR_MASK | IOB_BA_ERR_MASK |
+                             IOB_XGIC_ERR_MASK | IOB_RB_ERR_MASK)) ||
+             (pcp_lp_stat & CSW_SWITCH_TRACE_ERR_MASK) || reg))
+               return;
+
+       if (pcp_hp_stat & IOB_XGIC_ERR_MASK)
+               xgene_edac_iob_gic_report(edac_dev);
+
+       if (pcp_hp_stat & (IOB_RB_ERR_MASK | IOB_BA_ERR_MASK))
+               xgene_edac_rb_report(edac_dev);
+
+       if (pcp_hp_stat & IOB_PA_ERR_MASK)
+               xgene_edac_pa_report(edac_dev);
+
+       if (pcp_lp_stat & CSW_SWITCH_TRACE_ERR_MASK) {
+               dev_info(edac_dev->dev,
+                        "CSW switch trace correctable memory parity error\n");
+               edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
+       }
+
+       if (!reg)
+               return;
+       if (ctx->version == 1)
+               soc_mem_err = soc_mem_err_v1;
+       if (!soc_mem_err) {
+               dev_err(edac_dev->dev, "SoC memory parity error 0x%08X\n",
+                       reg);
+               edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
+               return;
+       }
+       for (i = 0; i < 31; i++) {
+               if (reg & (1 << i)) {
+                       dev_err(edac_dev->dev, "%s memory parity error\n",
+                               soc_mem_err[i]);
+                       edac_device_handle_ue(edac_dev, 0, 0,
+                                             edac_dev->ctl_name);
+               }
+       }
+}
+
+static void xgene_edac_soc_hw_init(struct edac_device_ctl_info *edac_dev,
+                                  bool enable)
+{
+       struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
+
+       /* Enable SoC IP error interrupt */
+       if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
+               if (enable) {
+                       xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
+                                              IOB_PA_ERR_MASK |
+                                              IOB_BA_ERR_MASK |
+                                              IOB_XGIC_ERR_MASK |
+                                              IOB_RB_ERR_MASK);
+                       xgene_edac_pcp_clrbits(ctx->edac, PCPLPERRINTMSK,
+                                              CSW_SWITCH_TRACE_ERR_MASK);
+               } else {
+                       xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
+                                              IOB_PA_ERR_MASK |
+                                              IOB_BA_ERR_MASK |
+                                              IOB_XGIC_ERR_MASK |
+                                              IOB_RB_ERR_MASK);
+                       xgene_edac_pcp_setbits(ctx->edac, PCPLPERRINTMSK,
+                                              CSW_SWITCH_TRACE_ERR_MASK);
+               }
+
+               writel(enable ? 0x0 : 0xFFFFFFFF,
+                      ctx->dev_csr + IOBAXIS0TRANSERRINTMSK);
+               writel(enable ? 0x0 : 0xFFFFFFFF,
+                      ctx->dev_csr + IOBAXIS1TRANSERRINTMSK);
+               writel(enable ? 0x0 : 0xFFFFFFFF,
+                      ctx->dev_csr + XGICTRANSERRINTMSK);
+
+               xgene_edac_pcp_setbits(ctx->edac, MEMERRINTMSK,
+                                      enable ? 0x0 : 0xFFFFFFFF);
+       }
+}
+
+static int xgene_edac_soc_add(struct xgene_edac *edac, struct device_node *np,
+                             int version)
+{
+       struct edac_device_ctl_info *edac_dev;
+       struct xgene_edac_dev_ctx *ctx;
+       void __iomem *dev_csr;
+       struct resource res;
+       int edac_idx;
+       int rc;
+
+       if (!devres_open_group(edac->dev, xgene_edac_soc_add, GFP_KERNEL))
+               return -ENOMEM;
+
+       rc = of_address_to_resource(np, 0, &res);
+       if (rc < 0) {
+               dev_err(edac->dev, "no SoC resource address\n");
+               goto err_release_group;
+       }
+       dev_csr = devm_ioremap_resource(edac->dev, &res);
+       if (IS_ERR(dev_csr)) {
+               dev_err(edac->dev,
+                       "devm_ioremap_resource failed for soc resource address\n");
+               rc = PTR_ERR(dev_csr);
+               goto err_release_group;
+       }
+
+       edac_idx = edac_device_alloc_index();
+       edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx),
+                                             "SOC", 1, "SOC", 1, 2, NULL, 0,
+                                             edac_idx);
+       if (!edac_dev) {
+               rc = -ENOMEM;
+               goto err_release_group;
+       }
+
+       ctx = edac_dev->pvt_info;
+       ctx->dev_csr = dev_csr;
+       ctx->name = "xgene_soc_err";
+       ctx->edac_idx = edac_idx;
+       ctx->edac = edac;
+       ctx->edac_dev = edac_dev;
+       ctx->ddev = *edac->dev;
+       ctx->version = version;
+       edac_dev->dev = &ctx->ddev;
+       edac_dev->ctl_name = ctx->name;
+       edac_dev->dev_name = ctx->name;
+       edac_dev->mod_name = EDAC_MOD_STR;
+
+       if (edac_op_state == EDAC_OPSTATE_POLL)
+               edac_dev->edac_check = xgene_edac_soc_check;
+
+       rc = edac_device_add_device(edac_dev);
+       if (rc > 0) {
+               dev_err(edac->dev, "failed edac_device_add_device()\n");
+               rc = -ENOMEM;
+               goto err_ctl_free;
+       }
+
+       if (edac_op_state == EDAC_OPSTATE_INT)
+               edac_dev->op_state = OP_RUNNING_INTERRUPT;
+
+       list_add(&ctx->next, &edac->socs);
+
+       xgene_edac_soc_hw_init(edac_dev, 1);
+
+       devres_remove_group(edac->dev, xgene_edac_soc_add);
+
+       dev_info(edac->dev, "X-Gene EDAC SoC registered\n");
+
+       return 0;
+
+err_ctl_free:
+       edac_device_free_ctl_info(edac_dev);
+err_release_group:
+       devres_release_group(edac->dev, xgene_edac_soc_add);
+       return rc;
+}
+
+static int xgene_edac_soc_remove(struct xgene_edac_dev_ctx *soc)
+{
+       struct edac_device_ctl_info *edac_dev = soc->edac_dev;
+
+       xgene_edac_soc_hw_init(edac_dev, 0);
+       edac_device_del_device(soc->edac->dev);
+       edac_device_free_ctl_info(edac_dev);
+       return 0;
+}
+
 static irqreturn_t xgene_edac_isr(int irq, void *dev_id)
 {
        struct xgene_edac *ctx = dev_id;
        struct xgene_edac_pmd_ctx *pmd;
+       struct xgene_edac_dev_ctx *node;
        unsigned int pcp_hp_stat;
        unsigned int pcp_lp_stat;
 
@@ -1030,9 +1783,8 @@ static irqreturn_t xgene_edac_isr(int irq, void *dev_id)
            (MCU_CORR_ERR_MASK & pcp_lp_stat)) {
                struct xgene_edac_mc_ctx *mcu;
 
-               list_for_each_entry(mcu, &ctx->mcus, next) {
+               list_for_each_entry(mcu, &ctx->mcus, next)
                        xgene_edac_mc_check(mcu->mci);
-               }
        }
 
        list_for_each_entry(pmd, &ctx->pmds, next) {
@@ -1040,6 +1792,12 @@ static irqreturn_t xgene_edac_isr(int irq, void *dev_id)
                        xgene_edac_pmd_check(pmd->edac_dev);
        }
 
+       list_for_each_entry(node, &ctx->l3s, next)
+               xgene_edac_l3_check(node->edac_dev);
+
+       list_for_each_entry(node, &ctx->socs, next)
+               xgene_edac_soc_check(node->edac_dev);
+
        return IRQ_HANDLED;
 }
 
@@ -1058,6 +1816,8 @@ static int xgene_edac_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, edac);
        INIT_LIST_HEAD(&edac->mcus);
        INIT_LIST_HEAD(&edac->pmds);
+       INIT_LIST_HEAD(&edac->l3s);
+       INIT_LIST_HEAD(&edac->socs);
        spin_lock_init(&edac->lock);
        mutex_init(&edac->mc_lock);
 
@@ -1122,6 +1882,8 @@ static int xgene_edac_probe(struct platform_device *pdev)
                }
        }
 
+       edac->dfs = edac_debugfs_create_dir(pdev->dev.kobj.name);
+
        for_each_child_of_node(pdev->dev.of_node, child) {
                if (!of_device_is_available(child))
                        continue;
@@ -1131,6 +1893,14 @@ static int xgene_edac_probe(struct platform_device *pdev)
                        xgene_edac_pmd_add(edac, child, 1);
                if (of_device_is_compatible(child, "apm,xgene-edac-pmd-v2"))
                        xgene_edac_pmd_add(edac, child, 2);
+               if (of_device_is_compatible(child, "apm,xgene-edac-l3"))
+                       xgene_edac_l3_add(edac, child, 1);
+               if (of_device_is_compatible(child, "apm,xgene-edac-l3-v2"))
+                       xgene_edac_l3_add(edac, child, 2);
+               if (of_device_is_compatible(child, "apm,xgene-edac-soc"))
+                       xgene_edac_soc_add(edac, child, 0);
+               if (of_device_is_compatible(child, "apm,xgene-edac-soc-v1"))
+                       xgene_edac_soc_add(edac, child, 1);
        }
 
        return 0;
@@ -1146,14 +1916,21 @@ static int xgene_edac_remove(struct platform_device *pdev)
        struct xgene_edac_mc_ctx *temp_mcu;
        struct xgene_edac_pmd_ctx *pmd;
        struct xgene_edac_pmd_ctx *temp_pmd;
+       struct xgene_edac_dev_ctx *node;
+       struct xgene_edac_dev_ctx *temp_node;
 
-       list_for_each_entry_safe(mcu, temp_mcu, &edac->mcus, next) {
+       list_for_each_entry_safe(mcu, temp_mcu, &edac->mcus, next)
                xgene_edac_mc_remove(mcu);
-       }
 
-       list_for_each_entry_safe(pmd, temp_pmd, &edac->pmds, next) {
+       list_for_each_entry_safe(pmd, temp_pmd, &edac->pmds, next)
                xgene_edac_pmd_remove(pmd);
-       }
+
+       list_for_each_entry_safe(node, temp_node, &edac->l3s, next)
+               xgene_edac_l3_remove(node);
+
+       list_for_each_entry_safe(node, temp_node, &edac->socs, next)
+               xgene_edac_soc_remove(node);
+
        return 0;
 }
 
index 84533e02fbf8ba292cddf960fde5881e1898821c..e1670d533f9742e94b505ba0684d9373956c8bb0 100644 (file)
@@ -52,6 +52,28 @@ config EFI_RUNTIME_MAP
 
          See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map.
 
+config EFI_FAKE_MEMMAP
+       bool "Enable EFI fake memory map"
+       depends on EFI && X86
+       default n
+       help
+         Saying Y here will enable "efi_fake_mem" boot option.
+         By specifying this parameter, you can add arbitrary attribute
+         to specific memory range by updating original (firmware provided)
+         EFI memmap.
+         This is useful for debugging of EFI memmap related feature.
+         e.g. Address Range Mirroring feature.
+
+config EFI_MAX_FAKE_MEM
+       int "maximum allowable number of ranges in efi_fake_mem boot option"
+       depends on EFI_FAKE_MEMMAP
+       range 1 128
+       default 8
+       help
+         Maximum allowable number of ranges in efi_fake_mem boot option.
+         Ranges can be set up to this value using comma-separated list.
+         The default value is 8.
+
 config EFI_PARAMS_FROM_FDT
        bool
        help
index 6fd3da938717c27c233af9c6b66b186f69fc93fa..c24f00569acb3b92c5e0a3a91cc04f257336f96f 100644 (file)
@@ -9,3 +9,4 @@ obj-$(CONFIG_UEFI_CPER)                 += cper.o
 obj-$(CONFIG_EFI_RUNTIME_MAP)          += runtime-map.o
 obj-$(CONFIG_EFI_RUNTIME_WRAPPERS)     += runtime-wrappers.o
 obj-$(CONFIG_EFI_STUB)                 += libstub/
+obj-$(CONFIG_EFI_FAKE_MEMMAP)          += fake_mem.o
index e992abc5ef264e38fefb0b3361f91c357fd1d08e..c8d794c5847989e1d150b8da968e76a24c6e0d91 100644 (file)
@@ -400,3 +400,4 @@ module_exit(efivars_pstore_exit);
 
 MODULE_DESCRIPTION("EFI variable backend for pstore");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:efivars");
index d6144e3b97c54235ca45a0ad71872957a36ffe48..027ca212179f7f81276733d0bda6b97a636c6770 100644 (file)
 #include <linux/platform_device.h>
 
 struct efi __read_mostly efi = {
-       .mps        = EFI_INVALID_TABLE_ADDR,
-       .acpi       = EFI_INVALID_TABLE_ADDR,
-       .acpi20     = EFI_INVALID_TABLE_ADDR,
-       .smbios     = EFI_INVALID_TABLE_ADDR,
-       .smbios3    = EFI_INVALID_TABLE_ADDR,
-       .sal_systab = EFI_INVALID_TABLE_ADDR,
-       .boot_info  = EFI_INVALID_TABLE_ADDR,
-       .hcdp       = EFI_INVALID_TABLE_ADDR,
-       .uga        = EFI_INVALID_TABLE_ADDR,
-       .uv_systab  = EFI_INVALID_TABLE_ADDR,
-       .fw_vendor  = EFI_INVALID_TABLE_ADDR,
-       .runtime    = EFI_INVALID_TABLE_ADDR,
-       .config_table  = EFI_INVALID_TABLE_ADDR,
-       .esrt       = EFI_INVALID_TABLE_ADDR,
+       .mps                    = EFI_INVALID_TABLE_ADDR,
+       .acpi                   = EFI_INVALID_TABLE_ADDR,
+       .acpi20                 = EFI_INVALID_TABLE_ADDR,
+       .smbios                 = EFI_INVALID_TABLE_ADDR,
+       .smbios3                = EFI_INVALID_TABLE_ADDR,
+       .sal_systab             = EFI_INVALID_TABLE_ADDR,
+       .boot_info              = EFI_INVALID_TABLE_ADDR,
+       .hcdp                   = EFI_INVALID_TABLE_ADDR,
+       .uga                    = EFI_INVALID_TABLE_ADDR,
+       .uv_systab              = EFI_INVALID_TABLE_ADDR,
+       .fw_vendor              = EFI_INVALID_TABLE_ADDR,
+       .runtime                = EFI_INVALID_TABLE_ADDR,
+       .config_table           = EFI_INVALID_TABLE_ADDR,
+       .esrt                   = EFI_INVALID_TABLE_ADDR,
+       .properties_table       = EFI_INVALID_TABLE_ADDR,
 };
 EXPORT_SYMBOL(efi);
 
@@ -63,6 +64,9 @@ static int __init parse_efi_cmdline(char *str)
                return -EINVAL;
        }
 
+       if (parse_option_str(str, "debug"))
+               set_bit(EFI_DBG, &efi.flags);
+
        if (parse_option_str(str, "noruntime"))
                disable_runtime = true;
 
@@ -250,7 +254,7 @@ subsys_initcall(efisubsys_init);
 int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
 {
        struct efi_memory_map *map = efi.memmap;
-       void *p, *e;
+       phys_addr_t p, e;
 
        if (!efi_enabled(EFI_MEMMAP)) {
                pr_err_once("EFI_MEMMAP is not enabled.\n");
@@ -282,10 +286,10 @@ int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
                 * So just always get our own virtual map on the CPU.
                 *
                 */
-               md = early_memremap((phys_addr_t)p, sizeof (*md));
+               md = early_memremap(p, sizeof (*md));
                if (!md) {
-                       pr_err_once("early_memremap(%p, %zu) failed.\n",
-                                   p, sizeof (*md));
+                       pr_err_once("early_memremap(%pa, %zu) failed.\n",
+                                   &p, sizeof (*md));
                        return -ENOMEM;
                }
 
@@ -362,6 +366,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
        {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3},
        {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
        {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
+       {EFI_PROPERTIES_TABLE_GUID, "PROP", &efi.properties_table},
        {NULL_GUID, NULL, NULL},
 };
 
@@ -421,6 +426,24 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz,
        }
        pr_cont("\n");
        set_bit(EFI_CONFIG_TABLES, &efi.flags);
+
+       /* Parse the EFI Properties table if it exists */
+       if (efi.properties_table != EFI_INVALID_TABLE_ADDR) {
+               efi_properties_table_t *tbl;
+
+               tbl = early_memremap(efi.properties_table, sizeof(*tbl));
+               if (tbl == NULL) {
+                       pr_err("Could not map Properties table!\n");
+                       return -ENOMEM;
+               }
+
+               if (tbl->memory_protection_attribute &
+                   EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA)
+                       set_bit(EFI_NX_PE_DATA, &efi.flags);
+
+               early_memunmap(tbl, sizeof(*tbl));
+       }
+
        return 0;
 }
 
@@ -489,7 +512,6 @@ static __initdata struct {
 };
 
 struct param_info {
-       int verbose;
        int found;
        void *params;
 };
@@ -520,21 +542,20 @@ static int __init fdt_find_uefi_params(unsigned long node, const char *uname,
                else
                        *(u64 *)dest = val;
 
-               if (info->verbose)
+               if (efi_enabled(EFI_DBG))
                        pr_info("  %s: 0x%0*llx\n", dt_params[i].name,
                                dt_params[i].size * 2, val);
        }
        return 1;
 }
 
-int __init efi_get_fdt_params(struct efi_fdt_params *params, int verbose)
+int __init efi_get_fdt_params(struct efi_fdt_params *params)
 {
        struct param_info info;
        int ret;
 
        pr_info("Getting EFI parameters from FDT:\n");
 
-       info.verbose = verbose;
        info.found = 0;
        info.params = params;
 
@@ -588,16 +609,19 @@ char * __init efi_md_typeattr_format(char *buf, size_t size,
 
        attr = md->attribute;
        if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT |
-                    EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP |
-                    EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RUNTIME))
+                    EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_RO |
+                    EFI_MEMORY_WP | EFI_MEMORY_RP | EFI_MEMORY_XP |
+                    EFI_MEMORY_RUNTIME | EFI_MEMORY_MORE_RELIABLE))
                snprintf(pos, size, "|attr=0x%016llx]",
                         (unsigned long long)attr);
        else
-               snprintf(pos, size, "|%3s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]",
+               snprintf(pos, size, "|%3s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]",
                         attr & EFI_MEMORY_RUNTIME ? "RUN" : "",
+                        attr & EFI_MEMORY_MORE_RELIABLE ? "MR" : "",
                         attr & EFI_MEMORY_XP      ? "XP"  : "",
                         attr & EFI_MEMORY_RP      ? "RP"  : "",
                         attr & EFI_MEMORY_WP      ? "WP"  : "",
+                        attr & EFI_MEMORY_RO      ? "RO"  : "",
                         attr & EFI_MEMORY_UCE     ? "UCE" : "",
                         attr & EFI_MEMORY_WB      ? "WB"  : "",
                         attr & EFI_MEMORY_WT      ? "WT"  : "",
@@ -605,3 +629,36 @@ char * __init efi_md_typeattr_format(char *buf, size_t size,
                         attr & EFI_MEMORY_UC      ? "UC"  : "");
        return buf;
 }
+
+/*
+ * efi_mem_attributes - lookup memmap attributes for physical address
+ * @phys_addr: the physical address to lookup
+ *
+ * Search in the EFI memory map for the region covering
+ * @phys_addr. Returns the EFI memory attributes if the region
+ * was found in the memory map, 0 otherwise.
+ *
+ * Despite being marked __weak, most architectures should *not*
+ * override this function. It is __weak solely for the benefit
+ * of ia64 which has a funky EFI memory map that doesn't work
+ * the same way as other architectures.
+ */
+u64 __weak efi_mem_attributes(unsigned long phys_addr)
+{
+       struct efi_memory_map *map;
+       efi_memory_desc_t *md;
+       void *p;
+
+       if (!efi_enabled(EFI_MEMMAP))
+               return 0;
+
+       map = efi.memmap;
+       for (p = map->map; p < map->map_end; p += map->desc_size) {
+               md = p;
+               if ((md->phys_addr <= phys_addr) &&
+                   (phys_addr < (md->phys_addr +
+                   (md->num_pages << EFI_PAGE_SHIFT))))
+                       return md->attribute;
+       }
+       return 0;
+}
index a5b95d61ae71efe1252978406ceb4b6bb922eb46..22c5285f77050f27d2d96a22b5ade945bae54316 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/kobject.h>
 #include <linux/list.h>
 #include <linux/memblock.h>
-#include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/types.h>
 
@@ -450,22 +449,10 @@ err:
        esrt = NULL;
        return error;
 }
+device_initcall(esrt_sysfs_init);
 
-static void __exit esrt_sysfs_exit(void)
-{
-       pr_debug("esrt-sysfs: unloading.\n");
-       cleanup_entry_list();
-       kset_unregister(esrt_kset);
-       sysfs_remove_group(esrt_kobj, &esrt_attr_group);
-       kfree(esrt);
-       esrt = NULL;
-       kobject_del(esrt_kobj);
-       kobject_put(esrt_kobj);
-}
-
-module_init(esrt_sysfs_init);
-module_exit(esrt_sysfs_exit);
-
+/*
 MODULE_AUTHOR("Peter Jones <pjones@redhat.com>");
 MODULE_DESCRIPTION("EFI System Resource Table support");
 MODULE_LICENSE("GPL");
+*/
diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c
new file mode 100644 (file)
index 0000000..ed3a854
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * fake_mem.c
+ *
+ * Copyright (C) 2015 FUJITSU LIMITED
+ * Author: Taku Izumi <izumi.taku@jp.fujitsu.com>
+ *
+ * This code introduces new boot option named "efi_fake_mem"
+ * By specifying this parameter, you can add arbitrary attribute to
+ * specific memory range by updating original (firmware provided) EFI
+ * memmap.
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ *
+ *  The full GNU General Public License is included in this distribution in
+ *  the file called "COPYING".
+ */
+
+#include <linux/kernel.h>
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/memblock.h>
+#include <linux/types.h>
+#include <linux/sort.h>
+#include <asm/efi.h>
+
+#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
+
+struct fake_mem {
+       struct range range;
+       u64 attribute;
+};
+static struct fake_mem fake_mems[EFI_MAX_FAKEMEM];
+static int nr_fake_mem;
+
+static int __init cmp_fake_mem(const void *x1, const void *x2)
+{
+       const struct fake_mem *m1 = x1;
+       const struct fake_mem *m2 = x2;
+
+       if (m1->range.start < m2->range.start)
+               return -1;
+       if (m1->range.start > m2->range.start)
+               return 1;
+       return 0;
+}
+
+void __init efi_fake_memmap(void)
+{
+       u64 start, end, m_start, m_end, m_attr;
+       int new_nr_map = memmap.nr_map;
+       efi_memory_desc_t *md;
+       phys_addr_t new_memmap_phy;
+       void *new_memmap;
+       void *old, *new;
+       int i;
+
+       if (!nr_fake_mem || !efi_enabled(EFI_MEMMAP))
+               return;
+
+       /* count up the number of EFI memory descriptor */
+       for (old = memmap.map; old < memmap.map_end; old += memmap.desc_size) {
+               md = old;
+               start = md->phys_addr;
+               end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
+
+               for (i = 0; i < nr_fake_mem; i++) {
+                       /* modifying range */
+                       m_start = fake_mems[i].range.start;
+                       m_end = fake_mems[i].range.end;
+
+                       if (m_start <= start) {
+                               /* split into 2 parts */
+                               if (start < m_end && m_end < end)
+                                       new_nr_map++;
+                       }
+                       if (start < m_start && m_start < end) {
+                               /* split into 3 parts */
+                               if (m_end < end)
+                                       new_nr_map += 2;
+                               /* split into 2 parts */
+                               if (end <= m_end)
+                                       new_nr_map++;
+                       }
+               }
+       }
+
+       /* allocate memory for new EFI memmap */
+       new_memmap_phy = memblock_alloc(memmap.desc_size * new_nr_map,
+                                       PAGE_SIZE);
+       if (!new_memmap_phy)
+               return;
+
+       /* create new EFI memmap */
+       new_memmap = early_memremap(new_memmap_phy,
+                                   memmap.desc_size * new_nr_map);
+       if (!new_memmap) {
+               memblock_free(new_memmap_phy, memmap.desc_size * new_nr_map);
+               return;
+       }
+
+       for (old = memmap.map, new = new_memmap;
+            old < memmap.map_end;
+            old += memmap.desc_size, new += memmap.desc_size) {
+
+               /* copy original EFI memory descriptor */
+               memcpy(new, old, memmap.desc_size);
+               md = new;
+               start = md->phys_addr;
+               end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
+
+               for (i = 0; i < nr_fake_mem; i++) {
+                       /* modifying range */
+                       m_start = fake_mems[i].range.start;
+                       m_end = fake_mems[i].range.end;
+                       m_attr = fake_mems[i].attribute;
+
+                       if (m_start <= start && end <= m_end)
+                               md->attribute |= m_attr;
+
+                       if (m_start <= start &&
+                           (start < m_end && m_end < end)) {
+                               /* first part */
+                               md->attribute |= m_attr;
+                               md->num_pages = (m_end - md->phys_addr + 1) >>
+                                       EFI_PAGE_SHIFT;
+                               /* latter part */
+                               new += memmap.desc_size;
+                               memcpy(new, old, memmap.desc_size);
+                               md = new;
+                               md->phys_addr = m_end + 1;
+                               md->num_pages = (end - md->phys_addr + 1) >>
+                                       EFI_PAGE_SHIFT;
+                       }
+
+                       if ((start < m_start && m_start < end) && m_end < end) {
+                               /* first part */
+                               md->num_pages = (m_start - md->phys_addr) >>
+                                       EFI_PAGE_SHIFT;
+                               /* middle part */
+                               new += memmap.desc_size;
+                               memcpy(new, old, memmap.desc_size);
+                               md = new;
+                               md->attribute |= m_attr;
+                               md->phys_addr = m_start;
+                               md->num_pages = (m_end - m_start + 1) >>
+                                       EFI_PAGE_SHIFT;
+                               /* last part */
+                               new += memmap.desc_size;
+                               memcpy(new, old, memmap.desc_size);
+                               md = new;
+                               md->phys_addr = m_end + 1;
+                               md->num_pages = (end - m_end) >>
+                                       EFI_PAGE_SHIFT;
+                       }
+
+                       if ((start < m_start && m_start < end) &&
+                           (end <= m_end)) {
+                               /* first part */
+                               md->num_pages = (m_start - md->phys_addr) >>
+                                       EFI_PAGE_SHIFT;
+                               /* latter part */
+                               new += memmap.desc_size;
+                               memcpy(new, old, memmap.desc_size);
+                               md = new;
+                               md->phys_addr = m_start;
+                               md->num_pages = (end - md->phys_addr + 1) >>
+                                       EFI_PAGE_SHIFT;
+                               md->attribute |= m_attr;
+                       }
+               }
+       }
+
+       /* swap into new EFI memmap */
+       efi_unmap_memmap();
+       memmap.map = new_memmap;
+       memmap.phys_map = new_memmap_phy;
+       memmap.nr_map = new_nr_map;
+       memmap.map_end = memmap.map + memmap.nr_map * memmap.desc_size;
+       set_bit(EFI_MEMMAP, &efi.flags);
+
+       /* print new EFI memmap */
+       efi_print_memmap();
+}
+
+static int __init setup_fake_mem(char *p)
+{
+       u64 start = 0, mem_size = 0, attribute = 0;
+       int i;
+
+       if (!p)
+               return -EINVAL;
+
+       while (*p != '\0') {
+               mem_size = memparse(p, &p);
+               if (*p == '@')
+                       start = memparse(p+1, &p);
+               else
+                       break;
+
+               if (*p == ':')
+                       attribute = simple_strtoull(p+1, &p, 0);
+               else
+                       break;
+
+               if (nr_fake_mem >= EFI_MAX_FAKEMEM)
+                       break;
+
+               fake_mems[nr_fake_mem].range.start = start;
+               fake_mems[nr_fake_mem].range.end = start + mem_size - 1;
+               fake_mems[nr_fake_mem].attribute = attribute;
+               nr_fake_mem++;
+
+               if (*p == ',')
+                       p++;
+       }
+
+       sort(fake_mems, nr_fake_mem, sizeof(struct fake_mem),
+            cmp_fake_mem, NULL);
+
+       for (i = 0; i < nr_fake_mem; i++)
+               pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]",
+                       fake_mems[i].attribute, fake_mems[i].range.start,
+                       fake_mems[i].range.end);
+
+       return *p == '\0' ? 0 : -EINVAL;
+}
+
+early_param("efi_fake_mem", setup_fake_mem);
index 8949b3f6f74d207f0bc935af11dd76cf956b49ba..b18bea08ff253398db70846445dca4ee3ea0eafc 100644 (file)
@@ -119,6 +119,13 @@ config GPIO_ALTERA
 
          If driver is built as a module it will be called gpio-altera.
 
+config GPIO_AMDPT
+       tristate "AMD Promontory GPIO support"
+       depends on ACPI
+       help
+         driver for GPIO functionality on Promontory IOHub
+         Require ACPI ASL code to enumerate as a platform device.
+
 config GPIO_BCM_KONA
        bool "Broadcom Kona GPIO"
        depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
@@ -176,16 +183,6 @@ config GPIO_ETRAXFS
        help
          Say yes here to support the GPIO controller on Axis ETRAX FS SoCs.
 
-config GPIO_F7188X
-       tristate "F71869, F71869A, F71882FG and F71889F GPIO support"
-       depends on X86
-       help
-         This option enables support for GPIOs found on Fintek Super-I/O
-         chips F71869, F71869A, F71882FG and F71889F.
-
-         To compile this driver as a module, choose M here: the module will
-         be called f7188x-gpio.
-
 config GPIO_GE_FPGA
        bool "GE FPGA based GPIO"
        depends on GE_FPGA
@@ -235,12 +232,6 @@ config GPIO_IOP
 
          If unsure, say N.
 
-config GPIO_IT8761E
-       tristate "IT8761E GPIO support"
-       depends on X86  # unconditional access to IO space.
-       help
-         Say yes here to support GPIO functionality of IT8761E super I/O chip.
-
 config GPIO_LOONGSON
        bool "Loongson-2/3 GPIO support"
        depends on CPU_LOONGSON2 || CPU_LOONGSON3
@@ -297,14 +288,6 @@ config GPIO_MPC8XXX
          Say Y here if you're going to use hardware that connects to the
          MPC512x/831x/834x/837x/8572/8610 GPIOs.
 
-config GPIO_MSM_V2
-       tristate "Qualcomm MSM GPIO v2"
-       depends on GPIOLIB && OF && ARCH_QCOM
-       help
-         Say yes here to support the GPIO interface on ARM v7 based
-         Qualcomm MSM chips.  Most of the pins on the MSM can be
-         selected for GPIO, and are controlled by this driver.
-
 config GPIO_MVEBU
        def_bool y
        depends on PLAT_ORION
@@ -368,42 +351,6 @@ config GPIO_SAMSUNG
          Legacy GPIO support. Use only for platforms without support for
          pinctrl.
 
-config GPIO_SCH
-       tristate "Intel SCH/TunnelCreek/Centerton/Quark X1000 GPIO"
-       depends on PCI && X86
-       select MFD_CORE
-       select LPC_SCH
-       help
-         Say yes here to support GPIO interface on Intel Poulsbo SCH,
-         Intel Tunnel Creek processor, Intel Centerton processor or
-         Intel Quark X1000 SoC.
-
-         The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
-         powered by the core power rail and are turned off during sleep
-         modes (S3 and higher). The remaining four GPIOs are powered by
-         the Intel SCH suspend power supply. These GPIOs remain
-         active during S3. The suspend powered GPIOs can be used to wake the
-         system from the Suspend-to-RAM state.
-
-         The Intel Tunnel Creek processor has 5 GPIOs powered by the
-         core power rail and 9 from suspend power supply.
-
-         The Intel Centerton processor has a total of 30 GPIO pins.
-         Twenty-one are powered by the core power rail and 9 from the
-         suspend power supply.
-
-         The Intel Quark X1000 SoC has 2 GPIOs powered by the core
-         power well and 6 from the suspend power well.
-
-config GPIO_SCH311X
-       tristate "SMSC SCH311x SuperI/O GPIO"
-       help
-         Driver to enable the GPIOs found on SMSC SMSC SCH3112, SCH3114 and
-         SCH3116 "Super I/O" chipsets.
-
-         To compile this driver as a module, choose M here: the module will
-         be called gpio-sch311x.
-
 config GPIO_SPEAR_SPICS
        bool "ST SPEAr13xx SPI Chip Select as GPIO support"
        depends on PLAT_SPEAR
@@ -440,15 +387,6 @@ config GPIO_TB10X
        select GENERIC_IRQ_CHIP
        select OF_GPIO
 
-config GPIO_TS5500
-       tristate "TS-5500 DIO blocks and compatibles"
-       depends on TS5500 || COMPILE_TEST
-       help
-         This driver supports Digital I/O exposed by pin blocks found on some
-         Technologic Systems platforms. It includes, but is not limited to, 3
-         blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
-         LCD port.
-
 config GPIO_TZ1090
        bool "Toumaz Xenif TZ1090 GPIO support"
        depends on SOC_TZ1090
@@ -508,13 +446,13 @@ config GPIO_XGENE_SB
 
 config GPIO_XILINX
        tristate "Xilinx GPIO support"
-       depends on OF_GPIO && (PPC || MICROBLAZE || ARCH_ZYNQ || X86)
+       depends on OF_GPIO
        help
          Say yes here to support the Xilinx FPGA GPIO device
 
 config GPIO_XLP
        tristate "Netlogic XLP GPIO support"
-       depends on CPU_XLP
+       depends on CPU_XLP && OF_GPIO
        select GPIOLIB_IRQCHIP
        help
          This driver provides support for GPIO interface on Netlogic XLP MIPS64
@@ -545,6 +483,87 @@ config GPIO_ZYNQ
        help
          Say yes here to support Xilinx Zynq GPIO controller.
 
+config GPIO_ZX
+       bool "ZTE ZX GPIO support"
+       select GPIOLIB_IRQCHIP
+       help
+         Say yes here to support the GPIO device on ZTE ZX SoCs.
+
+endmenu
+
+menu "Port-mapped I/O GPIO drivers"
+       depends on X86 # Unconditional I/O space access
+
+config GPIO_104_IDIO_16
+       tristate "ACCES 104-IDIO-16 GPIO support"
+       help
+         Enables GPIO support for the ACCES 104-IDIO-16 family.
+
+config GPIO_F7188X
+       tristate "F71869, F71869A, F71882FG and F71889F GPIO support"
+       help
+         This option enables support for GPIOs found on Fintek Super-I/O
+         chips F71869, F71869A, F71882FG and F71889F.
+
+         To compile this driver as a module, choose M here: the module will
+         be called f7188x-gpio.
+
+config GPIO_IT87
+       tristate "IT87xx GPIO support"
+       help
+         Say yes here to support GPIO functionality of IT87xx Super I/O chips.
+
+         This driver is tested with ITE IT8728 and IT8732 Super I/O chips, and
+         supports the IT8761E Super I/O chip as well.
+
+         To compile this driver as a module, choose M here: the module will
+         be called gpio_it87
+
+config GPIO_SCH
+       tristate "Intel SCH/TunnelCreek/Centerton/Quark X1000 GPIO"
+       depends on PCI
+       select MFD_CORE
+       select LPC_SCH
+       help
+         Say yes here to support GPIO interface on Intel Poulsbo SCH,
+         Intel Tunnel Creek processor, Intel Centerton processor or
+         Intel Quark X1000 SoC.
+
+         The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
+         powered by the core power rail and are turned off during sleep
+         modes (S3 and higher). The remaining four GPIOs are powered by
+         the Intel SCH suspend power supply. These GPIOs remain
+         active during S3. The suspend powered GPIOs can be used to wake the
+         system from the Suspend-to-RAM state.
+
+         The Intel Tunnel Creek processor has 5 GPIOs powered by the
+         core power rail and 9 from suspend power supply.
+
+         The Intel Centerton processor has a total of 30 GPIO pins.
+         Twenty-one are powered by the core power rail and 9 from the
+         suspend power supply.
+
+         The Intel Quark X1000 SoC has 2 GPIOs powered by the core
+         power well and 6 from the suspend power well.
+
+config GPIO_SCH311X
+       tristate "SMSC SCH311x SuperI/O GPIO"
+       help
+         Driver to enable the GPIOs found on SMSC SMSC SCH3112, SCH3114 and
+         SCH3116 "Super I/O" chipsets.
+
+         To compile this driver as a module, choose M here: the module will
+         be called gpio-sch311x.
+
+config GPIO_TS5500
+       tristate "TS-5500 DIO blocks and compatibles"
+       depends on TS5500 || COMPILE_TEST
+       help
+         This driver supports Digital I/O exposed by pin blocks found on some
+         Technologic Systems platforms. It includes, but is not limited to, 3
+         blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
+         LCD port.
+
 endmenu
 
 menu "I2C GPIO expanders"
@@ -552,7 +571,6 @@ menu "I2C GPIO expanders"
 
 config GPIO_ADP5588
        tristate "ADP5588 I2C GPIO expander"
-       depends on I2C
        help
          This option enables support for 18 GPIOs found
          on Analog Devices ADP5588 GPIO Expanders.
@@ -566,7 +584,7 @@ config GPIO_ADP5588_IRQ
 
 config GPIO_ADNP
        tristate "Avionic Design N-bit GPIO expander"
-       depends on I2C && OF_GPIO
+       depends on OF_GPIO
        select GPIOLIB_IRQCHIP
        help
          This option enables support for N GPIOs found on Avionic Design
@@ -578,14 +596,12 @@ config GPIO_ADNP
 
 config GPIO_MAX7300
        tristate "Maxim MAX7300 GPIO expander"
-       depends on I2C
        select GPIO_MAX730X
        help
          GPIO driver for Maxim MAX7300 I2C-based GPIO expander.
 
 config GPIO_MAX732X
        tristate "MAX7319, MAX7320-7327 I2C Port Expanders"
-       depends on I2C
        help
          Say yes here to support the MAX7319, MAX7320-7327 series of I2C
          Port Expanders. Each IO port on these chips has a fixed role of
@@ -618,7 +634,6 @@ config GPIO_MC9S08DZ60
 
 config GPIO_PCA953X
        tristate "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports"
-       depends on I2C
        help
          Say yes here to provide access to several register-oriented
          SMBus I/O expanders, made mostly by NXP or TI.  Compatible
@@ -646,7 +661,6 @@ config GPIO_PCA953X_IRQ
 
 config GPIO_PCF857X
        tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
-       depends on I2C
        select GPIOLIB_IRQCHIP
        select IRQ_DOMAIN
        help
@@ -976,7 +990,7 @@ menu "SPI GPIO expanders"
 
 config GPIO_74X164
        tristate "74x164 serial-in/parallel-out 8-bits shift register"
-       depends on SPI_MASTER && OF
+       depends on OF
        help
          Driver for 74x164 compatible serial-in/parallel-out 8-outputs
          shift registers. This driver can be used to provide access
@@ -984,32 +998,28 @@ config GPIO_74X164
 
 config GPIO_MAX7301
        tristate "Maxim MAX7301 GPIO expander"
-       depends on SPI_MASTER
        select GPIO_MAX730X
        help
          GPIO driver for Maxim MAX7301 SPI-based GPIO expander.
 
-config GPIO_MCP23S08
-       tristate "Microchip MCP23xxx I/O expander"
-       depends on (SPI_MASTER && !I2C) || I2C
-       help
-         SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
-         I/O expanders.
-         This provides a GPIO interface supporting inputs and outputs.
-         The I2C versions of the chips can be used as interrupt-controller.
-
 config GPIO_MC33880
        tristate "Freescale MC33880 high-side/low-side switch"
-       depends on SPI_MASTER
        help
          SPI driver for Freescale MC33880 high-side/low-side switch.
          This provides GPIO interface supporting inputs and outputs.
 
-config GPIO_ZX
-       bool "ZTE ZX GPIO support"
-       select GPIOLIB_IRQCHIP
+endmenu
+
+menu "SPI or I2C GPIO expanders"
+       depends on (SPI_MASTER && !I2C) || I2C
+
+config GPIO_MCP23S08
+       tristate "Microchip MCP23xxx I/O expander"
        help
-         Say yes here to support the GPIO device on ZTE ZX SoCs.
+         SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
+         I/O expanders.
+         This provides a GPIO interface supporting inputs and outputs.
+         The I2C versions of the chips can be used as interrupt-controller.
 
 endmenu
 
index f79a7c482a993c5cc534072398fa0ff2226798c4..986dbd838ceaceb03c42ffc15a704bf5f180b09d 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_GPIO_ACPI)               += gpiolib-acpi.o
 # Device drivers. Generally keep list sorted alphabetically
 obj-$(CONFIG_GPIO_GENERIC)     += gpio-generic.o
 
+obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o
 obj-$(CONFIG_GPIO_74X164)      += gpio-74x164.o
 obj-$(CONFIG_GPIO_74XX_MMIO)   += gpio-74xx-mmio.o
 obj-$(CONFIG_GPIO_ADNP)                += gpio-adnp.o
@@ -19,6 +20,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_AMD8111)     += gpio-amd8111.o
+obj-$(CONFIG_GPIO_AMDPT)       += gpio-amdpt.o
 obj-$(CONFIG_GPIO_ARIZONA)     += gpio-arizona.o
 obj-$(CONFIG_ATH79)            += gpio-ath79.o
 obj-$(CONFIG_GPIO_BCM_KONA)    += gpio-bcm-kona.o
@@ -40,7 +42,7 @@ obj-$(CONFIG_GPIO_GE_FPGA)    += gpio-ge.o
 obj-$(CONFIG_GPIO_GRGPIO)      += gpio-grgpio.o
 obj-$(CONFIG_GPIO_ICH)         += gpio-ich.o
 obj-$(CONFIG_GPIO_IOP)         += gpio-iop.o
-obj-$(CONFIG_GPIO_IT8761E)     += gpio-it8761e.o
+obj-$(CONFIG_GPIO_IT87)                += gpio-it87.o
 obj-$(CONFIG_GPIO_JANZ_TTL)    += gpio-janz-ttl.o
 obj-$(CONFIG_GPIO_KEMPLD)      += gpio-kempld.o
 obj-$(CONFIG_ARCH_KS8695)      += gpio-ks8695.o
@@ -64,7 +66,6 @@ obj-$(CONFIG_GPIO_MOXART)     += gpio-moxart.o
 obj-$(CONFIG_GPIO_MPC5200)     += gpio-mpc5200.o
 obj-$(CONFIG_GPIO_MPC8XXX)     += gpio-mpc8xxx.o
 obj-$(CONFIG_GPIO_MSIC)                += gpio-msic.o
-obj-$(CONFIG_GPIO_MSM_V2)      += gpio-msm-v2.o
 obj-$(CONFIG_GPIO_MVEBU)        += gpio-mvebu.o
 obj-$(CONFIG_GPIO_MXC)         += gpio-mxc.o
 obj-$(CONFIG_GPIO_MXS)         += gpio-mxs.o
diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c
new file mode 100644 (file)
index 0000000..5400d7d
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * GPIO driver for the ACCES 104-IDIO-16 family
+ * Copyright (C) 2015 William Breathitt Gray
+ *
+ * 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/device.h>
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+static unsigned idio_16_base;
+module_param(idio_16_base, uint, 0);
+MODULE_PARM_DESC(idio_16_base, "ACCES 104-IDIO-16 base address");
+
+/**
+ * struct idio_16_gpio - GPIO device private data structure
+ * @chip:      instance of the gpio_chip
+ * @lock:      synchronization lock to prevent gpio_set race conditions
+ * @base:      base port address of the GPIO device
+ * @extent:    extent of port address region of the GPIO device
+ * @out_state: output bits state
+ */
+struct idio_16_gpio {
+       struct gpio_chip chip;
+       spinlock_t lock;
+       unsigned base;
+       unsigned extent;
+       unsigned out_state;
+};
+
+static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+       if (offset > 15)
+               return 1;
+
+       return 0;
+}
+
+static int idio_16_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+       return 0;
+}
+
+static int idio_16_gpio_direction_output(struct gpio_chip *chip,
+       unsigned offset, int value)
+{
+       chip->set(chip, offset, value);
+       return 0;
+}
+
+static struct idio_16_gpio *to_idio16gpio(struct gpio_chip *gc)
+{
+       return container_of(gc, struct idio_16_gpio, chip);
+}
+
+static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip);
+       const unsigned BIT_MASK = 1U << (offset-16);
+
+       if (offset < 16)
+               return -EINVAL;
+
+       if (offset < 24)
+               return !!(inb(idio16gpio->base + 1) & BIT_MASK);
+
+       return !!(inb(idio16gpio->base + 5) & (BIT_MASK>>8));
+}
+
+static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip);
+       const unsigned BIT_MASK = 1U << offset;
+       unsigned long flags;
+
+       if (offset > 15)
+               return;
+
+       spin_lock_irqsave(&idio16gpio->lock, flags);
+
+       if (value)
+               idio16gpio->out_state |= BIT_MASK;
+       else
+               idio16gpio->out_state &= ~BIT_MASK;
+
+       if (offset > 7)
+               outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
+       else
+               outb(idio16gpio->out_state, idio16gpio->base);
+
+       spin_unlock_irqrestore(&idio16gpio->lock, flags);
+}
+
+static int __init idio_16_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct idio_16_gpio *idio16gpio;
+       int err;
+
+       const unsigned BASE = idio_16_base;
+       const unsigned EXTENT = 8;
+       const char *const NAME = dev_name(dev);
+
+       idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
+       if (!idio16gpio)
+               return -ENOMEM;
+
+       if (!request_region(BASE, EXTENT, NAME)) {
+               dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n",
+                       NAME, BASE, BASE + EXTENT);
+               err = -EBUSY;
+               goto err_lock_io_port;
+       }
+
+       idio16gpio->chip.label = NAME;
+       idio16gpio->chip.dev = dev;
+       idio16gpio->chip.owner = THIS_MODULE;
+       idio16gpio->chip.base = -1;
+       idio16gpio->chip.ngpio = 32;
+       idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
+       idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
+       idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
+       idio16gpio->chip.get = idio_16_gpio_get;
+       idio16gpio->chip.set = idio_16_gpio_set;
+       idio16gpio->base = BASE;
+       idio16gpio->extent = EXTENT;
+       idio16gpio->out_state = 0xFFFF;
+
+       spin_lock_init(&idio16gpio->lock);
+
+       dev_set_drvdata(dev, idio16gpio);
+
+       err = gpiochip_add(&idio16gpio->chip);
+       if (err) {
+               dev_err(dev, "GPIO registering failed (%d)\n", err);
+               goto err_gpio_register;
+       }
+
+       return 0;
+
+err_gpio_register:
+       release_region(BASE, EXTENT);
+err_lock_io_port:
+       return err;
+}
+
+static int idio_16_remove(struct platform_device *pdev)
+{
+       struct idio_16_gpio *const idio16gpio = platform_get_drvdata(pdev);
+
+       gpiochip_remove(&idio16gpio->chip);
+       release_region(idio16gpio->base, idio16gpio->extent);
+
+       return 0;
+}
+
+static struct platform_device *idio_16_device;
+
+static struct platform_driver idio_16_driver = {
+       .driver = {
+               .name = "104-idio-16"
+       },
+       .remove = idio_16_remove
+};
+
+static void __exit idio_16_exit(void)
+{
+       platform_device_unregister(idio_16_device);
+       platform_driver_unregister(&idio_16_driver);
+}
+
+static int __init idio_16_init(void)
+{
+       int err;
+
+       idio_16_device = platform_device_alloc(idio_16_driver.driver.name, -1);
+       if (!idio_16_device)
+               return -ENOMEM;
+
+       err = platform_device_add(idio_16_device);
+       if (err)
+               goto err_platform_device;
+
+       err = platform_driver_probe(&idio_16_driver, idio_16_probe);
+       if (err)
+               goto err_platform_driver;
+
+       return 0;
+
+err_platform_driver:
+       platform_device_del(idio_16_device);
+err_platform_device:
+       platform_device_put(idio_16_device);
+       return err;
+}
+
+module_init(idio_16_init);
+module_exit(idio_16_exit);
+
+MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
+MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
+MODULE_LICENSE("GPL");
index 1b44941574fa6965d68e474b9197907e71473692..3e6661bab54aed3a7fe05c6c5748622032017b46 100644 (file)
@@ -42,6 +42,11 @@ struct altera_gpio_chip {
        int mapped_irq;
 };
 
+static struct altera_gpio_chip *to_altera(struct gpio_chip *gc)
+{
+       return container_of(gc, struct altera_gpio_chip, mmchip.gc);
+}
+
 static void altera_gpio_irq_unmask(struct irq_data *d)
 {
        struct altera_gpio_chip *altera_gc;
@@ -49,7 +54,7 @@ static void altera_gpio_irq_unmask(struct irq_data *d)
        unsigned long flags;
        u32 intmask;
 
-       altera_gc = irq_data_get_irq_chip_data(d);
+       altera_gc = to_altera(irq_data_get_irq_chip_data(d));
        mm_gc = &altera_gc->mmchip;
 
        spin_lock_irqsave(&altera_gc->gpio_lock, flags);
@@ -67,7 +72,7 @@ static void altera_gpio_irq_mask(struct irq_data *d)
        unsigned long flags;
        u32 intmask;
 
-       altera_gc = irq_data_get_irq_chip_data(d);
+       altera_gc = to_altera(irq_data_get_irq_chip_data(d));
        mm_gc = &altera_gc->mmchip;
 
        spin_lock_irqsave(&altera_gc->gpio_lock, flags);
@@ -87,7 +92,7 @@ static int altera_gpio_irq_set_type(struct irq_data *d,
 {
        struct altera_gpio_chip *altera_gc;
 
-       altera_gc = irq_data_get_irq_chip_data(d);
+       altera_gc = to_altera(irq_data_get_irq_chip_data(d));
 
        if (type == IRQ_TYPE_NONE)
                return 0;
@@ -210,7 +215,7 @@ static void altera_gpio_irq_edge_handler(struct irq_desc *desc)
        unsigned long status;
        int i;
 
-       altera_gc = irq_desc_get_handler_data(desc);
+       altera_gc = to_altera(irq_desc_get_handler_data(desc));
        chip = irq_desc_get_chip(desc);
        mm_gc = &altera_gc->mmchip;
        irqdomain = altera_gc->mmchip.gc.irqdomain;
@@ -239,7 +244,7 @@ static void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc)
        unsigned long status;
        int i;
 
-       altera_gc = irq_desc_get_handler_data(desc);
+       altera_gc = to_altera(irq_desc_get_handler_data(desc));
        chip = irq_desc_get_chip(desc);
        mm_gc = &altera_gc->mmchip;
        irqdomain = altera_gc->mmchip.gc.irqdomain;
diff --git a/drivers/gpio/gpio-amdpt.c b/drivers/gpio/gpio-amdpt.c
new file mode 100644 (file)
index 0000000..cbbb966
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * AMD Promontory GPIO driver
+ *
+ * Copyright (C) 2015 ASMedia Technology Inc.
+ * Author: YD Tseng <yd_tseng@asmedia.com.tw>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio/driver.h>
+#include <linux/spinlock.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+
+#define PT_TOTAL_GPIO 8
+
+/* PCI-E MMIO register offsets */
+#define PT_DIRECTION_REG   0x00
+#define PT_INPUTDATA_REG   0x04
+#define PT_OUTPUTDATA_REG  0x08
+#define PT_CLOCKRATE_REG   0x0C
+#define PT_SYNC_REG        0x28
+
+struct pt_gpio_chip {
+       struct gpio_chip         gc;
+       void __iomem             *reg_base;
+       spinlock_t               lock;
+};
+
+#define to_pt_gpio(c)  container_of(c, struct pt_gpio_chip, gc)
+
+static int pt_gpio_request(struct gpio_chip *gc, unsigned offset)
+{
+       struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
+       unsigned long flags;
+       u32 using_pins;
+
+       dev_dbg(gc->dev, "pt_gpio_request offset=%x\n", offset);
+
+       spin_lock_irqsave(&pt_gpio->lock, flags);
+
+       using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
+       if (using_pins & BIT(offset)) {
+               dev_warn(gc->dev, "PT GPIO pin %x reconfigured\n",
+                       offset);
+               spin_unlock_irqrestore(&pt_gpio->lock, flags);
+               return -EINVAL;
+       }
+
+       writel(using_pins | BIT(offset), pt_gpio->reg_base + PT_SYNC_REG);
+
+       spin_unlock_irqrestore(&pt_gpio->lock, flags);
+
+       return 0;
+}
+
+static void pt_gpio_free(struct gpio_chip *gc, unsigned offset)
+{
+       struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
+       unsigned long flags;
+       u32 using_pins;
+
+       spin_lock_irqsave(&pt_gpio->lock, flags);
+
+       using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
+       using_pins &= ~BIT(offset);
+       writel(using_pins, pt_gpio->reg_base + PT_SYNC_REG);
+
+       spin_unlock_irqrestore(&pt_gpio->lock, flags);
+
+       dev_dbg(gc->dev, "pt_gpio_free offset=%x\n", offset);
+}
+
+static void pt_gpio_set_value(struct gpio_chip *gc, unsigned offset, int value)
+{
+       struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
+       unsigned long flags;
+       u32 data;
+
+       dev_dbg(gc->dev, "pt_gpio_set_value offset=%x, value=%x\n",
+               offset, value);
+
+       spin_lock_irqsave(&pt_gpio->lock, flags);
+
+       data = readl(pt_gpio->reg_base + PT_OUTPUTDATA_REG);
+       data &= ~BIT(offset);
+       if (value)
+               data |= BIT(offset);
+       writel(data, pt_gpio->reg_base + PT_OUTPUTDATA_REG);
+
+       spin_unlock_irqrestore(&pt_gpio->lock, flags);
+}
+
+static int pt_gpio_get_value(struct gpio_chip *gc, unsigned offset)
+{
+       struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
+       unsigned long flags;
+       u32 data;
+
+       spin_lock_irqsave(&pt_gpio->lock, flags);
+
+       data = readl(pt_gpio->reg_base + PT_DIRECTION_REG);
+
+       /* configure as output */
+       if (data & BIT(offset))
+               data = readl(pt_gpio->reg_base + PT_OUTPUTDATA_REG);
+       else    /* configure as input */
+               data = readl(pt_gpio->reg_base + PT_INPUTDATA_REG);
+
+       spin_unlock_irqrestore(&pt_gpio->lock, flags);
+
+       data >>= offset;
+       data &= 1;
+
+       dev_dbg(gc->dev, "pt_gpio_get_value offset=%x, value=%x\n",
+               offset, data);
+
+       return data;
+}
+
+static int pt_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+       struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
+       unsigned long flags;
+       u32 data;
+
+       dev_dbg(gc->dev, "pt_gpio_dirction_input offset=%x\n", offset);
+
+       spin_lock_irqsave(&pt_gpio->lock, flags);
+
+       data = readl(pt_gpio->reg_base + PT_DIRECTION_REG);
+       data &= ~BIT(offset);
+       writel(data, pt_gpio->reg_base + PT_DIRECTION_REG);
+
+       spin_unlock_irqrestore(&pt_gpio->lock, flags);
+
+       return 0;
+}
+
+static int pt_gpio_direction_output(struct gpio_chip *gc,
+                                       unsigned offset, int value)
+{
+       struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
+       unsigned long flags;
+       u32 data;
+
+       dev_dbg(gc->dev, "pt_gpio_direction_output offset=%x, value=%x\n",
+               offset, value);
+
+       spin_lock_irqsave(&pt_gpio->lock, flags);
+
+       data = readl(pt_gpio->reg_base + PT_OUTPUTDATA_REG);
+       if (value)
+               data |= BIT(offset);
+       else
+               data &= ~BIT(offset);
+       writel(data, pt_gpio->reg_base + PT_OUTPUTDATA_REG);
+
+       data = readl(pt_gpio->reg_base + PT_DIRECTION_REG);
+       data |= BIT(offset);
+       writel(data, pt_gpio->reg_base + PT_DIRECTION_REG);
+
+       spin_unlock_irqrestore(&pt_gpio->lock, flags);
+
+       return 0;
+}
+
+static int pt_gpio_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct acpi_device *acpi_dev;
+       acpi_handle handle = ACPI_HANDLE(dev);
+       struct pt_gpio_chip *pt_gpio;
+       struct resource *res_mem;
+       int ret = 0;
+
+       if (acpi_bus_get_device(handle, &acpi_dev)) {
+               dev_err(dev, "PT GPIO device node not found\n");
+               return -ENODEV;
+       }
+
+       pt_gpio = devm_kzalloc(dev, sizeof(struct pt_gpio_chip), GFP_KERNEL);
+       if (!pt_gpio)
+               return -ENOMEM;
+
+       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res_mem) {
+               dev_err(&pdev->dev, "Failed to get MMIO resource for PT GPIO.\n");
+               return -EINVAL;
+       }
+       pt_gpio->reg_base = devm_ioremap_resource(dev, res_mem);
+       if (IS_ERR(pt_gpio->reg_base)) {
+               dev_err(&pdev->dev, "Failed to map MMIO resource for PT GPIO.\n");
+               return PTR_ERR(pt_gpio->reg_base);
+       }
+
+       spin_lock_init(&pt_gpio->lock);
+
+       pt_gpio->gc.label            = pdev->name;
+       pt_gpio->gc.owner            = THIS_MODULE;
+       pt_gpio->gc.dev              = dev;
+       pt_gpio->gc.request          = pt_gpio_request;
+       pt_gpio->gc.free             = pt_gpio_free;
+       pt_gpio->gc.direction_input  = pt_gpio_direction_input;
+       pt_gpio->gc.direction_output = pt_gpio_direction_output;
+       pt_gpio->gc.get              = pt_gpio_get_value;
+       pt_gpio->gc.set              = pt_gpio_set_value;
+       pt_gpio->gc.base             = -1;
+       pt_gpio->gc.ngpio            = PT_TOTAL_GPIO;
+#if defined(CONFIG_OF_GPIO)
+       pt_gpio->gc.of_node          = pdev->dev.of_node;
+#endif
+       ret = gpiochip_add(&pt_gpio->gc);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register GPIO lib\n");
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, pt_gpio);
+
+       /* initialize register setting */
+       writel(0, pt_gpio->reg_base + PT_SYNC_REG);
+       writel(0, pt_gpio->reg_base + PT_CLOCKRATE_REG);
+
+       dev_dbg(&pdev->dev, "PT GPIO driver loaded\n");
+       return ret;
+}
+
+static int pt_gpio_remove(struct platform_device *pdev)
+{
+       struct pt_gpio_chip *pt_gpio = platform_get_drvdata(pdev);
+
+       gpiochip_remove(&pt_gpio->gc);
+
+       return 0;
+}
+
+static const struct acpi_device_id pt_gpio_acpi_match[] = {
+       { "AMDF030", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, pt_gpio_acpi_match);
+
+static struct platform_driver pt_gpio_driver = {
+       .driver = {
+               .name = "pt-gpio",
+               .acpi_match_table = ACPI_PTR(pt_gpio_acpi_match),
+       },
+       .probe = pt_gpio_probe,
+       .remove = pt_gpio_remove,
+};
+
+module_platform_driver(pt_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("YD Tseng <yd_tseng@asmedia.com.tw>");
+MODULE_DESCRIPTION("AMD Promontory GPIO Driver");
index 052fbc8fdaaa69da322297e6497a13778f3dabe3..ca002739616af3969431482de5a9df6a6e96a975 100644 (file)
@@ -118,6 +118,8 @@ static int arizona_gpio_probe(struct platform_device *pdev)
        case WM5110:
        case WM8280:
        case WM8997:
+       case WM8998:
+       case WM1814:
                arizona_gpio->gpio_chip.ngpio = 5;
                break;
        default:
index 03b995304ad68292046b2d7f2d231d6c79ee727c..e5827a56ff3b5eda451202849c41b2d8b0e54f31 100644 (file)
  *  by the Free Software Foundation.
  */
 
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/spinlock.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
 #include <linux/platform_data/gpio-ath79.h>
 #include <linux/of_device.h>
 
 #include <asm/mach-ath79/ar71xx_regs.h>
 
-static void __iomem *ath79_gpio_base;
-static u32 ath79_gpio_count;
-static DEFINE_SPINLOCK(ath79_gpio_lock);
+struct ath79_gpio_ctrl {
+       struct gpio_chip chip;
+       void __iomem *base;
+       spinlock_t lock;
+};
+
+#define to_ath79_gpio_ctrl(c) container_of(c, struct ath79_gpio_ctrl, chip)
 
-static void __ath79_gpio_set_value(unsigned gpio, int value)
+static void ath79_gpio_set_value(struct gpio_chip *chip,
+                               unsigned gpio, int value)
 {
-       void __iomem *base = ath79_gpio_base;
+       struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
 
        if (value)
-               __raw_writel(1 << gpio, base + AR71XX_GPIO_REG_SET);
+               __raw_writel(BIT(gpio), ctrl->base + AR71XX_GPIO_REG_SET);
        else
-               __raw_writel(1 << gpio, base + AR71XX_GPIO_REG_CLEAR);
+               __raw_writel(BIT(gpio), ctrl->base + AR71XX_GPIO_REG_CLEAR);
 }
 
-static int __ath79_gpio_get_value(unsigned gpio)
+static int ath79_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
 {
-       return (__raw_readl(ath79_gpio_base + AR71XX_GPIO_REG_IN) >> gpio) & 1;
-}
+       struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
 
-static int ath79_gpio_get_value(struct gpio_chip *chip, unsigned offset)
-{
-       return __ath79_gpio_get_value(offset);
-}
-
-static void ath79_gpio_set_value(struct gpio_chip *chip,
-                                 unsigned offset, int value)
-{
-       __ath79_gpio_set_value(offset, value);
+       return (__raw_readl(ctrl->base + AR71XX_GPIO_REG_IN) >> gpio) & 1;
 }
 
 static int ath79_gpio_direction_input(struct gpio_chip *chip,
                                       unsigned offset)
 {
-       void __iomem *base = ath79_gpio_base;
+       struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
        unsigned long flags;
 
-       spin_lock_irqsave(&ath79_gpio_lock, flags);
+       spin_lock_irqsave(&ctrl->lock, flags);
 
-       __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) & ~(1 << offset),
-                    base + AR71XX_GPIO_REG_OE);
+       __raw_writel(
+               __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & ~BIT(offset),
+               ctrl->base + AR71XX_GPIO_REG_OE);
 
-       spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+       spin_unlock_irqrestore(&ctrl->lock, flags);
 
        return 0;
 }
@@ -74,35 +64,37 @@ static int ath79_gpio_direction_input(struct gpio_chip *chip,
 static int ath79_gpio_direction_output(struct gpio_chip *chip,
                                        unsigned offset, int value)
 {
-       void __iomem *base = ath79_gpio_base;
+       struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
        unsigned long flags;
 
-       spin_lock_irqsave(&ath79_gpio_lock, flags);
+       spin_lock_irqsave(&ctrl->lock, flags);
 
        if (value)
-               __raw_writel(1 << offset, base + AR71XX_GPIO_REG_SET);
+               __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_SET);
        else
-               __raw_writel(1 << offset, base + AR71XX_GPIO_REG_CLEAR);
+               __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_CLEAR);
 
-       __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) | (1 << offset),
-                    base + AR71XX_GPIO_REG_OE);
+       __raw_writel(
+               __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) | BIT(offset),
+               ctrl->base + AR71XX_GPIO_REG_OE);
 
-       spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+       spin_unlock_irqrestore(&ctrl->lock, flags);
 
        return 0;
 }
 
 static int ar934x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
 {
-       void __iomem *base = ath79_gpio_base;
+       struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
        unsigned long flags;
 
-       spin_lock_irqsave(&ath79_gpio_lock, flags);
+       spin_lock_irqsave(&ctrl->lock, flags);
 
-       __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) | (1 << offset),
-                    base + AR71XX_GPIO_REG_OE);
+       __raw_writel(
+               __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) | BIT(offset),
+               ctrl->base + AR71XX_GPIO_REG_OE);
 
-       spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+       spin_unlock_irqrestore(&ctrl->lock, flags);
 
        return 0;
 }
@@ -110,25 +102,26 @@ static int ar934x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
 static int ar934x_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
                                        int value)
 {
-       void __iomem *base = ath79_gpio_base;
+       struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
        unsigned long flags;
 
-       spin_lock_irqsave(&ath79_gpio_lock, flags);
+       spin_lock_irqsave(&ctrl->lock, flags);
 
        if (value)
-               __raw_writel(1 << offset, base + AR71XX_GPIO_REG_SET);
+               __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_SET);
        else
-               __raw_writel(1 << offset, base + AR71XX_GPIO_REG_CLEAR);
+               __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_CLEAR);
 
-       __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) & ~(1 << offset),
-                    base + AR71XX_GPIO_REG_OE);
+       __raw_writel(
+               __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & BIT(offset),
+               ctrl->base + AR71XX_GPIO_REG_OE);
 
-       spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+       spin_unlock_irqrestore(&ctrl->lock, flags);
 
        return 0;
 }
 
-static struct gpio_chip ath79_gpio_chip = {
+static const struct gpio_chip ath79_gpio_chip = {
        .label                  = "ath79",
        .get                    = ath79_gpio_get_value,
        .set                    = ath79_gpio_set_value,
@@ -147,10 +140,16 @@ static int ath79_gpio_probe(struct platform_device *pdev)
 {
        struct ath79_gpio_platform_data *pdata = pdev->dev.platform_data;
        struct device_node *np = pdev->dev.of_node;
+       struct ath79_gpio_ctrl *ctrl;
        struct resource *res;
+       u32 ath79_gpio_count;
        bool oe_inverted;
        int err;
 
+       ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
+       if (!ctrl)
+               return -ENOMEM;
+
        if (np) {
                err = of_property_read_u32(np, "ngpios", &ath79_gpio_count);
                if (err) {
@@ -171,19 +170,21 @@ static int ath79_gpio_probe(struct platform_device *pdev)
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       ath79_gpio_base = devm_ioremap_nocache(
+       ctrl->base = devm_ioremap_nocache(
                &pdev->dev, res->start, resource_size(res));
-       if (!ath79_gpio_base)
+       if (!ctrl->base)
                return -ENOMEM;
 
-       ath79_gpio_chip.dev = &pdev->dev;
-       ath79_gpio_chip.ngpio = ath79_gpio_count;
+       spin_lock_init(&ctrl->lock);
+       memcpy(&ctrl->chip, &ath79_gpio_chip, sizeof(ctrl->chip));
+       ctrl->chip.dev = &pdev->dev;
+       ctrl->chip.ngpio = ath79_gpio_count;
        if (oe_inverted) {
-               ath79_gpio_chip.direction_input = ar934x_gpio_direction_input;
-               ath79_gpio_chip.direction_output = ar934x_gpio_direction_output;
+               ctrl->chip.direction_input = ar934x_gpio_direction_input;
+               ctrl->chip.direction_output = ar934x_gpio_direction_output;
        }
 
-       err = gpiochip_add(&ath79_gpio_chip);
+       err = gpiochip_add(&ctrl->chip);
        if (err) {
                dev_err(&pdev->dev,
                        "cannot add AR71xx GPIO chip, error=%d", err);
index 2ffcd9fdd1f2f7d3df917aa0324a07e3f23b17c8..5c15dd12172db3cf38945dc347f9c1e71ac81971 100644 (file)
@@ -176,6 +176,11 @@ static const struct etraxfs_gpio_info etraxfs_gpio_artpec3 = {
        .rw_intr_pins   = ARTPEC3_rw_intr_pins,
 };
 
+static struct etraxfs_gpio_chip *to_etraxfs(struct gpio_chip *gc)
+{
+       return container_of(gc, struct etraxfs_gpio_chip, bgc.gc);
+}
+
 static unsigned int etraxfs_gpio_chip_to_port(struct gpio_chip *gc)
 {
        return gc->label[0] - 'A';
@@ -220,7 +225,8 @@ static unsigned int etraxfs_gpio_to_group_pin(struct etraxfs_gpio_chip *chip,
 
 static void etraxfs_gpio_irq_ack(struct irq_data *d)
 {
-       struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
+       struct etraxfs_gpio_chip *chip =
+               to_etraxfs(irq_data_get_irq_chip_data(d));
        struct etraxfs_gpio_block *block = chip->block;
        unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
 
@@ -229,7 +235,8 @@ static void etraxfs_gpio_irq_ack(struct irq_data *d)
 
 static void etraxfs_gpio_irq_mask(struct irq_data *d)
 {
-       struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
+       struct etraxfs_gpio_chip *chip =
+               to_etraxfs(irq_data_get_irq_chip_data(d));
        struct etraxfs_gpio_block *block = chip->block;
        unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
 
@@ -241,7 +248,8 @@ static void etraxfs_gpio_irq_mask(struct irq_data *d)
 
 static void etraxfs_gpio_irq_unmask(struct irq_data *d)
 {
-       struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
+       struct etraxfs_gpio_chip *chip =
+               to_etraxfs(irq_data_get_irq_chip_data(d));
        struct etraxfs_gpio_block *block = chip->block;
        unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
 
@@ -253,7 +261,8 @@ static void etraxfs_gpio_irq_unmask(struct irq_data *d)
 
 static int etraxfs_gpio_irq_set_type(struct irq_data *d, u32 type)
 {
-       struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
+       struct etraxfs_gpio_chip *chip =
+               to_etraxfs(irq_data_get_irq_chip_data(d));
        struct etraxfs_gpio_block *block = chip->block;
        unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
        u32 cfg;
@@ -289,7 +298,8 @@ static int etraxfs_gpio_irq_set_type(struct irq_data *d, u32 type)
 
 static int etraxfs_gpio_irq_request_resources(struct irq_data *d)
 {
-       struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
+       struct etraxfs_gpio_chip *chip =
+               to_etraxfs(irq_data_get_irq_chip_data(d));
        struct etraxfs_gpio_block *block = chip->block;
        unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
        int ret = -EBUSY;
@@ -319,7 +329,8 @@ out:
 
 static void etraxfs_gpio_irq_release_resources(struct irq_data *d)
 {
-       struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
+       struct etraxfs_gpio_chip *chip =
+               to_etraxfs(irq_data_get_irq_chip_data(d));
        struct etraxfs_gpio_block *block = chip->block;
        unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
 
index a3f07537fe6250864e7e829b97821555e79bf426..bd5193c67a9c272cb0bebf779ce3277d22aac479 100644 (file)
@@ -579,40 +579,20 @@ EXPORT_SYMBOL_GPL(bgpio_init);
 
 static void __iomem *bgpio_map(struct platform_device *pdev,
                               const char *name,
-                              resource_size_t sane_sz,
-                              int *err)
+                              resource_size_t sane_sz)
 {
-       struct device *dev = &pdev->dev;
        struct resource *r;
-       resource_size_t start;
        resource_size_t sz;
-       void __iomem *ret;
-
-       *err = 0;
 
        r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
        if (!r)
                return NULL;
 
        sz = resource_size(r);
-       if (sz != sane_sz) {
-               *err = -EINVAL;
-               return NULL;
-       }
-
-       start = r->start;
-       if (!devm_request_mem_region(dev, start, sz, r->name)) {
-               *err = -EBUSY;
-               return NULL;
-       }
-
-       ret = devm_ioremap(dev, start, sz);
-       if (!ret) {
-               *err = -ENOMEM;
-               return NULL;
-       }
+       if (sz != sane_sz)
+               return IOMEM_ERR_PTR(-EINVAL);
 
-       return ret;
+       return devm_ioremap_resource(&pdev->dev, r);
 }
 
 static int bgpio_pdev_probe(struct platform_device *pdev)
@@ -636,25 +616,25 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
 
        sz = resource_size(r);
 
-       dat = bgpio_map(pdev, "dat", sz, &err);
-       if (!dat)
-               return err ? err : -EINVAL;
+       dat = bgpio_map(pdev, "dat", sz);
+       if (IS_ERR(dat))
+               return PTR_ERR(dat);
 
-       set = bgpio_map(pdev, "set", sz, &err);
-       if (err)
-               return err;
+       set = bgpio_map(pdev, "set", sz);
+       if (IS_ERR(set))
+               return PTR_ERR(set);
 
-       clr = bgpio_map(pdev, "clr", sz, &err);
-       if (err)
-               return err;
+       clr = bgpio_map(pdev, "clr", sz);
+       if (IS_ERR(clr))
+               return PTR_ERR(clr);
 
-       dirout = bgpio_map(pdev, "dirout", sz, &err);
-       if (err)
-               return err;
+       dirout = bgpio_map(pdev, "dirout", sz);
+       if (IS_ERR(dirout))
+               return PTR_ERR(dirout);
 
-       dirin = bgpio_map(pdev, "dirin", sz, &err);
-       if (err)
-               return err;
+       dirin = bgpio_map(pdev, "dirin", sz);
+       if (IS_ERR(dirin))
+               return PTR_ERR(dirin);
 
        bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL);
        if (!bgc)
diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c
new file mode 100644 (file)
index 0000000..21f6f7c
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ *  GPIO interface for IT87xx Super I/O chips
+ *
+ *  Author: Diego Elio Pettenò <flameeyes@flameeyes.eu>
+ *
+ *  Based on it87_wdt.c     by Oliver Schuster
+ *           gpio-it8761e.c by Denis Turischev
+ *           gpio-stmpe.c   by Rabin Vincent
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+/* Chip Id numbers */
+#define NO_DEV_ID      0xffff
+#define IT8728_ID      0x8728
+#define IT8732_ID      0x8732
+#define IT8761_ID      0x8761
+
+/* IO Ports */
+#define REG            0x2e
+#define VAL            0x2f
+
+/* Logical device Numbers LDN */
+#define GPIO           0x07
+
+/* Configuration Registers and Functions */
+#define LDNREG         0x07
+#define CHIPID         0x20
+#define CHIPREV                0x22
+
+/**
+ * struct it87_gpio - it87-specific GPIO chip
+ * @chip the underlying gpio_chip structure
+ * @lock a lock to avoid races between operations
+ * @io_base base address for gpio ports
+ * @io_size size of the port rage starting from io_base.
+ * @output_base Super I/O register address for Output Enable register
+ * @simple_base Super I/O 'Simple I/O' Enable register
+ * @simple_size Super IO 'Simple I/O' Enable register size; this is
+ *     required because IT87xx chips might only provide Simple I/O
+ *     switches on a subset of lines, whereas the others keep the
+ *     same status all time.
+ */
+struct it87_gpio {
+       struct gpio_chip chip;
+       spinlock_t lock;
+       u16 io_base;
+       u16 io_size;
+       u8 output_base;
+       u8 simple_base;
+       u8 simple_size;
+};
+
+static struct it87_gpio it87_gpio_chip = {
+       .lock = __SPIN_LOCK_UNLOCKED(it87_gpio_chip.lock),
+};
+
+static inline struct it87_gpio *to_it87_gpio(struct gpio_chip *chip)
+{
+       return container_of(chip, struct it87_gpio, chip);
+}
+
+/* Superio chip access functions; copied from wdt_it87 */
+
+static inline int superio_enter(void)
+{
+       /*
+        * Try to reserve REG and REG + 1 for exclusive access.
+        */
+       if (!request_muxed_region(REG, 2, KBUILD_MODNAME))
+               return -EBUSY;
+
+       outb(0x87, REG);
+       outb(0x01, REG);
+       outb(0x55, REG);
+       outb(0x55, REG);
+       return 0;
+}
+
+static inline void superio_exit(void)
+{
+       outb(0x02, REG);
+       outb(0x02, VAL);
+       release_region(REG, 2);
+}
+
+static inline void superio_select(int ldn)
+{
+       outb(LDNREG, REG);
+       outb(ldn, VAL);
+}
+
+static inline int superio_inb(int reg)
+{
+       outb(reg, REG);
+       return inb(VAL);
+}
+
+static inline void superio_outb(int val, int reg)
+{
+       outb(reg, REG);
+       outb(val, VAL);
+}
+
+static inline int superio_inw(int reg)
+{
+       int val;
+
+       outb(reg++, REG);
+       val = inb(VAL) << 8;
+       outb(reg, REG);
+       val |= inb(VAL);
+       return val;
+}
+
+static inline void superio_outw(int val, int reg)
+{
+       outb(reg++, REG);
+       outb(val >> 8, VAL);
+       outb(reg, REG);
+       outb(val, VAL);
+}
+
+static inline void superio_set_mask(int mask, int reg)
+{
+       u8 curr_val = superio_inb(reg);
+       u8 new_val = curr_val | mask;
+
+       if (curr_val != new_val)
+               superio_outb(new_val, reg);
+}
+
+static inline void superio_clear_mask(int mask, int reg)
+{
+       u8 curr_val = superio_inb(reg);
+       u8 new_val = curr_val & ~mask;
+
+       if (curr_val != new_val)
+               superio_outb(new_val, reg);
+}
+
+static int it87_gpio_request(struct gpio_chip *chip, unsigned gpio_num)
+{
+       u8 mask, group;
+       int rc = 0;
+       struct it87_gpio *it87_gpio = to_it87_gpio(chip);
+
+       mask = 1 << (gpio_num % 8);
+       group = (gpio_num / 8);
+
+       spin_lock(&it87_gpio->lock);
+
+       rc = superio_enter();
+       if (rc)
+               goto exit;
+
+       /* not all the IT87xx chips support Simple I/O and not all of
+        * them allow all the lines to be set/unset to Simple I/O.
+        */
+       if (group < it87_gpio->simple_size)
+               superio_set_mask(mask, group + it87_gpio->simple_base);
+
+       /* clear output enable, setting the pin to input, as all the
+        * newly-exported GPIO interfaces are set to input.
+        */
+       superio_clear_mask(mask, group + it87_gpio->output_base);
+
+       superio_exit();
+
+exit:
+       spin_unlock(&it87_gpio->lock);
+       return rc;
+}
+
+static int it87_gpio_get(struct gpio_chip *chip, unsigned gpio_num)
+{
+       u16 reg;
+       u8 mask;
+       struct it87_gpio *it87_gpio = to_it87_gpio(chip);
+
+       mask = 1 << (gpio_num % 8);
+       reg = (gpio_num / 8) + it87_gpio->io_base;
+
+       return !!(inb(reg) & mask);
+}
+
+static int it87_gpio_direction_in(struct gpio_chip *chip, unsigned gpio_num)
+{
+       u8 mask, group;
+       int rc = 0;
+       struct it87_gpio *it87_gpio = to_it87_gpio(chip);
+
+       mask = 1 << (gpio_num % 8);
+       group = (gpio_num / 8);
+
+       spin_lock(&it87_gpio->lock);
+
+       rc = superio_enter();
+       if (rc)
+               goto exit;
+
+       /* clear the output enable bit */
+       superio_clear_mask(mask, group + it87_gpio->output_base);
+
+       superio_exit();
+
+exit:
+       spin_unlock(&it87_gpio->lock);
+       return rc;
+}
+
+static void it87_gpio_set(struct gpio_chip *chip,
+                         unsigned gpio_num, int val)
+{
+       u8 mask, curr_vals;
+       u16 reg;
+       struct it87_gpio *it87_gpio = to_it87_gpio(chip);
+
+       mask = 1 << (gpio_num % 8);
+       reg = (gpio_num / 8) + it87_gpio->io_base;
+
+       curr_vals = inb(reg);
+       if (val)
+               outb(curr_vals | mask, reg);
+       else
+               outb(curr_vals & ~mask, reg);
+}
+
+static int it87_gpio_direction_out(struct gpio_chip *chip,
+                                  unsigned gpio_num, int val)
+{
+       u8 mask, group;
+       int rc = 0;
+       struct it87_gpio *it87_gpio = to_it87_gpio(chip);
+
+       mask = 1 << (gpio_num % 8);
+       group = (gpio_num / 8);
+
+       spin_lock(&it87_gpio->lock);
+
+       rc = superio_enter();
+       if (rc)
+               goto exit;
+
+       /* set the output enable bit */
+       superio_set_mask(mask, group + it87_gpio->output_base);
+
+       it87_gpio_set(chip, gpio_num, val);
+
+       superio_exit();
+
+exit:
+       spin_unlock(&it87_gpio->lock);
+       return rc;
+}
+
+static struct gpio_chip it87_template_chip = {
+       .label                  = KBUILD_MODNAME,
+       .owner                  = THIS_MODULE,
+       .request                = it87_gpio_request,
+       .get                    = it87_gpio_get,
+       .direction_input        = it87_gpio_direction_in,
+       .set                    = it87_gpio_set,
+       .direction_output       = it87_gpio_direction_out,
+       .base                   = -1
+};
+
+static int __init it87_gpio_init(void)
+{
+       int rc = 0, i;
+       u16 chip_type;
+       u8 chip_rev, gpio_ba_reg;
+       char *labels, **labels_table;
+
+       struct it87_gpio *it87_gpio = &it87_gpio_chip;
+
+       rc = superio_enter();
+       if (rc)
+               return rc;
+
+       chip_type = superio_inw(CHIPID);
+       chip_rev  = superio_inb(CHIPREV) & 0x0f;
+       superio_exit();
+
+       it87_gpio->chip = it87_template_chip;
+
+       switch (chip_type) {
+       case IT8728_ID:
+       case IT8732_ID:
+               gpio_ba_reg = 0x62;
+               it87_gpio->io_size = 8;
+               it87_gpio->output_base = 0xc8;
+               it87_gpio->simple_base = 0xc0;
+               it87_gpio->simple_size = 5;
+               it87_gpio->chip.ngpio = 64;
+               break;
+       case IT8761_ID:
+               gpio_ba_reg = 0x60;
+               it87_gpio->io_size = 4;
+               it87_gpio->output_base = 0xf0;
+               it87_gpio->simple_size = 0;
+               it87_gpio->chip.ngpio = 16;
+               break;
+       case NO_DEV_ID:
+               pr_err("no device\n");
+               return -ENODEV;
+       default:
+               pr_err("Unknown Chip found, Chip %04x Revision %x\n",
+                      chip_type, chip_rev);
+               return -ENODEV;
+       }
+
+       rc = superio_enter();
+       if (rc)
+               return rc;
+
+       superio_select(GPIO);
+
+       /* fetch GPIO base address */
+       it87_gpio->io_base = superio_inw(gpio_ba_reg);
+
+       superio_exit();
+
+       pr_info("Found Chip IT%04x rev %x. %u GPIO lines starting at %04xh\n",
+               chip_type, chip_rev, it87_gpio->chip.ngpio,
+               it87_gpio->io_base);
+
+       if (!request_region(it87_gpio->io_base, it87_gpio->io_size,
+                                                       KBUILD_MODNAME))
+               return -EBUSY;
+
+       /* Set up aliases for the GPIO connection.
+        *
+        * ITE documentation for recent chips such as the IT8728F
+        * refers to the GPIO lines as GPxy, with a coordinates system
+        * where x is the GPIO group (starting from 1) and y is the
+        * bit within the group.
+        *
+        * By creating these aliases, we make it easier to understand
+        * to which GPIO pin we're referring to.
+        */
+       labels = kcalloc(it87_gpio->chip.ngpio, sizeof("it87_gpXY"),
+                                                               GFP_KERNEL);
+       labels_table = kcalloc(it87_gpio->chip.ngpio, sizeof(const char *),
+                                                               GFP_KERNEL);
+
+       if (!labels || !labels_table) {
+               rc = -ENOMEM;
+               goto labels_free;
+       }
+
+       for (i = 0; i < it87_gpio->chip.ngpio; i++) {
+               char *label = &labels[i * sizeof("it87_gpXY")];
+
+               sprintf(label, "it87_gp%u%u", 1+(i/8), i%8);
+               labels_table[i] = label;
+       }
+
+       it87_gpio->chip.names = (const char *const*)labels_table;
+
+       rc = gpiochip_add(&it87_gpio->chip);
+       if (rc)
+               goto labels_free;
+
+       return 0;
+
+labels_free:
+       kfree(labels_table);
+       kfree(labels);
+       release_region(it87_gpio->io_base, it87_gpio->io_size);
+       return rc;
+}
+
+static void __exit it87_gpio_exit(void)
+{
+       struct it87_gpio *it87_gpio = &it87_gpio_chip;
+
+       gpiochip_remove(&it87_gpio->chip);
+       release_region(it87_gpio->io_base, it87_gpio->io_size);
+       kfree(it87_gpio->chip.names[0]);
+       kfree(it87_gpio->chip.names);
+}
+
+module_init(it87_gpio_init);
+module_exit(it87_gpio_exit);
+
+MODULE_AUTHOR("Diego Elio Pettenò <flameeyes@flameeyes.eu>");
+MODULE_DESCRIPTION("GPIO interface for IT87xx Super I/O chips");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-it8761e.c b/drivers/gpio/gpio-it8761e.c
deleted file mode 100644 (file)
index 30a8f24..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- *  GPIO interface for IT8761E Super I/O chip
- *
- *  Author: Denis Turischev <denis@compulab.co.il>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License 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 <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-
-#include <linux/gpio.h>
-
-#define SIO_CHIP_ID            0x8761
-#define CHIP_ID_HIGH_BYTE      0x20
-#define CHIP_ID_LOW_BYTE       0x21
-
-static u8 ports[2] = { 0x2e, 0x4e };
-static u8 port;
-
-static DEFINE_SPINLOCK(sio_lock);
-
-#define GPIO_NAME              "it8761-gpio"
-#define GPIO_BA_HIGH_BYTE      0x60
-#define GPIO_BA_LOW_BYTE       0x61
-#define GPIO_IOSIZE            4
-#define GPIO1X_IO              0xf0
-#define GPIO2X_IO              0xf1
-
-static u16 gpio_ba;
-
-static u8 read_reg(u8 addr, u8 port)
-{
-       outb(addr, port);
-       return inb(port + 1);
-}
-
-static void write_reg(u8 data, u8 addr, u8 port)
-{
-       outb(addr, port);
-       outb(data, port + 1);
-}
-
-static void enter_conf_mode(u8 port)
-{
-       outb(0x87, port);
-       outb(0x61, port);
-       outb(0x55, port);
-       outb((port == 0x2e) ? 0x55 : 0xaa, port);
-}
-
-static void exit_conf_mode(u8 port)
-{
-       outb(0x2, port);
-       outb(0x2, port + 1);
-}
-
-static void enter_gpio_mode(u8 port)
-{
-       write_reg(0x2, 0x7, port);
-}
-
-static int it8761e_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
-{
-       u16 reg;
-       u8 bit;
-
-       bit = gpio_num % 8;
-       reg = (gpio_num >= 8) ? gpio_ba + 1 : gpio_ba;
-
-       return !!(inb(reg) & (1 << bit));
-}
-
-static int it8761e_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
-{
-       u8 curr_dirs;
-       u8 io_reg, bit;
-
-       bit = gpio_num % 8;
-       io_reg = (gpio_num >= 8) ? GPIO2X_IO : GPIO1X_IO;
-
-       spin_lock(&sio_lock);
-
-       enter_conf_mode(port);
-       enter_gpio_mode(port);
-
-       curr_dirs = read_reg(io_reg, port);
-
-       if (curr_dirs & (1 << bit))
-               write_reg(curr_dirs & ~(1 << bit), io_reg, port);
-
-       exit_conf_mode(port);
-
-       spin_unlock(&sio_lock);
-       return 0;
-}
-
-static void it8761e_gpio_set(struct gpio_chip *gc,
-                               unsigned gpio_num, int val)
-{
-       u8 curr_vals, bit;
-       u16 reg;
-
-       bit = gpio_num % 8;
-       reg = (gpio_num >= 8) ? gpio_ba + 1 : gpio_ba;
-
-       spin_lock(&sio_lock);
-
-       curr_vals = inb(reg);
-       if (val)
-               outb(curr_vals | (1 << bit), reg);
-       else
-               outb(curr_vals & ~(1 << bit), reg);
-
-       spin_unlock(&sio_lock);
-}
-
-static int it8761e_gpio_direction_out(struct gpio_chip *gc,
-                                       unsigned gpio_num, int val)
-{
-       u8 curr_dirs, io_reg, bit;
-
-       bit = gpio_num % 8;
-       io_reg = (gpio_num >= 8) ? GPIO2X_IO : GPIO1X_IO;
-
-       it8761e_gpio_set(gc, gpio_num, val);
-
-       spin_lock(&sio_lock);
-
-       enter_conf_mode(port);
-       enter_gpio_mode(port);
-
-       curr_dirs = read_reg(io_reg, port);
-
-       if (!(curr_dirs & (1 << bit)))
-               write_reg(curr_dirs | (1 << bit), io_reg, port);
-
-       exit_conf_mode(port);
-
-       spin_unlock(&sio_lock);
-       return 0;
-}
-
-static struct gpio_chip it8761e_gpio_chip = {
-       .label                  = GPIO_NAME,
-       .owner                  = THIS_MODULE,
-       .get                    = it8761e_gpio_get,
-       .direction_input        = it8761e_gpio_direction_in,
-       .set                    = it8761e_gpio_set,
-       .direction_output       = it8761e_gpio_direction_out,
-};
-
-static int __init it8761e_gpio_init(void)
-{
-       int i, id, err;
-
-       /* chip and port detection */
-       for (i = 0; i < ARRAY_SIZE(ports); i++) {
-               spin_lock(&sio_lock);
-               enter_conf_mode(ports[i]);
-
-               id = (read_reg(CHIP_ID_HIGH_BYTE, ports[i]) << 8) +
-                               read_reg(CHIP_ID_LOW_BYTE, ports[i]);
-
-               exit_conf_mode(ports[i]);
-               spin_unlock(&sio_lock);
-
-               if (id == SIO_CHIP_ID) {
-                       port = ports[i];
-                       break;
-               }
-       }
-
-       if (!port)
-               return -ENODEV;
-
-       /* fetch GPIO base address */
-       enter_conf_mode(port);
-       enter_gpio_mode(port);
-       gpio_ba = (read_reg(GPIO_BA_HIGH_BYTE, port) << 8) +
-                               read_reg(GPIO_BA_LOW_BYTE, port);
-       exit_conf_mode(port);
-
-       if (!request_region(gpio_ba, GPIO_IOSIZE, GPIO_NAME))
-               return -EBUSY;
-
-       it8761e_gpio_chip.base = -1;
-       it8761e_gpio_chip.ngpio = 16;
-
-       err = gpiochip_add(&it8761e_gpio_chip);
-       if (err < 0)
-               goto gpiochip_add_err;
-
-       return 0;
-
-gpiochip_add_err:
-       release_region(gpio_ba, GPIO_IOSIZE);
-       gpio_ba = 0;
-       return err;
-}
-
-static void __exit it8761e_gpio_exit(void)
-{
-       if (gpio_ba) {
-               gpiochip_remove(&it8761e_gpio_chip);
-               release_region(gpio_ba, GPIO_IOSIZE);
-               gpio_ba = 0;
-       }
-}
-module_init(it8761e_gpio_init);
-module_exit(it8761e_gpio_exit);
-
-MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
-MODULE_DESCRIPTION("GPIO interface for IT8761E Super I/O chip");
-MODULE_LICENSE("GPL");
index eb68603136b0e78e76d6a87b2739efac997f2538..e39dcb0af8ae1b918a56672314f63d9913d3e238 100644 (file)
@@ -36,16 +36,6 @@ static inline struct lpc18xx_gpio_chip *to_lpc18xx_gpio(struct gpio_chip *chip)
        return container_of(chip, struct lpc18xx_gpio_chip, gpio);
 }
 
-static int lpc18xx_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(offset);
-}
-
-static void lpc18xx_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(offset);
-}
-
 static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 {
        struct lpc18xx_gpio_chip *gc = to_lpc18xx_gpio(chip);
@@ -95,8 +85,8 @@ static int lpc18xx_gpio_direction_output(struct gpio_chip *chip,
 
 static struct gpio_chip lpc18xx_chip = {
        .label                  = "lpc18xx/43xx-gpio",
-       .request                = lpc18xx_gpio_request,
-       .free                   = lpc18xx_gpio_free,
+       .request                = gpiochip_generic_request,
+       .free                   = gpiochip_generic_free,
        .direction_input        = lpc18xx_gpio_direction_input,
        .direction_output       = lpc18xx_gpio_direction_output,
        .set                    = lpc18xx_gpio_set,
index 18ab89e208065911cd9fe4261686579360331095..0f57d2d248ec9e367ac67a9194e2b3c8e9460948 100644 (file)
@@ -236,7 +236,6 @@ int __max730x_remove(struct device *dev)
        ts->write(dev, 0x04, 0x00);
        gpiochip_remove(&ts->chip);
        mutex_destroy(&ts->lock);
-       kfree(ts);
        return 0;
 }
 EXPORT_SYMBOL_GPL(__max730x_remove);
index abd8676ce2b69d5cdb26449c1c6d5b2fcd67d1b5..d3355a6dc9b1f126db770166de33f65fe1adbdbf 100644 (file)
 #define GPIO_DATA_IN           0x04
 #define GPIO_PIN_DIRECTION     0x08
 
-static int moxart_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(offset);
-}
-
-static void moxart_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(offset);
-}
-
 static int moxart_gpio_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -66,8 +56,8 @@ static int moxart_gpio_probe(struct platform_device *pdev)
        }
 
        bgc->gc.label = "moxart-gpio";
-       bgc->gc.request = moxart_gpio_request;
-       bgc->gc.free = moxart_gpio_free;
+       bgc->gc.request = gpiochip_generic_request;
+       bgc->gc.free = gpiochip_generic_free;
        bgc->data = bgc->read_reg(bgc->reg_set);
        bgc->gc.base = 0;
        bgc->gc.ngpio = 32;
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
deleted file mode 100644 (file)
index 4b42221..0000000
+++ /dev/null
@@ -1,453 +0,0 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. 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 and
- * only 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; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- */
-#define pr_fmt(fmt) "%s: " fmt, __func__
-
-#include <linux/bitmap.h>
-#include <linux/bitops.h>
-#include <linux/err.h>
-#include <linux/gpio.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irqchip/chained_irq.h>
-#include <linux/irq.h>
-#include <linux/irqdomain.h>
-#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-
-#define MAX_NR_GPIO 300
-
-/* Bits of interest in the GPIO_IN_OUT register.
- */
-enum {
-       GPIO_IN  = 0,
-       GPIO_OUT = 1
-};
-
-/* Bits of interest in the GPIO_INTR_STATUS register.
- */
-enum {
-       INTR_STATUS = 0,
-};
-
-/* Bits of interest in the GPIO_CFG register.
- */
-enum {
-       GPIO_OE = 9,
-};
-
-/* Bits of interest in the GPIO_INTR_CFG register.
- * When a GPIO triggers, two separate decisions are made, controlled
- * by two separate flags.
- *
- * - First, INTR_RAW_STATUS_EN controls whether or not the GPIO_INTR_STATUS
- * register for that GPIO will be updated to reflect the triggering of that
- * gpio.  If this bit is 0, this register will not be updated.
- * - Second, INTR_ENABLE controls whether an interrupt is triggered.
- *
- * If INTR_ENABLE is set and INTR_RAW_STATUS_EN is NOT set, an interrupt
- * can be triggered but the status register will not reflect it.
- */
-enum {
-       INTR_ENABLE        = 0,
-       INTR_POL_CTL       = 1,
-       INTR_DECT_CTL      = 2,
-       INTR_RAW_STATUS_EN = 3,
-};
-
-/* Codes of interest in GPIO_INTR_CFG_SU.
- */
-enum {
-       TARGET_PROC_SCORPION = 4,
-       TARGET_PROC_NONE     = 7,
-};
-
-/**
- * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure
- *
- * @enabled_irqs: a bitmap used to optimize the summary-irq handler.  By
- * keeping track of which gpios are unmasked as irq sources, we avoid
- * having to do readl calls on hundreds of iomapped registers each time
- * the summary interrupt fires in order to locate the active interrupts.
- *
- * @wake_irqs: a bitmap for tracking which interrupt lines are enabled
- * as wakeup sources.  When the device is suspended, interrupts which are
- * not wakeup sources are disabled.
- *
- * @dual_edge_irqs: a bitmap used to track which irqs are configured
- * as dual-edge, as this is not supported by the hardware and requires
- * some special handling in the driver.
- */
-struct msm_gpio_dev {
-       struct gpio_chip gpio_chip;
-       DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO);
-       DECLARE_BITMAP(wake_irqs, MAX_NR_GPIO);
-       DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO);
-       struct irq_domain *domain;
-       int summary_irq;
-       void __iomem *msm_tlmm_base;
-};
-
-static struct msm_gpio_dev msm_gpio;
-
-#define GPIO_INTR_CFG_SU(gpio)    (msm_gpio.msm_tlmm_base + 0x0400 + \
-                                                               (0x04 * (gpio)))
-#define GPIO_CONFIG(gpio)         (msm_gpio.msm_tlmm_base + 0x1000 + \
-                                                               (0x10 * (gpio)))
-#define GPIO_IN_OUT(gpio)         (msm_gpio.msm_tlmm_base + 0x1004 + \
-                                                               (0x10 * (gpio)))
-#define GPIO_INTR_CFG(gpio)       (msm_gpio.msm_tlmm_base + 0x1008 + \
-                                                               (0x10 * (gpio)))
-#define GPIO_INTR_STATUS(gpio)    (msm_gpio.msm_tlmm_base + 0x100c + \
-                                                               (0x10 * (gpio)))
-
-static DEFINE_SPINLOCK(tlmm_lock);
-
-static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip)
-{
-       return container_of(chip, struct msm_gpio_dev, gpio_chip);
-}
-
-static inline void set_gpio_bits(unsigned n, void __iomem *reg)
-{
-       writel(readl(reg) | n, reg);
-}
-
-static inline void clear_gpio_bits(unsigned n, void __iomem *reg)
-{
-       writel(readl(reg) & ~n, reg);
-}
-
-static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
-       return readl(GPIO_IN_OUT(offset)) & BIT(GPIO_IN);
-}
-
-static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
-{
-       writel(val ? BIT(GPIO_OUT) : 0, GPIO_IN_OUT(offset));
-}
-
-static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
-{
-       unsigned long irq_flags;
-
-       spin_lock_irqsave(&tlmm_lock, irq_flags);
-       clear_gpio_bits(BIT(GPIO_OE), GPIO_CONFIG(offset));
-       spin_unlock_irqrestore(&tlmm_lock, irq_flags);
-       return 0;
-}
-
-static int msm_gpio_direction_output(struct gpio_chip *chip,
-                               unsigned offset,
-                               int val)
-{
-       unsigned long irq_flags;
-
-       spin_lock_irqsave(&tlmm_lock, irq_flags);
-       msm_gpio_set(chip, offset, val);
-       set_gpio_bits(BIT(GPIO_OE), GPIO_CONFIG(offset));
-       spin_unlock_irqrestore(&tlmm_lock, irq_flags);
-       return 0;
-}
-
-static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return 0;
-}
-
-static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       return;
-}
-
-static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
-       struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
-       struct irq_domain *domain = g_dev->domain;
-
-       return irq_create_mapping(domain, offset);
-}
-
-/* For dual-edge interrupts in software, since the hardware has no
- * such support:
- *
- * At appropriate moments, this function may be called to flip the polarity
- * settings of both-edge irq lines to try and catch the next edge.
- *
- * The attempt is considered successful if:
- * - the status bit goes high, indicating that an edge was caught, or
- * - the input value of the gpio doesn't change during the attempt.
- * If the value changes twice during the process, that would cause the first
- * test to fail but would force the second, as two opposite
- * transitions would cause a detection no matter the polarity setting.
- *
- * The do-loop tries to sledge-hammer closed the timing hole between
- * the initial value-read and the polarity-write - if the line value changes
- * during that window, an interrupt is lost, the new polarity setting is
- * incorrect, and the first success test will fail, causing a retry.
- *
- * Algorithm comes from Google's msmgpio driver, see mach-msm/gpio.c.
- */
-static void msm_gpio_update_dual_edge_pos(unsigned gpio)
-{
-       int loop_limit = 100;
-       unsigned val, val2, intstat;
-
-       do {
-               val = readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN);
-               if (val)
-                       clear_gpio_bits(BIT(INTR_POL_CTL), GPIO_INTR_CFG(gpio));
-               else
-                       set_gpio_bits(BIT(INTR_POL_CTL), GPIO_INTR_CFG(gpio));
-               val2 = readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN);
-               intstat = readl(GPIO_INTR_STATUS(gpio)) & BIT(INTR_STATUS);
-               if (intstat || val == val2)
-                       return;
-       } while (loop_limit-- > 0);
-       pr_err("%s: dual-edge irq failed to stabilize, "
-              "interrupts dropped. %#08x != %#08x\n",
-              __func__, val, val2);
-}
-
-static void msm_gpio_irq_ack(struct irq_data *d)
-{
-       int gpio = d->hwirq;
-
-       writel(BIT(INTR_STATUS), GPIO_INTR_STATUS(gpio));
-       if (test_bit(gpio, msm_gpio.dual_edge_irqs))
-               msm_gpio_update_dual_edge_pos(gpio);
-}
-
-static void msm_gpio_irq_mask(struct irq_data *d)
-{
-       unsigned long irq_flags;
-       int gpio = d->hwirq;
-
-       spin_lock_irqsave(&tlmm_lock, irq_flags);
-       writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
-       clear_gpio_bits(BIT(INTR_RAW_STATUS_EN) | BIT(INTR_ENABLE), GPIO_INTR_CFG(gpio));
-       __clear_bit(gpio, msm_gpio.enabled_irqs);
-       spin_unlock_irqrestore(&tlmm_lock, irq_flags);
-}
-
-static void msm_gpio_irq_unmask(struct irq_data *d)
-{
-       unsigned long irq_flags;
-       int gpio = d->hwirq;
-
-       spin_lock_irqsave(&tlmm_lock, irq_flags);
-       __set_bit(gpio, msm_gpio.enabled_irqs);
-       set_gpio_bits(BIT(INTR_RAW_STATUS_EN) | BIT(INTR_ENABLE), GPIO_INTR_CFG(gpio));
-       writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
-       spin_unlock_irqrestore(&tlmm_lock, irq_flags);
-}
-
-static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
-{
-       unsigned long irq_flags;
-       int gpio = d->hwirq;
-       uint32_t bits;
-
-       spin_lock_irqsave(&tlmm_lock, irq_flags);
-
-       bits = readl(GPIO_INTR_CFG(gpio));
-
-       if (flow_type & IRQ_TYPE_EDGE_BOTH) {
-               bits |= BIT(INTR_DECT_CTL);
-               irq_set_handler_locked(d, handle_edge_irq);
-               if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
-                       __set_bit(gpio, msm_gpio.dual_edge_irqs);
-               else
-                       __clear_bit(gpio, msm_gpio.dual_edge_irqs);
-       } else {
-               bits &= ~BIT(INTR_DECT_CTL);
-               irq_set_handler_locked(d, handle_level_irq);
-               __clear_bit(gpio, msm_gpio.dual_edge_irqs);
-       }
-
-       if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
-               bits |= BIT(INTR_POL_CTL);
-       else
-               bits &= ~BIT(INTR_POL_CTL);
-
-       writel(bits, GPIO_INTR_CFG(gpio));
-
-       if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
-               msm_gpio_update_dual_edge_pos(gpio);
-
-       spin_unlock_irqrestore(&tlmm_lock, irq_flags);
-
-       return 0;
-}
-
-/*
- * When the summary IRQ is raised, any number of GPIO lines may be high.
- * It is the job of the summary handler to find all those GPIO lines
- * which have been set as summary IRQ lines and which are triggered,
- * and to call their interrupt handlers.
- */
-static void msm_summary_irq_handler(struct irq_desc *desc)
-{
-       unsigned long i;
-       struct irq_chip *chip = irq_desc_get_chip(desc);
-
-       chained_irq_enter(chip, desc);
-
-       for_each_set_bit(i, msm_gpio.enabled_irqs, MAX_NR_GPIO) {
-               if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS))
-                       generic_handle_irq(irq_find_mapping(msm_gpio.domain,
-                                                               i));
-       }
-
-       chained_irq_exit(chip, desc);
-}
-
-static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
-{
-       int gpio = d->hwirq;
-
-       if (on) {
-               if (bitmap_empty(msm_gpio.wake_irqs, MAX_NR_GPIO))
-                       irq_set_irq_wake(msm_gpio.summary_irq, 1);
-               set_bit(gpio, msm_gpio.wake_irqs);
-       } else {
-               clear_bit(gpio, msm_gpio.wake_irqs);
-               if (bitmap_empty(msm_gpio.wake_irqs, MAX_NR_GPIO))
-                       irq_set_irq_wake(msm_gpio.summary_irq, 0);
-       }
-
-       return 0;
-}
-
-static struct irq_chip msm_gpio_irq_chip = {
-       .name           = "msmgpio",
-       .irq_mask       = msm_gpio_irq_mask,
-       .irq_unmask     = msm_gpio_irq_unmask,
-       .irq_ack        = msm_gpio_irq_ack,
-       .irq_set_type   = msm_gpio_irq_set_type,
-       .irq_set_wake   = msm_gpio_irq_set_wake,
-};
-
-static struct lock_class_key msm_gpio_lock_class;
-
-static int msm_gpio_irq_domain_map(struct irq_domain *d, unsigned int irq,
-                                  irq_hw_number_t hwirq)
-{
-       irq_set_lockdep_class(irq, &msm_gpio_lock_class);
-       irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
-                       handle_level_irq);
-
-       return 0;
-}
-
-static const struct irq_domain_ops msm_gpio_irq_domain_ops = {
-       .xlate = irq_domain_xlate_twocell,
-       .map = msm_gpio_irq_domain_map,
-};
-
-static int msm_gpio_probe(struct platform_device *pdev)
-{
-       int ret, ngpio;
-       struct resource *res;
-
-       if (of_property_read_u32(pdev->dev.of_node, "ngpio", &ngpio)) {
-               dev_err(&pdev->dev, "%s: ngpio property missing\n", __func__);
-               return -EINVAL;
-       }
-
-       if (ngpio > MAX_NR_GPIO)
-               WARN(1, "ngpio exceeds the MAX_NR_GPIO. Increase MAX_NR_GPIO\n");
-
-       bitmap_zero(msm_gpio.enabled_irqs, MAX_NR_GPIO);
-       bitmap_zero(msm_gpio.wake_irqs, MAX_NR_GPIO);
-       bitmap_zero(msm_gpio.dual_edge_irqs, MAX_NR_GPIO);
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       msm_gpio.msm_tlmm_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(msm_gpio.msm_tlmm_base))
-               return PTR_ERR(msm_gpio.msm_tlmm_base);
-
-       msm_gpio.gpio_chip.ngpio = ngpio;
-       msm_gpio.gpio_chip.label = pdev->name;
-       msm_gpio.gpio_chip.dev = &pdev->dev;
-       msm_gpio.gpio_chip.base = 0;
-       msm_gpio.gpio_chip.direction_input = msm_gpio_direction_input;
-       msm_gpio.gpio_chip.direction_output = msm_gpio_direction_output;
-       msm_gpio.gpio_chip.get = msm_gpio_get;
-       msm_gpio.gpio_chip.set = msm_gpio_set;
-       msm_gpio.gpio_chip.to_irq = msm_gpio_to_irq;
-       msm_gpio.gpio_chip.request = msm_gpio_request;
-       msm_gpio.gpio_chip.free = msm_gpio_free;
-
-       ret = gpiochip_add(&msm_gpio.gpio_chip);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "gpiochip_add failed with error %d\n", ret);
-               return ret;
-       }
-
-       msm_gpio.summary_irq = platform_get_irq(pdev, 0);
-       if (msm_gpio.summary_irq < 0) {
-               dev_err(&pdev->dev, "No Summary irq defined for msmgpio\n");
-               return msm_gpio.summary_irq;
-       }
-
-       msm_gpio.domain = irq_domain_add_linear(pdev->dev.of_node, ngpio,
-                                               &msm_gpio_irq_domain_ops,
-                                               &msm_gpio);
-       if (!msm_gpio.domain)
-               return -ENODEV;
-
-       irq_set_chained_handler(msm_gpio.summary_irq, msm_summary_irq_handler);
-
-       return 0;
-}
-
-static const struct of_device_id msm_gpio_of_match[] = {
-       { .compatible = "qcom,msm-gpio", },
-       { },
-};
-MODULE_DEVICE_TABLE(of, msm_gpio_of_match);
-
-static int msm_gpio_remove(struct platform_device *dev)
-{
-       gpiochip_remove(&msm_gpio.gpio_chip);
-
-       irq_set_handler(msm_gpio.summary_irq, NULL);
-
-       return 0;
-}
-
-static struct platform_driver msm_gpio_driver = {
-       .probe = msm_gpio_probe,
-       .remove = msm_gpio_remove,
-       .driver = {
-               .name = "msmgpio",
-               .of_match_table = msm_gpio_of_match,
-       },
-};
-
-module_platform_driver(msm_gpio_driver)
-
-MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
-MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:msmgpio");
index df418b81456dfdc5f7a876221cc6c9a2ef80c380..d428b97876c52e933ee37b42540313b777c39c73 100644 (file)
@@ -185,16 +185,6 @@ static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip)
  * Functions implementing the gpio_chip methods
  */
 
-static int mvebu_gpio_request(struct gpio_chip *chip, unsigned pin)
-{
-       return pinctrl_request_gpio(chip->base + pin);
-}
-
-static void mvebu_gpio_free(struct gpio_chip *chip, unsigned pin)
-{
-       pinctrl_free_gpio(chip->base + pin);
-}
-
 static void mvebu_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
 {
        struct mvebu_gpio_chip *mvchip =
@@ -709,8 +699,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
        mvchip->soc_variant = soc_variant;
        mvchip->chip.label = dev_name(&pdev->dev);
        mvchip->chip.dev = &pdev->dev;
-       mvchip->chip.request = mvebu_gpio_request;
-       mvchip->chip.free = mvebu_gpio_free;
+       mvchip->chip.request = gpiochip_generic_request;
+       mvchip->chip.free = gpiochip_generic_free;
        mvchip->chip.direction_input = mvebu_gpio_direction_input;
        mvchip->chip.get = mvebu_gpio_get;
        mvchip->chip.direction_output = mvebu_gpio_direction_output;
index 5236db161e76047db31bf8c8e5ae7299f12fd06d..56d2d026e62e42bf6fec7db5ad2aa25aebd970f3 100644 (file)
@@ -51,7 +51,7 @@ struct gpio_regs {
 struct gpio_bank {
        struct list_head node;
        void __iomem *base;
-       u16 irq;
+       int irq;
        u32 non_wakeup_gpios;
        u32 enabled_non_wakeup_gpios;
        struct gpio_regs context;
@@ -59,6 +59,7 @@ struct gpio_bank {
        u32 level_mask;
        u32 toggle_mask;
        raw_spinlock_t lock;
+       raw_spinlock_t wa_lock;
        struct gpio_chip chip;
        struct clk *dbck;
        u32 mod_usage;
@@ -496,9 +497,6 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
                (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
                return -EINVAL;
 
-       if (!BANK_USED(bank))
-               pm_runtime_get_sync(bank->dev);
-
        raw_spin_lock_irqsave(&bank->lock, flags);
        retval = omap_set_gpio_triggering(bank, offset, type);
        if (retval) {
@@ -521,8 +519,6 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
        return 0;
 
 error:
-       if (!BANK_USED(bank))
-               pm_runtime_put(bank->dev);
        return retval;
 }
 
@@ -654,8 +650,13 @@ static int omap_gpio_wake_enable(struct irq_data *d, unsigned int enable)
 {
        struct gpio_bank *bank = omap_irq_data_get_bank(d);
        unsigned offset = d->hwirq;
+       int ret;
+
+       ret = omap_set_gpio_wakeup(bank, offset, enable);
+       if (!ret)
+               ret = irq_set_irq_wake(bank->irq, enable);
 
-       return omap_set_gpio_wakeup(bank, offset, enable);
+       return ret;
 }
 
 static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
@@ -709,26 +710,21 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
  * line's interrupt handler has been run, we may miss some nested
  * interrupts.
  */
-static void omap_gpio_irq_handler(struct irq_desc *desc)
+static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
 {
        void __iomem *isr_reg = NULL;
        u32 isr;
        unsigned int bit;
-       struct gpio_bank *bank;
-       int unmasked = 0;
-       struct irq_chip *irqchip = irq_desc_get_chip(desc);
-       struct gpio_chip *chip = irq_desc_get_handler_data(desc);
+       struct gpio_bank *bank = gpiobank;
+       unsigned long wa_lock_flags;
        unsigned long lock_flags;
 
-       chained_irq_enter(irqchip, desc);
-
-       bank = container_of(chip, struct gpio_bank, chip);
        isr_reg = bank->base + bank->regs->irqstatus;
-       pm_runtime_get_sync(bank->dev);
-
        if (WARN_ON(!isr_reg))
                goto exit;
 
+       pm_runtime_get_sync(bank->dev);
+
        while (1) {
                u32 isr_saved, level_mask = 0;
                u32 enabled;
@@ -750,13 +746,6 @@ static void omap_gpio_irq_handler(struct irq_desc *desc)
 
                raw_spin_unlock_irqrestore(&bank->lock, lock_flags);
 
-               /* if there is only edge sensitive GPIO pin interrupts
-               configured, we could unmask GPIO bank interrupt immediately */
-               if (!level_mask && !unmasked) {
-                       unmasked = 1;
-                       chained_irq_exit(irqchip, desc);
-               }
-
                if (!isr)
                        break;
 
@@ -777,18 +766,18 @@ static void omap_gpio_irq_handler(struct irq_desc *desc)
 
                        raw_spin_unlock_irqrestore(&bank->lock, lock_flags);
 
+                       raw_spin_lock_irqsave(&bank->wa_lock, wa_lock_flags);
+
                        generic_handle_irq(irq_find_mapping(bank->chip.irqdomain,
                                                            bit));
+
+                       raw_spin_unlock_irqrestore(&bank->wa_lock,
+                                                  wa_lock_flags);
                }
        }
-       /* if bank has any level sensitive GPIO pin interrupt
-       configured, we must unmask the bank interrupt only after
-       handler(s) are executed in order to avoid spurious bank
-       interrupt */
 exit:
-       if (!unmasked)
-               chained_irq_exit(irqchip, desc);
        pm_runtime_put(bank->dev);
+       return IRQ_HANDLED;
 }
 
 static unsigned int omap_gpio_irq_startup(struct irq_data *d)
@@ -797,9 +786,6 @@ static unsigned int omap_gpio_irq_startup(struct irq_data *d)
        unsigned long flags;
        unsigned offset = d->hwirq;
 
-       if (!BANK_USED(bank))
-               pm_runtime_get_sync(bank->dev);
-
        raw_spin_lock_irqsave(&bank->lock, flags);
 
        if (!LINE_USED(bank->mod_usage, offset))
@@ -815,8 +801,6 @@ static unsigned int omap_gpio_irq_startup(struct irq_data *d)
        return 0;
 err:
        raw_spin_unlock_irqrestore(&bank->lock, flags);
-       if (!BANK_USED(bank))
-               pm_runtime_put(bank->dev);
        return -EINVAL;
 }
 
@@ -835,6 +819,19 @@ static void omap_gpio_irq_shutdown(struct irq_data *d)
                omap_clear_gpio_debounce(bank, offset);
        omap_disable_gpio_module(bank, offset);
        raw_spin_unlock_irqrestore(&bank->lock, flags);
+}
+
+static void omap_gpio_irq_bus_lock(struct irq_data *data)
+{
+       struct gpio_bank *bank = omap_irq_data_get_bank(data);
+
+       if (!BANK_USED(bank))
+               pm_runtime_get_sync(bank->dev);
+}
+
+static void gpio_irq_bus_sync_unlock(struct irq_data *data)
+{
+       struct gpio_bank *bank = omap_irq_data_get_bank(data);
 
        /*
         * If this is the last IRQ to be freed in the bank,
@@ -1132,7 +1129,7 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
        }
 
        ret = gpiochip_irqchip_add(&bank->chip, irqc,
-                                  irq_base, omap_gpio_irq_handler,
+                                  irq_base, handle_bad_irq,
                                   IRQ_TYPE_NONE);
 
        if (ret) {
@@ -1141,10 +1138,14 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
                return -ENODEV;
        }
 
-       gpiochip_set_chained_irqchip(&bank->chip, irqc,
-                                    bank->irq, omap_gpio_irq_handler);
+       gpiochip_set_chained_irqchip(&bank->chip, irqc, bank->irq, NULL);
 
-       return 0;
+       ret = devm_request_irq(bank->dev, bank->irq, omap_gpio_irq_handler,
+                              0, dev_name(bank->dev), bank);
+       if (ret)
+               gpiochip_remove(&bank->chip);
+
+       return ret;
 }
 
 static const struct of_device_id omap_gpio_match[];
@@ -1183,6 +1184,8 @@ static int omap_gpio_probe(struct platform_device *pdev)
        irqc->irq_unmask = omap_gpio_unmask_irq,
        irqc->irq_set_type = omap_gpio_irq_type,
        irqc->irq_set_wake = omap_gpio_wake_enable,
+       irqc->irq_bus_lock = omap_gpio_irq_bus_lock,
+       irqc->irq_bus_sync_unlock = gpio_irq_bus_sync_unlock,
        irqc->name = dev_name(&pdev->dev);
 
        bank->irq = platform_get_irq(pdev, 0);
@@ -1224,6 +1227,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
                bank->set_dataout = omap_set_gpio_dataout_mask;
 
        raw_spin_lock_init(&bank->lock);
+       raw_spin_lock_init(&bank->wa_lock);
 
        /* Static mapping, never released */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
index 50caeb1ee3509da04b57d38dd332396e76ea25de..2d4892cc70fb0c4acc149dd6a9864439644a1673 100644 (file)
@@ -21,6 +21,7 @@
 #ifdef CONFIG_OF_GPIO
 #include <linux/of_platform.h>
 #endif
+#include <linux/acpi.h>
 
 #define PCA953X_INPUT          0
 #define PCA953X_OUTPUT         1
@@ -42,6 +43,9 @@
 #define PCA_INT                        0x0100
 #define PCA953X_TYPE           0x1000
 #define PCA957X_TYPE           0x2000
+#define PCA_TYPE_MASK          0xF000
+
+#define PCA_CHIP_TYPE(x)       ((x) & PCA_TYPE_MASK)
 
 static const struct i2c_device_id pca953x_id[] = {
        { "pca9505", 40 | PCA953X_TYPE | PCA_INT, },
@@ -67,11 +71,18 @@ static const struct i2c_device_id pca953x_id[] = {
        { "tca6408", 8  | PCA953X_TYPE | PCA_INT, },
        { "tca6416", 16 | PCA953X_TYPE | PCA_INT, },
        { "tca6424", 24 | PCA953X_TYPE | PCA_INT, },
+       { "tca9539", 16 | PCA953X_TYPE | PCA_INT, },
        { "xra1202", 8  | PCA953X_TYPE },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, pca953x_id);
 
+static const struct acpi_device_id pca953x_acpi_ids[] = {
+       { "INT3491", 16 | PCA953X_TYPE | PCA_INT, },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, pca953x_acpi_ids);
+
 #define MAX_BANK 5
 #define BANK_SZ 8
 
@@ -95,6 +106,7 @@ struct pca953x_chip {
        struct gpio_chip gpio_chip;
        const char *const *names;
        int     chip_type;
+       unsigned long driver_data;
 };
 
 static inline struct pca953x_chip *to_pca(struct gpio_chip *gc)
@@ -517,14 +529,13 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid)
 }
 
 static int pca953x_irq_setup(struct pca953x_chip *chip,
-                            const struct i2c_device_id *id,
                             int irq_base)
 {
        struct i2c_client *client = chip->client;
        int ret, i, offset = 0;
 
        if (client->irq && irq_base != -1
-                       && (id->driver_data & PCA_INT)) {
+                       && (chip->driver_data & PCA_INT)) {
 
                switch (chip->chip_type) {
                case PCA953X_TYPE:
@@ -581,12 +592,11 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
 
 #else /* CONFIG_GPIO_PCA953X_IRQ */
 static int pca953x_irq_setup(struct pca953x_chip *chip,
-                            const struct i2c_device_id *id,
                             int irq_base)
 {
        struct i2c_client *client = chip->client;
 
-       if (irq_base != -1 && (id->driver_data & PCA_INT))
+       if (irq_base != -1 && (chip->driver_data & PCA_INT))
                dev_warn(&client->dev, "interrupt support not compiled in\n");
 
        return 0;
@@ -635,11 +645,15 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
                memset(val, 0xFF, NBANK(chip));
        else
                memset(val, 0, NBANK(chip));
-       pca953x_write_regs(chip, PCA957X_INVRT, val);
+       ret = pca953x_write_regs(chip, PCA957X_INVRT, val);
+       if (ret)
+               goto out;
 
        /* To enable register 6, 7 to control pull up and pull down */
        memset(val, 0x02, NBANK(chip));
-       pca953x_write_regs(chip, PCA957X_BKEN, val);
+       ret = pca953x_write_regs(chip, PCA957X_BKEN, val);
+       if (ret)
+               goto out;
 
        return 0;
 out:
@@ -673,14 +687,26 @@ static int pca953x_probe(struct i2c_client *client,
 
        chip->client = client;
 
-       chip->chip_type = id->driver_data & (PCA953X_TYPE | PCA957X_TYPE);
+       if (id) {
+               chip->driver_data = id->driver_data;
+       } else {
+               const struct acpi_device_id *id;
+
+               id = acpi_match_device(pca953x_acpi_ids, &client->dev);
+               if (!id)
+                       return -ENODEV;
+
+               chip->driver_data = id->driver_data;
+       }
+
+       chip->chip_type = PCA_CHIP_TYPE(chip->driver_data);
 
        mutex_init(&chip->i2c_lock);
 
        /* initialize cached registers from their original values.
         * we can't share this chip with another i2c master.
         */
-       pca953x_setup_gpio(chip, id->driver_data & PCA_GPIO_MASK);
+       pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
 
        if (chip->chip_type == PCA953X_TYPE)
                ret = device_pca953x_init(chip, invert);
@@ -693,7 +719,7 @@ static int pca953x_probe(struct i2c_client *client,
        if (ret)
                return ret;
 
-       ret = pca953x_irq_setup(chip, id, irq_base);
+       ret = pca953x_irq_setup(chip, irq_base);
        if (ret)
                return ret;
 
@@ -765,6 +791,7 @@ static struct i2c_driver pca953x_driver = {
        .driver = {
                .name   = "pca953x",
                .of_match_table = pca953x_dt_ids,
+               .acpi_match_table = ACPI_PTR(pca953x_acpi_ids),
        },
        .probe          = pca953x_probe,
        .remove         = pca953x_remove,
index 229ef653e0f8f631172e1956ad4c9f9c74c50b02..4d4b3767670220a03076301ced6b3b12cffcd265 100644 (file)
@@ -52,36 +52,12 @@ struct pl061_gpio {
 
        void __iomem            *base;
        struct gpio_chip        gc;
-       bool                    uses_pinctrl;
 
 #ifdef CONFIG_PM
        struct pl061_context_save_regs csave_regs;
 #endif
 };
 
-static int pl061_gpio_request(struct gpio_chip *gc, unsigned offset)
-{
-       /*
-        * Map back to global GPIO space and request muxing, the direction
-        * parameter does not matter for this controller.
-        */
-       struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
-       int gpio = gc->base + offset;
-
-       if (chip->uses_pinctrl)
-               return pinctrl_request_gpio(gpio);
-       return 0;
-}
-
-static void pl061_gpio_free(struct gpio_chip *gc, unsigned offset)
-{
-       struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
-       int gpio = gc->base + offset;
-
-       if (chip->uses_pinctrl)
-               pinctrl_free_gpio(gpio);
-}
-
 static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
 {
        struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
@@ -152,6 +128,17 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
        if (offset < 0 || offset >= PL061_GPIO_NR)
                return -EINVAL;
 
+       if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) &&
+           (trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)))
+       {
+               dev_err(gc->dev,
+                       "trying to configure line %d for both level and edge "
+                       "detection, choose one!\n",
+                       offset);
+               return -EINVAL;
+       }
+
+
        spin_lock_irqsave(&chip->lock, flags);
 
        gpioiev = readb(chip->base + GPIOIEV);
@@ -159,23 +146,53 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
        gpioibe = readb(chip->base + GPIOIBE);
 
        if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
+               bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH;
+
+               /* Disable edge detection */
+               gpioibe &= ~bit;
+               /* Enable level detection */
                gpiois |= bit;
-               if (trigger & IRQ_TYPE_LEVEL_HIGH)
+               /* Select polarity */
+               if (polarity)
                        gpioiev |= bit;
                else
                        gpioiev &= ~bit;
-       } else
+               irq_set_handler_locked(d, handle_level_irq);
+               dev_dbg(gc->dev, "line %d: IRQ on %s level\n",
+                       offset,
+                       polarity ? "HIGH" : "LOW");
+       } else if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
+               /* Disable level detection */
                gpiois &= ~bit;
-
-       if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
-               /* Setting this makes GPIOEV be ignored */
+               /* Select both edges, setting this makes GPIOEV be ignored */
                gpioibe |= bit;
-       else {
+               irq_set_handler_locked(d, handle_edge_irq);
+               dev_dbg(gc->dev, "line %d: IRQ on both edges\n", offset);
+       } else if ((trigger & IRQ_TYPE_EDGE_RISING) ||
+                  (trigger & IRQ_TYPE_EDGE_FALLING)) {
+               bool rising = trigger & IRQ_TYPE_EDGE_RISING;
+
+               /* Disable level detection */
+               gpiois &= ~bit;
+               /* Clear detection on both edges */
                gpioibe &= ~bit;
-               if (trigger & IRQ_TYPE_EDGE_RISING)
+               /* Select edge */
+               if (rising)
                        gpioiev |= bit;
-               else if (trigger & IRQ_TYPE_EDGE_FALLING)
+               else
                        gpioiev &= ~bit;
+               irq_set_handler_locked(d, handle_edge_irq);
+               dev_dbg(gc->dev, "line %d: IRQ on %s edge\n",
+                       offset,
+                       rising ? "RISING" : "FALLING");
+       } else {
+               /* No trigger: disable everything */
+               gpiois &= ~bit;
+               gpioibe &= ~bit;
+               gpioiev &= ~bit;
+               irq_set_handler_locked(d, handle_bad_irq);
+               dev_warn(gc->dev, "no trigger selected for line %d\n",
+                        offset);
        }
 
        writeb(gpiois, chip->base + GPIOIS);
@@ -198,7 +215,6 @@ static void pl061_irq_handler(struct irq_desc *desc)
        chained_irq_enter(irqchip, desc);
 
        pending = readb(chip->base + GPIOMIS);
-       writeb(pending, chip->base + GPIOIC);
        if (pending) {
                for_each_set_bit(offset, &pending, PL061_GPIO_NR)
                        generic_handle_irq(irq_find_mapping(gc->irqdomain,
@@ -234,8 +250,28 @@ static void pl061_irq_unmask(struct irq_data *d)
        spin_unlock(&chip->lock);
 }
 
+/**
+ * pl061_irq_ack() - ACK an edge IRQ
+ * @d: IRQ data for this IRQ
+ *
+ * This gets called from the edge IRQ handler to ACK the edge IRQ
+ * in the GPIOIC (interrupt-clear) register. For level IRQs this is
+ * not needed: these go away when the level signal goes away.
+ */
+static void pl061_irq_ack(struct irq_data *d)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+       u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
+
+       spin_lock(&chip->lock);
+       writeb(mask, chip->base + GPIOIC);
+       spin_unlock(&chip->lock);
+}
+
 static struct irq_chip pl061_irqchip = {
        .name           = "pl061",
+       .irq_ack        = pl061_irq_ack,
        .irq_mask       = pl061_irq_mask,
        .irq_unmask     = pl061_irq_unmask,
        .irq_set_type   = pl061_irq_type,
@@ -269,11 +305,11 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
                return PTR_ERR(chip->base);
 
        spin_lock_init(&chip->lock);
-       if (of_property_read_bool(dev->of_node, "gpio-ranges"))
-               chip->uses_pinctrl = true;
+       if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
+               chip->gc.request = gpiochip_generic_request;
+               chip->gc.free = gpiochip_generic_free;
+       }
 
-       chip->gc.request = pl061_gpio_request;
-       chip->gc.free = pl061_gpio_free;
        chip->gc.direction_input = pl061_direction_input;
        chip->gc.direction_output = pl061_direction_output;
        chip->gc.get = pl061_get_value;
@@ -298,7 +334,7 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
        }
 
        ret = gpiochip_irqchip_add(&chip->gc, &pl061_irqchip,
-                                  irq_base, handle_simple_irq,
+                                  irq_base, handle_bad_irq,
                                   IRQ_TYPE_NONE);
        if (ret) {
                dev_info(&adev->dev, "could not add irqchip\n");
index 65bc9f47a68ed98c89889431a5ece6cf1f8a01b7..34b02b42ab9e5bbfd2bc2f7bd16ba31c704718ec 100644 (file)
@@ -102,7 +102,7 @@ static int sdv_xlate(struct irq_domain *h, struct device_node *node,
 {
        u32 line, type;
 
-       if (node != h->of_node)
+       if (node != irq_domain_get_of_node(h))
                return -EINVAL;
 
        if (intsize < 2)
index 9c6b96707c9f286a9bbf13c756beab7d4cedbace..76f920173a2f847cd7e370149b5d417d625ed750 100644 (file)
@@ -160,6 +160,11 @@ static const struct of_device_id sx150x_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, sx150x_of_match);
 
+struct sx150x_chip *to_sx150x(struct gpio_chip *gc)
+{
+       return container_of(gc, struct sx150x_chip, gpio_chip);
+}
+
 static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val)
 {
        s32 err = i2c_smbus_write_byte_data(client, reg, val);
@@ -296,11 +301,9 @@ static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val)
 
 static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset)
 {
-       struct sx150x_chip *chip;
+       struct sx150x_chip *chip = to_sx150x(gc);
        int status = -EINVAL;
 
-       chip = container_of(gc, struct sx150x_chip, gpio_chip);
-
        if (!offset_is_oscio(chip, offset)) {
                mutex_lock(&chip->lock);
                status = sx150x_get_io(chip, offset);
@@ -312,9 +315,7 @@ static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset)
 
 static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val)
 {
-       struct sx150x_chip *chip;
-
-       chip = container_of(gc, struct sx150x_chip, gpio_chip);
+       struct sx150x_chip *chip = to_sx150x(gc);
 
        mutex_lock(&chip->lock);
        if (offset_is_oscio(chip, offset))
@@ -326,11 +327,9 @@ static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val)
 
 static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
 {
-       struct sx150x_chip *chip;
+       struct sx150x_chip *chip = to_sx150x(gc);
        int status = -EINVAL;
 
-       chip = container_of(gc, struct sx150x_chip, gpio_chip);
-
        if (!offset_is_oscio(chip, offset)) {
                mutex_lock(&chip->lock);
                status = sx150x_io_input(chip, offset);
@@ -343,11 +342,9 @@ static int sx150x_gpio_direction_output(struct gpio_chip *gc,
                                        unsigned offset,
                                        int val)
 {
-       struct sx150x_chip *chip;
+       struct sx150x_chip *chip = to_sx150x(gc);
        int status = 0;
 
-       chip = container_of(gc, struct sx150x_chip, gpio_chip);
-
        if (!offset_is_oscio(chip, offset)) {
                mutex_lock(&chip->lock);
                status = sx150x_io_output(chip, offset, val);
@@ -358,7 +355,7 @@ static int sx150x_gpio_direction_output(struct gpio_chip *gc,
 
 static void sx150x_irq_mask(struct irq_data *d)
 {
-       struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
+       struct sx150x_chip *chip = to_sx150x(irq_data_get_irq_chip_data(d));
        unsigned n = d->hwirq;
 
        chip->irq_masked |= (1 << n);
@@ -367,7 +364,7 @@ static void sx150x_irq_mask(struct irq_data *d)
 
 static void sx150x_irq_unmask(struct irq_data *d)
 {
-       struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
+       struct sx150x_chip *chip = to_sx150x(irq_data_get_irq_chip_data(d));
        unsigned n = d->hwirq;
 
        chip->irq_masked &= ~(1 << n);
@@ -376,7 +373,7 @@ static void sx150x_irq_unmask(struct irq_data *d)
 
 static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
 {
-       struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
+       struct sx150x_chip *chip = to_sx150x(irq_data_get_irq_chip_data(d));
        unsigned n, val = 0;
 
        if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
@@ -431,14 +428,14 @@ static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
 
 static void sx150x_irq_bus_lock(struct irq_data *d)
 {
-       struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
+       struct sx150x_chip *chip = to_sx150x(irq_data_get_irq_chip_data(d));
 
        mutex_lock(&chip->lock);
 }
 
 static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
 {
-       struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
+       struct sx150x_chip *chip = to_sx150x(irq_data_get_irq_chip_data(d));
        unsigned n;
 
        if (chip->irq_update == NO_UPDATE_PENDING)
index 12c99d969b983638ca16838919f34b0db76c7148..4356e6c20fc59a3792c8575417db5dfa389b3b66 100644 (file)
@@ -138,16 +138,6 @@ static int tb10x_gpio_direction_out(struct gpio_chip *chip,
        return 0;
 }
 
-static int tb10x_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void tb10x_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
 {
        struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
@@ -213,8 +203,8 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
        tb10x_gpio->gc.get              = tb10x_gpio_get;
        tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out;
        tb10x_gpio->gc.set              = tb10x_gpio_set;
-       tb10x_gpio->gc.request          = tb10x_gpio_request;
-       tb10x_gpio->gc.free             = tb10x_gpio_free;
+       tb10x_gpio->gc.request          = gpiochip_generic_request;
+       tb10x_gpio->gc.free             = gpiochip_generic_free;
        tb10x_gpio->gc.base             = -1;
        tb10x_gpio->gc.ngpio            = ngpio;
        tb10x_gpio->gc.can_sleep        = false;
index ede7e403ffdee8565530de696e77af69f083e7e7..3623d009d8087fc6d1fc1538c732b260e36709de 100644 (file)
@@ -137,16 +137,6 @@ static void tz1090_pdc_gpio_set(struct gpio_chip *chip, unsigned int offset,
        __global_unlock2(lstat);
 }
 
-static int tz1090_pdc_gpio_request(struct gpio_chip *chip, unsigned int offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void tz1090_pdc_gpio_free(struct gpio_chip *chip, unsigned int offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static int tz1090_pdc_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
 {
        struct tz1090_pdc_gpio *priv = to_pdc(chip);
@@ -203,8 +193,8 @@ static int tz1090_pdc_gpio_probe(struct platform_device *pdev)
        priv->chip.direction_output     = tz1090_pdc_gpio_direction_output;
        priv->chip.get                  = tz1090_pdc_gpio_get;
        priv->chip.set                  = tz1090_pdc_gpio_set;
-       priv->chip.free                 = tz1090_pdc_gpio_free;
-       priv->chip.request              = tz1090_pdc_gpio_request;
+       priv->chip.free                 = gpiochip_generic_free;
+       priv->chip.request              = gpiochip_generic_request;
        priv->chip.to_irq               = tz1090_pdc_gpio_to_irq;
        priv->chip.of_node              = np;
 
index 069f9e4b7daae0b5afe7bef0543b822191de22f3..87b950cec6ec929689a3d52b428f9224acebc623 100644 (file)
@@ -62,6 +62,11 @@ struct vf610_gpio_port {
 
 static struct irq_chip vf610_gpio_irq_chip;
 
+static struct vf610_gpio_port *to_vf610_gp(struct gpio_chip *gc)
+{
+       return container_of(gc, struct vf610_gpio_port, gc);
+}
+
 static const struct of_device_id vf610_gpio_dt_ids[] = {
        { .compatible = "fsl,vf610-gpio" },
        { /* sentinel */ }
@@ -77,28 +82,16 @@ static inline u32 vf610_gpio_readl(void __iomem *reg)
        return readl_relaxed(reg);
 }
 
-static int vf610_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void vf610_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio)
 {
-       struct vf610_gpio_port *port =
-               container_of(gc, struct vf610_gpio_port, gc);
+       struct vf610_gpio_port *port = to_vf610_gp(gc);
 
        return !!(vf610_gpio_readl(port->gpio_base + GPIO_PDIR) & BIT(gpio));
 }
 
 static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
 {
-       struct vf610_gpio_port *port =
-               container_of(gc, struct vf610_gpio_port, gc);
+       struct vf610_gpio_port *port = to_vf610_gp(gc);
        unsigned long mask = BIT(gpio);
 
        if (val)
@@ -122,7 +115,8 @@ static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
 
 static void vf610_gpio_irq_handler(struct irq_desc *desc)
 {
-       struct vf610_gpio_port *port = irq_desc_get_handler_data(desc);
+       struct vf610_gpio_port *port =
+               to_vf610_gp(irq_desc_get_handler_data(desc));
        struct irq_chip *chip = irq_desc_get_chip(desc);
        int pin;
        unsigned long irq_isfr;
@@ -142,7 +136,8 @@ static void vf610_gpio_irq_handler(struct irq_desc *desc)
 
 static void vf610_gpio_irq_ack(struct irq_data *d)
 {
-       struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
+       struct vf610_gpio_port *port =
+               to_vf610_gp(irq_data_get_irq_chip_data(d));
        int gpio = d->hwirq;
 
        vf610_gpio_writel(BIT(gpio), port->base + PORT_ISFR);
@@ -150,7 +145,8 @@ static void vf610_gpio_irq_ack(struct irq_data *d)
 
 static int vf610_gpio_irq_set_type(struct irq_data *d, u32 type)
 {
-       struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
+       struct vf610_gpio_port *port =
+               to_vf610_gp(irq_data_get_irq_chip_data(d));
        u8 irqc;
 
        switch (type) {
@@ -185,7 +181,8 @@ static int vf610_gpio_irq_set_type(struct irq_data *d, u32 type)
 
 static void vf610_gpio_irq_mask(struct irq_data *d)
 {
-       struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
+       struct vf610_gpio_port *port =
+               to_vf610_gp(irq_data_get_irq_chip_data(d));
        void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
 
        vf610_gpio_writel(0, pcr_base);
@@ -193,7 +190,8 @@ static void vf610_gpio_irq_mask(struct irq_data *d)
 
 static void vf610_gpio_irq_unmask(struct irq_data *d)
 {
-       struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
+       struct vf610_gpio_port *port =
+               to_vf610_gp(irq_data_get_irq_chip_data(d));
        void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
 
        vf610_gpio_writel(port->irqc[d->hwirq] << PORT_PCR_IRQC_OFFSET,
@@ -202,7 +200,8 @@ static void vf610_gpio_irq_unmask(struct irq_data *d)
 
 static int vf610_gpio_irq_set_wake(struct irq_data *d, u32 enable)
 {
-       struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
+       struct vf610_gpio_port *port =
+               to_vf610_gp(irq_data_get_irq_chip_data(d));
 
        if (enable)
                enable_irq_wake(port->irq);
@@ -255,8 +254,8 @@ static int vf610_gpio_probe(struct platform_device *pdev)
        gc->ngpio = VF610_GPIO_PER_PORT;
        gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT;
 
-       gc->request = vf610_gpio_request;
-       gc->free = vf610_gpio_free;
+       gc->request = gpiochip_generic_request;
+       gc->free = gpiochip_generic_free;
        gc->direction_input = vf610_gpio_direction_input;
        gc->get = vf610_gpio_get;
        gc->direction_output = vf610_gpio_direction_output;
index e02499a15e72263846e813ee1f990916113923b3..bc06a2cd2c1d6663add40cec337953feebcb443c 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
 
 /*
  * XLP GPIO has multiple 32 bit registers for each feature where each register
@@ -208,25 +209,28 @@ static struct irq_chip xlp_gpio_irq_chip = {
        .flags          = IRQCHIP_ONESHOT_SAFE,
 };
 
-static irqreturn_t xlp_gpio_generic_handler(int irq, void *data)
+static void xlp_gpio_generic_handler(struct irq_desc *desc)
 {
-       struct xlp_gpio_priv *priv = data;
+       struct xlp_gpio_priv *priv = irq_desc_get_handler_data(desc);
+       struct irq_chip *irqchip = irq_desc_get_chip(desc);
        int gpio, regoff;
        u32 gpio_stat;
 
        regoff = -1;
        gpio_stat = 0;
+
+       chained_irq_enter(irqchip, desc);
        for_each_set_bit(gpio, priv->gpio_enabled_mask, XLP_MAX_NR_GPIO) {
                if (regoff != gpio / XLP_GPIO_REGSZ) {
                        regoff = gpio / XLP_GPIO_REGSZ;
                        gpio_stat = readl(priv->gpio_intr_stat + regoff * 4);
                }
+
                if (gpio_stat & BIT(gpio % XLP_GPIO_REGSZ))
                        generic_handle_irq(irq_find_mapping(
                                                priv->chip.irqdomain, gpio));
        }
-
-       return IRQ_HANDLED;
+       chained_irq_exit(irqchip, desc);
 }
 
 static int xlp_gpio_dir_output(struct gpio_chip *gc, unsigned gpio, int state)
@@ -378,12 +382,6 @@ static int xlp_gpio_probe(struct platform_device *pdev)
        gc->get = xlp_gpio_get;
 
        spin_lock_init(&priv->lock);
-
-       err = devm_request_irq(&pdev->dev, irq, xlp_gpio_generic_handler,
-                       IRQ_TYPE_NONE, pdev->name, priv);
-       if (err)
-               return err;
-
        irq_base = irq_alloc_descs(-1, XLP_GPIO_IRQ_BASE, gc->ngpio, 0);
        if (irq_base < 0) {
                dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n");
@@ -401,6 +399,9 @@ static int xlp_gpio_probe(struct platform_device *pdev)
                goto out_gpio_remove;
        }
 
+       gpiochip_set_chained_irqchip(gc, &xlp_gpio_irq_chip, irq,
+                       xlp_gpio_generic_handler);
+
        dev_info(&pdev->dev, "registered %d GPIOs\n", gc->ngpio);
 
        return 0;
index 4b8a2691070537cde2affdce5ff7624d032ce193..1dcf7a66dd36ed62eda1cf2bb4b4a867a29b89c5 100644 (file)
@@ -41,7 +41,6 @@ struct zx_gpio {
 
        void __iomem            *base;
        struct gpio_chip        gc;
-       bool                    uses_pinctrl;
 };
 
 static inline struct zx_gpio *to_zx(struct gpio_chip *gc)
@@ -49,25 +48,6 @@ static inline struct zx_gpio *to_zx(struct gpio_chip *gc)
        return container_of(gc, struct zx_gpio, gc);
 }
 
-static int zx_gpio_request(struct gpio_chip *gc, unsigned offset)
-{
-       struct zx_gpio *chip = to_zx(gc);
-       int gpio = gc->base + offset;
-
-       if (chip->uses_pinctrl)
-               return pinctrl_request_gpio(gpio);
-       return 0;
-}
-
-static void zx_gpio_free(struct gpio_chip *gc, unsigned offset)
-{
-       struct zx_gpio *chip = to_zx(gc);
-       int gpio = gc->base + offset;
-
-       if (chip->uses_pinctrl)
-               pinctrl_free_gpio(gpio);
-}
-
 static int zx_direction_input(struct gpio_chip *gc, unsigned offset)
 {
        struct zx_gpio *chip = to_zx(gc);
@@ -252,12 +232,12 @@ static int zx_gpio_probe(struct platform_device *pdev)
                return PTR_ERR(chip->base);
 
        spin_lock_init(&chip->lock);
-       if (of_property_read_bool(dev->of_node, "gpio-ranges"))
-               chip->uses_pinctrl = true;
+       if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
+               chip->gc.request = gpiochip_generic_request;
+               chip->gc.free = gpiochip_generic_free;
+       }
 
        id = of_alias_get_id(dev->of_node, "gpio");
-       chip->gc.request = zx_gpio_request;
-       chip->gc.free = zx_gpio_free;
        chip->gc.direction_input = zx_direction_input;
        chip->gc.direction_output = zx_direction_output;
        chip->gc.get = zx_get_value;
index 1d1a5865ede90449d8e2b4abba88f14f6c225b82..8abeacac588579c16eaf7e537f7da96b44b2d490 100644 (file)
@@ -130,6 +130,12 @@ struct zynq_platform_data {
 
 static struct irq_chip zynq_gpio_level_irqchip;
 static struct irq_chip zynq_gpio_edge_irqchip;
+
+static struct zynq_gpio *to_zynq_gpio(struct gpio_chip *gc)
+{
+       return container_of(gc, struct zynq_gpio, chip);
+}
+
 /**
  * zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank
  * for a given pin in the GPIO device
@@ -177,7 +183,7 @@ static int zynq_gpio_get_value(struct gpio_chip *chip, unsigned int pin)
 {
        u32 data;
        unsigned int bank_num, bank_pin_num;
-       struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
+       struct zynq_gpio *gpio = to_zynq_gpio(chip);
 
        zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
 
@@ -201,7 +207,7 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
                                int state)
 {
        unsigned int reg_offset, bank_num, bank_pin_num;
-       struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
+       struct zynq_gpio *gpio = to_zynq_gpio(chip);
 
        zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
 
@@ -238,7 +244,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
 {
        u32 reg;
        unsigned int bank_num, bank_pin_num;
-       struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
+       struct zynq_gpio *gpio = to_zynq_gpio(chip);
 
        zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
 
@@ -271,7 +277,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
 {
        u32 reg;
        unsigned int bank_num, bank_pin_num;
-       struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
+       struct zynq_gpio *gpio = to_zynq_gpio(chip);
 
        zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
 
@@ -301,7 +307,8 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
 static void zynq_gpio_irq_mask(struct irq_data *irq_data)
 {
        unsigned int device_pin_num, bank_num, bank_pin_num;
-       struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
+       struct zynq_gpio *gpio =
+               to_zynq_gpio(irq_data_get_irq_chip_data(irq_data));
 
        device_pin_num = irq_data->hwirq;
        zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
@@ -321,7 +328,8 @@ static void zynq_gpio_irq_mask(struct irq_data *irq_data)
 static void zynq_gpio_irq_unmask(struct irq_data *irq_data)
 {
        unsigned int device_pin_num, bank_num, bank_pin_num;
-       struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
+       struct zynq_gpio *gpio =
+               to_zynq_gpio(irq_data_get_irq_chip_data(irq_data));
 
        device_pin_num = irq_data->hwirq;
        zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
@@ -340,7 +348,8 @@ static void zynq_gpio_irq_unmask(struct irq_data *irq_data)
 static void zynq_gpio_irq_ack(struct irq_data *irq_data)
 {
        unsigned int device_pin_num, bank_num, bank_pin_num;
-       struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
+       struct zynq_gpio *gpio =
+               to_zynq_gpio(irq_data_get_irq_chip_data(irq_data));
 
        device_pin_num = irq_data->hwirq;
        zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
@@ -390,7 +399,8 @@ static int zynq_gpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
 {
        u32 int_type, int_pol, int_any;
        unsigned int device_pin_num, bank_num, bank_pin_num;
-       struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
+       struct zynq_gpio *gpio =
+               to_zynq_gpio(irq_data_get_irq_chip_data(irq_data));
 
        device_pin_num = irq_data->hwirq;
        zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
@@ -453,7 +463,8 @@ static int zynq_gpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
 
 static int zynq_gpio_set_wake(struct irq_data *data, unsigned int on)
 {
-       struct zynq_gpio *gpio = irq_data_get_irq_chip_data(data);
+       struct zynq_gpio *gpio =
+               to_zynq_gpio(irq_data_get_irq_chip_data(data));
 
        irq_set_irq_wake(gpio->irq, on);
 
@@ -518,7 +529,8 @@ static void zynq_gpio_irqhandler(struct irq_desc *desc)
 {
        u32 int_sts, int_enb;
        unsigned int bank_num;
-       struct zynq_gpio *gpio = irq_desc_get_handler_data(desc);
+       struct zynq_gpio *gpio =
+               to_zynq_gpio(irq_desc_get_handler_data(desc));
        struct irq_chip *irqchip = irq_desc_get_chip(desc);
 
        chained_irq_enter(irqchip, desc);
index 143a9bdbaa53e1ba056a02549e521ccd6efc2220..bbcac3af2a7ab84804fa862fe6a31869ca35aca3 100644 (file)
@@ -304,7 +304,6 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
        if (ACPI_FAILURE(status))
                return;
 
-       INIT_LIST_HEAD(&acpi_gpio->events);
        acpi_walk_resources(handle, "_AEI",
                            acpi_gpiochip_request_interrupt, acpi_gpio);
 }
@@ -603,6 +602,25 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
                                break;
                        }
                }
+
+               /*
+                * The same GPIO can be shared between operation region and
+                * event but only if the access here is ACPI_READ. In that
+                * case we "borrow" the event GPIO instead.
+                */
+               if (!found && agpio->sharable == ACPI_SHARED &&
+                    function == ACPI_READ) {
+                       struct acpi_gpio_event *event;
+
+                       list_for_each_entry(event, &achip->events, node) {
+                               if (event->pin == pin) {
+                                       desc = event->desc;
+                                       found = true;
+                                       break;
+                               }
+                       }
+               }
+
                if (!found) {
                        desc = gpiochip_request_own_desc(chip, pin,
                                                         "ACPI:OpRegion");
@@ -719,6 +737,7 @@ void acpi_gpiochip_add(struct gpio_chip *chip)
        }
 
        acpi_gpio->chip = chip;
+       INIT_LIST_HEAD(&acpi_gpio->events);
 
        status = acpi_attach_data(handle, acpi_gpio_chip_dh, acpi_gpio);
        if (ACPI_FAILURE(status)) {
index 8b830996fe0212d3ae0153ae5b708a84fa53976e..3a5c7011ad3b3e832d7cce6e55ed62562bba94be 100644 (file)
@@ -28,10 +28,6 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
        if (!desc && gpio_is_valid(gpio))
                return -EPROBE_DEFER;
 
-       err = gpiod_request(desc, label);
-       if (err)
-               return err;
-
        if (flags & GPIOF_OPEN_DRAIN)
                set_bit(FLAG_OPEN_DRAIN, &desc->flags);
 
@@ -41,6 +37,10 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
        if (flags & GPIOF_ACTIVE_LOW)
                set_bit(FLAG_ACTIVE_LOW, &desc->flags);
 
+       err = gpiod_request(desc, label);
+       if (err)
+               return err;
+
        if (flags & GPIOF_DIR_IN)
                err = gpiod_direction_input(desc);
        else
index fa6e3c8823d614dba89167e9eca1ad2dd74577b8..5fe34a9df3e6b8538ddc47a8ec6008654c89d89c 100644 (file)
@@ -119,20 +119,20 @@ int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
 EXPORT_SYMBOL(of_get_named_gpio_flags);
 
 /**
- * of_get_gpio_hog() - Get a GPIO hog descriptor, names and flags for GPIO API
+ * of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API
  * @np:                device node to get GPIO from
  * @name:      GPIO line name
  * @lflags:    gpio_lookup_flags - returned from of_find_gpio() or
- *             of_get_gpio_hog()
+ *             of_parse_own_gpio()
  * @dflags:    gpiod_flags - optional GPIO initialization flags
  *
  * Returns GPIO descriptor to use with Linux GPIO API, or one of the errno
  * value on the error condition.
  */
-static struct gpio_desc *of_get_gpio_hog(struct device_node *np,
-                                 const char **name,
-                                 enum gpio_lookup_flags *lflags,
-                                 enum gpiod_flags *dflags)
+static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
+                                          const char **name,
+                                          enum gpio_lookup_flags *lflags,
+                                          enum gpiod_flags *dflags)
 {
        struct device_node *chip_np;
        enum of_gpio_flags xlate_flags;
@@ -196,13 +196,13 @@ static struct gpio_desc *of_get_gpio_hog(struct device_node *np,
 }
 
 /**
- * of_gpiochip_scan_hogs - Scan gpio-controller and apply GPIO hog as requested
+ * of_gpiochip_scan_gpios - Scan gpio-controller for gpio definitions
  * @chip:      gpio chip to act on
  *
  * This is only used by of_gpiochip_add to request/set GPIO initial
  * configuration.
  */
-static void of_gpiochip_scan_hogs(struct gpio_chip *chip)
+static void of_gpiochip_scan_gpios(struct gpio_chip *chip)
 {
        struct gpio_desc *desc = NULL;
        struct device_node *np;
@@ -214,7 +214,7 @@ static void of_gpiochip_scan_hogs(struct gpio_chip *chip)
                if (!of_property_read_bool(np, "gpio-hog"))
                        continue;
 
-               desc = of_get_gpio_hog(np, &name, &lflags, &dflags);
+               desc = of_parse_own_gpio(np, &name, &lflags, &dflags);
                if (IS_ERR(desc))
                        continue;
 
@@ -440,7 +440,7 @@ int of_gpiochip_add(struct gpio_chip *chip)
 
        of_node_get(chip->of_node);
 
-       of_gpiochip_scan_hogs(chip);
+       of_gpiochip_scan_gpios(chip);
 
        return 0;
 }
index 5db3445552b176d2c11ca8b225ef88b4b80d22eb..6798355c61c6f763c6d30cc5ec623d0afd873827 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/acpi.h>
 #include <linux/gpio/driver.h>
 #include <linux/gpio/machine.h>
+#include <linux/pinctrl/consumer.h>
 
 #include "gpiolib.h"
 
@@ -47,8 +48,6 @@
  */
 DEFINE_SPINLOCK(gpio_lock);
 
-#define GPIO_OFFSET_VALID(chip, offset) (offset >= 0 && offset < chip->ngpio)
-
 static DEFINE_MUTEX(gpio_lookup_lock);
 static LIST_HEAD(gpio_lookup_list);
 LIST_HEAD(gpio_chips);
@@ -218,6 +217,68 @@ static int gpiochip_add_to_list(struct gpio_chip *chip)
        return err;
 }
 
+/**
+ * Convert a GPIO name to its descriptor
+ */
+static struct gpio_desc *gpio_name_to_desc(const char * const name)
+{
+       struct gpio_chip *chip;
+       unsigned long flags;
+
+       spin_lock_irqsave(&gpio_lock, flags);
+
+       list_for_each_entry(chip, &gpio_chips, list) {
+               int i;
+
+               for (i = 0; i != chip->ngpio; ++i) {
+                       struct gpio_desc *gpio = &chip->desc[i];
+
+                       if (!gpio->name)
+                               continue;
+
+                       if (!strcmp(gpio->name, name)) {
+                               spin_unlock_irqrestore(&gpio_lock, flags);
+                               return gpio;
+                       }
+               }
+       }
+
+       spin_unlock_irqrestore(&gpio_lock, flags);
+
+       return NULL;
+}
+
+/*
+ * Takes the names from gc->names and checks if they are all unique. If they
+ * are, they are assigned to their gpio descriptors.
+ *
+ * Returns -EEXIST if one of the names is already used for a different GPIO.
+ */
+static int gpiochip_set_desc_names(struct gpio_chip *gc)
+{
+       int i;
+
+       if (!gc->names)
+               return 0;
+
+       /* First check all names if they are unique */
+       for (i = 0; i != gc->ngpio; ++i) {
+               struct gpio_desc *gpio;
+
+               gpio = gpio_name_to_desc(gc->names[i]);
+               if (gpio)
+                       dev_warn(gc->dev, "Detected name collision for "
+                                "GPIO name '%s'\n",
+                                gc->names[i]);
+       }
+
+       /* Then add all names to the GPIO descriptors */
+       for (i = 0; i != gc->ngpio; ++i)
+               gc->desc[i].name = gc->names[i];
+
+       return 0;
+}
+
 /**
  * gpiochip_add() - register a gpio_chip
  * @chip: the chip to register, with chip->base initialized
@@ -290,6 +351,10 @@ int gpiochip_add(struct gpio_chip *chip)
        if (!chip->owner && chip->dev && chip->dev->driver)
                chip->owner = chip->dev->driver->owner;
 
+       status = gpiochip_set_desc_names(chip);
+       if (status)
+               goto err_remove_from_list;
+
        status = of_gpiochip_add(chip);
        if (status)
                goto err_remove_chip;
@@ -310,6 +375,7 @@ err_remove_chip:
        acpi_gpiochip_remove(chip);
        gpiochip_free_hogs(chip);
        of_gpiochip_remove(chip);
+err_remove_from_list:
        spin_lock_irqsave(&gpio_lock, flags);
        list_del(&chip->list);
        spin_unlock_irqrestore(&gpio_lock, flags);
@@ -680,6 +746,28 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {}
 
 #endif /* CONFIG_GPIOLIB_IRQCHIP */
 
+/**
+ * gpiochip_generic_request() - request the gpio function for a pin
+ * @chip: the gpiochip owning the GPIO
+ * @offset: the offset of the GPIO to request for GPIO function
+ */
+int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset)
+{
+       return pinctrl_request_gpio(chip->base + offset);
+}
+EXPORT_SYMBOL_GPL(gpiochip_generic_request);
+
+/**
+ * gpiochip_generic_free() - free the gpio function from a pin
+ * @chip: the gpiochip to request the gpio function for
+ * @offset: the offset of the GPIO to free from GPIO function
+ */
+void gpiochip_generic_free(struct gpio_chip *chip, unsigned offset)
+{
+       pinctrl_free_gpio(chip->base + offset);
+}
+EXPORT_SYMBOL_GPL(gpiochip_generic_free);
+
 #ifdef CONFIG_PINCTRL
 
 /**
@@ -839,6 +927,14 @@ static int __gpiod_request(struct gpio_desc *desc, const char *label)
                spin_lock_irqsave(&gpio_lock, flags);
        }
 done:
+       if (status < 0) {
+               /* Clear flags that might have been set by the caller before
+                * requesting the GPIO.
+                */
+               clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
+               clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
+               clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
+       }
        spin_unlock_irqrestore(&gpio_lock, flags);
        return status;
 }
@@ -928,7 +1024,7 @@ const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
 {
        struct gpio_desc *desc;
 
-       if (!GPIO_OFFSET_VALID(chip, offset))
+       if (offset >= chip->ngpio)
                return NULL;
 
        desc = &chip->desc[offset];
@@ -1735,6 +1831,13 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
        if (of_flags & OF_GPIO_ACTIVE_LOW)
                *flags |= GPIO_ACTIVE_LOW;
 
+       if (of_flags & OF_GPIO_SINGLE_ENDED) {
+               if (of_flags & OF_GPIO_ACTIVE_LOW)
+                       *flags |= GPIO_OPEN_DRAIN;
+               else
+                       *flags |= GPIO_OPEN_SOURCE;
+       }
+
        return desc;
 }
 
@@ -1953,13 +2056,28 @@ struct gpio_desc *__must_check gpiod_get_optional(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(gpiod_get_optional);
 
+/**
+ * gpiod_parse_flags - helper function to parse GPIO lookup flags
+ * @desc:      gpio to be setup
+ * @lflags:    gpio_lookup_flags - returned from of_find_gpio() or
+ *             of_get_gpio_hog()
+ *
+ * Set the GPIO descriptor flags based on the given GPIO lookup flags.
+ */
+static void gpiod_parse_flags(struct gpio_desc *desc, unsigned long lflags)
+{
+       if (lflags & GPIO_ACTIVE_LOW)
+               set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+       if (lflags & GPIO_OPEN_DRAIN)
+               set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+       if (lflags & GPIO_OPEN_SOURCE)
+               set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+}
 
 /**
  * gpiod_configure_flags - helper function to configure a given GPIO
  * @desc:      gpio whose value will be assigned
  * @con_id:    function within the GPIO consumer
- * @lflags:    gpio_lookup_flags - returned from of_find_gpio() or
- *             of_get_gpio_hog()
  * @dflags:    gpiod_flags - optional GPIO initialization flags
  *
  * Return 0 on success, -ENOENT if no GPIO has been assigned to the
@@ -1967,17 +2085,10 @@ EXPORT_SYMBOL_GPL(gpiod_get_optional);
  * occurred while trying to acquire the GPIO.
  */
 static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
-               unsigned long lflags, enum gpiod_flags dflags)
+                                enum gpiod_flags dflags)
 {
        int status;
 
-       if (lflags & GPIO_ACTIVE_LOW)
-               set_bit(FLAG_ACTIVE_LOW, &desc->flags);
-       if (lflags & GPIO_OPEN_DRAIN)
-               set_bit(FLAG_OPEN_DRAIN, &desc->flags);
-       if (lflags & GPIO_OPEN_SOURCE)
-               set_bit(FLAG_OPEN_SOURCE, &desc->flags);
-
        /* No particular flag request, return here... */
        if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
                pr_debug("no flags found for %s\n", con_id);
@@ -2044,11 +2155,13 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
                return desc;
        }
 
+       gpiod_parse_flags(desc, lookupflags);
+
        status = gpiod_request(desc, con_id);
        if (status < 0)
                return ERR_PTR(status);
 
-       status = gpiod_configure_flags(desc, con_id, lookupflags, flags);
+       status = gpiod_configure_flags(desc, con_id, flags);
        if (status < 0) {
                dev_dbg(dev, "setup of GPIO %s failed\n", con_id);
                gpiod_put(desc);
@@ -2078,6 +2191,7 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
 {
        struct gpio_desc *desc = ERR_PTR(-ENODEV);
        bool active_low = false;
+       bool single_ended = false;
        int ret;
 
        if (!fwnode)
@@ -2088,8 +2202,10 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
 
                desc = of_get_named_gpiod_flags(to_of_node(fwnode), propname, 0,
                                                &flags);
-               if (!IS_ERR(desc))
+               if (!IS_ERR(desc)) {
                        active_low = flags & OF_GPIO_ACTIVE_LOW;
+                       single_ended = flags & OF_GPIO_SINGLE_ENDED;
+               }
        } else if (is_acpi_node(fwnode)) {
                struct acpi_gpio_info info;
 
@@ -2102,14 +2218,20 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
        if (IS_ERR(desc))
                return desc;
 
+       if (active_low)
+               set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+
+       if (single_ended) {
+               if (active_low)
+                       set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+               else
+                       set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+       }
+
        ret = gpiod_request(desc, NULL);
        if (ret)
                return ERR_PTR(ret);
 
-       /* Only value flag can be set from both DT and ACPI is active_low */
-       if (active_low)
-               set_bit(FLAG_ACTIVE_LOW, &desc->flags);
-
        return desc;
 }
 EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
@@ -2162,6 +2284,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
        chip = gpiod_to_chip(desc);
        hwnum = gpio_chip_hwgpio(desc);
 
+       gpiod_parse_flags(desc, lflags);
+
        local_desc = gpiochip_request_own_desc(chip, hwnum, name);
        if (IS_ERR(local_desc)) {
                pr_err("requesting hog GPIO %s (chip %s, offset %d) failed\n",
@@ -2169,7 +2293,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
                return PTR_ERR(local_desc);
        }
 
-       status = gpiod_configure_flags(desc, name, lflags, dflags);
+       status = gpiod_configure_flags(desc, name, dflags);
        if (status < 0) {
                pr_err("setup of hog GPIO %s (chip %s, offset %d) failed\n",
                       name, chip->label, hwnum);
@@ -2309,14 +2433,19 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
        int                     is_irq;
 
        for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
-               if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
+               if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) {
+                       if (gdesc->name) {
+                               seq_printf(s, " gpio-%-3d (%-20.20s)\n",
+                                          gpio, gdesc->name);
+                       }
                        continue;
+               }
 
                gpiod_get_direction(gdesc);
                is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
                is_irq = test_bit(FLAG_USED_AS_IRQ, &gdesc->flags);
-               seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s",
-                       gpio, gdesc->label,
+               seq_printf(s, " gpio-%-3d (%-20.20s|%-20.20s) %s %s %s",
+                       gpio, gdesc->name ? gdesc->name : "", gdesc->label,
                        is_out ? "out" : "in ",
                        chip->get
                                ? (chip->get(chip, i) ? "hi" : "lo")
index bf343004b0085c4bcc21243a63195cb875ca636a..78e634d1c719b10846e4399de1c766f644d2f31c 100644 (file)
@@ -89,7 +89,10 @@ struct gpio_desc {
 #define FLAG_USED_AS_IRQ 9     /* GPIO is connected to an IRQ */
 #define FLAG_IS_HOGGED 11      /* GPIO is hogged */
 
+       /* Connection label */
        const char              *label;
+       /* Name of the GPIO */
+       const char              *name;
 };
 
 int gpiod_request(struct gpio_desc *desc, const char *label);
index 6647fb26ef25ce21dba9bffb87cb31e5abe73d80..0d13e6368b96de2ace3b4373a7aebd20f5cd3826 100644 (file)
@@ -1654,6 +1654,7 @@ struct amdgpu_pm {
        u8                      fan_max_rpm;
        /* dpm */
        bool                    dpm_enabled;
+       bool                    sysfs_initialized;
        struct amdgpu_dpm       dpm;
        const struct firmware   *fw;    /* SMC firmware */
        uint32_t                fw_version;
index dc29ed8145c256c61c6c47da3671664175f4f30d..6c9e0902a41438920ddfcbdde9408b3792b41b0e 100644 (file)
@@ -184,10 +184,6 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
                goto cleanup;
        }
 
-       fence_get(work->excl);
-       for (i = 0; i < work->shared_count; ++i)
-               fence_get(work->shared[i]);
-
        amdgpu_bo_get_tiling_flags(new_rbo, &tiling_flags);
        amdgpu_bo_unreserve(new_rbo);
 
index ed2bbe5b10afe9272cabe2325b183e508c3a1e61..22a8c7d3a3ab03e9dc3fb294c1f0eb818a6a4880 100644 (file)
@@ -695,6 +695,9 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev)
 {
        int ret;
 
+       if (adev->pm.sysfs_initialized)
+               return 0;
+
        if (adev->pm.funcs->get_temperature == NULL)
                return 0;
        adev->pm.int_hwmon_dev = hwmon_device_register_with_groups(adev->dev,
@@ -723,6 +726,8 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev)
                return ret;
        }
 
+       adev->pm.sysfs_initialized = true;
+
        return 0;
 }
 
index 33d877c65ced6a3c138af9ab47c824038410be0b..8328e7059205d266a456710613628c64b097f787 100644 (file)
@@ -4105,7 +4105,7 @@ drm_property_create_blob(struct drm_device *dev, size_t length,
        struct drm_property_blob *blob;
        int ret;
 
-       if (!length)
+       if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob))
                return ERR_PTR(-EINVAL);
 
        blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
@@ -4454,7 +4454,7 @@ int drm_mode_createblob_ioctl(struct drm_device *dev,
         * not associated with any file_priv. */
        mutex_lock(&dev->mode_config.blob_lock);
        out_resp->blob_id = blob->base.id;
-       list_add_tail(&file_priv->blobs, &blob->head_file);
+       list_add_tail(&blob->head_file, &file_priv->blobs);
        mutex_unlock(&dev->mode_config.blob_lock);
 
        return 0;
index 65adb9c723772d9f0011573d320182823b18d37a..bb292143997ee8ccce3e6e6898edba2a5eeb12b6 100644 (file)
@@ -237,6 +237,7 @@ void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder,
        backlight_update_status(bd);
 
        DRM_INFO("radeon atom DIG backlight initialized\n");
+       rdev->mode_info.bl_encoder = radeon_encoder;
 
        return;
 
@@ -1624,9 +1625,14 @@ radeon_atom_encoder_dpms_avivo(struct drm_encoder *encoder, int mode)
                } else
                        atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
                if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
-                       struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+                       if (rdev->mode_info.bl_encoder) {
+                               struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
 
-                       atombios_set_backlight_level(radeon_encoder, dig->backlight_level);
+                               atombios_set_backlight_level(radeon_encoder, dig->backlight_level);
+                       } else {
+                               args.ucAction = ATOM_LCD_BLON;
+                               atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+                       }
                }
                break;
        case DRM_MODE_DPMS_STANDBY:
@@ -1706,8 +1712,13 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
                        if (ASIC_IS_DCE4(rdev))
                                atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0);
                }
-               if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
-                       atombios_set_backlight_level(radeon_encoder, dig->backlight_level);
+               if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+                       if (rdev->mode_info.bl_encoder)
+                               atombios_set_backlight_level(radeon_encoder, dig->backlight_level);
+                       else
+                               atombios_dig_transmitter_setup(encoder,
+                                                              ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0);
+               }
                if (ext_encoder)
                        atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE);
                break;
index f03b7eb152336d1f799ab33d1d195c1fba40b360..b6cbd816537e7e69bc920482961cdb47b6dfd68d 100644 (file)
@@ -1658,6 +1658,7 @@ struct radeon_pm {
        u8                      fan_max_rpm;
        /* dpm */
        bool                    dpm_enabled;
+       bool                    sysfs_initialized;
        struct radeon_dpm       dpm;
 };
 
index ef99917f000d96a7dcf3fc88f089bdf08e0afc28..c6ee80216cf4a71f510bf239fc4fdf6f67235f48 100644 (file)
@@ -194,7 +194,6 @@ static void radeon_encoder_add_backlight(struct radeon_encoder *radeon_encoder,
                        radeon_atom_backlight_init(radeon_encoder, connector);
                else
                        radeon_legacy_backlight_init(radeon_encoder, connector);
-               rdev->mode_info.bl_encoder = radeon_encoder;
        }
 }
 
index 45715307db7177a9af7393a697d255c8ed277344..30de43366eae806d0afd45192ce9da032c08490c 100644 (file)
@@ -441,6 +441,7 @@ void radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder,
        backlight_update_status(bd);
 
        DRM_INFO("radeon legacy LVDS backlight initialized\n");
+       rdev->mode_info.bl_encoder = radeon_encoder;
 
        return;
 
index 6a0a176e26ec2ad82df440a94cbe7822d17e393f..5feee3b4c55741011ae3501f501f72df0e1311ac 100644 (file)
@@ -1528,19 +1528,23 @@ int radeon_pm_late_init(struct radeon_device *rdev)
 
        if (rdev->pm.pm_method == PM_METHOD_DPM) {
                if (rdev->pm.dpm_enabled) {
-                       ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state);
-                       if (ret)
-                               DRM_ERROR("failed to create device file for dpm state\n");
-                       ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
-                       if (ret)
-                               DRM_ERROR("failed to create device file for dpm state\n");
-                       /* XXX: these are noops for dpm but are here for backwards compat */
-                       ret = device_create_file(rdev->dev, &dev_attr_power_profile);
-                       if (ret)
-                               DRM_ERROR("failed to create device file for power profile\n");
-                       ret = device_create_file(rdev->dev, &dev_attr_power_method);
-                       if (ret)
-                               DRM_ERROR("failed to create device file for power method\n");
+                       if (!rdev->pm.sysfs_initialized) {
+                               ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state);
+                               if (ret)
+                                       DRM_ERROR("failed to create device file for dpm state\n");
+                               ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
+                               if (ret)
+                                       DRM_ERROR("failed to create device file for dpm state\n");
+                               /* XXX: these are noops for dpm but are here for backwards compat */
+                               ret = device_create_file(rdev->dev, &dev_attr_power_profile);
+                               if (ret)
+                                       DRM_ERROR("failed to create device file for power profile\n");
+                               ret = device_create_file(rdev->dev, &dev_attr_power_method);
+                               if (ret)
+                                       DRM_ERROR("failed to create device file for power method\n");
+                               if (!ret)
+                                       rdev->pm.sysfs_initialized = true;
+                       }
 
                        mutex_lock(&rdev->pm.mutex);
                        ret = radeon_dpm_late_enable(rdev);
@@ -1556,7 +1560,8 @@ int radeon_pm_late_init(struct radeon_device *rdev)
                        }
                }
        } else {
-               if (rdev->pm.num_power_states > 1) {
+               if ((rdev->pm.num_power_states > 1) &&
+                   (!rdev->pm.sysfs_initialized)) {
                        /* where's the best place to put these? */
                        ret = device_create_file(rdev->dev, &dev_attr_power_profile);
                        if (ret)
@@ -1564,6 +1569,8 @@ int radeon_pm_late_init(struct radeon_device *rdev)
                        ret = device_create_file(rdev->dev, &dev_attr_power_method);
                        if (ret)
                                DRM_ERROR("failed to create device file for power method\n");
+                       if (!ret)
+                               rdev->pm.sysfs_initialized = true;
                }
        }
        return ret;
index 8a76821177a6c0c1a3cc9659b4f119943d48b3ce..6377e8151000f5401309133b635150acf128c2ad 100644 (file)
@@ -415,16 +415,16 @@ static void vmw_cmdbuf_ctx_process(struct vmw_cmdbuf_man *man,
  *
  * Calls vmw_cmdbuf_ctx_process() on all contexts. If any context has
  * command buffers left that are not submitted to hardware, Make sure
- * IRQ handling is turned on. Otherwise, make sure it's turned off. This
- * function may return -EAGAIN to indicate it should be rerun due to
- * possibly missed IRQs if IRQs has just been turned on.
+ * IRQ handling is turned on. Otherwise, make sure it's turned off.
  */
-static int vmw_cmdbuf_man_process(struct vmw_cmdbuf_man *man)
+static void vmw_cmdbuf_man_process(struct vmw_cmdbuf_man *man)
 {
-       int notempty = 0;
+       int notempty;
        struct vmw_cmdbuf_context *ctx;
        int i;
 
+retry:
+       notempty = 0;
        for_each_cmdbuf_ctx(man, i, ctx)
                vmw_cmdbuf_ctx_process(man, ctx, &notempty);
 
@@ -440,10 +440,8 @@ static int vmw_cmdbuf_man_process(struct vmw_cmdbuf_man *man)
                man->irq_on = true;
 
                /* Rerun in case we just missed an irq. */
-               return -EAGAIN;
+               goto retry;
        }
-
-       return 0;
 }
 
 /**
@@ -468,8 +466,7 @@ static void vmw_cmdbuf_ctx_add(struct vmw_cmdbuf_man *man,
        header->cb_context = cb_context;
        list_add_tail(&header->list, &man->ctx[cb_context].submitted);
 
-       if (vmw_cmdbuf_man_process(man) == -EAGAIN)
-               vmw_cmdbuf_man_process(man);
+       vmw_cmdbuf_man_process(man);
 }
 
 /**
@@ -488,8 +485,7 @@ static void vmw_cmdbuf_man_tasklet(unsigned long data)
        struct vmw_cmdbuf_man *man = (struct vmw_cmdbuf_man *) data;
 
        spin_lock(&man->lock);
-       if (vmw_cmdbuf_man_process(man) == -EAGAIN)
-               (void) vmw_cmdbuf_man_process(man);
+       vmw_cmdbuf_man_process(man);
        spin_unlock(&man->lock);
 }
 
@@ -507,6 +503,7 @@ static void vmw_cmdbuf_work_func(struct work_struct *work)
        struct vmw_cmdbuf_man *man =
                container_of(work, struct vmw_cmdbuf_man, work);
        struct vmw_cmdbuf_header *entry, *next;
+       uint32_t dummy;
        bool restart = false;
 
        spin_lock_bh(&man->lock);
@@ -523,6 +520,8 @@ static void vmw_cmdbuf_work_func(struct work_struct *work)
        if (restart && vmw_cmdbuf_startstop(man, true))
                DRM_ERROR("Failed restarting command buffer context 0.\n");
 
+       /* Send a new fence in case one was removed */
+       vmw_fifo_send_fence(man->dev_priv, &dummy);
 }
 
 /**
@@ -682,7 +681,7 @@ static bool vmw_cmdbuf_try_alloc(struct vmw_cmdbuf_man *man,
                                         DRM_MM_SEARCH_DEFAULT,
                                         DRM_MM_CREATE_DEFAULT);
        if (ret) {
-               (void) vmw_cmdbuf_man_process(man);
+               vmw_cmdbuf_man_process(man);
                ret = drm_mm_insert_node_generic(&man->mm, info->node,
                                                 info->page_size, 0, 0,
                                                 DRM_MM_SEARCH_DEFAULT,
@@ -1168,7 +1167,14 @@ int vmw_cmdbuf_set_pool_size(struct vmw_cmdbuf_man *man,
        drm_mm_init(&man->mm, 0, size >> PAGE_SHIFT);
 
        man->has_pool = true;
-       man->default_size = default_size;
+
+       /*
+        * For now, set the default size to VMW_CMDBUF_INLINE_SIZE to
+        * prevent deadlocks from happening when vmw_cmdbuf_space_pool()
+        * needs to wait for space and we block on further command
+        * submissions to be able to free up space.
+        */
+       man->default_size = VMW_CMDBUF_INLINE_SIZE;
        DRM_INFO("Using command buffers with %s pool.\n",
                 (man->using_mob) ? "MOB" : "DMA");
 
index e13c902e8966977581f1959155b3f83774e94aa5..796569eeaf1d762c9d0283165b132c7a196d0e30 100644 (file)
@@ -840,6 +840,16 @@ config SENSORS_MAX6697
          This driver can also be built as a module.  If so, the module
          will be called max6697.
 
+config SENSORS_MAX31790
+       tristate "Maxim MAX31790 sensor chip"
+       depends on I2C
+       help
+         If you say yes here you get support for 6-Channel PWM-Output
+         Fan RPM Controller.
+
+         This driver can also be built as a module.  If so, the module
+         will be called max31790.
+
 config SENSORS_HTU21
        tristate "Measurement Specialties HTU21D humidity/temperature sensors"
        depends on I2C
index 9e0f3dd2841daaa1531acd3897c9077b2d18a09d..01855ee641d1d358dd01ed035ce6b20c0cbd080f 100644 (file)
@@ -115,6 +115,7 @@ obj-$(CONFIG_SENSORS_MAX6639)       += max6639.o
 obj-$(CONFIG_SENSORS_MAX6642)  += max6642.o
 obj-$(CONFIG_SENSORS_MAX6650)  += max6650.o
 obj-$(CONFIG_SENSORS_MAX6697)  += max6697.o
+obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
 obj-$(CONFIG_SENSORS_MCP3021)  += mcp3021.o
 obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
index 1fd46859ed29ded69dbb479c980316142bde5fd7..d87cae8c635fea8367609c4cd0013017cf41ceb6 100644 (file)
@@ -377,7 +377,7 @@ static int setup_irqs(struct platform_device *pdev)
        }
 
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
-               abx500_temp_irq_handler, IRQF_NO_SUSPEND, "abx500-temp", pdev);
+               abx500_temp_irq_handler, 0, "abx500-temp", pdev);
        if (ret < 0)
                dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
 
index 3e03379e7c5d92c0191af13883c4e440dbc2cc9c..6a27eb2fed1714a01b6b41f9ab6a1411b7c6a274 100644 (file)
@@ -52,7 +52,7 @@ module_param_named(tjmax, force_tjmax, int, 0444);
 MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
 
 #define BASE_SYSFS_ATTR_NO     2       /* Sysfs Base attr no for coretemp */
-#define NUM_REAL_CORES         32      /* Number of Real cores per cpu */
+#define NUM_REAL_CORES         128     /* Number of Real cores per cpu */
 #define CORETEMP_NAME_LENGTH   19      /* String Length of attrs */
 #define MAX_CORE_ATTRS         4       /* Maximum no of basic attrs */
 #define TOTAL_ATTRS            (MAX_CORE_ATTRS + 1)
index e80ee23b62d317c007d2445d69917b2e65b35559..5f7067d7b625c6578c4b135becd159d50e8b0eee 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/pci.h>
 #include <linux/bitops.h>
 #include <asm/processor.h>
+#include <asm/msr.h>
 
 MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
 MODULE_AUTHOR("Andreas Herrmann <herrmann.der.user@googlemail.com>");
@@ -41,12 +42,21 @@ MODULE_LICENSE("GPL");
 #define REG_TDP_RUNNING_AVERAGE                0xe0
 #define REG_TDP_LIMIT3                 0xe8
 
+#define FAM15H_MIN_NUM_ATTRS           2
+#define FAM15H_NUM_GROUPS              2
+
+#define MSR_F15H_CU_MAX_PWR_ACCUMULATOR        0xc001007b
+
 struct fam15h_power_data {
        struct pci_dev *pdev;
        unsigned int tdp_to_watts;
        unsigned int base_tdp;
        unsigned int processor_pwr_watts;
        unsigned int cpu_pwr_sample_ratio;
+       const struct attribute_group *groups[FAM15H_NUM_GROUPS];
+       struct attribute_group group;
+       /* maximum accumulated power of a compute unit */
+       u64 max_cu_acc_power;
 };
 
 static ssize_t show_power(struct device *dev,
@@ -105,29 +115,36 @@ static ssize_t show_power_crit(struct device *dev,
 }
 static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL);
 
-static umode_t fam15h_power_is_visible(struct kobject *kobj,
-                                      struct attribute *attr,
-                                      int index)
+static int fam15h_power_init_attrs(struct pci_dev *pdev,
+                                  struct fam15h_power_data *data)
 {
-       /* power1_input is only reported for Fam15h, Models 00h-0fh */
-       if (attr == &dev_attr_power1_input.attr &&
-          (boot_cpu_data.x86 != 0x15 || boot_cpu_data.x86_model > 0xf))
-               return 0;
+       int n = FAM15H_MIN_NUM_ATTRS;
+       struct attribute **fam15h_power_attrs;
+       struct cpuinfo_x86 *c = &boot_cpu_data;
 
-       return attr->mode;
-}
+       if (c->x86 == 0x15 &&
+           (c->x86_model <= 0xf ||
+            (c->x86_model >= 0x60 && c->x86_model <= 0x6f)))
+               n += 1;
 
-static struct attribute *fam15h_power_attrs[] = {
-       &dev_attr_power1_input.attr,
-       &dev_attr_power1_crit.attr,
-       NULL
-};
+       fam15h_power_attrs = devm_kcalloc(&pdev->dev, n,
+                                         sizeof(*fam15h_power_attrs),
+                                         GFP_KERNEL);
 
-static const struct attribute_group fam15h_power_group = {
-       .attrs = fam15h_power_attrs,
-       .is_visible = fam15h_power_is_visible,
-};
-__ATTRIBUTE_GROUPS(fam15h_power);
+       if (!fam15h_power_attrs)
+               return -ENOMEM;
+
+       n = 0;
+       fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr;
+       if (c->x86 == 0x15 &&
+           (c->x86_model <= 0xf ||
+            (c->x86_model >= 0x60 && c->x86_model <= 0x6f)))
+               fam15h_power_attrs[n++] = &dev_attr_power1_input.attr;
+
+       data->group.attrs = fam15h_power_attrs;
+
+       return 0;
+}
 
 static bool should_load_on_this_node(struct pci_dev *f4)
 {
@@ -186,11 +203,12 @@ static int fam15h_power_resume(struct pci_dev *pdev)
 #define fam15h_power_resume NULL
 #endif
 
-static void fam15h_power_init_data(struct pci_dev *f4,
-                                            struct fam15h_power_data *data)
+static int fam15h_power_init_data(struct pci_dev *f4,
+                                 struct fam15h_power_data *data)
 {
        u32 val, eax, ebx, ecx, edx;
        u64 tmp;
+       int ret;
 
        pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
        data->base_tdp = val >> 16;
@@ -211,11 +229,15 @@ static void fam15h_power_init_data(struct pci_dev *f4,
        /* convert to microWatt */
        data->processor_pwr_watts = (tmp * 15625) >> 10;
 
+       ret = fam15h_power_init_attrs(f4, data);
+       if (ret)
+               return ret;
+
        cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
 
        /* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */
        if (!(edx & BIT(12)))
-               return;
+               return 0;
 
        /*
         * determine the ratio of the compute unit power accumulator
@@ -223,14 +245,24 @@ static void fam15h_power_init_data(struct pci_dev *f4,
         * Fn8000_0007:ECX
         */
        data->cpu_pwr_sample_ratio = ecx;
+
+       if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &tmp)) {
+               pr_err("Failed to read max compute unit power accumulator MSR\n");
+               return -ENODEV;
+       }
+
+       data->max_cu_acc_power = tmp;
+
+       return 0;
 }
 
 static int fam15h_power_probe(struct pci_dev *pdev,
-                                       const struct pci_device_id *id)
+                             const struct pci_device_id *id)
 {
        struct fam15h_power_data *data;
        struct device *dev = &pdev->dev;
        struct device *hwmon_dev;
+       int ret;
 
        /*
         * though we ignore every other northbridge, we still have to
@@ -246,12 +278,17 @@ static int fam15h_power_probe(struct pci_dev *pdev,
        if (!data)
                return -ENOMEM;
 
-       fam15h_power_init_data(pdev, data);
+       ret = fam15h_power_init_data(pdev, data);
+       if (ret)
+               return ret;
+
        data->pdev = pdev;
 
+       data->groups[0] = &data->group;
+
        hwmon_dev = devm_hwmon_device_register_with_groups(dev, "fam15h_power",
                                                           data,
-                                                          fam15h_power_groups);
+                                                          &data->groups[0]);
        return PTR_ERR_OR_ZERO(hwmon_dev);
 }
 
index 4255514b2c72d917c2e2151f3d1fac405033e935..55b5a8ff1cfe2253c967e8fab4df33a44280a0fc 100644 (file)
@@ -474,11 +474,18 @@ static const struct platform_device_id opal_sensor_driver_ids[] = {
 };
 MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids);
 
+static const struct of_device_id opal_sensor_match[] = {
+       { .compatible   = "ibm,opal-sensor" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, opal_sensor_match);
+
 static struct platform_driver ibmpowernv_driver = {
        .probe          = ibmpowernv_probe,
        .id_table       = opal_sensor_driver_ids,
        .driver         = {
                .name   = DRVNAME,
+               .of_match_table = opal_sensor_match,
        },
 };
 
index 4d2815079fc209ff425267d333fa86b9c82ab97d..b24f1d3045f04386f7dc3a1c5af4b7ee7724e891 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/of.h>
 #include <linux/delay.h>
 #include <linux/util_macros.h>
+#include <linux/regmap.h>
 
 #include <linux/platform_data/ina2xx.h>
 
  */
 #define INA226_TOTAL_CONV_TIME_DEFAULT 2200
 
+static struct regmap_config ina2xx_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 16,
+};
+
 enum ina2xx_ids { ina219, ina226 };
 
 struct ina2xx_config {
@@ -97,20 +103,13 @@ struct ina2xx_config {
 };
 
 struct ina2xx_data {
-       struct i2c_client *client;
        const struct ina2xx_config *config;
 
        long rshunt;
-       u16 curr_config;
-
-       struct mutex update_lock;
-       bool valid;
-       unsigned long last_updated;
-       int update_interval; /* in jiffies */
+       struct mutex config_lock;
+       struct regmap *regmap;
 
-       int kind;
        const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS];
-       u16 regs[INA2XX_MAX_REGISTERS];
 };
 
 static const struct ina2xx_config ina2xx_config[] = {
@@ -153,7 +152,11 @@ static int ina226_reg_to_interval(u16 config)
        return DIV_ROUND_CLOSEST(avg * INA226_TOTAL_CONV_TIME_DEFAULT, 1000);
 }
 
-static u16 ina226_interval_to_reg(int interval, u16 config)
+/*
+ * Return the new, shifted AVG field value of CONFIG register,
+ * to use with regmap_update_bits
+ */
+static u16 ina226_interval_to_reg(int interval)
 {
        int avg, avg_bits;
 
@@ -162,15 +165,7 @@ static u16 ina226_interval_to_reg(int interval, u16 config)
        avg_bits = find_closest(avg, ina226_avg_tab,
                                ARRAY_SIZE(ina226_avg_tab));
 
-       return (config & ~INA226_AVG_RD_MASK) | INA226_SHIFT_AVG(avg_bits);
-}
-
-static void ina226_set_update_interval(struct ina2xx_data *data)
-{
-       int ms;
-
-       ms = ina226_reg_to_interval(data->curr_config);
-       data->update_interval = msecs_to_jiffies(ms);
+       return INA226_SHIFT_AVG(avg_bits);
 }
 
 static int ina2xx_calibrate(struct ina2xx_data *data)
@@ -178,8 +173,7 @@ static int ina2xx_calibrate(struct ina2xx_data *data)
        u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
                                    data->rshunt);
 
-       return i2c_smbus_write_word_swapped(data->client,
-                                           INA2XX_CALIBRATION, val);
+       return regmap_write(data->regmap, INA2XX_CALIBRATION, val);
 }
 
 /*
@@ -187,12 +181,8 @@ static int ina2xx_calibrate(struct ina2xx_data *data)
  */
 static int ina2xx_init(struct ina2xx_data *data)
 {
-       struct i2c_client *client = data->client;
-       int ret;
-
-       /* device configuration */
-       ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
-                                          data->curr_config);
+       int ret = regmap_write(data->regmap, INA2XX_CONFIG,
+                              data->config->config_default);
        if (ret < 0)
                return ret;
 
@@ -203,47 +193,52 @@ static int ina2xx_init(struct ina2xx_data *data)
        return ina2xx_calibrate(data);
 }
 
-static int ina2xx_do_update(struct device *dev)
+static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval)
 {
        struct ina2xx_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = data->client;
-       int i, rv, retry;
+       int ret, retry;
 
-       dev_dbg(&client->dev, "Starting ina2xx update\n");
+       dev_dbg(dev, "Starting register %d read\n", reg);
 
        for (retry = 5; retry; retry--) {
-               /* Read all registers */
-               for (i = 0; i < data->config->registers; i++) {
-                       rv = i2c_smbus_read_word_swapped(client, i);
-                       if (rv < 0)
-                               return rv;
-                       data->regs[i] = rv;
-               }
+
+               ret = regmap_read(data->regmap, reg, regval);
+               if (ret < 0)
+                       return ret;
+
+               dev_dbg(dev, "read %d, val = 0x%04x\n", reg, *regval);
 
                /*
                 * If the current value in the calibration register is 0, the
                 * power and current registers will also remain at 0. In case
                 * the chip has been reset let's check the calibration
                 * register and reinitialize if needed.
+                * We do that extra read of the calibration register if there
+                * is some hint of a chip reset.
                 */
-               if (data->regs[INA2XX_CALIBRATION] == 0) {
-                       dev_warn(dev, "chip not calibrated, reinitializing\n");
-
-                       rv = ina2xx_init(data);
-                       if (rv < 0)
-                               return rv;
-
-                       /*
-                        * Let's make sure the power and current registers
-                        * have been updated before trying again.
-                        */
-                       msleep(INA2XX_MAX_DELAY);
-                       continue;
+               if (*regval == 0) {
+                       unsigned int cal;
+
+                       ret = regmap_read(data->regmap, INA2XX_CALIBRATION,
+                                         &cal);
+                       if (ret < 0)
+                               return ret;
+
+                       if (cal == 0) {
+                               dev_warn(dev, "chip not calibrated, reinitializing\n");
+
+                               ret = ina2xx_init(data);
+                               if (ret < 0)
+                                       return ret;
+                               /*
+                                * Let's make sure the power and current
+                                * registers have been updated before trying
+                                * again.
+                                */
+                               msleep(INA2XX_MAX_DELAY);
+                               continue;
+                       }
                }
-
-               data->last_updated = jiffies;
-               data->valid = 1;
-
                return 0;
        }
 
@@ -256,51 +251,31 @@ static int ina2xx_do_update(struct device *dev)
        return -ENODEV;
 }
 
-static struct ina2xx_data *ina2xx_update_device(struct device *dev)
-{
-       struct ina2xx_data *data = dev_get_drvdata(dev);
-       struct ina2xx_data *ret = data;
-       unsigned long after;
-       int rv;
-
-       mutex_lock(&data->update_lock);
-
-       after = data->last_updated + data->update_interval;
-       if (time_after(jiffies, after) || !data->valid) {
-               rv = ina2xx_do_update(dev);
-               if (rv < 0)
-                       ret = ERR_PTR(rv);
-       }
-
-       mutex_unlock(&data->update_lock);
-       return ret;
-}
-
-static int ina2xx_get_value(struct ina2xx_data *data, u8 reg)
+static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
+                           unsigned int regval)
 {
        int val;
 
        switch (reg) {
        case INA2XX_SHUNT_VOLTAGE:
                /* signed register */
-               val = DIV_ROUND_CLOSEST((s16)data->regs[reg],
-                                       data->config->shunt_div);
+               val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div);
                break;
        case INA2XX_BUS_VOLTAGE:
-               val = (data->regs[reg] >> data->config->bus_voltage_shift)
+               val = (regval >> data->config->bus_voltage_shift)
                  * data->config->bus_voltage_lsb;
                val = DIV_ROUND_CLOSEST(val, 1000);
                break;
        case INA2XX_POWER:
-               val = data->regs[reg] * data->config->power_lsb;
+               val = regval * data->config->power_lsb;
                break;
        case INA2XX_CURRENT:
                /* signed register, LSB=1mA (selected), in mA */
-               val = (s16)data->regs[reg];
+               val = (s16)regval;
                break;
        case INA2XX_CALIBRATION:
                val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
-                                       data->regs[reg]);
+                                       regval);
                break;
        default:
                /* programmer goofed */
@@ -316,25 +291,25 @@ static ssize_t ina2xx_show_value(struct device *dev,
                                 struct device_attribute *da, char *buf)
 {
        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-       struct ina2xx_data *data = ina2xx_update_device(dev);
+       struct ina2xx_data *data = dev_get_drvdata(dev);
+       unsigned int regval;
+
+       int err = ina2xx_read_reg(dev, attr->index, &regval);
 
-       if (IS_ERR(data))
-               return PTR_ERR(data);
+       if (err < 0)
+               return err;
 
        return snprintf(buf, PAGE_SIZE, "%d\n",
-                       ina2xx_get_value(data, attr->index));
+                       ina2xx_get_value(data, attr->index, regval));
 }
 
 static ssize_t ina2xx_set_shunt(struct device *dev,
                                struct device_attribute *da,
                                const char *buf, size_t count)
 {
-       struct ina2xx_data *data = ina2xx_update_device(dev);
        unsigned long val;
        int status;
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
+       struct ina2xx_data *data = dev_get_drvdata(dev);
 
        status = kstrtoul(buf, 10, &val);
        if (status < 0)
@@ -345,10 +320,10 @@ static ssize_t ina2xx_set_shunt(struct device *dev,
            val > data->config->calibration_factor)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
+       mutex_lock(&data->config_lock);
        data->rshunt = val;
        status = ina2xx_calibrate(data);
-       mutex_unlock(&data->update_lock);
+       mutex_unlock(&data->config_lock);
        if (status < 0)
                return status;
 
@@ -370,17 +345,9 @@ static ssize_t ina226_set_interval(struct device *dev,
        if (val > INT_MAX || val == 0)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
-       data->curr_config = ina226_interval_to_reg(val,
-                                                  data->regs[INA2XX_CONFIG]);
-       status = i2c_smbus_write_word_swapped(data->client,
-                                             INA2XX_CONFIG,
-                                             data->curr_config);
-
-       ina226_set_update_interval(data);
-       /* Make sure the next access re-reads all registers. */
-       data->valid = 0;
-       mutex_unlock(&data->update_lock);
+       status = regmap_update_bits(data->regmap, INA2XX_CONFIG,
+                                   INA226_AVG_RD_MASK,
+                                   ina226_interval_to_reg(val));
        if (status < 0)
                return status;
 
@@ -390,18 +357,15 @@ static ssize_t ina226_set_interval(struct device *dev,
 static ssize_t ina226_show_interval(struct device *dev,
                                    struct device_attribute *da, char *buf)
 {
-       struct ina2xx_data *data = ina2xx_update_device(dev);
+       struct ina2xx_data *data = dev_get_drvdata(dev);
+       int status;
+       unsigned int regval;
 
-       if (IS_ERR(data))
-               return PTR_ERR(data);
+       status = regmap_read(data->regmap, INA2XX_CONFIG, &regval);
+       if (status)
+               return status;
 
-       /*
-        * We don't use data->update_interval here as we want to display
-        * the actual interval used by the chip and jiffies_to_msecs()
-        * doesn't seem to be accurate enough.
-        */
-       return snprintf(buf, PAGE_SIZE, "%d\n",
-                       ina226_reg_to_interval(data->regs[INA2XX_CONFIG]));
+       return snprintf(buf, PAGE_SIZE, "%d\n", ina226_reg_to_interval(regval));
 }
 
 /* shunt voltage */
@@ -455,60 +419,51 @@ static const struct attribute_group ina226_group = {
 static int ina2xx_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
-       struct i2c_adapter *adapter = client->adapter;
-       struct ina2xx_platform_data *pdata;
        struct device *dev = &client->dev;
        struct ina2xx_data *data;
        struct device *hwmon_dev;
        u32 val;
        int ret, group = 0;
 
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
-               return -ENODEV;
-
        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
-       if (dev_get_platdata(dev)) {
-               pdata = dev_get_platdata(dev);
-               data->rshunt = pdata->shunt_uohms;
-       } else if (!of_property_read_u32(dev->of_node,
-                                        "shunt-resistor", &val)) {
-               data->rshunt = val;
-       } else {
-               data->rshunt = INA2XX_RSHUNT_DEFAULT;
-       }
-
        /* set the device type */
-       data->kind = id->driver_data;
-       data->config = &ina2xx_config[data->kind];
-       data->curr_config = data->config->config_default;
-       data->client = client;
+       data->config = &ina2xx_config[id->driver_data];
 
-       /*
-        * Ina226 has a variable update_interval. For ina219 we
-        * use a constant value.
-        */
-       if (data->kind == ina226)
-               ina226_set_update_interval(data);
-       else
-               data->update_interval = HZ / INA2XX_CONVERSION_RATE;
+       if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) {
+               struct ina2xx_platform_data *pdata = dev_get_platdata(dev);
+
+               if (pdata)
+                       val = pdata->shunt_uohms;
+               else
+                       val = INA2XX_RSHUNT_DEFAULT;
+       }
 
-       if (data->rshunt <= 0 ||
-           data->rshunt > data->config->calibration_factor)
+       if (val <= 0 || val > data->config->calibration_factor)
                return -ENODEV;
 
+       data->rshunt = val;
+
+       ina2xx_regmap_config.max_register = data->config->registers;
+
+       data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config);
+       if (IS_ERR(data->regmap)) {
+               dev_err(dev, "failed to allocate register map\n");
+               return PTR_ERR(data->regmap);
+       }
+
        ret = ina2xx_init(data);
        if (ret < 0) {
                dev_err(dev, "error configuring the device: %d\n", ret);
                return -ENODEV;
        }
 
-       mutex_init(&data->update_lock);
+       mutex_init(&data->config_lock);
 
        data->groups[group++] = &ina2xx_group;
-       if (data->kind == ina226)
+       if (id->driver_data == ina226)
                data->groups[group++] = &ina226_group;
 
        hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
index e4e57bbafb10eeb621ad03ff03e8985030c43e4a..0addc84ba948a09e6b740db56593fbd124b8c8d5 100644 (file)
@@ -57,6 +57,7 @@ enum lm75_type {              /* keep sorted in alphabetical order */
        tmp175,
        tmp275,
        tmp75,
+       tmp75c,
 };
 
 /* Addresses scanned */
@@ -280,6 +281,11 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
                data->resolution = 12;
                data->sample_time = HZ / 2;
                break;
+       case tmp75c:
+               clr_mask |= 1 << 5;             /* not one-shot mode */
+               data->resolution = 12;
+               data->sample_time = HZ / 4;
+               break;
        }
 
        /* configure as specified */
@@ -343,6 +349,7 @@ static const struct i2c_device_id lm75_ids[] = {
        { "tmp175", tmp175, },
        { "tmp275", tmp275, },
        { "tmp75", tmp75, },
+       { "tmp75c", tmp75c, },
        { /* LIST END */ }
 };
 MODULE_DEVICE_TABLE(i2c, lm75_ids);
diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c
new file mode 100644 (file)
index 0000000..69c0ac8
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+ * max31790.c - Part of lm_sensors, Linux kernel modules for hardware
+ *             monitoring.
+ *
+ * (C) 2015 by Il Han <corone.il.han@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
+ * 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/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+/* MAX31790 registers */
+#define MAX31790_REG_GLOBAL_CONFIG     0x00
+#define MAX31790_REG_FAN_CONFIG(ch)    (0x02 + (ch))
+#define MAX31790_REG_FAN_DYNAMICS(ch)  (0x08 + (ch))
+#define MAX31790_REG_FAN_FAULT_STATUS2 0x10
+#define MAX31790_REG_FAN_FAULT_STATUS1 0x11
+#define MAX31790_REG_TACH_COUNT(ch)    (0x18 + (ch) * 2)
+#define MAX31790_REG_PWM_DUTY_CYCLE(ch)        (0x30 + (ch) * 2)
+#define MAX31790_REG_PWMOUT(ch)                (0x40 + (ch) * 2)
+#define MAX31790_REG_TARGET_COUNT(ch)  (0x50 + (ch) * 2)
+
+/* Fan Config register bits */
+#define MAX31790_FAN_CFG_RPM_MODE      0x80
+#define MAX31790_FAN_CFG_TACH_INPUT_EN 0x08
+#define MAX31790_FAN_CFG_TACH_INPUT    0x01
+
+/* Fan Dynamics register bits */
+#define MAX31790_FAN_DYN_SR_SHIFT      5
+#define MAX31790_FAN_DYN_SR_MASK       0xE0
+#define SR_FROM_REG(reg)               (((reg) & MAX31790_FAN_DYN_SR_MASK) \
+                                        >> MAX31790_FAN_DYN_SR_SHIFT)
+
+#define FAN_RPM_MIN                    120
+#define FAN_RPM_MAX                    7864320
+
+#define RPM_FROM_REG(reg, sr)          (((reg) >> 4) ? \
+                                        ((60 * (sr) * 8192) / ((reg) >> 4)) : \
+                                        FAN_RPM_MAX)
+#define RPM_TO_REG(rpm, sr)            ((60 * (sr) * 8192) / ((rpm) * 2))
+
+#define NR_CHANNEL                     6
+
+/*
+ * Client data (each client gets its own)
+ */
+struct max31790_data {
+       struct i2c_client *client;
+       struct mutex update_lock;
+       bool valid; /* zero until following fields are valid */
+       unsigned long last_updated; /* in jiffies */
+
+       /* register values */
+       u8 fan_config[NR_CHANNEL];
+       u8 fan_dynamics[NR_CHANNEL];
+       u16 fault_status;
+       u16 tach[NR_CHANNEL * 2];
+       u16 pwm[NR_CHANNEL];
+       u16 target_count[NR_CHANNEL];
+};
+
+static struct max31790_data *max31790_update_device(struct device *dev)
+{
+       struct max31790_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
+       struct max31790_data *ret = data;
+       int i;
+       int rv;
+
+       mutex_lock(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+               rv = i2c_smbus_read_byte_data(client,
+                               MAX31790_REG_FAN_FAULT_STATUS1);
+               if (rv < 0)
+                       goto abort;
+               data->fault_status = rv & 0x3F;
+
+               rv = i2c_smbus_read_byte_data(client,
+                               MAX31790_REG_FAN_FAULT_STATUS2);
+               if (rv < 0)
+                       goto abort;
+               data->fault_status |= (rv & 0x3F) << 6;
+
+               for (i = 0; i < NR_CHANNEL; i++) {
+                       rv = i2c_smbus_read_word_swapped(client,
+                                       MAX31790_REG_TACH_COUNT(i));
+                       if (rv < 0)
+                               goto abort;
+                       data->tach[i] = rv;
+
+                       if (data->fan_config[i]
+                           & MAX31790_FAN_CFG_TACH_INPUT) {
+                               rv = i2c_smbus_read_word_swapped(client,
+                                       MAX31790_REG_TACH_COUNT(NR_CHANNEL
+                                                               + i));
+                               if (rv < 0)
+                                       goto abort;
+                               data->tach[NR_CHANNEL + i] = rv;
+                       } else {
+                               rv = i2c_smbus_read_word_swapped(client,
+                                               MAX31790_REG_PWMOUT(i));
+                               if (rv < 0)
+                                       goto abort;
+                               data->pwm[i] = rv;
+
+                               rv = i2c_smbus_read_word_swapped(client,
+                                               MAX31790_REG_TARGET_COUNT(i));
+                               if (rv < 0)
+                                       goto abort;
+                               data->target_count[i] = rv;
+                       }
+               }
+
+               data->last_updated = jiffies;
+               data->valid = true;
+       }
+       goto done;
+
+abort:
+       data->valid = false;
+       ret = ERR_PTR(rv);
+
+done:
+       mutex_unlock(&data->update_lock);
+
+       return ret;
+}
+
+static const u8 tach_period[8] = { 1, 2, 4, 8, 16, 32, 32, 32 };
+
+static u8 get_tach_period(u8 fan_dynamics)
+{
+       return tach_period[SR_FROM_REG(fan_dynamics)];
+}
+
+static u8 bits_for_tach_period(int rpm)
+{
+       u8 bits;
+
+       if (rpm < 500)
+               bits = 0x0;
+       else if (rpm < 1000)
+               bits = 0x1;
+       else if (rpm < 2000)
+               bits = 0x2;
+       else if (rpm < 4000)
+               bits = 0x3;
+       else if (rpm < 8000)
+               bits = 0x4;
+       else
+               bits = 0x5;
+
+       return bits;
+}
+
+static ssize_t get_fan(struct device *dev,
+                      struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = max31790_update_device(dev);
+       int sr, rpm;
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       sr = get_tach_period(data->fan_dynamics[attr->index]);
+       rpm = RPM_FROM_REG(data->tach[attr->index], sr);
+
+       return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t get_fan_target(struct device *dev,
+                             struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = max31790_update_device(dev);
+       int sr, rpm;
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       sr = get_tach_period(data->fan_dynamics[attr->index]);
+       rpm = RPM_FROM_REG(data->target_count[attr->index], sr);
+
+       return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t set_fan_target(struct device *dev,
+                             struct device_attribute *devattr,
+                             const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
+       u8 bits;
+       int sr;
+       int target_count;
+       unsigned long rpm;
+       int err;
+
+       err = kstrtoul(buf, 10, &rpm);
+       if (err)
+               return err;
+
+       mutex_lock(&data->update_lock);
+
+       rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
+       bits = bits_for_tach_period(rpm);
+       data->fan_dynamics[attr->index] =
+                       ((data->fan_dynamics[attr->index]
+                         & ~MAX31790_FAN_DYN_SR_MASK)
+                        | (bits << MAX31790_FAN_DYN_SR_SHIFT));
+       err = i2c_smbus_write_byte_data(client,
+                       MAX31790_REG_FAN_DYNAMICS(attr->index),
+                       data->fan_dynamics[attr->index]);
+
+       if (err < 0) {
+               mutex_unlock(&data->update_lock);
+               return err;
+       }
+
+       sr = get_tach_period(data->fan_dynamics[attr->index]);
+       target_count = RPM_TO_REG(rpm, sr);
+       target_count = clamp_val(target_count, 0x1, 0x7FF);
+
+       data->target_count[attr->index] = target_count << 5;
+
+       err = i2c_smbus_write_word_swapped(client,
+                       MAX31790_REG_TARGET_COUNT(attr->index),
+                       data->target_count[attr->index]);
+
+       mutex_unlock(&data->update_lock);
+
+       if (err < 0)
+               return err;
+
+       return count;
+}
+
+static ssize_t get_pwm(struct device *dev,
+                      struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = max31790_update_device(dev);
+       int pwm;
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       pwm = data->pwm[attr->index] >> 8;
+
+       return sprintf(buf, "%d\n", pwm);
+}
+
+static ssize_t set_pwm(struct device *dev,
+                      struct device_attribute *devattr,
+                      const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
+       unsigned long pwm;
+       int err;
+
+       err = kstrtoul(buf, 10, &pwm);
+       if (err)
+               return err;
+
+       if (pwm > 255)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+
+       data->pwm[attr->index] = pwm << 8;
+       err = i2c_smbus_write_word_swapped(client,
+                       MAX31790_REG_PWMOUT(attr->index),
+                       data->pwm[attr->index]);
+
+       mutex_unlock(&data->update_lock);
+
+       if (err < 0)
+               return err;
+
+       return count;
+}
+
+static ssize_t get_pwm_enable(struct device *dev,
+                             struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = max31790_update_device(dev);
+       int mode;
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       if (data->fan_config[attr->index] & MAX31790_FAN_CFG_RPM_MODE)
+               mode = 2;
+       else if (data->fan_config[attr->index] & MAX31790_FAN_CFG_TACH_INPUT_EN)
+               mode = 1;
+       else
+               mode = 0;
+
+       return sprintf(buf, "%d\n", mode);
+}
+
+static ssize_t set_pwm_enable(struct device *dev,
+                             struct device_attribute *devattr,
+                             const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
+       unsigned long mode;
+       int err;
+
+       err = kstrtoul(buf, 10, &mode);
+       if (err)
+               return err;
+
+       switch (mode) {
+       case 0:
+               data->fan_config[attr->index] =
+                       data->fan_config[attr->index]
+                       & ~(MAX31790_FAN_CFG_TACH_INPUT_EN
+                           | MAX31790_FAN_CFG_RPM_MODE);
+               break;
+       case 1:
+               data->fan_config[attr->index] =
+                       (data->fan_config[attr->index]
+                        | MAX31790_FAN_CFG_TACH_INPUT_EN)
+                       & ~MAX31790_FAN_CFG_RPM_MODE;
+               break;
+       case 2:
+               data->fan_config[attr->index] =
+                       data->fan_config[attr->index]
+                       | MAX31790_FAN_CFG_TACH_INPUT_EN
+                       | MAX31790_FAN_CFG_RPM_MODE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       mutex_lock(&data->update_lock);
+
+       err = i2c_smbus_write_byte_data(client,
+                       MAX31790_REG_FAN_CONFIG(attr->index),
+                       data->fan_config[attr->index]);
+
+       mutex_unlock(&data->update_lock);
+
+       if (err < 0)
+               return err;
+
+       return count;
+}
+
+static ssize_t get_fan_fault(struct device *dev,
+                            struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = max31790_update_device(dev);
+       int fault;
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       fault = !!(data->fault_status & (1 << attr->index));
+
+       return sprintf(buf, "%d\n", fault);
+}
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, get_fan, NULL, 4);
+static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, get_fan, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_fault, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, get_fan_fault, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, get_fan_fault, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, get_fan_fault, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, get_fan_fault, NULL, 4);
+static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, get_fan_fault, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, get_fan, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, get_fan, NULL, 7);
+static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, get_fan, NULL, 8);
+static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, get_fan, NULL, 9);
+static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, get_fan, NULL, 10);
+static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, get_fan, NULL, 11);
+
+static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, get_fan_fault, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, get_fan_fault, NULL, 7);
+static SENSOR_DEVICE_ATTR(fan9_fault, S_IRUGO, get_fan_fault, NULL, 8);
+static SENSOR_DEVICE_ATTR(fan10_fault, S_IRUGO, get_fan_fault, NULL, 9);
+static SENSOR_DEVICE_ATTR(fan11_fault, S_IRUGO, get_fan_fault, NULL, 10);
+static SENSOR_DEVICE_ATTR(fan12_fault, S_IRUGO, get_fan_fault, NULL, 11);
+
+static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 0);
+static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 1);
+static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 2);
+static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 3);
+static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 4);
+static SENSOR_DEVICE_ATTR(fan6_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 5);
+
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3);
+static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 4);
+static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 5);
+
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 0);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 1);
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 2);
+static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 3);
+static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 4);
+static SENSOR_DEVICE_ATTR(pwm6_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 5);
+
+static struct attribute *max31790_attrs[] = {
+       &sensor_dev_attr_fan1_input.dev_attr.attr,
+       &sensor_dev_attr_fan2_input.dev_attr.attr,
+       &sensor_dev_attr_fan3_input.dev_attr.attr,
+       &sensor_dev_attr_fan4_input.dev_attr.attr,
+       &sensor_dev_attr_fan5_input.dev_attr.attr,
+       &sensor_dev_attr_fan6_input.dev_attr.attr,
+
+       &sensor_dev_attr_fan1_fault.dev_attr.attr,
+       &sensor_dev_attr_fan2_fault.dev_attr.attr,
+       &sensor_dev_attr_fan3_fault.dev_attr.attr,
+       &sensor_dev_attr_fan4_fault.dev_attr.attr,
+       &sensor_dev_attr_fan5_fault.dev_attr.attr,
+       &sensor_dev_attr_fan6_fault.dev_attr.attr,
+
+       &sensor_dev_attr_fan7_input.dev_attr.attr,
+       &sensor_dev_attr_fan8_input.dev_attr.attr,
+       &sensor_dev_attr_fan9_input.dev_attr.attr,
+       &sensor_dev_attr_fan10_input.dev_attr.attr,
+       &sensor_dev_attr_fan11_input.dev_attr.attr,
+       &sensor_dev_attr_fan12_input.dev_attr.attr,
+
+       &sensor_dev_attr_fan7_fault.dev_attr.attr,
+       &sensor_dev_attr_fan8_fault.dev_attr.attr,
+       &sensor_dev_attr_fan9_fault.dev_attr.attr,
+       &sensor_dev_attr_fan10_fault.dev_attr.attr,
+       &sensor_dev_attr_fan11_fault.dev_attr.attr,
+       &sensor_dev_attr_fan12_fault.dev_attr.attr,
+
+       &sensor_dev_attr_fan1_target.dev_attr.attr,
+       &sensor_dev_attr_fan2_target.dev_attr.attr,
+       &sensor_dev_attr_fan3_target.dev_attr.attr,
+       &sensor_dev_attr_fan4_target.dev_attr.attr,
+       &sensor_dev_attr_fan5_target.dev_attr.attr,
+       &sensor_dev_attr_fan6_target.dev_attr.attr,
+
+       &sensor_dev_attr_pwm1.dev_attr.attr,
+       &sensor_dev_attr_pwm2.dev_attr.attr,
+       &sensor_dev_attr_pwm3.dev_attr.attr,
+       &sensor_dev_attr_pwm4.dev_attr.attr,
+       &sensor_dev_attr_pwm5.dev_attr.attr,
+       &sensor_dev_attr_pwm6.dev_attr.attr,
+
+       &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm4_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm5_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm6_enable.dev_attr.attr,
+       NULL
+};
+
+static umode_t max31790_attrs_visible(struct kobject *kobj,
+                                    struct attribute *a, int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct max31790_data *data = dev_get_drvdata(dev);
+       struct device_attribute *devattr =
+                       container_of(a, struct device_attribute, attr);
+       int index = to_sensor_dev_attr(devattr)->index % NR_CHANNEL;
+       u8 fan_config;
+
+       fan_config = data->fan_config[index];
+
+       if (n >= NR_CHANNEL * 2 && n < NR_CHANNEL * 4 &&
+           !(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+               return 0;
+       if (n >= NR_CHANNEL * 4 && (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+               return 0;
+
+       return a->mode;
+}
+
+static const struct attribute_group max31790_group = {
+       .attrs = max31790_attrs,
+       .is_visible = max31790_attrs_visible,
+};
+__ATTRIBUTE_GROUPS(max31790);
+
+static int max31790_init_client(struct i2c_client *client,
+                               struct max31790_data *data)
+{
+       int i, rv;
+
+       for (i = 0; i < NR_CHANNEL; i++) {
+               rv = i2c_smbus_read_byte_data(client,
+                               MAX31790_REG_FAN_CONFIG(i));
+               if (rv < 0)
+                       return rv;
+               data->fan_config[i] = rv;
+
+               rv = i2c_smbus_read_byte_data(client,
+                               MAX31790_REG_FAN_DYNAMICS(i));
+               if (rv < 0)
+                       return rv;
+               data->fan_dynamics[i] = rv;
+       }
+
+       return 0;
+}
+
+static int max31790_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = client->adapter;
+       struct device *dev = &client->dev;
+       struct max31790_data *data;
+       struct device *hwmon_dev;
+       int err;
+
+       if (!i2c_check_functionality(adapter,
+                       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
+               return -ENODEV;
+
+       data = devm_kzalloc(dev, sizeof(struct max31790_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->client = client;
+       mutex_init(&data->update_lock);
+
+       /*
+        * Initialize the max31790 chip
+        */
+       err = max31790_init_client(client, data);
+       if (err)
+               return err;
+
+       hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+                       client->name, data, max31790_groups);
+
+       return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id max31790_id[] = {
+       { "max31790", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max31790_id);
+
+static struct i2c_driver max31790_driver = {
+       .class          = I2C_CLASS_HWMON,
+       .probe          = max31790_probe,
+       .driver = {
+               .name   = "max31790",
+       },
+       .id_table       = max31790_id,
+};
+
+module_i2c_driver(max31790_driver);
+
+MODULE_AUTHOR("Il Han <corone.il.han@gmail.com>");
+MODULE_DESCRIPTION("MAX31790 sensor driver");
+MODULE_LICENSE("GPL");
index 8b4fa55e46c6afceb3895515bc0df0cb3b8a4b85..d7ebdf8651f5f57fe598197b1e0620754d1dcc55 100644 (file)
@@ -515,16 +515,24 @@ static const char *const nct6779_temp_label[] = {
        "PCH_DIM1_TEMP",
        "PCH_DIM2_TEMP",
        "PCH_DIM3_TEMP",
-       "BYTE_TEMP"
+       "BYTE_TEMP",
+       "",
+       "",
+       "",
+       "",
+       "Virtual_TEMP"
 };
 
-static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1]
+#define NCT6779_NUM_LABELS     (ARRAY_SIZE(nct6779_temp_label) - 5)
+#define NCT6791_NUM_LABELS     ARRAY_SIZE(nct6779_temp_label)
+
+static const u16 NCT6779_REG_TEMP_ALTERNATE[NCT6791_NUM_LABELS - 1]
        = { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0,
            0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407,
            0x408, 0 };
 
-static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1]
+static const u16 NCT6779_REG_TEMP_CRIT[NCT6791_NUM_LABELS - 1]
        = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
 
 /* NCT6791 specific data */
@@ -557,6 +565,76 @@ static const u16 NCT6792_REG_TEMP_MON[] = {
 static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = {
        0xb2, 0xb3, 0xb4, 0xb5, 0xbf };
 
+static const char *const nct6792_temp_label[] = {
+       "",
+       "SYSTIN",
+       "CPUTIN",
+       "AUXTIN0",
+       "AUXTIN1",
+       "AUXTIN2",
+       "AUXTIN3",
+       "",
+       "SMBUSMASTER 0",
+       "SMBUSMASTER 1",
+       "SMBUSMASTER 2",
+       "SMBUSMASTER 3",
+       "SMBUSMASTER 4",
+       "SMBUSMASTER 5",
+       "SMBUSMASTER 6",
+       "SMBUSMASTER 7",
+       "PECI Agent 0",
+       "PECI Agent 1",
+       "PCH_CHIP_CPU_MAX_TEMP",
+       "PCH_CHIP_TEMP",
+       "PCH_CPU_TEMP",
+       "PCH_MCH_TEMP",
+       "PCH_DIM0_TEMP",
+       "PCH_DIM1_TEMP",
+       "PCH_DIM2_TEMP",
+       "PCH_DIM3_TEMP",
+       "BYTE_TEMP",
+       "PECI Agent 0 Calibration",
+       "PECI Agent 1 Calibration",
+       "",
+       "",
+       "Virtual_TEMP"
+};
+
+static const char *const nct6793_temp_label[] = {
+       "",
+       "SYSTIN",
+       "CPUTIN",
+       "AUXTIN0",
+       "AUXTIN1",
+       "AUXTIN2",
+       "AUXTIN3",
+       "",
+       "SMBUSMASTER 0",
+       "SMBUSMASTER 1",
+       "",
+       "",
+       "",
+       "",
+       "",
+       "",
+       "PECI Agent 0",
+       "PECI Agent 1",
+       "PCH_CHIP_CPU_MAX_TEMP",
+       "PCH_CHIP_TEMP",
+       "PCH_CPU_TEMP",
+       "PCH_MCH_TEMP",
+       "Agent0 Dimm0 ",
+       "Agent0 Dimm1",
+       "Agent1 Dimm0",
+       "Agent1 Dimm1",
+       "BYTE_TEMP0",
+       "BYTE_TEMP1",
+       "PECI Agent 0 Calibration",
+       "PECI Agent 1 Calibration",
+       "",
+       "Virtual_TEMP"
+};
+
 /* NCT6102D/NCT6106D specific data */
 
 #define NCT6106_REG_VBAT       0x318
@@ -3605,7 +3683,7 @@ static int nct6775_probe(struct platform_device *pdev)
                data->speed_tolerance_limit = 63;
 
                data->temp_label = nct6779_temp_label;
-               data->temp_label_num = ARRAY_SIZE(nct6779_temp_label);
+               data->temp_label_num = NCT6779_NUM_LABELS;
 
                data->REG_CONFIG = NCT6775_REG_CONFIG;
                data->REG_VBAT = NCT6775_REG_VBAT;
@@ -3682,8 +3760,19 @@ static int nct6775_probe(struct platform_device *pdev)
                data->tolerance_mask = 0x07;
                data->speed_tolerance_limit = 63;
 
-               data->temp_label = nct6779_temp_label;
-               data->temp_label_num = ARRAY_SIZE(nct6779_temp_label);
+               switch (data->kind) {
+               default:
+               case nct6791:
+                       data->temp_label = nct6779_temp_label;
+                       break;
+               case nct6792:
+                       data->temp_label = nct6792_temp_label;
+                       break;
+               case nct6793:
+                       data->temp_label = nct6793_temp_label;
+                       break;
+               }
+               data->temp_label_num = NCT6791_NUM_LABELS;
 
                data->REG_CONFIG = NCT6775_REG_CONFIG;
                data->REG_VBAT = NCT6775_REG_VBAT;
index 30059c1df2a3b57ea559fc984b559c79e2f06ce6..5801227b97ab089022f90608f6fc425ebd13cd87 100644 (file)
@@ -669,8 +669,6 @@ mv64xxx_i2c_can_offload(struct mv64xxx_i2c_data *drv_data)
        struct i2c_msg *msgs = drv_data->msgs;
        int num = drv_data->num_msgs;
 
-       return false;
-
        if (!drv_data->offload_enabled)
                return false;
 
index e814a36d9b78f26436cd8cf1745dd2aaa3b36db8..6f8b446be5b0e5787deede7f675044332d72f8cb 100644 (file)
@@ -600,7 +600,7 @@ static int i2c_pnx_controller_suspend(struct device *dev)
 {
        struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev);
 
-       clk_disable(alg_data->clk);
+       clk_disable_unprepare(alg_data->clk);
 
        return 0;
 }
@@ -609,7 +609,7 @@ static int i2c_pnx_controller_resume(struct device *dev)
 {
        struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev);
 
-       return clk_enable(alg_data->clk);
+       return clk_prepare_enable(alg_data->clk);
 }
 
 static SIMPLE_DEV_PM_OPS(i2c_pnx_pm,
@@ -672,7 +672,7 @@ static int i2c_pnx_probe(struct platform_device *pdev)
        if (IS_ERR(alg_data->ioaddr))
                return PTR_ERR(alg_data->ioaddr);
 
-       ret = clk_enable(alg_data->clk);
+       ret = clk_prepare_enable(alg_data->clk);
        if (ret)
                return ret;
 
@@ -726,7 +726,7 @@ static int i2c_pnx_probe(struct platform_device *pdev)
        return 0;
 
 out_clock:
-       clk_disable(alg_data->clk);
+       clk_disable_unprepare(alg_data->clk);
        return ret;
 }
 
@@ -735,7 +735,7 @@ static int i2c_pnx_remove(struct platform_device *pdev)
        struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev);
 
        i2c_del_adapter(&alg_data->adapter);
-       clk_disable(alg_data->clk);
+       clk_disable_unprepare(alg_data->clk);
 
        return 0;
 }
index df73cbd9387e89195eca1e30438cc04ae2f33be5..9ad014a7afc797b921d7b687b474cd4faaddfeba 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/ide.h>
+#include <linux/ktime.h>
 
 #include <asm/io.h>
 
@@ -243,13 +244,13 @@ static long read_counter(u32 dma_base)
  */
 static long detect_pll_input_clock(unsigned long dma_base)
 {
-       struct timeval start_time, end_time;
+       ktime_t start_time, end_time;
        long start_count, end_count;
        long pll_input, usec_elapsed;
        u8 scr1;
 
        start_count = read_counter(dma_base);
-       do_gettimeofday(&start_time);
+       start_time = ktime_get();
 
        /* Start the test mode */
        outb(0x01, dma_base + 0x01);
@@ -261,7 +262,7 @@ static long detect_pll_input_clock(unsigned long dma_base)
        mdelay(10);
 
        end_count = read_counter(dma_base);
-       do_gettimeofday(&end_time);
+       end_time = ktime_get();
 
        /* Stop the test mode */
        outb(0x01, dma_base + 0x01);
@@ -273,8 +274,7 @@ static long detect_pll_input_clock(unsigned long dma_base)
         * Calculate the input clock in Hz
         * (the clock counter is 30 bit wide and counts down)
         */
-       usec_elapsed = (end_time.tv_sec - start_time.tv_sec) * 1000000 +
-               (end_time.tv_usec - start_time.tv_usec);
+       usec_elapsed = ktime_us_delta(end_time, start_time);
        pll_input = ((start_count - end_count) & 0x3fffffff) / 10 *
                (10000000 / usec_elapsed);
 
index 70acda91eb2a934e79e999a1a3297d92a2148ae2..6a0bdfa0ce2e76f4741454a740426e0b891015bc 100644 (file)
@@ -1325,9 +1325,6 @@ static void nes_netdev_get_drvinfo(struct net_device *netdev,
                 "%u.%u", nesadapter->firmware_version >> 16,
                 nesadapter->firmware_version & 0x000000ff);
        strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version));
-       drvinfo->testinfo_len = 0;
-       drvinfo->eedump_len = 0;
-       drvinfo->regdump_len = 0;
 }
 
 
index 4d246861d692b810f3074aa7917cda86893ac6c2..41e6cb501e6a1d5b07801463460fe4ebbaace887 100644 (file)
@@ -100,7 +100,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
 #define ALPS_FOUR_BUTTONS      0x40    /* 4 direction button present */
 #define ALPS_PS2_INTERLEAVED   0x80    /* 3-byte PS/2 packet interleaved with
                                           6-byte ALPS packet */
-#define ALPS_DELL              0x100   /* device is a Dell laptop */
+#define ALPS_STICK_BITS                0x100   /* separate stick button bits */
 #define ALPS_BUTTONPAD         0x200   /* device is a clickpad */
 
 static const struct alps_model_info alps_model_data[] = {
@@ -159,6 +159,43 @@ static const struct alps_protocol_info alps_v8_protocol_data = {
        ALPS_PROTO_V8, 0x18, 0x18, 0
 };
 
+/*
+ * Some v2 models report the stick buttons in separate bits
+ */
+static const struct dmi_system_id alps_dmi_has_separate_stick_buttons[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+       {
+               /* Extrapolated from other entries */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D420"),
+               },
+       },
+       {
+               /* Reported-by: Hans de Bruin <jmdebruin@xmsnet.nl> */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D430"),
+               },
+       },
+       {
+               /* Reported-by: Hans de Goede <hdegoede@redhat.com> */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D620"),
+               },
+       },
+       {
+               /* Extrapolated from other entries */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D630"),
+               },
+       },
+#endif
+       { }
+};
+
 static void alps_set_abs_params_st(struct alps_data *priv,
                                   struct input_dev *dev1);
 static void alps_set_abs_params_semi_mt(struct alps_data *priv,
@@ -253,9 +290,8 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
                return;
        }
 
-       /* Dell non interleaved V2 dualpoint has separate stick button bits */
-       if (priv->proto_version == ALPS_PROTO_V2 &&
-           priv->flags == (ALPS_DELL | ALPS_PASS | ALPS_DUALPOINT)) {
+       /* Some models have separate stick button bits */
+       if (priv->flags & ALPS_STICK_BITS) {
                left |= packet[0] & 1;
                right |= packet[0] & 2;
                middle |= packet[0] & 4;
@@ -2552,8 +2588,6 @@ static int alps_set_protocol(struct psmouse *psmouse,
        priv->byte0 = protocol->byte0;
        priv->mask0 = protocol->mask0;
        priv->flags = protocol->flags;
-       if (dmi_name_in_vendors("Dell"))
-               priv->flags |= ALPS_DELL;
 
        priv->x_max = 2000;
        priv->y_max = 1400;
@@ -2568,6 +2602,8 @@ static int alps_set_protocol(struct psmouse *psmouse,
                priv->set_abs_params = alps_set_abs_params_st;
                priv->x_max = 1023;
                priv->y_max = 767;
+               if (dmi_check_system(alps_dmi_has_separate_stick_buttons))
+                       priv->flags |= ALPS_STICK_BITS;
                break;
 
        case ALPS_PROTO_V3:
index 600dcceff5426aaf4f6fc7b20ce966960bf0aa92..deb14c12ae8b19a84c5ba8e0074f4bac43260ecc 100644 (file)
@@ -1006,6 +1006,7 @@ config TOUCHSCREEN_SUN4I
 config TOUCHSCREEN_SUR40
        tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen"
        depends on USB && MEDIA_USB_SUPPORT && HAS_DMA
+       depends on VIDEO_V4L2
        select INPUT_POLLDEV
        select VIDEOBUF2_DMA_SG
        help
index 24d704cd9f882fb8517d036b00d5f64d46787993..7fbb3b0c857150170a293981e1e7940d70478c3f 100644 (file)
@@ -139,14 +139,14 @@ static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc)
                   tsc_readl(tsc, LPC32XX_TSC_CON) &
                             ~LPC32XX_TSC_ADCCON_AUTO_EN);
 
-       clk_disable(tsc->clk);
+       clk_disable_unprepare(tsc->clk);
 }
 
 static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc)
 {
        u32 tmp;
 
-       clk_enable(tsc->clk);
+       clk_prepare_enable(tsc->clk);
 
        tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP;
 
index 08d2775887f7add00e44858cc6bf4a5eece6e260..532e2a211fe1cf9b3d7ff71d36cbcfde49bd29df 100644 (file)
@@ -1974,8 +1974,8 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats)
 static void clear_dte_entry(u16 devid)
 {
        /* remove entry from the device table seen by the hardware */
-       amd_iommu_dev_table[devid].data[0] = IOMMU_PTE_P | IOMMU_PTE_TV;
-       amd_iommu_dev_table[devid].data[1] = 0;
+       amd_iommu_dev_table[devid].data[0]  = IOMMU_PTE_P | IOMMU_PTE_TV;
+       amd_iommu_dev_table[devid].data[1] &= DTE_FLAG_MASK;
 
        amd_iommu_apply_erratum_63(devid);
 }
index f65908841be0184db61f049bc8e2b5a622ca2fbe..c9b64722f62309d76e56ad52c5a3691737ffd6ac 100644 (file)
 #define IOMMU_PTE_IR (1ULL << 61)
 #define IOMMU_PTE_IW (1ULL << 62)
 
+#define DTE_FLAG_MASK  (0x3ffULL << 32)
 #define DTE_FLAG_IOTLB (0x01UL << 32)
 #define DTE_FLAG_GV    (0x01ULL << 55)
 #define DTE_GLX_SHIFT  (56)
index 1131664b918b0a574c7cc654a6a3cd04107f8e81..d21d4edf7236abac49072c086290b76d2ae201b5 100644 (file)
@@ -516,6 +516,13 @@ static void do_fault(struct work_struct *work)
                goto out;
        }
 
+       if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) {
+               /* handle_mm_fault would BUG_ON() */
+               up_read(&mm->mmap_sem);
+               handle_fault_error(fault);
+               goto out;
+       }
+
        ret = handle_mm_fault(mm, vma, address, write);
        if (ret & VM_FAULT_ERROR) {
                /* failed to service fault */
index 27b52c8729cd1e1a6f17e9945a14e894a78ee81f..4d7294e5d98271493e0ef6d2d90d10108178c83f 100644 (file)
@@ -123,6 +123,7 @@ config RENESAS_INTC_IRQPIN
 
 config RENESAS_IRQC
        bool
+       select GENERIC_IRQ_CHIP
        select IRQ_DOMAIN
 
 config ST_IRQCHIP
@@ -187,3 +188,8 @@ config IMX_GPCV2
        select IRQ_DOMAIN
        help
          Enables the wakeup IRQs for IMX platforms with GPCv2 block
+
+config IRQ_MXS
+       def_bool y if MACH_ASM9260 || ARCH_MXS
+       select IRQ_DOMAIN
+       select STMP_DEVICE
index bb3048f00e647d5c8c0665a7fb31cedc593f4e46..177f78f6e6d6313fd9fd16595fe36089296ce25f 100644 (file)
@@ -6,7 +6,7 @@ obj-$(CONFIG_ARCH_EXYNOS)               += exynos-combiner.o
 obj-$(CONFIG_ARCH_HIP04)               += irq-hip04.o
 obj-$(CONFIG_ARCH_MMP)                 += irq-mmp.o
 obj-$(CONFIG_ARCH_MVEBU)               += irq-armada-370-xp.o
-obj-$(CONFIG_ARCH_MXS)                 += irq-mxs.o
+obj-$(CONFIG_IRQ_MXS)                  += irq-mxs.o
 obj-$(CONFIG_ARCH_TEGRA)               += irq-tegra.o
 obj-$(CONFIG_ARCH_S3C24XX)             += irq-s3c24xx.o
 obj-$(CONFIG_DW_APB_ICTL)              += irq-dw-apb-ictl.o
diff --git a/drivers/irqchip/alphascale_asm9260-icoll.h b/drivers/irqchip/alphascale_asm9260-icoll.h
new file mode 100644 (file)
index 0000000..5cec108
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.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 _ALPHASCALE_ASM9260_ICOLL_H
+#define _ALPHASCALE_ASM9260_ICOLL_H
+
+#define ASM9260_NUM_IRQS               64
+/*
+ * this device provide 4 offsets for each register:
+ * 0x0 - plain read write mode
+ * 0x4 - set mode, OR logic.
+ * 0x8 - clr mode, XOR logic.
+ * 0xc - togle mode.
+ */
+
+#define ASM9260_HW_ICOLL_VECTOR                                0x0000
+/*
+ * bits 31:2
+ * This register presents the vector address for the interrupt currently
+ * active on the CPU IRQ input. Writing to this register notifies the
+ * interrupt collector that the interrupt service routine for the current
+ * interrupt has been entered.
+ * The exception trap should have a LDPC instruction from this address:
+ * LDPC ASM9260_HW_ICOLL_VECTOR_ADDR; IRQ exception at 0xffff0018
+ */
+
+/*
+ * The Interrupt Collector Level Acknowledge Register is used by software to
+ * indicate the completion of an interrupt on a specific level.
+ * This register is written at the very end of an interrupt service routine. If
+ * nesting is used then the CPU irq must be turned on before writing to this
+ * register to avoid a race condition in the CPU interrupt hardware.
+ */
+#define ASM9260_HW_ICOLL_LEVELACK                      0x0010
+#define ASM9260_BM_LEVELn(nr)                          BIT(nr)
+
+#define ASM9260_HW_ICOLL_CTRL                          0x0020
+/*
+ * ASM9260_BM_CTRL_SFTRST and ASM9260_BM_CTRL_CLKGATE are not available on
+ * asm9260.
+ */
+#define ASM9260_BM_CTRL_SFTRST                         BIT(31)
+#define ASM9260_BM_CTRL_CLKGATE                                BIT(30)
+/* disable interrupt level nesting */
+#define ASM9260_BM_CTRL_NO_NESTING                     BIT(19)
+/*
+ * Set this bit to one enable the RISC32-style read side effect associated with
+ * the vector address register. In this mode, interrupt in-service is signaled
+ * by the read of the ASM9260_HW_ICOLL_VECTOR register to acquire the interrupt
+ * vector address. Set this bit to zero for normal operation, in which the ISR
+ * signals in-service explicitly by means of a write to the
+ * ASM9260_HW_ICOLL_VECTOR register.
+ * 0 - Must Write to Vector register to go in-service.
+ * 1 - Go in-service as a read side effect
+ */
+#define ASM9260_BM_CTRL_ARM_RSE_MODE                   BIT(18)
+#define ASM9260_BM_CTRL_IRQ_ENABLE                     BIT(16)
+
+#define ASM9260_HW_ICOLL_STAT_OFFSET                   0x0030
+/*
+ * bits 5:0
+ * Vector number of current interrupt. Multiply by 4 and add to vector base
+ * address to obtain the value in ASM9260_HW_ICOLL_VECTOR.
+ */
+
+/*
+ * RAW0 and RAW1 provides a read-only view of the raw interrupt request lines
+ * coming from various parts of the chip. Its purpose is to improve diagnostic
+ * observability.
+ */
+#define ASM9260_HW_ICOLL_RAW0                          0x0040
+#define ASM9260_HW_ICOLL_RAW1                          0x0050
+
+#define ASM9260_HW_ICOLL_INTERRUPT0                    0x0060
+#define ASM9260_HW_ICOLL_INTERRUPTn(n)         (0x0060 + ((n) >> 2) * 0x10)
+/*
+ * WARNING: Modifying the priority of an enabled interrupt may result in
+ * undefined behavior.
+ */
+#define ASM9260_BM_INT_PRIORITY_MASK                   0x3
+#define ASM9260_BM_INT_ENABLE                          BIT(2)
+#define ASM9260_BM_INT_SOFTIRQ                         BIT(3)
+
+#define ASM9260_BM_ICOLL_INTERRUPTn_SHIFT(n)           (((n) & 0x3) << 3)
+#define ASM9260_BM_ICOLL_INTERRUPTn_ENABLE(n)          (1 << (2 + \
+                       ASM9260_BM_ICOLL_INTERRUPTn_SHIFT(n)))
+
+#define ASM9260_HW_ICOLL_VBASE                         0x0160
+/*
+ * bits 31:2
+ * This bitfield holds the upper 30 bits of the base address of the vector
+ * table.
+ */
+
+#define ASM9260_HW_ICOLL_CLEAR0                                0x01d0
+#define ASM9260_HW_ICOLL_CLEAR1                                0x01e0
+#define ASM9260_HW_ICOLL_CLEARn(n)                     (((n >> 5) * 0x10) \
+                                                       + SET_REG)
+#define ASM9260_BM_CLEAR_BIT(n)                                BIT(n & 0x1f)
+
+/* Scratchpad */
+#define ASM9260_HW_ICOLL_UNDEF_VECTOR                  0x01f0
+#endif
index cd7d3bc78e345c49b733b9120c5a5bed2540583b..ead15be2d20ab12c476157fd75be87d0064c8c41 100644 (file)
@@ -144,7 +144,7 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
                                     unsigned long *out_hwirq,
                                     unsigned int *out_type)
 {
-       if (d->of_node != controller)
+       if (irq_domain_get_of_node(d) != controller)
                return -EINVAL;
 
        if (intsize < 2)
index 655cb967a1f2fe0b9c41f5d3484dbebe6be7331d..3f3a8c3d2175ed6a49727bc86d8afde6e7cd9520 100644 (file)
@@ -56,9 +56,6 @@
 
 #define ARMADA_370_XP_MAX_PER_CPU_IRQS         (28)
 
-#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ       (5)
-#define ARMADA_370_XP_FABRIC_IRQ               (3)
-
 #define IPI_DOORBELL_START                      (0)
 #define IPI_DOORBELL_END                        (8)
 #define IPI_DOORBELL_MASK                       0xFF
@@ -81,13 +78,10 @@ static phys_addr_t msi_doorbell_addr;
 
 static inline bool is_percpu_irq(irq_hw_number_t irq)
 {
-       switch (irq) {
-       case ARMADA_370_XP_TIMER0_PER_CPU_IRQ:
-       case ARMADA_370_XP_FABRIC_IRQ:
+       if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS)
                return true;
-       default:
-               return false;
-       }
+
+       return false;
 }
 
 /*
@@ -317,6 +311,7 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
                                        handle_level_irq);
        }
        irq_set_probe(virq);
+       irq_clear_status_flags(virq, IRQ_NOAUTOEN);
 
        return 0;
 }
@@ -549,7 +544,7 @@ static void armada_370_xp_mpic_resume(void)
                if (virq == 0)
                        continue;
 
-               if (irq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
+               if (!is_percpu_irq(irq))
                        writel(irq, per_cpu_int_base +
                               ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
                else
index 63cd031b2c28d40c9c1296ae466005a5d48843d3..b12a5d58546f922b460d6aac34c073ff363ef1b8 100644 (file)
@@ -114,7 +114,7 @@ int aic_common_irq_domain_xlate(struct irq_domain *d,
 
 static void __init aic_common_ext_irq_of_init(struct irq_domain *domain)
 {
-       struct device_node *node = domain->of_node;
+       struct device_node *node = irq_domain_get_of_node(domain);
        struct irq_chip_generic *gc;
        struct aic_chip_data *aic;
        struct property *prop;
index f6d680485beecaf0ec870a0d7492146e3fb72090..62bb840c613f2a660966333f858426af5ab3d93e 100644 (file)
@@ -70,16 +70,15 @@ static struct irq_domain *aic5_domain;
 static asmlinkage void __exception_irq_entry
 aic5_handle(struct pt_regs *regs)
 {
-       struct irq_domain_chip_generic *dgc = aic5_domain->gc;
-       struct irq_chip_generic *gc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(aic5_domain, 0);
        u32 irqnr;
        u32 irqstat;
 
-       irqnr = irq_reg_readl(gc, AT91_AIC5_IVR);
-       irqstat = irq_reg_readl(gc, AT91_AIC5_ISR);
+       irqnr = irq_reg_readl(bgc, AT91_AIC5_IVR);
+       irqstat = irq_reg_readl(bgc, AT91_AIC5_ISR);
 
        if (!irqstat)
-               irq_reg_writel(gc, 0, AT91_AIC5_EOICR);
+               irq_reg_writel(bgc, 0, AT91_AIC5_EOICR);
        else
                handle_domain_irq(aic5_domain, irqnr, regs);
 }
@@ -87,8 +86,7 @@ aic5_handle(struct pt_regs *regs)
 static void aic5_mask(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
-       struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *bgc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 
        /*
@@ -105,8 +103,7 @@ static void aic5_mask(struct irq_data *d)
 static void aic5_unmask(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
-       struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *bgc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 
        /*
@@ -123,14 +120,13 @@ static void aic5_unmask(struct irq_data *d)
 static int aic5_retrigger(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
-       struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *gc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
 
        /* Enable interrupt on AIC5 */
-       irq_gc_lock(gc);
-       irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
-       irq_reg_writel(gc, 1, AT91_AIC5_ISCR);
-       irq_gc_unlock(gc);
+       irq_gc_lock(bgc);
+       irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
+       irq_reg_writel(bgc, 1, AT91_AIC5_ISCR);
+       irq_gc_unlock(bgc);
 
        return 0;
 }
@@ -138,18 +134,17 @@ static int aic5_retrigger(struct irq_data *d)
 static int aic5_set_type(struct irq_data *d, unsigned type)
 {
        struct irq_domain *domain = d->domain;
-       struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *gc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        unsigned int smr;
        int ret;
 
-       irq_gc_lock(gc);
-       irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
-       smr = irq_reg_readl(gc, AT91_AIC5_SMR);
+       irq_gc_lock(bgc);
+       irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
+       smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
        ret = aic_common_set_type(d, type, &smr);
        if (!ret)
-               irq_reg_writel(gc, smr, AT91_AIC5_SMR);
-       irq_gc_unlock(gc);
+               irq_reg_writel(bgc, smr, AT91_AIC5_SMR);
+       irq_gc_unlock(bgc);
 
        return ret;
 }
@@ -159,7 +154,7 @@ static void aic5_suspend(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
        struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *bgc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        int i;
        u32 mask;
@@ -183,7 +178,7 @@ static void aic5_resume(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
        struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *bgc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        int i;
        u32 mask;
@@ -207,7 +202,7 @@ static void aic5_pm_shutdown(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
        struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *bgc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        int i;
 
@@ -262,12 +257,11 @@ static int aic5_irq_domain_xlate(struct irq_domain *d,
                                 irq_hw_number_t *out_hwirq,
                                 unsigned int *out_type)
 {
-       struct irq_domain_chip_generic *dgc = d->gc;
-       struct irq_chip_generic *gc;
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(d, 0);
        unsigned smr;
        int ret;
 
-       if (!dgc)
+       if (!bgc)
                return -EINVAL;
 
        ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
@@ -275,15 +269,13 @@ static int aic5_irq_domain_xlate(struct irq_domain *d,
        if (ret)
                return ret;
 
-       gc = dgc->gc[0];
-
-       irq_gc_lock(gc);
-       irq_reg_writel(gc, *out_hwirq, AT91_AIC5_SSR);
-       smr = irq_reg_readl(gc, AT91_AIC5_SMR);
+       irq_gc_lock(bgc);
+       irq_reg_writel(bgc, *out_hwirq, AT91_AIC5_SSR);
+       smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
        ret = aic_common_set_priority(intspec[2], &smr);
        if (!ret)
-               irq_reg_writel(gc, intspec[2] | smr, AT91_AIC5_SMR);
-       irq_gc_unlock(gc);
+               irq_reg_writel(bgc, intspec[2] | smr, AT91_AIC5_SMR);
+       irq_gc_unlock(bgc);
 
        return ret;
 }
index a7f5626930f506289c6eaea34529c20b2051c750..75573fa431ba8552e21afcc19edc8190c329d131 100644 (file)
@@ -78,10 +78,13 @@ static struct irq_chip crossbar_chip = {
 static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
                            irq_hw_number_t hwirq)
 {
-       struct of_phandle_args args;
+       struct irq_fwspec fwspec;
        int i;
        int err;
 
+       if (!irq_domain_get_of_node(domain->parent))
+               return -EINVAL;
+
        raw_spin_lock(&cb->lock);
        for (i = cb->int_max - 1; i >= 0; i--) {
                if (cb->irq_map[i] == IRQ_FREE) {
@@ -94,13 +97,13 @@ static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
        if (i < 0)
                return -ENODEV;
 
-       args.np = domain->parent->of_node;
-       args.args_count = 3;
-       args.args[0] = 0;       /* SPI */
-       args.args[1] = i;
-       args.args[2] = IRQ_TYPE_LEVEL_HIGH;
+       fwspec.fwnode = domain->parent->fwnode;
+       fwspec.param_count = 3;
+       fwspec.param[0] = 0;    /* SPI */
+       fwspec.param[1] = i;
+       fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH;
 
-       err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
+       err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
        if (err)
                cb->irq_map[i] = IRQ_FREE;
        else
@@ -112,16 +115,16 @@ static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
 static int crossbar_domain_alloc(struct irq_domain *d, unsigned int virq,
                                 unsigned int nr_irqs, void *data)
 {
-       struct of_phandle_args *args = data;
+       struct irq_fwspec *fwspec = data;
        irq_hw_number_t hwirq;
        int i;
 
-       if (args->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL; /* Not GIC compliant */
-       if (args->args[0] != 0)
+       if (fwspec->param[0] != 0)
                return -EINVAL; /* No PPI should point to this domain */
 
-       hwirq = args->args[1];
+       hwirq = fwspec->param[1];
        if ((hwirq + nr_irqs) > cb->max_crossbar_sources)
                return -EINVAL; /* Can't deal with this */
 
@@ -166,28 +169,31 @@ static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq,
        raw_spin_unlock(&cb->lock);
 }
 
-static int crossbar_domain_xlate(struct irq_domain *d,
-                                struct device_node *controller,
-                                const u32 *intspec, unsigned int intsize,
-                                unsigned long *out_hwirq,
-                                unsigned int *out_type)
+static int crossbar_domain_translate(struct irq_domain *d,
+                                    struct irq_fwspec *fwspec,
+                                    unsigned long *hwirq,
+                                    unsigned int *type)
 {
-       if (d->of_node != controller)
-               return -EINVAL; /* Shouldn't happen, really... */
-       if (intsize != 3)
-               return -EINVAL; /* Not GIC compliant */
-       if (intspec[0] != 0)
-               return -EINVAL; /* No PPI should point to this domain */
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
+
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static const struct irq_domain_ops crossbar_domain_ops = {
-       .alloc  = crossbar_domain_alloc,
-       .free   = crossbar_domain_free,
-       .xlate  = crossbar_domain_xlate,
+       .alloc          = crossbar_domain_alloc,
+       .free           = crossbar_domain_free,
+       .translate      = crossbar_domain_translate,
 };
 
 static int __init crossbar_of_init(struct device_node *node)
index 9448e391cb710363d18df4c3079ae0504b0cbd44..44a077f3a4a2627c03953d9650a66e7607dd91e2 100644 (file)
 
 #include "irq-gic-common.h"
 
+void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
+               void *data)
+{
+       for (; quirks->desc; quirks++) {
+               if (quirks->iidr != (quirks->mask & iidr))
+                       continue;
+               quirks->init(data);
+               pr_info("GIC: enabling workaround for %s\n", quirks->desc);
+       }
+}
+
 int gic_configure_irq(unsigned int irq, unsigned int type,
                       void __iomem *base, void (*sync_access)(void))
 {
index 35a9884778bd5f337f43041f9a24b88b0371734c..fff697db8e2277be577f7f348917875f9066302e 100644 (file)
 #include <linux/of.h>
 #include <linux/irqdomain.h>
 
+struct gic_quirk {
+       const char *desc;
+       void (*init)(void *data);
+       u32 iidr;
+       u32 mask;
+};
+
 int gic_configure_irq(unsigned int irq, unsigned int type,
                        void __iomem *base, void (*sync_access)(void));
 void gic_dist_config(void __iomem *base, int gic_irqs,
                     void (*sync_access)(void));
 void gic_cpu_config(void __iomem *base, void (*sync_access)(void));
+void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
+               void *data);
 
 #endif /* _IRQ_GIC_COMMON_H */
index 12985daa66ab31c9c7981e65ea0926938e5662fa..87f8d104acab37970b834ded3b74a93ba8b692f6 100644 (file)
 #define V2M_MSI_SETSPI_NS             0x040
 #define V2M_MIN_SPI                   32
 #define V2M_MAX_SPI                   1019
+#define V2M_MSI_IIDR                  0xFCC
 
 #define V2M_MSI_TYPER_BASE_SPI(x)      \
               (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK)
 
 #define V2M_MSI_TYPER_NUM_SPI(x)       ((x) & V2M_MSI_TYPER_NUM_MASK)
 
+/* APM X-Gene with GICv2m MSI_IIDR register value */
+#define XGENE_GICV2M_MSI_IIDR          0x06000170
+
+/* List of flags for specific v2m implementation */
+#define GICV2M_NEEDS_SPI_OFFSET                0x00000001
+
+static LIST_HEAD(v2m_nodes);
+static DEFINE_SPINLOCK(v2m_lock);
+
 struct v2m_data {
-       spinlock_t msi_cnt_lock;
+       struct list_head entry;
+       struct device_node *node;
        struct resource res;    /* GICv2m resource */
        void __iomem *base;     /* GICv2m virt address */
        u32 spi_start;          /* The SPI number that MSIs start */
        u32 nr_spis;            /* The number of SPIs for MSIs */
        unsigned long *bm;      /* MSI vector bitmap */
+       u32 flags;              /* v2m flags for specific implementation */
 };
 
 static void gicv2m_mask_msi_irq(struct irq_data *d)
@@ -98,6 +110,9 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
        msg->address_hi = upper_32_bits(addr);
        msg->address_lo = lower_32_bits(addr);
        msg->data = data->hwirq;
+
+       if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
+               msg->data -= v2m->spi_start;
 }
 
 static struct irq_chip gicv2m_irq_chip = {
@@ -113,17 +128,21 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain,
                                       unsigned int virq,
                                       irq_hw_number_t hwirq)
 {
-       struct of_phandle_args args;
+       struct irq_fwspec fwspec;
        struct irq_data *d;
        int err;
 
-       args.np = domain->parent->of_node;
-       args.args_count = 3;
-       args.args[0] = 0;
-       args.args[1] = hwirq - 32;
-       args.args[2] = IRQ_TYPE_EDGE_RISING;
+       if (is_of_node(domain->parent->fwnode)) {
+               fwspec.fwnode = domain->parent->fwnode;
+               fwspec.param_count = 3;
+               fwspec.param[0] = 0;
+               fwspec.param[1] = hwirq - 32;
+               fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+       } else {
+               return -EINVAL;
+       }
 
-       err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
+       err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
        if (err)
                return err;
 
@@ -143,27 +162,30 @@ static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq)
                return;
        }
 
-       spin_lock(&v2m->msi_cnt_lock);
+       spin_lock(&v2m_lock);
        __clear_bit(pos, v2m->bm);
-       spin_unlock(&v2m->msi_cnt_lock);
+       spin_unlock(&v2m_lock);
 }
 
 static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
                                   unsigned int nr_irqs, void *args)
 {
-       struct v2m_data *v2m = domain->host_data;
+       struct v2m_data *v2m = NULL, *tmp;
        int hwirq, offset, err = 0;
 
-       spin_lock(&v2m->msi_cnt_lock);
-       offset = find_first_zero_bit(v2m->bm, v2m->nr_spis);
-       if (offset < v2m->nr_spis)
-               __set_bit(offset, v2m->bm);
-       else
-               err = -ENOSPC;
-       spin_unlock(&v2m->msi_cnt_lock);
+       spin_lock(&v2m_lock);
+       list_for_each_entry(tmp, &v2m_nodes, entry) {
+               offset = find_first_zero_bit(tmp->bm, tmp->nr_spis);
+               if (offset < tmp->nr_spis) {
+                       __set_bit(offset, tmp->bm);
+                       v2m = tmp;
+                       break;
+               }
+       }
+       spin_unlock(&v2m_lock);
 
-       if (err)
-               return err;
+       if (!v2m)
+               return -ENOSPC;
 
        hwirq = v2m->spi_start + offset;
 
@@ -224,12 +246,61 @@ static struct msi_domain_info gicv2m_pmsi_domain_info = {
        .chip   = &gicv2m_pmsi_irq_chip,
 };
 
+static void gicv2m_teardown(void)
+{
+       struct v2m_data *v2m, *tmp;
+
+       list_for_each_entry_safe(v2m, tmp, &v2m_nodes, entry) {
+               list_del(&v2m->entry);
+               kfree(v2m->bm);
+               iounmap(v2m->base);
+               of_node_put(v2m->node);
+               kfree(v2m);
+       }
+}
+
+static int gicv2m_allocate_domains(struct irq_domain *parent)
+{
+       struct irq_domain *inner_domain, *pci_domain, *plat_domain;
+       struct v2m_data *v2m;
+
+       v2m = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
+       if (!v2m)
+               return 0;
+
+       inner_domain = irq_domain_create_tree(of_node_to_fwnode(v2m->node),
+                                             &gicv2m_domain_ops, v2m);
+       if (!inner_domain) {
+               pr_err("Failed to create GICv2m domain\n");
+               return -ENOMEM;
+       }
+
+       inner_domain->bus_token = DOMAIN_BUS_NEXUS;
+       inner_domain->parent = parent;
+       pci_domain = pci_msi_create_irq_domain(of_node_to_fwnode(v2m->node),
+                                              &gicv2m_msi_domain_info,
+                                              inner_domain);
+       plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(v2m->node),
+                                                    &gicv2m_pmsi_domain_info,
+                                                    inner_domain);
+       if (!pci_domain || !plat_domain) {
+               pr_err("Failed to create MSI domains\n");
+               if (plat_domain)
+                       irq_domain_remove(plat_domain);
+               if (pci_domain)
+                       irq_domain_remove(pci_domain);
+               irq_domain_remove(inner_domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
 static int __init gicv2m_init_one(struct device_node *node,
                                  struct irq_domain *parent)
 {
        int ret;
        struct v2m_data *v2m;
-       struct irq_domain *inner_domain, *pci_domain, *plat_domain;
 
        v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL);
        if (!v2m) {
@@ -237,6 +308,9 @@ static int __init gicv2m_init_one(struct device_node *node,
                return -ENOMEM;
        }
 
+       INIT_LIST_HEAD(&v2m->entry);
+       v2m->node = node;
+
        ret = of_address_to_resource(node, 0, &v2m->res);
        if (ret) {
                pr_err("Failed to allocate v2m resource.\n");
@@ -266,6 +340,17 @@ static int __init gicv2m_init_one(struct device_node *node,
                goto err_iounmap;
        }
 
+       /*
+        * APM X-Gene GICv2m implementation has an erratum where
+        * the MSI data needs to be the offset from the spi_start
+        * in order to trigger the correct MSI interrupt. This is
+        * different from the standard GICv2m implementation where
+        * the MSI data is the absolute value within the range from
+        * spi_start to (spi_start + num_spis).
+        */
+       if (readl_relaxed(v2m->base + V2M_MSI_IIDR) == XGENE_GICV2M_MSI_IIDR)
+               v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
+
        v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis),
                          GFP_KERNEL);
        if (!v2m->bm) {
@@ -273,43 +358,13 @@ static int __init gicv2m_init_one(struct device_node *node,
                goto err_iounmap;
        }
 
-       inner_domain = irq_domain_add_tree(node, &gicv2m_domain_ops, v2m);
-       if (!inner_domain) {
-               pr_err("Failed to create GICv2m domain\n");
-               ret = -ENOMEM;
-               goto err_free_bm;
-       }
-
-       inner_domain->bus_token = DOMAIN_BUS_NEXUS;
-       inner_domain->parent = parent;
-       pci_domain = pci_msi_create_irq_domain(node, &gicv2m_msi_domain_info,
-                                              inner_domain);
-       plat_domain = platform_msi_create_irq_domain(node,
-                                                    &gicv2m_pmsi_domain_info,
-                                                    inner_domain);
-       if (!pci_domain || !plat_domain) {
-               pr_err("Failed to create MSI domains\n");
-               ret = -ENOMEM;
-               goto err_free_domains;
-       }
-
-       spin_lock_init(&v2m->msi_cnt_lock);
-
+       list_add_tail(&v2m->entry, &v2m_nodes);
        pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name,
                (unsigned long)v2m->res.start, (unsigned long)v2m->res.end,
                v2m->spi_start, (v2m->spi_start + v2m->nr_spis));
 
        return 0;
 
-err_free_domains:
-       if (plat_domain)
-               irq_domain_remove(plat_domain);
-       if (pci_domain)
-               irq_domain_remove(pci_domain);
-       if (inner_domain)
-               irq_domain_remove(inner_domain);
-err_free_bm:
-       kfree(v2m->bm);
 err_iounmap:
        iounmap(v2m->base);
 err_free_v2m:
@@ -339,5 +394,9 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
                }
        }
 
+       if (!ret)
+               ret = gicv2m_allocate_domains(parent);
+       if (ret)
+               gicv2m_teardown();
        return ret;
 }
index a7c8c9ffbafd3503228a6c4e63c6870b5d9c784c..aee60ed025dccfab0c6f6b81dbe4e1678a91279c 100644 (file)
@@ -42,7 +42,6 @@ static struct irq_chip its_msi_irq_chip = {
 
 struct its_pci_alias {
        struct pci_dev  *pdev;
-       u32             dev_id;
        u32             count;
 };
 
@@ -60,7 +59,6 @@ static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
 {
        struct its_pci_alias *dev_alias = data;
 
-       dev_alias->dev_id = alias;
        if (pdev != dev_alias->pdev)
                dev_alias->count += its_pci_msi_vec_count(pdev);
 
@@ -86,7 +84,7 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
        pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);
 
        /* ITS specific DeviceID, as the core ITS ignores dev. */
-       info->scratchpad[0].ul = dev_alias.dev_id;
+       info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
 
        return msi_info->ops->msi_prepare(domain->parent,
                                          dev, dev_alias.count, info);
@@ -125,7 +123,8 @@ static int __init its_pci_msi_init(void)
                        continue;
                }
 
-               if (!pci_msi_create_irq_domain(np, &its_pci_msi_domain_info,
+               if (!pci_msi_create_irq_domain(of_node_to_fwnode(np),
+                                              &its_pci_msi_domain_info,
                                               parent)) {
                        pr_err("%s: unable to create PCI domain\n",
                               np->full_name);
index a8655056277995417af6759591e6695ef71cc159..470b4aa7d62c2816015b57420a81f12b37ef4649 100644 (file)
@@ -29,13 +29,25 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
 {
        struct msi_domain_info *msi_info;
        u32 dev_id;
-       int ret;
+       int ret, index = 0;
 
        msi_info = msi_get_domain_info(domain->parent);
 
        /* Suck the DeviceID out of the msi-parent property */
-       ret = of_property_read_u32_index(dev->of_node, "msi-parent",
-                                        1, &dev_id);
+       do {
+               struct of_phandle_args args;
+
+               ret = of_parse_phandle_with_args(dev->of_node,
+                                                "msi-parent", "#msi-cells",
+                                                index, &args);
+               if (args.np == irq_domain_get_of_node(domain)) {
+                       if (WARN_ON(args.args_count != 1))
+                               return -EINVAL;
+                       dev_id = args.args[0];
+                       break;
+               }
+       } while (!ret);
+
        if (ret)
                return ret;
 
@@ -78,7 +90,8 @@ static int __init its_pmsi_init(void)
                        continue;
                }
 
-               if (!platform_msi_create_irq_domain(np, &its_pmsi_domain_info,
+               if (!platform_msi_create_irq_domain(of_node_to_fwnode(np),
+                                                   &its_pmsi_domain_info,
                                                    parent)) {
                        pr_err("%s: unable to create platform domain\n",
                               np->full_name);
index 25ceae9f7348b3208bdd6dec0048a88d4ce34f73..e23d1d18f9d6a39731bdd34633e2bbc9bc473525 100644 (file)
 #include <asm/cputype.h>
 #include <asm/exception.h>
 
-#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING          (1 << 0)
+#include "irq-gic-common.h"
+
+#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING          (1ULL << 0)
+#define ITS_FLAGS_WORKAROUND_CAVIUM_22375      (1ULL << 1)
 
 #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING    (1 << 0)
 
@@ -817,7 +820,22 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
        int i;
        int psz = SZ_64K;
        u64 shr = GITS_BASER_InnerShareable;
-       u64 cache = GITS_BASER_WaWb;
+       u64 cache;
+       u64 typer;
+       u32 ids;
+
+       if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) {
+               /*
+                * erratum 22375: only alloc 8MB table size
+                * erratum 24313: ignore memory access type
+                */
+               cache   = 0;
+               ids     = 0x14;                 /* 20 bits, 8MB */
+       } else {
+               cache   = GITS_BASER_WaWb;
+               typer   = readq_relaxed(its->base + GITS_TYPER);
+               ids     = GITS_TYPER_DEVBITS(typer);
+       }
 
        for (i = 0; i < GITS_BASER_NR_REGS; i++) {
                u64 val = readq_relaxed(its->base + GITS_BASER + i * 8);
@@ -825,6 +843,7 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
                u64 entry_size = GITS_BASER_ENTRY_SIZE(val);
                int order = get_order(psz);
                int alloc_size;
+               int alloc_pages;
                u64 tmp;
                void *base;
 
@@ -840,9 +859,6 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
                 * For other tables, only allocate a single page.
                 */
                if (type == GITS_BASER_TYPE_DEVICE) {
-                       u64 typer = readq_relaxed(its->base + GITS_TYPER);
-                       u32 ids = GITS_TYPER_DEVBITS(typer);
-
                        /*
                         * 'order' was initialized earlier to the default page
                         * granule of the the ITS.  We can't have an allocation
@@ -859,6 +875,14 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
                }
 
                alloc_size = (1 << order) * PAGE_SIZE;
+               alloc_pages = (alloc_size / psz);
+               if (alloc_pages > GITS_BASER_PAGES_MAX) {
+                       alloc_pages = GITS_BASER_PAGES_MAX;
+                       order = get_order(GITS_BASER_PAGES_MAX * psz);
+                       pr_warn("%s: Device Table too large, reduce its page order to %u (%u pages)\n",
+                               node_name, order, alloc_pages);
+               }
+
                base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
                if (!base) {
                        err = -ENOMEM;
@@ -887,7 +911,7 @@ retry_baser:
                        break;
                }
 
-               val |= (alloc_size / psz) - 1;
+               val |= alloc_pages - 1;
 
                writeq_relaxed(val, its->base + GITS_BASER + i * 8);
                tmp = readq_relaxed(its->base + GITS_BASER + i * 8);
@@ -1241,15 +1265,19 @@ static int its_irq_gic_domain_alloc(struct irq_domain *domain,
                                    unsigned int virq,
                                    irq_hw_number_t hwirq)
 {
-       struct of_phandle_args args;
+       struct irq_fwspec fwspec;
 
-       args.np = domain->parent->of_node;
-       args.args_count = 3;
-       args.args[0] = GIC_IRQ_TYPE_LPI;
-       args.args[1] = hwirq;
-       args.args[2] = IRQ_TYPE_EDGE_RISING;
+       if (irq_domain_get_of_node(domain->parent)) {
+               fwspec.fwnode = domain->parent->fwnode;
+               fwspec.param_count = 3;
+               fwspec.param[0] = GIC_IRQ_TYPE_LPI;
+               fwspec.param[1] = hwirq;
+               fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+       } else {
+               return -EINVAL;
+       }
 
-       return irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
+       return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
 }
 
 static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
@@ -1370,6 +1398,33 @@ static int its_force_quiescent(void __iomem *base)
        }
 }
 
+static void __maybe_unused its_enable_quirk_cavium_22375(void *data)
+{
+       struct its_node *its = data;
+
+       its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375;
+}
+
+static const struct gic_quirk its_quirks[] = {
+#ifdef CONFIG_CAVIUM_ERRATUM_22375
+       {
+               .desc   = "ITS: Cavium errata 22375, 24313",
+               .iidr   = 0xa100034c,   /* ThunderX pass 1.x */
+               .mask   = 0xffff0fff,
+               .init   = its_enable_quirk_cavium_22375,
+       },
+#endif
+       {
+       }
+};
+
+static void its_enable_quirks(struct its_node *its)
+{
+       u32 iidr = readl_relaxed(its->base + GITS_IIDR);
+
+       gic_enable_quirks(iidr, its_quirks, its);
+}
+
 static int its_probe(struct device_node *node, struct irq_domain *parent)
 {
        struct resource res;
@@ -1428,6 +1483,8 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
        }
        its->cmd_write = its->cmd_base;
 
+       its_enable_quirks(its);
+
        err = its_alloc_tables(node->full_name, its);
        if (err)
                goto out_free_cmd;
index 36ecfc870e5a6cb86698cb218ac00cccc491e299..d7be6ddc34f685a47dad313274f209b59dff6bf5 100644 (file)
@@ -108,57 +108,17 @@ static void gic_redist_wait_for_rwp(void)
        gic_do_wait_for_rwp(gic_data_rdist_rd_base());
 }
 
-/* Low level accessors */
-static u64 __maybe_unused gic_read_iar(void)
-{
-       u64 irqstat;
-
-       asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat));
-       return irqstat;
-}
-
-static void __maybe_unused gic_write_pmr(u64 val)
-{
-       asm volatile("msr_s " __stringify(ICC_PMR_EL1) ", %0" : : "r" (val));
-}
-
-static void __maybe_unused gic_write_ctlr(u64 val)
-{
-       asm volatile("msr_s " __stringify(ICC_CTLR_EL1) ", %0" : : "r" (val));
-       isb();
-}
-
-static void __maybe_unused gic_write_grpen1(u64 val)
-{
-       asm volatile("msr_s " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" (val));
-       isb();
-}
+#ifdef CONFIG_ARM64
+static DEFINE_STATIC_KEY_FALSE(is_cavium_thunderx);
 
-static void __maybe_unused gic_write_sgi1r(u64 val)
-{
-       asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val));
-}
-
-static void gic_enable_sre(void)
+static u64 __maybe_unused gic_read_iar(void)
 {
-       u64 val;
-
-       asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val));
-       val |= ICC_SRE_EL1_SRE;
-       asm volatile("msr_s " __stringify(ICC_SRE_EL1) ", %0" : : "r" (val));
-       isb();
-
-       /*
-        * Need to check that the SRE bit has actually been set. If
-        * not, it means that SRE is disabled at EL2. We're going to
-        * die painfully, and there is nothing we can do about it.
-        *
-        * Kindly inform the luser.
-        */
-       asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val));
-       if (!(val & ICC_SRE_EL1_SRE))
-               pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
+       if (static_branch_unlikely(&is_cavium_thunderx))
+               return gic_read_iar_cavium_thunderx();
+       else
+               return gic_read_iar_common();
 }
+#endif
 
 static void gic_enable_redist(bool enable)
 {
@@ -359,11 +319,11 @@ static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
        return 0;
 }
 
-static u64 gic_mpidr_to_affinity(u64 mpidr)
+static u64 gic_mpidr_to_affinity(unsigned long mpidr)
 {
        u64 aff;
 
-       aff = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 |
+       aff = ((u64)MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 |
               MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
               MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8  |
               MPIDR_AFFINITY_LEVEL(mpidr, 0));
@@ -373,7 +333,7 @@ static u64 gic_mpidr_to_affinity(u64 mpidr)
 
 static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
 {
-       u64 irqnr;
+       u32 irqnr;
 
        do {
                irqnr = gic_read_iar();
@@ -432,12 +392,12 @@ static void __init gic_dist_init(void)
         */
        affinity = gic_mpidr_to_affinity(cpu_logical_map(smp_processor_id()));
        for (i = 32; i < gic_data.irq_nr; i++)
-               writeq_relaxed(affinity, base + GICD_IROUTER + i * 8);
+               gic_write_irouter(affinity, base + GICD_IROUTER + i * 8);
 }
 
 static int gic_populate_rdist(void)
 {
-       u64 mpidr = cpu_logical_map(smp_processor_id());
+       unsigned long mpidr = cpu_logical_map(smp_processor_id());
        u64 typer;
        u32 aff;
        int i;
@@ -463,15 +423,14 @@ static int gic_populate_rdist(void)
                }
 
                do {
-                       typer = readq_relaxed(ptr + GICR_TYPER);
+                       typer = gic_read_typer(ptr + GICR_TYPER);
                        if ((typer >> 32) == aff) {
                                u64 offset = ptr - gic_data.redist_regions[i].redist_base;
                                gic_data_rdist_rd_base() = ptr;
                                gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset;
-                               pr_info("CPU%d: found redistributor %llx region %d:%pa\n",
-                                       smp_processor_id(),
-                                       (unsigned long long)mpidr,
-                                       i, &gic_data_rdist()->phys_base);
+                               pr_info("CPU%d: found redistributor %lx region %d:%pa\n",
+                                       smp_processor_id(), mpidr, i,
+                                       &gic_data_rdist()->phys_base);
                                return 0;
                        }
 
@@ -486,15 +445,22 @@ static int gic_populate_rdist(void)
        }
 
        /* We couldn't even deal with ourselves... */
-       WARN(true, "CPU%d: mpidr %llx has no re-distributor!\n",
-            smp_processor_id(), (unsigned long long)mpidr);
+       WARN(true, "CPU%d: mpidr %lx has no re-distributor!\n",
+            smp_processor_id(), mpidr);
        return -ENODEV;
 }
 
 static void gic_cpu_sys_reg_init(void)
 {
-       /* Enable system registers */
-       gic_enable_sre();
+       /*
+        * Need to check that the SRE bit has actually been set. If
+        * not, it means that SRE is disabled at EL2. We're going to
+        * die painfully, and there is nothing we can do about it.
+        *
+        * Kindly inform the luser.
+        */
+       if (!gic_enable_sre())
+               pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
 
        /* Set priority mask register */
        gic_write_pmr(DEFAULT_PMR_VALUE);
@@ -557,10 +523,10 @@ static struct notifier_block gic_cpu_notifier = {
 };
 
 static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
-                                  u64 cluster_id)
+                                  unsigned long cluster_id)
 {
        int cpu = *base_cpu;
-       u64 mpidr = cpu_logical_map(cpu);
+       unsigned long mpidr = cpu_logical_map(cpu);
        u16 tlist = 0;
 
        while (cpu < nr_cpu_ids) {
@@ -621,7 +587,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
        smp_wmb();
 
        for_each_cpu(cpu, mask) {
-               u64 cluster_id = cpu_logical_map(cpu) & ~0xffUL;
+               unsigned long cluster_id = cpu_logical_map(cpu) & ~0xffUL;
                u16 tlist;
 
                tlist = gic_compute_target_list(&cpu, mask, cluster_id);
@@ -657,7 +623,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
        reg = gic_dist_base(d) + GICD_IROUTER + (gic_irq(d) * 8);
        val = gic_mpidr_to_affinity(cpu_logical_map(cpu));
 
-       writeq_relaxed(val, reg);
+       gic_write_irouter(val, reg);
 
        /*
         * If the interrupt was enabled, enabled it again. Otherwise,
@@ -771,32 +737,34 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
        return 0;
 }
 
-static int gic_irq_domain_xlate(struct irq_domain *d,
-                               struct device_node *controller,
-                               const u32 *intspec, unsigned int intsize,
-                               unsigned long *out_hwirq, unsigned int *out_type)
+static int gic_irq_domain_translate(struct irq_domain *d,
+                                   struct irq_fwspec *fwspec,
+                                   unsigned long *hwirq,
+                                   unsigned int *type)
 {
-       if (d->of_node != controller)
-               return -EINVAL;
-       if (intsize < 3)
-               return -EINVAL;
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count < 3)
+                       return -EINVAL;
 
-       switch(intspec[0]) {
-       case 0:                 /* SPI */
-               *out_hwirq = intspec[1] + 32;
-               break;
-       case 1:                 /* PPI */
-               *out_hwirq = intspec[1] + 16;
-               break;
-       case GIC_IRQ_TYPE_LPI:  /* LPI */
-               *out_hwirq = intspec[1];
-               break;
-       default:
-               return -EINVAL;
+               switch (fwspec->param[0]) {
+               case 0:                 /* SPI */
+                       *hwirq = fwspec->param[1] + 32;
+                       break;
+               case 1:                 /* PPI */
+                       *hwirq = fwspec->param[1] + 16;
+                       break;
+               case GIC_IRQ_TYPE_LPI:  /* LPI */
+                       *hwirq = fwspec->param[1];
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+               return 0;
        }
 
-       *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
-       return 0;
+       return -EINVAL;
 }
 
 static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
@@ -805,10 +773,9 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
        int i, ret;
        irq_hw_number_t hwirq;
        unsigned int type = IRQ_TYPE_NONE;
-       struct of_phandle_args *irq_data = arg;
+       struct irq_fwspec *fwspec = arg;
 
-       ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args,
-                                  irq_data->args_count, &hwirq, &type);
+       ret = gic_irq_domain_translate(domain, fwspec, &hwirq, &type);
        if (ret)
                return ret;
 
@@ -831,11 +798,19 @@ static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
 }
 
 static const struct irq_domain_ops gic_irq_domain_ops = {
-       .xlate = gic_irq_domain_xlate,
+       .translate = gic_irq_domain_translate,
        .alloc = gic_irq_domain_alloc,
        .free = gic_irq_domain_free,
 };
 
+static void gicv3_enable_quirks(void)
+{
+#ifdef CONFIG_ARM64
+       if (cpus_have_cap(ARM64_WORKAROUND_CAVIUM_23154))
+               static_branch_enable(&is_cavium_thunderx);
+#endif
+}
+
 static int __init gic_of_init(struct device_node *node, struct device_node *parent)
 {
        void __iomem *dist_base;
@@ -901,6 +876,8 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
        gic_data.nr_redist_regions = nr_redist_regions;
        gic_data.redist_stride = redist_stride;
 
+       gicv3_enable_quirks();
+
        /*
         * Find out how many interrupts are supported.
         * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
index 982c09c2d79171d21355c75153b4de87f4a1bee2..1d0e76855106cf946627dcc5c5aaa728a38c6bc8 100644 (file)
 
 #include "irq-gic-common.h"
 
+#ifdef CONFIG_ARM64
+#include <asm/cpufeature.h>
+
+static void gic_check_cpu_features(void)
+{
+       WARN_TAINT_ONCE(cpus_have_cap(ARM64_HAS_SYSREG_GIC_CPUIF),
+                       TAINT_CPU_OUT_OF_SPEC,
+                       "GICv3 system registers enabled, broken firmware!\n");
+}
+#else
+#define gic_check_cpu_features()       do { } while(0)
+#endif
+
 union gic_base {
        void __iomem *common_base;
        void __percpu * __iomem *percpu_base;
@@ -903,28 +916,39 @@ static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
 {
 }
 
-static int gic_irq_domain_xlate(struct irq_domain *d,
-                               struct device_node *controller,
-                               const u32 *intspec, unsigned int intsize,
-                               unsigned long *out_hwirq, unsigned int *out_type)
+static int gic_irq_domain_translate(struct irq_domain *d,
+                                   struct irq_fwspec *fwspec,
+                                   unsigned long *hwirq,
+                                   unsigned int *type)
 {
-       unsigned long ret = 0;
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count < 3)
+                       return -EINVAL;
 
-       if (d->of_node != controller)
-               return -EINVAL;
-       if (intsize < 3)
-               return -EINVAL;
+               /* Get the interrupt number and add 16 to skip over SGIs */
+               *hwirq = fwspec->param[1] + 16;
 
-       /* Get the interrupt number and add 16 to skip over SGIs */
-       *out_hwirq = intspec[1] + 16;
+               /*
+                * For SPIs, we need to add 16 more to get the GIC irq
+                * ID number
+                */
+               if (!fwspec->param[0])
+                       *hwirq += 16;
 
-       /* For SPIs, we need to add 16 more to get the GIC irq ID number */
-       if (!intspec[0])
-               *out_hwirq += 16;
+               *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+               return 0;
+       }
 
-       *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+       if (fwspec->fwnode->type == FWNODE_IRQCHIP) {
+               if(fwspec->param_count != 2)
+                       return -EINVAL;
 
-       return ret;
+               *hwirq = fwspec->param[0];
+               *type = fwspec->param[1];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 #ifdef CONFIG_SMP
@@ -952,10 +976,9 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
        int i, ret;
        irq_hw_number_t hwirq;
        unsigned int type = IRQ_TYPE_NONE;
-       struct of_phandle_args *irq_data = arg;
+       struct irq_fwspec *fwspec = arg;
 
-       ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args,
-                                  irq_data->args_count, &hwirq, &type);
+       ret = gic_irq_domain_translate(domain, fwspec, &hwirq, &type);
        if (ret)
                return ret;
 
@@ -966,7 +989,7 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 }
 
 static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
-       .xlate = gic_irq_domain_xlate,
+       .translate = gic_irq_domain_translate,
        .alloc = gic_irq_domain_alloc,
        .free = irq_domain_free_irqs_top,
 };
@@ -974,12 +997,11 @@ static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
 static const struct irq_domain_ops gic_irq_domain_ops = {
        .map = gic_irq_domain_map,
        .unmap = gic_irq_domain_unmap,
-       .xlate = gic_irq_domain_xlate,
 };
 
 static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
                           void __iomem *dist_base, void __iomem *cpu_base,
-                          u32 percpu_offset, struct device_node *node)
+                          u32 percpu_offset, struct fwnode_handle *handle)
 {
        irq_hw_number_t hwirq_base;
        struct gic_chip_data *gic;
@@ -987,6 +1009,8 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
 
        BUG_ON(gic_nr >= MAX_GIC_NR);
 
+       gic_check_cpu_features();
+
        gic = &gic_data[gic_nr];
 #ifdef CONFIG_GIC_NON_BANKED
        if (percpu_offset) { /* Frankein-GIC without banked registers... */
@@ -1031,11 +1055,11 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
                gic_irqs = 1020;
        gic->gic_irqs = gic_irqs;
 
-       if (node) {             /* DT case */
-               gic->domain = irq_domain_add_linear(node, gic_irqs,
-                                                   &gic_irq_domain_hierarchy_ops,
-                                                   gic);
-       } else {                /* Non-DT case */
+       if (handle) {           /* DT/ACPI */
+               gic->domain = irq_domain_create_linear(handle, gic_irqs,
+                                                      &gic_irq_domain_hierarchy_ops,
+                                                      gic);
+       } else {                /* Legacy support */
                /*
                 * For primary GICs, skip over SGIs.
                 * For secondary GICs, skip over PPIs, too.
@@ -1058,7 +1082,7 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
                        irq_base = irq_start;
                }
 
-               gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
+               gic->domain = irq_domain_add_legacy(NULL, gic_irqs, irq_base,
                                        hwirq_base, &gic_irq_domain_ops, gic);
        }
 
@@ -1087,17 +1111,15 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
        gic_pm_init(gic);
 }
 
-void __init gic_init_bases(unsigned int gic_nr, int irq_start,
-                          void __iomem *dist_base, void __iomem *cpu_base,
-                          u32 percpu_offset, struct device_node *node)
+void __init gic_init(unsigned int gic_nr, int irq_start,
+                    void __iomem *dist_base, void __iomem *cpu_base)
 {
        /*
         * Non-DT/ACPI systems won't run a hypervisor, so let's not
         * bother with these...
         */
        static_key_slow_dec(&supports_deactivate);
-       __gic_init_bases(gic_nr, irq_start, dist_base, cpu_base,
-                        percpu_offset, node);
+       __gic_init_bases(gic_nr, irq_start, dist_base, cpu_base, 0, NULL);
 }
 
 #ifdef CONFIG_OF
@@ -1168,7 +1190,8 @@ gic_of_init(struct device_node *node, struct device_node *parent)
        if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
                percpu_offset = 0;
 
-       __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);
+       __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset,
+                        &node->fwnode);
        if (!gic_cnt)
                gic_init_physaddr(node);
 
@@ -1191,6 +1214,7 @@ IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init);
 IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);
 IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
 IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
+IRQCHIP_DECLARE(pl390, "arm,pl390", gic_of_init);
 
 #endif
 
@@ -1242,6 +1266,7 @@ int __init
 gic_v2_acpi_init(struct acpi_table_header *table)
 {
        void __iomem *cpu_base, *dist_base;
+       struct fwnode_handle *domain_handle;
        int count;
 
        /* Collect CPU base addresses */
@@ -1292,14 +1317,19 @@ gic_v2_acpi_init(struct acpi_table_header *table)
                static_key_slow_dec(&supports_deactivate);
 
        /*
-        * Initialize zero GIC instance (no multi-GIC support). Also, set GIC
-        * as default IRQ domain to allow for GSI registration and GSI to IRQ
-        * number translation (see acpi_register_gsi() and acpi_gsi_to_irq()).
+        * Initialize GIC instance zero (no multi-GIC support).
         */
-       __gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL);
-       irq_set_default_host(gic_data[0].domain);
+       domain_handle = irq_domain_alloc_fwnode(dist_base);
+       if (!domain_handle) {
+               pr_err("Unable to allocate domain handle\n");
+               iounmap(cpu_base);
+               iounmap(dist_base);
+               return -ENOMEM;
+       }
+
+       __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle);
 
-       acpi_irq_model = ACPI_IRQ_MODEL_GIC;
+       acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
        return 0;
 }
 #endif
index 8f3ca8f3a62b615b2d670851dd8afe1d75904bee..9688d2e2a6364d2accbe77f323dd3b9f1d735dfa 100644 (file)
@@ -325,7 +325,7 @@ static int hip04_irq_domain_xlate(struct irq_domain *d,
 {
        unsigned long ret = 0;
 
-       if (d->of_node != controller)
+       if (irq_domain_get_of_node(d) != controller)
                return -EINVAL;
        if (intsize < 3)
                return -EINVAL;
index e484fd2553210f55fd0eb72375f4ada1511c7fec..6b304eb39bd22c96b64c6a99629ef4195db721b5 100644 (file)
@@ -377,8 +377,8 @@ int __init i8259_of_init(struct device_node *node, struct device_node *parent)
        }
 
        domain = __init_i8259_irqs(node);
-       irq_set_handler_data(parent_irq, domain);
-       irq_set_chained_handler(parent_irq, i8259_irq_dispatch);
+       irq_set_chained_handler_and_data(parent_irq, i8259_irq_dispatch,
+                                        domain);
        return 0;
 }
 IRQCHIP_DECLARE(i8259, "intel,i8259", i8259_of_init);
index e48d3305456fe859e890413144066ff1fbb43875..15af9a9753e582b797a58de3c9713226800999f8 100644 (file)
@@ -150,49 +150,42 @@ static struct irq_chip gpcv2_irqchip_data_chip = {
 #endif
 };
 
-static int imx_gpcv2_domain_xlate(struct irq_domain *domain,
-                               struct device_node *controller,
-                               const u32 *intspec,
-                               unsigned int intsize,
-                               unsigned long *out_hwirq,
-                               unsigned int *out_type)
+static int imx_gpcv2_domain_translate(struct irq_domain *d,
+                                     struct irq_fwspec *fwspec,
+                                     unsigned long *hwirq,
+                                     unsigned int *type)
 {
-       /* Shouldn't happen, really... */
-       if (domain->of_node != controller)
-               return -EINVAL;
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       /* Not GIC compliant */
-       if (intsize != 3)
-               return -EINVAL;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
 
-       /* No PPI should point to this domain */
-       if (intspec[0] != 0)
-               return -EINVAL;
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+       return -EINVAL;
 }
 
 static int imx_gpcv2_domain_alloc(struct irq_domain *domain,
                                  unsigned int irq, unsigned int nr_irqs,
                                  void *data)
 {
-       struct of_phandle_args *args = data;
-       struct of_phandle_args parent_args;
+       struct irq_fwspec *fwspec = data;
+       struct irq_fwspec parent_fwspec;
        irq_hw_number_t hwirq;
+       unsigned int type;
+       int err;
        int i;
 
-       /* Not GIC compliant */
-       if (args->args_count != 3)
-               return -EINVAL;
-
-       /* No PPI should point to this domain */
-       if (args->args[0] != 0)
-               return -EINVAL;
+       err = imx_gpcv2_domain_translate(domain, fwspec, &hwirq, &type);
+       if (err)
+               return err;
 
-       /* Can't deal with this */
-       hwirq = args->args[1];
        if (hwirq >= GPC_MAX_IRQS)
                return -EINVAL;
 
@@ -201,15 +194,16 @@ static int imx_gpcv2_domain_alloc(struct irq_domain *domain,
                                &gpcv2_irqchip_data_chip, domain->host_data);
        }
 
-       parent_args = *args;
-       parent_args.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs, &parent_args);
+       parent_fwspec = *fwspec;
+       parent_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,
+                                           &parent_fwspec);
 }
 
 static struct irq_domain_ops gpcv2_irqchip_data_domain_ops = {
-       .xlate  = imx_gpcv2_domain_xlate,
-       .alloc  = imx_gpcv2_domain_alloc,
-       .free   = irq_domain_free_irqs_common,
+       .translate      = imx_gpcv2_domain_translate,
+       .alloc          = imx_gpcv2_domain_alloc,
+       .free           = irq_domain_free_irqs_common,
 };
 
 static int __init imx_gpcv2_irqchip_init(struct device_node *node,
index c8753da4c156ee9e2fa1b3b5ddfde63561733d30..63ac73b1d9c849345adfc0b1e0984ea21cb5f03c 100644 (file)
@@ -67,22 +67,25 @@ static struct irq_chip mtk_sysirq_chip = {
        .irq_set_affinity       = irq_chip_set_affinity_parent,
 };
 
-static int mtk_sysirq_domain_xlate(struct irq_domain *d,
-                                  struct device_node *controller,
-                                  const u32 *intspec, unsigned int intsize,
-                                  unsigned long *out_hwirq,
-                                  unsigned int *out_type)
+static int mtk_sysirq_domain_translate(struct irq_domain *d,
+                                      struct irq_fwspec *fwspec,
+                                      unsigned long *hwirq,
+                                      unsigned int *type)
 {
-       if (intsize != 3)
-               return -EINVAL;
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       /* sysirq doesn't support PPI */
-       if (intspec[0])
-               return -EINVAL;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
-       return 0;
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
@@ -90,30 +93,30 @@ static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 {
        int i;
        irq_hw_number_t hwirq;
-       struct of_phandle_args *irq_data = arg;
-       struct of_phandle_args gic_data = *irq_data;
+       struct irq_fwspec *fwspec = arg;
+       struct irq_fwspec gic_fwspec = *fwspec;
 
-       if (irq_data->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL;
 
        /* sysirq doesn't support PPI */
-       if (irq_data->args[0])
+       if (fwspec->param[0])
                return -EINVAL;
 
-       hwirq = irq_data->args[1];
+       hwirq = fwspec->param[1];
        for (i = 0; i < nr_irqs; i++)
                irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
                                              &mtk_sysirq_chip,
                                              domain->host_data);
 
-       gic_data.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
+       gic_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_fwspec);
 }
 
 static const struct irq_domain_ops sysirq_domain_ops = {
-       .xlate = mtk_sysirq_domain_xlate,
-       .alloc = mtk_sysirq_domain_alloc,
-       .free = irq_domain_free_irqs_common,
+       .translate      = mtk_sysirq_domain_translate,
+       .alloc          = mtk_sysirq_domain_alloc,
+       .free           = irq_domain_free_irqs_common,
 };
 
 static int __init mtk_sysirq_of_init(struct device_node *node,
index 604df63e2edf6f9706db28d08bf559a0347e8bd8..c22e2d40cb302452624c29efd44c483e577f5333 100644 (file)
@@ -1,5 +1,7 @@
 /*
  * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
+ *     Add Alphascale ASM9260 support.
  *
  * 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
 #include <linux/stmp_device.h>
 #include <asm/exception.h>
 
+#include "alphascale_asm9260-icoll.h"
+
+/*
+ * this device provide 4 offsets for each register:
+ * 0x0 - plain read write mode
+ * 0x4 - set mode, OR logic.
+ * 0x8 - clr mode, XOR logic.
+ * 0xc - togle mode.
+ */
+#define SET_REG 4
+#define CLR_REG 8
+
 #define HW_ICOLL_VECTOR                                0x0000
 #define HW_ICOLL_LEVELACK                      0x0010
 #define HW_ICOLL_CTRL                          0x0020
 #define HW_ICOLL_STAT_OFFSET                   0x0070
-#define HW_ICOLL_INTERRUPTn_SET(n)             (0x0124 + (n) * 0x10)
-#define HW_ICOLL_INTERRUPTn_CLR(n)             (0x0128 + (n) * 0x10)
-#define BM_ICOLL_INTERRUPTn_ENABLE             0x00000004
+#define HW_ICOLL_INTERRUPT0                    0x0120
+#define HW_ICOLL_INTERRUPTn(n)                 ((n) * 0x10)
+#define BM_ICOLL_INTR_ENABLE                   BIT(2)
 #define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0  0x1
 
 #define ICOLL_NUM_IRQS         128
 
-static void __iomem *icoll_base;
+enum icoll_type {
+       ICOLL,
+       ASM9260_ICOLL,
+};
+
+struct icoll_priv {
+       void __iomem *vector;
+       void __iomem *levelack;
+       void __iomem *ctrl;
+       void __iomem *stat;
+       void __iomem *intr;
+       void __iomem *clear;
+       enum icoll_type type;
+};
+
+static struct icoll_priv icoll_priv;
 static struct irq_domain *icoll_domain;
 
+/* calculate bit offset depending on number of intterupt per register */
+static u32 icoll_intr_bitshift(struct irq_data *d, u32 bit)
+{
+       /*
+        * mask lower part of hwirq to convert it
+        * in 0, 1, 2 or 3 and then multiply it by 8 (or shift by 3)
+        */
+       return bit << ((d->hwirq & 3) << 3);
+}
+
+/* calculate mem offset depending on number of intterupt per register */
+static void __iomem *icoll_intr_reg(struct irq_data *d)
+{
+       /* offset = hwirq / intr_per_reg * 0x10 */
+       return icoll_priv.intr + ((d->hwirq >> 2) * 0x10);
+}
+
 static void icoll_ack_irq(struct irq_data *d)
 {
        /*
@@ -50,19 +96,35 @@ static void icoll_ack_irq(struct irq_data *d)
         * BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 unconditionally.
         */
        __raw_writel(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0,
-                       icoll_base + HW_ICOLL_LEVELACK);
+                       icoll_priv.levelack);
 }
 
 static void icoll_mask_irq(struct irq_data *d)
 {
-       __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
-                       icoll_base + HW_ICOLL_INTERRUPTn_CLR(d->hwirq));
+       __raw_writel(BM_ICOLL_INTR_ENABLE,
+                       icoll_priv.intr + CLR_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
 }
 
 static void icoll_unmask_irq(struct irq_data *d)
 {
-       __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
-                       icoll_base + HW_ICOLL_INTERRUPTn_SET(d->hwirq));
+       __raw_writel(BM_ICOLL_INTR_ENABLE,
+                       icoll_priv.intr + SET_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
+}
+
+static void asm9260_mask_irq(struct irq_data *d)
+{
+       __raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
+                       icoll_intr_reg(d) + CLR_REG);
+}
+
+static void asm9260_unmask_irq(struct irq_data *d)
+{
+       __raw_writel(ASM9260_BM_CLEAR_BIT(d->hwirq),
+                    icoll_priv.clear +
+                    ASM9260_HW_ICOLL_CLEARn(d->hwirq));
+
+       __raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
+                       icoll_intr_reg(d) + SET_REG);
 }
 
 static struct irq_chip mxs_icoll_chip = {
@@ -71,19 +133,32 @@ static struct irq_chip mxs_icoll_chip = {
        .irq_unmask = icoll_unmask_irq,
 };
 
+static struct irq_chip asm9260_icoll_chip = {
+       .irq_ack = icoll_ack_irq,
+       .irq_mask = asm9260_mask_irq,
+       .irq_unmask = asm9260_unmask_irq,
+};
+
 asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
 {
        u32 irqnr;
 
-       irqnr = __raw_readl(icoll_base + HW_ICOLL_STAT_OFFSET);
-       __raw_writel(irqnr, icoll_base + HW_ICOLL_VECTOR);
+       irqnr = __raw_readl(icoll_priv.stat);
+       __raw_writel(irqnr, icoll_priv.vector);
        handle_domain_irq(icoll_domain, irqnr, regs);
 }
 
 static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
                                irq_hw_number_t hw)
 {
-       irq_set_chip_and_handler(virq, &mxs_icoll_chip, handle_level_irq);
+       struct irq_chip *chip;
+
+       if (icoll_priv.type == ICOLL)
+               chip = &mxs_icoll_chip;
+       else
+               chip = &asm9260_icoll_chip;
+
+       irq_set_chip_and_handler(virq, chip, handle_level_irq);
 
        return 0;
 }
@@ -93,20 +168,80 @@ static const struct irq_domain_ops icoll_irq_domain_ops = {
        .xlate = irq_domain_xlate_onecell,
 };
 
+static void __init icoll_add_domain(struct device_node *np,
+                         int num)
+{
+       icoll_domain = irq_domain_add_linear(np, num,
+                                            &icoll_irq_domain_ops, NULL);
+
+       if (!icoll_domain)
+               panic("%s: unable to create irq domain", np->full_name);
+}
+
+static void __iomem * __init icoll_init_iobase(struct device_node *np)
+{
+       void __iomem *icoll_base;
+
+       icoll_base = of_io_request_and_map(np, 0, np->name);
+       if (!icoll_base)
+               panic("%s: unable to map resource", np->full_name);
+       return icoll_base;
+}
+
 static int __init icoll_of_init(struct device_node *np,
                          struct device_node *interrupt_parent)
 {
-       icoll_base = of_iomap(np, 0);
-       WARN_ON(!icoll_base);
+       void __iomem *icoll_base;
+
+       icoll_priv.type = ICOLL;
+
+       icoll_base              = icoll_init_iobase(np);
+       icoll_priv.vector       = icoll_base + HW_ICOLL_VECTOR;
+       icoll_priv.levelack     = icoll_base + HW_ICOLL_LEVELACK;
+       icoll_priv.ctrl         = icoll_base + HW_ICOLL_CTRL;
+       icoll_priv.stat         = icoll_base + HW_ICOLL_STAT_OFFSET;
+       icoll_priv.intr         = icoll_base + HW_ICOLL_INTERRUPT0;
+       icoll_priv.clear        = NULL;
 
        /*
         * Interrupt Collector reset, which initializes the priority
         * for each irq to level 0.
         */
-       stmp_reset_block(icoll_base + HW_ICOLL_CTRL);
+       stmp_reset_block(icoll_priv.ctrl);
 
-       icoll_domain = irq_domain_add_linear(np, ICOLL_NUM_IRQS,
-                                            &icoll_irq_domain_ops, NULL);
-       return icoll_domain ? 0 : -ENODEV;
+       icoll_add_domain(np, ICOLL_NUM_IRQS);
+
+       return 0;
 }
 IRQCHIP_DECLARE(mxs, "fsl,icoll", icoll_of_init);
+
+static int __init asm9260_of_init(struct device_node *np,
+                         struct device_node *interrupt_parent)
+{
+       void __iomem *icoll_base;
+       int i;
+
+       icoll_priv.type = ASM9260_ICOLL;
+
+       icoll_base = icoll_init_iobase(np);
+       icoll_priv.vector       = icoll_base + ASM9260_HW_ICOLL_VECTOR;
+       icoll_priv.levelack     = icoll_base + ASM9260_HW_ICOLL_LEVELACK;
+       icoll_priv.ctrl         = icoll_base + ASM9260_HW_ICOLL_CTRL;
+       icoll_priv.stat         = icoll_base + ASM9260_HW_ICOLL_STAT_OFFSET;
+       icoll_priv.intr         = icoll_base + ASM9260_HW_ICOLL_INTERRUPT0;
+       icoll_priv.clear        = icoll_base + ASM9260_HW_ICOLL_CLEAR0;
+
+       writel_relaxed(ASM9260_BM_CTRL_IRQ_ENABLE,
+                       icoll_priv.ctrl);
+       /*
+        * ASM9260 don't provide reset bit. So, we need to set level 0
+        * manually.
+        */
+       for (i = 0; i < 16 * 0x10; i += 0x10)
+               writel(0, icoll_priv.intr + i);
+
+       icoll_add_domain(np, ASM9260_NUM_IRQS);
+
+       return 0;
+}
+IRQCHIP_DECLARE(asm9260, "alphascale,asm9260-icoll", asm9260_of_init);
index a878b8d03868823a222af0ee4d0f296793673a7d..b1777104fd9feca9c739db29caa1d94cc36d0edd 100644 (file)
@@ -48,16 +48,26 @@ nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs)
        handle_IRQ(irq, regs);
 }
 
+static int nvic_irq_domain_translate(struct irq_domain *d,
+                                    struct irq_fwspec *fwspec,
+                                    unsigned long *hwirq, unsigned int *type)
+{
+       if (WARN_ON(fwspec->param_count < 1))
+               return -EINVAL;
+       *hwirq = fwspec->param[0];
+       *type = IRQ_TYPE_NONE;
+       return 0;
+}
+
 static int nvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
                                unsigned int nr_irqs, void *arg)
 {
        int i, ret;
        irq_hw_number_t hwirq;
        unsigned int type = IRQ_TYPE_NONE;
-       struct of_phandle_args *irq_data = arg;
+       struct irq_fwspec *fwspec = arg;
 
-       ret = irq_domain_xlate_onecell(domain, irq_data->np, irq_data->args,
-                                  irq_data->args_count, &hwirq, &type);
+       ret = nvic_irq_domain_translate(domain, fwspec, &hwirq, &type);
        if (ret)
                return ret;
 
@@ -68,7 +78,7 @@ static int nvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 }
 
 static const struct irq_domain_ops nvic_irq_domain_ops = {
-       .xlate = irq_domain_xlate_onecell,
+       .translate = nvic_irq_domain_translate,
        .alloc = nvic_irq_domain_alloc,
        .free = irq_domain_free_irqs_top,
 };
index 9525335723f68f7e4c408d81a6538dc8e72a2b5f..c325806561bedd932db973470d4bedfaf3950886 100644 (file)
@@ -361,14 +361,16 @@ static const struct irq_domain_ops intc_irqpin_irq_domain_ops = {
        .xlate  = irq_domain_xlate_twocell,
 };
 
-static const struct intc_irqpin_irlm_config intc_irqpin_irlm_r8a7779 = {
+static const struct intc_irqpin_irlm_config intc_irqpin_irlm_r8a777x = {
        .irlm_bit = 23, /* ICR0.IRLM0 */
 };
 
 static const struct of_device_id intc_irqpin_dt_ids[] = {
        { .compatible = "renesas,intc-irqpin", },
+       { .compatible = "renesas,intc-irqpin-r8a7778",
+         .data = &intc_irqpin_irlm_r8a777x },
        { .compatible = "renesas,intc-irqpin-r8a7779",
-         .data = &intc_irqpin_irlm_r8a7779 },
+         .data = &intc_irqpin_irlm_r8a777x },
        {},
 };
 MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids);
index 35bf97ba4a3d196db74ff5835dc9c59fb056c2af..52304b139aa46a60966d03922197b060f4693cf1 100644 (file)
@@ -62,33 +62,20 @@ struct irqc_priv {
        struct irqc_irq irq[IRQC_IRQ_MAX];
        unsigned int number_of_irqs;
        struct platform_device *pdev;
-       struct irq_chip irq_chip;
+       struct irq_chip_generic *gc;
        struct irq_domain *irq_domain;
        struct clk *clk;
 };
 
-static void irqc_dbg(struct irqc_irq *i, char *str)
-{
-       dev_dbg(&i->p->pdev->dev, "%s (%d:%d)\n",
-               str, i->requested_irq, i->hw_irq);
-}
-
-static void irqc_irq_enable(struct irq_data *d)
+static struct irqc_priv *irq_data_to_priv(struct irq_data *data)
 {
-       struct irqc_priv *p = irq_data_get_irq_chip_data(d);
-       int hw_irq = irqd_to_hwirq(d);
-
-       irqc_dbg(&p->irq[hw_irq], "enable");
-       iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET);
+       return data->domain->host_data;
 }
 
-static void irqc_irq_disable(struct irq_data *d)
+static void irqc_dbg(struct irqc_irq *i, char *str)
 {
-       struct irqc_priv *p = irq_data_get_irq_chip_data(d);
-       int hw_irq = irqd_to_hwirq(d);
-
-       irqc_dbg(&p->irq[hw_irq], "disable");
-       iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS);
+       dev_dbg(&i->p->pdev->dev, "%s (%d:%d)\n",
+               str, i->requested_irq, i->hw_irq);
 }
 
 static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = {
@@ -101,7 +88,7 @@ static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = {
 
 static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
 {
-       struct irqc_priv *p = irq_data_get_irq_chip_data(d);
+       struct irqc_priv *p = irq_data_to_priv(d);
        int hw_irq = irqd_to_hwirq(d);
        unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK];
        u32 tmp;
@@ -120,7 +107,7 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
 
 static int irqc_irq_set_wake(struct irq_data *d, unsigned int on)
 {
-       struct irqc_priv *p = irq_data_get_irq_chip_data(d);
+       struct irqc_priv *p = irq_data_to_priv(d);
        int hw_irq = irqd_to_hwirq(d);
 
        irq_set_irq_wake(p->irq[hw_irq].requested_irq, on);
@@ -153,35 +140,11 @@ static irqreturn_t irqc_irq_handler(int irq, void *dev_id)
        return IRQ_NONE;
 }
 
-/*
- * This lock class tells lockdep that IRQC irqs are in a different
- * category than their parents, so it won't report false recursion.
- */
-static struct lock_class_key irqc_irq_lock_class;
-
-static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq,
-                              irq_hw_number_t hw)
-{
-       struct irqc_priv *p = h->host_data;
-
-       irqc_dbg(&p->irq[hw], "map");
-       irq_set_chip_data(virq, h->host_data);
-       irq_set_lockdep_class(virq, &irqc_irq_lock_class);
-       irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
-       return 0;
-}
-
-static const struct irq_domain_ops irqc_irq_domain_ops = {
-       .map    = irqc_irq_domain_map,
-       .xlate  = irq_domain_xlate_twocell,
-};
-
 static int irqc_probe(struct platform_device *pdev)
 {
        struct irqc_priv *p;
        struct resource *io;
        struct resource *irq;
-       struct irq_chip *irq_chip;
        const char *name = dev_name(&pdev->dev);
        int ret;
        int k;
@@ -241,40 +204,51 @@ static int irqc_probe(struct platform_device *pdev)
 
        p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */
 
-       irq_chip = &p->irq_chip;
-       irq_chip->name = name;
-       irq_chip->irq_mask = irqc_irq_disable;
-       irq_chip->irq_unmask = irqc_irq_enable;
-       irq_chip->irq_set_type = irqc_irq_set_type;
-       irq_chip->irq_set_wake = irqc_irq_set_wake;
-       irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND;
-
        p->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
                                              p->number_of_irqs,
-                                             &irqc_irq_domain_ops, p);
+                                             &irq_generic_chip_ops, p);
        if (!p->irq_domain) {
                ret = -ENXIO;
                dev_err(&pdev->dev, "cannot initialize irq domain\n");
                goto err2;
        }
 
+       ret = irq_alloc_domain_generic_chips(p->irq_domain, p->number_of_irqs,
+                                            1, name, handle_level_irq,
+                                            0, 0, IRQ_GC_INIT_NESTED_LOCK);
+       if (ret) {
+               dev_err(&pdev->dev, "cannot allocate generic chip\n");
+               goto err3;
+       }
+
+       p->gc = irq_get_domain_generic_chip(p->irq_domain, 0);
+       p->gc->reg_base = p->cpu_int_base;
+       p->gc->chip_types[0].regs.enable = IRQC_EN_SET;
+       p->gc->chip_types[0].regs.disable = IRQC_EN_STS;
+       p->gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
+       p->gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
+       p->gc->chip_types[0].chip.irq_set_type  = irqc_irq_set_type;
+       p->gc->chip_types[0].chip.irq_set_wake  = irqc_irq_set_wake;
+       p->gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
+
        /* request interrupts one by one */
        for (k = 0; k < p->number_of_irqs; k++) {
                if (request_irq(p->irq[k].requested_irq, irqc_irq_handler,
                                0, name, &p->irq[k])) {
                        dev_err(&pdev->dev, "failed to request IRQ\n");
                        ret = -ENOENT;
-                       goto err3;
+                       goto err4;
                }
        }
 
        dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
 
        return 0;
-err3:
+err4:
        while (--k >= 0)
                free_irq(p->irq[k].requested_irq, &p->irq[k]);
 
+err3:
        irq_domain_remove(p->irq_domain);
 err2:
        iounmap(p->iomem);
index 7154b011ddd2f65a65f7079bfe88783c50d0d373..c71914e8f596c3700d8c5a4da2448b32acf8c41f 100644 (file)
@@ -311,7 +311,7 @@ static void s3c_irq_demux(struct irq_desc *desc)
         * and one big domain for the dt case where the subintc
         * starts at hwirq number 32.
         */
-       offset = (intc->domain->of_node) ? 32 : 0;
+       offset = irq_domain_get_of_node(intc->domain) ? 32 : 0;
 
        chained_irq_enter(chip, desc);
 
@@ -342,7 +342,7 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc,
                return false;
 
        /* non-dt machines use individual domains */
-       if (!intc->domain->of_node)
+       if (!irq_domain_get_of_node(intc->domain))
                intc_offset = 0;
 
        /* We have a problem that the INTOFFSET register does not always
index c143dd58410c64cc97d5afcc5442a45d5b60ebf9..4ef178078e5bd7ddf388e215eddc1bde3151b0fb 100644 (file)
@@ -8,6 +8,9 @@
  * warranty of any kind, whether express or implied.
  */
 
+#define DRV_NAME       "sunxi-nmi"
+#define pr_fmt(fmt)    DRV_NAME ": " fmt
+
 #include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/io.h>
@@ -96,8 +99,8 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type)
                break;
        default:
                irq_gc_unlock(gc);
-               pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n",
-                       __func__, data->irq);
+               pr_err("Cannot assign multiple trigger modes to IRQ %d.\n",
+                       data->irq);
                return -EBADR;
        }
 
@@ -130,30 +133,29 @@ static int __init sunxi_sc_nmi_irq_init(struct device_node *node,
 
        domain = irq_domain_add_linear(node, 1, &irq_generic_chip_ops, NULL);
        if (!domain) {
-               pr_err("%s: Could not register interrupt domain.\n", node->name);
+               pr_err("Could not register interrupt domain.\n");
                return -ENOMEM;
        }
 
-       ret = irq_alloc_domain_generic_chips(domain, 1, 2, node->name,
+       ret = irq_alloc_domain_generic_chips(domain, 1, 2, DRV_NAME,
                                             handle_fasteoi_irq, clr, 0,
                                             IRQ_GC_INIT_MASK_CACHE);
        if (ret) {
-                pr_err("%s: Could not allocate generic interrupt chip.\n",
-                        node->name);
-                goto fail_irqd_remove;
+               pr_err("Could not allocate generic interrupt chip.\n");
+               goto fail_irqd_remove;
        }
 
        irq = irq_of_parse_and_map(node, 0);
        if (irq <= 0) {
-               pr_err("%s: unable to parse irq\n", node->name);
+               pr_err("unable to parse irq\n");
                ret = -EINVAL;
                goto fail_irqd_remove;
        }
 
        gc = irq_get_domain_generic_chip(domain, 0);
-       gc->reg_base = of_iomap(node, 0);
+       gc->reg_base = of_io_request_and_map(node, 0, of_node_full_name(node));
        if (!gc->reg_base) {
-               pr_err("%s: unable to map resource\n", node->name);
+               pr_err("unable to map resource\n");
                ret = -ENOMEM;
                goto fail_irqd_remove;
        }
index 2fd89eb88f3a29bc716ec1daf76ad4085e6a743d..121ec301372e69cbeca0bff90f41fc0653f0012a 100644 (file)
@@ -214,47 +214,50 @@ static struct irq_chip tegra_ictlr_chip = {
        .irq_unmask             = tegra_unmask,
        .irq_retrigger          = tegra_retrigger,
        .irq_set_wake           = tegra_set_wake,
+       .irq_set_type           = irq_chip_set_type_parent,
        .flags                  = IRQCHIP_MASK_ON_SUSPEND,
 #ifdef CONFIG_SMP
        .irq_set_affinity       = irq_chip_set_affinity_parent,
 #endif
 };
 
-static int tegra_ictlr_domain_xlate(struct irq_domain *domain,
-                                   struct device_node *controller,
-                                   const u32 *intspec,
-                                   unsigned int intsize,
-                                   unsigned long *out_hwirq,
-                                   unsigned int *out_type)
+static int tegra_ictlr_domain_translate(struct irq_domain *d,
+                                       struct irq_fwspec *fwspec,
+                                       unsigned long *hwirq,
+                                       unsigned int *type)
 {
-       if (domain->of_node != controller)
-               return -EINVAL; /* Shouldn't happen, really... */
-       if (intsize != 3)
-               return -EINVAL; /* Not GIC compliant */
-       if (intspec[0] != GIC_SPI)
-               return -EINVAL; /* No PPI should point to this domain */
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
+
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static int tegra_ictlr_domain_alloc(struct irq_domain *domain,
                                    unsigned int virq,
                                    unsigned int nr_irqs, void *data)
 {
-       struct of_phandle_args *args = data;
-       struct of_phandle_args parent_args;
+       struct irq_fwspec *fwspec = data;
+       struct irq_fwspec parent_fwspec;
        struct tegra_ictlr_info *info = domain->host_data;
        irq_hw_number_t hwirq;
        unsigned int i;
 
-       if (args->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL; /* Not GIC compliant */
-       if (args->args[0] != GIC_SPI)
+       if (fwspec->param[0] != GIC_SPI)
                return -EINVAL; /* No PPI should point to this domain */
 
-       hwirq = args->args[1];
+       hwirq = fwspec->param[1];
        if (hwirq >= (num_ictlrs * 32))
                return -EINVAL;
 
@@ -266,9 +269,10 @@ static int tegra_ictlr_domain_alloc(struct irq_domain *domain,
                                              info->base[ictlr]);
        }
 
-       parent_args = *args;
-       parent_args.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args);
+       parent_fwspec = *fwspec;
+       parent_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+                                           &parent_fwspec);
 }
 
 static void tegra_ictlr_domain_free(struct irq_domain *domain,
@@ -284,9 +288,9 @@ static void tegra_ictlr_domain_free(struct irq_domain *domain,
 }
 
 static const struct irq_domain_ops tegra_ictlr_domain_ops = {
-       .xlate  = tegra_ictlr_domain_xlate,
-       .alloc  = tegra_ictlr_domain_alloc,
-       .free   = tegra_ictlr_domain_free,
+       .translate      = tegra_ictlr_domain_translate,
+       .alloc          = tegra_ictlr_domain_alloc,
+       .free           = tegra_ictlr_domain_free,
 };
 
 static int __init tegra_ictlr_init(struct device_node *node,
index 2c22558864012efe6425ab1f058ea0e07a9f0728..56b5e3cb9de2c4f2c050fcad42b9f91419e92b81 100644 (file)
@@ -130,35 +130,51 @@ static int vf610_mscm_ir_domain_alloc(struct irq_domain *domain, unsigned int vi
 {
        int i;
        irq_hw_number_t hwirq;
-       struct of_phandle_args *irq_data = arg;
-       struct of_phandle_args gic_data;
+       struct irq_fwspec *fwspec = arg;
+       struct irq_fwspec parent_fwspec;
 
-       if (irq_data->args_count != 2)
+       if (!irq_domain_get_of_node(domain->parent))
                return -EINVAL;
 
-       hwirq = irq_data->args[0];
+       if (fwspec->param_count != 2)
+               return -EINVAL;
+
+       hwirq = fwspec->param[0];
        for (i = 0; i < nr_irqs; i++)
                irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
                                              &vf610_mscm_ir_irq_chip,
                                              domain->host_data);
 
-       gic_data.np = domain->parent->of_node;
+       parent_fwspec.fwnode = domain->parent->fwnode;
 
        if (mscm_ir_data->is_nvic) {
-               gic_data.args_count = 1;
-               gic_data.args[0] = irq_data->args[0];
+               parent_fwspec.param_count = 1;
+               parent_fwspec.param[0] = fwspec->param[0];
        } else {
-               gic_data.args_count = 3;
-               gic_data.args[0] = GIC_SPI;
-               gic_data.args[1] = irq_data->args[0];
-               gic_data.args[2] = irq_data->args[1];
+               parent_fwspec.param_count = 3;
+               parent_fwspec.param[0] = GIC_SPI;
+               parent_fwspec.param[1] = fwspec->param[0];
+               parent_fwspec.param[2] = fwspec->param[1];
        }
 
-       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
+       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+                                           &parent_fwspec);
+}
+
+static int vf610_mscm_ir_domain_translate(struct irq_domain *d,
+                                         struct irq_fwspec *fwspec,
+                                         unsigned long *hwirq,
+                                         unsigned int *type)
+{
+       if (WARN_ON(fwspec->param_count < 2))
+               return -EINVAL;
+       *hwirq = fwspec->param[0];
+       *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+       return 0;
 }
 
 static const struct irq_domain_ops mscm_irq_domain_ops = {
-       .xlate = irq_domain_xlate_twocell,
+       .translate = vf610_mscm_ir_domain_translate,
        .alloc = vf610_mscm_ir_domain_alloc,
        .free = irq_domain_free_irqs_common,
 };
@@ -205,7 +221,8 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node,
                goto out_unmap;
        }
 
-       if (of_device_is_compatible(domain->parent->of_node, "arm,armv7m-nvic"))
+       if (of_device_is_compatible(irq_domain_get_of_node(domain->parent),
+                                   "arm,armv7m-nvic"))
                mscm_ir_data->is_nvic = true;
 
        cpu_pm_register_notifier(&mscm_ir_notifier_block);
index 0e5d673871c05b30bc906e7131ead462eb86c667..9600cd771f1a3a234a3b7c647904f3dbd22385c7 100644 (file)
@@ -646,14 +646,14 @@ rx_d_frame(struct hfc4s8s_l1 *l1p, int ech)
 
                f1 = Read_hfc8_stable(l1p->hw, A_F1);
                f2 = Read_hfc8(l1p->hw, A_F2);
-               df = f1 - f2;
-               if ((f1 - f2) < 0)
-                       df = f1 - f2 + MAX_F_CNT + 1;
 
+               if (f1 < f2)
+                       df = MAX_F_CNT + 1 + f1 - f2;
+               else
+                       df = f1 - f2;
 
-               if (!df) {
+               if (!df)
                        return; /* no complete frame in fifo */
-               }
 
                z1 = Read_hfc16_stable(l1p->hw, A_Z1);
                z2 = Read_hfc16(l1p->hw, A_Z2);
index 18accb0a79cc51dea2d1851fc9a27891e8d79d6f..c53a53f6efb6a136ec09075996bd89081e50e536 100644 (file)
@@ -1247,7 +1247,7 @@ static void
 l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
 {
        struct PStack *st = fi->userdata;
-       struct sk_buff *skb;
+       struct sk_buff *skb, *nskb;
        struct Layer2 *l2 = &st->l2;
        u_char header[MAX_HEADER_LEN];
        int i, hdr_space_needed;
@@ -1262,14 +1262,10 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
                return;
 
        hdr_space_needed = l2headersize(l2, 0);
-       if (hdr_space_needed > skb_headroom(skb)) {
-               struct sk_buff *orig_skb = skb;
-
-               skb = skb_realloc_headroom(skb, hdr_space_needed);
-               if (!skb) {
-                       dev_kfree_skb(orig_skb);
-                       return;
-               }
+       nskb = skb_realloc_headroom(skb, hdr_space_needed);
+       if (!nskb) {
+               skb_queue_head(&l2->i_queue, skb);
+               return;
        }
        spin_lock_irqsave(&l2->lock, flags);
        if (test_bit(FLG_MOD128, &l2->flag))
@@ -1282,7 +1278,7 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
                       p1);
                dev_kfree_skb(l2->windowar[p1]);
        }
-       l2->windowar[p1] = skb_clone(skb, GFP_ATOMIC);
+       l2->windowar[p1] = skb;
 
        i = sethdraddr(&st->l2, header, CMD);
 
@@ -1295,8 +1291,8 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
                l2->vs = (l2->vs + 1) % 8;
        }
        spin_unlock_irqrestore(&l2->lock, flags);
-       memcpy(skb_push(skb, i), header, i);
-       st->l2.l2l1(st, PH_PULL | INDICATION, skb);
+       memcpy(skb_push(nskb, i), header, i);
+       st->l2.l2l1(st, PH_PULL | INDICATION, nskb);
        test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
        if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) {
                FsmDelTimer(&st->l2.t203, 13);
index c4198fa490bfac50dd3c05fa324502d4e6f228c8..9c1e8adaf4fc825c54ff84e9c85d36a68ecb5da7 100644 (file)
@@ -301,6 +301,8 @@ isdn_ppp_open(int min, struct file *file)
        is->compflags = 0;
 
        is->reset = isdn_ppp_ccp_reset_alloc(is);
+       if (!is->reset)
+               return -ENOMEM;
 
        is->lp = NULL;
        is->mp_seqno = 0;       /* MP sequence number */
@@ -320,6 +322,10 @@ isdn_ppp_open(int min, struct file *file)
         * VJ header compression init
         */
        is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */
+       if (IS_ERR(is->slcomp)) {
+               isdn_ppp_ccp_reset_free(is);
+               return PTR_ERR(is->slcomp);
+       }
 #endif
 #ifdef CONFIG_IPPP_FILTER
        is->pass_filter = NULL;
@@ -567,10 +573,8 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
                        is->maxcid = val;
 #ifdef CONFIG_ISDN_PPP_VJ
                        sltmp = slhc_init(16, val);
-                       if (!sltmp) {
-                               printk(KERN_ERR "ippp, can't realloc slhc struct\n");
-                               return -ENOMEM;
-                       }
+                       if (IS_ERR(sltmp))
+                               return PTR_ERR(sltmp);
                        if (is->slcomp)
                                slhc_free(is->slcomp);
                        is->slcomp = sltmp;
index 8b1a66c6ca8aa0e3c5a647945ba895172d3a4974..e72b4e73cd615f1fa0410b97661d7f07ef9bbaca 100644 (file)
@@ -235,7 +235,7 @@ void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
 
 int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
 {
-       int len, incomplete = 0, found = 0;
+       int incomplete = 0, found = 0;
        char *dup, *tok, *name, *args;
        struct dsp_element_entry *entry, *n;
        struct dsp_pipeline_entry *pipeline_entry;
@@ -247,17 +247,9 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
        if (!list_empty(&pipeline->list))
                _dsp_pipeline_destroy(pipeline);
 
-       if (!cfg)
-               return 0;
-
-       len = strlen(cfg);
-       if (!len)
-               return 0;
-
-       dup = kmalloc(len + 1, GFP_ATOMIC);
+       dup = kstrdup(cfg, GFP_ATOMIC);
        if (!dup)
                return 0;
-       strcpy(dup, cfg);
        while ((tok = strsep(&dup, "|"))) {
                if (!strlen(tok))
                        continue;
index 949cabb88f1c113c9606d26ff72627f6cf9db108..5eb380a2590394ba087e2f160281e1902112910f 100644 (file)
@@ -1476,7 +1476,7 @@ static void
 l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
 {
        struct layer2   *l2 = fi->userdata;
-       struct sk_buff  *skb, *nskb, *oskb;
+       struct sk_buff  *skb, *nskb;
        u_char          header[MAX_L2HEADER_LEN];
        u_int           i, p1;
 
@@ -1486,48 +1486,34 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
        skb = skb_dequeue(&l2->i_queue);
        if (!skb)
                return;
-
-       if (test_bit(FLG_MOD128, &l2->flag))
-               p1 = (l2->vs - l2->va) % 128;
-       else
-               p1 = (l2->vs - l2->va) % 8;
-       p1 = (p1 + l2->sow) % l2->window;
-       if (l2->windowar[p1]) {
-               printk(KERN_WARNING "%s: l2 try overwrite ack queue entry %d\n",
-                      mISDNDevName4ch(&l2->ch), p1);
-               dev_kfree_skb(l2->windowar[p1]);
-       }
-       l2->windowar[p1] = skb;
        i = sethdraddr(l2, header, CMD);
        if (test_bit(FLG_MOD128, &l2->flag)) {
                header[i++] = l2->vs << 1;
                header[i++] = l2->vr << 1;
+       } else
+               header[i++] = (l2->vr << 5) | (l2->vs << 1);
+       nskb = skb_realloc_headroom(skb, i);
+       if (!nskb) {
+               printk(KERN_WARNING "%s: no headroom(%d) copy for IFrame\n",
+                      mISDNDevName4ch(&l2->ch), i);
+               skb_queue_head(&l2->i_queue, skb);
+               return;
+       }
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               p1 = (l2->vs - l2->va) % 128;
                l2->vs = (l2->vs + 1) % 128;
        } else {
-               header[i++] = (l2->vr << 5) | (l2->vs << 1);
+               p1 = (l2->vs - l2->va) % 8;
                l2->vs = (l2->vs + 1) % 8;
        }
-
-       nskb = skb_clone(skb, GFP_ATOMIC);
-       p1 = skb_headroom(nskb);
-       if (p1 >= i)
-               memcpy(skb_push(nskb, i), header, i);
-       else {
-               printk(KERN_WARNING
-                      "%s: L2 pull_iqueue skb header(%d/%d) too short\n",
-                      mISDNDevName4ch(&l2->ch), i, p1);
-               oskb = nskb;
-               nskb = mI_alloc_skb(oskb->len + i, GFP_ATOMIC);
-               if (!nskb) {
-                       dev_kfree_skb(oskb);
-                       printk(KERN_WARNING "%s: no skb mem in %s\n",
-                              mISDNDevName4ch(&l2->ch), __func__);
-                       return;
-               }
-               memcpy(skb_put(nskb, i), header, i);
-               memcpy(skb_put(nskb, oskb->len), oskb->data, oskb->len);
-               dev_kfree_skb(oskb);
+       p1 = (p1 + l2->sow) % l2->window;
+       if (l2->windowar[p1]) {
+               printk(KERN_WARNING "%s: l2 try overwrite ack queue entry %d\n",
+                      mISDNDevName4ch(&l2->ch), p1);
+               dev_kfree_skb(l2->windowar[p1]);
        }
+       l2->windowar[p1] = skb;
+       memcpy(skb_push(nskb, i), header, i);
        l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb);
        test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
        if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) {
index 42990f2d03178885d6021bce5ef3a9315d70f447..b1ab8bdf82519b9da91c082198cf7acef6f61692 100644 (file)
@@ -556,6 +556,16 @@ config LEDS_KTD2692
 
          Say Y to enable this driver.
 
+config LEDS_SEAD3
+       tristate "LED support for the MIPS SEAD 3 board"
+       depends on LEDS_CLASS && MIPS_SEAD3
+       help
+         Say Y here to include support for the FLED and PLED LEDs on SEAD3 eval
+         boards.
+
+         This driver can also be built as a module. If so the module
+         will be called leds-sead3.
+
 comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
 
 config LEDS_BLINKM
index b503f92dc2c4a3990d5d435b62dab3120973e97f..e9d53092765d8ca8ae43e970dc63ec1c23e96b78 100644 (file)
@@ -65,6 +65,7 @@ obj-$(CONFIG_LEDS_VERSATILE)          += leds-versatile.o
 obj-$(CONFIG_LEDS_MENF21BMC)           += leds-menf21bmc.o
 obj-$(CONFIG_LEDS_KTD2692)             += leds-ktd2692.o
 obj-$(CONFIG_LEDS_POWERNV)             += leds-powernv.o
+obj-$(CONFIG_LEDS_SEAD3)               += leds-sead3.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)          += leds-dac124s085.o
index ca51d58bed244c488d815003c366d03acf79e418..7385f98dd54b4b42cf7e430b94952150b979827b 100644 (file)
@@ -102,70 +102,6 @@ static const struct attribute_group *led_groups[] = {
        NULL,
 };
 
-static void led_timer_function(unsigned long data)
-{
-       struct led_classdev *led_cdev = (void *)data;
-       unsigned long brightness;
-       unsigned long delay;
-
-       if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
-               led_set_brightness_async(led_cdev, LED_OFF);
-               return;
-       }
-
-       if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) {
-               led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
-               return;
-       }
-
-       brightness = led_get_brightness(led_cdev);
-       if (!brightness) {
-               /* Time to switch the LED on. */
-               if (led_cdev->delayed_set_value) {
-                       led_cdev->blink_brightness =
-                                       led_cdev->delayed_set_value;
-                       led_cdev->delayed_set_value = 0;
-               }
-               brightness = led_cdev->blink_brightness;
-               delay = led_cdev->blink_delay_on;
-       } else {
-               /* Store the current brightness value to be able
-                * to restore it when the delay_off period is over.
-                */
-               led_cdev->blink_brightness = brightness;
-               brightness = LED_OFF;
-               delay = led_cdev->blink_delay_off;
-       }
-
-       led_set_brightness_async(led_cdev, brightness);
-
-       /* Return in next iteration if led is in one-shot mode and we are in
-        * the final blink state so that the led is toggled each delay_on +
-        * delay_off milliseconds in worst case.
-        */
-       if (led_cdev->flags & LED_BLINK_ONESHOT) {
-               if (led_cdev->flags & LED_BLINK_INVERT) {
-                       if (brightness)
-                               led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
-               } else {
-                       if (!brightness)
-                               led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
-               }
-       }
-
-       mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
-}
-
-static void set_brightness_delayed(struct work_struct *ws)
-{
-       struct led_classdev *led_cdev =
-               container_of(ws, struct led_classdev, set_brightness_work);
-
-       led_stop_software_blink(led_cdev);
-
-       led_set_brightness_async(led_cdev, led_cdev->delayed_set_value);
-}
-
 /**
  * led_classdev_suspend - suspend an led_classdev.
  * @led_cdev: the led_classdev to suspend.
@@ -283,10 +219,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
 
        led_update_brightness(led_cdev);
 
-       INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);
-
-       setup_timer(&led_cdev->blink_timer, led_timer_function,
-                   (unsigned long)led_cdev);
+       led_init_core(led_cdev);
 
 #ifdef CONFIG_LEDS_TRIGGERS
        led_trigger_set_default(led_cdev);
index 549de7e24cfdf445f27180ab7c776da21af50a94..c1c3af089634cded4f27fd8e6397a9519025ad70 100644 (file)
@@ -25,6 +25,70 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
 LIST_HEAD(leds_list);
 EXPORT_SYMBOL_GPL(leds_list);
 
+static void led_timer_function(unsigned long data)
+{
+       struct led_classdev *led_cdev = (void *)data;
+       unsigned long brightness;
+       unsigned long delay;
+
+       if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
+               led_set_brightness_async(led_cdev, LED_OFF);
+               return;
+       }
+
+       if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) {
+               led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
+               return;
+       }
+
+       brightness = led_get_brightness(led_cdev);
+       if (!brightness) {
+               /* Time to switch the LED on. */
+               if (led_cdev->delayed_set_value) {
+                       led_cdev->blink_brightness =
+                                       led_cdev->delayed_set_value;
+                       led_cdev->delayed_set_value = 0;
+               }
+               brightness = led_cdev->blink_brightness;
+               delay = led_cdev->blink_delay_on;
+       } else {
+               /* Store the current brightness value to be able
+                * to restore it when the delay_off period is over.
+                */
+               led_cdev->blink_brightness = brightness;
+               brightness = LED_OFF;
+               delay = led_cdev->blink_delay_off;
+       }
+
+       led_set_brightness_async(led_cdev, brightness);
+
+       /* Return in next iteration if led is in one-shot mode and we are in
+        * the final blink state so that the led is toggled each delay_on +
+        * delay_off milliseconds in worst case.
+        */
+       if (led_cdev->flags & LED_BLINK_ONESHOT) {
+               if (led_cdev->flags & LED_BLINK_INVERT) {
+                       if (brightness)
+                               led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
+               } else {
+                       if (!brightness)
+                               led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
+               }
+       }
+
+       mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
+}
+
+static void set_brightness_delayed(struct work_struct *ws)
+{
+       struct led_classdev *led_cdev =
+               container_of(ws, struct led_classdev, set_brightness_work);
+
+       led_stop_software_blink(led_cdev);
+
+       led_set_brightness_async(led_cdev, led_cdev->delayed_set_value);
+}
+
 static void led_set_software_blink(struct led_classdev *led_cdev,
                                   unsigned long delay_on,
                                   unsigned long delay_off)
@@ -72,6 +136,15 @@ static void led_blink_setup(struct led_classdev *led_cdev,
        led_set_software_blink(led_cdev, *delay_on, *delay_off);
 }
 
+void led_init_core(struct led_classdev *led_cdev)
+{
+       INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);
+
+       setup_timer(&led_cdev->blink_timer, led_timer_function,
+                   (unsigned long)led_cdev);
+}
+EXPORT_SYMBOL_GPL(led_init_core);
+
 void led_blink_set(struct led_classdev *led_cdev,
                   unsigned long *delay_on,
                   unsigned long *delay_off)
index 1497a09166d6e44fb7e76edf002c0578a10aabf5..7870840e7cc9a43543ab72262082bce05e150c11 100644 (file)
@@ -142,6 +142,7 @@ static int pm860x_led_dt_init(struct platform_device *pdev,
                        of_property_read_u32(np, "marvell,88pm860x-iset",
                                             &iset);
                        data->iset = PM8606_LED_CURRENT(iset);
+                       of_node_put(np);
                        break;
                }
        }
index 1793727bc9ae5f6c64467a68b4646b92e0963e1b..c7ea5c62633186294eff8046dba51c8865d06eab 100644 (file)
 #define BCM6328_SERIAL_LED_SHIFT_DIR   BIT(16)
 #define BCM6328_LED_SHIFT_TEST         BIT(30)
 #define BCM6328_LED_TEST               BIT(31)
+#define BCM6328_INIT_MASK              (BCM6328_SERIAL_LED_EN | \
+                                        BCM6328_SERIAL_LED_MUX  | \
+                                        BCM6328_SERIAL_LED_CLK_NPOL | \
+                                        BCM6328_SERIAL_LED_DATA_PPOL | \
+                                        BCM6328_SERIAL_LED_SHIFT_DIR)
 
 #define BCM6328_LED_MODE_MASK          3
 #define BCM6328_LED_MODE_OFF           0
@@ -281,11 +286,10 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
                                                    "linux,default-trigger",
                                                    NULL);
 
+       spin_lock_irqsave(lock, flags);
        if (!of_property_read_string(nc, "default-state", &state)) {
-               spin_lock_irqsave(lock, flags);
                if (!strcmp(state, "on")) {
                        led->cdev.brightness = LED_FULL;
-                       bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
                } else if (!strcmp(state, "keep")) {
                        void __iomem *mode;
                        unsigned long val, shift;
@@ -296,21 +300,28 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
                        else
                                mode = mem + BCM6328_REG_MODE_LO;
 
-                       val = bcm6328_led_read(mode) >> (shift % 16);
+                       val = bcm6328_led_read(mode) >>
+                             BCM6328_LED_SHIFT(shift % 16);
                        val &= BCM6328_LED_MODE_MASK;
-                       if (val == BCM6328_LED_MODE_ON)
+                       if ((led->active_low && val == BCM6328_LED_MODE_ON) ||
+                           (!led->active_low && val == BCM6328_LED_MODE_OFF))
                                led->cdev.brightness = LED_FULL;
-                       else {
+                       else
                                led->cdev.brightness = LED_OFF;
-                               bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
-                       }
                } else {
                        led->cdev.brightness = LED_OFF;
-                       bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
                }
-               spin_unlock_irqrestore(lock, flags);
+       } else {
+               led->cdev.brightness = LED_OFF;
        }
 
+       if ((led->active_low && led->cdev.brightness == LED_FULL) ||
+           (!led->active_low && led->cdev.brightness == LED_OFF))
+               bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
+       else
+               bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
+       spin_unlock_irqrestore(lock, flags);
+
        led->cdev.brightness_set = bcm6328_led_set;
        led->cdev.blink_set = bcm6328_blink_set;
 
@@ -360,9 +371,17 @@ static int bcm6328_leds_probe(struct platform_device *pdev)
        bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_LO, 0);
 
        val = bcm6328_led_read(mem + BCM6328_REG_INIT);
-       val &= ~BCM6328_SERIAL_LED_EN;
+       val &= ~(BCM6328_INIT_MASK);
        if (of_property_read_bool(np, "brcm,serial-leds"))
                val |= BCM6328_SERIAL_LED_EN;
+       if (of_property_read_bool(np, "brcm,serial-mux"))
+               val |= BCM6328_SERIAL_LED_MUX;
+       if (of_property_read_bool(np, "brcm,serial-clk-low"))
+               val |= BCM6328_SERIAL_LED_CLK_NPOL;
+       if (!of_property_read_bool(np, "brcm,serial-dat-low"))
+               val |= BCM6328_SERIAL_LED_DATA_PPOL;
+       if (!of_property_read_bool(np, "brcm,serial-shift-inv"))
+               val |= BCM6328_SERIAL_LED_SHIFT_DIR;
        bcm6328_led_write(mem + BCM6328_REG_INIT, val);
 
        for_each_available_child_of_node(np, child) {
@@ -373,7 +392,7 @@ static int bcm6328_leds_probe(struct platform_device *pdev)
                        continue;
 
                if (reg >= BCM6328_LED_MAX_COUNT) {
-                       dev_err(dev, "invalid LED (>= %d)\n",
+                       dev_err(dev, "invalid LED (%u >= %d)\n", reg,
                                BCM6328_LED_MAX_COUNT);
                        continue;
                }
@@ -384,8 +403,10 @@ static int bcm6328_leds_probe(struct platform_device *pdev)
                        rc = bcm6328_led(dev, child, reg, mem, lock,
                                         blink_leds, blink_delay);
 
-               if (rc < 0)
+               if (rc < 0) {
+                       of_node_put(child);
                        return rc;
+               }
        }
 
        return 0;
index 7ea3526702e0c56b2a841e0bf2d2dccbe3f606f6..82b4ee1bc87e03e3df18fe5e044270c0996b18a5 100644 (file)
@@ -215,8 +215,10 @@ static int bcm6358_leds_probe(struct platform_device *pdev)
                }
 
                rc = bcm6358_led(dev, child, reg, mem, lock);
-               if (rc < 0)
+               if (rc < 0) {
+                       of_node_put(child);
                        return rc;
+               }
        }
 
        return 0;
index d975220804916c9f35c734b9b4db212e36389f0a..9be195707b392d0c9dd8ca0da5f4633553342de0 100644 (file)
@@ -36,7 +36,6 @@ static struct led_classdev qube_front_led = {
 static int cobalt_qube_led_probe(struct platform_device *pdev)
 {
        struct resource *res;
-       int retval;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res)
@@ -49,31 +48,11 @@ static int cobalt_qube_led_probe(struct platform_device *pdev)
        led_value = LED_FRONT_LEFT | LED_FRONT_RIGHT;
        writeb(led_value, led_port);
 
-       retval = led_classdev_register(&pdev->dev, &qube_front_led);
-       if (retval)
-               goto err_null;
-
-       return 0;
-
-err_null:
-       led_port = NULL;
-
-       return retval;
-}
-
-static int cobalt_qube_led_remove(struct platform_device *pdev)
-{
-       led_classdev_unregister(&qube_front_led);
-
-       if (led_port)
-               led_port = NULL;
-
-       return 0;
+       return devm_led_classdev_register(&pdev->dev, &qube_front_led);
 }
 
 static struct platform_driver cobalt_qube_led_driver = {
        .probe  = cobalt_qube_led_probe,
-       .remove = cobalt_qube_led_remove,
        .driver = {
                .name   = "cobalt-qube-leds",
        },
index af1876a3a77c883a630f1c7765864ea28d3f0a03..5db4515a4fd7aaf292958a5a7fa982bcccceb999 100644 (file)
@@ -291,9 +291,22 @@ static int gpio_led_remove(struct platform_device *pdev)
        return 0;
 }
 
+static void gpio_led_shutdown(struct platform_device *pdev)
+{
+       struct gpio_leds_priv *priv = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < priv->num_leds; i++) {
+               struct gpio_led_data *led = &priv->leds[i];
+
+               gpio_led_set(&led->cdev, LED_OFF);
+       }
+}
+
 static struct platform_driver gpio_led_driver = {
        .probe          = gpio_led_probe,
        .remove         = gpio_led_remove,
+       .shutdown       = gpio_led_shutdown,
        .driver         = {
                .name   = "leds-gpio",
                .of_match_table = of_gpio_leds_match,
index 0b84c0113126046ac5af4ca2a0f1d988b4a9b61c..a6b8db0e27f11b8ce030efabf72542e9eb361159 100644 (file)
@@ -59,28 +59,15 @@ static int hp6xxled_probe(struct platform_device *pdev)
 {
        int ret;
 
-       ret = led_classdev_register(&pdev->dev, &hp6xx_red_led);
+       ret = devm_led_classdev_register(&pdev->dev, &hp6xx_red_led);
        if (ret < 0)
                return ret;
 
-       ret = led_classdev_register(&pdev->dev, &hp6xx_green_led);
-       if (ret < 0)
-               led_classdev_unregister(&hp6xx_red_led);
-
-       return ret;
-}
-
-static int hp6xxled_remove(struct platform_device *pdev)
-{
-       led_classdev_unregister(&hp6xx_red_led);
-       led_classdev_unregister(&hp6xx_green_led);
-
-       return 0;
+       return devm_led_classdev_register(&pdev->dev, &hp6xx_green_led);
 }
 
 static struct platform_driver hp6xxled_driver = {
        .probe          = hp6xxled_probe,
-       .remove         = hp6xxled_remove,
        .driver         = {
                .name           = "hp6xx-led",
        },
index 3776f516cd88f80541affad80ce4dcb25963152c..fa262b6b25eb26dfab24fa784200ae6175996f06 100644 (file)
@@ -16,9 +16,9 @@
 #define LED_YELLOW     0x00
 #define LED_GREEN      0x01
 
-#define LED_EN          (1 << 4)        /* LED ON/OFF 0:off, 1:on                       */
-#define LED_AUTOSTOP    (1 << 5)        /* LED ON/OFF auto stop set 0:disable, 1:enable */
-#define LED_ALWAYS      (1 << 6)        /* LED Interrupt Mask 0:No mask, 1:mask         */
+#define LED_EN       (1 << 4) /* LED ON/OFF 0:off, 1:on                       */
+#define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop set 0:disable, 1:enable */
+#define LED_ALWAYS   (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask         */
 
 static void micro_leds_brightness_set(struct led_classdev *led_cdev,
                                      enum led_brightness value)
@@ -79,14 +79,14 @@ static int micro_leds_blink_set(struct led_classdev *led_cdev,
        };
 
        msg.tx_data[0] = LED_GREEN;
-        if (*delay_on > IPAQ_LED_MAX_DUTY ||
+       if (*delay_on > IPAQ_LED_MAX_DUTY ||
            *delay_off > IPAQ_LED_MAX_DUTY)
-                return -EINVAL;
+               return -EINVAL;
 
-        if (*delay_on == 0 && *delay_off == 0) {
-                *delay_on = 100;
-                *delay_off = 100;
-        }
+       if (*delay_on == 0 && *delay_off == 0) {
+               *delay_on = 100;
+               *delay_off = 100;
+       }
 
        msg.tx_data[1] = 0;
        if (*delay_on >= IPAQ_LED_MAX_DUTY)
@@ -111,7 +111,7 @@ static int micro_leds_probe(struct platform_device *pdev)
 {
        int ret;
 
-       ret = led_classdev_register(&pdev->dev, &micro_led);
+       ret = devm_led_classdev_register(&pdev->dev, &micro_led);
        if (ret) {
                dev_err(&pdev->dev, "registering led failed: %d\n", ret);
                return ret;
@@ -121,18 +121,11 @@ static int micro_leds_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int micro_leds_remove(struct platform_device *pdev)
-{
-       led_classdev_unregister(&micro_led);
-       return 0;
-}
-
 static struct platform_driver micro_leds_device_driver = {
        .driver = {
                .name    = "ipaq-micro-leds",
        },
        .probe   = micro_leds_probe,
-       .remove  = micro_leds_remove,
 };
 module_platform_driver(micro_leds_device_driver);
 
index 80ba048889d61faa76195805cd622b1dada80d02..24c4b53a6b9387095b464809809ca7e1d44c0cf4 100644 (file)
@@ -59,23 +59,13 @@ static int locomoled_probe(struct locomo_dev *ldev)
 {
        int ret;
 
-       ret = led_classdev_register(&ldev->dev, &locomo_led0);
+       ret = devm_led_classdev_register(&ldev->dev, &locomo_led0);
        if (ret < 0)
                return ret;
 
-       ret = led_classdev_register(&ldev->dev, &locomo_led1);
-       if (ret < 0)
-               led_classdev_unregister(&locomo_led0);
-
-       return ret;
+       return  devm_led_classdev_register(&ldev->dev, &locomo_led1);
 }
 
-static int locomoled_remove(struct locomo_dev *dev)
-{
-       led_classdev_unregister(&locomo_led0);
-       led_classdev_unregister(&locomo_led1);
-       return 0;
-}
 
 static struct locomo_driver locomoled_driver = {
        .drv = {
@@ -83,7 +73,6 @@ static struct locomo_driver locomoled_driver = {
        },
        .devid  = LOCOMO_DEVID_LED,
        .probe  = locomoled_probe,
-       .remove = locomoled_remove,
 };
 
 static int __init locomoled_init(void)
index 4b9eea815b1a420d0b1b97966aa04292a137c4ea..dec2a6e59676e074a9476a5c988c36f9f9c93865 100644 (file)
@@ -87,36 +87,20 @@ static int menf21bmc_led_probe(struct platform_device *pdev)
                leds[i].cdev.name = leds[i].name;
                leds[i].cdev.brightness_set = menf21bmc_led_set;
                leds[i].i2c_client = i2c_client;
-               ret = led_classdev_register(&pdev->dev, &leds[i].cdev);
-               if (ret < 0)
-                       goto err_free_leds;
+               ret = devm_led_classdev_register(&pdev->dev, &leds[i].cdev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to register LED device\n");
+                       return ret;
+               }
        }
        dev_info(&pdev->dev, "MEN 140F21P00 BMC LED device enabled\n");
 
        return 0;
 
-err_free_leds:
-       dev_err(&pdev->dev, "failed to register LED device\n");
-
-       for (i = i - 1; i >= 0; i--)
-               led_classdev_unregister(&leds[i].cdev);
-
-       return ret;
-}
-
-static int menf21bmc_led_remove(struct platform_device *pdev)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(leds); i++)
-               led_classdev_unregister(&leds[i].cdev);
-
-       return 0;
 }
 
 static struct platform_driver menf21bmc_led = {
        .probe          = menf21bmc_led_probe,
-       .remove         = menf21bmc_led_remove,
        .driver         = {
                .name           = "menf21bmc_led",
        },
index ec3a2e8adcae6840837b18fdeb336082c45331ae..0d214c2e403c2a049ca9287f265312d93390da23 100644 (file)
@@ -39,18 +39,11 @@ static struct led_classdev net48xx_error_led = {
 
 static int net48xx_led_probe(struct platform_device *pdev)
 {
-       return led_classdev_register(&pdev->dev, &net48xx_error_led);
-}
-
-static int net48xx_led_remove(struct platform_device *pdev)
-{
-       led_classdev_unregister(&net48xx_error_led);
-       return 0;
+       return devm_led_classdev_register(&pdev->dev, &net48xx_error_led);
 }
 
 static struct platform_driver net48xx_led_driver = {
        .probe          = net48xx_led_probe,
-       .remove         = net48xx_led_remove,
        .driver         = {
                .name           = DRVNAME,
        },
index 25e419752a7b7c5719baa69bd114d57720a62f92..4b88b93244be52fa711e8c329446c273a04a24fa 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/spinlock.h>
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/leds.h>
 #include <linux/platform_data/leds-kirkwood-netxbig.h>
 
@@ -70,7 +71,8 @@ static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext,
        spin_unlock_irqrestore(&gpio_ext_lock, flags);
 }
 
-static int gpio_ext_init(struct netxbig_gpio_ext *gpio_ext)
+static int gpio_ext_init(struct platform_device *pdev,
+                        struct netxbig_gpio_ext *gpio_ext)
 {
        int err;
        int i;
@@ -80,46 +82,28 @@ static int gpio_ext_init(struct netxbig_gpio_ext *gpio_ext)
 
        /* Configure address GPIOs. */
        for (i = 0; i < gpio_ext->num_addr; i++) {
-               err = gpio_request_one(gpio_ext->addr[i], GPIOF_OUT_INIT_LOW,
-                                      "GPIO extension addr");
+               err = devm_gpio_request_one(&pdev->dev, gpio_ext->addr[i],
+                                           GPIOF_OUT_INIT_LOW,
+                                           "GPIO extension addr");
                if (err)
-                       goto err_free_addr;
+                       return err;
        }
        /* Configure data GPIOs. */
        for (i = 0; i < gpio_ext->num_data; i++) {
-               err = gpio_request_one(gpio_ext->data[i], GPIOF_OUT_INIT_LOW,
-                                  "GPIO extension data");
+               err = devm_gpio_request_one(&pdev->dev, gpio_ext->data[i],
+                                           GPIOF_OUT_INIT_LOW,
+                                           "GPIO extension data");
                if (err)
-                       goto err_free_data;
+                       return err;
        }
        /* Configure "enable select" GPIO. */
-       err = gpio_request_one(gpio_ext->enable, GPIOF_OUT_INIT_LOW,
-                              "GPIO extension enable");
+       err = devm_gpio_request_one(&pdev->dev, gpio_ext->enable,
+                                   GPIOF_OUT_INIT_LOW,
+                                   "GPIO extension enable");
        if (err)
-               goto err_free_data;
+               return err;
 
        return 0;
-
-err_free_data:
-       for (i = i - 1; i >= 0; i--)
-               gpio_free(gpio_ext->data[i]);
-       i = gpio_ext->num_addr;
-err_free_addr:
-       for (i = i - 1; i >= 0; i--)
-               gpio_free(gpio_ext->addr[i]);
-
-       return err;
-}
-
-static void gpio_ext_free(struct netxbig_gpio_ext *gpio_ext)
-{
-       int i;
-
-       gpio_free(gpio_ext->enable);
-       for (i = gpio_ext->num_addr - 1; i >= 0; i--)
-               gpio_free(gpio_ext->addr[i]);
-       for (i = gpio_ext->num_data - 1; i >= 0; i--)
-               gpio_free(gpio_ext->data[i]);
 }
 
 /*
@@ -132,7 +116,6 @@ struct netxbig_led_data {
        int                     mode_addr;
        int                     *mode_val;
        int                     bright_addr;
-       int                     bright_max;
        struct                  netxbig_led_timer *timer;
        int                     num_timer;
        enum netxbig_led_mode   mode;
@@ -194,7 +177,7 @@ static void netxbig_led_set(struct led_classdev *led_cdev,
        struct netxbig_led_data *led_dat =
                container_of(led_cdev, struct netxbig_led_data, cdev);
        enum netxbig_led_mode mode;
-       int mode_val, bright_val;
+       int mode_val;
        int set_brightness = 1;
        unsigned long flags;
 
@@ -220,12 +203,9 @@ static void netxbig_led_set(struct led_classdev *led_cdev,
         * SATA LEDs. So, change the brightness setting for a single
         * SATA LED will affect all the others.
         */
-       if (set_brightness) {
-               bright_val = DIV_ROUND_UP(value * led_dat->bright_max,
-                                         LED_FULL);
+       if (set_brightness)
                gpio_ext_set_value(led_dat->gpio_ext,
-                                  led_dat->bright_addr, bright_val);
-       }
+                                  led_dat->bright_addr, value);
 
        spin_unlock_irqrestore(&led_dat->lock, flags);
 }
@@ -299,18 +279,11 @@ static struct attribute *netxbig_led_attrs[] = {
 };
 ATTRIBUTE_GROUPS(netxbig_led);
 
-static void delete_netxbig_led(struct netxbig_led_data *led_dat)
+static int create_netxbig_led(struct platform_device *pdev,
+                             struct netxbig_led_platform_data *pdata,
+                             struct netxbig_led_data *led_dat,
+                             const struct netxbig_led *template)
 {
-       led_classdev_unregister(&led_dat->cdev);
-}
-
-static int
-create_netxbig_led(struct platform_device *pdev,
-                  struct netxbig_led_data *led_dat,
-                  const struct netxbig_led *template)
-{
-       struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
-
        spin_lock_init(&led_dat->lock);
        led_dat->gpio_ext = pdata->gpio_ext;
        led_dat->cdev.name = template->name;
@@ -329,11 +302,11 @@ create_netxbig_led(struct platform_device *pdev,
         */
        led_dat->sata = 0;
        led_dat->cdev.brightness = LED_OFF;
+       led_dat->cdev.max_brightness = template->bright_max;
        led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
        led_dat->mode_addr = template->mode_addr;
        led_dat->mode_val = template->mode_val;
        led_dat->bright_addr = template->bright_addr;
-       led_dat->bright_max = (1 << pdata->gpio_ext->num_data) - 1;
        led_dat->timer = pdata->timer;
        led_dat->num_timer = pdata->num_timer;
        /*
@@ -343,67 +316,274 @@ create_netxbig_led(struct platform_device *pdev,
        if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE)
                led_dat->cdev.groups = netxbig_led_groups;
 
-       return led_classdev_register(&pdev->dev, &led_dat->cdev);
+       return devm_led_classdev_register(&pdev->dev, &led_dat->cdev);
 }
 
-static int netxbig_led_probe(struct platform_device *pdev)
+#ifdef CONFIG_OF_GPIO
+static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np,
+                                struct netxbig_gpio_ext *gpio_ext)
 {
-       struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
-       struct netxbig_led_data *leds_data;
-       int i;
+       int *addr, *data;
+       int num_addr, num_data;
        int ret;
+       int i;
 
-       if (!pdata)
-               return -EINVAL;
-
-       leds_data = devm_kzalloc(&pdev->dev,
-               sizeof(struct netxbig_led_data) * pdata->num_leds, GFP_KERNEL);
-       if (!leds_data)
+       ret = of_gpio_named_count(np, "addr-gpios");
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to count GPIOs in DT property addr-gpios\n");
+               return ret;
+       }
+       num_addr = ret;
+       addr = devm_kzalloc(dev, num_addr * sizeof(*addr), GFP_KERNEL);
+       if (!addr)
                return -ENOMEM;
 
-       ret = gpio_ext_init(pdata->gpio_ext);
-       if (ret < 0)
+       for (i = 0; i < num_addr; i++) {
+               ret = of_get_named_gpio(np, "addr-gpios", i);
+               if (ret < 0)
+                       return ret;
+               addr[i] = ret;
+       }
+       gpio_ext->addr = addr;
+       gpio_ext->num_addr = num_addr;
+
+       ret = of_gpio_named_count(np, "data-gpios");
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to count GPIOs in DT property data-gpios\n");
                return ret;
+       }
+       num_data = ret;
+       data = devm_kzalloc(dev, num_data * sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
 
-       for (i = 0; i < pdata->num_leds; i++) {
-               ret = create_netxbig_led(pdev, &leds_data[i], &pdata->leds[i]);
+       for (i = 0; i < num_data; i++) {
+               ret = of_get_named_gpio(np, "data-gpios", i);
                if (ret < 0)
-                       goto err_free_leds;
+                       return ret;
+               data[i] = ret;
        }
+       gpio_ext->data = data;
+       gpio_ext->num_data = num_data;
 
-       platform_set_drvdata(pdev, leds_data);
+       ret = of_get_named_gpio(np, "enable-gpio", 0);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to get GPIO from DT property enable-gpio\n");
+               return ret;
+       }
+       gpio_ext->enable = ret;
 
        return 0;
+}
+
+static int netxbig_leds_get_of_pdata(struct device *dev,
+                                    struct netxbig_led_platform_data *pdata)
+{
+       struct device_node *np = dev->of_node;
+       struct device_node *gpio_ext_np;
+       struct device_node *child;
+       struct netxbig_gpio_ext *gpio_ext;
+       struct netxbig_led_timer *timers;
+       struct netxbig_led *leds, *led;
+       int num_timers;
+       int num_leds = 0;
+       int ret;
+       int i;
 
-err_free_leds:
-       for (i = i - 1; i >= 0; i--)
-               delete_netxbig_led(&leds_data[i]);
+       /* GPIO extension */
+       gpio_ext_np = of_parse_phandle(np, "gpio-ext", 0);
+       if (!gpio_ext_np) {
+               dev_err(dev, "Failed to get DT handle gpio-ext\n");
+               return -EINVAL;
+       }
 
-       gpio_ext_free(pdata->gpio_ext);
+       gpio_ext = devm_kzalloc(dev, sizeof(*gpio_ext), GFP_KERNEL);
+       if (!gpio_ext)
+               return -ENOMEM;
+       ret = gpio_ext_get_of_pdata(dev, gpio_ext_np, gpio_ext);
+       if (ret)
+               return ret;
+       of_node_put(gpio_ext_np);
+       pdata->gpio_ext = gpio_ext;
+
+       /* Timers (optional) */
+       ret = of_property_count_u32_elems(np, "timers");
+       if (ret > 0) {
+               if (ret % 3)
+                       return -EINVAL;
+               num_timers = ret / 3;
+               timers = devm_kzalloc(dev, num_timers * sizeof(*timers),
+                                     GFP_KERNEL);
+               if (!timers)
+                       return -ENOMEM;
+               for (i = 0; i < num_timers; i++) {
+                       u32 tmp;
+
+                       of_property_read_u32_index(np, "timers", 3 * i,
+                                                  &timers[i].mode);
+                       if (timers[i].mode >= NETXBIG_LED_MODE_NUM)
+                               return -EINVAL;
+                       of_property_read_u32_index(np, "timers",
+                                                  3 * i + 1, &tmp);
+                       timers[i].delay_on = tmp;
+                       of_property_read_u32_index(np, "timers",
+                                                  3 * i + 2, &tmp);
+                       timers[i].delay_off = tmp;
+               }
+               pdata->timer = timers;
+               pdata->num_timer = num_timers;
+       }
+
+       /* LEDs */
+       num_leds = of_get_child_count(np);
+       if (!num_leds) {
+               dev_err(dev, "No LED subnodes found in DT\n");
+               return -ENODEV;
+       }
+
+       leds = devm_kzalloc(dev, num_leds * sizeof(*leds), GFP_KERNEL);
+       if (!leds)
+               return -ENOMEM;
+
+       led = leds;
+       for_each_child_of_node(np, child) {
+               const char *string;
+               int *mode_val;
+               int num_modes;
+
+               ret = of_property_read_u32(child, "mode-addr",
+                                          &led->mode_addr);
+               if (ret)
+                       goto err_node_put;
+
+               ret = of_property_read_u32(child, "bright-addr",
+                                          &led->bright_addr);
+               if (ret)
+                       goto err_node_put;
+
+               ret = of_property_read_u32(child, "max-brightness",
+                                          &led->bright_max);
+               if (ret)
+                       goto err_node_put;
+
+               mode_val =
+                       devm_kzalloc(dev,
+                                    NETXBIG_LED_MODE_NUM * sizeof(*mode_val),
+                                    GFP_KERNEL);
+               if (!mode_val) {
+                       ret = -ENOMEM;
+                       goto err_node_put;
+               }
+
+               for (i = 0; i < NETXBIG_LED_MODE_NUM; i++)
+                       mode_val[i] = NETXBIG_LED_INVALID_MODE;
+
+               ret = of_property_count_u32_elems(child, "mode-val");
+               if (ret < 0 || ret % 2) {
+                       ret = -EINVAL;
+                       goto err_node_put;
+               }
+               num_modes = ret / 2;
+               if (num_modes > NETXBIG_LED_MODE_NUM) {
+                       ret = -EINVAL;
+                       goto err_node_put;
+               }
+
+               for (i = 0; i < num_modes; i++) {
+                       int mode;
+                       int val;
+
+                       of_property_read_u32_index(child,
+                                                  "mode-val", 2 * i, &mode);
+                       of_property_read_u32_index(child,
+                                                  "mode-val", 2 * i + 1, &val);
+                       if (mode >= NETXBIG_LED_MODE_NUM) {
+                               ret = -EINVAL;
+                               goto err_node_put;
+                       }
+                       mode_val[mode] = val;
+               }
+               led->mode_val = mode_val;
+
+               if (!of_property_read_string(child, "label", &string))
+                       led->name = string;
+               else
+                       led->name = child->name;
+
+               if (!of_property_read_string(child,
+                                            "linux,default-trigger", &string))
+                       led->default_trigger = string;
+
+               led++;
+       }
+
+       pdata->leds = leds;
+       pdata->num_leds = num_leds;
+
+       return 0;
+
+err_node_put:
+       of_node_put(child);
        return ret;
 }
 
-static int netxbig_led_remove(struct platform_device *pdev)
+static const struct of_device_id of_netxbig_leds_match[] = {
+       { .compatible = "lacie,netxbig-leds", },
+       {},
+};
+#else
+static inline int
+netxbig_leds_get_of_pdata(struct device *dev,
+                         struct netxbig_led_platform_data *pdata)
+{
+       return -ENODEV;
+}
+#endif /* CONFIG_OF_GPIO */
+
+static int netxbig_led_probe(struct platform_device *pdev)
 {
        struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
        struct netxbig_led_data *leds_data;
        int i;
+       int ret;
 
-       leds_data = platform_get_drvdata(pdev);
+       if (!pdata) {
+               pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+               if (!pdata)
+                       return -ENOMEM;
+               ret = netxbig_leds_get_of_pdata(&pdev->dev, pdata);
+               if (ret)
+                       return ret;
+       }
+
+       leds_data = devm_kzalloc(&pdev->dev,
+                                pdata->num_leds * sizeof(*leds_data),
+                                GFP_KERNEL);
+       if (!leds_data)
+               return -ENOMEM;
 
-       for (i = 0; i < pdata->num_leds; i++)
-               delete_netxbig_led(&leds_data[i]);
+       ret = gpio_ext_init(pdev, pdata->gpio_ext);
+       if (ret < 0)
+               return ret;
 
-       gpio_ext_free(pdata->gpio_ext);
+       for (i = 0; i < pdata->num_leds; i++) {
+               ret = create_netxbig_led(pdev, pdata,
+                                        &leds_data[i], &pdata->leds[i]);
+               if (ret < 0)
+                       return ret;
+       }
 
        return 0;
 }
 
 static struct platform_driver netxbig_led_driver = {
        .probe          = netxbig_led_probe,
-       .remove         = netxbig_led_remove,
        .driver         = {
-               .name   = "leds-netxbig",
+               .name           = "leds-netxbig",
+               .of_match_table = of_match_ptr(of_netxbig_leds_match),
        },
 };
 
index 39870de20a26fff25d6eba7c767d68fb8c12aa8e..12af1127d9b78117086cb2c83cc0fb2c776625b7 100644 (file)
@@ -124,9 +124,9 @@ static int ot200_led_probe(struct platform_device *pdev)
                leds[i].cdev.name = leds[i].name;
                leds[i].cdev.brightness_set = ot200_led_brightness_set;
 
-               ret = led_classdev_register(&pdev->dev, &leds[i].cdev);
+               ret = devm_led_classdev_register(&pdev->dev, &leds[i].cdev);
                if (ret < 0)
-                       goto err;
+                       return ret;
        }
 
        leds_front = 0;         /* turn off all front leds */
@@ -135,27 +135,10 @@ static int ot200_led_probe(struct platform_device *pdev)
        outb(leds_back, 0x5a);
 
        return 0;
-
-err:
-       for (i = i - 1; i >= 0; i--)
-               led_classdev_unregister(&leds[i].cdev);
-
-       return ret;
-}
-
-static int ot200_led_remove(struct platform_device *pdev)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(leds); i++)
-               led_classdev_unregister(&leds[i].cdev);
-
-       return 0;
 }
 
 static struct platform_driver ot200_led_driver = {
        .probe          = ot200_led_probe,
-       .remove         = ot200_led_remove,
        .driver         = {
                .name   = "leds-ot200",
        },
index 2c5c5b12ab6475c1af3a2710bff8a5f78a2525d9..1e75e1fe9b726d83b7cd8a317fce7c4c727bf82c 100644 (file)
@@ -262,15 +262,19 @@ static int powernv_led_classdev(struct platform_device *pdev,
                while ((cur = of_prop_next_string(p, cur)) != NULL) {
                        powernv_led = devm_kzalloc(dev, sizeof(*powernv_led),
                                                   GFP_KERNEL);
-                       if (!powernv_led)
+                       if (!powernv_led) {
+                               of_node_put(np);
                                return -ENOMEM;
+                       }
 
                        powernv_led->common = powernv_led_common;
                        powernv_led->loc_code = (char *)np->name;
 
                        rc = powernv_led_create(dev, powernv_led, cur);
-                       if (rc)
+                       if (rc) {
+                               of_node_put(np);
                                return rc;
+                       }
                } /* while end */
        }
 
similarity index 99%
rename from arch/mips/mti-sead3/leds-sead3.c
rename to drivers/leds/leds-sead3.c
index c938ceeb8848a1b7ef3b2186ddde2fa5ecc1641e..eb97a3271bb37a265d885f68df82f31e13b15c8c 100644 (file)
@@ -59,6 +59,7 @@ static int sead3_led_remove(struct platform_device *pdev)
 {
        led_classdev_unregister(&sead3_pled);
        led_classdev_unregister(&sead3_fled);
+
        return 0;
 }
 
index 1ba3defdd46023ff028969aa46b8a4cd0d42d6f3..473fb6b97ed4f5f5ca86710f9b0afbfbaefa6ff3 100644 (file)
@@ -76,39 +76,19 @@ static int wrap_led_probe(struct platform_device *pdev)
 {
        int ret;
 
-       ret = led_classdev_register(&pdev->dev, &wrap_power_led);
+       ret = devm_led_classdev_register(&pdev->dev, &wrap_power_led);
        if (ret < 0)
                return ret;
 
-       ret = led_classdev_register(&pdev->dev, &wrap_error_led);
+       ret = devm_led_classdev_register(&pdev->dev, &wrap_error_led);
        if (ret < 0)
-               goto err1;
-
-       ret = led_classdev_register(&pdev->dev, &wrap_extra_led);
-       if (ret < 0)
-               goto err2;
-
-       return ret;
-
-err2:
-       led_classdev_unregister(&wrap_error_led);
-err1:
-       led_classdev_unregister(&wrap_power_led);
-
-       return ret;
-}
+               return ret;
 
-static int wrap_led_remove(struct platform_device *pdev)
-{
-       led_classdev_unregister(&wrap_power_led);
-       led_classdev_unregister(&wrap_error_led);
-       led_classdev_unregister(&wrap_extra_led);
-       return 0;
+       return  devm_led_classdev_register(&pdev->dev, &wrap_extra_led);
 }
 
 static struct platform_driver wrap_led_driver = {
        .probe          = wrap_led_probe,
-       .remove         = wrap_led_remove,
        .driver         = {
                .name           = DRVNAME,
        },
index bc89d7ace2c44e48b1e4c318dc53103767eba942..4238fbc31d3587923f95a61f5ba2b33e5cf74d92 100644 (file)
@@ -44,6 +44,7 @@ static inline int led_get_brightness(struct led_classdev *led_cdev)
        return led_cdev->brightness;
 }
 
+void led_init_core(struct led_classdev *led_cdev);
 void led_stop_software_blink(struct led_classdev *led_cdev);
 
 extern struct rw_semaphore leds_list_lock;
index fea6871d2609d405d9f0d0046059a7562bb8152f..8622ce651ae28f1b94ee8debd97d520bf1a9d004 100644 (file)
@@ -27,6 +27,7 @@ struct heartbeat_trig_data {
        unsigned int phase;
        unsigned int period;
        struct timer_list timer;
+       unsigned int invert;
 };
 
 static void led_heartbeat_function(unsigned long data)
@@ -56,21 +57,27 @@ static void led_heartbeat_function(unsigned long data)
                        msecs_to_jiffies(heartbeat_data->period);
                delay = msecs_to_jiffies(70);
                heartbeat_data->phase++;
-               brightness = led_cdev->max_brightness;
+               if (!heartbeat_data->invert)
+                       brightness = led_cdev->max_brightness;
                break;
        case 1:
                delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
                heartbeat_data->phase++;
+               if (heartbeat_data->invert)
+                       brightness = led_cdev->max_brightness;
                break;
        case 2:
                delay = msecs_to_jiffies(70);
                heartbeat_data->phase++;
-               brightness = led_cdev->max_brightness;
+               if (!heartbeat_data->invert)
+                       brightness = led_cdev->max_brightness;
                break;
        default:
                delay = heartbeat_data->period - heartbeat_data->period / 4 -
                        msecs_to_jiffies(70);
                heartbeat_data->phase = 0;
+               if (heartbeat_data->invert)
+                       brightness = led_cdev->max_brightness;
                break;
        }
 
@@ -78,15 +85,50 @@ static void led_heartbeat_function(unsigned long data)
        mod_timer(&heartbeat_data->timer, jiffies + delay);
 }
 
+static ssize_t led_invert_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
+
+       return sprintf(buf, "%u\n", heartbeat_data->invert);
+}
+
+static ssize_t led_invert_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
+       unsigned long state;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &state);
+       if (ret)
+               return ret;
+
+       heartbeat_data->invert = !!state;
+
+       return size;
+}
+
+static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
+
 static void heartbeat_trig_activate(struct led_classdev *led_cdev)
 {
        struct heartbeat_trig_data *heartbeat_data;
+       int rc;
 
        heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
        if (!heartbeat_data)
                return;
 
        led_cdev->trigger_data = heartbeat_data;
+       rc = device_create_file(led_cdev->dev, &dev_attr_invert);
+       if (rc) {
+               kfree(led_cdev->trigger_data);
+               return;
+       }
+
        setup_timer(&heartbeat_data->timer,
                    led_heartbeat_function, (unsigned long) led_cdev);
        heartbeat_data->phase = 0;
@@ -100,6 +142,7 @@ static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
 
        if (led_cdev->activated) {
                del_timer_sync(&heartbeat_data->timer);
+               device_remove_file(led_cdev->dev, &dev_attr_invert);
                kfree(heartbeat_data);
                led_cdev->activated = false;
        }
index c702de18207ae76ab56f1235ed5c98a9095ed050..3fe3d04a968ad1ae5e148abbc93cde43be576104 100644 (file)
@@ -8040,8 +8040,7 @@ static int remove_and_add_spares(struct mddev *mddev,
                       !test_bit(Bitmap_sync, &rdev->flags)))
                        continue;
 
-               if (rdev->saved_raid_disk < 0)
-                       rdev->recovery_offset = 0;
+               rdev->recovery_offset = 0;
                if (mddev->pers->
                    hot_add_disk(mddev, rdev) == 0) {
                        if (sysfs_link_rdev(mddev, rdev))
index ddd8a5f572aa1c023db07b3eab6b72bc0371c1dd..d9d031ede4bf5d73993a0fc607fab4274627c890 100644 (file)
@@ -2195,7 +2195,7 @@ static int narrow_write_error(struct r1bio *r1_bio, int i)
                bio_trim(wbio, sector - r1_bio->sector, sectors);
                wbio->bi_iter.bi_sector += rdev->data_offset;
                wbio->bi_bdev = rdev->bdev;
-               if (submit_bio_wait(WRITE, wbio) == 0)
+               if (submit_bio_wait(WRITE, wbio) < 0)
                        /* failure! */
                        ok = rdev_set_badblocks(rdev, sector,
                                                sectors, 0)
@@ -2258,15 +2258,16 @@ static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio)
                        rdev_dec_pending(conf->mirrors[m].rdev,
                                         conf->mddev);
                }
-       if (test_bit(R1BIO_WriteError, &r1_bio->state))
-               close_write(r1_bio);
        if (fail) {
                spin_lock_irq(&conf->device_lock);
                list_add(&r1_bio->retry_list, &conf->bio_end_io_list);
                spin_unlock_irq(&conf->device_lock);
                md_wakeup_thread(conf->mddev->thread);
-       } else
+       } else {
+               if (test_bit(R1BIO_WriteError, &r1_bio->state))
+                       close_write(r1_bio);
                raid_end_bio_io(r1_bio);
+       }
 }
 
 static void handle_read_error(struct r1conf *conf, struct r1bio *r1_bio)
@@ -2385,6 +2386,10 @@ static void raid1d(struct md_thread *thread)
                        r1_bio = list_first_entry(&tmp, struct r1bio,
                                                  retry_list);
                        list_del(&r1_bio->retry_list);
+                       if (mddev->degraded)
+                               set_bit(R1BIO_Degraded, &r1_bio->state);
+                       if (test_bit(R1BIO_WriteError, &r1_bio->state))
+                               close_write(r1_bio);
                        raid_end_bio_io(r1_bio);
                }
        }
index 9f69dc526f8cbf271f45b4d7745a442aa6324329..96f36596830696c2f1ba1bd8bc6fbb24d30a43d8 100644 (file)
@@ -39,6 +39,7 @@
  *    far_copies (stored in second byte of layout)
  *    far_offset (stored in bit 16 of layout )
  *    use_far_sets (stored in bit 17 of layout )
+ *    use_far_sets_bugfixed (stored in bit 18 of layout )
  *
  * The data to be stored is divided into chunks using chunksize.  Each device
  * is divided into far_copies sections.   In each section, chunks are laid out
@@ -1497,6 +1498,8 @@ static void status(struct seq_file *seq, struct mddev *mddev)
                        seq_printf(seq, " %d offset-copies", conf->geo.far_copies);
                else
                        seq_printf(seq, " %d far-copies", conf->geo.far_copies);
+               if (conf->geo.far_set_size != conf->geo.raid_disks)
+                       seq_printf(seq, " %d devices per set", conf->geo.far_set_size);
        }
        seq_printf(seq, " [%d/%d] [", conf->geo.raid_disks,
                                        conf->geo.raid_disks - mddev->degraded);
@@ -2467,7 +2470,7 @@ static int narrow_write_error(struct r10bio *r10_bio, int i)
                                   choose_data_offset(r10_bio, rdev) +
                                   (sector - r10_bio->sector));
                wbio->bi_bdev = rdev->bdev;
-               if (submit_bio_wait(WRITE, wbio) == 0)
+               if (submit_bio_wait(WRITE, wbio) < 0)
                        /* Failure! */
                        ok = rdev_set_badblocks(rdev, sector,
                                                sectors, 0)
@@ -2654,16 +2657,17 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
                                rdev_dec_pending(rdev, conf->mddev);
                        }
                }
-               if (test_bit(R10BIO_WriteError,
-                            &r10_bio->state))
-                       close_write(r10_bio);
                if (fail) {
                        spin_lock_irq(&conf->device_lock);
                        list_add(&r10_bio->retry_list, &conf->bio_end_io_list);
                        spin_unlock_irq(&conf->device_lock);
                        md_wakeup_thread(conf->mddev->thread);
-               } else
+               } else {
+                       if (test_bit(R10BIO_WriteError,
+                                    &r10_bio->state))
+                               close_write(r10_bio);
                        raid_end_bio_io(r10_bio);
+               }
        }
 }
 
@@ -2691,6 +2695,12 @@ static void raid10d(struct md_thread *thread)
                        r10_bio = list_first_entry(&tmp, struct r10bio,
                                                   retry_list);
                        list_del(&r10_bio->retry_list);
+                       if (mddev->degraded)
+                               set_bit(R10BIO_Degraded, &r10_bio->state);
+
+                       if (test_bit(R10BIO_WriteError,
+                                    &r10_bio->state))
+                               close_write(r10_bio);
                        raid_end_bio_io(r10_bio);
                }
        }
@@ -3387,7 +3397,7 @@ static int setup_geo(struct geom *geo, struct mddev *mddev, enum geo_type new)
                disks = mddev->raid_disks + mddev->delta_disks;
                break;
        }
-       if (layout >> 18)
+       if (layout >> 19)
                return -1;
        if (chunk < (PAGE_SIZE >> 9) ||
            !is_power_of_2(chunk))
@@ -3399,7 +3409,22 @@ static int setup_geo(struct geom *geo, struct mddev *mddev, enum geo_type new)
        geo->near_copies = nc;
        geo->far_copies = fc;
        geo->far_offset = fo;
-       geo->far_set_size = (layout & (1<<17)) ? disks / fc : disks;
+       switch (layout >> 17) {
+       case 0: /* original layout.  simple but not always optimal */
+               geo->far_set_size = disks;
+               break;
+       case 1: /* "improved" layout which was buggy.  Hopefully no-one is
+                * actually using this, but leave code here just in case.*/
+               geo->far_set_size = disks/fc;
+               WARN(geo->far_set_size < fc,
+                    "This RAID10 layout does not provide data safety - please backup and create new array\n");
+               break;
+       case 2: /* "improved" layout fixed to match documentation */
+               geo->far_set_size = fc * nc;
+               break;
+       default: /* Not a valid layout */
+               return -1;
+       }
        geo->chunk_mask = chunk - 1;
        geo->chunk_shift = ffz(~chunk);
        return nc*fc;
index 49bb8d3ff9be8c7741a5bebc6b210fde38989a09..45933c1606972c007fee4393b82c05db7b24f7ec 100644 (file)
@@ -3499,6 +3499,7 @@ returnbi:
                }
        if (!discard_pending &&
            test_bit(R5_Discard, &sh->dev[sh->pd_idx].flags)) {
+               int hash;
                clear_bit(R5_Discard, &sh->dev[sh->pd_idx].flags);
                clear_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags);
                if (sh->qd_idx >= 0) {
@@ -3512,16 +3513,17 @@ returnbi:
                 * no updated data, so remove it from hash list and the stripe
                 * will be reinitialized
                 */
-               spin_lock_irq(&conf->device_lock);
 unhash:
+               hash = sh->hash_lock_index;
+               spin_lock_irq(conf->hash_locks + hash);
                remove_hash(sh);
+               spin_unlock_irq(conf->hash_locks + hash);
                if (head_sh->batch_head) {
                        sh = list_first_entry(&sh->batch_list,
                                              struct stripe_head, batch_list);
                        if (sh != head_sh)
                                        goto unhash;
                }
-               spin_unlock_irq(&conf->device_lock);
                sh = head_sh;
 
                if (test_bit(STRIPE_SYNC_REQUESTED, &sh->state))
index c742cfd7674e0eb699b2e656cff1eba288932b6f..23b6c8e8701ccd6e3ec57c85483276de4b02f51b 100644 (file)
@@ -387,6 +387,24 @@ out:
        return ERR_PTR(err);
 }
 
+static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr,
+                                     struct mmc_blk_ioc_data *idata)
+{
+       struct mmc_ioc_cmd *ic = &idata->ic;
+
+       if (copy_to_user(&(ic_ptr->response), ic->response,
+                        sizeof(ic->response)))
+               return -EFAULT;
+
+       if (!idata->ic.write_flag) {
+               if (copy_to_user((void __user *)(unsigned long)ic->data_ptr,
+                                idata->buf, idata->buf_bytes))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
 static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status,
                                       u32 retries_max)
 {
@@ -447,12 +465,9 @@ out:
        return err;
 }
 
-static int mmc_blk_ioctl_cmd(struct block_device *bdev,
-       struct mmc_ioc_cmd __user *ic_ptr)
+static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
+                              struct mmc_blk_ioc_data *idata)
 {
-       struct mmc_blk_ioc_data *idata;
-       struct mmc_blk_data *md;
-       struct mmc_card *card;
        struct mmc_command cmd = {0};
        struct mmc_data data = {0};
        struct mmc_request mrq = {NULL};
@@ -461,33 +476,12 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
        int is_rpmb = false;
        u32 status = 0;
 
-       /*
-        * The caller must have CAP_SYS_RAWIO, and must be calling this on the
-        * whole block device, not on a partition.  This prevents overspray
-        * between sibling partitions.
-        */
-       if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
-               return -EPERM;
-
-       idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
-       if (IS_ERR(idata))
-               return PTR_ERR(idata);
-
-       md = mmc_blk_get(bdev->bd_disk);
-       if (!md) {
-               err = -EINVAL;
-               goto cmd_err;
-       }
+       if (!card || !md || !idata)
+               return -EINVAL;
 
        if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
                is_rpmb = true;
 
-       card = md->queue.card;
-       if (IS_ERR(card)) {
-               err = PTR_ERR(card);
-               goto cmd_done;
-       }
-
        cmd.opcode = idata->ic.opcode;
        cmd.arg = idata->ic.arg;
        cmd.flags = idata->ic.flags;
@@ -530,23 +524,21 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
 
        mrq.cmd = &cmd;
 
-       mmc_get_card(card);
-
        err = mmc_blk_part_switch(card, md);
        if (err)
-               goto cmd_rel_host;
+               return err;
 
        if (idata->ic.is_acmd) {
                err = mmc_app_cmd(card->host, card);
                if (err)
-                       goto cmd_rel_host;
+                       return err;
        }
 
        if (is_rpmb) {
                err = mmc_set_blockcount(card, data.blocks,
                        idata->ic.write_flag & (1 << 31));
                if (err)
-                       goto cmd_rel_host;
+                       return err;
        }
 
        if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) &&
@@ -557,7 +549,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
                        pr_err("%s: ioctl_do_sanitize() failed. err = %d",
                               __func__, err);
 
-               goto cmd_rel_host;
+               return err;
        }
 
        mmc_wait_for_req(card->host, &mrq);
@@ -565,14 +557,12 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
        if (cmd.error) {
                dev_err(mmc_dev(card->host), "%s: cmd error %d\n",
                                                __func__, cmd.error);
-               err = cmd.error;
-               goto cmd_rel_host;
+               return cmd.error;
        }
        if (data.error) {
                dev_err(mmc_dev(card->host), "%s: data error %d\n",
                                                __func__, data.error);
-               err = data.error;
-               goto cmd_rel_host;
+               return data.error;
        }
 
        /*
@@ -582,18 +572,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
        if (idata->ic.postsleep_min_us)
                usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us);
 
-       if (copy_to_user(&(ic_ptr->response), cmd.resp, sizeof(cmd.resp))) {
-               err = -EFAULT;
-               goto cmd_rel_host;
-       }
-
-       if (!idata->ic.write_flag) {
-               if (copy_to_user((void __user *)(unsigned long) idata->ic.data_ptr,
-                                               idata->buf, idata->buf_bytes)) {
-                       err = -EFAULT;
-                       goto cmd_rel_host;
-               }
-       }
+       memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp));
 
        if (is_rpmb) {
                /*
@@ -607,24 +586,132 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
                                        __func__, status, err);
        }
 
-cmd_rel_host:
+       return err;
+}
+
+static int mmc_blk_ioctl_cmd(struct block_device *bdev,
+                            struct mmc_ioc_cmd __user *ic_ptr)
+{
+       struct mmc_blk_ioc_data *idata;
+       struct mmc_blk_data *md;
+       struct mmc_card *card;
+       int err = 0, ioc_err = 0;
+
+       idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
+       if (IS_ERR(idata))
+               return PTR_ERR(idata);
+
+       md = mmc_blk_get(bdev->bd_disk);
+       if (!md) {
+               err = -EINVAL;
+               goto cmd_err;
+       }
+
+       card = md->queue.card;
+       if (IS_ERR(card)) {
+               err = PTR_ERR(card);
+               goto cmd_done;
+       }
+
+       mmc_get_card(card);
+
+       ioc_err = __mmc_blk_ioctl_cmd(card, md, idata);
+
        mmc_put_card(card);
 
+       err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata);
+
 cmd_done:
        mmc_blk_put(md);
 cmd_err:
        kfree(idata->buf);
        kfree(idata);
-       return err;
+       return ioc_err ? ioc_err : err;
+}
+
+static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev,
+                                  struct mmc_ioc_multi_cmd __user *user)
+{
+       struct mmc_blk_ioc_data **idata = NULL;
+       struct mmc_ioc_cmd __user *cmds = user->cmds;
+       struct mmc_card *card;
+       struct mmc_blk_data *md;
+       int i, err = 0, ioc_err = 0;
+       __u64 num_of_cmds;
+
+       if (copy_from_user(&num_of_cmds, &user->num_of_cmds,
+                          sizeof(num_of_cmds)))
+               return -EFAULT;
+
+       if (num_of_cmds > MMC_IOC_MAX_CMDS)
+               return -EINVAL;
+
+       idata = kcalloc(num_of_cmds, sizeof(*idata), GFP_KERNEL);
+       if (!idata)
+               return -ENOMEM;
+
+       for (i = 0; i < num_of_cmds; i++) {
+               idata[i] = mmc_blk_ioctl_copy_from_user(&cmds[i]);
+               if (IS_ERR(idata[i])) {
+                       err = PTR_ERR(idata[i]);
+                       num_of_cmds = i;
+                       goto cmd_err;
+               }
+       }
+
+       md = mmc_blk_get(bdev->bd_disk);
+       if (!md)
+               goto cmd_err;
+
+       card = md->queue.card;
+       if (IS_ERR(card)) {
+               err = PTR_ERR(card);
+               goto cmd_done;
+       }
+
+       mmc_get_card(card);
+
+       for (i = 0; i < num_of_cmds && !ioc_err; i++)
+               ioc_err = __mmc_blk_ioctl_cmd(card, md, idata[i]);
+
+       mmc_put_card(card);
+
+       /* copy to user if data and response */
+       for (i = 0; i < num_of_cmds && !err; i++)
+               err = mmc_blk_ioctl_copy_to_user(&cmds[i], idata[i]);
+
+cmd_done:
+       mmc_blk_put(md);
+cmd_err:
+       for (i = 0; i < num_of_cmds; i++) {
+               kfree(idata[i]->buf);
+               kfree(idata[i]);
+       }
+       kfree(idata);
+       return ioc_err ? ioc_err : err;
 }
 
 static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
        unsigned int cmd, unsigned long arg)
 {
-       int ret = -EINVAL;
-       if (cmd == MMC_IOC_CMD)
-               ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg);
-       return ret;
+       /*
+        * The caller must have CAP_SYS_RAWIO, and must be calling this on the
+        * whole block device, not on a partition.  This prevents overspray
+        * between sibling partitions.
+        */
+       if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
+               return -EPERM;
+
+       switch (cmd) {
+       case MMC_IOC_CMD:
+               return mmc_blk_ioctl_cmd(bdev,
+                               (struct mmc_ioc_cmd __user *)arg);
+       case MMC_IOC_MULTI_CMD:
+               return mmc_blk_ioctl_multi_cmd(bdev,
+                               (struct mmc_ioc_multi_cmd __user *)arg);
+       default:
+               return -EINVAL;
+       }
 }
 
 #ifdef CONFIG_COMPAT
index 9ebee72d9c3fd9cd42996f446cea6408e7b6e7c0..4c33d7690f2f66c4f8eb244480d06bce58acd3f1 100644 (file)
@@ -1,13 +1,3 @@
 #
 # MMC core configuration
 #
-
-config MMC_CLKGATE
-       bool "MMC host clock gating"
-       help
-         This will attempt to aggressively gate the clock to the MMC card.
-         This is done to save power due to gating off the logic and bus
-         noise when the MMC card is not in use. Your host driver has to
-         support handling this in order for it to be of any use.
-
-         If unsure, say N.
index a3eb20bdcd97bf32d7a56d741b1bde5097d65767..5ae89e48fd85b575cf743363b3a298b49872b7db 100644 (file)
@@ -187,8 +187,6 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
 
                if (mrq->done)
                        mrq->done(mrq);
-
-               mmc_host_clk_release(host);
        }
 }
 
@@ -206,6 +204,23 @@ static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
                return;
        }
 
+       /*
+        * For sdio rw commands we must wait for card busy otherwise some
+        * sdio devices won't work properly.
+        */
+       if (mmc_is_io_op(mrq->cmd->opcode) && host->ops->card_busy) {
+               int tries = 500; /* Wait aprox 500ms at maximum */
+
+               while (host->ops->card_busy(host) && --tries)
+                       mmc_delay(1);
+
+               if (tries == 0) {
+                       mrq->cmd->error = -EBUSY;
+                       mmc_request_done(host, mrq);
+                       return;
+               }
+       }
+
        host->ops->request(host, mrq);
 }
 
@@ -275,7 +290,6 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
                        mrq->stop->mrq = mrq;
                }
        }
-       mmc_host_clk_hold(host);
        led_trigger_event(host->led, LED_FULL);
        __mmc_start_request(host, mrq);
 
@@ -525,11 +539,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
 static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq,
                 bool is_first_req)
 {
-       if (host->ops->pre_req) {
-               mmc_host_clk_hold(host);
+       if (host->ops->pre_req)
                host->ops->pre_req(host, mrq, is_first_req);
-               mmc_host_clk_release(host);
-       }
 }
 
 /**
@@ -544,11 +555,8 @@ static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq,
 static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
                         int err)
 {
-       if (host->ops->post_req) {
-               mmc_host_clk_hold(host);
+       if (host->ops->post_req)
                host->ops->post_req(host, mrq, err);
-               mmc_host_clk_release(host);
-       }
 }
 
 /**
@@ -833,9 +841,9 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
                unsigned int timeout_us, limit_us;
 
                timeout_us = data->timeout_ns / 1000;
-               if (mmc_host_clk_rate(card->host))
+               if (card->host->ios.clock)
                        timeout_us += data->timeout_clks * 1000 /
-                               (mmc_host_clk_rate(card->host) / 1000);
+                               (card->host->ios.clock / 1000);
 
                if (data->flags & MMC_DATA_WRITE)
                        /*
@@ -1033,8 +1041,6 @@ static inline void mmc_set_ios(struct mmc_host *host)
                 ios->power_mode, ios->chip_select, ios->vdd,
                 ios->bus_width, ios->timing);
 
-       if (ios->clock > 0)
-               mmc_set_ungated(host);
        host->ops->set_ios(host, ios);
 }
 
@@ -1043,17 +1049,15 @@ static inline void mmc_set_ios(struct mmc_host *host)
  */
 void mmc_set_chip_select(struct mmc_host *host, int mode)
 {
-       mmc_host_clk_hold(host);
        host->ios.chip_select = mode;
        mmc_set_ios(host);
-       mmc_host_clk_release(host);
 }
 
 /*
  * Sets the host clock to the highest possible frequency that
  * is below "hz".
  */
-static void __mmc_set_clock(struct mmc_host *host, unsigned int hz)
+void mmc_set_clock(struct mmc_host *host, unsigned int hz)
 {
        WARN_ON(hz && hz < host->f_min);
 
@@ -1064,68 +1068,6 @@ static void __mmc_set_clock(struct mmc_host *host, unsigned int hz)
        mmc_set_ios(host);
 }
 
-void mmc_set_clock(struct mmc_host *host, unsigned int hz)
-{
-       mmc_host_clk_hold(host);
-       __mmc_set_clock(host, hz);
-       mmc_host_clk_release(host);
-}
-
-#ifdef CONFIG_MMC_CLKGATE
-/*
- * This gates the clock by setting it to 0 Hz.
- */
-void mmc_gate_clock(struct mmc_host *host)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&host->clk_lock, flags);
-       host->clk_old = host->ios.clock;
-       host->ios.clock = 0;
-       host->clk_gated = true;
-       spin_unlock_irqrestore(&host->clk_lock, flags);
-       mmc_set_ios(host);
-}
-
-/*
- * This restores the clock from gating by using the cached
- * clock value.
- */
-void mmc_ungate_clock(struct mmc_host *host)
-{
-       /*
-        * We should previously have gated the clock, so the clock shall
-        * be 0 here! The clock may however be 0 during initialization,
-        * when some request operations are performed before setting
-        * the frequency. When ungate is requested in that situation
-        * we just ignore the call.
-        */
-       if (host->clk_old) {
-               BUG_ON(host->ios.clock);
-               /* This call will also set host->clk_gated to false */
-               __mmc_set_clock(host, host->clk_old);
-       }
-}
-
-void mmc_set_ungated(struct mmc_host *host)
-{
-       unsigned long flags;
-
-       /*
-        * We've been given a new frequency while the clock is gated,
-        * so make sure we regard this as ungating it.
-        */
-       spin_lock_irqsave(&host->clk_lock, flags);
-       host->clk_gated = false;
-       spin_unlock_irqrestore(&host->clk_lock, flags);
-}
-
-#else
-void mmc_set_ungated(struct mmc_host *host)
-{
-}
-#endif
-
 int mmc_execute_tuning(struct mmc_card *card)
 {
        struct mmc_host *host = card->host;
@@ -1140,9 +1082,7 @@ int mmc_execute_tuning(struct mmc_card *card)
        else
                opcode = MMC_SEND_TUNING_BLOCK;
 
-       mmc_host_clk_hold(host);
        err = host->ops->execute_tuning(host, opcode);
-       mmc_host_clk_release(host);
 
        if (err)
                pr_err("%s: tuning execution failed\n", mmc_hostname(host));
@@ -1157,10 +1097,8 @@ int mmc_execute_tuning(struct mmc_card *card)
  */
 void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
 {
-       mmc_host_clk_hold(host);
        host->ios.bus_mode = mode;
        mmc_set_ios(host);
-       mmc_host_clk_release(host);
 }
 
 /*
@@ -1168,10 +1106,8 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
  */
 void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
 {
-       mmc_host_clk_hold(host);
        host->ios.bus_width = width;
        mmc_set_ios(host);
-       mmc_host_clk_release(host);
 }
 
 /*
@@ -1340,6 +1276,40 @@ struct device_node *mmc_of_find_child_device(struct mmc_host *host,
 
 #ifdef CONFIG_REGULATOR
 
+/**
+ * mmc_ocrbitnum_to_vdd - Convert a OCR bit number to its voltage
+ * @vdd_bit:   OCR bit number
+ * @min_uV:    minimum voltage value (mV)
+ * @max_uV:    maximum voltage value (mV)
+ *
+ * This function returns the voltage range according to the provided OCR
+ * bit number. If conversion is not possible a negative errno value returned.
+ */
+static int mmc_ocrbitnum_to_vdd(int vdd_bit, int *min_uV, int *max_uV)
+{
+       int             tmp;
+
+       if (!vdd_bit)
+               return -EINVAL;
+
+       /*
+        * REVISIT mmc_vddrange_to_ocrmask() may have set some
+        * bits this regulator doesn't quite support ... don't
+        * be too picky, most cards and regulators are OK with
+        * a 0.1V range goof (it's a small error percentage).
+        */
+       tmp = vdd_bit - ilog2(MMC_VDD_165_195);
+       if (tmp == 0) {
+               *min_uV = 1650 * 1000;
+               *max_uV = 1950 * 1000;
+       } else {
+               *min_uV = 1900 * 1000 + tmp * 100 * 1000;
+               *max_uV = *min_uV + 100 * 1000;
+       }
+
+       return 0;
+}
+
 /**
  * mmc_regulator_get_ocrmask - return mask of supported voltages
  * @supply: regulator to use
@@ -1403,22 +1373,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
        int                     min_uV, max_uV;
 
        if (vdd_bit) {
-               int             tmp;
-
-               /*
-                * REVISIT mmc_vddrange_to_ocrmask() may have set some
-                * bits this regulator doesn't quite support ... don't
-                * be too picky, most cards and regulators are OK with
-                * a 0.1V range goof (it's a small error percentage).
-                */
-               tmp = vdd_bit - ilog2(MMC_VDD_165_195);
-               if (tmp == 0) {
-                       min_uV = 1650 * 1000;
-                       max_uV = 1950 * 1000;
-               } else {
-                       min_uV = 1900 * 1000 + tmp * 100 * 1000;
-                       max_uV = min_uV + 100 * 1000;
-               }
+               mmc_ocrbitnum_to_vdd(vdd_bit, &min_uV, &max_uV);
 
                result = regulator_set_voltage(supply, min_uV, max_uV);
                if (result == 0 && !mmc->regulator_enabled) {
@@ -1439,6 +1394,84 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
 }
 EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr);
 
+static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
+                                                 int min_uV, int target_uV,
+                                                 int max_uV)
+{
+       /*
+        * Check if supported first to avoid errors since we may try several
+        * signal levels during power up and don't want to show errors.
+        */
+       if (!regulator_is_supported_voltage(regulator, min_uV, max_uV))
+               return -EINVAL;
+
+       return regulator_set_voltage_triplet(regulator, min_uV, target_uV,
+                                            max_uV);
+}
+
+/**
+ * mmc_regulator_set_vqmmc - Set VQMMC as per the ios
+ *
+ * For 3.3V signaling, we try to match VQMMC to VMMC as closely as possible.
+ * That will match the behavior of old boards where VQMMC and VMMC were supplied
+ * by the same supply.  The Bus Operating conditions for 3.3V signaling in the
+ * SD card spec also define VQMMC in terms of VMMC.
+ * If this is not possible we'll try the full 2.7-3.6V of the spec.
+ *
+ * For 1.2V and 1.8V signaling we'll try to get as close as possible to the
+ * requested voltage.  This is definitely a good idea for UHS where there's a
+ * separate regulator on the card that's trying to make 1.8V and it's best if
+ * we match.
+ *
+ * This function is expected to be used by a controller's
+ * start_signal_voltage_switch() function.
+ */
+int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct device *dev = mmc_dev(mmc);
+       int ret, volt, min_uV, max_uV;
+
+       /* If no vqmmc supply then we can't change the voltage */
+       if (IS_ERR(mmc->supply.vqmmc))
+               return -EINVAL;
+
+       switch (ios->signal_voltage) {
+       case MMC_SIGNAL_VOLTAGE_120:
+               return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+                                               1100000, 1200000, 1300000);
+       case MMC_SIGNAL_VOLTAGE_180:
+               return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+                                               1700000, 1800000, 1950000);
+       case MMC_SIGNAL_VOLTAGE_330:
+               ret = mmc_ocrbitnum_to_vdd(mmc->ios.vdd, &volt, &max_uV);
+               if (ret < 0)
+                       return ret;
+
+               dev_dbg(dev, "%s: found vmmc voltage range of %d-%duV\n",
+                       __func__, volt, max_uV);
+
+               min_uV = max(volt - 300000, 2700000);
+               max_uV = min(max_uV + 200000, 3600000);
+
+               /*
+                * Due to a limitation in the current implementation of
+                * regulator_set_voltage_triplet() which is taking the lowest
+                * voltage possible if below the target, search for a suitable
+                * voltage in two steps and try to stay close to vmmc
+                * with a 0.3V tolerance at first.
+                */
+               if (!mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+                                               min_uV, volt, max_uV))
+                       return 0;
+
+               return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+                                               2700000, volt, 3600000);
+       default:
+               return -EINVAL;
+       }
+}
+EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc);
+
 #endif /* CONFIG_REGULATOR */
 
 int mmc_regulator_get_supply(struct mmc_host *mmc)
@@ -1515,11 +1548,8 @@ int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
        int old_signal_voltage = host->ios.signal_voltage;
 
        host->ios.signal_voltage = signal_voltage;
-       if (host->ops->start_signal_voltage_switch) {
-               mmc_host_clk_hold(host);
+       if (host->ops->start_signal_voltage_switch)
                err = host->ops->start_signal_voltage_switch(host, &host->ios);
-               mmc_host_clk_release(host);
-       }
 
        if (err)
                host->ios.signal_voltage = old_signal_voltage;
@@ -1553,20 +1583,17 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
                pr_warn("%s: cannot verify signal voltage switch\n",
                        mmc_hostname(host));
 
-       mmc_host_clk_hold(host);
-
        cmd.opcode = SD_SWITCH_VOLTAGE;
        cmd.arg = 0;
        cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
 
        err = mmc_wait_for_cmd(host, &cmd, 0);
        if (err)
-               goto err_command;
+               return err;
+
+       if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
+               return -EIO;
 
-       if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) {
-               err = -EIO;
-               goto err_command;
-       }
        /*
         * The card should drive cmd and dat[0:3] low immediately
         * after the response of cmd11, but wait 1 ms to be sure
@@ -1615,9 +1642,6 @@ power_cycle:
                mmc_power_cycle(host, ocr);
        }
 
-err_command:
-       mmc_host_clk_release(host);
-
        return err;
 }
 
@@ -1626,10 +1650,8 @@ err_command:
  */
 void mmc_set_timing(struct mmc_host *host, unsigned int timing)
 {
-       mmc_host_clk_hold(host);
        host->ios.timing = timing;
        mmc_set_ios(host);
-       mmc_host_clk_release(host);
 }
 
 /*
@@ -1637,10 +1659,8 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing)
  */
 void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
 {
-       mmc_host_clk_hold(host);
        host->ios.drv_type = drv_type;
        mmc_set_ios(host);
-       mmc_host_clk_release(host);
 }
 
 int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
@@ -1648,7 +1668,6 @@ int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
 {
        struct mmc_host *host = card->host;
        int host_drv_type = SD_DRIVER_TYPE_B;
-       int drive_strength;
 
        *drv_type = 0;
 
@@ -1671,14 +1690,10 @@ int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
         * information and let the hardware specific code
         * return what is possible given the options
         */
-       mmc_host_clk_hold(host);
-       drive_strength = host->ops->select_drive_strength(card, max_dtr,
-                                                         host_drv_type,
-                                                         card_drv_type,
-                                                         drv_type);
-       mmc_host_clk_release(host);
-
-       return drive_strength;
+       return host->ops->select_drive_strength(card, max_dtr,
+                                               host_drv_type,
+                                               card_drv_type,
+                                               drv_type);
 }
 
 /*
@@ -1697,8 +1712,6 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
        if (host->ios.power_mode == MMC_POWER_ON)
                return;
 
-       mmc_host_clk_hold(host);
-
        mmc_pwrseq_pre_power_on(host);
 
        host->ios.vdd = fls(ocr) - 1;
@@ -1732,8 +1745,6 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
         * time required to reach a stable voltage.
         */
        mmc_delay(10);
-
-       mmc_host_clk_release(host);
 }
 
 void mmc_power_off(struct mmc_host *host)
@@ -1741,8 +1752,6 @@ void mmc_power_off(struct mmc_host *host)
        if (host->ios.power_mode == MMC_POWER_OFF)
                return;
 
-       mmc_host_clk_hold(host);
-
        mmc_pwrseq_power_off(host);
 
        host->ios.clock = 0;
@@ -1758,8 +1767,6 @@ void mmc_power_off(struct mmc_host *host)
         * can be successfully turned on again.
         */
        mmc_delay(1);
-
-       mmc_host_clk_release(host);
 }
 
 void mmc_power_cycle(struct mmc_host *host, u32 ocr)
@@ -1975,7 +1982,7 @@ static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card,
                 */
                timeout_clks <<= 1;
                timeout_us += (timeout_clks * 1000) /
-                             (mmc_host_clk_rate(card->host) / 1000);
+                             (card->host->ios.clock / 1000);
 
                erase_timeout = timeout_us / 1000;
 
@@ -2423,9 +2430,7 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
 {
        if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
                return;
-       mmc_host_clk_hold(host);
        host->ops->hw_reset(host);
-       mmc_host_clk_release(host);
 }
 
 int mmc_hw_reset(struct mmc_host *host)
@@ -2633,10 +2638,14 @@ void mmc_start_host(struct mmc_host *host)
        host->f_init = max(freqs[0], host->f_min);
        host->rescan_disable = 0;
        host->ios.power_mode = MMC_POWER_UNDEFINED;
+
+       mmc_claim_host(host);
        if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
                mmc_power_off(host);
        else
                mmc_power_up(host, host->ocr_avail);
+       mmc_release_host(host);
+
        mmc_gpiod_request_cd_irq(host);
        _mmc_detect_change(host, 0, false);
 }
@@ -2674,7 +2683,9 @@ void mmc_stop_host(struct mmc_host *host)
 
        BUG_ON(host->card);
 
+       mmc_claim_host(host);
        mmc_power_off(host);
+       mmc_release_host(host);
 }
 
 int mmc_power_save_host(struct mmc_host *host)
index 1a22a82209b26a30c261f4affb43f23641926f50..09241e56d62872ec3b4c3b6cc24ef815e29ed3d8 100644 (file)
@@ -40,9 +40,6 @@ void mmc_init_erase(struct mmc_card *card);
 
 void mmc_set_chip_select(struct mmc_host *host, int mode);
 void mmc_set_clock(struct mmc_host *host, unsigned int hz);
-void mmc_gate_clock(struct mmc_host *host);
-void mmc_ungate_clock(struct mmc_host *host);
-void mmc_set_ungated(struct mmc_host *host);
 void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
 void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
 u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
index e9142108a6c6d486d3ac15ba4cb8a317d1fd62b2..154aced0b91b63e33cb95a9ba1a82e386395392d 100644 (file)
@@ -126,6 +126,12 @@ static int mmc_ios_show(struct seq_file *s, void *data)
        case MMC_TIMING_SD_HS:
                str = "sd high-speed";
                break;
+       case MMC_TIMING_UHS_SDR12:
+               str = "sd uhs SDR12";
+               break;
+       case MMC_TIMING_UHS_SDR25:
+               str = "sd uhs SDR25";
+               break;
        case MMC_TIMING_UHS_SDR50:
                str = "sd uhs SDR50";
                break;
@@ -166,6 +172,25 @@ static int mmc_ios_show(struct seq_file *s, void *data)
        }
        seq_printf(s, "signal voltage:\t%u (%s)\n", ios->chip_select, str);
 
+       switch (ios->drv_type) {
+       case MMC_SET_DRIVER_TYPE_A:
+               str = "driver type A";
+               break;
+       case MMC_SET_DRIVER_TYPE_B:
+               str = "driver type B";
+               break;
+       case MMC_SET_DRIVER_TYPE_C:
+               str = "driver type C";
+               break;
+       case MMC_SET_DRIVER_TYPE_D:
+               str = "driver type D";
+               break;
+       default:
+               str = "invalid";
+               break;
+       }
+       seq_printf(s, "driver type:\t%u (%s)\n", ios->drv_type, str);
+
        return 0;
 }
 
@@ -230,11 +255,6 @@ void mmc_add_host_debugfs(struct mmc_host *host)
                        &mmc_clock_fops))
                goto err_node;
 
-#ifdef CONFIG_MMC_CLKGATE
-       if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
-                               root, &host->clk_delay))
-               goto err_node;
-#endif
 #ifdef CONFIG_FAIL_MMC_REQUEST
        if (fail_request)
                setup_fault_attr(&fail_default_attr, fail_request);
index 5466f25f0281e7b8a83ce4f34d21d1c14a3ebe4a..da950c44204d27d6db8cd5a2d56b6b3e88e27c09 100644 (file)
@@ -61,246 +61,6 @@ void mmc_unregister_host_class(void)
        class_unregister(&mmc_host_class);
 }
 
-#ifdef CONFIG_MMC_CLKGATE
-static ssize_t clkgate_delay_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct mmc_host *host = cls_dev_to_mmc_host(dev);
-       return snprintf(buf, PAGE_SIZE, "%lu\n", host->clkgate_delay);
-}
-
-static ssize_t clkgate_delay_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct mmc_host *host = cls_dev_to_mmc_host(dev);
-       unsigned long flags, value;
-
-       if (kstrtoul(buf, 0, &value))
-               return -EINVAL;
-
-       spin_lock_irqsave(&host->clk_lock, flags);
-       host->clkgate_delay = value;
-       spin_unlock_irqrestore(&host->clk_lock, flags);
-       return count;
-}
-
-/*
- * Enabling clock gating will make the core call out to the host
- * once up and once down when it performs a request or card operation
- * intermingled in any fashion. The driver will see this through
- * set_ios() operations with ios.clock field set to 0 to gate (disable)
- * the block clock, and to the old frequency to enable it again.
- */
-static void mmc_host_clk_gate_delayed(struct mmc_host *host)
-{
-       unsigned long tick_ns;
-       unsigned long freq = host->ios.clock;
-       unsigned long flags;
-
-       if (!freq) {
-               pr_debug("%s: frequency set to 0 in disable function, "
-                        "this means the clock is already disabled.\n",
-                        mmc_hostname(host));
-               return;
-       }
-       /*
-        * New requests may have appeared while we were scheduling,
-        * then there is no reason to delay the check before
-        * clk_disable().
-        */
-       spin_lock_irqsave(&host->clk_lock, flags);
-
-       /*
-        * Delay n bus cycles (at least 8 from MMC spec) before attempting
-        * to disable the MCI block clock. The reference count may have
-        * gone up again after this delay due to rescheduling!
-        */
-       if (!host->clk_requests) {
-               spin_unlock_irqrestore(&host->clk_lock, flags);
-               tick_ns = DIV_ROUND_UP(1000000000, freq);
-               ndelay(host->clk_delay * tick_ns);
-       } else {
-               /* New users appeared while waiting for this work */
-               spin_unlock_irqrestore(&host->clk_lock, flags);
-               return;
-       }
-       mutex_lock(&host->clk_gate_mutex);
-       spin_lock_irqsave(&host->clk_lock, flags);
-       if (!host->clk_requests) {
-               spin_unlock_irqrestore(&host->clk_lock, flags);
-               /* This will set host->ios.clock to 0 */
-               mmc_gate_clock(host);
-               spin_lock_irqsave(&host->clk_lock, flags);
-               pr_debug("%s: gated MCI clock\n", mmc_hostname(host));
-       }
-       spin_unlock_irqrestore(&host->clk_lock, flags);
-       mutex_unlock(&host->clk_gate_mutex);
-}
-
-/*
- * Internal work. Work to disable the clock at some later point.
- */
-static void mmc_host_clk_gate_work(struct work_struct *work)
-{
-       struct mmc_host *host = container_of(work, struct mmc_host,
-                                             clk_gate_work.work);
-
-       mmc_host_clk_gate_delayed(host);
-}
-
-/**
- *     mmc_host_clk_hold - ungate hardware MCI clocks
- *     @host: host to ungate.
- *
- *     Makes sure the host ios.clock is restored to a non-zero value
- *     past this call. Increase clock reference count and ungate clock
- *     if we're the first user.
- */
-void mmc_host_clk_hold(struct mmc_host *host)
-{
-       unsigned long flags;
-
-       /* cancel any clock gating work scheduled by mmc_host_clk_release() */
-       cancel_delayed_work_sync(&host->clk_gate_work);
-       mutex_lock(&host->clk_gate_mutex);
-       spin_lock_irqsave(&host->clk_lock, flags);
-       if (host->clk_gated) {
-               spin_unlock_irqrestore(&host->clk_lock, flags);
-               mmc_ungate_clock(host);
-               spin_lock_irqsave(&host->clk_lock, flags);
-               pr_debug("%s: ungated MCI clock\n", mmc_hostname(host));
-       }
-       host->clk_requests++;
-       spin_unlock_irqrestore(&host->clk_lock, flags);
-       mutex_unlock(&host->clk_gate_mutex);
-}
-
-/**
- *     mmc_host_may_gate_card - check if this card may be gated
- *     @card: card to check.
- */
-static bool mmc_host_may_gate_card(struct mmc_card *card)
-{
-       /* If there is no card we may gate it */
-       if (!card)
-               return true;
-       /*
-        * Don't gate SDIO cards! These need to be clocked at all times
-        * since they may be independent systems generating interrupts
-        * and other events. The clock requests counter from the core will
-        * go down to zero since the core does not need it, but we will not
-        * gate the clock, because there is somebody out there that may still
-        * be using it.
-        */
-       return !(card->quirks & MMC_QUIRK_BROKEN_CLK_GATING);
-}
-
-/**
- *     mmc_host_clk_release - gate off hardware MCI clocks
- *     @host: host to gate.
- *
- *     Calls the host driver with ios.clock set to zero as often as possible
- *     in order to gate off hardware MCI clocks. Decrease clock reference
- *     count and schedule disabling of clock.
- */
-void mmc_host_clk_release(struct mmc_host *host)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&host->clk_lock, flags);
-       host->clk_requests--;
-       if (mmc_host_may_gate_card(host->card) &&
-           !host->clk_requests)
-               schedule_delayed_work(&host->clk_gate_work,
-                                     msecs_to_jiffies(host->clkgate_delay));
-       spin_unlock_irqrestore(&host->clk_lock, flags);
-}
-
-/**
- *     mmc_host_clk_rate - get current clock frequency setting
- *     @host: host to get the clock frequency for.
- *
- *     Returns current clock frequency regardless of gating.
- */
-unsigned int mmc_host_clk_rate(struct mmc_host *host)
-{
-       unsigned long freq;
-       unsigned long flags;
-
-       spin_lock_irqsave(&host->clk_lock, flags);
-       if (host->clk_gated)
-               freq = host->clk_old;
-       else
-               freq = host->ios.clock;
-       spin_unlock_irqrestore(&host->clk_lock, flags);
-       return freq;
-}
-
-/**
- *     mmc_host_clk_init - set up clock gating code
- *     @host: host with potential clock to control
- */
-static inline void mmc_host_clk_init(struct mmc_host *host)
-{
-       host->clk_requests = 0;
-       /* Hold MCI clock for 8 cycles by default */
-       host->clk_delay = 8;
-       /*
-        * Default clock gating delay is 0ms to avoid wasting power.
-        * This value can be tuned by writing into sysfs entry.
-        */
-       host->clkgate_delay = 0;
-       host->clk_gated = false;
-       INIT_DELAYED_WORK(&host->clk_gate_work, mmc_host_clk_gate_work);
-       spin_lock_init(&host->clk_lock);
-       mutex_init(&host->clk_gate_mutex);
-}
-
-/**
- *     mmc_host_clk_exit - shut down clock gating code
- *     @host: host with potential clock to control
- */
-static inline void mmc_host_clk_exit(struct mmc_host *host)
-{
-       /*
-        * Wait for any outstanding gate and then make sure we're
-        * ungated before exiting.
-        */
-       if (cancel_delayed_work_sync(&host->clk_gate_work))
-               mmc_host_clk_gate_delayed(host);
-       if (host->clk_gated)
-               mmc_host_clk_hold(host);
-       /* There should be only one user now */
-       WARN_ON(host->clk_requests > 1);
-}
-
-static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
-{
-       host->clkgate_delay_attr.show = clkgate_delay_show;
-       host->clkgate_delay_attr.store = clkgate_delay_store;
-       sysfs_attr_init(&host->clkgate_delay_attr.attr);
-       host->clkgate_delay_attr.attr.name = "clkgate_delay";
-       host->clkgate_delay_attr.attr.mode = S_IRUGO | S_IWUSR;
-       if (device_create_file(&host->class_dev, &host->clkgate_delay_attr))
-               pr_err("%s: Failed to create clkgate_delay sysfs entry\n",
-                               mmc_hostname(host));
-}
-#else
-
-static inline void mmc_host_clk_init(struct mmc_host *host)
-{
-}
-
-static inline void mmc_host_clk_exit(struct mmc_host *host)
-{
-}
-
-static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
-{
-}
-
-#endif
-
 void mmc_retune_enable(struct mmc_host *host)
 {
        host->can_retune = 1;
@@ -507,6 +267,8 @@ int mmc_of_parse(struct mmc_host *host)
                host->caps |= MMC_CAP_UHS_DDR50;
        if (of_property_read_bool(np, "cap-power-off-card"))
                host->caps |= MMC_CAP_POWER_OFF_CARD;
+       if (of_property_read_bool(np, "cap-mmc-hw-reset"))
+               host->caps |= MMC_CAP_HW_RESET;
        if (of_property_read_bool(np, "cap-sdio-irq"))
                host->caps |= MMC_CAP_SDIO_IRQ;
        if (of_property_read_bool(np, "full-pwr-cycle"))
@@ -583,8 +345,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
                return NULL;
        }
 
-       mmc_host_clk_init(host);
-
        spin_lock_init(&host->lock);
        init_waitqueue_head(&host->wq);
        INIT_DELAYED_WORK(&host->detect, mmc_rescan);
@@ -633,7 +393,6 @@ int mmc_add_host(struct mmc_host *host)
 #ifdef CONFIG_DEBUG_FS
        mmc_add_host_debugfs(host);
 #endif
-       mmc_host_clk_sysfs_init(host);
 
        mmc_start_host(host);
        register_pm_notifier(&host->pm_notify);
@@ -663,8 +422,6 @@ void mmc_remove_host(struct mmc_host *host)
        device_del(&host->class_dev);
 
        led_trigger_unregister_simple(host->led);
-
-       mmc_host_clk_exit(host);
 }
 
 EXPORT_SYMBOL(mmc_remove_host);
index f6cd995dbe920392cce50062ff2e4f080634ee06..c793fda27321da0086bb01d92e33d9aa44480ed0 100644 (file)
@@ -1931,14 +1931,12 @@ static int mmc_reset(struct mmc_host *host)
        if (!mmc_can_reset(card))
                return -EOPNOTSUPP;
 
-       mmc_host_clk_hold(host);
        mmc_set_clock(host, host->f_init);
 
        host->ops->hw_reset(host);
 
        /* Set initial state and call mmc_set_ios */
        mmc_set_initial_state(host);
-       mmc_host_clk_release(host);
 
        return mmc_init_card(host, card->ocr, card);
 }
@@ -2006,14 +2004,13 @@ int mmc_attach_mmc(struct mmc_host *host)
 
        mmc_release_host(host);
        err = mmc_add_card(host->card);
-       mmc_claim_host(host);
        if (err)
                goto remove_card;
 
+       mmc_claim_host(host);
        return 0;
 
 remove_card:
-       mmc_release_host(host);
        mmc_remove_card(host->card);
        mmc_claim_host(host);
        host->card = NULL;
index 0e9ae1c276c800b82228e6c3676e6aacb9ad2755..1f444269ebbe66027831757403d1bdd53d1b6a8d 100644 (file)
@@ -579,7 +579,6 @@ out:
 
        return err;
 }
-EXPORT_SYMBOL_GPL(__mmc_switch);
 
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
                unsigned int timeout_ms)
@@ -589,7 +588,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
 }
 EXPORT_SYMBOL_GPL(mmc_switch);
 
-int mmc_send_tuning(struct mmc_host *host)
+int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error)
 {
        struct mmc_request mrq = {NULL};
        struct mmc_command cmd = {0};
@@ -599,16 +598,13 @@ int mmc_send_tuning(struct mmc_host *host)
        const u8 *tuning_block_pattern;
        int size, err = 0;
        u8 *data_buf;
-       u32 opcode;
 
        if (ios->bus_width == MMC_BUS_WIDTH_8) {
                tuning_block_pattern = tuning_blk_pattern_8bit;
                size = sizeof(tuning_blk_pattern_8bit);
-               opcode = MMC_SEND_TUNING_BLOCK_HS200;
        } else if (ios->bus_width == MMC_BUS_WIDTH_4) {
                tuning_block_pattern = tuning_blk_pattern_4bit;
                size = sizeof(tuning_blk_pattern_4bit);
-               opcode = MMC_SEND_TUNING_BLOCK;
        } else
                return -EINVAL;
 
@@ -639,6 +635,9 @@ int mmc_send_tuning(struct mmc_host *host)
 
        mmc_wait_for_req(host, &mrq);
 
+       if (cmd_error)
+               *cmd_error = cmd.error;
+
        if (cmd.error) {
                err = cmd.error;
                goto out;
index f498f9ae21f09bd405d687981ae959f061a63d3e..f1b8e81aaa284d1310c4da481a915cf5560e7700 100644 (file)
@@ -28,6 +28,9 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width);
 int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
 int mmc_can_ext_csd(struct mmc_card *card);
 int mmc_switch_status_error(struct mmc_host *host, u32 status);
+int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
+               unsigned int timeout_ms, bool use_busy_signal, bool send_status,
+               bool ignore_crc);
 
 #endif
 
index 9d6d2fb217967d069ff2f18db0b403dff05483e0..ad4f94ec7e8d465a4e4e45ba9388fb7d97231f4a 100644 (file)
@@ -76,7 +76,7 @@ struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host,
        if (!pwrseq)
                return ERR_PTR(-ENOMEM);
 
-       pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW);
+       pwrseq->reset_gpio = gpiod_get(dev, "reset", GPIOD_OUT_LOW);
        if (IS_ERR(pwrseq->reset_gpio)) {
                ret = PTR_ERR(pwrseq->reset_gpio);
                goto free;
@@ -84,11 +84,11 @@ struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host,
 
        /*
         * register reset handler to ensure emmc reset also from
-        * emergency_reboot(), priority 129 schedules it just before
-        * system reboot
+        * emergency_reboot(), priority 255 is the highest priority
+        * so it will be executed before any system reboot handler.
         */
        pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
-       pwrseq->reset_nb.priority = 129;
+       pwrseq->reset_nb.priority = 255;
        register_restart_handler(&pwrseq->reset_nb);
 
        pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
index 0b14b83a53d6c9614a2ac897893f5cc1690a5b6e..d10538bb5e07ac298fcadad3003bd84c02fa00d5 100644 (file)
@@ -23,18 +23,21 @@ struct mmc_pwrseq_simple {
        struct mmc_pwrseq pwrseq;
        bool clk_enabled;
        struct clk *ext_clk;
-       int nr_gpios;
-       struct gpio_desc *reset_gpios[0];
+       struct gpio_descs *reset_gpios;
 };
 
 static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
                                              int value)
 {
        int i;
+       struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
+       int values[reset_gpios->ndescs];
 
-       for (i = 0; i < pwrseq->nr_gpios; i++)
-               if (!IS_ERR(pwrseq->reset_gpios[i]))
-                       gpiod_set_value_cansleep(pwrseq->reset_gpios[i], value);
+       for (i = 0; i < reset_gpios->ndescs; i++)
+               values[i] = value;
+
+       gpiod_set_array_value_cansleep(reset_gpios->ndescs, reset_gpios->desc,
+                                      values);
 }
 
 static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
@@ -75,11 +78,8 @@ static void mmc_pwrseq_simple_free(struct mmc_host *host)
 {
        struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
                                        struct mmc_pwrseq_simple, pwrseq);
-       int i;
 
-       for (i = 0; i < pwrseq->nr_gpios; i++)
-               if (!IS_ERR(pwrseq->reset_gpios[i]))
-                       gpiod_put(pwrseq->reset_gpios[i]);
+       gpiod_put_array(pwrseq->reset_gpios);
 
        if (!IS_ERR(pwrseq->ext_clk))
                clk_put(pwrseq->ext_clk);
@@ -98,14 +98,9 @@ struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
                                           struct device *dev)
 {
        struct mmc_pwrseq_simple *pwrseq;
-       int i, nr_gpios, ret = 0;
-
-       nr_gpios = of_gpio_named_count(dev->of_node, "reset-gpios");
-       if (nr_gpios < 0)
-               nr_gpios = 0;
+       int ret = 0;
 
-       pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios *
-                        sizeof(struct gpio_desc *), GFP_KERNEL);
+       pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL);
        if (!pwrseq)
                return ERR_PTR(-ENOMEM);
 
@@ -116,22 +111,12 @@ struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
                goto free;
        }
 
-       for (i = 0; i < nr_gpios; i++) {
-               pwrseq->reset_gpios[i] = gpiod_get_index(dev, "reset", i,
-                                                        GPIOD_OUT_HIGH);
-               if (IS_ERR(pwrseq->reset_gpios[i]) &&
-                   PTR_ERR(pwrseq->reset_gpios[i]) != -ENOENT &&
-                   PTR_ERR(pwrseq->reset_gpios[i]) != -ENOSYS) {
-                       ret = PTR_ERR(pwrseq->reset_gpios[i]);
-
-                       while (i--)
-                               gpiod_put(pwrseq->reset_gpios[i]);
-
-                       goto clk_put;
-               }
+       pwrseq->reset_gpios = gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH);
+       if (IS_ERR(pwrseq->reset_gpios)) {
+               ret = PTR_ERR(pwrseq->reset_gpios);
+               goto clk_put;
        }
 
-       pwrseq->nr_gpios = nr_gpios;
        pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
 
        return &pwrseq->pwrseq;
index dd1d1e0fe32227edb4a184e3f8947ecbe27991b2..fad660b95809224e3a06abde0709be3d50acf307 100644 (file)
 #define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
 #endif
 
-/*
- * This hook just adds a quirk for all sdio devices
- */
-static void add_quirk_for_sdio_devices(struct mmc_card *card, int data)
-{
-       if (mmc_card_sdio(card))
-               card->quirks |= data;
-}
-
 static const struct mmc_fixup mmc_fixup_methods[] = {
-       /* by default sdio devices are considered CLK_GATING broken */
-       /* good cards will be whitelisted as they are tested */
-       SDIO_FIXUP(SDIO_ANY_ID, SDIO_ANY_ID,
-                  add_quirk_for_sdio_devices,
-                  MMC_QUIRK_BROKEN_CLK_GATING),
-
-       SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
-                  remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
-
        SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
                   add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
 
index 4e7366ab187f295faab7674b72bf83fc8fb49ba4..141eaa923e18eecc140039d34604d5f2969c023b 100644 (file)
@@ -357,8 +357,6 @@ int mmc_sd_switch_hs(struct mmc_card *card)
        if (card->sw_caps.hs_max_dtr == 0)
                return 0;
 
-       err = -EIO;
-
        status = kmalloc(64, GFP_KERNEL);
        if (!status) {
                pr_err("%s: could not allocate a buffer for "
@@ -628,9 +626,25 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
         * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
         */
        if (!mmc_host_is_spi(card->host) &&
-           (card->sd_bus_speed == UHS_SDR50_BUS_SPEED ||
-            card->sd_bus_speed == UHS_SDR104_BUS_SPEED))
+               (card->sd_bus_speed == UHS_SDR50_BUS_SPEED ||
+                card->sd_bus_speed == UHS_DDR50_BUS_SPEED ||
+                card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) {
                err = mmc_execute_tuning(card);
+
+               /*
+                * As SD Specifications Part1 Physical Layer Specification
+                * Version 3.01 says, CMD19 tuning is available for unlocked
+                * cards in transfer state of 1.8V signaling mode. The small
+                * difference between v3.00 and 3.01 spec means that CMD19
+                * tuning is also available for DDR50 mode.
+                */
+               if (err && card->sd_bus_speed == UHS_DDR50_BUS_SPEED) {
+                       pr_warn("%s: ddr50 tuning failed\n",
+                               mmc_hostname(card->host));
+                       err = 0;
+               }
+       }
+
 out:
        kfree(status);
 
@@ -786,9 +800,7 @@ static int mmc_sd_get_ro(struct mmc_host *host)
        if (!host->ops->get_ro)
                return -1;
 
-       mmc_host_clk_hold(host);
        ro = host->ops->get_ro(host);
-       mmc_host_clk_release(host);
 
        return ro;
 }
@@ -1231,14 +1243,13 @@ int mmc_attach_sd(struct mmc_host *host)
 
        mmc_release_host(host);
        err = mmc_add_card(host->card);
-       mmc_claim_host(host);
        if (err)
                goto remove_card;
 
+       mmc_claim_host(host);
        return 0;
 
 remove_card:
-       mmc_release_host(host);
        mmc_remove_card(host->card);
        host->card = NULL;
        mmc_claim_host(host);
index b91abedcfdca7054654a774f22a357d93661293a..16d838e6d623be3968a7a900e85348d17a642fe3 100644 (file)
@@ -897,11 +897,10 @@ static int mmc_sdio_pre_suspend(struct mmc_host *host)
  */
 static int mmc_sdio_suspend(struct mmc_host *host)
 {
-       if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
-               mmc_claim_host(host);
+       mmc_claim_host(host);
+
+       if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host))
                sdio_disable_wide(host->card);
-               mmc_release_host(host);
-       }
 
        if (!mmc_card_keep_power(host)) {
                mmc_power_off(host);
@@ -910,6 +909,8 @@ static int mmc_sdio_suspend(struct mmc_host *host)
                mmc_retune_needed(host);
        }
 
+       mmc_release_host(host);
+
        return 0;
 }
 
@@ -955,13 +956,10 @@ static int mmc_sdio_resume(struct mmc_host *host)
        }
 
        if (!err && host->sdio_irqs) {
-               if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
+               if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD))
                        wake_up_process(host->sdio_irq_thread);
-               } else if (host->caps & MMC_CAP_SDIO_IRQ) {
-                       mmc_host_clk_hold(host);
+               else if (host->caps & MMC_CAP_SDIO_IRQ)
                        host->ops->enable_sdio_irq(host, 1);
-                       mmc_host_clk_release(host);
-               }
        }
 
        mmc_release_host(host);
@@ -1018,15 +1016,24 @@ out:
 static int mmc_sdio_runtime_suspend(struct mmc_host *host)
 {
        /* No references to the card, cut the power to it. */
+       mmc_claim_host(host);
        mmc_power_off(host);
+       mmc_release_host(host);
+
        return 0;
 }
 
 static int mmc_sdio_runtime_resume(struct mmc_host *host)
 {
+       int ret;
+
        /* Restore power and re-initialize. */
+       mmc_claim_host(host);
        mmc_power_up(host, host->card->ocr);
-       return mmc_sdio_power_restore(host);
+       ret = mmc_sdio_power_restore(host);
+       mmc_release_host(host);
+
+       return ret;
 }
 
 static int mmc_sdio_reset(struct mmc_host *host)
index 09cc67d028f07b76165414625829bdd22f56b7f0..91bbbfb29f3f9dd1eeed0976f2757589c032ab99 100644 (file)
@@ -168,21 +168,15 @@ static int sdio_irq_thread(void *_host)
                }
 
                set_current_state(TASK_INTERRUPTIBLE);
-               if (host->caps & MMC_CAP_SDIO_IRQ) {
-                       mmc_host_clk_hold(host);
+               if (host->caps & MMC_CAP_SDIO_IRQ)
                        host->ops->enable_sdio_irq(host, 1);
-                       mmc_host_clk_release(host);
-               }
                if (!kthread_should_stop())
                        schedule_timeout(period);
                set_current_state(TASK_RUNNING);
        } while (!kthread_should_stop());
 
-       if (host->caps & MMC_CAP_SDIO_IRQ) {
-               mmc_host_clk_hold(host);
+       if (host->caps & MMC_CAP_SDIO_IRQ)
                host->ops->enable_sdio_irq(host, 0);
-               mmc_host_clk_release(host);
-       }
 
        pr_debug("%s: IRQ thread exiting with code %d\n",
                 mmc_hostname(host), ret);
@@ -208,9 +202,7 @@ static int sdio_card_irq_get(struct mmc_card *card)
                                return err;
                        }
                } else if (host->caps & MMC_CAP_SDIO_IRQ) {
-                       mmc_host_clk_hold(host);
                        host->ops->enable_sdio_irq(host, 1);
-                       mmc_host_clk_release(host);
                }
        }
 
@@ -229,9 +221,7 @@ static int sdio_card_irq_put(struct mmc_card *card)
                        atomic_set(&host->sdio_irq_thread_abort, 1);
                        kthread_stop(host->sdio_irq_thread);
                } else if (host->caps & MMC_CAP_SDIO_IRQ) {
-                       mmc_host_clk_hold(host);
                        host->ops->enable_sdio_irq(host, 0);
-                       mmc_host_clk_release(host);
                }
        }
 
index 12a4d3ab174cf9bcc19bff2d33d24cbb82f9c431..5660c7f459e94731b14a5055e9aab3fbb79ec3b1 100644 (file)
@@ -12,6 +12,8 @@
 #ifndef _MMC_SDIO_OPS_H
 #define _MMC_SDIO_OPS_H
 
+#include <linux/mmc/sdio.h>
+
 int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
 int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
        unsigned addr, u8 in, u8* out);
@@ -19,5 +21,10 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
        unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
 int sdio_reset(struct mmc_host *host);
 
+static inline bool mmc_is_io_op(u32 opcode)
+{
+       return opcode == SD_IO_RW_DIRECT || opcode == SD_IO_RW_EXTENDED;
+}
+
 #endif
 
index 8a1e3498261e9301cffad18889c5392e6751b7a6..af71de5fda3b48c99fefd122a0cb6df9af2234ac 100644 (file)
@@ -67,7 +67,7 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
          has the effect of scrambling the addresses and formats of data
          accessed in sizes other than the datum size.
 
-         This is the case for the Freescale eSDHC and Nintendo Wii SDHCI.
+         This is the case for the Nintendo Wii SDHCI.
 
 config MMC_SDHCI_PCI
        tristate "SDHCI support on PCI bus"
@@ -140,8 +140,8 @@ config MMC_SDHCI_OF_AT91
 config MMC_SDHCI_OF_ESDHC
        tristate "SDHCI OF support for the Freescale eSDHC controller"
        depends on MMC_SDHCI_PLTFM
-       depends on PPC
-       select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
+       depends on PPC || ARCH_MXC || ARCH_LAYERSCAPE
+       select MMC_SDHCI_IO_ACCESSORS
        help
          This selects the Freescale eSDHC controller support.
 
@@ -366,7 +366,7 @@ config MMC_OMAP
 config MMC_OMAP_HS
        tristate "TI OMAP High Speed Multimedia Card Interface support"
        depends on HAS_DMA
-       depends on ARCH_OMAP2PLUS || COMPILE_TEST
+       depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
        help
          This selects the TI OMAP High Speed Multimedia card Interface.
          If you have an omap2plus board with a Multimedia Card slot,
@@ -473,7 +473,7 @@ config MMC_DAVINCI
 
 config MMC_GOLDFISH
        tristate "goldfish qemu Multimedia Card Interface support"
-       depends on GOLDFISH
+       depends on GOLDFISH || COMPILE_TEST
        help
          This selects the Goldfish Multimedia card Interface emulation
          found on the Goldfish Android virtual device emulation.
@@ -615,15 +615,7 @@ config MMC_DW
        help
          This selects support for the Synopsys DesignWare Mobile Storage IP
          block, this provides host support for SD and MMC interfaces, in both
-         PIO and external DMA modes.
-
-config MMC_DW_IDMAC
-       bool "Internal DMAC interface"
-       depends on MMC_DW
-       help
-         This selects support for the internal DMAC block within the Synopsys
-         Designware Mobile Storage IP block. This disables the external DMA
-         interface.
+         PIO, internal DMA mode and external DMA mode.
 
 config MMC_DW_PLTFM
        tristate "Synopsys Designware MCI Support as platform device"
@@ -652,7 +644,6 @@ config MMC_DW_K3
        tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
        depends on MMC_DW
        select MMC_DW_PLTFM
-       select MMC_DW_IDMAC
        help
          This selects support for Hisilicon K3 SoC specific extensions to the
          Synopsys DesignWare Memory Card Interface driver. Select this option
index 4f3452afa6ca3d0340cb67b74a874900f61bf54d..3595f83e89dd2caf9d56cdf6ceb875ef85ece719 100644 (file)
@@ -9,8 +9,8 @@ obj-$(CONFIG_MMC_MXC)           += mxcmmc.o
 obj-$(CONFIG_MMC_MXS)          += mxs-mmc.o
 obj-$(CONFIG_MMC_SDHCI)                += sdhci.o
 obj-$(CONFIG_MMC_SDHCI_PCI)    += sdhci-pci.o
+sdhci-pci-y                    += sdhci-pci-core.o sdhci-pci-o2micro.o
 obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI))       += sdhci-pci-data.o
-obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI))       += sdhci-pci-o2micro.o
 obj-$(CONFIG_MMC_SDHCI_ACPI)   += sdhci-acpi.o
 obj-$(CONFIG_MMC_SDHCI_PXAV3)  += sdhci-pxav3.o
 obj-$(CONFIG_MMC_SDHCI_PXAV2)  += sdhci-pxav2.o
index 1e75309898b76b33f7f2d91a05425269fb941ffb..3a7e835a00339a3f1b4165ebc47c28c37d57fc85 100644 (file)
@@ -446,7 +446,7 @@ out:
        return loc;
 }
 
-static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot)
+static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
 {
        struct dw_mci *host = slot->host;
        struct dw_mci_exynos_priv_data *priv = host->priv;
@@ -461,7 +461,7 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot)
                mci_writel(host, TMOUT, ~0);
                smpl = dw_mci_exynos_move_next_clksmpl(host);
 
-               if (!mmc_send_tuning(mmc))
+               if (!mmc_send_tuning(mmc, opcode, NULL))
                        candiates |= (1 << smpl);
 
        } while (start_smpl != smpl);
index ec6dbcdec693beacc6fef823be15c515645e83a6..7e1d13b68b062b4199e65d914c7cac464e2b0daf 100644 (file)
@@ -59,6 +59,8 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
        host->pdata = pdev->dev.platform_data;
 
        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       /* Get registers' physical base address */
+       host->phy_regs = (void *)(regs->start);
        host->regs = devm_ioremap_resource(&pdev->dev, regs);
        if (IS_ERR(host->regs))
                return PTR_ERR(host->regs);
index bc76aa22473ea2f6e75f8b98d96e90b60ed99538..9becebeeccd17c925d933aa4bff36954a217ff9c 100644 (file)
 #include <linux/mmc/host.h>
 #include <linux/mmc/dw_mmc.h>
 #include <linux/of_address.h>
+#include <linux/slab.h>
 
 #include "dw_mmc.h"
 #include "dw_mmc-pltfm.h"
 
 #define RK3288_CLKGEN_DIV       2
 
+struct dw_mci_rockchip_priv_data {
+       struct clk              *drv_clk;
+       struct clk              *sample_clk;
+       int                     default_sample_phase;
+};
+
 static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
 {
        *cmdr |= SDMMC_CMD_USE_HOLD_REG;
@@ -33,6 +40,7 @@ static int dw_mci_rk3288_setup_clock(struct dw_mci *host)
 
 static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
 {
+       struct dw_mci_rockchip_priv_data *priv = host->priv;
        int ret;
        unsigned int cclkin;
        u32 bus_hz;
@@ -66,6 +74,158 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
                /* force dw_mci_setup_bus() */
                host->current_speed = 0;
        }
+
+       /* Make sure we use phases which we can enumerate with */
+       if (!IS_ERR(priv->sample_clk))
+               clk_set_phase(priv->sample_clk, priv->default_sample_phase);
+}
+
+#define NUM_PHASES                     360
+#define TUNING_ITERATION_TO_PHASE(i)   (DIV_ROUND_UP((i) * 360, NUM_PHASES))
+
+static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+{
+       struct dw_mci *host = slot->host;
+       struct dw_mci_rockchip_priv_data *priv = host->priv;
+       struct mmc_host *mmc = slot->mmc;
+       int ret = 0;
+       int i;
+       bool v, prev_v = 0, first_v;
+       struct range_t {
+               int start;
+               int end; /* inclusive */
+       };
+       struct range_t *ranges;
+       unsigned int range_count = 0;
+       int longest_range_len = -1;
+       int longest_range = -1;
+       int middle_phase;
+
+       if (IS_ERR(priv->sample_clk)) {
+               dev_err(host->dev, "Tuning clock (sample_clk) not defined.\n");
+               return -EIO;
+       }
+
+       ranges = kmalloc_array(NUM_PHASES / 2 + 1, sizeof(*ranges), GFP_KERNEL);
+       if (!ranges)
+               return -ENOMEM;
+
+       /* Try each phase and extract good ranges */
+       for (i = 0; i < NUM_PHASES; ) {
+               clk_set_phase(priv->sample_clk, TUNING_ITERATION_TO_PHASE(i));
+
+               v = !mmc_send_tuning(mmc, opcode, NULL);
+
+               if (i == 0)
+                       first_v = v;
+
+               if ((!prev_v) && v) {
+                       range_count++;
+                       ranges[range_count-1].start = i;
+               }
+               if (v) {
+                       ranges[range_count-1].end = i;
+                       i++;
+               } else if (i == NUM_PHASES - 1) {
+                       /* No extra skipping rules if we're at the end */
+                       i++;
+               } else {
+                       /*
+                        * No need to check too close to an invalid
+                        * one since testing bad phases is slow.  Skip
+                        * 20 degrees.
+                        */
+                       i += DIV_ROUND_UP(20 * NUM_PHASES, 360);
+
+                       /* Always test the last one */
+                       if (i >= NUM_PHASES)
+                               i = NUM_PHASES - 1;
+               }
+
+               prev_v = v;
+       }
+
+       if (range_count == 0) {
+               dev_warn(host->dev, "All phases bad!");
+               ret = -EIO;
+               goto free;
+       }
+
+       /* wrap around case, merge the end points */
+       if ((range_count > 1) && first_v && v) {
+               ranges[0].start = ranges[range_count-1].start;
+               range_count--;
+       }
+
+       if (ranges[0].start == 0 && ranges[0].end == NUM_PHASES - 1) {
+               clk_set_phase(priv->sample_clk, priv->default_sample_phase);
+               dev_info(host->dev, "All phases work, using default phase %d.",
+                        priv->default_sample_phase);
+               goto free;
+       }
+
+       /* Find the longest range */
+       for (i = 0; i < range_count; i++) {
+               int len = (ranges[i].end - ranges[i].start + 1);
+
+               if (len < 0)
+                       len += NUM_PHASES;
+
+               if (longest_range_len < len) {
+                       longest_range_len = len;
+                       longest_range = i;
+               }
+
+               dev_dbg(host->dev, "Good phase range %d-%d (%d len)\n",
+                       TUNING_ITERATION_TO_PHASE(ranges[i].start),
+                       TUNING_ITERATION_TO_PHASE(ranges[i].end),
+                       len
+               );
+       }
+
+       dev_dbg(host->dev, "Best phase range %d-%d (%d len)\n",
+               TUNING_ITERATION_TO_PHASE(ranges[longest_range].start),
+               TUNING_ITERATION_TO_PHASE(ranges[longest_range].end),
+               longest_range_len
+       );
+
+       middle_phase = ranges[longest_range].start + longest_range_len / 2;
+       middle_phase %= NUM_PHASES;
+       dev_info(host->dev, "Successfully tuned phase to %d\n",
+                TUNING_ITERATION_TO_PHASE(middle_phase));
+
+       clk_set_phase(priv->sample_clk,
+                     TUNING_ITERATION_TO_PHASE(middle_phase));
+
+free:
+       kfree(ranges);
+       return ret;
+}
+
+static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
+{
+       struct device_node *np = host->dev->of_node;
+       struct dw_mci_rockchip_priv_data *priv;
+
+       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       if (of_property_read_u32(np, "rockchip,default-sample-phase",
+                                       &priv->default_sample_phase))
+               priv->default_sample_phase = 0;
+
+       priv->drv_clk = devm_clk_get(host->dev, "ciu-drive");
+       if (IS_ERR(priv->drv_clk))
+               dev_dbg(host->dev, "ciu_drv not available\n");
+
+       priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
+       if (IS_ERR(priv->sample_clk))
+               dev_dbg(host->dev, "ciu_sample not available\n");
+
+       host->priv = priv;
+
+       return 0;
 }
 
 static int dw_mci_rockchip_init(struct dw_mci *host)
@@ -95,6 +255,8 @@ static const struct dw_mci_drv_data rk3288_drv_data = {
        .caps                   = dw_mci_rk3288_dwmmc_caps,
        .prepare_command        = dw_mci_rockchip_prepare_command,
        .set_ios                = dw_mci_rk3288_set_ios,
+       .execute_tuning         = dw_mci_rk3288_execute_tuning,
+       .parse_dt               = dw_mci_rk3288_parse_dt,
        .setup_clock    = dw_mci_rk3288_setup_clock,
        .init                   = dw_mci_rockchip_init,
 };
index fcbf5524fd3136f6242036ae206239f83c90db71..7a6cedbe48a837e7fd5800c9fe1da131d569df51 100644 (file)
@@ -56,7 +56,6 @@
 #define DW_MCI_FREQ_MAX        200000000       /* unit: HZ */
 #define DW_MCI_FREQ_MIN        400000          /* unit: HZ */
 
-#ifdef CONFIG_MMC_DW_IDMAC
 #define IDMAC_INT_CLR          (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
                                 SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
                                 SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \
@@ -102,7 +101,6 @@ struct idmac_desc {
 
 /* Each descriptor can transfer up to 4KB of data in chained mode */
 #define DW_MCI_DESC_DATA_LENGTH        0x1000
-#endif /* CONFIG_MMC_DW_IDMAC */
 
 static bool dw_mci_reset(struct dw_mci *host);
 static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset);
@@ -407,7 +405,6 @@ static int dw_mci_get_dma_dir(struct mmc_data *data)
                return DMA_FROM_DEVICE;
 }
 
-#ifdef CONFIG_MMC_DW_IDMAC
 static void dw_mci_dma_cleanup(struct dw_mci *host)
 {
        struct mmc_data *data = host->data;
@@ -445,12 +442,21 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host)
        mci_writel(host, BMOD, temp);
 }
 
-static void dw_mci_idmac_complete_dma(struct dw_mci *host)
+static void dw_mci_dmac_complete_dma(void *arg)
 {
+       struct dw_mci *host = arg;
        struct mmc_data *data = host->data;
 
        dev_vdbg(host->dev, "DMA complete\n");
 
+       if ((host->use_dma == TRANS_MODE_EDMAC) &&
+           data && (data->flags & MMC_DATA_READ))
+               /* Invalidate cache after read */
+               dma_sync_sg_for_cpu(mmc_dev(host->cur_slot->mmc),
+                                   data->sg,
+                                   data->sg_len,
+                                   DMA_FROM_DEVICE);
+
        host->dma_ops->cleanup(host);
 
        /*
@@ -564,7 +570,7 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data,
        wmb(); /* drain writebuffer */
 }
 
-static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
+static int dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
 {
        u32 temp;
 
@@ -589,6 +595,8 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
 
        /* Start it running */
        mci_writel(host, PLDMND, 1);
+
+       return 0;
 }
 
 static int dw_mci_idmac_init(struct dw_mci *host)
@@ -669,10 +677,110 @@ static const struct dw_mci_dma_ops dw_mci_idmac_ops = {
        .init = dw_mci_idmac_init,
        .start = dw_mci_idmac_start_dma,
        .stop = dw_mci_idmac_stop_dma,
-       .complete = dw_mci_idmac_complete_dma,
+       .complete = dw_mci_dmac_complete_dma,
+       .cleanup = dw_mci_dma_cleanup,
+};
+
+static void dw_mci_edmac_stop_dma(struct dw_mci *host)
+{
+       dmaengine_terminate_all(host->dms->ch);
+}
+
+static int dw_mci_edmac_start_dma(struct dw_mci *host,
+                                           unsigned int sg_len)
+{
+       struct dma_slave_config cfg;
+       struct dma_async_tx_descriptor *desc = NULL;
+       struct scatterlist *sgl = host->data->sg;
+       const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
+       u32 sg_elems = host->data->sg_len;
+       u32 fifoth_val;
+       u32 fifo_offset = host->fifo_reg - host->regs;
+       int ret = 0;
+
+       /* Set external dma config: burst size, burst width */
+       cfg.dst_addr = (dma_addr_t)(host->phy_regs + fifo_offset);
+       cfg.src_addr = cfg.dst_addr;
+       cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+       /* Match burst msize with external dma config */
+       fifoth_val = mci_readl(host, FIFOTH);
+       cfg.dst_maxburst = mszs[(fifoth_val >> 28) & 0x7];
+       cfg.src_maxburst = cfg.dst_maxburst;
+
+       if (host->data->flags & MMC_DATA_WRITE)
+               cfg.direction = DMA_MEM_TO_DEV;
+       else
+               cfg.direction = DMA_DEV_TO_MEM;
+
+       ret = dmaengine_slave_config(host->dms->ch, &cfg);
+       if (ret) {
+               dev_err(host->dev, "Failed to config edmac.\n");
+               return -EBUSY;
+       }
+
+       desc = dmaengine_prep_slave_sg(host->dms->ch, sgl,
+                                      sg_len, cfg.direction,
+                                      DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!desc) {
+               dev_err(host->dev, "Can't prepare slave sg.\n");
+               return -EBUSY;
+       }
+
+       /* Set dw_mci_dmac_complete_dma as callback */
+       desc->callback = dw_mci_dmac_complete_dma;
+       desc->callback_param = (void *)host;
+       dmaengine_submit(desc);
+
+       /* Flush cache before write */
+       if (host->data->flags & MMC_DATA_WRITE)
+               dma_sync_sg_for_device(mmc_dev(host->cur_slot->mmc), sgl,
+                                      sg_elems, DMA_TO_DEVICE);
+
+       dma_async_issue_pending(host->dms->ch);
+
+       return 0;
+}
+
+static int dw_mci_edmac_init(struct dw_mci *host)
+{
+       /* Request external dma channel */
+       host->dms = kzalloc(sizeof(struct dw_mci_dma_slave), GFP_KERNEL);
+       if (!host->dms)
+               return -ENOMEM;
+
+       host->dms->ch = dma_request_slave_channel(host->dev, "rx-tx");
+       if (!host->dms->ch) {
+               dev_err(host->dev, "Failed to get external DMA channel.\n");
+               kfree(host->dms);
+               host->dms = NULL;
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+static void dw_mci_edmac_exit(struct dw_mci *host)
+{
+       if (host->dms) {
+               if (host->dms->ch) {
+                       dma_release_channel(host->dms->ch);
+                       host->dms->ch = NULL;
+               }
+               kfree(host->dms);
+               host->dms = NULL;
+       }
+}
+
+static const struct dw_mci_dma_ops dw_mci_edmac_ops = {
+       .init = dw_mci_edmac_init,
+       .exit = dw_mci_edmac_exit,
+       .start = dw_mci_edmac_start_dma,
+       .stop = dw_mci_edmac_stop_dma,
+       .complete = dw_mci_dmac_complete_dma,
        .cleanup = dw_mci_dma_cleanup,
 };
-#endif /* CONFIG_MMC_DW_IDMAC */
 
 static int dw_mci_pre_dma_transfer(struct dw_mci *host,
                                   struct mmc_data *data,
@@ -752,7 +860,6 @@ static void dw_mci_post_req(struct mmc_host *mmc,
 
 static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
 {
-#ifdef CONFIG_MMC_DW_IDMAC
        unsigned int blksz = data->blksz;
        const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
        u32 fifo_width = 1 << host->data_shift;
@@ -760,6 +867,10 @@ static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
        u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers;
        int idx = ARRAY_SIZE(mszs) - 1;
 
+       /* pio should ship this scenario */
+       if (!host->use_dma)
+               return;
+
        tx_wmark = (host->fifo_depth) / 2;
        tx_wmark_invers = host->fifo_depth - tx_wmark;
 
@@ -788,7 +899,6 @@ static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
 done:
        fifoth_val = SDMMC_SET_FIFOTH(msize, rx_wmark, tx_wmark);
        mci_writel(host, FIFOTH, fifoth_val);
-#endif
 }
 
 static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data)
@@ -850,10 +960,12 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
 
        host->using_dma = 1;
 
-       dev_vdbg(host->dev,
-                "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
-                (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
-                sg_len);
+       if (host->use_dma == TRANS_MODE_IDMAC)
+               dev_vdbg(host->dev,
+                        "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
+                        (unsigned long)host->sg_cpu,
+                        (unsigned long)host->sg_dma,
+                        sg_len);
 
        /*
         * Decide the MSIZE and RX/TX Watermark.
@@ -875,7 +987,11 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
        mci_writel(host, INTMASK, temp);
        spin_unlock_irqrestore(&host->irq_lock, irqflags);
 
-       host->dma_ops->start(host, sg_len);
+       if (host->dma_ops->start(host, sg_len)) {
+               /* We can't do DMA */
+               dev_err(host->dev, "%s: failed to start DMA.\n", __func__);
+               return -ENODEV;
+       }
 
        return 0;
 }
@@ -1177,6 +1293,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
        /* DDR mode set */
        if (ios->timing == MMC_TIMING_MMC_DDR52 ||
+           ios->timing == MMC_TIMING_UHS_DDR50 ||
            ios->timing == MMC_TIMING_MMC_HS400)
                regs |= ((0x1 << slot->id) << 16);
        else
@@ -1279,7 +1396,6 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
        const struct dw_mci_drv_data *drv_data = host->drv_data;
        u32 uhs;
        u32 v18 = SDMMC_UHS_18V << slot->id;
-       int min_uv, max_uv;
        int ret;
 
        if (drv_data && drv_data->switch_voltage)
@@ -1291,22 +1407,18 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
         * does no harm but you need to set the regulator directly.  Try both.
         */
        uhs = mci_readl(host, UHS_REG);
-       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
-               min_uv = 2700000;
-               max_uv = 3600000;
+       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
                uhs &= ~v18;
-       } else {
-               min_uv = 1700000;
-               max_uv = 1950000;
+       else
                uhs |= v18;
-       }
+
        if (!IS_ERR(mmc->supply.vqmmc)) {
-               ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
+               ret = mmc_regulator_set_vqmmc(mmc, ios);
 
                if (ret) {
                        dev_dbg(&mmc->class_dev,
-                                        "Regulator set error %d: %d - %d\n",
-                                        ret, min_uv, max_uv);
+                                        "Regulator set error %d - %s V\n",
+                                        ret, uhs & v18 ? "1.8" : "3.3");
                        return ret;
                }
        }
@@ -1427,7 +1539,7 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
        int err = -EINVAL;
 
        if (drv_data && drv_data->execute_tuning)
-               err = drv_data->execute_tuning(slot);
+               err = drv_data->execute_tuning(slot, opcode);
        return err;
 }
 
@@ -2343,15 +2455,17 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
 
        }
 
-#ifdef CONFIG_MMC_DW_IDMAC
-       /* Handle DMA interrupts */
+       if (host->use_dma != TRANS_MODE_IDMAC)
+               return IRQ_HANDLED;
+
+       /* Handle IDMA interrupts */
        if (host->dma_64bit_address == 1) {
                pending = mci_readl(host, IDSTS64);
                if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
                        mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI |
                                                        SDMMC_IDMAC_INT_RI);
                        mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI);
-                       host->dma_ops->complete(host);
+                       host->dma_ops->complete((void *)host);
                }
        } else {
                pending = mci_readl(host, IDSTS);
@@ -2359,10 +2473,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
                        mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
                                                        SDMMC_IDMAC_INT_RI);
                        mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
-                       host->dma_ops->complete(host);
+                       host->dma_ops->complete((void *)host);
                }
        }
-#endif
 
        return IRQ_HANDLED;
 }
@@ -2471,13 +2584,21 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
                goto err_host_allocated;
 
        /* Useful defaults if platform data is unset. */
-       if (host->use_dma) {
+       if (host->use_dma == TRANS_MODE_IDMAC) {
                mmc->max_segs = host->ring_size;
                mmc->max_blk_size = 65536;
                mmc->max_seg_size = 0x1000;
                mmc->max_req_size = mmc->max_seg_size * host->ring_size;
                mmc->max_blk_count = mmc->max_req_size / 512;
+       } else if (host->use_dma == TRANS_MODE_EDMAC) {
+               mmc->max_segs = 64;
+               mmc->max_blk_size = 65536;
+               mmc->max_blk_count = 65535;
+               mmc->max_req_size =
+                               mmc->max_blk_size * mmc->max_blk_count;
+               mmc->max_seg_size = mmc->max_req_size;
        } else {
+               /* TRANS_MODE_PIO */
                mmc->max_segs = 64;
                mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
                mmc->max_blk_count = 512;
@@ -2517,38 +2638,74 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
 static void dw_mci_init_dma(struct dw_mci *host)
 {
        int addr_config;
-       /* Check ADDR_CONFIG bit in HCON to find IDMAC address bus width */
-       addr_config = (mci_readl(host, HCON) >> 27) & 0x01;
-
-       if (addr_config == 1) {
-               /* host supports IDMAC in 64-bit address mode */
-               host->dma_64bit_address = 1;
-               dev_info(host->dev, "IDMAC supports 64-bit address mode.\n");
-               if (!dma_set_mask(host->dev, DMA_BIT_MASK(64)))
-                       dma_set_coherent_mask(host->dev, DMA_BIT_MASK(64));
-       } else {
-               /* host supports IDMAC in 32-bit address mode */
-               host->dma_64bit_address = 0;
-               dev_info(host->dev, "IDMAC supports 32-bit address mode.\n");
-       }
+       struct device *dev = host->dev;
+       struct device_node *np = dev->of_node;
 
-       /* Alloc memory for sg translation */
-       host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE,
-                                         &host->sg_dma, GFP_KERNEL);
-       if (!host->sg_cpu) {
-               dev_err(host->dev, "%s: could not alloc DMA memory\n",
-                       __func__);
+       /*
+       * Check tansfer mode from HCON[17:16]
+       * Clear the ambiguous description of dw_mmc databook:
+       * 2b'00: No DMA Interface -> Actually means using Internal DMA block
+       * 2b'01: DesignWare DMA Interface -> Synopsys DW-DMA block
+       * 2b'10: Generic DMA Interface -> non-Synopsys generic DMA block
+       * 2b'11: Non DW DMA Interface -> pio only
+       * Compared to DesignWare DMA Interface, Generic DMA Interface has a
+       * simpler request/acknowledge handshake mechanism and both of them
+       * are regarded as external dma master for dw_mmc.
+       */
+       host->use_dma = SDMMC_GET_TRANS_MODE(mci_readl(host, HCON));
+       if (host->use_dma == DMA_INTERFACE_IDMA) {
+               host->use_dma = TRANS_MODE_IDMAC;
+       } else if (host->use_dma == DMA_INTERFACE_DWDMA ||
+                  host->use_dma == DMA_INTERFACE_GDMA) {
+               host->use_dma = TRANS_MODE_EDMAC;
+       } else {
                goto no_dma;
        }
 
        /* Determine which DMA interface to use */
-#ifdef CONFIG_MMC_DW_IDMAC
-       host->dma_ops = &dw_mci_idmac_ops;
-       dev_info(host->dev, "Using internal DMA controller.\n");
-#endif
+       if (host->use_dma == TRANS_MODE_IDMAC) {
+               /*
+               * Check ADDR_CONFIG bit in HCON to find
+               * IDMAC address bus width
+               */
+               addr_config = SDMMC_GET_ADDR_CONFIG(mci_readl(host, HCON));
+
+               if (addr_config == 1) {
+                       /* host supports IDMAC in 64-bit address mode */
+                       host->dma_64bit_address = 1;
+                       dev_info(host->dev,
+                                "IDMAC supports 64-bit address mode.\n");
+                       if (!dma_set_mask(host->dev, DMA_BIT_MASK(64)))
+                               dma_set_coherent_mask(host->dev,
+                                                     DMA_BIT_MASK(64));
+               } else {
+                       /* host supports IDMAC in 32-bit address mode */
+                       host->dma_64bit_address = 0;
+                       dev_info(host->dev,
+                                "IDMAC supports 32-bit address mode.\n");
+               }
 
-       if (!host->dma_ops)
-               goto no_dma;
+               /* Alloc memory for sg translation */
+               host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE,
+                                                  &host->sg_dma, GFP_KERNEL);
+               if (!host->sg_cpu) {
+                       dev_err(host->dev,
+                               "%s: could not alloc DMA memory\n",
+                               __func__);
+                       goto no_dma;
+               }
+
+               host->dma_ops = &dw_mci_idmac_ops;
+               dev_info(host->dev, "Using internal DMA controller.\n");
+       } else {
+               /* TRANS_MODE_EDMAC: check dma bindings again */
+               if ((of_property_count_strings(np, "dma-names") < 0) ||
+                   (!of_find_property(np, "dmas", NULL))) {
+                       goto no_dma;
+               }
+               host->dma_ops = &dw_mci_edmac_ops;
+               dev_info(host->dev, "Using external DMA controller.\n");
+       }
 
        if (host->dma_ops->init && host->dma_ops->start &&
            host->dma_ops->stop && host->dma_ops->cleanup) {
@@ -2562,12 +2719,11 @@ static void dw_mci_init_dma(struct dw_mci *host)
                goto no_dma;
        }
 
-       host->use_dma = 1;
        return;
 
 no_dma:
        dev_info(host->dev, "Using PIO mode.\n");
-       host->use_dma = 0;
+       host->use_dma = TRANS_MODE_PIO;
 }
 
 static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset)
@@ -2650,10 +2806,9 @@ static bool dw_mci_reset(struct dw_mci *host)
                }
        }
 
-#if IS_ENABLED(CONFIG_MMC_DW_IDMAC)
-       /* It is also recommended that we reset and reprogram idmac */
-       dw_mci_idmac_reset(host);
-#endif
+       if (host->use_dma == TRANS_MODE_IDMAC)
+               /* It is also recommended that we reset and reprogram idmac */
+               dw_mci_idmac_reset(host);
 
        ret = true;
 
@@ -2890,7 +3045,7 @@ int dw_mci_probe(struct dw_mci *host)
         * Get the host data width - this assumes that HCON has been set with
         * the correct values.
         */
-       i = (mci_readl(host, HCON) >> 7) & 0x7;
+       i = SDMMC_GET_HDATA_WIDTH(mci_readl(host, HCON));
        if (!i) {
                host->push_data = dw_mci_push_data16;
                host->pull_data = dw_mci_pull_data16;
@@ -2972,7 +3127,7 @@ int dw_mci_probe(struct dw_mci *host)
        if (host->pdata->num_slots)
                host->num_slots = host->pdata->num_slots;
        else
-               host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
+               host->num_slots = SDMMC_GET_SLOT_NUM(mci_readl(host, HCON));
 
        /*
         * Enable interrupts for command done, data over, data empty,
@@ -3067,6 +3222,9 @@ EXPORT_SYMBOL(dw_mci_remove);
  */
 int dw_mci_suspend(struct dw_mci *host)
 {
+       if (host->use_dma && host->dma_ops->exit)
+               host->dma_ops->exit(host);
+
        return 0;
 }
 EXPORT_SYMBOL(dw_mci_suspend);
index 8ce4674730a6ff8efde66e456136aa0d282e06c5..f695b58f06135868ce934cae1e322899f3dc6545 100644 (file)
 #define SDMMC_SET_FIFOTH(m, r, t)      (((m) & 0x7) << 28 | \
                                         ((r) & 0xFFF) << 16 | \
                                         ((t) & 0xFFF))
+/* HCON register defines */
+#define DMA_INTERFACE_IDMA             (0x0)
+#define DMA_INTERFACE_DWDMA            (0x1)
+#define DMA_INTERFACE_GDMA             (0x2)
+#define DMA_INTERFACE_NODMA            (0x3)
+#define SDMMC_GET_TRANS_MODE(x)                (((x)>>16) & 0x3)
+#define SDMMC_GET_SLOT_NUM(x)          ((((x)>>1) & 0x1F) + 1)
+#define SDMMC_GET_HDATA_WIDTH(x)       (((x)>>7) & 0x7)
+#define SDMMC_GET_ADDR_CONFIG(x)       (((x)>>27) & 0x1)
 /* Internal DMAC interrupt defines */
 #define SDMMC_IDMAC_INT_AI             BIT(9)
 #define SDMMC_IDMAC_INT_NI             BIT(8)
 /* Version ID register define */
 #define SDMMC_GET_VERID(x)             ((x) & 0xFFFF)
 /* Card read threshold */
-#define SDMMC_SET_RD_THLD(v, x)                (((v) & 0x1FFF) << 16 | (x))
+#define SDMMC_SET_RD_THLD(v, x)                (((v) & 0xFFF) << 16 | (x))
 #define SDMMC_UHS_18V                  BIT(0)
 /* All ctrl reset bits */
 #define SDMMC_CTRL_ALL_RESET_FLAGS \
@@ -281,7 +290,7 @@ struct dw_mci_drv_data {
        void            (*prepare_command)(struct dw_mci *host, u32 *cmdr);
        void            (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
        int             (*parse_dt)(struct dw_mci *host);
-       int             (*execute_tuning)(struct dw_mci_slot *slot);
+       int             (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode);
        int             (*prepare_hs400_tuning)(struct dw_mci *host,
                                                struct mmc_ios *ios);
        int             (*switch_voltage)(struct mmc_host *mmc,
index ae19d83bb9de0d966bb095f73c2f8b900578659a..8ee11f4120fcae295756118c492b9cdef150eadf 100644 (file)
@@ -1511,6 +1511,7 @@ static const struct of_device_id mmc_spi_of_match_table[] = {
        { .compatible = "mmc-spi-slot", },
        {},
 };
+MODULE_DEVICE_TABLE(of, mmc_spi_of_match_table);
 
 static struct spi_driver mmc_spi_driver = {
        .driver = {
index 006f1862444b245a8c7564c60cff9a915e13d127..79905ce895adaeffc6f8e07689af768398b272c1 100644 (file)
@@ -711,6 +711,7 @@ static const struct of_device_id moxart_mmc_match[] = {
        { .compatible = "faraday,ftsdc010" },
        { }
 };
+MODULE_DEVICE_TABLE(of, moxart_mmc_match);
 
 static struct platform_driver moxart_mmc_driver = {
        .probe      = moxart_probe,
index 7153500dd0071bc98da0951299463d2b80d26111..39568cc29a2a18cf752141dcf6233746b4d149e1 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
+#include <linux/slab.h>
 #include <linux/spinlock.h>
 
 #include <linux/mmc/card.h>
@@ -64,6 +65,7 @@
 #define SDC_RESP2        0x48
 #define SDC_RESP3        0x4c
 #define SDC_BLK_NUM      0x50
+#define EMMC_IOCON       0x7c
 #define SDC_ACMD_RESP    0x80
 #define MSDC_DMA_SA      0x90
 #define MSDC_DMA_CTRL    0x98
@@ -71,6 +73,8 @@
 #define MSDC_PATCH_BIT   0xb0
 #define MSDC_PATCH_BIT1  0xb4
 #define MSDC_PAD_TUNE    0xec
+#define PAD_DS_TUNE      0x188
+#define EMMC50_CFG0      0x208
 
 /*--------------------------------------------------------------------------*/
 /* Register Mask                                                            */
@@ -87,6 +91,7 @@
 #define MSDC_CFG_CKSTB          (0x1 << 7)     /* R  */
 #define MSDC_CFG_CKDIV          (0xff << 8)    /* RW */
 #define MSDC_CFG_CKMOD          (0x3 << 16)    /* RW */
+#define MSDC_CFG_HS400_CK_MODE  (0x1 << 18)    /* RW */
 
 /* MSDC_IOCON mask */
 #define MSDC_IOCON_SDR104CKS    (0x1 << 0)     /* RW */
 #define MSDC_PATCH_BIT_SPCPUSH    (0x1 << 29)  /* RW */
 #define MSDC_PATCH_BIT_DECRCTMO   (0x1 << 30)  /* RW */
 
+#define MSDC_PAD_TUNE_DATRRDLY   (0x1f <<  8)  /* RW */
+#define MSDC_PAD_TUNE_CMDRDLY    (0x1f << 16)  /* RW */
+
+#define PAD_DS_TUNE_DLY1         (0x1f << 2)   /* RW */
+#define PAD_DS_TUNE_DLY2         (0x1f << 7)   /* RW */
+#define PAD_DS_TUNE_DLY3         (0x1f << 12)  /* RW */
+
+#define EMMC50_CFG_PADCMD_LATCHCK (0x1 << 0)   /* RW */
+#define EMMC50_CFG_CRCSTS_EDGE    (0x1 << 3)   /* RW */
+#define EMMC50_CFG_CFCSTS_SEL     (0x1 << 4)   /* RW */
+
 #define REQ_CMD_EIO  (0x1 << 0)
 #define REQ_CMD_TMO  (0x1 << 1)
 #define REQ_DAT_ERR  (0x1 << 2)
 #define CMD_TIMEOUT         (HZ/10 * 5)        /* 100ms x5 */
 #define DAT_TIMEOUT         (HZ    * 5)        /* 1000ms x5 */
 
+#define PAD_DELAY_MAX  32 /* PAD delay cells */
 /*--------------------------------------------------------------------------*/
 /* Descriptor Structure                                                     */
 /*--------------------------------------------------------------------------*/
@@ -265,6 +282,14 @@ struct msdc_save_para {
        u32 pad_tune;
        u32 patch_bit0;
        u32 patch_bit1;
+       u32 pad_ds_tune;
+       u32 emmc50_cfg0;
+};
+
+struct msdc_delay_phase {
+       u8 maxlen;
+       u8 start;
+       u8 final_phase;
 };
 
 struct msdc_host {
@@ -297,8 +322,9 @@ struct msdc_host {
        u32 mclk;               /* mmc subsystem clock frequency */
        u32 src_clk_freq;       /* source clock frequency */
        u32 sclk;               /* SD/MS bus clock frequency */
-       bool ddr;
+       unsigned char timing;
        bool vqmmc_enabled;
+       u32 hs400_ds_delay;
        struct msdc_save_para save_para; /* used when gate HCLK */
 };
 
@@ -353,7 +379,10 @@ static void msdc_reset_hw(struct msdc_host *host)
 static void msdc_cmd_next(struct msdc_host *host,
                struct mmc_request *mrq, struct mmc_command *cmd);
 
-static u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
+static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR |
+                       MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY |
+                       MSDC_INTEN_ACMDCRCERR | MSDC_INTEN_ACMDTMO;
+static const u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
                        MSDC_INTEN_DATCRCERR | MSDC_INTEN_DMA_BDCSERR |
                        MSDC_INTEN_DMA_GPDCSERR | MSDC_INTEN_DMA_PROTECT;
 
@@ -485,7 +514,7 @@ static void msdc_ungate_clock(struct msdc_host *host)
                cpu_relax();
 }
 
-static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
+static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
 {
        u32 mode;
        u32 flags;
@@ -501,8 +530,15 @@ static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
 
        flags = readl(host->base + MSDC_INTEN);
        sdr_clr_bits(host->base + MSDC_INTEN, flags);
-       if (ddr) { /* may need to modify later */
-               mode = 0x2; /* ddr mode and use divisor */
+       sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_HS400_CK_MODE);
+       if (timing == MMC_TIMING_UHS_DDR50 ||
+           timing == MMC_TIMING_MMC_DDR52 ||
+           timing == MMC_TIMING_MMC_HS400) {
+               if (timing == MMC_TIMING_MMC_HS400)
+                       mode = 0x3;
+               else
+                       mode = 0x2; /* ddr mode and use divisor */
+
                if (hz >= (host->src_clk_freq >> 2)) {
                        div = 0; /* mean div = 1/4 */
                        sclk = host->src_clk_freq >> 2; /* sclk = clk / 4 */
@@ -511,6 +547,14 @@ static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
                        sclk = (host->src_clk_freq >> 2) / div;
                        div = (div >> 1);
                }
+
+               if (timing == MMC_TIMING_MMC_HS400 &&
+                   hz >= (host->src_clk_freq >> 1)) {
+                       sdr_set_bits(host->base + MSDC_CFG,
+                                    MSDC_CFG_HS400_CK_MODE);
+                       sclk = host->src_clk_freq >> 1;
+                       div = 0; /* div is ignore when bit18 is set */
+               }
        } else if (hz >= host->src_clk_freq) {
                mode = 0x1; /* no divisor */
                div = 0;
@@ -532,12 +576,12 @@ static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
                cpu_relax();
        host->sclk = sclk;
        host->mclk = hz;
-       host->ddr = ddr;
+       host->timing = timing;
        /* need because clk changed. */
        msdc_set_timeout(host, host->timeout_ns, host->timeout_clks);
        sdr_set_bits(host->base + MSDC_INTEN, flags);
 
-       dev_dbg(host->dev, "sclk: %d, ddr: %d\n", host->sclk, ddr);
+       dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
 }
 
 static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
@@ -725,11 +769,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
        if (done)
                return true;
 
-       sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
-                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
-                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
-                       MSDC_INTEN_ACMDTMO);
-       writel(cmd->arg, host->base + SDC_ARG);
+       sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
 
        if (cmd->flags & MMC_RSP_PRESENT) {
                if (cmd->flags & MMC_RSP_136) {
@@ -819,10 +859,7 @@ static void msdc_start_command(struct msdc_host *host,
        rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
        mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
 
-       sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
-                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
-                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
-                       MSDC_INTEN_ACMDTMO);
+       sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask);
        writel(cmd->arg, host->base + SDC_ARG);
        writel(rawcmd, host->base + SDC_CMD);
 }
@@ -896,7 +933,7 @@ static void msdc_data_xfer_next(struct msdc_host *host,
                                struct mmc_request *mrq, struct mmc_data *data)
 {
        if (mmc_op_multi(mrq->cmd->opcode) && mrq->stop && !mrq->stop->error &&
-           (!data->bytes_xfered || !mrq->sbc))
+           !mrq->sbc)
                msdc_start_command(host, mrq, mrq->stop);
        else
                msdc_request_done(host, mrq);
@@ -942,6 +979,8 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
 
                        if (events & MSDC_INT_DATTMO)
                                data->error = -ETIMEDOUT;
+                       else if (events & MSDC_INT_DATCRCERR)
+                               data->error = -EILSEQ;
 
                        dev_err(host->dev, "%s: cmd=%d; blocks=%d",
                                __func__, mrq->cmd->opcode, data->blocks);
@@ -1113,10 +1152,12 @@ static void msdc_init_hw(struct msdc_host *host)
 
        writel(0, host->base + MSDC_PAD_TUNE);
        writel(0, host->base + MSDC_IOCON);
-       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
-       writel(0x403c004f, host->base + MSDC_PATCH_BIT);
+       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
+       writel(0x403c0046, host->base + MSDC_PATCH_BIT);
        sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
        writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
+       sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
+
        /* Configure to enable SDIO mode.
         * it's must otherwise sdio cmd5 failed
         */
@@ -1148,11 +1189,14 @@ static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma)
        struct mt_bdma_desc *bd = dma->bd;
        int i;
 
-       memset(gpd, 0, sizeof(struct mt_gpdma_desc));
+       memset(gpd, 0, sizeof(struct mt_gpdma_desc) * 2);
 
        gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */
        gpd->ptr = (u32)dma->bd_addr; /* physical address */
-
+       /* gpd->next is must set for desc DMA
+        * That's why must alloc 2 gpd structure.
+        */
+       gpd->next = (u32)dma->gpd_addr + sizeof(struct mt_gpdma_desc);
        memset(bd, 0, sizeof(struct mt_bdma_desc) * MAX_BD_NUM);
        for (i = 0; i < (MAX_BD_NUM - 1); i++)
                bd[i].next = (u32)dma->bd_addr + sizeof(*bd) * (i + 1);
@@ -1162,20 +1206,16 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
        struct msdc_host *host = mmc_priv(mmc);
        int ret;
-       u32 ddr = 0;
 
        pm_runtime_get_sync(host->dev);
 
-       if (ios->timing == MMC_TIMING_UHS_DDR50 ||
-           ios->timing == MMC_TIMING_MMC_DDR52)
-               ddr = 1;
-
        msdc_set_buswidth(host, ios->bus_width);
 
        /* Suspend/Resume will do power off/on */
        switch (ios->power_mode) {
        case MMC_POWER_UP:
                if (!IS_ERR(mmc->supply.vmmc)) {
+                       msdc_init_hw(host);
                        ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
                                        ios->vdd);
                        if (ret) {
@@ -1206,14 +1246,207 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                break;
        }
 
-       if (host->mclk != ios->clock || host->ddr != ddr)
-               msdc_set_mclk(host, ddr, ios->clock);
+       if (host->mclk != ios->clock || host->timing != ios->timing)
+               msdc_set_mclk(host, ios->timing, ios->clock);
 
 end:
        pm_runtime_mark_last_busy(host->dev);
        pm_runtime_put_autosuspend(host->dev);
 }
 
+static u32 test_delay_bit(u32 delay, u32 bit)
+{
+       bit %= PAD_DELAY_MAX;
+       return delay & (1 << bit);
+}
+
+static int get_delay_len(u32 delay, u32 start_bit)
+{
+       int i;
+
+       for (i = 0; i < (PAD_DELAY_MAX - start_bit); i++) {
+               if (test_delay_bit(delay, start_bit + i) == 0)
+                       return i;
+       }
+       return PAD_DELAY_MAX - start_bit;
+}
+
+static struct msdc_delay_phase get_best_delay(struct msdc_host *host, u32 delay)
+{
+       int start = 0, len = 0;
+       int start_final = 0, len_final = 0;
+       u8 final_phase = 0xff;
+       struct msdc_delay_phase delay_phase;
+
+       if (delay == 0) {
+               dev_err(host->dev, "phase error: [map:%x]\n", delay);
+               delay_phase.final_phase = final_phase;
+               return delay_phase;
+       }
+
+       while (start < PAD_DELAY_MAX) {
+               len = get_delay_len(delay, start);
+               if (len_final < len) {
+                       start_final = start;
+                       len_final = len;
+               }
+               start += len ? len : 1;
+               if (len >= 8 && start_final < 4)
+                       break;
+       }
+
+       /* The rule is that to find the smallest delay cell */
+       if (start_final == 0)
+               final_phase = (start_final + len_final / 3) % PAD_DELAY_MAX;
+       else
+               final_phase = (start_final + len_final / 2) % PAD_DELAY_MAX;
+       dev_info(host->dev, "phase: [map:%x] [maxlen:%d] [final:%d]\n",
+                delay, len_final, final_phase);
+
+       delay_phase.maxlen = len_final;
+       delay_phase.start = start_final;
+       delay_phase.final_phase = final_phase;
+       return delay_phase;
+}
+
+static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
+{
+       struct msdc_host *host = mmc_priv(mmc);
+       u32 rise_delay = 0, fall_delay = 0;
+       struct msdc_delay_phase final_rise_delay, final_fall_delay;
+       u8 final_delay, final_maxlen;
+       int cmd_err;
+       int i;
+
+       sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+       for (i = 0 ; i < PAD_DELAY_MAX; i++) {
+               sdr_set_field(host->base + MSDC_PAD_TUNE,
+                             MSDC_PAD_TUNE_CMDRDLY, i);
+               mmc_send_tuning(mmc, opcode, &cmd_err);
+               if (!cmd_err)
+                       rise_delay |= (1 << i);
+       }
+
+       sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+       for (i = 0; i < PAD_DELAY_MAX; i++) {
+               sdr_set_field(host->base + MSDC_PAD_TUNE,
+                             MSDC_PAD_TUNE_CMDRDLY, i);
+               mmc_send_tuning(mmc, opcode, &cmd_err);
+               if (!cmd_err)
+                       fall_delay |= (1 << i);
+       }
+
+       final_rise_delay = get_best_delay(host, rise_delay);
+       final_fall_delay = get_best_delay(host, fall_delay);
+
+       final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
+       if (final_maxlen == final_rise_delay.maxlen) {
+               sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+               sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
+                             final_rise_delay.final_phase);
+               final_delay = final_rise_delay.final_phase;
+       } else {
+               sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+               sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
+                             final_fall_delay.final_phase);
+               final_delay = final_fall_delay.final_phase;
+       }
+
+       return final_delay == 0xff ? -EIO : 0;
+}
+
+static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
+{
+       struct msdc_host *host = mmc_priv(mmc);
+       u32 rise_delay = 0, fall_delay = 0;
+       struct msdc_delay_phase final_rise_delay, final_fall_delay;
+       u8 final_delay, final_maxlen;
+       int i, ret;
+
+       sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
+       sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+       for (i = 0 ; i < PAD_DELAY_MAX; i++) {
+               sdr_set_field(host->base + MSDC_PAD_TUNE,
+                             MSDC_PAD_TUNE_DATRRDLY, i);
+               ret = mmc_send_tuning(mmc, opcode, NULL);
+               if (!ret)
+                       rise_delay |= (1 << i);
+       }
+
+       sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
+       sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+       for (i = 0; i < PAD_DELAY_MAX; i++) {
+               sdr_set_field(host->base + MSDC_PAD_TUNE,
+                             MSDC_PAD_TUNE_DATRRDLY, i);
+               ret = mmc_send_tuning(mmc, opcode, NULL);
+               if (!ret)
+                       fall_delay |= (1 << i);
+       }
+
+       final_rise_delay = get_best_delay(host, rise_delay);
+       final_fall_delay = get_best_delay(host, fall_delay);
+
+       final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
+       /* Rising edge is more stable, prefer to use it */
+       if (final_rise_delay.maxlen >= 10)
+               final_maxlen = final_rise_delay.maxlen;
+       if (final_maxlen == final_rise_delay.maxlen) {
+               sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
+               sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+               sdr_set_field(host->base + MSDC_PAD_TUNE,
+                             MSDC_PAD_TUNE_DATRRDLY,
+                             final_rise_delay.final_phase);
+               final_delay = final_rise_delay.final_phase;
+       } else {
+               sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
+               sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+               sdr_set_field(host->base + MSDC_PAD_TUNE,
+                             MSDC_PAD_TUNE_DATRRDLY,
+                             final_fall_delay.final_phase);
+               final_delay = final_fall_delay.final_phase;
+       }
+
+       return final_delay == 0xff ? -EIO : 0;
+}
+
+static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+       struct msdc_host *host = mmc_priv(mmc);
+       int ret;
+
+       pm_runtime_get_sync(host->dev);
+       ret = msdc_tune_response(mmc, opcode);
+       if (ret == -EIO) {
+               dev_err(host->dev, "Tune response fail!\n");
+               goto out;
+       }
+       ret = msdc_tune_data(mmc, opcode);
+       if (ret == -EIO)
+               dev_err(host->dev, "Tune data fail!\n");
+
+out:
+       pm_runtime_mark_last_busy(host->dev);
+       pm_runtime_put_autosuspend(host->dev);
+       return ret;
+}
+
+static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct msdc_host *host = mmc_priv(mmc);
+
+       writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
+       return 0;
+}
+
+static void msdc_hw_reset(struct mmc_host *mmc)
+{
+       struct msdc_host *host = mmc_priv(mmc);
+
+       sdr_set_bits(host->base + EMMC_IOCON, 1);
+       udelay(10); /* 10us is enough */
+       sdr_clr_bits(host->base + EMMC_IOCON, 1);
+}
+
 static struct mmc_host_ops mt_msdc_ops = {
        .post_req = msdc_post_req,
        .pre_req = msdc_pre_req,
@@ -1221,6 +1454,9 @@ static struct mmc_host_ops mt_msdc_ops = {
        .set_ios = msdc_ops_set_ios,
        .start_signal_voltage_switch = msdc_ops_switch_volt,
        .card_busy = msdc_card_busy,
+       .execute_tuning = msdc_execute_tuning,
+       .prepare_hs400_tuning = msdc_prepare_hs400_tuning,
+       .hw_reset = msdc_hw_reset,
 };
 
 static int msdc_drv_probe(struct platform_device *pdev)
@@ -1294,6 +1530,11 @@ static int msdc_drv_probe(struct platform_device *pdev)
                goto host_free;
        }
 
+       if (!of_property_read_u32(pdev->dev.of_node, "hs400-ds-delay",
+                                 &host->hs400_ds_delay))
+               dev_dbg(&pdev->dev, "hs400-ds-delay: %x\n",
+                       host->hs400_ds_delay);
+
        host->dev = &pdev->dev;
        host->mmc = mmc;
        host->src_clk_freq = clk_get_rate(host->src_clk);
@@ -1302,6 +1543,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
        mmc->f_min = host->src_clk_freq / (4 * 255);
 
        mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
+       mmc->caps |= MMC_CAP_RUNTIME_RESUME;
        /* MMC core transfer sizes tunable parameters */
        mmc->max_segs = MAX_BD_NUM;
        mmc->max_seg_size = BDMA_DESC_BUFLEN;
@@ -1313,7 +1555,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
 
        host->timeout_clks = 3 * 1048576;
        host->dma.gpd = dma_alloc_coherent(&pdev->dev,
-                               sizeof(struct mt_gpdma_desc),
+                               2 * sizeof(struct mt_gpdma_desc),
                                &host->dma.gpd_addr, GFP_KERNEL);
        host->dma.bd = dma_alloc_coherent(&pdev->dev,
                                MAX_BD_NUM * sizeof(struct mt_bdma_desc),
@@ -1354,7 +1596,7 @@ release:
 release_mem:
        if (host->dma.gpd)
                dma_free_coherent(&pdev->dev,
-                       sizeof(struct mt_gpdma_desc),
+                       2 * sizeof(struct mt_gpdma_desc),
                        host->dma.gpd, host->dma.gpd_addr);
        if (host->dma.bd)
                dma_free_coherent(&pdev->dev,
@@ -1403,6 +1645,8 @@ static void msdc_save_reg(struct msdc_host *host)
        host->save_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
        host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
        host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
+       host->save_para.pad_ds_tune = readl(host->base + PAD_DS_TUNE);
+       host->save_para.emmc50_cfg0 = readl(host->base + EMMC50_CFG0);
 }
 
 static void msdc_restore_reg(struct msdc_host *host)
@@ -1413,6 +1657,8 @@ static void msdc_restore_reg(struct msdc_host *host)
        writel(host->save_para.pad_tune, host->base + MSDC_PAD_TUNE);
        writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
        writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
+       writel(host->save_para.pad_ds_tune, host->base + PAD_DS_TUNE);
+       writel(host->save_para.emmc50_cfg0, host->base + EMMC50_CFG0);
 }
 
 static int msdc_runtime_suspend(struct device *dev)
index b763b11ed9e1e72320a2d8e4a8d9e393b229766e..b9958a123594a6fa9d0b0075a61a92b1e0f7916b 100644 (file)
@@ -1490,6 +1490,7 @@ static const struct of_device_id mmc_omap_match[] = {
        { .compatible = "ti,omap2420-mmc", },
        { },
 };
+MODULE_DEVICE_TABLE(of, mmc_omap_match);
 #endif
 
 static struct platform_driver mmc_omap_driver = {
index 22d929fa3371adbf87c2efa744b0272701d72741..f6047fc9406204d6ee672b74544d0ec6866308cd 100644 (file)
@@ -207,7 +207,9 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
        .caps2   = MMC_CAP2_HC_ERASE_SZ,
        .flags   = SDHCI_ACPI_RUNTIME_PM,
        .quirks  = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
-       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC,
+       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+                  SDHCI_QUIRK2_STOP_WITH_TC |
+                  SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
        .probe_slot     = sdhci_acpi_emmc_probe_slot,
 };
 
@@ -239,6 +241,9 @@ struct sdhci_acpi_uid_slot {
 };
 
 static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
+       { "80865ACA", NULL, &sdhci_acpi_slot_int_sd },
+       { "80865ACC", NULL, &sdhci_acpi_slot_int_emmc },
+       { "80865AD0", NULL, &sdhci_acpi_slot_int_sdio },
        { "80860F14" , "1" , &sdhci_acpi_slot_int_emmc },
        { "80860F14" , "3" , &sdhci_acpi_slot_int_sd   },
        { "80860F16" , NULL, &sdhci_acpi_slot_int_sd   },
@@ -247,11 +252,15 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
        { "INT33C6"  , NULL, &sdhci_acpi_slot_int_sdio },
        { "INT3436"  , NULL, &sdhci_acpi_slot_int_sdio },
        { "INT344D"  , NULL, &sdhci_acpi_slot_int_sdio },
+       { "PNP0FFF"  , "3" , &sdhci_acpi_slot_int_sd   },
        { "PNP0D40"  },
        { },
 };
 
 static const struct acpi_device_id sdhci_acpi_ids[] = {
+       { "80865ACA" },
+       { "80865ACC" },
+       { "80865AD0" },
        { "80860F14" },
        { "80860F16" },
        { "INT33BB"  },
index 2bd90fb35c75e5b5c03f243aabd1edc99dce04d5..00a8a40a372954fdcbbb1d4cd92b3ab1cc953765 100644 (file)
@@ -273,7 +273,7 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
                host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
 
        dev_dbg(dev, "is_8bit=%c\n",
-               (host->mmc->caps | MMC_CAP_8_BIT_DATA) ? 'Y' : 'N');
+               (host->mmc->caps & MMC_CAP_8_BIT_DATA) ? 'Y' : 'N');
 
        ret = sdhci_bcm_kona_sd_reset(host);
        if (ret)
index 886d230f41d07357f9fe624b9a94de428789e962..1f1582f6cccbb3454f0824e9dcf39e170a76f395 100644 (file)
@@ -759,7 +759,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
        min = ESDHC_TUNE_CTRL_MIN;
        while (min < ESDHC_TUNE_CTRL_MAX) {
                esdhc_prepare_tuning(host, min);
-               if (!mmc_send_tuning(host->mmc))
+               if (!mmc_send_tuning(host->mmc, opcode, NULL))
                        break;
                min += ESDHC_TUNE_CTRL_STEP;
        }
@@ -768,7 +768,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
        max = min + ESDHC_TUNE_CTRL_STEP;
        while (max < ESDHC_TUNE_CTRL_MAX) {
                esdhc_prepare_tuning(host, max);
-               if (mmc_send_tuning(host->mmc)) {
+               if (mmc_send_tuning(host->mmc, opcode, NULL)) {
                        max -= ESDHC_TUNE_CTRL_STEP;
                        break;
                }
@@ -778,7 +778,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
        /* use average delay to get the best timing */
        avg = (min + max) / 2;
        esdhc_prepare_tuning(host, avg);
-       ret = mmc_send_tuning(host->mmc);
+       ret = mmc_send_tuning(host->mmc, opcode, NULL);
        esdhc_post_tuning(host);
 
        dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
index 163ac9974d9101dcf28220263a88cef8f43b7750..de132e28175342ea5869f1da1c003a0d16c2b6a7 100644 (file)
@@ -24,6 +24,8 @@
                                SDHCI_QUIRK_PIO_NEEDS_DELAY | \
                                SDHCI_QUIRK_NO_HISPD_BIT)
 
+#define ESDHC_PROCTL           0x28
+
 #define ESDHC_SYSTEM_CONTROL   0x2c
 #define ESDHC_CLOCK_MASK       0x0000fff0
 #define ESDHC_PREDIV_SHIFT     8
index 4bcee033fedaf520d95433f03e652f0d7a786924..4695bee203ea9306eff0c1a9d01f69afc9ce7d9b 100644 (file)
@@ -373,7 +373,7 @@ retry:
                if (rc)
                        return rc;
 
-               rc = mmc_send_tuning(mmc);
+               rc = mmc_send_tuning(mmc, opcode, NULL);
                if (!rc) {
                        /* Tuning is successful at this tuning point */
                        tuned_phases[tuned_phase_cnt++] = phase;
index a0f05de5409f7d0c42f3d2457ff4dc1bb0e74f3c..06d0b50dfe71d2ece8e7d8ee662af4c159860200 100644 (file)
@@ -111,7 +111,6 @@ static int sdhci_at91_probe(struct platform_device *pdev)
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to set gck");
                goto hclock_disable_unprepare;
-               return -EINVAL;
        }
        /*
         * We need to check if we have the requested rate for gck because in
index 653f335bef1516ca9aba102e1f3ab21d99a754ce..90e94a028a49a39169b754d43050d4937d4b52eb 100644 (file)
 
 #define VENDOR_V_22    0x12
 #define VENDOR_V_23    0x13
-static u32 esdhc_readl(struct sdhci_host *host, int reg)
+
+struct sdhci_esdhc {
+       u8 vendor_ver;
+       u8 spec_ver;
+};
+
+/**
+ * esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register
+ *                    to make it compatible with SD spec.
+ *
+ * @host: pointer to sdhci_host
+ * @spec_reg: SD spec register address
+ * @value: 32bit eSDHC register value on spec_reg address
+ *
+ * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC
+ * registers are 32 bits. There are differences in register size, register
+ * address, register function, bit position and function between eSDHC spec
+ * and SD spec.
+ *
+ * Return a fixed up register value
+ */
+static u32 esdhc_readl_fixup(struct sdhci_host *host,
+                                    int spec_reg, u32 value)
 {
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_esdhc *esdhc = pltfm_host->priv;
        u32 ret;
 
-       ret = in_be32(host->ioaddr + reg);
        /*
         * The bit of ADMA flag in eSDHC is not compatible with standard
         * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is
         * supported by eSDHC.
         * And for many FSL eSDHC controller, the reset value of field
-        * SDHCI_CAN_DO_ADMA1 is one, but some of them can't support ADMA,
+        * SDHCI_CAN_DO_ADMA1 is 1, but some of them can't support ADMA,
         * only these vendor version is greater than 2.2/0x12 support ADMA.
-        * For FSL eSDHC, must aligned 4-byte, so use 0xFC to read the
-        * the verdor version number, oxFE is SDHCI_HOST_VERSION.
         */
-       if ((reg == SDHCI_CAPABILITIES) && (ret & SDHCI_CAN_DO_ADMA1)) {
-               u32 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS);
-               tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
-               if (tmp > VENDOR_V_22)
-                       ret |= SDHCI_CAN_DO_ADMA2;
+       if ((spec_reg == SDHCI_CAPABILITIES) && (value & SDHCI_CAN_DO_ADMA1)) {
+               if (esdhc->vendor_ver > VENDOR_V_22) {
+                       ret = value | SDHCI_CAN_DO_ADMA2;
+                       return ret;
+               }
        }
-
+       ret = value;
        return ret;
 }
 
-static u16 esdhc_readw(struct sdhci_host *host, int reg)
+static u16 esdhc_readw_fixup(struct sdhci_host *host,
+                                    int spec_reg, u32 value)
 {
        u16 ret;
-       int base = reg & ~0x3;
-       int shift = (reg & 0x2) * 8;
+       int shift = (spec_reg & 0x2) * 8;
 
-       if (unlikely(reg == SDHCI_HOST_VERSION))
-               ret = in_be32(host->ioaddr + base) & 0xffff;
+       if (spec_reg == SDHCI_HOST_VERSION)
+               ret = value & 0xffff;
        else
-               ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff;
+               ret = (value >> shift) & 0xffff;
        return ret;
 }
 
-static u8 esdhc_readb(struct sdhci_host *host, int reg)
+static u8 esdhc_readb_fixup(struct sdhci_host *host,
+                                    int spec_reg, u32 value)
 {
-       int base = reg & ~0x3;
-       int shift = (reg & 0x3) * 8;
-       u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff;
+       u8 ret;
+       u8 dma_bits;
+       int shift = (spec_reg & 0x3) * 8;
+
+       ret = (value >> shift) & 0xff;
 
        /*
         * "DMA select" locates at offset 0x28 in SD specification, but on
         * P5020 or P3041, it locates at 0x29.
         */
-       if (reg == SDHCI_HOST_CONTROL) {
-               u32 dma_bits;
-
-               dma_bits = in_be32(host->ioaddr + reg);
+       if (spec_reg == SDHCI_HOST_CONTROL) {
                /* DMA select is 22,23 bits in Protocol Control Register */
-               dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK;
-
+               dma_bits = (value >> 5) & SDHCI_CTRL_DMA_MASK;
                /* fixup the result */
                ret &= ~SDHCI_CTRL_DMA_MASK;
                ret |= dma_bits;
        }
-
        return ret;
 }
 
-static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
+/**
+ * esdhc_write*_fixup - Fixup the SD spec register value so that it could be
+ *                     written into eSDHC register.
+ *
+ * @host: pointer to sdhci_host
+ * @spec_reg: SD spec register address
+ * @value: 8/16/32bit SD spec register value that would be written
+ * @old_value: 32bit eSDHC register value on spec_reg address
+ *
+ * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC
+ * registers are 32 bits. There are differences in register size, register
+ * address, register function, bit position and function between eSDHC spec
+ * and SD spec.
+ *
+ * Return a fixed up register value
+ */
+static u32 esdhc_writel_fixup(struct sdhci_host *host,
+                                    int spec_reg, u32 value, u32 old_value)
 {
+       u32 ret;
+
        /*
-        * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE]
-        * when SYSCTL[RSTD]) is set for some special operations.
-        * No any impact other operation.
+        * Enabling IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE]
+        * when SYSCTL[RSTD] is set for some special operations.
+        * No any impact on other operation.
         */
-       if (reg == SDHCI_INT_ENABLE)
-               val |= SDHCI_INT_BLK_GAP;
-       sdhci_be32bs_writel(host, val, reg);
+       if (spec_reg == SDHCI_INT_ENABLE)
+               ret = value | SDHCI_INT_BLK_GAP;
+       else
+               ret = value;
+
+       return ret;
 }
 
-static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
+static u32 esdhc_writew_fixup(struct sdhci_host *host,
+                                    int spec_reg, u16 value, u32 old_value)
 {
-       if (reg == SDHCI_BLOCK_SIZE) {
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       int shift = (spec_reg & 0x2) * 8;
+       u32 ret;
+
+       switch (spec_reg) {
+       case SDHCI_TRANSFER_MODE:
+               /*
+                * Postpone this write, we must do it together with a
+                * command write that is down below. Return old value.
+                */
+               pltfm_host->xfer_mode_shadow = value;
+               return old_value;
+       case SDHCI_COMMAND:
+               ret = (value << 16) | pltfm_host->xfer_mode_shadow;
+               return ret;
+       }
+
+       ret = old_value & (~(0xffff << shift));
+       ret |= (value << shift);
+
+       if (spec_reg == SDHCI_BLOCK_SIZE) {
                /*
                 * Two last DMA bits are reserved, and first one is used for
                 * non-standard blksz of 4096 bytes that we don't support
                 * yet. So clear the DMA boundary bits.
                 */
-               val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
+               ret &= (~SDHCI_MAKE_BLKSZ(0x7, 0));
        }
-       sdhci_be32bs_writew(host, val, reg);
+       return ret;
 }
 
-static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
+static u32 esdhc_writeb_fixup(struct sdhci_host *host,
+                                    int spec_reg, u8 value, u32 old_value)
 {
+       u32 ret;
+       u32 dma_bits;
+       u8 tmp;
+       int shift = (spec_reg & 0x3) * 8;
+
+       /*
+        * eSDHC doesn't have a standard power control register, so we do
+        * nothing here to avoid incorrect operation.
+        */
+       if (spec_reg == SDHCI_POWER_CONTROL)
+               return old_value;
        /*
         * "DMA select" location is offset 0x28 in SD specification, but on
         * P5020 or P3041, it's located at 0x29.
         */
-       if (reg == SDHCI_HOST_CONTROL) {
-               u32 dma_bits;
-
+       if (spec_reg == SDHCI_HOST_CONTROL) {
                /*
                 * If host control register is not standard, exit
                 * this function
                 */
                if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL)
-                       return;
+                       return old_value;
 
                /* DMA select is 22,23 bits in Protocol Control Register */
-               dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5;
-               clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5,
-                       dma_bits);
-               val &= ~SDHCI_CTRL_DMA_MASK;
-               val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK;
+               dma_bits = (value & SDHCI_CTRL_DMA_MASK) << 5;
+               ret = (old_value & (~(SDHCI_CTRL_DMA_MASK << 5))) | dma_bits;
+               tmp = (value & (~SDHCI_CTRL_DMA_MASK)) |
+                     (old_value & SDHCI_CTRL_DMA_MASK);
+               ret = (ret & (~0xff)) | tmp;
+
+               /* Prevent SDHCI core from writing reserved bits (e.g. HISPD) */
+               ret &= ~ESDHC_HOST_CONTROL_RES;
+               return ret;
        }
 
-       /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */
-       if (reg == SDHCI_HOST_CONTROL)
-               val &= ~ESDHC_HOST_CONTROL_RES;
-       sdhci_be32bs_writeb(host, val, reg);
+       ret = (old_value & (~(0xff << shift))) | (value << shift);
+       return ret;
+}
+
+static u32 esdhc_be_readl(struct sdhci_host *host, int reg)
+{
+       u32 ret;
+       u32 value;
+
+       value = ioread32be(host->ioaddr + reg);
+       ret = esdhc_readl_fixup(host, reg, value);
+
+       return ret;
+}
+
+static u32 esdhc_le_readl(struct sdhci_host *host, int reg)
+{
+       u32 ret;
+       u32 value;
+
+       value = ioread32(host->ioaddr + reg);
+       ret = esdhc_readl_fixup(host, reg, value);
+
+       return ret;
+}
+
+static u16 esdhc_be_readw(struct sdhci_host *host, int reg)
+{
+       u16 ret;
+       u32 value;
+       int base = reg & ~0x3;
+
+       value = ioread32be(host->ioaddr + base);
+       ret = esdhc_readw_fixup(host, reg, value);
+       return ret;
+}
+
+static u16 esdhc_le_readw(struct sdhci_host *host, int reg)
+{
+       u16 ret;
+       u32 value;
+       int base = reg & ~0x3;
+
+       value = ioread32(host->ioaddr + base);
+       ret = esdhc_readw_fixup(host, reg, value);
+       return ret;
+}
+
+static u8 esdhc_be_readb(struct sdhci_host *host, int reg)
+{
+       u8 ret;
+       u32 value;
+       int base = reg & ~0x3;
+
+       value = ioread32be(host->ioaddr + base);
+       ret = esdhc_readb_fixup(host, reg, value);
+       return ret;
+}
+
+static u8 esdhc_le_readb(struct sdhci_host *host, int reg)
+{
+       u8 ret;
+       u32 value;
+       int base = reg & ~0x3;
+
+       value = ioread32(host->ioaddr + base);
+       ret = esdhc_readb_fixup(host, reg, value);
+       return ret;
+}
+
+static void esdhc_be_writel(struct sdhci_host *host, u32 val, int reg)
+{
+       u32 value;
+
+       value = esdhc_writel_fixup(host, reg, val, 0);
+       iowrite32be(value, host->ioaddr + reg);
+}
+
+static void esdhc_le_writel(struct sdhci_host *host, u32 val, int reg)
+{
+       u32 value;
+
+       value = esdhc_writel_fixup(host, reg, val, 0);
+       iowrite32(value, host->ioaddr + reg);
+}
+
+static void esdhc_be_writew(struct sdhci_host *host, u16 val, int reg)
+{
+       int base = reg & ~0x3;
+       u32 value;
+       u32 ret;
+
+       value = ioread32be(host->ioaddr + base);
+       ret = esdhc_writew_fixup(host, reg, val, value);
+       if (reg != SDHCI_TRANSFER_MODE)
+               iowrite32be(ret, host->ioaddr + base);
+}
+
+static void esdhc_le_writew(struct sdhci_host *host, u16 val, int reg)
+{
+       int base = reg & ~0x3;
+       u32 value;
+       u32 ret;
+
+       value = ioread32(host->ioaddr + base);
+       ret = esdhc_writew_fixup(host, reg, val, value);
+       if (reg != SDHCI_TRANSFER_MODE)
+               iowrite32(ret, host->ioaddr + base);
+}
+
+static void esdhc_be_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+       int base = reg & ~0x3;
+       u32 value;
+       u32 ret;
+
+       value = ioread32be(host->ioaddr + base);
+       ret = esdhc_writeb_fixup(host, reg, val, value);
+       iowrite32be(ret, host->ioaddr + base);
+}
+
+static void esdhc_le_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+       int base = reg & ~0x3;
+       u32 value;
+       u32 ret;
+
+       value = ioread32(host->ioaddr + base);
+       ret = esdhc_writeb_fixup(host, reg, val, value);
+       iowrite32(ret, host->ioaddr + base);
 }
 
 /*
@@ -149,19 +351,17 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
  * For Continue, apply soft reset for data(SYSCTL[RSTD]);
  * and re-issue the entire read transaction from beginning.
  */
-static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask)
+static void esdhc_of_adma_workaround(struct sdhci_host *host, u32 intmask)
 {
-       u32 tmp;
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_esdhc *esdhc = pltfm_host->priv;
        bool applicable;
        dma_addr_t dmastart;
        dma_addr_t dmanow;
 
-       tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS);
-       tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
-
        applicable = (intmask & SDHCI_INT_DATA_END) &&
-               (intmask & SDHCI_INT_BLK_GAP) &&
-               (tmp == VENDOR_V_23);
+                    (intmask & SDHCI_INT_BLK_GAP) &&
+                    (esdhc->vendor_ver == VENDOR_V_23);
        if (!applicable)
                return;
 
@@ -179,7 +379,11 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask)
 
 static int esdhc_of_enable_dma(struct sdhci_host *host)
 {
-       setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
+       u32 value;
+
+       value = sdhci_readl(host, ESDHC_DMA_SYSCTL);
+       value |= ESDHC_DMA_SNOOP;
+       sdhci_writel(host, value, ESDHC_DMA_SYSCTL);
        return 0;
 }
 
@@ -199,6 +403,8 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
 
 static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
 {
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_esdhc *esdhc = pltfm_host->priv;
        int pre_div = 1;
        int div = 1;
        u32 temp;
@@ -209,9 +415,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
                return;
 
        /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */
-       temp = esdhc_readw(host, SDHCI_HOST_VERSION);
-       temp = (temp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
-       if (temp < VENDOR_V_23)
+       if (esdhc->vendor_ver < VENDOR_V_23)
                pre_div = 2;
 
        /* Workaround to reduce the clock frequency for p1010 esdhc */
@@ -247,39 +451,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
        mdelay(1);
 }
 
-static void esdhc_of_platform_init(struct sdhci_host *host)
-{
-       u32 vvn;
-
-       vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS);
-       vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
-       if (vvn == VENDOR_V_22)
-               host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23;
-
-       if (vvn > VENDOR_V_22)
-               host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
-}
-
 static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
 {
        u32 ctrl;
 
+       ctrl = sdhci_readl(host, ESDHC_PROCTL);
+       ctrl &= (~ESDHC_CTRL_BUSWIDTH_MASK);
        switch (width) {
        case MMC_BUS_WIDTH_8:
-               ctrl = ESDHC_CTRL_8BITBUS;
+               ctrl |= ESDHC_CTRL_8BITBUS;
                break;
 
        case MMC_BUS_WIDTH_4:
-               ctrl = ESDHC_CTRL_4BITBUS;
+               ctrl |= ESDHC_CTRL_4BITBUS;
                break;
 
        default:
-               ctrl = 0;
                break;
        }
 
-       clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL,
-                       ESDHC_CTRL_BUSWIDTH_MASK, ctrl);
+       sdhci_writel(host, ctrl, ESDHC_PROCTL);
 }
 
 static void esdhc_reset(struct sdhci_host *host, u8 mask)
@@ -290,32 +481,13 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
        sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
 }
 
-static const struct sdhci_ops sdhci_esdhc_ops = {
-       .read_l = esdhc_readl,
-       .read_w = esdhc_readw,
-       .read_b = esdhc_readb,
-       .write_l = esdhc_writel,
-       .write_w = esdhc_writew,
-       .write_b = esdhc_writeb,
-       .set_clock = esdhc_of_set_clock,
-       .enable_dma = esdhc_of_enable_dma,
-       .get_max_clock = esdhc_of_get_max_clock,
-       .get_min_clock = esdhc_of_get_min_clock,
-       .platform_init = esdhc_of_platform_init,
-       .adma_workaround = esdhci_of_adma_workaround,
-       .set_bus_width = esdhc_pltfm_set_bus_width,
-       .reset = esdhc_reset,
-       .set_uhs_signaling = sdhci_set_uhs_signaling,
-};
-
 #ifdef CONFIG_PM
-
 static u32 esdhc_proctl;
 static int esdhc_of_suspend(struct device *dev)
 {
        struct sdhci_host *host = dev_get_drvdata(dev);
 
-       esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL);
+       esdhc_proctl = sdhci_readl(host, SDHCI_HOST_CONTROL);
 
        return sdhci_suspend_host(host);
 }
@@ -328,9 +500,8 @@ static int esdhc_of_resume(struct device *dev)
        if (ret == 0) {
                /* Isn't this already done by sdhci_resume_host() ? --rmk */
                esdhc_of_enable_dma(host);
-               sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
+               sdhci_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
        }
-
        return ret;
 }
 
@@ -343,37 +514,103 @@ static const struct dev_pm_ops esdhc_pmops = {
 #define ESDHC_PMOPS NULL
 #endif
 
-static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {
-       /*
-        * card detection could be handled via GPIO
-        * eSDHC cannot support End Attribute in NOP ADMA descriptor
-        */
+static const struct sdhci_ops sdhci_esdhc_be_ops = {
+       .read_l = esdhc_be_readl,
+       .read_w = esdhc_be_readw,
+       .read_b = esdhc_be_readb,
+       .write_l = esdhc_be_writel,
+       .write_w = esdhc_be_writew,
+       .write_b = esdhc_be_writeb,
+       .set_clock = esdhc_of_set_clock,
+       .enable_dma = esdhc_of_enable_dma,
+       .get_max_clock = esdhc_of_get_max_clock,
+       .get_min_clock = esdhc_of_get_min_clock,
+       .adma_workaround = esdhc_of_adma_workaround,
+       .set_bus_width = esdhc_pltfm_set_bus_width,
+       .reset = esdhc_reset,
+       .set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static const struct sdhci_ops sdhci_esdhc_le_ops = {
+       .read_l = esdhc_le_readl,
+       .read_w = esdhc_le_readw,
+       .read_b = esdhc_le_readb,
+       .write_l = esdhc_le_writel,
+       .write_w = esdhc_le_writew,
+       .write_b = esdhc_le_writeb,
+       .set_clock = esdhc_of_set_clock,
+       .enable_dma = esdhc_of_enable_dma,
+       .get_max_clock = esdhc_of_get_max_clock,
+       .get_min_clock = esdhc_of_get_min_clock,
+       .adma_workaround = esdhc_of_adma_workaround,
+       .set_bus_width = esdhc_pltfm_set_bus_width,
+       .reset = esdhc_reset,
+       .set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = {
        .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
                | SDHCI_QUIRK_NO_CARD_NO_RESET
                | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
-       .ops = &sdhci_esdhc_ops,
+       .ops = &sdhci_esdhc_be_ops,
 };
 
+static const struct sdhci_pltfm_data sdhci_esdhc_le_pdata = {
+       .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
+               | SDHCI_QUIRK_NO_CARD_NO_RESET
+               | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+       .ops = &sdhci_esdhc_le_ops,
+};
+
+static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
+{
+       struct sdhci_pltfm_host *pltfm_host;
+       struct sdhci_esdhc *esdhc;
+       u16 host_ver;
+
+       pltfm_host = sdhci_priv(host);
+       esdhc = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_esdhc),
+                            GFP_KERNEL);
+
+       host_ver = sdhci_readw(host, SDHCI_HOST_VERSION);
+       esdhc->vendor_ver = (host_ver & SDHCI_VENDOR_VER_MASK) >>
+                            SDHCI_VENDOR_VER_SHIFT;
+       esdhc->spec_ver = host_ver & SDHCI_SPEC_VER_MASK;
+
+       pltfm_host->priv = esdhc;
+}
+
 static int sdhci_esdhc_probe(struct platform_device *pdev)
 {
        struct sdhci_host *host;
        struct device_node *np;
        int ret;
 
-       host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0);
+       np = pdev->dev.of_node;
+
+       if (of_get_property(np, "little-endian", NULL))
+               host = sdhci_pltfm_init(pdev, &sdhci_esdhc_le_pdata, 0);
+       else
+               host = sdhci_pltfm_init(pdev, &sdhci_esdhc_be_pdata, 0);
+
        if (IS_ERR(host))
                return PTR_ERR(host);
 
+       esdhc_init(pdev, host);
+
        sdhci_get_of_property(pdev);
 
-       np = pdev->dev.of_node;
        if (of_device_is_compatible(np, "fsl,p5040-esdhc") ||
            of_device_is_compatible(np, "fsl,p5020-esdhc") ||
            of_device_is_compatible(np, "fsl,p4080-esdhc") ||
            of_device_is_compatible(np, "fsl,p1020-esdhc") ||
-           of_device_is_compatible(np, "fsl,t1040-esdhc"))
+           of_device_is_compatible(np, "fsl,t1040-esdhc") ||
+           of_device_is_compatible(np, "fsl,ls1021a-esdhc"))
                host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
 
+       if (of_device_is_compatible(np, "fsl,ls1021a-esdhc"))
+               host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
+
        if (of_device_is_compatible(np, "fsl,p2020-esdhc")) {
                /*
                 * Freescale messed up with P2020 as it has a non-standard
similarity index 96%
rename from drivers/mmc/host/sdhci-pci.c
rename to drivers/mmc/host/sdhci-pci-core.c
index b3b0a3e4fca1652e3ceb1759c0bcbd13bcc1d22e..cf7ad458b4f44fe60beb0a614882fbb985aede37 100644 (file)
@@ -444,11 +444,7 @@ static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
        else
                scratch &= ~0x47;
 
-       ret = pci_write_config_byte(chip->pdev, 0xAE, scratch);
-       if (ret)
-               return ret;
-
-       return 0;
+       return pci_write_config_byte(chip->pdev, 0xAE, scratch);
 }
 
 static int jmicron_probe(struct sdhci_pci_chip *chip)
@@ -1112,6 +1108,62 @@ static const struct pci_device_id pci_ids[] = {
                .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_sd,
        },
 
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_DNV_EMMC,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_emmc,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_BXT_EMMC,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_emmc,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_BXT_SDIO,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_sdio,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_BXT_SD,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_sd,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_APL_EMMC,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_emmc,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_APL_SDIO,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_sdio,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_APL_SD,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_sd,
+       },
+
        {
                .vendor         = PCI_VENDOR_ID_O2,
                .device         = PCI_DEVICE_ID_O2_8120,
index e2ec108dba0e8220e1d23804f11b80ccfa71414f..d48f03104b5be39a978bdbe9d1ae7484f70beaca 100644 (file)
@@ -60,7 +60,7 @@ static void o2_pci_led_enable(struct sdhci_pci_chip *chip)
 
 }
 
-void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip)
+static void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip)
 {
        u32 scratch_32;
        int ret;
@@ -145,7 +145,6 @@ void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip)
        scratch_32 |= 0x00080000;
        pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32);
 }
-EXPORT_SYMBOL_GPL(sdhci_pci_o2_fujin2_pci_init);
 
 int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
 {
@@ -179,7 +178,6 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe_slot);
 
 int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
 {
@@ -385,11 +383,9 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe);
 
 int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
 {
        sdhci_pci_o2_probe(chip);
        return 0;
 }
-EXPORT_SYMBOL_GPL(sdhci_pci_o2_resume);
index f7ffc908d9a0d2f83b13a7a00e09c635aceb5079..770f53857211cc445853359eafc33a88cb94eb82 100644 (file)
@@ -64,8 +64,6 @@
 #define O2_SD_VENDOR_SETTING   0x110
 #define O2_SD_VENDOR_SETTING2  0x1C8
 
-extern void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip);
-
 extern int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot);
 
 extern int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip);
index 541f1cad5247f0f4ea04d18b9c89367e3f1801ba..d1a0b4db60db41d67ad7d0e34652f2d13d2d3507 100644 (file)
 #define PCI_DEVICE_ID_INTEL_SPT_EMMC   0x9d2b
 #define PCI_DEVICE_ID_INTEL_SPT_SDIO   0x9d2c
 #define PCI_DEVICE_ID_INTEL_SPT_SD     0x9d2d
+#define PCI_DEVICE_ID_INTEL_DNV_EMMC   0x19db
+#define PCI_DEVICE_ID_INTEL_BXT_SD     0x0aca
+#define PCI_DEVICE_ID_INTEL_BXT_EMMC   0x0acc
+#define PCI_DEVICE_ID_INTEL_BXT_SDIO   0x0ad0
+#define PCI_DEVICE_ID_INTEL_APL_SD     0x5aca
+#define PCI_DEVICE_ID_INTEL_APL_EMMC   0x5acc
+#define PCI_DEVICE_ID_INTEL_APL_SDIO   0x5ad0
 
 /*
  * PCI registers
index a207f5aaf62f53d7aa4db3699b4d695f9ce5d625..87fb5ea8ebe7cae575b8281f07c630ce03ce2223 100644 (file)
@@ -71,9 +71,7 @@ void sdhci_get_of_property(struct platform_device *pdev)
        struct device_node *np = pdev->dev.of_node;
        struct sdhci_host *host = platform_get_drvdata(pdev);
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-       const __be32 *clk;
        u32 bus_width;
-       int size;
 
        if (of_get_property(np, "sdhci,auto-cmd12", NULL))
                host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
@@ -101,9 +99,7 @@ void sdhci_get_of_property(struct platform_device *pdev)
            of_device_is_compatible(np, "fsl,mpc8536-esdhc"))
                host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
 
-       clk = of_get_property(np, "clock-frequency", &size);
-       if (clk && size == sizeof(*clk) && *clk)
-               pltfm_host->clock = be32_to_cpup(clk);
+       of_property_read_u32(np, "clock-frequency", &pltfm_host->clock);
 
        if (of_find_property(np, "keep-power-in-suspend", NULL))
                host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
index 884294576356d4c87e3869162186aadbc24b4f6a..34866f668dd7b7aee1b943d2001355ea9652c9d2 100644 (file)
@@ -50,7 +50,8 @@ static u32 sdhci_sirf_readl_le(struct sdhci_host *host, int reg)
        if (unlikely((reg == SDHCI_CAPABILITIES_1) &&
                        (host->mmc->caps & MMC_CAP_UHS_SDR50))) {
                /* fake CAP_1 register */
-               val = SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING;
+               val = SDHCI_SUPPORT_DDR50 |
+                       SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING;
        }
 
        if (unlikely(reg == SDHCI_SLOT_INT_STATUS)) {
@@ -97,7 +98,7 @@ retry:
                        clock_setting | phase,
                        SDHCI_CLK_DELAY_SETTING);
 
-               if (!mmc_send_tuning(mmc)) {
+               if (!mmc_send_tuning(mmc, opcode, NULL)) {
                        /* Tuning is successful at this tuning point */
                        tuned_phase_cnt++;
                        dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
index fbc7efdddcb5a4cb9c2726b91e2ee29acfa85f08..b48565ed5616c79302008963a05aa7e1e18d742c 100644 (file)
@@ -1895,9 +1895,9 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
                tuning_count = host->tuning_count;
 
        /*
-        * The Host Controller needs tuning only in case of SDR104 mode
-        * and for SDR50 mode when Use Tuning for SDR50 is set in the
-        * Capabilities register.
+        * The Host Controller needs tuning in case of SDR104 and DDR50
+        * mode, and for SDR50 mode when Use Tuning for SDR50 is set in
+        * the Capabilities register.
         * If the Host Controller supports the HS200 mode then the
         * tuning function has to be executed.
         */
@@ -1917,6 +1917,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
                break;
 
        case MMC_TIMING_UHS_SDR104:
+       case MMC_TIMING_UHS_DDR50:
                break;
 
        case MMC_TIMING_UHS_SDR50:
@@ -2716,17 +2717,6 @@ int sdhci_resume_host(struct sdhci_host *host)
                        host->ops->enable_dma(host);
        }
 
-       if (!device_may_wakeup(mmc_dev(host->mmc))) {
-               ret = request_threaded_irq(host->irq, sdhci_irq,
-                                          sdhci_thread_irq, IRQF_SHARED,
-                                          mmc_hostname(host->mmc), host);
-               if (ret)
-                       return ret;
-       } else {
-               sdhci_disable_irq_wakeups(host);
-               disable_irq_wake(host->irq);
-       }
-
        if ((host->mmc->pm_flags & MMC_PM_KEEP_POWER) &&
            (host->quirks2 & SDHCI_QUIRK2_HOST_OFF_CARD_ON)) {
                /* Card keeps power but host controller does not */
@@ -2739,6 +2729,17 @@ int sdhci_resume_host(struct sdhci_host *host)
                mmiowb();
        }
 
+       if (!device_may_wakeup(mmc_dev(host->mmc))) {
+               ret = request_threaded_irq(host->irq, sdhci_irq,
+                                          sdhci_thread_irq, IRQF_SHARED,
+                                          mmc_hostname(host->mmc), host);
+               if (ret)
+                       return ret;
+       } else {
+               sdhci_disable_irq_wakeups(host);
+               disable_irq_wake(host->irq);
+       }
+
        sdhci_enable_card_detection(host);
 
        return ret;
index b981b8552e43aad1778e7e7e7277b75d73884c68..83de82bceafc91a6dd6326d393a4285a5b9061ad 100644 (file)
@@ -873,6 +873,13 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
        spin_unlock_irqrestore(&host->lock, iflags);
 }
 
+static int sunxi_mmc_card_busy(struct mmc_host *mmc)
+{
+       struct sunxi_mmc_host *host = mmc_priv(mmc);
+
+       return !!(mmc_readl(host, REG_STAS) & SDXC_CARD_DATA_BUSY);
+}
+
 static const struct of_device_id sunxi_mmc_of_match[] = {
        { .compatible = "allwinner,sun4i-a10-mmc", },
        { .compatible = "allwinner,sun5i-a13-mmc", },
@@ -888,6 +895,7 @@ static struct mmc_host_ops sunxi_mmc_ops = {
        .get_cd          = mmc_gpio_get_cd,
        .enable_sdio_irq = sunxi_mmc_enable_sdio_irq,
        .hw_reset        = sunxi_mmc_hw_reset,
+       .card_busy       = sunxi_mmc_card_busy,
 };
 
 static const struct sunxi_mmc_clk_delay sunxi_mmc_clk_delays[] = {
index fbabbb82b35486d599c2718127ad8abaf3dabbbd..1e819f98b94f52fe0a2f289f2e38dd4dbf72c27b 100644 (file)
@@ -563,7 +563,7 @@ static void add_offloaded_reg(struct vub300_mmc_host *vub300,
                        i += 1;
                        continue;
                }
-       };
+       }
        __add_offloaded_reg_to_fifo(vub300, register_access, func);
 }
 
@@ -1372,7 +1372,7 @@ static void download_offload_pseudocode(struct vub300_mmc_host *vub300)
                l += snprintf(vub300->vub_name + l,
                              sizeof(vub300->vub_name) - l, "_%04X%04X",
                              sf->vendor, sf->device);
-       };
+       }
        snprintf(vub300->vub_name + l, sizeof(vub300->vub_name) - l, ".bin");
        dev_info(&vub300->udev->dev, "requesting offload firmware %s\n",
                 vub300->vub_name);
@@ -1893,7 +1893,7 @@ static int satisfy_request_from_offloaded_data(struct vub300_mmc_host *vub300,
                        i += 1;
                        continue;
                }
-       };
+       }
        if (vub300->total_offload_count == 0)
                return 0;
        else if (vub300->fn[func].offload_count == 0)
index ca183ea767b32b5bace0ab819b32fdb251bb4314..c3fd16d997caba4d903d6e8d3ea35acd0f350075 100644 (file)
@@ -809,7 +809,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
                        cmd->error = -EINVAL;
 
                        goto done;
-               };
+               }
        }
 
        /*
index d18eb607bee66ab1f586059a44de4fefd94352da..f184fb5bd11046d814f0b03e122bc7419bc7cdf5 100644 (file)
@@ -298,7 +298,10 @@ config NLMON
 
 config NET_VRF
        tristate "Virtual Routing and Forwarding (Lite)"
-       depends on IP_MULTIPLE_TABLES && IPV6_MULTIPLE_TABLES
+       depends on IP_MULTIPLE_TABLES
+       depends on NET_L3_MASTER_DEV
+       depends on IPV6 || IPV6=n
+       depends on IPV6_MULTIPLE_TABLES || IPV6=n
        ---help---
          This option enables the support for mapping interfaces into VRF's. The
          support enables VRF devices.
index 2a9c3c3abe9be27307b11a87697eb5196feb9ebd..39bd16f3f86decce4d5421413c1bcc4654168a4c 100644 (file)
@@ -103,6 +103,7 @@ config ARCNET_RIM_I
 
 config ARCNET_COM20020
        tristate "ARCnet COM20020 chipset driver"
+       depends on LEDS_CLASS
        help
          This is the driver for the new COM20020 chipset. It supports such
          things as promiscuous mode, so packet sniffing is possible, and
index 705e6ce2eb90b9debb4e635af396ddfdbb53a594..d78f30186642e299d803fa98fa6fd8001b2d831b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - "raw mode" packet encapsulation (no soft headers)
- * 
+ *
  * Written 1994-1999 by Avery Pennarun.
  * Derived from skeleton.c by Donald Becker.
  *
@@ -24,6 +24,8 @@
  * **********************
  */
 
+#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/gfp.h>
 #include <linux/init.h>
 #include <net/arp.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
-#include <linux/arcdevice.h>
-
-#define VERSION "arcnet: raw mode (`r') encapsulation support loaded.\n"
-
-
-static void rx(struct net_device *dev, int bufnum,
-              struct archdr *pkthdr, int length);
-static int build_header(struct sk_buff *skb, struct net_device *dev,
-                       unsigned short type, uint8_t daddr);
-static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
-                     int bufnum);
-
-static struct ArcProto rawmode_proto =
-{
-       .suffix         = 'r',
-       .mtu            = XMTU,
-       .rx             = rx,
-       .build_header   = build_header,
-       .prepare_tx     = prepare_tx,
-       .continue_tx    = NULL,
-       .ack_tx         = NULL
-};
-
-
-static int __init arcnet_raw_init(void)
-{
-       int count;
-
-       printk(VERSION);
-
-       for (count = 0; count < 256; count++)
-               if (arc_proto_map[count] == arc_proto_default)
-                       arc_proto_map[count] = &rawmode_proto;
-
-       /* for raw mode, we only set the bcast proto if there's no better one */
-       if (arc_bcast_proto == arc_proto_default)
-               arc_bcast_proto = &rawmode_proto;
-
-       arc_proto_default = &rawmode_proto;
-       return 0;
-}
-
-static void __exit arcnet_raw_exit(void)
-{
-       arcnet_unregister_proto(&rawmode_proto);
-}
-
-module_init(arcnet_raw_init);
-module_exit(arcnet_raw_exit);
-
-MODULE_LICENSE("GPL");
-
+#include "arcdevice.h"
 
 /* packet receiver */
 static void rx(struct net_device *dev, int bufnum,
@@ -93,7 +44,7 @@ static void rx(struct net_device *dev, int bufnum,
        struct archdr *pkt = pkthdr;
        int ofs;
 
-       BUGMSG(D_DURING, "it's a raw packet (length=%d)\n", length);
+       arc_printk(D_DURING, dev, "it's a raw packet (length=%d)\n", length);
 
        if (length > MTU)
                ofs = 512 - length;
@@ -101,15 +52,14 @@ static void rx(struct net_device *dev, int bufnum,
                ofs = 256 - length;
 
        skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
-       if (skb == NULL) {
-               BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
+       if (!skb) {
                dev->stats.rx_dropped++;
                return;
        }
        skb_put(skb, length + ARC_HDR_SIZE);
        skb->dev = dev;
 
-       pkt = (struct archdr *) skb->data;
+       pkt = (struct archdr *)skb->data;
 
        skb_reset_mac_header(skb);
        skb_pull(skb, ARC_HDR_SIZE);
@@ -121,38 +71,35 @@ static void rx(struct net_device *dev, int bufnum,
                                      pkt->soft.raw + sizeof(pkt->soft),
                                      length - sizeof(pkt->soft));
 
-       BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
+       if (BUGLVL(D_SKB))
+               arcnet_dump_skb(dev, skb, "rx");
 
        skb->protocol = cpu_to_be16(ETH_P_ARCNET);
        netif_rx(skb);
 }
 
-
-/*
- * Create the ARCnet hard/soft headers for raw mode.
+/* Create the ARCnet hard/soft headers for raw mode.
  * There aren't any soft headers in raw mode - not even the protocol id.
  */
 static int build_header(struct sk_buff *skb, struct net_device *dev,
                        unsigned short type, uint8_t daddr)
 {
        int hdr_size = ARC_HDR_SIZE;
-       struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
+       struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size);
 
-       /*
-        * Set the source hardware address.
+       /* Set the source hardware address.
         *
         * This is pretty pointless for most purposes, but it can help in
-        * debugging.  ARCnet does not allow us to change the source address in
-        * the actual packet sent)
+        * debugging.  ARCnet does not allow us to change the source address
+        * in the actual packet sent.
         */
        pkt->hard.source = *dev->dev_addr;
 
        /* see linux/net/ethernet/eth.c to see where I got the following */
 
        if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
-               /* 
-                * FIXME: fill in the last byte of the dest ipaddr here to better
-                * comply with RFC1051 in "noarp" mode.
+               /* FIXME: fill in the last byte of the dest ipaddr here
+                * to better comply with RFC1051 in "noarp" mode.
                 */
                pkt->hard.dest = 0;
                return hdr_size;
@@ -163,7 +110,6 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
        return hdr_size;        /* success */
 }
 
-
 static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
                      int bufnum)
 {
@@ -171,15 +117,16 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
        struct arc_hardware *hard = &pkt->hard;
        int ofs;
 
-       BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
-              lp->next_tx, lp->cur_tx, bufnum);
+       arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n",
+                  lp->next_tx, lp->cur_tx, bufnum);
 
-       length -= ARC_HDR_SIZE; /* hard header is not included in packet length */
+       /* hard header is not included in packet length */
+       length -= ARC_HDR_SIZE;
 
        if (length > XMTU) {
                /* should never happen! other people already check for this. */
-               BUGMSG(D_NORMAL, "Bug!  prepare_tx with size %d (> %d)\n",
-                      length, XMTU);
+               arc_printk(D_NORMAL, dev, "Bug!  prepare_tx with size %d (> %d)\n",
+                          length, XMTU);
                length = XMTU;
        }
        if (length >= MinTU) {
@@ -188,11 +135,12 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
        } else if (length > MTU) {
                hard->offset[0] = 0;
                hard->offset[1] = ofs = 512 - length - 3;
-       } else
+       } else {
                hard->offset[0] = ofs = 256 - length;
+       }
 
-       BUGMSG(D_DURING, "prepare_tx: length=%d ofs=%d\n",
-              length,ofs);
+       arc_printk(D_DURING, dev, "prepare_tx: length=%d ofs=%d\n",
+                  length, ofs);
 
        lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
        lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft, length);
@@ -201,3 +149,41 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
 
        return 1;               /* done */
 }
+
+static struct ArcProto rawmode_proto = {
+       .suffix         = 'r',
+       .mtu            = XMTU,
+       .rx             = rx,
+       .build_header   = build_header,
+       .prepare_tx     = prepare_tx,
+       .continue_tx    = NULL,
+       .ack_tx         = NULL
+};
+
+static int __init arcnet_raw_init(void)
+{
+       int count;
+
+       pr_info("raw mode (`r') encapsulation support loaded\n");
+
+       for (count = 0; count < 256; count++)
+               if (arc_proto_map[count] == arc_proto_default)
+                       arc_proto_map[count] = &rawmode_proto;
+
+       /* for raw mode, we only set the bcast proto if there's no better one */
+       if (arc_bcast_proto == arc_proto_default)
+               arc_bcast_proto = &rawmode_proto;
+
+       arc_proto_default = &rawmode_proto;
+       return 0;
+}
+
+static void __exit arcnet_raw_exit(void)
+{
+       arcnet_unregister_proto(&rawmode_proto);
+}
+
+module_init(arcnet_raw_init);
+module_exit(arcnet_raw_exit);
+
+MODULE_LICENSE("GPL");
index b8b4c7ba884f0c2ed9499e1ede4a20ad1df4f732..a07e24970be4216f16df473c80dab72b516ab11c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - "RIM I" (entirely mem-mapped) cards
- * 
+ *
  * Written 1994-1999 by Avery Pennarun.
  * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
  * Derived from skeleton.c by Donald Becker.
@@ -24,6 +24,9 @@
  *
  * **********************
  */
+
+#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/bootmem.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <asm/io.h>
-#include <linux/arcdevice.h>
-
-
-#define VERSION "arcnet: RIM I (entirely mem-mapped) support\n"
+#include <linux/io.h>
 
+#include "arcdevice.h"
+#include "com9026.h"
 
 /* Internal function declarations */
 
@@ -50,66 +51,46 @@ static void arcrimi_setmask(struct net_device *dev, int mask);
 static int arcrimi_reset(struct net_device *dev, int really_reset);
 static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
                                 void *buf, int count);
-static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, int offset,
-                                  void *buf, int count);
+static void arcrimi_copy_from_card(struct net_device *dev, int bufnum,
+                                  int offset, void *buf, int count);
 
 /* Handy defines for ARCnet specific stuff */
 
 /* Amount of I/O memory used by the card */
-#define BUFFER_SIZE (512)
-#define MIRROR_SIZE (BUFFER_SIZE*4)
+#define BUFFER_SIZE    (512)
+#define MIRROR_SIZE    (BUFFER_SIZE * 4)
 
-/* COM 9026 controller chip --> ARCnet register addresses */
-#define _INTMASK (ioaddr+0)    /* writable */
-#define _STATUS  (ioaddr+0)    /* readable */
-#define _COMMAND (ioaddr+1)    /* writable, returns random vals on read (?) */
-#define _RESET  (ioaddr+8)     /* software reset (on read) */
-#define _MEMDATA  (ioaddr+12)  /* Data port for IO-mapped memory */
-#define _ADDR_HI  (ioaddr+15)  /* Control registers for said */
-#define _ADDR_LO  (ioaddr+14)
-#define _CONFIG  (ioaddr+2)    /* Configuration register */
-
-#undef ASTATUS
-#undef ACOMMAND
-#undef AINTMASK
-
-#define ASTATUS()      readb(_STATUS)
-#define ACOMMAND(cmd)  writeb((cmd),_COMMAND)
-#define AINTMASK(msk)  writeb((msk),_INTMASK)
-#define SETCONF()      writeb(lp->config,_CONFIG)
-
-
-/*
- * We cannot probe for a RIM I card; one reason is I don't know how to reset
+/* We cannot probe for a RIM I card; one reason is I don't know how to reset
  * them.  In fact, we can't even get their node ID automatically.  So, we
  * need to be passed a specific shmem address, IRQ, and node ID.
  */
 static int __init arcrimi_probe(struct net_device *dev)
 {
-       BUGLVL(D_NORMAL) printk(VERSION);
-       BUGLVL(D_NORMAL) printk("E-mail me if you actually test the RIM I driver, please!\n");
-
-       BUGLVL(D_NORMAL) printk("Given: node %02Xh, shmem %lXh, irq %d\n",
-              dev->dev_addr[0], dev->mem_start, dev->irq);
+       if (BUGLVL(D_NORMAL)) {
+               pr_info("%s\n", "RIM I (entirely mem-mapped) support");
+               pr_info("E-mail me if you actually test the RIM I driver, please!\n");
+               pr_info("Given: node %02Xh, shmem %lXh, irq %d\n",
+                       dev->dev_addr[0], dev->mem_start, dev->irq);
+       }
 
        if (dev->mem_start <= 0 || dev->irq <= 0) {
-               BUGLVL(D_NORMAL) printk("No autoprobe for RIM I; you "
-                      "must specify the shmem and irq!\n");
+               if (BUGLVL(D_NORMAL))
+                       pr_err("No autoprobe for RIM I; you must specify the shmem and irq!\n");
                return -ENODEV;
        }
        if (dev->dev_addr[0] == 0) {
-               BUGLVL(D_NORMAL) printk("You need to specify your card's station "
-                      "ID!\n");
+               if (BUGLVL(D_NORMAL))
+                       pr_err("You need to specify your card's station ID!\n");
                return -ENODEV;
        }
-       /*
-        * Grab the memory region at mem_start for MIRROR_SIZE bytes.
+       /* Grab the memory region at mem_start for MIRROR_SIZE bytes.
         * Later in arcrimi_found() the real size will be determined
         * and this reserve will be released and the correct size
         * will be taken.
         */
        if (!request_mem_region(dev->mem_start, MIRROR_SIZE, "arcnet (90xx)")) {
-               BUGLVL(D_NORMAL) printk("Card memory already allocated\n");
+               if (BUGLVL(D_NORMAL))
+                       pr_notice("Card memory already allocated\n");
                return -ENODEV;
        }
        return arcrimi_found(dev);
@@ -125,7 +106,7 @@ static int check_mirror(unsigned long addr, size_t size)
 
        p = ioremap(addr, size);
        if (p) {
-               if (readb(p) == TESTvalue)
+               if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue)
                        res = 1;
                else
                        res = 0;
@@ -136,9 +117,8 @@ static int check_mirror(unsigned long addr, size_t size)
        return res;
 }
 
-/*
- * Set up the struct net_device associated with this card.  Called after
- * probing succeeds.
+/* Set up the struct net_device associated with this card.
+ * Called after probing succeeds.
  */
 static int __init arcrimi_found(struct net_device *dev)
 {
@@ -151,7 +131,7 @@ static int __init arcrimi_found(struct net_device *dev)
        p = ioremap(dev->mem_start, MIRROR_SIZE);
        if (!p) {
                release_mem_region(dev->mem_start, MIRROR_SIZE);
-               BUGMSG(D_NORMAL, "Can't ioremap\n");
+               arc_printk(D_NORMAL, dev, "Can't ioremap\n");
                return -ENODEV;
        }
 
@@ -159,13 +139,14 @@ static int __init arcrimi_found(struct net_device *dev)
        if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (RIM I)", dev)) {
                iounmap(p);
                release_mem_region(dev->mem_start, MIRROR_SIZE);
-               BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
+               arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq);
                return -ENODEV;
        }
 
        shmem = dev->mem_start;
-       writeb(TESTvalue, p);
-       writeb(dev->dev_addr[0], p + 1);        /* actually the node ID */
+       arcnet_writeb(TESTvalue, p, COM9026_REG_W_INTMASK);
+       arcnet_writeb(TESTvalue, p, COM9026_REG_W_COMMAND);
+                                       /* actually the station/node ID */
 
        /* find the real shared memory start/end points, including mirrors */
 
@@ -174,7 +155,7 @@ static int __init arcrimi_found(struct net_device *dev)
         * 2k (or there are no mirrors at all) but on some, it's 4k.
         */
        mirror_size = MIRROR_SIZE;
-       if (readb(p) == TESTvalue &&
+       if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue &&
            check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
            check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
                mirror_size = 2 * MIRROR_SIZE;
@@ -204,8 +185,7 @@ static int __init arcrimi_found(struct net_device *dev)
        lp->hw.copy_to_card = arcrimi_copy_to_card;
        lp->hw.copy_from_card = arcrimi_copy_from_card;
 
-       /*
-        * re-reserve the memory region - arcrimi_probe() alloced this reqion
+       /* re-reserve the memory region - arcrimi_probe() alloced this reqion
         * but didn't know the real size.  Free that region and then re-get
         * with the correct size.  There is a VERY slim chance this could
         * fail.
@@ -215,24 +195,25 @@ static int __init arcrimi_found(struct net_device *dev)
        if (!request_mem_region(dev->mem_start,
                                dev->mem_end - dev->mem_start + 1,
                                "arcnet (90xx)")) {
-               BUGMSG(D_NORMAL, "Card memory already allocated\n");
+               arc_printk(D_NORMAL, dev, "Card memory already allocated\n");
                goto err_free_irq;
        }
 
-       lp->mem_start = ioremap(dev->mem_start, dev->mem_end - dev->mem_start + 1);
+       lp->mem_start = ioremap(dev->mem_start,
+                               dev->mem_end - dev->mem_start + 1);
        if (!lp->mem_start) {
-               BUGMSG(D_NORMAL, "Can't remap device memory!\n");
+               arc_printk(D_NORMAL, dev, "Can't remap device memory!\n");
                goto err_release_mem;
        }
 
        /* get and check the station ID from offset 1 in shmem */
-       dev->dev_addr[0] = readb(lp->mem_start + 1);
+       dev->dev_addr[0] = arcnet_readb(lp->mem_start, COM9026_REG_R_STATION);
 
-       BUGMSG(D_NORMAL, "ARCnet RIM I: station %02Xh found at IRQ %d, "
-              "ShMem %lXh (%ld*%d bytes).\n",
-              dev->dev_addr[0],
-              dev->irq, dev->mem_start,
-        (dev->mem_end - dev->mem_start + 1) / mirror_size, mirror_size);
+       arc_printk(D_NORMAL, dev, "ARCnet RIM I: station %02Xh found at IRQ %d, ShMem %lXh (%ld*%d bytes)\n",
+                  dev->dev_addr[0],
+                  dev->irq, dev->mem_start,
+                  (dev->mem_end - dev->mem_start + 1) / mirror_size,
+                  mirror_size);
 
        err = register_netdev(dev);
        if (err)
@@ -249,9 +230,7 @@ err_free_irq:
        return -EIO;
 }
 
-
-/*
- * Do a hardware reset on the card, and set up necessary registers.
+/* Do a hardware reset on the card, and set up necessary registers.
  *
  * This should be called as little as possible, because it disrupts the
  * token on the network (causes a RECON) and requires a significant delay.
@@ -263,17 +242,19 @@ static int arcrimi_reset(struct net_device *dev, int really_reset)
        struct arcnet_local *lp = netdev_priv(dev);
        void __iomem *ioaddr = lp->mem_start + 0x800;
 
-       BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", dev->name, ASTATUS());
+       arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n",
+                  dev->name, arcnet_readb(ioaddr, COM9026_REG_R_STATUS));
 
        if (really_reset) {
-               writeb(TESTvalue, ioaddr - 0x800);      /* fake reset */
+               arcnet_writeb(TESTvalue, ioaddr, -0x800);       /* fake reset */
                return 0;
        }
-       ACOMMAND(CFLAGScmd | RESETclear);       /* clear flags & end reset */
-       ACOMMAND(CFLAGScmd | CONFIGclear);
+       /* clear flags & end reset */
+       arcnet_writeb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);
+       arcnet_writeb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);
 
        /* enable extended (512-byte) packets */
-       ACOMMAND(CONFIGcmd | EXTconf);
+       arcnet_writeb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
 
        /* done!  return success. */
        return 0;
@@ -284,7 +265,7 @@ static void arcrimi_setmask(struct net_device *dev, int mask)
        struct arcnet_local *lp = netdev_priv(dev);
        void __iomem *ioaddr = lp->mem_start + 0x800;
 
-       AINTMASK(mask);
+       arcnet_writeb(mask, ioaddr, COM9026_REG_W_INTMASK);
 }
 
 static int arcrimi_status(struct net_device *dev)
@@ -292,7 +273,7 @@ static int arcrimi_status(struct net_device *dev)
        struct arcnet_local *lp = netdev_priv(dev);
        void __iomem *ioaddr = lp->mem_start + 0x800;
 
-       return ASTATUS();
+       return arcnet_readb(ioaddr, COM9026_REG_R_STATUS);
 }
 
 static void arcrimi_command(struct net_device *dev, int cmd)
@@ -300,7 +281,7 @@ static void arcrimi_command(struct net_device *dev, int cmd)
        struct arcnet_local *lp = netdev_priv(dev);
        void __iomem *ioaddr = lp->mem_start + 0x800;
 
-       ACOMMAND(cmd);
+       arcnet_writeb(cmd, ioaddr, COM9026_REG_W_COMMAND);
 }
 
 static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
@@ -308,16 +289,17 @@ static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
 {
        struct arcnet_local *lp = netdev_priv(dev);
        void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
-       TIME("memcpy_toio", count, memcpy_toio(memaddr, buf, count));
-}
 
+       TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count));
+}
 
-static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, int offset,
-                                  void *buf, int count)
+static void arcrimi_copy_from_card(struct net_device *dev, int bufnum,
+                                  int offset, void *buf, int count)
 {
        struct arcnet_local *lp = netdev_priv(dev);
        void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
-       TIME("memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
+
+       TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
 }
 
 static int node;
@@ -374,12 +356,13 @@ static void __exit arc_rimi_exit(void)
 static int __init arcrimi_setup(char *s)
 {
        int ints[8];
+
        s = get_options(s, 8, ints);
        if (!ints[0])
                return 1;
        switch (ints[0]) {
        default:                /* ERROR */
-               printk("arcrimi: Too many arguments.\n");
+               pr_err("Too many arguments\n");
        case 3:         /* Node ID */
                node = ints[3];
        case 2:         /* IRQ */
similarity index 75%
rename from include/linux/arcdevice.h
rename to drivers/net/arcnet/arcdevice.h
index df0356220730601bd86fb644c2da629c5ddb1a35..20bfb9ba83ea23d38880676198e279513ed70123 100644 (file)
@@ -34,7 +34,6 @@
  */
 #define RECON_THRESHOLD 30
 
-
 /*
  * Define this to the minimum "timeout" value.  If a transmit takes longer
  * than TX_TIMEOUT jiffies, Linux will abort the TX and retry.  On a large
  */
 #define TX_TIMEOUT (HZ * 200 / 1000)
 
-
 /* Display warnings about the driver being an ALPHA version. */
 #undef ALPHA_WARNING
 
-
 /*
  * Debugging bitflags: each option can be enabled individually.
- * 
+ *
  * Note: only debug flags included in the ARCNET_DEBUG_MAX define will
  *   actually be available.  GCC will (at least, GCC 2.7.0 will) notice
  *   lines using a BUGLVL not in ARCNET_DEBUG_MAX and automatically optimize
 #endif
 
 #ifndef ARCNET_DEBUG
-#define ARCNET_DEBUG (D_NORMAL|D_EXTRA)
+#define ARCNET_DEBUG (D_NORMAL | D_EXTRA)
 #endif
 extern int arcnet_debug;
 
+#define BUGLVL(x)      ((x) & ARCNET_DEBUG_MAX & arcnet_debug)
+
 /* macros to simplify debug checking */
-#define BUGLVL(x) if ((ARCNET_DEBUG_MAX)&arcnet_debug&(x))
-#define BUGMSG2(x,msg,args...) do { BUGLVL(x) printk(msg, ## args); } while (0)
-#define BUGMSG(x,msg,args...) \
-       BUGMSG2(x, "%s%6s: " msg, \
-            x==D_NORMAL        ? KERN_WARNING \
-                       : x < D_DURING ? KERN_INFO : KERN_DEBUG, \
-           dev->name , ## args)
+#define arc_printk(x, dev, fmt, ...)                                   \
+do {                                                                   \
+       if (BUGLVL(x)) {                                                \
+               if ((x) == D_NORMAL)                                    \
+                       netdev_warn(dev, fmt, ##__VA_ARGS__);           \
+               else if ((x) < D_DURING)                                \
+                       netdev_info(dev, fmt, ##__VA_ARGS__);           \
+               else                                                    \
+                       netdev_dbg(dev, fmt, ##__VA_ARGS__);            \
+       }                                                               \
+} while (0)
+
+#define arc_cont(x, fmt, ...)                                          \
+do {                                                                   \
+       if (BUGLVL(x))                                                  \
+               pr_cont(fmt, ##__VA_ARGS__);                            \
+} while (0)
 
 /* see how long a function call takes to run, expressed in CPU cycles */
-#define TIME(name, bytes, call) BUGLVL(D_TIMING) { \
-           unsigned long _x, _y; \
-           _x = get_cycles(); \
-           call; \
-           _y = get_cycles(); \
-           BUGMSG(D_TIMING, \
-              "%s: %d bytes in %lu cycles == " \
-              "%lu Kbytes/100Mcycle\n",\
-                  name, bytes, _y - _x, \
-                  100000000 / 1024 * bytes / (_y - _x + 1));\
-       } \
-       else { \
-                   call;\
-       }
-
+#define TIME(dev, name, bytes, call)                                   \
+do {                                                                   \
+       if (BUGLVL(D_TIMING)) {                                         \
+               unsigned long _x, _y;                                   \
+               _x = get_cycles();                                      \
+               call;                                                   \
+               _y = get_cycles();                                      \
+               arc_printk(D_TIMING, dev,                               \
+                          "%s: %d bytes in %lu cycles == %lu Kbytes/100Mcycle\n", \
+                          name, bytes, _y - _x,                        \
+                          100000000 / 1024 * bytes / (_y - _x + 1));   \
+       } else {                                                        \
+               call;                                                   \
+       }                                                               \
+} while (0)
 
 /*
  * Time needed to reset the card - in ms (milliseconds).  This works on my
@@ -155,6 +164,7 @@ extern int arcnet_debug;
 #define CONFIGcmd       0x05   /* define configuration */
 #define CFLAGScmd       0x06   /* clear flags */
 #define TESTcmd         0x07   /* load test flags */
+#define STARTIOcmd      0x18   /* start internal operation */
 
 /* flags for "clear flags" command */
 #define RESETclear      0x08   /* power-on-reset */
@@ -182,29 +192,27 @@ extern int arcnet_debug;
 #define ARC_CAN_10MBIT  2   /* card uses COM20022, supporting 10MBit,
                                 but default is 2.5MBit. */
 
-
 /* information needed to define an encapsulation driver */
 struct ArcProto {
        char suffix;            /* a for RFC1201, e for ether-encap, etc. */
        int mtu;                /* largest possible packet */
        int is_ip;              /* This is a ip plugin - not a raw thing */
 
-       void (*rx) (struct net_device * dev, int bufnum,
-                   struct archdr * pkthdr, int length);
-       int (*build_header) (struct sk_buff * skb, struct net_device *dev,
-                            unsigned short ethproto, uint8_t daddr);
+       void (*rx)(struct net_device *dev, int bufnum,
+                  struct archdr *pkthdr, int length);
+       int (*build_header)(struct sk_buff *skb, struct net_device *dev,
+                           unsigned short ethproto, uint8_t daddr);
 
        /* these functions return '1' if the skb can now be freed */
-       int (*prepare_tx) (struct net_device * dev, struct archdr * pkt, int length,
-                          int bufnum);
-       int (*continue_tx) (struct net_device * dev, int bufnum);
-       int (*ack_tx) (struct net_device * dev, int acked);
+       int (*prepare_tx)(struct net_device *dev, struct archdr *pkt,
+                         int length, int bufnum);
+       int (*continue_tx)(struct net_device *dev, int bufnum);
+       int (*ack_tx)(struct net_device *dev, int acked);
 };
 
 extern struct ArcProto *arc_proto_map[256], *arc_proto_default,
        *arc_bcast_proto, *arc_raw_proto;
 
-
 /*
  * "Incoming" is information needed for each address that could be sending
  * to us.  Mostly for partially-received split packets.
@@ -216,7 +224,6 @@ struct Incoming {
                numpackets;     /* number of packets in split     */
 };
 
-
 /* only needed for RFC1201 */
 struct Outgoing {
        struct ArcProto *proto; /* protocol driver that owns this:
@@ -230,6 +237,7 @@ struct Outgoing {
                numsegs;        /* number of segments */
 };
 
+#define ARCNET_LED_NAME_SZ (IFNAMSIZ + 6)
 
 struct arcnet_local {
        uint8_t config,         /* current value of CONFIG register */
@@ -251,10 +259,16 @@ struct arcnet_local {
        char *card_name;        /* card ident string */
        int card_flags;         /* special card features */
 
-
        /* On preemtive and SMB a lock is needed */
        spinlock_t lock;
 
+       struct led_trigger *tx_led_trig;
+       char tx_led_trig_name[ARCNET_LED_NAME_SZ];
+       struct led_trigger *recon_led_trig;
+       char recon_led_trig_name[ARCNET_LED_NAME_SZ];
+
+       struct timer_list       timer;
+
        /*
         * Buffer management: an ARCnet card has 4 x 512-byte buffers, each of
         * which can be used for either sending or receiving.  The new dynamic
@@ -263,13 +277,13 @@ struct arcnet_local {
         * situations in which we (for example) want to pre-load a transmit
         * buffer, or start receiving while we copy a received packet to
         * memory.
-        * 
+        *
         * The rules: only the interrupt handler is allowed to _add_ buffers to
         * the queue; thus, this doesn't require a lock.  Both the interrupt
         * handler and the transmit function will want to _remove_ buffers, so
         * we need to handle the situation where they try to do it at the same
         * time.
-        * 
+        *
         * If next_buf == first_free_buf, the queue is empty.  Since there are
         * only four possible buffers, the queue should never be full.
         */
@@ -298,34 +312,41 @@ struct arcnet_local {
        /* hardware-specific functions */
        struct {
                struct module *owner;
-               void (*command) (struct net_device * dev, int cmd);
-               int (*status) (struct net_device * dev);
-               void (*intmask) (struct net_device * dev, int mask);
-               int (*reset) (struct net_device * dev, int really_reset);
-               void (*open) (struct net_device * dev);
-               void (*close) (struct net_device * dev);
-
-               void (*copy_to_card) (struct net_device * dev, int bufnum, int offset,
-                                     void *buf, int count);
-               void (*copy_from_card) (struct net_device * dev, int bufnum, int offset,
-                                       void *buf, int count);
+               void (*command)(struct net_device *dev, int cmd);
+               int (*status)(struct net_device *dev);
+               void (*intmask)(struct net_device *dev, int mask);
+               int (*reset)(struct net_device *dev, int really_reset);
+               void (*open)(struct net_device *dev);
+               void (*close)(struct net_device *dev);
+               void (*datatrigger) (struct net_device * dev, int enable);
+               void (*recontrigger) (struct net_device * dev, int enable);
+
+               void (*copy_to_card)(struct net_device *dev, int bufnum,
+                                    int offset, void *buf, int count);
+               void (*copy_from_card)(struct net_device *dev, int bufnum,
+                                      int offset, void *buf, int count);
        } hw;
 
        void __iomem *mem_start;        /* pointer to ioremap'ed MMIO */
 };
 
+enum arcnet_led_event {
+       ARCNET_LED_EVENT_RECON,
+       ARCNET_LED_EVENT_OPEN,
+       ARCNET_LED_EVENT_STOP,
+       ARCNET_LED_EVENT_TX,
+};
 
-#define ARCRESET(x)  (lp->hw.reset(dev, (x)))
-#define ACOMMAND(x)  (lp->hw.command(dev, (x)))
-#define ASTATUS()    (lp->hw.status(dev))
-#define AINTMASK(x)  (lp->hw.intmask(dev, (x)))
-
-
+void arcnet_led_event(struct net_device *netdev, enum arcnet_led_event event);
+void devm_arcnet_led_init(struct net_device *netdev, int index, int subid);
 
 #if ARCNET_DEBUG_MAX & D_SKB
 void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc);
 #else
-#define arcnet_dump_skb(dev,skb,desc) ;
+static inline
+void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc)
+{
+}
 #endif
 
 void arcnet_unregister_proto(struct ArcProto *proto);
@@ -335,8 +356,34 @@ struct net_device *alloc_arcdev(const char *name);
 int arcnet_open(struct net_device *dev);
 int arcnet_close(struct net_device *dev);
 netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
-                                    struct net_device *dev);
+                              struct net_device *dev);
 void arcnet_timeout(struct net_device *dev);
 
+/* I/O equivalents */
+
+#ifdef CONFIG_SA1100_CT6001
+#define BUS_ALIGN  2  /* 8 bit device on a 16 bit bus - needs padding */
+#else
+#define BUS_ALIGN  1
+#endif
+
+/* addr and offset allow register like names to define the actual IO  address.
+ * A configuration option multiplies the offset for alignment.
+ */
+#define arcnet_inb(addr, offset)                                       \
+       inb((addr) + BUS_ALIGN * (offset))
+#define arcnet_outb(value, addr, offset)                               \
+       outb(value, (addr) + BUS_ALIGN * (offset))
+
+#define arcnet_insb(addr, offset, buffer, count)                       \
+       insb((addr) + BUS_ALIGN * (offset), buffer, count)
+#define arcnet_outsb(addr, offset, buffer, count)                      \
+       outsb((addr) + BUS_ALIGN * (offset), buffer, count)
+
+#define arcnet_readb(addr, offset)                                     \
+       readb((addr) + (offset))
+#define arcnet_writeb(value, addr, offset)                             \
+       writeb(value, (addr) + (offset))
+
 #endif                         /* __KERNEL__ */
 #endif                         /* _LINUX_ARCDEVICE_H */
index 816d0e94961c9fec79ed48eefe7450a806df7f39..6ea963e3b89a1ab1c78ccb4d63c99a16e1bfbe0b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - device-independent routines
- * 
+ *
  * Written 1997 by David Woodhouse.
  * Written 1994-1999 by Avery Pennarun.
  * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
  * modified by SRC, incorporated herein by reference.
  *
  * **********************
- * 
+ *
  * The change log is now in a file called ChangeLog in this directory.
  *
  * Sources:
  *  - Crynwr arcnet.com/arcether.com packet drivers.
- *  - arcnet.c v0.00 dated 1/1/94 and apparently by 
+ *  - arcnet.c v0.00 dated 1/1/94 and apparently by
  *     Donald Becker - it didn't work :)
  *  - skeleton.c v0.05 dated 11/16/93 by Donald Becker
  *     (from Linux Kernel 1.1.45)
@@ -41,7 +41,7 @@
  *     <jojo@repas.de>
  */
 
-#define VERSION "arcnet: v3.94 BETA 2007/02/08 - by Avery Pennarun et al.\n"
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/if_arp.h>
 #include <net/arp.h>
 #include <linux/init.h>
-#include <linux/arcdevice.h>
 #include <linux/jiffies.h>
 
+#include <linux/leds.h>
+
+#include "arcdevice.h"
+#include "com9026.h"
+
 /* "do nothing" functions for protocol drivers */
 static void null_rx(struct net_device *dev, int bufnum,
                    struct archdr *pkthdr, int length);
@@ -63,17 +67,24 @@ static int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
 
 static void arcnet_rx(struct net_device *dev, int bufnum);
 
-/*
- * one ArcProto per possible proto ID.  None of the elements of
+/* one ArcProto per possible proto ID.  None of the elements of
  * arc_proto_map are allowed to be NULL; they will get set to
  * arc_proto_default instead.  It also must not be NULL; if you would like
  * to set it to NULL, set it to &arc_proto_null instead.
  */
- struct ArcProto *arc_proto_map[256], *arc_proto_default,
-   *arc_bcast_proto, *arc_raw_proto;
+struct ArcProto *arc_proto_map[256];
+EXPORT_SYMBOL(arc_proto_map);
 
-static struct ArcProto arc_proto_null =
-{
+struct ArcProto *arc_proto_default;
+EXPORT_SYMBOL(arc_proto_default);
+
+struct ArcProto *arc_bcast_proto;
+EXPORT_SYMBOL(arc_bcast_proto);
+
+struct ArcProto *arc_raw_proto;
+EXPORT_SYMBOL(arc_raw_proto);
+
+static struct ArcProto arc_proto_null = {
        .suffix         = '?',
        .mtu            = XMTU,
        .is_ip          = 0,
@@ -86,19 +97,7 @@ static struct ArcProto arc_proto_null =
 
 /* Exported function prototypes */
 int arcnet_debug = ARCNET_DEBUG;
-
-EXPORT_SYMBOL(arc_proto_map);
-EXPORT_SYMBOL(arc_proto_default);
-EXPORT_SYMBOL(arc_bcast_proto);
-EXPORT_SYMBOL(arc_raw_proto);
-EXPORT_SYMBOL(arcnet_unregister_proto);
 EXPORT_SYMBOL(arcnet_debug);
-EXPORT_SYMBOL(alloc_arcdev);
-EXPORT_SYMBOL(arcnet_interrupt);
-EXPORT_SYMBOL(arcnet_open);
-EXPORT_SYMBOL(arcnet_close);
-EXPORT_SYMBOL(arcnet_send_packet);
-EXPORT_SYMBOL(arcnet_timeout);
 
 /* Internal function prototypes */
 static int arcnet_header(struct sk_buff *skb, struct net_device *dev,
@@ -116,29 +115,20 @@ static int __init arcnet_init(void)
 
        arcnet_debug = debug;
 
-       printk("arcnet loaded.\n");
-
-#ifdef ALPHA_WARNING
-       BUGLVL(D_EXTRA) {
-               printk("arcnet: ***\n"
-               "arcnet: * Read arcnet.txt for important release notes!\n"
-                      "arcnet: *\n"
-                      "arcnet: * This is an ALPHA version! (Last stable release: v3.02)  E-mail\n"
-                      "arcnet: * me if you have any questions, comments, or bug reports.\n"
-                      "arcnet: ***\n");
-       }
-#endif
+       pr_info("arcnet loaded\n");
 
        /* initialize the protocol map */
        arc_raw_proto = arc_proto_default = arc_bcast_proto = &arc_proto_null;
        for (count = 0; count < 256; count++)
                arc_proto_map[count] = arc_proto_default;
 
-       BUGLVL(D_DURING)
-           printk("arcnet: struct sizes: %Zd %Zd %Zd %Zd %Zd\n",
-                sizeof(struct arc_hardware), sizeof(struct arc_rfc1201),
-               sizeof(struct arc_rfc1051), sizeof(struct arc_eth_encap),
-                  sizeof(struct archdr));
+       if (BUGLVL(D_DURING))
+               pr_info("struct sizes: %Zd %Zd %Zd %Zd %Zd\n",
+                       sizeof(struct arc_hardware),
+                       sizeof(struct arc_rfc1201),
+                       sizeof(struct arc_rfc1051),
+                       sizeof(struct arc_eth_encap),
+                       sizeof(struct archdr));
 
        return 0;
 }
@@ -150,9 +140,7 @@ static void __exit arcnet_exit(void)
 module_init(arcnet_init);
 module_exit(arcnet_exit);
 
-/*
- * Dump the contents of an sk_buff
- */
+/* Dump the contents of an sk_buff */
 #if ARCNET_DEBUG_MAX & D_SKB
 void arcnet_dump_skb(struct net_device *dev,
                     struct sk_buff *skb, char *desc)
@@ -164,14 +152,10 @@ void arcnet_dump_skb(struct net_device *dev,
        print_hex_dump(KERN_DEBUG, hdr, DUMP_PREFIX_OFFSET,
                       16, 1, skb->data, skb->len, true);
 }
-
 EXPORT_SYMBOL(arcnet_dump_skb);
 #endif
 
-
-/*
- * Dump the contents of an ARCnet buffer
- */
+/* Dump the contents of an ARCnet buffer */
 #if (ARCNET_DEBUG_MAX & (D_RX | D_TX))
 static void arcnet_dump_packet(struct net_device *dev, int bufnum,
                               char *desc, int take_arcnet_lock)
@@ -183,12 +167,13 @@ static void arcnet_dump_packet(struct net_device *dev, int bufnum,
        char hdr[32];
 
        /* hw.copy_from_card expects IRQ context so take the IRQ lock
-          to keep it single threaded */
-       if(take_arcnet_lock)
+        * to keep it single threaded
+        */
+       if (take_arcnet_lock)
                spin_lock_irqsave(&lp->lock, flags);
 
        lp->hw.copy_from_card(dev, bufnum, 0, buf, 512);
-       if(take_arcnet_lock)
+       if (take_arcnet_lock)
                spin_unlock_irqrestore(&lp->lock, flags);
 
        /* if the offset[0] byte is nonzero, this is a 256-byte packet */
@@ -202,13 +187,76 @@ static void arcnet_dump_packet(struct net_device *dev, int bufnum,
 
 #else
 
-#define arcnet_dump_packet(dev, bufnum, desc,take_arcnet_lock) do { } while (0)
+#define arcnet_dump_packet(dev, bufnum, desc, take_arcnet_lock) do { } while (0)
 
 #endif
 
+/* Trigger a LED event in response to a ARCNET device event */
+void arcnet_led_event(struct net_device *dev, enum arcnet_led_event event)
+{
+       struct arcnet_local *lp = netdev_priv(dev);
+       unsigned long led_delay = 350;
+       unsigned long tx_delay = 50;
+
+       switch (event) {
+       case ARCNET_LED_EVENT_RECON:
+               led_trigger_blink_oneshot(lp->recon_led_trig,
+                                         &led_delay, &led_delay, 0);
+               break;
+       case ARCNET_LED_EVENT_OPEN:
+               led_trigger_event(lp->tx_led_trig, LED_OFF);
+               led_trigger_event(lp->recon_led_trig, LED_OFF);
+               break;
+       case ARCNET_LED_EVENT_STOP:
+               led_trigger_event(lp->tx_led_trig, LED_OFF);
+               led_trigger_event(lp->recon_led_trig, LED_OFF);
+               break;
+       case ARCNET_LED_EVENT_TX:
+               led_trigger_blink_oneshot(lp->tx_led_trig,
+                                         &tx_delay, &tx_delay, 0);
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(arcnet_led_event);
 
-/*
- * Unregister a protocol driver from the arc_proto_map.  Protocol drivers
+static void arcnet_led_release(struct device *gendev, void *res)
+{
+       struct arcnet_local *lp = netdev_priv(to_net_dev(gendev));
+
+       led_trigger_unregister_simple(lp->tx_led_trig);
+       led_trigger_unregister_simple(lp->recon_led_trig);
+}
+
+/* Register ARCNET LED triggers for a arcnet device
+ *
+ * This is normally called from a driver's probe function
+ */
+void devm_arcnet_led_init(struct net_device *netdev, int index, int subid)
+{
+       struct arcnet_local *lp = netdev_priv(netdev);
+       void *res;
+
+       res = devres_alloc(arcnet_led_release, 0, GFP_KERNEL);
+       if (!res) {
+               netdev_err(netdev, "cannot register LED triggers\n");
+               return;
+       }
+
+       snprintf(lp->tx_led_trig_name, sizeof(lp->tx_led_trig_name),
+                "arc%d-%d-tx", index, subid);
+       snprintf(lp->recon_led_trig_name, sizeof(lp->recon_led_trig_name),
+                "arc%d-%d-recon", index, subid);
+
+       led_trigger_register_simple(lp->tx_led_trig_name,
+                                   &lp->tx_led_trig);
+       led_trigger_register_simple(lp->recon_led_trig_name,
+                                   &lp->recon_led_trig);
+
+       devres_add(&netdev->dev, res);
+}
+EXPORT_SYMBOL_GPL(devm_arcnet_led_init);
+
+/* Unregister a protocol driver from the arc_proto_map.  Protocol drivers
  * are responsible for registering themselves, but the unregister routine
  * is pretty generic so we'll do it here.
  */
@@ -228,12 +276,11 @@ void arcnet_unregister_proto(struct ArcProto *proto)
                        arc_proto_map[count] = arc_proto_default;
        }
 }
+EXPORT_SYMBOL(arcnet_unregister_proto);
 
-
-/*
- * Add a buffer to the queue.  Only the interrupt handler is allowed to do
+/* Add a buffer to the queue.  Only the interrupt handler is allowed to do
  * this, unless interrupts are disabled.
- * 
+ *
  * Note: we don't check for a full queue, since there aren't enough buffers
  * to more than fill it.
  */
@@ -245,19 +292,17 @@ static void release_arcbuf(struct net_device *dev, int bufnum)
        lp->buf_queue[lp->first_free_buf++] = bufnum;
        lp->first_free_buf %= 5;
 
-       BUGLVL(D_DURING) {
-               BUGMSG(D_DURING, "release_arcbuf: freed #%d; buffer queue is now: ",
-                      bufnum);
-               for (i = lp->next_buf; i != lp->first_free_buf; i = (i+1) % 5)
-                       BUGMSG2(D_DURING, "#%d ", lp->buf_queue[i]);
-               BUGMSG2(D_DURING, "\n");
+       if (BUGLVL(D_DURING)) {
+               arc_printk(D_DURING, dev, "release_arcbuf: freed #%d; buffer queue is now: ",
+                          bufnum);
+               for (i = lp->next_buf; i != lp->first_free_buf; i = (i + 1) % 5)
+                       arc_cont(D_DURING, "#%d ", lp->buf_queue[i]);
+               arc_cont(D_DURING, "\n");
        }
 }
 
-
-/*
- * Get a buffer from the queue.  If this returns -1, there are no buffers
- * available.
+/* Get a buffer from the queue.
+ * If this returns -1, there are no buffers available.
  */
 static int get_arcbuf(struct net_device *dev)
 {
@@ -266,34 +311,32 @@ static int get_arcbuf(struct net_device *dev)
 
        if (!atomic_dec_and_test(&lp->buf_lock)) {
                /* already in this function */
-               BUGMSG(D_NORMAL, "get_arcbuf: overlap (%d)!\n",
-                      lp->buf_lock.counter);
-       }
-       else {                  /* we can continue */
+               arc_printk(D_NORMAL, dev, "get_arcbuf: overlap (%d)!\n",
+                          lp->buf_lock.counter);
+       } else {                        /* we can continue */
                if (lp->next_buf >= 5)
                        lp->next_buf -= 5;
 
-               if (lp->next_buf == lp->first_free_buf)
-                       BUGMSG(D_NORMAL, "get_arcbuf: BUG: no buffers are available??\n");
-               else {
+               if (lp->next_buf == lp->first_free_buf) {
+                       arc_printk(D_NORMAL, dev, "get_arcbuf: BUG: no buffers are available??\n");
+               else {
                        buf = lp->buf_queue[lp->next_buf++];
                        lp->next_buf %= 5;
                }
        }
 
-
-       BUGLVL(D_DURING) {
-               BUGMSG(D_DURING, "get_arcbuf: got #%d; buffer queue is now: ", buf);
-               for (i = lp->next_buf; i != lp->first_free_buf; i = (i+1) % 5)
-                       BUGMSG2(D_DURING, "#%d ", lp->buf_queue[i]);
-               BUGMSG2(D_DURING, "\n");
+       if (BUGLVL(D_DURING)) {
+               arc_printk(D_DURING, dev, "get_arcbuf: got #%d; buffer queue is now: ",
+                          buf);
+               for (i = lp->next_buf; i != lp->first_free_buf; i = (i + 1) % 5)
+                       arc_cont(D_DURING, "#%d ", lp->buf_queue[i]);
+               arc_cont(D_DURING, "\n");
        }
 
        atomic_inc(&lp->buf_lock);
        return buf;
 }
 
-
 static int choose_mtu(void)
 {
        int count, mtu = 65535;
@@ -336,7 +379,16 @@ static void arcdev_setup(struct net_device *dev)
 
        /* New-style flags. */
        dev->flags = IFF_BROADCAST;
+}
 
+static void arcnet_timer(unsigned long data)
+{
+       struct net_device *dev = (struct net_device *)data;
+
+       if (!netif_carrier_ok(dev)) {
+               netif_carrier_on(dev);
+               netdev_info(dev, "link up\n");
+       }
 }
 
 struct net_device *alloc_arcdev(const char *name)
@@ -346,16 +398,20 @@ struct net_device *alloc_arcdev(const char *name)
        dev = alloc_netdev(sizeof(struct arcnet_local),
                           name && *name ? name : "arc%d", NET_NAME_UNKNOWN,
                           arcdev_setup);
-       if(dev) {
+       if (dev) {
                struct arcnet_local *lp = netdev_priv(dev);
+
                spin_lock_init(&lp->lock);
+               init_timer(&lp->timer);
+               lp->timer.data = (unsigned long) dev;
+               lp->timer.function = arcnet_timer;
        }
 
        return dev;
 }
+EXPORT_SYMBOL(alloc_arcdev);
 
-/*
- * Open/initialize the board.  This is called sometime after booting when
+/* Open/initialize the board.  This is called sometime after booting when
  * the 'ifconfig' program is run.
  *
  * This routine should set everything up anew at each open, even registers
@@ -367,34 +423,33 @@ int arcnet_open(struct net_device *dev)
        struct arcnet_local *lp = netdev_priv(dev);
        int count, newmtu, error;
 
-       BUGMSG(D_INIT,"opened.");
+       arc_printk(D_INIT, dev, "opened.");
 
        if (!try_module_get(lp->hw.owner))
                return -ENODEV;
 
-       BUGLVL(D_PROTO) {
-               BUGMSG(D_PROTO, "protocol map (default is '%c'): ",
-                      arc_proto_default->suffix);
+       if (BUGLVL(D_PROTO)) {
+               arc_printk(D_PROTO, dev, "protocol map (default is '%c'): ",
+                          arc_proto_default->suffix);
                for (count = 0; count < 256; count++)
-                       BUGMSG2(D_PROTO, "%c", arc_proto_map[count]->suffix);
-               BUGMSG2(D_PROTO, "\n");
+                       arc_cont(D_PROTO, "%c", arc_proto_map[count]->suffix);
+               arc_cont(D_PROTO, "\n");
        }
 
-
-       BUGMSG(D_INIT, "arcnet_open: resetting card.\n");
+       arc_printk(D_INIT, dev, "arcnet_open: resetting card.\n");
 
        /* try to put the card in a defined state - if it fails the first
         * time, actually reset it.
         */
        error = -ENODEV;
-       if (ARCRESET(0) && ARCRESET(1))
+       if (lp->hw.reset(dev, 0) && lp->hw.reset(dev, 1))
                goto out_module_put;
 
        newmtu = choose_mtu();
        if (newmtu < dev->mtu)
                dev->mtu = newmtu;
 
-       BUGMSG(D_INIT, "arcnet_open: mtu: %d.\n", dev->mtu);
+       arc_printk(D_INIT, dev, "arcnet_open: mtu: %d.\n", dev->mtu);
 
        /* autodetect the encapsulation for each host. */
        memset(lp->default_proto, 0, sizeof(lp->default_proto));
@@ -425,52 +480,57 @@ int arcnet_open(struct net_device *dev)
                lp->hw.open(dev);
 
        if (dev->dev_addr[0] == 0)
-               BUGMSG(D_NORMAL, "WARNING!  Station address 00 is reserved "
-                      "for broadcasts!\n");
+               arc_printk(D_NORMAL, dev, "WARNING!  Station address 00 is reserved for broadcasts!\n");
        else if (dev->dev_addr[0] == 255)
-               BUGMSG(D_NORMAL, "WARNING!  Station address FF may confuse "
-                      "DOS networking programs!\n");
+               arc_printk(D_NORMAL, dev, "WARNING!  Station address FF may confuse DOS networking programs!\n");
 
-       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
-       if (ASTATUS() & RESETflag) {
-               BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
-               ACOMMAND(CFLAGScmd | RESETclear);
+       arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
+       if (lp->hw.status(dev) & RESETflag) {
+               arc_printk(D_DEBUG, dev, "%s: %d: %s\n",
+                          __FILE__, __LINE__, __func__);
+               lp->hw.command(dev, CFLAGScmd | RESETclear);
        }
 
-
-       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
+       arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
        /* make sure we're ready to receive IRQ's. */
-       AINTMASK(0);
+       lp->hw.intmask(dev, 0);
        udelay(1);              /* give it time to set the mask before
                                 * we reset it again. (may not even be
                                 * necessary)
                                 */
-       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
+       arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
        lp->intmask = NORXflag | RECONflag;
-       AINTMASK(lp->intmask);
-       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
+       lp->hw.intmask(dev, lp->intmask);
+       arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
 
+       netif_carrier_off(dev);
        netif_start_queue(dev);
+       mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000));
 
+       arcnet_led_event(dev, ARCNET_LED_EVENT_OPEN);
        return 0;
 
  out_module_put:
        module_put(lp->hw.owner);
        return error;
 }
-
+EXPORT_SYMBOL(arcnet_open);
 
 /* The inverse routine to arcnet_open - shuts down the card. */
 int arcnet_close(struct net_device *dev)
 {
        struct arcnet_local *lp = netdev_priv(dev);
 
+       arcnet_led_event(dev, ARCNET_LED_EVENT_STOP);
+       del_timer_sync(&lp->timer);
+
        netif_stop_queue(dev);
+       netif_carrier_off(dev);
 
        /* flush TX and disable RX */
-       AINTMASK(0);
-       ACOMMAND(NOTXcmd);      /* stop transmit */
-       ACOMMAND(NORXcmd);      /* disable receive */
+       lp->hw.intmask(dev, 0);
+       lp->hw.command(dev, NOTXcmd);   /* stop transmit */
+       lp->hw.command(dev, NORXcmd);   /* disable receive */
        mdelay(1);
 
        /* shut down the card */
@@ -478,7 +538,7 @@ int arcnet_close(struct net_device *dev)
        module_put(lp->hw.owner);
        return 0;
 }
-
+EXPORT_SYMBOL(arcnet_close);
 
 static int arcnet_header(struct sk_buff *skb, struct net_device *dev,
                         unsigned short type, const void *daddr,
@@ -488,48 +548,44 @@ static int arcnet_header(struct sk_buff *skb, struct net_device *dev,
        uint8_t _daddr, proto_num;
        struct ArcProto *proto;
 
-       BUGMSG(D_DURING,
-           "create header from %d to %d; protocol %d (%Xh); size %u.\n",
-              saddr ? *(uint8_t *) saddr : -1,
-              daddr ? *(uint8_t *) daddr : -1,
-              type, type, len);
-
-       if (skb->len!=0 && len != skb->len)
-               BUGMSG(D_NORMAL, "arcnet_header: Yikes!  skb->len(%d) != len(%d)!\n",
-                      skb->len, len);
-
-
-       /* Type is host order - ? */
-       if(type == ETH_P_ARCNET) {
-               proto = arc_raw_proto;
-               BUGMSG(D_DEBUG, "arc_raw_proto used. proto='%c'\n",proto->suffix);
-               _daddr = daddr ? *(uint8_t *) daddr : 0;
-       }
-       else if (!daddr) {
-               /*
-                * if the dest addr isn't provided, we can't choose an encapsulation!
-                * Store the packet type (eg. ETH_P_IP) for now, and we'll push on a
-                * real header when we do rebuild_header.
-                */
-               *(uint16_t *) skb_push(skb, 2) = type;
-               /*
-                * XXX: Why not use skb->mac_len?
+       arc_printk(D_DURING, dev,
+                  "create header from %d to %d; protocol %d (%Xh); size %u.\n",
+                  saddr ? *(uint8_t *)saddr : -1,
+                  daddr ? *(uint8_t *)daddr : -1,
+                  type, type, len);
+
+       if (skb->len != 0 && len != skb->len)
+               arc_printk(D_NORMAL, dev, "arcnet_header: Yikes!  skb->len(%d) != len(%d)!\n",
+                          skb->len, len);
+
+       /* Type is host order - ? */
+       if (type == ETH_P_ARCNET) {
+               proto = arc_raw_proto;
+               arc_printk(D_DEBUG, dev, "arc_raw_proto used. proto='%c'\n",
+                          proto->suffix);
+               _daddr = daddr ? *(uint8_t *)daddr : 0;
+       } else if (!daddr) {
+               /* if the dest addr isn't provided, we can't choose an
+                * encapsulation!  Store the packet type (eg. ETH_P_IP)
+                * for now, and we'll push on a real header when we do
+                * rebuild_header.
                 */
+               *(uint16_t *)skb_push(skb, 2) = type;
+               /* XXX: Why not use skb->mac_len? */
                if (skb->network_header - skb->mac_header != 2)
-                       BUGMSG(D_NORMAL, "arcnet_header: Yikes!  diff (%d) is not 2!\n",
-                              (int)(skb->network_header - skb->mac_header));
+                       arc_printk(D_NORMAL, dev, "arcnet_header: Yikes!  diff (%u) is not 2!\n",
+                                  skb->network_header - skb->mac_header);
                return -2;      /* return error -- can't transmit yet! */
-       }
-       else {
+       } else {
                /* otherwise, we can just add the header as usual. */
-               _daddr = *(uint8_t *) daddr;
+               _daddr = *(uint8_t *)daddr;
                proto_num = lp->default_proto[_daddr];
                proto = arc_proto_map[proto_num];
-               BUGMSG(D_DURING, "building header for %02Xh using protocol '%c'\n",
-                      proto_num, proto->suffix);
+               arc_printk(D_DURING, dev, "building header for %02Xh using protocol '%c'\n",
+                          proto_num, proto->suffix);
                if (proto == &arc_proto_null && arc_bcast_proto != proto) {
-                       BUGMSG(D_DURING, "actually, let's use '%c' instead.\n",
-                              arc_bcast_proto->suffix);
+                       arc_printk(D_DURING, dev, "actually, let's use '%c' instead.\n",
+                                  arc_bcast_proto->suffix);
                        proto = arc_bcast_proto;
                }
        }
@@ -538,7 +594,7 @@ static int arcnet_header(struct sk_buff *skb, struct net_device *dev,
 
 /* Called by the kernel in order to transmit a packet. */
 netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
-                                    struct net_device *dev)
+                              struct net_device *dev)
 {
        struct arcnet_local *lp = netdev_priv(dev);
        struct archdr *pkt;
@@ -546,23 +602,24 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
        struct ArcProto *proto;
        int txbuf;
        unsigned long flags;
-       int freeskb, retval;
+       int retval;
 
-       BUGMSG(D_DURING,
-              "transmit requested (status=%Xh, txbufs=%d/%d, len=%d, protocol %x)\n",
-              ASTATUS(), lp->cur_tx, lp->next_tx, skb->len,skb->protocol);
+       arc_printk(D_DURING, dev,
+                  "transmit requested (status=%Xh, txbufs=%d/%d, len=%d, protocol %x)\n",
+                  lp->hw.status(dev), lp->cur_tx, lp->next_tx, skb->len, skb->protocol);
 
-       pkt = (struct archdr *) skb->data;
+       pkt = (struct archdr *)skb->data;
        soft = &pkt->soft.rfc1201;
        proto = arc_proto_map[soft->proto];
 
-       BUGMSG(D_SKB_SIZE, "skb: transmitting %d bytes to %02X\n",
-               skb->len, pkt->hard.dest);
-       BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "tx");
+       arc_printk(D_SKB_SIZE, dev, "skb: transmitting %d bytes to %02X\n",
+                  skb->len, pkt->hard.dest);
+       if (BUGLVL(D_SKB))
+               arcnet_dump_skb(dev, skb, "tx");
 
        /* fits in one packet? */
        if (skb->len - ARC_HDR_SIZE > XMTU && !proto->continue_tx) {
-               BUGMSG(D_NORMAL, "fixme: packet too large: compensating badly!\n");
+               arc_printk(D_NORMAL, dev, "fixme: packet too large: compensating badly!\n");
                dev_kfree_skb(skb);
                return NETDEV_TX_OK;    /* don't try again */
        }
@@ -571,96 +628,94 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
        netif_stop_queue(dev);
 
        spin_lock_irqsave(&lp->lock, flags);
-       AINTMASK(0);
-       if(lp->next_tx == -1)
+       lp->hw.intmask(dev, 0);
+       if (lp->next_tx == -1)
                txbuf = get_arcbuf(dev);
-       else {
+       else
                txbuf = -1;
-       }
+
        if (txbuf != -1) {
                if (proto->prepare_tx(dev, pkt, skb->len, txbuf) &&
                    !proto->ack_tx) {
                        /* done right away and we don't want to acknowledge
-                          the package later - forget about it now */
+                        *  the package later - forget about it now
+                        */
                        dev->stats.tx_bytes += skb->len;
-                       freeskb = 1;
+                       dev_kfree_skb(skb);
                } else {
                        /* do it the 'split' way */
                        lp->outgoing.proto = proto;
                        lp->outgoing.skb = skb;
                        lp->outgoing.pkt = pkt;
 
-                       freeskb = 0;
-
                        if (proto->continue_tx &&
                            proto->continue_tx(dev, txbuf)) {
-                         BUGMSG(D_NORMAL,
-                                "bug! continue_tx finished the first time! "
-                                "(proto='%c')\n", proto->suffix);
+                               arc_printk(D_NORMAL, dev,
+                                          "bug! continue_tx finished the first time! (proto='%c')\n",
+                                          proto->suffix);
                        }
                }
                retval = NETDEV_TX_OK;
                lp->next_tx = txbuf;
        } else {
                retval = NETDEV_TX_BUSY;
-               freeskb = 0;
        }
 
-       BUGMSG(D_DEBUG, "%s: %d: %s, status: %x\n",__FILE__,__LINE__,__func__,ASTATUS());
+       arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n",
+                  __FILE__, __LINE__, __func__, lp->hw.status(dev));
        /* make sure we didn't ignore a TX IRQ while we were in here */
-       AINTMASK(0);
+       lp->hw.intmask(dev, 0);
 
-       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
-       lp->intmask |= TXFREEflag|EXCNAKflag;
-       AINTMASK(lp->intmask);
-       BUGMSG(D_DEBUG, "%s: %d: %s, status: %x\n",__FILE__,__LINE__,__func__,ASTATUS());
+       arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
+       lp->intmask |= TXFREEflag | EXCNAKflag;
+       lp->hw.intmask(dev, lp->intmask);
+       arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n",
+                  __FILE__, __LINE__, __func__, lp->hw.status(dev));
+
+       arcnet_led_event(dev, ARCNET_LED_EVENT_TX);
 
        spin_unlock_irqrestore(&lp->lock, flags);
-       if (freeskb) {
-               dev_kfree_skb(skb);
-       }
        return retval;          /* no need to try again */
 }
+EXPORT_SYMBOL(arcnet_send_packet);
 
-
-/*
- * Actually start transmitting a packet that was loaded into a buffer
+/* Actually start transmitting a packet that was loaded into a buffer
  * by prepare_tx.  This should _only_ be called by the interrupt handler.
  */
 static int go_tx(struct net_device *dev)
 {
        struct arcnet_local *lp = netdev_priv(dev);
 
-       BUGMSG(D_DURING, "go_tx: status=%Xh, intmask=%Xh, next_tx=%d, cur_tx=%d\n",
-              ASTATUS(), lp->intmask, lp->next_tx, lp->cur_tx);
+       arc_printk(D_DURING, dev, "go_tx: status=%Xh, intmask=%Xh, next_tx=%d, cur_tx=%d\n",
+                  lp->hw.status(dev), lp->intmask, lp->next_tx, lp->cur_tx);
 
        if (lp->cur_tx != -1 || lp->next_tx == -1)
                return 0;
 
-       BUGLVL(D_TX) arcnet_dump_packet(dev, lp->next_tx, "go_tx", 0);
+       if (BUGLVL(D_TX))
+               arcnet_dump_packet(dev, lp->next_tx, "go_tx", 0);
 
        lp->cur_tx = lp->next_tx;
        lp->next_tx = -1;
 
        /* start sending */
-       ACOMMAND(TXcmd | (lp->cur_tx << 3));
+       lp->hw.command(dev, TXcmd | (lp->cur_tx << 3));
 
        dev->stats.tx_packets++;
        lp->lasttrans_dest = lp->lastload_dest;
        lp->lastload_dest = 0;
        lp->excnak_pending = 0;
-       lp->intmask |= TXFREEflag|EXCNAKflag;
+       lp->intmask |= TXFREEflag | EXCNAKflag;
 
        return 1;
 }
 
-
 /* Called by the kernel when transmit times out */
 void arcnet_timeout(struct net_device *dev)
 {
        unsigned long flags;
        struct arcnet_local *lp = netdev_priv(dev);
-       int status = ASTATUS();
+       int status = lp->hw.status(dev);
        char *msg;
 
        spin_lock_irqsave(&lp->lock, flags);
@@ -670,30 +725,29 @@ void arcnet_timeout(struct net_device *dev)
                msg = "";
                dev->stats.tx_aborted_errors++;
                lp->timed_out = 1;
-               ACOMMAND(NOTXcmd | (lp->cur_tx << 3));
+               lp->hw.command(dev, NOTXcmd | (lp->cur_tx << 3));
        }
        dev->stats.tx_errors++;
 
        /* make sure we didn't miss a TX or a EXC NAK IRQ */
-       AINTMASK(0);
-       lp->intmask |= TXFREEflag|EXCNAKflag;
-       AINTMASK(lp->intmask);
-       
+       lp->hw.intmask(dev, 0);
+       lp->intmask |= TXFREEflag | EXCNAKflag;
+       lp->hw.intmask(dev, lp->intmask);
+
        spin_unlock_irqrestore(&lp->lock, flags);
 
-       if (time_after(jiffies, lp->last_timeout + 10*HZ)) {
-               BUGMSG(D_EXTRA, "tx timed out%s (status=%Xh, intmask=%Xh, dest=%02Xh)\n",
-                      msg, status, lp->intmask, lp->lasttrans_dest);
+       if (time_after(jiffies, lp->last_timeout + 10 * HZ)) {
+               arc_printk(D_EXTRA, dev, "tx timed out%s (status=%Xh, intmask=%Xh, dest=%02Xh)\n",
+                          msg, status, lp->intmask, lp->lasttrans_dest);
                lp->last_timeout = jiffies;
        }
 
        if (lp->cur_tx == -1)
                netif_wake_queue(dev);
 }
+EXPORT_SYMBOL(arcnet_timeout);
 
-
-/*
- * The typical workload of the driver: Handle the network interface
+/* The typical workload of the driver: Handle the network interface
  * interrupts. Establish which device needs attention, and call the correct
  * chipset interrupt handler.
  */
@@ -704,125 +758,125 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
        int recbuf, status, diagstatus, didsomething, boguscount;
        int retval = IRQ_NONE;
 
-       BUGMSG(D_DURING, "\n");
+       arc_printk(D_DURING, dev, "\n");
 
-       BUGMSG(D_DURING, "in arcnet_interrupt\n");
+       arc_printk(D_DURING, dev, "in arcnet_interrupt\n");
 
        lp = netdev_priv(dev);
        BUG_ON(!lp);
-               
+
        spin_lock(&lp->lock);
 
-       /*
-        * RESET flag was enabled - if device is not running, we must clear it right
-        * away (but nothing else).
+       /* RESET flag was enabled - if device is not running, we must
+        * clear it right away (but nothing else).
         */
        if (!netif_running(dev)) {
-               if (ASTATUS() & RESETflag)
-                       ACOMMAND(CFLAGScmd | RESETclear);
-               AINTMASK(0);
+               if (lp->hw.status(dev) & RESETflag)
+                       lp->hw.command(dev, CFLAGScmd | RESETclear);
+               lp->hw.intmask(dev, 0);
                spin_unlock(&lp->lock);
                return retval;
        }
 
-       BUGMSG(D_DURING, "in arcnet_inthandler (status=%Xh, intmask=%Xh)\n",
-              ASTATUS(), lp->intmask);
+       arc_printk(D_DURING, dev, "in arcnet_inthandler (status=%Xh, intmask=%Xh)\n",
+                  lp->hw.status(dev), lp->intmask);
 
        boguscount = 5;
        do {
-               status = ASTATUS();
-                diagstatus = (status >> 8) & 0xFF;
+               status = lp->hw.status(dev);
+               diagstatus = (status >> 8) & 0xFF;
 
-               BUGMSG(D_DEBUG, "%s: %d: %s: status=%x\n",
-                       __FILE__,__LINE__,__func__,status);
+               arc_printk(D_DEBUG, dev, "%s: %d: %s: status=%x\n",
+                          __FILE__, __LINE__, __func__, status);
                didsomething = 0;
 
-               /*
-                * RESET flag was enabled - card is resetting and if RX is
+               /* RESET flag was enabled - card is resetting and if RX is
                 * disabled, it's NOT because we just got a packet.
-                * 
-                * The card is in an undefined state.  Clear it out and start over.
+                *
+                * The card is in an undefined state.
+                * Clear it out and start over.
                 */
                if (status & RESETflag) {
-                       BUGMSG(D_NORMAL, "spurious reset (status=%Xh)\n", status);
+                       arc_printk(D_NORMAL, dev, "spurious reset (status=%Xh)\n",
+                                  status);
                        arcnet_close(dev);
                        arcnet_open(dev);
 
                        /* get out of the interrupt handler! */
                        break;
                }
-               /* 
-                * RX is inhibited - we must have received something. Prepare to
-                * receive into the next buffer.
-                * 
-                * We don't actually copy the received packet from the card until
-                * after the transmit handler runs (and possibly launches the next
-                * tx); this should improve latency slightly if we get both types
-                * of interrupts at once. 
+               /* RX is inhibited - we must have received something.
+                * Prepare to receive into the next buffer.
+                *
+                * We don't actually copy the received packet from the card
+                * until after the transmit handler runs (and possibly
+                * launches the next tx); this should improve latency slightly
+                * if we get both types of interrupts at once.
                 */
                recbuf = -1;
                if (status & lp->intmask & NORXflag) {
                        recbuf = lp->cur_rx;
-                       BUGMSG(D_DURING, "Buffer #%d: receive irq (status=%Xh)\n",
-                              recbuf, status);
+                       arc_printk(D_DURING, dev, "Buffer #%d: receive irq (status=%Xh)\n",
+                                  recbuf, status);
 
                        lp->cur_rx = get_arcbuf(dev);
                        if (lp->cur_rx != -1) {
-                               BUGMSG(D_DURING, "enabling receive to buffer #%d\n",
-                                      lp->cur_rx);
-                               ACOMMAND(RXcmd | (lp->cur_rx << 3) | RXbcasts);
+                               arc_printk(D_DURING, dev, "enabling receive to buffer #%d\n",
+                                          lp->cur_rx);
+                               lp->hw.command(dev, RXcmd | (lp->cur_rx << 3) | RXbcasts);
                        }
                        didsomething++;
                }
 
-               if((diagstatus & EXCNAKflag)) {
-                       BUGMSG(D_DURING, "EXCNAK IRQ (diagstat=%Xh)\n",
-                              diagstatus);
+               if ((diagstatus & EXCNAKflag)) {
+                       arc_printk(D_DURING, dev, "EXCNAK IRQ (diagstat=%Xh)\n",
+                                  diagstatus);
 
-                        ACOMMAND(NOTXcmd);      /* disable transmit */
-                        lp->excnak_pending = 1;
+                       lp->hw.command(dev, NOTXcmd);      /* disable transmit */
+                       lp->excnak_pending = 1;
 
-                        ACOMMAND(EXCNAKclear);
+                       lp->hw.command(dev, EXCNAKclear);
                        lp->intmask &= ~(EXCNAKflag);
-                        didsomething++;
-                }
-
+                       didsomething++;
+               }
 
                /* a transmit finished, and we're interested in it. */
                if ((status & lp->intmask & TXFREEflag) || lp->timed_out) {
-                       lp->intmask &= ~(TXFREEflag|EXCNAKflag);
+                       lp->intmask &= ~(TXFREEflag | EXCNAKflag);
 
-                       BUGMSG(D_DURING, "TX IRQ (stat=%Xh)\n", status);
+                       arc_printk(D_DURING, dev, "TX IRQ (stat=%Xh)\n",
+                                  status);
 
                        if (lp->cur_tx != -1 && !lp->timed_out) {
-                               if(!(status & TXACKflag)) {
+                               if (!(status & TXACKflag)) {
                                        if (lp->lasttrans_dest != 0) {
-                                               BUGMSG(D_EXTRA,
-                                                      "transmit was not acknowledged! "
-                                                      "(status=%Xh, dest=%02Xh)\n",
-                                                      status, lp->lasttrans_dest);
+                                               arc_printk(D_EXTRA, dev,
+                                                          "transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
+                                                          status,
+                                                          lp->lasttrans_dest);
                                                dev->stats.tx_errors++;
                                                dev->stats.tx_carrier_errors++;
                                        } else {
-                                               BUGMSG(D_DURING,
-                                                      "broadcast was not acknowledged; that's normal "
-                                                      "(status=%Xh, dest=%02Xh)\n",
-                                                      status, lp->lasttrans_dest);
+                                               arc_printk(D_DURING, dev,
+                                                          "broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
+                                                          status,
+                                                          lp->lasttrans_dest);
                                        }
                                }
 
                                if (lp->outgoing.proto &&
                                    lp->outgoing.proto->ack_tx) {
-                                 int ackstatus;
-                                 if(status & TXACKflag)
-                                    ackstatus=2;
-                                  else if(lp->excnak_pending)
-                                    ackstatus=1;
-                                  else
-                                    ackstatus=0;
-
-                                  lp->outgoing.proto
-                                    ->ack_tx(dev, ackstatus);
+                                       int ackstatus;
+
+                                       if (status & TXACKflag)
+                                               ackstatus = 2;
+                                       else if (lp->excnak_pending)
+                                               ackstatus = 1;
+                                       else
+                                               ackstatus = 0;
+
+                                       lp->outgoing.proto
+                                               ->ack_tx(dev, ackstatus);
                                }
                        }
                        if (lp->cur_tx != -1)
@@ -836,17 +890,18 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
                        go_tx(dev);
 
                        /* continue a split packet, if any */
-                       if (lp->outgoing.proto && lp->outgoing.proto->continue_tx) {
+                       if (lp->outgoing.proto &&
+                           lp->outgoing.proto->continue_tx) {
                                int txbuf = get_arcbuf(dev);
+
                                if (txbuf != -1) {
                                        if (lp->outgoing.proto->continue_tx(dev, txbuf)) {
                                                /* that was the last segment */
                                                dev->stats.tx_bytes += lp->outgoing.skb->len;
-                                               if(!lp->outgoing.proto->ack_tx)
-                                                 {
-                                                   dev_kfree_skb_irq(lp->outgoing.skb);
-                                                   lp->outgoing.proto = NULL;
-                                                 }
+                                               if (!lp->outgoing.proto->ack_tx) {
+                                                       dev_kfree_skb_irq(lp->outgoing.skb);
+                                                       lp->outgoing.proto = NULL;
+                                               }
                                        }
                                        lp->next_tx = txbuf;
                                }
@@ -857,7 +912,8 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
                }
                /* now process the received packet, if any */
                if (recbuf != -1) {
-                       BUGLVL(D_RX) arcnet_dump_packet(dev, recbuf, "rx irq", 0);
+                       if (BUGLVL(D_RX))
+                               arcnet_dump_packet(dev, recbuf, "rx irq", 0);
 
                        arcnet_rx(dev, recbuf);
                        release_arcbuf(dev, recbuf);
@@ -865,32 +921,39 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
                        didsomething++;
                }
                if (status & lp->intmask & RECONflag) {
-                       ACOMMAND(CFLAGScmd | CONFIGclear);
+                       lp->hw.command(dev, CFLAGScmd | CONFIGclear);
                        dev->stats.tx_carrier_errors++;
 
-                       BUGMSG(D_RECON, "Network reconfiguration detected (status=%Xh)\n",
-                              status);
+                       arc_printk(D_RECON, dev, "Network reconfiguration detected (status=%Xh)\n",
+                                  status);
+                       if (netif_carrier_ok(dev)) {
+                               netif_carrier_off(dev);
+                               netdev_info(dev, "link down\n");
+                       }
+                       mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000));
+
+                       arcnet_led_event(dev, ARCNET_LED_EVENT_RECON);
                        /* MYRECON bit is at bit 7 of diagstatus */
-                       if(diagstatus & 0x80)
-                               BUGMSG(D_RECON,"Put out that recon myself\n");
+                       if (diagstatus & 0x80)
+                               arc_printk(D_RECON, dev, "Put out that recon myself\n");
 
                        /* is the RECON info empty or old? */
                        if (!lp->first_recon || !lp->last_recon ||
                            time_after(jiffies, lp->last_recon + HZ * 10)) {
                                if (lp->network_down)
-                                       BUGMSG(D_NORMAL, "reconfiguration detected: cabling restored?\n");
+                                       arc_printk(D_NORMAL, dev, "reconfiguration detected: cabling restored?\n");
                                lp->first_recon = lp->last_recon = jiffies;
                                lp->num_recons = lp->network_down = 0;
 
-                               BUGMSG(D_DURING, "recon: clearing counters.\n");
+                               arc_printk(D_DURING, dev, "recon: clearing counters.\n");
                        } else {        /* add to current RECON counter */
                                lp->last_recon = jiffies;
                                lp->num_recons++;
 
-                               BUGMSG(D_DURING, "recon: counter=%d, time=%lds, net=%d\n",
-                                      lp->num_recons,
-                                (lp->last_recon - lp->first_recon) / HZ,
-                                      lp->network_down);
+                               arc_printk(D_DURING, dev, "recon: counter=%d, time=%lds, net=%d\n",
+                                          lp->num_recons,
+                                          (lp->last_recon - lp->first_recon) / HZ,
+                                          lp->network_down);
 
                                /* if network is marked up;
                                 * and first_recon and last_recon are 60+ apart;
@@ -902,46 +965,45 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
                                    (lp->last_recon - lp->first_recon) <= HZ * 60 &&
                                    lp->num_recons >= RECON_THRESHOLD) {
                                        lp->network_down = 1;
-                                       BUGMSG(D_NORMAL, "many reconfigurations detected: cabling problem?\n");
+                                       arc_printk(D_NORMAL, dev, "many reconfigurations detected: cabling problem?\n");
                                } else if (!lp->network_down &&
                                           lp->last_recon - lp->first_recon > HZ * 60) {
-                                       /* reset counters if we've gone for over a minute. */
+                                       /* reset counters if we've gone for
+                                        *  over a minute.
+                                        */
                                        lp->first_recon = lp->last_recon;
                                        lp->num_recons = 1;
                                }
                        }
                } else if (lp->network_down &&
-                               time_after(jiffies, lp->last_recon + HZ * 10)) {
+                          time_after(jiffies, lp->last_recon + HZ * 10)) {
                        if (lp->network_down)
-                               BUGMSG(D_NORMAL, "cabling restored?\n");
+                               arc_printk(D_NORMAL, dev, "cabling restored?\n");
                        lp->first_recon = lp->last_recon = 0;
                        lp->num_recons = lp->network_down = 0;
 
-                       BUGMSG(D_DURING, "not recon: clearing counters anyway.\n");
+                       arc_printk(D_DURING, dev, "not recon: clearing counters anyway.\n");
+                       netif_carrier_on(dev);
                }
 
-               if(didsomething) {
+               if (didsomething)
                        retval |= IRQ_HANDLED;
-               }
-       }
-       while (--boguscount && didsomething);
-
-       BUGMSG(D_DURING, "arcnet_interrupt complete (status=%Xh, count=%d)\n",
-              ASTATUS(), boguscount);
-       BUGMSG(D_DURING, "\n");
+       } while (--boguscount && didsomething);
 
+       arc_printk(D_DURING, dev, "arcnet_interrupt complete (status=%Xh, count=%d)\n",
+                  lp->hw.status(dev), boguscount);
+       arc_printk(D_DURING, dev, "\n");
 
-       AINTMASK(0);
+       lp->hw.intmask(dev, 0);
        udelay(1);
-       AINTMASK(lp->intmask);
-       
+       lp->hw.intmask(dev, lp->intmask);
+
        spin_unlock(&lp->lock);
        return retval;
 }
+EXPORT_SYMBOL(arcnet_interrupt);
 
-
-/*
- * This is a generic packet receiver that calls arcnet??_rx depending on the
+/* This is a generic packet receiver that calls arcnet??_rx depending on the
  * protocol ID found.
  */
 static void arcnet_rx(struct net_device *dev, int bufnum)
@@ -963,32 +1025,31 @@ static void arcnet_rx(struct net_device *dev, int bufnum)
        }
 
        /* get the full header, if possible */
-       if (sizeof(pkt.soft) <= length)
+       if (sizeof(pkt.soft) <= length) {
                lp->hw.copy_from_card(dev, bufnum, ofs, soft, sizeof(pkt.soft));
-       else {
+       else {
                memset(&pkt.soft, 0, sizeof(pkt.soft));
                lp->hw.copy_from_card(dev, bufnum, ofs, soft, length);
        }
 
-       BUGMSG(D_DURING, "Buffer #%d: received packet from %02Xh to %02Xh "
-              "(%d+4 bytes)\n",
-              bufnum, pkt.hard.source, pkt.hard.dest, length);
+       arc_printk(D_DURING, dev, "Buffer #%d: received packet from %02Xh to %02Xh (%d+4 bytes)\n",
+                  bufnum, pkt.hard.source, pkt.hard.dest, length);
 
        dev->stats.rx_packets++;
        dev->stats.rx_bytes += length + ARC_HDR_SIZE;
 
        /* call the right receiver for the protocol */
        if (arc_proto_map[soft->proto]->is_ip) {
-               BUGLVL(D_PROTO) {
+               if (BUGLVL(D_PROTO)) {
                        struct ArcProto
                        *oldp = arc_proto_map[lp->default_proto[pkt.hard.source]],
                        *newp = arc_proto_map[soft->proto];
 
                        if (oldp != newp) {
-                               BUGMSG(D_PROTO,
-                                      "got protocol %02Xh; encap for host %02Xh is now '%c'"
-                                      " (was '%c')\n", soft->proto, pkt.hard.source,
-                                      newp->suffix, oldp->suffix);
+                               arc_printk(D_PROTO, dev,
+                                          "got protocol %02Xh; encap for host %02Xh is now '%c' (was '%c')\n",
+                                          soft->proto, pkt.hard.source,
+                                          newp->suffix, oldp->suffix);
                        }
                }
 
@@ -1002,30 +1063,27 @@ static void arcnet_rx(struct net_device *dev, int bufnum)
        arc_proto_map[soft->proto]->rx(dev, bufnum, &pkt, length);
 }
 
-
 static void null_rx(struct net_device *dev, int bufnum,
                    struct archdr *pkthdr, int length)
 {
-       BUGMSG(D_PROTO,
-       "rx: don't know how to deal with proto %02Xh from host %02Xh.\n",
-              pkthdr->soft.rfc1201.proto, pkthdr->hard.source);
+       arc_printk(D_PROTO, dev,
+                  "rx: don't know how to deal with proto %02Xh from host %02Xh.\n",
+                  pkthdr->soft.rfc1201.proto, pkthdr->hard.source);
 }
 
-
 static int null_build_header(struct sk_buff *skb, struct net_device *dev,
                             unsigned short type, uint8_t daddr)
 {
        struct arcnet_local *lp = netdev_priv(dev);
 
-       BUGMSG(D_PROTO,
-              "tx: can't build header for encap %02Xh; load a protocol driver.\n",
-              lp->default_proto[daddr]);
+       arc_printk(D_PROTO, dev,
+                  "tx: can't build header for encap %02Xh; load a protocol driver.\n",
+                  lp->default_proto[daddr]);
 
        /* always fails */
        return 0;
 }
 
-
 /* the "do nothing" prepare_tx function warns that there's nothing to do. */
 static int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
                           int length, int bufnum)
@@ -1033,7 +1091,7 @@ static int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
        struct arcnet_local *lp = netdev_priv(dev);
        struct arc_hardware newpkt;
 
-       BUGMSG(D_PROTO, "tx: no encap for this host; load a protocol driver.\n");
+       arc_printk(D_PROTO, dev, "tx: no encap for this host; load a protocol driver.\n");
 
        /* send a packet to myself -- will never get received, of course */
        newpkt.source = newpkt.dest = dev->dev_addr[0];
index 42fce91b71fc9c613fa894e910ceecd4fd9eb037..2056878fb087d6d3bc3bf6564220065f30322283 100644 (file)
@@ -26,6 +26,8 @@
  * **********************
  */
 
+#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/gfp.h>
 #include <linux/init.h>
@@ -33,9 +35,8 @@
 #include <net/arp.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
-#include <linux/arcdevice.h>
 
-#define VERSION "arcnet: cap mode (`c') encapsulation support loaded.\n"
+#include "arcdevice.h"
 
 /* packet receiver */
 static void rx(struct net_device *dev, int bufnum,
@@ -47,7 +48,8 @@ static void rx(struct net_device *dev, int bufnum,
        char *pktbuf, *pkthdrbuf;
        int ofs;
 
-       BUGMSG(D_DURING, "it's a raw(cap) packet (length=%d)\n", length);
+       arc_printk(D_DURING, dev, "it's a raw(cap) packet (length=%d)\n",
+                  length);
 
        if (length >= MinTU)
                ofs = 512 - length;
@@ -55,8 +57,7 @@ static void rx(struct net_device *dev, int bufnum,
                ofs = 256 - length;
 
        skb = alloc_skb(length + ARC_HDR_SIZE + sizeof(int), GFP_ATOMIC);
-       if (skb == NULL) {
-               BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
+       if (!skb) {
                dev->stats.rx_dropped++;
                return;
        }
@@ -66,17 +67,17 @@ static void rx(struct net_device *dev, int bufnum,
        pkt = (struct archdr *)skb_mac_header(skb);
        skb_pull(skb, ARC_HDR_SIZE);
 
-       /* up to sizeof(pkt->soft) has already been copied from the card */
-       /* squeeze in an int for the cap encapsulation */
-
-       /* use these variables to be sure we count in bytes, not in
-          sizeof(struct archdr) */
-       pktbuf=(char*)pkt;
-       pkthdrbuf=(char*)pkthdr;
-       memcpy(pktbuf, pkthdrbuf, ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto));
-       memcpy(pktbuf+ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto)+sizeof(int),
-              pkthdrbuf+ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto),
-              sizeof(struct archdr)-ARC_HDR_SIZE-sizeof(pkt->soft.cap.proto));
+       /* up to sizeof(pkt->soft) has already been copied from the card
+        * squeeze in an int for the cap encapsulation
+        * use these variables to be sure we count in bytes, not in
+        * sizeof(struct archdr)
+        */
+       pktbuf = (char *)pkt;
+       pkthdrbuf = (char *)pkthdr;
+       memcpy(pktbuf, pkthdrbuf, ARC_HDR_SIZE + sizeof(pkt->soft.cap.proto));
+       memcpy(pktbuf + ARC_HDR_SIZE + sizeof(pkt->soft.cap.proto) + sizeof(int),
+              pkthdrbuf + ARC_HDR_SIZE + sizeof(pkt->soft.cap.proto),
+              sizeof(struct archdr) - ARC_HDR_SIZE - sizeof(pkt->soft.cap.proto));
 
        if (length > sizeof(pkt->soft))
                lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
@@ -84,15 +85,14 @@ static void rx(struct net_device *dev, int bufnum,
                                      + sizeof(int),
                                      length - sizeof(pkt->soft));
 
-       BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
+       if (BUGLVL(D_SKB))
+               arcnet_dump_skb(dev, skb, "rx");
 
        skb->protocol = cpu_to_be16(ETH_P_ARCNET);
        netif_rx(skb);
 }
 
-
-/*
- * Create the ARCnet hard/soft headers for cap mode.
+/* Create the ARCnet hard/soft headers for cap mode.
  * There aren't any soft headers in cap mode - not even the protocol id.
  */
 static int build_header(struct sk_buff *skb,
@@ -101,12 +101,12 @@ static int build_header(struct sk_buff *skb,
                        uint8_t daddr)
 {
        int hdr_size = ARC_HDR_SIZE;
-       struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
+       struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size);
 
-       BUGMSG(D_PROTO, "Preparing header for cap packet %x.\n",
-              *((int*)&pkt->soft.cap.cookie[0]));
-       /*
-        * Set the source hardware address.
+       arc_printk(D_PROTO, dev, "Preparing header for cap packet %x.\n",
+                  *((int *)&pkt->soft.cap.cookie[0]));
+
+       /* Set the source hardware address.
         *
         * This is pretty pointless for most purposes, but it can help in
         * debugging.  ARCnet does not allow us to change the source address in
@@ -117,9 +117,8 @@ static int build_header(struct sk_buff *skb,
        /* see linux/net/ethernet/eth.c to see where I got the following */
 
        if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
-               /*
-                * FIXME: fill in the last byte of the dest ipaddr here to better
-                * comply with RFC1051 in "noarp" mode.
+               /* FIXME: fill in the last byte of the dest ipaddr here to
+                * better comply with RFC1051 in "noarp" mode.
                 */
                pkt->hard.dest = 0;
                return hdr_size;
@@ -130,7 +129,6 @@ static int build_header(struct sk_buff *skb,
        return hdr_size;        /* success */
 }
 
-
 static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
                      int bufnum)
 {
@@ -138,22 +136,21 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
        struct arc_hardware *hard = &pkt->hard;
        int ofs;
 
-
        /* hard header is not included in packet length */
        length -= ARC_HDR_SIZE;
        /* And neither is the cookie field */
        length -= sizeof(int);
 
-       BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
-              lp->next_tx, lp->cur_tx, bufnum);
+       arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n",
+                  lp->next_tx, lp->cur_tx, bufnum);
 
-       BUGMSG(D_PROTO, "Sending for cap packet %x.\n",
-              *((int*)&pkt->soft.cap.cookie[0]));
+       arc_printk(D_PROTO, dev, "Sending for cap packet %x.\n",
+                  *((int *)&pkt->soft.cap.cookie[0]));
 
        if (length > XMTU) {
                /* should never happen! other people already check for this. */
-               BUGMSG(D_NORMAL, "Bug!  prepare_tx with size %d (> %d)\n",
-                      length, XMTU);
+               arc_printk(D_NORMAL, dev, "Bug!  prepare_tx with size %d (> %d)\n",
+                          length, XMTU);
                length = XMTU;
        }
        if (length > MinTU) {
@@ -162,11 +159,12 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
        } else if (length > MTU) {
                hard->offset[0] = 0;
                hard->offset[1] = ofs = 512 - length - 3;
-       } else
+       } else {
                hard->offset[0] = ofs = 256 - length;
+       }
 
-       BUGMSG(D_DURING, "prepare_tx: length=%d ofs=%d\n",
-              length,ofs);
+       arc_printk(D_DURING, dev, "prepare_tx: length=%d ofs=%d\n",
+                  length, ofs);
 
        /* Copy the arcnet-header + the protocol byte down: */
        lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
@@ -174,9 +172,10 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
                            sizeof(pkt->soft.cap.proto));
 
        /* Skip the extra integer we have written into it as a cookie
-          but write the rest of the message: */
-       lp->hw.copy_to_card(dev, bufnum, ofs+1,
-                           ((unsigned char*)&pkt->soft.cap.mes),length-1);
+        * but write the rest of the message:
+        */
+       lp->hw.copy_to_card(dev, bufnum, ofs + 1,
+                           ((unsigned char *)&pkt->soft.cap.mes), length - 1);
 
        lp->lastload_dest = hard->dest;
 
@@ -188,21 +187,20 @@ static int ack_tx(struct net_device *dev, int acked)
        struct arcnet_local *lp = netdev_priv(dev);
        struct sk_buff *ackskb;
        struct archdr *ackpkt;
-       int length=sizeof(struct arc_cap);
+       int length = sizeof(struct arc_cap);
 
-       BUGMSG(D_DURING, "capmode: ack_tx: protocol: %x: result: %d\n",
-               lp->outgoing.skb->protocol, acked);
+       arc_printk(D_DURING, dev, "capmode: ack_tx: protocol: %x: result: %d\n",
+                  lp->outgoing.skb->protocol, acked);
 
-       BUGLVL(D_SKB) arcnet_dump_skb(dev, lp->outgoing.skb, "ack_tx");
+       if (BUGLVL(D_SKB))
+               arcnet_dump_skb(dev, lp->outgoing.skb, "ack_tx");
 
        /* Now alloc a skb to send back up through the layers: */
-       ackskb = alloc_skb(length + ARC_HDR_SIZE , GFP_ATOMIC);
-       if (ackskb == NULL) {
-               BUGMSG(D_NORMAL, "Memory squeeze, can't acknowledge.\n");
+       ackskb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
+       if (!ackskb)
                goto free_outskb;
-       }
 
-       skb_put(ackskb, length + ARC_HDR_SIZE );
+       skb_put(ackskb, length + ARC_HDR_SIZE);
        ackskb->dev = dev;
 
        skb_reset_mac_header(ackskb);
@@ -212,39 +210,40 @@ static int ack_tx(struct net_device *dev, int acked)
        skb_copy_from_linear_data(lp->outgoing.skb, ackpkt,
                                  ARC_HDR_SIZE + sizeof(struct arc_cap));
        ackpkt->soft.cap.proto = 0; /* using protocol 0 for acknowledge */
-       ackpkt->soft.cap.mes.ack=acked;
+       ackpkt->soft.cap.mes.ack = acked;
 
-       BUGMSG(D_PROTO, "Ackknowledge for cap packet %x.\n",
-                       *((int*)&ackpkt->soft.cap.cookie[0]));
+       arc_printk(D_PROTO, dev, "Ackknowledge for cap packet %x.\n",
+                  *((int *)&ackpkt->soft.cap.cookie[0]));
 
        ackskb->protocol = cpu_to_be16(ETH_P_ARCNET);
 
-       BUGLVL(D_SKB) arcnet_dump_skb(dev, ackskb, "ack_tx_recv");
+       if (BUGLVL(D_SKB))
+               arcnet_dump_skb(dev, ackskb, "ack_tx_recv");
        netif_rx(ackskb);
 
 free_outskb:
        dev_kfree_skb_irq(lp->outgoing.skb);
-       lp->outgoing.proto = NULL; /* We are always finished when in this protocol */
+       lp->outgoing.proto = NULL;
+                       /* We are always finished when in this protocol */
 
        return 0;
 }
 
-static struct ArcProto capmode_proto =
-{
-       'r',
-       XMTU,
-       0,
-       rx,
-       build_header,
-       prepare_tx,
-       NULL,
-       ack_tx
+static struct ArcProto capmode_proto = {
+       .suffix         = 'r',
+       .mtu            = XMTU,
+       .rx             = rx,
+       .build_header   = build_header,
+       .prepare_tx     = prepare_tx,
+       .ack_tx         = ack_tx
 };
 
-static void arcnet_cap_init(void)
+static int __init capmode_module_init(void)
 {
        int count;
 
+       pr_info("cap mode (`c') encapsulation support loaded\n");
+
        for (count = 1; count <= 8; count++)
                if (arc_proto_map[count] == arc_proto_default)
                        arc_proto_map[count] = &capmode_proto;
@@ -255,12 +254,7 @@ static void arcnet_cap_init(void)
 
        arc_proto_default = &capmode_proto;
        arc_raw_proto = &capmode_proto;
-}
 
-static int __init capmode_module_init(void)
-{
-       printk(VERSION);
-       arcnet_cap_init();
        return 0;
 }
 
index 45c61a2c5fbd8345ee592768b9693b2360048e7e..b9e9931353b22a727066eaef790dec1f63d3fd5f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - COM20020 chipset support
- * 
+ *
  * Written 1997 by David Woodhouse.
  * Written 1994-1999 by Avery Pennarun.
  * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
@@ -25,6 +25,9 @@
  *
  * **********************
  */
+
+#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/bootmem.h>
-#include <linux/arcdevice.h>
-#include <linux/com20020.h>
-
-#include <asm/io.h>
+#include <linux/io.h>
 
-#define VERSION "arcnet: COM20020 ISA support (by David Woodhouse et al.)\n"
+#include "arcdevice.h"
+#include "com20020.h"
 
-
-/*
- * We cannot (yet) probe for an IO mapped card, although we can check that
+/* We cannot (yet) probe for an IO mapped card, although we can check that
  * it's where we were told it was, and even do autoirq.
  */
 static int __init com20020isa_probe(struct net_device *dev)
@@ -55,21 +54,21 @@ static int __init com20020isa_probe(struct net_device *dev)
        struct arcnet_local *lp = netdev_priv(dev);
        int err;
 
-       BUGLVL(D_NORMAL) printk(VERSION);
+       if (BUGLVL(D_NORMAL))
+               pr_info("%s\n", "COM20020 ISA support (by David Woodhouse et al.)");
 
        ioaddr = dev->base_addr;
        if (!ioaddr) {
-               BUGMSG(D_NORMAL, "No autoprobe (yet) for IO mapped cards; you "
-                      "must specify the base address!\n");
+               arc_printk(D_NORMAL, dev, "No autoprobe (yet) for IO mapped cards; you must specify the base address!\n");
                return -ENODEV;
        }
        if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "arcnet (COM20020)")) {
-               BUGMSG(D_NORMAL, "IO region %xh-%xh already allocated.\n",
-                      ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
+               arc_printk(D_NORMAL, dev, "IO region %xh-%xh already allocated.\n",
+                          ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
                return -ENXIO;
        }
-       if (ASTATUS() == 0xFF) {
-               BUGMSG(D_NORMAL, "IO address %x empty\n", ioaddr);
+       if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {
+               arc_printk(D_NORMAL, dev, "IO address %x empty\n", ioaddr);
                err = -ENODEV;
                goto out;
        }
@@ -83,23 +82,24 @@ static int __init com20020isa_probe(struct net_device *dev)
                 * card has just reset and the NORXflag is on until
                 * we tell it to start receiving.
                 */
-               BUGMSG(D_INIT_REASONS, "intmask was %02Xh\n", inb(_INTMASK));
-               outb(0, _INTMASK);
+               arc_printk(D_INIT_REASONS, dev, "intmask was %02Xh\n",
+                          arcnet_inb(ioaddr, COM20020_REG_R_STATUS));
+               arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK);
                airqmask = probe_irq_on();
-               outb(NORXflag, _INTMASK);
+               arcnet_outb(NORXflag, ioaddr, COM20020_REG_W_INTMASK);
                udelay(1);
-               outb(0, _INTMASK);
+               arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK);
                dev->irq = probe_irq_off(airqmask);
 
                if ((int)dev->irq <= 0) {
-                       BUGMSG(D_INIT_REASONS, "Autoprobe IRQ failed first time\n");
+                       arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed first time\n");
                        airqmask = probe_irq_on();
-                       outb(NORXflag, _INTMASK);
+                       arcnet_outb(NORXflag, ioaddr, COM20020_REG_W_INTMASK);
                        udelay(5);
-                       outb(0, _INTMASK);
+                       arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK);
                        dev->irq = probe_irq_off(airqmask);
                        if ((int)dev->irq <= 0) {
-                               BUGMSG(D_NORMAL, "Autoprobe IRQ failed.\n");
+                               arc_printk(D_NORMAL, dev, "Autoprobe IRQ failed.\n");
                                err = -ENODEV;
                                goto out;
                        }
@@ -107,7 +107,9 @@ static int __init com20020isa_probe(struct net_device *dev)
        }
 
        lp->card_name = "ISA COM20020";
-       if ((err = com20020_found(dev, 0)) != 0)
+
+       err = com20020_found(dev, 0);
+       if (err != 0)
                goto out;
 
        return 0;
@@ -194,7 +196,7 @@ static int __init com20020isa_setup(char *s)
 
        switch (ints[0]) {
        default:                /* ERROR */
-               printk("com90xx: Too many arguments.\n");
+               pr_info("Too many arguments\n");
        case 6:         /* Timeout */
                timeout = ints[6];
        case 5:         /* CKP value */
index 96edc1346124eb95b29c863f53359ae327348e2d..239de38fbd6a588bbb0e90e3452ea60ca1e5a161 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Linux ARCnet driver - COM20020 PCI support
  * Contemporary Controls PCI20 and SOHARD SH-ARC PCI
- * 
+ *
  * Written 1994-1999 by Avery Pennarun,
  *    based on an ISA version by David Woodhouse.
  * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
@@ -26,6 +26,9 @@
  *
  * **********************
  */
+
+#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
-#include <linux/arcdevice.h>
-#include <linux/com20020.h>
 #include <linux/list.h>
+#include <linux/io.h>
+#include <linux/leds.h>
 
-#include <asm/io.h>
-
-
-#define VERSION "arcnet: COM20020 PCI support\n"
+#include "arcdevice.h"
+#include "com20020.h"
 
 /* Module parameters */
 
@@ -62,11 +63,43 @@ module_param(clockp, int, 0);
 module_param(clockm, int, 0);
 MODULE_LICENSE("GPL");
 
+static void led_tx_set(struct led_classdev *led_cdev,
+                            enum led_brightness value)
+{
+       struct com20020_dev *card;
+       struct com20020_priv *priv;
+       struct com20020_pci_card_info *ci;
+
+       card = container_of(led_cdev, struct com20020_dev, tx_led);
+
+       priv = card->pci_priv;
+       ci = priv->ci;
+
+       outb(!!value, priv->misc + ci->leds[card->index].green);
+}
+
+static void led_recon_set(struct led_classdev *led_cdev,
+                            enum led_brightness value)
+{
+       struct com20020_dev *card;
+       struct com20020_priv *priv;
+       struct com20020_pci_card_info *ci;
+
+       card = container_of(led_cdev, struct com20020_dev, recon_led);
+
+       priv = card->pci_priv;
+       ci = priv->ci;
+
+       outb(!!value, priv->misc + ci->leds[card->index].red);
+}
+
 static void com20020pci_remove(struct pci_dev *pdev);
 
-static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int com20020pci_probe(struct pci_dev *pdev,
+                            const struct pci_device_id *id)
 {
        struct com20020_pci_card_info *ci;
+       struct com20020_pci_channel_map *mm;
        struct net_device *dev;
        struct arcnet_local *lp;
        struct com20020_priv *priv;
@@ -83,9 +116,21 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
 
        ci = (struct com20020_pci_card_info *)id->driver_data;
        priv->ci = ci;
+       mm = &ci->misc_map;
 
        INIT_LIST_HEAD(&priv->list_dev);
 
+       if (mm->size) {
+               ioaddr = pci_resource_start(pdev, mm->bar) + mm->offset;
+               r = devm_request_region(&pdev->dev, ioaddr, mm->size,
+                                       "com20020-pci");
+               if (!r) {
+                       pr_err("IO region %xh-%xh already allocated.\n",
+                              ioaddr, ioaddr + mm->size - 1);
+                       return -EBUSY;
+               }
+               priv->misc = ioaddr;
+       }
 
        for (i = 0; i < ci->devcount; i++) {
                struct com20020_pci_channel_map *cm = &ci->chan_map_tbl[i];
@@ -96,18 +141,19 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
                        ret = -ENOMEM;
                        goto out_port;
                }
+               dev->dev_port = i;
 
                dev->netdev_ops = &com20020_netdev_ops;
 
                lp = netdev_priv(dev);
 
-               BUGMSG(D_NORMAL, "%s Controls\n", ci->name);
+               arc_printk(D_NORMAL, dev, "%s Controls\n", ci->name);
                ioaddr = pci_resource_start(pdev, cm->bar) + cm->offset;
 
                r = devm_request_region(&pdev->dev, ioaddr, cm->size,
                                        "com20020-pci");
                if (!r) {
-                       pr_err("IO region %xh-%xh already allocated.\n",
+                       pr_err("IO region %xh-%xh already allocated\n",
                               ioaddr, ioaddr + cm->size - 1);
                        ret = -EBUSY;
                        goto out_port;
@@ -117,8 +163,8 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
                 * ARCNET controller needs
                 * this access to detect bustype
                 */
-               outb(0x00, ioaddr + 1);
-               inb(ioaddr + 1);
+               arcnet_outb(0x00, ioaddr, COM20020_REG_W_COMMAND);
+               arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT);
 
                dev->base_addr = ioaddr;
                dev->dev_addr[0] = node;
@@ -131,7 +177,14 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
                lp->timeout = timeout;
                lp->hw.owner = THIS_MODULE;
 
-               if (ASTATUS() == 0xFF) {
+               /* Get the dev_id from the PLX rotary coder */
+               if (!strncmp(ci->name, "EAE PLX-PCI MA1", 15))
+                       dev->dev_id = 0xc;
+               dev->dev_id ^= inb(priv->misc + ci->rotary) >> 4;
+
+               snprintf(dev->name, sizeof(dev->name), "arc%d-%d", dev->dev_id, i);
+
+               if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {
                        pr_err("IO address %Xh is empty!\n", ioaddr);
                        ret = -EIO;
                        goto out_port;
@@ -143,21 +196,46 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
 
                card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev),
                                    GFP_KERNEL);
-               if (!card) {
-                       pr_err("%s out of memory!\n", __func__);
+               if (!card)
                        return -ENOMEM;
-               }
 
                card->index = i;
                card->pci_priv = priv;
+               card->tx_led.brightness_set = led_tx_set;
+               card->tx_led.default_trigger = devm_kasprintf(&pdev->dev,
+                                               GFP_KERNEL, "arc%d-%d-tx",
+                                               dev->dev_id, i);
+               card->tx_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+                                               "pci:green:tx:%d-%d",
+                                               dev->dev_id, i);
+
+               card->tx_led.dev = &dev->dev;
+               card->recon_led.brightness_set = led_recon_set;
+               card->recon_led.default_trigger = devm_kasprintf(&pdev->dev,
+                                               GFP_KERNEL, "arc%d-%d-recon",
+                                               dev->dev_id, i);
+               card->recon_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+                                               "pci:red:recon:%d-%d",
+                                               dev->dev_id, i);
+               card->recon_led.dev = &dev->dev;
                card->dev = dev;
 
+               ret = devm_led_classdev_register(&pdev->dev, &card->tx_led);
+               if (ret)
+                       goto out_port;
+
+               ret = devm_led_classdev_register(&pdev->dev, &card->recon_led);
+               if (ret)
+                       goto out_port;
+
                dev_set_drvdata(&dev->dev, card);
 
                ret = com20020_found(dev, IRQF_SHARED);
                if (ret)
                        goto out_port;
 
+               devm_arcnet_led_init(dev, dev->dev_id, i);
+
                list_add(&card->list, &priv->list_dev);
        }
 
@@ -190,7 +268,11 @@ static struct com20020_pci_card_info card_info_10mbit = {
        .name = "ARC-PCI",
        .devcount = 1,
        .chan_map_tbl = {
-               { 2, 0x00, 0x08 },
+               {
+                       .bar = 2,
+                       .offset = 0x00,
+                       .size = 0x08,
+               },
        },
        .flags = ARC_CAN_10MBIT,
 };
@@ -199,7 +281,11 @@ static struct com20020_pci_card_info card_info_5mbit = {
        .name = "ARC-PCI",
        .devcount = 1,
        .chan_map_tbl = {
-               { 2, 0x00, 0x08 },
+               {
+                       .bar = 2,
+                       .offset = 0x00,
+                       .size = 0x08,
+               },
        },
        .flags = ARC_IS_5MBIT,
 };
@@ -209,7 +295,11 @@ static struct com20020_pci_card_info card_info_sohard = {
        .devcount = 1,
        /* SOHARD needs PCI base addr 4 */
        .chan_map_tbl = {
-               {4, 0x00, 0x08},
+               {
+                       .bar = 4,
+                       .offset = 0x00,
+                       .size = 0x08
+               },
        },
        .flags = ARC_CAN_10MBIT,
 };
@@ -218,8 +308,24 @@ static struct com20020_pci_card_info card_info_eae_arc1 = {
        .name = "EAE PLX-PCI ARC1",
        .devcount = 1,
        .chan_map_tbl = {
-               { 2, 0x00, 0x08 },
+               {
+                       .bar = 2,
+                       .offset = 0x00,
+                       .size = 0x08,
+               },
        },
+       .misc_map = {
+               .bar = 2,
+               .offset = 0x10,
+               .size = 0x04,
+       },
+       .leds = {
+               {
+                       .green = 0x0,
+                       .red = 0x1,
+               },
+       },
+       .rotary = 0x0,
        .flags = ARC_CAN_10MBIT,
 };
 
@@ -227,9 +333,31 @@ static struct com20020_pci_card_info card_info_eae_ma1 = {
        .name = "EAE PLX-PCI MA1",
        .devcount = 2,
        .chan_map_tbl = {
-               { 2, 0x00, 0x08 },
-               { 2, 0x08, 0x08 }
+               {
+                       .bar = 2,
+                       .offset = 0x00,
+                       .size = 0x08,
+               }, {
+                       .bar = 2,
+                       .offset = 0x08,
+                       .size = 0x08,
+               }
+       },
+       .misc_map = {
+               .bar = 2,
+               .offset = 0x10,
+               .size = 0x04,
+       },
+       .leds = {
+               {
+                       .green = 0x0,
+                       .red = 0x1,
+               }, {
+                       .green = 0x2,
+                       .red = 0x3,
+               },
        },
+       .rotary = 0x0,
        .flags = ARC_CAN_10MBIT,
 };
 
@@ -404,7 +532,8 @@ static struct pci_driver com20020pci_driver = {
 
 static int __init com20020pci_init(void)
 {
-       BUGLVL(D_NORMAL) printk(VERSION);
+       if (BUGLVL(D_NORMAL))
+               pr_info("%s\n", "COM20020 PCI support");
        return pci_register_driver(&com20020pci_driver);
 }
 
index 1a8437842fbca819efeca443433c9ab82bc3236f..13d9ad4b3f5c977e99f3ac2f38d3f244de3ae203 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - COM20020 chipset support
- * 
+ *
  * Written 1997 by David Woodhouse.
  * Written 1994-1999 by Avery Pennarun.
  * Written 1999 by Martin Mares <mj@ucw.cz>.
@@ -25,6 +25,9 @@
  *
  * **********************
  */
+
+#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/netdevice.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/arcdevice.h>
-#include <linux/com20020.h>
+#include <linux/io.h>
 
-#include <asm/io.h>
+#include "arcdevice.h"
+#include "com20020.h"
 
-#define VERSION "arcnet: COM20020 chipset support (by David Woodhouse et al.)\n"
-
-static char *clockrates[] =
-{"10 Mb/s", "Reserved", "5 Mb/s",
- "2.5 Mb/s", "1.25Mb/s", "625 Kb/s", "312.5 Kb/s",
- "156.25 Kb/s", "Reserved", "Reserved", "Reserved"};
+static const char * const clockrates[] = {
+       "XXXXXXX", "XXXXXXXX", "XXXXXX", "2.5 Mb/s",
+       "1.25Mb/s", "625 Kb/s", "312.5 Kb/s", "156.25 Kb/s",
+       "Reserved", "Reserved", "Reserved"
+};
 
 static void com20020_command(struct net_device *dev, int command);
 static int com20020_status(struct net_device *dev);
@@ -63,35 +65,38 @@ static void com20020_copy_from_card(struct net_device *dev, int bufnum,
        int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset;
 
        /* set up the address register */
-       outb((ofs >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI);
-       outb(ofs & 0xff, _ADDR_LO);
+       arcnet_outb((ofs >> 8) | RDDATAflag | AUTOINCflag,
+                   ioaddr, COM20020_REG_W_ADDR_HI);
+       arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);
 
        /* copy the data */
-       TIME("insb", count, insb(_MEMDATA, buf, count));
+       TIME(dev, "insb", count,
+            arcnet_insb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count));
 }
 
-
 static void com20020_copy_to_card(struct net_device *dev, int bufnum,
                                  int offset, void *buf, int count)
 {
        int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset;
 
        /* set up the address register */
-       outb((ofs >> 8) | AUTOINCflag, _ADDR_HI);
-       outb(ofs & 0xff, _ADDR_LO);
+       arcnet_outb((ofs >> 8) | AUTOINCflag, ioaddr, COM20020_REG_W_ADDR_HI);
+       arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);
 
        /* copy the data */
-       TIME("outsb", count, outsb(_MEMDATA, buf, count));
+       TIME(dev, "outsb", count,
+            arcnet_outsb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count));
 }
 
-
 /* Reset the card and check some basic stuff during the detection stage. */
 int com20020_check(struct net_device *dev)
 {
        int ioaddr = dev->base_addr, status;
        struct arcnet_local *lp = netdev_priv(dev);
 
-       ARCRESET0;
+       arcnet_outb(XTOcfg(3) | RESETcfg, ioaddr, COM20020_REG_W_CONFIG);
+       udelay(5);
+       arcnet_outb(XTOcfg(3), ioaddr, COM20020_REG_W_CONFIG);
        mdelay(RESETtime);
 
        lp->setup = lp->clockm ? 0 : (lp->clockp << 1);
@@ -101,49 +106,46 @@ int com20020_check(struct net_device *dev)
        /* Enable P1Mode for backplane mode */
        lp->setup = lp->setup | P1MODE;
 
-       SET_SUBADR(SUB_SETUP1);
-       outb(lp->setup, _XREG);
+       com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);
+       arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);
+
+       if (lp->clockm != 0) {
+               com20020_set_subaddress(lp, ioaddr, SUB_SETUP2);
+               arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG);
 
-       if (lp->clockm != 0)
-       {
-               SET_SUBADR(SUB_SETUP2);
-               outb(lp->setup2, _XREG);
-       
                /* must now write the magic "restart operation" command */
                mdelay(1);
-               outb(0x18, _COMMAND);
+               arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND);
        }
 
-       lp->config = 0x21 | (lp->timeout << 3) | (lp->backplane << 2);
+       lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE;
        /* set node ID to 0x42 (but transmitter is disabled, so it's okay) */
-       SETCONF;
-       outb(0x42, ioaddr + BUS_ALIGN*7);
+       arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
+       arcnet_outb(0x42, ioaddr, COM20020_REG_W_XREG);
 
-       status = ASTATUS();
+       status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS);
 
        if ((status & 0x99) != (NORXflag | TXFREEflag | RESETflag)) {
-               BUGMSG(D_NORMAL, "status invalid (%Xh).\n", status);
+               arc_printk(D_NORMAL, dev, "status invalid (%Xh).\n", status);
                return -ENODEV;
        }
-       BUGMSG(D_INIT_REASONS, "status after reset: %X\n", status);
-
-       /* Enable TX */
-       outb(0x39, _CONFIG);
-       outb(inb(ioaddr + BUS_ALIGN*8), ioaddr + BUS_ALIGN*7);
-
-       ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
+       arc_printk(D_INIT_REASONS, dev, "status after reset: %X\n", status);
 
-       status = ASTATUS();
-       BUGMSG(D_INIT_REASONS, "status after reset acknowledged: %X\n",
-              status);
+       arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
+                   ioaddr, COM20020_REG_W_COMMAND);
+       status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS);
+       arc_printk(D_INIT_REASONS, dev, "status after reset acknowledged: %X\n",
+                  status);
 
        /* Read first location of memory */
-       outb(0 | RDDATAflag | AUTOINCflag, _ADDR_HI);
-       outb(0, _ADDR_LO);
-
-       if ((status = inb(_MEMDATA)) != TESTvalue) {
-               BUGMSG(D_NORMAL, "Signature byte not found (%02Xh != D1h).\n",
-                      status);
+       arcnet_outb(0 | RDDATAflag | AUTOINCflag,
+                   ioaddr, COM20020_REG_W_ADDR_HI);
+       arcnet_outb(0, ioaddr, COM20020_REG_W_ADDR_LO);
+
+       status = arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA);
+       if (status != TESTvalue) {
+               arc_printk(D_NORMAL, dev, "Signature byte not found (%02Xh != D1h).\n",
+                          status);
                return -ENODEV;
        }
        return 0;
@@ -156,15 +158,39 @@ static int com20020_set_hwaddr(struct net_device *dev, void *addr)
        struct sockaddr *hwaddr = addr;
 
        memcpy(dev->dev_addr, hwaddr->sa_data, 1);
-       SET_SUBADR(SUB_NODE);
-       outb(dev->dev_addr[0], _XREG);
+       com20020_set_subaddress(lp, ioaddr, SUB_NODE);
+       arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG);
+
+       return 0;
+}
+
+static int com20020_netdev_open(struct net_device *dev)
+{
+       int ioaddr = dev->base_addr;
+       struct arcnet_local *lp = netdev_priv(dev);
+
+       lp->config |= TXENcfg;
+       arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
 
+       return arcnet_open(dev);
+}
+
+static int com20020_netdev_close(struct net_device *dev)
+{
+       int ioaddr = dev->base_addr;
+       struct arcnet_local *lp = netdev_priv(dev);
+
+       arcnet_close(dev);
+
+       /* disable transmitter */
+       lp->config &= ~TXENcfg;
+       arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
        return 0;
 }
 
 const struct net_device_ops com20020_netdev_ops = {
-       .ndo_open       = arcnet_open,
-       .ndo_stop       = arcnet_close,
+       .ndo_open       = com20020_netdev_open,
+       .ndo_stop       = com20020_netdev_close,
        .ndo_start_xmit = arcnet_send_packet,
        .ndo_tx_timeout = arcnet_timeout,
        .ndo_set_mac_address = com20020_set_hwaddr,
@@ -192,48 +218,54 @@ int com20020_found(struct net_device *dev, int shared)
        lp->hw.copy_from_card = com20020_copy_from_card;
        lp->hw.close = com20020_close;
 
+       /* FIXME: do this some other way! */
        if (!dev->dev_addr[0])
-               dev->dev_addr[0] = inb(ioaddr + BUS_ALIGN*8);   /* FIXME: do this some other way! */
+               dev->dev_addr[0] = arcnet_inb(ioaddr, 8);
+
+       com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);
+       arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);
 
-       SET_SUBADR(SUB_SETUP1);
-       outb(lp->setup, _XREG);
+       if (lp->card_flags & ARC_CAN_10MBIT) {
+               com20020_set_subaddress(lp, ioaddr, SUB_SETUP2);
+               arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG);
 
-       if (lp->card_flags & ARC_CAN_10MBIT)
-       {
-               SET_SUBADR(SUB_SETUP2);
-               outb(lp->setup2, _XREG);
-       
                /* must now write the magic "restart operation" command */
                mdelay(1);
-               outb(0x18, _COMMAND);
+               arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND);
        }
 
-       lp->config = 0x20 | (lp->timeout << 3) | (lp->backplane << 2) | 1;
+       lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE;
        /* Default 0x38 + register: Node ID */
-       SETCONF;
-       outb(dev->dev_addr[0], _XREG);
+       arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
+       arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG);
 
        /* reserve the irq */
        if (request_irq(dev->irq, arcnet_interrupt, shared,
                        "arcnet (COM20020)", dev)) {
-               BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
+               arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq);
                return -ENODEV;
        }
 
        dev->base_addr = ioaddr;
 
-       BUGMSG(D_NORMAL, "%s: station %02Xh found at %03lXh, IRQ %d.\n",
-              lp->card_name, dev->dev_addr[0], dev->base_addr, dev->irq);
+       arc_printk(D_NORMAL, dev, "%s: station %02Xh found at %03lXh, IRQ %d.\n",
+                  lp->card_name, dev->dev_addr[0], dev->base_addr, dev->irq);
 
        if (lp->backplane)
-               BUGMSG(D_NORMAL, "Using backplane mode.\n");
+               arc_printk(D_NORMAL, dev, "Using backplane mode.\n");
 
        if (lp->timeout != 3)
-               BUGMSG(D_NORMAL, "Using extended timeout value of %d.\n", lp->timeout);
-
-       BUGMSG(D_NORMAL, "Using CKP %d - data rate %s.\n",
-              lp->setup >> 1, 
-              clockrates[3 - ((lp->setup2 & 0xF0) >> 4) + ((lp->setup & 0x0F) >> 1)]);
+               arc_printk(D_NORMAL, dev, "Using extended timeout value of %d\n",
+                          lp->timeout);
+
+       arc_printk(D_NORMAL, dev, "Using CKP %d - data rate %s\n",
+                  lp->setup >> 1,
+                  clockrates[3 -
+                             ((lp->setup2 & 0xF0) >> 4) +
+                             ((lp->setup & 0x0F) >> 1)]);
+                       /* The clockrates array index looks very fragile.
+                        * It seems like it could have negative indexing.
+                        */
 
        if (register_netdev(dev)) {
                free_irq(dev->irq, dev);
@@ -242,10 +274,8 @@ int com20020_found(struct net_device *dev, int shared)
        return 0;
 }
 
-
-/* 
- * Do a hardware reset on the card, and set up necessary registers.
- * 
+/* Do a hardware reset on the card, and set up necessary registers.
+ *
  * This should be called as little as possible, because it disrupts the
  * token on the network (causes a RECON) and requires a significant delay.
  *
@@ -257,65 +287,71 @@ static int com20020_reset(struct net_device *dev, int really_reset)
        u_int ioaddr = dev->base_addr;
        u_char inbyte;
 
-       BUGMSG(D_DEBUG, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n",
-               __FILE__,__LINE__,__func__,dev,lp,dev->name);
-       BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n",
-              dev->name, ASTATUS());
+       arc_printk(D_DEBUG, dev, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n",
+                  __FILE__, __LINE__, __func__, dev, lp, dev->name);
+       arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n",
+                  dev->name, arcnet_inb(ioaddr, COM20020_REG_R_STATUS));
 
-       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
-       lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2);
+       arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
+       lp->config |= (lp->timeout << 3) | (lp->backplane << 2);
        /* power-up defaults */
-       SETCONF;
-       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
+       arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
+       arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
 
        if (really_reset) {
                /* reset the card */
-               ARCRESET;
-               mdelay(RESETtime * 2);  /* COM20020 seems to be slower sometimes */
+               arcnet_outb(lp->config | RESETcfg, ioaddr, COM20020_REG_W_CONFIG);
+               udelay(5);
+               arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
+               mdelay(RESETtime * 2);
+                               /* COM20020 seems to be slower sometimes */
        }
        /* clear flags & end reset */
-       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
-       ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
+       arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
+       arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
+                   ioaddr, COM20020_REG_W_COMMAND);
 
        /* verify that the ARCnet signature byte is present */
-       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
+       arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
 
        com20020_copy_from_card(dev, 0, 0, &inbyte, 1);
-       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
+       arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
        if (inbyte != TESTvalue) {
-               BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
-               BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n");
+               arc_printk(D_DEBUG, dev, "%s: %d: %s\n",
+                          __FILE__, __LINE__, __func__);
+               arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");
                return 1;
        }
        /* enable extended (512-byte) packets */
-       ACOMMAND(CONFIGcmd | EXTconf);
-       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__);
+       arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM20020_REG_W_COMMAND);
+
+       arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
 
        /* done!  return success. */
        return 0;
 }
 
-
 static void com20020_setmask(struct net_device *dev, int mask)
 {
        u_int ioaddr = dev->base_addr;
-       BUGMSG(D_DURING, "Setting mask to %x at %x\n",mask,ioaddr);
-       AINTMASK(mask);
-}
 
+       arc_printk(D_DURING, dev, "Setting mask to %x at %x\n", mask, ioaddr);
+       arcnet_outb(mask, ioaddr, COM20020_REG_W_INTMASK);
+}
 
 static void com20020_command(struct net_device *dev, int cmd)
 {
        u_int ioaddr = dev->base_addr;
-       ACOMMAND(cmd);
-}
 
+       arcnet_outb(cmd, ioaddr, COM20020_REG_W_COMMAND);
+}
 
 static int com20020_status(struct net_device *dev)
 {
        u_int ioaddr = dev->base_addr;
 
-       return ASTATUS() + (ADIAGSTATUS()<<8);
+       return arcnet_inb(ioaddr, COM20020_REG_R_STATUS) +
+               (arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT) << 8);
 }
 
 static void com20020_close(struct net_device *dev)
@@ -325,7 +361,7 @@ static void com20020_close(struct net_device *dev)
 
        /* disable transmitter */
        lp->config &= ~TXENcfg;
-       SETCONF;
+       arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
 }
 
 /* Set or clear the multicast filter for this adaptor.
@@ -340,20 +376,20 @@ static void com20020_set_mc_list(struct net_device *dev)
        struct arcnet_local *lp = netdev_priv(dev);
        int ioaddr = dev->base_addr;
 
-       if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP)) {      /* Enable promiscuous mode */
+       if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP)) {
+               /* Enable promiscuous mode */
                if (!(lp->setup & PROMISCset))
-                       BUGMSG(D_NORMAL, "Setting promiscuous flag...\n");
-               SET_SUBADR(SUB_SETUP1);
+                       arc_printk(D_NORMAL, dev, "Setting promiscuous flag...\n");
+               com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);
                lp->setup |= PROMISCset;
-               outb(lp->setup, _XREG);
-       } else
+               arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);
+       } else {
                /* Disable promiscuous mode, use normal mode */
-       {
                if ((lp->setup & PROMISCset))
-                       BUGMSG(D_NORMAL, "Resetting promiscuous flag...\n");
-               SET_SUBADR(SUB_SETUP1);
+                       arc_printk(D_NORMAL, dev, "Resetting promiscuous flag...\n");
+               com20020_set_subaddress(lp, ioaddr, SUB_SETUP1);
                lp->setup &= ~PROMISCset;
-               outb(lp->setup, _XREG);
+               arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG);
        }
 }
 
@@ -371,7 +407,8 @@ MODULE_LICENSE("GPL");
 
 static int __init com20020_module_init(void)
 {
-       BUGLVL(D_NORMAL) printk(VERSION);
+       if (BUGLVL(D_NORMAL))
+               pr_info("%s\n", "COM20020 chipset support (by David Woodhouse et al.)");
        return 0;
 }
 
similarity index 61%
rename from include/linux/com20020.h
rename to drivers/net/arcnet/com20020.h
index 85898995b234edc244b589415fd2c3391d8cd62d..0bcc5d0a6903c34e305bab525b4989d30d912f59 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - COM20020 chipset support - function declarations
- * 
+ *
  * Written 1997 by David Woodhouse.
  * Written 1994-1999 by Avery Pennarun.
  * Derived from skeleton.c by Donald Becker.
@@ -26,6 +26,7 @@
  */
 #ifndef __COM20020_H
 #define __COM20020_H
+#include <linux/leds.h>
 
 int com20020_check(struct net_device *dev);
 int com20020_found(struct net_device *dev, int shared);
@@ -34,15 +35,13 @@ extern const struct net_device_ops com20020_netdev_ops;
 /* The number of low I/O ports used by the card. */
 #define ARCNET_TOTAL_SIZE 8
 
-/* various register addresses */
-#ifdef CONFIG_SA1100_CT6001
-#define BUS_ALIGN  2  /* 8 bit device on a 16 bit bus - needs padding */
-#else
-#define BUS_ALIGN  1
-#endif
-
 #define PLX_PCI_MAX_CARDS 2
 
+struct ledoffsets {
+       int green;
+       int red;
+};
+
 struct com20020_pci_channel_map {
        u32 bar;
        u32 offset;
@@ -54,6 +53,10 @@ struct com20020_pci_card_info {
        int devcount;
 
        struct com20020_pci_channel_map chan_map_tbl[PLX_PCI_MAX_CARDS];
+       struct com20020_pci_channel_map misc_map;
+
+       struct ledoffsets leds[PLX_PCI_MAX_CARDS];
+       int rotary;
 
        unsigned int flags;
 };
@@ -61,27 +64,32 @@ struct com20020_pci_card_info {
 struct com20020_priv {
        struct com20020_pci_card_info *ci;
        struct list_head list_dev;
+       resource_size_t misc;
 };
 
 struct com20020_dev {
        struct list_head list;
        struct net_device *dev;
 
+       struct led_classdev tx_led;
+       struct led_classdev recon_led;
+
        struct com20020_priv *pci_priv;
        int index;
 };
 
-#define _INTMASK  (ioaddr+BUS_ALIGN*0) /* writable */
-#define _STATUS   (ioaddr+BUS_ALIGN*0) /* readable */
-#define _COMMAND  (ioaddr+BUS_ALIGN*1) /* standard arcnet commands */
-#define _DIAGSTAT (ioaddr+BUS_ALIGN*1) /* diagnostic status register */
-#define _ADDR_HI  (ioaddr+BUS_ALIGN*2) /* control registers for IO-mapped memory */
-#define _ADDR_LO  (ioaddr+BUS_ALIGN*3)
-#define _MEMDATA  (ioaddr+BUS_ALIGN*4) /* data port for IO-mapped memory */
-#define _SUBADR   (ioaddr+BUS_ALIGN*5) /* the extended port _XREG refers to */
-#define _CONFIG   (ioaddr+BUS_ALIGN*6) /* configuration register */
-#define _XREG     (ioaddr+BUS_ALIGN*7) /* extra registers (indexed by _CONFIG
-                                       or _SUBADR) */
+#define COM20020_REG_W_INTMASK 0       /* writable */
+#define COM20020_REG_R_STATUS  0       /* readable */
+#define COM20020_REG_W_COMMAND 1       /* standard arcnet commands */
+#define COM20020_REG_R_DIAGSTAT        1       /* diagnostic status */
+#define COM20020_REG_W_ADDR_HI 2       /* control for IO-mapped memory */
+#define COM20020_REG_W_ADDR_LO 3
+#define COM20020_REG_RW_MEMDATA        4       /* data port for IO-mapped memory */
+#define COM20020_REG_W_SUBADR  5       /* the extended port _XREG refers to */
+#define COM20020_REG_W_CONFIG  6       /* configuration */
+#define COM20020_REG_W_XREG    7       /* extra
+                                        * (indexed by _CONFIG or _SUBADDR)
+                                        */
 
 /* in the ADDR_HI register */
 #define RDDATAflag     0x80    /* next access is a read (not a write) */
@@ -92,6 +100,7 @@ struct com20020_dev {
 /* in the CONFIG register */
 #define RESETcfg       0x80    /* put card in reset state */
 #define TXENcfg                0x20    /* enable TX */
+#define XTOcfg(x)      ((x) << 3)      /* extended timeout */
 
 /* in SETUP register */
 #define PROMISCset     0x10    /* enable RCV_ALL */
@@ -109,37 +118,15 @@ struct com20020_dev {
 #define SUB_BUSCTL     5       /* bus control options */
 #define SUB_DMACOUNT   6       /* DMA count options */
 
-#define SET_SUBADR(x) do { \
-       if ((x) < 4) \
-       { \
-               lp->config = (lp->config & ~0x03) | (x); \
-               SETCONF; \
-       } \
-       else \
-       { \
-               outb(x, _SUBADR); \
-       } \
-} while (0)
-
-#undef ARCRESET
-#undef ASTATUS
-#undef ACOMMAND
-#undef AINTMASK
-
-#define ARCRESET { outb(lp->config | 0x80, _CONFIG); \
-                   udelay(5);                        \
-                   outb(lp->config , _CONFIG);       \
-                  }
-#define ARCRESET0 { outb(0x18 | 0x80, _CONFIG);   \
-                   udelay(5);                       \
-                   outb(0x18 , _CONFIG);            \
-                  }
-
-#define ASTATUS()      inb(_STATUS)
-#define ADIAGSTATUS()  inb(_DIAGSTAT)
-#define ACOMMAND(cmd)  outb((cmd),_COMMAND)
-#define AINTMASK(msk)  outb((msk),_INTMASK)
-
-#define SETCONF                outb(lp->config, _CONFIG)
+static inline void com20020_set_subaddress(struct arcnet_local *lp,
+                                          int ioaddr, int val)
+{
+       if (val < 4) {
+               lp->config = (lp->config & ~0x03) | val;
+               arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
+       } else {
+               arcnet_outb(val, ioaddr, COM20020_REG_W_SUBADR);
+       }
+}
 
 #endif /* __COM20020_H */
index 057d9582132a6f659c7d5c08fbd99f883e50fb07..cf607ffcf358e95eab6a63c3c7bb63382cd57c49 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - COM20020 PCMCIA support
- * 
+ *
  * Written 1994-1999 by Avery Pennarun,
  *    based on an ISA version by David Woodhouse.
  * Derived from ibmtr_cs.c by Steve Kipisz (pcmcia-cs 3.1.4)
  * Director, National Security Agency.  This software may only be used
  * and distributed according to the terms of the GNU General Public License as
  * modified by SRC, incorporated herein by reference.
- * 
+ *
  * **********************
  * Changes:
  * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000
  * - reorganize kmallocs in com20020_attach, checking all for failure
  *   and releasing the previous allocations if one fails
  * **********************
- * 
+ *
  * For more details, see drivers/net/arcnet.c
  *
  * **********************
  */
+
+#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/ptrace.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
-#include <linux/arcdevice.h>
-#include <linux/com20020.h>
-
+#include <linux/io.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/ds.h>
 
-#include <asm/io.h>
-
-#define VERSION "arcnet: COM20020 PCMCIA support loaded.\n"
-
+#include "arcdevice.h"
+#include "com20020.h"
 
 static void regdump(struct net_device *dev)
 {
 #ifdef DEBUG
-    int ioaddr = dev->base_addr;
-    int count;
-    
-    netdev_dbg(dev, "register dump:\n");
-    for (count = ioaddr; count < ioaddr + 16; count++)
-    {
-       if (!(count % 16))
-           pr_cont("%04X:", count);
-       pr_cont(" %02X", inb(count));
-    }
-    pr_cont("\n");
-    
-    netdev_dbg(dev, "buffer0 dump:\n");
+       int ioaddr = dev->base_addr;
+       int count;
+
+       netdev_dbg(dev, "register dump:\n");
+       for (count = 0; count < 16; count++) {
+               if (!(count % 16))
+                       pr_cont("%04X:", ioaddr + count);
+               pr_cont(" %02X", arcnet_inb(ioaddr, count));
+       }
+       pr_cont("\n");
+
+       netdev_dbg(dev, "buffer0 dump:\n");
        /* set up the address register */
-        count = 0;
-       outb((count >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI);
-       outb(count & 0xff, _ADDR_LO);
-    
-    for (count = 0; count < 256+32; count++)
-    {
-       if (!(count % 16))
-           pr_cont("%04X:", count);
-       
-       /* copy the data */
-       pr_cont(" %02X", inb(_MEMDATA));
-    }
-    pr_cont("\n");
-#endif
-}
+       count = 0;
+       arcnet_outb((count >> 8) | RDDATAflag | AUTOINCflag,
+                   ioaddr, com20020_REG_W_ADDR_HI);
+       arcnet_outb(count & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);
 
+       for (count = 0; count < 256 + 32; count++) {
+               if (!(count % 16))
+                       pr_cont("%04X:", count);
 
+               /* copy the data */
+               pr_cont(" %02X", arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA));
+       }
+       pr_cont("\n");
+#endif
+}
 
 /*====================================================================*/
 
@@ -114,169 +110,161 @@ static void com20020_detach(struct pcmcia_device *p_dev);
 
 static int com20020_probe(struct pcmcia_device *p_dev)
 {
-    struct com20020_dev *info;
-    struct net_device *dev;
-    struct arcnet_local *lp;
+       struct com20020_dev *info;
+       struct net_device *dev;
+       struct arcnet_local *lp;
 
-    dev_dbg(&p_dev->dev, "com20020_attach()\n");
+       dev_dbg(&p_dev->dev, "com20020_attach()\n");
 
-    /* Create new network device */
-    info = kzalloc(sizeof(*info), GFP_KERNEL);
-    if (!info)
-       goto fail_alloc_info;
+       /* Create new network device */
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info)
+               goto fail_alloc_info;
 
-    dev = alloc_arcdev("");
-    if (!dev)
-       goto fail_alloc_dev;
+       dev = alloc_arcdev("");
+       if (!dev)
+               goto fail_alloc_dev;
 
-    lp = netdev_priv(dev);
-    lp->timeout = timeout;
-    lp->backplane = backplane;
-    lp->clockp = clockp;
-    lp->clockm = clockm & 3;
-    lp->hw.owner = THIS_MODULE;
+       lp = netdev_priv(dev);
+       lp->timeout = timeout;
+       lp->backplane = backplane;
+       lp->clockp = clockp;
+       lp->clockm = clockm & 3;
+       lp->hw.owner = THIS_MODULE;
 
-    /* fill in our module parameters as defaults */
-    dev->dev_addr[0] = node;
+       /* fill in our module parameters as defaults */
+       dev->dev_addr[0] = node;
 
-    p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
-    p_dev->resource[0]->end = 16;
-    p_dev->config_flags |= CONF_ENABLE_IRQ;
+       p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+       p_dev->resource[0]->end = 16;
+       p_dev->config_flags |= CONF_ENABLE_IRQ;
 
-    info->dev = dev;
-    p_dev->priv = info;
+       info->dev = dev;
+       p_dev->priv = info;
 
-    return com20020_config(p_dev);
+       return com20020_config(p_dev);
 
 fail_alloc_dev:
-    kfree(info);
+       kfree(info);
 fail_alloc_info:
-    return -ENOMEM;
+       return -ENOMEM;
 } /* com20020_attach */
 
 static void com20020_detach(struct pcmcia_device *link)
 {
-    struct com20020_dev *info = link->priv;
-    struct net_device *dev = info->dev;
+       struct com20020_dev *info = link->priv;
+       struct net_device *dev = info->dev;
 
-    dev_dbg(&link->dev, "detach...\n");
+       dev_dbg(&link->dev, "detach...\n");
 
-    dev_dbg(&link->dev, "com20020_detach\n");
+       dev_dbg(&link->dev, "com20020_detach\n");
 
-    dev_dbg(&link->dev, "unregister...\n");
+       dev_dbg(&link->dev, "unregister...\n");
 
-    unregister_netdev(dev);
+       unregister_netdev(dev);
 
-    /*
-     * this is necessary because we register our IRQ separately
-     * from card services.
-     */
-    if (dev->irq)
-           free_irq(dev->irq, dev);
+       /* this is necessary because we register our IRQ separately
+        * from card services.
+        */
+       if (dev->irq)
+               free_irq(dev->irq, dev);
 
-    com20020_release(link);
+       com20020_release(link);
 
-    /* Unlink device structure, free bits */
-    dev_dbg(&link->dev, "unlinking...\n");
-    if (link->priv)
-    {
-       dev = info->dev;
-       if (dev)
-       {
-           dev_dbg(&link->dev, "kfree...\n");
-           free_netdev(dev);
+       /* Unlink device structure, free bits */
+       dev_dbg(&link->dev, "unlinking...\n");
+       if (link->priv) {
+               dev = info->dev;
+               if (dev) {
+                       dev_dbg(&link->dev, "kfree...\n");
+                       free_netdev(dev);
+               }
+               dev_dbg(&link->dev, "kfree2...\n");
+               kfree(info);
        }
-       dev_dbg(&link->dev, "kfree2...\n");
-       kfree(info);
-    }
 
 } /* com20020_detach */
 
 static int com20020_config(struct pcmcia_device *link)
 {
-    struct arcnet_local *lp;
-    struct com20020_dev *info;
-    struct net_device *dev;
-    int i, ret;
-    int ioaddr;
+       struct arcnet_local *lp;
+       struct com20020_dev *info;
+       struct net_device *dev;
+       int i, ret;
+       int ioaddr;
+
+       info = link->priv;
+       dev = info->dev;
+
+       dev_dbg(&link->dev, "config...\n");
+
+       dev_dbg(&link->dev, "com20020_config\n");
 
-    info = link->priv;
-    dev = info->dev;
+       dev_dbg(&link->dev, "baseport1 is %Xh\n",
+               (unsigned int)link->resource[0]->start);
 
-    dev_dbg(&link->dev, "config...\n");
+       i = -ENODEV;
+       link->io_lines = 16;
 
-    dev_dbg(&link->dev, "com20020_config\n");
+       if (!link->resource[0]->start) {
+               for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10) {
+                       link->resource[0]->start = ioaddr;
+                       i = pcmcia_request_io(link);
+                       if (i == 0)
+                               break;
+               }
+       } else {
+               i = pcmcia_request_io(link);
+       }
 
-    dev_dbg(&link->dev, "baseport1 is %Xh\n",
-           (unsigned int) link->resource[0]->start);
+       if (i != 0) {
+               dev_dbg(&link->dev, "requestIO failed totally!\n");
+               goto failed;
+       }
 
-    i = -ENODEV;
-    link->io_lines = 16;
+       ioaddr = dev->base_addr = link->resource[0]->start;
+       dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr);
 
-    if (!link->resource[0]->start)
-    {
-       for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
-       {
-           link->resource[0]->start = ioaddr;
-           i = pcmcia_request_io(link);
-           if (i == 0)
-               break;
+       dev_dbg(&link->dev, "request IRQ %d\n",
+               link->irq);
+       if (!link->irq) {
+               dev_dbg(&link->dev, "requestIRQ failed totally!\n");
+               goto failed;
        }
-    }
-    else
-       i = pcmcia_request_io(link);
-    
-    if (i != 0)
-    {
-       dev_dbg(&link->dev, "requestIO failed totally!\n");
-       goto failed;
-    }
-       
-    ioaddr = dev->base_addr = link->resource[0]->start;
-    dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr);
-
-    dev_dbg(&link->dev, "request IRQ %d\n",
-           link->irq);
-    if (!link->irq)
-    {
-       dev_dbg(&link->dev, "requestIRQ failed totally!\n");
-       goto failed;
-    }
-
-    dev->irq = link->irq;
-
-    ret = pcmcia_enable_device(link);
-    if (ret)
-           goto failed;
-
-    if (com20020_check(dev))
-    {
-       regdump(dev);
-       goto failed;
-    }
-    
-    lp = netdev_priv(dev);
-    lp->card_name = "PCMCIA COM20020";
-    lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
-
-    SET_NETDEV_DEV(dev, &link->dev);
-
-    i = com20020_found(dev, 0);        /* calls register_netdev */
-    
-    if (i != 0) {
-       dev_notice(&link->dev,
-                  "com20020_found() failed\n");
-       goto failed;
-    }
-
-    netdev_dbg(dev, "port %#3lx, irq %d\n",
-              dev->base_addr, dev->irq);
-    return 0;
+
+       dev->irq = link->irq;
+
+       ret = pcmcia_enable_device(link);
+       if (ret)
+               goto failed;
+
+       if (com20020_check(dev)) {
+               regdump(dev);
+               goto failed;
+       }
+
+       lp = netdev_priv(dev);
+       lp->card_name = "PCMCIA COM20020";
+       lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
+
+       SET_NETDEV_DEV(dev, &link->dev);
+
+       i = com20020_found(dev, 0);     /* calls register_netdev */
+
+       if (i != 0) {
+               dev_notice(&link->dev,
+                          "com20020_found() failed\n");
+               goto failed;
+       }
+
+       netdev_dbg(dev, "port %#3lx, irq %d\n",
+                  dev->base_addr, dev->irq);
+       return 0;
 
 failed:
-    dev_dbg(&link->dev, "com20020_config failed...\n");
-    com20020_release(link);
-    return -ENODEV;
+       dev_dbg(&link->dev, "com20020_config failed...\n");
+       com20020_release(link);
+       return -ENODEV;
 } /* com20020_config */
 
 static void com20020_release(struct pcmcia_device *link)
@@ -304,7 +292,10 @@ static int com20020_resume(struct pcmcia_device *link)
        if (link->open) {
                int ioaddr = dev->base_addr;
                struct arcnet_local *lp = netdev_priv(dev);
-               ARCRESET;
+
+               arcnet_outb(lp->config | 0x80, ioaddr, COM20020_REG_W_CONFIG);
+               udelay(5);
+               arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
        }
 
        return 0;
@@ -312,9 +303,9 @@ static int com20020_resume(struct pcmcia_device *link)
 
 static const struct pcmcia_device_id com20020_ids[] = {
        PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
-                       "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
+                               "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
        PCMCIA_DEVICE_PROD_ID12("SoHard AG",
-                       "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
+                               "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
        PCMCIA_DEVICE_NULL
 };
 MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
diff --git a/drivers/net/arcnet/com9026.h b/drivers/net/arcnet/com9026.h
new file mode 100644 (file)
index 0000000..efcaf67
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef __COM9026_H
+#define __COM9026_H
+
+/* COM 9026 controller chip --> ARCnet register addresses */
+
+#define COM9026_REG_W_INTMASK  0       /* writable */
+#define COM9026_REG_R_STATUS   0       /* readable */
+#define COM9026_REG_W_COMMAND  1       /* writable, returns random vals on read (?) */
+#define COM9026_REG_RW_CONFIG  2       /* Configuration register */
+#define COM9026_REG_R_RESET    8       /* software reset (on read) */
+#define COM9026_REG_RW_MEMDATA 12      /* Data port for IO-mapped memory */
+#define COM9026_REG_W_ADDR_LO  14      /* Control registers for said */
+#define COM9026_REG_W_ADDR_HI  15
+
+#define COM9026_REG_R_STATION  1       /* Station ID */
+
+#endif
index 487d780ebbdfd73768f575e43353da5f2e8a1fa3..b57863df5bf53e6ee0269b4d9f2055a06011b1d1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - COM90xx chipset (IO-mapped buffers)
- * 
+ *
  * Written 1997 by David Woodhouse.
  * Written 1994-1999 by Avery Pennarun.
  * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
@@ -25,6 +25,9 @@
  *
  * **********************
  */
+
+#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/bootmem.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <asm/io.h>
-#include <linux/arcdevice.h>
-
-
-#define VERSION "arcnet: COM90xx IO-mapped mode support (by David Woodhouse et el.)\n"
+#include <linux/io.h>
 
+#include "arcdevice.h"
+#include "com9026.h"
 
 /* Internal function declarations */
 
@@ -50,35 +51,14 @@ static void com90io_setmask(struct net_device *dev, int mask);
 static int com90io_reset(struct net_device *dev, int really_reset);
 static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset,
                                 void *buf, int count);
-static void com90io_copy_from_card(struct net_device *dev, int bufnum, int offset,
-                                  void *buf, int count);
-
+static void com90io_copy_from_card(struct net_device *dev, int bufnum,
+                                  int offset, void *buf, int count);
 
 /* Handy defines for ARCnet specific stuff */
 
 /* The number of low I/O ports used by the card. */
 #define ARCNET_TOTAL_SIZE 16
 
-/* COM 9026 controller chip --> ARCnet register addresses */
-#define _INTMASK (ioaddr+0)    /* writable */
-#define _STATUS  (ioaddr+0)    /* readable */
-#define _COMMAND (ioaddr+1)    /* writable, returns random vals on read (?) */
-#define _RESET  (ioaddr+8)     /* software reset (on read) */
-#define _MEMDATA  (ioaddr+12)  /* Data port for IO-mapped memory */
-#define _ADDR_HI  (ioaddr+15)  /* Control registers for said */
-#define _ADDR_LO  (ioaddr+14)
-#define _CONFIG  (ioaddr+2)    /* Configuration register */
-
-#undef ASTATUS
-#undef ACOMMAND
-#undef AINTMASK
-
-#define ASTATUS()      inb(_STATUS)
-#define ACOMMAND(cmd) outb((cmd),_COMMAND)
-#define AINTMASK(msk)  outb((msk),_INTMASK)
-#define SETCONF()      outb((lp->config),_CONFIG)
-
-
 /****************************************************************************
  *                                                                          *
  * IO-mapped operation routines                                             *
@@ -92,58 +72,59 @@ static u_char get_buffer_byte(struct net_device *dev, unsigned offset)
 {
        int ioaddr = dev->base_addr;
 
-       outb(offset >> 8, _ADDR_HI);
-       outb(offset & 0xff, _ADDR_LO);
+       arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI);
+       arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);
 
-       return inb(_MEMDATA);
+       return arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);
 }
 
 #ifdef ONE_AT_A_TIME_TX
-static void put_buffer_byte(struct net_device *dev, unsigned offset, u_char datum)
+static void put_buffer_byte(struct net_device *dev, unsigned offset,
+                           u_char datum)
 {
        int ioaddr = dev->base_addr;
 
-       outb(offset >> 8, _ADDR_HI);
-       outb(offset & 0xff, _ADDR_LO);
+       arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI);
+       arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);
 
-       outb(datum, _MEMDATA);
+       arcnet_outb(datum, ioaddr, COM9026_REG_RW_MEMDATA);
 }
 
 #endif
 
-
-static void get_whole_buffer(struct net_device *dev, unsigned offset, unsigned length, char *dest)
+static void get_whole_buffer(struct net_device *dev, unsigned offset,
+                            unsigned length, char *dest)
 {
        int ioaddr = dev->base_addr;
 
-       outb((offset >> 8) | AUTOINCflag, _ADDR_HI);
-       outb(offset & 0xff, _ADDR_LO);
+       arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);
+       arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);
 
        while (length--)
 #ifdef ONE_AT_A_TIME_RX
                *(dest++) = get_buffer_byte(dev, offset++);
 #else
-               *(dest++) = inb(_MEMDATA);
+               *(dest++) = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);
 #endif
 }
 
-static void put_whole_buffer(struct net_device *dev, unsigned offset, unsigned length, char *dest)
+static void put_whole_buffer(struct net_device *dev, unsigned offset,
+                            unsigned length, char *dest)
 {
        int ioaddr = dev->base_addr;
 
-       outb((offset >> 8) | AUTOINCflag, _ADDR_HI);
-       outb(offset & 0xff, _ADDR_LO);
+       arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);
+       arcnet_outb(offset & 0xff, ioaddr,COM9026_REG_W_ADDR_LO);
 
        while (length--)
 #ifdef ONE_AT_A_TIME_TX
                put_buffer_byte(dev, offset++, *(dest++));
 #else
-               outb(*(dest++), _MEMDATA);
+               arcnet_outb(*(dest++), ioaddr, COM9026_REG_RW_MEMDATA);
 #endif
 }
 
-/*
- * We cannot probe for an IO mapped card either, although we can check that
+/* We cannot probe for an IO mapped card either, although we can check that
  * it's where we were told it was, and even autoirq
  */
 static int __init com90io_probe(struct net_device *dev)
@@ -151,71 +132,78 @@ static int __init com90io_probe(struct net_device *dev)
        int ioaddr = dev->base_addr, status;
        unsigned long airqmask;
 
-       BUGLVL(D_NORMAL) printk(VERSION);
-       BUGLVL(D_NORMAL) printk("E-mail me if you actually test this driver, please!\n");
+       if (BUGLVL(D_NORMAL)) {
+               pr_info("%s\n", "COM90xx IO-mapped mode support (by David Woodhouse et el.)");
+               pr_info("E-mail me if you actually test this driver, please!\n");
+       }
 
        if (!ioaddr) {
-               BUGMSG(D_NORMAL, "No autoprobe for IO mapped cards; you "
-                      "must specify the base address!\n");
+               arc_printk(D_NORMAL, dev, "No autoprobe for IO mapped cards; you must specify the base address!\n");
                return -ENODEV;
        }
        if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com90io probe")) {
-               BUGMSG(D_INIT_REASONS, "IO request_region %x-%x failed.\n",
-                      ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
+               arc_printk(D_INIT_REASONS, dev, "IO request_region %x-%x failed\n",
+                          ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
                return -ENXIO;
        }
-       if (ASTATUS() == 0xFF) {
-               BUGMSG(D_INIT_REASONS, "IO address %x empty\n", ioaddr);
+       if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) {
+               arc_printk(D_INIT_REASONS, dev, "IO address %x empty\n",
+                          ioaddr);
                goto err_out;
        }
-       inb(_RESET);
+       arcnet_inb(ioaddr, COM9026_REG_R_RESET);
        mdelay(RESETtime);
 
-       status = ASTATUS();
+       status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
 
        if ((status & 0x9D) != (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
-               BUGMSG(D_INIT_REASONS, "Status invalid (%Xh).\n", status);
+               arc_printk(D_INIT_REASONS, dev, "Status invalid (%Xh)\n",
+                          status);
                goto err_out;
        }
-       BUGMSG(D_INIT_REASONS, "Status after reset: %X\n", status);
+       arc_printk(D_INIT_REASONS, dev, "Status after reset: %X\n", status);
 
-       ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
+       arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
+                   ioaddr, COM9026_REG_W_COMMAND);
 
-       BUGMSG(D_INIT_REASONS, "Status after reset acknowledged: %X\n", status);
+       arc_printk(D_INIT_REASONS, dev, "Status after reset acknowledged: %X\n",
+                  status);
 
-       status = ASTATUS();
+       status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
 
        if (status & RESETflag) {
-               BUGMSG(D_INIT_REASONS, "Eternal reset (status=%Xh)\n", status);
+               arc_printk(D_INIT_REASONS, dev, "Eternal reset (status=%Xh)\n",
+                          status);
                goto err_out;
        }
-       outb((0x16 | IOMAPflag) & ~ENABLE16flag, _CONFIG);
+       arcnet_outb((0x16 | IOMAPflag) & ~ENABLE16flag,
+                   ioaddr, COM9026_REG_RW_CONFIG);
 
        /* Read first loc'n of memory */
 
-       outb(AUTOINCflag, _ADDR_HI);
-       outb(0, _ADDR_LO);
+       arcnet_outb(AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);
+       arcnet_outb(0, ioaddr,  COM9026_REG_W_ADDR_LO);
 
-       if ((status = inb(_MEMDATA)) != 0xd1) {
-               BUGMSG(D_INIT_REASONS, "Signature byte not found"
-                      " (%Xh instead).\n", status);
+       status = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);
+       if (status != 0xd1) {
+               arc_printk(D_INIT_REASONS, dev, "Signature byte not found (%Xh instead).\n",
+                          status);
                goto err_out;
        }
        if (!dev->irq) {
-               /*
-                * if we do this, we're sure to get an IRQ since the
+               /* if we do this, we're sure to get an IRQ since the
                 * card has just reset and the NORXflag is on until
                 * we tell it to start receiving.
                 */
 
                airqmask = probe_irq_on();
-               outb(NORXflag, _INTMASK);
+               arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK);
                udelay(1);
-               outb(0, _INTMASK);
+               arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK);
                dev->irq = probe_irq_off(airqmask);
 
                if ((int)dev->irq <= 0) {
-                       BUGMSG(D_INIT_REASONS, "Autoprobe IRQ failed\n");
+                       arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed\n");
                        goto err_out;
                }
        }
@@ -227,7 +215,6 @@ err_out:
        return -ENODEV;
 }
 
-
 /* Set up the struct net_device associated with this card.  Called after
  * probing succeeds.
  */
@@ -238,12 +225,14 @@ static int __init com90io_found(struct net_device *dev)
        int err;
 
        /* Reserve the irq */
-       if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (COM90xx-IO)", dev)) {
-               BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
+       if (request_irq(dev->irq, arcnet_interrupt, 0,
+                       "arcnet (COM90xx-IO)", dev)) {
+               arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq);
                return -ENODEV;
        }
        /* Reserve the I/O region */
-       if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE, "arcnet (COM90xx-IO)")) {
+       if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE,
+                           "arcnet (COM90xx-IO)")) {
                free_irq(dev->irq, dev);
                return -EBUSY;
        }
@@ -259,7 +248,7 @@ static int __init com90io_found(struct net_device *dev)
        lp->hw.copy_from_card = com90io_copy_from_card;
 
        lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag;
-       SETCONF();
+       arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG);
 
        /* get and check the station ID from offset 1 in shmem */
 
@@ -267,21 +256,20 @@ static int __init com90io_found(struct net_device *dev)
 
        err = register_netdev(dev);
        if (err) {
-               outb((inb(_CONFIG) & ~IOMAPflag), _CONFIG);
+               arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag,
+                           ioaddr, COM9026_REG_RW_CONFIG);
                free_irq(dev->irq, dev);
                release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
                return err;
        }
 
-       BUGMSG(D_NORMAL, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n",
-              dev->dev_addr[0], dev->base_addr, dev->irq);
+       arc_printk(D_NORMAL, dev, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n",
+                  dev->dev_addr[0], dev->base_addr, dev->irq);
 
        return 0;
 }
 
-
-/*
- * Do a hardware reset on the card, and set up necessary registers.
+/* Do a hardware reset on the card, and set up necessary registers.
  *
  * This should be called as little as possible, because it disrupts the
  * token on the network (causes a RECON) and requires a significant delay.
@@ -293,67 +281,66 @@ static int com90io_reset(struct net_device *dev, int really_reset)
        struct arcnet_local *lp = netdev_priv(dev);
        short ioaddr = dev->base_addr;
 
-       BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", dev->name, ASTATUS());
+       arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n",
+                  dev->name, arcnet_inb(ioaddr, COM9026_REG_R_STATUS));
 
        if (really_reset) {
                /* reset the card */
-               inb(_RESET);
+               arcnet_inb(ioaddr, COM9026_REG_R_RESET);
                mdelay(RESETtime);
        }
        /* Set the thing to IO-mapped, 8-bit  mode */
        lp->config = (0x1C | IOMAPflag) & ~ENABLE16flag;
-       SETCONF();
+       arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG);
 
-       ACOMMAND(CFLAGScmd | RESETclear);       /* clear flags & end reset */
-       ACOMMAND(CFLAGScmd | CONFIGclear);
+       arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);
+                                       /* clear flags & end reset */
+       arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);
 
        /* verify that the ARCnet signature byte is present */
        if (get_buffer_byte(dev, 0) != TESTvalue) {
-               BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n");
+               arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");
                return 1;
        }
        /* enable extended (512-byte) packets */
-       ACOMMAND(CONFIGcmd | EXTconf);
-
+       arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
        /* done!  return success. */
        return 0;
 }
 
-
 static void com90io_command(struct net_device *dev, int cmd)
 {
        short ioaddr = dev->base_addr;
 
-       ACOMMAND(cmd);
+       arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND);
 }
 
-
 static int com90io_status(struct net_device *dev)
 {
        short ioaddr = dev->base_addr;
 
-       return ASTATUS();
+       return arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
 }
 
-
 static void com90io_setmask(struct net_device *dev, int mask)
 {
        short ioaddr = dev->base_addr;
 
-       AINTMASK(mask);
+       arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK);
 }
 
-static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset,
-                                void *buf, int count)
+static void com90io_copy_to_card(struct net_device *dev, int bufnum,
+                                int offset, void *buf, int count)
 {
-       TIME("put_whole_buffer", count, put_whole_buffer(dev, bufnum * 512 + offset, count, buf));
+       TIME(dev, "put_whole_buffer", count,
+            put_whole_buffer(dev, bufnum * 512 + offset, count, buf));
 }
 
-
-static void com90io_copy_from_card(struct net_device *dev, int bufnum, int offset,
-                                  void *buf, int count)
+static void com90io_copy_from_card(struct net_device *dev, int bufnum,
+                                  int offset, void *buf, int count)
 {
-       TIME("get_whole_buffer", count, get_whole_buffer(dev, bufnum * 512 + offset, count, buf));
+       TIME(dev, "get_whole_buffer", count,
+            get_whole_buffer(dev, bufnum * 512 + offset, count, buf));
 }
 
 static int io;                 /* use the insmod io= irq= shmem= options */
@@ -369,12 +356,13 @@ MODULE_LICENSE("GPL");
 static int __init com90io_setup(char *s)
 {
        int ints[4];
+
        s = get_options(s, 4, ints);
        if (!ints[0])
                return 0;
        switch (ints[0]) {
        default:                /* ERROR */
-               printk("com90io: Too many arguments.\n");
+               pr_err("Too many arguments\n");
        case 2:         /* IRQ */
                irq = ints[2];
        case 1:         /* IO address */
@@ -421,8 +409,11 @@ static void __exit com90io_exit(void)
 
        unregister_netdev(dev);
 
-       /* Set the thing back to MMAP mode, in case the old driver is loaded later */
-       outb((inb(_CONFIG) & ~IOMAPflag), _CONFIG);
+       /* In case the old driver is loaded later,
+        * set the thing back to MMAP mode
+        */
+       arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag,
+                   ioaddr, COM9026_REG_RW_CONFIG);
 
        free_irq(dev->irq, dev);
        release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
index b80fbe40aa0e2b16b25fec455dd10e43c65ac355..0d9b45ff1bb21bc7878c251c63082a5400a7d93b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - COM90xx chipset (memory-mapped buffers)
- * 
+ *
  * Written 1994-1999 by Avery Pennarun.
  * Written 1999 by Martin Mares <mj@ucw.cz>.
  * Derived from skeleton.c by Donald Becker.
@@ -24,6 +24,9 @@
  *
  * **********************
  */
+
+#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/netdevice.h>
 #include <linux/slab.h>
-#include <asm/io.h>
-#include <linux/arcdevice.h>
-
-
-#define VERSION "arcnet: COM90xx chipset support\n"
+#include <linux/io.h>
 
+#include "arcdevice.h"
+#include "com9026.h"
 
 /* Define this to speed up the autoprobe by assuming if only one io port and
  * shmem are left in the list at Stage 5, they must correspond to each
@@ -53,7 +54,6 @@
  */
 #undef FAST_PROBE
 
-
 /* Internal function declarations */
 static int com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem *);
 static void com90xx_command(struct net_device *dev, int command);
@@ -62,8 +62,8 @@ static void com90xx_setmask(struct net_device *dev, int mask);
 static int com90xx_reset(struct net_device *dev, int really_reset);
 static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset,
                                 void *buf, int count);
-static void com90xx_copy_from_card(struct net_device *dev, int bufnum, int offset,
-                                  void *buf, int count);
+static void com90xx_copy_from_card(struct net_device *dev, int bufnum,
+                                  int offset, void *buf, int count);
 
 /* Known ARCnet cards */
 
@@ -77,26 +77,7 @@ static int numcards;
 
 /* Amount of I/O memory used by the card */
 #define BUFFER_SIZE (512)
-#define MIRROR_SIZE (BUFFER_SIZE*4)
-
-/* COM 9026 controller chip --> ARCnet register addresses */
-#define _INTMASK (ioaddr+0)    /* writable */
-#define _STATUS  (ioaddr+0)    /* readable */
-#define _COMMAND (ioaddr+1)    /* writable, returns random vals on read (?) */
-#define _CONFIG  (ioaddr+2)    /* Configuration register */
-#define _RESET   (ioaddr+8)    /* software reset (on read) */
-#define _MEMDATA (ioaddr+12)   /* Data port for IO-mapped memory */
-#define _ADDR_HI (ioaddr+15)   /* Control registers for said */
-#define _ADDR_LO (ioaddr+14)
-
-#undef ASTATUS
-#undef ACOMMAND
-#undef AINTMASK
-
-#define ASTATUS()      inb(_STATUS)
-#define ACOMMAND(cmd)  outb((cmd),_COMMAND)
-#define AINTMASK(msk)  outb((msk),_INTMASK)
-
+#define MIRROR_SIZE (BUFFER_SIZE * 4)
 
 static int com90xx_skip_probe __initdata = 0;
 
@@ -116,8 +97,7 @@ static void __init com90xx_probe(void)
 {
        int count, status, ioaddr, numprint, airq, openparen = 0;
        unsigned long airqmask;
-       int ports[(0x3f0 - 0x200) / 16 + 1] =
-       {0};
+       int ports[(0x3f0 - 0x200) / 16 + 1] = { 0 };
        unsigned long *shmems;
        void __iomem **iomem;
        int numports, numshmems, *port;
@@ -127,18 +107,19 @@ static void __init com90xx_probe(void)
        if (!io && !irq && !shmem && !*device && com90xx_skip_probe)
                return;
 
-       shmems = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(unsigned long),
+       shmems = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(unsigned long),
                         GFP_KERNEL);
        if (!shmems)
                return;
-       iomem = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(void __iomem *),
-                        GFP_KERNEL);
+       iomem = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(void __iomem *),
+                       GFP_KERNEL);
        if (!iomem) {
                kfree(shmems);
                return;
        }
 
-       BUGLVL(D_NORMAL) printk(VERSION);
+       if (BUGLVL(D_NORMAL))
+               pr_info("%s\n", "COM90xx chipset support");
 
        /* set up the arrays where we'll store the possible probe addresses */
        numports = numshmems = 0;
@@ -161,38 +142,43 @@ static void __init com90xx_probe(void)
                numprint++;
                numprint %= 8;
                if (!numprint) {
-                       BUGMSG2(D_INIT, "\n");
-                       BUGMSG2(D_INIT, "S1: ");
+                       arc_cont(D_INIT, "\n");
+                       arc_cont(D_INIT, "S1: ");
                }
-               BUGMSG2(D_INIT, "%Xh ", *port);
+               arc_cont(D_INIT, "%Xh ", *port);
 
                ioaddr = *port;
 
-               if (!request_region(*port, ARCNET_TOTAL_SIZE, "arcnet (90xx)")) {
-                       BUGMSG2(D_INIT_REASONS, "(request_region)\n");
-                       BUGMSG2(D_INIT_REASONS, "S1: ");
-                       BUGLVL(D_INIT_REASONS) numprint = 0;
+               if (!request_region(*port, ARCNET_TOTAL_SIZE,
+                                   "arcnet (90xx)")) {
+                       arc_cont(D_INIT_REASONS, "(request_region)\n");
+                       arc_cont(D_INIT_REASONS, "S1: ");
+                       if (BUGLVL(D_INIT_REASONS))
+                               numprint = 0;
                        *port-- = ports[--numports];
                        continue;
                }
-               if (ASTATUS() == 0xFF) {
-                       BUGMSG2(D_INIT_REASONS, "(empty)\n");
-                       BUGMSG2(D_INIT_REASONS, "S1: ");
-                       BUGLVL(D_INIT_REASONS) numprint = 0;
+               if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) {
+                       arc_cont(D_INIT_REASONS, "(empty)\n");
+                       arc_cont(D_INIT_REASONS, "S1: ");
+                       if (BUGLVL(D_INIT_REASONS))
+                               numprint = 0;
                        release_region(*port, ARCNET_TOTAL_SIZE);
                        *port-- = ports[--numports];
                        continue;
                }
-               inb(_RESET);    /* begin resetting card */
+               /* begin resetting card */
+               arcnet_inb(ioaddr, COM9026_REG_R_RESET);
 
-               BUGMSG2(D_INIT_REASONS, "\n");
-               BUGMSG2(D_INIT_REASONS, "S1: ");
-               BUGLVL(D_INIT_REASONS) numprint = 0;
+               arc_cont(D_INIT_REASONS, "\n");
+               arc_cont(D_INIT_REASONS, "S1: ");
+               if (BUGLVL(D_INIT_REASONS))
+                       numprint = 0;
        }
-       BUGMSG2(D_INIT, "\n");
+       arc_cont(D_INIT, "\n");
 
        if (!numports) {
-               BUGMSG2(D_NORMAL, "S1: No ARCnet cards found.\n");
+               arc_cont(D_NORMAL, "S1: No ARCnet cards found.\n");
                kfree(shmems);
                kfree(iomem);
                return;
@@ -206,12 +192,12 @@ static void __init com90xx_probe(void)
                numprint++;
                numprint %= 8;
                if (!numprint) {
-                       BUGMSG2(D_INIT, "\n");
-                       BUGMSG2(D_INIT, "S2: ");
+                       arc_cont(D_INIT, "\n");
+                       arc_cont(D_INIT, "S2: ");
                }
-               BUGMSG2(D_INIT, "%Xh ", *port);
+               arc_cont(D_INIT, "%Xh ", *port);
        }
-       BUGMSG2(D_INIT, "\n");
+       arc_cont(D_INIT, "\n");
        mdelay(RESETtime);
 
        /* Stage 3: abandon any shmem addresses that don't have the signature
@@ -224,29 +210,33 @@ static void __init com90xx_probe(void)
                numprint++;
                numprint %= 8;
                if (!numprint) {
-                       BUGMSG2(D_INIT, "\n");
-                       BUGMSG2(D_INIT, "S3: ");
+                       arc_cont(D_INIT, "\n");
+                       arc_cont(D_INIT, "S3: ");
                }
-               BUGMSG2(D_INIT, "%lXh ", *p);
+               arc_cont(D_INIT, "%lXh ", *p);
 
                if (!request_mem_region(*p, MIRROR_SIZE, "arcnet (90xx)")) {
-                       BUGMSG2(D_INIT_REASONS, "(request_mem_region)\n");
-                       BUGMSG2(D_INIT_REASONS, "Stage 3: ");
-                       BUGLVL(D_INIT_REASONS) numprint = 0;
+                       arc_cont(D_INIT_REASONS, "(request_mem_region)\n");
+                       arc_cont(D_INIT_REASONS, "Stage 3: ");
+                       if (BUGLVL(D_INIT_REASONS))
+                               numprint = 0;
                        goto out;
                }
                base = ioremap(*p, MIRROR_SIZE);
                if (!base) {
-                       BUGMSG2(D_INIT_REASONS, "(ioremap)\n");
-                       BUGMSG2(D_INIT_REASONS, "Stage 3: ");
-                       BUGLVL(D_INIT_REASONS) numprint = 0;
+                       arc_cont(D_INIT_REASONS, "(ioremap)\n");
+                       arc_cont(D_INIT_REASONS, "Stage 3: ");
+                       if (BUGLVL(D_INIT_REASONS))
+                               numprint = 0;
                        goto out1;
                }
-               if (readb(base) != TESTvalue) {
-                       BUGMSG2(D_INIT_REASONS, "(%02Xh != %02Xh)\n",
-                               readb(base), TESTvalue);
-                       BUGMSG2(D_INIT_REASONS, "S3: ");
-                       BUGLVL(D_INIT_REASONS) numprint = 0;
+               if (arcnet_readb(base, COM9026_REG_R_STATUS) != TESTvalue) {
+                       arc_cont(D_INIT_REASONS, "(%02Xh != %02Xh)\n",
+                                arcnet_readb(base, COM9026_REG_R_STATUS),
+                                TESTvalue);
+                       arc_cont(D_INIT_REASONS, "S3: ");
+                       if (BUGLVL(D_INIT_REASONS))
+                               numprint = 0;
                        goto out2;
                }
                /* By writing 0x42 to the TESTvalue location, we also make
@@ -254,15 +244,16 @@ static void __init com90xx_probe(void)
                 * in another pass through this loop, they will be discarded
                 * because *cptr != TESTvalue.
                 */
-               writeb(0x42, base);
-               if (readb(base) != 0x42) {
-                       BUGMSG2(D_INIT_REASONS, "(read only)\n");
-                       BUGMSG2(D_INIT_REASONS, "S3: ");
+               arcnet_writeb(0x42, base, COM9026_REG_W_INTMASK);
+               if (arcnet_readb(base, COM9026_REG_R_STATUS) != 0x42) {
+                       arc_cont(D_INIT_REASONS, "(read only)\n");
+                       arc_cont(D_INIT_REASONS, "S3: ");
                        goto out2;
                }
-               BUGMSG2(D_INIT_REASONS, "\n");
-               BUGMSG2(D_INIT_REASONS, "S3: ");
-               BUGLVL(D_INIT_REASONS) numprint = 0;
+               arc_cont(D_INIT_REASONS, "\n");
+               arc_cont(D_INIT_REASONS, "S3: ");
+               if (BUGLVL(D_INIT_REASONS))
+                       numprint = 0;
                iomem[index] = base;
                continue;
        out2:
@@ -273,10 +264,10 @@ static void __init com90xx_probe(void)
                *p-- = shmems[--numshmems];
                index--;
        }
-       BUGMSG2(D_INIT, "\n");
+       arc_cont(D_INIT, "\n");
 
        if (!numshmems) {
-               BUGMSG2(D_NORMAL, "S3: No ARCnet cards found.\n");
+               arc_cont(D_NORMAL, "S3: No ARCnet cards found.\n");
                for (port = &ports[0]; port < ports + numports; port++)
                        release_region(*port, ARCNET_TOTAL_SIZE);
                kfree(shmems);
@@ -291,12 +282,12 @@ static void __init com90xx_probe(void)
                numprint++;
                numprint %= 8;
                if (!numprint) {
-                       BUGMSG2(D_INIT, "\n");
-                       BUGMSG2(D_INIT, "S4: ");
+                       arc_cont(D_INIT, "\n");
+                       arc_cont(D_INIT, "S4: ");
                }
-               BUGMSG2(D_INIT, "%lXh ", *p);
+               arc_cont(D_INIT, "%lXh ", *p);
        }
-       BUGMSG2(D_INIT, "\n");
+       arc_cont(D_INIT, "\n");
 
        /* Stage 5: for any ports that have the correct status, can disable
         * the RESET flag, and (if no irq is given) generate an autoirq,
@@ -308,33 +299,37 @@ static void __init com90xx_probe(void)
        numprint = -1;
        for (port = &ports[0]; port < ports + numports; port++) {
                int found = 0;
+
                numprint++;
                numprint %= 8;
                if (!numprint) {
-                       BUGMSG2(D_INIT, "\n");
-                       BUGMSG2(D_INIT, "S5: ");
+                       arc_cont(D_INIT, "\n");
+                       arc_cont(D_INIT, "S5: ");
                }
-               BUGMSG2(D_INIT, "%Xh ", *port);
+               arc_cont(D_INIT, "%Xh ", *port);
 
                ioaddr = *port;
-               status = ASTATUS();
+               status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
 
                if ((status & 0x9D)
                    != (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
-                       BUGMSG2(D_INIT_REASONS, "(status=%Xh)\n", status);
-                       BUGMSG2(D_INIT_REASONS, "S5: ");
-                       BUGLVL(D_INIT_REASONS) numprint = 0;
+                       arc_cont(D_INIT_REASONS, "(status=%Xh)\n", status);
+                       arc_cont(D_INIT_REASONS, "S5: ");
+                       if (BUGLVL(D_INIT_REASONS))
+                               numprint = 0;
                        release_region(*port, ARCNET_TOTAL_SIZE);
                        *port-- = ports[--numports];
                        continue;
                }
-               ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
-               status = ASTATUS();
+               arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
+                           ioaddr, COM9026_REG_W_COMMAND);
+               status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
                if (status & RESETflag) {
-                       BUGMSG2(D_INIT_REASONS, " (eternal reset, status=%Xh)\n",
-                               status);
-                       BUGMSG2(D_INIT_REASONS, "S5: ");
-                       BUGLVL(D_INIT_REASONS) numprint = 0;
+                       arc_cont(D_INIT_REASONS, " (eternal reset, status=%Xh)\n",
+                                status);
+                       arc_cont(D_INIT_REASONS, "S5: ");
+                       if (BUGLVL(D_INIT_REASONS))
+                               numprint = 0;
                        release_region(*port, ARCNET_TOTAL_SIZE);
                        *port-- = ports[--numports];
                        continue;
@@ -348,15 +343,16 @@ static void __init com90xx_probe(void)
                         * we tell it to start receiving.
                         */
                        airqmask = probe_irq_on();
-                       AINTMASK(NORXflag);
+                       arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK);
                        udelay(1);
-                       AINTMASK(0);
+                       arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK);
                        airq = probe_irq_off(airqmask);
 
                        if (airq <= 0) {
-                               BUGMSG2(D_INIT_REASONS, "(airq=%d)\n", airq);
-                               BUGMSG2(D_INIT_REASONS, "S5: ");
-                               BUGLVL(D_INIT_REASONS) numprint = 0;
+                               arc_cont(D_INIT_REASONS, "(airq=%d)\n", airq);
+                               arc_cont(D_INIT_REASONS, "S5: ");
+                               if (BUGLVL(D_INIT_REASONS))
+                                       numprint = 0;
                                release_region(*port, ARCNET_TOTAL_SIZE);
                                *port-- = ports[--numports];
                                continue;
@@ -365,7 +361,7 @@ static void __init com90xx_probe(void)
                        airq = irq;
                }
 
-               BUGMSG2(D_INIT, "(%d,", airq);
+               arc_cont(D_INIT, "(%d,", airq);
                openparen = 1;
 
                /* Everything seems okay.  But which shmem, if any, puts
@@ -376,14 +372,15 @@ static void __init com90xx_probe(void)
                 */
 #ifdef FAST_PROBE
                if (numports > 1 || numshmems > 1) {
-                       inb(_RESET);
+                       arcnet_inb(ioaddr, COM9026_REG_R_RESET);
                        mdelay(RESETtime);
                } else {
                        /* just one shmem and port, assume they match */
-                       writeb(TESTvalue, iomem[0]);
+                       arcnet_writeb(TESTvalue, iomem[0],
+                                     COM9026_REG_W_INTMASK);
                }
 #else
-               inb(_RESET);
+               arcnet_inb(ioaddr, COM9026_REG_R_RESET);
                mdelay(RESETtime);
 #endif
 
@@ -391,8 +388,8 @@ static void __init com90xx_probe(void)
                        u_long ptr = shmems[index];
                        void __iomem *base = iomem[index];
 
-                       if (readb(base) == TESTvalue) { /* found one */
-                               BUGMSG2(D_INIT, "%lXh)\n", *p);
+                       if (arcnet_readb(base, COM9026_REG_R_STATUS) == TESTvalue) {    /* found one */
+                               arc_cont(D_INIT, "%lXh)\n", *p);
                                openparen = 0;
 
                                /* register the card */
@@ -405,25 +402,30 @@ static void __init com90xx_probe(void)
                                iomem[index] = iomem[numshmems];
                                break;  /* go to the next I/O port */
                        } else {
-                               BUGMSG2(D_INIT_REASONS, "%Xh-", readb(base));
+                               arc_cont(D_INIT_REASONS, "%Xh-",
+                                        arcnet_readb(base, COM9026_REG_R_STATUS));
                        }
                }
 
                if (openparen) {
-                       BUGLVL(D_INIT) printk("no matching shmem)\n");
-                       BUGLVL(D_INIT_REASONS) printk("S5: ");
-                       BUGLVL(D_INIT_REASONS) numprint = 0;
+                       if (BUGLVL(D_INIT))
+                               pr_cont("no matching shmem)\n");
+                       if (BUGLVL(D_INIT_REASONS)) {
+                               pr_cont("S5: ");
+                               numprint = 0;
+                       }
                }
                if (!found)
                        release_region(*port, ARCNET_TOTAL_SIZE);
                *port-- = ports[--numports];
        }
 
-       BUGLVL(D_INIT_REASONS) printk("\n");
+       if (BUGLVL(D_INIT_REASONS))
+               pr_cont("\n");
 
        /* Now put back TESTvalue on all leftover shmems. */
        for (index = 0; index < numshmems; index++) {
-               writeb(TESTvalue, iomem[index]);
+               arcnet_writeb(TESTvalue, iomem[index], COM9026_REG_W_INTMASK);
                iounmap(iomem[index]);
                release_mem_region(shmems[index], MIRROR_SIZE);
        }
@@ -441,7 +443,7 @@ static int check_mirror(unsigned long addr, size_t size)
 
        p = ioremap(addr, size);
        if (p) {
-               if (readb(p) == TESTvalue)
+               if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue)
                        res = 1;
                else
                        res = 0;
@@ -455,7 +457,8 @@ static int check_mirror(unsigned long addr, size_t size)
 /* Set up the struct net_device associated with this card.  Called after
  * probing succeeds.
  */
-static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem *p)
+static int __init com90xx_found(int ioaddr, int airq, u_long shmem,
+                               void __iomem *p)
 {
        struct net_device *dev = NULL;
        struct arcnet_local *lp;
@@ -465,7 +468,7 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem
        /* allocate struct net_device */
        dev = alloc_arcdev(device);
        if (!dev) {
-               BUGMSG2(D_NORMAL, "com90xx: Can't allocate device!\n");
+               arc_cont(D_NORMAL, "com90xx: Can't allocate device!\n");
                iounmap(p);
                release_mem_region(shmem, MIRROR_SIZE);
                return -ENOMEM;
@@ -478,7 +481,7 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem
         * 2k (or there are no mirrors at all) but on some, it's 4k.
         */
        mirror_size = MIRROR_SIZE;
-       if (readb(p) == TESTvalue &&
+       if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue &&
            check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
            check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
                mirror_size = 2 * MIRROR_SIZE;
@@ -499,12 +502,14 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem
        iounmap(p);
        release_mem_region(shmem, MIRROR_SIZE);
 
-       if (!request_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1, "arcnet (90xx)"))
+       if (!request_mem_region(dev->mem_start,
+                               dev->mem_end - dev->mem_start + 1,
+                               "arcnet (90xx)"))
                goto err_free_dev;
 
        /* reserve the irq */
        if (request_irq(airq, arcnet_interrupt, 0, "arcnet (90xx)", dev)) {
-               BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", airq);
+               arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", airq);
                goto err_release_mem;
        }
        dev->irq = airq;
@@ -518,22 +523,23 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem
        lp->hw.owner = THIS_MODULE;
        lp->hw.copy_to_card = com90xx_copy_to_card;
        lp->hw.copy_from_card = com90xx_copy_from_card;
-       lp->mem_start = ioremap(dev->mem_start, dev->mem_end - dev->mem_start + 1);
+       lp->mem_start = ioremap(dev->mem_start,
+                               dev->mem_end - dev->mem_start + 1);
        if (!lp->mem_start) {
-               BUGMSG(D_NORMAL, "Can't remap device memory!\n");
+               arc_printk(D_NORMAL, dev, "Can't remap device memory!\n");
                goto err_free_irq;
        }
 
        /* get and check the station ID from offset 1 in shmem */
-       dev->dev_addr[0] = readb(lp->mem_start + 1);
+       dev->dev_addr[0] = arcnet_readb(lp->mem_start, COM9026_REG_R_STATION);
 
        dev->base_addr = ioaddr;
 
-       BUGMSG(D_NORMAL, "COM90xx station %02Xh found at %03lXh, IRQ %d, "
-              "ShMem %lXh (%ld*%xh).\n",
-              dev->dev_addr[0],
-              dev->base_addr, dev->irq, dev->mem_start,
-        (dev->mem_end - dev->mem_start + 1) / mirror_size, mirror_size);
+       arc_printk(D_NORMAL, dev, "COM90xx station %02Xh found at %03lXh, IRQ %d, ShMem %lXh (%ld*%xh).\n",
+                  dev->dev_addr[0],
+                  dev->base_addr, dev->irq, dev->mem_start,
+                  (dev->mem_end - dev->mem_start + 1) / mirror_size,
+                  mirror_size);
 
        if (register_netdev(dev))
                goto err_unmap;
@@ -552,34 +558,29 @@ err_free_dev:
        return -EIO;
 }
 
-
 static void com90xx_command(struct net_device *dev, int cmd)
 {
        short ioaddr = dev->base_addr;
 
-       ACOMMAND(cmd);
+       arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND);
 }
 
-
 static int com90xx_status(struct net_device *dev)
 {
        short ioaddr = dev->base_addr;
 
-       return ASTATUS();
+       return arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
 }
 
-
 static void com90xx_setmask(struct net_device *dev, int mask)
 {
        short ioaddr = dev->base_addr;
 
-       AINTMASK(mask);
+       arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK);
 }
 
-
-/*
- * Do a hardware reset on the card, and set up necessary registers.
- * 
+/* Do a hardware reset on the card, and set up necessary registers.
+ *
  * This should be called as little as possible, because it disrupts the
  * token on the network (causes a RECON) and requires a significant delay.
  *
@@ -590,53 +591,58 @@ static int com90xx_reset(struct net_device *dev, int really_reset)
        struct arcnet_local *lp = netdev_priv(dev);
        short ioaddr = dev->base_addr;
 
-       BUGMSG(D_INIT, "Resetting (status=%02Xh)\n", ASTATUS());
+       arc_printk(D_INIT, dev, "Resetting (status=%02Xh)\n",
+                  arcnet_inb(ioaddr, COM9026_REG_R_STATUS));
 
        if (really_reset) {
                /* reset the card */
-               inb(_RESET);
+               arcnet_inb(ioaddr, COM9026_REG_R_RESET);
                mdelay(RESETtime);
        }
-       ACOMMAND(CFLAGScmd | RESETclear);       /* clear flags & end reset */
-       ACOMMAND(CFLAGScmd | CONFIGclear);
+       /* clear flags & end reset */
+       arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);
+       arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);
 
+#if 0
        /* don't do this until we verify that it doesn't hurt older cards! */
-       /* outb(inb(_CONFIG) | ENABLE16flag, _CONFIG); */
+       arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) | ENABLE16flag,
+                   ioaddr, COM9026_REG_RW_CONFIG);
+#endif
 
        /* verify that the ARCnet signature byte is present */
-       if (readb(lp->mem_start) != TESTvalue) {
+       if (arcnet_readb(lp->mem_start, COM9026_REG_R_STATUS) != TESTvalue) {
                if (really_reset)
-                       BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n");
+                       arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");
                return 1;
        }
        /* enable extended (512-byte) packets */
-       ACOMMAND(CONFIGcmd | EXTconf);
+       arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
 
        /* clean out all the memory to make debugging make more sense :) */
-       BUGLVL(D_DURING)
-           memset_io(lp->mem_start, 0x42, 2048);
+       if (BUGLVL(D_DURING))
+               memset_io(lp->mem_start, 0x42, 2048);
 
        /* done!  return success. */
        return 0;
 }
 
-static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset,
-                                void *buf, int count)
+static void com90xx_copy_to_card(struct net_device *dev, int bufnum,
+                                int offset, void *buf, int count)
 {
        struct arcnet_local *lp = netdev_priv(dev);
        void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
-       TIME("memcpy_toio", count, memcpy_toio(memaddr, buf, count));
-}
 
+       TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count));
+}
 
-static void com90xx_copy_from_card(struct net_device *dev, int bufnum, int offset,
-                                  void *buf, int count)
+static void com90xx_copy_from_card(struct net_device *dev, int bufnum,
+                                  int offset, void *buf, int count)
 {
        struct arcnet_local *lp = netdev_priv(dev);
        void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
-       TIME("memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
-}
 
+       TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
+}
 
 MODULE_LICENSE("GPL");
 
@@ -664,7 +670,8 @@ static void __exit com90xx_exit(void)
                free_irq(dev->irq, dev);
                iounmap(lp->mem_start);
                release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
-               release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
+               release_mem_region(dev->mem_start,
+                                  dev->mem_end - dev->mem_start + 1);
                free_netdev(dev);
        }
 }
@@ -679,13 +686,13 @@ static int __init com90xx_setup(char *s)
 
        s = get_options(s, 8, ints);
        if (!ints[0] && !*s) {
-               printk("com90xx: Disabled.\n");
+               pr_notice("Disabled\n");
                return 1;
        }
 
        switch (ints[0]) {
        default:                /* ERROR */
-               printk("com90xx: Too many arguments.\n");
+               pr_err("Too many arguments\n");
        case 3:         /* Mem address */
                shmem = ints[3];
        case 2:         /* IRQ */
index f81db4070a574500172fa4dd5521859cc6e063e3..4b1a75469cb10962f18f5958d94cb51fb51e18c9 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - RFC1051 ("simple" standard) packet encapsulation
- * 
+ *
  * Written 1994-1999 by Avery Pennarun.
  * Derived from skeleton.c by Donald Becker.
  *
@@ -23,6 +23,9 @@
  *
  * **********************
  */
+
+#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/gfp.h>
 #include <linux/init.h>
 #include <net/arp.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
-#include <linux/arcdevice.h>
-
-#define VERSION "arcnet: RFC1051 \"simple standard\" (`s') encapsulation support loaded.\n"
 
+#include "arcdevice.h"
 
 static __be16 type_trans(struct sk_buff *skb, struct net_device *dev);
 static void rx(struct net_device *dev, int bufnum,
@@ -43,9 +44,7 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
 static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
                      int bufnum);
 
-
-static struct ArcProto rfc1051_proto =
-{
+static struct ArcProto rfc1051_proto = {
        .suffix         = 's',
        .mtu            = XMTU - RFC1051_HDR_SIZE,
        .is_ip          = 1,
@@ -56,10 +55,9 @@ static struct ArcProto rfc1051_proto =
        .ack_tx         = NULL
 };
 
-
 static int __init arcnet_rfc1051_init(void)
 {
-       printk(VERSION);
+       pr_info("%s\n", "RFC1051 \"simple standard\" (`s') encapsulation support loaded");
 
        arc_proto_map[ARC_P_IP_RFC1051]
            = arc_proto_map[ARC_P_ARP_RFC1051]
@@ -82,14 +80,13 @@ module_exit(arcnet_rfc1051_exit);
 
 MODULE_LICENSE("GPL");
 
-/*
- * Determine a packet's protocol ID.
- * 
+/* Determine a packet's protocol ID.
+ *
  * With ARCnet we have to convert everything to Ethernet-style stuff.
  */
 static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
 {
-       struct archdr *pkt = (struct archdr *) skb->data;
+       struct archdr *pkt = (struct archdr *)skb->data;
        struct arc_rfc1051 *soft = &pkt->soft.rfc1051;
        int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
 
@@ -97,9 +94,9 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
        skb_reset_mac_header(skb);
        skb_pull(skb, hdr_size);
 
-       if (pkt->hard.dest == 0)
+       if (pkt->hard.dest == 0) {
                skb->pkt_type = PACKET_BROADCAST;
-       else if (dev->flags & IFF_PROMISC) {
+       else if (dev->flags & IFF_PROMISC) {
                /* if we're not sending to ourselves :) */
                if (pkt->hard.dest != dev->dev_addr[0])
                        skb->pkt_type = PACKET_OTHERHOST;
@@ -120,7 +117,6 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
        return htons(ETH_P_IP);
 }
 
-
 /* packet receiver */
 static void rx(struct net_device *dev, int bufnum,
               struct archdr *pkthdr, int length)
@@ -130,7 +126,7 @@ static void rx(struct net_device *dev, int bufnum,
        struct archdr *pkt = pkthdr;
        int ofs;
 
-       BUGMSG(D_DURING, "it's a raw packet (length=%d)\n", length);
+       arc_printk(D_DURING, dev, "it's a raw packet (length=%d)\n", length);
 
        if (length >= MinTU)
                ofs = 512 - length;
@@ -138,15 +134,14 @@ static void rx(struct net_device *dev, int bufnum,
                ofs = 256 - length;
 
        skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
-       if (skb == NULL) {
-               BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
+       if (!skb) {
                dev->stats.rx_dropped++;
                return;
        }
        skb_put(skb, length + ARC_HDR_SIZE);
        skb->dev = dev;
 
-       pkt = (struct archdr *) skb->data;
+       pkt = (struct archdr *)skb->data;
 
        /* up to sizeof(pkt->soft) has already been copied from the card */
        memcpy(pkt, pkthdr, sizeof(struct archdr));
@@ -155,21 +150,19 @@ static void rx(struct net_device *dev, int bufnum,
                                      pkt->soft.raw + sizeof(pkt->soft),
                                      length - sizeof(pkt->soft));
 
-       BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
+       if (BUGLVL(D_SKB))
+               arcnet_dump_skb(dev, skb, "rx");
 
        skb->protocol = type_trans(skb, dev);
        netif_rx(skb);
 }
 
-
-/*
- * Create the ARCnet hard/soft headers for RFC1051.
- */
+/* Create the ARCnet hard/soft headers for RFC1051 */
 static int build_header(struct sk_buff *skb, struct net_device *dev,
                        unsigned short type, uint8_t daddr)
 {
        int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
-       struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
+       struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size);
        struct arc_rfc1051 *soft = &pkt->soft.rfc1051;
 
        /* set the protocol ID according to RFC1051 */
@@ -181,29 +174,26 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
                soft->proto = ARC_P_ARP_RFC1051;
                break;
        default:
-               BUGMSG(D_NORMAL, "RFC1051: I don't understand protocol %d (%Xh)\n",
-                      type, type);
+               arc_printk(D_NORMAL, dev, "RFC1051: I don't understand protocol %d (%Xh)\n",
+                          type, type);
                dev->stats.tx_errors++;
                dev->stats.tx_aborted_errors++;
                return 0;
        }
 
-
-       /*
-        * Set the source hardware address.
+       /* Set the source hardware address.
         *
         * This is pretty pointless for most purposes, but it can help in
-        * debugging.  ARCnet does not allow us to change the source address in
-        * the actual packet sent)
+        * debugging.  ARCnet does not allow us to change the source address
+        * in the actual packet sent.
         */
        pkt->hard.source = *dev->dev_addr;
 
        /* see linux/net/ethernet/eth.c to see where I got the following */
 
        if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
-               /* 
-                * FIXME: fill in the last byte of the dest ipaddr here to better
-                * comply with RFC1051 in "noarp" mode.
+               /* FIXME: fill in the last byte of the dest ipaddr here to
+                * better comply with RFC1051 in "noarp" mode.
                 */
                pkt->hard.dest = 0;
                return hdr_size;
@@ -214,7 +204,6 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
        return hdr_size;        /* success */
 }
 
-
 static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
                      int bufnum)
 {
@@ -222,15 +211,16 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
        struct arc_hardware *hard = &pkt->hard;
        int ofs;
 
-       BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
-              lp->next_tx, lp->cur_tx, bufnum);
+       arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n",
+                  lp->next_tx, lp->cur_tx, bufnum);
 
-       length -= ARC_HDR_SIZE; /* hard header is not included in packet length */
+       /* hard header is not included in packet length */
+       length -= ARC_HDR_SIZE;
 
        if (length > XMTU) {
                /* should never happen! other people already check for this. */
-               BUGMSG(D_NORMAL, "Bug!  prepare_tx with size %d (> %d)\n",
-                      length, XMTU);
+               arc_printk(D_NORMAL, dev, "Bug!  prepare_tx with size %d (> %d)\n",
+                          length, XMTU);
                length = XMTU;
        }
        if (length > MinTU) {
@@ -239,8 +229,9 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
        } else if (length > MTU) {
                hard->offset[0] = 0;
                hard->offset[1] = ofs = 512 - length - 3;
-       } else
+       } else {
                hard->offset[0] = ofs = 256 - length;
+       }
 
        lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
        lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft, length);
index b71431aae0846ee8cb69101974f341246265fe59..566da5ecdc9d6b6c26b0242543f52b1bc7be8766 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - RFC1201 (standard) packet encapsulation
- * 
+ *
  * Written 1994-1999 by Avery Pennarun.
  * Derived from skeleton.c by Donald Becker.
  *
  *
  * **********************
  */
+
+#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
+
 #include <linux/gfp.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/if_arp.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
-#include <linux/arcdevice.h>
 
-MODULE_LICENSE("GPL");
-#define VERSION "arcnet: RFC1201 \"standard\" (`a') encapsulation support loaded.\n"
+#include "arcdevice.h"
 
+MODULE_LICENSE("GPL");
 
 static __be16 type_trans(struct sk_buff *skb, struct net_device *dev);
 static void rx(struct net_device *dev, int bufnum,
@@ -44,8 +46,7 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
                      int bufnum);
 static int continue_tx(struct net_device *dev, int bufnum);
 
-static struct ArcProto rfc1201_proto =
-{
+static struct ArcProto rfc1201_proto = {
        .suffix         = 'a',
        .mtu            = 1500, /* could be more, but some receivers can't handle it... */
        .is_ip          = 1,    /* This is for sending IP and ARP packages */
@@ -56,10 +57,9 @@ static struct ArcProto rfc1201_proto =
        .ack_tx         = NULL
 };
 
-
 static int __init arcnet_rfc1201_init(void)
 {
-       printk(VERSION);
+       pr_info("%s\n", "RFC1201 \"standard\" (`a') encapsulation support loaded");
 
        arc_proto_map[ARC_P_IP]
            = arc_proto_map[ARC_P_IPV6]
@@ -84,14 +84,13 @@ static void __exit arcnet_rfc1201_exit(void)
 module_init(arcnet_rfc1201_init);
 module_exit(arcnet_rfc1201_exit);
 
-/*
- * Determine a packet's protocol ID.
- * 
+/* Determine a packet's protocol ID.
+ *
  * With ARCnet we have to convert everything to Ethernet-style stuff.
  */
 static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
 {
-       struct archdr *pkt = (struct archdr *) skb->data;
+       struct archdr *pkt = (struct archdr *)skb->data;
        struct arc_rfc1201 *soft = &pkt->soft.rfc1201;
        int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
 
@@ -99,9 +98,9 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
        skb_reset_mac_header(skb);
        skb_pull(skb, hdr_size);
 
-       if (pkt->hard.dest == 0)
+       if (pkt->hard.dest == 0) {
                skb->pkt_type = PACKET_BROADCAST;
-       else if (dev->flags & IFF_PROMISC) {
+       else if (dev->flags & IFF_PROMISC) {
                /* if we're not sending to ourselves :) */
                if (pkt->hard.dest != dev->dev_addr[0])
                        skb->pkt_type = PACKET_OTHERHOST;
@@ -129,7 +128,6 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
        return htons(ETH_P_IP);
 }
 
-
 /* packet receiver */
 static void rx(struct net_device *dev, int bufnum,
               struct archdr *pkthdr, int length)
@@ -141,7 +139,8 @@ static void rx(struct net_device *dev, int bufnum,
        int saddr = pkt->hard.source, ofs;
        struct Incoming *in = &lp->rfc1201.incoming[saddr];
 
-       BUGMSG(D_DURING, "it's an RFC1201 packet (length=%d)\n", length);
+       arc_printk(D_DURING, dev, "it's an RFC1201 packet (length=%d)\n",
+                  length);
 
        if (length >= MinTU)
                ofs = 512 - length;
@@ -149,11 +148,11 @@ static void rx(struct net_device *dev, int bufnum,
                ofs = 256 - length;
 
        if (soft->split_flag == 0xFF) {         /* Exception Packet */
-               if (length >= 4 + RFC1201_HDR_SIZE)
-                       BUGMSG(D_DURING, "compensating for exception packet\n");
-               else {
-                       BUGMSG(D_EXTRA, "short RFC1201 exception packet from %02Xh",
-                              saddr);
+               if (length >= 4 + RFC1201_HDR_SIZE) {
+                       arc_printk(D_DURING, dev, "compensating for exception packet\n");
+               else {
+                       arc_printk(D_EXTRA, dev, "short RFC1201 exception packet from %02Xh",
+                                  saddr);
                        return;
                }
 
@@ -164,12 +163,13 @@ static void rx(struct net_device *dev, int bufnum,
                                      soft, sizeof(pkt->soft));
        }
        if (!soft->split_flag) {        /* not split */
-               BUGMSG(D_RX, "incoming is not split (splitflag=%d)\n",
-                      soft->split_flag);
+               arc_printk(D_RX, dev, "incoming is not split (splitflag=%d)\n",
+                          soft->split_flag);
 
                if (in->skb) {  /* already assembling one! */
-                       BUGMSG(D_EXTRA, "aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
-                        in->sequence, soft->split_flag, soft->sequence);
+                       arc_printk(D_EXTRA, dev, "aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
+                                  in->sequence, soft->split_flag,
+                                  soft->sequence);
                        lp->rfc1201.aborted_seq = soft->sequence;
                        dev_kfree_skb_irq(in->skb);
                        dev->stats.rx_errors++;
@@ -179,82 +179,86 @@ static void rx(struct net_device *dev, int bufnum,
                in->sequence = soft->sequence;
 
                skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
-               if (skb == NULL) {
-                       BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
+               if (!skb) {
                        dev->stats.rx_dropped++;
                        return;
                }
                skb_put(skb, length + ARC_HDR_SIZE);
                skb->dev = dev;
 
-               pkt = (struct archdr *) skb->data;
+               pkt = (struct archdr *)skb->data;
                soft = &pkt->soft.rfc1201;
 
-               /* up to sizeof(pkt->soft) has already been copied from the card */
+               /* up to sizeof(pkt->soft) has already
+                * been copied from the card
+                */
                memcpy(pkt, pkthdr, sizeof(struct archdr));
                if (length > sizeof(pkt->soft))
-                       lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
-                                      pkt->soft.raw + sizeof(pkt->soft),
+                       lp->hw.copy_from_card(dev, bufnum,
+                                             ofs + sizeof(pkt->soft),
+                                             pkt->soft.raw + sizeof(pkt->soft),
                                              length - sizeof(pkt->soft));
 
-               /*
-                * ARP packets have problems when sent from some DOS systems: the
-                * source address is always 0!  So we take the hardware source addr
-                * (which is impossible to fumble) and insert it ourselves.
+               /* ARP packets have problems when sent from some DOS systems:
+                * the source address is always 0!
+                * So we take the hardware source addr (which is impossible
+                * to fumble) and insert it ourselves.
                 */
                if (soft->proto == ARC_P_ARP) {
-                       struct arphdr *arp = (struct arphdr *) soft->payload;
+                       struct arphdr *arp = (struct arphdr *)soft->payload;
 
                        /* make sure addresses are the right length */
                        if (arp->ar_hln == 1 && arp->ar_pln == 4) {
-                               uint8_t *cptr = (uint8_t *) arp + sizeof(struct arphdr);
+                               uint8_t *cptr = (uint8_t *)arp + sizeof(struct arphdr);
 
                                if (!*cptr) {   /* is saddr = 00? */
-                                       BUGMSG(D_EXTRA,
-                                              "ARP source address was 00h, set to %02Xh.\n",
-                                              saddr);
+                                       arc_printk(D_EXTRA, dev,
+                                                  "ARP source address was 00h, set to %02Xh\n",
+                                                  saddr);
                                        dev->stats.rx_crc_errors++;
                                        *cptr = saddr;
                                } else {
-                                       BUGMSG(D_DURING, "ARP source address (%Xh) is fine.\n",
-                                              *cptr);
+                                       arc_printk(D_DURING, dev, "ARP source address (%Xh) is fine.\n",
+                                                  *cptr);
                                }
                        } else {
-                               BUGMSG(D_NORMAL, "funny-shaped ARP packet. (%Xh, %Xh)\n",
-                                      arp->ar_hln, arp->ar_pln);
+                               arc_printk(D_NORMAL, dev, "funny-shaped ARP packet. (%Xh, %Xh)\n",
+                                          arp->ar_hln, arp->ar_pln);
                                dev->stats.rx_errors++;
                                dev->stats.rx_crc_errors++;
                        }
                }
-               BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
+               if (BUGLVL(D_SKB))
+                       arcnet_dump_skb(dev, skb, "rx");
 
                skb->protocol = type_trans(skb, dev);
                netif_rx(skb);
        } else {                /* split packet */
-               /*
-                * NOTE: MSDOS ARP packet correction should only need to apply to
-                * unsplit packets, since ARP packets are so short.
+               /* NOTE: MSDOS ARP packet correction should only need to
+                * apply to unsplit packets, since ARP packets are so short.
                 *
-                * My interpretation of the RFC1201 document is that if a packet is
-                * received out of order, the entire assembly process should be
-                * aborted.
+                * My interpretation of the RFC1201 document is that if a
+                * packet is received out of order, the entire assembly
+                * process should be aborted.
                 *
-                * The RFC also mentions "it is possible for successfully received
-                * packets to be retransmitted." As of 0.40 all previously received
-                * packets are allowed, not just the most recent one.
+                * The RFC also mentions "it is possible for successfully
+                * received packets to be retransmitted." As of 0.40 all
+                * previously received packets are allowed, not just the
+                * most recent one.
                 *
-                * We allow multiple assembly processes, one for each ARCnet card
-                * possible on the network.  Seems rather like a waste of memory,
-                * but there's no other way to be reliable.
+                * We allow multiple assembly processes, one for each
+                * ARCnet card possible on the network.
+                * Seems rather like a waste of memory, but there's no
+                * other way to be reliable.
                 */
 
-               BUGMSG(D_RX, "packet is split (splitflag=%d, seq=%d)\n",
-                      soft->split_flag, in->sequence);
+               arc_printk(D_RX, dev, "packet is split (splitflag=%d, seq=%d)\n",
+                          soft->split_flag, in->sequence);
 
                if (in->skb && in->sequence != soft->sequence) {
-                       BUGMSG(D_EXTRA, "wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
-                              saddr, in->sequence, soft->sequence,
-                              soft->split_flag);
+                       arc_printk(D_EXTRA, dev, "wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
+                                  saddr, in->sequence, soft->sequence,
+                                  soft->split_flag);
                        dev_kfree_skb_irq(in->skb);
                        in->skb = NULL;
                        dev->stats.rx_errors++;
@@ -262,24 +266,23 @@ static void rx(struct net_device *dev, int bufnum,
                        in->lastpacket = in->numpackets = 0;
                }
                if (soft->split_flag & 1) {     /* first packet in split */
-                       BUGMSG(D_RX, "brand new splitpacket (splitflag=%d)\n",
-                              soft->split_flag);
+                       arc_printk(D_RX, dev, "brand new splitpacket (splitflag=%d)\n",
+                                  soft->split_flag);
                        if (in->skb) {  /* already assembling one! */
-                               BUGMSG(D_EXTRA, "aborting previous (seq=%d) assembly "
-                                      "(splitflag=%d, seq=%d)\n",
-                                      in->sequence, soft->split_flag,
-                                      soft->sequence);
+                               arc_printk(D_EXTRA, dev, "aborting previous (seq=%d) assembly (splitflag=%d, seq=%d)\n",
+                                          in->sequence, soft->split_flag,
+                                          soft->sequence);
                                dev->stats.rx_errors++;
                                dev->stats.rx_missed_errors++;
                                dev_kfree_skb_irq(in->skb);
                        }
                        in->sequence = soft->sequence;
-                       in->numpackets = ((unsigned) soft->split_flag >> 1) + 2;
+                       in->numpackets = ((unsigned)soft->split_flag >> 1) + 2;
                        in->lastpacket = 1;
 
                        if (in->numpackets > 16) {
-                               BUGMSG(D_EXTRA, "incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
-                                      soft->split_flag);
+                               arc_printk(D_EXTRA, dev, "incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
+                                          soft->split_flag);
                                lp->rfc1201.aborted_seq = soft->sequence;
                                dev->stats.rx_errors++;
                                dev->stats.rx_length_errors++;
@@ -287,14 +290,14 @@ static void rx(struct net_device *dev, int bufnum,
                        }
                        in->skb = skb = alloc_skb(508 * in->numpackets + ARC_HDR_SIZE,
                                                  GFP_ATOMIC);
-                       if (skb == NULL) {
-                               BUGMSG(D_NORMAL, "(split) memory squeeze, dropping packet.\n");
+                       if (!skb) {
+                               arc_printk(D_NORMAL, dev, "(split) memory squeeze, dropping packet.\n");
                                lp->rfc1201.aborted_seq = soft->sequence;
                                dev->stats.rx_dropped++;
                                return;
                        }
                        skb->dev = dev;
-                       pkt = (struct archdr *) skb->data;
+                       pkt = (struct archdr *)skb->data;
                        soft = &pkt->soft.rfc1201;
 
                        memcpy(pkt, pkthdr, ARC_HDR_SIZE + RFC1201_HDR_SIZE);
@@ -302,37 +305,37 @@ static void rx(struct net_device *dev, int bufnum,
 
                        soft->split_flag = 0;   /* end result won't be split */
                } else {        /* not first packet */
-                       int packetnum = ((unsigned) soft->split_flag >> 1) + 1;
+                       int packetnum = ((unsigned)soft->split_flag >> 1) + 1;
 
-                       /*
-                        * if we're not assembling, there's no point trying to
+                       /* if we're not assembling, there's no point trying to
                         * continue.
                         */
                        if (!in->skb) {
                                if (lp->rfc1201.aborted_seq != soft->sequence) {
-                                       BUGMSG(D_EXTRA, "can't continue split without starting "
-                                              "first! (splitflag=%d, seq=%d, aborted=%d)\n",
-                                       soft->split_flag, soft->sequence,
-                                              lp->rfc1201.aborted_seq);
+                                       arc_printk(D_EXTRA, dev, "can't continue split without starting first! (splitflag=%d, seq=%d, aborted=%d)\n",
+                                                  soft->split_flag,
+                                                  soft->sequence,
+                                                  lp->rfc1201.aborted_seq);
                                        dev->stats.rx_errors++;
                                        dev->stats.rx_missed_errors++;
                                }
                                return;
                        }
                        in->lastpacket++;
-                       if (packetnum != in->lastpacket) {      /* not the right flag! */
+                       /* if not the right flag */
+                       if (packetnum != in->lastpacket) {
                                /* harmless duplicate? ignore. */
                                if (packetnum <= in->lastpacket - 1) {
-                                       BUGMSG(D_EXTRA, "duplicate splitpacket ignored! (splitflag=%d)\n",
-                                              soft->split_flag);
+                                       arc_printk(D_EXTRA, dev, "duplicate splitpacket ignored! (splitflag=%d)\n",
+                                                  soft->split_flag);
                                        dev->stats.rx_errors++;
                                        dev->stats.rx_frame_errors++;
                                        return;
                                }
                                /* "bad" duplicate, kill reassembly */
-                               BUGMSG(D_EXTRA, "out-of-order splitpacket, reassembly "
-                                      "(seq=%d) aborted (splitflag=%d, seq=%d)\n",
-                                      in->sequence, soft->split_flag, soft->sequence);
+                               arc_printk(D_EXTRA, dev, "out-of-order splitpacket, reassembly (seq=%d) aborted (splitflag=%d, seq=%d)\n",
+                                          in->sequence, soft->split_flag,
+                                          soft->sequence);
                                lp->rfc1201.aborted_seq = soft->sequence;
                                dev_kfree_skb_irq(in->skb);
                                in->skb = NULL;
@@ -341,7 +344,7 @@ static void rx(struct net_device *dev, int bufnum,
                                in->lastpacket = in->numpackets = 0;
                                return;
                        }
-                       pkt = (struct archdr *) in->skb->data;
+                       pkt = (struct archdr *)in->skb->data;
                        soft = &pkt->soft.rfc1201;
                }
 
@@ -357,11 +360,12 @@ static void rx(struct net_device *dev, int bufnum,
                        in->skb = NULL;
                        in->lastpacket = in->numpackets = 0;
 
-           BUGMSG(D_SKB_SIZE, "skb: received %d bytes from %02X (unsplit)\n",
-               skb->len, pkt->hard.source);
-           BUGMSG(D_SKB_SIZE, "skb: received %d bytes from %02X (split)\n",
-               skb->len, pkt->hard.source);
-                       BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
+                       arc_printk(D_SKB_SIZE, dev, "skb: received %d bytes from %02X (unsplit)\n",
+                                  skb->len, pkt->hard.source);
+                       arc_printk(D_SKB_SIZE, dev, "skb: received %d bytes from %02X (split)\n",
+                                  skb->len, pkt->hard.source);
+                       if (BUGLVL(D_SKB))
+                               arcnet_dump_skb(dev, skb, "rx");
 
                        skb->protocol = type_trans(skb, dev);
                        netif_rx(skb);
@@ -369,14 +373,13 @@ static void rx(struct net_device *dev, int bufnum,
        }
 }
 
-
 /* Create the ARCnet hard/soft headers for RFC1201. */
 static int build_header(struct sk_buff *skb, struct net_device *dev,
                        unsigned short type, uint8_t daddr)
 {
        struct arcnet_local *lp = netdev_priv(dev);
        int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
-       struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
+       struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size);
        struct arc_rfc1201 *soft = &pkt->soft.rfc1201;
 
        /* set the protocol ID according to RFC1201 */
@@ -402,19 +405,18 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
                soft->proto = ARC_P_ATALK;
                break;
        default:
-               BUGMSG(D_NORMAL, "RFC1201: I don't understand protocol %d (%Xh)\n",
-                      type, type);
+               arc_printk(D_NORMAL, dev, "RFC1201: I don't understand protocol %d (%Xh)\n",
+                          type, type);
                dev->stats.tx_errors++;
                dev->stats.tx_aborted_errors++;
                return 0;
        }
 
-       /*
-        * Set the source hardware address.
+       /* Set the source hardware address.
         *
         * This is pretty pointless for most purposes, but it can help in
-        * debugging.  ARCnet does not allow us to change the source address in
-        * the actual packet sent)
+        * debugging.  ARCnet does not allow us to change the source address
+        * in the actual packet sent.
         */
        pkt->hard.source = *dev->dev_addr;
 
@@ -424,10 +426,10 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
        /* see linux/net/ethernet/eth.c to see where I got the following */
 
        if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
-               /* 
-                * FIXME: fill in the last byte of the dest ipaddr here to better
-                * comply with RFC1051 in "noarp" mode.  For now, always broadcasting
-                * will probably at least get packets sent out :)
+               /* FIXME: fill in the last byte of the dest ipaddr here
+                * to better comply with RFC1051 in "noarp" mode.
+                * For now, always broadcasting will probably at least get
+                * packets sent out :)
                 */
                pkt->hard.dest = 0;
                return hdr_size;
@@ -437,7 +439,6 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
        return hdr_size;
 }
 
-
 static void load_pkt(struct net_device *dev, struct arc_hardware *hard,
                     struct arc_rfc1201 *soft, int softlen, int bufnum)
 {
@@ -461,8 +462,9 @@ static void load_pkt(struct net_device *dev, struct arc_hardware *hard,
                hard->offset[1] = ofs - RFC1201_HDR_SIZE;
                lp->hw.copy_to_card(dev, bufnum, ofs - RFC1201_HDR_SIZE,
                                    &excsoft, RFC1201_HDR_SIZE);
-       } else
+       } else {
                hard->offset[0] = ofs = 256 - softlen;
+       }
 
        lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
        lp->hw.copy_to_card(dev, bufnum, ofs, soft, softlen);
@@ -470,7 +472,6 @@ static void load_pkt(struct net_device *dev, struct arc_hardware *hard,
        lp->lastload_dest = hard->dest;
 }
 
-
 static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
                      int bufnum)
 {
@@ -478,11 +479,11 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
        const int maxsegsize = XMTU - RFC1201_HDR_SIZE;
        struct Outgoing *out;
 
+       arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n",
+                  lp->next_tx, lp->cur_tx, bufnum);
 
-       BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
-              lp->next_tx, lp->cur_tx, bufnum);
-
-       length -= ARC_HDR_SIZE; /* hard header is not included in packet length */
+       /* hard header is not included in packet length */
+       length -= ARC_HDR_SIZE;
        pkt->soft.rfc1201.split_flag = 0;
 
        /* need to do a split packet? */
@@ -494,9 +495,9 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
                out->numsegs = (out->dataleft + maxsegsize - 1) / maxsegsize;
                out->segnum = 0;
 
-               BUGMSG(D_DURING, "rfc1201 prep_tx: ready for %d-segment split "
-                      "(%d bytes, seq=%d)\n", out->numsegs, out->length,
-                      pkt->soft.rfc1201.sequence);
+               arc_printk(D_DURING, dev, "rfc1201 prep_tx: ready for %d-segment split (%d bytes, seq=%d)\n",
+                          out->numsegs, out->length,
+                          pkt->soft.rfc1201.sequence);
 
                return 0;       /* not done */
        }
@@ -506,7 +507,6 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
        return 1;               /* done */
 }
 
-
 static int continue_tx(struct net_device *dev, int bufnum)
 {
        struct arcnet_local *lp = netdev_priv(dev);
@@ -516,9 +516,9 @@ static int continue_tx(struct net_device *dev, int bufnum)
        int maxsegsize = XMTU - RFC1201_HDR_SIZE;
        int seglen;
 
-       BUGMSG(D_DURING,
-         "rfc1201 continue_tx: loading segment %d(+1) of %d (seq=%d)\n",
-              out->segnum, out->numsegs, soft->sequence);
+       arc_printk(D_DURING, dev,
+                  "rfc1201 continue_tx: loading segment %d(+1) of %d (seq=%d)\n",
+                  out->segnum, out->numsegs, soft->sequence);
 
        /* the "new" soft header comes right before the data chunk */
        newsoft = (struct arc_rfc1201 *)
index 3c45358844eb94bc8d44c3934e0a5689b9adf994..940e2ebbdea81ef459cffa1128361816f7d309f1 100644 (file)
@@ -127,6 +127,7 @@ static void ad_marker_info_received(struct bond_marker *marker_info,
                                    struct port *port);
 static void ad_marker_response_received(struct bond_marker *marker,
                                        struct port *port);
+static void ad_update_actor_keys(struct port *port, bool reset);
 
 
 /* ================= api to bonding and kernel code ================== */
@@ -327,14 +328,12 @@ static u16 __get_link_speed(struct port *port)
 static u8 __get_duplex(struct port *port)
 {
        struct slave *slave = port->slave;
-       u8 retval;
+       u8 retval = 0x0;
 
        /* handling a special case: when the configuration starts with
         * link down, it sets the duplex to 0.
         */
-       if (slave->link != BOND_LINK_UP) {
-               retval = 0x0;
-       } else {
+       if (slave->link == BOND_LINK_UP) {
                switch (slave->duplex) {
                case DUPLEX_FULL:
                        retval = 0x1;
@@ -1953,14 +1952,7 @@ void bond_3ad_bind_slave(struct slave *slave)
                 * user key
                 */
                port->actor_admin_port_key = bond->params.ad_user_port_key << 6;
-               port->actor_admin_port_key |= __get_duplex(port);
-               port->actor_admin_port_key |= (__get_link_speed(port) << 1);
-               port->actor_oper_port_key = port->actor_admin_port_key;
-               /* if the port is not full duplex, then the port should be not
-                * lacp Enabled
-                */
-               if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS))
-                       port->sm_vars &= ~AD_PORT_LACP_ENABLED;
+               ad_update_actor_keys(port, false);
                /* actor system is the bond's system */
                port->actor_system = BOND_AD_INFO(bond).system.sys_mac_addr;
                port->actor_system_priority =
@@ -2310,45 +2302,60 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave,
 }
 
 /**
- * bond_3ad_adapter_speed_changed - handle a slave's speed change indication
- * @slave: slave struct to work on
+ * ad_update_actor_keys - Update the oper / admin keys for a port based on
+ * its current speed and duplex settings.
  *
- * Handle reselection of aggregator (if needed) for this port.
+ * @port: the port we'are looking at
+ * @reset: Boolean to just reset the speed and the duplex part of the key
+ *
+ * The logic to change the oper / admin keys is:
+ * (a) A full duplex port can participate in LACP with partner.
+ * (b) When the speed is changed, LACP need to be reinitiated.
  */
-void bond_3ad_adapter_speed_changed(struct slave *slave)
+static void ad_update_actor_keys(struct port *port, bool reset)
 {
-       struct port *port;
-
-       port = &(SLAVE_AD_INFO(slave)->port);
-
-       /* if slave is null, the whole port is not initialized */
-       if (!port->slave) {
-               netdev_warn(slave->bond->dev, "speed changed for uninitialized port on %s\n",
-                           slave->dev->name);
-               return;
+       u8 duplex = 0;
+       u16 ospeed = 0, speed = 0;
+       u16 old_oper_key = port->actor_oper_port_key;
+
+       port->actor_admin_port_key &= ~(AD_SPEED_KEY_MASKS|AD_DUPLEX_KEY_MASKS);
+       if (!reset) {
+               speed = __get_link_speed(port);
+               ospeed = (old_oper_key & AD_SPEED_KEY_MASKS) >> 1;
+               duplex = __get_duplex(port);
+               port->actor_admin_port_key |= (speed << 1) | duplex;
        }
-
-       spin_lock_bh(&slave->bond->mode_lock);
-
-       port->actor_admin_port_key &= ~AD_SPEED_KEY_MASKS;
-       port->actor_admin_port_key |= __get_link_speed(port) << 1;
        port->actor_oper_port_key = port->actor_admin_port_key;
-       netdev_dbg(slave->bond->dev, "Port %d changed speed\n", port->actor_port_number);
-       /* there is no need to reselect a new aggregator, just signal the
-        * state machines to reinitialize
-        */
-       port->sm_vars |= AD_PORT_BEGIN;
 
-       spin_unlock_bh(&slave->bond->mode_lock);
+       if (old_oper_key != port->actor_oper_port_key) {
+               /* Only 'duplex' port participates in LACP */
+               if (duplex)
+                       port->sm_vars |= AD_PORT_LACP_ENABLED;
+               else
+                       port->sm_vars &= ~AD_PORT_LACP_ENABLED;
+
+               if (!reset) {
+                       if (!speed) {
+                               netdev_err(port->slave->dev,
+                                          "speed changed to 0 for port %s",
+                                          port->slave->dev->name);
+                       } else if (duplex && ospeed != speed) {
+                               /* Speed change restarts LACP state-machine */
+                               port->sm_vars |= AD_PORT_BEGIN;
+                       }
+               }
+       }
 }
 
 /**
- * bond_3ad_adapter_duplex_changed - handle a slave's duplex change indication
+ * bond_3ad_adapter_speed_duplex_changed - handle a slave's speed / duplex
+ * change indication
+ *
  * @slave: slave struct to work on
  *
  * Handle reselection of aggregator (if needed) for this port.
  */
-void bond_3ad_adapter_duplex_changed(struct slave *slave)
+void bond_3ad_adapter_speed_duplex_changed(struct slave *slave)
 {
        struct port *port;
 
@@ -2356,25 +2363,16 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
 
        /* if slave is null, the whole port is not initialized */
        if (!port->slave) {
-               netdev_warn(slave->bond->dev, "duplex changed for uninitialized port on %s\n",
+               netdev_warn(slave->bond->dev,
+                           "speed/duplex changed for uninitialized port %s\n",
                            slave->dev->name);
                return;
        }
 
        spin_lock_bh(&slave->bond->mode_lock);
-
-       port->actor_admin_port_key &= ~AD_DUPLEX_KEY_MASKS;
-       port->actor_admin_port_key |= __get_duplex(port);
-       port->actor_oper_port_key = port->actor_admin_port_key;
-       netdev_dbg(slave->bond->dev, "Port %d slave %s changed duplex\n",
+       ad_update_actor_keys(port, false);
+       netdev_dbg(slave->bond->dev, "Port %d slave %s changed speed/duplex\n",
                   port->actor_port_number, slave->dev->name);
-       if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS)
-               port->sm_vars |= AD_PORT_LACP_ENABLED;
-       /* there is no need to reselect a new aggregator, just signal the
-        * state machines to reinitialize
-        */
-       port->sm_vars |= AD_PORT_BEGIN;
-
        spin_unlock_bh(&slave->bond->mode_lock);
 }
 
@@ -2406,26 +2404,17 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
         * on link up we are forcing recheck on the duplex and speed since
         * some of he adaptors(ce1000.lan) report.
         */
-       port->actor_admin_port_key &= ~(AD_DUPLEX_KEY_MASKS|AD_SPEED_KEY_MASKS);
        if (link == BOND_LINK_UP) {
                port->is_enabled = true;
-               port->actor_admin_port_key |=
-                       (__get_link_speed(port) << 1) | __get_duplex(port);
-               if (port->actor_admin_port_key & AD_DUPLEX_KEY_MASKS)
-                       port->sm_vars |= AD_PORT_LACP_ENABLED;
+               ad_update_actor_keys(port, false);
        } else {
                /* link has failed */
                port->is_enabled = false;
-               port->sm_vars &= ~AD_PORT_LACP_ENABLED;
+               ad_update_actor_keys(port, true);
        }
-       port->actor_oper_port_key = port->actor_admin_port_key;
        netdev_dbg(slave->bond->dev, "Port %d changed link status to %s\n",
                   port->actor_port_number,
                   link == BOND_LINK_UP ? "UP" : "DOWN");
-       /* there is no need to reselect a new aggregator, just signal the
-        * state machines to reinitialize
-        */
-       port->sm_vars |= AD_PORT_BEGIN;
 
        spin_unlock_bh(&slave->bond->mode_lock);
 
index 771a449d2f56384bae4a5409ffb588dfb40e1f61..b4351caf8e013dbe15fcaf7402e8c9dbaeee9abb 100644 (file)
@@ -1071,7 +1071,7 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
                                 NETIF_F_HIGHDMA | NETIF_F_LRO)
 
 #define BOND_ENC_FEATURES      (NETIF_F_ALL_CSUM | NETIF_F_SG | NETIF_F_RXCSUM |\
-                                NETIF_F_TSO)
+                                NETIF_F_ALL_TSO)
 
 static void bond_compute_features(struct bonding *bond)
 {
@@ -2943,8 +2943,6 @@ static int bond_slave_netdev_event(unsigned long event,
        struct slave *slave = bond_slave_get_rtnl(slave_dev), *primary;
        struct bonding *bond;
        struct net_device *bond_dev;
-       u32 old_speed;
-       u8 old_duplex;
 
        /* A netdev event can be generated while enslaving a device
         * before netdev_rx_handler_register is called in which case
@@ -2965,17 +2963,9 @@ static int bond_slave_netdev_event(unsigned long event,
                break;
        case NETDEV_UP:
        case NETDEV_CHANGE:
-               old_speed = slave->speed;
-               old_duplex = slave->duplex;
-
                bond_update_speed_duplex(slave);
-
-               if (BOND_MODE(bond) == BOND_MODE_8023AD) {
-                       if (old_speed != slave->speed)
-                               bond_3ad_adapter_speed_changed(slave);
-                       if (old_duplex != slave->duplex)
-                               bond_3ad_adapter_duplex_changed(slave);
-               }
+               if (BOND_MODE(bond) == BOND_MODE_8023AD)
+                       bond_3ad_adapter_speed_duplex_changed(slave);
                /* Fallthrough */
        case NETDEV_DOWN:
                /* Refresh slave-array if applicable!
@@ -3136,6 +3126,10 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb)
        struct flow_keys flow;
        u32 hash;
 
+       if (bond->params.xmit_policy == BOND_XMIT_POLICY_ENCAP34 &&
+           skb->l4_hash)
+               return skb->hash;
+
        if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 ||
            !bond_flow_dissect(bond, skb, &flow))
                return bond_eth_hash(skb);
index e8c96b8e86f48e66e68b2cd5285c0766865f067e..6d04183ed95528ee44905193ce4f975007de572f 100644 (file)
@@ -129,6 +129,16 @@ config CAN_RCAR
          To compile this driver as a module, choose M here: the module will
          be called rcar_can.
 
+config CAN_SUN4I
+       tristate "Allwinner A10 CAN controller"
+       depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST
+       ---help---
+         Say Y here if you want to use CAN controller found on Allwinner
+         A10/A20 SoCs.
+
+         To compile this driver as a module, choose M here: the module will
+         be called sun4i_can.
+
 config CAN_XILINXCAN
        tristate "Xilinx CAN"
        depends on ARCH_ZYNQ || ARM64 || MICROBLAZE || COMPILE_TEST
index c533c62b0f5ee7456c152e29f07e302924edb8a1..1f21cef1d458a293c15bcab1fdb9ea6a8f470443 100644 (file)
@@ -27,6 +27,7 @@ obj-$(CONFIG_CAN_FLEXCAN)     += flexcan.o
 obj-$(CONFIG_PCH_CAN)          += pch_can.o
 obj-$(CONFIG_CAN_GRCAN)                += grcan.o
 obj-$(CONFIG_CAN_RCAR)         += rcar_can.o
+obj-$(CONFIG_CAN_SUN4I)                += sun4i_can.o
 obj-$(CONFIG_CAN_XILINXCAN)    += xilinx_can.o
 
 subdir-ccflags-y += -D__CHECK_ENDIAN__
index 945c0955a9675198a8b0945ddc49dd6668380838..8b3275d7792acbab2d0ba9efe9fe3e2a6b283231 100644 (file)
@@ -8,15 +8,6 @@
  * Public License ("GPL") version 2 as distributed in the 'COPYING'
  * file from the main directory of the linux kernel source.
  *
- *
- * Your platform definition file should specify something like:
- *
- * static struct at91_can_data ek_can_data = {
- *     transceiver_switch = sam9263ek_transceiver_switch,
- * };
- *
- * at91_add_device_can(&ek_can_data);
- *
  */
 
 #include <linux/clk.h>
@@ -33,7 +24,6 @@
 #include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/types.h>
-#include <linux/platform_data/atmel.h>
 
 #include <linux/can/dev.h>
 #include <linux/can/error.h>
@@ -324,15 +314,6 @@ static inline u32 at91_can_id_to_reg_mid(canid_t can_id)
        return reg_mid;
 }
 
-/*
- * Swtich transceiver on or off
- */
-static void at91_transceiver_switch(const struct at91_priv *priv, int on)
-{
-       if (priv->pdata && priv->pdata->transceiver_switch)
-               priv->pdata->transceiver_switch(on);
-}
-
 static void at91_setup_mailboxes(struct net_device *dev)
 {
        struct at91_priv *priv = netdev_priv(dev);
@@ -416,7 +397,6 @@ static void at91_chip_start(struct net_device *dev)
 
        at91_set_bittiming(dev);
        at91_setup_mailboxes(dev);
-       at91_transceiver_switch(priv, 1);
 
        /* enable chip */
        if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
@@ -444,7 +424,6 @@ static void at91_chip_stop(struct net_device *dev, enum can_state state)
        reg_mr = at91_read(priv, AT91_MR);
        at91_write(priv, AT91_MR, reg_mr & ~AT91_MR_CANEN);
 
-       at91_transceiver_switch(priv, 0);
        priv->can.state = state;
 }
 
index aede704605c66973c6bc3697a91c90f743990a01..141c2a42d7edad6f9e833d7ab56068d7e2dd71bb 100644 (file)
@@ -915,7 +915,7 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
             nla_put(skb, IFLA_CAN_BITTIMING_CONST,
                     sizeof(*priv->bittiming_const), priv->bittiming_const)) ||
 
-           nla_put(skb, IFLA_CAN_CLOCK, sizeof(cm), &priv->clock) ||
+           nla_put(skb, IFLA_CAN_CLOCK, sizeof(priv->clock), &priv->clock) ||
            nla_put_u32(skb, IFLA_CAN_STATE, state) ||
            nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) ||
            nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) ||
index c83f0f03482ba1d2d9f2b121d2c844af29432f3c..868fe945e35a39786bf15fb8b142c36a720179a5 100644 (file)
 #include <linux/can/led.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
-#include <linux/if_arp.h>
-#include <linux/if_ether.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #define FLEXCAN_MCR_LPRIO_EN           BIT(13)
 #define FLEXCAN_MCR_AEN                        BIT(12)
 #define FLEXCAN_MCR_MAXMB(x)           ((x) & 0x7f)
-#define FLEXCAN_MCR_IDAM_A             (0 << 8)
-#define FLEXCAN_MCR_IDAM_B             (1 << 8)
-#define FLEXCAN_MCR_IDAM_C             (2 << 8)
-#define FLEXCAN_MCR_IDAM_D             (3 << 8)
+#define FLEXCAN_MCR_IDAM_A             (0x0 << 8)
+#define FLEXCAN_MCR_IDAM_B             (0x1 << 8)
+#define FLEXCAN_MCR_IDAM_C             (0x2 << 8)
+#define FLEXCAN_MCR_IDAM_D             (0x3 << 8)
 
 /* FLEXCAN control register (CANCTRL) bits */
 #define FLEXCAN_CTRL_PRESDIV(x)                (((x) & 0xff) << 24)
 #define FLEXCAN_MB_CODE_RX_INACTIVE    (0x0 << 24)
 #define FLEXCAN_MB_CODE_RX_EMPTY       (0x4 << 24)
 #define FLEXCAN_MB_CODE_RX_FULL                (0x2 << 24)
-#define FLEXCAN_MB_CODE_RX_OVERRRUN    (0x6 << 24)
+#define FLEXCAN_MB_CODE_RX_OVERRUN     (0x6 << 24)
 #define FLEXCAN_MB_CODE_RX_RANSWER     (0xa << 24)
 
 #define FLEXCAN_MB_CODE_TX_INACTIVE    (0x8 << 24)
 #define FLEXCAN_MB_CNT_LENGTH(x)       (((x) & 0xf) << 16)
 #define FLEXCAN_MB_CNT_TIMESTAMP(x)    ((x) & 0xffff)
 
-#define FLEXCAN_MB_CODE_MASK           (0xf0ffffff)
+#define FLEXCAN_TIMEOUT_US             (50)
 
-#define FLEXCAN_TIMEOUT_US             (50)
-
-/*
- * FLEXCAN hardware feature flags
+/* FLEXCAN hardware feature flags
  *
  * Below is some version info we got:
  *    SOC   Version   IP-Version  Glitch- [TR]WRN_INT Memory err RTR re-
  *
  * Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected.
  */
-#define FLEXCAN_HAS_V10_FEATURES       BIT(1) /* For core version >= 10 */
-#define FLEXCAN_HAS_BROKEN_ERR_STATE   BIT(2) /* [TR]WRN_INT not connected */
-#define FLEXCAN_HAS_MECR_FEATURES      BIT(3) /* Memory error detection */
+#define FLEXCAN_QUIRK_BROKEN_ERR_STATE BIT(1) /* [TR]WRN_INT not connected */
+#define FLEXCAN_QUIRK_DISABLE_RXFG     BIT(2) /* Disable RX FIFO Global mask */
+#define FLEXCAN_QUIRK_DISABLE_MECR     BIT(3) /* Disble Memory error detection */
 
 /* Structure of the message buffer */
 struct flexcan_mb {
@@ -228,7 +221,7 @@ struct flexcan_regs {
        u32 rxfgmask;           /* 0x48 */
        u32 rxfir;              /* 0x4c */
        u32 _reserved3[12];     /* 0x50 */
-       struct flexcan_mb cantxfg[64];  /* 0x80 */
+       struct flexcan_mb mb[64];       /* 0x80 */
        /* FIFO-mode:
         *                      MB
         * 0x080...0x08f        0       RX message buffer
@@ -236,7 +229,7 @@ struct flexcan_regs {
         * 0x0e0...0x0ff        6-7     8 entry ID table
         *                              (mx25, mx28, mx35, mx53)
         * 0x0e0...0x2df        6-7..37 8..128 entry ID table
-        *                              size conf'ed via ctrl2::RFFN
+        *                              size conf'ed via ctrl2::RFFN
         *                              (mx6, vf610)
         */
        u32 _reserved4[408];
@@ -251,14 +244,14 @@ struct flexcan_regs {
 };
 
 struct flexcan_devtype_data {
-       u32 features;   /* hardware controller features */
+       u32 quirks;             /* quirks needed for different IP cores */
 };
 
 struct flexcan_priv {
        struct can_priv can;
        struct napi_struct napi;
 
-       void __iomem *base;
+       struct flexcan_regs __iomem *regs;
        u32 reg_esr;
        u32 reg_ctrl_default;
 
@@ -270,14 +263,17 @@ struct flexcan_priv {
 };
 
 static struct flexcan_devtype_data fsl_p1010_devtype_data = {
-       .features = FLEXCAN_HAS_BROKEN_ERR_STATE,
+       .quirks = FLEXCAN_QUIRK_BROKEN_ERR_STATE,
 };
+
 static struct flexcan_devtype_data fsl_imx28_devtype_data;
+
 static struct flexcan_devtype_data fsl_imx6q_devtype_data = {
-       .features = FLEXCAN_HAS_V10_FEATURES,
+       .quirks = FLEXCAN_QUIRK_DISABLE_RXFG,
 };
+
 static struct flexcan_devtype_data fsl_vf610_devtype_data = {
-       .features = FLEXCAN_HAS_V10_FEATURES | FLEXCAN_HAS_MECR_FEATURES,
+       .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_DISABLE_MECR,
 };
 
 static const struct can_bittiming_const flexcan_bittiming_const = {
@@ -292,11 +288,10 @@ static const struct can_bittiming_const flexcan_bittiming_const = {
        .brp_inc = 1,
 };
 
-/*
- * Abstract off the read/write for arm versus ppc. This
+/* Abstract off the read/write for arm versus ppc. This
  * assumes that PPC uses big-endian registers and everything
  * else uses little-endian registers, independent of CPU
- * endianess.
+ * endianness.
  */
 #if defined(CONFIG_PPC)
 static inline u32 flexcan_read(void __iomem *addr)
@@ -345,7 +340,7 @@ static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv,
 
 static int flexcan_chip_enable(struct flexcan_priv *priv)
 {
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
        u32 reg;
 
@@ -364,7 +359,7 @@ static int flexcan_chip_enable(struct flexcan_priv *priv)
 
 static int flexcan_chip_disable(struct flexcan_priv *priv)
 {
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
        u32 reg;
 
@@ -383,7 +378,7 @@ static int flexcan_chip_disable(struct flexcan_priv *priv)
 
 static int flexcan_chip_freeze(struct flexcan_priv *priv)
 {
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        unsigned int timeout = 1000 * 1000 * 10 / priv->can.bittiming.bitrate;
        u32 reg;
 
@@ -402,7 +397,7 @@ static int flexcan_chip_freeze(struct flexcan_priv *priv)
 
 static int flexcan_chip_unfreeze(struct flexcan_priv *priv)
 {
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
        u32 reg;
 
@@ -421,7 +416,7 @@ static int flexcan_chip_unfreeze(struct flexcan_priv *priv)
 
 static int flexcan_chip_softreset(struct flexcan_priv *priv)
 {
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
 
        flexcan_write(FLEXCAN_MCR_SOFTRST, &regs->mcr);
@@ -434,12 +429,11 @@ static int flexcan_chip_softreset(struct flexcan_priv *priv)
        return 0;
 }
 
-
 static int __flexcan_get_berr_counter(const struct net_device *dev,
                                      struct can_berr_counter *bec)
 {
        const struct flexcan_priv *priv = netdev_priv(dev);
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        u32 reg = flexcan_read(&regs->ecr);
 
        bec->txerr = (reg >> 0) & 0xff;
@@ -474,9 +468,10 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
 static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        const struct flexcan_priv *priv = netdev_priv(dev);
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        struct can_frame *cf = (struct can_frame *)skb->data;
        u32 can_id;
+       u32 data;
        u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | (cf->can_dlc << 16);
 
        if (can_dropped_invalid_skb(dev, skb))
@@ -495,26 +490,26 @@ static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
                ctrl |= FLEXCAN_MB_CNT_RTR;
 
        if (cf->can_dlc > 0) {
-               u32 data = be32_to_cpup((__be32 *)&cf->data[0]);
-               flexcan_write(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[0]);
+               data = be32_to_cpup((__be32 *)&cf->data[0]);
+               flexcan_write(data, &regs->mb[FLEXCAN_TX_BUF_ID].data[0]);
        }
        if (cf->can_dlc > 3) {
-               u32 data = be32_to_cpup((__be32 *)&cf->data[4]);
-               flexcan_write(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[1]);
+               data = be32_to_cpup((__be32 *)&cf->data[4]);
+               flexcan_write(data, &regs->mb[FLEXCAN_TX_BUF_ID].data[1]);
        }
 
        can_put_echo_skb(skb, dev, 0);
 
-       flexcan_write(can_id, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_id);
-       flexcan_write(ctrl, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
+       flexcan_write(can_id, &regs->mb[FLEXCAN_TX_BUF_ID].can_id);
+       flexcan_write(ctrl, &regs->mb[FLEXCAN_TX_BUF_ID].can_ctrl);
 
        /* Errata ERR005829 step8:
         * Write twice INACTIVE(0x8) code to first MB.
         */
        flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
-                     &regs->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
+                     &regs->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
        flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
-                     &regs->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
+                     &regs->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
 
        return NETDEV_TX_OK;
 }
@@ -597,14 +592,14 @@ static int flexcan_poll_state(struct net_device *dev, u32 reg_esr)
        flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK;
        if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) {
                tx_state = unlikely(reg_esr & FLEXCAN_ESR_TX_WRN) ?
-                          CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
+                       CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
                rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ?
-                          CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
+                       CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
                new_state = max(tx_state, rx_state);
        } else {
                __flexcan_get_berr_counter(dev, &bec);
                new_state = flt == FLEXCAN_ESR_FLT_CONF_PASSIVE ?
-                           CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF;
+                       CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF;
                rx_state = bec.rxerr >= bec.txerr ? new_state : 0;
                tx_state = bec.rxerr <= bec.txerr ? new_state : 0;
        }
@@ -633,8 +628,8 @@ static void flexcan_read_fifo(const struct net_device *dev,
                              struct can_frame *cf)
 {
        const struct flexcan_priv *priv = netdev_priv(dev);
-       struct flexcan_regs __iomem *regs = priv->base;
-       struct flexcan_mb __iomem *mb = &regs->cantxfg[0];
+       struct flexcan_regs __iomem *regs = priv->regs;
+       struct flexcan_mb __iomem *mb = &regs->mb[0];
        u32 reg_ctrl, reg_id;
 
        reg_ctrl = flexcan_read(&mb->can_ctrl);
@@ -683,12 +678,11 @@ static int flexcan_poll(struct napi_struct *napi, int quota)
 {
        struct net_device *dev = napi->dev;
        const struct flexcan_priv *priv = netdev_priv(dev);
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        u32 reg_iflag1, reg_esr;
        int work_done = 0;
 
-       /*
-        * The error bits are cleared on read,
+       /* The error bits are cleared on read,
         * use saved value from irq handler.
         */
        reg_esr = flexcan_read(&regs->esr) | priv->reg_esr;
@@ -723,17 +717,17 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
        struct net_device *dev = dev_id;
        struct net_device_stats *stats = &dev->stats;
        struct flexcan_priv *priv = netdev_priv(dev);
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        u32 reg_iflag1, reg_esr;
 
        reg_iflag1 = flexcan_read(&regs->iflag1);
        reg_esr = flexcan_read(&regs->esr);
+
        /* ACK all bus error and state change IRQ sources */
        if (reg_esr & FLEXCAN_ESR_ALL_INT)
                flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, &regs->esr);
 
-       /*
-        * schedule NAPI in case of:
+       /* schedule NAPI in case of:
         * - rx IRQ
         * - state change IRQ
         * - bus error IRQ and bus error reporting is activated
@@ -741,15 +735,14 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
        if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) ||
            (reg_esr & FLEXCAN_ESR_ERR_STATE) ||
            flexcan_has_and_handle_berr(priv, reg_esr)) {
-               /*
-                * The error bits are cleared on read,
+               /* The error bits are cleared on read,
                 * save them for later use.
                 */
                priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS;
                flexcan_write(FLEXCAN_IFLAG_DEFAULT &
-                       ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->imask1);
+                             ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->imask1);
                flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
-                      &regs->ctrl);
+                             &regs->ctrl);
                napi_schedule(&priv->napi);
        }
 
@@ -765,9 +758,10 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
                stats->tx_bytes += can_get_echo_skb(dev, 0);
                stats->tx_packets++;
                can_led_event(dev, CAN_LED_EVENT_TX);
-               /* after sending a RTR frame mailbox is in RX mode */
+
+               /* after sending a RTR frame MB is in RX mode */
                flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
-                             &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
+                             &regs->mb[FLEXCAN_TX_BUF_ID].can_ctrl);
                flexcan_write((1 << FLEXCAN_TX_BUF_ID), &regs->iflag1);
                netif_wake_queue(dev);
        }
@@ -779,7 +773,7 @@ static void flexcan_set_bittiming(struct net_device *dev)
 {
        const struct flexcan_priv *priv = netdev_priv(dev);
        const struct can_bittiming *bt = &priv->can.bittiming;
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        u32 reg;
 
        reg = flexcan_read(&regs->ctrl);
@@ -813,8 +807,7 @@ static void flexcan_set_bittiming(struct net_device *dev)
                   flexcan_read(&regs->mcr), flexcan_read(&regs->ctrl));
 }
 
-/*
- * flexcan_chip_start
+/* flexcan_chip_start
  *
  * this functions is entered with clocks enabled
  *
@@ -822,7 +815,7 @@ static void flexcan_set_bittiming(struct net_device *dev)
 static int flexcan_chip_start(struct net_device *dev)
 {
        struct flexcan_priv *priv = netdev_priv(dev);
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr;
        int err, i;
 
@@ -838,29 +831,26 @@ static int flexcan_chip_start(struct net_device *dev)
 
        flexcan_set_bittiming(dev);
 
-       /*
-        * MCR
+       /* MCR
         *
         * enable freeze
         * enable fifo
         * halt now
         * only supervisor access
         * enable warning int
-        * choose format C
         * disable local echo
-        *
+        * choose format C
+        * set max mailbox number
         */
        reg_mcr = flexcan_read(&regs->mcr);
        reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
        reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |
-               FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN |
-               FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS |
-               FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);
+               FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS |
+               FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);
        netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
        flexcan_write(reg_mcr, &regs->mcr);
 
-       /*
-        * CTRL
+       /* CTRL
         *
         * disable timer sync feature
         *
@@ -875,12 +865,12 @@ static int flexcan_chip_start(struct net_device *dev)
        reg_ctrl &= ~FLEXCAN_CTRL_TSYN;
        reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF |
                FLEXCAN_CTRL_ERR_STATE;
-       /*
-        * enable the "error interrupt" (FLEXCAN_CTRL_ERR_MSK),
+
+       /* enable the "error interrupt" (FLEXCAN_CTRL_ERR_MSK),
         * on most Flexcan cores, too. Otherwise we don't get
         * any error warning or passive interrupts.
         */
-       if (priv->devtype_data->features & FLEXCAN_HAS_BROKEN_ERR_STATE ||
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_BROKEN_ERR_STATE ||
            priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
                reg_ctrl |= FLEXCAN_CTRL_ERR_MSK;
        else
@@ -888,41 +878,41 @@ static int flexcan_chip_start(struct net_device *dev)
 
        /* save for later use */
        priv->reg_ctrl_default = reg_ctrl;
+       /* leave interrupts disabled for now */
+       reg_ctrl &= ~FLEXCAN_CTRL_ERR_ALL;
        netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl);
        flexcan_write(reg_ctrl, &regs->ctrl);
 
        /* clear and invalidate all mailboxes first */
-       for (i = FLEXCAN_TX_BUF_ID; i < ARRAY_SIZE(regs->cantxfg); i++) {
+       for (i = FLEXCAN_TX_BUF_ID; i < ARRAY_SIZE(regs->mb); i++) {
                flexcan_write(FLEXCAN_MB_CODE_RX_INACTIVE,
-                             &regs->cantxfg[i].can_ctrl);
+                             &regs->mb[i].can_ctrl);
        }
 
        /* Errata ERR005829: mark first TX mailbox as INACTIVE */
        flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
-                     &regs->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
+                     &regs->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
 
        /* mark TX mailbox as INACTIVE */
        flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
-                     &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
+                     &regs->mb[FLEXCAN_TX_BUF_ID].can_ctrl);
 
        /* acceptance mask/acceptance code (accept everything) */
        flexcan_write(0x0, &regs->rxgmask);
        flexcan_write(0x0, &regs->rx14mask);
        flexcan_write(0x0, &regs->rx15mask);
 
-       if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES)
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG)
                flexcan_write(0x0, &regs->rxfgmask);
 
-       /*
-        * On Vybrid, disable memory error detection interrupts
+       /* On Vybrid, disable memory error detection interrupts
         * and freeze mode.
         * This also works around errata e5295 which generates
         * false positive memory errors and put the device in
         * freeze mode.
         */
-       if (priv->devtype_data->features & FLEXCAN_HAS_MECR_FEATURES) {
-               /*
-                * Follow the protocol as described in "Detection
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_MECR) {
+               /* Follow the protocol as described in "Detection
                 * and Correction of Memory Errors" to write to
                 * MECR register
                 */
@@ -934,7 +924,7 @@ static int flexcan_chip_start(struct net_device *dev)
                reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS;
                flexcan_write(reg_mecr, &regs->mecr);
                reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK |
-                               FLEXCAN_MECR_FANCEI_MSK);
+                             FLEXCAN_MECR_FANCEI_MSK);
                flexcan_write(reg_mecr, &regs->mecr);
        }
 
@@ -949,8 +939,11 @@ static int flexcan_chip_start(struct net_device *dev)
 
        priv->can.state = CAN_STATE_ERROR_ACTIVE;
 
-       /* enable FIFO interrupts */
+       /* enable interrupts atomically */
+       disable_irq(dev->irq);
+       flexcan_write(priv->reg_ctrl_default, &regs->ctrl);
        flexcan_write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
+       enable_irq(dev->irq);
 
        /* print chip status */
        netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__,
@@ -965,16 +958,14 @@ static int flexcan_chip_start(struct net_device *dev)
        return err;
 }
 
-/*
- * flexcan_chip_stop
+/* flexcan_chip_stop
  *
  * this functions is entered with clocks enabled
- *
  */
 static void flexcan_chip_stop(struct net_device *dev)
 {
        struct flexcan_priv *priv = netdev_priv(dev);
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
 
        /* freeze + disable module */
        flexcan_chip_freeze(priv);
@@ -987,8 +978,6 @@ static void flexcan_chip_stop(struct net_device *dev)
 
        flexcan_transceiver_disable(priv);
        priv->can.state = CAN_STATE_STOPPED;
-
-       return;
 }
 
 static int flexcan_open(struct net_device *dev)
@@ -1085,7 +1074,7 @@ static const struct net_device_ops flexcan_netdev_ops = {
 static int register_flexcandev(struct net_device *dev)
 {
        struct flexcan_priv *priv = netdev_priv(dev);
-       struct flexcan_regs __iomem *regs = priv->base;
+       struct flexcan_regs __iomem *regs = priv->regs;
        u32 reg, err;
 
        err = clk_prepare_enable(priv->clk_ipg);
@@ -1114,8 +1103,7 @@ static int register_flexcandev(struct net_device *dev)
                FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV;
        flexcan_write(reg, &regs->mcr);
 
-       /*
-        * Currently we only support newer versions of this core
+       /* Currently we only support newer versions of this core
         * featuring a RX FIFO. Older cores found on some Coldfire
         * derivates are not yet supported.
         */
@@ -1168,7 +1156,7 @@ static int flexcan_probe(struct platform_device *pdev)
        struct regulator *reg_xceiver;
        struct resource *mem;
        struct clk *clk_ipg = NULL, *clk_per = NULL;
-       void __iomem *base;
+       struct flexcan_regs __iomem *regs;
        int err, irq;
        u32 clock_freq = 0;
 
@@ -1180,7 +1168,7 @@ static int flexcan_probe(struct platform_device *pdev)
 
        if (pdev->dev.of_node)
                of_property_read_u32(pdev->dev.of_node,
-                                               "clock-frequency", &clock_freq);
+                                    "clock-frequency", &clock_freq);
 
        if (!clock_freq) {
                clk_ipg = devm_clk_get(&pdev->dev, "ipg");
@@ -1202,9 +1190,9 @@ static int flexcan_probe(struct platform_device *pdev)
        if (irq <= 0)
                return -ENODEV;
 
-       base = devm_ioremap_resource(&pdev->dev, mem);
-       if (IS_ERR(base))
-               return PTR_ERR(base);
+       regs = devm_ioremap_resource(&pdev->dev, mem);
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
 
        of_id = of_match_device(flexcan_of_match, &pdev->dev);
        if (of_id) {
@@ -1232,12 +1220,11 @@ static int flexcan_probe(struct platform_device *pdev)
        priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
                CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_3_SAMPLES |
                CAN_CTRLMODE_BERR_REPORTING;
-       priv->base = base;
+       priv->regs = regs;
        priv->clk_ipg = clk_ipg;
        priv->clk_per = clk_per;
        priv->pdata = dev_get_platdata(&pdev->dev);
        priv->devtype_data = devtype_data;
-
        priv->reg_xceiver = reg_xceiver;
 
        netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT);
@@ -1254,7 +1241,7 @@ static int flexcan_probe(struct platform_device *pdev)
        devm_can_led_init(dev);
 
        dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n",
-                priv->base, dev->irq);
+                priv->regs, dev->irq);
 
        return 0;
 
diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c
new file mode 100644 (file)
index 0000000..d9a42c6
--- /dev/null
@@ -0,0 +1,857 @@
+/*
+ * sun4i_can.c - CAN bus controller driver for Allwinner SUN4I&SUN7I based SoCs
+ *
+ * Copyright (C) 2013 Peter Chen
+ * Copyright (C) 2015 Gerhard Bertelsmann
+ * All rights reserved.
+ *
+ * Parts of this software are based on (derived from) the SJA1000 code by:
+ *   Copyright (C) 2014 Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ *   Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *   Copyright (C) 2002-2007 Volkswagen Group Electronic Research
+ *   Copyright (C) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ *   38106 Braunschweig, GERMANY
+ *
+ * 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 Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER 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/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/led.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#define DRV_NAME "sun4i_can"
+
+/* Registers address (physical base address 0x01C2BC00) */
+#define SUN4I_REG_MSEL_ADDR    0x0000  /* CAN Mode Select */
+#define SUN4I_REG_CMD_ADDR     0x0004  /* CAN Command */
+#define SUN4I_REG_STA_ADDR     0x0008  /* CAN Status */
+#define SUN4I_REG_INT_ADDR     0x000c  /* CAN Interrupt Flag */
+#define SUN4I_REG_INTEN_ADDR   0x0010  /* CAN Interrupt Enable */
+#define SUN4I_REG_BTIME_ADDR   0x0014  /* CAN Bus Timing 0 */
+#define SUN4I_REG_TEWL_ADDR    0x0018  /* CAN Tx Error Warning Limit */
+#define SUN4I_REG_ERRC_ADDR    0x001c  /* CAN Error Counter */
+#define SUN4I_REG_RMCNT_ADDR   0x0020  /* CAN Receive Message Counter */
+#define SUN4I_REG_RBUFSA_ADDR  0x0024  /* CAN Receive Buffer Start Address */
+#define SUN4I_REG_BUF0_ADDR    0x0040  /* CAN Tx/Rx Buffer 0 */
+#define SUN4I_REG_BUF1_ADDR    0x0044  /* CAN Tx/Rx Buffer 1 */
+#define SUN4I_REG_BUF2_ADDR    0x0048  /* CAN Tx/Rx Buffer 2 */
+#define SUN4I_REG_BUF3_ADDR    0x004c  /* CAN Tx/Rx Buffer 3 */
+#define SUN4I_REG_BUF4_ADDR    0x0050  /* CAN Tx/Rx Buffer 4 */
+#define SUN4I_REG_BUF5_ADDR    0x0054  /* CAN Tx/Rx Buffer 5 */
+#define SUN4I_REG_BUF6_ADDR    0x0058  /* CAN Tx/Rx Buffer 6 */
+#define SUN4I_REG_BUF7_ADDR    0x005c  /* CAN Tx/Rx Buffer 7 */
+#define SUN4I_REG_BUF8_ADDR    0x0060  /* CAN Tx/Rx Buffer 8 */
+#define SUN4I_REG_BUF9_ADDR    0x0064  /* CAN Tx/Rx Buffer 9 */
+#define SUN4I_REG_BUF10_ADDR   0x0068  /* CAN Tx/Rx Buffer 10 */
+#define SUN4I_REG_BUF11_ADDR   0x006c  /* CAN Tx/Rx Buffer 11 */
+#define SUN4I_REG_BUF12_ADDR   0x0070  /* CAN Tx/Rx Buffer 12 */
+#define SUN4I_REG_ACPC_ADDR    0x0040  /* CAN Acceptance Code 0 */
+#define SUN4I_REG_ACPM_ADDR    0x0044  /* CAN Acceptance Mask 0 */
+#define SUN4I_REG_RBUF_RBACK_START_ADDR        0x0180  /* CAN transmit buffer start */
+#define SUN4I_REG_RBUF_RBACK_END_ADDR  0x01b0  /* CAN transmit buffer end */
+
+/* Controller Register Description */
+
+/* mode select register (r/w)
+ * offset:0x0000 default:0x0000_0001
+ */
+#define SUN4I_MSEL_SLEEP_MODE          (0x01 << 4) /* write in reset mode */
+#define SUN4I_MSEL_WAKE_UP             (0x00 << 4)
+#define SUN4I_MSEL_SINGLE_FILTER       (0x01 << 3) /* write in reset mode */
+#define SUN4I_MSEL_DUAL_FILTERS                (0x00 << 3)
+#define SUN4I_MSEL_LOOPBACK_MODE       BIT(2)
+#define SUN4I_MSEL_LISTEN_ONLY_MODE    BIT(1)
+#define SUN4I_MSEL_RESET_MODE          BIT(0)
+
+/* command register (w)
+ * offset:0x0004 default:0x0000_0000
+ */
+#define SUN4I_CMD_BUS_OFF_REQ  BIT(5)
+#define SUN4I_CMD_SELF_RCV_REQ BIT(4)
+#define SUN4I_CMD_CLEAR_OR_FLAG        BIT(3)
+#define SUN4I_CMD_RELEASE_RBUF BIT(2)
+#define SUN4I_CMD_ABORT_REQ    BIT(1)
+#define SUN4I_CMD_TRANS_REQ    BIT(0)
+
+/* status register (r)
+ * offset:0x0008 default:0x0000_003c
+ */
+#define SUN4I_STA_BIT_ERR      (0x00 << 22)
+#define SUN4I_STA_FORM_ERR     (0x01 << 22)
+#define SUN4I_STA_STUFF_ERR    (0x02 << 22)
+#define SUN4I_STA_OTHER_ERR    (0x03 << 22)
+#define SUN4I_STA_MASK_ERR     (0x03 << 22)
+#define SUN4I_STA_ERR_DIR      BIT(21)
+#define SUN4I_STA_ERR_SEG_CODE (0x1f << 16)
+#define SUN4I_STA_START                (0x03 << 16)
+#define SUN4I_STA_ID28_21      (0x02 << 16)
+#define SUN4I_STA_ID20_18      (0x06 << 16)
+#define SUN4I_STA_SRTR         (0x04 << 16)
+#define SUN4I_STA_IDE          (0x05 << 16)
+#define SUN4I_STA_ID17_13      (0x07 << 16)
+#define SUN4I_STA_ID12_5       (0x0f << 16)
+#define SUN4I_STA_ID4_0                (0x0e << 16)
+#define SUN4I_STA_RTR          (0x0c << 16)
+#define SUN4I_STA_RB1          (0x0d << 16)
+#define SUN4I_STA_RB0          (0x09 << 16)
+#define SUN4I_STA_DLEN         (0x0b << 16)
+#define SUN4I_STA_DATA_FIELD   (0x0a << 16)
+#define SUN4I_STA_CRC_SEQUENCE (0x08 << 16)
+#define SUN4I_STA_CRC_DELIMITER        (0x18 << 16)
+#define SUN4I_STA_ACK          (0x19 << 16)
+#define SUN4I_STA_ACK_DELIMITER        (0x1b << 16)
+#define SUN4I_STA_END          (0x1a << 16)
+#define SUN4I_STA_INTERMISSION (0x12 << 16)
+#define SUN4I_STA_ACTIVE_ERROR (0x11 << 16)
+#define SUN4I_STA_PASSIVE_ERROR        (0x16 << 16)
+#define SUN4I_STA_TOLERATE_DOMINANT_BITS       (0x13 << 16)
+#define SUN4I_STA_ERROR_DELIMITER      (0x17 << 16)
+#define SUN4I_STA_OVERLOAD     (0x1c << 16)
+#define SUN4I_STA_BUS_OFF      BIT(7)
+#define SUN4I_STA_ERR_STA      BIT(6)
+#define SUN4I_STA_TRANS_BUSY   BIT(5)
+#define SUN4I_STA_RCV_BUSY     BIT(4)
+#define SUN4I_STA_TRANS_OVER   BIT(3)
+#define SUN4I_STA_TBUF_RDY     BIT(2)
+#define SUN4I_STA_DATA_ORUN    BIT(1)
+#define SUN4I_STA_RBUF_RDY     BIT(0)
+
+/* interrupt register (r)
+ * offset:0x000c default:0x0000_0000
+ */
+#define SUN4I_INT_BUS_ERR      BIT(7)
+#define SUN4I_INT_ARB_LOST     BIT(6)
+#define SUN4I_INT_ERR_PASSIVE  BIT(5)
+#define SUN4I_INT_WAKEUP       BIT(4)
+#define SUN4I_INT_DATA_OR      BIT(3)
+#define SUN4I_INT_ERR_WRN      BIT(2)
+#define SUN4I_INT_TBUF_VLD     BIT(1)
+#define SUN4I_INT_RBUF_VLD     BIT(0)
+
+/* interrupt enable register (r/w)
+ * offset:0x0010 default:0x0000_0000
+ */
+#define SUN4I_INTEN_BERR       BIT(7)
+#define SUN4I_INTEN_ARB_LOST   BIT(6)
+#define SUN4I_INTEN_ERR_PASSIVE        BIT(5)
+#define SUN4I_INTEN_WAKEUP     BIT(4)
+#define SUN4I_INTEN_OR         BIT(3)
+#define SUN4I_INTEN_ERR_WRN    BIT(2)
+#define SUN4I_INTEN_TX         BIT(1)
+#define SUN4I_INTEN_RX         BIT(0)
+
+/* error code */
+#define SUN4I_ERR_INRCV                (0x1 << 5)
+#define SUN4I_ERR_INTRANS      (0x0 << 5)
+
+/* filter mode */
+#define SUN4I_FILTER_CLOSE     0
+#define SUN4I_SINGLE_FLTER_MODE        1
+#define SUN4I_DUAL_FILTER_MODE 2
+
+/* message buffer flags */
+#define SUN4I_MSG_EFF_FLAG     BIT(7)
+#define SUN4I_MSG_RTR_FLAG     BIT(6)
+
+/* max. number of interrupts handled in ISR */
+#define SUN4I_CAN_MAX_IRQ      20
+#define SUN4I_MODE_MAX_RETRIES 100
+
+struct sun4ican_priv {
+       struct can_priv can;
+       void __iomem *base;
+       struct clk *clk;
+       spinlock_t cmdreg_lock; /* lock for concurrent cmd register writes */
+};
+
+static const struct can_bittiming_const sun4ican_bittiming_const = {
+       .name = DRV_NAME,
+       .tseg1_min = 1,
+       .tseg1_max = 16,
+       .tseg2_min = 1,
+       .tseg2_max = 8,
+       .sjw_max = 4,
+       .brp_min = 1,
+       .brp_max = 64,
+       .brp_inc = 1,
+};
+
+static void sun4i_can_write_cmdreg(struct sun4ican_priv *priv, u8 val)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->cmdreg_lock, flags);
+       writel(val, priv->base + SUN4I_REG_CMD_ADDR);
+       spin_unlock_irqrestore(&priv->cmdreg_lock, flags);
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+       struct sun4ican_priv *priv = netdev_priv(dev);
+       int retry = SUN4I_MODE_MAX_RETRIES;
+       u32 mod_reg_val = 0;
+
+       do {
+               mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR);
+               mod_reg_val &= ~SUN4I_MSEL_RESET_MODE;
+               writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR);
+       } while (retry-- && (mod_reg_val & SUN4I_MSEL_RESET_MODE));
+
+       if (readl(priv->base + SUN4I_REG_MSEL_ADDR) & SUN4I_MSEL_RESET_MODE) {
+               netdev_err(dev,
+                          "setting controller into normal mode failed!\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int set_reset_mode(struct net_device *dev)
+{
+       struct sun4ican_priv *priv = netdev_priv(dev);
+       int retry = SUN4I_MODE_MAX_RETRIES;
+       u32 mod_reg_val = 0;
+
+       do {
+               mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR);
+               mod_reg_val |= SUN4I_MSEL_RESET_MODE;
+               writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR);
+       } while (retry-- && !(mod_reg_val & SUN4I_MSEL_RESET_MODE));
+
+       if (!(readl(priv->base + SUN4I_REG_MSEL_ADDR) &
+             SUN4I_MSEL_RESET_MODE)) {
+               netdev_err(dev, "setting controller into reset mode failed!\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+/* bittiming is called in reset_mode only */
+static int sun4ican_set_bittiming(struct net_device *dev)
+{
+       struct sun4ican_priv *priv = netdev_priv(dev);
+       struct can_bittiming *bt = &priv->can.bittiming;
+       u32 cfg;
+
+       cfg = ((bt->brp - 1) & 0x3FF) |
+            (((bt->sjw - 1) & 0x3) << 14) |
+            (((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) << 16) |
+            (((bt->phase_seg2 - 1) & 0x7) << 20);
+       if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+               cfg |= 0x800000;
+
+       netdev_dbg(dev, "setting BITTIMING=0x%08x\n", cfg);
+       writel(cfg, priv->base + SUN4I_REG_BTIME_ADDR);
+
+       return 0;
+}
+
+static int sun4ican_get_berr_counter(const struct net_device *dev,
+                                    struct can_berr_counter *bec)
+{
+       struct sun4ican_priv *priv = netdev_priv(dev);
+       u32 errors;
+       int err;
+
+       err = clk_prepare_enable(priv->clk);
+       if (err) {
+               netdev_err(dev, "could not enable clock\n");
+               return err;
+       }
+
+       errors = readl(priv->base + SUN4I_REG_ERRC_ADDR);
+
+       bec->txerr = errors & 0xFF;
+       bec->rxerr = (errors >> 16) & 0xFF;
+
+       clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static int sun4i_can_start(struct net_device *dev)
+{
+       struct sun4ican_priv *priv = netdev_priv(dev);
+       int err;
+       u32 mod_reg_val;
+
+       /* we need to enter the reset mode */
+       err = set_reset_mode(dev);
+       if (err) {
+               netdev_err(dev, "could not enter reset mode\n");
+               return err;
+       }
+
+       /* set filters - we accept all */
+       writel(0x00000000, priv->base + SUN4I_REG_ACPC_ADDR);
+       writel(0xFFFFFFFF, priv->base + SUN4I_REG_ACPM_ADDR);
+
+       /* clear error counters and error code capture */
+       writel(0, priv->base + SUN4I_REG_ERRC_ADDR);
+
+       /* enable interrupts */
+       if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+               writel(0xFF, priv->base + SUN4I_REG_INTEN_ADDR);
+       else
+               writel(0xFF & ~SUN4I_INTEN_BERR,
+                      priv->base + SUN4I_REG_INTEN_ADDR);
+
+       /* enter the selected mode */
+       mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR);
+       if (priv->can.ctrlmode & CAN_CTRLMODE_PRESUME_ACK)
+               mod_reg_val |= SUN4I_MSEL_LOOPBACK_MODE;
+       else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+               mod_reg_val |= SUN4I_MSEL_LISTEN_ONLY_MODE;
+       writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR);
+
+       err = sun4ican_set_bittiming(dev);
+       if (err)
+               return err;
+
+       /* we are ready to enter the normal mode */
+       err = set_normal_mode(dev);
+       if (err) {
+               netdev_err(dev, "could not enter normal mode\n");
+               return err;
+       }
+
+       priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+       return 0;
+}
+
+static int sun4i_can_stop(struct net_device *dev)
+{
+       struct sun4ican_priv *priv = netdev_priv(dev);
+       int err;
+
+       priv->can.state = CAN_STATE_STOPPED;
+       /* we need to enter reset mode */
+       err = set_reset_mode(dev);
+       if (err) {
+               netdev_err(dev, "could not enter reset mode\n");
+               return err;
+       }
+
+       /* disable all interrupts */
+       writel(0, priv->base + SUN4I_REG_INTEN_ADDR);
+
+       return 0;
+}
+
+static int sun4ican_set_mode(struct net_device *dev, enum can_mode mode)
+{
+       int err;
+
+       switch (mode) {
+       case CAN_MODE_START:
+               err = sun4i_can_start(dev);
+               if (err) {
+                       netdev_err(dev, "starting CAN controller failed!\n");
+                       return err;
+               }
+               if (netif_queue_stopped(dev))
+                       netif_wake_queue(dev);
+               break;
+
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+/* transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx         ff         ll 00 11 22 33 44 55 66 77
+ * [ can_id ] [flags] [len] [can data (up to 8 bytes]
+ */
+static int sun4ican_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct sun4ican_priv *priv = netdev_priv(dev);
+       struct can_frame *cf = (struct can_frame *)skb->data;
+       u8 dlc;
+       u32 dreg, msg_flag_n;
+       canid_t id;
+       int i;
+
+       if (can_dropped_invalid_skb(dev, skb))
+               return NETDEV_TX_OK;
+
+       netif_stop_queue(dev);
+
+       id = cf->can_id;
+       dlc = cf->can_dlc;
+       msg_flag_n = dlc;
+
+       if (id & CAN_RTR_FLAG)
+               msg_flag_n |= SUN4I_MSG_RTR_FLAG;
+
+       if (id & CAN_EFF_FLAG) {
+               msg_flag_n |= SUN4I_MSG_EFF_FLAG;
+               dreg = SUN4I_REG_BUF5_ADDR;
+               writel((id >> 21) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR);
+               writel((id >> 13) & 0xFF, priv->base + SUN4I_REG_BUF2_ADDR);
+               writel((id >> 5)  & 0xFF, priv->base + SUN4I_REG_BUF3_ADDR);
+               writel((id << 3)  & 0xF8, priv->base + SUN4I_REG_BUF4_ADDR);
+       } else {
+               dreg = SUN4I_REG_BUF3_ADDR;
+               writel((id >> 3) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR);
+               writel((id << 5) & 0xE0, priv->base + SUN4I_REG_BUF2_ADDR);
+       }
+
+       for (i = 0; i < dlc; i++)
+               writel(cf->data[i], priv->base + (dreg + i * 4));
+
+       writel(msg_flag_n, priv->base + SUN4I_REG_BUF0_ADDR);
+
+       can_put_echo_skb(skb, dev, 0);
+
+       if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+               sun4i_can_write_cmdreg(priv, SUN4I_CMD_SELF_RCV_REQ);
+       else
+               sun4i_can_write_cmdreg(priv, SUN4I_CMD_TRANS_REQ);
+
+       return NETDEV_TX_OK;
+}
+
+static void sun4i_can_rx(struct net_device *dev)
+{
+       struct sun4ican_priv *priv = netdev_priv(dev);
+       struct net_device_stats *stats = &dev->stats;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       u8 fi;
+       u32 dreg;
+       canid_t id;
+       int i;
+
+       /* create zero'ed CAN frame buffer */
+       skb = alloc_can_skb(dev, &cf);
+       if (!skb)
+               return;
+
+       fi = readl(priv->base + SUN4I_REG_BUF0_ADDR);
+       cf->can_dlc = get_can_dlc(fi & 0x0F);
+       if (fi & SUN4I_MSG_EFF_FLAG) {
+               dreg = SUN4I_REG_BUF5_ADDR;
+               id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 21) |
+                    (readl(priv->base + SUN4I_REG_BUF2_ADDR) << 13) |
+                    (readl(priv->base + SUN4I_REG_BUF3_ADDR) << 5)  |
+                   ((readl(priv->base + SUN4I_REG_BUF4_ADDR) >> 3)  & 0x1f);
+               id |= CAN_EFF_FLAG;
+       } else {
+               dreg = SUN4I_REG_BUF3_ADDR;
+               id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 3) |
+                   ((readl(priv->base + SUN4I_REG_BUF2_ADDR) >> 5) & 0x7);
+       }
+
+       /* remote frame ? */
+       if (fi & SUN4I_MSG_RTR_FLAG)
+               id |= CAN_RTR_FLAG;
+       else
+               for (i = 0; i < cf->can_dlc; i++)
+                       cf->data[i] = readl(priv->base + dreg + i * 4);
+
+       cf->can_id = id;
+
+       sun4i_can_write_cmdreg(priv, SUN4I_CMD_RELEASE_RBUF);
+
+       stats->rx_packets++;
+       stats->rx_bytes += cf->can_dlc;
+       netif_rx(skb);
+
+       can_led_event(dev, CAN_LED_EVENT_RX);
+}
+
+static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status)
+{
+       struct sun4ican_priv *priv = netdev_priv(dev);
+       struct net_device_stats *stats = &dev->stats;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       enum can_state state = priv->can.state;
+       enum can_state rx_state, tx_state;
+       unsigned int rxerr, txerr, errc;
+       u32 ecc, alc;
+
+       /* we don't skip if alloc fails because we want the stats anyhow */
+       skb = alloc_can_err_skb(dev, &cf);
+
+       errc = readl(priv->base + SUN4I_REG_ERRC_ADDR);
+       rxerr = (errc >> 16) & 0xFF;
+       txerr = errc & 0xFF;
+
+       if (skb) {
+               cf->data[6] = txerr;
+               cf->data[7] = rxerr;
+       }
+
+       if (isrc & SUN4I_INT_DATA_OR) {
+               /* data overrun interrupt */
+               netdev_dbg(dev, "data overrun interrupt\n");
+               if (likely(skb)) {
+                       cf->can_id |= CAN_ERR_CRTL;
+                       cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+               }
+               stats->rx_over_errors++;
+               stats->rx_errors++;
+               /* clear bit */
+               sun4i_can_write_cmdreg(priv, SUN4I_CMD_CLEAR_OR_FLAG);
+       }
+       if (isrc & SUN4I_INT_ERR_WRN) {
+               /* error warning interrupt */
+               netdev_dbg(dev, "error warning interrupt\n");
+
+               if (status & SUN4I_STA_BUS_OFF)
+                       state = CAN_STATE_BUS_OFF;
+               else if (status & SUN4I_STA_ERR_STA)
+                       state = CAN_STATE_ERROR_WARNING;
+               else
+                       state = CAN_STATE_ERROR_ACTIVE;
+       }
+       if (isrc & SUN4I_INT_BUS_ERR) {
+               /* bus error interrupt */
+               netdev_dbg(dev, "bus error interrupt\n");
+               priv->can.can_stats.bus_error++;
+               stats->rx_errors++;
+
+               if (likely(skb)) {
+                       ecc = readl(priv->base + SUN4I_REG_STA_ADDR);
+
+                       cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+                       switch (ecc & SUN4I_STA_MASK_ERR) {
+                       case SUN4I_STA_BIT_ERR:
+                               cf->data[2] |= CAN_ERR_PROT_BIT;
+                               break;
+                       case SUN4I_STA_FORM_ERR:
+                               cf->data[2] |= CAN_ERR_PROT_FORM;
+                               break;
+                       case SUN4I_STA_STUFF_ERR:
+                               cf->data[2] |= CAN_ERR_PROT_STUFF;
+                               break;
+                       default:
+                               cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+                               cf->data[3] = (ecc & SUN4I_STA_ERR_SEG_CODE)
+                                              >> 16;
+                               break;
+                       }
+                       /* error occurred during transmission? */
+                       if ((ecc & SUN4I_STA_ERR_DIR) == 0)
+                               cf->data[2] |= CAN_ERR_PROT_TX;
+               }
+       }
+       if (isrc & SUN4I_INT_ERR_PASSIVE) {
+               /* error passive interrupt */
+               netdev_dbg(dev, "error passive interrupt\n");
+               if (state == CAN_STATE_ERROR_PASSIVE)
+                       state = CAN_STATE_ERROR_WARNING;
+               else
+                       state = CAN_STATE_ERROR_PASSIVE;
+       }
+       if (isrc & SUN4I_INT_ARB_LOST) {
+               /* arbitration lost interrupt */
+               netdev_dbg(dev, "arbitration lost interrupt\n");
+               alc = readl(priv->base + SUN4I_REG_STA_ADDR);
+               priv->can.can_stats.arbitration_lost++;
+               stats->tx_errors++;
+               if (likely(skb)) {
+                       cf->can_id |= CAN_ERR_LOSTARB;
+                       cf->data[0] = (alc >> 8) & 0x1f;
+               }
+       }
+
+       if (state != priv->can.state) {
+               tx_state = txerr >= rxerr ? state : 0;
+               rx_state = txerr <= rxerr ? state : 0;
+
+               if (likely(skb))
+                       can_change_state(dev, cf, tx_state, rx_state);
+               else
+                       priv->can.state = state;
+               if (state == CAN_STATE_BUS_OFF)
+                       can_bus_off(dev);
+       }
+
+       if (likely(skb)) {
+               stats->rx_packets++;
+               stats->rx_bytes += cf->can_dlc;
+               netif_rx(skb);
+       } else {
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static irqreturn_t sun4i_can_interrupt(int irq, void *dev_id)
+{
+       struct net_device *dev = (struct net_device *)dev_id;
+       struct sun4ican_priv *priv = netdev_priv(dev);
+       struct net_device_stats *stats = &dev->stats;
+       u8 isrc, status;
+       int n = 0;
+
+       while ((isrc = readl(priv->base + SUN4I_REG_INT_ADDR)) &&
+              (n < SUN4I_CAN_MAX_IRQ)) {
+               n++;
+               status = readl(priv->base + SUN4I_REG_STA_ADDR);
+
+               if (isrc & SUN4I_INT_WAKEUP)
+                       netdev_warn(dev, "wakeup interrupt\n");
+
+               if (isrc & SUN4I_INT_TBUF_VLD) {
+                       /* transmission complete interrupt */
+                       stats->tx_bytes +=
+                           readl(priv->base +
+                                 SUN4I_REG_RBUF_RBACK_START_ADDR) & 0xf;
+                       stats->tx_packets++;
+                       can_get_echo_skb(dev, 0);
+                       netif_wake_queue(dev);
+                       can_led_event(dev, CAN_LED_EVENT_TX);
+               }
+               if (isrc & SUN4I_INT_RBUF_VLD) {
+                       /* receive interrupt */
+                       while (status & SUN4I_STA_RBUF_RDY) {
+                               /* RX buffer is not empty */
+                               sun4i_can_rx(dev);
+                               status = readl(priv->base + SUN4I_REG_STA_ADDR);
+                       }
+               }
+               if (isrc &
+                   (SUN4I_INT_DATA_OR | SUN4I_INT_ERR_WRN | SUN4I_INT_BUS_ERR |
+                    SUN4I_INT_ERR_PASSIVE | SUN4I_INT_ARB_LOST)) {
+                       /* error interrupt */
+                       if (sun4i_can_err(dev, isrc, status))
+                               netdev_err(dev, "can't allocate buffer - clearing pending interrupts\n");
+               }
+               /* clear interrupts */
+               writel(isrc, priv->base + SUN4I_REG_INT_ADDR);
+               readl(priv->base + SUN4I_REG_INT_ADDR);
+       }
+       if (n >= SUN4I_CAN_MAX_IRQ)
+               netdev_dbg(dev, "%d messages handled in ISR", n);
+
+       return (n) ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int sun4ican_open(struct net_device *dev)
+{
+       struct sun4ican_priv *priv = netdev_priv(dev);
+       int err;
+
+       /* common open */
+       err = open_candev(dev);
+       if (err)
+               return err;
+
+       /* register interrupt handler */
+       err = request_irq(dev->irq, sun4i_can_interrupt, 0, dev->name, dev);
+       if (err) {
+               netdev_err(dev, "request_irq err: %d\n", err);
+               goto exit_irq;
+       }
+
+       /* turn on clocking for CAN peripheral block */
+       err = clk_prepare_enable(priv->clk);
+       if (err) {
+               netdev_err(dev, "could not enable CAN peripheral clock\n");
+               goto exit_clock;
+       }
+
+       err = sun4i_can_start(dev);
+       if (err) {
+               netdev_err(dev, "could not start CAN peripheral\n");
+               goto exit_can_start;
+       }
+
+       can_led_event(dev, CAN_LED_EVENT_OPEN);
+       netif_start_queue(dev);
+
+       return 0;
+
+exit_can_start:
+       clk_disable_unprepare(priv->clk);
+exit_clock:
+       free_irq(dev->irq, dev);
+exit_irq:
+       close_candev(dev);
+       return err;
+}
+
+static int sun4ican_close(struct net_device *dev)
+{
+       struct sun4ican_priv *priv = netdev_priv(dev);
+
+       netif_stop_queue(dev);
+       sun4i_can_stop(dev);
+       clk_disable_unprepare(priv->clk);
+
+       free_irq(dev->irq, dev);
+       close_candev(dev);
+       can_led_event(dev, CAN_LED_EVENT_STOP);
+
+       return 0;
+}
+
+static const struct net_device_ops sun4ican_netdev_ops = {
+       .ndo_open = sun4ican_open,
+       .ndo_stop = sun4ican_close,
+       .ndo_start_xmit = sun4ican_start_xmit,
+};
+
+static const struct of_device_id sun4ican_of_match[] = {
+       {.compatible = "allwinner,sun4i-a10-can"},
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, sun4ican_of_match);
+
+static int sun4ican_remove(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+
+       unregister_netdev(dev);
+       free_candev(dev);
+
+       return 0;
+}
+
+static int sun4ican_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct resource *mem;
+       struct clk *clk;
+       void __iomem *addr;
+       int err, irq;
+       struct net_device *dev;
+       struct sun4ican_priv *priv;
+
+       clk = of_clk_get(np, 0);
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "unable to request clock\n");
+               err = -ENODEV;
+               goto exit;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "could not get a valid irq\n");
+               err = -ENODEV;
+               goto exit;
+       }
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       addr = devm_ioremap_resource(&pdev->dev, mem);
+       if (IS_ERR(addr)) {
+               err = -EBUSY;
+               goto exit;
+       }
+
+       dev = alloc_candev(sizeof(struct sun4ican_priv), 1);
+       if (!dev) {
+               dev_err(&pdev->dev,
+                       "could not allocate memory for CAN device\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       dev->netdev_ops = &sun4ican_netdev_ops;
+       dev->irq = irq;
+       dev->flags |= IFF_ECHO;
+
+       priv = netdev_priv(dev);
+       priv->can.clock.freq = clk_get_rate(clk);
+       priv->can.bittiming_const = &sun4ican_bittiming_const;
+       priv->can.do_set_mode = sun4ican_set_mode;
+       priv->can.do_get_berr_counter = sun4ican_get_berr_counter;
+       priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING |
+                                      CAN_CTRLMODE_LISTENONLY |
+                                      CAN_CTRLMODE_LOOPBACK |
+                                      CAN_CTRLMODE_PRESUME_ACK |
+                                      CAN_CTRLMODE_3_SAMPLES;
+       priv->base = addr;
+       priv->clk = clk;
+       spin_lock_init(&priv->cmdreg_lock);
+
+       platform_set_drvdata(pdev, dev);
+       SET_NETDEV_DEV(dev, &pdev->dev);
+
+       err = register_candev(dev);
+       if (err) {
+               dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
+                       DRV_NAME, err);
+               goto exit_free;
+       }
+       devm_can_led_init(dev);
+
+       dev_info(&pdev->dev, "device registered (base=%p, irq=%d)\n",
+                priv->base, dev->irq);
+
+       return 0;
+
+exit_free:
+       free_candev(dev);
+exit:
+       return err;
+}
+
+static struct platform_driver sun4i_can_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .of_match_table = sun4ican_of_match,
+       },
+       .probe = sun4ican_probe,
+       .remove = sun4ican_remove,
+};
+
+module_platform_driver(sun4i_can_driver);
+
+MODULE_AUTHOR("Peter Chen <xingkongcp@gmail.com>");
+MODULE_AUTHOR("Gerhard Bertelsmann <info@gerhard-bertelsmann.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("CAN driver for Allwinner SoCs (A10/A20)");
index 9d56515f4c4da8ef307ff68ccb438a5e7742ceea..6f946fedbb77c1943770b31665a0f5e1e6e4d889 100644 (file)
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_address.h>
+#include <linux/of_net.h>
 #include <net/dsa.h>
 #include <linux/ethtool.h>
 #include <linux/if_bridge.h>
 #include <linux/brcmphy.h>
+#include <linux/etherdevice.h>
+#include <net/switchdev.h>
 
 #include "bcm_sf2.h"
 #include "bcm_sf2_regs.h"
@@ -264,6 +267,50 @@ static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable)
        }
 }
 
+static inline void bcm_sf2_port_intr_enable(struct bcm_sf2_priv *priv,
+                                           int port)
+{
+       unsigned int off;
+
+       switch (port) {
+       case 7:
+               off = P7_IRQ_OFF;
+               break;
+       case 0:
+               /* Port 0 interrupts are located on the first bank */
+               intrl2_0_mask_clear(priv, P_IRQ_MASK(P0_IRQ_OFF));
+               return;
+       default:
+               off = P_IRQ_OFF(port);
+               break;
+       }
+
+       intrl2_1_mask_clear(priv, P_IRQ_MASK(off));
+}
+
+static inline void bcm_sf2_port_intr_disable(struct bcm_sf2_priv *priv,
+                                            int port)
+{
+       unsigned int off;
+
+       switch (port) {
+       case 7:
+               off = P7_IRQ_OFF;
+               break;
+       case 0:
+               /* Port 0 interrupts are located on the first bank */
+               intrl2_0_mask_set(priv, P_IRQ_MASK(P0_IRQ_OFF));
+               intrl2_0_writel(priv, P_IRQ_MASK(P0_IRQ_OFF), INTRL2_CPU_CLEAR);
+               return;
+       default:
+               off = P_IRQ_OFF(port);
+               break;
+       }
+
+       intrl2_1_mask_set(priv, P_IRQ_MASK(off));
+       intrl2_1_writel(priv, P_IRQ_MASK(off), INTRL2_CPU_CLEAR);
+}
+
 static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
                              struct phy_device *phy)
 {
@@ -280,7 +327,7 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
        core_writel(priv, 0, CORE_G_PCTL_PORT(port));
 
        /* Re-enable the GPHY and re-apply workarounds */
-       if (port == 0 && priv->hw_params.num_gphy == 1) {
+       if (priv->int_phy_mask & 1 << port && priv->hw_params.num_gphy == 1) {
                bcm_sf2_gphy_enable_set(ds, true);
                if (phy) {
                        /* if phy_stop() has been called before, phy
@@ -297,9 +344,9 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
                }
        }
 
-       /* Enable port 7 interrupts to get notified */
-       if (port == 7)
-               intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF));
+       /* Enable MoCA port interrupts to get notified */
+       if (port == priv->moca_port)
+               bcm_sf2_port_intr_enable(priv, port);
 
        /* Set this port, and only this one to be in the default VLAN,
         * if member of a bridge, restore its membership prior to
@@ -329,12 +376,10 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port,
        if (priv->wol_ports_mask & (1 << port))
                return;
 
-       if (port == 7) {
-               intrl2_1_mask_set(priv, P_IRQ_MASK(P7_IRQ_OFF));
-               intrl2_1_writel(priv, P_IRQ_MASK(P7_IRQ_OFF), INTRL2_CPU_CLEAR);
-       }
+       if (port == priv->moca_port)
+               bcm_sf2_port_intr_disable(priv, port);
 
-       if (port == 0 && priv->hw_params.num_gphy == 1)
+       if (priv->int_phy_mask & 1 << port && priv->hw_params.num_gphy == 1)
                bcm_sf2_gphy_enable_set(ds, false);
 
        if (dsa_is_cpu_port(ds, port))
@@ -555,6 +600,236 @@ static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port,
        return 0;
 }
 
+/* Address Resolution Logic routines */
+static int bcm_sf2_arl_op_wait(struct bcm_sf2_priv *priv)
+{
+       unsigned int timeout = 10;
+       u32 reg;
+
+       do {
+               reg = core_readl(priv, CORE_ARLA_RWCTL);
+               if (!(reg & ARL_STRTDN))
+                       return 0;
+
+               usleep_range(1000, 2000);
+       } while (timeout--);
+
+       return -ETIMEDOUT;
+}
+
+static int bcm_sf2_arl_rw_op(struct bcm_sf2_priv *priv, unsigned int op)
+{
+       u32 cmd;
+
+       if (op > ARL_RW)
+               return -EINVAL;
+
+       cmd = core_readl(priv, CORE_ARLA_RWCTL);
+       cmd &= ~IVL_SVL_SELECT;
+       cmd |= ARL_STRTDN;
+       if (op)
+               cmd |= ARL_RW;
+       else
+               cmd &= ~ARL_RW;
+       core_writel(priv, cmd, CORE_ARLA_RWCTL);
+
+       return bcm_sf2_arl_op_wait(priv);
+}
+
+static int bcm_sf2_arl_read(struct bcm_sf2_priv *priv, u64 mac,
+                           u16 vid, struct bcm_sf2_arl_entry *ent, u8 *idx,
+                           bool is_valid)
+{
+       unsigned int i;
+       int ret;
+
+       ret = bcm_sf2_arl_op_wait(priv);
+       if (ret)
+               return ret;
+
+       /* Read the 4 bins */
+       for (i = 0; i < 4; i++) {
+               u64 mac_vid;
+               u32 fwd_entry;
+
+               mac_vid = core_readq(priv, CORE_ARLA_MACVID_ENTRY(i));
+               fwd_entry = core_readl(priv, CORE_ARLA_FWD_ENTRY(i));
+               bcm_sf2_arl_to_entry(ent, mac_vid, fwd_entry);
+
+               if (ent->is_valid && is_valid) {
+                       *idx = i;
+                       return 0;
+               }
+
+               /* This is the MAC we just deleted */
+               if (!is_valid && (mac_vid & mac))
+                       return 0;
+       }
+
+       return -ENOENT;
+}
+
+static int bcm_sf2_arl_op(struct bcm_sf2_priv *priv, int op, int port,
+                         const unsigned char *addr, u16 vid, bool is_valid)
+{
+       struct bcm_sf2_arl_entry ent;
+       u32 fwd_entry;
+       u64 mac, mac_vid = 0;
+       u8 idx = 0;
+       int ret;
+
+       /* Convert the array into a 64-bit MAC */
+       mac = bcm_sf2_mac_to_u64(addr);
+
+       /* Perform a read for the given MAC and VID */
+       core_writeq(priv, mac, CORE_ARLA_MAC);
+       core_writel(priv, vid, CORE_ARLA_VID);
+
+       /* Issue a read operation for this MAC */
+       ret = bcm_sf2_arl_rw_op(priv, 1);
+       if (ret)
+               return ret;
+
+       ret = bcm_sf2_arl_read(priv, mac, vid, &ent, &idx, is_valid);
+       /* If this is a read, just finish now */
+       if (op)
+               return ret;
+
+       /* We could not find a matching MAC, so reset to a new entry */
+       if (ret) {
+               fwd_entry = 0;
+               idx = 0;
+       }
+
+       memset(&ent, 0, sizeof(ent));
+       ent.port = port;
+       ent.is_valid = is_valid;
+       ent.vid = vid;
+       ent.is_static = true;
+       memcpy(ent.mac, addr, ETH_ALEN);
+       bcm_sf2_arl_from_entry(&mac_vid, &fwd_entry, &ent);
+
+       core_writeq(priv, mac_vid, CORE_ARLA_MACVID_ENTRY(idx));
+       core_writel(priv, fwd_entry, CORE_ARLA_FWD_ENTRY(idx));
+
+       ret = bcm_sf2_arl_rw_op(priv, 0);
+       if (ret)
+               return ret;
+
+       /* Re-read the entry to check */
+       return bcm_sf2_arl_read(priv, mac, vid, &ent, &idx, is_valid);
+}
+
+static int bcm_sf2_sw_fdb_prepare(struct dsa_switch *ds, int port,
+                                 const struct switchdev_obj_port_fdb *fdb,
+                                 struct switchdev_trans *trans)
+{
+       /* We do not need to do anything specific here yet */
+       return 0;
+}
+
+static int bcm_sf2_sw_fdb_add(struct dsa_switch *ds, int port,
+                             const struct switchdev_obj_port_fdb *fdb,
+                             struct switchdev_trans *trans)
+{
+       struct bcm_sf2_priv *priv = ds_to_priv(ds);
+
+       return bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, true);
+}
+
+static int bcm_sf2_sw_fdb_del(struct dsa_switch *ds, int port,
+                             const struct switchdev_obj_port_fdb *fdb)
+{
+       struct bcm_sf2_priv *priv = ds_to_priv(ds);
+
+       return bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, false);
+}
+
+static int bcm_sf2_arl_search_wait(struct bcm_sf2_priv *priv)
+{
+       unsigned timeout = 1000;
+       u32 reg;
+
+       do {
+               reg = core_readl(priv, CORE_ARLA_SRCH_CTL);
+               if (!(reg & ARLA_SRCH_STDN))
+                       return 0;
+
+               if (reg & ARLA_SRCH_VLID)
+                       return 0;
+
+               usleep_range(1000, 2000);
+       } while (timeout--);
+
+       return -ETIMEDOUT;
+}
+
+static void bcm_sf2_arl_search_rd(struct bcm_sf2_priv *priv, u8 idx,
+                                 struct bcm_sf2_arl_entry *ent)
+{
+       u64 mac_vid;
+       u32 fwd_entry;
+
+       mac_vid = core_readq(priv, CORE_ARLA_SRCH_RSLT_MACVID(idx));
+       fwd_entry = core_readl(priv, CORE_ARLA_SRCH_RSLT(idx));
+       bcm_sf2_arl_to_entry(ent, mac_vid, fwd_entry);
+}
+
+static int bcm_sf2_sw_fdb_copy(struct net_device *dev, int port,
+                              const struct bcm_sf2_arl_entry *ent,
+                              struct switchdev_obj_port_fdb *fdb,
+                              int (*cb)(struct switchdev_obj *obj))
+{
+       if (!ent->is_valid)
+               return 0;
+
+       if (port != ent->port)
+               return 0;
+
+       ether_addr_copy(fdb->addr, ent->mac);
+       fdb->vid = ent->vid;
+       fdb->ndm_state = ent->is_static ? NUD_NOARP : NUD_REACHABLE;
+
+       return cb(&fdb->obj);
+}
+
+static int bcm_sf2_sw_fdb_dump(struct dsa_switch *ds, int port,
+                              struct switchdev_obj_port_fdb *fdb,
+                              int (*cb)(struct switchdev_obj *obj))
+{
+       struct bcm_sf2_priv *priv = ds_to_priv(ds);
+       struct net_device *dev = ds->ports[port];
+       struct bcm_sf2_arl_entry results[2];
+       unsigned int count = 0;
+       int ret;
+
+       /* Start search operation */
+       core_writel(priv, ARLA_SRCH_STDN, CORE_ARLA_SRCH_CTL);
+
+       do {
+               ret = bcm_sf2_arl_search_wait(priv);
+               if (ret)
+                       return ret;
+
+               /* Read both entries, then return their values back */
+               bcm_sf2_arl_search_rd(priv, 0, &results[0]);
+               ret = bcm_sf2_sw_fdb_copy(dev, port, &results[0], fdb, cb);
+               if (ret)
+                       return ret;
+
+               bcm_sf2_arl_search_rd(priv, 1, &results[1]);
+               ret = bcm_sf2_sw_fdb_copy(dev, port, &results[1], fdb, cb);
+               if (ret)
+                       return ret;
+
+               if (!results[0].is_valid && !results[1].is_valid)
+                       break;
+
+       } while (count++ < CORE_ARLA_NUM_ENTRIES);
+
+       return 0;
+}
+
 static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
 {
        struct bcm_sf2_priv *priv = dev_id;
@@ -615,6 +890,42 @@ static void bcm_sf2_intr_disable(struct bcm_sf2_priv *priv)
        intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
 }
 
+static void bcm_sf2_identify_ports(struct bcm_sf2_priv *priv,
+                                  struct device_node *dn)
+{
+       struct device_node *port;
+       const char *phy_mode_str;
+       int mode;
+       unsigned int port_num;
+       int ret;
+
+       priv->moca_port = -1;
+
+       for_each_available_child_of_node(dn, port) {
+               if (of_property_read_u32(port, "reg", &port_num))
+                       continue;
+
+               /* Internal PHYs get assigned a specific 'phy-mode' property
+                * value: "internal" to help flag them before MDIO probing
+                * has completed, since they might be turned off at that
+                * time
+                */
+               mode = of_get_phy_mode(port);
+               if (mode < 0) {
+                       ret = of_property_read_string(port, "phy-mode",
+                                                     &phy_mode_str);
+                       if (ret < 0)
+                               continue;
+
+                       if (!strcasecmp(phy_mode_str, "internal"))
+                               priv->int_phy_mask |= 1 << port_num;
+               }
+
+               if (mode == PHY_INTERFACE_MODE_MOCA)
+                       priv->moca_port = port_num;
+       }
+}
+
 static int bcm_sf2_sw_setup(struct dsa_switch *ds)
 {
        const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME;
@@ -633,6 +944,7 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds)
         * level
         */
        dn = ds->pd->of_node->parent;
+       bcm_sf2_identify_ports(priv, ds->pd->of_node);
 
        priv->irq0 = irq_of_parse_and_map(dn, 0);
        priv->irq1 = irq_of_parse_and_map(dn, 1);
@@ -913,7 +1225,7 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
 
        status->link = 0;
 
-       /* Port 7 is special as we do not get link status from CORE_LNKSTS,
+       /* MoCA port is special as we do not get link status from CORE_LNKSTS,
         * which means that we need to force the link at the port override
         * level to get the data to flow. We do use what the interrupt handler
         * did determine before.
@@ -921,7 +1233,7 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
         * For the other ports, we just force the link status, since this is
         * a fixed PHY device.
         */
-       if (port == 7) {
+       if (port == priv->moca_port) {
                status->link = priv->port_sts[port].link;
                /* For MoCA interfaces, also force a link down notification
                 * since some version of the user-space daemon (mocad) use
@@ -1076,6 +1388,10 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = {
        .port_join_bridge       = bcm_sf2_sw_br_join,
        .port_leave_bridge      = bcm_sf2_sw_br_leave,
        .port_stp_update        = bcm_sf2_sw_br_set_stp_state,
+       .port_fdb_prepare       = bcm_sf2_sw_fdb_prepare,
+       .port_fdb_add           = bcm_sf2_sw_fdb_add,
+       .port_fdb_del           = bcm_sf2_sw_fdb_del,
+       .port_fdb_dump          = bcm_sf2_sw_fdb_dump,
 };
 
 static int __init bcm_sf2_init(void)
index 789d7b7737da4ada78f06850eb835cd437c1040a..6bba1c98d764cf2b4222c82211677e30c448a4f0 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/mutex.h>
 #include <linux/mii.h>
 #include <linux/ethtool.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
 
 #include <net/dsa.h>
 
@@ -50,6 +52,60 @@ struct bcm_sf2_port_status {
        u32 vlan_ctl_mask;
 };
 
+struct bcm_sf2_arl_entry {
+       u8 port;
+       u8 mac[ETH_ALEN];
+       u16 vid;
+       u8 is_valid:1;
+       u8 is_age:1;
+       u8 is_static:1;
+};
+
+static inline void bcm_sf2_mac_from_u64(u64 src, u8 *dst)
+{
+       unsigned int i;
+
+       for (i = 0; i < ETH_ALEN; i++)
+               dst[ETH_ALEN - 1 - i] = (src >> (8 * i)) & 0xff;
+}
+
+static inline u64 bcm_sf2_mac_to_u64(const u8 *src)
+{
+       unsigned int i;
+       u64 dst = 0;
+
+       for (i = 0; i < ETH_ALEN; i++)
+               dst |= (u64)src[ETH_ALEN - 1 - i] << (8 * i);
+
+       return dst;
+}
+
+static inline void bcm_sf2_arl_to_entry(struct bcm_sf2_arl_entry *ent,
+                                       u64 mac_vid, u32 fwd_entry)
+{
+       memset(ent, 0, sizeof(*ent));
+       ent->port = fwd_entry & PORTID_MASK;
+       ent->is_valid = !!(fwd_entry & ARL_VALID);
+       ent->is_age = !!(fwd_entry & ARL_AGE);
+       ent->is_static = !!(fwd_entry & ARL_STATIC);
+       bcm_sf2_mac_from_u64(mac_vid, ent->mac);
+       ent->vid = mac_vid >> VID_SHIFT;
+}
+
+static inline void bcm_sf2_arl_from_entry(u64 *mac_vid, u32 *fwd_entry,
+                                         const struct bcm_sf2_arl_entry *ent)
+{
+       *mac_vid = bcm_sf2_mac_to_u64(ent->mac);
+       *mac_vid |= (u64)(ent->vid & VID_MASK) << VID_SHIFT;
+       *fwd_entry = ent->port & PORTID_MASK;
+       if (ent->is_valid)
+               *fwd_entry |= ARL_VALID;
+       if (ent->is_static)
+               *fwd_entry |= ARL_STATIC;
+       if (ent->is_age)
+               *fwd_entry |= ARL_AGE;
+}
+
 struct bcm_sf2_priv {
        /* Base registers, keep those in order with BCM_SF2_REGS_NAME */
        void __iomem                    *core;
@@ -78,6 +134,12 @@ struct bcm_sf2_priv {
 
        /* Mask of ports enabled for Wake-on-LAN */
        u32                             wol_ports_mask;
+
+       /* MoCA port location */
+       int                             moca_port;
+
+       /* Bitmask of ports having an integrated PHY */
+       unsigned int                    int_phy_mask;
 };
 
 struct bcm_sf2_hw_stats {
index fa4e6e78c9ea75526bec5d6ed7356053753fb6cb..97780d43b5c0b6bc37439cf3f48cb5e8928c1e80 100644 (file)
 #define CORE_BRCM_HDR_RX_DIS           0x0980
 #define CORE_BRCM_HDR_TX_DIS           0x0988
 
+#define CORE_ARLA_NUM_ENTRIES          1024
+
+#define CORE_ARLA_RWCTL                        0x1400
+#define  ARL_RW                                (1 << 0)
+#define  IVL_SVL_SELECT                        (1 << 6)
+#define  ARL_STRTDN                    (1 << 7)
+
+#define CORE_ARLA_MAC                  0x1408
+#define CORE_ARLA_VID                  0x1420
+#define  ARLA_VIDTAB_INDX_MASK         0x1fff
+
+#define CORE_ARLA_MACVID0              0x1440
+#define  MAC_MASK                      0xffffffffff
+#define  VID_SHIFT                     48
+#define  VID_MASK                      0xfff
+
+#define CORE_ARLA_FWD_ENTRY0           0x1460
+#define  PORTID_MASK                   0x1ff
+#define  ARL_CON_SHIFT                 9
+#define  ARL_CON_MASK                  0x3
+#define  ARL_PRI_SHIFT                 11
+#define  ARL_PRI_MASK                  0x7
+#define  ARL_AGE                       (1 << 14)
+#define  ARL_STATIC                    (1 << 15)
+#define  ARL_VALID                     (1 << 16)
+
+#define CORE_ARLA_MACVID_ENTRY(x)      (CORE_ARLA_MACVID0 + ((x) * 0x40))
+#define CORE_ARLA_FWD_ENTRY(x)         (CORE_ARLA_FWD_ENTRY0 + ((x) * 0x40))
+
+#define CORE_ARLA_SRCH_CTL             0x1540
+#define  ARLA_SRCH_VLID                        (1 << 0)
+#define  IVL_SVL_SELECT                        (1 << 6)
+#define  ARLA_SRCH_STDN                        (1 << 7)
+
+#define CORE_ARLA_SRCH_ADR             0x1544
+#define  ARLA_SRCH_ADR_VALID           (1 << 15)
+
+#define CORE_ARLA_SRCH_RSLT_0_MACVID   0x1580
+#define CORE_ARLA_SRCH_RSLT_0          0x15a0
+
+#define CORE_ARLA_SRCH_RSLT_MACVID(x)  (CORE_ARLA_SRCH_RSLT_0_MACVID + ((x) * 0x40))
+#define CORE_ARLA_SRCH_RSLT(x)         (CORE_ARLA_SRCH_RSLT_0 + ((x) * 0x40))
+
 #define CORE_MEM_PSM_VDD_CTRL          0x2380
 #define  P_TXQ_PSM_VDD_SHIFT           2
 #define  P_TXQ_PSM_VDD_MASK            0x3
index c29aebe1e62b59801ca7bb2aed36f9c502733fbf..9093577755f69bac2c548518922b1b63215a62a6 100644 (file)
@@ -26,7 +26,7 @@ static int reg_read(struct dsa_switch *ds, int addr, int reg)
        if (bus == NULL)
                return -EINVAL;
 
-       return mdiobus_read(bus, ds->pd->sw_addr + addr, reg);
+       return mdiobus_read_nested(bus, ds->pd->sw_addr + addr, reg);
 }
 
 #define REG_READ(addr, reg)                                    \
@@ -47,7 +47,7 @@ static int reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
        if (bus == NULL)
                return -EINVAL;
 
-       return mdiobus_write(bus, ds->pd->sw_addr + addr, reg, val);
+       return mdiobus_write_nested(bus, ds->pd->sw_addr + addr, reg, val);
 }
 
 #define REG_WRITE(addr, reg, val)                              \
index 3de2a6d73fdc4dff0ac2f7071b95bbeb4bf33015..d4fcf4570d955c5ee637870cfa5a276aab63a5a8 100644 (file)
 #include <net/dsa.h>
 #include "mv88e6xxx.h"
 
+static const struct mv88e6xxx_switch_id mv88e6123_61_65_table[] = {
+       { PORT_SWITCH_ID_6123, "Marvell 88E6123" },
+       { PORT_SWITCH_ID_6123_A1, "Marvell 88E6123 (A1)" },
+       { PORT_SWITCH_ID_6123_A2, "Marvell 88E6123 (A2)" },
+       { PORT_SWITCH_ID_6161, "Marvell 88E6161" },
+       { PORT_SWITCH_ID_6161_A1, "Marvell 88E6161 (A1)" },
+       { PORT_SWITCH_ID_6161_A2, "Marvell 88E6161 (A2)" },
+       { PORT_SWITCH_ID_6165, "Marvell 88E6165" },
+       { PORT_SWITCH_ID_6165_A1, "Marvell 88E6165 (A1)" },
+       { PORT_SWITCH_ID_6165_A2, "Marvell 88e6165 (A2)" },
+};
+
 static char *mv88e6123_61_65_probe(struct device *host_dev, int sw_addr)
 {
-       struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
-       int ret;
-
-       if (bus == NULL)
-               return NULL;
-
-       ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
-       if (ret >= 0) {
-               if (ret == PORT_SWITCH_ID_6123_A1)
-                       return "Marvell 88E6123 (A1)";
-               if (ret == PORT_SWITCH_ID_6123_A2)
-                       return "Marvell 88E6123 (A2)";
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6123)
-                       return "Marvell 88E6123";
-
-               if (ret == PORT_SWITCH_ID_6161_A1)
-                       return "Marvell 88E6161 (A1)";
-               if (ret == PORT_SWITCH_ID_6161_A2)
-                       return "Marvell 88E6161 (A2)";
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6161)
-                       return "Marvell 88E6161";
-
-               if (ret == PORT_SWITCH_ID_6165_A1)
-                       return "Marvell 88E6165 (A1)";
-               if (ret == PORT_SWITCH_ID_6165_A2)
-                       return "Marvell 88e6165 (A2)";
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6165)
-                       return "Marvell 88E6165";
-       }
-
-       return NULL;
+       return mv88e6xxx_lookup_name(host_dev, sw_addr, mv88e6123_61_65_table,
+                                    ARRAY_SIZE(mv88e6123_61_65_table));
 }
 
 static int mv88e6123_61_65_setup_global(struct dsa_switch *ds)
@@ -125,7 +108,6 @@ struct dsa_switch_driver mv88e6123_61_65_switch_driver = {
        .set_addr               = mv88e6xxx_set_addr_indirect,
        .phy_read               = mv88e6xxx_phy_read,
        .phy_write              = mv88e6xxx_phy_write,
-       .poll_link              = mv88e6xxx_poll_link,
        .get_strings            = mv88e6xxx_get_strings,
        .get_ethtool_stats      = mv88e6xxx_get_ethtool_stats,
        .get_sset_count         = mv88e6xxx_get_sset_count,
index 3e838652996507d673b918e52ec3953d07795d3f..a92ca651c3990c1353922fadb242e57d8f7e0af9 100644 (file)
 #include <net/dsa.h>
 #include "mv88e6xxx.h"
 
+static const struct mv88e6xxx_switch_id mv88e6131_table[] = {
+       { PORT_SWITCH_ID_6085, "Marvell 88E6085" },
+       { PORT_SWITCH_ID_6095, "Marvell 88E6095/88E6095F" },
+       { PORT_SWITCH_ID_6131, "Marvell 88E6131" },
+       { PORT_SWITCH_ID_6131_B2, "Marvell 88E6131 (B2)" },
+       { PORT_SWITCH_ID_6185, "Marvell 88E6185" },
+};
+
 static char *mv88e6131_probe(struct device *host_dev, int sw_addr)
 {
-       struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
-       int ret;
-
-       if (bus == NULL)
-               return NULL;
-
-       ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
-       if (ret >= 0) {
-               int ret_masked = ret & 0xfff0;
-
-               if (ret_masked == PORT_SWITCH_ID_6085)
-                       return "Marvell 88E6085";
-               if (ret_masked == PORT_SWITCH_ID_6095)
-                       return "Marvell 88E6095/88E6095F";
-               if (ret == PORT_SWITCH_ID_6131_B2)
-                       return "Marvell 88E6131 (B2)";
-               if (ret_masked == PORT_SWITCH_ID_6131)
-                       return "Marvell 88E6131";
-               if (ret_masked == PORT_SWITCH_ID_6185)
-                       return "Marvell 88E6185";
-       }
-
-       return NULL;
+       return mv88e6xxx_lookup_name(host_dev, sw_addr, mv88e6131_table,
+                                    ARRAY_SIZE(mv88e6131_table));
 }
 
 static int mv88e6131_setup_global(struct dsa_switch *ds)
@@ -178,7 +165,6 @@ struct dsa_switch_driver mv88e6131_switch_driver = {
        .set_addr               = mv88e6xxx_set_addr_direct,
        .phy_read               = mv88e6131_phy_read,
        .phy_write              = mv88e6131_phy_write,
-       .poll_link              = mv88e6xxx_poll_link,
        .get_strings            = mv88e6xxx_get_strings,
        .get_ethtool_stats      = mv88e6xxx_get_ethtool_stats,
        .get_sset_count         = mv88e6xxx_get_sset_count,
index c2daaf087761c38c2dcbb6423c742950893a7024..54aa00012dd0ce5ca36427c0cfb41f773ea47138 100644 (file)
 #include <net/dsa.h>
 #include "mv88e6xxx.h"
 
+static const struct mv88e6xxx_switch_id mv88e6171_table[] = {
+       { PORT_SWITCH_ID_6171, "Marvell 88E6171" },
+       { PORT_SWITCH_ID_6175, "Marvell 88E6175" },
+       { PORT_SWITCH_ID_6350, "Marvell 88E6350" },
+       { PORT_SWITCH_ID_6351, "Marvell 88E6351" },
+};
+
 static char *mv88e6171_probe(struct device *host_dev, int sw_addr)
 {
-       struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
-       int ret;
-
-       if (bus == NULL)
-               return NULL;
-
-       ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
-       if (ret >= 0) {
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6171)
-                       return "Marvell 88E6171";
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6175)
-                       return "Marvell 88E6175";
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6350)
-                       return "Marvell 88E6350";
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6351)
-                       return "Marvell 88E6351";
-       }
-
-       return NULL;
+       return mv88e6xxx_lookup_name(host_dev, sw_addr, mv88e6171_table,
+                                    ARRAY_SIZE(mv88e6171_table));
 }
 
 static int mv88e6171_setup_global(struct dsa_switch *ds)
@@ -104,7 +94,6 @@ struct dsa_switch_driver mv88e6171_switch_driver = {
        .set_addr               = mv88e6xxx_set_addr_indirect,
        .phy_read               = mv88e6xxx_phy_read_indirect,
        .phy_write              = mv88e6xxx_phy_write_indirect,
-       .poll_link              = mv88e6xxx_poll_link,
        .get_strings            = mv88e6xxx_get_strings,
        .get_ethtool_stats      = mv88e6xxx_get_ethtool_stats,
        .get_sset_count         = mv88e6xxx_get_sset_count,
@@ -114,17 +103,16 @@ struct dsa_switch_driver mv88e6171_switch_driver = {
 #endif
        .get_regs_len           = mv88e6xxx_get_regs_len,
        .get_regs               = mv88e6xxx_get_regs,
-       .port_join_bridge       = mv88e6xxx_join_bridge,
-       .port_leave_bridge      = mv88e6xxx_leave_bridge,
        .port_stp_update        = mv88e6xxx_port_stp_update,
        .port_pvid_get          = mv88e6xxx_port_pvid_get,
-       .port_pvid_set          = mv88e6xxx_port_pvid_set,
+       .port_vlan_prepare      = mv88e6xxx_port_vlan_prepare,
        .port_vlan_add          = mv88e6xxx_port_vlan_add,
        .port_vlan_del          = mv88e6xxx_port_vlan_del,
        .vlan_getnext           = mv88e6xxx_vlan_getnext,
+       .port_fdb_prepare       = mv88e6xxx_port_fdb_prepare,
        .port_fdb_add           = mv88e6xxx_port_fdb_add,
        .port_fdb_del           = mv88e6xxx_port_fdb_del,
-       .port_fdb_getnext       = mv88e6xxx_port_fdb_getnext,
+       .port_fdb_dump          = mv88e6xxx_port_fdb_dump,
 };
 
 MODULE_ALIAS("platform:mv88e6171");
index 1f5129c105fb3cce995527104b049ebf6e89abac..ff846d0cfcebeff1b332b8f8ae1ca48d106c667d 100644 (file)
 #include <net/dsa.h>
 #include "mv88e6xxx.h"
 
+static const struct mv88e6xxx_switch_id mv88e6352_table[] = {
+       { PORT_SWITCH_ID_6172, "Marvell 88E6172" },
+       { PORT_SWITCH_ID_6176, "Marvell 88E6176" },
+       { PORT_SWITCH_ID_6320, "Marvell 88E6320" },
+       { PORT_SWITCH_ID_6320_A1, "Marvell 88E6320 (A1)" },
+       { PORT_SWITCH_ID_6320_A2, "Marvell 88e6320 (A2)" },
+       { PORT_SWITCH_ID_6321, "Marvell 88E6321" },
+       { PORT_SWITCH_ID_6321_A1, "Marvell 88E6321 (A1)" },
+       { PORT_SWITCH_ID_6321_A2, "Marvell 88e6321 (A2)" },
+       { PORT_SWITCH_ID_6352, "Marvell 88E6352" },
+       { PORT_SWITCH_ID_6352_A0, "Marvell 88E6352 (A0)" },
+       { PORT_SWITCH_ID_6352_A1, "Marvell 88E6352 (A1)" },
+};
+
 static char *mv88e6352_probe(struct device *host_dev, int sw_addr)
 {
-       struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
-       int ret;
-
-       if (bus == NULL)
-               return NULL;
-
-       ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
-       if (ret >= 0) {
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6172)
-                       return "Marvell 88E6172";
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6176)
-                       return "Marvell 88E6176";
-               if (ret == PORT_SWITCH_ID_6320_A1)
-                       return "Marvell 88E6320 (A1)";
-               if (ret == PORT_SWITCH_ID_6320_A2)
-                       return "Marvell 88e6320 (A2)";
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6320)
-                       return "Marvell 88E6320";
-               if (ret == PORT_SWITCH_ID_6321_A1)
-                       return "Marvell 88E6321 (A1)";
-               if (ret == PORT_SWITCH_ID_6321_A2)
-                       return "Marvell 88e6321 (A2)";
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6321)
-                       return "Marvell 88E6321";
-               if (ret == PORT_SWITCH_ID_6352_A0)
-                       return "Marvell 88E6352 (A0)";
-               if (ret == PORT_SWITCH_ID_6352_A1)
-                       return "Marvell 88E6352 (A1)";
-               if ((ret & 0xfff0) == PORT_SWITCH_ID_6352)
-                       return "Marvell 88E6352";
-       }
-
-       return NULL;
+       return mv88e6xxx_lookup_name(host_dev, sw_addr, mv88e6352_table,
+                                    ARRAY_SIZE(mv88e6352_table));
 }
 
 static int mv88e6352_setup_global(struct dsa_switch *ds)
@@ -324,7 +307,6 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
        .set_addr               = mv88e6xxx_set_addr_indirect,
        .phy_read               = mv88e6xxx_phy_read_indirect,
        .phy_write              = mv88e6xxx_phy_write_indirect,
-       .poll_link              = mv88e6xxx_poll_link,
        .get_strings            = mv88e6xxx_get_strings,
        .get_ethtool_stats      = mv88e6xxx_get_ethtool_stats,
        .get_sset_count         = mv88e6xxx_get_sset_count,
@@ -341,17 +323,16 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
        .set_eeprom             = mv88e6352_set_eeprom,
        .get_regs_len           = mv88e6xxx_get_regs_len,
        .get_regs               = mv88e6xxx_get_regs,
-       .port_join_bridge       = mv88e6xxx_join_bridge,
-       .port_leave_bridge      = mv88e6xxx_leave_bridge,
        .port_stp_update        = mv88e6xxx_port_stp_update,
        .port_pvid_get          = mv88e6xxx_port_pvid_get,
-       .port_pvid_set          = mv88e6xxx_port_pvid_set,
+       .port_vlan_prepare      = mv88e6xxx_port_vlan_prepare,
        .port_vlan_add          = mv88e6xxx_port_vlan_add,
        .port_vlan_del          = mv88e6xxx_port_vlan_del,
        .vlan_getnext           = mv88e6xxx_vlan_getnext,
+       .port_fdb_prepare       = mv88e6xxx_port_fdb_prepare,
        .port_fdb_add           = mv88e6xxx_port_fdb_add,
        .port_fdb_del           = mv88e6xxx_port_fdb_del,
-       .port_fdb_getnext       = mv88e6xxx_port_fdb_getnext,
+       .port_fdb_dump          = mv88e6xxx_port_fdb_dump,
 };
 
 MODULE_ALIAS("platform:mv88e6172");
index 1f7dd927cc5ea4777530a8ba23daae631f258bf9..04cff58d771bd5a86f3b2a0bf2a047de1ee4b6f4 100644 (file)
@@ -11,7 +11,6 @@
  * (at your option) any later version.
  */
 
-#include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/phy.h>
-#include <linux/seq_file.h>
 #include <net/dsa.h>
+#include <net/switchdev.h>
 #include "mv88e6xxx.h"
 
-/* MDIO bus access can be nested in the case of PHYs connected to the
- * internal MDIO bus of the switch, which is accessed via MDIO bus of
- * the Ethernet interface. Avoid lockdep false positives by using
- * mutex_lock_nested().
- */
-static int mv88e6xxx_mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
-{
-       int ret;
-
-       mutex_lock_nested(&bus->mdio_lock, SINGLE_DEPTH_NESTING);
-       ret = bus->read(bus, addr, regnum);
-       mutex_unlock(&bus->mdio_lock);
-
-       return ret;
-}
-
-static int mv88e6xxx_mdiobus_write(struct mii_bus *bus, int addr, u32 regnum,
-                                  u16 val)
+static void assert_smi_lock(struct dsa_switch *ds)
 {
-       int ret;
-
-       mutex_lock_nested(&bus->mdio_lock, SINGLE_DEPTH_NESTING);
-       ret = bus->write(bus, addr, regnum, val);
-       mutex_unlock(&bus->mdio_lock);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
 
-       return ret;
+       if (unlikely(!mutex_is_locked(&ps->smi_mutex))) {
+               dev_err(ds->master_dev, "SMI lock not held!\n");
+               dump_stack();
+       }
 }
 
 /* If the switch's ADDR[4:0] strap pins are strapped to zero, it will
@@ -67,7 +48,7 @@ static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
        int i;
 
        for (i = 0; i < 16; i++) {
-               ret = mv88e6xxx_mdiobus_read(bus, sw_addr, SMI_CMD);
+               ret = mdiobus_read_nested(bus, sw_addr, SMI_CMD);
                if (ret < 0)
                        return ret;
 
@@ -78,12 +59,13 @@ static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
        return -ETIMEDOUT;
 }
 
-int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
+static int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr,
+                               int reg)
 {
        int ret;
 
        if (sw_addr == 0)
-               return mv88e6xxx_mdiobus_read(bus, addr, reg);
+               return mdiobus_read_nested(bus, addr, reg);
 
        /* Wait for the bus to become free. */
        ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
@@ -91,8 +73,8 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
                return ret;
 
        /* Transmit the read command. */
-       ret = mv88e6xxx_mdiobus_write(bus, sw_addr, SMI_CMD,
-                                     SMI_CMD_OP_22_READ | (addr << 5) | reg);
+       ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
+                                  SMI_CMD_OP_22_READ | (addr << 5) | reg);
        if (ret < 0)
                return ret;
 
@@ -102,19 +84,20 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
                return ret;
 
        /* Read the data. */
-       ret = mv88e6xxx_mdiobus_read(bus, sw_addr, SMI_DATA);
+       ret = mdiobus_read_nested(bus, sw_addr, SMI_DATA);
        if (ret < 0)
                return ret;
 
        return ret & 0xffff;
 }
 
-/* Must be called with SMI mutex held */
 static int _mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
 {
        struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev);
        int ret;
 
+       assert_smi_lock(ds);
+
        if (bus == NULL)
                return -EINVAL;
 
@@ -140,13 +123,13 @@ int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
        return ret;
 }
 
-int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
-                         int reg, u16 val)
+static int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
+                                int reg, u16 val)
 {
        int ret;
 
        if (sw_addr == 0)
-               return mv88e6xxx_mdiobus_write(bus, addr, reg, val);
+               return mdiobus_write_nested(bus, addr, reg, val);
 
        /* Wait for the bus to become free. */
        ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
@@ -154,13 +137,13 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
                return ret;
 
        /* Transmit the data to write. */
-       ret = mv88e6xxx_mdiobus_write(bus, sw_addr, SMI_DATA, val);
+       ret = mdiobus_write_nested(bus, sw_addr, SMI_DATA, val);
        if (ret < 0)
                return ret;
 
        /* Transmit the write command. */
-       ret = mv88e6xxx_mdiobus_write(bus, sw_addr, SMI_CMD,
-                                     SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
+       ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
+                                  SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
        if (ret < 0)
                return ret;
 
@@ -172,12 +155,13 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
        return 0;
 }
 
-/* Must be called with SMI mutex held */
 static int _mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg,
                                u16 val)
 {
        struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev);
 
+       assert_smi_lock(ds);
+
        if (bus == NULL)
                return -EINVAL;
 
@@ -233,7 +217,6 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
        return 0;
 }
 
-/* Must be called with SMI mutex held */
 static int _mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum)
 {
        if (addr >= 0)
@@ -241,7 +224,6 @@ static int _mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum)
        return 0xffff;
 }
 
-/* Must be called with SMI mutex held */
 static int _mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum,
                                u16 val)
 {
@@ -388,73 +370,6 @@ int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr,
 }
 #endif
 
-void mv88e6xxx_poll_link(struct dsa_switch *ds)
-{
-       int i;
-
-       for (i = 0; i < DSA_MAX_PORTS; i++) {
-               struct net_device *dev;
-               int uninitialized_var(port_status);
-               int pcs_ctrl;
-               int link;
-               int speed;
-               int duplex;
-               int fc;
-
-               dev = ds->ports[i];
-               if (dev == NULL)
-                       continue;
-
-               pcs_ctrl = mv88e6xxx_reg_read(ds, REG_PORT(i), PORT_PCS_CTRL);
-               if (pcs_ctrl < 0 || pcs_ctrl & PORT_PCS_CTRL_FORCE_LINK)
-                       continue;
-
-               link = 0;
-               if (dev->flags & IFF_UP) {
-                       port_status = mv88e6xxx_reg_read(ds, REG_PORT(i),
-                                                        PORT_STATUS);
-                       if (port_status < 0)
-                               continue;
-
-                       link = !!(port_status & PORT_STATUS_LINK);
-               }
-
-               if (!link) {
-                       if (netif_carrier_ok(dev)) {
-                               netdev_info(dev, "link down\n");
-                               netif_carrier_off(dev);
-                       }
-                       continue;
-               }
-
-               switch (port_status & PORT_STATUS_SPEED_MASK) {
-               case PORT_STATUS_SPEED_10:
-                       speed = 10;
-                       break;
-               case PORT_STATUS_SPEED_100:
-                       speed = 100;
-                       break;
-               case PORT_STATUS_SPEED_1000:
-                       speed = 1000;
-                       break;
-               default:
-                       speed = -1;
-                       break;
-               }
-               duplex = (port_status & PORT_STATUS_DUPLEX) ? 1 : 0;
-               fc = (port_status & PORT_STATUS_PAUSE_EN) ? 1 : 0;
-
-               if (!netif_carrier_ok(dev)) {
-                       netdev_info(dev,
-                                   "link up, %d Mb/s, %s duplex, flow control %sabled\n",
-                                   speed,
-                                   duplex ? "full" : "half",
-                                   fc ? "en" : "dis");
-                       netif_carrier_on(dev);
-               }
-       }
-}
-
 static bool mv88e6xxx_6065_family(struct dsa_switch *ds)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
@@ -574,7 +489,8 @@ void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
                           struct phy_device *phydev)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       u32 ret, reg;
+       u32 reg;
+       int ret;
 
        if (!phy_is_pseudo_fixed_link(phydev))
                return;
@@ -633,7 +549,6 @@ out:
        mutex_unlock(&ps->smi_mutex);
 }
 
-/* Must be called with SMI mutex held */
 static int _mv88e6xxx_stats_wait(struct dsa_switch *ds)
 {
        int ret;
@@ -648,7 +563,6 @@ static int _mv88e6xxx_stats_wait(struct dsa_switch *ds)
        return -ETIMEDOUT;
 }
 
-/* Must be called with SMI mutex held */
 static int _mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
 {
        int ret;
@@ -671,7 +585,6 @@ static int _mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
        return 0;
 }
 
-/* Must be called with SMI mutex held */
 static void _mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
 {
        u32 _val;
@@ -884,7 +797,6 @@ void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
        }
 }
 
-/* Must be called with SMI lock held */
 static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset,
                           u16 mask)
 {
@@ -934,21 +846,12 @@ int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
                              GLOBAL2_EEPROM_OP_BUSY);
 }
 
-/* Must be called with SMI lock held */
 static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
 {
        return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_ATU_OP,
                               GLOBAL_ATU_OP_BUSY);
 }
 
-/* Must be called with SMI lock held */
-static int _mv88e6xxx_scratch_wait(struct dsa_switch *ds)
-{
-       return _mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SCRATCH_MISC,
-                              GLOBAL2_SCRATCH_BUSY);
-}
-
-/* Must be called with SMI mutex held */
 static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr,
                                        int regnum)
 {
@@ -967,7 +870,6 @@ static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr,
        return _mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_SMI_DATA);
 }
 
-/* Must be called with SMI mutex held */
 static int _mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr,
                                         int regnum, u16 val)
 {
@@ -1036,14 +938,10 @@ out:
        return ret;
 }
 
-static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd)
+static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, u16 cmd)
 {
        int ret;
 
-       ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid);
-       if (ret < 0)
-               return ret;
-
        ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
        if (ret < 0)
                return ret;
@@ -1051,15 +949,93 @@ static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd)
        return _mv88e6xxx_atu_wait(ds);
 }
 
-static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid)
+static int _mv88e6xxx_atu_data_write(struct dsa_switch *ds,
+                                    struct mv88e6xxx_atu_entry *entry)
 {
-       int ret;
+       u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
 
-       ret = _mv88e6xxx_atu_wait(ds);
-       if (ret < 0)
-               return ret;
+       if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
+               unsigned int mask, shift;
+
+               if (entry->trunk) {
+                       data |= GLOBAL_ATU_DATA_TRUNK;
+                       mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
+                       shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
+               } else {
+                       mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
+                       shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
+               }
+
+               data |= (entry->portv_trunkid << shift) & mask;
+       }
+
+       return _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA, data);
+}
+
+static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds,
+                                    struct mv88e6xxx_atu_entry *entry,
+                                    bool static_too)
+{
+       int op;
+       int err;
+
+       err = _mv88e6xxx_atu_wait(ds);
+       if (err)
+               return err;
+
+       err = _mv88e6xxx_atu_data_write(ds, entry);
+       if (err)
+               return err;
+
+       if (entry->fid) {
+               err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID,
+                                          entry->fid);
+               if (err)
+                       return err;
+
+               op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
+                       GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
+       } else {
+               op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
+                       GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
+       }
+
+       return _mv88e6xxx_atu_cmd(ds, op);
+}
+
+static int _mv88e6xxx_atu_flush(struct dsa_switch *ds, u16 fid, bool static_too)
+{
+       struct mv88e6xxx_atu_entry entry = {
+               .fid = fid,
+               .state = 0, /* EntryState bits must be 0 */
+       };
 
-       return _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB);
+       return _mv88e6xxx_atu_flush_move(ds, &entry, static_too);
+}
+
+static int _mv88e6xxx_atu_move(struct dsa_switch *ds, u16 fid, int from_port,
+                              int to_port, bool static_too)
+{
+       struct mv88e6xxx_atu_entry entry = {
+               .trunk = false,
+               .fid = fid,
+       };
+
+       /* EntryState bits must be 0xF */
+       entry.state = GLOBAL_ATU_DATA_STATE_MASK;
+
+       /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
+       entry.portv_trunkid = (to_port & 0x0f) << 4;
+       entry.portv_trunkid |= from_port & 0x0f;
+
+       return _mv88e6xxx_atu_flush_move(ds, &entry, static_too);
+}
+
+static int _mv88e6xxx_atu_remove(struct dsa_switch *ds, u16 fid, int port,
+                                bool static_too)
+{
+       /* Destination port 0xF means remove the entries */
+       return _mv88e6xxx_atu_move(ds, fid, port, 0x0f, static_too);
 }
 
 static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
@@ -1084,7 +1060,7 @@ static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
                 */
                if (oldstate >= PORT_CONTROL_STATE_LEARNING &&
                    state <= PORT_CONTROL_STATE_BLOCKING) {
-                       ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]);
+                       ret = _mv88e6xxx_atu_remove(ds, 0, port, false);
                        if (ret)
                                goto abort;
                }
@@ -1098,130 +1074,21 @@ abort:
        return ret;
 }
 
-/* Must be called with smi lock held */
-static int _mv88e6xxx_update_port_config(struct dsa_switch *ds, int port)
-{
-       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       u8 fid = ps->fid[port];
-       u16 reg = fid << 12;
-
-       if (dsa_is_cpu_port(ds, port))
-               reg |= ds->phys_port_mask;
-       else
-               reg |= (ps->bridge_mask[fid] |
-                      (1 << dsa_upstream_port(ds))) & ~(1 << port);
-
-       return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg);
-}
-
-/* Must be called with smi lock held */
-static int _mv88e6xxx_update_bridge_config(struct dsa_switch *ds, int fid)
+static int _mv88e6xxx_port_vlan_map_set(struct dsa_switch *ds, int port,
+                                       u16 output_ports)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       int port;
-       u32 mask;
-       int ret;
-
-       mask = ds->phys_port_mask;
-       while (mask) {
-               port = __ffs(mask);
-               mask &= ~(1 << port);
-               if (ps->fid[port] != fid)
-                       continue;
-
-               ret = _mv88e6xxx_update_port_config(ds, port);
-               if (ret)
-                       return ret;
-       }
-
-       return _mv88e6xxx_flush_fid(ds, fid);
-}
-
-/* Bridge handling functions */
-
-int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
-{
-       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       int ret = 0;
-       u32 nmask;
-       int fid;
-
-       /* If the bridge group is not empty, join that group.
-        * Otherwise create a new group.
-        */
-       fid = ps->fid[port];
-       nmask = br_port_mask & ~(1 << port);
-       if (nmask)
-               fid = ps->fid[__ffs(nmask)];
-
-       nmask = ps->bridge_mask[fid] | (1 << port);
-       if (nmask != br_port_mask) {
-               netdev_err(ds->ports[port],
-                          "join: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n",
-                          fid, br_port_mask, nmask);
-               return -EINVAL;
-       }
-
-       mutex_lock(&ps->smi_mutex);
-
-       ps->bridge_mask[fid] = br_port_mask;
-
-       if (fid != ps->fid[port]) {
-               clear_bit(ps->fid[port], ps->fid_bitmap);
-               ps->fid[port] = fid;
-               ret = _mv88e6xxx_update_bridge_config(ds, fid);
-       }
-
-       mutex_unlock(&ps->smi_mutex);
-
-       return ret;
-}
-
-int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
-{
-       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       u8 fid, newfid;
-       int ret;
-
-       fid = ps->fid[port];
-
-       if (ps->bridge_mask[fid] != br_port_mask) {
-               netdev_err(ds->ports[port],
-                          "leave: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n",
-                          fid, br_port_mask, ps->bridge_mask[fid]);
-               return -EINVAL;
-       }
-
-       /* If the port was the last port of a bridge, we are done.
-        * Otherwise assign a new fid to the port, and fix up
-        * the bridge configuration.
-        */
-       if (br_port_mask == (1 << port))
-               return 0;
-
-       mutex_lock(&ps->smi_mutex);
-
-       newfid = find_next_zero_bit(ps->fid_bitmap, VLAN_N_VID, 1);
-       if (unlikely(newfid > ps->num_ports)) {
-               netdev_err(ds->ports[port], "all first %d FIDs are used\n",
-                          ps->num_ports);
-               ret = -ENOSPC;
-               goto unlock;
-       }
-
-       ps->fid[port] = newfid;
-       set_bit(newfid, ps->fid_bitmap);
-       ps->bridge_mask[fid] &= ~(1 << port);
-       ps->bridge_mask[newfid] = 1 << port;
+       const u16 mask = (1 << ps->num_ports) - 1;
+       int reg;
 
-       ret = _mv88e6xxx_update_bridge_config(ds, fid);
-       if (!ret)
-               ret = _mv88e6xxx_update_bridge_config(ds, newfid);
+       reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN);
+       if (reg < 0)
+               return reg;
 
-unlock:
-       mutex_unlock(&ps->smi_mutex);
+       reg &= ~mask;
+       reg |= output_ports & mask;
 
-       return ret;
+       return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg);
 }
 
 int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
@@ -1258,6 +1125,19 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
        return 0;
 }
 
+static int _mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
+{
+       int ret;
+
+       ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_DEFAULT_VLAN);
+       if (ret < 0)
+               return ret;
+
+       *pvid = ret & PORT_DEFAULT_VLAN_MASK;
+
+       return 0;
+}
+
 int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
 {
        int ret;
@@ -1271,9 +1151,9 @@ int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
        return 0;
 }
 
-int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid)
+static int _mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid)
 {
-       return mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
+       return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
                                   pvid & PORT_DEFAULT_VLAN_MASK);
 }
 
@@ -1359,7 +1239,13 @@ static int _mv88e6xxx_vtu_stu_data_write(struct dsa_switch *ds,
        return 0;
 }
 
-static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, u16 vid,
+static int _mv88e6xxx_vtu_vid_write(struct dsa_switch *ds, u16 vid)
+{
+       return _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID,
+                                   vid & GLOBAL_VTU_VID_MASK);
+}
+
+static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds,
                                  struct mv88e6xxx_vtu_stu_entry *entry)
 {
        struct mv88e6xxx_vtu_stu_entry next = { 0 };
@@ -1369,11 +1255,6 @@ static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, u16 vid,
        if (ret < 0)
                return ret;
 
-       ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID,
-                                  vid & GLOBAL_VTU_VID_MASK);
-       if (ret < 0)
-               return ret;
-
        ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_GET_NEXT);
        if (ret < 0)
                return ret;
@@ -1533,14 +1414,15 @@ static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid,
        struct mv88e6xxx_vtu_stu_entry vlan = {
                .valid = true,
                .vid = vid,
+               .fid = vid, /* We use one FID per VLAN */
        };
        int i;
 
-       /* exclude all ports except the CPU */
+       /* exclude all ports except the CPU and DSA ports */
        for (i = 0; i < ps->num_ports; ++i)
-               vlan.data[i] = dsa_is_cpu_port(ds, i) ?
-                       GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED :
-                       GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
+               vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
+                       ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
+                       GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
 
        if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
            mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
@@ -1566,123 +1448,148 @@ static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid,
                                return err;
                }
 
-               /* Non-bridged ports and bridge groups use FIDs from 1 to
-                * num_ports; VLANs use FIDs from num_ports+1 to 4095.
-                */
-               vlan.fid = find_next_zero_bit(ps->fid_bitmap, VLAN_N_VID,
-                                             ps->num_ports + 1);
-               if (unlikely(vlan.fid == VLAN_N_VID)) {
-                       pr_err("no more FID available for VLAN %d\n", vid);
-                       return -ENOSPC;
-               }
-
-               err = _mv88e6xxx_flush_fid(ds, vlan.fid);
+               /* Clear all MAC addresses from the new database */
+               err = _mv88e6xxx_atu_flush(ds, vlan.fid, true);
                if (err)
                        return err;
-
-               set_bit(vlan.fid, ps->fid_bitmap);
        }
 
        *entry = vlan;
        return 0;
 }
 
-int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
-                           bool untagged)
+int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
+                               const struct switchdev_obj_port_vlan *vlan,
+                               struct switchdev_trans *trans)
+{
+       /* We don't need any dynamic resource from the kernel (yet),
+        * so skip the prepare phase.
+        */
+       return 0;
+}
+
+static int _mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
+                                   bool untagged)
 {
-       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        struct mv88e6xxx_vtu_stu_entry vlan;
        int err;
 
-       mutex_lock(&ps->smi_mutex);
-       err = _mv88e6xxx_vtu_getnext(ds, vid - 1, &vlan);
+       err = _mv88e6xxx_vtu_vid_write(ds, vid - 1);
        if (err)
-               goto unlock;
+               return err;
+
+       err = _mv88e6xxx_vtu_getnext(ds, &vlan);
+       if (err)
+               return err;
 
        if (vlan.vid != vid || !vlan.valid) {
                err = _mv88e6xxx_vlan_init(ds, vid, &vlan);
                if (err)
-                       goto unlock;
+                       return err;
        }
 
        vlan.data[port] = untagged ?
                GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
                GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
 
-       err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
+       return _mv88e6xxx_vtu_loadpurge(ds, &vlan);
+}
+
+int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
+                           const struct switchdev_obj_port_vlan *vlan,
+                           struct switchdev_trans *trans)
+{
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+       bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+       u16 vid;
+       int err = 0;
+
+       mutex_lock(&ps->smi_mutex);
+
+       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+               err = _mv88e6xxx_port_vlan_add(ds, port, vid, untagged);
+               if (err)
+                       goto unlock;
+       }
+
+       /* no PVID with ranges, otherwise it's a bug */
+       if (pvid)
+               err = _mv88e6xxx_port_pvid_set(ds, port, vid);
 unlock:
        mutex_unlock(&ps->smi_mutex);
 
        return err;
 }
 
-int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
+static int _mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        struct mv88e6xxx_vtu_stu_entry vlan;
-       bool keep = false;
        int i, err;
 
-       mutex_lock(&ps->smi_mutex);
+       err = _mv88e6xxx_vtu_vid_write(ds, vid - 1);
+       if (err)
+               return err;
 
-       err = _mv88e6xxx_vtu_getnext(ds, vid - 1, &vlan);
+       err = _mv88e6xxx_vtu_getnext(ds, &vlan);
        if (err)
-               goto unlock;
+               return err;
 
        if (vlan.vid != vid || !vlan.valid ||
-           vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
-               err = -ENOENT;
-               goto unlock;
-       }
+           vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
+               return -ENOENT;
 
        vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
 
        /* keep the VLAN unless all ports are excluded */
+       vlan.valid = false;
        for (i = 0; i < ps->num_ports; ++i) {
-               if (dsa_is_cpu_port(ds, i))
+               if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
                        continue;
 
                if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
-                       keep = true;
+                       vlan.valid = true;
                        break;
                }
        }
 
-       vlan.valid = keep;
        err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
        if (err)
-               goto unlock;
-
-       if (!keep)
-               clear_bit(vlan.fid, ps->fid_bitmap);
-
-unlock:
-       mutex_unlock(&ps->smi_mutex);
+               return err;
 
-       return err;
+       return _mv88e6xxx_atu_remove(ds, vlan.fid, port, false);
 }
 
-static int _mv88e6xxx_port_vtu_getnext(struct dsa_switch *ds, int port, u16 vid,
-                                      struct mv88e6xxx_vtu_stu_entry *entry)
+int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
+                           const struct switchdev_obj_port_vlan *vlan)
 {
-       int err;
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       u16 pvid, vid;
+       int err = 0;
 
-       do {
-               if (vid == 4095)
-                       return -ENOENT;
+       mutex_lock(&ps->smi_mutex);
+
+       err = _mv88e6xxx_port_pvid_get(ds, port, &pvid);
+       if (err)
+               goto unlock;
 
-               err = _mv88e6xxx_vtu_getnext(ds, vid, entry);
+       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+               err = _mv88e6xxx_port_vlan_del(ds, port, vid);
                if (err)
-                       return err;
+                       goto unlock;
 
-               if (!entry->valid)
-                       return -ENOENT;
+               if (vid == pvid) {
+                       err = _mv88e6xxx_port_pvid_set(ds, port, 0);
+                       if (err)
+                               goto unlock;
+               }
+       }
 
-               vid = entry->vid;
-       } while (entry->data[port] != GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED &&
-                entry->data[port] != GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED);
+unlock:
+       mutex_unlock(&ps->smi_mutex);
 
-       return 0;
+       return err;
 }
 
 int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
@@ -1697,7 +1604,12 @@ int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
                return -ENOENT;
 
        mutex_lock(&ps->smi_mutex);
-       err = _mv88e6xxx_vtu_getnext(ds, *vid, &next);
+       err = _mv88e6xxx_vtu_vid_write(ds, *vid);
+       if (err)
+               goto unlock;
+
+       err = _mv88e6xxx_vtu_getnext(ds, &next);
+unlock:
        mutex_unlock(&ps->smi_mutex);
 
        if (err)
@@ -1712,7 +1624,7 @@ int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
                clear_bit(port, ports);
                clear_bit(port, untagged);
 
-               if (dsa_is_cpu_port(ds, port))
+               if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
                        continue;
 
                if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED ||
@@ -1761,7 +1673,6 @@ static int _mv88e6xxx_atu_mac_read(struct dsa_switch *ds, unsigned char *addr)
 static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
                               struct mv88e6xxx_atu_entry *entry)
 {
-       u16 reg = 0;
        int ret;
 
        ret = _mv88e6xxx_atu_wait(ds);
@@ -1772,47 +1683,15 @@ static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
        if (ret < 0)
                return ret;
 
-       if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
-               unsigned int mask, shift;
-
-               if (entry->trunk) {
-                       reg |= GLOBAL_ATU_DATA_TRUNK;
-                       mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
-                       shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
-               } else {
-                       mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
-                       shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
-               }
-
-               reg |= (entry->portv_trunkid << shift) & mask;
-       }
-
-       reg |= entry->state & GLOBAL_ATU_DATA_STATE_MASK;
-
-       ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA, reg);
+       ret = _mv88e6xxx_atu_data_write(ds, entry);
        if (ret < 0)
                return ret;
 
-       return _mv88e6xxx_atu_cmd(ds, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
-}
-
-static int _mv88e6xxx_port_vid_to_fid(struct dsa_switch *ds, int port, u16 vid)
-{
-       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       struct mv88e6xxx_vtu_stu_entry vlan;
-       int err;
-
-       if (vid == 0)
-               return ps->fid[port];
-
-       err = _mv88e6xxx_port_vtu_getnext(ds, port, vid - 1, &vlan);
-       if (err)
-               return err;
-
-       if (vlan.vid == vid)
-               return vlan.fid;
+       ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, entry->fid);
+       if (ret < 0)
+               return ret;
 
-       return -ENOENT;
+       return _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_LOAD_DB);
 }
 
 static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port,
@@ -1820,13 +1699,8 @@ static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port,
                                    u8 state)
 {
        struct mv88e6xxx_atu_entry entry = { 0 };
-       int ret;
-
-       ret = _mv88e6xxx_port_vid_to_fid(ds, port, vid);
-       if (ret < 0)
-               return ret;
 
-       entry.fid = ret;
+       entry.fid = vid; /* We use one FID per VLAN */
        entry.state = state;
        ether_addr_copy(entry.mac, addr);
        if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
@@ -1837,30 +1711,45 @@ static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port,
        return _mv88e6xxx_atu_load(ds, &entry);
 }
 
+int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
+                              const struct switchdev_obj_port_fdb *fdb,
+                              struct switchdev_trans *trans)
+{
+       /* We don't use per-port FDB */
+       if (fdb->vid == 0)
+               return -EOPNOTSUPP;
+
+       /* We don't need any dynamic resource from the kernel (yet),
+        * so skip the prepare phase.
+        */
+       return 0;
+}
+
 int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
-                          const unsigned char *addr, u16 vid)
+                          const struct switchdev_obj_port_fdb *fdb,
+                          struct switchdev_trans *trans)
 {
-       int state = is_multicast_ether_addr(addr) ?
+       int state = is_multicast_ether_addr(fdb->addr) ?
                GLOBAL_ATU_DATA_STATE_MC_STATIC :
                GLOBAL_ATU_DATA_STATE_UC_STATIC;
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
 
        mutex_lock(&ps->smi_mutex);
-       ret = _mv88e6xxx_port_fdb_load(ds, port, addr, vid, state);
+       ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid, state);
        mutex_unlock(&ps->smi_mutex);
 
        return ret;
 }
 
 int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
-                          const unsigned char *addr, u16 vid)
+                          const struct switchdev_obj_port_fdb *fdb)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
 
        mutex_lock(&ps->smi_mutex);
-       ret = _mv88e6xxx_port_fdb_load(ds, port, addr, vid,
+       ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid,
                                       GLOBAL_ATU_DATA_STATE_UNUSED);
        mutex_unlock(&ps->smi_mutex);
 
@@ -1868,7 +1757,6 @@ int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
 }
 
 static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid,
-                                 const unsigned char *addr,
                                  struct mv88e6xxx_atu_entry *entry)
 {
        struct mv88e6xxx_atu_entry next = { 0 };
@@ -1880,11 +1768,11 @@ static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid,
        if (ret < 0)
                return ret;
 
-       ret = _mv88e6xxx_atu_mac_write(ds, addr);
+       ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid);
        if (ret < 0)
                return ret;
 
-       ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
+       ret = _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_GET_NEXT_DB);
        if (ret < 0)
                return ret;
 
@@ -1917,51 +1805,69 @@ static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid,
        return 0;
 }
 
-/* get next entry for port */
-int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
-                              unsigned char *addr, u16 *vid, bool *is_static)
+int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
+                           struct switchdev_obj_port_fdb *fdb,
+                           int (*cb)(struct switchdev_obj *obj))
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       struct mv88e6xxx_atu_entry next;
-       u16 fid;
-       int ret;
+       struct mv88e6xxx_vtu_stu_entry vlan = {
+               .vid = GLOBAL_VTU_VID_MASK, /* all ones */
+       };
+       int err;
 
        mutex_lock(&ps->smi_mutex);
 
-       ret = _mv88e6xxx_port_vid_to_fid(ds, port, *vid);
-       if (ret < 0)
+       err = _mv88e6xxx_vtu_vid_write(ds, vlan.vid);
+       if (err)
                goto unlock;
-       fid = ret;
 
        do {
-               if (is_broadcast_ether_addr(addr)) {
-                       struct mv88e6xxx_vtu_stu_entry vtu;
+               struct mv88e6xxx_atu_entry addr = {
+                       .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+               };
 
-                       ret = _mv88e6xxx_port_vtu_getnext(ds, port, *vid, &vtu);
-                       if (ret < 0)
-                               goto unlock;
+               err = _mv88e6xxx_vtu_getnext(ds, &vlan);
+               if (err)
+                       goto unlock;
 
-                       *vid = vtu.vid;
-                       fid = vtu.fid;
-               }
+               if (!vlan.valid)
+                       break;
 
-               ret = _mv88e6xxx_atu_getnext(ds, fid, addr, &next);
-               if (ret < 0)
+               err = _mv88e6xxx_atu_mac_write(ds, addr.mac);
+               if (err)
                        goto unlock;
 
-               ether_addr_copy(addr, next.mac);
+               do {
+                       err = _mv88e6xxx_atu_getnext(ds, vlan.fid, &addr);
+                       if (err)
+                               goto unlock;
 
-               if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED)
-                       continue;
-       } while (next.trunk || (next.portv_trunkid & BIT(port)) == 0);
+                       if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
+                               break;
+
+                       if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
+                               bool is_static = addr.state ==
+                                       (is_multicast_ether_addr(addr.mac) ?
+                                        GLOBAL_ATU_DATA_STATE_MC_STATIC :
+                                        GLOBAL_ATU_DATA_STATE_UC_STATIC);
+
+                               fdb->vid = vlan.vid;
+                               ether_addr_copy(fdb->addr, addr.mac);
+                               fdb->ndm_state = is_static ? NUD_NOARP :
+                                       NUD_REACHABLE;
+
+                               err = cb(&fdb->obj);
+                               if (err)
+                                       goto unlock;
+                       }
+               } while (!is_broadcast_ether_addr(addr.mac));
+
+       } while (vlan.vid < GLOBAL_VTU_VID_MASK);
 
-       *is_static = next.state == (is_multicast_ether_addr(addr) ?
-                                   GLOBAL_ATU_DATA_STATE_MC_STATIC :
-                                   GLOBAL_ATU_DATA_STATE_UC_STATIC);
 unlock:
        mutex_unlock(&ps->smi_mutex);
 
-       return ret;
+       return err;
 }
 
 static void mv88e6xxx_bridge_work(struct work_struct *work)
@@ -1983,7 +1889,7 @@ static void mv88e6xxx_bridge_work(struct work_struct *work)
 static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       int ret, fid;
+       int ret;
        u16 reg;
 
        mutex_lock(&ps->smi_mutex);
@@ -2109,7 +2015,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
                        reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
        }
 
-       reg |= PORT_CONTROL_2_8021Q_FALLBACK;
+       reg |= PORT_CONTROL_2_8021Q_SECURE;
 
        if (reg) {
                ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
@@ -2123,8 +2029,12 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
         * a port bitmap that has only the bit for this port set and
         * the other bits clear.
         */
-       ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_ASSOC_VECTOR,
-                                  1 << port);
+       reg = 1 << port;
+       /* Disable learning for DSA and CPU ports */
+       if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+               reg = PORT_ASSOC_VECTOR_LOCKED_PORT;
+
+       ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_ASSOC_VECTOR, reg);
        if (ret)
                goto abort;
 
@@ -2202,19 +2112,11 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
        if (ret)
                goto abort;
 
-       /* Port based VLAN map: give each port its own address
-        * database, allow the CPU port to talk to each of the 'real'
-        * ports, and allow each of the 'real' ports to only talk to
-        * the upstream port.
+       /* Port based VLAN map: do not give each port its own address
+        * database, and allow every port to egress frames on all other ports.
         */
-       fid = port + 1;
-       ps->fid[port] = fid;
-       set_bit(fid, ps->fid_bitmap);
-
-       if (!dsa_is_cpu_port(ds, port))
-               ps->bridge_mask[fid] = 1 << port;
-
-       ret = _mv88e6xxx_update_port_config(ds, port);
+       reg = BIT(ps->num_ports) - 1; /* all ports */
+       ret = _mv88e6xxx_port_vlan_map_set(ds, port, reg & ~port);
        if (ret)
                goto abort;
 
@@ -2242,267 +2144,9 @@ int mv88e6xxx_setup_ports(struct dsa_switch *ds)
        return 0;
 }
 
-static int mv88e6xxx_regs_show(struct seq_file *s, void *p)
-{
-       struct dsa_switch *ds = s->private;
-
-       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       int reg, port;
-
-       seq_puts(s, "    GLOBAL GLOBAL2 ");
-       for (port = 0 ; port < ps->num_ports; port++)
-               seq_printf(s, " %2d  ", port);
-       seq_puts(s, "\n");
-
-       for (reg = 0; reg < 32; reg++) {
-               seq_printf(s, "%2x: ", reg);
-               seq_printf(s, " %4x    %4x  ",
-                          mv88e6xxx_reg_read(ds, REG_GLOBAL, reg),
-                          mv88e6xxx_reg_read(ds, REG_GLOBAL2, reg));
-
-               for (port = 0 ; port < ps->num_ports; port++)
-                       seq_printf(s, "%4x ",
-                                  mv88e6xxx_reg_read(ds, REG_PORT(port), reg));
-               seq_puts(s, "\n");
-       }
-
-       return 0;
-}
-
-static int mv88e6xxx_regs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, mv88e6xxx_regs_show, inode->i_private);
-}
-
-static const struct file_operations mv88e6xxx_regs_fops = {
-       .open   = mv88e6xxx_regs_open,
-       .read   = seq_read,
-       .llseek = no_llseek,
-       .release = single_release,
-       .owner  = THIS_MODULE,
-};
-
-static void mv88e6xxx_atu_show_header(struct seq_file *s)
-{
-       seq_puts(s, "DB   T/P  Vec State Addr\n");
-}
-
-static void mv88e6xxx_atu_show_entry(struct seq_file *s, int dbnum,
-                                    unsigned char *addr, int data)
-{
-       bool trunk = !!(data & GLOBAL_ATU_DATA_TRUNK);
-       int portvec = ((data & GLOBAL_ATU_DATA_PORT_VECTOR_MASK) >>
-                      GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT);
-       int state = data & GLOBAL_ATU_DATA_STATE_MASK;
-
-       seq_printf(s, "%03x %5s %10pb   %x   %pM\n",
-                  dbnum, (trunk ? "Trunk" : "Port"), &portvec, state, addr);
-}
-
-static int mv88e6xxx_atu_show_db(struct seq_file *s, struct dsa_switch *ds,
-                                int dbnum)
-{
-       unsigned char bcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-       unsigned char addr[6];
-       int ret, data, state;
-
-       ret = _mv88e6xxx_atu_mac_write(ds, bcast);
-       if (ret < 0)
-               return ret;
-
-       do {
-               ret = _mv88e6xxx_atu_cmd(ds, dbnum, GLOBAL_ATU_OP_GET_NEXT_DB);
-               if (ret < 0)
-                       return ret;
-               data = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
-               if (data < 0)
-                       return data;
-
-               state = data & GLOBAL_ATU_DATA_STATE_MASK;
-               if (state == GLOBAL_ATU_DATA_STATE_UNUSED)
-                       break;
-               ret = _mv88e6xxx_atu_mac_read(ds, addr);
-               if (ret < 0)
-                       return ret;
-               mv88e6xxx_atu_show_entry(s, dbnum, addr, data);
-       } while (state != GLOBAL_ATU_DATA_STATE_UNUSED);
-
-       return 0;
-}
-
-static int mv88e6xxx_atu_show(struct seq_file *s, void *p)
-{
-       struct dsa_switch *ds = s->private;
-       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       int dbnum;
-
-       mv88e6xxx_atu_show_header(s);
-
-       for (dbnum = 0; dbnum < 255; dbnum++) {
-               mutex_lock(&ps->smi_mutex);
-               mv88e6xxx_atu_show_db(s, ds, dbnum);
-               mutex_unlock(&ps->smi_mutex);
-       }
-
-       return 0;
-}
-
-static int mv88e6xxx_atu_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, mv88e6xxx_atu_show, inode->i_private);
-}
-
-static const struct file_operations mv88e6xxx_atu_fops = {
-       .open   = mv88e6xxx_atu_open,
-       .read   = seq_read,
-       .llseek = no_llseek,
-       .release = single_release,
-       .owner  = THIS_MODULE,
-};
-
-static void mv88e6xxx_stats_show_header(struct seq_file *s,
-                                       struct mv88e6xxx_priv_state *ps)
-{
-       int port;
-
-       seq_puts(s, "      Statistic       ");
-       for (port = 0 ; port < ps->num_ports; port++)
-               seq_printf(s, "Port %2d  ", port);
-       seq_puts(s, "\n");
-}
-
-static int mv88e6xxx_stats_show(struct seq_file *s, void *p)
-{
-       struct dsa_switch *ds = s->private;
-       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       struct mv88e6xxx_hw_stat *stats = mv88e6xxx_hw_stats;
-       int port, stat, max_stats;
-       uint64_t value;
-
-       if (have_sw_in_discards(ds))
-               max_stats = ARRAY_SIZE(mv88e6xxx_hw_stats);
-       else
-               max_stats = ARRAY_SIZE(mv88e6xxx_hw_stats) - 3;
-
-       mv88e6xxx_stats_show_header(s, ps);
-
-       mutex_lock(&ps->smi_mutex);
-
-       for (stat = 0; stat < max_stats; stat++) {
-               seq_printf(s, "%19s: ", stats[stat].string);
-               for (port = 0 ; port < ps->num_ports; port++) {
-                       _mv88e6xxx_stats_snapshot(ds, port);
-                       value = _mv88e6xxx_get_ethtool_stat(ds, stat, stats,
-                                                           port);
-                       seq_printf(s, "%8llu ", value);
-               }
-               seq_puts(s, "\n");
-       }
-       mutex_unlock(&ps->smi_mutex);
-
-       return 0;
-}
-
-static int mv88e6xxx_stats_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, mv88e6xxx_stats_show, inode->i_private);
-}
-
-static const struct file_operations mv88e6xxx_stats_fops = {
-       .open   = mv88e6xxx_stats_open,
-       .read   = seq_read,
-       .llseek = no_llseek,
-       .release = single_release,
-       .owner  = THIS_MODULE,
-};
-
-static int mv88e6xxx_device_map_show(struct seq_file *s, void *p)
-{
-       struct dsa_switch *ds = s->private;
-       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       int target, ret;
-
-       seq_puts(s, "Target Port\n");
-
-       mutex_lock(&ps->smi_mutex);
-       for (target = 0; target < 32; target++) {
-               ret = _mv88e6xxx_reg_write(
-                       ds, REG_GLOBAL2, GLOBAL2_DEVICE_MAPPING,
-                       target << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT);
-               if (ret < 0)
-                       goto out;
-               ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL2,
-                                         GLOBAL2_DEVICE_MAPPING);
-               seq_printf(s, "  %2d   %2d\n", target,
-                          ret & GLOBAL2_DEVICE_MAPPING_PORT_MASK);
-       }
-out:
-       mutex_unlock(&ps->smi_mutex);
-
-       return 0;
-}
-
-static int mv88e6xxx_device_map_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, mv88e6xxx_device_map_show, inode->i_private);
-}
-
-static const struct file_operations mv88e6xxx_device_map_fops = {
-       .open   = mv88e6xxx_device_map_open,
-       .read   = seq_read,
-       .llseek = no_llseek,
-       .release = single_release,
-       .owner  = THIS_MODULE,
-};
-
-static int mv88e6xxx_scratch_show(struct seq_file *s, void *p)
-{
-       struct dsa_switch *ds = s->private;
-       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       int reg, ret;
-
-       seq_puts(s, "Register Value\n");
-
-       mutex_lock(&ps->smi_mutex);
-       for (reg = 0; reg < 0x80; reg++) {
-               ret = _mv88e6xxx_reg_write(
-                       ds, REG_GLOBAL2, GLOBAL2_SCRATCH_MISC,
-                       reg << GLOBAL2_SCRATCH_REGISTER_SHIFT);
-               if (ret < 0)
-                       goto out;
-
-               ret = _mv88e6xxx_scratch_wait(ds);
-               if (ret < 0)
-                       goto out;
-
-               ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL2,
-                                         GLOBAL2_SCRATCH_MISC);
-               seq_printf(s, "  %2x   %2x\n", reg,
-                          ret & GLOBAL2_SCRATCH_VALUE_MASK);
-       }
-out:
-       mutex_unlock(&ps->smi_mutex);
-
-       return 0;
-}
-
-static int mv88e6xxx_scratch_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, mv88e6xxx_scratch_show, inode->i_private);
-}
-
-static const struct file_operations mv88e6xxx_scratch_fops = {
-       .open   = mv88e6xxx_scratch_open,
-       .read   = seq_read,
-       .llseek = no_llseek,
-       .release = single_release,
-       .owner  = THIS_MODULE,
-};
-
 int mv88e6xxx_setup_common(struct dsa_switch *ds)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-       char *name;
 
        mutex_init(&ps->smi_mutex);
 
@@ -2510,24 +2154,6 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds)
 
        INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
 
-       name = kasprintf(GFP_KERNEL, "dsa%d", ds->index);
-       ps->dbgfs = debugfs_create_dir(name, NULL);
-       kfree(name);
-
-       debugfs_create_file("regs", S_IRUGO, ps->dbgfs, ds,
-                           &mv88e6xxx_regs_fops);
-
-       debugfs_create_file("atu", S_IRUGO, ps->dbgfs, ds,
-                           &mv88e6xxx_atu_fops);
-
-       debugfs_create_file("stats", S_IRUGO, ps->dbgfs, ds,
-                           &mv88e6xxx_stats_fops);
-
-       debugfs_create_file("device_map", S_IRUGO, ps->dbgfs, ds,
-                           &mv88e6xxx_device_map_fops);
-
-       debugfs_create_file("scratch", S_IRUGO, ps->dbgfs, ds,
-                           &mv88e6xxx_scratch_fops);
        return 0;
 }
 
@@ -2638,6 +2264,11 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
        if (ret < 0)
                goto unlock;
 
+       /* Clear all ATU entries */
+       ret = _mv88e6xxx_atu_flush(ds, 0, true);
+       if (ret < 0)
+               goto unlock;
+
        /* Clear all the VTU and STU entries */
        ret = _mv88e6xxx_vtu_stu_flush(ds);
 unlock:
@@ -2920,6 +2551,38 @@ int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
 }
 #endif /* CONFIG_NET_DSA_HWMON */
 
+char *mv88e6xxx_lookup_name(struct device *host_dev, int sw_addr,
+                           const struct mv88e6xxx_switch_id *table,
+                           unsigned int num)
+{
+       struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
+       int i, ret;
+
+       if (!bus)
+               return NULL;
+
+       ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
+       if (ret < 0)
+               return NULL;
+
+       /* Look up the exact switch ID */
+       for (i = 0; i < num; ++i)
+               if (table[i].id == ret)
+                       return table[i].name;
+
+       /* Look up only the product number */
+       for (i = 0; i < num; ++i) {
+               if (table[i].id == (ret & PORT_SWITCH_ID_PROD_NUM_MASK)) {
+                       dev_warn(host_dev, "unknown revision %d, using base switch 0x%x\n",
+                                ret & PORT_SWITCH_ID_REV_MASK,
+                                ret & PORT_SWITCH_ID_PROD_NUM_MASK);
+                       return table[i].name;
+               }
+       }
+
+       return NULL;
+}
+
 static int __init mv88e6xxx_init(void)
 {
 #if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
index 9b6f3d9d5ae1cfa9034f1a2fb381d631ecd0d017..fb9a8739712f3cfc8a4f1736d594d221dedeeb89 100644 (file)
@@ -60,6 +60,8 @@
 #define PORT_PCS_CTRL_UNFORCED         0x03
 #define PORT_PAUSE_CTRL                0x02
 #define PORT_SWITCH_ID         0x03
+#define PORT_SWITCH_ID_PROD_NUM_MASK   0xfff0
+#define PORT_SWITCH_ID_REV_MASK                0x000f
 #define PORT_SWITCH_ID_6031    0x0310
 #define PORT_SWITCH_ID_6035    0x0350
 #define PORT_SWITCH_ID_6046    0x0480
 #define PORT_RATE_CONTROL      0x09
 #define PORT_RATE_CONTROL_2    0x0a
 #define PORT_ASSOC_VECTOR      0x0b
+#define PORT_ASSOC_VECTOR_HOLD_AT_1            BIT(15)
+#define PORT_ASSOC_VECTOR_INT_AGE_OUT          BIT(14)
+#define PORT_ASSOC_VECTOR_LOCKED_PORT          BIT(13)
+#define PORT_ASSOC_VECTOR_IGNORE_WRONG         BIT(12)
+#define PORT_ASSOC_VECTOR_REFRESH_LOCKED       BIT(11)
 #define PORT_ATU_CONTROL       0x0c
 #define PORT_PRI_OVERRIDE      0x0d
 #define PORT_ETH_TYPE          0x0f
 #define GLOBAL_ATU_OP          0x0b
 #define GLOBAL_ATU_OP_BUSY     BIT(15)
 #define GLOBAL_ATU_OP_NOP              (0 << 12)
-#define GLOBAL_ATU_OP_FLUSH_ALL                ((1 << 12) | GLOBAL_ATU_OP_BUSY)
-#define GLOBAL_ATU_OP_FLUSH_NON_STATIC ((2 << 12) | GLOBAL_ATU_OP_BUSY)
+#define GLOBAL_ATU_OP_FLUSH_MOVE_ALL           ((1 << 12) | GLOBAL_ATU_OP_BUSY)
+#define GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC    ((2 << 12) | GLOBAL_ATU_OP_BUSY)
 #define GLOBAL_ATU_OP_LOAD_DB          ((3 << 12) | GLOBAL_ATU_OP_BUSY)
 #define GLOBAL_ATU_OP_GET_NEXT_DB      ((4 << 12) | GLOBAL_ATU_OP_BUSY)
-#define GLOBAL_ATU_OP_FLUSH_DB         ((5 << 12) | GLOBAL_ATU_OP_BUSY)
-#define GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB ((6 << 12) | GLOBAL_ATU_OP_BUSY)
+#define GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB                ((5 << 12) | GLOBAL_ATU_OP_BUSY)
+#define GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB ((6 << 12) | GLOBAL_ATU_OP_BUSY)
 #define GLOBAL_ATU_OP_GET_CLR_VIOLATION          ((7 << 12) | GLOBAL_ATU_OP_BUSY)
 #define GLOBAL_ATU_DATA                0x0c
 #define GLOBAL_ATU_DATA_TRUNK                  BIT(15)
 #define GLOBAL2_QOS_WEIGHT     0x1c
 #define GLOBAL2_MISC           0x1d
 
+struct mv88e6xxx_switch_id {
+       u16 id;
+       char *name;
+};
+
 struct mv88e6xxx_atu_entry {
        u16     fid;
        u8      state;
@@ -402,18 +414,10 @@ struct mv88e6xxx_priv_state {
        int             id; /* switch product id */
        int             num_ports;      /* number of switch ports */
 
-       /* hw bridging */
-
-       DECLARE_BITMAP(fid_bitmap, VLAN_N_VID); /* FIDs 1 to 4095 available */
-       u16 fid[DSA_MAX_PORTS];                 /* per (non-bridged) port FID */
-       u16 bridge_mask[DSA_MAX_PORTS];         /* br groups (indexed by FID) */
-
        unsigned long port_state_update_mask;
        u8 port_state[DSA_MAX_PORTS];
 
        struct work_struct bridge_work;
-
-       struct dentry *dbgfs;
 };
 
 struct mv88e6xxx_hw_stat {
@@ -423,13 +427,13 @@ struct mv88e6xxx_hw_stat {
 };
 
 int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active);
+char *mv88e6xxx_lookup_name(struct device *host_dev, int sw_addr,
+                           const struct mv88e6xxx_switch_id *table,
+                           unsigned int num);
 int mv88e6xxx_setup_ports(struct dsa_switch *ds);
 int mv88e6xxx_setup_common(struct dsa_switch *ds);
 int mv88e6xxx_setup_global(struct dsa_switch *ds);
-int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg);
 int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg);
-int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
-                         int reg, u16 val);
 int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val);
 int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr);
 int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr);
@@ -442,7 +446,6 @@ void mv88e6xxx_ppu_state_init(struct dsa_switch *ds);
 int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum);
 int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr,
                            int regnum, u16 val);
-void mv88e6xxx_poll_link(struct dsa_switch *ds);
 void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data);
 void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
                                 uint64_t *data);
@@ -465,22 +468,29 @@ int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum,
 int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
 int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
                      struct phy_device *phydev, struct ethtool_eee *e);
-int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
-int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
 int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state);
+int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
+                               const struct switchdev_obj_port_vlan *vlan,
+                               struct switchdev_trans *trans);
+int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
+                           const struct switchdev_obj_port_vlan *vlan,
+                           struct switchdev_trans *trans);
+int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
+                           const struct switchdev_obj_port_vlan *vlan);
 int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *vid);
-int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 vid);
-int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
-                           bool untagged);
-int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid);
 int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
                           unsigned long *ports, unsigned long *untagged);
+int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
+                              const struct switchdev_obj_port_fdb *fdb,
+                              struct switchdev_trans *trans);
 int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
-                          const unsigned char *addr, u16 vid);
+                          const struct switchdev_obj_port_fdb *fdb,
+                          struct switchdev_trans *trans);
 int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
-                          const unsigned char *addr, u16 vid);
-int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
-                              unsigned char *addr, u16 *vid, bool *is_static);
+                          const struct switchdev_obj_port_fdb *fdb);
+int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
+                           struct switchdev_obj_port_fdb *fdb,
+                           int (*cb)(struct switchdev_obj *obj));
 int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg);
 int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
                             int reg, int val);
index 815eb94990f5edb50883bf9d9a4f68a1dc535e65..69fc8409a9733ffe2f6f312f0955bb506657355b 100644 (file)
@@ -147,8 +147,12 @@ static void dummy_setup(struct net_device *dev)
        dev->flags |= IFF_NOARP;
        dev->flags &= ~IFF_MULTICAST;
        dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE;
-       dev->features   |= NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO;
+       dev->features   |= NETIF_F_SG | NETIF_F_FRAGLIST;
+       dev->features   |= NETIF_F_ALL_TSO | NETIF_F_UFO;
        dev->features   |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX;
+       dev->features   |= NETIF_F_GSO_ENCAP_ALL;
+       dev->hw_features |= dev->features;
+       dev->hw_enc_features |= dev->features;
        eth_hw_addr_random(dev);
 }
 
index edf72258ab1ddabe9364246c3195856fd71b590b..29c3075bfb052f1dbc7e788bf093bd0b8a94f152 100644 (file)
@@ -64,7 +64,7 @@ config ARM_ETHERH
          should say Y to this option if you wish to use it with Linux.
 
 config MAC8390
-       bool "Macintosh NS 8390 based ethernet cards"
+       tristate "Macintosh NS 8390 based ethernet cards"
        depends on MAC
        select CRC32
        ---help---
index 65cf60f6718c52fae6ebacdf3e1e4cf5444383d2..b9283901136e974a9ad4207cae898cb492207700 100644 (file)
@@ -454,34 +454,22 @@ MODULE_AUTHOR("David Huggins-Daines <dhd@debian.org> and others");
 MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver");
 MODULE_LICENSE("GPL");
 
-/* overkill, of course */
-static struct net_device *dev_mac8390[15];
-int init_module(void)
+static struct net_device *dev_mac8390;
+
+int __init init_module(void)
 {
-       int i;
-       for (i = 0; i < 15; i++) {
-               struct net_device *dev = mac8390_probe(-1);
-               if (IS_ERR(dev))
-                       break;
-               dev_mac890[i] = dev;
-       }
-       if (!i) {
-               pr_notice("No useable cards found, driver NOT installed.\n");
-               return -ENODEV;
+       dev_mac8390 = mac8390_probe(-1);
+       if (IS_ERR(dev_mac8390)) {
+               pr_warn("mac8390: No card found\n");
+               return PTR_ERR(dev_mac8390);
        }
        return 0;
 }
 
-void cleanup_module(void)
+void __exit cleanup_module(void)
 {
-       int i;
-       for (i = 0; i < 15; i++) {
-               struct net_device *dev = dev_mac890[i];
-               if (dev) {
-                       unregister_netdev(dev);
-                       free_netdev(dev);
-               }
-       }
+       unregister_netdev(dev_mac8390);
+       free_netdev(dev_mac8390);
 }
 
 #endif /* MODULE */
index ae89de7deb132587e0ea0993f1ade6f01c19228d..20bf55dbd76f074d4d525fd133bfe4cf7f83f103 100644 (file)
@@ -1141,8 +1141,6 @@ static void greth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *in
        strlcpy(info->version, "revision: 1.0", sizeof(info->version));
        strlcpy(info->bus_info, greth->dev->bus->name, sizeof(info->bus_info));
        strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
-       info->eedump_len = 0;
-       info->regdump_len = sizeof(struct greth_regs);
 }
 
 static void greth_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
index 48ce83e443c2b12bbcbc2e2545c5ac6b1983f3d0..8d50314ac3eb1f308d1cc556270058aba05c7b60 100644 (file)
@@ -847,21 +847,25 @@ static int emac_probe(struct platform_device *pdev)
        if (ndev->irq == -ENXIO) {
                netdev_err(ndev, "No irq resource\n");
                ret = ndev->irq;
-               goto out;
+               goto out_iounmap;
        }
 
        db->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(db->clk)) {
                ret = PTR_ERR(db->clk);
-               goto out;
+               goto out_iounmap;
        }
 
-       clk_prepare_enable(db->clk);
+       ret = clk_prepare_enable(db->clk);
+       if (ret) {
+               dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", ret);
+               goto out_iounmap;
+       }
 
        ret = sunxi_sram_claim(&pdev->dev);
        if (ret) {
                dev_err(&pdev->dev, "Error couldn't map SRAM to device\n");
-               goto out;
+               goto out_clk_disable_unprepare;
        }
 
        db->phy_node = of_parse_phandle(np, "phy", 0);
@@ -910,6 +914,10 @@ static int emac_probe(struct platform_device *pdev)
 
 out_release_sram:
        sunxi_sram_release(&pdev->dev);
+out_clk_disable_unprepare:
+       clk_disable_unprepare(db->clk);
+out_iounmap:
+       iounmap(db->membase);
 out:
        dev_err(db->dev, "not found (%d).\n", ret);
 
@@ -921,8 +929,12 @@ out:
 static int emac_remove(struct platform_device *pdev)
 {
        struct net_device *ndev = platform_get_drvdata(pdev);
+       struct emac_board_info *db = netdev_priv(ndev);
 
        unregister_netdev(ndev);
+       sunxi_sram_release(&pdev->dev);
+       clk_disable_unprepare(db->clk);
+       iounmap(db->membase);
        free_netdev(ndev);
 
        dev_dbg(&pdev->dev, "released and freed device\n");
index 98a10d555b793e029d20b6694ba1fab7cd13a053..66d0b73c39c03ba2a050c70e49f6cba4498f47c1 100644 (file)
@@ -661,6 +661,7 @@ void lance_poll(struct net_device *dev)
        spin_unlock(&lp->devlock);
        lance_interrupt(dev->irq, dev);
 }
+EXPORT_SYMBOL_GPL(lance_poll);
 #endif
 
 MODULE_LICENSE("GPL");
index afc62ea804fc35d416e60bb4585b938b16a4fdac..0038709fd317d83ca8f4226cab3357673c25565f 100644 (file)
@@ -100,7 +100,7 @@ config DECLANCE
          DEPCA series.  (This chipset is better known via the NE2100 cards.)
 
 config HPLANCE
-       bool "HP on-board LANCE support"
+       tristate "HP on-board LANCE support"
        depends on DIO
        select CRC32
        ---help---
index cb367cc59e0b65985069991b30c96d5c43362bd6..5330bcb8a9448a39c35331d38d4d23dbd3581543 100644 (file)
@@ -714,7 +714,6 @@ au1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
        strlcpy(info->version, DRV_VERSION, sizeof(info->version));
        snprintf(info->bus_info, sizeof(info->bus_info), "%s %d", DRV_NAME,
                 aup->mac_id);
-       info->regdump_len = 0;
 }
 
 static void au1000_set_msglevel(struct net_device *dev, u32 value)
index a4473d8ff4fa0e1ec7bbdb511f9edd51f1871d71..970781a9e67777a87b0b93f0cf8bf31d4541d855 100644 (file)
@@ -1595,7 +1595,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
                                  packet->rdesc_count, 1);
 
        /* Make sure ownership is written to the descriptor */
-       dma_wmb();
+       smp_wmb();
 
        ring->cur = cur_index + 1;
        if (!packet->skb->xmit_more ||
@@ -1940,84 +1940,31 @@ static void xgbe_config_mtl_mode(struct xgbe_prv_data *pdata)
 static unsigned int xgbe_calculate_per_queue_fifo(unsigned int fifo_size,
                                                  unsigned int queue_count)
 {
-       unsigned int q_fifo_size = 0;
-       enum xgbe_mtl_fifo_size p_fifo = XGMAC_MTL_FIFO_SIZE_256;
+       unsigned int q_fifo_size;
+       unsigned int p_fifo;
 
-       /* Calculate Tx/Rx fifo share per queue */
-       switch (fifo_size) {
-       case 0:
-               q_fifo_size = XGBE_FIFO_SIZE_B(128);
-               break;
-       case 1:
-               q_fifo_size = XGBE_FIFO_SIZE_B(256);
-               break;
-       case 2:
-               q_fifo_size = XGBE_FIFO_SIZE_B(512);
-               break;
-       case 3:
-               q_fifo_size = XGBE_FIFO_SIZE_KB(1);
-               break;
-       case 4:
-               q_fifo_size = XGBE_FIFO_SIZE_KB(2);
-               break;
-       case 5:
-               q_fifo_size = XGBE_FIFO_SIZE_KB(4);
-               break;
-       case 6:
-               q_fifo_size = XGBE_FIFO_SIZE_KB(8);
-               break;
-       case 7:
-               q_fifo_size = XGBE_FIFO_SIZE_KB(16);
-               break;
-       case 8:
-               q_fifo_size = XGBE_FIFO_SIZE_KB(32);
-               break;
-       case 9:
-               q_fifo_size = XGBE_FIFO_SIZE_KB(64);
-               break;
-       case 10:
-               q_fifo_size = XGBE_FIFO_SIZE_KB(128);
-               break;
-       case 11:
-               q_fifo_size = XGBE_FIFO_SIZE_KB(256);
-               break;
-       }
+       /* Calculate the configured fifo size */
+       q_fifo_size = 1 << (fifo_size + 7);
 
-       /* The configured value is not the actual amount of fifo RAM */
+       /* The configured value may not be the actual amount of fifo RAM */
        q_fifo_size = min_t(unsigned int, XGBE_FIFO_MAX, q_fifo_size);
 
        q_fifo_size = q_fifo_size / queue_count;
 
-       /* Set the queue fifo size programmable value */
-       if (q_fifo_size >= XGBE_FIFO_SIZE_KB(256))
-               p_fifo = XGMAC_MTL_FIFO_SIZE_256K;
-       else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(128))
-               p_fifo = XGMAC_MTL_FIFO_SIZE_128K;
-       else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(64))
-               p_fifo = XGMAC_MTL_FIFO_SIZE_64K;
-       else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(32))
-               p_fifo = XGMAC_MTL_FIFO_SIZE_32K;
-       else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(16))
-               p_fifo = XGMAC_MTL_FIFO_SIZE_16K;
-       else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(8))
-               p_fifo = XGMAC_MTL_FIFO_SIZE_8K;
-       else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(4))
-               p_fifo = XGMAC_MTL_FIFO_SIZE_4K;
-       else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(2))
-               p_fifo = XGMAC_MTL_FIFO_SIZE_2K;
-       else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(1))
-               p_fifo = XGMAC_MTL_FIFO_SIZE_1K;
-       else if (q_fifo_size >= XGBE_FIFO_SIZE_B(512))
-               p_fifo = XGMAC_MTL_FIFO_SIZE_512;
-       else if (q_fifo_size >= XGBE_FIFO_SIZE_B(256))
-               p_fifo = XGMAC_MTL_FIFO_SIZE_256;
+       /* Each increment in the queue fifo size represents 256 bytes of
+        * fifo, with 0 representing 256 bytes. Distribute the fifo equally
+        * between the queues.
+        */
+       p_fifo = q_fifo_size / 256;
+       if (p_fifo)
+               p_fifo--;
 
        return p_fifo;
 }
 
 static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata)
 {
-       enum xgbe_mtl_fifo_size fifo_size;
+       unsigned int fifo_size;
        unsigned int i;
 
        fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size,
@@ -2033,7 +1980,7 @@ static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata)
 
 static void xgbe_config_rx_fifo_size(struct xgbe_prv_data *pdata)
 {
-       enum xgbe_mtl_fifo_size fifo_size;
+       unsigned int fifo_size;
        unsigned int i;
 
        fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.rx_fifo_size,
@@ -2224,7 +2171,7 @@ static u64 xgbe_mmc_read(struct xgbe_prv_data *pdata, unsigned int reg_lo)
 
        default:
                read_hi = false;
-       };
+       }
 
        val = XGMAC_IOREAD(pdata, reg_lo);
 
index aae9d5ecd1822b16a2812de3bee503f59113adaa..53ce1222b11d13fc8649bc00bb3b083d0ee23525 100644 (file)
@@ -360,6 +360,9 @@ static irqreturn_t xgbe_isr(int irq, void *data)
                        }
                }
 
+               if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RBU))
+                       pdata->ext_stats.rx_buffer_unavailable++;
+
                /* Restart the device on a Fatal Bus Error */
                if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE))
                        schedule_work(&pdata->restart_work);
@@ -384,7 +387,8 @@ static irqreturn_t xgbe_isr(int irq, void *data)
                                /* Read Tx Timestamp to clear interrupt */
                                pdata->tx_tstamp =
                                        hw_if->get_tx_tstamp(pdata);
-                               schedule_work(&pdata->tx_tstamp_work);
+                               queue_work(pdata->dev_workqueue,
+                                          &pdata->tx_tstamp_work);
                        }
                }
        }
@@ -450,7 +454,7 @@ static void xgbe_service_timer(unsigned long data)
 {
        struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data;
 
-       schedule_work(&pdata->service_work);
+       queue_work(pdata->dev_workqueue, &pdata->service_work);
 
        mod_timer(&pdata->service_timer, jiffies + HZ);
 }
@@ -891,7 +895,7 @@ static int xgbe_start(struct xgbe_prv_data *pdata)
        netif_tx_start_all_queues(netdev);
 
        xgbe_start_timers(pdata);
-       schedule_work(&pdata->service_work);
+       queue_work(pdata->dev_workqueue, &pdata->service_work);
 
        DBGPR("<--xgbe_start\n");
 
@@ -1807,6 +1811,7 @@ static int xgbe_tx_poll(struct xgbe_channel *channel)
        struct netdev_queue *txq;
        int processed = 0;
        unsigned int tx_packets = 0, tx_bytes = 0;
+       unsigned int cur;
 
        DBGPR("-->xgbe_tx_poll\n");
 
@@ -1814,10 +1819,15 @@ static int xgbe_tx_poll(struct xgbe_channel *channel)
        if (!ring)
                return 0;
 
+       cur = ring->cur;
+
+       /* Be sure we get ring->cur before accessing descriptor data */
+       smp_rmb();
+
        txq = netdev_get_tx_queue(netdev, channel->queue_index);
 
        while ((processed < XGBE_TX_DESC_MAX_PROC) &&
-              (ring->dirty != ring->cur)) {
+              (ring->dirty != cur)) {
                rdata = XGBE_GET_DESC_DATA(ring, ring->dirty);
                rdesc = rdata->rdesc;
 
index 59e090e95c0e8648cfc27cac1fc63981c102df11..6040293db9c1694083ace197771a09245feea637 100644 (file)
@@ -179,6 +179,7 @@ static const struct xgbe_stats xgbe_gstring_stats[] = {
        XGMAC_MMC_STAT("rx_watchdog_errors", rxwatchdogerror),
        XGMAC_MMC_STAT("rx_pause_frames", rxpauseframes),
        XGMAC_EXT_STAT("rx_split_header_packets", rx_split_header_packets),
+       XGMAC_EXT_STAT("rx_buffer_unavailable", rx_buffer_unavailable),
 };
 
 #define XGBE_STATS_COUNT       ARRAY_SIZE(xgbe_gstring_stats)
@@ -187,8 +188,6 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
 {
        int i;
 
-       DBGPR("-->%s\n", __func__);
-
        switch (stringset) {
        case ETH_SS_STATS:
                for (i = 0; i < XGBE_STATS_COUNT; i++) {
@@ -198,8 +197,6 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
                }
                break;
        }
-
-       DBGPR("<--%s\n", __func__);
 }
 
 static void xgbe_get_ethtool_stats(struct net_device *netdev,
@@ -209,23 +206,17 @@ static void xgbe_get_ethtool_stats(struct net_device *netdev,
        u8 *stat;
        int i;
 
-       DBGPR("-->%s\n", __func__);
-
        pdata->hw_if.read_mmc_stats(pdata);
        for (i = 0; i < XGBE_STATS_COUNT; i++) {
                stat = (u8 *)pdata + xgbe_gstring_stats[i].stat_offset;
                *data++ = *(u64 *)stat;
        }
-
-       DBGPR("<--%s\n", __func__);
 }
 
 static int xgbe_get_sset_count(struct net_device *netdev, int stringset)
 {
        int ret;
 
-       DBGPR("-->%s\n", __func__);
-
        switch (stringset) {
        case ETH_SS_STATS:
                ret = XGBE_STATS_COUNT;
@@ -235,8 +226,6 @@ static int xgbe_get_sset_count(struct net_device *netdev, int stringset)
                ret = -EOPNOTSUPP;
        }
 
-       DBGPR("<--%s\n", __func__);
-
        return ret;
 }
 
@@ -245,13 +234,9 @@ static void xgbe_get_pauseparam(struct net_device *netdev,
 {
        struct xgbe_prv_data *pdata = netdev_priv(netdev);
 
-       DBGPR("-->xgbe_get_pauseparam\n");
-
        pause->autoneg = pdata->phy.pause_autoneg;
        pause->tx_pause = pdata->phy.tx_pause;
        pause->rx_pause = pdata->phy.rx_pause;
-
-       DBGPR("<--xgbe_get_pauseparam\n");
 }
 
 static int xgbe_set_pauseparam(struct net_device *netdev,
@@ -260,13 +245,11 @@ static int xgbe_set_pauseparam(struct net_device *netdev,
        struct xgbe_prv_data *pdata = netdev_priv(netdev);
        int ret = 0;
 
-       DBGPR("-->xgbe_set_pauseparam\n");
-
-       DBGPR("  autoneg = %d, tx_pause = %d, rx_pause = %d\n",
-             pause->autoneg, pause->tx_pause, pause->rx_pause);
-
-       if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE))
+       if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE)) {
+               netdev_err(netdev,
+                          "autoneg disabled, pause autoneg not avialable\n");
                return -EINVAL;
+       }
 
        pdata->phy.pause_autoneg = pause->autoneg;
        pdata->phy.tx_pause = pause->tx_pause;
@@ -286,8 +269,6 @@ static int xgbe_set_pauseparam(struct net_device *netdev,
        if (netif_running(netdev))
                ret = pdata->phy_if.phy_config_aneg(pdata);
 
-       DBGPR("<--xgbe_set_pauseparam\n");
-
        return ret;
 }
 
@@ -296,8 +277,6 @@ static int xgbe_get_settings(struct net_device *netdev,
 {
        struct xgbe_prv_data *pdata = netdev_priv(netdev);
 
-       DBGPR("-->xgbe_get_settings\n");
-
        cmd->phy_address = pdata->phy.address;
 
        cmd->supported = pdata->phy.supported;
@@ -311,8 +290,6 @@ static int xgbe_get_settings(struct net_device *netdev,
        cmd->port = PORT_NONE;
        cmd->transceiver = XCVR_INTERNAL;
 
-       DBGPR("<--xgbe_get_settings\n");
-
        return 0;
 }
 
@@ -323,16 +300,20 @@ static int xgbe_set_settings(struct net_device *netdev,
        u32 speed;
        int ret;
 
-       DBGPR("-->xgbe_set_settings\n");
-
        speed = ethtool_cmd_speed(cmd);
 
-       if (cmd->phy_address != pdata->phy.address)
+       if (cmd->phy_address != pdata->phy.address) {
+               netdev_err(netdev, "invalid phy address %hhu\n",
+                          cmd->phy_address);
                return -EINVAL;
+       }
 
        if ((cmd->autoneg != AUTONEG_ENABLE) &&
-           (cmd->autoneg != AUTONEG_DISABLE))
+           (cmd->autoneg != AUTONEG_DISABLE)) {
+               netdev_err(netdev, "unsupported autoneg %hhu\n",
+                          cmd->autoneg);
                return -EINVAL;
+       }
 
        if (cmd->autoneg == AUTONEG_DISABLE) {
                switch (speed) {
@@ -341,16 +322,27 @@ static int xgbe_set_settings(struct net_device *netdev,
                case SPEED_1000:
                        break;
                default:
+                       netdev_err(netdev, "unsupported speed %u\n", speed);
                        return -EINVAL;
                }
 
-               if (cmd->duplex != DUPLEX_FULL)
+               if (cmd->duplex != DUPLEX_FULL) {
+                       netdev_err(netdev, "unsupported duplex %hhu\n",
+                                  cmd->duplex);
                        return -EINVAL;
+               }
        }
 
+       netif_dbg(pdata, link, netdev,
+                 "requested advertisement %#x, phy supported %#x\n",
+                 cmd->advertising, pdata->phy.supported);
+
        cmd->advertising &= pdata->phy.supported;
-       if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising)
+       if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising) {
+               netdev_err(netdev,
+                          "unsupported requested advertisement\n");
                return -EINVAL;
+       }
 
        ret = 0;
        pdata->phy.autoneg = cmd->autoneg;
@@ -366,8 +358,6 @@ static int xgbe_set_settings(struct net_device *netdev,
        if (netif_running(netdev))
                ret = pdata->phy_if.phy_config_aneg(pdata);
 
-       DBGPR("<--xgbe_set_settings\n");
-
        return ret;
 }
 
@@ -385,7 +375,20 @@ static void xgbe_get_drvinfo(struct net_device *netdev,
                 XGMAC_GET_BITS(hw_feat->version, MAC_VR, USERVER),
                 XGMAC_GET_BITS(hw_feat->version, MAC_VR, DEVID),
                 XGMAC_GET_BITS(hw_feat->version, MAC_VR, SNPSVER));
-       drvinfo->n_stats = XGBE_STATS_COUNT;
+}
+
+static u32 xgbe_get_msglevel(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       return pdata->msg_enable;
+}
+
+static void xgbe_set_msglevel(struct net_device *netdev, u32 msglevel)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       pdata->msg_enable = msglevel;
 }
 
 static int xgbe_get_coalesce(struct net_device *netdev,
@@ -393,8 +396,6 @@ static int xgbe_get_coalesce(struct net_device *netdev,
 {
        struct xgbe_prv_data *pdata = netdev_priv(netdev);
 
-       DBGPR("-->xgbe_get_coalesce\n");
-
        memset(ec, 0, sizeof(struct ethtool_coalesce));
 
        ec->rx_coalesce_usecs = pdata->rx_usecs;
@@ -402,8 +403,6 @@ static int xgbe_get_coalesce(struct net_device *netdev,
 
        ec->tx_max_coalesced_frames = pdata->tx_frames;
 
-       DBGPR("<--xgbe_get_coalesce\n");
-
        return 0;
 }
 
@@ -415,8 +414,6 @@ static int xgbe_set_coalesce(struct net_device *netdev,
        unsigned int rx_frames, rx_riwt, rx_usecs;
        unsigned int tx_frames;
 
-       DBGPR("-->xgbe_set_coalesce\n");
-
        /* Check for not supported parameters  */
        if ((ec->rx_coalesce_usecs_irq) ||
            (ec->rx_max_coalesced_frames_irq) ||
@@ -436,8 +433,10 @@ static int xgbe_set_coalesce(struct net_device *netdev,
            (ec->rx_max_coalesced_frames_high) ||
            (ec->tx_coalesce_usecs_high) ||
            (ec->tx_max_coalesced_frames_high) ||
-           (ec->rate_sample_interval))
+           (ec->rate_sample_interval)) {
+               netdev_err(netdev, "unsupported coalescing parameter\n");
                return -EOPNOTSUPP;
+       }
 
        rx_riwt = hw_if->usec_to_riwt(pdata, ec->rx_coalesce_usecs);
        rx_usecs = ec->rx_coalesce_usecs;
@@ -449,13 +448,13 @@ static int xgbe_set_coalesce(struct net_device *netdev,
 
        /* Check the bounds of values for Rx */
        if (rx_riwt > XGMAC_MAX_DMA_RIWT) {
-               netdev_alert(netdev, "rx-usec is limited to %d usecs\n",
-                            hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT));
+               netdev_err(netdev, "rx-usec is limited to %d usecs\n",
+                          hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT));
                return -EINVAL;
        }
        if (rx_frames > pdata->rx_desc_count) {
-               netdev_alert(netdev, "rx-frames is limited to %d frames\n",
-                            pdata->rx_desc_count);
+               netdev_err(netdev, "rx-frames is limited to %d frames\n",
+                          pdata->rx_desc_count);
                return -EINVAL;
        }
 
@@ -463,8 +462,8 @@ static int xgbe_set_coalesce(struct net_device *netdev,
 
        /* Check the bounds of values for Tx */
        if (tx_frames > pdata->tx_desc_count) {
-               netdev_alert(netdev, "tx-frames is limited to %d frames\n",
-                            pdata->tx_desc_count);
+               netdev_err(netdev, "tx-frames is limited to %d frames\n",
+                          pdata->tx_desc_count);
                return -EINVAL;
        }
 
@@ -476,8 +475,6 @@ static int xgbe_set_coalesce(struct net_device *netdev,
        pdata->tx_frames = tx_frames;
        hw_if->config_tx_coalesce(pdata);
 
-       DBGPR("<--xgbe_set_coalesce\n");
-
        return 0;
 }
 
@@ -539,8 +536,10 @@ static int xgbe_set_rxfh(struct net_device *netdev, const u32 *indir,
        struct xgbe_hw_if *hw_if = &pdata->hw_if;
        unsigned int ret;
 
-       if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
+       if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) {
+               netdev_err(netdev, "unsupported hash function\n");
                return -EOPNOTSUPP;
+       }
 
        if (indir) {
                ret = hw_if->set_rss_lookup_table(pdata, indir);
@@ -594,6 +593,8 @@ static const struct ethtool_ops xgbe_ethtool_ops = {
        .get_settings = xgbe_get_settings,
        .set_settings = xgbe_set_settings,
        .get_drvinfo = xgbe_get_drvinfo,
+       .get_msglevel = xgbe_get_msglevel,
+       .set_msglevel = xgbe_set_msglevel,
        .get_link = ethtool_op_get_link,
        .get_coalesce = xgbe_get_coalesce,
        .set_coalesce = xgbe_set_coalesce,
index e83bd76abce66fd6623e3b05944b581c1027c97e..7dd893331785ebce1740280dfdee1acb13de0a52 100644 (file)
@@ -371,7 +371,7 @@ static int xgbe_probe(struct platform_device *pdev)
        set_bit(XGBE_DOWN, &pdata->dev_state);
 
        /* Check if we should use ACPI or DT */
-       pdata->use_acpi = (!pdata->adev || acpi_disabled) ? 0 : 1;
+       pdata->use_acpi = dev->of_node ? 0 : 1;
 
        phy_pdev = xgbe_get_phy_pdev(pdata);
        if (!phy_pdev) {
index 9088c3a35a207ec6a60e1b1eb10e112f1e2f2e90..4460580818665d80e04ad6e53039d7d7e7f5fc99 100644 (file)
@@ -1115,8 +1115,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata)
        unsigned int reg, link_aneg;
 
        if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) {
-               if (test_and_clear_bit(XGBE_LINK, &pdata->dev_state))
-                       netif_carrier_off(pdata->netdev);
+               netif_carrier_off(pdata->netdev);
 
                pdata->phy.link = 0;
                goto adjust_link;
@@ -1142,10 +1141,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata)
                if (test_bit(XGBE_LINK_INIT, &pdata->dev_state))
                        clear_bit(XGBE_LINK_INIT, &pdata->dev_state);
 
-               if (!test_bit(XGBE_LINK, &pdata->dev_state)) {
-                       set_bit(XGBE_LINK, &pdata->dev_state);
-                       netif_carrier_on(pdata->netdev);
-               }
+               netif_carrier_on(pdata->netdev);
        } else {
                if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) {
                        xgbe_check_link_timeout(pdata);
@@ -1156,10 +1152,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata)
 
                xgbe_phy_status_aneg(pdata);
 
-               if (test_bit(XGBE_LINK, &pdata->dev_state)) {
-                       clear_bit(XGBE_LINK, &pdata->dev_state);
-                       netif_carrier_off(pdata->netdev);
-               }
+               netif_carrier_off(pdata->netdev);
        }
 
 adjust_link:
@@ -1179,8 +1172,7 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
        devm_free_irq(pdata->dev, pdata->an_irq, pdata);
 
        pdata->phy.link = 0;
-       if (test_and_clear_bit(XGBE_LINK, &pdata->dev_state))
-               netif_carrier_off(pdata->netdev);
+       netif_carrier_off(pdata->netdev);
 
        xgbe_phy_adjust_link(pdata);
 }
index 8c9d01ef730d7eba47aede2bbc62d6292fb67e6f..e234b9970318a37ec1e5f3048fe86c8aa4dcf919 100644 (file)
 #define XGMAC_IOCTL_CONTEXT    2
 
 #define XGBE_FIFO_MAX          81920
-#define XGBE_FIFO_SIZE_B(x)    (x)
-#define XGBE_FIFO_SIZE_KB(x)   (x * 1024)
 
 #define XGBE_TC_MIN_QUANTUM    10
 
@@ -461,7 +459,6 @@ struct xgbe_channel {
 
 enum xgbe_state {
        XGBE_DOWN,
-       XGBE_LINK,
        XGBE_LINK_INIT,
        XGBE_LINK_ERR,
 };
@@ -483,20 +480,6 @@ enum xgbe_int_state {
        XGMAC_INT_STATE_RESTORE,
 };
 
-enum xgbe_mtl_fifo_size {
-       XGMAC_MTL_FIFO_SIZE_256  = 0x00,
-       XGMAC_MTL_FIFO_SIZE_512  = 0x01,
-       XGMAC_MTL_FIFO_SIZE_1K   = 0x03,
-       XGMAC_MTL_FIFO_SIZE_2K   = 0x07,
-       XGMAC_MTL_FIFO_SIZE_4K   = 0x0f,
-       XGMAC_MTL_FIFO_SIZE_8K   = 0x1f,
-       XGMAC_MTL_FIFO_SIZE_16K  = 0x3f,
-       XGMAC_MTL_FIFO_SIZE_32K  = 0x7f,
-       XGMAC_MTL_FIFO_SIZE_64K  = 0xff,
-       XGMAC_MTL_FIFO_SIZE_128K = 0x1ff,
-       XGMAC_MTL_FIFO_SIZE_256K = 0x3ff,
-};
-
 enum xgbe_speed {
        XGBE_SPEED_1000 = 0,
        XGBE_SPEED_2500,
@@ -598,6 +581,7 @@ struct xgbe_mmc_stats {
 struct xgbe_ext_stats {
        u64 tx_tso_packets;
        u64 rx_split_header_packets;
+       u64 rx_buffer_unavailable;
 };
 
 struct xgbe_hw_if {
index c4bb8027b3fb0824a38f012a02de57d372b76494..33850a0f7e823b08a322cd53107904da7d1f78da 100644 (file)
@@ -107,7 +107,8 @@ static void xgene_enet_set_ring_state(struct xgene_enet_desc_ring *ring)
 {
        xgene_enet_ring_set_type(ring);
 
-       if (xgene_enet_ring_owner(ring->id) == RING_OWNER_ETH0)
+       if (xgene_enet_ring_owner(ring->id) == RING_OWNER_ETH0 ||
+           xgene_enet_ring_owner(ring->id) == RING_OWNER_ETH1)
                xgene_enet_ring_set_recombbuf(ring);
 
        xgene_enet_ring_init(ring);
@@ -460,6 +461,7 @@ static void xgene_gmac_reset(struct xgene_enet_pdata *pdata)
 
 static void xgene_gmac_init(struct xgene_enet_pdata *pdata)
 {
+       struct device *dev = &pdata->pdev->dev;
        u32 value, mc2;
        u32 intf_ctl, rgmii;
        u32 icm0, icm2;
@@ -489,7 +491,12 @@ static void xgene_gmac_init(struct xgene_enet_pdata *pdata)
        default:
                ENET_INTERFACE_MODE2_SET(&mc2, 2);
                intf_ctl |= ENET_GHD_MODE;
-               CFG_TXCLK_MUXSEL0_SET(&rgmii, 4);
+
+               if (dev->of_node) {
+                       CFG_TXCLK_MUXSEL0_SET(&rgmii, pdata->tx_delay);
+                       CFG_RXCLK_MUXSEL0_SET(&rgmii, pdata->rx_delay);
+               }
+
                xgene_enet_rd_csr(pdata, DEBUG_REG_ADDR, &value);
                value |= CFG_BYPASS_UNISEC_TX | CFG_BYPASS_UNISEC_RX;
                xgene_enet_wr_csr(pdata, DEBUG_REG_ADDR, value);
index ff05bbcff26dc94feccd898faca2a381cbeef7c4..6dee73c3c1e132a7be7387e6e72be7c9b5239afe 100644 (file)
@@ -144,6 +144,7 @@ enum xgene_enet_rm {
 #define CFG_BYPASS_UNISEC_RX           BIT(1)
 #define CFG_CLE_BYPASS_EN0             BIT(31)
 #define CFG_TXCLK_MUXSEL0_SET(dst, val)        xgene_set_bits(dst, val, 29, 3)
+#define CFG_RXCLK_MUXSEL0_SET(dst, val)        xgene_set_bits(dst, val, 26, 3)
 
 #define CFG_CLE_IP_PROTOCOL0_SET(dst, val)     xgene_set_bits(dst, val, 16, 2)
 #define CFG_CLE_DSTQID0_SET(dst, val)          xgene_set_bits(dst, val, 0, 12)
index e47298faf78dedbdf1f562fe17c636793e19717f..ce1068771b327e711fd1723065f9f3ad3c5dadc3 100644 (file)
@@ -1118,6 +1118,47 @@ static int xgene_get_port_id_dt(struct device *dev, struct xgene_enet_pdata *pda
        return ret;
 }
 
+static int xgene_get_tx_delay(struct xgene_enet_pdata *pdata)
+{
+       struct device *dev = &pdata->pdev->dev;
+       int delay, ret;
+
+       ret = of_property_read_u32(dev->of_node, "tx-delay", &delay);
+       if (ret) {
+               pdata->tx_delay = 4;
+               return 0;
+       }
+
+       if (delay < 0 || delay > 7) {
+               dev_err(dev, "Invalid tx-delay specified\n");
+               return -EINVAL;
+       }
+
+       pdata->tx_delay = delay;
+
+       return 0;
+}
+
+static int xgene_get_rx_delay(struct xgene_enet_pdata *pdata)
+{
+       struct device *dev = &pdata->pdev->dev;
+       int delay, ret;
+
+       ret = of_property_read_u32(dev->of_node, "rx-delay", &delay);
+       if (ret) {
+               pdata->rx_delay = 2;
+               return 0;
+       }
+
+       if (delay < 0 || delay > 7) {
+               dev_err(dev, "Invalid rx-delay specified\n");
+               return -EINVAL;
+       }
+
+       pdata->rx_delay = delay;
+
+       return 0;
+}
 
 static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
 {
@@ -1194,6 +1235,14 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
                return -ENODEV;
        }
 
+       ret = xgene_get_tx_delay(pdata);
+       if (ret)
+               return ret;
+
+       ret = xgene_get_rx_delay(pdata);
+       if (ret)
+               return ret;
+
        ret = platform_get_irq(pdev, 0);
        if (ret <= 0) {
                dev_err(dev, "Unable to get ENET Rx IRQ\n");
@@ -1305,10 +1354,17 @@ static void xgene_enet_setup_ops(struct xgene_enet_pdata *pdata)
                        pdata->ring_num = START_RING_NUM_0;
                        break;
                case 1:
-                       pdata->cpu_bufnum = START_CPU_BUFNUM_1;
-                       pdata->eth_bufnum = START_ETH_BUFNUM_1;
-                       pdata->bp_bufnum = START_BP_BUFNUM_1;
-                       pdata->ring_num = START_RING_NUM_1;
+                       if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
+                               pdata->cpu_bufnum = XG_START_CPU_BUFNUM_1;
+                               pdata->eth_bufnum = XG_START_ETH_BUFNUM_1;
+                               pdata->bp_bufnum = XG_START_BP_BUFNUM_1;
+                               pdata->ring_num = XG_START_RING_NUM_1;
+                       } else {
+                               pdata->cpu_bufnum = START_CPU_BUFNUM_1;
+                               pdata->eth_bufnum = START_ETH_BUFNUM_1;
+                               pdata->bp_bufnum = START_BP_BUFNUM_1;
+                               pdata->ring_num = START_RING_NUM_1;
+                       }
                        break;
                default:
                        break;
@@ -1478,6 +1534,7 @@ static const struct acpi_device_id xgene_enet_acpi_match[] = {
        { "APMC0D05", XGENE_ENET1},
        { "APMC0D30", XGENE_ENET1},
        { "APMC0D31", XGENE_ENET1},
+       { "APMC0D3F", XGENE_ENET1},
        { "APMC0D26", XGENE_ENET2},
        { "APMC0D25", XGENE_ENET2},
        { }
index 50f92c39ed2a2de60c6f3b20975646df990e89ff..a6e56b88c0a07a6bee7a1f0e6a050db802e5e989 100644 (file)
 #define START_BP_BUFNUM_1      0x2A
 #define START_RING_NUM_1       264
 
+#define XG_START_CPU_BUFNUM_1  12
+#define XG_START_ETH_BUFNUM_1  2
+#define XG_START_BP_BUFNUM_1   0x22
+#define XG_START_RING_NUM_1    264
+
 #define X2_START_CPU_BUFNUM_0  0
 #define X2_START_ETH_BUFNUM_0  0
 #define X2_START_BP_BUFNUM_0   0x20
@@ -179,6 +184,8 @@ struct xgene_enet_pdata {
        u8 bp_bufnum;
        u16 ring_num;
        u32 mss;
+       u8 tx_delay;
+       u8 rx_delay;
 };
 
 struct xgene_indirect_ctl {
index d19a41b0c6d26691fa2050a546d585a7e73df6c5..31071297896c96b5343f1261666d9abcaf5b9ffe 100644 (file)
@@ -51,7 +51,7 @@ config BMAC
          will be called bmac.
 
 config MACMACE
-       bool "Macintosh (AV) onboard MACE ethernet"
+       tristate "Macintosh (AV) onboard MACE ethernet"
        depends on MAC
        select CRC32
        ---help---
index 48694c239d5cee024055731d2aa586e57fe529e5..872b7abb01962e2d70d327c6f7137241fedd9008 100644 (file)
@@ -233,10 +233,6 @@ static void atl1c_get_drvinfo(struct net_device *netdev,
                sizeof(drvinfo->version));
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->n_stats = 0;
-       drvinfo->testinfo_len = 0;
-       drvinfo->regdump_len = atl1c_get_regs_len(netdev);
-       drvinfo->eedump_len = atl1c_get_eeprom_len(netdev);
 }
 
 static void atl1c_get_wol(struct net_device *netdev,
index 1be072f4afc2a261b5504302db5dba50b81ad268..8e3dbd4d9f79eab2ac291f903816667d6f13b72b 100644 (file)
@@ -316,10 +316,6 @@ static void atl1e_get_drvinfo(struct net_device *netdev,
        strlcpy(drvinfo->fw_version, "L1e", sizeof(drvinfo->fw_version));
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->n_stats = 0;
-       drvinfo->testinfo_len = 0;
-       drvinfo->regdump_len = atl1e_get_regs_len(netdev);
-       drvinfo->eedump_len = atl1e_get_eeprom_len(netdev);
 }
 
 static void atl1e_get_wol(struct net_device *netdev,
index eca1d113fee187ccb310fbd6c3ba2c4b535a9114..529bca718334acd15395c020a5a0ca4c86b4154a 100644 (file)
@@ -3388,7 +3388,6 @@ static void atl1_get_drvinfo(struct net_device *netdev,
                sizeof(drvinfo->version));
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->eedump_len = ATL1_EEDUMP_LEN;
 }
 
 static void atl1_get_wol(struct net_device *netdev,
index 46a535318c7af64ea586baf230067f9e2d4ade5b..8f76f4558a88c15b0a14bb6ec3d9fb769fea9d0a 100644 (file)
@@ -2030,10 +2030,6 @@ static void atl2_get_drvinfo(struct net_device *netdev,
        strlcpy(drvinfo->fw_version, "L2", sizeof(drvinfo->fw_version));
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->n_stats = 0;
-       drvinfo->testinfo_len = 0;
-       drvinfo->regdump_len = atl2_get_regs_len(netdev);
-       drvinfo->eedump_len = atl2_get_eeprom_len(netdev);
 }
 
 static void atl2_get_wol(struct net_device *netdev,
index e930aa9a3cfb8ed3c4edad3fc03ae2b3ce6a586f..67a7d520d9f54fcf9eadf4fa81b05b17d76dd55a 100644 (file)
@@ -170,4 +170,23 @@ config SYSTEMPORT
          Broadcom BCM7xxx Set Top Box family chipset using an internal
          Ethernet switch.
 
+config BNXT
+       tristate "Broadcom NetXtreme-C/E support"
+       depends on PCI
+       select FW_LOADER
+       select LIBCRC32C
+       ---help---
+         This driver supports Broadcom NetXtreme-C/E 10/25/40/50 gigabit
+         Ethernet cards.  To compile this driver as a module, choose M here:
+         the module will be called bnxt_en.  This is recommended.
+
+config BNXT_SRIOV
+       bool "Broadcom NetXtreme-C/E SR-IOV support"
+       depends on BNXT && PCI_IOV
+       default y
+       ---help---
+         This configuration parameter enables Single Root Input Output
+         Virtualization support in the NetXtreme-C/E products. This
+         allows for virtual function acceleration in virtual environments.
+
 endif # NET_VENDOR_BROADCOM
index e2a958a657e0bb8f816d205e0792d3fdfbfc70a4..00584d78b3e0425aed06b1e66eceb1aa91bde3bc 100644 (file)
@@ -12,3 +12,4 @@ obj-$(CONFIG_SB1250_MAC) += sb1250-mac.o
 obj-$(CONFIG_TIGON3) += tg3.o
 obj-$(CONFIG_BGMAC) += bgmac.o
 obj-$(CONFIG_SYSTEMPORT) += bcmsysport.o
+obj-$(CONFIG_BNXT) += bnxt/
index a7f2cc3e485eebfae962fe24cfc1142021a74cde..8b1929e9f698c4d8da22573b483795af1bf5bc5b 100644 (file)
@@ -1333,7 +1333,6 @@ static void bcm_enet_get_drvinfo(struct net_device *netdev,
                sizeof(drvinfo->version));
        strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
        strlcpy(drvinfo->bus_info, "bcm63xx", sizeof(drvinfo->bus_info));
-       drvinfo->n_stats = BCM_ENET_STATS_LEN;
 }
 
 static int bcm_enet_get_sset_count(struct net_device *netdev,
@@ -2049,7 +2048,7 @@ static void swphy_poll_timer(unsigned long data)
 
        for (i = 0; i < priv->num_ports; i++) {
                struct bcm63xx_enetsw_port *port;
-               int val, j, up, advertise, lpa, lpa2, speed, duplex, media;
+               int val, j, up, advertise, lpa, speed, duplex, media;
                int external_phy = bcm_enet_port_is_rgmii(i);
                u8 override;
 
@@ -2092,22 +2091,27 @@ static void swphy_poll_timer(unsigned long data)
                lpa = bcmenet_sw_mdio_read(priv, external_phy, port->phy_id,
                                           MII_LPA);
 
-               lpa2 = bcmenet_sw_mdio_read(priv, external_phy, port->phy_id,
-                                           MII_STAT1000);
-
                /* figure out media and duplex from advertise and LPA values */
                media = mii_nway_result(lpa & advertise);
                duplex = (media & ADVERTISE_FULL) ? 1 : 0;
-               if (lpa2 & LPA_1000FULL)
-                       duplex = 1;
-
-               if (lpa2 & (LPA_1000FULL | LPA_1000HALF))
-                       speed = 1000;
-               else {
-                       if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF))
-                               speed = 100;
-                       else
-                               speed = 10;
+
+               if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF))
+                       speed = 100;
+               else
+                       speed = 10;
+
+               if (val & BMSR_ESTATEN) {
+                       advertise = bcmenet_sw_mdio_read(priv, external_phy,
+                                               port->phy_id, MII_CTRL1000);
+
+                       lpa = bcmenet_sw_mdio_read(priv, external_phy,
+                                               port->phy_id, MII_STAT1000);
+
+                       if (advertise & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)
+                                       && lpa & (LPA_1000FULL | LPA_1000HALF)) {
+                               speed = 1000;
+                               duplex = (lpa & LPA_1000FULL);
+                       }
                }
 
                dev_info(&priv->pdev->dev,
@@ -2597,7 +2601,6 @@ static void bcm_enetsw_get_drvinfo(struct net_device *netdev,
        strncpy(drvinfo->version, bcm_enet_driver_version, 32);
        strncpy(drvinfo->fw_version, "N/A", 32);
        strncpy(drvinfo->bus_info, "bcm63xx", 32);
-       drvinfo->n_stats = BCM_ENETSW_STATS_LEN;
 }
 
 static void bcm_enetsw_get_ethtool_stats(struct net_device *netdev,
index f1b5364f352170236269a4dbe2e805e35b97ce16..858106352ce9c5bad35da49ed2d5d41c16cda144 100644 (file)
@@ -287,7 +287,6 @@ static void bcm_sysport_get_drvinfo(struct net_device *dev,
        strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
        strlcpy(info->version, "0.1", sizeof(info->version));
        strlcpy(info->bus_info, "platform", sizeof(info->bus_info));
-       info->n_stats = BCM_SYSPORT_STATS_LEN;
 }
 
 static u32 bcm_sysport_get_msglvl(struct net_device *dev)
index 2b66ef3d8217cfe7ee1e4b142dc1cc8e2ad3835c..8fc3f3c137f87d2ac4fb0fc867acd50def69a03f 100644 (file)
@@ -812,6 +812,46 @@ bnx2_alloc_rx_mem(struct bnx2 *bp)
        return 0;
 }
 
+static void
+bnx2_free_stats_blk(struct net_device *dev)
+{
+       struct bnx2 *bp = netdev_priv(dev);
+
+       if (bp->status_blk) {
+               dma_free_coherent(&bp->pdev->dev, bp->status_stats_size,
+                                 bp->status_blk,
+                                 bp->status_blk_mapping);
+               bp->status_blk = NULL;
+               bp->stats_blk = NULL;
+       }
+}
+
+static int
+bnx2_alloc_stats_blk(struct net_device *dev)
+{
+       int status_blk_size;
+       void *status_blk;
+       struct bnx2 *bp = netdev_priv(dev);
+
+       /* Combine status and statistics blocks into one allocation. */
+       status_blk_size = L1_CACHE_ALIGN(sizeof(struct status_block));
+       if (bp->flags & BNX2_FLAG_MSIX_CAP)
+               status_blk_size = L1_CACHE_ALIGN(BNX2_MAX_MSIX_HW_VEC *
+                                                BNX2_SBLK_MSIX_ALIGN_SIZE);
+       bp->status_stats_size = status_blk_size +
+                               sizeof(struct statistics_block);
+       status_blk = dma_zalloc_coherent(&bp->pdev->dev, bp->status_stats_size,
+                                        &bp->status_blk_mapping, GFP_KERNEL);
+       if (status_blk == NULL)
+               return -ENOMEM;
+
+       bp->status_blk = status_blk;
+       bp->stats_blk = status_blk + status_blk_size;
+       bp->stats_blk_mapping = bp->status_blk_mapping + status_blk_size;
+
+       return 0;
+}
+
 static void
 bnx2_free_mem(struct bnx2 *bp)
 {
@@ -829,37 +869,19 @@ bnx2_free_mem(struct bnx2 *bp)
                        bp->ctx_blk[i] = NULL;
                }
        }
-       if (bnapi->status_blk.msi) {
-               dma_free_coherent(&bp->pdev->dev, bp->status_stats_size,
-                                 bnapi->status_blk.msi,
-                                 bp->status_blk_mapping);
+
+       if (bnapi->status_blk.msi)
                bnapi->status_blk.msi = NULL;
-               bp->stats_blk = NULL;
-       }
 }
 
 static int
 bnx2_alloc_mem(struct bnx2 *bp)
 {
-       int i, status_blk_size, err;
+       int i, err;
        struct bnx2_napi *bnapi;
-       void *status_blk;
-
-       /* Combine status and statistics blocks into one allocation. */
-       status_blk_size = L1_CACHE_ALIGN(sizeof(struct status_block));
-       if (bp->flags & BNX2_FLAG_MSIX_CAP)
-               status_blk_size = L1_CACHE_ALIGN(BNX2_MAX_MSIX_HW_VEC *
-                                                BNX2_SBLK_MSIX_ALIGN_SIZE);
-       bp->status_stats_size = status_blk_size +
-                               sizeof(struct statistics_block);
-
-       status_blk = dma_zalloc_coherent(&bp->pdev->dev, bp->status_stats_size,
-                                        &bp->status_blk_mapping, GFP_KERNEL);
-       if (status_blk == NULL)
-               goto alloc_mem_err;
 
        bnapi = &bp->bnx2_napi[0];
-       bnapi->status_blk.msi = status_blk;
+       bnapi->status_blk.msi = bp->status_blk;
        bnapi->hw_tx_cons_ptr =
                &bnapi->status_blk.msi->status_tx_quick_consumer_index0;
        bnapi->hw_rx_cons_ptr =
@@ -870,7 +892,7 @@ bnx2_alloc_mem(struct bnx2 *bp)
 
                        bnapi = &bp->bnx2_napi[i];
 
-                       sblk = (status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i);
+                       sblk = (bp->status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i);
                        bnapi->status_blk.msix = sblk;
                        bnapi->hw_tx_cons_ptr =
                                &sblk->status_tx_quick_consumer_index;
@@ -880,10 +902,6 @@ bnx2_alloc_mem(struct bnx2 *bp)
                }
        }
 
-       bp->stats_blk = status_blk + status_blk_size;
-
-       bp->stats_blk_mapping = bp->status_blk_mapping + status_blk_size;
-
        if (BNX2_CHIP(bp) == BNX2_CHIP_5709) {
                bp->ctx_pages = 0x2000 / BNX2_PAGE_SIZE;
                if (bp->ctx_pages == 0)
@@ -8330,6 +8348,11 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
 
        bp->phy_addr = 1;
 
+       /* allocate stats_blk */
+       rc = bnx2_alloc_stats_blk(dev);
+       if (rc)
+               goto err_out_unmap;
+
        /* Disable WOL support if we are running on a SERDES chip. */
        if (BNX2_CHIP(bp) == BNX2_CHIP_5709)
                bnx2_get_5709_media(bp);
@@ -8453,6 +8476,8 @@ err_out_disable:
        pci_disable_device(pdev);
 
 err_out:
+       kfree(bp->temp_stats_blk);
+
        return rc;
 }
 
@@ -8586,6 +8611,7 @@ error:
        pci_release_regions(pdev);
        pci_disable_device(pdev);
 err_free:
+       bnx2_free_stats_blk(dev);
        free_netdev(dev);
        return rc;
 }
@@ -8603,6 +8629,7 @@ bnx2_remove_one(struct pci_dev *pdev)
 
        pci_iounmap(bp->pdev, bp->regview);
 
+       bnx2_free_stats_blk(dev);
        kfree(bp->temp_stats_blk);
 
        if (bp->flags & BNX2_FLAG_AER_ENABLED) {
index f92f76c447569db422730f9c1f9a7b36d9d17b0f..380234d72b958666a3782780cd32bd15707016b1 100644 (file)
@@ -6928,6 +6928,7 @@ struct bnx2 {
 
        dma_addr_t              status_blk_mapping;
 
+       void *status_blk;
        struct statistics_block *stats_blk;
        struct statistics_block *temp_stats_blk;
        dma_addr_t              stats_blk_mapping;
index be628bd9fb18b6f0116125e5a3f9ea16ad1d3561..d84efcd34fac3da6ce1b87ad44a9f2ca449c0a02 100644 (file)
@@ -1090,10 +1090,6 @@ static void bnx2x_get_drvinfo(struct net_device *dev,
        bnx2x_fill_fw_str(bp, info->fw_version, sizeof(info->fw_version));
 
        strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info));
-       info->n_stats = BNX2X_NUM_STATS;
-       info->testinfo_len = BNX2X_NUM_TESTS(bp);
-       info->eedump_len = bp->common.flash_size;
-       info->regdump_len = bnx2x_get_regs_len(dev);
 }
 
 static void bnx2x_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
diff --git a/drivers/net/ethernet/broadcom/bnxt/Makefile b/drivers/net/ethernet/broadcom/bnxt/Makefile
new file mode 100644 (file)
index 0000000..97e78e2
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_BNXT) += bnxt_en.o
+
+bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
new file mode 100644 (file)
index 0000000..6c2e0c6
--- /dev/null
@@ -0,0 +1,5728 @@
+/* Broadcom NetXtreme-C/E network driver.
+ *
+ * Copyright (c) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+
+#include <linux/stringify.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/dma-mapping.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include <asm/page.h>
+#include <linux/time.h>
+#include <linux/mii.h>
+#include <linux/if.h>
+#include <linux/if_vlan.h>
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/checksum.h>
+#include <net/ip6_checksum.h>
+#if defined(CONFIG_VXLAN) || defined(CONFIG_VXLAN_MODULE)
+#include <net/vxlan.h>
+#endif
+#ifdef CONFIG_NET_RX_BUSY_POLL
+#include <net/busy_poll.h>
+#endif
+#include <linux/workqueue.h>
+#include <linux/prefetch.h>
+#include <linux/cache.h>
+#include <linux/log2.h>
+#include <linux/aer.h>
+#include <linux/bitmap.h>
+#include <linux/cpu_rmap.h>
+
+#include "bnxt_hsi.h"
+#include "bnxt.h"
+#include "bnxt_sriov.h"
+#include "bnxt_ethtool.h"
+
+#define BNXT_TX_TIMEOUT                (5 * HZ)
+
+static const char version[] =
+       "Broadcom NetXtreme-C/E driver " DRV_MODULE_NAME " v" DRV_MODULE_VERSION "\n";
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Broadcom BCM573xx network driver");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+#define BNXT_RX_OFFSET (NET_SKB_PAD + NET_IP_ALIGN)
+#define BNXT_RX_DMA_OFFSET NET_SKB_PAD
+#define BNXT_RX_COPY_THRESH 256
+
+#define BNXT_TX_PUSH_THRESH 92
+
+enum board_idx {
+       BCM57302,
+       BCM57304,
+       BCM57404,
+       BCM57406,
+       BCM57304_VF,
+       BCM57404_VF,
+};
+
+/* indexed by enum above */
+static const struct {
+       char *name;
+} board_info[] = {
+       { "Broadcom BCM57302 NetXtreme-C Single-port 10Gb/25Gb/40Gb/50Gb Ethernet" },
+       { "Broadcom BCM57304 NetXtreme-C Dual-port 10Gb/25Gb/40Gb/50Gb Ethernet" },
+       { "Broadcom BCM57404 NetXtreme-E Dual-port 10Gb/25Gb Ethernet" },
+       { "Broadcom BCM57406 NetXtreme-E Dual-port 10Gb Ethernet" },
+       { "Broadcom BCM57304 NetXtreme-C Ethernet Virtual Function" },
+       { "Broadcom BCM57404 NetXtreme-E Ethernet Virtual Function" },
+};
+
+static const struct pci_device_id bnxt_pci_tbl[] = {
+       { PCI_VDEVICE(BROADCOM, 0x16c9), .driver_data = BCM57302 },
+       { PCI_VDEVICE(BROADCOM, 0x16ca), .driver_data = BCM57304 },
+       { PCI_VDEVICE(BROADCOM, 0x16d1), .driver_data = BCM57404 },
+       { PCI_VDEVICE(BROADCOM, 0x16d2), .driver_data = BCM57406 },
+#ifdef CONFIG_BNXT_SRIOV
+       { PCI_VDEVICE(BROADCOM, 0x16cb), .driver_data = BCM57304_VF },
+       { PCI_VDEVICE(BROADCOM, 0x16d3), .driver_data = BCM57404_VF },
+#endif
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, bnxt_pci_tbl);
+
+static const u16 bnxt_vf_req_snif[] = {
+       HWRM_FUNC_CFG,
+       HWRM_PORT_PHY_QCFG,
+       HWRM_CFA_L2_FILTER_ALLOC,
+};
+
+static bool bnxt_vf_pciid(enum board_idx idx)
+{
+       return (idx == BCM57304_VF || idx == BCM57404_VF);
+}
+
+#define DB_CP_REARM_FLAGS      (DB_KEY_CP | DB_IDX_VALID)
+#define DB_CP_FLAGS            (DB_KEY_CP | DB_IDX_VALID | DB_IRQ_DIS)
+#define DB_CP_IRQ_DIS_FLAGS    (DB_KEY_CP | DB_IRQ_DIS)
+
+#define BNXT_CP_DB_REARM(db, raw_cons)                                 \
+               writel(DB_CP_REARM_FLAGS | RING_CMP(raw_cons), db)
+
+#define BNXT_CP_DB(db, raw_cons)                                       \
+               writel(DB_CP_FLAGS | RING_CMP(raw_cons), db)
+
+#define BNXT_CP_DB_IRQ_DIS(db)                                         \
+               writel(DB_CP_IRQ_DIS_FLAGS, db)
+
+static inline u32 bnxt_tx_avail(struct bnxt *bp, struct bnxt_tx_ring_info *txr)
+{
+       /* Tell compiler to fetch tx indices from memory. */
+       barrier();
+
+       return bp->tx_ring_size -
+               ((txr->tx_prod - txr->tx_cons) & bp->tx_ring_mask);
+}
+
+static const u16 bnxt_lhint_arr[] = {
+       TX_BD_FLAGS_LHINT_512_AND_SMALLER,
+       TX_BD_FLAGS_LHINT_512_TO_1023,
+       TX_BD_FLAGS_LHINT_1024_TO_2047,
+       TX_BD_FLAGS_LHINT_1024_TO_2047,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+       TX_BD_FLAGS_LHINT_2048_AND_LARGER,
+};
+
+static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       struct tx_bd *txbd;
+       struct tx_bd_ext *txbd1;
+       struct netdev_queue *txq;
+       int i;
+       dma_addr_t mapping;
+       unsigned int length, pad = 0;
+       u32 len, free_size, vlan_tag_flags, cfa_action, flags;
+       u16 prod, last_frag;
+       struct pci_dev *pdev = bp->pdev;
+       struct bnxt_napi *bnapi;
+       struct bnxt_tx_ring_info *txr;
+       struct bnxt_sw_tx_bd *tx_buf;
+
+       i = skb_get_queue_mapping(skb);
+       if (unlikely(i >= bp->tx_nr_rings)) {
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
+
+       bnapi = bp->bnapi[i];
+       txr = &bnapi->tx_ring;
+       txq = netdev_get_tx_queue(dev, i);
+       prod = txr->tx_prod;
+
+       free_size = bnxt_tx_avail(bp, txr);
+       if (unlikely(free_size < skb_shinfo(skb)->nr_frags + 2)) {
+               netif_tx_stop_queue(txq);
+               return NETDEV_TX_BUSY;
+       }
+
+       length = skb->len;
+       len = skb_headlen(skb);
+       last_frag = skb_shinfo(skb)->nr_frags;
+
+       txbd = &txr->tx_desc_ring[TX_RING(prod)][TX_IDX(prod)];
+
+       txbd->tx_bd_opaque = prod;
+
+       tx_buf = &txr->tx_buf_ring[prod];
+       tx_buf->skb = skb;
+       tx_buf->nr_frags = last_frag;
+
+       vlan_tag_flags = 0;
+       cfa_action = 0;
+       if (skb_vlan_tag_present(skb)) {
+               vlan_tag_flags = TX_BD_CFA_META_KEY_VLAN |
+                                skb_vlan_tag_get(skb);
+               /* Currently supports 8021Q, 8021AD vlan offloads
+                * QINQ1, QINQ2, QINQ3 vlan headers are deprecated
+                */
+               if (skb->vlan_proto == htons(ETH_P_8021Q))
+                       vlan_tag_flags |= 1 << TX_BD_CFA_META_TPID_SHIFT;
+       }
+
+       if (free_size == bp->tx_ring_size && length <= bp->tx_push_thresh) {
+               struct tx_push_bd *push = txr->tx_push;
+               struct tx_bd *tx_push = &push->txbd1;
+               struct tx_bd_ext *tx_push1 = &push->txbd2;
+               void *pdata = tx_push1 + 1;
+               int j;
+
+               /* Set COAL_NOW to be ready quickly for the next push */
+               tx_push->tx_bd_len_flags_type =
+                       cpu_to_le32((length << TX_BD_LEN_SHIFT) |
+                                       TX_BD_TYPE_LONG_TX_BD |
+                                       TX_BD_FLAGS_LHINT_512_AND_SMALLER |
+                                       TX_BD_FLAGS_COAL_NOW |
+                                       TX_BD_FLAGS_PACKET_END |
+                                       (2 << TX_BD_FLAGS_BD_CNT_SHIFT));
+
+               if (skb->ip_summed == CHECKSUM_PARTIAL)
+                       tx_push1->tx_bd_hsize_lflags =
+                                       cpu_to_le32(TX_BD_FLAGS_TCP_UDP_CHKSUM);
+               else
+                       tx_push1->tx_bd_hsize_lflags = 0;
+
+               tx_push1->tx_bd_cfa_meta = cpu_to_le32(vlan_tag_flags);
+               tx_push1->tx_bd_cfa_action = cpu_to_le32(cfa_action);
+
+               skb_copy_from_linear_data(skb, pdata, len);
+               pdata += len;
+               for (j = 0; j < last_frag; j++) {
+                       skb_frag_t *frag = &skb_shinfo(skb)->frags[j];
+                       void *fptr;
+
+                       fptr = skb_frag_address_safe(frag);
+                       if (!fptr)
+                               goto normal_tx;
+
+                       memcpy(pdata, fptr, skb_frag_size(frag));
+                       pdata += skb_frag_size(frag);
+               }
+
+               memcpy(txbd, tx_push, sizeof(*txbd));
+               prod = NEXT_TX(prod);
+               txbd = &txr->tx_desc_ring[TX_RING(prod)][TX_IDX(prod)];
+               memcpy(txbd, tx_push1, sizeof(*txbd));
+               prod = NEXT_TX(prod);
+               push->doorbell =
+                       cpu_to_le32(DB_KEY_TX_PUSH | DB_LONG_TX_PUSH | prod);
+               txr->tx_prod = prod;
+
+               netdev_tx_sent_queue(txq, skb->len);
+
+               __iowrite64_copy(txr->tx_doorbell, push,
+                                (length + sizeof(*push) + 8) / 8);
+
+               tx_buf->is_push = 1;
+
+               goto tx_done;
+       }
+
+normal_tx:
+       if (length < BNXT_MIN_PKT_SIZE) {
+               pad = BNXT_MIN_PKT_SIZE - length;
+               if (skb_pad(skb, pad)) {
+                       /* SKB already freed. */
+                       tx_buf->skb = NULL;
+                       return NETDEV_TX_OK;
+               }
+               length = BNXT_MIN_PKT_SIZE;
+       }
+
+       mapping = dma_map_single(&pdev->dev, skb->data, len, DMA_TO_DEVICE);
+
+       if (unlikely(dma_mapping_error(&pdev->dev, mapping))) {
+               dev_kfree_skb_any(skb);
+               tx_buf->skb = NULL;
+               return NETDEV_TX_OK;
+       }
+
+       dma_unmap_addr_set(tx_buf, mapping, mapping);
+       flags = (len << TX_BD_LEN_SHIFT) | TX_BD_TYPE_LONG_TX_BD |
+               ((last_frag + 2) << TX_BD_FLAGS_BD_CNT_SHIFT);
+
+       txbd->tx_bd_haddr = cpu_to_le64(mapping);
+
+       prod = NEXT_TX(prod);
+       txbd1 = (struct tx_bd_ext *)
+               &txr->tx_desc_ring[TX_RING(prod)][TX_IDX(prod)];
+
+       txbd1->tx_bd_hsize_lflags = 0;
+       if (skb_is_gso(skb)) {
+               u32 hdr_len;
+
+               if (skb->encapsulation)
+                       hdr_len = skb_inner_network_offset(skb) +
+                               skb_inner_network_header_len(skb) +
+                               inner_tcp_hdrlen(skb);
+               else
+                       hdr_len = skb_transport_offset(skb) +
+                               tcp_hdrlen(skb);
+
+               txbd1->tx_bd_hsize_lflags = cpu_to_le32(TX_BD_FLAGS_LSO |
+                                       TX_BD_FLAGS_T_IPID |
+                                       (hdr_len << (TX_BD_HSIZE_SHIFT - 1)));
+               length = skb_shinfo(skb)->gso_size;
+               txbd1->tx_bd_mss = cpu_to_le32(length);
+               length += hdr_len;
+       } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               txbd1->tx_bd_hsize_lflags =
+                       cpu_to_le32(TX_BD_FLAGS_TCP_UDP_CHKSUM);
+               txbd1->tx_bd_mss = 0;
+       }
+
+       length >>= 9;
+       flags |= bnxt_lhint_arr[length];
+       txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
+
+       txbd1->tx_bd_cfa_meta = cpu_to_le32(vlan_tag_flags);
+       txbd1->tx_bd_cfa_action = cpu_to_le32(cfa_action);
+       for (i = 0; i < last_frag; i++) {
+               skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+               prod = NEXT_TX(prod);
+               txbd = &txr->tx_desc_ring[TX_RING(prod)][TX_IDX(prod)];
+
+               len = skb_frag_size(frag);
+               mapping = skb_frag_dma_map(&pdev->dev, frag, 0, len,
+                                          DMA_TO_DEVICE);
+
+               if (unlikely(dma_mapping_error(&pdev->dev, mapping)))
+                       goto tx_dma_error;
+
+               tx_buf = &txr->tx_buf_ring[prod];
+               dma_unmap_addr_set(tx_buf, mapping, mapping);
+
+               txbd->tx_bd_haddr = cpu_to_le64(mapping);
+
+               flags = len << TX_BD_LEN_SHIFT;
+               txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
+       }
+
+       flags &= ~TX_BD_LEN;
+       txbd->tx_bd_len_flags_type =
+               cpu_to_le32(((len + pad) << TX_BD_LEN_SHIFT) | flags |
+                           TX_BD_FLAGS_PACKET_END);
+
+       netdev_tx_sent_queue(txq, skb->len);
+
+       /* Sync BD data before updating doorbell */
+       wmb();
+
+       prod = NEXT_TX(prod);
+       txr->tx_prod = prod;
+
+       writel(DB_KEY_TX | prod, txr->tx_doorbell);
+       writel(DB_KEY_TX | prod, txr->tx_doorbell);
+
+tx_done:
+
+       mmiowb();
+
+       if (unlikely(bnxt_tx_avail(bp, txr) <= MAX_SKB_FRAGS + 1)) {
+               netif_tx_stop_queue(txq);
+
+               /* netif_tx_stop_queue() must be done before checking
+                * tx index in bnxt_tx_avail() below, because in
+                * bnxt_tx_int(), we update tx index before checking for
+                * netif_tx_queue_stopped().
+                */
+               smp_mb();
+               if (bnxt_tx_avail(bp, txr) > bp->tx_wake_thresh)
+                       netif_tx_wake_queue(txq);
+       }
+       return NETDEV_TX_OK;
+
+tx_dma_error:
+       last_frag = i;
+
+       /* start back at beginning and unmap skb */
+       prod = txr->tx_prod;
+       tx_buf = &txr->tx_buf_ring[prod];
+       tx_buf->skb = NULL;
+       dma_unmap_single(&pdev->dev, dma_unmap_addr(tx_buf, mapping),
+                        skb_headlen(skb), PCI_DMA_TODEVICE);
+       prod = NEXT_TX(prod);
+
+       /* unmap remaining mapped pages */
+       for (i = 0; i < last_frag; i++) {
+               prod = NEXT_TX(prod);
+               tx_buf = &txr->tx_buf_ring[prod];
+               dma_unmap_page(&pdev->dev, dma_unmap_addr(tx_buf, mapping),
+                              skb_frag_size(&skb_shinfo(skb)->frags[i]),
+                              PCI_DMA_TODEVICE);
+       }
+
+       dev_kfree_skb_any(skb);
+       return NETDEV_TX_OK;
+}
+
+static void bnxt_tx_int(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts)
+{
+       struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
+       int index = bnapi->index;
+       struct netdev_queue *txq = netdev_get_tx_queue(bp->dev, index);
+       u16 cons = txr->tx_cons;
+       struct pci_dev *pdev = bp->pdev;
+       int i;
+       unsigned int tx_bytes = 0;
+
+       for (i = 0; i < nr_pkts; i++) {
+               struct bnxt_sw_tx_bd *tx_buf;
+               struct sk_buff *skb;
+               int j, last;
+
+               tx_buf = &txr->tx_buf_ring[cons];
+               cons = NEXT_TX(cons);
+               skb = tx_buf->skb;
+               tx_buf->skb = NULL;
+
+               if (tx_buf->is_push) {
+                       tx_buf->is_push = 0;
+                       goto next_tx_int;
+               }
+
+               dma_unmap_single(&pdev->dev, dma_unmap_addr(tx_buf, mapping),
+                                skb_headlen(skb), PCI_DMA_TODEVICE);
+               last = tx_buf->nr_frags;
+
+               for (j = 0; j < last; j++) {
+                       cons = NEXT_TX(cons);
+                       tx_buf = &txr->tx_buf_ring[cons];
+                       dma_unmap_page(
+                               &pdev->dev,
+                               dma_unmap_addr(tx_buf, mapping),
+                               skb_frag_size(&skb_shinfo(skb)->frags[j]),
+                               PCI_DMA_TODEVICE);
+               }
+
+next_tx_int:
+               cons = NEXT_TX(cons);
+
+               tx_bytes += skb->len;
+               dev_kfree_skb_any(skb);
+       }
+
+       netdev_tx_completed_queue(txq, nr_pkts, tx_bytes);
+       txr->tx_cons = cons;
+
+       /* Need to make the tx_cons update visible to bnxt_start_xmit()
+        * before checking for netif_tx_queue_stopped().  Without the
+        * memory barrier, there is a small possibility that bnxt_start_xmit()
+        * will miss it and cause the queue to be stopped forever.
+        */
+       smp_mb();
+
+       if (unlikely(netif_tx_queue_stopped(txq)) &&
+           (bnxt_tx_avail(bp, txr) > bp->tx_wake_thresh)) {
+               __netif_tx_lock(txq, smp_processor_id());
+               if (netif_tx_queue_stopped(txq) &&
+                   bnxt_tx_avail(bp, txr) > bp->tx_wake_thresh &&
+                   txr->dev_state != BNXT_DEV_STATE_CLOSING)
+                       netif_tx_wake_queue(txq);
+               __netif_tx_unlock(txq);
+       }
+}
+
+static inline u8 *__bnxt_alloc_rx_data(struct bnxt *bp, dma_addr_t *mapping,
+                                      gfp_t gfp)
+{
+       u8 *data;
+       struct pci_dev *pdev = bp->pdev;
+
+       data = kmalloc(bp->rx_buf_size, gfp);
+       if (!data)
+               return NULL;
+
+       *mapping = dma_map_single(&pdev->dev, data + BNXT_RX_DMA_OFFSET,
+                                 bp->rx_buf_use_size, PCI_DMA_FROMDEVICE);
+
+       if (dma_mapping_error(&pdev->dev, *mapping)) {
+               kfree(data);
+               data = NULL;
+       }
+       return data;
+}
+
+static inline int bnxt_alloc_rx_data(struct bnxt *bp,
+                                    struct bnxt_rx_ring_info *rxr,
+                                    u16 prod, gfp_t gfp)
+{
+       struct rx_bd *rxbd = &rxr->rx_desc_ring[RX_RING(prod)][RX_IDX(prod)];
+       struct bnxt_sw_rx_bd *rx_buf = &rxr->rx_buf_ring[prod];
+       u8 *data;
+       dma_addr_t mapping;
+
+       data = __bnxt_alloc_rx_data(bp, &mapping, gfp);
+       if (!data)
+               return -ENOMEM;
+
+       rx_buf->data = data;
+       dma_unmap_addr_set(rx_buf, mapping, mapping);
+
+       rxbd->rx_bd_haddr = cpu_to_le64(mapping);
+
+       return 0;
+}
+
+static void bnxt_reuse_rx_data(struct bnxt_rx_ring_info *rxr, u16 cons,
+                              u8 *data)
+{
+       u16 prod = rxr->rx_prod;
+       struct bnxt_sw_rx_bd *cons_rx_buf, *prod_rx_buf;
+       struct rx_bd *cons_bd, *prod_bd;
+
+       prod_rx_buf = &rxr->rx_buf_ring[prod];
+       cons_rx_buf = &rxr->rx_buf_ring[cons];
+
+       prod_rx_buf->data = data;
+
+       dma_unmap_addr_set(prod_rx_buf, mapping,
+                          dma_unmap_addr(cons_rx_buf, mapping));
+
+       prod_bd = &rxr->rx_desc_ring[RX_RING(prod)][RX_IDX(prod)];
+       cons_bd = &rxr->rx_desc_ring[RX_RING(cons)][RX_IDX(cons)];
+
+       prod_bd->rx_bd_haddr = cons_bd->rx_bd_haddr;
+}
+
+static inline u16 bnxt_find_next_agg_idx(struct bnxt_rx_ring_info *rxr, u16 idx)
+{
+       u16 next, max = rxr->rx_agg_bmap_size;
+
+       next = find_next_zero_bit(rxr->rx_agg_bmap, max, idx);
+       if (next >= max)
+               next = find_first_zero_bit(rxr->rx_agg_bmap, max);
+       return next;
+}
+
+static inline int bnxt_alloc_rx_page(struct bnxt *bp,
+                                    struct bnxt_rx_ring_info *rxr,
+                                    u16 prod, gfp_t gfp)
+{
+       struct rx_bd *rxbd =
+               &rxr->rx_agg_desc_ring[RX_RING(prod)][RX_IDX(prod)];
+       struct bnxt_sw_rx_agg_bd *rx_agg_buf;
+       struct pci_dev *pdev = bp->pdev;
+       struct page *page;
+       dma_addr_t mapping;
+       u16 sw_prod = rxr->rx_sw_agg_prod;
+
+       page = alloc_page(gfp);
+       if (!page)
+               return -ENOMEM;
+
+       mapping = dma_map_page(&pdev->dev, page, 0, PAGE_SIZE,
+                              PCI_DMA_FROMDEVICE);
+       if (dma_mapping_error(&pdev->dev, mapping)) {
+               __free_page(page);
+               return -EIO;
+       }
+
+       if (unlikely(test_bit(sw_prod, rxr->rx_agg_bmap)))
+               sw_prod = bnxt_find_next_agg_idx(rxr, sw_prod);
+
+       __set_bit(sw_prod, rxr->rx_agg_bmap);
+       rx_agg_buf = &rxr->rx_agg_ring[sw_prod];
+       rxr->rx_sw_agg_prod = NEXT_RX_AGG(sw_prod);
+
+       rx_agg_buf->page = page;
+       rx_agg_buf->mapping = mapping;
+       rxbd->rx_bd_haddr = cpu_to_le64(mapping);
+       rxbd->rx_bd_opaque = sw_prod;
+       return 0;
+}
+
+static void bnxt_reuse_rx_agg_bufs(struct bnxt_napi *bnapi, u16 cp_cons,
+                                  u32 agg_bufs)
+{
+       struct bnxt *bp = bnapi->bp;
+       struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+       struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+       u16 prod = rxr->rx_agg_prod;
+       u16 sw_prod = rxr->rx_sw_agg_prod;
+       u32 i;
+
+       for (i = 0; i < agg_bufs; i++) {
+               u16 cons;
+               struct rx_agg_cmp *agg;
+               struct bnxt_sw_rx_agg_bd *cons_rx_buf, *prod_rx_buf;
+               struct rx_bd *prod_bd;
+               struct page *page;
+
+               agg = (struct rx_agg_cmp *)
+                       &cpr->cp_desc_ring[CP_RING(cp_cons)][CP_IDX(cp_cons)];
+               cons = agg->rx_agg_cmp_opaque;
+               __clear_bit(cons, rxr->rx_agg_bmap);
+
+               if (unlikely(test_bit(sw_prod, rxr->rx_agg_bmap)))
+                       sw_prod = bnxt_find_next_agg_idx(rxr, sw_prod);
+
+               __set_bit(sw_prod, rxr->rx_agg_bmap);
+               prod_rx_buf = &rxr->rx_agg_ring[sw_prod];
+               cons_rx_buf = &rxr->rx_agg_ring[cons];
+
+               /* It is possible for sw_prod to be equal to cons, so
+                * set cons_rx_buf->page to NULL first.
+                */
+               page = cons_rx_buf->page;
+               cons_rx_buf->page = NULL;
+               prod_rx_buf->page = page;
+
+               prod_rx_buf->mapping = cons_rx_buf->mapping;
+
+               prod_bd = &rxr->rx_agg_desc_ring[RX_RING(prod)][RX_IDX(prod)];
+
+               prod_bd->rx_bd_haddr = cpu_to_le64(cons_rx_buf->mapping);
+               prod_bd->rx_bd_opaque = sw_prod;
+
+               prod = NEXT_RX_AGG(prod);
+               sw_prod = NEXT_RX_AGG(sw_prod);
+               cp_cons = NEXT_CMP(cp_cons);
+       }
+       rxr->rx_agg_prod = prod;
+       rxr->rx_sw_agg_prod = sw_prod;
+}
+
+static struct sk_buff *bnxt_rx_skb(struct bnxt *bp,
+                                  struct bnxt_rx_ring_info *rxr, u16 cons,
+                                  u16 prod, u8 *data, dma_addr_t dma_addr,
+                                  unsigned int len)
+{
+       int err;
+       struct sk_buff *skb;
+
+       err = bnxt_alloc_rx_data(bp, rxr, prod, GFP_ATOMIC);
+       if (unlikely(err)) {
+               bnxt_reuse_rx_data(rxr, cons, data);
+               return NULL;
+       }
+
+       skb = build_skb(data, 0);
+       dma_unmap_single(&bp->pdev->dev, dma_addr, bp->rx_buf_use_size,
+                        PCI_DMA_FROMDEVICE);
+       if (!skb) {
+               kfree(data);
+               return NULL;
+       }
+
+       skb_reserve(skb, BNXT_RX_OFFSET);
+       skb_put(skb, len);
+       return skb;
+}
+
+static struct sk_buff *bnxt_rx_pages(struct bnxt *bp, struct bnxt_napi *bnapi,
+                                    struct sk_buff *skb, u16 cp_cons,
+                                    u32 agg_bufs)
+{
+       struct pci_dev *pdev = bp->pdev;
+       struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+       struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+       u16 prod = rxr->rx_agg_prod;
+       u32 i;
+
+       for (i = 0; i < agg_bufs; i++) {
+               u16 cons, frag_len;
+               struct rx_agg_cmp *agg;
+               struct bnxt_sw_rx_agg_bd *cons_rx_buf;
+               struct page *page;
+               dma_addr_t mapping;
+
+               agg = (struct rx_agg_cmp *)
+                       &cpr->cp_desc_ring[CP_RING(cp_cons)][CP_IDX(cp_cons)];
+               cons = agg->rx_agg_cmp_opaque;
+               frag_len = (le32_to_cpu(agg->rx_agg_cmp_len_flags_type) &
+                           RX_AGG_CMP_LEN) >> RX_AGG_CMP_LEN_SHIFT;
+
+               cons_rx_buf = &rxr->rx_agg_ring[cons];
+               skb_fill_page_desc(skb, i, cons_rx_buf->page, 0, frag_len);
+               __clear_bit(cons, rxr->rx_agg_bmap);
+
+               /* It is possible for bnxt_alloc_rx_page() to allocate
+                * a sw_prod index that equals the cons index, so we
+                * need to clear the cons entry now.
+                */
+               mapping = dma_unmap_addr(cons_rx_buf, mapping);
+               page = cons_rx_buf->page;
+               cons_rx_buf->page = NULL;
+
+               if (bnxt_alloc_rx_page(bp, rxr, prod, GFP_ATOMIC) != 0) {
+                       struct skb_shared_info *shinfo;
+                       unsigned int nr_frags;
+
+                       shinfo = skb_shinfo(skb);
+                       nr_frags = --shinfo->nr_frags;
+                       __skb_frag_set_page(&shinfo->frags[nr_frags], NULL);
+
+                       dev_kfree_skb(skb);
+
+                       cons_rx_buf->page = page;
+
+                       /* Update prod since possibly some pages have been
+                        * allocated already.
+                        */
+                       rxr->rx_agg_prod = prod;
+                       bnxt_reuse_rx_agg_bufs(bnapi, cp_cons, agg_bufs - i);
+                       return NULL;
+               }
+
+               dma_unmap_page(&pdev->dev, mapping, PAGE_SIZE,
+                              PCI_DMA_FROMDEVICE);
+
+               skb->data_len += frag_len;
+               skb->len += frag_len;
+               skb->truesize += PAGE_SIZE;
+
+               prod = NEXT_RX_AGG(prod);
+               cp_cons = NEXT_CMP(cp_cons);
+       }
+       rxr->rx_agg_prod = prod;
+       return skb;
+}
+
+static int bnxt_agg_bufs_valid(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
+                              u8 agg_bufs, u32 *raw_cons)
+{
+       u16 last;
+       struct rx_agg_cmp *agg;
+
+       *raw_cons = ADV_RAW_CMP(*raw_cons, agg_bufs);
+       last = RING_CMP(*raw_cons);
+       agg = (struct rx_agg_cmp *)
+               &cpr->cp_desc_ring[CP_RING(last)][CP_IDX(last)];
+       return RX_AGG_CMP_VALID(agg, *raw_cons);
+}
+
+static inline struct sk_buff *bnxt_copy_skb(struct bnxt_napi *bnapi, u8 *data,
+                                           unsigned int len,
+                                           dma_addr_t mapping)
+{
+       struct bnxt *bp = bnapi->bp;
+       struct pci_dev *pdev = bp->pdev;
+       struct sk_buff *skb;
+
+       skb = napi_alloc_skb(&bnapi->napi, len);
+       if (!skb)
+               return NULL;
+
+       dma_sync_single_for_cpu(&pdev->dev, mapping,
+                               bp->rx_copy_thresh, PCI_DMA_FROMDEVICE);
+
+       memcpy(skb->data - BNXT_RX_OFFSET, data, len + BNXT_RX_OFFSET);
+
+       dma_sync_single_for_device(&pdev->dev, mapping,
+                                  bp->rx_copy_thresh,
+                                  PCI_DMA_FROMDEVICE);
+
+       skb_put(skb, len);
+       return skb;
+}
+
+static void bnxt_tpa_start(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
+                          struct rx_tpa_start_cmp *tpa_start,
+                          struct rx_tpa_start_cmp_ext *tpa_start1)
+{
+       u8 agg_id = TPA_START_AGG_ID(tpa_start);
+       u16 cons, prod;
+       struct bnxt_tpa_info *tpa_info;
+       struct bnxt_sw_rx_bd *cons_rx_buf, *prod_rx_buf;
+       struct rx_bd *prod_bd;
+       dma_addr_t mapping;
+
+       cons = tpa_start->rx_tpa_start_cmp_opaque;
+       prod = rxr->rx_prod;
+       cons_rx_buf = &rxr->rx_buf_ring[cons];
+       prod_rx_buf = &rxr->rx_buf_ring[prod];
+       tpa_info = &rxr->rx_tpa[agg_id];
+
+       prod_rx_buf->data = tpa_info->data;
+
+       mapping = tpa_info->mapping;
+       dma_unmap_addr_set(prod_rx_buf, mapping, mapping);
+
+       prod_bd = &rxr->rx_desc_ring[RX_RING(prod)][RX_IDX(prod)];
+
+       prod_bd->rx_bd_haddr = cpu_to_le64(mapping);
+
+       tpa_info->data = cons_rx_buf->data;
+       cons_rx_buf->data = NULL;
+       tpa_info->mapping = dma_unmap_addr(cons_rx_buf, mapping);
+
+       tpa_info->len =
+               le32_to_cpu(tpa_start->rx_tpa_start_cmp_len_flags_type) >>
+                               RX_TPA_START_CMP_LEN_SHIFT;
+       if (likely(TPA_START_HASH_VALID(tpa_start))) {
+               u32 hash_type = TPA_START_HASH_TYPE(tpa_start);
+
+               tpa_info->hash_type = PKT_HASH_TYPE_L4;
+               tpa_info->gso_type = SKB_GSO_TCPV4;
+               /* RSS profiles 1 and 3 with extract code 0 for inner 4-tuple */
+               if (hash_type == 3)
+                       tpa_info->gso_type = SKB_GSO_TCPV6;
+               tpa_info->rss_hash =
+                       le32_to_cpu(tpa_start->rx_tpa_start_cmp_rss_hash);
+       } else {
+               tpa_info->hash_type = PKT_HASH_TYPE_NONE;
+               tpa_info->gso_type = 0;
+               if (netif_msg_rx_err(bp))
+                       netdev_warn(bp->dev, "TPA packet without valid hash\n");
+       }
+       tpa_info->flags2 = le32_to_cpu(tpa_start1->rx_tpa_start_cmp_flags2);
+       tpa_info->metadata = le32_to_cpu(tpa_start1->rx_tpa_start_cmp_metadata);
+
+       rxr->rx_prod = NEXT_RX(prod);
+       cons = NEXT_RX(cons);
+       cons_rx_buf = &rxr->rx_buf_ring[cons];
+
+       bnxt_reuse_rx_data(rxr, cons, cons_rx_buf->data);
+       rxr->rx_prod = NEXT_RX(rxr->rx_prod);
+       cons_rx_buf->data = NULL;
+}
+
+static void bnxt_abort_tpa(struct bnxt *bp, struct bnxt_napi *bnapi,
+                          u16 cp_cons, u32 agg_bufs)
+{
+       if (agg_bufs)
+               bnxt_reuse_rx_agg_bufs(bnapi, cp_cons, agg_bufs);
+}
+
+#define BNXT_IPV4_HDR_SIZE     (sizeof(struct iphdr) + sizeof(struct tcphdr))
+#define BNXT_IPV6_HDR_SIZE     (sizeof(struct ipv6hdr) + sizeof(struct tcphdr))
+
+static inline struct sk_buff *bnxt_gro_skb(struct bnxt_tpa_info *tpa_info,
+                                          struct rx_tpa_end_cmp *tpa_end,
+                                          struct rx_tpa_end_cmp_ext *tpa_end1,
+                                          struct sk_buff *skb)
+{
+#ifdef CONFIG_INET
+       struct tcphdr *th;
+       int payload_off, tcp_opt_len = 0;
+       int len, nw_off;
+
+       NAPI_GRO_CB(skb)->count = TPA_END_TPA_SEGS(tpa_end);
+       skb_shinfo(skb)->gso_size =
+               le32_to_cpu(tpa_end1->rx_tpa_end_cmp_seg_len);
+       skb_shinfo(skb)->gso_type = tpa_info->gso_type;
+       payload_off = (le32_to_cpu(tpa_end->rx_tpa_end_cmp_misc_v1) &
+                      RX_TPA_END_CMP_PAYLOAD_OFFSET) >>
+                     RX_TPA_END_CMP_PAYLOAD_OFFSET_SHIFT;
+       if (TPA_END_GRO_TS(tpa_end))
+               tcp_opt_len = 12;
+
+       if (tpa_info->gso_type == SKB_GSO_TCPV4) {
+               struct iphdr *iph;
+
+               nw_off = payload_off - BNXT_IPV4_HDR_SIZE - tcp_opt_len -
+                        ETH_HLEN;
+               skb_set_network_header(skb, nw_off);
+               iph = ip_hdr(skb);
+               skb_set_transport_header(skb, nw_off + sizeof(struct iphdr));
+               len = skb->len - skb_transport_offset(skb);
+               th = tcp_hdr(skb);
+               th->check = ~tcp_v4_check(len, iph->saddr, iph->daddr, 0);
+       } else if (tpa_info->gso_type == SKB_GSO_TCPV6) {
+               struct ipv6hdr *iph;
+
+               nw_off = payload_off - BNXT_IPV6_HDR_SIZE - tcp_opt_len -
+                        ETH_HLEN;
+               skb_set_network_header(skb, nw_off);
+               iph = ipv6_hdr(skb);
+               skb_set_transport_header(skb, nw_off + sizeof(struct ipv6hdr));
+               len = skb->len - skb_transport_offset(skb);
+               th = tcp_hdr(skb);
+               th->check = ~tcp_v6_check(len, &iph->saddr, &iph->daddr, 0);
+       } else {
+               dev_kfree_skb_any(skb);
+               return NULL;
+       }
+       tcp_gro_complete(skb);
+
+       if (nw_off) { /* tunnel */
+               struct udphdr *uh = NULL;
+
+               if (skb->protocol == htons(ETH_P_IP)) {
+                       struct iphdr *iph = (struct iphdr *)skb->data;
+
+                       if (iph->protocol == IPPROTO_UDP)
+                               uh = (struct udphdr *)(iph + 1);
+               } else {
+                       struct ipv6hdr *iph = (struct ipv6hdr *)skb->data;
+
+                       if (iph->nexthdr == IPPROTO_UDP)
+                               uh = (struct udphdr *)(iph + 1);
+               }
+               if (uh) {
+                       if (uh->check)
+                               skb_shinfo(skb)->gso_type |=
+                                       SKB_GSO_UDP_TUNNEL_CSUM;
+                       else
+                               skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL;
+               }
+       }
+#endif
+       return skb;
+}
+
+static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp,
+                                          struct bnxt_napi *bnapi,
+                                          u32 *raw_cons,
+                                          struct rx_tpa_end_cmp *tpa_end,
+                                          struct rx_tpa_end_cmp_ext *tpa_end1,
+                                          bool *agg_event)
+{
+       struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+       struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+       u8 agg_id = TPA_END_AGG_ID(tpa_end);
+       u8 *data, agg_bufs;
+       u16 cp_cons = RING_CMP(*raw_cons);
+       unsigned int len;
+       struct bnxt_tpa_info *tpa_info;
+       dma_addr_t mapping;
+       struct sk_buff *skb;
+
+       tpa_info = &rxr->rx_tpa[agg_id];
+       data = tpa_info->data;
+       prefetch(data);
+       len = tpa_info->len;
+       mapping = tpa_info->mapping;
+
+       agg_bufs = (le32_to_cpu(tpa_end->rx_tpa_end_cmp_misc_v1) &
+                   RX_TPA_END_CMP_AGG_BUFS) >> RX_TPA_END_CMP_AGG_BUFS_SHIFT;
+
+       if (agg_bufs) {
+               if (!bnxt_agg_bufs_valid(bp, cpr, agg_bufs, raw_cons))
+                       return ERR_PTR(-EBUSY);
+
+               *agg_event = true;
+               cp_cons = NEXT_CMP(cp_cons);
+       }
+
+       if (unlikely(agg_bufs > MAX_SKB_FRAGS)) {
+               bnxt_abort_tpa(bp, bnapi, cp_cons, agg_bufs);
+               netdev_warn(bp->dev, "TPA frags %d exceeded MAX_SKB_FRAGS %d\n",
+                           agg_bufs, (int)MAX_SKB_FRAGS);
+               return NULL;
+       }
+
+       if (len <= bp->rx_copy_thresh) {
+               skb = bnxt_copy_skb(bnapi, data, len, mapping);
+               if (!skb) {
+                       bnxt_abort_tpa(bp, bnapi, cp_cons, agg_bufs);
+                       return NULL;
+               }
+       } else {
+               u8 *new_data;
+               dma_addr_t new_mapping;
+
+               new_data = __bnxt_alloc_rx_data(bp, &new_mapping, GFP_ATOMIC);
+               if (!new_data) {
+                       bnxt_abort_tpa(bp, bnapi, cp_cons, agg_bufs);
+                       return NULL;
+               }
+
+               tpa_info->data = new_data;
+               tpa_info->mapping = new_mapping;
+
+               skb = build_skb(data, 0);
+               dma_unmap_single(&bp->pdev->dev, mapping, bp->rx_buf_use_size,
+                                PCI_DMA_FROMDEVICE);
+
+               if (!skb) {
+                       kfree(data);
+                       bnxt_abort_tpa(bp, bnapi, cp_cons, agg_bufs);
+                       return NULL;
+               }
+               skb_reserve(skb, BNXT_RX_OFFSET);
+               skb_put(skb, len);
+       }
+
+       if (agg_bufs) {
+               skb = bnxt_rx_pages(bp, bnapi, skb, cp_cons, agg_bufs);
+               if (!skb) {
+                       /* Page reuse already handled by bnxt_rx_pages(). */
+                       return NULL;
+               }
+       }
+       skb->protocol = eth_type_trans(skb, bp->dev);
+
+       if (tpa_info->hash_type != PKT_HASH_TYPE_NONE)
+               skb_set_hash(skb, tpa_info->rss_hash, tpa_info->hash_type);
+
+       if (tpa_info->flags2 & RX_CMP_FLAGS2_META_FORMAT_VLAN) {
+               netdev_features_t features = skb->dev->features;
+               u16 vlan_proto = tpa_info->metadata >>
+                       RX_CMP_FLAGS2_METADATA_TPID_SFT;
+
+               if (((features & NETIF_F_HW_VLAN_CTAG_RX) &&
+                    vlan_proto == ETH_P_8021Q) ||
+                   ((features & NETIF_F_HW_VLAN_STAG_RX) &&
+                    vlan_proto == ETH_P_8021AD)) {
+                       __vlan_hwaccel_put_tag(skb, htons(vlan_proto),
+                                              tpa_info->metadata &
+                                              RX_CMP_FLAGS2_METADATA_VID_MASK);
+               }
+       }
+
+       skb_checksum_none_assert(skb);
+       if (likely(tpa_info->flags2 & RX_TPA_START_CMP_FLAGS2_L4_CS_CALC)) {
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               skb->csum_level =
+                       (tpa_info->flags2 & RX_CMP_FLAGS2_T_L4_CS_CALC) >> 3;
+       }
+
+       if (TPA_END_GRO(tpa_end))
+               skb = bnxt_gro_skb(tpa_info, tpa_end, tpa_end1, skb);
+
+       return skb;
+}
+
+/* returns the following:
+ * 1       - 1 packet successfully received
+ * 0       - successful TPA_START, packet not completed yet
+ * -EBUSY  - completion ring does not have all the agg buffers yet
+ * -ENOMEM - packet aborted due to out of memory
+ * -EIO    - packet aborted due to hw error indicated in BD
+ */
+static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons,
+                      bool *agg_event)
+{
+       struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+       struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+       struct net_device *dev = bp->dev;
+       struct rx_cmp *rxcmp;
+       struct rx_cmp_ext *rxcmp1;
+       u32 tmp_raw_cons = *raw_cons;
+       u16 cons, prod, cp_cons = RING_CMP(tmp_raw_cons);
+       struct bnxt_sw_rx_bd *rx_buf;
+       unsigned int len;
+       u8 *data, agg_bufs, cmp_type;
+       dma_addr_t dma_addr;
+       struct sk_buff *skb;
+       int rc = 0;
+
+       rxcmp = (struct rx_cmp *)
+                       &cpr->cp_desc_ring[CP_RING(cp_cons)][CP_IDX(cp_cons)];
+
+       tmp_raw_cons = NEXT_RAW_CMP(tmp_raw_cons);
+       cp_cons = RING_CMP(tmp_raw_cons);
+       rxcmp1 = (struct rx_cmp_ext *)
+                       &cpr->cp_desc_ring[CP_RING(cp_cons)][CP_IDX(cp_cons)];
+
+       if (!RX_CMP_VALID(rxcmp1, tmp_raw_cons))
+               return -EBUSY;
+
+       cmp_type = RX_CMP_TYPE(rxcmp);
+
+       prod = rxr->rx_prod;
+
+       if (cmp_type == CMP_TYPE_RX_L2_TPA_START_CMP) {
+               bnxt_tpa_start(bp, rxr, (struct rx_tpa_start_cmp *)rxcmp,
+                              (struct rx_tpa_start_cmp_ext *)rxcmp1);
+
+               goto next_rx_no_prod;
+
+       } else if (cmp_type == CMP_TYPE_RX_L2_TPA_END_CMP) {
+               skb = bnxt_tpa_end(bp, bnapi, &tmp_raw_cons,
+                                  (struct rx_tpa_end_cmp *)rxcmp,
+                                  (struct rx_tpa_end_cmp_ext *)rxcmp1,
+                                  agg_event);
+
+               if (unlikely(IS_ERR(skb)))
+                       return -EBUSY;
+
+               rc = -ENOMEM;
+               if (likely(skb)) {
+                       skb_record_rx_queue(skb, bnapi->index);
+                       skb_mark_napi_id(skb, &bnapi->napi);
+                       if (bnxt_busy_polling(bnapi))
+                               netif_receive_skb(skb);
+                       else
+                               napi_gro_receive(&bnapi->napi, skb);
+                       rc = 1;
+               }
+               goto next_rx_no_prod;
+       }
+
+       cons = rxcmp->rx_cmp_opaque;
+       rx_buf = &rxr->rx_buf_ring[cons];
+       data = rx_buf->data;
+       prefetch(data);
+
+       agg_bufs = (le32_to_cpu(rxcmp->rx_cmp_misc_v1) & RX_CMP_AGG_BUFS) >>
+                               RX_CMP_AGG_BUFS_SHIFT;
+
+       if (agg_bufs) {
+               if (!bnxt_agg_bufs_valid(bp, cpr, agg_bufs, &tmp_raw_cons))
+                       return -EBUSY;
+
+               cp_cons = NEXT_CMP(cp_cons);
+               *agg_event = true;
+       }
+
+       rx_buf->data = NULL;
+       if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L2_ERRORS) {
+               bnxt_reuse_rx_data(rxr, cons, data);
+               if (agg_bufs)
+                       bnxt_reuse_rx_agg_bufs(bnapi, cp_cons, agg_bufs);
+
+               rc = -EIO;
+               goto next_rx;
+       }
+
+       len = le32_to_cpu(rxcmp->rx_cmp_len_flags_type) >> RX_CMP_LEN_SHIFT;
+       dma_addr = dma_unmap_addr(rx_buf, mapping);
+
+       if (len <= bp->rx_copy_thresh) {
+               skb = bnxt_copy_skb(bnapi, data, len, dma_addr);
+               bnxt_reuse_rx_data(rxr, cons, data);
+               if (!skb) {
+                       rc = -ENOMEM;
+                       goto next_rx;
+               }
+       } else {
+               skb = bnxt_rx_skb(bp, rxr, cons, prod, data, dma_addr, len);
+               if (!skb) {
+                       rc = -ENOMEM;
+                       goto next_rx;
+               }
+       }
+
+       if (agg_bufs) {
+               skb = bnxt_rx_pages(bp, bnapi, skb, cp_cons, agg_bufs);
+               if (!skb) {
+                       rc = -ENOMEM;
+                       goto next_rx;
+               }
+       }
+
+       if (RX_CMP_HASH_VALID(rxcmp)) {
+               u32 hash_type = RX_CMP_HASH_TYPE(rxcmp);
+               enum pkt_hash_types type = PKT_HASH_TYPE_L4;
+
+               /* RSS profiles 1 and 3 with extract code 0 for inner 4-tuple */
+               if (hash_type != 1 && hash_type != 3)
+                       type = PKT_HASH_TYPE_L3;
+               skb_set_hash(skb, le32_to_cpu(rxcmp->rx_cmp_rss_hash), type);
+       }
+
+       skb->protocol = eth_type_trans(skb, dev);
+
+       if (rxcmp1->rx_cmp_flags2 &
+           cpu_to_le32(RX_CMP_FLAGS2_META_FORMAT_VLAN)) {
+               netdev_features_t features = skb->dev->features;
+               u32 meta_data = le32_to_cpu(rxcmp1->rx_cmp_meta_data);
+               u16 vlan_proto = meta_data >> RX_CMP_FLAGS2_METADATA_TPID_SFT;
+
+               if (((features & NETIF_F_HW_VLAN_CTAG_RX) &&
+                    vlan_proto == ETH_P_8021Q) ||
+                   ((features & NETIF_F_HW_VLAN_STAG_RX) &&
+                    vlan_proto == ETH_P_8021AD))
+                       __vlan_hwaccel_put_tag(skb, htons(vlan_proto),
+                                              meta_data &
+                                              RX_CMP_FLAGS2_METADATA_VID_MASK);
+       }
+
+       skb_checksum_none_assert(skb);
+       if (RX_CMP_L4_CS_OK(rxcmp1)) {
+               if (dev->features & NETIF_F_RXCSUM) {
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+                       skb->csum_level = RX_CMP_ENCAP(rxcmp1);
+               }
+       } else {
+               if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS)
+                       cpr->rx_l4_csum_errors++;
+       }
+
+       skb_record_rx_queue(skb, bnapi->index);
+       skb_mark_napi_id(skb, &bnapi->napi);
+       if (bnxt_busy_polling(bnapi))
+               netif_receive_skb(skb);
+       else
+               napi_gro_receive(&bnapi->napi, skb);
+       rc = 1;
+
+next_rx:
+       rxr->rx_prod = NEXT_RX(prod);
+
+next_rx_no_prod:
+       *raw_cons = tmp_raw_cons;
+
+       return rc;
+}
+
+static int bnxt_async_event_process(struct bnxt *bp,
+                                   struct hwrm_async_event_cmpl *cmpl)
+{
+       u16 event_id = le16_to_cpu(cmpl->event_id);
+
+       /* TODO CHIMP_FW: Define event id's for link change, error etc */
+       switch (event_id) {
+       case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE:
+               set_bit(BNXT_LINK_CHNG_SP_EVENT, &bp->sp_event);
+               schedule_work(&bp->sp_task);
+               break;
+       default:
+               netdev_err(bp->dev, "unhandled ASYNC event (id 0x%x)\n",
+                          event_id);
+               break;
+       }
+       return 0;
+}
+
+static int bnxt_hwrm_handler(struct bnxt *bp, struct tx_cmp *txcmp)
+{
+       u16 cmpl_type = TX_CMP_TYPE(txcmp), vf_id, seq_id;
+       struct hwrm_cmpl *h_cmpl = (struct hwrm_cmpl *)txcmp;
+       struct hwrm_fwd_req_cmpl *fwd_req_cmpl =
+                               (struct hwrm_fwd_req_cmpl *)txcmp;
+
+       switch (cmpl_type) {
+       case CMPL_BASE_TYPE_HWRM_DONE:
+               seq_id = le16_to_cpu(h_cmpl->sequence_id);
+               if (seq_id == bp->hwrm_intr_seq_id)
+                       bp->hwrm_intr_seq_id = HWRM_SEQ_ID_INVALID;
+               else
+                       netdev_err(bp->dev, "Invalid hwrm seq id %d\n", seq_id);
+               break;
+
+       case CMPL_BASE_TYPE_HWRM_FWD_REQ:
+               vf_id = le16_to_cpu(fwd_req_cmpl->source_id);
+
+               if ((vf_id < bp->pf.first_vf_id) ||
+                   (vf_id >= bp->pf.first_vf_id + bp->pf.active_vfs)) {
+                       netdev_err(bp->dev, "Msg contains invalid VF id %x\n",
+                                  vf_id);
+                       return -EINVAL;
+               }
+
+               set_bit(vf_id - bp->pf.first_vf_id, bp->pf.vf_event_bmap);
+               set_bit(BNXT_HWRM_EXEC_FWD_REQ_SP_EVENT, &bp->sp_event);
+               schedule_work(&bp->sp_task);
+               break;
+
+       case CMPL_BASE_TYPE_HWRM_ASYNC_EVENT:
+               bnxt_async_event_process(bp,
+                                        (struct hwrm_async_event_cmpl *)txcmp);
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static irqreturn_t bnxt_msix(int irq, void *dev_instance)
+{
+       struct bnxt_napi *bnapi = dev_instance;
+       struct bnxt *bp = bnapi->bp;
+       struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+       u32 cons = RING_CMP(cpr->cp_raw_cons);
+
+       prefetch(&cpr->cp_desc_ring[CP_RING(cons)][CP_IDX(cons)]);
+       napi_schedule(&bnapi->napi);
+       return IRQ_HANDLED;
+}
+
+static inline int bnxt_has_work(struct bnxt *bp, struct bnxt_cp_ring_info *cpr)
+{
+       u32 raw_cons = cpr->cp_raw_cons;
+       u16 cons = RING_CMP(raw_cons);
+       struct tx_cmp *txcmp;
+
+       txcmp = &cpr->cp_desc_ring[CP_RING(cons)][CP_IDX(cons)];
+
+       return TX_CMP_VALID(txcmp, raw_cons);
+}
+
+#define CAG_LEGACY_INT_STATUS  0x2014
+
+static irqreturn_t bnxt_inta(int irq, void *dev_instance)
+{
+       struct bnxt_napi *bnapi = dev_instance;
+       struct bnxt *bp = bnapi->bp;
+       struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+       u32 cons = RING_CMP(cpr->cp_raw_cons);
+       u32 int_status;
+
+       prefetch(&cpr->cp_desc_ring[CP_RING(cons)][CP_IDX(cons)]);
+
+       if (!bnxt_has_work(bp, cpr)) {
+               int_status = readl(bp->bar0 + CAG_LEGACY_INT_STATUS);
+               /* return if erroneous interrupt */
+               if (!(int_status & (0x10000 << cpr->cp_ring_struct.fw_ring_id)))
+                       return IRQ_NONE;
+       }
+
+       /* disable ring IRQ */
+       BNXT_CP_DB_IRQ_DIS(cpr->cp_doorbell);
+
+       /* Return here if interrupt is shared and is disabled. */
+       if (unlikely(atomic_read(&bp->intr_sem) != 0))
+               return IRQ_HANDLED;
+
+       napi_schedule(&bnapi->napi);
+       return IRQ_HANDLED;
+}
+
+static int bnxt_poll_work(struct bnxt *bp, struct bnxt_napi *bnapi, int budget)
+{
+       struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+       u32 raw_cons = cpr->cp_raw_cons;
+       u32 cons;
+       int tx_pkts = 0;
+       int rx_pkts = 0;
+       bool rx_event = false;
+       bool agg_event = false;
+       struct tx_cmp *txcmp;
+
+       while (1) {
+               int rc;
+
+               cons = RING_CMP(raw_cons);
+               txcmp = &cpr->cp_desc_ring[CP_RING(cons)][CP_IDX(cons)];
+
+               if (!TX_CMP_VALID(txcmp, raw_cons))
+                       break;
+
+               if (TX_CMP_TYPE(txcmp) == CMP_TYPE_TX_L2_CMP) {
+                       tx_pkts++;
+                       /* return full budget so NAPI will complete. */
+                       if (unlikely(tx_pkts > bp->tx_wake_thresh))
+                               rx_pkts = budget;
+               } else if ((TX_CMP_TYPE(txcmp) & 0x30) == 0x10) {
+                       rc = bnxt_rx_pkt(bp, bnapi, &raw_cons, &agg_event);
+                       if (likely(rc >= 0))
+                               rx_pkts += rc;
+                       else if (rc == -EBUSY)  /* partial completion */
+                               break;
+                       rx_event = true;
+               } else if (unlikely((TX_CMP_TYPE(txcmp) ==
+                                    CMPL_BASE_TYPE_HWRM_DONE) ||
+                                   (TX_CMP_TYPE(txcmp) ==
+                                    CMPL_BASE_TYPE_HWRM_FWD_REQ) ||
+                                   (TX_CMP_TYPE(txcmp) ==
+                                    CMPL_BASE_TYPE_HWRM_ASYNC_EVENT))) {
+                       bnxt_hwrm_handler(bp, txcmp);
+               }
+               raw_cons = NEXT_RAW_CMP(raw_cons);
+
+               if (rx_pkts == budget)
+                       break;
+       }
+
+       cpr->cp_raw_cons = raw_cons;
+       /* ACK completion ring before freeing tx ring and producing new
+        * buffers in rx/agg rings to prevent overflowing the completion
+        * ring.
+        */
+       BNXT_CP_DB(cpr->cp_doorbell, cpr->cp_raw_cons);
+
+       if (tx_pkts)
+               bnxt_tx_int(bp, bnapi, tx_pkts);
+
+       if (rx_event) {
+               struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+
+               writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell);
+               writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell);
+               if (agg_event) {
+                       writel(DB_KEY_RX | rxr->rx_agg_prod,
+                              rxr->rx_agg_doorbell);
+                       writel(DB_KEY_RX | rxr->rx_agg_prod,
+                              rxr->rx_agg_doorbell);
+               }
+       }
+       return rx_pkts;
+}
+
+static int bnxt_poll(struct napi_struct *napi, int budget)
+{
+       struct bnxt_napi *bnapi = container_of(napi, struct bnxt_napi, napi);
+       struct bnxt *bp = bnapi->bp;
+       struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+       int work_done = 0;
+
+       if (!bnxt_lock_napi(bnapi))
+               return budget;
+
+       while (1) {
+               work_done += bnxt_poll_work(bp, bnapi, budget - work_done);
+
+               if (work_done >= budget)
+                       break;
+
+               if (!bnxt_has_work(bp, cpr)) {
+                       napi_complete(napi);
+                       BNXT_CP_DB_REARM(cpr->cp_doorbell, cpr->cp_raw_cons);
+                       break;
+               }
+       }
+       mmiowb();
+       bnxt_unlock_napi(bnapi);
+       return work_done;
+}
+
+#ifdef CONFIG_NET_RX_BUSY_POLL
+static int bnxt_busy_poll(struct napi_struct *napi)
+{
+       struct bnxt_napi *bnapi = container_of(napi, struct bnxt_napi, napi);
+       struct bnxt *bp = bnapi->bp;
+       struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+       int rx_work, budget = 4;
+
+       if (atomic_read(&bp->intr_sem) != 0)
+               return LL_FLUSH_FAILED;
+
+       if (!bnxt_lock_poll(bnapi))
+               return LL_FLUSH_BUSY;
+
+       rx_work = bnxt_poll_work(bp, bnapi, budget);
+
+       BNXT_CP_DB_REARM(cpr->cp_doorbell, cpr->cp_raw_cons);
+
+       bnxt_unlock_poll(bnapi);
+       return rx_work;
+}
+#endif
+
+static void bnxt_free_tx_skbs(struct bnxt *bp)
+{
+       int i, max_idx;
+       struct pci_dev *pdev = bp->pdev;
+
+       if (!bp->bnapi)
+               return;
+
+       max_idx = bp->tx_nr_pages * TX_DESC_CNT;
+       for (i = 0; i < bp->tx_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_tx_ring_info *txr;
+               int j;
+
+               if (!bnapi)
+                       continue;
+
+               txr = &bnapi->tx_ring;
+               for (j = 0; j < max_idx;) {
+                       struct bnxt_sw_tx_bd *tx_buf = &txr->tx_buf_ring[j];
+                       struct sk_buff *skb = tx_buf->skb;
+                       int k, last;
+
+                       if (!skb) {
+                               j++;
+                               continue;
+                       }
+
+                       tx_buf->skb = NULL;
+
+                       if (tx_buf->is_push) {
+                               dev_kfree_skb(skb);
+                               j += 2;
+                               continue;
+                       }
+
+                       dma_unmap_single(&pdev->dev,
+                                        dma_unmap_addr(tx_buf, mapping),
+                                        skb_headlen(skb),
+                                        PCI_DMA_TODEVICE);
+
+                       last = tx_buf->nr_frags;
+                       j += 2;
+                       for (k = 0; k < last; k++, j = NEXT_TX(j)) {
+                               skb_frag_t *frag = &skb_shinfo(skb)->frags[k];
+
+                               tx_buf = &txr->tx_buf_ring[j];
+                               dma_unmap_page(
+                                       &pdev->dev,
+                                       dma_unmap_addr(tx_buf, mapping),
+                                       skb_frag_size(frag), PCI_DMA_TODEVICE);
+                       }
+                       dev_kfree_skb(skb);
+               }
+               netdev_tx_reset_queue(netdev_get_tx_queue(bp->dev, i));
+       }
+}
+
+static void bnxt_free_rx_skbs(struct bnxt *bp)
+{
+       int i, max_idx, max_agg_idx;
+       struct pci_dev *pdev = bp->pdev;
+
+       if (!bp->bnapi)
+               return;
+
+       max_idx = bp->rx_nr_pages * RX_DESC_CNT;
+       max_agg_idx = bp->rx_agg_nr_pages * RX_DESC_CNT;
+       for (i = 0; i < bp->rx_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_rx_ring_info *rxr;
+               int j;
+
+               if (!bnapi)
+                       continue;
+
+               rxr = &bnapi->rx_ring;
+
+               if (rxr->rx_tpa) {
+                       for (j = 0; j < MAX_TPA; j++) {
+                               struct bnxt_tpa_info *tpa_info =
+                                                       &rxr->rx_tpa[j];
+                               u8 *data = tpa_info->data;
+
+                               if (!data)
+                                       continue;
+
+                               dma_unmap_single(
+                                       &pdev->dev,
+                                       dma_unmap_addr(tpa_info, mapping),
+                                       bp->rx_buf_use_size,
+                                       PCI_DMA_FROMDEVICE);
+
+                               tpa_info->data = NULL;
+
+                               kfree(data);
+                       }
+               }
+
+               for (j = 0; j < max_idx; j++) {
+                       struct bnxt_sw_rx_bd *rx_buf = &rxr->rx_buf_ring[j];
+                       u8 *data = rx_buf->data;
+
+                       if (!data)
+                               continue;
+
+                       dma_unmap_single(&pdev->dev,
+                                        dma_unmap_addr(rx_buf, mapping),
+                                        bp->rx_buf_use_size,
+                                        PCI_DMA_FROMDEVICE);
+
+                       rx_buf->data = NULL;
+
+                       kfree(data);
+               }
+
+               for (j = 0; j < max_agg_idx; j++) {
+                       struct bnxt_sw_rx_agg_bd *rx_agg_buf =
+                               &rxr->rx_agg_ring[j];
+                       struct page *page = rx_agg_buf->page;
+
+                       if (!page)
+                               continue;
+
+                       dma_unmap_page(&pdev->dev,
+                                      dma_unmap_addr(rx_agg_buf, mapping),
+                                      PAGE_SIZE, PCI_DMA_FROMDEVICE);
+
+                       rx_agg_buf->page = NULL;
+                       __clear_bit(j, rxr->rx_agg_bmap);
+
+                       __free_page(page);
+               }
+       }
+}
+
+static void bnxt_free_skbs(struct bnxt *bp)
+{
+       bnxt_free_tx_skbs(bp);
+       bnxt_free_rx_skbs(bp);
+}
+
+static void bnxt_free_ring(struct bnxt *bp, struct bnxt_ring_struct *ring)
+{
+       struct pci_dev *pdev = bp->pdev;
+       int i;
+
+       for (i = 0; i < ring->nr_pages; i++) {
+               if (!ring->pg_arr[i])
+                       continue;
+
+               dma_free_coherent(&pdev->dev, ring->page_size,
+                                 ring->pg_arr[i], ring->dma_arr[i]);
+
+               ring->pg_arr[i] = NULL;
+       }
+       if (ring->pg_tbl) {
+               dma_free_coherent(&pdev->dev, ring->nr_pages * 8,
+                                 ring->pg_tbl, ring->pg_tbl_map);
+               ring->pg_tbl = NULL;
+       }
+       if (ring->vmem_size && *ring->vmem) {
+               vfree(*ring->vmem);
+               *ring->vmem = NULL;
+       }
+}
+
+static int bnxt_alloc_ring(struct bnxt *bp, struct bnxt_ring_struct *ring)
+{
+       int i;
+       struct pci_dev *pdev = bp->pdev;
+
+       if (ring->nr_pages > 1) {
+               ring->pg_tbl = dma_alloc_coherent(&pdev->dev,
+                                                 ring->nr_pages * 8,
+                                                 &ring->pg_tbl_map,
+                                                 GFP_KERNEL);
+               if (!ring->pg_tbl)
+                       return -ENOMEM;
+       }
+
+       for (i = 0; i < ring->nr_pages; i++) {
+               ring->pg_arr[i] = dma_alloc_coherent(&pdev->dev,
+                                                    ring->page_size,
+                                                    &ring->dma_arr[i],
+                                                    GFP_KERNEL);
+               if (!ring->pg_arr[i])
+                       return -ENOMEM;
+
+               if (ring->nr_pages > 1)
+                       ring->pg_tbl[i] = cpu_to_le64(ring->dma_arr[i]);
+       }
+
+       if (ring->vmem_size) {
+               *ring->vmem = vzalloc(ring->vmem_size);
+               if (!(*ring->vmem))
+                       return -ENOMEM;
+       }
+       return 0;
+}
+
+static void bnxt_free_rx_rings(struct bnxt *bp)
+{
+       int i;
+
+       if (!bp->bnapi)
+               return;
+
+       for (i = 0; i < bp->rx_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_rx_ring_info *rxr;
+               struct bnxt_ring_struct *ring;
+
+               if (!bnapi)
+                       continue;
+
+               rxr = &bnapi->rx_ring;
+
+               kfree(rxr->rx_tpa);
+               rxr->rx_tpa = NULL;
+
+               kfree(rxr->rx_agg_bmap);
+               rxr->rx_agg_bmap = NULL;
+
+               ring = &rxr->rx_ring_struct;
+               bnxt_free_ring(bp, ring);
+
+               ring = &rxr->rx_agg_ring_struct;
+               bnxt_free_ring(bp, ring);
+       }
+}
+
+static int bnxt_alloc_rx_rings(struct bnxt *bp)
+{
+       int i, rc, agg_rings = 0, tpa_rings = 0;
+
+       if (bp->flags & BNXT_FLAG_AGG_RINGS)
+               agg_rings = 1;
+
+       if (bp->flags & BNXT_FLAG_TPA)
+               tpa_rings = 1;
+
+       for (i = 0; i < bp->rx_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_rx_ring_info *rxr;
+               struct bnxt_ring_struct *ring;
+
+               if (!bnapi)
+                       continue;
+
+               rxr = &bnapi->rx_ring;
+               ring = &rxr->rx_ring_struct;
+
+               rc = bnxt_alloc_ring(bp, ring);
+               if (rc)
+                       return rc;
+
+               if (agg_rings) {
+                       u16 mem_size;
+
+                       ring = &rxr->rx_agg_ring_struct;
+                       rc = bnxt_alloc_ring(bp, ring);
+                       if (rc)
+                               return rc;
+
+                       rxr->rx_agg_bmap_size = bp->rx_agg_ring_mask + 1;
+                       mem_size = rxr->rx_agg_bmap_size / 8;
+                       rxr->rx_agg_bmap = kzalloc(mem_size, GFP_KERNEL);
+                       if (!rxr->rx_agg_bmap)
+                               return -ENOMEM;
+
+                       if (tpa_rings) {
+                               rxr->rx_tpa = kcalloc(MAX_TPA,
+                                               sizeof(struct bnxt_tpa_info),
+                                               GFP_KERNEL);
+                               if (!rxr->rx_tpa)
+                                       return -ENOMEM;
+                       }
+               }
+       }
+       return 0;
+}
+
+static void bnxt_free_tx_rings(struct bnxt *bp)
+{
+       int i;
+       struct pci_dev *pdev = bp->pdev;
+
+       if (!bp->bnapi)
+               return;
+
+       for (i = 0; i < bp->tx_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_tx_ring_info *txr;
+               struct bnxt_ring_struct *ring;
+
+               if (!bnapi)
+                       continue;
+
+               txr = &bnapi->tx_ring;
+
+               if (txr->tx_push) {
+                       dma_free_coherent(&pdev->dev, bp->tx_push_size,
+                                         txr->tx_push, txr->tx_push_mapping);
+                       txr->tx_push = NULL;
+               }
+
+               ring = &txr->tx_ring_struct;
+
+               bnxt_free_ring(bp, ring);
+       }
+}
+
+static int bnxt_alloc_tx_rings(struct bnxt *bp)
+{
+       int i, j, rc;
+       struct pci_dev *pdev = bp->pdev;
+
+       bp->tx_push_size = 0;
+       if (bp->tx_push_thresh) {
+               int push_size;
+
+               push_size  = L1_CACHE_ALIGN(sizeof(struct tx_push_bd) +
+                                       bp->tx_push_thresh);
+
+               if (push_size > 128) {
+                       push_size = 0;
+                       bp->tx_push_thresh = 0;
+               }
+
+               bp->tx_push_size = push_size;
+       }
+
+       for (i = 0, j = 0; i < bp->tx_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_tx_ring_info *txr;
+               struct bnxt_ring_struct *ring;
+
+               if (!bnapi)
+                       continue;
+
+               txr = &bnapi->tx_ring;
+               ring = &txr->tx_ring_struct;
+
+               rc = bnxt_alloc_ring(bp, ring);
+               if (rc)
+                       return rc;
+
+               if (bp->tx_push_size) {
+                       struct tx_bd *txbd;
+                       dma_addr_t mapping;
+
+                       /* One pre-allocated DMA buffer to backup
+                        * TX push operation
+                        */
+                       txr->tx_push = dma_alloc_coherent(&pdev->dev,
+                                               bp->tx_push_size,
+                                               &txr->tx_push_mapping,
+                                               GFP_KERNEL);
+
+                       if (!txr->tx_push)
+                               return -ENOMEM;
+
+                       txbd = &txr->tx_push->txbd1;
+
+                       mapping = txr->tx_push_mapping +
+                               sizeof(struct tx_push_bd);
+                       txbd->tx_bd_haddr = cpu_to_le64(mapping);
+
+                       memset(txbd + 1, 0, sizeof(struct tx_bd_ext));
+               }
+               ring->queue_id = bp->q_info[j].queue_id;
+               if (i % bp->tx_nr_rings_per_tc == (bp->tx_nr_rings_per_tc - 1))
+                       j++;
+       }
+       return 0;
+}
+
+static void bnxt_free_cp_rings(struct bnxt *bp)
+{
+       int i;
+
+       if (!bp->bnapi)
+               return;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr;
+               struct bnxt_ring_struct *ring;
+
+               if (!bnapi)
+                       continue;
+
+               cpr = &bnapi->cp_ring;
+               ring = &cpr->cp_ring_struct;
+
+               bnxt_free_ring(bp, ring);
+       }
+}
+
+static int bnxt_alloc_cp_rings(struct bnxt *bp)
+{
+       int i, rc;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr;
+               struct bnxt_ring_struct *ring;
+
+               if (!bnapi)
+                       continue;
+
+               cpr = &bnapi->cp_ring;
+               ring = &cpr->cp_ring_struct;
+
+               rc = bnxt_alloc_ring(bp, ring);
+               if (rc)
+                       return rc;
+       }
+       return 0;
+}
+
+static void bnxt_init_ring_struct(struct bnxt *bp)
+{
+       int i;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr;
+               struct bnxt_rx_ring_info *rxr;
+               struct bnxt_tx_ring_info *txr;
+               struct bnxt_ring_struct *ring;
+
+               if (!bnapi)
+                       continue;
+
+               cpr = &bnapi->cp_ring;
+               ring = &cpr->cp_ring_struct;
+               ring->nr_pages = bp->cp_nr_pages;
+               ring->page_size = HW_CMPD_RING_SIZE;
+               ring->pg_arr = (void **)cpr->cp_desc_ring;
+               ring->dma_arr = cpr->cp_desc_mapping;
+               ring->vmem_size = 0;
+
+               rxr = &bnapi->rx_ring;
+               ring = &rxr->rx_ring_struct;
+               ring->nr_pages = bp->rx_nr_pages;
+               ring->page_size = HW_RXBD_RING_SIZE;
+               ring->pg_arr = (void **)rxr->rx_desc_ring;
+               ring->dma_arr = rxr->rx_desc_mapping;
+               ring->vmem_size = SW_RXBD_RING_SIZE * bp->rx_nr_pages;
+               ring->vmem = (void **)&rxr->rx_buf_ring;
+
+               ring = &rxr->rx_agg_ring_struct;
+               ring->nr_pages = bp->rx_agg_nr_pages;
+               ring->page_size = HW_RXBD_RING_SIZE;
+               ring->pg_arr = (void **)rxr->rx_agg_desc_ring;
+               ring->dma_arr = rxr->rx_agg_desc_mapping;
+               ring->vmem_size = SW_RXBD_AGG_RING_SIZE * bp->rx_agg_nr_pages;
+               ring->vmem = (void **)&rxr->rx_agg_ring;
+
+               txr = &bnapi->tx_ring;
+               ring = &txr->tx_ring_struct;
+               ring->nr_pages = bp->tx_nr_pages;
+               ring->page_size = HW_RXBD_RING_SIZE;
+               ring->pg_arr = (void **)txr->tx_desc_ring;
+               ring->dma_arr = txr->tx_desc_mapping;
+               ring->vmem_size = SW_TXBD_RING_SIZE * bp->tx_nr_pages;
+               ring->vmem = (void **)&txr->tx_buf_ring;
+       }
+}
+
+static void bnxt_init_rxbd_pages(struct bnxt_ring_struct *ring, u32 type)
+{
+       int i;
+       u32 prod;
+       struct rx_bd **rx_buf_ring;
+
+       rx_buf_ring = (struct rx_bd **)ring->pg_arr;
+       for (i = 0, prod = 0; i < ring->nr_pages; i++) {
+               int j;
+               struct rx_bd *rxbd;
+
+               rxbd = rx_buf_ring[i];
+               if (!rxbd)
+                       continue;
+
+               for (j = 0; j < RX_DESC_CNT; j++, rxbd++, prod++) {
+                       rxbd->rx_bd_len_flags_type = cpu_to_le32(type);
+                       rxbd->rx_bd_opaque = prod;
+               }
+       }
+}
+
+static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr)
+{
+       struct net_device *dev = bp->dev;
+       struct bnxt_napi *bnapi = bp->bnapi[ring_nr];
+       struct bnxt_rx_ring_info *rxr;
+       struct bnxt_ring_struct *ring;
+       u32 prod, type;
+       int i;
+
+       if (!bnapi)
+               return -EINVAL;
+
+       type = (bp->rx_buf_use_size << RX_BD_LEN_SHIFT) |
+               RX_BD_TYPE_RX_PACKET_BD | RX_BD_FLAGS_EOP;
+
+       if (NET_IP_ALIGN == 2)
+               type |= RX_BD_FLAGS_SOP;
+
+       rxr = &bnapi->rx_ring;
+       ring = &rxr->rx_ring_struct;
+       bnxt_init_rxbd_pages(ring, type);
+
+       prod = rxr->rx_prod;
+       for (i = 0; i < bp->rx_ring_size; i++) {
+               if (bnxt_alloc_rx_data(bp, rxr, prod, GFP_KERNEL) != 0) {
+                       netdev_warn(dev, "init'ed rx ring %d with %d/%d skbs only\n",
+                                   ring_nr, i, bp->rx_ring_size);
+                       break;
+               }
+               prod = NEXT_RX(prod);
+       }
+       rxr->rx_prod = prod;
+       ring->fw_ring_id = INVALID_HW_RING_ID;
+
+       if (!(bp->flags & BNXT_FLAG_AGG_RINGS))
+               return 0;
+
+       ring = &rxr->rx_agg_ring_struct;
+
+       type = ((u32)PAGE_SIZE << RX_BD_LEN_SHIFT) |
+               RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP;
+
+       bnxt_init_rxbd_pages(ring, type);
+
+       prod = rxr->rx_agg_prod;
+       for (i = 0; i < bp->rx_agg_ring_size; i++) {
+               if (bnxt_alloc_rx_page(bp, rxr, prod, GFP_KERNEL) != 0) {
+                       netdev_warn(dev, "init'ed rx ring %d with %d/%d pages only\n",
+                                   ring_nr, i, bp->rx_ring_size);
+                       break;
+               }
+               prod = NEXT_RX_AGG(prod);
+       }
+       rxr->rx_agg_prod = prod;
+       ring->fw_ring_id = INVALID_HW_RING_ID;
+
+       if (bp->flags & BNXT_FLAG_TPA) {
+               if (rxr->rx_tpa) {
+                       u8 *data;
+                       dma_addr_t mapping;
+
+                       for (i = 0; i < MAX_TPA; i++) {
+                               data = __bnxt_alloc_rx_data(bp, &mapping,
+                                                           GFP_KERNEL);
+                               if (!data)
+                                       return -ENOMEM;
+
+                               rxr->rx_tpa[i].data = data;
+                               rxr->rx_tpa[i].mapping = mapping;
+                       }
+               } else {
+                       netdev_err(bp->dev, "No resource allocated for LRO/GRO\n");
+                       return -ENOMEM;
+               }
+       }
+
+       return 0;
+}
+
+static int bnxt_init_rx_rings(struct bnxt *bp)
+{
+       int i, rc = 0;
+
+       for (i = 0; i < bp->rx_nr_rings; i++) {
+               rc = bnxt_init_one_rx_ring(bp, i);
+               if (rc)
+                       break;
+       }
+
+       return rc;
+}
+
+static int bnxt_init_tx_rings(struct bnxt *bp)
+{
+       u16 i;
+
+       bp->tx_wake_thresh = max_t(int, bp->tx_ring_size / 2,
+                                  MAX_SKB_FRAGS + 1);
+
+       for (i = 0; i < bp->tx_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
+               struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
+
+               ring->fw_ring_id = INVALID_HW_RING_ID;
+       }
+
+       return 0;
+}
+
+static void bnxt_free_ring_grps(struct bnxt *bp)
+{
+       kfree(bp->grp_info);
+       bp->grp_info = NULL;
+}
+
+static int bnxt_init_ring_grps(struct bnxt *bp, bool irq_re_init)
+{
+       int i;
+
+       if (irq_re_init) {
+               bp->grp_info = kcalloc(bp->cp_nr_rings,
+                                      sizeof(struct bnxt_ring_grp_info),
+                                      GFP_KERNEL);
+               if (!bp->grp_info)
+                       return -ENOMEM;
+       }
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               if (irq_re_init)
+                       bp->grp_info[i].fw_stats_ctx = INVALID_HW_RING_ID;
+               bp->grp_info[i].fw_grp_id = INVALID_HW_RING_ID;
+               bp->grp_info[i].rx_fw_ring_id = INVALID_HW_RING_ID;
+               bp->grp_info[i].agg_fw_ring_id = INVALID_HW_RING_ID;
+               bp->grp_info[i].cp_fw_ring_id = INVALID_HW_RING_ID;
+       }
+       return 0;
+}
+
+static void bnxt_free_vnics(struct bnxt *bp)
+{
+       kfree(bp->vnic_info);
+       bp->vnic_info = NULL;
+       bp->nr_vnics = 0;
+}
+
+static int bnxt_alloc_vnics(struct bnxt *bp)
+{
+       int num_vnics = 1;
+
+#ifdef CONFIG_RFS_ACCEL
+       if (bp->flags & BNXT_FLAG_RFS)
+               num_vnics += bp->rx_nr_rings;
+#endif
+
+       bp->vnic_info = kcalloc(num_vnics, sizeof(struct bnxt_vnic_info),
+                               GFP_KERNEL);
+       if (!bp->vnic_info)
+               return -ENOMEM;
+
+       bp->nr_vnics = num_vnics;
+       return 0;
+}
+
+static void bnxt_init_vnics(struct bnxt *bp)
+{
+       int i;
+
+       for (i = 0; i < bp->nr_vnics; i++) {
+               struct bnxt_vnic_info *vnic = &bp->vnic_info[i];
+
+               vnic->fw_vnic_id = INVALID_HW_RING_ID;
+               vnic->fw_rss_cos_lb_ctx = INVALID_HW_RING_ID;
+               vnic->fw_l2_ctx_id = INVALID_HW_RING_ID;
+
+               if (bp->vnic_info[i].rss_hash_key) {
+                       if (i == 0)
+                               prandom_bytes(vnic->rss_hash_key,
+                                             HW_HASH_KEY_SIZE);
+                       else
+                               memcpy(vnic->rss_hash_key,
+                                      bp->vnic_info[0].rss_hash_key,
+                                      HW_HASH_KEY_SIZE);
+               }
+       }
+}
+
+static int bnxt_calc_nr_ring_pages(u32 ring_size, int desc_per_pg)
+{
+       int pages;
+
+       pages = ring_size / desc_per_pg;
+
+       if (!pages)
+               return 1;
+
+       pages++;
+
+       while (pages & (pages - 1))
+               pages++;
+
+       return pages;
+}
+
+static void bnxt_set_tpa_flags(struct bnxt *bp)
+{
+       bp->flags &= ~BNXT_FLAG_TPA;
+       if (bp->dev->features & NETIF_F_LRO)
+               bp->flags |= BNXT_FLAG_LRO;
+       if ((bp->dev->features & NETIF_F_GRO) && (bp->pdev->revision > 0))
+               bp->flags |= BNXT_FLAG_GRO;
+}
+
+/* bp->rx_ring_size, bp->tx_ring_size, dev->mtu, BNXT_FLAG_{G|L}RO flags must
+ * be set on entry.
+ */
+void bnxt_set_ring_params(struct bnxt *bp)
+{
+       u32 ring_size, rx_size, rx_space;
+       u32 agg_factor = 0, agg_ring_size = 0;
+
+       /* 8 for CRC and VLAN */
+       rx_size = SKB_DATA_ALIGN(bp->dev->mtu + ETH_HLEN + NET_IP_ALIGN + 8);
+
+       rx_space = rx_size + NET_SKB_PAD +
+               SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+       bp->rx_copy_thresh = BNXT_RX_COPY_THRESH;
+       ring_size = bp->rx_ring_size;
+       bp->rx_agg_ring_size = 0;
+       bp->rx_agg_nr_pages = 0;
+
+       if (bp->flags & BNXT_FLAG_TPA)
+               agg_factor = 4;
+
+       bp->flags &= ~BNXT_FLAG_JUMBO;
+       if (rx_space > PAGE_SIZE) {
+               u32 jumbo_factor;
+
+               bp->flags |= BNXT_FLAG_JUMBO;
+               jumbo_factor = PAGE_ALIGN(bp->dev->mtu - 40) >> PAGE_SHIFT;
+               if (jumbo_factor > agg_factor)
+                       agg_factor = jumbo_factor;
+       }
+       agg_ring_size = ring_size * agg_factor;
+
+       if (agg_ring_size) {
+               bp->rx_agg_nr_pages = bnxt_calc_nr_ring_pages(agg_ring_size,
+                                                       RX_DESC_CNT);
+               if (bp->rx_agg_nr_pages > MAX_RX_AGG_PAGES) {
+                       u32 tmp = agg_ring_size;
+
+                       bp->rx_agg_nr_pages = MAX_RX_AGG_PAGES;
+                       agg_ring_size = MAX_RX_AGG_PAGES * RX_DESC_CNT - 1;
+                       netdev_warn(bp->dev, "rx agg ring size %d reduced to %d.\n",
+                                   tmp, agg_ring_size);
+               }
+               bp->rx_agg_ring_size = agg_ring_size;
+               bp->rx_agg_ring_mask = (bp->rx_agg_nr_pages * RX_DESC_CNT) - 1;
+               rx_size = SKB_DATA_ALIGN(BNXT_RX_COPY_THRESH + NET_IP_ALIGN);
+               rx_space = rx_size + NET_SKB_PAD +
+                       SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+       }
+
+       bp->rx_buf_use_size = rx_size;
+       bp->rx_buf_size = rx_space;
+
+       bp->rx_nr_pages = bnxt_calc_nr_ring_pages(ring_size, RX_DESC_CNT);
+       bp->rx_ring_mask = (bp->rx_nr_pages * RX_DESC_CNT) - 1;
+
+       ring_size = bp->tx_ring_size;
+       bp->tx_nr_pages = bnxt_calc_nr_ring_pages(ring_size, TX_DESC_CNT);
+       bp->tx_ring_mask = (bp->tx_nr_pages * TX_DESC_CNT) - 1;
+
+       ring_size = bp->rx_ring_size * (2 + agg_factor) + bp->tx_ring_size;
+       bp->cp_ring_size = ring_size;
+
+       bp->cp_nr_pages = bnxt_calc_nr_ring_pages(ring_size, CP_DESC_CNT);
+       if (bp->cp_nr_pages > MAX_CP_PAGES) {
+               bp->cp_nr_pages = MAX_CP_PAGES;
+               bp->cp_ring_size = MAX_CP_PAGES * CP_DESC_CNT - 1;
+               netdev_warn(bp->dev, "completion ring size %d reduced to %d.\n",
+                           ring_size, bp->cp_ring_size);
+       }
+       bp->cp_bit = bp->cp_nr_pages * CP_DESC_CNT;
+       bp->cp_ring_mask = bp->cp_bit - 1;
+}
+
+static void bnxt_free_vnic_attributes(struct bnxt *bp)
+{
+       int i;
+       struct bnxt_vnic_info *vnic;
+       struct pci_dev *pdev = bp->pdev;
+
+       if (!bp->vnic_info)
+               return;
+
+       for (i = 0; i < bp->nr_vnics; i++) {
+               vnic = &bp->vnic_info[i];
+
+               kfree(vnic->fw_grp_ids);
+               vnic->fw_grp_ids = NULL;
+
+               kfree(vnic->uc_list);
+               vnic->uc_list = NULL;
+
+               if (vnic->mc_list) {
+                       dma_free_coherent(&pdev->dev, vnic->mc_list_size,
+                                         vnic->mc_list, vnic->mc_list_mapping);
+                       vnic->mc_list = NULL;
+               }
+
+               if (vnic->rss_table) {
+                       dma_free_coherent(&pdev->dev, PAGE_SIZE,
+                                         vnic->rss_table,
+                                         vnic->rss_table_dma_addr);
+                       vnic->rss_table = NULL;
+               }
+
+               vnic->rss_hash_key = NULL;
+               vnic->flags = 0;
+       }
+}
+
+static int bnxt_alloc_vnic_attributes(struct bnxt *bp)
+{
+       int i, rc = 0, size;
+       struct bnxt_vnic_info *vnic;
+       struct pci_dev *pdev = bp->pdev;
+       int max_rings;
+
+       for (i = 0; i < bp->nr_vnics; i++) {
+               vnic = &bp->vnic_info[i];
+
+               if (vnic->flags & BNXT_VNIC_UCAST_FLAG) {
+                       int mem_size = (BNXT_MAX_UC_ADDRS - 1) * ETH_ALEN;
+
+                       if (mem_size > 0) {
+                               vnic->uc_list = kmalloc(mem_size, GFP_KERNEL);
+                               if (!vnic->uc_list) {
+                                       rc = -ENOMEM;
+                                       goto out;
+                               }
+                       }
+               }
+
+               if (vnic->flags & BNXT_VNIC_MCAST_FLAG) {
+                       vnic->mc_list_size = BNXT_MAX_MC_ADDRS * ETH_ALEN;
+                       vnic->mc_list =
+                               dma_alloc_coherent(&pdev->dev,
+                                                  vnic->mc_list_size,
+                                                  &vnic->mc_list_mapping,
+                                                  GFP_KERNEL);
+                       if (!vnic->mc_list) {
+                               rc = -ENOMEM;
+                               goto out;
+                       }
+               }
+
+               if (vnic->flags & BNXT_VNIC_RSS_FLAG)
+                       max_rings = bp->rx_nr_rings;
+               else
+                       max_rings = 1;
+
+               vnic->fw_grp_ids = kcalloc(max_rings, sizeof(u16), GFP_KERNEL);
+               if (!vnic->fw_grp_ids) {
+                       rc = -ENOMEM;
+                       goto out;
+               }
+
+               /* Allocate rss table and hash key */
+               vnic->rss_table = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
+                                                    &vnic->rss_table_dma_addr,
+                                                    GFP_KERNEL);
+               if (!vnic->rss_table) {
+                       rc = -ENOMEM;
+                       goto out;
+               }
+
+               size = L1_CACHE_ALIGN(HW_HASH_INDEX_SIZE * sizeof(u16));
+
+               vnic->rss_hash_key = ((void *)vnic->rss_table) + size;
+               vnic->rss_hash_key_dma_addr = vnic->rss_table_dma_addr + size;
+       }
+       return 0;
+
+out:
+       return rc;
+}
+
+static void bnxt_free_hwrm_resources(struct bnxt *bp)
+{
+       struct pci_dev *pdev = bp->pdev;
+
+       dma_free_coherent(&pdev->dev, PAGE_SIZE, bp->hwrm_cmd_resp_addr,
+                         bp->hwrm_cmd_resp_dma_addr);
+
+       bp->hwrm_cmd_resp_addr = NULL;
+       if (bp->hwrm_dbg_resp_addr) {
+               dma_free_coherent(&pdev->dev, HWRM_DBG_REG_BUF_SIZE,
+                                 bp->hwrm_dbg_resp_addr,
+                                 bp->hwrm_dbg_resp_dma_addr);
+
+               bp->hwrm_dbg_resp_addr = NULL;
+       }
+}
+
+static int bnxt_alloc_hwrm_resources(struct bnxt *bp)
+{
+       struct pci_dev *pdev = bp->pdev;
+
+       bp->hwrm_cmd_resp_addr = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
+                                                  &bp->hwrm_cmd_resp_dma_addr,
+                                                  GFP_KERNEL);
+       if (!bp->hwrm_cmd_resp_addr)
+               return -ENOMEM;
+       bp->hwrm_dbg_resp_addr = dma_alloc_coherent(&pdev->dev,
+                                                   HWRM_DBG_REG_BUF_SIZE,
+                                                   &bp->hwrm_dbg_resp_dma_addr,
+                                                   GFP_KERNEL);
+       if (!bp->hwrm_dbg_resp_addr)
+               netdev_warn(bp->dev, "fail to alloc debug register dma mem\n");
+
+       return 0;
+}
+
+static void bnxt_free_stats(struct bnxt *bp)
+{
+       u32 size, i;
+       struct pci_dev *pdev = bp->pdev;
+
+       if (!bp->bnapi)
+               return;
+
+       size = sizeof(struct ctx_hw_stats);
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+
+               if (cpr->hw_stats) {
+                       dma_free_coherent(&pdev->dev, size, cpr->hw_stats,
+                                         cpr->hw_stats_map);
+                       cpr->hw_stats = NULL;
+               }
+       }
+}
+
+static int bnxt_alloc_stats(struct bnxt *bp)
+{
+       u32 size, i;
+       struct pci_dev *pdev = bp->pdev;
+
+       size = sizeof(struct ctx_hw_stats);
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+
+               cpr->hw_stats = dma_alloc_coherent(&pdev->dev, size,
+                                                  &cpr->hw_stats_map,
+                                                  GFP_KERNEL);
+               if (!cpr->hw_stats)
+                       return -ENOMEM;
+
+               cpr->hw_stats_ctx_id = INVALID_STATS_CTX_ID;
+       }
+       return 0;
+}
+
+static void bnxt_clear_ring_indices(struct bnxt *bp)
+{
+       int i;
+
+       if (!bp->bnapi)
+               return;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr;
+               struct bnxt_rx_ring_info *rxr;
+               struct bnxt_tx_ring_info *txr;
+
+               if (!bnapi)
+                       continue;
+
+               cpr = &bnapi->cp_ring;
+               cpr->cp_raw_cons = 0;
+
+               txr = &bnapi->tx_ring;
+               txr->tx_prod = 0;
+               txr->tx_cons = 0;
+
+               rxr = &bnapi->rx_ring;
+               rxr->rx_prod = 0;
+               rxr->rx_agg_prod = 0;
+               rxr->rx_sw_agg_prod = 0;
+       }
+}
+
+static void bnxt_free_ntp_fltrs(struct bnxt *bp, bool irq_reinit)
+{
+#ifdef CONFIG_RFS_ACCEL
+       int i;
+
+       /* Under rtnl_lock and all our NAPIs have been disabled.  It's
+        * safe to delete the hash table.
+        */
+       for (i = 0; i < BNXT_NTP_FLTR_HASH_SIZE; i++) {
+               struct hlist_head *head;
+               struct hlist_node *tmp;
+               struct bnxt_ntuple_filter *fltr;
+
+               head = &bp->ntp_fltr_hash_tbl[i];
+               hlist_for_each_entry_safe(fltr, tmp, head, hash) {
+                       hlist_del(&fltr->hash);
+                       kfree(fltr);
+               }
+       }
+       if (irq_reinit) {
+               kfree(bp->ntp_fltr_bmap);
+               bp->ntp_fltr_bmap = NULL;
+       }
+       bp->ntp_fltr_count = 0;
+#endif
+}
+
+static int bnxt_alloc_ntp_fltrs(struct bnxt *bp)
+{
+#ifdef CONFIG_RFS_ACCEL
+       int i, rc = 0;
+
+       if (!(bp->flags & BNXT_FLAG_RFS))
+               return 0;
+
+       for (i = 0; i < BNXT_NTP_FLTR_HASH_SIZE; i++)
+               INIT_HLIST_HEAD(&bp->ntp_fltr_hash_tbl[i]);
+
+       bp->ntp_fltr_count = 0;
+       bp->ntp_fltr_bmap = kzalloc(BITS_TO_LONGS(BNXT_NTP_FLTR_MAX_FLTR),
+                                   GFP_KERNEL);
+
+       if (!bp->ntp_fltr_bmap)
+               rc = -ENOMEM;
+
+       return rc;
+#else
+       return 0;
+#endif
+}
+
+static void bnxt_free_mem(struct bnxt *bp, bool irq_re_init)
+{
+       bnxt_free_vnic_attributes(bp);
+       bnxt_free_tx_rings(bp);
+       bnxt_free_rx_rings(bp);
+       bnxt_free_cp_rings(bp);
+       bnxt_free_ntp_fltrs(bp, irq_re_init);
+       if (irq_re_init) {
+               bnxt_free_stats(bp);
+               bnxt_free_ring_grps(bp);
+               bnxt_free_vnics(bp);
+               kfree(bp->bnapi);
+               bp->bnapi = NULL;
+       } else {
+               bnxt_clear_ring_indices(bp);
+       }
+}
+
+static int bnxt_alloc_mem(struct bnxt *bp, bool irq_re_init)
+{
+       int i, rc, size, arr_size;
+       void *bnapi;
+
+       if (irq_re_init) {
+               /* Allocate bnapi mem pointer array and mem block for
+                * all queues
+                */
+               arr_size = L1_CACHE_ALIGN(sizeof(struct bnxt_napi *) *
+                               bp->cp_nr_rings);
+               size = L1_CACHE_ALIGN(sizeof(struct bnxt_napi));
+               bnapi = kzalloc(arr_size + size * bp->cp_nr_rings, GFP_KERNEL);
+               if (!bnapi)
+                       return -ENOMEM;
+
+               bp->bnapi = bnapi;
+               bnapi += arr_size;
+               for (i = 0; i < bp->cp_nr_rings; i++, bnapi += size) {
+                       bp->bnapi[i] = bnapi;
+                       bp->bnapi[i]->index = i;
+                       bp->bnapi[i]->bp = bp;
+               }
+
+               rc = bnxt_alloc_stats(bp);
+               if (rc)
+                       goto alloc_mem_err;
+
+               rc = bnxt_alloc_ntp_fltrs(bp);
+               if (rc)
+                       goto alloc_mem_err;
+
+               rc = bnxt_alloc_vnics(bp);
+               if (rc)
+                       goto alloc_mem_err;
+       }
+
+       bnxt_init_ring_struct(bp);
+
+       rc = bnxt_alloc_rx_rings(bp);
+       if (rc)
+               goto alloc_mem_err;
+
+       rc = bnxt_alloc_tx_rings(bp);
+       if (rc)
+               goto alloc_mem_err;
+
+       rc = bnxt_alloc_cp_rings(bp);
+       if (rc)
+               goto alloc_mem_err;
+
+       bp->vnic_info[0].flags |= BNXT_VNIC_RSS_FLAG | BNXT_VNIC_MCAST_FLAG |
+                                 BNXT_VNIC_UCAST_FLAG;
+       rc = bnxt_alloc_vnic_attributes(bp);
+       if (rc)
+               goto alloc_mem_err;
+       return 0;
+
+alloc_mem_err:
+       bnxt_free_mem(bp, true);
+       return rc;
+}
+
+void bnxt_hwrm_cmd_hdr_init(struct bnxt *bp, void *request, u16 req_type,
+                           u16 cmpl_ring, u16 target_id)
+{
+       struct hwrm_cmd_req_hdr *req = request;
+
+       req->cmpl_ring_req_type =
+               cpu_to_le32(req_type | (cmpl_ring << HWRM_CMPL_RING_SFT));
+       req->target_id_seq_id = cpu_to_le32(target_id << HWRM_TARGET_FID_SFT);
+       req->resp_addr = cpu_to_le64(bp->hwrm_cmd_resp_dma_addr);
+}
+
+int _hwrm_send_message(struct bnxt *bp, void *msg, u32 msg_len, int timeout)
+{
+       int i, intr_process, rc;
+       struct hwrm_cmd_req_hdr *req = msg;
+       u32 *data = msg;
+       __le32 *resp_len, *valid;
+       u16 cp_ring_id, len = 0;
+       struct hwrm_err_output *resp = bp->hwrm_cmd_resp_addr;
+
+       req->target_id_seq_id |= cpu_to_le32(bp->hwrm_cmd_seq++);
+       memset(resp, 0, PAGE_SIZE);
+       cp_ring_id = (le32_to_cpu(req->cmpl_ring_req_type) &
+                     HWRM_CMPL_RING_MASK) >>
+                    HWRM_CMPL_RING_SFT;
+       intr_process = (cp_ring_id == INVALID_HW_RING_ID) ? 0 : 1;
+
+       /* Write request msg to hwrm channel */
+       __iowrite32_copy(bp->bar0, data, msg_len / 4);
+
+       /* currently supports only one outstanding message */
+       if (intr_process)
+               bp->hwrm_intr_seq_id = le32_to_cpu(req->target_id_seq_id) &
+                                      HWRM_SEQ_ID_MASK;
+
+       /* Ring channel doorbell */
+       writel(1, bp->bar0 + 0x100);
+
+       i = 0;
+       if (intr_process) {
+               /* Wait until hwrm response cmpl interrupt is processed */
+               while (bp->hwrm_intr_seq_id != HWRM_SEQ_ID_INVALID &&
+                      i++ < timeout) {
+                       usleep_range(600, 800);
+               }
+
+               if (bp->hwrm_intr_seq_id != HWRM_SEQ_ID_INVALID) {
+                       netdev_err(bp->dev, "Resp cmpl intr err msg: 0x%x\n",
+                                  req->cmpl_ring_req_type);
+                       return -1;
+               }
+       } else {
+               /* Check if response len is updated */
+               resp_len = bp->hwrm_cmd_resp_addr + HWRM_RESP_LEN_OFFSET;
+               for (i = 0; i < timeout; i++) {
+                       len = (le32_to_cpu(*resp_len) & HWRM_RESP_LEN_MASK) >>
+                             HWRM_RESP_LEN_SFT;
+                       if (len)
+                               break;
+                       usleep_range(600, 800);
+               }
+
+               if (i >= timeout) {
+                       netdev_err(bp->dev, "Error (timeout: %d) msg {0x%x 0x%x} len:%d\n",
+                                  timeout, req->cmpl_ring_req_type,
+                                  req->target_id_seq_id, *resp_len);
+                       return -1;
+               }
+
+               /* Last word of resp contains valid bit */
+               valid = bp->hwrm_cmd_resp_addr + len - 4;
+               for (i = 0; i < timeout; i++) {
+                       if (le32_to_cpu(*valid) & HWRM_RESP_VALID_MASK)
+                               break;
+                       usleep_range(600, 800);
+               }
+
+               if (i >= timeout) {
+                       netdev_err(bp->dev, "Error (timeout: %d) msg {0x%x 0x%x} len:%d v:%d\n",
+                                  timeout, req->cmpl_ring_req_type,
+                                  req->target_id_seq_id, len, *valid);
+                       return -1;
+               }
+       }
+
+       rc = le16_to_cpu(resp->error_code);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm req_type 0x%x seq id 0x%x error 0x%x\n",
+                          le16_to_cpu(resp->req_type),
+                          le16_to_cpu(resp->seq_id), rc);
+               return rc;
+       }
+       return 0;
+}
+
+int hwrm_send_message(struct bnxt *bp, void *msg, u32 msg_len, int timeout)
+{
+       int rc;
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, msg, msg_len, timeout);
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp)
+{
+       struct hwrm_func_drv_rgtr_input req = {0};
+       int i;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_DRV_RGTR, -1, -1);
+
+       req.enables =
+               cpu_to_le32(FUNC_DRV_RGTR_REQ_ENABLES_OS_TYPE |
+                           FUNC_DRV_RGTR_REQ_ENABLES_VER |
+                           FUNC_DRV_RGTR_REQ_ENABLES_ASYNC_EVENT_FWD);
+
+       /* TODO: current async event fwd bits are not defined and the firmware
+        * only checks if it is non-zero to enable async event forwarding
+        */
+       req.async_event_fwd[0] |= cpu_to_le32(1);
+       req.os_type = cpu_to_le16(1);
+       req.ver_maj = DRV_VER_MAJ;
+       req.ver_min = DRV_VER_MIN;
+       req.ver_upd = DRV_VER_UPD;
+
+       if (BNXT_PF(bp)) {
+               unsigned long vf_req_snif_bmap[4];
+               u32 *data = (u32 *)vf_req_snif_bmap;
+
+               memset(vf_req_snif_bmap, 0, 32);
+               for (i = 0; i < ARRAY_SIZE(bnxt_vf_req_snif); i++)
+                       __set_bit(bnxt_vf_req_snif[i], vf_req_snif_bmap);
+
+               for (i = 0; i < 8; i++) {
+                       req.vf_req_fwd[i] = cpu_to_le32(*data);
+                       data++;
+               }
+               req.enables |=
+                       cpu_to_le32(FUNC_DRV_RGTR_REQ_ENABLES_VF_REQ_FWD);
+       }
+
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
+static int bnxt_hwrm_tunnel_dst_port_free(struct bnxt *bp, u8 tunnel_type)
+{
+       u32 rc = 0;
+       struct hwrm_tunnel_dst_port_free_input req = {0};
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_TUNNEL_DST_PORT_FREE, -1, -1);
+       req.tunnel_type = tunnel_type;
+
+       switch (tunnel_type) {
+       case TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN:
+               req.tunnel_dst_port_id = bp->vxlan_fw_dst_port_id;
+               break;
+       case TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE:
+               req.tunnel_dst_port_id = bp->nge_fw_dst_port_id;
+               break;
+       default:
+               break;
+       }
+
+       rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (rc)
+               netdev_err(bp->dev, "hwrm_tunnel_dst_port_free failed. rc:%d\n",
+                          rc);
+       return rc;
+}
+
+static int bnxt_hwrm_tunnel_dst_port_alloc(struct bnxt *bp, __be16 port,
+                                          u8 tunnel_type)
+{
+       u32 rc = 0;
+       struct hwrm_tunnel_dst_port_alloc_input req = {0};
+       struct hwrm_tunnel_dst_port_alloc_output *resp = bp->hwrm_cmd_resp_addr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_TUNNEL_DST_PORT_ALLOC, -1, -1);
+
+       req.tunnel_type = tunnel_type;
+       req.tunnel_dst_port_val = port;
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm_tunnel_dst_port_alloc failed. rc:%d\n",
+                          rc);
+               goto err_out;
+       }
+
+       if (tunnel_type & TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN)
+               bp->vxlan_fw_dst_port_id = resp->tunnel_dst_port_id;
+
+       else if (tunnel_type & TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_GENEVE)
+               bp->nge_fw_dst_port_id = resp->tunnel_dst_port_id;
+err_out:
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_hwrm_cfa_l2_set_rx_mask(struct bnxt *bp, u16 vnic_id)
+{
+       struct hwrm_cfa_l2_set_rx_mask_input req = {0};
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id];
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_L2_SET_RX_MASK, -1, -1);
+       req.dflt_vnic_id = cpu_to_le32(vnic->fw_vnic_id);
+
+       req.num_mc_entries = cpu_to_le32(vnic->mc_list_count);
+       req.mc_tbl_addr = cpu_to_le64(vnic->mc_list_mapping);
+       req.mask = cpu_to_le32(vnic->rx_mask);
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
+#ifdef CONFIG_RFS_ACCEL
+static int bnxt_hwrm_cfa_ntuple_filter_free(struct bnxt *bp,
+                                           struct bnxt_ntuple_filter *fltr)
+{
+       struct hwrm_cfa_ntuple_filter_free_input req = {0};
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_NTUPLE_FILTER_FREE, -1, -1);
+       req.ntuple_filter_id = fltr->filter_id;
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
+#define BNXT_NTP_FLTR_FLAGS                                    \
+       (CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_L2_FILTER_ID |     \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_ETHERTYPE |        \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_MACADDR |      \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IPADDR_TYPE |      \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR |       \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR_MASK |  \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR |       \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR_MASK |  \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IP_PROTOCOL |      \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT |         \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT_MASK |    \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT |         \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK |    \
+        CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID)
+
+static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp,
+                                            struct bnxt_ntuple_filter *fltr)
+{
+       int rc = 0;
+       struct hwrm_cfa_ntuple_filter_alloc_input req = {0};
+       struct hwrm_cfa_ntuple_filter_alloc_output *resp =
+               bp->hwrm_cmd_resp_addr;
+       struct flow_keys *keys = &fltr->fkeys;
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[fltr->rxq + 1];
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_NTUPLE_FILTER_ALLOC, -1, -1);
+       req.l2_filter_id = bp->vnic_info[0].fw_l2_filter_id[0];
+
+       req.enables = cpu_to_le32(BNXT_NTP_FLTR_FLAGS);
+
+       req.ethertype = htons(ETH_P_IP);
+       memcpy(req.src_macaddr, fltr->src_mac_addr, ETH_ALEN);
+       req.ipaddr_type = 4;
+       req.ip_protocol = keys->basic.ip_proto;
+
+       req.src_ipaddr[0] = keys->addrs.v4addrs.src;
+       req.src_ipaddr_mask[0] = cpu_to_be32(0xffffffff);
+       req.dst_ipaddr[0] = keys->addrs.v4addrs.dst;
+       req.dst_ipaddr_mask[0] = cpu_to_be32(0xffffffff);
+
+       req.src_port = keys->ports.src;
+       req.src_port_mask = cpu_to_be16(0xffff);
+       req.dst_port = keys->ports.dst;
+       req.dst_port_mask = cpu_to_be16(0xffff);
+
+       req.dst_vnic_id = cpu_to_le16(vnic->fw_vnic_id);
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc)
+               fltr->filter_id = resp->ntuple_filter_id;
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+#endif
+
+static int bnxt_hwrm_set_vnic_filter(struct bnxt *bp, u16 vnic_id, u16 idx,
+                                    u8 *mac_addr)
+{
+       u32 rc = 0;
+       struct hwrm_cfa_l2_filter_alloc_input req = {0};
+       struct hwrm_cfa_l2_filter_alloc_output *resp = bp->hwrm_cmd_resp_addr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_L2_FILTER_ALLOC, -1, -1);
+       req.flags = cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX |
+                               CFA_L2_FILTER_ALLOC_REQ_FLAGS_OUTERMOST);
+       req.dst_vnic_id = cpu_to_le16(bp->vnic_info[vnic_id].fw_vnic_id);
+       req.enables =
+               cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR |
+                           CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID |
+                           CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR_MASK);
+       memcpy(req.l2_addr, mac_addr, ETH_ALEN);
+       req.l2_addr_mask[0] = 0xff;
+       req.l2_addr_mask[1] = 0xff;
+       req.l2_addr_mask[2] = 0xff;
+       req.l2_addr_mask[3] = 0xff;
+       req.l2_addr_mask[4] = 0xff;
+       req.l2_addr_mask[5] = 0xff;
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc)
+               bp->vnic_info[vnic_id].fw_l2_filter_id[idx] =
+                                                       resp->l2_filter_id;
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_hwrm_clear_vnic_filter(struct bnxt *bp)
+{
+       u16 i, j, num_of_vnics = 1; /* only vnic 0 supported */
+       int rc = 0;
+
+       /* Any associated ntuple filters will also be cleared by firmware. */
+       mutex_lock(&bp->hwrm_cmd_lock);
+       for (i = 0; i < num_of_vnics; i++) {
+               struct bnxt_vnic_info *vnic = &bp->vnic_info[i];
+
+               for (j = 0; j < vnic->uc_filter_count; j++) {
+                       struct hwrm_cfa_l2_filter_free_input req = {0};
+
+                       bnxt_hwrm_cmd_hdr_init(bp, &req,
+                                              HWRM_CFA_L2_FILTER_FREE, -1, -1);
+
+                       req.l2_filter_id = vnic->fw_l2_filter_id[j];
+
+                       rc = _hwrm_send_message(bp, &req, sizeof(req),
+                                               HWRM_CMD_TIMEOUT);
+               }
+               vnic->uc_filter_count = 0;
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+
+       return rc;
+}
+
+static int bnxt_hwrm_vnic_set_tpa(struct bnxt *bp, u16 vnic_id, u32 tpa_flags)
+{
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id];
+       struct hwrm_vnic_tpa_cfg_input req = {0};
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VNIC_TPA_CFG, -1, -1);
+
+       if (tpa_flags) {
+               u16 mss = bp->dev->mtu - 40;
+               u32 nsegs, n, segs = 0, flags;
+
+               flags = VNIC_TPA_CFG_REQ_FLAGS_TPA |
+                       VNIC_TPA_CFG_REQ_FLAGS_ENCAP_TPA |
+                       VNIC_TPA_CFG_REQ_FLAGS_RSC_WND_UPDATE |
+                       VNIC_TPA_CFG_REQ_FLAGS_AGG_WITH_ECN |
+                       VNIC_TPA_CFG_REQ_FLAGS_AGG_WITH_SAME_GRE_SEQ;
+               if (tpa_flags & BNXT_FLAG_GRO)
+                       flags |= VNIC_TPA_CFG_REQ_FLAGS_GRO;
+
+               req.flags = cpu_to_le32(flags);
+
+               req.enables =
+                       cpu_to_le32(VNIC_TPA_CFG_REQ_ENABLES_MAX_AGG_SEGS |
+                                   VNIC_TPA_CFG_REQ_ENABLES_MAX_AGGS);
+
+               /* Number of segs are log2 units, and first packet is not
+                * included as part of this units.
+                */
+               if (mss <= PAGE_SIZE) {
+                       n = PAGE_SIZE / mss;
+                       nsegs = (MAX_SKB_FRAGS - 1) * n;
+               } else {
+                       n = mss / PAGE_SIZE;
+                       if (mss & (PAGE_SIZE - 1))
+                               n++;
+                       nsegs = (MAX_SKB_FRAGS - n) / n;
+               }
+
+               segs = ilog2(nsegs);
+               req.max_agg_segs = cpu_to_le16(segs);
+               req.max_aggs = cpu_to_le16(VNIC_TPA_CFG_REQ_MAX_AGGS_MAX);
+       }
+       req.vnic_id = cpu_to_le16(vnic->fw_vnic_id);
+
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
+static int bnxt_hwrm_vnic_set_rss(struct bnxt *bp, u16 vnic_id, bool set_rss)
+{
+       u32 i, j, max_rings;
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id];
+       struct hwrm_vnic_rss_cfg_input req = {0};
+
+       if (vnic->fw_rss_cos_lb_ctx == INVALID_HW_RING_ID)
+               return 0;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VNIC_RSS_CFG, -1, -1);
+       if (set_rss) {
+               vnic->hash_type = BNXT_RSS_HASH_TYPE_FLAG_IPV4 |
+                                BNXT_RSS_HASH_TYPE_FLAG_TCP_IPV4 |
+                                BNXT_RSS_HASH_TYPE_FLAG_IPV6 |
+                                BNXT_RSS_HASH_TYPE_FLAG_TCP_IPV6;
+
+               req.hash_type = cpu_to_le32(vnic->hash_type);
+
+               if (vnic->flags & BNXT_VNIC_RSS_FLAG)
+                       max_rings = bp->rx_nr_rings;
+               else
+                       max_rings = 1;
+
+               /* Fill the RSS indirection table with ring group ids */
+               for (i = 0, j = 0; i < HW_HASH_INDEX_SIZE; i++, j++) {
+                       if (j == max_rings)
+                               j = 0;
+                       vnic->rss_table[i] = cpu_to_le16(vnic->fw_grp_ids[j]);
+               }
+
+               req.ring_grp_tbl_addr = cpu_to_le64(vnic->rss_table_dma_addr);
+               req.hash_key_tbl_addr =
+                       cpu_to_le64(vnic->rss_hash_key_dma_addr);
+       }
+       req.rss_ctx_idx = cpu_to_le16(vnic->fw_rss_cos_lb_ctx);
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
+static int bnxt_hwrm_vnic_set_hds(struct bnxt *bp, u16 vnic_id)
+{
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id];
+       struct hwrm_vnic_plcmodes_cfg_input req = {0};
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VNIC_PLCMODES_CFG, -1, -1);
+       req.flags = cpu_to_le32(VNIC_PLCMODES_CFG_REQ_FLAGS_JUMBO_PLACEMENT |
+                               VNIC_PLCMODES_CFG_REQ_FLAGS_HDS_IPV4 |
+                               VNIC_PLCMODES_CFG_REQ_FLAGS_HDS_IPV6);
+       req.enables =
+               cpu_to_le32(VNIC_PLCMODES_CFG_REQ_ENABLES_JUMBO_THRESH_VALID |
+                           VNIC_PLCMODES_CFG_REQ_ENABLES_HDS_THRESHOLD_VALID);
+       /* thresholds not implemented in firmware yet */
+       req.jumbo_thresh = cpu_to_le16(bp->rx_copy_thresh);
+       req.hds_threshold = cpu_to_le16(bp->rx_copy_thresh);
+       req.vnic_id = cpu_to_le32(vnic->fw_vnic_id);
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
+static void bnxt_hwrm_vnic_ctx_free_one(struct bnxt *bp, u16 vnic_id)
+{
+       struct hwrm_vnic_rss_cos_lb_ctx_free_input req = {0};
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VNIC_RSS_COS_LB_CTX_FREE, -1, -1);
+       req.rss_cos_lb_ctx_id =
+               cpu_to_le16(bp->vnic_info[vnic_id].fw_rss_cos_lb_ctx);
+
+       hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       bp->vnic_info[vnic_id].fw_rss_cos_lb_ctx = INVALID_HW_RING_ID;
+}
+
+static void bnxt_hwrm_vnic_ctx_free(struct bnxt *bp)
+{
+       int i;
+
+       for (i = 0; i < bp->nr_vnics; i++) {
+               struct bnxt_vnic_info *vnic = &bp->vnic_info[i];
+
+               if (vnic->fw_rss_cos_lb_ctx != INVALID_HW_RING_ID)
+                       bnxt_hwrm_vnic_ctx_free_one(bp, i);
+       }
+       bp->rsscos_nr_ctxs = 0;
+}
+
+static int bnxt_hwrm_vnic_ctx_alloc(struct bnxt *bp, u16 vnic_id)
+{
+       int rc;
+       struct hwrm_vnic_rss_cos_lb_ctx_alloc_input req = {0};
+       struct hwrm_vnic_rss_cos_lb_ctx_alloc_output *resp =
+                                               bp->hwrm_cmd_resp_addr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VNIC_RSS_COS_LB_CTX_ALLOC, -1,
+                              -1);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc)
+               bp->vnic_info[vnic_id].fw_rss_cos_lb_ctx =
+                       le16_to_cpu(resp->rss_cos_lb_ctx_id);
+       mutex_unlock(&bp->hwrm_cmd_lock);
+
+       return rc;
+}
+
+static int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id)
+{
+       int grp_idx = 0;
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id];
+       struct hwrm_vnic_cfg_input req = {0};
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VNIC_CFG, -1, -1);
+       /* Only RSS support for now TBD: COS & LB */
+       req.enables = cpu_to_le32(VNIC_CFG_REQ_ENABLES_DFLT_RING_GRP |
+                                 VNIC_CFG_REQ_ENABLES_RSS_RULE);
+       req.rss_rule = cpu_to_le16(vnic->fw_rss_cos_lb_ctx);
+       req.cos_rule = cpu_to_le16(0xffff);
+       if (vnic->flags & BNXT_VNIC_RSS_FLAG)
+               grp_idx = 0;
+       else if (vnic->flags & BNXT_VNIC_RFS_FLAG)
+               grp_idx = vnic_id - 1;
+
+       req.vnic_id = cpu_to_le16(vnic->fw_vnic_id);
+       req.dflt_ring_grp = cpu_to_le16(bp->grp_info[grp_idx].fw_grp_id);
+
+       req.lb_rule = cpu_to_le16(0xffff);
+       req.mru = cpu_to_le16(bp->dev->mtu + ETH_HLEN + ETH_FCS_LEN +
+                             VLAN_HLEN);
+
+       if (bp->flags & BNXT_FLAG_STRIP_VLAN)
+               req.flags |= cpu_to_le32(VNIC_CFG_REQ_FLAGS_VLAN_STRIP_MODE);
+
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
+static int bnxt_hwrm_vnic_free_one(struct bnxt *bp, u16 vnic_id)
+{
+       u32 rc = 0;
+
+       if (bp->vnic_info[vnic_id].fw_vnic_id != INVALID_HW_RING_ID) {
+               struct hwrm_vnic_free_input req = {0};
+
+               bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VNIC_FREE, -1, -1);
+               req.vnic_id =
+                       cpu_to_le32(bp->vnic_info[vnic_id].fw_vnic_id);
+
+               rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+               if (rc)
+                       return rc;
+               bp->vnic_info[vnic_id].fw_vnic_id = INVALID_HW_RING_ID;
+       }
+       return rc;
+}
+
+static void bnxt_hwrm_vnic_free(struct bnxt *bp)
+{
+       u16 i;
+
+       for (i = 0; i < bp->nr_vnics; i++)
+               bnxt_hwrm_vnic_free_one(bp, i);
+}
+
+static int bnxt_hwrm_vnic_alloc(struct bnxt *bp, u16 vnic_id, u16 start_grp_id,
+                               u16 end_grp_id)
+{
+       u32 rc = 0, i, j;
+       struct hwrm_vnic_alloc_input req = {0};
+       struct hwrm_vnic_alloc_output *resp = bp->hwrm_cmd_resp_addr;
+
+       /* map ring groups to this vnic */
+       for (i = start_grp_id, j = 0; i < end_grp_id; i++, j++) {
+               if (bp->grp_info[i].fw_grp_id == INVALID_HW_RING_ID) {
+                       netdev_err(bp->dev, "Not enough ring groups avail:%x req:%x\n",
+                                  j, (end_grp_id - start_grp_id));
+                       break;
+               }
+               bp->vnic_info[vnic_id].fw_grp_ids[j] =
+                                       bp->grp_info[i].fw_grp_id;
+       }
+
+       bp->vnic_info[vnic_id].fw_rss_cos_lb_ctx = INVALID_HW_RING_ID;
+       if (vnic_id == 0)
+               req.flags = cpu_to_le32(VNIC_ALLOC_REQ_FLAGS_DEFAULT);
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VNIC_ALLOC, -1, -1);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc)
+               bp->vnic_info[vnic_id].fw_vnic_id = le32_to_cpu(resp->vnic_id);
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_hwrm_ring_grp_alloc(struct bnxt *bp)
+{
+       u16 i;
+       u32 rc = 0;
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       for (i = 0; i < bp->rx_nr_rings; i++) {
+               struct hwrm_ring_grp_alloc_input req = {0};
+               struct hwrm_ring_grp_alloc_output *resp =
+                                       bp->hwrm_cmd_resp_addr;
+
+               bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_RING_GRP_ALLOC, -1, -1);
+
+               req.cr = cpu_to_le16(bp->grp_info[i].cp_fw_ring_id);
+               req.rr = cpu_to_le16(bp->grp_info[i].rx_fw_ring_id);
+               req.ar = cpu_to_le16(bp->grp_info[i].agg_fw_ring_id);
+               req.sc = cpu_to_le16(bp->grp_info[i].fw_stats_ctx);
+
+               rc = _hwrm_send_message(bp, &req, sizeof(req),
+                                       HWRM_CMD_TIMEOUT);
+               if (rc)
+                       break;
+
+               bp->grp_info[i].fw_grp_id = le32_to_cpu(resp->ring_group_id);
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_hwrm_ring_grp_free(struct bnxt *bp)
+{
+       u16 i;
+       u32 rc = 0;
+       struct hwrm_ring_grp_free_input req = {0};
+
+       if (!bp->grp_info)
+               return 0;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_RING_GRP_FREE, -1, -1);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               if (bp->grp_info[i].fw_grp_id == INVALID_HW_RING_ID)
+                       continue;
+               req.ring_group_id =
+                       cpu_to_le32(bp->grp_info[i].fw_grp_id);
+
+               rc = _hwrm_send_message(bp, &req, sizeof(req),
+                                       HWRM_CMD_TIMEOUT);
+               if (rc)
+                       break;
+               bp->grp_info[i].fw_grp_id = INVALID_HW_RING_ID;
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int hwrm_ring_alloc_send_msg(struct bnxt *bp,
+                                   struct bnxt_ring_struct *ring,
+                                   u32 ring_type, u32 map_index,
+                                   u32 stats_ctx_id)
+{
+       int rc = 0, err = 0;
+       struct hwrm_ring_alloc_input req = {0};
+       struct hwrm_ring_alloc_output *resp = bp->hwrm_cmd_resp_addr;
+       u16 ring_id;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_RING_ALLOC, -1, -1);
+
+       req.enables = 0;
+       if (ring->nr_pages > 1) {
+               req.page_tbl_addr = cpu_to_le64(ring->pg_tbl_map);
+               /* Page size is in log2 units */
+               req.page_size = BNXT_PAGE_SHIFT;
+               req.page_tbl_depth = 1;
+       } else {
+               req.page_tbl_addr =  cpu_to_le64(ring->dma_arr[0]);
+       }
+       req.fbo = 0;
+       /* Association of ring index with doorbell index and MSIX number */
+       req.logical_id = cpu_to_le16(map_index);
+
+       switch (ring_type) {
+       case HWRM_RING_ALLOC_TX:
+               req.ring_type = RING_ALLOC_REQ_RING_TYPE_TX;
+               /* Association of transmit ring with completion ring */
+               req.cmpl_ring_id =
+                       cpu_to_le16(bp->grp_info[map_index].cp_fw_ring_id);
+               req.length = cpu_to_le32(bp->tx_ring_mask + 1);
+               req.stat_ctx_id = cpu_to_le32(stats_ctx_id);
+               req.queue_id = cpu_to_le16(ring->queue_id);
+               break;
+       case HWRM_RING_ALLOC_RX:
+               req.ring_type = RING_ALLOC_REQ_RING_TYPE_RX;
+               req.length = cpu_to_le32(bp->rx_ring_mask + 1);
+               break;
+       case HWRM_RING_ALLOC_AGG:
+               req.ring_type = RING_ALLOC_REQ_RING_TYPE_RX;
+               req.length = cpu_to_le32(bp->rx_agg_ring_mask + 1);
+               break;
+       case HWRM_RING_ALLOC_CMPL:
+               req.ring_type = RING_ALLOC_REQ_RING_TYPE_CMPL;
+               req.length = cpu_to_le32(bp->cp_ring_mask + 1);
+               if (bp->flags & BNXT_FLAG_USING_MSIX)
+                       req.int_mode = RING_ALLOC_REQ_INT_MODE_MSIX;
+               break;
+       default:
+               netdev_err(bp->dev, "hwrm alloc invalid ring type %d\n",
+                          ring_type);
+               return -1;
+       }
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       err = le16_to_cpu(resp->error_code);
+       ring_id = le16_to_cpu(resp->ring_id);
+       mutex_unlock(&bp->hwrm_cmd_lock);
+
+       if (rc || err) {
+               switch (ring_type) {
+               case RING_FREE_REQ_RING_TYPE_CMPL:
+                       netdev_err(bp->dev, "hwrm_ring_alloc cp failed. rc:%x err:%x\n",
+                                  rc, err);
+                       return -1;
+
+               case RING_FREE_REQ_RING_TYPE_RX:
+                       netdev_err(bp->dev, "hwrm_ring_alloc rx failed. rc:%x err:%x\n",
+                                  rc, err);
+                       return -1;
+
+               case RING_FREE_REQ_RING_TYPE_TX:
+                       netdev_err(bp->dev, "hwrm_ring_alloc tx failed. rc:%x err:%x\n",
+                                  rc, err);
+                       return -1;
+
+               default:
+                       netdev_err(bp->dev, "Invalid ring\n");
+                       return -1;
+               }
+       }
+       ring->fw_ring_id = ring_id;
+       return rc;
+}
+
+static int bnxt_hwrm_ring_alloc(struct bnxt *bp)
+{
+       int i, rc = 0;
+
+       if (bp->cp_nr_rings) {
+               for (i = 0; i < bp->cp_nr_rings; i++) {
+                       struct bnxt_napi *bnapi = bp->bnapi[i];
+                       struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+                       struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
+
+                       rc = hwrm_ring_alloc_send_msg(bp, ring,
+                                                     HWRM_RING_ALLOC_CMPL, i,
+                                                     INVALID_STATS_CTX_ID);
+                       if (rc)
+                               goto err_out;
+                       cpr->cp_doorbell = bp->bar1 + i * 0x80;
+                       BNXT_CP_DB(cpr->cp_doorbell, cpr->cp_raw_cons);
+                       bp->grp_info[i].cp_fw_ring_id = ring->fw_ring_id;
+               }
+       }
+
+       if (bp->tx_nr_rings) {
+               for (i = 0; i < bp->tx_nr_rings; i++) {
+                       struct bnxt_napi *bnapi = bp->bnapi[i];
+                       struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
+                       struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
+                       u16 fw_stats_ctx = bp->grp_info[i].fw_stats_ctx;
+
+                       rc = hwrm_ring_alloc_send_msg(bp, ring,
+                                                     HWRM_RING_ALLOC_TX, i,
+                                                     fw_stats_ctx);
+                       if (rc)
+                               goto err_out;
+                       txr->tx_doorbell = bp->bar1 + i * 0x80;
+               }
+       }
+
+       if (bp->rx_nr_rings) {
+               for (i = 0; i < bp->rx_nr_rings; i++) {
+                       struct bnxt_napi *bnapi = bp->bnapi[i];
+                       struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+                       struct bnxt_ring_struct *ring = &rxr->rx_ring_struct;
+
+                       rc = hwrm_ring_alloc_send_msg(bp, ring,
+                                                     HWRM_RING_ALLOC_RX, i,
+                                                     INVALID_STATS_CTX_ID);
+                       if (rc)
+                               goto err_out;
+                       rxr->rx_doorbell = bp->bar1 + i * 0x80;
+                       writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell);
+                       bp->grp_info[i].rx_fw_ring_id = ring->fw_ring_id;
+               }
+       }
+
+       if (bp->flags & BNXT_FLAG_AGG_RINGS) {
+               for (i = 0; i < bp->rx_nr_rings; i++) {
+                       struct bnxt_napi *bnapi = bp->bnapi[i];
+                       struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+                       struct bnxt_ring_struct *ring =
+                                               &rxr->rx_agg_ring_struct;
+
+                       rc = hwrm_ring_alloc_send_msg(bp, ring,
+                                                     HWRM_RING_ALLOC_AGG,
+                                                     bp->rx_nr_rings + i,
+                                                     INVALID_STATS_CTX_ID);
+                       if (rc)
+                               goto err_out;
+
+                       rxr->rx_agg_doorbell =
+                               bp->bar1 + (bp->rx_nr_rings + i) * 0x80;
+                       writel(DB_KEY_RX | rxr->rx_agg_prod,
+                              rxr->rx_agg_doorbell);
+                       bp->grp_info[i].agg_fw_ring_id = ring->fw_ring_id;
+               }
+       }
+err_out:
+       return rc;
+}
+
+static int hwrm_ring_free_send_msg(struct bnxt *bp,
+                                  struct bnxt_ring_struct *ring,
+                                  u32 ring_type, int cmpl_ring_id)
+{
+       int rc;
+       struct hwrm_ring_free_input req = {0};
+       struct hwrm_ring_free_output *resp = bp->hwrm_cmd_resp_addr;
+       u16 error_code;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_RING_FREE, -1, -1);
+       req.ring_type = ring_type;
+       req.ring_id = cpu_to_le16(ring->fw_ring_id);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       error_code = le16_to_cpu(resp->error_code);
+       mutex_unlock(&bp->hwrm_cmd_lock);
+
+       if (rc || error_code) {
+               switch (ring_type) {
+               case RING_FREE_REQ_RING_TYPE_CMPL:
+                       netdev_err(bp->dev, "hwrm_ring_free cp failed. rc:%d\n",
+                                  rc);
+                       return rc;
+               case RING_FREE_REQ_RING_TYPE_RX:
+                       netdev_err(bp->dev, "hwrm_ring_free rx failed. rc:%d\n",
+                                  rc);
+                       return rc;
+               case RING_FREE_REQ_RING_TYPE_TX:
+                       netdev_err(bp->dev, "hwrm_ring_free tx failed. rc:%d\n",
+                                  rc);
+                       return rc;
+               default:
+                       netdev_err(bp->dev, "Invalid ring\n");
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static int bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path)
+{
+       int i, rc = 0;
+
+       if (!bp->bnapi)
+               return 0;
+
+       if (bp->tx_nr_rings) {
+               for (i = 0; i < bp->tx_nr_rings; i++) {
+                       struct bnxt_napi *bnapi = bp->bnapi[i];
+                       struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
+                       struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
+                       u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id;
+
+                       if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+                               hwrm_ring_free_send_msg(
+                                       bp, ring,
+                                       RING_FREE_REQ_RING_TYPE_TX,
+                                       close_path ? cmpl_ring_id :
+                                       INVALID_HW_RING_ID);
+                               ring->fw_ring_id = INVALID_HW_RING_ID;
+                       }
+               }
+       }
+
+       if (bp->rx_nr_rings) {
+               for (i = 0; i < bp->rx_nr_rings; i++) {
+                       struct bnxt_napi *bnapi = bp->bnapi[i];
+                       struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+                       struct bnxt_ring_struct *ring = &rxr->rx_ring_struct;
+                       u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id;
+
+                       if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+                               hwrm_ring_free_send_msg(
+                                       bp, ring,
+                                       RING_FREE_REQ_RING_TYPE_RX,
+                                       close_path ? cmpl_ring_id :
+                                       INVALID_HW_RING_ID);
+                               ring->fw_ring_id = INVALID_HW_RING_ID;
+                               bp->grp_info[i].rx_fw_ring_id =
+                                       INVALID_HW_RING_ID;
+                       }
+               }
+       }
+
+       if (bp->rx_agg_nr_pages) {
+               for (i = 0; i < bp->rx_nr_rings; i++) {
+                       struct bnxt_napi *bnapi = bp->bnapi[i];
+                       struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+                       struct bnxt_ring_struct *ring =
+                                               &rxr->rx_agg_ring_struct;
+                       u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id;
+
+                       if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+                               hwrm_ring_free_send_msg(
+                                       bp, ring,
+                                       RING_FREE_REQ_RING_TYPE_RX,
+                                       close_path ? cmpl_ring_id :
+                                       INVALID_HW_RING_ID);
+                               ring->fw_ring_id = INVALID_HW_RING_ID;
+                               bp->grp_info[i].agg_fw_ring_id =
+                                       INVALID_HW_RING_ID;
+                       }
+               }
+       }
+
+       if (bp->cp_nr_rings) {
+               for (i = 0; i < bp->cp_nr_rings; i++) {
+                       struct bnxt_napi *bnapi = bp->bnapi[i];
+                       struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+                       struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
+
+                       if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+                               hwrm_ring_free_send_msg(
+                                       bp, ring,
+                                       RING_FREE_REQ_RING_TYPE_CMPL,
+                                       INVALID_HW_RING_ID);
+                               ring->fw_ring_id = INVALID_HW_RING_ID;
+                               bp->grp_info[i].cp_fw_ring_id =
+                                                       INVALID_HW_RING_ID;
+                       }
+               }
+       }
+
+       return rc;
+}
+
+int bnxt_hwrm_set_coal(struct bnxt *bp)
+{
+       int i, rc = 0;
+       struct hwrm_ring_cmpl_ring_cfg_aggint_params_input req = {0};
+       u16 max_buf, max_buf_irq;
+       u16 buf_tmr, buf_tmr_irq;
+       u32 flags;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS,
+                              -1, -1);
+
+       /* Each rx completion (2 records) should be DMAed immediately */
+       max_buf = min_t(u16, bp->coal_bufs / 4, 2);
+       /* max_buf must not be zero */
+       max_buf = clamp_t(u16, max_buf, 1, 63);
+       max_buf_irq = clamp_t(u16, bp->coal_bufs_irq, 1, 63);
+       buf_tmr = max_t(u16, bp->coal_ticks / 4, 1);
+       buf_tmr_irq = max_t(u16, bp->coal_ticks_irq, 1);
+
+       flags = RING_CMPL_RING_CFG_AGGINT_PARAMS_REQ_FLAGS_TIMER_RESET;
+
+       /* RING_IDLE generates more IRQs for lower latency.  Enable it only
+        * if coal_ticks is less than 25 us.
+        */
+       if (BNXT_COAL_TIMER_TO_USEC(bp->coal_ticks) < 25)
+               flags |= RING_CMPL_RING_CFG_AGGINT_PARAMS_REQ_FLAGS_RING_IDLE;
+
+       req.flags = cpu_to_le16(flags);
+       req.num_cmpl_dma_aggr = cpu_to_le16(max_buf);
+       req.num_cmpl_dma_aggr_during_int = cpu_to_le16(max_buf_irq);
+       req.cmpl_aggr_dma_tmr = cpu_to_le16(buf_tmr);
+       req.cmpl_aggr_dma_tmr_during_int = cpu_to_le16(buf_tmr_irq);
+       req.int_lat_tmr_min = cpu_to_le16(buf_tmr);
+       req.int_lat_tmr_max = cpu_to_le16(bp->coal_ticks);
+       req.num_cmpl_aggr_int = cpu_to_le16(bp->coal_bufs);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               req.ring_id = cpu_to_le16(bp->grp_info[i].cp_fw_ring_id);
+
+               rc = _hwrm_send_message(bp, &req, sizeof(req),
+                                       HWRM_CMD_TIMEOUT);
+               if (rc)
+                       break;
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_hwrm_stat_ctx_free(struct bnxt *bp)
+{
+       int rc = 0, i;
+       struct hwrm_stat_ctx_free_input req = {0};
+
+       if (!bp->bnapi)
+               return 0;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_STAT_CTX_FREE, -1, -1);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+
+               if (cpr->hw_stats_ctx_id != INVALID_STATS_CTX_ID) {
+                       req.stat_ctx_id = cpu_to_le32(cpr->hw_stats_ctx_id);
+
+                       rc = _hwrm_send_message(bp, &req, sizeof(req),
+                                               HWRM_CMD_TIMEOUT);
+                       if (rc)
+                               break;
+
+                       cpr->hw_stats_ctx_id = INVALID_STATS_CTX_ID;
+               }
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_hwrm_stat_ctx_alloc(struct bnxt *bp)
+{
+       int rc = 0, i;
+       struct hwrm_stat_ctx_alloc_input req = {0};
+       struct hwrm_stat_ctx_alloc_output *resp = bp->hwrm_cmd_resp_addr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_STAT_CTX_ALLOC, -1, -1);
+
+       req.update_period_ms = cpu_to_le32(1000);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+
+               req.stats_dma_addr = cpu_to_le64(cpr->hw_stats_map);
+
+               rc = _hwrm_send_message(bp, &req, sizeof(req),
+                                       HWRM_CMD_TIMEOUT);
+               if (rc)
+                       break;
+
+               cpr->hw_stats_ctx_id = le32_to_cpu(resp->stat_ctx_id);
+
+               bp->grp_info[i].fw_stats_ctx = cpr->hw_stats_ctx_id;
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return 0;
+}
+
+static int bnxt_hwrm_func_qcaps(struct bnxt *bp)
+{
+       int rc = 0;
+       struct hwrm_func_qcaps_input req = {0};
+       struct hwrm_func_qcaps_output *resp = bp->hwrm_cmd_resp_addr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_QCAPS, -1, -1);
+       req.fid = cpu_to_le16(0xffff);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (rc)
+               goto hwrm_func_qcaps_exit;
+
+       if (BNXT_PF(bp)) {
+               struct bnxt_pf_info *pf = &bp->pf;
+
+               pf->fw_fid = le16_to_cpu(resp->fid);
+               pf->port_id = le16_to_cpu(resp->port_id);
+               memcpy(pf->mac_addr, resp->perm_mac_address, ETH_ALEN);
+               pf->max_rsscos_ctxs = le16_to_cpu(resp->max_rsscos_ctx);
+               pf->max_cp_rings = le16_to_cpu(resp->max_cmpl_rings);
+               pf->max_tx_rings = le16_to_cpu(resp->max_tx_rings);
+               pf->max_pf_tx_rings = pf->max_tx_rings;
+               pf->max_rx_rings = le16_to_cpu(resp->max_rx_rings);
+               pf->max_pf_rx_rings = pf->max_rx_rings;
+               pf->max_l2_ctxs = le16_to_cpu(resp->max_l2_ctxs);
+               pf->max_vnics = le16_to_cpu(resp->max_vnics);
+               pf->max_stat_ctxs = le16_to_cpu(resp->max_stat_ctx);
+               pf->first_vf_id = le16_to_cpu(resp->first_vf_id);
+               pf->max_vfs = le16_to_cpu(resp->max_vfs);
+               pf->max_encap_records = le32_to_cpu(resp->max_encap_records);
+               pf->max_decap_records = le32_to_cpu(resp->max_decap_records);
+               pf->max_tx_em_flows = le32_to_cpu(resp->max_tx_em_flows);
+               pf->max_tx_wm_flows = le32_to_cpu(resp->max_tx_wm_flows);
+               pf->max_rx_em_flows = le32_to_cpu(resp->max_rx_em_flows);
+               pf->max_rx_wm_flows = le32_to_cpu(resp->max_rx_wm_flows);
+       } else {
+#ifdef CONFIG_BNXT_SRIOV
+               struct bnxt_vf_info *vf = &bp->vf;
+
+               vf->fw_fid = le16_to_cpu(resp->fid);
+               memcpy(vf->mac_addr, resp->perm_mac_address, ETH_ALEN);
+               if (!is_valid_ether_addr(vf->mac_addr))
+                       random_ether_addr(vf->mac_addr);
+
+               vf->max_rsscos_ctxs = le16_to_cpu(resp->max_rsscos_ctx);
+               vf->max_cp_rings = le16_to_cpu(resp->max_cmpl_rings);
+               vf->max_tx_rings = le16_to_cpu(resp->max_tx_rings);
+               vf->max_rx_rings = le16_to_cpu(resp->max_rx_rings);
+               vf->max_l2_ctxs = le16_to_cpu(resp->max_l2_ctxs);
+               vf->max_vnics = le16_to_cpu(resp->max_vnics);
+               vf->max_stat_ctxs = le16_to_cpu(resp->max_stat_ctx);
+#endif
+       }
+
+       bp->tx_push_thresh = 0;
+       if (resp->flags &
+           cpu_to_le32(FUNC_QCAPS_RESP_FLAGS_PUSH_MODE_SUPPORTED))
+               bp->tx_push_thresh = BNXT_TX_PUSH_THRESH;
+
+hwrm_func_qcaps_exit:
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_hwrm_func_reset(struct bnxt *bp)
+{
+       struct hwrm_func_reset_input req = {0};
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_RESET, -1, -1);
+       req.enables = 0;
+
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_RESET_TIMEOUT);
+}
+
+static int bnxt_hwrm_queue_qportcfg(struct bnxt *bp)
+{
+       int rc = 0;
+       struct hwrm_queue_qportcfg_input req = {0};
+       struct hwrm_queue_qportcfg_output *resp = bp->hwrm_cmd_resp_addr;
+       u8 i, *qptr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_QUEUE_QPORTCFG, -1, -1);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (rc)
+               goto qportcfg_exit;
+
+       if (!resp->max_configurable_queues) {
+               rc = -EINVAL;
+               goto qportcfg_exit;
+       }
+       bp->max_tc = resp->max_configurable_queues;
+       if (bp->max_tc > BNXT_MAX_QUEUE)
+               bp->max_tc = BNXT_MAX_QUEUE;
+
+       qptr = &resp->queue_id0;
+       for (i = 0; i < bp->max_tc; i++) {
+               bp->q_info[i].queue_id = *qptr++;
+               bp->q_info[i].queue_profile = *qptr++;
+       }
+
+qportcfg_exit:
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_hwrm_ver_get(struct bnxt *bp)
+{
+       int rc;
+       struct hwrm_ver_get_input req = {0};
+       struct hwrm_ver_get_output *resp = bp->hwrm_cmd_resp_addr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VER_GET, -1, -1);
+       req.hwrm_intf_maj = HWRM_VERSION_MAJOR;
+       req.hwrm_intf_min = HWRM_VERSION_MINOR;
+       req.hwrm_intf_upd = HWRM_VERSION_UPDATE;
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (rc)
+               goto hwrm_ver_get_exit;
+
+       memcpy(&bp->ver_resp, resp, sizeof(struct hwrm_ver_get_output));
+
+       if (req.hwrm_intf_maj != resp->hwrm_intf_maj ||
+           req.hwrm_intf_min != resp->hwrm_intf_min ||
+           req.hwrm_intf_upd != resp->hwrm_intf_upd) {
+               netdev_warn(bp->dev, "HWRM interface %d.%d.%d does not match driver interface %d.%d.%d.\n",
+                           resp->hwrm_intf_maj, resp->hwrm_intf_min,
+                           resp->hwrm_intf_upd, req.hwrm_intf_maj,
+                           req.hwrm_intf_min, req.hwrm_intf_upd);
+               netdev_warn(bp->dev, "Please update driver or firmware with matching interface versions.\n");
+       }
+       snprintf(bp->fw_ver_str, BC_HWRM_STR_LEN, "bc %d.%d.%d rm %d.%d.%d",
+                resp->hwrm_fw_maj, resp->hwrm_fw_min, resp->hwrm_fw_bld,
+                resp->hwrm_intf_maj, resp->hwrm_intf_min, resp->hwrm_intf_upd);
+
+hwrm_ver_get_exit:
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static void bnxt_hwrm_free_tunnel_ports(struct bnxt *bp)
+{
+       if (bp->vxlan_port_cnt) {
+               bnxt_hwrm_tunnel_dst_port_free(
+                       bp, TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN);
+       }
+       bp->vxlan_port_cnt = 0;
+       if (bp->nge_port_cnt) {
+               bnxt_hwrm_tunnel_dst_port_free(
+                       bp, TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE);
+       }
+       bp->nge_port_cnt = 0;
+}
+
+static int bnxt_set_tpa(struct bnxt *bp, bool set_tpa)
+{
+       int rc, i;
+       u32 tpa_flags = 0;
+
+       if (set_tpa)
+               tpa_flags = bp->flags & BNXT_FLAG_TPA;
+       for (i = 0; i < bp->nr_vnics; i++) {
+               rc = bnxt_hwrm_vnic_set_tpa(bp, i, tpa_flags);
+               if (rc) {
+                       netdev_err(bp->dev, "hwrm vnic set tpa failure rc for vnic %d: %x\n",
+                                  rc, i);
+                       return rc;
+               }
+       }
+       return 0;
+}
+
+static void bnxt_hwrm_clear_vnic_rss(struct bnxt *bp)
+{
+       int i;
+
+       for (i = 0; i < bp->nr_vnics; i++)
+               bnxt_hwrm_vnic_set_rss(bp, i, false);
+}
+
+static void bnxt_hwrm_resource_free(struct bnxt *bp, bool close_path,
+                                   bool irq_re_init)
+{
+       if (bp->vnic_info) {
+               bnxt_hwrm_clear_vnic_filter(bp);
+               /* clear all RSS setting before free vnic ctx */
+               bnxt_hwrm_clear_vnic_rss(bp);
+               bnxt_hwrm_vnic_ctx_free(bp);
+               /* before free the vnic, undo the vnic tpa settings */
+               if (bp->flags & BNXT_FLAG_TPA)
+                       bnxt_set_tpa(bp, false);
+               bnxt_hwrm_vnic_free(bp);
+       }
+       bnxt_hwrm_ring_free(bp, close_path);
+       bnxt_hwrm_ring_grp_free(bp);
+       if (irq_re_init) {
+               bnxt_hwrm_stat_ctx_free(bp);
+               bnxt_hwrm_free_tunnel_ports(bp);
+       }
+}
+
+static int bnxt_setup_vnic(struct bnxt *bp, u16 vnic_id)
+{
+       int rc;
+
+       /* allocate context for vnic */
+       rc = bnxt_hwrm_vnic_ctx_alloc(bp, vnic_id);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm vnic %d alloc failure rc: %x\n",
+                          vnic_id, rc);
+               goto vnic_setup_err;
+       }
+       bp->rsscos_nr_ctxs++;
+
+       /* configure default vnic, ring grp */
+       rc = bnxt_hwrm_vnic_cfg(bp, vnic_id);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm vnic %d cfg failure rc: %x\n",
+                          vnic_id, rc);
+               goto vnic_setup_err;
+       }
+
+       /* Enable RSS hashing on vnic */
+       rc = bnxt_hwrm_vnic_set_rss(bp, vnic_id, true);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm vnic %d set rss failure rc: %x\n",
+                          vnic_id, rc);
+               goto vnic_setup_err;
+       }
+
+       if (bp->flags & BNXT_FLAG_AGG_RINGS) {
+               rc = bnxt_hwrm_vnic_set_hds(bp, vnic_id);
+               if (rc) {
+                       netdev_err(bp->dev, "hwrm vnic %d set hds failure rc: %x\n",
+                                  vnic_id, rc);
+               }
+       }
+
+vnic_setup_err:
+       return rc;
+}
+
+static int bnxt_alloc_rfs_vnics(struct bnxt *bp)
+{
+#ifdef CONFIG_RFS_ACCEL
+       int i, rc = 0;
+
+       for (i = 0; i < bp->rx_nr_rings; i++) {
+               u16 vnic_id = i + 1;
+               u16 ring_id = i;
+
+               if (vnic_id >= bp->nr_vnics)
+                       break;
+
+               bp->vnic_info[vnic_id].flags |= BNXT_VNIC_RFS_FLAG;
+               rc = bnxt_hwrm_vnic_alloc(bp, vnic_id, ring_id, ring_id + 1);
+               if (rc) {
+                       netdev_err(bp->dev, "hwrm vnic %d alloc failure rc: %x\n",
+                                  vnic_id, rc);
+                       break;
+               }
+               rc = bnxt_setup_vnic(bp, vnic_id);
+               if (rc)
+                       break;
+       }
+       return rc;
+#else
+       return 0;
+#endif
+}
+
+static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init)
+{
+       int rc = 0;
+
+       if (irq_re_init) {
+               rc = bnxt_hwrm_stat_ctx_alloc(bp);
+               if (rc) {
+                       netdev_err(bp->dev, "hwrm stat ctx alloc failure rc: %x\n",
+                                  rc);
+                       goto err_out;
+               }
+       }
+
+       rc = bnxt_hwrm_ring_alloc(bp);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm ring alloc failure rc: %x\n", rc);
+               goto err_out;
+       }
+
+       rc = bnxt_hwrm_ring_grp_alloc(bp);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm_ring_grp alloc failure: %x\n", rc);
+               goto err_out;
+       }
+
+       /* default vnic 0 */
+       rc = bnxt_hwrm_vnic_alloc(bp, 0, 0, bp->rx_nr_rings);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm vnic alloc failure rc: %x\n", rc);
+               goto err_out;
+       }
+
+       rc = bnxt_setup_vnic(bp, 0);
+       if (rc)
+               goto err_out;
+
+       if (bp->flags & BNXT_FLAG_RFS) {
+               rc = bnxt_alloc_rfs_vnics(bp);
+               if (rc)
+                       goto err_out;
+       }
+
+       if (bp->flags & BNXT_FLAG_TPA) {
+               rc = bnxt_set_tpa(bp, true);
+               if (rc)
+                       goto err_out;
+       }
+
+       if (BNXT_VF(bp))
+               bnxt_update_vf_mac(bp);
+
+       /* Filter for default vnic 0 */
+       rc = bnxt_hwrm_set_vnic_filter(bp, 0, 0, bp->dev->dev_addr);
+       if (rc) {
+               netdev_err(bp->dev, "HWRM vnic filter failure rc: %x\n", rc);
+               goto err_out;
+       }
+       bp->vnic_info[0].uc_filter_count = 1;
+
+       bp->vnic_info[0].rx_mask = CFA_L2_SET_RX_MASK_REQ_MASK_UNICAST |
+                                  CFA_L2_SET_RX_MASK_REQ_MASK_BCAST;
+
+       if ((bp->dev->flags & IFF_PROMISC) && BNXT_PF(bp))
+               bp->vnic_info[0].rx_mask |=
+                               CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS;
+
+       rc = bnxt_hwrm_cfa_l2_set_rx_mask(bp, 0);
+       if (rc) {
+               netdev_err(bp->dev, "HWRM cfa l2 rx mask failure rc: %x\n", rc);
+               goto err_out;
+       }
+
+       rc = bnxt_hwrm_set_coal(bp);
+       if (rc)
+               netdev_warn(bp->dev, "HWRM set coalescing failure rc: %x\n",
+                           rc);
+
+       return 0;
+
+err_out:
+       bnxt_hwrm_resource_free(bp, 0, true);
+
+       return rc;
+}
+
+static int bnxt_shutdown_nic(struct bnxt *bp, bool irq_re_init)
+{
+       bnxt_hwrm_resource_free(bp, 1, irq_re_init);
+       return 0;
+}
+
+static int bnxt_init_nic(struct bnxt *bp, bool irq_re_init)
+{
+       bnxt_init_rx_rings(bp);
+       bnxt_init_tx_rings(bp);
+       bnxt_init_ring_grps(bp, irq_re_init);
+       bnxt_init_vnics(bp);
+
+       return bnxt_init_chip(bp, irq_re_init);
+}
+
+static void bnxt_disable_int(struct bnxt *bp)
+{
+       int i;
+
+       if (!bp->bnapi)
+               return;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+
+               BNXT_CP_DB(cpr->cp_doorbell, cpr->cp_raw_cons);
+       }
+}
+
+static void bnxt_enable_int(struct bnxt *bp)
+{
+       int i;
+
+       atomic_set(&bp->intr_sem, 0);
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+
+               BNXT_CP_DB_REARM(cpr->cp_doorbell, cpr->cp_raw_cons);
+       }
+}
+
+static int bnxt_set_real_num_queues(struct bnxt *bp)
+{
+       int rc;
+       struct net_device *dev = bp->dev;
+
+       rc = netif_set_real_num_tx_queues(dev, bp->tx_nr_rings);
+       if (rc)
+               return rc;
+
+       rc = netif_set_real_num_rx_queues(dev, bp->rx_nr_rings);
+       if (rc)
+               return rc;
+
+#ifdef CONFIG_RFS_ACCEL
+       if (bp->rx_nr_rings)
+               dev->rx_cpu_rmap = alloc_irq_cpu_rmap(bp->rx_nr_rings);
+       if (!dev->rx_cpu_rmap)
+               rc = -ENOMEM;
+#endif
+
+       return rc;
+}
+
+static int bnxt_setup_msix(struct bnxt *bp)
+{
+       struct msix_entry *msix_ent;
+       struct net_device *dev = bp->dev;
+       int i, total_vecs, rc = 0;
+       const int len = sizeof(bp->irq_tbl[0].name);
+
+       bp->flags &= ~BNXT_FLAG_USING_MSIX;
+       total_vecs = bp->cp_nr_rings;
+
+       msix_ent = kcalloc(total_vecs, sizeof(struct msix_entry), GFP_KERNEL);
+       if (!msix_ent)
+               return -ENOMEM;
+
+       for (i = 0; i < total_vecs; i++) {
+               msix_ent[i].entry = i;
+               msix_ent[i].vector = 0;
+       }
+
+       total_vecs = pci_enable_msix_range(bp->pdev, msix_ent, 1, total_vecs);
+       if (total_vecs < 0) {
+               rc = -ENODEV;
+               goto msix_setup_exit;
+       }
+
+       bp->irq_tbl = kcalloc(total_vecs, sizeof(struct bnxt_irq), GFP_KERNEL);
+       if (bp->irq_tbl) {
+               int tcs;
+
+               /* Trim rings based upon num of vectors allocated */
+               bp->rx_nr_rings = min_t(int, total_vecs, bp->rx_nr_rings);
+               bp->tx_nr_rings = min_t(int, total_vecs, bp->tx_nr_rings);
+               bp->tx_nr_rings_per_tc = bp->tx_nr_rings;
+               tcs = netdev_get_num_tc(dev);
+               if (tcs > 1) {
+                       bp->tx_nr_rings_per_tc = bp->tx_nr_rings / tcs;
+                       if (bp->tx_nr_rings_per_tc == 0) {
+                               netdev_reset_tc(dev);
+                               bp->tx_nr_rings_per_tc = bp->tx_nr_rings;
+                       } else {
+                               int i, off, count;
+
+                               bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tcs;
+                               for (i = 0; i < tcs; i++) {
+                                       count = bp->tx_nr_rings_per_tc;
+                                       off = i * count;
+                                       netdev_set_tc_queue(dev, i, count, off);
+                               }
+                       }
+               }
+               bp->cp_nr_rings = max_t(int, bp->rx_nr_rings, bp->tx_nr_rings);
+
+               for (i = 0; i < bp->cp_nr_rings; i++) {
+                       bp->irq_tbl[i].vector = msix_ent[i].vector;
+                       snprintf(bp->irq_tbl[i].name, len,
+                                "%s-%s-%d", dev->name, "TxRx", i);
+                       bp->irq_tbl[i].handler = bnxt_msix;
+               }
+               rc = bnxt_set_real_num_queues(bp);
+               if (rc)
+                       goto msix_setup_exit;
+       } else {
+               rc = -ENOMEM;
+               goto msix_setup_exit;
+       }
+       bp->flags |= BNXT_FLAG_USING_MSIX;
+       kfree(msix_ent);
+       return 0;
+
+msix_setup_exit:
+       netdev_err(bp->dev, "bnxt_setup_msix err: %x\n", rc);
+       pci_disable_msix(bp->pdev);
+       kfree(msix_ent);
+       return rc;
+}
+
+static int bnxt_setup_inta(struct bnxt *bp)
+{
+       int rc;
+       const int len = sizeof(bp->irq_tbl[0].name);
+
+       if (netdev_get_num_tc(bp->dev))
+               netdev_reset_tc(bp->dev);
+
+       bp->irq_tbl = kcalloc(1, sizeof(struct bnxt_irq), GFP_KERNEL);
+       if (!bp->irq_tbl) {
+               rc = -ENOMEM;
+               return rc;
+       }
+       bp->rx_nr_rings = 1;
+       bp->tx_nr_rings = 1;
+       bp->cp_nr_rings = 1;
+       bp->tx_nr_rings_per_tc = bp->tx_nr_rings;
+       bp->irq_tbl[0].vector = bp->pdev->irq;
+       snprintf(bp->irq_tbl[0].name, len,
+                "%s-%s-%d", bp->dev->name, "TxRx", 0);
+       bp->irq_tbl[0].handler = bnxt_inta;
+       rc = bnxt_set_real_num_queues(bp);
+       return rc;
+}
+
+static int bnxt_setup_int_mode(struct bnxt *bp)
+{
+       int rc = 0;
+
+       if (bp->flags & BNXT_FLAG_MSIX_CAP)
+               rc = bnxt_setup_msix(bp);
+
+       if (!(bp->flags & BNXT_FLAG_USING_MSIX)) {
+               /* fallback to INTA */
+               rc = bnxt_setup_inta(bp);
+       }
+       return rc;
+}
+
+static void bnxt_free_irq(struct bnxt *bp)
+{
+       struct bnxt_irq *irq;
+       int i;
+
+#ifdef CONFIG_RFS_ACCEL
+       free_irq_cpu_rmap(bp->dev->rx_cpu_rmap);
+       bp->dev->rx_cpu_rmap = NULL;
+#endif
+       if (!bp->irq_tbl)
+               return;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               irq = &bp->irq_tbl[i];
+               if (irq->requested)
+                       free_irq(irq->vector, bp->bnapi[i]);
+               irq->requested = 0;
+       }
+       if (bp->flags & BNXT_FLAG_USING_MSIX)
+               pci_disable_msix(bp->pdev);
+       kfree(bp->irq_tbl);
+       bp->irq_tbl = NULL;
+}
+
+static int bnxt_request_irq(struct bnxt *bp)
+{
+       int i, rc = 0;
+       unsigned long flags = 0;
+#ifdef CONFIG_RFS_ACCEL
+       struct cpu_rmap *rmap = bp->dev->rx_cpu_rmap;
+#endif
+
+       if (!(bp->flags & BNXT_FLAG_USING_MSIX))
+               flags = IRQF_SHARED;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_irq *irq = &bp->irq_tbl[i];
+#ifdef CONFIG_RFS_ACCEL
+               if (rmap && (i < bp->rx_nr_rings)) {
+                       rc = irq_cpu_rmap_add(rmap, irq->vector);
+                       if (rc)
+                               netdev_warn(bp->dev, "failed adding irq rmap for ring %d\n",
+                                           i);
+               }
+#endif
+               rc = request_irq(irq->vector, irq->handler, flags, irq->name,
+                                bp->bnapi[i]);
+               if (rc)
+                       break;
+
+               irq->requested = 1;
+       }
+       return rc;
+}
+
+static void bnxt_del_napi(struct bnxt *bp)
+{
+       int i;
+
+       if (!bp->bnapi)
+               return;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+
+               napi_hash_del(&bnapi->napi);
+               netif_napi_del(&bnapi->napi);
+       }
+}
+
+static void bnxt_init_napi(struct bnxt *bp)
+{
+       int i;
+       struct bnxt_napi *bnapi;
+
+       if (bp->flags & BNXT_FLAG_USING_MSIX) {
+               for (i = 0; i < bp->cp_nr_rings; i++) {
+                       bnapi = bp->bnapi[i];
+                       netif_napi_add(bp->dev, &bnapi->napi,
+                                      bnxt_poll, 64);
+                       napi_hash_add(&bnapi->napi);
+               }
+       } else {
+               bnapi = bp->bnapi[0];
+               netif_napi_add(bp->dev, &bnapi->napi, bnxt_poll, 64);
+               napi_hash_add(&bnapi->napi);
+       }
+}
+
+static void bnxt_disable_napi(struct bnxt *bp)
+{
+       int i;
+
+       if (!bp->bnapi)
+               return;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               napi_disable(&bp->bnapi[i]->napi);
+               bnxt_disable_poll(bp->bnapi[i]);
+       }
+}
+
+static void bnxt_enable_napi(struct bnxt *bp)
+{
+       int i;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               bnxt_enable_poll(bp->bnapi[i]);
+               napi_enable(&bp->bnapi[i]->napi);
+       }
+}
+
+static void bnxt_tx_disable(struct bnxt *bp)
+{
+       int i;
+       struct bnxt_napi *bnapi;
+       struct bnxt_tx_ring_info *txr;
+       struct netdev_queue *txq;
+
+       if (bp->bnapi) {
+               for (i = 0; i < bp->tx_nr_rings; i++) {
+                       bnapi = bp->bnapi[i];
+                       txr = &bnapi->tx_ring;
+                       txq = netdev_get_tx_queue(bp->dev, i);
+                       __netif_tx_lock(txq, smp_processor_id());
+                       txr->dev_state = BNXT_DEV_STATE_CLOSING;
+                       __netif_tx_unlock(txq);
+               }
+       }
+       /* Stop all TX queues */
+       netif_tx_disable(bp->dev);
+       netif_carrier_off(bp->dev);
+}
+
+static void bnxt_tx_enable(struct bnxt *bp)
+{
+       int i;
+       struct bnxt_napi *bnapi;
+       struct bnxt_tx_ring_info *txr;
+       struct netdev_queue *txq;
+
+       for (i = 0; i < bp->tx_nr_rings; i++) {
+               bnapi = bp->bnapi[i];
+               txr = &bnapi->tx_ring;
+               txq = netdev_get_tx_queue(bp->dev, i);
+               txr->dev_state = 0;
+       }
+       netif_tx_wake_all_queues(bp->dev);
+       if (bp->link_info.link_up)
+               netif_carrier_on(bp->dev);
+}
+
+static void bnxt_report_link(struct bnxt *bp)
+{
+       if (bp->link_info.link_up) {
+               const char *duplex;
+               const char *flow_ctrl;
+               u16 speed;
+
+               netif_carrier_on(bp->dev);
+               if (bp->link_info.duplex == BNXT_LINK_DUPLEX_FULL)
+                       duplex = "full";
+               else
+                       duplex = "half";
+               if (bp->link_info.pause == BNXT_LINK_PAUSE_BOTH)
+                       flow_ctrl = "ON - receive & transmit";
+               else if (bp->link_info.pause == BNXT_LINK_PAUSE_TX)
+                       flow_ctrl = "ON - transmit";
+               else if (bp->link_info.pause == BNXT_LINK_PAUSE_RX)
+                       flow_ctrl = "ON - receive";
+               else
+                       flow_ctrl = "none";
+               speed = bnxt_fw_to_ethtool_speed(bp->link_info.link_speed);
+               netdev_info(bp->dev, "NIC Link is Up, %d Mbps %s duplex, Flow control: %s\n",
+                           speed, duplex, flow_ctrl);
+       } else {
+               netif_carrier_off(bp->dev);
+               netdev_err(bp->dev, "NIC Link is Down\n");
+       }
+}
+
+static int bnxt_update_link(struct bnxt *bp, bool chng_link_state)
+{
+       int rc = 0;
+       struct bnxt_link_info *link_info = &bp->link_info;
+       struct hwrm_port_phy_qcfg_input req = {0};
+       struct hwrm_port_phy_qcfg_output *resp = bp->hwrm_cmd_resp_addr;
+       u8 link_up = link_info->link_up;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_QCFG, -1, -1);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (rc) {
+               mutex_unlock(&bp->hwrm_cmd_lock);
+               return rc;
+       }
+
+       memcpy(&link_info->phy_qcfg_resp, resp, sizeof(*resp));
+       link_info->phy_link_status = resp->link;
+       link_info->duplex =  resp->duplex;
+       link_info->pause = resp->pause;
+       link_info->auto_mode = resp->auto_mode;
+       link_info->auto_pause_setting = resp->auto_pause;
+       link_info->force_pause_setting = resp->force_pause;
+       link_info->duplex_setting = resp->duplex_setting;
+       if (link_info->phy_link_status == BNXT_LINK_LINK)
+               link_info->link_speed = le16_to_cpu(resp->link_speed);
+       else
+               link_info->link_speed = 0;
+       link_info->force_link_speed = le16_to_cpu(resp->force_link_speed);
+       link_info->auto_link_speed = le16_to_cpu(resp->auto_link_speed);
+       link_info->support_speeds = le16_to_cpu(resp->support_speeds);
+       link_info->auto_link_speeds = le16_to_cpu(resp->auto_link_speed_mask);
+       link_info->preemphasis = le32_to_cpu(resp->preemphasis);
+       link_info->phy_ver[0] = resp->phy_maj;
+       link_info->phy_ver[1] = resp->phy_min;
+       link_info->phy_ver[2] = resp->phy_bld;
+       link_info->media_type = resp->media_type;
+       link_info->transceiver = resp->transceiver_type;
+       link_info->phy_addr = resp->phy_addr;
+
+       /* TODO: need to add more logic to report VF link */
+       if (chng_link_state) {
+               if (link_info->phy_link_status == BNXT_LINK_LINK)
+                       link_info->link_up = 1;
+               else
+                       link_info->link_up = 0;
+               if (link_up != link_info->link_up)
+                       bnxt_report_link(bp);
+       } else {
+               /* alwasy link down if not require to update link state */
+               link_info->link_up = 0;
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return 0;
+}
+
+static void
+bnxt_hwrm_set_pause_common(struct bnxt *bp, struct hwrm_port_phy_cfg_input *req)
+{
+       if (bp->link_info.autoneg & BNXT_AUTONEG_FLOW_CTRL) {
+               if (bp->link_info.req_flow_ctrl & BNXT_LINK_PAUSE_RX)
+                       req->auto_pause |= PORT_PHY_CFG_REQ_AUTO_PAUSE_RX;
+               if (bp->link_info.req_flow_ctrl & BNXT_LINK_PAUSE_TX)
+                       req->auto_pause |= PORT_PHY_CFG_REQ_AUTO_PAUSE_RX;
+               req->enables |=
+                       cpu_to_le32(PORT_PHY_CFG_REQ_ENABLES_AUTO_PAUSE);
+       } else {
+               if (bp->link_info.req_flow_ctrl & BNXT_LINK_PAUSE_RX)
+                       req->force_pause |= PORT_PHY_CFG_REQ_FORCE_PAUSE_RX;
+               if (bp->link_info.req_flow_ctrl & BNXT_LINK_PAUSE_TX)
+                       req->force_pause |= PORT_PHY_CFG_REQ_FORCE_PAUSE_TX;
+               req->enables |=
+                       cpu_to_le32(PORT_PHY_CFG_REQ_ENABLES_FORCE_PAUSE);
+       }
+}
+
+static void bnxt_hwrm_set_link_common(struct bnxt *bp,
+                                     struct hwrm_port_phy_cfg_input *req)
+{
+       u8 autoneg = bp->link_info.autoneg;
+       u16 fw_link_speed = bp->link_info.req_link_speed;
+       u32 advertising = bp->link_info.advertising;
+
+       if (autoneg & BNXT_AUTONEG_SPEED) {
+               req->auto_mode |=
+                       PORT_PHY_CFG_REQ_AUTO_MODE_MASK;
+
+               req->enables |= cpu_to_le32(
+                       PORT_PHY_CFG_REQ_ENABLES_AUTO_LINK_SPEED_MASK);
+               req->auto_link_speed_mask = cpu_to_le16(advertising);
+
+               req->enables |= cpu_to_le32(PORT_PHY_CFG_REQ_ENABLES_AUTO_MODE);
+               req->flags |=
+                       cpu_to_le32(PORT_PHY_CFG_REQ_FLAGS_RESTART_AUTONEG);
+       } else {
+               req->force_link_speed = cpu_to_le16(fw_link_speed);
+               req->flags |= cpu_to_le32(PORT_PHY_CFG_REQ_FLAGS_FORCE);
+       }
+
+       /* currently don't support half duplex */
+       req->auto_duplex = PORT_PHY_CFG_REQ_AUTO_DUPLEX_FULL;
+       req->enables |= cpu_to_le32(PORT_PHY_CFG_REQ_ENABLES_AUTO_DUPLEX);
+       /* tell chimp that the setting takes effect immediately */
+       req->flags |= cpu_to_le32(PORT_PHY_CFG_REQ_FLAGS_RESET_PHY);
+}
+
+int bnxt_hwrm_set_pause(struct bnxt *bp)
+{
+       struct hwrm_port_phy_cfg_input req = {0};
+       int rc;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_CFG, -1, -1);
+       bnxt_hwrm_set_pause_common(bp, &req);
+
+       if ((bp->link_info.autoneg & BNXT_AUTONEG_FLOW_CTRL) ||
+           bp->link_info.force_link_chng)
+               bnxt_hwrm_set_link_common(bp, &req);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc && !(bp->link_info.autoneg & BNXT_AUTONEG_FLOW_CTRL)) {
+               /* since changing of pause setting doesn't trigger any link
+                * change event, the driver needs to update the current pause
+                * result upon successfully return of the phy_cfg command
+                */
+               bp->link_info.pause =
+               bp->link_info.force_pause_setting = bp->link_info.req_flow_ctrl;
+               bp->link_info.auto_pause_setting = 0;
+               if (!bp->link_info.force_link_chng)
+                       bnxt_report_link(bp);
+       }
+       bp->link_info.force_link_chng = false;
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+int bnxt_hwrm_set_link_setting(struct bnxt *bp, bool set_pause)
+{
+       struct hwrm_port_phy_cfg_input req = {0};
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_CFG, -1, -1);
+       if (set_pause)
+               bnxt_hwrm_set_pause_common(bp, &req);
+
+       bnxt_hwrm_set_link_common(bp, &req);
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
+static int bnxt_update_phy_setting(struct bnxt *bp)
+{
+       int rc;
+       bool update_link = false;
+       bool update_pause = false;
+       struct bnxt_link_info *link_info = &bp->link_info;
+
+       rc = bnxt_update_link(bp, true);
+       if (rc) {
+               netdev_err(bp->dev, "failed to update link (rc: %x)\n",
+                          rc);
+               return rc;
+       }
+       if ((link_info->autoneg & BNXT_AUTONEG_FLOW_CTRL) &&
+           link_info->auto_pause_setting != link_info->req_flow_ctrl)
+               update_pause = true;
+       if (!(link_info->autoneg & BNXT_AUTONEG_FLOW_CTRL) &&
+           link_info->force_pause_setting != link_info->req_flow_ctrl)
+               update_pause = true;
+       if (link_info->req_duplex != link_info->duplex_setting)
+               update_link = true;
+       if (!(link_info->autoneg & BNXT_AUTONEG_SPEED)) {
+               if (BNXT_AUTO_MODE(link_info->auto_mode))
+                       update_link = true;
+               if (link_info->req_link_speed != link_info->force_link_speed)
+                       update_link = true;
+       } else {
+               if (link_info->auto_mode == BNXT_LINK_AUTO_NONE)
+                       update_link = true;
+               if (link_info->advertising != link_info->auto_link_speeds)
+                       update_link = true;
+               if (link_info->req_link_speed != link_info->auto_link_speed)
+                       update_link = true;
+       }
+
+       if (update_link)
+               rc = bnxt_hwrm_set_link_setting(bp, update_pause);
+       else if (update_pause)
+               rc = bnxt_hwrm_set_pause(bp);
+       if (rc) {
+               netdev_err(bp->dev, "failed to update phy setting (rc: %x)\n",
+                          rc);
+               return rc;
+       }
+
+       return rc;
+}
+
+static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
+{
+       int rc = 0;
+
+       netif_carrier_off(bp->dev);
+       if (irq_re_init) {
+               rc = bnxt_setup_int_mode(bp);
+               if (rc) {
+                       netdev_err(bp->dev, "bnxt_setup_int_mode err: %x\n",
+                                  rc);
+                       return rc;
+               }
+       }
+       if ((bp->flags & BNXT_FLAG_RFS) &&
+           !(bp->flags & BNXT_FLAG_USING_MSIX)) {
+               /* disable RFS if falling back to INTA */
+               bp->dev->hw_features &= ~NETIF_F_NTUPLE;
+               bp->flags &= ~BNXT_FLAG_RFS;
+       }
+
+       rc = bnxt_alloc_mem(bp, irq_re_init);
+       if (rc) {
+               netdev_err(bp->dev, "bnxt_alloc_mem err: %x\n", rc);
+               goto open_err_free_mem;
+       }
+
+       if (irq_re_init) {
+               bnxt_init_napi(bp);
+               rc = bnxt_request_irq(bp);
+               if (rc) {
+                       netdev_err(bp->dev, "bnxt_request_irq err: %x\n", rc);
+                       goto open_err;
+               }
+       }
+
+       bnxt_enable_napi(bp);
+
+       rc = bnxt_init_nic(bp, irq_re_init);
+       if (rc) {
+               netdev_err(bp->dev, "bnxt_init_nic err: %x\n", rc);
+               goto open_err;
+       }
+
+       if (link_re_init) {
+               rc = bnxt_update_phy_setting(bp);
+               if (rc)
+                       goto open_err;
+       }
+
+       if (irq_re_init) {
+#if defined(CONFIG_VXLAN) || defined(CONFIG_VXLAN_MODULE)
+               vxlan_get_rx_port(bp->dev);
+#endif
+               if (!bnxt_hwrm_tunnel_dst_port_alloc(
+                               bp, htons(0x17c1),
+                               TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE))
+                       bp->nge_port_cnt = 1;
+       }
+
+       bp->state = BNXT_STATE_OPEN;
+       bnxt_enable_int(bp);
+       /* Enable TX queues */
+       bnxt_tx_enable(bp);
+       mod_timer(&bp->timer, jiffies + bp->current_interval);
+
+       return 0;
+
+open_err:
+       bnxt_disable_napi(bp);
+       bnxt_del_napi(bp);
+
+open_err_free_mem:
+       bnxt_free_skbs(bp);
+       bnxt_free_irq(bp);
+       bnxt_free_mem(bp, true);
+       return rc;
+}
+
+/* rtnl_lock held */
+int bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
+{
+       int rc = 0;
+
+       rc = __bnxt_open_nic(bp, irq_re_init, link_re_init);
+       if (rc) {
+               netdev_err(bp->dev, "nic open fail (rc: %x)\n", rc);
+               dev_close(bp->dev);
+       }
+       return rc;
+}
+
+static int bnxt_open(struct net_device *dev)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       int rc = 0;
+
+       rc = bnxt_hwrm_func_reset(bp);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm chip reset failure rc: %x\n",
+                          rc);
+               rc = -1;
+               return rc;
+       }
+       return __bnxt_open_nic(bp, true, true);
+}
+
+static void bnxt_disable_int_sync(struct bnxt *bp)
+{
+       int i;
+
+       atomic_inc(&bp->intr_sem);
+       if (!netif_running(bp->dev))
+               return;
+
+       bnxt_disable_int(bp);
+       for (i = 0; i < bp->cp_nr_rings; i++)
+               synchronize_irq(bp->irq_tbl[i].vector);
+}
+
+int bnxt_close_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
+{
+       int rc = 0;
+
+#ifdef CONFIG_BNXT_SRIOV
+       if (bp->sriov_cfg) {
+               rc = wait_event_interruptible_timeout(bp->sriov_cfg_wait,
+                                                     !bp->sriov_cfg,
+                                                     BNXT_SRIOV_CFG_WAIT_TMO);
+               if (rc)
+                       netdev_warn(bp->dev, "timeout waiting for SRIOV config operation to complete!\n");
+       }
+#endif
+       /* Change device state to avoid TX queue wake up's */
+       bnxt_tx_disable(bp);
+
+       bp->state = BNXT_STATE_CLOSED;
+       cancel_work_sync(&bp->sp_task);
+
+       /* Flush rings before disabling interrupts */
+       bnxt_shutdown_nic(bp, irq_re_init);
+
+       /* TODO CHIMP_FW: Link/PHY related cleanup if (link_re_init) */
+
+       bnxt_disable_napi(bp);
+       bnxt_disable_int_sync(bp);
+       del_timer_sync(&bp->timer);
+       bnxt_free_skbs(bp);
+
+       if (irq_re_init) {
+               bnxt_free_irq(bp);
+               bnxt_del_napi(bp);
+       }
+       bnxt_free_mem(bp, irq_re_init);
+       return rc;
+}
+
+static int bnxt_close(struct net_device *dev)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       bnxt_close_nic(bp, true, true);
+       return 0;
+}
+
+/* rtnl_lock held */
+static int bnxt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       switch (cmd) {
+       case SIOCGMIIPHY:
+               /* fallthru */
+       case SIOCGMIIREG: {
+               if (!netif_running(dev))
+                       return -EAGAIN;
+
+               return 0;
+       }
+
+       case SIOCSMIIREG:
+               if (!netif_running(dev))
+                       return -EAGAIN;
+
+               return 0;
+
+       default:
+               /* do nothing */
+               break;
+       }
+       return -EOPNOTSUPP;
+}
+
+static struct rtnl_link_stats64 *
+bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+       u32 i;
+       struct bnxt *bp = netdev_priv(dev);
+
+       memset(stats, 0, sizeof(struct rtnl_link_stats64));
+
+       if (!bp->bnapi)
+               return stats;
+
+       /* TODO check if we need to synchronize with bnxt_close path */
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+               struct ctx_hw_stats *hw_stats = cpr->hw_stats;
+
+               stats->rx_packets += le64_to_cpu(hw_stats->rx_ucast_pkts);
+               stats->rx_packets += le64_to_cpu(hw_stats->rx_mcast_pkts);
+               stats->rx_packets += le64_to_cpu(hw_stats->rx_bcast_pkts);
+
+               stats->tx_packets += le64_to_cpu(hw_stats->tx_ucast_pkts);
+               stats->tx_packets += le64_to_cpu(hw_stats->tx_mcast_pkts);
+               stats->tx_packets += le64_to_cpu(hw_stats->tx_bcast_pkts);
+
+               stats->rx_bytes += le64_to_cpu(hw_stats->rx_ucast_bytes);
+               stats->rx_bytes += le64_to_cpu(hw_stats->rx_mcast_bytes);
+               stats->rx_bytes += le64_to_cpu(hw_stats->rx_bcast_bytes);
+
+               stats->tx_bytes += le64_to_cpu(hw_stats->tx_ucast_bytes);
+               stats->tx_bytes += le64_to_cpu(hw_stats->tx_mcast_bytes);
+               stats->tx_bytes += le64_to_cpu(hw_stats->tx_bcast_bytes);
+
+               stats->rx_missed_errors +=
+                       le64_to_cpu(hw_stats->rx_discard_pkts);
+
+               stats->multicast += le64_to_cpu(hw_stats->rx_mcast_pkts);
+
+               stats->rx_dropped += le64_to_cpu(hw_stats->rx_drop_pkts);
+
+               stats->tx_dropped += le64_to_cpu(hw_stats->tx_drop_pkts);
+       }
+
+       return stats;
+}
+
+static bool bnxt_mc_list_updated(struct bnxt *bp, u32 *rx_mask)
+{
+       struct net_device *dev = bp->dev;
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[0];
+       struct netdev_hw_addr *ha;
+       u8 *haddr;
+       int mc_count = 0;
+       bool update = false;
+       int off = 0;
+
+       netdev_for_each_mc_addr(ha, dev) {
+               if (mc_count >= BNXT_MAX_MC_ADDRS) {
+                       *rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST;
+                       vnic->mc_list_count = 0;
+                       return false;
+               }
+               haddr = ha->addr;
+               if (!ether_addr_equal(haddr, vnic->mc_list + off)) {
+                       memcpy(vnic->mc_list + off, haddr, ETH_ALEN);
+                       update = true;
+               }
+               off += ETH_ALEN;
+               mc_count++;
+       }
+       if (mc_count)
+               *rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_MCAST;
+
+       if (mc_count != vnic->mc_list_count) {
+               vnic->mc_list_count = mc_count;
+               update = true;
+       }
+       return update;
+}
+
+static bool bnxt_uc_list_updated(struct bnxt *bp)
+{
+       struct net_device *dev = bp->dev;
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[0];
+       struct netdev_hw_addr *ha;
+       int off = 0;
+
+       if (netdev_uc_count(dev) != (vnic->uc_filter_count - 1))
+               return true;
+
+       netdev_for_each_uc_addr(ha, dev) {
+               if (!ether_addr_equal(ha->addr, vnic->uc_list + off))
+                       return true;
+
+               off += ETH_ALEN;
+       }
+       return false;
+}
+
+static void bnxt_set_rx_mode(struct net_device *dev)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[0];
+       u32 mask = vnic->rx_mask;
+       bool mc_update = false;
+       bool uc_update;
+
+       if (!netif_running(dev))
+               return;
+
+       mask &= ~(CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS |
+                 CFA_L2_SET_RX_MASK_REQ_MASK_MCAST |
+                 CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST);
+
+       /* Only allow PF to be in promiscuous mode */
+       if ((dev->flags & IFF_PROMISC) && BNXT_PF(bp))
+               mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS;
+
+       uc_update = bnxt_uc_list_updated(bp);
+
+       if (dev->flags & IFF_ALLMULTI) {
+               mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST;
+               vnic->mc_list_count = 0;
+       } else {
+               mc_update = bnxt_mc_list_updated(bp, &mask);
+       }
+
+       if (mask != vnic->rx_mask || uc_update || mc_update) {
+               vnic->rx_mask = mask;
+
+               set_bit(BNXT_RX_MASK_SP_EVENT, &bp->sp_event);
+               schedule_work(&bp->sp_task);
+       }
+}
+
+static void bnxt_cfg_rx_mode(struct bnxt *bp)
+{
+       struct net_device *dev = bp->dev;
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[0];
+       struct netdev_hw_addr *ha;
+       int i, off = 0, rc;
+       bool uc_update;
+
+       netif_addr_lock_bh(dev);
+       uc_update = bnxt_uc_list_updated(bp);
+       netif_addr_unlock_bh(dev);
+
+       if (!uc_update)
+               goto skip_uc;
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       for (i = 1; i < vnic->uc_filter_count; i++) {
+               struct hwrm_cfa_l2_filter_free_input req = {0};
+
+               bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_L2_FILTER_FREE, -1,
+                                      -1);
+
+               req.l2_filter_id = vnic->fw_l2_filter_id[i];
+
+               rc = _hwrm_send_message(bp, &req, sizeof(req),
+                                       HWRM_CMD_TIMEOUT);
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+
+       vnic->uc_filter_count = 1;
+
+       netif_addr_lock_bh(dev);
+       if (netdev_uc_count(dev) > (BNXT_MAX_UC_ADDRS - 1)) {
+               vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS;
+       } else {
+               netdev_for_each_uc_addr(ha, dev) {
+                       memcpy(vnic->uc_list + off, ha->addr, ETH_ALEN);
+                       off += ETH_ALEN;
+                       vnic->uc_filter_count++;
+               }
+       }
+       netif_addr_unlock_bh(dev);
+
+       for (i = 1, off = 0; i < vnic->uc_filter_count; i++, off += ETH_ALEN) {
+               rc = bnxt_hwrm_set_vnic_filter(bp, 0, i, vnic->uc_list + off);
+               if (rc) {
+                       netdev_err(bp->dev, "HWRM vnic filter failure rc: %x\n",
+                                  rc);
+                       vnic->uc_filter_count = i;
+               }
+       }
+
+skip_uc:
+       rc = bnxt_hwrm_cfa_l2_set_rx_mask(bp, 0);
+       if (rc)
+               netdev_err(bp->dev, "HWRM cfa l2 rx mask failure rc: %x\n",
+                          rc);
+}
+
+static netdev_features_t bnxt_fix_features(struct net_device *dev,
+                                          netdev_features_t features)
+{
+       return features;
+}
+
+static int bnxt_set_features(struct net_device *dev, netdev_features_t features)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       u32 flags = bp->flags;
+       u32 changes;
+       int rc = 0;
+       bool re_init = false;
+       bool update_tpa = false;
+
+       flags &= ~BNXT_FLAG_ALL_CONFIG_FEATS;
+       if ((features & NETIF_F_GRO) && (bp->pdev->revision > 0))
+               flags |= BNXT_FLAG_GRO;
+       if (features & NETIF_F_LRO)
+               flags |= BNXT_FLAG_LRO;
+
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               flags |= BNXT_FLAG_STRIP_VLAN;
+
+       if (features & NETIF_F_NTUPLE)
+               flags |= BNXT_FLAG_RFS;
+
+       changes = flags ^ bp->flags;
+       if (changes & BNXT_FLAG_TPA) {
+               update_tpa = true;
+               if ((bp->flags & BNXT_FLAG_TPA) == 0 ||
+                   (flags & BNXT_FLAG_TPA) == 0)
+                       re_init = true;
+       }
+
+       if (changes & ~BNXT_FLAG_TPA)
+               re_init = true;
+
+       if (flags != bp->flags) {
+               u32 old_flags = bp->flags;
+
+               bp->flags = flags;
+
+               if (!netif_running(dev)) {
+                       if (update_tpa)
+                               bnxt_set_ring_params(bp);
+                       return rc;
+               }
+
+               if (re_init) {
+                       bnxt_close_nic(bp, false, false);
+                       if (update_tpa)
+                               bnxt_set_ring_params(bp);
+
+                       return bnxt_open_nic(bp, false, false);
+               }
+               if (update_tpa) {
+                       rc = bnxt_set_tpa(bp,
+                                         (flags & BNXT_FLAG_TPA) ?
+                                         true : false);
+                       if (rc)
+                               bp->flags = old_flags;
+               }
+       }
+       return rc;
+}
+
+static void bnxt_dbg_dump_states(struct bnxt *bp)
+{
+       int i;
+       struct bnxt_napi *bnapi;
+       struct bnxt_tx_ring_info *txr;
+       struct bnxt_rx_ring_info *rxr;
+       struct bnxt_cp_ring_info *cpr;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               bnapi = bp->bnapi[i];
+               txr = &bnapi->tx_ring;
+               rxr = &bnapi->rx_ring;
+               cpr = &bnapi->cp_ring;
+               if (netif_msg_drv(bp)) {
+                       netdev_info(bp->dev, "[%d]: tx{fw_ring: %d prod: %x cons: %x}\n",
+                                   i, txr->tx_ring_struct.fw_ring_id,
+                                   txr->tx_prod, txr->tx_cons);
+                       netdev_info(bp->dev, "[%d]: rx{fw_ring: %d prod: %x} rx_agg{fw_ring: %d agg_prod: %x sw_agg_prod: %x}\n",
+                                   i, rxr->rx_ring_struct.fw_ring_id,
+                                   rxr->rx_prod,
+                                   rxr->rx_agg_ring_struct.fw_ring_id,
+                                   rxr->rx_agg_prod, rxr->rx_sw_agg_prod);
+                       netdev_info(bp->dev, "[%d]: cp{fw_ring: %d raw_cons: %x}\n",
+                                   i, cpr->cp_ring_struct.fw_ring_id,
+                                   cpr->cp_raw_cons);
+               }
+       }
+}
+
+static void bnxt_reset_task(struct bnxt *bp)
+{
+       bnxt_dbg_dump_states(bp);
+       if (netif_running(bp->dev))
+               bnxt_tx_disable(bp); /* prevent tx timout again */
+}
+
+static void bnxt_tx_timeout(struct net_device *dev)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       netdev_err(bp->dev,  "TX timeout detected, starting reset task!\n");
+       set_bit(BNXT_RESET_TASK_SP_EVENT, &bp->sp_event);
+       schedule_work(&bp->sp_task);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void bnxt_poll_controller(struct net_device *dev)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       int i;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_irq *irq = &bp->irq_tbl[i];
+
+               disable_irq(irq->vector);
+               irq->handler(irq->vector, bp->bnapi[i]);
+               enable_irq(irq->vector);
+       }
+}
+#endif
+
+static void bnxt_timer(unsigned long data)
+{
+       struct bnxt *bp = (struct bnxt *)data;
+       struct net_device *dev = bp->dev;
+
+       if (!netif_running(dev))
+               return;
+
+       if (atomic_read(&bp->intr_sem) != 0)
+               goto bnxt_restart_timer;
+
+bnxt_restart_timer:
+       mod_timer(&bp->timer, jiffies + bp->current_interval);
+}
+
+static void bnxt_cfg_ntp_filters(struct bnxt *);
+
+static void bnxt_sp_task(struct work_struct *work)
+{
+       struct bnxt *bp = container_of(work, struct bnxt, sp_task);
+       int rc;
+
+       if (bp->state != BNXT_STATE_OPEN)
+               return;
+
+       if (test_and_clear_bit(BNXT_RX_MASK_SP_EVENT, &bp->sp_event))
+               bnxt_cfg_rx_mode(bp);
+
+       if (test_and_clear_bit(BNXT_RX_NTP_FLTR_SP_EVENT, &bp->sp_event))
+               bnxt_cfg_ntp_filters(bp);
+       if (test_and_clear_bit(BNXT_LINK_CHNG_SP_EVENT, &bp->sp_event)) {
+               rc = bnxt_update_link(bp, true);
+               if (rc)
+                       netdev_err(bp->dev, "SP task can't update link (rc: %x)\n",
+                                  rc);
+       }
+       if (test_and_clear_bit(BNXT_HWRM_EXEC_FWD_REQ_SP_EVENT, &bp->sp_event))
+               bnxt_hwrm_exec_fwd_req(bp);
+       if (test_and_clear_bit(BNXT_VXLAN_ADD_PORT_SP_EVENT, &bp->sp_event)) {
+               bnxt_hwrm_tunnel_dst_port_alloc(
+                       bp, bp->vxlan_port,
+                       TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN);
+       }
+       if (test_and_clear_bit(BNXT_VXLAN_DEL_PORT_SP_EVENT, &bp->sp_event)) {
+               bnxt_hwrm_tunnel_dst_port_free(
+                       bp, TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN);
+       }
+       if (test_and_clear_bit(BNXT_RESET_TASK_SP_EVENT, &bp->sp_event))
+               bnxt_reset_task(bp);
+}
+
+static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev)
+{
+       int rc;
+       struct bnxt *bp = netdev_priv(dev);
+
+       SET_NETDEV_DEV(dev, &pdev->dev);
+
+       /* enable device (incl. PCI PM wakeup), and bus-mastering */
+       rc = pci_enable_device(pdev);
+       if (rc) {
+               dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n");
+               goto init_err;
+       }
+
+       if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+               dev_err(&pdev->dev,
+                       "Cannot find PCI device base address, aborting\n");
+               rc = -ENODEV;
+               goto init_err_disable;
+       }
+
+       rc = pci_request_regions(pdev, DRV_MODULE_NAME);
+       if (rc) {
+               dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n");
+               goto init_err_disable;
+       }
+
+       if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)) != 0 &&
+           dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)) != 0) {
+               dev_err(&pdev->dev, "System does not support DMA, aborting\n");
+               goto init_err_disable;
+       }
+
+       pci_set_master(pdev);
+
+       bp->dev = dev;
+       bp->pdev = pdev;
+
+       bp->bar0 = pci_ioremap_bar(pdev, 0);
+       if (!bp->bar0) {
+               dev_err(&pdev->dev, "Cannot map device registers, aborting\n");
+               rc = -ENOMEM;
+               goto init_err_release;
+       }
+
+       bp->bar1 = pci_ioremap_bar(pdev, 2);
+       if (!bp->bar1) {
+               dev_err(&pdev->dev, "Cannot map doorbell registers, aborting\n");
+               rc = -ENOMEM;
+               goto init_err_release;
+       }
+
+       bp->bar2 = pci_ioremap_bar(pdev, 4);
+       if (!bp->bar2) {
+               dev_err(&pdev->dev, "Cannot map bar4 registers, aborting\n");
+               rc = -ENOMEM;
+               goto init_err_release;
+       }
+
+       INIT_WORK(&bp->sp_task, bnxt_sp_task);
+
+       spin_lock_init(&bp->ntp_fltr_lock);
+
+       bp->rx_ring_size = BNXT_DEFAULT_RX_RING_SIZE;
+       bp->tx_ring_size = BNXT_DEFAULT_TX_RING_SIZE;
+
+       bp->coal_ticks = BNXT_USEC_TO_COAL_TIMER(4);
+       bp->coal_bufs = 20;
+       bp->coal_ticks_irq = BNXT_USEC_TO_COAL_TIMER(1);
+       bp->coal_bufs_irq = 2;
+
+       init_timer(&bp->timer);
+       bp->timer.data = (unsigned long)bp;
+       bp->timer.function = bnxt_timer;
+       bp->current_interval = BNXT_TIMER_INTERVAL;
+
+       bp->state = BNXT_STATE_CLOSED;
+
+       return 0;
+
+init_err_release:
+       if (bp->bar2) {
+               pci_iounmap(pdev, bp->bar2);
+               bp->bar2 = NULL;
+       }
+
+       if (bp->bar1) {
+               pci_iounmap(pdev, bp->bar1);
+               bp->bar1 = NULL;
+       }
+
+       if (bp->bar0) {
+               pci_iounmap(pdev, bp->bar0);
+               bp->bar0 = NULL;
+       }
+
+       pci_release_regions(pdev);
+
+init_err_disable:
+       pci_disable_device(pdev);
+
+init_err:
+       return rc;
+}
+
+/* rtnl_lock held */
+static int bnxt_change_mac_addr(struct net_device *dev, void *p)
+{
+       struct sockaddr *addr = p;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+       return 0;
+}
+
+/* rtnl_lock held */
+static int bnxt_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       if (new_mtu < 60 || new_mtu > 9000)
+               return -EINVAL;
+
+       if (netif_running(dev))
+               bnxt_close_nic(bp, false, false);
+
+       dev->mtu = new_mtu;
+       bnxt_set_ring_params(bp);
+
+       if (netif_running(dev))
+               return bnxt_open_nic(bp, false, false);
+
+       return 0;
+}
+
+static int bnxt_setup_tc(struct net_device *dev, u8 tc)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       if (tc > bp->max_tc) {
+               netdev_err(dev, "too many traffic classes requested: %d Max supported is %d\n",
+                          tc, bp->max_tc);
+               return -EINVAL;
+       }
+
+       if (netdev_get_num_tc(dev) == tc)
+               return 0;
+
+       if (tc) {
+               int max_rx_rings, max_tx_rings;
+
+               bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
+               if (bp->tx_nr_rings_per_tc * tc > max_tx_rings)
+                       return -ENOMEM;
+       }
+
+       /* Needs to close the device and do hw resource re-allocations */
+       if (netif_running(bp->dev))
+               bnxt_close_nic(bp, true, false);
+
+       if (tc) {
+               bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc;
+               netdev_set_num_tc(dev, tc);
+       } else {
+               bp->tx_nr_rings = bp->tx_nr_rings_per_tc;
+               netdev_reset_tc(dev);
+       }
+       bp->cp_nr_rings = max_t(int, bp->tx_nr_rings, bp->rx_nr_rings);
+       bp->num_stat_ctxs = bp->cp_nr_rings;
+
+       if (netif_running(bp->dev))
+               return bnxt_open_nic(bp, true, false);
+
+       return 0;
+}
+
+#ifdef CONFIG_RFS_ACCEL
+static bool bnxt_fltr_match(struct bnxt_ntuple_filter *f1,
+                           struct bnxt_ntuple_filter *f2)
+{
+       struct flow_keys *keys1 = &f1->fkeys;
+       struct flow_keys *keys2 = &f2->fkeys;
+
+       if (keys1->addrs.v4addrs.src == keys2->addrs.v4addrs.src &&
+           keys1->addrs.v4addrs.dst == keys2->addrs.v4addrs.dst &&
+           keys1->ports.ports == keys2->ports.ports &&
+           keys1->basic.ip_proto == keys2->basic.ip_proto &&
+           keys1->basic.n_proto == keys2->basic.n_proto &&
+           ether_addr_equal(f1->src_mac_addr, f2->src_mac_addr))
+               return true;
+
+       return false;
+}
+
+static int bnxt_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb,
+                             u16 rxq_index, u32 flow_id)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_ntuple_filter *fltr, *new_fltr;
+       struct flow_keys *fkeys;
+       struct ethhdr *eth = (struct ethhdr *)skb_mac_header(skb);
+       int rc = 0, idx;
+       struct hlist_head *head;
+
+       if (skb->encapsulation)
+               return -EPROTONOSUPPORT;
+
+       new_fltr = kzalloc(sizeof(*new_fltr), GFP_ATOMIC);
+       if (!new_fltr)
+               return -ENOMEM;
+
+       fkeys = &new_fltr->fkeys;
+       if (!skb_flow_dissect_flow_keys(skb, fkeys, 0)) {
+               rc = -EPROTONOSUPPORT;
+               goto err_free;
+       }
+
+       if ((fkeys->basic.n_proto != htons(ETH_P_IP)) ||
+           ((fkeys->basic.ip_proto != IPPROTO_TCP) &&
+            (fkeys->basic.ip_proto != IPPROTO_UDP))) {
+               rc = -EPROTONOSUPPORT;
+               goto err_free;
+       }
+
+       memcpy(new_fltr->src_mac_addr, eth->h_source, ETH_ALEN);
+
+       idx = skb_get_hash_raw(skb) & BNXT_NTP_FLTR_HASH_MASK;
+       head = &bp->ntp_fltr_hash_tbl[idx];
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(fltr, head, hash) {
+               if (bnxt_fltr_match(fltr, new_fltr)) {
+                       rcu_read_unlock();
+                       rc = 0;
+                       goto err_free;
+               }
+       }
+       rcu_read_unlock();
+
+       spin_lock_bh(&bp->ntp_fltr_lock);
+       new_fltr->sw_id = bitmap_find_free_region(bp->ntp_fltr_bmap,
+                                                 BNXT_NTP_FLTR_MAX_FLTR, 0);
+       if (new_fltr->sw_id < 0) {
+               spin_unlock_bh(&bp->ntp_fltr_lock);
+               rc = -ENOMEM;
+               goto err_free;
+       }
+
+       new_fltr->flow_id = flow_id;
+       new_fltr->rxq = rxq_index;
+       hlist_add_head_rcu(&new_fltr->hash, head);
+       bp->ntp_fltr_count++;
+       spin_unlock_bh(&bp->ntp_fltr_lock);
+
+       set_bit(BNXT_RX_NTP_FLTR_SP_EVENT, &bp->sp_event);
+       schedule_work(&bp->sp_task);
+
+       return new_fltr->sw_id;
+
+err_free:
+       kfree(new_fltr);
+       return rc;
+}
+
+static void bnxt_cfg_ntp_filters(struct bnxt *bp)
+{
+       int i;
+
+       for (i = 0; i < BNXT_NTP_FLTR_HASH_SIZE; i++) {
+               struct hlist_head *head;
+               struct hlist_node *tmp;
+               struct bnxt_ntuple_filter *fltr;
+               int rc;
+
+               head = &bp->ntp_fltr_hash_tbl[i];
+               hlist_for_each_entry_safe(fltr, tmp, head, hash) {
+                       bool del = false;
+
+                       if (test_bit(BNXT_FLTR_VALID, &fltr->state)) {
+                               if (rps_may_expire_flow(bp->dev, fltr->rxq,
+                                                       fltr->flow_id,
+                                                       fltr->sw_id)) {
+                                       bnxt_hwrm_cfa_ntuple_filter_free(bp,
+                                                                        fltr);
+                                       del = true;
+                               }
+                       } else {
+                               rc = bnxt_hwrm_cfa_ntuple_filter_alloc(bp,
+                                                                      fltr);
+                               if (rc)
+                                       del = true;
+                               else
+                                       set_bit(BNXT_FLTR_VALID, &fltr->state);
+                       }
+
+                       if (del) {
+                               spin_lock_bh(&bp->ntp_fltr_lock);
+                               hlist_del_rcu(&fltr->hash);
+                               bp->ntp_fltr_count--;
+                               spin_unlock_bh(&bp->ntp_fltr_lock);
+                               synchronize_rcu();
+                               clear_bit(fltr->sw_id, bp->ntp_fltr_bmap);
+                               kfree(fltr);
+                       }
+               }
+       }
+}
+
+#else
+
+static void bnxt_cfg_ntp_filters(struct bnxt *bp)
+{
+}
+
+#endif /* CONFIG_RFS_ACCEL */
+
+static void bnxt_add_vxlan_port(struct net_device *dev, sa_family_t sa_family,
+                               __be16 port)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       if (!netif_running(dev))
+               return;
+
+       if (sa_family != AF_INET6 && sa_family != AF_INET)
+               return;
+
+       if (bp->vxlan_port_cnt && bp->vxlan_port != port)
+               return;
+
+       bp->vxlan_port_cnt++;
+       if (bp->vxlan_port_cnt == 1) {
+               bp->vxlan_port = port;
+               set_bit(BNXT_VXLAN_ADD_PORT_SP_EVENT, &bp->sp_event);
+               schedule_work(&bp->sp_task);
+       }
+}
+
+static void bnxt_del_vxlan_port(struct net_device *dev, sa_family_t sa_family,
+                               __be16 port)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       if (!netif_running(dev))
+               return;
+
+       if (sa_family != AF_INET6 && sa_family != AF_INET)
+               return;
+
+       if (bp->vxlan_port_cnt && bp->vxlan_port == port) {
+               bp->vxlan_port_cnt--;
+
+               if (bp->vxlan_port_cnt == 0) {
+                       set_bit(BNXT_VXLAN_DEL_PORT_SP_EVENT, &bp->sp_event);
+                       schedule_work(&bp->sp_task);
+               }
+       }
+}
+
+static const struct net_device_ops bnxt_netdev_ops = {
+       .ndo_open               = bnxt_open,
+       .ndo_start_xmit         = bnxt_start_xmit,
+       .ndo_stop               = bnxt_close,
+       .ndo_get_stats64        = bnxt_get_stats64,
+       .ndo_set_rx_mode        = bnxt_set_rx_mode,
+       .ndo_do_ioctl           = bnxt_ioctl,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = bnxt_change_mac_addr,
+       .ndo_change_mtu         = bnxt_change_mtu,
+       .ndo_fix_features       = bnxt_fix_features,
+       .ndo_set_features       = bnxt_set_features,
+       .ndo_tx_timeout         = bnxt_tx_timeout,
+#ifdef CONFIG_BNXT_SRIOV
+       .ndo_get_vf_config      = bnxt_get_vf_config,
+       .ndo_set_vf_mac         = bnxt_set_vf_mac,
+       .ndo_set_vf_vlan        = bnxt_set_vf_vlan,
+       .ndo_set_vf_rate        = bnxt_set_vf_bw,
+       .ndo_set_vf_link_state  = bnxt_set_vf_link_state,
+       .ndo_set_vf_spoofchk    = bnxt_set_vf_spoofchk,
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = bnxt_poll_controller,
+#endif
+       .ndo_setup_tc           = bnxt_setup_tc,
+#ifdef CONFIG_RFS_ACCEL
+       .ndo_rx_flow_steer      = bnxt_rx_flow_steer,
+#endif
+       .ndo_add_vxlan_port     = bnxt_add_vxlan_port,
+       .ndo_del_vxlan_port     = bnxt_del_vxlan_port,
+#ifdef CONFIG_NET_RX_BUSY_POLL
+       .ndo_busy_poll          = bnxt_busy_poll,
+#endif
+};
+
+static void bnxt_remove_one(struct pci_dev *pdev)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct bnxt *bp = netdev_priv(dev);
+
+       if (BNXT_PF(bp))
+               bnxt_sriov_disable(bp);
+
+       unregister_netdev(dev);
+       cancel_work_sync(&bp->sp_task);
+       bp->sp_event = 0;
+
+       bnxt_free_hwrm_resources(bp);
+       pci_iounmap(pdev, bp->bar2);
+       pci_iounmap(pdev, bp->bar1);
+       pci_iounmap(pdev, bp->bar0);
+       free_netdev(dev);
+
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+}
+
+static int bnxt_probe_phy(struct bnxt *bp)
+{
+       int rc = 0;
+       struct bnxt_link_info *link_info = &bp->link_info;
+       char phy_ver[PHY_VER_STR_LEN];
+
+       rc = bnxt_update_link(bp, false);
+       if (rc) {
+               netdev_err(bp->dev, "Probe phy can't update link (rc: %x)\n",
+                          rc);
+               return rc;
+       }
+
+       /*initialize the ethool setting copy with NVM settings */
+       if (BNXT_AUTO_MODE(link_info->auto_mode))
+               link_info->autoneg |= BNXT_AUTONEG_SPEED;
+
+       if (link_info->auto_pause_setting & BNXT_LINK_PAUSE_BOTH) {
+               if (link_info->auto_pause_setting == BNXT_LINK_PAUSE_BOTH)
+                       link_info->autoneg |= BNXT_AUTONEG_FLOW_CTRL;
+               link_info->req_flow_ctrl = link_info->auto_pause_setting;
+       } else if (link_info->force_pause_setting & BNXT_LINK_PAUSE_BOTH) {
+               link_info->req_flow_ctrl = link_info->force_pause_setting;
+       }
+       link_info->req_duplex = link_info->duplex_setting;
+       if (link_info->autoneg & BNXT_AUTONEG_SPEED)
+               link_info->req_link_speed = link_info->auto_link_speed;
+       else
+               link_info->req_link_speed = link_info->force_link_speed;
+       link_info->advertising = link_info->auto_link_speeds;
+       snprintf(phy_ver, PHY_VER_STR_LEN, " ph %d.%d.%d",
+                link_info->phy_ver[0],
+                link_info->phy_ver[1],
+                link_info->phy_ver[2]);
+       strcat(bp->fw_ver_str, phy_ver);
+       return rc;
+}
+
+static int bnxt_get_max_irq(struct pci_dev *pdev)
+{
+       u16 ctrl;
+
+       if (!pdev->msix_cap)
+               return 1;
+
+       pci_read_config_word(pdev, pdev->msix_cap + PCI_MSIX_FLAGS, &ctrl);
+       return (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
+}
+
+void bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx)
+{
+       int max_rings = 0;
+
+       if (BNXT_PF(bp)) {
+               *max_tx = bp->pf.max_pf_tx_rings;
+               *max_rx = bp->pf.max_pf_rx_rings;
+               max_rings = min_t(int, bp->pf.max_irqs, bp->pf.max_cp_rings);
+               max_rings = min_t(int, max_rings, bp->pf.max_stat_ctxs);
+       } else {
+#ifdef CONFIG_BNXT_SRIOV
+               *max_tx = bp->vf.max_tx_rings;
+               *max_rx = bp->vf.max_rx_rings;
+               max_rings = min_t(int, bp->vf.max_irqs, bp->vf.max_cp_rings);
+               max_rings = min_t(int, max_rings, bp->vf.max_stat_ctxs);
+#endif
+       }
+       if (bp->flags & BNXT_FLAG_AGG_RINGS)
+               *max_rx >>= 1;
+
+       *max_rx = min_t(int, *max_rx, max_rings);
+       *max_tx = min_t(int, *max_tx, max_rings);
+}
+
+static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       static int version_printed;
+       struct net_device *dev;
+       struct bnxt *bp;
+       int rc, max_rx_rings, max_tx_rings, max_irqs, dflt_rings;
+
+       if (version_printed++ == 0)
+               pr_info("%s", version);
+
+       max_irqs = bnxt_get_max_irq(pdev);
+       dev = alloc_etherdev_mq(sizeof(*bp), max_irqs);
+       if (!dev)
+               return -ENOMEM;
+
+       bp = netdev_priv(dev);
+
+       if (bnxt_vf_pciid(ent->driver_data))
+               bp->flags |= BNXT_FLAG_VF;
+
+       if (pdev->msix_cap) {
+               bp->flags |= BNXT_FLAG_MSIX_CAP;
+               if (BNXT_PF(bp))
+                       bp->flags |= BNXT_FLAG_RFS;
+       }
+
+       rc = bnxt_init_board(pdev, dev);
+       if (rc < 0)
+               goto init_err_free;
+
+       dev->netdev_ops = &bnxt_netdev_ops;
+       dev->watchdog_timeo = BNXT_TX_TIMEOUT;
+       dev->ethtool_ops = &bnxt_ethtool_ops;
+
+       pci_set_drvdata(pdev, dev);
+
+       dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG |
+                          NETIF_F_TSO | NETIF_F_TSO6 |
+                          NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE |
+                          NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT |
+                          NETIF_F_RXHASH |
+                          NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_GRO;
+
+       if (bp->flags & BNXT_FLAG_RFS)
+               dev->hw_features |= NETIF_F_NTUPLE;
+
+       dev->hw_enc_features =
+                       NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG |
+                       NETIF_F_TSO | NETIF_F_TSO6 |
+                       NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE |
+                       NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT;
+       dev->vlan_features = dev->hw_features | NETIF_F_HIGHDMA;
+       dev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX |
+                           NETIF_F_HW_VLAN_STAG_RX | NETIF_F_HW_VLAN_STAG_TX;
+       dev->features |= dev->hw_features | NETIF_F_HIGHDMA;
+       dev->priv_flags |= IFF_UNICAST_FLT;
+
+#ifdef CONFIG_BNXT_SRIOV
+       init_waitqueue_head(&bp->sriov_cfg_wait);
+#endif
+       rc = bnxt_alloc_hwrm_resources(bp);
+       if (rc)
+               goto init_err;
+
+       mutex_init(&bp->hwrm_cmd_lock);
+       bnxt_hwrm_ver_get(bp);
+
+       rc = bnxt_hwrm_func_drv_rgtr(bp);
+       if (rc)
+               goto init_err;
+
+       /* Get the MAX capabilities for this function */
+       rc = bnxt_hwrm_func_qcaps(bp);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm query capability failure rc: %x\n",
+                          rc);
+               rc = -1;
+               goto init_err;
+       }
+
+       rc = bnxt_hwrm_queue_qportcfg(bp);
+       if (rc) {
+               netdev_err(bp->dev, "hwrm query qportcfg failure rc: %x\n",
+                          rc);
+               rc = -1;
+               goto init_err;
+       }
+
+       bnxt_set_tpa_flags(bp);
+       bnxt_set_ring_params(bp);
+       dflt_rings = netif_get_num_default_rss_queues();
+       if (BNXT_PF(bp)) {
+               memcpy(dev->dev_addr, bp->pf.mac_addr, ETH_ALEN);
+               bp->pf.max_irqs = max_irqs;
+       } else {
+#if defined(CONFIG_BNXT_SRIOV)
+               memcpy(dev->dev_addr, bp->vf.mac_addr, ETH_ALEN);
+               bp->vf.max_irqs = max_irqs;
+#endif
+       }
+       bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
+       bp->rx_nr_rings = min_t(int, dflt_rings, max_rx_rings);
+       bp->tx_nr_rings_per_tc = min_t(int, dflt_rings, max_tx_rings);
+       bp->tx_nr_rings = bp->tx_nr_rings_per_tc;
+       bp->cp_nr_rings = max_t(int, bp->rx_nr_rings, bp->tx_nr_rings);
+       bp->num_stat_ctxs = bp->cp_nr_rings;
+
+       if (dev->hw_features & NETIF_F_HW_VLAN_CTAG_RX)
+               bp->flags |= BNXT_FLAG_STRIP_VLAN;
+
+       rc = bnxt_probe_phy(bp);
+       if (rc)
+               goto init_err;
+
+       rc = register_netdev(dev);
+       if (rc)
+               goto init_err;
+
+       netdev_info(dev, "%s found at mem %lx, node addr %pM\n",
+                   board_info[ent->driver_data].name,
+                   (long)pci_resource_start(pdev, 0), dev->dev_addr);
+
+       return 0;
+
+init_err:
+       pci_iounmap(pdev, bp->bar0);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+
+init_err_free:
+       free_netdev(dev);
+       return rc;
+}
+
+static struct pci_driver bnxt_pci_driver = {
+       .name           = DRV_MODULE_NAME,
+       .id_table       = bnxt_pci_tbl,
+       .probe          = bnxt_init_one,
+       .remove         = bnxt_remove_one,
+#if defined(CONFIG_BNXT_SRIOV)
+       .sriov_configure = bnxt_sriov_configure,
+#endif
+};
+
+module_pci_driver(bnxt_pci_driver);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
new file mode 100644 (file)
index 0000000..4f2267c
--- /dev/null
@@ -0,0 +1,1086 @@
+/* Broadcom NetXtreme-C/E network driver.
+ *
+ * Copyright (c) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ */
+
+#ifndef BNXT_H
+#define BNXT_H
+
+#define DRV_MODULE_NAME                "bnxt_en"
+#define DRV_MODULE_VERSION     "0.1.24"
+
+#define DRV_VER_MAJ    0
+#define DRV_VER_MIN    1
+#define DRV_VER_UPD    24
+
+struct tx_bd {
+       __le32 tx_bd_len_flags_type;
+       #define TX_BD_TYPE                                      (0x3f << 0)
+        #define TX_BD_TYPE_SHORT_TX_BD                          (0x00 << 0)
+        #define TX_BD_TYPE_LONG_TX_BD                           (0x10 << 0)
+       #define TX_BD_FLAGS_PACKET_END                          (1 << 6)
+       #define TX_BD_FLAGS_NO_CMPL                             (1 << 7)
+       #define TX_BD_FLAGS_BD_CNT                              (0x1f << 8)
+        #define TX_BD_FLAGS_BD_CNT_SHIFT                        8
+       #define TX_BD_FLAGS_LHINT                               (3 << 13)
+        #define TX_BD_FLAGS_LHINT_SHIFT                         13
+        #define TX_BD_FLAGS_LHINT_512_AND_SMALLER               (0 << 13)
+        #define TX_BD_FLAGS_LHINT_512_TO_1023                   (1 << 13)
+        #define TX_BD_FLAGS_LHINT_1024_TO_2047                  (2 << 13)
+        #define TX_BD_FLAGS_LHINT_2048_AND_LARGER               (3 << 13)
+       #define TX_BD_FLAGS_COAL_NOW                            (1 << 15)
+       #define TX_BD_LEN                                       (0xffff << 16)
+        #define TX_BD_LEN_SHIFT                                 16
+
+       u32 tx_bd_opaque;
+       __le64 tx_bd_haddr;
+} __packed;
+
+struct tx_bd_ext {
+       __le32 tx_bd_hsize_lflags;
+       #define TX_BD_FLAGS_TCP_UDP_CHKSUM                      (1 << 0)
+       #define TX_BD_FLAGS_IP_CKSUM                            (1 << 1)
+       #define TX_BD_FLAGS_NO_CRC                              (1 << 2)
+       #define TX_BD_FLAGS_STAMP                               (1 << 3)
+       #define TX_BD_FLAGS_T_IP_CHKSUM                         (1 << 4)
+       #define TX_BD_FLAGS_LSO                                 (1 << 5)
+       #define TX_BD_FLAGS_IPID_FMT                            (1 << 6)
+       #define TX_BD_FLAGS_T_IPID                              (1 << 7)
+       #define TX_BD_HSIZE                                     (0xff << 16)
+        #define TX_BD_HSIZE_SHIFT                               16
+
+       __le32 tx_bd_mss;
+       __le32 tx_bd_cfa_action;
+       #define TX_BD_CFA_ACTION                                (0xffff << 16)
+        #define TX_BD_CFA_ACTION_SHIFT                          16
+
+       __le32 tx_bd_cfa_meta;
+       #define TX_BD_CFA_META_MASK                             0xfffffff
+       #define TX_BD_CFA_META_VID_MASK                         0xfff
+       #define TX_BD_CFA_META_PRI_MASK                         (0xf << 12)
+        #define TX_BD_CFA_META_PRI_SHIFT                        12
+       #define TX_BD_CFA_META_TPID_MASK                        (3 << 16)
+        #define TX_BD_CFA_META_TPID_SHIFT                       16
+       #define TX_BD_CFA_META_KEY                              (0xf << 28)
+        #define TX_BD_CFA_META_KEY_SHIFT                        28
+       #define TX_BD_CFA_META_KEY_VLAN                         (1 << 28)
+};
+
+struct rx_bd {
+       __le32 rx_bd_len_flags_type;
+       #define RX_BD_TYPE                                      (0x3f << 0)
+        #define RX_BD_TYPE_RX_PACKET_BD                         0x4
+        #define RX_BD_TYPE_RX_BUFFER_BD                         0x5
+        #define RX_BD_TYPE_RX_AGG_BD                            0x6
+        #define RX_BD_TYPE_16B_BD_SIZE                          (0 << 4)
+        #define RX_BD_TYPE_32B_BD_SIZE                          (1 << 4)
+        #define RX_BD_TYPE_48B_BD_SIZE                          (2 << 4)
+        #define RX_BD_TYPE_64B_BD_SIZE                          (3 << 4)
+       #define RX_BD_FLAGS_SOP                                 (1 << 6)
+       #define RX_BD_FLAGS_EOP                                 (1 << 7)
+       #define RX_BD_FLAGS_BUFFERS                             (3 << 8)
+        #define RX_BD_FLAGS_1_BUFFER_PACKET                     (0 << 8)
+        #define RX_BD_FLAGS_2_BUFFER_PACKET                     (1 << 8)
+        #define RX_BD_FLAGS_3_BUFFER_PACKET                     (2 << 8)
+        #define RX_BD_FLAGS_4_BUFFER_PACKET                     (3 << 8)
+       #define RX_BD_LEN                                       (0xffff << 16)
+        #define RX_BD_LEN_SHIFT                                 16
+
+       u32 rx_bd_opaque;
+       __le64 rx_bd_haddr;
+};
+
+struct tx_cmp {
+       __le32 tx_cmp_flags_type;
+       #define CMP_TYPE                                        (0x3f << 0)
+        #define CMP_TYPE_TX_L2_CMP                              0
+        #define CMP_TYPE_RX_L2_CMP                              17
+        #define CMP_TYPE_RX_AGG_CMP                             18
+        #define CMP_TYPE_RX_L2_TPA_START_CMP                    19
+        #define CMP_TYPE_RX_L2_TPA_END_CMP                      21
+        #define CMP_TYPE_STATUS_CMP                             32
+        #define CMP_TYPE_REMOTE_DRIVER_REQ                      34
+        #define CMP_TYPE_REMOTE_DRIVER_RESP                     36
+        #define CMP_TYPE_ERROR_STATUS                           48
+        #define CMPL_BASE_TYPE_STAT_EJECT                       (0x1aUL << 0)
+        #define CMPL_BASE_TYPE_HWRM_DONE                        (0x20UL << 0)
+        #define CMPL_BASE_TYPE_HWRM_FWD_REQ                     (0x22UL << 0)
+        #define CMPL_BASE_TYPE_HWRM_FWD_RESP                    (0x24UL << 0)
+        #define CMPL_BASE_TYPE_HWRM_ASYNC_EVENT                 (0x2eUL << 0)
+
+       #define TX_CMP_FLAGS_ERROR                              (1 << 6)
+       #define TX_CMP_FLAGS_PUSH                               (1 << 7)
+
+       u32 tx_cmp_opaque;
+       __le32 tx_cmp_errors_v;
+       #define TX_CMP_V                                        (1 << 0)
+       #define TX_CMP_ERRORS_BUFFER_ERROR                      (7 << 1)
+        #define TX_CMP_ERRORS_BUFFER_ERROR_NO_ERROR             0
+        #define TX_CMP_ERRORS_BUFFER_ERROR_BAD_FORMAT           2
+        #define TX_CMP_ERRORS_BUFFER_ERROR_INVALID_STAG         4
+        #define TX_CMP_ERRORS_BUFFER_ERROR_STAG_BOUNDS          5
+        #define TX_CMP_ERRORS_ZERO_LENGTH_PKT                   (1 << 4)
+        #define TX_CMP_ERRORS_EXCESSIVE_BD_LEN                  (1 << 5)
+        #define TX_CMP_ERRORS_DMA_ERROR                         (1 << 6)
+        #define TX_CMP_ERRORS_HINT_TOO_SHORT                    (1 << 7)
+
+       __le32 tx_cmp_unsed_3;
+};
+
+struct rx_cmp {
+       __le32 rx_cmp_len_flags_type;
+       #define RX_CMP_CMP_TYPE                                 (0x3f << 0)
+       #define RX_CMP_FLAGS_ERROR                              (1 << 6)
+       #define RX_CMP_FLAGS_PLACEMENT                          (7 << 7)
+       #define RX_CMP_FLAGS_RSS_VALID                          (1 << 10)
+       #define RX_CMP_FLAGS_UNUSED                             (1 << 11)
+        #define RX_CMP_FLAGS_ITYPES_SHIFT                       12
+        #define RX_CMP_FLAGS_ITYPE_UNKNOWN                      (0 << 12)
+        #define RX_CMP_FLAGS_ITYPE_IP                           (1 << 12)
+        #define RX_CMP_FLAGS_ITYPE_TCP                          (2 << 12)
+        #define RX_CMP_FLAGS_ITYPE_UDP                          (3 << 12)
+        #define RX_CMP_FLAGS_ITYPE_FCOE                         (4 << 12)
+        #define RX_CMP_FLAGS_ITYPE_ROCE                         (5 << 12)
+        #define RX_CMP_FLAGS_ITYPE_PTP_WO_TS                    (8 << 12)
+        #define RX_CMP_FLAGS_ITYPE_PTP_W_TS                     (9 << 12)
+       #define RX_CMP_LEN                                      (0xffff << 16)
+        #define RX_CMP_LEN_SHIFT                                16
+
+       u32 rx_cmp_opaque;
+       __le32 rx_cmp_misc_v1;
+       #define RX_CMP_V1                                       (1 << 0)
+       #define RX_CMP_AGG_BUFS                                 (0x1f << 1)
+        #define RX_CMP_AGG_BUFS_SHIFT                           1
+       #define RX_CMP_RSS_HASH_TYPE                            (0x7f << 9)
+        #define RX_CMP_RSS_HASH_TYPE_SHIFT                      9
+       #define RX_CMP_PAYLOAD_OFFSET                           (0xff << 16)
+        #define RX_CMP_PAYLOAD_OFFSET_SHIFT                     16
+
+       __le32 rx_cmp_rss_hash;
+};
+
+#define RX_CMP_HASH_VALID(rxcmp)                               \
+       ((rxcmp)->rx_cmp_len_flags_type & cpu_to_le32(RX_CMP_FLAGS_RSS_VALID))
+
+#define RX_CMP_HASH_TYPE(rxcmp)                                        \
+       ((le32_to_cpu((rxcmp)->rx_cmp_misc_v1) & RX_CMP_RSS_HASH_TYPE) >>\
+        RX_CMP_RSS_HASH_TYPE_SHIFT)
+
+struct rx_cmp_ext {
+       __le32 rx_cmp_flags2;
+       #define RX_CMP_FLAGS2_IP_CS_CALC                        0x1
+       #define RX_CMP_FLAGS2_L4_CS_CALC                        (0x1 << 1)
+       #define RX_CMP_FLAGS2_T_IP_CS_CALC                      (0x1 << 2)
+       #define RX_CMP_FLAGS2_T_L4_CS_CALC                      (0x1 << 3)
+       #define RX_CMP_FLAGS2_META_FORMAT_VLAN                  (0x1 << 4)
+       __le32 rx_cmp_meta_data;
+       #define RX_CMP_FLAGS2_METADATA_VID_MASK                 0xfff
+       #define RX_CMP_FLAGS2_METADATA_TPID_MASK                0xffff0000
+        #define RX_CMP_FLAGS2_METADATA_TPID_SFT                 16
+       __le32 rx_cmp_cfa_code_errors_v2;
+       #define RX_CMP_V                                        (1 << 0)
+       #define RX_CMPL_ERRORS_MASK                             (0x7fff << 1)
+        #define RX_CMPL_ERRORS_SFT                              1
+       #define RX_CMPL_ERRORS_BUFFER_ERROR_MASK                (0x7 << 1)
+        #define RX_CMPL_ERRORS_BUFFER_ERROR_NO_BUFFER           (0x0 << 1)
+        #define RX_CMPL_ERRORS_BUFFER_ERROR_DID_NOT_FIT         (0x1 << 1)
+        #define RX_CMPL_ERRORS_BUFFER_ERROR_NOT_ON_CHIP         (0x2 << 1)
+        #define RX_CMPL_ERRORS_BUFFER_ERROR_BAD_FORMAT          (0x3 << 1)
+       #define RX_CMPL_ERRORS_IP_CS_ERROR                      (0x1 << 4)
+       #define RX_CMPL_ERRORS_L4_CS_ERROR                      (0x1 << 5)
+       #define RX_CMPL_ERRORS_T_IP_CS_ERROR                    (0x1 << 6)
+       #define RX_CMPL_ERRORS_T_L4_CS_ERROR                    (0x1 << 7)
+       #define RX_CMPL_ERRORS_CRC_ERROR                        (0x1 << 8)
+       #define RX_CMPL_ERRORS_T_PKT_ERROR_MASK                 (0x7 << 9)
+        #define RX_CMPL_ERRORS_T_PKT_ERROR_NO_ERROR             (0x0 << 9)
+        #define RX_CMPL_ERRORS_T_PKT_ERROR_T_L3_BAD_VERSION     (0x1 << 9)
+        #define RX_CMPL_ERRORS_T_PKT_ERROR_T_L3_BAD_HDR_LEN     (0x2 << 9)
+        #define RX_CMPL_ERRORS_T_PKT_ERROR_TUNNEL_TOTAL_ERROR   (0x3 << 9)
+        #define RX_CMPL_ERRORS_T_PKT_ERROR_T_IP_TOTAL_ERROR     (0x4 << 9)
+        #define RX_CMPL_ERRORS_T_PKT_ERROR_T_UDP_TOTAL_ERROR    (0x5 << 9)
+        #define RX_CMPL_ERRORS_T_PKT_ERROR_T_L3_BAD_TTL         (0x6 << 9)
+       #define RX_CMPL_ERRORS_PKT_ERROR_MASK                   (0xf << 12)
+        #define RX_CMPL_ERRORS_PKT_ERROR_NO_ERROR               (0x0 << 12)
+        #define RX_CMPL_ERRORS_PKT_ERROR_L3_BAD_VERSION         (0x1 << 12)
+        #define RX_CMPL_ERRORS_PKT_ERROR_L3_BAD_HDR_LEN         (0x2 << 12)
+        #define RX_CMPL_ERRORS_PKT_ERROR_L3_BAD_TTL             (0x3 << 12)
+        #define RX_CMPL_ERRORS_PKT_ERROR_IP_TOTAL_ERROR         (0x4 << 12)
+        #define RX_CMPL_ERRORS_PKT_ERROR_UDP_TOTAL_ERROR        (0x5 << 12)
+        #define RX_CMPL_ERRORS_PKT_ERROR_L4_BAD_HDR_LEN         (0x6 << 12)
+        #define RX_CMPL_ERRORS_PKT_ERROR_L4_BAD_HDR_LEN_TOO_SMALL (0x7 << 12)
+        #define RX_CMPL_ERRORS_PKT_ERROR_L4_BAD_OPT_LEN         (0x8 << 12)
+
+       #define RX_CMPL_CFA_CODE_MASK                           (0xffff << 16)
+        #define RX_CMPL_CFA_CODE_SFT                            16
+
+       __le32 rx_cmp_unused3;
+};
+
+#define RX_CMP_L2_ERRORS                                               \
+       cpu_to_le32(RX_CMPL_ERRORS_BUFFER_ERROR_MASK | RX_CMPL_ERRORS_CRC_ERROR)
+
+#define RX_CMP_L4_CS_BITS                                              \
+       (cpu_to_le32(RX_CMP_FLAGS2_L4_CS_CALC | RX_CMP_FLAGS2_T_L4_CS_CALC))
+
+#define RX_CMP_L4_CS_ERR_BITS                                          \
+       (cpu_to_le32(RX_CMPL_ERRORS_L4_CS_ERROR | RX_CMPL_ERRORS_T_L4_CS_ERROR))
+
+#define RX_CMP_L4_CS_OK(rxcmp1)                                                \
+           (((rxcmp1)->rx_cmp_flags2 & RX_CMP_L4_CS_BITS) &&           \
+            !((rxcmp1)->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS))
+
+#define RX_CMP_ENCAP(rxcmp1)                                           \
+           ((le32_to_cpu((rxcmp1)->rx_cmp_flags2) &                    \
+            RX_CMP_FLAGS2_T_L4_CS_CALC) >> 3)
+
+struct rx_agg_cmp {
+       __le32 rx_agg_cmp_len_flags_type;
+       #define RX_AGG_CMP_TYPE                                 (0x3f << 0)
+       #define RX_AGG_CMP_LEN                                  (0xffff << 16)
+        #define RX_AGG_CMP_LEN_SHIFT                            16
+       u32 rx_agg_cmp_opaque;
+       __le32 rx_agg_cmp_v;
+       #define RX_AGG_CMP_V                                    (1 << 0)
+       __le32 rx_agg_cmp_unused;
+};
+
+struct rx_tpa_start_cmp {
+       __le32 rx_tpa_start_cmp_len_flags_type;
+       #define RX_TPA_START_CMP_TYPE                           (0x3f << 0)
+       #define RX_TPA_START_CMP_FLAGS                          (0x3ff << 6)
+        #define RX_TPA_START_CMP_FLAGS_SHIFT                    6
+       #define RX_TPA_START_CMP_FLAGS_PLACEMENT                (0x7 << 7)
+        #define RX_TPA_START_CMP_FLAGS_PLACEMENT_SHIFT          7
+        #define RX_TPA_START_CMP_FLAGS_PLACEMENT_JUMBO          (0x1 << 7)
+        #define RX_TPA_START_CMP_FLAGS_PLACEMENT_HDS            (0x2 << 7)
+        #define RX_TPA_START_CMP_FLAGS_PLACEMENT_GRO_JUMBO      (0x5 << 7)
+        #define RX_TPA_START_CMP_FLAGS_PLACEMENT_GRO_HDS        (0x6 << 7)
+       #define RX_TPA_START_CMP_FLAGS_RSS_VALID                (0x1 << 10)
+       #define RX_TPA_START_CMP_FLAGS_ITYPES                   (0xf << 12)
+        #define RX_TPA_START_CMP_FLAGS_ITYPES_SHIFT             12
+        #define RX_TPA_START_CMP_FLAGS_ITYPE_TCP                (0x2 << 12)
+       #define RX_TPA_START_CMP_LEN                            (0xffff << 16)
+        #define RX_TPA_START_CMP_LEN_SHIFT                      16
+
+       u32 rx_tpa_start_cmp_opaque;
+       __le32 rx_tpa_start_cmp_misc_v1;
+       #define RX_TPA_START_CMP_V1                             (0x1 << 0)
+       #define RX_TPA_START_CMP_RSS_HASH_TYPE                  (0x7f << 9)
+        #define RX_TPA_START_CMP_RSS_HASH_TYPE_SHIFT            9
+       #define RX_TPA_START_CMP_AGG_ID                         (0x7f << 25)
+        #define RX_TPA_START_CMP_AGG_ID_SHIFT                   25
+
+       __le32 rx_tpa_start_cmp_rss_hash;
+};
+
+#define TPA_START_HASH_VALID(rx_tpa_start)                             \
+       ((rx_tpa_start)->rx_tpa_start_cmp_len_flags_type &              \
+        cpu_to_le32(RX_TPA_START_CMP_FLAGS_RSS_VALID))
+
+#define TPA_START_HASH_TYPE(rx_tpa_start)                              \
+       ((le32_to_cpu((rx_tpa_start)->rx_tpa_start_cmp_misc_v1) &       \
+         RX_TPA_START_CMP_RSS_HASH_TYPE) >>                            \
+        RX_TPA_START_CMP_RSS_HASH_TYPE_SHIFT)
+
+#define TPA_START_AGG_ID(rx_tpa_start)                                 \
+       ((le32_to_cpu((rx_tpa_start)->rx_tpa_start_cmp_misc_v1) &       \
+        RX_TPA_START_CMP_AGG_ID) >> RX_TPA_START_CMP_AGG_ID_SHIFT)
+
+struct rx_tpa_start_cmp_ext {
+       __le32 rx_tpa_start_cmp_flags2;
+       #define RX_TPA_START_CMP_FLAGS2_IP_CS_CALC              (0x1 << 0)
+       #define RX_TPA_START_CMP_FLAGS2_L4_CS_CALC              (0x1 << 1)
+       #define RX_TPA_START_CMP_FLAGS2_T_IP_CS_CALC            (0x1 << 2)
+       #define RX_TPA_START_CMP_FLAGS2_T_L4_CS_CALC            (0x1 << 3)
+
+       __le32 rx_tpa_start_cmp_metadata;
+       __le32 rx_tpa_start_cmp_cfa_code_v2;
+       #define RX_TPA_START_CMP_V2                             (0x1 << 0)
+       #define RX_TPA_START_CMP_CFA_CODE                       (0xffff << 16)
+        #define RX_TPA_START_CMPL_CFA_CODE_SHIFT                16
+       __le32 rx_tpa_start_cmp_unused5;
+};
+
+struct rx_tpa_end_cmp {
+       __le32 rx_tpa_end_cmp_len_flags_type;
+       #define RX_TPA_END_CMP_TYPE                             (0x3f << 0)
+       #define RX_TPA_END_CMP_FLAGS                            (0x3ff << 6)
+        #define RX_TPA_END_CMP_FLAGS_SHIFT                      6
+       #define RX_TPA_END_CMP_FLAGS_PLACEMENT                  (0x7 << 7)
+        #define RX_TPA_END_CMP_FLAGS_PLACEMENT_SHIFT            7
+        #define RX_TPA_END_CMP_FLAGS_PLACEMENT_JUMBO            (0x1 << 7)
+        #define RX_TPA_END_CMP_FLAGS_PLACEMENT_HDS              (0x2 << 7)
+        #define RX_TPA_END_CMP_FLAGS_PLACEMENT_GRO_JUMBO        (0x5 << 7)
+        #define RX_TPA_END_CMP_FLAGS_PLACEMENT_GRO_HDS          (0x6 << 7)
+       #define RX_TPA_END_CMP_FLAGS_RSS_VALID                  (0x1 << 10)
+       #define RX_TPA_END_CMP_FLAGS_ITYPES                     (0xf << 12)
+        #define RX_TPA_END_CMP_FLAGS_ITYPES_SHIFT               12
+        #define RX_TPA_END_CMP_FLAGS_ITYPE_TCP                  (0x2 << 12)
+       #define RX_TPA_END_CMP_LEN                              (0xffff << 16)
+        #define RX_TPA_END_CMP_LEN_SHIFT                        16
+
+       u32 rx_tpa_end_cmp_opaque;
+       __le32 rx_tpa_end_cmp_misc_v1;
+       #define RX_TPA_END_CMP_V1                               (0x1 << 0)
+       #define RX_TPA_END_CMP_AGG_BUFS                         (0x3f << 1)
+        #define RX_TPA_END_CMP_AGG_BUFS_SHIFT                   1
+       #define RX_TPA_END_CMP_TPA_SEGS                         (0xff << 8)
+        #define RX_TPA_END_CMP_TPA_SEGS_SHIFT                   8
+       #define RX_TPA_END_CMP_PAYLOAD_OFFSET                   (0xff << 16)
+        #define RX_TPA_END_CMP_PAYLOAD_OFFSET_SHIFT             16
+       #define RX_TPA_END_CMP_AGG_ID                           (0x7f << 25)
+        #define RX_TPA_END_CMP_AGG_ID_SHIFT                     25
+
+       __le32 rx_tpa_end_cmp_tsdelta;
+       #define RX_TPA_END_GRO_TS                               (0x1 << 31)
+};
+
+#define TPA_END_AGG_ID(rx_tpa_end)                                     \
+       ((le32_to_cpu((rx_tpa_end)->rx_tpa_end_cmp_misc_v1) &           \
+        RX_TPA_END_CMP_AGG_ID) >> RX_TPA_END_CMP_AGG_ID_SHIFT)
+
+#define TPA_END_TPA_SEGS(rx_tpa_end)                                   \
+       ((le32_to_cpu((rx_tpa_end)->rx_tpa_end_cmp_misc_v1) &           \
+        RX_TPA_END_CMP_TPA_SEGS) >> RX_TPA_END_CMP_TPA_SEGS_SHIFT)
+
+#define RX_TPA_END_CMP_FLAGS_PLACEMENT_ANY_GRO                         \
+       cpu_to_le32(RX_TPA_END_CMP_FLAGS_PLACEMENT_GRO_JUMBO &          \
+                   RX_TPA_END_CMP_FLAGS_PLACEMENT_GRO_HDS)
+
+#define TPA_END_GRO(rx_tpa_end)                                                \
+       ((rx_tpa_end)->rx_tpa_end_cmp_len_flags_type &                  \
+        RX_TPA_END_CMP_FLAGS_PLACEMENT_ANY_GRO)
+
+#define TPA_END_GRO_TS(rx_tpa_end)                                     \
+       ((rx_tpa_end)->rx_tpa_end_cmp_tsdelta & cpu_to_le32(RX_TPA_END_GRO_TS))
+
+struct rx_tpa_end_cmp_ext {
+       __le32 rx_tpa_end_cmp_dup_acks;
+       #define RX_TPA_END_CMP_TPA_DUP_ACKS                     (0xf << 0)
+
+       __le32 rx_tpa_end_cmp_seg_len;
+       #define RX_TPA_END_CMP_TPA_SEG_LEN                      (0xffff << 0)
+
+       __le32 rx_tpa_end_cmp_errors_v2;
+       #define RX_TPA_END_CMP_V2                               (0x1 << 0)
+       #define RX_TPA_END_CMP_ERRORS                           (0x7fff << 1)
+       #define RX_TPA_END_CMPL_ERRORS_SHIFT                     1
+
+       u32 rx_tpa_end_cmp_start_opaque;
+};
+
+#define DB_IDX_MASK                                            0xffffff
+#define DB_IDX_VALID                                           (0x1 << 26)
+#define DB_IRQ_DIS                                             (0x1 << 27)
+#define DB_KEY_TX                                              (0x0 << 28)
+#define DB_KEY_RX                                              (0x1 << 28)
+#define DB_KEY_CP                                              (0x2 << 28)
+#define DB_KEY_ST                                              (0x3 << 28)
+#define DB_KEY_TX_PUSH                                         (0x4 << 28)
+#define DB_LONG_TX_PUSH                                                (0x2 << 24)
+
+#define INVALID_HW_RING_ID     ((u16)-1)
+
+#define BNXT_RSS_HASH_TYPE_FLAG_IPV4           0x01
+#define BNXT_RSS_HASH_TYPE_FLAG_TCP_IPV4       0x02
+#define BNXT_RSS_HASH_TYPE_FLAG_IPV6           0x04
+#define BNXT_RSS_HASH_TYPE_FLAG_TCP_IPV6       0x08
+
+/* The hardware supports certain page sizes.  Use the supported page sizes
+ * to allocate the rings.
+ */
+#if (PAGE_SHIFT < 12)
+#define BNXT_PAGE_SHIFT        12
+#elif (PAGE_SHIFT <= 13)
+#define BNXT_PAGE_SHIFT        PAGE_SHIFT
+#elif (PAGE_SHIFT < 16)
+#define BNXT_PAGE_SHIFT        13
+#else
+#define BNXT_PAGE_SHIFT        16
+#endif
+
+#define BNXT_PAGE_SIZE (1 << BNXT_PAGE_SHIFT)
+
+#define BNXT_MIN_PKT_SIZE      45
+
+#define BNXT_NUM_TESTS(bp)     0
+
+#define BNXT_DEFAULT_RX_RING_SIZE      1023
+#define BNXT_DEFAULT_TX_RING_SIZE      512
+
+#define MAX_TPA                64
+
+#define MAX_RX_PAGES   8
+#define MAX_RX_AGG_PAGES       32
+#define MAX_TX_PAGES   8
+#define MAX_CP_PAGES   64
+
+#define RX_DESC_CNT (BNXT_PAGE_SIZE / sizeof(struct rx_bd))
+#define TX_DESC_CNT (BNXT_PAGE_SIZE / sizeof(struct tx_bd))
+#define CP_DESC_CNT (BNXT_PAGE_SIZE / sizeof(struct tx_cmp))
+
+#define SW_RXBD_RING_SIZE (sizeof(struct bnxt_sw_rx_bd) * RX_DESC_CNT)
+#define HW_RXBD_RING_SIZE (sizeof(struct rx_bd) * RX_DESC_CNT)
+
+#define SW_RXBD_AGG_RING_SIZE (sizeof(struct bnxt_sw_rx_agg_bd) * RX_DESC_CNT)
+
+#define SW_TXBD_RING_SIZE (sizeof(struct bnxt_sw_tx_bd) * TX_DESC_CNT)
+#define HW_TXBD_RING_SIZE (sizeof(struct tx_bd) * TX_DESC_CNT)
+
+#define HW_CMPD_RING_SIZE (sizeof(struct tx_cmp) * CP_DESC_CNT)
+
+#define BNXT_MAX_RX_DESC_CNT           (RX_DESC_CNT * MAX_RX_PAGES - 1)
+#define BNXT_MAX_RX_JUM_DESC_CNT       (RX_DESC_CNT * MAX_RX_AGG_PAGES - 1)
+#define BNXT_MAX_TX_DESC_CNT           (TX_DESC_CNT * MAX_TX_PAGES - 1)
+
+#define RX_RING(x)     (((x) & ~(RX_DESC_CNT - 1)) >> (BNXT_PAGE_SHIFT - 4))
+#define RX_IDX(x)      ((x) & (RX_DESC_CNT - 1))
+
+#define TX_RING(x)     (((x) & ~(TX_DESC_CNT - 1)) >> (BNXT_PAGE_SHIFT - 4))
+#define TX_IDX(x)      ((x) & (TX_DESC_CNT - 1))
+
+#define CP_RING(x)     (((x) & ~(CP_DESC_CNT - 1)) >> (BNXT_PAGE_SHIFT - 4))
+#define CP_IDX(x)      ((x) & (CP_DESC_CNT - 1))
+
+#define TX_CMP_VALID(txcmp, raw_cons)                                  \
+       (!!((txcmp)->tx_cmp_errors_v & cpu_to_le32(TX_CMP_V)) ==        \
+        !((raw_cons) & bp->cp_bit))
+
+#define RX_CMP_VALID(rxcmp1, raw_cons)                                 \
+       (!!((rxcmp1)->rx_cmp_cfa_code_errors_v2 & cpu_to_le32(RX_CMP_V)) ==\
+        !((raw_cons) & bp->cp_bit))
+
+#define RX_AGG_CMP_VALID(agg, raw_cons)                                \
+       (!!((agg)->rx_agg_cmp_v & cpu_to_le32(RX_AGG_CMP_V)) == \
+        !((raw_cons) & bp->cp_bit))
+
+#define TX_CMP_TYPE(txcmp)                                     \
+       (le32_to_cpu((txcmp)->tx_cmp_flags_type) & CMP_TYPE)
+
+#define RX_CMP_TYPE(rxcmp)                                     \
+       (le32_to_cpu((rxcmp)->rx_cmp_len_flags_type) & RX_CMP_CMP_TYPE)
+
+#define NEXT_RX(idx)           (((idx) + 1) & bp->rx_ring_mask)
+
+#define NEXT_RX_AGG(idx)       (((idx) + 1) & bp->rx_agg_ring_mask)
+
+#define NEXT_TX(idx)           (((idx) + 1) & bp->tx_ring_mask)
+
+#define ADV_RAW_CMP(idx, n)    ((idx) + (n))
+#define NEXT_RAW_CMP(idx)      ADV_RAW_CMP(idx, 1)
+#define RING_CMP(idx)          ((idx) & bp->cp_ring_mask)
+#define NEXT_CMP(idx)          RING_CMP(ADV_RAW_CMP(idx, 1))
+
+#define HWRM_CMD_TIMEOUT               500
+#define HWRM_RESET_TIMEOUT             ((HWRM_CMD_TIMEOUT) * 4)
+#define HWRM_RESP_ERR_CODE_MASK                0xffff
+#define HWRM_RESP_LEN_MASK             0xffff0000
+#define HWRM_RESP_LEN_SFT              16
+#define HWRM_RESP_VALID_MASK           0xff000000
+#define BNXT_HWRM_REQ_MAX_SIZE         128
+#define BNXT_HWRM_REQS_PER_PAGE                (BNXT_PAGE_SIZE /       \
+                                        BNXT_HWRM_REQ_MAX_SIZE)
+
+struct bnxt_sw_tx_bd {
+       struct sk_buff          *skb;
+       DEFINE_DMA_UNMAP_ADDR(mapping);
+       u8                      is_gso;
+       u8                      is_push;
+       unsigned short          nr_frags;
+};
+
+struct bnxt_sw_rx_bd {
+       u8                      *data;
+       DEFINE_DMA_UNMAP_ADDR(mapping);
+};
+
+struct bnxt_sw_rx_agg_bd {
+       struct page             *page;
+       dma_addr_t              mapping;
+};
+
+struct bnxt_ring_struct {
+       int                     nr_pages;
+       int                     page_size;
+       void                    **pg_arr;
+       dma_addr_t              *dma_arr;
+
+       __le64                  *pg_tbl;
+       dma_addr_t              pg_tbl_map;
+
+       int                     vmem_size;
+       void                    **vmem;
+
+       u16                     fw_ring_id; /* Ring id filled by Chimp FW */
+       u8                      queue_id;
+};
+
+struct tx_push_bd {
+       __le32                  doorbell;
+       struct tx_bd            txbd1;
+       struct tx_bd_ext        txbd2;
+};
+
+struct bnxt_tx_ring_info {
+       u16                     tx_prod;
+       u16                     tx_cons;
+       void __iomem            *tx_doorbell;
+
+       struct tx_bd            *tx_desc_ring[MAX_TX_PAGES];
+       struct bnxt_sw_tx_bd    *tx_buf_ring;
+
+       dma_addr_t              tx_desc_mapping[MAX_TX_PAGES];
+
+       struct tx_push_bd       *tx_push;
+       dma_addr_t              tx_push_mapping;
+
+#define BNXT_DEV_STATE_CLOSING 0x1
+       u32                     dev_state;
+
+       struct bnxt_ring_struct tx_ring_struct;
+};
+
+struct bnxt_tpa_info {
+       u8                      *data;
+       dma_addr_t              mapping;
+       u16                     len;
+       unsigned short          gso_type;
+       u32                     flags2;
+       u32                     metadata;
+       enum pkt_hash_types     hash_type;
+       u32                     rss_hash;
+};
+
+struct bnxt_rx_ring_info {
+       u16                     rx_prod;
+       u16                     rx_agg_prod;
+       u16                     rx_sw_agg_prod;
+       void __iomem            *rx_doorbell;
+       void __iomem            *rx_agg_doorbell;
+
+       struct rx_bd            *rx_desc_ring[MAX_RX_PAGES];
+       struct bnxt_sw_rx_bd    *rx_buf_ring;
+
+       struct rx_bd            *rx_agg_desc_ring[MAX_RX_AGG_PAGES];
+       struct bnxt_sw_rx_agg_bd        *rx_agg_ring;
+
+       unsigned long           *rx_agg_bmap;
+       u16                     rx_agg_bmap_size;
+
+       dma_addr_t              rx_desc_mapping[MAX_RX_PAGES];
+       dma_addr_t              rx_agg_desc_mapping[MAX_RX_AGG_PAGES];
+
+       struct bnxt_tpa_info    *rx_tpa;
+
+       struct bnxt_ring_struct rx_ring_struct;
+       struct bnxt_ring_struct rx_agg_ring_struct;
+};
+
+struct bnxt_cp_ring_info {
+       u32                     cp_raw_cons;
+       void __iomem            *cp_doorbell;
+
+       struct tx_cmp           *cp_desc_ring[MAX_CP_PAGES];
+
+       dma_addr_t              cp_desc_mapping[MAX_CP_PAGES];
+
+       struct ctx_hw_stats     *hw_stats;
+       dma_addr_t              hw_stats_map;
+       u32                     hw_stats_ctx_id;
+       u64                     rx_l4_csum_errors;
+
+       struct bnxt_ring_struct cp_ring_struct;
+};
+
+struct bnxt_napi {
+       struct napi_struct      napi;
+       struct bnxt             *bp;
+
+       int                     index;
+       struct bnxt_cp_ring_info        cp_ring;
+       struct bnxt_rx_ring_info        rx_ring;
+       struct bnxt_tx_ring_info        tx_ring;
+
+#ifdef CONFIG_NET_RX_BUSY_POLL
+       atomic_t                poll_state;
+#endif
+};
+
+#ifdef CONFIG_NET_RX_BUSY_POLL
+enum bnxt_poll_state_t {
+       BNXT_STATE_IDLE = 0,
+       BNXT_STATE_NAPI,
+       BNXT_STATE_POLL,
+       BNXT_STATE_DISABLE,
+};
+#endif
+
+struct bnxt_irq {
+       irq_handler_t   handler;
+       unsigned int    vector;
+       u8              requested;
+       char            name[IFNAMSIZ + 2];
+};
+
+#define HWRM_RING_ALLOC_TX     0x1
+#define HWRM_RING_ALLOC_RX     0x2
+#define HWRM_RING_ALLOC_AGG    0x4
+#define HWRM_RING_ALLOC_CMPL   0x8
+
+#define INVALID_STATS_CTX_ID   -1
+
+struct hwrm_cmd_req_hdr {
+#define HWRM_CMPL_RING_MASK    0xffff0000
+#define HWRM_CMPL_RING_SFT     16
+       __le32  cmpl_ring_req_type;
+#define HWRM_SEQ_ID_MASK       0xffff
+#define HWRM_SEQ_ID_INVALID -1
+#define HWRM_RESP_LEN_OFFSET   4
+#define HWRM_TARGET_FID_MASK   0xffff0000
+#define HWRM_TARGET_FID_SFT    16
+       __le32  target_id_seq_id;
+       __le64  resp_addr;
+};
+
+struct bnxt_ring_grp_info {
+       u16     fw_stats_ctx;
+       u16     fw_grp_id;
+       u16     rx_fw_ring_id;
+       u16     agg_fw_ring_id;
+       u16     cp_fw_ring_id;
+};
+
+struct bnxt_vnic_info {
+       u16             fw_vnic_id; /* returned by Chimp during alloc */
+       u16             fw_rss_cos_lb_ctx;
+       u16             fw_l2_ctx_id;
+#define BNXT_MAX_UC_ADDRS      4
+       __le64          fw_l2_filter_id[BNXT_MAX_UC_ADDRS];
+                               /* index 0 always dev_addr */
+       u16             uc_filter_count;
+       u8              *uc_list;
+
+       u16             *fw_grp_ids;
+       u16             hash_type;
+       dma_addr_t      rss_table_dma_addr;
+       __le16          *rss_table;
+       dma_addr_t      rss_hash_key_dma_addr;
+       u64             *rss_hash_key;
+       u32             rx_mask;
+
+       u8              *mc_list;
+       int             mc_list_size;
+       int             mc_list_count;
+       dma_addr_t      mc_list_mapping;
+#define BNXT_MAX_MC_ADDRS      16
+
+       u32             flags;
+#define BNXT_VNIC_RSS_FLAG     1
+#define BNXT_VNIC_RFS_FLAG     2
+#define BNXT_VNIC_MCAST_FLAG   4
+#define BNXT_VNIC_UCAST_FLAG   8
+};
+
+#if defined(CONFIG_BNXT_SRIOV)
+struct bnxt_vf_info {
+       u16     fw_fid;
+       u8      mac_addr[ETH_ALEN];
+       u16     max_rsscos_ctxs;
+       u16     max_cp_rings;
+       u16     max_tx_rings;
+       u16     max_rx_rings;
+       u16     max_l2_ctxs;
+       u16     max_irqs;
+       u16     max_vnics;
+       u16     max_stat_ctxs;
+       u16     vlan;
+       u32     flags;
+#define BNXT_VF_QOS            0x1
+#define BNXT_VF_SPOOFCHK       0x2
+#define BNXT_VF_LINK_FORCED    0x4
+#define BNXT_VF_LINK_UP                0x8
+       u32     func_flags; /* func cfg flags */
+       u32     min_tx_rate;
+       u32     max_tx_rate;
+       void    *hwrm_cmd_req_addr;
+       dma_addr_t      hwrm_cmd_req_dma_addr;
+};
+#endif
+
+struct bnxt_pf_info {
+#define BNXT_FIRST_PF_FID      1
+#define BNXT_FIRST_VF_FID      128
+       u32     fw_fid;
+       u8      port_id;
+       u8      mac_addr[ETH_ALEN];
+       u16     max_rsscos_ctxs;
+       u16     max_cp_rings;
+       u16     max_tx_rings; /* HW assigned max tx rings for this PF */
+       u16     max_pf_tx_rings; /* runtime max tx rings owned by PF */
+       u16     max_rx_rings; /* HW assigned max rx rings for this PF */
+       u16     max_pf_rx_rings; /* runtime max rx rings owned by PF */
+       u16     max_irqs;
+       u16     max_l2_ctxs;
+       u16     max_vnics;
+       u16     max_stat_ctxs;
+       u32     first_vf_id;
+       u16     active_vfs;
+       u16     max_vfs;
+       u32     max_encap_records;
+       u32     max_decap_records;
+       u32     max_tx_em_flows;
+       u32     max_tx_wm_flows;
+       u32     max_rx_em_flows;
+       u32     max_rx_wm_flows;
+       unsigned long   *vf_event_bmap;
+       u16     hwrm_cmd_req_pages;
+       void                    *hwrm_cmd_req_addr[4];
+       dma_addr_t              hwrm_cmd_req_dma_addr[4];
+       struct bnxt_vf_info     *vf;
+};
+
+struct bnxt_ntuple_filter {
+       struct hlist_node       hash;
+       u8                      src_mac_addr[ETH_ALEN];
+       struct flow_keys        fkeys;
+       __le64                  filter_id;
+       u16                     sw_id;
+       u16                     rxq;
+       u32                     flow_id;
+       unsigned long           state;
+#define BNXT_FLTR_VALID                0
+#define BNXT_FLTR_UPDATE       1
+};
+
+#define BNXT_ALL_COPPER_ETHTOOL_SPEED                          \
+       (ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Full | \
+        ADVERTISED_10000baseT_Full)
+
+struct bnxt_link_info {
+       u8                      media_type;
+       u8                      transceiver;
+       u8                      phy_addr;
+       u8                      phy_link_status;
+#define BNXT_LINK_NO_LINK      PORT_PHY_QCFG_RESP_LINK_NO_LINK
+#define BNXT_LINK_SIGNAL       PORT_PHY_QCFG_RESP_LINK_SIGNAL
+#define BNXT_LINK_LINK         PORT_PHY_QCFG_RESP_LINK_LINK
+       u8                      wire_speed;
+       u8                      loop_back;
+       u8                      link_up;
+       u8                      duplex;
+#define BNXT_LINK_DUPLEX_HALF  PORT_PHY_QCFG_RESP_DUPLEX_HALF
+#define BNXT_LINK_DUPLEX_FULL  PORT_PHY_QCFG_RESP_DUPLEX_FULL
+       u8                      pause;
+#define BNXT_LINK_PAUSE_TX     PORT_PHY_QCFG_RESP_PAUSE_TX
+#define BNXT_LINK_PAUSE_RX     PORT_PHY_QCFG_RESP_PAUSE_RX
+#define BNXT_LINK_PAUSE_BOTH   (PORT_PHY_QCFG_RESP_PAUSE_RX | \
+                                PORT_PHY_QCFG_RESP_PAUSE_TX)
+       u8                      auto_pause_setting;
+       u8                      force_pause_setting;
+       u8                      duplex_setting;
+       u8                      auto_mode;
+#define BNXT_AUTO_MODE(mode)   ((mode) > BNXT_LINK_AUTO_NONE && \
+                                (mode) <= BNXT_LINK_AUTO_MSK)
+#define BNXT_LINK_AUTO_NONE     PORT_PHY_QCFG_RESP_AUTO_MODE_NONE
+#define BNXT_LINK_AUTO_ALLSPDS PORT_PHY_QCFG_RESP_AUTO_MODE_ALL_SPEEDS
+#define BNXT_LINK_AUTO_ONESPD  PORT_PHY_QCFG_RESP_AUTO_MODE_ONE_SPEED
+#define BNXT_LINK_AUTO_ONEORBELOW PORT_PHY_QCFG_RESP_AUTO_MODE_ONE_OR_BELOW
+#define BNXT_LINK_AUTO_MSK     PORT_PHY_QCFG_RESP_AUTO_MODE_MASK
+#define PHY_VER_LEN            3
+       u8                      phy_ver[PHY_VER_LEN];
+       u16                     link_speed;
+#define BNXT_LINK_SPEED_100MB  PORT_PHY_QCFG_RESP_LINK_SPEED_100MB
+#define BNXT_LINK_SPEED_1GB    PORT_PHY_QCFG_RESP_LINK_SPEED_1GB
+#define BNXT_LINK_SPEED_2GB    PORT_PHY_QCFG_RESP_LINK_SPEED_2GB
+#define BNXT_LINK_SPEED_2_5GB  PORT_PHY_QCFG_RESP_LINK_SPEED_2_5GB
+#define BNXT_LINK_SPEED_10GB   PORT_PHY_QCFG_RESP_LINK_SPEED_10GB
+#define BNXT_LINK_SPEED_20GB   PORT_PHY_QCFG_RESP_LINK_SPEED_20GB
+#define BNXT_LINK_SPEED_25GB   PORT_PHY_QCFG_RESP_LINK_SPEED_25GB
+#define BNXT_LINK_SPEED_40GB   PORT_PHY_QCFG_RESP_LINK_SPEED_40GB
+#define BNXT_LINK_SPEED_50GB   PORT_PHY_QCFG_RESP_LINK_SPEED_50GB
+       u16                     support_speeds;
+       u16                     auto_link_speeds;
+#define BNXT_LINK_SPEED_MSK_100MB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_100MB
+#define BNXT_LINK_SPEED_MSK_1GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_1GB
+#define BNXT_LINK_SPEED_MSK_2GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_2GB
+#define BNXT_LINK_SPEED_MSK_10GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_10GB
+#define BNXT_LINK_SPEED_MSK_2_5GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_2_5GB
+#define BNXT_LINK_SPEED_MSK_20GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_20GB
+#define BNXT_LINK_SPEED_MSK_25GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_25GB
+#define BNXT_LINK_SPEED_MSK_40GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_40GB
+#define BNXT_LINK_SPEED_MSK_50GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_50GB
+       u16                     auto_link_speed;
+       u16                     force_link_speed;
+       u32                     preemphasis;
+
+       /* copy of requested setting from ethtool cmd */
+       u8                      autoneg;
+#define BNXT_AUTONEG_SPEED             1
+#define BNXT_AUTONEG_FLOW_CTRL         2
+       u8                      req_duplex;
+       u8                      req_flow_ctrl;
+       u16                     req_link_speed;
+       u32                     advertising;
+       bool                    force_link_chng;
+       /* a copy of phy_qcfg output used to report link
+        * info to VF
+        */
+       struct hwrm_port_phy_qcfg_output phy_qcfg_resp;
+};
+
+#define BNXT_MAX_QUEUE 8
+
+struct bnxt_queue_info {
+       u8      queue_id;
+       u8      queue_profile;
+};
+
+struct bnxt {
+       void __iomem            *bar0;
+       void __iomem            *bar1;
+       void __iomem            *bar2;
+
+       u32                     reg_base;
+
+       struct net_device       *dev;
+       struct pci_dev          *pdev;
+
+       atomic_t                intr_sem;
+
+       u32                     flags;
+       #define BNXT_FLAG_DCB_ENABLED   0x1
+       #define BNXT_FLAG_VF            0x2
+       #define BNXT_FLAG_LRO           0x4
+#ifdef CONFIG_INET
+       #define BNXT_FLAG_GRO           0x8
+#else
+       /* Cannot support hardware GRO if CONFIG_INET is not set */
+       #define BNXT_FLAG_GRO           0x0
+#endif
+       #define BNXT_FLAG_TPA           (BNXT_FLAG_LRO | BNXT_FLAG_GRO)
+       #define BNXT_FLAG_JUMBO         0x10
+       #define BNXT_FLAG_STRIP_VLAN    0x20
+       #define BNXT_FLAG_AGG_RINGS     (BNXT_FLAG_JUMBO | BNXT_FLAG_GRO | \
+                                        BNXT_FLAG_LRO)
+       #define BNXT_FLAG_USING_MSIX    0x40
+       #define BNXT_FLAG_MSIX_CAP      0x80
+       #define BNXT_FLAG_RFS           0x100
+       #define BNXT_FLAG_ALL_CONFIG_FEATS (BNXT_FLAG_TPA |             \
+                                           BNXT_FLAG_RFS |             \
+                                           BNXT_FLAG_STRIP_VLAN)
+
+#define BNXT_PF(bp)            (!((bp)->flags & BNXT_FLAG_VF))
+#define BNXT_VF(bp)            ((bp)->flags & BNXT_FLAG_VF)
+
+       struct bnxt_napi        **bnapi;
+
+       u32                     rx_buf_size;
+       u32                     rx_buf_use_size;        /* useable size */
+       u32                     rx_ring_size;
+       u32                     rx_agg_ring_size;
+       u32                     rx_copy_thresh;
+       u32                     rx_ring_mask;
+       u32                     rx_agg_ring_mask;
+       int                     rx_nr_pages;
+       int                     rx_agg_nr_pages;
+       int                     rx_nr_rings;
+       int                     rsscos_nr_ctxs;
+
+       u32                     tx_ring_size;
+       u32                     tx_ring_mask;
+       int                     tx_nr_pages;
+       int                     tx_nr_rings;
+       int                     tx_nr_rings_per_tc;
+
+       int                     tx_wake_thresh;
+       int                     tx_push_thresh;
+       int                     tx_push_size;
+
+       u32                     cp_ring_size;
+       u32                     cp_ring_mask;
+       u32                     cp_bit;
+       int                     cp_nr_pages;
+       int                     cp_nr_rings;
+
+       int                     num_stat_ctxs;
+       struct bnxt_ring_grp_info       *grp_info;
+       struct bnxt_vnic_info   *vnic_info;
+       int                     nr_vnics;
+
+       u8                      max_tc;
+       struct bnxt_queue_info  q_info[BNXT_MAX_QUEUE];
+
+       unsigned int            current_interval;
+#define BNXT_TIMER_INTERVAL    (HZ / 2)
+
+       struct timer_list       timer;
+
+       int                     state;
+#define BNXT_STATE_CLOSED      0
+#define BNXT_STATE_OPEN                1
+
+       struct bnxt_irq *irq_tbl;
+       u8                      mac_addr[ETH_ALEN];
+
+       u32                     msg_enable;
+
+       u16                     hwrm_cmd_seq;
+       u32                     hwrm_intr_seq_id;
+       void                    *hwrm_cmd_resp_addr;
+       dma_addr_t              hwrm_cmd_resp_dma_addr;
+       void                    *hwrm_dbg_resp_addr;
+       dma_addr_t              hwrm_dbg_resp_dma_addr;
+#define HWRM_DBG_REG_BUF_SIZE  128
+       struct mutex            hwrm_cmd_lock;  /* serialize hwrm messages */
+       struct hwrm_ver_get_output      ver_resp;
+#define FW_VER_STR_LEN         32
+#define BC_HWRM_STR_LEN                21
+#define PHY_VER_STR_LEN         (FW_VER_STR_LEN - BC_HWRM_STR_LEN)
+       char                    fw_ver_str[FW_VER_STR_LEN];
+       __be16                  vxlan_port;
+       u8                      vxlan_port_cnt;
+       __le16                  vxlan_fw_dst_port_id;
+       u8                      nge_port_cnt;
+       __le16                  nge_fw_dst_port_id;
+       u16                     coal_ticks;
+       u16                     coal_ticks_irq;
+       u16                     coal_bufs;
+       u16                     coal_bufs_irq;
+
+#define BNXT_USEC_TO_COAL_TIMER(x)     ((x) * 25 / 2)
+#define BNXT_COAL_TIMER_TO_USEC(x) ((x) * 2 / 25)
+
+       struct work_struct      sp_task;
+       unsigned long           sp_event;
+#define BNXT_RX_MASK_SP_EVENT          0
+#define BNXT_RX_NTP_FLTR_SP_EVENT      1
+#define BNXT_LINK_CHNG_SP_EVENT                2
+#define BNXT_HWRM_EXEC_FWD_REQ_SP_EVENT        4
+#define BNXT_VXLAN_ADD_PORT_SP_EVENT   8
+#define BNXT_VXLAN_DEL_PORT_SP_EVENT   16
+#define BNXT_RESET_TASK_SP_EVENT       32
+#define BNXT_RST_RING_SP_EVENT         64
+
+       struct bnxt_pf_info     pf;
+#ifdef CONFIG_BNXT_SRIOV
+       int                     nr_vfs;
+       struct bnxt_vf_info     vf;
+       wait_queue_head_t       sriov_cfg_wait;
+       bool                    sriov_cfg;
+#define BNXT_SRIOV_CFG_WAIT_TMO        msecs_to_jiffies(10000)
+#endif
+
+#define BNXT_NTP_FLTR_MAX_FLTR 4096
+#define BNXT_NTP_FLTR_HASH_SIZE        512
+#define BNXT_NTP_FLTR_HASH_MASK        (BNXT_NTP_FLTR_HASH_SIZE - 1)
+       struct hlist_head       ntp_fltr_hash_tbl[BNXT_NTP_FLTR_HASH_SIZE];
+       spinlock_t              ntp_fltr_lock;  /* for hash table add, del */
+
+       unsigned long           *ntp_fltr_bmap;
+       int                     ntp_fltr_count;
+
+       struct bnxt_link_info   link_info;
+};
+
+#ifdef CONFIG_NET_RX_BUSY_POLL
+static inline void bnxt_enable_poll(struct bnxt_napi *bnapi)
+{
+       atomic_set(&bnapi->poll_state, BNXT_STATE_IDLE);
+}
+
+/* called from the NAPI poll routine to get ownership of a bnapi */
+static inline bool bnxt_lock_napi(struct bnxt_napi *bnapi)
+{
+       int rc = atomic_cmpxchg(&bnapi->poll_state, BNXT_STATE_IDLE,
+                               BNXT_STATE_NAPI);
+
+       return rc == BNXT_STATE_IDLE;
+}
+
+static inline void bnxt_unlock_napi(struct bnxt_napi *bnapi)
+{
+       atomic_set(&bnapi->poll_state, BNXT_STATE_IDLE);
+}
+
+/* called from the busy poll routine to get ownership of a bnapi */
+static inline bool bnxt_lock_poll(struct bnxt_napi *bnapi)
+{
+       int rc = atomic_cmpxchg(&bnapi->poll_state, BNXT_STATE_IDLE,
+                               BNXT_STATE_POLL);
+
+       return rc == BNXT_STATE_IDLE;
+}
+
+static inline void bnxt_unlock_poll(struct bnxt_napi *bnapi)
+{
+       atomic_set(&bnapi->poll_state, BNXT_STATE_IDLE);
+}
+
+static inline bool bnxt_busy_polling(struct bnxt_napi *bnapi)
+{
+       return atomic_read(&bnapi->poll_state) == BNXT_STATE_POLL;
+}
+
+static inline void bnxt_disable_poll(struct bnxt_napi *bnapi)
+{
+       int old;
+
+       while (1) {
+               old = atomic_cmpxchg(&bnapi->poll_state, BNXT_STATE_IDLE,
+                                    BNXT_STATE_DISABLE);
+               if (old == BNXT_STATE_IDLE)
+                       break;
+               usleep_range(500, 5000);
+       }
+}
+
+#else
+
+static inline void bnxt_enable_poll(struct bnxt_napi *bnapi)
+{
+}
+
+static inline bool bnxt_lock_napi(struct bnxt_napi *bnapi)
+{
+       return true;
+}
+
+static inline void bnxt_unlock_napi(struct bnxt_napi *bnapi)
+{
+}
+
+static inline bool bnxt_lock_poll(struct bnxt_napi *bnapi)
+{
+       return false;
+}
+
+static inline void bnxt_unlock_poll(struct bnxt_napi *bnapi)
+{
+}
+
+static inline bool bnxt_busy_polling(struct bnxt_napi *bnapi)
+{
+       return false;
+}
+
+static inline void bnxt_disable_poll(struct bnxt_napi *bnapi)
+{
+}
+
+#endif
+
+void bnxt_set_ring_params(struct bnxt *);
+void bnxt_hwrm_cmd_hdr_init(struct bnxt *, void *, u16, u16, u16);
+int _hwrm_send_message(struct bnxt *, void *, u32, int);
+int hwrm_send_message(struct bnxt *, void *, u32, int);
+int bnxt_hwrm_set_coal(struct bnxt *);
+int bnxt_hwrm_set_pause(struct bnxt *);
+int bnxt_hwrm_set_link_setting(struct bnxt *, bool);
+int bnxt_open_nic(struct bnxt *, bool, bool);
+int bnxt_close_nic(struct bnxt *, bool, bool);
+void bnxt_get_max_rings(struct bnxt *, int *, int *);
+#endif
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
new file mode 100644 (file)
index 0000000..45bd628
--- /dev/null
@@ -0,0 +1,1149 @@
+/* Broadcom NetXtreme-C/E network driver.
+ *
+ * Copyright (c) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ */
+
+#include <linux/ethtool.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <linux/firmware.h>
+#include "bnxt_hsi.h"
+#include "bnxt.h"
+#include "bnxt_ethtool.h"
+#include "bnxt_nvm_defs.h"     /* NVRAM content constant and structure defs */
+#include "bnxt_fw_hdr.h"       /* Firmware hdr constant and structure defs */
+#define FLASH_NVRAM_TIMEOUT    ((HWRM_CMD_TIMEOUT) * 100)
+
+static u32 bnxt_get_msglevel(struct net_device *dev)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       return bp->msg_enable;
+}
+
+static void bnxt_set_msglevel(struct net_device *dev, u32 value)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       bp->msg_enable = value;
+}
+
+static int bnxt_get_coalesce(struct net_device *dev,
+                            struct ethtool_coalesce *coal)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       memset(coal, 0, sizeof(*coal));
+
+       coal->rx_coalesce_usecs =
+               max_t(u16, BNXT_COAL_TIMER_TO_USEC(bp->coal_ticks), 1);
+       coal->rx_max_coalesced_frames = bp->coal_bufs / 2;
+       coal->rx_coalesce_usecs_irq =
+               max_t(u16, BNXT_COAL_TIMER_TO_USEC(bp->coal_ticks_irq), 1);
+       coal->rx_max_coalesced_frames_irq = bp->coal_bufs_irq / 2;
+
+       return 0;
+}
+
+static int bnxt_set_coalesce(struct net_device *dev,
+                            struct ethtool_coalesce *coal)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       int rc = 0;
+
+       bp->coal_ticks = BNXT_USEC_TO_COAL_TIMER(coal->rx_coalesce_usecs);
+       bp->coal_bufs = coal->rx_max_coalesced_frames * 2;
+       bp->coal_ticks_irq =
+               BNXT_USEC_TO_COAL_TIMER(coal->rx_coalesce_usecs_irq);
+       bp->coal_bufs_irq = coal->rx_max_coalesced_frames_irq * 2;
+
+       if (netif_running(dev))
+               rc = bnxt_hwrm_set_coal(bp);
+
+       return rc;
+}
+
+#define BNXT_NUM_STATS 21
+
+static int bnxt_get_sset_count(struct net_device *dev, int sset)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       switch (sset) {
+       case ETH_SS_STATS:
+               return BNXT_NUM_STATS * bp->cp_nr_rings;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void bnxt_get_ethtool_stats(struct net_device *dev,
+                                  struct ethtool_stats *stats, u64 *buf)
+{
+       u32 i, j = 0;
+       struct bnxt *bp = netdev_priv(dev);
+       u32 buf_size = sizeof(struct ctx_hw_stats) * bp->cp_nr_rings;
+       u32 stat_fields = sizeof(struct ctx_hw_stats) / 8;
+
+       memset(buf, 0, buf_size);
+
+       if (!bp->bnapi)
+               return;
+
+       for (i = 0; i < bp->cp_nr_rings; i++) {
+               struct bnxt_napi *bnapi = bp->bnapi[i];
+               struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+               __le64 *hw_stats = (__le64 *)cpr->hw_stats;
+               int k;
+
+               for (k = 0; k < stat_fields; j++, k++)
+                       buf[j] = le64_to_cpu(hw_stats[k]);
+               buf[j++] = cpr->rx_l4_csum_errors;
+       }
+}
+
+static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       u32 i;
+
+       switch (stringset) {
+       /* The number of strings must match BNXT_NUM_STATS defined above. */
+       case ETH_SS_STATS:
+               for (i = 0; i < bp->cp_nr_rings; i++) {
+                       sprintf(buf, "[%d]: rx_ucast_packets", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: rx_mcast_packets", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: rx_bcast_packets", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: rx_discards", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: rx_drops", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: rx_ucast_bytes", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: rx_mcast_bytes", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: rx_bcast_bytes", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tx_ucast_packets", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tx_mcast_packets", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tx_bcast_packets", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tx_discards", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tx_drops", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tx_ucast_bytes", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tx_mcast_bytes", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tx_bcast_bytes", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tpa_packets", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tpa_bytes", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tpa_events", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: tpa_aborts", i);
+                       buf += ETH_GSTRING_LEN;
+                       sprintf(buf, "[%d]: rx_l4_csum_errors", i);
+                       buf += ETH_GSTRING_LEN;
+               }
+               break;
+       default:
+               netdev_err(bp->dev, "bnxt_get_strings invalid request %x\n",
+                          stringset);
+               break;
+       }
+}
+
+static void bnxt_get_ringparam(struct net_device *dev,
+                              struct ethtool_ringparam *ering)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       ering->rx_max_pending = BNXT_MAX_RX_DESC_CNT;
+       ering->rx_jumbo_max_pending = BNXT_MAX_RX_JUM_DESC_CNT;
+       ering->tx_max_pending = BNXT_MAX_TX_DESC_CNT;
+
+       ering->rx_pending = bp->rx_ring_size;
+       ering->rx_jumbo_pending = bp->rx_agg_ring_size;
+       ering->tx_pending = bp->tx_ring_size;
+}
+
+static int bnxt_set_ringparam(struct net_device *dev,
+                             struct ethtool_ringparam *ering)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       if ((ering->rx_pending > BNXT_MAX_RX_DESC_CNT) ||
+           (ering->tx_pending > BNXT_MAX_TX_DESC_CNT) ||
+           (ering->tx_pending <= MAX_SKB_FRAGS))
+               return -EINVAL;
+
+       if (netif_running(dev))
+               bnxt_close_nic(bp, false, false);
+
+       bp->rx_ring_size = ering->rx_pending;
+       bp->tx_ring_size = ering->tx_pending;
+       bnxt_set_ring_params(bp);
+
+       if (netif_running(dev))
+               return bnxt_open_nic(bp, false, false);
+
+       return 0;
+}
+
+static void bnxt_get_channels(struct net_device *dev,
+                             struct ethtool_channels *channel)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       int max_rx_rings, max_tx_rings, tcs;
+
+       bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
+       tcs = netdev_get_num_tc(dev);
+       if (tcs > 1)
+               max_tx_rings /= tcs;
+
+       channel->max_rx = max_rx_rings;
+       channel->max_tx = max_tx_rings;
+       channel->max_other = 0;
+       channel->max_combined = 0;
+       channel->rx_count = bp->rx_nr_rings;
+       channel->tx_count = bp->tx_nr_rings_per_tc;
+}
+
+static int bnxt_set_channels(struct net_device *dev,
+                            struct ethtool_channels *channel)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       int max_rx_rings, max_tx_rings, tcs;
+       u32 rc = 0;
+
+       if (channel->other_count || channel->combined_count ||
+           !channel->rx_count || !channel->tx_count)
+               return -EINVAL;
+
+       bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
+       tcs = netdev_get_num_tc(dev);
+       if (tcs > 1)
+               max_tx_rings /= tcs;
+
+       if (channel->rx_count > max_rx_rings ||
+           channel->tx_count > max_tx_rings)
+               return -EINVAL;
+
+       if (netif_running(dev)) {
+               if (BNXT_PF(bp)) {
+                       /* TODO CHIMP_FW: Send message to all VF's
+                        * before PF unload
+                        */
+               }
+               rc = bnxt_close_nic(bp, true, false);
+               if (rc) {
+                       netdev_err(bp->dev, "Set channel failure rc :%x\n",
+                                  rc);
+                       return rc;
+               }
+       }
+
+       bp->rx_nr_rings = channel->rx_count;
+       bp->tx_nr_rings_per_tc = channel->tx_count;
+       bp->tx_nr_rings = bp->tx_nr_rings_per_tc;
+       if (tcs > 1)
+               bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tcs;
+       bp->cp_nr_rings = max_t(int, bp->tx_nr_rings, bp->rx_nr_rings);
+       bp->num_stat_ctxs = bp->cp_nr_rings;
+
+       if (netif_running(dev)) {
+               rc = bnxt_open_nic(bp, true, false);
+               if ((!rc) && BNXT_PF(bp)) {
+                       /* TODO CHIMP_FW: Send message to all VF's
+                        * to renable
+                        */
+               }
+       }
+
+       return rc;
+}
+
+#ifdef CONFIG_RFS_ACCEL
+static int bnxt_grxclsrlall(struct bnxt *bp, struct ethtool_rxnfc *cmd,
+                           u32 *rule_locs)
+{
+       int i, j = 0;
+
+       cmd->data = bp->ntp_fltr_count;
+       for (i = 0; i < BNXT_NTP_FLTR_HASH_SIZE; i++) {
+               struct hlist_head *head;
+               struct bnxt_ntuple_filter *fltr;
+
+               head = &bp->ntp_fltr_hash_tbl[i];
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(fltr, head, hash) {
+                       if (j == cmd->rule_cnt)
+                               break;
+                       rule_locs[j++] = fltr->sw_id;
+               }
+               rcu_read_unlock();
+               if (j == cmd->rule_cnt)
+                       break;
+       }
+       cmd->rule_cnt = j;
+       return 0;
+}
+
+static int bnxt_grxclsrule(struct bnxt *bp, struct ethtool_rxnfc *cmd)
+{
+       struct ethtool_rx_flow_spec *fs =
+               (struct ethtool_rx_flow_spec *)&cmd->fs;
+       struct bnxt_ntuple_filter *fltr;
+       struct flow_keys *fkeys;
+       int i, rc = -EINVAL;
+
+       if (fs->location < 0 || fs->location >= BNXT_NTP_FLTR_MAX_FLTR)
+               return rc;
+
+       for (i = 0; i < BNXT_NTP_FLTR_HASH_SIZE; i++) {
+               struct hlist_head *head;
+
+               head = &bp->ntp_fltr_hash_tbl[i];
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(fltr, head, hash) {
+                       if (fltr->sw_id == fs->location)
+                               goto fltr_found;
+               }
+               rcu_read_unlock();
+       }
+       return rc;
+
+fltr_found:
+       fkeys = &fltr->fkeys;
+       if (fkeys->basic.ip_proto == IPPROTO_TCP)
+               fs->flow_type = TCP_V4_FLOW;
+       else if (fkeys->basic.ip_proto == IPPROTO_UDP)
+               fs->flow_type = UDP_V4_FLOW;
+       else
+               goto fltr_err;
+
+       fs->h_u.tcp_ip4_spec.ip4src = fkeys->addrs.v4addrs.src;
+       fs->m_u.tcp_ip4_spec.ip4src = cpu_to_be32(~0);
+
+       fs->h_u.tcp_ip4_spec.ip4dst = fkeys->addrs.v4addrs.dst;
+       fs->m_u.tcp_ip4_spec.ip4dst = cpu_to_be32(~0);
+
+       fs->h_u.tcp_ip4_spec.psrc = fkeys->ports.src;
+       fs->m_u.tcp_ip4_spec.psrc = cpu_to_be16(~0);
+
+       fs->h_u.tcp_ip4_spec.pdst = fkeys->ports.dst;
+       fs->m_u.tcp_ip4_spec.pdst = cpu_to_be16(~0);
+
+       fs->ring_cookie = fltr->rxq;
+       rc = 0;
+
+fltr_err:
+       rcu_read_unlock();
+
+       return rc;
+}
+
+static int bnxt_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
+                         u32 *rule_locs)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       int rc = 0;
+
+       switch (cmd->cmd) {
+       case ETHTOOL_GRXRINGS:
+               cmd->data = bp->rx_nr_rings;
+               break;
+
+       case ETHTOOL_GRXCLSRLCNT:
+               cmd->rule_cnt = bp->ntp_fltr_count;
+               cmd->data = BNXT_NTP_FLTR_MAX_FLTR;
+               break;
+
+       case ETHTOOL_GRXCLSRLALL:
+               rc = bnxt_grxclsrlall(bp, cmd, (u32 *)rule_locs);
+               break;
+
+       case ETHTOOL_GRXCLSRULE:
+               rc = bnxt_grxclsrule(bp, cmd);
+               break;
+
+       default:
+               rc = -EOPNOTSUPP;
+               break;
+       }
+
+       return rc;
+}
+#endif
+
+static u32 bnxt_get_rxfh_indir_size(struct net_device *dev)
+{
+       return HW_HASH_INDEX_SIZE;
+}
+
+static u32 bnxt_get_rxfh_key_size(struct net_device *dev)
+{
+       return HW_HASH_KEY_SIZE;
+}
+
+static int bnxt_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
+                        u8 *hfunc)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_vnic_info *vnic = &bp->vnic_info[0];
+       int i = 0;
+
+       if (hfunc)
+               *hfunc = ETH_RSS_HASH_TOP;
+
+       if (indir)
+               for (i = 0; i < HW_HASH_INDEX_SIZE; i++)
+                       indir[i] = le16_to_cpu(vnic->rss_table[i]);
+
+       if (key)
+               memcpy(key, vnic->rss_hash_key, HW_HASH_KEY_SIZE);
+
+       return 0;
+}
+
+static void bnxt_get_drvinfo(struct net_device *dev,
+                            struct ethtool_drvinfo *info)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
+       strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
+       strlcpy(info->fw_version, bp->fw_ver_str, sizeof(info->fw_version));
+       strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info));
+       info->n_stats = BNXT_NUM_STATS * bp->cp_nr_rings;
+       info->testinfo_len = BNXT_NUM_TESTS(bp);
+       /* TODO CHIMP_FW: eeprom dump details */
+       info->eedump_len = 0;
+       /* TODO CHIMP FW: reg dump details */
+       info->regdump_len = 0;
+}
+
+static u32 bnxt_fw_to_ethtool_support_spds(struct bnxt_link_info *link_info)
+{
+       u16 fw_speeds = link_info->support_speeds;
+       u32 speed_mask = 0;
+
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_100MB)
+               speed_mask |= SUPPORTED_100baseT_Full;
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_1GB)
+               speed_mask |= SUPPORTED_1000baseT_Full;
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_2_5GB)
+               speed_mask |= SUPPORTED_2500baseX_Full;
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_10GB)
+               speed_mask |= SUPPORTED_10000baseT_Full;
+       /* TODO: support 25GB, 50GB with different cable type */
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_20GB)
+               speed_mask |= SUPPORTED_20000baseMLD2_Full |
+                       SUPPORTED_20000baseKR2_Full;
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_40GB)
+               speed_mask |= SUPPORTED_40000baseKR4_Full |
+                       SUPPORTED_40000baseCR4_Full |
+                       SUPPORTED_40000baseSR4_Full |
+                       SUPPORTED_40000baseLR4_Full;
+
+       return speed_mask;
+}
+
+static u32 bnxt_fw_to_ethtool_advertised_spds(struct bnxt_link_info *link_info)
+{
+       u16 fw_speeds = link_info->auto_link_speeds;
+       u32 speed_mask = 0;
+
+       /* TODO: support 25GB, 40GB, 50GB with different cable type */
+       /* set the advertised speeds */
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_100MB)
+               speed_mask |= ADVERTISED_100baseT_Full;
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_1GB)
+               speed_mask |= ADVERTISED_1000baseT_Full;
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_2_5GB)
+               speed_mask |= ADVERTISED_2500baseX_Full;
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_10GB)
+               speed_mask |= ADVERTISED_10000baseT_Full;
+       /* TODO: how to advertise 20, 25, 40, 50GB with different cable type ?*/
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_20GB)
+               speed_mask |= ADVERTISED_20000baseMLD2_Full |
+                             ADVERTISED_20000baseKR2_Full;
+       if (fw_speeds & BNXT_LINK_SPEED_MSK_40GB)
+               speed_mask |= ADVERTISED_40000baseKR4_Full |
+                             ADVERTISED_40000baseCR4_Full |
+                             ADVERTISED_40000baseSR4_Full |
+                             ADVERTISED_40000baseLR4_Full;
+       return speed_mask;
+}
+
+u32 bnxt_fw_to_ethtool_speed(u16 fw_link_speed)
+{
+       switch (fw_link_speed) {
+       case BNXT_LINK_SPEED_100MB:
+               return SPEED_100;
+       case BNXT_LINK_SPEED_1GB:
+               return SPEED_1000;
+       case BNXT_LINK_SPEED_2_5GB:
+               return SPEED_2500;
+       case BNXT_LINK_SPEED_10GB:
+               return SPEED_10000;
+       case BNXT_LINK_SPEED_20GB:
+               return SPEED_20000;
+       case BNXT_LINK_SPEED_25GB:
+               return SPEED_25000;
+       case BNXT_LINK_SPEED_40GB:
+               return SPEED_40000;
+       case BNXT_LINK_SPEED_50GB:
+               return SPEED_50000;
+       default:
+               return SPEED_UNKNOWN;
+       }
+}
+
+static int bnxt_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_link_info *link_info = &bp->link_info;
+       u16 ethtool_speed;
+
+       cmd->supported = bnxt_fw_to_ethtool_support_spds(link_info);
+
+       if (link_info->auto_link_speeds)
+               cmd->supported |= SUPPORTED_Autoneg;
+
+       if (BNXT_AUTO_MODE(link_info->auto_mode)) {
+               cmd->advertising =
+                       bnxt_fw_to_ethtool_advertised_spds(link_info);
+               cmd->advertising |= ADVERTISED_Autoneg;
+               cmd->autoneg = AUTONEG_ENABLE;
+       } else {
+               cmd->autoneg = AUTONEG_DISABLE;
+               cmd->advertising = 0;
+       }
+       if (link_info->auto_pause_setting & BNXT_LINK_PAUSE_BOTH) {
+               if ((link_info->auto_pause_setting & BNXT_LINK_PAUSE_BOTH) ==
+                   BNXT_LINK_PAUSE_BOTH) {
+                       cmd->advertising |= ADVERTISED_Pause;
+                       cmd->supported |= SUPPORTED_Pause;
+               } else {
+                       cmd->advertising |= ADVERTISED_Asym_Pause;
+                       cmd->supported |= SUPPORTED_Asym_Pause;
+                       if (link_info->auto_pause_setting &
+                           BNXT_LINK_PAUSE_RX)
+                               cmd->advertising |= ADVERTISED_Pause;
+               }
+       } else if (link_info->force_pause_setting & BNXT_LINK_PAUSE_BOTH) {
+               if ((link_info->force_pause_setting & BNXT_LINK_PAUSE_BOTH) ==
+                   BNXT_LINK_PAUSE_BOTH) {
+                       cmd->supported |= SUPPORTED_Pause;
+               } else {
+                       cmd->supported |= SUPPORTED_Asym_Pause;
+                       if (link_info->force_pause_setting &
+                           BNXT_LINK_PAUSE_RX)
+                               cmd->supported |= SUPPORTED_Pause;
+               }
+       }
+
+       cmd->port = PORT_NONE;
+       if (link_info->media_type == PORT_PHY_QCFG_RESP_MEDIA_TYPE_TP) {
+               cmd->port = PORT_TP;
+               cmd->supported |= SUPPORTED_TP;
+               cmd->advertising |= ADVERTISED_TP;
+       } else {
+               cmd->supported |= SUPPORTED_FIBRE;
+               cmd->advertising |= ADVERTISED_FIBRE;
+
+               if (link_info->media_type == PORT_PHY_QCFG_RESP_MEDIA_TYPE_DAC)
+                       cmd->port = PORT_DA;
+               else if (link_info->media_type ==
+                        PORT_PHY_QCFG_RESP_MEDIA_TYPE_FIBRE)
+                       cmd->port = PORT_FIBRE;
+       }
+
+       if (link_info->phy_link_status == BNXT_LINK_LINK) {
+               if (link_info->duplex & BNXT_LINK_DUPLEX_FULL)
+                       cmd->duplex = DUPLEX_FULL;
+       } else {
+               cmd->duplex = DUPLEX_UNKNOWN;
+       }
+       ethtool_speed = bnxt_fw_to_ethtool_speed(link_info->link_speed);
+       ethtool_cmd_speed_set(cmd, ethtool_speed);
+       if (link_info->transceiver ==
+               PORT_PHY_QCFG_RESP_TRANSCEIVER_TYPE_XCVR_INTERNAL)
+               cmd->transceiver = XCVR_INTERNAL;
+       else
+               cmd->transceiver = XCVR_EXTERNAL;
+       cmd->phy_address = link_info->phy_addr;
+
+       return 0;
+}
+
+static u32 bnxt_get_fw_speed(struct net_device *dev, u16 ethtool_speed)
+{
+       switch (ethtool_speed) {
+       case SPEED_100:
+               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_100MB;
+       case SPEED_1000:
+               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_1GB;
+       case SPEED_2500:
+               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_2_5GB;
+       case SPEED_10000:
+               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_10GB;
+       case SPEED_20000:
+               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_20GB;
+       case SPEED_25000:
+               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_25GB;
+       case SPEED_40000:
+               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_40GB;
+       case SPEED_50000:
+               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_50GB;
+       default:
+               netdev_err(dev, "unsupported speed!\n");
+               break;
+       }
+       return 0;
+}
+
+static u16 bnxt_get_fw_auto_link_speeds(u32 advertising)
+{
+       u16 fw_speed_mask = 0;
+
+       /* only support autoneg at speed 100, 1000, and 10000 */
+       if (advertising & (ADVERTISED_100baseT_Full |
+                          ADVERTISED_100baseT_Half)) {
+               fw_speed_mask |= BNXT_LINK_SPEED_MSK_100MB;
+       }
+       if (advertising & (ADVERTISED_1000baseT_Full |
+                          ADVERTISED_1000baseT_Half)) {
+               fw_speed_mask |= BNXT_LINK_SPEED_MSK_1GB;
+       }
+       if (advertising & ADVERTISED_10000baseT_Full)
+               fw_speed_mask |= BNXT_LINK_SPEED_MSK_10GB;
+
+       return fw_speed_mask;
+}
+
+static int bnxt_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       int rc = 0;
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_link_info *link_info = &bp->link_info;
+       u32 speed, fw_advertising = 0;
+       bool set_pause = false;
+
+       if (BNXT_VF(bp))
+               return rc;
+
+       if (cmd->autoneg == AUTONEG_ENABLE) {
+               if (link_info->media_type != PORT_PHY_QCFG_RESP_MEDIA_TYPE_TP) {
+                       netdev_err(dev, "Media type doesn't support autoneg\n");
+                       rc = -EINVAL;
+                       goto set_setting_exit;
+               }
+               if (cmd->advertising & ~(BNXT_ALL_COPPER_ETHTOOL_SPEED |
+                                        ADVERTISED_Autoneg |
+                                        ADVERTISED_TP |
+                                        ADVERTISED_Pause |
+                                        ADVERTISED_Asym_Pause)) {
+                       netdev_err(dev, "Unsupported advertising mask (adv: 0x%x)\n",
+                                  cmd->advertising);
+                       rc = -EINVAL;
+                       goto set_setting_exit;
+               }
+               fw_advertising = bnxt_get_fw_auto_link_speeds(cmd->advertising);
+               if (fw_advertising & ~link_info->support_speeds) {
+                       netdev_err(dev, "Advertising parameters are not supported! (adv: 0x%x)\n",
+                                  cmd->advertising);
+                       rc = -EINVAL;
+                       goto set_setting_exit;
+               }
+               link_info->autoneg |= BNXT_AUTONEG_SPEED;
+               if (!fw_advertising)
+                       link_info->advertising = link_info->support_speeds;
+               else
+                       link_info->advertising = fw_advertising;
+               /* any change to autoneg will cause link change, therefore the
+                * driver should put back the original pause setting in autoneg
+                */
+               set_pause = true;
+       } else {
+               /* TODO: currently don't support half duplex */
+               if (cmd->duplex == DUPLEX_HALF) {
+                       netdev_err(dev, "HALF DUPLEX is not supported!\n");
+                       rc = -EINVAL;
+                       goto set_setting_exit;
+               }
+               /* If received a request for an unknown duplex, assume full*/
+               if (cmd->duplex == DUPLEX_UNKNOWN)
+                       cmd->duplex = DUPLEX_FULL;
+               speed = ethtool_cmd_speed(cmd);
+               link_info->req_link_speed = bnxt_get_fw_speed(dev, speed);
+               link_info->req_duplex = BNXT_LINK_DUPLEX_FULL;
+               link_info->autoneg &= ~BNXT_AUTONEG_SPEED;
+               link_info->advertising = 0;
+       }
+
+       if (netif_running(dev))
+               rc = bnxt_hwrm_set_link_setting(bp, set_pause);
+
+set_setting_exit:
+       return rc;
+}
+
+static void bnxt_get_pauseparam(struct net_device *dev,
+                               struct ethtool_pauseparam *epause)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_link_info *link_info = &bp->link_info;
+
+       if (BNXT_VF(bp))
+               return;
+       epause->autoneg = !!(link_info->auto_pause_setting &
+                            BNXT_LINK_PAUSE_BOTH);
+       epause->rx_pause = ((link_info->pause & BNXT_LINK_PAUSE_RX) != 0);
+       epause->tx_pause = ((link_info->pause & BNXT_LINK_PAUSE_TX) != 0);
+}
+
+static int bnxt_set_pauseparam(struct net_device *dev,
+                              struct ethtool_pauseparam *epause)
+{
+       int rc = 0;
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_link_info *link_info = &bp->link_info;
+
+       if (BNXT_VF(bp))
+               return rc;
+
+       if (epause->autoneg) {
+               link_info->autoneg |= BNXT_AUTONEG_FLOW_CTRL;
+               link_info->req_flow_ctrl |= BNXT_LINK_PAUSE_BOTH;
+       } else {
+               /* when transition from auto pause to force pause,
+                * force a link change
+                */
+               if (link_info->autoneg & BNXT_AUTONEG_FLOW_CTRL)
+                       link_info->force_link_chng = true;
+               link_info->autoneg &= ~BNXT_AUTONEG_FLOW_CTRL;
+               link_info->req_flow_ctrl &= ~BNXT_LINK_PAUSE_BOTH;
+       }
+       if (epause->rx_pause)
+               link_info->req_flow_ctrl |= BNXT_LINK_PAUSE_RX;
+       else
+               link_info->req_flow_ctrl &= ~BNXT_LINK_PAUSE_RX;
+
+       if (epause->tx_pause)
+               link_info->req_flow_ctrl |= BNXT_LINK_PAUSE_TX;
+       else
+               link_info->req_flow_ctrl &= ~BNXT_LINK_PAUSE_TX;
+
+       if (netif_running(dev))
+               rc = bnxt_hwrm_set_pause(bp);
+       return rc;
+}
+
+static u32 bnxt_get_link(struct net_device *dev)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       /* TODO: handle MF, VF, driver close case */
+       return bp->link_info.link_up;
+}
+
+static int bnxt_flash_nvram(struct net_device *dev,
+                           u16 dir_type,
+                           u16 dir_ordinal,
+                           u16 dir_ext,
+                           u16 dir_attr,
+                           const u8 *data,
+                           size_t data_len)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       int rc;
+       struct hwrm_nvm_write_input req = {0};
+       dma_addr_t dma_handle;
+       u8 *kmem;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_WRITE, -1, -1);
+
+       req.dir_type = cpu_to_le16(dir_type);
+       req.dir_ordinal = cpu_to_le16(dir_ordinal);
+       req.dir_ext = cpu_to_le16(dir_ext);
+       req.dir_attr = cpu_to_le16(dir_attr);
+       req.dir_data_length = cpu_to_le32(data_len);
+
+       kmem = dma_alloc_coherent(&bp->pdev->dev, data_len, &dma_handle,
+                                 GFP_KERNEL);
+       if (!kmem) {
+               netdev_err(dev, "dma_alloc_coherent failure, length = %u\n",
+                          (unsigned)data_len);
+               return -ENOMEM;
+       }
+       memcpy(kmem, data, data_len);
+       req.host_src_addr = cpu_to_le64(dma_handle);
+
+       rc = hwrm_send_message(bp, &req, sizeof(req), FLASH_NVRAM_TIMEOUT);
+       dma_free_coherent(&bp->pdev->dev, data_len, kmem, dma_handle);
+
+       return rc;
+}
+
+static int bnxt_flash_firmware(struct net_device *dev,
+                              u16 dir_type,
+                              const u8 *fw_data,
+                              size_t fw_size)
+{
+       int     rc = 0;
+       u16     code_type;
+       u32     stored_crc;
+       u32     calculated_crc;
+       struct bnxt_fw_header *header = (struct bnxt_fw_header *)fw_data;
+
+       switch (dir_type) {
+       case BNX_DIR_TYPE_BOOTCODE:
+       case BNX_DIR_TYPE_BOOTCODE_2:
+               code_type = CODE_BOOT;
+               break;
+       default:
+               netdev_err(dev, "Unsupported directory entry type: %u\n",
+                          dir_type);
+               return -EINVAL;
+       }
+       if (fw_size < sizeof(struct bnxt_fw_header)) {
+               netdev_err(dev, "Invalid firmware file size: %u\n",
+                          (unsigned int)fw_size);
+               return -EINVAL;
+       }
+       if (header->signature != cpu_to_le32(BNXT_FIRMWARE_BIN_SIGNATURE)) {
+               netdev_err(dev, "Invalid firmware signature: %08X\n",
+                          le32_to_cpu(header->signature));
+               return -EINVAL;
+       }
+       if (header->code_type != code_type) {
+               netdev_err(dev, "Expected firmware type: %d, read: %d\n",
+                          code_type, header->code_type);
+               return -EINVAL;
+       }
+       if (header->device != DEVICE_CUMULUS_FAMILY) {
+               netdev_err(dev, "Expected firmware device family %d, read: %d\n",
+                          DEVICE_CUMULUS_FAMILY, header->device);
+               return -EINVAL;
+       }
+       /* Confirm the CRC32 checksum of the file: */
+       stored_crc = le32_to_cpu(*(__le32 *)(fw_data + fw_size -
+                                            sizeof(stored_crc)));
+       calculated_crc = ~crc32(~0, fw_data, fw_size - sizeof(stored_crc));
+       if (calculated_crc != stored_crc) {
+               netdev_err(dev, "Firmware file CRC32 checksum (%08lX) does not match calculated checksum (%08lX)\n",
+                          (unsigned long)stored_crc,
+                          (unsigned long)calculated_crc);
+               return -EINVAL;
+       }
+       /* TODO: Validate digital signature (RSA-encrypted SHA-256 hash) here */
+       rc = bnxt_flash_nvram(dev, dir_type, BNX_DIR_ORDINAL_FIRST,
+                             0, 0, fw_data, fw_size);
+       if (rc == 0) {  /* Firmware update successful */
+               /* TODO: Notify processor it needs to reset itself
+                */
+       }
+       return rc;
+}
+
+static bool bnxt_dir_type_is_ape_bin_format(u16 dir_type)
+{
+       switch (dir_type) {
+       case BNX_DIR_TYPE_CHIMP_PATCH:
+       case BNX_DIR_TYPE_BOOTCODE:
+       case BNX_DIR_TYPE_BOOTCODE_2:
+       case BNX_DIR_TYPE_APE_FW:
+       case BNX_DIR_TYPE_APE_PATCH:
+       case BNX_DIR_TYPE_KONG_FW:
+       case BNX_DIR_TYPE_KONG_PATCH:
+               return true;
+       }
+
+       return false;
+}
+
+static bool bnxt_dir_type_is_unprotected_exec_format(u16 dir_type)
+{
+       switch (dir_type) {
+       case BNX_DIR_TYPE_AVS:
+       case BNX_DIR_TYPE_EXP_ROM_MBA:
+       case BNX_DIR_TYPE_PCIE:
+       case BNX_DIR_TYPE_TSCF_UCODE:
+       case BNX_DIR_TYPE_EXT_PHY:
+       case BNX_DIR_TYPE_CCM:
+       case BNX_DIR_TYPE_ISCSI_BOOT:
+       case BNX_DIR_TYPE_ISCSI_BOOT_IPV6:
+       case BNX_DIR_TYPE_ISCSI_BOOT_IPV4N6:
+               return true;
+       }
+
+       return false;
+}
+
+static bool bnxt_dir_type_is_executable(u16 dir_type)
+{
+       return bnxt_dir_type_is_ape_bin_format(dir_type) ||
+               bnxt_dir_type_is_unprotected_exec_format(dir_type);
+}
+
+static int bnxt_flash_firmware_from_file(struct net_device *dev,
+                                        u16 dir_type,
+                                        const char *filename)
+{
+       const struct firmware  *fw;
+       int                     rc;
+
+       if (bnxt_dir_type_is_executable(dir_type) == false)
+               return -EINVAL;
+
+       rc = request_firmware(&fw, filename, &dev->dev);
+       if (rc != 0) {
+               netdev_err(dev, "Error %d requesting firmware file: %s\n",
+                          rc, filename);
+               return rc;
+       }
+       if (bnxt_dir_type_is_ape_bin_format(dir_type) == true)
+               rc = bnxt_flash_firmware(dev, dir_type, fw->data, fw->size);
+       else
+               rc = bnxt_flash_nvram(dev, dir_type, BNX_DIR_ORDINAL_FIRST,
+                                     0, 0, fw->data, fw->size);
+       release_firmware(fw);
+       return rc;
+}
+
+static int bnxt_flash_package_from_file(struct net_device *dev,
+                                       char *filename)
+{
+       netdev_err(dev, "packages are not yet supported\n");
+       return -EINVAL;
+}
+
+static int bnxt_flash_device(struct net_device *dev,
+                            struct ethtool_flash *flash)
+{
+       if (!BNXT_PF((struct bnxt *)netdev_priv(dev))) {
+               netdev_err(dev, "flashdev not supported from a virtual function\n");
+               return -EINVAL;
+       }
+
+       if (flash->region == ETHTOOL_FLASH_ALL_REGIONS)
+               return bnxt_flash_package_from_file(dev, flash->data);
+
+       return bnxt_flash_firmware_from_file(dev, flash->region, flash->data);
+}
+
+static int nvm_get_dir_info(struct net_device *dev, u32 *entries, u32 *length)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       int rc;
+       struct hwrm_nvm_get_dir_info_input req = {0};
+       struct hwrm_nvm_get_dir_info_output *output = bp->hwrm_cmd_resp_addr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_GET_DIR_INFO, -1, -1);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc) {
+               *entries = le32_to_cpu(output->entries);
+               *length = le32_to_cpu(output->entry_length);
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_get_eeprom_len(struct net_device *dev)
+{
+       /* The -1 return value allows the entire 32-bit range of offsets to be
+        * passed via the ethtool command-line utility.
+        */
+       return -1;
+}
+
+static int bnxt_get_nvram_directory(struct net_device *dev, u32 len, u8 *data)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       int rc;
+       u32 dir_entries;
+       u32 entry_length;
+       u8 *buf;
+       size_t buflen;
+       dma_addr_t dma_handle;
+       struct hwrm_nvm_get_dir_entries_input req = {0};
+
+       rc = nvm_get_dir_info(dev, &dir_entries, &entry_length);
+       if (rc != 0)
+               return rc;
+
+       /* Insert 2 bytes of directory info (count and size of entries) */
+       if (len < 2)
+               return -EINVAL;
+
+       *data++ = dir_entries;
+       *data++ = entry_length;
+       len -= 2;
+       memset(data, 0xff, len);
+
+       buflen = dir_entries * entry_length;
+       buf = dma_alloc_coherent(&bp->pdev->dev, buflen, &dma_handle,
+                                GFP_KERNEL);
+       if (!buf) {
+               netdev_err(dev, "dma_alloc_coherent failure, length = %u\n",
+                          (unsigned)buflen);
+               return -ENOMEM;
+       }
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_GET_DIR_ENTRIES, -1, -1);
+       req.host_dest_addr = cpu_to_le64(dma_handle);
+       rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (rc == 0)
+               memcpy(data, buf, len > buflen ? buflen : len);
+       dma_free_coherent(&bp->pdev->dev, buflen, buf, dma_handle);
+       return rc;
+}
+
+static int bnxt_get_nvram_item(struct net_device *dev, u32 index, u32 offset,
+                              u32 length, u8 *data)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       int rc;
+       u8 *buf;
+       dma_addr_t dma_handle;
+       struct hwrm_nvm_read_input req = {0};
+
+       buf = dma_alloc_coherent(&bp->pdev->dev, length, &dma_handle,
+                                GFP_KERNEL);
+       if (!buf) {
+               netdev_err(dev, "dma_alloc_coherent failure, length = %u\n",
+                          (unsigned)length);
+               return -ENOMEM;
+       }
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_READ, -1, -1);
+       req.host_dest_addr = cpu_to_le64(dma_handle);
+       req.dir_idx = cpu_to_le16(index);
+       req.offset = cpu_to_le32(offset);
+       req.len = cpu_to_le32(length);
+
+       rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (rc == 0)
+               memcpy(data, buf, length);
+       dma_free_coherent(&bp->pdev->dev, length, buf, dma_handle);
+       return rc;
+}
+
+static int bnxt_get_eeprom(struct net_device *dev,
+                          struct ethtool_eeprom *eeprom,
+                          u8 *data)
+{
+       u32 index;
+       u32 offset;
+
+       if (eeprom->offset == 0) /* special offset value to get directory */
+               return bnxt_get_nvram_directory(dev, eeprom->len, data);
+
+       index = eeprom->offset >> 24;
+       offset = eeprom->offset & 0xffffff;
+
+       if (index == 0) {
+               netdev_err(dev, "unsupported index value: %d\n", index);
+               return -EINVAL;
+       }
+
+       return bnxt_get_nvram_item(dev, index - 1, offset, eeprom->len, data);
+}
+
+static int bnxt_erase_nvram_directory(struct net_device *dev, u8 index)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       struct hwrm_nvm_erase_dir_entry_input req = {0};
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_ERASE_DIR_ENTRY, -1, -1);
+       req.dir_idx = cpu_to_le16(index);
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
+static int bnxt_set_eeprom(struct net_device *dev,
+                          struct ethtool_eeprom *eeprom,
+                          u8 *data)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       u8 index, dir_op;
+       u16 type, ext, ordinal, attr;
+
+       if (!BNXT_PF(bp)) {
+               netdev_err(dev, "NVM write not supported from a virtual function\n");
+               return -EINVAL;
+       }
+
+       type = eeprom->magic >> 16;
+
+       if (type == 0xffff) { /* special value for directory operations */
+               index = eeprom->magic & 0xff;
+               dir_op = eeprom->magic >> 8;
+               if (index == 0)
+                       return -EINVAL;
+               switch (dir_op) {
+               case 0x0e: /* erase */
+                       if (eeprom->offset != ~eeprom->magic)
+                               return -EINVAL;
+                       return bnxt_erase_nvram_directory(dev, index - 1);
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       /* Create or re-write an NVM item: */
+       if (bnxt_dir_type_is_executable(type) == true)
+               return -EINVAL;
+       ext = eeprom->magic & 0xffff;
+       ordinal = eeprom->offset >> 16;
+       attr = eeprom->offset & 0xffff;
+
+       return bnxt_flash_nvram(dev, type, ordinal, ext, attr, data,
+                               eeprom->len);
+}
+
+const struct ethtool_ops bnxt_ethtool_ops = {
+       .get_settings           = bnxt_get_settings,
+       .set_settings           = bnxt_set_settings,
+       .get_pauseparam         = bnxt_get_pauseparam,
+       .set_pauseparam         = bnxt_set_pauseparam,
+       .get_drvinfo            = bnxt_get_drvinfo,
+       .get_coalesce           = bnxt_get_coalesce,
+       .set_coalesce           = bnxt_set_coalesce,
+       .get_msglevel           = bnxt_get_msglevel,
+       .set_msglevel           = bnxt_set_msglevel,
+       .get_sset_count         = bnxt_get_sset_count,
+       .get_strings            = bnxt_get_strings,
+       .get_ethtool_stats      = bnxt_get_ethtool_stats,
+       .set_ringparam          = bnxt_set_ringparam,
+       .get_ringparam          = bnxt_get_ringparam,
+       .get_channels           = bnxt_get_channels,
+       .set_channels           = bnxt_set_channels,
+#ifdef CONFIG_RFS_ACCEL
+       .get_rxnfc              = bnxt_get_rxnfc,
+#endif
+       .get_rxfh_indir_size    = bnxt_get_rxfh_indir_size,
+       .get_rxfh_key_size      = bnxt_get_rxfh_key_size,
+       .get_rxfh               = bnxt_get_rxfh,
+       .flash_device           = bnxt_flash_device,
+       .get_eeprom_len         = bnxt_get_eeprom_len,
+       .get_eeprom             = bnxt_get_eeprom,
+       .set_eeprom             = bnxt_set_eeprom,
+       .get_link               = bnxt_get_link,
+};
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h
new file mode 100644 (file)
index 0000000..98fa81e
--- /dev/null
@@ -0,0 +1,17 @@
+/* Broadcom NetXtreme-C/E network driver.
+ *
+ * Copyright (c) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ */
+
+#ifndef BNXT_ETHTOOL_H
+#define BNXT_ETHTOOL_H
+
+extern const struct ethtool_ops bnxt_ethtool_ops;
+
+u32 bnxt_fw_to_ethtool_speed(u16);
+
+#endif
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_fw_hdr.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_fw_hdr.h
new file mode 100644 (file)
index 0000000..e0aac65
--- /dev/null
@@ -0,0 +1,104 @@
+/* Broadcom NetXtreme-C/E network driver.
+ *
+ * Copyright (c) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ */
+
+#ifndef __BNXT_FW_HDR_H__
+#define __BNXT_FW_HDR_H__
+
+#define BNXT_FIRMWARE_BIN_SIGNATURE     0x1a4d4342     /* "BCM"+0x1a */
+
+enum SUPPORTED_FAMILY {
+       DEVICE_5702_3_4_FAMILY,         /* 0  - Denali, Vinson, K2 */
+       DEVICE_5705_FAMILY,             /* 1  - Bachelor */
+       DEVICE_SHASTA_FAMILY,           /* 2  - 5751 */
+       DEVICE_5706_FAMILY,             /* 3  - Teton */
+       DEVICE_5714_FAMILY,             /* 4  - Hamilton */
+       DEVICE_STANFORD_FAMILY,         /* 5  - 5755 */
+       DEVICE_STANFORD_ME_FAMILY,      /* 6  - 5756 */
+       DEVICE_SOLEDAD_FAMILY,          /* 7  - 5761[E] */
+       DEVICE_CILAI_FAMILY,            /* 8  - 57780/60/90/91 */
+       DEVICE_ASPEN_FAMILY,            /* 9  - 57781/85/61/65/91/95 */
+       DEVICE_ASPEN_PLUS_FAMILY,       /* 10 - 57786 */
+       DEVICE_LOGAN_FAMILY,            /* 11 - Any device in the Logan family
+                                        */
+       DEVICE_LOGAN_5762,              /* 12 - Logan Enterprise (aka Columbia)
+                                        */
+       DEVICE_LOGAN_57767,             /* 13 - Logan Client */
+       DEVICE_LOGAN_57787,             /* 14 - Logan Consumer */
+       DEVICE_LOGAN_5725,              /* 15 - Logan Server (TruManage-enabled)
+                                        */
+       DEVICE_SAWTOOTH_FAMILY,         /* 16 - 5717/18 */
+       DEVICE_COTOPAXI_FAMILY,         /* 17 - 5719 */
+       DEVICE_SNAGGLETOOTH_FAMILY,     /* 18 - 5720 */
+       DEVICE_CUMULUS_FAMILY,          /* 19 - Cumulus/Whitney */
+       MAX_DEVICE_FAMILY
+};
+
+enum SUPPORTED_CODE {
+       CODE_ASF1,              /* 0  - ASF VERSION 1.03 <deprecated> */
+       CODE_ASF2,              /* 1  - ASF VERSION 2.00 <deprecated> */
+       CODE_PASSTHRU,          /* 2  - PassThru         <deprecated> */
+       CODE_PT_SEC,            /* 3  - PassThru with security <deprecated> */
+       CODE_UMP,               /* 4  - UMP                     <deprecated> */
+       CODE_BOOT,              /* 5  - Bootcode */
+       CODE_DASH,              /* 6  - TruManage (DASH + ASF + PMCI)
+                                *      Management firmwares
+                                */
+       CODE_MCTP_PASSTHRU,     /* 7  - NCSI / MCTP Passt-hrough firmware */
+       CODE_PM_OFFLOAD,        /* 8  - Power-Management Proxy Offload firmwares
+                                */
+       CODE_MDNS_SD_OFFLOAD,   /* 9  - Multicast DNS Service Discovery Proxys
+                                *      Offload firmware
+                                */
+       CODE_DISC_OFFLOAD,      /* 10 - Discovery Offload firmware */
+       CODE_MUSTANG,           /* 11 - I2C Error reporting APE firmwares
+                                *      <deprecated>
+                                */
+       CODE_ARP_BATCH,         /* 12 - ARP Batch firmware */
+       CODE_SMASH,             /* 13 - TruManage (SMASH + DCMI/IPMI + PMCI)
+                                *      Management firmware
+                                */
+       CODE_APE_DIAG,          /* 14 - APE Test Diag firmware */
+       CODE_APE_PATCH,         /* 15 - APE Patch firmware */
+       CODE_TANG_PATCH,        /* 16 - TANG Patch firmware */
+       CODE_KONG_FW,           /* 17 - KONG firmware */
+       CODE_KONG_PATCH,        /* 18 - KONG Patch firmware */
+       CODE_BONO_FW,           /* 19 - BONO firmware */
+       CODE_BONO_PATCH,        /* 20 - BONO Patch firmware */
+
+       MAX_CODE_TYPE,
+};
+
+enum SUPPORTED_MEDIA {
+       MEDIA_COPPER,           /* 0 */
+       MEDIA_FIBER,            /* 1 */
+       MEDIA_NONE,             /* 2 */
+       MEDIA_COPPER_FIBER,     /* 3 */
+       MAX_MEDIA_TYPE,
+};
+
+struct bnxt_fw_header {
+       __le32 signature;       /* constains the constant value of
+                                * BNXT_Firmware_Bin_Signatures
+                                */
+       u8 flags;               /* reserved for ChiMP use */
+       u8 code_type;           /* enum SUPPORTED_CODE */
+       u8 device;              /* enum SUPPORTED_FAMILY */
+       u8 media;               /* enum SUPPORTED_MEDIA */
+       u8 version[16];         /* the null terminated version string to
+                                * indicate the version of the
+                                * file, this will be copied from the binary
+                                * file version string
+                                */
+       u8 build;
+       u8 revision;
+       u8 minor_ver;
+       u8 major_ver;
+};
+
+#endif
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
new file mode 100644 (file)
index 0000000..70fc825
--- /dev/null
@@ -0,0 +1,4046 @@
+/* Broadcom NetXtreme-C/E network driver.
+ *
+ * Copyright (c) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ */
+
+#ifndef BNXT_HSI_H
+#define BNXT_HSI_H
+
+/* per-context HW statistics -- chip view */
+struct ctx_hw_stats  {
+       __le64 rx_ucast_pkts;
+       __le64 rx_mcast_pkts;
+       __le64 rx_bcast_pkts;
+       __le64 rx_discard_pkts;
+       __le64 rx_drop_pkts;
+       __le64 rx_ucast_bytes;
+       __le64 rx_mcast_bytes;
+       __le64 rx_bcast_bytes;
+       __le64 tx_ucast_pkts;
+       __le64 tx_mcast_pkts;
+       __le64 tx_bcast_pkts;
+       __le64 tx_discard_pkts;
+       __le64 tx_drop_pkts;
+       __le64 tx_ucast_bytes;
+       __le64 tx_mcast_bytes;
+       __le64 tx_bcast_bytes;
+       __le64 tpa_pkts;
+       __le64 tpa_bytes;
+       __le64 tpa_events;
+       __le64 tpa_aborts;
+};
+
+/* Statistics Ejection Buffer Completion Record (16 bytes) */
+struct eject_cmpl {
+       __le16 type;
+       #define EJECT_CMPL_TYPE_MASK                                0x3fUL
+       #define EJECT_CMPL_TYPE_SFT                                 0
+       #define EJECT_CMPL_TYPE_STAT_EJECT                         (0x1aUL << 0)
+       __le16 len;
+       __le32 opaque;
+       __le32 v;
+       #define EJECT_CMPL_V                                        0x1UL
+       __le32 unused_2;
+};
+
+/* HWRM Completion Record (16 bytes) */
+struct hwrm_cmpl {
+       __le16 type;
+       #define HWRM_CMPL_TYPE_MASK                                 0x3fUL
+       #define HWRM_CMPL_TYPE_SFT                                  0
+       #define HWRM_CMPL_TYPE_HWRM_DONE                           (0x20UL << 0)
+       __le16 sequence_id;
+       __le32 unused_1;
+       __le32 v;
+       #define HWRM_CMPL_V                                         0x1UL
+       __le32 unused_3;
+};
+
+/* HWRM Forwarded Request (16 bytes) */
+struct hwrm_fwd_req_cmpl {
+       __le16 req_len_type;
+       #define HWRM_FWD_REQ_CMPL_TYPE_MASK                         0x3fUL
+       #define HWRM_FWD_REQ_CMPL_TYPE_SFT                          0
+       #define HWRM_FWD_REQ_CMPL_TYPE_HWRM_FWD_REQ                (0x22UL << 0)
+       #define HWRM_FWD_REQ_CMPL_REQ_LEN_MASK                      0xffc0UL
+       #define HWRM_FWD_REQ_CMPL_REQ_LEN_SFT                       6
+       __le16 source_id;
+       __le32 unused_0;
+       __le32 req_buf_addr_v[2];
+       #define HWRM_FWD_REQ_CMPL_V                                 0x1UL
+       #define HWRM_FWD_REQ_CMPL_REQ_BUF_ADDR_MASK                 0xfffffffeUL
+       #define HWRM_FWD_REQ_CMPL_REQ_BUF_ADDR_SFT                  1
+};
+
+/* HWRM Forwarded Response (16 bytes) */
+struct hwrm_fwd_resp_cmpl {
+       __le16 type;
+       #define HWRM_FWD_RESP_CMPL_TYPE_MASK                        0x3fUL
+       #define HWRM_FWD_RESP_CMPL_TYPE_SFT                         0
+       #define HWRM_FWD_RESP_CMPL_TYPE_HWRM_FWD_RESP              (0x24UL << 0)
+       __le16 source_id;
+       __le16 resp_len;
+       __le16 unused_1;
+       __le32 resp_buf_addr_v[2];
+       #define HWRM_FWD_RESP_CMPL_V                                0x1UL
+       #define HWRM_FWD_RESP_CMPL_RESP_BUF_ADDR_MASK               0xfffffffeUL
+       #define HWRM_FWD_RESP_CMPL_RESP_BUF_ADDR_SFT                1
+};
+
+/* HWRM Asynchronous Event Completion Record (16 bytes) */
+struct hwrm_async_event_cmpl {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_TYPE_MASK             0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_TYPE_SFT                      0
+       #define HWRM_ASYNC_EVENT_CMPL_TYPE_HWRM_ASYNC_EVENT       (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE (0x0UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_MTU_CHANGE    (0x1UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CHANGE  (0x2UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_DCB_CONFIG_CHANGE  (0x3UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED (0x4UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_UNLOAD   (0x10UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_LOAD     (0x11UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD     (0x20UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_LOAD       (0x20UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_FLR              (0x30UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_MAC_ADDR_CHANGE (0x31UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR          (0xffUL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_V                     0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_OPAQUE_MASK                   0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_OPAQUE_SFT                    1
+       u8 unused_1[3];
+       __le32 event_data1;
+};
+
+/* HWRM Asynchronous Event Completion Record for link status change (16 bytes) */
+struct hwrm_async_event_cmpl_link_status_change {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_TYPE_MASK 0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_TYPE_SFT  0
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_ID_LINK_STATUS_CHANGE (0x0UL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_V          0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_MASK 0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_SFT 1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_UP 0x1UL
+};
+
+/* HWRM Asynchronous Event Completion Record for link MTU change (16 bytes) */
+struct hwrm_async_event_cmpl_link_mtu_change {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_TYPE_MASK    0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_TYPE_SFT     0
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_ID_LINK_MTU_CHANGE (0x1UL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_V     0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_MASK  0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_SFT   1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_MASK 0xffffUL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_SFT 0
+};
+
+/* HWRM Asynchronous Event Completion Record for link speed change (16 bytes) */
+struct hwrm_async_event_cmpl_link_speed_change {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_TYPE_MASK  0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_TYPE_SFT   0
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_ID_LINK_SPEED_CHANGE (0x2UL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_V           0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_MASK 0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_SFT 1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_FORCE 0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_MASK 0xfffeUL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_SFT 1
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_100MB (0x1UL << 1)
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_1GB (0xaUL << 1)
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_2GB (0x14UL << 1)
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_2_5GB (0x19UL << 1)
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_10GB (0x64UL << 1)
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_20GB (0xc8UL << 1)
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_25GB (0xfaUL << 1)
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_40GB (0x190UL << 1)
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_50GB (0x1f4UL << 1)
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffff0000UL
+       #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_PORT_ID_SFT 16
+};
+
+/* HWRM Asynchronous Event Completion Record for DCB Config change (16 bytes) */
+struct hwrm_async_event_cmpl_dcb_config_change {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_TYPE_MASK  0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_TYPE_SFT   0
+       #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_ID_DCB_CONFIG_CHANGE (0x3UL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_V           0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_MASK 0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_SFT 1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffffUL
+       #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_SFT 0
+};
+
+/* HWRM Asynchronous Event Completion Record for port connection not allowed (16 bytes) */
+struct hwrm_async_event_cmpl_port_conn_not_allowed {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_TYPE_MASK 0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_TYPE_SFT 0
+       #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_ID_PORT_CONN_NOT_ALLOWED (0x4UL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_V      0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_MASK 0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_SFT 1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_MASK 0xffffUL
+       #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_SFT 0
+};
+
+/* HWRM Asynchronous Event Completion Record for Function Driver Unload (16 bytes) */
+struct hwrm_async_event_cmpl_func_drvr_unload {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_TYPE_MASK   0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_TYPE_SFT    0
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_ID_FUNC_DRVR_UNLOAD (0x10UL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_V            0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_SFT  1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0
+};
+
+/* HWRM Asynchronous Event Completion Record for Function Driver load (16 bytes) */
+struct hwrm_async_event_cmpl_func_drvr_load {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_TYPE_MASK     0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_TYPE_SFT      0
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_ID_FUNC_DRVR_LOAD (0x11UL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_V              0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_MASK   0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_SFT    1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
+       #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0
+};
+
+/* HWRM Asynchronous Event Completion Record for PF Driver Unload (16 bytes) */
+struct hwrm_async_event_cmpl_pf_drvr_unload {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_MASK     0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_SFT      0
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_ID_PF_DRVR_UNLOAD (0x20UL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_V              0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_MASK   0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_SFT    1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0
+};
+
+/* HWRM Asynchronous Event Completion Record for PF Driver load (16 bytes) */
+struct hwrm_async_event_cmpl_pf_drvr_load {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_MASK       0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_SFT         0
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_ID_PF_DRVR_LOAD (0x20UL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_V                0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_MASK     0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_SFT      1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
+       #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0
+};
+
+/* HWRM Asynchronous Event Completion Record for VF FLR (16 bytes) */
+struct hwrm_async_event_cmpl_vf_flr {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_TYPE_MASK              0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_TYPE_SFT               0
+       #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_ID_VF_FLR      (0x30UL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_V                      0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_MASK            0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_SFT     1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_MASK 0xffffUL
+       #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_SFT 0
+};
+
+/* HWRM Asynchronous Event Completion Record for VF MAC Addr change (16 bytes) */
+struct hwrm_async_event_cmpl_vf_mac_addr_change {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_TYPE_MASK 0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_TYPE_SFT  0
+       #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_ID_VF_MAC_ADDR_CHANGE (0x31UL << 0)
+       __le32 event_data2;
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_V          0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_MASK 0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_SFT 1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_MASK 0xffffUL
+       #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_SFT 0
+};
+
+/* HWRM Asynchronous Event Completion Record for HWRM Error (16 bytes) */
+struct hwrm_async_event_cmpl_hwrm_error {
+       __le16 type;
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_TYPE_MASK          0x3fUL
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_TYPE_SFT           0
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
+       __le16 event_id;
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_ID_HWRM_ERROR (0xffUL << 0)
+       __le32 event_data2;
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_MASK 0xffUL
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_SFT 0
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_WARNING (0x0UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_NONFATAL (0x1UL << 0)
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_FATAL (0x2UL << 0)
+       u8 opaque_v;
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_V                  0x1UL
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_MASK       0xfeUL
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_SFT         1
+       u8 unused_1[3];
+       __le32 event_data1;
+       #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA1_TIMESTAMP 0x1UL
+};
+
+/* HW Resource Manager Specification 0.7.8 */
+#define HWRM_VERSION_MAJOR     0
+#define HWRM_VERSION_MINOR     7
+#define HWRM_VERSION_UPDATE    8
+
+#define HWRM_VERSION_STR       "0.7.8"
+/* Following is the signature for HWRM message field that indicates not
+ * applicable (All F's). Need to cast it the size of the field if needed.
+ */
+#define HWRM_NA_SIGNATURE      ((__le32)(-1))
+#define HWRM_MAX_REQ_LEN    (128)  /* hwrm_func_buf_rgtr */
+#define HWRM_MAX_RESP_LEN    (176)  /* hwrm_func_qstats */
+#define HW_HASH_INDEX_SIZE      0x80    /* 7 bit indirection table index. */
+#define HW_HASH_KEY_SIZE       40
+#define HWRM_RESP_VALID_KEY      1 /* valid key for HWRM response */
+/* Input (16 bytes) */
+struct input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+};
+
+/* Output (8 bytes) */
+struct output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+};
+
+/* Command numbering (8 bytes) */
+struct cmd_nums {
+       __le16 req_type;
+       #define HWRM_VER_GET                                       (0x0UL)
+       #define HWRM_FUNC_DISABLE                                  (0x10UL)
+       #define HWRM_FUNC_RESET                            (0x11UL)
+       #define HWRM_FUNC_GETFID                                   (0x12UL)
+       #define HWRM_FUNC_VF_ALLOC                                 (0x13UL)
+       #define HWRM_FUNC_VF_FREE                                  (0x14UL)
+       #define HWRM_FUNC_QCAPS                            (0x15UL)
+       #define HWRM_FUNC_QCFG                                     (0x16UL)
+       #define HWRM_FUNC_CFG                                      (0x17UL)
+       #define HWRM_FUNC_QSTATS                                   (0x18UL)
+       #define HWRM_FUNC_CLR_STATS                                (0x19UL)
+       #define HWRM_FUNC_DRV_UNRGTR                               (0x1aUL)
+       #define HWRM_FUNC_VF_RESC_FREE                             (0x1bUL)
+       #define HWRM_FUNC_VF_VNIC_IDS_QUERY                        (0x1cUL)
+       #define HWRM_FUNC_DRV_RGTR                                 (0x1dUL)
+       #define HWRM_FUNC_DRV_QVER                                 (0x1eUL)
+       #define HWRM_FUNC_BUF_RGTR                                 (0x1fUL)
+       #define HWRM_FUNC_VF_CFG                                   (0x20UL)
+       #define HWRM_PORT_PHY_CFG                                  (0x20UL)
+       #define HWRM_PORT_MAC_CFG                                  (0x21UL)
+       #define HWRM_PORT_ENABLE                                   (0x22UL)
+       #define HWRM_PORT_QSTATS                                   (0x23UL)
+       #define HWRM_PORT_LPBK_QSTATS                              (0x24UL)
+       #define HWRM_PORT_CLR_STATS                                (0x25UL)
+       #define HWRM_PORT_LPBK_CLR_STATS                           (0x26UL)
+       #define HWRM_PORT_PHY_QCFG                                 (0x27UL)
+       #define HWRM_PORT_MAC_QCFG                                 (0x28UL)
+       #define HWRM_PORT_BLINK_LED                                (0x29UL)
+       #define HWRM_QUEUE_QPORTCFG                                (0x30UL)
+       #define HWRM_QUEUE_QCFG                            (0x31UL)
+       #define HWRM_QUEUE_CFG                                     (0x32UL)
+       #define HWRM_QUEUE_BUFFERS_QCFG                    (0x33UL)
+       #define HWRM_QUEUE_BUFFERS_CFG                             (0x34UL)
+       #define HWRM_QUEUE_PFCENABLE_QCFG                          (0x35UL)
+       #define HWRM_QUEUE_PFCENABLE_CFG                           (0x36UL)
+       #define HWRM_QUEUE_PRI2COS_QCFG                    (0x37UL)
+       #define HWRM_QUEUE_PRI2COS_CFG                             (0x38UL)
+       #define HWRM_QUEUE_COS2BW_QCFG                             (0x39UL)
+       #define HWRM_QUEUE_COS2BW_CFG                              (0x3aUL)
+       #define HWRM_VNIC_ALLOC                            (0x40UL)
+       #define HWRM_VNIC_FREE                                     (0x41UL)
+       #define HWRM_VNIC_CFG                                      (0x42UL)
+       #define HWRM_VNIC_QCFG                                     (0x43UL)
+       #define HWRM_VNIC_TPA_CFG                                  (0x44UL)
+       #define HWRM_VNIC_TPA_QCFG                                 (0x45UL)
+       #define HWRM_VNIC_RSS_CFG                                  (0x46UL)
+       #define HWRM_VNIC_RSS_QCFG                                 (0x47UL)
+       #define HWRM_VNIC_PLCMODES_CFG                             (0x48UL)
+       #define HWRM_VNIC_PLCMODES_QCFG                    (0x49UL)
+       #define HWRM_RING_ALLOC                            (0x50UL)
+       #define HWRM_RING_FREE                                     (0x51UL)
+       #define HWRM_RING_CMPL_RING_QAGGINT_PARAMS                 (0x52UL)
+       #define HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS              (0x53UL)
+       #define HWRM_RING_RESET                            (0x5eUL)
+       #define HWRM_RING_GRP_ALLOC                                (0x60UL)
+       #define HWRM_RING_GRP_FREE                                 (0x61UL)
+       #define HWRM_VNIC_RSS_COS_LB_CTX_ALLOC                     (0x70UL)
+       #define HWRM_VNIC_RSS_COS_LB_CTX_FREE                      (0x71UL)
+       #define HWRM_ARB_GRP_ALLOC                                 (0x80UL)
+       #define HWRM_ARB_GRP_CFG                                   (0x81UL)
+       #define HWRM_CFA_L2_FILTER_ALLOC                           (0x90UL)
+       #define HWRM_CFA_L2_FILTER_FREE                    (0x91UL)
+       #define HWRM_CFA_L2_FILTER_CFG                             (0x92UL)
+       #define HWRM_CFA_L2_SET_RX_MASK                    (0x93UL)
+       #define HWRM_CFA_L2_SET_BCASTMCAST_MIRRORING               (0x94UL)
+       #define HWRM_CFA_TUNNEL_FILTER_ALLOC                       (0x95UL)
+       #define HWRM_CFA_TUNNEL_FILTER_FREE                        (0x96UL)
+       #define HWRM_CFA_ENCAP_RECORD_ALLOC                        (0x97UL)
+       #define HWRM_CFA_ENCAP_RECORD_FREE                         (0x98UL)
+       #define HWRM_CFA_NTUPLE_FILTER_ALLOC                       (0x99UL)
+       #define HWRM_CFA_NTUPLE_FILTER_FREE                        (0x9aUL)
+       #define HWRM_CFA_NTUPLE_FILTER_CFG                         (0x9bUL)
+       #define HWRM_TUNNEL_DST_PORT_QUERY                         (0xa0UL)
+       #define HWRM_TUNNEL_DST_PORT_ALLOC                         (0xa1UL)
+       #define HWRM_TUNNEL_DST_PORT_FREE                          (0xa2UL)
+       #define HWRM_STAT_CTX_ALLOC                                (0xb0UL)
+       #define HWRM_STAT_CTX_FREE                                 (0xb1UL)
+       #define HWRM_STAT_CTX_QUERY                                (0xb2UL)
+       #define HWRM_STAT_CTX_CLR_STATS                    (0xb3UL)
+       #define HWRM_FW_RESET                                      (0xc0UL)
+       #define HWRM_FW_QSTATUS                            (0xc1UL)
+       #define HWRM_EXEC_FWD_RESP                                 (0xd0UL)
+       #define HWRM_REJECT_FWD_RESP                               (0xd1UL)
+       #define HWRM_FWD_RESP                                      (0xd2UL)
+       #define HWRM_FWD_ASYNC_EVENT_CMPL                          (0xd3UL)
+       #define HWRM_TEMP_MONITOR_QUERY                    (0xe0UL)
+       #define HWRM_MGMT_L2_FILTER_ALLOC                          (0x100UL)
+       #define HWRM_MGMT_L2_FILTER_FREE                           (0x101UL)
+       #define HWRM_DBG_READ_DIRECT                               (0xff10UL)
+       #define HWRM_DBG_READ_INDIRECT                             (0xff11UL)
+       #define HWRM_DBG_WRITE_DIRECT                              (0xff12UL)
+       #define HWRM_DBG_WRITE_INDIRECT                    (0xff13UL)
+       #define HWRM_DBG_DUMP                                      (0xff14UL)
+       #define HWRM_NVM_MODIFY                            (0xfff4UL)
+       #define HWRM_NVM_VERIFY_UPDATE                             (0xfff5UL)
+       #define HWRM_NVM_GET_DEV_INFO                              (0xfff6UL)
+       #define HWRM_NVM_ERASE_DIR_ENTRY                           (0xfff7UL)
+       #define HWRM_NVM_MOD_DIR_ENTRY                             (0xfff8UL)
+       #define HWRM_NVM_FIND_DIR_ENTRY                    (0xfff9UL)
+       #define HWRM_NVM_GET_DIR_ENTRIES                           (0xfffaUL)
+       #define HWRM_NVM_GET_DIR_INFO                              (0xfffbUL)
+       #define HWRM_NVM_RAW_DUMP                                  (0xfffcUL)
+       #define HWRM_NVM_READ                                      (0xfffdUL)
+       #define HWRM_NVM_WRITE                                     (0xfffeUL)
+       #define HWRM_NVM_RAW_WRITE_BLK                             (0xffffUL)
+       __le16 unused_0[3];
+};
+
+/* Return Codes (8 bytes) */
+struct ret_codes {
+       __le16 error_code;
+       #define HWRM_ERR_CODE_SUCCESS                              (0x0UL)
+       #define HWRM_ERR_CODE_FAIL                                 (0x1UL)
+       #define HWRM_ERR_CODE_INVALID_PARAMS                       (0x2UL)
+       #define HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED               (0x3UL)
+       #define HWRM_ERR_CODE_RESOURCE_ALLOC_ERROR                 (0x4UL)
+       #define HWRM_ERR_CODE_INVALID_FLAGS                        (0x5UL)
+       #define HWRM_ERR_CODE_INVALID_ENABLES                      (0x6UL)
+       #define HWRM_ERR_CODE_HWRM_ERROR                           (0xfUL)
+       #define HWRM_ERR_CODE_UNKNOWN_ERR                          (0xfffeUL)
+       #define HWRM_ERR_CODE_CMD_NOT_SUPPORTED            (0xffffUL)
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_err_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 opaque_0;
+       __le16 opaque_1;
+       u8 opaque_2;
+       u8 valid;
+};
+
+/* Port Tx Statistics Formats (408 bytes) */
+struct tx_port_stats {
+       __le64 tx_64b_frames;
+       __le64 tx_65b_127b_frames;
+       __le64 tx_128b_255b_frames;
+       __le64 tx_256b_511b_frames;
+       __le64 tx_512b_1023b_frames;
+       __le64 tx_1024b_1518_frames;
+       __le64 tx_good_vlan_frames;
+       __le64 tx_1519b_2047_frames;
+       __le64 tx_2048b_4095b_frames;
+       __le64 tx_4096b_9216b_frames;
+       __le64 tx_9217b_16383b_frames;
+       __le64 tx_good_frames;
+       __le64 tx_total_frames;
+       __le64 tx_ucast_frames;
+       __le64 tx_mcast_frames;
+       __le64 tx_bcast_frames;
+       __le64 tx_pause_frames;
+       __le64 tx_pfc_frames;
+       __le64 tx_jabber_frames;
+       __le64 tx_fcs_err_frames;
+       __le64 tx_control_frames;
+       __le64 tx_oversz_frames;
+       __le64 tx_single_dfrl_frames;
+       __le64 tx_multi_dfrl_frames;
+       __le64 tx_single_coll_frames;
+       __le64 tx_multi_coll_frames;
+       __le64 tx_late_coll_frames;
+       __le64 tx_excessive_coll_frames;
+       __le64 tx_frag_frames;
+       __le64 tx_err;
+       __le64 tx_tagged_frames;
+       __le64 tx_dbl_tagged_frames;
+       __le64 tx_runt_frames;
+       __le64 tx_fifo_underruns;
+       __le64 tx_pfc_ena_frames_pri0;
+       __le64 tx_pfc_ena_frames_pri1;
+       __le64 tx_pfc_ena_frames_pri2;
+       __le64 tx_pfc_ena_frames_pri3;
+       __le64 tx_pfc_ena_frames_pri4;
+       __le64 tx_pfc_ena_frames_pri5;
+       __le64 tx_pfc_ena_frames_pri6;
+       __le64 tx_pfc_ena_frames_pri7;
+       __le64 tx_eee_lpi_events;
+       __le64 tx_eee_lpi_duration;
+       __le64 tx_llfc_logical_msgs;
+       __le64 tx_hcfc_msgs;
+       __le64 tx_total_collisions;
+       __le64 tx_bytes;
+       __le64 tx_xthol_frames;
+       __le64 tx_stat_discard;
+       __le64 tx_stat_error;
+};
+
+/* Port Rx Statistics Formats (528 bytes) */
+struct rx_port_stats {
+       __le64 rx_64b_frames;
+       __le64 rx_65b_127b_frames;
+       __le64 rx_128b_255b_frames;
+       __le64 rx_256b_511b_frames;
+       __le64 rx_512b_1023b_frames;
+       __le64 rx_1024b_1518_frames;
+       __le64 rx_good_vlan_frames;
+       __le64 rx_1519b_2047b_frames;
+       __le64 rx_2048b_4095b_frames;
+       __le64 rx_4096b_9216b_frames;
+       __le64 rx_9217b_16383b_frames;
+       __le64 rx_total_frames;
+       __le64 rx_ucast_frames;
+       __le64 rx_mcast_frames;
+       __le64 rx_bcast_frames;
+       __le64 rx_fcs_err_frames;
+       __le64 rx_ctrl_frames;
+       __le64 rx_pause_frames;
+       __le64 rx_pfc_frames;
+       __le64 rx_unsupported_opcode_frames;
+       __le64 rx_unsupported_da_pausepfc_frames;
+       __le64 rx_wrong_sa_frames;
+       __le64 rx_align_err_frames;
+       __le64 rx_oor_len_frames;
+       __le64 rx_code_err_frames;
+       __le64 rx_false_carrier_frames;
+       __le64 rx_ovrsz_frames;
+       __le64 rx_jbr_frames;
+       __le64 rx_mtu_err_frames;
+       __le64 rx_match_crc_frames;
+       __le64 rx_promiscuous_frames;
+       __le64 rx_tagged_frames;
+       __le64 rx_double_tagged_frames;
+       __le64 rx_trunc_frames;
+       __le64 rx_good_frames;
+       __le64 rx_pfc_xon2xoff_frames_pri0;
+       __le64 rx_pfc_xon2xoff_frames_pri1;
+       __le64 rx_pfc_xon2xoff_frames_pri2;
+       __le64 rx_pfc_xon2xoff_frames_pri3;
+       __le64 rx_pfc_xon2xoff_frames_pri4;
+       __le64 rx_pfc_xon2xoff_frames_pri5;
+       __le64 rx_pfc_xon2xoff_frames_pri6;
+       __le64 rx_pfc_xon2xoff_frames_pri7;
+       __le64 rx_pfc_ena_frames_pri0;
+       __le64 rx_pfc_ena_frames_pri1;
+       __le64 rx_pfc_ena_frames_pri2;
+       __le64 rx_pfc_ena_frames_pri3;
+       __le64 rx_pfc_ena_frames_pri4;
+       __le64 rx_pfc_ena_frames_pri5;
+       __le64 rx_pfc_ena_frames_pri6;
+       __le64 rx_pfc_ena_frames_pri7;
+       __le64 rx_sch_crc_err_frames;
+       __le64 rx_undrsz_frames;
+       __le64 rx_frag_frames;
+       __le64 rx_eee_lpi_events;
+       __le64 rx_eee_lpi_duration;
+       __le64 rx_llfc_physical_msgs;
+       __le64 rx_llfc_logical_msgs;
+       __le64 rx_llfc_msgs_with_crc_err;
+       __le64 rx_hcfc_msgs;
+       __le64 rx_hcfc_msgs_with_crc_err;
+       __le64 rx_bytes;
+       __le64 rx_runt_bytes;
+       __le64 rx_runt_frames;
+       __le64 rx_stat_discard;
+       __le64 rx_stat_err;
+};
+
+/* hwrm_ver_get */
+/* Input (24 bytes) */
+struct hwrm_ver_get_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       u8 hwrm_intf_maj;
+       u8 hwrm_intf_min;
+       u8 hwrm_intf_upd;
+       u8 unused_0[5];
+};
+
+/* Output (128 bytes) */
+struct hwrm_ver_get_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       u8 hwrm_intf_maj;
+       u8 hwrm_intf_min;
+       u8 hwrm_intf_upd;
+       u8 hwrm_intf_rsvd;
+       u8 hwrm_fw_maj;
+       u8 hwrm_fw_min;
+       u8 hwrm_fw_bld;
+       u8 hwrm_fw_rsvd;
+       u8 ape_fw_maj;
+       u8 ape_fw_min;
+       u8 ape_fw_bld;
+       u8 ape_fw_rsvd;
+       u8 kong_fw_maj;
+       u8 kong_fw_min;
+       u8 kong_fw_bld;
+       u8 kong_fw_rsvd;
+       u8 tang_fw_maj;
+       u8 tang_fw_min;
+       u8 tang_fw_bld;
+       u8 tang_fw_rsvd;
+       u8 bono_fw_maj;
+       u8 bono_fw_min;
+       u8 bono_fw_bld;
+       u8 bono_fw_rsvd;
+       char hwrm_fw_name[16];
+       char ape_fw_name[16];
+       char kong_fw_name[16];
+       char tang_fw_name[16];
+       char bono_fw_name[16];
+       __le16 chip_num;
+       u8 chip_rev;
+       u8 chip_metal;
+       u8 chip_bond_id;
+       u8 unused_0;
+       __le16 max_req_win_len;
+       __le16 max_resp_len;
+       __le16 def_req_timeout;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_disable */
+/* Input (24 bytes) */
+struct hwrm_func_disable_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define FUNC_DISABLE_REQ_ENABLES_VF_ID_VALID                0x1UL
+       __le16 vf_id;
+       __le16 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_disable_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_reset */
+/* Input (24 bytes) */
+struct hwrm_func_reset_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define FUNC_RESET_REQ_ENABLES_VF_ID_VALID                  0x1UL
+       __le16 vf_id;
+       __le16 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_reset_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_getfid */
+/* Input (24 bytes) */
+struct hwrm_func_getfid_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define FUNC_GETFID_REQ_ENABLES_PCI_ID                      0x1UL
+       __le16 pci_id;
+       __le16 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_getfid_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 fid;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 unused_4;
+       u8 valid;
+};
+
+/* hwrm_func_vf_alloc */
+/* Input (24 bytes) */
+struct hwrm_func_vf_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define FUNC_VF_ALLOC_REQ_ENABLES_FIRST_VF_ID               0x1UL
+       __le16 first_vf_id;
+       __le16 num_vfs;
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_vf_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 first_vf_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 unused_4;
+       u8 valid;
+};
+
+/* hwrm_func_vf_free */
+/* Input (24 bytes) */
+struct hwrm_func_vf_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define FUNC_VF_FREE_REQ_ENABLES_FIRST_VF_ID                0x1UL
+       __le16 first_vf_id;
+       __le16 num_vfs;
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_vf_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_vf_cfg */
+/* Input (24 bytes) */
+struct hwrm_func_vf_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define FUNC_VF_CFG_REQ_ENABLES_MTU                         0x1UL
+       #define FUNC_VF_CFG_REQ_ENABLES_GUEST_VLAN                  0x2UL
+       __le16 mtu;
+       __le16 guest_vlan;
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_vf_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_qcaps */
+/* Input (24 bytes) */
+struct hwrm_func_qcaps_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 fid;
+       __le16 unused_0[3];
+};
+
+/* Output (80 bytes) */
+struct hwrm_func_qcaps_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 fid;
+       __le16 port_id;
+       __le32 flags;
+       #define FUNC_QCAPS_RESP_FLAGS_PUSH_MODE_SUPPORTED           0x1UL
+       #define FUNC_QCAPS_RESP_FLAGS_GLOBAL_MSIX_AUTOMASKING      0x2UL
+       u8 perm_mac_address[6];
+       __le16 max_rsscos_ctx;
+       __le16 max_cmpl_rings;
+       __le16 max_tx_rings;
+       __le16 max_rx_rings;
+       __le16 max_l2_ctxs;
+       __le16 max_vnics;
+       __le16 first_vf_id;
+       __le16 max_vfs;
+       __le16 max_stat_ctx;
+       __le32 max_encap_records;
+       __le32 max_decap_records;
+       __le32 max_tx_em_flows;
+       __le32 max_tx_wm_flows;
+       __le32 max_rx_em_flows;
+       __le32 max_rx_wm_flows;
+       __le32 max_mcast_filters;
+       __le32 max_flow_id;
+       __le32 max_hw_ring_grps;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_func_cfg */
+/* Input (88 bytes) */
+struct hwrm_func_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 vf_id;
+       u8 unused_0;
+       u8 unused_1;
+       __le32 flags;
+       #define FUNC_CFG_REQ_FLAGS_PROM_MODE                        0x1UL
+       #define FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK               0x2UL
+       #define FUNC_CFG_REQ_FLAGS_SRC_IP_ADDR_CHECK                0x4UL
+       #define FUNC_CFG_REQ_FLAGS_VLAN_PRI_MATCH                   0x8UL
+       #define FUNC_CFG_REQ_FLAGS_DFLT_PRI_NOMATCH                 0x10UL
+       #define FUNC_CFG_REQ_FLAGS_DISABLE_PAUSE                    0x20UL
+       #define FUNC_CFG_REQ_FLAGS_DISABLE_STP                      0x40UL
+       #define FUNC_CFG_REQ_FLAGS_DISABLE_LLDP             0x80UL
+       #define FUNC_CFG_REQ_FLAGS_DISABLE_PTPV2                    0x100UL
+       __le32 enables;
+       #define FUNC_CFG_REQ_ENABLES_MTU                            0x1UL
+       #define FUNC_CFG_REQ_ENABLES_MRU                            0x2UL
+       #define FUNC_CFG_REQ_ENABLES_NUM_RSSCOS_CTXS                0x4UL
+       #define FUNC_CFG_REQ_ENABLES_NUM_CMPL_RINGS                 0x8UL
+       #define FUNC_CFG_REQ_ENABLES_NUM_TX_RINGS                   0x10UL
+       #define FUNC_CFG_REQ_ENABLES_NUM_RX_RINGS                   0x20UL
+       #define FUNC_CFG_REQ_ENABLES_NUM_L2_CTXS                    0x40UL
+       #define FUNC_CFG_REQ_ENABLES_NUM_VNICS                      0x80UL
+       #define FUNC_CFG_REQ_ENABLES_NUM_STAT_CTXS                  0x100UL
+       #define FUNC_CFG_REQ_ENABLES_DFLT_MAC_ADDR                  0x200UL
+       #define FUNC_CFG_REQ_ENABLES_DFLT_VLAN                      0x400UL
+       #define FUNC_CFG_REQ_ENABLES_DFLT_IP_ADDR                   0x800UL
+       #define FUNC_CFG_REQ_ENABLES_MIN_BW                         0x1000UL
+       #define FUNC_CFG_REQ_ENABLES_MAX_BW                         0x2000UL
+       #define FUNC_CFG_REQ_ENABLES_ASYNC_EVENT_CR                 0x4000UL
+       #define FUNC_CFG_REQ_ENABLES_VLAN_ANTISPOOF_MODE            0x8000UL
+       #define FUNC_CFG_REQ_ENABLES_ALLOWED_VLAN_PRIS              0x10000UL
+       #define FUNC_CFG_REQ_ENABLES_EVB_MODE                       0x20000UL
+       #define FUNC_CFG_REQ_ENABLES_NUM_MCAST_FILTERS              0x40000UL
+       #define FUNC_CFG_REQ_ENABLES_NUM_HW_RING_GRPS               0x80000UL
+       __le16 mtu;
+       __le16 mru;
+       __le16 num_rsscos_ctxs;
+       __le16 num_cmpl_rings;
+       __le16 num_tx_rings;
+       __le16 num_rx_rings;
+       __le16 num_l2_ctxs;
+       __le16 num_vnics;
+       __le16 num_stat_ctxs;
+       __le16 num_hw_ring_grps;
+       u8 dflt_mac_addr[6];
+       __le16 dflt_vlan;
+       __be32 dflt_ip_addr[4];
+       __le32 min_bw;
+       __le32 max_bw;
+       __le16 async_event_cr;
+       u8 vlan_antispoof_mode;
+       #define FUNC_CFG_REQ_VLAN_ANTISPOOF_MODE_NOCHECK           (0x0UL << 0)
+       #define FUNC_CFG_REQ_VLAN_ANTISPOOF_MODE_VALIDATE_VLAN    (0x1UL << 0)
+       #define FUNC_CFG_REQ_VLAN_ANTISPOOF_MODE_INSERT_IF_VLANDNE (0x2UL << 0)
+       #define FUNC_CFG_REQ_VLAN_ANTISPOOF_MODE_INSERT_OR_OVERRIDE_VLAN (0x3UL << 0)
+       u8 allowed_vlan_pris;
+       #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_NOCHECK             (0x0UL << 0)
+       #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_VALIDATE_VLAN      (0x1UL << 0)
+       #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_INSERT_IF_VLANDNE  (0x2UL << 0)
+       #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_INSERT_OR_OVERRIDE_VLAN (0x3UL << 0)
+       u8 evb_mode;
+       #define FUNC_CFG_REQ_EVB_MODE_NO_EVB                       (0x0UL << 0)
+       #define FUNC_CFG_REQ_EVB_MODE_VEB                          (0x1UL << 0)
+       #define FUNC_CFG_REQ_EVB_MODE_VEPA                         (0x2UL << 0)
+       u8 unused_2;
+       __le16 num_mcast_filters;
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_qstats */
+/* Input (24 bytes) */
+struct hwrm_func_qstats_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 fid;
+       __le16 unused_0[3];
+};
+
+/* Output (176 bytes) */
+struct hwrm_func_qstats_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le64 tx_ucast_pkts;
+       __le64 tx_mcast_pkts;
+       __le64 tx_bcast_pkts;
+       __le64 tx_err_pkts;
+       __le64 tx_drop_pkts;
+       __le64 tx_ucast_bytes;
+       __le64 tx_mcast_bytes;
+       __le64 tx_bcast_bytes;
+       __le64 rx_ucast_pkts;
+       __le64 rx_mcast_pkts;
+       __le64 rx_bcast_pkts;
+       __le64 rx_err_pkts;
+       __le64 rx_drop_pkts;
+       __le64 rx_ucast_bytes;
+       __le64 rx_mcast_bytes;
+       __le64 rx_bcast_bytes;
+       __le64 rx_agg_pkts;
+       __le64 rx_agg_bytes;
+       __le64 rx_agg_events;
+       __le64 rx_agg_aborts;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_clr_stats */
+/* Input (24 bytes) */
+struct hwrm_func_clr_stats_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 fid;
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_clr_stats_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_vf_resc_free */
+/* Input (24 bytes) */
+struct hwrm_func_vf_resc_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 vf_id;
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_vf_resc_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_vf_vnic_ids_query */
+/* Input (32 bytes) */
+struct hwrm_func_vf_vnic_ids_query_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 vf_id;
+       u8 unused_0;
+       u8 unused_1;
+       __le32 max_vnic_id_cnt;
+       __le64 vnic_id_tbl_addr;
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_vf_vnic_ids_query_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 vnic_id_cnt;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_func_drv_rgtr */
+/* Input (80 bytes) */
+struct hwrm_func_drv_rgtr_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define FUNC_DRV_RGTR_REQ_FLAGS_FWD_ALL_MODE                0x1UL
+       #define FUNC_DRV_RGTR_REQ_FLAGS_FWD_NONE_MODE               0x2UL
+       __le32 enables;
+       #define FUNC_DRV_RGTR_REQ_ENABLES_OS_TYPE                   0x1UL
+       #define FUNC_DRV_RGTR_REQ_ENABLES_VER                       0x2UL
+       #define FUNC_DRV_RGTR_REQ_ENABLES_TIMESTAMP                 0x4UL
+       #define FUNC_DRV_RGTR_REQ_ENABLES_VF_REQ_FWD                0x8UL
+       #define FUNC_DRV_RGTR_REQ_ENABLES_ASYNC_EVENT_FWD           0x10UL
+       __le16 os_type;
+       u8 ver_maj;
+       u8 ver_min;
+       u8 ver_upd;
+       u8 unused_0;
+       __le16 unused_1;
+       __le32 timestamp;
+       __le32 unused_2;
+       __le32 vf_req_fwd[8];
+       __le32 async_event_fwd[8];
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_drv_rgtr_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_drv_unrgtr */
+/* Input (24 bytes) */
+struct hwrm_func_drv_unrgtr_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define FUNC_DRV_UNRGTR_REQ_FLAGS_PREPARE_FOR_SHUTDOWN     0x1UL
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_drv_unrgtr_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_buf_rgtr */
+/* Input (128 bytes) */
+struct hwrm_func_buf_rgtr_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define FUNC_BUF_RGTR_REQ_ENABLES_VF_ID             0x1UL
+       #define FUNC_BUF_RGTR_REQ_ENABLES_ERR_BUF_ADDR              0x2UL
+       __le16 vf_id;
+       __le16 req_buf_num_pages;
+       __le16 req_buf_page_size;
+       #define FUNC_BUF_RGTR_REQ_REQ_BUF_PAGE_SIZE_16B    (0x4UL << 0)
+       #define FUNC_BUF_RGTR_REQ_REQ_BUF_PAGE_SIZE_4K             (0xcUL << 0)
+       #define FUNC_BUF_RGTR_REQ_REQ_BUF_PAGE_SIZE_8K             (0xdUL << 0)
+       #define FUNC_BUF_RGTR_REQ_REQ_BUF_PAGE_SIZE_64K    (0x10UL << 0)
+       #define FUNC_BUF_RGTR_REQ_REQ_BUF_PAGE_SIZE_2M             (0x16UL << 0)
+       #define FUNC_BUF_RGTR_REQ_REQ_BUF_PAGE_SIZE_4M             (0x17UL << 0)
+       #define FUNC_BUF_RGTR_REQ_REQ_BUF_PAGE_SIZE_1G             (0x1eUL << 0)
+       __le16 req_buf_len;
+       __le16 resp_buf_len;
+       u8 unused_0;
+       u8 unused_1;
+       __le64 req_buf_page_addr0;
+       __le64 req_buf_page_addr1;
+       __le64 req_buf_page_addr2;
+       __le64 req_buf_page_addr3;
+       __le64 req_buf_page_addr4;
+       __le64 req_buf_page_addr5;
+       __le64 req_buf_page_addr6;
+       __le64 req_buf_page_addr7;
+       __le64 req_buf_page_addr8;
+       __le64 req_buf_page_addr9;
+       __le64 error_buf_addr;
+       __le64 resp_buf_addr;
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_buf_rgtr_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_func_drv_qver */
+/* Input (24 bytes) */
+struct hwrm_func_drv_qver_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define FUNC_DRV_QVER_REQ_ENABLES_OS_TYPE_VALID     0x1UL
+       #define FUNC_DRV_QVER_REQ_ENABLES_VER_VALID                 0x2UL
+       __le16 fid;
+       __le16 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_func_drv_qver_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 os_type;
+       u8 ver_maj;
+       u8 ver_min;
+       u8 ver_upd;
+       u8 unused_0;
+       u8 unused_1;
+       u8 valid;
+};
+
+/* hwrm_port_phy_cfg */
+/* Input (48 bytes) */
+struct hwrm_port_phy_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define PORT_PHY_CFG_REQ_FLAGS_RESET_PHY                    0x1UL
+       #define PORT_PHY_CFG_REQ_FLAGS_FORCE_LINK_DOWN              0x2UL
+       #define PORT_PHY_CFG_REQ_FLAGS_FORCE                        0x4UL
+       #define PORT_PHY_CFG_REQ_FLAGS_RESTART_AUTONEG              0x8UL
+       __le32 enables;
+       #define PORT_PHY_CFG_REQ_ENABLES_AUTO_MODE                  0x1UL
+       #define PORT_PHY_CFG_REQ_ENABLES_AUTO_DUPLEX                0x2UL
+       #define PORT_PHY_CFG_REQ_ENABLES_AUTO_PAUSE                 0x4UL
+       #define PORT_PHY_CFG_REQ_ENABLES_AUTO_LINK_SPEED            0x8UL
+       #define PORT_PHY_CFG_REQ_ENABLES_AUTO_LINK_SPEED_MASK      0x10UL
+       #define PORT_PHY_CFG_REQ_ENABLES_WIRESPEED                  0x20UL
+       #define PORT_PHY_CFG_REQ_ENABLES_LPBK                       0x40UL
+       #define PORT_PHY_CFG_REQ_ENABLES_PREEMPHASIS                0x80UL
+       #define PORT_PHY_CFG_REQ_ENABLES_FORCE_PAUSE                0x100UL
+       __le16 port_id;
+       __le16 force_link_speed;
+       #define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_100MB    (0x1UL << 0)
+       #define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_1GB              (0xaUL << 0)
+       #define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_2GB              (0x14UL << 0)
+       #define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_2_5GB    (0x19UL << 0)
+       #define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_10GB             (0x64UL << 0)
+       #define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_20GB             (0xc8UL << 0)
+       #define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_25GB             (0xfaUL << 0)
+       #define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_40GB             (0x190UL << 0)
+       #define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_50GB             (0x1f4UL << 0)
+       u8 auto_mode;
+       #define PORT_PHY_CFG_REQ_AUTO_MODE_NONE            (0x0UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_MODE_ALL_SPEEDS              (0x1UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_MODE_ONE_SPEED               (0x2UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_MODE_ONE_OR_BELOW    (0x3UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_MODE_MASK            (0x4UL << 0)
+       u8 auto_duplex;
+       #define PORT_PHY_CFG_REQ_AUTO_DUPLEX_HALF                  (0x0UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_DUPLEX_FULL                  (0x1UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_DUPLEX_BOTH                  (0x2UL << 0)
+       u8 auto_pause;
+       #define PORT_PHY_CFG_REQ_AUTO_PAUSE_TX                      0x1UL
+       #define PORT_PHY_CFG_REQ_AUTO_PAUSE_RX                      0x2UL
+       u8 unused_0;
+       __le16 auto_link_speed;
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_100MB             (0x1UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_1GB               (0xaUL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_2GB               (0x14UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_2_5GB             (0x19UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_10GB              (0x64UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_20GB              (0xc8UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_25GB              (0xfaUL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_40GB              (0x190UL << 0)
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_50GB              (0x1f4UL << 0)
+       __le16 auto_link_speed_mask;
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_100MBHD      0x1UL
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_100MB         0x2UL
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_1GBHD         0x4UL
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_1GB           0x8UL
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_2GB           0x10UL
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_2_5GB         0x20UL
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_10GB          0x40UL
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_20GB          0x80UL
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_25GB          0x100UL
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_40GB          0x200UL
+       #define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_50GB          0x400UL
+       u8 wirespeed;
+       #define PORT_PHY_CFG_REQ_WIRESPEED_OFF                     (0x0UL << 0)
+       #define PORT_PHY_CFG_REQ_WIRESPEED_ON                      (0x1UL << 0)
+       u8 lpbk;
+       #define PORT_PHY_CFG_REQ_LPBK_NONE                         (0x0UL << 0)
+       #define PORT_PHY_CFG_REQ_LPBK_LOCAL                        (0x1UL << 0)
+       #define PORT_PHY_CFG_REQ_LPBK_REMOTE                       (0x2UL << 0)
+       u8 force_pause;
+       #define PORT_PHY_CFG_REQ_FORCE_PAUSE_TX             0x1UL
+       #define PORT_PHY_CFG_REQ_FORCE_PAUSE_RX             0x2UL
+       u8 unused_1;
+       __le32 preemphasis;
+       __le32 unused_2;
+};
+
+/* Output (16 bytes) */
+struct hwrm_port_phy_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_port_phy_qcfg */
+/* Input (24 bytes) */
+struct hwrm_port_phy_qcfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 port_id;
+       __le16 unused_0[3];
+};
+
+/* Output (48 bytes) */
+struct hwrm_port_phy_qcfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       u8 link;
+       #define PORT_PHY_QCFG_RESP_LINK_NO_LINK            (0x0UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_SIGNAL                     (0x1UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_LINK                       (0x2UL << 0)
+       u8 unused_0;
+       __le16 link_speed;
+       #define PORT_PHY_QCFG_RESP_LINK_SPEED_100MB                (0x1UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_SPEED_1GB                  (0xaUL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_SPEED_2GB                  (0x14UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_SPEED_2_5GB                (0x19UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_SPEED_10GB                 (0x64UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_SPEED_20GB                 (0xc8UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_SPEED_25GB                 (0xfaUL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_SPEED_40GB                 (0x190UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_SPEED_50GB                 (0x1f4UL << 0)
+       u8 duplex;
+       #define PORT_PHY_QCFG_RESP_DUPLEX_HALF                     (0x0UL << 0)
+       #define PORT_PHY_QCFG_RESP_DUPLEX_FULL                     (0x1UL << 0)
+       u8 pause;
+       #define PORT_PHY_QCFG_RESP_PAUSE_TX                         0x1UL
+       #define PORT_PHY_QCFG_RESP_PAUSE_RX                         0x2UL
+       __le16 support_speeds;
+       #define PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_100MBHD           0x1UL
+       #define PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_100MB     0x2UL
+       #define PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_1GBHD     0x4UL
+       #define PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_1GB               0x8UL
+       #define PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_2GB               0x10UL
+       #define PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_2_5GB     0x20UL
+       #define PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_10GB              0x40UL
+       #define PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_20GB              0x80UL
+       #define PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_25GB              0x100UL
+       #define PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_40GB              0x200UL
+       #define PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_50GB              0x400UL
+       __le16 force_link_speed;
+       #define PORT_PHY_QCFG_RESP_FORCE_LINK_SPEED_100MB          (0x1UL << 0)
+       #define PORT_PHY_QCFG_RESP_FORCE_LINK_SPEED_1GB    (0xaUL << 0)
+       #define PORT_PHY_QCFG_RESP_FORCE_LINK_SPEED_2GB    (0x14UL << 0)
+       #define PORT_PHY_QCFG_RESP_FORCE_LINK_SPEED_2_5GB          (0x19UL << 0)
+       #define PORT_PHY_QCFG_RESP_FORCE_LINK_SPEED_10GB           (0x64UL << 0)
+       #define PORT_PHY_QCFG_RESP_FORCE_LINK_SPEED_20GB           (0xc8UL << 0)
+       #define PORT_PHY_QCFG_RESP_FORCE_LINK_SPEED_25GB           (0xfaUL << 0)
+       #define PORT_PHY_QCFG_RESP_FORCE_LINK_SPEED_40GB           (0x190UL << 0)
+       #define PORT_PHY_QCFG_RESP_FORCE_LINK_SPEED_50GB           (0x1f4UL << 0)
+       u8 auto_mode;
+       #define PORT_PHY_QCFG_RESP_AUTO_MODE_NONE                  (0x0UL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_MODE_ALL_SPEEDS    (0x1UL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_MODE_ONE_SPEED             (0x2UL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_MODE_ONE_OR_BELOW          (0x3UL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_MODE_MASK                  (0x4UL << 0)
+       u8 auto_pause;
+       #define PORT_PHY_QCFG_RESP_AUTO_PAUSE_TX                    0x1UL
+       #define PORT_PHY_QCFG_RESP_AUTO_PAUSE_RX                    0x2UL
+       __le16 auto_link_speed;
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_100MB           (0x1UL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_1GB             (0xaUL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_2GB             (0x14UL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_2_5GB           (0x19UL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_10GB    (0x64UL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_20GB    (0xc8UL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_25GB    (0xfaUL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_40GB    (0x190UL << 0)
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_50GB    (0x1f4UL << 0)
+       __le16 auto_link_speed_mask;
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_MASK_100MBHD    0x1UL
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_MASK_100MB      0x2UL
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_MASK_1GBHD      0x4UL
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_MASK_1GB         0x8UL
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_MASK_2GB         0x10UL
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_MASK_2_5GB      0x20UL
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_MASK_10GB       0x40UL
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_MASK_20GB       0x80UL
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_MASK_25GB       0x100UL
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_MASK_40GB       0x200UL
+       #define PORT_PHY_QCFG_RESP_AUTO_LINK_SPEED_MASK_50GB       0x400UL
+       u8 wirespeed;
+       #define PORT_PHY_QCFG_RESP_WIRESPEED_OFF                   (0x0UL << 0)
+       #define PORT_PHY_QCFG_RESP_WIRESPEED_ON            (0x1UL << 0)
+       u8 lpbk;
+       #define PORT_PHY_QCFG_RESP_LPBK_NONE                       (0x0UL << 0)
+       #define PORT_PHY_QCFG_RESP_LPBK_LOCAL                      (0x1UL << 0)
+       #define PORT_PHY_QCFG_RESP_LPBK_REMOTE                     (0x2UL << 0)
+       u8 force_pause;
+       #define PORT_PHY_QCFG_RESP_FORCE_PAUSE_TX                   0x1UL
+       #define PORT_PHY_QCFG_RESP_FORCE_PAUSE_RX                   0x2UL
+       u8 duplex_setting;
+       #define PORT_PHY_QCFG_RESP_DUPLEX_SETTING_HALF             (0x0UL << 0)
+       #define PORT_PHY_QCFG_RESP_DUPLEX_SETTING_FULL             (0x1UL << 0)
+       __le32 preemphasis;
+       u8 phy_maj;
+       u8 phy_min;
+       u8 phy_bld;
+       u8 phy_type;
+       #define PORT_PHY_QCFG_RESP_PHY_TYPE_BASECR4                (0x1UL << 0)
+       #define PORT_PHY_QCFG_RESP_PHY_TYPE_BASEKR4                (0x2UL << 0)
+       #define PORT_PHY_QCFG_RESP_PHY_TYPE_BASELR4                (0x3UL << 0)
+       #define PORT_PHY_QCFG_RESP_PHY_TYPE_BASESR4                (0x4UL << 0)
+       #define PORT_PHY_QCFG_RESP_PHY_TYPE_BASEKR2                (0x5UL << 0)
+       #define PORT_PHY_QCFG_RESP_PHY_TYPE_BASEKX4                (0x6UL << 0)
+       #define PORT_PHY_QCFG_RESP_PHY_TYPE_BASEKR                 (0x7UL << 0)
+       #define PORT_PHY_QCFG_RESP_PHY_TYPE_BASET                  (0x8UL << 0)
+       u8 media_type;
+       #define PORT_PHY_QCFG_RESP_MEDIA_TYPE_TP                   (0x1UL << 0)
+       #define PORT_PHY_QCFG_RESP_MEDIA_TYPE_DAC                  (0x2UL << 0)
+       #define PORT_PHY_QCFG_RESP_MEDIA_TYPE_FIBRE                (0x3UL << 0)
+       u8 transceiver_type;
+       #define PORT_PHY_QCFG_RESP_TRANSCEIVER_TYPE_XCVR_INTERNAL (0x1UL << 0)
+       #define PORT_PHY_QCFG_RESP_TRANSCEIVER_TYPE_XCVR_EXTERNAL (0x2UL << 0)
+       u8 phy_addr;
+       #define PORT_PHY_QCFG_RESP_PHY_ADDR_MASK                    0x1fUL
+       #define PORT_PHY_QCFG_RESP_PHY_ADDR_SFT             0
+       u8 unused_2;
+       __le16 link_partner_adv_speeds;
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_SPEEDS_100MBHD 0x1UL
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_SPEEDS_100MB   0x2UL
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_SPEEDS_1GBHD   0x4UL
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_SPEEDS_1GB     0x8UL
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_SPEEDS_2GB     0x10UL
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_SPEEDS_2_5GB   0x20UL
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_SPEEDS_10GB    0x40UL
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_SPEEDS_20GB    0x80UL
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_SPEEDS_25GB    0x100UL
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_SPEEDS_40GB    0x200UL
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_SPEEDS_50GB    0x400UL
+       u8 link_partner_adv_auto_mode;
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_AUTO_MODE_NONE (0x0UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_AUTO_MODE_ALL_SPEEDS (0x1UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_AUTO_MODE_ONE_SPEED (0x2UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_AUTO_MODE_ONE_OR_BELOW (0x3UL << 0)
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_AUTO_MODE_MASK (0x4UL << 0)
+       u8 link_partner_adv_pause;
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_PAUSE_TX       0x1UL
+       #define PORT_PHY_QCFG_RESP_LINK_PARTNER_ADV_PAUSE_RX       0x2UL
+       u8 unused_3;
+       u8 unused_4;
+       u8 unused_5;
+       u8 valid;
+};
+
+/* hwrm_port_mac_cfg */
+/* Input (32 bytes) */
+struct hwrm_port_mac_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define PORT_MAC_CFG_REQ_FLAGS_MATCH_LINK                   0x1UL
+       #define PORT_MAC_CFG_REQ_FLAGS_COS_ASSIGNMENT_ENABLE       0x2UL
+       #define PORT_MAC_CFG_REQ_FLAGS_TUNNEL_PRI2COS_ENABLE       0x4UL
+       #define PORT_MAC_CFG_REQ_FLAGS_IP_DSCP2COS_ENABLE           0x8UL
+       __le32 enables;
+       #define PORT_MAC_CFG_REQ_ENABLES_IPG                        0x1UL
+       #define PORT_MAC_CFG_REQ_ENABLES_LPBK                       0x2UL
+       #define PORT_MAC_CFG_REQ_ENABLES_IVLAN_PRI2COS_MAP_PRI     0x4UL
+       #define PORT_MAC_CFG_REQ_ENABLES_LCOS_MAP_PRI               0x8UL
+       #define PORT_MAC_CFG_REQ_ENABLES_TUNNEL_PRI2COS_MAP_PRI    0x10UL
+       #define PORT_MAC_CFG_REQ_ENABLES_DSCP2COS_MAP_PRI           0x20UL
+       __le16 port_id;
+       u8 ipg;
+       u8 lpbk;
+       #define PORT_MAC_CFG_REQ_LPBK_NONE                         (0x0UL << 0)
+       #define PORT_MAC_CFG_REQ_LPBK_LOCAL                        (0x1UL << 0)
+       #define PORT_MAC_CFG_REQ_LPBK_REMOTE                       (0x2UL << 0)
+       u8 ivlan_pri2cos_map_pri;
+       u8 lcos_map_pri;
+       u8 tunnel_pri2cos_map_pri;
+       u8 dscp2pri_map_pri;
+};
+
+/* Output (16 bytes) */
+struct hwrm_port_mac_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 mru;
+       __le16 mtu;
+       u8 ipg;
+       u8 lpbk;
+       #define PORT_MAC_CFG_RESP_LPBK_NONE                        (0x0UL << 0)
+       #define PORT_MAC_CFG_RESP_LPBK_LOCAL                       (0x1UL << 0)
+       #define PORT_MAC_CFG_RESP_LPBK_REMOTE                      (0x2UL << 0)
+       u8 unused_0;
+       u8 valid;
+};
+
+/* hwrm_port_enable */
+/* Input (24 bytes) */
+struct hwrm_port_enable_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define PORT_ENABLE_REQ_FLAGS_FORWARD_TRAFFIC               0x1UL
+       __le16 port_id;
+       __le16 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_port_enable_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_port_qstats */
+/* Input (40 bytes) */
+struct hwrm_port_qstats_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 port_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2[3];
+       u8 unused_3;
+       __le64 tx_stat_host_addr;
+       __le64 rx_stat_host_addr;
+};
+
+/* Output (16 bytes) */
+struct hwrm_port_qstats_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_port_lpbk_qstats */
+/* Input (16 bytes) */
+struct hwrm_port_lpbk_qstats_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+};
+
+/* Output (64 bytes) */
+struct hwrm_port_lpbk_qstats_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le64 lpbk_ucast_frames;
+       __le64 lpbk_mcast_frames;
+       __le64 lpbk_bcast_frames;
+       __le64 lpbk_ucast_bytes;
+       __le64 lpbk_mcast_bytes;
+       __le64 lpbk_bcast_bytes;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_port_clr_stats */
+/* Input (24 bytes) */
+struct hwrm_port_clr_stats_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 port_id;
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_port_clr_stats_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_port_lpbk_clr_stats */
+/* Input (16 bytes) */
+struct hwrm_port_lpbk_clr_stats_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+};
+
+/* Output (16 bytes) */
+struct hwrm_port_lpbk_clr_stats_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_port_blink_led */
+/* Input (24 bytes) */
+struct hwrm_port_blink_led_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 num_blinks;
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_port_blink_led_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_queue_qportcfg */
+/* Input (24 bytes) */
+struct hwrm_queue_qportcfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define QUEUE_QPORTCFG_REQ_FLAGS_PATH                       0x1UL
+       #define QUEUE_QPORTCFG_REQ_FLAGS_PATH_TX                   (0x0UL << 0)
+       #define QUEUE_QPORTCFG_REQ_FLAGS_PATH_RX                   (0x1UL << 0)
+       __le16 port_id;
+       __le16 unused_0;
+};
+
+/* Output (32 bytes) */
+struct hwrm_queue_qportcfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       u8 max_configurable_queues;
+       u8 max_configurable_lossless_queues;
+       u8 queue_cfg_allowed;
+       u8 queue_buffers_cfg_allowed;
+       u8 queue_pfcenable_cfg_allowed;
+       u8 queue_pri2cos_cfg_allowed;
+       u8 queue_cos2bw_cfg_allowed;
+       u8 queue_id0;
+       u8 queue_id0_service_profile;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSY (0x0UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSLESS (0x1UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_UNKNOWN (0xffUL << 0)
+       u8 queue_id1;
+       u8 queue_id1_service_profile;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_LOSSY (0x0UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_LOSSLESS (0x1UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_UNKNOWN (0xffUL << 0)
+       u8 queue_id2;
+       u8 queue_id2_service_profile;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_LOSSY (0x0UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_LOSSLESS (0x1UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_UNKNOWN (0xffUL << 0)
+       u8 queue_id3;
+       u8 queue_id3_service_profile;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_LOSSY (0x0UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_LOSSLESS (0x1UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_UNKNOWN (0xffUL << 0)
+       u8 queue_id4;
+       u8 queue_id4_service_profile;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_LOSSY (0x0UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_LOSSLESS (0x1UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_UNKNOWN (0xffUL << 0)
+       u8 queue_id5;
+       u8 queue_id5_service_profile;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_LOSSY (0x0UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_LOSSLESS (0x1UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_UNKNOWN (0xffUL << 0)
+       u8 queue_id6;
+       u8 queue_id6_service_profile;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_LOSSY (0x0UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_LOSSLESS (0x1UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_UNKNOWN (0xffUL << 0)
+       u8 queue_id7;
+       u8 queue_id7_service_profile;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LOSSY (0x0UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LOSSLESS (0x1UL << 0)
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_UNKNOWN (0xffUL << 0)
+       u8 valid;
+};
+
+/* hwrm_queue_cfg */
+/* Input (40 bytes) */
+struct hwrm_queue_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define QUEUE_CFG_REQ_FLAGS_PATH                            0x1UL
+       #define QUEUE_CFG_REQ_FLAGS_PATH_TX                        (0x0UL << 0)
+       #define QUEUE_CFG_REQ_FLAGS_PATH_RX                        (0x1UL << 0)
+       __le32 enables;
+       #define QUEUE_CFG_REQ_ENABLES_DFLT_LEN                      0x1UL
+       #define QUEUE_CFG_REQ_ENABLES_SERVICE_PROFILE               0x2UL
+       __le32 queue_id;
+       __le32 dflt_len;
+       u8 service_profile;
+       #define QUEUE_CFG_REQ_SERVICE_PROFILE_LOSSY                (0x0UL << 0)
+       #define QUEUE_CFG_REQ_SERVICE_PROFILE_LOSSLESS             (0x1UL << 0)
+       #define QUEUE_CFG_REQ_SERVICE_PROFILE_UNKNOWN              (0xffUL << 0)
+       u8 unused_0[7];
+};
+
+/* Output (16 bytes) */
+struct hwrm_queue_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_queue_buffers_cfg */
+/* Input (56 bytes) */
+struct hwrm_queue_buffers_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define QUEUE_BUFFERS_CFG_REQ_FLAGS_PATH                    0x1UL
+       #define QUEUE_BUFFERS_CFG_REQ_FLAGS_PATH_TX                (0x0UL << 0)
+       #define QUEUE_BUFFERS_CFG_REQ_FLAGS_PATH_RX                (0x1UL << 0)
+       __le32 enables;
+       #define QUEUE_BUFFERS_CFG_REQ_ENABLES_RESERVED              0x1UL
+       #define QUEUE_BUFFERS_CFG_REQ_ENABLES_SHARED                0x2UL
+       #define QUEUE_BUFFERS_CFG_REQ_ENABLES_GROUP                 0x4UL
+       #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XOFF                  0x8UL
+       #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XON                   0x10UL
+       #define QUEUE_BUFFERS_CFG_REQ_ENABLES_FULL                  0x20UL
+       #define QUEUE_BUFFERS_CFG_REQ_ENABLES_NOTFULL               0x40UL
+       #define QUEUE_BUFFERS_CFG_REQ_ENABLES_MAX                   0x80UL
+       __le32 queue_id;
+       __le32 reserved;
+       __le32 shared;
+       __le32 xoff;
+       __le32 xon;
+       __le32 full;
+       __le32 notfull;
+       __le32 max;
+};
+
+/* Output (16 bytes) */
+struct hwrm_queue_buffers_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_queue_pfcenable_cfg */
+/* Input (24 bytes) */
+struct hwrm_queue_pfcenable_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI0_PFC_ENABLED   0x1UL
+       #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI1_PFC_ENABLED   0x2UL
+       #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI2_PFC_ENABLED   0x4UL
+       #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI3_PFC_ENABLED   0x8UL
+       #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI4_PFC_ENABLED   0x10UL
+       #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI5_PFC_ENABLED   0x20UL
+       #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI6_PFC_ENABLED   0x40UL
+       #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI7_PFC_ENABLED   0x80UL
+       __le16 port_id;
+       __le16 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_queue_pfcenable_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_queue_pri2cos_cfg */
+/* Input (40 bytes) */
+struct hwrm_queue_pri2cos_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define QUEUE_PRI2COS_CFG_REQ_FLAGS_PATH                    0x1UL
+       #define QUEUE_PRI2COS_CFG_REQ_FLAGS_PATH_TX                (0x0UL << 0)
+       #define QUEUE_PRI2COS_CFG_REQ_FLAGS_PATH_RX                (0x1UL << 0)
+       #define QUEUE_PRI2COS_CFG_REQ_FLAGS_IVLAN                   0x2UL
+       __le32 enables;
+       u8 port_id;
+       u8 pri0_cos;
+       u8 pri1_cos;
+       u8 pri2_cos;
+       u8 pri3_cos;
+       u8 pri4_cos;
+       u8 pri5_cos;
+       u8 pri6_cos;
+       u8 pri7_cos;
+       u8 unused_0[7];
+};
+
+/* Output (16 bytes) */
+struct hwrm_queue_pri2cos_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_queue_cos2bw_cfg */
+/* Input (128 bytes) */
+struct hwrm_queue_cos2bw_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       __le32 enables;
+       #define QUEUE_COS2BW_CFG_REQ_ENABLES_COS_QUEUE_ID0_VALID   0x1UL
+       #define QUEUE_COS2BW_CFG_REQ_ENABLES_COS_QUEUE_ID1_VALID   0x2UL
+       #define QUEUE_COS2BW_CFG_REQ_ENABLES_COS_QUEUE_ID2_VALID   0x4UL
+       #define QUEUE_COS2BW_CFG_REQ_ENABLES_COS_QUEUE_ID3_VALID   0x8UL
+       #define QUEUE_COS2BW_CFG_REQ_ENABLES_COS_QUEUE_ID4_VALID   0x10UL
+       #define QUEUE_COS2BW_CFG_REQ_ENABLES_COS_QUEUE_ID5_VALID   0x20UL
+       #define QUEUE_COS2BW_CFG_REQ_ENABLES_COS_QUEUE_ID6_VALID   0x40UL
+       #define QUEUE_COS2BW_CFG_REQ_ENABLES_COS_QUEUE_ID7_VALID   0x80UL
+       __le16 port_id;
+       u8 queue_id0;
+       u8 unused_0;
+       __le32 queue_id0_min_bw;
+       __le32 queue_id0_max_bw;
+       u8 queue_id0_tsa_assign;
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID0_TSA_ASSIGN_SP      (0x0UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID0_TSA_ASSIGN_ETS     (0x1UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID0_TSA_ASSIGN_RESERVED_FIRST (0x2UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID0_TSA_ASSIGN_RESERVED_LAST (0xffffUL << 0)
+       u8 queue_id0_pri_lvl;
+       u8 queue_id0_bw_weight;
+       u8 queue_id1;
+       __le32 queue_id1_min_bw;
+       __le32 queue_id1_max_bw;
+       u8 queue_id1_tsa_assign;
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID1_TSA_ASSIGN_SP      (0x0UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID1_TSA_ASSIGN_ETS     (0x1UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID1_TSA_ASSIGN_RESERVED_FIRST (0x2UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID1_TSA_ASSIGN_RESERVED_LAST (0xffffUL << 0)
+       u8 queue_id1_pri_lvl;
+       u8 queue_id1_bw_weight;
+       u8 queue_id2;
+       __le32 queue_id2_min_bw;
+       __le32 queue_id2_max_bw;
+       u8 queue_id2_tsa_assign;
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID2_TSA_ASSIGN_SP      (0x0UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID2_TSA_ASSIGN_ETS     (0x1UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID2_TSA_ASSIGN_RESERVED_FIRST (0x2UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID2_TSA_ASSIGN_RESERVED_LAST (0xffffUL << 0)
+       u8 queue_id2_pri_lvl;
+       u8 queue_id2_bw_weight;
+       u8 queue_id3;
+       __le32 queue_id3_min_bw;
+       __le32 queue_id3_max_bw;
+       u8 queue_id3_tsa_assign;
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID3_TSA_ASSIGN_SP      (0x0UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID3_TSA_ASSIGN_ETS     (0x1UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID3_TSA_ASSIGN_RESERVED_FIRST (0x2UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID3_TSA_ASSIGN_RESERVED_LAST (0xffffUL << 0)
+       u8 queue_id3_pri_lvl;
+       u8 queue_id3_bw_weight;
+       u8 queue_id4;
+       __le32 queue_id4_min_bw;
+       __le32 queue_id4_max_bw;
+       u8 queue_id4_tsa_assign;
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID4_TSA_ASSIGN_SP      (0x0UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID4_TSA_ASSIGN_ETS     (0x1UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID4_TSA_ASSIGN_RESERVED_FIRST (0x2UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID4_TSA_ASSIGN_RESERVED_LAST (0xffffUL << 0)
+       u8 queue_id4_pri_lvl;
+       u8 queue_id4_bw_weight;
+       u8 queue_id5;
+       __le32 queue_id5_min_bw;
+       __le32 queue_id5_max_bw;
+       u8 queue_id5_tsa_assign;
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID5_TSA_ASSIGN_SP      (0x0UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID5_TSA_ASSIGN_ETS     (0x1UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID5_TSA_ASSIGN_RESERVED_FIRST (0x2UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID5_TSA_ASSIGN_RESERVED_LAST (0xffffUL << 0)
+       u8 queue_id5_pri_lvl;
+       u8 queue_id5_bw_weight;
+       u8 queue_id6;
+       __le32 queue_id6_min_bw;
+       __le32 queue_id6_max_bw;
+       u8 queue_id6_tsa_assign;
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID6_TSA_ASSIGN_SP      (0x0UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID6_TSA_ASSIGN_ETS     (0x1UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID6_TSA_ASSIGN_RESERVED_FIRST (0x2UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID6_TSA_ASSIGN_RESERVED_LAST (0xffffUL << 0)
+       u8 queue_id6_pri_lvl;
+       u8 queue_id6_bw_weight;
+       u8 queue_id7;
+       __le32 queue_id7_min_bw;
+       __le32 queue_id7_max_bw;
+       u8 queue_id7_tsa_assign;
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID7_TSA_ASSIGN_SP      (0x0UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID7_TSA_ASSIGN_ETS     (0x1UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID7_TSA_ASSIGN_RESERVED_FIRST (0x2UL << 0)
+       #define QUEUE_COS2BW_CFG_REQ_QUEUE_ID7_TSA_ASSIGN_RESERVED_LAST (0xffffUL << 0)
+       u8 queue_id7_pri_lvl;
+       u8 queue_id7_bw_weight;
+       u8 unused_1[5];
+};
+
+/* Output (16 bytes) */
+struct hwrm_queue_cos2bw_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_vnic_alloc */
+/* Input (24 bytes) */
+struct hwrm_vnic_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define VNIC_ALLOC_REQ_FLAGS_DEFAULT                        0x1UL
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_vnic_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 vnic_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_vnic_free */
+/* Input (24 bytes) */
+struct hwrm_vnic_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 vnic_id;
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_vnic_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_vnic_cfg */
+/* Input (40 bytes) */
+struct hwrm_vnic_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define VNIC_CFG_REQ_FLAGS_DEFAULT                          0x1UL
+       #define VNIC_CFG_REQ_FLAGS_VLAN_STRIP_MODE                  0x2UL
+       __le32 enables;
+       #define VNIC_CFG_REQ_ENABLES_DFLT_RING_GRP                  0x1UL
+       #define VNIC_CFG_REQ_ENABLES_RSS_RULE                       0x2UL
+       #define VNIC_CFG_REQ_ENABLES_COS_RULE                       0x4UL
+       #define VNIC_CFG_REQ_ENABLES_LB_RULE                        0x8UL
+       #define VNIC_CFG_REQ_ENABLES_MRU                            0x10UL
+       __le16 vnic_id;
+       __le16 dflt_ring_grp;
+       __le16 rss_rule;
+       __le16 cos_rule;
+       __le16 lb_rule;
+       __le16 mru;
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_vnic_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_vnic_tpa_cfg */
+/* Input (40 bytes) */
+struct hwrm_vnic_tpa_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define VNIC_TPA_CFG_REQ_FLAGS_TPA                          0x1UL
+       #define VNIC_TPA_CFG_REQ_FLAGS_ENCAP_TPA                    0x2UL
+       #define VNIC_TPA_CFG_REQ_FLAGS_RSC_WND_UPDATE               0x4UL
+       #define VNIC_TPA_CFG_REQ_FLAGS_GRO                          0x8UL
+       #define VNIC_TPA_CFG_REQ_FLAGS_AGG_WITH_ECN                 0x10UL
+       #define VNIC_TPA_CFG_REQ_FLAGS_AGG_WITH_SAME_GRE_SEQ       0x20UL
+       #define VNIC_TPA_CFG_REQ_FLAGS_GRO_IPID_CHECK               0x40UL
+       #define VNIC_TPA_CFG_REQ_FLAGS_GRO_TTL_CHECK                0x80UL
+       __le32 enables;
+       #define VNIC_TPA_CFG_REQ_ENABLES_MAX_AGG_SEGS               0x1UL
+       #define VNIC_TPA_CFG_REQ_ENABLES_MAX_AGGS                   0x2UL
+       #define VNIC_TPA_CFG_REQ_ENABLES_MAX_AGG_TIMER              0x4UL
+       #define VNIC_TPA_CFG_REQ_ENABLES_MIN_AGG_LEN                0x8UL
+       __le16 vnic_id;
+       __le16 max_agg_segs;
+       #define VNIC_TPA_CFG_REQ_MAX_AGG_SEGS_1            (0x0UL << 0)
+       #define VNIC_TPA_CFG_REQ_MAX_AGG_SEGS_2            (0x1UL << 0)
+       #define VNIC_TPA_CFG_REQ_MAX_AGG_SEGS_4            (0x2UL << 0)
+       #define VNIC_TPA_CFG_REQ_MAX_AGG_SEGS_8            (0x3UL << 0)
+       #define VNIC_TPA_CFG_REQ_MAX_AGG_SEGS_MAX                  (0x1fUL << 0)
+       __le16 max_aggs;
+       #define VNIC_TPA_CFG_REQ_MAX_AGGS_1                        (0x0UL << 0)
+       #define VNIC_TPA_CFG_REQ_MAX_AGGS_2                        (0x1UL << 0)
+       #define VNIC_TPA_CFG_REQ_MAX_AGGS_4                        (0x2UL << 0)
+       #define VNIC_TPA_CFG_REQ_MAX_AGGS_8                        (0x3UL << 0)
+       #define VNIC_TPA_CFG_REQ_MAX_AGGS_16                       (0x4UL << 0)
+       #define VNIC_TPA_CFG_REQ_MAX_AGGS_MAX                      (0x7UL << 0)
+       u8 unused_0;
+       u8 unused_1;
+       __le32 max_agg_timer;
+       __le32 min_agg_len;
+};
+
+/* Output (16 bytes) */
+struct hwrm_vnic_tpa_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_vnic_rss_cfg */
+/* Input (48 bytes) */
+struct hwrm_vnic_rss_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 hash_type;
+       #define VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4             0x1UL
+       #define VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4                 0x2UL
+       #define VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4                 0x4UL
+       #define VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6             0x8UL
+       #define VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6                 0x10UL
+       #define VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6                 0x20UL
+       __le32 unused_0;
+       __le64 ring_grp_tbl_addr;
+       __le64 hash_key_tbl_addr;
+       __le16 rss_ctx_idx;
+       __le16 unused_1[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_vnic_rss_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_vnic_plcmodes_cfg */
+/* Input (40 bytes) */
+struct hwrm_vnic_plcmodes_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define VNIC_PLCMODES_CFG_REQ_FLAGS_REGULAR_PLACEMENT      0x1UL
+       #define VNIC_PLCMODES_CFG_REQ_FLAGS_JUMBO_PLACEMENT         0x2UL
+       #define VNIC_PLCMODES_CFG_REQ_FLAGS_HDS_IPV4                0x4UL
+       #define VNIC_PLCMODES_CFG_REQ_FLAGS_HDS_IPV6                0x8UL
+       #define VNIC_PLCMODES_CFG_REQ_FLAGS_HDS_FCOE                0x10UL
+       #define VNIC_PLCMODES_CFG_REQ_FLAGS_HDS_ROCE                0x20UL
+       __le32 enables;
+       #define VNIC_PLCMODES_CFG_REQ_ENABLES_JUMBO_THRESH_VALID   0x1UL
+       #define VNIC_PLCMODES_CFG_REQ_ENABLES_HDS_OFFSET_VALID     0x2UL
+       #define VNIC_PLCMODES_CFG_REQ_ENABLES_HDS_THRESHOLD_VALID  0x4UL
+       __le32 vnic_id;
+       __le16 jumbo_thresh;
+       __le16 hds_offset;
+       __le16 hds_threshold;
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_vnic_plcmodes_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_vnic_rss_cos_lb_ctx_alloc */
+/* Input (16 bytes) */
+struct hwrm_vnic_rss_cos_lb_ctx_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+};
+
+/* Output (16 bytes) */
+struct hwrm_vnic_rss_cos_lb_ctx_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 rss_cos_lb_ctx_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 unused_4;
+       u8 valid;
+};
+
+/* hwrm_vnic_rss_cos_lb_ctx_free */
+/* Input (24 bytes) */
+struct hwrm_vnic_rss_cos_lb_ctx_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 rss_cos_lb_ctx_id;
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_vnic_rss_cos_lb_ctx_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_ring_alloc */
+/* Input (80 bytes) */
+struct hwrm_ring_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define RING_ALLOC_REQ_ENABLES_ARB_GRP_ID_VALID     0x1UL
+       #define RING_ALLOC_REQ_ENABLES_INPUT_NUM_VALID              0x2UL
+       #define RING_ALLOC_REQ_ENABLES_WEIGHT_VALID                 0x4UL
+       #define RING_ALLOC_REQ_ENABLES_STAT_CTX_ID_VALID            0x8UL
+       #define RING_ALLOC_REQ_ENABLES_MIN_BW_VALID                 0x10UL
+       #define RING_ALLOC_REQ_ENABLES_MAX_BW_VALID                 0x20UL
+       u8 ring_type;
+       #define RING_ALLOC_REQ_RING_TYPE_CMPL                      (0x0UL << 0)
+       #define RING_ALLOC_REQ_RING_TYPE_TX                        (0x1UL << 0)
+       #define RING_ALLOC_REQ_RING_TYPE_RX                        (0x2UL << 0)
+       #define RING_ALLOC_REQ_RING_TYPE_STATUS            (0x3UL << 0)
+       #define RING_ALLOC_REQ_RING_TYPE_CMD                       (0x4UL << 0)
+       u8 unused_0;
+       __le16 unused_1;
+       __le64 page_tbl_addr;
+       __le32 fbo;
+       u8 page_size;
+       u8 page_tbl_depth;
+       u8 unused_2;
+       u8 unused_3;
+       __le32 length;
+       __le16 logical_id;
+       __le16 cmpl_ring_id;
+       __le16 queue_id;
+       u8 unused_4;
+       u8 unused_5;
+       __le32 arb_grp_id;
+       __le16 input_number;
+       u8 unused_6;
+       u8 unused_7;
+       __le32 weight;
+       __le32 stat_ctx_id;
+       __le32 min_bw;
+       __le32 max_bw;
+       u8 int_mode;
+       #define RING_ALLOC_REQ_INT_MODE_LEGACY                     (0x0UL << 0)
+       #define RING_ALLOC_REQ_INT_MODE_MSI                        (0x1UL << 0)
+       #define RING_ALLOC_REQ_INT_MODE_MSIX                       (0x2UL << 0)
+       #define RING_ALLOC_REQ_INT_MODE_POLL                       (0x3UL << 0)
+       u8 unused_8[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_ring_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 ring_id;
+       __le16 logical_ring_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_ring_free */
+/* Input (24 bytes) */
+struct hwrm_ring_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       u8 ring_type;
+       #define RING_FREE_REQ_RING_TYPE_CMPL                       (0x0UL << 0)
+       #define RING_FREE_REQ_RING_TYPE_TX                         (0x1UL << 0)
+       #define RING_FREE_REQ_RING_TYPE_RX                         (0x2UL << 0)
+       #define RING_FREE_REQ_RING_TYPE_STATUS                     (0x3UL << 0)
+       #define RING_FREE_REQ_RING_TYPE_CMD                        (0x4UL << 0)
+       u8 unused_0;
+       __le16 ring_id;
+       __le32 unused_1;
+};
+
+/* Output (16 bytes) */
+struct hwrm_ring_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_ring_cmpl_ring_qaggint_params */
+/* Input (24 bytes) */
+struct hwrm_ring_cmpl_ring_qaggint_params_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 ring_id;
+       __le16 unused_0[3];
+};
+
+/* Output (32 bytes) */
+struct hwrm_ring_cmpl_ring_qaggint_params_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 flags;
+       #define RING_CMPL_RING_QAGGINT_PARAMS_RESP_FLAGS_TIMER_RESET 0x1UL
+       #define RING_CMPL_RING_QAGGINT_PARAMS_RESP_FLAGS_RING_IDLE 0x2UL
+       __le16 num_cmpl_dma_aggr;
+       __le16 num_cmpl_dma_aggr_during_int;
+       __le16 cmpl_aggr_dma_tmr;
+       __le16 cmpl_aggr_dma_tmr_during_int;
+       __le16 int_lat_tmr_min;
+       __le16 int_lat_tmr_max;
+       __le16 num_cmpl_aggr_int;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_ring_cmpl_ring_cfg_aggint_params */
+/* Input (40 bytes) */
+struct hwrm_ring_cmpl_ring_cfg_aggint_params_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 ring_id;
+       __le16 flags;
+       #define RING_CMPL_RING_CFG_AGGINT_PARAMS_REQ_FLAGS_TIMER_RESET 0x1UL
+       #define RING_CMPL_RING_CFG_AGGINT_PARAMS_REQ_FLAGS_RING_IDLE 0x2UL
+       __le16 num_cmpl_dma_aggr;
+       __le16 num_cmpl_dma_aggr_during_int;
+       __le16 cmpl_aggr_dma_tmr;
+       __le16 cmpl_aggr_dma_tmr_during_int;
+       __le16 int_lat_tmr_min;
+       __le16 int_lat_tmr_max;
+       __le16 num_cmpl_aggr_int;
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_ring_cmpl_ring_cfg_aggint_params_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_ring_reset */
+/* Input (24 bytes) */
+struct hwrm_ring_reset_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       u8 ring_type;
+       #define RING_RESET_REQ_RING_TYPE_CMPL                      (0x0UL << 0)
+       #define RING_RESET_REQ_RING_TYPE_TX                        (0x1UL << 0)
+       #define RING_RESET_REQ_RING_TYPE_RX                        (0x2UL << 0)
+       #define RING_RESET_REQ_RING_TYPE_STATUS            (0x3UL << 0)
+       #define RING_RESET_REQ_RING_TYPE_CMD                       (0x4UL << 0)
+       u8 unused_0;
+       __le16 ring_id;
+       __le32 unused_1;
+};
+
+/* Output (16 bytes) */
+struct hwrm_ring_reset_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_ring_grp_alloc */
+/* Input (24 bytes) */
+struct hwrm_ring_grp_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 cr;
+       __le16 rr;
+       __le16 ar;
+       __le16 sc;
+};
+
+/* Output (16 bytes) */
+struct hwrm_ring_grp_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 ring_group_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_ring_grp_free */
+/* Input (24 bytes) */
+struct hwrm_ring_grp_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 ring_group_id;
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_ring_grp_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_arb_grp_alloc */
+/* Input (24 bytes) */
+struct hwrm_arb_grp_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 input_number;
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_arb_grp_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 arb_grp_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 unused_4;
+       u8 valid;
+};
+
+/* hwrm_arb_grp_cfg */
+/* Input (32 bytes) */
+struct hwrm_arb_grp_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 arb_grp_id;
+       __le16 input_number;
+       __le16 tx_ring;
+       __le32 weight;
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_arb_grp_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_cfa_l2_filter_alloc */
+/* Input (96 bytes) */
+struct hwrm_cfa_l2_filter_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH                  0x1UL
+       #define CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_TX              (0x0UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX              (0x1UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_FLAGS_LOOPBACK              0x2UL
+       #define CFA_L2_FILTER_ALLOC_REQ_FLAGS_DROP                  0x4UL
+       #define CFA_L2_FILTER_ALLOC_REQ_FLAGS_OUTERMOST     0x8UL
+       __le32 enables;
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR     0x1UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR_MASK       0x2UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_OVLAN            0x4UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_OVLAN_MASK      0x8UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_IVLAN            0x10UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_IVLAN_MASK      0x20UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_T_L2_ADDR           0x40UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_T_L2_ADDR_MASK     0x80UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_T_L2_OVLAN          0x100UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_T_L2_OVLAN_MASK    0x200UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_T_L2_IVLAN          0x400UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_T_L2_IVLAN_MASK    0x800UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_SRC_TYPE            0x1000UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_SRC_ID              0x2000UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE         0x4000UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID         0x8000UL
+       #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID     0x10000UL
+       u8 l2_addr[6];
+       u8 unused_0;
+       u8 unused_1;
+       u8 l2_addr_mask[6];
+       __le16 l2_ovlan;
+       __le16 l2_ovlan_mask;
+       __le16 l2_ivlan;
+       __le16 l2_ivlan_mask;
+       u8 unused_2;
+       u8 unused_3;
+       u8 t_l2_addr[6];
+       u8 unused_4;
+       u8 unused_5;
+       u8 t_l2_addr_mask[6];
+       __le16 t_l2_ovlan;
+       __le16 t_l2_ovlan_mask;
+       __le16 t_l2_ivlan;
+       __le16 t_l2_ivlan_mask;
+       u8 src_type;
+       #define CFA_L2_FILTER_ALLOC_REQ_SRC_TYPE_NPORT             (0x0UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_SRC_TYPE_PF                (0x1UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_SRC_TYPE_VF                (0x2UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_SRC_TYPE_VNIC              (0x3UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_SRC_TYPE_KONG              (0x4UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_SRC_TYPE_APE               (0x5UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_SRC_TYPE_BONO              (0x6UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_SRC_TYPE_TANG              (0x7UL << 0)
+       u8 unused_6;
+       __le32 src_id;
+       u8 tunnel_type;
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL     (0x0UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN          (0x1UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE          (0x2UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE          (0x3UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP           (0x4UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE         (0x5UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS           (0x6UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT    (0x7UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE          (0x8UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL     (0xffUL << 0)
+       u8 unused_7;
+       __le16 dst_vnic_id;
+       __le16 mirror_vnic_id;
+       u8 pri_hint;
+       #define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_NO_PREFER         (0x0UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_ABOVE_FILTER     (0x1UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_BELOW_FILTER     (0x2UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_MAX               (0x3UL << 0)
+       #define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_MIN               (0x4UL << 0)
+       u8 unused_8;
+       __le32 unused_9;
+       __le64 l2_filter_id_hint;
+};
+
+/* Output (24 bytes) */
+struct hwrm_cfa_l2_filter_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le64 l2_filter_id;
+       __le32 flow_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_cfa_l2_filter_free */
+/* Input (24 bytes) */
+struct hwrm_cfa_l2_filter_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le64 l2_filter_id;
+};
+
+/* Output (16 bytes) */
+struct hwrm_cfa_l2_filter_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_cfa_l2_filter_cfg */
+/* Input (40 bytes) */
+struct hwrm_cfa_l2_filter_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define CFA_L2_FILTER_CFG_REQ_FLAGS_PATH                    0x1UL
+       #define CFA_L2_FILTER_CFG_REQ_FLAGS_PATH_TX                (0x0UL << 0)
+       #define CFA_L2_FILTER_CFG_REQ_FLAGS_PATH_RX                (0x1UL << 0)
+       #define CFA_L2_FILTER_CFG_REQ_FLAGS_DROP                    0x2UL
+       __le32 enables;
+       #define CFA_L2_FILTER_CFG_REQ_ENABLES_DST_VNIC_ID_VALID    0x1UL
+       __le64 l2_filter_id;
+       __le32 dst_vnic_id;
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_cfa_l2_filter_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_cfa_l2_set_rx_mask */
+/* Input (40 bytes) */
+struct hwrm_cfa_l2_set_rx_mask_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 dflt_vnic_id;
+       __le32 mask;
+       #define CFA_L2_SET_RX_MASK_REQ_MASK_UNICAST                 0x1UL
+       #define CFA_L2_SET_RX_MASK_REQ_MASK_MCAST                   0x2UL
+       #define CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST               0x4UL
+       #define CFA_L2_SET_RX_MASK_REQ_MASK_BCAST                   0x8UL
+       #define CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS     0x10UL
+       #define CFA_L2_SET_RX_MASK_REQ_MASK_OUTERMOST               0x20UL
+       __le64 mc_tbl_addr;
+       __le32 num_mc_entries;
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_cfa_l2_set_rx_mask_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_cfa_l2_set_bcastmcast_mirroring */
+/* Input (32 bytes) */
+struct hwrm_cfa_l2_set_bcastmcast_mirroring_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 dflt_vnic_id;
+       __le32 mirroring_flags;
+       #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_BCAST_MIRRORING 0x1UL
+       #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_MCAST_MIRRORING 0x2UL
+       #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_BCAST_SRC_KNOCKOUT 0x4UL
+       #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_MCAST_SRC_KNOCKOUT 0x8UL
+       #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_VLAN_ID_VALID 0x10UL
+       __le16 vlan_id;
+       u8 bcast_domain;
+       #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_PFONLY (0x0UL << 0)
+       #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_ALLPFS (0x1UL << 0)
+       #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_ALLPFSVFS (0x2UL << 0)
+       u8 mcast_domain;
+       #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_PFONLY (0x0UL << 0)
+       #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_ALLPFS (0x1UL << 0)
+       #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_ALLPFSVFS (0x2UL << 0)
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_cfa_l2_set_bcastmcast_mirroring_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_cfa_tunnel_filter_alloc */
+/* Input (88 bytes) */
+struct hwrm_cfa_tunnel_filter_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_FLAGS_LOOPBACK          0x1UL
+       __le32 enables;
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_ENABLES_L2_FILTER_ID   0x1UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_ENABLES_L2_ADDR         0x2UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_ENABLES_L2_IVLAN       0x4UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_ENABLES_L3_ADDR         0x8UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_ENABLES_L3_ADDR_TYPE   0x10UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_ENABLES_T_L3_ADDR_TYPE 0x20UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_ENABLES_T_L3_ADDR      0x40UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE    0x80UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_ENABLES_VNI     0x100UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID    0x200UL
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x400UL
+       __le64 l2_filter_id;
+       u8 l2_addr[6];
+       __le16 l2_ivlan;
+       __le32 l3_addr[4];
+       __le32 t_l3_addr[4];
+       u8 l3_addr_type;
+       u8 t_l3_addr_type;
+       u8 tunnel_type;
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0)
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN     (0x1UL << 0)
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE     (0x2UL << 0)
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE     (0x3UL << 0)
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP      (0x4UL << 0)
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE    (0x5UL << 0)
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS      (0x6UL << 0)
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT       (0x7UL << 0)
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE     (0x8UL << 0)
+       #define CFA_TUNNEL_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
+       u8 unused_0;
+       __le32 vni;
+       __le32 dst_vnic_id;
+       __le32 mirror_vnic_id;
+};
+
+/* Output (24 bytes) */
+struct hwrm_cfa_tunnel_filter_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le64 tunnel_filter_id;
+       __le32 flow_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_cfa_tunnel_filter_free */
+/* Input (24 bytes) */
+struct hwrm_cfa_tunnel_filter_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le64 tunnel_filter_id;
+};
+
+/* Output (16 bytes) */
+struct hwrm_cfa_tunnel_filter_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_cfa_encap_record_alloc */
+/* Input (32 bytes) */
+struct hwrm_cfa_encap_record_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define CFA_ENCAP_RECORD_ALLOC_REQ_FLAGS_LOOPBACK           0x1UL
+       u8 encap_type;
+       #define CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_VXLAN       (0x1UL << 0)
+       #define CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_NVGRE       (0x2UL << 0)
+       #define CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_L2GRE       (0x3UL << 0)
+       #define CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_IPIP         (0x4UL << 0)
+       #define CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_GENEVE      (0x5UL << 0)
+       #define CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_MPLS         (0x6UL << 0)
+       #define CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_VLAN         (0x7UL << 0)
+       #define CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_IPGRE       (0x8UL << 0)
+       u8 unused_0;
+       __le16 unused_1;
+       __le32 encap_data[16];
+};
+
+/* Output (24 bytes) */
+struct hwrm_cfa_encap_record_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le64 encap_record_id;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_cfa_encap_record_free */
+/* Input (24 bytes) */
+struct hwrm_cfa_encap_record_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le64 encap_record_id;
+};
+
+/* Output (16 bytes) */
+struct hwrm_cfa_encap_record_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_cfa_ntuple_filter_alloc */
+/* Input (128 bytes) */
+struct hwrm_cfa_ntuple_filter_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_LOOPBACK          0x1UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_DROP              0x2UL
+       __le32 enables;
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_L2_FILTER_ID   0x1UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_ETHERTYPE      0x2UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE    0x4UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_MACADDR    0x8UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IPADDR_TYPE    0x10UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR     0x20UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR_MASK 0x40UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR     0x80UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR_MASK 0x100UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IP_PROTOCOL    0x200UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT       0x400UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT_MASK  0x800UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT       0x1000UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK  0x2000UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_PRI_HINT       0x4000UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_NTUPLE_FILTER_ID 0x8000UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID    0x10000UL
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x20000UL
+       __le64 l2_filter_id;
+       u8 src_macaddr[6];
+       __be16 ethertype;
+       u8 ipaddr_type;
+       u8 ip_protocol;
+       __le16 dst_vnic_id;
+       __le16 mirror_vnic_id;
+       u8 tunnel_type;
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0)
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN     (0x1UL << 0)
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE     (0x2UL << 0)
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE     (0x3UL << 0)
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP      (0x4UL << 0)
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE    (0x5UL << 0)
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS      (0x6UL << 0)
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT       (0x7UL << 0)
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE     (0x8UL << 0)
+       #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
+       u8 pri_hint;
+       __be32 src_ipaddr[4];
+       __be32 src_ipaddr_mask[4];
+       __be32 dst_ipaddr[4];
+       __be32 dst_ipaddr_mask[4];
+       __be16 src_port;
+       __be16 src_port_mask;
+       __be16 dst_port;
+       __be16 dst_port_mask;
+       __le64 ntuple_filter_id_hint;
+};
+
+/* Output (24 bytes) */
+struct hwrm_cfa_ntuple_filter_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le64 ntuple_filter_id;
+       __le32 flow_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_cfa_ntuple_filter_free */
+/* Input (24 bytes) */
+struct hwrm_cfa_ntuple_filter_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le64 ntuple_filter_id;
+};
+
+/* Output (16 bytes) */
+struct hwrm_cfa_ntuple_filter_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_cfa_ntuple_filter_cfg */
+/* Input (40 bytes) */
+struct hwrm_cfa_ntuple_filter_cfg_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_DST_VNIC_ID_VALID 0x1UL
+       #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_MIRROR_VNIC_ID_VALID 0x2UL
+       __le32 unused_0;
+       __le64 ntuple_filter_id;
+       __le32 new_dst_vnic_id;
+       __le32 new_mirror_vnic_id;
+};
+
+/* Output (16 bytes) */
+struct hwrm_cfa_ntuple_filter_cfg_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_tunnel_dst_port_query */
+/* Input (24 bytes) */
+struct hwrm_tunnel_dst_port_query_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       u8 tunnel_type;
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_NONTUNNEL   (0x0UL << 0)
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN       (0x1UL << 0)
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_NVGRE       (0x2UL << 0)
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_L2GRE       (0x3UL << 0)
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_IPIP         (0x4UL << 0)
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_GENEVE      (0x5UL << 0)
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_MPLS         (0x6UL << 0)
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_STT          (0x7UL << 0)
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_IPGRE       (0x8UL << 0)
+       #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_ANYTUNNEL   (0xffUL << 0)
+       u8 unused_0[7];
+};
+
+/* Output (16 bytes) */
+struct hwrm_tunnel_dst_port_query_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 tunnel_dst_port_id;
+       __be16 tunnel_dst_port_val;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_tunnel_dst_port_alloc */
+/* Input (24 bytes) */
+struct hwrm_tunnel_dst_port_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       u8 tunnel_type;
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL   (0x0UL << 0)
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN       (0x1UL << 0)
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_NVGRE       (0x2UL << 0)
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_L2GRE       (0x3UL << 0)
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_IPIP         (0x4UL << 0)
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_GENEVE      (0x5UL << 0)
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_MPLS         (0x6UL << 0)
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_STT          (0x7UL << 0)
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_IPGRE       (0x8UL << 0)
+       #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL   (0xffUL << 0)
+       u8 unused_0;
+       __be16 tunnel_dst_port_val;
+       __le32 unused_1;
+};
+
+/* Output (16 bytes) */
+struct hwrm_tunnel_dst_port_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 tunnel_dst_port_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 unused_4;
+       u8 valid;
+};
+
+/* hwrm_tunnel_dst_port_free */
+/* Input (24 bytes) */
+struct hwrm_tunnel_dst_port_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       u8 tunnel_type;
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_NONTUNNEL    (0x0UL << 0)
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN         (0x1UL << 0)
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_NVGRE         (0x2UL << 0)
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_L2GRE         (0x3UL << 0)
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_IPIP          (0x4UL << 0)
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE       (0x5UL << 0)
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_MPLS          (0x6UL << 0)
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_STT           (0x7UL << 0)
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_IPGRE         (0x8UL << 0)
+       #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_ANYTUNNEL    (0xffUL << 0)
+       u8 unused_0;
+       __le16 tunnel_dst_port_id;
+       __le32 unused_1;
+};
+
+/* Output (16 bytes) */
+struct hwrm_tunnel_dst_port_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_stat_ctx_alloc */
+/* Input (32 bytes) */
+struct hwrm_stat_ctx_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le64 stats_dma_addr;
+       __le32 update_period_ms;
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_stat_ctx_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 stat_ctx_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_stat_ctx_free */
+/* Input (24 bytes) */
+struct hwrm_stat_ctx_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 stat_ctx_id;
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_stat_ctx_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 stat_ctx_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_stat_ctx_query */
+/* Input (24 bytes) */
+struct hwrm_stat_ctx_query_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 stat_ctx_id;
+       __le32 unused_0;
+};
+
+/* Output (176 bytes) */
+struct hwrm_stat_ctx_query_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le64 tx_ucast_pkts;
+       __le64 tx_mcast_pkts;
+       __le64 tx_bcast_pkts;
+       __le64 tx_err_pkts;
+       __le64 tx_drop_pkts;
+       __le64 tx_ucast_bytes;
+       __le64 tx_mcast_bytes;
+       __le64 tx_bcast_bytes;
+       __le64 rx_ucast_pkts;
+       __le64 rx_mcast_pkts;
+       __le64 rx_bcast_pkts;
+       __le64 rx_err_pkts;
+       __le64 rx_drop_pkts;
+       __le64 rx_ucast_bytes;
+       __le64 rx_mcast_bytes;
+       __le64 rx_bcast_bytes;
+       __le64 rx_agg_pkts;
+       __le64 rx_agg_bytes;
+       __le64 rx_agg_events;
+       __le64 rx_agg_aborts;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_stat_ctx_clr_stats */
+/* Input (24 bytes) */
+struct hwrm_stat_ctx_clr_stats_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 stat_ctx_id;
+       __le32 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_stat_ctx_clr_stats_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_mgmt_l2_filter_alloc */
+/* Input (56 bytes) */
+struct hwrm_mgmt_l2_filter_alloc_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 flags;
+       #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH                 0x1UL
+       #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH_TX             (0x0UL << 0)
+       #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX             (0x1UL << 0)
+       __le32 enables;
+       #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDRESS         0x1UL
+       #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_OVLAN              0x2UL
+       #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_IVLAN              0x4UL
+       #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_ACTION_ID          0x8UL
+       u8 l2_address[6];
+       u8 unused_0;
+       u8 unused_1;
+       u8 l2_address_mask[6];
+       __le16 ovlan;
+       __le16 ovlan_mask;
+       __le16 ivlan;
+       __le16 ivlan_mask;
+       u8 unused_2;
+       u8 unused_3;
+       __le32 action_id;
+       u8 action_bypass;
+       #define MGMT_L2_FILTER_ALLOC_REQ_ACTION_BYPASS              0x1UL
+       u8 unused_5[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_mgmt_l2_filter_alloc_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 mgmt_l2_filter_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 unused_4;
+       u8 valid;
+};
+
+/* hwrm_mgmt_l2_filter_free */
+/* Input (24 bytes) */
+struct hwrm_mgmt_l2_filter_free_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 mgmt_l2_filter_id;
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_mgmt_l2_filter_free_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_nvm_raw_write_blk */
+/* Input (32 bytes) */
+struct hwrm_nvm_raw_write_blk_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le64 host_src_addr;
+       __le32 dest_addr;
+       __le32 len;
+};
+
+/* Output (16 bytes) */
+struct hwrm_nvm_raw_write_blk_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_nvm_read */
+/* Input (40 bytes) */
+struct hwrm_nvm_read_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le64 host_dest_addr;
+       __le16 dir_idx;
+       u8 unused_0;
+       u8 unused_1;
+       __le32 offset;
+       __le32 len;
+       __le32 unused_2;
+};
+
+/* Output (16 bytes) */
+struct hwrm_nvm_read_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_nvm_raw_dump */
+/* Input (32 bytes) */
+struct hwrm_nvm_raw_dump_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le64 host_dest_addr;
+       __le32 offset;
+       __le32 len;
+};
+
+/* Output (16 bytes) */
+struct hwrm_nvm_raw_dump_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_nvm_get_dir_entries */
+/* Input (24 bytes) */
+struct hwrm_nvm_get_dir_entries_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le64 host_dest_addr;
+};
+
+/* Output (16 bytes) */
+struct hwrm_nvm_get_dir_entries_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_nvm_get_dir_info */
+/* Input (16 bytes) */
+struct hwrm_nvm_get_dir_info_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+};
+
+/* Output (24 bytes) */
+struct hwrm_nvm_get_dir_info_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 entries;
+       __le32 entry_length;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_nvm_write */
+/* Input (40 bytes) */
+struct hwrm_nvm_write_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le64 host_src_addr;
+       __le16 dir_type;
+       __le16 dir_ordinal;
+       __le16 dir_ext;
+       __le16 dir_attr;
+       __le32 dir_data_length;
+       __le16 option;
+       __le16 flags;
+       #define NVM_WRITE_REQ_FLAGS_KEEP_ORIG_ACTIVE_IMG            0x1UL
+};
+
+/* Output (16 bytes) */
+struct hwrm_nvm_write_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_nvm_modify */
+/* Input (40 bytes) */
+struct hwrm_nvm_modify_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le64 host_src_addr;
+       __le16 dir_idx;
+       u8 unused_0;
+       u8 unused_1;
+       __le32 offset;
+       __le32 len;
+       __le32 unused_2;
+};
+
+/* Output (16 bytes) */
+struct hwrm_nvm_modify_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_nvm_find_dir_entry */
+/* Input (32 bytes) */
+struct hwrm_nvm_find_dir_entry_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define NVM_FIND_DIR_ENTRY_REQ_ENABLES_DIR_IDX_VALID       0x1UL
+       __le16 dir_idx;
+       __le16 dir_type;
+       __le16 dir_ordinal;
+       __le16 dir_ext;
+       u8 opt_ordinal;
+       #define NVM_FIND_DIR_ENTRY_REQ_OPT_ORDINAL_MASK     0x3UL
+       #define NVM_FIND_DIR_ENTRY_REQ_OPT_ORDINAL_SFT              0
+       #define NVM_FIND_DIR_ENTRY_REQ_OPT_ORDINAL_EQ              (0x0UL << 0)
+       #define NVM_FIND_DIR_ENTRY_REQ_OPT_ORDINAL_GE              (0x1UL << 0)
+       #define NVM_FIND_DIR_ENTRY_REQ_OPT_ORDINAL_GT              (0x2UL << 0)
+       u8 unused_1[3];
+};
+
+/* Output (32 bytes) */
+struct hwrm_nvm_find_dir_entry_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 dir_item_length;
+       __le32 dir_data_length;
+       __le32 fw_ver;
+       __le16 dir_ordinal;
+       __le16 dir_idx;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_nvm_erase_dir_entry */
+/* Input (24 bytes) */
+struct hwrm_nvm_erase_dir_entry_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 dir_idx;
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_nvm_erase_dir_entry_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_nvm_get_dev_info */
+/* Input (16 bytes) */
+struct hwrm_nvm_get_dev_info_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+};
+
+/* Output (32 bytes) */
+struct hwrm_nvm_get_dev_info_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le16 manufacturer_id;
+       __le16 device_id;
+       __le32 sector_size;
+       __le32 nvram_size;
+       __le32 reserved_size;
+       __le32 available_size;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 valid;
+};
+
+/* hwrm_nvm_mod_dir_entry */
+/* Input (32 bytes) */
+struct hwrm_nvm_mod_dir_entry_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 enables;
+       #define NVM_MOD_DIR_ENTRY_REQ_ENABLES_CHECKSUM              0x1UL
+       __le16 dir_idx;
+       __le16 dir_ordinal;
+       __le16 dir_ext;
+       __le16 dir_attr;
+       __le32 checksum;
+};
+
+/* Output (16 bytes) */
+struct hwrm_nvm_mod_dir_entry_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_nvm_verify_update */
+/* Input (24 bytes) */
+struct hwrm_nvm_verify_update_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 dir_type;
+       __le16 dir_ordinal;
+       __le16 dir_ext;
+       __le16 unused_0;
+};
+
+/* Output (16 bytes) */
+struct hwrm_nvm_verify_update_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_exec_fwd_resp */
+/* Input (120 bytes) */
+struct hwrm_exec_fwd_resp_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 encap_request[24];
+       __le16 encap_resp_target_id;
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_exec_fwd_resp_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_reject_fwd_resp */
+/* Input (120 bytes) */
+struct hwrm_reject_fwd_resp_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le32 encap_request[24];
+       __le16 encap_resp_target_id;
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_reject_fwd_resp_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_fwd_resp */
+/* Input (40 bytes) */
+struct hwrm_fwd_resp_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 encap_resp_target_id;
+       __le16 encap_resp_cmpl_ring;
+       __le16 encap_resp_len;
+       u8 unused_0;
+       u8 unused_1;
+       __le64 encap_resp_addr;
+       __le32 encap_resp[24];
+};
+
+/* Output (16 bytes) */
+struct hwrm_fwd_resp_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_fwd_async_event_cmpl */
+/* Input (32 bytes) */
+struct hwrm_fwd_async_event_cmpl_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       __le16 encap_async_event_target_id;
+       u8 unused_0;
+       u8 unused_1;
+       u8 unused_2[3];
+       u8 unused_3;
+       __le32 encap_async_event_cmpl[4];
+};
+
+/* Output (16 bytes) */
+struct hwrm_fwd_async_event_cmpl_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       __le32 unused_0;
+       u8 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 valid;
+};
+
+/* hwrm_fw_reset */
+/* Input (24 bytes) */
+struct hwrm_fw_reset_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       u8 embedded_proc_type;
+       #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIMP              (0x0UL << 0)
+       #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_APE                (0x1UL << 0)
+       #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_KONG               (0x2UL << 0)
+       #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_BONO               (0x3UL << 0)
+       #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_TANG               (0x4UL << 0)
+       u8 selfrst_status;
+       #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE    (0x0UL << 0)
+       #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP    (0x1UL << 0)
+       #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST         (0x2UL << 0)
+       __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_fw_reset_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       u8 selfrst_status;
+       #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTNONE           (0x0UL << 0)
+       #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTASAP           (0x1UL << 0)
+       #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTPCIERST       (0x2UL << 0)
+       u8 unused_0;
+       __le16 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 unused_4;
+       u8 valid;
+};
+
+/* hwrm_fw_qstatus */
+/* Input (24 bytes) */
+struct hwrm_fw_qstatus_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+       u8 embedded_proc_type;
+       #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_CHIMP    (0x0UL << 0)
+       #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_APE              (0x1UL << 0)
+       #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_KONG             (0x2UL << 0)
+       #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_BONO             (0x3UL << 0)
+       #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_TANG             (0x4UL << 0)
+       u8 unused_0[7];
+};
+
+/* Output (16 bytes) */
+struct hwrm_fw_qstatus_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       u8 selfrst_status;
+       #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTNONE         (0x0UL << 0)
+       #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTASAP         (0x1UL << 0)
+       #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTPCIERST     (0x2UL << 0)
+       u8 unused_0;
+       __le16 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 unused_4;
+       u8 valid;
+};
+
+/* hwrm_temp_monitor_query */
+/* Input (16 bytes) */
+struct hwrm_temp_monitor_query_input {
+       __le16 req_type;
+       __le16 cmpl_ring;
+       __le16 seq_id;
+       __le16 target_id;
+       __le64 resp_addr;
+};
+
+/* Output (16 bytes) */
+struct hwrm_temp_monitor_query_output {
+       __le16 error_code;
+       __le16 req_type;
+       __le16 seq_id;
+       __le16 resp_len;
+       u8 temp;
+       u8 unused_0;
+       __le16 unused_1;
+       u8 unused_2;
+       u8 unused_3;
+       u8 unused_4;
+       u8 valid;
+};
+
+#endif
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_nvm_defs.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_nvm_defs.h
new file mode 100644 (file)
index 0000000..3cf3e1b
--- /dev/null
@@ -0,0 +1,59 @@
+/* Broadcom NetXtreme-C/E network driver.
+ *
+ * Copyright (c) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ */
+
+#ifndef _BNXT_NVM_DEFS_H_
+#define _BNXT_NVM_DEFS_H_
+
+enum bnxt_nvm_directory_type {
+       BNX_DIR_TYPE_UNUSED = 0,
+       BNX_DIR_TYPE_PKG_LOG = 1,
+       BNX_DIR_TYPE_CHIMP_PATCH = 3,
+       BNX_DIR_TYPE_BOOTCODE = 4,
+       BNX_DIR_TYPE_VPD = 5,
+       BNX_DIR_TYPE_EXP_ROM_MBA = 6,
+       BNX_DIR_TYPE_AVS = 7,
+       BNX_DIR_TYPE_PCIE = 8,
+       BNX_DIR_TYPE_PORT_MACRO = 9,
+       BNX_DIR_TYPE_APE_FW = 10,
+       BNX_DIR_TYPE_APE_PATCH = 11,
+       BNX_DIR_TYPE_KONG_FW = 12,
+       BNX_DIR_TYPE_KONG_PATCH = 13,
+       BNX_DIR_TYPE_BONO_FW = 14,
+       BNX_DIR_TYPE_BONO_PATCH = 15,
+       BNX_DIR_TYPE_TANG_FW = 16,
+       BNX_DIR_TYPE_TANG_PATCH = 17,
+       BNX_DIR_TYPE_BOOTCODE_2 = 18,
+       BNX_DIR_TYPE_CCM = 19,
+       BNX_DIR_TYPE_PCI_CFG = 20,
+       BNX_DIR_TYPE_TSCF_UCODE = 21,
+       BNX_DIR_TYPE_ISCSI_BOOT = 22,
+       BNX_DIR_TYPE_ISCSI_BOOT_IPV6 = 24,
+       BNX_DIR_TYPE_ISCSI_BOOT_IPV4N6 = 25,
+       BNX_DIR_TYPE_ISCSI_BOOT_CFG6 = 26,
+       BNX_DIR_TYPE_EXT_PHY = 27,
+       BNX_DIR_TYPE_SHARED_CFG = 40,
+       BNX_DIR_TYPE_PORT_CFG = 41,
+       BNX_DIR_TYPE_FUNC_CFG = 42,
+       BNX_DIR_TYPE_MGMT_CFG = 48,
+       BNX_DIR_TYPE_MGMT_DATA = 49,
+       BNX_DIR_TYPE_MGMT_WEB_DATA = 50,
+       BNX_DIR_TYPE_MGMT_WEB_META = 51,
+       BNX_DIR_TYPE_MGMT_EVENT_LOG = 52,
+       BNX_DIR_TYPE_MGMT_AUDIT_LOG = 53
+};
+
+#define BNX_DIR_ORDINAL_FIRST                  0
+
+#define BNX_DIR_EXT_INACTIVE                   (1 << 0)
+#define BNX_DIR_EXT_UPDATE                     (1 << 1)
+
+#define BNX_DIR_ATTR_NO_CHKSUM                 (1 << 0)
+#define BNX_DIR_ATTR_PROP_STREAM               (1 << 1)
+
+#endif                         /* Don't add anything after this line */
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
new file mode 100644 (file)
index 0000000..60989e7
--- /dev/null
@@ -0,0 +1,816 @@
+/* Broadcom NetXtreme-C/E network driver.
+ *
+ * Copyright (c) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/interrupt.h>
+#include <linux/etherdevice.h>
+#include "bnxt_hsi.h"
+#include "bnxt.h"
+#include "bnxt_sriov.h"
+#include "bnxt_ethtool.h"
+
+#ifdef CONFIG_BNXT_SRIOV
+static int bnxt_vf_ndo_prep(struct bnxt *bp, int vf_id)
+{
+       if (bp->state != BNXT_STATE_OPEN) {
+               netdev_err(bp->dev, "vf ndo called though PF is down\n");
+               return -EINVAL;
+       }
+       if (!bp->pf.active_vfs) {
+               netdev_err(bp->dev, "vf ndo called though sriov is disabled\n");
+               return -EINVAL;
+       }
+       if (vf_id >= bp->pf.max_vfs) {
+               netdev_err(bp->dev, "Invalid VF id %d\n", vf_id);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int bnxt_set_vf_spoofchk(struct net_device *dev, int vf_id, bool setting)
+{
+       struct hwrm_func_cfg_input req = {0};
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_vf_info *vf;
+       bool old_setting = false;
+       u32 func_flags;
+       int rc;
+
+       rc = bnxt_vf_ndo_prep(bp, vf_id);
+       if (rc)
+               return rc;
+
+       vf = &bp->pf.vf[vf_id];
+       if (vf->flags & BNXT_VF_SPOOFCHK)
+               old_setting = true;
+       if (old_setting == setting)
+               return 0;
+
+       func_flags = vf->func_flags;
+       if (setting)
+               func_flags |= FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK;
+       else
+               func_flags &= ~FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK;
+       /*TODO: if the driver supports VLAN filter on guest VLAN,
+        * the spoof check should also include vlan anti-spoofing
+        */
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
+       req.vf_id = cpu_to_le16(vf->fw_fid);
+       req.flags = cpu_to_le32(func_flags);
+       rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc) {
+               vf->func_flags = func_flags;
+               if (setting)
+                       vf->flags |= BNXT_VF_SPOOFCHK;
+               else
+                       vf->flags &= ~BNXT_VF_SPOOFCHK;
+       }
+       return rc;
+}
+
+int bnxt_get_vf_config(struct net_device *dev, int vf_id,
+                      struct ifla_vf_info *ivi)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_vf_info *vf;
+       int rc;
+
+       rc = bnxt_vf_ndo_prep(bp, vf_id);
+       if (rc)
+               return rc;
+
+       ivi->vf = vf_id;
+       vf = &bp->pf.vf[vf_id];
+
+       memcpy(&ivi->mac, vf->mac_addr, ETH_ALEN);
+       ivi->max_tx_rate = vf->max_tx_rate;
+       ivi->min_tx_rate = vf->min_tx_rate;
+       ivi->vlan = vf->vlan;
+       ivi->qos = vf->flags & BNXT_VF_QOS;
+       ivi->spoofchk = vf->flags & BNXT_VF_SPOOFCHK;
+       if (!(vf->flags & BNXT_VF_LINK_FORCED))
+               ivi->linkstate = IFLA_VF_LINK_STATE_AUTO;
+       else if (vf->flags & BNXT_VF_LINK_UP)
+               ivi->linkstate = IFLA_VF_LINK_STATE_ENABLE;
+       else
+               ivi->linkstate = IFLA_VF_LINK_STATE_DISABLE;
+
+       return 0;
+}
+
+int bnxt_set_vf_mac(struct net_device *dev, int vf_id, u8 *mac)
+{
+       struct hwrm_func_cfg_input req = {0};
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_vf_info *vf;
+       int rc;
+
+       rc = bnxt_vf_ndo_prep(bp, vf_id);
+       if (rc)
+               return rc;
+       /* reject bc or mc mac addr, zero mac addr means allow
+        * VF to use its own mac addr
+        */
+       if (is_multicast_ether_addr(mac)) {
+               netdev_err(dev, "Invalid VF ethernet address\n");
+               return -EINVAL;
+       }
+       vf = &bp->pf.vf[vf_id];
+
+       memcpy(vf->mac_addr, mac, ETH_ALEN);
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
+       req.vf_id = cpu_to_le16(vf->fw_fid);
+       req.flags = cpu_to_le32(vf->func_flags);
+       req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_DFLT_MAC_ADDR);
+       memcpy(req.dflt_mac_addr, mac, ETH_ALEN);
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
+int bnxt_set_vf_vlan(struct net_device *dev, int vf_id, u16 vlan_id, u8 qos)
+{
+       struct hwrm_func_cfg_input req = {0};
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_vf_info *vf;
+       u16 vlan_tag;
+       int rc;
+
+       rc = bnxt_vf_ndo_prep(bp, vf_id);
+       if (rc)
+               return rc;
+
+       /* TODO: needed to implement proper handling of user priority,
+        * currently fail the command if there is valid priority
+        */
+       if (vlan_id > 4095 || qos)
+               return -EINVAL;
+
+       vf = &bp->pf.vf[vf_id];
+       vlan_tag = vlan_id;
+       if (vlan_tag == vf->vlan)
+               return 0;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
+       req.vf_id = cpu_to_le16(vf->fw_fid);
+       req.flags = cpu_to_le32(vf->func_flags);
+       req.dflt_vlan = cpu_to_le16(vlan_tag);
+       req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_DFLT_VLAN);
+       rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc)
+               vf->vlan = vlan_tag;
+       return rc;
+}
+
+int bnxt_set_vf_bw(struct net_device *dev, int vf_id, int min_tx_rate,
+                  int max_tx_rate)
+{
+       struct hwrm_func_cfg_input req = {0};
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_vf_info *vf;
+       u32 pf_link_speed;
+       int rc;
+
+       rc = bnxt_vf_ndo_prep(bp, vf_id);
+       if (rc)
+               return rc;
+
+       vf = &bp->pf.vf[vf_id];
+       pf_link_speed = bnxt_fw_to_ethtool_speed(bp->link_info.link_speed);
+       if (max_tx_rate > pf_link_speed) {
+               netdev_info(bp->dev, "max tx rate %d exceed PF link speed for VF %d\n",
+                           max_tx_rate, vf_id);
+               return -EINVAL;
+       }
+
+       if (min_tx_rate > pf_link_speed || min_tx_rate > max_tx_rate) {
+               netdev_info(bp->dev, "min tx rate %d is invalid for VF %d\n",
+                           min_tx_rate, vf_id);
+               return -EINVAL;
+       }
+       if (min_tx_rate == vf->min_tx_rate && max_tx_rate == vf->max_tx_rate)
+               return 0;
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
+       req.vf_id = cpu_to_le16(vf->fw_fid);
+       req.flags = cpu_to_le32(vf->func_flags);
+       req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_MAX_BW);
+       req.max_bw = cpu_to_le32(max_tx_rate);
+       req.enables |= cpu_to_le32(FUNC_CFG_REQ_ENABLES_MIN_BW);
+       req.min_bw = cpu_to_le32(min_tx_rate);
+       rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc) {
+               vf->min_tx_rate = min_tx_rate;
+               vf->max_tx_rate = max_tx_rate;
+       }
+       return rc;
+}
+
+int bnxt_set_vf_link_state(struct net_device *dev, int vf_id, int link)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_vf_info *vf;
+       int rc;
+
+       rc = bnxt_vf_ndo_prep(bp, vf_id);
+       if (rc)
+               return rc;
+
+       vf = &bp->pf.vf[vf_id];
+
+       vf->flags &= ~(BNXT_VF_LINK_UP | BNXT_VF_LINK_FORCED);
+       switch (link) {
+       case IFLA_VF_LINK_STATE_AUTO:
+               vf->flags |= BNXT_VF_LINK_UP;
+               break;
+       case IFLA_VF_LINK_STATE_DISABLE:
+               vf->flags |= BNXT_VF_LINK_FORCED;
+               break;
+       case IFLA_VF_LINK_STATE_ENABLE:
+               vf->flags |= BNXT_VF_LINK_UP | BNXT_VF_LINK_FORCED;
+               break;
+       default:
+               netdev_err(bp->dev, "Invalid link option\n");
+               rc = -EINVAL;
+               break;
+       }
+       /* CHIMP TODO: send msg to VF to update new link state */
+
+       return rc;
+}
+
+static int bnxt_set_vf_attr(struct bnxt *bp, int num_vfs)
+{
+       int i;
+       struct bnxt_vf_info *vf;
+
+       for (i = 0; i < num_vfs; i++) {
+               vf = &bp->pf.vf[i];
+               memset(vf, 0, sizeof(*vf));
+               vf->flags = BNXT_VF_QOS | BNXT_VF_LINK_UP;
+       }
+       return 0;
+}
+
+static int bnxt_hwrm_func_vf_resource_free(struct bnxt *bp)
+{
+       int i, rc = 0;
+       struct bnxt_pf_info *pf = &bp->pf;
+       struct hwrm_func_vf_resc_free_input req = {0};
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_VF_RESC_FREE, -1, -1);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       for (i = pf->first_vf_id; i < pf->first_vf_id + pf->active_vfs; i++) {
+               req.vf_id = cpu_to_le16(i);
+               rc = _hwrm_send_message(bp, &req, sizeof(req),
+                                       HWRM_CMD_TIMEOUT);
+               if (rc)
+                       break;
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static void bnxt_free_vf_resources(struct bnxt *bp)
+{
+       struct pci_dev *pdev = bp->pdev;
+       int i;
+
+       kfree(bp->pf.vf_event_bmap);
+       bp->pf.vf_event_bmap = NULL;
+
+       for (i = 0; i < 4; i++) {
+               if (bp->pf.hwrm_cmd_req_addr[i]) {
+                       dma_free_coherent(&pdev->dev, BNXT_PAGE_SIZE,
+                                         bp->pf.hwrm_cmd_req_addr[i],
+                                         bp->pf.hwrm_cmd_req_dma_addr[i]);
+                       bp->pf.hwrm_cmd_req_addr[i] = NULL;
+               }
+       }
+
+       kfree(bp->pf.vf);
+       bp->pf.vf = NULL;
+}
+
+static int bnxt_alloc_vf_resources(struct bnxt *bp, int num_vfs)
+{
+       struct pci_dev *pdev = bp->pdev;
+       u32 nr_pages, size, i, j, k = 0;
+
+       bp->pf.vf = kcalloc(num_vfs, sizeof(struct bnxt_vf_info), GFP_KERNEL);
+       if (!bp->pf.vf)
+               return -ENOMEM;
+
+       bnxt_set_vf_attr(bp, num_vfs);
+
+       size = num_vfs * BNXT_HWRM_REQ_MAX_SIZE;
+       nr_pages = size / BNXT_PAGE_SIZE;
+       if (size & (BNXT_PAGE_SIZE - 1))
+               nr_pages++;
+
+       for (i = 0; i < nr_pages; i++) {
+               bp->pf.hwrm_cmd_req_addr[i] =
+                       dma_alloc_coherent(&pdev->dev, BNXT_PAGE_SIZE,
+                                          &bp->pf.hwrm_cmd_req_dma_addr[i],
+                                          GFP_KERNEL);
+
+               if (!bp->pf.hwrm_cmd_req_addr[i])
+                       return -ENOMEM;
+
+               for (j = 0; j < BNXT_HWRM_REQS_PER_PAGE && k < num_vfs; j++) {
+                       struct bnxt_vf_info *vf = &bp->pf.vf[k];
+
+                       vf->hwrm_cmd_req_addr = bp->pf.hwrm_cmd_req_addr[i] +
+                                               j * BNXT_HWRM_REQ_MAX_SIZE;
+                       vf->hwrm_cmd_req_dma_addr =
+                               bp->pf.hwrm_cmd_req_dma_addr[i] + j *
+                               BNXT_HWRM_REQ_MAX_SIZE;
+                       k++;
+               }
+       }
+
+       /* Max 128 VF's */
+       bp->pf.vf_event_bmap = kzalloc(16, GFP_KERNEL);
+       if (!bp->pf.vf_event_bmap)
+               return -ENOMEM;
+
+       bp->pf.hwrm_cmd_req_pages = nr_pages;
+       return 0;
+}
+
+static int bnxt_hwrm_func_buf_rgtr(struct bnxt *bp)
+{
+       struct hwrm_func_buf_rgtr_input req = {0};
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_BUF_RGTR, -1, -1);
+
+       req.req_buf_num_pages = cpu_to_le16(bp->pf.hwrm_cmd_req_pages);
+       req.req_buf_page_size = cpu_to_le16(BNXT_PAGE_SHIFT);
+       req.req_buf_len = cpu_to_le16(BNXT_HWRM_REQ_MAX_SIZE);
+       req.req_buf_page_addr0 = cpu_to_le64(bp->pf.hwrm_cmd_req_dma_addr[0]);
+       req.req_buf_page_addr1 = cpu_to_le64(bp->pf.hwrm_cmd_req_dma_addr[1]);
+       req.req_buf_page_addr2 = cpu_to_le64(bp->pf.hwrm_cmd_req_dma_addr[2]);
+       req.req_buf_page_addr3 = cpu_to_le64(bp->pf.hwrm_cmd_req_dma_addr[3]);
+
+       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
+/* only call by PF to reserve resources for VF */
+static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs)
+{
+       u32 rc = 0, mtu, i;
+       u16 vf_tx_rings, vf_rx_rings, vf_cp_rings, vf_stat_ctx, vf_vnics;
+       struct hwrm_func_cfg_input req = {0};
+       struct bnxt_pf_info *pf = &bp->pf;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
+
+       /* Remaining rings are distributed equally amongs VF's for now */
+       /* TODO: the following workaroud is needed to restrict total number
+        * of vf_cp_rings not exceed number of HW ring groups. This WA should
+        * be removed once new HWRM provides HW ring groups capability in
+        * hwrm_func_qcap.
+        */
+       vf_cp_rings = min_t(u16, bp->pf.max_cp_rings, bp->pf.max_stat_ctxs);
+       vf_cp_rings = (vf_cp_rings - bp->cp_nr_rings) / *num_vfs;
+       /* TODO: restore this logic below once the WA above is removed */
+       /* vf_cp_rings = (bp->pf.max_cp_rings - bp->cp_nr_rings) / *num_vfs; */
+       vf_stat_ctx = (bp->pf.max_stat_ctxs - bp->num_stat_ctxs) / *num_vfs;
+       if (bp->flags & BNXT_FLAG_AGG_RINGS)
+               vf_rx_rings = (bp->pf.max_rx_rings - bp->rx_nr_rings * 2) /
+                             *num_vfs;
+       else
+               vf_rx_rings = (bp->pf.max_rx_rings - bp->rx_nr_rings) /
+                             *num_vfs;
+       vf_tx_rings = (bp->pf.max_tx_rings - bp->tx_nr_rings) / *num_vfs;
+
+       req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_MTU |
+                                 FUNC_CFG_REQ_ENABLES_MRU |
+                                 FUNC_CFG_REQ_ENABLES_NUM_RSSCOS_CTXS |
+                                 FUNC_CFG_REQ_ENABLES_NUM_STAT_CTXS |
+                                 FUNC_CFG_REQ_ENABLES_NUM_CMPL_RINGS |
+                                 FUNC_CFG_REQ_ENABLES_NUM_TX_RINGS |
+                                 FUNC_CFG_REQ_ENABLES_NUM_RX_RINGS |
+                                 FUNC_CFG_REQ_ENABLES_NUM_L2_CTXS |
+                                 FUNC_CFG_REQ_ENABLES_NUM_VNICS);
+
+       mtu = bp->dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+       req.mru = cpu_to_le16(mtu);
+       req.mtu = cpu_to_le16(mtu);
+
+       req.num_rsscos_ctxs = cpu_to_le16(1);
+       req.num_cmpl_rings = cpu_to_le16(vf_cp_rings);
+       req.num_tx_rings = cpu_to_le16(vf_tx_rings);
+       req.num_rx_rings = cpu_to_le16(vf_rx_rings);
+       req.num_l2_ctxs = cpu_to_le16(4);
+       vf_vnics = 1;
+
+       req.num_vnics = cpu_to_le16(vf_vnics);
+       /* FIXME spec currently uses 1 bit for stats ctx */
+       req.num_stat_ctxs = cpu_to_le16(vf_stat_ctx);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       for (i = 0; i < *num_vfs; i++) {
+               req.vf_id = cpu_to_le16(pf->first_vf_id + i);
+               rc = _hwrm_send_message(bp, &req, sizeof(req),
+                                       HWRM_CMD_TIMEOUT);
+               if (rc)
+                       break;
+               bp->pf.active_vfs = i + 1;
+               bp->pf.vf[i].fw_fid = le16_to_cpu(req.vf_id);
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       if (!rc) {
+               bp->pf.max_pf_tx_rings = bp->tx_nr_rings;
+               if (bp->flags & BNXT_FLAG_AGG_RINGS)
+                       bp->pf.max_pf_rx_rings = bp->rx_nr_rings * 2;
+               else
+                       bp->pf.max_pf_rx_rings = bp->rx_nr_rings;
+       }
+       return rc;
+}
+
+static int bnxt_sriov_enable(struct bnxt *bp, int *num_vfs)
+{
+       int rc = 0, vfs_supported;
+       int min_rx_rings, min_tx_rings, min_rss_ctxs;
+       int tx_ok = 0, rx_ok = 0, rss_ok = 0;
+
+       /* Check if we can enable requested num of vf's. At a mininum
+        * we require 1 RX 1 TX rings for each VF. In this minimum conf
+        * features like TPA will not be available.
+        */
+       vfs_supported = *num_vfs;
+
+       while (vfs_supported) {
+               min_rx_rings = vfs_supported;
+               min_tx_rings = vfs_supported;
+               min_rss_ctxs = vfs_supported;
+
+               if (bp->flags & BNXT_FLAG_AGG_RINGS) {
+                       if (bp->pf.max_rx_rings - bp->rx_nr_rings * 2 >=
+                           min_rx_rings)
+                               rx_ok = 1;
+               } else {
+                       if (bp->pf.max_rx_rings - bp->rx_nr_rings >=
+                           min_rx_rings)
+                               rx_ok = 1;
+               }
+
+               if (bp->pf.max_tx_rings - bp->tx_nr_rings >= min_tx_rings)
+                       tx_ok = 1;
+
+               if (bp->pf.max_rsscos_ctxs - bp->rsscos_nr_ctxs >= min_rss_ctxs)
+                       rss_ok = 1;
+
+               if (tx_ok && rx_ok && rss_ok)
+                       break;
+
+               vfs_supported--;
+       }
+
+       if (!vfs_supported) {
+               netdev_err(bp->dev, "Cannot enable VF's as all resources are used by PF\n");
+               return -EINVAL;
+       }
+
+       if (vfs_supported != *num_vfs) {
+               netdev_info(bp->dev, "Requested VFs %d, can enable %d\n",
+                           *num_vfs, vfs_supported);
+               *num_vfs = vfs_supported;
+       }
+
+       rc = bnxt_alloc_vf_resources(bp, *num_vfs);
+       if (rc)
+               goto err_out1;
+
+       /* Reserve resources for VFs */
+       rc = bnxt_hwrm_func_cfg(bp, num_vfs);
+       if (rc)
+               goto err_out2;
+
+       /* Register buffers for VFs */
+       rc = bnxt_hwrm_func_buf_rgtr(bp);
+       if (rc)
+               goto err_out2;
+
+       rc = pci_enable_sriov(bp->pdev, *num_vfs);
+       if (rc)
+               goto err_out2;
+
+       return 0;
+
+err_out2:
+       /* Free the resources reserved for various VF's */
+       bnxt_hwrm_func_vf_resource_free(bp);
+
+err_out1:
+       bnxt_free_vf_resources(bp);
+
+       return rc;
+}
+
+void bnxt_sriov_disable(struct bnxt *bp)
+{
+       if (!bp->pf.active_vfs)
+               return;
+
+       pci_disable_sriov(bp->pdev);
+
+       /* Free the resources reserved for various VF's */
+       bnxt_hwrm_func_vf_resource_free(bp);
+
+       bnxt_free_vf_resources(bp);
+
+       bp->pf.active_vfs = 0;
+       bp->pf.max_pf_rx_rings = bp->pf.max_rx_rings;
+       bp->pf.max_pf_tx_rings = bp->pf.max_tx_rings;
+}
+
+int bnxt_sriov_configure(struct pci_dev *pdev, int num_vfs)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct bnxt *bp = netdev_priv(dev);
+
+       if (!(bp->flags & BNXT_FLAG_USING_MSIX)) {
+               netdev_warn(dev, "Not allow SRIOV if the irq mode is not MSIX\n");
+               return 0;
+       }
+
+       rtnl_lock();
+       if (!netif_running(dev)) {
+               netdev_warn(dev, "Reject SRIOV config request since if is down!\n");
+               rtnl_unlock();
+               return 0;
+       }
+       bp->sriov_cfg = true;
+       rtnl_unlock();
+       if (!num_vfs) {
+               bnxt_sriov_disable(bp);
+               return 0;
+       }
+
+       /* Check if enabled VFs is same as requested */
+       if (num_vfs == bp->pf.active_vfs)
+               return 0;
+
+       bnxt_sriov_enable(bp, &num_vfs);
+
+       bp->sriov_cfg = false;
+       wake_up(&bp->sriov_cfg_wait);
+
+       return num_vfs;
+}
+
+static int bnxt_hwrm_fwd_resp(struct bnxt *bp, struct bnxt_vf_info *vf,
+                             void *encap_resp, __le64 encap_resp_addr,
+                             __le16 encap_resp_cpr, u32 msg_size)
+{
+       int rc = 0;
+       struct hwrm_fwd_resp_input req = {0};
+       struct hwrm_fwd_resp_output *resp = bp->hwrm_cmd_resp_addr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FWD_RESP, -1, -1);
+
+       /* Set the new target id */
+       req.target_id = cpu_to_le16(vf->fw_fid);
+       req.encap_resp_len = cpu_to_le16(msg_size);
+       req.encap_resp_addr = encap_resp_addr;
+       req.encap_resp_cmpl_ring = encap_resp_cpr;
+       memcpy(req.encap_resp, encap_resp, msg_size);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+
+       if (rc) {
+               netdev_err(bp->dev, "hwrm_fwd_resp failed. rc:%d\n", rc);
+               goto fwd_resp_exit;
+       }
+
+       if (resp->error_code) {
+               netdev_err(bp->dev, "hwrm_fwd_resp error %d\n",
+                          resp->error_code);
+               rc = -1;
+       }
+
+fwd_resp_exit:
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_hwrm_fwd_err_resp(struct bnxt *bp, struct bnxt_vf_info *vf,
+                                 u32 msg_size)
+{
+       int rc = 0;
+       struct hwrm_reject_fwd_resp_input req = {0};
+       struct hwrm_reject_fwd_resp_output *resp = bp->hwrm_cmd_resp_addr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_REJECT_FWD_RESP, -1, -1);
+       /* Set the new target id */
+       req.target_id = cpu_to_le16(vf->fw_fid);
+       memcpy(req.encap_request, vf->hwrm_cmd_req_addr, msg_size);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+
+       if (rc) {
+               netdev_err(bp->dev, "hwrm_fwd_err_resp failed. rc:%d\n", rc);
+               goto fwd_err_resp_exit;
+       }
+
+       if (resp->error_code) {
+               netdev_err(bp->dev, "hwrm_fwd_err_resp error %d\n",
+                          resp->error_code);
+               rc = -1;
+       }
+
+fwd_err_resp_exit:
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_hwrm_exec_fwd_resp(struct bnxt *bp, struct bnxt_vf_info *vf,
+                                  u32 msg_size)
+{
+       int rc = 0;
+       struct hwrm_exec_fwd_resp_input req = {0};
+       struct hwrm_exec_fwd_resp_output *resp = bp->hwrm_cmd_resp_addr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_EXEC_FWD_RESP, -1, -1);
+       /* Set the new target id */
+       req.target_id = cpu_to_le16(vf->fw_fid);
+       memcpy(req.encap_request, vf->hwrm_cmd_req_addr, msg_size);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+
+       if (rc) {
+               netdev_err(bp->dev, "hwrm_exec_fw_resp failed. rc:%d\n", rc);
+               goto exec_fwd_resp_exit;
+       }
+
+       if (resp->error_code) {
+               netdev_err(bp->dev, "hwrm_exec_fw_resp error %d\n",
+                          resp->error_code);
+               rc = -1;
+       }
+
+exec_fwd_resp_exit:
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_vf_validate_set_mac(struct bnxt *bp, struct bnxt_vf_info *vf)
+{
+       u32 msg_size = sizeof(struct hwrm_cfa_l2_filter_alloc_input);
+       struct hwrm_cfa_l2_filter_alloc_input *req =
+               (struct hwrm_cfa_l2_filter_alloc_input *)vf->hwrm_cmd_req_addr;
+
+       if (!is_valid_ether_addr(vf->mac_addr) ||
+           ether_addr_equal((const u8 *)req->l2_addr, vf->mac_addr))
+               return bnxt_hwrm_exec_fwd_resp(bp, vf, msg_size);
+       else
+               return bnxt_hwrm_fwd_err_resp(bp, vf, msg_size);
+}
+
+static int bnxt_vf_set_link(struct bnxt *bp, struct bnxt_vf_info *vf)
+{
+       int rc = 0;
+
+       if (!(vf->flags & BNXT_VF_LINK_FORCED)) {
+               /* real link */
+               rc = bnxt_hwrm_exec_fwd_resp(
+                       bp, vf, sizeof(struct hwrm_port_phy_qcfg_input));
+       } else {
+               struct hwrm_port_phy_qcfg_output phy_qcfg_resp;
+               struct hwrm_port_phy_qcfg_input *phy_qcfg_req;
+
+               phy_qcfg_req =
+               (struct hwrm_port_phy_qcfg_input *)vf->hwrm_cmd_req_addr;
+               mutex_lock(&bp->hwrm_cmd_lock);
+               memcpy(&phy_qcfg_resp, &bp->link_info.phy_qcfg_resp,
+                      sizeof(phy_qcfg_resp));
+               mutex_unlock(&bp->hwrm_cmd_lock);
+               phy_qcfg_resp.seq_id = phy_qcfg_req->seq_id;
+
+               if (vf->flags & BNXT_VF_LINK_UP) {
+                       /* if physical link is down, force link up on VF */
+                       if (phy_qcfg_resp.link ==
+                           PORT_PHY_QCFG_RESP_LINK_NO_LINK) {
+                               phy_qcfg_resp.link =
+                                       PORT_PHY_QCFG_RESP_LINK_LINK;
+                               if (phy_qcfg_resp.auto_link_speed)
+                                       phy_qcfg_resp.link_speed =
+                                               phy_qcfg_resp.auto_link_speed;
+                               else
+                                       phy_qcfg_resp.link_speed =
+                                               phy_qcfg_resp.force_link_speed;
+                               phy_qcfg_resp.duplex =
+                                       PORT_PHY_QCFG_RESP_DUPLEX_FULL;
+                               phy_qcfg_resp.pause =
+                                       (PORT_PHY_QCFG_RESP_PAUSE_TX |
+                                        PORT_PHY_QCFG_RESP_PAUSE_RX);
+                       }
+               } else {
+                       /* force link down */
+                       phy_qcfg_resp.link = PORT_PHY_QCFG_RESP_LINK_NO_LINK;
+                       phy_qcfg_resp.link_speed = 0;
+                       phy_qcfg_resp.duplex = PORT_PHY_QCFG_RESP_DUPLEX_HALF;
+                       phy_qcfg_resp.pause = 0;
+               }
+               rc = bnxt_hwrm_fwd_resp(bp, vf, &phy_qcfg_resp,
+                                       phy_qcfg_req->resp_addr,
+                                       phy_qcfg_req->cmpl_ring,
+                                       sizeof(phy_qcfg_resp));
+       }
+       return rc;
+}
+
+static int bnxt_vf_req_validate_snd(struct bnxt *bp, struct bnxt_vf_info *vf)
+{
+       int rc = 0;
+       struct hwrm_cmd_req_hdr *encap_req = vf->hwrm_cmd_req_addr;
+       u32 req_type = le32_to_cpu(encap_req->cmpl_ring_req_type) & 0xffff;
+
+       switch (req_type) {
+       case HWRM_CFA_L2_FILTER_ALLOC:
+               rc = bnxt_vf_validate_set_mac(bp, vf);
+               break;
+       case HWRM_FUNC_CFG:
+               /* TODO Validate if VF is allowed to change mac address,
+                * mtu, num of rings etc
+                */
+               rc = bnxt_hwrm_exec_fwd_resp(
+                       bp, vf, sizeof(struct hwrm_func_cfg_input));
+               break;
+       case HWRM_PORT_PHY_QCFG:
+               rc = bnxt_vf_set_link(bp, vf);
+               break;
+       default:
+               break;
+       }
+       return rc;
+}
+
+void bnxt_hwrm_exec_fwd_req(struct bnxt *bp)
+{
+       u32 i = 0, active_vfs = bp->pf.active_vfs, vf_id;
+
+       /* Scan through VF's and process commands */
+       while (1) {
+               vf_id = find_next_bit(bp->pf.vf_event_bmap, active_vfs, i);
+               if (vf_id >= active_vfs)
+                       break;
+
+               clear_bit(vf_id, bp->pf.vf_event_bmap);
+               bnxt_vf_req_validate_snd(bp, &bp->pf.vf[vf_id]);
+               i = vf_id + 1;
+       }
+}
+
+void bnxt_update_vf_mac(struct bnxt *bp)
+{
+       struct hwrm_func_qcaps_input req = {0};
+       struct hwrm_func_qcaps_output *resp = bp->hwrm_cmd_resp_addr;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_QCAPS, -1, -1);
+       req.fid = cpu_to_le16(0xffff);
+
+       mutex_lock(&bp->hwrm_cmd_lock);
+       if (_hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT))
+               goto update_vf_mac_exit;
+
+       if (!is_valid_ether_addr(resp->perm_mac_address))
+               goto update_vf_mac_exit;
+
+       if (ether_addr_equal(resp->perm_mac_address, bp->vf.mac_addr))
+               goto update_vf_mac_exit;
+
+       memcpy(bp->vf.mac_addr, resp->perm_mac_address, ETH_ALEN);
+       memcpy(bp->dev->dev_addr, bp->vf.mac_addr, ETH_ALEN);
+update_vf_mac_exit:
+       mutex_unlock(&bp->hwrm_cmd_lock);
+}
+
+#else
+
+void bnxt_sriov_disable(struct bnxt *bp)
+{
+}
+
+void bnxt_hwrm_exec_fwd_req(struct bnxt *bp)
+{
+       netdev_err(bp->dev, "Invalid VF message received when SRIOV is not enable\n");
+}
+
+void bnxt_update_vf_mac(struct bnxt *bp)
+{
+}
+#endif
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h
new file mode 100644 (file)
index 0000000..c151280
--- /dev/null
@@ -0,0 +1,23 @@
+/* Broadcom NetXtreme-C/E network driver.
+ *
+ * Copyright (c) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ */
+
+#ifndef BNXT_SRIOV_H
+#define BNXT_SRIOV_H
+
+int bnxt_get_vf_config(struct net_device *, int, struct ifla_vf_info *);
+int bnxt_set_vf_mac(struct net_device *, int, u8 *);
+int bnxt_set_vf_vlan(struct net_device *, int, u16, u8);
+int bnxt_set_vf_bw(struct net_device *, int, int, int);
+int bnxt_set_vf_link_state(struct net_device *, int, int);
+int bnxt_set_vf_spoofchk(struct net_device *, int, bool);
+int bnxt_sriov_configure(struct pci_dev *pdev, int num_vfs);
+void bnxt_sriov_disable(struct bnxt *);
+void bnxt_hwrm_exec_fwd_req(struct bnxt *);
+void bnxt_update_vf_mac(struct bnxt *);
+#endif
index 1805541b4240e72c79445ea6f50bbc2a74ba13c7..17f017ab4dac6d560ffd0e79aa5f0c95a0e8077c 100644 (file)
@@ -205,6 +205,23 @@ enum dma_reg {
        DMA_INDEX2RING_5,
        DMA_INDEX2RING_6,
        DMA_INDEX2RING_7,
+       DMA_RING0_TIMEOUT,
+       DMA_RING1_TIMEOUT,
+       DMA_RING2_TIMEOUT,
+       DMA_RING3_TIMEOUT,
+       DMA_RING4_TIMEOUT,
+       DMA_RING5_TIMEOUT,
+       DMA_RING6_TIMEOUT,
+       DMA_RING7_TIMEOUT,
+       DMA_RING8_TIMEOUT,
+       DMA_RING9_TIMEOUT,
+       DMA_RING10_TIMEOUT,
+       DMA_RING11_TIMEOUT,
+       DMA_RING12_TIMEOUT,
+       DMA_RING13_TIMEOUT,
+       DMA_RING14_TIMEOUT,
+       DMA_RING15_TIMEOUT,
+       DMA_RING16_TIMEOUT,
 };
 
 static const u8 bcmgenet_dma_regs_v3plus[] = {
@@ -216,6 +233,23 @@ static const u8 bcmgenet_dma_regs_v3plus[] = {
        [DMA_PRIORITY_0]        = 0x30,
        [DMA_PRIORITY_1]        = 0x34,
        [DMA_PRIORITY_2]        = 0x38,
+       [DMA_RING0_TIMEOUT]     = 0x2C,
+       [DMA_RING1_TIMEOUT]     = 0x30,
+       [DMA_RING2_TIMEOUT]     = 0x34,
+       [DMA_RING3_TIMEOUT]     = 0x38,
+       [DMA_RING4_TIMEOUT]     = 0x3c,
+       [DMA_RING5_TIMEOUT]     = 0x40,
+       [DMA_RING6_TIMEOUT]     = 0x44,
+       [DMA_RING7_TIMEOUT]     = 0x48,
+       [DMA_RING8_TIMEOUT]     = 0x4c,
+       [DMA_RING9_TIMEOUT]     = 0x50,
+       [DMA_RING10_TIMEOUT]    = 0x54,
+       [DMA_RING11_TIMEOUT]    = 0x58,
+       [DMA_RING12_TIMEOUT]    = 0x5c,
+       [DMA_RING13_TIMEOUT]    = 0x60,
+       [DMA_RING14_TIMEOUT]    = 0x64,
+       [DMA_RING15_TIMEOUT]    = 0x68,
+       [DMA_RING16_TIMEOUT]    = 0x6C,
        [DMA_INDEX2RING_0]      = 0x70,
        [DMA_INDEX2RING_1]      = 0x74,
        [DMA_INDEX2RING_2]      = 0x78,
@@ -235,6 +269,23 @@ static const u8 bcmgenet_dma_regs_v2[] = {
        [DMA_PRIORITY_0]        = 0x34,
        [DMA_PRIORITY_1]        = 0x38,
        [DMA_PRIORITY_2]        = 0x3C,
+       [DMA_RING0_TIMEOUT]     = 0x2C,
+       [DMA_RING1_TIMEOUT]     = 0x30,
+       [DMA_RING2_TIMEOUT]     = 0x34,
+       [DMA_RING3_TIMEOUT]     = 0x38,
+       [DMA_RING4_TIMEOUT]     = 0x3c,
+       [DMA_RING5_TIMEOUT]     = 0x40,
+       [DMA_RING6_TIMEOUT]     = 0x44,
+       [DMA_RING7_TIMEOUT]     = 0x48,
+       [DMA_RING8_TIMEOUT]     = 0x4c,
+       [DMA_RING9_TIMEOUT]     = 0x50,
+       [DMA_RING10_TIMEOUT]    = 0x54,
+       [DMA_RING11_TIMEOUT]    = 0x58,
+       [DMA_RING12_TIMEOUT]    = 0x5c,
+       [DMA_RING13_TIMEOUT]    = 0x60,
+       [DMA_RING14_TIMEOUT]    = 0x64,
+       [DMA_RING15_TIMEOUT]    = 0x68,
+       [DMA_RING16_TIMEOUT]    = 0x6C,
 };
 
 static const u8 bcmgenet_dma_regs_v1[] = {
@@ -245,6 +296,23 @@ static const u8 bcmgenet_dma_regs_v1[] = {
        [DMA_PRIORITY_0]        = 0x34,
        [DMA_PRIORITY_1]        = 0x38,
        [DMA_PRIORITY_2]        = 0x3C,
+       [DMA_RING0_TIMEOUT]     = 0x2C,
+       [DMA_RING1_TIMEOUT]     = 0x30,
+       [DMA_RING2_TIMEOUT]     = 0x34,
+       [DMA_RING3_TIMEOUT]     = 0x38,
+       [DMA_RING4_TIMEOUT]     = 0x3c,
+       [DMA_RING5_TIMEOUT]     = 0x40,
+       [DMA_RING6_TIMEOUT]     = 0x44,
+       [DMA_RING7_TIMEOUT]     = 0x48,
+       [DMA_RING8_TIMEOUT]     = 0x4c,
+       [DMA_RING9_TIMEOUT]     = 0x50,
+       [DMA_RING10_TIMEOUT]    = 0x54,
+       [DMA_RING11_TIMEOUT]    = 0x58,
+       [DMA_RING12_TIMEOUT]    = 0x5c,
+       [DMA_RING13_TIMEOUT]    = 0x60,
+       [DMA_RING14_TIMEOUT]    = 0x64,
+       [DMA_RING15_TIMEOUT]    = 0x68,
+       [DMA_RING16_TIMEOUT]    = 0x6C,
 };
 
 /* Set at runtime once bcmgenet version is known */
@@ -498,6 +566,85 @@ static void bcmgenet_set_msglevel(struct net_device *dev, u32 level)
        priv->msg_enable = level;
 }
 
+static int bcmgenet_get_coalesce(struct net_device *dev,
+                                struct ethtool_coalesce *ec)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+
+       ec->tx_max_coalesced_frames =
+               bcmgenet_tdma_ring_readl(priv, DESC_INDEX,
+                                        DMA_MBUF_DONE_THRESH);
+       ec->rx_max_coalesced_frames =
+               bcmgenet_rdma_ring_readl(priv, DESC_INDEX,
+                                        DMA_MBUF_DONE_THRESH);
+       ec->rx_coalesce_usecs =
+               bcmgenet_rdma_readl(priv, DMA_RING16_TIMEOUT) * 8192 / 1000;
+
+       return 0;
+}
+
+static int bcmgenet_set_coalesce(struct net_device *dev,
+                                struct ethtool_coalesce *ec)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       unsigned int i;
+       u32 reg;
+
+       /* Base system clock is 125Mhz, DMA timeout is this reference clock
+        * divided by 1024, which yields roughly 8.192us, our maximum value
+        * has to fit in the DMA_TIMEOUT_MASK (16 bits)
+        */
+       if (ec->tx_max_coalesced_frames > DMA_INTR_THRESHOLD_MASK ||
+           ec->tx_max_coalesced_frames == 0 ||
+           ec->rx_max_coalesced_frames > DMA_INTR_THRESHOLD_MASK ||
+           ec->rx_coalesce_usecs > (DMA_TIMEOUT_MASK * 8) + 1)
+               return -EINVAL;
+
+       if (ec->rx_coalesce_usecs == 0 && ec->rx_max_coalesced_frames == 0)
+               return -EINVAL;
+
+       /* GENET TDMA hardware does not support a configurable timeout, but will
+        * always generate an interrupt either after MBDONE packets have been
+        * transmitted, or when the ring is emtpy.
+        */
+       if (ec->tx_coalesce_usecs || ec->tx_coalesce_usecs_high ||
+           ec->tx_coalesce_usecs_irq || ec->tx_coalesce_usecs_low)
+               return -EOPNOTSUPP;
+
+       /* Program all TX queues with the same values, as there is no
+        * ethtool knob to do coalescing on a per-queue basis
+        */
+       for (i = 0; i < priv->hw_params->tx_queues; i++)
+               bcmgenet_tdma_ring_writel(priv, i,
+                                         ec->tx_max_coalesced_frames,
+                                         DMA_MBUF_DONE_THRESH);
+       bcmgenet_tdma_ring_writel(priv, DESC_INDEX,
+                                 ec->tx_max_coalesced_frames,
+                                 DMA_MBUF_DONE_THRESH);
+
+       for (i = 0; i < priv->hw_params->rx_queues; i++) {
+               bcmgenet_rdma_ring_writel(priv, i,
+                                         ec->rx_max_coalesced_frames,
+                                         DMA_MBUF_DONE_THRESH);
+
+               reg = bcmgenet_rdma_readl(priv, DMA_RING0_TIMEOUT + i);
+               reg &= ~DMA_TIMEOUT_MASK;
+               reg |= DIV_ROUND_UP(ec->rx_coalesce_usecs * 1000, 8192);
+               bcmgenet_rdma_writel(priv, reg, DMA_RING0_TIMEOUT + i);
+       }
+
+       bcmgenet_rdma_ring_writel(priv, DESC_INDEX,
+                                 ec->rx_max_coalesced_frames,
+                                 DMA_MBUF_DONE_THRESH);
+
+       reg = bcmgenet_rdma_readl(priv, DMA_RING16_TIMEOUT);
+       reg &= ~DMA_TIMEOUT_MASK;
+       reg |= DIV_ROUND_UP(ec->rx_coalesce_usecs * 1000, 8192);
+       bcmgenet_rdma_writel(priv, reg, DMA_RING16_TIMEOUT);
+
+       return 0;
+}
+
 /* standard ethtool support functions. */
 enum bcmgenet_stat_type {
        BCMGENET_STAT_NETDEV = -1,
@@ -646,7 +793,6 @@ static void bcmgenet_get_drvinfo(struct net_device *dev,
 {
        strlcpy(info->driver, "bcmgenet", sizeof(info->driver));
        strlcpy(info->version, "v2.0", sizeof(info->version));
-       info->n_stats = BCMGENET_STATS_LEN;
 }
 
 static int bcmgenet_get_sset_count(struct net_device *dev, int string_set)
@@ -844,6 +990,8 @@ static struct ethtool_ops bcmgenet_ethtool_ops = {
        .get_eee                = bcmgenet_get_eee,
        .set_eee                = bcmgenet_set_eee,
        .nway_reset             = bcmgenet_nway_reset,
+       .get_coalesce           = bcmgenet_get_coalesce,
+       .set_coalesce           = bcmgenet_set_coalesce,
 };
 
 /* Power down the unimac, based on mode. */
@@ -907,8 +1055,10 @@ static void bcmgenet_power_up(struct bcmgenet_priv *priv,
        }
 
        bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
-       if (mode == GENET_POWER_PASSIVE)
+       if (mode == GENET_POWER_PASSIVE) {
                bcmgenet_phy_power_set(priv->dev, true);
+               bcmgenet_mii_reset(priv->dev);
+       }
 }
 
 /* ioctl handle special commands that are not present in ethtool. */
index 7299d10754226680e71ace26cbe4f996c8867127..9673675573099bb05c9c91577cf49e88d9e39067 100644 (file)
@@ -304,13 +304,12 @@ struct bcmgenet_mib_counters {
 #define UMAC_IRQ_RXDMA_MBDONE          (1 << 13)
 #define UMAC_IRQ_RXDMA_PDONE           (1 << 14)
 #define UMAC_IRQ_RXDMA_BDONE           (1 << 15)
-#define UMAC_IRQ_RXDMA_DONE            (UMAC_IRQ_RXDMA_PDONE | \
-                                        UMAC_IRQ_RXDMA_BDONE)
+#define UMAC_IRQ_RXDMA_DONE            UMAC_IRQ_RXDMA_MBDONE
 #define UMAC_IRQ_TXDMA_MBDONE          (1 << 16)
 #define UMAC_IRQ_TXDMA_PDONE           (1 << 17)
 #define UMAC_IRQ_TXDMA_BDONE           (1 << 18)
-#define UMAC_IRQ_TXDMA_DONE            (UMAC_IRQ_TXDMA_PDONE | \
-                                        UMAC_IRQ_TXDMA_BDONE)
+#define UMAC_IRQ_TXDMA_DONE            UMAC_IRQ_TXDMA_MBDONE
+
 /* Only valid for GENETv3+ */
 #define UMAC_IRQ_MDIO_DONE             (1 << 23)
 #define UMAC_IRQ_MDIO_ERROR            (1 << 24)
@@ -386,7 +385,7 @@ struct bcmgenet_mib_counters {
 #define DMA_RING_BUFFER_SIZE_MASK      0xFFFF
 
 /* DMA interrupt threshold register */
-#define DMA_INTR_THRESHOLD_MASK                0x00FF
+#define DMA_INTR_THRESHOLD_MASK                0x01FF
 
 /* DMA XON/XOFF register */
 #define DMA_XON_THREHOLD_MASK          0xFFFF
@@ -674,6 +673,7 @@ int bcmgenet_mii_init(struct net_device *dev);
 int bcmgenet_mii_config(struct net_device *dev);
 int bcmgenet_mii_probe(struct net_device *dev);
 void bcmgenet_mii_exit(struct net_device *dev);
+void bcmgenet_mii_reset(struct net_device *dev);
 void bcmgenet_phy_power_set(struct net_device *dev, bool enable);
 void bcmgenet_mii_setup(struct net_device *dev);
 
index c8affad76f368b14af1a0a6a803ef682e4d3b836..8bdfe53754ba8a4830c46afa9031f5169b30246a 100644 (file)
@@ -163,6 +163,7 @@ void bcmgenet_mii_setup(struct net_device *dev)
        phy_print_status(phydev);
 }
 
+
 static int bcmgenet_fixed_phy_link_update(struct net_device *dev,
                                          struct fixed_phy_status *status)
 {
@@ -172,6 +173,22 @@ static int bcmgenet_fixed_phy_link_update(struct net_device *dev,
        return 0;
 }
 
+/* Perform a voluntary PHY software reset, since the EPHY is very finicky about
+ * not doing it and will start corrupting packets
+ */
+void bcmgenet_mii_reset(struct net_device *dev)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+
+       if (GENET_IS_V4(priv))
+               return;
+
+       if (priv->phydev) {
+               phy_init_hw(priv->phydev);
+               phy_start_aneg(priv->phydev);
+       }
+}
+
 void bcmgenet_phy_power_set(struct net_device *dev, bool enable)
 {
        struct bcmgenet_priv *priv = netdev_priv(dev);
@@ -214,6 +231,7 @@ static void bcmgenet_internal_phy_setup(struct net_device *dev)
        reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
        reg |= EXT_PWR_DN_EN_LD;
        bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
+       bcmgenet_mii_reset(dev);
 }
 
 static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
index 9b35d142f47accfbaec039f0d8100fb45d68bbf8..8fb84e69c30ec8ddfdf6be710cbfab0a3cd1aa60 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 config NET_VENDOR_CAVIUM
-       tristate "Cavium ethernet drivers"
+       bool "Cavium ethernet drivers"
        depends on PCI
        default y
        ---help---
index 29f33083178431ac3735094683663d1e4ab2b836..245c063ed4db0f5dcf70fce926cdd6d1108b01db 100644 (file)
@@ -153,7 +153,6 @@ lio_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
        strncpy(drvinfo->fw_version, oct->fw_info.liquidio_firmware_version,
                ETHTOOL_FWVERS_LEN);
        strncpy(drvinfo->bus_info, pci_name(oct->pci_dev), 32);
-       drvinfo->regdump_len = OCT_ETHTOOL_REGDUMP_LEN;
 }
 
 static void
index b3a5947a2cc03e1675e5895fef39e35b10ea04d0..c561fdcb79a730aeeb890c5a985b05cb873faa45 100644 (file)
@@ -22,7 +22,6 @@
 
 struct nicpf {
        struct pci_dev          *pdev;
-       u8                      rev_id;
        u8                      node;
        unsigned int            flags;
        u8                      num_vf_en;      /* No of VF enabled */
@@ -44,6 +43,7 @@ struct nicpf {
        u8                      duplex[MAX_LMAC];
        u32                     speed[MAX_LMAC];
        u16                     cpi_base[MAX_NUM_VFS_SUPPORTED];
+       u16                     rssi_base[MAX_NUM_VFS_SUPPORTED];
        u16                     rss_ind_tbl_size;
        bool                    mbx_lock[MAX_NUM_VFS_SUPPORTED];
 
@@ -54,6 +54,11 @@ struct nicpf {
        bool                    irq_allocated[NIC_PF_MSIX_VECTORS];
 };
 
+static inline bool pass1_silicon(struct nicpf *nic)
+{
+       return nic->pdev->revision < 8;
+}
+
 /* Supported devices */
 static const struct pci_device_id nic_id_table[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_NIC_PF) },
@@ -117,7 +122,7 @@ static void nic_send_msg_to_vf(struct nicpf *nic, int vf, union nic_mbx *mbx)
         * when PF writes to MBOX(1), in next revisions when
         * PF writes to MBOX(0)
         */
-       if (nic->rev_id == 0) {
+       if (pass1_silicon(nic)) {
                /* see the comment for nic_reg_write()/nic_reg_read()
                 * functions above
                 */
@@ -305,9 +310,6 @@ static void nic_init_hw(struct nicpf *nic)
 {
        int i;
 
-       /* Reset NIC, in case the driver is repeatedly inserted and removed */
-       nic_reg_write(nic, NIC_PF_SOFT_RESET, 1);
-
        /* Enable NIC HW block */
        nic_reg_write(nic, NIC_PF_CFG, 0x3);
 
@@ -395,8 +397,18 @@ static void nic_config_cpi(struct nicpf *nic, struct cpi_cfg_msg *cfg)
                        padd = cpi % 8; /* 3 bits CS out of 6bits DSCP */
 
                /* Leave RSS_SIZE as '0' to disable RSS */
-               nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi << 3),
-                             (vnic << 24) | (padd << 16) | (rssi_base + rssi));
+               if (pass1_silicon(nic)) {
+                       nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi << 3),
+                                     (vnic << 24) | (padd << 16) |
+                                     (rssi_base + rssi));
+               } else {
+                       /* Set MPI_ALG to '0' to disable MCAM parsing */
+                       nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi << 3),
+                                     (padd << 16));
+                       /* MPI index is same as CPI if MPI_ALG is not enabled */
+                       nic_reg_write(nic, NIC_PF_MPI_0_2047_CFG | (cpi << 3),
+                                     (vnic << 24) | (rssi_base + rssi));
+               }
 
                if ((rssi + 1) >= cfg->rq_cnt)
                        continue;
@@ -409,6 +421,7 @@ static void nic_config_cpi(struct nicpf *nic, struct cpi_cfg_msg *cfg)
                        rssi = ((cpi - cpi_base) & 0x38) >> 3;
        }
        nic->cpi_base[cfg->vf_id] = cpi_base;
+       nic->rssi_base[cfg->vf_id] = rssi_base;
 }
 
 /* Responsds to VF with its RSS indirection table size */
@@ -434,10 +447,9 @@ static void nic_config_rss(struct nicpf *nic, struct rss_cfg_msg *cfg)
 {
        u8  qset, idx = 0;
        u64 cpi_cfg, cpi_base, rssi_base, rssi;
+       u64 idx_addr;
 
-       cpi_base = nic->cpi_base[cfg->vf_id];
-       cpi_cfg = nic_reg_read(nic, NIC_PF_CPI_0_2047_CFG | (cpi_base << 3));
-       rssi_base = (cpi_cfg & 0x0FFF) + cfg->tbl_offset;
+       rssi_base = nic->rssi_base[cfg->vf_id] + cfg->tbl_offset;
 
        rssi = rssi_base;
        qset = cfg->vf_id;
@@ -454,9 +466,15 @@ static void nic_config_rss(struct nicpf *nic, struct rss_cfg_msg *cfg)
                idx++;
        }
 
+       cpi_base = nic->cpi_base[cfg->vf_id];
+       if (pass1_silicon(nic))
+               idx_addr = NIC_PF_CPI_0_2047_CFG;
+       else
+               idx_addr = NIC_PF_MPI_0_2047_CFG;
+       cpi_cfg = nic_reg_read(nic, idx_addr | (cpi_base << 3));
        cpi_cfg &= ~(0xFULL << 20);
        cpi_cfg |= (cfg->hash_bits << 20);
-       nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi_base << 3), cpi_cfg);
+       nic_reg_write(nic, idx_addr | (cpi_base << 3), cpi_cfg);
 }
 
 /* 4 level transmit side scheduler configutation
@@ -1001,8 +1019,6 @@ static int nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_release_regions;
        }
 
-       pci_read_config_byte(pdev, PCI_REVISION_ID, &nic->rev_id);
-
        nic->node = nic_get_node_id(pdev);
 
        nic_set_lmac_vf_mapping(nic);
index 58197bb2f80528bf06de0daaa2c406b96d4fc0c4..dd536be20193119c3465cd772a4bff6d649e3319 100644 (file)
 #define   NIC_PF_ECC3_DBE_INT_W1S              (0x2708)
 #define   NIC_PF_ECC3_DBE_ENA_W1C              (0x2710)
 #define   NIC_PF_ECC3_DBE_ENA_W1S              (0x2718)
+#define   NIC_PF_MCAM_0_191_ENA                        (0x100000)
+#define   NIC_PF_MCAM_0_191_M_0_5_DATA         (0x110000)
+#define   NIC_PF_MCAM_CTRL                     (0x120000)
 #define   NIC_PF_CPI_0_2047_CFG                        (0x200000)
+#define   NIC_PF_MPI_0_2047_CFG                        (0x210000)
 #define   NIC_PF_RSSI_0_4097_RQ                        (0x220000)
 #define   NIC_PF_LMAC_0_7_CFG                  (0x240000)
 #define   NIC_PF_LMAC_0_7_SW_XOFF              (0x242000)
index b63e579aeb12d09bbe7f6e3de5badced5b2d44ba..a9377727c11c3fdb18a09f16ce8366ae4ef48057 100644 (file)
@@ -29,7 +29,7 @@
 static const struct pci_device_id nicvf_id_table[] = {
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
                         PCI_DEVICE_ID_THUNDER_NIC_VF,
-                        PCI_VENDOR_ID_CAVIUM, 0xA11E) },
+                        PCI_VENDOR_ID_CAVIUM, 0xA134) },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
                         PCI_DEVICE_ID_THUNDER_PASS1_NIC_VF,
                         PCI_VENDOR_ID_CAVIUM, 0xA11E) },
index 574c49278900943e5584fa9658f7eb6adb2d7452..180aa9fabf4820df042f18cba8c39d5ce1668e9d 100644 (file)
@@ -977,8 +977,10 @@ static int bgx_init_of_phy(struct bgx *bgx)
                SET_NETDEV_DEV(&bgx->lmac[lmac].netdev, &bgx->pdev->dev);
                bgx->lmac[lmac].lmacid = lmac;
                lmac++;
-               if (lmac == MAX_LMAC_PER_BGX)
+               if (lmac == MAX_LMAC_PER_BGX) {
+                       of_node_put(np_child);
                        break;
+               }
        }
        return 0;
 }
index fa0c7b54ec7ad8eb91f79203acb6df1a47e8184f..634e50c8c5ef766ae144a2c51344294751902dea 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/timer.h>
 #include <linux/vmalloc.h>
 #include <linux/etherdevice.h>
+#include <linux/net_tstamp.h>
 #include <asm/io.h>
 #include "cxgb4_uld.h"
 
@@ -478,6 +479,8 @@ struct port_info {
 #ifdef CONFIG_CHELSIO_T4_FCOE
        struct cxgb_fcoe fcoe;
 #endif /* CONFIG_CHELSIO_T4_FCOE */
+       bool rxtstamp;  /* Enable TS */
+       struct hwtstamp_config tstamp_config;
 };
 
 struct dentry;
@@ -517,6 +520,7 @@ struct sge_fl {                     /* SGE free-buffer queue state */
 
 /* A packet gather list */
 struct pkt_gl {
+       u64 sgetstamp;              /* SGE Time Stamp for Ingress Packet */
        struct page_frag frags[MAX_SKB_FRAGS];
        void *va;                         /* virtual address of first byte */
        unsigned int nfrags;              /* # of fragments */
index 0a87a3247464fdd1939d8bcb5867a9f0e735cf02..4269944c5db53b19840f1752793c70bbf6b19c70 100644 (file)
@@ -940,6 +940,7 @@ static const char * const devlog_level_strings[] = {
 
 static const char * const devlog_facility_strings[] = {
        [FW_DEVLOG_FACILITY_CORE]       = "CORE",
+       [FW_DEVLOG_FACILITY_CF]         = "CF",
        [FW_DEVLOG_FACILITY_SCHED]      = "SCHED",
        [FW_DEVLOG_FACILITY_TIMER]      = "TIMER",
        [FW_DEVLOG_FACILITY_RES]        = "RES",
@@ -1128,18 +1129,26 @@ static const struct file_operations devlog_fops = {
 static int mbox_show(struct seq_file *seq, void *v)
 {
        static const char * const owner[] = { "none", "FW", "driver",
-                                             "unknown" };
+                                             "unknown", "<unread>" };
 
        int i;
        unsigned int mbox = (uintptr_t)seq->private & 7;
        struct adapter *adap = seq->private - mbox;
        void __iomem *addr = adap->regs + PF_REG(mbox, CIM_PF_MAILBOX_DATA_A);
-       unsigned int ctrl_reg = (is_t4(adap->params.chip)
-                                ? CIM_PF_MAILBOX_CTRL_A
-                                : CIM_PF_MAILBOX_CTRL_SHADOW_COPY_A);
-       void __iomem *ctrl = adap->regs + PF_REG(mbox, ctrl_reg);
 
-       i = MBOWNER_G(readl(ctrl));
+       /* For T4 we don't have a shadow copy of the Mailbox Control register.
+        * And since reading that real register causes a side effect of
+        * granting ownership, we're best of simply not reading it at all.
+        */
+       if (is_t4(adap->params.chip)) {
+               i = 4; /* index of "<unread>" */
+       } else {
+               unsigned int ctrl_reg = CIM_PF_MAILBOX_CTRL_SHADOW_COPY_A;
+               void __iomem *ctrl = adap->regs + PF_REG(mbox, ctrl_reg);
+
+               i = MBOWNER_G(readl(ctrl));
+       }
+
        seq_printf(seq, "mailbox owned by %s\n\n", owner[i]);
 
        for (i = 0; i < MBOX_LEN; i += 8)
index 5eedb98ff581a8c67dd8284a1cc2ad9fd3fbdd6e..a077f9476daf3583ff7ca5a60c406c3b4d6e43c7 100644 (file)
@@ -35,79 +35,79 @@ static void set_msglevel(struct net_device *dev, u32 val)
 }
 
 static const char stats_strings[][ETH_GSTRING_LEN] = {
-       "TxOctetsOK         ",
-       "TxFramesOK         ",
-       "TxBroadcastFrames  ",
-       "TxMulticastFrames  ",
-       "TxUnicastFrames    ",
-       "TxErrorFrames      ",
-
-       "TxFrames64         ",
-       "TxFrames65To127    ",
-       "TxFrames128To255   ",
-       "TxFrames256To511   ",
-       "TxFrames512To1023  ",
-       "TxFrames1024To1518 ",
-       "TxFrames1519ToMax  ",
-
-       "TxFramesDropped    ",
-       "TxPauseFrames      ",
-       "TxPPP0Frames       ",
-       "TxPPP1Frames       ",
-       "TxPPP2Frames       ",
-       "TxPPP3Frames       ",
-       "TxPPP4Frames       ",
-       "TxPPP5Frames       ",
-       "TxPPP6Frames       ",
-       "TxPPP7Frames       ",
-
-       "RxOctetsOK         ",
-       "RxFramesOK         ",
-       "RxBroadcastFrames  ",
-       "RxMulticastFrames  ",
-       "RxUnicastFrames    ",
-
-       "RxFramesTooLong    ",
-       "RxJabberErrors     ",
-       "RxFCSErrors        ",
-       "RxLengthErrors     ",
-       "RxSymbolErrors     ",
-       "RxRuntFrames       ",
-
-       "RxFrames64         ",
-       "RxFrames65To127    ",
-       "RxFrames128To255   ",
-       "RxFrames256To511   ",
-       "RxFrames512To1023  ",
-       "RxFrames1024To1518 ",
-       "RxFrames1519ToMax  ",
-
-       "RxPauseFrames      ",
-       "RxPPP0Frames       ",
-       "RxPPP1Frames       ",
-       "RxPPP2Frames       ",
-       "RxPPP3Frames       ",
-       "RxPPP4Frames       ",
-       "RxPPP5Frames       ",
-       "RxPPP6Frames       ",
-       "RxPPP7Frames       ",
-
-       "RxBG0FramesDropped ",
-       "RxBG1FramesDropped ",
-       "RxBG2FramesDropped ",
-       "RxBG3FramesDropped ",
-       "RxBG0FramesTrunc   ",
-       "RxBG1FramesTrunc   ",
-       "RxBG2FramesTrunc   ",
-       "RxBG3FramesTrunc   ",
-
-       "TSO                ",
-       "TxCsumOffload      ",
-       "RxCsumGood         ",
-       "VLANextractions    ",
-       "VLANinsertions     ",
-       "GROpackets         ",
-       "GROmerged          ",
+       "tx_octets_ok           ",
+       "tx_frames_ok           ",
+       "tx_broadcast_frames    ",
+       "tx_multicast_frames    ",
+       "tx_unicast_frames      ",
+       "tx_error_frames        ",
+
+       "tx_frames_64           ",
+       "tx_frames_65_to_127    ",
+       "tx_frames_128_to_255   ",
+       "tx_frames_256_to_511   ",
+       "tx_frames_512_to_1023  ",
+       "tx_frames_1024_to_1518 ",
+       "tx_frames_1519_to_max  ",
+
+       "tx_frames_dropped      ",
+       "tx_pause_frames        ",
+       "tx_ppp0_frames         ",
+       "tx_ppp1_frames         ",
+       "tx_ppp2_frames         ",
+       "tx_ppp3_frames         ",
+       "tx_ppp4_frames         ",
+       "tx_ppp5_frames         ",
+       "tx_ppp6_frames         ",
+       "tx_ppp7_frames         ",
+
+       "rx_octets_ok           ",
+       "rx_frames_ok           ",
+       "rx_broadcast_frames    ",
+       "rx_multicast_frames    ",
+       "rx_unicast_frames      ",
+
+       "rx_frames_too_long     ",
+       "rx_jabber_errors       ",
+       "rx_fcs_errors          ",
+       "rx_length_errors       ",
+       "rx_symbol_errors       ",
+       "rx_runt_frames         ",
+
+       "rx_frames_64           ",
+       "rx_frames_65_to_127    ",
+       "rx_frames_128_to_255   ",
+       "rx_frames_256_to_511   ",
+       "rx_frames_512_to_1023  ",
+       "rx_frames_1024_to_1518 ",
+       "rx_frames_1519_to_max  ",
+
+       "rx_pause_frames        ",
+       "rx_ppp0_frames         ",
+       "rx_ppp1_frames         ",
+       "rx_ppp2_frames         ",
+       "rx_ppp3_frames         ",
+       "rx_ppp4_frames         ",
+       "rx_ppp5_frames         ",
+       "rx_ppp6_frames         ",
+       "rx_ppp7_frames         ",
+
+       "rx_bg0_frames_dropped  ",
+       "rx_bg1_frames_dropped  ",
+       "rx_bg2_frames_dropped  ",
+       "rx_bg3_frames_dropped  ",
+       "rx_bg0_frames_trunc    ",
+       "rx_bg1_frames_trunc    ",
+       "rx_bg2_frames_trunc    ",
+       "rx_bg3_frames_trunc    ",
+
+       "tso                    ",
+       "tx_csum_offload        ",
+       "rx_csum_good           ",
+       "vlan_extractions       ",
+       "vlan_insertions        ",
+       "gro_packets            ",
+       "gro_merged             ",
 };
 
 static char adapter_stats_strings[][ETH_GSTRING_LEN] = {
@@ -211,8 +211,11 @@ static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
                sizeof(info->version));
        strlcpy(info->bus_info, pci_name(adapter->pdev),
                sizeof(info->bus_info));
+       info->regdump_len = get_regs_len(dev);
 
-       if (adapter->params.fw_vers)
+       if (!adapter->params.fw_vers)
+               strcpy(info->fw_version, "N/A");
+       else
                snprintf(info->fw_version, sizeof(info->fw_version),
                         "%u.%u.%u.%u, TP %u.%u.%u.%u",
                         FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers),
@@ -612,6 +615,8 @@ static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
        struct port_info *p = netdev_priv(dev);
        struct link_config *lc = &p->link_cfg;
        u32 speed = ethtool_cmd_speed(cmd);
+       struct link_config old_lc;
+       int ret;
 
        if (cmd->duplex != DUPLEX_FULL)     /* only full-duplex supported */
                return -EINVAL;
@@ -626,13 +631,11 @@ static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                return -EINVAL;
        }
 
+       old_lc = *lc;
        if (cmd->autoneg == AUTONEG_DISABLE) {
                cap = speed_to_caps(speed);
 
-               if (!(lc->supported & cap) ||
-                   (speed == 1000) ||
-                   (speed == 10000) ||
-                   (speed == 40000))
+               if (!(lc->supported & cap))
                        return -EINVAL;
                lc->requested_speed = cap;
                lc->advertising = 0;
@@ -645,10 +648,14 @@ static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
        }
        lc->autoneg = cmd->autoneg;
 
-       if (netif_running(dev))
-               return t4_link_l1cfg(p->adapter, p->adapter->pf, p->tx_chan,
-                                    lc);
-       return 0;
+       /* If the firmware rejects the Link Configuration request, back out
+        * the changes and report the error.
+        */
+       ret = t4_link_l1cfg(p->adapter, p->adapter->mbox, p->tx_chan, lc);
+       if (ret)
+               *lc = old_lc;
+
+       return ret;
 }
 
 static void get_pauseparam(struct net_device *dev,
@@ -847,7 +854,7 @@ static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e,
 {
        int i, err = 0;
        struct adapter *adapter = netdev2adap(dev);
-       u8 *buf = kmalloc(EEPROMSIZE, GFP_KERNEL);
+       u8 *buf = t4_alloc_mem(EEPROMSIZE);
 
        if (!buf)
                return -ENOMEM;
@@ -858,7 +865,7 @@ static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e,
 
        if (!err)
                memcpy(data, buf + e->offset, e->len);
-       kfree(buf);
+       t4_free_mem(buf);
        return err;
 }
 
@@ -887,7 +894,7 @@ static int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
        if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) {
                /* RMW possibly needed for first or last words.
                 */
-               buf = kmalloc(aligned_len, GFP_KERNEL);
+               buf = t4_alloc_mem(aligned_len);
                if (!buf)
                        return -ENOMEM;
                err = eeprom_rd_phys(adapter, aligned_offset, (u32 *)buf);
@@ -915,7 +922,7 @@ static int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
                err = t4_seeprom_wp(adapter, true);
 out:
        if (buf != data)
-               kfree(buf);
+               t4_free_mem(buf);
        return err;
 }
 
@@ -961,6 +968,20 @@ static int set_flash(struct net_device *netdev, struct ethtool_flash *ef)
        return ret;
 }
 
+static int get_ts_info(struct net_device *dev, struct ethtool_ts_info *ts_info)
+{
+       ts_info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
+                                  SOF_TIMESTAMPING_RX_SOFTWARE |
+                                  SOF_TIMESTAMPING_SOFTWARE;
+
+       ts_info->so_timestamping |= SOF_TIMESTAMPING_RX_HARDWARE |
+                                   SOF_TIMESTAMPING_RAW_HARDWARE;
+
+       ts_info->phc_index = -1;
+
+       return 0;
+}
+
 static u32 get_rss_table_size(struct net_device *dev)
 {
        const struct port_info *pi = netdev_priv(dev);
@@ -997,11 +1018,15 @@ static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key,
        if (!p)
                return 0;
 
-       for (i = 0; i < pi->rss_size; i++)
-               pi->rss[i] = p[i];
-       if (pi->adapter->flags & FULL_INIT_DONE)
+       /* Interface must be brought up atleast once */
+       if (pi->adapter->flags & FULL_INIT_DONE) {
+               for (i = 0; i < pi->rss_size; i++)
+                       pi->rss[i] = p[i];
+
                return cxgb4_write_rss(pi, pi->rss);
-       return 0;
+       }
+
+       return -EPERM;
 }
 
 static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
@@ -1095,6 +1120,7 @@ static const struct ethtool_ops cxgb_ethtool_ops = {
        .get_rxfh          = get_rss_table,
        .set_rxfh          = set_rss_table,
        .flash_device      = set_flash,
+       .get_ts_info       = get_ts_info
 };
 
 void cxgb4_set_ethtool_ops(struct net_device *netdev)
index f5dcde27e40281d3b7dc8cf05468eec704b98f40..2cf81857a2971b280005715992c0842e4f21385f 100644 (file)
@@ -83,7 +83,7 @@ char cxgb4_driver_name[] = KBUILD_MODNAME;
 #endif
 #define DRV_VERSION "2.0.0-ko"
 const char cxgb4_driver_version[] = DRV_VERSION;
-#define DRV_DESC "Chelsio T4/T5 Network Driver"
+#define DRV_DESC "Chelsio T4/T5/T6 Network Driver"
 
 /* Host shadow copy of ingress filter entry.  This is in host native format
  * and doesn't match the ordering or bit order, etc. of the hardware of the
@@ -151,6 +151,7 @@ MODULE_VERSION(DRV_VERSION);
 MODULE_DEVICE_TABLE(pci, cxgb4_pci_tbl);
 MODULE_FIRMWARE(FW4_FNAME);
 MODULE_FIRMWARE(FW5_FNAME);
+MODULE_FIRMWARE(FW6_FNAME);
 
 /*
  * Normally we're willing to become the firmware's Master PF but will be happy
@@ -275,7 +276,7 @@ static void link_report(struct net_device *dev)
        else {
                static const char *fc[] = { "no", "Rx", "Tx", "Tx/Rx" };
 
-               const char *s = "10Mbps";
+               const char *s;
                const struct port_info *p = netdev_priv(dev);
 
                switch (p->link_cfg.speed) {
@@ -291,6 +292,10 @@ static void link_report(struct net_device *dev)
                case 40000:
                        s = "40Gbps";
                        break;
+               default:
+                       pr_info("%s: unsupported speed: %d\n",
+                               dev->name, p->link_cfg.speed);
+                       return;
                }
 
                netdev_info(dev, "link up, %s, full-duplex, %s PAUSE\n", s,
@@ -2959,6 +2964,30 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
                        ret = t4_mdio_wr(pi->adapter, mbox, prtad, devad,
                                         data->reg_num, data->val_in);
                break;
+       case SIOCGHWTSTAMP:
+               return copy_to_user(req->ifr_data, &pi->tstamp_config,
+                                   sizeof(pi->tstamp_config)) ?
+                       -EFAULT : 0;
+       case SIOCSHWTSTAMP:
+               if (copy_from_user(&pi->tstamp_config, req->ifr_data,
+                                  sizeof(pi->tstamp_config)))
+                       return -EFAULT;
+
+               switch (pi->tstamp_config.rx_filter) {
+               case HWTSTAMP_FILTER_NONE:
+                       pi->rxtstamp = false;
+                       break;
+               case HWTSTAMP_FILTER_ALL:
+                       pi->rxtstamp = true;
+                       break;
+               default:
+                       pi->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
+                       return -ERANGE;
+               }
+
+               return copy_to_user(req->ifr_data, &pi->tstamp_config,
+                                   sizeof(pi->tstamp_config)) ?
+                       -EFAULT : 0;
        default:
                return -EOPNOTSUPP;
        }
@@ -3670,7 +3699,7 @@ static int adap_init0(struct adapter *adap)
        t4_get_tp_version(adap, &adap->params.tp_vers);
        ret = t4_check_fw_version(adap);
        /* If firmware is too old (not supported by driver) force an update. */
-       if (ret == -EFAULT)
+       if (ret)
                state = DEV_STATE_UNINIT;
        if ((adap->flags & MASTER_PF) && state != DEV_STATE_INIT) {
                struct fw_info *fw_info;
@@ -4457,6 +4486,10 @@ static int enable_msix(struct adapter *adap)
        }
        for (i = 0; i < allocated; ++i)
                adap->msix_info[i].vec = entries[i].vector;
+       dev_info(adap->pdev_dev, "%d MSI-X vectors allocated, "
+                "nic %d iscsi %d rdma cpl %d rdma ciq %d\n",
+                allocated, s->max_ethqsets, s->ofldqsets, s->rdmaqs,
+                s->rdmaciqs);
 
        kfree(entries);
        return 0;
index 9162746d7729559fff5c33315da186f9fe0244d5..b7b93e7a643d80e806a299c08301f77b920a1aea 100644 (file)
@@ -1820,11 +1820,34 @@ static noinline int handle_trace_pkt(struct adapter *adap,
        return 0;
 }
 
+/**
+ * cxgb4_sgetim_to_hwtstamp - convert sge time stamp to hw time stamp
+ * @adap: the adapter
+ * @hwtstamps: time stamp structure to update
+ * @sgetstamp: 60bit iqe timestamp
+ *
+ * Every ingress queue entry has the 60-bit timestamp, convert that timestamp
+ * which is in Core Clock ticks into ktime_t and assign it
+ **/
+static void cxgb4_sgetim_to_hwtstamp(struct adapter *adap,
+                                    struct skb_shared_hwtstamps *hwtstamps,
+                                    u64 sgetstamp)
+{
+       u64 ns;
+       u64 tmp = (sgetstamp * 1000 * 1000 + adap->params.vpd.cclk / 2);
+
+       ns = div_u64(tmp, adap->params.vpd.cclk);
+
+       memset(hwtstamps, 0, sizeof(*hwtstamps));
+       hwtstamps->hwtstamp = ns_to_ktime(ns);
+}
+
 static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl,
                   const struct cpl_rx_pkt *pkt)
 {
        struct adapter *adapter = rxq->rspq.adap;
        struct sge *s = &adapter->sge;
+       struct port_info *pi;
        int ret;
        struct sk_buff *skb;
 
@@ -1842,6 +1865,10 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl,
        skb->ip_summed = CHECKSUM_UNNECESSARY;
        skb_record_rx_queue(skb, rxq->rspq.idx);
        skb_mark_napi_id(skb, &rxq->rspq.napi);
+       pi = netdev_priv(skb->dev);
+       if (pi->rxtstamp)
+               cxgb4_sgetim_to_hwtstamp(adapter, skb_hwtstamps(skb),
+                                        gl->sgetstamp);
        if (rxq->rspq.netdev->features & NETIF_F_RXHASH)
                skb_set_hash(skb, (__force u32)pkt->rsshdr.hash_val,
                             PKT_HASH_TYPE_L3);
@@ -1877,9 +1904,7 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
        struct sge *s = &q->adap->sge;
        int cpl_trace_pkt = is_t4(q->adap->params.chip) ?
                            CPL_TRACE_PKT : CPL_TRACE_PKT_T5;
-#ifdef CONFIG_CHELSIO_T4_FCOE
        struct port_info *pi;
-#endif
 
        if (unlikely(*(u8 *)rsp == cpl_trace_pkt))
                return handle_trace_pkt(q->adap, si);
@@ -1910,6 +1935,10 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
 
        rxq->stats.pkts++;
 
+       pi = netdev_priv(skb->dev);
+       if (pi->rxtstamp)
+               cxgb4_sgetim_to_hwtstamp(q->adap, skb_hwtstamps(skb),
+                                        si->sgetstamp);
        if (csum_ok && (pkt->l2info & htonl(RXF_UDP_F | RXF_TCP_F))) {
                if (!pkt->ip_frag) {
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -1926,7 +1955,6 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
 #define CPL_RX_PKT_FLAGS (RXF_PSH_F | RXF_SYN_F | RXF_UDP_F | \
                          RXF_TCP_F | RXF_IP_F | RXF_IP6_F | RXF_LRO_F)
 
-               pi = netdev_priv(skb->dev);
                if (!(pkt->l2info & cpu_to_be32(CPL_RX_PKT_FLAGS))) {
                        if ((pkt->l2info & cpu_to_be32(RXF_FCOE_F)) &&
                            (pi->fcoe.flags & CXGB_FCOE_ENABLED)) {
@@ -2067,6 +2095,8 @@ static int process_responses(struct sge_rspq *q, int budget)
                                unmap_rx_buf(q->adap, &rxq->fl);
                        }
 
+                       si.sgetstamp = SGE_TIMESTAMP_G(
+                                       be64_to_cpu(rc->last_flit));
                        /*
                         * Last buffer remains mapped so explicitly make it
                         * coherent for CPU access.
index 44806253c1780b3d2ffb5ece8c488eb74e06dac8..cf61a5869c6e06627ada3947397a3c8889c2d662 100644 (file)
@@ -699,50 +699,107 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
 {
        static const unsigned int t4_reg_ranges[] = {
                0x1008, 0x1108,
-               0x1180, 0x11b4,
+               0x1180, 0x1184,
+               0x1190, 0x1194,
+               0x11a0, 0x11a4,
+               0x11b0, 0x11b4,
                0x11fc, 0x123c,
                0x1300, 0x173c,
                0x1800, 0x18fc,
-               0x3000, 0x305c,
-               0x3068, 0x30d8,
-               0x30e0, 0x5924,
-               0x5960, 0x59d4,
-               0x5a00, 0x5af8,
+               0x3000, 0x30d8,
+               0x30e0, 0x30e4,
+               0x30ec, 0x5910,
+               0x5920, 0x5924,
+               0x5960, 0x5960,
+               0x5968, 0x5968,
+               0x5970, 0x5970,
+               0x5978, 0x5978,
+               0x5980, 0x5980,
+               0x5988, 0x5988,
+               0x5990, 0x5990,
+               0x5998, 0x5998,
+               0x59a0, 0x59d4,
+               0x5a00, 0x5ae0,
+               0x5ae8, 0x5ae8,
+               0x5af0, 0x5af0,
+               0x5af8, 0x5af8,
                0x6000, 0x6098,
                0x6100, 0x6150,
                0x6200, 0x6208,
                0x6240, 0x6248,
-               0x6280, 0x6338,
+               0x6280, 0x62b0,
+               0x62c0, 0x6338,
                0x6370, 0x638c,
                0x6400, 0x643c,
                0x6500, 0x6524,
-               0x6a00, 0x6a38,
-               0x6a60, 0x6a78,
-               0x6b00, 0x6b84,
-               0x6bf0, 0x6c84,
-               0x6cf0, 0x6d84,
-               0x6df0, 0x6e84,
-               0x6ef0, 0x6f84,
-               0x6ff0, 0x7084,
-               0x70f0, 0x7184,
-               0x71f0, 0x7284,
-               0x72f0, 0x7384,
-               0x73f0, 0x7450,
+               0x6a00, 0x6a04,
+               0x6a14, 0x6a38,
+               0x6a60, 0x6a70,
+               0x6a78, 0x6a78,
+               0x6b00, 0x6b0c,
+               0x6b1c, 0x6b84,
+               0x6bf0, 0x6bf8,
+               0x6c00, 0x6c0c,
+               0x6c1c, 0x6c84,
+               0x6cf0, 0x6cf8,
+               0x6d00, 0x6d0c,
+               0x6d1c, 0x6d84,
+               0x6df0, 0x6df8,
+               0x6e00, 0x6e0c,
+               0x6e1c, 0x6e84,
+               0x6ef0, 0x6ef8,
+               0x6f00, 0x6f0c,
+               0x6f1c, 0x6f84,
+               0x6ff0, 0x6ff8,
+               0x7000, 0x700c,
+               0x701c, 0x7084,
+               0x70f0, 0x70f8,
+               0x7100, 0x710c,
+               0x711c, 0x7184,
+               0x71f0, 0x71f8,
+               0x7200, 0x720c,
+               0x721c, 0x7284,
+               0x72f0, 0x72f8,
+               0x7300, 0x730c,
+               0x731c, 0x7384,
+               0x73f0, 0x73f8,
+               0x7400, 0x7450,
                0x7500, 0x7530,
-               0x7600, 0x761c,
+               0x7600, 0x760c,
+               0x7614, 0x761c,
                0x7680, 0x76cc,
                0x7700, 0x7798,
                0x77c0, 0x77fc,
                0x7900, 0x79fc,
-               0x7b00, 0x7c38,
-               0x7d00, 0x7efc,
-               0x8dc0, 0x8e1c,
+               0x7b00, 0x7b58,
+               0x7b60, 0x7b84,
+               0x7b8c, 0x7c38,
+               0x7d00, 0x7d38,
+               0x7d40, 0x7d80,
+               0x7d8c, 0x7ddc,
+               0x7de4, 0x7e04,
+               0x7e10, 0x7e1c,
+               0x7e24, 0x7e38,
+               0x7e40, 0x7e44,
+               0x7e4c, 0x7e78,
+               0x7e80, 0x7ea4,
+               0x7eac, 0x7edc,
+               0x7ee8, 0x7efc,
+               0x8dc0, 0x8e04,
+               0x8e10, 0x8e1c,
                0x8e30, 0x8e78,
-               0x8ea0, 0x8f6c,
-               0x8fc0, 0x9074,
+               0x8ea0, 0x8eb8,
+               0x8ec0, 0x8f6c,
+               0x8fc0, 0x9008,
+               0x9010, 0x9058,
+               0x9060, 0x9060,
+               0x9068, 0x9074,
                0x90fc, 0x90fc,
-               0x9400, 0x9458,
-               0x9600, 0x96bc,
+               0x9400, 0x9408,
+               0x9410, 0x9458,
+               0x9600, 0x9600,
+               0x9608, 0x9638,
+               0x9640, 0x96bc,
                0x9800, 0x9808,
                0x9820, 0x983c,
                0x9850, 0x9864,
@@ -754,23 +811,42 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x9e80, 0x9eec,
                0x9f00, 0x9f6c,
                0x9f80, 0x9fec,
-               0xd004, 0xd03c,
+               0xd004, 0xd004,
+               0xd010, 0xd03c,
                0xdfc0, 0xdfe0,
                0xe000, 0xea7c,
-               0xf000, 0x11110,
-               0x11118, 0x11190,
+               0xf000, 0x11190,
                0x19040, 0x1906c,
                0x19078, 0x19080,
-               0x1908c, 0x19124,
-               0x19150, 0x191b0,
+               0x1908c, 0x190e4,
+               0x190f0, 0x190f8,
+               0x19100, 0x19110,
+               0x19120, 0x19124,
+               0x19150, 0x19194,
+               0x1919c, 0x191b0,
                0x191d0, 0x191e8,
                0x19238, 0x1924c,
-               0x193f8, 0x19474,
-               0x19490, 0x194f8,
-               0x19800, 0x19f4c,
-               0x1a000, 0x1a06c,
-               0x1a0b0, 0x1a120,
-               0x1a128, 0x1a138,
+               0x193f8, 0x1943c,
+               0x1944c, 0x19474,
+               0x19490, 0x194e0,
+               0x194f0, 0x194f8,
+               0x19800, 0x19c08,
+               0x19c10, 0x19c90,
+               0x19ca0, 0x19ce4,
+               0x19cf0, 0x19d40,
+               0x19d50, 0x19d94,
+               0x19da0, 0x19de8,
+               0x19df0, 0x19e40,
+               0x19e50, 0x19e90,
+               0x19ea0, 0x19f4c,
+               0x1a000, 0x1a004,
+               0x1a010, 0x1a06c,
+               0x1a0b0, 0x1a0e4,
+               0x1a0ec, 0x1a0f4,
+               0x1a100, 0x1a108,
+               0x1a114, 0x1a120,
+               0x1a128, 0x1a130,
+               0x1a138, 0x1a138,
                0x1a190, 0x1a1c4,
                0x1a1fc, 0x1a1fc,
                0x1e040, 0x1e04c,
@@ -823,9 +899,12 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x1ffc0, 0x1ffc8,
                0x20000, 0x2002c,
                0x20100, 0x2013c,
-               0x20190, 0x201c8,
+               0x20190, 0x201a0,
+               0x201a8, 0x201b8,
+               0x201c4, 0x201c8,
                0x20200, 0x20318,
-               0x20400, 0x20528,
+               0x20400, 0x204b4,
+               0x204c0, 0x20528,
                0x20540, 0x20614,
                0x21000, 0x21040,
                0x2104c, 0x21060,
@@ -834,22 +913,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x21270, 0x21284,
                0x212fc, 0x21388,
                0x21400, 0x21404,
-               0x21500, 0x21518,
-               0x2152c, 0x2153c,
+               0x21500, 0x21500,
+               0x21510, 0x21518,
+               0x2152c, 0x21530,
+               0x2153c, 0x2153c,
                0x21550, 0x21554,
                0x21600, 0x21600,
-               0x21608, 0x21628,
-               0x21630, 0x2163c,
+               0x21608, 0x2161c,
+               0x21624, 0x21628,
+               0x21630, 0x21634,
+               0x2163c, 0x2163c,
                0x21700, 0x2171c,
                0x21780, 0x2178c,
-               0x21800, 0x21c38,
-               0x21c80, 0x21d7c,
+               0x21800, 0x21818,
+               0x21820, 0x21828,
+               0x21830, 0x21848,
+               0x21850, 0x21854,
+               0x21860, 0x21868,
+               0x21870, 0x21870,
+               0x21878, 0x21898,
+               0x218a0, 0x218a8,
+               0x218b0, 0x218c8,
+               0x218d0, 0x218d4,
+               0x218e0, 0x218e8,
+               0x218f0, 0x218f0,
+               0x218f8, 0x21a18,
+               0x21a20, 0x21a28,
+               0x21a30, 0x21a48,
+               0x21a50, 0x21a54,
+               0x21a60, 0x21a68,
+               0x21a70, 0x21a70,
+               0x21a78, 0x21a98,
+               0x21aa0, 0x21aa8,
+               0x21ab0, 0x21ac8,
+               0x21ad0, 0x21ad4,
+               0x21ae0, 0x21ae8,
+               0x21af0, 0x21af0,
+               0x21af8, 0x21c18,
+               0x21c20, 0x21c20,
+               0x21c28, 0x21c30,
+               0x21c38, 0x21c38,
+               0x21c80, 0x21c98,
+               0x21ca0, 0x21ca8,
+               0x21cb0, 0x21cc8,
+               0x21cd0, 0x21cd4,
+               0x21ce0, 0x21ce8,
+               0x21cf0, 0x21cf0,
+               0x21cf8, 0x21d7c,
                0x21e00, 0x21e04,
                0x22000, 0x2202c,
                0x22100, 0x2213c,
-               0x22190, 0x221c8,
+               0x22190, 0x221a0,
+               0x221a8, 0x221b8,
+               0x221c4, 0x221c8,
                0x22200, 0x22318,
-               0x22400, 0x22528,
+               0x22400, 0x224b4,
+               0x224c0, 0x22528,
                0x22540, 0x22614,
                0x23000, 0x23040,
                0x2304c, 0x23060,
@@ -858,22 +977,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x23270, 0x23284,
                0x232fc, 0x23388,
                0x23400, 0x23404,
-               0x23500, 0x23518,
-               0x2352c, 0x2353c,
+               0x23500, 0x23500,
+               0x23510, 0x23518,
+               0x2352c, 0x23530,
+               0x2353c, 0x2353c,
                0x23550, 0x23554,
                0x23600, 0x23600,
-               0x23608, 0x23628,
-               0x23630, 0x2363c,
+               0x23608, 0x2361c,
+               0x23624, 0x23628,
+               0x23630, 0x23634,
+               0x2363c, 0x2363c,
                0x23700, 0x2371c,
                0x23780, 0x2378c,
-               0x23800, 0x23c38,
-               0x23c80, 0x23d7c,
+               0x23800, 0x23818,
+               0x23820, 0x23828,
+               0x23830, 0x23848,
+               0x23850, 0x23854,
+               0x23860, 0x23868,
+               0x23870, 0x23870,
+               0x23878, 0x23898,
+               0x238a0, 0x238a8,
+               0x238b0, 0x238c8,
+               0x238d0, 0x238d4,
+               0x238e0, 0x238e8,
+               0x238f0, 0x238f0,
+               0x238f8, 0x23a18,
+               0x23a20, 0x23a28,
+               0x23a30, 0x23a48,
+               0x23a50, 0x23a54,
+               0x23a60, 0x23a68,
+               0x23a70, 0x23a70,
+               0x23a78, 0x23a98,
+               0x23aa0, 0x23aa8,
+               0x23ab0, 0x23ac8,
+               0x23ad0, 0x23ad4,
+               0x23ae0, 0x23ae8,
+               0x23af0, 0x23af0,
+               0x23af8, 0x23c18,
+               0x23c20, 0x23c20,
+               0x23c28, 0x23c30,
+               0x23c38, 0x23c38,
+               0x23c80, 0x23c98,
+               0x23ca0, 0x23ca8,
+               0x23cb0, 0x23cc8,
+               0x23cd0, 0x23cd4,
+               0x23ce0, 0x23ce8,
+               0x23cf0, 0x23cf0,
+               0x23cf8, 0x23d7c,
                0x23e00, 0x23e04,
                0x24000, 0x2402c,
                0x24100, 0x2413c,
-               0x24190, 0x241c8,
+               0x24190, 0x241a0,
+               0x241a8, 0x241b8,
+               0x241c4, 0x241c8,
                0x24200, 0x24318,
-               0x24400, 0x24528,
+               0x24400, 0x244b4,
+               0x244c0, 0x24528,
                0x24540, 0x24614,
                0x25000, 0x25040,
                0x2504c, 0x25060,
@@ -882,22 +1041,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x25270, 0x25284,
                0x252fc, 0x25388,
                0x25400, 0x25404,
-               0x25500, 0x25518,
-               0x2552c, 0x2553c,
+               0x25500, 0x25500,
+               0x25510, 0x25518,
+               0x2552c, 0x25530,
+               0x2553c, 0x2553c,
                0x25550, 0x25554,
                0x25600, 0x25600,
-               0x25608, 0x25628,
-               0x25630, 0x2563c,
+               0x25608, 0x2561c,
+               0x25624, 0x25628,
+               0x25630, 0x25634,
+               0x2563c, 0x2563c,
                0x25700, 0x2571c,
                0x25780, 0x2578c,
-               0x25800, 0x25c38,
-               0x25c80, 0x25d7c,
+               0x25800, 0x25818,
+               0x25820, 0x25828,
+               0x25830, 0x25848,
+               0x25850, 0x25854,
+               0x25860, 0x25868,
+               0x25870, 0x25870,
+               0x25878, 0x25898,
+               0x258a0, 0x258a8,
+               0x258b0, 0x258c8,
+               0x258d0, 0x258d4,
+               0x258e0, 0x258e8,
+               0x258f0, 0x258f0,
+               0x258f8, 0x25a18,
+               0x25a20, 0x25a28,
+               0x25a30, 0x25a48,
+               0x25a50, 0x25a54,
+               0x25a60, 0x25a68,
+               0x25a70, 0x25a70,
+               0x25a78, 0x25a98,
+               0x25aa0, 0x25aa8,
+               0x25ab0, 0x25ac8,
+               0x25ad0, 0x25ad4,
+               0x25ae0, 0x25ae8,
+               0x25af0, 0x25af0,
+               0x25af8, 0x25c18,
+               0x25c20, 0x25c20,
+               0x25c28, 0x25c30,
+               0x25c38, 0x25c38,
+               0x25c80, 0x25c98,
+               0x25ca0, 0x25ca8,
+               0x25cb0, 0x25cc8,
+               0x25cd0, 0x25cd4,
+               0x25ce0, 0x25ce8,
+               0x25cf0, 0x25cf0,
+               0x25cf8, 0x25d7c,
                0x25e00, 0x25e04,
                0x26000, 0x2602c,
                0x26100, 0x2613c,
-               0x26190, 0x261c8,
+               0x26190, 0x261a0,
+               0x261a8, 0x261b8,
+               0x261c4, 0x261c8,
                0x26200, 0x26318,
-               0x26400, 0x26528,
+               0x26400, 0x264b4,
+               0x264c0, 0x26528,
                0x26540, 0x26614,
                0x27000, 0x27040,
                0x2704c, 0x27060,
@@ -906,51 +1105,120 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x27270, 0x27284,
                0x272fc, 0x27388,
                0x27400, 0x27404,
-               0x27500, 0x27518,
-               0x2752c, 0x2753c,
+               0x27500, 0x27500,
+               0x27510, 0x27518,
+               0x2752c, 0x27530,
+               0x2753c, 0x2753c,
                0x27550, 0x27554,
                0x27600, 0x27600,
-               0x27608, 0x27628,
-               0x27630, 0x2763c,
+               0x27608, 0x2761c,
+               0x27624, 0x27628,
+               0x27630, 0x27634,
+               0x2763c, 0x2763c,
                0x27700, 0x2771c,
                0x27780, 0x2778c,
-               0x27800, 0x27c38,
-               0x27c80, 0x27d7c,
+               0x27800, 0x27818,
+               0x27820, 0x27828,
+               0x27830, 0x27848,
+               0x27850, 0x27854,
+               0x27860, 0x27868,
+               0x27870, 0x27870,
+               0x27878, 0x27898,
+               0x278a0, 0x278a8,
+               0x278b0, 0x278c8,
+               0x278d0, 0x278d4,
+               0x278e0, 0x278e8,
+               0x278f0, 0x278f0,
+               0x278f8, 0x27a18,
+               0x27a20, 0x27a28,
+               0x27a30, 0x27a48,
+               0x27a50, 0x27a54,
+               0x27a60, 0x27a68,
+               0x27a70, 0x27a70,
+               0x27a78, 0x27a98,
+               0x27aa0, 0x27aa8,
+               0x27ab0, 0x27ac8,
+               0x27ad0, 0x27ad4,
+               0x27ae0, 0x27ae8,
+               0x27af0, 0x27af0,
+               0x27af8, 0x27c18,
+               0x27c20, 0x27c20,
+               0x27c28, 0x27c30,
+               0x27c38, 0x27c38,
+               0x27c80, 0x27c98,
+               0x27ca0, 0x27ca8,
+               0x27cb0, 0x27cc8,
+               0x27cd0, 0x27cd4,
+               0x27ce0, 0x27ce8,
+               0x27cf0, 0x27cf0,
+               0x27cf8, 0x27d7c,
                0x27e00, 0x27e04,
        };
 
        static const unsigned int t5_reg_ranges[] = {
-               0x1008, 0x1148,
-               0x1180, 0x11b4,
+               0x1008, 0x10c0,
+               0x10cc, 0x10f8,
+               0x1100, 0x1100,
+               0x110c, 0x1148,
+               0x1180, 0x1184,
+               0x1190, 0x1194,
+               0x11a0, 0x11a4,
+               0x11b0, 0x11b4,
                0x11fc, 0x123c,
                0x1280, 0x173c,
                0x1800, 0x18fc,
                0x3000, 0x3028,
-               0x3068, 0x30d8,
+               0x3060, 0x30b0,
+               0x30b8, 0x30d8,
                0x30e0, 0x30fc,
                0x3140, 0x357c,
                0x35a8, 0x35cc,
                0x35ec, 0x35ec,
                0x3600, 0x5624,
-               0x56cc, 0x575c,
+               0x56cc, 0x56ec,
+               0x56f4, 0x5720,
+               0x5728, 0x575c,
                0x580c, 0x5814,
-               0x5890, 0x58bc,
-               0x5940, 0x59dc,
+               0x5890, 0x589c,
+               0x58a4, 0x58ac,
+               0x58b8, 0x58bc,
+               0x5940, 0x59c8,
+               0x59d0, 0x59dc,
                0x59fc, 0x5a18,
-               0x5a60, 0x5a9c,
+               0x5a60, 0x5a70,
+               0x5a80, 0x5a9c,
                0x5b94, 0x5bfc,
-               0x6000, 0x6040,
-               0x6058, 0x614c,
+               0x6000, 0x6020,
+               0x6028, 0x6040,
+               0x6058, 0x609c,
+               0x60a8, 0x614c,
                0x7700, 0x7798,
                0x77c0, 0x78fc,
-               0x7b00, 0x7c54,
-               0x7d00, 0x7efc,
+               0x7b00, 0x7b58,
+               0x7b60, 0x7b84,
+               0x7b8c, 0x7c54,
+               0x7d00, 0x7d38,
+               0x7d40, 0x7d80,
+               0x7d8c, 0x7ddc,
+               0x7de4, 0x7e04,
+               0x7e10, 0x7e1c,
+               0x7e24, 0x7e38,
+               0x7e40, 0x7e44,
+               0x7e4c, 0x7e78,
+               0x7e80, 0x7edc,
+               0x7ee8, 0x7efc,
                0x8dc0, 0x8de0,
-               0x8df8, 0x8e84,
+               0x8df8, 0x8e04,
+               0x8e10, 0x8e84,
                0x8ea0, 0x8f84,
-               0x8fc0, 0x90f8,
-               0x9400, 0x9470,
-               0x9600, 0x96f4,
+               0x8fc0, 0x9058,
+               0x9060, 0x9060,
+               0x9068, 0x90f8,
+               0x9400, 0x9408,
+               0x9410, 0x9470,
+               0x9600, 0x9600,
+               0x9608, 0x9638,
+               0x9640, 0x96f4,
                0x9800, 0x9808,
                0x9820, 0x983c,
                0x9850, 0x9864,
@@ -962,103 +1230,143 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x9e80, 0x9eec,
                0x9f00, 0x9f6c,
                0x9f80, 0xa020,
-               0xd004, 0xd03c,
+               0xd004, 0xd004,
+               0xd010, 0xd03c,
                0xdfc0, 0xdfe0,
-               0xe000, 0x11088,
-               0x1109c, 0x11110,
-               0x11118, 0x1117c,
+               0xe000, 0x1106c,
+               0x11074, 0x11088,
+               0x1109c, 0x1117c,
                0x11190, 0x11204,
                0x19040, 0x1906c,
                0x19078, 0x19080,
-               0x1908c, 0x19124,
-               0x19150, 0x191b0,
+               0x1908c, 0x190e8,
+               0x190f0, 0x190f8,
+               0x19100, 0x19110,
+               0x19120, 0x19124,
+               0x19150, 0x19194,
+               0x1919c, 0x191b0,
                0x191d0, 0x191e8,
                0x19238, 0x19290,
-               0x193f8, 0x19474,
+               0x193f8, 0x19428,
+               0x19430, 0x19444,
+               0x1944c, 0x1946c,
+               0x19474, 0x19474,
                0x19490, 0x194cc,
                0x194f0, 0x194f8,
-               0x19c00, 0x19c60,
-               0x19c94, 0x19e10,
-               0x19e50, 0x19f34,
+               0x19c00, 0x19c08,
+               0x19c10, 0x19c60,
+               0x19c94, 0x19ce4,
+               0x19cf0, 0x19d40,
+               0x19d50, 0x19d94,
+               0x19da0, 0x19de8,
+               0x19df0, 0x19e10,
+               0x19e50, 0x19e90,
+               0x19ea0, 0x19f24,
+               0x19f34, 0x19f34,
                0x19f40, 0x19f50,
-               0x19f90, 0x19fe4,
-               0x1a000, 0x1a06c,
-               0x1a0b0, 0x1a120,
-               0x1a128, 0x1a138,
+               0x19f90, 0x19fb4,
+               0x19fc4, 0x19fe4,
+               0x1a000, 0x1a004,
+               0x1a010, 0x1a06c,
+               0x1a0b0, 0x1a0e4,
+               0x1a0ec, 0x1a0f8,
+               0x1a100, 0x1a108,
+               0x1a114, 0x1a120,
+               0x1a128, 0x1a130,
+               0x1a138, 0x1a138,
                0x1a190, 0x1a1c4,
                0x1a1fc, 0x1a1fc,
                0x1e008, 0x1e00c,
-               0x1e040, 0x1e04c,
+               0x1e040, 0x1e044,
+               0x1e04c, 0x1e04c,
                0x1e284, 0x1e290,
                0x1e2c0, 0x1e2c0,
                0x1e2e0, 0x1e2e0,
                0x1e300, 0x1e384,
                0x1e3c0, 0x1e3c8,
                0x1e408, 0x1e40c,
-               0x1e440, 0x1e44c,
+               0x1e440, 0x1e444,
+               0x1e44c, 0x1e44c,
                0x1e684, 0x1e690,
                0x1e6c0, 0x1e6c0,
                0x1e6e0, 0x1e6e0,
                0x1e700, 0x1e784,
                0x1e7c0, 0x1e7c8,
                0x1e808, 0x1e80c,
-               0x1e840, 0x1e84c,
+               0x1e840, 0x1e844,
+               0x1e84c, 0x1e84c,
                0x1ea84, 0x1ea90,
                0x1eac0, 0x1eac0,
                0x1eae0, 0x1eae0,
                0x1eb00, 0x1eb84,
                0x1ebc0, 0x1ebc8,
                0x1ec08, 0x1ec0c,
-               0x1ec40, 0x1ec4c,
+               0x1ec40, 0x1ec44,
+               0x1ec4c, 0x1ec4c,
                0x1ee84, 0x1ee90,
                0x1eec0, 0x1eec0,
                0x1eee0, 0x1eee0,
                0x1ef00, 0x1ef84,
                0x1efc0, 0x1efc8,
                0x1f008, 0x1f00c,
-               0x1f040, 0x1f04c,
+               0x1f040, 0x1f044,
+               0x1f04c, 0x1f04c,
                0x1f284, 0x1f290,
                0x1f2c0, 0x1f2c0,
                0x1f2e0, 0x1f2e0,
                0x1f300, 0x1f384,
                0x1f3c0, 0x1f3c8,
                0x1f408, 0x1f40c,
-               0x1f440, 0x1f44c,
+               0x1f440, 0x1f444,
+               0x1f44c, 0x1f44c,
                0x1f684, 0x1f690,
                0x1f6c0, 0x1f6c0,
                0x1f6e0, 0x1f6e0,
                0x1f700, 0x1f784,
                0x1f7c0, 0x1f7c8,
                0x1f808, 0x1f80c,
-               0x1f840, 0x1f84c,
+               0x1f840, 0x1f844,
+               0x1f84c, 0x1f84c,
                0x1fa84, 0x1fa90,
                0x1fac0, 0x1fac0,
                0x1fae0, 0x1fae0,
                0x1fb00, 0x1fb84,
                0x1fbc0, 0x1fbc8,
                0x1fc08, 0x1fc0c,
-               0x1fc40, 0x1fc4c,
+               0x1fc40, 0x1fc44,
+               0x1fc4c, 0x1fc4c,
                0x1fe84, 0x1fe90,
                0x1fec0, 0x1fec0,
                0x1fee0, 0x1fee0,
                0x1ff00, 0x1ff84,
                0x1ffc0, 0x1ffc8,
                0x30000, 0x30030,
+               0x30038, 0x30038,
+               0x30040, 0x30040,
                0x30100, 0x30144,
-               0x30190, 0x301d0,
+               0x30190, 0x301a0,
+               0x301a8, 0x301b8,
+               0x301c4, 0x301c8,
+               0x301d0, 0x301d0,
                0x30200, 0x30318,
-               0x30400, 0x3052c,
+               0x30400, 0x304b4,
+               0x304c0, 0x3052c,
                0x30540, 0x3061c,
-               0x30800, 0x30834,
+               0x30800, 0x30828,
+               0x30834, 0x30834,
                0x308c0, 0x30908,
                0x30910, 0x309ac,
-               0x30a00, 0x30a2c,
+               0x30a00, 0x30a14,
+               0x30a1c, 0x30a2c,
                0x30a44, 0x30a50,
-               0x30a74, 0x30c24,
+               0x30a74, 0x30a74,
+               0x30a7c, 0x30afc,
+               0x30b08, 0x30c24,
                0x30d00, 0x30d00,
                0x30d08, 0x30d14,
                0x30d1c, 0x30d20,
-               0x30d3c, 0x30d50,
+               0x30d3c, 0x30d3c,
+               0x30d48, 0x30d50,
                0x31200, 0x3120c,
                0x31220, 0x31220,
                0x31240, 0x31240,
@@ -1078,27 +1386,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x322c8, 0x322fc,
                0x32600, 0x32630,
                0x32a00, 0x32abc,
-               0x32b00, 0x32b70,
-               0x33000, 0x33048,
-               0x33060, 0x3309c,
-               0x330f0, 0x33148,
-               0x33160, 0x3319c,
-               0x331f0, 0x332e4,
-               0x332f8, 0x333e4,
-               0x333f8, 0x33448,
-               0x33460, 0x3349c,
-               0x334f0, 0x33548,
-               0x33560, 0x3359c,
-               0x335f0, 0x336e4,
-               0x336f8, 0x337e4,
+               0x32b00, 0x32b10,
+               0x32b20, 0x32b30,
+               0x32b40, 0x32b50,
+               0x32b60, 0x32b70,
+               0x33000, 0x33028,
+               0x33030, 0x33048,
+               0x33060, 0x33068,
+               0x33070, 0x3309c,
+               0x330f0, 0x33128,
+               0x33130, 0x33148,
+               0x33160, 0x33168,
+               0x33170, 0x3319c,
+               0x331f0, 0x33238,
+               0x33240, 0x33240,
+               0x33248, 0x33250,
+               0x3325c, 0x33264,
+               0x33270, 0x332b8,
+               0x332c0, 0x332e4,
+               0x332f8, 0x33338,
+               0x33340, 0x33340,
+               0x33348, 0x33350,
+               0x3335c, 0x33364,
+               0x33370, 0x333b8,
+               0x333c0, 0x333e4,
+               0x333f8, 0x33428,
+               0x33430, 0x33448,
+               0x33460, 0x33468,
+               0x33470, 0x3349c,
+               0x334f0, 0x33528,
+               0x33530, 0x33548,
+               0x33560, 0x33568,
+               0x33570, 0x3359c,
+               0x335f0, 0x33638,
+               0x33640, 0x33640,
+               0x33648, 0x33650,
+               0x3365c, 0x33664,
+               0x33670, 0x336b8,
+               0x336c0, 0x336e4,
+               0x336f8, 0x33738,
+               0x33740, 0x33740,
+               0x33748, 0x33750,
+               0x3375c, 0x33764,
+               0x33770, 0x337b8,
+               0x337c0, 0x337e4,
                0x337f8, 0x337fc,
                0x33814, 0x33814,
                0x3382c, 0x3382c,
                0x33880, 0x3388c,
                0x338e8, 0x338ec,
-               0x33900, 0x33948,
-               0x33960, 0x3399c,
-               0x339f0, 0x33ae4,
+               0x33900, 0x33928,
+               0x33930, 0x33948,
+               0x33960, 0x33968,
+               0x33970, 0x3399c,
+               0x339f0, 0x33a38,
+               0x33a40, 0x33a40,
+               0x33a48, 0x33a50,
+               0x33a5c, 0x33a64,
+               0x33a70, 0x33ab8,
+               0x33ac0, 0x33ae4,
                0x33af8, 0x33b10,
                0x33b28, 0x33b28,
                0x33b3c, 0x33b50,
@@ -1107,21 +1453,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x33c3c, 0x33c50,
                0x33cf0, 0x33cfc,
                0x34000, 0x34030,
+               0x34038, 0x34038,
+               0x34040, 0x34040,
                0x34100, 0x34144,
-               0x34190, 0x341d0,
+               0x34190, 0x341a0,
+               0x341a8, 0x341b8,
+               0x341c4, 0x341c8,
+               0x341d0, 0x341d0,
                0x34200, 0x34318,
-               0x34400, 0x3452c,
+               0x34400, 0x344b4,
+               0x344c0, 0x3452c,
                0x34540, 0x3461c,
-               0x34800, 0x34834,
+               0x34800, 0x34828,
+               0x34834, 0x34834,
                0x348c0, 0x34908,
                0x34910, 0x349ac,
-               0x34a00, 0x34a2c,
+               0x34a00, 0x34a14,
+               0x34a1c, 0x34a2c,
                0x34a44, 0x34a50,
-               0x34a74, 0x34c24,
+               0x34a74, 0x34a74,
+               0x34a7c, 0x34afc,
+               0x34b08, 0x34c24,
                0x34d00, 0x34d00,
                0x34d08, 0x34d14,
                0x34d1c, 0x34d20,
-               0x34d3c, 0x34d50,
+               0x34d3c, 0x34d3c,
+               0x34d48, 0x34d50,
                0x35200, 0x3520c,
                0x35220, 0x35220,
                0x35240, 0x35240,
@@ -1141,27 +1498,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x362c8, 0x362fc,
                0x36600, 0x36630,
                0x36a00, 0x36abc,
-               0x36b00, 0x36b70,
-               0x37000, 0x37048,
-               0x37060, 0x3709c,
-               0x370f0, 0x37148,
-               0x37160, 0x3719c,
-               0x371f0, 0x372e4,
-               0x372f8, 0x373e4,
-               0x373f8, 0x37448,
-               0x37460, 0x3749c,
-               0x374f0, 0x37548,
-               0x37560, 0x3759c,
-               0x375f0, 0x376e4,
-               0x376f8, 0x377e4,
+               0x36b00, 0x36b10,
+               0x36b20, 0x36b30,
+               0x36b40, 0x36b50,
+               0x36b60, 0x36b70,
+               0x37000, 0x37028,
+               0x37030, 0x37048,
+               0x37060, 0x37068,
+               0x37070, 0x3709c,
+               0x370f0, 0x37128,
+               0x37130, 0x37148,
+               0x37160, 0x37168,
+               0x37170, 0x3719c,
+               0x371f0, 0x37238,
+               0x37240, 0x37240,
+               0x37248, 0x37250,
+               0x3725c, 0x37264,
+               0x37270, 0x372b8,
+               0x372c0, 0x372e4,
+               0x372f8, 0x37338,
+               0x37340, 0x37340,
+               0x37348, 0x37350,
+               0x3735c, 0x37364,
+               0x37370, 0x373b8,
+               0x373c0, 0x373e4,
+               0x373f8, 0x37428,
+               0x37430, 0x37448,
+               0x37460, 0x37468,
+               0x37470, 0x3749c,
+               0x374f0, 0x37528,
+               0x37530, 0x37548,
+               0x37560, 0x37568,
+               0x37570, 0x3759c,
+               0x375f0, 0x37638,
+               0x37640, 0x37640,
+               0x37648, 0x37650,
+               0x3765c, 0x37664,
+               0x37670, 0x376b8,
+               0x376c0, 0x376e4,
+               0x376f8, 0x37738,
+               0x37740, 0x37740,
+               0x37748, 0x37750,
+               0x3775c, 0x37764,
+               0x37770, 0x377b8,
+               0x377c0, 0x377e4,
                0x377f8, 0x377fc,
                0x37814, 0x37814,
                0x3782c, 0x3782c,
                0x37880, 0x3788c,
                0x378e8, 0x378ec,
-               0x37900, 0x37948,
-               0x37960, 0x3799c,
-               0x379f0, 0x37ae4,
+               0x37900, 0x37928,
+               0x37930, 0x37948,
+               0x37960, 0x37968,
+               0x37970, 0x3799c,
+               0x379f0, 0x37a38,
+               0x37a40, 0x37a40,
+               0x37a48, 0x37a50,
+               0x37a5c, 0x37a64,
+               0x37a70, 0x37ab8,
+               0x37ac0, 0x37ae4,
                0x37af8, 0x37b10,
                0x37b28, 0x37b28,
                0x37b3c, 0x37b50,
@@ -1170,21 +1565,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x37c3c, 0x37c50,
                0x37cf0, 0x37cfc,
                0x38000, 0x38030,
+               0x38038, 0x38038,
+               0x38040, 0x38040,
                0x38100, 0x38144,
-               0x38190, 0x381d0,
+               0x38190, 0x381a0,
+               0x381a8, 0x381b8,
+               0x381c4, 0x381c8,
+               0x381d0, 0x381d0,
                0x38200, 0x38318,
-               0x38400, 0x3852c,
+               0x38400, 0x384b4,
+               0x384c0, 0x3852c,
                0x38540, 0x3861c,
-               0x38800, 0x38834,
+               0x38800, 0x38828,
+               0x38834, 0x38834,
                0x388c0, 0x38908,
                0x38910, 0x389ac,
-               0x38a00, 0x38a2c,
+               0x38a00, 0x38a14,
+               0x38a1c, 0x38a2c,
                0x38a44, 0x38a50,
-               0x38a74, 0x38c24,
+               0x38a74, 0x38a74,
+               0x38a7c, 0x38afc,
+               0x38b08, 0x38c24,
                0x38d00, 0x38d00,
                0x38d08, 0x38d14,
                0x38d1c, 0x38d20,
-               0x38d3c, 0x38d50,
+               0x38d3c, 0x38d3c,
+               0x38d48, 0x38d50,
                0x39200, 0x3920c,
                0x39220, 0x39220,
                0x39240, 0x39240,
@@ -1204,27 +1610,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x3a2c8, 0x3a2fc,
                0x3a600, 0x3a630,
                0x3aa00, 0x3aabc,
-               0x3ab00, 0x3ab70,
-               0x3b000, 0x3b048,
-               0x3b060, 0x3b09c,
-               0x3b0f0, 0x3b148,
-               0x3b160, 0x3b19c,
-               0x3b1f0, 0x3b2e4,
-               0x3b2f8, 0x3b3e4,
-               0x3b3f8, 0x3b448,
-               0x3b460, 0x3b49c,
-               0x3b4f0, 0x3b548,
-               0x3b560, 0x3b59c,
-               0x3b5f0, 0x3b6e4,
-               0x3b6f8, 0x3b7e4,
+               0x3ab00, 0x3ab10,
+               0x3ab20, 0x3ab30,
+               0x3ab40, 0x3ab50,
+               0x3ab60, 0x3ab70,
+               0x3b000, 0x3b028,
+               0x3b030, 0x3b048,
+               0x3b060, 0x3b068,
+               0x3b070, 0x3b09c,
+               0x3b0f0, 0x3b128,
+               0x3b130, 0x3b148,
+               0x3b160, 0x3b168,
+               0x3b170, 0x3b19c,
+               0x3b1f0, 0x3b238,
+               0x3b240, 0x3b240,
+               0x3b248, 0x3b250,
+               0x3b25c, 0x3b264,
+               0x3b270, 0x3b2b8,
+               0x3b2c0, 0x3b2e4,
+               0x3b2f8, 0x3b338,
+               0x3b340, 0x3b340,
+               0x3b348, 0x3b350,
+               0x3b35c, 0x3b364,
+               0x3b370, 0x3b3b8,
+               0x3b3c0, 0x3b3e4,
+               0x3b3f8, 0x3b428,
+               0x3b430, 0x3b448,
+               0x3b460, 0x3b468,
+               0x3b470, 0x3b49c,
+               0x3b4f0, 0x3b528,
+               0x3b530, 0x3b548,
+               0x3b560, 0x3b568,
+               0x3b570, 0x3b59c,
+               0x3b5f0, 0x3b638,
+               0x3b640, 0x3b640,
+               0x3b648, 0x3b650,
+               0x3b65c, 0x3b664,
+               0x3b670, 0x3b6b8,
+               0x3b6c0, 0x3b6e4,
+               0x3b6f8, 0x3b738,
+               0x3b740, 0x3b740,
+               0x3b748, 0x3b750,
+               0x3b75c, 0x3b764,
+               0x3b770, 0x3b7b8,
+               0x3b7c0, 0x3b7e4,
                0x3b7f8, 0x3b7fc,
                0x3b814, 0x3b814,
                0x3b82c, 0x3b82c,
                0x3b880, 0x3b88c,
                0x3b8e8, 0x3b8ec,
-               0x3b900, 0x3b948,
-               0x3b960, 0x3b99c,
-               0x3b9f0, 0x3bae4,
+               0x3b900, 0x3b928,
+               0x3b930, 0x3b948,
+               0x3b960, 0x3b968,
+               0x3b970, 0x3b99c,
+               0x3b9f0, 0x3ba38,
+               0x3ba40, 0x3ba40,
+               0x3ba48, 0x3ba50,
+               0x3ba5c, 0x3ba64,
+               0x3ba70, 0x3bab8,
+               0x3bac0, 0x3bae4,
                0x3baf8, 0x3bb10,
                0x3bb28, 0x3bb28,
                0x3bb3c, 0x3bb50,
@@ -1233,21 +1677,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x3bc3c, 0x3bc50,
                0x3bcf0, 0x3bcfc,
                0x3c000, 0x3c030,
+               0x3c038, 0x3c038,
+               0x3c040, 0x3c040,
                0x3c100, 0x3c144,
-               0x3c190, 0x3c1d0,
+               0x3c190, 0x3c1a0,
+               0x3c1a8, 0x3c1b8,
+               0x3c1c4, 0x3c1c8,
+               0x3c1d0, 0x3c1d0,
                0x3c200, 0x3c318,
-               0x3c400, 0x3c52c,
+               0x3c400, 0x3c4b4,
+               0x3c4c0, 0x3c52c,
                0x3c540, 0x3c61c,
-               0x3c800, 0x3c834,
+               0x3c800, 0x3c828,
+               0x3c834, 0x3c834,
                0x3c8c0, 0x3c908,
                0x3c910, 0x3c9ac,
-               0x3ca00, 0x3ca2c,
+               0x3ca00, 0x3ca14,
+               0x3ca1c, 0x3ca2c,
                0x3ca44, 0x3ca50,
-               0x3ca74, 0x3cc24,
+               0x3ca74, 0x3ca74,
+               0x3ca7c, 0x3cafc,
+               0x3cb08, 0x3cc24,
                0x3cd00, 0x3cd00,
                0x3cd08, 0x3cd14,
                0x3cd1c, 0x3cd20,
-               0x3cd3c, 0x3cd50,
+               0x3cd3c, 0x3cd3c,
+               0x3cd48, 0x3cd50,
                0x3d200, 0x3d20c,
                0x3d220, 0x3d220,
                0x3d240, 0x3d240,
@@ -1267,27 +1722,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x3e2c8, 0x3e2fc,
                0x3e600, 0x3e630,
                0x3ea00, 0x3eabc,
-               0x3eb00, 0x3eb70,
-               0x3f000, 0x3f048,
-               0x3f060, 0x3f09c,
-               0x3f0f0, 0x3f148,
-               0x3f160, 0x3f19c,
-               0x3f1f0, 0x3f2e4,
-               0x3f2f8, 0x3f3e4,
-               0x3f3f8, 0x3f448,
-               0x3f460, 0x3f49c,
-               0x3f4f0, 0x3f548,
-               0x3f560, 0x3f59c,
-               0x3f5f0, 0x3f6e4,
-               0x3f6f8, 0x3f7e4,
+               0x3eb00, 0x3eb10,
+               0x3eb20, 0x3eb30,
+               0x3eb40, 0x3eb50,
+               0x3eb60, 0x3eb70,
+               0x3f000, 0x3f028,
+               0x3f030, 0x3f048,
+               0x3f060, 0x3f068,
+               0x3f070, 0x3f09c,
+               0x3f0f0, 0x3f128,
+               0x3f130, 0x3f148,
+               0x3f160, 0x3f168,
+               0x3f170, 0x3f19c,
+               0x3f1f0, 0x3f238,
+               0x3f240, 0x3f240,
+               0x3f248, 0x3f250,
+               0x3f25c, 0x3f264,
+               0x3f270, 0x3f2b8,
+               0x3f2c0, 0x3f2e4,
+               0x3f2f8, 0x3f338,
+               0x3f340, 0x3f340,
+               0x3f348, 0x3f350,
+               0x3f35c, 0x3f364,
+               0x3f370, 0x3f3b8,
+               0x3f3c0, 0x3f3e4,
+               0x3f3f8, 0x3f428,
+               0x3f430, 0x3f448,
+               0x3f460, 0x3f468,
+               0x3f470, 0x3f49c,
+               0x3f4f0, 0x3f528,
+               0x3f530, 0x3f548,
+               0x3f560, 0x3f568,
+               0x3f570, 0x3f59c,
+               0x3f5f0, 0x3f638,
+               0x3f640, 0x3f640,
+               0x3f648, 0x3f650,
+               0x3f65c, 0x3f664,
+               0x3f670, 0x3f6b8,
+               0x3f6c0, 0x3f6e4,
+               0x3f6f8, 0x3f738,
+               0x3f740, 0x3f740,
+               0x3f748, 0x3f750,
+               0x3f75c, 0x3f764,
+               0x3f770, 0x3f7b8,
+               0x3f7c0, 0x3f7e4,
                0x3f7f8, 0x3f7fc,
                0x3f814, 0x3f814,
                0x3f82c, 0x3f82c,
                0x3f880, 0x3f88c,
                0x3f8e8, 0x3f8ec,
-               0x3f900, 0x3f948,
-               0x3f960, 0x3f99c,
-               0x3f9f0, 0x3fae4,
+               0x3f900, 0x3f928,
+               0x3f930, 0x3f948,
+               0x3f960, 0x3f968,
+               0x3f970, 0x3f99c,
+               0x3f9f0, 0x3fa38,
+               0x3fa40, 0x3fa40,
+               0x3fa48, 0x3fa50,
+               0x3fa5c, 0x3fa64,
+               0x3fa70, 0x3fab8,
+               0x3fac0, 0x3fae4,
                0x3faf8, 0x3fb10,
                0x3fb28, 0x3fb28,
                0x3fb3c, 0x3fb50,
@@ -1296,108 +1789,224 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x3fc3c, 0x3fc50,
                0x3fcf0, 0x3fcfc,
                0x40000, 0x4000c,
-               0x40040, 0x40068,
-               0x4007c, 0x40144,
+               0x40040, 0x40050,
+               0x40060, 0x40068,
+               0x4007c, 0x4008c,
+               0x40094, 0x400b0,
+               0x400c0, 0x40144,
                0x40180, 0x4018c,
-               0x40200, 0x40298,
-               0x402ac, 0x4033c,
+               0x40200, 0x40254,
+               0x40260, 0x40264,
+               0x40270, 0x40288,
+               0x40290, 0x40298,
+               0x402ac, 0x402c8,
+               0x402d0, 0x402e0,
+               0x402f0, 0x402f0,
+               0x40300, 0x4033c,
                0x403f8, 0x403fc,
                0x41304, 0x413c4,
-               0x41400, 0x4141c,
+               0x41400, 0x4140c,
+               0x41414, 0x4141c,
                0x41480, 0x414d0,
-               0x44000, 0x44078,
-               0x440c0, 0x44278,
-               0x442c0, 0x44478,
-               0x444c0, 0x44678,
-               0x446c0, 0x44878,
-               0x448c0, 0x449fc,
-               0x45000, 0x45068,
+               0x44000, 0x44054,
+               0x4405c, 0x44078,
+               0x440c0, 0x44174,
+               0x44180, 0x441ac,
+               0x441b4, 0x441b8,
+               0x441c0, 0x44254,
+               0x4425c, 0x44278,
+               0x442c0, 0x44374,
+               0x44380, 0x443ac,
+               0x443b4, 0x443b8,
+               0x443c0, 0x44454,
+               0x4445c, 0x44478,
+               0x444c0, 0x44574,
+               0x44580, 0x445ac,
+               0x445b4, 0x445b8,
+               0x445c0, 0x44654,
+               0x4465c, 0x44678,
+               0x446c0, 0x44774,
+               0x44780, 0x447ac,
+               0x447b4, 0x447b8,
+               0x447c0, 0x44854,
+               0x4485c, 0x44878,
+               0x448c0, 0x44974,
+               0x44980, 0x449ac,
+               0x449b4, 0x449b8,
+               0x449c0, 0x449fc,
+               0x45000, 0x45004,
+               0x45010, 0x45030,
+               0x45040, 0x45060,
+               0x45068, 0x45068,
                0x45080, 0x45084,
                0x450a0, 0x450b0,
-               0x45200, 0x45268,
+               0x45200, 0x45204,
+               0x45210, 0x45230,
+               0x45240, 0x45260,
+               0x45268, 0x45268,
                0x45280, 0x45284,
                0x452a0, 0x452b0,
                0x460c0, 0x460e4,
-               0x47000, 0x4708c,
+               0x47000, 0x4703c,
+               0x47044, 0x4708c,
                0x47200, 0x47250,
-               0x47400, 0x47420,
+               0x47400, 0x47408,
+               0x47414, 0x47420,
                0x47600, 0x47618,
                0x47800, 0x47814,
                0x48000, 0x4800c,
-               0x48040, 0x48068,
-               0x4807c, 0x48144,
+               0x48040, 0x48050,
+               0x48060, 0x48068,
+               0x4807c, 0x4808c,
+               0x48094, 0x480b0,
+               0x480c0, 0x48144,
                0x48180, 0x4818c,
-               0x48200, 0x48298,
-               0x482ac, 0x4833c,
+               0x48200, 0x48254,
+               0x48260, 0x48264,
+               0x48270, 0x48288,
+               0x48290, 0x48298,
+               0x482ac, 0x482c8,
+               0x482d0, 0x482e0,
+               0x482f0, 0x482f0,
+               0x48300, 0x4833c,
                0x483f8, 0x483fc,
                0x49304, 0x493c4,
-               0x49400, 0x4941c,
+               0x49400, 0x4940c,
+               0x49414, 0x4941c,
                0x49480, 0x494d0,
-               0x4c000, 0x4c078,
-               0x4c0c0, 0x4c278,
-               0x4c2c0, 0x4c478,
-               0x4c4c0, 0x4c678,
-               0x4c6c0, 0x4c878,
-               0x4c8c0, 0x4c9fc,
-               0x4d000, 0x4d068,
+               0x4c000, 0x4c054,
+               0x4c05c, 0x4c078,
+               0x4c0c0, 0x4c174,
+               0x4c180, 0x4c1ac,
+               0x4c1b4, 0x4c1b8,
+               0x4c1c0, 0x4c254,
+               0x4c25c, 0x4c278,
+               0x4c2c0, 0x4c374,
+               0x4c380, 0x4c3ac,
+               0x4c3b4, 0x4c3b8,
+               0x4c3c0, 0x4c454,
+               0x4c45c, 0x4c478,
+               0x4c4c0, 0x4c574,
+               0x4c580, 0x4c5ac,
+               0x4c5b4, 0x4c5b8,
+               0x4c5c0, 0x4c654,
+               0x4c65c, 0x4c678,
+               0x4c6c0, 0x4c774,
+               0x4c780, 0x4c7ac,
+               0x4c7b4, 0x4c7b8,
+               0x4c7c0, 0x4c854,
+               0x4c85c, 0x4c878,
+               0x4c8c0, 0x4c974,
+               0x4c980, 0x4c9ac,
+               0x4c9b4, 0x4c9b8,
+               0x4c9c0, 0x4c9fc,
+               0x4d000, 0x4d004,
+               0x4d010, 0x4d030,
+               0x4d040, 0x4d060,
+               0x4d068, 0x4d068,
                0x4d080, 0x4d084,
                0x4d0a0, 0x4d0b0,
-               0x4d200, 0x4d268,
+               0x4d200, 0x4d204,
+               0x4d210, 0x4d230,
+               0x4d240, 0x4d260,
+               0x4d268, 0x4d268,
                0x4d280, 0x4d284,
                0x4d2a0, 0x4d2b0,
                0x4e0c0, 0x4e0e4,
-               0x4f000, 0x4f08c,
+               0x4f000, 0x4f03c,
+               0x4f044, 0x4f08c,
                0x4f200, 0x4f250,
-               0x4f400, 0x4f420,
+               0x4f400, 0x4f408,
+               0x4f414, 0x4f420,
                0x4f600, 0x4f618,
                0x4f800, 0x4f814,
-               0x50000, 0x500cc,
+               0x50000, 0x50084,
+               0x50090, 0x500cc,
                0x50400, 0x50400,
-               0x50800, 0x508cc,
+               0x50800, 0x50884,
+               0x50890, 0x508cc,
                0x50c00, 0x50c00,
                0x51000, 0x5101c,
                0x51300, 0x51308,
        };
 
        static const unsigned int t6_reg_ranges[] = {
-               0x1008, 0x1124,
-               0x1138, 0x114c,
-               0x1180, 0x11b4,
+               0x1008, 0x101c,
+               0x1024, 0x10a8,
+               0x10b4, 0x10f8,
+               0x1100, 0x1114,
+               0x111c, 0x112c,
+               0x1138, 0x113c,
+               0x1144, 0x114c,
+               0x1180, 0x1184,
+               0x1190, 0x1194,
+               0x11a0, 0x11a4,
+               0x11b0, 0x11b4,
                0x11fc, 0x1254,
                0x1280, 0x133c,
                0x1800, 0x18fc,
                0x3000, 0x302c,
-               0x3060, 0x30d8,
+               0x3060, 0x30b0,
+               0x30b8, 0x30d8,
                0x30e0, 0x30fc,
                0x3140, 0x357c,
                0x35a8, 0x35cc,
                0x35ec, 0x35ec,
                0x3600, 0x5624,
-               0x56cc, 0x575c,
+               0x56cc, 0x56ec,
+               0x56f4, 0x5720,
+               0x5728, 0x575c,
                0x580c, 0x5814,
-               0x5890, 0x58bc,
+               0x5890, 0x589c,
+               0x58a4, 0x58ac,
+               0x58b8, 0x58bc,
                0x5940, 0x595c,
                0x5980, 0x598c,
-               0x59b0, 0x59dc,
+               0x59b0, 0x59c8,
+               0x59d0, 0x59dc,
                0x59fc, 0x5a18,
                0x5a60, 0x5a6c,
-               0x5a80, 0x5a9c,
+               0x5a80, 0x5a8c,
+               0x5a94, 0x5a9c,
                0x5b94, 0x5bfc,
-               0x5c10, 0x5ec0,
+               0x5c10, 0x5e48,
+               0x5e50, 0x5e94,
+               0x5ea0, 0x5eb0,
+               0x5ec0, 0x5ec0,
                0x5ec8, 0x5ecc,
-               0x6000, 0x6040,
-               0x6058, 0x619c,
+               0x6000, 0x6020,
+               0x6028, 0x6040,
+               0x6058, 0x609c,
+               0x60a8, 0x619c,
                0x7700, 0x7798,
                0x77c0, 0x7880,
                0x78cc, 0x78fc,
-               0x7b00, 0x7c54,
-               0x7d00, 0x7efc,
+               0x7b00, 0x7b58,
+               0x7b60, 0x7b84,
+               0x7b8c, 0x7c54,
+               0x7d00, 0x7d38,
+               0x7d40, 0x7d84,
+               0x7d8c, 0x7ddc,
+               0x7de4, 0x7e04,
+               0x7e10, 0x7e1c,
+               0x7e24, 0x7e38,
+               0x7e40, 0x7e44,
+               0x7e4c, 0x7e78,
+               0x7e80, 0x7edc,
+               0x7ee8, 0x7efc,
                0x8dc0, 0x8de4,
-               0x8df8, 0x8e84,
+               0x8df8, 0x8e04,
+               0x8e10, 0x8e84,
                0x8ea0, 0x8f88,
-               0x8fb8, 0x9124,
+               0x8fb8, 0x9058,
+               0x9060, 0x9060,
+               0x9068, 0x90f8,
+               0x9100, 0x9124,
                0x9400, 0x9470,
-               0x9600, 0x971c,
+               0x9600, 0x9600,
+               0x9608, 0x9638,
+               0x9640, 0x9704,
+               0x9710, 0x971c,
                0x9800, 0x9808,
                0x9820, 0x983c,
                0x9850, 0x9864,
@@ -1411,109 +2020,170 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x9f80, 0xa020,
                0xd004, 0xd03c,
                0xd100, 0xd118,
-               0xd200, 0xd31c,
+               0xd200, 0xd214,
+               0xd220, 0xd234,
+               0xd240, 0xd254,
+               0xd260, 0xd274,
+               0xd280, 0xd294,
+               0xd2a0, 0xd2b4,
+               0xd2c0, 0xd2d4,
+               0xd2e0, 0xd2f4,
+               0xd300, 0xd31c,
                0xdfc0, 0xdfe0,
                0xe000, 0xf008,
                0x11000, 0x11014,
-               0x11048, 0x1117c,
-               0x11190, 0x11270,
+               0x11048, 0x1106c,
+               0x11074, 0x11088,
+               0x11098, 0x11120,
+               0x1112c, 0x1117c,
+               0x11190, 0x112e0,
                0x11300, 0x1130c,
                0x12000, 0x1206c,
                0x19040, 0x1906c,
                0x19078, 0x19080,
-               0x1908c, 0x19124,
-               0x19150, 0x191b0,
+               0x1908c, 0x190e8,
+               0x190f0, 0x190f8,
+               0x19100, 0x19110,
+               0x19120, 0x19124,
+               0x19150, 0x19194,
+               0x1919c, 0x191b0,
                0x191d0, 0x191e8,
-               0x19238, 0x192bc,
-               0x193f8, 0x19474,
+               0x19238, 0x192b0,
+               0x192bc, 0x192bc,
+               0x19348, 0x1934c,
+               0x193f8, 0x19418,
+               0x19420, 0x19428,
+               0x19430, 0x19444,
+               0x1944c, 0x1946c,
+               0x19474, 0x19474,
                0x19490, 0x194cc,
                0x194f0, 0x194f8,
-               0x19c00, 0x19c80,
-               0x19c94, 0x19cbc,
-               0x19ce4, 0x19d28,
+               0x19c00, 0x19c48,
+               0x19c50, 0x19c80,
+               0x19c94, 0x19c98,
+               0x19ca0, 0x19cbc,
+               0x19ce4, 0x19ce4,
+               0x19cf0, 0x19cf8,
+               0x19d00, 0x19d28,
                0x19d50, 0x19d78,
-               0x19d94, 0x19dc8,
+               0x19d94, 0x19d98,
+               0x19da0, 0x19dc8,
                0x19df0, 0x19e10,
                0x19e50, 0x19e6c,
-               0x19ea0, 0x19f34,
+               0x19ea0, 0x19ebc,
+               0x19ec4, 0x19ef4,
+               0x19f04, 0x19f2c,
+               0x19f34, 0x19f34,
                0x19f40, 0x19f50,
                0x19f90, 0x19fac,
-               0x19fc4, 0x19fe4,
-               0x1a000, 0x1a06c,
-               0x1a0b0, 0x1a120,
-               0x1a128, 0x1a138,
+               0x19fc4, 0x19fc8,
+               0x19fd0, 0x19fe4,
+               0x1a000, 0x1a004,
+               0x1a010, 0x1a06c,
+               0x1a0b0, 0x1a0e4,
+               0x1a0ec, 0x1a0f8,
+               0x1a100, 0x1a108,
+               0x1a114, 0x1a120,
+               0x1a128, 0x1a130,
+               0x1a138, 0x1a138,
                0x1a190, 0x1a1c4,
                0x1a1fc, 0x1a1fc,
                0x1e008, 0x1e00c,
-               0x1e040, 0x1e04c,
+               0x1e040, 0x1e044,
+               0x1e04c, 0x1e04c,
                0x1e284, 0x1e290,
                0x1e2c0, 0x1e2c0,
                0x1e2e0, 0x1e2e0,
                0x1e300, 0x1e384,
                0x1e3c0, 0x1e3c8,
                0x1e408, 0x1e40c,
-               0x1e440, 0x1e44c,
+               0x1e440, 0x1e444,
+               0x1e44c, 0x1e44c,
                0x1e684, 0x1e690,
                0x1e6c0, 0x1e6c0,
                0x1e6e0, 0x1e6e0,
                0x1e700, 0x1e784,
                0x1e7c0, 0x1e7c8,
                0x1e808, 0x1e80c,
-               0x1e840, 0x1e84c,
+               0x1e840, 0x1e844,
+               0x1e84c, 0x1e84c,
                0x1ea84, 0x1ea90,
                0x1eac0, 0x1eac0,
                0x1eae0, 0x1eae0,
                0x1eb00, 0x1eb84,
                0x1ebc0, 0x1ebc8,
                0x1ec08, 0x1ec0c,
-               0x1ec40, 0x1ec4c,
+               0x1ec40, 0x1ec44,
+               0x1ec4c, 0x1ec4c,
                0x1ee84, 0x1ee90,
                0x1eec0, 0x1eec0,
                0x1eee0, 0x1eee0,
                0x1ef00, 0x1ef84,
                0x1efc0, 0x1efc8,
                0x1f008, 0x1f00c,
-               0x1f040, 0x1f04c,
+               0x1f040, 0x1f044,
+               0x1f04c, 0x1f04c,
                0x1f284, 0x1f290,
                0x1f2c0, 0x1f2c0,
                0x1f2e0, 0x1f2e0,
                0x1f300, 0x1f384,
                0x1f3c0, 0x1f3c8,
                0x1f408, 0x1f40c,
-               0x1f440, 0x1f44c,
+               0x1f440, 0x1f444,
+               0x1f44c, 0x1f44c,
                0x1f684, 0x1f690,
                0x1f6c0, 0x1f6c0,
                0x1f6e0, 0x1f6e0,
                0x1f700, 0x1f784,
                0x1f7c0, 0x1f7c8,
                0x1f808, 0x1f80c,
-               0x1f840, 0x1f84c,
+               0x1f840, 0x1f844,
+               0x1f84c, 0x1f84c,
                0x1fa84, 0x1fa90,
                0x1fac0, 0x1fac0,
                0x1fae0, 0x1fae0,
                0x1fb00, 0x1fb84,
                0x1fbc0, 0x1fbc8,
                0x1fc08, 0x1fc0c,
-               0x1fc40, 0x1fc4c,
+               0x1fc40, 0x1fc44,
+               0x1fc4c, 0x1fc4c,
                0x1fe84, 0x1fe90,
                0x1fec0, 0x1fec0,
                0x1fee0, 0x1fee0,
                0x1ff00, 0x1ff84,
                0x1ffc0, 0x1ffc8,
-               0x30000, 0x30070,
-               0x30100, 0x301d0,
+               0x30000, 0x30030,
+               0x30038, 0x30038,
+               0x30040, 0x30040,
+               0x30048, 0x30048,
+               0x30050, 0x30050,
+               0x3005c, 0x30060,
+               0x30068, 0x30068,
+               0x30070, 0x30070,
+               0x30100, 0x30168,
+               0x30190, 0x301a0,
+               0x301a8, 0x301b8,
+               0x301c4, 0x301c8,
+               0x301d0, 0x301d0,
                0x30200, 0x30320,
-               0x30400, 0x3052c,
+               0x30400, 0x304b4,
+               0x304c0, 0x3052c,
                0x30540, 0x3061c,
-               0x30800, 0x30890,
+               0x30800, 0x308a0,
                0x308c0, 0x30908,
                0x30910, 0x309b8,
                0x30a00, 0x30a04,
-               0x30a0c, 0x30a2c,
+               0x30a0c, 0x30a14,
+               0x30a1c, 0x30a2c,
                0x30a44, 0x30a50,
-               0x30a74, 0x30c24,
-               0x30d00, 0x30d3c,
-               0x30d44, 0x30d7c,
+               0x30a74, 0x30a74,
+               0x30a7c, 0x30afc,
+               0x30b08, 0x30c24,
+               0x30d00, 0x30d14,
+               0x30d1c, 0x30d3c,
+               0x30d44, 0x30d4c,
+               0x30d54, 0x30d74,
+               0x30d7c, 0x30d7c,
                0x30de0, 0x30de0,
                0x30e00, 0x30ed4,
                0x30f00, 0x30fa4,
@@ -1542,7 +2212,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x31bb0, 0x31bb4,
                0x31bc8, 0x31bd4,
                0x32140, 0x3218c,
-               0x321f0, 0x32200,
+               0x321f0, 0x321f4,
+               0x32200, 0x32200,
                0x32218, 0x32218,
                0x32400, 0x32400,
                0x32408, 0x3241c,
@@ -1551,46 +2222,108 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x326a8, 0x326a8,
                0x326ec, 0x326ec,
                0x32a00, 0x32abc,
-               0x32b00, 0x32b78,
+               0x32b00, 0x32b38,
+               0x32b40, 0x32b58,
+               0x32b60, 0x32b78,
                0x32c00, 0x32c00,
                0x32c08, 0x32c3c,
                0x32e00, 0x32e2c,
                0x32f00, 0x32f2c,
-               0x33000, 0x330ac,
-               0x330c0, 0x331ac,
-               0x331c0, 0x332c4,
-               0x332e4, 0x333c4,
-               0x333e4, 0x334ac,
-               0x334c0, 0x335ac,
-               0x335c0, 0x336c4,
-               0x336e4, 0x337c4,
+               0x33000, 0x3302c,
+               0x33034, 0x33050,
+               0x33058, 0x33058,
+               0x33060, 0x3308c,
+               0x3309c, 0x330ac,
+               0x330c0, 0x330c0,
+               0x330c8, 0x330d0,
+               0x330d8, 0x330e0,
+               0x330ec, 0x3312c,
+               0x33134, 0x33150,
+               0x33158, 0x33158,
+               0x33160, 0x3318c,
+               0x3319c, 0x331ac,
+               0x331c0, 0x331c0,
+               0x331c8, 0x331d0,
+               0x331d8, 0x331e0,
+               0x331ec, 0x33290,
+               0x33298, 0x332c4,
+               0x332e4, 0x33390,
+               0x33398, 0x333c4,
+               0x333e4, 0x3342c,
+               0x33434, 0x33450,
+               0x33458, 0x33458,
+               0x33460, 0x3348c,
+               0x3349c, 0x334ac,
+               0x334c0, 0x334c0,
+               0x334c8, 0x334d0,
+               0x334d8, 0x334e0,
+               0x334ec, 0x3352c,
+               0x33534, 0x33550,
+               0x33558, 0x33558,
+               0x33560, 0x3358c,
+               0x3359c, 0x335ac,
+               0x335c0, 0x335c0,
+               0x335c8, 0x335d0,
+               0x335d8, 0x335e0,
+               0x335ec, 0x33690,
+               0x33698, 0x336c4,
+               0x336e4, 0x33790,
+               0x33798, 0x337c4,
                0x337e4, 0x337fc,
                0x33814, 0x33814,
                0x33854, 0x33868,
                0x33880, 0x3388c,
                0x338c0, 0x338d0,
                0x338e8, 0x338ec,
-               0x33900, 0x339ac,
-               0x339c0, 0x33ac4,
+               0x33900, 0x3392c,
+               0x33934, 0x33950,
+               0x33958, 0x33958,
+               0x33960, 0x3398c,
+               0x3399c, 0x339ac,
+               0x339c0, 0x339c0,
+               0x339c8, 0x339d0,
+               0x339d8, 0x339e0,
+               0x339ec, 0x33a90,
+               0x33a98, 0x33ac4,
                0x33ae4, 0x33b10,
-               0x33b24, 0x33b50,
+               0x33b24, 0x33b28,
+               0x33b38, 0x33b50,
                0x33bf0, 0x33c10,
-               0x33c24, 0x33c50,
+               0x33c24, 0x33c28,
+               0x33c38, 0x33c50,
                0x33cf0, 0x33cfc,
-               0x34000, 0x34070,
-               0x34100, 0x341d0,
+               0x34000, 0x34030,
+               0x34038, 0x34038,
+               0x34040, 0x34040,
+               0x34048, 0x34048,
+               0x34050, 0x34050,
+               0x3405c, 0x34060,
+               0x34068, 0x34068,
+               0x34070, 0x34070,
+               0x34100, 0x34168,
+               0x34190, 0x341a0,
+               0x341a8, 0x341b8,
+               0x341c4, 0x341c8,
+               0x341d0, 0x341d0,
                0x34200, 0x34320,
-               0x34400, 0x3452c,
+               0x34400, 0x344b4,
+               0x344c0, 0x3452c,
                0x34540, 0x3461c,
-               0x34800, 0x34890,
+               0x34800, 0x348a0,
                0x348c0, 0x34908,
                0x34910, 0x349b8,
                0x34a00, 0x34a04,
-               0x34a0c, 0x34a2c,
+               0x34a0c, 0x34a14,
+               0x34a1c, 0x34a2c,
                0x34a44, 0x34a50,
-               0x34a74, 0x34c24,
-               0x34d00, 0x34d3c,
-               0x34d44, 0x34d7c,
+               0x34a74, 0x34a74,
+               0x34a7c, 0x34afc,
+               0x34b08, 0x34c24,
+               0x34d00, 0x34d14,
+               0x34d1c, 0x34d3c,
+               0x34d44, 0x34d4c,
+               0x34d54, 0x34d74,
+               0x34d7c, 0x34d7c,
                0x34de0, 0x34de0,
                0x34e00, 0x34ed4,
                0x34f00, 0x34fa4,
@@ -1619,7 +2352,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x35bb0, 0x35bb4,
                0x35bc8, 0x35bd4,
                0x36140, 0x3618c,
-               0x361f0, 0x36200,
+               0x361f0, 0x361f4,
+               0x36200, 0x36200,
                0x36218, 0x36218,
                0x36400, 0x36400,
                0x36408, 0x3641c,
@@ -1628,31 +2362,75 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x366a8, 0x366a8,
                0x366ec, 0x366ec,
                0x36a00, 0x36abc,
-               0x36b00, 0x36b78,
+               0x36b00, 0x36b38,
+               0x36b40, 0x36b58,
+               0x36b60, 0x36b78,
                0x36c00, 0x36c00,
                0x36c08, 0x36c3c,
                0x36e00, 0x36e2c,
                0x36f00, 0x36f2c,
-               0x37000, 0x370ac,
-               0x370c0, 0x371ac,
-               0x371c0, 0x372c4,
-               0x372e4, 0x373c4,
-               0x373e4, 0x374ac,
-               0x374c0, 0x375ac,
-               0x375c0, 0x376c4,
-               0x376e4, 0x377c4,
+               0x37000, 0x3702c,
+               0x37034, 0x37050,
+               0x37058, 0x37058,
+               0x37060, 0x3708c,
+               0x3709c, 0x370ac,
+               0x370c0, 0x370c0,
+               0x370c8, 0x370d0,
+               0x370d8, 0x370e0,
+               0x370ec, 0x3712c,
+               0x37134, 0x37150,
+               0x37158, 0x37158,
+               0x37160, 0x3718c,
+               0x3719c, 0x371ac,
+               0x371c0, 0x371c0,
+               0x371c8, 0x371d0,
+               0x371d8, 0x371e0,
+               0x371ec, 0x37290,
+               0x37298, 0x372c4,
+               0x372e4, 0x37390,
+               0x37398, 0x373c4,
+               0x373e4, 0x3742c,
+               0x37434, 0x37450,
+               0x37458, 0x37458,
+               0x37460, 0x3748c,
+               0x3749c, 0x374ac,
+               0x374c0, 0x374c0,
+               0x374c8, 0x374d0,
+               0x374d8, 0x374e0,
+               0x374ec, 0x3752c,
+               0x37534, 0x37550,
+               0x37558, 0x37558,
+               0x37560, 0x3758c,
+               0x3759c, 0x375ac,
+               0x375c0, 0x375c0,
+               0x375c8, 0x375d0,
+               0x375d8, 0x375e0,
+               0x375ec, 0x37690,
+               0x37698, 0x376c4,
+               0x376e4, 0x37790,
+               0x37798, 0x377c4,
                0x377e4, 0x377fc,
                0x37814, 0x37814,
                0x37854, 0x37868,
                0x37880, 0x3788c,
                0x378c0, 0x378d0,
                0x378e8, 0x378ec,
-               0x37900, 0x379ac,
-               0x379c0, 0x37ac4,
+               0x37900, 0x3792c,
+               0x37934, 0x37950,
+               0x37958, 0x37958,
+               0x37960, 0x3798c,
+               0x3799c, 0x379ac,
+               0x379c0, 0x379c0,
+               0x379c8, 0x379d0,
+               0x379d8, 0x379e0,
+               0x379ec, 0x37a90,
+               0x37a98, 0x37ac4,
                0x37ae4, 0x37b10,
-               0x37b24, 0x37b50,
+               0x37b24, 0x37b28,
+               0x37b38, 0x37b50,
                0x37bf0, 0x37c10,
-               0x37c24, 0x37c50,
+               0x37c24, 0x37c28,
+               0x37c38, 0x37c50,
                0x37cf0, 0x37cfc,
                0x40040, 0x40040,
                0x40080, 0x40084,
@@ -1664,36 +2442,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
                0x40280, 0x40280,
                0x40304, 0x40304,
                0x40330, 0x4033c,
-               0x41304, 0x413dc,
-               0x41400, 0x4141c,
+               0x41304, 0x413c8,
+               0x413d0, 0x413dc,
+               0x413f0, 0x413f0,
+               0x41400, 0x4140c,
+               0x41414, 0x4141c,
                0x41480, 0x414d0,
                0x44000, 0x4407c,
-               0x440c0, 0x4427c,
-               0x442c0, 0x4447c,
-               0x444c0, 0x4467c,
-               0x446c0, 0x4487c,
-               0x448c0, 0x44a7c,
-               0x44ac0, 0x44c7c,
-               0x44cc0, 0x44e7c,
-               0x44ec0, 0x4507c,
-               0x450c0, 0x451fc,
-               0x45800, 0x45868,
+               0x440c0, 0x441ac,
+               0x441b4, 0x4427c,
+               0x442c0, 0x443ac,
+               0x443b4, 0x4447c,
+               0x444c0, 0x445ac,
+               0x445b4, 0x4467c,
+               0x446c0, 0x447ac,
+               0x447b4, 0x4487c,
+               0x448c0, 0x449ac,
+               0x449b4, 0x44a7c,
+               0x44ac0, 0x44bac,
+               0x44bb4, 0x44c7c,
+               0x44cc0, 0x44dac,
+               0x44db4, 0x44e7c,
+               0x44ec0, 0x44fac,
+               0x44fb4, 0x4507c,
+               0x450c0, 0x451ac,
+               0x451b4, 0x451fc,
+               0x45800, 0x45804,
+               0x45810, 0x45830,
+               0x45840, 0x45860,
+               0x45868, 0x45868,
                0x45880, 0x45884,
                0x458a0, 0x458b0,
-               0x45a00, 0x45a68,
+               0x45a00, 0x45a04,
+               0x45a10, 0x45a30,
+               0x45a40, 0x45a60,
+               0x45a68, 0x45a68,
                0x45a80, 0x45a84,
                0x45aa0, 0x45ab0,
                0x460c0, 0x460e4,
-               0x47000, 0x4708c,
+               0x47000, 0x4703c,
+               0x47044, 0x4708c,
                0x47200, 0x47250,
-               0x47400, 0x47420,
+               0x47400, 0x47408,
+               0x47414, 0x47420,
                0x47600, 0x47618,
-               0x47800, 0x4782c,
-               0x50000, 0x500cc,
+               0x47800, 0x47814,
+               0x47820, 0x4782c,
+               0x50000, 0x50084,
+               0x50090, 0x500cc,
+               0x50300, 0x50384,
                0x50400, 0x50400,
-               0x50800, 0x508cc,
+               0x50800, 0x50884,
+               0x50890, 0x508cc,
+               0x50b00, 0x50b84,
                0x50c00, 0x50c00,
-               0x51000, 0x510b0,
+               0x51000, 0x51020,
+               0x51028, 0x510b0,
                0x51300, 0x51324,
        };
 
@@ -2177,11 +2981,15 @@ int t4_get_exprom_version(struct adapter *adap, u32 *vers)
  */
 int t4_check_fw_version(struct adapter *adap)
 {
-       int ret, major, minor, micro;
+       int i, ret, major, minor, micro;
        int exp_major, exp_minor, exp_micro;
        unsigned int chip_version = CHELSIO_CHIP_VERSION(adap->params.chip);
 
        ret = t4_get_fw_version(adap, &adap->params.fw_vers);
+       /* Try multiple times before returning error */
+       for (i = 0; (ret == -EBUSY || ret == -EAGAIN) && i < 3; i++)
+               ret = t4_get_fw_version(adap, &adap->params.fw_vers);
+
        if (ret)
                return ret;
 
index 640369df8b3a3cb155eb35c9d7a08009d0716da2..13708fde1668be1c8f5c7d6e830a354a71de7620 100644 (file)
@@ -263,4 +263,9 @@ enum {
 #undef FLASH_START
 #undef FLASH_MAX_SIZE
 
+#define SGE_TIMESTAMP_S 0
+#define SGE_TIMESTAMP_M 0xfffffffffffffffULL
+#define SGE_TIMESTAMP_V(x) ((__u64)(x) << SGE_TIMESTAMP_S)
+#define SGE_TIMESTAMP_G(x) (((__u64)(x) >> SGE_TIMESTAMP_S) & SGE_TIMESTAMP_M)
+
 #endif /* __T4_HW_H */
index b2b5e5bbe04c5f3b307a79ba9371bd017d9b1147..0cfa5d72cafd4027a1f06593bfc3f63c0703ba49 100644 (file)
@@ -56,7 +56,7 @@
  * Generic information about the driver.
  */
 #define DRV_VERSION "2.0.0-ko"
-#define DRV_DESC "Chelsio T4/T5 Virtual Function (VF) Network Driver"
+#define DRV_DESC "Chelsio T4/T5/T6 Virtual Function (VF) Network Driver"
 
 /*
  * Module Parameters.
index 8b53f7d4bebf33075f7f891bd3ceafc717fb6208..1671fa3332c2d3011db8c308a17ea5a9bf8b3fb2 100644 (file)
@@ -50,6 +50,7 @@ struct enic_msix_entry {
        char devname[IFNAMSIZ];
        irqreturn_t (*isr)(int, void *);
        void *devid;
+       cpumask_var_t affinity_mask;
 };
 
 /* Store only the lower range.  Higher range is given by fw. */
@@ -143,6 +144,7 @@ struct enic {
        struct vnic_dev *vdev;
        struct timer_list notify_timer;
        struct work_struct reset;
+       struct work_struct tx_hang_reset;
        struct work_struct change_mtu_work;
        struct msix_entry msix_entry[ENIC_INTR_MAX];
        struct enic_msix_entry msix[ENIC_INTR_MAX];
@@ -262,6 +264,32 @@ static inline unsigned int enic_msix_notify_intr(struct enic *enic)
        return enic->rq_count + enic->wq_count + 1;
 }
 
+static inline bool enic_is_err_intr(struct enic *enic, int intr)
+{
+       switch (vnic_dev_get_intr_mode(enic->vdev)) {
+       case VNIC_DEV_INTR_MODE_INTX:
+               return intr == enic_legacy_err_intr();
+       case VNIC_DEV_INTR_MODE_MSIX:
+               return intr == enic_msix_err_intr(enic);
+       case VNIC_DEV_INTR_MODE_MSI:
+       default:
+               return false;
+       }
+}
+
+static inline bool enic_is_notify_intr(struct enic *enic, int intr)
+{
+       switch (vnic_dev_get_intr_mode(enic->vdev)) {
+       case VNIC_DEV_INTR_MODE_INTX:
+               return intr == enic_legacy_notify_intr();
+       case VNIC_DEV_INTR_MODE_MSIX:
+               return intr == enic_msix_notify_intr(enic);
+       case VNIC_DEV_INTR_MODE_MSI:
+       default:
+               return false;
+       }
+}
+
 static inline int enic_dma_map_check(struct enic *enic, dma_addr_t dma_addr)
 {
        if (unlikely(pci_dma_mapping_error(enic->pdev, dma_addr))) {
index 3352d027ab895c59195ef79bd322112964c21c75..b36643ef0593a1679dc0e0605711bb2846cfd58d 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/prefetch.h>
 #include <net/ip6_checksum.h>
 #include <linux/ktime.h>
+#include <linux/numa.h>
 #ifdef CONFIG_RFS_ACCEL
 #include <linux/cpu_rmap.h>
 #endif
@@ -112,6 +113,71 @@ static struct enic_intr_mod_range mod_range[ENIC_MAX_LINK_SPEEDS] = {
        {3,  6}, /* 10 - 40 Gbps */
 };
 
+static void enic_init_affinity_hint(struct enic *enic)
+{
+       int numa_node = dev_to_node(&enic->pdev->dev);
+       int i;
+
+       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_empty(enic->msix[i].affinity_mask)))
+                       continue;
+               if (zalloc_cpumask_var(&enic->msix[i].affinity_mask,
+                                      GFP_KERNEL))
+                       cpumask_set_cpu(cpumask_local_spread(i, numa_node),
+                                       enic->msix[i].affinity_mask);
+       }
+}
+
+static void enic_free_affinity_hint(struct enic *enic)
+{
+       int i;
+
+       for (i = 0; i < enic->intr_count; i++) {
+               if (enic_is_err_intr(enic, i) || enic_is_notify_intr(enic, i))
+                       continue;
+               free_cpumask_var(enic->msix[i].affinity_mask);
+       }
+}
+
+static void enic_set_affinity_hint(struct enic *enic)
+{
+       int i;
+       int err;
+
+       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_empty(enic->msix[i].affinity_mask))
+                       continue;
+               err = irq_set_affinity_hint(enic->msix_entry[i].vector,
+                                           enic->msix[i].affinity_mask);
+               if (err)
+                       netdev_warn(enic->netdev, "irq_set_affinity_hint failed, err %d\n",
+                                   err);
+       }
+
+       for (i = 0; i < enic->wq_count; i++) {
+               int wq_intr = enic_msix_wq_intr(enic, i);
+
+               if (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,
+                                           i);
+       }
+}
+
+static void enic_unset_affinity_hint(struct enic *enic)
+{
+       int i;
+
+       for (i = 0; i < enic->intr_count; i++)
+               irq_set_affinity_hint(enic->msix_entry[i].vector, NULL);
+}
+
 int enic_is_dynamic(struct enic *enic)
 {
        return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_DYN;
@@ -178,13 +244,15 @@ static int enic_wq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc,
        return 0;
 }
 
-static void enic_log_q_error(struct enic *enic)
+static bool enic_log_q_error(struct enic *enic)
 {
        unsigned int i;
        u32 error_status;
+       bool err = false;
 
        for (i = 0; i < enic->wq_count; i++) {
                error_status = vnic_wq_error_status(&enic->wq[i]);
+               err |= error_status;
                if (error_status)
                        netdev_err(enic->netdev, "WQ[%d] error_status %d\n",
                                i, error_status);
@@ -192,10 +260,13 @@ static void enic_log_q_error(struct enic *enic)
 
        for (i = 0; i < enic->rq_count; i++) {
                error_status = vnic_rq_error_status(&enic->rq[i]);
+               err |= error_status;
                if (error_status)
                        netdev_err(enic->netdev, "RQ[%d] error_status %d\n",
                                i, error_status);
        }
+
+       return err;
 }
 
 static void enic_msglvl_check(struct enic *enic)
@@ -333,10 +404,9 @@ static irqreturn_t enic_isr_msix_err(int irq, void *data)
 
        vnic_intr_return_all_credits(&enic->intr[intr]);
 
-       enic_log_q_error(enic);
-
-       /* schedule recovery from WQ/RQ error */
-       schedule_work(&enic->reset);
+       if (enic_log_q_error(enic))
+               /* schedule recovery from WQ/RQ error */
+               schedule_work(&enic->reset);
 
        return IRQ_HANDLED;
 }
@@ -804,7 +874,7 @@ static void enic_set_rx_mode(struct net_device *netdev)
 static void enic_tx_timeout(struct net_device *netdev)
 {
        struct enic *enic = netdev_priv(netdev);
-       schedule_work(&enic->reset);
+       schedule_work(&enic->tx_hang_reset);
 }
 
 static int enic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
@@ -1645,6 +1715,8 @@ static int enic_open(struct net_device *netdev)
                netdev_err(netdev, "Unable to request irq.\n");
                return err;
        }
+       enic_init_affinity_hint(enic);
+       enic_set_affinity_hint(enic);
 
        err = enic_dev_notify_set(enic);
        if (err) {
@@ -1697,6 +1769,7 @@ err_out_free_rq:
                vnic_rq_clean(&enic->rq[i], enic_free_rq_buf);
        enic_dev_notify_unset(enic);
 err_out_free_intr:
+       enic_unset_affinity_hint(enic);
        enic_free_intr(enic);
 
        return err;
@@ -1750,6 +1823,7 @@ static int enic_stop(struct net_device *netdev)
        }
 
        enic_dev_notify_unset(enic);
+       enic_unset_affinity_hint(enic);
        enic_free_intr(enic);
 
        for (i = 0; i < enic->wq_count; i++)
@@ -1924,6 +1998,19 @@ static int enic_dev_open(struct enic *enic)
        return err;
 }
 
+static int enic_dev_soft_reset(struct enic *enic)
+{
+       int err;
+
+       err = enic_dev_wait(enic->vdev, vnic_dev_soft_reset,
+                           vnic_dev_soft_reset_done, 0);
+       if (err)
+               netdev_err(enic->netdev, "vNIC soft reset failed, err %d\n",
+                          err);
+
+       return err;
+}
+
 static int enic_dev_hang_reset(struct enic *enic)
 {
        int err;
@@ -2059,6 +2146,26 @@ static void enic_reset(struct work_struct *work)
 
        rtnl_lock();
 
+       spin_lock(&enic->enic_api_lock);
+       enic_stop(enic->netdev);
+       enic_dev_soft_reset(enic);
+       enic_reset_addr_lists(enic);
+       enic_init_vnic_resources(enic);
+       enic_set_rss_nic_cfg(enic);
+       enic_dev_set_ig_vlan_rewrite_mode(enic);
+       enic_open(enic->netdev);
+       spin_unlock(&enic->enic_api_lock);
+       call_netdevice_notifiers(NETDEV_REBOOT, enic->netdev);
+
+       rtnl_unlock();
+}
+
+static void enic_tx_hang_reset(struct work_struct *work)
+{
+       struct enic *enic = container_of(work, struct enic, tx_hang_reset);
+
+       rtnl_lock();
+
        spin_lock(&enic->enic_api_lock);
        enic_dev_hang_notify(enic);
        enic_stop(enic->netdev);
@@ -2272,6 +2379,7 @@ static void enic_dev_deinit(struct enic *enic)
 
        enic_free_vnic_resources(enic);
        enic_clear_intr_mode(enic);
+       enic_free_affinity_hint(enic);
 }
 
 static void enic_kdump_kernel_config(struct enic *enic)
@@ -2367,6 +2475,7 @@ static int enic_dev_init(struct enic *enic)
        return 0;
 
 err_out_free_vnic_resources:
+       enic_free_affinity_hint(enic);
        enic_clear_intr_mode(enic);
        enic_free_vnic_resources(enic);
 
@@ -2583,6 +2692,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        enic_set_rx_coal_setting(enic);
        INIT_WORK(&enic->reset, enic_reset);
+       INIT_WORK(&enic->tx_hang_reset, enic_tx_hang_reset);
        INIT_WORK(&enic->change_mtu_work, enic_change_mtu_work);
 
        for (i = 0; i < enic->wq_count; i++)
index a3badefaf360a13c2bfe0a3e86c7488ee8f8a338..1ffd1050860bb5cde576fd291e0651b351d578ff 100644 (file)
@@ -659,14 +659,14 @@ int vnic_dev_open_done(struct vnic_dev *vdev, int *done)
        return 0;
 }
 
-static int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg)
+int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg)
 {
        u64 a0 = (u32)arg, a1 = 0;
        int wait = 1000;
        return vnic_dev_cmd(vdev, CMD_SOFT_RESET, &a0, &a1, wait);
 }
 
-static int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done)
+int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done)
 {
        u64 a0 = 0, a1 = 0;
        int wait = 1000;
index b013b6a78e8772a9bdc077e0c0bd136092307193..54156c48442484dcdcc78a027eac0173ab1cd62f 100644 (file)
@@ -155,7 +155,9 @@ int vnic_dev_deinit(struct vnic_dev *vdev);
 void vnic_dev_intr_coal_timer_info_default(struct vnic_dev *vdev);
 int vnic_dev_intr_coal_timer_info(struct vnic_dev *vdev);
 int vnic_dev_hang_reset(struct vnic_dev *vdev, int arg);
+int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg);
 int vnic_dev_hang_reset_done(struct vnic_dev *vdev, int *done);
+int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done);
 void vnic_dev_set_intr_mode(struct vnic_dev *vdev,
        enum vnic_dev_intr_mode intr_mode);
 enum vnic_dev_intr_mode vnic_dev_get_intr_mode(struct vnic_dev *vdev);
index a02ecc4f90022e70026e648e2ae3ad1cfc2b53e3..cadcee645f74e8fe45a6f34e2b7e0bf949ea14e7 100644 (file)
@@ -1597,7 +1597,6 @@ static void de_get_drvinfo (struct net_device *dev,struct ethtool_drvinfo *info)
        strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
        strlcpy(info->version, DRV_VERSION, sizeof(info->version));
        strlcpy(info->bus_info, pci_name(de->pdev), sizeof(info->bus_info));
-       info->eedump_len = DE_EEPROM_SIZE;
 }
 
 static int de_get_regs_len(struct net_device *dev)
index 2c9ed1710ba6f4c16d8a3d800602b6d2337abb59..f4cb8e425853a3fd85151d3b6df5e7651b95f81a 100644 (file)
@@ -234,9 +234,6 @@ static void be_get_drvinfo(struct net_device *netdev,
 
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->testinfo_len = 0;
-       drvinfo->regdump_len = 0;
-       drvinfo->eedump_len = 0;
 }
 
 static u32 lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
index a2c96fd883938b6d15aa1335e4169b290f6bf241..ff665493ca976bb7662b7f49601671b875e200ae 100644 (file)
@@ -201,6 +201,7 @@ struct ethoc {
        void __iomem *membase;
        int dma_alloc;
        resource_size_t io_region_size;
+       bool big_endian;
 
        unsigned int num_bd;
        unsigned int num_tx;
@@ -236,12 +237,18 @@ struct ethoc_bd {
 
 static inline u32 ethoc_read(struct ethoc *dev, loff_t offset)
 {
-       return ioread32(dev->iobase + offset);
+       if (dev->big_endian)
+               return ioread32be(dev->iobase + offset);
+       else
+               return ioread32(dev->iobase + offset);
 }
 
 static inline void ethoc_write(struct ethoc *dev, loff_t offset, u32 data)
 {
-       iowrite32(data, dev->iobase + offset);
+       if (dev->big_endian)
+               iowrite32be(data, dev->iobase + offset);
+       else
+               iowrite32(data, dev->iobase + offset);
 }
 
 static inline void ethoc_read_bd(struct ethoc *dev, int index,
@@ -1106,6 +1113,9 @@ static int ethoc_probe(struct platform_device *pdev)
                priv->dma_alloc = buffer_size;
        }
 
+       priv->big_endian = pdata ? pdata->big_endian :
+               of_device_is_big_endian(pdev->dev.of_node);
+
        /* calculate the number of TX/RX buffers, maximum 128 supported */
        num_bd = min_t(unsigned int,
                128, (netdev->mem_end - netdev->mem_start + 1) / ETHOC_BUFSIZ);
index dd4ca39d5d8f62cf96190576224799d00d17b2ff..b2a32209ffbfc2806d725d7924b470d2752e03d5 100644 (file)
@@ -3070,7 +3070,6 @@ static void fec_poll_controller(struct net_device *dev)
 }
 #endif
 
-#define FEATURES_NEED_QUIESCE NETIF_F_RXCSUM
 static inline void fec_enet_set_netdev_features(struct net_device *netdev,
        netdev_features_t features)
 {
@@ -3094,7 +3093,7 @@ static int fec_set_features(struct net_device *netdev,
        struct fec_enet_private *fep = netdev_priv(netdev);
        netdev_features_t changed = features ^ netdev->features;
 
-       if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
+       if (netif_running(netdev) && changed & NETIF_F_RXCSUM) {
                napi_disable(&fep->napi);
                netif_tx_lock_bh(netdev);
                fec_stop(netdev);
@@ -3262,7 +3261,7 @@ static void fec_reset_phy(struct platform_device *pdev)
                return;
        }
        msleep(msec);
-       gpio_set_value(phy_reset, 1);
+       gpio_set_value_cansleep(phy_reset, 1);
 }
 #else /* CONFIG_OF */
 static void fec_reset_phy(struct platform_device *pdev)
index 1543cf0e8ef647c92f1748e474833cf55300a490..f9e74461bdc0b089c64b751571e8a999a050433b 100644 (file)
@@ -112,9 +112,8 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable)
        unsigned long flags;
        u32 val, tempval;
        int inc;
-       struct timespec ts;
+       struct timespec64 ts;
        u64 ns;
-       u32 remainder;
        val = 0;
 
        if (!(fep->hwts_tx_en || fep->hwts_rx_en)) {
@@ -163,8 +162,7 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable)
                tempval = readl(fep->hwp + FEC_ATIME);
                /* Convert the ptp local counter to 1588 timestamp */
                ns = timecounter_cyc2time(&fep->tc, tempval);
-               ts.tv_sec = div_u64_rem(ns, 1000000000ULL, &remainder);
-               ts.tv_nsec = remainder;
+               ts = ns_to_timespec64(ns);
 
                /* The tempval is  less than 3 seconds, and  so val is less than
                 * 4 seconds. No overflow for 32bit calculation.
index 710715fcb23dea7765ce3fc579c88fcd29ecb0da..3e6b9b437497dba9431fdedd3cf03262c0af3b0f 100644 (file)
 
 #include "gianfar.h"
 
-#define TX_TIMEOUT      (1*HZ)
+#define TX_TIMEOUT      (5*HZ)
 
 const char gfar_driver_version[] = "2.0";
 
@@ -341,7 +341,7 @@ static void gfar_rx_offload_en(struct gfar_private *priv)
        if (priv->ndev->features & (NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_RX))
                priv->uses_rxfcb = 1;
 
-       if (priv->hwts_rx_en)
+       if (priv->hwts_rx_en || priv->rx_filer_enable)
                priv->uses_rxfcb = 1;
 }
 
@@ -351,7 +351,7 @@ static void gfar_mac_rx_config(struct gfar_private *priv)
        u32 rctrl = 0;
 
        if (priv->rx_filer_enable) {
-               rctrl |= RCTRL_FILREN;
+               rctrl |= RCTRL_FILREN | RCTRL_PRSDEP_INIT;
                /* Program the RIR0 reg with the required distribution */
                if (priv->poll_mode == GFAR_SQ_POLLING)
                        gfar_write(&regs->rir0, DEFAULT_2RXQ_RIR0);
@@ -907,6 +907,9 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
        if (of_find_property(np, "fsl,magic-packet", NULL))
                priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
 
+       if (of_get_property(np, "fsl,wake-on-filer", NULL))
+               priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER;
+
        priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
 
        /* In the case of a fixed PHY, the DT node associated
@@ -1415,8 +1418,14 @@ static int gfar_probe(struct platform_device *ofdev)
                goto register_fail;
        }
 
-       device_set_wakeup_capable(&dev->dev, priv->device_flags &
-                                 FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
+       if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET)
+               priv->wol_supported |= GFAR_WOL_MAGIC;
+
+       if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER) &&
+           priv->rx_filer_enable)
+               priv->wol_supported |= GFAR_WOL_FILER_UCAST;
+
+       device_set_wakeup_capable(&ofdev->dev, priv->wol_supported);
 
        /* fill out IRQ number and name fields */
        for (i = 0; i < priv->num_grps; i++) {
@@ -1479,15 +1488,122 @@ static int gfar_remove(struct platform_device *ofdev)
 
 #ifdef CONFIG_PM
 
+static void __gfar_filer_disable(struct gfar_private *priv)
+{
+       struct gfar __iomem *regs = priv->gfargrp[0].regs;
+       u32 temp;
+
+       temp = gfar_read(&regs->rctrl);
+       temp &= ~(RCTRL_FILREN | RCTRL_PRSDEP_INIT);
+       gfar_write(&regs->rctrl, temp);
+}
+
+static void __gfar_filer_enable(struct gfar_private *priv)
+{
+       struct gfar __iomem *regs = priv->gfargrp[0].regs;
+       u32 temp;
+
+       temp = gfar_read(&regs->rctrl);
+       temp |= RCTRL_FILREN | RCTRL_PRSDEP_INIT;
+       gfar_write(&regs->rctrl, temp);
+}
+
+/* Filer rules implementing wol capabilities */
+static void gfar_filer_config_wol(struct gfar_private *priv)
+{
+       unsigned int i;
+       u32 rqfcr;
+
+       __gfar_filer_disable(priv);
+
+       /* clear the filer table, reject any packet by default */
+       rqfcr = RQFCR_RJE | RQFCR_CMP_MATCH;
+       for (i = 0; i <= MAX_FILER_IDX; i++)
+               gfar_write_filer(priv, i, rqfcr, 0);
+
+       i = 0;
+       if (priv->wol_opts & GFAR_WOL_FILER_UCAST) {
+               /* unicast packet, accept it */
+               struct net_device *ndev = priv->ndev;
+               /* get the default rx queue index */
+               u8 qindex = (u8)priv->gfargrp[0].rx_queue->qindex;
+               u32 dest_mac_addr = (ndev->dev_addr[0] << 16) |
+                                   (ndev->dev_addr[1] << 8) |
+                                    ndev->dev_addr[2];
+
+               rqfcr = (qindex << 10) | RQFCR_AND |
+                       RQFCR_CMP_EXACT | RQFCR_PID_DAH;
+
+               gfar_write_filer(priv, i++, rqfcr, dest_mac_addr);
+
+               dest_mac_addr = (ndev->dev_addr[3] << 16) |
+                               (ndev->dev_addr[4] << 8) |
+                                ndev->dev_addr[5];
+               rqfcr = (qindex << 10) | RQFCR_GPI |
+                       RQFCR_CMP_EXACT | RQFCR_PID_DAL;
+               gfar_write_filer(priv, i++, rqfcr, dest_mac_addr);
+       }
+
+       __gfar_filer_enable(priv);
+}
+
+static void gfar_filer_restore_table(struct gfar_private *priv)
+{
+       u32 rqfcr, rqfpr;
+       unsigned int i;
+
+       __gfar_filer_disable(priv);
+
+       for (i = 0; i <= MAX_FILER_IDX; i++) {
+               rqfcr = priv->ftp_rqfcr[i];
+               rqfpr = priv->ftp_rqfpr[i];
+               gfar_write_filer(priv, i, rqfcr, rqfpr);
+       }
+
+       __gfar_filer_enable(priv);
+}
+
+/* gfar_start() for Rx only and with the FGPI filer interrupt enabled */
+static void gfar_start_wol_filer(struct gfar_private *priv)
+{
+       struct gfar __iomem *regs = priv->gfargrp[0].regs;
+       u32 tempval;
+       int i = 0;
+
+       /* Enable Rx hw queues */
+       gfar_write(&regs->rqueue, priv->rqueue);
+
+       /* Initialize DMACTRL to have WWR and WOP */
+       tempval = gfar_read(&regs->dmactrl);
+       tempval |= DMACTRL_INIT_SETTINGS;
+       gfar_write(&regs->dmactrl, tempval);
+
+       /* Make sure we aren't stopped */
+       tempval = gfar_read(&regs->dmactrl);
+       tempval &= ~DMACTRL_GRS;
+       gfar_write(&regs->dmactrl, tempval);
+
+       for (i = 0; i < priv->num_grps; i++) {
+               regs = priv->gfargrp[i].regs;
+               /* Clear RHLT, so that the DMA starts polling now */
+               gfar_write(&regs->rstat, priv->gfargrp[i].rstat);
+               /* enable the Filer General Purpose Interrupt */
+               gfar_write(&regs->imask, IMASK_FGPI);
+       }
+
+       /* Enable Rx DMA */
+       tempval = gfar_read(&regs->maccfg1);
+       tempval |= MACCFG1_RX_EN;
+       gfar_write(&regs->maccfg1, tempval);
+}
+
 static int gfar_suspend(struct device *dev)
 {
        struct gfar_private *priv = dev_get_drvdata(dev);
        struct net_device *ndev = priv->ndev;
        struct gfar __iomem *regs = priv->gfargrp[0].regs;
        u32 tempval;
-       int magic_packet = priv->wol_en &&
-                          (priv->device_flags &
-                           FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
+       u16 wol = priv->wol_opts;
 
        if (!netif_running(ndev))
                return 0;
@@ -1499,7 +1615,7 @@ static int gfar_suspend(struct device *dev)
 
        gfar_halt(priv);
 
-       if (magic_packet) {
+       if (wol & GFAR_WOL_MAGIC) {
                /* Enable interrupt on Magic Packet */
                gfar_write(&regs->imask, IMASK_MAG);
 
@@ -1513,6 +1629,10 @@ static int gfar_suspend(struct device *dev)
                tempval |= MACCFG1_RX_EN;
                gfar_write(&regs->maccfg1, tempval);
 
+       } else if (wol & GFAR_WOL_FILER_UCAST) {
+               gfar_filer_config_wol(priv);
+               gfar_start_wol_filer(priv);
+
        } else {
                phy_stop(priv->phydev);
        }
@@ -1526,18 +1646,22 @@ static int gfar_resume(struct device *dev)
        struct net_device *ndev = priv->ndev;
        struct gfar __iomem *regs = priv->gfargrp[0].regs;
        u32 tempval;
-       int magic_packet = priv->wol_en &&
-                          (priv->device_flags &
-                           FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
+       u16 wol = priv->wol_opts;
 
        if (!netif_running(ndev))
                return 0;
 
-       if (magic_packet) {
+       if (wol & GFAR_WOL_MAGIC) {
                /* Disable Magic Packet mode */
                tempval = gfar_read(&regs->maccfg2);
                tempval &= ~MACCFG2_MPEN;
                gfar_write(&regs->maccfg2, tempval);
+
+       } else if (wol & GFAR_WOL_FILER_UCAST) {
+               /* need to stop rx only, tx is already down */
+               gfar_halt(priv);
+               gfar_filer_restore_table(priv);
+
        } else {
                phy_start(priv->phydev);
        }
@@ -1998,6 +2122,8 @@ static int register_grp_irqs(struct gfar_priv_grp *grp)
                                  gfar_irq(grp, RX)->irq);
                        goto rx_irq_fail;
                }
+               enable_irq_wake(gfar_irq(grp, RX)->irq);
+
        } else {
                err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt, 0,
                                  gfar_irq(grp, TX)->name, grp);
@@ -2743,7 +2869,14 @@ irqreturn_t gfar_receive(int irq, void *grp_id)
 {
        struct gfar_priv_grp *grp = (struct gfar_priv_grp *)grp_id;
        unsigned long flags;
-       u32 imask;
+       u32 imask, ievent;
+
+       ievent = gfar_read(&grp->regs->ievent);
+
+       if (unlikely(ievent & IEVENT_FGPI)) {
+               gfar_write(&grp->regs->ievent, IEVENT_FGPI);
+               return IRQ_HANDLED;
+       }
 
        if (likely(napi_schedule_prep(&grp->napi_rx))) {
                spin_lock_irqsave(&grp->grplock, flags);
@@ -3462,11 +3595,9 @@ static irqreturn_t gfar_error(int irq, void *grp_id)
                netif_dbg(priv, tx_err, dev, "Transmit Error\n");
        }
        if (events & IEVENT_BSY) {
-               dev->stats.rx_errors++;
+               dev->stats.rx_over_errors++;
                atomic64_inc(&priv->extra_stats.rx_bsy);
 
-               gfar_receive(irq, grp_id);
-
                netif_dbg(priv, rx_err, dev, "busy error (rstat: %x)\n",
                          gfar_read(&regs->rstat));
        }
index 8c1994856e93823174d29c08ed8ae691c1530120..f266b20f9ef5be53e6c55f2579484bb72d8794a1 100644 (file)
@@ -340,6 +340,7 @@ extern const char gfar_driver_version[];
 #define IEVENT_MAG             0x00000800
 #define IEVENT_GRSC            0x00000100
 #define IEVENT_RXF0            0x00000080
+#define IEVENT_FGPI            0x00000010
 #define IEVENT_FIR             0x00000008
 #define IEVENT_FIQ             0x00000004
 #define IEVENT_DPE             0x00000002
@@ -372,6 +373,7 @@ extern const char gfar_driver_version[];
 #define IMASK_MAG              0x00000800
 #define IMASK_GRSC              0x00000100
 #define IMASK_RXFEN0           0x00000080
+#define IMASK_FGPI             0x00000010
 #define IMASK_FIR              0x00000008
 #define IMASK_FIQ              0x00000004
 #define IMASK_DPE              0x00000002
@@ -540,6 +542,9 @@ extern const char gfar_driver_version[];
 
 #define GFAR_INT_NAME_MAX      (IFNAMSIZ + 6)  /* '_g#_xx' */
 
+#define GFAR_WOL_MAGIC         0x00000001
+#define GFAR_WOL_FILER_UCAST   0x00000002
+
 struct txbd8
 {
        union {
@@ -917,6 +922,7 @@ struct gfar {
 #define FSL_GIANFAR_DEV_HAS_BD_STASHING                0x00000200
 #define FSL_GIANFAR_DEV_HAS_BUF_STASHING       0x00000400
 #define FSL_GIANFAR_DEV_HAS_TIMER              0x00000800
+#define FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER      0x00001000
 
 #if (MAXGROUPS == 2)
 #define DEFAULT_MAPPING        0xAA
@@ -1161,8 +1167,6 @@ struct gfar_private {
                extended_hash:1,
                bd_stash_en:1,
                rx_filer_enable:1,
-               /* Wake-on-LAN enabled */
-               wol_en:1,
                /* Enable priorty based Tx scheduling in Hw */
                prio_sched_en:1,
                /* Flow control flags */
@@ -1191,6 +1195,10 @@ struct gfar_private {
        u32 __iomem *hash_regs[16];
        int hash_width;
 
+       /* wake-on-lan settings */
+       u16 wol_opts;
+       u16 wol_supported;
+
        /*Filer table*/
        unsigned int ftp_rqfpr[MAX_FILER_IDX + 1];
        unsigned int ftp_rqfcr[MAX_FILER_IDX + 1];
index 6bdc89179b72d6487d031ecda8551453b9268cd4..4b0ee855edd72fad9b748c2d7e41e30fb2aae9b1 100644 (file)
@@ -182,8 +182,6 @@ static void gfar_gdrvinfo(struct net_device *dev,
                sizeof(drvinfo->version));
        strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
        strlcpy(drvinfo->bus_info, "N/A", sizeof(drvinfo->bus_info));
-       drvinfo->regdump_len = 0;
-       drvinfo->eedump_len = 0;
 }
 
 
@@ -644,28 +642,49 @@ static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 {
        struct gfar_private *priv = netdev_priv(dev);
 
-       if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) {
-               wol->supported = WAKE_MAGIC;
-               wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0;
-       } else {
-               wol->supported = wol->wolopts = 0;
-       }
+       wol->supported = 0;
+       wol->wolopts = 0;
+
+       if (priv->wol_supported & GFAR_WOL_MAGIC)
+               wol->supported |= WAKE_MAGIC;
+
+       if (priv->wol_supported & GFAR_WOL_FILER_UCAST)
+               wol->supported |= WAKE_UCAST;
+
+       if (priv->wol_opts & GFAR_WOL_MAGIC)
+               wol->wolopts |= WAKE_MAGIC;
+
+       if (priv->wol_opts & GFAR_WOL_FILER_UCAST)
+               wol->wolopts |= WAKE_UCAST;
 }
 
 static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 {
        struct gfar_private *priv = netdev_priv(dev);
+       u16 wol_opts = 0;
+       int err;
 
-       if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
-           wol->wolopts != 0)
+       if (!priv->wol_supported && wol->wolopts)
                return -EINVAL;
 
-       if (wol->wolopts & ~WAKE_MAGIC)
+       if (wol->wolopts & ~(WAKE_MAGIC | WAKE_UCAST))
                return -EINVAL;
 
-       device_set_wakeup_enable(&dev->dev, wol->wolopts & WAKE_MAGIC);
+       if (wol->wolopts & WAKE_MAGIC) {
+               wol_opts |= GFAR_WOL_MAGIC;
+       } else {
+               if (wol->wolopts & WAKE_UCAST)
+                       wol_opts |= GFAR_WOL_FILER_UCAST;
+       }
+
+       wol_opts &= priv->wol_supported;
+       priv->wol_opts = 0;
+
+       err = device_set_wakeup_enable(priv->dev, wol_opts);
+       if (err)
+               return err;
 
-       priv->wol_en = !!device_may_wakeup(&dev->dev);
+       priv->wol_opts = wol_opts;
 
        return 0;
 }
@@ -676,14 +695,14 @@ static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow)
        u32 fcr = 0x0, fpr = FPR_FILER_MASK;
 
        if (ethflow & RXH_L2DA) {
-               fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH |
+               fcr = RQFCR_PID_DAH | RQFCR_CMP_NOMATCH |
                      RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
                priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
                priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
                gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
                priv->cur_filer_idx = priv->cur_filer_idx - 1;
 
-               fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH |
+               fcr = RQFCR_PID_DAL | RQFCR_CMP_NOMATCH |
                      RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
                priv->ftp_rqfpr[priv->cur_filer_idx] = fpr;
                priv->ftp_rqfcr[priv->cur_filer_idx] = fcr;
index cc83350d56ba1c05aa7eb619b607635f76de6400..89714f5e0dfc57b2246affedf002e19da0d17c3c 100644 (file)
@@ -351,8 +351,6 @@ uec_get_drvinfo(struct net_device *netdev,
        strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version));
        strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
        strlcpy(drvinfo->bus_info, "QUICC ENGINE", sizeof(drvinfo->bus_info));
-       drvinfo->eedump_len = 0;
-       drvinfo->regdump_len = uec_get_regs_len(netdev);
 }
 
 #ifdef CONFIG_PM
index dead17b5d769ca9639b879b9ef48b4e4e3679d44..f250dec488fd2a2b908a0cb67bb2df61c53ac7ee 100644 (file)
@@ -5,7 +5,7 @@
 config NET_VENDOR_HISILICON
        bool "Hisilicon devices"
        default y
-       depends on ARM
+       depends on OF && (ARM || ARM64 || COMPILE_TEST)
        ---help---
          If you have a network (Ethernet) card belonging to this class, say Y.
 
@@ -24,11 +24,42 @@ config HIX5HD2_GMAC
 
 config HIP04_ETH
        tristate "HISILICON P04 Ethernet support"
-       select PHYLIB
        select MARVELL_PHY
        select MFD_SYSCON
+       select HNS_MDIO
        ---help---
          If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
          want to use the internal ethernet then you should answer Y to this.
 
+config HNS_MDIO
+       tristate
+       select PHYLIB
+       ---help---
+         This selects the HNS MDIO support. It is needed by HNS_DSAF to access
+         the PHY
+
+config HNS
+       tristate "Hisilicon Network Subsystem Support (Framework)"
+       ---help---
+         This selects the framework support for Hisilicon Network Subsystem. It
+         is needed by any driver which provides HNS acceleration engine or make
+         use of the engine
+
+config HNS_DSAF
+       tristate "Hisilicon HNS DSAF device Support"
+       select HNS
+       select HNS_MDIO
+       ---help---
+         This selects the DSAF (Distributed System Area Frabric) network
+         acceleration engine support. The engine is used in Hisilicon hip05,
+         Hi1610 and further ICT SoC
+
+config HNS_ENET
+       tristate "Hisilicon HNS Ethernet Device Support"
+       select PHYLIB
+       select HNS
+       ---help---
+         This selects the general ethernet driver for HNS.  This module make
+         use of any HNS AE driver, such as HNS_DSAF
+
 endif # NET_VENDOR_HISILICON
index 6c14540a4dc5e18c6049aaa24e6e8184af99ddd7..390b71fb300062f18ffe0621fa20ded653aef11e 100644 (file)
@@ -3,4 +3,6 @@
 #
 
 obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o
-obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o hip04_eth.o
+obj-$(CONFIG_HIP04_ETH) += hip04_eth.o
+obj-$(CONFIG_HNS_MDIO) += hns_mdio.o
+obj-$(CONFIG_HNS) += hns/
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
deleted file mode 100644 (file)
index fca0a5b..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/* Copyright (c) 2014 Linaro Ltd.
- * Copyright (c) 2014 Hisilicon Limited.
- *
- * 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/module.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/of_mdio.h>
-#include <linux/delay.h>
-
-#define MDIO_CMD_REG           0x0
-#define MDIO_ADDR_REG          0x4
-#define MDIO_WDATA_REG         0x8
-#define MDIO_RDATA_REG         0xc
-#define MDIO_STA_REG           0x10
-
-#define MDIO_START             BIT(14)
-#define MDIO_R_VALID           BIT(1)
-#define MDIO_READ              (BIT(12) | BIT(11) | MDIO_START)
-#define MDIO_WRITE             (BIT(12) | BIT(10) | MDIO_START)
-
-struct hip04_mdio_priv {
-       void __iomem *base;
-};
-
-#define WAIT_TIMEOUT 10
-static int hip04_mdio_wait_ready(struct mii_bus *bus)
-{
-       struct hip04_mdio_priv *priv = bus->priv;
-       int i;
-
-       for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
-               if (i == WAIT_TIMEOUT)
-                       return -ETIMEDOUT;
-               msleep(20);
-       }
-
-       return 0;
-}
-
-static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
-{
-       struct hip04_mdio_priv *priv = bus->priv;
-       u32 val;
-       int ret;
-
-       ret = hip04_mdio_wait_ready(bus);
-       if (ret < 0)
-               goto out;
-
-       val = regnum | (mii_id << 5) | MDIO_READ;
-       writel_relaxed(val, priv->base + MDIO_CMD_REG);
-
-       ret = hip04_mdio_wait_ready(bus);
-       if (ret < 0)
-               goto out;
-
-       val = readl_relaxed(priv->base + MDIO_STA_REG);
-       if (val & MDIO_R_VALID) {
-               dev_err(bus->parent, "SMI bus read not valid\n");
-               ret = -ENODEV;
-               goto out;
-       }
-
-       val = readl_relaxed(priv->base + MDIO_RDATA_REG);
-       ret = val & 0xFFFF;
-out:
-       return ret;
-}
-
-static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
-                           int regnum, u16 value)
-{
-       struct hip04_mdio_priv *priv = bus->priv;
-       u32 val;
-       int ret;
-
-       ret = hip04_mdio_wait_ready(bus);
-       if (ret < 0)
-               goto out;
-
-       writel_relaxed(value, priv->base + MDIO_WDATA_REG);
-       val = regnum | (mii_id << 5) | MDIO_WRITE;
-       writel_relaxed(val, priv->base + MDIO_CMD_REG);
-out:
-       return ret;
-}
-
-static int hip04_mdio_reset(struct mii_bus *bus)
-{
-       int temp, i;
-
-       for (i = 0; i < PHY_MAX_ADDR; i++) {
-               hip04_mdio_write(bus, i, 22, 0);
-               temp = hip04_mdio_read(bus, i, MII_BMCR);
-               if (temp < 0)
-                       continue;
-
-               temp |= BMCR_RESET;
-               if (hip04_mdio_write(bus, i, MII_BMCR, temp) < 0)
-                       continue;
-       }
-
-       mdelay(500);
-       return 0;
-}
-
-static int hip04_mdio_probe(struct platform_device *pdev)
-{
-       struct resource *r;
-       struct mii_bus *bus;
-       struct hip04_mdio_priv *priv;
-       int ret;
-
-       bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
-       if (!bus) {
-               dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
-               return -ENOMEM;
-       }
-
-       bus->name = "hip04_mdio_bus";
-       bus->read = hip04_mdio_read;
-       bus->write = hip04_mdio_write;
-       bus->reset = hip04_mdio_reset;
-       snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
-       bus->parent = &pdev->dev;
-       priv = bus->priv;
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       priv->base = devm_ioremap_resource(&pdev->dev, r);
-       if (IS_ERR(priv->base)) {
-               ret = PTR_ERR(priv->base);
-               goto out_mdio;
-       }
-
-       ret = of_mdiobus_register(bus, pdev->dev.of_node);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
-               goto out_mdio;
-       }
-
-       platform_set_drvdata(pdev, bus);
-
-       return 0;
-
-out_mdio:
-       mdiobus_free(bus);
-       return ret;
-}
-
-static int hip04_mdio_remove(struct platform_device *pdev)
-{
-       struct mii_bus *bus = platform_get_drvdata(pdev);
-
-       mdiobus_unregister(bus);
-       mdiobus_free(bus);
-
-       return 0;
-}
-
-static const struct of_device_id hip04_mdio_match[] = {
-       { .compatible = "hisilicon,hip04-mdio" },
-       { }
-};
-MODULE_DEVICE_TABLE(of, hip04_mdio_match);
-
-static struct platform_driver hip04_mdio_driver = {
-       .probe = hip04_mdio_probe,
-       .remove = hip04_mdio_remove,
-       .driver = {
-               .name = "hip04-mdio",
-               .of_match_table = hip04_mdio_match,
-       },
-};
-
-module_platform_driver(hip04_mdio_driver);
-
-MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:hip04-mdio");
index a5e077eac99a3b8292fd5355c142733dccbf8f89..e51892d518ff3ccca8aaed43838d7343b4844dc3 100644 (file)
@@ -371,7 +371,7 @@ static void hix5hd2_port_enable(struct hix5hd2_priv *priv)
 
 static void hix5hd2_port_disable(struct hix5hd2_priv *priv)
 {
-       writel_relaxed(~(BITS_RX_EN | BITS_TX_EN), priv->base + PORT_EN);
+       writel_relaxed(~(u32)(BITS_RX_EN | BITS_TX_EN), priv->base + PORT_EN);
        writel_relaxed(0, priv->base + DESC_WR_RD_ENA);
 }
 
diff --git a/drivers/net/ethernet/hisilicon/hns/Makefile b/drivers/net/ethernet/hisilicon/hns/Makefile
new file mode 100644 (file)
index 0000000..6010c83
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HNS) += hnae.o
+
+obj-$(CONFIG_HNS_DSAF) += hns_dsaf.o
+hns_dsaf-objs = hns_ae_adapt.o hns_dsaf_gmac.o hns_dsaf_mac.o hns_dsaf_misc.o \
+       hns_dsaf_main.o hns_dsaf_ppe.o hns_dsaf_rcb.o hns_dsaf_xgmac.o
+
+obj-$(CONFIG_HNS_ENET) += hns_enet_drv.o
+hns_enet_drv-objs = hns_enet.o hns_ethtool.o
diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c
new file mode 100644 (file)
index 0000000..b364529
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+
+#include "hnae.h"
+
+#define cls_to_ae_dev(dev) container_of(dev, struct hnae_ae_dev, cls_dev)
+
+static struct class *hnae_class;
+
+static void
+hnae_list_add(spinlock_t *lock, struct list_head *node, struct list_head *head)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(lock, flags);
+       list_add_tail_rcu(node, head);
+       spin_unlock_irqrestore(lock, flags);
+}
+
+static void hnae_list_del(spinlock_t *lock, struct list_head *node)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(lock, flags);
+       list_del_rcu(node);
+       spin_unlock_irqrestore(lock, flags);
+}
+
+static int hnae_alloc_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb)
+{
+       unsigned int order = hnae_page_order(ring);
+       struct page *p = dev_alloc_pages(order);
+
+       if (!p)
+               return -ENOMEM;
+
+       cb->priv = p;
+       cb->page_offset = 0;
+       cb->reuse_flag = 0;
+       cb->buf  = page_address(p);
+       cb->length = hnae_page_size(ring);
+       cb->type = DESC_TYPE_PAGE;
+
+       return 0;
+}
+
+static void hnae_free_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb)
+{
+       if (cb->type == DESC_TYPE_SKB)
+               dev_kfree_skb_any((struct sk_buff *)cb->priv);
+       else if (unlikely(is_rx_ring(ring)))
+               put_page((struct page *)cb->priv);
+       memset(cb, 0, sizeof(*cb));
+}
+
+static int hnae_map_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb)
+{
+       cb->dma = dma_map_page(ring_to_dev(ring), cb->priv, 0,
+                              cb->length, ring_to_dma_dir(ring));
+
+       if (dma_mapping_error(ring_to_dev(ring), cb->dma))
+               return -EIO;
+
+       return 0;
+}
+
+static void hnae_unmap_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb)
+{
+       if (cb->type == DESC_TYPE_SKB)
+               dma_unmap_single(ring_to_dev(ring), cb->dma, cb->length,
+                                ring_to_dma_dir(ring));
+       else
+               dma_unmap_page(ring_to_dev(ring), cb->dma, cb->length,
+                              ring_to_dma_dir(ring));
+}
+
+static struct hnae_buf_ops hnae_bops = {
+       .alloc_buffer = hnae_alloc_buffer,
+       .free_buffer = hnae_free_buffer,
+       .map_buffer = hnae_map_buffer,
+       .unmap_buffer = hnae_unmap_buffer,
+};
+
+static int __ae_match(struct device *dev, const void *data)
+{
+       struct hnae_ae_dev *hdev = cls_to_ae_dev(dev);
+       const char *ae_id = data;
+
+       if (!strncmp(ae_id, hdev->name, AE_NAME_SIZE))
+               return 1;
+
+       return 0;
+}
+
+static struct hnae_ae_dev *find_ae(const char *ae_id)
+{
+       struct device *dev;
+
+       WARN_ON(!ae_id);
+
+       dev = class_find_device(hnae_class, NULL, ae_id, __ae_match);
+
+       return dev ? cls_to_ae_dev(dev) : NULL;
+}
+
+static void hnae_free_buffers(struct hnae_ring *ring)
+{
+       int i;
+
+       for (i = 0; i < ring->desc_num; i++)
+               hnae_free_buffer_detach(ring, i);
+}
+
+/* Allocate memory for raw pkg, and map with dma */
+static int hnae_alloc_buffers(struct hnae_ring *ring)
+{
+       int i, j, ret;
+
+       for (i = 0; i < ring->desc_num; i++) {
+               ret = hnae_alloc_buffer_attach(ring, i);
+               if (ret)
+                       goto out_buffer_fail;
+       }
+
+       return 0;
+
+out_buffer_fail:
+       for (j = i - 1; j >= 0; j--)
+               hnae_free_buffer_detach(ring, j);
+       return ret;
+}
+
+/* free desc along with its attached buffer */
+static void hnae_free_desc(struct hnae_ring *ring)
+{
+       hnae_free_buffers(ring);
+       dma_unmap_single(ring_to_dev(ring), ring->desc_dma_addr,
+                        ring->desc_num * sizeof(ring->desc[0]),
+                        ring_to_dma_dir(ring));
+       ring->desc_dma_addr = 0;
+       kfree(ring->desc);
+       ring->desc = NULL;
+}
+
+/* alloc desc, without buffer attached */
+static int hnae_alloc_desc(struct hnae_ring *ring)
+{
+       int size = ring->desc_num * sizeof(ring->desc[0]);
+
+       ring->desc = kzalloc(size, GFP_KERNEL);
+       if (!ring->desc)
+               return -ENOMEM;
+
+       ring->desc_dma_addr = dma_map_single(ring_to_dev(ring),
+               ring->desc, size, ring_to_dma_dir(ring));
+       if (dma_mapping_error(ring_to_dev(ring), ring->desc_dma_addr)) {
+               ring->desc_dma_addr = 0;
+               kfree(ring->desc);
+               ring->desc = NULL;
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+/* fini ring, also free the buffer for the ring */
+static void hnae_fini_ring(struct hnae_ring *ring)
+{
+       hnae_free_desc(ring);
+       kfree(ring->desc_cb);
+       ring->desc_cb = NULL;
+       ring->next_to_clean = 0;
+       ring->next_to_use = 0;
+}
+
+/* init ring, and with buffer for rx ring */
+static int
+hnae_init_ring(struct hnae_queue *q, struct hnae_ring *ring, int flags)
+{
+       int ret;
+
+       if (ring->desc_num <= 0 || ring->buf_size <= 0)
+               return -EINVAL;
+
+       ring->q = q;
+       ring->flags = flags;
+       assert(!ring->desc && !ring->desc_cb && !ring->desc_dma_addr);
+
+       /* not matter for tx or rx ring, the ntc and ntc start from 0 */
+       assert(ring->next_to_use == 0);
+       assert(ring->next_to_clean == 0);
+
+       ring->desc_cb = kcalloc(ring->desc_num, sizeof(ring->desc_cb[0]),
+                       GFP_KERNEL);
+       if (!ring->desc_cb) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = hnae_alloc_desc(ring);
+       if (ret)
+               goto out_with_desc_cb;
+
+       if (is_rx_ring(ring)) {
+               ret = hnae_alloc_buffers(ring);
+               if (ret)
+                       goto out_with_desc;
+       }
+
+       return 0;
+
+out_with_desc:
+       hnae_free_desc(ring);
+out_with_desc_cb:
+       kfree(ring->desc_cb);
+       ring->desc_cb = NULL;
+out:
+       return ret;
+}
+
+static int hnae_init_queue(struct hnae_handle *h, struct hnae_queue *q,
+                          struct hnae_ae_dev *dev)
+{
+       int ret;
+
+       q->dev = dev;
+       q->handle = h;
+
+       ret = hnae_init_ring(q, &q->tx_ring, q->tx_ring.flags | RINGF_DIR);
+       if (ret)
+               goto out;
+
+       ret = hnae_init_ring(q, &q->rx_ring, q->rx_ring.flags & ~RINGF_DIR);
+       if (ret)
+               goto out_with_tx_ring;
+
+       if (dev->ops->init_queue)
+               dev->ops->init_queue(q);
+
+       return 0;
+
+out_with_tx_ring:
+       hnae_fini_ring(&q->tx_ring);
+out:
+       return ret;
+}
+
+static void hnae_fini_queue(struct hnae_queue *q)
+{
+       if (q->dev->ops->fini_queue)
+               q->dev->ops->fini_queue(q);
+
+       hnae_fini_ring(&q->tx_ring);
+       hnae_fini_ring(&q->rx_ring);
+}
+
+/**
+ * ae_chain - define ae chain head
+ */
+static RAW_NOTIFIER_HEAD(ae_chain);
+
+int hnae_register_notifier(struct notifier_block *nb)
+{
+       return raw_notifier_chain_register(&ae_chain, nb);
+}
+EXPORT_SYMBOL(hnae_register_notifier);
+
+void hnae_unregister_notifier(struct notifier_block *nb)
+{
+       if (raw_notifier_chain_unregister(&ae_chain, nb))
+               dev_err(NULL, "notifier chain unregister fail\n");
+}
+EXPORT_SYMBOL(hnae_unregister_notifier);
+
+int hnae_reinit_handle(struct hnae_handle *handle)
+{
+       int i, j;
+       int ret;
+
+       for (i = 0; i < handle->q_num; i++) /* free ring*/
+               hnae_fini_queue(handle->qs[i]);
+
+       if (handle->dev->ops->reset)
+               handle->dev->ops->reset(handle);
+
+       for (i = 0; i < handle->q_num; i++) {/* reinit ring*/
+               ret = hnae_init_queue(handle, handle->qs[i], handle->dev);
+               if (ret)
+                       goto out_when_init_queue;
+       }
+       return 0;
+out_when_init_queue:
+       for (j = i - 1; j >= 0; j--)
+               hnae_fini_queue(handle->qs[j]);
+       return ret;
+}
+EXPORT_SYMBOL(hnae_reinit_handle);
+
+/* hnae_get_handle - get a handle from the AE
+ * @owner_dev: the dev use this handle
+ * @ae_id: the id of the ae to be used
+ * @ae_opts: the options set for the handle
+ * @bops: the callbacks for buffer management
+ *
+ * return handle ptr or ERR_PTR
+ */
+struct hnae_handle *hnae_get_handle(struct device *owner_dev,
+                                   const char *ae_id, u32 port_id,
+                                   struct hnae_buf_ops *bops)
+{
+       struct hnae_ae_dev *dev;
+       struct hnae_handle *handle;
+       int i, j;
+       int ret;
+
+       dev = find_ae(ae_id);
+       if (!dev)
+               return ERR_PTR(-ENODEV);
+
+       handle = dev->ops->get_handle(dev, port_id);
+       if (IS_ERR(handle))
+               return handle;
+
+       handle->dev = dev;
+       handle->owner_dev = owner_dev;
+       handle->bops = bops ? bops : &hnae_bops;
+       handle->eport_id = port_id;
+
+       for (i = 0; i < handle->q_num; i++) {
+               ret = hnae_init_queue(handle, handle->qs[i], dev);
+               if (ret)
+                       goto out_when_init_queue;
+       }
+
+       __module_get(dev->owner);
+
+       hnae_list_add(&dev->lock, &handle->node, &dev->handle_list);
+
+       return handle;
+
+out_when_init_queue:
+       for (j = i - 1; j >= 0; j--)
+               hnae_fini_queue(handle->qs[j]);
+
+       return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL(hnae_get_handle);
+
+void hnae_put_handle(struct hnae_handle *h)
+{
+       struct hnae_ae_dev *dev = h->dev;
+       int i;
+
+       for (i = 0; i < h->q_num; i++)
+               hnae_fini_queue(h->qs[i]);
+
+       if (h->dev->ops->reset)
+               h->dev->ops->reset(h);
+
+       hnae_list_del(&dev->lock, &h->node);
+
+       if (dev->ops->put_handle)
+               dev->ops->put_handle(h);
+
+       module_put(dev->owner);
+}
+EXPORT_SYMBOL(hnae_put_handle);
+
+static void hnae_release(struct device *dev)
+{
+}
+
+/**
+ * hnae_ae_register - register a AE engine to hnae framework
+ * @hdev: the hnae ae engine device
+ * @owner:  the module who provides this dev
+ * NOTE: the duplicated name will not be checked
+ */
+int hnae_ae_register(struct hnae_ae_dev *hdev, struct module *owner)
+{
+       static atomic_t id = ATOMIC_INIT(-1);
+       int ret;
+
+       if (!hdev->dev)
+               return -ENODEV;
+
+       if (!hdev->ops || !hdev->ops->get_handle ||
+           !hdev->ops->toggle_ring_irq ||
+           !hdev->ops->toggle_queue_status ||
+           !hdev->ops->get_status || !hdev->ops->adjust_link)
+               return -EINVAL;
+
+       hdev->owner = owner;
+       hdev->id = (int)atomic_inc_return(&id);
+       hdev->cls_dev.parent = hdev->dev;
+       hdev->cls_dev.class = hnae_class;
+       hdev->cls_dev.release = hnae_release;
+       (void)dev_set_name(&hdev->cls_dev, "hnae%d", hdev->id);
+       ret = device_register(&hdev->cls_dev);
+       if (ret)
+               return ret;
+
+       __module_get(THIS_MODULE);
+
+       INIT_LIST_HEAD(&hdev->handle_list);
+       spin_lock_init(&hdev->lock);
+
+       ret = raw_notifier_call_chain(&ae_chain, HNAE_AE_REGISTER, NULL);
+       if (ret)
+               dev_dbg(hdev->dev,
+                       "has not notifier for AE: %s\n", hdev->name);
+
+       return 0;
+}
+EXPORT_SYMBOL(hnae_ae_register);
+
+/**
+ * hnae_ae_unregister - unregisters a HNAE AE engine
+ * @cdev: the device to unregister
+ */
+void hnae_ae_unregister(struct hnae_ae_dev *hdev)
+{
+       device_unregister(&hdev->cls_dev);
+       module_put(THIS_MODULE);
+}
+EXPORT_SYMBOL(hnae_ae_unregister);
+
+static int __init hnae_init(void)
+{
+       hnae_class = class_create(THIS_MODULE, "hnae");
+       return PTR_ERR_OR_ZERO(hnae_class);
+}
+
+static void __exit hnae_exit(void)
+{
+       class_destroy(hnae_class);
+}
+
+subsys_initcall(hnae_init);
+module_exit(hnae_exit);
+
+MODULE_AUTHOR("Hisilicon, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Hisilicon Network Acceleration Engine Framework");
+
+/* vi: set tw=78 noet: */
diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h
new file mode 100644 (file)
index 0000000..cec95ac
--- /dev/null
@@ -0,0 +1,585 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 __HNAE_H
+#define __HNAE_H
+
+/* Names used in this framework:
+ *      ae handle (handle):
+ *        a set of queues provided by AE
+ *      ring buffer queue (rbq):
+ *        the channel between upper layer and the AE, can do tx and rx
+ *      ring:
+ *        a tx or rx channel within a rbq
+ *      ring description (desc):
+ *        an element in the ring with packet information
+ *      buffer:
+ *        a memory region referred by desc with the full packet payload
+ *
+ * "num" means a static number set as a parameter, "count" mean a dynamic
+ *   number set while running
+ * "cb" means control block
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/notifier.h>
+#include <linux/phy.h>
+#include <linux/types.h>
+
+#define HNAE_DRIVER_VERSION "1.3.0"
+#define HNAE_DRIVER_NAME "hns"
+#define HNAE_COPYRIGHT "Copyright(c) 2015 Huawei Corporation."
+#define HNAE_DRIVER_STRING "Hisilicon Network Subsystem Driver"
+#define HNAE_DEFAULT_DEVICE_DESCR "Hisilicon Network Subsystem"
+
+#ifdef DEBUG
+
+#ifndef assert
+#define assert(expr) \
+do { \
+       if (!(expr)) { \
+               pr_err("Assertion failed! %s, %s, %s, line %d\n", \
+                          #expr, __FILE__, __func__, __LINE__); \
+       } \
+} while (0)
+#endif
+
+#else
+
+#ifndef assert
+#define assert(expr)
+#endif
+
+#endif
+
+#define AE_VERSION_1 ('6' << 16 | '6' << 8 | '0')
+#define AE_VERSION_2 ('1' << 24 | '6' << 16 | '1' << 8 | '0')
+#define AE_NAME_SIZE 16
+
+/* some said the RX and TX RCB format should not be the same in the future. But
+ * it is the same now...
+ */
+#define RCB_REG_BASEADDR_L         0x00 /* P660 support only 32bit accessing */
+#define RCB_REG_BASEADDR_H         0x04
+#define RCB_REG_BD_NUM             0x08
+#define RCB_REG_BD_LEN             0x0C
+#define RCB_REG_PKTLINE            0x10
+#define RCB_REG_TAIL               0x18
+#define RCB_REG_HEAD               0x1C
+#define RCB_REG_FBDNUM             0x20
+#define RCB_REG_OFFSET             0x24 /* pkt num to be handled */
+#define RCB_REG_PKTNUM_RECORD      0x2C /* total pkt received */
+
+#define HNS_RX_HEAD_SIZE 256
+
+#define HNAE_AE_REGISTER 0x1
+
+#define RCB_RING_NAME_LEN 16
+
+enum hnae_led_state {
+       HNAE_LED_INACTIVE,
+       HNAE_LED_ACTIVE,
+       HNAE_LED_ON,
+       HNAE_LED_OFF
+};
+
+#define HNS_RX_FLAG_VLAN_PRESENT 0x1
+#define HNS_RX_FLAG_L3ID_IPV4 0x0
+#define HNS_RX_FLAG_L3ID_IPV6 0x1
+#define HNS_RX_FLAG_L4ID_UDP 0x0
+#define HNS_RX_FLAG_L4ID_TCP 0x1
+
+#define HNS_TXD_ASID_S 0
+#define HNS_TXD_ASID_M (0xff << HNS_TXD_ASID_S)
+#define HNS_TXD_BUFNUM_S 8
+#define HNS_TXD_BUFNUM_M (0x3 << HNS_TXD_BUFNUM_S)
+#define HNS_TXD_PORTID_S 10
+#define HNS_TXD_PORTID_M (0x7 << HNS_TXD_PORTID_S)
+
+#define HNS_TXD_RA_B 8
+#define HNS_TXD_RI_B 9
+#define HNS_TXD_L4CS_B 10
+#define HNS_TXD_L3CS_B 11
+#define HNS_TXD_FE_B 12
+#define HNS_TXD_VLD_B 13
+#define HNS_TXD_IPOFFSET_S 14
+#define HNS_TXD_IPOFFSET_M (0xff << HNS_TXD_IPOFFSET_S)
+
+#define HNS_RXD_IPOFFSET_S 0
+#define HNS_RXD_IPOFFSET_M (0xff << HNS_TXD_IPOFFSET_S)
+#define HNS_RXD_BUFNUM_S 8
+#define HNS_RXD_BUFNUM_M (0x3 << HNS_RXD_BUFNUM_S)
+#define HNS_RXD_PORTID_S 10
+#define HNS_RXD_PORTID_M (0x7 << HNS_RXD_PORTID_S)
+#define HNS_RXD_DMAC_S 13
+#define HNS_RXD_DMAC_M (0x3 << HNS_RXD_DMAC_S)
+#define HNS_RXD_VLAN_S 15
+#define HNS_RXD_VLAN_M (0x3 << HNS_RXD_VLAN_S)
+#define HNS_RXD_L3ID_S 17
+#define HNS_RXD_L3ID_M (0xf << HNS_RXD_L3ID_S)
+#define HNS_RXD_L4ID_S 21
+#define HNS_RXD_L4ID_M (0xf << HNS_RXD_L4ID_S)
+#define HNS_RXD_FE_B 25
+#define HNS_RXD_FRAG_B 26
+#define HNS_RXD_VLD_B 27
+#define HNS_RXD_L2E_B 28
+#define HNS_RXD_L3E_B 29
+#define HNS_RXD_L4E_B 30
+#define HNS_RXD_DROP_B 31
+
+#define HNS_RXD_VLANID_S 8
+#define HNS_RXD_VLANID_M (0xfff << HNS_RXD_VLANID_S)
+#define HNS_RXD_CFI_B 20
+#define HNS_RXD_PRI_S 21
+#define HNS_RXD_PRI_M (0x7 << HNS_RXD_PRI_S)
+#define HNS_RXD_ASID_S 24
+#define HNS_RXD_ASID_M (0xff << HNS_RXD_ASID_S)
+
+/* hardware spec ring buffer format */
+struct __packed hnae_desc {
+       __le64 addr;
+       union {
+               struct {
+                       __le16 asid_bufnum_pid;
+                       __le16 send_size;
+                       __le32 flag_ipoffset;
+                       __le32 reserved_3[4];
+               } tx;
+
+               struct {
+                       __le32 ipoff_bnum_pid_flag;
+                       __le16 pkt_len;
+                       __le16 size;
+                       __le32 vlan_pri_asid;
+                       __le32 reserved_2[3];
+               } rx;
+       };
+};
+
+struct hnae_desc_cb {
+       dma_addr_t dma; /* dma address of this desc */
+       void *buf;      /* cpu addr for a desc */
+
+       /* priv data for the desc, e.g. skb when use with ip stack*/
+       void *priv;
+       u16 page_offset;
+       u16 reuse_flag;
+
+       u16 length;     /* length of the buffer */
+
+       /* desc type, used by the ring user to mark the type of the priv data */
+       u16 type;
+};
+
+#define setflags(flags, bits) ((flags) |= (bits))
+#define unsetflags(flags, bits) ((flags) &= ~(bits))
+
+/* hnae_ring->flags fields */
+#define RINGF_DIR 0x1      /* TX or RX ring, set if TX */
+#define is_tx_ring(ring) ((ring)->flags & RINGF_DIR)
+#define is_rx_ring(ring) (!is_tx_ring(ring))
+#define ring_to_dma_dir(ring) (is_tx_ring(ring) ? \
+       DMA_TO_DEVICE : DMA_FROM_DEVICE)
+
+struct ring_stats {
+       u64 io_err_cnt;
+       u64 sw_err_cnt;
+       u64 seg_pkt_cnt;
+       union {
+               struct {
+                       u64 tx_pkts;
+                       u64 tx_bytes;
+                       u64 tx_err_cnt;
+                       u64 restart_queue;
+                       u64 tx_busy;
+               };
+               struct {
+                       u64 rx_pkts;
+                       u64 rx_bytes;
+                       u64 rx_err_cnt;
+                       u64 reuse_pg_cnt;
+                       u64 err_pkt_len;
+                       u64 non_vld_descs;
+                       u64 err_bd_num;
+                       u64 l2_err;
+                       u64 l3l4_csum_err;
+               };
+       };
+};
+
+struct hnae_queue;
+
+struct hnae_ring {
+       u8 __iomem *io_base; /* base io address for the ring */
+       struct hnae_desc *desc; /* dma map address space */
+       struct hnae_desc_cb *desc_cb;
+       struct hnae_queue *q;
+       int irq;
+       char ring_name[RCB_RING_NAME_LEN];
+
+       /* statistic */
+       struct ring_stats stats;
+
+       dma_addr_t desc_dma_addr;
+       u32 buf_size;       /* size for hnae_desc->addr, preset by AE */
+       u16 desc_num;       /* total number of desc */
+       u16 max_desc_num_per_pkt;
+       u16 max_raw_data_sz_per_desc;
+       u16 max_pkt_size;
+       int next_to_use;    /* idx of next spare desc */
+
+       /* idx of lastest sent desc, the ring is empty when equal to
+        * next_to_use
+        */
+       int next_to_clean;
+
+       int flags;          /* ring attribute */
+       int irq_init_flag;
+};
+
+#define ring_ptr_move_fw(ring, p) \
+       ((ring)->p = ((ring)->p + 1) % (ring)->desc_num)
+#define ring_ptr_move_bw(ring, p) \
+       ((ring)->p = ((ring)->p - 1 + (ring)->desc_num) % (ring)->desc_num)
+
+enum hns_desc_type {
+       DESC_TYPE_SKB,
+       DESC_TYPE_PAGE,
+};
+
+#define assert_is_ring_idx(ring, idx) \
+       assert((idx) >= 0 && (idx) < (ring)->desc_num)
+
+/* the distance between [begin, end) in a ring buffer
+ * note: there is a unuse slot between the begin and the end
+ */
+static inline int ring_dist(struct hnae_ring *ring, int begin, int end)
+{
+       assert_is_ring_idx(ring, begin);
+       assert_is_ring_idx(ring, end);
+
+       return (end - begin + ring->desc_num) % ring->desc_num;
+}
+
+static inline int ring_space(struct hnae_ring *ring)
+{
+       return ring->desc_num -
+               ring_dist(ring, ring->next_to_clean, ring->next_to_use) - 1;
+}
+
+static inline int is_ring_empty(struct hnae_ring *ring)
+{
+       assert_is_ring_idx(ring, ring->next_to_use);
+       assert_is_ring_idx(ring, ring->next_to_clean);
+
+       return ring->next_to_use == ring->next_to_clean;
+}
+
+#define hnae_buf_size(_ring) ((_ring)->buf_size)
+#define hnae_page_order(_ring) (get_order(hnae_buf_size(_ring)))
+#define hnae_page_size(_ring) (PAGE_SIZE << hnae_page_order(_ring))
+
+struct hnae_handle;
+
+/* allocate and dma map space for hnae desc */
+struct hnae_buf_ops {
+       int (*alloc_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb);
+       void (*free_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb);
+       int (*map_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb);
+       void (*unmap_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb);
+};
+
+struct hnae_queue {
+       void __iomem *io_base;
+       phys_addr_t phy_base;
+       struct hnae_ae_dev *dev;        /* the device who use this queue */
+       struct hnae_ring rx_ring, tx_ring;
+       struct hnae_handle *handle;
+};
+
+/*hnae loop mode*/
+enum hnae_loop {
+       MAC_INTERNALLOOP_MAC = 0,
+       MAC_INTERNALLOOP_SERDES,
+       MAC_INTERNALLOOP_PHY,
+       MAC_LOOP_NONE,
+};
+
+/*hnae port type*/
+enum hnae_port_type {
+       HNAE_PORT_SERVICE = 0,
+       HNAE_PORT_DEBUG
+};
+
+/* This struct defines the operation on the handle.
+ *
+ * get_handle(): (mandatory)
+ *   Get a handle from AE according to its name and options.
+ *   the AE driver should manage the space used by handle and its queues while
+ *   the HNAE framework will allocate desc and desc_cb for all rings in the
+ *   queues.
+ * put_handle():
+ *   Release the handle.
+ * start():
+ *   Enable the hardware, include all queues
+ * stop():
+ *   Disable the hardware
+ * set_opts(): (mandatory)
+ *   Set options to the AE
+ * get_opts(): (mandatory)
+ *   Get options from the AE
+ * get_status():
+ *   Get the carrier state of the back channel of the handle, 1 for ok, 0 for
+ *   non-ok
+ * toggle_ring_irq(): (mandatory)
+ *   Set the ring irq to be enabled(0) or disable(1)
+ * toggle_queue_status(): (mandatory)
+ *   Set the queue to be enabled(1) or disable(0), this will not change the
+ *   ring irq state
+ * adjust_link()
+ *   adjust link status
+ * set_loopback()
+ *   set loopback
+ * get_ring_bdnum_limit()
+ *   get ring bd number limit
+ * get_pauseparam()
+ *   get tx and rx of pause frame use
+ * set_autoneg()
+ *   set auto autonegotiation of pause frame use
+ * get_autoneg()
+ *   get auto autonegotiation of pause frame use
+ * set_pauseparam()
+ *   set tx and rx of pause frame use
+ * get_coalesce_usecs()
+ *   get usecs to delay a TX interrupt after a packet is sent
+ * get_rx_max_coalesced_frames()
+ *   get Maximum number of packets to be sent before a TX interrupt.
+ * set_coalesce_usecs()
+ *   set usecs to delay a TX interrupt after a packet is sent
+ * set_coalesce_frames()
+ *   set Maximum number of packets to be sent before a TX interrupt.
+ * get_ringnum()
+ *   get RX/TX ring number
+ * get_max_ringnum()
+ *   get RX/TX ring maximum number
+ * get_mac_addr()
+ *   get mac address
+ * set_mac_addr()
+ *   set mac address
+ * set_mc_addr()
+ *   set multicast mode
+ * set_mtu()
+ *   set mtu
+ * update_stats()
+ *   update Old network device statistics
+ * get_ethtool_stats()
+ *   get ethtool network device statistics
+ * get_strings()
+ *   get a set of strings that describe the requested objects
+ * get_sset_count()
+ *   get number of strings that @get_strings will write
+ * update_led_status()
+ *   update the led status
+ * set_led_id()
+ *   set led id
+ * get_regs()
+ *   get regs dump
+ * get_regs_len()
+ *   get the len of the regs dump
+ */
+struct hnae_ae_ops {
+       struct hnae_handle *(*get_handle)(struct hnae_ae_dev *dev,
+                                         u32 port_id);
+       void (*put_handle)(struct hnae_handle *handle);
+       void (*init_queue)(struct hnae_queue *q);
+       void (*fini_queue)(struct hnae_queue *q);
+       int (*start)(struct hnae_handle *handle);
+       void (*stop)(struct hnae_handle *handle);
+       void (*reset)(struct hnae_handle *handle);
+       int (*set_opts)(struct hnae_handle *handle, int type, void *opts);
+       int (*get_opts)(struct hnae_handle *handle, int type, void **opts);
+       int (*get_status)(struct hnae_handle *handle);
+       int (*get_info)(struct hnae_handle *handle,
+                       u8 *auto_neg, u16 *speed, u8 *duplex);
+       void (*toggle_ring_irq)(struct hnae_ring *ring, u32 val);
+       void (*toggle_queue_status)(struct hnae_queue *queue, u32 val);
+       void (*adjust_link)(struct hnae_handle *handle, int speed, int duplex);
+       int (*set_loopback)(struct hnae_handle *handle,
+                           enum hnae_loop loop_mode, int en);
+       void (*get_ring_bdnum_limit)(struct hnae_queue *queue,
+                                    u32 *uplimit);
+       void (*get_pauseparam)(struct hnae_handle *handle,
+                              u32 *auto_neg, u32 *rx_en, u32 *tx_en);
+       int (*set_autoneg)(struct hnae_handle *handle, u8 enable);
+       int (*get_autoneg)(struct hnae_handle *handle);
+       int (*set_pauseparam)(struct hnae_handle *handle,
+                             u32 auto_neg, u32 rx_en, u32 tx_en);
+       void (*get_coalesce_usecs)(struct hnae_handle *handle,
+                                  u32 *tx_usecs, u32 *rx_usecs);
+       void (*get_rx_max_coalesced_frames)(struct hnae_handle *handle,
+                                           u32 *tx_frames, u32 *rx_frames);
+       void (*set_coalesce_usecs)(struct hnae_handle *handle, u32 timeout);
+       int (*set_coalesce_frames)(struct hnae_handle *handle,
+                                  u32 coalesce_frames);
+       void (*set_promisc_mode)(struct hnae_handle *handle, u32 en);
+       int (*get_mac_addr)(struct hnae_handle *handle, void **p);
+       int (*set_mac_addr)(struct hnae_handle *handle, void *p);
+       int (*set_mc_addr)(struct hnae_handle *handle, void *addr);
+       int (*set_mtu)(struct hnae_handle *handle, int new_mtu);
+       void (*update_stats)(struct hnae_handle *handle,
+                            struct net_device_stats *net_stats);
+       void (*get_stats)(struct hnae_handle *handle, u64 *data);
+       void (*get_strings)(struct hnae_handle *handle,
+                           u32 stringset, u8 *data);
+       int (*get_sset_count)(struct hnae_handle *handle, int stringset);
+       void (*update_led_status)(struct hnae_handle *handle);
+       int (*set_led_id)(struct hnae_handle *handle,
+                         enum hnae_led_state status);
+       void (*get_regs)(struct hnae_handle *handle, void *data);
+       int (*get_regs_len)(struct hnae_handle *handle);
+};
+
+struct hnae_ae_dev {
+       struct device cls_dev; /* the class dev */
+       struct device *dev; /* the presented dev */
+       struct hnae_ae_ops *ops;
+       struct list_head node;
+       struct module *owner; /* the module who provides this dev */
+       int id;
+       char name[AE_NAME_SIZE];
+       struct list_head handle_list;
+       spinlock_t lock; /* lock to protect the handle_list */
+};
+
+struct hnae_handle {
+       struct device *owner_dev; /* the device which make use of this handle */
+       struct hnae_ae_dev *dev;  /* the device who provides this handle */
+       struct device_node *phy_node;
+       phy_interface_t phy_if;
+       u32 if_support;
+       int q_num;
+       int vf_id;
+       u32 eport_id;
+       enum hnae_port_type port_type;
+       struct list_head node;    /* list to hnae_ae_dev->handle_list */
+       struct hnae_buf_ops *bops; /* operation for the buffer */
+       struct hnae_queue **qs;  /* array base of all queues */
+};
+
+#define ring_to_dev(ring) ((ring)->q->dev->dev)
+
+struct hnae_handle *hnae_get_handle(struct device *owner_dev, const char *ae_id,
+                                   u32 port_id, struct hnae_buf_ops *bops);
+void hnae_put_handle(struct hnae_handle *handle);
+int hnae_ae_register(struct hnae_ae_dev *dev, struct module *owner);
+void hnae_ae_unregister(struct hnae_ae_dev *dev);
+
+int hnae_register_notifier(struct notifier_block *nb);
+void hnae_unregister_notifier(struct notifier_block *nb);
+int hnae_reinit_handle(struct hnae_handle *handle);
+
+#define hnae_queue_xmit(q, buf_num) writel_relaxed(buf_num, \
+       (q)->tx_ring.io_base + RCB_REG_TAIL)
+
+#ifndef assert
+#define assert(cond)
+#endif
+
+static inline int hnae_reserve_buffer_map(struct hnae_ring *ring,
+                                         struct hnae_desc_cb *cb)
+{
+       struct hnae_buf_ops *bops = ring->q->handle->bops;
+       int ret;
+
+       ret = bops->alloc_buffer(ring, cb);
+       if (ret)
+               goto out;
+
+       ret = bops->map_buffer(ring, cb);
+       if (ret)
+               goto out_with_buf;
+
+       return 0;
+
+out_with_buf:
+       bops->free_buffer(ring, cb);
+out:
+       return ret;
+}
+
+static inline int hnae_alloc_buffer_attach(struct hnae_ring *ring, int i)
+{
+       int ret = hnae_reserve_buffer_map(ring, &ring->desc_cb[i]);
+
+       if (ret)
+               return ret;
+
+       ring->desc[i].addr = (__le64)ring->desc_cb[i].dma;
+
+       return 0;
+}
+
+static inline void hnae_buffer_detach(struct hnae_ring *ring, int i)
+{
+       ring->q->handle->bops->unmap_buffer(ring, &ring->desc_cb[i]);
+       ring->desc[i].addr = 0;
+}
+
+static inline void hnae_free_buffer_detach(struct hnae_ring *ring, int i)
+{
+       struct hnae_buf_ops *bops = ring->q->handle->bops;
+       struct hnae_desc_cb *cb = &ring->desc_cb[i];
+
+       if (!ring->desc_cb[i].dma)
+               return;
+
+       hnae_buffer_detach(ring, i);
+       bops->free_buffer(ring, cb);
+}
+
+/* detach a in-used buffer and replace with a reserved one  */
+static inline void hnae_replace_buffer(struct hnae_ring *ring, int i,
+                                      struct hnae_desc_cb *res_cb)
+{
+       struct hnae_buf_ops *bops = ring->q->handle->bops;
+       struct hnae_desc_cb tmp_cb = ring->desc_cb[i];
+
+       bops->unmap_buffer(ring, &ring->desc_cb[i]);
+       ring->desc_cb[i] = *res_cb;
+       *res_cb = tmp_cb;
+       ring->desc[i].addr = (__le64)ring->desc_cb[i].dma;
+       ring->desc[i].rx.ipoff_bnum_pid_flag = 0;
+}
+
+static inline void hnae_reuse_buffer(struct hnae_ring *ring, int i)
+{
+       ring->desc_cb[i].reuse_flag = 0;
+       ring->desc[i].addr = (__le64)(ring->desc_cb[i].dma
+               + ring->desc_cb[i].page_offset);
+       ring->desc[i].rx.ipoff_bnum_pid_flag = 0;
+}
+
+#define hnae_set_field(origin, mask, shift, val) \
+       do { \
+               (origin) &= (~(mask)); \
+               (origin) |= ((val) << (shift)) & (mask); \
+       } while (0)
+
+#define hnae_set_bit(origin, shift, val) \
+       hnae_set_field((origin), (0x1 << (shift)), (shift), (val))
+
+#define hnae_get_field(origin, mask, shift) (((origin) & (mask)) >> (shift))
+
+#define hnae_get_bit(origin, shift) \
+       hnae_get_field((origin), (0x1 << (shift)), (shift))
+
+#endif
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
new file mode 100644 (file)
index 0000000..1a16c03
--- /dev/null
@@ -0,0 +1,783 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+
+#include "hnae.h"
+#include "hns_dsaf_mac.h"
+#include "hns_dsaf_main.h"
+#include "hns_dsaf_ppe.h"
+#include "hns_dsaf_rcb.h"
+
+#define AE_NAME_PORT_ID_IDX 6
+#define ETH_STATIC_REG  1
+#define ETH_DUMP_REG    5
+#define ETH_GSTRING_LEN        32
+
+static struct hns_mac_cb *hns_get_mac_cb(struct hnae_handle *handle)
+{
+       struct  hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle);
+
+       return vf_cb->mac_cb;
+}
+
+/**
+ * hns_ae_map_eport_to_dport - translate enet port id to dsaf port id
+ * @port_id: enet port id
+ *: debug port 0-1, service port 2 -7 (dsaf mode only 2)
+ * return: dsaf port id
+ *: service ports 0 - 5, debug port 6-7
+ **/
+static int hns_ae_map_eport_to_dport(u32 port_id)
+{
+       int port_index;
+
+       if (port_id < DSAF_DEBUG_NW_NUM)
+               port_index = port_id + DSAF_SERVICE_PORT_NUM_PER_DSAF;
+       else
+               port_index = port_id - DSAF_DEBUG_NW_NUM;
+
+       return port_index;
+}
+
+static struct dsaf_device *hns_ae_get_dsaf_dev(struct hnae_ae_dev *dev)
+{
+       return container_of(dev, struct dsaf_device, ae_dev);
+}
+
+static struct hns_ppe_cb *hns_get_ppe_cb(struct hnae_handle *handle)
+{
+       int ppe_index;
+       int ppe_common_index;
+       struct ppe_common_cb *ppe_comm;
+       struct  hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle);
+
+       if (vf_cb->port_index < DSAF_SERVICE_PORT_NUM_PER_DSAF) {
+               ppe_index = vf_cb->port_index;
+               ppe_common_index = 0;
+       } else {
+               ppe_index = 0;
+               ppe_common_index =
+                       vf_cb->port_index - DSAF_SERVICE_PORT_NUM_PER_DSAF + 1;
+       }
+       ppe_comm = vf_cb->dsaf_dev->ppe_common[ppe_common_index];
+       return &ppe_comm->ppe_cb[ppe_index];
+}
+
+static int hns_ae_get_q_num_per_vf(
+       struct dsaf_device *dsaf_dev, int port)
+{
+       int common_idx = hns_dsaf_get_comm_idx_by_port(port);
+
+       return dsaf_dev->rcb_common[common_idx]->max_q_per_vf;
+}
+
+static int hns_ae_get_vf_num_per_port(
+       struct dsaf_device *dsaf_dev, int port)
+{
+       int common_idx = hns_dsaf_get_comm_idx_by_port(port);
+
+       return dsaf_dev->rcb_common[common_idx]->max_vfn;
+}
+
+static struct ring_pair_cb *hns_ae_get_base_ring_pair(
+       struct dsaf_device *dsaf_dev, int port)
+{
+       int common_idx = hns_dsaf_get_comm_idx_by_port(port);
+       struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[common_idx];
+       int q_num = rcb_comm->max_q_per_vf;
+       int vf_num = rcb_comm->max_vfn;
+
+       if (common_idx == HNS_DSAF_COMM_SERVICE_NW_IDX)
+               return &rcb_comm->ring_pair_cb[port * q_num * vf_num];
+       else
+               return &rcb_comm->ring_pair_cb[0];
+}
+
+static struct ring_pair_cb *hns_ae_get_ring_pair(struct hnae_queue *q)
+{
+       return container_of(q, struct ring_pair_cb, q);
+}
+
+struct hnae_handle *hns_ae_get_handle(struct hnae_ae_dev *dev,
+                                     u32 port_id)
+{
+       int port_idx;
+       int vfnum_per_port;
+       int qnum_per_vf;
+       int i;
+       struct dsaf_device *dsaf_dev;
+       struct hnae_handle *ae_handle;
+       struct ring_pair_cb *ring_pair_cb;
+       struct hnae_vf_cb *vf_cb;
+
+       dsaf_dev = hns_ae_get_dsaf_dev(dev);
+       port_idx = hns_ae_map_eport_to_dport(port_id);
+
+       ring_pair_cb = hns_ae_get_base_ring_pair(dsaf_dev, port_idx);
+       vfnum_per_port = hns_ae_get_vf_num_per_port(dsaf_dev, port_idx);
+       qnum_per_vf = hns_ae_get_q_num_per_vf(dsaf_dev, port_idx);
+
+       vf_cb = kzalloc(sizeof(*vf_cb) +
+                       qnum_per_vf * sizeof(struct hnae_queue *), GFP_KERNEL);
+       if (unlikely(!vf_cb)) {
+               dev_err(dsaf_dev->dev, "malloc vf_cb fail!\n");
+               ae_handle = ERR_PTR(-ENOMEM);
+               goto handle_err;
+       }
+       ae_handle = &vf_cb->ae_handle;
+       /* ae_handle Init  */
+       ae_handle->owner_dev = dsaf_dev->dev;
+       ae_handle->dev = dev;
+       ae_handle->q_num = qnum_per_vf;
+
+       /* find ring pair, and set vf id*/
+       for (ae_handle->vf_id = 0;
+               ae_handle->vf_id < vfnum_per_port; ae_handle->vf_id++) {
+               if (!ring_pair_cb->used_by_vf)
+                       break;
+               ring_pair_cb += qnum_per_vf;
+       }
+       if (ae_handle->vf_id >= vfnum_per_port) {
+               dev_err(dsaf_dev->dev, "malloc queue fail!\n");
+               ae_handle = ERR_PTR(-EINVAL);
+               goto vf_id_err;
+       }
+
+       ae_handle->qs = (struct hnae_queue **)(&ae_handle->qs + 1);
+       for (i = 0; i < qnum_per_vf; i++) {
+               ae_handle->qs[i] = &ring_pair_cb->q;
+               ae_handle->qs[i]->rx_ring.q = ae_handle->qs[i];
+               ae_handle->qs[i]->tx_ring.q = ae_handle->qs[i];
+
+               ring_pair_cb->used_by_vf = 1;
+               if (port_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF)
+                       ring_pair_cb->port_id_in_dsa = port_idx;
+               else
+                       ring_pair_cb->port_id_in_dsa = 0;
+
+               ring_pair_cb++;
+       }
+
+       vf_cb->dsaf_dev = dsaf_dev;
+       vf_cb->port_index = port_idx;
+       vf_cb->mac_cb = &dsaf_dev->mac_cb[port_idx];
+
+       ae_handle->phy_if = vf_cb->mac_cb->phy_if;
+       ae_handle->phy_node = vf_cb->mac_cb->phy_node;
+       ae_handle->if_support = vf_cb->mac_cb->if_support;
+       ae_handle->port_type = vf_cb->mac_cb->mac_type;
+
+       return ae_handle;
+vf_id_err:
+       kfree(vf_cb);
+handle_err:
+       return ae_handle;
+}
+
+static void hns_ae_put_handle(struct hnae_handle *handle)
+{
+       struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle);
+       int i;
+
+       vf_cb->mac_cb    = NULL;
+
+       kfree(vf_cb);
+
+       for (i = 0; i < handle->q_num; i++)
+               hns_ae_get_ring_pair(handle->qs[i])->used_by_vf = 0;
+}
+
+static void hns_ae_ring_enable_all(struct hnae_handle *handle, int val)
+{
+       int q_num = handle->q_num;
+       int i;
+
+       for (i = 0; i < q_num; i++)
+               hns_rcb_ring_enable_hw(handle->qs[i], val);
+}
+
+static void hns_ae_init_queue(struct hnae_queue *q)
+{
+       struct ring_pair_cb *ring =
+               container_of(q, struct ring_pair_cb, q);
+
+       hns_rcb_init_hw(ring);
+}
+
+static void hns_ae_fini_queue(struct hnae_queue *q)
+{
+       struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(q->handle);
+
+       if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE)
+               hns_rcb_reset_ring_hw(q);
+}
+
+static int hns_ae_set_mac_address(struct hnae_handle *handle, void *p)
+{
+       int ret;
+       struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
+
+       if (!p || !is_valid_ether_addr((const u8 *)p)) {
+               dev_err(handle->owner_dev, "is not valid ether addr !\n");
+               return -EADDRNOTAVAIL;
+       }
+
+       ret = hns_mac_change_vf_addr(mac_cb, handle->vf_id, p);
+       if (ret != 0) {
+               dev_err(handle->owner_dev,
+                       "set_mac_address fail, ret=%d!\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr)
+{
+       int ret;
+       char *mac_addr = (char *)addr;
+       struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
+
+       assert(mac_cb);
+
+       if (mac_cb->mac_type != HNAE_PORT_SERVICE)
+               return 0;
+
+       ret = hns_mac_set_multi(mac_cb, mac_cb->mac_id, mac_addr, ENABLE);
+       if (ret) {
+               dev_err(handle->owner_dev,
+                       "mac add mul_mac:%pM port%d  fail, ret = %#x!\n",
+                       mac_addr, mac_cb->mac_id, ret);
+               return ret;
+       }
+
+       ret = hns_mac_set_multi(mac_cb, DSAF_BASE_INNER_PORT_NUM,
+                               mac_addr, ENABLE);
+       if (ret)
+               dev_err(handle->owner_dev,
+                       "mac add mul_mac:%pM port%d  fail, ret = %#x!\n",
+                       mac_addr, DSAF_BASE_INNER_PORT_NUM, ret);
+
+       return ret;
+}
+
+static int hns_ae_set_mtu(struct hnae_handle *handle, int new_mtu)
+{
+       struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
+
+       return hns_mac_set_mtu(mac_cb, new_mtu);
+}
+
+static int hns_ae_start(struct hnae_handle *handle)
+{
+       int ret;
+       struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
+
+       ret = hns_mac_vm_config_bc_en(mac_cb, 0, ENABLE);
+       if (ret)
+               return ret;
+
+       hns_ae_ring_enable_all(handle, 1);
+       msleep(100);
+
+       hns_mac_start(mac_cb);
+
+       return 0;
+}
+
+void hns_ae_stop(struct hnae_handle *handle)
+{
+       struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
+
+       /* just clean tx fbd, neednot rx fbd*/
+       hns_rcb_wait_fbd_clean(handle->qs, handle->q_num, RCB_INT_FLAG_TX);
+
+       msleep(20);
+
+       hns_mac_stop(mac_cb);
+
+       usleep_range(10000, 20000);
+
+       hns_ae_ring_enable_all(handle, 0);
+
+       (void)hns_mac_vm_config_bc_en(mac_cb, 0, DISABLE);
+}
+
+static void hns_ae_reset(struct hnae_handle *handle)
+{
+       struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle);
+
+       if (vf_cb->mac_cb->mac_type == HNAE_PORT_DEBUG) {
+               u8 ppe_common_index =
+                       vf_cb->port_index - DSAF_SERVICE_PORT_NUM_PER_DSAF + 1;
+
+               hns_mac_reset(vf_cb->mac_cb);
+               hns_ppe_reset_common(vf_cb->dsaf_dev, ppe_common_index);
+       }
+}
+
+void hns_ae_toggle_ring_irq(struct hnae_ring *ring, u32 mask)
+{
+       u32 flag;
+
+       if (is_tx_ring(ring))
+               flag = RCB_INT_FLAG_TX;
+       else
+               flag = RCB_INT_FLAG_RX;
+
+       hns_rcb_int_clr_hw(ring->q, flag);
+       hns_rcb_int_ctrl_hw(ring->q, flag, mask);
+}
+
+static void hns_ae_toggle_queue_status(struct hnae_queue *queue, u32 val)
+{
+       hns_rcb_start(queue, val);
+}
+
+static int hns_ae_get_link_status(struct hnae_handle *handle)
+{
+       u32 link_status;
+       struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
+
+       hns_mac_get_link_status(mac_cb, &link_status);
+
+       return !!link_status;
+}
+
+static int hns_ae_get_mac_info(struct hnae_handle *handle,
+                              u8 *auto_neg, u16 *speed, u8 *duplex)
+{
+       struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
+
+       return hns_mac_get_port_info(mac_cb, auto_neg, speed, duplex);
+}
+
+static void hns_ae_adjust_link(struct hnae_handle *handle, int speed,
+                              int duplex)
+{
+       struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
+
+       hns_mac_adjust_link(mac_cb, speed, duplex);
+}
+
+static void hns_ae_get_ring_bdnum_limit(struct hnae_queue *queue,
+                                       u32 *uplimit)
+{
+       *uplimit = HNS_RCB_RING_MAX_PENDING_BD;
+}
+
+static void hns_ae_get_pauseparam(struct hnae_handle *handle,
+                                 u32 *auto_neg, u32 *rx_en, u32 *tx_en)
+{
+       assert(handle);
+
+       hns_mac_get_autoneg(hns_get_mac_cb(handle), auto_neg);
+
+       hns_mac_get_pauseparam(hns_get_mac_cb(handle), rx_en, tx_en);
+}
+
+static int hns_ae_set_autoneg(struct hnae_handle *handle, u8 enable)
+{
+       assert(handle);
+
+       return hns_mac_set_autoneg(hns_get_mac_cb(handle), enable);
+}
+
+static void hns_ae_set_promisc_mode(struct hnae_handle *handle, u32 en)
+{
+       hns_dsaf_set_promisc_mode(hns_ae_get_dsaf_dev(handle->dev), en);
+}
+
+static int hns_ae_get_autoneg(struct hnae_handle *handle)
+{
+       u32     auto_neg;
+
+       assert(handle);
+
+       hns_mac_get_autoneg(hns_get_mac_cb(handle), &auto_neg);
+
+       return auto_neg;
+}
+
+static int hns_ae_set_pauseparam(struct hnae_handle *handle,
+                                u32 autoneg, u32 rx_en, u32 tx_en)
+{
+       struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
+       int ret;
+
+       ret = hns_mac_set_autoneg(mac_cb, autoneg);
+       if (ret)
+               return ret;
+
+       return hns_mac_set_pauseparam(mac_cb, rx_en, tx_en);
+}
+
+static void hns_ae_get_coalesce_usecs(struct hnae_handle *handle,
+                                     u32 *tx_usecs, u32 *rx_usecs)
+{
+       int port;
+
+       port = hns_ae_map_eport_to_dport(handle->eport_id);
+
+       *tx_usecs = hns_rcb_get_coalesce_usecs(
+               hns_ae_get_dsaf_dev(handle->dev),
+               hns_dsaf_get_comm_idx_by_port(port));
+       *rx_usecs = hns_rcb_get_coalesce_usecs(
+               hns_ae_get_dsaf_dev(handle->dev),
+               hns_dsaf_get_comm_idx_by_port(port));
+}
+
+static void hns_ae_get_rx_max_coalesced_frames(struct hnae_handle *handle,
+                                              u32 *tx_frames, u32 *rx_frames)
+{
+       int port;
+
+       assert(handle);
+
+       port = hns_ae_map_eport_to_dport(handle->eport_id);
+
+       *tx_frames = hns_rcb_get_coalesced_frames(
+               hns_ae_get_dsaf_dev(handle->dev), port);
+       *rx_frames = hns_rcb_get_coalesced_frames(
+               hns_ae_get_dsaf_dev(handle->dev), port);
+}
+
+static void hns_ae_set_coalesce_usecs(struct hnae_handle *handle,
+                                     u32 timeout)
+{
+       int port;
+
+       assert(handle);
+
+       port = hns_ae_map_eport_to_dport(handle->eport_id);
+
+       hns_rcb_set_coalesce_usecs(hns_ae_get_dsaf_dev(handle->dev),
+                                  port, timeout);
+}
+
+static int  hns_ae_set_coalesce_frames(struct hnae_handle *handle,
+                                      u32 coalesce_frames)
+{
+       int port;
+       int ret;
+
+       assert(handle);
+
+       port = hns_ae_map_eport_to_dport(handle->eport_id);
+
+       ret = hns_rcb_set_coalesced_frames(hns_ae_get_dsaf_dev(handle->dev),
+                                          port, coalesce_frames);
+       return ret;
+}
+
+void hns_ae_update_stats(struct hnae_handle *handle,
+                        struct net_device_stats *net_stats)
+{
+       int port;
+       int idx;
+       struct dsaf_device *dsaf_dev;
+       struct hns_mac_cb *mac_cb;
+       struct hns_ppe_cb *ppe_cb;
+       struct hnae_queue *queue;
+       struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle);
+       u64 tx_bytes = 0, rx_bytes = 0, tx_packets = 0, rx_packets = 0;
+       u64 rx_errors = 0, tx_errors = 0, tx_dropped = 0;
+       u64 rx_missed_errors = 0;
+
+       dsaf_dev = hns_ae_get_dsaf_dev(handle->dev);
+       if (!dsaf_dev)
+               return;
+       port = vf_cb->port_index;
+       ppe_cb = hns_get_ppe_cb(handle);
+       mac_cb = hns_get_mac_cb(handle);
+
+       for (idx = 0; idx < handle->q_num; idx++) {
+               queue = handle->qs[idx];
+               hns_rcb_update_stats(queue);
+
+               tx_bytes += queue->tx_ring.stats.tx_bytes;
+               tx_packets += queue->tx_ring.stats.tx_pkts;
+               rx_bytes += queue->rx_ring.stats.rx_bytes;
+               rx_packets += queue->rx_ring.stats.rx_pkts;
+
+               rx_errors += queue->rx_ring.stats.err_pkt_len
+                               + queue->rx_ring.stats.l2_err
+                               + queue->rx_ring.stats.l3l4_csum_err;
+       }
+
+       hns_ppe_update_stats(ppe_cb);
+       rx_missed_errors = ppe_cb->hw_stats.rx_drop_no_buf;
+       tx_errors += ppe_cb->hw_stats.tx_err_checksum
+               + ppe_cb->hw_stats.tx_err_fifo_empty;
+
+       if (mac_cb->mac_type == HNAE_PORT_SERVICE) {
+               hns_dsaf_update_stats(dsaf_dev, port);
+               /* for port upline direction, i.e., rx. */
+               rx_missed_errors += dsaf_dev->hw_stats[port].bp_drop;
+               rx_missed_errors += dsaf_dev->hw_stats[port].pad_drop;
+               rx_missed_errors += dsaf_dev->hw_stats[port].crc_false;
+
+               /* for port downline direction, i.e., tx. */
+               port = port + DSAF_PPE_INODE_BASE;
+               hns_dsaf_update_stats(dsaf_dev, port);
+               tx_dropped += dsaf_dev->hw_stats[port].bp_drop;
+               tx_dropped += dsaf_dev->hw_stats[port].pad_drop;
+               tx_dropped += dsaf_dev->hw_stats[port].crc_false;
+               tx_dropped += dsaf_dev->hw_stats[port].rslt_drop;
+               tx_dropped += dsaf_dev->hw_stats[port].vlan_drop;
+               tx_dropped += dsaf_dev->hw_stats[port].stp_drop;
+       }
+
+       hns_mac_update_stats(mac_cb);
+       rx_errors += mac_cb->hw_stats.rx_fifo_overrun_err;
+
+       tx_errors += mac_cb->hw_stats.tx_bad_pkts
+               + mac_cb->hw_stats.tx_fragment_err
+               + mac_cb->hw_stats.tx_jabber_err
+               + mac_cb->hw_stats.tx_underrun_err
+               + mac_cb->hw_stats.tx_crc_err;
+
+       net_stats->tx_bytes = tx_bytes;
+       net_stats->tx_packets = tx_packets;
+       net_stats->rx_bytes = rx_bytes;
+       net_stats->rx_dropped = 0;
+       net_stats->rx_packets = rx_packets;
+       net_stats->rx_errors = rx_errors;
+       net_stats->tx_errors = tx_errors;
+       net_stats->tx_dropped = tx_dropped;
+       net_stats->rx_missed_errors = rx_missed_errors;
+       net_stats->rx_crc_errors = mac_cb->hw_stats.rx_fcs_err;
+       net_stats->rx_frame_errors = mac_cb->hw_stats.rx_align_err;
+       net_stats->rx_fifo_errors = mac_cb->hw_stats.rx_fifo_overrun_err;
+       net_stats->rx_length_errors = mac_cb->hw_stats.rx_len_err;
+       net_stats->multicast = mac_cb->hw_stats.rx_mc_pkts;
+}
+
+void hns_ae_get_stats(struct hnae_handle *handle, u64 *data)
+{
+       int idx;
+       struct hns_mac_cb *mac_cb;
+       struct hns_ppe_cb *ppe_cb;
+       u64 *p = data;
+       struct  hnae_vf_cb *vf_cb;
+
+       if (!handle || !data) {
+               pr_err("hns_ae_get_stats NULL handle or data pointer!\n");
+               return;
+       }
+
+       vf_cb = hns_ae_get_vf_cb(handle);
+       mac_cb = hns_get_mac_cb(handle);
+       ppe_cb = hns_get_ppe_cb(handle);
+
+       for (idx = 0; idx < handle->q_num; idx++) {
+               hns_rcb_get_stats(handle->qs[idx], p);
+               p += hns_rcb_get_ring_sset_count((int)ETH_SS_STATS);
+       }
+
+       hns_ppe_get_stats(ppe_cb, p);
+       p += hns_ppe_get_sset_count((int)ETH_SS_STATS);
+
+       hns_mac_get_stats(mac_cb, p);
+       p += hns_mac_get_sset_count(mac_cb, (int)ETH_SS_STATS);
+
+       if (mac_cb->mac_type == HNAE_PORT_SERVICE)
+               hns_dsaf_get_stats(vf_cb->dsaf_dev, p, vf_cb->port_index);
+}
+
+void hns_ae_get_strings(struct hnae_handle *handle,
+                       u32 stringset, u8 *data)
+{
+       int port;
+       int idx;
+       struct hns_mac_cb *mac_cb;
+       struct hns_ppe_cb *ppe_cb;
+       u8 *p = data;
+       struct  hnae_vf_cb *vf_cb;
+
+       assert(handle);
+
+       vf_cb = hns_ae_get_vf_cb(handle);
+       port = vf_cb->port_index;
+       mac_cb = hns_get_mac_cb(handle);
+       ppe_cb = hns_get_ppe_cb(handle);
+
+       for (idx = 0; idx < handle->q_num; idx++) {
+               hns_rcb_get_strings(stringset, p, idx);
+               p += ETH_GSTRING_LEN * hns_rcb_get_ring_sset_count(stringset);
+       }
+
+       hns_ppe_get_strings(ppe_cb, stringset, p);
+       p += ETH_GSTRING_LEN * hns_ppe_get_sset_count(stringset);
+
+       hns_mac_get_strings(mac_cb, stringset, p);
+       p += ETH_GSTRING_LEN * hns_mac_get_sset_count(mac_cb, stringset);
+
+       if (mac_cb->mac_type == HNAE_PORT_SERVICE)
+               hns_dsaf_get_strings(stringset, p, port);
+}
+
+int hns_ae_get_sset_count(struct hnae_handle *handle, int stringset)
+{
+       u32 sset_count = 0;
+       struct hns_mac_cb *mac_cb;
+
+       assert(handle);
+
+       mac_cb = hns_get_mac_cb(handle);
+
+       sset_count += hns_rcb_get_ring_sset_count(stringset) * handle->q_num;
+       sset_count += hns_ppe_get_sset_count(stringset);
+       sset_count += hns_mac_get_sset_count(mac_cb, stringset);
+
+       if (mac_cb->mac_type == HNAE_PORT_SERVICE)
+               sset_count += hns_dsaf_get_sset_count(stringset);
+
+       return sset_count;
+}
+
+static int hns_ae_config_loopback(struct hnae_handle *handle,
+                                 enum hnae_loop loop, int en)
+{
+       int ret;
+       struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle);
+
+       switch (loop) {
+       case MAC_INTERNALLOOP_SERDES:
+               ret = hns_mac_config_sds_loopback(vf_cb->mac_cb, en);
+               break;
+       case MAC_INTERNALLOOP_MAC:
+               ret = hns_mac_config_mac_loopback(vf_cb->mac_cb, loop, en);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+void hns_ae_update_led_status(struct hnae_handle *handle)
+{
+       struct hns_mac_cb *mac_cb;
+
+       assert(handle);
+       mac_cb = hns_get_mac_cb(handle);
+       if (!mac_cb->cpld_vaddr)
+               return;
+       hns_set_led_opt(mac_cb);
+}
+
+int hns_ae_cpld_set_led_id(struct hnae_handle *handle,
+                          enum hnae_led_state status)
+{
+       struct hns_mac_cb *mac_cb;
+
+       assert(handle);
+
+       mac_cb = hns_get_mac_cb(handle);
+
+       return hns_cpld_led_set_id(mac_cb, status);
+}
+
+void hns_ae_get_regs(struct hnae_handle *handle, void *data)
+{
+       u32 *p = data;
+       u32 rcb_com_idx;
+       int i;
+       struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle);
+       struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
+
+       hns_ppe_get_regs(ppe_cb, p);
+       p += hns_ppe_get_regs_count();
+
+       rcb_com_idx = hns_dsaf_get_comm_idx_by_port(vf_cb->port_index);
+       hns_rcb_get_common_regs(vf_cb->dsaf_dev->rcb_common[rcb_com_idx], p);
+       p += hns_rcb_get_common_regs_count();
+
+       for (i = 0; i < handle->q_num; i++) {
+               hns_rcb_get_ring_regs(handle->qs[i], p);
+               p += hns_rcb_get_ring_regs_count();
+       }
+
+       hns_mac_get_regs(vf_cb->mac_cb, p);
+       p += hns_mac_get_regs_count(vf_cb->mac_cb);
+
+       if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE)
+               hns_dsaf_get_regs(vf_cb->dsaf_dev, vf_cb->port_index, p);
+}
+
+int hns_ae_get_regs_len(struct hnae_handle *handle)
+{
+       u32 total_num;
+       struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle);
+
+       total_num = hns_ppe_get_regs_count();
+       total_num += hns_rcb_get_common_regs_count();
+       total_num += hns_rcb_get_ring_regs_count() * handle->q_num;
+       total_num += hns_mac_get_regs_count(vf_cb->mac_cb);
+
+       if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE)
+               total_num += hns_dsaf_get_regs_count();
+
+       return total_num;
+}
+
+static struct hnae_ae_ops hns_dsaf_ops = {
+       .get_handle = hns_ae_get_handle,
+       .put_handle = hns_ae_put_handle,
+       .init_queue = hns_ae_init_queue,
+       .fini_queue = hns_ae_fini_queue,
+       .start = hns_ae_start,
+       .stop = hns_ae_stop,
+       .reset = hns_ae_reset,
+       .toggle_ring_irq = hns_ae_toggle_ring_irq,
+       .toggle_queue_status = hns_ae_toggle_queue_status,
+       .get_status = hns_ae_get_link_status,
+       .get_info = hns_ae_get_mac_info,
+       .adjust_link = hns_ae_adjust_link,
+       .set_loopback = hns_ae_config_loopback,
+       .get_ring_bdnum_limit = hns_ae_get_ring_bdnum_limit,
+       .get_pauseparam = hns_ae_get_pauseparam,
+       .set_autoneg = hns_ae_set_autoneg,
+       .get_autoneg = hns_ae_get_autoneg,
+       .set_pauseparam = hns_ae_set_pauseparam,
+       .get_coalesce_usecs = hns_ae_get_coalesce_usecs,
+       .get_rx_max_coalesced_frames = hns_ae_get_rx_max_coalesced_frames,
+       .set_coalesce_usecs = hns_ae_set_coalesce_usecs,
+       .set_coalesce_frames = hns_ae_set_coalesce_frames,
+       .set_promisc_mode = hns_ae_set_promisc_mode,
+       .set_mac_addr = hns_ae_set_mac_address,
+       .set_mc_addr = hns_ae_set_multicast_one,
+       .set_mtu = hns_ae_set_mtu,
+       .update_stats = hns_ae_update_stats,
+       .get_stats = hns_ae_get_stats,
+       .get_strings = hns_ae_get_strings,
+       .get_sset_count = hns_ae_get_sset_count,
+       .update_led_status = hns_ae_update_led_status,
+       .set_led_id = hns_ae_cpld_set_led_id,
+       .get_regs = hns_ae_get_regs,
+       .get_regs_len = hns_ae_get_regs_len
+};
+
+int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev)
+{
+       struct hnae_ae_dev *ae_dev = &dsaf_dev->ae_dev;
+
+       ae_dev->ops = &hns_dsaf_ops;
+       ae_dev->dev = dsaf_dev->dev;
+
+       return hnae_ae_register(ae_dev, THIS_MODULE);
+}
+
+void hns_dsaf_ae_uninit(struct dsaf_device *dsaf_dev)
+{
+       hnae_ae_unregister(&dsaf_dev->ae_dev);
+}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c
new file mode 100644 (file)
index 0000000..b8517b0
--- /dev/null
@@ -0,0 +1,704 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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/delay.h>
+#include <linux/of_mdio.h>
+#include "hns_dsaf_main.h"
+#include "hns_dsaf_mac.h"
+#include "hns_dsaf_gmac.h"
+
+static const struct mac_stats_string g_gmac_stats_string[] = {
+       {"gmac_rx_octets_total_ok", MAC_STATS_FIELD_OFF(rx_good_bytes)},
+       {"gmac_rx_octets_bad", MAC_STATS_FIELD_OFF(rx_bad_bytes)},
+       {"gmac_rx_uc_pkts", MAC_STATS_FIELD_OFF(rx_uc_pkts)},
+       {"gamc_rx_mc_pkts", MAC_STATS_FIELD_OFF(rx_mc_pkts)},
+       {"gmac_rx_bc_pkts", MAC_STATS_FIELD_OFF(rx_bc_pkts)},
+       {"gmac_rx_pkts_64octets", MAC_STATS_FIELD_OFF(rx_64bytes)},
+       {"gmac_rx_pkts_65to127", MAC_STATS_FIELD_OFF(rx_65to127)},
+       {"gmac_rx_pkts_128to255", MAC_STATS_FIELD_OFF(rx_128to255)},
+       {"gmac_rx_pkts_256to511", MAC_STATS_FIELD_OFF(rx_256to511)},
+       {"gmac_rx_pkts_512to1023", MAC_STATS_FIELD_OFF(rx_512to1023)},
+       {"gmac_rx_pkts_1024to1518", MAC_STATS_FIELD_OFF(rx_1024to1518)},
+       {"gmac_rx_pkts_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax)},
+       {"gmac_rx_fcs_errors", MAC_STATS_FIELD_OFF(rx_fcs_err)},
+       {"gmac_rx_tagged", MAC_STATS_FIELD_OFF(rx_vlan_pkts)},
+       {"gmac_rx_data_err", MAC_STATS_FIELD_OFF(rx_data_err)},
+       {"gmac_rx_align_errors", MAC_STATS_FIELD_OFF(rx_align_err)},
+       {"gmac_rx_long_errors", MAC_STATS_FIELD_OFF(rx_oversize)},
+       {"gmac_rx_jabber_errors", MAC_STATS_FIELD_OFF(rx_jabber_err)},
+       {"gmac_rx_pause_maccontrol", MAC_STATS_FIELD_OFF(rx_pfc_tc0)},
+       {"gmac_rx_unknown_maccontrol", MAC_STATS_FIELD_OFF(rx_unknown_ctrl)},
+       {"gmac_rx_very_long_err", MAC_STATS_FIELD_OFF(rx_long_err)},
+       {"gmac_rx_runt_err", MAC_STATS_FIELD_OFF(rx_minto64)},
+       {"gmac_rx_short_err", MAC_STATS_FIELD_OFF(rx_under_min)},
+       {"gmac_rx_filt_pkt", MAC_STATS_FIELD_OFF(rx_filter_bytes)},
+       {"gmac_rx_octets_total_filt", MAC_STATS_FIELD_OFF(rx_filter_pkts)},
+       {"gmac_rx_overrun_cnt", MAC_STATS_FIELD_OFF(rx_fifo_overrun_err)},
+       {"gmac_rx_length_err", MAC_STATS_FIELD_OFF(rx_len_err)},
+       {"gmac_rx_fail_comma", MAC_STATS_FIELD_OFF(rx_comma_err)},
+
+       {"gmac_tx_octets_ok", MAC_STATS_FIELD_OFF(tx_good_bytes)},
+       {"gmac_tx_octets_bad", MAC_STATS_FIELD_OFF(tx_bad_bytes)},
+       {"gmac_tx_uc_pkts", MAC_STATS_FIELD_OFF(tx_uc_pkts)},
+       {"gmac_tx_mc_pkts", MAC_STATS_FIELD_OFF(tx_mc_pkts)},
+       {"gmac_tx_bc_pkts", MAC_STATS_FIELD_OFF(tx_bc_pkts)},
+       {"gmac_tx_pkts_64octets", MAC_STATS_FIELD_OFF(tx_64bytes)},
+       {"gmac_tx_pkts_65to127", MAC_STATS_FIELD_OFF(tx_65to127)},
+       {"gmac_tx_pkts_128to255", MAC_STATS_FIELD_OFF(tx_128to255)},
+       {"gmac_tx_pkts_256to511", MAC_STATS_FIELD_OFF(tx_256to511)},
+       {"gmac_tx_pkts_512to1023", MAC_STATS_FIELD_OFF(tx_512to1023)},
+       {"gmac_tx_pkts_1024to1518", MAC_STATS_FIELD_OFF(tx_1024to1518)},
+       {"gmac_tx_pkts_1519tomax", MAC_STATS_FIELD_OFF(tx_1519tomax)},
+       {"gmac_tx_excessive_length_drop", MAC_STATS_FIELD_OFF(tx_jabber_err)},
+       {"gmac_tx_underrun", MAC_STATS_FIELD_OFF(tx_underrun_err)},
+       {"gmac_tx_tagged", MAC_STATS_FIELD_OFF(tx_vlan)},
+       {"gmac_tx_crc_error", MAC_STATS_FIELD_OFF(tx_crc_err)},
+       {"gmac_tx_pause_frames", MAC_STATS_FIELD_OFF(tx_pfc_tc0)}
+};
+
+static void hns_gmac_enable(void *mac_drv, enum mac_commom_mode mode)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       /*enable GE rX/tX */
+       if ((mode == MAC_COMM_MODE_TX) || (mode == MAC_COMM_MODE_RX_AND_TX))
+               dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_TX_EN_B, 1);
+
+       if ((mode == MAC_COMM_MODE_RX) || (mode == MAC_COMM_MODE_RX_AND_TX))
+               dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 1);
+}
+
+static void hns_gmac_disable(void *mac_drv, enum mac_commom_mode mode)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       /*disable GE rX/tX */
+       if ((mode == MAC_COMM_MODE_TX) || (mode == MAC_COMM_MODE_RX_AND_TX))
+               dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_TX_EN_B, 0);
+
+       if ((mode == MAC_COMM_MODE_RX) || (mode == MAC_COMM_MODE_RX_AND_TX))
+               dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 0);
+}
+
+/**
+*hns_gmac_get_en - get port enable
+*@mac_drv:mac device
+*@rx:rx enable
+*@tx:tx enable
+*/
+static void hns_gmac_get_en(void *mac_drv, u32 *rx, u32 *tx)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       u32 porten;
+
+       porten = dsaf_read_dev(drv, GMAC_PORT_EN_REG);
+       *tx = dsaf_get_bit(porten, GMAC_PORT_TX_EN_B);
+       *rx = dsaf_get_bit(porten, GMAC_PORT_RX_EN_B);
+}
+
+static void hns_gmac_free(void *mac_drv)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       struct dsaf_device *dsaf_dev
+               = (struct dsaf_device *)dev_get_drvdata(drv->dev);
+
+       u32 mac_id = drv->mac_id;
+
+       hns_dsaf_ge_srst_by_port(dsaf_dev, mac_id, 0);
+}
+
+static void hns_gmac_set_tx_auto_pause_frames(void *mac_drv, u16 newval)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       dsaf_set_dev_field(drv, GMAC_FC_TX_TIMER_REG, GMAC_FC_TX_TIMER_M,
+                          GMAC_FC_TX_TIMER_S, newval);
+}
+
+static void hns_gmac_get_tx_auto_pause_frames(void *mac_drv, u16 *newval)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       *newval = dsaf_get_dev_field(drv, GMAC_FC_TX_TIMER_REG,
+                                    GMAC_FC_TX_TIMER_M, GMAC_FC_TX_TIMER_S);
+}
+
+static void hns_gmac_set_rx_auto_pause_frames(void *mac_drv, u32 newval)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       dsaf_set_dev_bit(drv, GMAC_PAUSE_EN_REG,
+                        GMAC_PAUSE_EN_RX_FDFC_B, !!newval);
+}
+
+static void hns_gmac_config_max_frame_length(void *mac_drv, u16 newval)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       dsaf_set_dev_field(drv, GMAC_MAX_FRM_SIZE_REG, GMAC_MAX_FRM_SIZE_M,
+                          GMAC_MAX_FRM_SIZE_S, newval);
+
+       dsaf_set_dev_field(drv, GAMC_RX_MAX_FRAME, GMAC_MAX_FRM_SIZE_M,
+                          GMAC_MAX_FRM_SIZE_S, newval);
+}
+
+static void hns_gmac_config_an_mode(void *mac_drv, u8 newval)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       dsaf_set_dev_bit(drv, GMAC_TRANSMIT_CONTROL_REG,
+                        GMAC_TX_AN_EN_B, !!newval);
+}
+
+static void hns_gmac_tx_loop_pkt_dis(void *mac_drv)
+{
+       u32 tx_loop_pkt_pri;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       tx_loop_pkt_pri = dsaf_read_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG);
+       dsaf_set_bit(tx_loop_pkt_pri, GMAC_TX_LOOP_PKT_EN_B, 1);
+       dsaf_set_bit(tx_loop_pkt_pri, GMAC_TX_LOOP_PKT_HIG_PRI_B, 0);
+       dsaf_write_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG, tx_loop_pkt_pri);
+}
+
+static void hns_gmac_set_duplex_type(void *mac_drv, u8 newval)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       dsaf_set_dev_bit(drv, GMAC_DUPLEX_TYPE_REG,
+                        GMAC_DUPLEX_TYPE_B, !!newval);
+}
+
+static void hns_gmac_get_duplex_type(void *mac_drv,
+                                    enum hns_gmac_duplex_mdoe *duplex_mode)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       *duplex_mode = (enum hns_gmac_duplex_mdoe)dsaf_get_dev_bit(
+               drv, GMAC_DUPLEX_TYPE_REG, GMAC_DUPLEX_TYPE_B);
+}
+
+static void hns_gmac_get_port_mode(void *mac_drv, enum hns_port_mode *port_mode)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       *port_mode = (enum hns_port_mode)dsaf_get_dev_field(
+               drv, GMAC_PORT_MODE_REG, GMAC_PORT_MODE_M, GMAC_PORT_MODE_S);
+}
+
+static void hns_gmac_port_mode_get(void *mac_drv,
+                                  struct hns_gmac_port_mode_cfg *port_mode)
+{
+       u32 tx_ctrl;
+       u32 recv_ctrl;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       port_mode->port_mode = (enum hns_port_mode)dsaf_get_dev_field(
+               drv, GMAC_PORT_MODE_REG, GMAC_PORT_MODE_M, GMAC_PORT_MODE_S);
+
+       tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG);
+       recv_ctrl = dsaf_read_dev(drv, GMAC_RECV_CONTROL_REG);
+
+       port_mode->max_frm_size =
+               dsaf_get_dev_field(drv, GMAC_MAX_FRM_SIZE_REG,
+                                  GMAC_MAX_FRM_SIZE_M, GMAC_MAX_FRM_SIZE_S);
+       port_mode->short_runts_thr =
+               dsaf_get_dev_field(drv, GMAC_SHORT_RUNTS_THR_REG,
+                                  GMAC_SHORT_RUNTS_THR_M,
+                                  GMAC_SHORT_RUNTS_THR_S);
+
+       port_mode->pad_enable = dsaf_get_bit(tx_ctrl, GMAC_TX_PAD_EN_B);
+       port_mode->crc_add = dsaf_get_bit(tx_ctrl, GMAC_TX_CRC_ADD_B);
+       port_mode->an_enable = dsaf_get_bit(tx_ctrl, GMAC_TX_AN_EN_B);
+
+       port_mode->runt_pkt_en =
+               dsaf_get_bit(recv_ctrl, GMAC_RECV_CTRL_RUNT_PKT_EN_B);
+       port_mode->strip_pad_en =
+               dsaf_get_bit(recv_ctrl, GMAC_RECV_CTRL_STRIP_PAD_EN_B);
+}
+
+static void hns_gmac_pause_frm_cfg(void *mac_drv, u32 rx_pause_en,
+                                  u32 tx_pause_en)
+{
+       u32 pause_en;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       pause_en = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG);
+       dsaf_set_bit(pause_en, GMAC_PAUSE_EN_RX_FDFC_B, !!rx_pause_en);
+       dsaf_set_bit(pause_en, GMAC_PAUSE_EN_TX_FDFC_B, !!tx_pause_en);
+       dsaf_write_dev(drv, GMAC_PAUSE_EN_REG, pause_en);
+}
+
+static void hns_gmac_get_pausefrm_cfg(void *mac_drv, u32 *rx_pause_en,
+                                     u32 *tx_pause_en)
+{
+       u32 pause_en;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       pause_en = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG);
+
+       *rx_pause_en = dsaf_get_bit(pause_en, GMAC_PAUSE_EN_RX_FDFC_B);
+       *tx_pause_en = dsaf_get_bit(pause_en, GMAC_PAUSE_EN_TX_FDFC_B);
+}
+
+static int hns_gmac_adjust_link(void *mac_drv, enum mac_speed speed,
+                               u32 full_duplex)
+{
+       u32 tx_ctrl;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       dsaf_set_dev_bit(drv, GMAC_DUPLEX_TYPE_REG,
+                        GMAC_DUPLEX_TYPE_B, !!full_duplex);
+
+       switch (speed) {
+       case MAC_SPEED_10:
+               dsaf_set_dev_field(
+                       drv, GMAC_PORT_MODE_REG,
+                       GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x6);
+               break;
+       case MAC_SPEED_100:
+               dsaf_set_dev_field(
+                       drv, GMAC_PORT_MODE_REG,
+                       GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x7);
+               break;
+       case MAC_SPEED_1000:
+               dsaf_set_dev_field(
+                       drv, GMAC_PORT_MODE_REG,
+                       GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x8);
+               break;
+       default:
+               dev_err(drv->dev,
+                       "hns_gmac_adjust_link fail, speed%d mac%d\n",
+                       speed, drv->mac_id);
+               return -EINVAL;
+       }
+
+       tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG);
+       dsaf_set_bit(tx_ctrl, GMAC_TX_PAD_EN_B, 1);
+       dsaf_set_bit(tx_ctrl, GMAC_TX_CRC_ADD_B, 1);
+       dsaf_write_dev(drv, GMAC_TRANSMIT_CONTROL_REG, tx_ctrl);
+
+       dsaf_set_dev_bit(drv, GMAC_MODE_CHANGE_EN_REG,
+                        GMAC_MODE_CHANGE_EB_B, 1);
+
+       return 0;
+}
+
+static void hns_gmac_init(void *mac_drv)
+{
+       u32 port;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       struct dsaf_device *dsaf_dev
+               = (struct dsaf_device *)dev_get_drvdata(drv->dev);
+
+       port = drv->mac_id;
+
+       hns_dsaf_ge_srst_by_port(dsaf_dev, port, 0);
+       mdelay(10);
+       hns_dsaf_ge_srst_by_port(dsaf_dev, port, 1);
+       mdelay(10);
+       hns_gmac_disable(mac_drv, MAC_COMM_MODE_RX_AND_TX);
+       hns_gmac_tx_loop_pkt_dis(mac_drv);
+}
+
+void hns_gmac_update_stats(void *mac_drv)
+{
+       struct mac_hw_stats *hw_stats = NULL;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       hw_stats = &drv->mac_cb->hw_stats;
+
+       /* RX */
+       hw_stats->rx_good_bytes
+               += dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_OK_REG);
+       hw_stats->rx_bad_bytes
+               += dsaf_read_dev(drv, GMAC_RX_OCTETS_BAD_REG);
+       hw_stats->rx_uc_pkts += dsaf_read_dev(drv, GMAC_RX_UC_PKTS_REG);
+       hw_stats->rx_mc_pkts += dsaf_read_dev(drv, GMAC_RX_MC_PKTS_REG);
+       hw_stats->rx_bc_pkts += dsaf_read_dev(drv, GMAC_RX_BC_PKTS_REG);
+       hw_stats->rx_64bytes
+               += dsaf_read_dev(drv, GMAC_RX_PKTS_64OCTETS_REG);
+       hw_stats->rx_65to127
+               += dsaf_read_dev(drv, GMAC_RX_PKTS_65TO127OCTETS_REG);
+       hw_stats->rx_128to255
+               += dsaf_read_dev(drv, GMAC_RX_PKTS_128TO255OCTETS_REG);
+       hw_stats->rx_256to511
+               += dsaf_read_dev(drv, GMAC_RX_PKTS_255TO511OCTETS_REG);
+       hw_stats->rx_512to1023
+               += dsaf_read_dev(drv, GMAC_RX_PKTS_512TO1023OCTETS_REG);
+       hw_stats->rx_1024to1518
+               += dsaf_read_dev(drv, GMAC_RX_PKTS_1024TO1518OCTETS_REG);
+       hw_stats->rx_1519tomax
+               += dsaf_read_dev(drv, GMAC_RX_PKTS_1519TOMAXOCTETS_REG);
+       hw_stats->rx_fcs_err += dsaf_read_dev(drv, GMAC_RX_FCS_ERRORS_REG);
+       hw_stats->rx_vlan_pkts += dsaf_read_dev(drv, GMAC_RX_TAGGED_REG);
+       hw_stats->rx_data_err += dsaf_read_dev(drv, GMAC_RX_DATA_ERR_REG);
+       hw_stats->rx_align_err
+               += dsaf_read_dev(drv, GMAC_RX_ALIGN_ERRORS_REG);
+       hw_stats->rx_oversize
+               += dsaf_read_dev(drv, GMAC_RX_LONG_ERRORS_REG);
+       hw_stats->rx_jabber_err
+               += dsaf_read_dev(drv, GMAC_RX_JABBER_ERRORS_REG);
+       hw_stats->rx_pfc_tc0
+               += dsaf_read_dev(drv, GMAC_RX_PAUSE_MACCTRL_FRAM_REG);
+       hw_stats->rx_unknown_ctrl
+               += dsaf_read_dev(drv, GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG);
+       hw_stats->rx_long_err
+               += dsaf_read_dev(drv, GMAC_RX_VERY_LONG_ERR_CNT_REG);
+       hw_stats->rx_minto64
+               += dsaf_read_dev(drv, GMAC_RX_RUNT_ERR_CNT_REG);
+       hw_stats->rx_under_min
+               += dsaf_read_dev(drv, GMAC_RX_SHORT_ERR_CNT_REG);
+       hw_stats->rx_filter_pkts
+               += dsaf_read_dev(drv, GMAC_RX_FILT_PKT_CNT_REG);
+       hw_stats->rx_filter_bytes
+               += dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_FILT_REG);
+       hw_stats->rx_fifo_overrun_err
+               += dsaf_read_dev(drv, GMAC_RX_OVERRUN_CNT_REG);
+       hw_stats->rx_len_err
+               += dsaf_read_dev(drv, GMAC_RX_LENGTHFIELD_ERR_CNT_REG);
+       hw_stats->rx_comma_err
+               += dsaf_read_dev(drv, GMAC_RX_FAIL_COMMA_CNT_REG);
+
+       /* TX */
+       hw_stats->tx_good_bytes
+               += dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_OK_REG);
+       hw_stats->tx_bad_bytes
+               += dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_BAD_REG);
+       hw_stats->tx_uc_pkts += dsaf_read_dev(drv, GMAC_TX_UC_PKTS_REG);
+       hw_stats->tx_mc_pkts += dsaf_read_dev(drv, GMAC_TX_MC_PKTS_REG);
+       hw_stats->tx_bc_pkts += dsaf_read_dev(drv, GMAC_TX_BC_PKTS_REG);
+       hw_stats->tx_64bytes
+               += dsaf_read_dev(drv, GMAC_TX_PKTS_64OCTETS_REG);
+       hw_stats->tx_65to127
+               += dsaf_read_dev(drv, GMAC_TX_PKTS_65TO127OCTETS_REG);
+       hw_stats->tx_128to255
+               += dsaf_read_dev(drv, GMAC_TX_PKTS_128TO255OCTETS_REG);
+       hw_stats->tx_256to511
+               += dsaf_read_dev(drv, GMAC_TX_PKTS_255TO511OCTETS_REG);
+       hw_stats->tx_512to1023
+               += dsaf_read_dev(drv, GMAC_TX_PKTS_512TO1023OCTETS_REG);
+       hw_stats->tx_1024to1518
+               += dsaf_read_dev(drv, GMAC_TX_PKTS_1024TO1518OCTETS_REG);
+       hw_stats->tx_1519tomax
+               += dsaf_read_dev(drv, GMAC_TX_PKTS_1519TOMAXOCTETS_REG);
+       hw_stats->tx_jabber_err
+               += dsaf_read_dev(drv, GMAC_TX_EXCESSIVE_LENGTH_DROP_REG);
+       hw_stats->tx_underrun_err
+               += dsaf_read_dev(drv, GMAC_TX_UNDERRUN_REG);
+       hw_stats->tx_vlan += dsaf_read_dev(drv, GMAC_TX_TAGGED_REG);
+       hw_stats->tx_crc_err += dsaf_read_dev(drv, GMAC_TX_CRC_ERROR_REG);
+       hw_stats->tx_pfc_tc0
+               += dsaf_read_dev(drv, GMAC_TX_PAUSE_FRAMES_REG);
+}
+
+static void hns_gmac_set_mac_addr(void *mac_drv, char *mac_addr)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       if (drv->mac_id >= DSAF_SERVICE_NW_NUM) {
+               u32 high_val = mac_addr[1] | (mac_addr[0] << 8);
+
+               u32 low_val = mac_addr[5] | (mac_addr[4] << 8)
+                       | (mac_addr[3] << 16) | (mac_addr[2] << 24);
+               dsaf_write_dev(drv, GMAC_STATION_ADDR_LOW_2_REG, low_val);
+               dsaf_write_dev(drv, GMAC_STATION_ADDR_HIGH_2_REG, high_val);
+       }
+}
+
+static int hns_gmac_config_loopback(void *mac_drv, enum hnae_loop loop_mode,
+                                   u8 enable)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       switch (loop_mode) {
+       case MAC_INTERNALLOOP_MAC:
+               dsaf_set_dev_bit(drv, GMAC_LOOP_REG, GMAC_LP_REG_CF2MI_LP_EN_B,
+                                !!enable);
+               break;
+       default:
+               dev_err(drv->dev, "loop_mode error\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void hns_gmac_config_pad_and_crc(void *mac_drv, u8 newval)
+{
+       u32 tx_ctrl;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG);
+       dsaf_set_bit(tx_ctrl, GMAC_TX_PAD_EN_B, !!newval);
+       dsaf_set_bit(tx_ctrl, GMAC_TX_CRC_ADD_B, !!newval);
+       dsaf_write_dev(drv, GMAC_TRANSMIT_CONTROL_REG, tx_ctrl);
+}
+
+static void hns_gmac_get_id(void *mac_drv, u8 *mac_id)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       *mac_id = drv->mac_id;
+}
+
+static void hns_gmac_get_info(void *mac_drv, struct mac_info *mac_info)
+{
+       enum hns_gmac_duplex_mdoe duplex;
+       enum hns_port_mode speed;
+       u32 rx_pause;
+       u32 tx_pause;
+       u32 rx;
+       u32 tx;
+       u16 fc_tx_timer;
+       struct hns_gmac_port_mode_cfg port_mode = { GMAC_10M_MII, 0 };
+
+       hns_gmac_port_mode_get(mac_drv, &port_mode);
+       mac_info->pad_and_crc_en = port_mode.crc_add && port_mode.pad_enable;
+       mac_info->auto_neg = port_mode.an_enable;
+
+       hns_gmac_get_tx_auto_pause_frames(mac_drv, &fc_tx_timer);
+       mac_info->tx_pause_time = fc_tx_timer;
+
+       hns_gmac_get_en(mac_drv, &rx, &tx);
+       mac_info->port_en = rx && tx;
+
+       hns_gmac_get_duplex_type(mac_drv, &duplex);
+       mac_info->duplex = duplex;
+
+       hns_gmac_get_port_mode(mac_drv, &speed);
+       switch (speed) {
+       case GMAC_10M_SGMII:
+               mac_info->speed = MAC_SPEED_10;
+               break;
+       case GMAC_100M_SGMII:
+               mac_info->speed = MAC_SPEED_100;
+               break;
+       case GMAC_1000M_SGMII:
+               mac_info->speed = MAC_SPEED_1000;
+               break;
+       default:
+               mac_info->speed = 0;
+               break;
+       }
+
+       hns_gmac_get_pausefrm_cfg(mac_drv, &rx_pause, &tx_pause);
+       mac_info->rx_pause_en = rx_pause;
+       mac_info->tx_pause_en = tx_pause;
+}
+
+static void hns_gmac_autoneg_stat(void *mac_drv, u32 *enable)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       *enable = dsaf_get_dev_bit(drv, GMAC_TRANSMIT_CONTROL_REG,
+                                  GMAC_TX_AN_EN_B);
+}
+
+static void hns_gmac_get_link_status(void *mac_drv, u32 *link_stat)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       *link_stat = dsaf_get_dev_bit(drv, GMAC_AN_NEG_STATE_REG,
+                                     GMAC_AN_NEG_STAT_RX_SYNC_OK_B);
+}
+
+static void hns_gmac_get_regs(void *mac_drv, void *data)
+{
+       u32 *regs = data;
+       int i;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       /* base config registers */
+       regs[0] = dsaf_read_dev(drv, GMAC_DUPLEX_TYPE_REG);
+       regs[1] = dsaf_read_dev(drv, GMAC_FD_FC_TYPE_REG);
+       regs[2] = dsaf_read_dev(drv, GMAC_FC_TX_TIMER_REG);
+       regs[3] = dsaf_read_dev(drv, GMAC_FD_FC_ADDR_LOW_REG);
+       regs[4] = dsaf_read_dev(drv, GMAC_FD_FC_ADDR_HIGH_REG);
+       regs[5] = dsaf_read_dev(drv, GMAC_IPG_TX_TIMER_REG);
+       regs[6] = dsaf_read_dev(drv, GMAC_PAUSE_THR_REG);
+       regs[7] = dsaf_read_dev(drv, GMAC_MAX_FRM_SIZE_REG);
+       regs[8] = dsaf_read_dev(drv, GMAC_PORT_MODE_REG);
+       regs[9] = dsaf_read_dev(drv, GMAC_PORT_EN_REG);
+       regs[10] = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG);
+       regs[11] = dsaf_read_dev(drv, GMAC_SHORT_RUNTS_THR_REG);
+       regs[12] = dsaf_read_dev(drv, GMAC_AN_NEG_STATE_REG);
+       regs[13] = dsaf_read_dev(drv, GMAC_TX_LOCAL_PAGE_REG);
+       regs[14] = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG);
+       regs[15] = dsaf_read_dev(drv, GMAC_REC_FILT_CONTROL_REG);
+       regs[16] = dsaf_read_dev(drv, GMAC_PTP_CONFIG_REG);
+
+       /* rx static registers */
+       regs[17] = dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_OK_REG);
+       regs[18] = dsaf_read_dev(drv, GMAC_RX_OCTETS_BAD_REG);
+       regs[19] = dsaf_read_dev(drv, GMAC_RX_UC_PKTS_REG);
+       regs[20] = dsaf_read_dev(drv, GMAC_RX_MC_PKTS_REG);
+       regs[21] = dsaf_read_dev(drv, GMAC_RX_BC_PKTS_REG);
+       regs[22] = dsaf_read_dev(drv, GMAC_RX_PKTS_64OCTETS_REG);
+       regs[23] = dsaf_read_dev(drv, GMAC_RX_PKTS_65TO127OCTETS_REG);
+       regs[24] = dsaf_read_dev(drv, GMAC_RX_PKTS_128TO255OCTETS_REG);
+       regs[25] = dsaf_read_dev(drv, GMAC_RX_PKTS_255TO511OCTETS_REG);
+       regs[26] = dsaf_read_dev(drv, GMAC_RX_PKTS_512TO1023OCTETS_REG);
+       regs[27] = dsaf_read_dev(drv, GMAC_RX_PKTS_1024TO1518OCTETS_REG);
+       regs[28] = dsaf_read_dev(drv, GMAC_RX_PKTS_1519TOMAXOCTETS_REG);
+       regs[29] = dsaf_read_dev(drv, GMAC_RX_FCS_ERRORS_REG);
+       regs[30] = dsaf_read_dev(drv, GMAC_RX_TAGGED_REG);
+       regs[31] = dsaf_read_dev(drv, GMAC_RX_DATA_ERR_REG);
+       regs[32] = dsaf_read_dev(drv, GMAC_RX_ALIGN_ERRORS_REG);
+       regs[33] = dsaf_read_dev(drv, GMAC_RX_LONG_ERRORS_REG);
+       regs[34] = dsaf_read_dev(drv, GMAC_RX_JABBER_ERRORS_REG);
+       regs[35] = dsaf_read_dev(drv, GMAC_RX_PAUSE_MACCTRL_FRAM_REG);
+       regs[36] = dsaf_read_dev(drv, GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG);
+       regs[37] = dsaf_read_dev(drv, GMAC_RX_VERY_LONG_ERR_CNT_REG);
+       regs[38] = dsaf_read_dev(drv, GMAC_RX_RUNT_ERR_CNT_REG);
+       regs[39] = dsaf_read_dev(drv, GMAC_RX_SHORT_ERR_CNT_REG);
+       regs[40] = dsaf_read_dev(drv, GMAC_RX_FILT_PKT_CNT_REG);
+       regs[41] = dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_FILT_REG);
+
+       /* tx static registers */
+       regs[42] = dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_OK_REG);
+       regs[43] = dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_BAD_REG);
+       regs[44] = dsaf_read_dev(drv, GMAC_TX_UC_PKTS_REG);
+       regs[45] = dsaf_read_dev(drv, GMAC_TX_MC_PKTS_REG);
+       regs[46] = dsaf_read_dev(drv, GMAC_TX_BC_PKTS_REG);
+       regs[47] = dsaf_read_dev(drv, GMAC_TX_PKTS_64OCTETS_REG);
+       regs[48] = dsaf_read_dev(drv, GMAC_TX_PKTS_65TO127OCTETS_REG);
+       regs[49] = dsaf_read_dev(drv, GMAC_TX_PKTS_128TO255OCTETS_REG);
+       regs[50] = dsaf_read_dev(drv, GMAC_TX_PKTS_255TO511OCTETS_REG);
+       regs[51] = dsaf_read_dev(drv, GMAC_TX_PKTS_512TO1023OCTETS_REG);
+       regs[52] = dsaf_read_dev(drv, GMAC_TX_PKTS_1024TO1518OCTETS_REG);
+       regs[53] = dsaf_read_dev(drv, GMAC_TX_PKTS_1519TOMAXOCTETS_REG);
+       regs[54] = dsaf_read_dev(drv, GMAC_TX_EXCESSIVE_LENGTH_DROP_REG);
+       regs[55] = dsaf_read_dev(drv, GMAC_TX_UNDERRUN_REG);
+       regs[56] = dsaf_read_dev(drv, GMAC_TX_TAGGED_REG);
+       regs[57] = dsaf_read_dev(drv, GMAC_TX_CRC_ERROR_REG);
+       regs[58] = dsaf_read_dev(drv, GMAC_TX_PAUSE_FRAMES_REG);
+
+       regs[59] = dsaf_read_dev(drv, GAMC_RX_MAX_FRAME);
+       regs[60] = dsaf_read_dev(drv, GMAC_LINE_LOOP_BACK_REG);
+       regs[61] = dsaf_read_dev(drv, GMAC_CF_CRC_STRIP_REG);
+       regs[62] = dsaf_read_dev(drv, GMAC_MODE_CHANGE_EN_REG);
+       regs[63] = dsaf_read_dev(drv, GMAC_SIXTEEN_BIT_CNTR_REG);
+       regs[64] = dsaf_read_dev(drv, GMAC_LD_LINK_COUNTER_REG);
+       regs[65] = dsaf_read_dev(drv, GMAC_LOOP_REG);
+       regs[66] = dsaf_read_dev(drv, GMAC_RECV_CONTROL_REG);
+       regs[67] = dsaf_read_dev(drv, GMAC_VLAN_CODE_REG);
+       regs[68] = dsaf_read_dev(drv, GMAC_RX_OVERRUN_CNT_REG);
+       regs[69] = dsaf_read_dev(drv, GMAC_RX_LENGTHFIELD_ERR_CNT_REG);
+       regs[70] = dsaf_read_dev(drv, GMAC_RX_FAIL_COMMA_CNT_REG);
+
+       regs[71] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_0_REG);
+       regs[72] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_0_REG);
+       regs[73] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_1_REG);
+       regs[74] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_1_REG);
+       regs[75] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_2_REG);
+       regs[76] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_2_REG);
+       regs[77] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_3_REG);
+       regs[78] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_3_REG);
+       regs[79] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_4_REG);
+       regs[80] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_4_REG);
+       regs[81] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_5_REG);
+       regs[82] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_5_REG);
+       regs[83] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_MSK_0_REG);
+       regs[84] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_MSK_0_REG);
+       regs[85] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_MSK_1_REG);
+       regs[86] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_MSK_1_REG);
+       regs[87] = dsaf_read_dev(drv, GMAC_MAC_SKIP_LEN_REG);
+       regs[88] = dsaf_read_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG);
+
+       /* mark end of mac regs */
+       for (i = 89; i < 96; i++)
+               regs[i] = 0xaaaaaaaa;
+}
+
+static void hns_gmac_get_stats(void *mac_drv, u64 *data)
+{
+       u32 i;
+       u64 *buf = data;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       struct mac_hw_stats *hw_stats = NULL;
+
+       hw_stats = &drv->mac_cb->hw_stats;
+
+       for (i = 0; i < ARRAY_SIZE(g_gmac_stats_string); i++) {
+               buf[i] = DSAF_STATS_READ(hw_stats,
+                       g_gmac_stats_string[i].offset);
+       }
+}
+
+static void hns_gmac_get_strings(u32 stringset, u8 *data)
+{
+       char *buff = (char *)data;
+       u32 i;
+
+       if (stringset != ETH_SS_STATS)
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(g_gmac_stats_string); i++) {
+               snprintf(buff, ETH_GSTRING_LEN, g_gmac_stats_string[i].desc);
+               buff = buff + ETH_GSTRING_LEN;
+       }
+}
+
+static int hns_gmac_get_sset_count(int stringset)
+{
+       if (stringset == ETH_SS_STATS)
+               return ARRAY_SIZE(g_gmac_stats_string);
+
+       return 0;
+}
+
+static int hns_gmac_get_regs_count(void)
+{
+       return ETH_GMAC_DUMP_NUM;
+}
+
+void *hns_gmac_config(struct hns_mac_cb *mac_cb, struct mac_params *mac_param)
+{
+       struct mac_driver *mac_drv;
+
+       mac_drv = devm_kzalloc(mac_cb->dev, sizeof(*mac_drv), GFP_KERNEL);
+       if (!mac_drv)
+               return NULL;
+
+       mac_drv->mac_init = hns_gmac_init;
+       mac_drv->mac_enable = hns_gmac_enable;
+       mac_drv->mac_disable = hns_gmac_disable;
+       mac_drv->mac_free = hns_gmac_free;
+       mac_drv->adjust_link = hns_gmac_adjust_link;
+       mac_drv->set_tx_auto_pause_frames = hns_gmac_set_tx_auto_pause_frames;
+       mac_drv->config_max_frame_length = hns_gmac_config_max_frame_length;
+       mac_drv->mac_pausefrm_cfg = hns_gmac_pause_frm_cfg;
+
+       mac_drv->mac_id = mac_param->mac_id;
+       mac_drv->mac_mode = mac_param->mac_mode;
+       mac_drv->io_base = mac_param->vaddr;
+       mac_drv->dev = mac_param->dev;
+       mac_drv->mac_cb = mac_cb;
+
+       mac_drv->set_mac_addr = hns_gmac_set_mac_addr;
+       mac_drv->set_an_mode = hns_gmac_config_an_mode;
+       mac_drv->config_loopback = hns_gmac_config_loopback;
+       mac_drv->config_pad_and_crc = hns_gmac_config_pad_and_crc;
+       mac_drv->config_half_duplex = hns_gmac_set_duplex_type;
+       mac_drv->set_rx_ignore_pause_frames = hns_gmac_set_rx_auto_pause_frames;
+       mac_drv->mac_get_id = hns_gmac_get_id;
+       mac_drv->get_info = hns_gmac_get_info;
+       mac_drv->autoneg_stat = hns_gmac_autoneg_stat;
+       mac_drv->get_pause_enable = hns_gmac_get_pausefrm_cfg;
+       mac_drv->get_link_status = hns_gmac_get_link_status;
+       mac_drv->get_regs = hns_gmac_get_regs;
+       mac_drv->get_regs_count = hns_gmac_get_regs_count;
+       mac_drv->get_ethtool_stats = hns_gmac_get_stats;
+       mac_drv->get_sset_count = hns_gmac_get_sset_count;
+       mac_drv->get_strings = hns_gmac_get_strings;
+       mac_drv->update_stats = hns_gmac_update_stats;
+
+       return (void *)mac_drv;
+}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h
new file mode 100644 (file)
index 0000000..44fe301
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 _HNS_GMAC_H
+#define _HNS_GMAC_H
+
+#include "hns_dsaf_mac.h"
+
+enum hns_port_mode {
+       GMAC_10M_MII = 0,
+       GMAC_100M_MII,
+       GMAC_1000M_GMII,
+       GMAC_10M_RGMII,
+       GMAC_100M_RGMII,
+       GMAC_1000M_RGMII,
+       GMAC_10M_SGMII,
+       GMAC_100M_SGMII,
+       GMAC_1000M_SGMII,
+       GMAC_10000M_SGMII       /* 10GE */
+};
+
+enum hns_gmac_duplex_mdoe {
+       GMAC_HALF_DUPLEX_MODE = 0,
+       GMAC_FULL_DUPLEX_MODE
+};
+
+struct hns_gmac_port_mode_cfg {
+       enum hns_port_mode port_mode;
+       u32 max_frm_size;
+       u32 short_runts_thr;
+       u32 pad_enable;
+       u32 crc_add;
+       u32 an_enable;  /*auto-nego enable  */
+       u32 runt_pkt_en;
+       u32 strip_pad_en;
+};
+
+#define ETH_GMAC_DUMP_NUM              96
+#endif                         /* __HNS_GMAC_H__ */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
new file mode 100644 (file)
index 0000000..026b386
--- /dev/null
@@ -0,0 +1,902 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/phy_fixed.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "hns_dsaf_misc.h"
+#include "hns_dsaf_main.h"
+#include "hns_dsaf_rcb.h"
+
+#define MAC_EN_FLAG_V          0xada0328
+
+static const u16 mac_phy_to_speed[] = {
+       [PHY_INTERFACE_MODE_MII] = MAC_SPEED_100,
+       [PHY_INTERFACE_MODE_GMII] = MAC_SPEED_1000,
+       [PHY_INTERFACE_MODE_SGMII] = MAC_SPEED_1000,
+       [PHY_INTERFACE_MODE_TBI] = MAC_SPEED_1000,
+       [PHY_INTERFACE_MODE_RMII] = MAC_SPEED_100,
+       [PHY_INTERFACE_MODE_RGMII] = MAC_SPEED_1000,
+       [PHY_INTERFACE_MODE_RGMII_ID] = MAC_SPEED_1000,
+       [PHY_INTERFACE_MODE_RGMII_RXID] = MAC_SPEED_1000,
+       [PHY_INTERFACE_MODE_RGMII_TXID] = MAC_SPEED_1000,
+       [PHY_INTERFACE_MODE_RTBI] = MAC_SPEED_1000,
+       [PHY_INTERFACE_MODE_XGMII] = MAC_SPEED_10000
+};
+
+static const enum mac_mode g_mac_mode_100[] = {
+       [PHY_INTERFACE_MODE_MII]        = MAC_MODE_MII_100,
+       [PHY_INTERFACE_MODE_RMII]   = MAC_MODE_RMII_100
+};
+
+static const enum mac_mode g_mac_mode_1000[] = {
+       [PHY_INTERFACE_MODE_GMII]   = MAC_MODE_GMII_1000,
+       [PHY_INTERFACE_MODE_SGMII]  = MAC_MODE_SGMII_1000,
+       [PHY_INTERFACE_MODE_TBI]        = MAC_MODE_TBI_1000,
+       [PHY_INTERFACE_MODE_RGMII]  = MAC_MODE_RGMII_1000,
+       [PHY_INTERFACE_MODE_RGMII_ID]   = MAC_MODE_RGMII_1000,
+       [PHY_INTERFACE_MODE_RGMII_RXID] = MAC_MODE_RGMII_1000,
+       [PHY_INTERFACE_MODE_RGMII_TXID] = MAC_MODE_RGMII_1000,
+       [PHY_INTERFACE_MODE_RTBI]   = MAC_MODE_RTBI_1000
+};
+
+static enum mac_mode hns_mac_dev_to_enet_if(const struct hns_mac_cb *mac_cb)
+{
+       switch (mac_cb->max_speed) {
+       case MAC_SPEED_100:
+               return g_mac_mode_100[mac_cb->phy_if];
+       case MAC_SPEED_1000:
+               return g_mac_mode_1000[mac_cb->phy_if];
+       case MAC_SPEED_10000:
+               return MAC_MODE_XGMII_10000;
+       default:
+               return MAC_MODE_MII_100;
+       }
+}
+
+static enum mac_mode hns_get_enet_interface(const struct hns_mac_cb *mac_cb)
+{
+       switch (mac_cb->max_speed) {
+       case MAC_SPEED_100:
+               return g_mac_mode_100[mac_cb->phy_if];
+       case MAC_SPEED_1000:
+               return g_mac_mode_1000[mac_cb->phy_if];
+       case MAC_SPEED_10000:
+               return MAC_MODE_XGMII_10000;
+       default:
+               return MAC_MODE_MII_100;
+       }
+}
+
+int hns_mac_get_sfp_prsnt(struct hns_mac_cb *mac_cb, int *sfp_prsnt)
+{
+       if (!mac_cb->cpld_vaddr)
+               return -ENODEV;
+
+       *sfp_prsnt = !dsaf_read_b((u8 *)mac_cb->cpld_vaddr
+                                       + MAC_SFP_PORT_OFFSET);
+
+       return 0;
+}
+
+void hns_mac_get_link_status(struct hns_mac_cb *mac_cb, u32 *link_status)
+{
+       struct mac_driver *mac_ctrl_drv;
+       int ret, sfp_prsnt;
+
+       mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       if (mac_ctrl_drv->get_link_status)
+               mac_ctrl_drv->get_link_status(mac_ctrl_drv, link_status);
+       else
+               *link_status = 0;
+
+       ret = hns_mac_get_sfp_prsnt(mac_cb, &sfp_prsnt);
+       if (!ret)
+               *link_status = *link_status && sfp_prsnt;
+
+       mac_cb->link = *link_status;
+}
+
+int hns_mac_get_port_info(struct hns_mac_cb *mac_cb,
+                         u8 *auto_neg, u16 *speed, u8 *duplex)
+{
+       struct mac_driver *mac_ctrl_drv;
+       struct mac_info    info;
+
+       mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       if (!mac_ctrl_drv->get_info)
+               return -ENODEV;
+
+       mac_ctrl_drv->get_info(mac_ctrl_drv, &info);
+       if (auto_neg)
+               *auto_neg = info.auto_neg;
+       if (speed)
+               *speed = info.speed;
+       if (duplex)
+               *duplex = info.duplex;
+
+       return 0;
+}
+
+void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex)
+{
+       int ret;
+       struct mac_driver *mac_ctrl_drv;
+
+       mac_ctrl_drv = (struct mac_driver *)(mac_cb->priv.mac);
+
+       mac_cb->speed = speed;
+       mac_cb->half_duplex = !duplex;
+       mac_ctrl_drv->mac_mode = hns_mac_dev_to_enet_if(mac_cb);
+
+       if (mac_ctrl_drv->adjust_link) {
+               ret = mac_ctrl_drv->adjust_link(mac_ctrl_drv,
+                       (enum mac_speed)speed, duplex);
+               if (ret) {
+                       dev_err(mac_cb->dev,
+                               "adjust_link failed,%s mac%d ret = %#x!\n",
+                               mac_cb->dsaf_dev->ae_dev.name,
+                               mac_cb->mac_id, ret);
+                       return;
+               }
+       }
+}
+
+/**
+ *hns_mac_get_inner_port_num - get mac table inner port number
+ *@mac_cb: mac device
+ *@vmid: vm id
+ *@port_num:port number
+ *
+ */
+static int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb,
+                                     u8 vmid, u8 *port_num)
+{
+       u8 tmp_port;
+       u32 comm_idx;
+
+       if (mac_cb->dsaf_dev->dsaf_mode <= DSAF_MODE_ENABLE) {
+               if (mac_cb->mac_id != DSAF_MAX_PORT_NUM_PER_CHIP) {
+                       dev_err(mac_cb->dev,
+                               "input invalid,%s mac%d vmid%d !\n",
+                               mac_cb->dsaf_dev->ae_dev.name,
+                               mac_cb->mac_id, vmid);
+                       return -EINVAL;
+               }
+       } else if (mac_cb->dsaf_dev->dsaf_mode < DSAF_MODE_MAX) {
+               if (mac_cb->mac_id >= DSAF_MAX_PORT_NUM_PER_CHIP) {
+                       dev_err(mac_cb->dev,
+                               "input invalid,%s mac%d vmid%d!\n",
+                               mac_cb->dsaf_dev->ae_dev.name,
+                               mac_cb->mac_id, vmid);
+                       return -EINVAL;
+               }
+       } else {
+               dev_err(mac_cb->dev, "dsaf mode invalid,%s mac%d!\n",
+                       mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id);
+               return -EINVAL;
+       }
+
+       comm_idx = hns_dsaf_get_comm_idx_by_port(mac_cb->mac_id);
+
+       if (vmid >= mac_cb->dsaf_dev->rcb_common[comm_idx]->max_vfn) {
+               dev_err(mac_cb->dev, "input invalid,%s mac%d vmid%d !\n",
+                       mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id, vmid);
+               return -EINVAL;
+       }
+
+       switch (mac_cb->dsaf_dev->dsaf_mode) {
+       case DSAF_MODE_ENABLE_FIX:
+               tmp_port = 0;
+               break;
+       case DSAF_MODE_DISABLE_FIX:
+               tmp_port = 0;
+               break;
+       case DSAF_MODE_ENABLE_0VM:
+       case DSAF_MODE_ENABLE_8VM:
+       case DSAF_MODE_ENABLE_16VM:
+       case DSAF_MODE_ENABLE_32VM:
+       case DSAF_MODE_ENABLE_128VM:
+       case DSAF_MODE_DISABLE_2PORT_8VM:
+       case DSAF_MODE_DISABLE_2PORT_16VM:
+       case DSAF_MODE_DISABLE_2PORT_64VM:
+       case DSAF_MODE_DISABLE_6PORT_0VM:
+       case DSAF_MODE_DISABLE_6PORT_2VM:
+       case DSAF_MODE_DISABLE_6PORT_4VM:
+       case DSAF_MODE_DISABLE_6PORT_16VM:
+               tmp_port = vmid;
+               break;
+       default:
+               dev_err(mac_cb->dev, "dsaf mode invalid,%s mac%d!\n",
+                       mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id);
+               return -EINVAL;
+       }
+       tmp_port += DSAF_BASE_INNER_PORT_NUM;
+
+       *port_num = tmp_port;
+
+       return 0;
+}
+
+/**
+ *hns_mac_get_inner_port_num - change vf mac address
+ *@mac_cb: mac device
+ *@vmid: vmid
+ *@addr:mac address
+ */
+int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb,
+                          u32 vmid, char *addr)
+{
+       int ret;
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+       struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
+       struct dsaf_drv_mac_single_dest_entry mac_entry;
+       struct mac_entry_idx *old_entry;
+
+       old_entry = &mac_cb->addr_entry_idx[vmid];
+       if (dsaf_dev) {
+               memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr));
+               mac_entry.in_vlan_id = old_entry->vlan_id;
+               mac_entry.in_port_num = mac_cb->mac_id;
+               ret = hns_mac_get_inner_port_num(mac_cb, (u8)vmid,
+                                                &mac_entry.port_num);
+               if (ret)
+                       return ret;
+
+               if ((old_entry->valid != 0) &&
+                   (memcmp(old_entry->addr,
+                   addr, sizeof(mac_entry.addr)) != 0)) {
+                       ret = hns_dsaf_del_mac_entry(dsaf_dev,
+                                                    old_entry->vlan_id,
+                                                    mac_cb->mac_id,
+                                                    old_entry->addr);
+                       if (ret)
+                               return ret;
+               }
+
+               ret = hns_dsaf_set_mac_uc_entry(dsaf_dev, &mac_entry);
+               if (ret)
+                       return ret;
+       }
+
+       if ((mac_ctrl_drv->set_mac_addr) && (vmid == 0))
+               mac_ctrl_drv->set_mac_addr(mac_cb->priv.mac, addr);
+
+       memcpy(old_entry->addr, addr, sizeof(old_entry->addr));
+       old_entry->valid = 1;
+       return 0;
+}
+
+int hns_mac_set_multi(struct hns_mac_cb *mac_cb,
+                     u32 port_num, char *addr, u8 en)
+{
+       int ret;
+       struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
+       struct dsaf_drv_mac_single_dest_entry mac_entry;
+
+       if (dsaf_dev && addr) {
+               memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr));
+               mac_entry.in_vlan_id = 0;/*vlan_id;*/
+               mac_entry.in_port_num = mac_cb->mac_id;
+               mac_entry.port_num = port_num;
+
+               if (en == DISABLE)
+                       ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
+               else
+                       ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
+               if (ret) {
+                       dev_err(dsaf_dev->dev,
+                               "set mac mc port failed,%s mac%d ret = %#x!\n",
+                               mac_cb->dsaf_dev->ae_dev.name,
+                               mac_cb->mac_id, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ *hns_mac_del_mac - delete mac address into dsaf table,can't delete the same
+ *                  address twice
+ *@net_dev: net device
+ *@vfn :   vf lan
+ *@mac : mac address
+ *return status
+ */
+int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac)
+{
+       struct mac_entry_idx *old_mac;
+       struct dsaf_device *dsaf_dev;
+       u32 ret;
+
+       dsaf_dev = mac_cb->dsaf_dev;
+
+       if (vfn < DSAF_MAX_VM_NUM) {
+               old_mac = &mac_cb->addr_entry_idx[vfn];
+       } else {
+               dev_err(mac_cb->dev,
+                       "vf queue is too large,%s mac%d queue = %#x!\n",
+                       mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id, vfn);
+               return -EINVAL;
+       }
+
+       if (dsaf_dev) {
+               ret = hns_dsaf_del_mac_entry(dsaf_dev, old_mac->vlan_id,
+                                            mac_cb->mac_id, old_mac->addr);
+               if (ret)
+                       return ret;
+
+               if (memcmp(old_mac->addr, mac, sizeof(old_mac->addr)) == 0)
+                       old_mac->valid = 0;
+       }
+
+       return 0;
+}
+
+static void hns_mac_param_get(struct mac_params *param,
+                             struct hns_mac_cb *mac_cb)
+{
+       param->vaddr = (void *)mac_cb->vaddr;
+       param->mac_mode = hns_get_enet_interface(mac_cb);
+       memcpy(param->addr, mac_cb->addr_entry_idx[0].addr,
+              MAC_NUM_OCTETS_PER_ADDR);
+       param->mac_id = mac_cb->mac_id;
+       param->dev = mac_cb->dev;
+}
+
+/**
+ *hns_mac_queue_config_bc_en - set broadcast rx&tx enable
+ *@mac_cb: mac device
+ *@queue: queue number
+ *@en:enable
+ *retuen 0 - success , negative --fail
+ */
+static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb,
+                                    u32 port_num, u16 vlan_id, u8 en)
+{
+       int ret;
+       struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
+       u8 addr[MAC_NUM_OCTETS_PER_ADDR]
+               = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+       struct dsaf_drv_mac_single_dest_entry mac_entry;
+
+       /* directy return ok in debug network mode */
+       if (mac_cb->mac_type == HNAE_PORT_DEBUG)
+               return 0;
+
+       if (dsaf_dev) {
+               memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr));
+               mac_entry.in_vlan_id = vlan_id;
+               mac_entry.in_port_num = mac_cb->mac_id;
+               mac_entry.port_num = port_num;
+
+               if (en == DISABLE)
+                       ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
+               else
+                       ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
+               return ret;
+       }
+
+       return 0;
+}
+
+/**
+ *hns_mac_vm_config_bc_en - set broadcast rx&tx enable
+ *@mac_cb: mac device
+ *@vmid: vm id
+ *@en:enable
+ *retuen 0 - success , negative --fail
+ */
+int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, u8 en)
+{
+       int ret;
+       struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
+       u8 port_num;
+       u8 addr[MAC_NUM_OCTETS_PER_ADDR]
+               = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+       struct mac_entry_idx *uc_mac_entry;
+       struct dsaf_drv_mac_single_dest_entry mac_entry;
+
+       if (mac_cb->mac_type == HNAE_PORT_DEBUG)
+               return 0;
+
+       uc_mac_entry = &mac_cb->addr_entry_idx[vmid];
+
+       if (dsaf_dev)  {
+               memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr));
+               mac_entry.in_vlan_id = uc_mac_entry->vlan_id;
+               mac_entry.in_port_num = mac_cb->mac_id;
+               ret = hns_mac_get_inner_port_num(mac_cb, vmid, &port_num);
+               if (ret)
+                       return ret;
+               mac_entry.port_num = port_num;
+
+               if (en == DISABLE)
+                       ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
+               else
+                       ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
+               return ret;
+       }
+
+       return 0;
+}
+
+void hns_mac_reset(struct hns_mac_cb *mac_cb)
+{
+       struct mac_driver *drv;
+
+       drv = hns_mac_get_drv(mac_cb);
+
+       drv->mac_init(drv);
+
+       if (drv->config_max_frame_length)
+               drv->config_max_frame_length(drv, mac_cb->max_frm);
+
+       if (drv->set_tx_auto_pause_frames)
+               drv->set_tx_auto_pause_frames(drv, mac_cb->tx_pause_frm_time);
+
+       if (drv->set_an_mode)
+               drv->set_an_mode(drv, 1);
+
+       if (drv->mac_pausefrm_cfg) {
+               if (mac_cb->mac_type == HNAE_PORT_DEBUG)
+                       drv->mac_pausefrm_cfg(drv, 0, 0);
+               else /* mac rx must disable, dsaf pfc close instead of it*/
+                       drv->mac_pausefrm_cfg(drv, 0, 1);
+       }
+}
+
+int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu)
+{
+       struct mac_driver *drv = hns_mac_get_drv(mac_cb);
+       u32 buf_size = mac_cb->dsaf_dev->buf_size;
+       u32 new_frm = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+
+       if ((new_mtu < MAC_MIN_MTU) || (new_frm > MAC_MAX_MTU) ||
+           (new_frm > HNS_RCB_RING_MAX_BD_PER_PKT * buf_size))
+               return -EINVAL;
+
+       if (!drv->config_max_frame_length)
+               return -ECHILD;
+
+       /* adjust max frame to be at least the size of a standard frame */
+       if (new_frm < (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN))
+               new_frm = (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN);
+
+       drv->config_max_frame_length(drv, new_frm);
+
+       mac_cb->max_frm = new_frm;
+
+       return 0;
+}
+
+void hns_mac_start(struct hns_mac_cb *mac_cb)
+{
+       struct mac_driver *mac_drv = hns_mac_get_drv(mac_cb);
+
+       /* for virt */
+       if (mac_drv->mac_en_flg == MAC_EN_FLAG_V) {
+               /*plus 1 when the virtual mac has been enabled */
+               mac_drv->virt_dev_num += 1;
+               return;
+       }
+
+       if (mac_drv->mac_enable) {
+               mac_drv->mac_enable(mac_cb->priv.mac, MAC_COMM_MODE_RX_AND_TX);
+               mac_drv->mac_en_flg = MAC_EN_FLAG_V;
+       }
+}
+
+void hns_mac_stop(struct hns_mac_cb *mac_cb)
+{
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       /*modified for virtualization */
+       if (mac_ctrl_drv->virt_dev_num > 0) {
+               mac_ctrl_drv->virt_dev_num -= 1;
+               if (mac_ctrl_drv->virt_dev_num > 0)
+                       return;
+       }
+
+       if (mac_ctrl_drv->mac_disable)
+               mac_ctrl_drv->mac_disable(mac_cb->priv.mac,
+                       MAC_COMM_MODE_RX_AND_TX);
+
+       mac_ctrl_drv->mac_en_flg = 0;
+       mac_cb->link = 0;
+       cpld_led_reset(mac_cb);
+}
+
+/**
+ * hns_mac_get_autoneg - get auto autonegotiation
+ * @mac_cb: mac control block
+ * @enable: enable or not
+ * retuen 0 - success , negative --fail
+ */
+void hns_mac_get_autoneg(struct hns_mac_cb *mac_cb, u32 *auto_neg)
+{
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       if (mac_ctrl_drv->autoneg_stat)
+               mac_ctrl_drv->autoneg_stat(mac_ctrl_drv, auto_neg);
+       else
+               *auto_neg = 0;
+}
+
+/**
+ * hns_mac_get_pauseparam - set rx & tx pause parameter
+ * @mac_cb: mac control block
+ * @rx_en: rx enable status
+ * @tx_en: tx enable status
+ * retuen 0 - success , negative --fail
+ */
+void hns_mac_get_pauseparam(struct hns_mac_cb *mac_cb, u32 *rx_en, u32 *tx_en)
+{
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       if (mac_ctrl_drv->get_pause_enable) {
+               mac_ctrl_drv->get_pause_enable(mac_ctrl_drv, rx_en, tx_en);
+       } else {
+               *rx_en = 0;
+               *tx_en = 0;
+       }
+
+       /* Due to the chip defect, the service mac's rx pause CAN'T be enabled.
+        * We set the rx pause frm always be true (1), because DSAF deals with
+        * the rx pause frm instead of service mac. After all, we still support
+        * rx pause frm.
+        */
+       if (mac_cb->mac_type == HNAE_PORT_SERVICE)
+               *rx_en = 1;
+}
+
+/**
+ * hns_mac_set_autoneg - set auto autonegotiation
+ * @mac_cb: mac control block
+ * @enable: enable or not
+ * retuen 0 - success , negative --fail
+ */
+int hns_mac_set_autoneg(struct hns_mac_cb *mac_cb, u8 enable)
+{
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII && enable) {
+               dev_err(mac_cb->dev, "enable autoneg is not allowed!");
+               return -ENOTSUPP;
+       }
+
+       if (mac_ctrl_drv->set_an_mode)
+               mac_ctrl_drv->set_an_mode(mac_ctrl_drv, enable);
+
+       return 0;
+}
+
+/**
+ * hns_mac_set_autoneg - set rx & tx pause parameter
+ * @mac_cb: mac control block
+ * @rx_en: rx enable or not
+ * @tx_en: tx enable or not
+ * return 0 - success , negative --fail
+ */
+int hns_mac_set_pauseparam(struct hns_mac_cb *mac_cb, u32 rx_en, u32 tx_en)
+{
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       if (mac_cb->mac_type == HNAE_PORT_SERVICE) {
+               if (!rx_en) {
+                       dev_err(mac_cb->dev, "disable rx_pause is not allowed!");
+                       return -EINVAL;
+               }
+       } else if (mac_cb->mac_type == HNAE_PORT_DEBUG) {
+               if (tx_en || rx_en) {
+                       dev_err(mac_cb->dev, "enable tx_pause or enable rx_pause are not allowed!");
+                       return -EINVAL;
+               }
+       } else {
+               dev_err(mac_cb->dev, "Unsupport this operation!");
+               return -EINVAL;
+       }
+
+       if (mac_ctrl_drv->mac_pausefrm_cfg)
+               mac_ctrl_drv->mac_pausefrm_cfg(mac_ctrl_drv, rx_en, tx_en);
+
+       return 0;
+}
+
+/**
+ * hns_mac_init_ex - mac init
+ * @mac_cb: mac control block
+ * retuen 0 - success , negative --fail
+ */
+static int hns_mac_init_ex(struct hns_mac_cb *mac_cb)
+{
+       int ret;
+       struct mac_params param;
+       struct mac_driver *drv;
+
+       hns_dsaf_fix_mac_mode(mac_cb);
+
+       memset(&param, 0, sizeof(struct mac_params));
+       hns_mac_param_get(&param, mac_cb);
+
+       if (MAC_SPEED_FROM_MODE(param.mac_mode) < MAC_SPEED_10000)
+               drv = (struct mac_driver *)hns_gmac_config(mac_cb, &param);
+       else
+               drv = (struct mac_driver *)hns_xgmac_config(mac_cb, &param);
+
+       if (!drv)
+               return -ENOMEM;
+
+       mac_cb->priv.mac = (void *)drv;
+       hns_mac_reset(mac_cb);
+
+       hns_mac_adjust_link(mac_cb, mac_cb->speed, !mac_cb->half_duplex);
+
+       ret = hns_mac_port_config_bc_en(mac_cb, mac_cb->mac_id, 0, ENABLE);
+       if (ret)
+               goto free_mac_drv;
+
+       return 0;
+
+free_mac_drv:
+       drv->mac_free(mac_cb->priv.mac);
+       mac_cb->priv.mac = NULL;
+
+       return ret;
+}
+
+/**
+ *mac_free_dev  - get mac information from device node
+ *@mac_cb: mac device
+ *@np:device node
+ *@mac_mode_idx:mac mode index
+ */
+static void hns_mac_get_info(struct hns_mac_cb *mac_cb,
+                            struct device_node *np, u32 mac_mode_idx)
+{
+       mac_cb->link = false;
+       mac_cb->half_duplex = false;
+       mac_cb->speed = mac_phy_to_speed[mac_cb->phy_if];
+       mac_cb->max_speed = mac_cb->speed;
+
+       if (mac_cb->phy_if == PHY_INTERFACE_MODE_SGMII) {
+               mac_cb->if_support = MAC_GMAC_SUPPORTED;
+               mac_cb->if_support |= SUPPORTED_1000baseT_Full;
+       } else if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII) {
+               mac_cb->if_support = SUPPORTED_10000baseR_FEC;
+               mac_cb->if_support |= SUPPORTED_10000baseKR_Full;
+       }
+
+       mac_cb->max_frm = MAC_DEFAULT_MTU;
+       mac_cb->tx_pause_frm_time = MAC_DEFAULT_PAUSE_TIME;
+
+       /* Get the rest of the PHY information */
+       mac_cb->phy_node = of_parse_phandle(np, "phy-handle", mac_cb->mac_id);
+       if (mac_cb->phy_node)
+               dev_dbg(mac_cb->dev, "mac%d phy_node: %s\n",
+                       mac_cb->mac_id, mac_cb->phy_node->name);
+}
+
+/**
+ * hns_mac_get_mode - get mac mode
+ * @phy_if: phy interface
+ * retuen 0 - gmac, 1 - xgmac , negative --fail
+ */
+static int hns_mac_get_mode(phy_interface_t phy_if)
+{
+       switch (phy_if) {
+       case PHY_INTERFACE_MODE_SGMII:
+               return MAC_GMAC_IDX;
+       case PHY_INTERFACE_MODE_XGMII:
+               return MAC_XGMAC_IDX;
+       default:
+               return -EINVAL;
+       }
+}
+
+u8 __iomem *hns_mac_get_vaddr(struct dsaf_device *dsaf_dev,
+                             struct hns_mac_cb *mac_cb, u32 mac_mode_idx)
+{
+       u8 __iomem *base = dsaf_dev->io_base;
+       int mac_id = mac_cb->mac_id;
+
+       if (mac_cb->mac_type == HNAE_PORT_SERVICE)
+               return base + 0x40000 + mac_id * 0x4000 -
+                               mac_mode_idx * 0x20000;
+       else
+               return mac_cb->serdes_vaddr + 0x1000
+                       + (mac_id - DSAF_SERVICE_PORT_NUM_PER_DSAF) * 0x100000;
+}
+
+/**
+ * hns_mac_get_cfg - get mac cfg from dtb or acpi table
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @mac_idx: mac index
+ * retuen 0 - success , negative --fail
+ */
+int hns_mac_get_cfg(struct dsaf_device *dsaf_dev, int mac_idx)
+{
+       int ret;
+       u32 mac_mode_idx;
+       struct hns_mac_cb *mac_cb = &dsaf_dev->mac_cb[mac_idx];
+
+       mac_cb->dsaf_dev = dsaf_dev;
+       mac_cb->dev = dsaf_dev->dev;
+       mac_cb->mac_id = mac_idx;
+
+       mac_cb->sys_ctl_vaddr = dsaf_dev->sc_base;
+       mac_cb->serdes_vaddr = dsaf_dev->sds_base;
+
+       if (dsaf_dev->cpld_base &&
+           mac_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) {
+               mac_cb->cpld_vaddr = dsaf_dev->cpld_base +
+                       mac_cb->mac_id * CPLD_ADDR_PORT_OFFSET;
+               cpld_led_reset(mac_cb);
+       }
+       mac_cb->sfp_prsnt = 0;
+       mac_cb->txpkt_for_led = 0;
+       mac_cb->rxpkt_for_led = 0;
+
+       if (mac_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF)
+               mac_cb->mac_type = HNAE_PORT_SERVICE;
+       else
+               mac_cb->mac_type = HNAE_PORT_DEBUG;
+
+       mac_cb->phy_if = hns_mac_get_phy_if(mac_cb);
+
+       ret = hns_mac_get_mode(mac_cb->phy_if);
+       if (ret < 0) {
+               dev_err(dsaf_dev->dev,
+                       "hns_mac_get_mode failed,mac%d ret = %#x!\n",
+                       mac_cb->mac_id, ret);
+               return ret;
+       }
+       mac_mode_idx = (u32)ret;
+
+       hns_mac_get_info(mac_cb, mac_cb->dev->of_node, mac_mode_idx);
+
+       mac_cb->vaddr = hns_mac_get_vaddr(dsaf_dev, mac_cb, mac_mode_idx);
+
+       return 0;
+}
+
+/**
+ * hns_mac_init - init mac
+ * @dsaf_dev: dsa fabric device struct pointer
+ * retuen 0 - success , negative --fail
+ */
+int hns_mac_init(struct dsaf_device *dsaf_dev)
+{
+       int i;
+       int ret;
+       size_t size;
+       struct hns_mac_cb *mac_cb;
+
+       size = sizeof(struct hns_mac_cb) * DSAF_MAX_PORT_NUM_PER_CHIP;
+       dsaf_dev->mac_cb = devm_kzalloc(dsaf_dev->dev, size, GFP_KERNEL);
+       if (!dsaf_dev->mac_cb)
+               return -ENOMEM;
+
+       for (i = 0; i < DSAF_MAX_PORT_NUM_PER_CHIP; i++) {
+               ret = hns_mac_get_cfg(dsaf_dev, i);
+               if (ret)
+                       goto free_mac_cb;
+
+               mac_cb = &dsaf_dev->mac_cb[i];
+               ret = hns_mac_init_ex(mac_cb);
+               if (ret)
+                       goto free_mac_cb;
+       }
+
+       return 0;
+
+free_mac_cb:
+       dsaf_dev->mac_cb = NULL;
+
+       return ret;
+}
+
+void hns_mac_uninit(struct dsaf_device *dsaf_dev)
+{
+       cpld_led_reset(dsaf_dev->mac_cb);
+       dsaf_dev->mac_cb = NULL;
+}
+
+int hns_mac_config_mac_loopback(struct hns_mac_cb *mac_cb,
+                               enum hnae_loop loop, int en)
+{
+       int ret;
+       struct mac_driver *drv = hns_mac_get_drv(mac_cb);
+
+       if (drv->config_loopback)
+               ret = drv->config_loopback(drv, loop, en);
+       else
+               ret = -ENOTSUPP;
+
+       return ret;
+}
+
+void hns_mac_update_stats(struct hns_mac_cb *mac_cb)
+{
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       mac_ctrl_drv->update_stats(mac_ctrl_drv);
+}
+
+void hns_mac_get_stats(struct hns_mac_cb *mac_cb, u64 *data)
+{
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       mac_ctrl_drv->get_ethtool_stats(mac_ctrl_drv, data);
+}
+
+void hns_mac_get_strings(struct hns_mac_cb *mac_cb,
+                        int stringset, u8 *data)
+{
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       mac_ctrl_drv->get_strings(stringset, data);
+}
+
+int hns_mac_get_sset_count(struct hns_mac_cb *mac_cb, int stringset)
+{
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       return mac_ctrl_drv->get_sset_count(stringset);
+}
+
+int hns_mac_get_regs_count(struct hns_mac_cb *mac_cb)
+{
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       return mac_ctrl_drv->get_regs_count();
+}
+
+void hns_mac_get_regs(struct hns_mac_cb *mac_cb, void *data)
+{
+       struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
+
+       mac_ctrl_drv->get_regs(mac_ctrl_drv, data);
+}
+
+void hns_set_led_opt(struct hns_mac_cb *mac_cb)
+{
+       int nic_data = 0;
+       int txpkts, rxpkts;
+
+       txpkts = mac_cb->txpkt_for_led - mac_cb->hw_stats.tx_good_pkts;
+       rxpkts = mac_cb->rxpkt_for_led - mac_cb->hw_stats.rx_good_pkts;
+       if (txpkts || rxpkts)
+               nic_data = 1;
+       else
+               nic_data = 0;
+       mac_cb->txpkt_for_led = mac_cb->hw_stats.tx_good_pkts;
+       mac_cb->rxpkt_for_led = mac_cb->hw_stats.rx_good_pkts;
+       hns_cpld_set_led(mac_cb, (int)mac_cb->link,
+                        mac_cb->speed, nic_data);
+}
+
+int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb,
+                       enum hnae_led_state status)
+{
+       if (!mac_cb || !mac_cb->cpld_vaddr)
+               return 0;
+
+       return cpld_set_led_id(mac_cb, status);
+}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h
new file mode 100644 (file)
index 0000000..7da95a7
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 _HNS_DSAF_MAC_H
+#define _HNS_DSAF_MAC_H
+
+#include <linux/phy.h>
+#include <linux/kernel.h>
+#include <linux/if_vlan.h>
+#include "hns_dsaf_main.h"
+
+struct dsaf_device;
+
+#define MAC_GMAC_SUPPORTED \
+       (SUPPORTED_10baseT_Half \
+       | SUPPORTED_10baseT_Full \
+       | SUPPORTED_100baseT_Half \
+       | SUPPORTED_100baseT_Full \
+       | SUPPORTED_Autoneg)
+
+#define MAC_DEFAULT_MTU        (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN + ETH_DATA_LEN)
+#define MAC_MAX_MTU            9600
+#define MAC_MIN_MTU            68
+
+#define MAC_DEFAULT_PAUSE_TIME 0xff
+
+#define MAC_GMAC_IDX 0
+#define MAC_XGMAC_IDX 1
+
+#define ETH_STATIC_REG  1
+#define ETH_DUMP_REG    5
+/* check mac addr broadcast */
+#define MAC_IS_BROADCAST(p)    ((*(p) == 0xff) && (*((p) + 1) == 0xff) && \
+               (*((p) + 2) == 0xff) &&  (*((p) + 3) == 0xff)  && \
+               (*((p) + 4) == 0xff) && (*((p) + 5) == 0xff))
+
+/* check mac addr is 01-00-5e-xx-xx-xx*/
+#define MAC_IS_L3_MULTICAST(p) ((*((p) + 0) == 0x01) && \
+                       (*((p) + 1) == 0x00)   && \
+                       (*((p) + 2) == 0x5e))
+
+/*check the mac addr is 0 in all bit*/
+#define MAC_IS_ALL_ZEROS(p)   ((*(p) == 0) && (*((p) + 1) == 0) && \
+       (*((p) + 2) == 0) && (*((p) + 3) == 0) && \
+       (*((p) + 4) == 0) && (*((p) + 5) == 0))
+
+/*check mac addr multicast*/
+#define MAC_IS_MULTICAST(p)    ((*((u8 *)((p) + 0)) & 0x01) ? (1) : (0))
+
+/**< Number of octets (8-bit bytes) in an ethernet address */
+#define MAC_NUM_OCTETS_PER_ADDR 6
+
+struct mac_priv {
+       void *mac;
+};
+
+/* net speed */
+enum mac_speed {
+       MAC_SPEED_10    = 10,      /**< 10 Mbps */
+       MAC_SPEED_100   = 100,    /**< 100 Mbps */
+       MAC_SPEED_1000  = 1000,  /**< 1000 Mbps = 1 Gbps */
+       MAC_SPEED_10000 = 10000  /**< 10000 Mbps = 10 Gbps */
+};
+
+/*mac interface keyword        */
+enum mac_intf {
+       MAC_IF_NONE  = 0x00000000,   /**< interface not invalid */
+       MAC_IF_MII   = 0x00010000,   /**< MII interface */
+       MAC_IF_RMII  = 0x00020000,   /**< RMII interface */
+       MAC_IF_SMII  = 0x00030000,   /**< SMII interface */
+       MAC_IF_GMII  = 0x00040000,   /**< GMII interface */
+       MAC_IF_RGMII = 0x00050000,   /**< RGMII interface */
+       MAC_IF_TBI   = 0x00060000,   /**< TBI interface */
+       MAC_IF_RTBI  = 0x00070000,   /**< RTBI interface */
+       MAC_IF_SGMII = 0x00080000,   /**< SGMII interface */
+       MAC_IF_XGMII = 0x00090000,   /**< XGMII interface */
+       MAC_IF_QSGMII = 0x000a0000      /**< QSGMII interface */
+};
+
+/*mac mode */
+enum mac_mode {
+       /**< Invalid Ethernet mode */
+       MAC_MODE_INVALID         = 0,
+       /**<    10 Mbps MII   */
+       MAC_MODE_MII_10   = (MAC_IF_MII   | MAC_SPEED_10),
+       /**<   100 Mbps MII   */
+       MAC_MODE_MII_100         = (MAC_IF_MII   | MAC_SPEED_100),
+       /**<    10 Mbps RMII  */
+       MAC_MODE_RMII_10         = (MAC_IF_RMII  | MAC_SPEED_10),
+       /**<   100 Mbps RMII  */
+       MAC_MODE_RMII_100       = (MAC_IF_RMII  | MAC_SPEED_100),
+       /**<    10 Mbps SMII  */
+       MAC_MODE_SMII_10         = (MAC_IF_SMII  | MAC_SPEED_10),
+       /**<   100 Mbps SMII  */
+       MAC_MODE_SMII_100       = (MAC_IF_SMII  | MAC_SPEED_100),
+       /**<  1000 Mbps GMII  */
+       MAC_MODE_GMII_1000   = (MAC_IF_GMII  | MAC_SPEED_1000),
+       /**<    10 Mbps RGMII */
+       MAC_MODE_RGMII_10       = (MAC_IF_RGMII | MAC_SPEED_10),
+       /**<   100 Mbps RGMII */
+       MAC_MODE_RGMII_100   = (MAC_IF_RGMII | MAC_SPEED_100),
+       /**<  1000 Mbps RGMII */
+       MAC_MODE_RGMII_1000  = (MAC_IF_RGMII | MAC_SPEED_1000),
+       /**<  1000 Mbps TBI   */
+       MAC_MODE_TBI_1000       = (MAC_IF_TBI   | MAC_SPEED_1000),
+       /**<  1000 Mbps RTBI  */
+       MAC_MODE_RTBI_1000   = (MAC_IF_RTBI  | MAC_SPEED_1000),
+       /**<    10 Mbps SGMII */
+       MAC_MODE_SGMII_10       = (MAC_IF_SGMII | MAC_SPEED_10),
+       /**<   100 Mbps SGMII */
+       MAC_MODE_SGMII_100   = (MAC_IF_SGMII | MAC_SPEED_100),
+       /**<  1000 Mbps SGMII */
+       MAC_MODE_SGMII_1000  = (MAC_IF_SGMII | MAC_SPEED_1000),
+       /**< 10000 Mbps XGMII */
+       MAC_MODE_XGMII_10000 = (MAC_IF_XGMII | MAC_SPEED_10000),
+       /**<  1000 Mbps QSGMII */
+       MAC_MODE_QSGMII_1000 = (MAC_IF_QSGMII | MAC_SPEED_1000)
+};
+
+/*mac communicate mode*/
+enum mac_commom_mode {
+       MAC_COMM_MODE_NONE        = 0, /**< No transmit/receive communication */
+       MAC_COMM_MODE_RX                = 1, /**< Only receive communication */
+       MAC_COMM_MODE_TX                = 2, /**< Only transmit communication */
+       MAC_COMM_MODE_RX_AND_TX = 3  /**< Both tx and rx communication */
+};
+
+/*mac statistics */
+struct mac_statistics {
+       u64  stat_pkts64; /* r-10G tr-DT 64 byte frame counter */
+       u64  stat_pkts65to127; /* r-10G 65 to 127 byte frame counter */
+       u64  stat_pkts128to255; /* r-10G 128 to 255 byte frame counter */
+       u64  stat_pkts256to511; /*r-10G 256 to 511 byte frame counter */
+       u64  stat_pkts512to1023;/* r-10G 512 to 1023 byte frame counter */
+       u64  stat_pkts1024to1518; /* r-10G 1024 to 1518 byte frame counter */
+       u64  stat_pkts1519to1522; /* r-10G 1519 to 1522 byte good frame count*/
+       /* Total number of packets that were less than 64 octets */
+       /*                      long with a wrong CRC.*/
+       u64  stat_fragments;
+       /* Total number of packets longer than valid maximum length octets */
+       u64  stat_jabbers;
+       /* number of dropped packets due to internal errors of */
+       /*                      the MAC Client. */
+       u64  stat_drop_events;
+       /* Incremented when frames of correct length but with */
+       /*                      CRC error are received.*/
+       u64  stat_crc_align_errors;
+       /* Total number of packets that were less than 64 octets */
+       /*                      long with a good CRC.*/
+       u64  stat_undersize_pkts;
+       u64  stat_oversize_pkts;  /**< T,B.D*/
+
+       u64  stat_rx_pause;                /**< Pause MAC Control received */
+       u64  stat_tx_pause;                /**< Pause MAC Control sent */
+
+       u64  in_octets;         /**< Total number of byte received. */
+       u64  in_pkts;           /* Total number of packets received.*/
+       u64  in_mcast_pkts;     /* Total number of multicast frame received */
+       u64  in_bcast_pkts;     /* Total number of broadcast frame received */
+                               /* Frames received, but discarded due to */
+                               /* problems within the MAC RX. */
+       u64  in_discards;
+       u64  in_errors;         /* Number of frames received with error: */
+                               /*      - FIFO Overflow Error */
+                               /*      - CRC Error */
+                               /*      - Frame Too Long Error */
+                               /*      - Alignment Error */
+       u64  out_octets; /*Total number of byte sent. */
+       u64  out_pkts;  /**< Total number of packets sent .*/
+       u64  out_mcast_pkts; /* Total number of multicast frame sent */
+       u64  out_bcast_pkts; /* Total number of multicast frame sent */
+       /* Frames received, but discarded due to problems within */
+       /*                      the MAC TX N/A!.*/
+       u64  out_discards;
+       u64  out_errors;        /*Number of frames transmitted with error: */
+                       /*      - FIFO Overflow Error */
+                       /*      - FIFO Underflow Error */
+                       /*       - Other */
+};
+
+/*mac para struct ,mac get param from nic or dsaf when initialize*/
+struct mac_params {
+       char addr[MAC_NUM_OCTETS_PER_ADDR];
+       void *vaddr; /*virtual address*/
+       struct device *dev;
+       u8 mac_id;
+       /**< Ethernet operation mode (MAC-PHY interface and speed) */
+       enum mac_mode mac_mode;
+};
+
+struct mac_info {
+       u16 speed;/* The forced speed (lower bits) in */
+               /*               *mbps. Please use */
+               /*               * ethtool_cmd_speed()/_set() to */
+               /*               * access it */
+       u8 duplex;              /* Duplex, half or full */
+       u8 auto_neg;    /* Enable or disable autonegotiation */
+       enum hnae_loop loop_mode;
+       u8 tx_pause_en;
+       u8 tx_pause_time;
+       u8 rx_pause_en;
+       u8 pad_and_crc_en;
+       u8 promiscuous_en;
+       u8 port_en;      /*port enable*/
+};
+
+struct mac_entry_idx {
+       u8 addr[MAC_NUM_OCTETS_PER_ADDR];
+       u16 vlan_id:12;
+       u16 valid:1;
+       u16 qos:3;
+};
+
+struct mac_hw_stats {
+       u64 rx_good_pkts;       /* only for xgmac */
+       u64 rx_good_bytes;
+       u64 rx_total_pkts;      /* only for xgmac */
+       u64 rx_total_bytes;     /* only for xgmac */
+       u64 rx_bad_bytes;       /* only for gmac */
+       u64 rx_uc_pkts;
+       u64 rx_mc_pkts;
+       u64 rx_bc_pkts;
+       u64 rx_fragment_err;    /* only for xgmac */
+       u64 rx_undersize;       /* only for xgmac */
+       u64 rx_under_min;
+       u64 rx_minto64;         /* only for gmac */
+       u64 rx_64bytes;
+       u64 rx_65to127;
+       u64 rx_128to255;
+       u64 rx_256to511;
+       u64 rx_512to1023;
+       u64 rx_1024to1518;
+       u64 rx_1519tomax;
+       u64 rx_1519tomax_good;  /* only for xgmac */
+       u64 rx_oversize;
+       u64 rx_jabber_err;
+       u64 rx_fcs_err;
+       u64 rx_vlan_pkts;       /* only for gmac */
+       u64 rx_data_err;        /* only for gmac */
+       u64 rx_align_err;       /* only for gmac */
+       u64 rx_long_err;        /* only for gmac */
+       u64 rx_pfc_tc0;
+       u64 rx_pfc_tc1;         /* only for xgmac */
+       u64 rx_pfc_tc2;         /* only for xgmac */
+       u64 rx_pfc_tc3;         /* only for xgmac */
+       u64 rx_pfc_tc4;         /* only for xgmac */
+       u64 rx_pfc_tc5;         /* only for xgmac */
+       u64 rx_pfc_tc6;         /* only for xgmac */
+       u64 rx_pfc_tc7;         /* only for xgmac */
+       u64 rx_unknown_ctrl;
+       u64 rx_filter_pkts;     /* only for gmac */
+       u64 rx_filter_bytes;    /* only for gmac */
+       u64 rx_fifo_overrun_err;/* only for gmac */
+       u64 rx_len_err;         /* only for gmac */
+       u64 rx_comma_err;       /* only for gmac */
+       u64 rx_symbol_err;      /* only for xgmac */
+       u64 tx_good_to_sw;      /* only for xgmac */
+       u64 tx_bad_to_sw;       /* only for xgmac */
+       u64 rx_1731_pkts;       /* only for xgmac */
+
+       u64 tx_good_bytes;
+       u64 tx_good_pkts;       /* only for xgmac */
+       u64 tx_total_bytes;     /* only for xgmac */
+       u64 tx_total_pkts;      /* only for xgmac */
+       u64 tx_bad_bytes;       /* only for gmac */
+       u64 tx_bad_pkts;        /* only for xgmac */
+       u64 tx_uc_pkts;
+       u64 tx_mc_pkts;
+       u64 tx_bc_pkts;
+       u64 tx_undersize;       /* only for xgmac */
+       u64 tx_fragment_err;    /* only for xgmac */
+       u64 tx_under_min_pkts;  /* only for gmac */
+       u64 tx_64bytes;
+       u64 tx_65to127;
+       u64 tx_128to255;
+       u64 tx_256to511;
+       u64 tx_512to1023;
+       u64 tx_1024to1518;
+       u64 tx_1519tomax;
+       u64 tx_1519tomax_good;  /* only for xgmac */
+       u64 tx_oversize;        /* only for xgmac */
+       u64 tx_jabber_err;
+       u64 tx_underrun_err;    /* only for gmac */
+       u64 tx_vlan;            /* only for gmac */
+       u64 tx_crc_err;         /* only for gmac */
+       u64 tx_pfc_tc0;
+       u64 tx_pfc_tc1;         /* only for xgmac */
+       u64 tx_pfc_tc2;         /* only for xgmac */
+       u64 tx_pfc_tc3;         /* only for xgmac */
+       u64 tx_pfc_tc4;         /* only for xgmac */
+       u64 tx_pfc_tc5;         /* only for xgmac */
+       u64 tx_pfc_tc6;         /* only for xgmac */
+       u64 tx_pfc_tc7;         /* only for xgmac */
+       u64 tx_ctrl;            /* only for xgmac */
+       u64 tx_1731_pkts;       /* only for xgmac */
+       u64 tx_1588_pkts;       /* only for xgmac */
+       u64 rx_good_from_sw;    /* only for xgmac */
+       u64 rx_bad_from_sw;     /* only for xgmac */
+};
+
+struct hns_mac_cb {
+       struct device *dev;
+       struct dsaf_device *dsaf_dev;
+       struct mac_priv priv;
+       u8 __iomem *vaddr;
+       u8 __iomem *cpld_vaddr;
+       u8 __iomem *sys_ctl_vaddr;
+       u8 __iomem *serdes_vaddr;
+       struct mac_entry_idx addr_entry_idx[DSAF_MAX_VM_NUM];
+       u8 sfp_prsnt;
+       u8 cpld_led_value;
+       u8 mac_id;
+
+       u8 link;
+       u8 half_duplex;
+       u16 speed;
+       u16 max_speed;
+       u16 max_frm;
+       u16 tx_pause_frm_time;
+       u32 if_support;
+       u64 txpkt_for_led;
+       u64 rxpkt_for_led;
+       enum hnae_port_type mac_type;
+       phy_interface_t phy_if;
+       enum hnae_loop loop_mode;
+
+       struct device_node *phy_node;
+
+       struct mac_hw_stats hw_stats;
+};
+
+struct mac_driver {
+       /*init Mac when init nic or dsaf*/
+       void (*mac_init)(void *mac_drv);
+       /*remove mac when remove nic or dsaf*/
+       void (*mac_free)(void *mac_drv);
+       /*enable mac when enable nic or dsaf*/
+       void (*mac_enable)(void *mac_drv, enum mac_commom_mode mode);
+       /*disable mac when disable nic or dsaf*/
+       void (*mac_disable)(void *mac_drv, enum mac_commom_mode mode);
+       /* config mac address*/
+       void (*set_mac_addr)(void *mac_drv,     char *mac_addr);
+       /*adjust mac mode of port,include speed and duplex*/
+       int (*adjust_link)(void *mac_drv, enum mac_speed speed,
+                          u32 full_duplex);
+       /* config autoegotaite mode of port*/
+       void (*set_an_mode)(void *mac_drv, u8 enable);
+       /* config loopbank mode */
+       int (*config_loopback)(void *mac_drv, enum hnae_loop loop_mode,
+                              u8 enable);
+       /* config mtu*/
+       void (*config_max_frame_length)(void *mac_drv, u16 newval);
+       /*config PAD and CRC enable */
+       void (*config_pad_and_crc)(void *mac_drv, u8 newval);
+       /* config duplex mode*/
+       void (*config_half_duplex)(void *mac_drv, u8 newval);
+       /*config tx pause time,if pause_time is zero,disable tx pause enable*/
+       void (*set_tx_auto_pause_frames)(void *mac_drv, u16 pause_time);
+       /*config rx pause enable*/
+       void (*set_rx_ignore_pause_frames)(void *mac_drv, u32 enable);
+       /* config rx mode for promiscuous*/
+       int (*set_promiscuous)(void *mac_drv, u8 enable);
+       /* get mac id */
+       void (*mac_get_id)(void *mac_drv, u8 *mac_id);
+       void (*mac_pausefrm_cfg)(void *mac_drv, u32 rx_en, u32 tx_en);
+
+       void (*autoneg_stat)(void *mac_drv, u32 *enable);
+       int (*set_pause_enable)(void *mac_drv, u32 rx_en, u32 tx_en);
+       void (*get_pause_enable)(void *mac_drv, u32 *rx_en, u32 *tx_en);
+       void (*get_link_status)(void *mac_drv, u32 *link_stat);
+       /* get the imporant regs*/
+       void (*get_regs)(void *mac_drv, void *data);
+       int (*get_regs_count)(void);
+       /* get strings name for ethtool statistic */
+       void (*get_strings)(u32 stringset, u8 *data);
+       /* get the number of strings*/
+       int (*get_sset_count)(int stringset);
+
+       /* get the statistic by ethtools*/
+       void (*get_ethtool_stats)(void *mac_drv, u64 *data);
+
+       /* get mac information */
+       void (*get_info)(void *mac_drv, struct mac_info *mac_info);
+
+       void (*update_stats)(void *mac_drv);
+
+       enum mac_mode mac_mode;
+       u8 mac_id;
+       struct hns_mac_cb *mac_cb;
+       void __iomem *io_base;
+       unsigned int mac_en_flg;/*you'd better don't enable mac twice*/
+       unsigned int virt_dev_num;
+       struct device *dev;
+};
+
+struct mac_stats_string {
+       char desc[64];
+       unsigned long offset;
+};
+
+#define MAC_MAKE_MODE(interface, speed) (enum mac_mode)((interface) | (speed))
+#define MAC_INTERFACE_FROM_MODE(mode) (enum mac_intf)((mode) & 0xFFFF0000)
+#define MAC_SPEED_FROM_MODE(mode) (enum mac_speed)((mode) & 0x0000FFFF)
+#define MAC_STATS_FIELD_OFF(field) (offsetof(struct mac_hw_stats, field))
+
+static inline struct mac_driver *hns_mac_get_drv(
+       const struct hns_mac_cb *mac_cb)
+{
+       return (struct mac_driver *)(mac_cb->priv.mac);
+}
+
+void *hns_gmac_config(struct hns_mac_cb *mac_cb,
+                     struct mac_params *mac_param);
+void *hns_xgmac_config(struct hns_mac_cb *mac_cb,
+                      struct mac_params *mac_param);
+
+int hns_mac_init(struct dsaf_device *dsaf_dev);
+void mac_adjust_link(struct net_device *net_dev);
+void hns_mac_get_link_status(struct hns_mac_cb *mac_cb,        u32 *link_status);
+int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, u32 vmid, char *addr);
+int hns_mac_set_multi(struct hns_mac_cb *mac_cb,
+                     u32 port_num, char *addr, u8 en);
+int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vm, u8 en);
+void hns_mac_start(struct hns_mac_cb *mac_cb);
+void hns_mac_stop(struct hns_mac_cb *mac_cb);
+int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac);
+void hns_mac_uninit(struct dsaf_device *dsaf_dev);
+void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex);
+void hns_mac_reset(struct hns_mac_cb *mac_cb);
+void hns_mac_get_autoneg(struct hns_mac_cb *mac_cb, u32 *auto_neg);
+void hns_mac_get_pauseparam(struct hns_mac_cb *mac_cb, u32 *rx_en, u32 *tx_en);
+int hns_mac_set_autoneg(struct hns_mac_cb *mac_cb, u8 enable);
+int hns_mac_set_pauseparam(struct hns_mac_cb *mac_cb, u32 rx_en, u32 tx_en);
+int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu);
+int hns_mac_get_port_info(struct hns_mac_cb *mac_cb,
+                         u8 *auto_neg, u16 *speed, u8 *duplex);
+phy_interface_t hns_mac_get_phy_if(struct hns_mac_cb *mac_cb);
+int hns_mac_config_sds_loopback(struct hns_mac_cb *mac_cb, u8 en);
+int hns_mac_config_mac_loopback(struct hns_mac_cb *mac_cb,
+                               enum hnae_loop loop, int en);
+void hns_mac_update_stats(struct hns_mac_cb *mac_cb);
+void hns_mac_get_stats(struct hns_mac_cb *mac_cb, u64 *data);
+void hns_mac_get_strings(struct hns_mac_cb *mac_cb, int stringset, u8 *data);
+int hns_mac_get_sset_count(struct hns_mac_cb *mac_cb, int stringset);
+void hns_mac_get_regs(struct hns_mac_cb *mac_cb, void *data);
+int hns_mac_get_regs_count(struct hns_mac_cb *mac_cb);
+void hns_set_led_opt(struct hns_mac_cb *mac_cb);
+int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb,
+                       enum hnae_led_state status);
+#endif /* _HNS_DSAF_MAC_H */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
new file mode 100644 (file)
index 0000000..2a98eba
--- /dev/null
@@ -0,0 +1,2474 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/device.h>
+#include <linux/vmalloc.h>
+
+#include "hns_dsaf_main.h"
+#include "hns_dsaf_rcb.h"
+#include "hns_dsaf_ppe.h"
+#include "hns_dsaf_mac.h"
+
+const char *g_dsaf_mode_match[DSAF_MODE_MAX] = {
+       [DSAF_MODE_DISABLE_2PORT_64VM] = "2port-64vf",
+       [DSAF_MODE_DISABLE_6PORT_0VM] = "6port-16rss",
+       [DSAF_MODE_DISABLE_6PORT_16VM] = "6port-16vf",
+};
+
+int hns_dsaf_get_cfg(struct dsaf_device *dsaf_dev)
+{
+       int ret, i;
+       u32 desc_num;
+       u32 buf_size;
+       const char *name, *mode_str;
+       struct device_node *np = dsaf_dev->dev->of_node;
+
+       if (of_device_is_compatible(np, "hisilicon,hns-dsaf-v2"))
+               dsaf_dev->dsaf_ver = AE_VERSION_2;
+       else
+               dsaf_dev->dsaf_ver = AE_VERSION_1;
+
+       ret = of_property_read_string(np, "dsa_name", &name);
+       if (ret) {
+               dev_err(dsaf_dev->dev, "get dsaf name fail, ret=%d!\n", ret);
+               return ret;
+       }
+       strncpy(dsaf_dev->ae_dev.name, name, AE_NAME_SIZE);
+       dsaf_dev->ae_dev.name[AE_NAME_SIZE - 1] = '\0';
+
+       ret = of_property_read_string(np, "mode", &mode_str);
+       if (ret) {
+               dev_err(dsaf_dev->dev, "get dsaf mode fail, ret=%d!\n", ret);
+               return ret;
+       }
+       for (i = 0; i < DSAF_MODE_MAX; i++) {
+               if (g_dsaf_mode_match[i] &&
+                   !strcmp(mode_str, g_dsaf_mode_match[i]))
+                       break;
+       }
+       if (i >= DSAF_MODE_MAX ||
+           i == DSAF_MODE_INVALID || i == DSAF_MODE_ENABLE) {
+               dev_err(dsaf_dev->dev,
+                       "%s prs mode str fail!\n", dsaf_dev->ae_dev.name);
+               return -EINVAL;
+       }
+       dsaf_dev->dsaf_mode = (enum dsaf_mode)i;
+
+       if (dsaf_dev->dsaf_mode > DSAF_MODE_ENABLE)
+               dsaf_dev->dsaf_en = HRD_DSAF_NO_DSAF_MODE;
+       else
+               dsaf_dev->dsaf_en = HRD_DSAF_MODE;
+
+       if ((i == DSAF_MODE_ENABLE_16VM) ||
+           (i == DSAF_MODE_DISABLE_2PORT_8VM) ||
+           (i == DSAF_MODE_DISABLE_6PORT_2VM))
+               dsaf_dev->dsaf_tc_mode = HRD_DSAF_8TC_MODE;
+       else
+               dsaf_dev->dsaf_tc_mode = HRD_DSAF_4TC_MODE;
+
+       dsaf_dev->sc_base = of_iomap(np, 0);
+       if (!dsaf_dev->sc_base) {
+               dev_err(dsaf_dev->dev,
+                       "%s of_iomap 0 fail!\n", dsaf_dev->ae_dev.name);
+               ret = -ENOMEM;
+               goto unmap_base_addr;
+       }
+
+       dsaf_dev->sds_base = of_iomap(np, 1);
+       if (!dsaf_dev->sds_base) {
+               dev_err(dsaf_dev->dev,
+                       "%s of_iomap 1 fail!\n", dsaf_dev->ae_dev.name);
+               ret = -ENOMEM;
+               goto unmap_base_addr;
+       }
+
+       dsaf_dev->ppe_base = of_iomap(np, 2);
+       if (!dsaf_dev->ppe_base) {
+               dev_err(dsaf_dev->dev,
+                       "%s of_iomap 2 fail!\n", dsaf_dev->ae_dev.name);
+               ret = -ENOMEM;
+               goto unmap_base_addr;
+       }
+
+       dsaf_dev->io_base = of_iomap(np, 3);
+       if (!dsaf_dev->io_base) {
+               dev_err(dsaf_dev->dev,
+                       "%s of_iomap 3 fail!\n", dsaf_dev->ae_dev.name);
+               ret = -ENOMEM;
+               goto unmap_base_addr;
+       }
+
+       dsaf_dev->cpld_base = of_iomap(np, 4);
+       if (!dsaf_dev->cpld_base)
+               dev_dbg(dsaf_dev->dev, "NO CPLD ADDR");
+
+       ret = of_property_read_u32(np, "desc-num", &desc_num);
+       if (ret < 0 || desc_num < HNS_DSAF_MIN_DESC_CNT ||
+           desc_num > HNS_DSAF_MAX_DESC_CNT) {
+               dev_err(dsaf_dev->dev, "get desc-num(%d) fail, ret=%d!\n",
+                       desc_num, ret);
+               goto unmap_base_addr;
+       }
+       dsaf_dev->desc_num = desc_num;
+
+       ret = of_property_read_u32(np, "buf-size", &buf_size);
+       if (ret < 0) {
+               dev_err(dsaf_dev->dev,
+                       "get buf-size fail, ret=%d!\r\n", ret);
+               goto unmap_base_addr;
+       }
+       dsaf_dev->buf_size = buf_size;
+
+       dsaf_dev->buf_size_type = hns_rcb_buf_size2type(buf_size);
+       if (dsaf_dev->buf_size_type < 0) {
+               dev_err(dsaf_dev->dev,
+                       "buf_size(%d) is wrong!\n", buf_size);
+               goto unmap_base_addr;
+       }
+
+       if (!dma_set_mask_and_coherent(dsaf_dev->dev, DMA_BIT_MASK(64ULL)))
+               dev_dbg(dsaf_dev->dev, "set mask to 64bit\n");
+       else
+               dev_err(dsaf_dev->dev, "set mask to 64bit fail!\n");
+
+       return 0;
+
+unmap_base_addr:
+       if (dsaf_dev->io_base)
+               iounmap(dsaf_dev->io_base);
+       if (dsaf_dev->ppe_base)
+               iounmap(dsaf_dev->ppe_base);
+       if (dsaf_dev->sds_base)
+               iounmap(dsaf_dev->sds_base);
+       if (dsaf_dev->sc_base)
+               iounmap(dsaf_dev->sc_base);
+       if (dsaf_dev->cpld_base)
+               iounmap(dsaf_dev->cpld_base);
+       return ret;
+}
+
+static void hns_dsaf_free_cfg(struct dsaf_device *dsaf_dev)
+{
+       if (dsaf_dev->io_base)
+               iounmap(dsaf_dev->io_base);
+
+       if (dsaf_dev->ppe_base)
+               iounmap(dsaf_dev->ppe_base);
+
+       if (dsaf_dev->sds_base)
+               iounmap(dsaf_dev->sds_base);
+
+       if (dsaf_dev->sc_base)
+               iounmap(dsaf_dev->sc_base);
+
+       if (dsaf_dev->cpld_base)
+               iounmap(dsaf_dev->cpld_base);
+}
+
+/**
+ * hns_dsaf_sbm_link_sram_init_en - config dsaf_sbm_init_en
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_sbm_link_sram_init_en(struct dsaf_device *dsaf_dev)
+{
+       dsaf_set_dev_bit(dsaf_dev, DSAF_CFG_0_REG, DSAF_CFG_SBM_INIT_S, 1);
+}
+
+/**
+ * hns_dsaf_reg_cnt_clr_ce - config hns_dsaf_reg_cnt_clr_ce
+ * @dsaf_id: dsa fabric id
+ * @hns_dsaf_reg_cnt_clr_ce: config value
+ */
+static void
+hns_dsaf_reg_cnt_clr_ce(struct dsaf_device *dsaf_dev, u32 reg_cnt_clr_ce)
+{
+       dsaf_set_dev_bit(dsaf_dev, DSAF_DSA_REG_CNT_CLR_CE_REG,
+                        DSAF_CNT_CLR_CE_S, reg_cnt_clr_ce);
+}
+
+/**
+ * hns_ppe_qid_cfg - config ppe qid
+ * @dsaf_id: dsa fabric id
+ * @pppe_qid_cfg: value array
+ */
+static void
+hns_dsaf_ppe_qid_cfg(struct dsaf_device *dsaf_dev, u32 qid_cfg)
+{
+       u32 i;
+
+       for (i = 0; i < DSAF_COMM_CHN; i++) {
+               dsaf_set_dev_field(dsaf_dev,
+                                  DSAF_PPE_QID_CFG_0_REG + 0x0004 * i,
+                                  DSAF_PPE_QID_CFG_M, DSAF_PPE_QID_CFG_S,
+                                  qid_cfg);
+       }
+}
+
+static void hns_dsaf_mix_def_qid_cfg(struct dsaf_device *dsaf_dev)
+{
+       u16 max_q_per_vf, max_vfn;
+       u32 q_id, q_num_per_port;
+       u32 i;
+
+       hns_rcb_get_queue_mode(dsaf_dev->dsaf_mode,
+                              HNS_DSAF_COMM_SERVICE_NW_IDX,
+                              &max_vfn, &max_q_per_vf);
+       q_num_per_port = max_vfn * max_q_per_vf;
+
+       for (i = 0, q_id = 0; i < DSAF_SERVICE_NW_NUM; i++) {
+               dsaf_set_dev_field(dsaf_dev,
+                                  DSAF_MIX_DEF_QID_0_REG + 0x0004 * i,
+                                  0xff, 0, q_id);
+               q_id += q_num_per_port;
+       }
+}
+
+/**
+ * hns_dsaf_sw_port_type_cfg - cfg sw type
+ * @dsaf_id: dsa fabric id
+ * @psw_port_type: array
+ */
+static void hns_dsaf_sw_port_type_cfg(struct dsaf_device *dsaf_dev,
+                                     enum dsaf_sw_port_type port_type)
+{
+       u32 i;
+
+       for (i = 0; i < DSAF_SW_PORT_NUM; i++) {
+               dsaf_set_dev_field(dsaf_dev,
+                                  DSAF_SW_PORT_TYPE_0_REG + 0x0004 * i,
+                                  DSAF_SW_PORT_TYPE_M, DSAF_SW_PORT_TYPE_S,
+                                  port_type);
+       }
+}
+
+/**
+ * hns_dsaf_stp_port_type_cfg - cfg stp type
+ * @dsaf_id: dsa fabric id
+ * @pstp_port_type: array
+ */
+static void hns_dsaf_stp_port_type_cfg(struct dsaf_device *dsaf_dev,
+                                      enum dsaf_stp_port_type port_type)
+{
+       u32 i;
+
+       for (i = 0; i < DSAF_COMM_CHN; i++) {
+               dsaf_set_dev_field(dsaf_dev,
+                                  DSAF_STP_PORT_TYPE_0_REG + 0x0004 * i,
+                                  DSAF_STP_PORT_TYPE_M, DSAF_STP_PORT_TYPE_S,
+                                  port_type);
+       }
+}
+
+/**
+ * hns_dsaf_sbm_cfg - config sbm
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_sbm_cfg(struct dsaf_device *dsaf_dev)
+{
+       u32 o_sbm_cfg;
+       u32 i;
+
+       for (i = 0; i < DSAF_SBM_NUM; i++) {
+               o_sbm_cfg = dsaf_read_dev(dsaf_dev,
+                                         DSAF_SBM_CFG_REG_0_REG + 0x80 * i);
+               dsaf_set_bit(o_sbm_cfg, DSAF_SBM_CFG_EN_S, 1);
+               dsaf_set_bit(o_sbm_cfg, DSAF_SBM_CFG_SHCUT_EN_S, 0);
+               dsaf_write_dev(dsaf_dev,
+                              DSAF_SBM_CFG_REG_0_REG + 0x80 * i, o_sbm_cfg);
+       }
+}
+
+/**
+ * hns_dsaf_sbm_cfg_mib_en - config sbm
+ * @dsaf_id: dsa fabric id
+ */
+static int hns_dsaf_sbm_cfg_mib_en(struct dsaf_device *dsaf_dev)
+{
+       u32 sbm_cfg_mib_en;
+       u32 i;
+       u32 reg;
+       u32 read_cnt;
+
+       for (i = 0; i < DSAF_SBM_NUM; i++) {
+               reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i;
+               dsaf_set_dev_bit(dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S, 1);
+       }
+
+       /* waitint for all sbm enable finished */
+       for (i = 0; i < DSAF_SBM_NUM; i++) {
+               read_cnt = 0;
+               reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i;
+               do {
+                       udelay(1);
+                       sbm_cfg_mib_en = dsaf_get_dev_bit(
+                                       dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S);
+                       read_cnt++;
+               } while (sbm_cfg_mib_en == 0 &&
+                       read_cnt < DSAF_CFG_READ_CNT);
+
+               if (sbm_cfg_mib_en == 0) {
+                       dev_err(dsaf_dev->dev,
+                               "sbm_cfg_mib_en fail,%s,sbm_num=%d\n",
+                               dsaf_dev->ae_dev.name, i);
+                       return -ENODEV;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * hns_dsaf_sbm_bp_wl_cfg - config sbm
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev)
+{
+       u32 o_sbm_bp_cfg0;
+       u32 o_sbm_bp_cfg1;
+       u32 o_sbm_bp_cfg2;
+       u32 o_sbm_bp_cfg3;
+       u32 reg;
+       u32 i;
+
+       /* XGE */
+       for (i = 0; i < DSAF_XGE_NUM; i++) {
+               reg = DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + 0x80 * i;
+               o_sbm_bp_cfg0 = dsaf_read_dev(dsaf_dev, reg);
+               dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M,
+                              DSAF_SBM_CFG0_COM_MAX_BUF_NUM_S, 512);
+               dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M,
+                              DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_S, 0);
+               dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M,
+                              DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_S, 0);
+               dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg0);
+
+               reg = DSAF_SBM_BP_CFG_1_REG_0_REG + 0x80 * i;
+               o_sbm_bp_cfg1 = dsaf_read_dev(dsaf_dev, reg);
+               dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M,
+                              DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_S, 0);
+               dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M,
+                              DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_S, 0);
+               dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg1);
+
+               reg = DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + 0x80 * i;
+               o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg);
+               dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M,
+                              DSAF_SBM_CFG2_SET_BUF_NUM_S, 104);
+               dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
+                              DSAF_SBM_CFG2_RESET_BUF_NUM_S, 128);
+               dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2);
+
+               reg = DSAF_SBM_BP_CFG_3_REG_0_REG + 0x80 * i;
+               o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg);
+               dsaf_set_field(o_sbm_bp_cfg3,
+                              DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M,
+                              DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 110);
+               dsaf_set_field(o_sbm_bp_cfg3,
+                              DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M,
+                              DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 160);
+               dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3);
+
+               /* for no enable pfc mode */
+               reg = DSAF_SBM_BP_CFG_4_REG_0_REG + 0x80 * i;
+               o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg);
+               dsaf_set_field(o_sbm_bp_cfg3,
+                              DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M,
+                              DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 128);
+               dsaf_set_field(o_sbm_bp_cfg3,
+                              DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M,
+                              DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 192);
+               dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3);
+       }
+
+       /* PPE */
+       for (i = 0; i < DSAF_COMM_CHN; i++) {
+               reg = DSAF_SBM_BP_CFG_2_PPE_REG_0_REG + 0x80 * i;
+               o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg);
+               dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M,
+                              DSAF_SBM_CFG2_SET_BUF_NUM_S, 10);
+               dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
+                              DSAF_SBM_CFG2_RESET_BUF_NUM_S, 12);
+               dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2);
+       }
+
+       /* RoCEE */
+       for (i = 0; i < DSAF_COMM_CHN; i++) {
+               reg = DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG + 0x80 * i;
+               o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg);
+               dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M,
+                              DSAF_SBM_CFG2_SET_BUF_NUM_S, 2);
+               dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
+                              DSAF_SBM_CFG2_RESET_BUF_NUM_S, 4);
+               dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2);
+       }
+}
+
+/**
+ * hns_dsaf_voq_bp_all_thrd_cfg -  voq
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_voq_bp_all_thrd_cfg(struct dsaf_device *dsaf_dev)
+{
+       u32 voq_bp_all_thrd;
+       u32 i;
+
+       for (i = 0; i < DSAF_VOQ_NUM; i++) {
+               voq_bp_all_thrd = dsaf_read_dev(
+                       dsaf_dev, DSAF_VOQ_BP_ALL_THRD_0_REG + 0x40 * i);
+               if (i < DSAF_XGE_NUM) {
+                       dsaf_set_field(voq_bp_all_thrd,
+                                      DSAF_VOQ_BP_ALL_DOWNTHRD_M,
+                                      DSAF_VOQ_BP_ALL_DOWNTHRD_S, 930);
+                       dsaf_set_field(voq_bp_all_thrd,
+                                      DSAF_VOQ_BP_ALL_UPTHRD_M,
+                                      DSAF_VOQ_BP_ALL_UPTHRD_S, 950);
+               } else {
+                       dsaf_set_field(voq_bp_all_thrd,
+                                      DSAF_VOQ_BP_ALL_DOWNTHRD_M,
+                                      DSAF_VOQ_BP_ALL_DOWNTHRD_S, 220);
+                       dsaf_set_field(voq_bp_all_thrd,
+                                      DSAF_VOQ_BP_ALL_UPTHRD_M,
+                                      DSAF_VOQ_BP_ALL_UPTHRD_S, 230);
+               }
+               dsaf_write_dev(
+                       dsaf_dev, DSAF_VOQ_BP_ALL_THRD_0_REG + 0x40 * i,
+                       voq_bp_all_thrd);
+       }
+}
+
+/**
+ * hns_dsaf_tbl_tcam_data_cfg - tbl
+ * @dsaf_id: dsa fabric id
+ * @ptbl_tcam_data: addr
+ */
+static void hns_dsaf_tbl_tcam_data_cfg(
+       struct dsaf_device *dsaf_dev,
+       struct dsaf_tbl_tcam_data *ptbl_tcam_data)
+{
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_LOW_0_REG,
+                      ptbl_tcam_data->tbl_tcam_data_low);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_HIGH_0_REG,
+                      ptbl_tcam_data->tbl_tcam_data_high);
+}
+
+/**
+ * dsaf_tbl_tcam_mcast_cfg - tbl
+ * @dsaf_id: dsa fabric id
+ * @ptbl_tcam_mcast: addr
+ */
+static void hns_dsaf_tbl_tcam_mcast_cfg(
+       struct dsaf_device *dsaf_dev,
+       struct dsaf_tbl_tcam_mcast_cfg *mcast)
+{
+       u32 mcast_cfg4;
+
+       mcast_cfg4 = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG);
+       dsaf_set_bit(mcast_cfg4, DSAF_TBL_MCAST_CFG4_ITEM_VLD_S,
+                    mcast->tbl_mcast_item_vld);
+       dsaf_set_bit(mcast_cfg4, DSAF_TBL_MCAST_CFG4_OLD_EN_S,
+                    mcast->tbl_mcast_old_en);
+       dsaf_set_field(mcast_cfg4, DSAF_TBL_MCAST_CFG4_VM128_112_M,
+                      DSAF_TBL_MCAST_CFG4_VM128_112_S,
+                      mcast->tbl_mcast_port_msk[4]);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG, mcast_cfg4);
+
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG,
+                      mcast->tbl_mcast_port_msk[3]);
+
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG,
+                      mcast->tbl_mcast_port_msk[2]);
+
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG,
+                      mcast->tbl_mcast_port_msk[1]);
+
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG,
+                      mcast->tbl_mcast_port_msk[0]);
+}
+
+/**
+ * hns_dsaf_tbl_tcam_ucast_cfg - tbl
+ * @dsaf_id: dsa fabric id
+ * @ptbl_tcam_ucast: addr
+ */
+static void hns_dsaf_tbl_tcam_ucast_cfg(
+       struct dsaf_device *dsaf_dev,
+       struct dsaf_tbl_tcam_ucast_cfg *tbl_tcam_ucast)
+{
+       u32 ucast_cfg1;
+
+       ucast_cfg1 = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_UCAST_CFG_0_REG);
+       dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S,
+                    tbl_tcam_ucast->tbl_ucast_mac_discard);
+       dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_ITEM_VLD_S,
+                    tbl_tcam_ucast->tbl_ucast_item_vld);
+       dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_OLD_EN_S,
+                    tbl_tcam_ucast->tbl_ucast_old_en);
+       dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_DVC_S,
+                    tbl_tcam_ucast->tbl_ucast_dvc);
+       dsaf_set_field(ucast_cfg1, DSAF_TBL_UCAST_CFG1_OUT_PORT_M,
+                      DSAF_TBL_UCAST_CFG1_OUT_PORT_S,
+                      tbl_tcam_ucast->tbl_ucast_out_port);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_UCAST_CFG_0_REG, ucast_cfg1);
+}
+
+/**
+ * hns_dsaf_tbl_line_cfg - tbl
+ * @dsaf_id: dsa fabric id
+ * @ptbl_lin: addr
+ */
+static void hns_dsaf_tbl_line_cfg(struct dsaf_device *dsaf_dev,
+                                 struct dsaf_tbl_line_cfg *tbl_lin)
+{
+       u32 tbl_line;
+
+       tbl_line = dsaf_read_dev(dsaf_dev, DSAF_TBL_LIN_CFG_0_REG);
+       dsaf_set_bit(tbl_line, DSAF_TBL_LINE_CFG_MAC_DISCARD_S,
+                    tbl_lin->tbl_line_mac_discard);
+       dsaf_set_bit(tbl_line, DSAF_TBL_LINE_CFG_DVC_S,
+                    tbl_lin->tbl_line_dvc);
+       dsaf_set_field(tbl_line, DSAF_TBL_LINE_CFG_OUT_PORT_M,
+                      DSAF_TBL_LINE_CFG_OUT_PORT_S,
+                      tbl_lin->tbl_line_out_port);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_LIN_CFG_0_REG, tbl_line);
+}
+
+/**
+ * hns_dsaf_tbl_tcam_mcast_pul - tbl
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_tbl_tcam_mcast_pul(struct dsaf_device *dsaf_dev)
+{
+       u32 o_tbl_pul;
+
+       o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 1);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 0);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul);
+}
+
+/**
+ * hns_dsaf_tbl_line_pul - tbl
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_tbl_line_pul(struct dsaf_device *dsaf_dev)
+{
+       u32 tbl_pul;
+
+       tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG);
+       dsaf_set_bit(tbl_pul, DSAF_TBL_PUL_LINE_VLD_S, 1);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, tbl_pul);
+       dsaf_set_bit(tbl_pul, DSAF_TBL_PUL_LINE_VLD_S, 0);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, tbl_pul);
+}
+
+/**
+ * hns_dsaf_tbl_tcam_data_mcast_pul - tbl
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_tbl_tcam_data_mcast_pul(
+       struct dsaf_device *dsaf_dev)
+{
+       u32 o_tbl_pul;
+
+       o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 1);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 1);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 0);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 0);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul);
+}
+
+/**
+ * hns_dsaf_tbl_tcam_data_ucast_pul - tbl
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_tbl_tcam_data_ucast_pul(
+       struct dsaf_device *dsaf_dev)
+{
+       u32 o_tbl_pul;
+
+       o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 1);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_UCAST_VLD_S, 1);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 0);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_UCAST_VLD_S, 0);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul);
+}
+
+void hns_dsaf_set_promisc_mode(struct dsaf_device *dsaf_dev, u32 en)
+{
+       dsaf_set_dev_bit(dsaf_dev, DSAF_CFG_0_REG, DSAF_CFG_MIX_MODE_S, !!en);
+}
+
+/**
+ * hns_dsaf_tbl_stat_en - tbl
+ * @dsaf_id: dsa fabric id
+ * @ptbl_stat_en: addr
+ */
+static void hns_dsaf_tbl_stat_en(struct dsaf_device *dsaf_dev)
+{
+       u32 o_tbl_ctrl;
+
+       o_tbl_ctrl = dsaf_read_dev(dsaf_dev, DSAF_TBL_DFX_CTRL_0_REG);
+       dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_LINE_LKUP_NUM_EN_S, 1);
+       dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_UC_LKUP_NUM_EN_S, 1);
+       dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_MC_LKUP_NUM_EN_S, 1);
+       dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_BC_LKUP_NUM_EN_S, 1);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_DFX_CTRL_0_REG, o_tbl_ctrl);
+}
+
+/**
+ * hns_dsaf_rocee_bp_en - rocee back press enable
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_rocee_bp_en(struct dsaf_device *dsaf_dev)
+{
+       dsaf_set_dev_bit(dsaf_dev, DSAF_XGE_CTRL_SIG_CFG_0_REG,
+                        DSAF_FC_XGE_TX_PAUSE_S, 1);
+}
+
+/* set msk for dsaf exception irq*/
+static void hns_dsaf_int_xge_msk_set(struct dsaf_device *dsaf_dev,
+                                    u32 chnn_num, u32 mask_set)
+{
+       dsaf_write_dev(dsaf_dev,
+                      DSAF_XGE_INT_MSK_0_REG + 0x4 * chnn_num, mask_set);
+}
+
+static void hns_dsaf_int_ppe_msk_set(struct dsaf_device *dsaf_dev,
+                                    u32 chnn_num, u32 msk_set)
+{
+       dsaf_write_dev(dsaf_dev,
+                      DSAF_PPE_INT_MSK_0_REG + 0x4 * chnn_num, msk_set);
+}
+
+static void hns_dsaf_int_rocee_msk_set(struct dsaf_device *dsaf_dev,
+                                      u32 chnn, u32 msk_set)
+{
+       dsaf_write_dev(dsaf_dev,
+                      DSAF_ROCEE_INT_MSK_0_REG + 0x4 * chnn, msk_set);
+}
+
+static void
+hns_dsaf_int_tbl_msk_set(struct dsaf_device *dsaf_dev, u32 msk_set)
+{
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_INT_MSK_0_REG, msk_set);
+}
+
+/* clr dsaf exception irq*/
+static void hns_dsaf_int_xge_src_clr(struct dsaf_device *dsaf_dev,
+                                    u32 chnn_num, u32 int_src)
+{
+       dsaf_write_dev(dsaf_dev,
+                      DSAF_XGE_INT_SRC_0_REG + 0x4 * chnn_num, int_src);
+}
+
+static void hns_dsaf_int_ppe_src_clr(struct dsaf_device *dsaf_dev,
+                                    u32 chnn, u32 int_src)
+{
+       dsaf_write_dev(dsaf_dev,
+                      DSAF_PPE_INT_SRC_0_REG + 0x4 * chnn, int_src);
+}
+
+static void hns_dsaf_int_rocee_src_clr(struct dsaf_device *dsaf_dev,
+                                      u32 chnn, u32 int_src)
+{
+       dsaf_write_dev(dsaf_dev,
+                      DSAF_ROCEE_INT_SRC_0_REG + 0x4 * chnn, int_src);
+}
+
+static void hns_dsaf_int_tbl_src_clr(struct dsaf_device *dsaf_dev,
+                                    u32 int_src)
+{
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_INT_SRC_0_REG, int_src);
+}
+
+/**
+ * hns_dsaf_single_line_tbl_cfg - INT
+ * @dsaf_id: dsa fabric id
+ * @address:
+ * @ptbl_line:
+ */
+static void hns_dsaf_single_line_tbl_cfg(
+       struct dsaf_device *dsaf_dev,
+       u32 address, struct dsaf_tbl_line_cfg *ptbl_line)
+{
+       /*Write Addr*/
+       hns_dsaf_tbl_line_addr_cfg(dsaf_dev, address);
+
+       /*Write Line*/
+       hns_dsaf_tbl_line_cfg(dsaf_dev, ptbl_line);
+
+       /*Write Plus*/
+       hns_dsaf_tbl_line_pul(dsaf_dev);
+}
+
+/**
+ * hns_dsaf_tcam_uc_cfg - INT
+ * @dsaf_id: dsa fabric id
+ * @address,
+ * @ptbl_tcam_data,
+ */
+static void hns_dsaf_tcam_uc_cfg(
+       struct dsaf_device *dsaf_dev, u32 address,
+       struct dsaf_tbl_tcam_data *ptbl_tcam_data,
+       struct dsaf_tbl_tcam_ucast_cfg *ptbl_tcam_ucast)
+{
+       /*Write Addr*/
+       hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address);
+       /*Write Tcam Data*/
+       hns_dsaf_tbl_tcam_data_cfg(dsaf_dev, ptbl_tcam_data);
+       /*Write Tcam Ucast*/
+       hns_dsaf_tbl_tcam_ucast_cfg(dsaf_dev, ptbl_tcam_ucast);
+       /*Write Plus*/
+       hns_dsaf_tbl_tcam_data_ucast_pul(dsaf_dev);
+}
+
+/**
+ * hns_dsaf_tcam_mc_cfg - INT
+ * @dsaf_id: dsa fabric id
+ * @address,
+ * @ptbl_tcam_data,
+ * @ptbl_tcam_mcast,
+ */
+static void hns_dsaf_tcam_mc_cfg(
+       struct dsaf_device *dsaf_dev, u32 address,
+       struct dsaf_tbl_tcam_data *ptbl_tcam_data,
+       struct dsaf_tbl_tcam_mcast_cfg *ptbl_tcam_mcast)
+{
+       /*Write Addr*/
+       hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address);
+       /*Write Tcam Data*/
+       hns_dsaf_tbl_tcam_data_cfg(dsaf_dev, ptbl_tcam_data);
+       /*Write Tcam Mcast*/
+       hns_dsaf_tbl_tcam_mcast_cfg(dsaf_dev, ptbl_tcam_mcast);
+       /*Write Plus*/
+       hns_dsaf_tbl_tcam_data_mcast_pul(dsaf_dev);
+}
+
+/**
+ * hns_dsaf_tcam_mc_invld - INT
+ * @dsaf_id: dsa fabric id
+ * @address
+ */
+static void hns_dsaf_tcam_mc_invld(struct dsaf_device *dsaf_dev, u32 address)
+{
+       /*Write Addr*/
+       hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address);
+
+       /*write tcam mcast*/
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG, 0);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG, 0);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG, 0);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG, 0);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG, 0);
+
+       /*Write Plus*/
+       hns_dsaf_tbl_tcam_mcast_pul(dsaf_dev);
+}
+
+/**
+ * hns_dsaf_tcam_uc_get - INT
+ * @dsaf_id: dsa fabric id
+ * @address
+ * @ptbl_tcam_data
+ * @ptbl_tcam_ucast
+ */
+static void hns_dsaf_tcam_uc_get(
+       struct dsaf_device *dsaf_dev, u32 address,
+       struct dsaf_tbl_tcam_data *ptbl_tcam_data,
+       struct dsaf_tbl_tcam_ucast_cfg *ptbl_tcam_ucast)
+{
+       u32 tcam_read_data0;
+       u32 tcam_read_data4;
+
+       /*Write Addr*/
+       hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address);
+
+       /*read tcam item puls*/
+       hns_dsaf_tbl_tcam_load_pul(dsaf_dev);
+
+       /*read tcam data*/
+       ptbl_tcam_data->tbl_tcam_data_high
+               = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_LOW_0_REG);
+       ptbl_tcam_data->tbl_tcam_data_low
+               = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG);
+
+       /*read tcam mcast*/
+       tcam_read_data0 = dsaf_read_dev(dsaf_dev,
+                                       DSAF_TBL_TCAM_RAM_RDATA0_0_REG);
+       tcam_read_data4 = dsaf_read_dev(dsaf_dev,
+                                       DSAF_TBL_TCAM_RAM_RDATA4_0_REG);
+
+       ptbl_tcam_ucast->tbl_ucast_item_vld
+               = dsaf_get_bit(tcam_read_data4,
+                              DSAF_TBL_MCAST_CFG4_ITEM_VLD_S);
+       ptbl_tcam_ucast->tbl_ucast_old_en
+               = dsaf_get_bit(tcam_read_data4, DSAF_TBL_MCAST_CFG4_OLD_EN_S);
+       ptbl_tcam_ucast->tbl_ucast_mac_discard
+               = dsaf_get_bit(tcam_read_data0,
+                              DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S);
+       ptbl_tcam_ucast->tbl_ucast_out_port
+               = dsaf_get_field(tcam_read_data0,
+                                DSAF_TBL_UCAST_CFG1_OUT_PORT_M,
+                                DSAF_TBL_UCAST_CFG1_OUT_PORT_S);
+       ptbl_tcam_ucast->tbl_ucast_dvc
+               = dsaf_get_bit(tcam_read_data0, DSAF_TBL_UCAST_CFG1_DVC_S);
+}
+
+/**
+ * hns_dsaf_tcam_mc_get - INT
+ * @dsaf_id: dsa fabric id
+ * @address
+ * @ptbl_tcam_data
+ * @ptbl_tcam_ucast
+ */
+static void hns_dsaf_tcam_mc_get(
+       struct dsaf_device *dsaf_dev, u32 address,
+       struct dsaf_tbl_tcam_data *ptbl_tcam_data,
+       struct dsaf_tbl_tcam_mcast_cfg *ptbl_tcam_mcast)
+{
+       u32 data_tmp;
+
+       /*Write Addr*/
+       hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address);
+
+       /*read tcam item puls*/
+       hns_dsaf_tbl_tcam_load_pul(dsaf_dev);
+
+       /*read tcam data*/
+       ptbl_tcam_data->tbl_tcam_data_high =
+               dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_LOW_0_REG);
+       ptbl_tcam_data->tbl_tcam_data_low =
+               dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG);
+
+       /*read tcam mcast*/
+       ptbl_tcam_mcast->tbl_mcast_port_msk[0] =
+               dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA0_0_REG);
+       ptbl_tcam_mcast->tbl_mcast_port_msk[1] =
+               dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA1_0_REG);
+       ptbl_tcam_mcast->tbl_mcast_port_msk[2] =
+               dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA2_0_REG);
+       ptbl_tcam_mcast->tbl_mcast_port_msk[3] =
+               dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA3_0_REG);
+
+       data_tmp = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA4_0_REG);
+       ptbl_tcam_mcast->tbl_mcast_item_vld =
+               dsaf_get_bit(data_tmp, DSAF_TBL_MCAST_CFG4_ITEM_VLD_S);
+       ptbl_tcam_mcast->tbl_mcast_old_en =
+               dsaf_get_bit(data_tmp, DSAF_TBL_MCAST_CFG4_OLD_EN_S);
+       ptbl_tcam_mcast->tbl_mcast_port_msk[4] =
+               dsaf_get_field(data_tmp, DSAF_TBL_MCAST_CFG4_VM128_112_M,
+                              DSAF_TBL_MCAST_CFG4_VM128_112_S);
+}
+
+/**
+ * hns_dsaf_tbl_line_init - INT
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_tbl_line_init(struct dsaf_device *dsaf_dev)
+{
+       u32 i;
+       /* defaultly set all lineal mac table entry resulting discard */
+       struct dsaf_tbl_line_cfg tbl_line[] = {{1, 0, 0} };
+
+       for (i = 0; i < DSAF_LINE_SUM; i++)
+               hns_dsaf_single_line_tbl_cfg(dsaf_dev, i, tbl_line);
+}
+
+/**
+ * hns_dsaf_tbl_tcam_init - INT
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_tbl_tcam_init(struct dsaf_device *dsaf_dev)
+{
+       u32 i;
+       struct dsaf_tbl_tcam_data tcam_data[] = {{0, 0} };
+       struct dsaf_tbl_tcam_ucast_cfg tcam_ucast[] = {{0, 0, 0, 0, 0} };
+
+       /*tcam tbl*/
+       for (i = 0; i < DSAF_TCAM_SUM; i++)
+               hns_dsaf_tcam_uc_cfg(dsaf_dev, i, tcam_data, tcam_ucast);
+}
+
+/**
+ * hns_dsaf_pfc_en_cfg - dsaf pfc pause cfg
+ * @mac_cb: mac contrl block
+ */
+static void hns_dsaf_pfc_en_cfg(struct dsaf_device *dsaf_dev,
+                               int mac_id, int en)
+{
+       if (!en)
+               dsaf_write_dev(dsaf_dev, DSAF_PFC_EN_0_REG + mac_id * 4, 0);
+       else
+               dsaf_write_dev(dsaf_dev, DSAF_PFC_EN_0_REG + mac_id * 4, 0xff);
+}
+
+/**
+ * hns_dsaf_tbl_tcam_init - INT
+ * @dsaf_id: dsa fabric id
+ * @dsaf_mode
+ */
+static void hns_dsaf_comm_init(struct dsaf_device *dsaf_dev)
+{
+       u32 i;
+       u32 o_dsaf_cfg;
+
+       o_dsaf_cfg = dsaf_read_dev(dsaf_dev, DSAF_CFG_0_REG);
+       dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_EN_S, dsaf_dev->dsaf_en);
+       dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_TC_MODE_S, dsaf_dev->dsaf_tc_mode);
+       dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_CRC_EN_S, 0);
+       dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_MIX_MODE_S, 0);
+       dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_LOCA_ADDR_EN_S, 0);
+       dsaf_write_dev(dsaf_dev, DSAF_CFG_0_REG, o_dsaf_cfg);
+
+       hns_dsaf_reg_cnt_clr_ce(dsaf_dev, 1);
+       hns_dsaf_stp_port_type_cfg(dsaf_dev, DSAF_STP_PORT_TYPE_FORWARD);
+
+       /* set 22 queue per tx ppe engine, only used in switch mode */
+       hns_dsaf_ppe_qid_cfg(dsaf_dev, DSAF_DEFAUTL_QUEUE_NUM_PER_PPE);
+
+       /* set promisc def queue id */
+       hns_dsaf_mix_def_qid_cfg(dsaf_dev);
+
+       /* in non switch mode, set all port to access mode */
+       hns_dsaf_sw_port_type_cfg(dsaf_dev, DSAF_SW_PORT_TYPE_NON_VLAN);
+
+       /*set dsaf pfc  to 0 for parseing rx pause*/
+       for (i = 0; i < DSAF_COMM_CHN; i++)
+               hns_dsaf_pfc_en_cfg(dsaf_dev, i, 0);
+
+       /*msk and  clr exception irqs */
+       for (i = 0; i < DSAF_COMM_CHN; i++) {
+               hns_dsaf_int_xge_src_clr(dsaf_dev, i, 0xfffffffful);
+               hns_dsaf_int_ppe_src_clr(dsaf_dev, i, 0xfffffffful);
+               hns_dsaf_int_rocee_src_clr(dsaf_dev, i, 0xfffffffful);
+
+               hns_dsaf_int_xge_msk_set(dsaf_dev, i, 0xfffffffful);
+               hns_dsaf_int_ppe_msk_set(dsaf_dev, i, 0xfffffffful);
+               hns_dsaf_int_rocee_msk_set(dsaf_dev, i, 0xfffffffful);
+       }
+       hns_dsaf_int_tbl_src_clr(dsaf_dev, 0xfffffffful);
+       hns_dsaf_int_tbl_msk_set(dsaf_dev, 0xfffffffful);
+}
+
+/**
+ * hns_dsaf_inode_init - INT
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_inode_init(struct dsaf_device *dsaf_dev)
+{
+       u32 reg;
+       u32 tc_cfg;
+       u32 i;
+
+       if (dsaf_dev->dsaf_tc_mode == HRD_DSAF_4TC_MODE)
+               tc_cfg = HNS_DSAF_I4TC_CFG;
+       else
+               tc_cfg = HNS_DSAF_I8TC_CFG;
+
+       for (i = 0; i < DSAF_INODE_NUM; i++) {
+               reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i;
+               dsaf_set_dev_field(dsaf_dev, reg, DSAF_INODE_IN_PORT_NUM_M,
+                                  DSAF_INODE_IN_PORT_NUM_S, i % DSAF_XGE_NUM);
+
+               reg = DSAF_INODE_PRI_TC_CFG_0_REG + 0x80 * i;
+               dsaf_write_dev(dsaf_dev, reg, tc_cfg);
+       }
+}
+
+/**
+ * hns_dsaf_sbm_init - INT
+ * @dsaf_id: dsa fabric id
+ */
+static int hns_dsaf_sbm_init(struct dsaf_device *dsaf_dev)
+{
+       u32 flag;
+       u32 cnt = 0;
+       int ret;
+
+       hns_dsaf_sbm_bp_wl_cfg(dsaf_dev);
+
+       /* enable sbm chanel, disable sbm chanel shcut function*/
+       hns_dsaf_sbm_cfg(dsaf_dev);
+
+       /* enable sbm mib */
+       ret = hns_dsaf_sbm_cfg_mib_en(dsaf_dev);
+       if (ret) {
+               dev_err(dsaf_dev->dev,
+                       "hns_dsaf_sbm_cfg_mib_en fail,%s, ret=%d\n",
+                       dsaf_dev->ae_dev.name, ret);
+               return ret;
+       }
+
+       /* enable sbm initial link sram */
+       hns_dsaf_sbm_link_sram_init_en(dsaf_dev);
+
+       do {
+               usleep_range(200, 210);/*udelay(200);*/
+               flag = dsaf_read_dev(dsaf_dev, DSAF_SRAM_INIT_OVER_0_REG);
+               cnt++;
+       } while (flag != DSAF_SRAM_INIT_FINISH_FLAG && cnt < DSAF_CFG_READ_CNT);
+
+       if (flag != DSAF_SRAM_INIT_FINISH_FLAG) {
+               dev_err(dsaf_dev->dev,
+                       "hns_dsaf_sbm_init fail %s, flag=%d, cnt=%d\n",
+                       dsaf_dev->ae_dev.name, flag, cnt);
+               return -ENODEV;
+       }
+
+       hns_dsaf_rocee_bp_en(dsaf_dev);
+
+       return 0;
+}
+
+/**
+ * hns_dsaf_tbl_init - INT
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_tbl_init(struct dsaf_device *dsaf_dev)
+{
+       hns_dsaf_tbl_stat_en(dsaf_dev);
+
+       hns_dsaf_tbl_tcam_init(dsaf_dev);
+       hns_dsaf_tbl_line_init(dsaf_dev);
+}
+
+/**
+ * hns_dsaf_voq_init - INT
+ * @dsaf_id: dsa fabric id
+ */
+static void hns_dsaf_voq_init(struct dsaf_device *dsaf_dev)
+{
+       hns_dsaf_voq_bp_all_thrd_cfg(dsaf_dev);
+}
+
+/**
+ * hns_dsaf_init_hw - init dsa fabric hardware
+ * @dsaf_dev: dsa fabric device struct pointer
+ */
+static int hns_dsaf_init_hw(struct dsaf_device *dsaf_dev)
+{
+       int ret;
+
+       dev_dbg(dsaf_dev->dev,
+               "hns_dsaf_init_hw begin %s !\n", dsaf_dev->ae_dev.name);
+
+       hns_dsaf_rst(dsaf_dev, 0);
+       mdelay(10);
+       hns_dsaf_rst(dsaf_dev, 1);
+
+       hns_dsaf_comm_init(dsaf_dev);
+
+       /*init XBAR_INODE*/
+       hns_dsaf_inode_init(dsaf_dev);
+
+       /*init SBM*/
+       ret = hns_dsaf_sbm_init(dsaf_dev);
+       if (ret)
+               return ret;
+
+       /*init TBL*/
+       hns_dsaf_tbl_init(dsaf_dev);
+
+       /*init VOQ*/
+       hns_dsaf_voq_init(dsaf_dev);
+
+       return 0;
+}
+
+/**
+ * hns_dsaf_remove_hw - uninit dsa fabric hardware
+ * @dsaf_dev: dsa fabric device struct pointer
+ */
+static void hns_dsaf_remove_hw(struct dsaf_device *dsaf_dev)
+{
+       /*reset*/
+       hns_dsaf_rst(dsaf_dev, 0);
+}
+
+/**
+ * hns_dsaf_init - init dsa fabric
+ * @dsaf_dev: dsa fabric device struct pointer
+ * retuen 0 - success , negative --fail
+ */
+static int hns_dsaf_init(struct dsaf_device *dsaf_dev)
+{
+       struct dsaf_drv_priv *priv =
+           (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev);
+       u32 i;
+       int ret;
+
+       ret = hns_dsaf_init_hw(dsaf_dev);
+       if (ret)
+               return ret;
+
+       /* malloc mem for tcam mac key(vlan+mac) */
+       priv->soft_mac_tbl = vzalloc(sizeof(*priv->soft_mac_tbl)
+                 * DSAF_TCAM_SUM);
+       if (!priv->soft_mac_tbl) {
+               ret = -ENOMEM;
+               goto remove_hw;
+       }
+
+       /*all entry invall */
+       for (i = 0; i < DSAF_TCAM_SUM; i++)
+               (priv->soft_mac_tbl + i)->index = DSAF_INVALID_ENTRY_IDX;
+
+       return 0;
+
+remove_hw:
+       hns_dsaf_remove_hw(dsaf_dev);
+       return ret;
+}
+
+/**
+ * hns_dsaf_free - free dsa fabric
+ * @dsaf_dev: dsa fabric device struct pointer
+ */
+static void hns_dsaf_free(struct dsaf_device *dsaf_dev)
+{
+       struct dsaf_drv_priv *priv =
+           (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev);
+
+       hns_dsaf_remove_hw(dsaf_dev);
+
+       /* free all mac mem */
+       vfree(priv->soft_mac_tbl);
+       priv->soft_mac_tbl = NULL;
+}
+
+/**
+ * hns_dsaf_find_soft_mac_entry - find dsa fabric soft entry
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @mac_key: mac entry struct pointer
+ */
+static u16 hns_dsaf_find_soft_mac_entry(
+       struct dsaf_device *dsaf_dev,
+       struct dsaf_drv_tbl_tcam_key *mac_key)
+{
+       struct dsaf_drv_priv *priv =
+           (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev);
+       struct dsaf_drv_soft_mac_tbl *soft_mac_entry;
+       u32 i;
+
+       soft_mac_entry = priv->soft_mac_tbl;
+       for (i = 0; i < DSAF_TCAM_SUM; i++) {
+               /* invall tab entry */
+               if ((soft_mac_entry->index != DSAF_INVALID_ENTRY_IDX) &&
+                   (soft_mac_entry->tcam_key.high.val == mac_key->high.val) &&
+                   (soft_mac_entry->tcam_key.low.val == mac_key->low.val))
+                       /* return find result --soft index */
+                       return soft_mac_entry->index;
+
+               soft_mac_entry++;
+       }
+       return DSAF_INVALID_ENTRY_IDX;
+}
+
+/**
+ * hns_dsaf_find_empty_mac_entry - search dsa fabric soft empty-entry
+ * @dsaf_dev: dsa fabric device struct pointer
+ */
+static u16 hns_dsaf_find_empty_mac_entry(struct dsaf_device *dsaf_dev)
+{
+       struct dsaf_drv_priv *priv =
+           (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev);
+       struct dsaf_drv_soft_mac_tbl *soft_mac_entry;
+       u32 i;
+
+       soft_mac_entry = priv->soft_mac_tbl;
+       for (i = 0; i < DSAF_TCAM_SUM; i++) {
+               /* inv all entry */
+               if (soft_mac_entry->index == DSAF_INVALID_ENTRY_IDX)
+                       /* return find result --soft index */
+                       return i;
+
+               soft_mac_entry++;
+       }
+       return DSAF_INVALID_ENTRY_IDX;
+}
+
+/**
+ * hns_dsaf_set_mac_key - set mac key
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @mac_key: tcam key pointer
+ * @vlan_id: vlan id
+ * @in_port_num: input port num
+ * @addr: mac addr
+ */
+static void hns_dsaf_set_mac_key(
+       struct dsaf_device *dsaf_dev,
+       struct dsaf_drv_tbl_tcam_key *mac_key, u16 vlan_id, u8 in_port_num,
+       u8 *addr)
+{
+       u8 port;
+
+       if (dsaf_dev->dsaf_mode <= DSAF_MODE_ENABLE)
+               /*DSAF mode : in port id fixed 0*/
+               port = 0;
+       else
+               /*non-dsaf mode*/
+               port = in_port_num;
+
+       mac_key->high.bits.mac_0 = addr[0];
+       mac_key->high.bits.mac_1 = addr[1];
+       mac_key->high.bits.mac_2 = addr[2];
+       mac_key->high.bits.mac_3 = addr[3];
+       mac_key->low.bits.mac_4 = addr[4];
+       mac_key->low.bits.mac_5 = addr[5];
+       mac_key->low.bits.vlan = vlan_id;
+       mac_key->low.bits.port = port;
+}
+
+/**
+ * hns_dsaf_set_mac_uc_entry - set mac uc-entry
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @mac_entry: uc-mac entry
+ */
+int hns_dsaf_set_mac_uc_entry(
+       struct dsaf_device *dsaf_dev,
+       struct dsaf_drv_mac_single_dest_entry *mac_entry)
+{
+       u16 entry_index = DSAF_INVALID_ENTRY_IDX;
+       struct dsaf_drv_tbl_tcam_key mac_key;
+       struct dsaf_tbl_tcam_ucast_cfg mac_data;
+       struct dsaf_drv_priv *priv =
+           (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev);
+       struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl;
+
+       /* mac addr check */
+       if (MAC_IS_ALL_ZEROS(mac_entry->addr) ||
+           MAC_IS_BROADCAST(mac_entry->addr) ||
+           MAC_IS_MULTICAST(mac_entry->addr)) {
+               dev_err(dsaf_dev->dev,
+                       "set_uc %s Mac %02x:%02x:%02x:%02x:%02x:%02x err!\n",
+                       dsaf_dev->ae_dev.name, mac_entry->addr[0],
+                       mac_entry->addr[1], mac_entry->addr[2],
+                       mac_entry->addr[3], mac_entry->addr[4],
+                       mac_entry->addr[5]);
+               return -EINVAL;
+       }
+
+       /* config key */
+       hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id,
+                            mac_entry->in_port_num, mac_entry->addr);
+
+       /* entry ie exist? */
+       entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key);
+       if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+               /*if has not inv entry,find a empty entry */
+               entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev);
+               if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+                       /* has not empty,return error */
+                       dev_err(dsaf_dev->dev,
+                               "set_uc_entry failed, %s Mac key(%#x:%#x)\n",
+                               dsaf_dev->ae_dev.name,
+                               mac_key.high.val, mac_key.low.val);
+                       return -EINVAL;
+               }
+       }
+
+       dev_dbg(dsaf_dev->dev,
+               "set_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n",
+               dsaf_dev->ae_dev.name, mac_key.high.val,
+               mac_key.low.val, entry_index);
+
+       /* config hardware entry */
+       mac_data.tbl_ucast_item_vld = 1;
+       mac_data.tbl_ucast_mac_discard = 0;
+       mac_data.tbl_ucast_old_en = 0;
+       /* default config dvc to 0 */
+       mac_data.tbl_ucast_dvc = 0;
+       mac_data.tbl_ucast_out_port = mac_entry->port_num;
+       hns_dsaf_tcam_uc_cfg(
+               dsaf_dev, entry_index,
+               (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data);
+
+       /* config software entry */
+       soft_mac_entry += entry_index;
+       soft_mac_entry->index = entry_index;
+       soft_mac_entry->tcam_key.high.val = mac_key.high.val;
+       soft_mac_entry->tcam_key.low.val = mac_key.low.val;
+
+       return 0;
+}
+
+/**
+ * hns_dsaf_set_mac_mc_entry - set mac mc-entry
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @mac_entry: mc-mac entry
+ */
+int hns_dsaf_set_mac_mc_entry(
+       struct dsaf_device *dsaf_dev,
+       struct dsaf_drv_mac_multi_dest_entry *mac_entry)
+{
+       u16 entry_index = DSAF_INVALID_ENTRY_IDX;
+       struct dsaf_drv_tbl_tcam_key mac_key;
+       struct dsaf_tbl_tcam_mcast_cfg mac_data;
+       struct dsaf_drv_priv *priv =
+           (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev);
+       struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl;
+       struct dsaf_drv_tbl_tcam_key tmp_mac_key;
+
+       /* mac addr check */
+       if (MAC_IS_ALL_ZEROS(mac_entry->addr)) {
+               dev_err(dsaf_dev->dev,
+                       "set uc %s Mac %02x:%02x:%02x:%02x:%02x:%02x err!\n",
+                       dsaf_dev->ae_dev.name, mac_entry->addr[0],
+                       mac_entry->addr[1], mac_entry->addr[2],
+                       mac_entry->addr[3],
+                       mac_entry->addr[4], mac_entry->addr[5]);
+               return -EINVAL;
+       }
+
+       /*config key */
+       hns_dsaf_set_mac_key(dsaf_dev, &mac_key,
+                            mac_entry->in_vlan_id,
+                            mac_entry->in_port_num, mac_entry->addr);
+
+       /* entry ie exist? */
+       entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key);
+       if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+               /*if hasnot, find enpty entry*/
+               entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev);
+               if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+                       /*if hasnot empty, error*/
+                       dev_err(dsaf_dev->dev,
+                               "set_uc_entry failed, %s Mac key(%#x:%#x)\n",
+                               dsaf_dev->ae_dev.name,
+                               mac_key.high.val, mac_key.low.val);
+                       return -EINVAL;
+               }
+
+               /* config hardware entry */
+               memset(mac_data.tbl_mcast_port_msk,
+                      0, sizeof(mac_data.tbl_mcast_port_msk));
+       } else {
+               /* config hardware entry */
+               hns_dsaf_tcam_mc_get(
+                       dsaf_dev, entry_index,
+                       (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data);
+       }
+       mac_data.tbl_mcast_old_en = 0;
+       mac_data.tbl_mcast_item_vld = 1;
+       dsaf_set_field(mac_data.tbl_mcast_port_msk[0],
+                      0x3F, 0, mac_entry->port_mask[0]);
+
+       dev_dbg(dsaf_dev->dev,
+               "set_uc_entry, %s key(%#x:%#x) entry_index%d\n",
+               dsaf_dev->ae_dev.name, mac_key.high.val,
+               mac_key.low.val, entry_index);
+
+       hns_dsaf_tcam_mc_cfg(
+               dsaf_dev, entry_index,
+               (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data);
+
+       /* config software entry */
+       soft_mac_entry += entry_index;
+       soft_mac_entry->index = entry_index;
+       soft_mac_entry->tcam_key.high.val = mac_key.high.val;
+       soft_mac_entry->tcam_key.low.val = mac_key.low.val;
+
+       return 0;
+}
+
+/**
+ * hns_dsaf_add_mac_mc_port - add mac mc-port
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @mac_entry: mc-mac entry
+ */
+int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev,
+                            struct dsaf_drv_mac_single_dest_entry *mac_entry)
+{
+       u16 entry_index = DSAF_INVALID_ENTRY_IDX;
+       struct dsaf_drv_tbl_tcam_key mac_key;
+       struct dsaf_tbl_tcam_mcast_cfg mac_data;
+       struct dsaf_drv_priv *priv =
+           (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev);
+       struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl;
+       struct dsaf_drv_tbl_tcam_key tmp_mac_key;
+       int mskid;
+
+       /*chechk mac addr */
+       if (MAC_IS_ALL_ZEROS(mac_entry->addr)) {
+               dev_err(dsaf_dev->dev,
+                       "set_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x!\n",
+                       mac_entry->addr[0], mac_entry->addr[1],
+                       mac_entry->addr[2], mac_entry->addr[3],
+                       mac_entry->addr[4], mac_entry->addr[5]);
+               return -EINVAL;
+       }
+
+       /*config key */
+       hns_dsaf_set_mac_key(
+               dsaf_dev, &mac_key, mac_entry->in_vlan_id,
+               mac_entry->in_port_num, mac_entry->addr);
+
+       memset(&mac_data, 0, sizeof(struct dsaf_tbl_tcam_mcast_cfg));
+
+       /*check exist? */
+       entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key);
+       if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+               /*if hasnot , find a empty*/
+               entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev);
+               if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+                       /*if hasnot empty, error*/
+                       dev_err(dsaf_dev->dev,
+                               "set_uc_entry failed, %s Mac key(%#x:%#x)\n",
+                               dsaf_dev->ae_dev.name, mac_key.high.val,
+                               mac_key.low.val);
+                       return -EINVAL;
+               }
+       } else {
+               /*if exist, add in */
+               hns_dsaf_tcam_mc_get(
+                       dsaf_dev, entry_index,
+                       (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data);
+       }
+       /* config hardware entry */
+       if (mac_entry->port_num < DSAF_SERVICE_NW_NUM) {
+               mskid = mac_entry->port_num;
+       } else if (mac_entry->port_num >= DSAF_BASE_INNER_PORT_NUM) {
+               mskid = mac_entry->port_num -
+                       DSAF_BASE_INNER_PORT_NUM + DSAF_SERVICE_NW_NUM;
+       } else {
+               dev_err(dsaf_dev->dev,
+                       "%s,pnum(%d)error,key(%#x:%#x)\n",
+                       dsaf_dev->ae_dev.name, mac_entry->port_num,
+                       mac_key.high.val, mac_key.low.val);
+               return -EINVAL;
+       }
+       dsaf_set_bit(mac_data.tbl_mcast_port_msk[mskid / 32], mskid % 32, 1);
+       mac_data.tbl_mcast_old_en = 0;
+       mac_data.tbl_mcast_item_vld = 1;
+
+       dev_dbg(dsaf_dev->dev,
+               "set_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n",
+               dsaf_dev->ae_dev.name, mac_key.high.val,
+               mac_key.low.val, entry_index);
+
+       hns_dsaf_tcam_mc_cfg(
+               dsaf_dev, entry_index,
+               (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data);
+
+       /*config software entry */
+       soft_mac_entry += entry_index;
+       soft_mac_entry->index = entry_index;
+       soft_mac_entry->tcam_key.high.val = mac_key.high.val;
+       soft_mac_entry->tcam_key.low.val = mac_key.low.val;
+
+       return 0;
+}
+
+/**
+ * hns_dsaf_del_mac_entry - del mac mc-port
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @vlan_id: vlian id
+ * @in_port_num: input port num
+ * @addr : mac addr
+ */
+int hns_dsaf_del_mac_entry(struct dsaf_device *dsaf_dev, u16 vlan_id,
+                          u8 in_port_num, u8 *addr)
+{
+       u16 entry_index = DSAF_INVALID_ENTRY_IDX;
+       struct dsaf_drv_tbl_tcam_key mac_key;
+       struct dsaf_drv_priv *priv =
+           (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev);
+       struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl;
+
+       /*check mac addr */
+       if (MAC_IS_ALL_ZEROS(addr) || MAC_IS_BROADCAST(addr)) {
+               dev_err(dsaf_dev->dev,
+                       "del_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x!\n",
+                       addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+               return -EINVAL;
+       }
+
+       /*config key */
+       hns_dsaf_set_mac_key(dsaf_dev, &mac_key, vlan_id, in_port_num, addr);
+
+       /*exist ?*/
+       entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key);
+       if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+               /*not exist, error */
+               dev_err(dsaf_dev->dev,
+                       "del_mac_entry failed, %s Mac key(%#x:%#x)\n",
+                       dsaf_dev->ae_dev.name,
+                       mac_key.high.val, mac_key.low.val);
+               return -EINVAL;
+       }
+       dev_dbg(dsaf_dev->dev,
+               "del_mac_entry, %s Mac key(%#x:%#x) entry_index%d\n",
+               dsaf_dev->ae_dev.name, mac_key.high.val,
+               mac_key.low.val, entry_index);
+
+       /*do del opt*/
+       hns_dsaf_tcam_mc_invld(dsaf_dev, entry_index);
+
+       /*del soft emtry */
+       soft_mac_entry += entry_index;
+       soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX;
+
+       return 0;
+}
+
+/**
+ * hns_dsaf_del_mac_mc_port - del mac mc- port
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @mac_entry: mac entry
+ */
+int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev,
+                            struct dsaf_drv_mac_single_dest_entry *mac_entry)
+{
+       u16 entry_index = DSAF_INVALID_ENTRY_IDX;
+       struct dsaf_drv_tbl_tcam_key mac_key;
+       struct dsaf_drv_priv *priv =
+           (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev);
+       struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl;
+       u16 vlan_id;
+       u8 in_port_num;
+       struct dsaf_tbl_tcam_mcast_cfg mac_data;
+       struct dsaf_drv_tbl_tcam_key tmp_mac_key;
+       int mskid;
+       const u8 empty_msk[sizeof(mac_data.tbl_mcast_port_msk)] = {0};
+
+       if (!(void *)mac_entry) {
+               dev_err(dsaf_dev->dev,
+                       "hns_dsaf_del_mac_mc_port mac_entry is NULL\n");
+               return -EINVAL;
+       }
+
+       /*get key info*/
+       vlan_id = mac_entry->in_vlan_id;
+       in_port_num = mac_entry->in_port_num;
+
+       /*check mac addr */
+       if (MAC_IS_ALL_ZEROS(mac_entry->addr)) {
+               dev_err(dsaf_dev->dev,
+                       "del_port failed, addr %02x:%02x:%02x:%02x:%02x:%02x!\n",
+                       mac_entry->addr[0], mac_entry->addr[1],
+                       mac_entry->addr[2], mac_entry->addr[3],
+                       mac_entry->addr[4], mac_entry->addr[5]);
+               return -EINVAL;
+       }
+
+       /*config key */
+       hns_dsaf_set_mac_key(dsaf_dev, &mac_key, vlan_id, in_port_num,
+                            mac_entry->addr);
+
+       /*check is exist? */
+       entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key);
+       if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+               /*find none */
+               dev_err(dsaf_dev->dev,
+                       "find_soft_mac_entry failed, %s Mac key(%#x:%#x)\n",
+                       dsaf_dev->ae_dev.name,
+                       mac_key.high.val, mac_key.low.val);
+               return -EINVAL;
+       }
+
+       dev_dbg(dsaf_dev->dev,
+               "del_mac_mc_port, %s key(%#x:%#x) index%d\n",
+               dsaf_dev->ae_dev.name, mac_key.high.val,
+               mac_key.low.val, entry_index);
+
+       /*read entry*/
+       hns_dsaf_tcam_mc_get(
+               dsaf_dev, entry_index,
+               (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data);
+
+       /*del the port*/
+       if (mac_entry->port_num < DSAF_SERVICE_NW_NUM) {
+               mskid = mac_entry->port_num;
+       } else if (mac_entry->port_num >= DSAF_BASE_INNER_PORT_NUM) {
+               mskid = mac_entry->port_num -
+                       DSAF_BASE_INNER_PORT_NUM + DSAF_SERVICE_NW_NUM;
+       } else {
+               dev_err(dsaf_dev->dev,
+                       "%s,pnum(%d)error,key(%#x:%#x)\n",
+                       dsaf_dev->ae_dev.name, mac_entry->port_num,
+                       mac_key.high.val, mac_key.low.val);
+               return -EINVAL;
+       }
+       dsaf_set_bit(mac_data.tbl_mcast_port_msk[mskid / 32], mskid % 32, 0);
+
+       /*check non port, do del entry */
+       if (!memcmp(mac_data.tbl_mcast_port_msk, empty_msk,
+                   sizeof(mac_data.tbl_mcast_port_msk))) {
+               hns_dsaf_tcam_mc_invld(dsaf_dev, entry_index);
+
+               /* del soft entry */
+               soft_mac_entry += entry_index;
+               soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX;
+       } else { /* not zer, just del port, updata*/
+               hns_dsaf_tcam_mc_cfg(
+                       dsaf_dev, entry_index,
+                       (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data);
+       }
+
+       return 0;
+}
+
+/**
+ * hns_dsaf_get_mac_uc_entry - get mac uc entry
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @mac_entry: mac entry
+ */
+int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev,
+                             struct dsaf_drv_mac_single_dest_entry *mac_entry)
+{
+       u16 entry_index = DSAF_INVALID_ENTRY_IDX;
+       struct dsaf_drv_tbl_tcam_key mac_key;
+
+       struct dsaf_tbl_tcam_ucast_cfg mac_data;
+
+       /* check macaddr */
+       if (MAC_IS_ALL_ZEROS(mac_entry->addr) ||
+           MAC_IS_BROADCAST(mac_entry->addr)) {
+               dev_err(dsaf_dev->dev,
+                       "get_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x\n",
+                       mac_entry->addr[0], mac_entry->addr[1],
+                       mac_entry->addr[2], mac_entry->addr[3],
+                       mac_entry->addr[4], mac_entry->addr[5]);
+               return -EINVAL;
+       }
+
+       /*config key */
+       hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id,
+                            mac_entry->in_port_num, mac_entry->addr);
+
+       /*check exist? */
+       entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key);
+       if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+               /*find none, error */
+               dev_err(dsaf_dev->dev,
+                       "get_uc_entry failed, %s Mac key(%#x:%#x)\n",
+                       dsaf_dev->ae_dev.name,
+                       mac_key.high.val, mac_key.low.val);
+               return -EINVAL;
+       }
+       dev_dbg(dsaf_dev->dev,
+               "get_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n",
+               dsaf_dev->ae_dev.name, mac_key.high.val,
+               mac_key.low.val, entry_index);
+
+       /*read entry*/
+       hns_dsaf_tcam_uc_get(dsaf_dev, entry_index,
+                            (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data);
+       mac_entry->port_num = mac_data.tbl_ucast_out_port;
+
+       return 0;
+}
+
+/**
+ * hns_dsaf_get_mac_mc_entry - get mac mc entry
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @mac_entry: mac entry
+ */
+int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev,
+                             struct dsaf_drv_mac_multi_dest_entry *mac_entry)
+{
+       u16 entry_index = DSAF_INVALID_ENTRY_IDX;
+       struct dsaf_drv_tbl_tcam_key mac_key;
+
+       struct dsaf_tbl_tcam_mcast_cfg mac_data;
+
+       /*check mac addr */
+       if (MAC_IS_ALL_ZEROS(mac_entry->addr) ||
+           MAC_IS_BROADCAST(mac_entry->addr)) {
+               dev_err(dsaf_dev->dev,
+                       "get_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x\n",
+                       mac_entry->addr[0], mac_entry->addr[1],
+                       mac_entry->addr[2], mac_entry->addr[3],
+                       mac_entry->addr[4], mac_entry->addr[5]);
+               return -EINVAL;
+       }
+
+       /*config key */
+       hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id,
+                            mac_entry->in_port_num, mac_entry->addr);
+
+       /*check exist? */
+       entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key);
+       if (entry_index == DSAF_INVALID_ENTRY_IDX) {
+               /* find none, error */
+               dev_err(dsaf_dev->dev,
+                       "get_mac_uc_entry failed, %s Mac key(%#x:%#x)\n",
+                       dsaf_dev->ae_dev.name, mac_key.high.val,
+                       mac_key.low.val);
+               return -EINVAL;
+       }
+       dev_dbg(dsaf_dev->dev,
+               "get_mac_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n",
+               dsaf_dev->ae_dev.name, mac_key.high.val,
+               mac_key.low.val, entry_index);
+
+       /*read entry */
+       hns_dsaf_tcam_mc_get(dsaf_dev, entry_index,
+                            (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data);
+
+       mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F;
+       return 0;
+}
+
+/**
+ * hns_dsaf_get_mac_entry_by_index - get mac entry by tab index
+ * @dsaf_dev: dsa fabric device struct pointer
+ * @entry_index: tab entry index
+ * @mac_entry: mac entry
+ */
+int hns_dsaf_get_mac_entry_by_index(
+       struct dsaf_device *dsaf_dev,
+       u16 entry_index, struct dsaf_drv_mac_multi_dest_entry *mac_entry)
+{
+       struct dsaf_drv_tbl_tcam_key mac_key;
+
+       struct dsaf_tbl_tcam_mcast_cfg mac_data;
+       struct dsaf_tbl_tcam_ucast_cfg mac_uc_data;
+       char mac_addr[MAC_NUM_OCTETS_PER_ADDR] = {0};
+
+       if (entry_index >= DSAF_TCAM_SUM) {
+               /* find none, del error */
+               dev_err(dsaf_dev->dev, "get_uc_entry failed, %s\n",
+                       dsaf_dev->ae_dev.name);
+               return -EINVAL;
+       }
+
+       /* mc entry, do read opt */
+       hns_dsaf_tcam_mc_get(dsaf_dev, entry_index,
+                            (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data);
+
+       mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F;
+
+       /***get mac addr*/
+       mac_addr[0] = mac_key.high.bits.mac_0;
+       mac_addr[1] = mac_key.high.bits.mac_1;
+       mac_addr[2] = mac_key.high.bits.mac_2;
+       mac_addr[3] = mac_key.high.bits.mac_3;
+       mac_addr[4] = mac_key.low.bits.mac_4;
+       mac_addr[5] = mac_key.low.bits.mac_5;
+       /**is mc or uc*/
+       if (MAC_IS_MULTICAST((u8 *)mac_addr) ||
+           MAC_IS_L3_MULTICAST((u8 *)mac_addr)) {
+               /**mc donot do*/
+       } else {
+               /*is not mc, just uc... */
+               hns_dsaf_tcam_uc_get(dsaf_dev, entry_index,
+                                    (struct dsaf_tbl_tcam_data *)&mac_key,
+                                    &mac_uc_data);
+               mac_entry->port_mask[0] = (1 << mac_uc_data.tbl_ucast_out_port);
+       }
+
+       return 0;
+}
+
+static struct dsaf_device *hns_dsaf_alloc_dev(struct device *dev,
+                                             size_t sizeof_priv)
+{
+       struct dsaf_device *dsaf_dev;
+
+       dsaf_dev = devm_kzalloc(dev,
+                               sizeof(*dsaf_dev) + sizeof_priv, GFP_KERNEL);
+       if (unlikely(!dsaf_dev)) {
+               dsaf_dev = ERR_PTR(-ENOMEM);
+       } else {
+               dsaf_dev->dev = dev;
+               dev_set_drvdata(dev, dsaf_dev);
+       }
+
+       return dsaf_dev;
+}
+
+/**
+ * hns_dsaf_free_dev - free dev mem
+ * @dev: struct device pointer
+ */
+static void hns_dsaf_free_dev(struct dsaf_device *dsaf_dev)
+{
+       (void)dev_set_drvdata(dsaf_dev->dev, NULL);
+}
+
+/**
+ * dsaf_pfc_unit_cnt - set pfc unit count
+ * @dsaf_id: dsa fabric id
+ * @pport_rate:  value array
+ * @pdsaf_pfc_unit_cnt:  value array
+ */
+static void hns_dsaf_pfc_unit_cnt(struct dsaf_device *dsaf_dev, int  mac_id,
+                                 enum dsaf_port_rate_mode rate)
+{
+       u32 unit_cnt;
+
+       switch (rate) {
+       case DSAF_PORT_RATE_10000:
+               unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_XGE;
+               break;
+       case DSAF_PORT_RATE_1000:
+               unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000;
+               break;
+       case DSAF_PORT_RATE_2500:
+               unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000;
+               break;
+       default:
+               unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_XGE;
+       }
+
+       dsaf_set_dev_field(dsaf_dev,
+                          (DSAF_PFC_UNIT_CNT_0_REG + 0x4 * (u64)mac_id),
+                          DSAF_PFC_UNINT_CNT_M, DSAF_PFC_UNINT_CNT_S,
+                          unit_cnt);
+}
+
+/**
+ * dsaf_port_work_rate_cfg - fifo
+ * @dsaf_id: dsa fabric id
+ * @xge_ge_work_mode
+ */
+void hns_dsaf_port_work_rate_cfg(struct dsaf_device *dsaf_dev, int mac_id,
+                                enum dsaf_port_rate_mode rate_mode)
+{
+       u32 port_work_mode;
+
+       port_work_mode = dsaf_read_dev(
+               dsaf_dev, DSAF_XGE_GE_WORK_MODE_0_REG + 0x4 * (u64)mac_id);
+
+       if (rate_mode == DSAF_PORT_RATE_10000)
+               dsaf_set_bit(port_work_mode, DSAF_XGE_GE_WORK_MODE_S, 1);
+       else
+               dsaf_set_bit(port_work_mode, DSAF_XGE_GE_WORK_MODE_S, 0);
+
+       dsaf_write_dev(dsaf_dev,
+                      DSAF_XGE_GE_WORK_MODE_0_REG + 0x4 * (u64)mac_id,
+                      port_work_mode);
+
+       hns_dsaf_pfc_unit_cnt(dsaf_dev, mac_id, rate_mode);
+}
+
+/**
+ * hns_dsaf_fix_mac_mode - dsaf modify mac mode
+ * @mac_cb: mac contrl block
+ */
+void hns_dsaf_fix_mac_mode(struct hns_mac_cb *mac_cb)
+{
+       enum dsaf_port_rate_mode mode;
+       struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
+       int mac_id = mac_cb->mac_id;
+
+       if (mac_cb->mac_type != HNAE_PORT_SERVICE)
+               return;
+       if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII)
+               mode = DSAF_PORT_RATE_10000;
+       else
+               mode = DSAF_PORT_RATE_1000;
+
+       hns_dsaf_port_work_rate_cfg(dsaf_dev, mac_id, mode);
+}
+
+void hns_dsaf_update_stats(struct dsaf_device *dsaf_dev, u32 node_num)
+{
+       struct dsaf_hw_stats *hw_stats
+               = &dsaf_dev->hw_stats[node_num];
+
+       hw_stats->pad_drop += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_PAD_DISCARD_NUM_0_REG + 0x80 * (u64)node_num);
+       hw_stats->man_pkts += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_FINAL_IN_MAN_NUM_0_REG + 0x80 * (u64)node_num);
+       hw_stats->rx_pkts += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_FINAL_IN_PKT_NUM_0_REG + 0x80 * (u64)node_num);
+       hw_stats->rx_pkt_id += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_SBM_PID_NUM_0_REG + 0x80 * (u64)node_num);
+       hw_stats->rx_pause_frame += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG + 0x80 * (u64)node_num);
+       hw_stats->release_buf_num += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_SBM_RELS_NUM_0_REG + 0x80 * (u64)node_num);
+       hw_stats->sbm_drop += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_SBM_DROP_NUM_0_REG + 0x80 * (u64)node_num);
+       hw_stats->crc_false += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_CRC_FALSE_NUM_0_REG + 0x80 * (u64)node_num);
+       hw_stats->bp_drop += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_BP_DISCARD_NUM_0_REG + 0x80 * (u64)node_num);
+       hw_stats->rslt_drop += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_RSLT_DISCARD_NUM_0_REG + 0x80 * (u64)node_num);
+       hw_stats->local_addr_false += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG + 0x80 * (u64)node_num);
+
+       hw_stats->vlan_drop += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_SW_VLAN_TAG_DISC_0_REG + 0x80 * (u64)node_num);
+       hw_stats->stp_drop += dsaf_read_dev(dsaf_dev,
+               DSAF_INODE_IN_DATA_STP_DISC_0_REG + 0x80 * (u64)node_num);
+
+       hw_stats->tx_pkts += dsaf_read_dev(dsaf_dev,
+               DSAF_XOD_RCVPKT_CNT_0_REG + 0x90 * (u64)node_num);
+}
+
+/**
+ *hns_dsaf_get_regs - dump dsaf regs
+ *@dsaf_dev: dsaf device
+ *@data:data for value of regs
+ */
+void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data)
+{
+       u32 i = 0;
+       u32 j;
+       u32 *p = data;
+
+       /* dsaf common registers */
+       p[0] = dsaf_read_dev(ddev, DSAF_SRAM_INIT_OVER_0_REG);
+       p[1] = dsaf_read_dev(ddev, DSAF_CFG_0_REG);
+       p[2] = dsaf_read_dev(ddev, DSAF_ECC_ERR_INVERT_0_REG);
+       p[3] = dsaf_read_dev(ddev, DSAF_ABNORMAL_TIMEOUT_0_REG);
+       p[4] = dsaf_read_dev(ddev, DSAF_FSM_TIMEOUT_0_REG);
+       p[5] = dsaf_read_dev(ddev, DSAF_DSA_REG_CNT_CLR_CE_REG);
+       p[6] = dsaf_read_dev(ddev, DSAF_DSA_SBM_INF_FIFO_THRD_REG);
+       p[7] = dsaf_read_dev(ddev, DSAF_DSA_SRAM_1BIT_ECC_SEL_REG);
+       p[8] = dsaf_read_dev(ddev, DSAF_DSA_SRAM_1BIT_ECC_CNT_REG);
+
+       p[9] = dsaf_read_dev(ddev, DSAF_PFC_EN_0_REG + port * 4);
+       p[10] = dsaf_read_dev(ddev, DSAF_PFC_UNIT_CNT_0_REG + port * 4);
+       p[11] = dsaf_read_dev(ddev, DSAF_XGE_INT_MSK_0_REG + port * 4);
+       p[12] = dsaf_read_dev(ddev, DSAF_XGE_INT_SRC_0_REG + port * 4);
+       p[13] = dsaf_read_dev(ddev, DSAF_XGE_INT_STS_0_REG + port * 4);
+       p[14] = dsaf_read_dev(ddev, DSAF_XGE_INT_MSK_0_REG + port * 4);
+       p[15] = dsaf_read_dev(ddev, DSAF_PPE_INT_MSK_0_REG + port * 4);
+       p[16] = dsaf_read_dev(ddev, DSAF_ROCEE_INT_MSK_0_REG + port * 4);
+       p[17] = dsaf_read_dev(ddev, DSAF_XGE_INT_SRC_0_REG + port * 4);
+       p[18] = dsaf_read_dev(ddev, DSAF_PPE_INT_SRC_0_REG + port * 4);
+       p[19] =  dsaf_read_dev(ddev, DSAF_ROCEE_INT_SRC_0_REG + port * 4);
+       p[20] = dsaf_read_dev(ddev, DSAF_XGE_INT_STS_0_REG + port * 4);
+       p[21] = dsaf_read_dev(ddev, DSAF_PPE_INT_STS_0_REG + port * 4);
+       p[22] = dsaf_read_dev(ddev, DSAF_ROCEE_INT_STS_0_REG + port * 4);
+       p[23] = dsaf_read_dev(ddev, DSAF_PPE_QID_CFG_0_REG + port * 4);
+
+       for (i = 0; i < DSAF_SW_PORT_NUM; i++)
+               p[24 + i] = dsaf_read_dev(ddev,
+                               DSAF_SW_PORT_TYPE_0_REG + i * 4);
+
+       p[32] = dsaf_read_dev(ddev, DSAF_MIX_DEF_QID_0_REG + port * 4);
+
+       for (i = 0; i < DSAF_SW_PORT_NUM; i++)
+               p[33 + i] = dsaf_read_dev(ddev,
+                               DSAF_PORT_DEF_VLAN_0_REG + i * 4);
+
+       for (i = 0; i < DSAF_TOTAL_QUEUE_NUM; i++)
+               p[41 + i] = dsaf_read_dev(ddev,
+                               DSAF_VM_DEF_VLAN_0_REG + i * 4);
+
+       /* dsaf inode registers */
+       p[170] = dsaf_read_dev(ddev, DSAF_INODE_CUT_THROUGH_CFG_0_REG);
+
+       p[171] = dsaf_read_dev(ddev,
+                       DSAF_INODE_ECC_ERR_ADDR_0_REG + port * 0x80);
+
+       for (i = 0; i < DSAF_INODE_NUM / DSAF_COMM_CHN; i++) {
+               j = i * DSAF_COMM_CHN + port;
+               p[172 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_IN_PORT_NUM_0_REG + j * 0x80);
+               p[175 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_PRI_TC_CFG_0_REG + j * 0x80);
+               p[178 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_BP_STATUS_0_REG + j * 0x80);
+               p[181 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_PAD_DISCARD_NUM_0_REG + j * 0x80);
+               p[184 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_FINAL_IN_MAN_NUM_0_REG + j * 0x80);
+               p[187 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_FINAL_IN_PKT_NUM_0_REG + j * 0x80);
+               p[190 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_SBM_PID_NUM_0_REG + j * 0x80);
+               p[193 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG + j * 0x80);
+               p[196 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_SBM_RELS_NUM_0_REG + j * 0x80);
+               p[199 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_SBM_DROP_NUM_0_REG + j * 0x80);
+               p[202 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_CRC_FALSE_NUM_0_REG + j * 0x80);
+               p[205 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_BP_DISCARD_NUM_0_REG + j * 0x80);
+               p[208 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_RSLT_DISCARD_NUM_0_REG + j * 0x80);
+               p[211 + i] = dsaf_read_dev(ddev,
+                       DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG + j * 0x80);
+               p[214 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_VOQ_OVER_NUM_0_REG + j * 0x80);
+               p[217 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_BD_SAVE_STATUS_0_REG + j * 4);
+               p[220 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_BD_ORDER_STATUS_0_REG + j * 4);
+               p[223 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_SW_VLAN_TAG_DISC_0_REG + j * 4);
+               p[224 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_IN_DATA_STP_DISC_0_REG + j * 4);
+       }
+
+       p[227] = dsaf_read_dev(ddev, DSAF_INODE_GE_FC_EN_0_REG + port * 4);
+
+       for (i = 0; i < DSAF_INODE_NUM / DSAF_COMM_CHN; i++) {
+               j = i * DSAF_COMM_CHN + port;
+               p[228 + i] = dsaf_read_dev(ddev,
+                               DSAF_INODE_VC0_IN_PKT_NUM_0_REG + j * 4);
+       }
+
+       p[231] = dsaf_read_dev(ddev,
+               DSAF_INODE_VC1_IN_PKT_NUM_0_REG + port * 4);
+
+       /* dsaf inode registers */
+       for (i = 0; i < DSAF_SBM_NUM / DSAF_COMM_CHN; i++) {
+               j = i * DSAF_COMM_CHN + port;
+               p[232 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_CFG_REG_0_REG + j * 0x80);
+               p[235 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + j * 0x80);
+               p[238 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_BP_CFG_1_REG_0_REG + j * 0x80);
+               p[241 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + j * 0x80);
+               p[244 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_FREE_CNT_0_0_REG + j * 0x80);
+               p[245 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_FREE_CNT_1_0_REG + j * 0x80);
+               p[248 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_BP_CNT_0_0_REG + j * 0x80);
+               p[251 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_BP_CNT_1_0_REG + j * 0x80);
+               p[254 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_BP_CNT_2_0_REG + j * 0x80);
+               p[257 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_BP_CNT_3_0_REG + j * 0x80);
+               p[260 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_INER_ST_0_REG + j * 0x80);
+               p[263 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_MIB_REQ_FAILED_TC_0_REG + j * 0x80);
+               p[266 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_INPORT_CNT_0_REG + j * 0x80);
+               p[269 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_DROP_CNT_0_REG + j * 0x80);
+               p[272 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_INF_OUTPORT_CNT_0_REG + j * 0x80);
+               p[275 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_INPORT_TC0_CNT_0_REG + j * 0x80);
+               p[278 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_INPORT_TC1_CNT_0_REG + j * 0x80);
+               p[281 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_INPORT_TC2_CNT_0_REG + j * 0x80);
+               p[284 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_INPORT_TC3_CNT_0_REG + j * 0x80);
+               p[287 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_INPORT_TC4_CNT_0_REG + j * 0x80);
+               p[290 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_INPORT_TC5_CNT_0_REG + j * 0x80);
+               p[293 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_INPORT_TC6_CNT_0_REG + j * 0x80);
+               p[296 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_INPORT_TC7_CNT_0_REG + j * 0x80);
+               p[299 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_REQ_CNT_0_REG + j * 0x80);
+               p[302 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_LNK_RELS_CNT_0_REG + j * 0x80);
+               p[305 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_BP_CFG_3_REG_0_REG + j * 0x80);
+               p[308 + i] = dsaf_read_dev(ddev,
+                               DSAF_SBM_BP_CFG_4_REG_0_REG + j * 0x80);
+       }
+
+       /* dsaf onode registers */
+       for (i = 0; i < DSAF_XOD_NUM; i++) {
+               p[311 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_ETS_TSA_TC0_TC3_CFG_0_REG + j * 0x90);
+               p[319 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_ETS_TSA_TC4_TC7_CFG_0_REG + j * 0x90);
+               p[327 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_ETS_BW_TC0_TC3_CFG_0_REG + j * 0x90);
+               p[335 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_ETS_BW_TC4_TC7_CFG_0_REG + j * 0x90);
+               p[343 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_ETS_BW_OFFSET_CFG_0_REG + j * 0x90);
+               p[351 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_ETS_TOKEN_CFG_0_REG + j * 0x90);
+       }
+
+       p[359] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_0_0_REG + port * 0x90);
+       p[360] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_1_0_REG + port * 0x90);
+       p[361] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_2_0_REG + port * 0x90);
+
+       for (i = 0; i < DSAF_XOD_BIG_NUM / DSAF_COMM_CHN; i++) {
+               j = i * DSAF_COMM_CHN + port;
+               p[362 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_GNT_L_0_REG + j * 0x90);
+               p[365 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_GNT_H_0_REG + j * 0x90);
+               p[368 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_CONNECT_STATE_0_REG + j * 0x90);
+               p[371 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_RCVPKT_CNT_0_REG + j * 0x90);
+               p[374 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_RCVTC0_CNT_0_REG + j * 0x90);
+               p[377 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_RCVTC1_CNT_0_REG + j * 0x90);
+               p[380 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_RCVTC2_CNT_0_REG + j * 0x90);
+               p[383 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_RCVTC3_CNT_0_REG + j * 0x90);
+               p[386 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_RCVVC0_CNT_0_REG + j * 0x90);
+               p[389 + i] = dsaf_read_dev(ddev,
+                               DSAF_XOD_RCVVC1_CNT_0_REG + j * 0x90);
+       }
+
+       p[392] = dsaf_read_dev(ddev,
+               DSAF_XOD_XGE_RCVIN0_CNT_0_REG + port * 0x90);
+       p[393] = dsaf_read_dev(ddev,
+               DSAF_XOD_XGE_RCVIN1_CNT_0_REG + port * 0x90);
+       p[394] = dsaf_read_dev(ddev,
+               DSAF_XOD_XGE_RCVIN2_CNT_0_REG + port * 0x90);
+       p[395] = dsaf_read_dev(ddev,
+               DSAF_XOD_XGE_RCVIN3_CNT_0_REG + port * 0x90);
+       p[396] = dsaf_read_dev(ddev,
+               DSAF_XOD_XGE_RCVIN4_CNT_0_REG + port * 0x90);
+       p[397] = dsaf_read_dev(ddev,
+               DSAF_XOD_XGE_RCVIN5_CNT_0_REG + port * 0x90);
+       p[398] = dsaf_read_dev(ddev,
+               DSAF_XOD_XGE_RCVIN6_CNT_0_REG + port * 0x90);
+       p[399] = dsaf_read_dev(ddev,
+               DSAF_XOD_XGE_RCVIN7_CNT_0_REG + port * 0x90);
+       p[400] = dsaf_read_dev(ddev,
+               DSAF_XOD_PPE_RCVIN0_CNT_0_REG + port * 0x90);
+       p[401] = dsaf_read_dev(ddev,
+               DSAF_XOD_PPE_RCVIN1_CNT_0_REG + port * 0x90);
+       p[402] = dsaf_read_dev(ddev,
+               DSAF_XOD_ROCEE_RCVIN0_CNT_0_REG + port * 0x90);
+       p[403] = dsaf_read_dev(ddev,
+               DSAF_XOD_ROCEE_RCVIN1_CNT_0_REG + port * 0x90);
+       p[404] = dsaf_read_dev(ddev,
+               DSAF_XOD_FIFO_STATUS_0_REG + port * 0x90);
+
+       /* dsaf voq registers */
+       for (i = 0; i < DSAF_VOQ_NUM / DSAF_COMM_CHN; i++) {
+               j = (i * DSAF_COMM_CHN + port) * 0x90;
+               p[405 + i] = dsaf_read_dev(ddev,
+                       DSAF_VOQ_ECC_INVERT_EN_0_REG + j);
+               p[408 + i] = dsaf_read_dev(ddev,
+                       DSAF_VOQ_SRAM_PKT_NUM_0_REG + j);
+               p[411 + i] = dsaf_read_dev(ddev, DSAF_VOQ_IN_PKT_NUM_0_REG + j);
+               p[414 + i] = dsaf_read_dev(ddev,
+                       DSAF_VOQ_OUT_PKT_NUM_0_REG + j);
+               p[417 + i] = dsaf_read_dev(ddev,
+                       DSAF_VOQ_ECC_ERR_ADDR_0_REG + j);
+               p[420 + i] = dsaf_read_dev(ddev, DSAF_VOQ_BP_STATUS_0_REG + j);
+               p[423 + i] = dsaf_read_dev(ddev, DSAF_VOQ_SPUP_IDLE_0_REG + j);
+               p[426 + i] = dsaf_read_dev(ddev,
+                       DSAF_VOQ_XGE_XOD_REQ_0_0_REG + j);
+               p[429 + i] = dsaf_read_dev(ddev,
+                       DSAF_VOQ_XGE_XOD_REQ_1_0_REG + j);
+               p[432 + i] = dsaf_read_dev(ddev,
+                       DSAF_VOQ_PPE_XOD_REQ_0_REG + j);
+               p[435 + i] = dsaf_read_dev(ddev,
+                       DSAF_VOQ_ROCEE_XOD_REQ_0_REG + j);
+               p[438 + i] = dsaf_read_dev(ddev,
+                       DSAF_VOQ_BP_ALL_THRD_0_REG + j);
+       }
+
+       /* dsaf tbl registers */
+       p[441] = dsaf_read_dev(ddev, DSAF_TBL_CTRL_0_REG);
+       p[442] = dsaf_read_dev(ddev, DSAF_TBL_INT_MSK_0_REG);
+       p[443] = dsaf_read_dev(ddev, DSAF_TBL_INT_SRC_0_REG);
+       p[444] = dsaf_read_dev(ddev, DSAF_TBL_INT_STS_0_REG);
+       p[445] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_ADDR_0_REG);
+       p[446] = dsaf_read_dev(ddev, DSAF_TBL_LINE_ADDR_0_REG);
+       p[447] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_HIGH_0_REG);
+       p[448] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_LOW_0_REG);
+       p[449] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG);
+       p[450] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG);
+       p[451] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG);
+       p[452] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG);
+       p[453] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG);
+       p[454] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_UCAST_CFG_0_REG);
+       p[455] = dsaf_read_dev(ddev, DSAF_TBL_LIN_CFG_0_REG);
+       p[456] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG);
+       p[457] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_LOW_0_REG);
+       p[458] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA4_0_REG);
+       p[459] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA3_0_REG);
+       p[460] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA2_0_REG);
+       p[461] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA1_0_REG);
+       p[462] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA0_0_REG);
+       p[463] = dsaf_read_dev(ddev, DSAF_TBL_LIN_RDATA_0_REG);
+
+       for (i = 0; i < DSAF_SW_PORT_NUM; i++) {
+               j = i * 0x8;
+               p[464 + 2 * i] = dsaf_read_dev(ddev,
+                       DSAF_TBL_DA0_MIS_INFO1_0_REG + j);
+               p[465 + 2 * i] = dsaf_read_dev(ddev,
+                       DSAF_TBL_DA0_MIS_INFO0_0_REG + j);
+       }
+
+       p[480] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO2_0_REG);
+       p[481] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO1_0_REG);
+       p[482] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO0_0_REG);
+       p[483] = dsaf_read_dev(ddev, DSAF_TBL_PUL_0_REG);
+       p[484] = dsaf_read_dev(ddev, DSAF_TBL_OLD_RSLT_0_REG);
+       p[485] = dsaf_read_dev(ddev, DSAF_TBL_OLD_SCAN_VAL_0_REG);
+       p[486] = dsaf_read_dev(ddev, DSAF_TBL_DFX_CTRL_0_REG);
+       p[487] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_0_REG);
+       p[488] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_2_0_REG);
+       p[489] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_I_0_REG);
+       p[490] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_O_0_REG);
+       p[491] = dsaf_read_dev(ddev, DSAF_TBL_UCAST_BCAST_MIS_INFO_0_0_REG);
+
+       /* dsaf other registers */
+       p[492] = dsaf_read_dev(ddev, DSAF_INODE_FIFO_WL_0_REG + port * 0x4);
+       p[493] = dsaf_read_dev(ddev, DSAF_ONODE_FIFO_WL_0_REG + port * 0x4);
+       p[494] = dsaf_read_dev(ddev, DSAF_XGE_GE_WORK_MODE_0_REG + port * 0x4);
+       p[495] = dsaf_read_dev(ddev,
+               DSAF_XGE_APP_RX_LINK_UP_0_REG + port * 0x4);
+       p[496] = dsaf_read_dev(ddev, DSAF_NETPORT_CTRL_SIG_0_REG + port * 0x4);
+       p[497] = dsaf_read_dev(ddev, DSAF_XGE_CTRL_SIG_CFG_0_REG + port * 0x4);
+
+       /* mark end of dsaf regs */
+       for (i = 498; i < 504; i++)
+               p[i] = 0xdddddddd;
+}
+
+static char *hns_dsaf_get_node_stats_strings(char *data, int node)
+{
+       char *buff = data;
+
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_pad_drop_pkts", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_manage_pkts", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pkts", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pkt_id", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pause_frame", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_release_buf_num", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_sbm_drop_pkts", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_crc_false_pkts", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_bp_drop_pkts", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_lookup_rslt_drop_pkts", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_local_rslt_fail_pkts", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_vlan_drop_pkts", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "innod%d_stp_drop_pkts", node);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "onnod%d_tx_pkts", node);
+       buff = buff + ETH_GSTRING_LEN;
+
+       return buff;
+}
+
+static u64 *hns_dsaf_get_node_stats(struct dsaf_device *ddev, u64 *data,
+                                   int node_num)
+{
+       u64 *p = data;
+       struct dsaf_hw_stats *hw_stats = &ddev->hw_stats[node_num];
+
+       p[0] = hw_stats->pad_drop;
+       p[1] = hw_stats->man_pkts;
+       p[2] = hw_stats->rx_pkts;
+       p[3] = hw_stats->rx_pkt_id;
+       p[4] = hw_stats->rx_pause_frame;
+       p[5] = hw_stats->release_buf_num;
+       p[6] = hw_stats->sbm_drop;
+       p[7] = hw_stats->crc_false;
+       p[8] = hw_stats->bp_drop;
+       p[9] = hw_stats->rslt_drop;
+       p[10] = hw_stats->local_addr_false;
+       p[11] = hw_stats->vlan_drop;
+       p[12] = hw_stats->stp_drop;
+       p[13] = hw_stats->tx_pkts;
+
+       return &p[14];
+}
+
+/**
+ *hns_dsaf_get_stats - get dsaf statistic
+ *@ddev: dsaf device
+ *@data:statistic value
+ *@port: port num
+ */
+void hns_dsaf_get_stats(struct dsaf_device *ddev, u64 *data, int port)
+{
+       u64 *p = data;
+       int node_num = port;
+
+       /* for ge/xge node info */
+       p = hns_dsaf_get_node_stats(ddev, p, node_num);
+
+       /* for ppe node info */
+       node_num = port + DSAF_PPE_INODE_BASE;
+       (void)hns_dsaf_get_node_stats(ddev, p, node_num);
+}
+
+/**
+ *hns_dsaf_get_sset_count - get dsaf string set count
+ *@stringset: type of values in data
+ *return dsaf string name count
+ */
+int hns_dsaf_get_sset_count(int stringset)
+{
+       if (stringset == ETH_SS_STATS)
+               return DSAF_STATIC_NUM;
+
+       return 0;
+}
+
+/**
+ *hns_dsaf_get_strings - get dsaf string set
+ *@stringset:srting set index
+ *@data:strings name value
+ *@port:port index
+ */
+void hns_dsaf_get_strings(int stringset, u8 *data, int port)
+{
+       char *buff = (char *)data;
+       int node = port;
+
+       if (stringset != ETH_SS_STATS)
+               return;
+
+       /* for ge/xge node info */
+       buff = hns_dsaf_get_node_stats_strings(buff, node);
+
+       /* for ppe node info */
+       node = port + DSAF_PPE_INODE_BASE;
+       (void)hns_dsaf_get_node_stats_strings(buff, node);
+}
+
+/**
+ *hns_dsaf_get_sset_count - get dsaf regs count
+ *return dsaf regs count
+ */
+int hns_dsaf_get_regs_count(void)
+{
+       return DSAF_DUMP_REGS_NUM;
+}
+
+/**
+ * dsaf_probe - probo dsaf dev
+ * @pdev: dasf platform device
+ * retuen 0 - success , negative --fail
+ */
+static int hns_dsaf_probe(struct platform_device *pdev)
+{
+       struct dsaf_device *dsaf_dev;
+       int ret;
+
+       dsaf_dev = hns_dsaf_alloc_dev(&pdev->dev, sizeof(struct dsaf_drv_priv));
+       if (IS_ERR(dsaf_dev)) {
+               ret = PTR_ERR(dsaf_dev);
+               dev_err(&pdev->dev,
+                       "dsaf_probe dsaf_alloc_dev failed, ret = %#x!\n", ret);
+               return ret;
+       }
+
+       ret = hns_dsaf_get_cfg(dsaf_dev);
+       if (ret)
+               goto free_dev;
+
+       ret = hns_dsaf_init(dsaf_dev);
+       if (ret)
+               goto free_cfg;
+
+       ret = hns_mac_init(dsaf_dev);
+       if (ret)
+               goto uninit_dsaf;
+
+       ret = hns_ppe_init(dsaf_dev);
+       if (ret)
+               goto uninit_mac;
+
+       ret = hns_dsaf_ae_init(dsaf_dev);
+       if (ret)
+               goto uninit_ppe;
+
+       return 0;
+
+uninit_ppe:
+       hns_ppe_uninit(dsaf_dev);
+
+uninit_mac:
+       hns_mac_uninit(dsaf_dev);
+
+uninit_dsaf:
+       hns_dsaf_free(dsaf_dev);
+
+free_cfg:
+       hns_dsaf_free_cfg(dsaf_dev);
+
+free_dev:
+       hns_dsaf_free_dev(dsaf_dev);
+
+       return ret;
+}
+
+/**
+ * dsaf_remove - remove dsaf dev
+ * @pdev: dasf platform device
+ */
+static int hns_dsaf_remove(struct platform_device *pdev)
+{
+       struct dsaf_device *dsaf_dev = dev_get_drvdata(&pdev->dev);
+
+       hns_dsaf_ae_uninit(dsaf_dev);
+
+       hns_ppe_uninit(dsaf_dev);
+
+       hns_mac_uninit(dsaf_dev);
+
+       hns_dsaf_free(dsaf_dev);
+
+       hns_dsaf_free_cfg(dsaf_dev);
+
+       hns_dsaf_free_dev(dsaf_dev);
+
+       return 0;
+}
+
+static const struct of_device_id g_dsaf_match[] = {
+       {.compatible = "hisilicon,hns-dsaf-v1"},
+       {.compatible = "hisilicon,hns-dsaf-v2"},
+       {}
+};
+
+static struct platform_driver g_dsaf_driver = {
+       .probe = hns_dsaf_probe,
+       .remove = hns_dsaf_remove,
+       .driver = {
+               .name = DSAF_DRV_NAME,
+               .of_match_table = g_dsaf_match,
+       },
+};
+
+module_platform_driver(g_dsaf_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
+MODULE_DESCRIPTION("HNS DSAF driver");
+MODULE_VERSION(DSAF_MOD_VERSION);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
new file mode 100644 (file)
index 0000000..b2b9348
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 __HNS_DSAF_MAIN_H
+#define __HNS_DSAF_MAIN_H
+#include "hnae.h"
+
+#include "hns_dsaf_reg.h"
+#include "hns_dsaf_mac.h"
+
+struct hns_mac_cb;
+
+#define DSAF_DRV_NAME "hns_dsaf"
+#define DSAF_MOD_VERSION "v1.0"
+
+#define ENABLE         (0x1)
+#define DISABLE                (0x0)
+
+#define HNS_DSAF_DEBUG_NW_REG_OFFSET (0x100000)
+
+#define DSAF_BASE_INNER_PORT_NUM (127)  /* mac tbl qid*/
+
+#define DSAF_MAX_CHIP_NUM (2)  /*max 2 chips */
+
+#define DSAF_DEFAUTL_QUEUE_NUM_PER_PPE (22)
+
+#define HNS_DSAF_MAX_DESC_CNT (1024)
+#define HNS_DSAF_MIN_DESC_CNT (16)
+
+#define DSAF_INVALID_ENTRY_IDX (0xffff)
+
+#define DSAF_CFG_READ_CNT   (30)
+#define DSAF_SRAM_INIT_FINISH_FLAG (0xff)
+
+#define MAC_NUM_OCTETS_PER_ADDR 6
+
+#define DSAF_DUMP_REGS_NUM 504
+#define DSAF_STATIC_NUM 28
+
+#define DSAF_STATS_READ(p, offset) (*((u64 *)((u8 *)(p) + (offset))))
+
+enum hal_dsaf_mode {
+       HRD_DSAF_NO_DSAF_MODE   = 0x0,
+       HRD_DSAF_MODE           = 0x1,
+};
+
+enum hal_dsaf_tc_mode {
+       HRD_DSAF_4TC_MODE               = 0X0,
+       HRD_DSAF_8TC_MODE               = 0X1,
+};
+
+struct dsaf_vm_def_vlan {
+       u32 vm_def_vlan_id;
+       u32 vm_def_vlan_cfi;
+       u32 vm_def_vlan_pri;
+};
+
+struct dsaf_tbl_tcam_data {
+       u32 tbl_tcam_data_high;
+       u32 tbl_tcam_data_low;
+};
+
+#define DSAF_PORT_MSK_NUM \
+       ((DSAF_TOTAL_QUEUE_NUM + DSAF_SERVICE_NW_NUM - 1) / 32 + 1)
+struct dsaf_tbl_tcam_mcast_cfg {
+       u8 tbl_mcast_old_en;
+       u8 tbl_mcast_item_vld;
+       u32 tbl_mcast_port_msk[DSAF_PORT_MSK_NUM];
+};
+
+struct dsaf_tbl_tcam_ucast_cfg {
+       u32 tbl_ucast_old_en;
+       u32 tbl_ucast_item_vld;
+       u32 tbl_ucast_mac_discard;
+       u32 tbl_ucast_dvc;
+       u32 tbl_ucast_out_port;
+};
+
+struct dsaf_tbl_line_cfg {
+       u32 tbl_line_mac_discard;
+       u32 tbl_line_dvc;
+       u32 tbl_line_out_port;
+};
+
+enum dsaf_port_rate_mode {
+       DSAF_PORT_RATE_1000 = 0,
+       DSAF_PORT_RATE_2500,
+       DSAF_PORT_RATE_10000
+};
+
+enum dsaf_stp_port_type {
+       DSAF_STP_PORT_TYPE_DISCARD = 0,
+       DSAF_STP_PORT_TYPE_BLOCK = 1,
+       DSAF_STP_PORT_TYPE_LISTEN = 2,
+       DSAF_STP_PORT_TYPE_LEARN = 3,
+       DSAF_STP_PORT_TYPE_FORWARD = 4
+};
+
+enum dsaf_sw_port_type {
+       DSAF_SW_PORT_TYPE_NON_VLAN = 0,
+       DSAF_SW_PORT_TYPE_ACCESS = 1,
+       DSAF_SW_PORT_TYPE_TRUNK = 2,
+};
+
+#define DSAF_SUB_BASE_SIZE                        (0x10000)
+
+/* dsaf mode define */
+enum dsaf_mode {
+       DSAF_MODE_INVALID = 0,  /**< Invalid dsaf mode */
+       DSAF_MODE_ENABLE_FIX,   /**< en DSAF-mode, fixed to queue*/
+       DSAF_MODE_ENABLE_0VM,   /**< en DSAF-mode, support 0 VM */
+       DSAF_MODE_ENABLE_8VM,   /**< en DSAF-mode, support 8 VM */
+       DSAF_MODE_ENABLE_16VM,  /**< en DSAF-mode, support 16 VM */
+       DSAF_MODE_ENABLE_32VM,  /**< en DSAF-mode, support 32 VM */
+       DSAF_MODE_ENABLE_128VM, /**< en DSAF-mode, support 128 VM */
+       DSAF_MODE_ENABLE,               /**< before is enable DSAF mode*/
+       DSAF_MODE_DISABLE_FIX,  /**< non-dasf, fixed to queue*/
+       DSAF_MODE_DISABLE_2PORT_8VM,    /**< non-dasf, 2port 8VM */
+       DSAF_MODE_DISABLE_2PORT_16VM,   /**< non-dasf, 2port 16VM */
+       DSAF_MODE_DISABLE_2PORT_64VM,   /**< non-dasf, 2port 64VM */
+       DSAF_MODE_DISABLE_6PORT_0VM,    /**< non-dasf, 6port 0VM */
+       DSAF_MODE_DISABLE_6PORT_2VM,    /**< non-dasf, 6port 2VM */
+       DSAF_MODE_DISABLE_6PORT_4VM,    /**< non-dasf, 6port 4VM */
+       DSAF_MODE_DISABLE_6PORT_16VM,   /**< non-dasf, 6port 16VM */
+       DSAF_MODE_MAX           /**< the last one, use as the num */
+};
+
+#define DSAF_DEST_PORT_NUM 256 /* DSAF max port num */
+#define DSAF_WORD_BIT_CNT 32  /* the num bit of word */
+
+/*mac entry, mc or uc entry*/
+struct dsaf_drv_mac_single_dest_entry {
+       /* mac addr, match the entry*/
+       u8 addr[MAC_NUM_OCTETS_PER_ADDR];
+       u16 in_vlan_id; /* value of VlanId */
+
+       /* the vld input port num, dsaf-mode fix 0, */
+       /*      non-dasf is the entry whitch port vld*/
+       u8 in_port_num;
+
+       u8 port_num; /*output port num*/
+       u8 rsv[6];
+};
+
+/*only mc entry*/
+struct dsaf_drv_mac_multi_dest_entry {
+       /* mac addr, match the entry*/
+       u8 addr[MAC_NUM_OCTETS_PER_ADDR];
+       u16 in_vlan_id;
+       /* this mac addr output port,*/
+       /*      bit0-bit5 means Port0-Port5(1bit is vld)**/
+       u32 port_mask[DSAF_DEST_PORT_NUM / DSAF_WORD_BIT_CNT];
+
+       /* the vld input port num, dsaf-mode fix 0,*/
+       /*      non-dasf is the entry whitch port vld*/
+       u8 in_port_num;
+       u8 rsv[7];
+};
+
+struct dsaf_hw_stats {
+       u64 pad_drop;
+       u64 man_pkts;
+       u64 rx_pkts;
+       u64 rx_pkt_id;
+       u64 rx_pause_frame;
+       u64 release_buf_num;
+       u64 sbm_drop;
+       u64 crc_false;
+       u64 bp_drop;
+       u64 rslt_drop;
+       u64 local_addr_false;
+       u64 vlan_drop;
+       u64 stp_drop;
+       u64 tx_pkts;
+};
+
+struct hnae_vf_cb {
+       u8 port_index;
+       struct hns_mac_cb *mac_cb;
+       struct dsaf_device *dsaf_dev;
+       struct hnae_handle  ae_handle; /* must be the last number */
+};
+
+struct dsaf_int_xge_src {
+       u32    xid_xge_ecc_err_int_src;
+       u32    xid_xge_fsm_timout_int_src;
+       u32    sbm_xge_lnk_fsm_timout_int_src;
+       u32    sbm_xge_lnk_ecc_2bit_int_src;
+       u32    sbm_xge_mib_req_failed_int_src;
+       u32    sbm_xge_mib_req_fsm_timout_int_src;
+       u32    sbm_xge_mib_rels_fsm_timout_int_src;
+       u32    sbm_xge_sram_ecc_2bit_int_src;
+       u32    sbm_xge_mib_buf_sum_err_int_src;
+       u32    sbm_xge_mib_req_extra_int_src;
+       u32    sbm_xge_mib_rels_extra_int_src;
+       u32    voq_xge_start_to_over_0_int_src;
+       u32    voq_xge_start_to_over_1_int_src;
+       u32    voq_xge_ecc_err_int_src;
+};
+
+struct dsaf_int_ppe_src {
+       u32    xid_ppe_fsm_timout_int_src;
+       u32    sbm_ppe_lnk_fsm_timout_int_src;
+       u32    sbm_ppe_lnk_ecc_2bit_int_src;
+       u32    sbm_ppe_mib_req_failed_int_src;
+       u32    sbm_ppe_mib_req_fsm_timout_int_src;
+       u32    sbm_ppe_mib_rels_fsm_timout_int_src;
+       u32    sbm_ppe_sram_ecc_2bit_int_src;
+       u32    sbm_ppe_mib_buf_sum_err_int_src;
+       u32    sbm_ppe_mib_req_extra_int_src;
+       u32    sbm_ppe_mib_rels_extra_int_src;
+       u32    voq_ppe_start_to_over_0_int_src;
+       u32    voq_ppe_ecc_err_int_src;
+       u32    xod_ppe_fifo_rd_empty_int_src;
+       u32    xod_ppe_fifo_wr_full_int_src;
+};
+
+struct dsaf_int_rocee_src {
+       u32    xid_rocee_fsm_timout_int_src;
+       u32    sbm_rocee_lnk_fsm_timout_int_src;
+       u32    sbm_rocee_lnk_ecc_2bit_int_src;
+       u32    sbm_rocee_mib_req_failed_int_src;
+       u32    sbm_rocee_mib_req_fsm_timout_int_src;
+       u32    sbm_rocee_mib_rels_fsm_timout_int_src;
+       u32    sbm_rocee_sram_ecc_2bit_int_src;
+       u32    sbm_rocee_mib_buf_sum_err_int_src;
+       u32    sbm_rocee_mib_req_extra_int_src;
+       u32    sbm_rocee_mib_rels_extra_int_src;
+       u32    voq_rocee_start_to_over_0_int_src;
+       u32    voq_rocee_ecc_err_int_src;
+};
+
+struct dsaf_int_tbl_src {
+       u32    tbl_da0_mis_src;
+       u32    tbl_da1_mis_src;
+       u32    tbl_da2_mis_src;
+       u32    tbl_da3_mis_src;
+       u32    tbl_da4_mis_src;
+       u32    tbl_da5_mis_src;
+       u32    tbl_da6_mis_src;
+       u32    tbl_da7_mis_src;
+       u32    tbl_sa_mis_src;
+       u32    tbl_old_sech_end_src;
+       u32    lram_ecc_err1_src;
+       u32    lram_ecc_err2_src;
+       u32    tram_ecc_err1_src;
+       u32    tram_ecc_err2_src;
+       u32    tbl_ucast_bcast_xge0_src;
+       u32    tbl_ucast_bcast_xge1_src;
+       u32    tbl_ucast_bcast_xge2_src;
+       u32    tbl_ucast_bcast_xge3_src;
+       u32    tbl_ucast_bcast_xge4_src;
+       u32    tbl_ucast_bcast_xge5_src;
+       u32    tbl_ucast_bcast_ppe_src;
+       u32    tbl_ucast_bcast_rocee_src;
+};
+
+struct dsaf_int_stat {
+       struct dsaf_int_xge_src dsaf_int_xge_stat[DSAF_COMM_CHN];
+       struct dsaf_int_ppe_src dsaf_int_ppe_stat[DSAF_COMM_CHN];
+       struct dsaf_int_rocee_src dsaf_int_rocee_stat[DSAF_COMM_CHN];
+       struct dsaf_int_tbl_src dsaf_int_tbl_stat[1];
+
+};
+
+/* Dsaf device struct define ,and mac ->  dsaf */
+struct dsaf_device {
+       struct device *dev;
+       struct hnae_ae_dev ae_dev;
+
+       void *priv;
+
+       int virq[DSAF_IRQ_NUM];
+
+       u8 __iomem *sc_base;
+       u8 __iomem *sds_base;
+       u8 __iomem *ppe_base;
+       u8 __iomem *io_base;
+       u8 __iomem *cpld_base;
+
+       u32 desc_num; /*  desc num per queue*/
+       u32 buf_size; /*  ring buffer size */
+       int buf_size_type; /* ring buffer size-type */
+       enum dsaf_mode dsaf_mode;        /* dsaf mode  */
+       enum hal_dsaf_mode dsaf_en;
+       enum hal_dsaf_tc_mode dsaf_tc_mode;
+       u32 dsaf_ver;
+
+       struct ppe_common_cb *ppe_common[DSAF_COMM_DEV_NUM];
+       struct rcb_common_cb *rcb_common[DSAF_COMM_DEV_NUM];
+       struct hns_mac_cb *mac_cb;
+
+       struct dsaf_hw_stats hw_stats[DSAF_NODE_NUM];
+       struct dsaf_int_stat int_stat;
+};
+
+static inline void *hns_dsaf_dev_priv(const struct dsaf_device *dsaf_dev)
+{
+       return (void *)((u8 *)dsaf_dev + sizeof(*dsaf_dev));
+}
+
+struct dsaf_drv_tbl_tcam_key {
+       union {
+               struct {
+                       u8 mac_3;
+                       u8 mac_2;
+                       u8 mac_1;
+                       u8 mac_0;
+               } bits;
+
+               u32 val;
+       } high;
+       union {
+               struct {
+                       u32 port:4; /* port id, */
+                       /* dsaf-mode fixed 0, non-dsaf-mode port id*/
+                       u32 vlan:12; /* vlan id */
+                       u32 mac_5:8;
+                       u32 mac_4:8;
+               } bits;
+
+               u32 val;
+       } low;
+};
+
+struct dsaf_drv_soft_mac_tbl {
+       struct dsaf_drv_tbl_tcam_key tcam_key;
+       u16 index; /*the entry's index in tcam tab*/
+};
+
+struct dsaf_drv_priv {
+       /* soft tab Mac key, for hardware tab*/
+       struct dsaf_drv_soft_mac_tbl *soft_mac_tbl;
+};
+
+static inline void hns_dsaf_tbl_tcam_addr_cfg(struct dsaf_device *dsaf_dev,
+                                             u32 tab_tcam_addr)
+{
+       dsaf_set_dev_field(dsaf_dev, DSAF_TBL_TCAM_ADDR_0_REG,
+                          DSAF_TBL_TCAM_ADDR_M, DSAF_TBL_TCAM_ADDR_S,
+                          tab_tcam_addr);
+}
+
+static inline void hns_dsaf_tbl_tcam_load_pul(struct dsaf_device *dsaf_dev)
+{
+       u32 o_tbl_pul;
+
+       o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_LOAD_S, 1);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul);
+       dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_LOAD_S, 0);
+       dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul);
+}
+
+static inline void hns_dsaf_tbl_line_addr_cfg(struct dsaf_device *dsaf_dev,
+                                             u32 tab_line_addr)
+{
+       dsaf_set_dev_field(dsaf_dev, DSAF_TBL_LINE_ADDR_0_REG,
+                          DSAF_TBL_LINE_ADDR_M, DSAF_TBL_LINE_ADDR_S,
+                          tab_line_addr);
+}
+
+static inline int hns_dsaf_get_comm_idx_by_port(int port)
+{
+       if ((port < DSAF_COMM_CHN) || (port == DSAF_MAX_PORT_NUM_PER_CHIP))
+               return 0;
+       else
+               return (port - DSAF_COMM_CHN + 1);
+}
+
+static inline struct hnae_vf_cb *hns_ae_get_vf_cb(
+       struct hnae_handle *handle)
+{
+       return container_of(handle, struct hnae_vf_cb, ae_handle);
+}
+
+int hns_dsaf_set_mac_uc_entry(struct dsaf_device *dsaf_dev,
+                             struct dsaf_drv_mac_single_dest_entry *mac_entry);
+int hns_dsaf_set_mac_mc_entry(struct dsaf_device *dsaf_dev,
+                             struct dsaf_drv_mac_multi_dest_entry *mac_entry);
+int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev,
+                            struct dsaf_drv_mac_single_dest_entry *mac_entry);
+int hns_dsaf_del_mac_entry(struct dsaf_device *dsaf_dev, u16 vlan_id,
+                          u8 in_port_num, u8 *addr);
+int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev,
+                            struct dsaf_drv_mac_single_dest_entry *mac_entry);
+int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev,
+                             struct dsaf_drv_mac_single_dest_entry *mac_entry);
+int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev,
+                             struct dsaf_drv_mac_multi_dest_entry *mac_entry);
+int hns_dsaf_get_mac_entry_by_index(
+       struct dsaf_device *dsaf_dev,
+       u16 entry_index,
+       struct dsaf_drv_mac_multi_dest_entry *mac_entry);
+
+void hns_dsaf_rst(struct dsaf_device *dsaf_dev, u32 val);
+
+void hns_ppe_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val);
+
+void hns_ppe_com_srst(struct ppe_common_cb *ppe_common, u32 val);
+
+void hns_dsaf_fix_mac_mode(struct hns_mac_cb *mac_cb);
+
+int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev);
+void hns_dsaf_ae_uninit(struct dsaf_device *dsaf_dev);
+
+void hns_dsaf_xge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val);
+void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val);
+void hns_dsaf_xge_core_srst_by_port(struct dsaf_device *dsaf_dev,
+                                   u32 port, u32 val);
+
+void hns_dsaf_update_stats(struct dsaf_device *dsaf_dev, u32 inode_num);
+
+int hns_dsaf_get_sset_count(int stringset);
+void hns_dsaf_get_stats(struct dsaf_device *ddev, u64 *data, int port);
+void hns_dsaf_get_strings(int stringset, u8 *data, int port);
+
+void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data);
+int hns_dsaf_get_regs_count(void);
+void hns_dsaf_set_promisc_mode(struct dsaf_device *dsaf_dev, u32 en);
+
+#endif /* __HNS_DSAF_MAIN_H__ */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c
new file mode 100644 (file)
index 0000000..523e9b8
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 "hns_dsaf_misc.h"
+#include "hns_dsaf_mac.h"
+#include "hns_dsaf_reg.h"
+#include "hns_dsaf_ppe.h"
+
+void hns_cpld_set_led(struct hns_mac_cb *mac_cb, int link_status,
+                     u16 speed, int data)
+{
+       int speed_reg = 0;
+       u8 value;
+
+       if (!mac_cb) {
+               pr_err("sfp_led_opt mac_dev is null!\n");
+               return;
+       }
+       if (!mac_cb->cpld_vaddr) {
+               dev_err(mac_cb->dev, "mac_id=%d, cpld_vaddr is null !\n",
+                       mac_cb->mac_id);
+               return;
+       }
+
+       if (speed == MAC_SPEED_10000)
+               speed_reg = 1;
+
+       value = mac_cb->cpld_led_value;
+
+       if (link_status) {
+               dsaf_set_bit(value, DSAF_LED_LINK_B, link_status);
+               dsaf_set_field(value, DSAF_LED_SPEED_M,
+                              DSAF_LED_SPEED_S, speed_reg);
+               dsaf_set_bit(value, DSAF_LED_DATA_B, data);
+
+               if (value != mac_cb->cpld_led_value) {
+                       dsaf_write_b(mac_cb->cpld_vaddr, value);
+                       mac_cb->cpld_led_value = value;
+               }
+       } else {
+               dsaf_write_b(mac_cb->cpld_vaddr, CPLD_LED_DEFAULT_VALUE);
+               mac_cb->cpld_led_value = CPLD_LED_DEFAULT_VALUE;
+       }
+}
+
+void cpld_led_reset(struct hns_mac_cb *mac_cb)
+{
+       if (!mac_cb || !mac_cb->cpld_vaddr)
+               return;
+
+       dsaf_write_b(mac_cb->cpld_vaddr, CPLD_LED_DEFAULT_VALUE);
+       mac_cb->cpld_led_value = CPLD_LED_DEFAULT_VALUE;
+}
+
+int cpld_set_led_id(struct hns_mac_cb *mac_cb,
+                   enum hnae_led_state status)
+{
+       switch (status) {
+       case HNAE_LED_ACTIVE:
+               mac_cb->cpld_led_value = dsaf_read_b(mac_cb->cpld_vaddr);
+               dsaf_set_bit(mac_cb->cpld_led_value, DSAF_LED_ANCHOR_B,
+                            CPLD_LED_ON_VALUE);
+               dsaf_write_b(mac_cb->cpld_vaddr, mac_cb->cpld_led_value);
+               return 2;
+       case HNAE_LED_INACTIVE:
+               dsaf_set_bit(mac_cb->cpld_led_value, DSAF_LED_ANCHOR_B,
+                            CPLD_LED_DEFAULT_VALUE);
+               dsaf_write_b(mac_cb->cpld_vaddr, mac_cb->cpld_led_value);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+#define RESET_REQ_OR_DREQ 1
+
+void hns_dsaf_rst(struct dsaf_device *dsaf_dev, u32 val)
+{
+       u32 xbar_reg_addr;
+       u32 nt_reg_addr;
+
+       if (!val) {
+               xbar_reg_addr = DSAF_SUB_SC_XBAR_RESET_REQ_REG;
+               nt_reg_addr = DSAF_SUB_SC_NT_RESET_REQ_REG;
+       } else {
+               xbar_reg_addr = DSAF_SUB_SC_XBAR_RESET_DREQ_REG;
+               nt_reg_addr = DSAF_SUB_SC_NT_RESET_DREQ_REG;
+       }
+
+       dsaf_write_reg(dsaf_dev->sc_base, xbar_reg_addr,
+                      RESET_REQ_OR_DREQ);
+       dsaf_write_reg(dsaf_dev->sc_base, nt_reg_addr,
+                      RESET_REQ_OR_DREQ);
+}
+
+void hns_dsaf_xge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val)
+{
+       u32 reg_val = 0;
+       u32 reg_addr;
+
+       if (port >= DSAF_XGE_NUM)
+               return;
+
+       reg_val |= RESET_REQ_OR_DREQ;
+       reg_val |= 0x2082082 << port;
+
+       if (val == 0)
+               reg_addr = DSAF_SUB_SC_XGE_RESET_REQ_REG;
+       else
+               reg_addr = DSAF_SUB_SC_XGE_RESET_DREQ_REG;
+
+       dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val);
+}
+
+void hns_dsaf_xge_core_srst_by_port(struct dsaf_device *dsaf_dev,
+                                   u32 port, u32 val)
+{
+       u32 reg_val = 0;
+       u32 reg_addr;
+
+       if (port >= DSAF_XGE_NUM)
+               return;
+
+       reg_val |= XGMAC_TRX_CORE_SRST_M << port;
+
+       if (val == 0)
+               reg_addr = DSAF_SUB_SC_XGE_RESET_REQ_REG;
+       else
+               reg_addr = DSAF_SUB_SC_XGE_RESET_DREQ_REG;
+
+       dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val);
+}
+
+void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val)
+{
+       u32 reg_val_1;
+       u32 reg_val_2;
+
+       if (port >= DSAF_GE_NUM)
+               return;
+
+       if (port < DSAF_SERVICE_NW_NUM) {
+               reg_val_1  = 0x1 << port;
+               reg_val_2  = 0x1041041 << port;
+
+               if (val == 0) {
+                       dsaf_write_reg(dsaf_dev->sc_base,
+                                      DSAF_SUB_SC_GE_RESET_REQ1_REG,
+                                      reg_val_1);
+
+                       dsaf_write_reg(dsaf_dev->sc_base,
+                                      DSAF_SUB_SC_GE_RESET_REQ0_REG,
+                                      reg_val_2);
+               } else {
+                       dsaf_write_reg(dsaf_dev->sc_base,
+                                      DSAF_SUB_SC_GE_RESET_DREQ0_REG,
+                                      reg_val_2);
+
+                       dsaf_write_reg(dsaf_dev->sc_base,
+                                      DSAF_SUB_SC_GE_RESET_DREQ1_REG,
+                                      reg_val_1);
+               }
+       } else {
+               reg_val_1 = 0x15540 << (port - 6);
+               reg_val_2 = 0x100 << (port - 6);
+
+               if (val == 0) {
+                       dsaf_write_reg(dsaf_dev->sc_base,
+                                      DSAF_SUB_SC_GE_RESET_REQ1_REG,
+                                      reg_val_1);
+
+                       dsaf_write_reg(dsaf_dev->sc_base,
+                                      DSAF_SUB_SC_PPE_RESET_REQ_REG,
+                                      reg_val_2);
+               } else {
+                       dsaf_write_reg(dsaf_dev->sc_base,
+                                      DSAF_SUB_SC_GE_RESET_DREQ1_REG,
+                                      reg_val_1);
+
+                       dsaf_write_reg(dsaf_dev->sc_base,
+                                      DSAF_SUB_SC_PPE_RESET_DREQ_REG,
+                                      reg_val_2);
+               }
+       }
+}
+
+void hns_ppe_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val)
+{
+       u32 reg_val = 0;
+       u32 reg_addr;
+
+       reg_val |= RESET_REQ_OR_DREQ << port;
+
+       if (val == 0)
+               reg_addr = DSAF_SUB_SC_PPE_RESET_REQ_REG;
+       else
+               reg_addr = DSAF_SUB_SC_PPE_RESET_DREQ_REG;
+
+       dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val);
+}
+
+void hns_ppe_com_srst(struct ppe_common_cb *ppe_common, u32 val)
+{
+       int comm_index = ppe_common->comm_index;
+       struct dsaf_device *dsaf_dev = ppe_common->dsaf_dev;
+       u32 reg_val;
+       u32 reg_addr;
+
+       if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) {
+               reg_val = RESET_REQ_OR_DREQ;
+               if (val == 0)
+                       reg_addr = DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG;
+               else
+                       reg_addr = DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG;
+
+       } else {
+               reg_val = 0x100 << (comm_index - 1);
+
+               if (val == 0)
+                       reg_addr = DSAF_SUB_SC_PPE_RESET_REQ_REG;
+               else
+                       reg_addr = DSAF_SUB_SC_PPE_RESET_DREQ_REG;
+       }
+
+       dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val);
+}
+
+/**
+ * hns_mac_get_sds_mode - get phy ifterface form serdes mode
+ * @mac_cb: mac control block
+ * retuen phy interface
+ */
+phy_interface_t hns_mac_get_phy_if(struct hns_mac_cb *mac_cb)
+{
+       u32 hilink3_mode;
+       u32 hilink4_mode;
+       void __iomem *sys_ctl_vaddr = mac_cb->sys_ctl_vaddr;
+       int dev_id = mac_cb->mac_id;
+       phy_interface_t phy_if = PHY_INTERFACE_MODE_NA;
+
+       hilink3_mode = dsaf_read_reg(sys_ctl_vaddr, HNS_MAC_HILINK3_REG);
+       hilink4_mode = dsaf_read_reg(sys_ctl_vaddr, HNS_MAC_HILINK4_REG);
+       if (dev_id >= 0 && dev_id <= 3) {
+               if (hilink4_mode == 0)
+                       phy_if = PHY_INTERFACE_MODE_SGMII;
+               else
+                       phy_if = PHY_INTERFACE_MODE_XGMII;
+       } else if (dev_id >= 4 && dev_id <= 5) {
+               if (hilink3_mode == 0)
+                       phy_if = PHY_INTERFACE_MODE_SGMII;
+               else
+                       phy_if = PHY_INTERFACE_MODE_XGMII;
+       } else {
+               phy_if = PHY_INTERFACE_MODE_SGMII;
+       }
+
+       dev_dbg(mac_cb->dev,
+               "hilink3_mode=%d, hilink4_mode=%d dev_id=%d, phy_if=%d\n",
+               hilink3_mode, hilink4_mode, dev_id, phy_if);
+       return phy_if;
+}
+
+/**
+ * hns_mac_config_sds_loopback - set loop back for serdes
+ * @mac_cb: mac control block
+ * retuen 0 == success
+ */
+int hns_mac_config_sds_loopback(struct hns_mac_cb *mac_cb, u8 en)
+{
+       /* port 0-3 hilink4 base is serdes_vaddr + 0x00280000
+        * port 4-7 hilink3 base is serdes_vaddr + 0x00200000
+        */
+       u8 *base_addr = (u8 *)mac_cb->serdes_vaddr +
+                      (mac_cb->mac_id <= 3 ? 0x00280000 : 0x00200000);
+       const u8 lane_id[] = {
+               0,      /* mac 0 -> lane 0 */
+               1,      /* mac 1 -> lane 1 */
+               2,      /* mac 2 -> lane 2 */
+               3,      /* mac 3 -> lane 3 */
+               2,      /* mac 4 -> lane 2 */
+               3,      /* mac 5 -> lane 3 */
+               0,      /* mac 6 -> lane 0 */
+               1       /* mac 7 -> lane 1 */
+       };
+#define RX_CSR(lane, reg) ((0x4080 + (reg) * 0x0002 + (lane) * 0x0200) * 2)
+       u64 reg_offset = RX_CSR(lane_id[mac_cb->mac_id], 0);
+
+       int sfp_prsnt;
+       int ret = hns_mac_get_sfp_prsnt(mac_cb, &sfp_prsnt);
+
+       if (!mac_cb->phy_node) {
+               if (ret)
+                       pr_info("please confirm sfp is present or not\n");
+               else
+                       if (!sfp_prsnt)
+                               pr_info("no sfp in this eth\n");
+       }
+
+       dsaf_set_reg_field(base_addr, reg_offset, 1ull << 10, 10, !!en);
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h
new file mode 100644 (file)
index 0000000..419f07a
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 _HNS_DSAF_MISC_H
+#define _HNS_DSAF_MISC_H
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#include "hns_dsaf_mac.h"
+
+#define CPLD_ADDR_PORT_OFFSET  0x4
+
+#define HS_LED_ON              0xE
+#define HS_LED_OFF             0xF
+
+#define CPLD_LED_ON_VALUE      1
+#define CPLD_LED_DEFAULT_VALUE 0
+
+#define MAC_SFP_PORT_OFFSET    0x2
+
+#define DSAF_LED_SPEED_S 0
+#define DSAF_LED_SPEED_M (0x3 << DSAF_LED_SPEED_S)
+
+#define DSAF_LED_LINK_B 2
+#define DSAF_LED_DATA_B 4
+#define DSAF_LED_ANCHOR_B 5
+
+void hns_cpld_set_led(struct hns_mac_cb *mac_cb, int link_status,
+                     u16 speed, int data);
+void cpld_led_reset(struct hns_mac_cb *mac_cb);
+int cpld_set_led_id(struct hns_mac_cb *mac_cb,
+                   enum hnae_led_state status);
+int hns_mac_get_sfp_prsnt(struct hns_mac_cb *mac_cb, int *sfp_prsnt);
+
+#endif
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
new file mode 100644 (file)
index 0000000..67f33f1
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+#include "hns_dsaf_ppe.h"
+
+static void __iomem *hns_ppe_common_get_ioaddr(
+       struct ppe_common_cb *ppe_common)
+{
+       void __iomem *base_addr;
+
+       int idx = ppe_common->comm_index;
+
+       if (idx == HNS_DSAF_COMM_SERVICE_NW_IDX)
+               base_addr = ppe_common->dsaf_dev->ppe_base
+                       + PPE_COMMON_REG_OFFSET;
+       else
+               base_addr = ppe_common->dsaf_dev->sds_base
+                       + (idx - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET
+                       + PPE_COMMON_REG_OFFSET;
+
+       return base_addr;
+}
+
+/**
+ * hns_ppe_common_get_cfg - get ppe common config
+ * @dsaf_dev: dasf device
+ * comm_index: common index
+ * retuen 0 - success , negative --fail
+ */
+int hns_ppe_common_get_cfg(struct dsaf_device *dsaf_dev, int comm_index)
+{
+       struct ppe_common_cb *ppe_common;
+       int ppe_num;
+
+       if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX)
+               ppe_num = HNS_PPE_SERVICE_NW_ENGINE_NUM;
+       else
+               ppe_num = HNS_PPE_DEBUG_NW_ENGINE_NUM;
+
+       ppe_common = devm_kzalloc(dsaf_dev->dev, sizeof(*ppe_common) +
+               ppe_num * sizeof(struct hns_ppe_cb), GFP_KERNEL);
+       if (!ppe_common)
+               return -ENOMEM;
+
+       ppe_common->ppe_num = ppe_num;
+       ppe_common->dsaf_dev = dsaf_dev;
+       ppe_common->comm_index = comm_index;
+       if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX)
+               ppe_common->ppe_mode = PPE_COMMON_MODE_SERVICE;
+       else
+               ppe_common->ppe_mode = PPE_COMMON_MODE_DEBUG;
+       ppe_common->dev = dsaf_dev->dev;
+
+       ppe_common->io_base = hns_ppe_common_get_ioaddr(ppe_common);
+
+       dsaf_dev->ppe_common[comm_index] = ppe_common;
+
+       return 0;
+}
+
+void hns_ppe_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index)
+{
+       dsaf_dev->ppe_common[comm_index] = NULL;
+}
+
+static void __iomem *hns_ppe_get_iobase(struct ppe_common_cb *ppe_common,
+                                       int ppe_idx)
+{
+       void __iomem *base_addr;
+       int common_idx = ppe_common->comm_index;
+
+       if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE) {
+               base_addr = ppe_common->dsaf_dev->ppe_base +
+                       ppe_idx * PPE_REG_OFFSET;
+
+       } else {
+               base_addr = ppe_common->dsaf_dev->sds_base +
+                       (common_idx - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET;
+       }
+
+       return base_addr;
+}
+
+static int hns_ppe_get_port(struct ppe_common_cb *ppe_common, int idx)
+{
+       int port;
+
+       if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE)
+               port = idx;
+       else
+               port = HNS_PPE_SERVICE_NW_ENGINE_NUM
+                       + ppe_common->comm_index - 1;
+
+       return port;
+}
+
+static void hns_ppe_get_cfg(struct ppe_common_cb *ppe_common)
+{
+       u32 i;
+       struct hns_ppe_cb *ppe_cb;
+       u32 ppe_num = ppe_common->ppe_num;
+
+       for (i = 0; i < ppe_num; i++) {
+               ppe_cb = &ppe_common->ppe_cb[i];
+               ppe_cb->dev = ppe_common->dev;
+               ppe_cb->next = NULL;
+               ppe_cb->ppe_common_cb = ppe_common;
+               ppe_cb->index = i;
+               ppe_cb->port = hns_ppe_get_port(ppe_common, i);
+               ppe_cb->io_base = hns_ppe_get_iobase(ppe_common, i);
+               ppe_cb->virq = 0;
+       }
+}
+
+static void hns_ppe_cnt_clr_ce(struct hns_ppe_cb *ppe_cb)
+{
+       dsaf_set_dev_bit(ppe_cb, PPE_TNL_0_5_CNT_CLR_CE_REG,
+                        PPE_CNT_CLR_CE_B, 1);
+}
+
+/**
+ * hns_ppe_checksum_hw - set ppe checksum caculate
+ * @ppe_device: ppe device
+ * @value: value
+ */
+static void hns_ppe_checksum_hw(struct hns_ppe_cb *ppe_cb, u32 value)
+{
+       dsaf_set_dev_field(ppe_cb, PPE_CFG_PRO_CHECK_EN_REG,
+                          0xfffffff, 0, value);
+}
+
+static void hns_ppe_set_qid_mode(struct ppe_common_cb *ppe_common,
+                                enum ppe_qid_mode qid_mdoe)
+{
+       dsaf_set_dev_field(ppe_common, PPE_COM_CFG_QID_MODE_REG,
+                          PPE_CFG_QID_MODE_CF_QID_MODE_M,
+                          PPE_CFG_QID_MODE_CF_QID_MODE_S, qid_mdoe);
+}
+
+/**
+ * hns_ppe_set_qid - set ppe qid
+ * @ppe_common: ppe common device
+ * @qid: queue id
+ */
+static void hns_ppe_set_qid(struct ppe_common_cb *ppe_common, u32 qid)
+{
+       u32 qid_mod = dsaf_read_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG);
+
+       if (!dsaf_get_field(qid_mod, PPE_CFG_QID_MODE_DEF_QID_M,
+                           PPE_CFG_QID_MODE_DEF_QID_S)) {
+               dsaf_set_field(qid_mod, PPE_CFG_QID_MODE_DEF_QID_M,
+                              PPE_CFG_QID_MODE_DEF_QID_S, qid);
+               dsaf_write_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG, qid_mod);
+       }
+}
+
+/**
+ * hns_ppe_set_port_mode - set port mode
+ * @ppe_device: ppe device
+ * @mode: port mode
+ */
+static void hns_ppe_set_port_mode(struct hns_ppe_cb *ppe_cb,
+                                 enum ppe_port_mode mode)
+{
+       dsaf_write_dev(ppe_cb, PPE_CFG_XGE_MODE_REG, mode);
+}
+
+/**
+ * hns_ppe_common_init_hw - init ppe common device
+ * @ppe_common: ppe common device
+ *
+ * Return 0 on success, negative on failure
+ */
+static int hns_ppe_common_init_hw(struct ppe_common_cb *ppe_common)
+{
+       enum ppe_qid_mode qid_mode;
+       enum dsaf_mode dsaf_mode = ppe_common->dsaf_dev->dsaf_mode;
+
+       hns_ppe_com_srst(ppe_common, 0);
+       mdelay(100);
+       hns_ppe_com_srst(ppe_common, 1);
+       mdelay(100);
+
+       if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE) {
+               switch (dsaf_mode) {
+               case DSAF_MODE_ENABLE_FIX:
+               case DSAF_MODE_DISABLE_FIX:
+                       qid_mode = PPE_QID_MODE0;
+                       hns_ppe_set_qid(ppe_common, 0);
+                       break;
+               case DSAF_MODE_ENABLE_0VM:
+               case DSAF_MODE_DISABLE_2PORT_64VM:
+                       qid_mode = PPE_QID_MODE3;
+                       break;
+               case DSAF_MODE_ENABLE_8VM:
+               case DSAF_MODE_DISABLE_2PORT_16VM:
+                       qid_mode = PPE_QID_MODE4;
+                       break;
+               case DSAF_MODE_ENABLE_16VM:
+               case DSAF_MODE_DISABLE_6PORT_0VM:
+                       qid_mode = PPE_QID_MODE5;
+                       break;
+               case DSAF_MODE_ENABLE_32VM:
+               case DSAF_MODE_DISABLE_6PORT_16VM:
+                       qid_mode = PPE_QID_MODE2;
+                       break;
+               case DSAF_MODE_ENABLE_128VM:
+               case DSAF_MODE_DISABLE_6PORT_4VM:
+                       qid_mode = PPE_QID_MODE1;
+                       break;
+               case DSAF_MODE_DISABLE_2PORT_8VM:
+                       qid_mode = PPE_QID_MODE7;
+                       break;
+               case DSAF_MODE_DISABLE_6PORT_2VM:
+                       qid_mode = PPE_QID_MODE6;
+                       break;
+               default:
+                       dev_err(ppe_common->dev,
+                               "get ppe queue mode failed! dsaf_mode=%d\n",
+                               dsaf_mode);
+                       return -EINVAL;
+               }
+               hns_ppe_set_qid_mode(ppe_common, qid_mode);
+       }
+
+       dsaf_set_dev_bit(ppe_common, PPE_COM_COMMON_CNT_CLR_CE_REG,
+                        PPE_COMMON_CNT_CLR_CE_B, 1);
+
+       return 0;
+}
+
+/*clr ppe exception irq*/
+static void hns_ppe_exc_irq_en(struct hns_ppe_cb *ppe_cb, int en)
+{
+       u32 clr_vlue = 0xfffffffful;
+       u32 msk_vlue = en ? 0xfffffffful : 0; /*1 is en, 0 is dis*/
+       u32 vld_msk = 0;
+
+       /*only care bit 0,1,7*/
+       dsaf_set_bit(vld_msk, 0, 1);
+       dsaf_set_bit(vld_msk, 1, 1);
+       dsaf_set_bit(vld_msk, 7, 1);
+
+       /*clr sts**/
+       dsaf_write_dev(ppe_cb, PPE_RINT_REG, clr_vlue);
+
+       /*for some reserved bits, so set 0**/
+       dsaf_write_dev(ppe_cb, PPE_INTEN_REG, msk_vlue & vld_msk);
+}
+
+/**
+ * ppe_init_hw - init ppe
+ * @ppe_device: ppe device
+ */
+static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb)
+{
+       struct ppe_common_cb *ppe_common_cb = ppe_cb->ppe_common_cb;
+       u32 port = ppe_cb->port;
+       struct dsaf_device *dsaf_dev = ppe_common_cb->dsaf_dev;
+
+       hns_ppe_srst_by_port(dsaf_dev, port, 0);
+       mdelay(10);
+       hns_ppe_srst_by_port(dsaf_dev, port, 1);
+
+       /* clr and msk except irq*/
+       hns_ppe_exc_irq_en(ppe_cb, 0);
+
+       if (ppe_common_cb->ppe_mode == PPE_COMMON_MODE_DEBUG)
+               hns_ppe_set_port_mode(ppe_cb, PPE_MODE_GE);
+       else
+               hns_ppe_set_port_mode(ppe_cb, PPE_MODE_XGE);
+       hns_ppe_checksum_hw(ppe_cb, 0xffffffff);
+       hns_ppe_cnt_clr_ce(ppe_cb);
+}
+
+/**
+ * ppe_uninit_hw - uninit ppe
+ * @ppe_device: ppe device
+ */
+static void hns_ppe_uninit_hw(struct hns_ppe_cb *ppe_cb)
+{
+       u32 port;
+
+       if (ppe_cb->ppe_common_cb) {
+               port = ppe_cb->index;
+               hns_ppe_srst_by_port(ppe_cb->ppe_common_cb->dsaf_dev, port, 0);
+       }
+}
+
+void hns_ppe_uninit_ex(struct ppe_common_cb *ppe_common)
+{
+       u32 i;
+
+       for (i = 0; i < ppe_common->ppe_num; i++) {
+               hns_ppe_uninit_hw(&ppe_common->ppe_cb[i]);
+               memset(&ppe_common->ppe_cb[i], 0, sizeof(struct hns_ppe_cb));
+       }
+}
+
+void hns_ppe_uninit(struct dsaf_device *dsaf_dev)
+{
+       u32 i;
+
+       for (i = 0; i < HNS_PPE_COM_NUM; i++) {
+               if (dsaf_dev->ppe_common[i])
+                       hns_ppe_uninit_ex(dsaf_dev->ppe_common[i]);
+               hns_rcb_common_free_cfg(dsaf_dev, i);
+               hns_ppe_common_free_cfg(dsaf_dev, i);
+       }
+}
+
+/**
+ * hns_ppe_reset - reinit ppe/rcb hw
+ * @dsaf_dev: dasf device
+ * retuen void
+ */
+void hns_ppe_reset_common(struct dsaf_device *dsaf_dev, u8 ppe_common_index)
+{
+       u32 i;
+       int ret;
+       struct ppe_common_cb *ppe_common;
+
+       ppe_common = dsaf_dev->ppe_common[ppe_common_index];
+       ret = hns_ppe_common_init_hw(ppe_common);
+       if (ret)
+               return;
+
+       ret = hns_rcb_common_init_hw(dsaf_dev->rcb_common[ppe_common_index]);
+       if (ret)
+               return;
+
+       for (i = 0; i < ppe_common->ppe_num; i++)
+               hns_ppe_init_hw(&ppe_common->ppe_cb[i]);
+
+       hns_rcb_common_init_commit_hw(dsaf_dev->rcb_common[ppe_common_index]);
+}
+
+void hns_ppe_update_stats(struct hns_ppe_cb *ppe_cb)
+{
+       struct hns_ppe_hw_stats *hw_stats = &ppe_cb->hw_stats;
+
+       hw_stats->rx_pkts_from_sw
+               += dsaf_read_dev(ppe_cb, PPE_HIS_RX_SW_PKT_CNT_REG);
+       hw_stats->rx_pkts
+               += dsaf_read_dev(ppe_cb, PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG);
+       hw_stats->rx_drop_no_bd
+               += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_NO_BUF_CNT_REG);
+       hw_stats->rx_alloc_buf_fail
+               += dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_FAIL_CNT_REG);
+       hw_stats->rx_alloc_buf_wait
+               += dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_WAIT_CNT_REG);
+       hw_stats->rx_drop_no_buf
+               += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_FUL_CNT_REG);
+       hw_stats->rx_err_fifo_full
+               += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_PRT_CNT_REG);
+
+       hw_stats->tx_bd_form_rcb
+               += dsaf_read_dev(ppe_cb, PPE_HIS_TX_BD_CNT_REG);
+       hw_stats->tx_pkts_from_rcb
+               += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CNT_REG);
+       hw_stats->tx_pkts
+               += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_OK_CNT_REG);
+       hw_stats->tx_err_fifo_empty
+               += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_EPT_CNT_REG);
+       hw_stats->tx_err_checksum
+               += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CS_FAIL_CNT_REG);
+}
+
+int hns_ppe_get_sset_count(int stringset)
+{
+       if (stringset == ETH_SS_STATS)
+               return ETH_PPE_STATIC_NUM;
+       return 0;
+}
+
+int hns_ppe_get_regs_count(void)
+{
+       return ETH_PPE_DUMP_NUM;
+}
+
+/**
+ * ppe_get_strings - get ppe srting
+ * @ppe_device: ppe device
+ * @stringset: string set type
+ * @data: output string
+ */
+void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data)
+{
+       char *buff = (char *)data;
+       int index = ppe_cb->index;
+
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_sw_pkt", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_ok", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_drop_pkt_no_bd", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_alloc_buf_fail", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_alloc_buf_wait", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_drop_no_buf", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_err_fifo_full", index);
+       buff = buff + ETH_GSTRING_LEN;
+
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_bd", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_ok", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_err_fifo_empty", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_err_csum_fail", index);
+}
+
+void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data)
+{
+       u64 *regs_buff = data;
+       struct hns_ppe_hw_stats *hw_stats = &ppe_cb->hw_stats;
+
+       regs_buff[0] = hw_stats->rx_pkts_from_sw;
+       regs_buff[1] = hw_stats->rx_pkts;
+       regs_buff[2] = hw_stats->rx_drop_no_bd;
+       regs_buff[3] = hw_stats->rx_alloc_buf_fail;
+       regs_buff[4] = hw_stats->rx_alloc_buf_wait;
+       regs_buff[5] = hw_stats->rx_drop_no_buf;
+       regs_buff[6] = hw_stats->rx_err_fifo_full;
+
+       regs_buff[7] = hw_stats->tx_bd_form_rcb;
+       regs_buff[8] = hw_stats->tx_pkts_from_rcb;
+       regs_buff[9] = hw_stats->tx_pkts;
+       regs_buff[10] = hw_stats->tx_err_fifo_empty;
+       regs_buff[11] = hw_stats->tx_err_checksum;
+}
+
+/**
+ * hns_ppe_init - init ppe device
+ * @dsaf_dev: dasf device
+ * retuen 0 - success , negative --fail
+ */
+int hns_ppe_init(struct dsaf_device *dsaf_dev)
+{
+       int i, k;
+       int ret;
+
+       for (i = 0; i < HNS_PPE_COM_NUM; i++) {
+               ret = hns_ppe_common_get_cfg(dsaf_dev, i);
+               if (ret)
+                       goto get_ppe_cfg_fail;
+
+               ret = hns_rcb_common_get_cfg(dsaf_dev, i);
+               if (ret)
+                       goto get_rcb_cfg_fail;
+
+               hns_ppe_get_cfg(dsaf_dev->ppe_common[i]);
+
+               hns_rcb_get_cfg(dsaf_dev->rcb_common[i]);
+       }
+
+       for (i = 0; i < HNS_PPE_COM_NUM; i++)
+               hns_ppe_reset_common(dsaf_dev, i);
+
+       return 0;
+
+get_rcb_cfg_fail:
+       hns_ppe_common_free_cfg(dsaf_dev, i);
+get_ppe_cfg_fail:
+       for (k = i - 1; k >= 0; k--) {
+               hns_rcb_common_free_cfg(dsaf_dev, k);
+               hns_ppe_common_free_cfg(dsaf_dev, k);
+       }
+       return ret;
+}
+
+void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data)
+{
+       struct ppe_common_cb *ppe_common = ppe_cb->ppe_common_cb;
+       u32 *regs = data;
+       u32 i;
+       u32 offset;
+
+       /* ppe common registers */
+       regs[0] = dsaf_read_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG);
+       regs[1] = dsaf_read_dev(ppe_common, PPE_COM_INTEN_REG);
+       regs[2] = dsaf_read_dev(ppe_common, PPE_COM_RINT_REG);
+       regs[3] = dsaf_read_dev(ppe_common, PPE_COM_INTSTS_REG);
+       regs[4] = dsaf_read_dev(ppe_common, PPE_COM_COMMON_CNT_CLR_CE_REG);
+
+       for (i = 0; i < DSAF_TOTAL_QUEUE_NUM; i++) {
+               offset = PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG + 0x4 * i;
+               regs[5 + i] = dsaf_read_dev(ppe_common, offset);
+               offset = PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG + 0x4 * i;
+               regs[5 + i + DSAF_TOTAL_QUEUE_NUM]
+                               = dsaf_read_dev(ppe_common, offset);
+               offset = PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG + 0x4 * i;
+               regs[5 + i + DSAF_TOTAL_QUEUE_NUM * 2]
+                               = dsaf_read_dev(ppe_common, offset);
+               offset = PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG + 0x4 * i;
+               regs[5 + i + DSAF_TOTAL_QUEUE_NUM * 3]
+                               = dsaf_read_dev(ppe_common, offset);
+       }
+
+       /* mark end of ppe regs */
+       for (i = 521; i < 524; i++)
+               regs[i] = 0xeeeeeeee;
+
+       /* ppe channel registers */
+       regs[525] = dsaf_read_dev(ppe_cb, PPE_CFG_TX_FIFO_THRSLD_REG);
+       regs[526] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_THRSLD_REG);
+       regs[527] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_PAUSE_THRSLD_REG);
+       regs[528] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_SW_BP_THRSLD_REG);
+       regs[529] = dsaf_read_dev(ppe_cb, PPE_CFG_PAUSE_IDLE_CNT_REG);
+       regs[530] = dsaf_read_dev(ppe_cb, PPE_CFG_BUS_CTRL_REG);
+       regs[531] = dsaf_read_dev(ppe_cb, PPE_CFG_TNL_TO_BE_RST_REG);
+       regs[532] = dsaf_read_dev(ppe_cb, PPE_CURR_TNL_CAN_RST_REG);
+
+       regs[533] = dsaf_read_dev(ppe_cb, PPE_CFG_XGE_MODE_REG);
+       regs[534] = dsaf_read_dev(ppe_cb, PPE_CFG_MAX_FRAME_LEN_REG);
+       regs[535] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_PKT_MODE_REG);
+       regs[536] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_VLAN_TAG_REG);
+       regs[537] = dsaf_read_dev(ppe_cb, PPE_CFG_TAG_GEN_REG);
+       regs[538] = dsaf_read_dev(ppe_cb, PPE_CFG_PARSE_TAG_REG);
+       regs[539] = dsaf_read_dev(ppe_cb, PPE_CFG_PRO_CHECK_EN_REG);
+
+       regs[540] = dsaf_read_dev(ppe_cb, PPE_INTEN_REG);
+       regs[541] = dsaf_read_dev(ppe_cb, PPE_RINT_REG);
+       regs[542] = dsaf_read_dev(ppe_cb, PPE_INTSTS_REG);
+       regs[543] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_PKT_INT_REG);
+
+       regs[544] = dsaf_read_dev(ppe_cb, PPE_CFG_HEAT_DECT_TIME0_REG);
+       regs[545] = dsaf_read_dev(ppe_cb, PPE_CFG_HEAT_DECT_TIME1_REG);
+
+       /* ppe static */
+       regs[546] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_SW_PKT_CNT_REG);
+       regs[547] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG);
+       regs[548] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_NO_BUF_CNT_REG);
+       regs[549] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_BD_CNT_REG);
+       regs[550] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CNT_REG);
+       regs[551] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_OK_CNT_REG);
+       regs[552] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_EPT_CNT_REG);
+       regs[553] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CS_FAIL_CNT_REG);
+       regs[554] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_FAIL_CNT_REG);
+       regs[555] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_WAIT_CNT_REG);
+       regs[556] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_FUL_CNT_REG);
+       regs[557] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_PRT_CNT_REG);
+
+       regs[558] = dsaf_read_dev(ppe_cb, PPE_TNL_0_5_CNT_CLR_CE_REG);
+       regs[559] = dsaf_read_dev(ppe_cb, PPE_CFG_AXI_DBG_REG);
+       regs[560] = dsaf_read_dev(ppe_cb, PPE_HIS_PRO_ERR_REG);
+       regs[561] = dsaf_read_dev(ppe_cb, PPE_HIS_TNL_FIFO_ERR_REG);
+       regs[562] = dsaf_read_dev(ppe_cb, PPE_CURR_CFF_DATA_NUM_REG);
+       regs[563] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_ST_REG);
+       regs[564] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_ST_REG);
+       regs[565] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_FIFO0_REG);
+       regs[566] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_FIFO1_REG);
+       regs[567] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_FIFO0_REG);
+       regs[568] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_FIFO1_REG);
+       regs[569] = dsaf_read_dev(ppe_cb, PPE_ECO0_REG);
+       regs[570] = dsaf_read_dev(ppe_cb, PPE_ECO1_REG);
+       regs[571] = dsaf_read_dev(ppe_cb, PPE_ECO2_REG);
+
+       /* mark end of ppe regs */
+       for (i = 572; i < 576; i++)
+               regs[i] = 0xeeeeeeee;
+}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h
new file mode 100644 (file)
index 0000000..4894f9a
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 _HNS_DSAF_PPE_H
+#define _HNS_DSAF_PPE_H
+
+#include <linux/platform_device.h>
+
+#include "hns_dsaf_main.h"
+#include "hns_dsaf_mac.h"
+#include "hns_dsaf_rcb.h"
+
+#define HNS_PPE_SERVICE_NW_ENGINE_NUM DSAF_COMM_CHN
+#define HNS_PPE_DEBUG_NW_ENGINE_NUM 1
+#define HNS_PPE_COM_NUM DSAF_COMM_DEV_NUM
+
+#define PPE_COMMON_REG_OFFSET 0x70000
+#define PPE_REG_OFFSET 0x10000
+
+#define ETH_PPE_DUMP_NUM 576
+#define ETH_PPE_STATIC_NUM 12
+enum ppe_qid_mode {
+       PPE_QID_MODE0 = 0,      /* fixed queue id mode */
+       PPE_QID_MODE1,          /* switch:128VM non switch:6Port/4VM/4TC */
+       PPE_QID_MODE2,          /* switch:32VM/4TC non switch:6Port/16VM */
+       PPE_QID_MODE3,          /* switch:4TC/8TAG non switch:2Port/64VM */
+       PPE_QID_MODE4,          /* switch:8VM/16TAG non switch:2Port/16VM/4TC */
+       PPE_QID_MODE5,          /* non switch:6Port/16TAG */
+       PPE_QID_MODE6,          /* non switch:6Port/2VM/8TC */
+       PPE_QID_MODE7,          /* non switch:2Port/8VM/8TC */
+};
+
+enum ppe_port_mode {
+       PPE_MODE_GE = 0,
+       PPE_MODE_XGE,
+};
+
+enum ppe_common_mode {
+       PPE_COMMON_MODE_DEBUG = 0,
+       PPE_COMMON_MODE_SERVICE,
+       PPE_COMMON_MODE_MAX
+};
+
+struct hns_ppe_hw_stats {
+       u64 rx_pkts_from_sw;
+       u64 rx_pkts;
+       u64 rx_drop_no_bd;
+       u64 rx_alloc_buf_fail;
+       u64 rx_alloc_buf_wait;
+       u64 rx_drop_no_buf;
+       u64 rx_err_fifo_full;
+       u64 tx_bd_form_rcb;
+       u64 tx_pkts_from_rcb;
+       u64 tx_pkts;
+       u64 tx_err_fifo_empty;
+       u64 tx_err_checksum;
+};
+
+struct hns_ppe_cb {
+       struct device *dev;
+       struct hns_ppe_cb *next;        /* pointer to next ppe device */
+       struct ppe_common_cb *ppe_common_cb; /* belong to */
+       struct hns_ppe_hw_stats hw_stats;
+
+       u8 index;       /* index in a ppe common device */
+       u8 port;                         /* port id in dsaf  */
+       void __iomem *io_base;
+       int virq;
+};
+
+struct ppe_common_cb {
+       struct device *dev;
+       struct dsaf_device *dsaf_dev;
+       void __iomem *io_base;
+
+       enum ppe_common_mode ppe_mode;
+
+       u8 comm_index;   /*ppe_common index*/
+
+       u32 ppe_num;
+       struct hns_ppe_cb ppe_cb[0];
+
+};
+
+int hns_ppe_init(struct dsaf_device *dsaf_dev);
+
+void hns_ppe_uninit(struct dsaf_device *dsaf_dev);
+
+void hns_ppe_reset_common(struct dsaf_device *dsaf_dev, u8 ppe_common_index);
+
+void hns_ppe_update_stats(struct hns_ppe_cb *ppe_cb);
+
+int hns_ppe_get_sset_count(int stringset);
+int hns_ppe_get_regs_count(void);
+void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data);
+
+void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data);
+void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data);
+#endif /* _HNS_DSAF_PPE_H */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
new file mode 100644 (file)
index 0000000..4db32c6
--- /dev/null
@@ -0,0 +1,1021 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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/cdev.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <asm/cacheflush.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/spinlock.h>
+
+#include "hns_dsaf_main.h"
+#include "hns_dsaf_ppe.h"
+#include "hns_dsaf_rcb.h"
+
+#define RCB_COMMON_REG_OFFSET 0x80000
+#define TX_RING 0
+#define RX_RING 1
+
+#define RCB_RESET_WAIT_TIMES 30
+#define RCB_RESET_TRY_TIMES 10
+
+/**
+ *hns_rcb_wait_fbd_clean - clean fbd
+ *@qs: ring struct pointer array
+ *@qnum: num of array
+ *@flag: tx or rx flag
+ */
+void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag)
+{
+       int i, wait_cnt;
+       u32 fbd_num;
+
+       for (wait_cnt = i = 0; i < q_num; wait_cnt++) {
+               usleep_range(200, 300);
+               fbd_num = 0;
+               if (flag & RCB_INT_FLAG_TX)
+                       fbd_num += dsaf_read_dev(qs[i],
+                                                RCB_RING_TX_RING_FBDNUM_REG);
+               if (flag & RCB_INT_FLAG_RX)
+                       fbd_num += dsaf_read_dev(qs[i],
+                                                RCB_RING_RX_RING_FBDNUM_REG);
+               if (!fbd_num)
+                       i++;
+               if (wait_cnt >= 10000)
+                       break;
+       }
+
+       if (i < q_num)
+               dev_err(qs[i]->handle->owner_dev,
+                       "queue(%d) wait fbd(%d) clean fail!!\n", i, fbd_num);
+}
+
+/**
+ *hns_rcb_reset_ring_hw - ring reset
+ *@q: ring struct pointer
+ */
+void hns_rcb_reset_ring_hw(struct hnae_queue *q)
+{
+       u32 wait_cnt;
+       u32 try_cnt = 0;
+       u32 could_ret;
+
+       u32 tx_fbd_num;
+
+       while (try_cnt++ < RCB_RESET_TRY_TIMES) {
+               usleep_range(100, 200);
+               tx_fbd_num = dsaf_read_dev(q, RCB_RING_TX_RING_FBDNUM_REG);
+               if (tx_fbd_num)
+                       continue;
+
+               dsaf_write_dev(q, RCB_RING_PREFETCH_EN_REG, 0);
+
+               dsaf_write_dev(q, RCB_RING_T0_BE_RST, 1);
+
+               msleep(20);
+               could_ret = dsaf_read_dev(q, RCB_RING_COULD_BE_RST);
+
+               wait_cnt = 0;
+               while (!could_ret && (wait_cnt < RCB_RESET_WAIT_TIMES)) {
+                       dsaf_write_dev(q, RCB_RING_T0_BE_RST, 0);
+
+                       dsaf_write_dev(q, RCB_RING_T0_BE_RST, 1);
+
+                       msleep(20);
+                       could_ret = dsaf_read_dev(q, RCB_RING_COULD_BE_RST);
+
+                       wait_cnt++;
+               }
+
+               dsaf_write_dev(q, RCB_RING_T0_BE_RST, 0);
+
+               if (could_ret)
+                       break;
+       }
+
+       if (try_cnt >= RCB_RESET_TRY_TIMES)
+               dev_err(q->dev->dev, "port%d reset ring fail\n",
+                       hns_ae_get_vf_cb(q->handle)->port_index);
+}
+
+/**
+ *hns_rcb_int_ctrl_hw - rcb irq enable control
+ *@q: hnae queue struct pointer
+ *@flag:ring flag tx or rx
+ *@mask:mask
+ */
+void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask)
+{
+       u32 int_mask_en = !!mask;
+
+       if (flag & RCB_INT_FLAG_TX) {
+               dsaf_write_dev(q, RCB_RING_INTMSK_TXWL_REG, int_mask_en);
+               dsaf_write_dev(q, RCB_RING_INTMSK_TX_OVERTIME_REG,
+                              int_mask_en);
+       }
+
+       if (flag & RCB_INT_FLAG_RX) {
+               dsaf_write_dev(q, RCB_RING_INTMSK_RXWL_REG, int_mask_en);
+               dsaf_write_dev(q, RCB_RING_INTMSK_RX_OVERTIME_REG,
+                              int_mask_en);
+       }
+}
+
+void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag)
+{
+       u32 clr = 1;
+
+       if (flag & RCB_INT_FLAG_TX) {
+               dsaf_write_dev(q, RCB_RING_INTSTS_TX_RING_REG, clr);
+               dsaf_write_dev(q, RCB_RING_INTSTS_TX_OVERTIME_REG, clr);
+       }
+
+       if (flag & RCB_INT_FLAG_RX) {
+               dsaf_write_dev(q, RCB_RING_INTSTS_RX_RING_REG, clr);
+               dsaf_write_dev(q, RCB_RING_INTSTS_RX_OVERTIME_REG, clr);
+       }
+}
+
+/**
+ *hns_rcb_ring_enable_hw - enable ring
+ *@ring: rcb ring
+ */
+void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val)
+{
+       dsaf_write_dev(q, RCB_RING_PREFETCH_EN_REG, !!val);
+}
+
+void hns_rcb_start(struct hnae_queue *q, u32 val)
+{
+       hns_rcb_ring_enable_hw(q, val);
+}
+
+/**
+ *hns_rcb_common_init_commit_hw - make rcb common init completed
+ *@rcb_common: rcb common device
+ */
+void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common)
+{
+       wmb();  /* Sync point before breakpoint */
+       dsaf_write_dev(rcb_common, RCB_COM_CFG_SYS_FSH_REG, 1);
+       wmb();  /* Sync point after breakpoint */
+}
+
+/**
+ *hns_rcb_ring_init - init rcb ring
+ *@ring_pair: ring pair control block
+ *@ring_type: ring type, RX_RING or TX_RING
+ */
+static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type)
+{
+       struct hnae_queue *q = &ring_pair->q;
+       struct rcb_common_cb *rcb_common = ring_pair->rcb_common;
+       u32 bd_size_type = rcb_common->dsaf_dev->buf_size_type;
+       struct hnae_ring *ring =
+               (ring_type == RX_RING) ? &q->rx_ring : &q->tx_ring;
+       dma_addr_t dma = ring->desc_dma_addr;
+
+       if (ring_type == RX_RING) {
+               dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_L_REG,
+                              (u32)dma);
+               dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_H_REG,
+                              (u32)((dma >> 31) >> 1));
+               dsaf_write_dev(q, RCB_RING_RX_RING_BD_LEN_REG,
+                              bd_size_type);
+               dsaf_write_dev(q, RCB_RING_RX_RING_BD_NUM_REG,
+                              ring_pair->port_id_in_dsa);
+               dsaf_write_dev(q, RCB_RING_RX_RING_PKTLINE_REG,
+                              ring_pair->port_id_in_dsa);
+       } else {
+               dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_L_REG,
+                              (u32)dma);
+               dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_H_REG,
+                              (u32)((dma >> 31) >> 1));
+               dsaf_write_dev(q, RCB_RING_TX_RING_BD_LEN_REG,
+                              bd_size_type);
+               dsaf_write_dev(q, RCB_RING_TX_RING_BD_NUM_REG,
+                              ring_pair->port_id_in_dsa);
+               dsaf_write_dev(q, RCB_RING_TX_RING_PKTLINE_REG,
+                              ring_pair->port_id_in_dsa);
+       }
+}
+
+/**
+ *hns_rcb_init_hw - init rcb hardware
+ *@ring: rcb ring
+ */
+void hns_rcb_init_hw(struct ring_pair_cb *ring)
+{
+       hns_rcb_ring_init(ring, RX_RING);
+       hns_rcb_ring_init(ring, TX_RING);
+}
+
+/**
+ *hns_rcb_set_port_desc_cnt - set rcb port description num
+ *@rcb_common: rcb_common device
+ *@port_idx:port index
+ *@desc_cnt:BD num
+ */
+static void hns_rcb_set_port_desc_cnt(struct rcb_common_cb *rcb_common,
+                                     u32 port_idx, u32 desc_cnt)
+{
+       if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM)
+               port_idx = 0;
+
+       dsaf_write_dev(rcb_common, RCB_CFG_BD_NUM_REG + port_idx * 4,
+                      desc_cnt);
+}
+
+/**
+ *hns_rcb_set_port_coalesced_frames - set rcb port coalesced frames
+ *@rcb_common: rcb_common device
+ *@port_idx:port index
+ *@coalesced_frames:BD num for coalesced frames
+ */
+static int  hns_rcb_set_port_coalesced_frames(struct rcb_common_cb *rcb_common,
+                                             u32 port_idx,
+                                             u32 coalesced_frames)
+{
+       if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM)
+               port_idx = 0;
+       if (coalesced_frames >= rcb_common->desc_num ||
+           coalesced_frames > HNS_RCB_MAX_COALESCED_FRAMES)
+               return -EINVAL;
+
+       dsaf_write_dev(rcb_common, RCB_CFG_PKTLINE_REG + port_idx * 4,
+                      coalesced_frames);
+       return 0;
+}
+
+/**
+ *hns_rcb_get_port_coalesced_frames - set rcb port coalesced frames
+ *@rcb_common: rcb_common device
+ *@port_idx:port index
+ * return coaleseced frames value
+ */
+static u32 hns_rcb_get_port_coalesced_frames(struct rcb_common_cb *rcb_common,
+                                            u32 port_idx)
+{
+       if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM)
+               port_idx = 0;
+
+       return dsaf_read_dev(rcb_common,
+                            RCB_CFG_PKTLINE_REG + port_idx * 4);
+}
+
+/**
+ *hns_rcb_set_timeout - set rcb port coalesced time_out
+ *@rcb_common: rcb_common device
+ *@time_out:time for coalesced time_out
+ */
+static void hns_rcb_set_timeout(struct rcb_common_cb *rcb_common,
+                               u32 timeout)
+{
+       dsaf_write_dev(rcb_common, RCB_CFG_OVERTIME_REG, timeout);
+}
+
+static int hns_rcb_common_get_port_num(struct rcb_common_cb *rcb_common)
+{
+       if (rcb_common->comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX)
+               return HNS_RCB_SERVICE_NW_ENGINE_NUM;
+       else
+               return HNS_RCB_DEBUG_NW_ENGINE_NUM;
+}
+
+/*clr rcb comm exception irq**/
+static void hns_rcb_comm_exc_irq_en(
+                       struct rcb_common_cb *rcb_common, int en)
+{
+       u32 clr_vlue = 0xfffffffful;
+       u32 msk_vlue = en ? 0 : 0xfffffffful;
+
+       /* clr int*/
+       dsaf_write_dev(rcb_common, RCB_COM_INTSTS_ECC_ERR_REG, clr_vlue);
+
+       dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_RING_STS, clr_vlue);
+
+       dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_BD_RINT_STS, clr_vlue);
+
+       dsaf_write_dev(rcb_common, RCB_COM_RINT_TX_PKT_REG, clr_vlue);
+       dsaf_write_dev(rcb_common, RCB_COM_AXI_ERR_STS, clr_vlue);
+
+       /*en msk*/
+       dsaf_write_dev(rcb_common, RCB_COM_INTMASK_ECC_ERR_REG, msk_vlue);
+
+       dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_INTMASK_RING, msk_vlue);
+
+       /*for tx bd neednot cacheline, so msk sf_txring_fbd_intmask (bit 1)**/
+       dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_INTMASK_BD, msk_vlue | 2);
+
+       dsaf_write_dev(rcb_common, RCB_COM_INTMSK_TX_PKT_REG, msk_vlue);
+       dsaf_write_dev(rcb_common, RCB_COM_AXI_WR_ERR_INTMASK, msk_vlue);
+}
+
+/**
+ *hns_rcb_common_init_hw - init rcb common hardware
+ *@rcb_common: rcb_common device
+ *retuen 0 - success , negative --fail
+ */
+int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common)
+{
+       u32 reg_val;
+       int i;
+       int port_num = hns_rcb_common_get_port_num(rcb_common);
+
+       hns_rcb_comm_exc_irq_en(rcb_common, 0);
+
+       reg_val = dsaf_read_dev(rcb_common, RCB_COM_CFG_INIT_FLAG_REG);
+       if (0x1 != (reg_val & 0x1)) {
+               dev_err(rcb_common->dsaf_dev->dev,
+                       "RCB_COM_CFG_INIT_FLAG_REG reg = 0x%x\n", reg_val);
+               return -EBUSY;
+       }
+
+       for (i = 0; i < port_num; i++) {
+               hns_rcb_set_port_desc_cnt(rcb_common, i, rcb_common->desc_num);
+               (void)hns_rcb_set_port_coalesced_frames(
+                       rcb_common, i, rcb_common->coalesced_frames);
+       }
+       hns_rcb_set_timeout(rcb_common, rcb_common->timeout);
+
+       dsaf_write_dev(rcb_common, RCB_COM_CFG_ENDIAN_REG,
+                      HNS_RCB_COMMON_ENDIAN);
+
+       return 0;
+}
+
+int hns_rcb_buf_size2type(u32 buf_size)
+{
+       int bd_size_type;
+
+       switch (buf_size) {
+       case 512:
+               bd_size_type = HNS_BD_SIZE_512_TYPE;
+               break;
+       case 1024:
+               bd_size_type = HNS_BD_SIZE_1024_TYPE;
+               break;
+       case 2048:
+               bd_size_type = HNS_BD_SIZE_2048_TYPE;
+               break;
+       case 4096:
+               bd_size_type = HNS_BD_SIZE_4096_TYPE;
+               break;
+       default:
+               bd_size_type = -EINVAL;
+       }
+
+       return bd_size_type;
+}
+
+static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type)
+{
+       struct hnae_ring *ring;
+       struct rcb_common_cb *rcb_common;
+       struct ring_pair_cb *ring_pair_cb;
+       u32 buf_size;
+       u16 desc_num;
+       int irq_idx;
+
+       ring_pair_cb = container_of(q, struct ring_pair_cb, q);
+       if (ring_type == RX_RING) {
+               ring = &q->rx_ring;
+               ring->io_base = ring_pair_cb->q.io_base;
+               irq_idx = HNS_RCB_IRQ_IDX_RX;
+       } else {
+               ring = &q->tx_ring;
+               ring->io_base = (u8 __iomem *)ring_pair_cb->q.io_base +
+                       HNS_RCB_TX_REG_OFFSET;
+               irq_idx = HNS_RCB_IRQ_IDX_TX;
+       }
+
+       rcb_common = ring_pair_cb->rcb_common;
+       buf_size = rcb_common->dsaf_dev->buf_size;
+       desc_num = rcb_common->dsaf_dev->desc_num;
+
+       ring->desc = NULL;
+       ring->desc_cb = NULL;
+
+       ring->irq = ring_pair_cb->virq[irq_idx];
+       ring->desc_dma_addr = 0;
+
+       ring->buf_size = buf_size;
+       ring->desc_num = desc_num;
+       ring->max_desc_num_per_pkt = HNS_RCB_RING_MAX_BD_PER_PKT;
+       ring->max_raw_data_sz_per_desc = HNS_RCB_MAX_PKT_SIZE;
+       ring->max_pkt_size = HNS_RCB_MAX_PKT_SIZE;
+       ring->next_to_use = 0;
+       ring->next_to_clean = 0;
+}
+
+static void hns_rcb_ring_pair_get_cfg(struct ring_pair_cb *ring_pair_cb)
+{
+       ring_pair_cb->q.handle = NULL;
+
+       hns_rcb_ring_get_cfg(&ring_pair_cb->q, RX_RING);
+       hns_rcb_ring_get_cfg(&ring_pair_cb->q, TX_RING);
+}
+
+static int hns_rcb_get_port(struct rcb_common_cb *rcb_common, int ring_idx)
+{
+       int comm_index = rcb_common->comm_index;
+       int port;
+       int q_num;
+
+       if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) {
+               q_num = (int)rcb_common->max_q_per_vf * rcb_common->max_vfn;
+               port = ring_idx / q_num;
+       } else {
+               port = HNS_RCB_SERVICE_NW_ENGINE_NUM + comm_index - 1;
+       }
+
+       return port;
+}
+
+static int hns_rcb_get_base_irq_idx(struct rcb_common_cb *rcb_common)
+{
+       int comm_index = rcb_common->comm_index;
+
+       if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX)
+               return HNS_SERVICE_RING_IRQ_IDX;
+       else
+               return HNS_DEBUG_RING_IRQ_IDX + (comm_index - 1) * 2;
+}
+
+#define RCB_COMM_BASE_TO_RING_BASE(base, ringid)\
+       ((base) + 0x10000 + HNS_RCB_REG_OFFSET * (ringid))
+/**
+ *hns_rcb_get_cfg - get rcb config
+ *@rcb_common: rcb common device
+ */
+void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common)
+{
+       struct ring_pair_cb *ring_pair_cb;
+       u32 i;
+       u32 ring_num = rcb_common->ring_num;
+       int base_irq_idx = hns_rcb_get_base_irq_idx(rcb_common);
+       struct device_node *np = rcb_common->dsaf_dev->dev->of_node;
+
+       for (i = 0; i < ring_num; i++) {
+               ring_pair_cb = &rcb_common->ring_pair_cb[i];
+               ring_pair_cb->rcb_common = rcb_common;
+               ring_pair_cb->dev = rcb_common->dsaf_dev->dev;
+               ring_pair_cb->index = i;
+               ring_pair_cb->q.io_base =
+                       RCB_COMM_BASE_TO_RING_BASE(rcb_common->io_base, i);
+               ring_pair_cb->port_id_in_dsa = hns_rcb_get_port(rcb_common, i);
+               ring_pair_cb->virq[HNS_RCB_IRQ_IDX_TX]
+                       = irq_of_parse_and_map(np, base_irq_idx + i * 2);
+               ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX]
+                       = irq_of_parse_and_map(np, base_irq_idx + i * 2 + 1);
+               ring_pair_cb->q.phy_base =
+                       RCB_COMM_BASE_TO_RING_BASE(rcb_common->phy_base, i);
+               hns_rcb_ring_pair_get_cfg(ring_pair_cb);
+       }
+}
+
+/**
+ *hns_rcb_get_coalesced_frames - get rcb port coalesced frames
+ *@rcb_common: rcb_common device
+ *@comm_index:port index
+ *return coalesced_frames
+ */
+u32 hns_rcb_get_coalesced_frames(struct dsaf_device *dsaf_dev, int port)
+{
+       int comm_index =  hns_dsaf_get_comm_idx_by_port(port);
+       struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index];
+
+       return hns_rcb_get_port_coalesced_frames(rcb_comm, port);
+}
+
+/**
+ *hns_rcb_get_coalesce_usecs - get rcb port coalesced time_out
+ *@rcb_common: rcb_common device
+ *@comm_index:port index
+ *return time_out
+ */
+u32 hns_rcb_get_coalesce_usecs(struct dsaf_device *dsaf_dev, int comm_index)
+{
+       struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index];
+
+       return rcb_comm->timeout;
+}
+
+/**
+ *hns_rcb_set_coalesce_usecs - set rcb port coalesced time_out
+ *@rcb_common: rcb_common device
+ *@comm_index: comm :index
+ *@etx_usecs:tx time for coalesced time_out
+ *@rx_usecs:rx time for coalesced time_out
+ */
+void hns_rcb_set_coalesce_usecs(struct dsaf_device *dsaf_dev,
+                               int port, u32 timeout)
+{
+       int comm_index =  hns_dsaf_get_comm_idx_by_port(port);
+       struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index];
+
+       if (rcb_comm->timeout == timeout)
+               return;
+
+       if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) {
+               dev_err(dsaf_dev->dev,
+                       "error: not support coalesce_usecs setting!\n");
+               return;
+       }
+       rcb_comm->timeout = timeout;
+       hns_rcb_set_timeout(rcb_comm, rcb_comm->timeout);
+}
+
+/**
+ *hns_rcb_set_coalesced_frames - set rcb coalesced frames
+ *@rcb_common: rcb_common device
+ *@tx_frames:tx BD num for coalesced frames
+ *@rx_frames:rx BD num for coalesced frames
+ *Return 0 on success, negative on failure
+ */
+int hns_rcb_set_coalesced_frames(struct dsaf_device *dsaf_dev,
+                                int port, u32 coalesced_frames)
+{
+       int comm_index =  hns_dsaf_get_comm_idx_by_port(port);
+       struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index];
+       u32 coalesced_reg_val;
+       int ret;
+
+       coalesced_reg_val = hns_rcb_get_port_coalesced_frames(rcb_comm, port);
+
+       if (coalesced_reg_val == coalesced_frames)
+               return 0;
+
+       if (coalesced_frames >= HNS_RCB_MIN_COALESCED_FRAMES) {
+               ret = hns_rcb_set_port_coalesced_frames(rcb_comm, port,
+                                                       coalesced_frames);
+               return ret;
+       } else {
+               return -EINVAL;
+       }
+}
+
+/**
+ *hns_rcb_get_queue_mode - get max VM number and max ring number per VM
+ *                                             accordding to dsaf mode
+ *@dsaf_mode: dsaf mode
+ *@max_vfn : max vfn number
+ *@max_q_per_vf:max ring number per vm
+ */
+void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index,
+                           u16 *max_vfn, u16 *max_q_per_vf)
+{
+       if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) {
+               switch (dsaf_mode) {
+               case DSAF_MODE_DISABLE_6PORT_0VM:
+                       *max_vfn = 1;
+                       *max_q_per_vf = 16;
+                       break;
+               case DSAF_MODE_DISABLE_FIX:
+                       *max_vfn = 1;
+                       *max_q_per_vf = 1;
+                       break;
+               case DSAF_MODE_DISABLE_2PORT_64VM:
+                       *max_vfn = 64;
+                       *max_q_per_vf = 1;
+                       break;
+               case DSAF_MODE_DISABLE_6PORT_16VM:
+                       *max_vfn = 16;
+                       *max_q_per_vf = 1;
+                       break;
+               default:
+                       *max_vfn = 1;
+                       *max_q_per_vf = 16;
+                       break;
+               }
+       } else {
+               *max_vfn = 1;
+               *max_q_per_vf = 1;
+       }
+}
+
+int hns_rcb_get_ring_num(struct dsaf_device *dsaf_dev, int comm_index)
+{
+       if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) {
+               switch (dsaf_dev->dsaf_mode) {
+               case DSAF_MODE_ENABLE_FIX:
+                       return 1;
+
+               case DSAF_MODE_DISABLE_FIX:
+                       return 6;
+
+               case DSAF_MODE_ENABLE_0VM:
+                       return 32;
+
+               case DSAF_MODE_DISABLE_6PORT_0VM:
+               case DSAF_MODE_ENABLE_16VM:
+               case DSAF_MODE_DISABLE_6PORT_2VM:
+               case DSAF_MODE_DISABLE_6PORT_16VM:
+               case DSAF_MODE_DISABLE_6PORT_4VM:
+               case DSAF_MODE_ENABLE_8VM:
+                       return 96;
+
+               case DSAF_MODE_DISABLE_2PORT_16VM:
+               case DSAF_MODE_DISABLE_2PORT_8VM:
+               case DSAF_MODE_ENABLE_32VM:
+               case DSAF_MODE_DISABLE_2PORT_64VM:
+               case DSAF_MODE_ENABLE_128VM:
+                       return 128;
+
+               default:
+                       dev_warn(dsaf_dev->dev,
+                                "get ring num fail,use default!dsaf_mode=%d\n",
+                                dsaf_dev->dsaf_mode);
+                       return 128;
+               }
+       } else {
+               return 1;
+       }
+}
+
+void __iomem *hns_rcb_common_get_vaddr(struct dsaf_device *dsaf_dev,
+                                      int comm_index)
+{
+       void __iomem *base_addr;
+
+       if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX)
+               base_addr = dsaf_dev->ppe_base + RCB_COMMON_REG_OFFSET;
+       else
+               base_addr = dsaf_dev->sds_base
+                       + (comm_index - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET
+                       + RCB_COMMON_REG_OFFSET;
+
+       return base_addr;
+}
+
+static phys_addr_t hns_rcb_common_get_paddr(struct dsaf_device *dsaf_dev,
+                                           int comm_index)
+{
+       struct device_node *np = dsaf_dev->dev->of_node;
+       phys_addr_t phy_addr;
+       const __be32 *tmp_addr;
+       u64 addr_offset = 0;
+       u64 size = 0;
+       int index = 0;
+
+       if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) {
+               index    = 2;
+               addr_offset = RCB_COMMON_REG_OFFSET;
+       } else {
+               index    = 1;
+               addr_offset = (comm_index - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET +
+                               RCB_COMMON_REG_OFFSET;
+       }
+       tmp_addr  = of_get_address(np, index, &size, NULL);
+       phy_addr  = of_translate_address(np, tmp_addr);
+       return phy_addr + addr_offset;
+}
+
+int hns_rcb_common_get_cfg(struct dsaf_device *dsaf_dev,
+                          int comm_index)
+{
+       struct rcb_common_cb *rcb_common;
+       enum dsaf_mode dsaf_mode = dsaf_dev->dsaf_mode;
+       u16 max_vfn;
+       u16 max_q_per_vf;
+       int ring_num = hns_rcb_get_ring_num(dsaf_dev, comm_index);
+
+       rcb_common =
+               devm_kzalloc(dsaf_dev->dev, sizeof(*rcb_common) +
+                       ring_num * sizeof(struct ring_pair_cb), GFP_KERNEL);
+       if (!rcb_common) {
+               dev_err(dsaf_dev->dev, "rcb common devm_kzalloc fail!\n");
+               return -ENOMEM;
+       }
+       rcb_common->comm_index = comm_index;
+       rcb_common->ring_num = ring_num;
+       rcb_common->dsaf_dev = dsaf_dev;
+
+       rcb_common->desc_num = dsaf_dev->desc_num;
+       rcb_common->coalesced_frames = HNS_RCB_DEF_COALESCED_FRAMES;
+       rcb_common->timeout = HNS_RCB_MAX_TIME_OUT;
+
+       hns_rcb_get_queue_mode(dsaf_mode, comm_index, &max_vfn, &max_q_per_vf);
+       rcb_common->max_vfn = max_vfn;
+       rcb_common->max_q_per_vf = max_q_per_vf;
+
+       rcb_common->io_base = hns_rcb_common_get_vaddr(dsaf_dev, comm_index);
+       rcb_common->phy_base = hns_rcb_common_get_paddr(dsaf_dev, comm_index);
+
+       dsaf_dev->rcb_common[comm_index] = rcb_common;
+       return 0;
+}
+
+void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev,
+                            u32 comm_index)
+{
+       dsaf_dev->rcb_common[comm_index] = NULL;
+}
+
+void hns_rcb_update_stats(struct hnae_queue *queue)
+{
+       struct ring_pair_cb *ring =
+               container_of(queue, struct ring_pair_cb, q);
+       struct dsaf_device *dsaf_dev = ring->rcb_common->dsaf_dev;
+       struct ppe_common_cb *ppe_common
+               = dsaf_dev->ppe_common[ring->rcb_common->comm_index];
+       struct hns_ring_hw_stats *hw_stats = &ring->hw_stats;
+
+       hw_stats->rx_pkts += dsaf_read_dev(queue,
+                        RCB_RING_RX_RING_PKTNUM_RECORD_REG);
+       dsaf_write_dev(queue, RCB_RING_RX_RING_PKTNUM_RECORD_REG, 0x1);
+
+       hw_stats->ppe_rx_ok_pkts += dsaf_read_dev(ppe_common,
+                        PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG + 4 * ring->index);
+       hw_stats->ppe_rx_drop_pkts += dsaf_read_dev(ppe_common,
+                        PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG + 4 * ring->index);
+
+       hw_stats->tx_pkts += dsaf_read_dev(queue,
+                        RCB_RING_TX_RING_PKTNUM_RECORD_REG);
+       dsaf_write_dev(queue, RCB_RING_TX_RING_PKTNUM_RECORD_REG, 0x1);
+
+       hw_stats->ppe_tx_ok_pkts += dsaf_read_dev(ppe_common,
+                        PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG + 4 * ring->index);
+       hw_stats->ppe_tx_drop_pkts += dsaf_read_dev(ppe_common,
+                        PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG + 4 * ring->index);
+}
+
+/**
+ *hns_rcb_get_stats - get rcb statistic
+ *@ring: rcb ring
+ *@data:statistic value
+ */
+void hns_rcb_get_stats(struct hnae_queue *queue, u64 *data)
+{
+       u64 *regs_buff = data;
+       struct ring_pair_cb *ring =
+               container_of(queue, struct ring_pair_cb, q);
+       struct hns_ring_hw_stats *hw_stats = &ring->hw_stats;
+
+       regs_buff[0] = hw_stats->tx_pkts;
+       regs_buff[1] = hw_stats->ppe_tx_ok_pkts;
+       regs_buff[2] = hw_stats->ppe_tx_drop_pkts;
+       regs_buff[3] =
+               dsaf_read_dev(queue, RCB_RING_TX_RING_FBDNUM_REG);
+
+       regs_buff[4] = queue->tx_ring.stats.tx_pkts;
+       regs_buff[5] = queue->tx_ring.stats.tx_bytes;
+       regs_buff[6] = queue->tx_ring.stats.tx_err_cnt;
+       regs_buff[7] = queue->tx_ring.stats.io_err_cnt;
+       regs_buff[8] = queue->tx_ring.stats.sw_err_cnt;
+       regs_buff[9] = queue->tx_ring.stats.seg_pkt_cnt;
+       regs_buff[10] = queue->tx_ring.stats.restart_queue;
+       regs_buff[11] = queue->tx_ring.stats.tx_busy;
+
+       regs_buff[12] = hw_stats->rx_pkts;
+       regs_buff[13] = hw_stats->ppe_rx_ok_pkts;
+       regs_buff[14] = hw_stats->ppe_rx_drop_pkts;
+       regs_buff[15] =
+               dsaf_read_dev(queue, RCB_RING_RX_RING_FBDNUM_REG);
+
+       regs_buff[16] = queue->rx_ring.stats.rx_pkts;
+       regs_buff[17] = queue->rx_ring.stats.rx_bytes;
+       regs_buff[18] = queue->rx_ring.stats.rx_err_cnt;
+       regs_buff[19] = queue->rx_ring.stats.io_err_cnt;
+       regs_buff[20] = queue->rx_ring.stats.sw_err_cnt;
+       regs_buff[21] = queue->rx_ring.stats.seg_pkt_cnt;
+       regs_buff[22] = queue->rx_ring.stats.reuse_pg_cnt;
+       regs_buff[23] = queue->rx_ring.stats.err_pkt_len;
+       regs_buff[24] = queue->rx_ring.stats.non_vld_descs;
+       regs_buff[25] = queue->rx_ring.stats.err_bd_num;
+       regs_buff[26] = queue->rx_ring.stats.l2_err;
+       regs_buff[27] = queue->rx_ring.stats.l3l4_csum_err;
+}
+
+/**
+ *hns_rcb_get_ring_sset_count - rcb string set count
+ *@stringset:ethtool cmd
+ *return rcb ring string set count
+ */
+int hns_rcb_get_ring_sset_count(int stringset)
+{
+       if (stringset == ETH_SS_STATS)
+               return HNS_RING_STATIC_REG_NUM;
+
+       return 0;
+}
+
+/**
+ *hns_rcb_get_common_regs_count - rcb common regs count
+ *return regs count
+ */
+int hns_rcb_get_common_regs_count(void)
+{
+       return HNS_RCB_COMMON_DUMP_REG_NUM;
+}
+
+/**
+ *rcb_get_sset_count - rcb ring regs count
+ *return regs count
+ */
+int hns_rcb_get_ring_regs_count(void)
+{
+       return HNS_RCB_RING_DUMP_REG_NUM;
+}
+
+/**
+ *hns_rcb_get_strings - get rcb string set
+ *@stringset:string set index
+ *@data:strings name value
+ *@index:queue index
+ */
+void hns_rcb_get_strings(int stringset, u8 *data, int index)
+{
+       char *buff = (char *)data;
+
+       if (stringset != ETH_SS_STATS)
+               return;
+
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_rcb_pkt_num", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_ppe_tx_pkt_num", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_ppe_drop_pkt_num", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_fbd_num", index);
+       buff = buff + ETH_GSTRING_LEN;
+
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_pkt_num", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_bytes", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_err_cnt", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_io_err", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_sw_err", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_seg_pkt", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_restart_queue", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_tx_busy", index);
+       buff = buff + ETH_GSTRING_LEN;
+
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_rcb_pkt_num", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_ppe_pkt_num", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_ppe_drop_pkt_num", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_fbd_num", index);
+       buff = buff + ETH_GSTRING_LEN;
+
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_pkt_num", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_bytes", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_err_cnt", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_io_err", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_sw_err", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_seg_pkt", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_reuse_pg", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_len_err", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_non_vld_desc_err", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_bd_num_err", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_l2_err", index);
+       buff = buff + ETH_GSTRING_LEN;
+       snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_l3l4csum_err", index);
+}
+
+void hns_rcb_get_common_regs(struct rcb_common_cb *rcb_com, void *data)
+{
+       u32 *regs = data;
+       u32 i = 0;
+
+       /*rcb common registers */
+       regs[0] = dsaf_read_dev(rcb_com, RCB_COM_CFG_ENDIAN_REG);
+       regs[1] = dsaf_read_dev(rcb_com, RCB_COM_CFG_SYS_FSH_REG);
+       regs[2] = dsaf_read_dev(rcb_com, RCB_COM_CFG_INIT_FLAG_REG);
+
+       regs[3] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PKT_REG);
+       regs[4] = dsaf_read_dev(rcb_com, RCB_COM_CFG_RINVLD_REG);
+       regs[5] = dsaf_read_dev(rcb_com, RCB_COM_CFG_FNA_REG);
+       regs[6] = dsaf_read_dev(rcb_com, RCB_COM_CFG_FA_REG);
+       regs[7] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PKT_TC_BP_REG);
+       regs[8] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PPE_TNL_CLKEN_REG);
+
+       regs[9] = dsaf_read_dev(rcb_com, RCB_COM_INTMSK_TX_PKT_REG);
+       regs[10] = dsaf_read_dev(rcb_com, RCB_COM_RINT_TX_PKT_REG);
+       regs[11] = dsaf_read_dev(rcb_com, RCB_COM_INTMASK_ECC_ERR_REG);
+       regs[12] = dsaf_read_dev(rcb_com, RCB_COM_INTSTS_ECC_ERR_REG);
+       regs[13] = dsaf_read_dev(rcb_com, RCB_COM_EBD_SRAM_ERR_REG);
+       regs[14] = dsaf_read_dev(rcb_com, RCB_COM_RXRING_ERR_REG);
+       regs[15] = dsaf_read_dev(rcb_com, RCB_COM_TXRING_ERR_REG);
+       regs[16] = dsaf_read_dev(rcb_com, RCB_COM_TX_FBD_ERR_REG);
+       regs[17] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK_EN_REG);
+       regs[18] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK0_REG);
+       regs[19] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK1_REG);
+       regs[20] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK2_REG);
+       regs[21] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK3_REG);
+       regs[22] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK4_REG);
+       regs[23] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK5_REG);
+       regs[24] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR0_REG);
+       regs[25] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR3_REG);
+       regs[26] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR4_REG);
+       regs[27] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR5_REG);
+
+       regs[28] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_INTMASK_RING);
+       regs[29] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_RING_STS);
+       regs[30] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_RING);
+       regs[31] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_INTMASK_BD);
+       regs[32] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_BD_RINT_STS);
+       regs[33] = dsaf_read_dev(rcb_com, RCB_COM_RCB_RD_BD_BUSY);
+       regs[34] = dsaf_read_dev(rcb_com, RCB_COM_RCB_FBD_CRT_EN);
+       regs[35] = dsaf_read_dev(rcb_com, RCB_COM_AXI_WR_ERR_INTMASK);
+       regs[36] = dsaf_read_dev(rcb_com, RCB_COM_AXI_ERR_STS);
+       regs[37] = dsaf_read_dev(rcb_com, RCB_COM_CHK_TX_FBD_NUM_REG);
+
+       /* rcb common entry registers */
+       for (i = 0; i < 16; i++) { /* total 16 model registers */
+               regs[38 + i]
+                       = dsaf_read_dev(rcb_com, RCB_CFG_BD_NUM_REG + 4 * i);
+               regs[54 + i]
+                       = dsaf_read_dev(rcb_com, RCB_CFG_PKTLINE_REG + 4 * i);
+       }
+
+       regs[70] = dsaf_read_dev(rcb_com, RCB_CFG_OVERTIME_REG);
+       regs[71] = dsaf_read_dev(rcb_com, RCB_CFG_PKTLINE_INT_NUM_REG);
+       regs[72] = dsaf_read_dev(rcb_com, RCB_CFG_OVERTIME_INT_NUM_REG);
+
+       /* mark end of rcb common regs */
+       for (i = 73; i < 80; i++)
+               regs[i] = 0xcccccccc;
+}
+
+void hns_rcb_get_ring_regs(struct hnae_queue *queue, void *data)
+{
+       u32 *regs = data;
+       struct ring_pair_cb *ring_pair
+               = container_of(queue, struct ring_pair_cb, q);
+       u32 i = 0;
+
+       /*rcb ring registers */
+       regs[0] = dsaf_read_dev(queue, RCB_RING_RX_RING_BASEADDR_L_REG);
+       regs[1] = dsaf_read_dev(queue, RCB_RING_RX_RING_BASEADDR_H_REG);
+       regs[2] = dsaf_read_dev(queue, RCB_RING_RX_RING_BD_NUM_REG);
+       regs[3] = dsaf_read_dev(queue, RCB_RING_RX_RING_BD_LEN_REG);
+       regs[4] = dsaf_read_dev(queue, RCB_RING_RX_RING_PKTLINE_REG);
+       regs[5] = dsaf_read_dev(queue, RCB_RING_RX_RING_TAIL_REG);
+       regs[6] = dsaf_read_dev(queue, RCB_RING_RX_RING_HEAD_REG);
+       regs[7] = dsaf_read_dev(queue, RCB_RING_RX_RING_FBDNUM_REG);
+       regs[8] = dsaf_read_dev(queue, RCB_RING_RX_RING_PKTNUM_RECORD_REG);
+
+       regs[9] = dsaf_read_dev(queue, RCB_RING_TX_RING_BASEADDR_L_REG);
+       regs[10] = dsaf_read_dev(queue, RCB_RING_TX_RING_BASEADDR_H_REG);
+       regs[11] = dsaf_read_dev(queue, RCB_RING_TX_RING_BD_NUM_REG);
+       regs[12] = dsaf_read_dev(queue, RCB_RING_TX_RING_BD_LEN_REG);
+       regs[13] = dsaf_read_dev(queue, RCB_RING_TX_RING_PKTLINE_REG);
+       regs[15] = dsaf_read_dev(queue, RCB_RING_TX_RING_TAIL_REG);
+       regs[16] = dsaf_read_dev(queue, RCB_RING_TX_RING_HEAD_REG);
+       regs[17] = dsaf_read_dev(queue, RCB_RING_TX_RING_FBDNUM_REG);
+       regs[18] = dsaf_read_dev(queue, RCB_RING_TX_RING_OFFSET_REG);
+       regs[19] = dsaf_read_dev(queue, RCB_RING_TX_RING_PKTNUM_RECORD_REG);
+
+       regs[20] = dsaf_read_dev(queue, RCB_RING_PREFETCH_EN_REG);
+       regs[21] = dsaf_read_dev(queue, RCB_RING_CFG_VF_NUM_REG);
+       regs[22] = dsaf_read_dev(queue, RCB_RING_ASID_REG);
+       regs[23] = dsaf_read_dev(queue, RCB_RING_RX_VM_REG);
+       regs[24] = dsaf_read_dev(queue, RCB_RING_T0_BE_RST);
+       regs[25] = dsaf_read_dev(queue, RCB_RING_COULD_BE_RST);
+       regs[26] = dsaf_read_dev(queue, RCB_RING_WRR_WEIGHT_REG);
+
+       regs[27] = dsaf_read_dev(queue, RCB_RING_INTMSK_RXWL_REG);
+       regs[28] = dsaf_read_dev(queue, RCB_RING_INTSTS_RX_RING_REG);
+       regs[29] = dsaf_read_dev(queue, RCB_RING_INTMSK_TXWL_REG);
+       regs[30] = dsaf_read_dev(queue, RCB_RING_INTSTS_TX_RING_REG);
+       regs[31] = dsaf_read_dev(queue, RCB_RING_INTMSK_RX_OVERTIME_REG);
+       regs[32] = dsaf_read_dev(queue, RCB_RING_INTSTS_RX_OVERTIME_REG);
+       regs[33] = dsaf_read_dev(queue, RCB_RING_INTMSK_TX_OVERTIME_REG);
+       regs[34] = dsaf_read_dev(queue, RCB_RING_INTSTS_TX_OVERTIME_REG);
+
+       /* mark end of ring regs */
+       for (i = 35; i < 40; i++)
+               regs[i] = 0xcccccc00 + ring_pair->index;
+}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h
new file mode 100644 (file)
index 0000000..3a2afe2
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 _HNS_DSAF_RCB_H
+#define _HNS_DSAF_RCB_H
+
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+
+#include "hnae.h"
+#include "hns_dsaf_main.h"
+
+struct rcb_common_cb;
+
+#define HNS_RCB_IRQ_NUM_PER_QUEUE              2
+#define HNS_RCB_IRQ_IDX_TX                     0
+#define HNS_RCB_IRQ_IDX_RX                     1
+#define HNS_RCB_TX_REG_OFFSET                  0x40
+
+#define HNS_RCB_SERVICE_NW_ENGINE_NUM          DSAF_COMM_CHN
+#define HNS_RCB_DEBUG_NW_ENGINE_NUM            1
+#define HNS_RCB_RING_MAX_BD_PER_PKT            3
+#define HNS_RCB_MAX_PKT_SIZE MAC_MAX_MTU
+
+#define HNS_RCB_RING_MAX_PENDING_BD            1024
+#define HNS_RCB_RING_MIN_PENDING_BD            16
+
+#define HNS_RCB_REG_OFFSET                     0x10000
+
+#define HNS_RCB_MAX_COALESCED_FRAMES           1023
+#define HNS_RCB_MIN_COALESCED_FRAMES           1
+#define HNS_RCB_DEF_COALESCED_FRAMES           50
+#define HNS_RCB_MAX_TIME_OUT                   0x500
+
+#define HNS_RCB_COMMON_ENDIAN                  1
+
+#define HNS_BD_SIZE_512_TYPE                   0
+#define HNS_BD_SIZE_1024_TYPE                  1
+#define HNS_BD_SIZE_2048_TYPE                  2
+#define HNS_BD_SIZE_4096_TYPE                  3
+
+#define HNS_RCB_COMMON_DUMP_REG_NUM 80
+#define HNS_RCB_RING_DUMP_REG_NUM 40
+#define HNS_RING_STATIC_REG_NUM 28
+
+#define HNS_DUMP_REG_NUM                       500
+#define HNS_STATIC_REG_NUM                     12
+
+enum rcb_int_flag {
+       RCB_INT_FLAG_TX = 0x1,
+       RCB_INT_FLAG_RX = (0x1 << 1),
+       RCB_INT_FLAG_MAX = (0x1 << 2),  /*must be the last element */
+};
+
+struct hns_ring_hw_stats {
+       u64 tx_pkts;
+       u64 ppe_tx_ok_pkts;
+       u64 ppe_tx_drop_pkts;
+       u64 rx_pkts;
+       u64 ppe_rx_ok_pkts;
+       u64 ppe_rx_drop_pkts;
+};
+
+struct ring_pair_cb {
+       struct rcb_common_cb *rcb_common;       /*  ring belongs to */
+       struct device *dev;     /*device for DMA mapping */
+       struct hnae_queue q;
+
+       u16 index;      /* global index in a rcb common device */
+       u16 buf_size;
+
+       int virq[HNS_RCB_IRQ_NUM_PER_QUEUE];
+
+       u8 port_id_in_dsa;
+       u8 used_by_vf;
+
+       struct hns_ring_hw_stats hw_stats;
+};
+
+struct rcb_common_cb {
+       u8 __iomem *io_base;
+       phys_addr_t phy_base;
+       struct dsaf_device *dsaf_dev;
+       u16 max_vfn;
+       u16 max_q_per_vf;
+
+       u8 comm_index;
+       u32 ring_num;
+       u32 coalesced_frames; /* frames  threshold of  rx interrupt   */
+       u32 timeout; /* time threshold of  rx interrupt  */
+       u32 desc_num; /*  desc num per queue*/
+
+       struct ring_pair_cb ring_pair_cb[0];
+};
+
+int hns_rcb_buf_size2type(u32 buf_size);
+
+int hns_rcb_common_get_cfg(struct dsaf_device *dsaf_dev, int comm_index);
+void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index);
+int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common);
+void hns_rcb_start(struct hnae_queue *q, u32 val);
+void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common);
+void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common);
+void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index,
+                           u16 *max_vfn, u16 *max_q_per_vf);
+
+void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val);
+void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag);
+void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 enable);
+void hns_rcb_init_hw(struct ring_pair_cb *ring);
+void hns_rcb_reset_ring_hw(struct hnae_queue *q);
+void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag);
+
+u32 hns_rcb_get_coalesced_frames(struct dsaf_device *dsaf_dev, int comm_index);
+u32 hns_rcb_get_coalesce_usecs(struct dsaf_device *dsaf_dev, int comm_index);
+void hns_rcb_set_coalesce_usecs(struct dsaf_device *dsaf_dev,
+                               int comm_index, u32 timeout);
+int hns_rcb_set_coalesced_frames(struct dsaf_device *dsaf_dev,
+                                int comm_index, u32 coalesce_frames);
+void hns_rcb_update_stats(struct hnae_queue *queue);
+
+void hns_rcb_get_stats(struct hnae_queue *queue, u64 *data);
+
+void hns_rcb_get_common_regs(struct rcb_common_cb *rcb_common, void *data);
+
+int hns_rcb_get_ring_sset_count(int stringset);
+int hns_rcb_get_common_regs_count(void);
+int hns_rcb_get_ring_regs_count(void);
+
+void hns_rcb_get_ring_regs(struct hnae_queue *queue, void *data);
+
+void hns_rcb_get_strings(int stringset, u8 *data, int index);
+#endif /* _HNS_DSAF_RCB_H */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
new file mode 100644 (file)
index 0000000..b475e1b
--- /dev/null
@@ -0,0 +1,972 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 _DSAF_REG_H_
+#define _DSAF_REG_H_
+
+#define HNS_GE_FIFO_ERR_INTNUM 8
+#define HNS_XGE_ERR_INTNUM 6
+#define HNS_RCB_COMM_ERR_INTNUM 12
+#define HNS_PPE_TNL_ERR_INTNUM 8
+#define HNS_DSAF_EVENT_INTNUM 21
+#define HNS_DEBUG_RING_INTNUM 4
+#define HNS_SERVICE_RING_INTNUM 256
+
+#define HNS_DEBUG_RING_IRQ_IDX (HNS_GE_FIFO_ERR_INTNUM + HNS_XGE_ERR_INTNUM +\
+               HNS_RCB_COMM_ERR_INTNUM + HNS_PPE_TNL_ERR_INTNUM +\
+               HNS_DSAF_EVENT_INTNUM)
+#define HNS_SERVICE_RING_IRQ_IDX (HNS_DEBUG_RING_IRQ_IDX +\
+               HNS_DEBUG_RING_INTNUM)
+
+#define DSAF_IRQ_NUM 18
+
+#define DSAF_MAX_PORT_NUM_PER_CHIP 8
+#define DSAF_SERVICE_PORT_NUM_PER_DSAF 6
+#define DSAF_MAX_VM_NUM 128
+
+#define DSAF_COMM_DEV_NUM 3
+#define DSAF_PPE_INODE_BASE 6
+#define HNS_DSAF_COMM_SERVICE_NW_IDX 0
+#define DSAF_DEBUG_NW_NUM      2
+#define DSAF_SERVICE_NW_NUM    6
+#define DSAF_COMM_CHN          DSAF_SERVICE_NW_NUM
+#define DSAF_GE_NUM            ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM))
+#define DSAF_PORT_NUM          ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM))
+#define DSAF_XGE_NUM           DSAF_SERVICE_NW_NUM
+#define DSAF_NODE_NUM          18
+#define DSAF_XOD_BIG_NUM       DSAF_NODE_NUM
+#define DSAF_SBM_NUM           DSAF_NODE_NUM
+#define DSAF_VOQ_NUM           DSAF_NODE_NUM
+#define DSAF_INODE_NUM         DSAF_NODE_NUM
+#define DSAF_XOD_NUM           8
+#define DSAF_TBL_NUM           8
+#define DSAF_SW_PORT_NUM       8
+#define DSAF_TOTAL_QUEUE_NUM   129
+
+#define DSAF_TCAM_SUM          512
+#define DSAF_LINE_SUM          (2048 * 14)
+
+#define DSAF_SUB_SC_NT_SRAM_CLK_SEL_REG                0x100
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG              0x180
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL1_REG              0x184
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL2_REG              0x188
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL3_REG              0x18C
+#define DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG              0x190
+#define DSAF_SUB_SC_HILINK4_CRG_CTRL1_REG              0x194
+#define DSAF_SUB_SC_DSAF_CLK_EN_REG                    0x300
+#define DSAF_SUB_SC_DSAF_CLK_DIS_REG                   0x304
+#define DSAF_SUB_SC_NT_CLK_EN_REG                      0x308
+#define DSAF_SUB_SC_NT_CLK_DIS_REG                     0x30C
+#define DSAF_SUB_SC_XGE_CLK_EN_REG                     0x310
+#define DSAF_SUB_SC_XGE_CLK_DIS_REG                    0x314
+#define DSAF_SUB_SC_GE_CLK_EN_REG                      0x318
+#define DSAF_SUB_SC_GE_CLK_DIS_REG                     0x31C
+#define DSAF_SUB_SC_PPE_CLK_EN_REG                     0x320
+#define DSAF_SUB_SC_PPE_CLK_DIS_REG                    0x324
+#define DSAF_SUB_SC_RCB_PPE_COM_CLK_EN_REG             0x350
+#define DSAF_SUB_SC_RCB_PPE_COM_CLK_DIS_REG            0x354
+#define DSAF_SUB_SC_XBAR_RESET_REQ_REG                 0xA00
+#define DSAF_SUB_SC_XBAR_RESET_DREQ_REG                0xA04
+#define DSAF_SUB_SC_NT_RESET_REQ_REG                   0xA08
+#define DSAF_SUB_SC_NT_RESET_DREQ_REG                  0xA0C
+#define DSAF_SUB_SC_XGE_RESET_REQ_REG                  0xA10
+#define DSAF_SUB_SC_XGE_RESET_DREQ_REG                 0xA14
+#define DSAF_SUB_SC_GE_RESET_REQ0_REG                  0xA18
+#define DSAF_SUB_SC_GE_RESET_DREQ0_REG                 0xA1C
+#define DSAF_SUB_SC_GE_RESET_REQ1_REG                  0xA20
+#define DSAF_SUB_SC_GE_RESET_DREQ1_REG                 0xA24
+#define DSAF_SUB_SC_PPE_RESET_REQ_REG                  0xA48
+#define DSAF_SUB_SC_PPE_RESET_DREQ_REG                 0xA4C
+#define DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG          0xA88
+#define DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG         0xA8C
+#define DSAF_SUB_SC_LIGHT_MODULE_DETECT_EN_REG         0x2060
+#define DSAF_SUB_SC_TCAM_MBIST_EN_REG                  0x2300
+#define DSAF_SUB_SC_DSAF_CLK_ST_REG                    0x5300
+#define DSAF_SUB_SC_NT_CLK_ST_REG                      0x5304
+#define DSAF_SUB_SC_XGE_CLK_ST_REG                     0x5308
+#define DSAF_SUB_SC_GE_CLK_ST_REG                      0x530C
+#define DSAF_SUB_SC_PPE_CLK_ST_REG                     0x5310
+#define DSAF_SUB_SC_ROCEE_CLK_ST_REG                   0x5314
+#define DSAF_SUB_SC_CPU_CLK_ST_REG                     0x5318
+#define DSAF_SUB_SC_RCB_PPE_COM_CLK_ST_REG             0x5328
+#define DSAF_SUB_SC_XBAR_RESET_ST_REG                  0x5A00
+#define DSAF_SUB_SC_NT_RESET_ST_REG                    0x5A04
+#define DSAF_SUB_SC_XGE_RESET_ST_REG                   0x5A08
+#define DSAF_SUB_SC_GE_RESET_ST0_REG                   0x5A0C
+#define DSAF_SUB_SC_GE_RESET_ST1_REG                   0x5A10
+#define DSAF_SUB_SC_PPE_RESET_ST_REG                   0x5A24
+#define DSAF_SUB_SC_RCB_PPE_COM_RESET_ST_REG           0x5A44
+
+/*serdes offset**/
+#define HNS_MAC_HILINK3_REG DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG
+#define HNS_MAC_HILINK4_REG DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG
+#define HNS_MAC_LANE0_CTLEDFE_REG 0x000BFFCCULL
+#define HNS_MAC_LANE1_CTLEDFE_REG 0x000BFFBCULL
+#define HNS_MAC_LANE2_CTLEDFE_REG 0x000BFFACULL
+#define HNS_MAC_LANE3_CTLEDFE_REG 0x000BFF9CULL
+#define HNS_MAC_LANE0_STATE_REG 0x000BFFD4ULL
+#define HNS_MAC_LANE1_STATE_REG 0x000BFFC4ULL
+#define HNS_MAC_LANE2_STATE_REG 0x000BFFB4ULL
+#define HNS_MAC_LANE3_STATE_REG 0x000BFFA4ULL
+
+#define HILINK_RESET_TIMOUT 10000
+
+#define DSAF_SRAM_INIT_OVER_0_REG      0x0
+#define DSAF_CFG_0_REG                 0x4
+#define DSAF_ECC_ERR_INVERT_0_REG      0x8
+#define DSAF_ABNORMAL_TIMEOUT_0_REG    0x1C
+#define DSAF_FSM_TIMEOUT_0_REG         0x20
+#define DSAF_DSA_REG_CNT_CLR_CE_REG    0x2C
+#define DSAF_DSA_SBM_INF_FIFO_THRD_REG 0x30
+#define DSAF_DSA_SRAM_1BIT_ECC_SEL_REG 0x34
+#define DSAF_DSA_SRAM_1BIT_ECC_CNT_REG 0x38
+#define DSAF_PFC_EN_0_REG              0x50
+#define DSAF_PFC_UNIT_CNT_0_REG                0x70
+#define DSAF_XGE_INT_MSK_0_REG         0x100
+#define DSAF_PPE_INT_MSK_0_REG         0x120
+#define DSAF_ROCEE_INT_MSK_0_REG       0x140
+#define DSAF_XGE_INT_SRC_0_REG         0x160
+#define DSAF_PPE_INT_SRC_0_REG         0x180
+#define DSAF_ROCEE_INT_SRC_0_REG       0x1A0
+#define DSAF_XGE_INT_STS_0_REG         0x1C0
+#define DSAF_PPE_INT_STS_0_REG         0x1E0
+#define DSAF_ROCEE_INT_STS_0_REG       0x200
+#define DSAF_PPE_QID_CFG_0_REG         0x300
+#define DSAF_SW_PORT_TYPE_0_REG                0x320
+#define DSAF_STP_PORT_TYPE_0_REG       0x340
+#define DSAF_MIX_DEF_QID_0_REG         0x360
+#define DSAF_PORT_DEF_VLAN_0_REG       0x380
+#define DSAF_VM_DEF_VLAN_0_REG         0x400
+
+#define DSAF_INODE_CUT_THROUGH_CFG_0_REG       0x1000
+#define DSAF_INODE_ECC_INVERT_EN_0_REG         0x1008
+#define DSAF_INODE_ECC_ERR_ADDR_0_REG          0x100C
+#define DSAF_INODE_IN_PORT_NUM_0_REG           0x1018
+#define DSAF_INODE_PRI_TC_CFG_0_REG            0x101C
+#define DSAF_INODE_BP_STATUS_0_REG             0x1020
+#define DSAF_INODE_PAD_DISCARD_NUM_0_REG       0x1028
+#define DSAF_INODE_FINAL_IN_MAN_NUM_0_REG      0x102C
+#define DSAF_INODE_FINAL_IN_PKT_NUM_0_REG      0x1030
+#define DSAF_INODE_SBM_PID_NUM_0_REG           0x1038
+#define DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG    0x103C
+#define DSAF_INODE_SBM_RELS_NUM_0_REG          0x104C
+#define DSAF_INODE_SBM_DROP_NUM_0_REG          0x1050
+#define DSAF_INODE_CRC_FALSE_NUM_0_REG         0x1054
+#define DSAF_INODE_BP_DISCARD_NUM_0_REG                0x1058
+#define DSAF_INODE_RSLT_DISCARD_NUM_0_REG      0x105C
+#define DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG  0x1060
+#define DSAF_INODE_VOQ_OVER_NUM_0_REG          0x1068
+#define DSAF_INODE_BD_SAVE_STATUS_0_REG                0x1900
+#define DSAF_INODE_BD_ORDER_STATUS_0_REG       0x1950
+#define DSAF_INODE_SW_VLAN_TAG_DISC_0_REG      0x1A00
+#define DSAF_INODE_IN_DATA_STP_DISC_0_REG      0x1A50
+#define DSAF_INODE_GE_FC_EN_0_REG              0x1B00
+#define DSAF_INODE_VC0_IN_PKT_NUM_0_REG                0x1B50
+#define DSAF_INODE_VC1_IN_PKT_NUM_0_REG                0x1C00
+
+#define DSAF_SBM_CFG_REG_0_REG                 0x2000
+#define DSAF_SBM_BP_CFG_0_XGE_REG_0_REG                0x2004
+#define DSAF_SBM_BP_CFG_0_PPE_REG_0_REG                0x2304
+#define DSAF_SBM_BP_CFG_0_ROCEE_REG_0_REG      0x2604
+#define DSAF_SBM_BP_CFG_1_REG_0_REG            0x2008
+#define DSAF_SBM_BP_CFG_2_XGE_REG_0_REG                0x200C
+#define DSAF_SBM_BP_CFG_2_PPE_REG_0_REG                0x230C
+#define DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG      0x260C
+#define DSAF_SBM_FREE_CNT_0_0_REG              0x2010
+#define DSAF_SBM_FREE_CNT_1_0_REG              0x2014
+#define DSAF_SBM_BP_CNT_0_0_REG                        0x2018
+#define DSAF_SBM_BP_CNT_1_0_REG                        0x201C
+#define DSAF_SBM_BP_CNT_2_0_REG                        0x2020
+#define DSAF_SBM_BP_CNT_3_0_REG                        0x2024
+#define DSAF_SBM_INER_ST_0_REG                 0x2028
+#define DSAF_SBM_MIB_REQ_FAILED_TC_0_REG       0x202C
+#define DSAF_SBM_LNK_INPORT_CNT_0_REG          0x2030
+#define DSAF_SBM_LNK_DROP_CNT_0_REG            0x2034
+#define DSAF_SBM_INF_OUTPORT_CNT_0_REG         0x2038
+#define DSAF_SBM_LNK_INPORT_TC0_CNT_0_REG      0x203C
+#define DSAF_SBM_LNK_INPORT_TC1_CNT_0_REG      0x2040
+#define DSAF_SBM_LNK_INPORT_TC2_CNT_0_REG      0x2044
+#define DSAF_SBM_LNK_INPORT_TC3_CNT_0_REG      0x2048
+#define DSAF_SBM_LNK_INPORT_TC4_CNT_0_REG      0x204C
+#define DSAF_SBM_LNK_INPORT_TC5_CNT_0_REG      0x2050
+#define DSAF_SBM_LNK_INPORT_TC6_CNT_0_REG      0x2054
+#define DSAF_SBM_LNK_INPORT_TC7_CNT_0_REG      0x2058
+#define DSAF_SBM_LNK_REQ_CNT_0_REG             0x205C
+#define DSAF_SBM_LNK_RELS_CNT_0_REG            0x2060
+#define DSAF_SBM_BP_CFG_3_REG_0_REG            0x2068
+#define DSAF_SBM_BP_CFG_4_REG_0_REG            0x206C
+
+#define DSAF_XOD_ETS_TSA_TC0_TC3_CFG_0_REG     0x3000
+#define DSAF_XOD_ETS_TSA_TC4_TC7_CFG_0_REG     0x3004
+#define DSAF_XOD_ETS_BW_TC0_TC3_CFG_0_REG      0x3008
+#define DSAF_XOD_ETS_BW_TC4_TC7_CFG_0_REG      0x300C
+#define DSAF_XOD_ETS_BW_OFFSET_CFG_0_REG       0x3010
+#define DSAF_XOD_ETS_TOKEN_CFG_0_REG           0x3014
+#define DSAF_XOD_PFS_CFG_0_0_REG               0x3018
+#define DSAF_XOD_PFS_CFG_1_0_REG               0x301C
+#define DSAF_XOD_PFS_CFG_2_0_REG               0x3020
+#define DSAF_XOD_GNT_L_0_REG                   0x3024
+#define DSAF_XOD_GNT_H_0_REG                   0x3028
+#define DSAF_XOD_CONNECT_STATE_0_REG           0x302C
+#define DSAF_XOD_RCVPKT_CNT_0_REG              0x3030
+#define DSAF_XOD_RCVTC0_CNT_0_REG              0x3034
+#define DSAF_XOD_RCVTC1_CNT_0_REG              0x3038
+#define DSAF_XOD_RCVTC2_CNT_0_REG              0x303C
+#define DSAF_XOD_RCVTC3_CNT_0_REG              0x3040
+#define DSAF_XOD_RCVVC0_CNT_0_REG              0x3044
+#define DSAF_XOD_RCVVC1_CNT_0_REG              0x3048
+#define DSAF_XOD_XGE_RCVIN0_CNT_0_REG          0x304C
+#define DSAF_XOD_XGE_RCVIN1_CNT_0_REG          0x3050
+#define DSAF_XOD_XGE_RCVIN2_CNT_0_REG          0x3054
+#define DSAF_XOD_XGE_RCVIN3_CNT_0_REG          0x3058
+#define DSAF_XOD_XGE_RCVIN4_CNT_0_REG          0x305C
+#define DSAF_XOD_XGE_RCVIN5_CNT_0_REG          0x3060
+#define DSAF_XOD_XGE_RCVIN6_CNT_0_REG          0x3064
+#define DSAF_XOD_XGE_RCVIN7_CNT_0_REG          0x3068
+#define DSAF_XOD_PPE_RCVIN0_CNT_0_REG          0x306C
+#define DSAF_XOD_PPE_RCVIN1_CNT_0_REG          0x3070
+#define DSAF_XOD_ROCEE_RCVIN0_CNT_0_REG                0x3074
+#define DSAF_XOD_ROCEE_RCVIN1_CNT_0_REG                0x3078
+#define DSAF_XOD_FIFO_STATUS_0_REG             0x307C
+
+#define DSAF_VOQ_ECC_INVERT_EN_0_REG           0x4004
+#define DSAF_VOQ_SRAM_PKT_NUM_0_REG            0x4008
+#define DSAF_VOQ_IN_PKT_NUM_0_REG              0x400C
+#define DSAF_VOQ_OUT_PKT_NUM_0_REG             0x4010
+#define DSAF_VOQ_ECC_ERR_ADDR_0_REG            0x4014
+#define DSAF_VOQ_BP_STATUS_0_REG               0x4018
+#define DSAF_VOQ_SPUP_IDLE_0_REG               0x401C
+#define DSAF_VOQ_XGE_XOD_REQ_0_0_REG           0x4024
+#define DSAF_VOQ_XGE_XOD_REQ_1_0_REG           0x4028
+#define DSAF_VOQ_PPE_XOD_REQ_0_REG             0x402C
+#define DSAF_VOQ_ROCEE_XOD_REQ_0_REG           0x4030
+#define DSAF_VOQ_BP_ALL_THRD_0_REG             0x4034
+
+#define DSAF_TBL_CTRL_0_REG                    0x5000
+#define DSAF_TBL_INT_MSK_0_REG                 0x5004
+#define DSAF_TBL_INT_SRC_0_REG                 0x5008
+#define DSAF_TBL_INT_STS_0_REG                 0x5100
+#define DSAF_TBL_TCAM_ADDR_0_REG               0x500C
+#define DSAF_TBL_LINE_ADDR_0_REG               0x5010
+#define DSAF_TBL_TCAM_HIGH_0_REG               0x5014
+#define DSAF_TBL_TCAM_LOW_0_REG                        0x5018
+#define DSAF_TBL_TCAM_MCAST_CFG_4_0_REG                0x501C
+#define DSAF_TBL_TCAM_MCAST_CFG_3_0_REG                0x5020
+#define DSAF_TBL_TCAM_MCAST_CFG_2_0_REG                0x5024
+#define DSAF_TBL_TCAM_MCAST_CFG_1_0_REG                0x5028
+#define DSAF_TBL_TCAM_MCAST_CFG_0_0_REG                0x502C
+#define DSAF_TBL_TCAM_UCAST_CFG_0_REG          0x5030
+#define DSAF_TBL_LIN_CFG_0_REG                 0x5034
+#define DSAF_TBL_TCAM_RDATA_HIGH_0_REG         0x5038
+#define DSAF_TBL_TCAM_RDATA_LOW_0_REG          0x503C
+#define DSAF_TBL_TCAM_RAM_RDATA4_0_REG         0x5040
+#define DSAF_TBL_TCAM_RAM_RDATA3_0_REG         0x5044
+#define DSAF_TBL_TCAM_RAM_RDATA2_0_REG         0x5048
+#define DSAF_TBL_TCAM_RAM_RDATA1_0_REG         0x504C
+#define DSAF_TBL_TCAM_RAM_RDATA0_0_REG         0x5050
+#define DSAF_TBL_LIN_RDATA_0_REG               0x5054
+#define DSAF_TBL_DA0_MIS_INFO1_0_REG           0x5058
+#define DSAF_TBL_DA0_MIS_INFO0_0_REG           0x505C
+#define DSAF_TBL_SA_MIS_INFO2_0_REG            0x5104
+#define DSAF_TBL_SA_MIS_INFO1_0_REG            0x5098
+#define DSAF_TBL_SA_MIS_INFO0_0_REG            0x509C
+#define DSAF_TBL_PUL_0_REG                     0x50A0
+#define DSAF_TBL_OLD_RSLT_0_REG                        0x50A4
+#define DSAF_TBL_OLD_SCAN_VAL_0_REG            0x50A8
+#define DSAF_TBL_DFX_CTRL_0_REG                        0x50AC
+#define DSAF_TBL_DFX_STAT_0_REG                        0x50B0
+#define DSAF_TBL_DFX_STAT_2_0_REG              0x5108
+#define DSAF_TBL_LKUP_NUM_I_0_REG              0x50C0
+#define DSAF_TBL_LKUP_NUM_O_0_REG              0x50E0
+#define DSAF_TBL_UCAST_BCAST_MIS_INFO_0_0_REG  0x510C
+
+#define DSAF_INODE_FIFO_WL_0_REG               0x6000
+#define DSAF_ONODE_FIFO_WL_0_REG               0x6020
+#define DSAF_XGE_GE_WORK_MODE_0_REG            0x6040
+#define DSAF_XGE_APP_RX_LINK_UP_0_REG          0x6080
+#define DSAF_NETPORT_CTRL_SIG_0_REG            0x60A0
+#define DSAF_XGE_CTRL_SIG_CFG_0_REG            0x60C0
+
+#define PPE_COM_CFG_QID_MODE_REG               0x0
+#define PPE_COM_INTEN_REG                      0x110
+#define PPE_COM_RINT_REG                       0x114
+#define PPE_COM_INTSTS_REG                     0x118
+#define PPE_COM_COMMON_CNT_CLR_CE_REG          0x1120
+#define PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG    0x300
+#define PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG      0x600
+#define PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG     0x900
+#define PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG      0xC00
+#define PPE_COM_COMMON_CNT_CLR_CE_REG          0x1120
+
+#define PPE_CFG_TX_FIFO_THRSLD_REG             0x0
+#define PPE_CFG_RX_FIFO_THRSLD_REG             0x4
+#define PPE_CFG_RX_FIFO_PAUSE_THRSLD_REG       0x8
+#define PPE_CFG_RX_FIFO_SW_BP_THRSLD_REG       0xC
+#define PPE_CFG_PAUSE_IDLE_CNT_REG             0x10
+#define PPE_CFG_BUS_CTRL_REG                   0x40
+#define PPE_CFG_TNL_TO_BE_RST_REG              0x48
+#define PPE_CURR_TNL_CAN_RST_REG               0x4C
+#define PPE_CFG_XGE_MODE_REG                   0x80
+#define PPE_CFG_MAX_FRAME_LEN_REG              0x84
+#define PPE_CFG_RX_PKT_MODE_REG                        0x88
+#define PPE_CFG_RX_VLAN_TAG_REG                        0x8C
+#define PPE_CFG_TAG_GEN_REG                    0x90
+#define PPE_CFG_PARSE_TAG_REG                  0x94
+#define PPE_CFG_PRO_CHECK_EN_REG               0x98
+#define PPE_INTEN_REG                          0x100
+#define PPE_RINT_REG                           0x104
+#define PPE_INTSTS_REG                         0x108
+#define PPE_CFG_RX_PKT_INT_REG                 0x140
+#define PPE_CFG_HEAT_DECT_TIME0_REG            0x144
+#define PPE_CFG_HEAT_DECT_TIME1_REG            0x148
+#define PPE_HIS_RX_SW_PKT_CNT_REG              0x200
+#define PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG                0x204
+#define PPE_HIS_RX_PKT_NO_BUF_CNT_REG          0x208
+#define PPE_HIS_TX_BD_CNT_REG                  0x20C
+#define PPE_HIS_TX_PKT_CNT_REG                 0x210
+#define PPE_HIS_TX_PKT_OK_CNT_REG              0x214
+#define PPE_HIS_TX_PKT_EPT_CNT_REG             0x218
+#define PPE_HIS_TX_PKT_CS_FAIL_CNT_REG         0x21C
+#define PPE_HIS_RX_APP_BUF_FAIL_CNT_REG                0x220
+#define PPE_HIS_RX_APP_BUF_WAIT_CNT_REG                0x224
+#define PPE_HIS_RX_PKT_DROP_FUL_CNT_REG                0x228
+#define PPE_HIS_RX_PKT_DROP_PRT_CNT_REG                0x22C
+#define PPE_TNL_0_5_CNT_CLR_CE_REG             0x300
+#define PPE_CFG_AXI_DBG_REG                    0x304
+#define PPE_HIS_PRO_ERR_REG                    0x308
+#define PPE_HIS_TNL_FIFO_ERR_REG               0x30C
+#define PPE_CURR_CFF_DATA_NUM_REG              0x310
+#define PPE_CURR_RX_ST_REG                     0x314
+#define PPE_CURR_TX_ST_REG                     0x318
+#define PPE_CURR_RX_FIFO0_REG                  0x31C
+#define PPE_CURR_RX_FIFO1_REG                  0x320
+#define PPE_CURR_TX_FIFO0_REG                  0x324
+#define PPE_CURR_TX_FIFO1_REG                  0x328
+#define PPE_ECO0_REG                           0x32C
+#define PPE_ECO1_REG                           0x330
+#define PPE_ECO2_REG                           0x334
+
+#define RCB_COM_CFG_ENDIAN_REG                 0x0
+#define RCB_COM_CFG_SYS_FSH_REG                        0xC
+#define RCB_COM_CFG_INIT_FLAG_REG              0x10
+#define RCB_COM_CFG_PKT_REG                    0x30
+#define RCB_COM_CFG_RINVLD_REG                 0x34
+#define RCB_COM_CFG_FNA_REG                    0x38
+#define RCB_COM_CFG_FA_REG                     0x3C
+#define RCB_COM_CFG_PKT_TC_BP_REG              0x40
+#define RCB_COM_CFG_PPE_TNL_CLKEN_REG          0x44
+
+#define RCB_COM_INTMSK_TX_PKT_REG              0x3A0
+#define RCB_COM_RINT_TX_PKT_REG                        0x3A8
+#define RCB_COM_INTMASK_ECC_ERR_REG            0x400
+#define RCB_COM_INTSTS_ECC_ERR_REG             0x408
+#define RCB_COM_EBD_SRAM_ERR_REG               0x410
+#define RCB_COM_RXRING_ERR_REG                 0x41C
+#define RCB_COM_TXRING_ERR_REG                 0x420
+#define RCB_COM_TX_FBD_ERR_REG                 0x424
+#define RCB_SRAM_ECC_CHK_EN_REG                        0x428
+#define RCB_SRAM_ECC_CHK0_REG                  0x42C
+#define RCB_SRAM_ECC_CHK1_REG                  0x430
+#define RCB_SRAM_ECC_CHK2_REG                  0x434
+#define RCB_SRAM_ECC_CHK3_REG                  0x438
+#define RCB_SRAM_ECC_CHK4_REG                  0x43c
+#define RCB_SRAM_ECC_CHK5_REG                  0x440
+#define RCB_ECC_ERR_ADDR0_REG                  0x450
+#define RCB_ECC_ERR_ADDR3_REG                  0x45C
+#define RCB_ECC_ERR_ADDR4_REG                  0x460
+#define RCB_ECC_ERR_ADDR5_REG                  0x464
+
+#define RCB_COM_SF_CFG_INTMASK_RING            0x480
+#define RCB_COM_SF_CFG_RING_STS                        0x484
+#define RCB_COM_SF_CFG_RING                    0x488
+#define RCB_COM_SF_CFG_INTMASK_BD              0x48C
+#define RCB_COM_SF_CFG_BD_RINT_STS             0x470
+#define RCB_COM_RCB_RD_BD_BUSY                 0x490
+#define RCB_COM_RCB_FBD_CRT_EN                 0x494
+#define RCB_COM_AXI_WR_ERR_INTMASK             0x498
+#define RCB_COM_AXI_ERR_STS                    0x49C
+#define RCB_COM_CHK_TX_FBD_NUM_REG             0x4a0
+
+#define RCB_CFG_BD_NUM_REG                     0x9000
+#define RCB_CFG_PKTLINE_REG                    0x9050
+
+#define RCB_CFG_OVERTIME_REG                   0x9300
+#define RCB_CFG_PKTLINE_INT_NUM_REG            0x9304
+#define RCB_CFG_OVERTIME_INT_NUM_REG           0x9308
+
+#define RCB_RING_RX_RING_BASEADDR_L_REG                0x00000
+#define RCB_RING_RX_RING_BASEADDR_H_REG                0x00004
+#define RCB_RING_RX_RING_BD_NUM_REG            0x00008
+#define RCB_RING_RX_RING_BD_LEN_REG            0x0000C
+#define RCB_RING_RX_RING_PKTLINE_REG           0x00010
+#define RCB_RING_RX_RING_TAIL_REG              0x00018
+#define RCB_RING_RX_RING_HEAD_REG              0x0001C
+#define RCB_RING_RX_RING_FBDNUM_REG            0x00020
+#define RCB_RING_RX_RING_PKTNUM_RECORD_REG     0x0002C
+
+#define RCB_RING_TX_RING_BASEADDR_L_REG                0x00040
+#define RCB_RING_TX_RING_BASEADDR_H_REG                0x00044
+#define RCB_RING_TX_RING_BD_NUM_REG            0x00048
+#define RCB_RING_TX_RING_BD_LEN_REG            0x0004C
+#define RCB_RING_TX_RING_PKTLINE_REG           0x00050
+#define RCB_RING_TX_RING_TAIL_REG              0x00058
+#define RCB_RING_TX_RING_HEAD_REG              0x0005C
+#define RCB_RING_TX_RING_FBDNUM_REG            0x00060
+#define RCB_RING_TX_RING_OFFSET_REG            0x00064
+#define RCB_RING_TX_RING_PKTNUM_RECORD_REG     0x0006C
+
+#define RCB_RING_PREFETCH_EN_REG               0x0007C
+#define RCB_RING_CFG_VF_NUM_REG                        0x00080
+#define RCB_RING_ASID_REG                      0x0008C
+#define RCB_RING_RX_VM_REG                     0x00090
+#define RCB_RING_T0_BE_RST                     0x00094
+#define RCB_RING_COULD_BE_RST                  0x00098
+#define RCB_RING_WRR_WEIGHT_REG                        0x0009c
+
+#define RCB_RING_INTMSK_RXWL_REG               0x000A0
+#define RCB_RING_INTSTS_RX_RING_REG            0x000A4
+#define RCB_RING_INTMSK_TXWL_REG               0x000AC
+#define RCB_RING_INTSTS_TX_RING_REG            0x000B0
+#define RCB_RING_INTMSK_RX_OVERTIME_REG                0x000B8
+#define RCB_RING_INTSTS_RX_OVERTIME_REG                0x000BC
+#define RCB_RING_INTMSK_TX_OVERTIME_REG                0x000C4
+#define RCB_RING_INTSTS_TX_OVERTIME_REG                0x000C8
+
+#define GMAC_DUPLEX_TYPE_REG                   0x0008UL
+#define GMAC_FD_FC_TYPE_REG                    0x000CUL
+#define GMAC_FC_TX_TIMER_REG                   0x001CUL
+#define GMAC_FD_FC_ADDR_LOW_REG                        0x0020UL
+#define GMAC_FD_FC_ADDR_HIGH_REG               0x0024UL
+#define GMAC_IPG_TX_TIMER_REG                  0x0030UL
+#define GMAC_PAUSE_THR_REG                     0x0038UL
+#define GMAC_MAX_FRM_SIZE_REG                  0x003CUL
+#define GMAC_PORT_MODE_REG                     0x0040UL
+#define GMAC_PORT_EN_REG                       0x0044UL
+#define GMAC_PAUSE_EN_REG                      0x0048UL
+#define GMAC_SHORT_RUNTS_THR_REG               0x0050UL
+#define GMAC_AN_NEG_STATE_REG                  0x0058UL
+#define GMAC_TX_LOCAL_PAGE_REG                 0x005CUL
+#define GMAC_TRANSMIT_CONTROL_REG              0x0060UL
+#define GMAC_REC_FILT_CONTROL_REG              0x0064UL
+#define GMAC_PTP_CONFIG_REG                    0x0074UL
+
+#define GMAC_RX_OCTETS_TOTAL_OK_REG            0x0080UL
+#define GMAC_RX_OCTETS_BAD_REG                 0x0084UL
+#define GMAC_RX_UC_PKTS_REG                    0x0088UL
+#define GMAC_RX_MC_PKTS_REG                    0x008CUL
+#define GMAC_RX_BC_PKTS_REG                    0x0090UL
+#define GMAC_RX_PKTS_64OCTETS_REG              0x0094UL
+#define GMAC_RX_PKTS_65TO127OCTETS_REG         0x0098UL
+#define GMAC_RX_PKTS_128TO255OCTETS_REG                0x009CUL
+#define GMAC_RX_PKTS_255TO511OCTETS_REG                0x00A0UL
+#define GMAC_RX_PKTS_512TO1023OCTETS_REG       0x00A4UL
+#define GMAC_RX_PKTS_1024TO1518OCTETS_REG      0x00A8UL
+#define GMAC_RX_PKTS_1519TOMAXOCTETS_REG       0x00ACUL
+#define GMAC_RX_FCS_ERRORS_REG                 0x00B0UL
+#define GMAC_RX_TAGGED_REG                     0x00B4UL
+#define GMAC_RX_DATA_ERR_REG                   0x00B8UL
+#define GMAC_RX_ALIGN_ERRORS_REG               0x00BCUL
+#define GMAC_RX_LONG_ERRORS_REG                        0x00C0UL
+#define GMAC_RX_JABBER_ERRORS_REG              0x00C4UL
+#define GMAC_RX_PAUSE_MACCTRL_FRAM_REG         0x00C8UL
+#define GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG       0x00CCUL
+#define GMAC_RX_VERY_LONG_ERR_CNT_REG          0x00D0UL
+#define GMAC_RX_RUNT_ERR_CNT_REG               0x00D4UL
+#define GMAC_RX_SHORT_ERR_CNT_REG              0x00D8UL
+#define GMAC_RX_FILT_PKT_CNT_REG               0x00E8UL
+#define GMAC_RX_OCTETS_TOTAL_FILT_REG          0x00ECUL
+#define GMAC_OCTETS_TRANSMITTED_OK_REG         0x0100UL
+#define GMAC_OCTETS_TRANSMITTED_BAD_REG                0x0104UL
+#define GMAC_TX_UC_PKTS_REG                    0x0108UL
+#define GMAC_TX_MC_PKTS_REG                    0x010CUL
+#define GMAC_TX_BC_PKTS_REG                    0x0110UL
+#define GMAC_TX_PKTS_64OCTETS_REG              0x0114UL
+#define GMAC_TX_PKTS_65TO127OCTETS_REG         0x0118UL
+#define GMAC_TX_PKTS_128TO255OCTETS_REG                0x011CUL
+#define GMAC_TX_PKTS_255TO511OCTETS_REG                0x0120UL
+#define GMAC_TX_PKTS_512TO1023OCTETS_REG       0x0124UL
+#define GMAC_TX_PKTS_1024TO1518OCTETS_REG      0x0128UL
+#define GMAC_TX_PKTS_1519TOMAXOCTETS_REG       0x012CUL
+#define GMAC_TX_EXCESSIVE_LENGTH_DROP_REG      0x014CUL
+#define GMAC_TX_UNDERRUN_REG                   0x0150UL
+#define GMAC_TX_TAGGED_REG                     0x0154UL
+#define GMAC_TX_CRC_ERROR_REG                  0x0158UL
+#define GMAC_TX_PAUSE_FRAMES_REG               0x015CUL
+#define GAMC_RX_MAX_FRAME                      0x0170UL
+#define GMAC_LINE_LOOP_BACK_REG                        0x01A8UL
+#define GMAC_CF_CRC_STRIP_REG                  0x01B0UL
+#define GMAC_MODE_CHANGE_EN_REG                        0x01B4UL
+#define GMAC_SIXTEEN_BIT_CNTR_REG              0x01CCUL
+#define GMAC_LD_LINK_COUNTER_REG               0x01D0UL
+#define GMAC_LOOP_REG                          0x01DCUL
+#define GMAC_RECV_CONTROL_REG                  0x01E0UL
+#define GMAC_VLAN_CODE_REG                     0x01E8UL
+#define GMAC_RX_OVERRUN_CNT_REG                        0x01ECUL
+#define GMAC_RX_LENGTHFIELD_ERR_CNT_REG                0x01F4UL
+#define GMAC_RX_FAIL_COMMA_CNT_REG             0x01F8UL
+#define GMAC_STATION_ADDR_LOW_0_REG            0x0200UL
+#define GMAC_STATION_ADDR_HIGH_0_REG           0x0204UL
+#define GMAC_STATION_ADDR_LOW_1_REG            0x0208UL
+#define GMAC_STATION_ADDR_HIGH_1_REG           0x020CUL
+#define GMAC_STATION_ADDR_LOW_2_REG            0x0210UL
+#define GMAC_STATION_ADDR_HIGH_2_REG           0x0214UL
+#define GMAC_STATION_ADDR_LOW_3_REG            0x0218UL
+#define GMAC_STATION_ADDR_HIGH_3_REG           0x021CUL
+#define GMAC_STATION_ADDR_LOW_4_REG            0x0220UL
+#define GMAC_STATION_ADDR_HIGH_4_REG           0x0224UL
+#define GMAC_STATION_ADDR_LOW_5_REG            0x0228UL
+#define GMAC_STATION_ADDR_HIGH_5_REG           0x022CUL
+#define GMAC_STATION_ADDR_LOW_MSK_0_REG                0x0230UL
+#define GMAC_STATION_ADDR_HIGH_MSK_0_REG       0x0234UL
+#define GMAC_STATION_ADDR_LOW_MSK_1_REG                0x0238UL
+#define GMAC_STATION_ADDR_HIGH_MSK_1_REG       0x023CUL
+#define GMAC_MAC_SKIP_LEN_REG                  0x0240UL
+#define GMAC_TX_LOOP_PKT_PRI_REG               0x0378UL
+
+#define XGMAC_INT_STATUS_REG                   0x0
+#define XGMAC_INT_ENABLE_REG                   0x4
+#define XGMAC_INT_SET_REG                      0x8
+#define XGMAC_IERR_U_INFO_REG                  0xC
+#define XGMAC_OVF_INFO_REG                     0x10
+#define XGMAC_OVF_CNT_REG                      0x14
+#define XGMAC_PORT_MODE_REG                    0x40
+#define XGMAC_CLK_ENABLE_REG                   0x44
+#define XGMAC_RESET_REG                                0x48
+#define XGMAC_LINK_CONTROL_REG                 0x50
+#define XGMAC_LINK_STATUS_REG                  0x54
+#define XGMAC_SPARE_REG                                0xC0
+#define XGMAC_SPARE_CNT_REG                    0xC4
+
+#define XGMAC_MAC_ENABLE_REG                   0x100
+#define XGMAC_MAC_CONTROL_REG                  0x104
+#define XGMAC_MAC_IPG_REG                      0x120
+#define XGMAC_MAC_MSG_CRC_EN_REG               0x124
+#define XGMAC_MAC_MSG_IMG_REG                  0x128
+#define XGMAC_MAC_MSG_FC_CFG_REG               0x12C
+#define XGMAC_MAC_MSG_TC_CFG_REG               0x130
+#define XGMAC_MAC_PAD_SIZE_REG                 0x134
+#define XGMAC_MAC_MIN_PKT_SIZE_REG             0x138
+#define XGMAC_MAC_MAX_PKT_SIZE_REG             0x13C
+#define XGMAC_MAC_PAUSE_CTRL_REG               0x160
+#define XGMAC_MAC_PAUSE_TIME_REG               0x164
+#define XGMAC_MAC_PAUSE_GAP_REG                        0x168
+#define XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG                0x16C
+#define XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG                0x170
+#define XGMAC_MAC_PAUSE_PEER_MAC_H_REG         0x174
+#define XGMAC_MAC_PAUSE_PEER_MAC_L_REG         0x178
+#define XGMAC_MAC_PFC_PRI_EN_REG               0x17C
+#define XGMAC_MAC_1588_CTRL_REG                        0x180
+#define XGMAC_MAC_1588_TX_PORT_DLY_REG         0x184
+#define XGMAC_MAC_1588_RX_PORT_DLY_REG         0x188
+#define XGMAC_MAC_1588_ASYM_DLY_REG            0x18C
+#define XGMAC_MAC_1588_ADJUST_CFG_REG          0x190
+#define XGMAC_MAC_Y1731_ETH_TYPE_REG           0x194
+#define XGMAC_MAC_MIB_CONTROL_REG              0x198
+#define XGMAC_MAC_WAN_RATE_ADJUST_REG          0x19C
+#define XGMAC_MAC_TX_ERR_MARK_REG              0x1A0
+#define XGMAC_MAC_TX_LF_RF_CONTROL_REG         0x1A4
+#define XGMAC_MAC_RX_LF_RF_STATUS_REG          0x1A8
+#define XGMAC_MAC_TX_RUNT_PKT_CNT_REG          0x1C0
+#define XGMAC_MAC_RX_RUNT_PKT_CNT_REG          0x1C4
+#define XGMAC_MAC_RX_PREAM_ERR_PKT_CNT_REG     0x1C8
+#define XGMAC_MAC_TX_LF_RF_TERM_PKT_CNT_REG    0x1CC
+#define XGMAC_MAC_TX_SN_MISMATCH_PKT_CNT_REG   0x1D0
+#define XGMAC_MAC_RX_ERR_MSG_CNT_REG           0x1D4
+#define XGMAC_MAC_RX_ERR_EFD_CNT_REG           0x1D8
+#define XGMAC_MAC_ERR_INFO_REG                 0x1DC
+#define XGMAC_MAC_DBG_INFO_REG                 0x1E0
+
+#define XGMAC_PCS_BASER_SYNC_THD_REG           0x330
+#define XGMAC_PCS_STATUS1_REG                  0x404
+#define XGMAC_PCS_BASER_STATUS1_REG            0x410
+#define XGMAC_PCS_BASER_STATUS2_REG            0x414
+#define XGMAC_PCS_BASER_SEEDA_0_REG            0x420
+#define XGMAC_PCS_BASER_SEEDA_1_REG            0x424
+#define XGMAC_PCS_BASER_SEEDB_0_REG            0x428
+#define XGMAC_PCS_BASER_SEEDB_1_REG            0x42C
+#define XGMAC_PCS_BASER_TEST_CONTROL_REG       0x430
+#define XGMAC_PCS_BASER_TEST_ERR_CNT_REG       0x434
+#define XGMAC_PCS_DBG_INFO_REG                 0x4C0
+#define XGMAC_PCS_DBG_INFO1_REG                        0x4C4
+#define XGMAC_PCS_DBG_INFO2_REG                        0x4C8
+#define XGMAC_PCS_DBG_INFO3_REG                        0x4CC
+
+#define XGMAC_PMA_ENABLE_REG                   0x700
+#define XGMAC_PMA_CONTROL_REG                  0x704
+#define XGMAC_PMA_SIGNAL_STATUS_REG            0x708
+#define XGMAC_PMA_DBG_INFO_REG                 0x70C
+#define XGMAC_PMA_FEC_ABILITY_REG              0x740
+#define XGMAC_PMA_FEC_CONTROL_REG              0x744
+#define XGMAC_PMA_FEC_CORR_BLOCK_CNT__REG      0x750
+#define XGMAC_PMA_FEC_UNCORR_BLOCK_CNT__REG    0x760
+
+#define XGMAC_TX_PKTS_FRAGMENT                 0x0000
+#define XGMAC_TX_PKTS_UNDERSIZE                        0x0008
+#define XGMAC_TX_PKTS_UNDERMIN                 0x0010
+#define XGMAC_TX_PKTS_64OCTETS                 0x0018
+#define XGMAC_TX_PKTS_65TO127OCTETS            0x0020
+#define XGMAC_TX_PKTS_128TO255OCTETS           0x0028
+#define XGMAC_TX_PKTS_256TO511OCTETS           0x0030
+#define XGMAC_TX_PKTS_512TO1023OCTETS          0x0038
+#define XGMAC_TX_PKTS_1024TO1518OCTETS         0x0040
+#define XGMAC_TX_PKTS_1519TOMAXOCTETS          0x0048
+#define XGMAC_TX_PKTS_1519TOMAXOCTETSOK                0x0050
+#define XGMAC_TX_PKTS_OVERSIZE                 0x0058
+#define XGMAC_TX_PKTS_JABBER                   0x0060
+#define XGMAC_TX_GOODPKTS                      0x0068
+#define XGMAC_TX_GOODOCTETS                    0x0070
+#define XGMAC_TX_TOTAL_PKTS                    0x0078
+#define XGMAC_TX_TOTALOCTETS                   0x0080
+#define XGMAC_TX_UNICASTPKTS                   0x0088
+#define XGMAC_TX_MULTICASTPKTS                 0x0090
+#define XGMAC_TX_BROADCASTPKTS                 0x0098
+#define XGMAC_TX_PRI0PAUSEPKTS                 0x00a0
+#define XGMAC_TX_PRI1PAUSEPKTS                 0x00a8
+#define XGMAC_TX_PRI2PAUSEPKTS                 0x00b0
+#define XGMAC_TX_PRI3PAUSEPKTS                 0x00b8
+#define XGMAC_TX_PRI4PAUSEPKTS                 0x00c0
+#define XGMAC_TX_PRI5PAUSEPKTS                 0x00c8
+#define XGMAC_TX_PRI6PAUSEPKTS                 0x00d0
+#define XGMAC_TX_PRI7PAUSEPKTS                 0x00d8
+#define XGMAC_TX_MACCTRLPKTS                   0x00e0
+#define XGMAC_TX_1731PKTS                      0x00e8
+#define XGMAC_TX_1588PKTS                      0x00f0
+#define XGMAC_RX_FROMAPPGOODPKTS               0x00f8
+#define XGMAC_RX_FROMAPPBADPKTS                        0x0100
+#define XGMAC_TX_ERRALLPKTS                    0x0108
+
+#define XGMAC_RX_PKTS_FRAGMENT                 0x0110
+#define XGMAC_RX_PKTSUNDERSIZE                 0x0118
+#define XGMAC_RX_PKTS_UNDERMIN                 0x0120
+#define XGMAC_RX_PKTS_64OCTETS                 0x0128
+#define XGMAC_RX_PKTS_65TO127OCTETS            0x0130
+#define XGMAC_RX_PKTS_128TO255OCTETS           0x0138
+#define XGMAC_RX_PKTS_256TO511OCTETS           0x0140
+#define XGMAC_RX_PKTS_512TO1023OCTETS          0x0148
+#define XGMAC_RX_PKTS_1024TO1518OCTETS         0x0150
+#define XGMAC_RX_PKTS_1519TOMAXOCTETS          0x0158
+#define XGMAC_RX_PKTS_1519TOMAXOCTETSOK                0x0160
+#define XGMAC_RX_PKTS_OVERSIZE                 0x0168
+#define XGMAC_RX_PKTS_JABBER                   0x0170
+#define XGMAC_RX_GOODPKTS                      0x0178
+#define XGMAC_RX_GOODOCTETS                    0x0180
+#define XGMAC_RX_TOTAL_PKTS                    0x0188
+#define XGMAC_RX_TOTALOCTETS                   0x0190
+#define XGMAC_RX_UNICASTPKTS                   0x0198
+#define XGMAC_RX_MULTICASTPKTS                 0x01a0
+#define XGMAC_RX_BROADCASTPKTS                 0x01a8
+#define XGMAC_RX_PRI0PAUSEPKTS                 0x01b0
+#define XGMAC_RX_PRI1PAUSEPKTS                 0x01b8
+#define XGMAC_RX_PRI2PAUSEPKTS                 0x01c0
+#define XGMAC_RX_PRI3PAUSEPKTS                 0x01c8
+#define XGMAC_RX_PRI4PAUSEPKTS                 0x01d0
+#define XGMAC_RX_PRI5PAUSEPKTS                 0x01d8
+#define XGMAC_RX_PRI6PAUSEPKTS                 0x01e0
+#define XGMAC_RX_PRI7PAUSEPKTS                 0x01e8
+#define XGMAC_RX_MACCTRLPKTS                   0x01f0
+#define XGMAC_TX_SENDAPPGOODPKTS               0x01f8
+#define XGMAC_TX_SENDAPPBADPKTS                        0x0200
+#define XGMAC_RX_1731PKTS                      0x0208
+#define XGMAC_RX_SYMBOLERRPKTS                 0x0210
+#define XGMAC_RX_FCSERRPKTS                    0x0218
+
+#define XGMAC_TRX_CORE_SRST_M                  0x2080
+
+#define DSAF_CFG_EN_S 0
+#define DSAF_CFG_TC_MODE_S 1
+#define DSAF_CFG_CRC_EN_S 2
+#define DSAF_CFG_SBM_INIT_S 3
+#define DSAF_CFG_MIX_MODE_S 4
+#define DSAF_CFG_STP_MODE_S 5
+#define DSAF_CFG_LOCA_ADDR_EN_S 6
+
+#define DSAF_CNT_CLR_CE_S 0
+#define DSAF_SNAP_EN_S 1
+
+#define HNS_DSAF_PFC_UNIT_CNT_FOR_XGE 41
+#define HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000 410
+#define HNS_DSAF_PFC_UNIT_CNT_FOR_GE_2500 103
+
+#define DSAF_PFC_UNINT_CNT_M ((1ULL << 9) - 1)
+#define DSAF_PFC_UNINT_CNT_S 0
+
+#define DSAF_PPE_QID_CFG_M 0xFF
+#define DSAF_PPE_QID_CFG_S 0
+
+#define DSAF_SW_PORT_TYPE_M 3
+#define DSAF_SW_PORT_TYPE_S 0
+
+#define DSAF_STP_PORT_TYPE_M 7
+#define DSAF_STP_PORT_TYPE_S 0
+
+#define DSAF_INODE_IN_PORT_NUM_M 7
+#define DSAF_INODE_IN_PORT_NUM_S 0
+
+#define HNS_DSAF_I4TC_CFG 0x18688688
+#define HNS_DSAF_I8TC_CFG 0x18FAC688
+
+#define DSAF_SBM_CFG_SHCUT_EN_S 0
+#define DSAF_SBM_CFG_EN_S 1
+#define DSAF_SBM_CFG_MIB_EN_S 2
+#define DSAF_SBM_CFG_ECC_INVERT_EN_S 3
+
+#define DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_S 0
+#define DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 0)
+#define DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_S 10
+#define DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 10)
+#define DSAF_SBM_CFG0_COM_MAX_BUF_NUM_S 20
+#define DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M (((1ULL << 11) - 1) << 20)
+
+#define DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_S 0
+#define DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 0)
+#define DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_S 10
+#define DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 10)
+
+#define DSAF_SBM_CFG2_SET_BUF_NUM_S 0
+#define DSAF_SBM_CFG2_SET_BUF_NUM_M (((1ULL << 10) - 1) << 0)
+#define DSAF_SBM_CFG2_RESET_BUF_NUM_S 10
+#define DSAF_SBM_CFG2_RESET_BUF_NUM_M (((1ULL << 10) - 1) << 10)
+
+#define DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S 0
+#define DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M (((1ULL << 10) - 1) << 0)
+#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S 10
+#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M (((1ULL << 10) - 1) << 10)
+
+#define DSAF_TBL_TCAM_ADDR_S 0
+#define DSAF_TBL_TCAM_ADDR_M ((1ULL << 9) - 1)
+
+#define DSAF_TBL_LINE_ADDR_S 0
+#define DSAF_TBL_LINE_ADDR_M ((1ULL << 15) - 1)
+
+#define DSAF_TBL_MCAST_CFG4_VM128_112_S 0
+#define DSAF_TBL_MCAST_CFG4_VM128_112_M (((1ULL << 7) - 1) << 0)
+#define DSAF_TBL_MCAST_CFG4_ITEM_VLD_S 7
+#define DSAF_TBL_MCAST_CFG4_OLD_EN_S 8
+
+#define DSAF_TBL_MCAST_CFG0_XGE5_0_S 0
+#define DSAF_TBL_MCAST_CFG0_XGE5_0_M (((1ULL << 6) - 1) << 0)
+#define DSAF_TBL_MCAST_CFG0_VM25_0_S 6
+#define DSAF_TBL_MCAST_CFG0_VM25_0_M (((1ULL << 26) - 1) << 6)
+
+#define DSAF_TBL_UCAST_CFG1_OUT_PORT_S 0
+#define DSAF_TBL_UCAST_CFG1_OUT_PORT_M (((1ULL << 8) - 1) << 0)
+#define DSAF_TBL_UCAST_CFG1_DVC_S 8
+#define DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S 9
+#define DSAF_TBL_UCAST_CFG1_ITEM_VLD_S 10
+#define DSAF_TBL_UCAST_CFG1_OLD_EN_S 11
+
+#define DSAF_TBL_LINE_CFG_OUT_PORT_S 0
+#define DSAF_TBL_LINE_CFG_OUT_PORT_M (((1ULL << 8) - 1) << 0)
+#define DSAF_TBL_LINE_CFG_DVC_S 8
+#define DSAF_TBL_LINE_CFG_MAC_DISCARD_S 9
+
+#define DSAF_TBL_PUL_OLD_RSLT_RE_S 0
+#define DSAF_TBL_PUL_MCAST_VLD_S 1
+#define DSAF_TBL_PUL_TCAM_DATA_VLD_S 2
+#define DSAF_TBL_PUL_UCAST_VLD_S 3
+#define DSAF_TBL_PUL_LINE_VLD_S 4
+#define DSAF_TBL_PUL_TCAM_LOAD_S 5
+#define DSAF_TBL_PUL_LINE_LOAD_S 6
+
+#define DSAF_TBL_DFX_LINE_LKUP_NUM_EN_S 0
+#define DSAF_TBL_DFX_UC_LKUP_NUM_EN_S 1
+#define DSAF_TBL_DFX_MC_LKUP_NUM_EN_S 2
+#define DSAF_TBL_DFX_BC_LKUP_NUM_EN_S 3
+#define DSAF_TBL_DFX_RAM_ERR_INJECT_EN_S 4
+
+#define DSAF_VOQ_BP_ALL_DOWNTHRD_S 0
+#define DSAF_VOQ_BP_ALL_DOWNTHRD_M (((1ULL << 10) - 1) << 0)
+#define DSAF_VOQ_BP_ALL_UPTHRD_S 10
+#define DSAF_VOQ_BP_ALL_UPTHRD_M (((1ULL << 10) - 1) << 10)
+
+#define DSAF_XGE_GE_WORK_MODE_S 0
+#define DSAF_XGE_GE_LOOPBACK_S 1
+
+#define DSAF_FC_XGE_TX_PAUSE_S 0
+#define DSAF_REGS_XGE_CNT_CAR_S 1
+
+#define PPE_CFG_QID_MODE_DEF_QID_S     0
+#define PPE_CFG_QID_MODE_DEF_QID_M     (0xff << PPE_CFG_QID_MODE_DEF_QID_S)
+
+#define PPE_CFG_QID_MODE_CF_QID_MODE_S 8
+#define PPE_CFG_QID_MODE_CF_QID_MODE_M (0x7 << PPE_CFG_QID_MODE_CF_QID_MODE_S)
+
+#define PPE_CNT_CLR_CE_B       0
+#define PPE_CNT_CLR_SNAP_EN_B  1
+
+#define PPE_COMMON_CNT_CLR_CE_B        0
+#define PPE_COMMON_CNT_CLR_SNAP_EN_B   1
+
+#define GMAC_DUPLEX_TYPE_B 0
+
+#define GMAC_FC_TX_TIMER_S 0
+#define GMAC_FC_TX_TIMER_M 0xffff
+
+#define GMAC_MAX_FRM_SIZE_S 0
+#define GMAC_MAX_FRM_SIZE_M 0xffff
+
+#define GMAC_PORT_MODE_S       0
+#define GMAC_PORT_MODE_M       0xf
+
+#define GMAC_RGMII_1000M_DELAY_B       4
+#define GMAC_MII_TX_EDGE_SEL_B         5
+#define GMAC_FIFO_ERR_AUTO_RST_B       6
+#define GMAC_DBG_CLK_LOS_MSK_B         7
+
+#define GMAC_PORT_RX_EN_B      1
+#define GMAC_PORT_TX_EN_B      2
+
+#define GMAC_PAUSE_EN_RX_FDFC_B 0
+#define GMAC_PAUSE_EN_TX_FDFC_B 1
+#define GMAC_PAUSE_EN_TX_HDFC_B 2
+
+#define GMAC_SHORT_RUNTS_THR_S 0
+#define GMAC_SHORT_RUNTS_THR_M 0x1f
+
+#define GMAC_AN_NEG_STAT_FD_B          5
+#define GMAC_AN_NEG_STAT_HD_B          6
+#define GMAC_AN_NEG_STAT_RF1_DUPLIEX_B 12
+#define GMAC_AN_NEG_STAT_RF2_B         13
+
+#define GMAC_AN_NEG_STAT_NP_LNK_OK_B   15
+#define GMAC_AN_NEG_STAT_RX_SYNC_OK_B  20
+#define GMAC_AN_NEG_STAT_AN_DONE_B     21
+
+#define GMAC_AN_NEG_STAT_PS_S          7
+#define GMAC_AN_NEG_STAT_PS_M          (0x3 << GMAC_AN_NEG_STAT_PS_S)
+
+#define GMAC_AN_NEG_STAT_SPEED_S       10
+#define GMAC_AN_NEG_STAT_SPEED_M       (0x3 << GMAC_AN_NEG_STAT_SPEED_S)
+
+#define GMAC_TX_AN_EN_B                5
+#define GMAC_TX_CRC_ADD_B      6
+#define GMAC_TX_PAD_EN_B       7
+
+#define GMAC_LINE_LOOPBACK_B   0
+
+#define GMAC_LP_REG_CF_EXT_DRV_LP_B    1
+#define GMAC_LP_REG_CF2MI_LP_EN_B      2
+
+#define GMAC_MODE_CHANGE_EB_B  0
+
+#define GMAC_RECV_CTRL_STRIP_PAD_EN_B  3
+#define GMAC_RECV_CTRL_RUNT_PKT_EN_B   4
+
+#define GMAC_TX_LOOP_PKT_HIG_PRI_B     0
+#define GMAC_TX_LOOP_PKT_EN_B          1
+
+#define XGMAC_PORT_MODE_TX_S           0x0
+#define XGMAC_PORT_MODE_TX_M           (0x3 << XGMAC_PORT_MODE_TX_S)
+#define XGMAC_PORT_MODE_TX_40G_B       0x3
+#define XGMAC_PORT_MODE_RX_S           0x4
+#define XGMAC_PORT_MODE_RX_M           (0x3 << XGMAC_PORT_MODE_RX_S)
+#define XGMAC_PORT_MODE_RX_40G_B       0x7
+
+#define XGMAC_ENABLE_TX_B              0
+#define XGMAC_ENABLE_RX_B              1
+
+#define XGMAC_CTL_TX_FCS_B             0
+#define XGMAC_CTL_TX_PAD_B             1
+#define XGMAC_CTL_TX_PREAMBLE_TRANS_B  3
+#define XGMAC_CTL_TX_UNDER_MIN_ERR_B   4
+#define XGMAC_CTL_TX_TRUNCATE_B                5
+#define XGMAC_CTL_TX_1588_B            8
+#define XGMAC_CTL_TX_1731_B            9
+#define XGMAC_CTL_TX_PFC_B             10
+#define XGMAC_CTL_RX_FCS_B             16
+#define XGMAC_CTL_RX_FCS_STRIP_B       17
+#define XGMAC_CTL_RX_PREAMBLE_TRANS_B  19
+#define XGMAC_CTL_RX_UNDER_MIN_ERR_B   20
+#define XGMAC_CTL_RX_TRUNCATE_B                21
+#define XGMAC_CTL_RX_1588_B            24
+#define XGMAC_CTL_RX_1731_B            25
+#define XGMAC_CTL_RX_PFC_B             26
+
+#define XGMAC_PMA_FEC_CTL_TX_B         0
+#define XGMAC_PMA_FEC_CTL_RX_B         1
+#define XGMAC_PMA_FEC_CTL_ERR_EN       2
+#define XGMAC_PMA_FEC_CTL_ERR_SH       3
+
+#define XGMAC_PAUSE_CTL_TX_B           0
+#define XGMAC_PAUSE_CTL_RX_B           1
+#define XGMAC_PAUSE_CTL_RSP_MODE_B     2
+#define XGMAC_PAUSE_CTL_TX_XOFF_B      3
+
+static inline void dsaf_write_reg(void *base, u32 reg, u32 value)
+{
+       u8 __iomem *reg_addr = ACCESS_ONCE(base);
+
+       writel(value, reg_addr + reg);
+}
+
+#define dsaf_write_dev(a, reg, value) \
+       dsaf_write_reg((a)->io_base, (reg), (value))
+
+static inline u32 dsaf_read_reg(u8 *base, u32 reg)
+{
+       u8 __iomem *reg_addr = ACCESS_ONCE(base);
+
+       return readl(reg_addr + reg);
+}
+
+#define dsaf_read_dev(a, reg) \
+       dsaf_read_reg((a)->io_base, (reg))
+
+#define dsaf_set_field(origin, mask, shift, val) \
+       do { \
+               (origin) &= (~(mask)); \
+               (origin) |= (((val) << (shift)) & (mask)); \
+       } while (0)
+
+#define dsaf_set_bit(origin, shift, val) \
+       dsaf_set_field((origin), (1ull << (shift)), (shift), (val))
+
+static inline void dsaf_set_reg_field(void *base, u32 reg, u32 mask, u32 shift,
+                                     u32 val)
+{
+       u32 origin = dsaf_read_reg(base, reg);
+
+       dsaf_set_field(origin, mask, shift, val);
+       dsaf_write_reg(base, reg, origin);
+}
+
+#define dsaf_set_dev_field(dev, reg, mask, shift, val) \
+       dsaf_set_reg_field((dev)->io_base, (reg), (mask), (shift), (val))
+
+#define dsaf_set_dev_bit(dev, reg, bit, val) \
+       dsaf_set_reg_field((dev)->io_base, (reg), (1ull << (bit)), (bit), (val))
+
+#define dsaf_get_field(origin, mask, shift) (((origin) & (mask)) >> (shift))
+
+#define dsaf_get_bit(origin, shift) \
+       dsaf_get_field((origin), (1ull << (shift)), (shift))
+
+static inline u32 dsaf_get_reg_field(void *base, u32 reg, u32 mask, u32 shift)
+{
+       u32 origin;
+
+       origin = dsaf_read_reg(base, reg);
+       return dsaf_get_field(origin, mask, shift);
+}
+
+#define dsaf_get_dev_field(dev, reg, mask, shift) \
+       dsaf_get_reg_field((dev)->io_base, (reg), (mask), (shift))
+
+#define dsaf_get_dev_bit(dev, reg, bit) \
+       dsaf_get_reg_field((dev)->io_base, (reg), (1ull << (bit)), (bit))
+
+#define dsaf_write_b(addr, data)\
+       writeb((data), (__iomem unsigned char *)(addr))
+#define dsaf_read_b(addr)\
+       readb((__iomem unsigned char *)(addr))
+
+#define hns_mac_reg_read64(drv, offset) \
+       readq((__iomem void *)(((u8 *)(drv)->io_base + 0xc00 + (offset))))
+
+#endif /* _DSAF_REG_H */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c
new file mode 100644 (file)
index 0000000..802d554
--- /dev/null
@@ -0,0 +1,837 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 <asm-generic/io-64-nonatomic-hi-lo.h>
+#include <linux/of_mdio.h>
+#include "hns_dsaf_main.h"
+#include "hns_dsaf_mac.h"
+#include "hns_dsaf_xgmac.h"
+#include "hns_dsaf_reg.h"
+
+static const struct mac_stats_string g_xgmac_stats_string[] = {
+       {"xgmac_tx_bad_pkts_minto64", MAC_STATS_FIELD_OFF(tx_fragment_err)},
+       {"xgmac_tx_good_pkts_minto64", MAC_STATS_FIELD_OFF(tx_undersize)},
+       {"xgmac_tx_total_pkts_minto64", MAC_STATS_FIELD_OFF(tx_under_min_pkts)},
+       {"xgmac_tx_pkts_64", MAC_STATS_FIELD_OFF(tx_64bytes)},
+       {"xgmac_tx_pkts_65to127", MAC_STATS_FIELD_OFF(tx_65to127)},
+       {"xgmac_tx_pkts_128to255", MAC_STATS_FIELD_OFF(tx_128to255)},
+       {"xgmac_tx_pkts_256to511", MAC_STATS_FIELD_OFF(tx_256to511)},
+       {"xgmac_tx_pkts_512to1023", MAC_STATS_FIELD_OFF(tx_512to1023)},
+       {"xgmac_tx_pkts_1024to1518", MAC_STATS_FIELD_OFF(tx_1024to1518)},
+       {"xgmac_tx_pkts_1519tomax", MAC_STATS_FIELD_OFF(tx_1519tomax)},
+       {"xgmac_tx_good_pkts_1519tomax",
+               MAC_STATS_FIELD_OFF(tx_1519tomax_good)},
+       {"xgmac_tx_good_pkts_untralmax", MAC_STATS_FIELD_OFF(tx_oversize)},
+       {"xgmac_tx_bad_pkts_untralmax", MAC_STATS_FIELD_OFF(tx_jabber_err)},
+       {"xgmac_tx_good_pkts_all", MAC_STATS_FIELD_OFF(tx_good_pkts)},
+       {"xgmac_tx_good_byte_all", MAC_STATS_FIELD_OFF(tx_good_bytes)},
+       {"xgmac_tx_total_pkt", MAC_STATS_FIELD_OFF(tx_total_pkts)},
+       {"xgmac_tx_total_byt", MAC_STATS_FIELD_OFF(tx_total_bytes)},
+       {"xgmac_tx_uc_pkt", MAC_STATS_FIELD_OFF(tx_uc_pkts)},
+       {"xgmac_tx_mc_pkt", MAC_STATS_FIELD_OFF(tx_mc_pkts)},
+       {"xgmac_tx_bc_pkt", MAC_STATS_FIELD_OFF(tx_bc_pkts)},
+       {"xgmac_tx_pause_frame_num", MAC_STATS_FIELD_OFF(tx_pfc_tc0)},
+       {"xgmac_tx_pfc_per_1pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc1)},
+       {"xgmac_tx_pfc_per_2pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc2)},
+       {"xgmac_tx_pfc_per_3pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc3)},
+       {"xgmac_tx_pfc_per_4pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc4)},
+       {"xgmac_tx_pfc_per_5pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc5)},
+       {"xgmac_tx_pfc_per_6pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc6)},
+       {"xgmac_tx_pfc_per_7pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc7)},
+       {"xgmac_tx_mac_ctrol_frame", MAC_STATS_FIELD_OFF(tx_ctrl)},
+       {"xgmac_tx_1731_pkts", MAC_STATS_FIELD_OFF(tx_1731_pkts)},
+       {"xgmac_tx_1588_pkts", MAC_STATS_FIELD_OFF(tx_1588_pkts)},
+       {"xgmac_rx_good_pkt_from_dsaf", MAC_STATS_FIELD_OFF(rx_good_from_sw)},
+       {"xgmac_rx_bad_pkt_from_dsaf", MAC_STATS_FIELD_OFF(rx_bad_from_sw)},
+       {"xgmac_tx_bad_pkt_64tomax", MAC_STATS_FIELD_OFF(tx_bad_pkts)},
+
+       {"xgmac_rx_bad_pkts_minto64", MAC_STATS_FIELD_OFF(rx_fragment_err)},
+       {"xgmac_rx_good_pkts_minto64", MAC_STATS_FIELD_OFF(rx_undersize)},
+       {"xgmac_rx_total_pkts_minto64", MAC_STATS_FIELD_OFF(rx_under_min)},
+       {"xgmac_rx_pkt_64", MAC_STATS_FIELD_OFF(rx_64bytes)},
+       {"xgmac_rx_pkt_65to127", MAC_STATS_FIELD_OFF(rx_65to127)},
+       {"xgmac_rx_pkt_128to255", MAC_STATS_FIELD_OFF(rx_128to255)},
+       {"xgmac_rx_pkt_256to511", MAC_STATS_FIELD_OFF(rx_256to511)},
+       {"xgmac_rx_pkt_512to1023", MAC_STATS_FIELD_OFF(rx_512to1023)},
+       {"xgmac_rx_pkt_1024to1518", MAC_STATS_FIELD_OFF(rx_1024to1518)},
+       {"xgmac_rx_pkt_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax)},
+       {"xgmac_rx_good_pkt_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax_good)},
+       {"xgmac_rx_good_pkt_untramax", MAC_STATS_FIELD_OFF(rx_oversize)},
+       {"xgmac_rx_bad_pkt_untramax", MAC_STATS_FIELD_OFF(rx_jabber_err)},
+       {"xgmac_rx_good_pkt", MAC_STATS_FIELD_OFF(rx_good_pkts)},
+       {"xgmac_rx_good_byt", MAC_STATS_FIELD_OFF(rx_good_bytes)},
+       {"xgmac_rx_pkt", MAC_STATS_FIELD_OFF(rx_total_pkts)},
+       {"xgmac_rx_byt", MAC_STATS_FIELD_OFF(rx_total_bytes)},
+       {"xgmac_rx_uc_pkt", MAC_STATS_FIELD_OFF(rx_uc_pkts)},
+       {"xgmac_rx_mc_pkt", MAC_STATS_FIELD_OFF(rx_mc_pkts)},
+       {"xgmac_rx_bc_pkt", MAC_STATS_FIELD_OFF(rx_bc_pkts)},
+       {"xgmac_rx_pause_frame_num", MAC_STATS_FIELD_OFF(rx_pfc_tc0)},
+       {"xgmac_rx_pfc_per_1pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc1)},
+       {"xgmac_rx_pfc_per_2pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc2)},
+       {"xgmac_rx_pfc_per_3pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc3)},
+       {"xgmac_rx_pfc_per_4pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc4)},
+       {"xgmac_rx_pfc_per_5pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc5)},
+       {"xgmac_rx_pfc_per_6pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc6)},
+       {"xgmac_rx_pfc_per_7pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc7)},
+       {"xgmac_rx_mac_control", MAC_STATS_FIELD_OFF(rx_unknown_ctrl)},
+       {"xgmac_tx_good_pkt_todsaf", MAC_STATS_FIELD_OFF(tx_good_to_sw)},
+       {"xgmac_tx_bad_pkt_todsaf", MAC_STATS_FIELD_OFF(tx_bad_to_sw)},
+       {"xgmac_rx_1731_pkt", MAC_STATS_FIELD_OFF(rx_1731_pkts)},
+       {"xgmac_rx_symbol_err_pkt", MAC_STATS_FIELD_OFF(rx_symbol_err)},
+       {"xgmac_rx_fcs_pkt", MAC_STATS_FIELD_OFF(rx_fcs_err)}
+};
+
+/**
+ *hns_xgmac_tx_enable - xgmac port tx enable
+ *@drv: mac driver
+ *@value: value of enable
+ */
+static void hns_xgmac_tx_enable(struct mac_driver *drv, u32 value)
+{
+       dsaf_set_dev_bit(drv, XGMAC_MAC_ENABLE_REG, XGMAC_ENABLE_TX_B, !!value);
+}
+
+/**
+ *hns_xgmac_rx_enable - xgmac port rx enable
+ *@drv: mac driver
+ *@value: value of enable
+ */
+static void hns_xgmac_rx_enable(struct mac_driver *drv, u32 value)
+{
+       dsaf_set_dev_bit(drv, XGMAC_MAC_ENABLE_REG, XGMAC_ENABLE_RX_B, !!value);
+}
+
+/**
+ *hns_xgmac_enable - enable xgmac port
+ *@drv: mac driver
+ *@mode: mode of mac port
+ */
+static void hns_xgmac_enable(void *mac_drv, enum mac_commom_mode mode)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       struct dsaf_device *dsaf_dev
+               = (struct dsaf_device *)dev_get_drvdata(drv->dev);
+       u32 port = drv->mac_id;
+
+       hns_dsaf_xge_core_srst_by_port(dsaf_dev, port, 1);
+       mdelay(10);
+
+       /*enable XGE rX/tX */
+       if (mode == MAC_COMM_MODE_TX) {
+               hns_xgmac_tx_enable(drv, 1);
+       } else if (mode == MAC_COMM_MODE_RX) {
+               hns_xgmac_rx_enable(drv, 1);
+       } else if (mode == MAC_COMM_MODE_RX_AND_TX) {
+               hns_xgmac_tx_enable(drv, 1);
+               hns_xgmac_rx_enable(drv, 1);
+       } else {
+               dev_err(drv->dev, "error mac mode:%d\n", mode);
+       }
+}
+
+/**
+ *hns_xgmac_disable - disable xgmac port
+ *@mac_drv: mac driver
+ *@mode: mode of mac port
+ */
+static void hns_xgmac_disable(void *mac_drv, enum mac_commom_mode mode)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       struct dsaf_device *dsaf_dev
+               = (struct dsaf_device *)dev_get_drvdata(drv->dev);
+       u32 port = drv->mac_id;
+
+       if (mode == MAC_COMM_MODE_TX) {
+               hns_xgmac_tx_enable(drv, 0);
+       } else if (mode == MAC_COMM_MODE_RX) {
+               hns_xgmac_rx_enable(drv, 0);
+       } else if (mode == MAC_COMM_MODE_RX_AND_TX) {
+               hns_xgmac_tx_enable(drv, 0);
+               hns_xgmac_rx_enable(drv, 0);
+       }
+
+       mdelay(10);
+       hns_dsaf_xge_core_srst_by_port(dsaf_dev, port, 0);
+}
+
+/**
+ *hns_xgmac_pma_fec_enable - xgmac PMA FEC enable
+ *@drv: mac driver
+ *@tx_value: tx value
+ *@rx_value: rx value
+ *return status
+ */
+static void hns_xgmac_pma_fec_enable(struct mac_driver *drv, u32 tx_value,
+                                    u32 rx_value)
+{
+       u32 origin = dsaf_read_dev(drv, XGMAC_PMA_FEC_CONTROL_REG);
+
+       dsaf_set_bit(origin, XGMAC_PMA_FEC_CTL_TX_B, !!tx_value);
+       dsaf_set_bit(origin, XGMAC_PMA_FEC_CTL_RX_B, !!rx_value);
+       dsaf_write_dev(drv, XGMAC_PMA_FEC_CONTROL_REG, origin);
+}
+
+/* clr exc irq for xge*/
+static void hns_xgmac_exc_irq_en(struct mac_driver *drv, u32 en)
+{
+       u32 clr_vlue = 0xfffffffful;
+       u32 msk_vlue = en ? 0xfffffffful : 0; /*1 is en, 0 is dis*/
+
+       dsaf_write_dev(drv, XGMAC_INT_STATUS_REG, clr_vlue);
+       dsaf_write_dev(drv, XGMAC_INT_ENABLE_REG, msk_vlue);
+}
+
+/**
+ *hns_xgmac_init - initialize XGE
+ *@mac_drv: mac driver
+ */
+static void hns_xgmac_init(void *mac_drv)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       struct dsaf_device *dsaf_dev
+               = (struct dsaf_device *)dev_get_drvdata(drv->dev);
+       u32 port = drv->mac_id;
+
+       hns_dsaf_xge_srst_by_port(dsaf_dev, port, 0);
+       mdelay(100);
+       hns_dsaf_xge_srst_by_port(dsaf_dev, port, 1);
+
+       mdelay(100);
+       hns_xgmac_exc_irq_en(drv, 0);
+
+       hns_xgmac_pma_fec_enable(drv, 0x0, 0x0);
+
+       hns_xgmac_disable(mac_drv, MAC_COMM_MODE_RX_AND_TX);
+}
+
+/**
+ *hns_xgmac_config_pad_and_crc - set xgmac pad and crc enable the same time
+ *@mac_drv: mac driver
+ *@newval:enable of pad and crc
+ */
+static void hns_xgmac_config_pad_and_crc(void *mac_drv, u8 newval)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       u32 origin = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG);
+
+       dsaf_set_bit(origin, XGMAC_CTL_TX_PAD_B, !!newval);
+       dsaf_set_bit(origin, XGMAC_CTL_TX_FCS_B, !!newval);
+       dsaf_set_bit(origin, XGMAC_CTL_RX_FCS_B, !!newval);
+       dsaf_write_dev(drv, XGMAC_MAC_CONTROL_REG, origin);
+}
+
+/**
+ *hns_xgmac_pausefrm_cfg - set pause param about xgmac
+ *@mac_drv: mac driver
+ *@newval:enable of pad and crc
+ */
+static void hns_xgmac_pausefrm_cfg(void *mac_drv, u32 rx_en, u32 tx_en)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       u32 origin = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG);
+
+       dsaf_set_bit(origin, XGMAC_PAUSE_CTL_TX_B, !!tx_en);
+       dsaf_set_bit(origin, XGMAC_PAUSE_CTL_RX_B, !!rx_en);
+       dsaf_write_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG, origin);
+}
+
+static void hns_xgmac_set_pausefrm_mac_addr(void *mac_drv, char *mac_addr)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       u32 high_val = mac_addr[1] | (mac_addr[0] << 8);
+       u32 low_val = mac_addr[5] | (mac_addr[4] << 8)
+               | (mac_addr[3] << 16) | (mac_addr[2] << 24);
+       dsaf_write_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG, low_val);
+       dsaf_write_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG, high_val);
+}
+
+/**
+ *hns_xgmac_set_rx_ignore_pause_frames - set rx pause param about xgmac
+ *@mac_drv: mac driver
+ *@enable:enable rx pause param
+ */
+static void hns_xgmac_set_rx_ignore_pause_frames(void *mac_drv, u32 enable)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       dsaf_set_dev_bit(drv, XGMAC_MAC_PAUSE_CTRL_REG,
+                        XGMAC_PAUSE_CTL_RX_B, !!enable);
+}
+
+/**
+ *hns_xgmac_set_tx_auto_pause_frames - set tx pause param about xgmac
+ *@mac_drv: mac driver
+ *@enable:enable tx pause param
+ */
+static void hns_xgmac_set_tx_auto_pause_frames(void *mac_drv, u16 enable)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       dsaf_set_dev_bit(drv, XGMAC_MAC_PAUSE_CTRL_REG,
+                        XGMAC_PAUSE_CTL_TX_B, !!enable);
+
+       /*if enable is not zero ,set tx pause time */
+       if (enable)
+               dsaf_write_dev(drv, XGMAC_MAC_PAUSE_TIME_REG, enable);
+}
+
+/**
+ *hns_xgmac_get_id - get xgmac port id
+ *@mac_drv: mac driver
+ *@newval:xgmac max frame length
+ */
+static void hns_xgmac_get_id(void *mac_drv, u8 *mac_id)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       *mac_id = drv->mac_id;
+}
+
+/**
+ *hns_xgmac_config_max_frame_length - set xgmac max frame length
+ *@mac_drv: mac driver
+ *@newval:xgmac max frame length
+ */
+static void hns_xgmac_config_max_frame_length(void *mac_drv, u16 newval)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       dsaf_write_dev(drv, XGMAC_MAC_MAX_PKT_SIZE_REG, newval);
+}
+
+void hns_xgmac_update_stats(void *mac_drv)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       struct mac_hw_stats *hw_stats = &drv->mac_cb->hw_stats;
+
+       /* TX */
+       hw_stats->tx_fragment_err
+               = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_FRAGMENT);
+       hw_stats->tx_undersize
+               = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERSIZE);
+       hw_stats->tx_under_min_pkts
+               = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERMIN);
+       hw_stats->tx_64bytes = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_64OCTETS);
+       hw_stats->tx_65to127
+               = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_65TO127OCTETS);
+       hw_stats->tx_128to255
+               = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_128TO255OCTETS);
+       hw_stats->tx_256to511
+               = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_256TO511OCTETS);
+       hw_stats->tx_512to1023
+               = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_512TO1023OCTETS);
+       hw_stats->tx_1024to1518
+               = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1024TO1518OCTETS);
+       hw_stats->tx_1519tomax
+               = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETS);
+       hw_stats->tx_1519tomax_good
+               = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETSOK);
+       hw_stats->tx_oversize = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_OVERSIZE);
+       hw_stats->tx_jabber_err = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_JABBER);
+       hw_stats->tx_good_pkts = hns_mac_reg_read64(drv, XGMAC_TX_GOODPKTS);
+       hw_stats->tx_good_bytes = hns_mac_reg_read64(drv, XGMAC_TX_GOODOCTETS);
+       hw_stats->tx_total_pkts = hns_mac_reg_read64(drv, XGMAC_TX_TOTAL_PKTS);
+       hw_stats->tx_total_bytes
+               = hns_mac_reg_read64(drv, XGMAC_TX_TOTALOCTETS);
+       hw_stats->tx_uc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_UNICASTPKTS);
+       hw_stats->tx_mc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_MULTICASTPKTS);
+       hw_stats->tx_bc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_BROADCASTPKTS);
+       hw_stats->tx_pfc_tc0 = hns_mac_reg_read64(drv, XGMAC_TX_PRI0PAUSEPKTS);
+       hw_stats->tx_pfc_tc1 = hns_mac_reg_read64(drv, XGMAC_TX_PRI1PAUSEPKTS);
+       hw_stats->tx_pfc_tc2 = hns_mac_reg_read64(drv, XGMAC_TX_PRI2PAUSEPKTS);
+       hw_stats->tx_pfc_tc3 = hns_mac_reg_read64(drv, XGMAC_TX_PRI3PAUSEPKTS);
+       hw_stats->tx_pfc_tc4 = hns_mac_reg_read64(drv, XGMAC_TX_PRI4PAUSEPKTS);
+       hw_stats->tx_pfc_tc5 = hns_mac_reg_read64(drv, XGMAC_TX_PRI5PAUSEPKTS);
+       hw_stats->tx_pfc_tc6 = hns_mac_reg_read64(drv, XGMAC_TX_PRI6PAUSEPKTS);
+       hw_stats->tx_pfc_tc7 = hns_mac_reg_read64(drv, XGMAC_TX_PRI7PAUSEPKTS);
+       hw_stats->tx_ctrl = hns_mac_reg_read64(drv, XGMAC_TX_MACCTRLPKTS);
+       hw_stats->tx_1731_pkts = hns_mac_reg_read64(drv, XGMAC_TX_1731PKTS);
+       hw_stats->tx_1588_pkts = hns_mac_reg_read64(drv, XGMAC_TX_1588PKTS);
+       hw_stats->rx_good_from_sw
+               = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPGOODPKTS);
+       hw_stats->rx_bad_from_sw
+               = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPBADPKTS);
+       hw_stats->tx_bad_pkts = hns_mac_reg_read64(drv, XGMAC_TX_ERRALLPKTS);
+
+       /* RX */
+       hw_stats->rx_fragment_err
+               = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_FRAGMENT);
+       hw_stats->rx_undersize
+               = hns_mac_reg_read64(drv, XGMAC_RX_PKTSUNDERSIZE);
+       hw_stats->rx_under_min
+               = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_UNDERMIN);
+       hw_stats->rx_64bytes = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_64OCTETS);
+       hw_stats->rx_65to127
+               = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_65TO127OCTETS);
+       hw_stats->rx_128to255
+               = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_128TO255OCTETS);
+       hw_stats->rx_256to511
+               = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_256TO511OCTETS);
+       hw_stats->rx_512to1023
+               = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_512TO1023OCTETS);
+       hw_stats->rx_1024to1518
+               = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1024TO1518OCTETS);
+       hw_stats->rx_1519tomax
+               = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETS);
+       hw_stats->rx_1519tomax_good
+               = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETSOK);
+       hw_stats->rx_oversize = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_OVERSIZE);
+       hw_stats->rx_jabber_err = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_JABBER);
+       hw_stats->rx_good_pkts = hns_mac_reg_read64(drv, XGMAC_RX_GOODPKTS);
+       hw_stats->rx_good_bytes = hns_mac_reg_read64(drv, XGMAC_RX_GOODOCTETS);
+       hw_stats->rx_total_pkts = hns_mac_reg_read64(drv, XGMAC_RX_TOTAL_PKTS);
+       hw_stats->rx_total_bytes
+               = hns_mac_reg_read64(drv, XGMAC_RX_TOTALOCTETS);
+       hw_stats->rx_uc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_UNICASTPKTS);
+       hw_stats->rx_mc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_MULTICASTPKTS);
+       hw_stats->rx_bc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_BROADCASTPKTS);
+       hw_stats->rx_pfc_tc0 = hns_mac_reg_read64(drv, XGMAC_RX_PRI0PAUSEPKTS);
+       hw_stats->rx_pfc_tc1 = hns_mac_reg_read64(drv, XGMAC_RX_PRI1PAUSEPKTS);
+       hw_stats->rx_pfc_tc2 = hns_mac_reg_read64(drv, XGMAC_RX_PRI2PAUSEPKTS);
+       hw_stats->rx_pfc_tc3 = hns_mac_reg_read64(drv, XGMAC_RX_PRI3PAUSEPKTS);
+       hw_stats->rx_pfc_tc4 = hns_mac_reg_read64(drv, XGMAC_RX_PRI4PAUSEPKTS);
+       hw_stats->rx_pfc_tc5 = hns_mac_reg_read64(drv, XGMAC_RX_PRI5PAUSEPKTS);
+       hw_stats->rx_pfc_tc6 = hns_mac_reg_read64(drv, XGMAC_RX_PRI6PAUSEPKTS);
+       hw_stats->rx_pfc_tc7 = hns_mac_reg_read64(drv, XGMAC_RX_PRI7PAUSEPKTS);
+
+       hw_stats->rx_unknown_ctrl
+               = hns_mac_reg_read64(drv, XGMAC_RX_MACCTRLPKTS);
+       hw_stats->tx_good_to_sw
+               = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPGOODPKTS);
+       hw_stats->tx_bad_to_sw
+               = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPBADPKTS);
+       hw_stats->rx_1731_pkts = hns_mac_reg_read64(drv, XGMAC_RX_1731PKTS);
+       hw_stats->rx_symbol_err
+               = hns_mac_reg_read64(drv, XGMAC_RX_SYMBOLERRPKTS);
+       hw_stats->rx_fcs_err = hns_mac_reg_read64(drv, XGMAC_RX_FCSERRPKTS);
+}
+
+/**
+ *hns_xgmac_free - free xgmac driver
+ *@mac_drv: mac driver
+ */
+static void hns_xgmac_free(void *mac_drv)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       struct dsaf_device *dsaf_dev
+               = (struct dsaf_device *)dev_get_drvdata(drv->dev);
+
+       u32 mac_id = drv->mac_id;
+
+       hns_dsaf_xge_srst_by_port(dsaf_dev, mac_id, 0);
+}
+
+/**
+ *hns_xgmac_get_info - get xgmac information
+ *@mac_drv: mac driver
+ *@mac_info:mac information
+ */
+static void hns_xgmac_get_info(void *mac_drv, struct mac_info *mac_info)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       u32 pause_time, pause_ctrl, port_mode, ctrl_val;
+
+       ctrl_val = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG);
+       mac_info->pad_and_crc_en = dsaf_get_bit(ctrl_val, XGMAC_CTL_TX_PAD_B);
+       mac_info->auto_neg = 0;
+
+       pause_time = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_TIME_REG);
+       mac_info->tx_pause_time = pause_time;
+
+       port_mode = dsaf_read_dev(drv, XGMAC_PORT_MODE_REG);
+       mac_info->port_en = dsaf_get_field(port_mode, XGMAC_PORT_MODE_TX_M,
+                                          XGMAC_PORT_MODE_TX_S) &&
+                               dsaf_get_field(port_mode, XGMAC_PORT_MODE_RX_M,
+                                              XGMAC_PORT_MODE_RX_S);
+       mac_info->duplex = 1;
+       mac_info->speed = MAC_SPEED_10000;
+
+       pause_ctrl = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG);
+       mac_info->rx_pause_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_RX_B);
+       mac_info->tx_pause_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_TX_B);
+}
+
+/**
+ *hns_xgmac_get_pausefrm_cfg - get xgmac pause param
+ *@mac_drv: mac driver
+ *@rx_en:xgmac rx pause enable
+ *@tx_en:xgmac tx pause enable
+ */
+static void hns_xgmac_get_pausefrm_cfg(void *mac_drv, u32 *rx_en, u32 *tx_en)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       u32 pause_ctrl;
+
+       pause_ctrl = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG);
+       *rx_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_RX_B);
+       *tx_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_TX_B);
+}
+
+/**
+ *hns_xgmac_get_link_status - get xgmac link status
+ *@mac_drv: mac driver
+ *@link_stat: xgmac link stat
+ */
+static void hns_xgmac_get_link_status(void *mac_drv, u32 *link_stat)
+{
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+
+       *link_stat = dsaf_read_dev(drv, XGMAC_LINK_STATUS_REG);
+}
+
+/**
+ *hns_xgmac_get_regs - dump xgmac regs
+ *@mac_drv: mac driver
+ *@cmd:ethtool cmd
+ *@data:data for value of regs
+ */
+static void hns_xgmac_get_regs(void *mac_drv, void *data)
+{
+       u32 i = 0;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       u32 *regs = data;
+       u64 qtmp;
+
+       /* base config registers */
+       regs[0] = dsaf_read_dev(drv, XGMAC_INT_STATUS_REG);
+       regs[1] = dsaf_read_dev(drv, XGMAC_INT_ENABLE_REG);
+       regs[2] = dsaf_read_dev(drv, XGMAC_INT_SET_REG);
+       regs[3] = dsaf_read_dev(drv, XGMAC_IERR_U_INFO_REG);
+       regs[4] = dsaf_read_dev(drv, XGMAC_OVF_INFO_REG);
+       regs[5] = dsaf_read_dev(drv, XGMAC_OVF_CNT_REG);
+       regs[6] = dsaf_read_dev(drv, XGMAC_PORT_MODE_REG);
+       regs[7] = dsaf_read_dev(drv, XGMAC_CLK_ENABLE_REG);
+       regs[8] = dsaf_read_dev(drv, XGMAC_RESET_REG);
+       regs[9] = dsaf_read_dev(drv, XGMAC_LINK_CONTROL_REG);
+       regs[10] = dsaf_read_dev(drv, XGMAC_LINK_STATUS_REG);
+
+       regs[11] = dsaf_read_dev(drv, XGMAC_SPARE_REG);
+       regs[12] = dsaf_read_dev(drv, XGMAC_SPARE_CNT_REG);
+       regs[13] = dsaf_read_dev(drv, XGMAC_MAC_ENABLE_REG);
+       regs[14] = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG);
+       regs[15] = dsaf_read_dev(drv, XGMAC_MAC_IPG_REG);
+       regs[16] = dsaf_read_dev(drv, XGMAC_MAC_MSG_CRC_EN_REG);
+       regs[17] = dsaf_read_dev(drv, XGMAC_MAC_MSG_IMG_REG);
+       regs[18] = dsaf_read_dev(drv, XGMAC_MAC_MSG_FC_CFG_REG);
+       regs[19] = dsaf_read_dev(drv, XGMAC_MAC_MSG_TC_CFG_REG);
+       regs[20] = dsaf_read_dev(drv, XGMAC_MAC_PAD_SIZE_REG);
+       regs[21] = dsaf_read_dev(drv, XGMAC_MAC_MIN_PKT_SIZE_REG);
+       regs[22] = dsaf_read_dev(drv, XGMAC_MAC_MAX_PKT_SIZE_REG);
+       regs[23] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG);
+       regs[24] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_TIME_REG);
+       regs[25] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_GAP_REG);
+       regs[26] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG);
+       regs[27] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG);
+       regs[28] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_PEER_MAC_H_REG);
+       regs[29] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_PEER_MAC_L_REG);
+       regs[30] = dsaf_read_dev(drv, XGMAC_MAC_PFC_PRI_EN_REG);
+       regs[31] = dsaf_read_dev(drv, XGMAC_MAC_1588_CTRL_REG);
+       regs[32] = dsaf_read_dev(drv, XGMAC_MAC_1588_TX_PORT_DLY_REG);
+       regs[33] = dsaf_read_dev(drv, XGMAC_MAC_1588_RX_PORT_DLY_REG);
+       regs[34] = dsaf_read_dev(drv, XGMAC_MAC_1588_ASYM_DLY_REG);
+       regs[35] = dsaf_read_dev(drv, XGMAC_MAC_1588_ADJUST_CFG_REG);
+
+       regs[36] = dsaf_read_dev(drv, XGMAC_MAC_Y1731_ETH_TYPE_REG);
+       regs[37] = dsaf_read_dev(drv, XGMAC_MAC_MIB_CONTROL_REG);
+       regs[38] = dsaf_read_dev(drv, XGMAC_MAC_WAN_RATE_ADJUST_REG);
+       regs[39] = dsaf_read_dev(drv, XGMAC_MAC_TX_ERR_MARK_REG);
+       regs[40] = dsaf_read_dev(drv, XGMAC_MAC_TX_LF_RF_CONTROL_REG);
+       regs[41] = dsaf_read_dev(drv, XGMAC_MAC_RX_LF_RF_STATUS_REG);
+       regs[42] = dsaf_read_dev(drv, XGMAC_MAC_TX_RUNT_PKT_CNT_REG);
+       regs[43] = dsaf_read_dev(drv, XGMAC_MAC_RX_RUNT_PKT_CNT_REG);
+       regs[44] = dsaf_read_dev(drv, XGMAC_MAC_RX_PREAM_ERR_PKT_CNT_REG);
+       regs[45] = dsaf_read_dev(drv, XGMAC_MAC_TX_LF_RF_TERM_PKT_CNT_REG);
+       regs[46] = dsaf_read_dev(drv, XGMAC_MAC_TX_SN_MISMATCH_PKT_CNT_REG);
+       regs[47] = dsaf_read_dev(drv, XGMAC_MAC_RX_ERR_MSG_CNT_REG);
+       regs[48] = dsaf_read_dev(drv, XGMAC_MAC_RX_ERR_EFD_CNT_REG);
+       regs[49] = dsaf_read_dev(drv, XGMAC_MAC_ERR_INFO_REG);
+       regs[50] = dsaf_read_dev(drv, XGMAC_MAC_DBG_INFO_REG);
+
+       regs[51] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SYNC_THD_REG);
+       regs[52] = dsaf_read_dev(drv, XGMAC_PCS_STATUS1_REG);
+       regs[53] = dsaf_read_dev(drv, XGMAC_PCS_BASER_STATUS1_REG);
+       regs[54] = dsaf_read_dev(drv, XGMAC_PCS_BASER_STATUS2_REG);
+       regs[55] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDA_0_REG);
+       regs[56] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDA_1_REG);
+       regs[57] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDB_0_REG);
+       regs[58] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDB_1_REG);
+       regs[59] = dsaf_read_dev(drv, XGMAC_PCS_BASER_TEST_CONTROL_REG);
+       regs[60] = dsaf_read_dev(drv, XGMAC_PCS_BASER_TEST_ERR_CNT_REG);
+       regs[61] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO_REG);
+       regs[62] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO1_REG);
+       regs[63] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO2_REG);
+       regs[64] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO3_REG);
+
+       regs[65] = dsaf_read_dev(drv, XGMAC_PMA_ENABLE_REG);
+       regs[66] = dsaf_read_dev(drv, XGMAC_PMA_CONTROL_REG);
+       regs[67] = dsaf_read_dev(drv, XGMAC_PMA_SIGNAL_STATUS_REG);
+       regs[68] = dsaf_read_dev(drv, XGMAC_PMA_DBG_INFO_REG);
+       regs[69] = dsaf_read_dev(drv, XGMAC_PMA_FEC_ABILITY_REG);
+       regs[70] = dsaf_read_dev(drv, XGMAC_PMA_FEC_CONTROL_REG);
+       regs[71] = dsaf_read_dev(drv, XGMAC_PMA_FEC_CORR_BLOCK_CNT__REG);
+       regs[72] = dsaf_read_dev(drv, XGMAC_PMA_FEC_UNCORR_BLOCK_CNT__REG);
+
+       /* status registers */
+#define hns_xgmac_cpy_q(p, q) \
+       do {\
+               *(p) = (u32)(q);\
+               *((p) + 1) = (u32)((q) >> 32);\
+       } while (0)
+
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_FRAGMENT);
+       hns_xgmac_cpy_q(&regs[73], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERSIZE);
+       hns_xgmac_cpy_q(&regs[75], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERMIN);
+       hns_xgmac_cpy_q(&regs[77], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_64OCTETS);
+       hns_xgmac_cpy_q(&regs[79], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_65TO127OCTETS);
+       hns_xgmac_cpy_q(&regs[81], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_128TO255OCTETS);
+       hns_xgmac_cpy_q(&regs[83], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_256TO511OCTETS);
+       hns_xgmac_cpy_q(&regs[85], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_512TO1023OCTETS);
+       hns_xgmac_cpy_q(&regs[87], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1024TO1518OCTETS);
+       hns_xgmac_cpy_q(&regs[89], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETS);
+       hns_xgmac_cpy_q(&regs[91], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETSOK);
+       hns_xgmac_cpy_q(&regs[93], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_OVERSIZE);
+       hns_xgmac_cpy_q(&regs[95], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_JABBER);
+       hns_xgmac_cpy_q(&regs[97], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_GOODPKTS);
+       hns_xgmac_cpy_q(&regs[99], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_GOODOCTETS);
+       hns_xgmac_cpy_q(&regs[101], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_TOTAL_PKTS);
+       hns_xgmac_cpy_q(&regs[103], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_TOTALOCTETS);
+       hns_xgmac_cpy_q(&regs[105], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_UNICASTPKTS);
+       hns_xgmac_cpy_q(&regs[107], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_MULTICASTPKTS);
+       hns_xgmac_cpy_q(&regs[109], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_BROADCASTPKTS);
+       hns_xgmac_cpy_q(&regs[111], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI0PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[113], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI1PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[115], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI2PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[117], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI3PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[119], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI4PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[121], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI5PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[123], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI6PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[125], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI7PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[127], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_MACCTRLPKTS);
+       hns_xgmac_cpy_q(&regs[129], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_1731PKTS);
+       hns_xgmac_cpy_q(&regs[131], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_1588PKTS);
+       hns_xgmac_cpy_q(&regs[133], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPGOODPKTS);
+       hns_xgmac_cpy_q(&regs[135], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPBADPKTS);
+       hns_xgmac_cpy_q(&regs[137], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_ERRALLPKTS);
+       hns_xgmac_cpy_q(&regs[139], qtmp);
+
+       /* RX */
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_FRAGMENT);
+       hns_xgmac_cpy_q(&regs[141], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTSUNDERSIZE);
+       hns_xgmac_cpy_q(&regs[143], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_UNDERMIN);
+       hns_xgmac_cpy_q(&regs[145], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_64OCTETS);
+       hns_xgmac_cpy_q(&regs[147], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_65TO127OCTETS);
+       hns_xgmac_cpy_q(&regs[149], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_128TO255OCTETS);
+       hns_xgmac_cpy_q(&regs[151], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_256TO511OCTETS);
+       hns_xgmac_cpy_q(&regs[153], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_512TO1023OCTETS);
+       hns_xgmac_cpy_q(&regs[155], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1024TO1518OCTETS);
+       hns_xgmac_cpy_q(&regs[157], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETS);
+       hns_xgmac_cpy_q(&regs[159], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETSOK);
+       hns_xgmac_cpy_q(&regs[161], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_OVERSIZE);
+       hns_xgmac_cpy_q(&regs[163], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_JABBER);
+       hns_xgmac_cpy_q(&regs[165], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_GOODPKTS);
+       hns_xgmac_cpy_q(&regs[167], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_GOODOCTETS);
+       hns_xgmac_cpy_q(&regs[169], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_TOTAL_PKTS);
+       hns_xgmac_cpy_q(&regs[171], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_TOTALOCTETS);
+       hns_xgmac_cpy_q(&regs[173], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_UNICASTPKTS);
+       hns_xgmac_cpy_q(&regs[175], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_MULTICASTPKTS);
+       hns_xgmac_cpy_q(&regs[177], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_BROADCASTPKTS);
+       hns_xgmac_cpy_q(&regs[179], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI0PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[181], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI1PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[183], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI2PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[185], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI3PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[187], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI4PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[189], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI5PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[191], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI6PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[193], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI7PAUSEPKTS);
+       hns_xgmac_cpy_q(&regs[195], qtmp);
+
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_MACCTRLPKTS);
+       hns_xgmac_cpy_q(&regs[197], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPGOODPKTS);
+       hns_xgmac_cpy_q(&regs[199], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPBADPKTS);
+       hns_xgmac_cpy_q(&regs[201], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_1731PKTS);
+       hns_xgmac_cpy_q(&regs[203], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_SYMBOLERRPKTS);
+       hns_xgmac_cpy_q(&regs[205], qtmp);
+       qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FCSERRPKTS);
+       hns_xgmac_cpy_q(&regs[207], qtmp);
+
+       /* mark end of mac regs */
+       for (i = 208; i < 214; i++)
+               regs[i] = 0xaaaaaaaa;
+}
+
+/**
+ *hns_xgmac_get_stats - get xgmac statistic
+ *@mac_drv: mac driver
+ *@data:data for value of stats regs
+ */
+static void hns_xgmac_get_stats(void *mac_drv, u64 *data)
+{
+       u32 i;
+       u64 *buf = data;
+       struct mac_driver *drv = (struct mac_driver *)mac_drv;
+       struct mac_hw_stats *hw_stats = NULL;
+
+       hw_stats = &drv->mac_cb->hw_stats;
+
+       for (i = 0; i < ARRAY_SIZE(g_xgmac_stats_string); i++) {
+               buf[i] = DSAF_STATS_READ(hw_stats,
+                       g_xgmac_stats_string[i].offset);
+       }
+}
+
+/**
+ *hns_xgmac_get_strings - get xgmac strings name
+ *@stringset: type of values in data
+ *@data:data for value of string name
+ */
+static void hns_xgmac_get_strings(u32 stringset, u8 *data)
+{
+       char *buff = (char *)data;
+       u32 i;
+
+       if (stringset != ETH_SS_STATS)
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(g_xgmac_stats_string); i++) {
+               snprintf(buff, ETH_GSTRING_LEN, g_xgmac_stats_string[i].desc);
+               buff = buff + ETH_GSTRING_LEN;
+       }
+}
+
+/**
+ *hns_xgmac_get_sset_count - get xgmac string set count
+ *@stringset: type of values in data
+ *return xgmac string set count
+ */
+static int hns_xgmac_get_sset_count(int stringset)
+{
+       if (stringset == ETH_SS_STATS)
+               return ARRAY_SIZE(g_xgmac_stats_string);
+
+       return 0;
+}
+
+/**
+ *hns_xgmac_get_regs_count - get xgmac regs count
+ *return xgmac regs count
+ */
+static int hns_xgmac_get_regs_count(void)
+{
+       return ETH_XGMAC_DUMP_NUM;
+}
+
+void *hns_xgmac_config(struct hns_mac_cb *mac_cb, struct mac_params *mac_param)
+{
+       struct mac_driver *mac_drv;
+
+       mac_drv = devm_kzalloc(mac_cb->dev, sizeof(*mac_drv), GFP_KERNEL);
+       if (!mac_drv)
+               return NULL;
+
+       mac_drv->mac_init = hns_xgmac_init;
+       mac_drv->mac_enable = hns_xgmac_enable;
+       mac_drv->mac_disable = hns_xgmac_disable;
+
+       mac_drv->mac_id = mac_param->mac_id;
+       mac_drv->mac_mode = mac_param->mac_mode;
+       mac_drv->io_base = mac_param->vaddr;
+       mac_drv->dev = mac_param->dev;
+       mac_drv->mac_cb = mac_cb;
+
+       mac_drv->set_mac_addr = hns_xgmac_set_pausefrm_mac_addr;
+       mac_drv->set_an_mode = NULL;
+       mac_drv->config_loopback = NULL;
+       mac_drv->config_pad_and_crc = hns_xgmac_config_pad_and_crc;
+       mac_drv->config_half_duplex = NULL;
+       mac_drv->set_rx_ignore_pause_frames =
+               hns_xgmac_set_rx_ignore_pause_frames;
+       mac_drv->mac_get_id = hns_xgmac_get_id;
+       mac_drv->mac_free = hns_xgmac_free;
+       mac_drv->adjust_link = NULL;
+       mac_drv->set_tx_auto_pause_frames = hns_xgmac_set_tx_auto_pause_frames;
+       mac_drv->config_max_frame_length = hns_xgmac_config_max_frame_length;
+       mac_drv->mac_pausefrm_cfg = hns_xgmac_pausefrm_cfg;
+       mac_drv->autoneg_stat = NULL;
+       mac_drv->get_info = hns_xgmac_get_info;
+       mac_drv->get_pause_enable = hns_xgmac_get_pausefrm_cfg;
+       mac_drv->get_link_status = hns_xgmac_get_link_status;
+       mac_drv->get_regs = hns_xgmac_get_regs;
+       mac_drv->get_ethtool_stats = hns_xgmac_get_stats;
+       mac_drv->get_sset_count = hns_xgmac_get_sset_count;
+       mac_drv->get_regs_count = hns_xgmac_get_regs_count;
+       mac_drv->get_strings = hns_xgmac_get_strings;
+       mac_drv->update_stats = hns_xgmac_update_stats;
+
+       return (void *)mac_drv;
+}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h
new file mode 100644 (file)
index 0000000..139f729
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 _HNS_XGMAC_H
+#define _HNS_XGMAC_H
+
+#define ETH_XGMAC_DUMP_NUM             (214)
+
+#endif
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
new file mode 100644 (file)
index 0000000..08cef0d
--- /dev/null
@@ -0,0 +1,1642 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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/clk.h>
+#include <linux/cpumask.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+
+#include "hnae.h"
+#include "hns_enet.h"
+
+#define NIC_MAX_Q_PER_VF 16
+#define HNS_NIC_TX_TIMEOUT (5 * HZ)
+
+#define SERVICE_TIMER_HZ (1 * HZ)
+
+#define NIC_TX_CLEAN_MAX_NUM 256
+#define NIC_RX_CLEAN_MAX_NUM 64
+
+#define RCB_IRQ_NOT_INITED 0
+#define RCB_IRQ_INITED 1
+
+static void fill_desc(struct hnae_ring *ring, void *priv,
+                     int size, dma_addr_t dma, int frag_end,
+                     int buf_num, enum hns_desc_type type)
+{
+       struct hnae_desc *desc = &ring->desc[ring->next_to_use];
+       struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use];
+       struct sk_buff *skb;
+       __be16 protocol;
+       u32 ip_offset;
+       u32 asid_bufnum_pid = 0;
+       u32 flag_ipoffset = 0;
+
+       desc_cb->priv = priv;
+       desc_cb->length = size;
+       desc_cb->dma = dma;
+       desc_cb->type = type;
+
+       desc->addr = cpu_to_le64(dma);
+       desc->tx.send_size = cpu_to_le16((u16)size);
+
+       /*config bd buffer end */
+       flag_ipoffset |= 1 << HNS_TXD_VLD_B;
+
+       asid_bufnum_pid |= buf_num << HNS_TXD_BUFNUM_S;
+
+       if (type == DESC_TYPE_SKB) {
+               skb = (struct sk_buff *)priv;
+
+               if (skb->ip_summed == CHECKSUM_PARTIAL) {
+                       protocol = skb->protocol;
+                       ip_offset = ETH_HLEN;
+
+                       /*if it is a SW VLAN check the next protocol*/
+                       if (protocol == htons(ETH_P_8021Q)) {
+                               ip_offset += VLAN_HLEN;
+                               protocol = vlan_get_protocol(skb);
+                               skb->protocol = protocol;
+                       }
+
+                       if (skb->protocol == htons(ETH_P_IP)) {
+                               flag_ipoffset |= 1 << HNS_TXD_L3CS_B;
+                               /* check for tcp/udp header */
+                               flag_ipoffset |= 1 << HNS_TXD_L4CS_B;
+
+                       } else if (skb->protocol == htons(ETH_P_IPV6)) {
+                               /* ipv6 has not l3 cs, check for L4 header */
+                               flag_ipoffset |= 1 << HNS_TXD_L4CS_B;
+                       }
+
+                       flag_ipoffset |= ip_offset << HNS_TXD_IPOFFSET_S;
+               }
+       }
+
+       flag_ipoffset |= frag_end << HNS_TXD_FE_B;
+
+       desc->tx.asid_bufnum_pid = cpu_to_le16(asid_bufnum_pid);
+       desc->tx.flag_ipoffset = cpu_to_le32(flag_ipoffset);
+
+       ring_ptr_move_fw(ring, next_to_use);
+}
+
+static void unfill_desc(struct hnae_ring *ring)
+{
+       ring_ptr_move_bw(ring, next_to_use);
+}
+
+int hns_nic_net_xmit_hw(struct net_device *ndev,
+                       struct sk_buff *skb,
+                       struct hns_nic_ring_data *ring_data)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct device *dev = priv->dev;
+       struct hnae_ring *ring = ring_data->ring;
+       struct netdev_queue *dev_queue;
+       struct skb_frag_struct *frag;
+       int buf_num;
+       dma_addr_t dma;
+       int size, next_to_use;
+       int i, j;
+       struct sk_buff *new_skb;
+
+       assert(ring->max_desc_num_per_pkt <= ring->desc_num);
+
+       /* no. of segments (plus a header) */
+       buf_num = skb_shinfo(skb)->nr_frags + 1;
+
+       if (unlikely(buf_num > ring->max_desc_num_per_pkt)) {
+               if (ring_space(ring) < 1) {
+                       ring->stats.tx_busy++;
+                       goto out_net_tx_busy;
+               }
+
+               new_skb = skb_copy(skb, GFP_ATOMIC);
+               if (!new_skb) {
+                       ring->stats.sw_err_cnt++;
+                       netdev_err(ndev, "no memory to xmit!\n");
+                       goto out_err_tx_ok;
+               }
+
+               dev_kfree_skb_any(skb);
+               skb = new_skb;
+               buf_num = 1;
+               assert(skb_shinfo(skb)->nr_frags == 1);
+       } else if (buf_num > ring_space(ring)) {
+               ring->stats.tx_busy++;
+               goto out_net_tx_busy;
+       }
+       next_to_use = ring->next_to_use;
+
+       /* fill the first part */
+       size = skb_headlen(skb);
+       dma = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE);
+       if (dma_mapping_error(dev, dma)) {
+               netdev_err(ndev, "TX head DMA map failed\n");
+               ring->stats.sw_err_cnt++;
+               goto out_err_tx_ok;
+       }
+       fill_desc(ring, skb, size, dma, buf_num == 1 ? 1 : 0, buf_num,
+                 DESC_TYPE_SKB);
+
+       /* fill the fragments */
+       for (i = 1; i < buf_num; i++) {
+               frag = &skb_shinfo(skb)->frags[i - 1];
+               size = skb_frag_size(frag);
+               dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE);
+               if (dma_mapping_error(dev, dma)) {
+                       netdev_err(ndev, "TX frag(%d) DMA map failed\n", i);
+                       ring->stats.sw_err_cnt++;
+                       goto out_map_frag_fail;
+               }
+               fill_desc(ring, skb_frag_page(frag), size, dma,
+                         buf_num - 1 == i ? 1 : 0, buf_num, DESC_TYPE_PAGE);
+       }
+
+       /*complete translate all packets*/
+       dev_queue = netdev_get_tx_queue(ndev, skb->queue_mapping);
+       netdev_tx_sent_queue(dev_queue, skb->len);
+
+       wmb(); /* commit all data before submit */
+       assert(skb->queue_mapping < priv->ae_handle->q_num);
+       hnae_queue_xmit(priv->ae_handle->qs[skb->queue_mapping], buf_num);
+       ring->stats.tx_pkts++;
+       ring->stats.tx_bytes += skb->len;
+
+       return NETDEV_TX_OK;
+
+out_map_frag_fail:
+
+       for (j = i - 1; j > 0; j--) {
+               unfill_desc(ring);
+               next_to_use = ring->next_to_use;
+               dma_unmap_page(dev, ring->desc_cb[next_to_use].dma,
+                              ring->desc_cb[next_to_use].length,
+                              DMA_TO_DEVICE);
+       }
+
+       unfill_desc(ring);
+       next_to_use = ring->next_to_use;
+       dma_unmap_single(dev, ring->desc_cb[next_to_use].dma,
+                        ring->desc_cb[next_to_use].length, DMA_TO_DEVICE);
+
+out_err_tx_ok:
+
+       dev_kfree_skb_any(skb);
+       return NETDEV_TX_OK;
+
+out_net_tx_busy:
+
+       netif_stop_subqueue(ndev, skb->queue_mapping);
+
+       /* Herbert's original patch had:
+        *  smp_mb__after_netif_stop_queue();
+        * but since that doesn't exist yet, just open code it.
+        */
+       smp_mb();
+       return NETDEV_TX_BUSY;
+}
+
+/**
+ * hns_nic_get_headlen - determine size of header for RSC/LRO/GRO/FCOE
+ * @data: pointer to the start of the headers
+ * @max: total length of section to find headers in
+ *
+ * This function is meant to determine the length of headers that will
+ * be recognized by hardware for LRO, GRO, and RSC offloads.  The main
+ * motivation of doing this is to only perform one pull for IPv4 TCP
+ * packets so that we can do basic things like calculating the gso_size
+ * based on the average data per packet.
+ **/
+static unsigned int hns_nic_get_headlen(unsigned char *data, u32 flag,
+                                       unsigned int max_size)
+{
+       unsigned char *network;
+       u8 hlen;
+
+       /* this should never happen, but better safe than sorry */
+       if (max_size < ETH_HLEN)
+               return max_size;
+
+       /* initialize network frame pointer */
+       network = data;
+
+       /* set first protocol and move network header forward */
+       network += ETH_HLEN;
+
+       /* handle any vlan tag if present */
+       if (hnae_get_field(flag, HNS_RXD_VLAN_M, HNS_RXD_VLAN_S)
+               == HNS_RX_FLAG_VLAN_PRESENT) {
+               if ((typeof(max_size))(network - data) > (max_size - VLAN_HLEN))
+                       return max_size;
+
+               network += VLAN_HLEN;
+       }
+
+       /* handle L3 protocols */
+       if (hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S)
+               == HNS_RX_FLAG_L3ID_IPV4) {
+               if ((typeof(max_size))(network - data) >
+                   (max_size - sizeof(struct iphdr)))
+                       return max_size;
+
+               /* access ihl as a u8 to avoid unaligned access on ia64 */
+               hlen = (network[0] & 0x0F) << 2;
+
+               /* verify hlen meets minimum size requirements */
+               if (hlen < sizeof(struct iphdr))
+                       return network - data;
+
+               /* record next protocol if header is present */
+       } else if (hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S)
+               == HNS_RX_FLAG_L3ID_IPV6) {
+               if ((typeof(max_size))(network - data) >
+                   (max_size - sizeof(struct ipv6hdr)))
+                       return max_size;
+
+               /* record next protocol */
+               hlen = sizeof(struct ipv6hdr);
+       } else {
+               return network - data;
+       }
+
+       /* relocate pointer to start of L4 header */
+       network += hlen;
+
+       /* finally sort out TCP/UDP */
+       if (hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S)
+               == HNS_RX_FLAG_L4ID_TCP) {
+               if ((typeof(max_size))(network - data) >
+                   (max_size - sizeof(struct tcphdr)))
+                       return max_size;
+
+               /* access doff as a u8 to avoid unaligned access on ia64 */
+               hlen = (network[12] & 0xF0) >> 2;
+
+               /* verify hlen meets minimum size requirements */
+               if (hlen < sizeof(struct tcphdr))
+                       return network - data;
+
+               network += hlen;
+       } else if (hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S)
+               == HNS_RX_FLAG_L4ID_UDP) {
+               if ((typeof(max_size))(network - data) >
+                   (max_size - sizeof(struct udphdr)))
+                       return max_size;
+
+               network += sizeof(struct udphdr);
+       }
+
+       /* If everything has gone correctly network should be the
+        * data section of the packet and will be the end of the header.
+        * If not then it probably represents the end of the last recognized
+        * header.
+        */
+       if ((typeof(max_size))(network - data) < max_size)
+               return network - data;
+       else
+               return max_size;
+}
+
+static void
+hns_nic_reuse_page(struct hnae_desc_cb *desc_cb, int tsize, int last_offset)
+{
+        /* avoid re-using remote pages,flag default unreuse */
+       if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) {
+               /* move offset up to the next cache line */
+               desc_cb->page_offset += tsize;
+
+               if (desc_cb->page_offset <= last_offset) {
+                       desc_cb->reuse_flag = 1;
+                       /* bump ref count on page before it is given*/
+                       get_page(desc_cb->priv);
+               }
+       }
+}
+
+static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data,
+                              struct sk_buff **out_skb, int *out_bnum)
+{
+       struct hnae_ring *ring = ring_data->ring;
+       struct net_device *ndev = ring_data->napi.dev;
+       struct sk_buff *skb;
+       struct hnae_desc *desc;
+       struct hnae_desc_cb *desc_cb;
+       unsigned char *va;
+       int bnum, length, size, i, truesize, last_offset;
+       int pull_len;
+       u32 bnum_flag;
+
+       last_offset = hnae_page_size(ring) - hnae_buf_size(ring);
+       desc = &ring->desc[ring->next_to_clean];
+       desc_cb = &ring->desc_cb[ring->next_to_clean];
+       length = le16_to_cpu(desc->rx.pkt_len);
+       bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag);
+       bnum = hnae_get_field(bnum_flag, HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S);
+       *out_bnum = bnum;
+       va = (unsigned char *)desc_cb->buf + desc_cb->page_offset;
+
+       skb = *out_skb = napi_alloc_skb(&ring_data->napi, HNS_RX_HEAD_SIZE);
+       if (unlikely(!skb)) {
+               netdev_err(ndev, "alloc rx skb fail\n");
+               ring->stats.sw_err_cnt++;
+               return -ENOMEM;
+       }
+
+       if (length <= HNS_RX_HEAD_SIZE) {
+               memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long)));
+
+               /* we can reuse buffer as-is, just make sure it is local */
+               if (likely(page_to_nid(desc_cb->priv) == numa_node_id()))
+                       desc_cb->reuse_flag = 1;
+               else /* this page cannot be reused so discard it */
+                       put_page(desc_cb->priv);
+
+               ring_ptr_move_fw(ring, next_to_clean);
+
+               if (unlikely(bnum != 1)) { /* check err*/
+                       *out_bnum = 1;
+                       goto out_bnum_err;
+               }
+       } else {
+               ring->stats.seg_pkt_cnt++;
+
+               pull_len = hns_nic_get_headlen(va, bnum_flag, HNS_RX_HEAD_SIZE);
+               memcpy(__skb_put(skb, pull_len), va,
+                      ALIGN(pull_len, sizeof(long)));
+
+               size = le16_to_cpu(desc->rx.size);
+               truesize = ALIGN(size, L1_CACHE_BYTES);
+               skb_add_rx_frag(skb, 0, desc_cb->priv,
+                               desc_cb->page_offset + pull_len,
+                               size - pull_len, truesize - pull_len);
+
+               hns_nic_reuse_page(desc_cb, truesize, last_offset);
+               ring_ptr_move_fw(ring, next_to_clean);
+
+               if (unlikely(bnum >= (int)MAX_SKB_FRAGS)) { /* check err*/
+                       *out_bnum = 1;
+                       goto out_bnum_err;
+               }
+               for (i = 1; i < bnum; i++) {
+                       desc = &ring->desc[ring->next_to_clean];
+                       desc_cb = &ring->desc_cb[ring->next_to_clean];
+                       size = le16_to_cpu(desc->rx.size);
+                       truesize = ALIGN(size, L1_CACHE_BYTES);
+                       skb_add_rx_frag(skb, i, desc_cb->priv,
+                                       desc_cb->page_offset,
+                                       size, truesize);
+
+                       hns_nic_reuse_page(desc_cb, truesize, last_offset);
+                       ring_ptr_move_fw(ring, next_to_clean);
+               }
+       }
+
+       /* check except process, free skb and jump the desc */
+       if (unlikely((!bnum) || (bnum > ring->max_desc_num_per_pkt))) {
+out_bnum_err:
+               *out_bnum = *out_bnum ? *out_bnum : 1; /* ntc moved,cannot 0*/
+               netdev_err(ndev, "invalid bnum(%d,%d,%d,%d),%016llx,%016llx\n",
+                          bnum, ring->max_desc_num_per_pkt,
+                          length, (int)MAX_SKB_FRAGS,
+                          ((u64 *)desc)[0], ((u64 *)desc)[1]);
+               ring->stats.err_bd_num++;
+               dev_kfree_skb_any(skb);
+               return -EDOM;
+       }
+
+       bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag);
+
+       if (unlikely(!hnae_get_bit(bnum_flag, HNS_RXD_VLD_B))) {
+               netdev_err(ndev, "no valid bd,%016llx,%016llx\n",
+                          ((u64 *)desc)[0], ((u64 *)desc)[1]);
+               ring->stats.non_vld_descs++;
+               dev_kfree_skb_any(skb);
+               return -EINVAL;
+       }
+
+       if (unlikely((!desc->rx.pkt_len) ||
+                    hnae_get_bit(bnum_flag, HNS_RXD_DROP_B))) {
+               ring->stats.err_pkt_len++;
+               dev_kfree_skb_any(skb);
+               return -EFAULT;
+       }
+
+       if (unlikely(hnae_get_bit(bnum_flag, HNS_RXD_L2E_B))) {
+               ring->stats.l2_err++;
+               dev_kfree_skb_any(skb);
+               return -EFAULT;
+       }
+
+       ring->stats.rx_pkts++;
+       ring->stats.rx_bytes += skb->len;
+
+       if (unlikely(hnae_get_bit(bnum_flag, HNS_RXD_L3E_B) ||
+                    hnae_get_bit(bnum_flag, HNS_RXD_L4E_B))) {
+               ring->stats.l3l4_csum_err++;
+               return 0;
+       }
+
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       return 0;
+}
+
+static void
+hns_nic_alloc_rx_buffers(struct hns_nic_ring_data *ring_data, int cleand_count)
+{
+       int i, ret;
+       struct hnae_desc_cb res_cbs;
+       struct hnae_desc_cb *desc_cb;
+       struct hnae_ring *ring = ring_data->ring;
+       struct net_device *ndev = ring_data->napi.dev;
+
+       for (i = 0; i < cleand_count; i++) {
+               desc_cb = &ring->desc_cb[ring->next_to_use];
+               if (desc_cb->reuse_flag) {
+                       ring->stats.reuse_pg_cnt++;
+                       hnae_reuse_buffer(ring, ring->next_to_use);
+               } else {
+                       ret = hnae_reserve_buffer_map(ring, &res_cbs);
+                       if (ret) {
+                               ring->stats.sw_err_cnt++;
+                               netdev_err(ndev, "hnae reserve buffer map failed.\n");
+                               break;
+                       }
+                       hnae_replace_buffer(ring, ring->next_to_use, &res_cbs);
+               }
+
+               ring_ptr_move_fw(ring, next_to_use);
+       }
+
+       wmb(); /* make all data has been write before submit */
+       writel_relaxed(i, ring->io_base + RCB_REG_HEAD);
+}
+
+/* return error number for error or number of desc left to take
+ */
+static void hns_nic_rx_up_pro(struct hns_nic_ring_data *ring_data,
+                             struct sk_buff *skb)
+{
+       struct net_device *ndev = ring_data->napi.dev;
+
+       skb->protocol = eth_type_trans(skb, ndev);
+       (void)napi_gro_receive(&ring_data->napi, skb);
+       ndev->last_rx = jiffies;
+}
+
+static int hns_nic_rx_poll_one(struct hns_nic_ring_data *ring_data,
+                              int budget, void *v)
+{
+       struct hnae_ring *ring = ring_data->ring;
+       struct sk_buff *skb;
+       int num, bnum, ex_num;
+#define RCB_NOF_ALLOC_RX_BUFF_ONCE 16
+       int recv_pkts, recv_bds, clean_count, err;
+
+       num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
+       rmb(); /* make sure num taken effect before the other data is touched */
+
+       recv_pkts = 0, recv_bds = 0, clean_count = 0;
+recv:
+       while (recv_pkts < budget && recv_bds < num) {
+               /* reuse or realloc buffers*/
+               if (clean_count >= RCB_NOF_ALLOC_RX_BUFF_ONCE) {
+                       hns_nic_alloc_rx_buffers(ring_data, clean_count);
+                       clean_count = 0;
+               }
+
+               /* poll one pkg*/
+               err = hns_nic_poll_rx_skb(ring_data, &skb, &bnum);
+               if (unlikely(!skb)) /* this fault cannot be repaired */
+                       break;
+
+               recv_bds += bnum;
+               clean_count += bnum;
+               if (unlikely(err)) {  /* do jump the err */
+                       recv_pkts++;
+                       continue;
+               }
+
+               /* do update ip stack process*/
+               ((void (*)(struct hns_nic_ring_data *, struct sk_buff *))v)(
+                                                       ring_data, skb);
+               recv_pkts++;
+       }
+
+       /* make all data has been write before submit */
+       if (clean_count > 0) {
+               hns_nic_alloc_rx_buffers(ring_data, clean_count);
+               clean_count = 0;
+       }
+
+       if (recv_pkts < budget) {
+               ex_num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
+               rmb(); /*complete read rx ring bd number*/
+               if (ex_num > 0) {
+                       num += ex_num;
+                       goto recv;
+               }
+       }
+
+       return recv_pkts;
+}
+
+static void hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data)
+{
+       struct hnae_ring *ring = ring_data->ring;
+       int num = 0;
+
+       /* for hardware bug fixed */
+       num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
+
+       if (num > 0) {
+               ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
+                       ring_data->ring, 1);
+
+               napi_schedule(&ring_data->napi);
+       }
+}
+
+static inline void hns_nic_reclaim_one_desc(struct hnae_ring *ring,
+                                           int *bytes, int *pkts)
+{
+       struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_clean];
+
+       (*pkts) += (desc_cb->type == DESC_TYPE_SKB);
+       (*bytes) += desc_cb->length;
+       /* desc_cb will be cleaned, after hnae_free_buffer_detach*/
+       hnae_free_buffer_detach(ring, ring->next_to_clean);
+
+       ring_ptr_move_fw(ring, next_to_clean);
+}
+
+static int is_valid_clean_head(struct hnae_ring *ring, int h)
+{
+       int u = ring->next_to_use;
+       int c = ring->next_to_clean;
+
+       if (unlikely(h > ring->desc_num))
+               return 0;
+
+       assert(u > 0 && u < ring->desc_num);
+       assert(c > 0 && c < ring->desc_num);
+       assert(u != c && h != c); /* must be checked before call this func */
+
+       return u > c ? (h > c && h <= u) : (h > c || h <= u);
+}
+
+/* netif_tx_lock will turn down the performance, set only when necessary */
+#ifdef CONFIG_NET_POLL_CONTROLLER
+#define NETIF_TX_LOCK(ndev) netif_tx_lock(ndev)
+#define NETIF_TX_UNLOCK(ndev) netif_tx_unlock(ndev)
+#else
+#define NETIF_TX_LOCK(ndev)
+#define NETIF_TX_UNLOCK(ndev)
+#endif
+/* reclaim all desc in one budget
+ * return error or number of desc left
+ */
+static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data,
+                              int budget, void *v)
+{
+       struct hnae_ring *ring = ring_data->ring;
+       struct net_device *ndev = ring_data->napi.dev;
+       struct netdev_queue *dev_queue;
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       int head;
+       int bytes, pkts;
+
+       NETIF_TX_LOCK(ndev);
+
+       head = readl_relaxed(ring->io_base + RCB_REG_HEAD);
+       rmb(); /* make sure head is ready before touch any data */
+
+       if (is_ring_empty(ring) || head == ring->next_to_clean) {
+               NETIF_TX_UNLOCK(ndev);
+               return 0; /* no data to poll */
+       }
+
+       if (!is_valid_clean_head(ring, head)) {
+               netdev_err(ndev, "wrong head (%d, %d-%d)\n", head,
+                          ring->next_to_use, ring->next_to_clean);
+               ring->stats.io_err_cnt++;
+               NETIF_TX_UNLOCK(ndev);
+               return -EIO;
+       }
+
+       bytes = 0;
+       pkts = 0;
+       while (head != ring->next_to_clean)
+               hns_nic_reclaim_one_desc(ring, &bytes, &pkts);
+
+       NETIF_TX_UNLOCK(ndev);
+
+       dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index);
+       netdev_tx_completed_queue(dev_queue, pkts, bytes);
+
+       if (unlikely(pkts && netif_carrier_ok(ndev) &&
+                    (ring_space(ring) >= ring->max_desc_num_per_pkt * 2))) {
+               /* Make sure that anybody stopping the queue after this
+                * sees the new next_to_clean.
+                */
+               smp_mb();
+               if (netif_tx_queue_stopped(dev_queue) &&
+                   !test_bit(NIC_STATE_DOWN, &priv->state)) {
+                       netif_tx_wake_queue(dev_queue);
+                       ring->stats.restart_queue++;
+               }
+       }
+       return 0;
+}
+
+static void hns_nic_tx_fini_pro(struct hns_nic_ring_data *ring_data)
+{
+       struct hnae_ring *ring = ring_data->ring;
+       int head = ring->next_to_clean;
+
+       /* for hardware bug fixed */
+       head = readl_relaxed(ring->io_base + RCB_REG_HEAD);
+
+       if (head != ring->next_to_clean) {
+               ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
+                       ring_data->ring, 1);
+
+               napi_schedule(&ring_data->napi);
+       }
+}
+
+static void hns_nic_tx_clr_all_bufs(struct hns_nic_ring_data *ring_data)
+{
+       struct hnae_ring *ring = ring_data->ring;
+       struct net_device *ndev = ring_data->napi.dev;
+       struct netdev_queue *dev_queue;
+       int head;
+       int bytes, pkts;
+
+       NETIF_TX_LOCK(ndev);
+
+       head = ring->next_to_use; /* ntu :soft setted ring position*/
+       bytes = 0;
+       pkts = 0;
+       while (head != ring->next_to_clean)
+               hns_nic_reclaim_one_desc(ring, &bytes, &pkts);
+
+       NETIF_TX_UNLOCK(ndev);
+
+       dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index);
+       netdev_tx_reset_queue(dev_queue);
+}
+
+static int hns_nic_common_poll(struct napi_struct *napi, int budget)
+{
+       struct hns_nic_ring_data *ring_data =
+               container_of(napi, struct hns_nic_ring_data, napi);
+       int clean_complete = ring_data->poll_one(
+                               ring_data, budget, ring_data->ex_process);
+
+       if (clean_complete >= 0 && clean_complete < budget) {
+               napi_complete(napi);
+               ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
+                       ring_data->ring, 0);
+
+               ring_data->fini_process(ring_data);
+       }
+
+       return clean_complete;
+}
+
+static irqreturn_t hns_irq_handle(int irq, void *dev)
+{
+       struct hns_nic_ring_data *ring_data = (struct hns_nic_ring_data *)dev;
+
+       ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
+               ring_data->ring, 1);
+       napi_schedule(&ring_data->napi);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ *hns_nic_adjust_link - adjust net work mode by the phy stat or new param
+ *@ndev: net device
+ */
+static void hns_nic_adjust_link(struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h = priv->ae_handle;
+
+       h->dev->ops->adjust_link(h, ndev->phydev->speed, ndev->phydev->duplex);
+}
+
+/**
+ *hns_nic_init_phy - init phy
+ *@ndev: net device
+ *@h: ae handle
+ * Return 0 on success, negative on failure
+ */
+int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct phy_device *phy_dev = NULL;
+
+       if (!h->phy_node)
+               return 0;
+
+       if (h->phy_if != PHY_INTERFACE_MODE_XGMII)
+               phy_dev = of_phy_connect(ndev, h->phy_node,
+                                        hns_nic_adjust_link, 0, h->phy_if);
+       else
+               phy_dev = of_phy_attach(ndev, h->phy_node, 0, h->phy_if);
+
+       if (unlikely(!phy_dev) || IS_ERR(phy_dev))
+               return !phy_dev ? -ENODEV : PTR_ERR(phy_dev);
+
+       phy_dev->supported &= h->if_support;
+       phy_dev->advertising = phy_dev->supported;
+
+       if (h->phy_if == PHY_INTERFACE_MODE_XGMII)
+               phy_dev->autoneg = false;
+
+       priv->phy = phy_dev;
+
+       return 0;
+}
+
+static int hns_nic_ring_open(struct net_device *netdev, int idx)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct hnae_handle *h = priv->ae_handle;
+
+       napi_enable(&priv->ring_data[idx].napi);
+
+       enable_irq(priv->ring_data[idx].ring->irq);
+       h->dev->ops->toggle_ring_irq(priv->ring_data[idx].ring, 0);
+
+       return 0;
+}
+
+static int hns_nic_net_set_mac_address(struct net_device *ndev, void *p)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h = priv->ae_handle;
+       struct sockaddr *mac_addr = p;
+       int ret;
+
+       if (!mac_addr || !is_valid_ether_addr((const u8 *)mac_addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       ret = h->dev->ops->set_mac_addr(h, mac_addr->sa_data);
+       if (ret) {
+               netdev_err(ndev, "set_mac_address fail, ret=%d!\n", ret);
+               return ret;
+       }
+
+       memcpy(ndev->dev_addr, mac_addr->sa_data, ndev->addr_len);
+
+       return 0;
+}
+
+void hns_nic_update_stats(struct net_device *netdev)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct hnae_handle *h = priv->ae_handle;
+
+       h->dev->ops->update_stats(h, &netdev->stats);
+}
+
+/* set mac addr if it is configed. or leave it to the AE driver */
+static void hns_init_mac_addr(struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct device_node *node = priv->dev->of_node;
+       const void *mac_addr_temp;
+
+       mac_addr_temp = of_get_mac_address(node);
+       if (mac_addr_temp && is_valid_ether_addr(mac_addr_temp)) {
+               memcpy(ndev->dev_addr, mac_addr_temp, ndev->addr_len);
+       } else {
+               eth_hw_addr_random(ndev);
+               dev_warn(priv->dev, "No valid mac, use random mac %pM",
+                        ndev->dev_addr);
+       }
+}
+
+static void hns_nic_ring_close(struct net_device *netdev, int idx)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct hnae_handle *h = priv->ae_handle;
+
+       h->dev->ops->toggle_ring_irq(priv->ring_data[idx].ring, 1);
+       disable_irq(priv->ring_data[idx].ring->irq);
+
+       napi_disable(&priv->ring_data[idx].napi);
+}
+
+static int hns_nic_init_irq(struct hns_nic_priv *priv)
+{
+       struct hnae_handle *h = priv->ae_handle;
+       struct hns_nic_ring_data *rd;
+       int i;
+       int ret;
+       int cpu;
+       cpumask_t mask;
+
+       for (i = 0; i < h->q_num * 2; i++) {
+               rd = &priv->ring_data[i];
+
+               if (rd->ring->irq_init_flag == RCB_IRQ_INITED)
+                       break;
+
+               snprintf(rd->ring->ring_name, RCB_RING_NAME_LEN,
+                        "%s-%s%d", priv->netdev->name,
+                        (i < h->q_num ? "tx" : "rx"), rd->queue_index);
+
+               rd->ring->ring_name[RCB_RING_NAME_LEN - 1] = '\0';
+
+               ret = request_irq(rd->ring->irq,
+                                 hns_irq_handle, 0, rd->ring->ring_name, rd);
+               if (ret) {
+                       netdev_err(priv->netdev, "request irq(%d) fail\n",
+                                  rd->ring->irq);
+                       return ret;
+               }
+               disable_irq(rd->ring->irq);
+               rd->ring->irq_init_flag = RCB_IRQ_INITED;
+
+               /*set cpu affinity*/
+               if (cpu_online(rd->queue_index)) {
+                       cpumask_clear(&mask);
+                       cpu = rd->queue_index;
+                       cpumask_set_cpu(cpu, &mask);
+                       irq_set_affinity_hint(rd->ring->irq, &mask);
+               }
+       }
+
+       return 0;
+}
+
+static int hns_nic_net_up(struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h = priv->ae_handle;
+       int i, j, k;
+       int ret;
+
+       ret = hns_nic_init_irq(priv);
+       if (ret != 0) {
+               netdev_err(ndev, "hns init irq failed! ret=%d\n", ret);
+               return ret;
+       }
+
+       for (i = 0; i < h->q_num * 2; i++) {
+               ret = hns_nic_ring_open(ndev, i);
+               if (ret)
+                       goto out_has_some_queues;
+       }
+
+       for (k = 0; k < h->q_num; k++)
+               h->dev->ops->toggle_queue_status(h->qs[k], 1);
+
+       ret = h->dev->ops->set_mac_addr(h, ndev->dev_addr);
+       if (ret)
+               goto out_set_mac_addr_err;
+
+       ret = h->dev->ops->start ? h->dev->ops->start(h) : 0;
+       if (ret)
+               goto out_start_err;
+
+       if (priv->phy)
+               phy_start(priv->phy);
+
+       clear_bit(NIC_STATE_DOWN, &priv->state);
+       (void)mod_timer(&priv->service_timer, jiffies + SERVICE_TIMER_HZ);
+
+       return 0;
+
+out_start_err:
+       netif_stop_queue(ndev);
+out_set_mac_addr_err:
+       for (k = 0; k < h->q_num; k++)
+               h->dev->ops->toggle_queue_status(h->qs[k], 0);
+out_has_some_queues:
+       for (j = i - 1; j >= 0; j--)
+               hns_nic_ring_close(ndev, j);
+
+       set_bit(NIC_STATE_DOWN, &priv->state);
+
+       return ret;
+}
+
+static void hns_nic_net_down(struct net_device *ndev)
+{
+       int i;
+       struct hnae_ae_ops *ops;
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+
+       if (test_and_set_bit(NIC_STATE_DOWN, &priv->state))
+               return;
+
+       (void)del_timer_sync(&priv->service_timer);
+       netif_tx_stop_all_queues(ndev);
+       netif_carrier_off(ndev);
+       netif_tx_disable(ndev);
+       priv->link = 0;
+
+       if (priv->phy)
+               phy_stop(priv->phy);
+
+       ops = priv->ae_handle->dev->ops;
+
+       if (ops->stop)
+               ops->stop(priv->ae_handle);
+
+       netif_tx_stop_all_queues(ndev);
+
+       for (i = priv->ae_handle->q_num - 1; i >= 0; i--) {
+               hns_nic_ring_close(ndev, i);
+               hns_nic_ring_close(ndev, i + priv->ae_handle->q_num);
+
+               /* clean tx buffers*/
+               hns_nic_tx_clr_all_bufs(priv->ring_data + i);
+       }
+}
+
+void hns_nic_net_reset(struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *handle = priv->ae_handle;
+
+       while (test_and_set_bit(NIC_STATE_RESETTING, &priv->state))
+               usleep_range(1000, 2000);
+
+       (void)hnae_reinit_handle(handle);
+
+       clear_bit(NIC_STATE_RESETTING, &priv->state);
+}
+
+void hns_nic_net_reinit(struct net_device *netdev)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+
+       priv->netdev->trans_start = jiffies;
+       while (test_and_set_bit(NIC_STATE_REINITING, &priv->state))
+               usleep_range(1000, 2000);
+
+       hns_nic_net_down(netdev);
+       hns_nic_net_reset(netdev);
+       (void)hns_nic_net_up(netdev);
+       clear_bit(NIC_STATE_REINITING, &priv->state);
+}
+
+static int hns_nic_net_open(struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h = priv->ae_handle;
+       int ret;
+
+       if (test_bit(NIC_STATE_TESTING, &priv->state))
+               return -EBUSY;
+
+       priv->link = 0;
+       netif_carrier_off(ndev);
+
+       ret = netif_set_real_num_tx_queues(ndev, h->q_num);
+       if (ret < 0) {
+               netdev_err(ndev, "netif_set_real_num_tx_queues fail, ret=%d!\n",
+                          ret);
+               return ret;
+       }
+
+       ret = netif_set_real_num_rx_queues(ndev, h->q_num);
+       if (ret < 0) {
+               netdev_err(ndev,
+                          "netif_set_real_num_rx_queues fail, ret=%d!\n", ret);
+               return ret;
+       }
+
+       ret = hns_nic_net_up(ndev);
+       if (ret) {
+               netdev_err(ndev,
+                          "hns net up fail, ret=%d!\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int hns_nic_net_stop(struct net_device *ndev)
+{
+       hns_nic_net_down(ndev);
+
+       return 0;
+}
+
+static void hns_tx_timeout_reset(struct hns_nic_priv *priv);
+static void hns_nic_net_timeout(struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+
+       hns_tx_timeout_reset(priv);
+}
+
+static int hns_nic_do_ioctl(struct net_device *netdev, struct ifreq *ifr,
+                           int cmd)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct phy_device *phy_dev = priv->phy;
+
+       if (!netif_running(netdev))
+               return -EINVAL;
+
+       if (!phy_dev)
+               return -ENOTSUPP;
+
+       return phy_mii_ioctl(phy_dev, ifr, cmd);
+}
+
+/* use only for netconsole to poll with the device without interrupt */
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void hns_nic_poll_controller(struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       unsigned long flags;
+       int i;
+
+       local_irq_save(flags);
+       for (i = 0; i < priv->ae_handle->q_num * 2; i++)
+               napi_schedule(&priv->ring_data[i].napi);
+       local_irq_restore(flags);
+}
+#endif
+
+static netdev_tx_t hns_nic_net_xmit(struct sk_buff *skb,
+                                   struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       int ret;
+
+       assert(skb->queue_mapping < ndev->ae_handle->q_num);
+       ret = hns_nic_net_xmit_hw(ndev, skb,
+                                 &tx_ring_data(priv, skb->queue_mapping));
+       if (ret == NETDEV_TX_OK) {
+               ndev->trans_start = jiffies;
+               ndev->stats.tx_bytes += skb->len;
+               ndev->stats.tx_packets++;
+       }
+       return (netdev_tx_t)ret;
+}
+
+static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h = priv->ae_handle;
+       int ret;
+
+       /* MTU < 68 is an error and causes problems on some kernels */
+       if (new_mtu < 68)
+               return -EINVAL;
+
+       if (!h->dev->ops->set_mtu)
+               return -ENOTSUPP;
+
+       if (netif_running(ndev)) {
+               (void)hns_nic_net_stop(ndev);
+               msleep(100);
+
+               ret = h->dev->ops->set_mtu(h, new_mtu);
+               if (ret)
+                       netdev_err(ndev, "set mtu fail, return value %d\n",
+                                  ret);
+
+               if (hns_nic_net_open(ndev))
+                       netdev_err(ndev, "hns net open fail\n");
+       } else {
+               ret = h->dev->ops->set_mtu(h, new_mtu);
+       }
+
+       if (!ret)
+               ndev->mtu = new_mtu;
+
+       return ret;
+}
+
+/**
+ * nic_set_multicast_list - set mutl mac address
+ * @netdev: net device
+ * @p: mac address
+ *
+ * return void
+ */
+void hns_set_multicast_list(struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h = priv->ae_handle;
+       struct netdev_hw_addr *ha = NULL;
+
+       if (!h) {
+               netdev_err(ndev, "hnae handle is null\n");
+               return;
+       }
+
+       if (h->dev->ops->set_mc_addr) {
+               netdev_for_each_mc_addr(ha, ndev)
+                       if (h->dev->ops->set_mc_addr(h, ha->addr))
+                               netdev_err(ndev, "set multicast fail\n");
+       }
+}
+
+void hns_nic_set_rx_mode(struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h = priv->ae_handle;
+
+       if (h->dev->ops->set_promisc_mode) {
+               if (ndev->flags & IFF_PROMISC)
+                       h->dev->ops->set_promisc_mode(h, 1);
+               else
+                       h->dev->ops->set_promisc_mode(h, 0);
+       }
+
+       hns_set_multicast_list(ndev);
+}
+
+struct rtnl_link_stats64 *hns_nic_get_stats64(struct net_device *ndev,
+                                             struct rtnl_link_stats64 *stats)
+{
+       int idx = 0;
+       u64 tx_bytes = 0;
+       u64 rx_bytes = 0;
+       u64 tx_pkts = 0;
+       u64 rx_pkts = 0;
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h = priv->ae_handle;
+
+       for (idx = 0; idx < h->q_num; idx++) {
+               tx_bytes += h->qs[idx]->tx_ring.stats.tx_bytes;
+               tx_pkts += h->qs[idx]->tx_ring.stats.tx_pkts;
+               rx_bytes += h->qs[idx]->rx_ring.stats.rx_bytes;
+               rx_pkts += h->qs[idx]->rx_ring.stats.rx_pkts;
+       }
+
+       stats->tx_bytes = tx_bytes;
+       stats->tx_packets = tx_pkts;
+       stats->rx_bytes = rx_bytes;
+       stats->rx_packets = rx_pkts;
+
+       stats->rx_errors = ndev->stats.rx_errors;
+       stats->multicast = ndev->stats.multicast;
+       stats->rx_length_errors = ndev->stats.rx_length_errors;
+       stats->rx_crc_errors = ndev->stats.rx_crc_errors;
+       stats->rx_missed_errors = ndev->stats.rx_missed_errors;
+
+       stats->tx_errors = ndev->stats.tx_errors;
+       stats->rx_dropped = ndev->stats.rx_dropped;
+       stats->tx_dropped = ndev->stats.tx_dropped;
+       stats->collisions = ndev->stats.collisions;
+       stats->rx_over_errors = ndev->stats.rx_over_errors;
+       stats->rx_frame_errors = ndev->stats.rx_frame_errors;
+       stats->rx_fifo_errors = ndev->stats.rx_fifo_errors;
+       stats->tx_aborted_errors = ndev->stats.tx_aborted_errors;
+       stats->tx_carrier_errors = ndev->stats.tx_carrier_errors;
+       stats->tx_fifo_errors = ndev->stats.tx_fifo_errors;
+       stats->tx_heartbeat_errors = ndev->stats.tx_heartbeat_errors;
+       stats->tx_window_errors = ndev->stats.tx_window_errors;
+       stats->rx_compressed = ndev->stats.rx_compressed;
+       stats->tx_compressed = ndev->stats.tx_compressed;
+
+       return stats;
+}
+
+static const struct net_device_ops hns_nic_netdev_ops = {
+       .ndo_open = hns_nic_net_open,
+       .ndo_stop = hns_nic_net_stop,
+       .ndo_start_xmit = hns_nic_net_xmit,
+       .ndo_tx_timeout = hns_nic_net_timeout,
+       .ndo_set_mac_address = hns_nic_net_set_mac_address,
+       .ndo_change_mtu = hns_nic_change_mtu,
+       .ndo_do_ioctl = hns_nic_do_ioctl,
+       .ndo_get_stats64 = hns_nic_get_stats64,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller = hns_nic_poll_controller,
+#endif
+       .ndo_set_rx_mode = hns_nic_set_rx_mode,
+};
+
+static void hns_nic_update_link_status(struct net_device *netdev)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+
+       struct hnae_handle *h = priv->ae_handle;
+       int state = 1;
+
+       if (priv->phy) {
+               if (!genphy_update_link(priv->phy))
+                       state = priv->phy->link;
+               else
+                       state = 0;
+       }
+       state = state && h->dev->ops->get_status(h);
+
+       if (state != priv->link) {
+               if (state) {
+                       netif_carrier_on(netdev);
+                       netif_tx_wake_all_queues(netdev);
+                       netdev_info(netdev, "link up\n");
+               } else {
+                       netif_carrier_off(netdev);
+                       netdev_info(netdev, "link down\n");
+               }
+               priv->link = state;
+       }
+}
+
+/* for dumping key regs*/
+static void hns_nic_dump(struct hns_nic_priv *priv)
+{
+       struct hnae_handle *h = priv->ae_handle;
+       struct hnae_ae_ops *ops = h->dev->ops;
+       u32 *data, reg_num, i;
+
+       if (ops->get_regs_len && ops->get_regs) {
+               reg_num = ops->get_regs_len(priv->ae_handle);
+               reg_num = (reg_num + 3ul) & ~3ul;
+               data = kcalloc(reg_num, sizeof(u32), GFP_KERNEL);
+               if (data) {
+                       ops->get_regs(priv->ae_handle, data);
+                       for (i = 0; i < reg_num; i += 4)
+                               pr_info("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                                       i, data[i], data[i + 1],
+                                       data[i + 2], data[i + 3]);
+                       kfree(data);
+               }
+       }
+
+       for (i = 0; i < h->q_num; i++) {
+               pr_info("tx_queue%d_next_to_clean:%d\n",
+                       i, h->qs[i]->tx_ring.next_to_clean);
+               pr_info("tx_queue%d_next_to_use:%d\n",
+                       i, h->qs[i]->tx_ring.next_to_use);
+               pr_info("rx_queue%d_next_to_clean:%d\n",
+                       i, h->qs[i]->rx_ring.next_to_clean);
+               pr_info("rx_queue%d_next_to_use:%d\n",
+                       i, h->qs[i]->rx_ring.next_to_use);
+       }
+}
+
+/* for resetting suntask*/
+static void hns_nic_reset_subtask(struct hns_nic_priv *priv)
+{
+       enum hnae_port_type type = priv->ae_handle->port_type;
+
+       if (!test_bit(NIC_STATE2_RESET_REQUESTED, &priv->state))
+               return;
+       clear_bit(NIC_STATE2_RESET_REQUESTED, &priv->state);
+
+       /* If we're already down, removing or resetting, just bail */
+       if (test_bit(NIC_STATE_DOWN, &priv->state) ||
+           test_bit(NIC_STATE_REMOVING, &priv->state) ||
+           test_bit(NIC_STATE_RESETTING, &priv->state))
+               return;
+
+       hns_nic_dump(priv);
+       netdev_info(priv->netdev, "Reset %s port\n",
+                   (type == HNAE_PORT_DEBUG ? "debug" : "business"));
+
+       rtnl_lock();
+       /* put off any impending NetWatchDogTimeout */
+       priv->netdev->trans_start = jiffies;
+
+       if (type == HNAE_PORT_DEBUG)
+               hns_nic_net_reinit(priv->netdev);
+       rtnl_unlock();
+}
+
+/* for doing service complete*/
+static void hns_nic_service_event_complete(struct hns_nic_priv *priv)
+{
+       assert(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state));
+
+       smp_mb__before_atomic();
+       clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state);
+}
+
+static void hns_nic_service_task(struct work_struct *work)
+{
+       struct hns_nic_priv *priv
+               = container_of(work, struct hns_nic_priv, service_task);
+       struct hnae_handle *h = priv->ae_handle;
+
+       hns_nic_update_link_status(priv->netdev);
+       h->dev->ops->update_led_status(h);
+       hns_nic_update_stats(priv->netdev);
+
+       hns_nic_reset_subtask(priv);
+       hns_nic_service_event_complete(priv);
+}
+
+static void hns_nic_task_schedule(struct hns_nic_priv *priv)
+{
+       if (!test_bit(NIC_STATE_DOWN, &priv->state) &&
+           !test_bit(NIC_STATE_REMOVING, &priv->state) &&
+           !test_and_set_bit(NIC_STATE_SERVICE_SCHED, &priv->state))
+               (void)schedule_work(&priv->service_task);
+}
+
+static void hns_nic_service_timer(unsigned long data)
+{
+       struct hns_nic_priv *priv = (struct hns_nic_priv *)data;
+
+       (void)mod_timer(&priv->service_timer, jiffies + SERVICE_TIMER_HZ);
+
+       hns_nic_task_schedule(priv);
+}
+
+/**
+ * hns_tx_timeout_reset - initiate reset due to Tx timeout
+ * @priv: driver private struct
+ **/
+static void hns_tx_timeout_reset(struct hns_nic_priv *priv)
+{
+       /* Do the reset outside of interrupt context */
+       if (!test_bit(NIC_STATE_DOWN, &priv->state)) {
+               set_bit(NIC_STATE2_RESET_REQUESTED, &priv->state);
+               netdev_warn(priv->netdev,
+                           "initiating reset due to tx timeout(%llu,0x%lx)\n",
+                           priv->tx_timeout_count, priv->state);
+               priv->tx_timeout_count++;
+               hns_nic_task_schedule(priv);
+       }
+}
+
+static int hns_nic_init_ring_data(struct hns_nic_priv *priv)
+{
+       struct hnae_handle *h = priv->ae_handle;
+       struct hns_nic_ring_data *rd;
+       int i;
+
+       if (h->q_num > NIC_MAX_Q_PER_VF) {
+               netdev_err(priv->netdev, "too much queue (%d)\n", h->q_num);
+               return -EINVAL;
+       }
+
+       priv->ring_data = kzalloc(h->q_num * sizeof(*priv->ring_data) * 2,
+                                 GFP_KERNEL);
+       if (!priv->ring_data)
+               return -ENOMEM;
+
+       for (i = 0; i < h->q_num; i++) {
+               rd = &priv->ring_data[i];
+               rd->queue_index = i;
+               rd->ring = &h->qs[i]->tx_ring;
+               rd->poll_one = hns_nic_tx_poll_one;
+               rd->fini_process = hns_nic_tx_fini_pro;
+
+               netif_napi_add(priv->netdev, &rd->napi,
+                              hns_nic_common_poll, NIC_TX_CLEAN_MAX_NUM);
+               rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED;
+       }
+       for (i = h->q_num; i < h->q_num * 2; i++) {
+               rd = &priv->ring_data[i];
+               rd->queue_index = i - h->q_num;
+               rd->ring = &h->qs[i - h->q_num]->rx_ring;
+               rd->poll_one = hns_nic_rx_poll_one;
+               rd->ex_process = hns_nic_rx_up_pro;
+               rd->fini_process = hns_nic_rx_fini_pro;
+
+               netif_napi_add(priv->netdev, &rd->napi,
+                              hns_nic_common_poll, NIC_RX_CLEAN_MAX_NUM);
+               rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED;
+       }
+
+       return 0;
+}
+
+static void hns_nic_uninit_ring_data(struct hns_nic_priv *priv)
+{
+       struct hnae_handle *h = priv->ae_handle;
+       int i;
+
+       for (i = 0; i < h->q_num * 2; i++) {
+               netif_napi_del(&priv->ring_data[i].napi);
+               if (priv->ring_data[i].ring->irq_init_flag == RCB_IRQ_INITED) {
+                       irq_set_affinity_hint(priv->ring_data[i].ring->irq,
+                                             NULL);
+                       free_irq(priv->ring_data[i].ring->irq,
+                                &priv->ring_data[i]);
+               }
+
+               priv->ring_data[i].ring->irq_init_flag = RCB_IRQ_NOT_INITED;
+       }
+       kfree(priv->ring_data);
+}
+
+static int hns_nic_try_get_ae(struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h;
+       int ret;
+
+       h = hnae_get_handle(&priv->netdev->dev,
+                           priv->ae_name, priv->port_id, NULL);
+       if (IS_ERR_OR_NULL(h)) {
+               ret = PTR_ERR(h);
+               dev_dbg(priv->dev, "has not handle, register notifier!\n");
+               goto out;
+       }
+       priv->ae_handle = h;
+
+       ret = hns_nic_init_phy(ndev, h);
+       if (ret) {
+               dev_err(priv->dev, "probe phy device fail!\n");
+               goto out_init_phy;
+       }
+
+       ret = hns_nic_init_ring_data(priv);
+       if (ret) {
+               ret = -ENOMEM;
+               goto out_init_ring_data;
+       }
+
+       ret = register_netdev(ndev);
+       if (ret) {
+               dev_err(priv->dev, "probe register netdev fail!\n");
+               goto out_reg_ndev_fail;
+       }
+       return 0;
+
+out_reg_ndev_fail:
+       hns_nic_uninit_ring_data(priv);
+       priv->ring_data = NULL;
+out_init_phy:
+out_init_ring_data:
+       hnae_put_handle(priv->ae_handle);
+       priv->ae_handle = NULL;
+out:
+       return ret;
+}
+
+static int hns_nic_notifier_action(struct notifier_block *nb,
+                                  unsigned long action, void *data)
+{
+       struct hns_nic_priv *priv =
+               container_of(nb, struct hns_nic_priv, notifier_block);
+
+       assert(action == HNAE_AE_REGISTER);
+
+       if (!hns_nic_try_get_ae(priv->netdev)) {
+               hnae_unregister_notifier(&priv->notifier_block);
+               priv->notifier_block.notifier_call = NULL;
+       }
+       return 0;
+}
+
+static int hns_nic_dev_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct net_device *ndev;
+       struct hns_nic_priv *priv;
+       struct device_node *node = dev->of_node;
+       int ret;
+
+       ndev = alloc_etherdev_mq(sizeof(struct hns_nic_priv), NIC_MAX_Q_PER_VF);
+       if (!ndev)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ndev);
+
+       priv = netdev_priv(ndev);
+       priv->dev = dev;
+       priv->netdev = ndev;
+
+       if (of_device_is_compatible(node, "hisilicon,hns-nic-v2"))
+               priv->enet_ver = AE_VERSION_2;
+       else
+               priv->enet_ver = AE_VERSION_1;
+
+       ret = of_property_read_string(node, "ae-name", &priv->ae_name);
+       if (ret)
+               goto out_read_string_fail;
+
+       ret = of_property_read_u32(node, "port-id", &priv->port_id);
+       if (ret)
+               goto out_read_string_fail;
+
+       hns_init_mac_addr(ndev);
+
+       ndev->watchdog_timeo = HNS_NIC_TX_TIMEOUT;
+       ndev->priv_flags |= IFF_UNICAST_FLT;
+       ndev->netdev_ops = &hns_nic_netdev_ops;
+       hns_ethtool_set_ops(ndev);
+       ndev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+               NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |
+               NETIF_F_GRO;
+       ndev->vlan_features |=
+               NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM;
+       ndev->vlan_features |= NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO;
+
+       SET_NETDEV_DEV(ndev, dev);
+
+       if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)))
+               dev_dbg(dev, "set mask to 64bit\n");
+       else
+               dev_err(dev, "set mask to 32bit fail!\n");
+
+       /* carrier off reporting is important to ethtool even BEFORE open */
+       netif_carrier_off(ndev);
+
+       setup_timer(&priv->service_timer, hns_nic_service_timer,
+                   (unsigned long)priv);
+       INIT_WORK(&priv->service_task, hns_nic_service_task);
+
+       set_bit(NIC_STATE_SERVICE_INITED, &priv->state);
+       clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state);
+       set_bit(NIC_STATE_DOWN, &priv->state);
+
+       if (hns_nic_try_get_ae(priv->netdev)) {
+               priv->notifier_block.notifier_call = hns_nic_notifier_action;
+               ret = hnae_register_notifier(&priv->notifier_block);
+               if (ret) {
+                       dev_err(dev, "register notifier fail!\n");
+                       goto out_notify_fail;
+               }
+               dev_dbg(dev, "has not handle, register notifier!\n");
+       }
+
+       return 0;
+
+out_notify_fail:
+       (void)cancel_work_sync(&priv->service_task);
+out_read_string_fail:
+       free_netdev(ndev);
+       return ret;
+}
+
+static int hns_nic_dev_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+
+       if (ndev->reg_state != NETREG_UNINITIALIZED)
+               unregister_netdev(ndev);
+
+       if (priv->ring_data)
+               hns_nic_uninit_ring_data(priv);
+       priv->ring_data = NULL;
+
+       if (priv->phy)
+               phy_disconnect(priv->phy);
+       priv->phy = NULL;
+
+       if (!IS_ERR_OR_NULL(priv->ae_handle))
+               hnae_put_handle(priv->ae_handle);
+       priv->ae_handle = NULL;
+       if (priv->notifier_block.notifier_call)
+               hnae_unregister_notifier(&priv->notifier_block);
+       priv->notifier_block.notifier_call = NULL;
+
+       set_bit(NIC_STATE_REMOVING, &priv->state);
+       (void)cancel_work_sync(&priv->service_task);
+
+       free_netdev(ndev);
+       return 0;
+}
+
+static const struct of_device_id hns_enet_of_match[] = {
+       {.compatible = "hisilicon,hns-nic-v1",},
+       {.compatible = "hisilicon,hns-nic-v2",},
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, hns_enet_of_match);
+
+static struct platform_driver hns_nic_dev_driver = {
+       .driver = {
+               .name = "hns-nic",
+               .of_match_table = hns_enet_of_match,
+       },
+       .probe = hns_nic_dev_probe,
+       .remove = hns_nic_dev_remove,
+};
+
+module_platform_driver(hns_nic_dev_driver);
+
+MODULE_DESCRIPTION("HISILICON HNS Ethernet driver");
+MODULE_AUTHOR("Hisilicon, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:hns-nic");
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h
new file mode 100644 (file)
index 0000000..dae0ed1
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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 __HNS_ENET_H
+#define __HNS_ENET_H
+
+#include <linux/netdevice.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+
+#include "hnae.h"
+
+enum hns_nic_state {
+       NIC_STATE_TESTING = 0,
+       NIC_STATE_RESETTING,
+       NIC_STATE_REINITING,
+       NIC_STATE_DOWN,
+       NIC_STATE_DISABLED,
+       NIC_STATE_REMOVING,
+       NIC_STATE_SERVICE_INITED,
+       NIC_STATE_SERVICE_SCHED,
+       NIC_STATE2_RESET_REQUESTED,
+       NIC_STATE_MAX
+};
+
+struct hns_nic_ring_data {
+       struct hnae_ring *ring;
+       struct napi_struct napi;
+       int queue_index;
+       int (*poll_one)(struct hns_nic_ring_data *, int, void *);
+       void (*ex_process)(struct hns_nic_ring_data *, struct sk_buff *);
+       void (*fini_process)(struct hns_nic_ring_data *);
+};
+
+struct hns_nic_priv {
+       const char *ae_name;
+       u32 enet_ver;
+       u32 port_id;
+       int phy_mode;
+       int phy_led_val;
+       struct phy_device *phy;
+       struct net_device *netdev;
+       struct device *dev;
+       struct hnae_handle *ae_handle;
+
+       /* the cb for nic to manage the ring buffer, the first half of the
+        * array is for tx_ring and vice versa for the second half
+        */
+       struct hns_nic_ring_data *ring_data;
+
+       /* The most recently read link state */
+       int link;
+       u64 tx_timeout_count;
+
+       unsigned long state;
+
+       struct timer_list service_timer;
+
+       struct work_struct service_task;
+
+       struct notifier_block notifier_block;
+};
+
+#define tx_ring_data(priv, idx) ((priv)->ring_data[idx])
+#define rx_ring_data(priv, idx) \
+       ((priv)->ring_data[(priv)->ae_handle->q_num + (idx)])
+
+void hns_ethtool_set_ops(struct net_device *ndev);
+void hns_nic_net_reset(struct net_device *ndev);
+void hns_nic_net_reinit(struct net_device *netdev);
+int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h);
+int hns_nic_net_xmit_hw(struct net_device *ndev,
+                       struct sk_buff *skb,
+                       struct hns_nic_ring_data *ring_data);
+
+#endif /**__HNS_ENET_H */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
new file mode 100644 (file)
index 0000000..a033212
--- /dev/null
@@ -0,0 +1,1214 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "hns_enet.h"
+
+#define HNS_PHY_PAGE_MDIX      0
+#define HNS_PHY_PAGE_LED       3
+#define HNS_PHY_PAGE_COPPER    0
+
+#define HNS_PHY_PAGE_REG       22      /* Page Selection Reg. */
+#define HNS_PHY_CSC_REG                16      /* Copper Specific Control Register */
+#define HNS_PHY_CSS_REG                17      /* Copper Specific Status Register */
+#define HNS_LED_FC_REG         16      /* LED Function Control Reg. */
+#define HNS_LED_PC_REG         17      /* LED Polarity Control Reg. */
+
+#define HNS_LED_FORCE_ON       9
+#define HNS_LED_FORCE_OFF      8
+
+#define HNS_CHIP_VERSION 660
+#define HNS_NET_STATS_CNT 26
+
+#define PHY_MDIX_CTRL_S                (5)
+#define PHY_MDIX_CTRL_M                (3 << PHY_MDIX_CTRL_S)
+
+#define PHY_MDIX_STATUS_B      (6)
+#define PHY_SPEED_DUP_RESOLVE_B        (11)
+
+/**
+ *hns_nic_get_link - get current link status
+ *@net_dev: net_device
+ *retuen 0 - success , negative --fail
+ */
+static u32 hns_nic_get_link(struct net_device *net_dev)
+{
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+       u32 link_stat = priv->link;
+       struct hnae_handle *h;
+
+       assert(priv && priv->ae_handle);
+       h = priv->ae_handle;
+
+       if (priv->phy) {
+               if (!genphy_update_link(priv->phy))
+                       link_stat = priv->phy->link;
+               else
+                       link_stat = 0;
+       }
+
+       if (h->dev && h->dev->ops && h->dev->ops->get_status)
+               link_stat = link_stat && h->dev->ops->get_status(h);
+       else
+               link_stat = 0;
+
+       return link_stat;
+}
+
+static void hns_get_mdix_mode(struct net_device *net_dev,
+                             struct ethtool_cmd *cmd)
+{
+       int mdix_ctrl, mdix, retval, is_resolved;
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+       struct phy_device *phy_dev = priv->phy;
+
+       if (!phy_dev || !phy_dev->bus) {
+               cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID;
+               cmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
+               return;
+       }
+
+       (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG,
+                           HNS_PHY_PAGE_MDIX);
+
+       retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSC_REG);
+       mdix_ctrl = hnae_get_field(retval, PHY_MDIX_CTRL_M, PHY_MDIX_CTRL_S);
+
+       retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSS_REG);
+       mdix = hnae_get_bit(retval, PHY_MDIX_STATUS_B);
+       is_resolved = hnae_get_bit(retval, PHY_SPEED_DUP_RESOLVE_B);
+
+       (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG,
+                           HNS_PHY_PAGE_COPPER);
+
+       switch (mdix_ctrl) {
+       case 0x0:
+               cmd->eth_tp_mdix_ctrl = ETH_TP_MDI;
+               break;
+       case 0x1:
+               cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_X;
+               break;
+       case 0x3:
+               cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO;
+               break;
+       default:
+               cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID;
+               break;
+       }
+
+       if (!is_resolved)
+               cmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
+       else if (mdix)
+               cmd->eth_tp_mdix = ETH_TP_MDI_X;
+       else
+               cmd->eth_tp_mdix = ETH_TP_MDI;
+}
+
+/**
+ *hns_nic_get_settings - implement ethtool get settings
+ *@net_dev: net_device
+ *@cmd: ethtool_cmd
+ *retuen 0 - success , negative --fail
+ */
+static int hns_nic_get_settings(struct net_device *net_dev,
+                               struct ethtool_cmd *cmd)
+{
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+       struct hnae_handle *h;
+       u32 link_stat;
+       int ret;
+       u8 duplex;
+       u16 speed;
+
+       if (!priv || !priv->ae_handle)
+               return -ESRCH;
+
+       h = priv->ae_handle;
+       if (!h->dev || !h->dev->ops || !h->dev->ops->get_info)
+               return -ESRCH;
+
+       ret = h->dev->ops->get_info(h, NULL, &speed, &duplex);
+       if (ret < 0) {
+               netdev_err(net_dev, "%s get_info error!\n", __func__);
+               return -EINVAL;
+       }
+
+       /* When there is no phy, autoneg is off. */
+       cmd->autoneg = false;
+       ethtool_cmd_speed_set(cmd, speed);
+       cmd->duplex = duplex;
+
+       if (priv->phy)
+               (void)phy_ethtool_gset(priv->phy, cmd);
+
+       link_stat = hns_nic_get_link(net_dev);
+       if (!link_stat) {
+               ethtool_cmd_speed_set(cmd, (u32)SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
+       }
+
+       if (cmd->autoneg)
+               cmd->advertising |= ADVERTISED_Autoneg;
+
+       cmd->supported |= h->if_support;
+       if (h->phy_if == PHY_INTERFACE_MODE_SGMII) {
+               cmd->supported |= SUPPORTED_TP;
+               cmd->advertising |= ADVERTISED_1000baseT_Full;
+       } else if (h->phy_if == PHY_INTERFACE_MODE_XGMII) {
+               cmd->supported |= SUPPORTED_FIBRE;
+               cmd->advertising |= ADVERTISED_10000baseKR_Full;
+       }
+
+       if (h->port_type == HNAE_PORT_SERVICE) {
+               cmd->port = PORT_FIBRE;
+               cmd->supported |= SUPPORTED_Pause;
+       } else {
+               cmd->port = PORT_TP;
+       }
+
+       cmd->transceiver = XCVR_EXTERNAL;
+       cmd->mdio_support = (ETH_MDIO_SUPPORTS_C45 | ETH_MDIO_SUPPORTS_C22);
+       hns_get_mdix_mode(net_dev, cmd);
+
+       return 0;
+}
+
+/**
+ *hns_nic_set_settings - implement ethtool set settings
+ *@net_dev: net_device
+ *@cmd: ethtool_cmd
+ *retuen 0 - success , negative --fail
+ */
+static int hns_nic_set_settings(struct net_device *net_dev,
+                               struct ethtool_cmd *cmd)
+{
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+       struct hnae_handle *h;
+       u32 speed;
+
+       if (!netif_running(net_dev))
+               return -ESRCH;
+
+       if (!priv || !priv->ae_handle || !priv->ae_handle->dev ||
+           !priv->ae_handle->dev->ops)
+               return -ENODEV;
+
+       h = priv->ae_handle;
+       speed = ethtool_cmd_speed(cmd);
+
+       if (h->phy_if == PHY_INTERFACE_MODE_XGMII) {
+               if (cmd->autoneg == AUTONEG_ENABLE || speed != SPEED_10000 ||
+                   cmd->duplex != DUPLEX_FULL)
+                       return -EINVAL;
+       } else if (h->phy_if == PHY_INTERFACE_MODE_SGMII) {
+               if (!priv->phy && cmd->autoneg == AUTONEG_ENABLE)
+                       return -EINVAL;
+
+               if (speed == SPEED_1000 && cmd->duplex == DUPLEX_HALF)
+                       return -EINVAL;
+               if (priv->phy)
+                       return phy_ethtool_sset(priv->phy, cmd);
+
+               if ((speed != SPEED_10 && speed != SPEED_100 &&
+                    speed != SPEED_1000) || (cmd->duplex != DUPLEX_HALF &&
+                    cmd->duplex != DUPLEX_FULL))
+                       return -EINVAL;
+       } else {
+               netdev_err(net_dev, "Not supported!");
+               return -ENOTSUPP;
+       }
+
+       if (h->dev->ops->adjust_link) {
+               h->dev->ops->adjust_link(h, (int)speed, cmd->duplex);
+               return 0;
+       }
+
+       netdev_err(net_dev, "Not supported!");
+       return -ENOTSUPP;
+}
+
+static const char hns_nic_test_strs[][ETH_GSTRING_LEN] = {
+       "Mac    Loopback test",
+       "Serdes Loopback test",
+       "Phy    Loopback test"
+};
+
+static int hns_nic_config_phy_loopback(struct phy_device *phy_dev, u8 en)
+{
+#define COPPER_CONTROL_REG 0
+#define PHY_LOOP_BACK BIT(14)
+       u16 val = 0;
+
+       if (phy_dev->is_c45) /* c45 branch adding for XGE PHY */
+               return -ENOTSUPP;
+
+       if (en) {
+               /* speed : 1000M */
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   HNS_PHY_PAGE_REG, 2);
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   21, 0x1046);
+               /* Force Master */
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   9, 0x1F00);
+               /* Soft-reset */
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   0, 0x9140);
+               /* If autoneg disabled,two soft-reset operations */
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   0, 0x9140);
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   22, 0xFA);
+
+               /* Default is 0x0400 */
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   1, 0x418);
+
+               /* Force 1000M Link, Default is 0x0200 */
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   7, 0x20C);
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   22, 0);
+
+               /* Enable MAC loop-back */
+               val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr,
+                                       COPPER_CONTROL_REG);
+               val |= PHY_LOOP_BACK;
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   COPPER_CONTROL_REG, val);
+       } else {
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   22, 0xFA);
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   1, 0x400);
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   7, 0x200);
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   22, 0);
+
+               val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr,
+                                       COPPER_CONTROL_REG);
+               val &= ~PHY_LOOP_BACK;
+               (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                   COPPER_CONTROL_REG, val);
+       }
+       return 0;
+}
+
+static int __lb_setup(struct net_device *ndev,
+                     enum hnae_loop loop)
+{
+       int ret = 0;
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct phy_device *phy_dev = priv->phy;
+       struct hnae_handle *h = priv->ae_handle;
+
+       switch (loop) {
+       case MAC_INTERNALLOOP_PHY:
+               if ((phy_dev) && (!phy_dev->is_c45))
+                       ret = hns_nic_config_phy_loopback(phy_dev, 0x1);
+               break;
+       case MAC_INTERNALLOOP_MAC:
+               if ((h->dev->ops->set_loopback) &&
+                   (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII))
+                       ret = h->dev->ops->set_loopback(h, loop, 0x1);
+               break;
+       case MAC_INTERNALLOOP_SERDES:
+               if (h->dev->ops->set_loopback)
+                       ret = h->dev->ops->set_loopback(h, loop, 0x1);
+               break;
+       case MAC_LOOP_NONE:
+               if ((phy_dev) && (!phy_dev->is_c45))
+                       ret |= hns_nic_config_phy_loopback(phy_dev, 0x0);
+
+               if (h->dev->ops->set_loopback) {
+                       if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII)
+                               ret |= h->dev->ops->set_loopback(h,
+                                       MAC_INTERNALLOOP_MAC, 0x0);
+
+                       ret |= h->dev->ops->set_loopback(h,
+                               MAC_INTERNALLOOP_SERDES, 0x0);
+               }
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int __lb_up(struct net_device *ndev,
+                  enum hnae_loop loop_mode)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h = priv->ae_handle;
+       int speed, duplex;
+       int ret;
+
+       hns_nic_net_reset(ndev);
+
+       if (priv->phy) {
+               phy_disconnect(priv->phy);
+               msleep(100);
+
+               ret = hns_nic_init_phy(ndev, h);
+               if (ret)
+                       return ret;
+       }
+
+       ret = __lb_setup(ndev, loop_mode);
+       if (ret)
+               return ret;
+
+       msleep(100);
+
+       ret = h->dev->ops->start ? h->dev->ops->start(h) : 0;
+       if (ret)
+               return ret;
+
+       if (priv->phy)
+               phy_start(priv->phy);
+
+       /* link adjust duplex*/
+       if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII)
+               speed = 1000;
+       else
+               speed = 10000;
+       duplex = 1;
+
+       h->dev->ops->adjust_link(h, speed, duplex);
+
+       return 0;
+}
+
+static void __lb_other_process(struct hns_nic_ring_data *ring_data,
+                              struct sk_buff *skb)
+{
+       struct net_device *ndev;
+       struct hnae_ring *ring;
+       struct netdev_queue *dev_queue;
+       struct sk_buff *new_skb;
+       unsigned int frame_size;
+       int check_ok;
+       u32 i;
+       char buff[33]; /* 32B data and the last character '\0' */
+
+       if (!ring_data) { /* Just for doing create frame*/
+               frame_size = skb->len;
+               memset(skb->data, 0xFF, frame_size);
+               frame_size &= ~1ul;
+               memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1);
+               memset(&skb->data[frame_size / 2 + 10], 0xBE,
+                      frame_size / 2 - 11);
+               memset(&skb->data[frame_size / 2 + 12], 0xAF,
+                      frame_size / 2 - 13);
+               return;
+       }
+
+       ring = ring_data->ring;
+       ndev = ring_data->napi.dev;
+       if (is_tx_ring(ring)) { /* for tx queue reset*/
+               dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index);
+               netdev_tx_reset_queue(dev_queue);
+               return;
+       }
+
+       frame_size = skb->len;
+       frame_size &= ~1ul;
+       /* for mutl buffer*/
+       new_skb = skb_copy(skb, GFP_ATOMIC);
+       dev_kfree_skb_any(skb);
+       skb = new_skb;
+
+       check_ok = 0;
+       if (*(skb->data + 10) == 0xFF) { /* for rx check frame*/
+               if ((*(skb->data + frame_size / 2 + 10) == 0xBE) &&
+                   (*(skb->data + frame_size / 2 + 12) == 0xAF))
+                       check_ok = 1;
+       }
+
+       if (check_ok) {
+               ndev->stats.rx_packets++;
+               ndev->stats.rx_bytes += skb->len;
+       } else {
+               ndev->stats.rx_frame_errors++;
+               for (i = 0; i < skb->len; i++) {
+                       snprintf(buff + i % 16 * 2, 3, /* tailing \0*/
+                                "%02x", *(skb->data + i));
+                       if ((i % 16 == 15) || (i == skb->len - 1))
+                               pr_info("%s\n", buff);
+               }
+       }
+       dev_kfree_skb_any(skb);
+}
+
+static int __lb_clean_rings(struct hns_nic_priv *priv,
+                           int ringid0, int ringid1, int budget)
+{
+       int i, ret;
+       struct hns_nic_ring_data *ring_data;
+       struct net_device *ndev = priv->netdev;
+       unsigned long rx_packets = ndev->stats.rx_packets;
+       unsigned long rx_bytes = ndev->stats.rx_bytes;
+       unsigned long rx_frame_errors = ndev->stats.rx_frame_errors;
+
+       for (i = ringid0; i <= ringid1; i++) {
+               ring_data = &priv->ring_data[i];
+               (void)ring_data->poll_one(ring_data,
+                                         budget, __lb_other_process);
+       }
+       ret = (int)(ndev->stats.rx_packets - rx_packets);
+       ndev->stats.rx_packets = rx_packets;
+       ndev->stats.rx_bytes = rx_bytes;
+       ndev->stats.rx_frame_errors = rx_frame_errors;
+       return ret;
+}
+
+/**
+ * nic_run_loopback_test -  run loopback test
+ * @nic_dev: net device
+ * @loopback_type: loopback type
+ */
+static int __lb_run_test(struct net_device *ndev,
+                        enum hnae_loop loop_mode)
+{
+#define NIC_LB_TEST_PKT_NUM_PER_CYCLE 1
+#define NIC_LB_TEST_RING_ID 0
+#define NIC_LB_TEST_FRAME_SIZE 128
+/* nic loopback test err  */
+#define NIC_LB_TEST_NO_MEM_ERR 1
+#define NIC_LB_TEST_TX_CNT_ERR 2
+#define NIC_LB_TEST_RX_CNT_ERR 3
+#define NIC_LB_TEST_RX_PKG_ERR 4
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h = priv->ae_handle;
+       int i, j, lc, good_cnt, ret_val = 0;
+       unsigned int size;
+       netdev_tx_t tx_ret_val;
+       struct sk_buff *skb;
+
+       size = NIC_LB_TEST_FRAME_SIZE;
+       /* allocate test skb */
+       skb = alloc_skb(size, GFP_KERNEL);
+       if (!skb)
+               return NIC_LB_TEST_NO_MEM_ERR;
+
+       /* place data into test skb */
+       (void)skb_put(skb, size);
+       __lb_other_process(NULL, skb);
+       skb->queue_mapping = NIC_LB_TEST_RING_ID;
+
+       lc = 1;
+       for (j = 0; j < lc; j++) {
+               /* reset count of good packets */
+               good_cnt = 0;
+               /* place 64 packets on the transmit queue*/
+               for (i = 0; i < NIC_LB_TEST_PKT_NUM_PER_CYCLE; i++) {
+                       (void)skb_get(skb);
+
+                       tx_ret_val = (netdev_tx_t)hns_nic_net_xmit_hw(
+                               ndev, skb,
+                               &tx_ring_data(priv, skb->queue_mapping));
+                       if (tx_ret_val == NETDEV_TX_OK)
+                               good_cnt++;
+                       else
+                               break;
+               }
+               if (good_cnt != NIC_LB_TEST_PKT_NUM_PER_CYCLE) {
+                       ret_val = NIC_LB_TEST_TX_CNT_ERR;
+                       dev_err(priv->dev, "%s sent fail, cnt=0x%x, budget=0x%x\n",
+                               hns_nic_test_strs[loop_mode], good_cnt,
+                               NIC_LB_TEST_PKT_NUM_PER_CYCLE);
+                       break;
+               }
+
+               /* allow 100 milliseconds for packets to go from Tx to Rx */
+               msleep(100);
+
+               good_cnt = __lb_clean_rings(priv,
+                                           h->q_num, h->q_num * 2 - 1,
+                                           NIC_LB_TEST_PKT_NUM_PER_CYCLE);
+               if (good_cnt != NIC_LB_TEST_PKT_NUM_PER_CYCLE) {
+                       ret_val = NIC_LB_TEST_RX_CNT_ERR;
+                       dev_err(priv->dev, "%s recv fail, cnt=0x%x, budget=0x%x\n",
+                               hns_nic_test_strs[loop_mode], good_cnt,
+                               NIC_LB_TEST_PKT_NUM_PER_CYCLE);
+                       break;
+               }
+               (void)__lb_clean_rings(priv,
+                                      NIC_LB_TEST_RING_ID, NIC_LB_TEST_RING_ID,
+                                      NIC_LB_TEST_PKT_NUM_PER_CYCLE);
+       }
+
+       /* free the original skb */
+       kfree_skb(skb);
+
+       return ret_val;
+}
+
+static int __lb_down(struct net_device *ndev)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       struct hnae_handle *h = priv->ae_handle;
+       int ret;
+
+       ret = __lb_setup(ndev, MAC_LOOP_NONE);
+       if (ret)
+               netdev_err(ndev, "%s: __lb_setup return error(%d)!\n",
+                          __func__,
+                          ret);
+
+       if (priv->phy)
+               phy_stop(priv->phy);
+
+       if (h->dev->ops->stop)
+               h->dev->ops->stop(h);
+
+       usleep_range(10000, 20000);
+       (void)__lb_clean_rings(priv, 0, h->q_num - 1, 256);
+
+       hns_nic_net_reset(ndev);
+
+       return 0;
+}
+
+/**
+ * hns_nic_self_test - self test
+ * @dev: net device
+ * @eth_test: test cmd
+ * @data: test result
+ */
+static void hns_nic_self_test(struct net_device *ndev,
+                             struct ethtool_test *eth_test, u64 *data)
+{
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+       bool if_running = netif_running(ndev);
+#define SELF_TEST_TPYE_NUM 3
+       int st_param[SELF_TEST_TPYE_NUM][2];
+       int i;
+       int test_index = 0;
+
+       st_param[0][0] = MAC_INTERNALLOOP_MAC; /* XGE not supported lb */
+       st_param[0][1] = (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII);
+       st_param[1][0] = MAC_INTERNALLOOP_SERDES;
+       st_param[1][1] = 1; /*serdes must exist*/
+       st_param[2][0] = MAC_INTERNALLOOP_PHY; /* only supporte phy node*/
+       st_param[2][1] = ((!!(priv->ae_handle->phy_node)) &&
+               (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII));
+
+       if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
+               set_bit(NIC_STATE_TESTING, &priv->state);
+
+               if (if_running)
+                       (void)dev_close(ndev);
+
+               for (i = 0; i < SELF_TEST_TPYE_NUM; i++) {
+                       if (!st_param[i][1])
+                               continue;       /* NEXT testing */
+
+                       data[test_index] = __lb_up(ndev,
+                               (enum hnae_loop)st_param[i][0]);
+                       if (!data[test_index]) {
+                               data[test_index] = __lb_run_test(
+                                       ndev, (enum hnae_loop)st_param[i][0]);
+                               (void)__lb_down(ndev);
+                       }
+
+                       if (data[test_index])
+                               eth_test->flags |= ETH_TEST_FL_FAILED;
+
+                       test_index++;
+               }
+
+               hns_nic_net_reset(priv->netdev);
+
+               clear_bit(NIC_STATE_TESTING, &priv->state);
+
+               if (if_running)
+                       (void)dev_open(ndev);
+       }
+       /* Online tests aren't run; pass by default */
+
+       (void)msleep_interruptible(4 * 1000);
+}
+
+/**
+ * hns_nic_get_drvinfo - get net driver info
+ * @dev: net device
+ * @drvinfo: driver info
+ */
+static void hns_nic_get_drvinfo(struct net_device *net_dev,
+                               struct ethtool_drvinfo *drvinfo)
+{
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+
+       assert(priv);
+
+       strncpy(drvinfo->version, HNAE_DRIVER_VERSION,
+               sizeof(drvinfo->version));
+       drvinfo->version[sizeof(drvinfo->version) - 1] = '\0';
+
+       strncpy(drvinfo->driver, HNAE_DRIVER_NAME, sizeof(drvinfo->driver));
+       drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0';
+
+       strncpy(drvinfo->bus_info, priv->dev->bus->name,
+               sizeof(drvinfo->bus_info));
+       drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0';
+
+       strncpy(drvinfo->fw_version, "N/A", ETHTOOL_FWVERS_LEN);
+}
+
+/**
+ * hns_get_ringparam - get ring parameter
+ * @dev: net device
+ * @param: ethtool parameter
+ */
+void hns_get_ringparam(struct net_device *net_dev,
+                      struct ethtool_ringparam *param)
+{
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+       struct hnae_ae_ops *ops;
+       struct hnae_queue *queue;
+       u32 uplimit = 0;
+
+       queue = priv->ae_handle->qs[0];
+       ops = priv->ae_handle->dev->ops;
+
+       if (ops->get_ring_bdnum_limit)
+               ops->get_ring_bdnum_limit(queue, &uplimit);
+
+       param->rx_max_pending = uplimit;
+       param->tx_max_pending = uplimit;
+       param->rx_pending = queue->rx_ring.desc_num;
+       param->tx_pending = queue->tx_ring.desc_num;
+}
+
+/**
+ * hns_get_pauseparam - get pause parameter
+ * @dev: net device
+ * @param: pause parameter
+ */
+static void hns_get_pauseparam(struct net_device *net_dev,
+                              struct ethtool_pauseparam *param)
+{
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+       struct hnae_ae_ops *ops;
+
+       ops = priv->ae_handle->dev->ops;
+
+       if (ops->get_pauseparam)
+               ops->get_pauseparam(priv->ae_handle, &param->autoneg,
+                                           &param->rx_pause, &param->tx_pause);
+}
+
+/**
+ * hns_set_pauseparam - set pause parameter
+ * @dev: net device
+ * @param: pause parameter
+ *
+ * Return 0 on success, negative on failure
+ */
+static int hns_set_pauseparam(struct net_device *net_dev,
+                             struct ethtool_pauseparam *param)
+{
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+       struct hnae_handle *h;
+       struct hnae_ae_ops *ops;
+
+       assert(priv || priv->ae_handle);
+
+       h = priv->ae_handle;
+       ops = h->dev->ops;
+
+       if (!ops->set_pauseparam)
+               return -ESRCH;
+
+       return ops->set_pauseparam(priv->ae_handle, param->autoneg,
+                                  param->rx_pause, param->tx_pause);
+}
+
+/**
+ * hns_get_coalesce - get coalesce info.
+ * @dev: net device
+ * @ec: coalesce info.
+ *
+ * Return 0 on success, negative on failure.
+ */
+static int hns_get_coalesce(struct net_device *net_dev,
+                           struct ethtool_coalesce *ec)
+{
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+       struct hnae_ae_ops *ops;
+
+       ops = priv->ae_handle->dev->ops;
+
+       ec->use_adaptive_rx_coalesce = 1;
+       ec->use_adaptive_tx_coalesce = 1;
+
+       if ((!ops->get_coalesce_usecs) ||
+           (!ops->get_rx_max_coalesced_frames))
+               return -ESRCH;
+
+       ops->get_coalesce_usecs(priv->ae_handle,
+                                       &ec->tx_coalesce_usecs,
+                                       &ec->rx_coalesce_usecs);
+
+       ops->get_rx_max_coalesced_frames(
+               priv->ae_handle,
+               &ec->tx_max_coalesced_frames,
+               &ec->rx_max_coalesced_frames);
+
+       return 0;
+}
+
+/**
+ * hns_set_coalesce - set coalesce info.
+ * @dev: net device
+ * @ec: coalesce info.
+ *
+ * Return 0 on success, negative on failure.
+ */
+static int hns_set_coalesce(struct net_device *net_dev,
+                           struct ethtool_coalesce *ec)
+{
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+       struct hnae_ae_ops *ops;
+       int ret;
+
+       assert(priv || priv->ae_handle);
+
+       ops = priv->ae_handle->dev->ops;
+
+       if (ec->tx_coalesce_usecs != ec->rx_coalesce_usecs)
+               return -EINVAL;
+
+       if (ec->rx_max_coalesced_frames != ec->tx_max_coalesced_frames)
+               return -EINVAL;
+
+       if ((!ops->set_coalesce_usecs) ||
+           (!ops->set_coalesce_frames))
+               return -ESRCH;
+
+       ops->set_coalesce_usecs(priv->ae_handle,
+                                       ec->rx_coalesce_usecs);
+
+       ret = ops->set_coalesce_frames(
+               priv->ae_handle,
+               ec->rx_max_coalesced_frames);
+
+       return ret;
+}
+
+/**
+ * hns_get_channels - get channel info.
+ * @dev: net device
+ * @ch: channel info.
+ */
+void hns_get_channels(struct net_device *net_dev, struct ethtool_channels *ch)
+{
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+
+       ch->max_rx = priv->ae_handle->q_num;
+       ch->max_tx = priv->ae_handle->q_num;
+
+       ch->rx_count = priv->ae_handle->q_num;
+       ch->tx_count = priv->ae_handle->q_num;
+}
+
+/**
+ * get_ethtool_stats - get detail statistics.
+ * @dev: net device
+ * @stats: statistics info.
+ * @data: statistics data.
+ */
+void hns_get_ethtool_stats(struct net_device *netdev,
+                          struct ethtool_stats *stats, u64 *data)
+{
+       u64 *p = data;
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct hnae_handle *h = priv->ae_handle;
+       const struct rtnl_link_stats64 *net_stats;
+       struct rtnl_link_stats64 temp;
+
+       if (!h->dev->ops->get_stats || !h->dev->ops->update_stats) {
+               netdev_err(netdev, "get_stats or update_stats is null!\n");
+               return;
+       }
+
+       h->dev->ops->update_stats(h, &netdev->stats);
+
+       net_stats = dev_get_stats(netdev, &temp);
+
+       /* get netdev statistics */
+       p[0] = net_stats->rx_packets;
+       p[1] = net_stats->tx_packets;
+       p[2] = net_stats->rx_bytes;
+       p[3] = net_stats->tx_bytes;
+       p[4] = net_stats->rx_errors;
+       p[5] = net_stats->tx_errors;
+       p[6] = net_stats->rx_dropped;
+       p[7] = net_stats->tx_dropped;
+       p[8] = net_stats->multicast;
+       p[9] = net_stats->collisions;
+       p[10] = net_stats->rx_over_errors;
+       p[11] = net_stats->rx_crc_errors;
+       p[12] = net_stats->rx_frame_errors;
+       p[13] = net_stats->rx_fifo_errors;
+       p[14] = net_stats->rx_missed_errors;
+       p[15] = net_stats->tx_aborted_errors;
+       p[16] = net_stats->tx_carrier_errors;
+       p[17] = net_stats->tx_fifo_errors;
+       p[18] = net_stats->tx_heartbeat_errors;
+       p[19] = net_stats->rx_length_errors;
+       p[20] = net_stats->tx_window_errors;
+       p[21] = net_stats->rx_compressed;
+       p[22] = net_stats->tx_compressed;
+
+       p[23] = netdev->rx_dropped.counter;
+       p[24] = netdev->tx_dropped.counter;
+
+       p[25] = priv->tx_timeout_count;
+
+       /* get driver statistics */
+       h->dev->ops->get_stats(h, &p[26]);
+}
+
+/**
+ * get_strings: Return a set of strings that describe the requested objects
+ * @dev: net device
+ * @stats: string set ID.
+ * @data: objects data.
+ */
+void hns_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct hnae_handle *h = priv->ae_handle;
+       char *buff = (char *)data;
+
+       if (!h->dev->ops->get_strings) {
+               netdev_err(netdev, "h->dev->ops->get_strings is null!\n");
+               return;
+       }
+
+       if (stringset == ETH_SS_TEST) {
+               if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII) {
+                       memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_MAC],
+                              ETH_GSTRING_LEN);
+                       buff += ETH_GSTRING_LEN;
+               }
+               memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_SERDES],
+                      ETH_GSTRING_LEN);
+               buff += ETH_GSTRING_LEN;
+               if ((priv->phy) && (!priv->phy->is_c45))
+                       memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_PHY],
+                              ETH_GSTRING_LEN);
+
+       } else {
+               snprintf(buff, ETH_GSTRING_LEN, "rx_packets");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "tx_packets");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "rx_bytes");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "tx_bytes");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "rx_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "tx_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "rx_dropped");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "tx_dropped");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "multicast");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "collisions");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "rx_over_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "rx_crc_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "rx_frame_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "rx_fifo_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "rx_missed_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "tx_aborted_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "tx_carrier_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "tx_fifo_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "tx_heartbeat_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "rx_length_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "tx_window_errors");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "rx_compressed");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "tx_compressed");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "netdev_rx_dropped");
+               buff = buff + ETH_GSTRING_LEN;
+               snprintf(buff, ETH_GSTRING_LEN, "netdev_tx_dropped");
+               buff = buff + ETH_GSTRING_LEN;
+
+               snprintf(buff, ETH_GSTRING_LEN, "netdev_tx_timeout");
+               buff = buff + ETH_GSTRING_LEN;
+
+               h->dev->ops->get_strings(h, stringset, (u8 *)buff);
+       }
+}
+
+/**
+ * nic_get_sset_count - get string set count witch returned by nic_get_strings.
+ * @dev: net device
+ * @stringset: string set index, 0: self test string; 1: statistics string.
+ *
+ * Return string set count.
+ */
+int hns_get_sset_count(struct net_device *netdev, int stringset)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct hnae_handle *h = priv->ae_handle;
+       struct hnae_ae_ops *ops = h->dev->ops;
+
+       if (!ops->get_sset_count) {
+               netdev_err(netdev, "get_sset_count is null!\n");
+               return -EOPNOTSUPP;
+       }
+       if (stringset == ETH_SS_TEST) {
+               u32 cnt = (sizeof(hns_nic_test_strs) / ETH_GSTRING_LEN);
+
+               if (priv->ae_handle->phy_if == PHY_INTERFACE_MODE_XGMII)
+                       cnt--;
+
+               if ((!priv->phy) || (priv->phy->is_c45))
+                       cnt--;
+
+               return cnt;
+       } else {
+               return (HNS_NET_STATS_CNT + ops->get_sset_count(h, stringset));
+       }
+}
+
+/**
+ * hns_phy_led_set - set phy LED status.
+ * @dev: net device
+ * @value: LED state.
+ *
+ * Return 0 on success, negative on failure.
+ */
+int hns_phy_led_set(struct net_device *netdev, int value)
+{
+       int retval;
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct phy_device *phy_dev = priv->phy;
+
+       if (!phy_dev->bus) {
+               netdev_err(netdev, "phy_dev->bus is null!\n");
+               return -EINVAL;
+       }
+       retval = mdiobus_write(phy_dev->bus, phy_dev->addr,
+                              HNS_PHY_PAGE_REG, HNS_PHY_PAGE_LED);
+       retval = mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_LED_FC_REG,
+                              value);
+       retval = mdiobus_write(phy_dev->bus, phy_dev->addr,
+                              HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER);
+       if (retval) {
+               netdev_err(netdev, "mdiobus_write fail !\n");
+               return retval;
+       }
+       return 0;
+}
+
+/**
+ * nic_set_phys_id - set phy identify LED.
+ * @dev: net device
+ * @state: LED state.
+ *
+ * Return 0 on success, negative on failure.
+ */
+int hns_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct hnae_handle *h = priv->ae_handle;
+       struct phy_device *phy_dev = priv->phy;
+       int ret;
+
+       if (phy_dev)
+               switch (state) {
+               case ETHTOOL_ID_ACTIVE:
+                       ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                           HNS_PHY_PAGE_REG,
+                                           HNS_PHY_PAGE_LED);
+                       if (ret)
+                               return ret;
+
+                       priv->phy_led_val = (u16)mdiobus_read(phy_dev->bus,
+                                                             phy_dev->addr,
+                                                             HNS_LED_FC_REG);
+
+                       ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                           HNS_PHY_PAGE_REG,
+                                           HNS_PHY_PAGE_COPPER);
+                       if (ret)
+                               return ret;
+                       return 2;
+               case ETHTOOL_ID_ON:
+                       ret = hns_phy_led_set(netdev, HNS_LED_FORCE_ON);
+                       if (ret)
+                               return ret;
+                       break;
+               case ETHTOOL_ID_OFF:
+                       ret = hns_phy_led_set(netdev, HNS_LED_FORCE_OFF);
+                       if (ret)
+                               return ret;
+                       break;
+               case ETHTOOL_ID_INACTIVE:
+                       ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                           HNS_PHY_PAGE_REG,
+                                           HNS_PHY_PAGE_LED);
+                       if (ret)
+                               return ret;
+
+                       ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                           HNS_LED_FC_REG, priv->phy_led_val);
+                       if (ret)
+                               return ret;
+
+                       ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
+                                           HNS_PHY_PAGE_REG,
+                                           HNS_PHY_PAGE_COPPER);
+                       if (ret)
+                               return ret;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       else
+               switch (state) {
+               case ETHTOOL_ID_ACTIVE:
+                       return h->dev->ops->set_led_id(h, HNAE_LED_ACTIVE);
+               case ETHTOOL_ID_ON:
+                       return h->dev->ops->set_led_id(h, HNAE_LED_ON);
+               case ETHTOOL_ID_OFF:
+                       return h->dev->ops->set_led_id(h, HNAE_LED_OFF);
+               case ETHTOOL_ID_INACTIVE:
+                       return h->dev->ops->set_led_id(h, HNAE_LED_INACTIVE);
+               default:
+                       return -EINVAL;
+               }
+
+       return 0;
+}
+
+/**
+ * hns_get_regs - get net device register
+ * @dev: net device
+ * @cmd: ethtool cmd
+ * @date: register data
+ */
+void hns_get_regs(struct net_device *net_dev, struct ethtool_regs *cmd,
+                 void *data)
+{
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+       struct hnae_ae_ops *ops;
+
+       assert(priv || priv->ae_handle);
+
+       ops = priv->ae_handle->dev->ops;
+
+       cmd->version = HNS_CHIP_VERSION;
+       if (!ops->get_regs) {
+               netdev_err(net_dev, "ops->get_regs is null!\n");
+               return;
+       }
+       ops->get_regs(priv->ae_handle, data);
+}
+
+/**
+ * nic_get_regs_len - get total register len.
+ * @dev: net device
+ *
+ * Return total register len.
+ */
+static int hns_get_regs_len(struct net_device *net_dev)
+{
+       u32 reg_num;
+       struct hns_nic_priv *priv = netdev_priv(net_dev);
+       struct hnae_ae_ops *ops;
+
+       assert(priv || priv->ae_handle);
+
+       ops = priv->ae_handle->dev->ops;
+       if (!ops->get_regs_len) {
+               netdev_err(net_dev, "ops->get_regs_len is null!\n");
+               return -EOPNOTSUPP;
+       }
+
+       reg_num = ops->get_regs_len(priv->ae_handle);
+       if (reg_num > 0)
+               return reg_num * sizeof(u32);
+       else
+               return reg_num; /* error code */
+}
+
+/**
+ * hns_nic_nway_reset - nway reset
+ * @dev: net device
+ *
+ * Return 0 on success, negative on failure
+ */
+static int hns_nic_nway_reset(struct net_device *netdev)
+{
+       int ret = 0;
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct phy_device *phy = priv->phy;
+
+       if (netif_running(netdev)) {
+               if (phy)
+                       ret = genphy_restart_aneg(phy);
+       }
+
+       return ret;
+}
+
+static struct ethtool_ops hns_ethtool_ops = {
+       .get_drvinfo = hns_nic_get_drvinfo,
+       .get_link  = hns_nic_get_link,
+       .get_settings  = hns_nic_get_settings,
+       .set_settings  = hns_nic_set_settings,
+       .get_ringparam = hns_get_ringparam,
+       .get_pauseparam = hns_get_pauseparam,
+       .set_pauseparam = hns_set_pauseparam,
+       .get_coalesce = hns_get_coalesce,
+       .set_coalesce = hns_set_coalesce,
+       .get_channels = hns_get_channels,
+       .self_test = hns_nic_self_test,
+       .get_strings = hns_get_strings,
+       .get_sset_count = hns_get_sset_count,
+       .get_ethtool_stats = hns_get_ethtool_stats,
+       .set_phys_id = hns_set_phys_id,
+       .get_regs_len = hns_get_regs_len,
+       .get_regs = hns_get_regs,
+       .nway_reset = hns_nic_nway_reset,
+};
+
+void hns_ethtool_set_ops(struct net_device *ndev)
+{
+       ndev->ethtool_ops = &hns_ethtool_ops;
+}
diff --git a/drivers/net/ethernet/hisilicon/hns_mdio.c b/drivers/net/ethernet/hisilicon/hns_mdio.c
new file mode 100644 (file)
index 0000000..37491c8
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ *
+ * 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/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock_types.h>
+
+#define MDIO_DRV_NAME "Hi-HNS_MDIO"
+#define MDIO_BUS_NAME "Hisilicon MII Bus"
+#define MDIO_DRV_VERSION "1.3.0"
+#define MDIO_COPYRIGHT "Copyright(c) 2015 Huawei Corporation."
+#define MDIO_DRV_STRING MDIO_BUS_NAME
+#define MDIO_DEFAULT_DEVICE_DESCR MDIO_BUS_NAME
+
+#define MDIO_CTL_DEV_ADDR(x)   (x & 0x1f)
+#define MDIO_CTL_PORT_ADDR(x)  ((x & 0x1f) << 5)
+
+#define MDIO_TIMEOUT                   1000000
+
+struct hns_mdio_device {
+       void *vbase;            /* mdio reg base address */
+       struct regmap *subctrl_vbase;
+};
+
+/* mdio reg */
+#define MDIO_COMMAND_REG               0x0
+#define MDIO_ADDR_REG                  0x4
+#define MDIO_WDATA_REG                 0x8
+#define MDIO_RDATA_REG                 0xc
+#define MDIO_STA_REG                   0x10
+
+/* cfg phy bit map */
+#define MDIO_CMD_DEVAD_M       0x1f
+#define MDIO_CMD_DEVAD_S       0
+#define MDIO_CMD_PRTAD_M       0x1f
+#define MDIO_CMD_PRTAD_S       5
+#define MDIO_CMD_OP_M          0x3
+#define MDIO_CMD_OP_S          10
+#define MDIO_CMD_ST_M          0x3
+#define MDIO_CMD_ST_S          12
+#define MDIO_CMD_START_B       14
+
+#define MDIO_ADDR_DATA_M       0xffff
+#define MDIO_ADDR_DATA_S       0
+
+#define MDIO_WDATA_DATA_M      0xffff
+#define MDIO_WDATA_DATA_S      0
+
+#define MDIO_RDATA_DATA_M      0xffff
+#define MDIO_RDATA_DATA_S      0
+
+#define MDIO_STATE_STA_B       0
+
+enum mdio_st_clause {
+       MDIO_ST_CLAUSE_45 = 0,
+       MDIO_ST_CLAUSE_22
+};
+
+enum mdio_c22_op_seq {
+       MDIO_C22_WRITE = 1,
+       MDIO_C22_READ = 2
+};
+
+enum mdio_c45_op_seq {
+       MDIO_C45_WRITE_ADDR = 0,
+       MDIO_C45_WRITE_DATA,
+       MDIO_C45_READ_INCREMENT,
+       MDIO_C45_READ
+};
+
+/* peri subctrl reg */
+#define MDIO_SC_CLK_EN         0x338
+#define MDIO_SC_CLK_DIS                0x33C
+#define MDIO_SC_RESET_REQ      0xA38
+#define MDIO_SC_RESET_DREQ     0xA3C
+#define MDIO_SC_CTRL           0x2010
+#define MDIO_SC_CLK_ST         0x531C
+#define MDIO_SC_RESET_ST       0x5A1C
+
+static void mdio_write_reg(void *base, u32 reg, u32 value)
+{
+       u8 __iomem *reg_addr = (u8 __iomem *)base;
+
+       writel_relaxed(value, reg_addr + reg);
+}
+
+#define MDIO_WRITE_REG(a, reg, value) \
+       mdio_write_reg((a)->vbase, (reg), (value))
+
+static u32 mdio_read_reg(void *base, u32 reg)
+{
+       u8 __iomem *reg_addr = (u8 __iomem *)base;
+
+       return readl_relaxed(reg_addr + reg);
+}
+
+#define mdio_set_field(origin, mask, shift, val) \
+       do { \
+               (origin) &= (~((mask) << (shift))); \
+               (origin) |= (((val) & (mask)) << (shift)); \
+       } while (0)
+
+#define mdio_get_field(origin, mask, shift) (((origin) >> (shift)) & (mask))
+
+static void mdio_set_reg_field(void *base, u32 reg, u32 mask, u32 shift,
+                              u32 val)
+{
+       u32 origin = mdio_read_reg(base, reg);
+
+       mdio_set_field(origin, mask, shift, val);
+       mdio_write_reg(base, reg, origin);
+}
+
+#define MDIO_SET_REG_FIELD(dev, reg, mask, shift, val) \
+       mdio_set_reg_field((dev)->vbase, (reg), (mask), (shift), (val))
+
+static u32 mdio_get_reg_field(void *base, u32 reg, u32 mask, u32 shift)
+{
+       u32 origin;
+
+       origin = mdio_read_reg(base, reg);
+       return mdio_get_field(origin, mask, shift);
+}
+
+#define MDIO_GET_REG_FIELD(dev, reg, mask, shift) \
+               mdio_get_reg_field((dev)->vbase, (reg), (mask), (shift))
+
+#define MDIO_GET_REG_BIT(dev, reg, bit) \
+               mdio_get_reg_field((dev)->vbase, (reg), 0x1ull, (bit))
+
+#define MDIO_CHECK_SET_ST      1
+#define MDIO_CHECK_CLR_ST      0
+
+static int mdio_sc_cfg_reg_write(struct hns_mdio_device *mdio_dev,
+                                u32 cfg_reg, u32 set_val,
+                                u32 st_reg, u32 st_msk, u8 check_st)
+{
+       u32 time_cnt;
+       u32 reg_value;
+
+       regmap_write(mdio_dev->subctrl_vbase, cfg_reg, set_val);
+
+       for (time_cnt = MDIO_TIMEOUT; time_cnt; time_cnt--) {
+               regmap_read(mdio_dev->subctrl_vbase, st_reg, &reg_value);
+               reg_value &= st_msk;
+               if ((!!check_st) == (!!reg_value))
+                       break;
+       }
+
+       if ((!!check_st) != (!!reg_value))
+               return -EBUSY;
+
+       return 0;
+}
+
+static int hns_mdio_wait_ready(struct mii_bus *bus)
+{
+       struct hns_mdio_device *mdio_dev = bus->priv;
+       int i;
+       u32 cmd_reg_value = 1;
+
+       /* waitting for MDIO_COMMAND_REG 's mdio_start==0 */
+       /* after that can do read or write*/
+       for (i = 0; cmd_reg_value; i++) {
+               cmd_reg_value = MDIO_GET_REG_BIT(mdio_dev,
+                                                MDIO_COMMAND_REG,
+                                                MDIO_CMD_START_B);
+               if (i == MDIO_TIMEOUT)
+                       return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static void hns_mdio_cmd_write(struct hns_mdio_device *mdio_dev,
+                              u8 is_c45, u8 op, u8 phy_id, u16 cmd)
+{
+       u32 cmd_reg_value;
+       u8 st = is_c45 ? MDIO_ST_CLAUSE_45 : MDIO_ST_CLAUSE_22;
+
+       cmd_reg_value = st << MDIO_CMD_ST_S;
+       cmd_reg_value |= op << MDIO_CMD_OP_S;
+       cmd_reg_value |=
+               (phy_id & MDIO_CMD_PRTAD_M) << MDIO_CMD_PRTAD_S;
+       cmd_reg_value |= (cmd & MDIO_CMD_DEVAD_M) << MDIO_CMD_DEVAD_S;
+       cmd_reg_value |= 1 << MDIO_CMD_START_B;
+
+       MDIO_WRITE_REG(mdio_dev, MDIO_COMMAND_REG, cmd_reg_value);
+}
+
+/**
+ * hns_mdio_write - access phy register
+ * @bus: mdio bus
+ * @phy_id: phy id
+ * @regnum: register num
+ * @value: register value
+ *
+ * Return 0 on success, negative on failure
+ */
+static int hns_mdio_write(struct mii_bus *bus,
+                         int phy_id, int regnum, u16 data)
+{
+       int ret;
+       struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv;
+       u8 devad = ((regnum >> 16) & 0x1f);
+       u8 is_c45 = !!(regnum & MII_ADDR_C45);
+       u16 reg = (u16)(regnum & 0xffff);
+       u8 op;
+       u16 cmd_reg_cfg;
+
+       dev_dbg(&bus->dev, "mdio write %s,base is %p\n",
+               bus->id, mdio_dev->vbase);
+       dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x, write data=%d\n",
+               phy_id, is_c45, devad, reg, data);
+
+       /* wait for ready */
+       ret = hns_mdio_wait_ready(bus);
+       if (ret) {
+               dev_err(&bus->dev, "MDIO bus is busy\n");
+               return ret;
+       }
+
+       if (!is_c45) {
+               cmd_reg_cfg = reg;
+               op = MDIO_C22_WRITE;
+       } else {
+               /* config the cmd-reg to write addr*/
+               MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M,
+                                  MDIO_ADDR_DATA_S, reg);
+
+               hns_mdio_cmd_write(mdio_dev, is_c45,
+                                  MDIO_C45_WRITE_ADDR, phy_id, devad);
+
+               /* check for read or write opt is finished */
+               ret = hns_mdio_wait_ready(bus);
+               if (ret) {
+                       dev_err(&bus->dev, "MDIO bus is busy\n");
+                       return ret;
+               }
+
+               /* config the data needed writing */
+               cmd_reg_cfg = devad;
+               op = MDIO_C45_WRITE_ADDR;
+       }
+
+       MDIO_SET_REG_FIELD(mdio_dev, MDIO_WDATA_REG, MDIO_WDATA_DATA_M,
+                          MDIO_WDATA_DATA_S, data);
+
+       hns_mdio_cmd_write(mdio_dev, is_c45, op, phy_id, cmd_reg_cfg);
+
+       return 0;
+}
+
+/**
+ * hns_mdio_read - access phy register
+ * @bus: mdio bus
+ * @phy_id: phy id
+ * @regnum: register num
+ * @value: register value
+ *
+ * Return phy register value
+ */
+static int hns_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+       int ret;
+       u16 reg_val = 0;
+       u8 devad = ((regnum >> 16) & 0x1f);
+       u8 is_c45 = !!(regnum & MII_ADDR_C45);
+       u16 reg = (u16)(regnum & 0xffff);
+       struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv;
+
+       dev_dbg(&bus->dev, "mdio read %s,base is %p\n",
+               bus->id, mdio_dev->vbase);
+       dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x!\n",
+               phy_id, is_c45, devad, reg);
+
+       /* Step 1: wait for ready */
+       ret = hns_mdio_wait_ready(bus);
+       if (ret) {
+               dev_err(&bus->dev, "MDIO bus is busy\n");
+               return ret;
+       }
+
+       if (!is_c45) {
+               hns_mdio_cmd_write(mdio_dev, is_c45,
+                                  MDIO_C22_READ, phy_id, reg);
+       } else {
+               MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M,
+                                  MDIO_ADDR_DATA_S, reg);
+
+               /* Step 2; config the cmd-reg to write addr*/
+               hns_mdio_cmd_write(mdio_dev, is_c45,
+                                  MDIO_C45_WRITE_ADDR, phy_id, devad);
+
+               /* Step 3: check for read or write opt is finished */
+               ret = hns_mdio_wait_ready(bus);
+               if (ret) {
+                       dev_err(&bus->dev, "MDIO bus is busy\n");
+                       return ret;
+               }
+
+               hns_mdio_cmd_write(mdio_dev, is_c45,
+                                  MDIO_C45_WRITE_ADDR, phy_id, devad);
+       }
+
+       /* Step 5: waitting for MDIO_COMMAND_REG 's mdio_start==0,*/
+       /* check for read or write opt is finished */
+       ret = hns_mdio_wait_ready(bus);
+       if (ret) {
+               dev_err(&bus->dev, "MDIO bus is busy\n");
+               return ret;
+       }
+
+       reg_val = MDIO_GET_REG_BIT(mdio_dev, MDIO_STA_REG, MDIO_STATE_STA_B);
+       if (reg_val) {
+               dev_err(&bus->dev, " ERROR! MDIO Read failed!\n");
+               return -EBUSY;
+       }
+
+       /* Step 6; get out data*/
+       reg_val = (u16)MDIO_GET_REG_FIELD(mdio_dev, MDIO_RDATA_REG,
+                                         MDIO_RDATA_DATA_M, MDIO_RDATA_DATA_S);
+
+       return reg_val;
+}
+
+/**
+ * hns_mdio_reset - reset mdio bus
+ * @bus: mdio bus
+ *
+ * Return 0 on success, negative on failure
+ */
+static int hns_mdio_reset(struct mii_bus *bus)
+{
+       struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv;
+       int ret;
+
+       if (!mdio_dev->subctrl_vbase) {
+               dev_err(&bus->dev, "mdio sys ctl reg has not maped\n");
+               return -ENODEV;
+       }
+
+       /*1. reset req, and read reset st check*/
+       ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_RESET_REQ, 0x1,
+                                   MDIO_SC_RESET_ST, 0x1,
+                                   MDIO_CHECK_SET_ST);
+       if (ret) {
+               dev_err(&bus->dev, "MDIO reset fail\n");
+               return ret;
+       }
+
+       /*2. dis clk, and read clk st check*/
+       ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_CLK_DIS,
+                                   0x1, MDIO_SC_CLK_ST, 0x1,
+                                   MDIO_CHECK_CLR_ST);
+       if (ret) {
+               dev_err(&bus->dev, "MDIO dis clk fail\n");
+               return ret;
+       }
+
+       /*3. reset dreq, and read reset st check*/
+       ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_RESET_DREQ, 0x1,
+                                   MDIO_SC_RESET_ST, 0x1,
+                                   MDIO_CHECK_CLR_ST);
+       if (ret) {
+               dev_err(&bus->dev, "MDIO dis clk fail\n");
+               return ret;
+       }
+
+       /*4. en clk, and read clk st check*/
+       ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_CLK_EN,
+                                   0x1, MDIO_SC_CLK_ST, 0x1,
+                                   MDIO_CHECK_SET_ST);
+       if (ret)
+               dev_err(&bus->dev, "MDIO en clk fail\n");
+
+       return ret;
+}
+
+/**
+ * hns_mdio_bus_name - get mdio bus name
+ * @name: mdio bus name
+ * @np: mdio device node pointer
+ */
+static void hns_mdio_bus_name(char *name, struct device_node *np)
+{
+       const u32 *addr;
+       u64 taddr = OF_BAD_ADDR;
+
+       addr = of_get_address(np, 0, NULL, NULL);
+       if (addr)
+               taddr = of_translate_address(np, addr);
+
+       snprintf(name, MII_BUS_ID_SIZE, "%s@%llx", np->name,
+                (unsigned long long)taddr);
+}
+
+/**
+ * hns_mdio_probe - probe mdio device
+ * @pdev: mdio platform device
+ *
+ * Return 0 on success, negative on failure
+ */
+static int hns_mdio_probe(struct platform_device *pdev)
+{
+       struct device_node *np;
+       struct hns_mdio_device *mdio_dev;
+       struct mii_bus *new_bus;
+       struct resource *res;
+       int ret;
+
+       if (!pdev) {
+               dev_err(NULL, "pdev is NULL!\r\n");
+               return -ENODEV;
+       }
+       np = pdev->dev.of_node;
+       mdio_dev = devm_kzalloc(&pdev->dev, sizeof(*mdio_dev), GFP_KERNEL);
+       if (!mdio_dev)
+               return -ENOMEM;
+
+       new_bus = devm_mdiobus_alloc(&pdev->dev);
+       if (!new_bus) {
+               dev_err(&pdev->dev, "mdiobus_alloc fail!\n");
+               return -ENOMEM;
+       }
+
+       new_bus->name = MDIO_BUS_NAME;
+       new_bus->read = hns_mdio_read;
+       new_bus->write = hns_mdio_write;
+       new_bus->reset = hns_mdio_reset;
+       new_bus->priv = mdio_dev;
+       hns_mdio_bus_name(new_bus->id, np);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       mdio_dev->vbase = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(mdio_dev->vbase)) {
+               ret = PTR_ERR(mdio_dev->vbase);
+               return ret;
+       }
+
+       mdio_dev->subctrl_vbase =
+               syscon_node_to_regmap(of_parse_phandle(np, "subctrl_vbase", 0));
+       if (IS_ERR(mdio_dev->subctrl_vbase)) {
+               dev_warn(&pdev->dev, "no syscon hisilicon,peri-c-subctrl\n");
+               mdio_dev->subctrl_vbase = NULL;
+       }
+       new_bus->irq = devm_kcalloc(&pdev->dev, PHY_MAX_ADDR,
+                                   sizeof(int), GFP_KERNEL);
+       if (!new_bus->irq)
+               return -ENOMEM;
+
+       new_bus->parent = &pdev->dev;
+       platform_set_drvdata(pdev, new_bus);
+
+       ret = of_mdiobus_register(new_bus, np);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot register as MDIO bus!\n");
+               platform_set_drvdata(pdev, NULL);
+               return ret;
+       }
+
+       return 0;
+}
+
+/**
+ * hns_mdio_remove - remove mdio device
+ * @pdev: mdio platform device
+ *
+ * Return 0 on success, negative on failure
+ */
+static int hns_mdio_remove(struct platform_device *pdev)
+{
+       struct mii_bus *bus;
+
+       bus = platform_get_drvdata(pdev);
+
+       mdiobus_unregister(bus);
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+static const struct of_device_id hns_mdio_match[] = {
+       {.compatible = "hisilicon,mdio"},
+       {.compatible = "hisilicon,hns-mdio"},
+       {}
+};
+
+static struct platform_driver hns_mdio_driver = {
+       .probe = hns_mdio_probe,
+       .remove = hns_mdio_remove,
+       .driver = {
+                  .name = MDIO_DRV_NAME,
+                  .of_match_table = hns_mdio_match,
+                  },
+};
+
+module_platform_driver(hns_mdio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
+MODULE_DESCRIPTION("Hisilicon HNS MDIO driver");
+MODULE_ALIAS("platform:" MDIO_DRV_NAME);
index b60a34d982a90f6eff42601d8ee0e29a7d356c5d..5d7db6c01c46c04adcf0b12b49e67610c0d67ca7 100644 (file)
@@ -2204,7 +2204,6 @@ static void emac_ethtool_get_drvinfo(struct net_device *ndev,
        strlcpy(info->version, DRV_VERSION, sizeof(info->version));
        snprintf(info->bus_info, sizeof(info->bus_info), "PPC 4xx EMAC-%d %s",
                 dev->cell_index, dev->ofdev->dev.of_node->full_name);
-       info->regdump_len = emac_ethtool_get_regs_len(ndev);
 }
 
 static const struct ethtool_ops emac_ethtool_ops = {
index ac02c675c59c4167b4870c892c6afcceb13aec4c..93ae11494810a2a3d9804309cf175a9e967ce27e 100644 (file)
@@ -181,7 +181,7 @@ struct emac_instance {
        struct mal_commac               commac;
 
        /* PHY infos */
-       u32                             phy_mode;
+       int                             phy_mode;
        u32                             phy_map;
        u32                             phy_address;
        u32                             phy_feat_exc;
index 4270ad2d4ddfa91f9e98a759959f67441b743a9f..83e557c7f2796874771a534a640670a5fd5b8f1a 100644 (file)
@@ -559,8 +559,6 @@ static void e1000_get_drvinfo(struct net_device *netdev,
 
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->regdump_len = e1000_get_regs_len(netdev);
-       drvinfo->eedump_len = e1000_get_eeprom_len(netdev);
 }
 
 static void e1000_get_ringparam(struct net_device *netdev,
index 45c8c864104ee7cb8f6a856ae322772da174c088..b1af0d613caabc55f7598b63201f59ca4c27c0e6 100644 (file)
@@ -3900,10 +3900,6 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
                return E1000_SUCCESS;
        }
 
-       /* If eeprom is not yet detected, do so now */
-       if (eeprom->word_size == 0)
-               e1000_init_eeprom_params(hw);
-
        /* A check for invalid values:  offset too large, too many words, and
         * not enough words.
         */
@@ -4074,10 +4070,6 @@ static s32 e1000_do_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
                return E1000_SUCCESS;
        }
 
-       /* If eeprom is not yet detected, do so now */
-       if (eeprom->word_size == 0)
-               e1000_init_eeprom_params(hw);
-
        /* A check for invalid values:  offset too large, too many words, and
         * not enough words.
         */
index 74dc150559711f2dd8f3b9b707288cb5bd77319a..fd7be860c20131e7db9a557dcdd40db348d9edf5 100644 (file)
@@ -3820,7 +3820,7 @@ static int e1000_clean(struct napi_struct *napi, int budget)
        if (work_done < budget) {
                if (likely(adapter->itr_setting & 3))
                        e1000_set_itr(adapter);
-               napi_complete(napi);
+               napi_complete_done(napi, work_done);
                if (!test_bit(__E1000_DOWN, &adapter->flags))
                        e1000_irq_enable(adapter);
        }
index ad6daa656d3e9f8b21a9bd3e43e40346711238b6..6cab1f30d41e93957551ae68b031679b458b9da7 100644 (file)
@@ -648,8 +648,6 @@ static void e1000_get_drvinfo(struct net_device *netdev,
 
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->regdump_len = e1000_get_regs_len(netdev);
-       drvinfo->eedump_len = e1000_get_eeprom_len(netdev);
 }
 
 static void e1000_get_ringparam(struct net_device *netdev,
index faf4b3f3d0b53ed5ecbf17c87507bba0e45ff1df..0a854a47d31a77ef0723231dd75e3aceddc5d6b0 100644 (file)
@@ -2693,7 +2693,7 @@ static int e1000e_poll(struct napi_struct *napi, int weight)
        if (work_done < weight) {
                if (adapter->itr_setting & 3)
                        e1000_set_itr(adapter);
-               napi_complete(napi);
+               napi_complete_done(napi, work_done);
                if (!test_bit(__E1000_DOWN, &adapter->state)) {
                        if (adapter->msix_entries)
                                ew32(IMS, adapter->rx_ring->ims_val);
@@ -6952,6 +6952,7 @@ static const struct net_device_ops e1000e_netdev_ops = {
 #endif
        .ndo_set_features = e1000_set_features,
        .ndo_fix_features = e1000_fix_features,
+       .ndo_features_check     = passthru_features_check,
 };
 
 /**
index c8c8c5baefda0552c011d8149812298585ae5709..14440200499b0300978caabc6d877a91f4ca91a9 100644 (file)
@@ -101,12 +101,19 @@ struct fm10k_tx_queue_stats {
        u64 csum_err;
        u64 tx_busy;
        u64 tx_done_old;
+       u64 csum_good;
 };
 
 struct fm10k_rx_queue_stats {
        u64 alloc_failed;
        u64 csum_err;
        u64 errors;
+       u64 csum_good;
+       u64 switch_errors;
+       u64 drops;
+       u64 pp_errors;
+       u64 link_errors;
+       u64 length_errors;
 };
 
 struct fm10k_ring {
@@ -251,6 +258,7 @@ struct fm10k_intfc {
 #define FM10K_FLAG_RSS_FIELD_IPV6_UDP          (u32)(1 << 2)
 #define FM10K_FLAG_RX_TS_ENABLED               (u32)(1 << 3)
 #define FM10K_FLAG_SWPRI_CONFIG                        (u32)(1 << 4)
+#define FM10K_FLAG_DEBUG_STATS                 (u32)(1 << 5)
        int xcast_mode;
 
        /* Tx fast path data */
@@ -277,6 +285,17 @@ struct fm10k_intfc {
        u64 rx_drops_nic;
        u64 rx_overrun_pf;
        u64 rx_overrun_vf;
+
+       /* Debug Statistics */
+       u64 hw_sm_mbx_full;
+       u64 hw_csum_tx_good;
+       u64 hw_csum_rx_good;
+       u64 rx_switch_errors;
+       u64 rx_drops;
+       u64 rx_pp_errors;
+       u64 rx_link_errors;
+       u64 rx_length_errors;
+
        u32 tx_timeout_count;
 
        /* RX */
index f45b4d71adb85f4e3daf92cf83a42832a47d5723..5304bc1fbecd4e218497ea7b7680b7b81d964411 100644 (file)
@@ -37,7 +37,8 @@ static void *fm10k_dbg_desc_seq_start(struct seq_file *s, loff_t *pos)
 }
 
 static void *fm10k_dbg_desc_seq_next(struct seq_file *s,
-                                    void __always_unused *v, loff_t *pos)
+                                    void __always_unused *v,
+                                    loff_t *pos)
 {
        struct fm10k_ring *ring = s->private;
 
@@ -45,7 +46,7 @@ static void *fm10k_dbg_desc_seq_next(struct seq_file *s,
 }
 
 static void fm10k_dbg_desc_seq_stop(struct seq_file __always_unused *s,
-                                   __always_unused void *v)
+                                   void __always_unused *v)
 {
        /* Do nothing. */
 }
@@ -175,7 +176,7 @@ void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector)
                return;
 
        /* Generate a folder for each q_vector */
-       sprintf(name, "q_vector.%03d", q_vector->v_idx);
+       snprintf(name, sizeof(name), "q_vector.%03d", q_vector->v_idx);
 
        q_vector->dbg_q_vector = debugfs_create_dir(name, interface->dbg_intfc);
        if (!q_vector->dbg_q_vector)
@@ -185,7 +186,7 @@ void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector)
        for (i = 0; i < q_vector->tx.count; i++) {
                struct fm10k_ring *ring = &q_vector->tx.ring[i];
 
-               sprintf(name, "tx_ring.%03d", ring->queue_index);
+               snprintf(name, sizeof(name), "tx_ring.%03d", ring->queue_index);
 
                debugfs_create_file(name, 0600,
                                    q_vector->dbg_q_vector, ring,
@@ -196,7 +197,7 @@ void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector)
        for (i = 0; i < q_vector->rx.count; i++) {
                struct fm10k_ring *ring = &q_vector->rx.ring[i];
 
-               sprintf(name, "rx_ring.%03d", ring->queue_index);
+               snprintf(name, sizeof(name), "rx_ring.%03d", ring->queue_index);
 
                debugfs_create_file(name, 0600,
                                    q_vector->dbg_q_vector, ring,
index c6dc9683429e4aa4810722b7e861d4ed0cb4ad83..2ce0eba5e04034a4cfa6dc3c7ca8dbbc3282ec49 100644 (file)
@@ -76,19 +76,22 @@ static const struct fm10k_stats fm10k_gstrings_global_stats[] = {
        FM10K_STAT("mac_rules_used", hw.swapi.mac.used),
        FM10K_STAT("mac_rules_avail", hw.swapi.mac.avail),
 
-       FM10K_STAT("mbx_tx_busy", hw.mbx.tx_busy),
-       FM10K_STAT("mbx_tx_oversized", hw.mbx.tx_dropped),
-       FM10K_STAT("mbx_tx_messages", hw.mbx.tx_messages),
-       FM10K_STAT("mbx_tx_dwords", hw.mbx.tx_dwords),
-       FM10K_STAT("mbx_rx_messages", hw.mbx.rx_messages),
-       FM10K_STAT("mbx_rx_dwords", hw.mbx.rx_dwords),
-       FM10K_STAT("mbx_rx_parse_err", hw.mbx.rx_parse_err),
-
        FM10K_STAT("tx_hang_count", tx_timeout_count),
 
        FM10K_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
 };
 
+static const struct fm10k_stats fm10k_gstrings_debug_stats[] = {
+       FM10K_STAT("hw_sm_mbx_full", hw_sm_mbx_full),
+       FM10K_STAT("hw_csum_tx_good", hw_csum_tx_good),
+       FM10K_STAT("hw_csum_rx_good", hw_csum_rx_good),
+       FM10K_STAT("rx_switch_errors", rx_switch_errors),
+       FM10K_STAT("rx_drops", rx_drops),
+       FM10K_STAT("rx_pp_errors", rx_pp_errors),
+       FM10K_STAT("rx_link_errors", rx_link_errors),
+       FM10K_STAT("rx_length_errors", rx_length_errors),
+};
+
 static const struct fm10k_stats fm10k_gstrings_pf_stats[] = {
        FM10K_STAT("timeout", stats.timeout.count),
        FM10K_STAT("ur", stats.ur.count),
@@ -100,14 +103,33 @@ static const struct fm10k_stats fm10k_gstrings_pf_stats[] = {
        FM10K_STAT("nodesc_drop", stats.nodesc_drop.count),
 };
 
+#define FM10K_MBX_STAT(_name, _stat) { \
+       .stat_string = _name, \
+       .sizeof_stat = FIELD_SIZEOF(struct fm10k_mbx_info, _stat), \
+       .stat_offset = offsetof(struct fm10k_mbx_info, _stat) \
+}
+
+static const struct fm10k_stats fm10k_gstrings_mbx_stats[] = {
+       FM10K_MBX_STAT("mbx_tx_busy", tx_busy),
+       FM10K_MBX_STAT("mbx_tx_oversized", tx_dropped),
+       FM10K_MBX_STAT("mbx_tx_messages", tx_messages),
+       FM10K_MBX_STAT("mbx_tx_dwords", tx_dwords),
+       FM10K_MBX_STAT("mbx_rx_messages", rx_messages),
+       FM10K_MBX_STAT("mbx_rx_dwords", rx_dwords),
+       FM10K_MBX_STAT("mbx_rx_parse_err", rx_parse_err),
+};
+
 #define FM10K_GLOBAL_STATS_LEN ARRAY_SIZE(fm10k_gstrings_global_stats)
+#define FM10K_DEBUG_STATS_LEN ARRAY_SIZE(fm10k_gstrings_debug_stats)
 #define FM10K_PF_STATS_LEN ARRAY_SIZE(fm10k_gstrings_pf_stats)
+#define FM10K_MBX_STATS_LEN ARRAY_SIZE(fm10k_gstrings_mbx_stats)
 
 #define FM10K_QUEUE_STATS_LEN(_n) \
        ( (_n) * 2 * (sizeof(struct fm10k_queue_stats) / sizeof(u64)))
 
 #define FM10K_STATIC_STATS_LEN (FM10K_GLOBAL_STATS_LEN + \
-                               FM10K_NETDEV_STATS_LEN)
+                               FM10K_NETDEV_STATS_LEN + \
+                               FM10K_MBX_STATS_LEN)
 
 static const char fm10k_gstrings_test[][ETH_GSTRING_LEN] = {
        "Mailbox test (on/offline)"
@@ -120,47 +142,97 @@ enum fm10k_self_test_types {
        FM10K_TEST_MAX = FM10K_TEST_LEN
 };
 
-static void fm10k_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+enum {
+       FM10K_PRV_FLAG_DEBUG_STATS,
+       FM10K_PRV_FLAG_LEN,
+};
+
+static const char fm10k_prv_flags[FM10K_PRV_FLAG_LEN][ETH_GSTRING_LEN] = {
+       "debug-statistics",
+};
+
+static void fm10k_get_stat_strings(struct net_device *dev, u8 *data)
 {
        struct fm10k_intfc *interface = netdev_priv(dev);
+       struct fm10k_iov_data *iov_data = interface->iov_data;
        char *p = (char *)data;
        unsigned int i;
+       unsigned int j;
 
-       switch (stringset) {
-       case ETH_SS_TEST:
-               memcpy(data, *fm10k_gstrings_test,
-                      FM10K_TEST_LEN * ETH_GSTRING_LEN);
-               break;
-       case ETH_SS_STATS:
-               for (i = 0; i < FM10K_NETDEV_STATS_LEN; i++) {
-                       memcpy(p, fm10k_gstrings_net_stats[i].stat_string,
+       for (i = 0; i < FM10K_NETDEV_STATS_LEN; i++) {
+               memcpy(p, fm10k_gstrings_net_stats[i].stat_string,
+                      ETH_GSTRING_LEN);
+               p += ETH_GSTRING_LEN;
+       }
+
+       for (i = 0; i < FM10K_GLOBAL_STATS_LEN; i++) {
+               memcpy(p, fm10k_gstrings_global_stats[i].stat_string,
+                      ETH_GSTRING_LEN);
+               p += ETH_GSTRING_LEN;
+       }
+
+       if (interface->flags & FM10K_FLAG_DEBUG_STATS) {
+               for (i = 0; i < FM10K_DEBUG_STATS_LEN; i++) {
+                       memcpy(p, fm10k_gstrings_debug_stats[i].stat_string,
                               ETH_GSTRING_LEN);
                        p += ETH_GSTRING_LEN;
                }
-               for (i = 0; i < FM10K_GLOBAL_STATS_LEN; i++) {
-                       memcpy(p, fm10k_gstrings_global_stats[i].stat_string,
+       }
+
+       for (i = 0; i < FM10K_MBX_STATS_LEN; i++) {
+               memcpy(p, fm10k_gstrings_mbx_stats[i].stat_string,
+                      ETH_GSTRING_LEN);
+               p += ETH_GSTRING_LEN;
+       }
+
+       if (interface->hw.mac.type != fm10k_mac_vf) {
+               for (i = 0; i < FM10K_PF_STATS_LEN; i++) {
+                       memcpy(p, fm10k_gstrings_pf_stats[i].stat_string,
                               ETH_GSTRING_LEN);
                        p += ETH_GSTRING_LEN;
                }
+       }
 
-               if (interface->hw.mac.type != fm10k_mac_vf) {
-                       for (i = 0; i < FM10K_PF_STATS_LEN; i++) {
-                               memcpy(p, fm10k_gstrings_pf_stats[i].stat_string,
-                                      ETH_GSTRING_LEN);
+       if ((interface->flags & FM10K_FLAG_DEBUG_STATS) && iov_data) {
+               for (i = 0; i < iov_data->num_vfs; i++) {
+                       for (j = 0; j < FM10K_MBX_STATS_LEN; j++) {
+                               snprintf(p,
+                                        ETH_GSTRING_LEN,
+                                        "vf_%u_%s", i,
+                                        fm10k_gstrings_mbx_stats[j].stat_string);
                                p += ETH_GSTRING_LEN;
                        }
                }
+       }
 
-               for (i = 0; i < interface->hw.mac.max_queues; i++) {
-                       sprintf(p, "tx_queue_%u_packets", i);
-                       p += ETH_GSTRING_LEN;
-                       sprintf(p, "tx_queue_%u_bytes", i);
-                       p += ETH_GSTRING_LEN;
-                       sprintf(p, "rx_queue_%u_packets", i);
-                       p += ETH_GSTRING_LEN;
-                       sprintf(p, "rx_queue_%u_bytes", i);
-                       p += ETH_GSTRING_LEN;
-               }
+       for (i = 0; i < interface->hw.mac.max_queues; i++) {
+               snprintf(p, ETH_GSTRING_LEN, "tx_queue_%u_packets", i);
+               p += ETH_GSTRING_LEN;
+               snprintf(p, ETH_GSTRING_LEN, "tx_queue_%u_bytes", i);
+               p += ETH_GSTRING_LEN;
+               snprintf(p, ETH_GSTRING_LEN, "rx_queue_%u_packets", i);
+               p += ETH_GSTRING_LEN;
+               snprintf(p, ETH_GSTRING_LEN, "rx_queue_%u_bytes", i);
+               p += ETH_GSTRING_LEN;
+       }
+}
+
+static void fm10k_get_strings(struct net_device *dev,
+                             u32 stringset, u8 *data)
+{
+       char *p = (char *)data;
+
+       switch (stringset) {
+       case ETH_SS_TEST:
+               memcpy(data, *fm10k_gstrings_test,
+                      FM10K_TEST_LEN * ETH_GSTRING_LEN);
+               break;
+       case ETH_SS_STATS:
+               fm10k_get_stat_strings(dev, data);
+               break;
+       case ETH_SS_PRIV_FLAGS:
+               memcpy(p, fm10k_prv_flags,
+                      FM10K_PRV_FLAG_LEN * ETH_GSTRING_LEN);
                break;
        }
 }
@@ -168,6 +240,7 @@ static void fm10k_get_strings(struct net_device *dev, u32 stringset, u8 *data)
 static int fm10k_get_sset_count(struct net_device *dev, int sset)
 {
        struct fm10k_intfc *interface = netdev_priv(dev);
+       struct fm10k_iov_data *iov_data = interface->iov_data;
        struct fm10k_hw *hw = &interface->hw;
        int stats_len = FM10K_STATIC_STATS_LEN;
 
@@ -180,7 +253,16 @@ static int fm10k_get_sset_count(struct net_device *dev, int sset)
                if (hw->mac.type != fm10k_mac_vf)
                        stats_len += FM10K_PF_STATS_LEN;
 
+               if (interface->flags & FM10K_FLAG_DEBUG_STATS) {
+                       stats_len += FM10K_DEBUG_STATS_LEN;
+
+                       if (iov_data)
+                               stats_len += FM10K_MBX_STATS_LEN * iov_data->num_vfs;
+               }
+
                return stats_len;
+       case ETH_SS_PRIV_FLAGS:
+               return FM10K_PRV_FLAG_LEN;
        default:
                return -EOPNOTSUPP;
        }
@@ -192,6 +274,7 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev,
 {
        const int stat_count = sizeof(struct fm10k_queue_stats) / sizeof(u64);
        struct fm10k_intfc *interface = netdev_priv(netdev);
+       struct fm10k_iov_data *iov_data = interface->iov_data;
        struct net_device_stats *net_stats = &netdev->stats;
        char *p;
        int i, j;
@@ -211,13 +294,47 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev,
                        sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
        }
 
-       if (interface->hw.mac.type != fm10k_mac_vf)
+       if (interface->flags & FM10K_FLAG_DEBUG_STATS) {
+               for (i = 0; i < FM10K_DEBUG_STATS_LEN; i++) {
+                       p = (char *)interface + fm10k_gstrings_debug_stats[i].stat_offset;
+                       *(data++) = (fm10k_gstrings_debug_stats[i].sizeof_stat ==
+                                    sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+               }
+       }
+
+       for (i = 0; i < FM10K_MBX_STATS_LEN; i++) {
+               p = (char *)&interface->hw.mbx + fm10k_gstrings_mbx_stats[i].stat_offset;
+               *(data++) = (fm10k_gstrings_mbx_stats[i].sizeof_stat ==
+                       sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+       }
+
+       if (interface->hw.mac.type != fm10k_mac_vf) {
                for (i = 0; i < FM10K_PF_STATS_LEN; i++) {
                        p = (char *)interface +
                            fm10k_gstrings_pf_stats[i].stat_offset;
                        *(data++) = (fm10k_gstrings_pf_stats[i].sizeof_stat ==
                                     sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
                }
+       }
+
+       if ((interface->flags & FM10K_FLAG_DEBUG_STATS) && iov_data) {
+               for (i = 0; i < iov_data->num_vfs; i++) {
+                       struct fm10k_vf_info *vf_info;
+                       vf_info = &iov_data->vf_info[i];
+
+                       /* skip stats if we don't have a vf info */
+                       if (!vf_info) {
+                               data += FM10K_MBX_STATS_LEN;
+                               continue;
+                       }
+
+                       for (j = 0; j < FM10K_MBX_STATS_LEN; j++) {
+                               p = (char *)&vf_info->mbx + fm10k_gstrings_mbx_stats[j].stat_offset;
+                               *(data++) = (fm10k_gstrings_mbx_stats[j].sizeof_stat ==
+                                            sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+                       }
+               }
+       }
 
        for (i = 0; i < interface->hw.mac.max_queues; i++) {
                struct fm10k_ring *ring;
@@ -398,10 +515,6 @@ static void fm10k_get_drvinfo(struct net_device *dev,
                sizeof(info->version) - 1);
        strncpy(info->bus_info, pci_name(interface->pdev),
                sizeof(info->bus_info) - 1);
-
-       info->n_stats = fm10k_get_sset_count(dev, ETH_SS_STATS);
-
-       info->regdump_len = fm10k_get_regs_len(dev);
 }
 
 static void fm10k_get_pauseparam(struct net_device *dev,
@@ -881,6 +994,33 @@ static void fm10k_self_test(struct net_device *dev,
                eth_test->flags |= ETH_TEST_FL_FAILED;
 }
 
+static u32 fm10k_get_priv_flags(struct net_device *netdev)
+{
+       struct fm10k_intfc *interface = netdev_priv(netdev);
+       u32 priv_flags = 0;
+
+       if (interface->flags & FM10K_FLAG_DEBUG_STATS)
+               priv_flags |= 1 << FM10K_PRV_FLAG_DEBUG_STATS;
+
+       return priv_flags;
+}
+
+static int fm10k_set_priv_flags(struct net_device *netdev, u32 priv_flags)
+{
+       struct fm10k_intfc *interface = netdev_priv(netdev);
+
+       if (priv_flags >= (1 << FM10K_PRV_FLAG_LEN))
+               return -EINVAL;
+
+       if (priv_flags & (1 << FM10K_PRV_FLAG_DEBUG_STATS))
+               interface->flags |= FM10K_FLAG_DEBUG_STATS;
+       else
+               interface->flags &= ~FM10K_FLAG_DEBUG_STATS;
+
+       return 0;
+}
+
+
 static u32 fm10k_get_reta_size(struct net_device __always_unused *netdev)
 {
        return FM10K_RETA_SIZE * FM10K_RETA_ENTRIES_PER_REG;
@@ -1094,6 +1234,8 @@ static const struct ethtool_ops fm10k_ethtool_ops = {
        .get_regs               = fm10k_get_regs,
        .get_regs_len           = fm10k_get_regs_len,
        .self_test              = fm10k_self_test,
+       .get_priv_flags         = fm10k_get_priv_flags,
+       .set_priv_flags         = fm10k_set_priv_flags,
        .get_rxfh_indir_size    = fm10k_get_reta_size,
        .get_rxfh_key_size      = fm10k_get_rssrk_size,
        .get_rxfh               = fm10k_get_rssh,
index 94571e6e790c55f9d204535fd4cc364857551e75..acfb8b1f88a70b3b725a6d81c8a8a8cb8b19577f 100644 (file)
@@ -137,8 +137,11 @@ process_mbx:
                }
 
                /* guarantee we have free space in the SM mailbox */
-               if (!hw->mbx.ops.tx_ready(&hw->mbx, FM10K_VFMBX_MSG_MTU))
+               if (!hw->mbx.ops.tx_ready(&hw->mbx, FM10K_VFMBX_MSG_MTU)) {
+                       /* keep track of how many times this occurs */
+                       interface->hw_sm_mbx_full++;
                        break;
+               }
 
                /* cleanup mailbox and process received messages */
                mbx->ops.process(hw, mbx);
@@ -228,9 +231,6 @@ int fm10k_iov_resume(struct pci_dev *pdev)
                hw->iov.ops.set_lport(hw, vf_info, i,
                                      FM10K_VF_FLAG_MULTI_CAPABLE);
 
-               /* assign our default vid to the VF following reset */
-               vf_info->sw_vid = hw->mac.default_vid;
-
                /* mailbox is disconnected so we don't send a message */
                hw->iov.ops.assign_default_mac_vlan(hw, vf_info);
 
index b5b2925103ec10c1e835429c2ab7d2efee093838..e76a44cf330cd47d57084a05fd69611985a79e15 100644 (file)
@@ -398,6 +398,8 @@ static inline void fm10k_rx_checksum(struct fm10k_ring *ring,
                return;
 
        skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       ring->rx_stats.csum_good++;
 }
 
 #define FM10K_RSS_L4_TYPES_MASK \
@@ -497,8 +499,11 @@ static unsigned int fm10k_process_skb_fields(struct fm10k_ring *rx_ring,
        if (rx_desc->w.vlan) {
                u16 vid = le16_to_cpu(rx_desc->w.vlan);
 
-               if (vid != rx_ring->vid)
+               if ((vid & VLAN_VID_MASK) != rx_ring->vid)
                        __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
+               else if (vid & VLAN_PRIO_MASK)
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+                                              vid & VLAN_PRIO_MASK);
        }
 
        fm10k_type_trans(rx_ring, rx_desc, skb);
@@ -553,6 +558,18 @@ static bool fm10k_cleanup_headers(struct fm10k_ring *rx_ring,
 {
        if (unlikely((fm10k_test_staterr(rx_desc,
                                         FM10K_RXD_STATUS_RXE)))) {
+#define FM10K_TEST_RXD_BIT(rxd, bit) \
+       ((rxd)->w.csum_err & cpu_to_le16(bit))
+               if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_SWITCH_ERROR))
+                       rx_ring->rx_stats.switch_errors++;
+               if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_NO_DESCRIPTOR))
+                       rx_ring->rx_stats.drops++;
+               if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_PP_ERROR))
+                       rx_ring->rx_stats.pp_errors++;
+               if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_SWITCH_READY))
+                       rx_ring->rx_stats.link_errors++;
+               if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_TOO_BIG))
+                       rx_ring->rx_stats.length_errors++;
                dev_kfree_skb_any(skb);
                rx_ring->rx_stats.errors++;
                return true;
@@ -576,9 +593,9 @@ static void fm10k_receive_skb(struct fm10k_q_vector *q_vector,
        napi_gro_receive(&q_vector->napi, skb);
 }
 
-static bool fm10k_clean_rx_irq(struct fm10k_q_vector *q_vector,
-                              struct fm10k_ring *rx_ring,
-                              int budget)
+static int fm10k_clean_rx_irq(struct fm10k_q_vector *q_vector,
+                             struct fm10k_ring *rx_ring,
+                             int budget)
 {
        struct sk_buff *skb = rx_ring->skb;
        unsigned int total_bytes = 0, total_packets = 0;
@@ -645,7 +662,7 @@ static bool fm10k_clean_rx_irq(struct fm10k_q_vector *q_vector,
        q_vector->rx.total_packets += total_packets;
        q_vector->rx.total_bytes += total_bytes;
 
-       return total_packets < budget;
+       return total_packets;
 }
 
 #define VXLAN_HLEN (sizeof(struct udphdr) + 8)
@@ -878,6 +895,7 @@ static void fm10k_tx_csum(struct fm10k_ring *tx_ring,
 
        /* update TX checksum flag */
        first->tx_flags |= FM10K_TX_FLAGS_CSUM;
+       tx_ring->tx_stats.csum_good++;
 
 no_csum:
        /* populate Tx descriptor header size and mss */
@@ -1079,9 +1097,7 @@ netdev_tx_t fm10k_xmit_frame_ring(struct sk_buff *skb,
        struct fm10k_tx_buffer *first;
        int tso;
        u32 tx_flags = 0;
-#if PAGE_SIZE > FM10K_MAX_DATA_PER_TXD
        unsigned short f;
-#endif
        u16 count = TXD_USE_COUNT(skb_headlen(skb));
 
        /* need: 1 descriptor per page * PAGE_SIZE/FM10K_MAX_DATA_PER_TXD,
@@ -1089,12 +1105,9 @@ netdev_tx_t fm10k_xmit_frame_ring(struct sk_buff *skb,
         *       + 2 desc gap to keep tail from touching head
         * otherwise try next time
         */
-#if PAGE_SIZE > FM10K_MAX_DATA_PER_TXD
        for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
                count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
-#else
-       count += skb_shinfo(skb)->nr_frags;
-#endif
+
        if (fm10k_maybe_stop_tx(tx_ring, count + 3)) {
                tx_ring->tx_stats.tx_busy++;
                return NETDEV_TX_BUSY;
@@ -1409,7 +1422,7 @@ static int fm10k_poll(struct napi_struct *napi, int budget)
        struct fm10k_q_vector *q_vector =
                               container_of(napi, struct fm10k_q_vector, napi);
        struct fm10k_ring *ring;
-       int per_ring_budget;
+       int per_ring_budget, work_done = 0;
        bool clean_complete = true;
 
        fm10k_for_each_ring(ring, q_vector->tx)
@@ -1423,16 +1436,19 @@ static int fm10k_poll(struct napi_struct *napi, int budget)
        else
                per_ring_budget = budget;
 
-       fm10k_for_each_ring(ring, q_vector->rx)
-               clean_complete &= fm10k_clean_rx_irq(q_vector, ring,
-                                                    per_ring_budget);
+       fm10k_for_each_ring(ring, q_vector->rx) {
+               int work = fm10k_clean_rx_irq(q_vector, ring, per_ring_budget);
+
+               work_done += work;
+               clean_complete &= !!(work < per_ring_budget);
+       }
 
        /* If all work not completed, return budget and keep polling */
        if (!clean_complete)
                return budget;
 
        /* all work done, exit the polling mode */
-       napi_complete(napi);
+       napi_complete_done(napi, work_done);
 
        /* re-enable the q_vector */
        fm10k_qv_enable(q_vector);
@@ -1892,7 +1908,7 @@ static void fm10k_init_reta(struct fm10k_intfc *interface)
        u32 reta, base;
 
        /* If the netdev is initialized we have to maintain table if possible */
-       if (interface->netdev->reg_state) {
+       if (interface->netdev->reg_state != NETREG_UNINITIALIZED) {
                for (i = FM10K_RETA_SIZE; i--;) {
                        reta = interface->reta[i];
                        if ((((reta << 24) >> 24) < rss_i) &&
index 1a4b52637de9123d900f7362b54b985e46e345de..af09a1b272e68112c052df952c35ad6e8123dd6b 100644 (file)
@@ -129,8 +129,8 @@ static u16 fm10k_fifo_head_drop(struct fm10k_mbx_fifo *fifo)
  *  fm10k_fifo_drop_all - Drop all messages in FIFO
  *  @fifo: pointer to FIFO
  *
- *  This function resets the head pointer to drop all messages in the FIFO,
- *  and ensure the FIFO is empty.
+ *  This function resets the head pointer to drop all messages in the FIFO and
+ *  ensure the FIFO is empty.
  **/
 static void fm10k_fifo_drop_all(struct fm10k_mbx_fifo *fifo)
 {
@@ -898,6 +898,27 @@ static void fm10k_mbx_create_disconnect_hdr(struct fm10k_mbx_info *mbx)
        mbx->mbx_hdr = hdr | FM10K_MSG_HDR_FIELD_SET(crc, CRC);
 }
 
+/**
+ *  fm10k_mbx_create_fake_disconnect_hdr - Generate a false disconnect mailbox header
+ *  @mbx: pointer to mailbox
+ *
+ *  This function creates a fake disconnect header for loading into remote
+ *  mailbox header. The primary purpose is to prevent errors on immediate
+ *  start up after mbx->connect.
+ **/
+static void fm10k_mbx_create_fake_disconnect_hdr(struct fm10k_mbx_info *mbx)
+{
+       u32 hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_DISCONNECT, TYPE) |
+                 FM10K_MSG_HDR_FIELD_SET(mbx->head, TAIL) |
+                 FM10K_MSG_HDR_FIELD_SET(mbx->tail, HEAD);
+       u16 crc = fm10k_crc_16b(&hdr, mbx->local, 1);
+
+       mbx->mbx_lock |= FM10K_MBX_ACK;
+
+       /* load header to memory to be written */
+       mbx->mbx_hdr = hdr | FM10K_MSG_HDR_FIELD_SET(crc, CRC);
+}
+
 /**
  *  fm10k_mbx_create_error_msg - Generate a error message
  *  @mbx: pointer to mailbox
@@ -1046,9 +1067,26 @@ static s32 fm10k_mbx_create_reply(struct fm10k_hw *hw,
  **/
 static void fm10k_mbx_reset_work(struct fm10k_mbx_info *mbx)
 {
+       u16 len, head, ack;
+
        /* reset our outgoing max size back to Rx limits */
        mbx->max_size = mbx->rx.size - 1;
 
+       /* update mbx->pulled to account for tail_len and ack */
+       head = FM10K_MSG_HDR_FIELD_GET(mbx->mbx_hdr, HEAD);
+       ack = fm10k_mbx_index_len(mbx, head, mbx->tail);
+       mbx->pulled += mbx->tail_len - ack;
+
+       /* now drop any messages which have started or finished transmitting */
+       while (fm10k_fifo_head_len(&mbx->tx) && mbx->pulled) {
+               len = fm10k_fifo_head_drop(&mbx->tx);
+               mbx->tx_dropped++;
+               if (mbx->pulled >= len)
+                       mbx->pulled -= len;
+               else
+                       mbx->pulled = 0;
+       }
+
        /* just do a quick resysnc to start of message */
        mbx->pushed = 0;
        mbx->pulled = 0;
@@ -1418,8 +1456,10 @@ static s32 fm10k_mbx_connect(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx)
        /* Place mbx in ready to connect state */
        mbx->state = FM10K_STATE_CONNECT;
 
+       fm10k_mbx_reset_work(mbx);
+
        /* initialize header of remote mailbox */
-       fm10k_mbx_create_disconnect_hdr(mbx);
+       fm10k_mbx_create_fake_disconnect_hdr(mbx);
        fm10k_write_reg(hw, mbx->mbmem_reg ^ mbx->mbmem_len, mbx->mbx_hdr);
 
        /* enable interrupt and notify other party of new message */
@@ -1725,7 +1765,7 @@ static void fm10k_sm_mbx_disconnect(struct fm10k_hw *hw,
        mbx->state = FM10K_STATE_CLOSED;
        mbx->remote = 0;
        fm10k_mbx_reset_work(mbx);
-       fm10k_mbx_update_max_size(mbx, 0);
+       fm10k_fifo_drop_all(&mbx->tx);
 
        fm10k_write_reg(hw, mbx->mbmem_reg, 0);
 }
index 99228bf46c1202170ef8eb934624e15d99a257d9..639263d5e833a04d19967f6df3cd20d8ce574a3b 100644 (file)
@@ -758,6 +758,7 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set)
        struct fm10k_intfc *interface = netdev_priv(netdev);
        struct fm10k_hw *hw = &interface->hw;
        s32 err;
+       int i;
 
        /* updates do not apply to VLAN 0 */
        if (!vid)
@@ -775,8 +776,25 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set)
        if (!set)
                clear_bit(vid, interface->active_vlans);
 
-       /* if default VLAN is already present do nothing */
-       if (vid == hw->mac.default_vid)
+       /* disable the default VID on ring if we have an active VLAN */
+       for (i = 0; i < interface->num_rx_queues; i++) {
+               struct fm10k_ring *rx_ring = interface->rx_ring[i];
+               u16 rx_vid = rx_ring->vid & (VLAN_N_VID - 1);
+
+               if (test_bit(rx_vid, interface->active_vlans))
+                       rx_ring->vid |= FM10K_VLAN_CLEAR;
+               else
+                       rx_ring->vid &= ~FM10K_VLAN_CLEAR;
+       }
+
+       /* Do not remove default VID related entries from VLAN and MAC tables */
+       if (!set && vid == hw->mac.default_vid)
+               return 0;
+
+       /* Do not throw an error if the interface is down. We will sync once
+        * we come up
+        */
+       if (test_bit(__FM10K_DOWN, &interface->state))
                return 0;
 
        fm10k_mbx_lock(interface);
@@ -996,21 +1014,6 @@ void fm10k_restore_rx_state(struct fm10k_intfc *interface)
        int xcast_mode;
        u16 vid, glort;
 
-       /* restore our address if perm_addr is set */
-       if (hw->mac.type == fm10k_mac_vf) {
-               if (is_valid_ether_addr(hw->mac.perm_addr)) {
-                       ether_addr_copy(hw->mac.addr, hw->mac.perm_addr);
-                       ether_addr_copy(netdev->perm_addr, hw->mac.perm_addr);
-                       ether_addr_copy(netdev->dev_addr, hw->mac.perm_addr);
-                       netdev->addr_assign_type &= ~NET_ADDR_RANDOM;
-               }
-
-               if (hw->mac.vlan_override)
-                       netdev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
-               else
-                       netdev->features |= NETIF_F_HW_VLAN_CTAG_RX;
-       }
-
        /* record glort for this interface */
        glort = interface->glort;
 
@@ -1045,7 +1048,7 @@ void fm10k_restore_rx_state(struct fm10k_intfc *interface)
                                           vid, true, 0);
        }
 
-       /* update xcast mode before syncronizing addresses */
+       /* update xcast mode before synchronizing addresses */
        hw->mac.ops.update_xcast_mode(hw, glort, xcast_mode);
 
        /* synchronize all of the addresses */
index ce53ff25f88d2ad7ebe03c475ffcbeb082f980fc..74be792f3f1b7e0d31e729980b27c6602aeac263 100644 (file)
@@ -170,6 +170,21 @@ static void fm10k_reinit(struct fm10k_intfc *interface)
        /* reassociate interrupts */
        fm10k_mbx_request_irq(interface);
 
+       /* update hardware address for VFs if perm_addr has changed */
+       if (hw->mac.type == fm10k_mac_vf) {
+               if (is_valid_ether_addr(hw->mac.perm_addr)) {
+                       ether_addr_copy(hw->mac.addr, hw->mac.perm_addr);
+                       ether_addr_copy(netdev->perm_addr, hw->mac.perm_addr);
+                       ether_addr_copy(netdev->dev_addr, hw->mac.perm_addr);
+                       netdev->addr_assign_type &= ~NET_ADDR_RANDOM;
+               }
+
+               if (hw->mac.vlan_override)
+                       netdev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
+               else
+                       netdev->features |= NETIF_F_HW_VLAN_CTAG_RX;
+       }
+
        /* reset clock */
        fm10k_ts_reset(interface);
 
@@ -259,8 +274,6 @@ static void fm10k_watchdog_update_host_state(struct fm10k_intfc *interface)
  * @interface: board private structure
  *
  * This function will process both the upstream and downstream mailboxes.
- * It is necessary for us to hold the rtnl_lock while doing this as the
- * mailbox accesses are protected by this lock.
  **/
 static void fm10k_mbx_subtask(struct fm10k_intfc *interface)
 {
@@ -315,6 +328,9 @@ void fm10k_update_stats(struct fm10k_intfc *interface)
 {
        struct net_device_stats *net_stats = &interface->netdev->stats;
        struct fm10k_hw *hw = &interface->hw;
+       u64 hw_csum_tx_good = 0, hw_csum_rx_good = 0, rx_length_errors = 0;
+       u64 rx_switch_errors = 0, rx_drops = 0, rx_pp_errors = 0;
+       u64 rx_link_errors = 0;
        u64 rx_errors = 0, rx_csum_errors = 0, tx_csum_errors = 0;
        u64 restart_queue = 0, tx_busy = 0, alloc_failed = 0;
        u64 rx_bytes_nic = 0, rx_pkts_nic = 0, rx_drops_nic = 0;
@@ -334,6 +350,7 @@ void fm10k_update_stats(struct fm10k_intfc *interface)
                tx_csum_errors += tx_ring->tx_stats.csum_err;
                bytes += tx_ring->stats.bytes;
                pkts += tx_ring->stats.packets;
+               hw_csum_tx_good += tx_ring->tx_stats.csum_good;
        }
 
        interface->restart_queue = restart_queue;
@@ -341,6 +358,8 @@ void fm10k_update_stats(struct fm10k_intfc *interface)
        net_stats->tx_bytes = bytes;
        net_stats->tx_packets = pkts;
        interface->tx_csum_errors = tx_csum_errors;
+       interface->hw_csum_tx_good = hw_csum_tx_good;
+
        /* gather some stats to the interface struct that are per queue */
        for (bytes = 0, pkts = 0, i = 0; i < interface->num_rx_queues; i++) {
                struct fm10k_ring *rx_ring = interface->rx_ring[i];
@@ -350,12 +369,24 @@ void fm10k_update_stats(struct fm10k_intfc *interface)
                alloc_failed += rx_ring->rx_stats.alloc_failed;
                rx_csum_errors += rx_ring->rx_stats.csum_err;
                rx_errors += rx_ring->rx_stats.errors;
+               hw_csum_rx_good += rx_ring->rx_stats.csum_good;
+               rx_switch_errors += rx_ring->rx_stats.switch_errors;
+               rx_drops += rx_ring->rx_stats.drops;
+               rx_pp_errors += rx_ring->rx_stats.pp_errors;
+               rx_link_errors += rx_ring->rx_stats.link_errors;
+               rx_length_errors += rx_ring->rx_stats.length_errors;
        }
 
        net_stats->rx_bytes = bytes;
        net_stats->rx_packets = pkts;
        interface->alloc_failed = alloc_failed;
        interface->rx_csum_errors = rx_csum_errors;
+       interface->hw_csum_rx_good = hw_csum_rx_good;
+       interface->rx_switch_errors = rx_switch_errors;
+       interface->rx_drops = rx_drops;
+       interface->rx_pp_errors = rx_pp_errors;
+       interface->rx_link_errors = rx_link_errors;
+       interface->rx_length_errors = rx_length_errors;
 
        hw->mac.ops.update_hw_stats(hw, &interface->stats);
 
@@ -483,7 +514,7 @@ static void fm10k_service_task(struct work_struct *work)
 
        interface = container_of(work, struct fm10k_intfc, service_task);
 
-       /* tasks always capable of running, but must be rtnl protected */
+       /* tasks run even when interface is down */
        fm10k_mbx_subtask(interface);
        fm10k_detach_subtask(interface);
        fm10k_reset_subtask(interface);
@@ -663,6 +694,10 @@ static void fm10k_configure_rx_ring(struct fm10k_intfc *interface,
        /* assign default VLAN to queue */
        ring->vid = hw->mac.default_vid;
 
+       /* if we have an active VLAN, disable default VID */
+       if (test_bit(hw->mac.default_vid, interface->active_vlans))
+               ring->vid |= FM10K_VLAN_CLEAR;
+
        /* Map interrupt */
        if (ring->q_vector) {
                rxint = ring->q_vector->v_idx + NON_Q_VECTORS(hw);
@@ -861,10 +896,12 @@ void fm10k_netpoll(struct net_device *netdev)
 
 #endif
 #define FM10K_ERR_MSG(type) case (type): error = #type; break
-static void fm10k_print_fault(struct fm10k_intfc *interface, int type,
+static void fm10k_handle_fault(struct fm10k_intfc *interface, int type,
                              struct fm10k_fault *fault)
 {
        struct pci_dev *pdev = interface->pdev;
+       struct fm10k_hw *hw = &interface->hw;
+       struct fm10k_iov_data *iov_data = interface->iov_data;
        char *error;
 
        switch (type) {
@@ -918,6 +955,30 @@ static void fm10k_print_fault(struct fm10k_intfc *interface, int type,
                 "%s Address: 0x%llx SpecInfo: 0x%x Func: %02x.%0x\n",
                 error, fault->address, fault->specinfo,
                 PCI_SLOT(fault->func), PCI_FUNC(fault->func));
+
+       /* For VF faults, clear out the respective LPORT, reset the queue
+        * resources, and then reconnect to the mailbox. This allows the
+        * VF in question to resume behavior. For transient faults that are
+        * the result of non-malicious behavior this will log the fault and
+        * allow the VF to resume functionality. Obviously for malicious VFs
+        * they will be able to attempt malicious behavior again. In this
+        * case, the system administrator will need to step in and manually
+        * remove or disable the VF in question.
+        */
+       if (fault->func && iov_data) {
+               int vf = fault->func - 1;
+               struct fm10k_vf_info *vf_info = &iov_data->vf_info[vf];
+
+               hw->iov.ops.reset_lport(hw, vf_info);
+               hw->iov.ops.reset_resources(hw, vf_info);
+
+               /* reset_lport disables the VF, so re-enable it */
+               hw->iov.ops.set_lport(hw, vf_info, vf,
+                                     FM10K_VF_FLAG_MULTI_CAPABLE);
+
+               /* reset_resources will disconnect from the mbx  */
+               vf_info->mbx.ops.connect(hw, &vf_info->mbx);
+       }
 }
 
 static void fm10k_report_fault(struct fm10k_intfc *interface, u32 eicr)
@@ -941,7 +1002,7 @@ static void fm10k_report_fault(struct fm10k_intfc *interface, u32 eicr)
                        continue;
                }
 
-               fm10k_print_fault(interface, type, &fault);
+               fm10k_handle_fault(interface, type, &fault);
        }
 }
 
@@ -1705,22 +1766,86 @@ static int fm10k_sw_init(struct fm10k_intfc *interface,
 
 static void fm10k_slot_warn(struct fm10k_intfc *interface)
 {
-       struct device *dev = &interface->pdev->dev;
+       enum pcie_link_width width = PCIE_LNK_WIDTH_UNKNOWN;
+       enum pci_bus_speed speed = PCI_SPEED_UNKNOWN;
        struct fm10k_hw *hw = &interface->hw;
+       int max_gts = 0, expected_gts = 0;
+
+       if (pcie_get_minimum_link(interface->pdev, &speed, &width) ||
+           speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) {
+               dev_warn(&interface->pdev->dev,
+                        "Unable to determine PCI Express bandwidth.\n");
+               return;
+       }
+
+       switch (speed) {
+       case PCIE_SPEED_2_5GT:
+               /* 8b/10b encoding reduces max throughput by 20% */
+               max_gts = 2 * width;
+               break;
+       case PCIE_SPEED_5_0GT:
+               /* 8b/10b encoding reduces max throughput by 20% */
+               max_gts = 4 * width;
+               break;
+       case PCIE_SPEED_8_0GT:
+               /* 128b/130b encoding has less than 2% impact on throughput */
+               max_gts = 8 * width;
+               break;
+       default:
+               dev_warn(&interface->pdev->dev,
+                        "Unable to determine PCI Express bandwidth.\n");
+               return;
+       }
+
+       dev_info(&interface->pdev->dev,
+                "PCI Express bandwidth of %dGT/s available\n",
+                max_gts);
+       dev_info(&interface->pdev->dev,
+                "(Speed:%s, Width: x%d, Encoding Loss:%s, Payload:%s)\n",
+                (speed == PCIE_SPEED_8_0GT ? "8.0GT/s" :
+                 speed == PCIE_SPEED_5_0GT ? "5.0GT/s" :
+                 speed == PCIE_SPEED_2_5GT ? "2.5GT/s" :
+                 "Unknown"),
+                hw->bus.width,
+                (speed == PCIE_SPEED_2_5GT ? "20%" :
+                 speed == PCIE_SPEED_5_0GT ? "20%" :
+                 speed == PCIE_SPEED_8_0GT ? "<2%" :
+                 "Unknown"),
+                (hw->bus.payload == fm10k_bus_payload_128 ? "128B" :
+                 hw->bus.payload == fm10k_bus_payload_256 ? "256B" :
+                 hw->bus.payload == fm10k_bus_payload_512 ? "512B" :
+                 "Unknown"));
 
-       if (hw->mac.ops.is_slot_appropriate(hw))
+       switch (hw->bus_caps.speed) {
+       case fm10k_bus_speed_2500:
+               /* 8b/10b encoding reduces max throughput by 20% */
+               expected_gts = 2 * hw->bus_caps.width;
+               break;
+       case fm10k_bus_speed_5000:
+               /* 8b/10b encoding reduces max throughput by 20% */
+               expected_gts = 4 * hw->bus_caps.width;
+               break;
+       case fm10k_bus_speed_8000:
+               /* 128b/130b encoding has less than 2% impact on throughput */
+               expected_gts = 8 * hw->bus_caps.width;
+               break;
+       default:
+               dev_warn(&interface->pdev->dev,
+                        "Unable to determine expected PCI Express bandwidth.\n");
                return;
+       }
 
-       dev_warn(dev,
-                "For optimal performance, a %s %s slot is recommended.\n",
-                (hw->bus_caps.width == fm10k_bus_width_pcie_x1 ? "x1" :
-                 hw->bus_caps.width == fm10k_bus_width_pcie_x4 ? "x4" :
-                 "x8"),
-                (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s" :
-                 hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s" :
-                 "8.0GT/s"));
-       dev_warn(dev,
-                "A slot with more lanes and/or higher speed is suggested.\n");
+       if (max_gts < expected_gts) {
+               dev_warn(&interface->pdev->dev,
+                        "This device requires %dGT/s of bandwidth for optimal performance.\n",
+                        expected_gts);
+               dev_warn(&interface->pdev->dev,
+                        "A %sslot with x%d lanes is suggested.\n",
+                        (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s " :
+                         hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s " :
+                         hw->bus_caps.speed == fm10k_bus_speed_8000 ? "8.0GT/s " : ""),
+                        hw->bus_caps.width);
+       }
 }
 
 /**
@@ -1739,7 +1864,6 @@ static int fm10k_probe(struct pci_dev *pdev,
 {
        struct net_device *netdev;
        struct fm10k_intfc *interface;
-       struct fm10k_hw *hw;
        int err;
 
        err = pci_enable_device_mem(pdev);
@@ -1783,7 +1907,6 @@ static int fm10k_probe(struct pci_dev *pdev,
 
        interface->netdev = netdev;
        interface->pdev = pdev;
-       hw = &interface->hw;
 
        interface->uc_addr = ioremap(pci_resource_start(pdev, 0),
                                     FM10K_UC_ADDR_SIZE);
@@ -1825,24 +1948,12 @@ static int fm10k_probe(struct pci_dev *pdev,
        /* Register PTP interface */
        fm10k_ptp_register(interface);
 
-       /* print bus type/speed/width info */
-       dev_info(&pdev->dev, "(PCI Express:%s Width: %s Payload: %s)\n",
-                (hw->bus.speed == fm10k_bus_speed_8000 ? "8.0GT/s" :
-                 hw->bus.speed == fm10k_bus_speed_5000 ? "5.0GT/s" :
-                 hw->bus.speed == fm10k_bus_speed_2500 ? "2.5GT/s" :
-                 "Unknown"),
-                (hw->bus.width == fm10k_bus_width_pcie_x8 ? "x8" :
-                 hw->bus.width == fm10k_bus_width_pcie_x4 ? "x4" :
-                 hw->bus.width == fm10k_bus_width_pcie_x1 ? "x1" :
-                 "Unknown"),
-                (hw->bus.payload == fm10k_bus_payload_128 ? "128B" :
-                 hw->bus.payload == fm10k_bus_payload_256 ? "256B" :
-                 hw->bus.payload == fm10k_bus_payload_512 ? "512B" :
-                 "Unknown"));
-
        /* print warning for non-optimal configurations */
        fm10k_slot_warn(interface);
 
+       /* report MAC address for logging */
+       dev_info(&pdev->dev, "%pM\n", netdev->dev_addr);
+
        /* enable SR-IOV after registering netdev to enforce PF/VF ordering */
        fm10k_iov_configure(pdev, 0);
 
@@ -1983,6 +2094,16 @@ static int fm10k_resume(struct pci_dev *pdev)
        if (err)
                return err;
 
+       /* assume host is not ready, to prevent race with watchdog in case we
+        * actually don't have connection to the switch
+        */
+       interface->host_ready = false;
+       fm10k_watchdog_host_not_ready(interface);
+
+       /* clear the service task disable bit to allow service task to start */
+       clear_bit(__FM10K_SERVICE_DISABLE, &interface->state);
+       fm10k_service_event_schedule(interface);
+
        /* restore SR-IOV interface */
        fm10k_iov_resume(pdev);
 
@@ -2010,6 +2131,15 @@ static int fm10k_suspend(struct pci_dev *pdev,
 
        fm10k_iov_suspend(pdev);
 
+       /* the watchdog tasks may read registers, which will appear like a
+        * surprise-remove event once the PCI device is disabled. This will
+        * cause us to close the netdevice, so we don't retain the open/closed
+        * state post-resume. Prevent this by disabling the service task while
+        * suspended, until we actually resume.
+        */
+       set_bit(__FM10K_SERVICE_DISABLE, &interface->state);
+       cancel_work_sync(&interface->service_task);
+
        rtnl_lock();
 
        if (netif_running(netdev))
index 3ca0233b3ea23682c23126bc24357629e3a4eb9a..8c0bdc4e4edd44eb744810619ead6709fe760c06 100644 (file)
@@ -59,6 +59,11 @@ static s32 fm10k_reset_hw_pf(struct fm10k_hw *hw)
        if (reg & (FM10K_DMA_CTRL_TX_ACTIVE | FM10K_DMA_CTRL_RX_ACTIVE))
                return FM10K_ERR_DMA_PENDING;
 
+       /* verify the switch is ready for reset */
+       reg = fm10k_read_reg(hw, FM10K_DMA_CTRL2);
+       if (!(reg & FM10K_DMA_CTRL2_SWITCH_READY))
+               goto out;
+
        /* Inititate data path reset */
        reg |= FM10K_DMA_CTRL_DATAPATH_RESET;
        fm10k_write_reg(hw, FM10K_DMA_CTRL, reg);
@@ -72,6 +77,7 @@ static s32 fm10k_reset_hw_pf(struct fm10k_hw *hw)
        if (!(reg & FM10K_IP_NOTINRESET))
                err = FM10K_ERR_RESET_FAILED;
 
+out:
        return err;
 }
 
@@ -184,19 +190,6 @@ static s32 fm10k_init_hw_pf(struct fm10k_hw *hw)
        return 0;
 }
 
-/**
- *  fm10k_is_slot_appropriate_pf - Indicate appropriate slot for this SKU
- *  @hw: pointer to hardware structure
- *
- *  Looks at the PCIe bus info to confirm whether or not this slot can support
- *  the necessary bandwidth for this device.
- **/
-static bool fm10k_is_slot_appropriate_pf(struct fm10k_hw *hw)
-{
-       return (hw->bus.speed == hw->bus_caps.speed) &&
-              (hw->bus.width == hw->bus_caps.width);
-}
-
 /**
  *  fm10k_update_vlan_pf - Update status of VLAN ID in VLAN filter table
  *  @hw: pointer to hardware structure
@@ -1161,6 +1154,24 @@ s32 fm10k_iov_msg_msix_pf(struct fm10k_hw *hw, u32 **results,
        return hw->iov.ops.assign_int_moderator(hw, vf_idx);
 }
 
+/**
+ * fm10k_iov_select_vid - Select correct default VID
+ * @hw: Pointer to hardware structure
+ * @vid: VID to correct
+ *
+ * Will report an error if VID is out of range. For VID = 0, it will return
+ * either the pf_vid or sw_vid depending on which one is set.
+ */
+static inline s32 fm10k_iov_select_vid(struct fm10k_vf_info *vf_info, u16 vid)
+{
+       if (!vid)
+               return vf_info->pf_vid ? vf_info->pf_vid : vf_info->sw_vid;
+       else if (vf_info->pf_vid && vid != vf_info->pf_vid)
+               return FM10K_ERR_PARAM;
+       else
+               return vid;
+}
+
 /**
  *  fm10k_iov_msg_mac_vlan_pf - Message handler for MAC/VLAN request from VF
  *  @hw: Pointer to hardware structure
@@ -1175,9 +1186,10 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
                              struct fm10k_mbx_info *mbx)
 {
        struct fm10k_vf_info *vf_info = (struct fm10k_vf_info *)mbx;
-       int err = 0;
        u8 mac[ETH_ALEN];
        u32 *result;
+       int err = 0;
+       bool set;
        u16 vlan;
        u32 vid;
 
@@ -1193,19 +1205,21 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
                if (err)
                        return err;
 
-               /* if VLAN ID is 0, set the default VLAN ID instead of 0 */
-               if (!vid || (vid == FM10K_VLAN_CLEAR)) {
-                       if (vf_info->pf_vid)
-                               vid |= vf_info->pf_vid;
-                       else
-                               vid |= vf_info->sw_vid;
-               } else if (vid != vf_info->pf_vid) {
+               /* verify upper 16 bits are zero */
+               if (vid >> 16)
                        return FM10K_ERR_PARAM;
-               }
+
+               set = !(vid & FM10K_VLAN_CLEAR);
+               vid &= ~FM10K_VLAN_CLEAR;
+
+               err = fm10k_iov_select_vid(vf_info, vid);
+               if (err < 0)
+                       return err;
+               else
+                       vid = err;
 
                /* update VSI info for VF in regards to VLAN table */
-               err = hw->mac.ops.update_vlan(hw, vid, vf_info->vsi,
-                                             !(vid & FM10K_VLAN_CLEAR));
+               err = hw->mac.ops.update_vlan(hw, vid, vf_info->vsi, set);
        }
 
        if (!err && !!results[FM10K_MAC_VLAN_MSG_MAC]) {
@@ -1221,19 +1235,18 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
                    memcmp(mac, vf_info->mac, ETH_ALEN))
                        return FM10K_ERR_PARAM;
 
-               /* if VLAN ID is 0, set the default VLAN ID instead of 0 */
-               if (!vlan || (vlan == FM10K_VLAN_CLEAR)) {
-                       if (vf_info->pf_vid)
-                               vlan |= vf_info->pf_vid;
-                       else
-                               vlan |= vf_info->sw_vid;
-               } else if (vf_info->pf_vid) {
-                       return FM10K_ERR_PARAM;
-               }
+               set = !(vlan & FM10K_VLAN_CLEAR);
+               vlan &= ~FM10K_VLAN_CLEAR;
+
+               err = fm10k_iov_select_vid(vf_info, vlan);
+               if (err < 0)
+                       return err;
+               else
+                       vlan = err;
 
                /* notify switch of request for new unicast address */
-               err = hw->mac.ops.update_uc_addr(hw, vf_info->glort, mac, vlan,
-                                                !(vlan & FM10K_VLAN_CLEAR), 0);
+               err = hw->mac.ops.update_uc_addr(hw, vf_info->glort,
+                                                mac, vlan, set, 0);
        }
 
        if (!err && !!results[FM10K_MAC_VLAN_MSG_MULTICAST]) {
@@ -1248,19 +1261,18 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
                if (!(vf_info->vf_flags & FM10K_VF_FLAG_MULTI_ENABLED))
                        return FM10K_ERR_PARAM;
 
-               /* if VLAN ID is 0, set the default VLAN ID instead of 0 */
-               if (!vlan || (vlan == FM10K_VLAN_CLEAR)) {
-                       if (vf_info->pf_vid)
-                               vlan |= vf_info->pf_vid;
-                       else
-                               vlan |= vf_info->sw_vid;
-               } else if (vf_info->pf_vid) {
-                       return FM10K_ERR_PARAM;
-               }
+               set = !(vlan & FM10K_VLAN_CLEAR);
+               vlan &= ~FM10K_VLAN_CLEAR;
+
+               err = fm10k_iov_select_vid(vf_info, vlan);
+               if (err < 0)
+                       return err;
+               else
+                       vlan = err;
 
                /* notify switch of request for new multicast address */
-               err = hw->mac.ops.update_mc_addr(hw, vf_info->glort, mac, vlan,
-                                                !(vlan & FM10K_VLAN_CLEAR));
+               err = hw->mac.ops.update_mc_addr(hw, vf_info->glort,
+                                                mac, vlan, set);
        }
 
        return err;
@@ -1849,7 +1861,6 @@ static struct fm10k_mac_ops mac_ops_pf = {
        .init_hw                = &fm10k_init_hw_pf,
        .start_hw               = &fm10k_start_hw_generic,
        .stop_hw                = &fm10k_stop_hw_generic,
-       .is_slot_appropriate    = &fm10k_is_slot_appropriate_pf,
        .update_vlan            = &fm10k_update_vlan_pf,
        .read_mac_addr          = &fm10k_read_mac_addr_pf,
        .update_uc_addr         = &fm10k_update_uc_addr_pf,
index 2a17d82fa37d47c14e7190a3bbe1f01db080f69e..318a212f0a78e54e9705780b23a6ba3dd6ddba4b 100644 (file)
@@ -521,7 +521,6 @@ struct fm10k_mac_ops {
        s32 (*stop_hw)(struct fm10k_hw *);
        s32 (*get_bus_info)(struct fm10k_hw *);
        s32 (*get_host_state)(struct fm10k_hw *, bool *);
-       bool (*is_slot_appropriate)(struct fm10k_hw *);
        s32 (*update_vlan)(struct fm10k_hw *, u32, u8, bool);
        s32 (*read_mac_addr)(struct fm10k_hw *);
        s32 (*update_uc_addr)(struct fm10k_hw *, u16, const u8 *,
@@ -763,6 +762,12 @@ enum fm10k_rxdesc_xc {
 #define FM10K_RXD_STATUS_L4E           0x4000 /* L4 csum error */
 #define FM10K_RXD_STATUS_IPE           0x8000 /* IPv4 csum error */
 
+#define FM10K_RXD_ERR_SWITCH_ERROR     0x0001 /* Switch found bad packet */
+#define FM10K_RXD_ERR_NO_DESCRIPTOR    0x0002 /* No descriptor available */
+#define FM10K_RXD_ERR_PP_ERROR         0x0004 /* RAM error during processing */
+#define FM10K_RXD_ERR_SWITCH_READY     0x0008 /* Link transition mid-packet */
+#define FM10K_RXD_ERR_TOO_BIG          0x0010 /* Pkt too big for single buf */
+
 struct fm10k_ftag {
        __be16 swpri_type_user;
        __be16 vlan;
index 94f0f6a146d96e7e57fe9bf647bf6ae480a727b4..36c8b0aa08fd2eeadfbc7b87ee5ce9d8d619b589 100644 (file)
@@ -131,19 +131,6 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw)
        return 0;
 }
 
-/**
- *  fm10k_is_slot_appropriate_vf - Indicate appropriate slot for this SKU
- *  @hw: pointer to hardware structure
- *
- *  Looks at the PCIe bus info to confirm whether or not this slot can support
- *  the necessary bandwidth for this device. Since the VF has no control over
- *  the "slot" it is in, always indicate that the slot is appropriate.
- **/
-static bool fm10k_is_slot_appropriate_vf(struct fm10k_hw *hw)
-{
-       return true;
-}
-
 /* This structure defines the attibutes to be parsed below */
 const struct fm10k_tlv_attr fm10k_mac_vlan_msg_attr[] = {
        FM10K_TLV_ATTR_U32(FM10K_MAC_VLAN_MSG_VLAN),
@@ -552,7 +539,6 @@ static struct fm10k_mac_ops mac_ops_vf = {
        .init_hw                = &fm10k_init_hw_vf,
        .start_hw               = &fm10k_start_hw_generic,
        .stop_hw                = &fm10k_stop_hw_vf,
-       .is_slot_appropriate    = &fm10k_is_slot_appropriate_vf,
        .update_vlan            = &fm10k_update_vlan_vf,
        .read_mac_addr          = &fm10k_read_mac_addr_vf,
        .update_uc_addr         = &fm10k_update_uc_addr_vf,
index e7462793d48d416bf33d826edd470e3f0cf247cc..4dd3e26129b44657cdb05e2e9335ab4f554bcd76 100644 (file)
@@ -71,7 +71,6 @@
 #define I40E_MAX_VEB          16
 
 #define I40E_MAX_NUM_DESCRIPTORS      4096
-#define I40E_MAX_REGISTER     0x800000
 #define I40E_MAX_CSR_SPACE (4 * 1024 * 1024 - 64 * 1024)
 #define I40E_DEFAULT_NUM_DESCRIPTORS  512
 #define I40E_REQ_DESCRIPTOR_MULTIPLE  32
 #endif /* I40E_FCOE */
 #define I40E_MAX_AQ_BUF_SIZE          4096
 #define I40E_AQ_LEN                   256
-#define I40E_AQ_WORK_LIMIT            32
+#define I40E_AQ_WORK_LIMIT            66 /* max number of VFs + a little */
 #define I40E_MAX_USER_PRIORITY        8
 #define I40E_DEFAULT_MSG_ENABLE       4
 #define I40E_QUEUE_WAIT_RETRY_LIMIT   10
-#define I40E_INT_NAME_STR_LEN        (IFNAMSIZ + 9)
+#define I40E_INT_NAME_STR_LEN        (IFNAMSIZ + 16)
 
 /* Ethtool Private Flags */
 #define I40E_PRIV_FLAGS_NPAR_FLAG      BIT(0)
+#define I40E_PRIV_FLAGS_LINKPOLL_FLAG  BIT(1)
+#define I40E_PRIV_FLAGS_FD_ATR         BIT(2)
+#define I40E_PRIV_FLAGS_VEB_STATS      BIT(3)
 
 #define I40E_NVM_VERSION_LO_SHIFT  0
 #define I40E_NVM_VERSION_LO_MASK   (0xff << I40E_NVM_VERSION_LO_SHIFT)
 #define I40E_NVM_VERSION_HI_SHIFT  12
 #define I40E_NVM_VERSION_HI_MASK   (0xf << I40E_NVM_VERSION_HI_SHIFT)
+#define I40E_OEM_VER_BUILD_MASK    0xffff
+#define I40E_OEM_VER_PATCH_MASK    0xff
+#define I40E_OEM_VER_BUILD_SHIFT   8
+#define I40E_OEM_VER_SHIFT         24
 
 /* The values in here are decimal coded as hex as is the case in the NVM map*/
 #define I40E_CURRENT_NVM_VERSION_HI 0x2
@@ -243,7 +249,6 @@ struct i40e_pf {
        struct pci_dev *pdev;
        struct i40e_hw hw;
        unsigned long state;
-       unsigned long link_check_timeout;
        struct msix_entry *msix_entries;
        bool fc_autoneg_status;
 
@@ -305,7 +310,6 @@ struct i40e_pf {
 #ifdef I40E_FCOE
 #define I40E_FLAG_FCOE_ENABLED                 BIT_ULL(11)
 #endif /* I40E_FCOE */
-#define I40E_FLAG_IN_NETPOLL                   BIT_ULL(12)
 #define I40E_FLAG_16BYTE_RX_DESC_ENABLED       BIT_ULL(13)
 #define I40E_FLAG_CLEAN_ADMINQ                 BIT_ULL(14)
 #define I40E_FLAG_FILTER_SYNC                  BIT_ULL(15)
@@ -327,8 +331,11 @@ struct i40e_pf {
 #define I40E_FLAG_OUTER_UDP_CSUM_CAPABLE       BIT_ULL(33)
 #define I40E_FLAG_128_QP_RSS_CAPABLE           BIT_ULL(34)
 #define I40E_FLAG_WB_ON_ITR_CAPABLE            BIT_ULL(35)
+#define I40E_FLAG_VEB_STATS_ENABLED            BIT_ULL(37)
 #define I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE  BIT_ULL(38)
+#define I40E_FLAG_LINK_POLLING_ENABLED         BIT_ULL(39)
 #define I40E_FLAG_VEB_MODE_ENABLED             BIT_ULL(40)
+#define I40E_FLAG_NO_PCI_LINK_CHECK            BIT_ULL(42)
 
        /* tracks features that get auto disabled by errors */
        u64 auto_disable_flags;
@@ -409,6 +416,9 @@ struct i40e_pf {
        /* These are only valid in NPAR modes */
        u32 npar_max_bw;
        u32 npar_min_bw;
+
+       u32 ioremap_len;
+       u32 fd_inv;
 };
 
 struct i40e_mac_filter {
@@ -460,6 +470,8 @@ struct i40e_vsi {
 #define I40E_VSI_FLAG_VEB_OWNER                BIT(1)
        unsigned long flags;
 
+       /* Per VSI lock to protect elements/list (MAC filter) */
+       spinlock_t mac_filter_list_lock;
        struct list_head mac_filter_list;
 
        /* VSI stats */
@@ -474,6 +486,7 @@ struct i40e_vsi {
 #endif
        u32 tx_restart;
        u32 tx_busy;
+       u64 tx_linearize;
        u32 rx_buf_failed;
        u32 rx_page_failed;
 
@@ -489,6 +502,7 @@ struct i40e_vsi {
         */
        u16 rx_itr_setting;
        u16 tx_itr_setting;
+       u16 int_rate_limit;  /* value in usecs */
 
        u16 rss_table_size;
        u16 rss_size;
@@ -534,6 +548,7 @@ struct i40e_vsi {
        u16 idx;               /* index in pf->vsi[] */
        u16 veb_idx;           /* index of VEB parent */
        struct kobject *kobj;  /* sysfs object */
+       bool current_isup;     /* Sync 'link up' logging */
 
        /* VSI specific handlers */
        irqreturn_t (*irq_handler)(int irq, void *data);
@@ -564,6 +579,8 @@ struct i40e_q_vector {
        struct rcu_head rcu;    /* to avoid race with update stats on free */
        char name[I40E_INT_NAME_STR_LEN];
        bool arm_wb_state;
+#define ITR_COUNTDOWN_START 100
+       u8 itr_countdown;       /* when 0 should adjust ITR */
 } ____cacheline_internodealigned_in_smp;
 
 /* lan device */
@@ -573,22 +590,29 @@ struct i40e_device {
 };
 
 /**
- * i40e_fw_version_str - format the FW and NVM version strings
+ * i40e_nvm_version_str - format the NVM version strings
  * @hw: ptr to the hardware info
  **/
-static inline char *i40e_fw_version_str(struct i40e_hw *hw)
+static inline char *i40e_nvm_version_str(struct i40e_hw *hw)
 {
        static char buf[32];
+       u32 full_ver;
+       u8 ver, patch;
+       u16 build;
+
+       full_ver = hw->nvm.oem_ver;
+       ver = (u8)(full_ver >> I40E_OEM_VER_SHIFT);
+       build = (u16)((full_ver >> I40E_OEM_VER_BUILD_SHIFT)
+                & I40E_OEM_VER_BUILD_MASK);
+       patch = (u8)(full_ver & I40E_OEM_VER_PATCH_MASK);
 
        snprintf(buf, sizeof(buf),
-                "f%d.%d.%05d a%d.%d n%x.%02x e%x",
-                hw->aq.fw_maj_ver, hw->aq.fw_min_ver, hw->aq.fw_build,
-                hw->aq.api_maj_ver, hw->aq.api_min_ver,
+                "%x.%02x 0x%x %d.%d.%d",
                 (hw->nvm.version & I40E_NVM_VERSION_HI_MASK) >>
                        I40E_NVM_VERSION_HI_SHIFT,
                 (hw->nvm.version & I40E_NVM_VERSION_LO_MASK) >>
                        I40E_NVM_VERSION_LO_SHIFT,
-                (hw->nvm.eetrack & 0xffffff));
+                hw->nvm.eetrack, ver, build, patch);
 
        return buf;
 }
@@ -667,7 +691,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
                                        bool is_vf, bool is_netdev);
 void i40e_del_filter(struct i40e_vsi *vsi, u8 *macaddr, s16 vlan,
                     bool is_vf, bool is_netdev);
-int i40e_sync_vsi_filters(struct i40e_vsi *vsi);
+int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl);
 struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
                                u16 uplink, u32 param1);
 int i40e_vsi_release(struct i40e_vsi *vsi);
@@ -700,7 +724,24 @@ static inline void i40e_dbg_pf_exit(struct i40e_pf *pf) {}
 static inline void i40e_dbg_init(void) {}
 static inline void i40e_dbg_exit(void) {}
 #endif /* CONFIG_DEBUG_FS*/
-void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector);
+/**
+ * i40e_irq_dynamic_enable - Enable default interrupt generation settings
+ * @vsi: pointer to a vsi
+ * @vector: enable a particular Hw Interrupt vector, without base_vector
+ **/
+static inline void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector)
+{
+       struct i40e_pf *pf = vsi->back;
+       struct i40e_hw *hw = &pf->hw;
+       u32 val;
+
+       val = I40E_PFINT_DYN_CTLN_INTENA_MASK |
+             I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
+             (I40E_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT);
+       wr32(hw, I40E_PFINT_DYN_CTLN(vector + vsi->base_vector - 1), val);
+       /* skip the flush */
+}
+
 void i40e_irq_dynamic_disable(struct i40e_vsi *vsi, int vector);
 void i40e_irq_dynamic_disable_icr0(struct i40e_pf *pf);
 void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf);
@@ -739,7 +780,7 @@ int i40e_fcoe_vsi_init(struct i40e_vsi *vsi, struct i40e_vsi_context *ctxt);
 u8 i40e_get_fcoe_tc_map(struct i40e_pf *pf);
 void i40e_fcoe_config_netdev(struct net_device *netdev, struct i40e_vsi *vsi);
 void i40e_fcoe_vsi_setup(struct i40e_pf *pf);
-int i40e_init_pf_fcoe(struct i40e_pf *pf);
+void i40e_init_pf_fcoe(struct i40e_pf *pf);
 int i40e_fcoe_setup_ddp_resources(struct i40e_vsi *vsi);
 void i40e_fcoe_free_ddp_resources(struct i40e_vsi *vsi);
 int i40e_fcoe_handle_offload(struct i40e_ring *rx_ring,
@@ -771,4 +812,5 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi);
 i40e_status i40e_get_npar_bw_setting(struct i40e_pf *pf);
 i40e_status i40e_set_npar_bw_setting(struct i40e_pf *pf);
 i40e_status i40e_commit_npar_bw_setting(struct i40e_pf *pf);
+void i40e_print_link_message(struct i40e_vsi *vsi, bool isup);
 #endif /* _I40E_H_ */
index c0e943aecd1394262757e790b04a6ce1eccda72a..0ff8f01e57ee5a4a7de890485e5987fd9eec7f8a 100644 (file)
@@ -482,8 +482,12 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw)
 {
        i40e_status ret_code = 0;
 
-       if (hw->aq.asq.count == 0)
-               return I40E_ERR_NOT_READY;
+       mutex_lock(&hw->aq.asq_mutex);
+
+       if (hw->aq.asq.count == 0) {
+               ret_code = I40E_ERR_NOT_READY;
+               goto shutdown_asq_out;
+       }
 
        /* Stop firmware AdminQ processing */
        wr32(hw, hw->aq.asq.head, 0);
@@ -492,16 +496,13 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw)
        wr32(hw, hw->aq.asq.bal, 0);
        wr32(hw, hw->aq.asq.bah, 0);
 
-       /* make sure lock is available */
-       mutex_lock(&hw->aq.asq_mutex);
-
        hw->aq.asq.count = 0; /* to indicate uninitialized queue */
 
        /* free ring buffers */
        i40e_free_asq_bufs(hw);
 
+shutdown_asq_out:
        mutex_unlock(&hw->aq.asq_mutex);
-
        return ret_code;
 }
 
@@ -515,8 +516,12 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw)
 {
        i40e_status ret_code = 0;
 
-       if (hw->aq.arq.count == 0)
-               return I40E_ERR_NOT_READY;
+       mutex_lock(&hw->aq.arq_mutex);
+
+       if (hw->aq.arq.count == 0) {
+               ret_code = I40E_ERR_NOT_READY;
+               goto shutdown_arq_out;
+       }
 
        /* Stop firmware AdminQ processing */
        wr32(hw, hw->aq.arq.head, 0);
@@ -525,16 +530,13 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw)
        wr32(hw, hw->aq.arq.bal, 0);
        wr32(hw, hw->aq.arq.bah, 0);
 
-       /* make sure lock is available */
-       mutex_lock(&hw->aq.arq_mutex);
-
        hw->aq.arq.count = 0; /* to indicate uninitialized queue */
 
        /* free ring buffers */
        i40e_free_arq_bufs(hw);
 
+shutdown_arq_out:
        mutex_unlock(&hw->aq.arq_mutex);
-
        return ret_code;
 }
 
@@ -551,8 +553,9 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw)
  **/
 i40e_status i40e_init_adminq(struct i40e_hw *hw)
 {
-       i40e_status ret_code;
+       u16 cfg_ptr, oem_hi, oem_lo;
        u16 eetrack_lo, eetrack_hi;
+       i40e_status ret_code;
        int retry = 0;
 
        /* verify input for valid configuration */
@@ -611,6 +614,12 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw)
        i40e_read_nvm_word(hw, I40E_SR_NVM_EETRACK_LO, &eetrack_lo);
        i40e_read_nvm_word(hw, I40E_SR_NVM_EETRACK_HI, &eetrack_hi);
        hw->nvm.eetrack = (eetrack_hi << 16) | eetrack_lo;
+       i40e_read_nvm_word(hw, I40E_SR_BOOT_CONFIG_PTR, &cfg_ptr);
+       i40e_read_nvm_word(hw, (cfg_ptr + I40E_NVM_OEM_VER_OFF),
+                          &oem_hi);
+       i40e_read_nvm_word(hw, (cfg_ptr + (I40E_NVM_OEM_VER_OFF + 1)),
+                          &oem_lo);
+       hw->nvm.oem_ver = ((u32)oem_hi << 16) | oem_lo;
 
        if (hw->aq.api_maj_ver > I40E_FW_API_VERSION_MAJOR) {
                ret_code = I40E_ERR_FIRMWARE_API_VERSION;
@@ -657,6 +666,9 @@ i40e_status i40e_shutdown_adminq(struct i40e_hw *hw)
 
        /* destroy the locks */
 
+       if (hw->nvm_buff.va)
+               i40e_free_virt_mem(hw, &hw->nvm_buff);
+
        return ret_code;
 }
 
@@ -678,8 +690,7 @@ static u16 i40e_clean_asq(struct i40e_hw *hw)
        details = I40E_ADMINQ_DETAILS(*asq, ntc);
        while (rd32(hw, hw->aq.asq.head) != ntc) {
                i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,
-                          "%s: ntc %d head %d.\n", __func__, ntc,
-                          rd32(hw, hw->aq.asq.head));
+                          "ntc %d head %d.\n", ntc, rd32(hw, hw->aq.asq.head));
 
                if (details->callback) {
                        I40E_ADMINQ_CALLBACK cb_func =
@@ -742,19 +753,23 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
        u16  retval = 0;
        u32  val = 0;
 
-       val = rd32(hw, hw->aq.asq.head);
-       if (val >= hw->aq.num_asq_entries) {
+       mutex_lock(&hw->aq.asq_mutex);
+
+       if (hw->aq.asq.count == 0) {
                i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,
-                          "AQTX: head overrun at %d\n", val);
+                          "AQTX: Admin queue not initialized.\n");
                status = I40E_ERR_QUEUE_EMPTY;
-               goto asq_send_command_exit;
+               goto asq_send_command_error;
        }
 
-       if (hw->aq.asq.count == 0) {
+       hw->aq.asq_last_status = I40E_AQ_RC_OK;
+
+       val = rd32(hw, hw->aq.asq.head);
+       if (val >= hw->aq.num_asq_entries) {
                i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,
-                          "AQTX: Admin queue not initialized.\n");
+                          "AQTX: head overrun at %d\n", val);
                status = I40E_ERR_QUEUE_EMPTY;
-               goto asq_send_command_exit;
+               goto asq_send_command_error;
        }
 
        details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use);
@@ -779,8 +794,6 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
        desc->flags &= ~cpu_to_le16(details->flags_dis);
        desc->flags |= cpu_to_le16(details->flags_ena);
 
-       mutex_lock(&hw->aq.asq_mutex);
-
        if (buff_size > hw->aq.asq_buf_size) {
                i40e_debug(hw,
                           I40E_DEBUG_AQ_MESSAGE,
@@ -889,6 +902,10 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                   "AQTX: desc and buffer writeback:\n");
        i40e_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, buff, buff_size);
 
+       /* save writeback aq if requested */
+       if (details->wb_desc)
+               *details->wb_desc = *desc_on_ring;
+
        /* update the error if time out occurred */
        if ((!cmd_completed) &&
            (!details->async && !details->postpone)) {
@@ -900,7 +917,6 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
 
 asq_send_command_error:
        mutex_unlock(&hw->aq.asq_mutex);
-asq_send_command_exit:
        return status;
 }
 
@@ -1023,6 +1039,19 @@ clean_arq_element_err:
                        i40e_release_nvm(hw);
                        hw->aq.nvm_release_on_done = false;
                }
+
+               switch (hw->nvmupd_state) {
+               case I40E_NVMUPD_STATE_INIT_WAIT:
+                       hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
+                       break;
+
+               case I40E_NVMUPD_STATE_WRITE_WAIT:
+                       hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING;
+                       break;
+
+               default:
+                       break;
+               }
        }
 
        return ret_code;
index 28e519a50de4063edcae851c6e94fce88687203b..12fbbddea299efdf39a441bc922418efd97ebd40 100644 (file)
@@ -69,6 +69,7 @@ struct i40e_asq_cmd_details {
        u16 flags_dis;
        bool async;
        bool postpone;
+       struct i40e_aq_desc *wb_desc;
 };
 
 #define I40E_ADMINQ_DETAILS(R, i)   \
@@ -108,9 +109,10 @@ struct i40e_adminq_info {
 
 /**
  * i40e_aq_rc_to_posix - convert errors to user-land codes
- * aq_rc: AdminQ error code to convert
+ * aq_ret: AdminQ handler error code can override aq_rc
+ * aq_rc: AdminQ firmware error code to convert
  **/
-static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc)
+static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc)
 {
        int aq_to_posix[] = {
                0,           /* I40E_AQ_RC_OK */
@@ -142,8 +144,9 @@ static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc)
        if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT)
                return -EAGAIN;
 
-       if (aq_rc >= ARRAY_SIZE(aq_to_posix))
+       if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0]))))
                return -ERANGE;
+
        return aq_to_posix[aq_rc];
 }
 
index 95d23bfbcbf11bbb17d9f6a133d1e0ed5ff44187..6584b6cd73fd30187c0c2ca9d37dc3cbff51ab81 100644 (file)
@@ -1722,11 +1722,13 @@ struct i40e_aqc_get_link_status {
        u8      phy_type;    /* i40e_aq_phy_type   */
        u8      link_speed;  /* i40e_aq_link_speed */
        u8      link_info;
-#define I40E_AQ_LINK_UP                        0x01
+#define I40E_AQ_LINK_UP                        0x01    /* obsolete */
+#define I40E_AQ_LINK_UP_FUNCTION       0x01
 #define I40E_AQ_LINK_FAULT             0x02
 #define I40E_AQ_LINK_FAULT_TX          0x04
 #define I40E_AQ_LINK_FAULT_RX          0x08
 #define I40E_AQ_LINK_FAULT_REMOTE      0x10
+#define I40E_AQ_LINK_UP_PORT           0x20
 #define I40E_AQ_MEDIA_AVAILABLE                0x40
 #define I40E_AQ_SIGNAL_DETECT          0x80
        u8      an_info;
@@ -2062,6 +2064,7 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start);
 #define I40E_AQC_CEE_APP_ISCSI_MASK    (0x7 << I40E_AQC_CEE_APP_ISCSI_SHIFT)
 #define I40E_AQC_CEE_APP_FIP_SHIFT     0x8
 #define I40E_AQC_CEE_APP_FIP_MASK      (0x7 << I40E_AQC_CEE_APP_FIP_SHIFT)
+
 #define I40E_AQC_CEE_PG_STATUS_SHIFT   0x0
 #define I40E_AQC_CEE_PG_STATUS_MASK    (0x7 << I40E_AQC_CEE_PG_STATUS_SHIFT)
 #define I40E_AQC_CEE_PFC_STATUS_SHIFT  0x3
@@ -2070,10 +2073,19 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start);
 #define I40E_AQC_CEE_APP_STATUS_MASK   (0x7 << I40E_AQC_CEE_APP_STATUS_SHIFT)
 #define I40E_AQC_CEE_FCOE_STATUS_SHIFT 0x8
 #define I40E_AQC_CEE_FCOE_STATUS_MASK  (0x7 << I40E_AQC_CEE_FCOE_STATUS_SHIFT)
-#define I40E_AQC_CEE_ISCSI_STATUS_SHIFT        0xA
+#define I40E_AQC_CEE_ISCSI_STATUS_SHIFT        0xB
 #define I40E_AQC_CEE_ISCSI_STATUS_MASK (0x7 << I40E_AQC_CEE_ISCSI_STATUS_SHIFT)
 #define I40E_AQC_CEE_FIP_STATUS_SHIFT  0x10
 #define I40E_AQC_CEE_FIP_STATUS_MASK   (0x7 << I40E_AQC_CEE_FIP_STATUS_SHIFT)
+
+/* struct i40e_aqc_get_cee_dcb_cfg_v1_resp was originally defined with
+ * word boundary layout issues, which the Linux compilers silently deal
+ * with by adding padding, making the actual struct larger than designed.
+ * However, the FW compiler for the NIC is less lenient and complains
+ * about the struct.  Hence, the struct defined here has an extra byte in
+ * fields reserved3 and reserved4 to directly acknowledge that padding,
+ * and the new length is used in the length check macro.
+ */
 struct i40e_aqc_get_cee_dcb_cfg_v1_resp {
        u8      reserved1;
        u8      oper_num_tc;
@@ -2081,9 +2093,9 @@ struct i40e_aqc_get_cee_dcb_cfg_v1_resp {
        u8      reserved2;
        u8      oper_tc_bw[8];
        u8      oper_pfc_en;
-       u8      reserved3;
+       u8      reserved3[2];
        __le16  oper_app_prio;
-       u8      reserved4;
+       u8      reserved4[2];
        __le16  tlv_status;
 };
 
@@ -2120,6 +2132,13 @@ I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_get_cee_dcb_cfg_resp);
 struct i40e_aqc_lldp_set_local_mib {
 #define SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT       0
 #define SET_LOCAL_MIB_AC_TYPE_DCBX_MASK        (1 << SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT)
+#define SET_LOCAL_MIB_AC_TYPE_DCBX_MASK        (1 << \
+                                       SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT)
+#define SET_LOCAL_MIB_AC_TYPE_LOCAL_MIB        0x0
+#define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_SHIFT   (1)
+#define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_MASK    (1 << \
+                               SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_SHIFT)
+#define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS         0x1
        u8      type;
        u8      reserved0;
        __le16  length;
index 114dc6450183b37a136e48ad9763ed9fb4c68cbc..2d74c6e4d7b618fe3c5dd44d3ae93c2a433d3491 100644 (file)
@@ -51,7 +51,9 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw)
                case I40E_DEV_ID_QSFP_B:
                case I40E_DEV_ID_QSFP_C:
                case I40E_DEV_ID_10G_BASE_T:
+               case I40E_DEV_ID_10G_BASE_T4:
                case I40E_DEV_ID_20G_KR2:
+               case I40E_DEV_ID_20G_KR2_A:
                        hw->mac.type = I40E_MAC_XL710;
                        break;
                case I40E_DEV_ID_SFP_X722:
@@ -85,7 +87,7 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw)
  * @hw: pointer to the HW structure
  * @aq_err: the AQ error code to convert
  **/
-char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err)
+const char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err)
 {
        switch (aq_err) {
        case I40E_AQ_RC_OK:
@@ -145,7 +147,7 @@ char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err)
  * @hw: pointer to the HW structure
  * @stat_err: the status error code to convert
  **/
-char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err)
+const char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err)
 {
        switch (stat_err) {
        case 0:
@@ -329,25 +331,11 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc,
                        len = buf_len;
                /* write the full 16-byte chunks */
                for (i = 0; i < (len - 16); i += 16)
-                       i40e_debug(hw, mask,
-                                  "\t0x%04X  %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
-                                  i, buf[i], buf[i + 1], buf[i + 2],
-                                  buf[i + 3], buf[i + 4], buf[i + 5],
-                                  buf[i + 6], buf[i + 7], buf[i + 8],
-                                  buf[i + 9], buf[i + 10], buf[i + 11],
-                                  buf[i + 12], buf[i + 13], buf[i + 14],
-                                  buf[i + 15]);
+                       i40e_debug(hw, mask, "\t0x%04X  %16ph\n", i, buf + i);
                /* write whatever's left over without overrunning the buffer */
-               if (i < len) {
-                       char d_buf[80];
-                       int j = 0;
-
-                       memset(d_buf, 0, sizeof(d_buf));
-                       j += sprintf(d_buf, "\t0x%04X ", i);
-                       while (i < len)
-                               j += sprintf(&d_buf[j], " %02X", buf[i++]);
-                       i40e_debug(hw, mask, "%s\n", d_buf);
-               }
+               if (i < len)
+                       i40e_debug(hw, mask, "\t0x%04X  %*ph\n",
+                                            i, len - i, buf + i);
        }
 }
 
@@ -441,9 +429,6 @@ static i40e_status i40e_aq_get_set_rss_lut(struct i40e_hw *hw,
                                        I40E_AQC_SET_RSS_LUT_TABLE_TYPE_SHIFT) &
                                        I40E_AQC_SET_RSS_LUT_TABLE_TYPE_MASK));
 
-       cmd_resp->addr_high = cpu_to_le32(high_16_bits((u64)lut));
-       cmd_resp->addr_low = cpu_to_le32(lower_32_bits((u64)lut));
-
        status = i40e_asq_send_command(hw, &desc, lut, lut_size, NULL);
 
        return status;
@@ -518,8 +503,6 @@ static i40e_status i40e_aq_get_set_rss_key(struct i40e_hw *hw,
                                          I40E_AQC_SET_RSS_KEY_VSI_ID_SHIFT) &
                                          I40E_AQC_SET_RSS_KEY_VSI_ID_MASK));
        cmd_resp->vsi_id |= cpu_to_le16((u16)I40E_AQC_SET_RSS_KEY_VSI_VALID);
-       cmd_resp->addr_high = cpu_to_le32(high_16_bits((u64)key));
-       cmd_resp->addr_low = cpu_to_le32(lower_32_bits((u64)key));
 
        status = i40e_asq_send_command(hw, &desc, key, key_size, NULL);
 
@@ -961,6 +944,9 @@ i40e_status i40e_init_shared_code(struct i40e_hw *hw)
        else
                hw->pf_id = (u8)(func_rid & 0x7);
 
+       if (hw->mac.type == I40E_MAC_X722)
+               hw->flags |= I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE;
+
        status = i40e_init_nvm(hw);
        return status;
 }
@@ -1038,7 +1024,7 @@ i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr)
        status = i40e_aq_mac_address_read(hw, &flags, &addrs, NULL);
 
        if (flags & I40E_AQC_LAN_ADDR_VALID)
-               memcpy(mac_addr, &addrs.pf_lan_mac, sizeof(addrs.pf_lan_mac));
+               ether_addr_copy(mac_addr, addrs.pf_lan_mac);
 
        return status;
 }
@@ -1061,7 +1047,7 @@ i40e_status i40e_get_port_mac_addr(struct i40e_hw *hw, u8 *mac_addr)
                return status;
 
        if (flags & I40E_AQC_PORT_ADDR_VALID)
-               memcpy(mac_addr, &addrs.port_mac, sizeof(addrs.port_mac));
+               ether_addr_copy(mac_addr, addrs.port_mac);
        else
                status = I40E_ERR_INVALID_MAC_ADDR;
 
@@ -1119,7 +1105,7 @@ i40e_status i40e_get_san_mac_addr(struct i40e_hw *hw, u8 *mac_addr)
                return status;
 
        if (flags & I40E_AQC_SAN_ADDR_VALID)
-               memcpy(mac_addr, &addrs.pf_san_mac, sizeof(addrs.pf_san_mac));
+               ether_addr_copy(mac_addr, addrs.pf_san_mac);
        else
                status = I40E_ERR_INVALID_MAC_ADDR;
 
@@ -1260,7 +1246,7 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw)
        grst_del = (rd32(hw, I40E_GLGEN_RSTCTL) &
                    I40E_GLGEN_RSTCTL_GRSTDEL_MASK) >>
                    I40E_GLGEN_RSTCTL_GRSTDEL_SHIFT;
-       for (cnt = 0; cnt < grst_del + 2; cnt++) {
+       for (cnt = 0; cnt < grst_del + 10; cnt++) {
                reg = rd32(hw, I40E_GLGEN_RSTAT);
                if (!(reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK))
                        break;
@@ -1620,6 +1606,9 @@ i40e_status i40e_aq_get_phy_capabilities(struct i40e_hw *hw,
        if (hw->aq.asq_last_status == I40E_AQ_RC_EIO)
                status = I40E_ERR_UNKNOWN_PHY;
 
+       if (report_init)
+               hw->phy.phy_types = le32_to_cpu(abilities->phy_type);
+
        return status;
 }
 
@@ -1720,14 +1709,14 @@ enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures,
                        *aq_failures |= I40E_SET_FC_AQ_FAIL_SET;
        }
        /* Update the link info */
-       status = i40e_aq_get_link_info(hw, true, NULL, NULL);
+       status = i40e_update_link_info(hw);
        if (status) {
                /* Wait a little bit (on 40G cards it sometimes takes a really
                 * long time for link to come back from the atomic reset)
                 * and try once more
                 */
                msleep(1000);
-               status = i40e_aq_get_link_info(hw, true, NULL, NULL);
+               status = i40e_update_link_info(hw);
        }
        if (status)
                *aq_failures |= I40E_SET_FC_AQ_FAIL_UPDATE;
@@ -2238,27 +2227,54 @@ i40e_status i40e_aq_send_driver_version(struct i40e_hw *hw,
 /**
  * i40e_get_link_status - get status of the HW network link
  * @hw: pointer to the hw struct
+ * @link_up: pointer to bool (true/false = linkup/linkdown)
  *
- * Returns true if link is up, false if link is down.
+ * Variable link_up true if link is up, false if link is down.
+ * The variable link_up is invalid if returned value of status != 0
  *
  * Side effect: LinkStatusEvent reporting becomes enabled
  **/
-bool i40e_get_link_status(struct i40e_hw *hw)
+i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up)
 {
        i40e_status status = 0;
-       bool link_status = false;
 
        if (hw->phy.get_link_info) {
-               status = i40e_aq_get_link_info(hw, true, NULL, NULL);
+               status = i40e_update_link_info(hw);
 
                if (status)
-                       goto i40e_get_link_status_exit;
+                       i40e_debug(hw, I40E_DEBUG_LINK, "get link failed: status %d\n",
+                                  status);
        }
 
-       link_status = hw->phy.link_info.link_info & I40E_AQ_LINK_UP;
+       *link_up = hw->phy.link_info.link_info & I40E_AQ_LINK_UP;
+
+       return status;
+}
+
+/**
+ * i40e_updatelink_status - update status of the HW network link
+ * @hw: pointer to the hw struct
+ **/
+i40e_status i40e_update_link_info(struct i40e_hw *hw)
+{
+       struct i40e_aq_get_phy_abilities_resp abilities;
+       i40e_status status = 0;
+
+       status = i40e_aq_get_link_info(hw, true, NULL, NULL);
+       if (status)
+               return status;
+
+       if (hw->phy.link_info.link_info & I40E_AQ_MEDIA_AVAILABLE) {
+               status = i40e_aq_get_phy_capabilities(hw, false, false,
+                                                     &abilities, NULL);
+               if (status)
+                       return status;
 
-i40e_get_link_status_exit:
-       return link_status;
+               memcpy(hw->phy.link_info.module_type, &abilities.module_type,
+                      sizeof(hw->phy.link_info.module_type));
+       }
+
+       return status;
 }
 
 /**
@@ -2365,6 +2381,7 @@ i40e_status i40e_aq_get_veb_parameters(struct i40e_hw *hw,
                *vebs_free = le16_to_cpu(cmd_resp->vebs_free);
        if (floating) {
                u16 flags = le16_to_cpu(cmd_resp->veb_flags);
+
                if (flags & I40E_AQC_ADD_VEB_FLOATING)
                        *floating = true;
                else
@@ -3779,7 +3796,7 @@ i40e_status i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw,
        }
 
        if (mac_addr)
-               memcpy(cmd->mac, mac_addr, ETH_ALEN);
+               ether_addr_copy(cmd->mac, mac_addr);
 
        cmd->etype = cpu_to_le16(ethtype);
        cmd->flags = cpu_to_le16(flags);
@@ -3797,6 +3814,28 @@ i40e_status i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw,
        return status;
 }
 
+/**
+ * i40e_add_filter_to_drop_tx_flow_control_frames- filter to drop flow control
+ * @hw: pointer to the hw struct
+ * @seid: VSI seid to add ethertype filter from
+ **/
+#define I40E_FLOW_CONTROL_ETHTYPE 0x8808
+void i40e_add_filter_to_drop_tx_flow_control_frames(struct i40e_hw *hw,
+                                                   u16 seid)
+{
+       u16 flag = I40E_AQC_ADD_CONTROL_PACKET_FLAGS_IGNORE_MAC |
+                  I40E_AQC_ADD_CONTROL_PACKET_FLAGS_DROP |
+                  I40E_AQC_ADD_CONTROL_PACKET_FLAGS_TX;
+       u16 ethtype = I40E_FLOW_CONTROL_ETHTYPE;
+       i40e_status status;
+
+       status = i40e_aq_add_rem_control_packet_filter(hw, NULL, ethtype, flag,
+                                                      seid, 0, true, NULL,
+                                                      NULL);
+       if (status)
+               hw_dbg(hw, "Ethtype Filter Add failed: Error pruning Tx flow control frames\n");
+}
+
 /**
  * i40e_aq_alternate_read
  * @hw: pointer to the hardware structure
index 90de46aef557927e283b81a6ca2674af764d7b06..2691277c0055d2572f2994e24c120066a7bec28b 100644 (file)
@@ -291,6 +291,190 @@ static void i40e_parse_ieee_tlv(struct i40e_lldp_org_tlv *tlv,
        }
 }
 
+/**
+ * i40e_parse_cee_pgcfg_tlv
+ * @tlv: CEE DCBX PG CFG TLV
+ * @dcbcfg: Local store to update ETS CFG data
+ *
+ * Parses CEE DCBX PG CFG TLV
+ **/
+static void i40e_parse_cee_pgcfg_tlv(struct i40e_cee_feat_tlv *tlv,
+                                    struct i40e_dcbx_config *dcbcfg)
+{
+       struct i40e_dcb_ets_config *etscfg;
+       u8 *buf = tlv->tlvinfo;
+       u16 offset = 0;
+       u8 priority;
+       int i;
+
+       etscfg = &dcbcfg->etscfg;
+
+       if (tlv->en_will_err & I40E_CEE_FEAT_TLV_WILLING_MASK)
+               etscfg->willing = 1;
+
+       etscfg->cbs = 0;
+       /* Priority Group Table (4 octets)
+        * Octets:|    1    |    2    |    3    |    4    |
+        *        -----------------------------------------
+        *        |pri0|pri1|pri2|pri3|pri4|pri5|pri6|pri7|
+        *        -----------------------------------------
+        *   Bits:|7  4|3  0|7  4|3  0|7  4|3  0|7  4|3  0|
+        *        -----------------------------------------
+        */
+       for (i = 0; i < 4; i++) {
+               priority = (u8)((buf[offset] & I40E_CEE_PGID_PRIO_1_MASK) >>
+                                I40E_CEE_PGID_PRIO_1_SHIFT);
+               etscfg->prioritytable[i * 2] =  priority;
+               priority = (u8)((buf[offset] & I40E_CEE_PGID_PRIO_0_MASK) >>
+                                I40E_CEE_PGID_PRIO_0_SHIFT);
+               etscfg->prioritytable[i * 2 + 1] = priority;
+               offset++;
+       }
+
+       /* PG Percentage Table (8 octets)
+        * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
+        *        ---------------------------------
+        *        |pg0|pg1|pg2|pg3|pg4|pg5|pg6|pg7|
+        *        ---------------------------------
+        */
+       for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
+               etscfg->tcbwtable[i] = buf[offset++];
+
+       /* Number of TCs supported (1 octet) */
+       etscfg->maxtcs = buf[offset];
+}
+
+/**
+ * i40e_parse_cee_pfccfg_tlv
+ * @tlv: CEE DCBX PFC CFG TLV
+ * @dcbcfg: Local store to update PFC CFG data
+ *
+ * Parses CEE DCBX PFC CFG TLV
+ **/
+static void i40e_parse_cee_pfccfg_tlv(struct i40e_cee_feat_tlv *tlv,
+                                     struct i40e_dcbx_config *dcbcfg)
+{
+       u8 *buf = tlv->tlvinfo;
+
+       if (tlv->en_will_err & I40E_CEE_FEAT_TLV_WILLING_MASK)
+               dcbcfg->pfc.willing = 1;
+
+       /* ------------------------
+        * | PFC Enable | PFC TCs |
+        * ------------------------
+        * | 1 octet    | 1 octet |
+        */
+       dcbcfg->pfc.pfcenable = buf[0];
+       dcbcfg->pfc.pfccap = buf[1];
+}
+
+/**
+ * i40e_parse_cee_app_tlv
+ * @tlv: CEE DCBX APP TLV
+ * @dcbcfg: Local store to update APP PRIO data
+ *
+ * Parses CEE DCBX APP PRIO TLV
+ **/
+static void i40e_parse_cee_app_tlv(struct i40e_cee_feat_tlv *tlv,
+                                  struct i40e_dcbx_config *dcbcfg)
+{
+       u16 length, typelength, offset = 0;
+       struct i40e_cee_app_prio *app;
+       u8 i, up, selector;
+
+       typelength = ntohs(tlv->hdr.typelen);
+       length = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >>
+                      I40E_LLDP_TLV_LEN_SHIFT);
+
+       dcbcfg->numapps = length / sizeof(*app);
+       if (!dcbcfg->numapps)
+               return;
+
+       for (i = 0; i < dcbcfg->numapps; i++) {
+               app = (struct i40e_cee_app_prio *)(tlv->tlvinfo + offset);
+               for (up = 0; up < I40E_MAX_USER_PRIORITY; up++) {
+                       if (app->prio_map & BIT(up))
+                               break;
+               }
+               dcbcfg->app[i].priority = up;
+
+               /* Get Selector from lower 2 bits, and convert to IEEE */
+               selector = (app->upper_oui_sel & I40E_CEE_APP_SELECTOR_MASK);
+               if (selector == I40E_CEE_APP_SEL_ETHTYPE)
+                       dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE;
+               else if (selector == I40E_CEE_APP_SEL_TCPIP)
+                       dcbcfg->app[i].selector = I40E_APP_SEL_TCPIP;
+               else
+                       /* Keep selector as it is for unknown types */
+                       dcbcfg->app[i].selector = selector;
+
+               dcbcfg->app[i].protocolid = ntohs(app->protocol);
+               /* Move to next app */
+               offset += sizeof(*app);
+       }
+}
+
+/**
+ * i40e_parse_cee_tlv
+ * @tlv: CEE DCBX TLV
+ * @dcbcfg: Local store to update DCBX config data
+ *
+ * Get the TLV subtype and send it to parsing function
+ * based on the subtype value
+ **/
+static void i40e_parse_cee_tlv(struct i40e_lldp_org_tlv *tlv,
+                              struct i40e_dcbx_config *dcbcfg)
+{
+       u16 len, tlvlen, sublen, typelength;
+       struct i40e_cee_feat_tlv *sub_tlv;
+       u8 subtype, feat_tlv_count = 0;
+       u32 ouisubtype;
+
+       ouisubtype = ntohl(tlv->ouisubtype);
+       subtype = (u8)((ouisubtype & I40E_LLDP_TLV_SUBTYPE_MASK) >>
+                      I40E_LLDP_TLV_SUBTYPE_SHIFT);
+       /* Return if not CEE DCBX */
+       if (subtype != I40E_CEE_DCBX_TYPE)
+               return;
+
+       typelength = ntohs(tlv->typelength);
+       tlvlen = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >>
+                       I40E_LLDP_TLV_LEN_SHIFT);
+       len = sizeof(tlv->typelength) + sizeof(ouisubtype) +
+             sizeof(struct i40e_cee_ctrl_tlv);
+       /* Return if no CEE DCBX Feature TLVs */
+       if (tlvlen <= len)
+               return;
+
+       sub_tlv = (struct i40e_cee_feat_tlv *)((char *)tlv + len);
+       while (feat_tlv_count < I40E_CEE_MAX_FEAT_TYPE) {
+               typelength = ntohs(sub_tlv->hdr.typelen);
+               sublen = (u16)((typelength &
+                               I40E_LLDP_TLV_LEN_MASK) >>
+                               I40E_LLDP_TLV_LEN_SHIFT);
+               subtype = (u8)((typelength & I40E_LLDP_TLV_TYPE_MASK) >>
+                               I40E_LLDP_TLV_TYPE_SHIFT);
+               switch (subtype) {
+               case I40E_CEE_SUBTYPE_PG_CFG:
+                       i40e_parse_cee_pgcfg_tlv(sub_tlv, dcbcfg);
+                       break;
+               case I40E_CEE_SUBTYPE_PFC_CFG:
+                       i40e_parse_cee_pfccfg_tlv(sub_tlv, dcbcfg);
+                       break;
+               case I40E_CEE_SUBTYPE_APP_PRI:
+                       i40e_parse_cee_app_tlv(sub_tlv, dcbcfg);
+                       break;
+               default:
+                       return; /* Invalid Sub-type return */
+               }
+               feat_tlv_count++;
+               /* Move to next sub TLV */
+               sub_tlv = (struct i40e_cee_feat_tlv *)((char *)sub_tlv +
+                                               sizeof(sub_tlv->hdr.typelen) +
+                                               sublen);
+       }
+}
+
 /**
  * i40e_parse_org_tlv
  * @tlv: Organization specific TLV
@@ -312,6 +496,9 @@ static void i40e_parse_org_tlv(struct i40e_lldp_org_tlv *tlv,
        case I40E_IEEE_8021QAZ_OUI:
                i40e_parse_ieee_tlv(tlv, dcbcfg);
                break;
+       case I40E_CEE_DCBX_OUI:
+               i40e_parse_cee_tlv(tlv, dcbcfg);
+               break;
        default:
                break;
        }
@@ -502,15 +689,18 @@ static void i40e_cee_to_dcb_config(
        /* CEE PG data to ETS config */
        dcbcfg->etscfg.maxtcs = cee_cfg->oper_num_tc;
 
+       /* Note that the FW creates the oper_prio_tc nibbles reversed
+        * from those in the CEE Priority Group sub-TLV.
+        */
        for (i = 0; i < 4; i++) {
-               tc = (u8)((cee_cfg->oper_prio_tc[i] &
-                        I40E_CEE_PGID_PRIO_1_MASK) >>
-                        I40E_CEE_PGID_PRIO_1_SHIFT);
-               dcbcfg->etscfg.prioritytable[i*2] =  tc;
                tc = (u8)((cee_cfg->oper_prio_tc[i] &
                         I40E_CEE_PGID_PRIO_0_MASK) >>
                         I40E_CEE_PGID_PRIO_0_SHIFT);
-               dcbcfg->etscfg.prioritytable[i*2 + 1] = tc;
+               dcbcfg->etscfg.prioritytable[i * 2] =  tc;
+               tc = (u8)((cee_cfg->oper_prio_tc[i] &
+                        I40E_CEE_PGID_PRIO_1_MASK) >>
+                        I40E_CEE_PGID_PRIO_1_SHIFT);
+               dcbcfg->etscfg.prioritytable[i * 2 + 1] = tc;
        }
 
        for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
@@ -531,37 +721,85 @@ static void i40e_cee_to_dcb_config(
        dcbcfg->pfc.pfcenable = cee_cfg->oper_pfc_en;
        dcbcfg->pfc.pfccap = I40E_MAX_TRAFFIC_CLASS;
 
-       status = (tlv_status & I40E_AQC_CEE_APP_STATUS_MASK) >>
-                 I40E_AQC_CEE_APP_STATUS_SHIFT;
+       i = 0;
+       status = (tlv_status & I40E_AQC_CEE_FCOE_STATUS_MASK) >>
+                 I40E_AQC_CEE_FCOE_STATUS_SHIFT;
        err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0;
        sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0;
        oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0;
-       /* Add APPs if Error is False and Oper/Sync is True */
+       /* Add FCoE APP if Error is False and Oper/Sync is True */
        if (!err && sync && oper) {
-               /* CEE operating configuration supports FCoE/iSCSI/FIP only */
-               dcbcfg->numapps = I40E_CEE_OPER_MAX_APPS;
-
                /* FCoE APP */
-               dcbcfg->app[0].priority =
+               dcbcfg->app[i].priority =
                        (app_prio & I40E_AQC_CEE_APP_FCOE_MASK) >>
                         I40E_AQC_CEE_APP_FCOE_SHIFT;
-               dcbcfg->app[0].selector = I40E_APP_SEL_ETHTYPE;
-               dcbcfg->app[0].protocolid = I40E_APP_PROTOID_FCOE;
+               dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE;
+               dcbcfg->app[i].protocolid = I40E_APP_PROTOID_FCOE;
+               i++;
+       }
 
+       status = (tlv_status & I40E_AQC_CEE_ISCSI_STATUS_MASK) >>
+                 I40E_AQC_CEE_ISCSI_STATUS_SHIFT;
+       err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0;
+       sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0;
+       oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0;
+       /* Add iSCSI APP if Error is False and Oper/Sync is True */
+       if (!err && sync && oper) {
                /* iSCSI APP */
-               dcbcfg->app[1].priority =
+               dcbcfg->app[i].priority =
                        (app_prio & I40E_AQC_CEE_APP_ISCSI_MASK) >>
                         I40E_AQC_CEE_APP_ISCSI_SHIFT;
-               dcbcfg->app[1].selector = I40E_APP_SEL_TCPIP;
-               dcbcfg->app[1].protocolid = I40E_APP_PROTOID_ISCSI;
+               dcbcfg->app[i].selector = I40E_APP_SEL_TCPIP;
+               dcbcfg->app[i].protocolid = I40E_APP_PROTOID_ISCSI;
+               i++;
+       }
 
+       status = (tlv_status & I40E_AQC_CEE_FIP_STATUS_MASK) >>
+                 I40E_AQC_CEE_FIP_STATUS_SHIFT;
+       err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0;
+       sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0;
+       oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0;
+       /* Add FIP APP if Error is False and Oper/Sync is True */
+       if (!err && sync && oper) {
                /* FIP APP */
-               dcbcfg->app[2].priority =
+               dcbcfg->app[i].priority =
                        (app_prio & I40E_AQC_CEE_APP_FIP_MASK) >>
                         I40E_AQC_CEE_APP_FIP_SHIFT;
-               dcbcfg->app[2].selector = I40E_APP_SEL_ETHTYPE;
-               dcbcfg->app[2].protocolid = I40E_APP_PROTOID_FIP;
+               dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE;
+               dcbcfg->app[i].protocolid = I40E_APP_PROTOID_FIP;
+               i++;
        }
+       dcbcfg->numapps = i;
+}
+
+/**
+ * i40e_get_ieee_dcb_config
+ * @hw: pointer to the hw struct
+ *
+ * Get IEEE mode DCB configuration from the Firmware
+ **/
+static i40e_status i40e_get_ieee_dcb_config(struct i40e_hw *hw)
+{
+       i40e_status ret = 0;
+
+       /* IEEE mode */
+       hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_IEEE;
+       /* Get Local DCB Config */
+       ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_LOCAL, 0,
+                                    &hw->local_dcbx_config);
+       if (ret)
+               goto out;
+
+       /* Get Remote DCB Config */
+       ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE,
+                                    I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE,
+                                    &hw->remote_dcbx_config);
+       /* Don't treat ENOENT as an error for Remote MIBs */
+       if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT)
+               ret = 0;
+
+out:
+       return ret;
 }
 
 /**
@@ -579,7 +817,7 @@ i40e_status i40e_get_dcb_config(struct i40e_hw *hw)
        /* If Firmware version < v4.33 IEEE only */
        if (((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver < 33)) ||
            (hw->aq.fw_maj_ver < 4))
-               goto ieee;
+               return i40e_get_ieee_dcb_config(hw);
 
        /* If Firmware version == v4.33 use old CEE struct */
        if ((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver == 33)) {
@@ -608,16 +846,14 @@ i40e_status i40e_get_dcb_config(struct i40e_hw *hw)
 
        /* CEE mode not enabled try querying IEEE data */
        if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT)
-               goto ieee;
-       else
+               return i40e_get_ieee_dcb_config(hw);
+
+       if (ret)
                goto out;
 
-ieee:
-       /* IEEE mode */
-       hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_IEEE;
-       /* Get Local DCB Config */
+       /* Get CEE DCB Desired Config */
        ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_LOCAL, 0,
-                                    &hw->local_dcbx_config);
+                                    &hw->desired_dcbx_config);
        if (ret)
                goto out;
 
index 50fc894a4cde3b78fa0b9bbc5edf8564532bbd29..92d01042c1f6f91b96561f4a22616f4015bf803e 100644 (file)
 #define I40E_IEEE_SUBTYPE_PFC_CFG      11
 #define I40E_IEEE_SUBTYPE_APP_PRI      12
 
+#define I40E_CEE_DCBX_OUI              0x001b21
+#define I40E_CEE_DCBX_TYPE             2
+
+#define I40E_CEE_SUBTYPE_CTRL          1
+#define I40E_CEE_SUBTYPE_PG_CFG                2
+#define I40E_CEE_SUBTYPE_PFC_CFG       3
+#define I40E_CEE_SUBTYPE_APP_PRI       4
+
+#define I40E_CEE_MAX_FEAT_TYPE         3
 /* Defines for LLDP TLV header */
 #define I40E_LLDP_TLV_LEN_SHIFT                0
 #define I40E_LLDP_TLV_LEN_MASK         (0x01FF << I40E_LLDP_TLV_LEN_SHIFT)
@@ -98,6 +107,36 @@ struct i40e_lldp_org_tlv {
        __be32 ouisubtype;
        u8 tlvinfo[1];
 };
+
+struct i40e_cee_tlv_hdr {
+       __be16 typelen;
+       u8 operver;
+       u8 maxver;
+};
+
+struct i40e_cee_ctrl_tlv {
+       struct i40e_cee_tlv_hdr hdr;
+       __be32 seqno;
+       __be32 ackno;
+};
+
+struct i40e_cee_feat_tlv {
+       struct i40e_cee_tlv_hdr hdr;
+       u8 en_will_err; /* Bits: |En|Will|Err|Reserved(5)| */
+#define I40E_CEE_FEAT_TLV_ENABLE_MASK  0x80
+#define I40E_CEE_FEAT_TLV_WILLING_MASK 0x40
+#define I40E_CEE_FEAT_TLV_ERR_MASK     0x20
+       u8 subtype;
+       u8 tlvinfo[1];
+};
+
+struct i40e_cee_app_prio {
+       __be16 protocol;
+       u8 upper_oui_sel; /* Bits: |Upper OUI(6)|Selector(2)| */
+#define I40E_CEE_APP_SELECTOR_MASK     0x03
+       __be16 lower_oui;
+       u8 prio_map;
+};
 #pragma pack()
 
 i40e_status i40e_get_dcbx_status(struct i40e_hw *hw,
index 1c51f736a8d0ab54bba24fa8160fddd1f1d93174..886e667f2f1c8a54c4a48586e10626f7ce3accf5 100644 (file)
@@ -236,14 +236,13 @@ static void i40e_dcbnl_del_app(struct i40e_pf *pf,
                               struct i40e_dcb_app_priority_table *app)
 {
        int v, err;
+
        for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v] && pf->vsi[v]->netdev) {
                        err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app);
-                       if (err)
-                               dev_info(&pf->pdev->dev, "%s: Failed deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n",
-                                        __func__, pf->vsi[v]->seid,
-                                        err, app->selector,
-                                        app->protocolid, app->priority);
+                       dev_dbg(&pf->pdev->dev, "Deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n",
+                               pf->vsi[v]->seid, err, app->selector,
+                               app->protocolid, app->priority);
                }
        }
 }
index d7c15d17faa634c1cb901fc360619e47436dfb39..d4b7af9a2fc82f8b55878188a0c53a5911d6ddf1 100644 (file)
@@ -404,82 +404,82 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
        nstat = i40e_get_vsi_stats_struct(vsi);
        dev_info(&pf->pdev->dev,
                 "    net_stats: rx_packets = %lu, rx_bytes = %lu, rx_errors = %lu, rx_dropped = %lu\n",
-                (long unsigned int)nstat->rx_packets,
-                (long unsigned int)nstat->rx_bytes,
-                (long unsigned int)nstat->rx_errors,
-                (long unsigned int)nstat->rx_dropped);
+                (unsigned long int)nstat->rx_packets,
+                (unsigned long int)nstat->rx_bytes,
+                (unsigned long int)nstat->rx_errors,
+                (unsigned long int)nstat->rx_dropped);
        dev_info(&pf->pdev->dev,
                 "    net_stats: tx_packets = %lu, tx_bytes = %lu, tx_errors = %lu, tx_dropped = %lu\n",
-                (long unsigned int)nstat->tx_packets,
-                (long unsigned int)nstat->tx_bytes,
-                (long unsigned int)nstat->tx_errors,
-                (long unsigned int)nstat->tx_dropped);
+                (unsigned long int)nstat->tx_packets,
+                (unsigned long int)nstat->tx_bytes,
+                (unsigned long int)nstat->tx_errors,
+                (unsigned long int)nstat->tx_dropped);
        dev_info(&pf->pdev->dev,
                 "    net_stats: multicast = %lu, collisions = %lu\n",
-                (long unsigned int)nstat->multicast,
-                (long unsigned int)nstat->collisions);
+                (unsigned long int)nstat->multicast,
+                (unsigned long int)nstat->collisions);
        dev_info(&pf->pdev->dev,
                 "    net_stats: rx_length_errors = %lu, rx_over_errors = %lu, rx_crc_errors = %lu\n",
-                (long unsigned int)nstat->rx_length_errors,
-                (long unsigned int)nstat->rx_over_errors,
-                (long unsigned int)nstat->rx_crc_errors);
+                (unsigned long int)nstat->rx_length_errors,
+                (unsigned long int)nstat->rx_over_errors,
+                (unsigned long int)nstat->rx_crc_errors);
        dev_info(&pf->pdev->dev,
                 "    net_stats: rx_frame_errors = %lu, rx_fifo_errors = %lu, rx_missed_errors = %lu\n",
-                (long unsigned int)nstat->rx_frame_errors,
-                (long unsigned int)nstat->rx_fifo_errors,
-                (long unsigned int)nstat->rx_missed_errors);
+                (unsigned long int)nstat->rx_frame_errors,
+                (unsigned long int)nstat->rx_fifo_errors,
+                (unsigned long int)nstat->rx_missed_errors);
        dev_info(&pf->pdev->dev,
                 "    net_stats: tx_aborted_errors = %lu, tx_carrier_errors = %lu, tx_fifo_errors = %lu\n",
-                (long unsigned int)nstat->tx_aborted_errors,
-                (long unsigned int)nstat->tx_carrier_errors,
-                (long unsigned int)nstat->tx_fifo_errors);
+                (unsigned long int)nstat->tx_aborted_errors,
+                (unsigned long int)nstat->tx_carrier_errors,
+                (unsigned long int)nstat->tx_fifo_errors);
        dev_info(&pf->pdev->dev,
                 "    net_stats: tx_heartbeat_errors = %lu, tx_window_errors = %lu\n",
-                (long unsigned int)nstat->tx_heartbeat_errors,
-                (long unsigned int)nstat->tx_window_errors);
+                (unsigned long int)nstat->tx_heartbeat_errors,
+                (unsigned long int)nstat->tx_window_errors);
        dev_info(&pf->pdev->dev,
                 "    net_stats: rx_compressed = %lu, tx_compressed = %lu\n",
-                (long unsigned int)nstat->rx_compressed,
-                (long unsigned int)nstat->tx_compressed);
+                (unsigned long int)nstat->rx_compressed,
+                (unsigned long int)nstat->tx_compressed);
        dev_info(&pf->pdev->dev,
                 "    net_stats_offsets: rx_packets = %lu, rx_bytes = %lu, rx_errors = %lu, rx_dropped = %lu\n",
-                (long unsigned int)vsi->net_stats_offsets.rx_packets,
-                (long unsigned int)vsi->net_stats_offsets.rx_bytes,
-                (long unsigned int)vsi->net_stats_offsets.rx_errors,
-                (long unsigned int)vsi->net_stats_offsets.rx_dropped);
+                (unsigned long int)vsi->net_stats_offsets.rx_packets,
+                (unsigned long int)vsi->net_stats_offsets.rx_bytes,
+                (unsigned long int)vsi->net_stats_offsets.rx_errors,
+                (unsigned long int)vsi->net_stats_offsets.rx_dropped);
        dev_info(&pf->pdev->dev,
                 "    net_stats_offsets: tx_packets = %lu, tx_bytes = %lu, tx_errors = %lu, tx_dropped = %lu\n",
-                (long unsigned int)vsi->net_stats_offsets.tx_packets,
-                (long unsigned int)vsi->net_stats_offsets.tx_bytes,
-                (long unsigned int)vsi->net_stats_offsets.tx_errors,
-                (long unsigned int)vsi->net_stats_offsets.tx_dropped);
+                (unsigned long int)vsi->net_stats_offsets.tx_packets,
+                (unsigned long int)vsi->net_stats_offsets.tx_bytes,
+                (unsigned long int)vsi->net_stats_offsets.tx_errors,
+                (unsigned long int)vsi->net_stats_offsets.tx_dropped);
        dev_info(&pf->pdev->dev,
                 "    net_stats_offsets: multicast = %lu, collisions = %lu\n",
-                (long unsigned int)vsi->net_stats_offsets.multicast,
-                (long unsigned int)vsi->net_stats_offsets.collisions);
+                (unsigned long int)vsi->net_stats_offsets.multicast,
+                (unsigned long int)vsi->net_stats_offsets.collisions);
        dev_info(&pf->pdev->dev,
                 "    net_stats_offsets: rx_length_errors = %lu, rx_over_errors = %lu, rx_crc_errors = %lu\n",
-                (long unsigned int)vsi->net_stats_offsets.rx_length_errors,
-                (long unsigned int)vsi->net_stats_offsets.rx_over_errors,
-                (long unsigned int)vsi->net_stats_offsets.rx_crc_errors);
+                (unsigned long int)vsi->net_stats_offsets.rx_length_errors,
+                (unsigned long int)vsi->net_stats_offsets.rx_over_errors,
+                (unsigned long int)vsi->net_stats_offsets.rx_crc_errors);
        dev_info(&pf->pdev->dev,
                 "    net_stats_offsets: rx_frame_errors = %lu, rx_fifo_errors = %lu, rx_missed_errors = %lu\n",
-                (long unsigned int)vsi->net_stats_offsets.rx_frame_errors,
-                (long unsigned int)vsi->net_stats_offsets.rx_fifo_errors,
-                (long unsigned int)vsi->net_stats_offsets.rx_missed_errors);
+                (unsigned long int)vsi->net_stats_offsets.rx_frame_errors,
+                (unsigned long int)vsi->net_stats_offsets.rx_fifo_errors,
+                (unsigned long int)vsi->net_stats_offsets.rx_missed_errors);
        dev_info(&pf->pdev->dev,
                 "    net_stats_offsets: tx_aborted_errors = %lu, tx_carrier_errors = %lu, tx_fifo_errors = %lu\n",
-                (long unsigned int)vsi->net_stats_offsets.tx_aborted_errors,
-                (long unsigned int)vsi->net_stats_offsets.tx_carrier_errors,
-                (long unsigned int)vsi->net_stats_offsets.tx_fifo_errors);
+                (unsigned long int)vsi->net_stats_offsets.tx_aborted_errors,
+                (unsigned long int)vsi->net_stats_offsets.tx_carrier_errors,
+                (unsigned long int)vsi->net_stats_offsets.tx_fifo_errors);
        dev_info(&pf->pdev->dev,
                 "    net_stats_offsets: tx_heartbeat_errors = %lu, tx_window_errors = %lu\n",
-                (long unsigned int)vsi->net_stats_offsets.tx_heartbeat_errors,
-                (long unsigned int)vsi->net_stats_offsets.tx_window_errors);
+                (unsigned long int)vsi->net_stats_offsets.tx_heartbeat_errors,
+                (unsigned long int)vsi->net_stats_offsets.tx_window_errors);
        dev_info(&pf->pdev->dev,
                 "    net_stats_offsets: rx_compressed = %lu, tx_compressed = %lu\n",
-                (long unsigned int)vsi->net_stats_offsets.rx_compressed,
-                (long unsigned int)vsi->net_stats_offsets.tx_compressed);
+                (unsigned long int)vsi->net_stats_offsets.rx_compressed,
+                (unsigned long int)vsi->net_stats_offsets.tx_compressed);
        dev_info(&pf->pdev->dev,
                 "    tx_restart = %d, tx_busy = %d, rx_buf_failed = %d, rx_page_failed = %d\n",
                 vsi->tx_restart, vsi->tx_busy,
@@ -487,6 +487,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
        rcu_read_lock();
        for (i = 0; i < vsi->num_queue_pairs; i++) {
                struct i40e_ring *rx_ring = ACCESS_ONCE(vsi->rx_rings[i]);
+
                if (!rx_ring)
                        continue;
 
@@ -527,7 +528,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
                dev_info(&pf->pdev->dev,
                         "    rx_rings[%i]: size = %i, dma = 0x%08lx\n",
                         i, rx_ring->size,
-                        (long unsigned int)rx_ring->dma);
+                        (unsigned long int)rx_ring->dma);
                dev_info(&pf->pdev->dev,
                         "    rx_rings[%i]: vsi = %p, q_vector = %p\n",
                         i, rx_ring->vsi,
@@ -535,6 +536,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
        }
        for (i = 0; i < vsi->num_queue_pairs; i++) {
                struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[i]);
+
                if (!tx_ring)
                        continue;
 
@@ -573,7 +575,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
                dev_info(&pf->pdev->dev,
                         "    tx_rings[%i]: size = %i, dma = 0x%08lx\n",
                         i, tx_ring->size,
-                        (long unsigned int)tx_ring->dma);
+                        (unsigned long int)tx_ring->dma);
                dev_info(&pf->pdev->dev,
                         "    tx_rings[%i]: vsi = %p, q_vector = %p\n",
                         i, tx_ring->vsi,
@@ -743,6 +745,7 @@ static void i40e_dbg_dump_aq_desc(struct i40e_pf *pf)
        ring = &(hw->aq.asq);
        for (i = 0; i < ring->count; i++) {
                struct i40e_aq_desc *d = I40E_ADMINQ_DESC(*ring, i);
+
                dev_info(&pf->pdev->dev,
                         "   at[%02d] flags=0x%04x op=0x%04x dlen=0x%04x ret=0x%04x cookie_h=0x%08x cookie_l=0x%08x\n",
                         i, d->flags, d->opcode, d->datalen, d->retval,
@@ -755,6 +758,7 @@ static void i40e_dbg_dump_aq_desc(struct i40e_pf *pf)
        ring = &(hw->aq.arq);
        for (i = 0; i < ring->count; i++) {
                struct i40e_aq_desc *d = I40E_ADMINQ_DESC(*ring, i);
+
                dev_info(&pf->pdev->dev,
                         "   ar[%02d] flags=0x%04x op=0x%04x dlen=0x%04x ret=0x%04x cookie_h=0x%08x cookie_l=0x%08x\n",
                         i, d->flags, d->opcode, d->datalen, d->retval,
@@ -949,24 +953,6 @@ static void i40e_dbg_dump_veb_all(struct i40e_pf *pf)
        }
 }
 
-/**
- * i40e_dbg_cmd_fd_ctrl - Enable/disable FD sideband/ATR
- * @pf: the PF that would be altered
- * @flag: flag that needs enabling or disabling
- * @enable: Enable/disable FD SD/ATR
- **/
-static void i40e_dbg_cmd_fd_ctrl(struct i40e_pf *pf, u64 flag, bool enable)
-{
-       if (enable) {
-               pf->flags |= flag;
-       } else {
-               pf->flags &= ~flag;
-               pf->auto_disable_flags |= flag;
-       }
-       dev_info(&pf->pdev->dev, "requesting a PF reset\n");
-       i40e_do_reset_safe(pf, BIT(__I40E_PF_RESET_REQUESTED));
-}
-
 #define I40E_MAX_DEBUG_OUT_BUFFER (4096*4)
 /**
  * i40e_dbg_command_write - write into command datum
@@ -1038,7 +1024,13 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        dev_info(&pf->pdev->dev, "'%s' failed\n", cmd_buf);
 
        } else if (strncmp(cmd_buf, "del vsi", 7) == 0) {
-               sscanf(&cmd_buf[7], "%i", &vsi_seid);
+               cnt = sscanf(&cmd_buf[7], "%i", &vsi_seid);
+               if (cnt != 1) {
+                       dev_info(&pf->pdev->dev,
+                                "del vsi: bad command string, cnt=%d\n",
+                                cnt);
+                       goto command_write_done;
+               }
                vsi = i40e_dbg_find_vsi(pf, vsi_seid);
                if (!vsi) {
                        dev_info(&pf->pdev->dev, "del VSI %d: seid not found\n",
@@ -1145,8 +1137,10 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        goto command_write_done;
                }
 
+               spin_lock_bh(&vsi->mac_filter_list_lock);
                f = i40e_add_filter(vsi, ma, vlan, false, false);
-               ret = i40e_sync_vsi_filters(vsi);
+               spin_unlock_bh(&vsi->mac_filter_list_lock);
+               ret = i40e_sync_vsi_filters(vsi, true);
                if (f && !ret)
                        dev_info(&pf->pdev->dev,
                                 "add macaddr: %pM vlan=%d added to VSI %d\n",
@@ -1182,8 +1176,10 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        goto command_write_done;
                }
 
+               spin_lock_bh(&vsi->mac_filter_list_lock);
                i40e_del_filter(vsi, ma, vlan, false, false);
-               ret = i40e_sync_vsi_filters(vsi);
+               spin_unlock_bh(&vsi->mac_filter_list_lock);
+               ret = i40e_sync_vsi_filters(vsi, true);
                if (!ret)
                        dev_info(&pf->pdev->dev,
                                 "del macaddr: %pM vlan=%d removed from VSI %d\n",
@@ -1488,6 +1484,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
        } else if (strncmp(cmd_buf, "read", 4) == 0) {
                u32 address;
                u32 value;
+
                cnt = sscanf(&cmd_buf[4], "%i", &address);
                if (cnt != 1) {
                        dev_info(&pf->pdev->dev, "read <reg>\n");
@@ -1495,9 +1492,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                }
 
                /* check the range on address */
-               if (address >= I40E_MAX_REGISTER) {
-                       dev_info(&pf->pdev->dev, "read reg address 0x%08x too large\n",
-                                address);
+               if (address > (pf->ioremap_len - sizeof(u32))) {
+                       dev_info(&pf->pdev->dev, "read reg address 0x%08x too large, max=0x%08lx\n",
+                                address, (unsigned long int)(pf->ioremap_len - sizeof(u32)));
                        goto command_write_done;
                }
 
@@ -1507,6 +1504,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
 
        } else if (strncmp(cmd_buf, "write", 5) == 0) {
                u32 address, value;
+
                cnt = sscanf(&cmd_buf[5], "%i %i", &address, &value);
                if (cnt != 2) {
                        dev_info(&pf->pdev->dev, "write <reg> <value>\n");
@@ -1514,9 +1512,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                }
 
                /* check the range on address */
-               if (address >= I40E_MAX_REGISTER) {
-                       dev_info(&pf->pdev->dev, "write reg address 0x%08x too large\n",
-                                address);
+               if (address > (pf->ioremap_len - sizeof(u32))) {
+                       dev_info(&pf->pdev->dev, "write reg address 0x%08x too large, max=0x%08lx\n",
+                                address, (unsigned long int)(pf->ioremap_len - sizeof(u32)));
                        goto command_write_done;
                }
                wr32(&pf->hw, address, value);
@@ -1528,6 +1526,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        cnt = sscanf(&cmd_buf[15], "%i", &vsi_seid);
                        if (cnt == 0) {
                                int i;
+
                                for (i = 0; i < pf->num_alloc_vsi; i++)
                                        i40e_vsi_reset_stats(pf->vsi[i]);
                                dev_info(&pf->pdev->dev, "vsi clear stats called for all vsi's\n");
@@ -1726,8 +1725,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                                   packet_len, I40E_FDIR_MAX_RAW_PACKET_SIZE);
 
                for (i = 0; i < packet_len; i++) {
-                       sscanf(&asc_packet[j], "%2hhx ",
-                              &raw_packet[i]);
+                       cnt = sscanf(&asc_packet[j], "%2hhx ", &raw_packet[i]);
+                       if (!cnt)
+                               break;
                        j += 3;
                }
                dev_info(&pf->pdev->dev, "FD raw packet dump\n");
@@ -1745,16 +1745,13 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                raw_packet = NULL;
                kfree(asc_packet);
                asc_packet = NULL;
-       } else if (strncmp(cmd_buf, "fd-atr off", 10) == 0) {
-               i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, false);
-       } else if (strncmp(cmd_buf, "fd-atr on", 9) == 0) {
-               i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, true);
        } else if (strncmp(cmd_buf, "fd current cnt", 14) == 0) {
                dev_info(&pf->pdev->dev, "FD current total filter count for this interface: %d\n",
                         i40e_get_current_fd_count(pf));
        } else if (strncmp(cmd_buf, "lldp", 4) == 0) {
                if (strncmp(&cmd_buf[5], "stop", 4) == 0) {
                        int ret;
+
                        ret = i40e_aq_stop_lldp(&pf->hw, false, NULL);
                        if (ret) {
                                dev_info(&pf->pdev->dev,
@@ -1779,6 +1776,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
 #endif /* CONFIG_I40E_DCB */
                } else if (strncmp(&cmd_buf[5], "start", 5) == 0) {
                        int ret;
+
                        ret = i40e_aq_add_rem_control_packet_filter(&pf->hw,
                                                pf->hw.mac.addr,
                                                I40E_ETH_P_LLDP, 0,
@@ -1807,6 +1805,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        u16 llen, rlen;
                        int ret;
                        u8 *buff;
+
                        buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL);
                        if (!buff)
                                goto command_write_done;
@@ -1833,6 +1832,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        u16 llen, rlen;
                        int ret;
                        u8 *buff;
+
                        buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL);
                        if (!buff)
                                goto command_write_done;
@@ -1858,6 +1858,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        buff = NULL;
                } else if (strncmp(&cmd_buf[5], "event on", 8) == 0) {
                        int ret;
+
                        ret = i40e_aq_cfg_lldp_mib_change_event(&pf->hw,
                                                                true, NULL);
                        if (ret) {
@@ -1868,6 +1869,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        }
                } else if (strncmp(&cmd_buf[5], "event off", 9) == 0) {
                        int ret;
+
                        ret = i40e_aq_cfg_lldp_mib_change_event(&pf->hw,
                                                                false, NULL);
                        if (ret) {
@@ -1969,8 +1971,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                dev_info(&pf->pdev->dev, "  send indirect aq_cmd <flags> <opcode> <datalen> <retval> <cookie_h> <cookie_l> <param0> <param1> <param2> <param3> <buffer_len>\n");
                dev_info(&pf->pdev->dev, "  add fd_filter <dest q_index> <flex_off> <pctype> <dest_vsi> <dest_ctl> <fd_status> <cnt_index> <fd_id> <packet_len> <packet>\n");
                dev_info(&pf->pdev->dev, "  rem fd_filter <dest q_index> <flex_off> <pctype> <dest_vsi> <dest_ctl> <fd_status> <cnt_index> <fd_id> <packet_len> <packet>\n");
-               dev_info(&pf->pdev->dev, "  fd-atr off\n");
-               dev_info(&pf->pdev->dev, "  fd-atr on\n");
                dev_info(&pf->pdev->dev, "  fd current cnt");
                dev_info(&pf->pdev->dev, "  lldp start\n");
                dev_info(&pf->pdev->dev, "  lldp stop\n");
@@ -2105,6 +2105,7 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,
                }
        } else if (strncmp(i40e_dbg_netdev_ops_buf, "change_mtu", 10) == 0) {
                int mtu;
+
                cnt = sscanf(&i40e_dbg_netdev_ops_buf[11], "%i %i",
                             &vsi_seid, &mtu);
                if (cnt != 2) {
@@ -2220,7 +2221,6 @@ void i40e_dbg_pf_init(struct i40e_pf *pf)
 create_failed:
        dev_info(dev, "debugfs dir/file for %s failed\n", name);
        debugfs_remove_recursive(pf->i40e_dbg_pf);
-       return;
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/i40e/i40e_devids.h b/drivers/net/ethernet/intel/i40e/i40e_devids.h
new file mode 100644 (file)
index 0000000..c601ca4
--- /dev/null
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ *
+ * Intel Ethernet Controller XL710 Family Linux Driver
+ * Copyright(c) 2013 - 2015 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ ******************************************************************************/
+
+#ifndef _I40E_DEVIDS_H_
+#define _I40E_DEVIDS_H_
+
+/* Device IDs */
+#define I40E_DEV_ID_SFP_XL710          0x1572
+#define I40E_DEV_ID_QEMU               0x1574
+#define I40E_DEV_ID_KX_A               0x157F
+#define I40E_DEV_ID_KX_B               0x1580
+#define I40E_DEV_ID_KX_C               0x1581
+#define I40E_DEV_ID_QSFP_A             0x1583
+#define I40E_DEV_ID_QSFP_B             0x1584
+#define I40E_DEV_ID_QSFP_C             0x1585
+#define I40E_DEV_ID_10G_BASE_T         0x1586
+#define I40E_DEV_ID_20G_KR2            0x1587
+#define I40E_DEV_ID_20G_KR2_A          0x1588
+#define I40E_DEV_ID_10G_BASE_T4                0x1589
+#define I40E_DEV_ID_VF                 0x154C
+#define I40E_DEV_ID_VF_HV              0x1571
+#define I40E_DEV_ID_SFP_X722           0x37D0
+#define I40E_DEV_ID_1G_BASE_T_X722     0x37D1
+#define I40E_DEV_ID_10G_BASE_T_X722    0x37D2
+#define I40E_DEV_ID_X722_VF            0x37CD
+#define I40E_DEV_ID_X722_VF_HV         0x37D9
+
+#define i40e_is_40G_device(d)          ((d) == I40E_DEV_ID_QSFP_A  || \
+                                        (d) == I40E_DEV_ID_QSFP_B  || \
+                                        (d) == I40E_DEV_ID_QSFP_C)
+
+#endif /* _I40E_DEVIDS_H_ */
index e972b5ecbf0b6e1bfb2f11ca93a3f13a18438715..3f385ffe420f712abbda79b14c46e2b6196d8dc8 100644 (file)
@@ -87,11 +87,9 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = {
        I40E_VSI_STAT("rx_broadcast", eth_stats.rx_broadcast),
        I40E_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast),
        I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol),
+       I40E_VSI_STAT("tx_linearize", tx_linearize),
 };
 
-static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
-                                struct ethtool_rxnfc *cmd);
-
 /* These PF_STATs might look like duplicates of some NETDEV_STATs,
  * but they are separate.  This device supports Virtualization, and
  * as such might have several netdevs supporting VMDq and FCoE going
@@ -229,10 +227,12 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = {
 
 static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = {
        "NPAR",
+       "LinkPolling",
+       "flow-director-atr",
+       "veb-stats",
 };
 
-#define I40E_PRIV_FLAGS_STR_LEN \
-       (sizeof(i40e_priv_flags_strings) / ETH_GSTRING_LEN)
+#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings)
 
 /**
  * i40e_partition_setting_complaint - generic complaint for MFP restriction
@@ -253,7 +253,8 @@ static void i40e_partition_setting_complaint(struct i40e_pf *pf)
  **/
 static void i40e_get_settings_link_up(struct i40e_hw *hw,
                                      struct ethtool_cmd *ecmd,
-                                     struct net_device *netdev)
+                                     struct net_device *netdev,
+                                     struct i40e_pf *pf)
 {
        struct i40e_link_status *hw_link_info = &hw->phy.link_info;
        u32 link_speed = hw_link_info->link_speed;
@@ -272,65 +273,49 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw,
        case I40E_PHY_TYPE_40GBASE_AOC:
                ecmd->supported = SUPPORTED_40000baseCR4_Full;
                break;
-       case I40E_PHY_TYPE_40GBASE_KR4:
-               ecmd->supported = SUPPORTED_Autoneg |
-                                 SUPPORTED_40000baseKR4_Full;
-               ecmd->advertising = ADVERTISED_Autoneg |
-                                   ADVERTISED_40000baseKR4_Full;
-               break;
        case I40E_PHY_TYPE_40GBASE_SR4:
                ecmd->supported = SUPPORTED_40000baseSR4_Full;
                break;
        case I40E_PHY_TYPE_40GBASE_LR4:
                ecmd->supported = SUPPORTED_40000baseLR4_Full;
                break;
-       case I40E_PHY_TYPE_20GBASE_KR2:
-               ecmd->supported = SUPPORTED_Autoneg |
-                                 SUPPORTED_20000baseKR2_Full;
-               ecmd->advertising = ADVERTISED_Autoneg |
-                                   ADVERTISED_20000baseKR2_Full;
-               break;
-       case I40E_PHY_TYPE_10GBASE_KX4:
-               ecmd->supported = SUPPORTED_Autoneg |
-                                 SUPPORTED_10000baseKX4_Full;
-               ecmd->advertising = ADVERTISED_Autoneg |
-                                   ADVERTISED_10000baseKX4_Full;
-               break;
-       case I40E_PHY_TYPE_10GBASE_KR:
-               ecmd->supported = SUPPORTED_Autoneg |
-                                 SUPPORTED_10000baseKR_Full;
-               ecmd->advertising = ADVERTISED_Autoneg |
-                                   ADVERTISED_10000baseKR_Full;
-               break;
        case I40E_PHY_TYPE_10GBASE_SR:
        case I40E_PHY_TYPE_10GBASE_LR:
        case I40E_PHY_TYPE_1000BASE_SX:
        case I40E_PHY_TYPE_1000BASE_LX:
-               ecmd->supported = SUPPORTED_10000baseT_Full |
-                                 SUPPORTED_1000baseT_Full;
+               ecmd->supported = SUPPORTED_10000baseT_Full;
+               if (hw_link_info->module_type[2] &
+                   I40E_MODULE_TYPE_1000BASE_SX ||
+                   hw_link_info->module_type[2] &
+                   I40E_MODULE_TYPE_1000BASE_LX) {
+                       ecmd->supported |= SUPPORTED_1000baseT_Full;
+                       if (hw_link_info->requested_speeds &
+                           I40E_LINK_SPEED_1GB)
+                               ecmd->advertising |= ADVERTISED_1000baseT_Full;
+               }
                if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
                        ecmd->advertising |= ADVERTISED_10000baseT_Full;
-               if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
-                       ecmd->advertising |= ADVERTISED_1000baseT_Full;
-               break;
-       case I40E_PHY_TYPE_1000BASE_KX:
-               ecmd->supported = SUPPORTED_Autoneg |
-                                 SUPPORTED_1000baseKX_Full;
-               ecmd->advertising = ADVERTISED_Autoneg |
-                                   ADVERTISED_1000baseKX_Full;
                break;
        case I40E_PHY_TYPE_10GBASE_T:
        case I40E_PHY_TYPE_1000BASE_T:
-       case I40E_PHY_TYPE_100BASE_TX:
                ecmd->supported = SUPPORTED_Autoneg |
                                  SUPPORTED_10000baseT_Full |
-                                 SUPPORTED_1000baseT_Full |
-                                 SUPPORTED_100baseT_Full;
+                                 SUPPORTED_1000baseT_Full;
                ecmd->advertising = ADVERTISED_Autoneg;
                if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
                        ecmd->advertising |= ADVERTISED_10000baseT_Full;
                if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
                        ecmd->advertising |= ADVERTISED_1000baseT_Full;
+               break;
+       case I40E_PHY_TYPE_1000BASE_T_OPTICAL:
+               ecmd->supported = SUPPORTED_Autoneg |
+                                 SUPPORTED_1000baseT_Full;
+               ecmd->advertising = ADVERTISED_Autoneg |
+                                   ADVERTISED_1000baseT_Full;
+               break;
+       case I40E_PHY_TYPE_100BASE_TX:
+               ecmd->supported = SUPPORTED_Autoneg |
+                                 SUPPORTED_100baseT_Full;
                if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB)
                        ecmd->advertising |= ADVERTISED_100baseT_Full;
                break;
@@ -350,12 +335,24 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw,
                break;
        case I40E_PHY_TYPE_SGMII:
                ecmd->supported = SUPPORTED_Autoneg |
-                                 SUPPORTED_1000baseT_Full |
-                                 SUPPORTED_100baseT_Full;
+                                 SUPPORTED_1000baseT_Full;
                if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
                        ecmd->advertising |= ADVERTISED_1000baseT_Full;
-               if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB)
-                       ecmd->advertising |= ADVERTISED_100baseT_Full;
+               if (pf->hw.mac.type == I40E_MAC_X722) {
+                       ecmd->supported |= SUPPORTED_100baseT_Full;
+                       if (hw_link_info->requested_speeds &
+                           I40E_LINK_SPEED_100MB)
+                               ecmd->advertising |= ADVERTISED_100baseT_Full;
+               }
+               break;
+       /* Backplane is set based on supported phy types in get_settings
+        * so don't set anything here but don't warn either
+        */
+       case I40E_PHY_TYPE_40GBASE_KR4:
+       case I40E_PHY_TYPE_20GBASE_KR2:
+       case I40E_PHY_TYPE_10GBASE_KR:
+       case I40E_PHY_TYPE_10GBASE_KX4:
+       case I40E_PHY_TYPE_1000BASE_KX:
                break;
        default:
                /* if we got here and link is up something bad is afoot */
@@ -394,64 +391,73 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw,
  * Reports link settings that can be determined when link is down
  **/
 static void i40e_get_settings_link_down(struct i40e_hw *hw,
-                                       struct ethtool_cmd *ecmd)
+                                       struct ethtool_cmd *ecmd,
+                                       struct i40e_pf *pf)
 {
-       struct i40e_link_status *hw_link_info = &hw->phy.link_info;
+       enum i40e_aq_capabilities_phy_type phy_types = hw->phy.phy_types;
 
        /* link is down and the driver needs to fall back on
-        * device ID to determine what kinds of info to display,
-        * it's mostly a guess that may change when link is up
+        * supported phy types to figure out what info to display
         */
-       switch (hw->device_id) {
-       case I40E_DEV_ID_QSFP_A:
-       case I40E_DEV_ID_QSFP_B:
-       case I40E_DEV_ID_QSFP_C:
-               /* pluggable QSFP */
-               ecmd->supported = SUPPORTED_40000baseSR4_Full |
-                                 SUPPORTED_40000baseCR4_Full |
-                                 SUPPORTED_40000baseLR4_Full;
-               ecmd->advertising = ADVERTISED_40000baseSR4_Full |
-                                   ADVERTISED_40000baseCR4_Full |
-                                   ADVERTISED_40000baseLR4_Full;
-               break;
-       case I40E_DEV_ID_KX_B:
-               /* backplane 40G */
-               ecmd->supported = SUPPORTED_40000baseKR4_Full;
-               ecmd->advertising = ADVERTISED_40000baseKR4_Full;
-               break;
-       case I40E_DEV_ID_KX_C:
-               /* backplane 10G */
-               ecmd->supported = SUPPORTED_10000baseKR_Full;
-               ecmd->advertising = ADVERTISED_10000baseKR_Full;
-               break;
-       case I40E_DEV_ID_10G_BASE_T:
-               ecmd->supported = SUPPORTED_10000baseT_Full |
-                                 SUPPORTED_1000baseT_Full |
-                                 SUPPORTED_100baseT_Full;
-               /* Figure out what has been requested */
-               if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
-                       ecmd->advertising |= ADVERTISED_10000baseT_Full;
-               if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
-                       ecmd->advertising |= ADVERTISED_1000baseT_Full;
-               if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB)
+       ecmd->supported = 0x0;
+       ecmd->advertising = 0x0;
+       if (phy_types & I40E_CAP_PHY_TYPE_SGMII) {
+               ecmd->supported |= SUPPORTED_Autoneg |
+                                  SUPPORTED_1000baseT_Full;
+               ecmd->advertising |= ADVERTISED_Autoneg |
+                                    ADVERTISED_1000baseT_Full;
+               if (pf->hw.mac.type == I40E_MAC_X722) {
+                       ecmd->supported |= SUPPORTED_100baseT_Full;
                        ecmd->advertising |= ADVERTISED_100baseT_Full;
-               break;
-       case I40E_DEV_ID_20G_KR2:
-               /* backplane 20G */
-               ecmd->supported = SUPPORTED_20000baseKR2_Full;
-               ecmd->advertising = ADVERTISED_20000baseKR2_Full;
-               break;
-       default:
-               /* all the rest are 10G/1G */
-               ecmd->supported = SUPPORTED_10000baseT_Full |
-                                 SUPPORTED_1000baseT_Full;
-               /* Figure out what has been requested */
-               if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
-                       ecmd->advertising |= ADVERTISED_10000baseT_Full;
-               if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
-                       ecmd->advertising |= ADVERTISED_1000baseT_Full;
-               break;
+               }
        }
+       if (phy_types & I40E_CAP_PHY_TYPE_XAUI ||
+           phy_types & I40E_CAP_PHY_TYPE_XFI ||
+           phy_types & I40E_CAP_PHY_TYPE_SFI ||
+           phy_types & I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU ||
+           phy_types & I40E_CAP_PHY_TYPE_10GBASE_AOC)
+               ecmd->supported |= SUPPORTED_10000baseT_Full;
+       if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1_CU ||
+           phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1 ||
+           phy_types & I40E_CAP_PHY_TYPE_10GBASE_T ||
+           phy_types & I40E_CAP_PHY_TYPE_10GBASE_SR ||
+           phy_types & I40E_CAP_PHY_TYPE_10GBASE_LR) {
+               ecmd->supported |= SUPPORTED_Autoneg |
+                                  SUPPORTED_10000baseT_Full;
+               ecmd->advertising |= ADVERTISED_Autoneg |
+                                    ADVERTISED_10000baseT_Full;
+       }
+       if (phy_types & I40E_CAP_PHY_TYPE_XLAUI ||
+           phy_types & I40E_CAP_PHY_TYPE_XLPPI ||
+           phy_types & I40E_CAP_PHY_TYPE_40GBASE_AOC)
+               ecmd->supported |= SUPPORTED_40000baseCR4_Full;
+       if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4_CU ||
+           phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4) {
+               ecmd->supported |= SUPPORTED_Autoneg |
+                                 SUPPORTED_40000baseCR4_Full;
+               ecmd->advertising |= ADVERTISED_Autoneg |
+                                   ADVERTISED_40000baseCR4_Full;
+       }
+       if ((phy_types & I40E_CAP_PHY_TYPE_100BASE_TX) &&
+           !(phy_types & I40E_CAP_PHY_TYPE_1000BASE_T)) {
+               ecmd->supported |= SUPPORTED_Autoneg |
+                                  SUPPORTED_100baseT_Full;
+               ecmd->advertising |= ADVERTISED_Autoneg |
+                                    ADVERTISED_100baseT_Full;
+       }
+       if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_T ||
+           phy_types & I40E_CAP_PHY_TYPE_1000BASE_SX ||
+           phy_types & I40E_CAP_PHY_TYPE_1000BASE_LX ||
+           phy_types & I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL) {
+               ecmd->supported |= SUPPORTED_Autoneg |
+                                  SUPPORTED_1000baseT_Full;
+               ecmd->advertising |= ADVERTISED_Autoneg |
+                                    ADVERTISED_1000baseT_Full;
+       }
+       if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_SR4)
+               ecmd->supported |= SUPPORTED_40000baseSR4_Full;
+       if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_LR4)
+               ecmd->supported |= SUPPORTED_40000baseLR4_Full;
 
        /* With no link speed and duplex are unknown */
        ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
@@ -475,12 +481,43 @@ static int i40e_get_settings(struct net_device *netdev,
        bool link_up = hw_link_info->link_info & I40E_AQ_LINK_UP;
 
        if (link_up)
-               i40e_get_settings_link_up(hw, ecmd, netdev);
+               i40e_get_settings_link_up(hw, ecmd, netdev, pf);
        else
-               i40e_get_settings_link_down(hw, ecmd);
+               i40e_get_settings_link_down(hw, ecmd, pf);
 
        /* Now set the settings that don't rely on link being up/down */
 
+       /* For backplane, supported and advertised are only reliant on the
+        * phy types the NVM specifies are supported.
+        */
+       if (hw->device_id == I40E_DEV_ID_KX_B ||
+           hw->device_id == I40E_DEV_ID_KX_C ||
+           hw->device_id == I40E_DEV_ID_20G_KR2 ||
+           hw->device_id ==  I40E_DEV_ID_20G_KR2_A) {
+               ecmd->supported = SUPPORTED_Autoneg;
+               ecmd->advertising = ADVERTISED_Autoneg;
+               if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_40GBASE_KR4) {
+                       ecmd->supported |= SUPPORTED_40000baseKR4_Full;
+                       ecmd->advertising |= ADVERTISED_40000baseKR4_Full;
+               }
+               if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_20GBASE_KR2) {
+                       ecmd->supported |= SUPPORTED_20000baseKR2_Full;
+                       ecmd->advertising |= ADVERTISED_20000baseKR2_Full;
+               }
+               if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR) {
+                       ecmd->supported |= SUPPORTED_10000baseKR_Full;
+                       ecmd->advertising |= ADVERTISED_10000baseKR_Full;
+               }
+               if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4) {
+                       ecmd->supported |= SUPPORTED_10000baseKX4_Full;
+                       ecmd->advertising |= ADVERTISED_10000baseKX4_Full;
+               }
+               if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX) {
+                       ecmd->supported |= SUPPORTED_1000baseKX_Full;
+                       ecmd->advertising |= ADVERTISED_1000baseKX_Full;
+               }
+       }
+
        /* Set autoneg settings */
        ecmd->autoneg = ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ?
                          AUTONEG_ENABLE : AUTONEG_DISABLE);
@@ -580,6 +617,14 @@ static int i40e_set_settings(struct net_device *netdev,
            hw->phy.link_info.link_info & I40E_AQ_LINK_UP)
                return -EOPNOTSUPP;
 
+       if (hw->device_id == I40E_DEV_ID_KX_B ||
+           hw->device_id == I40E_DEV_ID_KX_C ||
+           hw->device_id == I40E_DEV_ID_20G_KR2 ||
+           hw->device_id == I40E_DEV_ID_20G_KR2_A) {
+               netdev_info(netdev, "Changing settings is not supported on backplane.\n");
+               return -EOPNOTSUPP;
+       }
+
        /* get our own copy of the bits to check against */
        memset(&safe_ecmd, 0, sizeof(struct ethtool_cmd));
        i40e_get_settings(netdev, &safe_ecmd);
@@ -616,28 +661,31 @@ static int i40e_set_settings(struct net_device *netdev,
 
        /* Check autoneg */
        if (autoneg == AUTONEG_ENABLE) {
-               /* If autoneg is not supported, return error */
-               if (!(safe_ecmd.supported & SUPPORTED_Autoneg)) {
-                       netdev_info(netdev, "Autoneg not supported on this phy\n");
-                       return -EINVAL;
-               }
                /* If autoneg was not already enabled */
                if (!(hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED)) {
+                       /* If autoneg is not supported, return error */
+                       if (!(safe_ecmd.supported & SUPPORTED_Autoneg)) {
+                               netdev_info(netdev, "Autoneg not supported on this phy\n");
+                               return -EINVAL;
+                       }
+                       /* Autoneg is allowed to change */
                        config.abilities = abilities.abilities |
                                           I40E_AQ_PHY_ENABLE_AN;
                        change = true;
                }
        } else {
-               /* If autoneg is supported 10GBASE_T is the only phy that
-                * can disable it, so otherwise return error
-                */
-               if (safe_ecmd.supported & SUPPORTED_Autoneg &&
-                   hw->phy.link_info.phy_type != I40E_PHY_TYPE_10GBASE_T) {
-                       netdev_info(netdev, "Autoneg cannot be disabled on this phy\n");
-                       return -EINVAL;
-               }
                /* If autoneg is currently enabled */
                if (hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED) {
+                       /* If autoneg is supported 10GBASE_T is the only PHY
+                        * that can disable it, so otherwise return error
+                        */
+                       if (safe_ecmd.supported & SUPPORTED_Autoneg &&
+                           hw->phy.link_info.phy_type !=
+                           I40E_PHY_TYPE_10GBASE_T) {
+                               netdev_info(netdev, "Autoneg cannot be disabled on this phy\n");
+                               return -EINVAL;
+                       }
+                       /* Autoneg is allowed to change */
                        config.abilities = abilities.abilities &
                                           ~I40E_AQ_PHY_ENABLE_AN;
                        change = true;
@@ -664,6 +712,13 @@ static int i40e_set_settings(struct net_device *netdev,
            advertise & ADVERTISED_40000baseLR4_Full)
                config.link_speed |= I40E_LINK_SPEED_40GB;
 
+       /* If speed didn't get set, set it to what it currently is.
+        * This is needed because if advertise is 0 (as it is when autoneg
+        * is disabled) then speed won't get set.
+        */
+       if (!config.link_speed)
+               config.link_speed = abilities.link_speed;
+
        if (change || (abilities.link_speed != config.link_speed)) {
                /* copy over the rest of the abilities */
                config.phy_type = abilities.phy_type;
@@ -680,7 +735,7 @@ static int i40e_set_settings(struct net_device *netdev,
                        /* Tell the OS link is going down, the link will go
                         * back up when fw says it is ready asynchronously
                         */
-                       netdev_info(netdev, "PHY settings change requested, NIC Link is going down.\n");
+                       i40e_print_link_message(vsi, false);
                        netif_carrier_off(netdev);
                        netif_tx_stop_all_queues(netdev);
                }
@@ -694,11 +749,11 @@ static int i40e_set_settings(struct net_device *netdev,
                        return -EAGAIN;
                }
 
-               status = i40e_aq_get_link_info(hw, true, NULL, NULL);
+               status = i40e_update_link_info(hw);
                if (status)
-                       netdev_info(netdev, "Updating link info failed with err %s aq_err %s\n",
-                                   i40e_stat_str(hw, status),
-                                   i40e_aq_str(hw, hw->aq.asq_last_status));
+                       netdev_dbg(netdev, "Updating link info failed with err %s aq_err %s\n",
+                                  i40e_stat_str(hw, status),
+                                  i40e_aq_str(hw, hw->aq.asq_last_status));
 
        } else {
                netdev_info(netdev, "Nothing changed, exiting without setting anything.\n");
@@ -824,7 +879,7 @@ static int i40e_set_pauseparam(struct net_device *netdev,
        /* Tell the OS link is going down, the link will go back up when fw
         * says it is ready asynchronously
         */
-       netdev_info(netdev, "Flow control settings change requested, NIC Link is going down.\n");
+       i40e_print_link_message(vsi, false);
        netif_carrier_off(netdev);
        netif_tx_stop_all_queues(netdev);
 
@@ -948,9 +1003,7 @@ static int i40e_get_eeprom(struct net_device *netdev,
 
                cmd = (struct i40e_nvm_access *)eeprom;
                ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno);
-               if (ret_val &&
-                   ((hw->aq.asq_last_status != I40E_AQ_RC_EACCES) ||
-                    (hw->debug_mask & I40E_DEBUG_NVM)))
+               if (ret_val && (hw->debug_mask & I40E_DEBUG_NVM))
                        dev_info(&pf->pdev->dev,
                                 "NVMUpdate read failed err=%d status=0x%x errno=%d module=%d offset=0x%x size=%d\n",
                                 ret_val, hw->aq.asq_last_status, errno,
@@ -1054,10 +1107,7 @@ static int i40e_set_eeprom(struct net_device *netdev,
 
        cmd = (struct i40e_nvm_access *)eeprom;
        ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno);
-       if (ret_val &&
-           ((hw->aq.asq_last_status != I40E_AQ_RC_EPERM &&
-             hw->aq.asq_last_status != I40E_AQ_RC_EBUSY) ||
-            (hw->debug_mask & I40E_DEBUG_NVM)))
+       if (ret_val && (hw->debug_mask & I40E_DEBUG_NVM))
                dev_info(&pf->pdev->dev,
                         "NVMUpdate write failed err=%d status=0x%x errno=%d module=%d offset=0x%x size=%d\n",
                         ret_val, hw->aq.asq_last_status, errno,
@@ -1077,11 +1127,10 @@ static void i40e_get_drvinfo(struct net_device *netdev,
        strlcpy(drvinfo->driver, i40e_driver_name, sizeof(drvinfo->driver));
        strlcpy(drvinfo->version, i40e_driver_version_str,
                sizeof(drvinfo->version));
-       strlcpy(drvinfo->fw_version, i40e_fw_version_str(&pf->hw),
+       strlcpy(drvinfo->fw_version, i40e_nvm_version_str(&pf->hw),
                sizeof(drvinfo->fw_version));
        strlcpy(drvinfo->bus_info, pci_name(pf->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->n_priv_flags = I40E_PRIV_FLAGS_STR_LEN;
 }
 
 static void i40e_get_ringparam(struct net_device *netdev,
@@ -1166,6 +1215,11 @@ static int i40e_set_ringparam(struct net_device *netdev,
                        /* clone ring and setup updated count */
                        tx_rings[i] = *vsi->tx_rings[i];
                        tx_rings[i].count = new_tx_count;
+                       /* the desc and bi pointers will be reallocated in the
+                        * setup call
+                        */
+                       tx_rings[i].desc = NULL;
+                       tx_rings[i].rx_bi = NULL;
                        err = i40e_setup_tx_descriptors(&tx_rings[i]);
                        if (err) {
                                while (i) {
@@ -1196,6 +1250,11 @@ static int i40e_set_ringparam(struct net_device *netdev,
                        /* clone ring and setup updated count */
                        rx_rings[i] = *vsi->rx_rings[i];
                        rx_rings[i].count = new_rx_count;
+                       /* the desc and bi pointers will be reallocated in the
+                        * setup call
+                        */
+                       rx_rings[i].desc = NULL;
+                       rx_rings[i].rx_bi = NULL;
                        err = i40e_setup_rx_descriptors(&rx_rings[i]);
                        if (err) {
                                while (i) {
@@ -1263,7 +1322,8 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset)
                if (vsi == pf->vsi[pf->lan_vsi] && pf->hw.partition_id == 1) {
                        int len = I40E_PF_STATS_LEN(netdev);
 
-                       if (pf->lan_veb != I40E_NO_VEB)
+                       if ((pf->lan_veb != I40E_NO_VEB) &&
+                           (pf->flags & I40E_FLAG_VEB_STATS_ENABLED))
                                len += I40E_VEB_STATS_TOTAL;
                        return len;
                } else {
@@ -1336,14 +1396,22 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
        if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1)
                return;
 
-       if (pf->lan_veb != I40E_NO_VEB) {
+       if ((pf->lan_veb != I40E_NO_VEB) &&
+           (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) {
                struct i40e_veb *veb = pf->veb[pf->lan_veb];
+
                for (j = 0; j < I40E_VEB_STATS_LEN; j++) {
                        p = (char *)veb;
                        p += i40e_gstrings_veb_stats[j].stat_offset;
                        data[i++] = (i40e_gstrings_veb_stats[j].sizeof_stat ==
                                     sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
                }
+               for (j = 0; j < I40E_MAX_TRAFFIC_CLASS; j++) {
+                       data[i++] = veb->tc_stats.tc_tx_packets[j];
+                       data[i++] = veb->tc_stats.tc_tx_bytes[j];
+                       data[i++] = veb->tc_stats.tc_rx_packets[j];
+                       data[i++] = veb->tc_stats.tc_rx_bytes[j];
+               }
        }
        for (j = 0; j < I40E_GLOBAL_STATS_LEN; j++) {
                p = (char *)pf + i40e_gstrings_stats[j].stat_offset;
@@ -1409,7 +1477,8 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset,
                if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1)
                        return;
 
-               if (pf->lan_veb != I40E_NO_VEB) {
+               if ((pf->lan_veb != I40E_NO_VEB) &&
+                   (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) {
                        for (i = 0; i < I40E_VEB_STATS_LEN; i++) {
                                snprintf(p, ETH_GSTRING_LEN, "veb.%s",
                                        i40e_gstrings_veb_stats[i].stat_string);
@@ -1504,9 +1573,18 @@ static int i40e_link_test(struct net_device *netdev, u64 *data)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_pf *pf = np->vsi->back;
+       i40e_status status;
+       bool link_up = false;
 
        netif_info(pf, hw, netdev, "link test\n");
-       if (i40e_get_link_status(&pf->hw))
+       status = i40e_get_link_status(&pf->hw, &link_up);
+       if (status) {
+               netif_err(pf, drv, netdev, "link query timed out, please retry test\n");
+               *data = 1;
+               return *data;
+       }
+
+       if (link_up)
                *data = 0;
        else
                *data = 1;
@@ -1575,7 +1653,7 @@ static inline bool i40e_active_vfs(struct i40e_pf *pf)
        int i;
 
        for (i = 0; i < pf->num_alloc_vfs; i++)
-               if (vfs[i].vf_states & I40E_VF_STAT_ACTIVE)
+               if (test_bit(I40E_VF_STAT_ACTIVE, &vfs[i].vf_states))
                        return true;
        return false;
 }
@@ -1782,6 +1860,14 @@ static int i40e_get_coalesce(struct net_device *netdev,
 
        ec->rx_coalesce_usecs = vsi->rx_itr_setting & ~I40E_ITR_DYNAMIC;
        ec->tx_coalesce_usecs = vsi->tx_itr_setting & ~I40E_ITR_DYNAMIC;
+       /* we use the _usecs_high to store/set the interrupt rate limit
+        * that the hardware supports, that almost but not quite
+        * fits the original intent of the ethtool variable,
+        * the rx_coalesce_usecs_high limits total interrupts
+        * per second from both tx/rx sources.
+        */
+       ec->rx_coalesce_usecs_high = vsi->int_rate_limit;
+       ec->tx_coalesce_usecs_high = vsi->int_rate_limit;
 
        return 0;
 }
@@ -1800,6 +1886,17 @@ static int i40e_set_coalesce(struct net_device *netdev,
        if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq)
                vsi->work_limit = ec->tx_max_coalesced_frames_irq;
 
+       /* tx_coalesce_usecs_high is ignored, use rx-usecs-high instead */
+       if (ec->tx_coalesce_usecs_high != vsi->int_rate_limit) {
+               netif_info(pf, drv, netdev, "tx-usecs-high is not used, please program rx-usecs-high\n");
+               return -EINVAL;
+       }
+
+       if (ec->rx_coalesce_usecs_high >= INTRL_REG_TO_USEC(I40E_MAX_INTRL)) {
+               netif_info(pf, drv, netdev, "Invalid value, rx-usecs-high range is 0-235\n");
+               return -EINVAL;
+       }
+
        vector = vsi->base_vector;
        if ((ec->rx_coalesce_usecs >= (I40E_MIN_ITR << 1)) &&
            (ec->rx_coalesce_usecs <= (I40E_MAX_ITR << 1))) {
@@ -1813,6 +1910,8 @@ static int i40e_set_coalesce(struct net_device *netdev,
                return -EINVAL;
        }
 
+       vsi->int_rate_limit = ec->rx_coalesce_usecs_high;
+
        if ((ec->tx_coalesce_usecs >= (I40E_MIN_ITR << 1)) &&
            (ec->tx_coalesce_usecs <= (I40E_MAX_ITR << 1))) {
                vsi->tx_itr_setting = ec->tx_coalesce_usecs;
@@ -1837,11 +1936,14 @@ static int i40e_set_coalesce(struct net_device *netdev,
                vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC;
 
        for (i = 0; i < vsi->num_q_vectors; i++, vector++) {
+               u16 intrl = INTRL_USEC_TO_REG(vsi->int_rate_limit);
+
                q_vector = vsi->q_vectors[i];
                q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting);
                wr32(hw, I40E_PFINT_ITRN(0, vector - 1), q_vector->rx.itr);
                q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting);
                wr32(hw, I40E_PFINT_ITRN(1, vector - 1), q_vector->tx.itr);
+               wr32(hw, I40E_PFINT_RATEN(vector - 1), intrl);
                i40e_flush(hw);
        }
 
@@ -2604,10 +2706,51 @@ static u32 i40e_get_priv_flags(struct net_device *dev)
 
        ret_flags |= pf->hw.func_caps.npar_enable ?
                I40E_PRIV_FLAGS_NPAR_FLAG : 0;
+       ret_flags |= pf->flags & I40E_FLAG_LINK_POLLING_ENABLED ?
+               I40E_PRIV_FLAGS_LINKPOLL_FLAG : 0;
+       ret_flags |= pf->flags & I40E_FLAG_FD_ATR_ENABLED ?
+               I40E_PRIV_FLAGS_FD_ATR : 0;
+       ret_flags |= pf->flags & I40E_FLAG_VEB_STATS_ENABLED ?
+               I40E_PRIV_FLAGS_VEB_STATS : 0;
 
        return ret_flags;
 }
 
+/**
+ * i40e_set_priv_flags - set private flags
+ * @dev: network interface device structure
+ * @flags: bit flags to be set
+ **/
+static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
+{
+       struct i40e_netdev_priv *np = netdev_priv(dev);
+       struct i40e_vsi *vsi = np->vsi;
+       struct i40e_pf *pf = vsi->back;
+
+       if (flags & I40E_PRIV_FLAGS_LINKPOLL_FLAG)
+               pf->flags |= I40E_FLAG_LINK_POLLING_ENABLED;
+       else
+               pf->flags &= ~I40E_FLAG_LINK_POLLING_ENABLED;
+
+       /* allow the user to control the state of the Flow
+        * Director ATR (Application Targeted Routing) feature
+        * of the driver
+        */
+       if (flags & I40E_PRIV_FLAGS_FD_ATR) {
+               pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
+       } else {
+               pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+               pf->auto_disable_flags |= I40E_FLAG_FD_ATR_ENABLED;
+       }
+
+       if (flags & I40E_PRIV_FLAGS_VEB_STATS)
+               pf->flags |= I40E_FLAG_VEB_STATS_ENABLED;
+       else
+               pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED;
+
+       return 0;
+}
+
 static const struct ethtool_ops i40e_ethtool_ops = {
        .get_settings           = i40e_get_settings,
        .set_settings           = i40e_set_settings,
@@ -2644,6 +2787,7 @@ static const struct ethtool_ops i40e_ethtool_ops = {
        .set_channels           = i40e_set_channels,
        .get_ts_info            = i40e_get_ts_info,
        .get_priv_flags         = i40e_get_priv_flags,
+       .set_priv_flags         = i40e_set_priv_flags,
 };
 
 void i40e_set_ethtool_ops(struct net_device *netdev)
index 5ea75dd537d62f6e9da5545af1f597071b4aa6bd..fe5d9bf3ed6d80801f5881d47309af3b69ec01e4 100644 (file)
@@ -272,10 +272,8 @@ out:
 /**
  * i40e_fcoe_sw_init - sets up the HW for FCoE
  * @pf: pointer to PF
- *
- * Returns 0 if FCoE is supported otherwise the error code
  **/
-int i40e_init_pf_fcoe(struct i40e_pf *pf)
+void i40e_init_pf_fcoe(struct i40e_pf *pf)
 {
        struct i40e_hw *hw = &pf->hw;
        u32 val;
@@ -286,14 +284,14 @@ int i40e_init_pf_fcoe(struct i40e_pf *pf)
        pf->fcoe_hmc_filt_num = 0;
 
        if (!pf->hw.func_caps.fcoe) {
-               dev_info(&pf->pdev->dev, "FCoE capability is disabled\n");
-               return 0;
+               dev_dbg(&pf->pdev->dev, "FCoE capability is disabled\n");
+               return;
        }
 
        if (!pf->hw.func_caps.dcb) {
                dev_warn(&pf->pdev->dev,
                         "Hardware is not DCB capable not enabling FCoE.\n");
-               return 0;
+               return;
        }
 
        /* enable FCoE hash filter */
@@ -326,7 +324,6 @@ int i40e_init_pf_fcoe(struct i40e_pf *pf)
        wr32(hw, I40E_GLFCOE_RCTL, val);
 
        dev_info(&pf->pdev->dev, "FCoE is supported.\n");
-       return 0;
 }
 
 /**
@@ -1519,10 +1516,12 @@ void i40e_fcoe_config_netdev(struct net_device *netdev, struct i40e_vsi *vsi)
         * same PCI function.
         */
        netdev->dev_port = 1;
+       spin_lock_bh(&vsi->mac_filter_list_lock);
        i40e_add_filter(vsi, hw->mac.san_addr, 0, false, false);
        i40e_add_filter(vsi, (u8[6]) FC_FCOE_FLOGI_MAC, 0, false, false);
        i40e_add_filter(vsi, FIP_ALL_FCOE_MACS, 0, false, false);
        i40e_add_filter(vsi, FIP_ALL_ENODE_MACS, 0, false, false);
+       spin_unlock_bh(&vsi->mac_filter_list_lock);
 
        /* use san mac */
        ether_addr_copy(netdev->dev_addr, hw->mac.san_addr);
index fa371a2a40c6817e6f9f1dd8ff8b924ff3b08153..79ae7beeafe5afa17531083ba8e933fb15a43f9b 100644 (file)
@@ -431,9 +431,8 @@ exit_sd_error:
                        pd_idx1 = max(pd_idx,
                                      ((j - 1) * I40E_HMC_MAX_BP_COUNT));
                        pd_lmt1 = min(pd_lmt, (j * I40E_HMC_MAX_BP_COUNT));
-                       for (i = pd_idx1; i < pd_lmt1; i++) {
+                       for (i = pd_idx1; i < pd_lmt1; i++)
                                i40e_remove_pd_bp(hw, info->hmc_info, i);
-                       }
                        i40e_remove_pd_page(hw, info->hmc_info, (j - 1));
                        break;
                case I40E_SD_TYPE_DIRECT:
index dd44fafd8798613be8c440c36c2366233d0958d0..b825f978d441d1987581b249694298bb5996538d 100644 (file)
@@ -39,7 +39,7 @@ static const char i40e_driver_string[] =
 
 #define DRV_VERSION_MAJOR 1
 #define DRV_VERSION_MINOR 3
-#define DRV_VERSION_BUILD 9
+#define DRV_VERSION_BUILD 46
 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
             __stringify(DRV_VERSION_MINOR) "." \
             __stringify(DRV_VERSION_BUILD)    DRV_KERN
@@ -75,10 +75,13 @@ static const struct pci_device_id i40e_pci_tbl[] = {
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T), 0},
+       {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T4), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X722), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_X722), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_X722), 0},
+       {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0},
+       {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2_A), 0},
        /* required last entry */
        {0, }
 };
@@ -213,10 +216,10 @@ static int i40e_get_lump(struct i40e_pf *pf, struct i40e_lump_tracking *pile,
                        ret = i;
                        pile->search_hint = i + j;
                        break;
-               } else {
-                       /* not enough, so skip over it and continue looking */
-                       i += j;
                }
+
+               /* not enough, so skip over it and continue looking */
+               i += j;
        }
 
        return ret;
@@ -299,25 +302,69 @@ static void i40e_tx_timeout(struct net_device *netdev)
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_vsi *vsi = np->vsi;
        struct i40e_pf *pf = vsi->back;
+       struct i40e_ring *tx_ring = NULL;
+       unsigned int i, hung_queue = 0;
+       u32 head, val;
 
        pf->tx_timeout_count++;
 
+       /* find the stopped queue the same way the stack does */
+       for (i = 0; i < netdev->num_tx_queues; i++) {
+               struct netdev_queue *q;
+               unsigned long trans_start;
+
+               q = netdev_get_tx_queue(netdev, i);
+               trans_start = q->trans_start ? : netdev->trans_start;
+               if (netif_xmit_stopped(q) &&
+                   time_after(jiffies,
+                              (trans_start + netdev->watchdog_timeo))) {
+                       hung_queue = i;
+                       break;
+               }
+       }
+
+       if (i == netdev->num_tx_queues) {
+               netdev_info(netdev, "tx_timeout: no netdev hung queue found\n");
+       } else {
+               /* now that we have an index, find the tx_ring struct */
+               for (i = 0; i < vsi->num_queue_pairs; i++) {
+                       if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc) {
+                               if (hung_queue ==
+                                   vsi->tx_rings[i]->queue_index) {
+                                       tx_ring = vsi->tx_rings[i];
+                                       break;
+                               }
+                       }
+               }
+       }
+
        if (time_after(jiffies, (pf->tx_timeout_last_recovery + HZ*20)))
-               pf->tx_timeout_recovery_level = 1;
+               pf->tx_timeout_recovery_level = 1;  /* reset after some time */
+       else if (time_before(jiffies,
+                     (pf->tx_timeout_last_recovery + netdev->watchdog_timeo)))
+               return;   /* don't do any new action before the next timeout */
+
+       if (tx_ring) {
+               head = i40e_get_head(tx_ring);
+               /* Read interrupt register */
+               if (pf->flags & I40E_FLAG_MSIX_ENABLED)
+                       val = rd32(&pf->hw,
+                            I40E_PFINT_DYN_CTLN(tx_ring->q_vector->v_idx +
+                                               tx_ring->vsi->base_vector - 1));
+               else
+                       val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0);
+
+               netdev_info(netdev, "tx_timeout: VSI_seid: %d, Q %d, NTC: 0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x, INT: 0x%x\n",
+                           vsi->seid, hung_queue, tx_ring->next_to_clean,
+                           head, tx_ring->next_to_use,
+                           readl(tx_ring->tail), val);
+       }
+
        pf->tx_timeout_last_recovery = jiffies;
-       netdev_info(netdev, "tx_timeout recovery level %d\n",
-                   pf->tx_timeout_recovery_level);
+       netdev_info(netdev, "tx_timeout recovery level %d, hung_queue %d\n",
+                   pf->tx_timeout_recovery_level, hung_queue);
 
        switch (pf->tx_timeout_recovery_level) {
-       case 0:
-               /* disable and re-enable queues for the VSI */
-               if (in_interrupt()) {
-                       set_bit(__I40E_REINIT_REQUESTED, &pf->state);
-                       set_bit(__I40E_REINIT_REQUESTED, &vsi->state);
-               } else {
-                       i40e_vsi_reinit_locked(vsi);
-               }
-               break;
        case 1:
                set_bit(__I40E_PF_RESET_REQUESTED, &pf->state);
                break;
@@ -329,10 +376,9 @@ static void i40e_tx_timeout(struct net_device *netdev)
                break;
        default:
                netdev_err(netdev, "tx_timeout recovery unsuccessful\n");
-               set_bit(__I40E_DOWN_REQUESTED, &pf->state);
-               set_bit(__I40E_DOWN_REQUESTED, &vsi->state);
                break;
        }
+
        i40e_service_event_schedule(pf);
        pf->tx_timeout_recovery_level++;
 }
@@ -431,6 +477,7 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct(
        stats->tx_errors        = vsi_stats->tx_errors;
        stats->tx_dropped       = vsi_stats->tx_dropped;
        stats->rx_errors        = vsi_stats->rx_errors;
+       stats->rx_dropped       = vsi_stats->rx_dropped;
        stats->rx_crc_errors    = vsi_stats->rx_crc_errors;
        stats->rx_length_errors = vsi_stats->rx_length_errors;
 
@@ -456,11 +503,11 @@ void i40e_vsi_reset_stats(struct i40e_vsi *vsi)
        memset(&vsi->eth_stats_offsets, 0, sizeof(vsi->eth_stats_offsets));
        if (vsi->rx_rings && vsi->rx_rings[0]) {
                for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       memset(&vsi->rx_rings[i]->stats, 0 ,
+                       memset(&vsi->rx_rings[i]->stats, 0,
                               sizeof(vsi->rx_rings[i]->stats));
-                       memset(&vsi->rx_rings[i]->rx_stats, 0 ,
+                       memset(&vsi->rx_rings[i]->rx_stats, 0,
                               sizeof(vsi->rx_rings[i]->rx_stats));
-                       memset(&vsi->tx_rings[i]->stats, 0 ,
+                       memset(&vsi->tx_rings[i]->stats, 0,
                               sizeof(vsi->tx_rings[i]->stats));
                        memset(&vsi->tx_rings[i]->tx_stats, 0,
                               sizeof(vsi->tx_rings[i]->tx_stats));
@@ -754,7 +801,6 @@ static void i40e_update_link_xoff_rx(struct i40e_pf *pf)
        struct i40e_hw_port_stats *nsd = &pf->stats;
        struct i40e_hw *hw = &pf->hw;
        u64 xoff = 0;
-       u16 i, v;
 
        if ((hw->fc.current_mode != I40E_FC_FULL) &&
            (hw->fc.current_mode != I40E_FC_RX_PAUSE))
@@ -769,18 +815,6 @@ static void i40e_update_link_xoff_rx(struct i40e_pf *pf)
        if (!(nsd->link_xoff_rx - xoff))
                return;
 
-       /* Clear the __I40E_HANG_CHECK_ARMED bit for all Tx rings */
-       for (v = 0; v < pf->num_alloc_vsi; v++) {
-               struct i40e_vsi *vsi = pf->vsi[v];
-
-               if (!vsi || !vsi->tx_rings[0])
-                       continue;
-
-               for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       struct i40e_ring *ring = vsi->tx_rings[i];
-                       clear_bit(__I40E_HANG_CHECK_ARMED, &ring->state);
-               }
-       }
 }
 
 /**
@@ -796,7 +830,7 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf)
        bool xoff[I40E_MAX_TRAFFIC_CLASS] = {false};
        struct i40e_dcbx_config *dcb_cfg;
        struct i40e_hw *hw = &pf->hw;
-       u16 i, v;
+       u16 i;
        u8 tc;
 
        dcb_cfg = &hw->local_dcbx_config;
@@ -809,6 +843,7 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf)
 
        for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
                u64 prio_xoff = nsd->priority_xoff_rx[i];
+
                i40e_stat_update32(hw, I40E_GLPRT_PXOFFRXC(hw->port, i),
                                   pf->stat_offsets_loaded,
                                   &osd->priority_xoff_rx[i],
@@ -821,23 +856,6 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf)
                tc = dcb_cfg->etscfg.prioritytable[i];
                xoff[tc] = true;
        }
-
-       /* Clear the __I40E_HANG_CHECK_ARMED bit for Tx rings */
-       for (v = 0; v < pf->num_alloc_vsi; v++) {
-               struct i40e_vsi *vsi = pf->vsi[v];
-
-               if (!vsi || !vsi->tx_rings[0])
-                       continue;
-
-               for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       struct i40e_ring *ring = vsi->tx_rings[i];
-
-                       tc = ring->dcb_tc;
-                       if (xoff[tc])
-                               clear_bit(__I40E_HANG_CHECK_ARMED,
-                                         &ring->state);
-               }
-       }
 }
 
 /**
@@ -862,6 +880,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
        u32 rx_page, rx_buf;
        u64 bytes, packets;
        unsigned int start;
+       u64 tx_linearize;
        u64 rx_p, rx_b;
        u64 tx_p, tx_b;
        u16 q;
@@ -880,7 +899,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
         */
        rx_b = rx_p = 0;
        tx_b = tx_p = 0;
-       tx_restart = tx_busy = 0;
+       tx_restart = tx_busy = tx_linearize = 0;
        rx_page = 0;
        rx_buf = 0;
        rcu_read_lock();
@@ -897,6 +916,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
                tx_p += packets;
                tx_restart += p->tx_stats.restart_queue;
                tx_busy += p->tx_stats.tx_busy;
+               tx_linearize += p->tx_stats.tx_linearize;
 
                /* Rx queue is part of the same block as Tx queue */
                p = &p[1];
@@ -913,6 +933,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
        rcu_read_unlock();
        vsi->tx_restart = tx_restart;
        vsi->tx_busy = tx_busy;
+       vsi->tx_linearize = tx_linearize;
        vsi->rx_page_failed = rx_page;
        vsi->rx_buf_failed = rx_buf;
 
@@ -1256,7 +1277,7 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi)
         * so we have to go through all the list in order to make sure
         */
        list_for_each_entry(f, &vsi->mac_filter_list, list) {
-               if (f->vlan >= 0)
+               if (f->vlan >= 0 || vsi->info.pvid)
                        return true;
        }
 
@@ -1334,6 +1355,9 @@ static int i40e_rm_default_mac_filter(struct i40e_vsi *vsi, u8 *macaddr)
  * @is_netdev: make sure its a netdev filter, else doesn't matter
  *
  * Returns ptr to the filter object or NULL when no memory available.
+ *
+ * NOTE: This function is expected to be called with mac_filter_list_lock
+ * being held.
  **/
 struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
                                        u8 *macaddr, s16 vlan,
@@ -1392,6 +1416,9 @@ add_filter_out:
  * @vlan: the vlan
  * @is_vf: make sure it's a VF filter, else doesn't matter
  * @is_netdev: make sure it's a netdev filter, else doesn't matter
+ *
+ * NOTE: This function is expected to be called with mac_filter_list_lock
+ * being held.
  **/
 void i40e_del_filter(struct i40e_vsi *vsi,
                     u8 *macaddr, s16 vlan,
@@ -1419,6 +1446,7 @@ void i40e_del_filter(struct i40e_vsi *vsi,
        } else {
                /* make sure we don't remove a filter in use by VF or netdev */
                int min_f = 0;
+
                min_f += (f->is_vf ? 1 : 0);
                min_f += (f->is_netdev ? 1 : 0);
 
@@ -1477,6 +1505,7 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
 
        if (vsi->type == I40E_VSI_MAIN) {
                i40e_status ret;
+
                ret = i40e_aq_mac_address_write(&vsi->back->hw,
                                                I40E_AQC_WRITE_TYPE_LAA_WOL,
                                                addr->sa_data, NULL);
@@ -1496,8 +1525,10 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
                element.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH;
                i40e_aq_remove_macvlan(&pf->hw, vsi->seid, &element, 1, NULL);
        } else {
+               spin_lock_bh(&vsi->mac_filter_list_lock);
                i40e_del_filter(vsi, netdev->dev_addr, I40E_VLAN_ANY,
                                false, false);
+               spin_unlock_bh(&vsi->mac_filter_list_lock);
        }
 
        if (ether_addr_equal(addr->sa_data, hw->mac.addr)) {
@@ -1508,13 +1539,15 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
                element.flags = cpu_to_le16(I40E_AQC_MACVLAN_ADD_PERFECT_MATCH);
                i40e_aq_add_macvlan(&pf->hw, vsi->seid, &element, 1, NULL);
        } else {
+               spin_lock_bh(&vsi->mac_filter_list_lock);
                f = i40e_add_filter(vsi, addr->sa_data, I40E_VLAN_ANY,
                                    false, false);
                if (f)
                        f->is_laa = true;
+               spin_unlock_bh(&vsi->mac_filter_list_lock);
        }
 
-       i40e_sync_vsi_filters(vsi);
+       i40e_sync_vsi_filters(vsi, false);
        ether_addr_copy(netdev->dev_addr, addr->sa_data);
 
        return 0;
@@ -1684,6 +1717,8 @@ static void i40e_set_rx_mode(struct net_device *netdev)
        struct netdev_hw_addr *mca;
        struct netdev_hw_addr *ha;
 
+       spin_lock_bh(&vsi->mac_filter_list_lock);
+
        /* add addr if not already in the filter list */
        netdev_for_each_uc_addr(uca, netdev) {
                if (!i40e_find_mac(vsi, uca->addr, false, true)) {
@@ -1709,37 +1744,29 @@ static void i40e_set_rx_mode(struct net_device *netdev)
 
        /* remove filter if not in netdev list */
        list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
-               bool found = false;
 
                if (!f->is_netdev)
                        continue;
 
-               if (is_multicast_ether_addr(f->macaddr)) {
-                       netdev_for_each_mc_addr(mca, netdev) {
-                               if (ether_addr_equal(mca->addr, f->macaddr)) {
-                                       found = true;
-                                       break;
-                               }
-                       }
-               } else {
-                       netdev_for_each_uc_addr(uca, netdev) {
-                               if (ether_addr_equal(uca->addr, f->macaddr)) {
-                                       found = true;
-                                       break;
-                               }
-                       }
+               netdev_for_each_mc_addr(mca, netdev)
+                       if (ether_addr_equal(mca->addr, f->macaddr))
+                               goto bottom_of_search_loop;
 
-                       for_each_dev_addr(netdev, ha) {
-                               if (ether_addr_equal(ha->addr, f->macaddr)) {
-                                       found = true;
-                                       break;
-                               }
-                       }
-               }
-               if (!found)
-                       i40e_del_filter(
-                          vsi, f->macaddr, I40E_VLAN_ANY, false, true);
+               netdev_for_each_uc_addr(uca, netdev)
+                       if (ether_addr_equal(uca->addr, f->macaddr))
+                               goto bottom_of_search_loop;
+
+               for_each_dev_addr(netdev, ha)
+                       if (ether_addr_equal(ha->addr, f->macaddr))
+                               goto bottom_of_search_loop;
+
+               /* f->macaddr wasn't found in uc, mc, or ha list so delete it */
+               i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY, false, true);
+
+bottom_of_search_loop:
+               continue;
        }
+       spin_unlock_bh(&vsi->mac_filter_list_lock);
 
        /* check for other flag changes */
        if (vsi->current_netdev_flags != vsi->netdev->flags) {
@@ -1748,21 +1775,97 @@ static void i40e_set_rx_mode(struct net_device *netdev)
        }
 }
 
+/**
+ * i40e_mac_filter_entry_clone - Clones a MAC filter entry
+ * @src: source MAC filter entry to be clones
+ *
+ * Returns the pointer to newly cloned MAC filter entry or NULL
+ * in case of error
+ **/
+static struct i40e_mac_filter *i40e_mac_filter_entry_clone(
+                                       struct i40e_mac_filter *src)
+{
+       struct i40e_mac_filter *f;
+
+       f = kzalloc(sizeof(*f), GFP_ATOMIC);
+       if (!f)
+               return NULL;
+       *f = *src;
+
+       INIT_LIST_HEAD(&f->list);
+
+       return f;
+}
+
+/**
+ * i40e_undo_del_filter_entries - Undo the changes made to MAC filter entries
+ * @vsi: pointer to vsi struct
+ * @from: Pointer to list which contains MAC filter entries - changes to
+ *        those entries needs to be undone.
+ *
+ * MAC filter entries from list were slated to be removed from device.
+ **/
+static void i40e_undo_del_filter_entries(struct i40e_vsi *vsi,
+                                        struct list_head *from)
+{
+       struct i40e_mac_filter *f, *ftmp;
+
+       list_for_each_entry_safe(f, ftmp, from, list) {
+               f->changed = true;
+               /* Move the element back into MAC filter list*/
+               list_move_tail(&f->list, &vsi->mac_filter_list);
+       }
+}
+
+/**
+ * i40e_undo_add_filter_entries - Undo the changes made to MAC filter entries
+ * @vsi: pointer to vsi struct
+ *
+ * MAC filter entries from list were slated to be added from device.
+ **/
+static void i40e_undo_add_filter_entries(struct i40e_vsi *vsi)
+{
+       struct i40e_mac_filter *f, *ftmp;
+
+       list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
+               if (!f->changed && f->counter)
+                       f->changed = true;
+       }
+}
+
+/**
+ * i40e_cleanup_add_list - Deletes the element from add list and release
+ *                     memory
+ * @add_list: Pointer to list which contains MAC filter entries
+ **/
+static void i40e_cleanup_add_list(struct list_head *add_list)
+{
+       struct i40e_mac_filter *f, *ftmp;
+
+       list_for_each_entry_safe(f, ftmp, add_list, list) {
+               list_del(&f->list);
+               kfree(f);
+       }
+}
+
 /**
  * i40e_sync_vsi_filters - Update the VSI filter list to the HW
  * @vsi: ptr to the VSI
+ * @grab_rtnl: whether RTNL needs to be grabbed
  *
  * Push any outstanding VSI filter changes through the AdminQ.
  *
  * Returns 0 or error value
  **/
-int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
+int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
 {
-       struct i40e_mac_filter *f, *ftmp;
+       struct list_head tmp_del_list, tmp_add_list;
+       struct i40e_mac_filter *f, *ftmp, *fclone;
        bool promisc_forced_on = false;
        bool add_happened = false;
        int filter_list_len = 0;
        u32 changed_flags = 0;
+       bool err_cond = false;
        i40e_status ret = 0;
        struct i40e_pf *pf;
        int num_add = 0;
@@ -1783,17 +1886,13 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                vsi->current_netdev_flags = vsi->netdev->flags;
        }
 
+       INIT_LIST_HEAD(&tmp_del_list);
+       INIT_LIST_HEAD(&tmp_add_list);
+
        if (vsi->flags & I40E_VSI_FLAG_FILTER_CHANGED) {
                vsi->flags &= ~I40E_VSI_FLAG_FILTER_CHANGED;
 
-               filter_list_len = pf->hw.aq.asq_buf_size /
-                           sizeof(struct i40e_aqc_remove_macvlan_element_data);
-               del_list = kcalloc(filter_list_len,
-                           sizeof(struct i40e_aqc_remove_macvlan_element_data),
-                           GFP_KERNEL);
-               if (!del_list)
-                       return -ENOMEM;
-
+               spin_lock_bh(&vsi->mac_filter_list_lock);
                list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
                        if (!f->changed)
                                continue;
@@ -1801,6 +1900,58 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                        if (f->counter != 0)
                                continue;
                        f->changed = false;
+
+                       /* Move the element into temporary del_list */
+                       list_move_tail(&f->list, &tmp_del_list);
+               }
+
+               list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
+                       if (!f->changed)
+                               continue;
+
+                       if (f->counter == 0)
+                               continue;
+                       f->changed = false;
+
+                       /* Clone MAC filter entry and add into temporary list */
+                       fclone = i40e_mac_filter_entry_clone(f);
+                       if (!fclone) {
+                               err_cond = true;
+                               break;
+                       }
+                       list_add_tail(&fclone->list, &tmp_add_list);
+               }
+
+               /* if failed to clone MAC filter entry - undo */
+               if (err_cond) {
+                       i40e_undo_del_filter_entries(vsi, &tmp_del_list);
+                       i40e_undo_add_filter_entries(vsi);
+               }
+               spin_unlock_bh(&vsi->mac_filter_list_lock);
+
+               if (err_cond)
+                       i40e_cleanup_add_list(&tmp_add_list);
+       }
+
+       /* Now process 'del_list' outside the lock */
+       if (!list_empty(&tmp_del_list)) {
+               filter_list_len = pf->hw.aq.asq_buf_size /
+                           sizeof(struct i40e_aqc_remove_macvlan_element_data);
+               del_list = kcalloc(filter_list_len,
+                           sizeof(struct i40e_aqc_remove_macvlan_element_data),
+                           GFP_KERNEL);
+               if (!del_list) {
+                       i40e_cleanup_add_list(&tmp_add_list);
+
+                       /* Undo VSI's MAC filter entry element updates */
+                       spin_lock_bh(&vsi->mac_filter_list_lock);
+                       i40e_undo_del_filter_entries(vsi, &tmp_del_list);
+                       i40e_undo_add_filter_entries(vsi);
+                       spin_unlock_bh(&vsi->mac_filter_list_lock);
+                       return -ENOMEM;
+               }
+
+               list_for_each_entry_safe(f, ftmp, &tmp_del_list, list) {
                        cmd_flags = 0;
 
                        /* add to delete list */
@@ -1813,10 +1964,6 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                        del_list[num_del].flags = cmd_flags;
                        num_del++;
 
-                       /* unlink from filter list */
-                       list_del(&f->list);
-                       kfree(f);
-
                        /* flush a full buffer */
                        if (num_del == filter_list_len) {
                                ret = i40e_aq_remove_macvlan(&pf->hw,
@@ -1827,12 +1974,18 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                                memset(del_list, 0, sizeof(*del_list));
 
                                if (ret && aq_err != I40E_AQ_RC_ENOENT)
-                                       dev_info(&pf->pdev->dev,
-                                                "ignoring delete macvlan error, err %s, aq_err %s while flushing a full buffer\n",
-                                                i40e_stat_str(&pf->hw, ret),
-                                                i40e_aq_str(&pf->hw, aq_err));
+                                       dev_err(&pf->pdev->dev,
+                                               "ignoring delete macvlan error, err %s, aq_err %s while flushing a full buffer\n",
+                                               i40e_stat_str(&pf->hw, ret),
+                                               i40e_aq_str(&pf->hw, aq_err));
                        }
+                       /* Release memory for MAC filter entries which were
+                        * synced up with HW.
+                        */
+                       list_del(&f->list);
+                       kfree(f);
                }
+
                if (num_del) {
                        ret = i40e_aq_remove_macvlan(&pf->hw, vsi->seid,
                                                     del_list, num_del, NULL);
@@ -1848,6 +2001,9 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
 
                kfree(del_list);
                del_list = NULL;
+       }
+
+       if (!list_empty(&tmp_add_list)) {
 
                /* do all the adds now */
                filter_list_len = pf->hw.aq.asq_buf_size /
@@ -1855,16 +2011,19 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                add_list = kcalloc(filter_list_len,
                               sizeof(struct i40e_aqc_add_macvlan_element_data),
                               GFP_KERNEL);
-               if (!add_list)
+               if (!add_list) {
+                       /* Purge element from temporary lists */
+                       i40e_cleanup_add_list(&tmp_add_list);
+
+                       /* Undo add filter entries from VSI MAC filter list */
+                       spin_lock_bh(&vsi->mac_filter_list_lock);
+                       i40e_undo_add_filter_entries(vsi);
+                       spin_unlock_bh(&vsi->mac_filter_list_lock);
                        return -ENOMEM;
+               }
 
-               list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
-                       if (!f->changed)
-                               continue;
+               list_for_each_entry_safe(f, ftmp, &tmp_add_list, list) {
 
-                       if (f->counter == 0)
-                               continue;
-                       f->changed = false;
                        add_happened = true;
                        cmd_flags = 0;
 
@@ -1891,7 +2050,13 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                                        break;
                                memset(add_list, 0, sizeof(*add_list));
                        }
+                       /* Entries from tmp_add_list were cloned from MAC
+                        * filter list, hence clean those cloned entries
+                        */
+                       list_del(&f->list);
+                       kfree(f);
                }
+
                if (num_add) {
                        ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
                                                  add_list, num_add, NULL);
@@ -1920,6 +2085,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
        /* check for changes in promiscuous modes */
        if (changed_flags & IFF_ALLMULTI) {
                bool cur_multipromisc;
+
                cur_multipromisc = !!(vsi->current_netdev_flags & IFF_ALLMULTI);
                ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw,
                                                            vsi->seid,
@@ -1934,6 +2100,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
        }
        if ((changed_flags & IFF_PROMISC) || promisc_forced_on) {
                bool cur_promisc;
+
                cur_promisc = (!!(vsi->current_netdev_flags & IFF_PROMISC) ||
                               test_bit(__I40E_FILTER_OVERFLOW_PROMISC,
                                        &vsi->state));
@@ -1945,7 +2112,11 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                         */
                        if (pf->cur_promisc != cur_promisc) {
                                pf->cur_promisc = cur_promisc;
-                               i40e_do_reset_safe(pf,
+                               if (grab_rtnl)
+                                       i40e_do_reset_safe(pf,
+                                               BIT(__I40E_PF_RESET_REQUESTED));
+                               else
+                                       i40e_do_reset(pf,
                                                BIT(__I40E_PF_RESET_REQUESTED));
                        }
                } else {
@@ -1996,7 +2167,7 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf)
        for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v] &&
                    (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED))
-                       i40e_sync_vsi_filters(pf->vsi[v]);
+                       i40e_sync_vsi_filters(pf->vsi[v], true);
        }
 }
 
@@ -2137,6 +2308,9 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
        is_vf = (vsi->type == I40E_VSI_SRIOV);
        is_netdev = !!(vsi->netdev);
 
+       /* Locked once because all functions invoked below iterates list*/
+       spin_lock_bh(&vsi->mac_filter_list_lock);
+
        if (is_netdev) {
                add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, vid,
                                        is_vf, is_netdev);
@@ -2144,6 +2318,7 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
                        dev_info(&vsi->back->pdev->dev,
                                 "Could not add vlan filter %d for %pM\n",
                                 vid, vsi->netdev->dev_addr);
+                       spin_unlock_bh(&vsi->mac_filter_list_lock);
                        return -ENOMEM;
                }
        }
@@ -2154,6 +2329,7 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
                        dev_info(&vsi->back->pdev->dev,
                                 "Could not add vlan filter %d for %pM\n",
                                 vid, f->macaddr);
+                       spin_unlock_bh(&vsi->mac_filter_list_lock);
                        return -ENOMEM;
                }
        }
@@ -2175,6 +2351,7 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
                                dev_info(&vsi->back->pdev->dev,
                                         "Could not add filter 0 for %pM\n",
                                         vsi->netdev->dev_addr);
+                               spin_unlock_bh(&vsi->mac_filter_list_lock);
                                return -ENOMEM;
                        }
                }
@@ -2183,27 +2360,33 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
        /* Do not assume that I40E_VLAN_ANY should be reset to VLAN 0 */
        if (vid > 0 && !vsi->info.pvid) {
                list_for_each_entry(f, &vsi->mac_filter_list, list) {
-                       if (i40e_find_filter(vsi, f->macaddr, I40E_VLAN_ANY,
-                                            is_vf, is_netdev)) {
-                               i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY,
-                                               is_vf, is_netdev);
-                               add_f = i40e_add_filter(vsi, f->macaddr,
-                                                       0, is_vf, is_netdev);
-                               if (!add_f) {
-                                       dev_info(&vsi->back->pdev->dev,
-                                                "Could not add filter 0 for %pM\n",
-                                                f->macaddr);
-                                       return -ENOMEM;
-                               }
+                       if (!i40e_find_filter(vsi, f->macaddr, I40E_VLAN_ANY,
+                                             is_vf, is_netdev))
+                               continue;
+                       i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY,
+                                       is_vf, is_netdev);
+                       add_f = i40e_add_filter(vsi, f->macaddr,
+                                               0, is_vf, is_netdev);
+                       if (!add_f) {
+                               dev_info(&vsi->back->pdev->dev,
+                                        "Could not add filter 0 for %pM\n",
+                                       f->macaddr);
+                               spin_unlock_bh(&vsi->mac_filter_list_lock);
+                               return -ENOMEM;
                        }
                }
        }
 
+       /* Make sure to release before sync_vsi_filter because that
+        * function will lock/unlock as necessary
+        */
+       spin_unlock_bh(&vsi->mac_filter_list_lock);
+
        if (test_bit(__I40E_DOWN, &vsi->back->state) ||
            test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state))
                return 0;
 
-       return i40e_sync_vsi_filters(vsi);
+       return i40e_sync_vsi_filters(vsi, false);
 }
 
 /**
@@ -2223,6 +2406,9 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid)
        is_vf = (vsi->type == I40E_VSI_SRIOV);
        is_netdev = !!(netdev);
 
+       /* Locked once because all functions invoked below iterates list */
+       spin_lock_bh(&vsi->mac_filter_list_lock);
+
        if (is_netdev)
                i40e_del_filter(vsi, netdev->dev_addr, vid, is_vf, is_netdev);
 
@@ -2253,6 +2439,7 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid)
                        dev_info(&vsi->back->pdev->dev,
                                 "Could not add filter %d for %pM\n",
                                 I40E_VLAN_ANY, netdev->dev_addr);
+                       spin_unlock_bh(&vsi->mac_filter_list_lock);
                        return -ENOMEM;
                }
        }
@@ -2261,21 +2448,27 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid)
                list_for_each_entry(f, &vsi->mac_filter_list, list) {
                        i40e_del_filter(vsi, f->macaddr, 0, is_vf, is_netdev);
                        add_f = i40e_add_filter(vsi, f->macaddr, I40E_VLAN_ANY,
-                                           is_vf, is_netdev);
+                                               is_vf, is_netdev);
                        if (!add_f) {
                                dev_info(&vsi->back->pdev->dev,
                                         "Could not add filter %d for %pM\n",
                                         I40E_VLAN_ANY, f->macaddr);
+                               spin_unlock_bh(&vsi->mac_filter_list_lock);
                                return -ENOMEM;
                        }
                }
        }
 
+       /* Make sure to release before sync_vsi_filter because that
+        * function with lock/unlock as necessary
+        */
+       spin_unlock_bh(&vsi->mac_filter_list_lock);
+
        if (test_bit(__I40E_DOWN, &vsi->back->state) ||
            test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state))
                return 0;
 
-       return i40e_sync_vsi_filters(vsi);
+       return i40e_sync_vsi_filters(vsi, false);
 }
 
 /**
@@ -2609,8 +2802,6 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring)
        wr32(hw, I40E_QTX_CTL(pf_q), qtx_ctl);
        i40e_flush(hw);
 
-       clear_bit(__I40E_HANG_CHECK_ARMED, &ring->state);
-
        /* cache tail off for easier writes later */
        ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q);
 
@@ -2882,11 +3073,9 @@ static int i40e_vsi_configure(struct i40e_vsi *vsi)
 static void i40e_vsi_configure_msix(struct i40e_vsi *vsi)
 {
        struct i40e_pf *pf = vsi->back;
-       struct i40e_q_vector *q_vector;
        struct i40e_hw *hw = &pf->hw;
        u16 vector;
        int i, q;
-       u32 val;
        u32 qp;
 
        /* The interrupt indexing is offset by 1 in the PFINT_ITRn
@@ -2896,7 +3085,9 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi)
        qp = vsi->base_queue;
        vector = vsi->base_vector;
        for (i = 0; i < vsi->num_q_vectors; i++, vector++) {
-               q_vector = vsi->q_vectors[i];
+               struct i40e_q_vector *q_vector = vsi->q_vectors[i];
+
+               q_vector->itr_countdown = ITR_COUNTDOWN_START;
                q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting);
                q_vector->rx.latency_range = I40E_LOW_LATENCY;
                wr32(hw, I40E_PFINT_ITRN(I40E_RX_ITR, vector - 1),
@@ -2905,10 +3096,14 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi)
                q_vector->tx.latency_range = I40E_LOW_LATENCY;
                wr32(hw, I40E_PFINT_ITRN(I40E_TX_ITR, vector - 1),
                     q_vector->tx.itr);
+               wr32(hw, I40E_PFINT_RATEN(vector - 1),
+                    INTRL_USEC_TO_REG(vsi->int_rate_limit));
 
                /* Linked list for the queuepairs assigned to this vector */
                wr32(hw, I40E_PFINT_LNKLSTN(vector - 1), qp);
                for (q = 0; q < q_vector->num_ringpairs; q++) {
+                       u32 val;
+
                        val = I40E_QINT_RQCTL_CAUSE_ENA_MASK |
                              (I40E_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT)  |
                              (vector      << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) |
@@ -2988,6 +3183,7 @@ static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi)
        u32 val;
 
        /* set the ITR configuration */
+       q_vector->itr_countdown = ITR_COUNTDOWN_START;
        q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting);
        q_vector->rx.latency_range = I40E_LOW_LATENCY;
        wr32(hw, I40E_PFINT_ITR0(I40E_RX_ITR), q_vector->rx.itr);
@@ -3045,24 +3241,6 @@ void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf)
        i40e_flush(hw);
 }
 
-/**
- * i40e_irq_dynamic_enable - Enable default interrupt generation settings
- * @vsi: pointer to a vsi
- * @vector: enable a particular Hw Interrupt vector
- **/
-void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector)
-{
-       struct i40e_pf *pf = vsi->back;
-       struct i40e_hw *hw = &pf->hw;
-       u32 val;
-
-       val = I40E_PFINT_DYN_CTLN_INTENA_MASK |
-             I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
-             (I40E_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT);
-       wr32(hw, I40E_PFINT_DYN_CTLN(vector - 1), val);
-       /* skip the flush */
-}
-
 /**
  * i40e_irq_dynamic_disable - Disable default interrupt generation settings
  * @vsi: pointer to a vsi
@@ -3091,7 +3269,7 @@ static irqreturn_t i40e_msix_clean_rings(int irq, void *data)
        if (!q_vector->tx.ring && !q_vector->rx.ring)
                return IRQ_HANDLED;
 
-       napi_schedule(&q_vector->napi);
+       napi_schedule_irqoff(&q_vector->napi);
 
        return IRQ_HANDLED;
 }
@@ -3136,8 +3314,7 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename)
                                  q_vector);
                if (err) {
                        dev_info(&pf->pdev->dev,
-                                "%s: request_irq failed, error: %d\n",
-                                __func__, err);
+                                "MSIX request_irq failed, error: %d\n", err);
                        goto free_queue_irqs;
                }
                /* assign the mask for this irq */
@@ -3202,8 +3379,7 @@ static int i40e_vsi_enable_irq(struct i40e_vsi *vsi)
        int i;
 
        if (pf->flags & I40E_FLAG_MSIX_ENABLED) {
-               for (i = vsi->base_vector;
-                    i < (vsi->num_q_vectors + vsi->base_vector); i++)
+               for (i = 0; i < vsi->num_q_vectors; i++)
                        i40e_irq_dynamic_enable(vsi, i);
        } else {
                i40e_irq_dynamic_enable_icr0(pf);
@@ -3262,9 +3438,12 @@ static irqreturn_t i40e_intr(int irq, void *data)
 
        /* only q0 is used in MSI/Legacy mode, and none are used in MSIX */
        if (icr0 & I40E_PFINT_ICR0_QUEUE_0_MASK) {
+               struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
+               struct i40e_q_vector *q_vector = vsi->q_vectors[0];
 
                /* temporarily disable queue cause for NAPI processing */
                u32 qval = rd32(hw, I40E_QINT_RQCTL(0));
+
                qval &= ~I40E_QINT_RQCTL_CAUSE_ENA_MASK;
                wr32(hw, I40E_QINT_RQCTL(0), qval);
 
@@ -3273,7 +3452,7 @@ static irqreturn_t i40e_intr(int irq, void *data)
                wr32(hw, I40E_QINT_TQCTL(0), qval);
 
                if (!test_bit(__I40E_DOWN, &pf->state))
-                       napi_schedule(&pf->vsi[pf->lan_vsi]->q_vectors[0]->napi);
+                       napi_schedule_irqoff(&q_vector->napi);
        }
 
        if (icr0 & I40E_PFINT_ICR0_ADMINQ_MASK) {
@@ -3434,10 +3613,9 @@ static bool i40e_clean_fdir_tx_irq(struct i40e_ring *tx_ring, int budget)
        i += tx_ring->count;
        tx_ring->next_to_clean = i;
 
-       if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) {
-               i40e_irq_dynamic_enable(vsi,
-                               tx_ring->q_vector->v_idx + vsi->base_vector);
-       }
+       if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED)
+               i40e_irq_dynamic_enable(vsi, tx_ring->q_vector->v_idx);
+
        return budget > 0;
 }
 
@@ -3575,14 +3753,12 @@ static void i40e_netpoll(struct net_device *netdev)
        if (test_bit(__I40E_DOWN, &vsi->state))
                return;
 
-       pf->flags |= I40E_FLAG_IN_NETPOLL;
        if (pf->flags & I40E_FLAG_MSIX_ENABLED) {
                for (i = 0; i < vsi->num_q_vectors; i++)
                        i40e_msix_clean_rings(0, vsi->q_vectors[i]);
        } else {
                i40e_intr(pf->pdev->irq, netdev);
        }
-       pf->flags &= ~I40E_FLAG_IN_NETPOLL;
 }
 #endif
 
@@ -3663,9 +3839,8 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable)
                ret = i40e_pf_txq_wait(pf, pf_q, enable);
                if (ret) {
                        dev_info(&pf->pdev->dev,
-                                "%s: VSI seid %d Tx ring %d %sable timeout\n",
-                                __func__, vsi->seid, pf_q,
-                                (enable ? "en" : "dis"));
+                                "VSI seid %d Tx ring %d %sable timeout\n",
+                                vsi->seid, pf_q, (enable ? "en" : "dis"));
                        break;
                }
        }
@@ -3741,9 +3916,8 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable)
                ret = i40e_pf_rxq_wait(pf, pf_q, enable);
                if (ret) {
                        dev_info(&pf->pdev->dev,
-                                "%s: VSI seid %d Rx ring %d %sable timeout\n",
-                                __func__, vsi->seid, pf_q,
-                                (enable ? "en" : "dis"));
+                                "VSI seid %d Rx ring %d %sable timeout\n",
+                                vsi->seid, pf_q, (enable ? "en" : "dis"));
                        break;
                }
        }
@@ -4038,17 +4212,15 @@ static void i40e_quiesce_vsi(struct i40e_vsi *vsi)
        if ((test_bit(__I40E_PORT_TX_SUSPENDED, &vsi->back->state)) &&
            vsi->type == I40E_VSI_FCOE) {
                dev_dbg(&vsi->back->pdev->dev,
-                       "%s: VSI seid %d skipping FCoE VSI disable\n",
-                        __func__, vsi->seid);
+                        "VSI seid %d skipping FCoE VSI disable\n", vsi->seid);
                return;
        }
 
        set_bit(__I40E_NEEDS_RESTART, &vsi->state);
-       if (vsi->netdev && netif_running(vsi->netdev)) {
+       if (vsi->netdev && netif_running(vsi->netdev))
                vsi->netdev->netdev_ops->ndo_stop(vsi->netdev);
-       } else {
+       else
                i40e_vsi_close(vsi);
-       }
 }
 
 /**
@@ -4113,8 +4285,8 @@ static int i40e_vsi_wait_txq_disabled(struct i40e_vsi *vsi)
                ret = i40e_pf_txq_wait(pf, pf_q, false);
                if (ret) {
                        dev_info(&pf->pdev->dev,
-                                "%s: VSI seid %d Tx ring %d disable timeout\n",
-                                __func__, vsi->seid, pf_q);
+                                "VSI seid %d Tx ring %d disable timeout\n",
+                                vsi->seid, pf_q);
                        return ret;
                }
        }
@@ -4146,6 +4318,108 @@ static int i40e_pf_wait_txq_disabled(struct i40e_pf *pf)
 }
 
 #endif
+
+/**
+ * i40e_detect_recover_hung_queue - Function to detect and recover hung_queue
+ * @q_idx: TX queue number
+ * @vsi: Pointer to VSI struct
+ *
+ * This function checks specified queue for given VSI. Detects hung condition.
+ * Sets hung bit since it is two step process. Before next run of service task
+ * if napi_poll runs, it reset 'hung' bit for respective q_vector. If not,
+ * hung condition remain unchanged and during subsequent run, this function
+ * issues SW interrupt to recover from hung condition.
+ **/
+static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi)
+{
+       struct i40e_ring *tx_ring = NULL;
+       struct i40e_pf  *pf;
+       u32 head, val, tx_pending;
+       int i;
+
+       pf = vsi->back;
+
+       /* now that we have an index, find the tx_ring struct */
+       for (i = 0; i < vsi->num_queue_pairs; i++) {
+               if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc) {
+                       if (q_idx == vsi->tx_rings[i]->queue_index) {
+                               tx_ring = vsi->tx_rings[i];
+                               break;
+                       }
+               }
+       }
+
+       if (!tx_ring)
+               return;
+
+       /* Read interrupt register */
+       if (pf->flags & I40E_FLAG_MSIX_ENABLED)
+               val = rd32(&pf->hw,
+                          I40E_PFINT_DYN_CTLN(tx_ring->q_vector->v_idx +
+                                              tx_ring->vsi->base_vector - 1));
+       else
+               val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0);
+
+       head = i40e_get_head(tx_ring);
+
+       tx_pending = i40e_get_tx_pending(tx_ring);
+
+       /* Interrupts are disabled and TX pending is non-zero,
+        * trigger the SW interrupt (don't wait). Worst case
+        * there will be one extra interrupt which may result
+        * into not cleaning any queues because queues are cleaned.
+        */
+       if (tx_pending && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK)))
+               i40e_force_wb(vsi, tx_ring->q_vector);
+}
+
+/**
+ * i40e_detect_recover_hung - Function to detect and recover hung_queues
+ * @pf:  pointer to PF struct
+ *
+ * LAN VSI has netdev and netdev has TX queues. This function is to check
+ * each of those TX queues if they are hung, trigger recovery by issuing
+ * SW interrupt.
+ **/
+static void i40e_detect_recover_hung(struct i40e_pf *pf)
+{
+       struct net_device *netdev;
+       struct i40e_vsi *vsi;
+       int i;
+
+       /* Only for LAN VSI */
+       vsi = pf->vsi[pf->lan_vsi];
+
+       if (!vsi)
+               return;
+
+       /* Make sure, VSI state is not DOWN/RECOVERY_PENDING */
+       if (test_bit(__I40E_DOWN, &vsi->back->state) ||
+           test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state))
+               return;
+
+       /* Make sure type is MAIN VSI */
+       if (vsi->type != I40E_VSI_MAIN)
+               return;
+
+       netdev = vsi->netdev;
+       if (!netdev)
+               return;
+
+       /* Bail out if netif_carrier is not OK */
+       if (!netif_carrier_ok(netdev))
+               return;
+
+       /* Go thru' TX queues for netdev */
+       for (i = 0; i < netdev->num_tx_queues; i++) {
+               struct netdev_queue *q;
+
+               q = netdev_get_tx_queue(netdev, i);
+               if (q)
+                       i40e_detect_recover_hung_queue(i, vsi);
+       }
+}
+
 /**
  * i40e_get_iscsi_tc_map - Return TC map for iSCSI APP
  * @pf: pointer to PF
@@ -4745,11 +5019,14 @@ out:
  * i40e_print_link_message - print link up or down
  * @vsi: the VSI for which link needs a message
  */
-static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup)
+void i40e_print_link_message(struct i40e_vsi *vsi, bool isup)
 {
-       char speed[SPEED_SIZE] = "Unknown";
-       char fc[FC_SIZE] = "RX/TX";
+       char *speed = "Unknown";
+       char *fc = "Unknown";
 
+       if (vsi->current_isup == isup)
+               return;
+       vsi->current_isup = isup;
        if (!isup) {
                netdev_info(vsi->netdev, "NIC Link is Down\n");
                return;
@@ -4766,19 +5043,19 @@ static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup)
 
        switch (vsi->back->hw.phy.link_info.link_speed) {
        case I40E_LINK_SPEED_40GB:
-               strlcpy(speed, "40 Gbps", SPEED_SIZE);
+               speed = "40 G";
                break;
        case I40E_LINK_SPEED_20GB:
-               strncpy(speed, "20 Gbps", SPEED_SIZE);
+               speed = "20 G";
                break;
        case I40E_LINK_SPEED_10GB:
-               strlcpy(speed, "10 Gbps", SPEED_SIZE);
+               speed = "10 G";
                break;
        case I40E_LINK_SPEED_1GB:
-               strlcpy(speed, "1000 Mbps", SPEED_SIZE);
+               speed = "1000 M";
                break;
        case I40E_LINK_SPEED_100MB:
-               strncpy(speed, "100 Mbps", SPEED_SIZE);
+               speed = "100 M";
                break;
        default:
                break;
@@ -4786,20 +5063,20 @@ static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup)
 
        switch (vsi->back->hw.fc.current_mode) {
        case I40E_FC_FULL:
-               strlcpy(fc, "RX/TX", FC_SIZE);
+               fc = "RX/TX";
                break;
        case I40E_FC_TX_PAUSE:
-               strlcpy(fc, "TX", FC_SIZE);
+               fc = "TX";
                break;
        case I40E_FC_RX_PAUSE:
-               strlcpy(fc, "RX", FC_SIZE);
+               fc = "RX";
                break;
        default:
-               strlcpy(fc, "None", FC_SIZE);
+               fc = "None";
                break;
        }
 
-       netdev_info(vsi->netdev, "NIC Link is Up %s Full Duplex, Flow Control: %s\n",
+       netdev_info(vsi->netdev, "NIC Link is Up %sbps Full Duplex, Flow Control: %s\n",
                    speed, fc);
 }
 
@@ -5218,15 +5495,13 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
                         "VSI reinit requested\n");
                for (v = 0; v < pf->num_alloc_vsi; v++) {
                        struct i40e_vsi *vsi = pf->vsi[v];
+
                        if (vsi != NULL &&
                            test_bit(__I40E_REINIT_REQUESTED, &vsi->state)) {
                                i40e_vsi_reinit_locked(pf->vsi[v]);
                                clear_bit(__I40E_REINIT_REQUESTED, &vsi->state);
                        }
                }
-
-               /* no further action needed, so return now */
-               return;
        } else if (reset_flags & BIT_ULL(__I40E_DOWN_REQUESTED)) {
                int v;
 
@@ -5234,6 +5509,7 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
                dev_info(&pf->pdev->dev, "VSI down requested\n");
                for (v = 0; v < pf->num_alloc_vsi; v++) {
                        struct i40e_vsi *vsi = pf->vsi[v];
+
                        if (vsi != NULL &&
                            test_bit(__I40E_DOWN_REQUESTED, &vsi->state)) {
                                set_bit(__I40E_DOWN, &vsi->state);
@@ -5241,13 +5517,9 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
                                clear_bit(__I40E_DOWN_REQUESTED, &vsi->state);
                        }
                }
-
-               /* no further action needed, so return now */
-               return;
        } else {
                dev_info(&pf->pdev->dev,
                         "bad reset request 0x%08x\n", reset_flags);
-               return;
        }
 }
 
@@ -5303,8 +5575,7 @@ bool i40e_dcb_need_reconfig(struct i40e_pf *pf,
                dev_dbg(&pf->pdev->dev, "APP Table change detected.\n");
        }
 
-       dev_dbg(&pf->pdev->dev, "%s: need_reconfig=%d\n", __func__,
-               need_reconfig);
+       dev_dbg(&pf->pdev->dev, "dcb need_reconfig=%d\n", need_reconfig);
        return need_reconfig;
 }
 
@@ -5331,16 +5602,14 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf,
        /* Ignore if event is not for Nearest Bridge */
        type = ((mib->type >> I40E_AQ_LLDP_BRIDGE_TYPE_SHIFT)
                & I40E_AQ_LLDP_BRIDGE_TYPE_MASK);
-       dev_dbg(&pf->pdev->dev,
-               "%s: LLDP event mib bridge type 0x%x\n", __func__, type);
+       dev_dbg(&pf->pdev->dev, "LLDP event mib bridge type 0x%x\n", type);
        if (type != I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE)
                return ret;
 
        /* Check MIB Type and return if event for Remote MIB update */
        type = mib->type & I40E_AQ_LLDP_MIB_TYPE_MASK;
        dev_dbg(&pf->pdev->dev,
-               "%s: LLDP event mib type %s\n", __func__,
-               type ? "remote" : "local");
+               "LLDP event mib type %s\n", type ? "remote" : "local");
        if (type == I40E_AQ_LLDP_MIB_REMOTE) {
                /* Update the remote cached instance and return */
                ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE,
@@ -5525,7 +5794,9 @@ u32 i40e_get_global_fd_count(struct i40e_pf *pf)
  **/
 void i40e_fdir_check_and_reenable(struct i40e_pf *pf)
 {
+       struct i40e_fdir_filter *filter;
        u32 fcnt_prog, fcnt_avail;
+       struct hlist_node *node;
 
        if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state))
                return;
@@ -5554,6 +5825,18 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf)
                                dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table now\n");
                }
        }
+
+       /* if hw had a problem adding a filter, delete it */
+       if (pf->fd_inv > 0) {
+               hlist_for_each_entry_safe(filter, node,
+                                         &pf->fdir_filter_list, fdir_node) {
+                       if (filter->fd_id == pf->fd_inv) {
+                               hlist_del(&filter->fdir_node);
+                               kfree(filter);
+                               pf->fdir_pf_active_filters--;
+                       }
+               }
+       }
 }
 
 #define I40E_MIN_FD_FLUSH_INTERVAL 10
@@ -5573,49 +5856,51 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf)
        if (!(pf->flags & (I40E_FLAG_FD_SB_ENABLED | I40E_FLAG_FD_ATR_ENABLED)))
                return;
 
-       if (time_after(jiffies, pf->fd_flush_timestamp +
-                               (I40E_MIN_FD_FLUSH_INTERVAL * HZ))) {
-               /* If the flush is happening too quick and we have mostly
-                * SB rules we should not re-enable ATR for some time.
-                */
-               min_flush_time = pf->fd_flush_timestamp
-                               + (I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE * HZ);
-               fd_room = pf->fdir_pf_filter_count - pf->fdir_pf_active_filters;
+       if (!time_after(jiffies, pf->fd_flush_timestamp +
+                                (I40E_MIN_FD_FLUSH_INTERVAL * HZ)))
+               return;
 
-               if (!(time_after(jiffies, min_flush_time)) &&
-                   (fd_room < I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) {
-                       if (I40E_DEBUG_FD & pf->hw.debug_mask)
-                               dev_info(&pf->pdev->dev, "ATR disabled, not enough FD filter space.\n");
-                       disable_atr = true;
-               }
+       /* If the flush is happening too quick and we have mostly SB rules we
+        * should not re-enable ATR for some time.
+        */
+       min_flush_time = pf->fd_flush_timestamp +
+                        (I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE * HZ);
+       fd_room = pf->fdir_pf_filter_count - pf->fdir_pf_active_filters;
 
-               pf->fd_flush_timestamp = jiffies;
-               pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
-               /* flush all filters */
-               wr32(&pf->hw, I40E_PFQF_CTL_1,
-                    I40E_PFQF_CTL_1_CLEARFDTABLE_MASK);
-               i40e_flush(&pf->hw);
-               pf->fd_flush_cnt++;
-               pf->fd_add_err = 0;
-               do {
-                       /* Check FD flush status every 5-6msec */
-                       usleep_range(5000, 6000);
-                       reg = rd32(&pf->hw, I40E_PFQF_CTL_1);
-                       if (!(reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK))
-                               break;
-               } while (flush_wait_retry--);
-               if (reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK) {
-                       dev_warn(&pf->pdev->dev, "FD table did not flush, needs more time\n");
-               } else {
-                       /* replay sideband filters */
-                       i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]);
-                       if (!disable_atr)
-                               pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
-                       clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state);
-                       if (I40E_DEBUG_FD & pf->hw.debug_mask)
-                               dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n");
-               }
+       if (!(time_after(jiffies, min_flush_time)) &&
+           (fd_room < I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) {
+               if (I40E_DEBUG_FD & pf->hw.debug_mask)
+                       dev_info(&pf->pdev->dev, "ATR disabled, not enough FD filter space.\n");
+               disable_atr = true;
+       }
+
+       pf->fd_flush_timestamp = jiffies;
+       pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+       /* flush all filters */
+       wr32(&pf->hw, I40E_PFQF_CTL_1,
+            I40E_PFQF_CTL_1_CLEARFDTABLE_MASK);
+       i40e_flush(&pf->hw);
+       pf->fd_flush_cnt++;
+       pf->fd_add_err = 0;
+       do {
+               /* Check FD flush status every 5-6msec */
+               usleep_range(5000, 6000);
+               reg = rd32(&pf->hw, I40E_PFQF_CTL_1);
+               if (!(reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK))
+                       break;
+       } while (flush_wait_retry--);
+       if (reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK) {
+               dev_warn(&pf->pdev->dev, "FD table did not flush, needs more time\n");
+       } else {
+               /* replay sideband filters */
+               i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]);
+               if (!disable_atr)
+                       pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
+               clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state);
+               if (I40E_DEBUG_FD & pf->hw.debug_mask)
+                       dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n");
        }
+
 }
 
 /**
@@ -5723,15 +6008,23 @@ static void i40e_veb_link_event(struct i40e_veb *veb, bool link_up)
  **/
 static void i40e_link_event(struct i40e_pf *pf)
 {
-       bool new_link, old_link;
        struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
        u8 new_link_speed, old_link_speed;
+       i40e_status status;
+       bool new_link, old_link;
 
        /* set this to force the get_link_status call to refresh state */
        pf->hw.phy.get_link_info = true;
 
        old_link = (pf->hw.phy.link_info_old.link_info & I40E_AQ_LINK_UP);
-       new_link = i40e_get_link_status(&pf->hw);
+
+       status = i40e_get_link_status(&pf->hw, &new_link);
+       if (status) {
+               dev_dbg(&pf->pdev->dev, "couldn't get link state, status: %d\n",
+                       status);
+               return;
+       }
+
        old_link_speed = pf->hw.phy.link_info_old.link_speed;
        new_link_speed = pf->hw.phy.link_info.link_speed;
 
@@ -5759,68 +6052,6 @@ static void i40e_link_event(struct i40e_pf *pf)
                i40e_ptp_set_increment(pf);
 }
 
-/**
- * i40e_check_hang_subtask - Check for hung queues and dropped interrupts
- * @pf: board private structure
- *
- * Set the per-queue flags to request a check for stuck queues in the irq
- * clean functions, then force interrupts to be sure the irq clean is called.
- **/
-static void i40e_check_hang_subtask(struct i40e_pf *pf)
-{
-       int i, v;
-
-       /* If we're down or resetting, just bail */
-       if (test_bit(__I40E_DOWN, &pf->state) ||
-           test_bit(__I40E_CONFIG_BUSY, &pf->state))
-               return;
-
-       /* for each VSI/netdev
-        *     for each Tx queue
-        *         set the check flag
-        *     for each q_vector
-        *         force an interrupt
-        */
-       for (v = 0; v < pf->num_alloc_vsi; v++) {
-               struct i40e_vsi *vsi = pf->vsi[v];
-               int armed = 0;
-
-               if (!pf->vsi[v] ||
-                   test_bit(__I40E_DOWN, &vsi->state) ||
-                   (vsi->netdev && !netif_carrier_ok(vsi->netdev)))
-                       continue;
-
-               for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       set_check_for_tx_hang(vsi->tx_rings[i]);
-                       if (test_bit(__I40E_HANG_CHECK_ARMED,
-                                    &vsi->tx_rings[i]->state))
-                               armed++;
-               }
-
-               if (armed) {
-                       if (!(pf->flags & I40E_FLAG_MSIX_ENABLED)) {
-                               wr32(&vsi->back->hw, I40E_PFINT_DYN_CTL0,
-                                    (I40E_PFINT_DYN_CTL0_INTENA_MASK |
-                                     I40E_PFINT_DYN_CTL0_SWINT_TRIG_MASK |
-                                     I40E_PFINT_DYN_CTL0_ITR_INDX_MASK |
-                                     I40E_PFINT_DYN_CTL0_SW_ITR_INDX_ENA_MASK |
-                                     I40E_PFINT_DYN_CTL0_SW_ITR_INDX_MASK));
-                       } else {
-                               u16 vec = vsi->base_vector - 1;
-                               u32 val = (I40E_PFINT_DYN_CTLN_INTENA_MASK |
-                                     I40E_PFINT_DYN_CTLN_SWINT_TRIG_MASK |
-                                     I40E_PFINT_DYN_CTLN_ITR_INDX_MASK |
-                                     I40E_PFINT_DYN_CTLN_SW_ITR_INDX_ENA_MASK |
-                                     I40E_PFINT_DYN_CTLN_SW_ITR_INDX_MASK);
-                               for (i = 0; i < vsi->num_q_vectors; i++, vec++)
-                                       wr32(&vsi->back->hw,
-                                            I40E_PFINT_DYN_CTLN(vec), val);
-                       }
-                       i40e_flush(&vsi->back->hw);
-               }
-       }
-}
-
 /**
  * i40e_watchdog_subtask - periodic checks not using event driven response
  * @pf: board private structure
@@ -5840,8 +6071,8 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf)
                return;
        pf->service_timer_previous = jiffies;
 
-       i40e_check_hang_subtask(pf);
-       i40e_link_event(pf);
+       if (pf->flags & I40E_FLAG_LINK_POLLING_ENABLED)
+               i40e_link_event(pf);
 
        /* Update the stats for active netdevs so the network stack
         * can look at updated numbers whenever it cares to
@@ -5850,10 +6081,12 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf)
                if (pf->vsi[i] && pf->vsi[i]->netdev)
                        i40e_update_stats(pf->vsi[i]);
 
-       /* Update the stats for the active switching components */
-       for (i = 0; i < I40E_MAX_VEB; i++)
-               if (pf->veb[i])
-                       i40e_update_veb_stats(pf->veb[i]);
+       if (pf->flags & I40E_FLAG_VEB_STATS_ENABLED) {
+               /* Update the stats for the active switching components */
+               for (i = 0; i < I40E_MAX_VEB; i++)
+                       if (pf->veb[i])
+                               i40e_update_veb_stats(pf->veb[i]);
+       }
 
        i40e_ptp_rx_hang(pf->vsi[pf->lan_vsi]);
 }
@@ -6164,8 +6397,9 @@ static void i40e_config_bridge_mode(struct i40e_veb *veb)
 {
        struct i40e_pf *pf = veb->pf;
 
-       dev_info(&pf->pdev->dev, "enabling bridge mode: %s\n",
-                veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB");
+       if (pf->hw.debug_mask & I40E_DEBUG_LAN)
+               dev_info(&pf->pdev->dev, "enabling bridge mode: %s\n",
+                        veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB");
        if (veb->bridge_mode & BRIDGE_MODE_VEPA)
                i40e_disable_pf_switch_lb(pf);
        else
@@ -6232,6 +6466,7 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb)
 
                if (pf->vsi[v]->veb_idx == veb->idx) {
                        struct i40e_vsi *vsi = pf->vsi[v];
+
                        vsi->uplink_seid = veb->seid;
                        ret = i40e_add_vsi(vsi);
                        if (ret) {
@@ -6296,12 +6531,6 @@ static int i40e_get_capabilities(struct i40e_pf *pf)
                }
        } while (err);
 
-       if (((pf->hw.aq.fw_maj_ver == 2) && (pf->hw.aq.fw_min_ver < 22)) ||
-           (pf->hw.aq.fw_maj_ver < 2)) {
-               pf->hw.func_caps.num_msix_vectors++;
-               pf->hw.func_caps.num_msix_vectors_vf++;
-       }
-
        if (pf->hw.debug_mask & I40E_DEBUG_USER)
                dev_info(&pf->pdev->dev,
                         "pf=%d, num_vfs=%d, msix_pf=%d, msix_vf=%d, fd_g=%d, fd_b=%d, pf_max_q=%d num_vsi=%d\n",
@@ -6514,9 +6743,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
        }
 #endif /* CONFIG_I40E_DCB */
 #ifdef I40E_FCOE
-       ret = i40e_init_pf_fcoe(pf);
-       if (ret)
-               dev_info(&pf->pdev->dev, "init_pf_fcoe failed: %d\n", ret);
+       i40e_init_pf_fcoe(pf);
 
 #endif
        /* do basic switch setup */
@@ -6538,9 +6765,9 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
        /* make sure our flow control settings are restored */
        ret = i40e_set_fc(&pf->hw, &set_fc_aq_fail, true);
        if (ret)
-               dev_info(&pf->pdev->dev, "set fc fail, err %s aq_err %s\n",
-                        i40e_stat_str(&pf->hw, ret),
-                        i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
+               dev_dbg(&pf->pdev->dev, "setting flow control: ret = %s last_status = %s\n",
+                       i40e_stat_str(&pf->hw, ret),
+                       i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
 
        /* Rebuild the VSIs and VEBs that existed before reset.
         * They are still in our local switch element arrays, so only
@@ -6610,6 +6837,15 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
        if (pf->flags & I40E_FLAG_MSIX_ENABLED)
                ret = i40e_setup_misc_vector(pf);
 
+       /* Add a filter to drop all Flow control frames from any VSI from being
+        * transmitted. By doing so we stop a malicious VF from sending out
+        * PAUSE or PFC frames and potentially controlling traffic for other
+        * PF/VF VSIs.
+        * The FW can still send Flow control frames if enabled.
+        */
+       i40e_add_filter_to_drop_tx_flow_control_frames(&pf->hw,
+                                                      pf->main_vsi_seid);
+
        /* restart the VSIs that were rebuilt and running before the reset */
        i40e_pf_unquiesce_all_vsi(pf);
 
@@ -6808,6 +7044,7 @@ static void i40e_service_task(struct work_struct *work)
                return;
        }
 
+       i40e_detect_recover_hung(pf);
        i40e_reset_subtask(pf);
        i40e_handle_mdd_event(pf);
        i40e_vc_process_vflr_event(pf);
@@ -6991,6 +7228,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
        vsi->idx = vsi_idx;
        vsi->rx_itr_setting = pf->rx_itr_default;
        vsi->tx_itr_setting = pf->tx_itr_default;
+       vsi->int_rate_limit = 0;
        vsi->rss_table_size = (vsi->type == I40E_VSI_MAIN) ?
                                pf->rss_table_size : 64;
        vsi->netdev_registered = false;
@@ -7009,6 +7247,8 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
        /* Setup default MSIX irq handler for VSI */
        i40e_vsi_setup_irqhandler(vsi, i40e_msix_clean_rings);
 
+       /* Initialize VSI lock */
+       spin_lock_init(&vsi->mac_filter_list_lock);
        pf->vsi[vsi_idx] = vsi;
        ret = vsi_idx;
        goto unlock_pf;
@@ -7566,7 +7806,7 @@ static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed)
                         "Cannot set RSS key, err %s aq_err %s\n",
                         i40e_stat_str(&pf->hw, ret),
                         i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
-               return ret;
+               goto config_rss_aq_out;
        }
 
        if (vsi->type == I40E_VSI_MAIN)
@@ -7580,6 +7820,8 @@ static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed)
                         i40e_stat_str(&pf->hw, ret),
                         i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
 
+config_rss_aq_out:
+       kfree(rss_lut);
        return ret;
 }
 
@@ -7854,6 +8096,7 @@ static int i40e_sw_init(struct i40e_pf *pf)
        /* Set default capability flags */
        pf->flags = I40E_FLAG_RX_CSUM_ENABLED |
                    I40E_FLAG_MSI_ENABLED     |
+                   I40E_FLAG_LINK_POLLING_ENABLED |
                    I40E_FLAG_MSIX_ENABLED;
 
        if (iommu_present(&pci_bus_type))
@@ -7896,12 +8139,12 @@ static int i40e_sw_init(struct i40e_pf *pf)
            (pf->hw.func_caps.fd_filters_best_effort > 0)) {
                pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
                pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE;
-               if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) {
-                       pf->flags |= I40E_FLAG_FD_SB_ENABLED;
-               } else {
+               if (pf->flags & I40E_FLAG_MFP_ENABLED &&
+                   pf->hw.num_partitions > 1)
                        dev_info(&pf->pdev->dev,
                                 "Flow Director Sideband mode Disabled in MFP mode\n");
-               }
+               else
+                       pf->flags |= I40E_FLAG_FD_SB_ENABLED;
                pf->fdir_pf_filter_count =
                                 pf->hw.func_caps.fd_filters_guaranteed;
                pf->hw.fdir_shared_filter_count =
@@ -7911,12 +8154,11 @@ static int i40e_sw_init(struct i40e_pf *pf)
        if (pf->hw.func_caps.vmdq) {
                pf->num_vmdq_vsis = I40E_DEFAULT_NUM_VMDQ_VSI;
                pf->flags |= I40E_FLAG_VMDQ_ENABLED;
+               pf->num_vmdq_qps = i40e_default_queues_per_vmdq(pf);
        }
 
 #ifdef I40E_FCOE
-       err = i40e_init_pf_fcoe(pf);
-       if (err)
-               dev_info(&pf->pdev->dev, "init_pf_fcoe failed: %d\n", err);
+       i40e_init_pf_fcoe(pf);
 
 #endif /* I40E_FCOE */
 #ifdef CONFIG_PCI_IOV
@@ -7940,6 +8182,9 @@ static int i40e_sw_init(struct i40e_pf *pf)
        pf->lan_veb = I40E_NO_VEB;
        pf->lan_vsi = I40E_NO_VSI;
 
+       /* By default FW has this off for performance reasons */
+       pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED;
+
        /* set up queue assignment tracking */
        size = sizeof(struct i40e_lump_tracking)
                + (sizeof(u16) * pf->hw.func_caps.num_tx_qp);
@@ -8119,9 +8364,6 @@ static void i40e_del_vxlan_port(struct net_device *netdev,
                pf->vxlan_ports[idx] = 0;
                pf->pending_vxlan_bitmap |= BIT_ULL(idx);
                pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC;
-
-               dev_info(&pf->pdev->dev, "deleting vxlan port %d\n",
-                        ntohs(port));
        } else {
                netdev_warn(netdev, "vxlan port %d was not found, not deleting\n",
                            ntohs(port));
@@ -8273,13 +8515,15 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev,
  * @seq: RTNL message seq #
  * @dev: the netdev being configured
  * @filter_mask: unused
+ * @nlflags: netlink flags passed in
  *
  * Return the mode in which the hardware bridge is operating in
  * i.e VEB or VEPA.
  **/
 static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
                                   struct net_device *dev,
-                                  u32 filter_mask, int nlflags)
+                                  u32 __always_unused filter_mask,
+                                  int nlflags)
 {
        struct i40e_netdev_priv *np = netdev_priv(dev);
        struct i40e_vsi *vsi = np->vsi;
@@ -8308,7 +8552,7 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
 /**
  * i40e_features_check - Validate encapsulated packet conforms to limits
  * @skb: skb buff
- * @netdev: This physical port's netdev
+ * @dev: This physical port's netdev
  * @features: Offload features that the stack believes apply
  **/
 static netdev_features_t i40e_features_check(struct sk_buff *skb,
@@ -8423,17 +8667,26 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
                 * default a MAC-VLAN filter that accepts any tagged packet
                 * which must be replaced by a normal filter.
                 */
-               if (!i40e_rm_default_mac_filter(vsi, mac_addr))
+               if (!i40e_rm_default_mac_filter(vsi, mac_addr)) {
+                       spin_lock_bh(&vsi->mac_filter_list_lock);
                        i40e_add_filter(vsi, mac_addr,
                                        I40E_VLAN_ANY, false, true);
+                       spin_unlock_bh(&vsi->mac_filter_list_lock);
+               }
        } else {
                /* relate the VSI_VMDQ name to the VSI_MAIN name */
                snprintf(netdev->name, IFNAMSIZ, "%sv%%d",
                         pf->vsi[pf->lan_vsi]->netdev->name);
                random_ether_addr(mac_addr);
+
+               spin_lock_bh(&vsi->mac_filter_list_lock);
                i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY, false, false);
+               spin_unlock_bh(&vsi->mac_filter_list_lock);
        }
+
+       spin_lock_bh(&vsi->mac_filter_list_lock);
        i40e_add_filter(vsi, brdcast, I40E_VLAN_ANY, false, false);
+       spin_unlock_bh(&vsi->mac_filter_list_lock);
 
        ether_addr_copy(netdev->dev_addr, mac_addr);
        ether_addr_copy(netdev->perm_addr, mac_addr);
@@ -8489,12 +8742,22 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi)
                return 1;
 
        veb = pf->veb[vsi->veb_idx];
+       if (!veb) {
+               dev_info(&pf->pdev->dev,
+                        "There is no veb associated with the bridge\n");
+               return -ENOENT;
+       }
+
        /* Uplink is a bridge in VEPA mode */
-       if (veb && (veb->bridge_mode & BRIDGE_MODE_VEPA))
+       if (veb->bridge_mode & BRIDGE_MODE_VEPA) {
                return 0;
+       } else {
+               /* Uplink is a bridge in VEB mode */
+               return 1;
+       }
 
-       /* Uplink is a bridge in VEB mode */
-       return 1;
+       /* VEPA is now default bridge, so return 0 */
+       return 0;
 }
 
 /**
@@ -8507,10 +8770,13 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi)
 static int i40e_add_vsi(struct i40e_vsi *vsi)
 {
        int ret = -ENODEV;
-       struct i40e_mac_filter *f, *ftmp;
+       u8 laa_macaddr[ETH_ALEN];
+       bool found_laa_mac_filter = false;
        struct i40e_pf *pf = vsi->back;
        struct i40e_hw *hw = &pf->hw;
        struct i40e_vsi_context ctxt;
+       struct i40e_mac_filter *f, *ftmp;
+
        u8 enabled_tc = 0x1; /* TC0 enabled */
        int f_count = 0;
 
@@ -8682,32 +8948,41 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
                vsi->id = ctxt.vsi_number;
        }
 
+       spin_lock_bh(&vsi->mac_filter_list_lock);
        /* If macvlan filters already exist, force them to get loaded */
        list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
                f->changed = true;
                f_count++;
 
+               /* Expected to have only one MAC filter entry for LAA in list */
                if (f->is_laa && vsi->type == I40E_VSI_MAIN) {
-                       struct i40e_aqc_remove_macvlan_element_data element;
+                       ether_addr_copy(laa_macaddr, f->macaddr);
+                       found_laa_mac_filter = true;
+               }
+       }
+       spin_unlock_bh(&vsi->mac_filter_list_lock);
 
-                       memset(&element, 0, sizeof(element));
-                       ether_addr_copy(element.mac_addr, f->macaddr);
-                       element.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH;
-                       ret = i40e_aq_remove_macvlan(hw, vsi->seid,
-                                                    &element, 1, NULL);
-                       if (ret) {
-                               /* some older FW has a different default */
-                               element.flags |=
-                                              I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
-                               i40e_aq_remove_macvlan(hw, vsi->seid,
-                                                      &element, 1, NULL);
-                       }
+       if (found_laa_mac_filter) {
+               struct i40e_aqc_remove_macvlan_element_data element;
 
-                       i40e_aq_mac_address_write(hw,
-                                                 I40E_AQC_WRITE_TYPE_LAA_WOL,
-                                                 f->macaddr, NULL);
+               memset(&element, 0, sizeof(element));
+               ether_addr_copy(element.mac_addr, laa_macaddr);
+               element.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH;
+               ret = i40e_aq_remove_macvlan(hw, vsi->seid,
+                                            &element, 1, NULL);
+               if (ret) {
+                       /* some older FW has a different default */
+                       element.flags |=
+                                      I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
+                       i40e_aq_remove_macvlan(hw, vsi->seid,
+                                              &element, 1, NULL);
                }
+
+               i40e_aq_mac_address_write(hw,
+                                         I40E_AQC_WRITE_TYPE_LAA_WOL,
+                                         laa_macaddr, NULL);
        }
+
        if (f_count) {
                vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
                pf->flags |= I40E_FLAG_FILTER_SYNC;
@@ -8770,10 +9045,13 @@ int i40e_vsi_release(struct i40e_vsi *vsi)
                i40e_vsi_disable_irq(vsi);
        }
 
+       spin_lock_bh(&vsi->mac_filter_list_lock);
        list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list)
                i40e_del_filter(vsi, f->macaddr, f->vlan,
                                f->is_vf, f->is_netdev);
-       i40e_sync_vsi_filters(vsi);
+       spin_unlock_bh(&vsi->mac_filter_list_lock);
+
+       i40e_sync_vsi_filters(vsi, false);
 
        i40e_vsi_delete(vsi);
        i40e_vsi_free_q_vectors(vsi);
@@ -8998,8 +9276,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
                if (veb) {
                        if (vsi->seid != pf->vsi[pf->lan_vsi]->seid) {
                                dev_info(&vsi->back->pdev->dev,
-                                        "%s: New VSI creation error, uplink seid of LAN VSI expected.\n",
-                                        __func__);
+                                        "New VSI creation error, uplink seid of LAN VSI expected.\n");
                                return NULL;
                        }
                        /* We come up by default in VEPA mode if SRIOV is not
@@ -9649,6 +9926,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
        } else {
                /* force a reset of TC and queue layout configurations */
                u8 enabled_tc = pf->vsi[pf->lan_vsi]->tc_config.enabled_tc;
+
                pf->vsi[pf->lan_vsi]->tc_config.enabled_tc = 0;
                pf->vsi[pf->lan_vsi]->seid = pf->main_vsi_seid;
                i40e_vsi_config_tc(pf->vsi[pf->lan_vsi], enabled_tc);
@@ -9672,7 +9950,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
                i40e_config_rss(pf);
 
        /* fill in link information and enable LSE reporting */
-       i40e_aq_get_link_info(&pf->hw, true, NULL, NULL);
+       i40e_update_link_info(&pf->hw);
        i40e_link_event(pf);
 
        /* Initialize user-specific link properties */
@@ -9790,8 +10068,14 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
        }
 
        pf->queues_left = queues_left;
+       dev_dbg(&pf->pdev->dev,
+               "qs_avail=%d FD SB=%d lan_qs=%d lan_tc0=%d vf=%d*%d vmdq=%d*%d, remaining=%d\n",
+               pf->hw.func_caps.num_tx_qp,
+               !!(pf->flags & I40E_FLAG_FD_SB_ENABLED),
+               pf->num_lan_qps, pf->rss_size, pf->num_req_vfs, pf->num_vf_qps,
+               pf->num_vmdq_vsis, pf->num_vmdq_qps, queues_left);
 #ifdef I40E_FCOE
-       dev_info(&pf->pdev->dev, "fcoe queues = %d\n", pf->num_fcoe_qps);
+       dev_dbg(&pf->pdev->dev, "fcoe queues = %d\n", pf->num_fcoe_qps);
 #endif
 }
 
@@ -9859,12 +10143,19 @@ static void i40e_print_features(struct i40e_pf *pf)
        }
        if (pf->flags & I40E_FLAG_DCB_CAPABLE)
                buf += sprintf(buf, "DCB ");
+#if IS_ENABLED(CONFIG_VXLAN)
+       buf += sprintf(buf, "VxLAN ");
+#endif
        if (pf->flags & I40E_FLAG_PTP)
                buf += sprintf(buf, "PTP ");
 #ifdef I40E_FCOE
        if (pf->flags & I40E_FLAG_FCOE_ENABLED)
                buf += sprintf(buf, "FCOE ");
 #endif
+       if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED)
+               buf += sprintf(buf, "VEB ");
+       else
+               buf += sprintf(buf, "VEPA ");
 
        BUG_ON(buf > (string + INFO_STRING_LEN));
        dev_info(&pf->pdev->dev, "%s\n", string);
@@ -9885,14 +10176,15 @@ static void i40e_print_features(struct i40e_pf *pf)
 static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        struct i40e_aq_get_phy_abilities_resp abilities;
-       unsigned long ioremap_len;
        struct i40e_pf *pf;
        struct i40e_hw *hw;
        static u16 pfs_found;
+       u16 wol_nvm_bits;
        u16 link_status;
-       int err = 0;
+       int err;
        u32 len;
        u32 i;
+       u8 set_fc_aq_fail;
 
        err = pci_enable_device_mem(pdev);
        if (err)
@@ -9938,15 +10230,15 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        hw = &pf->hw;
        hw->back = pf;
 
-       ioremap_len = min_t(unsigned long, pci_resource_len(pdev, 0),
-                           I40E_MAX_CSR_SPACE);
+       pf->ioremap_len = min_t(int, pci_resource_len(pdev, 0),
+                               I40E_MAX_CSR_SPACE);
 
-       hw->hw_addr = ioremap(pci_resource_start(pdev, 0), ioremap_len);
+       hw->hw_addr = ioremap(pci_resource_start(pdev, 0), pf->ioremap_len);
        if (!hw->hw_addr) {
                err = -EIO;
                dev_info(&pdev->dev, "ioremap(0x%04x, 0x%04x) failed: 0x%x\n",
                         (unsigned int)pci_resource_start(pdev, 0),
-                        (unsigned int)pci_resource_len(pdev, 0), err);
+                        pf->ioremap_len, err);
                goto err_ioremap;
        }
        hw->vendor_id = pdev->vendor;
@@ -10004,7 +10296,13 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        pf->hw.fc.requested_mode = I40E_FC_NONE;
 
        err = i40e_init_adminq(hw);
-       dev_info(&pdev->dev, "%s\n", i40e_fw_version_str(hw));
+
+       /* provide nvm, fw, api versions */
+       dev_info(&pdev->dev, "fw %d.%d.%05d api %d.%d nvm %s\n",
+                hw->aq.fw_maj_ver, hw->aq.fw_min_ver, hw->aq.fw_build,
+                hw->aq.api_maj_ver, hw->aq.api_min_ver,
+                i40e_nvm_version_str(hw));
+
        if (err) {
                dev_info(&pdev->dev,
                         "The driver for the device stopped because the NVM image is newer than expected. You must install the most recent version of the network driver.\n");
@@ -10104,10 +10402,13 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        INIT_WORK(&pf->service_task, i40e_service_task);
        clear_bit(__I40E_SERVICE_SCHED, &pf->state);
        pf->flags |= I40E_FLAG_NEED_LINK_UPDATE;
-       pf->link_check_timeout = jiffies;
 
-       /* WoL defaults to disabled */
-       pf->wol_en = false;
+       /* NVM bit on means WoL disabled for the port */
+       i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits);
+       if ((1 << hw->port) & wol_nvm_bits || hw->partition_id != 1)
+               pf->wol_en = false;
+       else
+               pf->wol_en = true;
        device_set_wakeup_enable(&pf->pdev->dev, pf->wol_en);
 
        /* set up the main switch operations */
@@ -10148,6 +10449,25 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                dev_info(&pdev->dev, "setup_pf_switch failed: %d\n", err);
                goto err_vsis;
        }
+
+       /* Make sure flow control is set according to current settings */
+       err = i40e_set_fc(hw, &set_fc_aq_fail, true);
+       if (set_fc_aq_fail & I40E_SET_FC_AQ_FAIL_GET)
+               dev_dbg(&pf->pdev->dev,
+                       "Set fc with err %s aq_err %s on get_phy_cap\n",
+                       i40e_stat_str(hw, err),
+                       i40e_aq_str(hw, hw->aq.asq_last_status));
+       if (set_fc_aq_fail & I40E_SET_FC_AQ_FAIL_SET)
+               dev_dbg(&pf->pdev->dev,
+                       "Set fc with err %s aq_err %s on set_phy_config\n",
+                       i40e_stat_str(hw, err),
+                       i40e_aq_str(hw, hw->aq.asq_last_status));
+       if (set_fc_aq_fail & I40E_SET_FC_AQ_FAIL_UPDATE)
+               dev_dbg(&pf->pdev->dev,
+                       "Set fc with err %s aq_err %s on get_link_info\n",
+                       i40e_stat_str(hw, err),
+                       i40e_aq_str(hw, hw->aq.asq_last_status));
+
        /* if FDIR VSI was set up, start it now */
        for (i = 0; i < pf->num_alloc_vsi; i++) {
                if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
@@ -10238,37 +10558,82 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        i40e_fcoe_vsi_setup(pf);
 
 #endif
-       /* Get the negotiated link width and speed from PCI config space */
-       pcie_capability_read_word(pf->pdev, PCI_EXP_LNKSTA, &link_status);
+#define PCI_SPEED_SIZE 8
+#define PCI_WIDTH_SIZE 8
+       /* Devices on the IOSF bus do not have this information
+        * and will report PCI Gen 1 x 1 by default so don't bother
+        * checking them.
+        */
+       if (!(pf->flags & I40E_FLAG_NO_PCI_LINK_CHECK)) {
+               char speed[PCI_SPEED_SIZE] = "Unknown";
+               char width[PCI_WIDTH_SIZE] = "Unknown";
 
-       i40e_set_pci_config_data(hw, link_status);
+               /* Get the negotiated link width and speed from PCI config
+                * space
+                */
+               pcie_capability_read_word(pf->pdev, PCI_EXP_LNKSTA,
+                                         &link_status);
+
+               i40e_set_pci_config_data(hw, link_status);
+
+               switch (hw->bus.speed) {
+               case i40e_bus_speed_8000:
+                       strncpy(speed, "8.0", PCI_SPEED_SIZE); break;
+               case i40e_bus_speed_5000:
+                       strncpy(speed, "5.0", PCI_SPEED_SIZE); break;
+               case i40e_bus_speed_2500:
+                       strncpy(speed, "2.5", PCI_SPEED_SIZE); break;
+               default:
+                       break;
+               }
+               switch (hw->bus.width) {
+               case i40e_bus_width_pcie_x8:
+                       strncpy(width, "8", PCI_WIDTH_SIZE); break;
+               case i40e_bus_width_pcie_x4:
+                       strncpy(width, "4", PCI_WIDTH_SIZE); break;
+               case i40e_bus_width_pcie_x2:
+                       strncpy(width, "2", PCI_WIDTH_SIZE); break;
+               case i40e_bus_width_pcie_x1:
+                       strncpy(width, "1", PCI_WIDTH_SIZE); break;
+               default:
+                       break;
+               }
 
-       dev_info(&pdev->dev, "PCI-Express: %s %s\n",
-               (hw->bus.speed == i40e_bus_speed_8000 ? "Speed 8.0GT/s" :
-                hw->bus.speed == i40e_bus_speed_5000 ? "Speed 5.0GT/s" :
-                hw->bus.speed == i40e_bus_speed_2500 ? "Speed 2.5GT/s" :
-                "Unknown"),
-               (hw->bus.width == i40e_bus_width_pcie_x8 ? "Width x8" :
-                hw->bus.width == i40e_bus_width_pcie_x4 ? "Width x4" :
-                hw->bus.width == i40e_bus_width_pcie_x2 ? "Width x2" :
-                hw->bus.width == i40e_bus_width_pcie_x1 ? "Width x1" :
-                "Unknown"));
+               dev_info(&pdev->dev, "PCI-Express: Speed %sGT/s Width x%s\n",
+                        speed, width);
 
-       if (hw->bus.width < i40e_bus_width_pcie_x8 ||
-           hw->bus.speed < i40e_bus_speed_8000) {
-               dev_warn(&pdev->dev, "PCI-Express bandwidth available for this device may be insufficient for optimal performance.\n");
-               dev_warn(&pdev->dev, "Please move the device to a different PCI-e link with more lanes and/or higher transfer rate.\n");
+               if (hw->bus.width < i40e_bus_width_pcie_x8 ||
+                   hw->bus.speed < i40e_bus_speed_8000) {
+                       dev_warn(&pdev->dev, "PCI-Express bandwidth available for this device may be insufficient for optimal performance.\n");
+                       dev_warn(&pdev->dev, "Please move the device to a different PCI-e link with more lanes and/or higher transfer rate.\n");
+               }
        }
 
        /* get the requested speeds from the fw */
        err = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, NULL);
        if (err)
-               dev_info(&pf->pdev->dev,
-                        "get phy capabilities failed, err %s aq_err %s, advertised speed settings may not be correct\n",
-                        i40e_stat_str(&pf->hw, err),
-                        i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
+               dev_dbg(&pf->pdev->dev, "get requested speeds ret =  %s last_status =  %s\n",
+                       i40e_stat_str(&pf->hw, err),
+                       i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
        pf->hw.phy.link_info.requested_speeds = abilities.link_speed;
 
+       /* get the supported phy types from the fw */
+       err = i40e_aq_get_phy_capabilities(hw, false, true, &abilities, NULL);
+       if (err)
+               dev_dbg(&pf->pdev->dev, "get supported phy types ret =  %s last_status =  %s\n",
+                       i40e_stat_str(&pf->hw, err),
+                       i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
+       pf->hw.phy.phy_types = le32_to_cpu(abilities.phy_type);
+
+       /* Add a filter to drop all Flow control frames from any VSI from being
+        * transmitted. By doing so we stop a malicious VF from sending out
+        * PAUSE or PFC frames and potentially controlling traffic for other
+        * PF/VF VSIs.
+        * The FW can still send Flow control frames if enabled.
+        */
+       i40e_add_filter_to_drop_tx_flow_control_frames(&pf->hw,
+                                                      pf->main_vsi_seid);
+
        /* print a string summarizing features */
        i40e_print_features(pf);
 
@@ -10316,6 +10681,7 @@ err_dma:
 static void i40e_remove(struct pci_dev *pdev)
 {
        struct i40e_pf *pf = pci_get_drvdata(pdev);
+       struct i40e_hw *hw = &pf->hw;
        i40e_status ret_code;
        int i;
 
@@ -10323,6 +10689,10 @@ static void i40e_remove(struct pci_dev *pdev)
 
        i40e_ptp_stop(pf);
 
+       /* Disable RSS in hw */
+       wr32(hw, I40E_PFQF_HENA(0), 0);
+       wr32(hw, I40E_PFQF_HENA(1), 0);
+
        /* no more scheduling of any task */
        set_bit(__I40E_DOWN, &pf->state);
        del_timer_sync(&pf->service_timer);
@@ -10439,7 +10809,7 @@ static pci_ers_result_t i40e_pci_error_slot_reset(struct pci_dev *pdev)
        int err;
        u32 reg;
 
-       dev_info(&pdev->dev, "%s\n", __func__);
+       dev_dbg(&pdev->dev, "%s\n", __func__);
        if (pci_enable_device_mem(pdev)) {
                dev_info(&pdev->dev,
                         "Cannot re-enable PCI device after reset.\n");
@@ -10479,13 +10849,13 @@ static void i40e_pci_error_resume(struct pci_dev *pdev)
 {
        struct i40e_pf *pf = pci_get_drvdata(pdev);
 
-       dev_info(&pdev->dev, "%s\n", __func__);
+       dev_dbg(&pdev->dev, "%s\n", __func__);
        if (test_bit(__I40E_SUSPENDED, &pf->state))
                return;
 
        rtnl_lock();
        i40e_handle_reset_warning(pf);
-       rtnl_lock();
+       rtnl_unlock();
 }
 
 /**
@@ -10571,9 +10941,7 @@ static int i40e_resume(struct pci_dev *pdev)
 
        err = pci_enable_device_mem(pdev);
        if (err) {
-               dev_err(&pdev->dev,
-                       "%s: Cannot enable PCI device from suspend\n",
-                       __func__);
+               dev_err(&pdev->dev, "Cannot enable PCI device from suspend\n");
                return err;
        }
        pci_set_master(pdev);
index 9b83abc0e774cbfa255889d75b962e43c8299c1b..6100cdd9ad13b420f20a640f8dc2572b89afb92d 100644 (file)
@@ -290,9 +290,18 @@ static i40e_status i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset,
 i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset,
                               u16 *data)
 {
-       if (hw->mac.type == I40E_MAC_X722)
-               return i40e_read_nvm_word_aq(hw, offset, data);
-       return i40e_read_nvm_word_srctl(hw, offset, data);
+       enum i40e_status_code ret_code = 0;
+
+       if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) {
+               ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
+               if (!ret_code) {
+                       ret_code = i40e_read_nvm_word_aq(hw, offset, data);
+                       i40e_release_nvm(hw);
+               }
+       } else {
+               ret_code = i40e_read_nvm_word_srctl(hw, offset, data);
+       }
+       return ret_code;
 }
 
 /**
@@ -397,9 +406,19 @@ read_nvm_buffer_aq_exit:
 i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset,
                                 u16 *words, u16 *data)
 {
-       if (hw->mac.type == I40E_MAC_X722)
-               return i40e_read_nvm_buffer_aq(hw, offset, words, data);
-       return i40e_read_nvm_buffer_srctl(hw, offset, words, data);
+       enum i40e_status_code ret_code = 0;
+
+       if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) {
+               ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
+               if (!ret_code) {
+                       ret_code = i40e_read_nvm_buffer_aq(hw, offset, words,
+                                                          data);
+                       i40e_release_nvm(hw);
+               }
+       } else {
+               ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data);
+       }
+       return ret_code;
 }
 
 /**
@@ -418,6 +437,10 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer,
                                     bool last_command)
 {
        i40e_status ret_code = I40E_ERR_NVM;
+       struct i40e_asq_cmd_details cmd_details;
+
+       memset(&cmd_details, 0, sizeof(cmd_details));
+       cmd_details.wb_desc = &hw->nvm_wb_desc;
 
        /* Here we are checking the SR limit only for the flat memory model.
         * We cannot do it for the module-based model, as we did not acquire
@@ -443,7 +466,7 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer,
                ret_code = i40e_aq_update_nvm(hw, module_pointer,
                                              2 * offset,  /*bytes*/
                                              2 * words,   /*bytes*/
-                                             data, last_command, NULL);
+                                             data, last_command, &cmd_details);
 
        return ret_code;
 }
@@ -461,7 +484,7 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer,
 static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw,
                                                    u16 *checksum)
 {
-       i40e_status ret_code = 0;
+       i40e_status ret_code;
        struct i40e_virt_mem vmem;
        u16 pcie_alt_module = 0;
        u16 checksum_local = 0;
@@ -541,13 +564,16 @@ i40e_calc_nvm_checksum_exit:
  **/
 i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw)
 {
-       i40e_status ret_code = 0;
+       i40e_status ret_code;
        u16 checksum;
+       __le16 le_sum;
 
        ret_code = i40e_calc_nvm_checksum(hw, &checksum);
-       if (!ret_code)
+       if (!ret_code) {
+               le_sum = cpu_to_le16(checksum);
                ret_code = i40e_write_nvm_aq(hw, 0x00, I40E_SR_SW_CHECKSUM_WORD,
-                                            1, &checksum, true);
+                                            1, &le_sum, true);
+       }
 
        return ret_code;
 }
@@ -592,25 +618,31 @@ i40e_validate_nvm_checksum_exit:
 
 static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
                                          struct i40e_nvm_access *cmd,
-                                         u8 *bytes, int *errno);
+                                         u8 *bytes, int *perrno);
 static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw,
                                             struct i40e_nvm_access *cmd,
-                                            u8 *bytes, int *errno);
+                                            u8 *bytes, int *perrno);
 static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw,
                                             struct i40e_nvm_access *cmd,
                                             u8 *bytes, int *errno);
 static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
                                                struct i40e_nvm_access *cmd,
-                                               int *errno);
+                                               int *perrno);
 static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
                                         struct i40e_nvm_access *cmd,
-                                        int *errno);
+                                        int *perrno);
 static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw,
                                         struct i40e_nvm_access *cmd,
-                                        u8 *bytes, int *errno);
+                                        u8 *bytes, int *perrno);
 static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
                                        struct i40e_nvm_access *cmd,
-                                       u8 *bytes, int *errno);
+                                       u8 *bytes, int *perrno);
+static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw,
+                                      struct i40e_nvm_access *cmd,
+                                      u8 *bytes, int *perrno);
+static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw,
+                                            struct i40e_nvm_access *cmd,
+                                            u8 *bytes, int *perrno);
 static inline u8 i40e_nvmupd_get_module(u32 val)
 {
        return (u8)(val & I40E_NVM_MOD_PNT_MASK);
@@ -620,7 +652,7 @@ static inline u8 i40e_nvmupd_get_transaction(u32 val)
        return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT);
 }
 
-static char *i40e_nvm_update_state_str[] = {
+static const char * const i40e_nvm_update_state_str[] = {
        "I40E_NVMUPD_INVALID",
        "I40E_NVMUPD_READ_CON",
        "I40E_NVMUPD_READ_SNT",
@@ -634,6 +666,9 @@ static char *i40e_nvm_update_state_str[] = {
        "I40E_NVMUPD_CSUM_CON",
        "I40E_NVMUPD_CSUM_SA",
        "I40E_NVMUPD_CSUM_LCB",
+       "I40E_NVMUPD_STATUS",
+       "I40E_NVMUPD_EXEC_AQ",
+       "I40E_NVMUPD_GET_AQ_RESULT",
 };
 
 /**
@@ -641,30 +676,60 @@ static char *i40e_nvm_update_state_str[] = {
  * @hw: pointer to hardware structure
  * @cmd: pointer to nvm update command
  * @bytes: pointer to the data buffer
- * @errno: pointer to return error code
+ * @perrno: pointer to return error code
  *
  * Dispatches command depending on what update state is current
  **/
 i40e_status i40e_nvmupd_command(struct i40e_hw *hw,
                                struct i40e_nvm_access *cmd,
-                               u8 *bytes, int *errno)
+                               u8 *bytes, int *perrno)
 {
        i40e_status status;
+       enum i40e_nvmupd_cmd upd_cmd;
 
        /* assume success */
-       *errno = 0;
+       *perrno = 0;
+
+       /* early check for status command and debug msgs */
+       upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
+
+       i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n",
+                  i40e_nvm_update_state_str[upd_cmd],
+                  hw->nvmupd_state,
+                  hw->aq.nvm_release_on_done);
+
+       if (upd_cmd == I40E_NVMUPD_INVALID) {
+               *perrno = -EFAULT;
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "i40e_nvmupd_validate_command returns %d errno %d\n",
+                          upd_cmd, *perrno);
+       }
+
+       /* a status request returns immediately rather than
+        * going into the state machine
+        */
+       if (upd_cmd == I40E_NVMUPD_STATUS) {
+               bytes[0] = hw->nvmupd_state;
+               return 0;
+       }
 
        switch (hw->nvmupd_state) {
        case I40E_NVMUPD_STATE_INIT:
-               status = i40e_nvmupd_state_init(hw, cmd, bytes, errno);
+               status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno);
                break;
 
        case I40E_NVMUPD_STATE_READING:
-               status = i40e_nvmupd_state_reading(hw, cmd, bytes, errno);
+               status = i40e_nvmupd_state_reading(hw, cmd, bytes, perrno);
                break;
 
        case I40E_NVMUPD_STATE_WRITING:
-               status = i40e_nvmupd_state_writing(hw, cmd, bytes, errno);
+               status = i40e_nvmupd_state_writing(hw, cmd, bytes, perrno);
+               break;
+
+       case I40E_NVMUPD_STATE_INIT_WAIT:
+       case I40E_NVMUPD_STATE_WRITE_WAIT:
+               status = I40E_ERR_NOT_READY;
+               *perrno = -EBUSY;
                break;
 
        default:
@@ -672,7 +737,7 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw,
                i40e_debug(hw, I40E_DEBUG_NVM,
                           "NVMUPD: no such state %d\n", hw->nvmupd_state);
                status = I40E_NOT_SUPPORTED;
-               *errno = -ESRCH;
+               *perrno = -ESRCH;
                break;
        }
        return status;
@@ -683,28 +748,28 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw,
  * @hw: pointer to hardware structure
  * @cmd: pointer to nvm update command buffer
  * @bytes: pointer to the data buffer
- * @errno: pointer to return error code
+ * @perrno: pointer to return error code
  *
  * Process legitimate commands of the Init state and conditionally set next
  * state. Reject all other commands.
  **/
 static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
                                          struct i40e_nvm_access *cmd,
-                                         u8 *bytes, int *errno)
+                                         u8 *bytes, int *perrno)
 {
        i40e_status status = 0;
        enum i40e_nvmupd_cmd upd_cmd;
 
-       upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno);
+       upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
 
        switch (upd_cmd) {
        case I40E_NVMUPD_READ_SA:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(status,
+                       *perrno = i40e_aq_rc_to_posix(status,
                                                     hw->aq.asq_last_status);
                } else {
-                       status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
+                       status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
                        i40e_release_nvm(hw);
                }
                break;
@@ -712,10 +777,10 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
        case I40E_NVMUPD_READ_SNT:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(status,
+                       *perrno = i40e_aq_rc_to_posix(status,
                                                     hw->aq.asq_last_status);
                } else {
-                       status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
+                       status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
                        if (status)
                                i40e_release_nvm(hw);
                        else
@@ -726,70 +791,83 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
        case I40E_NVMUPD_WRITE_ERA:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(status,
+                       *perrno = i40e_aq_rc_to_posix(status,
                                                     hw->aq.asq_last_status);
                } else {
-                       status = i40e_nvmupd_nvm_erase(hw, cmd, errno);
-                       if (status)
+                       status = i40e_nvmupd_nvm_erase(hw, cmd, perrno);
+                       if (status) {
                                i40e_release_nvm(hw);
-                       else
+                       } else {
                                hw->aq.nvm_release_on_done = true;
+                               hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
+                       }
                }
                break;
 
        case I40E_NVMUPD_WRITE_SA:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(status,
+                       *perrno = i40e_aq_rc_to_posix(status,
                                                     hw->aq.asq_last_status);
                } else {
-                       status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
-                       if (status)
+                       status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
+                       if (status) {
                                i40e_release_nvm(hw);
-                       else
+                       } else {
                                hw->aq.nvm_release_on_done = true;
+                               hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
+                       }
                }
                break;
 
        case I40E_NVMUPD_WRITE_SNT:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(status,
+                       *perrno = i40e_aq_rc_to_posix(status,
                                                     hw->aq.asq_last_status);
                } else {
-                       status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
+                       status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
                        if (status)
                                i40e_release_nvm(hw);
                        else
-                               hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING;
+                               hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT;
                }
                break;
 
        case I40E_NVMUPD_CSUM_SA:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(status,
+                       *perrno = i40e_aq_rc_to_posix(status,
                                                     hw->aq.asq_last_status);
                } else {
                        status = i40e_update_nvm_checksum(hw);
                        if (status) {
-                               *errno = hw->aq.asq_last_status ?
+                               *perrno = hw->aq.asq_last_status ?
                                   i40e_aq_rc_to_posix(status,
                                                       hw->aq.asq_last_status) :
                                   -EIO;
                                i40e_release_nvm(hw);
                        } else {
                                hw->aq.nvm_release_on_done = true;
+                               hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
                        }
                }
                break;
 
+       case I40E_NVMUPD_EXEC_AQ:
+               status = i40e_nvmupd_exec_aq(hw, cmd, bytes, perrno);
+               break;
+
+       case I40E_NVMUPD_GET_AQ_RESULT:
+               status = i40e_nvmupd_get_aq_result(hw, cmd, bytes, perrno);
+               break;
+
        default:
                i40e_debug(hw, I40E_DEBUG_NVM,
                           "NVMUPD: bad cmd %s in init state\n",
                           i40e_nvm_update_state_str[upd_cmd]);
                status = I40E_ERR_NVM;
-               *errno = -ESRCH;
+               *perrno = -ESRCH;
                break;
        }
        return status;
@@ -800,28 +878,28 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
  * @hw: pointer to hardware structure
  * @cmd: pointer to nvm update command buffer
  * @bytes: pointer to the data buffer
- * @errno: pointer to return error code
+ * @perrno: pointer to return error code
  *
  * NVM ownership is already held.  Process legitimate commands and set any
  * change in state; reject all other commands.
  **/
 static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw,
                                             struct i40e_nvm_access *cmd,
-                                            u8 *bytes, int *errno)
+                                            u8 *bytes, int *perrno)
 {
-       i40e_status status;
+       i40e_status status = 0;
        enum i40e_nvmupd_cmd upd_cmd;
 
-       upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno);
+       upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
 
        switch (upd_cmd) {
        case I40E_NVMUPD_READ_SA:
        case I40E_NVMUPD_READ_CON:
-               status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
+               status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
                break;
 
        case I40E_NVMUPD_READ_LCB:
-               status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
+               status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
                i40e_release_nvm(hw);
                hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
                break;
@@ -831,7 +909,7 @@ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw,
                           "NVMUPD: bad cmd %s in reading state.\n",
                           i40e_nvm_update_state_str[upd_cmd]);
                status = I40E_NOT_SUPPORTED;
-               *errno = -ESRCH;
+               *perrno = -ESRCH;
                break;
        }
        return status;
@@ -842,55 +920,68 @@ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw,
  * @hw: pointer to hardware structure
  * @cmd: pointer to nvm update command buffer
  * @bytes: pointer to the data buffer
- * @errno: pointer to return error code
+ * @perrno: pointer to return error code
  *
  * NVM ownership is already held.  Process legitimate commands and set any
  * change in state; reject all other commands
  **/
 static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw,
                                             struct i40e_nvm_access *cmd,
-                                            u8 *bytes, int *errno)
+                                            u8 *bytes, int *perrno)
 {
-       i40e_status status;
+       i40e_status status = 0;
        enum i40e_nvmupd_cmd upd_cmd;
        bool retry_attempt = false;
 
-       upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno);
+       upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
 
 retry:
        switch (upd_cmd) {
        case I40E_NVMUPD_WRITE_CON:
-               status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
+               status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
+               if (!status)
+                       hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT;
                break;
 
        case I40E_NVMUPD_WRITE_LCB:
-               status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
-               if (!status)
+               status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
+               if (status) {
+                       *perrno = hw->aq.asq_last_status ?
+                                  i40e_aq_rc_to_posix(status,
+                                                      hw->aq.asq_last_status) :
+                                  -EIO;
+                       hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
+               } else {
                        hw->aq.nvm_release_on_done = true;
-               hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
+                       hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
+               }
                break;
 
        case I40E_NVMUPD_CSUM_CON:
                status = i40e_update_nvm_checksum(hw);
                if (status) {
-                       *errno = hw->aq.asq_last_status ?
+                       *perrno = hw->aq.asq_last_status ?
                                   i40e_aq_rc_to_posix(status,
                                                       hw->aq.asq_last_status) :
                                   -EIO;
                        hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
+               } else {
+                       hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT;
                }
                break;
 
        case I40E_NVMUPD_CSUM_LCB:
                status = i40e_update_nvm_checksum(hw);
-               if (status)
-                       *errno = hw->aq.asq_last_status ?
+               if (status) {
+                       *perrno = hw->aq.asq_last_status ?
                                   i40e_aq_rc_to_posix(status,
                                                       hw->aq.asq_last_status) :
                                   -EIO;
-               else
+                       hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
+               } else {
                        hw->aq.nvm_release_on_done = true;
-               hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
+                       hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
+               }
                break;
 
        default:
@@ -898,7 +989,7 @@ retry:
                           "NVMUPD: bad cmd %s in writing state.\n",
                           i40e_nvm_update_state_str[upd_cmd]);
                status = I40E_NOT_SUPPORTED;
-               *errno = -ESRCH;
+               *perrno = -ESRCH;
                break;
        }
 
@@ -941,21 +1032,22 @@ retry:
  * i40e_nvmupd_validate_command - Validate given command
  * @hw: pointer to hardware structure
  * @cmd: pointer to nvm update command buffer
- * @errno: pointer to return error code
+ * @perrno: pointer to return error code
  *
  * Return one of the valid command types or I40E_NVMUPD_INVALID
  **/
 static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
                                                 struct i40e_nvm_access *cmd,
-                                                int *errno)
+                                                int *perrno)
 {
        enum i40e_nvmupd_cmd upd_cmd;
-       u8 transaction;
+       u8 module, transaction;
 
        /* anything that doesn't match a recognized case is an error */
        upd_cmd = I40E_NVMUPD_INVALID;
 
        transaction = i40e_nvmupd_get_transaction(cmd->config);
+       module = i40e_nvmupd_get_module(cmd->config);
 
        /* limits on data size */
        if ((cmd->data_size < 1) ||
@@ -963,7 +1055,7 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
                i40e_debug(hw, I40E_DEBUG_NVM,
                           "i40e_nvmupd_validate_command data_size %d\n",
                           cmd->data_size);
-               *errno = -EFAULT;
+               *perrno = -EFAULT;
                return I40E_NVMUPD_INVALID;
        }
 
@@ -982,6 +1074,12 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
                case I40E_NVM_SA:
                        upd_cmd = I40E_NVMUPD_READ_SA;
                        break;
+               case I40E_NVM_EXEC:
+                       if (module == 0xf)
+                               upd_cmd = I40E_NVMUPD_STATUS;
+                       else if (module == 0)
+                               upd_cmd = I40E_NVMUPD_GET_AQ_RESULT;
+                       break;
                }
                break;
 
@@ -1011,21 +1109,155 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
                case (I40E_NVM_CSUM|I40E_NVM_LCB):
                        upd_cmd = I40E_NVMUPD_CSUM_LCB;
                        break;
+               case I40E_NVM_EXEC:
+                       if (module == 0)
+                               upd_cmd = I40E_NVMUPD_EXEC_AQ;
+                       break;
                }
                break;
        }
-       i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n",
-                  i40e_nvm_update_state_str[upd_cmd],
-                  hw->nvmupd_state,
-                  hw->aq.nvm_release_on_done);
 
-       if (upd_cmd == I40E_NVMUPD_INVALID) {
-               *errno = -EFAULT;
+       return upd_cmd;
+}
+
+/**
+ * i40e_nvmupd_exec_aq - Run an AQ command
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command buffer
+ * @bytes: pointer to the data buffer
+ * @perrno: pointer to return error code
+ *
+ * cmd structure contains identifiers and data buffer
+ **/
+static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw,
+                                      struct i40e_nvm_access *cmd,
+                                      u8 *bytes, int *perrno)
+{
+       struct i40e_asq_cmd_details cmd_details;
+       i40e_status status;
+       struct i40e_aq_desc *aq_desc;
+       u32 buff_size = 0;
+       u8 *buff = NULL;
+       u32 aq_desc_len;
+       u32 aq_data_len;
+
+       i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__);
+       memset(&cmd_details, 0, sizeof(cmd_details));
+       cmd_details.wb_desc = &hw->nvm_wb_desc;
+
+       aq_desc_len = sizeof(struct i40e_aq_desc);
+       memset(&hw->nvm_wb_desc, 0, aq_desc_len);
+
+       /* get the aq descriptor */
+       if (cmd->data_size < aq_desc_len) {
                i40e_debug(hw, I40E_DEBUG_NVM,
-                          "i40e_nvmupd_validate_command returns %d errno %d\n",
-                          upd_cmd, *errno);
+                          "NVMUPD: not enough aq desc bytes for exec, size %d < %d\n",
+                          cmd->data_size, aq_desc_len);
+               *perrno = -EINVAL;
+               return I40E_ERR_PARAM;
        }
-       return upd_cmd;
+       aq_desc = (struct i40e_aq_desc *)bytes;
+
+       /* if data buffer needed, make sure it's ready */
+       aq_data_len = cmd->data_size - aq_desc_len;
+       buff_size = max_t(u32, aq_data_len, le16_to_cpu(aq_desc->datalen));
+       if (buff_size) {
+               if (!hw->nvm_buff.va) {
+                       status = i40e_allocate_virt_mem(hw, &hw->nvm_buff,
+                                                       hw->aq.asq_buf_size);
+                       if (status)
+                               i40e_debug(hw, I40E_DEBUG_NVM,
+                                          "NVMUPD: i40e_allocate_virt_mem for exec buff failed, %d\n",
+                                          status);
+               }
+
+               if (hw->nvm_buff.va) {
+                       buff = hw->nvm_buff.va;
+                       memcpy(buff, &bytes[aq_desc_len], aq_data_len);
+               }
+       }
+
+       /* and away we go! */
+       status = i40e_asq_send_command(hw, aq_desc, buff,
+                                      buff_size, &cmd_details);
+       if (status) {
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "i40e_nvmupd_exec_aq err %s aq_err %s\n",
+                          i40e_stat_str(hw, status),
+                          i40e_aq_str(hw, hw->aq.asq_last_status));
+               *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
+       }
+
+       return status;
+}
+
+/**
+ * i40e_nvmupd_get_aq_result - Get the results from the previous exec_aq
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command buffer
+ * @bytes: pointer to the data buffer
+ * @perrno: pointer to return error code
+ *
+ * cmd structure contains identifiers and data buffer
+ **/
+static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw,
+                                            struct i40e_nvm_access *cmd,
+                                            u8 *bytes, int *perrno)
+{
+       u32 aq_total_len;
+       u32 aq_desc_len;
+       int remainder;
+       u8 *buff;
+
+       i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__);
+
+       aq_desc_len = sizeof(struct i40e_aq_desc);
+       aq_total_len = aq_desc_len + le16_to_cpu(hw->nvm_wb_desc.datalen);
+
+       /* check offset range */
+       if (cmd->offset > aq_total_len) {
+               i40e_debug(hw, I40E_DEBUG_NVM, "%s: offset too big %d > %d\n",
+                          __func__, cmd->offset, aq_total_len);
+               *perrno = -EINVAL;
+               return I40E_ERR_PARAM;
+       }
+
+       /* check copylength range */
+       if (cmd->data_size > (aq_total_len - cmd->offset)) {
+               int new_len = aq_total_len - cmd->offset;
+
+               i40e_debug(hw, I40E_DEBUG_NVM, "%s: copy length %d too big, trimming to %d\n",
+                          __func__, cmd->data_size, new_len);
+               cmd->data_size = new_len;
+       }
+
+       remainder = cmd->data_size;
+       if (cmd->offset < aq_desc_len) {
+               u32 len = aq_desc_len - cmd->offset;
+
+               len = min(len, cmd->data_size);
+               i40e_debug(hw, I40E_DEBUG_NVM, "%s: aq_desc bytes %d to %d\n",
+                          __func__, cmd->offset, cmd->offset + len);
+
+               buff = ((u8 *)&hw->nvm_wb_desc) + cmd->offset;
+               memcpy(bytes, buff, len);
+
+               bytes += len;
+               remainder -= len;
+               buff = hw->nvm_buff.va;
+       } else {
+               buff = hw->nvm_buff.va + (cmd->offset - aq_desc_len);
+       }
+
+       if (remainder > 0) {
+               int start_byte = buff - (u8 *)hw->nvm_buff.va;
+
+               i40e_debug(hw, I40E_DEBUG_NVM, "%s: databuf bytes %d to %d\n",
+                          __func__, start_byte, start_byte + remainder);
+               memcpy(bytes, buff, remainder);
+       }
+
+       return 0;
 }
 
 /**
@@ -1033,14 +1265,15 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
  * @hw: pointer to hardware structure
  * @cmd: pointer to nvm update command buffer
  * @bytes: pointer to the data buffer
- * @errno: pointer to return error code
+ * @perrno: pointer to return error code
  *
  * cmd structure contains identifiers and data buffer
  **/
 static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
                                        struct i40e_nvm_access *cmd,
-                                       u8 *bytes, int *errno)
+                                       u8 *bytes, int *perrno)
 {
+       struct i40e_asq_cmd_details cmd_details;
        i40e_status status;
        u8 module, transaction;
        bool last;
@@ -1049,8 +1282,11 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
        module = i40e_nvmupd_get_module(cmd->config);
        last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA);
 
+       memset(&cmd_details, 0, sizeof(cmd_details));
+       cmd_details.wb_desc = &hw->nvm_wb_desc;
+
        status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
-                                 bytes, last, NULL);
+                                 bytes, last, &cmd_details);
        if (status) {
                i40e_debug(hw, I40E_DEBUG_NVM,
                           "i40e_nvmupd_nvm_read mod 0x%x  off 0x%x  len 0x%x\n",
@@ -1058,7 +1294,7 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
                i40e_debug(hw, I40E_DEBUG_NVM,
                           "i40e_nvmupd_nvm_read status %d aq %d\n",
                           status, hw->aq.asq_last_status);
-               *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
+               *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
        }
 
        return status;
@@ -1068,23 +1304,28 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
  * i40e_nvmupd_nvm_erase - Erase an NVM module
  * @hw: pointer to hardware structure
  * @cmd: pointer to nvm update command buffer
- * @errno: pointer to return error code
+ * @perrno: pointer to return error code
  *
  * module, offset, data_size and data are in cmd structure
  **/
 static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
                                         struct i40e_nvm_access *cmd,
-                                        int *errno)
+                                        int *perrno)
 {
        i40e_status status = 0;
+       struct i40e_asq_cmd_details cmd_details;
        u8 module, transaction;
        bool last;
 
        transaction = i40e_nvmupd_get_transaction(cmd->config);
        module = i40e_nvmupd_get_module(cmd->config);
        last = (transaction & I40E_NVM_LCB);
+
+       memset(&cmd_details, 0, sizeof(cmd_details));
+       cmd_details.wb_desc = &hw->nvm_wb_desc;
+
        status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
-                                  last, NULL);
+                                  last, &cmd_details);
        if (status) {
                i40e_debug(hw, I40E_DEBUG_NVM,
                           "i40e_nvmupd_nvm_erase mod 0x%x  off 0x%x len 0x%x\n",
@@ -1092,7 +1333,7 @@ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
                i40e_debug(hw, I40E_DEBUG_NVM,
                           "i40e_nvmupd_nvm_erase status %d aq %d\n",
                           status, hw->aq.asq_last_status);
-               *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
+               *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
        }
 
        return status;
@@ -1103,15 +1344,16 @@ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
  * @hw: pointer to hardware structure
  * @cmd: pointer to nvm update command buffer
  * @bytes: pointer to the data buffer
- * @errno: pointer to return error code
+ * @perrno: pointer to return error code
  *
  * module, offset, data_size and data are in cmd structure
  **/
 static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw,
                                         struct i40e_nvm_access *cmd,
-                                        u8 *bytes, int *errno)
+                                        u8 *bytes, int *perrno)
 {
        i40e_status status = 0;
+       struct i40e_asq_cmd_details cmd_details;
        u8 module, transaction;
        bool last;
 
@@ -1119,8 +1361,12 @@ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw,
        module = i40e_nvmupd_get_module(cmd->config);
        last = (transaction & I40E_NVM_LCB);
 
+       memset(&cmd_details, 0, sizeof(cmd_details));
+       cmd_details.wb_desc = &hw->nvm_wb_desc;
+
        status = i40e_aq_update_nvm(hw, module, cmd->offset,
-                                   (u16)cmd->data_size, bytes, last, NULL);
+                                   (u16)cmd->data_size, bytes, last,
+                                   &cmd_details);
        if (status) {
                i40e_debug(hw, I40E_DEBUG_NVM,
                           "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n",
@@ -1128,7 +1374,7 @@ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw,
                i40e_debug(hw, I40E_DEBUG_NVM,
                           "i40e_nvmupd_nvm_write status %d aq %d\n",
                           status, hw->aq.asq_last_status);
-               *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
+               *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
        }
 
        return status;
index dcb72a8ee8e590dfa390b43691cdbb3b39b9933d..bb9d583e5416fdf469b7853a92ce8c435901bafd 100644 (file)
@@ -58,8 +58,8 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask,
 void i40e_idle_aq(struct i40e_hw *hw);
 bool i40e_check_asq_alive(struct i40e_hw *hw);
 i40e_status i40e_aq_queue_shutdown(struct i40e_hw *hw, bool unloading);
-char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err);
-char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err);
+const char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err);
+const char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err);
 
 i40e_status i40e_aq_get_rss_lut(struct i40e_hw *hw, u16 seid,
                                bool pf_lut, u8 *lut, u16 lut_size);
@@ -258,7 +258,8 @@ i40e_status i40e_init_shared_code(struct i40e_hw *hw);
 i40e_status i40e_pf_reset(struct i40e_hw *hw);
 void i40e_clear_hw(struct i40e_hw *hw);
 void i40e_clear_pxe_mode(struct i40e_hw *hw);
-bool i40e_get_link_status(struct i40e_hw *hw);
+i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up);
+i40e_status i40e_update_link_info(struct i40e_hw *hw);
 i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr);
 i40e_status i40e_read_bw_from_alt_ram(struct i40e_hw *hw,
                                      u32 *max_bw, u32 *min_bw, bool *min_valid,
@@ -321,4 +322,6 @@ i40e_status i40e_aq_debug_dump(struct i40e_hw *hw, u8 cluster_id,
                               void *buff, u16 *ret_buff_size,
                               u8 *ret_next_table, u32 *ret_next_index,
                               struct i40e_asq_cmd_details *cmd_details);
+void i40e_add_filter_to_drop_tx_flow_control_frames(struct i40e_hw *hw,
+                                                   u16 vsi_seid);
 #endif /* _I40E_PROTOTYPE_H_ */
index 8c40d6ea15fda5cf4283769ab9d5753c67298917..565ca7c835bc3989459065e129254e0a16eadba6 100644 (file)
@@ -618,9 +618,8 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf)
 
        /* Attempt to register the clock before enabling the hardware. */
        pf->ptp_clock = ptp_clock_register(&pf->ptp_caps, &pf->pdev->dev);
-       if (IS_ERR(pf->ptp_clock)) {
+       if (IS_ERR(pf->ptp_clock))
                return PTR_ERR(pf->ptp_clock);
-       }
 
        /* clear the hwtstamp settings here during clock create, instead of
         * during regular init, so that we can maintain settings across a
@@ -675,8 +674,8 @@ void i40e_ptp_init(struct i40e_pf *pf)
                struct timespec64 ts;
                u32 regval;
 
-               dev_info(&pf->pdev->dev, "%s: added PHC on %s\n", __func__,
-                        netdev->name);
+               if (pf->hw.debug_mask & I40E_DEBUG_LAN)
+                       dev_info(&pf->pdev->dev, "PHC enabled\n");
                pf->flags |= I40E_FLAG_PTP;
 
                /* Ensure the clocks are running. */
index 738aca68f665f8cad4382e0b57b6c744c24dab79..635b3ac17877b153eba5c77a352ab8428d2b0742 100644 (file)
@@ -465,10 +465,11 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring,
                I40E_RX_PROG_STATUS_DESC_QW1_ERROR_SHIFT;
 
        if (error == BIT(I40E_RX_PROG_STATUS_DESC_FD_TBL_FULL_SHIFT)) {
+               pf->fd_inv = le32_to_cpu(rx_desc->wb.qword0.hi_dword.fd_id);
                if ((rx_desc->wb.qword0.hi_dword.fd_id != 0) ||
                    (I40E_DEBUG_FD & pf->hw.debug_mask))
                        dev_warn(&pdev->dev, "ntuple filter loc = %d, could not be added\n",
-                                rx_desc->wb.qword0.hi_dword.fd_id);
+                                pf->fd_inv);
 
                /* Check if the programming error is for ATR.
                 * If so, auto disable ATR and set a state for
@@ -600,20 +601,6 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring)
        }
 }
 
-/**
- * i40e_get_head - Retrieve head from head writeback
- * @tx_ring:  tx ring to fetch head of
- *
- * Returns value of Tx ring head based on value stored
- * in head write-back location
- **/
-static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
-{
-       void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
-
-       return le32_to_cpu(*(volatile __le32 *)head);
-}
-
 /**
  * i40e_get_tx_pending - how many tx descriptors not processed
  * @tx_ring: the ring of descriptors
@@ -621,7 +608,7 @@ static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
  * Since there is no access to the ring head register
  * in XL710, we need to use our local copies
  **/
-static u32 i40e_get_tx_pending(struct i40e_ring *ring)
+u32 i40e_get_tx_pending(struct i40e_ring *ring)
 {
        u32 head, tail;
 
@@ -635,50 +622,6 @@ static u32 i40e_get_tx_pending(struct i40e_ring *ring)
        return 0;
 }
 
-/**
- * i40e_check_tx_hang - Is there a hang in the Tx queue
- * @tx_ring: the ring of descriptors
- **/
-static bool i40e_check_tx_hang(struct i40e_ring *tx_ring)
-{
-       u32 tx_done = tx_ring->stats.packets;
-       u32 tx_done_old = tx_ring->tx_stats.tx_done_old;
-       u32 tx_pending = i40e_get_tx_pending(tx_ring);
-       struct i40e_pf *pf = tx_ring->vsi->back;
-       bool ret = false;
-
-       clear_check_for_tx_hang(tx_ring);
-
-       /* Check for a hung queue, but be thorough. This verifies
-        * that a transmit has been completed since the previous
-        * check AND there is at least one packet pending. The
-        * ARMED bit is set to indicate a potential hang. The
-        * bit is cleared if a pause frame is received to remove
-        * false hang detection due to PFC or 802.3x frames. By
-        * requiring this to fail twice we avoid races with
-        * PFC clearing the ARMED bit and conditions where we
-        * run the check_tx_hang logic with a transmit completion
-        * pending but without time to complete it yet.
-        */
-       if ((tx_done_old == tx_done) && tx_pending) {
-               /* make sure it is true for two checks in a row */
-               ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED,
-                                      &tx_ring->state);
-       } else if (tx_done_old == tx_done &&
-                  (tx_pending < I40E_MIN_DESC_PENDING) && (tx_pending > 0)) {
-               if (I40E_DEBUG_FLOW & pf->hw.debug_mask)
-                       dev_info(tx_ring->dev, "HW needs some more descs to do a cacheline flush. tx_pending %d, queue %d",
-                                tx_pending, tx_ring->queue_index);
-               pf->tx_sluggish_count++;
-       } else {
-               /* update completed stats and disarm the hang check */
-               tx_ring->tx_stats.tx_done_old = tx_done;
-               clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state);
-       }
-
-       return ret;
-}
-
 #define WB_STRIDE 0x3
 
 /**
@@ -784,42 +727,21 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
        tx_ring->q_vector->tx.total_bytes += total_bytes;
        tx_ring->q_vector->tx.total_packets += total_packets;
 
-       /* check to see if there are any non-cache aligned descriptors
-        * waiting to be written back, and kick the hardware to force
-        * them to be written back in case of napi polling
-        */
-       if (budget &&
-           !((i & WB_STRIDE) == WB_STRIDE) &&
-           !test_bit(__I40E_DOWN, &tx_ring->vsi->state) &&
-           (I40E_DESC_UNUSED(tx_ring) != tx_ring->count))
-               tx_ring->arm_wb = true;
-       else
-               tx_ring->arm_wb = false;
-
-       if (check_for_tx_hang(tx_ring) && i40e_check_tx_hang(tx_ring)) {
-               /* schedule immediate reset if we believe we hung */
-               dev_info(tx_ring->dev, "Detected Tx Unit Hang\n"
-                        "  VSI                  <%d>\n"
-                        "  Tx Queue             <%d>\n"
-                        "  next_to_use          <%x>\n"
-                        "  next_to_clean        <%x>\n",
-                        tx_ring->vsi->seid,
-                        tx_ring->queue_index,
-                        tx_ring->next_to_use, i);
-
-               netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index);
-
-               dev_info(tx_ring->dev,
-                        "tx hang detected on queue %d, reset requested\n",
-                        tx_ring->queue_index);
-
-               /* do not fire the reset immediately, wait for the stack to
-                * decide we are truly stuck, also prevents every queue from
-                * simultaneously requesting a reset
+       if (tx_ring->flags & I40E_TXR_FLAGS_WB_ON_ITR) {
+               unsigned int j = 0;
+
+               /* check to see if there are < 4 descriptors
+                * waiting to be written back, then kick the hardware to force
+                * them to be written back in case we stay in NAPI.
+                * In this mode on X722 we do not enable Interrupt.
                 */
+               j = i40e_get_tx_pending(tx_ring);
 
-               /* the adapter is about to reset, no point in enabling polling */
-               budget = 1;
+               if (budget &&
+                   ((j / (WB_STRIDE + 1)) == 0) && (j != 0) &&
+                   !test_bit(__I40E_DOWN, &tx_ring->vsi->state) &&
+                   (I40E_DESC_UNUSED(tx_ring) != tx_ring->count))
+                       tx_ring->arm_wb = true;
        }
 
        netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev,
@@ -851,7 +773,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
  * @q_vector: the vector  on which to force writeback
  *
  **/
-static void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector)
+void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector)
 {
        u16 flags = q_vector->tx.ring[0].flags;
 
@@ -893,6 +815,8 @@ static void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector)
  * i40e_set_new_dynamic_itr - Find new ITR level
  * @rc: structure containing ring performance data
  *
+ * Returns true if ITR changed, false if not
+ *
  * Stores a new ITR value based on packets and byte counts during
  * the last interrupt.  The advantage of per interrupt computation
  * is faster updates and more accurate ITR for the current traffic
@@ -901,21 +825,32 @@ static void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector)
  * testing data as well as attempting to minimize response time
  * while increasing bulk throughput.
  **/
-static void i40e_set_new_dynamic_itr(struct i40e_ring_container *rc)
+static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc)
 {
        enum i40e_latency_range new_latency_range = rc->latency_range;
+       struct i40e_q_vector *qv = rc->ring->q_vector;
        u32 new_itr = rc->itr;
        int bytes_per_int;
+       int usecs;
 
        if (rc->total_packets == 0 || !rc->itr)
-               return;
+               return false;
 
        /* simple throttlerate management
-        *   0-10MB/s   lowest (100000 ints/s)
+        *   0-10MB/s   lowest (50000 ints/s)
         *  10-20MB/s   low    (20000 ints/s)
-        *  20-1249MB/s bulk   (8000 ints/s)
+        *  20-1249MB/s bulk   (18000 ints/s)
+        *  > 40000 Rx packets per second (8000 ints/s)
+        *
+        * The math works out because the divisor is in 10^(-6) which
+        * turns the bytes/us input value into MB/s values, but
+        * make sure to use usecs, as the register values written
+        * are in 2 usec increments in the ITR registers, and make sure
+        * to use the smoothed values that the countdown timer gives us.
         */
-       bytes_per_int = rc->total_bytes / rc->itr;
+       usecs = (rc->itr << 1) * ITR_COUNTDOWN_START;
+       bytes_per_int = rc->total_bytes / usecs;
+
        switch (new_latency_range) {
        case I40E_LOWEST_LATENCY:
                if (bytes_per_int > 10)
@@ -928,35 +863,52 @@ static void i40e_set_new_dynamic_itr(struct i40e_ring_container *rc)
                        new_latency_range = I40E_LOWEST_LATENCY;
                break;
        case I40E_BULK_LATENCY:
-               if (bytes_per_int <= 20)
-                       new_latency_range = I40E_LOW_LATENCY;
-               break;
+       case I40E_ULTRA_LATENCY:
        default:
                if (bytes_per_int <= 20)
                        new_latency_range = I40E_LOW_LATENCY;
                break;
        }
+
+       /* this is to adjust RX more aggressively when streaming small
+        * packets.  The value of 40000 was picked as it is just beyond
+        * what the hardware can receive per second if in low latency
+        * mode.
+        */
+#define RX_ULTRA_PACKET_RATE 40000
+
+       if ((((rc->total_packets * 1000000) / usecs) > RX_ULTRA_PACKET_RATE) &&
+           (&qv->rx == rc))
+               new_latency_range = I40E_ULTRA_LATENCY;
+
        rc->latency_range = new_latency_range;
 
        switch (new_latency_range) {
        case I40E_LOWEST_LATENCY:
-               new_itr = I40E_ITR_100K;
+               new_itr = I40E_ITR_50K;
                break;
        case I40E_LOW_LATENCY:
                new_itr = I40E_ITR_20K;
                break;
        case I40E_BULK_LATENCY:
+               new_itr = I40E_ITR_18K;
+               break;
+       case I40E_ULTRA_LATENCY:
                new_itr = I40E_ITR_8K;
                break;
        default:
                break;
        }
 
-       if (new_itr != rc->itr)
-               rc->itr = new_itr;
-
        rc->total_bytes = 0;
        rc->total_packets = 0;
+
+       if (new_itr != rc->itr) {
+               rc->itr = new_itr;
+               return true;
+       }
+
+       return false;
 }
 
 /**
@@ -1002,6 +954,8 @@ int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring)
        if (!dev)
                return -ENOMEM;
 
+       /* warn if we are about to overwrite the pointer */
+       WARN_ON(tx_ring->tx_bi);
        bi_size = sizeof(struct i40e_tx_buffer) * tx_ring->count;
        tx_ring->tx_bi = kzalloc(bi_size, GFP_KERNEL);
        if (!tx_ring->tx_bi)
@@ -1162,6 +1116,8 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring)
        struct device *dev = rx_ring->dev;
        int bi_size;
 
+       /* warn if we are about to overwrite the pointer */
+       WARN_ON(rx_ring->rx_bi);
        bi_size = sizeof(struct i40e_rx_buffer) * rx_ring->count;
        rx_ring->rx_bi = kzalloc(bi_size, GFP_KERNEL);
        if (!rx_ring->rx_bi)
@@ -1342,16 +1298,11 @@ static void i40e_receive_skb(struct i40e_ring *rx_ring,
                             struct sk_buff *skb, u16 vlan_tag)
 {
        struct i40e_q_vector *q_vector = rx_ring->q_vector;
-       struct i40e_vsi *vsi = rx_ring->vsi;
-       u64 flags = vsi->back->flags;
 
        if (vlan_tag & VLAN_VID_MASK)
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
 
-       if (flags & I40E_FLAG_IN_NETPOLL)
-               netif_rx(skb);
-       else
-               napi_gro_receive(&q_vector->napi, skb);
+       napi_gro_receive(&q_vector->napi, skb);
 }
 
 /**
@@ -1518,7 +1469,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
        unsigned int total_rx_bytes = 0, total_rx_packets = 0;
        u16 rx_packet_len, rx_header_len, rx_sph, rx_hbo;
        u16 cleaned_count = I40E_DESC_UNUSED(rx_ring);
-       const int current_node = numa_node_id();
+       const int current_node = numa_mem_id();
        struct i40e_vsi *vsi = rx_ring->vsi;
        u16 i = rx_ring->next_to_clean;
        union i40e_rx_desc *rx_desc;
@@ -1596,6 +1547,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
                cleaned_count++;
                if (rx_hbo || rx_sph) {
                        int len;
+
                        if (rx_hbo)
                                len = I40E_RX_HDR_SIZE;
                        else
@@ -1781,9 +1733,6 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget)
                /* ERR_MASK will only have valid bits if EOP set */
                if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) {
                        dev_kfree_skb_any(skb);
-                       /* TODO: shouldn't we increment a counter indicating the
-                        * drop?
-                        */
                        continue;
                }
 
@@ -1828,6 +1777,21 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget)
        return total_rx_packets;
 }
 
+static u32 i40e_buildreg_itr(const int type, const u16 itr)
+{
+       u32 val;
+
+       val = I40E_PFINT_DYN_CTLN_INTENA_MASK |
+             I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
+             (type << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT) |
+             (itr << I40E_PFINT_DYN_CTLN_INTERVAL_SHIFT);
+
+       return val;
+}
+
+/* a small macro to shorten up some long lines */
+#define INTREG I40E_PFINT_DYN_CTLN
+
 /**
  * i40e_update_enable_itr - Update itr and re-enable MSIX interrupt
  * @vsi: the VSI we care about
@@ -1838,56 +1802,69 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi,
                                          struct i40e_q_vector *q_vector)
 {
        struct i40e_hw *hw = &vsi->back->hw;
-       u16 old_itr;
+       bool rx = false, tx = false;
+       u32 rxval, txval;
        int vector;
-       u32 val;
 
        vector = (q_vector->v_idx + vsi->base_vector);
+
+       /* avoid dynamic calculation if in countdown mode OR if
+        * all dynamic is disabled
+        */
+       rxval = txval = i40e_buildreg_itr(I40E_ITR_NONE, 0);
+
+       if (q_vector->itr_countdown > 0 ||
+           (!ITR_IS_DYNAMIC(vsi->rx_itr_setting) &&
+            !ITR_IS_DYNAMIC(vsi->tx_itr_setting))) {
+               goto enable_int;
+       }
+
        if (ITR_IS_DYNAMIC(vsi->rx_itr_setting)) {
-               old_itr = q_vector->rx.itr;
-               i40e_set_new_dynamic_itr(&q_vector->rx);
-               if (old_itr != q_vector->rx.itr) {
-                       val = I40E_PFINT_DYN_CTLN_INTENA_MASK |
-                       I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
-                       (I40E_RX_ITR <<
-                               I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT) |
-                       (q_vector->rx.itr <<
-                               I40E_PFINT_DYN_CTLN_INTERVAL_SHIFT);
-               } else {
-                       val = I40E_PFINT_DYN_CTLN_INTENA_MASK |
-                       I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
-                       (I40E_ITR_NONE <<
-                               I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT);
-               }
-               if (!test_bit(__I40E_DOWN, &vsi->state))
-                       wr32(hw, I40E_PFINT_DYN_CTLN(vector - 1), val);
-       } else {
-               i40e_irq_dynamic_enable(vsi,
-                                       q_vector->v_idx + vsi->base_vector);
+               rx = i40e_set_new_dynamic_itr(&q_vector->rx);
+               rxval = i40e_buildreg_itr(I40E_RX_ITR, q_vector->rx.itr);
        }
+
        if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) {
-               old_itr = q_vector->tx.itr;
-               i40e_set_new_dynamic_itr(&q_vector->tx);
-               if (old_itr != q_vector->tx.itr) {
-                       val = I40E_PFINT_DYN_CTLN_INTENA_MASK |
-                               I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
-                               (I40E_TX_ITR <<
-                                  I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT) |
-                               (q_vector->tx.itr <<
-                                  I40E_PFINT_DYN_CTLN_INTERVAL_SHIFT);
-               } else {
-                       val = I40E_PFINT_DYN_CTLN_INTENA_MASK |
-                               I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
-                               (I40E_ITR_NONE <<
-                                  I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT);
-               }
-               if (!test_bit(__I40E_DOWN, &vsi->state))
-                       wr32(hw, I40E_PFINT_DYN_CTLN(q_vector->v_idx +
-                             vsi->base_vector - 1), val);
-       } else {
-               i40e_irq_dynamic_enable(vsi,
-                                       q_vector->v_idx + vsi->base_vector);
+               tx = i40e_set_new_dynamic_itr(&q_vector->tx);
+               txval = i40e_buildreg_itr(I40E_TX_ITR, q_vector->tx.itr);
+       }
+
+       if (rx || tx) {
+               /* get the higher of the two ITR adjustments and
+                * use the same value for both ITR registers
+                * when in adaptive mode (Rx and/or Tx)
+                */
+               u16 itr = max(q_vector->tx.itr, q_vector->rx.itr);
+
+               q_vector->tx.itr = q_vector->rx.itr = itr;
+               txval = i40e_buildreg_itr(I40E_TX_ITR, itr);
+               tx = true;
+               rxval = i40e_buildreg_itr(I40E_RX_ITR, itr);
+               rx = true;
        }
+
+       /* only need to enable the interrupt once, but need
+        * to possibly update both ITR values
+        */
+       if (rx) {
+               /* set the INTENA_MSK_MASK so that this first write
+                * won't actually enable the interrupt, instead just
+                * updating the ITR (it's bit 31 PF and VF)
+                */
+               rxval |= BIT(31);
+               /* don't check _DOWN because interrupt isn't being enabled */
+               wr32(hw, INTREG(vector - 1), rxval);
+       }
+
+enable_int:
+       if (!test_bit(__I40E_DOWN, &vsi->state))
+               wr32(hw, INTREG(vector - 1), txval);
+
+       if (q_vector->itr_countdown)
+               q_vector->itr_countdown--;
+       else
+               q_vector->itr_countdown = ITR_COUNTDOWN_START;
+
 }
 
 /**
@@ -1908,7 +1885,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
        bool clean_complete = true;
        bool arm_wb = false;
        int budget_per_ring;
-       int cleaned;
+       int work_done = 0;
 
        if (test_bit(__I40E_DOWN, &vsi->state)) {
                napi_complete(napi);
@@ -1921,24 +1898,34 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
        i40e_for_each_ring(ring, q_vector->tx) {
                clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit);
                arm_wb |= ring->arm_wb;
+               ring->arm_wb = false;
        }
 
+       /* Handle case where we are called by netpoll with a budget of 0 */
+       if (budget <= 0)
+               goto tx_only;
+
        /* We attempt to distribute budget to each Rx queue fairly, but don't
         * allow the budget to go below 1 because that would exit polling early.
         */
        budget_per_ring = max(budget/q_vector->num_ringpairs, 1);
 
        i40e_for_each_ring(ring, q_vector->rx) {
+               int cleaned;
+
                if (ring_is_ps_enabled(ring))
                        cleaned = i40e_clean_rx_irq_ps(ring, budget_per_ring);
                else
                        cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring);
+
+               work_done += cleaned;
                /* if we didn't clean as many as budgeted, we must be done */
                clean_complete &= (budget_per_ring != cleaned);
        }
 
        /* If work not completed, return budget and polling will return */
        if (!clean_complete) {
+tx_only:
                if (arm_wb)
                        i40e_force_wb(vsi, q_vector);
                return budget;
@@ -1948,7 +1935,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
                q_vector->arm_wb_state = false;
 
        /* Work is done so exit the polling mode and re-enable the interrupt */
-       napi_complete(napi);
+       napi_complete_done(napi, work_done);
        if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) {
                i40e_update_enable_itr(vsi, q_vector);
        } else { /* Legacy mode */
@@ -2156,6 +2143,7 @@ static inline int i40e_tx_prepare_vlan_flags(struct sk_buff *skb,
        /* else if it is a SW VLAN, check the next protocol and store the tag */
        } else if (protocol == htons(ETH_P_8021Q)) {
                struct vlan_hdr *vhdr, _vhdr;
+
                vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), &_vhdr);
                if (!vhdr)
                        return -EINVAL;
@@ -2199,6 +2187,7 @@ out:
  * @tx_ring:  ptr to the ring to send
  * @skb:      ptr to the skb we're sending
  * @hdr_len:  ptr to the size of the packet header
+ * @cd_type_cmd_tso_mss: ptr to u64 object
  * @cd_tunneling: ptr to context descriptor bits
  *
  * Returns 0 if no TSO can happen, 1 if tso is going, or error
@@ -2258,6 +2247,7 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
  * @tx_ring:  ptr to the ring to send
  * @skb:      ptr to the skb we're sending
  * @tx_flags: the collected send information
+ * @cd_type_cmd_tso_mss: ptr to u64 object
  *
  * Returns 0 if no Tx timestamp can happen and 1 if the timestamp will happen
  **/
@@ -2300,6 +2290,7 @@ static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb,
  * @tx_flags: pointer to Tx flags currently set
  * @td_cmd: Tx descriptor command bits to set
  * @td_offset: Tx descriptor header offsets to set
+ * @tx_ring: Tx descriptor ring
  * @cd_tunneling: ptr to context desc bits
  **/
 static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags,
@@ -2324,6 +2315,9 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags,
                        l4_tunnel = I40E_TXD_CTX_UDP_TUNNELING;
                        *tx_flags |= I40E_TX_FLAGS_VXLAN_TUNNEL;
                        break;
+               case IPPROTO_GRE:
+                       l4_tunnel = I40E_TXD_CTX_GRE_TUNNELING;
+                       break;
                default:
                        return;
                }
@@ -2581,6 +2575,9 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
        u32 td_tag = 0;
        dma_addr_t dma;
        u16 gso_segs;
+       u16 desc_count = 0;
+       bool tail_bump = true;
+       bool do_rs = false;
 
        if (tx_flags & I40E_TX_FLAGS_HW_VLAN) {
                td_cmd |= I40E_TX_DESC_CMD_IL2TAG1;
@@ -2621,6 +2618,8 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
 
                        tx_desc++;
                        i++;
+                       desc_count++;
+
                        if (i == tx_ring->count) {
                                tx_desc = I40E_TX_DESC(tx_ring, 0);
                                i = 0;
@@ -2640,6 +2639,8 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
 
                tx_desc++;
                i++;
+               desc_count++;
+
                if (i == tx_ring->count) {
                        tx_desc = I40E_TX_DESC(tx_ring, 0);
                        i = 0;
@@ -2654,34 +2655,6 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
                tx_bi = &tx_ring->tx_bi[i];
        }
 
-       /* Place RS bit on last descriptor of any packet that spans across the
-        * 4th descriptor (WB_STRIDE aka 0x3) in a 64B cacheline.
-        */
-       if (((i & WB_STRIDE) != WB_STRIDE) &&
-           (first <= &tx_ring->tx_bi[i]) &&
-           (first >= &tx_ring->tx_bi[i & ~WB_STRIDE])) {
-               tx_desc->cmd_type_offset_bsz =
-                       build_ctob(td_cmd, td_offset, size, td_tag) |
-                       cpu_to_le64((u64)I40E_TX_DESC_CMD_EOP <<
-                                        I40E_TXD_QW1_CMD_SHIFT);
-       } else {
-               tx_desc->cmd_type_offset_bsz =
-                       build_ctob(td_cmd, td_offset, size, td_tag) |
-                       cpu_to_le64((u64)I40E_TXD_CMD <<
-                                        I40E_TXD_QW1_CMD_SHIFT);
-       }
-
-       netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev,
-                                                tx_ring->queue_index),
-                            first->bytecount);
-
-       /* Force memory writes to complete before letting h/w
-        * know there are new descriptors to fetch.  (Only
-        * applicable for weak-ordered memory model archs,
-        * such as IA-64).
-        */
-       wmb();
-
        /* set next_to_watch value indicating a packet is present */
        first->next_to_watch = tx_desc;
 
@@ -2691,15 +2664,72 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
 
        tx_ring->next_to_use = i;
 
+       netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev,
+                                                tx_ring->queue_index),
+                                                first->bytecount);
        i40e_maybe_stop_tx(tx_ring, DESC_NEEDED);
+
+       /* Algorithm to optimize tail and RS bit setting:
+        * if xmit_more is supported
+        *      if xmit_more is true
+        *              do not update tail and do not mark RS bit.
+        *      if xmit_more is false and last xmit_more was false
+        *              if every packet spanned less than 4 desc
+        *                      then set RS bit on 4th packet and update tail
+        *                      on every packet
+        *              else
+        *                      update tail and set RS bit on every packet.
+        *      if xmit_more is false and last_xmit_more was true
+        *              update tail and set RS bit.
+        *
+        * Optimization: wmb to be issued only in case of tail update.
+        * Also optimize the Descriptor WB path for RS bit with the same
+        * algorithm.
+        *
+        * Note: If there are less than 4 packets
+        * pending and interrupts were disabled the service task will
+        * trigger a force WB.
+        */
+       if (skb->xmit_more  &&
+           !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev,
+                                                   tx_ring->queue_index))) {
+               tx_ring->flags |= I40E_TXR_FLAGS_LAST_XMIT_MORE_SET;
+               tail_bump = false;
+       } else if (!skb->xmit_more &&
+                  !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev,
+                                                      tx_ring->queue_index)) &&
+                  (!(tx_ring->flags & I40E_TXR_FLAGS_LAST_XMIT_MORE_SET)) &&
+                  (tx_ring->packet_stride < WB_STRIDE) &&
+                  (desc_count < WB_STRIDE)) {
+               tx_ring->packet_stride++;
+       } else {
+               tx_ring->packet_stride = 0;
+               tx_ring->flags &= ~I40E_TXR_FLAGS_LAST_XMIT_MORE_SET;
+               do_rs = true;
+       }
+       if (do_rs)
+               tx_ring->packet_stride = 0;
+
+       tx_desc->cmd_type_offset_bsz =
+                       build_ctob(td_cmd, td_offset, size, td_tag) |
+                       cpu_to_le64((u64)(do_rs ? I40E_TXD_CMD :
+                                                 I40E_TX_DESC_CMD_EOP) <<
+                                                 I40E_TXD_QW1_CMD_SHIFT);
+
        /* notify HW of packet */
-       if (!skb->xmit_more ||
-           netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev,
-                                                  tx_ring->queue_index)))
-               writel(i, tx_ring->tail);
-       else
+       if (!tail_bump)
                prefetchw(tx_desc + 1);
 
+       if (tail_bump) {
+               /* Force memory writes to complete before letting h/w
+                * know there are new descriptors to fetch.  (Only
+                * applicable for weak-ordered memory model archs,
+                * such as IA-64).
+                */
+               wmb();
+               writel(i, tx_ring->tail);
+       }
+
        return;
 
 dma_error:
@@ -2776,6 +2806,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
        u8 hdr_len = 0;
        int tsyn;
        int tso;
+
        if (0 == i40e_xmit_descriptor_count(skb, tx_ring))
                return NETDEV_TX_BUSY;
 
@@ -2808,10 +2839,11 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
        if (tsyn)
                tx_flags |= I40E_TX_FLAGS_TSYN;
 
-       if (i40e_chk_linearize(skb, tx_flags))
+       if (i40e_chk_linearize(skb, tx_flags)) {
                if (skb_linearize(skb))
                        goto out_drop;
-
+               tx_ring->tx_stats.tx_linearize++;
+       }
        skb_tx_timestamp(skb);
 
        /* always enable CRC insertion offload */
index f1385a1989fa822fba79074348549ba631f2509e..6779fb771d6af9b9c8fcf46fd56ccb498c2951e3 100644 (file)
 #define I40E_MAX_ITR               0x0FF0  /* reg uses 2 usec resolution */
 #define I40E_MIN_ITR               0x0001  /* reg uses 2 usec resolution */
 #define I40E_ITR_100K              0x0005
+#define I40E_ITR_50K               0x000A
 #define I40E_ITR_20K               0x0019
+#define I40E_ITR_18K               0x001B
 #define I40E_ITR_8K                0x003E
 #define I40E_ITR_4K                0x007A
-#define I40E_ITR_RX_DEF            I40E_ITR_8K
-#define I40E_ITR_TX_DEF            I40E_ITR_4K
+#define I40E_MAX_INTRL             0x3B    /* reg uses 4 usec resolution */
+#define I40E_ITR_RX_DEF            I40E_ITR_20K
+#define I40E_ITR_TX_DEF            I40E_ITR_20K
 #define I40E_ITR_DYNAMIC           0x8000  /* use top bit as a flag */
 #define I40E_MIN_INT_RATE          250     /* ~= 1000000 / (I40E_MAX_ITR * 2) */
 #define I40E_MAX_INT_RATE          500000  /* == 1000000 / (I40E_MIN_ITR * 2) */
 #define ITR_TO_REG(setting) ((setting & ~I40E_ITR_DYNAMIC) >> 1)
 #define ITR_IS_DYNAMIC(setting) (!!(setting & I40E_ITR_DYNAMIC))
 #define ITR_REG_TO_USEC(itr_reg) (itr_reg << 1)
+/* 0x40 is the enable bit for interrupt rate limiting, and must be set if
+ * the value of the rate limit is non-zero
+ */
+#define INTRL_ENA                  BIT(6)
+#define INTRL_REG_TO_USEC(intrl) ((intrl & ~INTRL_ENA) << 2)
+#define INTRL_USEC_TO_REG(set) ((set) ? ((set) >> 2) | INTRL_ENA : 0)
+#define I40E_INTRL_8K              125     /* 8000 ints/sec */
+#define I40E_INTRL_62K             16      /* 62500 ints/sec */
+#define I40E_INTRL_83K             12      /* 83333 ints/sec */
 
 #define I40E_QUEUE_END_OF_LIST 0x7FF
 
@@ -79,12 +91,12 @@ enum i40e_dyn_idx_t {
        BIT_ULL(I40E_FILTER_PCTYPE_L2_PAYLOAD))
 
 #define I40E_DEFAULT_RSS_HENA_EXPANDED (I40E_DEFAULT_RSS_HENA | \
-       BIT(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK) | \
-       BIT(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \
-       BIT(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \
-       BIT(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK) | \
-       BIT(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \
-       BIT(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP))
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK) | \
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK) | \
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP))
 
 #define i40e_pf_get_default_rss_hena(pf) \
        (((pf)->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \
@@ -165,6 +177,7 @@ struct i40e_tx_buffer {
        };
        unsigned int bytecount;
        unsigned short gso_segs;
+
        DEFINE_DMA_UNMAP_ADDR(dma);
        DEFINE_DMA_UNMAP_LEN(len);
        u32 tx_flags;
@@ -188,6 +201,7 @@ struct i40e_tx_queue_stats {
        u64 restart_queue;
        u64 tx_busy;
        u64 tx_done_old;
+       u64 tx_linearize;
 };
 
 struct i40e_rx_queue_stats {
@@ -199,8 +213,6 @@ struct i40e_rx_queue_stats {
 enum i40e_ring_state_t {
        __I40E_TX_FDIR_INIT_DONE,
        __I40E_TX_XPS_INIT_DONE,
-       __I40E_TX_DETECT_HANG,
-       __I40E_HANG_CHECK_ARMED,
        __I40E_RX_PS_ENABLED,
        __I40E_RX_16BYTE_DESC_ENABLED,
 };
@@ -211,12 +223,6 @@ enum i40e_ring_state_t {
        set_bit(__I40E_RX_PS_ENABLED, &(ring)->state)
 #define clear_ring_ps_enabled(ring) \
        clear_bit(__I40E_RX_PS_ENABLED, &(ring)->state)
-#define check_for_tx_hang(ring) \
-       test_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
-#define set_check_for_tx_hang(ring) \
-       set_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
-#define clear_check_for_tx_hang(ring) \
-       clear_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
 #define ring_is_16byte_desc_enabled(ring) \
        test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state)
 #define set_ring_16byte_desc_enabled(ring) \
@@ -264,10 +270,12 @@ struct i40e_ring {
 
        bool ring_active;               /* is ring online or not */
        bool arm_wb;            /* do something to arm write back */
+       u8 packet_stride;
 
        u16 flags;
 #define I40E_TXR_FLAGS_WB_ON_ITR       BIT(0)
 #define I40E_TXR_FLAGS_OUTER_UDP_CSUM  BIT(1)
+#define I40E_TXR_FLAGS_LAST_XMIT_MORE_SET BIT(2)
 
        /* stats structs */
        struct i40e_queue_stats stats;
@@ -290,6 +298,7 @@ enum i40e_latency_range {
        I40E_LOWEST_LATENCY = 0,
        I40E_LOW_LATENCY = 1,
        I40E_BULK_LATENCY = 2,
+       I40E_ULTRA_LATENCY = 3,
 };
 
 struct i40e_ring_container {
@@ -326,4 +335,20 @@ int i40e_xmit_descriptor_count(struct sk_buff *skb, struct i40e_ring *tx_ring);
 int i40e_tx_prepare_vlan_flags(struct sk_buff *skb,
                               struct i40e_ring *tx_ring, u32 *flags);
 #endif
+void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector);
+u32 i40e_get_tx_pending(struct i40e_ring *ring);
+
+/**
+ * i40e_get_head - Retrieve head from head writeback
+ * @tx_ring:  tx ring to fetch head of
+ *
+ * Returns value of Tx ring head based on value stored
+ * in head write-back location
+ **/
+static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
+{
+       void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
+
+       return le32_to_cpu(*(volatile __le32 *)head);
+}
 #endif /* _I40E_TXRX_H_ */
index 4842239ee77791e6ed428d38837fb59e37e0a524..dd2da356d9a1bb6628fcd0825f400350fc037e6f 100644 (file)
 #include "i40e_adminq.h"
 #include "i40e_hmc.h"
 #include "i40e_lan_hmc.h"
-
-/* Device IDs */
-#define I40E_DEV_ID_SFP_XL710          0x1572
-#define I40E_DEV_ID_QEMU               0x1574
-#define I40E_DEV_ID_KX_A               0x157F
-#define I40E_DEV_ID_KX_B               0x1580
-#define I40E_DEV_ID_KX_C               0x1581
-#define I40E_DEV_ID_QSFP_A             0x1583
-#define I40E_DEV_ID_QSFP_B             0x1584
-#define I40E_DEV_ID_QSFP_C             0x1585
-#define I40E_DEV_ID_10G_BASE_T         0x1586
-#define I40E_DEV_ID_20G_KR2            0x1587
-#define I40E_DEV_ID_VF                 0x154C
-#define I40E_DEV_ID_VF_HV              0x1571
-#define I40E_DEV_ID_SFP_X722           0x37D0
-#define I40E_DEV_ID_1G_BASE_T_X722     0x37D1
-#define I40E_DEV_ID_10G_BASE_T_X722    0x37D2
-#define I40E_DEV_ID_X722_VF            0x37CD
-#define I40E_DEV_ID_X722_VF_HV         0x37D9
-
-#define i40e_is_40G_device(d)          ((d) == I40E_DEV_ID_QSFP_A  || \
-                                        (d) == I40E_DEV_ID_QSFP_B  || \
-                                        (d) == I40E_DEV_ID_QSFP_C)
+#include "i40e_devids.h"
 
 /* I40E_MASK is a macro used on 32 bit registers */
 #define I40E_MASK(mask, shift) (mask << shift)
@@ -158,14 +136,14 @@ enum i40e_set_fc_aq_failures {
 };
 
 enum i40e_vsi_type {
-       I40E_VSI_MAIN = 0,
-       I40E_VSI_VMDQ1,
-       I40E_VSI_VMDQ2,
-       I40E_VSI_CTRL,
-       I40E_VSI_FCOE,
-       I40E_VSI_MIRROR,
-       I40E_VSI_SRIOV,
-       I40E_VSI_FDIR,
+       I40E_VSI_MAIN   = 0,
+       I40E_VSI_VMDQ1  = 1,
+       I40E_VSI_VMDQ2  = 2,
+       I40E_VSI_CTRL   = 3,
+       I40E_VSI_FCOE   = 4,
+       I40E_VSI_MIRROR = 5,
+       I40E_VSI_SRIOV  = 6,
+       I40E_VSI_FDIR   = 7,
        I40E_VSI_TYPE_UNKNOWN
 };
 
@@ -189,16 +167,65 @@ struct i40e_link_status {
        bool crc_enable;
        u8 pacing;
        u8 requested_speeds;
+       u8 module_type[3];
+       /* 1st byte: module identifier */
+#define I40E_MODULE_TYPE_SFP           0x03
+#define I40E_MODULE_TYPE_QSFP          0x0D
+       /* 2nd byte: ethernet compliance codes for 10/40G */
+#define I40E_MODULE_TYPE_40G_ACTIVE    0x01
+#define I40E_MODULE_TYPE_40G_LR4       0x02
+#define I40E_MODULE_TYPE_40G_SR4       0x04
+#define I40E_MODULE_TYPE_40G_CR4       0x08
+#define I40E_MODULE_TYPE_10G_BASE_SR   0x10
+#define I40E_MODULE_TYPE_10G_BASE_LR   0x20
+#define I40E_MODULE_TYPE_10G_BASE_LRM  0x40
+#define I40E_MODULE_TYPE_10G_BASE_ER   0x80
+       /* 3rd byte: ethernet compliance codes for 1G */
+#define I40E_MODULE_TYPE_1000BASE_SX   0x01
+#define I40E_MODULE_TYPE_1000BASE_LX   0x02
+#define I40E_MODULE_TYPE_1000BASE_CX   0x04
+#define I40E_MODULE_TYPE_1000BASE_T    0x08
+};
+
+enum i40e_aq_capabilities_phy_type {
+       I40E_CAP_PHY_TYPE_SGMII           = BIT(I40E_PHY_TYPE_SGMII),
+       I40E_CAP_PHY_TYPE_1000BASE_KX     = BIT(I40E_PHY_TYPE_1000BASE_KX),
+       I40E_CAP_PHY_TYPE_10GBASE_KX4     = BIT(I40E_PHY_TYPE_10GBASE_KX4),
+       I40E_CAP_PHY_TYPE_10GBASE_KR      = BIT(I40E_PHY_TYPE_10GBASE_KR),
+       I40E_CAP_PHY_TYPE_40GBASE_KR4     = BIT(I40E_PHY_TYPE_40GBASE_KR4),
+       I40E_CAP_PHY_TYPE_XAUI            = BIT(I40E_PHY_TYPE_XAUI),
+       I40E_CAP_PHY_TYPE_XFI             = BIT(I40E_PHY_TYPE_XFI),
+       I40E_CAP_PHY_TYPE_SFI             = BIT(I40E_PHY_TYPE_SFI),
+       I40E_CAP_PHY_TYPE_XLAUI           = BIT(I40E_PHY_TYPE_XLAUI),
+       I40E_CAP_PHY_TYPE_XLPPI           = BIT(I40E_PHY_TYPE_XLPPI),
+       I40E_CAP_PHY_TYPE_40GBASE_CR4_CU  = BIT(I40E_PHY_TYPE_40GBASE_CR4_CU),
+       I40E_CAP_PHY_TYPE_10GBASE_CR1_CU  = BIT(I40E_PHY_TYPE_10GBASE_CR1_CU),
+       I40E_CAP_PHY_TYPE_10GBASE_AOC     = BIT(I40E_PHY_TYPE_10GBASE_AOC),
+       I40E_CAP_PHY_TYPE_40GBASE_AOC     = BIT(I40E_PHY_TYPE_40GBASE_AOC),
+       I40E_CAP_PHY_TYPE_100BASE_TX      = BIT(I40E_PHY_TYPE_100BASE_TX),
+       I40E_CAP_PHY_TYPE_1000BASE_T      = BIT(I40E_PHY_TYPE_1000BASE_T),
+       I40E_CAP_PHY_TYPE_10GBASE_T       = BIT(I40E_PHY_TYPE_10GBASE_T),
+       I40E_CAP_PHY_TYPE_10GBASE_SR      = BIT(I40E_PHY_TYPE_10GBASE_SR),
+       I40E_CAP_PHY_TYPE_10GBASE_LR      = BIT(I40E_PHY_TYPE_10GBASE_LR),
+       I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU = BIT(I40E_PHY_TYPE_10GBASE_SFPP_CU),
+       I40E_CAP_PHY_TYPE_10GBASE_CR1     = BIT(I40E_PHY_TYPE_10GBASE_CR1),
+       I40E_CAP_PHY_TYPE_40GBASE_CR4     = BIT(I40E_PHY_TYPE_40GBASE_CR4),
+       I40E_CAP_PHY_TYPE_40GBASE_SR4     = BIT(I40E_PHY_TYPE_40GBASE_SR4),
+       I40E_CAP_PHY_TYPE_40GBASE_LR4     = BIT(I40E_PHY_TYPE_40GBASE_LR4),
+       I40E_CAP_PHY_TYPE_1000BASE_SX     = BIT(I40E_PHY_TYPE_1000BASE_SX),
+       I40E_CAP_PHY_TYPE_1000BASE_LX     = BIT(I40E_PHY_TYPE_1000BASE_LX),
+       I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL =
+                                        BIT(I40E_PHY_TYPE_1000BASE_T_OPTICAL),
+       I40E_CAP_PHY_TYPE_20GBASE_KR2     = BIT(I40E_PHY_TYPE_20GBASE_KR2)
 };
 
 struct i40e_phy_info {
        struct i40e_link_status link_info;
        struct i40e_link_status link_info_old;
-       u32 autoneg_advertised;
-       u32 phy_id;
-       u32 module_type;
        bool get_link_info;
        enum i40e_media_type media_type;
+       /* all the phy types the NVM is capable of */
+       enum i40e_aq_capabilities_phy_type phy_types;
 };
 
 #define I40E_HW_CAP_MAX_GPIO                   30
@@ -287,6 +314,7 @@ struct i40e_nvm_info {
        bool blank_nvm_mode;      /* is NVM empty (no FW present)*/
        u16 version;              /* NVM package version */
        u32 eetrack;              /* NVM data version */
+       u32 oem_ver;              /* OEM version info */
 };
 
 /* definitions used in NVM update support */
@@ -305,12 +333,17 @@ enum i40e_nvmupd_cmd {
        I40E_NVMUPD_CSUM_CON,
        I40E_NVMUPD_CSUM_SA,
        I40E_NVMUPD_CSUM_LCB,
+       I40E_NVMUPD_STATUS,
+       I40E_NVMUPD_EXEC_AQ,
+       I40E_NVMUPD_GET_AQ_RESULT,
 };
 
 enum i40e_nvmupd_state {
        I40E_NVMUPD_STATE_INIT,
        I40E_NVMUPD_STATE_READING,
-       I40E_NVMUPD_STATE_WRITING
+       I40E_NVMUPD_STATE_WRITING,
+       I40E_NVMUPD_STATE_INIT_WAIT,
+       I40E_NVMUPD_STATE_WRITE_WAIT,
 };
 
 /* nvm_access definition and its masks/shifts need to be accessible to
@@ -329,6 +362,7 @@ enum i40e_nvmupd_state {
 #define I40E_NVM_SA            (I40E_NVM_SNT | I40E_NVM_LCB)
 #define I40E_NVM_ERA           0x4
 #define I40E_NVM_CSUM          0x8
+#define I40E_NVM_EXEC          0xf
 
 #define I40E_NVM_ADAPT_SHIFT   16
 #define I40E_NVM_ADAPT_MASK    (0xffff << I40E_NVM_ADAPT_SHIFT)
@@ -409,6 +443,8 @@ struct i40e_fc_info {
 #define I40E_APP_PROTOID_FIP           0x8914
 #define I40E_APP_SEL_ETHTYPE           0x1
 #define I40E_APP_SEL_TCPIP             0x2
+#define I40E_CEE_APP_SEL_ETHTYPE       0x0
+#define I40E_CEE_APP_SEL_TCPIP         0x1
 
 /* CEE or IEEE 802.1Qaz ETS Configuration data */
 struct i40e_dcb_ets_config {
@@ -439,6 +475,8 @@ struct i40e_dcbx_config {
        u8  dcbx_mode;
 #define I40E_DCBX_MODE_CEE     0x1
 #define I40E_DCBX_MODE_IEEE    0x2
+       u8  app_mode;
+#define I40E_DCBX_APPS_NON_WILLING     0x1
        u32 numapps;
        u32 tlv_status; /* CEE mode TLV status */
        struct i40e_dcb_ets_config etscfg;
@@ -492,6 +530,8 @@ struct i40e_hw {
 
        /* state of nvm update process */
        enum i40e_nvmupd_state nvmupd_state;
+       struct i40e_aq_desc nvm_wb_desc;
+       struct i40e_virt_mem nvm_buff;
 
        /* HMC info */
        struct i40e_hmc_info hmc; /* HMC info struct */
@@ -500,8 +540,12 @@ struct i40e_hw {
        u16 dcbx_status;
 
        /* DCBX info */
-       struct i40e_dcbx_config local_dcbx_config;
-       struct i40e_dcbx_config remote_dcbx_config;
+       struct i40e_dcbx_config local_dcbx_config; /* Oper/Local Cfg */
+       struct i40e_dcbx_config remote_dcbx_config; /* Peer Cfg */
+       struct i40e_dcbx_config desired_dcbx_config; /* CEE Desired Cfg */
+
+#define I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE BIT_ULL(0)
+       u64 flags;
 
        /* debug mask */
        u32 debug_mask;
@@ -1024,8 +1068,8 @@ enum i40e_filter_program_desc_fd_status {
 };
 
 #define I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT       23
-#define I40E_TXD_FLTR_QW0_DEST_VSI_MASK \
-                                      BIT_ULL(I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT)
+#define I40E_TXD_FLTR_QW0_DEST_VSI_MASK        (0x1FFUL << \
+                                        I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT)
 
 #define I40E_TXD_FLTR_QW1_CMD_SHIFT    4
 #define I40E_TXD_FLTR_QW1_CMD_MASK     (0xFFFFULL << \
@@ -1193,6 +1237,8 @@ struct i40e_hw_port_stats {
 #define I40E_SR_EMP_MODULE_PTR                 0x0F
 #define I40E_SR_PBA_FLAGS                      0x15
 #define I40E_SR_PBA_BLOCK_PTR                  0x16
+#define I40E_SR_BOOT_CONFIG_PTR                        0x17
+#define I40E_NVM_OEM_VER_OFF                   0x83
 #define I40E_SR_NVM_DEV_STARTER_VERSION                0x18
 #define I40E_SR_NVM_WAKE_ON_LAN                        0x19
 #define I40E_SR_ALTERNATE_SAN_MAC_ADDRESS_PTR  0x27
index 0f8d4156f8b10796e962bbd85aae54de75b08b69..ae879826084b790a467f9eed01196f430daf47ae 100644 (file)
@@ -81,7 +81,6 @@ enum i40e_virtchnl_ops {
        I40E_VIRTCHNL_OP_GET_STATS = 15,
        I40E_VIRTCHNL_OP_FCOE = 16,
        I40E_VIRTCHNL_OP_EVENT = 17,
-       I40E_VIRTCHNL_OP_CONFIG_RSS = 18,
 };
 
 /* Virtual channel message descriptor. This overlays the admin queue
@@ -151,6 +150,7 @@ struct i40e_virtchnl_vsi_resource {
 #define I40E_VIRTCHNL_VF_OFFLOAD_FCOE          0x00000004
 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ                0x00000008
 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG       0x00000010
+#define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR     0x00000020
 #define I40E_VIRTCHNL_VF_OFFLOAD_VLAN          0x00010000
 #define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING    0x00020000
 
index d99c116032f368cb25cd40e1e63bf2ab240780c0..44462b40f2d7666f059b6741123107755e18e415 100644 (file)
@@ -536,6 +536,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
        }
        if (type == I40E_VSI_SRIOV) {
                u8 brdcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
                vf->lan_vsi_idx = vsi->idx;
                vf->lan_vsi_id = vsi->id;
                /* If the port VLAN has been configured and then the
@@ -546,6 +547,8 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
                 */
                if (vf->port_vlan_id)
                        i40e_vsi_add_pvid(vsi, vf->port_vlan_id);
+
+               spin_lock_bh(&vsi->mac_filter_list_lock);
                f = i40e_add_filter(vsi, vf->default_lan_addr.addr,
                                    vf->port_vlan_id ? vf->port_vlan_id : -1,
                                    true, false);
@@ -558,10 +561,11 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
                if (!f)
                        dev_info(&pf->pdev->dev,
                                 "Could not allocate VF broadcast filter\n");
+               spin_unlock_bh(&vsi->mac_filter_list_lock);
        }
 
        /* program mac filter */
-       ret = i40e_sync_vsi_filters(vsi);
+       ret = i40e_sync_vsi_filters(vsi, false);
        if (ret)
                dev_err(&pf->pdev->dev, "Unable to program ucast filters\n");
 
@@ -605,6 +609,7 @@ static void i40e_enable_vf_mappings(struct i40e_vf *vf)
        /* map PF queues to VF queues */
        for (j = 0; j < pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; j++) {
                u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id, j);
+
                reg = (qid & I40E_VPLAN_QTABLE_QINDEX_MASK);
                wr32(hw, I40E_VPLAN_QTABLE(total_queue_pairs, vf->vf_id), reg);
                total_queue_pairs++;
@@ -701,6 +706,7 @@ static void i40e_free_vf_res(struct i40e_vf *vf)
         */
        vf->num_queue_pairs = 0;
        vf->vf_states = 0;
+       clear_bit(I40E_VF_STAT_INIT, &vf->vf_states);
 }
 
 /**
@@ -839,11 +845,11 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr)
 complete_reset:
        /* reallocate VF resources to reset the VSI state */
        i40e_free_vf_res(vf);
-       i40e_alloc_vf_res(vf);
-       i40e_enable_vf_mappings(vf);
-       set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states);
-       clear_bit(I40E_VF_STAT_DISABLED, &vf->vf_states);
-
+       if (!i40e_alloc_vf_res(vf)) {
+               i40e_enable_vf_mappings(vf);
+               set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states);
+               clear_bit(I40E_VF_STAT_DISABLED, &vf->vf_states);
+       }
        /* tell the VF the reset is done */
        wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), I40E_VFR_VFACTIVE);
        i40e_flush(hw);
@@ -872,6 +878,11 @@ void i40e_free_vfs(struct i40e_pf *pf)
                        i40e_vsi_control_rings(pf->vsi[pf->vf[i].lan_vsi_idx],
                                               false);
 
+       for (i = 0; i < pf->num_alloc_vfs; i++)
+               if (test_bit(I40E_VF_STAT_INIT, &pf->vf[i].vf_states))
+                       i40e_vsi_control_rings(pf->vsi[pf->vf[i].lan_vsi_idx],
+                                              false);
+
        /* Disable IOV before freeing resources. This lets any VF drivers
         * running in the host get themselves cleaned up before we yank
         * the carpet out from underneath their feet.
@@ -933,6 +944,7 @@ int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs)
        if (pci_num_vf(pf->pdev) != num_alloc_vfs) {
                ret = pci_enable_sriov(pf->pdev, num_alloc_vfs);
                if (ret) {
+                       pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED;
                        pf->num_alloc_vfs = 0;
                        goto err_iov;
                }
@@ -957,8 +969,6 @@ int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs)
                /* VF resources get allocated during reset */
                i40e_reset_vf(&vfs[i], false);
 
-               /* enable VF vplan_qtable mappings */
-               i40e_enable_vf_mappings(&vfs[i]);
        }
        pf->num_alloc_vfs = num_alloc_vfs;
 
@@ -986,24 +996,26 @@ static int i40e_pci_sriov_enable(struct pci_dev *pdev, int num_vfs)
        int pre_existing_vfs = pci_num_vf(pdev);
        int err = 0;
 
-       if (pf->state & __I40E_TESTING) {
+       if (test_bit(__I40E_TESTING, &pf->state)) {
                dev_warn(&pdev->dev,
                         "Cannot enable SR-IOV virtual functions while the device is undergoing diagnostic testing\n");
                err = -EPERM;
                goto err_out;
        }
 
-       dev_info(&pdev->dev, "Allocating %d VFs.\n", num_vfs);
        if (pre_existing_vfs && pre_existing_vfs != num_vfs)
                i40e_free_vfs(pf);
        else if (pre_existing_vfs && pre_existing_vfs == num_vfs)
                goto out;
 
        if (num_vfs > pf->num_req_vfs) {
+               dev_warn(&pdev->dev, "Unable to enable %d VFs. Limited to %d VFs due to device resource constraints.\n",
+                        num_vfs, pf->num_req_vfs);
                err = -EPERM;
                goto err_out;
        }
 
+       dev_info(&pdev->dev, "Allocating %d VFs.\n", num_vfs);
        err = i40e_alloc_vfs(pf, num_vfs);
        if (err) {
                dev_warn(&pdev->dev, "Failed to enable SR-IOV: %d\n", err);
@@ -1094,6 +1106,8 @@ static int i40e_vc_send_msg_to_vf(struct i40e_vf *vf, u32 v_opcode,
                }
        } else {
                vf->num_valid_msgs++;
+               /* reset the invalid counter, if a valid message is received. */
+               vf->num_invalid_msgs = 0;
        }
 
        aq_ret = i40e_aq_send_msg_to_vf(hw, abs_vf_id,  v_opcode, v_retval,
@@ -1195,16 +1209,22 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg)
        } else {
                vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG;
        }
+
+       if (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING)
+               vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING;
+
        vfres->num_vsis = num_vsis;
        vfres->num_queue_pairs = vf->num_queue_pairs;
        vfres->max_vectors = pf->hw.func_caps.num_msix_vectors_vf;
        if (vf->lan_vsi_idx) {
                vfres->vsi_res[i].vsi_id = vf->lan_vsi_id;
                vfres->vsi_res[i].vsi_type = I40E_VSI_SRIOV;
-               vfres->vsi_res[i].num_queue_pairs =
-                   pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs;
-               memcpy(vfres->vsi_res[i].default_mac_addr,
-                      vf->default_lan_addr.addr, ETH_ALEN);
+               vfres->vsi_res[i].num_queue_pairs = vsi->alloc_queue_pairs;
+               /* VFs only use TC 0 */
+               vfres->vsi_res[i].qset_handle
+                                         = le16_to_cpu(vsi->info.qs_handle[0]);
+               ether_addr_copy(vfres->vsi_res[i].default_mac_addr,
+                               vf->default_lan_addr.addr);
                i++;
        }
        set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states);
@@ -1582,6 +1602,11 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
        }
        vsi = pf->vsi[vf->lan_vsi_idx];
 
+       /* Lock once, because all function inside for loop accesses VSI's
+        * MAC filter list which needs to be protected using same lock.
+        */
+       spin_lock_bh(&vsi->mac_filter_list_lock);
+
        /* add new addresses to the list */
        for (i = 0; i < al->num_elements; i++) {
                struct i40e_mac_filter *f;
@@ -1600,12 +1625,14 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
                        dev_err(&pf->pdev->dev,
                                "Unable to add VF MAC filter\n");
                        ret = I40E_ERR_PARAM;
+                       spin_unlock_bh(&vsi->mac_filter_list_lock);
                        goto error_param;
                }
        }
+       spin_unlock_bh(&vsi->mac_filter_list_lock);
 
        /* program the updated filter list */
-       if (i40e_sync_vsi_filters(vsi))
+       if (i40e_sync_vsi_filters(vsi, false))
                dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n");
 
 error_param:
@@ -1650,13 +1677,15 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
        }
        vsi = pf->vsi[vf->lan_vsi_idx];
 
+       spin_lock_bh(&vsi->mac_filter_list_lock);
        /* delete addresses from the list */
        for (i = 0; i < al->num_elements; i++)
                i40e_del_filter(vsi, al->list[i].addr,
                                I40E_VLAN_ANY, true, false);
+       spin_unlock_bh(&vsi->mac_filter_list_lock);
 
        /* program the updated filter list */
-       if (i40e_sync_vsi_filters(vsi))
+       if (i40e_sync_vsi_filters(vsi, false))
                dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n");
 
 error_param:
@@ -1708,6 +1737,7 @@ static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
        for (i = 0; i < vfl->num_elements; i++) {
                /* add new VLAN filter */
                int ret = i40e_vsi_add_vlan(vsi, vfl->vlan_id[i]);
+
                if (ret)
                        dev_err(&pf->pdev->dev,
                                "Unable to add VF vlan filter %d, error %d\n",
@@ -1759,6 +1789,7 @@ static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
 
        for (i = 0; i < vfl->num_elements; i++) {
                int ret = i40e_vsi_kill_vlan(vsi, vfl->vlan_id[i]);
+
                if (ret)
                        dev_err(&pf->pdev->dev,
                                "Unable to delete VF vlan filter %d, error %d\n",
@@ -1870,7 +1901,6 @@ static int i40e_vc_validate_vf_msg(struct i40e_vf *vf, u32 v_opcode,
        case I40E_VIRTCHNL_OP_UNKNOWN:
        default:
                return -EPERM;
-               break;
        }
        /* few more checks */
        if ((valid_len != msglen) || (err_msg_format)) {
@@ -2049,6 +2079,11 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
                goto error_param;
        }
 
+       /* Lock once because below invoked function add/del_filter requires
+        * mac_filter_list_lock to be held
+        */
+       spin_lock_bh(&vsi->mac_filter_list_lock);
+
        /* delete the temporary mac address */
        i40e_del_filter(vsi, vf->default_lan_addr.addr,
                        vf->port_vlan_id ? vf->port_vlan_id : -1,
@@ -2060,9 +2095,11 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
        list_for_each_entry(f, &vsi->mac_filter_list, list)
                i40e_del_filter(vsi, f->macaddr, f->vlan, true, false);
 
+       spin_unlock_bh(&vsi->mac_filter_list_lock);
+
        dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", mac, vf_id);
        /* program mac filter */
-       if (i40e_sync_vsi_filters(vsi)) {
+       if (i40e_sync_vsi_filters(vsi, false)) {
                dev_err(&pf->pdev->dev, "Unable to program ucast filters\n");
                ret = -EIO;
                goto error_param;
@@ -2089,8 +2126,10 @@ error_param:
 int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
                              int vf_id, u16 vlan_id, u8 qos)
 {
+       u16 vlanprio = vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT);
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_pf *pf = np->vsi->back;
+       bool is_vsi_in_vlan = false;
        struct i40e_vsi *vsi;
        struct i40e_vf *vf;
        int ret = 0;
@@ -2116,12 +2155,15 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
                goto error_pvid;
        }
 
-       if (le16_to_cpu(vsi->info.pvid) ==
-           (vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT)))
+       if (le16_to_cpu(vsi->info.pvid) == vlanprio)
                /* duplicate request, so just return success */
                goto error_pvid;
 
-       if (le16_to_cpu(vsi->info.pvid) == 0 && i40e_is_vsi_in_vlan(vsi)) {
+       spin_lock_bh(&vsi->mac_filter_list_lock);
+       is_vsi_in_vlan = i40e_is_vsi_in_vlan(vsi);
+       spin_unlock_bh(&vsi->mac_filter_list_lock);
+
+       if (le16_to_cpu(vsi->info.pvid) == 0 && is_vsi_in_vlan) {
                dev_err(&pf->pdev->dev,
                        "VF %d has already configured VLAN filters and the administrator is requesting a port VLAN override.\nPlease unload and reload the VF driver for this change to take effect.\n",
                        vf_id);
@@ -2141,7 +2183,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
         * MAC addresses deleted.
         */
        if ((!(vlan_id || qos) ||
-           (vlan_id | qos) != le16_to_cpu(vsi->info.pvid)) &&
+           vlanprio != le16_to_cpu(vsi->info.pvid)) &&
            vsi->info.pvid)
                ret = i40e_vsi_add_vlan(vsi, I40E_VLAN_ANY);
 
@@ -2156,8 +2198,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
                }
        }
        if (vlan_id || qos)
-               ret = i40e_vsi_add_pvid(vsi,
-                               vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT));
+               ret = i40e_vsi_add_pvid(vsi, vlanprio);
        else
                i40e_vsi_remove_pvid(vsi);
 
@@ -2310,7 +2351,7 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
 
        ivi->vf = vf_id;
 
-       memcpy(&ivi->mac, vf->default_lan_addr.addr, ETH_ALEN);
+       ether_addr_copy(ivi->mac, vf->default_lan_addr.addr);
 
        ivi->max_tx_rate = vf->tx_rate;
        ivi->min_tx_rate = 0;
index 736f6f08b4f26c98042375db16767eefca0fbe66..da44995def42f3b034a1b08780a0c901335e02c7 100644 (file)
@@ -29,8 +29,6 @@
 
 #include "i40e.h"
 
-#define I40E_MAX_MACVLAN_FILTERS 256
-#define I40E_MAX_VLAN_FILTERS 256
 #define I40E_MAX_VLANID 4095
 
 #define I40E_VIRTCHNL_SUPPORTED_QTYPES 2
@@ -98,7 +96,8 @@ struct i40e_vf {
 
        u8 num_queue_pairs;     /* num of qps assigned to VF vsis */
        u64 num_mdd_events;     /* num of mdd events detected */
-       u64 num_invalid_msgs;   /* num of malformed or invalid msgs detected */
+       /* num of continuous malformed or invalid msgs detected */
+       u64 num_invalid_msgs;
        u64 num_valid_msgs;     /* num of valid msgs detected */
 
        unsigned long vf_caps;  /* vf's adv. capabilities */
index a23ebfd5cd254014cd0632979aa441a2a6f6eadf..fd123ca60761e84721a02ab991ed677925338126 100644 (file)
@@ -469,8 +469,12 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw)
 {
        i40e_status ret_code = 0;
 
-       if (hw->aq.asq.count == 0)
-               return I40E_ERR_NOT_READY;
+       mutex_lock(&hw->aq.asq_mutex);
+
+       if (hw->aq.asq.count == 0) {
+               ret_code = I40E_ERR_NOT_READY;
+               goto shutdown_asq_out;
+       }
 
        /* Stop firmware AdminQ processing */
        wr32(hw, hw->aq.asq.head, 0);
@@ -479,16 +483,13 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw)
        wr32(hw, hw->aq.asq.bal, 0);
        wr32(hw, hw->aq.asq.bah, 0);
 
-       /* make sure lock is available */
-       mutex_lock(&hw->aq.asq_mutex);
-
        hw->aq.asq.count = 0; /* to indicate uninitialized queue */
 
        /* free ring buffers */
        i40e_free_asq_bufs(hw);
 
+shutdown_asq_out:
        mutex_unlock(&hw->aq.asq_mutex);
-
        return ret_code;
 }
 
@@ -502,8 +503,12 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw)
 {
        i40e_status ret_code = 0;
 
-       if (hw->aq.arq.count == 0)
-               return I40E_ERR_NOT_READY;
+       mutex_lock(&hw->aq.arq_mutex);
+
+       if (hw->aq.arq.count == 0) {
+               ret_code = I40E_ERR_NOT_READY;
+               goto shutdown_arq_out;
+       }
 
        /* Stop firmware AdminQ processing */
        wr32(hw, hw->aq.arq.head, 0);
@@ -512,16 +517,13 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw)
        wr32(hw, hw->aq.arq.bal, 0);
        wr32(hw, hw->aq.arq.bah, 0);
 
-       /* make sure lock is available */
-       mutex_lock(&hw->aq.arq_mutex);
-
        hw->aq.arq.count = 0; /* to indicate uninitialized queue */
 
        /* free ring buffers */
        i40e_free_arq_bufs(hw);
 
+shutdown_arq_out:
        mutex_unlock(&hw->aq.arq_mutex);
-
        return ret_code;
 }
 
@@ -596,6 +598,9 @@ i40e_status i40evf_shutdown_adminq(struct i40e_hw *hw)
 
        /* destroy the locks */
 
+       if (hw->nvm_buff.va)
+               i40e_free_virt_mem(hw, &hw->nvm_buff);
+
        return ret_code;
 }
 
@@ -617,8 +622,7 @@ static u16 i40e_clean_asq(struct i40e_hw *hw)
        details = I40E_ADMINQ_DETAILS(*asq, ntc);
        while (rd32(hw, hw->aq.asq.head) != ntc) {
                i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,
-                          "%s: ntc %d head %d.\n", __func__, ntc,
-                          rd32(hw, hw->aq.asq.head));
+                          "ntc %d head %d.\n", ntc, rd32(hw, hw->aq.asq.head));
 
                if (details->callback) {
                        I40E_ADMINQ_CALLBACK cb_func =
@@ -682,19 +686,23 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw,
        u16  retval = 0;
        u32  val = 0;
 
-       val = rd32(hw, hw->aq.asq.head);
-       if (val >= hw->aq.num_asq_entries) {
+       mutex_lock(&hw->aq.asq_mutex);
+
+       if (hw->aq.asq.count == 0) {
                i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,
-                          "AQTX: head overrun at %d\n", val);
+                          "AQTX: Admin queue not initialized.\n");
                status = I40E_ERR_QUEUE_EMPTY;
-               goto asq_send_command_exit;
+               goto asq_send_command_error;
        }
 
-       if (hw->aq.asq.count == 0) {
+       hw->aq.asq_last_status = I40E_AQ_RC_OK;
+
+       val = rd32(hw, hw->aq.asq.head);
+       if (val >= hw->aq.num_asq_entries) {
                i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,
-                          "AQTX: Admin queue not initialized.\n");
+                          "AQTX: head overrun at %d\n", val);
                status = I40E_ERR_QUEUE_EMPTY;
-               goto asq_send_command_exit;
+               goto asq_send_command_error;
        }
 
        details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use);
@@ -719,8 +727,6 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw,
        desc->flags &= ~cpu_to_le16(details->flags_dis);
        desc->flags |= cpu_to_le16(details->flags_ena);
 
-       mutex_lock(&hw->aq.asq_mutex);
-
        if (buff_size > hw->aq.asq_buf_size) {
                i40e_debug(hw,
                           I40E_DEBUG_AQ_MESSAGE,
@@ -830,6 +836,10 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw,
        i40evf_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, buff,
                        buff_size);
 
+       /* save writeback aq if requested */
+       if (details->wb_desc)
+               *details->wb_desc = *desc_on_ring;
+
        /* update the error if time out occurred */
        if ((!cmd_completed) &&
            (!details->async && !details->postpone)) {
@@ -841,7 +851,6 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw,
 
 asq_send_command_error:
        mutex_unlock(&hw->aq.asq_mutex);
-asq_send_command_exit:
        return status;
 }
 
index ef43d68f67b30632b504962f1fd627799f16dd7f..a3eae5d9a2bd4416c2f53336c0ba10e435182025 100644 (file)
@@ -69,6 +69,7 @@ struct i40e_asq_cmd_details {
        u16 flags_dis;
        bool async;
        bool postpone;
+       struct i40e_aq_desc *wb_desc;
 };
 
 #define I40E_ADMINQ_DETAILS(R, i)   \
@@ -108,9 +109,10 @@ struct i40e_adminq_info {
 
 /**
  * i40e_aq_rc_to_posix - convert errors to user-land codes
- * aq_rc: AdminQ error code to convert
+ * aq_ret: AdminQ handler error code can override aq_rc
+ * aq_rc: AdminQ firmware error code to convert
  **/
-static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc)
+static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc)
 {
        int aq_to_posix[] = {
                0,           /* I40E_AQ_RC_OK */
@@ -142,8 +144,9 @@ static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc)
        if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT)
                return -EAGAIN;
 
-       if (aq_rc >= ARRAY_SIZE(aq_to_posix))
+       if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0]))))
                return -ERANGE;
+
        return aq_to_posix[aq_rc];
 }
 
index c8022092d36986acce3062af4ec8b2d643314495..fcb9ef34cc7a28566003b0c53824a04d736d1b95 100644 (file)
@@ -1719,11 +1719,13 @@ struct i40e_aqc_get_link_status {
        u8      phy_type;    /* i40e_aq_phy_type   */
        u8      link_speed;  /* i40e_aq_link_speed */
        u8      link_info;
-#define I40E_AQ_LINK_UP                        0x01
+#define I40E_AQ_LINK_UP                        0x01    /* obsolete */
+#define I40E_AQ_LINK_UP_FUNCTION       0x01
 #define I40E_AQ_LINK_FAULT             0x02
 #define I40E_AQ_LINK_FAULT_TX          0x04
 #define I40E_AQ_LINK_FAULT_RX          0x08
 #define I40E_AQ_LINK_FAULT_REMOTE      0x10
+#define I40E_AQ_LINK_UP_PORT           0x20
 #define I40E_AQ_MEDIA_AVAILABLE                0x40
 #define I40E_AQ_SIGNAL_DETECT          0x80
        u8      an_info;
index d45d0ae6bd3b6d3788eb6fdedb71f198d194ede0..72b1942a94aab4748cbbd242e94677065dbcef4d 100644 (file)
@@ -51,7 +51,9 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw)
                case I40E_DEV_ID_QSFP_B:
                case I40E_DEV_ID_QSFP_C:
                case I40E_DEV_ID_10G_BASE_T:
+               case I40E_DEV_ID_10G_BASE_T4:
                case I40E_DEV_ID_20G_KR2:
+               case I40E_DEV_ID_20G_KR2_A:
                        hw->mac.type = I40E_MAC_XL710;
                        break;
                case I40E_DEV_ID_SFP_X722:
@@ -85,7 +87,7 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw)
  * @hw: pointer to the HW structure
  * @aq_err: the AQ error code to convert
  **/
-char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err)
+const char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err)
 {
        switch (aq_err) {
        case I40E_AQ_RC_OK:
@@ -145,7 +147,7 @@ char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err)
  * @hw: pointer to the HW structure
  * @stat_err: the status error code to convert
  **/
-char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err)
+const char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err)
 {
        switch (stat_err) {
        case 0:
@@ -329,25 +331,11 @@ void i40evf_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc,
                        len = buf_len;
                /* write the full 16-byte chunks */
                for (i = 0; i < (len - 16); i += 16)
-                       i40e_debug(hw, mask,
-                                  "\t0x%04X  %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
-                                  i, buf[i], buf[i + 1], buf[i + 2],
-                                  buf[i + 3], buf[i + 4], buf[i + 5],
-                                  buf[i + 6], buf[i + 7], buf[i + 8],
-                                  buf[i + 9], buf[i + 10], buf[i + 11],
-                                  buf[i + 12], buf[i + 13], buf[i + 14],
-                                  buf[i + 15]);
+                       i40e_debug(hw, mask, "\t0x%04X  %16ph\n", i, buf + i);
                /* write whatever's left over without overrunning the buffer */
-               if (i < len) {
-                       char d_buf[80];
-                       int j = 0;
-
-                       memset(d_buf, 0, sizeof(d_buf));
-                       j += sprintf(d_buf, "\t0x%04X ", i);
-                       while (i < len)
-                               j += sprintf(&d_buf[j], " %02X", buf[i++]);
-                       i40e_debug(hw, mask, "%s\n", d_buf);
-               }
+               if (i < len)
+                       i40e_debug(hw, mask, "\t0x%04X  %*ph\n",
+                                            i, len - i, buf + i);
        }
 }
 
@@ -441,9 +429,6 @@ static i40e_status i40e_aq_get_set_rss_lut(struct i40e_hw *hw,
                                        I40E_AQC_SET_RSS_LUT_TABLE_TYPE_SHIFT) &
                                        I40E_AQC_SET_RSS_LUT_TABLE_TYPE_MASK));
 
-       cmd_resp->addr_high = cpu_to_le32(high_16_bits((u64)lut));
-       cmd_resp->addr_low = cpu_to_le32(lower_32_bits((u64)lut));
-
        status = i40evf_asq_send_command(hw, &desc, lut, lut_size, NULL);
 
        return status;
@@ -518,8 +503,6 @@ static i40e_status i40e_aq_get_set_rss_key(struct i40e_hw *hw,
                                          I40E_AQC_SET_RSS_KEY_VSI_ID_SHIFT) &
                                          I40E_AQC_SET_RSS_KEY_VSI_ID_MASK));
        cmd_resp->vsi_id |= cpu_to_le16((u16)I40E_AQC_SET_RSS_KEY_VSI_VALID);
-       cmd_resp->addr_high = cpu_to_le32(high_16_bits((u64)key));
-       cmd_resp->addr_low = cpu_to_le32(lower_32_bits((u64)key));
 
        status = i40evf_asq_send_command(hw, &desc, key, key_size, NULL);
 
@@ -990,10 +973,10 @@ void i40e_vf_parse_hw_config(struct i40e_hw *hw,
                             I40E_VIRTCHNL_VF_OFFLOAD_FCOE) ? 1 : 0;
        for (i = 0; i < msg->num_vsis; i++) {
                if (vsi_res->vsi_type == I40E_VSI_SRIOV) {
-                       memcpy(hw->mac.perm_addr, vsi_res->default_mac_addr,
-                              ETH_ALEN);
-                       memcpy(hw->mac.addr, vsi_res->default_mac_addr,
-                              ETH_ALEN);
+                       ether_addr_copy(hw->mac.perm_addr,
+                                       vsi_res->default_mac_addr);
+                       ether_addr_copy(hw->mac.addr,
+                                       vsi_res->default_mac_addr);
                }
                vsi_res++;
        }
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_devids.h b/drivers/net/ethernet/intel/i40evf/i40e_devids.h
new file mode 100644 (file)
index 0000000..e6a39c9
--- /dev/null
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ *
+ * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
+ * Copyright(c) 2013 - 2015 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ ******************************************************************************/
+
+#ifndef _I40E_DEVIDS_H_
+#define _I40E_DEVIDS_H_
+
+/* Device IDs */
+#define I40E_DEV_ID_SFP_XL710          0x1572
+#define I40E_DEV_ID_QEMU               0x1574
+#define I40E_DEV_ID_KX_A               0x157F
+#define I40E_DEV_ID_KX_B               0x1580
+#define I40E_DEV_ID_KX_C               0x1581
+#define I40E_DEV_ID_QSFP_A             0x1583
+#define I40E_DEV_ID_QSFP_B             0x1584
+#define I40E_DEV_ID_QSFP_C             0x1585
+#define I40E_DEV_ID_10G_BASE_T         0x1586
+#define I40E_DEV_ID_20G_KR2            0x1587
+#define I40E_DEV_ID_20G_KR2_A          0x1588
+#define I40E_DEV_ID_10G_BASE_T4                0x1589
+#define I40E_DEV_ID_VF                 0x154C
+#define I40E_DEV_ID_VF_HV              0x1571
+#define I40E_DEV_ID_SFP_X722           0x37D0
+#define I40E_DEV_ID_1G_BASE_T_X722     0x37D1
+#define I40E_DEV_ID_10G_BASE_T_X722    0x37D2
+#define I40E_DEV_ID_X722_VF            0x37CD
+#define I40E_DEV_ID_X722_VF_HV         0x37D9
+
+#define i40e_is_40G_device(d)          ((d) == I40E_DEV_ID_QSFP_A  || \
+                                        (d) == I40E_DEV_ID_QSFP_B  || \
+                                        (d) == I40E_DEV_ID_QSFP_C)
+
+#endif /* _I40E_DEVIDS_H_ */
index 55ae4b0f8192fea531f0ea0e29013b111b540a4b..cbd9a1b078abf6caaa959218e0b4a4920c414d02 100644 (file)
@@ -60,8 +60,8 @@ void i40e_idle_aq(struct i40e_hw *hw);
 void i40evf_resume_aq(struct i40e_hw *hw);
 bool i40evf_check_asq_alive(struct i40e_hw *hw);
 i40e_status i40evf_aq_queue_shutdown(struct i40e_hw *hw, bool unloading);
-char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err);
-char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err);
+const char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err);
+const char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err);
 
 i40e_status i40evf_aq_get_rss_lut(struct i40e_hw *hw, u16 seid,
                                  bool pf_lut, u8 *lut, u16 lut_size);
@@ -101,4 +101,6 @@ i40e_status i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw,
                                u16 vsi_seid, u16 queue, bool is_add,
                                struct i40e_control_filter_stats *stats,
                                struct i40e_asq_cmd_details *cmd_details);
+void i40e_add_filter_to_drop_tx_flow_control_frames(struct i40e_hw *hw,
+                                                   u16 vsi_seid);
 #endif /* _I40E_PROTOTYPE_H_ */
index 7e91d825c760fbaea5a43d44a32feea7d950b868..47e9a90d6b100d9874c6a6e77e123c7e5ccc7ac2 100644 (file)
@@ -140,65 +140,6 @@ static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
        return le32_to_cpu(*(volatile __le32 *)head);
 }
 
-/**
- * i40e_get_tx_pending - how many tx descriptors not processed
- * @tx_ring: the ring of descriptors
- *
- * Since there is no access to the ring head register
- * in XL710, we need to use our local copies
- **/
-static u32 i40e_get_tx_pending(struct i40e_ring *ring)
-{
-       u32 head, tail;
-
-       head = i40e_get_head(ring);
-       tail = readl(ring->tail);
-
-       if (head != tail)
-               return (head < tail) ?
-                       tail - head : (tail + ring->count - head);
-
-       return 0;
-}
-
-/**
- * i40e_check_tx_hang - Is there a hang in the Tx queue
- * @tx_ring: the ring of descriptors
- **/
-static bool i40e_check_tx_hang(struct i40e_ring *tx_ring)
-{
-       u32 tx_done = tx_ring->stats.packets;
-       u32 tx_done_old = tx_ring->tx_stats.tx_done_old;
-       u32 tx_pending = i40e_get_tx_pending(tx_ring);
-       bool ret = false;
-
-       clear_check_for_tx_hang(tx_ring);
-
-       /* Check for a hung queue, but be thorough. This verifies
-        * that a transmit has been completed since the previous
-        * check AND there is at least one packet pending. The
-        * ARMED bit is set to indicate a potential hang. The
-        * bit is cleared if a pause frame is received to remove
-        * false hang detection due to PFC or 802.3x frames. By
-        * requiring this to fail twice we avoid races with
-        * PFC clearing the ARMED bit and conditions where we
-        * run the check_tx_hang logic with a transmit completion
-        * pending but without time to complete it yet.
-        */
-       if ((tx_done_old == tx_done) && tx_pending) {
-               /* make sure it is true for two checks in a row */
-               ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED,
-                                      &tx_ring->state);
-       } else if (tx_done_old == tx_done &&
-                  (tx_pending < I40E_MIN_DESC_PENDING) && (tx_pending > 0)) {
-               /* update completed stats and disarm the hang check */
-               tx_ring->tx_stats.tx_done_old = tx_done;
-               clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state);
-       }
-
-       return ret;
-}
-
 #define WB_STRIDE 0x3
 
 /**
@@ -304,36 +245,15 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
        tx_ring->q_vector->tx.total_bytes += total_bytes;
        tx_ring->q_vector->tx.total_packets += total_packets;
 
+       /* check to see if there are any non-cache aligned descriptors
+        * waiting to be written back, and kick the hardware to force
+        * them to be written back in case of napi polling
+        */
        if (budget &&
            !((i & WB_STRIDE) == WB_STRIDE) &&
            !test_bit(__I40E_DOWN, &tx_ring->vsi->state) &&
            (I40E_DESC_UNUSED(tx_ring) != tx_ring->count))
                tx_ring->arm_wb = true;
-       else
-               tx_ring->arm_wb = false;
-
-       if (check_for_tx_hang(tx_ring) && i40e_check_tx_hang(tx_ring)) {
-               /* schedule immediate reset if we believe we hung */
-               dev_info(tx_ring->dev, "Detected Tx Unit Hang\n"
-                        "  VSI                  <%d>\n"
-                        "  Tx Queue             <%d>\n"
-                        "  next_to_use          <%x>\n"
-                        "  next_to_clean        <%x>\n",
-                        tx_ring->vsi->seid,
-                        tx_ring->queue_index,
-                        tx_ring->next_to_use, i);
-
-               netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index);
-
-               dev_info(tx_ring->dev,
-                        "tx hang detected on queue %d, resetting adapter\n",
-                        tx_ring->queue_index);
-
-               tx_ring->netdev->netdev_ops->ndo_tx_timeout(tx_ring->netdev);
-
-               /* the adapter is about to reset, no point in enabling stuff */
-               return true;
-       }
 
        netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev,
                                                      tx_ring->queue_index),
@@ -355,16 +275,16 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
                }
        }
 
-       return budget > 0;
+       return !!budget;
 }
 
 /**
- * i40e_force_wb -Arm hardware to do a wb on noncache aligned descriptors
+ * i40evf_force_wb -Arm hardware to do a wb on noncache aligned descriptors
  * @vsi: the VSI we care about
  * @q_vector: the vector  on which to force writeback
  *
  **/
-static void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector)
+static void i40evf_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector)
 {
        u16 flags = q_vector->tx.ring[0].flags;
 
@@ -398,6 +318,8 @@ static void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector)
  * i40e_set_new_dynamic_itr - Find new ITR level
  * @rc: structure containing ring performance data
  *
+ * Returns true if ITR changed, false if not
+ *
  * Stores a new ITR value based on packets and byte counts during
  * the last interrupt.  The advantage of per interrupt computation
  * is faster updates and more accurate ITR for the current traffic
@@ -406,21 +328,32 @@ static void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector)
  * testing data as well as attempting to minimize response time
  * while increasing bulk throughput.
  **/
-static void i40e_set_new_dynamic_itr(struct i40e_ring_container *rc)
+static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc)
 {
        enum i40e_latency_range new_latency_range = rc->latency_range;
+       struct i40e_q_vector *qv = rc->ring->q_vector;
        u32 new_itr = rc->itr;
        int bytes_per_int;
+       int usecs;
 
        if (rc->total_packets == 0 || !rc->itr)
-               return;
+               return false;
 
        /* simple throttlerate management
-        *   0-10MB/s   lowest (100000 ints/s)
+        *   0-10MB/s   lowest (50000 ints/s)
         *  10-20MB/s   low    (20000 ints/s)
-        *  20-1249MB/s bulk   (8000 ints/s)
+        *  20-1249MB/s bulk   (18000 ints/s)
+        *  > 40000 Rx packets per second (8000 ints/s)
+        *
+        * The math works out because the divisor is in 10^(-6) which
+        * turns the bytes/us input value into MB/s values, but
+        * make sure to use usecs, as the register values written
+        * are in 2 usec increments in the ITR registers, and make sure
+        * to use the smoothed values that the countdown timer gives us.
         */
-       bytes_per_int = rc->total_bytes / rc->itr;
+       usecs = (rc->itr << 1) * ITR_COUNTDOWN_START;
+       bytes_per_int = rc->total_bytes / usecs;
+
        switch (new_latency_range) {
        case I40E_LOWEST_LATENCY:
                if (bytes_per_int > 10)
@@ -433,35 +366,52 @@ static void i40e_set_new_dynamic_itr(struct i40e_ring_container *rc)
                        new_latency_range = I40E_LOWEST_LATENCY;
                break;
        case I40E_BULK_LATENCY:
-               if (bytes_per_int <= 20)
-                       new_latency_range = I40E_LOW_LATENCY;
-               break;
+       case I40E_ULTRA_LATENCY:
        default:
                if (bytes_per_int <= 20)
                        new_latency_range = I40E_LOW_LATENCY;
                break;
        }
+
+       /* this is to adjust RX more aggressively when streaming small
+        * packets.  The value of 40000 was picked as it is just beyond
+        * what the hardware can receive per second if in low latency
+        * mode.
+        */
+#define RX_ULTRA_PACKET_RATE 40000
+
+       if ((((rc->total_packets * 1000000) / usecs) > RX_ULTRA_PACKET_RATE) &&
+           (&qv->rx == rc))
+               new_latency_range = I40E_ULTRA_LATENCY;
+
        rc->latency_range = new_latency_range;
 
        switch (new_latency_range) {
        case I40E_LOWEST_LATENCY:
-               new_itr = I40E_ITR_100K;
+               new_itr = I40E_ITR_50K;
                break;
        case I40E_LOW_LATENCY:
                new_itr = I40E_ITR_20K;
                break;
        case I40E_BULK_LATENCY:
+               new_itr = I40E_ITR_18K;
+               break;
+       case I40E_ULTRA_LATENCY:
                new_itr = I40E_ITR_8K;
                break;
        default:
                break;
        }
 
-       if (new_itr != rc->itr)
-               rc->itr = new_itr;
-
        rc->total_bytes = 0;
        rc->total_packets = 0;
+
+       if (new_itr != rc->itr) {
+               rc->itr = new_itr;
+               return true;
+       }
+
+       return false;
 }
 
 /*
@@ -822,16 +772,11 @@ static void i40e_receive_skb(struct i40e_ring *rx_ring,
                             struct sk_buff *skb, u16 vlan_tag)
 {
        struct i40e_q_vector *q_vector = rx_ring->q_vector;
-       struct i40e_vsi *vsi = rx_ring->vsi;
-       u64 flags = vsi->back->flags;
 
        if (vlan_tag & VLAN_VID_MASK)
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
 
-       if (flags & I40E_FLAG_IN_NETPOLL)
-               netif_rx(skb);
-       else
-               napi_gro_receive(&q_vector->napi, skb);
+       napi_gro_receive(&q_vector->napi, skb);
 }
 
 /**
@@ -997,7 +942,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
        unsigned int total_rx_bytes = 0, total_rx_packets = 0;
        u16 rx_packet_len, rx_header_len, rx_sph, rx_hbo;
        u16 cleaned_count = I40E_DESC_UNUSED(rx_ring);
-       const int current_node = numa_node_id();
+       const int current_node = numa_mem_id();
        struct i40e_vsi *vsi = rx_ring->vsi;
        u16 i = rx_ring->next_to_clean;
        union i40e_rx_desc *rx_desc;
@@ -1067,6 +1012,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
                cleaned_count++;
                if (rx_hbo || rx_sph) {
                        int len;
+
                        if (rx_hbo)
                                len = I40E_RX_HDR_SIZE;
                        else
@@ -1240,9 +1186,6 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget)
                /* ERR_MASK will only have valid bits if EOP set */
                if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) {
                        dev_kfree_skb_any(skb);
-                       /* TODO: shouldn't we increment a counter indicating the
-                        * drop?
-                        */
                        continue;
                }
 
@@ -1274,6 +1217,21 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget)
        return total_rx_packets;
 }
 
+static u32 i40e_buildreg_itr(const int type, const u16 itr)
+{
+       u32 val;
+
+       val = I40E_VFINT_DYN_CTLN1_INTENA_MASK |
+             I40E_VFINT_DYN_CTLN1_CLEARPBA_MASK |
+             (type << I40E_VFINT_DYN_CTLN1_ITR_INDX_SHIFT) |
+             (itr << I40E_VFINT_DYN_CTLN1_INTERVAL_SHIFT);
+
+       return val;
+}
+
+/* a small macro to shorten up some long lines */
+#define INTREG I40E_VFINT_DYN_CTLN1
+
 /**
  * i40e_update_enable_itr - Update itr and re-enable MSIX interrupt
  * @vsi: the VSI we care about
@@ -1284,55 +1242,67 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi,
                                          struct i40e_q_vector *q_vector)
 {
        struct i40e_hw *hw = &vsi->back->hw;
-       u16 old_itr;
+       bool rx = false, tx = false;
+       u32 rxval, txval;
        int vector;
-       u32 val;
 
        vector = (q_vector->v_idx + vsi->base_vector);
+
+       /* avoid dynamic calculation if in countdown mode OR if
+        * all dynamic is disabled
+        */
+       rxval = txval = i40e_buildreg_itr(I40E_ITR_NONE, 0);
+
+       if (q_vector->itr_countdown > 0 ||
+           (!ITR_IS_DYNAMIC(vsi->rx_itr_setting) &&
+            !ITR_IS_DYNAMIC(vsi->tx_itr_setting))) {
+               goto enable_int;
+       }
+
        if (ITR_IS_DYNAMIC(vsi->rx_itr_setting)) {
-               old_itr = q_vector->rx.itr;
-               i40e_set_new_dynamic_itr(&q_vector->rx);
-               if (old_itr != q_vector->rx.itr) {
-                       val = I40E_VFINT_DYN_CTLN1_INTENA_MASK |
-                       I40E_VFINT_DYN_CTLN1_CLEARPBA_MASK |
-                       (I40E_RX_ITR <<
-                               I40E_VFINT_DYN_CTLN1_ITR_INDX_SHIFT) |
-                       (q_vector->rx.itr <<
-                               I40E_VFINT_DYN_CTLN1_INTERVAL_SHIFT);
-               } else {
-                       val = I40E_VFINT_DYN_CTLN1_INTENA_MASK |
-                       I40E_VFINT_DYN_CTLN1_CLEARPBA_MASK |
-                       (I40E_ITR_NONE <<
-                               I40E_VFINT_DYN_CTLN1_ITR_INDX_SHIFT);
-               }
-               if (!test_bit(__I40E_DOWN, &vsi->state))
-                       wr32(hw, I40E_VFINT_DYN_CTLN1(vector - 1), val);
-       } else {
-               i40evf_irq_enable_queues(vsi->back, 1
-                       << q_vector->v_idx);
+               rx = i40e_set_new_dynamic_itr(&q_vector->rx);
+               rxval = i40e_buildreg_itr(I40E_RX_ITR, q_vector->rx.itr);
        }
        if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) {
-               old_itr = q_vector->tx.itr;
-               i40e_set_new_dynamic_itr(&q_vector->tx);
-               if (old_itr != q_vector->tx.itr) {
-                       val = I40E_VFINT_DYN_CTLN1_INTENA_MASK |
-                               I40E_VFINT_DYN_CTLN1_CLEARPBA_MASK |
-                               (I40E_TX_ITR <<
-                                  I40E_VFINT_DYN_CTLN1_ITR_INDX_SHIFT) |
-                               (q_vector->tx.itr <<
-                                  I40E_VFINT_DYN_CTLN1_INTERVAL_SHIFT);
+               tx = i40e_set_new_dynamic_itr(&q_vector->tx);
+               txval = i40e_buildreg_itr(I40E_TX_ITR, q_vector->tx.itr);
+       }
+       if (rx || tx) {
+               /* get the higher of the two ITR adjustments and
+                * use the same value for both ITR registers
+                * when in adaptive mode (Rx and/or Tx)
+                */
+               u16 itr = max(q_vector->tx.itr, q_vector->rx.itr);
 
-               } else {
-                       val = I40E_VFINT_DYN_CTLN1_INTENA_MASK |
-                               I40E_VFINT_DYN_CTLN1_CLEARPBA_MASK |
-                               (I40E_ITR_NONE <<
-                                  I40E_VFINT_DYN_CTLN1_ITR_INDX_SHIFT);
-               }
-               if (!test_bit(__I40E_DOWN, &vsi->state))
-                       wr32(hw, I40E_VFINT_DYN_CTLN1(vector - 1), val);
-       } else {
-               i40evf_irq_enable_queues(vsi->back, BIT(q_vector->v_idx));
+               q_vector->tx.itr = q_vector->rx.itr = itr;
+               txval = i40e_buildreg_itr(I40E_TX_ITR, itr);
+               tx = true;
+               rxval = i40e_buildreg_itr(I40E_RX_ITR, itr);
+               rx = true;
        }
+
+       /* only need to enable the interrupt once, but need
+        * to possibly update both ITR values
+        */
+       if (rx) {
+               /* set the INTENA_MSK_MASK so that this first write
+                * won't actually enable the interrupt, instead just
+                * updating the ITR (it's bit 31 PF and VF)
+                */
+               rxval |= BIT(31);
+               /* don't check _DOWN because interrupt isn't being enabled */
+               wr32(hw, INTREG(vector - 1), rxval);
+       }
+
+enable_int:
+       if (!test_bit(__I40E_DOWN, &vsi->state))
+               wr32(hw, INTREG(vector - 1), txval);
+
+       if (q_vector->itr_countdown)
+               q_vector->itr_countdown--;
+       else
+               q_vector->itr_countdown = ITR_COUNTDOWN_START;
+
 }
 
 /**
@@ -1353,7 +1323,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget)
        bool clean_complete = true;
        bool arm_wb = false;
        int budget_per_ring;
-       int cleaned;
+       int work_done = 0;
 
        if (test_bit(__I40E_DOWN, &vsi->state)) {
                napi_complete(napi);
@@ -1366,26 +1336,36 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget)
        i40e_for_each_ring(ring, q_vector->tx) {
                clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit);
                arm_wb |= ring->arm_wb;
+               ring->arm_wb = false;
        }
 
+       /* Handle case where we are called by netpoll with a budget of 0 */
+       if (budget <= 0)
+               goto tx_only;
+
        /* We attempt to distribute budget to each Rx queue fairly, but don't
         * allow the budget to go below 1 because that would exit polling early.
         */
        budget_per_ring = max(budget/q_vector->num_ringpairs, 1);
 
        i40e_for_each_ring(ring, q_vector->rx) {
+               int cleaned;
+
                if (ring_is_ps_enabled(ring))
                        cleaned = i40e_clean_rx_irq_ps(ring, budget_per_ring);
                else
                        cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring);
+
+               work_done += cleaned;
                /* if we didn't clean as many as budgeted, we must be done */
                clean_complete &= (budget_per_ring != cleaned);
        }
 
        /* If work not completed, return budget and polling will return */
        if (!clean_complete) {
+tx_only:
                if (arm_wb)
-                       i40e_force_wb(vsi, q_vector);
+                       i40evf_force_wb(vsi, q_vector);
                return budget;
        }
 
@@ -1393,7 +1373,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget)
                q_vector->arm_wb_state = false;
 
        /* Work is done so exit the polling mode and re-enable the interrupt */
-       napi_complete(napi);
+       napi_complete_done(napi, work_done);
        i40e_update_enable_itr(vsi, q_vector);
        return 0;
 }
@@ -1437,6 +1417,7 @@ static inline int i40evf_tx_prepare_vlan_flags(struct sk_buff *skb,
        /* else if it is a SW VLAN, check the next protocol and store the tag */
        } else if (protocol == htons(ETH_P_8021Q)) {
                struct vlan_hdr *vhdr, _vhdr;
+
                vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), &_vhdr);
                if (!vhdr)
                        return -EINVAL;
@@ -1979,6 +1960,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
        u32 td_cmd = 0;
        u8 hdr_len = 0;
        int tso;
+
        if (0 == i40evf_xmit_descriptor_count(skb, tx_ring))
                return NETDEV_TX_BUSY;
 
@@ -2006,10 +1988,11 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
        else if (tso)
                tx_flags |= I40E_TX_FLAGS_TSO;
 
-       if (i40e_chk_linearize(skb, tx_flags))
+       if (i40e_chk_linearize(skb, tx_flags)) {
                if (skb_linearize(skb))
                        goto out_drop;
-
+               tx_ring->tx_stats.tx_linearize++;
+       }
        skb_tx_timestamp(skb);
 
        /* always enable CRC insertion offload */
index 9a30f5d8c0899c2f813319f347ff82b6e04faee1..ebc1bf77f03606fb05312b2a5c84e788b92435cb 100644 (file)
 #define I40E_MAX_ITR               0x0FF0  /* reg uses 2 usec resolution */
 #define I40E_MIN_ITR               0x0001  /* reg uses 2 usec resolution */
 #define I40E_ITR_100K              0x0005
+#define I40E_ITR_50K               0x000A
 #define I40E_ITR_20K               0x0019
+#define I40E_ITR_18K               0x001B
 #define I40E_ITR_8K                0x003E
 #define I40E_ITR_4K                0x007A
-#define I40E_ITR_RX_DEF            I40E_ITR_8K
-#define I40E_ITR_TX_DEF            I40E_ITR_4K
+#define I40E_MAX_INTRL             0x3B    /* reg uses 4 usec resolution */
+#define I40E_ITR_RX_DEF            I40E_ITR_20K
+#define I40E_ITR_TX_DEF            I40E_ITR_20K
 #define I40E_ITR_DYNAMIC           0x8000  /* use top bit as a flag */
 #define I40E_MIN_INT_RATE          250     /* ~= 1000000 / (I40E_MAX_ITR * 2) */
 #define I40E_MAX_INT_RATE          500000  /* == 1000000 / (I40E_MIN_ITR * 2) */
 #define ITR_TO_REG(setting) ((setting & ~I40E_ITR_DYNAMIC) >> 1)
 #define ITR_IS_DYNAMIC(setting) (!!(setting & I40E_ITR_DYNAMIC))
 #define ITR_REG_TO_USEC(itr_reg) (itr_reg << 1)
+/* 0x40 is the enable bit for interrupt rate limiting, and must be set if
+ * the value of the rate limit is non-zero
+ */
+#define INTRL_ENA                  BIT(6)
+#define INTRL_REG_TO_USEC(intrl) ((intrl & ~INTRL_ENA) << 2)
+#define INTRL_USEC_TO_REG(set) ((set) ? ((set) >> 2) | INTRL_ENA : 0)
+#define I40E_INTRL_8K              125     /* 8000 ints/sec */
+#define I40E_INTRL_62K             16      /* 62500 ints/sec */
+#define I40E_INTRL_83K             12      /* 83333 ints/sec */
 
 #define I40E_QUEUE_END_OF_LIST 0x7FF
 
@@ -79,16 +91,16 @@ enum i40e_dyn_idx_t {
        BIT_ULL(I40E_FILTER_PCTYPE_L2_PAYLOAD))
 
 #define I40E_DEFAULT_RSS_HENA_EXPANDED (I40E_DEFAULT_RSS_HENA | \
-               BIT(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK) | \
-               BIT(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \
-               BIT(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \
-               BIT(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK) | \
-               BIT(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \
-               BIT(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP))
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK) | \
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK) | \
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \
+       BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP))
 
 #define i40e_pf_get_default_rss_hena(pf) \
        (((pf)->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \
-               I40E_DEFAULT_RSS_HENA_EXPANDED : I40E_DEFAULT_RSS_HENA)
+         I40E_DEFAULT_RSS_HENA_EXPANDED : I40E_DEFAULT_RSS_HENA)
 
 /* Supported Rx Buffer Sizes */
 #define I40E_RXBUFFER_512   512    /* Used for packet split */
@@ -164,6 +176,7 @@ struct i40e_tx_buffer {
        };
        unsigned int bytecount;
        unsigned short gso_segs;
+
        DEFINE_DMA_UNMAP_ADDR(dma);
        DEFINE_DMA_UNMAP_LEN(len);
        u32 tx_flags;
@@ -187,6 +200,7 @@ struct i40e_tx_queue_stats {
        u64 restart_queue;
        u64 tx_busy;
        u64 tx_done_old;
+       u64 tx_linearize;
 };
 
 struct i40e_rx_queue_stats {
@@ -198,8 +212,6 @@ struct i40e_rx_queue_stats {
 enum i40e_ring_state_t {
        __I40E_TX_FDIR_INIT_DONE,
        __I40E_TX_XPS_INIT_DONE,
-       __I40E_TX_DETECT_HANG,
-       __I40E_HANG_CHECK_ARMED,
        __I40E_RX_PS_ENABLED,
        __I40E_RX_16BYTE_DESC_ENABLED,
 };
@@ -210,12 +222,6 @@ enum i40e_ring_state_t {
        set_bit(__I40E_RX_PS_ENABLED, &(ring)->state)
 #define clear_ring_ps_enabled(ring) \
        clear_bit(__I40E_RX_PS_ENABLED, &(ring)->state)
-#define check_for_tx_hang(ring) \
-       test_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
-#define set_check_for_tx_hang(ring) \
-       set_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
-#define clear_check_for_tx_hang(ring) \
-       clear_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
 #define ring_is_16byte_desc_enabled(ring) \
        test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state)
 #define set_ring_16byte_desc_enabled(ring) \
@@ -287,6 +293,7 @@ enum i40e_latency_range {
        I40E_LOWEST_LATENCY = 0,
        I40E_LOW_LATENCY = 1,
        I40E_BULK_LATENCY = 2,
+       I40E_ULTRA_LATENCY = 3,
 };
 
 struct i40e_ring_container {
index 24a2693869a109af5725bd9182a66cbadfc6bd7e..301fe2b6dd03b153f6c3d0814ca25ef84e69af1a 100644 (file)
 #include "i40e_adminq.h"
 #include "i40e_hmc.h"
 #include "i40e_lan_hmc.h"
-
-/* Device IDs */
-#define I40E_DEV_ID_SFP_XL710          0x1572
-#define I40E_DEV_ID_QEMU               0x1574
-#define I40E_DEV_ID_KX_A               0x157F
-#define I40E_DEV_ID_KX_B               0x1580
-#define I40E_DEV_ID_KX_C               0x1581
-#define I40E_DEV_ID_QSFP_A             0x1583
-#define I40E_DEV_ID_QSFP_B             0x1584
-#define I40E_DEV_ID_QSFP_C             0x1585
-#define I40E_DEV_ID_10G_BASE_T         0x1586
-#define I40E_DEV_ID_20G_KR2            0x1587
-#define I40E_DEV_ID_VF                 0x154C
-#define I40E_DEV_ID_VF_HV              0x1571
-#define I40E_DEV_ID_SFP_X722           0x37D0
-#define I40E_DEV_ID_1G_BASE_T_X722     0x37D1
-#define I40E_DEV_ID_10G_BASE_T_X722    0x37D2
-#define I40E_DEV_ID_X722_VF            0x37CD
-#define I40E_DEV_ID_X722_VF_HV         0x37D9
-
-#define i40e_is_40G_device(d)          ((d) == I40E_DEV_ID_QSFP_A  || \
-                                        (d) == I40E_DEV_ID_QSFP_B  || \
-                                        (d) == I40E_DEV_ID_QSFP_C)
+#include "i40e_devids.h"
 
 /* I40E_MASK is a macro used on 32 bit registers */
 #define I40E_MASK(mask, shift) (mask << shift)
@@ -158,14 +136,14 @@ enum i40e_set_fc_aq_failures {
 };
 
 enum i40e_vsi_type {
-       I40E_VSI_MAIN = 0,
-       I40E_VSI_VMDQ1,
-       I40E_VSI_VMDQ2,
-       I40E_VSI_CTRL,
-       I40E_VSI_FCOE,
-       I40E_VSI_MIRROR,
-       I40E_VSI_SRIOV,
-       I40E_VSI_FDIR,
+       I40E_VSI_MAIN   = 0,
+       I40E_VSI_VMDQ1  = 1,
+       I40E_VSI_VMDQ2  = 2,
+       I40E_VSI_CTRL   = 3,
+       I40E_VSI_FCOE   = 4,
+       I40E_VSI_MIRROR = 5,
+       I40E_VSI_SRIOV  = 6,
+       I40E_VSI_FDIR   = 7,
        I40E_VSI_TYPE_UNKNOWN
 };
 
@@ -189,16 +167,65 @@ struct i40e_link_status {
        bool crc_enable;
        u8 pacing;
        u8 requested_speeds;
+       u8 module_type[3];
+       /* 1st byte: module identifier */
+#define I40E_MODULE_TYPE_SFP           0x03
+#define I40E_MODULE_TYPE_QSFP          0x0D
+       /* 2nd byte: ethernet compliance codes for 10/40G */
+#define I40E_MODULE_TYPE_40G_ACTIVE    0x01
+#define I40E_MODULE_TYPE_40G_LR4       0x02
+#define I40E_MODULE_TYPE_40G_SR4       0x04
+#define I40E_MODULE_TYPE_40G_CR4       0x08
+#define I40E_MODULE_TYPE_10G_BASE_SR   0x10
+#define I40E_MODULE_TYPE_10G_BASE_LR   0x20
+#define I40E_MODULE_TYPE_10G_BASE_LRM  0x40
+#define I40E_MODULE_TYPE_10G_BASE_ER   0x80
+       /* 3rd byte: ethernet compliance codes for 1G */
+#define I40E_MODULE_TYPE_1000BASE_SX   0x01
+#define I40E_MODULE_TYPE_1000BASE_LX   0x02
+#define I40E_MODULE_TYPE_1000BASE_CX   0x04
+#define I40E_MODULE_TYPE_1000BASE_T    0x08
+};
+
+enum i40e_aq_capabilities_phy_type {
+       I40E_CAP_PHY_TYPE_SGMII           = BIT(I40E_PHY_TYPE_SGMII),
+       I40E_CAP_PHY_TYPE_1000BASE_KX     = BIT(I40E_PHY_TYPE_1000BASE_KX),
+       I40E_CAP_PHY_TYPE_10GBASE_KX4     = BIT(I40E_PHY_TYPE_10GBASE_KX4),
+       I40E_CAP_PHY_TYPE_10GBASE_KR      = BIT(I40E_PHY_TYPE_10GBASE_KR),
+       I40E_CAP_PHY_TYPE_40GBASE_KR4     = BIT(I40E_PHY_TYPE_40GBASE_KR4),
+       I40E_CAP_PHY_TYPE_XAUI            = BIT(I40E_PHY_TYPE_XAUI),
+       I40E_CAP_PHY_TYPE_XFI             = BIT(I40E_PHY_TYPE_XFI),
+       I40E_CAP_PHY_TYPE_SFI             = BIT(I40E_PHY_TYPE_SFI),
+       I40E_CAP_PHY_TYPE_XLAUI           = BIT(I40E_PHY_TYPE_XLAUI),
+       I40E_CAP_PHY_TYPE_XLPPI           = BIT(I40E_PHY_TYPE_XLPPI),
+       I40E_CAP_PHY_TYPE_40GBASE_CR4_CU  = BIT(I40E_PHY_TYPE_40GBASE_CR4_CU),
+       I40E_CAP_PHY_TYPE_10GBASE_CR1_CU  = BIT(I40E_PHY_TYPE_10GBASE_CR1_CU),
+       I40E_CAP_PHY_TYPE_10GBASE_AOC     = BIT(I40E_PHY_TYPE_10GBASE_AOC),
+       I40E_CAP_PHY_TYPE_40GBASE_AOC     = BIT(I40E_PHY_TYPE_40GBASE_AOC),
+       I40E_CAP_PHY_TYPE_100BASE_TX      = BIT(I40E_PHY_TYPE_100BASE_TX),
+       I40E_CAP_PHY_TYPE_1000BASE_T      = BIT(I40E_PHY_TYPE_1000BASE_T),
+       I40E_CAP_PHY_TYPE_10GBASE_T       = BIT(I40E_PHY_TYPE_10GBASE_T),
+       I40E_CAP_PHY_TYPE_10GBASE_SR      = BIT(I40E_PHY_TYPE_10GBASE_SR),
+       I40E_CAP_PHY_TYPE_10GBASE_LR      = BIT(I40E_PHY_TYPE_10GBASE_LR),
+       I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU = BIT(I40E_PHY_TYPE_10GBASE_SFPP_CU),
+       I40E_CAP_PHY_TYPE_10GBASE_CR1     = BIT(I40E_PHY_TYPE_10GBASE_CR1),
+       I40E_CAP_PHY_TYPE_40GBASE_CR4     = BIT(I40E_PHY_TYPE_40GBASE_CR4),
+       I40E_CAP_PHY_TYPE_40GBASE_SR4     = BIT(I40E_PHY_TYPE_40GBASE_SR4),
+       I40E_CAP_PHY_TYPE_40GBASE_LR4     = BIT(I40E_PHY_TYPE_40GBASE_LR4),
+       I40E_CAP_PHY_TYPE_1000BASE_SX     = BIT(I40E_PHY_TYPE_1000BASE_SX),
+       I40E_CAP_PHY_TYPE_1000BASE_LX     = BIT(I40E_PHY_TYPE_1000BASE_LX),
+       I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL =
+                                        BIT(I40E_PHY_TYPE_1000BASE_T_OPTICAL),
+       I40E_CAP_PHY_TYPE_20GBASE_KR2     = BIT(I40E_PHY_TYPE_20GBASE_KR2)
 };
 
 struct i40e_phy_info {
        struct i40e_link_status link_info;
        struct i40e_link_status link_info_old;
-       u32 autoneg_advertised;
-       u32 phy_id;
-       u32 module_type;
        bool get_link_info;
        enum i40e_media_type media_type;
+       /* all the phy types the NVM is capable of */
+       enum i40e_aq_capabilities_phy_type phy_types;
 };
 
 #define I40E_HW_CAP_MAX_GPIO                   30
@@ -286,6 +313,7 @@ struct i40e_nvm_info {
        bool blank_nvm_mode;      /* is NVM empty (no FW present)*/
        u16 version;              /* NVM package version */
        u32 eetrack;              /* NVM data version */
+       u32 oem_ver;              /* OEM version info */
 };
 
 /* definitions used in NVM update support */
@@ -304,12 +332,17 @@ enum i40e_nvmupd_cmd {
        I40E_NVMUPD_CSUM_CON,
        I40E_NVMUPD_CSUM_SA,
        I40E_NVMUPD_CSUM_LCB,
+       I40E_NVMUPD_STATUS,
+       I40E_NVMUPD_EXEC_AQ,
+       I40E_NVMUPD_GET_AQ_RESULT,
 };
 
 enum i40e_nvmupd_state {
        I40E_NVMUPD_STATE_INIT,
        I40E_NVMUPD_STATE_READING,
-       I40E_NVMUPD_STATE_WRITING
+       I40E_NVMUPD_STATE_WRITING,
+       I40E_NVMUPD_STATE_INIT_WAIT,
+       I40E_NVMUPD_STATE_WRITE_WAIT,
 };
 
 /* nvm_access definition and its masks/shifts need to be accessible to
@@ -328,6 +361,7 @@ enum i40e_nvmupd_state {
 #define I40E_NVM_SA            (I40E_NVM_SNT | I40E_NVM_LCB)
 #define I40E_NVM_ERA           0x4
 #define I40E_NVM_CSUM          0x8
+#define I40E_NVM_EXEC          0xf
 
 #define I40E_NVM_ADAPT_SHIFT   16
 #define I40E_NVM_ADAPT_MASK    (0xffff << I40E_NVM_ADAPT_SHIFT)
@@ -486,6 +520,8 @@ struct i40e_hw {
 
        /* state of nvm update process */
        enum i40e_nvmupd_state nvmupd_state;
+       struct i40e_aq_desc nvm_wb_desc;
+       struct i40e_virt_mem nvm_buff;
 
        /* HMC info */
        struct i40e_hmc_info hmc; /* HMC info struct */
@@ -494,8 +530,9 @@ struct i40e_hw {
        u16 dcbx_status;
 
        /* DCBX info */
-       struct i40e_dcbx_config local_dcbx_config;
-       struct i40e_dcbx_config remote_dcbx_config;
+       struct i40e_dcbx_config local_dcbx_config; /* Oper/Local Cfg */
+       struct i40e_dcbx_config remote_dcbx_config; /* Peer Cfg */
+       struct i40e_dcbx_config desired_dcbx_config; /* CEE Desired Cfg */
 
        /* debug mask */
        u32 debug_mask;
@@ -1018,8 +1055,8 @@ enum i40e_filter_program_desc_fd_status {
 };
 
 #define I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT       23
-#define I40E_TXD_FLTR_QW0_DEST_VSI_MASK \
-                                      BIT_ULL(I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT)
+#define I40E_TXD_FLTR_QW0_DEST_VSI_MASK        (0x1FFUL << \
+                                        I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT)
 
 #define I40E_TXD_FLTR_QW1_CMD_SHIFT    4
 #define I40E_TXD_FLTR_QW1_CMD_MASK     (0xFFFFULL << \
@@ -1162,6 +1199,7 @@ struct i40e_hw_port_stats {
 /* Checksum and Shadow RAM pointers */
 #define I40E_SR_NVM_CONTROL_WORD               0x00
 #define I40E_SR_EMP_MODULE_PTR                 0x0F
+#define I40E_NVM_OEM_VER_OFF                   0x83
 #define I40E_SR_NVM_DEV_STARTER_VERSION                0x18
 #define I40E_SR_NVM_WAKE_ON_LAN                        0x19
 #define I40E_SR_ALTERNATE_SAN_MAC_ADDRESS_PTR  0x27
index e6db20e8a395b8576175c1b2c7c3398e723d9d73..9f7b279b9d9c8f827dec988b25801f26f0386f7c 100644 (file)
@@ -81,7 +81,6 @@ enum i40e_virtchnl_ops {
        I40E_VIRTCHNL_OP_GET_STATS = 15,
        I40E_VIRTCHNL_OP_FCOE = 16,
        I40E_VIRTCHNL_OP_EVENT = 17,
-       I40E_VIRTCHNL_OP_CONFIG_RSS = 18,
 };
 
 /* Virtual channel message descriptor. This overlays the admin queue
@@ -151,6 +150,7 @@ struct i40e_virtchnl_vsi_resource {
 #define I40E_VIRTCHNL_VF_OFFLOAD_FCOE          0x00000004
 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ                0x00000008
 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG       0x00000010
+#define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR     0x00000020
 #define I40E_VIRTCHNL_VF_OFFLOAD_VLAN          0x00010000
 #define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING    0x00020000
 
index 3817cbbf45e6e753089b3d36e0c03daf7b5073d8..22fc3d49c4b95233fe1103aa92b95e5f97ecf6ba 100644 (file)
 
 #define DEFAULT_DEBUG_LEVEL_SHIFT 3
 #define PFX "i40evf: "
-#define DPRINTK(nlevel, klevel, fmt, args...) \
-       ((void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \
-       printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->netdev->name, \
-               __func__ , ## args)))
 
 /* dummy struct to make common code less painful */
 struct i40e_vsi {
@@ -70,6 +66,7 @@ struct i40e_vsi {
         */
        u16 rx_itr_setting;
        u16 tx_itr_setting;
+       u16 qs_handle;
 };
 
 /* How many Rx Buffers do we bundle into one write to the hardware ? */
@@ -90,7 +87,7 @@ struct i40e_vsi {
 #define I40EVF_MAX_RXBUFFER   16384  /* largest size for single descriptor */
 #define I40EVF_MAX_AQ_BUF_SIZE    4096
 #define I40EVF_AQ_LEN             32
-#define I40EVF_AQ_MAX_ERR         10 /* times to try before resetting AQ */
+#define I40EVF_AQ_MAX_ERR         20 /* times to try before resetting AQ */
 
 #define MAXIMUM_ETHERNET_VLAN_SIZE (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN)
 
@@ -115,6 +112,8 @@ struct i40e_q_vector {
        struct i40e_ring_container tx;
        u32 ring_mask;
        u8 num_ringpairs;       /* total number of ring pairs in vector */
+#define ITR_COUNTDOWN_START 100
+       u8 itr_countdown;       /* when 0 or 1 update ITR */
        int v_idx;        /* vector index in list */
        char name[IFNAMSIZ + 9];
        bool arm_wb_state;
@@ -214,7 +213,6 @@ struct i40evf_adapter {
 #define I40EVF_FLAG_RX_1BUF_CAPABLE              BIT(1)
 #define I40EVF_FLAG_RX_PS_CAPABLE                BIT(2)
 #define I40EVF_FLAG_RX_PS_ENABLED                BIT(3)
-#define I40EVF_FLAG_IN_NETPOLL                   BIT(4)
 #define I40EVF_FLAG_IMIR_ENABLED                 BIT(5)
 #define I40EVF_FLAG_MQ_CAPABLE                   BIT(6)
 #define I40EVF_FLAG_NEED_LINK_UPDATE             BIT(7)
@@ -223,10 +221,10 @@ struct i40evf_adapter {
 #define I40EVF_FLAG_RESET_NEEDED                 BIT(10)
 #define I40EVF_FLAG_WB_ON_ITR_CAPABLE          BIT(11)
 #define I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE     BIT(12)
+#define I40EVF_FLAG_ADDR_SET_BY_PF             BIT(13)
 /* duplicates for common code */
 #define I40E_FLAG_FDIR_ATR_ENABLED              0
 #define I40E_FLAG_DCB_ENABLED                   0
-#define I40E_FLAG_IN_NETPOLL                    I40EVF_FLAG_IN_NETPOLL
 #define I40E_FLAG_RX_CSUM_ENABLED                I40EVF_FLAG_RX_CSUM_ENABLED
 #define I40E_FLAG_WB_ON_ITR_CAPABLE            I40EVF_FLAG_WB_ON_ITR_CAPABLE
 #define I40E_FLAG_OUTER_UDP_CSUM_CAPABLE       I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE
index e85849b9ff98aab6dcbc1ada4e75f3123deee2b6..d962164dfb0fbf579a150223b8d594ecb073b51f 100644 (file)
@@ -34,7 +34,7 @@ char i40evf_driver_name[] = "i40evf";
 static const char i40evf_driver_string[] =
        "Intel(R) XL710/X710 Virtual Function Network Driver";
 
-#define DRV_VERSION "1.3.5"
+#define DRV_VERSION "1.3.33"
 const char i40evf_driver_version[] = DRV_VERSION;
 static const char i40evf_copyright[] =
        "Copyright (c) 2013 - 2015 Intel Corporation.";
@@ -282,6 +282,7 @@ static void i40evf_fire_sw_int(struct i40evf_adapter *adapter, u32 mask)
 /**
  * i40evf_irq_enable - Enable default interrupt generation settings
  * @adapter: board private structure
+ * @flush: boolean value whether to run rd32()
  **/
 void i40evf_irq_enable(struct i40evf_adapter *adapter, bool flush)
 {
@@ -305,15 +306,14 @@ static irqreturn_t i40evf_msix_aq(int irq, void *data)
        struct i40evf_adapter *adapter = netdev_priv(netdev);
        struct i40e_hw *hw = &adapter->hw;
        u32 val;
-       u32 ena_mask;
 
        /* handle non-queue interrupts */
-       val = rd32(hw, I40E_VFINT_ICR01);
-       ena_mask = rd32(hw, I40E_VFINT_ICR0_ENA1);
+       rd32(hw, I40E_VFINT_ICR01);
+       rd32(hw, I40E_VFINT_ICR0_ENA1);
 
 
-       val = rd32(hw, I40E_VFINT_DYN_CTL01);
-       val = val | I40E_VFINT_DYN_CTL01_CLEARPBA_MASK;
+       val = rd32(hw, I40E_VFINT_DYN_CTL01) |
+             I40E_VFINT_DYN_CTL01_CLEARPBA_MASK;
        wr32(hw, I40E_VFINT_DYN_CTL01, val);
 
        /* schedule work on the private workqueue */
@@ -334,7 +334,7 @@ static irqreturn_t i40evf_msix_clean_rings(int irq, void *data)
        if (!q_vector->tx.ring && !q_vector->rx.ring)
                return IRQ_HANDLED;
 
-       napi_schedule(&q_vector->napi);
+       napi_schedule_irqoff(&q_vector->napi);
 
        return IRQ_HANDLED;
 }
@@ -357,6 +357,7 @@ i40evf_map_vector_to_rxq(struct i40evf_adapter *adapter, int v_idx, int r_idx)
        q_vector->rx.ring = rx_ring;
        q_vector->rx.count++;
        q_vector->rx.latency_range = I40E_LOW_LATENCY;
+       q_vector->itr_countdown = ITR_COUNTDOWN_START;
 }
 
 /**
@@ -377,6 +378,7 @@ i40evf_map_vector_to_txq(struct i40evf_adapter *adapter, int v_idx, int t_idx)
        q_vector->tx.ring = tx_ring;
        q_vector->tx.count++;
        q_vector->tx.latency_range = I40E_LOW_LATENCY;
+       q_vector->itr_countdown = ITR_COUNTDOWN_START;
        q_vector->num_ringpairs++;
        q_vector->ring_mask |= BIT(t_idx);
 }
@@ -444,6 +446,29 @@ out:
        return err;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/**
+ * i40evf_netpoll - A Polling 'interrupt' handler
+ * @netdev: network interface device structure
+ *
+ * This is used by netconsole to send skbs without having to re-enable
+ * interrupts.  It's not called while the normal interrupt routine is executing.
+ **/
+static void i40evf_netpoll(struct net_device *netdev)
+{
+       struct i40evf_adapter *adapter = netdev_priv(netdev);
+       int q_vectors = adapter->num_msix_vectors - NONQ_VECS;
+       int i;
+
+       /* if interface is down do nothing */
+       if (test_bit(__I40E_DOWN, &adapter->vsi.state))
+               return;
+
+       for (i = 0; i < q_vectors; i++)
+               i40evf_msix_clean_rings(0, adapter->q_vector[i]);
+}
+
+#endif
 /**
  * i40evf_request_traffic_irqs - Initialize MSI-X interrupts
  * @adapter: board private structure
@@ -489,8 +514,7 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename)
                        q_vector);
                if (err) {
                        dev_info(&adapter->pdev->dev,
-                                "%s: request_irq failed, error: %d\n",
-                               __func__, err);
+                                "Request_irq failed, error: %d\n", err);
                        goto free_queue_irqs;
                }
                /* assign the mask for this irq */
@@ -731,6 +755,8 @@ static int i40evf_vlan_rx_add_vid(struct net_device *netdev,
 {
        struct i40evf_adapter *adapter = netdev_priv(netdev);
 
+       if (!VLAN_ALLOWED(adapter))
+               return -EIO;
        if (i40evf_add_vlan(adapter, vid) == NULL)
                return -ENOMEM;
        return 0;
@@ -746,8 +772,11 @@ static int i40evf_vlan_rx_kill_vid(struct net_device *netdev,
 {
        struct i40evf_adapter *adapter = netdev_priv(netdev);
 
-       i40evf_del_vlan(adapter, vid);
-       return 0;
+       if (VLAN_ALLOWED(adapter)) {
+               i40evf_del_vlan(adapter, vid);
+               return 0;
+       }
+       return -EIO;
 }
 
 /**
@@ -837,6 +866,15 @@ static int i40evf_set_mac(struct net_device *netdev, void *p)
        if (ether_addr_equal(netdev->dev_addr, addr->sa_data))
                return 0;
 
+       if (adapter->flags & I40EVF_FLAG_ADDR_SET_BY_PF)
+               return -EPERM;
+
+       f = i40evf_find_filter(adapter, hw->mac.addr);
+       if (f) {
+               f->remove = true;
+               adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER;
+       }
+
        f = i40evf_add_filter(adapter, addr->sa_data);
        if (f) {
                ether_addr_copy(hw->mac.addr, addr->sa_data);
@@ -856,6 +894,7 @@ static void i40evf_set_rx_mode(struct net_device *netdev)
        struct i40evf_mac_filter *f, *ftmp;
        struct netdev_hw_addr *uca;
        struct netdev_hw_addr *mca;
+       struct netdev_hw_addr *ha;
        int count = 50;
 
        /* add addr if not already in the filter list */
@@ -877,29 +916,27 @@ static void i40evf_set_rx_mode(struct net_device *netdev)
        }
        /* remove filter if not in netdev list */
        list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) {
-               bool found = false;
-
-               if (is_multicast_ether_addr(f->macaddr)) {
-                       netdev_for_each_mc_addr(mca, netdev) {
-                               if (ether_addr_equal(mca->addr, f->macaddr)) {
-                                       found = true;
-                                       break;
-                               }
-                       }
-               } else {
-                       netdev_for_each_uc_addr(uca, netdev) {
-                               if (ether_addr_equal(uca->addr, f->macaddr)) {
-                                       found = true;
-                                       break;
-                               }
-                       }
-                       if (ether_addr_equal(f->macaddr, adapter->hw.mac.addr))
-                               found = true;
-               }
-               if (!found) {
-                       f->remove = true;
-                       adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER;
-               }
+               netdev_for_each_mc_addr(mca, netdev)
+                       if (ether_addr_equal(mca->addr, f->macaddr))
+                               goto bottom_of_search_loop;
+
+               netdev_for_each_uc_addr(uca, netdev)
+                       if (ether_addr_equal(uca->addr, f->macaddr))
+                               goto bottom_of_search_loop;
+
+               for_each_dev_addr(netdev, ha)
+                       if (ether_addr_equal(ha->addr, f->macaddr))
+                               goto bottom_of_search_loop;
+
+               if (ether_addr_equal(f->macaddr, adapter->hw.mac.addr))
+                       goto bottom_of_search_loop;
+
+               /* f->macaddr wasn't found in uc, mc, or ha list so delete it */
+               f->remove = true;
+               adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER;
+
+bottom_of_search_loop:
+               continue;
        }
        clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
 }
@@ -1111,6 +1148,8 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
                tx_ring->netdev = adapter->netdev;
                tx_ring->dev = &adapter->pdev->dev;
                tx_ring->count = adapter->tx_desc_count;
+               if (adapter->flags & I40E_FLAG_WB_ON_ITR_CAPABLE)
+                       tx_ring->flags |= I40E_TXR_FLAGS_WB_ON_ITR;
                adapter->tx_rings[i] = tx_ring;
 
                rx_ring = &tx_ring[1];
@@ -1165,7 +1204,7 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter)
        for (vector = 0; vector < v_budget; vector++)
                adapter->msix_entries[vector].entry = vector;
 
-       i40evf_acquire_msix_vectors(adapter, v_budget);
+       err = i40evf_acquire_msix_vectors(adapter, v_budget);
 
 out:
        adapter->netdev->real_num_tx_queues = pairs;
@@ -1421,16 +1460,16 @@ static void i40evf_watchdog_task(struct work_struct *work)
                                                      struct i40evf_adapter,
                                                      watchdog_task);
        struct i40e_hw *hw = &adapter->hw;
-       uint32_t rstat_val;
+       u32 reg_val;
 
        if (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section))
                goto restart_watchdog;
 
        if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) {
-               rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
-                           I40E_VFGEN_RSTAT_VFR_STATE_MASK;
-               if ((rstat_val == I40E_VFR_VFACTIVE) ||
-                   (rstat_val == I40E_VFR_COMPLETED)) {
+               reg_val = rd32(hw, I40E_VFGEN_RSTAT) &
+                         I40E_VFGEN_RSTAT_VFR_STATE_MASK;
+               if ((reg_val == I40E_VFR_VFACTIVE) ||
+                   (reg_val == I40E_VFR_COMPLETED)) {
                        /* A chance for redemption! */
                        dev_err(&adapter->pdev->dev, "Hardware came out of reset. Attempting reinit.\n");
                        adapter->state = __I40EVF_STARTUP;
@@ -1455,11 +1494,8 @@ static void i40evf_watchdog_task(struct work_struct *work)
                goto watchdog_done;
 
        /* check for reset */
-       rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
-                   I40E_VFGEN_RSTAT_VFR_STATE_MASK;
-       if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING) &&
-           (rstat_val != I40E_VFR_VFACTIVE) &&
-           (rstat_val != I40E_VFR_COMPLETED)) {
+       reg_val = rd32(hw, I40E_VF_ARQLEN1) & I40E_VF_ARQLEN1_ARQENABLE_MASK;
+       if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING) && !reg_val) {
                adapter->state = __I40EVF_RESETTING;
                adapter->flags |= I40EVF_FLAG_RESET_PENDING;
                dev_err(&adapter->pdev->dev, "Hardware reset detected\n");
@@ -1573,8 +1609,9 @@ static void i40evf_reset_task(struct work_struct *work)
                                                      reset_task);
        struct net_device *netdev = adapter->netdev;
        struct i40e_hw *hw = &adapter->hw;
+       struct i40evf_vlan_filter *vlf;
        struct i40evf_mac_filter *f;
-       uint32_t rstat_val;
+       u32 reg_val;
        int i = 0, err;
 
        while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK,
@@ -1595,12 +1632,11 @@ static void i40evf_reset_task(struct work_struct *work)
 
        /* poll until we see the reset actually happen */
        for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) {
-               rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
-                           I40E_VFGEN_RSTAT_VFR_STATE_MASK;
-               if ((rstat_val != I40E_VFR_VFACTIVE) &&
-                   (rstat_val != I40E_VFR_COMPLETED))
+               reg_val = rd32(hw, I40E_VF_ARQLEN1) &
+                         I40E_VF_ARQLEN1_ARQENABLE_MASK;
+               if (!reg_val)
                        break;
-               usleep_range(500, 1000);
+               usleep_range(5000, 10000);
        }
        if (i == I40EVF_RESET_WAIT_COUNT) {
                dev_info(&adapter->pdev->dev, "Never saw reset\n");
@@ -1609,21 +1645,21 @@ static void i40evf_reset_task(struct work_struct *work)
 
        /* wait until the reset is complete and the PF is responding to us */
        for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) {
-               rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
-                           I40E_VFGEN_RSTAT_VFR_STATE_MASK;
-               if (rstat_val == I40E_VFR_VFACTIVE)
+               reg_val = rd32(hw, I40E_VFGEN_RSTAT) &
+                         I40E_VFGEN_RSTAT_VFR_STATE_MASK;
+               if (reg_val == I40E_VFR_VFACTIVE)
                        break;
                msleep(I40EVF_RESET_WAIT_MS);
        }
        /* extra wait to make sure minimum wait is met */
        msleep(I40EVF_RESET_WAIT_MS);
        if (i == I40EVF_RESET_WAIT_COUNT) {
-               struct i40evf_mac_filter *f, *ftmp;
+               struct i40evf_mac_filter *ftmp;
                struct i40evf_vlan_filter *fv, *fvtmp;
 
                /* reset never finished */
                dev_err(&adapter->pdev->dev, "Reset never finished (%x)\n",
-                       rstat_val);
+                       reg_val);
                adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED;
 
                if (netif_running(adapter->netdev)) {
@@ -1697,8 +1733,8 @@ continue_reset:
                f->add = true;
        }
        /* re-add all VLAN filters */
-       list_for_each_entry(f, &adapter->vlan_filter_list, list) {
-               f->add = true;
+       list_for_each_entry(vlf, &adapter->vlan_filter_list, list) {
+               vlf->add = true;
        }
        adapter->aq_required |= I40EVF_FLAG_AQ_ADD_MAC_FILTER;
        adapter->aq_required |= I40EVF_FLAG_AQ_ADD_VLAN_FILTER;
@@ -1853,8 +1889,7 @@ static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter)
                if (!err)
                        continue;
                dev_err(&adapter->pdev->dev,
-                       "%s: Allocation for Tx Queue %u failed\n",
-                       __func__, i);
+                       "Allocation for Tx Queue %u failed\n", i);
                break;
        }
 
@@ -1881,8 +1916,7 @@ static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter)
                if (!err)
                        continue;
                dev_err(&adapter->pdev->dev,
-                       "%s: Allocation for Rx Queue %u failed\n",
-                       __func__, i);
+                       "Allocation for Rx Queue %u failed\n", i);
                break;
        }
        return err;
@@ -2041,6 +2075,9 @@ static const struct net_device_ops i40evf_netdev_ops = {
        .ndo_tx_timeout         = i40evf_tx_timeout,
        .ndo_vlan_rx_add_vid    = i40evf_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid   = i40evf_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = i40evf_netpoll,
+#endif
 };
 
 /**
@@ -2089,7 +2126,10 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
 
        if (adapter->vf_res->vf_offload_flags
            & I40E_VIRTCHNL_VF_OFFLOAD_VLAN) {
-               netdev->vlan_features = netdev->features;
+               netdev->vlan_features = netdev->features &
+                                       ~(NETIF_F_HW_VLAN_CTAG_TX |
+                                         NETIF_F_HW_VLAN_CTAG_RX |
+                                         NETIF_F_HW_VLAN_CTAG_FILTER);
                netdev->features |= NETIF_F_HW_VLAN_CTAG_TX |
                                    NETIF_F_HW_VLAN_CTAG_RX |
                                    NETIF_F_HW_VLAN_CTAG_FILTER;
@@ -2118,6 +2158,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
        adapter->vsi.tx_itr_setting = (I40E_ITR_DYNAMIC |
                                       ITR_REG_TO_USEC(I40E_ITR_TX_DEF));
        adapter->vsi.netdev = adapter->netdev;
+       adapter->vsi.qs_handle = adapter->vsi_res->qset_handle;
        return 0;
 }
 
@@ -2246,10 +2287,13 @@ static void i40evf_init_task(struct work_struct *work)
        if (!is_valid_ether_addr(adapter->hw.mac.addr)) {
                dev_info(&pdev->dev, "Invalid MAC address %pM, using random\n",
                         adapter->hw.mac.addr);
-               random_ether_addr(adapter->hw.mac.addr);
+               eth_hw_addr_random(netdev);
+               ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
+       } else {
+               adapter->flags |= I40EVF_FLAG_ADDR_SET_BY_PF;
+               ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr);
+               ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr);
        }
-       ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr);
-       ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr);
 
        init_timer(&adapter->watchdog_timer);
        adapter->watchdog_timer.function = &i40evf_watchdog_timer;
@@ -2265,6 +2309,9 @@ static void i40evf_init_task(struct work_struct *work)
        if (err)
                goto err_sw_init;
        i40evf_map_rings_to_vectors(adapter);
+       if (adapter->vf_res->vf_offload_flags &
+                   I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR)
+               adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE;
        if (!RSS_AQ(adapter))
                i40evf_configure_rss(adapter);
        err = i40evf_request_misc_irq(adapter);
@@ -2300,8 +2347,7 @@ static void i40evf_init_task(struct work_struct *work)
        }
        return;
 restart:
-       schedule_delayed_work(&adapter->init_task,
-                             msecs_to_jiffies(50));
+       schedule_delayed_work(&adapter->init_task, msecs_to_jiffies(30));
        return;
 
 err_register:
@@ -2314,11 +2360,14 @@ err_alloc:
 err:
        /* Things went into the weeds, so try again later */
        if (++adapter->aq_wait_count > I40EVF_AQ_MAX_ERR) {
-               dev_err(&pdev->dev, "Failed to communicate with PF; giving up\n");
+               dev_err(&pdev->dev, "Failed to communicate with PF; waiting before retry\n");
                adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED;
-               return; /* do not reschedule */
+               i40evf_shutdown_adminq(hw);
+               adapter->state = __I40EVF_STARTUP;
+               schedule_delayed_work(&adapter->init_task, HZ * 5);
+               return;
        }
-       schedule_delayed_work(&adapter->init_task, HZ * 3);
+       schedule_delayed_work(&adapter->init_task, HZ);
 }
 
 /**
@@ -2434,7 +2483,8 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        INIT_WORK(&adapter->adminq_task, i40evf_adminq_task);
        INIT_WORK(&adapter->watchdog_task, i40evf_watchdog_task);
        INIT_DELAYED_WORK(&adapter->init_task, i40evf_init_task);
-       schedule_delayed_work(&adapter->init_task, 10);
+       schedule_delayed_work(&adapter->init_task,
+                             msecs_to_jiffies(5 * (pdev->devfn & 0x07)));
 
        return 0;
 
@@ -2510,6 +2560,7 @@ static int i40evf_resume(struct pci_dev *pdev)
        rtnl_lock();
        err = i40evf_set_interrupt_capability(adapter);
        if (err) {
+               rtnl_unlock();
                dev_err(&pdev->dev, "Cannot enable MSI-X interrupts.\n");
                return err;
        }
index d4eb1a5e7d42c4562a659202685d0e8331d384c9..32e620e1eb5c9549db7f2743a1b2b2986523666f 100644 (file)
@@ -156,7 +156,8 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter)
        caps = I40E_VIRTCHNL_VF_OFFLOAD_L2 |
               I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ |
               I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG |
-              I40E_VIRTCHNL_VF_OFFLOAD_VLAN;
+              I40E_VIRTCHNL_VF_OFFLOAD_VLAN |
+              I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR;
        adapter->current_op = I40E_VIRTCHNL_OP_GET_VF_RESOURCES;
        adapter->aq_required &= ~I40EVF_FLAG_AQ_GET_CONFIG;
        if (PF_IS_V11(adapter))
@@ -234,8 +235,8 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter)
 
        if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
                /* bail because we already have a command pending */
-               dev_err(&adapter->pdev->dev, "%s: command %d pending\n",
-                       __func__, adapter->current_op);
+               dev_err(&adapter->pdev->dev, "Cannot configure queues, command %d pending\n",
+                       adapter->current_op);
                return;
        }
        adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES;
@@ -288,8 +289,8 @@ void i40evf_enable_queues(struct i40evf_adapter *adapter)
 
        if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
                /* bail because we already have a command pending */
-               dev_err(&adapter->pdev->dev, "%s: command %d pending\n",
-                       __func__, adapter->current_op);
+               dev_err(&adapter->pdev->dev, "Cannot enable queues, command %d pending\n",
+                       adapter->current_op);
                return;
        }
        adapter->current_op = I40E_VIRTCHNL_OP_ENABLE_QUEUES;
@@ -313,8 +314,8 @@ void i40evf_disable_queues(struct i40evf_adapter *adapter)
 
        if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
                /* bail because we already have a command pending */
-               dev_err(&adapter->pdev->dev, "%s: command %d pending\n",
-                       __func__, adapter->current_op);
+               dev_err(&adapter->pdev->dev, "Cannot disable queues, command %d pending\n",
+                       adapter->current_op);
                return;
        }
        adapter->current_op = I40E_VIRTCHNL_OP_DISABLE_QUEUES;
@@ -341,8 +342,8 @@ void i40evf_map_queues(struct i40evf_adapter *adapter)
 
        if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
                /* bail because we already have a command pending */
-               dev_err(&adapter->pdev->dev, "%s: command %d pending\n",
-                       __func__, adapter->current_op);
+               dev_err(&adapter->pdev->dev, "Cannot map queues to vectors, command %d pending\n",
+                       adapter->current_op);
                return;
        }
        adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP;
@@ -393,8 +394,8 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
 
        if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
                /* bail because we already have a command pending */
-               dev_err(&adapter->pdev->dev, "%s: command %d pending\n",
-                       __func__, adapter->current_op);
+               dev_err(&adapter->pdev->dev, "Cannot add filters, command %d pending\n",
+                       adapter->current_op);
                return;
        }
        list_for_each_entry(f, &adapter->mac_filter_list, list) {
@@ -410,8 +411,7 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_ether_addr_list) +
              (count * sizeof(struct i40e_virtchnl_ether_addr));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n",
-                        __func__);
+               dev_warn(&adapter->pdev->dev, "Too many add MAC changes in one request\n");
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_ether_addr_list)) /
                        sizeof(struct i40e_virtchnl_ether_addr);
@@ -453,8 +453,8 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
 
        if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
                /* bail because we already have a command pending */
-               dev_err(&adapter->pdev->dev, "%s: command %d pending\n",
-                       __func__, adapter->current_op);
+               dev_err(&adapter->pdev->dev, "Cannot remove filters, command %d pending\n",
+                       adapter->current_op);
                return;
        }
        list_for_each_entry(f, &adapter->mac_filter_list, list) {
@@ -470,8 +470,7 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_ether_addr_list) +
              (count * sizeof(struct i40e_virtchnl_ether_addr));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n",
-                        __func__);
+               dev_warn(&adapter->pdev->dev, "Too many delete MAC changes in one request\n");
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_ether_addr_list)) /
                        sizeof(struct i40e_virtchnl_ether_addr);
@@ -513,8 +512,8 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
 
        if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
                /* bail because we already have a command pending */
-               dev_err(&adapter->pdev->dev, "%s: command %d pending\n",
-                       __func__, adapter->current_op);
+               dev_err(&adapter->pdev->dev, "Cannot add VLANs, command %d pending\n",
+                       adapter->current_op);
                return;
        }
 
@@ -531,8 +530,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
              (count * sizeof(u16));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n",
-                        __func__);
+               dev_warn(&adapter->pdev->dev, "Too many add VLAN changes in one request\n");
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_vlan_filter_list)) /
                        sizeof(u16);
@@ -572,8 +570,8 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
 
        if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
                /* bail because we already have a command pending */
-               dev_err(&adapter->pdev->dev, "%s: command %d pending\n",
-                       __func__, adapter->current_op);
+               dev_err(&adapter->pdev->dev, "Cannot remove VLANs, command %d pending\n",
+                       adapter->current_op);
                return;
        }
 
@@ -590,8 +588,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
              (count * sizeof(u16));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n",
-                        __func__);
+               dev_warn(&adapter->pdev->dev, "Too many delete VLAN changes in one request\n");
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_vlan_filter_list)) /
                        sizeof(u16);
@@ -629,8 +626,8 @@ void i40evf_set_promiscuous(struct i40evf_adapter *adapter, int flags)
 
        if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
                /* bail because we already have a command pending */
-               dev_err(&adapter->pdev->dev, "%s: command %d pending\n",
-                       __func__, adapter->current_op);
+               dev_err(&adapter->pdev->dev, "Cannot set promiscuous mode, command %d pending\n",
+                       adapter->current_op);
                return;
        }
        adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE;
@@ -720,17 +717,16 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
                        }
                        break;
                default:
-                       dev_err(&adapter->pdev->dev,
-                               "%s: Unknown event %d from pf\n",
-                               __func__, vpe->event);
+                       dev_err(&adapter->pdev->dev, "Unknown event %d from PF\n",
+                               vpe->event);
                        break;
                }
                return;
        }
        if (v_retval) {
-               dev_err(&adapter->pdev->dev, "%s: PF returned error %d (%s) to our request %d\n",
-                       __func__, v_retval,
-                       i40evf_stat_str(&adapter->hw, v_retval), v_opcode);
+               dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n",
+                       v_retval, i40evf_stat_str(&adapter->hw, v_retval),
+                       v_opcode);
        }
        switch (v_opcode) {
        case I40E_VIRTCHNL_OP_GET_STATS: {
@@ -756,6 +752,8 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
                          sizeof(struct i40e_virtchnl_vsi_resource);
                memcpy(adapter->vf_res, msg, min(msglen, len));
                i40e_vf_parse_hw_config(&adapter->hw, adapter->vf_res);
+               /* restore current mac address */
+               ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
                i40evf_process_config(adapter);
                }
                break;
index 212d668dabb382160ae04dfce3e6b1375b44832d..1a2f1cc44b2836499dc231be9edb896b8b6ddbe6 100644 (file)
@@ -444,8 +444,8 @@ struct igb_adapter {
 
        struct ptp_pin_desc sdp_config[IGB_N_SDP];
        struct {
-               struct timespec start;
-               struct timespec period;
+               struct timespec64 start;
+               struct timespec64 period;
        } perout[IGB_N_PEROUT];
 
        char fw_version[32];
index 74262768b09b75bd7ad267afb67e720a617bce10..2529bc625de4532e78407500d4c43ba92714f06a 100644 (file)
@@ -842,10 +842,6 @@ static void igb_get_drvinfo(struct net_device *netdev,
                sizeof(drvinfo->fw_version));
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->n_stats = IGB_STATS_LEN;
-       drvinfo->testinfo_len = IGB_TEST_LEN;
-       drvinfo->regdump_len = igb_get_regs_len(netdev);
-       drvinfo->eedump_len = igb_get_eeprom_len(netdev);
 }
 
 static void igb_get_ringparam(struct net_device *netdev,
index e174fbbdba4049d9a335b33498b4866da08c96ac..ea7b098872456e5903bbfd5dead770eba52f69ef 100644 (file)
@@ -151,7 +151,7 @@ static void igb_setup_dca(struct igb_adapter *);
 #endif /* CONFIG_IGB_DCA */
 static int igb_poll(struct napi_struct *, int);
 static bool igb_clean_tx_irq(struct igb_q_vector *);
-static bool igb_clean_rx_irq(struct igb_q_vector *, int);
+static int igb_clean_rx_irq(struct igb_q_vector *, int);
 static int igb_ioctl(struct net_device *, struct ifreq *, int cmd);
 static void igb_tx_timeout(struct net_device *);
 static void igb_reset_task(struct work_struct *);
@@ -2986,6 +2986,9 @@ static int igb_sw_init(struct igb_adapter *adapter)
        }
 #endif /* CONFIG_PCI_IOV */
 
+       /* Assume MSI-X interrupts, will be checked during IRQ allocation */
+       adapter->flags |= IGB_FLAG_HAS_MSIX;
+
        igb_probe_vfs(adapter);
 
        igb_init_queue_configuration(adapter);
@@ -5389,7 +5392,7 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter)
 {
        struct e1000_hw *hw = &adapter->hw;
        struct ptp_clock_event event;
-       struct timespec ts;
+       struct timespec64 ts;
        u32 ack = 0, tsauxc, sec, nsec, tsicr = rd32(E1000_TSICR);
 
        if (tsicr & TSINTR_SYS_WRAP) {
@@ -5409,10 +5412,11 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter)
 
        if (tsicr & TSINTR_TT0) {
                spin_lock(&adapter->tmreg_lock);
-               ts = timespec_add(adapter->perout[0].start,
-                                 adapter->perout[0].period);
+               ts = timespec64_add(adapter->perout[0].start,
+                                   adapter->perout[0].period);
+               /* u32 conversion of tv_sec is safe until y2106 */
                wr32(E1000_TRGTTIML0, ts.tv_nsec);
-               wr32(E1000_TRGTTIMH0, ts.tv_sec);
+               wr32(E1000_TRGTTIMH0, (u32)ts.tv_sec);
                tsauxc = rd32(E1000_TSAUXC);
                tsauxc |= TSAUXC_EN_TT0;
                wr32(E1000_TSAUXC, tsauxc);
@@ -5423,10 +5427,10 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter)
 
        if (tsicr & TSINTR_TT1) {
                spin_lock(&adapter->tmreg_lock);
-               ts = timespec_add(adapter->perout[1].start,
-                                 adapter->perout[1].period);
+               ts = timespec64_add(adapter->perout[1].start,
+                                   adapter->perout[1].period);
                wr32(E1000_TRGTTIML1, ts.tv_nsec);
-               wr32(E1000_TRGTTIMH1, ts.tv_sec);
+               wr32(E1000_TRGTTIMH1, (u32)ts.tv_sec);
                tsauxc = rd32(E1000_TSAUXC);
                tsauxc |= TSAUXC_EN_TT1;
                wr32(E1000_TSAUXC, tsauxc);
@@ -6360,6 +6364,7 @@ static int igb_poll(struct napi_struct *napi, int budget)
                                                     struct igb_q_vector,
                                                     napi);
        bool clean_complete = true;
+       int work_done = 0;
 
 #ifdef CONFIG_IGB_DCA
        if (q_vector->adapter->flags & IGB_FLAG_DCA_ENABLED)
@@ -6368,15 +6373,19 @@ static int igb_poll(struct napi_struct *napi, int budget)
        if (q_vector->tx.ring)
                clean_complete = igb_clean_tx_irq(q_vector);
 
-       if (q_vector->rx.ring)
-               clean_complete &= igb_clean_rx_irq(q_vector, budget);
+       if (q_vector->rx.ring) {
+               int cleaned = igb_clean_rx_irq(q_vector, budget);
+
+               work_done += cleaned;
+               clean_complete &= (cleaned < budget);
+       }
 
        /* If all work not completed, return budget and keep polling */
        if (!clean_complete)
                return budget;
 
        /* If not enough Rx work done, exit the polling mode */
-       napi_complete(napi);
+       napi_complete_done(napi, work_done);
        igb_ring_irq_enable(q_vector);
 
        return 0;
@@ -6900,7 +6909,7 @@ static void igb_process_skb_fields(struct igb_ring *rx_ring,
        skb->protocol = eth_type_trans(skb, rx_ring->netdev);
 }
 
-static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
+static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
 {
        struct igb_ring *rx_ring = q_vector->rx.ring;
        struct sk_buff *skb = rx_ring->skb;
@@ -6974,7 +6983,7 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
        if (cleaned_count)
                igb_alloc_rx_buffers(rx_ring, cleaned_count);
 
-       return total_packets < budget;
+       return total_packets;
 }
 
 static bool igb_alloc_mapped_page(struct igb_ring *rx_ring,
index 5982f28d521a2c116d49ba4cda22d8520c0cb1dc..c44df87c38de233578c531e13ea7cb9ef211ceb7 100644 (file)
@@ -143,7 +143,7 @@ static void igb_ptp_write_i210(struct igb_adapter *adapter,
         * sub-nanosecond resolution.
         */
        wr32(E1000_SYSTIML, ts->tv_nsec);
-       wr32(E1000_SYSTIMH, ts->tv_sec);
+       wr32(E1000_SYSTIMH, (u32)ts->tv_sec);
 }
 
 /**
@@ -479,7 +479,7 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp,
        struct e1000_hw *hw = &igb->hw;
        u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh, freqout;
        unsigned long flags;
-       struct timespec ts;
+       struct timespec64 ts;
        int use_freq = 0, pin = -1;
        s64 ns;
 
@@ -523,14 +523,14 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp,
                }
                ts.tv_sec = rq->perout.period.sec;
                ts.tv_nsec = rq->perout.period.nsec;
-               ns = timespec_to_ns(&ts);
+               ns = timespec64_to_ns(&ts);
                ns = ns >> 1;
                if (on && ns <= 70000000LL) {
                        if (ns < 8LL)
                                return -EINVAL;
                        use_freq = 1;
                }
-               ts = ns_to_timespec(ns);
+               ts = ns_to_timespec64(ns);
                if (rq->perout.index == 1) {
                        if (use_freq) {
                                tsauxc_mask = TSAUXC_EN_CLK1 | TSAUXC_ST1;
index c6996feb1cb4e2559b2b3a260d95a6b0f980b449..b74ce53d7b523e0b8414c21bd20ff9e884ec722f 100644 (file)
@@ -196,8 +196,6 @@ static void igbvf_get_drvinfo(struct net_device *netdev,
                sizeof(drvinfo->version));
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->regdump_len = igbvf_get_regs_len(netdev);
-       drvinfo->eedump_len = igbvf_get_eeprom_len(netdev);
 }
 
 static void igbvf_get_ringparam(struct net_device *netdev,
index 686fa7184179a473599584f25e0a6d714baff54f..297af801f0519136eb1fe0f69a50a746e84c06b8 100644 (file)
@@ -1211,7 +1211,7 @@ static int igbvf_poll(struct napi_struct *napi, int budget)
 
        /* If not enough Rx work done, exit the polling mode */
        if (work_done < budget) {
-               napi_complete(napi);
+               napi_complete_done(napi, work_done);
 
                if (adapter->requested_itr & 3)
                        igbvf_set_itr(adapter);
@@ -2615,6 +2615,7 @@ static const struct net_device_ops igbvf_netdev_ops = {
        .ndo_poll_controller    = igbvf_netpoll,
 #endif
        .ndo_set_features       = igbvf_set_features,
+       .ndo_features_check     = passthru_features_check,
 };
 
 /**
index b311e9e710d2636ddc3a17ad4d1cc10ba917cfb1..d2b29b490ae0167fe8325fd47758a9d6c0ca5a90 100644 (file)
@@ -479,9 +479,6 @@ ixgb_get_drvinfo(struct net_device *netdev,
                sizeof(drvinfo->version));
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->n_stats = IXGB_STATS_LEN;
-       drvinfo->regdump_len = ixgb_get_regs_len(netdev);
-       drvinfo->eedump_len = ixgb_get_eeprom_len(netdev);
 }
 
 static void
index edf1fb91320967f98cdc3ba56a4ad984a48b1063..1d2174526a4c909422ada878ac9dc13c60d7c513 100644 (file)
@@ -152,9 +152,17 @@ struct vf_data_storage {
        u16 vlan_count;
        u8 spoofchk_enabled;
        bool rss_query_enabled;
+       u8 trusted;
+       int xcast_mode;
        unsigned int vf_api;
 };
 
+enum ixgbevf_xcast_modes {
+       IXGBEVF_XCAST_MODE_NONE = 0,
+       IXGBEVF_XCAST_MODE_MULTI,
+       IXGBEVF_XCAST_MODE_ALLMULTI,
+};
+
 struct vf_macvlans {
        struct list_head l;
        int vf;
@@ -539,8 +547,7 @@ struct hwmon_buff {
 #define IXGBE_MIN_RSC_ITR      24
 #define IXGBE_100K_ITR         40
 #define IXGBE_20K_ITR          200
-#define IXGBE_10K_ITR          400
-#define IXGBE_8K_ITR           500
+#define IXGBE_12K_ITR          336
 
 /* ixgbe_test_staterr - tests bits in Rx descriptor status and error fields */
 static inline __le32 ixgbe_test_staterr(union ixgbe_adv_rx_desc *rx_desc,
@@ -595,6 +602,7 @@ struct ixgbe_mac_addr {
 
 /* default to trying for four seconds */
 #define IXGBE_TRY_LINK_TIMEOUT (4 * HZ)
+#define IXGBE_SFP_POLL_JIFFIES (2 * HZ)        /* SFP poll every 2 seconds */
 
 /* board specific private data structure */
 struct ixgbe_adapter {
@@ -708,6 +716,7 @@ struct ixgbe_adapter {
 
        u32 link_speed;
        bool link_up;
+       unsigned long sfp_poll_time;
        unsigned long link_check_timeout;
 
        struct timer_list service_timer;
index dd7062fed61adfff717d56db55d6b275059b7c07..a39afcf03e2c4a84fb6e6aea7960c47a3d589528 100644 (file)
@@ -44,9 +44,8 @@
 static void ixgbe_disable_tx_laser_multispeed_fiber(struct ixgbe_hw *hw);
 static void ixgbe_enable_tx_laser_multispeed_fiber(struct ixgbe_hw *hw);
 static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw);
-static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
-                                                ixgbe_link_speed speed,
-                                                bool autoneg_wait_to_complete);
+static void
+ixgbe_set_hard_rate_select_speed(struct ixgbe_hw *, ixgbe_link_speed);
 static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw,
                                           ixgbe_link_speed speed,
                                           bool autoneg_wait_to_complete);
@@ -109,6 +108,9 @@ static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw)
        if (hw->phy.multispeed_fiber) {
                /* Set up dual speed SFP+ support */
                mac->ops.setup_link = &ixgbe_setup_mac_link_multispeed_fiber;
+               mac->ops.setup_mac_link = ixgbe_setup_mac_link_82599;
+               mac->ops.set_rate_select_speed =
+                                              ixgbe_set_hard_rate_select_speed;
        } else {
                if ((mac->ops.get_media_type(hw) ==
                     ixgbe_media_type_backplane) &&
@@ -646,176 +648,32 @@ static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw)
 }
 
 /**
- *  ixgbe_setup_mac_link_multispeed_fiber - Set MAC link speed
- *  @hw: pointer to hardware structure
- *  @speed: new link speed
- *  @autoneg_wait_to_complete: true when waiting for completion is needed
+ * ixgbe_set_hard_rate_select_speed - Set module link speed
+ * @hw: pointer to hardware structure
+ * @speed: link speed to set
  *
- *  Set the link speed in the AUTOC register and restarts link.
- **/
-static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
-                                         ixgbe_link_speed speed,
-                                         bool autoneg_wait_to_complete)
+ * Set module link speed via RS0/RS1 rate select pins.
+ */
+static void
+ixgbe_set_hard_rate_select_speed(struct ixgbe_hw *hw, ixgbe_link_speed speed)
 {
-       s32 status = 0;
-       ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN;
-       ixgbe_link_speed highest_link_speed = IXGBE_LINK_SPEED_UNKNOWN;
-       u32 speedcnt = 0;
        u32 esdp_reg = IXGBE_READ_REG(hw, IXGBE_ESDP);
-       u32 i = 0;
-       bool link_up = false;
-       bool autoneg = false;
-
-       /* Mask off requested but non-supported speeds */
-       status = hw->mac.ops.get_link_capabilities(hw, &link_speed,
-                                                  &autoneg);
-       if (status != 0)
-               return status;
-
-       speed &= link_speed;
-
-       /*
-        * Try each speed one by one, highest priority first.  We do this in
-        * software because 10gb fiber doesn't support speed autonegotiation.
-        */
-       if (speed & IXGBE_LINK_SPEED_10GB_FULL) {
-               speedcnt++;
-               highest_link_speed = IXGBE_LINK_SPEED_10GB_FULL;
-
-               /* If we already have link at this speed, just jump out */
-               status = hw->mac.ops.check_link(hw, &link_speed, &link_up,
-                                               false);
-               if (status != 0)
-                       return status;
-
-               if ((link_speed == IXGBE_LINK_SPEED_10GB_FULL) && link_up)
-                       goto out;
-
-               /* Set the module link speed */
-               switch (hw->phy.media_type) {
-               case ixgbe_media_type_fiber:
-                       esdp_reg |= (IXGBE_ESDP_SDP5_DIR | IXGBE_ESDP_SDP5);
-                       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
-                       IXGBE_WRITE_FLUSH(hw);
-                       break;
-               case ixgbe_media_type_fiber_qsfp:
-                       /* QSFP module automatically detects MAC link speed */
-                       break;
-               default:
-                       hw_dbg(hw, "Unexpected media type.\n");
-                       break;
-               }
-
-               /* Allow module to change analog characteristics (1G->10G) */
-               msleep(40);
-
-               status = ixgbe_setup_mac_link_82599(hw,
-                                                   IXGBE_LINK_SPEED_10GB_FULL,
-                                                   autoneg_wait_to_complete);
-               if (status != 0)
-                       return status;
-
-               /* Flap the tx laser if it has not already been done */
-               if (hw->mac.ops.flap_tx_laser)
-                       hw->mac.ops.flap_tx_laser(hw);
-
-               /*
-                * Wait for the controller to acquire link.  Per IEEE 802.3ap,
-                * Section 73.10.2, we may have to wait up to 500ms if KR is
-                * attempted.  82599 uses the same timing for 10g SFI.
-                */
-               for (i = 0; i < 5; i++) {
-                       /* Wait for the link partner to also set speed */
-                       msleep(100);
-
-                       /* If we have link, just jump out */
-                       status = hw->mac.ops.check_link(hw, &link_speed,
-                                                       &link_up, false);
-                       if (status != 0)
-                               return status;
-
-                       if (link_up)
-                               goto out;
-               }
-       }
-
-       if (speed & IXGBE_LINK_SPEED_1GB_FULL) {
-               speedcnt++;
-               if (highest_link_speed == IXGBE_LINK_SPEED_UNKNOWN)
-                       highest_link_speed = IXGBE_LINK_SPEED_1GB_FULL;
-
-               /* If we already have link at this speed, just jump out */
-               status = hw->mac.ops.check_link(hw, &link_speed, &link_up,
-                                               false);
-               if (status != 0)
-                       return status;
-
-               if ((link_speed == IXGBE_LINK_SPEED_1GB_FULL) && link_up)
-                       goto out;
-
-               /* Set the module link speed */
-               switch (hw->phy.media_type) {
-               case ixgbe_media_type_fiber:
-                       esdp_reg &= ~IXGBE_ESDP_SDP5;
-                       esdp_reg |= IXGBE_ESDP_SDP5_DIR;
-                       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
-                       IXGBE_WRITE_FLUSH(hw);
-                       break;
-               case ixgbe_media_type_fiber_qsfp:
-                       /* QSFP module automatically detects MAC link speed */
-                       break;
-               default:
-                       hw_dbg(hw, "Unexpected media type.\n");
-                       break;
-               }
-
-               /* Allow module to change analog characteristics (10G->1G) */
-               msleep(40);
-
-               status = ixgbe_setup_mac_link_82599(hw,
-                                                   IXGBE_LINK_SPEED_1GB_FULL,
-                                                   autoneg_wait_to_complete);
-               if (status != 0)
-                       return status;
-
-               /* Flap the tx laser if it has not already been done */
-               if (hw->mac.ops.flap_tx_laser)
-                       hw->mac.ops.flap_tx_laser(hw);
 
-               /* Wait for the link partner to also set speed */
-               msleep(100);
-
-               /* If we have link, just jump out */
-               status = hw->mac.ops.check_link(hw, &link_speed, &link_up,
-                                               false);
-               if (status != 0)
-                       return status;
-
-               if (link_up)
-                       goto out;
+       switch (speed) {
+       case IXGBE_LINK_SPEED_10GB_FULL:
+               esdp_reg |= (IXGBE_ESDP_SDP5_DIR | IXGBE_ESDP_SDP5);
+               break;
+       case IXGBE_LINK_SPEED_1GB_FULL:
+               esdp_reg &= ~IXGBE_ESDP_SDP5;
+               esdp_reg |= IXGBE_ESDP_SDP5_DIR;
+               break;
+       default:
+               hw_dbg(hw, "Invalid fixed module speed\n");
+               return;
        }
 
-       /*
-        * We didn't get link.  Configure back to the highest speed we tried,
-        * (if there was more than one).  We call ourselves back with just the
-        * single highest speed that the user requested.
-        */
-       if (speedcnt > 1)
-               status = ixgbe_setup_mac_link_multispeed_fiber(hw,
-                                                              highest_link_speed,
-                                                              autoneg_wait_to_complete);
-
-out:
-       /* Set autoneg_advertised value based on input link speed */
-       hw->phy.autoneg_advertised = 0;
-
-       if (speed & IXGBE_LINK_SPEED_10GB_FULL)
-               hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL;
-
-       if (speed & IXGBE_LINK_SPEED_1GB_FULL)
-               hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL;
-
-       return status;
+       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
+       IXGBE_WRITE_FLUSH(hw);
 }
 
 /**
@@ -1766,6 +1624,16 @@ s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw,
        IXGBE_WRITE_REG(hw, IXGBE_FDIRTCPM, ~fdirtcpm);
        IXGBE_WRITE_REG(hw, IXGBE_FDIRUDPM, ~fdirtcpm);
 
+       /* also use it for SCTP */
+       switch (hw->mac.type) {
+       case ixgbe_mac_X550:
+       case ixgbe_mac_X550EM_x:
+               IXGBE_WRITE_REG(hw, IXGBE_FDIRSCTPM, ~fdirtcpm);
+               break;
+       default:
+               break;
+       }
+
        /* store source and destination IP masks (big-enian) */
        IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRSIP4M,
                             ~input_mask->formatted.src_ip[0]);
index 3f56a8080118ef0737e00e57ebc0e43deb4dc77a..ce61b36b94f10102644d3304359a03d208d60e3f 100644 (file)
@@ -297,13 +297,13 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw)
 
        /* Setup flow control */
        ret_val = ixgbe_setup_fc(hw);
-       if (!ret_val)
-               return 0;
+       if (ret_val)
+               return ret_val;
 
        /* Clear adapter stopped flag */
        hw->adapter_stopped = false;
 
-       return ret_val;
+       return 0;
 }
 
 /**
@@ -2164,10 +2164,11 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw)
                        /*
                         * In order to prevent Tx hangs when the internal Tx
                         * switch is enabled we must set the high water mark
-                        * to the maximum FCRTH value.  This allows the Tx
-                        * switch to function even under heavy Rx workloads.
+                        * to the Rx packet buffer size - 24KB.  This allows
+                        * the Tx switch to function even under heavy Rx
+                        * workloads.
                         */
-                       fcrth = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 32;
+                       fcrth = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 24576;
                }
 
                IXGBE_WRITE_REG(hw, IXGBE_FCRTH_82599(i), fcrth);
@@ -2476,6 +2477,9 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw)
        hw_dbg(hw, "GIO Master Disable bit didn't clear - requesting resets\n");
        hw->mac.flags |= IXGBE_FLAGS_DOUBLE_RESET_REQUIRED;
 
+       if (hw->mac.type >= ixgbe_mac_X550)
+               return 0;
+
        /*
         * Before proceeding, make sure that the PCIe block does not have
         * transactions pending.
@@ -3920,3 +3924,213 @@ bool ixgbe_mng_present(struct ixgbe_hw *hw)
        fwsm &= IXGBE_FWSM_MODE_MASK;
        return fwsm == IXGBE_FWSM_FW_MODE_PT;
 }
+
+/**
+ *  ixgbe_setup_mac_link_multispeed_fiber - Set MAC link speed
+ *  @hw: pointer to hardware structure
+ *  @speed: new link speed
+ *  @autoneg_wait_to_complete: true when waiting for completion is needed
+ *
+ *  Set the link speed in the MAC and/or PHY register and restarts link.
+ */
+s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
+                                         ixgbe_link_speed speed,
+                                         bool autoneg_wait_to_complete)
+{
+       ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN;
+       ixgbe_link_speed highest_link_speed = IXGBE_LINK_SPEED_UNKNOWN;
+       s32 status = 0;
+       u32 speedcnt = 0;
+       u32 i = 0;
+       bool autoneg, link_up = false;
+
+       /* Mask off requested but non-supported speeds */
+       status = hw->mac.ops.get_link_capabilities(hw, &link_speed, &autoneg);
+       if (status)
+               return status;
+
+       speed &= link_speed;
+
+       /* Try each speed one by one, highest priority first.  We do this in
+        * software because 10Gb fiber doesn't support speed autonegotiation.
+        */
+       if (speed & IXGBE_LINK_SPEED_10GB_FULL) {
+               speedcnt++;
+               highest_link_speed = IXGBE_LINK_SPEED_10GB_FULL;
+
+               /* If we already have link at this speed, just jump out */
+               status = hw->mac.ops.check_link(hw, &link_speed, &link_up,
+                                               false);
+               if (status)
+                       return status;
+
+               if (link_speed == IXGBE_LINK_SPEED_10GB_FULL && link_up)
+                       goto out;
+
+               /* Set the module link speed */
+               switch (hw->phy.media_type) {
+               case ixgbe_media_type_fiber:
+                       hw->mac.ops.set_rate_select_speed(hw,
+                                                   IXGBE_LINK_SPEED_10GB_FULL);
+                       break;
+               case ixgbe_media_type_fiber_qsfp:
+                       /* QSFP module automatically detects MAC link speed */
+                       break;
+               default:
+                       hw_dbg(hw, "Unexpected media type\n");
+                       break;
+               }
+
+               /* Allow module to change analog characteristics (1G->10G) */
+               msleep(40);
+
+               status = hw->mac.ops.setup_mac_link(hw,
+                                                   IXGBE_LINK_SPEED_10GB_FULL,
+                                                   autoneg_wait_to_complete);
+               if (status)
+                       return status;
+
+               /* Flap the Tx laser if it has not already been done */
+               if (hw->mac.ops.flap_tx_laser)
+                       hw->mac.ops.flap_tx_laser(hw);
+
+               /* Wait for the controller to acquire link.  Per IEEE 802.3ap,
+                * Section 73.10.2, we may have to wait up to 500ms if KR is
+                * attempted.  82599 uses the same timing for 10g SFI.
+                */
+               for (i = 0; i < 5; i++) {
+                       /* Wait for the link partner to also set speed */
+                       msleep(100);
+
+                       /* If we have link, just jump out */
+                       status = hw->mac.ops.check_link(hw, &link_speed,
+                                                       &link_up, false);
+                       if (status)
+                               return status;
+
+                       if (link_up)
+                               goto out;
+               }
+       }
+
+       if (speed & IXGBE_LINK_SPEED_1GB_FULL) {
+               speedcnt++;
+               if (highest_link_speed == IXGBE_LINK_SPEED_UNKNOWN)
+                       highest_link_speed = IXGBE_LINK_SPEED_1GB_FULL;
+
+               /* If we already have link at this speed, just jump out */
+               status = hw->mac.ops.check_link(hw, &link_speed, &link_up,
+                                               false);
+               if (status)
+                       return status;
+
+               if (link_speed == IXGBE_LINK_SPEED_1GB_FULL && link_up)
+                       goto out;
+
+               /* Set the module link speed */
+               switch (hw->phy.media_type) {
+               case ixgbe_media_type_fiber:
+                       hw->mac.ops.set_rate_select_speed(hw,
+                                                    IXGBE_LINK_SPEED_1GB_FULL);
+                       break;
+               case ixgbe_media_type_fiber_qsfp:
+                       /* QSFP module automatically detects link speed */
+                       break;
+               default:
+                       hw_dbg(hw, "Unexpected media type\n");
+                       break;
+               }
+
+               /* Allow module to change analog characteristics (10G->1G) */
+               msleep(40);
+
+               status = hw->mac.ops.setup_mac_link(hw,
+                                                   IXGBE_LINK_SPEED_1GB_FULL,
+                                                   autoneg_wait_to_complete);
+               if (status)
+                       return status;
+
+               /* Flap the Tx laser if it has not already been done */
+               if (hw->mac.ops.flap_tx_laser)
+                       hw->mac.ops.flap_tx_laser(hw);
+
+               /* Wait for the link partner to also set speed */
+               msleep(100);
+
+               /* If we have link, just jump out */
+               status = hw->mac.ops.check_link(hw, &link_speed, &link_up,
+                                               false);
+               if (status)
+                       return status;
+
+               if (link_up)
+                       goto out;
+       }
+
+       /* We didn't get link.  Configure back to the highest speed we tried,
+        * (if there was more than one).  We call ourselves back with just the
+        * single highest speed that the user requested.
+        */
+       if (speedcnt > 1)
+               status = ixgbe_setup_mac_link_multispeed_fiber(hw,
+                                                     highest_link_speed,
+                                                     autoneg_wait_to_complete);
+
+out:
+       /* Set autoneg_advertised value based on input link speed */
+       hw->phy.autoneg_advertised = 0;
+
+       if (speed & IXGBE_LINK_SPEED_10GB_FULL)
+               hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL;
+
+       if (speed & IXGBE_LINK_SPEED_1GB_FULL)
+               hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL;
+
+       return status;
+}
+
+/**
+ *  ixgbe_set_soft_rate_select_speed - Set module link speed
+ *  @hw: pointer to hardware structure
+ *  @speed: link speed to set
+ *
+ *  Set module link speed via the soft rate select.
+ */
+void ixgbe_set_soft_rate_select_speed(struct ixgbe_hw *hw,
+                                     ixgbe_link_speed speed)
+{
+       s32 status;
+       u8 rs, eeprom_data;
+
+       switch (speed) {
+       case IXGBE_LINK_SPEED_10GB_FULL:
+               /* one bit mask same as setting on */
+               rs = IXGBE_SFF_SOFT_RS_SELECT_10G;
+               break;
+       case IXGBE_LINK_SPEED_1GB_FULL:
+               rs = IXGBE_SFF_SOFT_RS_SELECT_1G;
+               break;
+       default:
+               hw_dbg(hw, "Invalid fixed module speed\n");
+               return;
+       }
+
+       /* Set RS0 */
+       status = hw->phy.ops.read_i2c_byte(hw, IXGBE_SFF_SFF_8472_OSCB,
+                                          IXGBE_I2C_EEPROM_DEV_ADDR2,
+                                          &eeprom_data);
+       if (status) {
+               hw_dbg(hw, "Failed to read Rx Rate Select RS0\n");
+               return;
+       }
+
+       eeprom_data = (eeprom_data & ~IXGBE_SFF_SOFT_RS_SELECT_MASK) | rs;
+
+       status = hw->phy.ops.write_i2c_byte(hw, IXGBE_SFF_SFF_8472_OSCB,
+                                           IXGBE_I2C_EEPROM_DEV_ADDR2,
+                                           eeprom_data);
+       if (status) {
+               hw_dbg(hw, "Failed to write Rx Rate Select RS0\n");
+               return;
+       }
+}
index 2f779f35dc4f5c6b7034c4263a79a03252b51aeb..a0044e4a8b90c92c49c08598423743fec070850c 100644 (file)
@@ -135,6 +135,11 @@ s32 ixgbe_get_thermal_sensor_data_generic(struct ixgbe_hw *hw);
 s32 ixgbe_init_thermal_sensor_thresh_generic(struct ixgbe_hw *hw);
 void ixgbe_disable_rx_generic(struct ixgbe_hw *hw);
 void ixgbe_enable_rx_generic(struct ixgbe_hw *hw);
+s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
+                                         ixgbe_link_speed speed,
+                                         bool autoneg_wait_to_complete);
+void ixgbe_set_soft_rate_select_speed(struct ixgbe_hw *hw,
+                                     ixgbe_link_speed speed);
 
 #define IXGBE_FAILED_READ_REG 0xffffffffU
 #define IXGBE_FAILED_READ_CFG_DWORD 0xffffffffU
index 3b932fe64ab66c916f86f4184f45d626cc687cb1..23277ab153b6b177f234e23a8895bcf90a9a6f5d 100644 (file)
@@ -259,7 +259,13 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc)
                        fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE;
                        IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), fcrtl);
                } else {
-                       reg = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 32;
+                       /* In order to prevent Tx hangs when the internal Tx
+                        * switch is enabled we must set the high water mark
+                        * to the Rx packet buffer size - 24KB.  This allows
+                        * the Tx switch to function even under heavy Rx
+                        * workloads.
+                        */
+                       reg = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 24576;
                        IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), 0);
                }
 
index ab2edc8e7703f014e0dc09f8d91de5dee1054083..d681273bd39d52ee060c14b1c6c17d7f5e427dfb 100644 (file)
@@ -943,9 +943,6 @@ static void ixgbe_get_drvinfo(struct net_device *netdev,
 
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->n_stats = IXGBE_STATS_LEN;
-       drvinfo->testinfo_len = IXGBE_TEST_LEN;
-       drvinfo->regdump_len = ixgbe_get_regs_len(netdev);
 }
 
 static void ixgbe_get_ringparam(struct net_device *netdev,
@@ -2286,7 +2283,7 @@ static int ixgbe_set_coalesce(struct net_device *netdev,
                adapter->tx_itr_setting = ec->tx_coalesce_usecs;
 
        if (adapter->tx_itr_setting == 1)
-               tx_itr_param = IXGBE_10K_ITR;
+               tx_itr_param = IXGBE_12K_ITR;
        else
                tx_itr_param = adapter->tx_itr_setting;
 
index 68e1e757ecefce38e456c5cf2ae017ddea2ed054..f3168bcc7d87905b2f3563ea64c0fbfeda01d1d4 100644 (file)
@@ -866,7 +866,7 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter,
        if (txr_count && !rxr_count) {
                /* tx only vector */
                if (adapter->tx_itr_setting == 1)
-                       q_vector->itr = IXGBE_10K_ITR;
+                       q_vector->itr = IXGBE_12K_ITR;
                else
                        q_vector->itr = adapter->tx_itr_setting;
        } else {
index 63b2cfe9416b22eaef239ea854852daa6ddcce03..47395ff5d908c43174a4c6afc4a8a089ae948c70 100644 (file)
@@ -79,7 +79,7 @@ char ixgbe_default_device_descr[] =
 static char ixgbe_default_device_descr[] =
                              "Intel(R) 10 Gigabit Network Connection";
 #endif
-#define DRV_VERSION "4.0.1-k"
+#define DRV_VERSION "4.2.1-k"
 const char ixgbe_driver_version[] = DRV_VERSION;
 static const char ixgbe_copyright[] =
                                "Copyright (c) 1999-2015 Intel Corporation.";
@@ -137,6 +137,7 @@ static const struct pci_device_id ixgbe_pci_tbl[] = {
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KX4), board_X550EM_x},
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KR), board_X550EM_x},
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_10G_T), board_X550EM_x},
+       {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_SFP), board_X550EM_x},
        /* required last entry */
        {0, }
 };
@@ -1244,9 +1245,12 @@ static void ixgbe_update_tx_dca(struct ixgbe_adapter *adapter,
                                int cpu)
 {
        struct ixgbe_hw *hw = &adapter->hw;
-       u32 txctrl = dca3_get_tag(tx_ring->dev, cpu);
+       u32 txctrl = 0;
        u16 reg_offset;
 
+       if (adapter->flags & IXGBE_FLAG_DCA_ENABLED)
+               txctrl = dca3_get_tag(tx_ring->dev, cpu);
+
        switch (hw->mac.type) {
        case ixgbe_mac_82598EB:
                reg_offset = IXGBE_DCA_TXCTRL(tx_ring->reg_idx);
@@ -1278,9 +1282,11 @@ static void ixgbe_update_rx_dca(struct ixgbe_adapter *adapter,
                                int cpu)
 {
        struct ixgbe_hw *hw = &adapter->hw;
-       u32 rxctrl = dca3_get_tag(rx_ring->dev, cpu);
+       u32 rxctrl = 0;
        u8 reg_idx = rx_ring->reg_idx;
 
+       if (adapter->flags & IXGBE_FLAG_DCA_ENABLED)
+               rxctrl = dca3_get_tag(rx_ring->dev, cpu);
 
        switch (hw->mac.type) {
        case ixgbe_mac_82599EB:
@@ -1297,6 +1303,7 @@ static void ixgbe_update_rx_dca(struct ixgbe_adapter *adapter,
         * which will cause the DCA tag to be cleared.
         */
        rxctrl |= IXGBE_DCA_RXCTRL_DESC_RRO_EN |
+                 IXGBE_DCA_RXCTRL_DATA_DCA_EN |
                  IXGBE_DCA_RXCTRL_DESC_DCA_EN;
 
        IXGBE_WRITE_REG(hw, IXGBE_DCA_RXCTRL(reg_idx), rxctrl);
@@ -1326,11 +1333,13 @@ static void ixgbe_setup_dca(struct ixgbe_adapter *adapter)
 {
        int i;
 
-       if (!(adapter->flags & IXGBE_FLAG_DCA_ENABLED))
-               return;
-
        /* always use CB2 mode, difference is masked in the CB driver */
-       IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, 2);
+       if (adapter->flags & IXGBE_FLAG_DCA_ENABLED)
+               IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL,
+                               IXGBE_DCA_CTRL_DCA_MODE_CB2);
+       else
+               IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL,
+                               IXGBE_DCA_CTRL_DCA_DISABLE);
 
        for (i = 0; i < adapter->num_q_vectors; i++) {
                adapter->q_vector[i]->cpu = -1;
@@ -1353,7 +1362,8 @@ static int __ixgbe_notify_dca(struct device *dev, void *data)
                        break;
                if (dca_add_requester(dev) == 0) {
                        adapter->flags |= IXGBE_FLAG_DCA_ENABLED;
-                       ixgbe_setup_dca(adapter);
+                       IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL,
+                                       IXGBE_DCA_CTRL_DCA_MODE_CB2);
                        break;
                }
                /* Fall Through since DCA is disabled. */
@@ -1361,7 +1371,8 @@ static int __ixgbe_notify_dca(struct device *dev, void *data)
                if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) {
                        dca_remove_requester(dev);
                        adapter->flags &= ~IXGBE_FLAG_DCA_ENABLED;
-                       IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, 1);
+                       IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL,
+                                       IXGBE_DCA_CTRL_DCA_DISABLE);
                }
                break;
        }
@@ -2261,7 +2272,7 @@ static void ixgbe_update_itr(struct ixgbe_q_vector *q_vector,
        /* simple throttlerate management
         *   0-10MB/s   lowest (100000 ints/s)
         *  10-20MB/s   low    (20000 ints/s)
-        *  20-1249MB/s bulk   (8000 ints/s)
+        *  20-1249MB/s bulk   (12000 ints/s)
         */
        /* what was last interrupt timeslice? */
        timepassed_us = q_vector->itr >> 2;
@@ -2350,7 +2361,7 @@ static void ixgbe_set_itr(struct ixgbe_q_vector *q_vector)
                new_itr = IXGBE_20K_ITR;
                break;
        case bulk_latency:
-               new_itr = IXGBE_8K_ITR;
+               new_itr = IXGBE_12K_ITR;
                break;
        default:
                break;
@@ -2495,17 +2506,27 @@ static inline bool ixgbe_is_sfp(struct ixgbe_hw *hw)
 static void ixgbe_check_sfp_event(struct ixgbe_adapter *adapter, u32 eicr)
 {
        struct ixgbe_hw *hw = &adapter->hw;
+       u32 eicr_mask = IXGBE_EICR_GPI_SDP2(hw);
+
+       if (!ixgbe_is_sfp(hw))
+               return;
 
-       if (eicr & IXGBE_EICR_GPI_SDP2(hw)) {
+       /* Later MAC's use different SDP */
+       if (hw->mac.type >= ixgbe_mac_X540)
+               eicr_mask = IXGBE_EICR_GPI_SDP0_X540;
+
+       if (eicr & eicr_mask) {
                /* Clear the interrupt */
-               IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP2(hw));
+               IXGBE_WRITE_REG(hw, IXGBE_EICR, eicr_mask);
                if (!test_bit(__IXGBE_DOWN, &adapter->state)) {
                        adapter->flags2 |= IXGBE_FLAG2_SFP_NEEDS_RESET;
+                       adapter->sfp_poll_time = 0;
                        ixgbe_service_event_schedule(adapter);
                }
        }
 
-       if (eicr & IXGBE_EICR_GPI_SDP1(hw)) {
+       if (adapter->hw.mac.type == ixgbe_mac_82599EB &&
+           (eicr & IXGBE_EICR_GPI_SDP1(hw))) {
                /* Clear the interrupt */
                IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1(hw));
                if (!test_bit(__IXGBE_DOWN, &adapter->state)) {
@@ -2622,6 +2643,8 @@ static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter, bool queues,
        case ixgbe_mac_X540:
        case ixgbe_mac_X550:
        case ixgbe_mac_X550EM_x:
+               if (adapter->hw.device_id == IXGBE_DEV_ID_X550EM_X_SFP)
+                       mask |= IXGBE_EIMS_GPI_SDP0(&adapter->hw);
                if (adapter->hw.phy.type == ixgbe_phy_x550em_ext_t)
                        mask |= IXGBE_EICR_GPI_SDP0_X540;
                mask |= IXGBE_EIMS_ECC;
@@ -2752,7 +2775,7 @@ int ixgbe_poll(struct napi_struct *napi, int budget)
                                container_of(napi, struct ixgbe_q_vector, napi);
        struct ixgbe_adapter *adapter = q_vector->adapter;
        struct ixgbe_ring *ring;
-       int per_ring_budget;
+       int per_ring_budget, work_done = 0;
        bool clean_complete = true;
 
 #ifdef CONFIG_IXGBE_DCA
@@ -2773,9 +2796,13 @@ int ixgbe_poll(struct napi_struct *napi, int budget)
        else
                per_ring_budget = budget;
 
-       ixgbe_for_each_ring(ring, q_vector->rx)
-               clean_complete &= (ixgbe_clean_rx_irq(q_vector, ring,
-                                  per_ring_budget) < per_ring_budget);
+       ixgbe_for_each_ring(ring, q_vector->rx) {
+               int cleaned = ixgbe_clean_rx_irq(q_vector, ring,
+                                                per_ring_budget);
+
+               work_done += cleaned;
+               clean_complete &= (cleaned < per_ring_budget);
+       }
 
        ixgbe_qv_unlock_napi(q_vector);
        /* If all work not completed, return budget and keep polling */
@@ -2783,7 +2810,7 @@ int ixgbe_poll(struct napi_struct *napi, int budget)
                return budget;
 
        /* all work done, exit the polling mode */
-       napi_complete(napi);
+       napi_complete_done(napi, work_done);
        if (adapter->rx_itr_setting & 1)
                ixgbe_set_itr(q_vector);
        if (!test_bit(__IXGBE_DOWN, &adapter->state))
@@ -3700,14 +3727,20 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter)
        hw->mac.ops.set_mac_anti_spoofing(hw, (adapter->num_vfs != 0),
                                          adapter->num_vfs);
 
-       /* Ensure LLDP is set for Ethertype Antispoofing if we will be
+       /* Ensure LLDP and FC is set for Ethertype Antispoofing if we will be
         * calling set_ethertype_anti_spoofing for each VF in loop below
         */
-       if (hw->mac.ops.set_ethertype_anti_spoofing)
+       if (hw->mac.ops.set_ethertype_anti_spoofing) {
                IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_LLDP),
-                               (IXGBE_ETQF_FILTER_EN    | /* enable filter */
-                                IXGBE_ETQF_TX_ANTISPOOF | /* tx antispoof */
-                                IXGBE_ETH_P_LLDP));       /* LLDP eth type */
+                               (IXGBE_ETQF_FILTER_EN    |
+                                IXGBE_ETQF_TX_ANTISPOOF |
+                                IXGBE_ETH_P_LLDP));
+
+               IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_FC),
+                               (IXGBE_ETQF_FILTER_EN |
+                                IXGBE_ETQF_TX_ANTISPOOF |
+                                ETH_P_PAUSE));
+       }
 
        /* For VFs that have spoof checking turned off */
        for (i = 0; i < adapter->num_vfs; i++) {
@@ -3777,8 +3810,6 @@ static void ixgbe_setup_rdrxctl(struct ixgbe_adapter *adapter)
        u32 rdrxctl = IXGBE_READ_REG(hw, IXGBE_RDRXCTL);
 
        switch (hw->mac.type) {
-       case ixgbe_mac_X550:
-       case ixgbe_mac_X550EM_x:
        case ixgbe_mac_82598EB:
                /*
                 * For VMDq support of different descriptor types or
@@ -3792,6 +3823,11 @@ static void ixgbe_setup_rdrxctl(struct ixgbe_adapter *adapter)
                 */
                rdrxctl |= IXGBE_RDRXCTL_MVMEN;
                break;
+       case ixgbe_mac_X550:
+       case ixgbe_mac_X550EM_x:
+               if (adapter->num_vfs)
+                       rdrxctl |= IXGBE_RDRXCTL_PSP;
+               /* fall through for older HW */
        case ixgbe_mac_82599EB:
        case ixgbe_mac_X540:
                /* Disable RSC for ACK packets */
@@ -4767,6 +4803,12 @@ static void ixgbe_configure(struct ixgbe_adapter *adapter)
                break;
        }
 
+#ifdef CONFIG_IXGBE_DCA
+       /* configure DCA */
+       if (adapter->flags & IXGBE_FLAG_DCA_CAPABLE)
+               ixgbe_setup_dca(adapter);
+#endif /* CONFIG_IXGBE_DCA */
+
 #ifdef IXGBE_FCOE
        /* configure FCoE L2 filters, redirection table, and Rx control */
        ixgbe_configure_fcoe(adapter);
@@ -4793,6 +4835,7 @@ static void ixgbe_sfp_link_config(struct ixgbe_adapter *adapter)
                adapter->flags2 |= IXGBE_FLAG2_SEARCH_FOR_SFP;
 
        adapter->flags2 |= IXGBE_FLAG2_SFP_NEEDS_RESET;
+       adapter->sfp_poll_time = 0;
 }
 
 /**
@@ -4883,9 +4926,6 @@ static void ixgbe_setup_gpie(struct ixgbe_adapter *adapter)
                case ixgbe_mac_82599EB:
                        gpie |= IXGBE_SDP0_GPIEN_8259X;
                        break;
-               case ixgbe_mac_X540:
-                       gpie |= IXGBE_EIMS_TS;
-                       break;
                default:
                        break;
                }
@@ -4895,9 +4935,15 @@ static void ixgbe_setup_gpie(struct ixgbe_adapter *adapter)
        if (adapter->flags & IXGBE_FLAG_FAN_FAIL_CAPABLE)
                gpie |= IXGBE_SDP1_GPIEN(hw);
 
-       if (hw->mac.type == ixgbe_mac_82599EB) {
-               gpie |= IXGBE_SDP1_GPIEN_8259X;
-               gpie |= IXGBE_SDP2_GPIEN_8259X;
+       switch (hw->mac.type) {
+       case ixgbe_mac_82599EB:
+               gpie |= IXGBE_SDP1_GPIEN_8259X | IXGBE_SDP2_GPIEN_8259X;
+               break;
+       case ixgbe_mac_X550EM_x:
+               gpie |= IXGBE_SDP0_GPIEN_X540;
+               break;
+       default:
+               break;
        }
 
        IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie);
@@ -5220,11 +5266,6 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
 
        ixgbe_clean_all_tx_rings(adapter);
        ixgbe_clean_all_rx_rings(adapter);
-
-#ifdef CONFIG_IXGBE_DCA
-       /* since we reset the hardware DCA settings were cleared */
-       ixgbe_setup_dca(adapter);
-#endif
 }
 
 /**
@@ -5270,7 +5311,6 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter)
        rss = min_t(int, ixgbe_max_rss_indices(adapter), num_online_cpus());
        adapter->ring_feature[RING_F_RSS].limit = rss;
        adapter->flags2 |= IXGBE_FLAG2_RSC_CAPABLE;
-       adapter->flags2 |= IXGBE_FLAG2_RSC_ENABLED;
        adapter->max_q_vectors = MAX_Q_VECTORS_82599;
        adapter->atr_sample_rate = 20;
        fdir = min_t(int, IXGBE_MAX_FDIR_INDICES, num_online_cpus());
@@ -5296,7 +5336,6 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter)
        switch (hw->mac.type) {
        case ixgbe_mac_82598EB:
                adapter->flags2 &= ~IXGBE_FLAG2_RSC_CAPABLE;
-               adapter->flags2 &= ~IXGBE_FLAG2_RSC_ENABLED;
 
                if (hw->device_id == IXGBE_DEV_ID_82598AT)
                        adapter->flags |= IXGBE_FLAG_FAN_FAIL_CAPABLE;
@@ -6692,10 +6731,16 @@ static void ixgbe_sfp_detection_subtask(struct ixgbe_adapter *adapter)
            !(adapter->flags2 & IXGBE_FLAG2_SFP_NEEDS_RESET))
                return;
 
+       if (adapter->sfp_poll_time &&
+           time_after(adapter->sfp_poll_time, jiffies))
+               return; /* If not yet time to poll for SFP */
+
        /* someone else is in init, wait until next service event */
        if (test_and_set_bit(__IXGBE_IN_SFP_INIT, &adapter->state))
                return;
 
+       adapter->sfp_poll_time = jiffies + IXGBE_SFP_POLL_JIFFIES - 1;
+
        err = hw->phy.ops.identify_sfp(hw);
        if (err == IXGBE_ERR_SFP_NOT_SUPPORTED)
                goto sfp_out;
@@ -8362,6 +8407,7 @@ static const struct net_device_ops ixgbe_netdev_ops = {
        .ndo_set_vf_rate        = ixgbe_ndo_set_vf_bw,
        .ndo_set_vf_spoofchk    = ixgbe_ndo_set_vf_spoofchk,
        .ndo_set_vf_rss_query_en = ixgbe_ndo_set_vf_rss_query_en,
+       .ndo_set_vf_trust       = ixgbe_ndo_set_vf_trust,
        .ndo_get_vf_config      = ixgbe_ndo_get_vf_config,
        .ndo_get_stats64        = ixgbe_get_stats64,
 #ifdef CONFIG_IXGBE_DCB
@@ -8695,8 +8741,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        hw->phy.reset_if_overtemp = true;
        err = hw->mac.ops.reset_hw(hw);
        hw->phy.reset_if_overtemp = false;
-       if (err == IXGBE_ERR_SFP_NOT_PRESENT &&
-           hw->mac.type == ixgbe_mac_82598EB) {
+       if (err == IXGBE_ERR_SFP_NOT_PRESENT) {
                err = 0;
        } else if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) {
                e_dev_err("failed to load because an unsupported SFP+ or QSFP module type was detected.\n");
@@ -9008,7 +9053,8 @@ static void ixgbe_remove(struct pci_dev *pdev)
        if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) {
                adapter->flags &= ~IXGBE_FLAG_DCA_ENABLED;
                dca_remove_requester(&pdev->dev);
-               IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, 1);
+               IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL,
+                               IXGBE_DCA_CTRL_DCA_DISABLE);
        }
 
 #endif
@@ -9019,12 +9065,12 @@ static void ixgbe_remove(struct pci_dev *pdev)
        /* remove the added san mac */
        ixgbe_del_sanmac_netdev(netdev);
 
-       if (netdev->reg_state == NETREG_REGISTERED)
-               unregister_netdev(netdev);
-
 #ifdef CONFIG_PCI_IOV
        ixgbe_disable_sriov(adapter);
 #endif
+       if (netdev->reg_state == NETREG_REGISTERED)
+               unregister_netdev(netdev);
+
        ixgbe_clear_interrupt_scheme(adapter);
 
        ixgbe_release_hw_control(adapter);
index b1e4703ff2a5949fc4c35db6c0c1cd1dceea6675..8daa95f74548857fbb44f9e05c724b9c09052db3 100644 (file)
@@ -102,6 +102,8 @@ enum ixgbe_pfvf_api_rev {
 #define IXGBE_VF_GET_RETA      0x0a    /* VF request for RETA */
 #define IXGBE_VF_GET_RSS_KEY   0x0b    /* get RSS key */
 
+#define IXGBE_VF_UPDATE_XCAST_MODE     0x0c
+
 /* length of permanent address message returned from PF */
 #define IXGBE_VF_PERMADDR_MSG_LEN 4
 /* word in permanent address message with the current multicast type */
index 597d0b1c23701eadad2c3d9a68448acc7d4c941c..fb8673d6380689fa7a79ebba5d5d493777d1bdc2 100644 (file)
@@ -100,16 +100,17 @@ static u8 ixgbe_ones_comp_byte_add(u8 add1, u8 add2)
 }
 
 /**
- *  ixgbe_read_i2c_combined_generic - Perform I2C read combined operation
+ *  ixgbe_read_i2c_combined_generic_int - Perform I2C read combined operation
  *  @hw: pointer to the hardware structure
  *  @addr: I2C bus address to read from
  *  @reg: I2C device register to read from
  *  @val: pointer to location to receive read value
+ *  @lock: true if to take and release semaphore
  *
  *  Returns an error code on error.
- **/
-s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr,
-                                   u16 reg, u16 *val)
+ */
+static s32 ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr,
+                                              u16 reg, u16 *val, bool lock)
 {
        u32 swfw_mask = hw->phy.phy_semaphore_mask;
        int max_retry = 10;
@@ -124,7 +125,7 @@ s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr,
        csum = ixgbe_ones_comp_byte_add(reg_high, reg & 0xFF);
        csum = ~csum;
        do {
-               if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
+               if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
                        return IXGBE_ERR_SWFW_SYNC;
                ixgbe_i2c_start(hw);
                /* Device Address and write indication */
@@ -157,13 +158,15 @@ s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr,
                if (ixgbe_clock_out_i2c_bit(hw, false))
                        goto fail;
                ixgbe_i2c_stop(hw);
-               hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+               if (lock)
+                       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
                *val = (high_bits << 8) | low_bits;
                return 0;
 
 fail:
                ixgbe_i2c_bus_clear(hw);
-               hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+               if (lock)
+                       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
                retry++;
                if (retry < max_retry)
                        hw_dbg(hw, "I2C byte read combined error - Retry.\n");
@@ -175,17 +178,49 @@ fail:
 }
 
 /**
- *  ixgbe_write_i2c_combined_generic - Perform I2C write combined operation
+ *  ixgbe_read_i2c_combined_generic - Perform I2C read combined operation
+ *  @hw: pointer to the hardware structure
+ *  @addr: I2C bus address to read from
+ *  @reg: I2C device register to read from
+ *  @val: pointer to location to receive read value
+ *
+ *  Returns an error code on error.
+ */
+s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr,
+                                   u16 reg, u16 *val)
+{
+       return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, true);
+}
+
+/**
+ *  ixgbe_read_i2c_combined_generic_unlocked - Unlocked I2C read combined
+ *  @hw: pointer to the hardware structure
+ *  @addr: I2C bus address to read from
+ *  @reg: I2C device register to read from
+ *  @val: pointer to location to receive read value
+ *
+ *  Returns an error code on error.
+ */
+s32 ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr,
+                                            u16 reg, u16 *val)
+{
+       return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, false);
+}
+
+/**
+ *  ixgbe_write_i2c_combined_generic_int - Perform I2C write combined operation
  *  @hw: pointer to the hardware structure
  *  @addr: I2C bus address to write to
  *  @reg: I2C device register to write to
  *  @val: value to write
+ *  @lock: true if to take and release semaphore
  *
  *  Returns an error code on error.
- **/
-s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw,
-                                    u8 addr, u16 reg, u16 val)
+ */
+static s32 ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr,
+                                               u16 reg, u16 val, bool lock)
 {
+       u32 swfw_mask = hw->phy.phy_semaphore_mask;
        int max_retry = 1;
        int retry = 0;
        u8 reg_high;
@@ -197,6 +232,8 @@ s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw,
        csum = ixgbe_ones_comp_byte_add(csum, val & 0xFF);
        csum = ~csum;
        do {
+               if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
+                       return IXGBE_ERR_SWFW_SYNC;
                ixgbe_i2c_start(hw);
                /* Device Address and write indication */
                if (ixgbe_out_i2c_byte_ack(hw, addr))
@@ -217,10 +254,14 @@ s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw,
                if (ixgbe_out_i2c_byte_ack(hw, csum))
                        goto fail;
                ixgbe_i2c_stop(hw);
+               if (lock)
+                       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
                return 0;
 
 fail:
                ixgbe_i2c_bus_clear(hw);
+               if (lock)
+                       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
                retry++;
                if (retry < max_retry)
                        hw_dbg(hw, "I2C byte write combined error - Retry.\n");
@@ -231,6 +272,36 @@ fail:
        return IXGBE_ERR_I2C;
 }
 
+/**
+ *  ixgbe_write_i2c_combined_generic - Perform I2C write combined operation
+ *  @hw: pointer to the hardware structure
+ *  @addr: I2C bus address to write to
+ *  @reg: I2C device register to write to
+ *  @val: value to write
+ *
+ *  Returns an error code on error.
+ */
+s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw,
+                                    u8 addr, u16 reg, u16 val)
+{
+       return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, true);
+}
+
+/**
+ *  ixgbe_write_i2c_combined_generic_unlocked - Unlocked I2C write combined
+ *  @hw: pointer to the hardware structure
+ *  @addr: I2C bus address to write to
+ *  @reg: I2C device register to write to
+ *  @val: value to write
+ *
+ *  Returns an error code on error.
+ */
+s32 ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw,
+                                             u8 addr, u16 reg, u16 val)
+{
+       return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, false);
+}
+
 /**
  *  ixgbe_identify_phy_generic - Get physical layer module
  *  @hw: pointer to hardware structure
@@ -1100,6 +1171,9 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
                return IXGBE_ERR_SFP_NOT_PRESENT;
        }
 
+       /* LAN ID is needed for sfp_type determination */
+       hw->mac.ops.set_lan_id(hw);
+
        status = hw->phy.ops.read_i2c_eeprom(hw,
                                             IXGBE_SFF_IDENTIFIER,
                                             &identifier);
@@ -1107,9 +1181,6 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
        if (status)
                goto err_read_i2c_eeprom;
 
-       /* LAN ID is needed for sfp_type determination */
-       hw->mac.ops.set_lan_id(hw);
-
        if (identifier != IXGBE_SFF_IDENTIFIER_SFP) {
                hw->phy.type = ixgbe_phy_sfp_unsupported;
                return IXGBE_ERR_SFP_NOT_SUPPORTED;
@@ -1159,7 +1230,7 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
                        hw->phy.sfp_type = ixgbe_sfp_type_lr;
                else
                        hw->phy.sfp_type = ixgbe_sfp_type_unknown;
-       } else if (hw->mac.type == ixgbe_mac_82599EB) {
+       } else {
                if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) {
                        if (hw->bus.lan_id == 0)
                                hw->phy.sfp_type =
@@ -1660,26 +1731,46 @@ s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
 }
 
 /**
- *  ixgbe_read_i2c_byte_generic - Reads 8 bit word over I2C
+ * ixgbe_is_sfp_probe - Returns true if SFP is being detected
+ * @hw: pointer to hardware structure
+ * @offset: eeprom offset to be read
+ * @addr: I2C address to be read
+ */
+static bool ixgbe_is_sfp_probe(struct ixgbe_hw *hw, u8 offset, u8 addr)
+{
+       if (addr == IXGBE_I2C_EEPROM_DEV_ADDR &&
+           offset == IXGBE_SFF_IDENTIFIER &&
+           hw->phy.sfp_type == ixgbe_sfp_type_not_present)
+               return true;
+       return false;
+}
+
+/**
+ *  ixgbe_read_i2c_byte_generic_int - Reads 8 bit word over I2C
  *  @hw: pointer to hardware structure
  *  @byte_offset: byte offset to read
  *  @data: value read
+ *  @lock: true if to take and release semaphore
  *
  *  Performs byte read operation to SFP module's EEPROM over I2C interface at
  *  a specified device address.
- **/
-s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                               u8 dev_addr, u8 *data)
+ */
+static s32 ixgbe_read_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset,
+                                          u8 dev_addr, u8 *data, bool lock)
 {
        s32 status;
        u32 max_retry = 10;
        u32 retry = 0;
        u32 swfw_mask = hw->phy.phy_semaphore_mask;
        bool nack = true;
+
+       if (ixgbe_is_sfp_probe(hw, byte_offset, dev_addr))
+               max_retry = IXGBE_SFP_DETECT_RETRIES;
+
        *data = 0;
 
        do {
-               if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
+               if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
                        return IXGBE_ERR_SWFW_SYNC;
 
                ixgbe_i2c_start(hw);
@@ -1721,12 +1812,16 @@ s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
                        goto fail;
 
                ixgbe_i2c_stop(hw);
-               break;
+               if (lock)
+                       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+               return 0;
 
 fail:
                ixgbe_i2c_bus_clear(hw);
-               hw->mac.ops.release_swfw_sync(hw, swfw_mask);
-               msleep(100);
+               if (lock) {
+                       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+                       msleep(100);
+               }
                retry++;
                if (retry < max_retry)
                        hw_dbg(hw, "I2C byte read error - Retrying.\n");
@@ -1735,29 +1830,60 @@ fail:
 
        } while (retry < max_retry);
 
-       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
-
        return status;
 }
 
 /**
- *  ixgbe_write_i2c_byte_generic - Writes 8 bit word over I2C
+ *  ixgbe_read_i2c_byte_generic - Reads 8 bit word over I2C
+ *  @hw: pointer to hardware structure
+ *  @byte_offset: byte offset to read
+ *  @data: value read
+ *
+ *  Performs byte read operation to SFP module's EEPROM over I2C interface at
+ *  a specified device address.
+ */
+s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
+                               u8 dev_addr, u8 *data)
+{
+       return ixgbe_read_i2c_byte_generic_int(hw, byte_offset, dev_addr,
+                                              data, true);
+}
+
+/**
+ *  ixgbe_read_i2c_byte_generic_unlocked - Reads 8 bit word over I2C
+ *  @hw: pointer to hardware structure
+ *  @byte_offset: byte offset to read
+ *  @data: value read
+ *
+ *  Performs byte read operation to SFP module's EEPROM over I2C interface at
+ *  a specified device address.
+ */
+s32 ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset,
+                                        u8 dev_addr, u8 *data)
+{
+       return ixgbe_read_i2c_byte_generic_int(hw, byte_offset, dev_addr,
+                                              data, false);
+}
+
+/**
+ *  ixgbe_write_i2c_byte_generic_int - Writes 8 bit word over I2C
  *  @hw: pointer to hardware structure
  *  @byte_offset: byte offset to write
  *  @data: value to write
+ *  @lock: true if to take and release semaphore
  *
  *  Performs byte write operation to SFP module's EEPROM over I2C interface at
  *  a specified device address.
- **/
-s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                u8 dev_addr, u8 data)
+ */
+static s32 ixgbe_write_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset,
+                                           u8 dev_addr, u8 data, bool lock)
 {
        s32 status;
        u32 max_retry = 1;
        u32 retry = 0;
        u32 swfw_mask = hw->phy.phy_semaphore_mask;
 
-       if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
+       if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
                return IXGBE_ERR_SWFW_SYNC;
 
        do {
@@ -1788,7 +1914,9 @@ s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
                        goto fail;
 
                ixgbe_i2c_stop(hw);
-               break;
+               if (lock)
+                       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+               return 0;
 
 fail:
                ixgbe_i2c_bus_clear(hw);
@@ -1799,21 +1927,57 @@ fail:
                        hw_dbg(hw, "I2C byte write error.\n");
        } while (retry < max_retry);
 
-       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+       if (lock)
+               hw->mac.ops.release_swfw_sync(hw, swfw_mask);
 
        return status;
 }
 
+/**
+ *  ixgbe_write_i2c_byte_generic - Writes 8 bit word over I2C
+ *  @hw: pointer to hardware structure
+ *  @byte_offset: byte offset to write
+ *  @data: value to write
+ *
+ *  Performs byte write operation to SFP module's EEPROM over I2C interface at
+ *  a specified device address.
+ */
+s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
+                                u8 dev_addr, u8 data)
+{
+       return ixgbe_write_i2c_byte_generic_int(hw, byte_offset, dev_addr,
+                                               data, true);
+}
+
+/**
+ *  ixgbe_write_i2c_byte_generic_unlocked - Writes 8 bit word over I2C
+ *  @hw: pointer to hardware structure
+ *  @byte_offset: byte offset to write
+ *  @data: value to write
+ *
+ *  Performs byte write operation to SFP module's EEPROM over I2C interface at
+ *  a specified device address.
+ */
+s32 ixgbe_write_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset,
+                                         u8 dev_addr, u8 data)
+{
+       return ixgbe_write_i2c_byte_generic_int(hw, byte_offset, dev_addr,
+                                               data, false);
+}
+
 /**
  *  ixgbe_i2c_start - Sets I2C start condition
  *  @hw: pointer to hardware structure
  *
  *  Sets I2C start condition (High -> Low on SDA while SCL is High)
+ *  Set bit-bang mode on X550 hardware.
  **/
 static void ixgbe_i2c_start(struct ixgbe_hw *hw)
 {
        u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw));
 
+       i2cctl |= IXGBE_I2C_BB_EN(hw);
+
        /* Start condition must begin with data and clock high */
        ixgbe_set_i2c_data(hw, &i2cctl, 1);
        ixgbe_raise_i2c_clk(hw, &i2cctl);
@@ -1838,10 +2002,15 @@ static void ixgbe_i2c_start(struct ixgbe_hw *hw)
  *  @hw: pointer to hardware structure
  *
  *  Sets I2C stop condition (Low -> High on SDA while SCL is High)
+ *  Disables bit-bang mode and negates data output enable on X550
+ *  hardware.
  **/
 static void ixgbe_i2c_stop(struct ixgbe_hw *hw)
 {
        u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw));
+       u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw);
+       u32 clk_oe_bit = IXGBE_I2C_CLK_OE_N_EN(hw);
+       u32 bb_en_bit = IXGBE_I2C_BB_EN(hw);
 
        /* Stop condition must begin with data low and clock high */
        ixgbe_set_i2c_data(hw, &i2cctl, 0);
@@ -1854,6 +2023,13 @@ static void ixgbe_i2c_stop(struct ixgbe_hw *hw)
 
        /* bus free time between stop and start (4.7us)*/
        udelay(IXGBE_I2C_T_BUF);
+
+       if (bb_en_bit || data_oe_bit || clk_oe_bit) {
+               i2cctl &= ~bb_en_bit;
+               i2cctl |= data_oe_bit | clk_oe_bit;
+               IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl);
+               IXGBE_WRITE_FLUSH(hw);
+       }
 }
 
 /**
@@ -1868,6 +2044,7 @@ static s32 ixgbe_clock_in_i2c_byte(struct ixgbe_hw *hw, u8 *data)
        s32 i;
        bool bit = false;
 
+       *data = 0;
        for (i = 7; i >= 0; i--) {
                ixgbe_clock_in_i2c_bit(hw, &bit);
                *data |= bit << i;
@@ -1901,6 +2078,7 @@ static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data)
        /* Release SDA line (set high) */
        i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw));
        i2cctl |= IXGBE_I2C_DATA_OUT(hw);
+       i2cctl |= IXGBE_I2C_DATA_OE_N_EN(hw);
        IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl);
        IXGBE_WRITE_FLUSH(hw);
 
@@ -1915,15 +2093,21 @@ static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data)
  **/
 static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw)
 {
+       u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw);
        s32 status = 0;
        u32 i = 0;
        u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw));
        u32 timeout = 10;
        bool ack = true;
 
+       if (data_oe_bit) {
+               i2cctl |= IXGBE_I2C_DATA_OUT(hw);
+               i2cctl |= data_oe_bit;
+               IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl);
+               IXGBE_WRITE_FLUSH(hw);
+       }
        ixgbe_raise_i2c_clk(hw, &i2cctl);
 
-
        /* Minimum high period of clock is 4us */
        udelay(IXGBE_I2C_T_HIGH);
 
@@ -1961,7 +2145,14 @@ static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw)
 static s32 ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data)
 {
        u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw));
+       u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw);
 
+       if (data_oe_bit) {
+               i2cctl |= IXGBE_I2C_DATA_OUT(hw);
+               i2cctl |= data_oe_bit;
+               IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl);
+               IXGBE_WRITE_FLUSH(hw);
+       }
        ixgbe_raise_i2c_clk(hw, &i2cctl);
 
        /* Minimum high period of clock is 4us */
@@ -2016,13 +2207,20 @@ static s32 ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data)
  *  @i2cctl: Current value of I2CCTL register
  *
  *  Raises the I2C clock line '0'->'1'
+ *  Negates the I2C clock output enable on X550 hardware.
  **/
 static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl)
 {
+       u32 clk_oe_bit = IXGBE_I2C_CLK_OE_N_EN(hw);
        u32 i = 0;
        u32 timeout = IXGBE_I2C_CLOCK_STRETCHING_TIMEOUT;
        u32 i2cctl_r = 0;
 
+       if (clk_oe_bit) {
+               *i2cctl |= clk_oe_bit;
+               IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl);
+       }
+
        for (i = 0; i < timeout; i++) {
                *i2cctl |= IXGBE_I2C_CLK_OUT(hw);
                IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl);
@@ -2042,11 +2240,13 @@ static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl)
  *  @i2cctl: Current value of I2CCTL register
  *
  *  Lowers the I2C clock line '1'->'0'
+ *  Asserts the I2C clock output enable on X550 hardware.
  **/
 static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl)
 {
 
        *i2cctl &= ~IXGBE_I2C_CLK_OUT(hw);
+       *i2cctl &= ~IXGBE_I2C_CLK_OE_N_EN(hw);
 
        IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl);
        IXGBE_WRITE_FLUSH(hw);
@@ -2062,13 +2262,17 @@ static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl)
  *  @data: I2C data value (0 or 1) to set
  *
  *  Sets the I2C data bit
+ *  Asserts the I2C data output enable on X550 hardware.
  **/
 static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data)
 {
+       u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw);
+
        if (data)
                *i2cctl |= IXGBE_I2C_DATA_OUT(hw);
        else
                *i2cctl &= ~IXGBE_I2C_DATA_OUT(hw);
+       *i2cctl &= ~data_oe_bit;
 
        IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl);
        IXGBE_WRITE_FLUSH(hw);
@@ -2076,6 +2280,14 @@ static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data)
        /* Data rise/fall (1000ns/300ns) and set-up time (250ns) */
        udelay(IXGBE_I2C_T_RISE + IXGBE_I2C_T_FALL + IXGBE_I2C_T_SU_DATA);
 
+       if (!data)      /* Can't verify data in this case */
+               return 0;
+       if (data_oe_bit) {
+               *i2cctl |= data_oe_bit;
+               IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl);
+               IXGBE_WRITE_FLUSH(hw);
+       }
+
        /* Verify data was set correctly */
        *i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw));
        if (data != ixgbe_get_i2c_data(hw, i2cctl)) {
@@ -2092,9 +2304,19 @@ static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data)
  *  @i2cctl: Current value of I2CCTL register
  *
  *  Returns the I2C data bit value
+ *  Negates the I2C data output enable on X550 hardware.
  **/
 static bool ixgbe_get_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl)
 {
+       u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw);
+
+       if (data_oe_bit) {
+               *i2cctl |= data_oe_bit;
+               IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl);
+               IXGBE_WRITE_FLUSH(hw);
+               udelay(IXGBE_I2C_T_FALL);
+       }
+
        if (*i2cctl & IXGBE_I2C_DATA_IN(hw))
                return true;
        return false;
@@ -2109,10 +2331,11 @@ static bool ixgbe_get_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl)
  **/
 static void ixgbe_i2c_bus_clear(struct ixgbe_hw *hw)
 {
-       u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw));
+       u32 i2cctl;
        u32 i;
 
        ixgbe_i2c_start(hw);
+       i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw));
 
        ixgbe_set_i2c_data(hw, &i2cctl, 1);
 
index e45988c4dad556e9b31195f2112de3a44fcb35ab..5abd66c84d005f4d211d20bab2fd42b1a5b2a5c7 100644 (file)
@@ -66,6 +66,9 @@
 #define IXGBE_SFF_1GBASET_CAPABLE              0x8
 #define IXGBE_SFF_10GBASESR_CAPABLE            0x10
 #define IXGBE_SFF_10GBASELR_CAPABLE            0x20
+#define IXGBE_SFF_SOFT_RS_SELECT_MASK          0x8
+#define IXGBE_SFF_SOFT_RS_SELECT_10G           0x8
+#define IXGBE_SFF_SOFT_RS_SELECT_1G            0x0
 #define IXGBE_SFF_ADDRESSING_MODE              0x4
 #define IXGBE_SFF_QSFP_DA_ACTIVE_CABLE         0x1
 #define IXGBE_SFF_QSFP_DA_PASSIVE_CABLE                0x8
 #define IXGBE_I2C_EEPROM_STATUS_FAIL           0x2
 #define IXGBE_I2C_EEPROM_STATUS_IN_PROGRESS    0x3
 #define IXGBE_CS4227                           0xBE    /* CS4227 address */
-#define IXGBE_CS4227_SPARE24_LSB               0x12B0  /* Reg to program EDC */
+#define IXGBE_CS4227_SCRATCH                   2
+#define IXGBE_CS4227_RESET_PENDING             0x1357
+#define IXGBE_CS4227_RESET_COMPLETE            0x5AA5
+#define IXGBE_CS4227_RETRIES                   15
+#define IXGBE_CS4227_EFUSE_STATUS              0x0181
+#define IXGBE_CS4227_LINE_SPARE22_MSB          0x12AD  /* Reg to set speed */
+#define IXGBE_CS4227_LINE_SPARE24_LSB          0x12B0  /* Reg to set EDC */
+#define IXGBE_CS4227_HOST_SPARE22_MSB          0x1AAD  /* Reg to set speed */
+#define IXGBE_CS4227_HOST_SPARE24_LSB          0x1AB0  /* Reg to program EDC */
+#define IXGBE_CS4227_EEPROM_STATUS             0x5001
+#define IXGBE_CS4227_EEPROM_LOAD_OK            0x0001
+#define IXGBE_CS4227_SPEED_1G                  0x8000
+#define IXGBE_CS4227_SPEED_10G                 0
 #define IXGBE_CS4227_EDC_MODE_CX1              0x0002
 #define IXGBE_CS4227_EDC_MODE_SR               0x0004
+#define IXGBE_CS4227_EDC_MODE_DIAG             0x0008
+#define IXGBE_CS4227_RESET_HOLD                        500     /* microseconds */
+#define IXGBE_CS4227_RESET_DELAY               500     /* milliseconds */
+#define IXGBE_CS4227_CHECK_DELAY               30      /* milliseconds */
+#define IXGBE_PE                               0xE0    /* Port expander addr */
+#define IXGBE_PE_OUTPUT                                1       /* Output reg offset */
+#define IXGBE_PE_CONFIG                                3       /* Config reg offset */
+#define IXGBE_PE_BIT1                          (1 << 1)
 
 /* Flow control defines */
 #define IXGBE_TAF_SYM_PAUSE                  0x400
 #define IXGBE_I2C_T_SU_STO  4
 #define IXGBE_I2C_T_BUF     5
 
+#define IXGBE_SFP_DETECT_RETRIES       2
+
 #define IXGBE_TN_LASI_STATUS_REG        0x9005
 #define IXGBE_TN_LASI_STATUS_TEMP_ALARM 0x0008
 
@@ -154,8 +179,12 @@ s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
 s32 ixgbe_tn_check_overtemp(struct ixgbe_hw *hw);
 s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
                                u8 dev_addr, u8 *data);
+s32 ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset,
+                                        u8 dev_addr, u8 *data);
 s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
                                 u8 dev_addr, u8 data);
+s32 ixgbe_write_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset,
+                                         u8 dev_addr, u8 data);
 s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
                                  u8 *eeprom_data);
 s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset,
@@ -164,6 +193,10 @@ s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
                                   u8 eeprom_data);
 s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr,
                                    u16 reg, u16 *val);
+s32 ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr,
+                                            u16 reg, u16 *val);
 s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr,
                                     u16 reg, u16 val);
+s32 ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr,
+                                             u16 reg, u16 val);
 #endif /* _IXGBE_PHY_H_ */
index 1d17b5872dd1f266625891b600d673c98a8cfa0f..fcd8b27a0ccba90bb7f67332f57174a71ef5bf1c 100644 (file)
@@ -116,6 +116,12 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
                         * we want to disable the querying by default.
                         */
                        adapter->vfinfo[i].rss_query_enabled = 0;
+
+                       /* Untrust all VFs */
+                       adapter->vfinfo[i].trusted = false;
+
+                       /* set the default xcast mode */
+                       adapter->vfinfo[i].xcast_mode = IXGBEVF_XCAST_MODE_NONE;
                }
 
                return 0;
@@ -1001,6 +1007,59 @@ static int ixgbe_get_vf_rss_key(struct ixgbe_adapter *adapter,
        return 0;
 }
 
+static int ixgbe_update_vf_xcast_mode(struct ixgbe_adapter *adapter,
+                                     u32 *msgbuf, u32 vf)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       int xcast_mode = msgbuf[1];
+       u32 vmolr, disable, enable;
+
+       /* verify the PF is supporting the correct APIs */
+       switch (adapter->vfinfo[vf].vf_api) {
+       case ixgbe_mbox_api_12:
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       if (xcast_mode > IXGBEVF_XCAST_MODE_MULTI &&
+           !adapter->vfinfo[vf].trusted) {
+               xcast_mode = IXGBEVF_XCAST_MODE_MULTI;
+       }
+
+       if (adapter->vfinfo[vf].xcast_mode == xcast_mode)
+               goto out;
+
+       switch (xcast_mode) {
+       case IXGBEVF_XCAST_MODE_NONE:
+               disable = IXGBE_VMOLR_BAM | IXGBE_VMOLR_ROMPE | IXGBE_VMOLR_MPE;
+               enable = 0;
+               break;
+       case IXGBEVF_XCAST_MODE_MULTI:
+               disable = IXGBE_VMOLR_MPE;
+               enable = IXGBE_VMOLR_BAM | IXGBE_VMOLR_ROMPE;
+               break;
+       case IXGBEVF_XCAST_MODE_ALLMULTI:
+               disable = 0;
+               enable = IXGBE_VMOLR_BAM | IXGBE_VMOLR_ROMPE | IXGBE_VMOLR_MPE;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(vf));
+       vmolr &= ~disable;
+       vmolr |= enable;
+       IXGBE_WRITE_REG(hw, IXGBE_VMOLR(vf), vmolr);
+
+       adapter->vfinfo[vf].xcast_mode = xcast_mode;
+
+out:
+       msgbuf[1] = xcast_mode;
+
+       return 0;
+}
+
 static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf)
 {
        u32 mbx_size = IXGBE_VFMAILBOX_SIZE;
@@ -1063,6 +1122,9 @@ static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf)
        case IXGBE_VF_GET_RSS_KEY:
                retval = ixgbe_get_vf_rss_key(adapter, msgbuf, vf);
                break;
+       case IXGBE_VF_UPDATE_XCAST_MODE:
+               retval = ixgbe_update_vf_xcast_mode(adapter, msgbuf, vf);
+               break;
        default:
                e_err(drv, "Unhandled Msg %8.8x\n", msgbuf[0]);
                retval = IXGBE_ERR_MBX;
@@ -1124,6 +1186,17 @@ void ixgbe_disable_tx_rx(struct ixgbe_adapter *adapter)
        IXGBE_WRITE_REG(hw, IXGBE_VFRE(1), 0);
 }
 
+static inline void ixgbe_ping_vf(struct ixgbe_adapter *adapter, int vf)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       u32 ping;
+
+       ping = IXGBE_PF_CONTROL_MSG;
+       if (adapter->vfinfo[vf].clear_to_send)
+               ping |= IXGBE_VT_MSGTYPE_CTS;
+       ixgbe_write_mbx(hw, &ping, 1, vf);
+}
+
 void ixgbe_ping_all_vfs(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
@@ -1416,6 +1489,28 @@ int ixgbe_ndo_set_vf_rss_query_en(struct net_device *netdev, int vf,
        return 0;
 }
 
+int ixgbe_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+       if (vf >= adapter->num_vfs)
+               return -EINVAL;
+
+       /* nothing to do */
+       if (adapter->vfinfo[vf].trusted == setting)
+               return 0;
+
+       adapter->vfinfo[vf].trusted = setting;
+
+       /* reset VF to reconfigure features */
+       adapter->vfinfo[vf].clear_to_send = false;
+       ixgbe_ping_vf(adapter, vf);
+
+       e_info(drv, "VF %u is %strusted\n", vf, setting ? "" : "not ");
+
+       return 0;
+}
+
 int ixgbe_ndo_get_vf_config(struct net_device *netdev,
                            int vf, struct ifla_vf_info *ivi)
 {
@@ -1430,5 +1525,6 @@ int ixgbe_ndo_get_vf_config(struct net_device *netdev,
        ivi->qos = adapter->vfinfo[vf].pf_qos;
        ivi->spoofchk = adapter->vfinfo[vf].spoofchk_enabled;
        ivi->rss_query_en = adapter->vfinfo[vf].rss_query_enabled;
+       ivi->trusted = adapter->vfinfo[vf].trusted;
        return 0;
 }
index 2c197e6d1fe7c3cb66c1be785bba2a8084e806fa..dad925706f4cf554537d4978c28286357854486c 100644 (file)
@@ -49,6 +49,7 @@ int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate,
 int ixgbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting);
 int ixgbe_ndo_set_vf_rss_query_en(struct net_device *netdev, int vf,
                                  bool setting);
+int ixgbe_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting);
 int ixgbe_ndo_get_vf_config(struct net_device *netdev,
                            int vf, struct ifla_vf_info *ivi);
 void ixgbe_check_vf_rate_limit(struct ixgbe_adapter *adapter);
index 63689192b149a0d44d1dcf1edcfbe39f6f9d8158..995f03107eacd03511dab6aa8e342a6c474375e1 100644 (file)
@@ -402,6 +402,7 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_FDIRSIP4M 0x0EE40
 #define IXGBE_FDIRTCPM  0x0EE44
 #define IXGBE_FDIRUDPM  0x0EE48
+#define IXGBE_FDIRSCTPM        0x0EE78
 #define IXGBE_FDIRIP6M  0x0EE74
 #define IXGBE_FDIRM     0x0EE70
 
@@ -1192,6 +1193,7 @@ struct ixgbe_thermal_sensor_data {
 /* RDRXCTL Bit Masks */
 #define IXGBE_RDRXCTL_RDMTS_1_2     0x00000000 /* Rx Desc Min Threshold Size */
 #define IXGBE_RDRXCTL_CRCSTRIP      0x00000002 /* CRC Strip */
+#define IXGBE_RDRXCTL_PSP           0x00000004 /* Pad small packet */
 #define IXGBE_RDRXCTL_MVMEN         0x00000020
 #define IXGBE_RDRXCTL_DMAIDONE      0x00000008 /* DMA init cycle done */
 #define IXGBE_RDRXCTL_AGGDIS        0x00010000 /* Aggregation disable */
@@ -1750,6 +1752,9 @@ enum {
  *    FCoE (0x8906):         Filter 2
  *    1588 (0x88f7):         Filter 3
  *    FIP  (0x8914):         Filter 4
+ *    LLDP (0x88CC):         Filter 5
+ *    LACP (0x8809):         Filter 6
+ *    FC   (0x8808):         Filter 7
  */
 #define IXGBE_ETQF_FILTER_EAPOL          0
 #define IXGBE_ETQF_FILTER_FCOE           2
@@ -1757,6 +1762,7 @@ enum {
 #define IXGBE_ETQF_FILTER_FIP            4
 #define IXGBE_ETQF_FILTER_LLDP          5
 #define IXGBE_ETQF_FILTER_LACP          6
+#define IXGBE_ETQF_FILTER_FC            7
 
 /* VLAN Control Bit Masks */
 #define IXGBE_VLNCTRL_VET       0x0000FFFF  /* bits 0-15 */
@@ -1948,6 +1954,7 @@ enum {
 #define IXGBE_GSSR_SW_MNG_SM           0x0400
 #define IXGBE_GSSR_SHARED_I2C_SM       0x1806 /* Wait for both phys & I2Cs */
 #define IXGBE_GSSR_I2C_MASK            0x1800
+#define IXGBE_GSSR_NVM_PHY_MASK                0xF
 
 /* FW Status register bitmask */
 #define IXGBE_FWSTS_FWRI    0x00000200 /* Firmware Reset Indication */
@@ -3255,9 +3262,11 @@ struct ixgbe_mac_operations {
        void (*flap_tx_laser)(struct ixgbe_hw *);
        void (*stop_link_on_d3)(struct ixgbe_hw *);
        s32 (*setup_link)(struct ixgbe_hw *, ixgbe_link_speed, bool);
+       s32 (*setup_mac_link)(struct ixgbe_hw *, ixgbe_link_speed, bool);
        s32 (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *, bool);
        s32 (*get_link_capabilities)(struct ixgbe_hw *, ixgbe_link_speed *,
                                     bool *);
+       void (*set_rate_select_speed)(struct ixgbe_hw *, ixgbe_link_speed);
 
        /* Packet Buffer Manipulation */
        void (*set_rxpba)(struct ixgbe_hw *, int, u32, int);
@@ -3328,6 +3337,10 @@ struct ixgbe_phy_operations {
        s32 (*set_phy_power)(struct ixgbe_hw *, bool on);
        s32 (*enter_lplu)(struct ixgbe_hw *);
        s32 (*handle_lasi)(struct ixgbe_hw *hw);
+       s32 (*read_i2c_combined_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg,
+                                         u16 *value);
+       s32 (*write_i2c_combined_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg,
+                                          u16 value);
 };
 
 struct ixgbe_eeprom_info {
index 4e758435ece872943231ee490ab222df6a86fa25..c1d4584f6469df00e9b12d9254f6c2e17bd1070f 100644 (file)
@@ -567,19 +567,25 @@ static s32 ixgbe_poll_flash_update_done_X540(struct ixgbe_hw *hw)
  **/
 s32 ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask)
 {
-       u32 swfw_sync;
-       u32 swmask = mask;
-       u32 fwmask = mask << 5;
-       u32 hwmask = 0;
+       u32 swmask = mask & IXGBE_GSSR_NVM_PHY_MASK;
+       u32 swi2c_mask = mask & IXGBE_GSSR_I2C_MASK;
+       u32 fwmask = swmask << 5;
        u32 timeout = 200;
+       u32 hwmask = 0;
+       u32 swfw_sync;
        u32 i;
 
-       if (swmask == IXGBE_GSSR_EEP_SM)
+       if (swmask & IXGBE_GSSR_EEP_SM)
                hwmask = IXGBE_GSSR_FLASH_SM;
 
+       /* SW only mask does not have FW bit pair */
+       if (mask & IXGBE_GSSR_SW_MNG_SM)
+               swmask |= IXGBE_GSSR_SW_MNG_SM;
+
+       swmask |= swi2c_mask;
+       fwmask |= swi2c_mask << 2;
        for (i = 0; i < timeout; i++) {
-               /*
-                * SW NVM semaphore bit is used for access to all
+               /* SW NVM semaphore bit is used for access to all
                 * SW_FW_SYNC bits (not just NVM)
                 */
                if (ixgbe_get_swfw_sync_semaphore(hw))
@@ -590,39 +596,56 @@ s32 ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask)
                        swfw_sync |= swmask;
                        IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync);
                        ixgbe_release_swfw_sync_semaphore(hw);
-                       break;
-               } else {
-                       /*
-                        * Firmware currently using resource (fwmask),
-                        * hardware currently using resource (hwmask),
-                        * or other software thread currently using
-                        * resource (swmask)
-                        */
-                       ixgbe_release_swfw_sync_semaphore(hw);
-                       usleep_range(5000, 10000);
+                       usleep_range(5000, 6000);
+                       return 0;
                }
+               /* Firmware currently using resource (fwmask), hardware
+                * currently using resource (hwmask), or other software
+                * thread currently using resource (swmask)
+                */
+               ixgbe_release_swfw_sync_semaphore(hw);
+               usleep_range(5000, 10000);
        }
 
-       /*
-        * If the resource is not released by the FW/HW the SW can assume that
-        * the FW/HW malfunctions. In that case the SW should sets the
-        * SW bit(s) of the requested resource(s) while ignoring the
-        * corresponding FW/HW bits in the SW_FW_SYNC register.
-        */
-       if (i >= timeout) {
-               swfw_sync = IXGBE_READ_REG(hw, IXGBE_SWFW_SYNC(hw));
-               if (swfw_sync & (fwmask | hwmask)) {
-                       if (ixgbe_get_swfw_sync_semaphore(hw))
-                               return IXGBE_ERR_SWFW_SYNC;
+       /* Failed to get SW only semaphore */
+       if (swmask == IXGBE_GSSR_SW_MNG_SM) {
+               hw_dbg(hw, "Failed to get SW only semaphore\n");
+               return IXGBE_ERR_SWFW_SYNC;
+       }
 
-                       swfw_sync |= swmask;
-                       IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync);
-                       ixgbe_release_swfw_sync_semaphore(hw);
-               }
+       /* If the resource is not released by the FW/HW the SW can assume that
+        * the FW/HW malfunctions. In that case the SW should set the SW bit(s)
+        * of the requested resource(s) while ignoring the corresponding FW/HW
+        * bits in the SW_FW_SYNC register.
+        */
+       if (ixgbe_get_swfw_sync_semaphore(hw))
+               return IXGBE_ERR_SWFW_SYNC;
+       swfw_sync = IXGBE_READ_REG(hw, IXGBE_SWFW_SYNC(hw));
+       if (swfw_sync & (fwmask | hwmask)) {
+               swfw_sync |= swmask;
+               IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync);
+               ixgbe_release_swfw_sync_semaphore(hw);
+               usleep_range(5000, 6000);
+               return 0;
        }
+       /* If the resource is not released by other SW the SW can assume that
+        * the other SW malfunctions. In that case the SW should clear all SW
+        * flags that it does not own and then repeat the whole process once
+        * again.
+        */
+       if (swfw_sync & swmask) {
+               u32 rmask = IXGBE_GSSR_EEP_SM | IXGBE_GSSR_PHY0_SM |
+                           IXGBE_GSSR_PHY1_SM | IXGBE_GSSR_MAC_CSR_SM;
+
+               if (swi2c_mask)
+                       rmask |= IXGBE_GSSR_I2C_MASK;
+               ixgbe_release_swfw_sync_X540(hw, rmask);
+               ixgbe_release_swfw_sync_semaphore(hw);
+               return IXGBE_ERR_SWFW_SYNC;
+       }
+       ixgbe_release_swfw_sync_semaphore(hw);
 
-       usleep_range(5000, 10000);
-       return 0;
+       return IXGBE_ERR_SWFW_SYNC;
 }
 
 /**
@@ -635,9 +658,11 @@ s32 ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask)
  **/
 void ixgbe_release_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask)
 {
+       u32 swmask = mask & (IXGBE_GSSR_NVM_PHY_MASK | IXGBE_GSSR_SW_MNG_SM);
        u32 swfw_sync;
-       u32 swmask = mask;
 
+       if (mask & IXGBE_GSSR_I2C_MASK)
+               swmask |= mask & IXGBE_GSSR_I2C_MASK;
        ixgbe_get_swfw_sync_semaphore(hw);
 
        swfw_sync = IXGBE_READ_REG(hw, IXGBE_SWFW_SYNC(hw));
@@ -645,7 +670,7 @@ void ixgbe_release_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask)
        IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync);
 
        ixgbe_release_swfw_sync_semaphore(hw);
-       usleep_range(5000, 10000);
+       usleep_range(5000, 6000);
 }
 
 /**
@@ -686,6 +711,11 @@ static s32 ixgbe_get_swfw_sync_semaphore(struct ixgbe_hw *hw)
                usleep_range(50, 100);
        }
 
+       /* Release semaphores and return error if SW NVM semaphore
+        * was not granted because we do not have access to the EEPROM
+        */
+       hw_dbg(hw, "REGSMP Software NVM semaphore not granted\n");
+       ixgbe_release_swfw_sync_semaphore(hw);
        return IXGBE_ERR_EEPROM;
 }
 
index 9fe9445cd73b0061d5c2c0a7dd1f405b01f1d200..ebe0ac950b14e72a0d5dae2e51d3250c250846d1 100644 (file)
@@ -56,6 +56,292 @@ static void ixgbe_setup_mux_ctl(struct ixgbe_hw *hw)
        IXGBE_WRITE_FLUSH(hw);
 }
 
+/**
+ * ixgbe_read_cs4227 - Read CS4227 register
+ * @hw: pointer to hardware structure
+ * @reg: register number to write
+ * @value: pointer to receive value read
+ *
+ * Returns status code
+ */
+static s32 ixgbe_read_cs4227(struct ixgbe_hw *hw, u16 reg, u16 *value)
+{
+       return hw->phy.ops.read_i2c_combined_unlocked(hw, IXGBE_CS4227, reg,
+                                                     value);
+}
+
+/**
+ * ixgbe_write_cs4227 - Write CS4227 register
+ * @hw: pointer to hardware structure
+ * @reg: register number to write
+ * @value: value to write to register
+ *
+ * Returns status code
+ */
+static s32 ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value)
+{
+       return hw->phy.ops.write_i2c_combined_unlocked(hw, IXGBE_CS4227, reg,
+                                                      value);
+}
+
+/**
+ * ixgbe_check_cs4227_reg - Perform diag on a CS4227 register
+ * @hw: pointer to hardware structure
+ * @reg: the register to check
+ *
+ * Performs a diagnostic on a register in the CS4227 chip. Returns an error
+ * if it is not operating correctly.
+ * This function assumes that the caller has acquired the proper semaphore.
+ */
+static s32 ixgbe_check_cs4227_reg(struct ixgbe_hw *hw, u16 reg)
+{
+       s32 status;
+       u32 retry;
+       u16 reg_val;
+
+       reg_val = (IXGBE_CS4227_EDC_MODE_DIAG << 1) | 1;
+       status = ixgbe_write_cs4227(hw, reg, reg_val);
+       if (status)
+               return status;
+       for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
+               msleep(IXGBE_CS4227_CHECK_DELAY);
+               reg_val = 0xFFFF;
+               ixgbe_read_cs4227(hw, reg, &reg_val);
+               if (!reg_val)
+                       break;
+       }
+       if (reg_val) {
+               hw_err(hw, "CS4227 reg 0x%04X failed diagnostic\n", reg);
+               return status;
+       }
+
+       return 0;
+}
+
+/**
+ * ixgbe_get_cs4227_status - Return CS4227 status
+ * @hw: pointer to hardware structure
+ *
+ * Performs a diagnostic on the CS4227 chip. Returns an error if it is
+ * not operating correctly.
+ * This function assumes that the caller has acquired the proper semaphore.
+ */
+static s32 ixgbe_get_cs4227_status(struct ixgbe_hw *hw)
+{
+       s32 status;
+       u16 value = 0;
+
+       /* Exit if the diagnostic has already been performed. */
+       status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value);
+       if (status)
+               return status;
+       if (value == IXGBE_CS4227_RESET_COMPLETE)
+               return 0;
+
+       /* Check port 0. */
+       status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB);
+       if (status)
+               return status;
+
+       status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB);
+       if (status)
+               return status;
+
+       /* Check port 1. */
+       status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB +
+                                       (1 << 12));
+       if (status)
+               return status;
+
+       return ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB +
+                                     (1 << 12));
+}
+
+/**
+ * ixgbe_read_pe - Read register from port expander
+ * @hw: pointer to hardware structure
+ * @reg: register number to read
+ * @value: pointer to receive read value
+ *
+ * Returns status code
+ */
+static s32 ixgbe_read_pe(struct ixgbe_hw *hw, u8 reg, u8 *value)
+{
+       s32 status;
+
+       status = ixgbe_read_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE, value);
+       if (status)
+               hw_err(hw, "port expander access failed with %d\n", status);
+       return status;
+}
+
+/**
+ * ixgbe_write_pe - Write register to port expander
+ * @hw: pointer to hardware structure
+ * @reg: register number to write
+ * @value: value to write
+ *
+ * Returns status code
+ */
+static s32 ixgbe_write_pe(struct ixgbe_hw *hw, u8 reg, u8 value)
+{
+       s32 status;
+
+       status = ixgbe_write_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE,
+                                                      value);
+       if (status)
+               hw_err(hw, "port expander access failed with %d\n", status);
+       return status;
+}
+
+/**
+ * ixgbe_reset_cs4227 - Reset CS4227 using port expander
+ * @hw: pointer to hardware structure
+ *
+ * This function assumes that the caller has acquired the proper semaphore.
+ * Returns error code
+ */
+static s32 ixgbe_reset_cs4227(struct ixgbe_hw *hw)
+{
+       s32 status;
+       u32 retry;
+       u16 value;
+       u8 reg;
+
+       /* Trigger hard reset. */
+       status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, &reg);
+       if (status)
+               return status;
+       reg |= IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
+       if (status)
+               return status;
+
+       status = ixgbe_read_pe(hw, IXGBE_PE_CONFIG, &reg);
+       if (status)
+               return status;
+       reg &= ~IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_CONFIG, reg);
+       if (status)
+               return status;
+
+       status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, &reg);
+       if (status)
+               return status;
+       reg &= ~IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
+       if (status)
+               return status;
+
+       usleep_range(IXGBE_CS4227_RESET_HOLD, IXGBE_CS4227_RESET_HOLD + 100);
+
+       status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, &reg);
+       if (status)
+               return status;
+       reg |= IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
+       if (status)
+               return status;
+
+       /* Wait for the reset to complete. */
+       msleep(IXGBE_CS4227_RESET_DELAY);
+       for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
+               status = ixgbe_read_cs4227(hw, IXGBE_CS4227_EFUSE_STATUS,
+                                          &value);
+               if (!status && value == IXGBE_CS4227_EEPROM_LOAD_OK)
+                       break;
+               msleep(IXGBE_CS4227_CHECK_DELAY);
+       }
+       if (retry == IXGBE_CS4227_RETRIES) {
+               hw_err(hw, "CS4227 reset did not complete\n");
+               return IXGBE_ERR_PHY;
+       }
+
+       status = ixgbe_read_cs4227(hw, IXGBE_CS4227_EEPROM_STATUS, &value);
+       if (status || !(value & IXGBE_CS4227_EEPROM_LOAD_OK)) {
+               hw_err(hw, "CS4227 EEPROM did not load successfully\n");
+               return IXGBE_ERR_PHY;
+       }
+
+       return 0;
+}
+
+/**
+ * ixgbe_check_cs4227 - Check CS4227 and reset as needed
+ * @hw: pointer to hardware structure
+ */
+static void ixgbe_check_cs4227(struct ixgbe_hw *hw)
+{
+       u32 swfw_mask = hw->phy.phy_semaphore_mask;
+       s32 status;
+       u16 value;
+       u8 retry;
+
+       for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
+               status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask);
+               if (status) {
+                       hw_err(hw, "semaphore failed with %d\n", status);
+                       msleep(IXGBE_CS4227_CHECK_DELAY);
+                       continue;
+               }
+
+               /* Get status of reset flow. */
+               status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value);
+               if (!status && value == IXGBE_CS4227_RESET_COMPLETE)
+                       goto out;
+
+               if (status || value != IXGBE_CS4227_RESET_PENDING)
+                       break;
+
+               /* Reset is pending. Wait and check again. */
+               hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+               msleep(IXGBE_CS4227_CHECK_DELAY);
+       }
+       /* If still pending, assume other instance failed. */
+       if (retry == IXGBE_CS4227_RETRIES) {
+               status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask);
+               if (status) {
+                       hw_err(hw, "semaphore failed with %d\n", status);
+                       return;
+               }
+       }
+
+       /* Reset the CS4227. */
+       status = ixgbe_reset_cs4227(hw);
+       if (status) {
+               hw_err(hw, "CS4227 reset failed: %d", status);
+               goto out;
+       }
+
+       /* Reset takes so long, temporarily release semaphore in case the
+        * other driver instance is waiting for the reset indication.
+        */
+       ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH,
+                          IXGBE_CS4227_RESET_PENDING);
+       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+       usleep_range(10000, 12000);
+       status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask);
+       if (status) {
+               hw_err(hw, "semaphore failed with %d", status);
+               return;
+       }
+
+       /* Is the CS4227 working correctly? */
+       status = ixgbe_get_cs4227_status(hw);
+       if (status) {
+               hw_err(hw, "CS4227 status failed: %d", status);
+               goto out;
+       }
+
+       /* Record completion for next time. */
+       status = ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH,
+                                   IXGBE_CS4227_RESET_COMPLETE);
+
+out:
+       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+       msleep(hw->eeprom.semaphore_delay);
+}
+
 /** ixgbe_identify_phy_x550em - Get PHY type based on device id
  *  @hw: pointer to hardware structure
  *
@@ -68,7 +354,7 @@ static s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw)
                /* set up for CS4227 usage */
                hw->phy.phy_semaphore_mask = IXGBE_GSSR_SHARED_I2C_SM;
                ixgbe_setup_mux_ctl(hw);
-
+               ixgbe_check_cs4227(hw);
                return ixgbe_identify_module_generic(hw);
        case IXGBE_DEV_ID_X550EM_X_KX4:
                hw->phy.type = ixgbe_phy_x550em_kx4;
@@ -909,6 +1195,96 @@ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed)
        return status;
 }
 
+/**
+ *  ixgbe_supported_sfp_modules_X550em - Check if SFP module type is supported
+ *  @hw: pointer to hardware structure
+ *  @linear: true if SFP module is linear
+ */
+static s32 ixgbe_supported_sfp_modules_X550em(struct ixgbe_hw *hw, bool *linear)
+{
+       switch (hw->phy.sfp_type) {
+       case ixgbe_sfp_type_not_present:
+               return IXGBE_ERR_SFP_NOT_PRESENT;
+       case ixgbe_sfp_type_da_cu_core0:
+       case ixgbe_sfp_type_da_cu_core1:
+               *linear = true;
+               break;
+       case ixgbe_sfp_type_srlr_core0:
+       case ixgbe_sfp_type_srlr_core1:
+       case ixgbe_sfp_type_da_act_lmt_core0:
+       case ixgbe_sfp_type_da_act_lmt_core1:
+       case ixgbe_sfp_type_1g_sx_core0:
+       case ixgbe_sfp_type_1g_sx_core1:
+       case ixgbe_sfp_type_1g_lx_core0:
+       case ixgbe_sfp_type_1g_lx_core1:
+               *linear = false;
+               break;
+       case ixgbe_sfp_type_unknown:
+       case ixgbe_sfp_type_1g_cu_core0:
+       case ixgbe_sfp_type_1g_cu_core1:
+       default:
+               return IXGBE_ERR_SFP_NOT_SUPPORTED;
+       }
+
+       return 0;
+}
+
+/**
+ *  ixgbe_setup_mac_link_sfp_x550em - Configure the KR PHY for SFP.
+ *  @hw: pointer to hardware structure
+ *
+ *  Configures the extern PHY and the integrated KR PHY for SFP support.
+ */
+static s32
+ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw,
+                               ixgbe_link_speed speed,
+                               __always_unused bool autoneg_wait_to_complete)
+{
+       s32 status;
+       u16 slice, value;
+       bool setup_linear = false;
+
+       /* Check if SFP module is supported and linear */
+       status = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear);
+
+       /* If no SFP module present, then return success. Return success since
+        * there is no reason to configure CS4227 and SFP not present error is
+        * not accepted in the setup MAC link flow.
+        */
+       if (status == IXGBE_ERR_SFP_NOT_PRESENT)
+               return 0;
+
+       if (status)
+               return status;
+
+       /* Configure CS4227 LINE side to 10G SR. */
+       slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12);
+       value = IXGBE_CS4227_SPEED_10G;
+       status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
+                                                 value);
+
+       /* Configure CS4227 for HOST connection rate then type. */
+       slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12);
+       value = speed & IXGBE_LINK_SPEED_10GB_FULL ?
+               IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G;
+       status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
+                                                 value);
+
+       slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12);
+       if (setup_linear)
+               value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1;
+       else
+               value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1;
+       status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
+                                                 value);
+
+       /* If internal link mode is XFI, then setup XFI internal link. */
+       if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE))
+               status = ixgbe_setup_ixfi_x550em(hw, &speed);
+
+       return status;
+}
+
 /**
  * ixgbe_setup_mac_link_t_X550em - Sets the auto advertised link speed
  * @hw: pointer to hardware structure
@@ -1003,6 +1379,10 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw)
                mac->ops.disable_tx_laser = NULL;
                mac->ops.enable_tx_laser = NULL;
                mac->ops.flap_tx_laser = NULL;
+               mac->ops.setup_link = ixgbe_setup_mac_link_multispeed_fiber;
+               mac->ops.setup_mac_link = ixgbe_setup_mac_link_sfp_x550em;
+               mac->ops.set_rate_select_speed =
+                                       ixgbe_set_soft_rate_select_speed;
                break;
        case ixgbe_media_type_copper:
                mac->ops.setup_link = ixgbe_setup_mac_link_t_X550em;
@@ -1018,53 +1398,18 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw)
  */
 static s32 ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw)
 {
-       bool setup_linear;
-       u16 reg_slice, edc_mode;
-       s32 ret_val;
+       s32 status;
+       bool linear;
 
-       switch (hw->phy.sfp_type) {
-       case ixgbe_sfp_type_unknown:
-               return 0;
-       case ixgbe_sfp_type_not_present:
-               return IXGBE_ERR_SFP_NOT_PRESENT;
-       case ixgbe_sfp_type_da_cu_core0:
-       case ixgbe_sfp_type_da_cu_core1:
-               setup_linear = true;
-               break;
-       case ixgbe_sfp_type_srlr_core0:
-       case ixgbe_sfp_type_srlr_core1:
-       case ixgbe_sfp_type_da_act_lmt_core0:
-       case ixgbe_sfp_type_da_act_lmt_core1:
-       case ixgbe_sfp_type_1g_sx_core0:
-       case ixgbe_sfp_type_1g_sx_core1:
-               setup_linear = false;
-               break;
-       default:
-               return IXGBE_ERR_SFP_NOT_SUPPORTED;
-       }
+       /* Check if SFP module is supported */
+       status = ixgbe_supported_sfp_modules_X550em(hw, &linear);
+       if (status)
+               return status;
 
        ixgbe_init_mac_link_ops_X550em(hw);
        hw->phy.ops.reset = NULL;
 
-       /* The CS4227 slice address is the base address + the port-pair reg
-        * offset. I.e. Slice 0 = 0x12B0 and slice 1 = 0x22B0.
-        */
-       reg_slice = IXGBE_CS4227_SPARE24_LSB + (hw->bus.lan_id << 12);
-
-       if (setup_linear)
-               edc_mode = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 0x1;
-       else
-               edc_mode = (IXGBE_CS4227_EDC_MODE_SR << 1) | 0x1;
-
-       /* Configure CS4227 for connection type. */
-       ret_val = hw->phy.ops.write_i2c_combined(hw, IXGBE_CS4227, reg_slice,
-                                                edc_mode);
-
-       if (ret_val)
-               ret_val = hw->phy.ops.write_i2c_combined(hw, 0x80, reg_slice,
-                                                        edc_mode);
-
-       return ret_val;
+       return 0;
 }
 
 /** ixgbe_get_link_capabilities_x550em - Determines link capabilities
@@ -1272,7 +1617,7 @@ static s32 ixgbe_handle_lasi_ext_t_x550em(struct ixgbe_hw *hw)
        if (status)
                return status;
 
-       if (lsc)
+       if (lsc && phy->ops.setup_internal_link)
                return phy->ops.setup_internal_link(hw);
 
        return 0;
@@ -1927,6 +2272,62 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw,
        IXGBE_WRITE_REG(hw, IXGBE_PFFLPH, (u32)(pfflp >> 32));
 }
 
+/**
+ * ixgbe_set_mux - Set mux for port 1 access with CS4227
+ * @hw: pointer to hardware structure
+ * @state: set mux if 1, clear if 0
+ */
+static void ixgbe_set_mux(struct ixgbe_hw *hw, u8 state)
+{
+       u32 esdp;
+
+       if (!hw->bus.lan_id)
+               return;
+       esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
+       if (state)
+               esdp |= IXGBE_ESDP_SDP1;
+       else
+               esdp &= ~IXGBE_ESDP_SDP1;
+       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
+       IXGBE_WRITE_FLUSH(hw);
+}
+
+/**
+ * ixgbe_acquire_swfw_sync_X550em - Acquire SWFW semaphore
+ * @hw: pointer to hardware structure
+ * @mask: Mask to specify which semaphore to acquire
+ *
+ * Acquires the SWFW semaphore and sets the I2C MUX
+ */
+static s32 ixgbe_acquire_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask)
+{
+       s32 status;
+
+       status = ixgbe_acquire_swfw_sync_X540(hw, mask);
+       if (status)
+               return status;
+
+       if (mask & IXGBE_GSSR_I2C_MASK)
+               ixgbe_set_mux(hw, 1);
+
+       return 0;
+}
+
+/**
+ * ixgbe_release_swfw_sync_X550em - Release SWFW semaphore
+ * @hw: pointer to hardware structure
+ * @mask: Mask to specify which semaphore to release
+ *
+ * Releases the SWFW semaphore and sets the I2C MUX
+ */
+static void ixgbe_release_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask)
+{
+       if (mask & IXGBE_GSSR_I2C_MASK)
+               ixgbe_set_mux(hw, 0);
+
+       ixgbe_release_swfw_sync_X540(hw, mask);
+}
+
 #define X550_COMMON_MAC \
        .init_hw                        = &ixgbe_init_hw_generic, \
        .start_hw                       = &ixgbe_start_hw_X540, \
@@ -1964,8 +2365,6 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw,
                                &ixgbe_set_source_address_pruning_X550, \
        .set_ethertype_anti_spoofing    = \
                                &ixgbe_set_ethertype_anti_spoofing_X550, \
-       .acquire_swfw_sync              = &ixgbe_acquire_swfw_sync_X540, \
-       .release_swfw_sync              = &ixgbe_release_swfw_sync_X540, \
        .disable_rx_buff                = &ixgbe_disable_rx_buff_generic, \
        .enable_rx_buff                 = &ixgbe_enable_rx_buff_generic, \
        .get_thermal_sensor_data        = NULL, \
@@ -1985,6 +2384,8 @@ static struct ixgbe_mac_operations mac_ops_X550 = {
        .get_link_capabilities  = &ixgbe_get_copper_link_capabilities_generic,
        .get_bus_info           = &ixgbe_get_bus_info_generic,
        .setup_sfp              = NULL,
+       .acquire_swfw_sync      = &ixgbe_acquire_swfw_sync_X540,
+       .release_swfw_sync      = &ixgbe_release_swfw_sync_X540,
 };
 
 static struct ixgbe_mac_operations mac_ops_X550EM_x = {
@@ -1997,7 +2398,8 @@ static struct ixgbe_mac_operations mac_ops_X550EM_x = {
        .get_link_capabilities  = &ixgbe_get_link_capabilities_X550em,
        .get_bus_info           = &ixgbe_get_bus_info_X550em,
        .setup_sfp              = ixgbe_setup_sfp_modules_X550em,
-
+       .acquire_swfw_sync      = &ixgbe_acquire_swfw_sync_X550em,
+       .release_swfw_sync      = &ixgbe_release_swfw_sync_X550em,
 };
 
 #define X550_COMMON_EEP \
@@ -2039,14 +2441,17 @@ static struct ixgbe_phy_operations phy_ops_X550 = {
        X550_COMMON_PHY
        .init                   = NULL,
        .identify               = &ixgbe_identify_phy_generic,
-       .read_i2c_combined      = &ixgbe_read_i2c_combined_generic,
-       .write_i2c_combined     = &ixgbe_write_i2c_combined_generic,
 };
 
 static struct ixgbe_phy_operations phy_ops_X550EM_x = {
        X550_COMMON_PHY
        .init                   = &ixgbe_init_phy_ops_X550em,
        .identify               = &ixgbe_identify_phy_x550em,
+       .read_i2c_combined      = &ixgbe_read_i2c_combined_generic,
+       .write_i2c_combined     = &ixgbe_write_i2c_combined_generic,
+       .read_i2c_combined_unlocked = &ixgbe_read_i2c_combined_generic_unlocked,
+       .write_i2c_combined_unlocked =
+                                    &ixgbe_write_i2c_combined_generic_unlocked,
 };
 
 static const u32 ixgbe_mvals_X550[IXGBE_MVALS_IDX_LIMIT] = {
index 04c7ec8446e0329c71eaee8eea346a4e92836b8e..ec31472796213197fe50d163ce0473a0d34df7fe 100644 (file)
@@ -471,6 +471,12 @@ enum ixgbevf_boards {
        board_X550EM_x_vf,
 };
 
+enum ixgbevf_xcast_modes {
+       IXGBEVF_XCAST_MODE_NONE = 0,
+       IXGBEVF_XCAST_MODE_MULTI,
+       IXGBEVF_XCAST_MODE_ALLMULTI,
+};
+
 extern const struct ixgbevf_info ixgbevf_82599_vf_info;
 extern const struct ixgbevf_info ixgbevf_X540_vf_info;
 extern const struct ixgbevf_info ixgbevf_X550_vf_info;
index 149a0b4489be4088514f9e02c7c768629185449f..592ff237d69207837358a38025e31f4e9989b540 100644 (file)
@@ -1008,7 +1008,7 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
                container_of(napi, struct ixgbevf_q_vector, napi);
        struct ixgbevf_adapter *adapter = q_vector->adapter;
        struct ixgbevf_ring *ring;
-       int per_ring_budget;
+       int per_ring_budget, work_done = 0;
        bool clean_complete = true;
 
        ixgbevf_for_each_ring(ring, q_vector->tx)
@@ -1027,10 +1027,12 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
        else
                per_ring_budget = budget;
 
-       ixgbevf_for_each_ring(ring, q_vector->rx)
-               clean_complete &= (ixgbevf_clean_rx_irq(q_vector, ring,
-                                                       per_ring_budget)
-                                  < per_ring_budget);
+       ixgbevf_for_each_ring(ring, q_vector->rx) {
+               int cleaned = ixgbevf_clean_rx_irq(q_vector, ring,
+                                                  per_ring_budget);
+               work_done += cleaned;
+               clean_complete &= (cleaned < per_ring_budget);
+       }
 
 #ifdef CONFIG_NET_RX_BUSY_POLL
        ixgbevf_qv_unlock_napi(q_vector);
@@ -1040,7 +1042,7 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
        if (!clean_complete)
                return budget;
        /* all work done, exit the polling mode */
-       napi_complete(napi);
+       napi_complete_done(napi, work_done);
        if (adapter->rx_itr_setting & 1)
                ixgbevf_set_itr(q_vector);
        if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
@@ -1892,9 +1894,17 @@ static void ixgbevf_set_rx_mode(struct net_device *netdev)
 {
        struct ixgbevf_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
+       unsigned int flags = netdev->flags;
+       int xcast_mode;
+
+       xcast_mode = (flags & IFF_ALLMULTI) ? IXGBEVF_XCAST_MODE_ALLMULTI :
+                    (flags & (IFF_BROADCAST | IFF_MULTICAST)) ?
+                    IXGBEVF_XCAST_MODE_MULTI : IXGBEVF_XCAST_MODE_NONE;
 
        spin_lock_bh(&adapter->mbx_lock);
 
+       hw->mac.ops.update_xcast_mode(hw, netdev, xcast_mode);
+
        /* reprogram multicast list */
        hw->mac.ops.update_mc_addr_list(hw, netdev);
 
@@ -3896,6 +3906,7 @@ static const struct net_device_ops ixgbevf_netdev_ops = {
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = ixgbevf_netpoll,
 #endif
+       .ndo_features_check     = passthru_features_check,
 };
 
 static void ixgbevf_assign_netdev_ops(struct net_device *dev)
index 82f44e06e5fca11ad86ec92930428fbbc130c93d..340cdd469455ef646f38b25bd0ecde62788976ce 100644 (file)
@@ -112,6 +112,8 @@ enum ixgbe_pfvf_api_rev {
 #define IXGBE_VF_GET_RETA      0x0a    /* VF request for RETA */
 #define IXGBE_VF_GET_RSS_KEY   0x0b    /* get RSS hash key */
 
+#define IXGBE_VF_UPDATE_XCAST_MODE     0x0c
+
 /* length of permanent address message returned from PF */
 #define IXGBE_VF_PERMADDR_MSG_LEN      4
 /* word in permanent address message with the current multicast type */
index d1339b0506274ce0acbfc1e642b1b7fa57c332e8..427f3605cbfc8035f03910d564eeb5e707a5c58c 100644 (file)
@@ -468,6 +468,46 @@ static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw,
        return 0;
 }
 
+/**
+ *  ixgbevf_update_xcast_mode - Update Multicast mode
+ *  @hw: pointer to the HW structure
+ *  @netdev: pointer to net device structure
+ *  @xcast_mode: new multicast mode
+ *
+ *  Updates the Multicast Mode of VF.
+ **/
+static s32 ixgbevf_update_xcast_mode(struct ixgbe_hw *hw,
+                                    struct net_device *netdev, int xcast_mode)
+{
+       struct ixgbe_mbx_info *mbx = &hw->mbx;
+       u32 msgbuf[2];
+       s32 err;
+
+       switch (hw->api_version) {
+       case ixgbe_mbox_api_12:
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       msgbuf[0] = IXGBE_VF_UPDATE_XCAST_MODE;
+       msgbuf[1] = xcast_mode;
+
+       err = mbx->ops.write_posted(hw, msgbuf, 2);
+       if (err)
+               return err;
+
+       err = mbx->ops.read_posted(hw, msgbuf, 2);
+       if (err)
+               return err;
+
+       msgbuf[0] &= ~IXGBE_VT_MSGTYPE_CTS;
+       if (msgbuf[0] == (IXGBE_VF_UPDATE_XCAST_MODE | IXGBE_VT_MSGTYPE_NACK))
+               return -EPERM;
+
+       return 0;
+}
+
 /**
  *  ixgbevf_set_vfta_vf - Set/Unset VLAN filter table address
  *  @hw: pointer to the HW structure
@@ -727,6 +767,7 @@ static const struct ixgbe_mac_operations ixgbevf_mac_ops = {
        .check_link             = ixgbevf_check_mac_link_vf,
        .set_rar                = ixgbevf_set_rar_vf,
        .update_mc_addr_list    = ixgbevf_update_mc_addr_list_vf,
+       .update_xcast_mode      = ixgbevf_update_xcast_mode,
        .set_uc_addr            = ixgbevf_set_uc_addr_vf,
        .set_vfta               = ixgbevf_set_vfta_vf,
 };
index d40f036b6df0c828b5a3efe10ee90ba780b36a52..ef9f7736b4dc6524ea12ea96c31c2218c6600f76 100644 (file)
@@ -63,6 +63,7 @@ struct ixgbe_mac_operations {
        s32 (*set_uc_addr)(struct ixgbe_hw *, u32, u8 *);
        s32 (*init_rx_addrs)(struct ixgbe_hw *);
        s32 (*update_mc_addr_list)(struct ixgbe_hw *, struct net_device *);
+       s32 (*update_xcast_mode)(struct ixgbe_hw *, struct net_device *, int);
        s32 (*enable_mc)(struct ixgbe_hw *);
        s32 (*disable_mc)(struct ixgbe_hw *);
        s32 (*clear_vfta)(struct ixgbe_hw *);
index 960169efe636a659241f7e8fae10706fd0503939..4182290fdbcf109aadf6a0f25154b71757d3d25c 100644 (file)
@@ -759,11 +759,23 @@ txq_put_data_tso(struct net_device *dev, struct tx_queue *txq,
 
        desc->l4i_chk = 0;
        desc->byte_cnt = length;
-       desc->buf_ptr = dma_map_single(dev->dev.parent, data,
-                                      length, DMA_TO_DEVICE);
-       if (unlikely(dma_mapping_error(dev->dev.parent, desc->buf_ptr))) {
-               WARN(1, "dma_map_single failed!\n");
-               return -ENOMEM;
+
+       if (length <= 8 && (uintptr_t)data & 0x7) {
+               /* Copy unaligned small data fragment to TSO header data area */
+               memcpy(txq->tso_hdrs + txq->tx_curr_desc * TSO_HEADER_SIZE,
+                      data, length);
+               desc->buf_ptr = txq->tso_hdrs_dma
+                       + txq->tx_curr_desc * TSO_HEADER_SIZE;
+       } else {
+               /* Alignment is okay, map buffer and hand off to hardware */
+               txq->tx_desc_mapping[tx_index] = DESC_DMA_MAP_SINGLE;
+               desc->buf_ptr = dma_map_single(dev->dev.parent, data,
+                       length, DMA_TO_DEVICE);
+               if (unlikely(dma_mapping_error(dev->dev.parent,
+                                              desc->buf_ptr))) {
+                       WARN(1, "dma_map_single failed!\n");
+                       return -ENOMEM;
+               }
        }
 
        cmd_sts = BUFFER_OWNED_BY_DMA;
@@ -779,7 +791,8 @@ txq_put_data_tso(struct net_device *dev, struct tx_queue *txq,
 }
 
 static inline void
-txq_put_hdr_tso(struct sk_buff *skb, struct tx_queue *txq, int length)
+txq_put_hdr_tso(struct sk_buff *skb, struct tx_queue *txq, int length,
+               u32 *first_cmd_sts, bool first_desc)
 {
        struct mv643xx_eth_private *mp = txq_to_mp(txq);
        int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
@@ -788,6 +801,7 @@ txq_put_hdr_tso(struct sk_buff *skb, struct tx_queue *txq, int length)
        int ret;
        u32 cmd_csum = 0;
        u16 l4i_chk = 0;
+       u32 cmd_sts;
 
        tx_index = txq->tx_curr_desc;
        desc = &txq->tx_desc_area[tx_index];
@@ -803,9 +817,17 @@ txq_put_hdr_tso(struct sk_buff *skb, struct tx_queue *txq, int length)
        desc->byte_cnt = hdr_len;
        desc->buf_ptr = txq->tso_hdrs_dma +
                        txq->tx_curr_desc * TSO_HEADER_SIZE;
-       desc->cmd_sts = cmd_csum | BUFFER_OWNED_BY_DMA  | TX_FIRST_DESC |
+       cmd_sts = cmd_csum | BUFFER_OWNED_BY_DMA  | TX_FIRST_DESC |
                                   GEN_CRC;
 
+       /* Defer updating the first command descriptor until all
+        * following descriptors have been written.
+        */
+       if (first_desc)
+               *first_cmd_sts = cmd_sts;
+       else
+               desc->cmd_sts = cmd_sts;
+
        txq->tx_curr_desc++;
        if (txq->tx_curr_desc == txq->tx_ring_size)
                txq->tx_curr_desc = 0;
@@ -819,6 +841,8 @@ static int txq_submit_tso(struct tx_queue *txq, struct sk_buff *skb,
        int desc_count = 0;
        struct tso_t tso;
        int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       struct tx_desc *first_tx_desc;
+       u32 first_cmd_sts = 0;
 
        /* Count needed descriptors */
        if ((txq->tx_desc_count + tso_count_descs(skb)) >= txq->tx_ring_size) {
@@ -826,11 +850,14 @@ static int txq_submit_tso(struct tx_queue *txq, struct sk_buff *skb,
                return -EBUSY;
        }
 
+       first_tx_desc = &txq->tx_desc_area[txq->tx_curr_desc];
+
        /* Initialize the TSO handler, and prepare the first payload */
        tso_start(skb, &tso);
 
        total_len = skb->len - hdr_len;
        while (total_len > 0) {
+               bool first_desc = (desc_count == 0);
                char *hdr;
 
                data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
@@ -840,7 +867,8 @@ static int txq_submit_tso(struct tx_queue *txq, struct sk_buff *skb,
                /* prepare packet headers: MAC + IP + TCP */
                hdr = txq->tso_hdrs + txq->tx_curr_desc * TSO_HEADER_SIZE;
                tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
-               txq_put_hdr_tso(skb, txq, data_left);
+               txq_put_hdr_tso(skb, txq, data_left, &first_cmd_sts,
+                               first_desc);
 
                while (data_left > 0) {
                        int size;
@@ -860,6 +888,10 @@ static int txq_submit_tso(struct tx_queue *txq, struct sk_buff *skb,
        __skb_queue_tail(&txq->tx_skb, skb);
        skb_tx_timestamp(skb);
 
+       /* ensure all other descriptors are written before first cmd_sts */
+       wmb();
+       first_tx_desc->cmd_sts = first_cmd_sts;
+
        /* clear TX_END status */
        mp->work_tx_end &= ~(1 << txq->index);
 
@@ -1586,7 +1618,6 @@ static void mv643xx_eth_get_drvinfo(struct net_device *dev,
                sizeof(drvinfo->version));
        strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
        strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info));
-       drvinfo->n_stats = ARRAY_SIZE(mv643xx_eth_stats);
 }
 
 static int mv643xx_eth_nway_reset(struct net_device *dev)
@@ -1845,29 +1876,19 @@ static void mv643xx_eth_program_multicast_filter(struct net_device *dev)
        struct netdev_hw_addr *ha;
        int i;
 
-       if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
-               int port_num;
-               u32 accept;
+       if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI))
+               goto promiscuous;
 
-oom:
-               port_num = mp->port_num;
-               accept = 0x01010101;
-               for (i = 0; i < 0x100; i += 4) {
-                       wrl(mp, SPECIAL_MCAST_TABLE(port_num) + i, accept);
-                       wrl(mp, OTHER_MCAST_TABLE(port_num) + i, accept);
-               }
-               return;
-       }
-
-       mc_spec = kzalloc(0x200, GFP_ATOMIC);
-       if (mc_spec == NULL)
-               goto oom;
-       mc_other = mc_spec + (0x100 >> 2);
+       /* Allocate both mc_spec and mc_other tables */
+       mc_spec = kcalloc(128, sizeof(u32), GFP_ATOMIC);
+       if (!mc_spec)
+               goto promiscuous;
+       mc_other = &mc_spec[64];
 
        netdev_for_each_mc_addr(ha, dev) {
                u8 *a = ha->addr;
                u32 *table;
-               int entry;
+               u8 entry;
 
                if (memcmp(a, "\x01\x00\x5e\x00\x00", 5) == 0) {
                        table = mc_spec;
@@ -1880,12 +1901,23 @@ oom:
                table[entry >> 2] |= 1 << (8 * (entry & 3));
        }
 
-       for (i = 0; i < 0x100; i += 4) {
-               wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, mc_spec[i >> 2]);
-               wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, mc_other[i >> 2]);
+       for (i = 0; i < 64; i++) {
+               wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i * sizeof(u32),
+                   mc_spec[i]);
+               wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i * sizeof(u32),
+                   mc_other[i]);
        }
 
        kfree(mc_spec);
+       return;
+
+promiscuous:
+       for (i = 0; i < 64; i++) {
+               wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i * sizeof(u32),
+                   0x01010101u);
+               wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i * sizeof(u32),
+                   0x01010101u);
+       }
 }
 
 static void mv643xx_eth_set_rx_mode(struct net_device *dev)
@@ -2785,8 +2817,10 @@ static int mv643xx_eth_shared_of_probe(struct platform_device *pdev)
 
        for_each_available_child_of_node(np, pnp) {
                ret = mv643xx_eth_shared_of_add_port(pdev, pnp);
-               if (ret)
+               if (ret) {
+                       of_node_put(pnp);
                        return ret;
+               }
        }
        return 0;
 }
index 514df76fc70f36430a2d646fbee30997e6767210..a47496a020d99eb2b56f185c3be8f1cf797b567a 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/of_address.h>
 #include <linux/phy.h>
 #include <linux/clk.h>
+#include <linux/cpu.h>
 
 /* Registers */
 #define MVNETA_RXQ_CONFIG_REG(q)                (0x1400 + ((q) << 2))
 #define MVNETA_TXQ_CMD                           0x2448
 #define      MVNETA_TXQ_DISABLE_SHIFT            8
 #define      MVNETA_TXQ_ENABLE_MASK              0x000000ff
+#define MVNETA_RX_DISCARD_FRAME_COUNT           0x2484
+#define MVNETA_OVERRUN_FRAME_COUNT              0x2488
 #define MVNETA_GMAC_CLOCK_DIVIDER                0x24f4
 #define      MVNETA_GMAC_1MS_CLOCK_ENABLE        BIT(31)
 #define MVNETA_ACC_MODE                          0x2500
 #define      MVNETA_GMAC_AN_FLOW_CTRL_EN         BIT(11)
 #define      MVNETA_GMAC_CONFIG_FULL_DUPLEX      BIT(12)
 #define      MVNETA_GMAC_AN_DUPLEX_EN            BIT(13)
-#define MVNETA_MIB_COUNTERS_BASE                 0x3080
+#define MVNETA_MIB_COUNTERS_BASE                 0x3000
 #define      MVNETA_MIB_LATE_COLLISION           0x7c
 #define MVNETA_DA_FILT_SPEC_MCAST                0x3400
 #define MVNETA_DA_FILT_OTH_MCAST                 0x3500
 
 #define MVNETA_RX_BUF_SIZE(pkt_size)   ((pkt_size) + NET_SKB_PAD)
 
+struct mvneta_statistic {
+       unsigned short offset;
+       unsigned short type;
+       const char name[ETH_GSTRING_LEN];
+};
+
+#define T_REG_32       32
+#define T_REG_64       64
+
+static const struct mvneta_statistic mvneta_statistics[] = {
+       { 0x3000, T_REG_64, "good_octets_received", },
+       { 0x3010, T_REG_32, "good_frames_received", },
+       { 0x3008, T_REG_32, "bad_octets_received", },
+       { 0x3014, T_REG_32, "bad_frames_received", },
+       { 0x3018, T_REG_32, "broadcast_frames_received", },
+       { 0x301c, T_REG_32, "multicast_frames_received", },
+       { 0x3050, T_REG_32, "unrec_mac_control_received", },
+       { 0x3058, T_REG_32, "good_fc_received", },
+       { 0x305c, T_REG_32, "bad_fc_received", },
+       { 0x3060, T_REG_32, "undersize_received", },
+       { 0x3064, T_REG_32, "fragments_received", },
+       { 0x3068, T_REG_32, "oversize_received", },
+       { 0x306c, T_REG_32, "jabber_received", },
+       { 0x3070, T_REG_32, "mac_receive_error", },
+       { 0x3074, T_REG_32, "bad_crc_event", },
+       { 0x3078, T_REG_32, "collision", },
+       { 0x307c, T_REG_32, "late_collision", },
+       { 0x2484, T_REG_32, "rx_discard", },
+       { 0x2488, T_REG_32, "rx_overrun", },
+       { 0x3020, T_REG_32, "frames_64_octets", },
+       { 0x3024, T_REG_32, "frames_65_to_127_octets", },
+       { 0x3028, T_REG_32, "frames_128_to_255_octets", },
+       { 0x302c, T_REG_32, "frames_256_to_511_octets", },
+       { 0x3030, T_REG_32, "frames_512_to_1023_octets", },
+       { 0x3034, T_REG_32, "frames_1024_to_max_octets", },
+       { 0x3038, T_REG_64, "good_octets_sent", },
+       { 0x3040, T_REG_32, "good_frames_sent", },
+       { 0x3044, T_REG_32, "excessive_collision", },
+       { 0x3048, T_REG_32, "multicast_frames_sent", },
+       { 0x304c, T_REG_32, "broadcast_frames_sent", },
+       { 0x3054, T_REG_32, "fc_sent", },
+       { 0x300c, T_REG_32, "internal_mac_transmit_err", },
+};
+
 struct mvneta_pcpu_stats {
        struct  u64_stats_sync syncp;
        u64     rx_packets;
@@ -285,23 +332,34 @@ struct mvneta_pcpu_stats {
        u64     tx_bytes;
 };
 
+struct mvneta_pcpu_port {
+       /* Pointer to the shared port */
+       struct mvneta_port      *pp;
+
+       /* Pointer to the CPU-local NAPI struct */
+       struct napi_struct      napi;
+
+       /* Cause of the previous interrupt */
+       u32                     cause_rx_tx;
+};
+
 struct mvneta_port {
+       struct mvneta_pcpu_port __percpu        *ports;
+       struct mvneta_pcpu_stats __percpu       *stats;
+
        int pkt_size;
        unsigned int frag_size;
        void __iomem *base;
        struct mvneta_rx_queue *rxqs;
        struct mvneta_tx_queue *txqs;
        struct net_device *dev;
-
-       u32 cause_rx_tx;
-       struct napi_struct napi;
+       struct notifier_block cpu_notifier;
 
        /* Core clock */
        struct clk *clk;
        u8 mcast_count[256];
        u16 tx_ring_size;
        u16 rx_ring_size;
-       struct mvneta_pcpu_stats *stats;
 
        struct mii_bus *mii_bus;
        struct phy_device *phy_dev;
@@ -312,6 +370,8 @@ struct mvneta_port {
        unsigned int speed;
        unsigned int tx_csum_limit;
        int use_inband_status:1;
+
+       u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
 };
 
 /* The mvneta_tx_desc and mvneta_rx_desc structures describe the
@@ -468,7 +528,7 @@ struct mvneta_rx_queue {
 /* The hardware supports eight (8) rx queues, but we are only allowing
  * the first one to be used. Therefore, let's just allocate one queue.
  */
-static int rxq_number = 1;
+static int rxq_number = 8;
 static int txq_number = 8;
 
 static int rxq_def;
@@ -518,6 +578,8 @@ static void mvneta_mib_counters_clear(struct mvneta_port *pp)
        /* Perform dummy reads from MIB counters */
        for (i = 0; i < MVNETA_MIB_LATE_COLLISION; i += 4)
                dummy = mvreg_read(pp, (MVNETA_MIB_COUNTERS_BASE + i));
+       dummy = mvreg_read(pp, MVNETA_RX_DISCARD_FRAME_COUNT);
+       dummy = mvreg_read(pp, MVNETA_OVERRUN_FRAME_COUNT);
 }
 
 /* Get System Network Statistics */
@@ -746,7 +808,6 @@ static void mvneta_port_up(struct mvneta_port *pp)
        u32 q_map;
 
        /* Enable all initialized TXs. */
-       mvneta_mib_counters_clear(pp);
        q_map = 0;
        for (queue = 0; queue < txq_number; queue++) {
                struct mvneta_tx_queue *txq = &pp->txqs[queue];
@@ -756,14 +817,7 @@ static void mvneta_port_up(struct mvneta_port *pp)
        mvreg_write(pp, MVNETA_TXQ_CMD, q_map);
 
        /* Enable all initialized RXQs. */
-       q_map = 0;
-       for (queue = 0; queue < rxq_number; queue++) {
-               struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
-               if (rxq->descs != NULL)
-                       q_map |= (1 << queue);
-       }
-
-       mvreg_write(pp, MVNETA_RXQ_CMD, q_map);
+       mvreg_write(pp, MVNETA_RXQ_CMD, BIT(rxq_def));
 }
 
 /* Stop the Ethernet port activity */
@@ -949,7 +1003,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
        /* Set CPU queue access map - all CPUs have access to all RX
         * queues and to all TX queues
         */
-       for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++)
+       for_each_present_cpu(cpu)
                mvreg_write(pp, MVNETA_CPU_MAP(cpu),
                            (MVNETA_CPU_RXQ_ACCESS_ALL_MASK |
                             MVNETA_CPU_TXQ_ACCESS_ALL_MASK));
@@ -1030,6 +1084,8 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
        mvreg_write(pp, MVNETA_INTR_ENABLE,
                    (MVNETA_RXQ_INTR_ENABLE_ALL_MASK
                     | MVNETA_TXQ_INTR_ENABLE_ALL_MASK));
+
+       mvneta_mib_counters_clear(pp);
 }
 
 /* Set max sizes for tx queues */
@@ -1426,17 +1482,6 @@ static u32 mvneta_skb_tx_csum(struct mvneta_port *pp, struct sk_buff *skb)
        return MVNETA_TX_L4_CSUM_NOT;
 }
 
-/* Returns rx queue pointer (find last set bit) according to causeRxTx
- * value
- */
-static struct mvneta_rx_queue *mvneta_rx_policy(struct mvneta_port *pp,
-                                               u32 cause)
-{
-       int queue = fls(cause >> 8) - 1;
-
-       return (queue < 0 || queue >= rxq_number) ? NULL : &pp->rxqs[queue];
-}
-
 /* Drop packets received by the RXQ and free buffers */
 static void mvneta_rxq_drop_pkts(struct mvneta_port *pp,
                                 struct mvneta_rx_queue *rxq)
@@ -1461,6 +1506,7 @@ static void mvneta_rxq_drop_pkts(struct mvneta_port *pp,
 static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
                     struct mvneta_rx_queue *rxq)
 {
+       struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
        struct net_device *dev = pp->dev;
        int rx_done;
        u32 rcvd_pkts = 0;
@@ -1515,7 +1561,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
 
                        skb->protocol = eth_type_trans(skb, dev);
                        mvneta_rx_csum(pp, rx_status, skb);
-                       napi_gro_receive(&pp->napi, skb);
+                       napi_gro_receive(&port->napi, skb);
 
                        rcvd_pkts++;
                        rcvd_bytes += rx_bytes;
@@ -1550,7 +1596,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
 
                mvneta_rx_csum(pp, rx_status, skb);
 
-               napi_gro_receive(&pp->napi, skb);
+               napi_gro_receive(&port->napi, skb);
        }
 
        if (rcvd_pkts) {
@@ -2061,12 +2107,10 @@ static void mvneta_set_rx_mode(struct net_device *dev)
 /* Interrupt handling - the callback for request_irq() */
 static irqreturn_t mvneta_isr(int irq, void *dev_id)
 {
-       struct mvneta_port *pp = (struct mvneta_port *)dev_id;
-
-       /* Mask all interrupts */
-       mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
+       struct mvneta_pcpu_port *port = (struct mvneta_pcpu_port *)dev_id;
 
-       napi_schedule(&pp->napi);
+       disable_percpu_irq(port->pp->dev->irq);
+       napi_schedule(&port->napi);
 
        return IRQ_HANDLED;
 }
@@ -2104,11 +2148,11 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
 {
        int rx_done = 0;
        u32 cause_rx_tx;
-       unsigned long flags;
        struct mvneta_port *pp = netdev_priv(napi->dev);
+       struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
 
        if (!netif_running(pp->dev)) {
-               napi_complete(napi);
+               napi_complete(&port->napi);
                return rx_done;
        }
 
@@ -2135,47 +2179,17 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
        /* For the case where the last mvneta_poll did not process all
         * RX packets
         */
-       cause_rx_tx |= pp->cause_rx_tx;
-       if (rxq_number > 1) {
-               while ((cause_rx_tx & MVNETA_RX_INTR_MASK_ALL) && (budget > 0)) {
-                       int count;
-                       struct mvneta_rx_queue *rxq;
-                       /* get rx queue number from cause_rx_tx */
-                       rxq = mvneta_rx_policy(pp, cause_rx_tx);
-                       if (!rxq)
-                               break;
-
-                       /* process the packet in that rx queue */
-                       count = mvneta_rx(pp, budget, rxq);
-                       rx_done += count;
-                       budget -= count;
-                       if (budget > 0) {
-                               /* set off the rx bit of the
-                                * corresponding bit in the cause rx
-                                * tx register, so that next iteration
-                                * will find the next rx queue where
-                                * packets are received on
-                                */
-                               cause_rx_tx &= ~((1 << rxq->id) << 8);
-                       }
-               }
-       } else {
-               rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]);
-               budget -= rx_done;
-       }
+       cause_rx_tx |= port->cause_rx_tx;
+       rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]);
+       budget -= rx_done;
 
        if (budget > 0) {
                cause_rx_tx = 0;
-               napi_complete(napi);
-               local_irq_save(flags);
-               mvreg_write(pp, MVNETA_INTR_NEW_MASK,
-                           MVNETA_RX_INTR_MASK(rxq_number) |
-                           MVNETA_TX_INTR_MASK(txq_number) |
-                           MVNETA_MISCINTR_INTR_MASK);
-               local_irq_restore(flags);
+               napi_complete(&port->napi);
+               enable_percpu_irq(pp->dev->irq, 0);
        }
 
-       pp->cause_rx_tx = cause_rx_tx;
+       port->cause_rx_tx = cause_rx_tx;
        return rx_done;
 }
 
@@ -2379,26 +2393,19 @@ static void mvneta_cleanup_txqs(struct mvneta_port *pp)
 /* Cleanup all Rx queues */
 static void mvneta_cleanup_rxqs(struct mvneta_port *pp)
 {
-       int queue;
-
-       for (queue = 0; queue < rxq_number; queue++)
-               mvneta_rxq_deinit(pp, &pp->rxqs[queue]);
+       mvneta_rxq_deinit(pp, &pp->rxqs[rxq_def]);
 }
 
 
 /* Init all Rx queues */
 static int mvneta_setup_rxqs(struct mvneta_port *pp)
 {
-       int queue;
-
-       for (queue = 0; queue < rxq_number; queue++) {
-               int err = mvneta_rxq_init(pp, &pp->rxqs[queue]);
-               if (err) {
-                       netdev_err(pp->dev, "%s: can't create rxq=%d\n",
-                                  __func__, queue);
-                       mvneta_cleanup_rxqs(pp);
-                       return err;
-               }
+       int err = mvneta_rxq_init(pp, &pp->rxqs[rxq_def]);
+       if (err) {
+               netdev_err(pp->dev, "%s: can't create rxq=%d\n",
+                          __func__, rxq_def);
+               mvneta_cleanup_rxqs(pp);
+               return err;
        }
 
        return 0;
@@ -2424,6 +2431,8 @@ static int mvneta_setup_txqs(struct mvneta_port *pp)
 
 static void mvneta_start_dev(struct mvneta_port *pp)
 {
+       unsigned int cpu;
+
        mvneta_max_rx_size_set(pp, pp->pkt_size);
        mvneta_txq_max_tx_size_set(pp, pp->pkt_size);
 
@@ -2431,7 +2440,11 @@ static void mvneta_start_dev(struct mvneta_port *pp)
        mvneta_port_enable(pp);
 
        /* Enable polling on the port */
-       napi_enable(&pp->napi);
+       for_each_present_cpu(cpu) {
+               struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
+
+               napi_enable(&port->napi);
+       }
 
        /* Unmask interrupts */
        mvreg_write(pp, MVNETA_INTR_NEW_MASK,
@@ -2449,9 +2462,15 @@ static void mvneta_start_dev(struct mvneta_port *pp)
 
 static void mvneta_stop_dev(struct mvneta_port *pp)
 {
+       unsigned int cpu;
+
        phy_stop(pp->phy_dev);
 
-       napi_disable(&pp->napi);
+       for_each_present_cpu(cpu) {
+               struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
+
+               napi_disable(&port->napi);
+       }
 
        netif_carrier_off(pp->dev);
 
@@ -2691,6 +2710,125 @@ static void mvneta_mdio_remove(struct mvneta_port *pp)
        pp->phy_dev = NULL;
 }
 
+static void mvneta_percpu_enable(void *arg)
+{
+       struct mvneta_port *pp = arg;
+
+       enable_percpu_irq(pp->dev->irq, IRQ_TYPE_NONE);
+}
+
+static void mvneta_percpu_disable(void *arg)
+{
+       struct mvneta_port *pp = arg;
+
+       disable_percpu_irq(pp->dev->irq);
+}
+
+static void mvneta_percpu_elect(struct mvneta_port *pp)
+{
+       int online_cpu_idx, cpu, i = 0;
+
+       online_cpu_idx = rxq_def % num_online_cpus();
+
+       for_each_online_cpu(cpu) {
+               if (i == online_cpu_idx)
+                       /* Enable per-CPU interrupt on the one CPU we
+                        * just elected
+                        */
+                       smp_call_function_single(cpu, mvneta_percpu_enable,
+                                               pp, true);
+               else
+                       /* Disable per-CPU interrupt on all the other CPU */
+                       smp_call_function_single(cpu, mvneta_percpu_disable,
+                                               pp, true);
+               i++;
+       }
+};
+
+static int mvneta_percpu_notifier(struct notifier_block *nfb,
+                                 unsigned long action, void *hcpu)
+{
+       struct mvneta_port *pp = container_of(nfb, struct mvneta_port,
+                                             cpu_notifier);
+       int cpu = (unsigned long)hcpu, other_cpu;
+       struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               netif_tx_stop_all_queues(pp->dev);
+
+               /* We have to synchronise on tha napi of each CPU
+                * except the one just being waked up
+                */
+               for_each_online_cpu(other_cpu) {
+                       if (other_cpu != cpu) {
+                               struct mvneta_pcpu_port *other_port =
+                                       per_cpu_ptr(pp->ports, other_cpu);
+
+                               napi_synchronize(&other_port->napi);
+                       }
+               }
+
+               /* Mask all ethernet port interrupts */
+               mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
+               mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
+               mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
+               napi_enable(&port->napi);
+
+               /* Enable per-CPU interrupt on the one CPU we care
+                * about.
+                */
+               mvneta_percpu_elect(pp);
+
+               /* Unmask all ethernet port interrupts */
+               mvreg_write(pp, MVNETA_INTR_NEW_MASK,
+                       MVNETA_RX_INTR_MASK(rxq_number) |
+                       MVNETA_TX_INTR_MASK(txq_number) |
+                       MVNETA_MISCINTR_INTR_MASK);
+               mvreg_write(pp, MVNETA_INTR_MISC_MASK,
+                       MVNETA_CAUSE_PHY_STATUS_CHANGE |
+                       MVNETA_CAUSE_LINK_CHANGE |
+                       MVNETA_CAUSE_PSC_SYNC_CHANGE);
+               netif_tx_start_all_queues(pp->dev);
+               break;
+       case CPU_DOWN_PREPARE:
+       case CPU_DOWN_PREPARE_FROZEN:
+               netif_tx_stop_all_queues(pp->dev);
+               /* Mask all ethernet port interrupts */
+               mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
+               mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
+               mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
+
+               napi_synchronize(&port->napi);
+               napi_disable(&port->napi);
+               /* Disable per-CPU interrupts on the CPU that is
+                * brought down.
+                */
+               smp_call_function_single(cpu, mvneta_percpu_disable,
+                                        pp, true);
+
+               break;
+       case CPU_DEAD:
+       case CPU_DEAD_FROZEN:
+               /* Check if a new CPU must be elected now this on is down */
+               mvneta_percpu_elect(pp);
+               /* Unmask all ethernet port interrupts */
+               mvreg_write(pp, MVNETA_INTR_NEW_MASK,
+                       MVNETA_RX_INTR_MASK(rxq_number) |
+                       MVNETA_TX_INTR_MASK(txq_number) |
+                       MVNETA_MISCINTR_INTR_MASK);
+               mvreg_write(pp, MVNETA_INTR_MISC_MASK,
+                       MVNETA_CAUSE_PHY_STATUS_CHANGE |
+                       MVNETA_CAUSE_LINK_CHANGE |
+                       MVNETA_CAUSE_PSC_SYNC_CHANGE);
+               netif_tx_start_all_queues(pp->dev);
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
 static int mvneta_open(struct net_device *dev)
 {
        struct mvneta_port *pp = netdev_priv(dev);
@@ -2709,13 +2847,29 @@ static int mvneta_open(struct net_device *dev)
                goto err_cleanup_rxqs;
 
        /* Connect to port interrupt line */
-       ret = request_irq(pp->dev->irq, mvneta_isr, 0,
-                         MVNETA_DRIVER_NAME, pp);
+       ret = request_percpu_irq(pp->dev->irq, mvneta_isr,
+                                MVNETA_DRIVER_NAME, pp->ports);
        if (ret) {
                netdev_err(pp->dev, "cannot request irq %d\n", pp->dev->irq);
                goto err_cleanup_txqs;
        }
 
+       /* Even though the documentation says that request_percpu_irq
+        * doesn't enable the interrupts automatically, it actually
+        * does so on the local CPU.
+        *
+        * Make sure it's disabled.
+        */
+       mvneta_percpu_disable(pp);
+
+       /* Elect a CPU to handle our RX queue interrupt */
+       mvneta_percpu_elect(pp);
+
+       /* Register a CPU notifier to handle the case where our CPU
+        * might be taken offline.
+        */
+       register_cpu_notifier(&pp->cpu_notifier);
+
        /* In default link is down */
        netif_carrier_off(pp->dev);
 
@@ -2730,7 +2884,7 @@ static int mvneta_open(struct net_device *dev)
        return 0;
 
 err_free_irq:
-       free_irq(pp->dev->irq, pp);
+       free_percpu_irq(pp->dev->irq, pp->ports);
 err_cleanup_txqs:
        mvneta_cleanup_txqs(pp);
 err_cleanup_rxqs:
@@ -2742,10 +2896,14 @@ err_cleanup_rxqs:
 static int mvneta_stop(struct net_device *dev)
 {
        struct mvneta_port *pp = netdev_priv(dev);
+       int cpu;
 
        mvneta_stop_dev(pp);
        mvneta_mdio_remove(pp);
-       free_irq(dev->irq, pp);
+       unregister_cpu_notifier(&pp->cpu_notifier);
+       for_each_present_cpu(cpu)
+               smp_call_function_single(cpu, mvneta_percpu_disable, pp, true);
+       free_percpu_irq(dev->irq, pp->ports);
        mvneta_cleanup_rxqs(pp);
        mvneta_cleanup_txqs(pp);
 
@@ -2875,6 +3033,65 @@ static int mvneta_ethtool_set_ringparam(struct net_device *dev,
        return 0;
 }
 
+static void mvneta_ethtool_get_strings(struct net_device *netdev, u32 sset,
+                                      u8 *data)
+{
+       if (sset == ETH_SS_STATS) {
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(mvneta_statistics); i++)
+                       memcpy(data + i * ETH_GSTRING_LEN,
+                              mvneta_statistics[i].name, ETH_GSTRING_LEN);
+       }
+}
+
+static void mvneta_ethtool_update_stats(struct mvneta_port *pp)
+{
+       const struct mvneta_statistic *s;
+       void __iomem *base = pp->base;
+       u32 high, low, val;
+       int i;
+
+       for (i = 0, s = mvneta_statistics;
+            s < mvneta_statistics + ARRAY_SIZE(mvneta_statistics);
+            s++, i++) {
+               val = 0;
+
+               switch (s->type) {
+               case T_REG_32:
+                       val = readl_relaxed(base + s->offset);
+                       break;
+               case T_REG_64:
+                       /* Docs say to read low 32-bit then high */
+                       low = readl_relaxed(base + s->offset);
+                       high = readl_relaxed(base + s->offset + 4);
+                       val = (u64)high << 32 | low;
+                       break;
+               }
+
+               pp->ethtool_stats[i] += val;
+       }
+}
+
+static void mvneta_ethtool_get_stats(struct net_device *dev,
+                                    struct ethtool_stats *stats, u64 *data)
+{
+       struct mvneta_port *pp = netdev_priv(dev);
+       int i;
+
+       mvneta_ethtool_update_stats(pp);
+
+       for (i = 0; i < ARRAY_SIZE(mvneta_statistics); i++)
+               *data++ = pp->ethtool_stats[i];
+}
+
+static int mvneta_ethtool_get_sset_count(struct net_device *dev, int sset)
+{
+       if (sset == ETH_SS_STATS)
+               return ARRAY_SIZE(mvneta_statistics);
+       return -EOPNOTSUPP;
+}
+
 static const struct net_device_ops mvneta_netdev_ops = {
        .ndo_open            = mvneta_open,
        .ndo_stop            = mvneta_stop,
@@ -2896,6 +3113,9 @@ const struct ethtool_ops mvneta_eth_tool_ops = {
        .get_drvinfo    = mvneta_ethtool_get_drvinfo,
        .get_ringparam  = mvneta_ethtool_get_ringparam,
        .set_ringparam  = mvneta_ethtool_set_ringparam,
+       .get_strings    = mvneta_ethtool_get_strings,
+       .get_ethtool_stats = mvneta_ethtool_get_stats,
+       .get_sset_count = mvneta_ethtool_get_sset_count,
 };
 
 /* Initialize hw */
@@ -3032,14 +3252,7 @@ static int mvneta_probe(struct platform_device *pdev)
        const char *managed;
        int phy_mode;
        int err;
-
-       /* Our multiqueue support is not complete, so for now, only
-        * allow the usage of the first RX queue
-        */
-       if (rxq_def != 0) {
-               dev_err(&pdev->dev, "Invalid rxq_def argument: %d\n", rxq_def);
-               return -EINVAL;
-       }
+       int cpu;
 
        dev = alloc_etherdev_mqs(sizeof(struct mvneta_port), txq_number, rxq_number);
        if (!dev)
@@ -3091,6 +3304,7 @@ static int mvneta_probe(struct platform_device *pdev)
        err = of_property_read_string(dn, "managed", &managed);
        pp->use_inband_status = (err == 0 &&
                                 strcmp(managed, "in-band-status") == 0);
+       pp->cpu_notifier.notifier_call = mvneta_percpu_notifier;
 
        pp->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(pp->clk)) {
@@ -3107,11 +3321,18 @@ static int mvneta_probe(struct platform_device *pdev)
                goto err_clk;
        }
 
+       /* Alloc per-cpu port structure */
+       pp->ports = alloc_percpu(struct mvneta_pcpu_port);
+       if (!pp->ports) {
+               err = -ENOMEM;
+               goto err_clk;
+       }
+
        /* Alloc per-cpu stats */
        pp->stats = netdev_alloc_pcpu_stats(struct mvneta_pcpu_stats);
        if (!pp->stats) {
                err = -ENOMEM;
-               goto err_clk;
+               goto err_free_ports;
        }
 
        dt_mac_addr = of_get_mac_address(dn);
@@ -3152,7 +3373,12 @@ static int mvneta_probe(struct platform_device *pdev)
        if (dram_target_info)
                mvneta_conf_mbus_windows(pp, dram_target_info);
 
-       netif_napi_add(dev, &pp->napi, mvneta_poll, NAPI_POLL_WEIGHT);
+       for_each_present_cpu(cpu) {
+               struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
+
+               netif_napi_add(dev, &port->napi, mvneta_poll, NAPI_POLL_WEIGHT);
+               port->pp = pp;
+       }
 
        dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
        dev->hw_features |= dev->features;
@@ -3183,6 +3409,8 @@ static int mvneta_probe(struct platform_device *pdev)
 
 err_free_stats:
        free_percpu(pp->stats);
+err_free_ports:
+       free_percpu(pp->ports);
 err_clk:
        clk_disable_unprepare(pp->clk);
 err_put_phy_node:
@@ -3202,6 +3430,7 @@ static int mvneta_remove(struct platform_device *pdev)
 
        unregister_netdev(dev);
        clk_disable_unprepare(pp->clk);
+       free_percpu(pp->ports);
        free_percpu(pp->stats);
        irq_dispose_mapping(dev->irq);
        of_node_put(pp->phy_node);
index d9f4498832a10b400e31b461e3b821d1fe56f714..5606a043063e3a83c5532ea0ebccbaf6fca77259 100644 (file)
@@ -4819,6 +4819,18 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port,
                memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8,
                              ETH_ALEN);
 
+       /* if the address is invalid, use a random value */
+       if (!is_valid_ether_addr(dev->dev_addr)) {
+               struct sockaddr sa = { AF_UNSPEC };
+
+               netdev_warn(dev,
+                           "Invalid MAC address, defaulting to random\n");
+               eth_hw_addr_random(dev);
+               memcpy(sa.sa_data, dev->dev_addr, ETH_ALEN);
+               if (sky2_set_mac_address(dev, &sa))
+                       netdev_warn(dev, "Failed to set MAC address.\n");
+       }
+
        return dev;
 }
 
index 0a3202047569c707a28f62376466e72fdcd8cd00..2177e56ed0be7d18ee40d2428b69003bd6f8cca5 100644 (file)
@@ -2398,7 +2398,7 @@ int mlx4_multi_func_init(struct mlx4_dev *dev)
                        }
                }
 
-               memset(&priv->mfunc.master.cmd_eqe, 0, dev->caps.eqe_size);
+               memset(&priv->mfunc.master.cmd_eqe, 0, sizeof(struct mlx4_eqe));
                priv->mfunc.master.cmd_eqe.type = MLX4_EVENT_TYPE_CMD;
                INIT_WORK(&priv->mfunc.master.comm_work,
                          mlx4_master_comm_channel);
index f79d8124321e525b04de13e7ad1509105b789a2d..ddb5541882f5bfb5dd9086bb015e44cf2648ff99 100644 (file)
@@ -95,9 +95,6 @@ mlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo)
                (u16) (mdev->dev->caps.fw_ver & 0xffff));
        strlcpy(drvinfo->bus_info, pci_name(mdev->dev->persist->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->n_stats = 0;
-       drvinfo->regdump_len = 0;
-       drvinfo->eedump_len = 0;
 }
 
 static const char mlx4_en_priv_flags[][ETH_GSTRING_LEN] = {
index 4726122ea76b296f4b45258cb8ee9358c668ff60..886e1bc86374d990fc92655c7e1b40a3d611b45c 100644 (file)
@@ -573,10 +573,8 @@ static int mlx4_en_get_qp(struct mlx4_en_priv *priv)
 {
        struct mlx4_en_dev *mdev = priv->mdev;
        struct mlx4_dev *dev = mdev->dev;
-       struct mlx4_mac_entry *entry;
        int index = 0;
        int err = 0;
-       u64 reg_id = 0;
        int *qpn = &priv->base_qpn;
        u64 mac = mlx4_mac_to_u64(priv->dev->dev_addr);
 
@@ -600,44 +598,11 @@ static int mlx4_en_get_qp(struct mlx4_en_priv *priv)
        en_dbg(DRV, priv, "Reserved qp %d\n", *qpn);
        if (err) {
                en_err(priv, "Failed to reserve qp for mac registration\n");
-               goto qp_err;
-       }
-
-       err = mlx4_en_uc_steer_add(priv, priv->dev->dev_addr, qpn, &reg_id);
-       if (err)
-               goto steer_err;
-
-       err = mlx4_en_tunnel_steer_add(priv, priv->dev->dev_addr, *qpn,
-                                      &priv->tunnel_reg_id);
-       if (err)
-               goto tunnel_err;
-
-       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
-       if (!entry) {
-               err = -ENOMEM;
-               goto alloc_err;
+               mlx4_unregister_mac(dev, priv->port, mac);
+               return err;
        }
-       memcpy(entry->mac, priv->dev->dev_addr, sizeof(entry->mac));
-       memcpy(priv->current_mac, entry->mac, sizeof(priv->current_mac));
-       entry->reg_id = reg_id;
-
-       hlist_add_head_rcu(&entry->hlist,
-                          &priv->mac_hash[entry->mac[MLX4_EN_MAC_HASH_IDX]]);
 
        return 0;
-
-alloc_err:
-       if (priv->tunnel_reg_id)
-               mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id);
-tunnel_err:
-       mlx4_en_uc_steer_release(priv, priv->dev->dev_addr, *qpn, reg_id);
-
-steer_err:
-       mlx4_qp_release_range(dev, *qpn, 1);
-
-qp_err:
-       mlx4_unregister_mac(dev, priv->port, mac);
-       return err;
 }
 
 static void mlx4_en_put_qp(struct mlx4_en_priv *priv)
@@ -645,39 +610,13 @@ static void mlx4_en_put_qp(struct mlx4_en_priv *priv)
        struct mlx4_en_dev *mdev = priv->mdev;
        struct mlx4_dev *dev = mdev->dev;
        int qpn = priv->base_qpn;
-       u64 mac;
 
        if (dev->caps.steering_mode == MLX4_STEERING_MODE_A0) {
-               mac = mlx4_mac_to_u64(priv->dev->dev_addr);
+               u64 mac = mlx4_mac_to_u64(priv->dev->dev_addr);
                en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n",
                       priv->dev->dev_addr);
                mlx4_unregister_mac(dev, priv->port, mac);
        } else {
-               struct mlx4_mac_entry *entry;
-               struct hlist_node *tmp;
-               struct hlist_head *bucket;
-               unsigned int i;
-
-               for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) {
-                       bucket = &priv->mac_hash[i];
-                       hlist_for_each_entry_safe(entry, tmp, bucket, hlist) {
-                               mac = mlx4_mac_to_u64(entry->mac);
-                               en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n",
-                                      entry->mac);
-                               mlx4_en_uc_steer_release(priv, entry->mac,
-                                                        qpn, entry->reg_id);
-
-                               mlx4_unregister_mac(dev, priv->port, mac);
-                               hlist_del_rcu(&entry->hlist);
-                               kfree_rcu(entry, rcu);
-                       }
-               }
-
-               if (priv->tunnel_reg_id) {
-                       mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id);
-                       priv->tunnel_reg_id = 0;
-               }
-
                en_dbg(DRV, priv, "Releasing qp: port %d, qpn %d\n",
                       priv->port, qpn);
                mlx4_qp_release_range(dev, qpn, 1);
@@ -1283,6 +1222,75 @@ static void mlx4_en_netpoll(struct net_device *dev)
 }
 #endif
 
+static int mlx4_en_set_rss_steer_rules(struct mlx4_en_priv *priv)
+{
+       u64 reg_id;
+       int err = 0;
+       int *qpn = &priv->base_qpn;
+       struct mlx4_mac_entry *entry;
+
+       err = mlx4_en_uc_steer_add(priv, priv->dev->dev_addr, qpn, &reg_id);
+       if (err)
+               return err;
+
+       err = mlx4_en_tunnel_steer_add(priv, priv->dev->dev_addr, *qpn,
+                                      &priv->tunnel_reg_id);
+       if (err)
+               goto tunnel_err;
+
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry) {
+               err = -ENOMEM;
+               goto alloc_err;
+       }
+
+       memcpy(entry->mac, priv->dev->dev_addr, sizeof(entry->mac));
+       memcpy(priv->current_mac, entry->mac, sizeof(priv->current_mac));
+       entry->reg_id = reg_id;
+       hlist_add_head_rcu(&entry->hlist,
+                          &priv->mac_hash[entry->mac[MLX4_EN_MAC_HASH_IDX]]);
+
+       return 0;
+
+alloc_err:
+       if (priv->tunnel_reg_id)
+               mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id);
+
+tunnel_err:
+       mlx4_en_uc_steer_release(priv, priv->dev->dev_addr, *qpn, reg_id);
+       return err;
+}
+
+static void mlx4_en_delete_rss_steer_rules(struct mlx4_en_priv *priv)
+{
+       u64 mac;
+       unsigned int i;
+       int qpn = priv->base_qpn;
+       struct hlist_head *bucket;
+       struct hlist_node *tmp;
+       struct mlx4_mac_entry *entry;
+
+       for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) {
+               bucket = &priv->mac_hash[i];
+               hlist_for_each_entry_safe(entry, tmp, bucket, hlist) {
+                       mac = mlx4_mac_to_u64(entry->mac);
+                       en_dbg(DRV, priv, "Registering MAC:%pM for deleting\n",
+                              entry->mac);
+                       mlx4_en_uc_steer_release(priv, entry->mac,
+                                                qpn, entry->reg_id);
+
+                       mlx4_unregister_mac(priv->mdev->dev, priv->port, mac);
+                       hlist_del_rcu(&entry->hlist);
+                       kfree_rcu(entry, rcu);
+               }
+       }
+
+       if (priv->tunnel_reg_id) {
+               mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id);
+               priv->tunnel_reg_id = 0;
+       }
+}
+
 static void mlx4_en_tx_timeout(struct net_device *dev)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
@@ -1684,6 +1692,11 @@ int mlx4_en_start_port(struct net_device *dev)
                goto tx_err;
        }
 
+       /* Set Unicast and VXLAN steering rules */
+       if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0 &&
+           mlx4_en_set_rss_steer_rules(priv))
+               mlx4_warn(mdev, "Failed setting steering rules\n");
+
        /* Attach rx QP to bradcast address */
        eth_broadcast_addr(&mc_list[10]);
        mc_list[5] = priv->port; /* needed for B0 steering support */
@@ -1831,6 +1844,9 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
        for (i = 0; i < priv->tx_ring_num; i++)
                mlx4_en_free_tx_buf(dev, priv->tx_ring[i]);
 
+       if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0)
+               mlx4_en_delete_rss_steer_rules(priv);
+
        /* Free RSS qps */
        mlx4_en_release_rss_steer(priv);
 
@@ -2800,7 +2816,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
        struct mlx4_en_priv *priv;
        int i;
        int err;
-       u64 mac_u64;
 
        dev = alloc_etherdev_mqs(sizeof(struct mlx4_en_priv),
                                 MAX_TX_RINGS, MAX_RX_RINGS);
@@ -2892,17 +2907,17 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
        dev->addr_len = ETH_ALEN;
        mlx4_en_u64_to_mac(dev->dev_addr, mdev->dev->caps.def_mac[priv->port]);
        if (!is_valid_ether_addr(dev->dev_addr)) {
-               if (mlx4_is_slave(priv->mdev->dev)) {
-                       eth_hw_addr_random(dev);
-                       en_warn(priv, "Assigned random MAC address %pM\n", dev->dev_addr);
-                       mac_u64 = mlx4_mac_to_u64(dev->dev_addr);
-                       mdev->dev->caps.def_mac[priv->port] = mac_u64;
-               } else {
-                       en_err(priv, "Port: %d, invalid mac burned: %pM, quiting\n",
-                              priv->port, dev->dev_addr);
-                       err = -EINVAL;
-                       goto out;
-               }
+               en_err(priv, "Port: %d, invalid mac burned: %pM, quiting\n",
+                      priv->port, dev->dev_addr);
+               err = -EINVAL;
+               goto out;
+       } else if (mlx4_is_slave(priv->mdev->dev) &&
+                  (priv->mdev->dev->port_random_macs & 1 << priv->port)) {
+               /* Random MAC was assigned in mlx4_slave_cap
+                * in mlx4_core module
+                */
+               dev->addr_assign_type |= NET_ADDR_RANDOM;
+               en_warn(priv, "Assigned random MAC address %pM\n", dev->dev_addr);
        }
 
        memcpy(priv->current_mac, dev->dev_addr, sizeof(priv->current_mac));
index 494e7762fdb19efb83d76f187b88fd37d422d5b5..4421bf5463f67159618c3a4c572b4e9ebc04dd7b 100644 (file)
@@ -964,6 +964,8 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
                        tx_desc->ctrl.ins_vlan = MLX4_WQE_CTRL_INS_SVLAN;
                else if (vlan_proto == ETH_P_8021Q)
                        tx_desc->ctrl.ins_vlan = MLX4_WQE_CTRL_INS_CVLAN;
+               else
+                       tx_desc->ctrl.ins_vlan = 0;
 
                tx_desc->ctrl.fence_size = real_size;
 
index c3448847936570582ace12e6baec0f51604f1072..603d1c3d3b2ea1a8a98de110f1e777f1d751e649 100644 (file)
@@ -196,7 +196,7 @@ static void slave_event(struct mlx4_dev *dev, u8 slave, struct mlx4_eqe *eqe)
                return;
        }
 
-       memcpy(s_eqe, eqe, dev->caps.eqe_size - 1);
+       memcpy(s_eqe, eqe, sizeof(struct mlx4_eqe) - 1);
        s_eqe->slave_id = slave;
        /* ensure all information is written before setting the ownersip bit */
        dma_wmb();
index e8ec1dec5789a8d80499e8c478e4822567480284..f13a4d7bbf9597535e5f6271dea3769389bc90b6 100644 (file)
@@ -2840,3 +2840,19 @@ int set_phv_bit(struct mlx4_dev *dev, u8 port, int new_val)
        return -EOPNOTSUPP;
 }
 EXPORT_SYMBOL(set_phv_bit);
+
+void mlx4_replace_zero_macs(struct mlx4_dev *dev)
+{
+       int i;
+       u8 mac_addr[ETH_ALEN];
+
+       dev->port_random_macs = 0;
+       for (i = 1; i <= dev->caps.num_ports; ++i)
+               if (!dev->caps.def_mac[i] &&
+                   dev->caps.port_type[i] == MLX4_PORT_TYPE_ETH) {
+                       eth_random_addr(mac_addr);
+                       dev->port_random_macs |= 1 << i;
+                       dev->caps.def_mac[i] = mlx4_mac_to_u64(mac_addr);
+               }
+}
+EXPORT_SYMBOL_GPL(mlx4_replace_zero_macs);
index cc3a9897574c542ee368ab067c109128ce89b290..85f1b1e7e505727bcdb7f424348d9dce0c5825d3 100644 (file)
@@ -863,6 +863,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
                return -ENODEV;
        }
 
+       mlx4_replace_zero_macs(dev);
+
        dev->caps.qp0_qkey = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL);
        dev->caps.qp0_tunnel = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL);
        dev->caps.qp0_proxy = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL);
index 232b2b55f23b9170b32f351926c200b6ac0e7f1c..e1cf9036af225992c3961254cad0b474ca191e84 100644 (file)
@@ -1378,6 +1378,8 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work);
 
 void mlx4_init_quotas(struct mlx4_dev *dev);
 
+/* for VFs, replace zero MACs with randomly-generated MACs at driver start */
+void mlx4_replace_zero_macs(struct mlx4_dev *dev);
 int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave, int port);
 /* Returns the VF index of slave */
 int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave);
index 78f51e103880d4dcae7745ec5cb5b2425e370e73..93195191f45bf00b09ff34a9db04ec9e0797f586 100644 (file)
@@ -318,7 +318,7 @@ int mlx4_mr_hw_get_mpt(struct mlx4_dev *dev, struct mlx4_mr *mmr,
                                key, NULL);
        } else {
                mailbox = mlx4_alloc_cmd_mailbox(dev);
-               if (IS_ERR_OR_NULL(mailbox))
+               if (IS_ERR(mailbox))
                        return PTR_ERR(mailbox);
 
                err = mlx4_cmd_box(dev, 0, mailbox->dma, key,
index 20268634a9abc7cac1f539612d7499c55d0dc645..3311f35d08e0719381a6e267e8cdf0acf9198b4d 100644 (file)
@@ -422,15 +422,15 @@ int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn,
        u64 qp_mask = 0;
        int err = 0;
 
+       if (!attr || (attr & ~MLX4_UPDATE_QP_SUPPORTED_ATTRS))
+               return -EINVAL;
+
        mailbox = mlx4_alloc_cmd_mailbox(dev);
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
 
        cmd = (struct mlx4_update_qp_context *)mailbox->buf;
 
-       if (!attr || (attr & ~MLX4_UPDATE_QP_SUPPORTED_ATTRS))
-               return -EINVAL;
-
        if (attr & MLX4_UPDATE_QP_SMAC) {
                pri_addr_path_mask |= 1ULL << MLX4_UPD_QP_PATH_MASK_MAC_INDEX;
                cmd->qp_context.pri_path.grh_mylmc = params->smac_index;
index 731423ca575da8583deb8c944a524f728a967bbe..ac4b99ab1f851c41d1fa108dcbe104f4c307cf48 100644 (file)
@@ -1238,8 +1238,10 @@ static int add_res_range(struct mlx4_dev *dev, int slave, u64 base, int count,
        return 0;
 
 undo:
-       for (--i; i >= base; --i)
+       for (--i; i >= 0; --i) {
                rb_erase(&res_arr[i]->node, root);
+               list_del_init(&res_arr[i]->list);
+       }
 
        spin_unlock_irq(mlx4_tlock(dev));
 
index 75ff58dc1ff5f9d9af725f7e5e3285e338b1be8c..fabfc9e0a948dfe8aa90f34f1565d76abe592f3e 100644 (file)
@@ -254,6 +254,156 @@ static void dump_buf(void *buf, int size, int data_only, int offset)
                pr_debug("\n");
 }
 
+enum {
+       MLX5_DRIVER_STATUS_ABORTED = 0xfe,
+       MLX5_DRIVER_SYND = 0xbadd00de,
+};
+
+static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
+                                      u32 *synd, u8 *status)
+{
+       *synd = 0;
+       *status = 0;
+
+       switch (op) {
+       case MLX5_CMD_OP_TEARDOWN_HCA:
+       case MLX5_CMD_OP_DISABLE_HCA:
+       case MLX5_CMD_OP_MANAGE_PAGES:
+       case MLX5_CMD_OP_DESTROY_MKEY:
+       case MLX5_CMD_OP_DESTROY_EQ:
+       case MLX5_CMD_OP_DESTROY_CQ:
+       case MLX5_CMD_OP_DESTROY_QP:
+       case MLX5_CMD_OP_DESTROY_PSV:
+       case MLX5_CMD_OP_DESTROY_SRQ:
+       case MLX5_CMD_OP_DESTROY_XRC_SRQ:
+       case MLX5_CMD_OP_DESTROY_DCT:
+       case MLX5_CMD_OP_DEALLOC_Q_COUNTER:
+       case MLX5_CMD_OP_DEALLOC_PD:
+       case MLX5_CMD_OP_DEALLOC_UAR:
+       case MLX5_CMD_OP_DETTACH_FROM_MCG:
+       case MLX5_CMD_OP_DEALLOC_XRCD:
+       case MLX5_CMD_OP_DEALLOC_TRANSPORT_DOMAIN:
+       case MLX5_CMD_OP_DELETE_VXLAN_UDP_DPORT:
+       case MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY:
+       case MLX5_CMD_OP_DESTROY_TIR:
+       case MLX5_CMD_OP_DESTROY_SQ:
+       case MLX5_CMD_OP_DESTROY_RQ:
+       case MLX5_CMD_OP_DESTROY_RMP:
+       case MLX5_CMD_OP_DESTROY_TIS:
+       case MLX5_CMD_OP_DESTROY_RQT:
+       case MLX5_CMD_OP_DESTROY_FLOW_TABLE:
+       case MLX5_CMD_OP_DESTROY_FLOW_GROUP:
+       case MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY:
+               return MLX5_CMD_STAT_OK;
+
+       case MLX5_CMD_OP_QUERY_HCA_CAP:
+       case MLX5_CMD_OP_QUERY_ADAPTER:
+       case MLX5_CMD_OP_INIT_HCA:
+       case MLX5_CMD_OP_ENABLE_HCA:
+       case MLX5_CMD_OP_QUERY_PAGES:
+       case MLX5_CMD_OP_SET_HCA_CAP:
+       case MLX5_CMD_OP_QUERY_ISSI:
+       case MLX5_CMD_OP_SET_ISSI:
+       case MLX5_CMD_OP_CREATE_MKEY:
+       case MLX5_CMD_OP_QUERY_MKEY:
+       case MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS:
+       case MLX5_CMD_OP_PAGE_FAULT_RESUME:
+       case MLX5_CMD_OP_CREATE_EQ:
+       case MLX5_CMD_OP_QUERY_EQ:
+       case MLX5_CMD_OP_GEN_EQE:
+       case MLX5_CMD_OP_CREATE_CQ:
+       case MLX5_CMD_OP_QUERY_CQ:
+       case MLX5_CMD_OP_MODIFY_CQ:
+       case MLX5_CMD_OP_CREATE_QP:
+       case MLX5_CMD_OP_RST2INIT_QP:
+       case MLX5_CMD_OP_INIT2RTR_QP:
+       case MLX5_CMD_OP_RTR2RTS_QP:
+       case MLX5_CMD_OP_RTS2RTS_QP:
+       case MLX5_CMD_OP_SQERR2RTS_QP:
+       case MLX5_CMD_OP_2ERR_QP:
+       case MLX5_CMD_OP_2RST_QP:
+       case MLX5_CMD_OP_QUERY_QP:
+       case MLX5_CMD_OP_SQD_RTS_QP:
+       case MLX5_CMD_OP_INIT2INIT_QP:
+       case MLX5_CMD_OP_CREATE_PSV:
+       case MLX5_CMD_OP_CREATE_SRQ:
+       case MLX5_CMD_OP_QUERY_SRQ:
+       case MLX5_CMD_OP_ARM_RQ:
+       case MLX5_CMD_OP_CREATE_XRC_SRQ:
+       case MLX5_CMD_OP_QUERY_XRC_SRQ:
+       case MLX5_CMD_OP_ARM_XRC_SRQ:
+       case MLX5_CMD_OP_CREATE_DCT:
+       case MLX5_CMD_OP_DRAIN_DCT:
+       case MLX5_CMD_OP_QUERY_DCT:
+       case MLX5_CMD_OP_ARM_DCT_FOR_KEY_VIOLATION:
+       case MLX5_CMD_OP_QUERY_VPORT_STATE:
+       case MLX5_CMD_OP_MODIFY_VPORT_STATE:
+       case MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT:
+       case MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT:
+       case MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT:
+       case MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT:
+       case MLX5_CMD_OP_QUERY_ROCE_ADDRESS:
+       case MLX5_CMD_OP_SET_ROCE_ADDRESS:
+       case MLX5_CMD_OP_QUERY_HCA_VPORT_CONTEXT:
+       case MLX5_CMD_OP_MODIFY_HCA_VPORT_CONTEXT:
+       case MLX5_CMD_OP_QUERY_HCA_VPORT_GID:
+       case MLX5_CMD_OP_QUERY_HCA_VPORT_PKEY:
+       case MLX5_CMD_OP_QUERY_VPORT_COUNTER:
+       case MLX5_CMD_OP_ALLOC_Q_COUNTER:
+       case MLX5_CMD_OP_QUERY_Q_COUNTER:
+       case MLX5_CMD_OP_ALLOC_PD:
+       case MLX5_CMD_OP_ALLOC_UAR:
+       case MLX5_CMD_OP_CONFIG_INT_MODERATION:
+       case MLX5_CMD_OP_ACCESS_REG:
+       case MLX5_CMD_OP_ATTACH_TO_MCG:
+       case MLX5_CMD_OP_GET_DROPPED_PACKET_LOG:
+       case MLX5_CMD_OP_MAD_IFC:
+       case MLX5_CMD_OP_QUERY_MAD_DEMUX:
+       case MLX5_CMD_OP_SET_MAD_DEMUX:
+       case MLX5_CMD_OP_NOP:
+       case MLX5_CMD_OP_ALLOC_XRCD:
+       case MLX5_CMD_OP_ALLOC_TRANSPORT_DOMAIN:
+       case MLX5_CMD_OP_QUERY_CONG_STATUS:
+       case MLX5_CMD_OP_MODIFY_CONG_STATUS:
+       case MLX5_CMD_OP_QUERY_CONG_PARAMS:
+       case MLX5_CMD_OP_MODIFY_CONG_PARAMS:
+       case MLX5_CMD_OP_QUERY_CONG_STATISTICS:
+       case MLX5_CMD_OP_ADD_VXLAN_UDP_DPORT:
+       case MLX5_CMD_OP_SET_L2_TABLE_ENTRY:
+       case MLX5_CMD_OP_QUERY_L2_TABLE_ENTRY:
+       case MLX5_CMD_OP_CREATE_TIR:
+       case MLX5_CMD_OP_MODIFY_TIR:
+       case MLX5_CMD_OP_QUERY_TIR:
+       case MLX5_CMD_OP_CREATE_SQ:
+       case MLX5_CMD_OP_MODIFY_SQ:
+       case MLX5_CMD_OP_QUERY_SQ:
+       case MLX5_CMD_OP_CREATE_RQ:
+       case MLX5_CMD_OP_MODIFY_RQ:
+       case MLX5_CMD_OP_QUERY_RQ:
+       case MLX5_CMD_OP_CREATE_RMP:
+       case MLX5_CMD_OP_MODIFY_RMP:
+       case MLX5_CMD_OP_QUERY_RMP:
+       case MLX5_CMD_OP_CREATE_TIS:
+       case MLX5_CMD_OP_MODIFY_TIS:
+       case MLX5_CMD_OP_QUERY_TIS:
+       case MLX5_CMD_OP_CREATE_RQT:
+       case MLX5_CMD_OP_MODIFY_RQT:
+       case MLX5_CMD_OP_QUERY_RQT:
+       case MLX5_CMD_OP_CREATE_FLOW_TABLE:
+       case MLX5_CMD_OP_QUERY_FLOW_TABLE:
+       case MLX5_CMD_OP_CREATE_FLOW_GROUP:
+       case MLX5_CMD_OP_QUERY_FLOW_GROUP:
+       case MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY:
+       case MLX5_CMD_OP_QUERY_FLOW_TABLE_ENTRY:
+               *status = MLX5_DRIVER_STATUS_ABORTED;
+               *synd = MLX5_DRIVER_SYND;
+               return -EIO;
+       default:
+               mlx5_core_err(dev, "Unknown FW command (%d)\n", op);
+               return -EINVAL;
+       }
+}
+
 const char *mlx5_command_str(int command)
 {
        switch (command) {
@@ -473,6 +623,7 @@ static void cmd_work_handler(struct work_struct *work)
        struct mlx5_core_dev *dev = container_of(cmd, struct mlx5_core_dev, cmd);
        struct mlx5_cmd_layout *lay;
        struct semaphore *sem;
+       unsigned long flags;
 
        sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem;
        down(sem);
@@ -485,6 +636,9 @@ static void cmd_work_handler(struct work_struct *work)
                }
        } else {
                ent->idx = cmd->max_reg_cmds;
+               spin_lock_irqsave(&cmd->alloc_lock, flags);
+               clear_bit(ent->idx, &cmd->bitmask);
+               spin_unlock_irqrestore(&cmd->alloc_lock, flags);
        }
 
        ent->token = alloc_token(cmd);
@@ -584,6 +738,16 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
        return err;
 }
 
+static __be32 *get_synd_ptr(struct mlx5_outbox_hdr *out)
+{
+       return &out->syndrome;
+}
+
+static u8 *get_status_ptr(struct mlx5_outbox_hdr *out)
+{
+       return &out->status;
+}
+
 /*  Notes:
  *    1. Callback functions may not sleep
  *    2. page queue commands do not support asynchrous completion
@@ -1081,7 +1245,7 @@ static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg)
        }
 }
 
-void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector)
+void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec)
 {
        struct mlx5_cmd *cmd = &dev->cmd;
        struct mlx5_cmd_work_ent *ent;
@@ -1092,7 +1256,10 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector)
        s64 ds;
        struct mlx5_cmd_stats *stats;
        unsigned long flags;
+       unsigned long vector;
 
+       /* there can be at most 32 command queues */
+       vector = vec & 0xffffffff;
        for (i = 0; i < (1 << cmd->log_sz); i++) {
                if (test_bit(i, &vector)) {
                        struct semaphore *sem;
@@ -1110,11 +1277,16 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector)
                                        ent->ret = verify_signature(ent);
                                else
                                        ent->ret = 0;
-                               ent->status = ent->lay->status_own >> 1;
+                               if (vec & MLX5_TRIGGERED_CMD_COMP)
+                                       ent->status = MLX5_DRIVER_STATUS_ABORTED;
+                               else
+                                       ent->status = ent->lay->status_own >> 1;
+
                                mlx5_core_dbg(dev, "command completed. ret 0x%x, delivery status %s(0x%x)\n",
                                              ent->ret, deliv_status_to_str(ent->status), ent->status);
                        }
                        free_ent(cmd, ent->idx);
+
                        if (ent->callback) {
                                ds = ent->ts2 - ent->ts1;
                                if (ent->op < ARRAY_SIZE(cmd->stats)) {
@@ -1136,6 +1308,7 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector)
                                mlx5_free_cmd_msg(dev, ent->out);
                                free_msg(dev, ent->in);
 
+                               err = err ? err : ent->status;
                                free_cmd(ent);
                                callback(err, context);
                        } else {
@@ -1183,6 +1356,11 @@ static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size,
        return msg;
 }
 
+static u16 opcode_from_in(struct mlx5_inbox_hdr *in)
+{
+       return be16_to_cpu(in->opcode);
+}
+
 static int is_manage_pages(struct mlx5_inbox_hdr *in)
 {
        return be16_to_cpu(in->opcode) == MLX5_CMD_OP_MANAGE_PAGES;
@@ -1197,6 +1375,15 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
        gfp_t gfp;
        int err;
        u8 status = 0;
+       u32 drv_synd;
+
+       if (pci_channel_offline(dev->pdev) ||
+           dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
+               err = mlx5_internal_err_ret_value(dev, opcode_from_in(in), &drv_synd, &status);
+               *get_synd_ptr(out) = cpu_to_be32(drv_synd);
+               *get_status_ptr(out) = status;
+               return err;
+       }
 
        pages_queue = is_manage_pages(in);
        gfp = callback ? GFP_ATOMIC : GFP_KERNEL;
@@ -1363,6 +1550,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev)
        int err;
        int i;
 
+       memset(cmd, 0, sizeof(*cmd));
        cmd_if_rev = cmdif_rev(dev);
        if (cmd_if_rev != CMD_IF_REV) {
                dev_err(&dev->pdev->dev,
index 04ab7e445eae080b0888d74022af1222479ea36b..b51e42d6fbecaaed913d592f3bedc490a4dc3ecb 100644 (file)
@@ -242,6 +242,7 @@ int mlx5_init_cq_table(struct mlx5_core_dev *dev)
        struct mlx5_cq_table *table = &dev->priv.cq_table;
        int err;
 
+       memset(table, 0, sizeof(*table));
        spin_lock_init(&table->lock);
        INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
        err = mlx5_cq_debugfs_init(dev);
index 0983a208b299531e2ffb1ebc1aa6c710ed671a31..f2ae62dd8c094ac4c11917f29d65fbb63c2194a2 100644 (file)
@@ -617,5 +617,11 @@ static inline void mlx5e_cq_arm(struct mlx5e_cq *cq)
        mlx5_cq_arm(mcq, MLX5_CQ_DB_REQ_NOT, mcq->uar->map, NULL, cq->wq.cc);
 }
 
+static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev)
+{
+       return min_t(int, mdev->priv.eq_table.num_comp_vectors,
+                    MLX5E_MAX_NUM_CHANNELS);
+}
+
 extern const struct ethtool_ops mlx5e_ethtool_ops;
 u16 mlx5e_get_max_inline_cap(struct mlx5_core_dev *mdev);
index bce912688ca821e5b17f6781c16a0cec713913ef..2e022e9009393eee6e923c7e8b02c8bf24fa2f13 100644 (file)
@@ -345,9 +345,8 @@ static void mlx5e_get_channels(struct net_device *dev,
                               struct ethtool_channels *ch)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
-       int ncv = priv->mdev->priv.eq_table.num_comp_vectors;
 
-       ch->max_combined   = ncv;
+       ch->max_combined   = mlx5e_get_max_num_channels(priv->mdev);
        ch->combined_count = priv->params.num_channels;
 }
 
@@ -355,7 +354,7 @@ static int mlx5e_set_channels(struct net_device *dev,
                              struct ethtool_channels *ch)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
-       int ncv = priv->mdev->priv.eq_table.num_comp_vectors;
+       int ncv = mlx5e_get_max_num_channels(priv->mdev);
        unsigned int count = ch->combined_count;
        bool was_opened;
        int err = 0;
index 59874d666cfff00dc92f23418039f599a6e42077..5fc4d2d78cdf7525c4f469733ee317ae1f0142ca 100644 (file)
@@ -442,12 +442,12 @@ static void mlx5e_disable_rq(struct mlx5e_rq *rq)
 
 static int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq)
 {
+       unsigned long exp_time = jiffies + msecs_to_jiffies(20000);
        struct mlx5e_channel *c = rq->channel;
        struct mlx5e_priv *priv = c->priv;
        struct mlx5_wq_ll *wq = &rq->wq;
-       int i;
 
-       for (i = 0; i < 1000; i++) {
+       while (time_before(jiffies, exp_time)) {
                if (wq->cur_sz >= priv->params.min_rx_wqes)
                        return 0;
 
@@ -1367,13 +1367,13 @@ int mlx5e_open_locked(struct net_device *netdev)
 
        err = mlx5e_set_dev_port_mtu(netdev);
        if (err)
-               return err;
+               goto err_clear_state_opened_flag;
 
        err = mlx5e_open_channels(priv);
        if (err) {
                netdev_err(netdev, "%s: mlx5e_open_channels failed, %d\n",
                           __func__, err);
-               return err;
+               goto err_clear_state_opened_flag;
        }
 
        mlx5e_update_carrier(priv);
@@ -1382,6 +1382,10 @@ int mlx5e_open_locked(struct net_device *netdev)
        schedule_delayed_work(&priv->update_stats_work, 0);
 
        return 0;
+
+err_clear_state_opened_flag:
+       clear_bit(MLX5E_STATE_OPENED, &priv->state);
+       return err;
 }
 
 static int mlx5e_open(struct net_device *netdev)
@@ -1400,6 +1404,12 @@ int mlx5e_close_locked(struct net_device *netdev)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
 
+       /* May already be CLOSED in case a previous configuration operation
+        * (e.g RX/TX queue size change) that involves close&open failed.
+        */
+       if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
+               return 0;
+
        clear_bit(MLX5E_STATE_OPENED, &priv->state);
 
        mlx5e_redirect_rqts(priv);
@@ -1833,7 +1843,7 @@ static int mlx5e_set_features(struct net_device *netdev,
                        mlx5e_disable_vlan_filter(priv);
        }
 
-       return 0;
+       return err;
 }
 
 static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu)
@@ -1994,6 +2004,7 @@ static void mlx5e_build_netdev(struct net_device *netdev)
                netdev->vlan_features    |= NETIF_F_LRO;
 
        netdev->hw_features       = netdev->vlan_features;
+       netdev->hw_features      |= NETIF_F_HW_VLAN_CTAG_TX;
        netdev->hw_features      |= NETIF_F_HW_VLAN_CTAG_RX;
        netdev->hw_features      |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
@@ -2037,8 +2048,7 @@ static void *mlx5e_create_netdev(struct mlx5_core_dev *mdev)
 {
        struct net_device *netdev;
        struct mlx5e_priv *priv;
-       int nch = min_t(int, mdev->priv.eq_table.num_comp_vectors,
-                       MLX5E_MAX_NUM_CHANNELS);
+       int nch = mlx5e_get_max_num_channels(mdev);
        int err;
 
        if (mlx5e_check_required_hca_cap(mdev))
index b73672f32e2c688169c4e37e44bcf0a5cbf065a7..cd8f85a251d7da5509c0651b6ab781ce1d152084 100644 (file)
@@ -116,7 +116,7 @@ static inline u16 mlx5e_get_inline_hdr_size(struct mlx5e_sq *sq,
         * headers and occur before the data gather.
         * Therefore these headers must be copied into the WQE
         */
-#define MLX5E_MIN_INLINE (ETH_HLEN + 2/*vlan tag*/)
+#define MLX5E_MIN_INLINE ETH_HLEN
 
        if (bf && (skb_headlen(skb) <= sq->max_inline))
                return skb_headlen(skb);
@@ -124,6 +124,21 @@ static inline u16 mlx5e_get_inline_hdr_size(struct mlx5e_sq *sq,
        return MLX5E_MIN_INLINE;
 }
 
+static inline void mlx5e_insert_vlan(void *start, struct sk_buff *skb, u16 ihs)
+{
+       struct vlan_ethhdr *vhdr = (struct vlan_ethhdr *)start;
+       int cpy1_sz = 2 * ETH_ALEN;
+       int cpy2_sz = ihs - cpy1_sz;
+
+       skb_copy_from_linear_data(skb, vhdr, cpy1_sz);
+       skb_pull_inline(skb, cpy1_sz);
+       vhdr->h_vlan_proto = skb->vlan_proto;
+       vhdr->h_vlan_TCI = cpu_to_be16(skb_vlan_tag_get(skb));
+       skb_copy_from_linear_data(skb, &vhdr->h_vlan_encapsulated_proto,
+                                 cpy2_sz);
+       skb_pull_inline(skb, cpy2_sz);
+}
+
 static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
 {
        struct mlx5_wq_cyc       *wq   = &sq->wq;
@@ -175,8 +190,13 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
                                                        ETH_ZLEN);
        }
 
-       skb_copy_from_linear_data(skb, eseg->inline_hdr_start, ihs);
-       skb_pull_inline(skb, ihs);
+       if (skb_vlan_tag_present(skb)) {
+               mlx5e_insert_vlan(eseg->inline_hdr_start, skb, ihs);
+               ihs += VLAN_HLEN;
+       } else {
+               skb_copy_from_linear_data(skb, eseg->inline_hdr_start, ihs);
+               skb_pull_inline(skb, ihs);
+       }
 
        eseg->inline_hdr_sz = cpu_to_be16(ihs);
 
index a40b96d4c6621231987ece6fb5364384c4714e4c..713ead583347f1002bd8022ed443466b3e1bf11a 100644 (file)
@@ -346,6 +346,7 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
        int inlen;
 
        eq->nent = roundup_pow_of_two(nent + MLX5_NUM_SPARE_EQE);
+       eq->cons_index = 0;
        err = mlx5_buf_alloc(dev, eq->nent * MLX5_EQE_SIZE, &eq->buf);
        if (err)
                return err;
@@ -381,10 +382,10 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
                 name, pci_name(dev->pdev));
 
        eq->eqn = out.eq_number;
-       eq->irqn = vecidx;
+       eq->irqn = priv->msix_arr[vecidx].vector;
        eq->dev = dev;
        eq->doorbell = uar->map + MLX5_EQ_DOORBEL_OFFSET;
-       err = request_irq(priv->msix_arr[vecidx].vector, mlx5_msix_handler, 0,
+       err = request_irq(eq->irqn, mlx5_msix_handler, 0,
                          priv->irq_info[vecidx].name, eq);
        if (err)
                goto err_eq;
@@ -420,12 +421,12 @@ int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
        int err;
 
        mlx5_debug_eq_remove(dev, eq);
-       free_irq(dev->priv.msix_arr[eq->irqn].vector, eq);
+       free_irq(eq->irqn, eq);
        err = mlx5_cmd_destroy_eq(dev, eq->eqn);
        if (err)
                mlx5_core_warn(dev, "failed to destroy a previously created eq: eqn %d\n",
                               eq->eqn);
-       synchronize_irq(dev->priv.msix_arr[eq->irqn].vector);
+       synchronize_irq(eq->irqn);
        mlx5_buf_free(dev, &eq->buf);
 
        return err;
index 292d76f2a9041105bceb9ae0a792fe4eb05c53d2..f5deb642d0d6c0e693805e34234cbb0cb2e5ccc6 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/module.h>
 #include <linux/random.h>
 #include <linux/vmalloc.h>
+#include <linux/hardirq.h>
 #include <linux/mlx5/driver.h>
 #include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
@@ -46,39 +47,113 @@ enum {
 enum {
        MLX5_HEALTH_SYNDR_FW_ERR                = 0x1,
        MLX5_HEALTH_SYNDR_IRISC_ERR             = 0x7,
+       MLX5_HEALTH_SYNDR_HW_UNRECOVERABLE_ERR  = 0x8,
        MLX5_HEALTH_SYNDR_CRC_ERR               = 0x9,
        MLX5_HEALTH_SYNDR_FETCH_PCI_ERR         = 0xa,
        MLX5_HEALTH_SYNDR_HW_FTL_ERR            = 0xb,
        MLX5_HEALTH_SYNDR_ASYNC_EQ_OVERRUN_ERR  = 0xc,
        MLX5_HEALTH_SYNDR_EQ_ERR                = 0xd,
+       MLX5_HEALTH_SYNDR_EQ_INV                = 0xe,
        MLX5_HEALTH_SYNDR_FFSER_ERR             = 0xf,
+       MLX5_HEALTH_SYNDR_HIGH_TEMP             = 0x10
 };
 
-static DEFINE_SPINLOCK(health_lock);
-static LIST_HEAD(health_list);
-static struct work_struct health_work;
+enum {
+       MLX5_NIC_IFC_FULL               = 0,
+       MLX5_NIC_IFC_DISABLED           = 1,
+       MLX5_NIC_IFC_NO_DRAM_NIC        = 2
+};
 
-static void health_care(struct work_struct *work)
+static u8 get_nic_interface(struct mlx5_core_dev *dev)
 {
-       struct mlx5_core_health *health, *n;
-       struct mlx5_core_dev *dev;
-       struct mlx5_priv *priv;
-       LIST_HEAD(tlist);
+       return (ioread32be(&dev->iseg->cmdq_addr_l_sz) >> 8) & 3;
+}
+
+static void trigger_cmd_completions(struct mlx5_core_dev *dev)
+{
+       unsigned long flags;
+       u64 vector;
 
-       spin_lock_irq(&health_lock);
-       list_splice_init(&health_list, &tlist);
+       /* wait for pending handlers to complete */
+       synchronize_irq(dev->priv.msix_arr[MLX5_EQ_VEC_CMD].vector);
+       spin_lock_irqsave(&dev->cmd.alloc_lock, flags);
+       vector = ~dev->cmd.bitmask & ((1ul << (1 << dev->cmd.log_sz)) - 1);
+       if (!vector)
+               goto no_trig;
+
+       vector |= MLX5_TRIGGERED_CMD_COMP;
+       spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags);
+
+       mlx5_core_dbg(dev, "vector 0x%llx\n", vector);
+       mlx5_cmd_comp_handler(dev, vector);
+       return;
+
+no_trig:
+       spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags);
+}
+
+static int in_fatal(struct mlx5_core_dev *dev)
+{
+       struct mlx5_core_health *health = &dev->priv.health;
+       struct health_buffer __iomem *h = health->health;
 
-       spin_unlock_irq(&health_lock);
+       if (get_nic_interface(dev) == MLX5_NIC_IFC_DISABLED)
+               return 1;
 
-       list_for_each_entry_safe(health, n, &tlist, list) {
-               priv = container_of(health, struct mlx5_priv, health);
-               dev = container_of(priv, struct mlx5_core_dev, priv);
-               mlx5_core_warn(dev, "handling bad device here\n");
-               /* nothing yet */
-               spin_lock_irq(&health_lock);
-               list_del_init(&health->list);
-               spin_unlock_irq(&health_lock);
+       if (ioread32be(&h->fw_ver) == 0xffffffff)
+               return 1;
+
+       return 0;
+}
+
+void mlx5_enter_error_state(struct mlx5_core_dev *dev)
+{
+       if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR)
+               return;
+
+       mlx5_core_err(dev, "start\n");
+       if (pci_channel_offline(dev->pdev) || in_fatal(dev))
+               dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
+
+       mlx5_core_event(dev, MLX5_DEV_EVENT_SYS_ERROR, 0);
+       mlx5_core_err(dev, "end\n");
+}
+
+static void mlx5_handle_bad_state(struct mlx5_core_dev *dev)
+{
+       u8 nic_interface = get_nic_interface(dev);
+
+       switch (nic_interface) {
+       case MLX5_NIC_IFC_FULL:
+               mlx5_core_warn(dev, "Expected to see disabled NIC but it is full driver\n");
+               break;
+
+       case MLX5_NIC_IFC_DISABLED:
+               mlx5_core_warn(dev, "starting teardown\n");
+               break;
+
+       case MLX5_NIC_IFC_NO_DRAM_NIC:
+               mlx5_core_warn(dev, "Expected to see disabled NIC but it is no dram nic\n");
+               break;
+       default:
+               mlx5_core_warn(dev, "Expected to see disabled NIC but it is has invalid value %d\n",
+                              nic_interface);
        }
+
+       mlx5_disable_device(dev);
+}
+
+static void health_care(struct work_struct *work)
+{
+       struct mlx5_core_health *health;
+       struct mlx5_core_dev *dev;
+       struct mlx5_priv *priv;
+
+       health = container_of(work, struct mlx5_core_health, work);
+       priv = container_of(health, struct mlx5_priv, health);
+       dev = container_of(priv, struct mlx5_core_dev, priv);
+       mlx5_core_warn(dev, "handling bad device here\n");
+       mlx5_handle_bad_state(dev);
 }
 
 static const char *hsynd_str(u8 synd)
@@ -88,6 +163,8 @@ static const char *hsynd_str(u8 synd)
                return "firmware internal error";
        case MLX5_HEALTH_SYNDR_IRISC_ERR:
                return "irisc not responding";
+       case MLX5_HEALTH_SYNDR_HW_UNRECOVERABLE_ERR:
+               return "unrecoverable hardware error";
        case MLX5_HEALTH_SYNDR_CRC_ERR:
                return "firmware CRC error";
        case MLX5_HEALTH_SYNDR_FETCH_PCI_ERR:
@@ -98,48 +175,81 @@ static const char *hsynd_str(u8 synd)
                return "async EQ buffer overrun";
        case MLX5_HEALTH_SYNDR_EQ_ERR:
                return "EQ error";
+       case MLX5_HEALTH_SYNDR_EQ_INV:
+               return "Invalid EQ refrenced";
        case MLX5_HEALTH_SYNDR_FFSER_ERR:
                return "FFSER error";
+       case MLX5_HEALTH_SYNDR_HIGH_TEMP:
+               return "High temprature";
        default:
                return "unrecognized error";
        }
 }
 
-static u16 read_be16(__be16 __iomem *p)
+static u16 get_maj(u32 fw)
 {
-       return swab16(readl((__force u16 __iomem *) p));
+       return fw >> 28;
 }
 
-static u32 read_be32(__be32 __iomem *p)
+static u16 get_min(u32 fw)
 {
-       return swab32(readl((__force u32 __iomem *) p));
+       return fw >> 16 & 0xfff;
+}
+
+static u16 get_sub(u32 fw)
+{
+       return fw & 0xffff;
 }
 
 static void print_health_info(struct mlx5_core_dev *dev)
 {
        struct mlx5_core_health *health = &dev->priv.health;
        struct health_buffer __iomem *h = health->health;
+       char fw_str[18];
+       u32 fw;
        int i;
 
+       /* If the syndrom is 0, the device is OK and no need to print buffer */
+       if (!ioread8(&h->synd))
+               return;
+
        for (i = 0; i < ARRAY_SIZE(h->assert_var); i++)
-               pr_info("assert_var[%d] 0x%08x\n", i, read_be32(h->assert_var + i));
+               dev_err(&dev->pdev->dev, "assert_var[%d] 0x%08x\n", i, ioread32be(h->assert_var + i));
+
+       dev_err(&dev->pdev->dev, "assert_exit_ptr 0x%08x\n", ioread32be(&h->assert_exit_ptr));
+       dev_err(&dev->pdev->dev, "assert_callra 0x%08x\n", ioread32be(&h->assert_callra));
+       fw = ioread32be(&h->fw_ver);
+       sprintf(fw_str, "%d.%d.%d", get_maj(fw), get_min(fw), get_sub(fw));
+       dev_err(&dev->pdev->dev, "fw_ver %s\n", fw_str);
+       dev_err(&dev->pdev->dev, "hw_id 0x%08x\n", ioread32be(&h->hw_id));
+       dev_err(&dev->pdev->dev, "irisc_index %d\n", ioread8(&h->irisc_index));
+       dev_err(&dev->pdev->dev, "synd 0x%x: %s\n", ioread8(&h->synd), hsynd_str(ioread8(&h->synd)));
+       dev_err(&dev->pdev->dev, "ext_synd 0x%04x\n", ioread16be(&h->ext_synd));
+}
+
+static unsigned long get_next_poll_jiffies(void)
+{
+       unsigned long next;
 
-       pr_info("assert_exit_ptr 0x%08x\n", read_be32(&h->assert_exit_ptr));
-       pr_info("assert_callra 0x%08x\n", read_be32(&h->assert_callra));
-       pr_info("fw_ver 0x%08x\n", read_be32(&h->fw_ver));
-       pr_info("hw_id 0x%08x\n", read_be32(&h->hw_id));
-       pr_info("irisc_index %d\n", readb(&h->irisc_index));
-       pr_info("synd 0x%x: %s\n", readb(&h->synd), hsynd_str(readb(&h->synd)));
-       pr_info("ext_sync 0x%04x\n", read_be16(&h->ext_sync));
+       get_random_bytes(&next, sizeof(next));
+       next %= HZ;
+       next += jiffies + MLX5_HEALTH_POLL_INTERVAL;
+
+       return next;
 }
 
 static void poll_health(unsigned long data)
 {
        struct mlx5_core_dev *dev = (struct mlx5_core_dev *)data;
        struct mlx5_core_health *health = &dev->priv.health;
-       unsigned long next;
        u32 count;
 
+       if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
+               trigger_cmd_completions(dev);
+               mod_timer(&health->timer, get_next_poll_jiffies());
+               return;
+       }
+
        count = ioread32be(health->health_counter);
        if (count == health->prev)
                ++health->miss_counter;
@@ -148,18 +258,16 @@ static void poll_health(unsigned long data)
 
        health->prev = count;
        if (health->miss_counter == MAX_MISSES) {
-               mlx5_core_err(dev, "device's health compromised\n");
+               dev_err(&dev->pdev->dev, "device's health compromised - reached miss count\n");
                print_health_info(dev);
-               spin_lock_irq(&health_lock);
-               list_add_tail(&health->list, &health_list);
-               spin_unlock_irq(&health_lock);
-
-               queue_work(mlx5_core_wq, &health_work);
        } else {
-               get_random_bytes(&next, sizeof(next));
-               next %= HZ;
-               next += jiffies + MLX5_HEALTH_POLL_INTERVAL;
-               mod_timer(&health->timer, next);
+               mod_timer(&health->timer, get_next_poll_jiffies());
+       }
+
+       if (in_fatal(dev) && !health->sick) {
+               health->sick = true;
+               print_health_info(dev);
+               queue_work(health->wq, &health->work);
        }
 }
 
@@ -167,7 +275,6 @@ void mlx5_start_health_poll(struct mlx5_core_dev *dev)
 {
        struct mlx5_core_health *health = &dev->priv.health;
 
-       INIT_LIST_HEAD(&health->list);
        init_timer(&health->timer);
        health->health = &dev->iseg->health;
        health->health_counter = &dev->iseg->health_counter;
@@ -183,18 +290,33 @@ void mlx5_stop_health_poll(struct mlx5_core_dev *dev)
        struct mlx5_core_health *health = &dev->priv.health;
 
        del_timer_sync(&health->timer);
-
-       spin_lock_irq(&health_lock);
-       if (!list_empty(&health->list))
-               list_del_init(&health->list);
-       spin_unlock_irq(&health_lock);
 }
 
-void mlx5_health_cleanup(void)
+void mlx5_health_cleanup(struct mlx5_core_dev *dev)
 {
+       struct mlx5_core_health *health = &dev->priv.health;
+
+       destroy_workqueue(health->wq);
 }
 
-void  __init mlx5_health_init(void)
+int mlx5_health_init(struct mlx5_core_dev *dev)
 {
-       INIT_WORK(&health_work, health_care);
+       struct mlx5_core_health *health;
+       char *name;
+
+       health = &dev->priv.health;
+       name = kmalloc(64, GFP_KERNEL);
+       if (!name)
+               return -ENOMEM;
+
+       strcpy(name, "mlx5_health");
+       strcat(name, dev_name(&dev->pdev->dev));
+       health->wq = create_singlethread_workqueue(name);
+       kfree(name);
+       if (!health->wq)
+               return -ENOMEM;
+
+       INIT_WORK(&health->work, health_care);
+
+       return 0;
 }
index 03aabdd79abe77f3227630260044c734d8f17ad4..2388aec208fa92fc56da476882b51fd6d0d2b6d9 100644 (file)
 #include <linux/slab.h>
 #include <linux/io-mapping.h>
 #include <linux/interrupt.h>
+#include <linux/delay.h>
 #include <linux/mlx5/driver.h>
 #include <linux/mlx5/cq.h>
 #include <linux/mlx5/qp.h>
 #include <linux/mlx5/srq.h>
 #include <linux/debugfs.h>
 #include <linux/kmod.h>
+#include <linux/delay.h>
 #include <linux/mlx5/mlx5_ifc.h>
 #include "mlx5_core.h"
 
@@ -62,7 +64,6 @@ static int prof_sel = MLX5_DEFAULT_PROF;
 module_param_named(prof_sel, prof_sel, int, 0444);
 MODULE_PARM_DESC(prof_sel, "profile selector. Valid range 0 - 2");
 
-struct workqueue_struct *mlx5_core_wq;
 static LIST_HEAD(intf_list);
 static LIST_HEAD(dev_list);
 static DEFINE_MUTEX(intf_mutex);
@@ -152,6 +153,25 @@ static struct mlx5_profile profile[] = {
        },
 };
 
+#define FW_INIT_TIMEOUT_MILI   2000
+#define FW_INIT_WAIT_MS                2
+
+static int wait_fw_init(struct mlx5_core_dev *dev, u32 max_wait_mili)
+{
+       unsigned long end = jiffies + msecs_to_jiffies(max_wait_mili);
+       int err = 0;
+
+       while (fw_initializing(dev)) {
+               if (time_after(jiffies, end)) {
+                       err = -EBUSY;
+                       break;
+               }
+               msleep(FW_INIT_WAIT_MS);
+       }
+
+       return err;
+}
+
 static int set_dma_caps(struct pci_dev *pdev)
 {
        int err;
@@ -182,6 +202,34 @@ static int set_dma_caps(struct pci_dev *pdev)
        return err;
 }
 
+static int mlx5_pci_enable_device(struct mlx5_core_dev *dev)
+{
+       struct pci_dev *pdev = dev->pdev;
+       int err = 0;
+
+       mutex_lock(&dev->pci_status_mutex);
+       if (dev->pci_status == MLX5_PCI_STATUS_DISABLED) {
+               err = pci_enable_device(pdev);
+               if (!err)
+                       dev->pci_status = MLX5_PCI_STATUS_ENABLED;
+       }
+       mutex_unlock(&dev->pci_status_mutex);
+
+       return err;
+}
+
+static void mlx5_pci_disable_device(struct mlx5_core_dev *dev)
+{
+       struct pci_dev *pdev = dev->pdev;
+
+       mutex_lock(&dev->pci_status_mutex);
+       if (dev->pci_status == MLX5_PCI_STATUS_ENABLED) {
+               pci_disable_device(pdev);
+               dev->pci_status = MLX5_PCI_STATUS_DISABLED;
+       }
+       mutex_unlock(&dev->pci_status_mutex);
+}
+
 static int request_bar(struct pci_dev *pdev)
 {
        int err = 0;
@@ -672,12 +720,126 @@ static void unmap_bf_area(struct mlx5_core_dev *dev)
                io_mapping_free(dev->priv.bf_mapping);
 }
 
-static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev)
+static void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
+{
+       struct mlx5_device_context *dev_ctx;
+       struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv);
+
+       dev_ctx = kmalloc(sizeof(*dev_ctx), GFP_KERNEL);
+       if (!dev_ctx)
+               return;
+
+       dev_ctx->intf    = intf;
+       dev_ctx->context = intf->add(dev);
+
+       if (dev_ctx->context) {
+               spin_lock_irq(&priv->ctx_lock);
+               list_add_tail(&dev_ctx->list, &priv->ctx_list);
+               spin_unlock_irq(&priv->ctx_lock);
+       } else {
+               kfree(dev_ctx);
+       }
+}
+
+static void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
+{
+       struct mlx5_device_context *dev_ctx;
+       struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv);
+
+       list_for_each_entry(dev_ctx, &priv->ctx_list, list)
+               if (dev_ctx->intf == intf) {
+                       spin_lock_irq(&priv->ctx_lock);
+                       list_del(&dev_ctx->list);
+                       spin_unlock_irq(&priv->ctx_lock);
+
+                       intf->remove(dev, dev_ctx->context);
+                       kfree(dev_ctx);
+                       return;
+               }
+}
+
+static int mlx5_register_device(struct mlx5_core_dev *dev)
 {
        struct mlx5_priv *priv = &dev->priv;
-       int err;
+       struct mlx5_interface *intf;
+
+       mutex_lock(&intf_mutex);
+       list_add_tail(&priv->dev_list, &dev_list);
+       list_for_each_entry(intf, &intf_list, list)
+               mlx5_add_device(intf, priv);
+       mutex_unlock(&intf_mutex);
+
+       return 0;
+}
+
+static void mlx5_unregister_device(struct mlx5_core_dev *dev)
+{
+       struct mlx5_priv *priv = &dev->priv;
+       struct mlx5_interface *intf;
+
+       mutex_lock(&intf_mutex);
+       list_for_each_entry(intf, &intf_list, list)
+               mlx5_remove_device(intf, priv);
+       list_del(&priv->dev_list);
+       mutex_unlock(&intf_mutex);
+}
+
+int mlx5_register_interface(struct mlx5_interface *intf)
+{
+       struct mlx5_priv *priv;
+
+       if (!intf->add || !intf->remove)
+               return -EINVAL;
+
+       mutex_lock(&intf_mutex);
+       list_add_tail(&intf->list, &intf_list);
+       list_for_each_entry(priv, &dev_list, dev_list)
+               mlx5_add_device(intf, priv);
+       mutex_unlock(&intf_mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL(mlx5_register_interface);
+
+void mlx5_unregister_interface(struct mlx5_interface *intf)
+{
+       struct mlx5_priv *priv;
+
+       mutex_lock(&intf_mutex);
+       list_for_each_entry(priv, &dev_list, dev_list)
+               mlx5_remove_device(intf, priv);
+       list_del(&intf->list);
+       mutex_unlock(&intf_mutex);
+}
+EXPORT_SYMBOL(mlx5_unregister_interface);
+
+void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol)
+{
+       struct mlx5_priv *priv = &mdev->priv;
+       struct mlx5_device_context *dev_ctx;
+       unsigned long flags;
+       void *result = NULL;
+
+       spin_lock_irqsave(&priv->ctx_lock, flags);
+
+       list_for_each_entry(dev_ctx, &mdev->priv.ctx_list, list)
+               if ((dev_ctx->intf->protocol == protocol) &&
+                   dev_ctx->intf->get_dev) {
+                       result = dev_ctx->intf->get_dev(dev_ctx->context);
+                       break;
+               }
+
+       spin_unlock_irqrestore(&priv->ctx_lock, flags);
+
+       return result;
+}
+EXPORT_SYMBOL(mlx5_get_protocol_dev);
+
+static int mlx5_pci_init(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
+{
+       struct pci_dev *pdev = dev->pdev;
+       int err = 0;
 
-       dev->pdev = pdev;
        pci_set_drvdata(dev->pdev, dev);
        strncpy(priv->name, dev_name(&pdev->dev), MLX5_MAX_NAME_LEN);
        priv->name[MLX5_MAX_NAME_LEN - 1] = 0;
@@ -694,7 +856,7 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev)
        if (!priv->dbg_root)
                return -ENOMEM;
 
-       err = pci_enable_device(pdev);
+       err = mlx5_pci_enable_device(dev);
        if (err) {
                dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n");
                goto err_dbg;
@@ -721,13 +883,61 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev)
                dev_err(&pdev->dev, "Failed mapping initialization segment, aborting\n");
                goto err_clr_master;
        }
+
+       return 0;
+
+err_clr_master:
+       pci_clear_master(dev->pdev);
+       release_bar(dev->pdev);
+err_disable:
+       mlx5_pci_disable_device(dev);
+
+err_dbg:
+       debugfs_remove(priv->dbg_root);
+       return err;
+}
+
+static void mlx5_pci_close(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
+{
+       iounmap(dev->iseg);
+       pci_clear_master(dev->pdev);
+       release_bar(dev->pdev);
+       mlx5_pci_disable_device(dev);
+       debugfs_remove(priv->dbg_root);
+}
+
+#define MLX5_IB_MOD "mlx5_ib"
+static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
+{
+       struct pci_dev *pdev = dev->pdev;
+       int err;
+
+       mutex_lock(&dev->intf_state_mutex);
+       if (dev->interface_state == MLX5_INTERFACE_STATE_UP) {
+               dev_warn(&dev->pdev->dev, "%s: interface is up, NOP\n",
+                        __func__);
+               goto out;
+       }
+
        dev_info(&pdev->dev, "firmware version: %d.%d.%d\n", fw_rev_maj(dev),
                 fw_rev_min(dev), fw_rev_sub(dev));
 
+       /* on load removing any previous indication of internal error, device is
+        * up
+        */
+       dev->state = MLX5_DEVICE_STATE_UP;
+
        err = mlx5_cmd_init(dev);
        if (err) {
                dev_err(&pdev->dev, "Failed initializing command interface, aborting\n");
-               goto err_unmap;
+               goto out_err;
+       }
+
+       err = wait_fw_init(dev, FW_INIT_TIMEOUT_MILI);
+       if (err) {
+               dev_err(&dev->pdev->dev, "Firmware over %d MS in initializing state, aborting\n",
+                       FW_INIT_TIMEOUT_MILI);
+               goto out_err;
        }
 
        mlx5_pagealloc_init(dev);
@@ -842,8 +1052,29 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev)
        mlx5_init_srq_table(dev);
        mlx5_init_mr_table(dev);
 
+       err = mlx5_register_device(dev);
+       if (err) {
+               dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err);
+               goto err_reg_dev;
+       }
+
+       err = request_module_nowait(MLX5_IB_MOD);
+       if (err)
+               pr_info("failed request module on %s\n", MLX5_IB_MOD);
+
+       dev->interface_state = MLX5_INTERFACE_STATE_UP;
+out:
+       mutex_unlock(&dev->intf_state_mutex);
+
        return 0;
 
+err_reg_dev:
+       mlx5_cleanup_mr_table(dev);
+       mlx5_cleanup_srq_table(dev);
+       mlx5_cleanup_qp_table(dev);
+       mlx5_cleanup_cq_table(dev);
+       mlx5_irq_clear_affinity_hints(dev);
+
 err_unmap_bf_area:
        unmap_bf_area(dev);
 
@@ -865,7 +1096,7 @@ err_stop_poll:
        mlx5_stop_health_poll(dev);
        if (mlx5_cmd_teardown_hca(dev)) {
                dev_err(&dev->pdev->dev, "tear_down_hca failed, skip cleanup\n");
-               return err;
+               goto out_err;
        }
 
 err_pagealloc_stop:
@@ -881,25 +1112,25 @@ err_pagealloc_cleanup:
        mlx5_pagealloc_cleanup(dev);
        mlx5_cmd_cleanup(dev);
 
-err_unmap:
-       iounmap(dev->iseg);
-
-err_clr_master:
-       pci_clear_master(dev->pdev);
-       release_bar(dev->pdev);
-
-err_disable:
-       pci_disable_device(dev->pdev);
+out_err:
+       dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
+       mutex_unlock(&dev->intf_state_mutex);
 
-err_dbg:
-       debugfs_remove(priv->dbg_root);
        return err;
 }
 
-static void mlx5_dev_cleanup(struct mlx5_core_dev *dev)
+static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
 {
-       struct mlx5_priv *priv = &dev->priv;
+       int err = 0;
 
+       mutex_lock(&dev->intf_state_mutex);
+       if (dev->interface_state == MLX5_INTERFACE_STATE_DOWN) {
+               dev_warn(&dev->pdev->dev, "%s: interface is down, NOP\n",
+                        __func__);
+               goto out;
+       }
+       mlx5_unregister_device(dev);
+       mlx5_cleanup_mr_table(dev);
        mlx5_cleanup_srq_table(dev);
        mlx5_cleanup_qp_table(dev);
        mlx5_cleanup_cq_table(dev);
@@ -911,139 +1142,25 @@ static void mlx5_dev_cleanup(struct mlx5_core_dev *dev)
        mlx5_eq_cleanup(dev);
        mlx5_disable_msix(dev);
        mlx5_stop_health_poll(dev);
-       if (mlx5_cmd_teardown_hca(dev)) {
+       err = mlx5_cmd_teardown_hca(dev);
+       if (err) {
                dev_err(&dev->pdev->dev, "tear_down_hca failed, skip cleanup\n");
-               return;
+               goto out;
        }
        mlx5_pagealloc_stop(dev);
        mlx5_reclaim_startup_pages(dev);
        mlx5_core_disable_hca(dev);
        mlx5_pagealloc_cleanup(dev);
        mlx5_cmd_cleanup(dev);
-       iounmap(dev->iseg);
-       pci_clear_master(dev->pdev);
-       release_bar(dev->pdev);
-       pci_disable_device(dev->pdev);
-       debugfs_remove(priv->dbg_root);
-}
-
-static void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
-{
-       struct mlx5_device_context *dev_ctx;
-       struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv);
-
-       dev_ctx = kmalloc(sizeof(*dev_ctx), GFP_KERNEL);
-       if (!dev_ctx) {
-               pr_warn("mlx5_add_device: alloc context failed\n");
-               return;
-       }
-
-       dev_ctx->intf    = intf;
-       dev_ctx->context = intf->add(dev);
-
-       if (dev_ctx->context) {
-               spin_lock_irq(&priv->ctx_lock);
-               list_add_tail(&dev_ctx->list, &priv->ctx_list);
-               spin_unlock_irq(&priv->ctx_lock);
-       } else {
-               kfree(dev_ctx);
-       }
-}
-
-static void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
-{
-       struct mlx5_device_context *dev_ctx;
-       struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv);
-
-       list_for_each_entry(dev_ctx, &priv->ctx_list, list)
-               if (dev_ctx->intf == intf) {
-                       spin_lock_irq(&priv->ctx_lock);
-                       list_del(&dev_ctx->list);
-                       spin_unlock_irq(&priv->ctx_lock);
-
-                       intf->remove(dev, dev_ctx->context);
-                       kfree(dev_ctx);
-                       return;
-               }
-}
-static int mlx5_register_device(struct mlx5_core_dev *dev)
-{
-       struct mlx5_priv *priv = &dev->priv;
-       struct mlx5_interface *intf;
-
-       mutex_lock(&intf_mutex);
-       list_add_tail(&priv->dev_list, &dev_list);
-       list_for_each_entry(intf, &intf_list, list)
-               mlx5_add_device(intf, priv);
-       mutex_unlock(&intf_mutex);
-
-       return 0;
-}
-static void mlx5_unregister_device(struct mlx5_core_dev *dev)
-{
-       struct mlx5_priv *priv = &dev->priv;
-       struct mlx5_interface *intf;
-
-       mutex_lock(&intf_mutex);
-       list_for_each_entry(intf, &intf_list, list)
-               mlx5_remove_device(intf, priv);
-       list_del(&priv->dev_list);
-       mutex_unlock(&intf_mutex);
-}
-
-int mlx5_register_interface(struct mlx5_interface *intf)
-{
-       struct mlx5_priv *priv;
-
-       if (!intf->add || !intf->remove)
-               return -EINVAL;
-
-       mutex_lock(&intf_mutex);
-       list_add_tail(&intf->list, &intf_list);
-       list_for_each_entry(priv, &dev_list, dev_list)
-               mlx5_add_device(intf, priv);
-       mutex_unlock(&intf_mutex);
-
-       return 0;
-}
-EXPORT_SYMBOL(mlx5_register_interface);
-
-void mlx5_unregister_interface(struct mlx5_interface *intf)
-{
-       struct mlx5_priv *priv;
-
-       mutex_lock(&intf_mutex);
-       list_for_each_entry(priv, &dev_list, dev_list)
-              mlx5_remove_device(intf, priv);
-       list_del(&intf->list);
-       mutex_unlock(&intf_mutex);
-}
-EXPORT_SYMBOL(mlx5_unregister_interface);
-
-void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol)
-{
-       struct mlx5_priv *priv = &mdev->priv;
-       struct mlx5_device_context *dev_ctx;
-       unsigned long flags;
-       void *result = NULL;
-
-       spin_lock_irqsave(&priv->ctx_lock, flags);
-
-       list_for_each_entry(dev_ctx, &mdev->priv.ctx_list, list)
-               if ((dev_ctx->intf->protocol == protocol) &&
-                   dev_ctx->intf->get_dev) {
-                       result = dev_ctx->intf->get_dev(dev_ctx->context);
-                       break;
-               }
 
-       spin_unlock_irqrestore(&priv->ctx_lock, flags);
-
-       return result;
+out:
+       dev->interface_state = MLX5_INTERFACE_STATE_DOWN;
+       mutex_unlock(&dev->intf_state_mutex);
+       return err;
 }
-EXPORT_SYMBOL(mlx5_get_protocol_dev);
 
-static void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
-                           unsigned long param)
+void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
+                    unsigned long param)
 {
        struct mlx5_priv *priv = &dev->priv;
        struct mlx5_device_context *dev_ctx;
@@ -1064,7 +1181,6 @@ struct mlx5_core_event_handler {
                      void *data);
 };
 
-#define MLX5_IB_MOD "mlx5_ib"
 
 static int init_one(struct pci_dev *pdev,
                    const struct pci_device_id *id)
@@ -1088,43 +1204,166 @@ static int init_one(struct pci_dev *pdev,
                prof_sel = MLX5_DEFAULT_PROF;
        }
        dev->profile = &profile[prof_sel];
+       dev->pdev = pdev;
        dev->event = mlx5_core_event;
 
        INIT_LIST_HEAD(&priv->ctx_list);
        spin_lock_init(&priv->ctx_lock);
-       err = mlx5_dev_init(dev, pdev);
+       mutex_init(&dev->pci_status_mutex);
+       mutex_init(&dev->intf_state_mutex);
+       err = mlx5_pci_init(dev, priv);
        if (err) {
-               dev_err(&pdev->dev, "mlx5_dev_init failed %d\n", err);
-               goto out;
+               dev_err(&pdev->dev, "mlx5_pci_init failed with error code %d\n", err);
+               goto clean_dev;
        }
 
-       err = mlx5_register_device(dev);
+       err = mlx5_health_init(dev);
        if (err) {
-               dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err);
-               goto out_init;
+               dev_err(&pdev->dev, "mlx5_health_init failed with error code %d\n", err);
+               goto close_pci;
        }
 
-       err = request_module_nowait(MLX5_IB_MOD);
-       if (err)
-               pr_info("failed request module on %s\n", MLX5_IB_MOD);
+       err = mlx5_load_one(dev, priv);
+       if (err) {
+               dev_err(&pdev->dev, "mlx5_load_one failed with error code %d\n", err);
+               goto clean_health;
+       }
 
        return 0;
 
-out_init:
-       mlx5_dev_cleanup(dev);
-out:
+clean_health:
+       mlx5_health_cleanup(dev);
+close_pci:
+       mlx5_pci_close(dev, priv);
+clean_dev:
+       pci_set_drvdata(pdev, NULL);
        kfree(dev);
+
        return err;
 }
+
 static void remove_one(struct pci_dev *pdev)
 {
        struct mlx5_core_dev *dev  = pci_get_drvdata(pdev);
+       struct mlx5_priv *priv = &dev->priv;
 
-       mlx5_unregister_device(dev);
-       mlx5_dev_cleanup(dev);
+       if (mlx5_unload_one(dev, priv)) {
+               dev_err(&dev->pdev->dev, "mlx5_unload_one failed\n");
+               mlx5_health_cleanup(dev);
+               return;
+       }
+       mlx5_health_cleanup(dev);
+       mlx5_pci_close(dev, priv);
+       pci_set_drvdata(pdev, NULL);
        kfree(dev);
 }
 
+static pci_ers_result_t mlx5_pci_err_detected(struct pci_dev *pdev,
+                                             pci_channel_state_t state)
+{
+       struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+       struct mlx5_priv *priv = &dev->priv;
+
+       dev_info(&pdev->dev, "%s was called\n", __func__);
+       mlx5_enter_error_state(dev);
+       mlx5_unload_one(dev, priv);
+       mlx5_pci_disable_device(dev);
+       return state == pci_channel_io_perm_failure ?
+               PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t mlx5_pci_slot_reset(struct pci_dev *pdev)
+{
+       struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+       int err = 0;
+
+       dev_info(&pdev->dev, "%s was called\n", __func__);
+
+       err = mlx5_pci_enable_device(dev);
+       if (err) {
+               dev_err(&pdev->dev, "%s: mlx5_pci_enable_device failed with error code: %d\n"
+                       , __func__, err);
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+       pci_set_master(pdev);
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+
+       return err ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
+}
+
+void mlx5_disable_device(struct mlx5_core_dev *dev)
+{
+       mlx5_pci_err_detected(dev->pdev, 0);
+}
+
+/* wait for the device to show vital signs. For now we check
+ * that we can read the device ID and that the health buffer
+ * shows a non zero value which is different than 0xffffffff
+ */
+static void wait_vital(struct pci_dev *pdev)
+{
+       struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+       struct mlx5_core_health *health = &dev->priv.health;
+       const int niter = 100;
+       u32 count;
+       u16 did;
+       int i;
+
+       /* Wait for firmware to be ready after reset */
+       msleep(1000);
+       for (i = 0; i < niter; i++) {
+               if (pci_read_config_word(pdev, 2, &did)) {
+                       dev_warn(&pdev->dev, "failed reading config word\n");
+                       break;
+               }
+               if (did == pdev->device) {
+                       dev_info(&pdev->dev, "device ID correctly read after %d iterations\n", i);
+                       break;
+               }
+               msleep(50);
+       }
+       if (i == niter)
+               dev_warn(&pdev->dev, "%s-%d: could not read device ID\n", __func__, __LINE__);
+
+       for (i = 0; i < niter; i++) {
+               count = ioread32be(health->health_counter);
+               if (count && count != 0xffffffff) {
+                       dev_info(&pdev->dev, "Counter value 0x%x after %d iterations\n", count, i);
+                       break;
+               }
+               msleep(50);
+       }
+
+       if (i == niter)
+               dev_warn(&pdev->dev, "%s-%d: could not read device ID\n", __func__, __LINE__);
+}
+
+static void mlx5_pci_resume(struct pci_dev *pdev)
+{
+       struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+       struct mlx5_priv *priv = &dev->priv;
+       int err;
+
+       dev_info(&pdev->dev, "%s was called\n", __func__);
+
+       pci_save_state(pdev);
+       wait_vital(pdev);
+
+       err = mlx5_load_one(dev, priv);
+       if (err)
+               dev_err(&pdev->dev, "%s: mlx5_load_one failed with error code: %d\n"
+                       , __func__, err);
+       else
+               dev_info(&pdev->dev, "%s: device recovered\n", __func__);
+}
+
+static const struct pci_error_handlers mlx5_err_handler = {
+       .error_detected = mlx5_pci_err_detected,
+       .slot_reset     = mlx5_pci_slot_reset,
+       .resume         = mlx5_pci_resume
+};
+
 static const struct pci_device_id mlx5_core_pci_table[] = {
        { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */
        { PCI_VDEVICE(MELLANOX, 0x1012) }, /* Connect-IB VF */
@@ -1141,7 +1380,8 @@ static struct pci_driver mlx5_core_driver = {
        .name           = DRIVER_NAME,
        .id_table       = mlx5_core_pci_table,
        .probe          = init_one,
-       .remove         = remove_one
+       .remove         = remove_one,
+       .err_handler    = &mlx5_err_handler
 };
 
 static int __init init(void)
@@ -1149,16 +1389,10 @@ static int __init init(void)
        int err;
 
        mlx5_register_debugfs();
-       mlx5_core_wq = create_singlethread_workqueue("mlx5_core_wq");
-       if (!mlx5_core_wq) {
-               err = -ENOMEM;
-               goto err_debug;
-       }
-       mlx5_health_init();
 
        err = pci_register_driver(&mlx5_core_driver);
        if (err)
-               goto err_health;
+               goto err_debug;
 
 #ifdef CONFIG_MLX5_CORE_EN
        mlx5e_init();
@@ -1166,9 +1400,6 @@ static int __init init(void)
 
        return 0;
 
-err_health:
-       mlx5_health_cleanup();
-       destroy_workqueue(mlx5_core_wq);
 err_debug:
        mlx5_unregister_debugfs();
        return err;
@@ -1180,8 +1411,6 @@ static void __exit cleanup(void)
        mlx5e_cleanup();
 #endif
        pci_unregister_driver(&mlx5_core_driver);
-       mlx5_health_cleanup();
-       destroy_workqueue(mlx5_core_wq);
        mlx5_unregister_debugfs();
 }
 
index 566a70488db12ddef46f5623cb77c3e1e4c4ab2c..cee5b7a839bc335fc140ca03eb9aadda09b25900 100644 (file)
 
 extern int mlx5_core_debug_mask;
 
-#define mlx5_core_dbg(dev, format, ...)                                        \
-       pr_debug("%s:%s:%d:(pid %d): " format,                          \
-                (dev)->priv.name, __func__, __LINE__, current->pid,    \
+#define mlx5_core_dbg(__dev, format, ...)                              \
+       dev_dbg(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format,      \
+                (__dev)->priv.name, __func__, __LINE__, current->pid,  \
                 ##__VA_ARGS__)
 
-#define mlx5_core_dbg_mask(dev, mask, format, ...)                     \
+#define mlx5_core_dbg_mask(__dev, mask, format, ...)                   \
 do {                                                                   \
        if ((mask) & mlx5_core_debug_mask)                              \
-               mlx5_core_dbg(dev, format, ##__VA_ARGS__);              \
+               mlx5_core_dbg(__dev, format, ##__VA_ARGS__);            \
 } while (0)
 
-#define mlx5_core_err(dev, format, ...)                                        \
-       pr_err("%s:%s:%d:(pid %d): " format,                            \
-              (dev)->priv.name, __func__, __LINE__, current->pid,      \
+#define mlx5_core_err(__dev, format, ...)                              \
+       dev_err(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format,      \
+              (__dev)->priv.name, __func__, __LINE__, current->pid,    \
               ##__VA_ARGS__)
 
-#define mlx5_core_warn(dev, format, ...)                               \
-       pr_warn("%s:%s:%d:(pid %d): " format,                           \
-               (dev)->priv.name, __func__, __LINE__, current->pid,     \
+#define mlx5_core_warn(__dev, format, ...)                             \
+       dev_warn(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format,     \
+               (__dev)->priv.name, __func__, __LINE__, current->pid,   \
                ##__VA_ARGS__)
 
 enum {
@@ -86,6 +86,10 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev);
 int mlx5_query_board_id(struct mlx5_core_dev *dev);
 int mlx5_cmd_init_hca(struct mlx5_core_dev *dev);
 int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev);
+void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
+                    unsigned long param);
+void mlx5_enter_error_state(struct mlx5_core_dev *dev);
+void mlx5_disable_device(struct mlx5_core_dev *dev);
 
 void mlx5e_init(void);
 void mlx5e_cleanup(void);
index 1adb300dd850691eaafbcf5c5f68212090e585cd..6fa22b51e4607d4f235aeb9078dff67fa03ee4a9 100644 (file)
@@ -40,6 +40,7 @@ void mlx5_init_mr_table(struct mlx5_core_dev *dev)
 {
        struct mlx5_mr_table *table = &dev->priv.mr_table;
 
+       memset(table, 0, sizeof(*table));
        rwlock_init(&table->lock);
        INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
 }
index 8a64542abc16127627374cf4cb065eea017d135f..1cda5d268ec96e27b8121ca4b00147d576b8b48b 100644 (file)
@@ -275,12 +275,36 @@ out_alloc:
 
        return err;
 }
+
+static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id)
+{
+       struct mlx5_manage_pages_inbox *in;
+       struct mlx5_manage_pages_outbox out;
+       int err;
+
+       in = kzalloc(sizeof(*in), GFP_KERNEL);
+       if (!in)
+               return;
+
+       memset(&out, 0, sizeof(out));
+       in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES);
+       in->hdr.opmod = cpu_to_be16(MLX5_PAGES_CANT_GIVE);
+       in->func_id = cpu_to_be16(func_id);
+       err = mlx5_cmd_exec(dev, in, sizeof(*in), &out, sizeof(out));
+       if (!err)
+               err = mlx5_cmd_status_to_err(&out.hdr);
+
+       if (err)
+               mlx5_core_warn(dev, "page notify failed\n");
+
+       kfree(in);
+}
+
 static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
                      int notify_fail)
 {
        struct mlx5_manage_pages_inbox *in;
        struct mlx5_manage_pages_outbox out;
-       struct mlx5_manage_pages_inbox *nin;
        int inlen;
        u64 addr;
        int err;
@@ -289,8 +313,9 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
        inlen = sizeof(*in) + npages * sizeof(in->pas[0]);
        in = mlx5_vzalloc(inlen);
        if (!in) {
+               err = -ENOMEM;
                mlx5_core_warn(dev, "vzalloc failed %d\n", inlen);
-               return -ENOMEM;
+               goto out_free;
        }
        memset(&out, 0, sizeof(out));
 
@@ -316,43 +341,29 @@ retry:
        if (err) {
                mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n",
                               func_id, npages, err);
-               goto out_alloc;
+               goto out_4k;
        }
        dev->priv.fw_pages += npages;
 
-       if (out.hdr.status) {
-               err = mlx5_cmd_status_to_err(&out.hdr);
-               if (err) {
-                       mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n",
-                                      func_id, npages, out.hdr.status);
-                       goto out_alloc;
-               }
+       err = mlx5_cmd_status_to_err(&out.hdr);
+       if (err) {
+               mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n",
+                              func_id, npages, out.hdr.status);
+               goto out_4k;
        }
 
        mlx5_core_dbg(dev, "err %d\n", err);
 
-       goto out_free;
-
-out_alloc:
-       if (notify_fail) {
-               nin = kzalloc(sizeof(*nin), GFP_KERNEL);
-               if (!nin) {
-                       mlx5_core_warn(dev, "allocation failed\n");
-                       goto out_4k;
-               }
-               memset(&out, 0, sizeof(out));
-               nin->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES);
-               nin->hdr.opmod = cpu_to_be16(MLX5_PAGES_CANT_GIVE);
-               if (mlx5_cmd_exec(dev, nin, sizeof(*nin), &out, sizeof(out)))
-                       mlx5_core_warn(dev, "page notify failed\n");
-               kfree(nin);
-       }
+       kvfree(in);
+       return 0;
 
 out_4k:
        for (i--; i >= 0; i--)
                free_4k(dev, be64_to_cpu(in->pas[i]));
 out_free:
        kvfree(in);
+       if (notify_fail)
+               page_notify_fail(dev, func_id);
        return err;
 }
 
@@ -482,15 +493,20 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
        struct fw_page *fwp;
        struct rb_node *p;
        int nclaimed = 0;
-       int err;
+       int err = 0;
 
        do {
                p = rb_first(&dev->priv.page_root);
                if (p) {
                        fwp = rb_entry(p, struct fw_page, rb_node);
-                       err = reclaim_pages(dev, fwp->func_id,
-                                           optimal_reclaimed_pages(),
-                                           &nclaimed);
+                       if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
+                               free_4k(dev, fwp->addr);
+                               nclaimed = 1;
+                       } else {
+                               err = reclaim_pages(dev, fwp->func_id,
+                                                   optimal_reclaimed_pages(),
+                                                   &nclaimed);
+                       }
                        if (err) {
                                mlx5_core_warn(dev, "failed reclaiming pages (%d)\n",
                                               err);
index 3b9480fa3403dee018931c16b678f5ec6c98e913..a87e773e93f3439dbd4cf93bc08a9a7563f4977b 100644 (file)
@@ -90,16 +90,13 @@ int mlx5_set_port_caps(struct mlx5_core_dev *dev, u8 port_num, u32 caps)
 {
        struct mlx5_reg_pcap in;
        struct mlx5_reg_pcap out;
-       int err;
 
        memset(&in, 0, sizeof(in));
        in.caps_127_96 = cpu_to_be32(caps);
        in.port_num = port_num;
 
-       err = mlx5_core_access_reg(dev, &in, sizeof(in), &out,
-                                  sizeof(out), MLX5_REG_PCAP, 0, 1);
-
-       return err;
+       return mlx5_core_access_reg(dev, &in, sizeof(in), &out,
+                                   sizeof(out), MLX5_REG_PCAP, 0, 1);
 }
 EXPORT_SYMBOL_GPL(mlx5_set_port_caps);
 
@@ -107,16 +104,13 @@ int mlx5_query_port_ptys(struct mlx5_core_dev *dev, u32 *ptys,
                         int ptys_size, int proto_mask, u8 local_port)
 {
        u32 in[MLX5_ST_SZ_DW(ptys_reg)];
-       int err;
 
        memset(in, 0, sizeof(in));
        MLX5_SET(ptys_reg, in, local_port, local_port);
        MLX5_SET(ptys_reg, in, proto_mask, proto_mask);
 
-       err = mlx5_core_access_reg(dev, in, sizeof(in), ptys,
-                                  ptys_size, MLX5_REG_PTYS, 0, 0);
-
-       return err;
+       return mlx5_core_access_reg(dev, in, sizeof(in), ptys,
+                                   ptys_size, MLX5_REG_PTYS, 0, 0);
 }
 EXPORT_SYMBOL_GPL(mlx5_query_port_ptys);
 
@@ -199,7 +193,6 @@ int mlx5_set_port_proto(struct mlx5_core_dev *dev, u32 proto_admin,
 {
        u32 in[MLX5_ST_SZ_DW(ptys_reg)];
        u32 out[MLX5_ST_SZ_DW(ptys_reg)];
-       int err;
 
        memset(in, 0, sizeof(in));
 
@@ -210,9 +203,8 @@ int mlx5_set_port_proto(struct mlx5_core_dev *dev, u32 proto_admin,
        else
                MLX5_SET(ptys_reg, in, ib_proto_admin, proto_admin);
 
-       err = mlx5_core_access_reg(dev, in, sizeof(in), out,
-                                  sizeof(out), MLX5_REG_PTYS, 0, 1);
-       return err;
+       return mlx5_core_access_reg(dev, in, sizeof(in), out,
+                                   sizeof(out), MLX5_REG_PTYS, 0, 1);
 }
 EXPORT_SYMBOL_GPL(mlx5_set_port_proto);
 
@@ -250,7 +242,7 @@ int mlx5_query_port_admin_status(struct mlx5_core_dev *dev,
                return err;
 
        *status = MLX5_GET(paos_reg, out, admin_status);
-       return err;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(mlx5_query_port_admin_status);
 
@@ -308,15 +300,12 @@ static int mlx5_query_port_pvlc(struct mlx5_core_dev *dev, u32 *pvlc,
                                int pvlc_size,  u8 local_port)
 {
        u32 in[MLX5_ST_SZ_DW(pvlc_reg)];
-       int err;
 
        memset(in, 0, sizeof(in));
        MLX5_SET(pvlc_reg, in, local_port, local_port);
 
-       err = mlx5_core_access_reg(dev, in, sizeof(in), pvlc,
-                                  pvlc_size, MLX5_REG_PVLC, 0, 0);
-
-       return err;
+       return mlx5_core_access_reg(dev, in, sizeof(in), pvlc,
+                                   pvlc_size, MLX5_REG_PVLC, 0, 0);
 }
 
 int mlx5_query_port_vl_hw_cap(struct mlx5_core_dev *dev,
@@ -339,16 +328,14 @@ int mlx5_set_port_pause(struct mlx5_core_dev *dev, u32 rx_pause, u32 tx_pause)
 {
        u32 in[MLX5_ST_SZ_DW(pfcc_reg)];
        u32 out[MLX5_ST_SZ_DW(pfcc_reg)];
-       int err;
 
        memset(in, 0, sizeof(in));
        MLX5_SET(pfcc_reg, in, local_port, 1);
        MLX5_SET(pfcc_reg, in, pptx, tx_pause);
        MLX5_SET(pfcc_reg, in, pprx, rx_pause);
 
-       err = mlx5_core_access_reg(dev, in, sizeof(in), out,
-                                  sizeof(out), MLX5_REG_PFCC, 0, 1);
-       return err;
+       return mlx5_core_access_reg(dev, in, sizeof(in), out,
+                                   sizeof(out), MLX5_REG_PFCC, 0, 1);
 }
 EXPORT_SYMBOL_GPL(mlx5_set_port_pause);
 
index 8b494b5622631f3ecd5a625be835cd8116c29d50..30e2ba3f5f16cb871fd20b0d05bb99433ea22ab1 100644 (file)
@@ -345,6 +345,7 @@ void mlx5_init_qp_table(struct mlx5_core_dev *dev)
 {
        struct mlx5_qp_table *table = &dev->priv.qp_table;
 
+       memset(table, 0, sizeof(*table));
        spin_lock_init(&table->lock);
        INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
        mlx5_qp_debugfs_init(dev);
index c48f504ccbeba67198dc74696110c7cde5e471c7..ffada801976bff52ef05320d5605f08798af3129 100644 (file)
@@ -531,6 +531,7 @@ void mlx5_init_srq_table(struct mlx5_core_dev *dev)
 {
        struct mlx5_srq_table *table = &dev->priv.srq_table;
 
+       memset(table, 0, sizeof(*table));
        spin_lock_init(&table->lock);
        INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
 }
index b4c87c7b0cf01470ac4ac79a418c174c3f0fc820..d7068f54e80066acdcaf6bf081082d50feeebc57 100644 (file)
@@ -177,7 +177,7 @@ int mlx5_core_modify_tir(struct mlx5_core_dev *dev, u32 tirn, u32 *in,
 
 void mlx5_core_destroy_tir(struct mlx5_core_dev *dev, u32 tirn)
 {
-       u32 in[MLX5_ST_SZ_DW(destroy_tir_out)];
+       u32 in[MLX5_ST_SZ_DW(destroy_tir_in)];
        u32 out[MLX5_ST_SZ_DW(destroy_tir_out)];
 
        memset(in, 0, sizeof(in));
@@ -206,7 +206,7 @@ int mlx5_core_create_tis(struct mlx5_core_dev *dev, u32 *in, int inlen,
 
 void mlx5_core_destroy_tis(struct mlx5_core_dev *dev, u32 tisn)
 {
-       u32 in[MLX5_ST_SZ_DW(destroy_tis_out)];
+       u32 in[MLX5_ST_SZ_DW(destroy_tis_in)];
        u32 out[MLX5_ST_SZ_DW(destroy_tis_out)];
 
        memset(in, 0, sizeof(in));
index 2941d9c5ae486250f340915901464b22cc5aef93..e36e12219c9be2efbf919e9f3427f5dc26e37ce2 100644 (file)
@@ -30,3 +30,14 @@ config MLXSW_SWITCHX2
 
          To compile this driver as a module, choose M here: the
          module will be called mlxsw_switchx2.
+
+config MLXSW_SPECTRUM
+       tristate "Mellanox Technologies Spectrum support"
+       depends on MLXSW_CORE && NET_SWITCHDEV
+       default m
+       ---help---
+         This driver supports Mellanox Technologies Spectrum Ethernet
+         Switch ASICs.
+
+         To compile this driver as a module, choose M here: the
+         module will be called mlxsw_spectrum.
index 0a05f65ee81480f9f976176918e1869cc6a36a72..af015818fd19030b2b699913f75a8368c733745b 100644 (file)
@@ -4,3 +4,6 @@ obj-$(CONFIG_MLXSW_PCI)         += mlxsw_pci.o
 mlxsw_pci-objs                 := pci.o
 obj-$(CONFIG_MLXSW_SWITCHX2)   += mlxsw_switchx2.o
 mlxsw_switchx2-objs            := switchx2.o
+obj-$(CONFIG_MLXSW_SPECTRUM)   += mlxsw_spectrum.o
+mlxsw_spectrum-objs            := spectrum.o spectrum_buffers.o \
+                                  spectrum_switchdev.o
index 770db17eb03f4701228e994cfde910679551c9c5..cd63b82636888a2c7e9a90f41a00a8d96b28ae6f 100644 (file)
@@ -464,6 +464,8 @@ MLXSW_ITEM32(cmd_mbox, query_aq_cap, max_sg_rq, 0x10, 0, 8);
  * passed in this command must be pinned.
  */
 
+#define MLXSW_CMD_MAP_FA_VPM_ENTRIES_MAX 32
+
 static inline int mlxsw_cmd_map_fa(struct mlxsw_core *mlxsw_core,
                                   char *in_mbox, u32 vpm_entries_count)
 {
@@ -568,7 +570,7 @@ MLXSW_ITEM32(cmd_mbox, config_profile, set_max_vlan_groups, 0x0C, 6, 1);
  */
 MLXSW_ITEM32(cmd_mbox, config_profile, set_max_regions, 0x0C, 7, 1);
 
-/* cmd_mbox_config_profile_set_fid_based
+/* cmd_mbox_config_profile_set_flood_mode
  * Capability bit. Setting a bit to 1 configures the profile
  * according to the mailbox contents.
  */
@@ -649,12 +651,8 @@ MLXSW_ITEM32(cmd_mbox, config_profile, max_vlan_groups, 0x28, 0, 12);
 MLXSW_ITEM32(cmd_mbox, config_profile, max_regions, 0x2C, 0, 16);
 
 /* cmd_mbox_config_profile_max_flood_tables
- * Maximum number of Flooding Tables. Flooding Tables are associated to
- * the different packet types for the different switch partitions.
- * Note that the table size depends on the fid_based mode.
- * In SwitchX silicon, tables are split equally between the switch
- * partitions. e.g. for 2 swids and 8 tables, the first 4 are associated
- * with swid-1 and the last 4 are associated with swid-2.
+ * Maximum number of single-entry flooding tables. Different flooding tables
+ * can be associated with different packet types.
  */
 MLXSW_ITEM32(cmd_mbox, config_profile, max_flood_tables, 0x30, 16, 4);
 
@@ -665,15 +663,42 @@ MLXSW_ITEM32(cmd_mbox, config_profile, max_flood_tables, 0x30, 16, 4);
  */
 MLXSW_ITEM32(cmd_mbox, config_profile, max_vid_flood_tables, 0x30, 8, 4);
 
-/* cmd_mbox_config_profile_fid_based
- * FID Based Flood Mode
- * 00 Do not use FID to offset the index into the Port Group Table/Multicast ID
- * 01 Use FID to offset the index to the Port Group Table (pgi)
- * 10 Use FID to offset the index to the Port Group Table (pgi) and
- * the Multicast ID
+/* cmd_mbox_config_profile_flood_mode
+ * Flooding mode to use.
+ * 0-2 - Backward compatible modes for SwitchX devices.
+ * 3 - Mixed mode, where:
+ * max_flood_tables indicates the number of single-entry tables.
+ * max_vid_flood_tables indicates the number of per-VID tables.
+ * max_fid_offset_flood_tables indicates the number of FID-offset tables.
+ * max_fid_flood_tables indicates the number of per-FID tables.
  */
 MLXSW_ITEM32(cmd_mbox, config_profile, flood_mode, 0x30, 0, 2);
 
+/* cmd_mbox_config_profile_max_fid_offset_flood_tables
+ * Maximum number of FID-offset flooding tables.
+ */
+MLXSW_ITEM32(cmd_mbox, config_profile,
+            max_fid_offset_flood_tables, 0x34, 24, 4);
+
+/* cmd_mbox_config_profile_fid_offset_flood_table_size
+ * The size (number of entries) of each FID-offset flood table.
+ */
+MLXSW_ITEM32(cmd_mbox, config_profile,
+            fid_offset_flood_table_size, 0x34, 0, 16);
+
+/* cmd_mbox_config_profile_max_fid_flood_tables
+ * Maximum number of per-FID flooding tables.
+ *
+ * Note: This flooding tables cover special FIDs only (vFIDs), starting at
+ * FID value 4K and higher.
+ */
+MLXSW_ITEM32(cmd_mbox, config_profile, max_fid_flood_tables, 0x38, 24, 4);
+
+/* cmd_mbox_config_profile_fid_flood_table_size
+ * The size (number of entries) of each per-FID table.
+ */
+MLXSW_ITEM32(cmd_mbox, config_profile, fid_flood_table_size, 0x38, 0, 16);
+
 /* cmd_mbox_config_profile_max_ib_mc
  * Maximum number of multicast FDB records for InfiniBand
  * FDB (in 512 chunks) per InfiniBand switch partition.
index 28c19cc1a17c5f44a7b15103ce168651502843d8..97f0d93caf994e25908ec2239a15a2aedd71a8ef 100644 (file)
@@ -287,7 +287,7 @@ static void mlxsw_emad_pack_op_tlv(char *op_tlv,
        mlxsw_emad_op_tlv_status_set(op_tlv, 0);
        mlxsw_emad_op_tlv_register_id_set(op_tlv, reg->id);
        mlxsw_emad_op_tlv_r_set(op_tlv, MLXSW_EMAD_OP_TLV_REQUEST);
-       if (MLXSW_CORE_REG_ACCESS_TYPE_QUERY == type)
+       if (type == MLXSW_CORE_REG_ACCESS_TYPE_QUERY)
                mlxsw_emad_op_tlv_method_set(op_tlv,
                                             MLXSW_EMAD_OP_TLV_METHOD_QUERY);
        else
@@ -362,7 +362,7 @@ static bool mlxsw_emad_is_resp(const struct sk_buff *skb)
        char *op_tlv;
 
        op_tlv = mlxsw_emad_op_tlv(skb);
-       return (MLXSW_EMAD_OP_TLV_RESPONSE == mlxsw_emad_op_tlv_r_get(op_tlv));
+       return (mlxsw_emad_op_tlv_r_get(op_tlv) == MLXSW_EMAD_OP_TLV_RESPONSE);
 }
 
 #define MLXSW_EMAD_TIMEOUT_MS 200
@@ -511,7 +511,6 @@ static int mlxsw_emad_traps_set(struct mlxsw_core *mlxsw_core)
                return err;
 
        mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU,
-                           MLXSW_REG_HTGT_TRAP_GROUP_EMAD,
                            MLXSW_TRAP_ID_ETHEMAD);
        return mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl);
 }
@@ -556,8 +555,8 @@ static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core)
 {
        char hpkt_pl[MLXSW_REG_HPKT_LEN];
 
+       mlxsw_core->emad.use_emad = false;
        mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD,
-                           MLXSW_REG_HTGT_TRAP_GROUP_EMAD,
                            MLXSW_TRAP_ID_ETHEMAD);
        mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl);
 
index 165808471188567613efde6488b34eca38ed1522..807827350a89900b0d25b108c9f894c1d5622be5 100644 (file)
@@ -54,6 +54,7 @@
        MODULE_ALIAS(MLXSW_MODULE_ALIAS_PREFIX kind)
 
 #define MLXSW_DEVICE_KIND_SWITCHX2 "switchx2"
+#define MLXSW_DEVICE_KIND_SPECTRUM "spectrum"
 
 struct mlxsw_core;
 struct mlxsw_driver;
@@ -153,6 +154,10 @@ struct mlxsw_config_profile {
        u8      max_flood_tables;
        u8      max_vid_flood_tables;
        u8      flood_mode;
+       u8      max_fid_offset_flood_tables;
+       u16     fid_offset_flood_table_size;
+       u8      max_fid_flood_tables;
+       u16     fid_flood_table_size;
        u16     max_ib_mc;
        u16     max_pkey;
        u8      ar_sec;
index 36fb1cec53c98e9c7dad0cb7b9b59051d7e0c352..a94dbda6590b36ee8a59a90c36f1ede510a2ae14 100644 (file)
@@ -171,15 +171,21 @@ static inline void __mlxsw_item_set64(char *buf, struct mlxsw_item *item,
 }
 
 static inline void __mlxsw_item_memcpy_from(char *buf, char *dst,
-                                           struct mlxsw_item *item)
+                                           struct mlxsw_item *item,
+                                           unsigned short index)
 {
-       memcpy(dst, &buf[item->offset], item->size.bytes);
+       unsigned int offset = __mlxsw_item_offset(item, index, sizeof(char));
+
+       memcpy(dst, &buf[offset], item->size.bytes);
 }
 
-static inline void __mlxsw_item_memcpy_to(char *buf, char *src,
-                                         struct mlxsw_item *item)
+static inline void __mlxsw_item_memcpy_to(char *buf, const char *src,
+                                         struct mlxsw_item *item,
+                                         unsigned short index)
 {
-       memcpy(&buf[item->offset], src, item->size.bytes);
+       unsigned int offset = __mlxsw_item_offset(item, index, sizeof(char));
+
+       memcpy(&buf[offset], src, item->size.bytes);
 }
 
 static inline u16
@@ -373,12 +379,40 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = {                   \
 static inline void                                                             \
 mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(char *buf, char *dst)                \
 {                                                                              \
-       __mlxsw_item_memcpy_from(buf, dst, &__ITEM_NAME(_type, _cname, _iname));\
+       __mlxsw_item_memcpy_from(buf, dst,                                      \
+                                &__ITEM_NAME(_type, _cname, _iname), 0);       \
+}                                                                              \
+static inline void                                                             \
+mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, const char *src)    \
+{                                                                              \
+       __mlxsw_item_memcpy_to(buf, src,                                        \
+                              &__ITEM_NAME(_type, _cname, _iname), 0);         \
+}
+
+#define MLXSW_ITEM_BUF_INDEXED(_type, _cname, _iname, _offset, _sizebytes,     \
+                              _step, _instepoffset)                            \
+static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = {                        \
+       .offset = _offset,                                                      \
+       .step = _step,                                                          \
+       .in_step_offset = _instepoffset,                                        \
+       .size = {.bytes = _sizebytes,},                                         \
+       .name = #_type "_" #_cname "_" #_iname,                                 \
+};                                                                             \
+static inline void                                                             \
+mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(char *buf,                   \
+                                                 unsigned short index,         \
+                                                 char *dst)                    \
+{                                                                              \
+       __mlxsw_item_memcpy_from(buf, dst,                                      \
+                                &__ITEM_NAME(_type, _cname, _iname), index);   \
 }                                                                              \
 static inline void                                                             \
-mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, char *src)          \
+mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf,                     \
+                                               unsigned short index,           \
+                                               const char *src)                \
 {                                                                              \
-       __mlxsw_item_memcpy_to(buf, src, &__ITEM_NAME(_type, _cname, _iname));  \
+       __mlxsw_item_memcpy_to(buf, src,                                        \
+                              &__ITEM_NAME(_type, _cname, _iname), index);     \
 }
 
 #define MLXSW_ITEM_BIT_ARRAY(_type, _cname, _iname, _offset, _sizebytes,       \
index cef866c37648ca0c93d4f109eb9d4f3671fcd890..de69e719dc9d96ffa149f50c2744325ce68053d6 100644 (file)
@@ -57,6 +57,7 @@ static const char mlxsw_pci_driver_name[] = "mlxsw_pci";
 
 static const struct pci_device_id mlxsw_pci_id_table[] = {
        {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SWITCHX2), 0},
+       {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM), 0},
        {0, }
 };
 
@@ -67,6 +68,8 @@ static const char *mlxsw_pci_device_kind_get(const struct pci_device_id *id)
        switch (id->device) {
        case PCI_DEVICE_ID_MELLANOX_SWITCHX2:
                return MLXSW_DEVICE_KIND_SWITCHX2;
+       case PCI_DEVICE_ID_MELLANOX_SPECTRUM:
+               return MLXSW_DEVICE_KIND_SPECTRUM;
        default:
                BUG();
        }
@@ -171,8 +174,8 @@ struct mlxsw_pci {
        struct msix_entry msix_entry;
        struct mlxsw_core *core;
        struct {
-               u16 num_pages;
                struct mlxsw_pci_mem_item *items;
+               unsigned int count;
        } fw_area;
        struct {
                struct mlxsw_pci_mem_item out_mbox;
@@ -431,8 +434,7 @@ static int mlxsw_pci_wqe_frag_map(struct mlxsw_pci *mlxsw_pci, char *wqe,
 
        mapaddr = pci_map_single(pdev, frag_data, frag_len, direction);
        if (unlikely(pci_dma_mapping_error(pdev, mapaddr))) {
-               if (net_ratelimit())
-                       dev_err(&pdev->dev, "failed to dma map tx frag\n");
+               dev_err_ratelimited(&pdev->dev, "failed to dma map tx frag\n");
                return -EIO;
        }
        mlxsw_pci_wqe_address_set(wqe, index, mapaddr);
@@ -497,6 +499,7 @@ static int mlxsw_pci_rdq_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
                              struct mlxsw_pci_queue *q)
 {
        struct mlxsw_pci_queue_elem_info *elem_info;
+       u8 sdq_count = mlxsw_pci_sdq_count(mlxsw_pci);
        int i;
        int err;
 
@@ -504,9 +507,9 @@ static int mlxsw_pci_rdq_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
        q->consumer_counter = 0;
 
        /* Set CQ of same number of this RDQ with base
-        * above MLXSW_PCI_SDQS_MAX as the lower ones are assigned to SDQs.
+        * above SDQ count as the lower ones are assigned to SDQs.
         */
-       mlxsw_cmd_mbox_sw2hw_dq_cq_set(mbox, q->num + MLXSW_PCI_SDQS_COUNT);
+       mlxsw_cmd_mbox_sw2hw_dq_cq_set(mbox, sdq_count + q->num);
        mlxsw_cmd_mbox_sw2hw_dq_log2_dq_sz_set(mbox, 3); /* 8 pages */
        for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) {
                dma_addr_t mapaddr = __mlxsw_pci_queue_page_get(q, i);
@@ -699,8 +702,8 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci,
 put_new_skb:
        memset(wqe, 0, q->elem_size);
        err = mlxsw_pci_rdq_skb_alloc(mlxsw_pci, elem_info);
-       if (err && net_ratelimit())
-               dev_dbg(&pdev->dev, "Failed to alloc skb for RDQ\n");
+       if (err)
+               dev_dbg_ratelimited(&pdev->dev, "Failed to alloc skb for RDQ\n");
        /* Everything is set up, ring doorbell to pass elem to HW */
        q->producer_counter++;
        mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q);
@@ -830,7 +833,8 @@ static void mlxsw_pci_eq_tasklet(unsigned long data)
 {
        struct mlxsw_pci_queue *q = (struct mlxsw_pci_queue *) data;
        struct mlxsw_pci *mlxsw_pci = q->pci;
-       unsigned long active_cqns[BITS_TO_LONGS(MLXSW_PCI_CQS_COUNT)];
+       u8 cq_count = mlxsw_pci_cq_count(mlxsw_pci);
+       unsigned long active_cqns[BITS_TO_LONGS(MLXSW_PCI_CQS_MAX)];
        char *eqe;
        u8 cqn;
        bool cq_handle = false;
@@ -866,7 +870,7 @@ static void mlxsw_pci_eq_tasklet(unsigned long data)
 
        if (!cq_handle)
                return;
-       for_each_set_bit(cqn, active_cqns, MLXSW_PCI_CQS_COUNT) {
+       for_each_set_bit(cqn, active_cqns, cq_count) {
                q = mlxsw_pci_cq_get(mlxsw_pci, cqn);
                mlxsw_pci_queue_tasklet_schedule(q);
        }
@@ -1067,10 +1071,8 @@ static int mlxsw_pci_aqs_init(struct mlxsw_pci *mlxsw_pci, char *mbox)
        num_eqs = mlxsw_cmd_mbox_query_aq_cap_max_num_eqs_get(mbox);
        eq_log2sz = mlxsw_cmd_mbox_query_aq_cap_log_max_eq_sz_get(mbox);
 
-       if ((num_sdqs != MLXSW_PCI_SDQS_COUNT) ||
-           (num_rdqs != MLXSW_PCI_RDQS_COUNT) ||
-           (num_cqs != MLXSW_PCI_CQS_COUNT) ||
-           (num_eqs != MLXSW_PCI_EQS_COUNT)) {
+       if (num_sdqs + num_rdqs > num_cqs ||
+           num_cqs > MLXSW_PCI_CQS_MAX || num_eqs != MLXSW_PCI_EQS_COUNT) {
                dev_err(&pdev->dev, "Unsupported number of queues\n");
                return -EINVAL;
        }
@@ -1215,6 +1217,14 @@ static int mlxsw_pci_config_profile(struct mlxsw_pci *mlxsw_pci, char *mbox,
                        mbox, profile->max_flood_tables);
                mlxsw_cmd_mbox_config_profile_max_vid_flood_tables_set(
                        mbox, profile->max_vid_flood_tables);
+               mlxsw_cmd_mbox_config_profile_max_fid_offset_flood_tables_set(
+                       mbox, profile->max_fid_offset_flood_tables);
+               mlxsw_cmd_mbox_config_profile_fid_offset_flood_table_size_set(
+                       mbox, profile->fid_offset_flood_table_size);
+               mlxsw_cmd_mbox_config_profile_max_fid_flood_tables_set(
+                       mbox, profile->max_fid_flood_tables);
+               mlxsw_cmd_mbox_config_profile_fid_flood_table_size_set(
+                       mbox, profile->fid_flood_table_size);
        }
        if (profile->used_flood_mode) {
                mlxsw_cmd_mbox_config_profile_set_flood_mode_set(
@@ -1272,6 +1282,7 @@ static int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
                                  u16 num_pages)
 {
        struct mlxsw_pci_mem_item *mem_item;
+       int nent = 0;
        int i;
        int err;
 
@@ -1279,7 +1290,7 @@ static int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
                                           GFP_KERNEL);
        if (!mlxsw_pci->fw_area.items)
                return -ENOMEM;
-       mlxsw_pci->fw_area.num_pages = num_pages;
+       mlxsw_pci->fw_area.count = num_pages;
 
        mlxsw_cmd_mbox_zero(mbox);
        for (i = 0; i < num_pages; i++) {
@@ -1293,13 +1304,22 @@ static int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
                        err = -ENOMEM;
                        goto err_alloc;
                }
-               mlxsw_cmd_mbox_map_fa_pa_set(mbox, i, mem_item->mapaddr);
-               mlxsw_cmd_mbox_map_fa_log2size_set(mbox, i, 0); /* 1 page */
+               mlxsw_cmd_mbox_map_fa_pa_set(mbox, nent, mem_item->mapaddr);
+               mlxsw_cmd_mbox_map_fa_log2size_set(mbox, nent, 0); /* 1 page */
+               if (++nent == MLXSW_CMD_MAP_FA_VPM_ENTRIES_MAX) {
+                       err = mlxsw_cmd_map_fa(mlxsw_pci->core, mbox, nent);
+                       if (err)
+                               goto err_cmd_map_fa;
+                       nent = 0;
+                       mlxsw_cmd_mbox_zero(mbox);
+               }
        }
 
-       err = mlxsw_cmd_map_fa(mlxsw_pci->core, mbox, num_pages);
-       if (err)
-               goto err_cmd_map_fa;
+       if (nent) {
+               err = mlxsw_cmd_map_fa(mlxsw_pci->core, mbox, nent);
+               if (err)
+                       goto err_cmd_map_fa;
+       }
 
        return 0;
 
@@ -1322,7 +1342,7 @@ static void mlxsw_pci_fw_area_fini(struct mlxsw_pci *mlxsw_pci)
 
        mlxsw_cmd_unmap_fa(mlxsw_pci->core);
 
-       for (i = 0; i < mlxsw_pci->fw_area.num_pages; i++) {
+       for (i = 0; i < mlxsw_pci->fw_area.count; i++) {
                mem_item = &mlxsw_pci->fw_area.items[i];
 
                pci_free_consistent(mlxsw_pci->pdev, mem_item->size,
@@ -1642,8 +1662,9 @@ static int mlxsw_pci_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod,
                                                           CIR_OUT_PARAM_LO));
                        memcpy(out_mbox + sizeof(tmp), &tmp, sizeof(tmp));
                }
-       } else if (!err && out_mbox)
+       } else if (!err && out_mbox) {
                memcpy(out_mbox, mlxsw_pci->cmd.out_mbox.buf, out_mbox_size);
+       }
 
        mutex_unlock(&mlxsw_pci->cmd.lock);
 
index 1ef9664b451255bdc7c2d08a1fabffe91256c9aa..142f33d978c5f940fb3ebb0bd721adcab62d8bb0 100644 (file)
@@ -40,6 +40,7 @@
 #include "item.h"
 
 #define PCI_DEVICE_ID_MELLANOX_SWITCHX2        0xc738
+#define PCI_DEVICE_ID_MELLANOX_SPECTRUM        0xcb84
 #define MLXSW_PCI_BAR0_SIZE            (1024 * 1024) /* 1MB */
 #define MLXSW_PCI_PAGE_SIZE            4096
 
@@ -71,9 +72,7 @@
 #define MLXSW_PCI_DOORBELL(offset, type_offset, num)   \
        ((offset) + (type_offset) + (num) * 4)
 
-#define MLXSW_PCI_RDQS_COUNT   24
-#define MLXSW_PCI_SDQS_COUNT   24
-#define MLXSW_PCI_CQS_COUNT    (MLXSW_PCI_RDQS_COUNT + MLXSW_PCI_SDQS_COUNT)
+#define MLXSW_PCI_CQS_MAX      96
 #define MLXSW_PCI_EQS_COUNT    2
 #define MLXSW_PCI_EQ_ASYNC_NUM 0
 #define MLXSW_PCI_EQ_COMP_NUM  1
index 096e1c12175a89481838db432cf9be676d339bb5..236fb5d2ad69c9e051b324a556b2d09ac620f0ec 100644 (file)
@@ -99,57 +99,6 @@ static const struct mlxsw_reg_info mlxsw_reg_spad = {
  */
 MLXSW_ITEM_BUF(reg, spad, base_mac, 0x02, 6);
 
-/* SMID - Switch Multicast ID
- * --------------------------
- * In multi-chip configuration, each device should maintain mapping between
- * Multicast ID (MID) into a list of local ports. This mapping is used in all
- * the devices other than the ingress device, and is implemented as part of the
- * FDB. The MID record maps from a MID, which is a unique identi- fier of the
- * multicast group within the stacking domain, into a list of local ports into
- * which the packet is replicated.
- */
-#define MLXSW_REG_SMID_ID 0x2007
-#define MLXSW_REG_SMID_LEN 0x420
-
-static const struct mlxsw_reg_info mlxsw_reg_smid = {
-       .id = MLXSW_REG_SMID_ID,
-       .len = MLXSW_REG_SMID_LEN,
-};
-
-/* reg_smid_swid
- * Switch partition ID.
- * Access: Index
- */
-MLXSW_ITEM32(reg, smid, swid, 0x00, 24, 8);
-
-/* reg_smid_mid
- * Multicast identifier - global identifier that represents the multicast group
- * across all devices
- * Access: Index
- */
-MLXSW_ITEM32(reg, smid, mid, 0x00, 0, 16);
-
-/* reg_smid_port
- * Local port memebership (1 bit per port).
- * Access: RW
- */
-MLXSW_ITEM_BIT_ARRAY(reg, smid, port, 0x20, 0x20, 1);
-
-/* reg_smid_port_mask
- * Local port mask (1 bit per port).
- * Access: W
- */
-MLXSW_ITEM_BIT_ARRAY(reg, smid, port_mask, 0x220, 0x20, 1);
-
-static inline void mlxsw_reg_smid_pack(char *payload, u16 mid)
-{
-       MLXSW_REG_ZERO(smid, payload);
-       mlxsw_reg_smid_swid_set(payload, 0);
-       mlxsw_reg_smid_mid_set(payload, mid);
-       mlxsw_reg_smid_port_set(payload, MLXSW_PORT_CPU_PORT, 1);
-       mlxsw_reg_smid_port_mask_set(payload, MLXSW_PORT_CPU_PORT, 1);
-}
-
 /* SSPR - Switch System Port Record Register
  * -----------------------------------------
  * Configures the system port to local port mapping.
@@ -208,11 +157,359 @@ static inline void mlxsw_reg_sspr_pack(char *payload, u8 local_port)
        mlxsw_reg_sspr_system_port_set(payload, local_port);
 }
 
+/* SFDAT - Switch Filtering Database Aging Time
+ * --------------------------------------------
+ * Controls the Switch aging time. Aging time is able to be set per Switch
+ * Partition.
+ */
+#define MLXSW_REG_SFDAT_ID 0x2009
+#define MLXSW_REG_SFDAT_LEN 0x8
+
+static const struct mlxsw_reg_info mlxsw_reg_sfdat = {
+       .id = MLXSW_REG_SFDAT_ID,
+       .len = MLXSW_REG_SFDAT_LEN,
+};
+
+/* reg_sfdat_swid
+ * Switch partition ID.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sfdat, swid, 0x00, 24, 8);
+
+/* reg_sfdat_age_time
+ * Aging time in seconds
+ * Min - 10 seconds
+ * Max - 1,000,000 seconds
+ * Default is 300 seconds.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sfdat, age_time, 0x04, 0, 20);
+
+static inline void mlxsw_reg_sfdat_pack(char *payload, u32 age_time)
+{
+       MLXSW_REG_ZERO(sfdat, payload);
+       mlxsw_reg_sfdat_swid_set(payload, 0);
+       mlxsw_reg_sfdat_age_time_set(payload, age_time);
+}
+
+/* SFD - Switch Filtering Database
+ * -------------------------------
+ * The following register defines the access to the filtering database.
+ * The register supports querying, adding, removing and modifying the database.
+ * The access is optimized for bulk updates in which case more than one
+ * FDB record is present in the same command.
+ */
+#define MLXSW_REG_SFD_ID 0x200A
+#define MLXSW_REG_SFD_BASE_LEN 0x10 /* base length, without records */
+#define MLXSW_REG_SFD_REC_LEN 0x10 /* record length */
+#define MLXSW_REG_SFD_REC_MAX_COUNT 64
+#define MLXSW_REG_SFD_LEN (MLXSW_REG_SFD_BASE_LEN +    \
+                          MLXSW_REG_SFD_REC_LEN * MLXSW_REG_SFD_REC_MAX_COUNT)
+
+static const struct mlxsw_reg_info mlxsw_reg_sfd = {
+       .id = MLXSW_REG_SFD_ID,
+       .len = MLXSW_REG_SFD_LEN,
+};
+
+/* reg_sfd_swid
+ * Switch partition ID for queries. Reserved on Write.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sfd, swid, 0x00, 24, 8);
+
+enum mlxsw_reg_sfd_op {
+       /* Dump entire FDB a (process according to record_locator) */
+       MLXSW_REG_SFD_OP_QUERY_DUMP = 0,
+       /* Query records by {MAC, VID/FID} value */
+       MLXSW_REG_SFD_OP_QUERY_QUERY = 1,
+       /* Query and clear activity. Query records by {MAC, VID/FID} value */
+       MLXSW_REG_SFD_OP_QUERY_QUERY_AND_CLEAR_ACTIVITY = 2,
+       /* Test. Response indicates if each of the records could be
+        * added to the FDB.
+        */
+       MLXSW_REG_SFD_OP_WRITE_TEST = 0,
+       /* Add/modify. Aged-out records cannot be added. This command removes
+        * the learning notification of the {MAC, VID/FID}. Response includes
+        * the entries that were added to the FDB.
+        */
+       MLXSW_REG_SFD_OP_WRITE_EDIT = 1,
+       /* Remove record by {MAC, VID/FID}. This command also removes
+        * the learning notification and aged-out notifications
+        * of the {MAC, VID/FID}. The response provides current (pre-removal)
+        * entries as non-aged-out.
+        */
+       MLXSW_REG_SFD_OP_WRITE_REMOVE = 2,
+       /* Remove learned notification by {MAC, VID/FID}. The response provides
+        * the removed learning notification.
+        */
+       MLXSW_REG_SFD_OP_WRITE_REMOVE_NOTIFICATION = 2,
+};
+
+/* reg_sfd_op
+ * Operation.
+ * Access: OP
+ */
+MLXSW_ITEM32(reg, sfd, op, 0x04, 30, 2);
+
+/* reg_sfd_record_locator
+ * Used for querying the FDB. Use record_locator=0 to initiate the
+ * query. When a record is returned, a new record_locator is
+ * returned to be used in the subsequent query.
+ * Reserved for database update.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sfd, record_locator, 0x04, 0, 30);
+
+/* reg_sfd_num_rec
+ * Request: Number of records to read/add/modify/remove
+ * Response: Number of records read/added/replaced/removed
+ * See above description for more details.
+ * Ranges 0..64
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sfd, num_rec, 0x08, 0, 8);
+
+static inline void mlxsw_reg_sfd_pack(char *payload, enum mlxsw_reg_sfd_op op,
+                                     u32 record_locator)
+{
+       MLXSW_REG_ZERO(sfd, payload);
+       mlxsw_reg_sfd_op_set(payload, op);
+       mlxsw_reg_sfd_record_locator_set(payload, record_locator);
+}
+
+/* reg_sfd_rec_swid
+ * Switch partition ID.
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, rec_swid, MLXSW_REG_SFD_BASE_LEN, 24, 8,
+                    MLXSW_REG_SFD_REC_LEN, 0x00, false);
+
+enum mlxsw_reg_sfd_rec_type {
+       MLXSW_REG_SFD_REC_TYPE_UNICAST = 0x0,
+};
+
+/* reg_sfd_rec_type
+ * FDB record type.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, rec_type, MLXSW_REG_SFD_BASE_LEN, 20, 4,
+                    MLXSW_REG_SFD_REC_LEN, 0x00, false);
+
+enum mlxsw_reg_sfd_rec_policy {
+       /* Replacement disabled, aging disabled. */
+       MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY = 0,
+       /* (mlag remote): Replacement enabled, aging disabled,
+        * learning notification enabled on this port.
+        */
+       MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_MLAG = 1,
+       /* (ingress device): Replacement enabled, aging enabled. */
+       MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS = 3,
+};
+
+/* reg_sfd_rec_policy
+ * Policy.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, rec_policy, MLXSW_REG_SFD_BASE_LEN, 18, 2,
+                    MLXSW_REG_SFD_REC_LEN, 0x00, false);
+
+/* reg_sfd_rec_a
+ * Activity. Set for new static entries. Set for static entries if a frame SMAC
+ * lookup hits on the entry.
+ * To clear the a bit, use "query and clear activity" op.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, rec_a, MLXSW_REG_SFD_BASE_LEN, 16, 1,
+                    MLXSW_REG_SFD_REC_LEN, 0x00, false);
+
+/* reg_sfd_rec_mac
+ * MAC address.
+ * Access: Index
+ */
+MLXSW_ITEM_BUF_INDEXED(reg, sfd, rec_mac, MLXSW_REG_SFD_BASE_LEN, 6,
+                      MLXSW_REG_SFD_REC_LEN, 0x02);
+
+enum mlxsw_reg_sfd_rec_action {
+       /* forward */
+       MLXSW_REG_SFD_REC_ACTION_NOP = 0,
+       /* forward and trap, trap_id is FDB_TRAP */
+       MLXSW_REG_SFD_REC_ACTION_MIRROR_TO_CPU = 1,
+       /* trap and do not forward, trap_id is FDB_TRAP */
+       MLXSW_REG_SFD_REC_ACTION_TRAP = 3,
+       MLXSW_REG_SFD_REC_ACTION_DISCARD_ERROR = 15,
+};
+
+/* reg_sfd_rec_action
+ * Action to apply on the packet.
+ * Note: Dynamic entries can only be configured with NOP action.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, rec_action, MLXSW_REG_SFD_BASE_LEN, 28, 4,
+                    MLXSW_REG_SFD_REC_LEN, 0x0C, false);
+
+/* reg_sfd_uc_sub_port
+ * VEPA channel on local port.
+ * Valid only if local port is a non-stacking port. Must be 0 if multichannel
+ * VEPA is not enabled.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, uc_sub_port, MLXSW_REG_SFD_BASE_LEN, 16, 8,
+                    MLXSW_REG_SFD_REC_LEN, 0x08, false);
+
+/* reg_sfd_uc_fid_vid
+ * Filtering ID or VLAN ID
+ * For SwitchX and SwitchX-2:
+ * - Dynamic entries (policy 2,3) use FID
+ * - Static entries (policy 0) use VID
+ * - When independent learning is configured, VID=FID
+ * For Spectrum: use FID for both Dynamic and Static entries.
+ * VID should not be used.
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, uc_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16,
+                    MLXSW_REG_SFD_REC_LEN, 0x08, false);
+
+/* reg_sfd_uc_system_port
+ * Unique port identifier for the final destination of the packet.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, uc_system_port, MLXSW_REG_SFD_BASE_LEN, 0, 16,
+                    MLXSW_REG_SFD_REC_LEN, 0x0C, false);
+
+static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index,
+                                        enum mlxsw_reg_sfd_rec_policy policy,
+                                        const char *mac, u16 vid,
+                                        enum mlxsw_reg_sfd_rec_action action,
+                                        u8 local_port)
+{
+       u8 num_rec = mlxsw_reg_sfd_num_rec_get(payload);
+
+       if (rec_index >= num_rec)
+               mlxsw_reg_sfd_num_rec_set(payload, rec_index + 1);
+       mlxsw_reg_sfd_rec_swid_set(payload, rec_index, 0);
+       mlxsw_reg_sfd_rec_type_set(payload, rec_index,
+                                  MLXSW_REG_SFD_REC_TYPE_UNICAST);
+       mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy);
+       mlxsw_reg_sfd_rec_mac_memcpy_to(payload, rec_index, mac);
+       mlxsw_reg_sfd_uc_sub_port_set(payload, rec_index, 0);
+       mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, vid);
+       mlxsw_reg_sfd_rec_action_set(payload, rec_index, action);
+       mlxsw_reg_sfd_uc_system_port_set(payload, rec_index, local_port);
+}
+
+static inline void mlxsw_reg_sfd_uc_unpack(char *payload, int rec_index,
+                                          char *mac, u16 *p_vid,
+                                          u8 *p_local_port)
+{
+       mlxsw_reg_sfd_rec_mac_memcpy_from(payload, rec_index, mac);
+       *p_vid = mlxsw_reg_sfd_uc_fid_vid_get(payload, rec_index);
+       *p_local_port = mlxsw_reg_sfd_uc_system_port_get(payload, rec_index);
+}
+
+/* SFN - Switch FDB Notification Register
+ * -------------------------------------------
+ * The switch provides notifications on newly learned FDB entries and
+ * aged out entries. The notifications can be polled by software.
+ */
+#define MLXSW_REG_SFN_ID 0x200B
+#define MLXSW_REG_SFN_BASE_LEN 0x10 /* base length, without records */
+#define MLXSW_REG_SFN_REC_LEN 0x10 /* record length */
+#define MLXSW_REG_SFN_REC_MAX_COUNT 64
+#define MLXSW_REG_SFN_LEN (MLXSW_REG_SFN_BASE_LEN +    \
+                          MLXSW_REG_SFN_REC_LEN * MLXSW_REG_SFN_REC_MAX_COUNT)
+
+static const struct mlxsw_reg_info mlxsw_reg_sfn = {
+       .id = MLXSW_REG_SFN_ID,
+       .len = MLXSW_REG_SFN_LEN,
+};
+
+/* reg_sfn_swid
+ * Switch partition ID.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sfn, swid, 0x00, 24, 8);
+
+/* reg_sfn_num_rec
+ * Request: Number of learned notifications and aged-out notification
+ * records requested.
+ * Response: Number of notification records returned (must be smaller
+ * than or equal to the value requested)
+ * Ranges 0..64
+ * Access: OP
+ */
+MLXSW_ITEM32(reg, sfn, num_rec, 0x04, 0, 8);
+
+static inline void mlxsw_reg_sfn_pack(char *payload)
+{
+       MLXSW_REG_ZERO(sfn, payload);
+       mlxsw_reg_sfn_swid_set(payload, 0);
+       mlxsw_reg_sfn_num_rec_set(payload, MLXSW_REG_SFN_REC_MAX_COUNT);
+}
+
+/* reg_sfn_rec_swid
+ * Switch partition ID.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, rec_swid, MLXSW_REG_SFN_BASE_LEN, 24, 8,
+                    MLXSW_REG_SFN_REC_LEN, 0x00, false);
+
+enum mlxsw_reg_sfn_rec_type {
+       /* MAC addresses learned on a regular port. */
+       MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC = 0x5,
+       /* Aged-out MAC address on a regular port */
+       MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC = 0x7,
+};
+
+/* reg_sfn_rec_type
+ * Notification record type.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, rec_type, MLXSW_REG_SFN_BASE_LEN, 20, 4,
+                    MLXSW_REG_SFN_REC_LEN, 0x00, false);
+
+/* reg_sfn_rec_mac
+ * MAC address.
+ * Access: RO
+ */
+MLXSW_ITEM_BUF_INDEXED(reg, sfn, rec_mac, MLXSW_REG_SFN_BASE_LEN, 6,
+                      MLXSW_REG_SFN_REC_LEN, 0x02);
+
+/* reg_sfn_mac_sub_port
+ * VEPA channel on the local port.
+ * 0 if multichannel VEPA is not enabled.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, mac_sub_port, MLXSW_REG_SFN_BASE_LEN, 16, 8,
+                    MLXSW_REG_SFN_REC_LEN, 0x08, false);
+
+/* reg_sfn_mac_fid
+ * Filtering identifier.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, mac_fid, MLXSW_REG_SFN_BASE_LEN, 0, 16,
+                    MLXSW_REG_SFN_REC_LEN, 0x08, false);
+
+/* reg_sfn_mac_system_port
+ * Unique port identifier for the final destination of the packet.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, mac_system_port, MLXSW_REG_SFN_BASE_LEN, 0, 16,
+                    MLXSW_REG_SFN_REC_LEN, 0x0C, false);
+
+static inline void mlxsw_reg_sfn_mac_unpack(char *payload, int rec_index,
+                                           char *mac, u16 *p_vid,
+                                           u8 *p_local_port)
+{
+       mlxsw_reg_sfn_rec_mac_memcpy_from(payload, rec_index, mac);
+       *p_vid = mlxsw_reg_sfn_mac_fid_get(payload, rec_index);
+       *p_local_port = mlxsw_reg_sfn_mac_system_port_get(payload, rec_index);
+}
+
 /* SPMS - Switch Port MSTP/RSTP State Register
  * -------------------------------------------
  * Configures the spanning tree state of a physical port.
  */
-#define MLXSW_REG_SPMS_ID 0x200d
+#define MLXSW_REG_SPMS_ID 0x200D
 #define MLXSW_REG_SPMS_LEN 0x404
 
 static const struct mlxsw_reg_info mlxsw_reg_spms = {
@@ -243,20 +540,166 @@ enum mlxsw_reg_spms_state {
  */
 MLXSW_ITEM_BIT_ARRAY(reg, spms, state, 0x04, 0x400, 2);
 
-static inline void mlxsw_reg_spms_pack(char *payload, u8 local_port, u16 vid,
-                                      enum mlxsw_reg_spms_state state)
+static inline void mlxsw_reg_spms_pack(char *payload, u8 local_port)
 {
        MLXSW_REG_ZERO(spms, payload);
        mlxsw_reg_spms_local_port_set(payload, local_port);
+}
+
+static inline void mlxsw_reg_spms_vid_pack(char *payload, u16 vid,
+                                          enum mlxsw_reg_spms_state state)
+{
        mlxsw_reg_spms_state_set(payload, vid, state);
 }
 
+/* SPVID - Switch Port VID
+ * -----------------------
+ * The switch port VID configures the default VID for a port.
+ */
+#define MLXSW_REG_SPVID_ID 0x200E
+#define MLXSW_REG_SPVID_LEN 0x08
+
+static const struct mlxsw_reg_info mlxsw_reg_spvid = {
+       .id = MLXSW_REG_SPVID_ID,
+       .len = MLXSW_REG_SPVID_LEN,
+};
+
+/* reg_spvid_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, spvid, local_port, 0x00, 16, 8);
+
+/* reg_spvid_sub_port
+ * Virtual port within the physical port.
+ * Should be set to 0 when virtual ports are not enabled on the port.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, spvid, sub_port, 0x00, 8, 8);
+
+/* reg_spvid_pvid
+ * Port default VID
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, spvid, pvid, 0x04, 0, 12);
+
+static inline void mlxsw_reg_spvid_pack(char *payload, u8 local_port, u16 pvid)
+{
+       MLXSW_REG_ZERO(spvid, payload);
+       mlxsw_reg_spvid_local_port_set(payload, local_port);
+       mlxsw_reg_spvid_pvid_set(payload, pvid);
+}
+
+/* SPVM - Switch Port VLAN Membership
+ * ----------------------------------
+ * The Switch Port VLAN Membership register configures the VLAN membership
+ * of a port in a VLAN denoted by VID. VLAN membership is managed per
+ * virtual port. The register can be used to add and remove VID(s) from a port.
+ */
+#define MLXSW_REG_SPVM_ID 0x200F
+#define MLXSW_REG_SPVM_BASE_LEN 0x04 /* base length, without records */
+#define MLXSW_REG_SPVM_REC_LEN 0x04 /* record length */
+#define MLXSW_REG_SPVM_REC_MAX_COUNT 256
+#define MLXSW_REG_SPVM_LEN (MLXSW_REG_SPVM_BASE_LEN +  \
+                   MLXSW_REG_SPVM_REC_LEN * MLXSW_REG_SPVM_REC_MAX_COUNT)
+
+static const struct mlxsw_reg_info mlxsw_reg_spvm = {
+       .id = MLXSW_REG_SPVM_ID,
+       .len = MLXSW_REG_SPVM_LEN,
+};
+
+/* reg_spvm_pt
+ * Priority tagged. If this bit is set, packets forwarded to the port with
+ * untagged VLAN membership (u bit is set) will be tagged with priority tag
+ * (VID=0)
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, spvm, pt, 0x00, 31, 1);
+
+/* reg_spvm_pte
+ * Priority Tagged Update Enable. On Write operations, if this bit is cleared,
+ * the pt bit will NOT be updated. To update the pt bit, pte must be set.
+ * Access: WO
+ */
+MLXSW_ITEM32(reg, spvm, pte, 0x00, 30, 1);
+
+/* reg_spvm_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, spvm, local_port, 0x00, 16, 8);
+
+/* reg_spvm_sub_port
+ * Virtual port within the physical port.
+ * Should be set to 0 when virtual ports are not enabled on the port.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, spvm, sub_port, 0x00, 8, 8);
+
+/* reg_spvm_num_rec
+ * Number of records to update. Each record contains: i, e, u, vid.
+ * Access: OP
+ */
+MLXSW_ITEM32(reg, spvm, num_rec, 0x00, 0, 8);
+
+/* reg_spvm_rec_i
+ * Ingress membership in VLAN ID.
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, spvm, rec_i,
+                    MLXSW_REG_SPVM_BASE_LEN, 14, 1,
+                    MLXSW_REG_SPVM_REC_LEN, 0, false);
+
+/* reg_spvm_rec_e
+ * Egress membership in VLAN ID.
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, spvm, rec_e,
+                    MLXSW_REG_SPVM_BASE_LEN, 13, 1,
+                    MLXSW_REG_SPVM_REC_LEN, 0, false);
+
+/* reg_spvm_rec_u
+ * Untagged - port is an untagged member - egress transmission uses untagged
+ * frames on VID<n>
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, spvm, rec_u,
+                    MLXSW_REG_SPVM_BASE_LEN, 12, 1,
+                    MLXSW_REG_SPVM_REC_LEN, 0, false);
+
+/* reg_spvm_rec_vid
+ * Egress membership in VLAN ID.
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, spvm, rec_vid,
+                    MLXSW_REG_SPVM_BASE_LEN, 0, 12,
+                    MLXSW_REG_SPVM_REC_LEN, 0, false);
+
+static inline void mlxsw_reg_spvm_pack(char *payload, u8 local_port,
+                                      u16 vid_begin, u16 vid_end,
+                                      bool is_member, bool untagged)
+{
+       int size = vid_end - vid_begin + 1;
+       int i;
+
+       MLXSW_REG_ZERO(spvm, payload);
+       mlxsw_reg_spvm_local_port_set(payload, local_port);
+       mlxsw_reg_spvm_num_rec_set(payload, size);
+
+       for (i = 0; i < size; i++) {
+               mlxsw_reg_spvm_rec_i_set(payload, i, is_member);
+               mlxsw_reg_spvm_rec_e_set(payload, i, is_member);
+               mlxsw_reg_spvm_rec_u_set(payload, i, untagged);
+               mlxsw_reg_spvm_rec_vid_set(payload, i, vid_begin + i);
+       }
+}
+
 /* SFGC - Switch Flooding Group Configuration
  * ------------------------------------------
  * The following register controls the association of flooding tables and MIDs
  * to packet types used for flooding.
  */
-#define MLXSW_REG_SFGC_ID  0x2011
+#define MLXSW_REG_SFGC_ID 0x2011
 #define MLXSW_REG_SFGC_LEN 0x10
 
 static const struct mlxsw_reg_info mlxsw_reg_sfgc = {
@@ -265,13 +708,15 @@ static const struct mlxsw_reg_info mlxsw_reg_sfgc = {
 };
 
 enum mlxsw_reg_sfgc_type {
-       MLXSW_REG_SFGC_TYPE_BROADCAST = 0,
-       MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST = 1,
-       MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4 = 2,
-       MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6 = 3,
-       MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP = 5,
-       MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL = 6,
-       MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST = 7,
+       MLXSW_REG_SFGC_TYPE_BROADCAST,
+       MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST,
+       MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4,
+       MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6,
+       MLXSW_REG_SFGC_TYPE_RESERVED,
+       MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP,
+       MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL,
+       MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST,
+       MLXSW_REG_SFGC_TYPE_MAX,
 };
 
 /* reg_sfgc_type
@@ -408,7 +853,7 @@ static inline void mlxsw_reg_sftr_pack(char *payload,
                                       unsigned int flood_table,
                                       unsigned int index,
                                       enum mlxsw_flood_table_type table_type,
-                                      unsigned int range)
+                                      unsigned int range, u8 port, bool set)
 {
        MLXSW_REG_ZERO(sftr, payload);
        mlxsw_reg_sftr_swid_set(payload, 0);
@@ -416,8 +861,8 @@ static inline void mlxsw_reg_sftr_pack(char *payload,
        mlxsw_reg_sftr_index_set(payload, index);
        mlxsw_reg_sftr_table_type_set(payload, table_type);
        mlxsw_reg_sftr_range_set(payload, range);
-       mlxsw_reg_sftr_port_set(payload, MLXSW_PORT_CPU_PORT, 1);
-       mlxsw_reg_sftr_port_mask_set(payload, MLXSW_PORT_CPU_PORT, 1);
+       mlxsw_reg_sftr_port_set(payload, port, set);
+       mlxsw_reg_sftr_port_mask_set(payload, port, 1);
 }
 
 /* SPMLR - Switch Port MAC Learning Register
@@ -473,6 +918,285 @@ static inline void mlxsw_reg_spmlr_pack(char *payload, u8 local_port,
        mlxsw_reg_spmlr_learn_mode_set(payload, mode);
 }
 
+/* SVFA - Switch VID to FID Allocation Register
+ * --------------------------------------------
+ * Controls the VID to FID mapping and {Port, VID} to FID mapping for
+ * virtualized ports.
+ */
+#define MLXSW_REG_SVFA_ID 0x201C
+#define MLXSW_REG_SVFA_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_svfa = {
+       .id = MLXSW_REG_SVFA_ID,
+       .len = MLXSW_REG_SVFA_LEN,
+};
+
+/* reg_svfa_swid
+ * Switch partition ID.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, svfa, swid, 0x00, 24, 8);
+
+/* reg_svfa_local_port
+ * Local port number.
+ * Access: Index
+ *
+ * Note: Reserved for 802.1Q FIDs.
+ */
+MLXSW_ITEM32(reg, svfa, local_port, 0x00, 16, 8);
+
+enum mlxsw_reg_svfa_mt {
+       MLXSW_REG_SVFA_MT_VID_TO_FID,
+       MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
+};
+
+/* reg_svfa_mapping_table
+ * Mapping table:
+ * 0 - VID to FID
+ * 1 - {Port, VID} to FID
+ * Access: Index
+ *
+ * Note: Reserved for SwitchX-2.
+ */
+MLXSW_ITEM32(reg, svfa, mapping_table, 0x00, 8, 3);
+
+/* reg_svfa_v
+ * Valid.
+ * Valid if set.
+ * Access: RW
+ *
+ * Note: Reserved for SwitchX-2.
+ */
+MLXSW_ITEM32(reg, svfa, v, 0x00, 0, 1);
+
+/* reg_svfa_fid
+ * Filtering ID.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, svfa, fid, 0x04, 16, 16);
+
+/* reg_svfa_vid
+ * VLAN ID.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, svfa, vid, 0x04, 0, 12);
+
+/* reg_svfa_counter_set_type
+ * Counter set type for flow counters.
+ * Access: RW
+ *
+ * Note: Reserved for SwitchX-2.
+ */
+MLXSW_ITEM32(reg, svfa, counter_set_type, 0x08, 24, 8);
+
+/* reg_svfa_counter_index
+ * Counter index for flow counters.
+ * Access: RW
+ *
+ * Note: Reserved for SwitchX-2.
+ */
+MLXSW_ITEM32(reg, svfa, counter_index, 0x08, 0, 24);
+
+static inline void mlxsw_reg_svfa_pack(char *payload, u8 local_port,
+                                      enum mlxsw_reg_svfa_mt mt, bool valid,
+                                      u16 fid, u16 vid)
+{
+       MLXSW_REG_ZERO(svfa, payload);
+       local_port = mt == MLXSW_REG_SVFA_MT_VID_TO_FID ? 0 : local_port;
+       mlxsw_reg_svfa_swid_set(payload, 0);
+       mlxsw_reg_svfa_local_port_set(payload, local_port);
+       mlxsw_reg_svfa_mapping_table_set(payload, mt);
+       mlxsw_reg_svfa_v_set(payload, valid);
+       mlxsw_reg_svfa_fid_set(payload, fid);
+       mlxsw_reg_svfa_vid_set(payload, vid);
+}
+
+/* SVPE - Switch Virtual-Port Enabling Register
+ * --------------------------------------------
+ * Enables port virtualization.
+ */
+#define MLXSW_REG_SVPE_ID 0x201E
+#define MLXSW_REG_SVPE_LEN 0x4
+
+static const struct mlxsw_reg_info mlxsw_reg_svpe = {
+       .id = MLXSW_REG_SVPE_ID,
+       .len = MLXSW_REG_SVPE_LEN,
+};
+
+/* reg_svpe_local_port
+ * Local port number
+ * Access: Index
+ *
+ * Note: CPU port is not supported (uses VLAN mode only).
+ */
+MLXSW_ITEM32(reg, svpe, local_port, 0x00, 16, 8);
+
+/* reg_svpe_vp_en
+ * Virtual port enable.
+ * 0 - Disable, VLAN mode (VID to FID).
+ * 1 - Enable, Virtual port mode ({Port, VID} to FID).
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, svpe, vp_en, 0x00, 8, 1);
+
+static inline void mlxsw_reg_svpe_pack(char *payload, u8 local_port,
+                                      bool enable)
+{
+       MLXSW_REG_ZERO(svpe, payload);
+       mlxsw_reg_svpe_local_port_set(payload, local_port);
+       mlxsw_reg_svpe_vp_en_set(payload, enable);
+}
+
+/* SFMR - Switch FID Management Register
+ * -------------------------------------
+ * Creates and configures FIDs.
+ */
+#define MLXSW_REG_SFMR_ID 0x201F
+#define MLXSW_REG_SFMR_LEN 0x18
+
+static const struct mlxsw_reg_info mlxsw_reg_sfmr = {
+       .id = MLXSW_REG_SFMR_ID,
+       .len = MLXSW_REG_SFMR_LEN,
+};
+
+enum mlxsw_reg_sfmr_op {
+       MLXSW_REG_SFMR_OP_CREATE_FID,
+       MLXSW_REG_SFMR_OP_DESTROY_FID,
+};
+
+/* reg_sfmr_op
+ * Operation.
+ * 0 - Create or edit FID.
+ * 1 - Destroy FID.
+ * Access: WO
+ */
+MLXSW_ITEM32(reg, sfmr, op, 0x00, 24, 4);
+
+/* reg_sfmr_fid
+ * Filtering ID.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sfmr, fid, 0x00, 0, 16);
+
+/* reg_sfmr_fid_offset
+ * FID offset.
+ * Used to point into the flooding table selected by SFGC register if
+ * the table is of type FID-Offset. Otherwise, this field is reserved.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sfmr, fid_offset, 0x08, 0, 16);
+
+/* reg_sfmr_vtfp
+ * Valid Tunnel Flood Pointer.
+ * If not set, then nve_tunnel_flood_ptr is reserved and considered NULL.
+ * Access: RW
+ *
+ * Note: Reserved for 802.1Q FIDs.
+ */
+MLXSW_ITEM32(reg, sfmr, vtfp, 0x0C, 31, 1);
+
+/* reg_sfmr_nve_tunnel_flood_ptr
+ * Underlay Flooding and BC Pointer.
+ * Used as a pointer to the first entry of the group based link lists of
+ * flooding or BC entries (for NVE tunnels).
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sfmr, nve_tunnel_flood_ptr, 0x0C, 0, 24);
+
+/* reg_sfmr_vv
+ * VNI Valid.
+ * If not set, then vni is reserved.
+ * Access: RW
+ *
+ * Note: Reserved for 802.1Q FIDs.
+ */
+MLXSW_ITEM32(reg, sfmr, vv, 0x10, 31, 1);
+
+/* reg_sfmr_vni
+ * Virtual Network Identifier.
+ * Access: RW
+ *
+ * Note: A given VNI can only be assigned to one FID.
+ */
+MLXSW_ITEM32(reg, sfmr, vni, 0x10, 0, 24);
+
+static inline void mlxsw_reg_sfmr_pack(char *payload,
+                                      enum mlxsw_reg_sfmr_op op, u16 fid,
+                                      u16 fid_offset)
+{
+       MLXSW_REG_ZERO(sfmr, payload);
+       mlxsw_reg_sfmr_op_set(payload, op);
+       mlxsw_reg_sfmr_fid_set(payload, fid);
+       mlxsw_reg_sfmr_fid_offset_set(payload, fid_offset);
+       mlxsw_reg_sfmr_vtfp_set(payload, false);
+       mlxsw_reg_sfmr_vv_set(payload, false);
+}
+
+/* SPVMLR - Switch Port VLAN MAC Learning Register
+ * -----------------------------------------------
+ * Controls the switch MAC learning policy per {Port, VID}.
+ */
+#define MLXSW_REG_SPVMLR_ID 0x2020
+#define MLXSW_REG_SPVMLR_BASE_LEN 0x04 /* base length, without records */
+#define MLXSW_REG_SPVMLR_REC_LEN 0x04 /* record length */
+#define MLXSW_REG_SPVMLR_REC_MAX_COUNT 256
+#define MLXSW_REG_SPVMLR_LEN (MLXSW_REG_SPVMLR_BASE_LEN + \
+                             MLXSW_REG_SPVMLR_REC_LEN * \
+                             MLXSW_REG_SPVMLR_REC_MAX_COUNT)
+
+static const struct mlxsw_reg_info mlxsw_reg_spvmlr = {
+       .id = MLXSW_REG_SPVMLR_ID,
+       .len = MLXSW_REG_SPVMLR_LEN,
+};
+
+/* reg_spvmlr_local_port
+ * Local ingress port.
+ * Access: Index
+ *
+ * Note: CPU port is not supported.
+ */
+MLXSW_ITEM32(reg, spvmlr, local_port, 0x00, 16, 8);
+
+/* reg_spvmlr_num_rec
+ * Number of records to update.
+ * Access: OP
+ */
+MLXSW_ITEM32(reg, spvmlr, num_rec, 0x00, 0, 8);
+
+/* reg_spvmlr_rec_learn_enable
+ * 0 - Disable learning for {Port, VID}.
+ * 1 - Enable learning for {Port, VID}.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, spvmlr, rec_learn_enable, MLXSW_REG_SPVMLR_BASE_LEN,
+                    31, 1, MLXSW_REG_SPVMLR_REC_LEN, 0x00, false);
+
+/* reg_spvmlr_rec_vid
+ * VLAN ID to be added/removed from port or for querying.
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, spvmlr, rec_vid, MLXSW_REG_SPVMLR_BASE_LEN, 0, 12,
+                    MLXSW_REG_SPVMLR_REC_LEN, 0x00, false);
+
+static inline void mlxsw_reg_spvmlr_pack(char *payload, u8 local_port,
+                                        u16 vid_begin, u16 vid_end,
+                                        bool learn_enable)
+{
+       int num_rec = vid_end - vid_begin + 1;
+       int i;
+
+       WARN_ON(num_rec < 1 || num_rec > MLXSW_REG_SPVMLR_REC_MAX_COUNT);
+
+       MLXSW_REG_ZERO(spvmlr, payload);
+       mlxsw_reg_spvmlr_local_port_set(payload, local_port);
+       mlxsw_reg_spvmlr_num_rec_set(payload, num_rec);
+
+       for (i = 0; i < num_rec; i++) {
+               mlxsw_reg_spvmlr_rec_learn_enable_set(payload, i, learn_enable);
+               mlxsw_reg_spvmlr_rec_vid_set(payload, i, vid_begin + i);
+       }
+}
+
 /* PMLP - Ports Module to Local Port Register
  * ------------------------------------------
  * Configures the assignment of modules to local ports.
@@ -1008,12 +1732,88 @@ static inline void mlxsw_reg_ppcnt_pack(char *payload, u8 local_port)
        mlxsw_reg_ppcnt_prio_tc_set(payload, 0);
 }
 
+/* PBMC - Port Buffer Management Control Register
+ * ----------------------------------------------
+ * The PBMC register configures and retrieves the port packet buffer
+ * allocation for different Prios, and the Pause threshold management.
+ */
+#define MLXSW_REG_PBMC_ID 0x500C
+#define MLXSW_REG_PBMC_LEN 0x68
+
+static const struct mlxsw_reg_info mlxsw_reg_pbmc = {
+       .id = MLXSW_REG_PBMC_ID,
+       .len = MLXSW_REG_PBMC_LEN,
+};
+
+/* reg_pbmc_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, pbmc, local_port, 0x00, 16, 8);
+
+/* reg_pbmc_xoff_timer_value
+ * When device generates a pause frame, it uses this value as the pause
+ * timer (time for the peer port to pause in quota-512 bit time).
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, pbmc, xoff_timer_value, 0x04, 16, 16);
+
+/* reg_pbmc_xoff_refresh
+ * The time before a new pause frame should be sent to refresh the pause RW
+ * state. Using the same units as xoff_timer_value above (in quota-512 bit
+ * time).
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, pbmc, xoff_refresh, 0x04, 0, 16);
+
+/* reg_pbmc_buf_lossy
+ * The field indicates if the buffer is lossy.
+ * 0 - Lossless
+ * 1 - Lossy
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, pbmc, buf_lossy, 0x0C, 25, 1, 0x08, 0x00, false);
+
+/* reg_pbmc_buf_epsb
+ * Eligible for Port Shared buffer.
+ * If epsb is set, packets assigned to buffer are allowed to insert the port
+ * shared buffer.
+ * When buf_lossy is MLXSW_REG_PBMC_LOSSY_LOSSY this field is reserved.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, pbmc, buf_epsb, 0x0C, 24, 1, 0x08, 0x00, false);
+
+/* reg_pbmc_buf_size
+ * The part of the packet buffer array is allocated for the specific buffer.
+ * Units are represented in cells.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, pbmc, buf_size, 0x0C, 0, 16, 0x08, 0x00, false);
+
+static inline void mlxsw_reg_pbmc_pack(char *payload, u8 local_port,
+                                      u16 xoff_timer_value, u16 xoff_refresh)
+{
+       MLXSW_REG_ZERO(pbmc, payload);
+       mlxsw_reg_pbmc_local_port_set(payload, local_port);
+       mlxsw_reg_pbmc_xoff_timer_value_set(payload, xoff_timer_value);
+       mlxsw_reg_pbmc_xoff_refresh_set(payload, xoff_refresh);
+}
+
+static inline void mlxsw_reg_pbmc_lossy_buffer_pack(char *payload,
+                                                   int buf_index,
+                                                   u16 size)
+{
+       mlxsw_reg_pbmc_buf_lossy_set(payload, buf_index, 1);
+       mlxsw_reg_pbmc_buf_epsb_set(payload, buf_index, 0);
+       mlxsw_reg_pbmc_buf_size_set(payload, buf_index, size);
+}
+
 /* PSPA - Port Switch Partition Allocation
  * ---------------------------------------
  * Controls the association of a port with a switch partition and enables
  * configuring ports as stacking ports.
  */
-#define MLXSW_REG_PSPA_ID 0x500d
+#define MLXSW_REG_PSPA_ID 0x500D
 #define MLXSW_REG_PSPA_LEN 0x8
 
 static const struct mlxsw_reg_info mlxsw_reg_pspa = {
@@ -1074,8 +1874,11 @@ MLXSW_ITEM32(reg, htgt, swid, 0x00, 24, 8);
  */
 MLXSW_ITEM32(reg, htgt, type, 0x00, 8, 4);
 
-#define MLXSW_REG_HTGT_TRAP_GROUP_EMAD 0x0
-#define MLXSW_REG_HTGT_TRAP_GROUP_RX   0x1
+enum mlxsw_reg_htgt_trap_group {
+       MLXSW_REG_HTGT_TRAP_GROUP_EMAD,
+       MLXSW_REG_HTGT_TRAP_GROUP_RX,
+       MLXSW_REG_HTGT_TRAP_GROUP_CTRL,
+};
 
 /* reg_htgt_trap_group
  * Trap group number. User defined number specifying which trap groups
@@ -1142,6 +1945,7 @@ MLXSW_ITEM32(reg, htgt, local_path_cpu_tclass, 0x10, 16, 6);
 
 #define MLXSW_REG_HTGT_LOCAL_PATH_RDQ_EMAD     0x15
 #define MLXSW_REG_HTGT_LOCAL_PATH_RDQ_RX       0x14
+#define MLXSW_REG_HTGT_LOCAL_PATH_RDQ_CTRL     0x13
 
 /* reg_htgt_local_path_rdq
  * Receive descriptor queue (RDQ) to use for the trap group.
@@ -1149,21 +1953,29 @@ MLXSW_ITEM32(reg, htgt, local_path_cpu_tclass, 0x10, 16, 6);
  */
 MLXSW_ITEM32(reg, htgt, local_path_rdq, 0x10, 0, 6);
 
-static inline void mlxsw_reg_htgt_pack(char *payload, u8 trap_group)
+static inline void mlxsw_reg_htgt_pack(char *payload,
+                                      enum mlxsw_reg_htgt_trap_group group)
 {
        u8 swid, rdq;
 
        MLXSW_REG_ZERO(htgt, payload);
-       if (MLXSW_REG_HTGT_TRAP_GROUP_EMAD == trap_group) {
+       switch (group) {
+       case MLXSW_REG_HTGT_TRAP_GROUP_EMAD:
                swid = MLXSW_PORT_SWID_ALL_SWIDS;
                rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_EMAD;
-       } else {
+               break;
+       case MLXSW_REG_HTGT_TRAP_GROUP_RX:
                swid = 0;
                rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_RX;
+               break;
+       case MLXSW_REG_HTGT_TRAP_GROUP_CTRL:
+               swid = 0;
+               rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_CTRL;
+               break;
        }
        mlxsw_reg_htgt_swid_set(payload, swid);
        mlxsw_reg_htgt_type_set(payload, MLXSW_REG_HTGT_PATH_TYPE_LOCAL);
-       mlxsw_reg_htgt_trap_group_set(payload, trap_group);
+       mlxsw_reg_htgt_trap_group_set(payload, group);
        mlxsw_reg_htgt_pide_set(payload, MLXSW_REG_HTGT_POLICER_DISABLE);
        mlxsw_reg_htgt_pid_set(payload, 0);
        mlxsw_reg_htgt_mirror_action_set(payload, MLXSW_REG_HTGT_TRAP_TO_CPU);
@@ -1254,17 +2066,290 @@ enum {
  */
 MLXSW_ITEM32(reg, hpkt, ctrl, 0x04, 16, 2);
 
-static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action,
-                                      u8 trap_group, u16 trap_id)
+static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id)
 {
+       enum mlxsw_reg_htgt_trap_group trap_group;
+
        MLXSW_REG_ZERO(hpkt, payload);
        mlxsw_reg_hpkt_ack_set(payload, MLXSW_REG_HPKT_ACK_NOT_REQUIRED);
        mlxsw_reg_hpkt_action_set(payload, action);
+       switch (trap_id) {
+       case MLXSW_TRAP_ID_ETHEMAD:
+       case MLXSW_TRAP_ID_PUDE:
+               trap_group = MLXSW_REG_HTGT_TRAP_GROUP_EMAD;
+               break;
+       default:
+               trap_group = MLXSW_REG_HTGT_TRAP_GROUP_RX;
+               break;
+       }
        mlxsw_reg_hpkt_trap_group_set(payload, trap_group);
        mlxsw_reg_hpkt_trap_id_set(payload, trap_id);
        mlxsw_reg_hpkt_ctrl_set(payload, MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT);
 }
 
+/* SBPR - Shared Buffer Pools Register
+ * -----------------------------------
+ * The SBPR configures and retrieves the shared buffer pools and configuration.
+ */
+#define MLXSW_REG_SBPR_ID 0xB001
+#define MLXSW_REG_SBPR_LEN 0x14
+
+static const struct mlxsw_reg_info mlxsw_reg_sbpr = {
+       .id = MLXSW_REG_SBPR_ID,
+       .len = MLXSW_REG_SBPR_LEN,
+};
+
+enum mlxsw_reg_sbpr_dir {
+       MLXSW_REG_SBPR_DIR_INGRESS,
+       MLXSW_REG_SBPR_DIR_EGRESS,
+};
+
+/* reg_sbpr_dir
+ * Direction.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sbpr, dir, 0x00, 24, 2);
+
+/* reg_sbpr_pool
+ * Pool index.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sbpr, pool, 0x00, 0, 4);
+
+/* reg_sbpr_size
+ * Pool size in buffer cells.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sbpr, size, 0x04, 0, 24);
+
+enum mlxsw_reg_sbpr_mode {
+       MLXSW_REG_SBPR_MODE_STATIC,
+       MLXSW_REG_SBPR_MODE_DYNAMIC,
+};
+
+/* reg_sbpr_mode
+ * Pool quota calculation mode.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sbpr, mode, 0x08, 0, 4);
+
+static inline void mlxsw_reg_sbpr_pack(char *payload, u8 pool,
+                                      enum mlxsw_reg_sbpr_dir dir,
+                                      enum mlxsw_reg_sbpr_mode mode, u32 size)
+{
+       MLXSW_REG_ZERO(sbpr, payload);
+       mlxsw_reg_sbpr_pool_set(payload, pool);
+       mlxsw_reg_sbpr_dir_set(payload, dir);
+       mlxsw_reg_sbpr_mode_set(payload, mode);
+       mlxsw_reg_sbpr_size_set(payload, size);
+}
+
+/* SBCM - Shared Buffer Class Management Register
+ * ----------------------------------------------
+ * The SBCM register configures and retrieves the shared buffer allocation
+ * and configuration according to Port-PG, including the binding to pool
+ * and definition of the associated quota.
+ */
+#define MLXSW_REG_SBCM_ID 0xB002
+#define MLXSW_REG_SBCM_LEN 0x28
+
+static const struct mlxsw_reg_info mlxsw_reg_sbcm = {
+       .id = MLXSW_REG_SBCM_ID,
+       .len = MLXSW_REG_SBCM_LEN,
+};
+
+/* reg_sbcm_local_port
+ * Local port number.
+ * For Ingress: excludes CPU port and Router port
+ * For Egress: excludes IP Router
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sbcm, local_port, 0x00, 16, 8);
+
+/* reg_sbcm_pg_buff
+ * PG buffer - Port PG (dir=ingress) / traffic class (dir=egress)
+ * For PG buffer: range is 0..cap_max_pg_buffers - 1
+ * For traffic class: range is 0..cap_max_tclass - 1
+ * Note that when traffic class is in MC aware mode then the traffic
+ * classes which are MC aware cannot be configured.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sbcm, pg_buff, 0x00, 8, 6);
+
+enum mlxsw_reg_sbcm_dir {
+       MLXSW_REG_SBCM_DIR_INGRESS,
+       MLXSW_REG_SBCM_DIR_EGRESS,
+};
+
+/* reg_sbcm_dir
+ * Direction.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sbcm, dir, 0x00, 0, 2);
+
+/* reg_sbcm_min_buff
+ * Minimum buffer size for the limiter, in cells.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sbcm, min_buff, 0x18, 0, 24);
+
+/* reg_sbcm_max_buff
+ * When the pool associated to the port-pg/tclass is configured to
+ * static, Maximum buffer size for the limiter configured in cells.
+ * When the pool associated to the port-pg/tclass is configured to
+ * dynamic, the max_buff holds the "alpha" parameter, supporting
+ * the following values:
+ * 0: 0
+ * i: (1/128)*2^(i-1), for i=1..14
+ * 0xFF: Infinity
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sbcm, max_buff, 0x1C, 0, 24);
+
+/* reg_sbcm_pool
+ * Association of the port-priority to a pool.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sbcm, pool, 0x24, 0, 4);
+
+static inline void mlxsw_reg_sbcm_pack(char *payload, u8 local_port, u8 pg_buff,
+                                      enum mlxsw_reg_sbcm_dir dir,
+                                      u32 min_buff, u32 max_buff, u8 pool)
+{
+       MLXSW_REG_ZERO(sbcm, payload);
+       mlxsw_reg_sbcm_local_port_set(payload, local_port);
+       mlxsw_reg_sbcm_pg_buff_set(payload, pg_buff);
+       mlxsw_reg_sbcm_dir_set(payload, dir);
+       mlxsw_reg_sbcm_min_buff_set(payload, min_buff);
+       mlxsw_reg_sbcm_max_buff_set(payload, max_buff);
+       mlxsw_reg_sbcm_pool_set(payload, pool);
+}
+
+/* SBPM - Shared Buffer Class Management Register
+ * ----------------------------------------------
+ * The SBPM register configures and retrieves the shared buffer allocation
+ * and configuration according to Port-Pool, including the definition
+ * of the associated quota.
+ */
+#define MLXSW_REG_SBPM_ID 0xB003
+#define MLXSW_REG_SBPM_LEN 0x28
+
+static const struct mlxsw_reg_info mlxsw_reg_sbpm = {
+       .id = MLXSW_REG_SBPM_ID,
+       .len = MLXSW_REG_SBPM_LEN,
+};
+
+/* reg_sbpm_local_port
+ * Local port number.
+ * For Ingress: excludes CPU port and Router port
+ * For Egress: excludes IP Router
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sbpm, local_port, 0x00, 16, 8);
+
+/* reg_sbpm_pool
+ * The pool associated to quota counting on the local_port.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sbpm, pool, 0x00, 8, 4);
+
+enum mlxsw_reg_sbpm_dir {
+       MLXSW_REG_SBPM_DIR_INGRESS,
+       MLXSW_REG_SBPM_DIR_EGRESS,
+};
+
+/* reg_sbpm_dir
+ * Direction.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sbpm, dir, 0x00, 0, 2);
+
+/* reg_sbpm_min_buff
+ * Minimum buffer size for the limiter, in cells.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sbpm, min_buff, 0x18, 0, 24);
+
+/* reg_sbpm_max_buff
+ * When the pool associated to the port-pg/tclass is configured to
+ * static, Maximum buffer size for the limiter configured in cells.
+ * When the pool associated to the port-pg/tclass is configured to
+ * dynamic, the max_buff holds the "alpha" parameter, supporting
+ * the following values:
+ * 0: 0
+ * i: (1/128)*2^(i-1), for i=1..14
+ * 0xFF: Infinity
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sbpm, max_buff, 0x1C, 0, 24);
+
+static inline void mlxsw_reg_sbpm_pack(char *payload, u8 local_port, u8 pool,
+                                      enum mlxsw_reg_sbpm_dir dir,
+                                      u32 min_buff, u32 max_buff)
+{
+       MLXSW_REG_ZERO(sbpm, payload);
+       mlxsw_reg_sbpm_local_port_set(payload, local_port);
+       mlxsw_reg_sbpm_pool_set(payload, pool);
+       mlxsw_reg_sbpm_dir_set(payload, dir);
+       mlxsw_reg_sbpm_min_buff_set(payload, min_buff);
+       mlxsw_reg_sbpm_max_buff_set(payload, max_buff);
+}
+
+/* SBMM - Shared Buffer Multicast Management Register
+ * --------------------------------------------------
+ * The SBMM register configures and retrieves the shared buffer allocation
+ * and configuration for MC packets according to Switch-Priority, including
+ * the binding to pool and definition of the associated quota.
+ */
+#define MLXSW_REG_SBMM_ID 0xB004
+#define MLXSW_REG_SBMM_LEN 0x28
+
+static const struct mlxsw_reg_info mlxsw_reg_sbmm = {
+       .id = MLXSW_REG_SBMM_ID,
+       .len = MLXSW_REG_SBMM_LEN,
+};
+
+/* reg_sbmm_prio
+ * Switch Priority.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sbmm, prio, 0x00, 8, 4);
+
+/* reg_sbmm_min_buff
+ * Minimum buffer size for the limiter, in cells.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sbmm, min_buff, 0x18, 0, 24);
+
+/* reg_sbmm_max_buff
+ * When the pool associated to the port-pg/tclass is configured to
+ * static, Maximum buffer size for the limiter configured in cells.
+ * When the pool associated to the port-pg/tclass is configured to
+ * dynamic, the max_buff holds the "alpha" parameter, supporting
+ * the following values:
+ * 0: 0
+ * i: (1/128)*2^(i-1), for i=1..14
+ * 0xFF: Infinity
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sbmm, max_buff, 0x1C, 0, 24);
+
+/* reg_sbmm_pool
+ * Association of the port-priority to a pool.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sbmm, pool, 0x24, 0, 4);
+
+static inline void mlxsw_reg_sbmm_pack(char *payload, u8 prio, u32 min_buff,
+                                      u32 max_buff, u8 pool)
+{
+       MLXSW_REG_ZERO(sbmm, payload);
+       mlxsw_reg_sbmm_prio_set(payload, prio);
+       mlxsw_reg_sbmm_min_buff_set(payload, min_buff);
+       mlxsw_reg_sbmm_max_buff_set(payload, max_buff);
+       mlxsw_reg_sbmm_pool_set(payload, pool);
+}
+
 static inline const char *mlxsw_reg_id_str(u16 reg_id)
 {
        switch (reg_id) {
@@ -1272,18 +2357,34 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id)
                return "SGCR";
        case MLXSW_REG_SPAD_ID:
                return "SPAD";
-       case MLXSW_REG_SMID_ID:
-               return "SMID";
        case MLXSW_REG_SSPR_ID:
                return "SSPR";
+       case MLXSW_REG_SFDAT_ID:
+               return "SFDAT";
+       case MLXSW_REG_SFD_ID:
+               return "SFD";
+       case MLXSW_REG_SFN_ID:
+               return "SFN";
        case MLXSW_REG_SPMS_ID:
                return "SPMS";
+       case MLXSW_REG_SPVID_ID:
+               return "SPVID";
+       case MLXSW_REG_SPVM_ID:
+               return "SPVM";
        case MLXSW_REG_SFGC_ID:
                return "SFGC";
        case MLXSW_REG_SFTR_ID:
                return "SFTR";
        case MLXSW_REG_SPMLR_ID:
                return "SPMLR";
+       case MLXSW_REG_SVFA_ID:
+               return "SVFA";
+       case MLXSW_REG_SVPE_ID:
+               return "SVPE";
+       case MLXSW_REG_SFMR_ID:
+               return "SFMR";
+       case MLXSW_REG_SPVMLR_ID:
+               return "SPVMLR";
        case MLXSW_REG_PMLP_ID:
                return "PMLP";
        case MLXSW_REG_PMTU_ID:
@@ -1296,12 +2397,22 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id)
                return "PAOS";
        case MLXSW_REG_PPCNT_ID:
                return "PPCNT";
+       case MLXSW_REG_PBMC_ID:
+               return "PBMC";
        case MLXSW_REG_PSPA_ID:
                return "PSPA";
        case MLXSW_REG_HTGT_ID:
                return "HTGT";
        case MLXSW_REG_HPKT_ID:
                return "HPKT";
+       case MLXSW_REG_SBPR_ID:
+               return "SBPR";
+       case MLXSW_REG_SBCM_ID:
+               return "SBCM";
+       case MLXSW_REG_SBPM_ID:
+               return "SBPM";
+       case MLXSW_REG_SBMM_ID:
+               return "SBMM";
        default:
                return "*UNKNOWN*";
        }
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
new file mode 100644 (file)
index 0000000..3be4a23
--- /dev/null
@@ -0,0 +1,1949 @@
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+ * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com>
+ * Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com>
+ * Copyright (c) 2015 Elad Raz <eladr@mellanox.com>
+ *
+ * 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 names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/skbuff.h>
+#include <linux/if_vlan.h>
+#include <linux/if_bridge.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/bitops.h>
+#include <net/switchdev.h>
+#include <generated/utsrelease.h>
+
+#include "spectrum.h"
+#include "core.h"
+#include "reg.h"
+#include "port.h"
+#include "trap.h"
+#include "txheader.h"
+
+static const char mlxsw_sp_driver_name[] = "mlxsw_spectrum";
+static const char mlxsw_sp_driver_version[] = "1.0";
+
+/* tx_hdr_version
+ * Tx header version.
+ * Must be set to 1.
+ */
+MLXSW_ITEM32(tx, hdr, version, 0x00, 28, 4);
+
+/* tx_hdr_ctl
+ * Packet control type.
+ * 0 - Ethernet control (e.g. EMADs, LACP)
+ * 1 - Ethernet data
+ */
+MLXSW_ITEM32(tx, hdr, ctl, 0x00, 26, 2);
+
+/* tx_hdr_proto
+ * Packet protocol type. Must be set to 1 (Ethernet).
+ */
+MLXSW_ITEM32(tx, hdr, proto, 0x00, 21, 3);
+
+/* tx_hdr_rx_is_router
+ * Packet is sent from the router. Valid for data packets only.
+ */
+MLXSW_ITEM32(tx, hdr, rx_is_router, 0x00, 19, 1);
+
+/* tx_hdr_fid_valid
+ * Indicates if the 'fid' field is valid and should be used for
+ * forwarding lookup. Valid for data packets only.
+ */
+MLXSW_ITEM32(tx, hdr, fid_valid, 0x00, 16, 1);
+
+/* tx_hdr_swid
+ * Switch partition ID. Must be set to 0.
+ */
+MLXSW_ITEM32(tx, hdr, swid, 0x00, 12, 3);
+
+/* tx_hdr_control_tclass
+ * Indicates if the packet should use the control TClass and not one
+ * of the data TClasses.
+ */
+MLXSW_ITEM32(tx, hdr, control_tclass, 0x00, 6, 1);
+
+/* tx_hdr_etclass
+ * Egress TClass to be used on the egress device on the egress port.
+ */
+MLXSW_ITEM32(tx, hdr, etclass, 0x00, 0, 4);
+
+/* tx_hdr_port_mid
+ * Destination local port for unicast packets.
+ * Destination multicast ID for multicast packets.
+ *
+ * Control packets are directed to a specific egress port, while data
+ * packets are transmitted through the CPU port (0) into the switch partition,
+ * where forwarding rules are applied.
+ */
+MLXSW_ITEM32(tx, hdr, port_mid, 0x04, 16, 16);
+
+/* tx_hdr_fid
+ * Forwarding ID used for L2 forwarding lookup. Valid only if 'fid_valid' is
+ * set, otherwise calculated based on the packet's VID using VID to FID mapping.
+ * Valid for data packets only.
+ */
+MLXSW_ITEM32(tx, hdr, fid, 0x08, 0, 16);
+
+/* tx_hdr_type
+ * 0 - Data packets
+ * 6 - Control packets
+ */
+MLXSW_ITEM32(tx, hdr, type, 0x0C, 0, 4);
+
+static void mlxsw_sp_txhdr_construct(struct sk_buff *skb,
+                                    const struct mlxsw_tx_info *tx_info)
+{
+       char *txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
+
+       memset(txhdr, 0, MLXSW_TXHDR_LEN);
+
+       mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1);
+       mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL);
+       mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
+       mlxsw_tx_hdr_swid_set(txhdr, 0);
+       mlxsw_tx_hdr_control_tclass_set(txhdr, 1);
+       mlxsw_tx_hdr_port_mid_set(txhdr, tx_info->local_port);
+       mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL);
+}
+
+static int mlxsw_sp_base_mac_get(struct mlxsw_sp *mlxsw_sp)
+{
+       char spad_pl[MLXSW_REG_SPAD_LEN];
+       int err;
+
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(spad), spad_pl);
+       if (err)
+               return err;
+       mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_sp->base_mac);
+       return 0;
+}
+
+static int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                         bool is_up)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char paos_pl[MLXSW_REG_PAOS_LEN];
+
+       mlxsw_reg_paos_pack(paos_pl, mlxsw_sp_port->local_port,
+                           is_up ? MLXSW_PORT_ADMIN_STATUS_UP :
+                           MLXSW_PORT_ADMIN_STATUS_DOWN);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(paos), paos_pl);
+}
+
+static int mlxsw_sp_port_oper_status_get(struct mlxsw_sp_port *mlxsw_sp_port,
+                                        bool *p_is_up)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char paos_pl[MLXSW_REG_PAOS_LEN];
+       u8 oper_status;
+       int err;
+
+       mlxsw_reg_paos_pack(paos_pl, mlxsw_sp_port->local_port, 0);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(paos), paos_pl);
+       if (err)
+               return err;
+       oper_status = mlxsw_reg_paos_oper_status_get(paos_pl);
+       *p_is_up = oper_status == MLXSW_PORT_ADMIN_STATUS_UP ? true : false;
+       return 0;
+}
+
+static int mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, u16 vfid)
+{
+       char sfmr_pl[MLXSW_REG_SFMR_LEN];
+       int err;
+
+       mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID,
+                           MLXSW_SP_VFID_BASE + vfid, 0);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
+
+       if (err)
+               return err;
+
+       set_bit(vfid, mlxsw_sp->active_vfids);
+       return 0;
+}
+
+static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, u16 vfid)
+{
+       char sfmr_pl[MLXSW_REG_SFMR_LEN];
+
+       clear_bit(vfid, mlxsw_sp->active_vfids);
+
+       mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID,
+                           MLXSW_SP_VFID_BASE + vfid, 0);
+       mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
+}
+
+static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                     unsigned char *addr)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char ppad_pl[MLXSW_REG_PPAD_LEN];
+
+       mlxsw_reg_ppad_pack(ppad_pl, true, mlxsw_sp_port->local_port);
+       mlxsw_reg_ppad_mac_memcpy_to(ppad_pl, addr);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppad), ppad_pl);
+}
+
+static int mlxsw_sp_port_dev_addr_init(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       unsigned char *addr = mlxsw_sp_port->dev->dev_addr;
+
+       ether_addr_copy(addr, mlxsw_sp->base_mac);
+       addr[ETH_ALEN - 1] += mlxsw_sp_port->local_port;
+       return mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr);
+}
+
+static int mlxsw_sp_port_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                      u16 vid, enum mlxsw_reg_spms_state state)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char *spms_pl;
+       int err;
+
+       spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL);
+       if (!spms_pl)
+               return -ENOMEM;
+       mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port);
+       mlxsw_reg_spms_vid_pack(spms_pl, vid, state);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl);
+       kfree(spms_pl);
+       return err;
+}
+
+static int mlxsw_sp_port_mtu_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char pmtu_pl[MLXSW_REG_PMTU_LEN];
+       int max_mtu;
+       int err;
+
+       mtu += MLXSW_TXHDR_LEN + ETH_HLEN;
+       mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sp_port->local_port, 0);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pmtu), pmtu_pl);
+       if (err)
+               return err;
+       max_mtu = mlxsw_reg_pmtu_max_mtu_get(pmtu_pl);
+
+       if (mtu > max_mtu)
+               return -EINVAL;
+
+       mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sp_port->local_port, mtu);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmtu), pmtu_pl);
+}
+
+static int mlxsw_sp_port_swid_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 swid)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char pspa_pl[MLXSW_REG_PSPA_LEN];
+
+       mlxsw_reg_pspa_pack(pspa_pl, swid, mlxsw_sp_port->local_port);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pspa), pspa_pl);
+}
+
+static int mlxsw_sp_port_vp_mode_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                    bool enable)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char svpe_pl[MLXSW_REG_SVPE_LEN];
+
+       mlxsw_reg_svpe_pack(svpe_pl, mlxsw_sp_port->local_port, enable);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svpe), svpe_pl);
+}
+
+int mlxsw_sp_port_vid_to_fid_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                enum mlxsw_reg_svfa_mt mt, bool valid, u16 fid,
+                                u16 vid)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char svfa_pl[MLXSW_REG_SVFA_LEN];
+
+       mlxsw_reg_svfa_pack(svfa_pl, mlxsw_sp_port->local_port, mt, valid,
+                           fid, vid);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svfa), svfa_pl);
+}
+
+static int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                         u16 vid, bool learn_enable)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char *spvmlr_pl;
+       int err;
+
+       spvmlr_pl = kmalloc(MLXSW_REG_SPVMLR_LEN, GFP_KERNEL);
+       if (!spvmlr_pl)
+               return -ENOMEM;
+       mlxsw_reg_spvmlr_pack(spvmlr_pl, mlxsw_sp_port->local_port, vid, vid,
+                             learn_enable);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvmlr), spvmlr_pl);
+       kfree(spvmlr_pl);
+       return err;
+}
+
+static int
+mlxsw_sp_port_system_port_mapping_set(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char sspr_pl[MLXSW_REG_SSPR_LEN];
+
+       mlxsw_reg_sspr_pack(sspr_pl, mlxsw_sp_port->local_port);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sspr), sspr_pl);
+}
+
+static int mlxsw_sp_port_module_check(struct mlxsw_sp_port *mlxsw_sp_port,
+                                     bool *p_usable)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char pmlp_pl[MLXSW_REG_PMLP_LEN];
+       int err;
+
+       mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sp_port->local_port);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
+       if (err)
+               return err;
+       *p_usable = mlxsw_reg_pmlp_width_get(pmlp_pl) ? true : false;
+       return 0;
+}
+
+static int mlxsw_sp_port_open(struct net_device *dev)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       int err;
+
+       err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true);
+       if (err)
+               return err;
+       netif_start_queue(dev);
+       return 0;
+}
+
+static int mlxsw_sp_port_stop(struct net_device *dev)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+
+       netif_stop_queue(dev);
+       return mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
+}
+
+static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb,
+                                     struct net_device *dev)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       struct mlxsw_sp_port_pcpu_stats *pcpu_stats;
+       const struct mlxsw_tx_info tx_info = {
+               .local_port = mlxsw_sp_port->local_port,
+               .is_emad = false,
+       };
+       u64 len;
+       int err;
+
+       if (mlxsw_core_skb_transmit_busy(mlxsw_sp, &tx_info))
+               return NETDEV_TX_BUSY;
+
+       if (unlikely(skb_headroom(skb) < MLXSW_TXHDR_LEN)) {
+               struct sk_buff *skb_orig = skb;
+
+               skb = skb_realloc_headroom(skb, MLXSW_TXHDR_LEN);
+               if (!skb) {
+                       this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
+                       dev_kfree_skb_any(skb_orig);
+                       return NETDEV_TX_OK;
+               }
+       }
+
+       if (eth_skb_pad(skb)) {
+               this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
+               return NETDEV_TX_OK;
+       }
+
+       mlxsw_sp_txhdr_construct(skb, &tx_info);
+       len = skb->len;
+       /* Due to a race we might fail here because of a full queue. In that
+        * unlikely case we simply drop the packet.
+        */
+       err = mlxsw_core_skb_transmit(mlxsw_sp, skb, &tx_info);
+
+       if (!err) {
+               pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats);
+               u64_stats_update_begin(&pcpu_stats->syncp);
+               pcpu_stats->tx_packets++;
+               pcpu_stats->tx_bytes += len;
+               u64_stats_update_end(&pcpu_stats->syncp);
+       } else {
+               this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
+               dev_kfree_skb_any(skb);
+       }
+       return NETDEV_TX_OK;
+}
+
+static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       struct sockaddr *addr = p;
+       int err;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       err = mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr->sa_data);
+       if (err)
+               return err;
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+       return 0;
+}
+
+static int mlxsw_sp_port_change_mtu(struct net_device *dev, int mtu)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       int err;
+
+       err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, mtu);
+       if (err)
+               return err;
+       dev->mtu = mtu;
+       return 0;
+}
+
+static struct rtnl_link_stats64 *
+mlxsw_sp_port_get_stats64(struct net_device *dev,
+                         struct rtnl_link_stats64 *stats)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       struct mlxsw_sp_port_pcpu_stats *p;
+       u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
+       u32 tx_dropped = 0;
+       unsigned int start;
+       int i;
+
+       for_each_possible_cpu(i) {
+               p = per_cpu_ptr(mlxsw_sp_port->pcpu_stats, i);
+               do {
+                       start = u64_stats_fetch_begin_irq(&p->syncp);
+                       rx_packets      = p->rx_packets;
+                       rx_bytes        = p->rx_bytes;
+                       tx_packets      = p->tx_packets;
+                       tx_bytes        = p->tx_bytes;
+               } while (u64_stats_fetch_retry_irq(&p->syncp, start));
+
+               stats->rx_packets       += rx_packets;
+               stats->rx_bytes         += rx_bytes;
+               stats->tx_packets       += tx_packets;
+               stats->tx_bytes         += tx_bytes;
+               /* tx_dropped is u32, updated without syncp protection. */
+               tx_dropped      += p->tx_dropped;
+       }
+       stats->tx_dropped       = tx_dropped;
+       return stats;
+}
+
+int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin,
+                          u16 vid_end, bool is_member, bool untagged)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char *spvm_pl;
+       int err;
+
+       spvm_pl = kmalloc(MLXSW_REG_SPVM_LEN, GFP_KERNEL);
+       if (!spvm_pl)
+               return -ENOMEM;
+
+       mlxsw_reg_spvm_pack(spvm_pl, mlxsw_sp_port->local_port, vid_begin,
+                           vid_end, is_member, untagged);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvm), spvm_pl);
+       kfree(spvm_pl);
+       return err;
+}
+
+static int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
+       u16 vid, last_visited_vid;
+       int err;
+
+       for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
+               err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, true, vid,
+                                                  vid);
+               if (err) {
+                       last_visited_vid = vid;
+                       goto err_port_vid_to_fid_set;
+               }
+       }
+
+       err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, true);
+       if (err) {
+               last_visited_vid = VLAN_N_VID;
+               goto err_port_vid_to_fid_set;
+       }
+
+       return 0;
+
+err_port_vid_to_fid_set:
+       for_each_set_bit(vid, mlxsw_sp_port->active_vlans, last_visited_vid)
+               mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, false, vid,
+                                            vid);
+       return err;
+}
+
+static int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
+       u16 vid;
+       int err;
+
+       err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
+       if (err)
+               return err;
+
+       for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
+               err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, false,
+                                                  vid, vid);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
+                         u16 vid)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char *sftr_pl;
+       int err;
+
+       /* VLAN 0 is added to HW filter when device goes up, but it is
+        * reserved in our case, so simply return.
+        */
+       if (!vid)
+               return 0;
+
+       if (test_bit(vid, mlxsw_sp_port->active_vfids)) {
+               netdev_warn(dev, "VID=%d already configured\n", vid);
+               return 0;
+       }
+
+       if (!test_bit(vid, mlxsw_sp->active_vfids)) {
+               err = mlxsw_sp_vfid_create(mlxsw_sp, vid);
+               if (err) {
+                       netdev_err(dev, "Failed to create vFID=%d\n",
+                                  MLXSW_SP_VFID_BASE + vid);
+                       return err;
+               }
+
+               sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
+               if (!sftr_pl) {
+                       err = -ENOMEM;
+                       goto err_flood_table_alloc;
+               }
+               mlxsw_reg_sftr_pack(sftr_pl, 0, vid,
+                                   MLXSW_REG_SFGC_TABLE_TYPE_FID, 0,
+                                   MLXSW_PORT_CPU_PORT, true);
+               err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
+               kfree(sftr_pl);
+               if (err) {
+                       netdev_err(dev, "Failed to configure flood table\n");
+                       goto err_flood_table_config;
+               }
+       }
+
+       /* In case we fail in the following steps, we intentionally do not
+        * destroy the associated vFID.
+        */
+
+       /* When adding the first VLAN interface on a bridged port we need to
+        * transition all the active 802.1Q bridge VLANs to use explicit
+        * {Port, VID} to FID mappings and set the port's mode to Virtual mode.
+        */
+       if (!mlxsw_sp_port->nr_vfids) {
+               err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port);
+               if (err) {
+                       netdev_err(dev, "Failed to set to Virtual mode\n");
+                       return err;
+               }
+       }
+
+       err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port,
+                                          MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
+                                          true, MLXSW_SP_VFID_BASE + vid, vid);
+       if (err) {
+               netdev_err(dev, "Failed to map {Port, VID=%d} to vFID=%d\n",
+                          vid, MLXSW_SP_VFID_BASE + vid);
+               goto err_port_vid_to_fid_set;
+       }
+
+       err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
+       if (err) {
+               netdev_err(dev, "Failed to disable learning for VID=%d\n", vid);
+               goto err_port_vid_learning_set;
+       }
+
+       err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, false);
+       if (err) {
+               netdev_err(dev, "Failed to set VLAN membership for VID=%d\n",
+                          vid);
+               goto err_port_add_vid;
+       }
+
+       err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid,
+                                         MLXSW_REG_SPMS_STATE_FORWARDING);
+       if (err) {
+               netdev_err(dev, "Failed to set STP state for VID=%d\n", vid);
+               goto err_port_stp_state_set;
+       }
+
+       mlxsw_sp_port->nr_vfids++;
+       set_bit(vid, mlxsw_sp_port->active_vfids);
+
+       return 0;
+
+err_flood_table_config:
+err_flood_table_alloc:
+       mlxsw_sp_vfid_destroy(mlxsw_sp, vid);
+       return err;
+
+err_port_stp_state_set:
+       mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
+err_port_add_vid:
+       mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
+err_port_vid_learning_set:
+       mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port,
+                                    MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, false,
+                                    MLXSW_SP_VFID_BASE + vid, vid);
+err_port_vid_to_fid_set:
+       mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
+       return err;
+}
+
+int mlxsw_sp_port_kill_vid(struct net_device *dev,
+                          __be16 __always_unused proto, u16 vid)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       int err;
+
+       /* VLAN 0 is removed from HW filter when device goes down, but
+        * it is reserved in our case, so simply return.
+        */
+       if (!vid)
+               return 0;
+
+       if (!test_bit(vid, mlxsw_sp_port->active_vfids)) {
+               netdev_warn(dev, "VID=%d does not exist\n", vid);
+               return 0;
+       }
+
+       err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid,
+                                         MLXSW_REG_SPMS_STATE_DISCARDING);
+       if (err) {
+               netdev_err(dev, "Failed to set STP state for VID=%d\n", vid);
+               return err;
+       }
+
+       err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
+       if (err) {
+               netdev_err(dev, "Failed to set VLAN membership for VID=%d\n",
+                          vid);
+               return err;
+       }
+
+       err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
+       if (err) {
+               netdev_err(dev, "Failed to enable learning for VID=%d\n", vid);
+               return err;
+       }
+
+       err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port,
+                                          MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
+                                          false, MLXSW_SP_VFID_BASE + vid,
+                                          vid);
+       if (err) {
+               netdev_err(dev, "Failed to invalidate {Port, VID=%d} to vFID=%d mapping\n",
+                          vid, MLXSW_SP_VFID_BASE + vid);
+               return err;
+       }
+
+       /* When removing the last VLAN interface on a bridged port we need to
+        * transition all active 802.1Q bridge VLANs to use VID to FID
+        * mappings and set port's mode to VLAN mode.
+        */
+       if (mlxsw_sp_port->nr_vfids == 1) {
+               err = mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
+               if (err) {
+                       netdev_err(dev, "Failed to set to VLAN mode\n");
+                       return err;
+               }
+       }
+
+       mlxsw_sp_port->nr_vfids--;
+       clear_bit(vid, mlxsw_sp_port->active_vfids);
+
+       return 0;
+}
+
+static const struct net_device_ops mlxsw_sp_port_netdev_ops = {
+       .ndo_open               = mlxsw_sp_port_open,
+       .ndo_stop               = mlxsw_sp_port_stop,
+       .ndo_start_xmit         = mlxsw_sp_port_xmit,
+       .ndo_set_mac_address    = mlxsw_sp_port_set_mac_address,
+       .ndo_change_mtu         = mlxsw_sp_port_change_mtu,
+       .ndo_get_stats64        = mlxsw_sp_port_get_stats64,
+       .ndo_vlan_rx_add_vid    = mlxsw_sp_port_add_vid,
+       .ndo_vlan_rx_kill_vid   = mlxsw_sp_port_kill_vid,
+       .ndo_fdb_add            = switchdev_port_fdb_add,
+       .ndo_fdb_del            = switchdev_port_fdb_del,
+       .ndo_fdb_dump           = switchdev_port_fdb_dump,
+       .ndo_bridge_setlink     = switchdev_port_bridge_setlink,
+       .ndo_bridge_getlink     = switchdev_port_bridge_getlink,
+       .ndo_bridge_dellink     = switchdev_port_bridge_dellink,
+};
+
+static void mlxsw_sp_port_get_drvinfo(struct net_device *dev,
+                                     struct ethtool_drvinfo *drvinfo)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+
+       strlcpy(drvinfo->driver, mlxsw_sp_driver_name, sizeof(drvinfo->driver));
+       strlcpy(drvinfo->version, mlxsw_sp_driver_version,
+               sizeof(drvinfo->version));
+       snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
+                "%d.%d.%d",
+                mlxsw_sp->bus_info->fw_rev.major,
+                mlxsw_sp->bus_info->fw_rev.minor,
+                mlxsw_sp->bus_info->fw_rev.subminor);
+       strlcpy(drvinfo->bus_info, mlxsw_sp->bus_info->device_name,
+               sizeof(drvinfo->bus_info));
+}
+
+struct mlxsw_sp_port_hw_stats {
+       char str[ETH_GSTRING_LEN];
+       u64 (*getter)(char *payload);
+};
+
+static const struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = {
+       {
+               .str = "a_frames_transmitted_ok",
+               .getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get,
+       },
+       {
+               .str = "a_frames_received_ok",
+               .getter = mlxsw_reg_ppcnt_a_frames_received_ok_get,
+       },
+       {
+               .str = "a_frame_check_sequence_errors",
+               .getter = mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get,
+       },
+       {
+               .str = "a_alignment_errors",
+               .getter = mlxsw_reg_ppcnt_a_alignment_errors_get,
+       },
+       {
+               .str = "a_octets_transmitted_ok",
+               .getter = mlxsw_reg_ppcnt_a_octets_transmitted_ok_get,
+       },
+       {
+               .str = "a_octets_received_ok",
+               .getter = mlxsw_reg_ppcnt_a_octets_received_ok_get,
+       },
+       {
+               .str = "a_multicast_frames_xmitted_ok",
+               .getter = mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get,
+       },
+       {
+               .str = "a_broadcast_frames_xmitted_ok",
+               .getter = mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get,
+       },
+       {
+               .str = "a_multicast_frames_received_ok",
+               .getter = mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get,
+       },
+       {
+               .str = "a_broadcast_frames_received_ok",
+               .getter = mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get,
+       },
+       {
+               .str = "a_in_range_length_errors",
+               .getter = mlxsw_reg_ppcnt_a_in_range_length_errors_get,
+       },
+       {
+               .str = "a_out_of_range_length_field",
+               .getter = mlxsw_reg_ppcnt_a_out_of_range_length_field_get,
+       },
+       {
+               .str = "a_frame_too_long_errors",
+               .getter = mlxsw_reg_ppcnt_a_frame_too_long_errors_get,
+       },
+       {
+               .str = "a_symbol_error_during_carrier",
+               .getter = mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get,
+       },
+       {
+               .str = "a_mac_control_frames_transmitted",
+               .getter = mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get,
+       },
+       {
+               .str = "a_mac_control_frames_received",
+               .getter = mlxsw_reg_ppcnt_a_mac_control_frames_received_get,
+       },
+       {
+               .str = "a_unsupported_opcodes_received",
+               .getter = mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get,
+       },
+       {
+               .str = "a_pause_mac_ctrl_frames_received",
+               .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_received_get,
+       },
+       {
+               .str = "a_pause_mac_ctrl_frames_xmitted",
+               .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_transmitted_get,
+       },
+};
+
+#define MLXSW_SP_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_stats)
+
+static void mlxsw_sp_port_get_strings(struct net_device *dev,
+                                     u32 stringset, u8 *data)
+{
+       u8 *p = data;
+       int i;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++) {
+                       memcpy(p, mlxsw_sp_port_hw_stats[i].str,
+                              ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+               break;
+       }
+}
+
+static void mlxsw_sp_port_get_stats(struct net_device *dev,
+                                   struct ethtool_stats *stats, u64 *data)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
+       int i;
+       int err;
+
+       mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl);
+       for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++)
+               data[i] = !err ? mlxsw_sp_port_hw_stats[i].getter(ppcnt_pl) : 0;
+}
+
+static int mlxsw_sp_port_get_sset_count(struct net_device *dev, int sset)
+{
+       switch (sset) {
+       case ETH_SS_STATS:
+               return MLXSW_SP_PORT_HW_STATS_LEN;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+struct mlxsw_sp_port_link_mode {
+       u32 mask;
+       u32 supported;
+       u32 advertised;
+       u32 speed;
+};
+
+static const struct mlxsw_sp_port_link_mode mlxsw_sp_port_link_mode[] = {
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_100BASE_T,
+               .supported      = SUPPORTED_100baseT_Full,
+               .advertised     = ADVERTISED_100baseT_Full,
+               .speed          = 100,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_100BASE_TX,
+               .speed          = 100,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_SGMII |
+                                 MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX,
+               .supported      = SUPPORTED_1000baseKX_Full,
+               .advertised     = ADVERTISED_1000baseKX_Full,
+               .speed          = 1000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_T,
+               .supported      = SUPPORTED_10000baseT_Full,
+               .advertised     = ADVERTISED_10000baseT_Full,
+               .speed          = 10000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 |
+                                 MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4,
+               .supported      = SUPPORTED_10000baseKX4_Full,
+               .advertised     = ADVERTISED_10000baseKX4_Full,
+               .speed          = 10000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
+                                 MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
+                                 MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
+                                 MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR,
+               .supported      = SUPPORTED_10000baseKR_Full,
+               .advertised     = ADVERTISED_10000baseKR_Full,
+               .speed          = 10000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_20GBASE_KR2,
+               .supported      = SUPPORTED_20000baseKR2_Full,
+               .advertised     = ADVERTISED_20000baseKR2_Full,
+               .speed          = 20000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4,
+               .supported      = SUPPORTED_40000baseCR4_Full,
+               .advertised     = ADVERTISED_40000baseCR4_Full,
+               .speed          = 40000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4,
+               .supported      = SUPPORTED_40000baseKR4_Full,
+               .advertised     = ADVERTISED_40000baseKR4_Full,
+               .speed          = 40000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4,
+               .supported      = SUPPORTED_40000baseSR4_Full,
+               .advertised     = ADVERTISED_40000baseSR4_Full,
+               .speed          = 40000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4,
+               .supported      = SUPPORTED_40000baseLR4_Full,
+               .advertised     = ADVERTISED_40000baseLR4_Full,
+               .speed          = 40000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR |
+                                 MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR |
+                                 MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR,
+               .speed          = 25000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR4 |
+                                 MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2 |
+                                 MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2,
+               .speed          = 50000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_56GBASE_R4,
+               .supported      = SUPPORTED_56000baseKR4_Full,
+               .advertised     = ADVERTISED_56000baseKR4_Full,
+               .speed          = 56000,
+       },
+       {
+               .mask           = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4 |
+                                 MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
+                                 MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 |
+                                 MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4,
+               .speed          = 100000,
+       },
+};
+
+#define MLXSW_SP_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sp_port_link_mode)
+
+static u32 mlxsw_sp_from_ptys_supported_port(u32 ptys_eth_proto)
+{
+       if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
+                             MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
+                             MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 |
+                             MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 |
+                             MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
+                             MLXSW_REG_PTYS_ETH_SPEED_SGMII))
+               return SUPPORTED_FIBRE;
+
+       if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
+                             MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 |
+                             MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 |
+                             MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 |
+                             MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX))
+               return SUPPORTED_Backplane;
+       return 0;
+}
+
+static u32 mlxsw_sp_from_ptys_supported_link(u32 ptys_eth_proto)
+{
+       u32 modes = 0;
+       int i;
+
+       for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) {
+               if (ptys_eth_proto & mlxsw_sp_port_link_mode[i].mask)
+                       modes |= mlxsw_sp_port_link_mode[i].supported;
+       }
+       return modes;
+}
+
+static u32 mlxsw_sp_from_ptys_advert_link(u32 ptys_eth_proto)
+{
+       u32 modes = 0;
+       int i;
+
+       for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) {
+               if (ptys_eth_proto & mlxsw_sp_port_link_mode[i].mask)
+                       modes |= mlxsw_sp_port_link_mode[i].advertised;
+       }
+       return modes;
+}
+
+static void mlxsw_sp_from_ptys_speed_duplex(bool carrier_ok, u32 ptys_eth_proto,
+                                           struct ethtool_cmd *cmd)
+{
+       u32 speed = SPEED_UNKNOWN;
+       u8 duplex = DUPLEX_UNKNOWN;
+       int i;
+
+       if (!carrier_ok)
+               goto out;
+
+       for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) {
+               if (ptys_eth_proto & mlxsw_sp_port_link_mode[i].mask) {
+                       speed = mlxsw_sp_port_link_mode[i].speed;
+                       duplex = DUPLEX_FULL;
+                       break;
+               }
+       }
+out:
+       ethtool_cmd_speed_set(cmd, speed);
+       cmd->duplex = duplex;
+}
+
+static u8 mlxsw_sp_port_connector_port(u32 ptys_eth_proto)
+{
+       if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
+                             MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 |
+                             MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
+                             MLXSW_REG_PTYS_ETH_SPEED_SGMII))
+               return PORT_FIBRE;
+
+       if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
+                             MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 |
+                             MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4))
+               return PORT_DA;
+
+       if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
+                             MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 |
+                             MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 |
+                             MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4))
+               return PORT_NONE;
+
+       return PORT_OTHER;
+}
+
+static int mlxsw_sp_port_get_settings(struct net_device *dev,
+                                     struct ethtool_cmd *cmd)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char ptys_pl[MLXSW_REG_PTYS_LEN];
+       u32 eth_proto_cap;
+       u32 eth_proto_admin;
+       u32 eth_proto_oper;
+       int err;
+
+       mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, 0);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
+       if (err) {
+               netdev_err(dev, "Failed to get proto");
+               return err;
+       }
+       mlxsw_reg_ptys_unpack(ptys_pl, &eth_proto_cap,
+                             &eth_proto_admin, &eth_proto_oper);
+
+       cmd->supported = mlxsw_sp_from_ptys_supported_port(eth_proto_cap) |
+                        mlxsw_sp_from_ptys_supported_link(eth_proto_cap) |
+                        SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+       cmd->advertising = mlxsw_sp_from_ptys_advert_link(eth_proto_admin);
+       mlxsw_sp_from_ptys_speed_duplex(netif_carrier_ok(dev),
+                                       eth_proto_oper, cmd);
+
+       eth_proto_oper = eth_proto_oper ? eth_proto_oper : eth_proto_cap;
+       cmd->port = mlxsw_sp_port_connector_port(eth_proto_oper);
+       cmd->lp_advertising = mlxsw_sp_from_ptys_advert_link(eth_proto_oper);
+
+       cmd->transceiver = XCVR_INTERNAL;
+       return 0;
+}
+
+static u32 mlxsw_sp_to_ptys_advert_link(u32 advertising)
+{
+       u32 ptys_proto = 0;
+       int i;
+
+       for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) {
+               if (advertising & mlxsw_sp_port_link_mode[i].advertised)
+                       ptys_proto |= mlxsw_sp_port_link_mode[i].mask;
+       }
+       return ptys_proto;
+}
+
+static u32 mlxsw_sp_to_ptys_speed(u32 speed)
+{
+       u32 ptys_proto = 0;
+       int i;
+
+       for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) {
+               if (speed == mlxsw_sp_port_link_mode[i].speed)
+                       ptys_proto |= mlxsw_sp_port_link_mode[i].mask;
+       }
+       return ptys_proto;
+}
+
+static int mlxsw_sp_port_set_settings(struct net_device *dev,
+                                     struct ethtool_cmd *cmd)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char ptys_pl[MLXSW_REG_PTYS_LEN];
+       u32 speed;
+       u32 eth_proto_new;
+       u32 eth_proto_cap;
+       u32 eth_proto_admin;
+       bool is_up;
+       int err;
+
+       speed = ethtool_cmd_speed(cmd);
+
+       eth_proto_new = cmd->autoneg == AUTONEG_ENABLE ?
+               mlxsw_sp_to_ptys_advert_link(cmd->advertising) :
+               mlxsw_sp_to_ptys_speed(speed);
+
+       mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, 0);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
+       if (err) {
+               netdev_err(dev, "Failed to get proto");
+               return err;
+       }
+       mlxsw_reg_ptys_unpack(ptys_pl, &eth_proto_cap, &eth_proto_admin, NULL);
+
+       eth_proto_new = eth_proto_new & eth_proto_cap;
+       if (!eth_proto_new) {
+               netdev_err(dev, "Not supported proto admin requested");
+               return -EINVAL;
+       }
+       if (eth_proto_new == eth_proto_admin)
+               return 0;
+
+       mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, eth_proto_new);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
+       if (err) {
+               netdev_err(dev, "Failed to set proto admin");
+               return err;
+       }
+
+       err = mlxsw_sp_port_oper_status_get(mlxsw_sp_port, &is_up);
+       if (err) {
+               netdev_err(dev, "Failed to get oper status");
+               return err;
+       }
+       if (!is_up)
+               return 0;
+
+       err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
+       if (err) {
+               netdev_err(dev, "Failed to set admin status");
+               return err;
+       }
+
+       err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true);
+       if (err) {
+               netdev_err(dev, "Failed to set admin status");
+               return err;
+       }
+
+       return 0;
+}
+
+static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
+       .get_drvinfo            = mlxsw_sp_port_get_drvinfo,
+       .get_link               = ethtool_op_get_link,
+       .get_strings            = mlxsw_sp_port_get_strings,
+       .get_ethtool_stats      = mlxsw_sp_port_get_stats,
+       .get_sset_count         = mlxsw_sp_port_get_sset_count,
+       .get_settings           = mlxsw_sp_port_get_settings,
+       .set_settings           = mlxsw_sp_port_set_settings,
+};
+
+static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       struct net_device *dev;
+       bool usable;
+       int err;
+
+       dev = alloc_etherdev(sizeof(struct mlxsw_sp_port));
+       if (!dev)
+               return -ENOMEM;
+       mlxsw_sp_port = netdev_priv(dev);
+       mlxsw_sp_port->dev = dev;
+       mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
+       mlxsw_sp_port->local_port = local_port;
+       mlxsw_sp_port->learning = 1;
+       mlxsw_sp_port->learning_sync = 1;
+       mlxsw_sp_port->uc_flood = 1;
+       mlxsw_sp_port->pvid = 1;
+
+       mlxsw_sp_port->pcpu_stats =
+               netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats);
+       if (!mlxsw_sp_port->pcpu_stats) {
+               err = -ENOMEM;
+               goto err_alloc_stats;
+       }
+
+       dev->netdev_ops = &mlxsw_sp_port_netdev_ops;
+       dev->ethtool_ops = &mlxsw_sp_port_ethtool_ops;
+
+       err = mlxsw_sp_port_dev_addr_init(mlxsw_sp_port);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unable to init port mac address\n",
+                       mlxsw_sp_port->local_port);
+               goto err_dev_addr_init;
+       }
+
+       netif_carrier_off(dev);
+
+       dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_LLTX | NETIF_F_SG |
+                        NETIF_F_HW_VLAN_CTAG_FILTER;
+
+       /* Each packet needs to have a Tx header (metadata) on top all other
+        * headers.
+        */
+       dev->hard_header_len += MLXSW_TXHDR_LEN;
+
+       err = mlxsw_sp_port_module_check(mlxsw_sp_port, &usable);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to check module\n",
+                       mlxsw_sp_port->local_port);
+               goto err_port_module_check;
+       }
+
+       if (!usable) {
+               dev_dbg(mlxsw_sp->bus_info->dev, "Port %d: Not usable, skipping initialization\n",
+                       mlxsw_sp_port->local_port);
+               goto port_not_usable;
+       }
+
+       err = mlxsw_sp_port_system_port_mapping_set(mlxsw_sp_port);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set system port mapping\n",
+                       mlxsw_sp_port->local_port);
+               goto err_port_system_port_mapping_set;
+       }
+
+       err = mlxsw_sp_port_swid_set(mlxsw_sp_port, 0);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set SWID\n",
+                       mlxsw_sp_port->local_port);
+               goto err_port_swid_set;
+       }
+
+       err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, ETH_DATA_LEN);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set MTU\n",
+                       mlxsw_sp_port->local_port);
+               goto err_port_mtu_set;
+       }
+
+       err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
+       if (err)
+               goto err_port_admin_status_set;
+
+       err = mlxsw_sp_port_buffers_init(mlxsw_sp_port);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize buffers\n",
+                       mlxsw_sp_port->local_port);
+               goto err_port_buffers_init;
+       }
+
+       mlxsw_sp_port_switchdev_init(mlxsw_sp_port);
+       err = register_netdev(dev);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to register netdev\n",
+                       mlxsw_sp_port->local_port);
+               goto err_register_netdev;
+       }
+
+       err = mlxsw_sp_port_vlan_init(mlxsw_sp_port);
+       if (err)
+               goto err_port_vlan_init;
+
+       mlxsw_sp->ports[local_port] = mlxsw_sp_port;
+       return 0;
+
+err_port_vlan_init:
+       unregister_netdev(dev);
+err_register_netdev:
+err_port_buffers_init:
+err_port_admin_status_set:
+err_port_mtu_set:
+err_port_swid_set:
+err_port_system_port_mapping_set:
+port_not_usable:
+err_port_module_check:
+err_dev_addr_init:
+       free_percpu(mlxsw_sp_port->pcpu_stats);
+err_alloc_stats:
+       free_netdev(dev);
+       return err;
+}
+
+static void mlxsw_sp_vfids_fini(struct mlxsw_sp *mlxsw_sp)
+{
+       u16 vfid;
+
+       for_each_set_bit(vfid, mlxsw_sp->active_vfids, VLAN_N_VID)
+               mlxsw_sp_vfid_destroy(mlxsw_sp, vfid);
+}
+
+static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port];
+
+       if (!mlxsw_sp_port)
+               return;
+       mlxsw_sp_port_kill_vid(mlxsw_sp_port->dev, 0, 1);
+       unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */
+       mlxsw_sp_port_switchdev_fini(mlxsw_sp_port);
+       free_percpu(mlxsw_sp_port->pcpu_stats);
+       free_netdev(mlxsw_sp_port->dev);
+}
+
+static void mlxsw_sp_ports_remove(struct mlxsw_sp *mlxsw_sp)
+{
+       int i;
+
+       for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++)
+               mlxsw_sp_port_remove(mlxsw_sp, i);
+       kfree(mlxsw_sp->ports);
+}
+
+static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
+{
+       size_t alloc_size;
+       int i;
+       int err;
+
+       alloc_size = sizeof(struct mlxsw_sp_port *) * MLXSW_PORT_MAX_PORTS;
+       mlxsw_sp->ports = kzalloc(alloc_size, GFP_KERNEL);
+       if (!mlxsw_sp->ports)
+               return -ENOMEM;
+
+       for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) {
+               err = mlxsw_sp_port_create(mlxsw_sp, i);
+               if (err)
+                       goto err_port_create;
+       }
+       return 0;
+
+err_port_create:
+       for (i--; i >= 1; i--)
+               mlxsw_sp_port_remove(mlxsw_sp, i);
+       kfree(mlxsw_sp->ports);
+       return err;
+}
+
+static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg,
+                                    char *pude_pl, void *priv)
+{
+       struct mlxsw_sp *mlxsw_sp = priv;
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       enum mlxsw_reg_pude_oper_status status;
+       u8 local_port;
+
+       local_port = mlxsw_reg_pude_local_port_get(pude_pl);
+       mlxsw_sp_port = mlxsw_sp->ports[local_port];
+       if (!mlxsw_sp_port) {
+               dev_warn(mlxsw_sp->bus_info->dev, "Port %d: Link event received for non-existent port\n",
+                        local_port);
+               return;
+       }
+
+       status = mlxsw_reg_pude_oper_status_get(pude_pl);
+       if (status == MLXSW_PORT_OPER_STATUS_UP) {
+               netdev_info(mlxsw_sp_port->dev, "link up\n");
+               netif_carrier_on(mlxsw_sp_port->dev);
+       } else {
+               netdev_info(mlxsw_sp_port->dev, "link down\n");
+               netif_carrier_off(mlxsw_sp_port->dev);
+       }
+}
+
+static struct mlxsw_event_listener mlxsw_sp_pude_event = {
+       .func = mlxsw_sp_pude_event_func,
+       .trap_id = MLXSW_TRAP_ID_PUDE,
+};
+
+static int mlxsw_sp_event_register(struct mlxsw_sp *mlxsw_sp,
+                                  enum mlxsw_event_trap_id trap_id)
+{
+       struct mlxsw_event_listener *el;
+       char hpkt_pl[MLXSW_REG_HPKT_LEN];
+       int err;
+
+       switch (trap_id) {
+       case MLXSW_TRAP_ID_PUDE:
+               el = &mlxsw_sp_pude_event;
+               break;
+       }
+       err = mlxsw_core_event_listener_register(mlxsw_sp->core, el, mlxsw_sp);
+       if (err)
+               return err;
+
+       mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, trap_id);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl);
+       if (err)
+               goto err_event_trap_set;
+
+       return 0;
+
+err_event_trap_set:
+       mlxsw_core_event_listener_unregister(mlxsw_sp->core, el, mlxsw_sp);
+       return err;
+}
+
+static void mlxsw_sp_event_unregister(struct mlxsw_sp *mlxsw_sp,
+                                     enum mlxsw_event_trap_id trap_id)
+{
+       struct mlxsw_event_listener *el;
+
+       switch (trap_id) {
+       case MLXSW_TRAP_ID_PUDE:
+               el = &mlxsw_sp_pude_event;
+               break;
+       }
+       mlxsw_core_event_listener_unregister(mlxsw_sp->core, el, mlxsw_sp);
+}
+
+static void mlxsw_sp_rx_listener_func(struct sk_buff *skb, u8 local_port,
+                                     void *priv)
+{
+       struct mlxsw_sp *mlxsw_sp = priv;
+       struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port];
+       struct mlxsw_sp_port_pcpu_stats *pcpu_stats;
+
+       if (unlikely(!mlxsw_sp_port)) {
+               dev_warn_ratelimited(mlxsw_sp->bus_info->dev, "Port %d: skb received for non-existent port\n",
+                                    local_port);
+               return;
+       }
+
+       skb->dev = mlxsw_sp_port->dev;
+
+       pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats);
+       u64_stats_update_begin(&pcpu_stats->syncp);
+       pcpu_stats->rx_packets++;
+       pcpu_stats->rx_bytes += skb->len;
+       u64_stats_update_end(&pcpu_stats->syncp);
+
+       skb->protocol = eth_type_trans(skb, skb->dev);
+       netif_receive_skb(skb);
+}
+
+static const struct mlxsw_rx_listener mlxsw_sp_rx_listener[] = {
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_FDB_MC,
+       },
+       /* Traps for specific L2 packet types, not trapped as FDB MC */
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_STP,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_LACP,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_EAPOL,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_LLDP,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_MMRP,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_MVRP,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_RPVST,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_DHCP,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_IGMP_QUERY,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_IGMP_V1_REPORT,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_IGMP_V2_REPORT,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_IGMP_V2_LEAVE,
+       },
+       {
+               .func = mlxsw_sp_rx_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = MLXSW_TRAP_ID_IGMP_V3_REPORT,
+       },
+};
+
+static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp)
+{
+       char htgt_pl[MLXSW_REG_HTGT_LEN];
+       char hpkt_pl[MLXSW_REG_HPKT_LEN];
+       int i;
+       int err;
+
+       mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_RX);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_CTRL);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl);
+       if (err)
+               return err;
+
+       for (i = 0; i < ARRAY_SIZE(mlxsw_sp_rx_listener); i++) {
+               err = mlxsw_core_rx_listener_register(mlxsw_sp->core,
+                                                     &mlxsw_sp_rx_listener[i],
+                                                     mlxsw_sp);
+               if (err)
+                       goto err_rx_listener_register;
+
+               mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU,
+                                   mlxsw_sp_rx_listener[i].trap_id);
+               err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl);
+               if (err)
+                       goto err_rx_trap_set;
+       }
+       return 0;
+
+err_rx_trap_set:
+       mlxsw_core_rx_listener_unregister(mlxsw_sp->core,
+                                         &mlxsw_sp_rx_listener[i],
+                                         mlxsw_sp);
+err_rx_listener_register:
+       for (i--; i >= 0; i--) {
+               mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD,
+                                   mlxsw_sp_rx_listener[i].trap_id);
+               mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl);
+
+               mlxsw_core_rx_listener_unregister(mlxsw_sp->core,
+                                                 &mlxsw_sp_rx_listener[i],
+                                                 mlxsw_sp);
+       }
+       return err;
+}
+
+static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp)
+{
+       char hpkt_pl[MLXSW_REG_HPKT_LEN];
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mlxsw_sp_rx_listener); i++) {
+               mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD,
+                                   mlxsw_sp_rx_listener[i].trap_id);
+               mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl);
+
+               mlxsw_core_rx_listener_unregister(mlxsw_sp->core,
+                                                 &mlxsw_sp_rx_listener[i],
+                                                 mlxsw_sp);
+       }
+}
+
+static int __mlxsw_sp_flood_init(struct mlxsw_core *mlxsw_core,
+                                enum mlxsw_reg_sfgc_type type,
+                                enum mlxsw_reg_sfgc_bridge_type bridge_type)
+{
+       enum mlxsw_flood_table_type table_type;
+       enum mlxsw_sp_flood_table flood_table;
+       char sfgc_pl[MLXSW_REG_SFGC_LEN];
+
+       if (bridge_type == MLXSW_REG_SFGC_BRIDGE_TYPE_VFID) {
+               table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID;
+               flood_table = 0;
+       } else {
+               table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST;
+               if (type == MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST)
+                       flood_table = MLXSW_SP_FLOOD_TABLE_UC;
+               else
+                       flood_table = MLXSW_SP_FLOOD_TABLE_BM;
+       }
+
+       mlxsw_reg_sfgc_pack(sfgc_pl, type, bridge_type, table_type,
+                           flood_table);
+       return mlxsw_reg_write(mlxsw_core, MLXSW_REG(sfgc), sfgc_pl);
+}
+
+static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp)
+{
+       int type, err;
+
+       /* For non-offloaded netdevs, flood all traffic types to CPU
+        * port.
+        */
+       for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) {
+               if (type == MLXSW_REG_SFGC_TYPE_RESERVED)
+                       continue;
+
+               err = __mlxsw_sp_flood_init(mlxsw_sp->core, type,
+                                           MLXSW_REG_SFGC_BRIDGE_TYPE_VFID);
+               if (err)
+                       return err;
+       }
+
+       /* For bridged ports, use one flooding table for unknown unicast
+        * traffic and a second table for unregistered multicast and
+        * broadcast.
+        */
+       for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) {
+               if (type == MLXSW_REG_SFGC_TYPE_RESERVED)
+                       continue;
+
+               err = __mlxsw_sp_flood_init(mlxsw_sp->core, type,
+                                           MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
+                        const struct mlxsw_bus_info *mlxsw_bus_info)
+{
+       struct mlxsw_sp *mlxsw_sp = priv;
+       int err;
+
+       mlxsw_sp->core = mlxsw_core;
+       mlxsw_sp->bus_info = mlxsw_bus_info;
+
+       err = mlxsw_sp_base_mac_get(mlxsw_sp);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to get base mac\n");
+               return err;
+       }
+
+       err = mlxsw_sp_ports_create(mlxsw_sp);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n");
+               goto err_ports_create;
+       }
+
+       err = mlxsw_sp_event_register(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to register for PUDE events\n");
+               goto err_event_register;
+       }
+
+       err = mlxsw_sp_traps_init(mlxsw_sp);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to set traps for RX\n");
+               goto err_rx_listener_register;
+       }
+
+       err = mlxsw_sp_flood_init(mlxsw_sp);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize flood tables\n");
+               goto err_flood_init;
+       }
+
+       err = mlxsw_sp_buffers_init(mlxsw_sp);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize buffers\n");
+               goto err_buffers_init;
+       }
+
+       err = mlxsw_sp_switchdev_init(mlxsw_sp);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize switchdev\n");
+               goto err_switchdev_init;
+       }
+
+       return 0;
+
+err_switchdev_init:
+err_buffers_init:
+err_flood_init:
+       mlxsw_sp_traps_fini(mlxsw_sp);
+err_rx_listener_register:
+       mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
+err_event_register:
+       mlxsw_sp_ports_remove(mlxsw_sp);
+err_ports_create:
+       mlxsw_sp_vfids_fini(mlxsw_sp);
+       return err;
+}
+
+static void mlxsw_sp_fini(void *priv)
+{
+       struct mlxsw_sp *mlxsw_sp = priv;
+
+       mlxsw_sp_switchdev_fini(mlxsw_sp);
+       mlxsw_sp_traps_fini(mlxsw_sp);
+       mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
+       mlxsw_sp_ports_remove(mlxsw_sp);
+       mlxsw_sp_vfids_fini(mlxsw_sp);
+}
+
+static struct mlxsw_config_profile mlxsw_sp_config_profile = {
+       .used_max_vepa_channels         = 1,
+       .max_vepa_channels              = 0,
+       .used_max_lag                   = 1,
+       .max_lag                        = 64,
+       .used_max_port_per_lag          = 1,
+       .max_port_per_lag               = 16,
+       .used_max_mid                   = 1,
+       .max_mid                        = 7000,
+       .used_max_pgt                   = 1,
+       .max_pgt                        = 0,
+       .used_max_system_port           = 1,
+       .max_system_port                = 64,
+       .used_max_vlan_groups           = 1,
+       .max_vlan_groups                = 127,
+       .used_max_regions               = 1,
+       .max_regions                    = 400,
+       .used_flood_tables              = 1,
+       .used_flood_mode                = 1,
+       .flood_mode                     = 3,
+       .max_fid_offset_flood_tables    = 2,
+       .fid_offset_flood_table_size    = VLAN_N_VID - 1,
+       .max_fid_flood_tables           = 1,
+       .fid_flood_table_size           = VLAN_N_VID,
+       .used_max_ib_mc                 = 1,
+       .max_ib_mc                      = 0,
+       .used_max_pkey                  = 1,
+       .max_pkey                       = 0,
+       .swid_config                    = {
+               {
+                       .used_type      = 1,
+                       .type           = MLXSW_PORT_SWID_TYPE_ETH,
+               }
+       },
+};
+
+static struct mlxsw_driver mlxsw_sp_driver = {
+       .kind                   = MLXSW_DEVICE_KIND_SPECTRUM,
+       .owner                  = THIS_MODULE,
+       .priv_size              = sizeof(struct mlxsw_sp),
+       .init                   = mlxsw_sp_init,
+       .fini                   = mlxsw_sp_fini,
+       .txhdr_construct        = mlxsw_sp_txhdr_construct,
+       .txhdr_len              = MLXSW_TXHDR_LEN,
+       .profile                = &mlxsw_sp_config_profile,
+};
+
+static bool mlxsw_sp_port_dev_check(const struct net_device *dev)
+{
+       return dev->netdev_ops == &mlxsw_sp_port_netdev_ops;
+}
+
+static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       struct net_device *dev = mlxsw_sp_port->dev;
+       int err;
+
+       /* When port is not bridged untagged packets are tagged with
+        * PVID=VID=1, thereby creating an implicit VLAN interface in
+        * the device. Remove it and let bridge code take care of its
+        * own VLANs.
+        */
+       err = mlxsw_sp_port_kill_vid(dev, 0, 1);
+       if (err)
+               netdev_err(dev, "Failed to remove VID 1\n");
+
+       return err;
+}
+
+static int mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       struct net_device *dev = mlxsw_sp_port->dev;
+       int err;
+
+       /* Add implicit VLAN interface in the device, so that untagged
+        * packets will be classified to the default vFID.
+        */
+       err = mlxsw_sp_port_add_vid(dev, 0, 1);
+       if (err)
+               netdev_err(dev, "Failed to add VID 1\n");
+
+       return err;
+}
+
+static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp,
+                                        struct net_device *br_dev)
+{
+       return !mlxsw_sp->master_bridge.dev ||
+              mlxsw_sp->master_bridge.dev == br_dev;
+}
+
+static void mlxsw_sp_master_bridge_inc(struct mlxsw_sp *mlxsw_sp,
+                                      struct net_device *br_dev)
+{
+       mlxsw_sp->master_bridge.dev = br_dev;
+       mlxsw_sp->master_bridge.ref_count++;
+}
+
+static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp,
+                                      struct net_device *br_dev)
+{
+       if (--mlxsw_sp->master_bridge.ref_count == 0)
+               mlxsw_sp->master_bridge.dev = NULL;
+}
+
+static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
+                                   unsigned long event, void *ptr)
+{
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct netdev_notifier_changeupper_info *info;
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       struct net_device *upper_dev;
+       struct mlxsw_sp *mlxsw_sp;
+       int err;
+
+       if (!mlxsw_sp_port_dev_check(dev))
+               return NOTIFY_DONE;
+
+       mlxsw_sp_port = netdev_priv(dev);
+       mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       info = ptr;
+
+       switch (event) {
+       case NETDEV_PRECHANGEUPPER:
+               upper_dev = info->upper_dev;
+               /* HW limitation forbids to put ports to multiple bridges. */
+               if (info->master && info->linking &&
+                   netif_is_bridge_master(upper_dev) &&
+                   !mlxsw_sp_master_bridge_check(mlxsw_sp, upper_dev))
+                       return NOTIFY_BAD;
+               break;
+       case NETDEV_CHANGEUPPER:
+               upper_dev = info->upper_dev;
+               if (info->master &&
+                   netif_is_bridge_master(upper_dev)) {
+                       if (info->linking) {
+                               err = mlxsw_sp_port_bridge_join(mlxsw_sp_port);
+                               if (err)
+                                       netdev_err(dev, "Failed to join bridge\n");
+                               mlxsw_sp_master_bridge_inc(mlxsw_sp, upper_dev);
+                               mlxsw_sp_port->bridged = 1;
+                       } else {
+                               err = mlxsw_sp_port_bridge_leave(mlxsw_sp_port);
+                               if (err)
+                                       netdev_err(dev, "Failed to leave bridge\n");
+                               mlxsw_sp_port->bridged = 0;
+                               mlxsw_sp_master_bridge_dec(mlxsw_sp, upper_dev);
+                       }
+               }
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block mlxsw_sp_netdevice_nb __read_mostly = {
+       .notifier_call = mlxsw_sp_netdevice_event,
+};
+
+static int __init mlxsw_sp_module_init(void)
+{
+       int err;
+
+       register_netdevice_notifier(&mlxsw_sp_netdevice_nb);
+       err = mlxsw_core_driver_register(&mlxsw_sp_driver);
+       if (err)
+               goto err_core_driver_register;
+       return 0;
+
+err_core_driver_register:
+       unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb);
+       return err;
+}
+
+static void __exit mlxsw_sp_module_exit(void)
+{
+       mlxsw_core_driver_unregister(&mlxsw_sp_driver);
+       unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb);
+}
+
+module_init(mlxsw_sp_module_init);
+module_exit(mlxsw_sp_module_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox Spectrum driver");
+MODULE_MLXSW_DRIVER_ALIAS(MLXSW_DEVICE_KIND_SPECTRUM);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
new file mode 100644 (file)
index 0000000..4365c8b
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+ * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com>
+ * Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com>
+ * Copyright (c) 2015 Elad Raz <eladr@mellanox.com>
+ *
+ * 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 names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
+ */
+
+#ifndef _MLXSW_SPECTRUM_H
+#define _MLXSW_SPECTRUM_H
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/bitops.h>
+#include <linux/if_vlan.h>
+#include <net/switchdev.h>
+
+#include "core.h"
+
+#define MLXSW_SP_VFID_BASE VLAN_N_VID
+
+struct mlxsw_sp_port;
+
+struct mlxsw_sp {
+       unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)];
+       unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)];
+       struct mlxsw_sp_port **ports;
+       struct mlxsw_core *core;
+       const struct mlxsw_bus_info *bus_info;
+       unsigned char base_mac[ETH_ALEN];
+       struct {
+               struct delayed_work dw;
+#define MLXSW_SP_DEFAULT_LEARNING_INTERVAL 100
+               unsigned int interval; /* ms */
+       } fdb_notify;
+#define MLXSW_SP_DEFAULT_AGEING_TIME 300
+       u32 ageing_time;
+       struct {
+               struct net_device *dev;
+               unsigned int ref_count;
+       } master_bridge;
+};
+
+struct mlxsw_sp_port_pcpu_stats {
+       u64                     rx_packets;
+       u64                     rx_bytes;
+       u64                     tx_packets;
+       u64                     tx_bytes;
+       struct u64_stats_sync   syncp;
+       u32                     tx_dropped;
+};
+
+struct mlxsw_sp_port {
+       struct net_device *dev;
+       struct mlxsw_sp_port_pcpu_stats __percpu *pcpu_stats;
+       struct mlxsw_sp *mlxsw_sp;
+       u8 local_port;
+       u8 stp_state;
+       u8 learning:1,
+          learning_sync:1,
+          uc_flood:1,
+          bridged:1;
+       u16 pvid;
+       /* 802.1Q bridge VLANs */
+       unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+       /* VLAN interfaces */
+       unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)];
+       u16 nr_vfids;
+};
+
+enum mlxsw_sp_flood_table {
+       MLXSW_SP_FLOOD_TABLE_UC,
+       MLXSW_SP_FLOOD_TABLE_BM,
+};
+
+int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp);
+int mlxsw_sp_port_buffers_init(struct mlxsw_sp_port *mlxsw_sp_port);
+
+int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp);
+void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp);
+int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port);
+void mlxsw_sp_port_switchdev_init(struct mlxsw_sp_port *mlxsw_sp_port);
+void mlxsw_sp_port_switchdev_fini(struct mlxsw_sp_port *mlxsw_sp_port);
+int mlxsw_sp_port_vid_to_fid_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                enum mlxsw_reg_svfa_mt mt, bool valid, u16 fid,
+                                u16 vid);
+int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin,
+                          u16 vid_end, bool is_member, bool untagged);
+int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
+                         u16 vid);
+int mlxsw_sp_port_kill_vid(struct net_device *dev,
+                          __be16 __always_unused proto, u16 vid);
+
+#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c
new file mode 100644 (file)
index 0000000..d59195e
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c
+ * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com>
+ *
+ * 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 names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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/kernel.h>
+#include <linux/types.h>
+
+#include "spectrum.h"
+#include "core.h"
+#include "port.h"
+#include "reg.h"
+
+struct mlxsw_sp_pb {
+       u8 index;
+       u16 size;
+};
+
+#define MLXSW_SP_PB(_index, _size)     \
+       {                               \
+               .index = _index,        \
+               .size = _size,          \
+       }
+
+static const struct mlxsw_sp_pb mlxsw_sp_pbs[] = {
+       MLXSW_SP_PB(0, 208),
+       MLXSW_SP_PB(1, 208),
+       MLXSW_SP_PB(2, 208),
+       MLXSW_SP_PB(3, 208),
+       MLXSW_SP_PB(4, 208),
+       MLXSW_SP_PB(5, 208),
+       MLXSW_SP_PB(6, 208),
+       MLXSW_SP_PB(7, 208),
+       MLXSW_SP_PB(9, 208),
+};
+
+#define MLXSW_SP_PBS_LEN ARRAY_SIZE(mlxsw_sp_pbs)
+
+static int mlxsw_sp_port_pb_init(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       char pbmc_pl[MLXSW_REG_PBMC_LEN];
+       int i;
+
+       mlxsw_reg_pbmc_pack(pbmc_pl, mlxsw_sp_port->local_port,
+                           0xffff, 0xffff / 2);
+       for (i = 0; i < MLXSW_SP_PBS_LEN; i++) {
+               const struct mlxsw_sp_pb *pb;
+
+               pb = &mlxsw_sp_pbs[i];
+               mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, pb->index, pb->size);
+       }
+       return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core,
+                              MLXSW_REG(pbmc), pbmc_pl);
+}
+
+#define MLXSW_SP_SB_BYTES_PER_CELL 96
+
+struct mlxsw_sp_sb_pool {
+       u8 pool;
+       enum mlxsw_reg_sbpr_dir dir;
+       enum mlxsw_reg_sbpr_mode mode;
+       u32 size;
+};
+
+#define MLXSW_SP_SB_POOL_INGRESS_SIZE                          \
+       ((15000000 - (2 * 20000 * MLXSW_PORT_MAX_PORTS)) /      \
+        MLXSW_SP_SB_BYTES_PER_CELL)
+#define MLXSW_SP_SB_POOL_EGRESS_SIZE                           \
+       ((14000000 - (8 * 1500 * MLXSW_PORT_MAX_PORTS)) /       \
+        MLXSW_SP_SB_BYTES_PER_CELL)
+
+#define MLXSW_SP_SB_POOL(_pool, _dir, _mode, _size)            \
+       {                                                       \
+               .pool = _pool,                                  \
+               .dir = _dir,                                    \
+               .mode = _mode,                                  \
+               .size = _size,                                  \
+       }
+
+#define MLXSW_SP_SB_POOL_INGRESS(_pool, _size)                 \
+       MLXSW_SP_SB_POOL(_pool, MLXSW_REG_SBPR_DIR_INGRESS,     \
+                        MLXSW_REG_SBPR_MODE_DYNAMIC, _size)
+
+#define MLXSW_SP_SB_POOL_EGRESS(_pool, _size)                  \
+       MLXSW_SP_SB_POOL(_pool, MLXSW_REG_SBPR_DIR_EGRESS,      \
+                        MLXSW_REG_SBPR_MODE_DYNAMIC, _size)
+
+static const struct mlxsw_sp_sb_pool mlxsw_sp_sb_pools[] = {
+       MLXSW_SP_SB_POOL_INGRESS(0, MLXSW_SP_SB_POOL_INGRESS_SIZE),
+       MLXSW_SP_SB_POOL_INGRESS(1, 0),
+       MLXSW_SP_SB_POOL_INGRESS(2, 0),
+       MLXSW_SP_SB_POOL_INGRESS(3, 0),
+       MLXSW_SP_SB_POOL_EGRESS(0, MLXSW_SP_SB_POOL_EGRESS_SIZE),
+       MLXSW_SP_SB_POOL_EGRESS(1, 0),
+       MLXSW_SP_SB_POOL_EGRESS(2, 0),
+       MLXSW_SP_SB_POOL_EGRESS(2, MLXSW_SP_SB_POOL_EGRESS_SIZE),
+};
+
+#define MLXSW_SP_SB_POOLS_LEN ARRAY_SIZE(mlxsw_sp_sb_pools)
+
+static int mlxsw_sp_sb_pools_init(struct mlxsw_sp *mlxsw_sp)
+{
+       char sbpr_pl[MLXSW_REG_SBPR_LEN];
+       int i;
+       int err;
+
+       for (i = 0; i < MLXSW_SP_SB_POOLS_LEN; i++) {
+               const struct mlxsw_sp_sb_pool *pool;
+
+               pool = &mlxsw_sp_sb_pools[i];
+               mlxsw_reg_sbpr_pack(sbpr_pl, pool->pool, pool->dir,
+                                   pool->mode, pool->size);
+               err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbpr), sbpr_pl);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+struct mlxsw_sp_sb_cm {
+       union {
+               u8 pg;
+               u8 tc;
+       } u;
+       enum mlxsw_reg_sbcm_dir dir;
+       u32 min_buff;
+       u32 max_buff;
+       u8 pool;
+};
+
+#define MLXSW_SP_SB_CM(_pg_tc, _dir, _min_buff, _max_buff, _pool)      \
+       {                                                               \
+               .u.pg = _pg_tc,                                         \
+               .dir = _dir,                                            \
+               .min_buff = _min_buff,                                  \
+               .max_buff = _max_buff,                                  \
+               .pool = _pool,                                          \
+       }
+
+#define MLXSW_SP_SB_CM_INGRESS(_pg, _min_buff, _max_buff)              \
+       MLXSW_SP_SB_CM(_pg, MLXSW_REG_SBCM_DIR_INGRESS,                 \
+                      _min_buff, _max_buff, 0)
+
+#define MLXSW_SP_SB_CM_EGRESS(_tc, _min_buff, _max_buff)               \
+       MLXSW_SP_SB_CM(_tc, MLXSW_REG_SBCM_DIR_EGRESS,                  \
+                      _min_buff, _max_buff, 0)
+
+#define MLXSW_SP_CPU_PORT_SB_CM_EGRESS(_tc)                            \
+       MLXSW_SP_SB_CM(_tc, MLXSW_REG_SBCM_DIR_EGRESS, 104, 2, 3)
+
+static const struct mlxsw_sp_sb_cm mlxsw_sp_sb_cms[] = {
+       MLXSW_SP_SB_CM_INGRESS(0, 10000 / MLXSW_SP_SB_BYTES_PER_CELL, 8),
+       MLXSW_SP_SB_CM_INGRESS(1, 0, 0),
+       MLXSW_SP_SB_CM_INGRESS(2, 0, 0),
+       MLXSW_SP_SB_CM_INGRESS(3, 0, 0),
+       MLXSW_SP_SB_CM_INGRESS(4, 0, 0),
+       MLXSW_SP_SB_CM_INGRESS(5, 0, 0),
+       MLXSW_SP_SB_CM_INGRESS(6, 0, 0),
+       MLXSW_SP_SB_CM_INGRESS(7, 0, 0),
+       MLXSW_SP_SB_CM_INGRESS(9, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff),
+       MLXSW_SP_SB_CM_EGRESS(0, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9),
+       MLXSW_SP_SB_CM_EGRESS(1, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9),
+       MLXSW_SP_SB_CM_EGRESS(2, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9),
+       MLXSW_SP_SB_CM_EGRESS(3, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9),
+       MLXSW_SP_SB_CM_EGRESS(4, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9),
+       MLXSW_SP_SB_CM_EGRESS(5, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9),
+       MLXSW_SP_SB_CM_EGRESS(6, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9),
+       MLXSW_SP_SB_CM_EGRESS(7, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9),
+       MLXSW_SP_SB_CM_EGRESS(8, 0, 0),
+       MLXSW_SP_SB_CM_EGRESS(9, 0, 0),
+       MLXSW_SP_SB_CM_EGRESS(10, 0, 0),
+       MLXSW_SP_SB_CM_EGRESS(11, 0, 0),
+       MLXSW_SP_SB_CM_EGRESS(12, 0, 0),
+       MLXSW_SP_SB_CM_EGRESS(13, 0, 0),
+       MLXSW_SP_SB_CM_EGRESS(14, 0, 0),
+       MLXSW_SP_SB_CM_EGRESS(15, 0, 0),
+       MLXSW_SP_SB_CM_EGRESS(16, 1, 0xff),
+};
+
+#define MLXSW_SP_SB_CMS_LEN ARRAY_SIZE(mlxsw_sp_sb_cms)
+
+static const struct mlxsw_sp_sb_cm mlxsw_sp_cpu_port_sb_cms[] = {
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(0),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(1),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(2),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(3),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(4),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(5),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(6),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(7),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(8),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(9),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(10),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(11),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(12),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(13),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(14),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(15),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(16),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(17),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(18),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(19),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(20),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(21),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(22),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(23),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(24),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(25),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(26),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(27),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(28),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(29),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(30),
+       MLXSW_SP_CPU_PORT_SB_CM_EGRESS(31),
+};
+
+#define MLXSW_SP_CPU_PORT_SB_MCS_LEN \
+       ARRAY_SIZE(mlxsw_sp_cpu_port_sb_cms)
+
+static int mlxsw_sp_sb_cms_init(struct mlxsw_sp *mlxsw_sp, u8 local_port,
+                               const struct mlxsw_sp_sb_cm *cms,
+                               size_t cms_len)
+{
+       char sbcm_pl[MLXSW_REG_SBCM_LEN];
+       int i;
+       int err;
+
+       for (i = 0; i < cms_len; i++) {
+               const struct mlxsw_sp_sb_cm *cm;
+
+               cm = &cms[i];
+               mlxsw_reg_sbcm_pack(sbcm_pl, local_port, cm->u.pg, cm->dir,
+                                   cm->min_buff, cm->max_buff, cm->pool);
+               err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbcm), sbcm_pl);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static int mlxsw_sp_port_sb_cms_init(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       return mlxsw_sp_sb_cms_init(mlxsw_sp_port->mlxsw_sp,
+                                   mlxsw_sp_port->local_port, mlxsw_sp_sb_cms,
+                                   MLXSW_SP_SB_CMS_LEN);
+}
+
+static int mlxsw_sp_cpu_port_sb_cms_init(struct mlxsw_sp *mlxsw_sp)
+{
+       return mlxsw_sp_sb_cms_init(mlxsw_sp, 0, mlxsw_sp_cpu_port_sb_cms,
+                                   MLXSW_SP_CPU_PORT_SB_MCS_LEN);
+}
+
+struct mlxsw_sp_sb_pm {
+       u8 pool;
+       enum mlxsw_reg_sbpm_dir dir;
+       u32 min_buff;
+       u32 max_buff;
+};
+
+#define MLXSW_SP_SB_PM(_pool, _dir, _min_buff, _max_buff)      \
+       {                                                       \
+               .pool = _pool,                                  \
+               .dir = _dir,                                    \
+               .min_buff = _min_buff,                          \
+               .max_buff = _max_buff,                          \
+       }
+
+#define MLXSW_SP_SB_PM_INGRESS(_pool, _min_buff, _max_buff)    \
+       MLXSW_SP_SB_PM(_pool, MLXSW_REG_SBPM_DIR_INGRESS,       \
+                      _min_buff, _max_buff)
+
+#define MLXSW_SP_SB_PM_EGRESS(_pool, _min_buff, _max_buff)     \
+       MLXSW_SP_SB_PM(_pool, MLXSW_REG_SBPM_DIR_EGRESS,        \
+                      _min_buff, _max_buff)
+
+static const struct mlxsw_sp_sb_pm mlxsw_sp_sb_pms[] = {
+       MLXSW_SP_SB_PM_INGRESS(0, 0, 0xff),
+       MLXSW_SP_SB_PM_INGRESS(1, 0, 0),
+       MLXSW_SP_SB_PM_INGRESS(2, 0, 0),
+       MLXSW_SP_SB_PM_INGRESS(3, 0, 0),
+       MLXSW_SP_SB_PM_EGRESS(0, 0, 7),
+       MLXSW_SP_SB_PM_EGRESS(1, 0, 0),
+       MLXSW_SP_SB_PM_EGRESS(2, 0, 0),
+       MLXSW_SP_SB_PM_EGRESS(3, 0, 0),
+};
+
+#define MLXSW_SP_SB_PMS_LEN ARRAY_SIZE(mlxsw_sp_sb_pms)
+
+static int mlxsw_sp_port_sb_pms_init(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       char sbpm_pl[MLXSW_REG_SBPM_LEN];
+       int i;
+       int err;
+
+       for (i = 0; i < MLXSW_SP_SB_PMS_LEN; i++) {
+               const struct mlxsw_sp_sb_pm *pm;
+
+               pm = &mlxsw_sp_sb_pms[i];
+               mlxsw_reg_sbpm_pack(sbpm_pl, mlxsw_sp_port->local_port,
+                                   pm->pool, pm->dir,
+                                   pm->min_buff, pm->max_buff);
+               err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core,
+                                     MLXSW_REG(sbpm), sbpm_pl);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+struct mlxsw_sp_sb_mm {
+       u8 prio;
+       u32 min_buff;
+       u32 max_buff;
+       u8 pool;
+};
+
+#define MLXSW_SP_SB_MM(_prio, _min_buff, _max_buff, _pool)     \
+       {                                                       \
+               .prio = _prio,                                  \
+               .min_buff = _min_buff,                          \
+               .max_buff = _max_buff,                          \
+               .pool = _pool,                                  \
+       }
+
+static const struct mlxsw_sp_sb_mm mlxsw_sp_sb_mms[] = {
+       MLXSW_SP_SB_MM(0, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(1, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(2, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(3, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(4, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(5, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(6, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(7, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(8, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(9, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(10, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(11, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(12, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(13, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+       MLXSW_SP_SB_MM(14, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0),
+};
+
+#define MLXSW_SP_SB_MMS_LEN ARRAY_SIZE(mlxsw_sp_sb_mms)
+
+static int mlxsw_sp_sb_mms_init(struct mlxsw_sp *mlxsw_sp)
+{
+       char sbmm_pl[MLXSW_REG_SBMM_LEN];
+       int i;
+       int err;
+
+       for (i = 0; i < MLXSW_SP_SB_MMS_LEN; i++) {
+               const struct mlxsw_sp_sb_mm *mc;
+
+               mc = &mlxsw_sp_sb_mms[i];
+               mlxsw_reg_sbmm_pack(sbmm_pl, mc->prio, mc->min_buff,
+                                   mc->max_buff, mc->pool);
+               err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbmm), sbmm_pl);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp)
+{
+       int err;
+
+       err = mlxsw_sp_sb_pools_init(mlxsw_sp);
+       if (err)
+               return err;
+       err = mlxsw_sp_cpu_port_sb_cms_init(mlxsw_sp);
+       if (err)
+               return err;
+       err = mlxsw_sp_sb_mms_init(mlxsw_sp);
+
+       return err;
+}
+
+int mlxsw_sp_port_buffers_init(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       int err;
+
+       err = mlxsw_sp_port_pb_init(mlxsw_sp_port);
+       if (err)
+               return err;
+       err = mlxsw_sp_port_sb_cms_init(mlxsw_sp_port);
+       if (err)
+               return err;
+       err = mlxsw_sp_port_sb_pms_init(mlxsw_sp_port);
+
+       return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
new file mode 100644 (file)
index 0000000..617fb22
--- /dev/null
@@ -0,0 +1,903 @@
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+ * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com>
+ * Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com>
+ * Copyright (c) 2015 Elad Raz <eladr@mellanox.com>
+ *
+ * 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 names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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/kernel.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/skbuff.h>
+#include <linux/if_vlan.h>
+#include <linux/if_bridge.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <net/switchdev.h>
+
+#include "spectrum.h"
+#include "core.h"
+#include "reg.h"
+
+static int mlxsw_sp_port_attr_get(struct net_device *dev,
+                                 struct switchdev_attr *attr)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+
+       switch (attr->id) {
+       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
+               attr->u.ppid.id_len = sizeof(mlxsw_sp->base_mac);
+               memcpy(&attr->u.ppid.id, &mlxsw_sp->base_mac,
+                      attr->u.ppid.id_len);
+               break;
+       case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+               attr->u.brport_flags =
+                       (mlxsw_sp_port->learning ? BR_LEARNING : 0) |
+                       (mlxsw_sp_port->learning_sync ? BR_LEARNING_SYNC : 0) |
+                       (mlxsw_sp_port->uc_flood ? BR_FLOOD : 0);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int mlxsw_sp_port_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                      u8 state)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       enum mlxsw_reg_spms_state spms_state;
+       char *spms_pl;
+       u16 vid;
+       int err;
+
+       switch (state) {
+       case BR_STATE_DISABLED: /* fall-through */
+       case BR_STATE_FORWARDING:
+               spms_state = MLXSW_REG_SPMS_STATE_FORWARDING;
+               break;
+       case BR_STATE_LISTENING: /* fall-through */
+       case BR_STATE_LEARNING:
+               spms_state = MLXSW_REG_SPMS_STATE_LEARNING;
+               break;
+       case BR_STATE_BLOCKING:
+               spms_state = MLXSW_REG_SPMS_STATE_DISCARDING;
+               break;
+       default:
+               BUG();
+       }
+
+       spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL);
+       if (!spms_pl)
+               return -ENOMEM;
+       mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port);
+       for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID)
+               mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state);
+
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl);
+       kfree(spms_pl);
+       return err;
+}
+
+static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                           struct switchdev_trans *trans,
+                                           u8 state)
+{
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
+       mlxsw_sp_port->stp_state = state;
+       return mlxsw_sp_port_stp_state_set(mlxsw_sp_port, state);
+}
+
+static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                    u16 fid_begin, u16 fid_end, bool set,
+                                    bool only_uc)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       u16 range = fid_end - fid_begin + 1;
+       char *sftr_pl;
+       int err;
+
+       sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
+       if (!sftr_pl)
+               return -ENOMEM;
+
+       mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid_begin,
+                           MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range,
+                           mlxsw_sp_port->local_port, set);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
+       if (err)
+               goto buffer_out;
+
+       /* Flooding control allows one to decide whether a given port will
+        * flood unicast traffic for which there is no FDB entry.
+        */
+       if (only_uc)
+               goto buffer_out;
+
+       mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid_begin,
+                           MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range,
+                           mlxsw_sp_port->local_port, set);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
+
+buffer_out:
+       kfree(sftr_pl);
+       return err;
+}
+
+static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                     bool set)
+{
+       struct net_device *dev = mlxsw_sp_port->dev;
+       u16 vid, last_visited_vid;
+       int err;
+
+       for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
+               err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, vid, set,
+                                               true);
+               if (err) {
+                       last_visited_vid = vid;
+                       goto err_port_flood_set;
+               }
+       }
+
+       return 0;
+
+err_port_flood_set:
+       for_each_set_bit(vid, mlxsw_sp_port->active_vlans, last_visited_vid)
+               __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, vid, !set, true);
+       netdev_err(dev, "Failed to configure unicast flooding\n");
+       return err;
+}
+
+static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                          struct switchdev_trans *trans,
+                                          unsigned long brport_flags)
+{
+       unsigned long uc_flood = mlxsw_sp_port->uc_flood ? BR_FLOOD : 0;
+       bool set;
+       int err;
+
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
+       if ((uc_flood ^ brport_flags) & BR_FLOOD) {
+               set = mlxsw_sp_port->uc_flood ? false : true;
+               err = mlxsw_sp_port_uc_flood_set(mlxsw_sp_port, set);
+               if (err)
+                       return err;
+       }
+
+       mlxsw_sp_port->uc_flood = brport_flags & BR_FLOOD ? 1 : 0;
+       mlxsw_sp_port->learning = brport_flags & BR_LEARNING ? 1 : 0;
+       mlxsw_sp_port->learning_sync = brport_flags & BR_LEARNING_SYNC ? 1 : 0;
+
+       return 0;
+}
+
+static int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time)
+{
+       char sfdat_pl[MLXSW_REG_SFDAT_LEN];
+       int err;
+
+       mlxsw_reg_sfdat_pack(sfdat_pl, ageing_time);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdat), sfdat_pl);
+       if (err)
+               return err;
+       mlxsw_sp->ageing_time = ageing_time;
+       return 0;
+}
+
+static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                           struct switchdev_trans *trans,
+                                           unsigned long ageing_clock_t)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
+       u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
+
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
+       return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time);
+}
+
+static int mlxsw_sp_port_attr_set(struct net_device *dev,
+                                 const struct switchdev_attr *attr,
+                                 struct switchdev_trans *trans)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       int err = 0;
+
+       switch (attr->id) {
+       case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+               err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans,
+                                                      attr->u.stp_state);
+               break;
+       case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+               err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, trans,
+                                                     attr->u.brport_flags);
+               break;
+       case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
+               err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans,
+                                                      attr->u.ageing_time);
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+
+       return err;
+}
+
+static int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char spvid_pl[MLXSW_REG_SPVID_LEN];
+
+       mlxsw_reg_spvid_pack(spvid_pl, mlxsw_sp_port->local_port, vid);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvid), spvid_pl);
+}
+
+static int mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, u16 fid)
+{
+       char sfmr_pl[MLXSW_REG_SFMR_LEN];
+       int err;
+
+       mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID, fid, fid);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
+
+       if (err)
+               return err;
+
+       set_bit(fid, mlxsw_sp->active_fids);
+       return 0;
+}
+
+static void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, u16 fid)
+{
+       char sfmr_pl[MLXSW_REG_SFMR_LEN];
+
+       clear_bit(fid, mlxsw_sp->active_fids);
+
+       mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID,
+                           fid, fid);
+       mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
+}
+
+static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
+{
+       enum mlxsw_reg_svfa_mt mt;
+
+       if (mlxsw_sp_port->nr_vfids)
+               mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
+       else
+               mt = MLXSW_REG_SVFA_MT_VID_TO_FID;
+
+       return mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, true, fid, fid);
+}
+
+static int mlxsw_sp_port_fid_unmap(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
+{
+       enum mlxsw_reg_svfa_mt mt;
+
+       if (!mlxsw_sp_port->nr_vfids)
+               return 0;
+
+       mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
+       return mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, false, fid, fid);
+}
+
+static int mlxsw_sp_port_add_vids(struct net_device *dev, u16 vid_begin,
+                                 u16 vid_end)
+{
+       u16 vid;
+       int err;
+
+       for (vid = vid_begin; vid <= vid_end; vid++) {
+               err = mlxsw_sp_port_add_vid(dev, 0, vid);
+               if (err)
+                       goto err_port_add_vid;
+       }
+       return 0;
+
+err_port_add_vid:
+       for (vid--; vid >= vid_begin; vid--)
+               mlxsw_sp_port_kill_vid(dev, 0, vid);
+       return err;
+}
+
+static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
+                                    u16 vid_begin, u16 vid_end,
+                                    bool flag_untagged, bool flag_pvid)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       struct net_device *dev = mlxsw_sp_port->dev;
+       enum mlxsw_reg_svfa_mt mt;
+       u16 vid, vid_e;
+       int err;
+
+       /* In case this is invoked with BRIDGE_FLAGS_SELF and port is
+        * not bridged, then packets ingressing through the port with
+        * the specified VIDs will be directed to CPU.
+        */
+       if (!mlxsw_sp_port->bridged)
+               return mlxsw_sp_port_add_vids(dev, vid_begin, vid_end);
+
+       for (vid = vid_begin; vid <= vid_end; vid++) {
+               if (!test_bit(vid, mlxsw_sp->active_fids)) {
+                       err = mlxsw_sp_fid_create(mlxsw_sp, vid);
+                       if (err) {
+                               netdev_err(dev, "Failed to create FID=%d\n",
+                                          vid);
+                               return err;
+                       }
+
+                       /* When creating a FID, we set a VID to FID mapping
+                        * regardless of the port's mode.
+                        */
+                       mt = MLXSW_REG_SVFA_MT_VID_TO_FID;
+                       err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt,
+                                                          true, vid, vid);
+                       if (err) {
+                               netdev_err(dev, "Failed to create FID=VID=%d mapping\n",
+                                          vid);
+                               return err;
+                       }
+               }
+
+               /* Set FID mapping according to port's mode */
+               err = mlxsw_sp_port_fid_map(mlxsw_sp_port, vid);
+               if (err) {
+                       netdev_err(dev, "Failed to map FID=%d", vid);
+                       return err;
+               }
+       }
+
+       err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid_begin, vid_end,
+                                       true, false);
+       if (err) {
+               netdev_err(dev, "Failed to configure flooding\n");
+               return err;
+       }
+
+       for (vid = vid_begin; vid <= vid_end;
+            vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
+               vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
+                           vid_end);
+
+               err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, true,
+                                            flag_untagged);
+               if (err) {
+                       netdev_err(mlxsw_sp_port->dev, "Unable to add VIDs %d-%d\n",
+                                  vid, vid_e);
+                       return err;
+               }
+       }
+
+       vid = vid_begin;
+       if (flag_pvid && mlxsw_sp_port->pvid != vid) {
+               err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid);
+               if (err) {
+                       netdev_err(mlxsw_sp_port->dev, "Unable to add PVID %d\n",
+                                  vid);
+                       return err;
+               }
+               mlxsw_sp_port->pvid = vid;
+       }
+
+       /* Changing activity bits only if HW operation succeded */
+       for (vid = vid_begin; vid <= vid_end; vid++)
+               set_bit(vid, mlxsw_sp_port->active_vlans);
+
+       return mlxsw_sp_port_stp_state_set(mlxsw_sp_port,
+                                          mlxsw_sp_port->stp_state);
+}
+
+static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
+                                  const struct switchdev_obj_port_vlan *vlan,
+                                  struct switchdev_trans *trans)
+{
+       bool untagged_flag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+       bool pvid_flag = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
+       return __mlxsw_sp_port_vlans_add(mlxsw_sp_port,
+                                        vlan->vid_begin, vlan->vid_end,
+                                        untagged_flag, pvid_flag);
+}
+
+static int mlxsw_sp_port_fdb_op(struct mlxsw_sp_port *mlxsw_sp_port,
+                               const char *mac, u16 vid, bool adding,
+                               bool dynamic)
+{
+       enum mlxsw_reg_sfd_rec_policy policy;
+       enum mlxsw_reg_sfd_op op;
+       char *sfd_pl;
+       int err;
+
+       if (!vid)
+               vid = mlxsw_sp_port->pvid;
+
+       sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
+       if (!sfd_pl)
+               return -ENOMEM;
+
+       policy = dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
+                          MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY;
+       op = adding ? MLXSW_REG_SFD_OP_WRITE_EDIT :
+                     MLXSW_REG_SFD_OP_WRITE_REMOVE;
+       mlxsw_reg_sfd_pack(sfd_pl, op, 0);
+       mlxsw_reg_sfd_uc_pack(sfd_pl, 0, policy,
+                             mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP,
+                             mlxsw_sp_port->local_port);
+       err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(sfd),
+                             sfd_pl);
+       kfree(sfd_pl);
+
+       return err;
+}
+
+static int
+mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port,
+                            const struct switchdev_obj_port_fdb *fdb,
+                            struct switchdev_trans *trans)
+{
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
+       return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid,
+                                   true, false);
+}
+
+static int mlxsw_sp_port_obj_add(struct net_device *dev,
+                                const struct switchdev_obj *obj,
+                                struct switchdev_trans *trans)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       int err = 0;
+
+       switch (obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               err = mlxsw_sp_port_vlans_add(mlxsw_sp_port,
+                                             SWITCHDEV_OBJ_PORT_VLAN(obj),
+                                             trans);
+               break;
+       case SWITCHDEV_OBJ_ID_PORT_FDB:
+               err = mlxsw_sp_port_fdb_static_add(mlxsw_sp_port,
+                                                  SWITCHDEV_OBJ_PORT_FDB(obj),
+                                                  trans);
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+
+       return err;
+}
+
+static int mlxsw_sp_port_kill_vids(struct net_device *dev, u16 vid_begin,
+                                  u16 vid_end)
+{
+       u16 vid;
+       int err;
+
+       for (vid = vid_begin; vid <= vid_end; vid++) {
+               err = mlxsw_sp_port_kill_vid(dev, 0, vid);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
+                                    u16 vid_begin, u16 vid_end, bool init)
+{
+       struct net_device *dev = mlxsw_sp_port->dev;
+       u16 vid, vid_e;
+       int err;
+
+       /* In case this is invoked with BRIDGE_FLAGS_SELF and port is
+        * not bridged, then prevent packets ingressing through the
+        * port with the specified VIDs from being trapped to CPU.
+        */
+       if (!init && !mlxsw_sp_port->bridged)
+               return mlxsw_sp_port_kill_vids(dev, vid_begin, vid_end);
+
+       for (vid = vid_begin; vid <= vid_end;
+            vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
+               vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
+                           vid_end);
+               err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, false,
+                                            false);
+               if (err) {
+                       netdev_err(mlxsw_sp_port->dev, "Unable to del VIDs %d-%d\n",
+                                  vid, vid_e);
+                       return err;
+               }
+       }
+
+       if ((mlxsw_sp_port->pvid >= vid_begin) &&
+           (mlxsw_sp_port->pvid <= vid_end)) {
+               /* Default VLAN is always 1 */
+               mlxsw_sp_port->pvid = 1;
+               err = mlxsw_sp_port_pvid_set(mlxsw_sp_port,
+                                            mlxsw_sp_port->pvid);
+               if (err) {
+                       netdev_err(mlxsw_sp_port->dev, "Unable to del PVID %d\n",
+                                  vid);
+                       return err;
+               }
+       }
+
+       if (init)
+               goto out;
+
+       err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid_begin, vid_end,
+                                       false, false);
+       if (err) {
+               netdev_err(dev, "Failed to clear flooding\n");
+               return err;
+       }
+
+       for (vid = vid_begin; vid <= vid_end; vid++) {
+               /* Remove FID mapping in case of Virtual mode */
+               err = mlxsw_sp_port_fid_unmap(mlxsw_sp_port, vid);
+               if (err) {
+                       netdev_err(dev, "Failed to unmap FID=%d", vid);
+                       return err;
+               }
+       }
+
+out:
+       /* Changing activity bits only if HW operation succeded */
+       for (vid = vid_begin; vid <= vid_end; vid++)
+               clear_bit(vid, mlxsw_sp_port->active_vlans);
+
+       return 0;
+}
+
+static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
+                                  const struct switchdev_obj_port_vlan *vlan)
+{
+       return __mlxsw_sp_port_vlans_del(mlxsw_sp_port,
+                                        vlan->vid_begin, vlan->vid_end, false);
+}
+
+static int
+mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port,
+                            const struct switchdev_obj_port_fdb *fdb)
+{
+       return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid,
+                                   false, false);
+}
+
+static int mlxsw_sp_port_obj_del(struct net_device *dev,
+                                const struct switchdev_obj *obj)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       int err = 0;
+
+       switch (obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               err = mlxsw_sp_port_vlans_del(mlxsw_sp_port,
+                                             SWITCHDEV_OBJ_PORT_VLAN(obj));
+               break;
+       case SWITCHDEV_OBJ_ID_PORT_FDB:
+               err = mlxsw_sp_port_fdb_static_del(mlxsw_sp_port,
+                                                  SWITCHDEV_OBJ_PORT_FDB(obj));
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+
+       return err;
+}
+
+static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
+                                 struct switchdev_obj_port_fdb *fdb,
+                                 switchdev_obj_dump_cb_t *cb)
+{
+       char *sfd_pl;
+       char mac[ETH_ALEN];
+       u16 vid;
+       u8 local_port;
+       u8 num_rec;
+       int stored_err = 0;
+       int i;
+       int err;
+
+       sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
+       if (!sfd_pl)
+               return -ENOMEM;
+
+       mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0);
+       do {
+               mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT);
+               err = mlxsw_reg_query(mlxsw_sp_port->mlxsw_sp->core,
+                                     MLXSW_REG(sfd), sfd_pl);
+               if (err)
+                       goto out;
+
+               num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
+
+               /* Even in case of error, we have to run the dump to the end
+                * so the session in firmware is finished.
+                */
+               if (stored_err)
+                       continue;
+
+               for (i = 0; i < num_rec; i++) {
+                       switch (mlxsw_reg_sfd_rec_type_get(sfd_pl, i)) {
+                       case MLXSW_REG_SFD_REC_TYPE_UNICAST:
+                               mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &vid,
+                                                       &local_port);
+                               if (local_port == mlxsw_sp_port->local_port) {
+                                       ether_addr_copy(fdb->addr, mac);
+                                       fdb->ndm_state = NUD_REACHABLE;
+                                       fdb->vid = vid;
+                                       err = cb(&fdb->obj);
+                                       if (err)
+                                               stored_err = err;
+                               }
+                       }
+               }
+       } while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT);
+
+out:
+       kfree(sfd_pl);
+       return stored_err ? stored_err : err;
+}
+
+static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port,
+                                  struct switchdev_obj_port_vlan *vlan,
+                                  switchdev_obj_dump_cb_t *cb)
+{
+       u16 vid;
+       int err = 0;
+
+       for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
+               vlan->flags = 0;
+               if (vid == mlxsw_sp_port->pvid)
+                       vlan->flags |= BRIDGE_VLAN_INFO_PVID;
+               vlan->vid_begin = vid;
+               vlan->vid_end = vid;
+               err = cb(&vlan->obj);
+               if (err)
+                       break;
+       }
+       return err;
+}
+
+static int mlxsw_sp_port_obj_dump(struct net_device *dev,
+                                 struct switchdev_obj *obj,
+                                 switchdev_obj_dump_cb_t *cb)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       int err = 0;
+
+       switch (obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               err = mlxsw_sp_port_vlan_dump(mlxsw_sp_port,
+                                             SWITCHDEV_OBJ_PORT_VLAN(obj), cb);
+               break;
+       case SWITCHDEV_OBJ_ID_PORT_FDB:
+               err = mlxsw_sp_port_fdb_dump(mlxsw_sp_port,
+                                            SWITCHDEV_OBJ_PORT_FDB(obj), cb);
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+
+       return err;
+}
+
+static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
+       .switchdev_port_attr_get        = mlxsw_sp_port_attr_get,
+       .switchdev_port_attr_set        = mlxsw_sp_port_attr_set,
+       .switchdev_port_obj_add         = mlxsw_sp_port_obj_add,
+       .switchdev_port_obj_del         = mlxsw_sp_port_obj_del,
+       .switchdev_port_obj_dump        = mlxsw_sp_port_obj_dump,
+};
+
+static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
+                                           char *sfn_pl, int rec_index,
+                                           bool adding)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       char mac[ETH_ALEN];
+       u8 local_port;
+       u16 vid;
+       int err;
+
+       mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &vid, &local_port);
+       mlxsw_sp_port = mlxsw_sp->ports[local_port];
+       if (!mlxsw_sp_port) {
+               dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n");
+               return;
+       }
+
+       err = mlxsw_sp_port_fdb_op(mlxsw_sp_port, mac, vid,
+                                  adding && mlxsw_sp_port->learning, true);
+       if (err) {
+               if (net_ratelimit())
+                       netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
+               return;
+       }
+
+       if (mlxsw_sp_port->learning && mlxsw_sp_port->learning_sync) {
+               struct switchdev_notifier_fdb_info info;
+               unsigned long notifier_type;
+
+               info.addr = mac;
+               info.vid = vid;
+               notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL;
+               call_switchdev_notifiers(notifier_type, mlxsw_sp_port->dev,
+                                        &info.info);
+       }
+}
+
+static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
+                                           char *sfn_pl, int rec_index)
+{
+       switch (mlxsw_reg_sfn_rec_type_get(sfn_pl, rec_index)) {
+       case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC:
+               mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
+                                               rec_index, true);
+               break;
+       case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC:
+               mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
+                                               rec_index, false);
+               break;
+       }
+}
+
+static void mlxsw_sp_fdb_notify_work_schedule(struct mlxsw_sp *mlxsw_sp)
+{
+       schedule_delayed_work(&mlxsw_sp->fdb_notify.dw,
+                             msecs_to_jiffies(mlxsw_sp->fdb_notify.interval));
+}
+
+static void mlxsw_sp_fdb_notify_work(struct work_struct *work)
+{
+       struct mlxsw_sp *mlxsw_sp;
+       char *sfn_pl;
+       u8 num_rec;
+       int i;
+       int err;
+
+       sfn_pl = kmalloc(MLXSW_REG_SFN_LEN, GFP_KERNEL);
+       if (!sfn_pl)
+               return;
+
+       mlxsw_sp = container_of(work, struct mlxsw_sp, fdb_notify.dw.work);
+
+       do {
+               mlxsw_reg_sfn_pack(sfn_pl);
+               err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl);
+               if (err) {
+                       dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n");
+                       break;
+               }
+               num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl);
+               for (i = 0; i < num_rec; i++)
+                       mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i);
+
+       } while (num_rec);
+
+       kfree(sfn_pl);
+       mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
+}
+
+static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
+{
+       int err;
+
+       err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n");
+               return err;
+       }
+       INIT_DELAYED_WORK(&mlxsw_sp->fdb_notify.dw, mlxsw_sp_fdb_notify_work);
+       mlxsw_sp->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL;
+       mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
+       return 0;
+}
+
+static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp)
+{
+       cancel_delayed_work_sync(&mlxsw_sp->fdb_notify.dw);
+}
+
+static void mlxsw_sp_fids_fini(struct mlxsw_sp *mlxsw_sp)
+{
+       u16 fid;
+
+       for_each_set_bit(fid, mlxsw_sp->active_fids, VLAN_N_VID)
+               mlxsw_sp_fid_destroy(mlxsw_sp, fid);
+}
+
+int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp)
+{
+       return mlxsw_sp_fdb_init(mlxsw_sp);
+}
+
+void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp)
+{
+       mlxsw_sp_fdb_fini(mlxsw_sp);
+       mlxsw_sp_fids_fini(mlxsw_sp);
+}
+
+int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       struct net_device *dev = mlxsw_sp_port->dev;
+       int err;
+
+       /* Allow only untagged packets to ingress and tag them internally
+        * with VID 1.
+        */
+       mlxsw_sp_port->pvid = 1;
+       err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID, true);
+       if (err) {
+               netdev_err(dev, "Unable to init VLANs\n");
+               return err;
+       }
+
+       /* Add implicit VLAN interface in the device, so that untagged
+        * packets will be classified to the default vFID.
+        */
+       err = mlxsw_sp_port_add_vid(dev, 0, 1);
+       if (err)
+               netdev_err(dev, "Failed to configure default vFID\n");
+
+       return err;
+}
+
+void mlxsw_sp_port_switchdev_init(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       mlxsw_sp_port->dev->switchdev_ops = &mlxsw_sp_port_switchdev_ops;
+}
+
+void mlxsw_sp_port_switchdev_fini(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+}
index 62cbbd1ada8da6a5d8343beb394b67dc5af6a81c..d85960cfb69438cf349517e4c15f97ede6eb1683 100644 (file)
@@ -57,13 +57,11 @@ static const char mlxsw_sx_driver_version[] = "1.0";
 
 struct mlxsw_sx_port;
 
-#define MLXSW_SW_HW_ID_LEN 6
-
 struct mlxsw_sx {
        struct mlxsw_sx_port **ports;
        struct mlxsw_core *core;
        const struct mlxsw_bus_info *bus_info;
-       u8 hw_id[MLXSW_SW_HW_ID_LEN];
+       u8 hw_id[ETH_ALEN];
 };
 
 struct mlxsw_sx_port_pcpu_stats {
@@ -868,7 +866,7 @@ static int mlxsw_sx_port_attr_get(struct net_device *dev,
        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 
        switch (attr->id) {
-       case SWITCHDEV_ATTR_PORT_PARENT_ID:
+       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
                attr->u.ppid.id_len = sizeof(mlxsw_sx->hw_id);
                memcpy(&attr->u.ppid.id, &mlxsw_sx->hw_id, attr->u.ppid.id_len);
                break;
@@ -925,7 +923,8 @@ static int mlxsw_sx_port_stp_state_set(struct mlxsw_sx_port *mlxsw_sx_port,
        spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL);
        if (!spms_pl)
                return -ENOMEM;
-       mlxsw_reg_spms_pack(spms_pl, mlxsw_sx_port->local_port, vid, state);
+       mlxsw_reg_spms_pack(spms_pl, mlxsw_sx_port->local_port);
+       mlxsw_reg_spms_vid_pack(spms_pl, vid, state);
        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spms), spms_pl);
        kfree(spms_pl);
        return err;
@@ -1148,7 +1147,7 @@ static void mlxsw_sx_pude_event_func(const struct mlxsw_reg_info *reg,
        }
 
        status = mlxsw_reg_pude_oper_status_get(pude_pl);
-       if (MLXSW_PORT_OPER_STATUS_UP == status) {
+       if (status == MLXSW_PORT_OPER_STATUS_UP) {
                netdev_info(mlxsw_sx_port->dev, "link up\n");
                netif_carrier_on(mlxsw_sx_port->dev);
        } else {
@@ -1178,8 +1177,7 @@ static int mlxsw_sx_event_register(struct mlxsw_sx *mlxsw_sx,
        if (err)
                return err;
 
-       mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD,
-                           MLXSW_REG_HTGT_TRAP_GROUP_EMAD, trap_id);
+       mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, trap_id);
        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl);
        if (err)
                goto err_event_trap_set;
@@ -1212,9 +1210,8 @@ static void mlxsw_sx_rx_listener_func(struct sk_buff *skb, u8 local_port,
        struct mlxsw_sx_port_pcpu_stats *pcpu_stats;
 
        if (unlikely(!mlxsw_sx_port)) {
-               if (net_ratelimit())
-                       dev_warn(mlxsw_sx->bus_info->dev, "Port %d: skb received for non-existent port\n",
-                                local_port);
+               dev_warn_ratelimited(mlxsw_sx->bus_info->dev, "Port %d: skb received for non-existent port\n",
+                                    local_port);
                return;
        }
 
@@ -1316,6 +1313,11 @@ static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx)
        if (err)
                return err;
 
+       mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_CTRL);
+       err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(htgt), htgt_pl);
+       if (err)
+               return err;
+
        for (i = 0; i < ARRAY_SIZE(mlxsw_sx_rx_listener); i++) {
                err = mlxsw_core_rx_listener_register(mlxsw_sx->core,
                                                      &mlxsw_sx_rx_listener[i],
@@ -1324,7 +1326,6 @@ static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx)
                        goto err_rx_listener_register;
 
                mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU,
-                                   MLXSW_REG_HTGT_TRAP_GROUP_RX,
                                    mlxsw_sx_rx_listener[i].trap_id);
                err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl);
                if (err)
@@ -1339,7 +1340,6 @@ err_rx_trap_set:
 err_rx_listener_register:
        for (i--; i >= 0; i--) {
                mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD,
-                                   MLXSW_REG_HTGT_TRAP_GROUP_RX,
                                    mlxsw_sx_rx_listener[i].trap_id);
                mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl);
 
@@ -1357,7 +1357,6 @@ static void mlxsw_sx_traps_fini(struct mlxsw_sx *mlxsw_sx)
 
        for (i = 0; i < ARRAY_SIZE(mlxsw_sx_rx_listener); i++) {
                mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD,
-                                   MLXSW_REG_HTGT_TRAP_GROUP_RX,
                                    mlxsw_sx_rx_listener[i].trap_id);
                mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl);
 
@@ -1371,25 +1370,15 @@ static int mlxsw_sx_flood_init(struct mlxsw_sx *mlxsw_sx)
 {
        char sfgc_pl[MLXSW_REG_SFGC_LEN];
        char sgcr_pl[MLXSW_REG_SGCR_LEN];
-       char *smid_pl;
        char *sftr_pl;
        int err;
 
-       /* Due to FW bug, we must configure SMID. */
-       smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL);
-       if (!smid_pl)
-               return -ENOMEM;
-       mlxsw_reg_smid_pack(smid_pl, MLXSW_PORT_MID);
-       err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(smid), smid_pl);
-       kfree(smid_pl);
-       if (err)
-               return err;
-
        /* Configure a flooding table, which includes only CPU port. */
        sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
        if (!sftr_pl)
                return -ENOMEM;
-       mlxsw_reg_sftr_pack(sftr_pl, 0, 0, MLXSW_REG_SFGC_TABLE_TYPE_SINGLE, 0);
+       mlxsw_reg_sftr_pack(sftr_pl, 0, 0, MLXSW_REG_SFGC_TABLE_TYPE_SINGLE, 0,
+                           MLXSW_PORT_CPU_PORT, true);
        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sftr), sftr_pl);
        kfree(sftr_pl);
        if (err)
index 06fc46c78a0b85b4a59c638798097291cdca4a03..fdf94720ca62554a3bc28341dcb8f857af490e97 100644 (file)
@@ -38,6 +38,7 @@
 
 #define MLXSW_TXHDR_LEN 0x10
 #define MLXSW_TXHDR_VERSION_0 0
+#define MLXSW_TXHDR_VERSION_1 1
 
 enum {
        MLXSW_TXHDR_ETH_CTL,
index 3fd8ca6d4e7ca4b02385ef8667dc33c36e0ebe13..36a09d94b368ae18508cb46e6dbb0b7e03fa2c77 100644 (file)
@@ -33,4 +33,13 @@ config ENC28J60_WRITEVERIFY
          Enable the verify after the buffer write useful for debugging purpose.
          If unsure, say N.
 
+config ENCX24J600
+    tristate "ENCX24J600 support"
+    depends on SPI
+    ---help---
+      Support for the Microchip ENC424J600/624J600 ethernet chip.
+
+      To compile this driver as a module, choose M here. The module will be
+      called encx24j600.
+
 endif # NET_VENDOR_MICROCHIP
index 573d4292b9ea96f24994a21bc32f449bcf250035..ff78f621b59a7153ec9e3dabfd99d6b028de0b7a 100644 (file)
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_ENC28J60) += enc28j60.o
+obj-$(CONFIG_ENCX24J600) += encx24j600.o encx24j600-regmap.o
diff --git a/drivers/net/ethernet/microchip/encx24j600-regmap.c b/drivers/net/ethernet/microchip/encx24j600-regmap.c
new file mode 100644 (file)
index 0000000..f3bb905
--- /dev/null
@@ -0,0 +1,513 @@
+/**
+ * Register map access API - ENCX24J600 support
+ *
+ * Copyright 2015 Gridpoint
+ *
+ * Author: Jon Ringle <jringle@gridpoint.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/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "encx24j600_hw.h"
+
+static inline bool is_bits_set(int value, int mask)
+{
+       return (value & mask) == mask;
+}
+
+static int encx24j600_switch_bank(struct encx24j600_context *ctx,
+                                        int bank)
+{
+       int ret = 0;
+
+       int bank_opcode = BANK_SELECT(bank);
+       ret = spi_write(ctx->spi, &bank_opcode, 1);
+       if (ret == 0)
+               ctx->bank = bank;
+
+       return ret;
+}
+
+static int encx24j600_cmdn(struct encx24j600_context *ctx, u8 opcode,
+                           const void *buf, size_t len)
+{
+       struct spi_message m;
+       struct spi_transfer t[2] = { { .tx_buf = &opcode, .len = 1, },
+                                    { .tx_buf = buf, .len = len }, };
+       spi_message_init(&m);
+       spi_message_add_tail(&t[0], &m);
+       spi_message_add_tail(&t[1], &m);
+
+       return spi_sync(ctx->spi, &m);
+}
+
+static void regmap_lock_mutex(void *context)
+{
+       struct encx24j600_context *ctx = context;
+       mutex_lock(&ctx->mutex);
+}
+
+static void regmap_unlock_mutex(void *context)
+{
+       struct encx24j600_context *ctx = context;
+       mutex_unlock(&ctx->mutex);
+}
+
+static int regmap_encx24j600_sfr_read(void *context, u8 reg, u8 *val,
+                                     size_t len)
+{
+       struct encx24j600_context *ctx = context;
+       u8 banked_reg = reg & ADDR_MASK;
+       u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT);
+       u8 cmd = RCRU;
+       int ret = 0;
+       int i = 0;
+       u8 tx_buf[2];
+
+       if (reg < 0x80) {
+               cmd = RCRCODE | banked_reg;
+               if ((banked_reg < 0x16) && (ctx->bank != bank))
+                       ret = encx24j600_switch_bank(ctx, bank);
+               if (unlikely(ret))
+                       return ret;
+       } else {
+               /* Translate registers that are more effecient using
+                * 3-byte SPI commands
+                */
+               switch (reg) {
+               case EGPRDPT:
+                       cmd = RGPRDPT; break;
+               case EGPWRPT:
+                       cmd = RGPWRPT; break;
+               case ERXRDPT:
+                       cmd = RRXRDPT; break;
+               case ERXWRPT:
+                       cmd = RRXWRPT; break;
+               case EUDARDPT:
+                       cmd = RUDARDPT; break;
+               case EUDAWRPT:
+                       cmd = RUDAWRPT; break;
+               case EGPDATA:
+               case ERXDATA:
+               case EUDADATA:
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       tx_buf[i++] = cmd;
+       if (cmd == RCRU)
+               tx_buf[i++] = reg;
+
+       ret = spi_write_then_read(ctx->spi, tx_buf, i, val, len);
+
+       return ret;
+}
+
+static int regmap_encx24j600_sfr_update(struct encx24j600_context *ctx,
+                                       u8 reg, u8 *val, size_t len,
+                                       u8 unbanked_cmd, u8 banked_code)
+{
+       u8 banked_reg = reg & ADDR_MASK;
+       u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT);
+       u8 cmd = unbanked_cmd;
+       struct spi_message m;
+       struct spi_transfer t[3] = { { .tx_buf = &cmd, .len = sizeof(cmd), },
+                                    { .tx_buf = &reg, .len = sizeof(reg), },
+                                    { .tx_buf = val, .len = len }, };
+
+       if (reg < 0x80) {
+               int ret = 0;
+               cmd = banked_code | banked_reg;
+               if ((banked_reg < 0x16) && (ctx->bank != bank))
+                       ret = encx24j600_switch_bank(ctx, bank);
+               if (unlikely(ret))
+                       return ret;
+       } else {
+               /* Translate registers that are more effecient using
+                * 3-byte SPI commands
+                */
+               switch (reg) {
+               case EGPRDPT:
+                       cmd = WGPRDPT; break;
+               case EGPWRPT:
+                       cmd = WGPWRPT; break;
+               case ERXRDPT:
+                       cmd = WRXRDPT; break;
+               case ERXWRPT:
+                       cmd = WRXWRPT; break;
+               case EUDARDPT:
+                       cmd = WUDARDPT; break;
+               case EUDAWRPT:
+                       cmd = WUDAWRPT; break;
+               case EGPDATA:
+               case ERXDATA:
+               case EUDADATA:
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t[0], &m);
+
+       if (cmd == unbanked_cmd) {
+               t[1].tx_buf = &reg;
+               spi_message_add_tail(&t[1], &m);
+       }
+
+       spi_message_add_tail(&t[2], &m);
+       return spi_sync(ctx->spi, &m);
+}
+
+static int regmap_encx24j600_sfr_write(void *context, u8 reg, u8 *val,
+                                      size_t len)
+{
+       struct encx24j600_context *ctx = context;
+       return regmap_encx24j600_sfr_update(ctx, reg, val, len, WCRU, WCRCODE);
+}
+
+static int regmap_encx24j600_sfr_set_bits(struct encx24j600_context *ctx,
+                                         u8 reg, u8 val)
+{
+       return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFSU, BFSCODE);
+}
+
+static int regmap_encx24j600_sfr_clr_bits(struct encx24j600_context *ctx,
+                                         u8 reg, u8 val)
+{
+       return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFCU, BFCCODE);
+}
+
+static int regmap_encx24j600_reg_update_bits(void *context, unsigned int reg,
+                                            unsigned int mask,
+                                            unsigned int val)
+{
+       struct encx24j600_context *ctx = context;
+
+       int ret = 0;
+       unsigned int set_mask = mask & val;
+       unsigned int clr_mask = mask & ~val;
+
+       if ((reg >= 0x40 && reg < 0x6c) || reg >= 0x80)
+               return -EINVAL;
+
+       if (set_mask & 0xff)
+               ret = regmap_encx24j600_sfr_set_bits(ctx, reg, set_mask);
+
+       set_mask = (set_mask & 0xff00) >> 8;
+
+       if ((set_mask & 0xff) && (ret == 0))
+               ret = regmap_encx24j600_sfr_set_bits(ctx, reg + 1, set_mask);
+
+       if ((clr_mask & 0xff) && (ret == 0))
+               ret = regmap_encx24j600_sfr_clr_bits(ctx, reg, clr_mask);
+
+       clr_mask = (clr_mask & 0xff00) >> 8;
+
+       if ((clr_mask & 0xff) && (ret == 0))
+               ret = regmap_encx24j600_sfr_clr_bits(ctx, reg + 1, clr_mask);
+
+       return ret;
+}
+
+int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data,
+                               size_t count)
+{
+       struct encx24j600_context *ctx = context;
+
+       if (reg < 0xc0)
+               return encx24j600_cmdn(ctx, reg, data, count);
+       else
+               /* SPI 1-byte command. Ignore data */
+               return spi_write(ctx->spi, &reg, 1);
+}
+EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_write);
+
+int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count)
+{
+       struct encx24j600_context *ctx = context;
+
+       if (reg == RBSEL && count > 1)
+               count = 1;
+
+       return spi_write_then_read(ctx->spi, &reg, sizeof(reg), data, count);
+}
+EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_read);
+
+static int regmap_encx24j600_write(void *context, const void *data,
+                                  size_t len)
+{
+       u8 *dout = (u8 *)data;
+       u8 reg = dout[0];
+       ++dout;
+       --len;
+
+       if (reg > 0xa0)
+               return regmap_encx24j600_spi_write(context, reg, dout, len);
+
+       if (len > 2)
+               return -EINVAL;
+
+       return regmap_encx24j600_sfr_write(context, reg, dout, len);
+}
+
+static int regmap_encx24j600_read(void *context,
+                                 const void *reg_buf, size_t reg_size,
+                                 void *val, size_t val_size)
+{
+       u8 reg = *(const u8 *)reg_buf;
+
+       if (reg_size != 1) {
+               pr_err("%s: reg=%02x reg_size=%zu\n", __func__, reg, reg_size);
+               return -EINVAL;
+       }
+
+       if (reg > 0xa0)
+               return regmap_encx24j600_spi_read(context, reg, val, val_size);
+
+       if (val_size > 2) {
+               pr_err("%s: reg=%02x val_size=%zu\n", __func__, reg, val_size);
+               return -EINVAL;
+       }
+
+       return regmap_encx24j600_sfr_read(context, reg, val, val_size);
+}
+
+static bool encx24j600_regmap_readable(struct device *dev, unsigned int reg)
+{
+       if ((reg < 0x36) ||
+           ((reg >= 0x40) && (reg < 0x4c)) ||
+           ((reg >= 0x52) && (reg < 0x56)) ||
+           ((reg >= 0x60) && (reg < 0x66)) ||
+           ((reg >= 0x68) && (reg < 0x80)) ||
+           ((reg >= 0x86) && (reg < 0x92)) ||
+           (reg == 0xc8))
+               return true;
+       else
+               return false;
+}
+
+static bool encx24j600_regmap_writeable(struct device *dev, unsigned int reg)
+{
+       if ((reg < 0x12) ||
+           ((reg >= 0x14) && (reg < 0x1a)) ||
+           ((reg >= 0x1c) && (reg < 0x36)) ||
+           ((reg >= 0x40) && (reg < 0x4c)) ||
+           ((reg >= 0x52) && (reg < 0x56)) ||
+           ((reg >= 0x60) && (reg < 0x68)) ||
+           ((reg >= 0x6c) && (reg < 0x80)) ||
+           ((reg >= 0x86) && (reg < 0x92)) ||
+           ((reg >= 0xc0) && (reg < 0xc8)) ||
+           ((reg >= 0xca) && (reg < 0xf0)))
+               return true;
+       else
+               return false;
+}
+
+static bool encx24j600_regmap_volatile(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ERXHEAD:
+       case EDMACS:
+       case ETXSTAT:
+       case ETXWIRE:
+       case ECON1:     /* Can be modified via single byte cmds */
+       case ECON2:     /* Can be modified via single byte cmds */
+       case ESTAT:
+       case EIR:       /* Can be modified via single byte cmds */
+       case MIRD:
+       case MISTAT:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+
+static bool encx24j600_regmap_precious(struct device *dev, unsigned int reg)
+{
+       /* single byte cmds are precious */
+       if (((reg >= 0xc0) && (reg < 0xc8)) ||
+           ((reg >= 0xca) && (reg < 0xf0)))
+               return true;
+       else
+               return false;
+}
+
+static int regmap_encx24j600_phy_reg_read(void *context, unsigned int reg,
+                                         unsigned int *val)
+{
+       struct encx24j600_context *ctx = context;
+       int ret;
+       unsigned int mistat;
+
+       reg = MIREGADR_VAL | (reg & PHREG_MASK);
+       ret = regmap_write(ctx->regmap, MIREGADR, reg);
+       if (unlikely(ret))
+               goto err_out;
+
+       ret = regmap_write(ctx->regmap, MICMD, MIIRD);
+       if (unlikely(ret))
+               goto err_out;
+
+       usleep_range(26, 100);
+       while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) &&
+              (mistat & BUSY))
+               cpu_relax();
+
+       if (unlikely(ret))
+               goto err_out;
+
+       ret = regmap_write(ctx->regmap, MICMD, 0);
+       if (unlikely(ret))
+               goto err_out;
+
+       ret = regmap_read(ctx->regmap, MIRD, val);
+
+err_out:
+       if (ret)
+               pr_err("%s: error %d reading reg %02x\n", __func__, ret,
+                      reg & PHREG_MASK);
+
+       return ret;
+}
+
+static int regmap_encx24j600_phy_reg_write(void *context, unsigned int reg,
+                                          unsigned int val)
+{
+       struct encx24j600_context *ctx = context;
+       int ret;
+       unsigned int mistat;
+
+       reg = MIREGADR_VAL | (reg & PHREG_MASK);
+       ret = regmap_write(ctx->regmap, MIREGADR, reg);
+       if (unlikely(ret))
+               goto err_out;
+
+       ret = regmap_write(ctx->regmap, MIWR, val);
+       if (unlikely(ret))
+               goto err_out;
+
+       usleep_range(26, 100);
+       while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) &&
+              (mistat & BUSY))
+               cpu_relax();
+
+err_out:
+       if (ret)
+               pr_err("%s: error %d writing reg %02x=%04x\n", __func__, ret,
+                      reg & PHREG_MASK, val);
+
+       return ret;
+}
+
+static bool encx24j600_phymap_readable(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case PHCON1:
+       case PHSTAT1:
+       case PHANA:
+       case PHANLPA:
+       case PHANE:
+       case PHCON2:
+       case PHSTAT2:
+       case PHSTAT3:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool encx24j600_phymap_writeable(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case PHCON1:
+       case PHCON2:
+       case PHANA:
+               return true;
+       case PHSTAT1:
+       case PHSTAT2:
+       case PHSTAT3:
+       case PHANLPA:
+       case PHANE:
+       default:
+               return false;
+       }
+}
+
+static bool encx24j600_phymap_volatile(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case PHSTAT1:
+       case PHSTAT2:
+       case PHSTAT3:
+       case PHANLPA:
+       case PHANE:
+       case PHCON2:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static struct regmap_config regcfg = {
+       .name = "reg",
+       .reg_bits = 8,
+       .val_bits = 16,
+       .max_register = 0xee,
+       .reg_stride = 2,
+       .cache_type = REGCACHE_RBTREE,
+       .val_format_endian = REGMAP_ENDIAN_LITTLE,
+       .readable_reg = encx24j600_regmap_readable,
+       .writeable_reg = encx24j600_regmap_writeable,
+       .volatile_reg = encx24j600_regmap_volatile,
+       .precious_reg = encx24j600_regmap_precious,
+       .lock = regmap_lock_mutex,
+       .unlock = regmap_unlock_mutex,
+};
+
+static struct regmap_bus regmap_encx24j600 = {
+       .write = regmap_encx24j600_write,
+       .read = regmap_encx24j600_read,
+       .reg_update_bits = regmap_encx24j600_reg_update_bits,
+};
+
+static struct regmap_config phycfg = {
+       .name = "phy",
+       .reg_bits = 8,
+       .val_bits = 16,
+       .max_register = 0x1f,
+       .cache_type = REGCACHE_RBTREE,
+       .val_format_endian = REGMAP_ENDIAN_LITTLE,
+       .readable_reg = encx24j600_phymap_readable,
+       .writeable_reg = encx24j600_phymap_writeable,
+       .volatile_reg = encx24j600_phymap_volatile,
+};
+static struct regmap_bus phymap_encx24j600 = {
+       .reg_write = regmap_encx24j600_phy_reg_write,
+       .reg_read = regmap_encx24j600_phy_reg_read,
+};
+
+void devm_regmap_init_encx24j600(struct device *dev,
+                                struct encx24j600_context *ctx)
+{
+       mutex_init(&ctx->mutex);
+       regcfg.lock_arg = ctx;
+       ctx->regmap = devm_regmap_init(dev, &regmap_encx24j600, ctx, &regcfg);
+       ctx->phymap = devm_regmap_init(dev, &phymap_encx24j600, ctx, &phycfg);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_encx24j600);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c
new file mode 100644 (file)
index 0000000..2056b71
--- /dev/null
@@ -0,0 +1,1129 @@
+/**
+ * Microchip ENCX24J600 ethernet driver
+ *
+ * Copyright (C) 2015 Gridpoint
+ * Author: Jon Ringle <jringle@gridpoint.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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/regmap.h>
+#include <linux/skbuff.h>
+#include <linux/spi/spi.h>
+
+#include "encx24j600_hw.h"
+
+#define DRV_NAME       "encx24j600"
+#define DRV_VERSION    "1.0"
+
+#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
+static int debug = -1;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
+/* SRAM memory layout:
+ *
+ * 0x0000-0x05ff TX buffers  1.5KB  (1*1536) reside in the GP area in SRAM
+ * 0x0600-0x5fff RX buffers 22.5KB (15*1536) reside in the RX area in SRAM
+ */
+#define ENC_TX_BUF_START 0x0000U
+#define ENC_RX_BUF_START 0x0600U
+#define ENC_RX_BUF_END   0x5fffU
+#define ENC_SRAM_SIZE    0x6000U
+
+enum {
+       RXFILTER_NORMAL,
+       RXFILTER_MULTI,
+       RXFILTER_PROMISC
+};
+
+struct encx24j600_priv {
+       struct net_device        *ndev;
+       struct mutex              lock; /* device access lock */
+       struct encx24j600_context ctx;
+       struct sk_buff           *tx_skb;
+       struct task_struct       *kworker_task;
+       struct kthread_worker     kworker;
+       struct kthread_work       tx_work;
+       struct kthread_work       setrx_work;
+       u16                       next_packet;
+       bool                      hw_enabled;
+       bool                      full_duplex;
+       bool                      autoneg;
+       u16                       speed;
+       int                       rxfilter;
+       u32                       msg_enable;
+};
+
+static void dump_packet(const char *msg, int len, const char *data)
+{
+       pr_debug(DRV_NAME ": %s - packet len:%d\n", msg, len);
+       print_hex_dump_bytes("pk data: ", DUMP_PREFIX_OFFSET, data, len);
+}
+
+static void encx24j600_dump_rsv(struct encx24j600_priv *priv, const char *msg,
+                               struct rsv *rsv)
+{
+       struct net_device *dev = priv->ndev;
+
+       netdev_info(dev, "RX packet Len:%d\n", rsv->len);
+       netdev_dbg(dev, "%s - NextPk: 0x%04x\n", msg,
+                  rsv->next_packet);
+       netdev_dbg(dev, "RxOK: %d, DribbleNibble: %d\n",
+                  RSV_GETBIT(rsv->rxstat, RSV_RXOK),
+                  RSV_GETBIT(rsv->rxstat, RSV_DRIBBLENIBBLE));
+       netdev_dbg(dev, "CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n",
+                  RSV_GETBIT(rsv->rxstat, RSV_CRCERROR),
+                  RSV_GETBIT(rsv->rxstat, RSV_LENCHECKERR),
+                  RSV_GETBIT(rsv->rxstat, RSV_LENOUTOFRANGE));
+       netdev_dbg(dev, "Multicast: %d, Broadcast: %d, LongDropEvent: %d, CarrierEvent: %d\n",
+                  RSV_GETBIT(rsv->rxstat, RSV_RXMULTICAST),
+                  RSV_GETBIT(rsv->rxstat, RSV_RXBROADCAST),
+                  RSV_GETBIT(rsv->rxstat, RSV_RXLONGEVDROPEV),
+                  RSV_GETBIT(rsv->rxstat, RSV_CARRIEREV));
+       netdev_dbg(dev, "ControlFrame: %d, PauseFrame: %d, UnknownOp: %d, VLanTagFrame: %d\n",
+                  RSV_GETBIT(rsv->rxstat, RSV_RXCONTROLFRAME),
+                  RSV_GETBIT(rsv->rxstat, RSV_RXPAUSEFRAME),
+                  RSV_GETBIT(rsv->rxstat, RSV_RXUNKNOWNOPCODE),
+                  RSV_GETBIT(rsv->rxstat, RSV_RXTYPEVLAN));
+}
+
+static u16 encx24j600_read_reg(struct encx24j600_priv *priv, u8 reg)
+{
+       struct net_device *dev = priv->ndev;
+       unsigned int val = 0;
+       int ret = regmap_read(priv->ctx.regmap, reg, &val);
+       if (unlikely(ret))
+               netif_err(priv, drv, dev, "%s: error %d reading reg %02x\n",
+                         __func__, ret, reg);
+       return val;
+}
+
+static void encx24j600_write_reg(struct encx24j600_priv *priv, u8 reg, u16 val)
+{
+       struct net_device *dev = priv->ndev;
+       int ret = regmap_write(priv->ctx.regmap, reg, val);
+       if (unlikely(ret))
+               netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n",
+                         __func__, ret, reg, val);
+}
+
+static void encx24j600_update_reg(struct encx24j600_priv *priv, u8 reg,
+                                 u16 mask, u16 val)
+{
+       struct net_device *dev = priv->ndev;
+       int ret = regmap_update_bits(priv->ctx.regmap, reg, mask, val);
+       if (unlikely(ret))
+               netif_err(priv, drv, dev, "%s: error %d updating reg %02x=%04x~%04x\n",
+                         __func__, ret, reg, val, mask);
+}
+
+static u16 encx24j600_read_phy(struct encx24j600_priv *priv, u8 reg)
+{
+       struct net_device *dev = priv->ndev;
+       unsigned int val = 0;
+       int ret = regmap_read(priv->ctx.phymap, reg, &val);
+       if (unlikely(ret))
+               netif_err(priv, drv, dev, "%s: error %d reading %02x\n",
+                         __func__, ret, reg);
+       return val;
+}
+
+static void encx24j600_write_phy(struct encx24j600_priv *priv, u8 reg, u16 val)
+{
+       struct net_device *dev = priv->ndev;
+       int ret = regmap_write(priv->ctx.phymap, reg, val);
+       if (unlikely(ret))
+               netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n",
+                         __func__, ret, reg, val);
+}
+
+static void encx24j600_clr_bits(struct encx24j600_priv *priv, u8 reg, u16 mask)
+{
+       encx24j600_update_reg(priv, reg, mask, 0);
+}
+
+static void encx24j600_set_bits(struct encx24j600_priv *priv, u8 reg, u16 mask)
+{
+       encx24j600_update_reg(priv, reg, mask, mask);
+}
+
+static void encx24j600_cmd(struct encx24j600_priv *priv, u8 cmd)
+{
+       struct net_device *dev = priv->ndev;
+       int ret = regmap_write(priv->ctx.regmap, cmd, 0);
+       if (unlikely(ret))
+               netif_err(priv, drv, dev, "%s: error %d with cmd %02x\n",
+                         __func__, ret, cmd);
+}
+
+static int encx24j600_raw_read(struct encx24j600_priv *priv, u8 reg, u8 *data,
+                              size_t count)
+{
+       int ret;
+       mutex_lock(&priv->ctx.mutex);
+       ret = regmap_encx24j600_spi_read(&priv->ctx, reg, data, count);
+       mutex_unlock(&priv->ctx.mutex);
+
+       return ret;
+}
+
+static int encx24j600_raw_write(struct encx24j600_priv *priv, u8 reg,
+                               const u8 *data, size_t count)
+{
+       int ret;
+       mutex_lock(&priv->ctx.mutex);
+       ret = regmap_encx24j600_spi_write(&priv->ctx, reg, data, count);
+       mutex_unlock(&priv->ctx.mutex);
+
+       return ret;
+}
+
+static void encx24j600_update_phcon1(struct encx24j600_priv *priv)
+{
+       u16 phcon1 = encx24j600_read_phy(priv, PHCON1);
+       if (priv->autoneg == AUTONEG_ENABLE) {
+               phcon1 |= ANEN | RENEG;
+       } else {
+               phcon1 &= ~ANEN;
+               if (priv->speed == SPEED_100)
+                       phcon1 |= SPD100;
+               else
+                       phcon1 &= ~SPD100;
+
+               if (priv->full_duplex)
+                       phcon1 |= PFULDPX;
+               else
+                       phcon1 &= ~PFULDPX;
+       }
+       encx24j600_write_phy(priv, PHCON1, phcon1);
+}
+
+/* Waits for autonegotiation to complete. */
+static int encx24j600_wait_for_autoneg(struct encx24j600_priv *priv)
+{
+       struct net_device *dev = priv->ndev;
+       unsigned long timeout = jiffies + msecs_to_jiffies(2000);
+       u16 phstat1;
+       u16 estat;
+       int ret = 0;
+
+       phstat1 = encx24j600_read_phy(priv, PHSTAT1);
+       while ((phstat1 & ANDONE) == 0) {
+               if (time_after(jiffies, timeout)) {
+                       u16 phstat3;
+
+                       netif_notice(priv, drv, dev, "timeout waiting for autoneg done\n");
+
+                       priv->autoneg = AUTONEG_DISABLE;
+                       phstat3 = encx24j600_read_phy(priv, PHSTAT3);
+                       priv->speed = (phstat3 & PHY3SPD100)
+                                     ? SPEED_100 : SPEED_10;
+                       priv->full_duplex = (phstat3 & PHY3DPX) ? 1 : 0;
+                       encx24j600_update_phcon1(priv);
+                       netif_notice(priv, drv, dev, "Using parallel detection: %s/%s",
+                                    priv->speed == SPEED_100 ? "100" : "10",
+                                    priv->full_duplex ? "Full" : "Half");
+
+                       return -ETIMEDOUT;
+               }
+               cpu_relax();
+               phstat1 = encx24j600_read_phy(priv, PHSTAT1);
+       }
+
+       estat = encx24j600_read_reg(priv, ESTAT);
+       if (estat & PHYDPX) {
+               encx24j600_set_bits(priv, MACON2, FULDPX);
+               encx24j600_write_reg(priv, MABBIPG, 0x15);
+       } else {
+               encx24j600_clr_bits(priv, MACON2, FULDPX);
+               encx24j600_write_reg(priv, MABBIPG, 0x12);
+               /* Max retransmittions attempt  */
+               encx24j600_write_reg(priv, MACLCON, 0x370f);
+       }
+
+       return ret;
+}
+
+/* Access the PHY to determine link status */
+static void encx24j600_check_link_status(struct encx24j600_priv *priv)
+{
+       struct net_device *dev = priv->ndev;
+       u16 estat;
+
+       estat = encx24j600_read_reg(priv, ESTAT);
+
+       if (estat & PHYLNK) {
+               if (priv->autoneg == AUTONEG_ENABLE)
+                       encx24j600_wait_for_autoneg(priv);
+
+               netif_carrier_on(dev);
+               netif_info(priv, ifup, dev, "link up\n");
+       } else {
+               netif_info(priv, ifdown, dev, "link down\n");
+
+               /* Re-enable autoneg since we won't know what we might be
+                * connected to when the link is brought back up again.
+                */
+               priv->autoneg  = AUTONEG_ENABLE;
+               priv->full_duplex = true;
+               priv->speed = SPEED_100;
+               netif_carrier_off(dev);
+       }
+}
+
+static void encx24j600_int_link_handler(struct encx24j600_priv *priv)
+{
+       struct net_device *dev = priv->ndev;
+
+       netif_dbg(priv, intr, dev, "%s", __func__);
+       encx24j600_check_link_status(priv);
+       encx24j600_clr_bits(priv, EIR, LINKIF);
+}
+
+static void encx24j600_tx_complete(struct encx24j600_priv *priv, bool err)
+{
+       struct net_device *dev = priv->ndev;
+
+       if (!priv->tx_skb) {
+               BUG();
+               return;
+       }
+
+       mutex_lock(&priv->lock);
+
+       if (err)
+               dev->stats.tx_errors++;
+       else
+               dev->stats.tx_packets++;
+
+       dev->stats.tx_bytes += priv->tx_skb->len;
+
+       encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF);
+
+       netif_dbg(priv, tx_done, dev, "TX Done%s\n", err ? ": Err" : "");
+
+       dev_kfree_skb(priv->tx_skb);
+       priv->tx_skb = NULL;
+
+       netif_wake_queue(dev);
+
+       mutex_unlock(&priv->lock);
+}
+
+static int encx24j600_receive_packet(struct encx24j600_priv *priv,
+                                    struct rsv *rsv)
+{
+       struct net_device *dev = priv->ndev;
+       struct sk_buff *skb = netdev_alloc_skb(dev, rsv->len + NET_IP_ALIGN);
+       if (!skb) {
+               pr_err_ratelimited("RX: OOM: packet dropped\n");
+               dev->stats.rx_dropped++;
+               return -ENOMEM;
+       }
+       skb_reserve(skb, NET_IP_ALIGN);
+       encx24j600_raw_read(priv, RRXDATA, skb_put(skb, rsv->len), rsv->len);
+
+       if (netif_msg_pktdata(priv))
+               dump_packet("RX", skb->len, skb->data);
+
+       skb->dev = dev;
+       skb->protocol = eth_type_trans(skb, dev);
+       skb->ip_summed = CHECKSUM_COMPLETE;
+
+       /* Maintain stats */
+       dev->stats.rx_packets++;
+       dev->stats.rx_bytes += rsv->len;
+       priv->next_packet = rsv->next_packet;
+
+       netif_rx(skb);
+
+       return 0;
+}
+
+static void encx24j600_rx_packets(struct encx24j600_priv *priv, u8 packet_count)
+{
+       struct net_device *dev = priv->ndev;
+
+       while (packet_count--) {
+               struct rsv rsv;
+               u16 newrxtail;
+
+               encx24j600_write_reg(priv, ERXRDPT, priv->next_packet);
+               encx24j600_raw_read(priv, RRXDATA, (u8 *)&rsv, sizeof(rsv));
+
+               if (netif_msg_rx_status(priv))
+                       encx24j600_dump_rsv(priv, __func__, &rsv);
+
+               if (!RSV_GETBIT(rsv.rxstat, RSV_RXOK) ||
+                   (rsv.len > MAX_FRAMELEN)) {
+                       netif_err(priv, rx_err, dev, "RX Error %04x\n",
+                                 rsv.rxstat);
+                       dev->stats.rx_errors++;
+
+                       if (RSV_GETBIT(rsv.rxstat, RSV_CRCERROR))
+                               dev->stats.rx_crc_errors++;
+                       if (RSV_GETBIT(rsv.rxstat, RSV_LENCHECKERR))
+                               dev->stats.rx_frame_errors++;
+                       if (rsv.len > MAX_FRAMELEN)
+                               dev->stats.rx_over_errors++;
+               } else {
+                       encx24j600_receive_packet(priv, &rsv);
+               }
+
+               newrxtail = priv->next_packet - 2;
+               if (newrxtail == ENC_RX_BUF_START)
+                       newrxtail = SRAM_SIZE - 2;
+
+               encx24j600_cmd(priv, SETPKTDEC);
+               encx24j600_write_reg(priv, ERXTAIL, newrxtail);
+       }
+}
+
+static irqreturn_t encx24j600_isr(int irq, void *dev_id)
+{
+       struct encx24j600_priv *priv = dev_id;
+       struct net_device *dev = priv->ndev;
+       int eir;
+
+       /* Clear interrupts */
+       encx24j600_cmd(priv, CLREIE);
+
+       eir = encx24j600_read_reg(priv, EIR);
+
+       if (eir & LINKIF)
+               encx24j600_int_link_handler(priv);
+
+       if (eir & TXIF)
+               encx24j600_tx_complete(priv, false);
+
+       if (eir & TXABTIF)
+               encx24j600_tx_complete(priv, true);
+
+       if (eir & RXABTIF) {
+               if (eir & PCFULIF) {
+                       /* Packet counter is full */
+                       netif_err(priv, rx_err, dev, "Packet counter full\n");
+               }
+               dev->stats.rx_dropped++;
+               encx24j600_clr_bits(priv, EIR, RXABTIF);
+       }
+
+       if (eir & PKTIF) {
+               u8 packet_count;
+
+               mutex_lock(&priv->lock);
+
+               packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff;
+               while (packet_count) {
+                       encx24j600_rx_packets(priv, packet_count);
+                       packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff;
+               }
+
+               mutex_unlock(&priv->lock);
+       }
+
+       /* Enable interrupts */
+       encx24j600_cmd(priv, SETEIE);
+
+       return IRQ_HANDLED;
+}
+
+static int encx24j600_soft_reset(struct encx24j600_priv *priv)
+{
+       int ret = 0;
+       int timeout;
+       u16 eudast;
+
+       /* Write and verify a test value to EUDAST */
+       regcache_cache_bypass(priv->ctx.regmap, true);
+       timeout = 10;
+       do {
+               encx24j600_write_reg(priv, EUDAST, EUDAST_TEST_VAL);
+               eudast = encx24j600_read_reg(priv, EUDAST);
+               usleep_range(25, 100);
+       } while ((eudast != EUDAST_TEST_VAL) && --timeout);
+       regcache_cache_bypass(priv->ctx.regmap, false);
+
+       if (timeout == 0) {
+               ret = -ETIMEDOUT;
+               goto err_out;
+       }
+
+       /* Wait for CLKRDY to become set */
+       timeout = 10;
+       while (!(encx24j600_read_reg(priv, ESTAT) & CLKRDY) && --timeout)
+               usleep_range(25, 100);
+
+       if (timeout == 0) {
+               ret = -ETIMEDOUT;
+               goto err_out;
+       }
+
+       /* Issue a System Reset command */
+       encx24j600_cmd(priv, SETETHRST);
+       usleep_range(25, 100);
+
+       /* Confirm that EUDAST has 0000h after system reset */
+       if (encx24j600_read_reg(priv, EUDAST) != 0) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       /* Wait for PHY register and status bits to become available */
+       usleep_range(256, 1000);
+
+err_out:
+       return ret;
+}
+
+static int encx24j600_hw_reset(struct encx24j600_priv *priv)
+{
+       int ret;
+
+       mutex_lock(&priv->lock);
+       ret = encx24j600_soft_reset(priv);
+       mutex_unlock(&priv->lock);
+
+       return ret;
+}
+
+static void encx24j600_reset_hw_tx(struct encx24j600_priv *priv)
+{
+       encx24j600_set_bits(priv, ECON2, TXRST);
+       encx24j600_clr_bits(priv, ECON2, TXRST);
+}
+
+static void encx24j600_hw_init_tx(struct encx24j600_priv *priv)
+{
+       /* Reset TX */
+       encx24j600_reset_hw_tx(priv);
+
+       /* Clear the TXIF flag if were previously set */
+       encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF);
+
+       /* Write the Tx Buffer pointer */
+       encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START);
+}
+
+static void encx24j600_hw_init_rx(struct encx24j600_priv *priv)
+{
+       encx24j600_cmd(priv, DISABLERX);
+
+       /* Set up RX packet start address in the SRAM */
+       encx24j600_write_reg(priv, ERXST, ENC_RX_BUF_START);
+
+       /* Preload the RX Data pointer to the beginning of the RX area */
+       encx24j600_write_reg(priv, ERXRDPT, ENC_RX_BUF_START);
+
+       priv->next_packet = ENC_RX_BUF_START;
+
+       /* Set up RX end address in the SRAM */
+       encx24j600_write_reg(priv, ERXTAIL, ENC_SRAM_SIZE - 2);
+
+       /* Reset the  user data pointers    */
+       encx24j600_write_reg(priv, EUDAST, ENC_SRAM_SIZE);
+       encx24j600_write_reg(priv, EUDAND, ENC_SRAM_SIZE + 1);
+
+       /* Set Max Frame length */
+       encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN);
+}
+
+static void encx24j600_dump_config(struct encx24j600_priv *priv,
+                                  const char *msg)
+{
+       pr_info(DRV_NAME ": %s\n", msg);
+
+       /* CHIP configuration */
+       pr_info(DRV_NAME " ECON1:   %04X\n", encx24j600_read_reg(priv, ECON1));
+       pr_info(DRV_NAME " ECON2:   %04X\n", encx24j600_read_reg(priv, ECON2));
+       pr_info(DRV_NAME " ERXFCON: %04X\n", encx24j600_read_reg(priv,
+                                                                ERXFCON));
+       pr_info(DRV_NAME " ESTAT:   %04X\n", encx24j600_read_reg(priv, ESTAT));
+       pr_info(DRV_NAME " EIR:     %04X\n", encx24j600_read_reg(priv, EIR));
+       pr_info(DRV_NAME " EIDLED:  %04X\n", encx24j600_read_reg(priv, EIDLED));
+
+       /* MAC layer configuration */
+       pr_info(DRV_NAME " MACON1:  %04X\n", encx24j600_read_reg(priv, MACON1));
+       pr_info(DRV_NAME " MACON2:  %04X\n", encx24j600_read_reg(priv, MACON2));
+       pr_info(DRV_NAME " MAIPG:   %04X\n", encx24j600_read_reg(priv, MAIPG));
+       pr_info(DRV_NAME " MACLCON: %04X\n", encx24j600_read_reg(priv,
+                                                                MACLCON));
+       pr_info(DRV_NAME " MABBIPG: %04X\n", encx24j600_read_reg(priv,
+                                                                MABBIPG));
+
+       /* PHY configuation */
+       pr_info(DRV_NAME " PHCON1:  %04X\n", encx24j600_read_phy(priv, PHCON1));
+       pr_info(DRV_NAME " PHCON2:  %04X\n", encx24j600_read_phy(priv, PHCON2));
+       pr_info(DRV_NAME " PHANA:   %04X\n", encx24j600_read_phy(priv, PHANA));
+       pr_info(DRV_NAME " PHANLPA: %04X\n", encx24j600_read_phy(priv,
+                                                                PHANLPA));
+       pr_info(DRV_NAME " PHANE:   %04X\n", encx24j600_read_phy(priv, PHANE));
+       pr_info(DRV_NAME " PHSTAT1: %04X\n", encx24j600_read_phy(priv,
+                                                                PHSTAT1));
+       pr_info(DRV_NAME " PHSTAT2: %04X\n", encx24j600_read_phy(priv,
+                                                                PHSTAT2));
+       pr_info(DRV_NAME " PHSTAT3: %04X\n", encx24j600_read_phy(priv,
+                                                                PHSTAT3));
+}
+
+static void encx24j600_set_rxfilter_mode(struct encx24j600_priv *priv)
+{
+       switch (priv->rxfilter) {
+       case RXFILTER_PROMISC:
+               encx24j600_set_bits(priv, MACON1, PASSALL);
+               encx24j600_write_reg(priv, ERXFCON, UCEN | MCEN | NOTMEEN);
+               break;
+       case RXFILTER_MULTI:
+               encx24j600_clr_bits(priv, MACON1, PASSALL);
+               encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN | MCEN);
+               break;
+       case RXFILTER_NORMAL:
+       default:
+               encx24j600_clr_bits(priv, MACON1, PASSALL);
+               encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN);
+               break;
+       }
+}
+
+static int encx24j600_hw_init(struct encx24j600_priv *priv)
+{
+       struct net_device *dev = priv->ndev;
+       int ret = 0;
+       u16 eidled;
+       u16 macon2;
+
+       priv->hw_enabled = false;
+
+       eidled = encx24j600_read_reg(priv, EIDLED);
+       if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       netif_info(priv, drv, dev, "Silicon rev ID: 0x%02x\n",
+                  (eidled & REVID_MASK) >> REVID_SHIFT);
+
+       /* PHY Leds: link status,
+        * LEDA: Link State + collision events
+        * LEDB: Link State + transmit/receive events
+        */
+       encx24j600_update_reg(priv, EIDLED, 0xff00, 0xcb00);
+
+       /* Loopback disabled */
+       encx24j600_write_reg(priv, MACON1, 0x9);
+
+       /* interpacket gap value */
+       encx24j600_write_reg(priv, MAIPG, 0x0c12);
+
+       /* Write the auto negotiation pattern */
+       encx24j600_write_phy(priv, PHANA, PHANA_DEFAULT);
+
+       encx24j600_update_phcon1(priv);
+       encx24j600_check_link_status(priv);
+
+       macon2 = MACON2_RSV1 | TXCRCEN | PADCFG0 | PADCFG2 | MACON2_DEFER;
+       if ((priv->autoneg == AUTONEG_DISABLE) && priv->full_duplex)
+               macon2 |= FULDPX;
+
+       encx24j600_set_bits(priv, MACON2, macon2);
+
+       priv->rxfilter = RXFILTER_NORMAL;
+       encx24j600_set_rxfilter_mode(priv);
+
+       /* Program the Maximum frame length */
+       encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN);
+
+       /* Init Tx pointers */
+       encx24j600_hw_init_tx(priv);
+
+       /* Init Rx pointers */
+       encx24j600_hw_init_rx(priv);
+
+       if (netif_msg_hw(priv))
+               encx24j600_dump_config(priv, "Hw is initialized");
+
+err_out:
+       return ret;
+}
+
+static void encx24j600_hw_enable(struct encx24j600_priv *priv)
+{
+       /* Clear the interrupt flags in case was set */
+       encx24j600_clr_bits(priv, EIR, (PCFULIF | RXABTIF | TXABTIF | TXIF |
+                                       PKTIF | LINKIF));
+
+       /* Enable the interrupts */
+       encx24j600_write_reg(priv, EIE, (PCFULIE | RXABTIE | TXABTIE | TXIE |
+                                        PKTIE | LINKIE | INTIE));
+
+       /* Enable RX */
+       encx24j600_cmd(priv, ENABLERX);
+
+       priv->hw_enabled = true;
+}
+
+static void encx24j600_hw_disable(struct encx24j600_priv *priv)
+{
+       /* Disable all interrupts */
+       encx24j600_write_reg(priv, EIE, 0);
+
+       /* Disable RX */
+       encx24j600_cmd(priv, DISABLERX);
+
+       priv->hw_enabled = false;
+}
+
+static int encx24j600_setlink(struct net_device *dev, u8 autoneg, u16 speed,
+                             u8 duplex)
+{
+       struct encx24j600_priv *priv = netdev_priv(dev);
+       int ret = 0;
+
+       if (!priv->hw_enabled) {
+               /* link is in low power mode now; duplex setting
+                * will take effect on next encx24j600_hw_init()
+                */
+               if (speed == SPEED_10 || speed == SPEED_100) {
+                       priv->autoneg = (autoneg == AUTONEG_ENABLE);
+                       priv->full_duplex = (duplex == DUPLEX_FULL);
+                       priv->speed = (speed == SPEED_100);
+               } else {
+                       netif_warn(priv, link, dev, "unsupported link speed setting\n");
+                       /*speeds other than SPEED_10 and SPEED_100 */
+                       /*are not supported by chip */
+                       ret = -EOPNOTSUPP;
+               }
+       } else {
+               netif_warn(priv, link, dev, "Warning: hw must be disabled to set link mode\n");
+               ret = -EBUSY;
+       }
+       return ret;
+}
+
+static void encx24j600_hw_get_macaddr(struct encx24j600_priv *priv,
+                                     unsigned char *ethaddr)
+{
+       unsigned short val;
+
+       val = encx24j600_read_reg(priv, MAADR1);
+
+       ethaddr[0] = val & 0x00ff;
+       ethaddr[1] = (val & 0xff00) >> 8;
+
+       val = encx24j600_read_reg(priv, MAADR2);
+
+       ethaddr[2] = val & 0x00ffU;
+       ethaddr[3] = (val & 0xff00U) >> 8;
+
+       val = encx24j600_read_reg(priv, MAADR3);
+
+       ethaddr[4] = val & 0x00ffU;
+       ethaddr[5] = (val & 0xff00U) >> 8;
+}
+
+/* Program the hardware MAC address from dev->dev_addr.*/
+static int encx24j600_set_hw_macaddr(struct net_device *dev)
+{
+       struct encx24j600_priv *priv = netdev_priv(dev);
+
+       if (priv->hw_enabled) {
+               netif_info(priv, drv, dev, "Hardware must be disabled to set Mac address\n");
+               return -EBUSY;
+       }
+
+       mutex_lock(&priv->lock);
+
+       netif_info(priv, drv, dev, "%s: Setting MAC address to %pM\n",
+                  dev->name, dev->dev_addr);
+
+       encx24j600_write_reg(priv, MAADR3, (dev->dev_addr[4] |
+                            dev->dev_addr[5] << 8));
+       encx24j600_write_reg(priv, MAADR2, (dev->dev_addr[2] |
+                            dev->dev_addr[3] << 8));
+       encx24j600_write_reg(priv, MAADR1, (dev->dev_addr[0] |
+                            dev->dev_addr[1] << 8));
+
+       mutex_unlock(&priv->lock);
+
+       return 0;
+}
+
+/* Store the new hardware address in dev->dev_addr, and update the MAC.*/
+static int encx24j600_set_mac_address(struct net_device *dev, void *addr)
+{
+       struct sockaddr *address = addr;
+
+       if (netif_running(dev))
+               return -EBUSY;
+       if (!is_valid_ether_addr(address->sa_data))
+               return -EADDRNOTAVAIL;
+
+       memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
+       return encx24j600_set_hw_macaddr(dev);
+}
+
+static int encx24j600_open(struct net_device *dev)
+{
+       struct encx24j600_priv *priv = netdev_priv(dev);
+
+       int ret = request_threaded_irq(priv->ctx.spi->irq, NULL, encx24j600_isr,
+                                      IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                      DRV_NAME, priv);
+       if (unlikely(ret < 0)) {
+               netdev_err(dev, "request irq %d failed (ret = %d)\n",
+                          priv->ctx.spi->irq, ret);
+               return ret;
+       }
+
+       encx24j600_hw_disable(priv);
+       encx24j600_hw_init(priv);
+       encx24j600_hw_enable(priv);
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+static int encx24j600_stop(struct net_device *dev)
+{
+       struct encx24j600_priv *priv = netdev_priv(dev);
+
+       netif_stop_queue(dev);
+       free_irq(priv->ctx.spi->irq, priv);
+       return 0;
+}
+
+static void encx24j600_setrx_proc(struct kthread_work *ws)
+{
+       struct encx24j600_priv *priv =
+                       container_of(ws, struct encx24j600_priv, setrx_work);
+
+       mutex_lock(&priv->lock);
+       encx24j600_set_rxfilter_mode(priv);
+       mutex_unlock(&priv->lock);
+}
+
+static void encx24j600_set_multicast_list(struct net_device *dev)
+{
+       struct encx24j600_priv *priv = netdev_priv(dev);
+       int oldfilter = priv->rxfilter;
+
+       if (dev->flags & IFF_PROMISC) {
+               netif_dbg(priv, link, dev, "promiscuous mode\n");
+               priv->rxfilter = RXFILTER_PROMISC;
+       } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
+               netif_dbg(priv, link, dev, "%smulticast mode\n",
+                         (dev->flags & IFF_ALLMULTI) ? "all-" : "");
+               priv->rxfilter = RXFILTER_MULTI;
+       } else {
+               netif_dbg(priv, link, dev, "normal mode\n");
+               priv->rxfilter = RXFILTER_NORMAL;
+       }
+
+       if (oldfilter != priv->rxfilter)
+               queue_kthread_work(&priv->kworker, &priv->setrx_work);
+}
+
+static void encx24j600_hw_tx(struct encx24j600_priv *priv)
+{
+       struct net_device *dev = priv->ndev;
+       netif_info(priv, tx_queued, dev, "TX Packet Len:%d\n",
+                  priv->tx_skb->len);
+
+       if (netif_msg_pktdata(priv))
+               dump_packet("TX", priv->tx_skb->len, priv->tx_skb->data);
+
+       if (encx24j600_read_reg(priv, EIR) & TXABTIF)
+               /* Last transmition aborted due to error. Reset TX interface */
+               encx24j600_reset_hw_tx(priv);
+
+       /* Clear the TXIF flag if were previously set */
+       encx24j600_clr_bits(priv, EIR, TXIF);
+
+       /* Set the data pointer to the TX buffer address in the SRAM */
+       encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START);
+
+       /* Copy the packet into the SRAM */
+       encx24j600_raw_write(priv, WGPDATA, (u8 *)priv->tx_skb->data,
+                            priv->tx_skb->len);
+
+       /* Program the Tx buffer start pointer */
+       encx24j600_write_reg(priv, ETXST, ENC_TX_BUF_START);
+
+       /* Program the packet length */
+       encx24j600_write_reg(priv, ETXLEN, priv->tx_skb->len);
+
+       /* Start the transmission */
+       encx24j600_cmd(priv, SETTXRTS);
+}
+
+static void encx24j600_tx_proc(struct kthread_work *ws)
+{
+       struct encx24j600_priv *priv =
+                       container_of(ws, struct encx24j600_priv, tx_work);
+
+       mutex_lock(&priv->lock);
+       encx24j600_hw_tx(priv);
+       mutex_unlock(&priv->lock);
+}
+
+static netdev_tx_t encx24j600_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct encx24j600_priv *priv = netdev_priv(dev);
+
+       netif_stop_queue(dev);
+
+       /* save the timestamp */
+       dev->trans_start = jiffies;
+
+       /* Remember the skb for deferred processing */
+       priv->tx_skb = skb;
+
+       queue_kthread_work(&priv->kworker, &priv->tx_work);
+
+       return NETDEV_TX_OK;
+}
+
+/* Deal with a transmit timeout */
+static void encx24j600_tx_timeout(struct net_device *dev)
+{
+       struct encx24j600_priv *priv = netdev_priv(dev);
+
+       netif_err(priv, tx_err, dev, "TX timeout at %ld, latency %ld\n",
+                 jiffies, jiffies - dev->trans_start);
+
+       dev->stats.tx_errors++;
+       netif_wake_queue(dev);
+       return;
+}
+
+static int encx24j600_get_regs_len(struct net_device *dev)
+{
+       return SFR_REG_COUNT;
+}
+
+static void encx24j600_get_regs(struct net_device *dev,
+                               struct ethtool_regs *regs, void *p)
+{
+       struct encx24j600_priv *priv = netdev_priv(dev);
+       u16 *buff = p;
+       u8 reg;
+
+       regs->version = 1;
+       mutex_lock(&priv->lock);
+       for (reg = 0; reg < SFR_REG_COUNT; reg += 2) {
+               unsigned int val = 0;
+               /* ignore errors for unreadable registers */
+               regmap_read(priv->ctx.regmap, reg, &val);
+               buff[reg] = val & 0xffff;
+       }
+       mutex_unlock(&priv->lock);
+}
+
+static void encx24j600_get_drvinfo(struct net_device *dev,
+                                  struct ethtool_drvinfo *info)
+{
+       strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+       strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+       strlcpy(info->bus_info, dev_name(dev->dev.parent),
+               sizeof(info->bus_info));
+}
+
+static int encx24j600_get_settings(struct net_device *dev,
+                                  struct ethtool_cmd *cmd)
+{
+       struct encx24j600_priv *priv = netdev_priv(dev);
+
+       cmd->transceiver = XCVR_INTERNAL;
+       cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+                        SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+                        SUPPORTED_Autoneg | SUPPORTED_TP;
+
+       ethtool_cmd_speed_set(cmd, priv->speed);
+       cmd->duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
+       cmd->port = PORT_TP;
+       cmd->autoneg = priv->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+
+       return 0;
+}
+
+static int encx24j600_set_settings(struct net_device *dev,
+                                  struct ethtool_cmd *cmd)
+{
+       return encx24j600_setlink(dev, cmd->autoneg,
+                                 ethtool_cmd_speed(cmd), cmd->duplex);
+}
+
+static u32 encx24j600_get_msglevel(struct net_device *dev)
+{
+       struct encx24j600_priv *priv = netdev_priv(dev);
+       return priv->msg_enable;
+}
+
+static void encx24j600_set_msglevel(struct net_device *dev, u32 val)
+{
+       struct encx24j600_priv *priv = netdev_priv(dev);
+       priv->msg_enable = val;
+}
+
+static const struct ethtool_ops encx24j600_ethtool_ops = {
+       .get_settings = encx24j600_get_settings,
+       .set_settings = encx24j600_set_settings,
+       .get_drvinfo = encx24j600_get_drvinfo,
+       .get_msglevel = encx24j600_get_msglevel,
+       .set_msglevel = encx24j600_set_msglevel,
+       .get_regs_len = encx24j600_get_regs_len,
+       .get_regs = encx24j600_get_regs,
+};
+
+static const struct net_device_ops encx24j600_netdev_ops = {
+       .ndo_open = encx24j600_open,
+       .ndo_stop = encx24j600_stop,
+       .ndo_start_xmit = encx24j600_tx,
+       .ndo_set_rx_mode = encx24j600_set_multicast_list,
+       .ndo_set_mac_address = encx24j600_set_mac_address,
+       .ndo_tx_timeout = encx24j600_tx_timeout,
+       .ndo_validate_addr = eth_validate_addr,
+};
+
+static int encx24j600_spi_probe(struct spi_device *spi)
+{
+       int ret;
+
+       struct net_device *ndev;
+       struct encx24j600_priv *priv;
+
+       ndev = alloc_etherdev(sizeof(struct encx24j600_priv));
+
+       if (!ndev) {
+               ret = -ENOMEM;
+               goto error_out;
+       }
+
+       priv = netdev_priv(ndev);
+       spi_set_drvdata(spi, priv);
+       dev_set_drvdata(&spi->dev, priv);
+       SET_NETDEV_DEV(ndev, &spi->dev);
+
+       priv->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
+       priv->ndev = ndev;
+
+       /* Default configuration PHY configuration */
+       priv->full_duplex = true;
+       priv->autoneg = AUTONEG_ENABLE;
+       priv->speed = SPEED_100;
+
+       priv->ctx.spi = spi;
+       devm_regmap_init_encx24j600(&spi->dev, &priv->ctx);
+       ndev->irq = spi->irq;
+       ndev->netdev_ops = &encx24j600_netdev_ops;
+
+       mutex_init(&priv->lock);
+
+       /* Reset device and check if it is connected */
+       if (encx24j600_hw_reset(priv)) {
+               netif_err(priv, probe, ndev,
+                         DRV_NAME ": Chip is not detected\n");
+               ret = -EIO;
+               goto out_free;
+       }
+
+       /* Initialize the device HW to the consistent state */
+       if (encx24j600_hw_init(priv)) {
+               netif_err(priv, probe, ndev,
+                         DRV_NAME ": HW initialization error\n");
+               ret = -EIO;
+               goto out_free;
+       }
+
+       init_kthread_worker(&priv->kworker);
+       init_kthread_work(&priv->tx_work, encx24j600_tx_proc);
+       init_kthread_work(&priv->setrx_work, encx24j600_setrx_proc);
+
+       priv->kworker_task = kthread_run(kthread_worker_fn, &priv->kworker,
+                                        "encx24j600");
+
+       if (IS_ERR(priv->kworker_task)) {
+               ret = PTR_ERR(priv->kworker_task);
+               goto out_free;
+       }
+
+       /* Get the MAC address from the chip */
+       encx24j600_hw_get_macaddr(priv, ndev->dev_addr);
+
+       ndev->ethtool_ops = &encx24j600_ethtool_ops;
+
+       ret = register_netdev(ndev);
+       if (unlikely(ret)) {
+               netif_err(priv, probe, ndev, "Error %d initializing card encx24j600 card\n",
+                         ret);
+               goto out_free;
+       }
+
+       netif_info(priv, drv, priv->ndev, "MAC address %pM\n", ndev->dev_addr);
+
+       return ret;
+
+out_free:
+       free_netdev(ndev);
+
+error_out:
+       return ret;
+}
+
+static int encx24j600_spi_remove(struct spi_device *spi)
+{
+       struct encx24j600_priv *priv = dev_get_drvdata(&spi->dev);
+
+       unregister_netdev(priv->ndev);
+
+       free_netdev(priv->ndev);
+
+       return 0;
+}
+
+static const struct spi_device_id encx24j600_spi_id_table[] = {
+       { .name = "encx24j600" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, encx24j600_spi_id_table);
+
+static struct spi_driver encx24j600_spi_net_driver = {
+       .driver = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+               .bus    = &spi_bus_type,
+       },
+       .probe          = encx24j600_spi_probe,
+       .remove         = encx24j600_spi_remove,
+       .id_table       = encx24j600_spi_id_table,
+};
+
+static int __init encx24j600_init(void)
+{
+       return spi_register_driver(&encx24j600_spi_net_driver);
+}
+module_init(encx24j600_init);
+
+static void encx24j600_exit(void)
+{
+       spi_unregister_driver(&encx24j600_spi_net_driver);
+}
+module_exit(encx24j600_exit);
+
+MODULE_DESCRIPTION(DRV_NAME " ethernet driver");
+MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:" DRV_NAME);
diff --git a/drivers/net/ethernet/microchip/encx24j600_hw.h b/drivers/net/ethernet/microchip/encx24j600_hw.h
new file mode 100644 (file)
index 0000000..4be73d5
--- /dev/null
@@ -0,0 +1,437 @@
+/**
+ * encx24j600_hw.h: Register definitions
+ *
+ */
+
+#ifndef _ENCX24J600_HW_H
+#define _ENCX24J600_HW_H
+
+struct encx24j600_context {
+       struct spi_device *spi;
+       struct regmap *regmap;
+       struct regmap *phymap;
+       struct mutex mutex; /* mutex to protect access to regmap */
+       int bank;
+};
+
+void devm_regmap_init_encx24j600(struct device *dev,
+                                struct encx24j600_context *ctx);
+
+/* Single-byte instructions */
+#define BANK_SELECT(bank) (0xC0 | ((bank & (BANK_MASK >> BANK_SHIFT)) << 1))
+#define B0SEL 0xC0             /* Bank 0 Select */
+#define B1SEL 0xC2             /* Bank 1 Select */
+#define B2SEL 0xC4             /* Bank 2 Select */
+#define B3SEL 0xC6             /* Bank 3 Select */
+#define SETETHRST 0xCA         /* System Reset */
+#define FCDISABLE 0xE0         /* Flow Control Disable */
+#define FCSINGLE 0xE2          /* Flow Control Single */
+#define FCMULTIPLE 0xE4                /* Flow Control Multiple */
+#define FCCLEAR 0xE6           /* Flow Control Clear */
+#define SETPKTDEC 0xCC         /* Decrement Packet Counter */
+#define DMASTOP 0xD2           /* DMA Stop */
+#define DMACKSUM 0xD8          /* DMA Start Checksum */
+#define DMACKSUMS 0xDA         /* DMA Start Checksum with Seed */
+#define DMACOPY 0xDC           /* DMA Start Copy */
+#define DMACOPYS 0xDE          /* DMA Start Copy and Checksum with Seed */
+#define SETTXRTS 0xD4          /* Request Packet Transmission */
+#define ENABLERX 0xE8          /* Enable RX */
+#define DISABLERX 0xEA         /* Disable RX */
+#define SETEIE 0xEC            /* Enable Interrupts */
+#define CLREIE 0xEE            /* Disable Interrupts */
+
+/* Two byte instructions */
+#define RBSEL 0xC8             /* Read Bank Select */
+
+/* Three byte instructions */
+#define WGPRDPT 0x60           /* Write EGPRDPT */
+#define RGPRDPT 0x62           /* Read EGPRDPT */
+#define WRXRDPT 0x64           /* Write ERXRDPT */
+#define RRXRDPT 0x66           /* Read ERXRDPT */
+#define WUDARDPT 0x68          /* Write EUDARDPT */
+#define RUDARDPT 0x6A          /* Read EUDARDPT */
+#define WGPWRPT 0x6C           /* Write EGPWRPT */
+#define RGPWRPT 0x6E           /* Read EGPWRPT */
+#define WRXWRPT 0x70           /* Write ERXWRPT */
+#define RRXWRPT 0x72           /* Read ERXWRPT */
+#define WUDAWRPT 0x74          /* Write EUDAWRPT */
+#define RUDAWRPT 0x76          /* Read EUDAWRPT */
+
+/* n byte instructions */
+#define RCRCODE 0x00
+#define WCRCODE 0x40
+#define BFSCODE 0x80
+#define BFCCODE 0xA0
+#define RCR(addr) (RCRCODE | (addr & ADDR_MASK)) /* Read Control Register */
+#define WCR(addr) (WCRCODE | (addr & ADDR_MASK)) /* Write Control Register */
+#define RCRU 0x20              /* Read Control Register Unbanked */
+#define WCRU 0x22              /* Write Control Register Unbanked */
+#define BFS(addr) (BFSCODE | (addr & ADDR_MASK)) /* Bit Field Set */
+#define BFC(addr) (BFCCODE | (addr & ADDR_MASK)) /* Bit Field Clear */
+#define BFSU 0x24              /* Bit Field Set Unbanked */
+#define BFCU 0x26              /* Bit Field Clear Unbanked */
+#define RGPDATA 0x28           /* Read EGPDATA */
+#define WGPDATA 0x2A           /* Write EGPDATA */
+#define RRXDATA 0x2C           /* Read ERXDATA */
+#define WRXDATA 0x2E           /* Write ERXDATA */
+#define RUDADATA 0x30          /* Read EUDADATA */
+#define WUDADATA 0x32          /* Write EUDADATA */
+
+#define SFR_REG_COUNT  0xA0
+
+/* ENC424J600 Control Registers
+ * Control register definitions are a combination of address
+ * and bank number
+ * - Register address (bits 0-4)
+ * - Bank number (bits 5-6)
+ */
+#define ADDR_MASK 0x1F
+#define BANK_MASK 0x60
+#define BANK_SHIFT 5
+
+/* All-bank registers */
+#define EUDAST 0x16
+#define EUDAND 0x18
+#define ESTAT 0x1A
+#define EIR 0x1C
+#define ECON1 0x1E
+
+/* Bank 0 registers */
+#define ETXST (0x00 | 0x00)
+#define ETXLEN (0x02 | 0x00)
+#define ERXST (0x04 | 0x00)
+#define ERXTAIL (0x06 | 0x00)
+#define ERXHEAD (0x08 | 0x00)
+#define EDMAST (0x0A | 0x00)
+#define EDMALEN (0x0C | 0x00)
+#define EDMADST (0x0E | 0x00)
+#define EDMACS (0x10 | 0x00)
+#define ETXSTAT (0x12 | 0x00)
+#define ETXWIRE (0x14 | 0x00)
+
+/* Bank 1 registers */
+#define EHT1 (0x00 | 0x20)
+#define EHT2 (0x02 | 0x20)
+#define EHT3 (0x04 | 0x20)
+#define EHT4 (0x06 | 0x20)
+#define EPMM1 (0x08 | 0x20)
+#define EPMM2 (0x0A | 0x20)
+#define EPMM3 (0x0C | 0x20)
+#define EPMM4 (0x0E | 0x20)
+#define EPMCS (0x10 | 0x20)
+#define EPMO (0x12 | 0x20)
+#define ERXFCON (0x14 | 0x20)
+
+/* Bank 2 registers */
+#define MACON1 (0x00 | 0x40)
+#define MACON2 (0x02 | 0x40)
+#define MABBIPG (0x04 | 0x40)
+#define MAIPG (0x06 | 0x40)
+#define MACLCON (0x08 | 0x40)
+#define MAMXFL (0x0A | 0x40)
+#define MICMD (0x12 | 0x40)
+#define MIREGADR (0x14 | 0x40)
+
+/* Bank 3 registers */
+#define MAADR3 (0x00 | 0x60)
+#define MAADR2 (0x02 | 0x60)
+#define MAADR1 (0x04 | 0x60)
+#define MIWR (0x06 | 0x60)
+#define MIRD (0x08 | 0x60)
+#define MISTAT (0x0A | 0x60)
+#define EPAUS (0x0C | 0x60)
+#define ECON2 (0x0E | 0x60)
+#define ERXWM (0x10 | 0x60)
+#define EIE (0x12 | 0x60)
+#define EIDLED (0x14 | 0x60)
+
+/* Unbanked registers */
+#define EGPDATA (0x00 | 0x80)
+#define ERXDATA (0x02 | 0x80)
+#define EUDADATA (0x04 | 0x80)
+#define EGPRDPT (0x06 | 0x80)
+#define EGPWRPT (0x08 | 0x80)
+#define ERXRDPT (0x0A | 0x80)
+#define ERXWRPT (0x0C | 0x80)
+#define EUDARDPT (0x0E | 0x80)
+#define EUDAWRPT (0x10 | 0x80)
+
+
+/* Register bit definitions */
+/* ESTAT */
+#define INT (1 << 15)
+#define FCIDLE (1 << 14)
+#define RXBUSY (1 << 13)
+#define CLKRDY (1 << 12)
+#define PHYDPX (1 << 10)
+#define PHYLNK (1 << 8)
+
+/* EIR */
+#define CRYPTEN (1 << 15)
+#define MODEXIF (1 << 14)
+#define HASHIF (1 << 13)
+#define AESIF (1 << 12)
+#define LINKIF (1 << 11)
+#define PKTIF (1 << 6)
+#define DMAIF (1 << 5)
+#define TXIF (1 << 3)
+#define TXABTIF (1 << 2)
+#define RXABTIF (1 << 1)
+#define PCFULIF (1 << 0)
+
+/* ECON1 */
+#define MODEXST (1 << 15)
+#define HASHEN (1 << 14)
+#define HASHOP (1 << 13)
+#define HASHLST (1 << 12)
+#define AESST (1 << 11)
+#define AESOP1 (1 << 10)
+#define AESOP0 (1 << 9)
+#define PKTDEC (1 << 8)
+#define FCOP1 (1 << 7)
+#define FCOP0 (1 << 6)
+#define DMAST (1 << 5)
+#define DMACPY (1 << 4)
+#define DMACSSD (1 << 3)
+#define DMANOCS (1 << 2)
+#define TXRTS (1 << 1)
+#define RXEN (1 << 0)
+
+/* ETXSTAT */
+#define LATECOL (1 << 10)
+#define MAXCOL (1 << 9)
+#define EXDEFER (1 << 8)
+#define ETXSTATL_DEFER (1 << 7)
+#define CRCBAD (1 << 4)
+#define COLCNT_MASK 0xF
+
+/* ERXFCON */
+#define HTEN (1 << 15)
+#define MPEN (1 << 14)
+#define NOTPM (1 << 12)
+#define PMEN3 (1 << 11)
+#define PMEN2 (1 << 10)
+#define PMEN1 (1 << 9)
+#define PMEN0 (1 << 8)
+#define CRCEEN (1 << 7)
+#define CRCEN (1 << 6)
+#define RUNTEEN (1 << 5)
+#define RUNTEN (1 << 4)
+#define UCEN (1 << 3)
+#define NOTMEEN (1 << 2)
+#define MCEN (1 << 1)
+#define BCEN (1 << 0)
+
+/* MACON1 */
+#define LOOPBK (1 << 4)
+#define RXPAUS (1 << 2)
+#define PASSALL (1 << 1)
+
+/* MACON2 */
+#define MACON2_DEFER (1 << 14)
+#define BPEN (1 << 13)
+#define NOBKOFF (1 << 12)
+#define PADCFG2 (1 << 7)
+#define PADCFG1 (1 << 6)
+#define PADCFG0 (1 << 5)
+#define TXCRCEN (1 << 4)
+#define PHDREN (1 << 3)
+#define HFRMEN (1 << 2)
+#define MACON2_RSV1 (1 << 1)
+#define FULDPX (1 << 0)
+
+/* MAIPG */
+/* value of the high byte is given by the reserved bits,
+ * value of the low byte is recomended setting of the
+ * IPG parameter.
+ */
+#define MAIPGH_VAL 0x0C
+#define MAIPGL_VAL 0x12
+
+/* MIREGADRH */
+#define MIREGADR_VAL (1 << 8)
+
+/* MIREGADRL */
+#define PHREG_MASK 0x1F
+
+/* MICMD */
+#define MIISCAN (1 << 1)
+#define MIIRD (1 << 0)
+
+/* MISTAT */
+#define NVALID (1 << 2)
+#define SCAN (1 << 1)
+#define BUSY (1 << 0)
+
+/* ECON2 */
+#define ETHEN (1 << 15)
+#define STRCH (1 << 14)
+#define TXMAC (1 << 13)
+#define SHA1MD5 (1 << 12)
+#define COCON3 (1 << 11)
+#define COCON2 (1 << 10)
+#define COCON1 (1 << 9)
+#define COCON0 (1 << 8)
+#define AUTOFC (1 << 7)
+#define TXRST (1 << 6)
+#define RXRST (1 << 5)
+#define ETHRST (1 << 4)
+#define MODLEN1 (1 << 3)
+#define MODLEN0 (1 << 2)
+#define AESLEN1 (1 << 1)
+#define AESLEN0 (1 << 0)
+
+/* EIE */
+#define INTIE (1 << 15)
+#define MODEXIE (1 << 14)
+#define HASHIE (1 << 13)
+#define AESIE (1 << 12)
+#define LINKIE (1 << 11)
+#define PKTIE (1 << 6)
+#define DMAIE (1 << 5)
+#define TXIE (1 << 3)
+#define TXABTIE (1 << 2)
+#define RXABTIE (1 << 1)
+#define PCFULIE (1 << 0)
+
+/* EIDLED */
+#define LACFG3 (1 << 15)
+#define LACFG2 (1 << 14)
+#define LACFG1 (1 << 13)
+#define LACFG0 (1 << 12)
+#define LBCFG3 (1 << 11)
+#define LBCFG2 (1 << 10)
+#define LBCFG1 (1 << 9)
+#define LBCFG0 (1 << 8)
+#define DEVID_SHIFT 5
+#define DEVID_MASK (0x7 << DEVID_SHIFT)
+#define REVID_SHIFT 0
+#define REVID_MASK (0x1F << REVID_SHIFT)
+
+/* PHY registers */
+#define PHCON1 0x00
+#define PHSTAT1 0x01
+#define PHANA 0x04
+#define PHANLPA 0x05
+#define PHANE 0x06
+#define PHCON2 0x11
+#define PHSTAT2 0x1B
+#define PHSTAT3 0x1F
+
+/* PHCON1 */
+#define PRST (1 << 15)
+#define PLOOPBK (1 << 14)
+#define SPD100 (1 << 13)
+#define ANEN (1 << 12)
+#define PSLEEP (1 << 11)
+#define RENEG (1 << 9)
+#define PFULDPX (1 << 8)
+
+/* PHSTAT1 */
+#define FULL100 (1 << 14)
+#define HALF100 (1 << 13)
+#define FULL10 (1 << 12)
+#define HALF10 (1 << 11)
+#define ANDONE (1 << 5)
+#define LRFAULT (1 << 4)
+#define ANABLE (1 << 3)
+#define LLSTAT (1 << 2)
+#define EXTREGS (1 << 0)
+
+/* PHSTAT2 */
+#define PLRITY (1 << 4)
+
+/* PHSTAT3 */
+#define PHY3SPD100 (1 << 3)
+#define PHY3DPX (1 << 4)
+#define SPDDPX_SHIFT 2
+#define SPDDPX_MASK (0x7 << SPDDPX_SHIFT)
+
+/* PHANA */
+/* Default value for PHY initialization*/
+#define PHANA_DEFAULT 0x05E1
+
+/* PHANE */
+#define PDFLT (1 << 4)
+#define LPARCD (1 << 1)
+#define LPANABL (1 << 0)
+
+#define EUDAST_TEST_VAL 0x1234
+
+#define TSV_SIZE 7
+
+#define ENCX24J600_DEV_ID 0x1
+
+/* Configuration */
+
+/* Led is on when the link is present and driven low
+ * temporarily when packet is TX'd or RX'd
+ */
+#define LED_A_SETTINGS 0xC
+
+/* Led is on if the link is in 100 Mbps mode */
+#define LED_B_SETTINGS 0x8
+
+/* maximum ethernet frame length
+ * Currently not used as a limit anywhere
+ * (we're using the "huge frame enable" feature of
+ * enc424j600).
+ */
+#define MAX_FRAMELEN 1518
+
+/* Size in bytes of the receive buffer in enc424j600.
+ * Must be word aligned (even).
+ */
+#define RX_BUFFER_SIZE (15 * MAX_FRAMELEN)
+
+/* Start of the general purpose area in sram */
+#define SRAM_GP_START 0x0
+
+/* SRAM size */
+#define SRAM_SIZE 0x6000
+
+/* Start of the receive buffer */
+#define ERXST_VAL (SRAM_SIZE - RX_BUFFER_SIZE)
+
+#define RSV_RXLONGEVDROPEV     16
+#define RSV_CARRIEREV          18
+#define RSV_CRCERROR           20
+#define RSV_LENCHECKERR                21
+#define RSV_LENOUTOFRANGE      22
+#define RSV_RXOK               23
+#define RSV_RXMULTICAST                24
+#define RSV_RXBROADCAST                25
+#define RSV_DRIBBLENIBBLE      26
+#define RSV_RXCONTROLFRAME     27
+#define RSV_RXPAUSEFRAME       28
+#define RSV_RXUNKNOWNOPCODE    29
+#define RSV_RXTYPEVLAN         30
+
+#define RSV_RUNTFILTERMATCH    31
+#define RSV_NOTMEFILTERMATCH   32
+#define RSV_HASHFILTERMATCH    33
+#define RSV_MAGICPKTFILTERMATCH        34
+#define RSV_PTRNMTCHFILTERMATCH        35
+#define RSV_UNICASTFILTERMATCH 36
+
+#define RSV_SIZE               8
+#define RSV_BITMASK(x)         (1 << ((x) - 16))
+#define RSV_GETBIT(x, y)       (((x) & RSV_BITMASK(y)) ? 1 : 0)
+
+struct rsv {
+       u16 next_packet;
+       u16 len;
+       u32 rxstat;
+};
+
+/* Put RX buffer at 0 as suggested by the Errata datasheet */
+
+#define RXSTART_INIT           ERXST_VAL
+#define RXEND_INIT             0x5FFF
+
+int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data,
+                               size_t count);
+int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count);
+
+
+#endif
index 2d1b9427407982b43673e96a085c7a9ff69e0a53..9ba975853ec6c712f0463d24a48a837d9c7b0152 100644 (file)
@@ -5389,8 +5389,6 @@ static void s2io_ethtool_gdrvinfo(struct net_device *dev,
        strlcpy(info->driver, s2io_driver_name, sizeof(info->driver));
        strlcpy(info->version, s2io_driver_version, sizeof(info->version));
        strlcpy(info->bus_info, pci_name(sp->pdev), sizeof(info->bus_info));
-       info->regdump_len = XENA_REG_SPACE;
-       info->eedump_len = XENA_EEPROM_SPACE;
 }
 
 /**
index be916eb2f2e7304dbbfa35d8df22b618298ca3bc..9a2967016c18aa15f307e46c1542c7c1804e3217 100644 (file)
@@ -105,10 +105,6 @@ static void vxge_ethtool_gdrvinfo(struct net_device *dev,
        strlcpy(info->version, DRV_VERSION, sizeof(info->version));
        strlcpy(info->fw_version, vdev->fw_version, sizeof(info->fw_version));
        strlcpy(info->bus_info, pci_name(vdev->pdev), sizeof(info->bus_info));
-       info->regdump_len = sizeof(struct vxge_hw_vpath_reg)
-                               * vdev->no_of_vpath;
-
-       info->n_stats = STAT_LEN;
 }
 
 /**
index a41bb5e6b954f0e6b40375b76a52ed8a254cdb18..75e88f4c15315c84c20106022f38edc1dccf75e7 100644 (file)
@@ -4076,6 +4076,8 @@ static void nv_do_nic_poll(unsigned long data)
        struct fe_priv *np = netdev_priv(dev);
        u8 __iomem *base = get_hwbase(dev);
        u32 mask = 0;
+       unsigned long flags;
+       unsigned int irq = 0;
 
        /*
         * First disable irq(s) and then
@@ -4085,25 +4087,27 @@ static void nv_do_nic_poll(unsigned long data)
 
        if (!using_multi_irqs(dev)) {
                if (np->msi_flags & NV_MSI_X_ENABLED)
-                       disable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector);
+                       irq = np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector;
                else
-                       disable_irq_lockdep(np->pci_dev->irq);
+                       irq = np->pci_dev->irq;
                mask = np->irqmask;
        } else {
                if (np->nic_poll_irq & NVREG_IRQ_RX_ALL) {
-                       disable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
+                       irq = np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector;
                        mask |= NVREG_IRQ_RX_ALL;
                }
                if (np->nic_poll_irq & NVREG_IRQ_TX_ALL) {
-                       disable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector);
+                       irq = np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector;
                        mask |= NVREG_IRQ_TX_ALL;
                }
                if (np->nic_poll_irq & NVREG_IRQ_OTHER) {
-                       disable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector);
+                       irq = np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector;
                        mask |= NVREG_IRQ_OTHER;
                }
        }
-       /* disable_irq() contains synchronize_irq, thus no irq handler can run now */
+
+       disable_irq_nosync_lockdep_irqsave(irq, &flags);
+       synchronize_irq(irq);
 
        if (np->recover_error) {
                np->recover_error = 0;
@@ -4156,28 +4160,22 @@ static void nv_do_nic_poll(unsigned long data)
                        nv_nic_irq_optimized(0, dev);
                else
                        nv_nic_irq(0, dev);
-               if (np->msi_flags & NV_MSI_X_ENABLED)
-                       enable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector);
-               else
-                       enable_irq_lockdep(np->pci_dev->irq);
        } else {
                if (np->nic_poll_irq & NVREG_IRQ_RX_ALL) {
                        np->nic_poll_irq &= ~NVREG_IRQ_RX_ALL;
                        nv_nic_irq_rx(0, dev);
-                       enable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
                }
                if (np->nic_poll_irq & NVREG_IRQ_TX_ALL) {
                        np->nic_poll_irq &= ~NVREG_IRQ_TX_ALL;
                        nv_nic_irq_tx(0, dev);
-                       enable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector);
                }
                if (np->nic_poll_irq & NVREG_IRQ_OTHER) {
                        np->nic_poll_irq &= ~NVREG_IRQ_OTHER;
                        nv_nic_irq_other(0, dev);
-                       enable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector);
                }
        }
 
+       enable_irq_lockdep_irqrestore(irq, &flags);
 }
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
index 7bf9c028d8d7fea824859142d81307d87e056fdd..c177c7cec13b462b80a2001a9d8a272b606710b1 100644 (file)
@@ -1344,10 +1344,6 @@ static void octeon_mgmt_get_drvinfo(struct net_device *netdev,
        strlcpy(info->version, DRV_VERSION, sizeof(info->version));
        strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
        strlcpy(info->bus_info, "N/A", sizeof(info->bus_info));
-       info->n_stats = 0;
-       info->testinfo_len = 0;
-       info->regdump_len = 0;
-       info->eedump_len = 0;
 }
 
 static int octeon_mgmt_get_settings(struct net_device *netdev,
index f6fcf7450352631ad34f7c052fbe5ac960238297..b19be7c6c1f41efe0d7210493ec572d2812066e0 100644 (file)
@@ -164,7 +164,6 @@ static void pch_gbe_get_drvinfo(struct net_device *netdev,
        strlcpy(drvinfo->version, pch_driver_version, sizeof(drvinfo->version));
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->regdump_len = pch_gbe_get_regs_len(netdev);
 }
 
 /**
index f1f0108c275d7017fff5a5ad39eefeb9beddeb70..30a6f246dfc9f004ac68f4b2cc84658186dce9e6 100644 (file)
@@ -91,4 +91,15 @@ config NETXEN_NIC
        ---help---
          This enables the support for NetXen's Gigabit Ethernet card.
 
+config QED
+       tristate "QLogic QED 25/40/100Gb core driver"
+       depends on PCI
+       ---help---
+         This enables the support for ...
+
+config QEDE
+       tristate "QLogic QED 25/40/100Gb Ethernet NIC"
+       depends on QED
+       ---help---
+         This enables the support for ...
 endif # NET_VENDOR_QLOGIC
index b2a283d9ae60b0a767aaa0353394a494fe40d9d3..cee90e05beb8fec1ee856787833802cb996a9916 100644 (file)
@@ -6,3 +6,5 @@ obj-$(CONFIG_QLA3XXX) += qla3xxx.o
 obj-$(CONFIG_QLCNIC) += qlcnic/
 obj-$(CONFIG_QLGE) += qlge/
 obj-$(CONFIG_NETXEN_NIC) += netxen/
+obj-$(CONFIG_QED) += qed/
+obj-$(CONFIG_QEDE)+= qede/
index 87e073c6e291609793106bfc5646c4695b9698d2..f9034467736c3bb578b97661eebdee37d71054b7 100644 (file)
@@ -93,8 +93,6 @@ netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo)
 
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->regdump_len = NETXEN_NIC_REGS_LEN;
-       drvinfo->eedump_len = netxen_nic_get_eeprom_len(dev);
 }
 
 static int
diff --git a/drivers/net/ethernet/qlogic/qed/Makefile b/drivers/net/ethernet/qlogic/qed/Makefile
new file mode 100644 (file)
index 0000000..5c2fd57
--- /dev/null
@@ -0,0 +1,4 @@
+obj-$(CONFIG_QED) := qed.o
+
+qed-y := qed_cxt.o qed_dev.o qed_hw.o qed_init_fw_funcs.o qed_init_ops.o \
+        qed_int.o qed_main.o qed_mcp.o qed_sp_commands.o qed_spq.o qed_l2.o
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
new file mode 100644 (file)
index 0000000..ac17d86
--- /dev/null
@@ -0,0 +1,496 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_H
+#define _QED_H
+
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+#include <linux/zlib.h>
+#include <linux/hashtable.h>
+#include <linux/qed/qed_if.h>
+#include "qed_hsi.h"
+
+extern const struct qed_common_ops qed_common_ops_pass;
+#define DRV_MODULE_VERSION "8.4.0.0"
+
+#define MAX_HWFNS_PER_DEVICE    (4)
+#define NAME_SIZE 16
+#define VER_SIZE 16
+
+/* cau states */
+enum qed_coalescing_mode {
+       QED_COAL_MODE_DISABLE,
+       QED_COAL_MODE_ENABLE
+};
+
+struct qed_eth_cb_ops;
+struct qed_dev_info;
+
+/* helpers */
+static inline u32 qed_db_addr(u32 cid, u32 DEMS)
+{
+       u32 db_addr = FIELD_VALUE(DB_LEGACY_ADDR_DEMS, DEMS) |
+                     FIELD_VALUE(DB_LEGACY_ADDR_ICID, cid);
+
+       return db_addr;
+}
+
+#define ALIGNED_TYPE_SIZE(type_name, p_hwfn)                                \
+       ((sizeof(type_name) + (u32)(1 << (p_hwfn->cdev->cache_shift)) - 1) & \
+        ~((1 << (p_hwfn->cdev->cache_shift)) - 1))
+
+#define for_each_hwfn(cdev, i)  for (i = 0; i < cdev->num_hwfns; i++)
+
+#define D_TRINE(val, cond1, cond2, true1, true2, def) \
+       (val == (cond1) ? true1 :                     \
+        (val == (cond2) ? true2 : def))
+
+/* forward */
+struct qed_ptt_pool;
+struct qed_spq;
+struct qed_sb_info;
+struct qed_sb_attn_info;
+struct qed_cxt_mngr;
+struct qed_sb_sp_info;
+struct qed_mcp_info;
+
+struct qed_rt_data {
+       u32 init_val;
+       bool b_valid;
+};
+
+/* The PCI personality is not quite synonymous to protocol ID:
+ * 1. All personalities need CORE connections
+ * 2. The Ethernet personality may support also the RoCE protocol
+ */
+enum qed_pci_personality {
+       QED_PCI_ETH,
+       QED_PCI_DEFAULT /* default in shmem */
+};
+
+/* All VFs are symmetric, all counters are PF + all VFs */
+struct qed_qm_iids {
+       u32 cids;
+       u32 vf_cids;
+       u32 tids;
+};
+
+enum QED_RESOURCES {
+       QED_SB,
+       QED_L2_QUEUE,
+       QED_VPORT,
+       QED_RSS_ENG,
+       QED_PQ,
+       QED_RL,
+       QED_MAC,
+       QED_VLAN,
+       QED_ILT,
+       QED_MAX_RESC,
+};
+
+enum QED_FEATURE {
+       QED_PF_L2_QUE,
+       QED_MAX_FEATURES,
+};
+
+enum QED_PORT_MODE {
+       QED_PORT_MODE_DE_2X40G,
+       QED_PORT_MODE_DE_2X50G,
+       QED_PORT_MODE_DE_1X100G,
+       QED_PORT_MODE_DE_4X10G_F,
+       QED_PORT_MODE_DE_4X10G_E,
+       QED_PORT_MODE_DE_4X20G,
+       QED_PORT_MODE_DE_1X40G,
+       QED_PORT_MODE_DE_2X25G,
+       QED_PORT_MODE_DE_1X25G
+};
+
+struct qed_hw_info {
+       /* PCI personality */
+       enum qed_pci_personality        personality;
+
+       /* Resource Allocation scheme results */
+       u32                             resc_start[QED_MAX_RESC];
+       u32                             resc_num[QED_MAX_RESC];
+       u32                             feat_num[QED_MAX_FEATURES];
+
+#define RESC_START(_p_hwfn, resc) ((_p_hwfn)->hw_info.resc_start[resc])
+#define RESC_NUM(_p_hwfn, resc) ((_p_hwfn)->hw_info.resc_num[resc])
+#define FEAT_NUM(_p_hwfn, resc) ((_p_hwfn)->hw_info.feat_num[resc])
+
+       u8                              num_tc;
+       u8                              offload_tc;
+       u8                              non_offload_tc;
+
+       u32                             concrete_fid;
+       u16                             opaque_fid;
+       u16                             ovlan;
+       u32                             part_num[4];
+
+       u32                             vendor_id;
+       u32                             device_id;
+
+       unsigned char                   hw_mac_addr[ETH_ALEN];
+
+       struct qed_igu_info             *p_igu_info;
+
+       u32                             port_mode;
+       u32                             hw_mode;
+};
+
+struct qed_hw_cid_data {
+       u32     cid;
+       bool    b_cid_allocated;
+
+       /* Additional identifiers */
+       u16     opaque_fid;
+       u8      vport_id;
+};
+
+/* maximun size of read/write commands (HW limit) */
+#define DMAE_MAX_RW_SIZE        0x2000
+
+struct qed_dmae_info {
+       /* Mutex for synchronizing access to functions */
+       struct mutex    mutex;
+
+       u8              channel;
+
+       dma_addr_t      completion_word_phys_addr;
+
+       /* The memory location where the DMAE writes the completion
+        * value when an operation is finished on this context.
+        */
+       u32             *p_completion_word;
+
+       dma_addr_t      intermediate_buffer_phys_addr;
+
+       /* An intermediate buffer for DMAE operations that use virtual
+        * addresses - data is DMA'd to/from this buffer and then
+        * memcpy'd to/from the virtual address
+        */
+       u32             *p_intermediate_buffer;
+
+       dma_addr_t      dmae_cmd_phys_addr;
+       struct dmae_cmd *p_dmae_cmd;
+};
+
+struct qed_qm_info {
+       struct init_qm_pq_params        *qm_pq_params;
+       struct init_qm_vport_params     *qm_vport_params;
+       struct init_qm_port_params      *qm_port_params;
+       u16                             start_pq;
+       u8                              start_vport;
+       u8                              pure_lb_pq;
+       u8                              offload_pq;
+       u8                              pure_ack_pq;
+       u8                              vf_queues_offset;
+       u16                             num_pqs;
+       u16                             num_vf_pqs;
+       u8                              num_vports;
+       u8                              max_phys_tcs_per_port;
+       bool                            pf_rl_en;
+       bool                            pf_wfq_en;
+       bool                            vport_rl_en;
+       bool                            vport_wfq_en;
+       u8                              pf_wfq;
+       u32                             pf_rl;
+};
+
+struct storm_stats {
+       u32     address;
+       u32     len;
+};
+
+struct qed_storm_stats {
+       struct storm_stats mstats;
+       struct storm_stats pstats;
+       struct storm_stats tstats;
+       struct storm_stats ustats;
+};
+
+struct qed_fw_data {
+       struct fw_ver_info      *fw_ver_info;
+       const u8                *modes_tree_buf;
+       union init_op           *init_ops;
+       const u32               *arr_data;
+       u32                     init_ops_size;
+};
+
+struct qed_simd_fp_handler {
+       void    *token;
+       void    (*func)(void *);
+};
+
+struct qed_hwfn {
+       struct qed_dev                  *cdev;
+       u8                              my_id;          /* ID inside the PF */
+#define IS_LEAD_HWFN(edev)              (!((edev)->my_id))
+       u8                              rel_pf_id;      /* Relative to engine*/
+       u8                              abs_pf_id;
+#define QED_PATH_ID(_p_hwfn)           ((_p_hwfn)->abs_pf_id & 1)
+       u8                              port_id;
+       bool                            b_active;
+
+       u32                             dp_module;
+       u8                              dp_level;
+       char                            name[NAME_SIZE];
+
+       bool                            first_on_engine;
+       bool                            hw_init_done;
+
+       /* BAR access */
+       void __iomem                    *regview;
+       void __iomem                    *doorbells;
+       u64                             db_phys_addr;
+       unsigned long                   db_size;
+
+       /* PTT pool */
+       struct qed_ptt_pool             *p_ptt_pool;
+
+       /* HW info */
+       struct qed_hw_info              hw_info;
+
+       /* rt_array (for init-tool) */
+       struct qed_rt_data              *rt_data;
+
+       /* SPQ */
+       struct qed_spq                  *p_spq;
+
+       /* EQ */
+       struct qed_eq                   *p_eq;
+
+       /* Consolidate Q*/
+       struct qed_consq                *p_consq;
+
+       /* Slow-Path definitions */
+       struct tasklet_struct           *sp_dpc;
+       bool                            b_sp_dpc_enabled;
+
+       struct qed_ptt                  *p_main_ptt;
+       struct qed_ptt                  *p_dpc_ptt;
+
+       struct qed_sb_sp_info           *p_sp_sb;
+       struct qed_sb_attn_info         *p_sb_attn;
+
+       /* Protocol related */
+       struct qed_pf_params            pf_params;
+
+       /* Array of sb_info of all status blocks */
+       struct qed_sb_info              *sbs_info[MAX_SB_PER_PF_MIMD];
+       u16                             num_sbs;
+
+       struct qed_cxt_mngr             *p_cxt_mngr;
+
+       /* Flag indicating whether interrupts are enabled or not*/
+       bool                            b_int_enabled;
+
+       struct qed_mcp_info             *mcp_info;
+
+       struct qed_hw_cid_data          *p_tx_cids;
+       struct qed_hw_cid_data          *p_rx_cids;
+
+       struct qed_dmae_info            dmae_info;
+
+       /* QM init */
+       struct qed_qm_info              qm_info;
+       struct qed_storm_stats          storm_stats;
+
+       /* Buffer for unzipping firmware data */
+       void                            *unzip_buf;
+
+       struct qed_simd_fp_handler      simd_proto_handler[64];
+
+       struct z_stream_s               *stream;
+};
+
+struct pci_params {
+       int             pm_cap;
+
+       unsigned long   mem_start;
+       unsigned long   mem_end;
+       unsigned int    irq;
+       u8              pf_num;
+};
+
+struct qed_int_param {
+       u32     int_mode;
+       u8      num_vectors;
+       u8      min_msix_cnt; /* for minimal functionality */
+};
+
+struct qed_int_params {
+       struct qed_int_param    in;
+       struct qed_int_param    out;
+       struct msix_entry       *msix_table;
+       bool                    fp_initialized;
+       u8                      fp_msix_base;
+       u8                      fp_msix_cnt;
+};
+
+struct qed_dev {
+       u32     dp_module;
+       u8      dp_level;
+       char    name[NAME_SIZE];
+
+       u8      type;
+#define QED_DEV_TYPE_BB_A0      (0 << 0)
+#define QED_DEV_TYPE_MASK       (0x3)
+#define QED_DEV_TYPE_SHIFT      (0)
+
+       u16     chip_num;
+#define CHIP_NUM_MASK                   0xffff
+#define CHIP_NUM_SHIFT                  16
+
+       u16     chip_rev;
+#define CHIP_REV_MASK                   0xf
+#define CHIP_REV_SHIFT                  12
+
+       u16                             chip_metal;
+#define CHIP_METAL_MASK                 0xff
+#define CHIP_METAL_SHIFT                4
+
+       u16                             chip_bond_id;
+#define CHIP_BOND_ID_MASK               0xf
+#define CHIP_BOND_ID_SHIFT              0
+
+       u8                              num_engines;
+       u8                              num_ports_in_engines;
+       u8                              num_funcs_in_port;
+
+       u8                              path_id;
+       enum mf_mode                    mf_mode;
+#define IS_MF(_p_hwfn)          (((_p_hwfn)->cdev)->mf_mode != SF)
+#define IS_MF_SI(_p_hwfn)       (((_p_hwfn)->cdev)->mf_mode == MF_NPAR)
+#define IS_MF_SD(_p_hwfn)       (((_p_hwfn)->cdev)->mf_mode == MF_OVLAN)
+
+       int                             pcie_width;
+       int                             pcie_speed;
+       u8                              ver_str[VER_SIZE];
+
+       /* Add MF related configuration */
+       u8                              mcp_rev;
+       u8                              boot_mode;
+
+       u8                              wol;
+
+       u32                             int_mode;
+       enum qed_coalescing_mode        int_coalescing_mode;
+       u8                              rx_coalesce_usecs;
+       u8                              tx_coalesce_usecs;
+
+       /* Start Bar offset of first hwfn */
+       void __iomem                    *regview;
+       void __iomem                    *doorbells;
+       u64                             db_phys_addr;
+       unsigned long                   db_size;
+
+       /* PCI */
+       u8                              cache_shift;
+
+       /* Init */
+       const struct iro                *iro_arr;
+#define IRO (p_hwfn->cdev->iro_arr)
+
+       /* HW functions */
+       u8                              num_hwfns;
+       struct qed_hwfn                 hwfns[MAX_HWFNS_PER_DEVICE];
+
+       u32                             drv_type;
+
+       struct qed_eth_stats            *reset_stats;
+       struct qed_fw_data              *fw_data;
+
+       u32                             mcp_nvm_resp;
+
+       /* Linux specific here */
+       struct  qede_dev                *edev;
+       struct  pci_dev                 *pdev;
+       int                             msg_enable;
+
+       struct pci_params               pci_params;
+
+       struct qed_int_params           int_params;
+
+       u8                              protocol;
+#define IS_QED_ETH_IF(cdev)     ((cdev)->protocol == QED_PROTOCOL_ETH)
+
+       /* Callbacks to protocol driver */
+       union {
+               struct qed_common_cb_ops        *common;
+               struct qed_eth_cb_ops           *eth;
+       } protocol_ops;
+       void                            *ops_cookie;
+
+       const struct firmware           *firmware;
+};
+
+#define QED_GET_TYPE(dev)       (((dev)->type & QED_DEV_TYPE_MASK) >> \
+                                QED_DEV_TYPE_SHIFT)
+#define QED_IS_BB_A0(dev)       (QED_GET_TYPE(dev) == QED_DEV_TYPE_BB_A0)
+#define QED_IS_BB(dev)  (QED_IS_BB_A0(dev))
+
+#define NUM_OF_SBS(dev)         MAX_SB_PER_PATH_BB
+#define NUM_OF_ENG_PFS(dev)     MAX_NUM_PFS_BB
+
+/**
+ * @brief qed_concrete_to_sw_fid - get the sw function id from
+ *        the concrete value.
+ *
+ * @param concrete_fid
+ *
+ * @return inline u8
+ */
+static inline u8 qed_concrete_to_sw_fid(struct qed_dev *cdev,
+                                       u32 concrete_fid)
+{
+       u8 pfid = GET_FIELD(concrete_fid, PXP_CONCRETE_FID_PFID);
+
+       return pfid;
+}
+
+#define PURE_LB_TC 8
+
+#define QED_LEADING_HWFN(dev)   (&dev->hwfns[0])
+
+/* Other Linux specific common definitions */
+#define DP_NAME(cdev) ((cdev)->name)
+
+#define REG_ADDR(cdev, offset)          (void __iomem *)((u8 __iomem *)\
+                                               (cdev->regview) + \
+                                                        (offset))
+
+#define REG_RD(cdev, offset)            readl(REG_ADDR(cdev, offset))
+#define REG_WR(cdev, offset, val)       writel((u32)val, REG_ADDR(cdev, offset))
+#define REG_WR16(cdev, offset, val)     writew((u16)val, REG_ADDR(cdev, offset))
+
+#define DOORBELL(cdev, db_addr, val)                    \
+       writel((u32)val, (void __iomem *)((u8 __iomem *)\
+                                         (cdev->doorbells) + (db_addr)))
+
+/* Prototypes */
+int qed_fill_dev_info(struct qed_dev *cdev,
+                     struct qed_dev_info *dev_info);
+void qed_link_update(struct qed_hwfn *hwfn);
+u32 qed_unzip_data(struct qed_hwfn *p_hwfn,
+                  u32 input_len, u8 *input_buf,
+                  u32 max_size, u8 *unzip_buf);
+
+#define QED_ETH_INTERFACE_VERSION       300
+
+#endif /* _QED_H */
diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c
new file mode 100644 (file)
index 0000000..7ccdb46
--- /dev/null
@@ -0,0 +1,847 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/log2.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include "qed.h"
+#include "qed_cxt.h"
+#include "qed_dev_api.h"
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_init_ops.h"
+#include "qed_reg_addr.h"
+
+/* Max number of connection types in HW (DQ/CDU etc.) */
+#define MAX_CONN_TYPES         PROTOCOLID_COMMON
+#define NUM_TASK_TYPES         2
+#define NUM_TASK_PF_SEGMENTS   4
+
+/* QM constants */
+#define QM_PQ_ELEMENT_SIZE     4 /* in bytes */
+
+/* Doorbell-Queue constants */
+#define DQ_RANGE_SHIFT         4
+#define DQ_RANGE_ALIGN         BIT(DQ_RANGE_SHIFT)
+
+/* ILT constants */
+#define ILT_DEFAULT_HW_P_SIZE          3
+#define ILT_PAGE_IN_BYTES(hw_p_size)   (1U << ((hw_p_size) + 12))
+#define ILT_CFG_REG(cli, reg)  PSWRQ2_REG_ ## cli ## _ ## reg ## _RT_OFFSET
+
+/* ILT entry structure */
+#define ILT_ENTRY_PHY_ADDR_MASK                0x000FFFFFFFFFFFULL
+#define ILT_ENTRY_PHY_ADDR_SHIFT       0
+#define ILT_ENTRY_VALID_MASK           0x1ULL
+#define ILT_ENTRY_VALID_SHIFT          52
+#define ILT_ENTRY_IN_REGS              2
+#define ILT_REG_SIZE_IN_BYTES          4
+
+/* connection context union */
+union conn_context {
+       struct core_conn_context core_ctx;
+       struct eth_conn_context eth_ctx;
+};
+
+#define CONN_CXT_SIZE(p_hwfn) \
+       ALIGNED_TYPE_SIZE(union conn_context, p_hwfn)
+
+/* PF per protocl configuration object */
+struct qed_conn_type_cfg {
+       u32 cid_count;
+       u32 cid_start;
+};
+
+/* ILT Client configuration, Per connection type (protocol) resources. */
+#define ILT_CLI_PF_BLOCKS      (1 + NUM_TASK_PF_SEGMENTS * 2)
+#define CDUC_BLK               (0)
+
+enum ilt_clients {
+       ILT_CLI_CDUC,
+       ILT_CLI_QM,
+       ILT_CLI_MAX
+};
+
+struct ilt_cfg_pair {
+       u32 reg;
+       u32 val;
+};
+
+struct qed_ilt_cli_blk {
+       u32 total_size; /* 0 means not active */
+       u32 real_size_in_page;
+       u32 start_line;
+};
+
+struct qed_ilt_client_cfg {
+       bool active;
+
+       /* ILT boundaries */
+       struct ilt_cfg_pair first;
+       struct ilt_cfg_pair last;
+       struct ilt_cfg_pair p_size;
+
+       /* ILT client blocks for PF */
+       struct qed_ilt_cli_blk pf_blks[ILT_CLI_PF_BLOCKS];
+       u32 pf_total_lines;
+};
+
+/* Per Path -
+ *      ILT shadow table
+ *      Protocol acquired CID lists
+ *      PF start line in ILT
+ */
+struct qed_dma_mem {
+       dma_addr_t p_phys;
+       void *p_virt;
+       size_t size;
+};
+
+struct qed_cid_acquired_map {
+       u32             start_cid;
+       u32             max_count;
+       unsigned long   *cid_map;
+};
+
+struct qed_cxt_mngr {
+       /* Per protocl configuration */
+       struct qed_conn_type_cfg        conn_cfg[MAX_CONN_TYPES];
+
+       /* computed ILT structure */
+       struct qed_ilt_client_cfg       clients[ILT_CLI_MAX];
+
+       /* Acquired CIDs */
+       struct qed_cid_acquired_map     acquired[MAX_CONN_TYPES];
+
+       /* ILT  shadow table */
+       struct qed_dma_mem              *ilt_shadow;
+       u32                             pf_start_line;
+};
+
+static u32 qed_cxt_cdu_iids(struct qed_cxt_mngr *p_mngr)
+{
+       u32 type, pf_cids = 0;
+
+       for (type = 0; type < MAX_CONN_TYPES; type++)
+               pf_cids += p_mngr->conn_cfg[type].cid_count;
+
+       return pf_cids;
+}
+
+static void qed_cxt_qm_iids(struct qed_hwfn *p_hwfn,
+                           struct qed_qm_iids *iids)
+{
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       int type;
+
+       for (type = 0; type < MAX_CONN_TYPES; type++)
+               iids->cids += p_mngr->conn_cfg[type].cid_count;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_ILT, "iids: CIDS %08x\n", iids->cids);
+}
+
+/* set the iids count per protocol */
+static void qed_cxt_set_proto_cid_count(struct qed_hwfn *p_hwfn,
+                                       enum protocol_type type,
+                                       u32 cid_count)
+{
+       struct qed_cxt_mngr *p_mgr = p_hwfn->p_cxt_mngr;
+       struct qed_conn_type_cfg *p_conn = &p_mgr->conn_cfg[type];
+
+       p_conn->cid_count = roundup(cid_count, DQ_RANGE_ALIGN);
+}
+
+static void qed_ilt_cli_blk_fill(struct qed_ilt_client_cfg *p_cli,
+                                struct qed_ilt_cli_blk *p_blk,
+                                u32 start_line, u32 total_size,
+                                u32 elem_size)
+{
+       u32 ilt_size = ILT_PAGE_IN_BYTES(p_cli->p_size.val);
+
+       /* verify thatits called only once for each block */
+       if (p_blk->total_size)
+               return;
+
+       p_blk->total_size = total_size;
+       p_blk->real_size_in_page = 0;
+       if (elem_size)
+               p_blk->real_size_in_page = (ilt_size / elem_size) * elem_size;
+       p_blk->start_line = start_line;
+}
+
+static void qed_ilt_cli_adv_line(struct qed_hwfn *p_hwfn,
+                                struct qed_ilt_client_cfg *p_cli,
+                                struct qed_ilt_cli_blk *p_blk,
+                                u32 *p_line, enum ilt_clients client_id)
+{
+       if (!p_blk->total_size)
+               return;
+
+       if (!p_cli->active)
+               p_cli->first.val = *p_line;
+
+       p_cli->active = true;
+       *p_line += DIV_ROUND_UP(p_blk->total_size,
+                               p_blk->real_size_in_page);
+       p_cli->last.val = *p_line - 1;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_ILT,
+                  "ILT[Client %d] - Lines: [%08x - %08x]. Block - Size %08x [Real %08x] Start line %d\n",
+                  client_id, p_cli->first.val,
+                  p_cli->last.val, p_blk->total_size,
+                  p_blk->real_size_in_page, p_blk->start_line);
+}
+
+int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn)
+{
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       struct qed_ilt_client_cfg *p_cli;
+       struct qed_ilt_cli_blk *p_blk;
+       u32 curr_line, total, pf_cids;
+       struct qed_qm_iids qm_iids;
+
+       memset(&qm_iids, 0, sizeof(qm_iids));
+
+       p_mngr->pf_start_line = RESC_START(p_hwfn, QED_ILT);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_ILT,
+                  "hwfn [%d] - Set context manager starting line to be 0x%08x\n",
+                  p_hwfn->my_id, p_hwfn->p_cxt_mngr->pf_start_line);
+
+       /* CDUC */
+       p_cli = &p_mngr->clients[ILT_CLI_CDUC];
+       curr_line = p_mngr->pf_start_line;
+       p_cli->pf_total_lines = 0;
+
+       /* get the counters for the CDUC and QM clients  */
+       pf_cids = qed_cxt_cdu_iids(p_mngr);
+
+       p_blk = &p_cli->pf_blks[CDUC_BLK];
+
+       total = pf_cids * CONN_CXT_SIZE(p_hwfn);
+
+       qed_ilt_cli_blk_fill(p_cli, p_blk, curr_line,
+                            total, CONN_CXT_SIZE(p_hwfn));
+
+       qed_ilt_cli_adv_line(p_hwfn, p_cli, p_blk, &curr_line, ILT_CLI_CDUC);
+       p_cli->pf_total_lines = curr_line - p_blk->start_line;
+
+       /* QM */
+       p_cli = &p_mngr->clients[ILT_CLI_QM];
+       p_blk = &p_cli->pf_blks[0];
+
+       qed_cxt_qm_iids(p_hwfn, &qm_iids);
+       total = qed_qm_pf_mem_size(p_hwfn->rel_pf_id, qm_iids.cids, 0, 0,
+                                  p_hwfn->qm_info.num_pqs, 0);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_ILT,
+                  "QM ILT Info, (cids=%d, num_pqs=%d, memory_size=%d)\n",
+                  qm_iids.cids, p_hwfn->qm_info.num_pqs, total);
+
+       qed_ilt_cli_blk_fill(p_cli, p_blk,
+                            curr_line, total * 0x1000,
+                            QM_PQ_ELEMENT_SIZE);
+
+       qed_ilt_cli_adv_line(p_hwfn, p_cli, p_blk, &curr_line, ILT_CLI_QM);
+       p_cli->pf_total_lines = curr_line - p_blk->start_line;
+
+       if (curr_line - p_hwfn->p_cxt_mngr->pf_start_line >
+           RESC_NUM(p_hwfn, QED_ILT)) {
+               DP_ERR(p_hwfn, "too many ilt lines...#lines=%d\n",
+                      curr_line - p_hwfn->p_cxt_mngr->pf_start_line);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+#define for_each_ilt_valid_client(pos, clients)        \
+               for (pos = 0; pos < ILT_CLI_MAX; pos++)
+
+/* Total number of ILT lines used by this PF */
+static u32 qed_cxt_ilt_shadow_size(struct qed_ilt_client_cfg *ilt_clients)
+{
+       u32 size = 0;
+       u32 i;
+
+       for_each_ilt_valid_client(i, ilt_clients) {
+               if (!ilt_clients[i].active)
+                       continue;
+               size += (ilt_clients[i].last.val -
+                        ilt_clients[i].first.val + 1);
+       }
+
+       return size;
+}
+
+static void qed_ilt_shadow_free(struct qed_hwfn *p_hwfn)
+{
+       struct qed_ilt_client_cfg *p_cli = p_hwfn->p_cxt_mngr->clients;
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       u32 ilt_size, i;
+
+       ilt_size = qed_cxt_ilt_shadow_size(p_cli);
+
+       for (i = 0; p_mngr->ilt_shadow && i < ilt_size; i++) {
+               struct qed_dma_mem *p_dma = &p_mngr->ilt_shadow[i];
+
+               if (p_dma->p_virt)
+                       dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                                         p_dma->size, p_dma->p_virt,
+                                         p_dma->p_phys);
+               p_dma->p_virt = NULL;
+       }
+       kfree(p_mngr->ilt_shadow);
+}
+
+static int qed_ilt_blk_alloc(struct qed_hwfn *p_hwfn,
+                            struct qed_ilt_cli_blk *p_blk,
+                            enum ilt_clients ilt_client,
+                            u32 start_line_offset)
+{
+       struct qed_dma_mem *ilt_shadow = p_hwfn->p_cxt_mngr->ilt_shadow;
+       u32 lines, line, sz_left;
+
+       if (!p_blk->total_size)
+               return 0;
+
+       sz_left = p_blk->total_size;
+       lines = DIV_ROUND_UP(sz_left, p_blk->real_size_in_page);
+       line = p_blk->start_line + start_line_offset -
+              p_hwfn->p_cxt_mngr->pf_start_line;
+
+       for (; lines; lines--) {
+               dma_addr_t p_phys;
+               void *p_virt;
+               u32 size;
+
+               size = min_t(u32, sz_left,
+                            p_blk->real_size_in_page);
+               p_virt = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                           size,
+                                           &p_phys,
+                                           GFP_KERNEL);
+               if (!p_virt)
+                       return -ENOMEM;
+               memset(p_virt, 0, size);
+
+               ilt_shadow[line].p_phys = p_phys;
+               ilt_shadow[line].p_virt = p_virt;
+               ilt_shadow[line].size = size;
+
+               DP_VERBOSE(p_hwfn, QED_MSG_ILT,
+                          "ILT shadow: Line [%d] Physical 0x%llx Virtual %p Size %d\n",
+                           line, (u64)p_phys, p_virt, size);
+
+               sz_left -= size;
+               line++;
+       }
+
+       return 0;
+}
+
+static int qed_ilt_shadow_alloc(struct qed_hwfn *p_hwfn)
+{
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       struct qed_ilt_client_cfg *clients = p_mngr->clients;
+       struct qed_ilt_cli_blk *p_blk;
+       u32 size, i, j;
+       int rc;
+
+       size = qed_cxt_ilt_shadow_size(clients);
+       p_mngr->ilt_shadow = kcalloc(size, sizeof(struct qed_dma_mem),
+                                    GFP_KERNEL);
+       if (!p_mngr->ilt_shadow) {
+               DP_NOTICE(p_hwfn, "Failed to allocate ilt shadow table\n");
+               rc = -ENOMEM;
+               goto ilt_shadow_fail;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_ILT,
+                  "Allocated 0x%x bytes for ilt shadow\n",
+                  (u32)(size * sizeof(struct qed_dma_mem)));
+
+       for_each_ilt_valid_client(i, clients) {
+               if (!clients[i].active)
+                       continue;
+               for (j = 0; j < ILT_CLI_PF_BLOCKS; j++) {
+                       p_blk = &clients[i].pf_blks[j];
+                       rc = qed_ilt_blk_alloc(p_hwfn, p_blk, i, 0);
+                       if (rc != 0)
+                               goto ilt_shadow_fail;
+               }
+       }
+
+       return 0;
+
+ilt_shadow_fail:
+       qed_ilt_shadow_free(p_hwfn);
+       return rc;
+}
+
+static void qed_cid_map_free(struct qed_hwfn *p_hwfn)
+{
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       u32 type;
+
+       for (type = 0; type < MAX_CONN_TYPES; type++) {
+               kfree(p_mngr->acquired[type].cid_map);
+               p_mngr->acquired[type].max_count = 0;
+               p_mngr->acquired[type].start_cid = 0;
+       }
+}
+
+static int qed_cid_map_alloc(struct qed_hwfn *p_hwfn)
+{
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       u32 start_cid = 0;
+       u32 type;
+
+       for (type = 0; type < MAX_CONN_TYPES; type++) {
+               u32 cid_cnt = p_hwfn->p_cxt_mngr->conn_cfg[type].cid_count;
+               u32 size;
+
+               if (cid_cnt == 0)
+                       continue;
+
+               size = DIV_ROUND_UP(cid_cnt,
+                                   sizeof(unsigned long) * BITS_PER_BYTE) *
+                      sizeof(unsigned long);
+               p_mngr->acquired[type].cid_map = kzalloc(size, GFP_KERNEL);
+               if (!p_mngr->acquired[type].cid_map)
+                       goto cid_map_fail;
+
+               p_mngr->acquired[type].max_count = cid_cnt;
+               p_mngr->acquired[type].start_cid = start_cid;
+
+               p_hwfn->p_cxt_mngr->conn_cfg[type].cid_start = start_cid;
+
+               DP_VERBOSE(p_hwfn, QED_MSG_CXT,
+                          "Type %08x start: %08x count %08x\n",
+                          type, p_mngr->acquired[type].start_cid,
+                          p_mngr->acquired[type].max_count);
+               start_cid += cid_cnt;
+       }
+
+       return 0;
+
+cid_map_fail:
+       qed_cid_map_free(p_hwfn);
+       return -ENOMEM;
+}
+
+int qed_cxt_mngr_alloc(struct qed_hwfn *p_hwfn)
+{
+       struct qed_cxt_mngr *p_mngr;
+       u32 i;
+
+       p_mngr = kzalloc(sizeof(*p_mngr), GFP_ATOMIC);
+       if (!p_mngr) {
+               DP_NOTICE(p_hwfn, "Failed to allocate `struct qed_cxt_mngr'\n");
+               return -ENOMEM;
+       }
+
+       /* Initialize ILT client registers */
+       p_mngr->clients[ILT_CLI_CDUC].first.reg = ILT_CFG_REG(CDUC, FIRST_ILT);
+       p_mngr->clients[ILT_CLI_CDUC].last.reg = ILT_CFG_REG(CDUC, LAST_ILT);
+       p_mngr->clients[ILT_CLI_CDUC].p_size.reg = ILT_CFG_REG(CDUC, P_SIZE);
+
+       p_mngr->clients[ILT_CLI_QM].first.reg = ILT_CFG_REG(QM, FIRST_ILT);
+       p_mngr->clients[ILT_CLI_QM].last.reg = ILT_CFG_REG(QM, LAST_ILT);
+       p_mngr->clients[ILT_CLI_QM].p_size.reg = ILT_CFG_REG(QM, P_SIZE);
+
+       /* default ILT page size for all clients is 32K */
+       for (i = 0; i < ILT_CLI_MAX; i++)
+               p_mngr->clients[i].p_size.val = ILT_DEFAULT_HW_P_SIZE;
+
+       /* Set the cxt mangr pointer priori to further allocations */
+       p_hwfn->p_cxt_mngr = p_mngr;
+
+       return 0;
+}
+
+int qed_cxt_tables_alloc(struct qed_hwfn *p_hwfn)
+{
+       int rc;
+
+       /* Allocate the ILT shadow table */
+       rc = qed_ilt_shadow_alloc(p_hwfn);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "Failed to allocate ilt memory\n");
+               goto tables_alloc_fail;
+       }
+
+       /* Allocate and initialize the acquired cids bitmaps */
+       rc = qed_cid_map_alloc(p_hwfn);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "Failed to allocate cid maps\n");
+               goto tables_alloc_fail;
+       }
+
+       return 0;
+
+tables_alloc_fail:
+       qed_cxt_mngr_free(p_hwfn);
+       return rc;
+}
+
+void qed_cxt_mngr_free(struct qed_hwfn *p_hwfn)
+{
+       if (!p_hwfn->p_cxt_mngr)
+               return;
+
+       qed_cid_map_free(p_hwfn);
+       qed_ilt_shadow_free(p_hwfn);
+       kfree(p_hwfn->p_cxt_mngr);
+
+       p_hwfn->p_cxt_mngr = NULL;
+}
+
+void qed_cxt_mngr_setup(struct qed_hwfn *p_hwfn)
+{
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       int type;
+
+       /* Reset acquired cids */
+       for (type = 0; type < MAX_CONN_TYPES; type++) {
+               u32 cid_cnt = p_hwfn->p_cxt_mngr->conn_cfg[type].cid_count;
+
+               if (cid_cnt == 0)
+                       continue;
+
+               memset(p_mngr->acquired[type].cid_map, 0,
+                      DIV_ROUND_UP(cid_cnt,
+                                   sizeof(unsigned long) * BITS_PER_BYTE) *
+                      sizeof(unsigned long));
+       }
+}
+
+/* CDU Common */
+#define CDUC_CXT_SIZE_SHIFT \
+       CDU_REG_CID_ADDR_PARAMS_CONTEXT_SIZE_SHIFT
+
+#define CDUC_CXT_SIZE_MASK \
+       (CDU_REG_CID_ADDR_PARAMS_CONTEXT_SIZE >> CDUC_CXT_SIZE_SHIFT)
+
+#define CDUC_BLOCK_WASTE_SHIFT \
+       CDU_REG_CID_ADDR_PARAMS_BLOCK_WASTE_SHIFT
+
+#define CDUC_BLOCK_WASTE_MASK \
+       (CDU_REG_CID_ADDR_PARAMS_BLOCK_WASTE >> CDUC_BLOCK_WASTE_SHIFT)
+
+#define CDUC_NCIB_SHIFT        \
+       CDU_REG_CID_ADDR_PARAMS_NCIB_SHIFT
+
+#define CDUC_NCIB_MASK \
+       (CDU_REG_CID_ADDR_PARAMS_NCIB >> CDUC_NCIB_SHIFT)
+
+static void qed_cdu_init_common(struct qed_hwfn *p_hwfn)
+{
+       u32 page_sz, elems_per_page, block_waste, cxt_size, cdu_params = 0;
+
+       /* CDUC - connection configuration */
+       page_sz = p_hwfn->p_cxt_mngr->clients[ILT_CLI_CDUC].p_size.val;
+       cxt_size = CONN_CXT_SIZE(p_hwfn);
+       elems_per_page = ILT_PAGE_IN_BYTES(page_sz) / cxt_size;
+       block_waste = ILT_PAGE_IN_BYTES(page_sz) - elems_per_page * cxt_size;
+
+       SET_FIELD(cdu_params, CDUC_CXT_SIZE, cxt_size);
+       SET_FIELD(cdu_params, CDUC_BLOCK_WASTE, block_waste);
+       SET_FIELD(cdu_params, CDUC_NCIB, elems_per_page);
+       STORE_RT_REG(p_hwfn, CDU_REG_CID_ADDR_PARAMS_RT_OFFSET, cdu_params);
+}
+
+void qed_qm_init_pf(struct qed_hwfn *p_hwfn)
+{
+       struct qed_qm_pf_rt_init_params params;
+       struct qed_qm_info *qm_info = &p_hwfn->qm_info;
+       struct qed_qm_iids iids;
+
+       memset(&iids, 0, sizeof(iids));
+       qed_cxt_qm_iids(p_hwfn, &iids);
+
+       memset(&params, 0, sizeof(params));
+       params.port_id = p_hwfn->port_id;
+       params.pf_id = p_hwfn->rel_pf_id;
+       params.max_phys_tcs_per_port = qm_info->max_phys_tcs_per_port;
+       params.is_first_pf = p_hwfn->first_on_engine;
+       params.num_pf_cids = iids.cids;
+       params.start_pq = qm_info->start_pq;
+       params.num_pf_pqs = qm_info->num_pqs;
+       params.start_vport = qm_info->num_vports;
+       params.pf_wfq = qm_info->pf_wfq;
+       params.pf_rl = qm_info->pf_rl;
+       params.pq_params = qm_info->qm_pq_params;
+       params.vport_params = qm_info->qm_vport_params;
+
+       qed_qm_pf_rt_init(p_hwfn, p_hwfn->p_main_ptt, &params);
+}
+
+/* CM PF */
+static int qed_cm_init_pf(struct qed_hwfn *p_hwfn)
+{
+       union qed_qm_pq_params pq_params;
+       u16 pq;
+
+       /* XCM pure-LB queue */
+       memset(&pq_params, 0, sizeof(pq_params));
+       pq_params.core.tc = LB_TC;
+       pq = qed_get_qm_pq(p_hwfn, PROTOCOLID_CORE, &pq_params);
+       STORE_RT_REG(p_hwfn, XCM_REG_CON_PHY_Q3_RT_OFFSET, pq);
+
+       return 0;
+}
+
+/* DQ PF */
+static void qed_dq_init_pf(struct qed_hwfn *p_hwfn)
+{
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       u32 dq_pf_max_cid = 0;
+
+       dq_pf_max_cid += (p_mngr->conn_cfg[0].cid_count >> DQ_RANGE_SHIFT);
+       STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_0_RT_OFFSET, dq_pf_max_cid);
+
+       dq_pf_max_cid += (p_mngr->conn_cfg[1].cid_count >> DQ_RANGE_SHIFT);
+       STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_1_RT_OFFSET, dq_pf_max_cid);
+
+       dq_pf_max_cid += (p_mngr->conn_cfg[2].cid_count >> DQ_RANGE_SHIFT);
+       STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_2_RT_OFFSET, dq_pf_max_cid);
+
+       dq_pf_max_cid += (p_mngr->conn_cfg[3].cid_count >> DQ_RANGE_SHIFT);
+       STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_3_RT_OFFSET, dq_pf_max_cid);
+
+       dq_pf_max_cid += (p_mngr->conn_cfg[4].cid_count >> DQ_RANGE_SHIFT);
+       STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_4_RT_OFFSET, dq_pf_max_cid);
+
+       /* 5 - PF */
+       dq_pf_max_cid += (p_mngr->conn_cfg[5].cid_count >> DQ_RANGE_SHIFT);
+       STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_5_RT_OFFSET, dq_pf_max_cid);
+}
+
+static void qed_ilt_bounds_init(struct qed_hwfn *p_hwfn)
+{
+       struct qed_ilt_client_cfg *ilt_clients;
+       int i;
+
+       ilt_clients = p_hwfn->p_cxt_mngr->clients;
+       for_each_ilt_valid_client(i, ilt_clients) {
+               if (!ilt_clients[i].active)
+                       continue;
+               STORE_RT_REG(p_hwfn,
+                            ilt_clients[i].first.reg,
+                            ilt_clients[i].first.val);
+               STORE_RT_REG(p_hwfn,
+                            ilt_clients[i].last.reg,
+                            ilt_clients[i].last.val);
+               STORE_RT_REG(p_hwfn,
+                            ilt_clients[i].p_size.reg,
+                            ilt_clients[i].p_size.val);
+       }
+}
+
+/* ILT (PSWRQ2) PF */
+static void qed_ilt_init_pf(struct qed_hwfn *p_hwfn)
+{
+       struct qed_ilt_client_cfg *clients;
+       struct qed_cxt_mngr *p_mngr;
+       struct qed_dma_mem *p_shdw;
+       u32 line, rt_offst, i;
+
+       qed_ilt_bounds_init(p_hwfn);
+
+       p_mngr = p_hwfn->p_cxt_mngr;
+       p_shdw = p_mngr->ilt_shadow;
+       clients = p_hwfn->p_cxt_mngr->clients;
+
+       for_each_ilt_valid_client(i, clients) {
+               if (!clients[i].active)
+                       continue;
+
+               /** Client's 1st val and RT array are absolute, ILT shadows'
+                *  lines are relative.
+                */
+               line = clients[i].first.val - p_mngr->pf_start_line;
+               rt_offst = PSWRQ2_REG_ILT_MEMORY_RT_OFFSET +
+                          clients[i].first.val * ILT_ENTRY_IN_REGS;
+
+               for (; line <= clients[i].last.val - p_mngr->pf_start_line;
+                    line++, rt_offst += ILT_ENTRY_IN_REGS) {
+                       u64 ilt_hw_entry = 0;
+
+                       /** p_virt could be NULL incase of dynamic
+                        *  allocation
+                        */
+                       if (p_shdw[line].p_virt) {
+                               SET_FIELD(ilt_hw_entry, ILT_ENTRY_VALID, 1ULL);
+                               SET_FIELD(ilt_hw_entry, ILT_ENTRY_PHY_ADDR,
+                                         (p_shdw[line].p_phys >> 12));
+
+                               DP_VERBOSE(p_hwfn, QED_MSG_ILT,
+                                          "Setting RT[0x%08x] from ILT[0x%08x] [Client is %d] to Physical addr: 0x%llx\n",
+                                          rt_offst, line, i,
+                                          (u64)(p_shdw[line].p_phys >> 12));
+                       }
+
+                       STORE_RT_REG_AGG(p_hwfn, rt_offst, ilt_hw_entry);
+               }
+       }
+}
+
+void qed_cxt_hw_init_common(struct qed_hwfn *p_hwfn)
+{
+       qed_cdu_init_common(p_hwfn);
+}
+
+void qed_cxt_hw_init_pf(struct qed_hwfn *p_hwfn)
+{
+       qed_qm_init_pf(p_hwfn);
+       qed_cm_init_pf(p_hwfn);
+       qed_dq_init_pf(p_hwfn);
+       qed_ilt_init_pf(p_hwfn);
+}
+
+int qed_cxt_acquire_cid(struct qed_hwfn *p_hwfn,
+                       enum protocol_type type,
+                       u32 *p_cid)
+{
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       u32 rel_cid;
+
+       if (type >= MAX_CONN_TYPES || !p_mngr->acquired[type].cid_map) {
+               DP_NOTICE(p_hwfn, "Invalid protocol type %d", type);
+               return -EINVAL;
+       }
+
+       rel_cid = find_first_zero_bit(p_mngr->acquired[type].cid_map,
+                                     p_mngr->acquired[type].max_count);
+
+       if (rel_cid >= p_mngr->acquired[type].max_count) {
+               DP_NOTICE(p_hwfn, "no CID available for protocol %d\n",
+                         type);
+               return -EINVAL;
+       }
+
+       __set_bit(rel_cid, p_mngr->acquired[type].cid_map);
+
+       *p_cid = rel_cid + p_mngr->acquired[type].start_cid;
+
+       return 0;
+}
+
+static bool qed_cxt_test_cid_acquired(struct qed_hwfn *p_hwfn,
+                                     u32 cid,
+                                     enum protocol_type *p_type)
+{
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       struct qed_cid_acquired_map *p_map;
+       enum protocol_type p;
+       u32 rel_cid;
+
+       /* Iterate over protocols and find matching cid range */
+       for (p = 0; p < MAX_CONN_TYPES; p++) {
+               p_map = &p_mngr->acquired[p];
+
+               if (!p_map->cid_map)
+                       continue;
+               if (cid >= p_map->start_cid &&
+                   cid < p_map->start_cid + p_map->max_count)
+                       break;
+       }
+       *p_type = p;
+
+       if (p == MAX_CONN_TYPES) {
+               DP_NOTICE(p_hwfn, "Invalid CID %d", cid);
+               return false;
+       }
+
+       rel_cid = cid - p_map->start_cid;
+       if (!test_bit(rel_cid, p_map->cid_map)) {
+               DP_NOTICE(p_hwfn, "CID %d not acquired", cid);
+               return false;
+       }
+       return true;
+}
+
+void qed_cxt_release_cid(struct qed_hwfn *p_hwfn,
+                        u32 cid)
+{
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       enum protocol_type type;
+       bool b_acquired;
+       u32 rel_cid;
+
+       /* Test acquired and find matching per-protocol map */
+       b_acquired = qed_cxt_test_cid_acquired(p_hwfn, cid, &type);
+
+       if (!b_acquired)
+               return;
+
+       rel_cid = cid - p_mngr->acquired[type].start_cid;
+       __clear_bit(rel_cid, p_mngr->acquired[type].cid_map);
+}
+
+int qed_cxt_get_cid_info(struct qed_hwfn *p_hwfn,
+                        struct qed_cxt_info *p_info)
+{
+       struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr;
+       u32 conn_cxt_size, hw_p_size, cxts_per_p, line;
+       enum protocol_type type;
+       bool b_acquired;
+
+       /* Test acquired and find matching per-protocol map */
+       b_acquired = qed_cxt_test_cid_acquired(p_hwfn, p_info->iid, &type);
+
+       if (!b_acquired)
+               return -EINVAL;
+
+       /* set the protocl type */
+       p_info->type = type;
+
+       /* compute context virtual pointer */
+       hw_p_size = p_hwfn->p_cxt_mngr->clients[ILT_CLI_CDUC].p_size.val;
+
+       conn_cxt_size = CONN_CXT_SIZE(p_hwfn);
+       cxts_per_p = ILT_PAGE_IN_BYTES(hw_p_size) / conn_cxt_size;
+       line = p_info->iid / cxts_per_p;
+
+       /* Make sure context is allocated (dynamic allocation) */
+       if (!p_mngr->ilt_shadow[line].p_virt)
+               return -EINVAL;
+
+       p_info->p_cxt = p_mngr->ilt_shadow[line].p_virt +
+                       p_info->iid % cxts_per_p * conn_cxt_size;
+
+       DP_VERBOSE(p_hwfn, (QED_MSG_ILT | QED_MSG_CXT),
+                  "Accessing ILT shadow[%d]: CXT pointer is at %p (for iid %d)\n",
+                  p_info->iid / cxts_per_p, p_info->p_cxt, p_info->iid);
+
+       return 0;
+}
+
+int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn)
+{
+       struct qed_eth_pf_params *p_params = &p_hwfn->pf_params.eth_pf_params;
+
+       /* Set the number of required CORE connections */
+       u32 core_cids = 1; /* SPQ */
+
+       qed_cxt_set_proto_cid_count(p_hwfn, PROTOCOLID_CORE, core_cids);
+
+       qed_cxt_set_proto_cid_count(p_hwfn, PROTOCOLID_ETH,
+                                   p_params->num_cons);
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.h b/drivers/net/ethernet/qlogic/qed/qed_cxt.h
new file mode 100644 (file)
index 0000000..c8e1f5e
--- /dev/null
@@ -0,0 +1,139 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_CXT_H
+#define _QED_CXT_H
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/qed/qed_if.h>
+#include "qed_hsi.h"
+#include "qed.h"
+
+struct qed_cxt_info {
+       void                    *p_cxt;
+       u32                     iid;
+       enum protocol_type      type;
+};
+
+/**
+ * @brief qed_cxt_acquire - Acquire a new cid of a specific protocol type
+ *
+ * @param p_hwfn
+ * @param type
+ * @param p_cid
+ *
+ * @return int
+ */
+int qed_cxt_acquire_cid(struct qed_hwfn *p_hwfn,
+                       enum protocol_type type,
+                       u32 *p_cid);
+
+/**
+ * @brief qedo_cid_get_cxt_info - Returns the context info for a specific cid
+ *
+ *
+ * @param p_hwfn
+ * @param p_info in/out
+ *
+ * @return int
+ */
+int qed_cxt_get_cid_info(struct qed_hwfn *p_hwfn,
+                        struct qed_cxt_info *p_info);
+
+enum qed_cxt_elem_type {
+       QED_ELEM_CXT,
+       QED_ELEM_TASK
+};
+
+/**
+ * @brief qed_cxt_set_pf_params - Set the PF params for cxt init
+ *
+ * @param p_hwfn
+ *
+ * @return int
+ */
+int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_cxt_cfg_ilt_compute - compute ILT init parameters
+ *
+ * @param p_hwfn
+ *
+ * @return int
+ */
+int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_cxt_mngr_alloc - Allocate and init the context manager struct
+ *
+ * @param p_hwfn
+ *
+ * @return int
+ */
+int qed_cxt_mngr_alloc(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_cxt_mngr_free
+ *
+ * @param p_hwfn
+ */
+void qed_cxt_mngr_free(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_cxt_tables_alloc - Allocate ILT shadow, Searcher T2, acquired map
+ *
+ * @param p_hwfn
+ *
+ * @return int
+ */
+int qed_cxt_tables_alloc(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_cxt_mngr_setup - Reset the acquired CIDs
+ *
+ * @param p_hwfn
+ */
+void qed_cxt_mngr_setup(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_cxt_hw_init_common - Initailze ILT and DQ, common phase, per path.
+ *
+ *
+ *
+ * @param p_hwfn
+ */
+void qed_cxt_hw_init_common(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_cxt_hw_init_pf - Initailze ILT and DQ, PF phase, per path.
+ *
+ *
+ *
+ * @param p_hwfn
+ */
+void qed_cxt_hw_init_pf(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_qm_init_pf - Initailze the QM PF phase, per path
+ *
+ * @param p_hwfn
+ */
+
+void qed_qm_init_pf(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_cxt_release - Release a cid
+ *
+ * @param p_hwfn
+ * @param cid
+ */
+void qed_cxt_release_cid(struct qed_hwfn *p_hwfn,
+                        u32 cid);
+
+#endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
new file mode 100644 (file)
index 0000000..b9b7b7e
--- /dev/null
@@ -0,0 +1,1797 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/etherdevice.h>
+#include <linux/qed/qed_chain.h>
+#include <linux/qed/qed_if.h>
+#include "qed.h"
+#include "qed_cxt.h"
+#include "qed_dev_api.h"
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_init_ops.h"
+#include "qed_int.h"
+#include "qed_mcp.h"
+#include "qed_reg_addr.h"
+#include "qed_sp.h"
+
+/* API common to all protocols */
+void qed_init_dp(struct qed_dev *cdev,
+                u32 dp_module, u8 dp_level)
+{
+       u32 i;
+
+       cdev->dp_level = dp_level;
+       cdev->dp_module = dp_module;
+       for (i = 0; i < MAX_HWFNS_PER_DEVICE; i++) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               p_hwfn->dp_level = dp_level;
+               p_hwfn->dp_module = dp_module;
+       }
+}
+
+void qed_init_struct(struct qed_dev *cdev)
+{
+       u8 i;
+
+       for (i = 0; i < MAX_HWFNS_PER_DEVICE; i++) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               p_hwfn->cdev = cdev;
+               p_hwfn->my_id = i;
+               p_hwfn->b_active = false;
+
+               mutex_init(&p_hwfn->dmae_info.mutex);
+       }
+
+       /* hwfn 0 is always active */
+       cdev->hwfns[0].b_active = true;
+
+       /* set the default cache alignment to 128 */
+       cdev->cache_shift = 7;
+}
+
+static void qed_qm_info_free(struct qed_hwfn *p_hwfn)
+{
+       struct qed_qm_info *qm_info = &p_hwfn->qm_info;
+
+       kfree(qm_info->qm_pq_params);
+       qm_info->qm_pq_params = NULL;
+       kfree(qm_info->qm_vport_params);
+       qm_info->qm_vport_params = NULL;
+       kfree(qm_info->qm_port_params);
+       qm_info->qm_port_params = NULL;
+}
+
+void qed_resc_free(struct qed_dev *cdev)
+{
+       int i;
+
+       kfree(cdev->fw_data);
+       cdev->fw_data = NULL;
+
+       kfree(cdev->reset_stats);
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               kfree(p_hwfn->p_tx_cids);
+               p_hwfn->p_tx_cids = NULL;
+               kfree(p_hwfn->p_rx_cids);
+               p_hwfn->p_rx_cids = NULL;
+       }
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               qed_cxt_mngr_free(p_hwfn);
+               qed_qm_info_free(p_hwfn);
+               qed_spq_free(p_hwfn);
+               qed_eq_free(p_hwfn, p_hwfn->p_eq);
+               qed_consq_free(p_hwfn, p_hwfn->p_consq);
+               qed_int_free(p_hwfn);
+               qed_dmae_info_free(p_hwfn);
+       }
+}
+
+static int qed_init_qm_info(struct qed_hwfn *p_hwfn)
+{
+       struct qed_qm_info *qm_info = &p_hwfn->qm_info;
+       struct init_qm_port_params *p_qm_port;
+       u8 num_vports, i, vport_id, num_ports;
+       u16 num_pqs, multi_cos_tcs = 1;
+
+       memset(qm_info, 0, sizeof(*qm_info));
+
+       num_pqs = multi_cos_tcs + 1; /* The '1' is for pure-LB */
+       num_vports = (u8)RESC_NUM(p_hwfn, QED_VPORT);
+
+       /* Sanity checking that setup requires legal number of resources */
+       if (num_pqs > RESC_NUM(p_hwfn, QED_PQ)) {
+               DP_ERR(p_hwfn,
+                      "Need too many Physical queues - 0x%04x when only %04x are available\n",
+                      num_pqs, RESC_NUM(p_hwfn, QED_PQ));
+               return -EINVAL;
+       }
+
+       /* PQs will be arranged as follows: First per-TC PQ then pure-LB quete.
+        */
+       qm_info->qm_pq_params = kzalloc(sizeof(*qm_info->qm_pq_params) *
+                                       num_pqs, GFP_ATOMIC);
+       if (!qm_info->qm_pq_params)
+               goto alloc_err;
+
+       qm_info->qm_vport_params = kzalloc(sizeof(*qm_info->qm_vport_params) *
+                                          num_vports, GFP_ATOMIC);
+       if (!qm_info->qm_vport_params)
+               goto alloc_err;
+
+       qm_info->qm_port_params = kzalloc(sizeof(*qm_info->qm_port_params) *
+                                         MAX_NUM_PORTS, GFP_ATOMIC);
+       if (!qm_info->qm_port_params)
+               goto alloc_err;
+
+       vport_id = (u8)RESC_START(p_hwfn, QED_VPORT);
+
+       /* First init per-TC PQs */
+       for (i = 0; i < multi_cos_tcs; i++) {
+               struct init_qm_pq_params *params = &qm_info->qm_pq_params[i];
+
+               params->vport_id = vport_id;
+               params->tc_id = p_hwfn->hw_info.non_offload_tc;
+               params->wrr_group = 1;
+       }
+
+       /* Then init pure-LB PQ */
+       qm_info->pure_lb_pq = i;
+       qm_info->qm_pq_params[i].vport_id = (u8)RESC_START(p_hwfn, QED_VPORT);
+       qm_info->qm_pq_params[i].tc_id = PURE_LB_TC;
+       qm_info->qm_pq_params[i].wrr_group = 1;
+       i++;
+
+       qm_info->offload_pq = 0;
+       qm_info->num_pqs = num_pqs;
+       qm_info->num_vports = num_vports;
+
+       /* Initialize qm port parameters */
+       num_ports = p_hwfn->cdev->num_ports_in_engines;
+       for (i = 0; i < num_ports; i++) {
+               p_qm_port = &qm_info->qm_port_params[i];
+               p_qm_port->active = 1;
+               p_qm_port->num_active_phys_tcs = 4;
+               p_qm_port->num_pbf_cmd_lines = PBF_MAX_CMD_LINES / num_ports;
+               p_qm_port->num_btb_blocks = BTB_MAX_BLOCKS / num_ports;
+       }
+
+       qm_info->max_phys_tcs_per_port = NUM_OF_PHYS_TCS;
+
+       qm_info->start_pq = (u16)RESC_START(p_hwfn, QED_PQ);
+
+       qm_info->start_vport = (u8)RESC_START(p_hwfn, QED_VPORT);
+
+       qm_info->pf_wfq = 0;
+       qm_info->pf_rl = 0;
+       qm_info->vport_rl_en = 1;
+
+       return 0;
+
+alloc_err:
+       DP_NOTICE(p_hwfn, "Failed to allocate memory for QM params\n");
+       kfree(qm_info->qm_pq_params);
+       kfree(qm_info->qm_vport_params);
+       kfree(qm_info->qm_port_params);
+
+       return -ENOMEM;
+}
+
+int qed_resc_alloc(struct qed_dev *cdev)
+{
+       struct qed_consq *p_consq;
+       struct qed_eq *p_eq;
+       int i, rc = 0;
+
+       cdev->fw_data = kzalloc(sizeof(*cdev->fw_data), GFP_KERNEL);
+       if (!cdev->fw_data)
+               return -ENOMEM;
+
+       /* Allocate Memory for the Queue->CID mapping */
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+               int tx_size = sizeof(struct qed_hw_cid_data) *
+                                    RESC_NUM(p_hwfn, QED_L2_QUEUE);
+               int rx_size = sizeof(struct qed_hw_cid_data) *
+                                    RESC_NUM(p_hwfn, QED_L2_QUEUE);
+
+               p_hwfn->p_tx_cids = kzalloc(tx_size, GFP_KERNEL);
+               if (!p_hwfn->p_tx_cids) {
+                       DP_NOTICE(p_hwfn,
+                                 "Failed to allocate memory for Tx Cids\n");
+                       goto alloc_err;
+               }
+
+               p_hwfn->p_rx_cids = kzalloc(rx_size, GFP_KERNEL);
+               if (!p_hwfn->p_rx_cids) {
+                       DP_NOTICE(p_hwfn,
+                                 "Failed to allocate memory for Rx Cids\n");
+                       goto alloc_err;
+               }
+       }
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               /* First allocate the context manager structure */
+               rc = qed_cxt_mngr_alloc(p_hwfn);
+               if (rc)
+                       goto alloc_err;
+
+               /* Set the HW cid/tid numbers (in the contest manager)
+                * Must be done prior to any further computations.
+                */
+               rc = qed_cxt_set_pf_params(p_hwfn);
+               if (rc)
+                       goto alloc_err;
+
+               /* Prepare and process QM requirements */
+               rc = qed_init_qm_info(p_hwfn);
+               if (rc)
+                       goto alloc_err;
+
+               /* Compute the ILT client partition */
+               rc = qed_cxt_cfg_ilt_compute(p_hwfn);
+               if (rc)
+                       goto alloc_err;
+
+               /* CID map / ILT shadow table / T2
+                * The talbes sizes are determined by the computations above
+                */
+               rc = qed_cxt_tables_alloc(p_hwfn);
+               if (rc)
+                       goto alloc_err;
+
+               /* SPQ, must follow ILT because initializes SPQ context */
+               rc = qed_spq_alloc(p_hwfn);
+               if (rc)
+                       goto alloc_err;
+
+               /* SP status block allocation */
+               p_hwfn->p_dpc_ptt = qed_get_reserved_ptt(p_hwfn,
+                                                        RESERVED_PTT_DPC);
+
+               rc = qed_int_alloc(p_hwfn, p_hwfn->p_main_ptt);
+               if (rc)
+                       goto alloc_err;
+
+               /* EQ */
+               p_eq = qed_eq_alloc(p_hwfn, 256);
+
+               if (!p_eq)
+                       goto alloc_err;
+               p_hwfn->p_eq = p_eq;
+
+               p_consq = qed_consq_alloc(p_hwfn);
+               if (!p_consq)
+                       goto alloc_err;
+               p_hwfn->p_consq = p_consq;
+
+               /* DMA info initialization */
+               rc = qed_dmae_info_alloc(p_hwfn);
+               if (rc) {
+                       DP_NOTICE(p_hwfn,
+                                 "Failed to allocate memory for dmae_info structure\n");
+                       goto alloc_err;
+               }
+       }
+
+       cdev->reset_stats = kzalloc(sizeof(*cdev->reset_stats), GFP_KERNEL);
+       if (!cdev->reset_stats) {
+               DP_NOTICE(cdev, "Failed to allocate reset statistics\n");
+               goto alloc_err;
+       }
+
+       return 0;
+
+alloc_err:
+       qed_resc_free(cdev);
+       return rc;
+}
+
+void qed_resc_setup(struct qed_dev *cdev)
+{
+       int i;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               qed_cxt_mngr_setup(p_hwfn);
+               qed_spq_setup(p_hwfn);
+               qed_eq_setup(p_hwfn, p_hwfn->p_eq);
+               qed_consq_setup(p_hwfn, p_hwfn->p_consq);
+
+               /* Read shadow of current MFW mailbox */
+               qed_mcp_read_mb(p_hwfn, p_hwfn->p_main_ptt);
+               memcpy(p_hwfn->mcp_info->mfw_mb_shadow,
+                      p_hwfn->mcp_info->mfw_mb_cur,
+                      p_hwfn->mcp_info->mfw_mb_length);
+
+               qed_int_setup(p_hwfn, p_hwfn->p_main_ptt);
+       }
+}
+
+#define FINAL_CLEANUP_CMD_OFFSET        (0)
+#define FINAL_CLEANUP_CMD (0x1)
+#define FINAL_CLEANUP_VALID_OFFSET      (6)
+#define FINAL_CLEANUP_VFPF_ID_SHIFT     (7)
+#define FINAL_CLEANUP_COMP (0x2)
+#define FINAL_CLEANUP_POLL_CNT          (100)
+#define FINAL_CLEANUP_POLL_TIME         (10)
+int qed_final_cleanup(struct qed_hwfn *p_hwfn,
+                     struct qed_ptt *p_ptt,
+                     u16 id)
+{
+       u32 command = 0, addr, count = FINAL_CLEANUP_POLL_CNT;
+       int rc = -EBUSY;
+
+       addr = GTT_BAR0_MAP_REG_USDM_RAM + USTORM_FLR_FINAL_ACK_OFFSET;
+
+       command |= FINAL_CLEANUP_CMD << FINAL_CLEANUP_CMD_OFFSET;
+       command |= 1 << FINAL_CLEANUP_VALID_OFFSET;
+       command |= id << FINAL_CLEANUP_VFPF_ID_SHIFT;
+       command |= FINAL_CLEANUP_COMP << SDM_OP_GEN_COMP_TYPE_SHIFT;
+
+       /* Make sure notification is not set before initiating final cleanup */
+       if (REG_RD(p_hwfn, addr)) {
+               DP_NOTICE(
+                       p_hwfn,
+                       "Unexpected; Found final cleanup notification before initiating final cleanup\n");
+               REG_WR(p_hwfn, addr, 0);
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_IOV,
+                  "Sending final cleanup for PFVF[%d] [Command %08x\n]",
+                  id, command);
+
+       qed_wr(p_hwfn, p_ptt, XSDM_REG_OPERATION_GEN, command);
+
+       /* Poll until completion */
+       while (!REG_RD(p_hwfn, addr) && count--)
+               msleep(FINAL_CLEANUP_POLL_TIME);
+
+       if (REG_RD(p_hwfn, addr))
+               rc = 0;
+       else
+               DP_NOTICE(p_hwfn,
+                         "Failed to receive FW final cleanup notification\n");
+
+       /* Cleanup afterwards */
+       REG_WR(p_hwfn, addr, 0);
+
+       return rc;
+}
+
+static void qed_calc_hw_mode(struct qed_hwfn *p_hwfn)
+{
+       int hw_mode = 0;
+
+       hw_mode = (1 << MODE_BB_A0);
+
+       switch (p_hwfn->cdev->num_ports_in_engines) {
+       case 1:
+               hw_mode |= 1 << MODE_PORTS_PER_ENG_1;
+               break;
+       case 2:
+               hw_mode |= 1 << MODE_PORTS_PER_ENG_2;
+               break;
+       case 4:
+               hw_mode |= 1 << MODE_PORTS_PER_ENG_4;
+               break;
+       default:
+               DP_NOTICE(p_hwfn, "num_ports_in_engine = %d not supported\n",
+                         p_hwfn->cdev->num_ports_in_engines);
+               return;
+       }
+
+       switch (p_hwfn->cdev->mf_mode) {
+       case SF:
+               hw_mode |= 1 << MODE_SF;
+               break;
+       case MF_OVLAN:
+               hw_mode |= 1 << MODE_MF_SD;
+               break;
+       case MF_NPAR:
+               hw_mode |= 1 << MODE_MF_SI;
+               break;
+       default:
+               DP_NOTICE(p_hwfn, "Unsupported MF mode, init as SF\n");
+               hw_mode |= 1 << MODE_SF;
+       }
+
+       hw_mode |= 1 << MODE_ASIC;
+
+       p_hwfn->hw_info.hw_mode = hw_mode;
+}
+
+/* Init run time data for all PFs on an engine. */
+static void qed_init_cau_rt_data(struct qed_dev *cdev)
+{
+       u32 offset = CAU_REG_SB_VAR_MEMORY_RT_OFFSET;
+       int i, sb_id;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+               struct qed_igu_info *p_igu_info;
+               struct qed_igu_block *p_block;
+               struct cau_sb_entry sb_entry;
+
+               p_igu_info = p_hwfn->hw_info.p_igu_info;
+
+               for (sb_id = 0; sb_id < QED_MAPPING_MEMORY_SIZE(cdev);
+                    sb_id++) {
+                       p_block = &p_igu_info->igu_map.igu_blocks[sb_id];
+                       if (!p_block->is_pf)
+                               continue;
+
+                       qed_init_cau_sb_entry(p_hwfn, &sb_entry,
+                                             p_block->function_id,
+                                             0, 0);
+                       STORE_RT_REG_AGG(p_hwfn, offset + sb_id * 2,
+                                        sb_entry);
+               }
+       }
+}
+
+static int qed_hw_init_common(struct qed_hwfn *p_hwfn,
+                             struct qed_ptt *p_ptt,
+                             int hw_mode)
+{
+       struct qed_qm_info *qm_info = &p_hwfn->qm_info;
+       struct qed_qm_common_rt_init_params params;
+       struct qed_dev *cdev = p_hwfn->cdev;
+       int rc = 0;
+
+       qed_init_cau_rt_data(cdev);
+
+       /* Program GTT windows */
+       qed_gtt_init(p_hwfn);
+
+       if (p_hwfn->mcp_info) {
+               if (p_hwfn->mcp_info->func_info.bandwidth_max)
+                       qm_info->pf_rl_en = 1;
+               if (p_hwfn->mcp_info->func_info.bandwidth_min)
+                       qm_info->pf_wfq_en = 1;
+       }
+
+       memset(&params, 0, sizeof(params));
+       params.max_ports_per_engine = p_hwfn->cdev->num_ports_in_engines;
+       params.max_phys_tcs_per_port = qm_info->max_phys_tcs_per_port;
+       params.pf_rl_en = qm_info->pf_rl_en;
+       params.pf_wfq_en = qm_info->pf_wfq_en;
+       params.vport_rl_en = qm_info->vport_rl_en;
+       params.vport_wfq_en = qm_info->vport_wfq_en;
+       params.port_params = qm_info->qm_port_params;
+
+       qed_qm_common_rt_init(p_hwfn, &params);
+
+       qed_cxt_hw_init_common(p_hwfn);
+
+       /* Close gate from NIG to BRB/Storm; By default they are open, but
+        * we close them to prevent NIG from passing data to reset blocks.
+        * Should have been done in the ENGINE phase, but init-tool lacks
+        * proper port-pretend capabilities.
+        */
+       qed_wr(p_hwfn, p_ptt, NIG_REG_RX_BRB_OUT_EN, 0);
+       qed_wr(p_hwfn, p_ptt, NIG_REG_STORM_OUT_EN, 0);
+       qed_port_pretend(p_hwfn, p_ptt, p_hwfn->port_id ^ 1);
+       qed_wr(p_hwfn, p_ptt, NIG_REG_RX_BRB_OUT_EN, 0);
+       qed_wr(p_hwfn, p_ptt, NIG_REG_STORM_OUT_EN, 0);
+       qed_port_unpretend(p_hwfn, p_ptt);
+
+       rc = qed_init_run(p_hwfn, p_ptt, PHASE_ENGINE, ANY_PHASE_ID, hw_mode);
+       if (rc != 0)
+               return rc;
+
+       qed_wr(p_hwfn, p_ptt, PSWRQ2_REG_L2P_VALIDATE_VFID, 0);
+       qed_wr(p_hwfn, p_ptt, PGLUE_B_REG_USE_CLIENTID_IN_TAG, 1);
+
+       /* Disable relaxed ordering in the PCI config space */
+       qed_wr(p_hwfn, p_ptt, 0x20b4,
+              qed_rd(p_hwfn, p_ptt, 0x20b4) & ~0x10);
+
+       return rc;
+}
+
+static int qed_hw_init_port(struct qed_hwfn *p_hwfn,
+                           struct qed_ptt *p_ptt,
+                           int hw_mode)
+{
+       int rc = 0;
+
+       rc = qed_init_run(p_hwfn, p_ptt, PHASE_PORT, p_hwfn->port_id,
+                         hw_mode);
+       return rc;
+}
+
+static int qed_hw_init_pf(struct qed_hwfn *p_hwfn,
+                         struct qed_ptt *p_ptt,
+                         int hw_mode,
+                         bool b_hw_start,
+                         enum qed_int_mode int_mode,
+                         bool allow_npar_tx_switch)
+{
+       u8 rel_pf_id = p_hwfn->rel_pf_id;
+       int rc = 0;
+
+       if (p_hwfn->mcp_info) {
+               struct qed_mcp_function_info *p_info;
+
+               p_info = &p_hwfn->mcp_info->func_info;
+               if (p_info->bandwidth_min)
+                       p_hwfn->qm_info.pf_wfq = p_info->bandwidth_min;
+
+               /* Update rate limit once we'll actually have a link */
+               p_hwfn->qm_info.pf_rl = 100;
+       }
+
+       qed_cxt_hw_init_pf(p_hwfn);
+
+       qed_int_igu_init_rt(p_hwfn);
+
+       /* Set VLAN in NIG if needed */
+       if (hw_mode & (1 << MODE_MF_SD)) {
+               DP_VERBOSE(p_hwfn, NETIF_MSG_HW, "Configuring LLH_FUNC_TAG\n");
+               STORE_RT_REG(p_hwfn, NIG_REG_LLH_FUNC_TAG_EN_RT_OFFSET, 1);
+               STORE_RT_REG(p_hwfn, NIG_REG_LLH_FUNC_TAG_VALUE_RT_OFFSET,
+                            p_hwfn->hw_info.ovlan);
+       }
+
+       /* Enable classification by MAC if needed */
+       if (hw_mode & MODE_MF_SI) {
+               DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
+                          "Configuring TAGMAC_CLS_TYPE\n");
+               STORE_RT_REG(p_hwfn,
+                            NIG_REG_LLH_FUNC_TAGMAC_CLS_TYPE_RT_OFFSET, 1);
+       }
+
+       /* Protocl Configuration  */
+       STORE_RT_REG(p_hwfn, PRS_REG_SEARCH_TCP_RT_OFFSET, 0);
+       STORE_RT_REG(p_hwfn, PRS_REG_SEARCH_FCOE_RT_OFFSET, 0);
+       STORE_RT_REG(p_hwfn, PRS_REG_SEARCH_ROCE_RT_OFFSET, 0);
+
+       /* Cleanup chip from previous driver if such remains exist */
+       rc = qed_final_cleanup(p_hwfn, p_ptt, rel_pf_id);
+       if (rc != 0)
+               return rc;
+
+       /* PF Init sequence */
+       rc = qed_init_run(p_hwfn, p_ptt, PHASE_PF, rel_pf_id, hw_mode);
+       if (rc)
+               return rc;
+
+       /* QM_PF Init sequence (may be invoked separately e.g. for DCB) */
+       rc = qed_init_run(p_hwfn, p_ptt, PHASE_QM_PF, rel_pf_id, hw_mode);
+       if (rc)
+               return rc;
+
+       /* Pure runtime initializations - directly to the HW  */
+       qed_int_igu_init_pure_rt(p_hwfn, p_ptt, true, true);
+
+       if (b_hw_start) {
+               /* enable interrupts */
+               qed_int_igu_enable(p_hwfn, p_ptt, int_mode);
+
+               /* send function start command */
+               rc = qed_sp_pf_start(p_hwfn, p_hwfn->cdev->mf_mode);
+               if (rc)
+                       DP_NOTICE(p_hwfn, "Function start ramrod failed\n");
+       }
+       return rc;
+}
+
+static int qed_change_pci_hwfn(struct qed_hwfn *p_hwfn,
+                              struct qed_ptt *p_ptt,
+                              u8 enable)
+{
+       u32 delay_idx = 0, val, set_val = enable ? 1 : 0;
+
+       /* Change PF in PXP */
+       qed_wr(p_hwfn, p_ptt,
+              PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER, set_val);
+
+       /* wait until value is set - try for 1 second every 50us */
+       for (delay_idx = 0; delay_idx < 20000; delay_idx++) {
+               val = qed_rd(p_hwfn, p_ptt,
+                            PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER);
+               if (val == set_val)
+                       break;
+
+               usleep_range(50, 60);
+       }
+
+       if (val != set_val) {
+               DP_NOTICE(p_hwfn,
+                         "PFID_ENABLE_MASTER wasn't changed after a second\n");
+               return -EAGAIN;
+       }
+
+       return 0;
+}
+
+static void qed_reset_mb_shadow(struct qed_hwfn *p_hwfn,
+                               struct qed_ptt *p_main_ptt)
+{
+       /* Read shadow of current MFW mailbox */
+       qed_mcp_read_mb(p_hwfn, p_main_ptt);
+       memcpy(p_hwfn->mcp_info->mfw_mb_shadow,
+              p_hwfn->mcp_info->mfw_mb_cur,
+              p_hwfn->mcp_info->mfw_mb_length);
+}
+
+int qed_hw_init(struct qed_dev *cdev,
+               bool b_hw_start,
+               enum qed_int_mode int_mode,
+               bool allow_npar_tx_switch,
+               const u8 *bin_fw_data)
+{
+       struct qed_storm_stats *p_stat;
+       u32 load_code, param, *p_address;
+       int rc, mfw_rc, i;
+       u8 fw_vport = 0;
+
+       rc = qed_init_fw_data(cdev, bin_fw_data);
+       if (rc != 0)
+               return rc;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               rc = qed_fw_vport(p_hwfn, 0, &fw_vport);
+               if (rc != 0)
+                       return rc;
+
+               /* Enable DMAE in PXP */
+               rc = qed_change_pci_hwfn(p_hwfn, p_hwfn->p_main_ptt, true);
+
+               qed_calc_hw_mode(p_hwfn);
+
+               rc = qed_mcp_load_req(p_hwfn, p_hwfn->p_main_ptt,
+                                     &load_code);
+               if (rc) {
+                       DP_NOTICE(p_hwfn, "Failed sending LOAD_REQ command\n");
+                       return rc;
+               }
+
+               qed_reset_mb_shadow(p_hwfn, p_hwfn->p_main_ptt);
+
+               DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                          "Load request was sent. Resp:0x%x, Load code: 0x%x\n",
+                          rc, load_code);
+
+               p_hwfn->first_on_engine = (load_code ==
+                                          FW_MSG_CODE_DRV_LOAD_ENGINE);
+
+               switch (load_code) {
+               case FW_MSG_CODE_DRV_LOAD_ENGINE:
+                       rc = qed_hw_init_common(p_hwfn, p_hwfn->p_main_ptt,
+                                               p_hwfn->hw_info.hw_mode);
+                       if (rc)
+                               break;
+               /* Fall into */
+               case FW_MSG_CODE_DRV_LOAD_PORT:
+                       rc = qed_hw_init_port(p_hwfn, p_hwfn->p_main_ptt,
+                                             p_hwfn->hw_info.hw_mode);
+                       if (rc)
+                               break;
+
+               /* Fall into */
+               case FW_MSG_CODE_DRV_LOAD_FUNCTION:
+                       rc = qed_hw_init_pf(p_hwfn, p_hwfn->p_main_ptt,
+                                           p_hwfn->hw_info.hw_mode,
+                                           b_hw_start, int_mode,
+                                           allow_npar_tx_switch);
+                       break;
+               default:
+                       rc = -EINVAL;
+                       break;
+               }
+
+               if (rc)
+                       DP_NOTICE(p_hwfn,
+                                 "init phase failed for loadcode 0x%x (rc %d)\n",
+                                  load_code, rc);
+
+               /* ACK mfw regardless of success or failure of initialization */
+               mfw_rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt,
+                                    DRV_MSG_CODE_LOAD_DONE,
+                                    0, &load_code, &param);
+               if (rc)
+                       return rc;
+               if (mfw_rc) {
+                       DP_NOTICE(p_hwfn, "Failed sending LOAD_DONE command\n");
+                       return mfw_rc;
+               }
+
+               p_hwfn->hw_init_done = true;
+
+               /* init PF stats */
+               p_stat = &p_hwfn->storm_stats;
+               p_stat->mstats.address = BAR0_MAP_REG_MSDM_RAM +
+                                        MSTORM_QUEUE_STAT_OFFSET(fw_vport);
+               p_stat->mstats.len = sizeof(struct eth_mstorm_per_queue_stat);
+
+               p_stat->ustats.address = BAR0_MAP_REG_USDM_RAM +
+                                        USTORM_QUEUE_STAT_OFFSET(fw_vport);
+               p_stat->ustats.len = sizeof(struct eth_ustorm_per_queue_stat);
+
+               p_stat->pstats.address = BAR0_MAP_REG_PSDM_RAM +
+                                        PSTORM_QUEUE_STAT_OFFSET(fw_vport);
+               p_stat->pstats.len = sizeof(struct eth_pstorm_per_queue_stat);
+
+               p_address = &p_stat->tstats.address;
+               *p_address = BAR0_MAP_REG_TSDM_RAM +
+                            TSTORM_PORT_STAT_OFFSET(MFW_PORT(p_hwfn));
+               p_stat->tstats.len = sizeof(struct tstorm_per_port_stat);
+       }
+
+       return 0;
+}
+
+#define QED_HW_STOP_RETRY_LIMIT (10)
+int qed_hw_stop(struct qed_dev *cdev)
+{
+       int rc = 0, t_rc;
+       int i, j;
+
+       for_each_hwfn(cdev, j) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[j];
+               struct qed_ptt *p_ptt = p_hwfn->p_main_ptt;
+
+               DP_VERBOSE(p_hwfn, NETIF_MSG_IFDOWN, "Stopping hw/fw\n");
+
+               /* mark the hw as uninitialized... */
+               p_hwfn->hw_init_done = false;
+
+               rc = qed_sp_pf_stop(p_hwfn);
+               if (rc)
+                       return rc;
+
+               qed_wr(p_hwfn, p_ptt,
+                      NIG_REG_RX_LLH_BRB_GATE_DNTFWD_PERPF, 0x1);
+
+               qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_TCP, 0x0);
+               qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_UDP, 0x0);
+               qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_FCOE, 0x0);
+               qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_ROCE, 0x0);
+               qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_OPENFLOW, 0x0);
+
+               qed_wr(p_hwfn, p_ptt, TM_REG_PF_ENABLE_CONN, 0x0);
+               qed_wr(p_hwfn, p_ptt, TM_REG_PF_ENABLE_TASK, 0x0);
+               for (i = 0; i < QED_HW_STOP_RETRY_LIMIT; i++) {
+                       if ((!qed_rd(p_hwfn, p_ptt,
+                                    TM_REG_PF_SCAN_ACTIVE_CONN)) &&
+                           (!qed_rd(p_hwfn, p_ptt,
+                                    TM_REG_PF_SCAN_ACTIVE_TASK)))
+                               break;
+
+                       usleep_range(1000, 2000);
+               }
+               if (i == QED_HW_STOP_RETRY_LIMIT)
+                       DP_NOTICE(p_hwfn,
+                                 "Timers linear scans are not over [Connection %02x Tasks %02x]\n",
+                                 (u8)qed_rd(p_hwfn, p_ptt,
+                                            TM_REG_PF_SCAN_ACTIVE_CONN),
+                                 (u8)qed_rd(p_hwfn, p_ptt,
+                                            TM_REG_PF_SCAN_ACTIVE_TASK));
+
+               /* Disable Attention Generation */
+               qed_int_igu_disable_int(p_hwfn, p_ptt);
+
+               qed_wr(p_hwfn, p_ptt, IGU_REG_LEADING_EDGE_LATCH, 0);
+               qed_wr(p_hwfn, p_ptt, IGU_REG_TRAILING_EDGE_LATCH, 0);
+
+               qed_int_igu_init_pure_rt(p_hwfn, p_ptt, false, true);
+
+               /* Need to wait 1ms to guarantee SBs are cleared */
+               usleep_range(1000, 2000);
+       }
+
+       /* Disable DMAE in PXP - in CMT, this should only be done for
+        * first hw-function, and only after all transactions have
+        * stopped for all active hw-functions.
+        */
+       t_rc = qed_change_pci_hwfn(&cdev->hwfns[0],
+                                  cdev->hwfns[0].p_main_ptt,
+                                  false);
+       if (t_rc != 0)
+               rc = t_rc;
+
+       return rc;
+}
+
+void qed_hw_stop_fastpath(struct qed_dev *cdev)
+{
+       int i, j;
+
+       for_each_hwfn(cdev, j) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[j];
+               struct qed_ptt *p_ptt   = p_hwfn->p_main_ptt;
+
+               DP_VERBOSE(p_hwfn,
+                          NETIF_MSG_IFDOWN,
+                          "Shutting down the fastpath\n");
+
+               qed_wr(p_hwfn, p_ptt,
+                      NIG_REG_RX_LLH_BRB_GATE_DNTFWD_PERPF, 0x1);
+
+               qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_TCP, 0x0);
+               qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_UDP, 0x0);
+               qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_FCOE, 0x0);
+               qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_ROCE, 0x0);
+               qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_OPENFLOW, 0x0);
+
+               qed_wr(p_hwfn, p_ptt, TM_REG_PF_ENABLE_CONN, 0x0);
+               qed_wr(p_hwfn, p_ptt, TM_REG_PF_ENABLE_TASK, 0x0);
+               for (i = 0; i < QED_HW_STOP_RETRY_LIMIT; i++) {
+                       if ((!qed_rd(p_hwfn, p_ptt,
+                                    TM_REG_PF_SCAN_ACTIVE_CONN)) &&
+                           (!qed_rd(p_hwfn, p_ptt,
+                                    TM_REG_PF_SCAN_ACTIVE_TASK)))
+                               break;
+
+                       usleep_range(1000, 2000);
+               }
+               if (i == QED_HW_STOP_RETRY_LIMIT)
+                       DP_NOTICE(p_hwfn,
+                                 "Timers linear scans are not over [Connection %02x Tasks %02x]\n",
+                                 (u8)qed_rd(p_hwfn, p_ptt,
+                                            TM_REG_PF_SCAN_ACTIVE_CONN),
+                                 (u8)qed_rd(p_hwfn, p_ptt,
+                                            TM_REG_PF_SCAN_ACTIVE_TASK));
+
+               qed_int_igu_init_pure_rt(p_hwfn, p_ptt, false, false);
+
+               /* Need to wait 1ms to guarantee SBs are cleared */
+               usleep_range(1000, 2000);
+       }
+}
+
+void qed_hw_start_fastpath(struct qed_hwfn *p_hwfn)
+{
+       /* Re-open incoming traffic */
+       qed_wr(p_hwfn, p_hwfn->p_main_ptt,
+              NIG_REG_RX_LLH_BRB_GATE_DNTFWD_PERPF, 0x0);
+}
+
+static int qed_reg_assert(struct qed_hwfn *hwfn,
+                         struct qed_ptt *ptt, u32 reg,
+                         bool expected)
+{
+       u32 assert_val = qed_rd(hwfn, ptt, reg);
+
+       if (assert_val != expected) {
+               DP_NOTICE(hwfn, "Value at address 0x%x != 0x%08x\n",
+                         reg, expected);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int qed_hw_reset(struct qed_dev *cdev)
+{
+       int rc = 0;
+       u32 unload_resp, unload_param;
+       int i;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               DP_VERBOSE(p_hwfn, NETIF_MSG_IFDOWN, "Resetting hw/fw\n");
+
+               /* Check for incorrect states */
+               qed_reg_assert(p_hwfn, p_hwfn->p_main_ptt,
+                              QM_REG_USG_CNT_PF_TX, 0);
+               qed_reg_assert(p_hwfn, p_hwfn->p_main_ptt,
+                              QM_REG_USG_CNT_PF_OTHER, 0);
+
+               /* Disable PF in HW blocks */
+               qed_wr(p_hwfn, p_hwfn->p_main_ptt, DORQ_REG_PF_DB_ENABLE, 0);
+               qed_wr(p_hwfn, p_hwfn->p_main_ptt, QM_REG_PF_EN, 0);
+               qed_wr(p_hwfn, p_hwfn->p_main_ptt,
+                      TCFC_REG_STRONG_ENABLE_PF, 0);
+               qed_wr(p_hwfn, p_hwfn->p_main_ptt,
+                      CCFC_REG_STRONG_ENABLE_PF, 0);
+
+               /* Send unload command to MCP */
+               rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt,
+                                DRV_MSG_CODE_UNLOAD_REQ,
+                                DRV_MB_PARAM_UNLOAD_WOL_MCP,
+                                &unload_resp, &unload_param);
+               if (rc) {
+                       DP_NOTICE(p_hwfn, "qed_hw_reset: UNLOAD_REQ failed\n");
+                       unload_resp = FW_MSG_CODE_DRV_UNLOAD_ENGINE;
+               }
+
+               rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt,
+                                DRV_MSG_CODE_UNLOAD_DONE,
+                                0, &unload_resp, &unload_param);
+               if (rc) {
+                       DP_NOTICE(p_hwfn, "qed_hw_reset: UNLOAD_DONE failed\n");
+                       return rc;
+               }
+       }
+
+       return rc;
+}
+
+/* Free hwfn memory and resources acquired in hw_hwfn_prepare */
+static void qed_hw_hwfn_free(struct qed_hwfn *p_hwfn)
+{
+       qed_ptt_pool_free(p_hwfn);
+       kfree(p_hwfn->hw_info.p_igu_info);
+}
+
+/* Setup bar access */
+static int qed_hw_hwfn_prepare(struct qed_hwfn *p_hwfn)
+{
+       int rc;
+
+       /* Allocate PTT pool */
+       rc = qed_ptt_pool_alloc(p_hwfn);
+       if (rc)
+               return rc;
+
+       /* Allocate the main PTT */
+       p_hwfn->p_main_ptt = qed_get_reserved_ptt(p_hwfn, RESERVED_PTT_MAIN);
+
+       /* clear indirect access */
+       qed_wr(p_hwfn, p_hwfn->p_main_ptt, PGLUE_B_REG_PGL_ADDR_88_F0, 0);
+       qed_wr(p_hwfn, p_hwfn->p_main_ptt, PGLUE_B_REG_PGL_ADDR_8C_F0, 0);
+       qed_wr(p_hwfn, p_hwfn->p_main_ptt, PGLUE_B_REG_PGL_ADDR_90_F0, 0);
+       qed_wr(p_hwfn, p_hwfn->p_main_ptt, PGLUE_B_REG_PGL_ADDR_94_F0, 0);
+
+       /* Clean Previous errors if such exist */
+       qed_wr(p_hwfn, p_hwfn->p_main_ptt,
+              PGLUE_B_REG_WAS_ERROR_PF_31_0_CLR,
+              1 << p_hwfn->abs_pf_id);
+
+       /* enable internal target-read */
+       qed_wr(p_hwfn, p_hwfn->p_main_ptt,
+              PGLUE_B_REG_INTERNAL_PFID_ENABLE_TARGET_READ, 1);
+
+       return 0;
+}
+
+static void get_function_id(struct qed_hwfn *p_hwfn)
+{
+       /* ME Register */
+       p_hwfn->hw_info.opaque_fid = (u16)REG_RD(p_hwfn, PXP_PF_ME_OPAQUE_ADDR);
+
+       p_hwfn->hw_info.concrete_fid = REG_RD(p_hwfn, PXP_PF_ME_CONCRETE_ADDR);
+
+       p_hwfn->abs_pf_id = (p_hwfn->hw_info.concrete_fid >> 16) & 0xf;
+       p_hwfn->rel_pf_id = GET_FIELD(p_hwfn->hw_info.concrete_fid,
+                                     PXP_CONCRETE_FID_PFID);
+       p_hwfn->port_id = GET_FIELD(p_hwfn->hw_info.concrete_fid,
+                                   PXP_CONCRETE_FID_PORT);
+}
+
+static void qed_hw_set_feat(struct qed_hwfn *p_hwfn)
+{
+       u32 *feat_num = p_hwfn->hw_info.feat_num;
+       int num_features = 1;
+
+       feat_num[QED_PF_L2_QUE] = min_t(u32, RESC_NUM(p_hwfn, QED_SB) /
+                                               num_features,
+                                       RESC_NUM(p_hwfn, QED_L2_QUEUE));
+       DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE,
+                  "#PF_L2_QUEUES=%d #SBS=%d num_features=%d\n",
+                  feat_num[QED_PF_L2_QUE], RESC_NUM(p_hwfn, QED_SB),
+                  num_features);
+}
+
+static void qed_hw_get_resc(struct qed_hwfn *p_hwfn)
+{
+       u32 *resc_start = p_hwfn->hw_info.resc_start;
+       u32 *resc_num = p_hwfn->hw_info.resc_num;
+       int num_funcs, i;
+
+       num_funcs = IS_MF(p_hwfn) ? MAX_NUM_PFS_BB
+                                 : p_hwfn->cdev->num_ports_in_engines;
+
+       resc_num[QED_SB] = min_t(u32,
+                                (MAX_SB_PER_PATH_BB / num_funcs),
+                                qed_int_get_num_sbs(p_hwfn, NULL));
+       resc_num[QED_L2_QUEUE] = MAX_NUM_L2_QUEUES_BB / num_funcs;
+       resc_num[QED_VPORT] = MAX_NUM_VPORTS_BB / num_funcs;
+       resc_num[QED_RSS_ENG] = ETH_RSS_ENGINE_NUM_BB / num_funcs;
+       resc_num[QED_PQ] = MAX_QM_TX_QUEUES_BB / num_funcs;
+       resc_num[QED_RL] = 8;
+       resc_num[QED_MAC] = ETH_NUM_MAC_FILTERS / num_funcs;
+       resc_num[QED_VLAN] = (ETH_NUM_VLAN_FILTERS - 1 /*For vlan0*/) /
+                            num_funcs;
+       resc_num[QED_ILT] = 950;
+
+       for (i = 0; i < QED_MAX_RESC; i++)
+               resc_start[i] = resc_num[i] * p_hwfn->rel_pf_id;
+
+       qed_hw_set_feat(p_hwfn);
+
+       DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE,
+                  "The numbers for each resource are:\n"
+                  "SB = %d start = %d\n"
+                  "L2_QUEUE = %d start = %d\n"
+                  "VPORT = %d start = %d\n"
+                  "PQ = %d start = %d\n"
+                  "RL = %d start = %d\n"
+                  "MAC = %d start = %d\n"
+                  "VLAN = %d start = %d\n"
+                  "ILT = %d start = %d\n",
+                  p_hwfn->hw_info.resc_num[QED_SB],
+                  p_hwfn->hw_info.resc_start[QED_SB],
+                  p_hwfn->hw_info.resc_num[QED_L2_QUEUE],
+                  p_hwfn->hw_info.resc_start[QED_L2_QUEUE],
+                  p_hwfn->hw_info.resc_num[QED_VPORT],
+                  p_hwfn->hw_info.resc_start[QED_VPORT],
+                  p_hwfn->hw_info.resc_num[QED_PQ],
+                  p_hwfn->hw_info.resc_start[QED_PQ],
+                  p_hwfn->hw_info.resc_num[QED_RL],
+                  p_hwfn->hw_info.resc_start[QED_RL],
+                  p_hwfn->hw_info.resc_num[QED_MAC],
+                  p_hwfn->hw_info.resc_start[QED_MAC],
+                  p_hwfn->hw_info.resc_num[QED_VLAN],
+                  p_hwfn->hw_info.resc_start[QED_VLAN],
+                  p_hwfn->hw_info.resc_num[QED_ILT],
+                  p_hwfn->hw_info.resc_start[QED_ILT]);
+}
+
+static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn,
+                              struct qed_ptt *p_ptt)
+{
+       u32 nvm_cfg1_offset, mf_mode, addr, generic_cont0, core_cfg;
+       u32 port_cfg_addr, link_temp, val, nvm_cfg_addr;
+       struct qed_mcp_link_params *link;
+
+       /* Read global nvm_cfg address */
+       nvm_cfg_addr = qed_rd(p_hwfn, p_ptt, MISC_REG_GEN_PURP_CR0);
+
+       /* Verify MCP has initialized it */
+       if (!nvm_cfg_addr) {
+               DP_NOTICE(p_hwfn, "Shared memory not initialized\n");
+               return -EINVAL;
+       }
+
+       /* Read nvm_cfg1  (Notice this is just offset, and not offsize (TBD) */
+       nvm_cfg1_offset = qed_rd(p_hwfn, p_ptt, nvm_cfg_addr + 4);
+
+       /* Read Vendor Id / Device Id */
+       addr = MCP_REG_SCRATCH + nvm_cfg1_offset +
+              offsetof(struct nvm_cfg1, glob) +
+              offsetof(struct nvm_cfg1_glob, pci_id);
+       p_hwfn->hw_info.vendor_id = qed_rd(p_hwfn, p_ptt, addr) &
+                                   NVM_CFG1_GLOB_VENDOR_ID_MASK;
+
+       addr = MCP_REG_SCRATCH + nvm_cfg1_offset +
+              offsetof(struct nvm_cfg1, glob) +
+              offsetof(struct nvm_cfg1_glob, core_cfg);
+
+       core_cfg = qed_rd(p_hwfn, p_ptt, addr);
+
+       switch ((core_cfg & NVM_CFG1_GLOB_NETWORK_PORT_MODE_MASK) >>
+               NVM_CFG1_GLOB_NETWORK_PORT_MODE_OFFSET) {
+       case NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_2X40G:
+               p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_2X40G;
+               break;
+       case NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_2X50G:
+               p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_2X50G;
+               break;
+       case NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_1X100G:
+               p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_1X100G;
+               break;
+       case NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_4X10G_F:
+               p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_4X10G_F;
+               break;
+       case NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_4X10G_E:
+               p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_4X10G_E;
+               break;
+       case NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_4X20G:
+               p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_4X20G;
+               break;
+       case NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_1X40G:
+               p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_1X40G;
+               break;
+       case NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_2X25G:
+               p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_2X25G;
+               break;
+       case NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_1X25G:
+               p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_1X25G;
+               break;
+       default:
+               DP_NOTICE(p_hwfn, "Unknown port mode in 0x%08x\n",
+                         core_cfg);
+               break;
+       }
+
+       addr = MCP_REG_SCRATCH + nvm_cfg1_offset +
+              offsetof(struct nvm_cfg1, func[MCP_PF_ID(p_hwfn)]) +
+              offsetof(struct nvm_cfg1_func, device_id);
+       val = qed_rd(p_hwfn, p_ptt, addr);
+
+       if (IS_MF(p_hwfn)) {
+               p_hwfn->hw_info.device_id =
+                       (val & NVM_CFG1_FUNC_MF_VENDOR_DEVICE_ID_MASK) >>
+                       NVM_CFG1_FUNC_MF_VENDOR_DEVICE_ID_OFFSET;
+       } else {
+               p_hwfn->hw_info.device_id =
+                       (val & NVM_CFG1_FUNC_VENDOR_DEVICE_ID_MASK) >>
+                       NVM_CFG1_FUNC_VENDOR_DEVICE_ID_OFFSET;
+       }
+
+       /* Read default link configuration */
+       link = &p_hwfn->mcp_info->link_input;
+       port_cfg_addr = MCP_REG_SCRATCH + nvm_cfg1_offset +
+                       offsetof(struct nvm_cfg1, port[MFW_PORT(p_hwfn)]);
+       link_temp = qed_rd(p_hwfn, p_ptt,
+                          port_cfg_addr +
+                          offsetof(struct nvm_cfg1_port, speed_cap_mask));
+       link->speed.advertised_speeds =
+               link_temp & NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_MASK;
+
+       p_hwfn->mcp_info->link_capabilities.speed_capabilities =
+                                               link->speed.advertised_speeds;
+
+       link_temp = qed_rd(p_hwfn, p_ptt,
+                          port_cfg_addr +
+                          offsetof(struct nvm_cfg1_port, link_settings));
+       switch ((link_temp & NVM_CFG1_PORT_DRV_LINK_SPEED_MASK) >>
+               NVM_CFG1_PORT_DRV_LINK_SPEED_OFFSET) {
+       case NVM_CFG1_PORT_DRV_LINK_SPEED_AUTONEG:
+               link->speed.autoneg = true;
+               break;
+       case NVM_CFG1_PORT_DRV_LINK_SPEED_1G:
+               link->speed.forced_speed = 1000;
+               break;
+       case NVM_CFG1_PORT_DRV_LINK_SPEED_10G:
+               link->speed.forced_speed = 10000;
+               break;
+       case NVM_CFG1_PORT_DRV_LINK_SPEED_25G:
+               link->speed.forced_speed = 25000;
+               break;
+       case NVM_CFG1_PORT_DRV_LINK_SPEED_40G:
+               link->speed.forced_speed = 40000;
+               break;
+       case NVM_CFG1_PORT_DRV_LINK_SPEED_50G:
+               link->speed.forced_speed = 50000;
+               break;
+       case NVM_CFG1_PORT_DRV_LINK_SPEED_100G:
+               link->speed.forced_speed = 100000;
+               break;
+       default:
+               DP_NOTICE(p_hwfn, "Unknown Speed in 0x%08x\n",
+                         link_temp);
+       }
+
+       link_temp &= NVM_CFG1_PORT_DRV_FLOW_CONTROL_MASK;
+       link_temp >>= NVM_CFG1_PORT_DRV_FLOW_CONTROL_OFFSET;
+       link->pause.autoneg = !!(link_temp &
+                                NVM_CFG1_PORT_DRV_FLOW_CONTROL_AUTONEG);
+       link->pause.forced_rx = !!(link_temp &
+                                  NVM_CFG1_PORT_DRV_FLOW_CONTROL_RX);
+       link->pause.forced_tx = !!(link_temp &
+                                  NVM_CFG1_PORT_DRV_FLOW_CONTROL_TX);
+       link->loopback_mode = 0;
+
+       DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
+                  "Read default link: Speed 0x%08x, Adv. Speed 0x%08x, AN: 0x%02x, PAUSE AN: 0x%02x\n",
+                  link->speed.forced_speed, link->speed.advertised_speeds,
+                  link->speed.autoneg, link->pause.autoneg);
+
+       /* Read Multi-function information from shmem */
+       addr = MCP_REG_SCRATCH + nvm_cfg1_offset +
+              offsetof(struct nvm_cfg1, glob) +
+              offsetof(struct nvm_cfg1_glob, generic_cont0);
+
+       generic_cont0 = qed_rd(p_hwfn, p_ptt, addr);
+
+       mf_mode = (generic_cont0 & NVM_CFG1_GLOB_MF_MODE_MASK) >>
+                 NVM_CFG1_GLOB_MF_MODE_OFFSET;
+
+       switch (mf_mode) {
+       case NVM_CFG1_GLOB_MF_MODE_MF_ALLOWED:
+               p_hwfn->cdev->mf_mode = MF_OVLAN;
+               break;
+       case NVM_CFG1_GLOB_MF_MODE_NPAR1_0:
+               p_hwfn->cdev->mf_mode = MF_NPAR;
+               break;
+       case NVM_CFG1_GLOB_MF_MODE_FORCED_SF:
+               p_hwfn->cdev->mf_mode = SF;
+               break;
+       }
+       DP_INFO(p_hwfn, "Multi function mode is %08x\n",
+               p_hwfn->cdev->mf_mode);
+
+       return qed_mcp_fill_shmem_func_info(p_hwfn, p_ptt);
+}
+
+static int
+qed_get_hw_info(struct qed_hwfn *p_hwfn,
+               struct qed_ptt *p_ptt,
+               enum qed_pci_personality personality)
+{
+       u32 port_mode;
+       int rc;
+
+       /* Read the port mode */
+       port_mode = qed_rd(p_hwfn, p_ptt,
+                          CNIG_REG_NW_PORT_MODE_BB_B0);
+
+       if (port_mode < 3) {
+               p_hwfn->cdev->num_ports_in_engines = 1;
+       } else if (port_mode <= 5) {
+               p_hwfn->cdev->num_ports_in_engines = 2;
+       } else {
+               DP_NOTICE(p_hwfn, "PORT MODE: %d not supported\n",
+                         p_hwfn->cdev->num_ports_in_engines);
+
+               /* Default num_ports_in_engines to something */
+               p_hwfn->cdev->num_ports_in_engines = 1;
+       }
+
+       qed_hw_get_nvm_info(p_hwfn, p_ptt);
+
+       rc = qed_int_igu_read_cam(p_hwfn, p_ptt);
+       if (rc)
+               return rc;
+
+       if (qed_mcp_is_init(p_hwfn))
+               ether_addr_copy(p_hwfn->hw_info.hw_mac_addr,
+                               p_hwfn->mcp_info->func_info.mac);
+       else
+               eth_random_addr(p_hwfn->hw_info.hw_mac_addr);
+
+       if (qed_mcp_is_init(p_hwfn)) {
+               if (p_hwfn->mcp_info->func_info.ovlan != QED_MCP_VLAN_UNSET)
+                       p_hwfn->hw_info.ovlan =
+                               p_hwfn->mcp_info->func_info.ovlan;
+
+               qed_mcp_cmd_port_init(p_hwfn, p_ptt);
+       }
+
+       if (qed_mcp_is_init(p_hwfn)) {
+               enum qed_pci_personality protocol;
+
+               protocol = p_hwfn->mcp_info->func_info.protocol;
+               p_hwfn->hw_info.personality = protocol;
+       }
+
+       qed_hw_get_resc(p_hwfn);
+
+       return rc;
+}
+
+static void qed_get_dev_info(struct qed_dev *cdev)
+{
+       u32 tmp;
+
+       cdev->chip_num = (u16)qed_rd(cdev->hwfns, cdev->hwfns[0].p_main_ptt,
+                                    MISCS_REG_CHIP_NUM);
+       cdev->chip_rev = (u16)qed_rd(cdev->hwfns, cdev->hwfns[0].p_main_ptt,
+                                    MISCS_REG_CHIP_REV);
+       MASK_FIELD(CHIP_REV, cdev->chip_rev);
+
+       /* Learn number of HW-functions */
+       tmp = qed_rd(cdev->hwfns, cdev->hwfns[0].p_main_ptt,
+                    MISCS_REG_CMT_ENABLED_FOR_PAIR);
+
+       if (tmp & (1 << cdev->hwfns[0].rel_pf_id)) {
+               DP_NOTICE(cdev->hwfns, "device in CMT mode\n");
+               cdev->num_hwfns = 2;
+       } else {
+               cdev->num_hwfns = 1;
+       }
+
+       cdev->chip_bond_id = qed_rd(cdev->hwfns, cdev->hwfns[0].p_main_ptt,
+                                   MISCS_REG_CHIP_TEST_REG) >> 4;
+       MASK_FIELD(CHIP_BOND_ID, cdev->chip_bond_id);
+       cdev->chip_metal = (u16)qed_rd(cdev->hwfns, cdev->hwfns[0].p_main_ptt,
+                                      MISCS_REG_CHIP_METAL);
+       MASK_FIELD(CHIP_METAL, cdev->chip_metal);
+
+       DP_INFO(cdev->hwfns,
+               "Chip details - Num: %04x Rev: %04x Bond id: %04x Metal: %04x\n",
+               cdev->chip_num, cdev->chip_rev,
+               cdev->chip_bond_id, cdev->chip_metal);
+}
+
+static int qed_hw_prepare_single(struct qed_hwfn *p_hwfn,
+                                void __iomem *p_regview,
+                                void __iomem *p_doorbells,
+                                enum qed_pci_personality personality)
+{
+       int rc = 0;
+
+       /* Split PCI bars evenly between hwfns */
+       p_hwfn->regview = p_regview;
+       p_hwfn->doorbells = p_doorbells;
+
+       /* Validate that chip access is feasible */
+       if (REG_RD(p_hwfn, PXP_PF_ME_OPAQUE_ADDR) == 0xffffffff) {
+               DP_ERR(p_hwfn,
+                      "Reading the ME register returns all Fs; Preventing further chip access\n");
+               return -EINVAL;
+       }
+
+       get_function_id(p_hwfn);
+
+       rc = qed_hw_hwfn_prepare(p_hwfn);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "Failed to prepare hwfn's hw\n");
+               goto err0;
+       }
+
+       /* First hwfn learns basic information, e.g., number of hwfns */
+       if (!p_hwfn->my_id)
+               qed_get_dev_info(p_hwfn->cdev);
+
+       /* Initialize MCP structure */
+       rc = qed_mcp_cmd_init(p_hwfn, p_hwfn->p_main_ptt);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "Failed initializing mcp command\n");
+               goto err1;
+       }
+
+       /* Read the device configuration information from the HW and SHMEM */
+       rc = qed_get_hw_info(p_hwfn, p_hwfn->p_main_ptt, personality);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "Failed to get HW information\n");
+               goto err2;
+       }
+
+       /* Allocate the init RT array and initialize the init-ops engine */
+       rc = qed_init_alloc(p_hwfn);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "Failed to allocate the init array\n");
+               goto err2;
+       }
+
+       return rc;
+err2:
+       qed_mcp_free(p_hwfn);
+err1:
+       qed_hw_hwfn_free(p_hwfn);
+err0:
+       return rc;
+}
+
+static u32 qed_hw_bar_size(struct qed_dev *cdev,
+                          u8 bar_id)
+{
+       u32 size = pci_resource_len(cdev->pdev, (bar_id > 0) ? 2 : 0);
+
+       return size / cdev->num_hwfns;
+}
+
+int qed_hw_prepare(struct qed_dev *cdev,
+                  int personality)
+{
+       int rc, i;
+
+       /* Store the precompiled init data ptrs */
+       qed_init_iro_array(cdev);
+
+       /* Initialize the first hwfn - will learn number of hwfns */
+       rc = qed_hw_prepare_single(&cdev->hwfns[0], cdev->regview,
+                                  cdev->doorbells, personality);
+       if (rc)
+               return rc;
+
+       personality = cdev->hwfns[0].hw_info.personality;
+
+       /* Initialize the rest of the hwfns */
+       for (i = 1; i < cdev->num_hwfns; i++) {
+               void __iomem *p_regview, *p_doorbell;
+
+               p_regview =  cdev->regview +
+                            i * qed_hw_bar_size(cdev, 0);
+               p_doorbell = cdev->doorbells +
+                            i * qed_hw_bar_size(cdev, 1);
+               rc = qed_hw_prepare_single(&cdev->hwfns[i], p_regview,
+                                          p_doorbell, personality);
+               if (rc) {
+                       /* Cleanup previously initialized hwfns */
+                       while (--i >= 0) {
+                               qed_init_free(&cdev->hwfns[i]);
+                               qed_mcp_free(&cdev->hwfns[i]);
+                               qed_hw_hwfn_free(&cdev->hwfns[i]);
+                       }
+                       return rc;
+               }
+       }
+
+       return 0;
+}
+
+void qed_hw_remove(struct qed_dev *cdev)
+{
+       int i;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               qed_init_free(p_hwfn);
+               qed_hw_hwfn_free(p_hwfn);
+               qed_mcp_free(p_hwfn);
+       }
+}
+
+int qed_chain_alloc(struct qed_dev *cdev,
+                   enum qed_chain_use_mode intended_use,
+                   enum qed_chain_mode mode,
+                   u16 num_elems,
+                   size_t elem_size,
+                   struct qed_chain *p_chain)
+{
+       dma_addr_t p_pbl_phys = 0;
+       void *p_pbl_virt = NULL;
+       dma_addr_t p_phys = 0;
+       void *p_virt = NULL;
+       u16 page_cnt = 0;
+       size_t size;
+
+       if (mode == QED_CHAIN_MODE_SINGLE)
+               page_cnt = 1;
+       else
+               page_cnt = QED_CHAIN_PAGE_CNT(num_elems, elem_size, mode);
+
+       size = page_cnt * QED_CHAIN_PAGE_SIZE;
+       p_virt = dma_alloc_coherent(&cdev->pdev->dev,
+                                   size, &p_phys, GFP_KERNEL);
+       if (!p_virt) {
+               DP_NOTICE(cdev, "Failed to allocate chain mem\n");
+               goto nomem;
+       }
+
+       if (mode == QED_CHAIN_MODE_PBL) {
+               size = page_cnt * QED_CHAIN_PBL_ENTRY_SIZE;
+               p_pbl_virt = dma_alloc_coherent(&cdev->pdev->dev,
+                                               size, &p_pbl_phys,
+                                               GFP_KERNEL);
+               if (!p_pbl_virt) {
+                       DP_NOTICE(cdev, "Failed to allocate chain pbl mem\n");
+                       goto nomem;
+               }
+
+               qed_chain_pbl_init(p_chain, p_virt, p_phys, page_cnt,
+                                  (u8)elem_size, intended_use,
+                                  p_pbl_phys, p_pbl_virt);
+       } else {
+               qed_chain_init(p_chain, p_virt, p_phys, page_cnt,
+                              (u8)elem_size, intended_use, mode);
+       }
+
+       return 0;
+
+nomem:
+       dma_free_coherent(&cdev->pdev->dev,
+                         page_cnt * QED_CHAIN_PAGE_SIZE,
+                         p_virt, p_phys);
+       dma_free_coherent(&cdev->pdev->dev,
+                         page_cnt * QED_CHAIN_PBL_ENTRY_SIZE,
+                         p_pbl_virt, p_pbl_phys);
+
+       return -ENOMEM;
+}
+
+void qed_chain_free(struct qed_dev *cdev,
+                   struct qed_chain *p_chain)
+{
+       size_t size;
+
+       if (!p_chain->p_virt_addr)
+               return;
+
+       if (p_chain->mode == QED_CHAIN_MODE_PBL) {
+               size = p_chain->page_cnt * QED_CHAIN_PBL_ENTRY_SIZE;
+               dma_free_coherent(&cdev->pdev->dev, size,
+                                 p_chain->pbl.p_virt_table,
+                                 p_chain->pbl.p_phys_table);
+       }
+
+       size = p_chain->page_cnt * QED_CHAIN_PAGE_SIZE;
+       dma_free_coherent(&cdev->pdev->dev, size,
+                         p_chain->p_virt_addr,
+                         p_chain->p_phys_addr);
+}
+
+static void __qed_get_vport_stats(struct qed_dev *cdev,
+                                 struct qed_eth_stats  *stats)
+{
+       int i, j;
+
+       memset(stats, 0, sizeof(*stats));
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+               struct eth_mstorm_per_queue_stat mstats;
+               struct eth_ustorm_per_queue_stat ustats;
+               struct eth_pstorm_per_queue_stat pstats;
+               struct tstorm_per_port_stat tstats;
+               struct port_stats port_stats;
+               struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
+
+               if (!p_ptt) {
+                       DP_ERR(p_hwfn, "Failed to acquire ptt\n");
+                       continue;
+               }
+
+               memset(&mstats, 0, sizeof(mstats));
+               qed_memcpy_from(p_hwfn, p_ptt, &mstats,
+                               p_hwfn->storm_stats.mstats.address,
+                               p_hwfn->storm_stats.mstats.len);
+
+               memset(&ustats, 0, sizeof(ustats));
+               qed_memcpy_from(p_hwfn, p_ptt, &ustats,
+                               p_hwfn->storm_stats.ustats.address,
+                               p_hwfn->storm_stats.ustats.len);
+
+               memset(&pstats, 0, sizeof(pstats));
+               qed_memcpy_from(p_hwfn, p_ptt, &pstats,
+                               p_hwfn->storm_stats.pstats.address,
+                               p_hwfn->storm_stats.pstats.len);
+
+               memset(&tstats, 0, sizeof(tstats));
+               qed_memcpy_from(p_hwfn, p_ptt, &tstats,
+                               p_hwfn->storm_stats.tstats.address,
+                               p_hwfn->storm_stats.tstats.len);
+
+               memset(&port_stats, 0, sizeof(port_stats));
+
+               if (p_hwfn->mcp_info)
+                       qed_memcpy_from(p_hwfn, p_ptt, &port_stats,
+                                       p_hwfn->mcp_info->port_addr +
+                                       offsetof(struct public_port, stats),
+                                       sizeof(port_stats));
+               qed_ptt_release(p_hwfn, p_ptt);
+
+               stats->no_buff_discards +=
+                       HILO_64_REGPAIR(mstats.no_buff_discard);
+               stats->packet_too_big_discard +=
+                       HILO_64_REGPAIR(mstats.packet_too_big_discard);
+               stats->ttl0_discard +=
+                       HILO_64_REGPAIR(mstats.ttl0_discard);
+               stats->tpa_coalesced_pkts +=
+                       HILO_64_REGPAIR(mstats.tpa_coalesced_pkts);
+               stats->tpa_coalesced_events +=
+                       HILO_64_REGPAIR(mstats.tpa_coalesced_events);
+               stats->tpa_aborts_num +=
+                       HILO_64_REGPAIR(mstats.tpa_aborts_num);
+               stats->tpa_coalesced_bytes +=
+                       HILO_64_REGPAIR(mstats.tpa_coalesced_bytes);
+
+               stats->rx_ucast_bytes +=
+                       HILO_64_REGPAIR(ustats.rcv_ucast_bytes);
+               stats->rx_mcast_bytes +=
+                       HILO_64_REGPAIR(ustats.rcv_mcast_bytes);
+               stats->rx_bcast_bytes +=
+                       HILO_64_REGPAIR(ustats.rcv_bcast_bytes);
+               stats->rx_ucast_pkts +=
+                       HILO_64_REGPAIR(ustats.rcv_ucast_pkts);
+               stats->rx_mcast_pkts +=
+                       HILO_64_REGPAIR(ustats.rcv_mcast_pkts);
+               stats->rx_bcast_pkts +=
+                       HILO_64_REGPAIR(ustats.rcv_bcast_pkts);
+
+               stats->mftag_filter_discards +=
+                       HILO_64_REGPAIR(tstats.mftag_filter_discard);
+               stats->mac_filter_discards +=
+                       HILO_64_REGPAIR(tstats.eth_mac_filter_discard);
+
+               stats->tx_ucast_bytes +=
+                       HILO_64_REGPAIR(pstats.sent_ucast_bytes);
+               stats->tx_mcast_bytes +=
+                       HILO_64_REGPAIR(pstats.sent_mcast_bytes);
+               stats->tx_bcast_bytes +=
+                       HILO_64_REGPAIR(pstats.sent_bcast_bytes);
+               stats->tx_ucast_pkts +=
+                       HILO_64_REGPAIR(pstats.sent_ucast_pkts);
+               stats->tx_mcast_pkts +=
+                       HILO_64_REGPAIR(pstats.sent_mcast_pkts);
+               stats->tx_bcast_pkts +=
+                       HILO_64_REGPAIR(pstats.sent_bcast_pkts);
+               stats->tx_err_drop_pkts +=
+                       HILO_64_REGPAIR(pstats.error_drop_pkts);
+               stats->rx_64_byte_packets       += port_stats.pmm.r64;
+               stats->rx_127_byte_packets      += port_stats.pmm.r127;
+               stats->rx_255_byte_packets      += port_stats.pmm.r255;
+               stats->rx_511_byte_packets      += port_stats.pmm.r511;
+               stats->rx_1023_byte_packets     += port_stats.pmm.r1023;
+               stats->rx_1518_byte_packets     += port_stats.pmm.r1518;
+               stats->rx_1522_byte_packets     += port_stats.pmm.r1522;
+               stats->rx_2047_byte_packets     += port_stats.pmm.r2047;
+               stats->rx_4095_byte_packets     += port_stats.pmm.r4095;
+               stats->rx_9216_byte_packets     += port_stats.pmm.r9216;
+               stats->rx_16383_byte_packets    += port_stats.pmm.r16383;
+               stats->rx_crc_errors        += port_stats.pmm.rfcs;
+               stats->rx_mac_crtl_frames       += port_stats.pmm.rxcf;
+               stats->rx_pause_frames    += port_stats.pmm.rxpf;
+               stats->rx_pfc_frames        += port_stats.pmm.rxpp;
+               stats->rx_align_errors    += port_stats.pmm.raln;
+               stats->rx_carrier_errors        += port_stats.pmm.rfcr;
+               stats->rx_oversize_packets      += port_stats.pmm.rovr;
+               stats->rx_jabbers              += port_stats.pmm.rjbr;
+               stats->rx_undersize_packets     += port_stats.pmm.rund;
+               stats->rx_fragments          += port_stats.pmm.rfrg;
+               stats->tx_64_byte_packets       += port_stats.pmm.t64;
+               stats->tx_65_to_127_byte_packets += port_stats.pmm.t127;
+               stats->tx_128_to_255_byte_packets += port_stats.pmm.t255;
+               stats->tx_256_to_511_byte_packets  += port_stats.pmm.t511;
+               stats->tx_512_to_1023_byte_packets += port_stats.pmm.t1023;
+               stats->tx_1024_to_1518_byte_packets += port_stats.pmm.t1518;
+               stats->tx_1519_to_2047_byte_packets += port_stats.pmm.t2047;
+               stats->tx_2048_to_4095_byte_packets += port_stats.pmm.t4095;
+               stats->tx_4096_to_9216_byte_packets += port_stats.pmm.t9216;
+               stats->tx_9217_to_16383_byte_packets += port_stats.pmm.t16383;
+               stats->tx_pause_frames    += port_stats.pmm.txpf;
+               stats->tx_pfc_frames        += port_stats.pmm.txpp;
+               stats->tx_lpi_entry_count       += port_stats.pmm.tlpiec;
+               stats->tx_total_collisions      += port_stats.pmm.tncl;
+               stats->rx_mac_bytes          += port_stats.pmm.rbyte;
+               stats->rx_mac_uc_packets        += port_stats.pmm.rxuca;
+               stats->rx_mac_mc_packets        += port_stats.pmm.rxmca;
+               stats->rx_mac_bc_packets        += port_stats.pmm.rxbca;
+               stats->rx_mac_frames_ok  += port_stats.pmm.rxpok;
+               stats->tx_mac_bytes          += port_stats.pmm.tbyte;
+               stats->tx_mac_uc_packets        += port_stats.pmm.txuca;
+               stats->tx_mac_mc_packets        += port_stats.pmm.txmca;
+               stats->tx_mac_bc_packets        += port_stats.pmm.txbca;
+               stats->tx_mac_ctrl_frames       += port_stats.pmm.txcf;
+
+               for (j = 0; j < 8; j++) {
+                       stats->brb_truncates += port_stats.brb.brb_truncate[j];
+                       stats->brb_discards += port_stats.brb.brb_discard[j];
+               }
+       }
+}
+
+void qed_get_vport_stats(struct qed_dev *cdev,
+                        struct qed_eth_stats *stats)
+{
+       u32 i;
+
+       if (!cdev) {
+               memset(stats, 0, sizeof(*stats));
+               return;
+       }
+
+       __qed_get_vport_stats(cdev, stats);
+
+       if (!cdev->reset_stats)
+               return;
+
+       /* Reduce the statistics baseline */
+       for (i = 0; i < sizeof(struct qed_eth_stats) / sizeof(u64); i++)
+               ((u64 *)stats)[i] -= ((u64 *)cdev->reset_stats)[i];
+}
+
+/* zeroes V-PORT specific portion of stats (Port stats remains untouched) */
+void qed_reset_vport_stats(struct qed_dev *cdev)
+{
+       int i;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+               struct eth_mstorm_per_queue_stat mstats;
+               struct eth_ustorm_per_queue_stat ustats;
+               struct eth_pstorm_per_queue_stat pstats;
+               struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
+
+               if (!p_ptt) {
+                       DP_ERR(p_hwfn, "Failed to acquire ptt\n");
+                       continue;
+               }
+
+               memset(&mstats, 0, sizeof(mstats));
+               qed_memcpy_to(p_hwfn, p_ptt,
+                             p_hwfn->storm_stats.mstats.address,
+                             &mstats,
+                             p_hwfn->storm_stats.mstats.len);
+
+               memset(&ustats, 0, sizeof(ustats));
+               qed_memcpy_to(p_hwfn, p_ptt,
+                             p_hwfn->storm_stats.ustats.address,
+                             &ustats,
+                             p_hwfn->storm_stats.ustats.len);
+
+               memset(&pstats, 0, sizeof(pstats));
+               qed_memcpy_to(p_hwfn, p_ptt,
+                             p_hwfn->storm_stats.pstats.address,
+                             &pstats,
+                             p_hwfn->storm_stats.pstats.len);
+
+               qed_ptt_release(p_hwfn, p_ptt);
+       }
+
+       /* PORT statistics are not necessarily reset, so we need to
+        * read and create a baseline for future statistics.
+        */
+       if (!cdev->reset_stats)
+               DP_INFO(cdev, "Reset stats not allocated\n");
+       else
+               __qed_get_vport_stats(cdev, cdev->reset_stats);
+}
+
+int qed_fw_l2_queue(struct qed_hwfn *p_hwfn,
+                   u16 src_id, u16 *dst_id)
+{
+       if (src_id >= RESC_NUM(p_hwfn, QED_L2_QUEUE)) {
+               u16 min, max;
+
+               min = (u16)RESC_START(p_hwfn, QED_L2_QUEUE);
+               max = min + RESC_NUM(p_hwfn, QED_L2_QUEUE);
+               DP_NOTICE(p_hwfn,
+                         "l2_queue id [%d] is not valid, available indices [%d - %d]\n",
+                         src_id, min, max);
+
+               return -EINVAL;
+       }
+
+       *dst_id = RESC_START(p_hwfn, QED_L2_QUEUE) + src_id;
+
+       return 0;
+}
+
+int qed_fw_vport(struct qed_hwfn *p_hwfn,
+                u8 src_id, u8 *dst_id)
+{
+       if (src_id >= RESC_NUM(p_hwfn, QED_VPORT)) {
+               u8 min, max;
+
+               min = (u8)RESC_START(p_hwfn, QED_VPORT);
+               max = min + RESC_NUM(p_hwfn, QED_VPORT);
+               DP_NOTICE(p_hwfn,
+                         "vport id [%d] is not valid, available indices [%d - %d]\n",
+                         src_id, min, max);
+
+               return -EINVAL;
+       }
+
+       *dst_id = RESC_START(p_hwfn, QED_VPORT) + src_id;
+
+       return 0;
+}
+
+int qed_fw_rss_eng(struct qed_hwfn *p_hwfn,
+                  u8 src_id, u8 *dst_id)
+{
+       if (src_id >= RESC_NUM(p_hwfn, QED_RSS_ENG)) {
+               u8 min, max;
+
+               min = (u8)RESC_START(p_hwfn, QED_RSS_ENG);
+               max = min + RESC_NUM(p_hwfn, QED_RSS_ENG);
+               DP_NOTICE(p_hwfn,
+                         "rss_eng id [%d] is not valid, available indices [%d - %d]\n",
+                         src_id, min, max);
+
+               return -EINVAL;
+       }
+
+       *dst_id = RESC_START(p_hwfn, QED_RSS_ENG) + src_id;
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h
new file mode 100644 (file)
index 0000000..e29a3ba
--- /dev/null
@@ -0,0 +1,283 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_DEV_API_H
+#define _QED_DEV_API_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/qed/qed_chain.h>
+#include <linux/qed/qed_if.h>
+#include "qed_int.h"
+
+/**
+ * @brief qed_init_dp - initialize the debug level
+ *
+ * @param cdev
+ * @param dp_module
+ * @param dp_level
+ */
+void qed_init_dp(struct qed_dev *cdev,
+                u32 dp_module,
+                u8 dp_level);
+
+/**
+ * @brief qed_init_struct - initialize the device structure to
+ *        its defaults
+ *
+ * @param cdev
+ */
+void qed_init_struct(struct qed_dev *cdev);
+
+/**
+ * @brief qed_resc_free -
+ *
+ * @param cdev
+ */
+void qed_resc_free(struct qed_dev *cdev);
+
+/**
+ * @brief qed_resc_alloc -
+ *
+ * @param cdev
+ *
+ * @return int
+ */
+int qed_resc_alloc(struct qed_dev *cdev);
+
+/**
+ * @brief qed_resc_setup -
+ *
+ * @param cdev
+ */
+void qed_resc_setup(struct qed_dev *cdev);
+
+/**
+ * @brief qed_hw_init -
+ *
+ * @param cdev
+ * @param b_hw_start
+ * @param int_mode - interrupt mode [msix, inta, etc.] to use.
+ * @param allow_npar_tx_switch - npar tx switching to be used
+ *       for vports configured for tx-switching.
+ * @param bin_fw_data - binary fw data pointer in binary fw file.
+ *                     Pass NULL if not using binary fw file.
+ *
+ * @return int
+ */
+int qed_hw_init(struct qed_dev *cdev,
+               bool b_hw_start,
+               enum qed_int_mode int_mode,
+               bool allow_npar_tx_switch,
+               const u8 *bin_fw_data);
+
+/**
+ * @brief qed_hw_stop -
+ *
+ * @param cdev
+ *
+ * @return int
+ */
+int qed_hw_stop(struct qed_dev *cdev);
+
+/**
+ * @brief qed_hw_stop_fastpath -should be called incase
+ *             slowpath is still required for the device,
+ *             but fastpath is not.
+ *
+ * @param cdev
+ *
+ */
+void qed_hw_stop_fastpath(struct qed_dev *cdev);
+
+/**
+ * @brief qed_hw_start_fastpath -restart fastpath traffic,
+ *             only if hw_stop_fastpath was called
+ *
+ * @param cdev
+ *
+ */
+void qed_hw_start_fastpath(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_hw_reset -
+ *
+ * @param cdev
+ *
+ * @return int
+ */
+int qed_hw_reset(struct qed_dev *cdev);
+
+/**
+ * @brief qed_hw_prepare -
+ *
+ * @param cdev
+ * @param personality - personality to initialize
+ *
+ * @return int
+ */
+int qed_hw_prepare(struct qed_dev *cdev,
+                  int personality);
+
+/**
+ * @brief qed_hw_remove -
+ *
+ * @param cdev
+ */
+void qed_hw_remove(struct qed_dev *cdev);
+
+/**
+ * @brief qed_ptt_acquire - Allocate a PTT window
+ *
+ * Should be called at the entry point to the driver (at the beginning of an
+ * exported function)
+ *
+ * @param p_hwfn
+ *
+ * @return struct qed_ptt
+ */
+struct qed_ptt *qed_ptt_acquire(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_ptt_release - Release PTT Window
+ *
+ * Should be called at the end of a flow - at the end of the function that
+ * acquired the PTT.
+ *
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ */
+void qed_ptt_release(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt);
+void qed_get_vport_stats(struct qed_dev *cdev,
+                        struct qed_eth_stats   *stats);
+void qed_reset_vport_stats(struct qed_dev *cdev);
+
+enum qed_dmae_address_type_t {
+       QED_DMAE_ADDRESS_HOST_VIRT,
+       QED_DMAE_ADDRESS_HOST_PHYS,
+       QED_DMAE_ADDRESS_GRC
+};
+
+/* value of flags If QED_DMAE_FLAG_RW_REPL_SRC flag is set and the
+ * source is a block of length DMAE_MAX_RW_SIZE and the
+ * destination is larger, the source block will be duplicated as
+ * many times as required to fill the destination block. This is
+ * used mostly to write a zeroed buffer to destination address
+ * using DMA
+ */
+#define QED_DMAE_FLAG_RW_REPL_SRC       0x00000001
+#define QED_DMAE_FLAG_COMPLETION_DST    0x00000008
+
+struct qed_dmae_params {
+       u32     flags; /* consists of QED_DMAE_FLAG_* values */
+};
+
+/**
+ * @brief qed_dmae_host2grc - copy data from source addr to
+ * dmae registers using the given ptt
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param source_addr
+ * @param grc_addr (dmae_data_offset)
+ * @param size_in_dwords
+ * @param flags (one of the flags defined above)
+ */
+int
+qed_dmae_host2grc(struct qed_hwfn *p_hwfn,
+                 struct qed_ptt *p_ptt,
+                 u64 source_addr,
+                 u32 grc_addr,
+                 u32 size_in_dwords,
+                 u32 flags);
+
+/**
+ * @brief qed_chain_alloc - Allocate and initialize a chain
+ *
+ * @param p_hwfn
+ * @param intended_use
+ * @param mode
+ * @param num_elems
+ * @param elem_size
+ * @param p_chain
+ *
+ * @return int
+ */
+int
+qed_chain_alloc(struct qed_dev *cdev,
+               enum qed_chain_use_mode intended_use,
+               enum qed_chain_mode mode,
+               u16 num_elems,
+               size_t elem_size,
+               struct qed_chain *p_chain);
+
+/**
+ * @brief qed_chain_free - Free chain DMA memory
+ *
+ * @param p_hwfn
+ * @param p_chain
+ */
+void qed_chain_free(struct qed_dev *cdev,
+                   struct qed_chain *p_chain);
+
+/**
+ * @@brief qed_fw_l2_queue - Get absolute L2 queue ID
+ *
+ *  @param p_hwfn
+ *  @param src_id - relative to p_hwfn
+ *  @param dst_id - absolute per engine
+ *
+ *  @return int
+ */
+int qed_fw_l2_queue(struct qed_hwfn *p_hwfn,
+                   u16 src_id,
+                   u16 *dst_id);
+
+/**
+ * @@brief qed_fw_vport - Get absolute vport ID
+ *
+ *  @param p_hwfn
+ *  @param src_id - relative to p_hwfn
+ *  @param dst_id - absolute per engine
+ *
+ *  @return int
+ */
+int qed_fw_vport(struct qed_hwfn *p_hwfn,
+                u8 src_id,
+                u8 *dst_id);
+
+/**
+ * @@brief qed_fw_rss_eng - Get absolute RSS engine ID
+ *
+ *  @param p_hwfn
+ *  @param src_id - relative to p_hwfn
+ *  @param dst_id - absolute per engine
+ *
+ *  @return int
+ */
+int qed_fw_rss_eng(struct qed_hwfn *p_hwfn,
+                  u8 src_id,
+                  u8 *dst_id);
+
+/**
+ * *@brief Cleanup of previous driver remains prior to load
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param id - For PF, engine-relative. For VF, PF-relative.
+ *
+ * @return int
+ */
+int qed_final_cleanup(struct qed_hwfn *p_hwfn,
+                     struct qed_ptt *p_ptt,
+                     u16 id);
+
+#endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
new file mode 100644 (file)
index 0000000..b2f8e85
--- /dev/null
@@ -0,0 +1,5291 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_HSI_H
+#define _QED_HSI_H
+
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/qed/common_hsi.h>
+#include <linux/qed/eth_common.h>
+
+struct qed_hwfn;
+struct qed_ptt;
+/********************************/
+/* Add include to common target */
+/********************************/
+
+/* opcodes for the event ring */
+enum common_event_opcode {
+       COMMON_EVENT_PF_START,
+       COMMON_EVENT_PF_STOP,
+       COMMON_EVENT_RESERVED,
+       COMMON_EVENT_RESERVED2,
+       COMMON_EVENT_RESERVED3,
+       COMMON_EVENT_RESERVED4,
+       COMMON_EVENT_RESERVED5,
+       MAX_COMMON_EVENT_OPCODE
+};
+
+/* Common Ramrod Command IDs */
+enum common_ramrod_cmd_id {
+       COMMON_RAMROD_UNUSED,
+       COMMON_RAMROD_PF_START /* PF Function Start Ramrod */,
+       COMMON_RAMROD_PF_STOP /* PF Function Stop Ramrod */,
+       COMMON_RAMROD_RESERVED,
+       COMMON_RAMROD_RESERVED2,
+       COMMON_RAMROD_RESERVED3,
+       MAX_COMMON_RAMROD_CMD_ID
+};
+
+/* The core storm context for the Ystorm */
+struct ystorm_core_conn_st_ctx {
+       __le32 reserved[4];
+};
+
+/* The core storm context for the Pstorm */
+struct pstorm_core_conn_st_ctx {
+       __le32 reserved[4];
+};
+
+/* Core Slowpath Connection storm context of Xstorm */
+struct xstorm_core_conn_st_ctx {
+       __le32          spq_base_lo /* SPQ Ring Base Address low dword */;
+       __le32          spq_base_hi /* SPQ Ring Base Address high dword */;
+       struct regpair  consolid_base_addr;
+       __le16          spq_cons /* SPQ Ring Consumer */;
+       __le16          consolid_cons /* Consolidation Ring Consumer */;
+       __le32          reserved0[55] /* Pad to 15 cycles */;
+};
+
+struct xstorm_core_conn_ag_ctx {
+       u8      reserved0 /* cdu_validation */;
+       u8      core_state /* state */;
+       u8      flags0;
+#define XSTORM_CORE_CONN_AG_CTX_EXIST_IN_QM0_MASK         0x1
+#define XSTORM_CORE_CONN_AG_CTX_EXIST_IN_QM0_SHIFT        0
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED1_MASK            0x1
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED1_SHIFT           1
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED2_MASK            0x1
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED2_SHIFT           2
+#define XSTORM_CORE_CONN_AG_CTX_EXIST_IN_QM3_MASK         0x1
+#define XSTORM_CORE_CONN_AG_CTX_EXIST_IN_QM3_SHIFT        3
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED3_MASK            0x1
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED3_SHIFT           4
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED4_MASK            0x1
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED4_SHIFT           5
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED5_MASK            0x1   /* bit6 */
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED5_SHIFT           6
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED6_MASK            0x1   /* bit7 */
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED6_SHIFT           7
+       u8 flags1;
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED7_MASK            0x1   /* bit8 */
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED7_SHIFT           0
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED8_MASK            0x1   /* bit9 */
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED8_SHIFT           1
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED9_MASK            0x1   /* bit10 */
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED9_SHIFT           2
+#define XSTORM_CORE_CONN_AG_CTX_BIT11_MASK                0x1   /* bit11 */
+#define XSTORM_CORE_CONN_AG_CTX_BIT11_SHIFT               3
+#define XSTORM_CORE_CONN_AG_CTX_BIT12_MASK                0x1   /* bit12 */
+#define XSTORM_CORE_CONN_AG_CTX_BIT12_SHIFT               4
+#define XSTORM_CORE_CONN_AG_CTX_BIT13_MASK                0x1   /* bit13 */
+#define XSTORM_CORE_CONN_AG_CTX_BIT13_SHIFT               5
+#define XSTORM_CORE_CONN_AG_CTX_TX_RULE_ACTIVE_MASK       0x1   /* bit14 */
+#define XSTORM_CORE_CONN_AG_CTX_TX_RULE_ACTIVE_SHIFT      6
+#define XSTORM_CORE_CONN_AG_CTX_DQ_CF_ACTIVE_MASK         0x1   /* bit15 */
+#define XSTORM_CORE_CONN_AG_CTX_DQ_CF_ACTIVE_SHIFT        7
+       u8 flags2;
+#define XSTORM_CORE_CONN_AG_CTX_CF0_MASK                  0x3   /* timer0cf */
+#define XSTORM_CORE_CONN_AG_CTX_CF0_SHIFT                 0
+#define XSTORM_CORE_CONN_AG_CTX_CF1_MASK                  0x3   /* timer1cf */
+#define XSTORM_CORE_CONN_AG_CTX_CF1_SHIFT                 2
+#define XSTORM_CORE_CONN_AG_CTX_CF2_MASK                  0x3   /* timer2cf */
+#define XSTORM_CORE_CONN_AG_CTX_CF2_SHIFT                 4
+#define XSTORM_CORE_CONN_AG_CTX_CF3_MASK                  0x3
+#define XSTORM_CORE_CONN_AG_CTX_CF3_SHIFT                 6
+       u8 flags3;
+#define XSTORM_CORE_CONN_AG_CTX_CF4_MASK                  0x3   /* cf4 */
+#define XSTORM_CORE_CONN_AG_CTX_CF4_SHIFT                 0
+#define XSTORM_CORE_CONN_AG_CTX_CF5_MASK                  0x3   /* cf5 */
+#define XSTORM_CORE_CONN_AG_CTX_CF5_SHIFT                 2
+#define XSTORM_CORE_CONN_AG_CTX_CF6_MASK                  0x3   /* cf6 */
+#define XSTORM_CORE_CONN_AG_CTX_CF6_SHIFT                 4
+#define XSTORM_CORE_CONN_AG_CTX_CF7_MASK                  0x3   /* cf7 */
+#define XSTORM_CORE_CONN_AG_CTX_CF7_SHIFT                 6
+       u8 flags4;
+#define XSTORM_CORE_CONN_AG_CTX_CF8_MASK                  0x3   /* cf8 */
+#define XSTORM_CORE_CONN_AG_CTX_CF8_SHIFT                 0
+#define XSTORM_CORE_CONN_AG_CTX_CF9_MASK                  0x3   /* cf9 */
+#define XSTORM_CORE_CONN_AG_CTX_CF9_SHIFT                 2
+#define XSTORM_CORE_CONN_AG_CTX_CF10_MASK                 0x3   /* cf10 */
+#define XSTORM_CORE_CONN_AG_CTX_CF10_SHIFT                4
+#define XSTORM_CORE_CONN_AG_CTX_CF11_MASK                 0x3   /* cf11 */
+#define XSTORM_CORE_CONN_AG_CTX_CF11_SHIFT                6
+       u8 flags5;
+#define XSTORM_CORE_CONN_AG_CTX_CF12_MASK                 0x3   /* cf12 */
+#define XSTORM_CORE_CONN_AG_CTX_CF12_SHIFT                0
+#define XSTORM_CORE_CONN_AG_CTX_CF13_MASK                 0x3   /* cf13 */
+#define XSTORM_CORE_CONN_AG_CTX_CF13_SHIFT                2
+#define XSTORM_CORE_CONN_AG_CTX_CF14_MASK                 0x3   /* cf14 */
+#define XSTORM_CORE_CONN_AG_CTX_CF14_SHIFT                4
+#define XSTORM_CORE_CONN_AG_CTX_CF15_MASK                 0x3   /* cf15 */
+#define XSTORM_CORE_CONN_AG_CTX_CF15_SHIFT                6
+       u8 flags6;
+#define XSTORM_CORE_CONN_AG_CTX_CONSOLID_PROD_CF_MASK     0x3   /* cf16 */
+#define XSTORM_CORE_CONN_AG_CTX_CONSOLID_PROD_CF_SHIFT    0
+#define XSTORM_CORE_CONN_AG_CTX_CF17_MASK                 0x3
+#define XSTORM_CORE_CONN_AG_CTX_CF17_SHIFT                2
+#define XSTORM_CORE_CONN_AG_CTX_DQ_CF_MASK                0x3   /* cf18 */
+#define XSTORM_CORE_CONN_AG_CTX_DQ_CF_SHIFT               4
+#define XSTORM_CORE_CONN_AG_CTX_TERMINATE_CF_MASK         0x3   /* cf19 */
+#define XSTORM_CORE_CONN_AG_CTX_TERMINATE_CF_SHIFT        6
+       u8 flags7;
+#define XSTORM_CORE_CONN_AG_CTX_FLUSH_Q0_MASK             0x3   /* cf20 */
+#define XSTORM_CORE_CONN_AG_CTX_FLUSH_Q0_SHIFT            0
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED10_MASK           0x3   /* cf21 */
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED10_SHIFT          2
+#define XSTORM_CORE_CONN_AG_CTX_SLOW_PATH_MASK            0x3   /* cf22 */
+#define XSTORM_CORE_CONN_AG_CTX_SLOW_PATH_SHIFT           4
+#define XSTORM_CORE_CONN_AG_CTX_CF0EN_MASK                0x1   /* cf0en */
+#define XSTORM_CORE_CONN_AG_CTX_CF0EN_SHIFT               6
+#define XSTORM_CORE_CONN_AG_CTX_CF1EN_MASK                0x1   /* cf1en */
+#define XSTORM_CORE_CONN_AG_CTX_CF1EN_SHIFT               7
+       u8 flags8;
+#define XSTORM_CORE_CONN_AG_CTX_CF2EN_MASK                0x1   /* cf2en */
+#define XSTORM_CORE_CONN_AG_CTX_CF2EN_SHIFT               0
+#define XSTORM_CORE_CONN_AG_CTX_CF3EN_MASK                0x1   /* cf3en */
+#define XSTORM_CORE_CONN_AG_CTX_CF3EN_SHIFT               1
+#define XSTORM_CORE_CONN_AG_CTX_CF4EN_MASK                0x1   /* cf4en */
+#define XSTORM_CORE_CONN_AG_CTX_CF4EN_SHIFT               2
+#define XSTORM_CORE_CONN_AG_CTX_CF5EN_MASK                0x1   /* cf5en */
+#define XSTORM_CORE_CONN_AG_CTX_CF5EN_SHIFT               3
+#define XSTORM_CORE_CONN_AG_CTX_CF6EN_MASK                0x1   /* cf6en */
+#define XSTORM_CORE_CONN_AG_CTX_CF6EN_SHIFT               4
+#define XSTORM_CORE_CONN_AG_CTX_CF7EN_MASK                0x1   /* cf7en */
+#define XSTORM_CORE_CONN_AG_CTX_CF7EN_SHIFT               5
+#define XSTORM_CORE_CONN_AG_CTX_CF8EN_MASK                0x1   /* cf8en */
+#define XSTORM_CORE_CONN_AG_CTX_CF8EN_SHIFT               6
+#define XSTORM_CORE_CONN_AG_CTX_CF9EN_MASK                0x1   /* cf9en */
+#define XSTORM_CORE_CONN_AG_CTX_CF9EN_SHIFT               7
+       u8 flags9;
+#define XSTORM_CORE_CONN_AG_CTX_CF10EN_MASK               0x1   /* cf10en */
+#define XSTORM_CORE_CONN_AG_CTX_CF10EN_SHIFT              0
+#define XSTORM_CORE_CONN_AG_CTX_CF11EN_MASK               0x1   /* cf11en */
+#define XSTORM_CORE_CONN_AG_CTX_CF11EN_SHIFT              1
+#define XSTORM_CORE_CONN_AG_CTX_CF12EN_MASK               0x1   /* cf12en */
+#define XSTORM_CORE_CONN_AG_CTX_CF12EN_SHIFT              2
+#define XSTORM_CORE_CONN_AG_CTX_CF13EN_MASK               0x1   /* cf13en */
+#define XSTORM_CORE_CONN_AG_CTX_CF13EN_SHIFT              3
+#define XSTORM_CORE_CONN_AG_CTX_CF14EN_MASK               0x1   /* cf14en */
+#define XSTORM_CORE_CONN_AG_CTX_CF14EN_SHIFT              4
+#define XSTORM_CORE_CONN_AG_CTX_CF15EN_MASK               0x1   /* cf15en */
+#define XSTORM_CORE_CONN_AG_CTX_CF15EN_SHIFT              5
+#define XSTORM_CORE_CONN_AG_CTX_CONSOLID_PROD_CF_EN_MASK  0x1   /* cf16en */
+#define XSTORM_CORE_CONN_AG_CTX_CONSOLID_PROD_CF_EN_SHIFT 6
+#define XSTORM_CORE_CONN_AG_CTX_CF17EN_MASK               0x1
+#define XSTORM_CORE_CONN_AG_CTX_CF17EN_SHIFT              7
+       u8 flags10;
+#define XSTORM_CORE_CONN_AG_CTX_DQ_CF_EN_MASK             0x1   /* cf18en */
+#define XSTORM_CORE_CONN_AG_CTX_DQ_CF_EN_SHIFT            0
+#define XSTORM_CORE_CONN_AG_CTX_TERMINATE_CF_EN_MASK      0x1   /* cf19en */
+#define XSTORM_CORE_CONN_AG_CTX_TERMINATE_CF_EN_SHIFT     1
+#define XSTORM_CORE_CONN_AG_CTX_FLUSH_Q0_EN_MASK          0x1   /* cf20en */
+#define XSTORM_CORE_CONN_AG_CTX_FLUSH_Q0_EN_SHIFT         2
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED11_MASK           0x1   /* cf21en */
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED11_SHIFT          3
+#define XSTORM_CORE_CONN_AG_CTX_SLOW_PATH_EN_MASK         0x1   /* cf22en */
+#define XSTORM_CORE_CONN_AG_CTX_SLOW_PATH_EN_SHIFT        4
+#define XSTORM_CORE_CONN_AG_CTX_CF23EN_MASK               0x1   /* cf23en */
+#define XSTORM_CORE_CONN_AG_CTX_CF23EN_SHIFT              5
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED12_MASK           0x1   /* rule0en */
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED12_SHIFT          6
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED13_MASK           0x1   /* rule1en */
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED13_SHIFT          7
+       u8 flags11;
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED14_MASK           0x1   /* rule2en */
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED14_SHIFT          0
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED15_MASK           0x1   /* rule3en */
+#define XSTORM_CORE_CONN_AG_CTX_RESERVED15_SHIFT          1
+#define XSTORM_CORE_CONN_AG_CTX_TX_DEC_RULE_EN_MASK       0x1   /* rule4en */
+#define XSTORM_CORE_CONN_AG_CTX_TX_DEC_RULE_EN_SHIFT      2
+#define XSTORM_CORE_CONN_AG_CTX_RULE5EN_MASK              0x1   /* rule5en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE5EN_SHIFT             3
+#define XSTORM_CORE_CONN_AG_CTX_RULE6EN_MASK              0x1   /* rule6en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE6EN_SHIFT             4
+#define XSTORM_CORE_CONN_AG_CTX_RULE7EN_MASK              0x1   /* rule7en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE7EN_SHIFT             5
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED1_MASK         0x1   /* rule8en */
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED1_SHIFT        6
+#define XSTORM_CORE_CONN_AG_CTX_RULE9EN_MASK              0x1   /* rule9en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE9EN_SHIFT             7
+       u8 flags12;
+#define XSTORM_CORE_CONN_AG_CTX_RULE10EN_MASK             0x1   /* rule10en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE10EN_SHIFT            0
+#define XSTORM_CORE_CONN_AG_CTX_RULE11EN_MASK             0x1   /* rule11en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE11EN_SHIFT            1
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED2_MASK         0x1   /* rule12en */
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED2_SHIFT        2
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED3_MASK         0x1   /* rule13en */
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED3_SHIFT        3
+#define XSTORM_CORE_CONN_AG_CTX_RULE14EN_MASK             0x1   /* rule14en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE14EN_SHIFT            4
+#define XSTORM_CORE_CONN_AG_CTX_RULE15EN_MASK             0x1   /* rule15en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE15EN_SHIFT            5
+#define XSTORM_CORE_CONN_AG_CTX_RULE16EN_MASK             0x1   /* rule16en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE16EN_SHIFT            6
+#define XSTORM_CORE_CONN_AG_CTX_RULE17EN_MASK             0x1   /* rule17en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE17EN_SHIFT            7
+       u8 flags13;
+#define XSTORM_CORE_CONN_AG_CTX_RULE18EN_MASK             0x1   /* rule18en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE18EN_SHIFT            0
+#define XSTORM_CORE_CONN_AG_CTX_RULE19EN_MASK             0x1   /* rule19en */
+#define XSTORM_CORE_CONN_AG_CTX_RULE19EN_SHIFT            1
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED4_MASK         0x1   /* rule20en */
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED4_SHIFT        2
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED5_MASK         0x1   /* rule21en */
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED5_SHIFT        3
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED6_MASK         0x1   /* rule22en */
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED6_SHIFT        4
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED7_MASK         0x1   /* rule23en */
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED7_SHIFT        5
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED8_MASK         0x1   /* rule24en */
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED8_SHIFT        6
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED9_MASK         0x1   /* rule25en */
+#define XSTORM_CORE_CONN_AG_CTX_A0_RESERVED9_SHIFT        7
+       u8 flags14;
+#define XSTORM_CORE_CONN_AG_CTX_BIT16_MASK                0x1   /* bit16 */
+#define XSTORM_CORE_CONN_AG_CTX_BIT16_SHIFT               0
+#define XSTORM_CORE_CONN_AG_CTX_BIT17_MASK                0x1   /* bit17 */
+#define XSTORM_CORE_CONN_AG_CTX_BIT17_SHIFT               1
+#define XSTORM_CORE_CONN_AG_CTX_BIT18_MASK                0x1   /* bit18 */
+#define XSTORM_CORE_CONN_AG_CTX_BIT18_SHIFT               2
+#define XSTORM_CORE_CONN_AG_CTX_BIT19_MASK                0x1   /* bit19 */
+#define XSTORM_CORE_CONN_AG_CTX_BIT19_SHIFT               3
+#define XSTORM_CORE_CONN_AG_CTX_BIT20_MASK                0x1   /* bit20 */
+#define XSTORM_CORE_CONN_AG_CTX_BIT20_SHIFT               4
+#define XSTORM_CORE_CONN_AG_CTX_BIT21_MASK                0x1   /* bit21 */
+#define XSTORM_CORE_CONN_AG_CTX_BIT21_SHIFT               5
+#define XSTORM_CORE_CONN_AG_CTX_CF23_MASK                 0x3   /* cf23 */
+#define XSTORM_CORE_CONN_AG_CTX_CF23_SHIFT                6
+       u8      byte2 /* byte2 */;
+       __le16  physical_q0 /* physical_q0 */;
+       __le16  consolid_prod /* physical_q1 */;
+       __le16  reserved16 /* physical_q2 */;
+       __le16  tx_bd_cons /* word3 */;
+       __le16  tx_bd_or_spq_prod /* word4 */;
+       __le16  word5 /* word5 */;
+       __le16  conn_dpi /* conn_dpi */;
+       u8      byte3 /* byte3 */;
+       u8      byte4 /* byte4 */;
+       u8      byte5 /* byte5 */;
+       u8      byte6 /* byte6 */;
+       __le32  reg0 /* reg0 */;
+       __le32  reg1 /* reg1 */;
+       __le32  reg2 /* reg2 */;
+       __le32  reg3 /* reg3 */;
+       __le32  reg4 /* reg4 */;
+       __le32  reg5 /* cf_array0 */;
+       __le32  reg6 /* cf_array1 */;
+       __le16  word7 /* word7 */;
+       __le16  word8 /* word8 */;
+       __le16  word9 /* word9 */;
+       __le16  word10 /* word10 */;
+       __le32  reg7 /* reg7 */;
+       __le32  reg8 /* reg8 */;
+       __le32  reg9 /* reg9 */;
+       u8      byte7 /* byte7 */;
+       u8      byte8 /* byte8 */;
+       u8      byte9 /* byte9 */;
+       u8      byte10 /* byte10 */;
+       u8      byte11 /* byte11 */;
+       u8      byte12 /* byte12 */;
+       u8      byte13 /* byte13 */;
+       u8      byte14 /* byte14 */;
+       u8      byte15 /* byte15 */;
+       u8      byte16 /* byte16 */;
+       __le16  word11 /* word11 */;
+       __le32  reg10 /* reg10 */;
+       __le32  reg11 /* reg11 */;
+       __le32  reg12 /* reg12 */;
+       __le32  reg13 /* reg13 */;
+       __le32  reg14 /* reg14 */;
+       __le32  reg15 /* reg15 */;
+       __le32  reg16 /* reg16 */;
+       __le32  reg17 /* reg17 */;
+       __le32  reg18 /* reg18 */;
+       __le32  reg19 /* reg19 */;
+       __le16  word12 /* word12 */;
+       __le16  word13 /* word13 */;
+       __le16  word14 /* word14 */;
+       __le16  word15 /* word15 */;
+};
+
+/* The core storm context for the Mstorm */
+struct mstorm_core_conn_st_ctx {
+       __le32 reserved[24];
+};
+
+/* The core storm context for the Ustorm */
+struct ustorm_core_conn_st_ctx {
+       __le32 reserved[4];
+};
+
+/* core connection context */
+struct core_conn_context {
+       struct ystorm_core_conn_st_ctx  ystorm_st_context;
+       struct regpair                  ystorm_st_padding[2] /* padding */;
+       struct pstorm_core_conn_st_ctx  pstorm_st_context;
+       struct regpair                  pstorm_st_padding[2];
+       struct xstorm_core_conn_st_ctx  xstorm_st_context;
+       struct xstorm_core_conn_ag_ctx  xstorm_ag_context;
+       struct mstorm_core_conn_st_ctx  mstorm_st_context;
+       struct regpair                  mstorm_st_padding[2];
+       struct ustorm_core_conn_st_ctx  ustorm_st_context;
+       struct regpair                  ustorm_st_padding[2] /* padding */;
+};
+
+struct eth_mstorm_per_queue_stat {
+       struct regpair  ttl0_discard;
+       struct regpair  packet_too_big_discard;
+       struct regpair  no_buff_discard;
+       struct regpair  not_active_discard;
+       struct regpair  tpa_coalesced_pkts;
+       struct regpair  tpa_coalesced_events;
+       struct regpair  tpa_aborts_num;
+       struct regpair  tpa_coalesced_bytes;
+};
+
+struct eth_pstorm_per_queue_stat {
+       struct regpair  sent_ucast_bytes;
+       struct regpair  sent_mcast_bytes;
+       struct regpair  sent_bcast_bytes;
+       struct regpair  sent_ucast_pkts;
+       struct regpair  sent_mcast_pkts;
+       struct regpair  sent_bcast_pkts;
+       struct regpair  error_drop_pkts;
+};
+
+struct eth_ustorm_per_queue_stat {
+       struct regpair  rcv_ucast_bytes;
+       struct regpair  rcv_mcast_bytes;
+       struct regpair  rcv_bcast_bytes;
+       struct regpair  rcv_ucast_pkts;
+       struct regpair  rcv_mcast_pkts;
+       struct regpair  rcv_bcast_pkts;
+};
+
+/* Event Ring Next Page Address */
+struct event_ring_next_addr {
+       struct regpair  addr /* Next Page Address */;
+       __le32          reserved[2] /* Reserved */;
+};
+
+union event_ring_element {
+       struct event_ring_entry         entry /* Event Ring Entry */;
+       struct event_ring_next_addr     next_addr;
+};
+
+enum personality_type {
+       PERSONALITY_RESERVED,
+       PERSONALITY_RESERVED2,
+       PERSONALITY_RDMA_AND_ETH /* Roce or Iwarp */,
+       PERSONALITY_RESERVED3,
+       PERSONALITY_ETH /* Ethernet */,
+       PERSONALITY_RESERVED4,
+       MAX_PERSONALITY_TYPE
+};
+
+struct pf_start_tunnel_config {
+       u8      set_vxlan_udp_port_flg;
+       u8      set_geneve_udp_port_flg;
+       u8      tx_enable_vxlan /* If set, enable VXLAN tunnel in TX path. */;
+       u8      tx_enable_l2geneve;
+       u8      tx_enable_ipgeneve;
+       u8      tx_enable_l2gre /* If set, enable l2 GRE tunnel in TX path. */;
+       u8      tx_enable_ipgre /* If set, enable IP GRE tunnel in TX path. */;
+       u8      tunnel_clss_vxlan /* Classification scheme for VXLAN tunnel. */;
+       u8      tunnel_clss_l2geneve;
+       u8      tunnel_clss_ipgeneve;
+       u8      tunnel_clss_l2gre;
+       u8      tunnel_clss_ipgre;
+       __le16  vxlan_udp_port /* VXLAN tunnel UDP destination port. */;
+       __le16  geneve_udp_port /* GENEVE tunnel UDP destination port. */;
+};
+
+/* Ramrod data for PF start ramrod */
+struct pf_start_ramrod_data {
+       struct regpair                  event_ring_pbl_addr;
+       struct regpair                  consolid_q_pbl_addr;
+       struct pf_start_tunnel_config   tunnel_config;
+       __le16                          event_ring_sb_id;
+       u8                              base_vf_id;
+       u8                              num_vfs;
+       u8                              event_ring_num_pages;
+       u8                              event_ring_sb_index;
+       u8                              path_id;
+       u8                              warning_as_error;
+       u8                              dont_log_ramrods;
+       u8                              personality;
+       __le16                          log_type_mask;
+       u8                              mf_mode /* Multi function mode */;
+       u8                              integ_phase /* Integration phase */;
+       u8                              allow_npar_tx_switching;
+       u8                              inner_to_outer_pri_map[8];
+       u8                              pri_map_valid;
+       u32                             outer_tag;
+       u8                              reserved0[4];
+};
+
+enum ports_mode {
+       ENGX2_PORTX1 /* 2 engines x 1 port */,
+       ENGX2_PORTX2 /* 2 engines x 2 ports */,
+       ENGX1_PORTX1 /* 1 engine  x 1 port */,
+       ENGX1_PORTX2 /* 1 engine  x 2 ports */,
+       ENGX1_PORTX4 /* 1 engine  x 4 ports */,
+       MAX_PORTS_MODE
+};
+
+/* Ramrod Header of SPQE */
+struct ramrod_header {
+       __le32  cid /* Slowpath Connection CID */;
+       u8      cmd_id /* Ramrod Cmd (Per Protocol Type) */;
+       u8      protocol_id /* Ramrod Protocol ID */;
+       __le16  echo /* Ramrod echo */;
+};
+
+/* Slowpath Element (SPQE) */
+struct slow_path_element {
+       struct ramrod_header    hdr /* Ramrod Header */;
+       struct regpair          data_ptr;
+};
+
+struct tstorm_per_port_stat {
+       struct regpair  trunc_error_discard;
+       struct regpair  mac_error_discard;
+       struct regpair  mftag_filter_discard;
+       struct regpair  eth_mac_filter_discard;
+       struct regpair  ll2_mac_filter_discard;
+       struct regpair  ll2_conn_disabled_discard;
+       struct regpair  iscsi_irregular_pkt;
+       struct regpair  fcoe_irregular_pkt;
+       struct regpair  roce_irregular_pkt;
+       struct regpair  eth_irregular_pkt;
+       struct regpair  toe_irregular_pkt;
+       struct regpair  preroce_irregular_pkt;
+};
+
+struct atten_status_block {
+       __le32  atten_bits;
+       __le32  atten_ack;
+       __le16  reserved0;
+       __le16  sb_index /* status block running index */;
+       __le32  reserved1;
+};
+
+enum block_addr {
+       GRCBASE_GRC             = 0x50000,
+       GRCBASE_MISCS           = 0x9000,
+       GRCBASE_MISC            = 0x8000,
+       GRCBASE_DBU             = 0xa000,
+       GRCBASE_PGLUE_B         = 0x2a8000,
+       GRCBASE_CNIG            = 0x218000,
+       GRCBASE_CPMU            = 0x30000,
+       GRCBASE_NCSI            = 0x40000,
+       GRCBASE_OPTE            = 0x53000,
+       GRCBASE_BMB             = 0x540000,
+       GRCBASE_PCIE            = 0x54000,
+       GRCBASE_MCP             = 0xe00000,
+       GRCBASE_MCP2            = 0x52000,
+       GRCBASE_PSWHST          = 0x2a0000,
+       GRCBASE_PSWHST2         = 0x29e000,
+       GRCBASE_PSWRD           = 0x29c000,
+       GRCBASE_PSWRD2          = 0x29d000,
+       GRCBASE_PSWWR           = 0x29a000,
+       GRCBASE_PSWWR2          = 0x29b000,
+       GRCBASE_PSWRQ           = 0x280000,
+       GRCBASE_PSWRQ2          = 0x240000,
+       GRCBASE_PGLCS           = 0x0,
+       GRCBASE_PTU             = 0x560000,
+       GRCBASE_DMAE            = 0xc000,
+       GRCBASE_TCM             = 0x1180000,
+       GRCBASE_MCM             = 0x1200000,
+       GRCBASE_UCM             = 0x1280000,
+       GRCBASE_XCM             = 0x1000000,
+       GRCBASE_YCM             = 0x1080000,
+       GRCBASE_PCM             = 0x1100000,
+       GRCBASE_QM              = 0x2f0000,
+       GRCBASE_TM              = 0x2c0000,
+       GRCBASE_DORQ            = 0x100000,
+       GRCBASE_BRB             = 0x340000,
+       GRCBASE_SRC             = 0x238000,
+       GRCBASE_PRS             = 0x1f0000,
+       GRCBASE_TSDM            = 0xfb0000,
+       GRCBASE_MSDM            = 0xfc0000,
+       GRCBASE_USDM            = 0xfd0000,
+       GRCBASE_XSDM            = 0xf80000,
+       GRCBASE_YSDM            = 0xf90000,
+       GRCBASE_PSDM            = 0xfa0000,
+       GRCBASE_TSEM            = 0x1700000,
+       GRCBASE_MSEM            = 0x1800000,
+       GRCBASE_USEM            = 0x1900000,
+       GRCBASE_XSEM            = 0x1400000,
+       GRCBASE_YSEM            = 0x1500000,
+       GRCBASE_PSEM            = 0x1600000,
+       GRCBASE_RSS             = 0x238800,
+       GRCBASE_TMLD            = 0x4d0000,
+       GRCBASE_MULD            = 0x4e0000,
+       GRCBASE_YULD            = 0x4c8000,
+       GRCBASE_XYLD            = 0x4c0000,
+       GRCBASE_PRM             = 0x230000,
+       GRCBASE_PBF_PB1         = 0xda0000,
+       GRCBASE_PBF_PB2         = 0xda4000,
+       GRCBASE_RPB             = 0x23c000,
+       GRCBASE_BTB             = 0xdb0000,
+       GRCBASE_PBF             = 0xd80000,
+       GRCBASE_RDIF            = 0x300000,
+       GRCBASE_TDIF            = 0x310000,
+       GRCBASE_CDU             = 0x580000,
+       GRCBASE_CCFC            = 0x2e0000,
+       GRCBASE_TCFC            = 0x2d0000,
+       GRCBASE_IGU             = 0x180000,
+       GRCBASE_CAU             = 0x1c0000,
+       GRCBASE_UMAC            = 0x51000,
+       GRCBASE_XMAC            = 0x210000,
+       GRCBASE_DBG             = 0x10000,
+       GRCBASE_NIG             = 0x500000,
+       GRCBASE_WOL             = 0x600000,
+       GRCBASE_BMBN            = 0x610000,
+       GRCBASE_IPC             = 0x20000,
+       GRCBASE_NWM             = 0x800000,
+       GRCBASE_NWS             = 0x700000,
+       GRCBASE_MS              = 0x6a0000,
+       GRCBASE_PHY_PCIE        = 0x618000,
+       GRCBASE_MISC_AEU        = 0x8000,
+       GRCBASE_BAR0_MAP        = 0x1c00000,
+       MAX_BLOCK_ADDR
+};
+
+enum block_id {
+       BLOCK_GRC,
+       BLOCK_MISCS,
+       BLOCK_MISC,
+       BLOCK_DBU,
+       BLOCK_PGLUE_B,
+       BLOCK_CNIG,
+       BLOCK_CPMU,
+       BLOCK_NCSI,
+       BLOCK_OPTE,
+       BLOCK_BMB,
+       BLOCK_PCIE,
+       BLOCK_MCP,
+       BLOCK_MCP2,
+       BLOCK_PSWHST,
+       BLOCK_PSWHST2,
+       BLOCK_PSWRD,
+       BLOCK_PSWRD2,
+       BLOCK_PSWWR,
+       BLOCK_PSWWR2,
+       BLOCK_PSWRQ,
+       BLOCK_PSWRQ2,
+       BLOCK_PGLCS,
+       BLOCK_PTU,
+       BLOCK_DMAE,
+       BLOCK_TCM,
+       BLOCK_MCM,
+       BLOCK_UCM,
+       BLOCK_XCM,
+       BLOCK_YCM,
+       BLOCK_PCM,
+       BLOCK_QM,
+       BLOCK_TM,
+       BLOCK_DORQ,
+       BLOCK_BRB,
+       BLOCK_SRC,
+       BLOCK_PRS,
+       BLOCK_TSDM,
+       BLOCK_MSDM,
+       BLOCK_USDM,
+       BLOCK_XSDM,
+       BLOCK_YSDM,
+       BLOCK_PSDM,
+       BLOCK_TSEM,
+       BLOCK_MSEM,
+       BLOCK_USEM,
+       BLOCK_XSEM,
+       BLOCK_YSEM,
+       BLOCK_PSEM,
+       BLOCK_RSS,
+       BLOCK_TMLD,
+       BLOCK_MULD,
+       BLOCK_YULD,
+       BLOCK_XYLD,
+       BLOCK_PRM,
+       BLOCK_PBF_PB1,
+       BLOCK_PBF_PB2,
+       BLOCK_RPB,
+       BLOCK_BTB,
+       BLOCK_PBF,
+       BLOCK_RDIF,
+       BLOCK_TDIF,
+       BLOCK_CDU,
+       BLOCK_CCFC,
+       BLOCK_TCFC,
+       BLOCK_IGU,
+       BLOCK_CAU,
+       BLOCK_UMAC,
+       BLOCK_XMAC,
+       BLOCK_DBG,
+       BLOCK_NIG,
+       BLOCK_WOL,
+       BLOCK_BMBN,
+       BLOCK_IPC,
+       BLOCK_NWM,
+       BLOCK_NWS,
+       BLOCK_MS,
+       BLOCK_PHY_PCIE,
+       BLOCK_MISC_AEU,
+       BLOCK_BAR0_MAP,
+       MAX_BLOCK_ID
+};
+
+enum command_type_bit {
+       IGU_COMMAND_TYPE_NOP    = 0,
+       IGU_COMMAND_TYPE_SET    = 1,
+       MAX_COMMAND_TYPE_BIT
+};
+
+struct dmae_cmd {
+       __le32 opcode;
+#define DMAE_CMD_SRC_MASK              0x1
+#define DMAE_CMD_SRC_SHIFT             0
+#define DMAE_CMD_DST_MASK              0x3
+#define DMAE_CMD_DST_SHIFT             1
+#define DMAE_CMD_C_DST_MASK            0x1
+#define DMAE_CMD_C_DST_SHIFT           3
+#define DMAE_CMD_CRC_RESET_MASK        0x1
+#define DMAE_CMD_CRC_RESET_SHIFT       4
+#define DMAE_CMD_SRC_ADDR_RESET_MASK   0x1
+#define DMAE_CMD_SRC_ADDR_RESET_SHIFT  5
+#define DMAE_CMD_DST_ADDR_RESET_MASK   0x1
+#define DMAE_CMD_DST_ADDR_RESET_SHIFT  6
+#define DMAE_CMD_COMP_FUNC_MASK        0x1
+#define DMAE_CMD_COMP_FUNC_SHIFT       7
+#define DMAE_CMD_COMP_WORD_EN_MASK     0x1
+#define DMAE_CMD_COMP_WORD_EN_SHIFT    8
+#define DMAE_CMD_COMP_CRC_EN_MASK      0x1
+#define DMAE_CMD_COMP_CRC_EN_SHIFT     9
+#define DMAE_CMD_COMP_CRC_OFFSET_MASK  0x7
+#define DMAE_CMD_COMP_CRC_OFFSET_SHIFT 10
+#define DMAE_CMD_RESERVED1_MASK        0x1
+#define DMAE_CMD_RESERVED1_SHIFT       13
+#define DMAE_CMD_ENDIANITY_MODE_MASK   0x3
+#define DMAE_CMD_ENDIANITY_MODE_SHIFT  14
+#define DMAE_CMD_ERR_HANDLING_MASK     0x3
+#define DMAE_CMD_ERR_HANDLING_SHIFT    16
+#define DMAE_CMD_PORT_ID_MASK          0x3
+#define DMAE_CMD_PORT_ID_SHIFT         18
+#define DMAE_CMD_SRC_PF_ID_MASK        0xF
+#define DMAE_CMD_SRC_PF_ID_SHIFT       20
+#define DMAE_CMD_DST_PF_ID_MASK        0xF
+#define DMAE_CMD_DST_PF_ID_SHIFT       24
+#define DMAE_CMD_SRC_VF_ID_VALID_MASK  0x1
+#define DMAE_CMD_SRC_VF_ID_VALID_SHIFT 28
+#define DMAE_CMD_DST_VF_ID_VALID_MASK  0x1
+#define DMAE_CMD_DST_VF_ID_VALID_SHIFT 29
+#define DMAE_CMD_RESERVED2_MASK        0x3
+#define DMAE_CMD_RESERVED2_SHIFT       30
+       __le32  src_addr_lo;
+       __le32  src_addr_hi;
+       __le32  dst_addr_lo;
+       __le32  dst_addr_hi;
+       __le16  length /* Length in DW */;
+       __le16  opcode_b;
+#define DMAE_CMD_SRC_VF_ID_MASK        0xFF     /* Source VF id */
+#define DMAE_CMD_SRC_VF_ID_SHIFT       0
+#define DMAE_CMD_DST_VF_ID_MASK        0xFF     /* Destination VF id */
+#define DMAE_CMD_DST_VF_ID_SHIFT       8
+       __le32  comp_addr_lo /* PCIe completion address low or grc address */;
+       __le32  comp_addr_hi;
+       __le32  comp_val /* Value to write to copmletion address */;
+       __le32  crc32 /* crc16 result */;
+       __le32  crc_32_c /* crc32_c result */;
+       __le16  crc16 /* crc16 result */;
+       __le16  crc16_c /* crc16_c result */;
+       __le16  crc10 /* crc_t10 result */;
+       __le16  reserved;
+       __le16  xsum16 /* checksum16 result  */;
+       __le16  xsum8 /* checksum8 result  */;
+};
+
+struct igu_cleanup {
+       __le32 sb_id_and_flags;
+#define IGU_CLEANUP_RESERVED0_MASK     0x7FFFFFF
+#define IGU_CLEANUP_RESERVED0_SHIFT    0
+#define IGU_CLEANUP_CLEANUP_SET_MASK   0x1 /* cleanup clear - 0, set - 1 */
+#define IGU_CLEANUP_CLEANUP_SET_SHIFT  27
+#define IGU_CLEANUP_CLEANUP_TYPE_MASK  0x7
+#define IGU_CLEANUP_CLEANUP_TYPE_SHIFT 28
+#define IGU_CLEANUP_COMMAND_TYPE_MASK  0x1
+#define IGU_CLEANUP_COMMAND_TYPE_SHIFT 31
+       __le32 reserved1;
+};
+
+union igu_command {
+       struct igu_prod_cons_update     prod_cons_update;
+       struct igu_cleanup              cleanup;
+};
+
+struct igu_command_reg_ctrl {
+       __le16  opaque_fid;
+       __le16  igu_command_reg_ctrl_fields;
+#define IGU_COMMAND_REG_CTRL_PXP_BAR_ADDR_MASK  0xFFF
+#define IGU_COMMAND_REG_CTRL_PXP_BAR_ADDR_SHIFT 0
+#define IGU_COMMAND_REG_CTRL_RESERVED_MASK      0x7
+#define IGU_COMMAND_REG_CTRL_RESERVED_SHIFT     12
+#define IGU_COMMAND_REG_CTRL_COMMAND_TYPE_MASK  0x1
+#define IGU_COMMAND_REG_CTRL_COMMAND_TYPE_SHIFT 15
+};
+
+struct igu_mapping_line {
+       __le32 igu_mapping_line_fields;
+#define IGU_MAPPING_LINE_VALID_MASK            0x1
+#define IGU_MAPPING_LINE_VALID_SHIFT           0
+#define IGU_MAPPING_LINE_VECTOR_NUMBER_MASK    0xFF
+#define IGU_MAPPING_LINE_VECTOR_NUMBER_SHIFT   1
+#define IGU_MAPPING_LINE_FUNCTION_NUMBER_MASK  0xFF
+#define IGU_MAPPING_LINE_FUNCTION_NUMBER_SHIFT 9
+#define IGU_MAPPING_LINE_PF_VALID_MASK         0x1      /* PF-1, VF-0 */
+#define IGU_MAPPING_LINE_PF_VALID_SHIFT        17
+#define IGU_MAPPING_LINE_IPS_GROUP_MASK        0x3F
+#define IGU_MAPPING_LINE_IPS_GROUP_SHIFT       18
+#define IGU_MAPPING_LINE_RESERVED_MASK         0xFF
+#define IGU_MAPPING_LINE_RESERVED_SHIFT        24
+};
+
+struct igu_msix_vector {
+       struct regpair  address;
+       __le32          data;
+       __le32          msix_vector_fields;
+#define IGU_MSIX_VECTOR_MASK_BIT_MASK      0x1
+#define IGU_MSIX_VECTOR_MASK_BIT_SHIFT     0
+#define IGU_MSIX_VECTOR_RESERVED0_MASK     0x7FFF
+#define IGU_MSIX_VECTOR_RESERVED0_SHIFT    1
+#define IGU_MSIX_VECTOR_STEERING_TAG_MASK  0xFF
+#define IGU_MSIX_VECTOR_STEERING_TAG_SHIFT 16
+#define IGU_MSIX_VECTOR_RESERVED1_MASK     0xFF
+#define IGU_MSIX_VECTOR_RESERVED1_SHIFT    24
+};
+
+enum init_modes {
+       MODE_BB_A0,
+       MODE_RESERVED,
+       MODE_RESERVED2,
+       MODE_ASIC,
+       MODE_RESERVED3,
+       MODE_RESERVED4,
+       MODE_RESERVED5,
+       MODE_SF,
+       MODE_MF_SD,
+       MODE_MF_SI,
+       MODE_PORTS_PER_ENG_1,
+       MODE_PORTS_PER_ENG_2,
+       MODE_PORTS_PER_ENG_4,
+       MODE_40G,
+       MODE_100G,
+       MODE_EAGLE_ENG1_WORKAROUND,
+       MAX_INIT_MODES
+};
+
+enum init_phases {
+       PHASE_ENGINE,
+       PHASE_PORT,
+       PHASE_PF,
+       PHASE_RESERVED,
+       PHASE_QM_PF,
+       MAX_INIT_PHASES
+};
+
+struct mstorm_core_conn_ag_ctx {
+       u8      byte0 /* cdu_validation */;
+       u8      byte1 /* state */;
+       u8      flags0;
+#define MSTORM_CORE_CONN_AG_CTX_BIT0_MASK     0x1       /* exist_in_qm0 */
+#define MSTORM_CORE_CONN_AG_CTX_BIT0_SHIFT    0
+#define MSTORM_CORE_CONN_AG_CTX_BIT1_MASK     0x1       /* exist_in_qm1 */
+#define MSTORM_CORE_CONN_AG_CTX_BIT1_SHIFT    1
+#define MSTORM_CORE_CONN_AG_CTX_CF0_MASK      0x3       /* cf0 */
+#define MSTORM_CORE_CONN_AG_CTX_CF0_SHIFT     2
+#define MSTORM_CORE_CONN_AG_CTX_CF1_MASK      0x3       /* cf1 */
+#define MSTORM_CORE_CONN_AG_CTX_CF1_SHIFT     4
+#define MSTORM_CORE_CONN_AG_CTX_CF2_MASK      0x3       /* cf2 */
+#define MSTORM_CORE_CONN_AG_CTX_CF2_SHIFT     6
+       u8 flags1;
+#define MSTORM_CORE_CONN_AG_CTX_CF0EN_MASK    0x1       /* cf0en */
+#define MSTORM_CORE_CONN_AG_CTX_CF0EN_SHIFT   0
+#define MSTORM_CORE_CONN_AG_CTX_CF1EN_MASK    0x1       /* cf1en */
+#define MSTORM_CORE_CONN_AG_CTX_CF1EN_SHIFT   1
+#define MSTORM_CORE_CONN_AG_CTX_CF2EN_MASK    0x1       /* cf2en */
+#define MSTORM_CORE_CONN_AG_CTX_CF2EN_SHIFT   2
+#define MSTORM_CORE_CONN_AG_CTX_RULE0EN_MASK  0x1       /* rule0en */
+#define MSTORM_CORE_CONN_AG_CTX_RULE0EN_SHIFT 3
+#define MSTORM_CORE_CONN_AG_CTX_RULE1EN_MASK  0x1       /* rule1en */
+#define MSTORM_CORE_CONN_AG_CTX_RULE1EN_SHIFT 4
+#define MSTORM_CORE_CONN_AG_CTX_RULE2EN_MASK  0x1       /* rule2en */
+#define MSTORM_CORE_CONN_AG_CTX_RULE2EN_SHIFT 5
+#define MSTORM_CORE_CONN_AG_CTX_RULE3EN_MASK  0x1       /* rule3en */
+#define MSTORM_CORE_CONN_AG_CTX_RULE3EN_SHIFT 6
+#define MSTORM_CORE_CONN_AG_CTX_RULE4EN_MASK  0x1       /* rule4en */
+#define MSTORM_CORE_CONN_AG_CTX_RULE4EN_SHIFT 7
+       __le16  word0 /* word0 */;
+       __le16  word1 /* word1 */;
+       __le32  reg0 /* reg0 */;
+       __le32  reg1 /* reg1 */;
+};
+
+/* per encapsulation type enabling flags */
+struct prs_reg_encapsulation_type_en {
+       u8 flags;
+#define PRS_REG_ENCAPSULATION_TYPE_EN_ETH_OVER_GRE_ENABLE_MASK     0x1
+#define PRS_REG_ENCAPSULATION_TYPE_EN_ETH_OVER_GRE_ENABLE_SHIFT    0
+#define PRS_REG_ENCAPSULATION_TYPE_EN_IP_OVER_GRE_ENABLE_MASK      0x1
+#define PRS_REG_ENCAPSULATION_TYPE_EN_IP_OVER_GRE_ENABLE_SHIFT     1
+#define PRS_REG_ENCAPSULATION_TYPE_EN_VXLAN_ENABLE_MASK            0x1
+#define PRS_REG_ENCAPSULATION_TYPE_EN_VXLAN_ENABLE_SHIFT           2
+#define PRS_REG_ENCAPSULATION_TYPE_EN_T_TAG_ENABLE_MASK            0x1
+#define PRS_REG_ENCAPSULATION_TYPE_EN_T_TAG_ENABLE_SHIFT           3
+#define PRS_REG_ENCAPSULATION_TYPE_EN_ETH_OVER_GENEVE_ENABLE_MASK  0x1
+#define PRS_REG_ENCAPSULATION_TYPE_EN_ETH_OVER_GENEVE_ENABLE_SHIFT 4
+#define PRS_REG_ENCAPSULATION_TYPE_EN_IP_OVER_GENEVE_ENABLE_MASK   0x1
+#define PRS_REG_ENCAPSULATION_TYPE_EN_IP_OVER_GENEVE_ENABLE_SHIFT  5
+#define PRS_REG_ENCAPSULATION_TYPE_EN_RESERVED_MASK                0x3
+#define PRS_REG_ENCAPSULATION_TYPE_EN_RESERVED_SHIFT               6
+};
+
+enum pxp_tph_st_hint {
+       TPH_ST_HINT_BIDIR /* Read/Write access by Host and Device */,
+       TPH_ST_HINT_REQUESTER /* Read/Write access by Device */,
+       TPH_ST_HINT_TARGET,
+       TPH_ST_HINT_TARGET_PRIO,
+       MAX_PXP_TPH_ST_HINT
+};
+
+/* QM hardware structure of enable bypass credit mask */
+struct qm_rf_bypass_mask {
+       u8 flags;
+#define QM_RF_BYPASS_MASK_LINEVOQ_MASK    0x1
+#define QM_RF_BYPASS_MASK_LINEVOQ_SHIFT   0
+#define QM_RF_BYPASS_MASK_RESERVED0_MASK  0x1
+#define QM_RF_BYPASS_MASK_RESERVED0_SHIFT 1
+#define QM_RF_BYPASS_MASK_PFWFQ_MASK      0x1
+#define QM_RF_BYPASS_MASK_PFWFQ_SHIFT     2
+#define QM_RF_BYPASS_MASK_VPWFQ_MASK      0x1
+#define QM_RF_BYPASS_MASK_VPWFQ_SHIFT     3
+#define QM_RF_BYPASS_MASK_PFRL_MASK       0x1
+#define QM_RF_BYPASS_MASK_PFRL_SHIFT      4
+#define QM_RF_BYPASS_MASK_VPQCNRL_MASK    0x1
+#define QM_RF_BYPASS_MASK_VPQCNRL_SHIFT   5
+#define QM_RF_BYPASS_MASK_FWPAUSE_MASK    0x1
+#define QM_RF_BYPASS_MASK_FWPAUSE_SHIFT   6
+#define QM_RF_BYPASS_MASK_RESERVED1_MASK  0x1
+#define QM_RF_BYPASS_MASK_RESERVED1_SHIFT 7
+};
+
+/* QM hardware structure of opportunistic credit mask */
+struct qm_rf_opportunistic_mask {
+       __le16 flags;
+#define QM_RF_OPPORTUNISTIC_MASK_LINEVOQ_MASK     0x1
+#define QM_RF_OPPORTUNISTIC_MASK_LINEVOQ_SHIFT    0
+#define QM_RF_OPPORTUNISTIC_MASK_BYTEVOQ_MASK     0x1
+#define QM_RF_OPPORTUNISTIC_MASK_BYTEVOQ_SHIFT    1
+#define QM_RF_OPPORTUNISTIC_MASK_PFWFQ_MASK       0x1
+#define QM_RF_OPPORTUNISTIC_MASK_PFWFQ_SHIFT      2
+#define QM_RF_OPPORTUNISTIC_MASK_VPWFQ_MASK       0x1
+#define QM_RF_OPPORTUNISTIC_MASK_VPWFQ_SHIFT      3
+#define QM_RF_OPPORTUNISTIC_MASK_PFRL_MASK        0x1
+#define QM_RF_OPPORTUNISTIC_MASK_PFRL_SHIFT       4
+#define QM_RF_OPPORTUNISTIC_MASK_VPQCNRL_MASK     0x1
+#define QM_RF_OPPORTUNISTIC_MASK_VPQCNRL_SHIFT    5
+#define QM_RF_OPPORTUNISTIC_MASK_FWPAUSE_MASK     0x1
+#define QM_RF_OPPORTUNISTIC_MASK_FWPAUSE_SHIFT    6
+#define QM_RF_OPPORTUNISTIC_MASK_RESERVED0_MASK   0x1
+#define QM_RF_OPPORTUNISTIC_MASK_RESERVED0_SHIFT  7
+#define QM_RF_OPPORTUNISTIC_MASK_QUEUEEMPTY_MASK  0x1
+#define QM_RF_OPPORTUNISTIC_MASK_QUEUEEMPTY_SHIFT 8
+#define QM_RF_OPPORTUNISTIC_MASK_RESERVED1_MASK   0x7F
+#define QM_RF_OPPORTUNISTIC_MASK_RESERVED1_SHIFT  9
+};
+
+/* QM hardware structure of QM map memory */
+struct qm_rf_pq_map {
+       u32 reg;
+#define QM_RF_PQ_MAP_PQ_VALID_MASK          0x1         /* PQ active */
+#define QM_RF_PQ_MAP_PQ_VALID_SHIFT         0
+#define QM_RF_PQ_MAP_RL_ID_MASK             0xFF        /* RL ID */
+#define QM_RF_PQ_MAP_RL_ID_SHIFT            1
+#define QM_RF_PQ_MAP_VP_PQ_ID_MASK          0x1FF
+#define QM_RF_PQ_MAP_VP_PQ_ID_SHIFT         9
+#define QM_RF_PQ_MAP_VOQ_MASK               0x1F        /* VOQ */
+#define QM_RF_PQ_MAP_VOQ_SHIFT              18
+#define QM_RF_PQ_MAP_WRR_WEIGHT_GROUP_MASK  0x3         /* WRR weight */
+#define QM_RF_PQ_MAP_WRR_WEIGHT_GROUP_SHIFT 23
+#define QM_RF_PQ_MAP_RL_VALID_MASK          0x1         /* RL active */
+#define QM_RF_PQ_MAP_RL_VALID_SHIFT         25
+#define QM_RF_PQ_MAP_RESERVED_MASK          0x3F
+#define QM_RF_PQ_MAP_RESERVED_SHIFT         26
+};
+
+/* SDM operation gen command (generate aggregative interrupt) */
+struct sdm_op_gen {
+       __le32 command;
+#define SDM_OP_GEN_COMP_PARAM_MASK  0xFFFF      /* completion parameters 0-15 */
+#define SDM_OP_GEN_COMP_PARAM_SHIFT 0
+#define SDM_OP_GEN_COMP_TYPE_MASK   0xF         /* completion type 16-19 */
+#define SDM_OP_GEN_COMP_TYPE_SHIFT  16
+#define SDM_OP_GEN_RESERVED_MASK    0xFFF       /* reserved 20-31 */
+#define SDM_OP_GEN_RESERVED_SHIFT   20
+};
+
+struct tstorm_core_conn_ag_ctx {
+       u8      byte0 /* cdu_validation */;
+       u8      byte1 /* state */;
+       u8      flags0;
+#define TSTORM_CORE_CONN_AG_CTX_BIT0_MASK     0x1       /* exist_in_qm0 */
+#define TSTORM_CORE_CONN_AG_CTX_BIT0_SHIFT    0
+#define TSTORM_CORE_CONN_AG_CTX_BIT1_MASK     0x1       /* exist_in_qm1 */
+#define TSTORM_CORE_CONN_AG_CTX_BIT1_SHIFT    1
+#define TSTORM_CORE_CONN_AG_CTX_BIT2_MASK     0x1       /* bit2 */
+#define TSTORM_CORE_CONN_AG_CTX_BIT2_SHIFT    2
+#define TSTORM_CORE_CONN_AG_CTX_BIT3_MASK     0x1       /* bit3 */
+#define TSTORM_CORE_CONN_AG_CTX_BIT3_SHIFT    3
+#define TSTORM_CORE_CONN_AG_CTX_BIT4_MASK     0x1       /* bit4 */
+#define TSTORM_CORE_CONN_AG_CTX_BIT4_SHIFT    4
+#define TSTORM_CORE_CONN_AG_CTX_BIT5_MASK     0x1       /* bit5 */
+#define TSTORM_CORE_CONN_AG_CTX_BIT5_SHIFT    5
+#define TSTORM_CORE_CONN_AG_CTX_CF0_MASK      0x3       /* timer0cf */
+#define TSTORM_CORE_CONN_AG_CTX_CF0_SHIFT     6
+       u8 flags1;
+#define TSTORM_CORE_CONN_AG_CTX_CF1_MASK      0x3       /* timer1cf */
+#define TSTORM_CORE_CONN_AG_CTX_CF1_SHIFT     0
+#define TSTORM_CORE_CONN_AG_CTX_CF2_MASK      0x3       /* timer2cf */
+#define TSTORM_CORE_CONN_AG_CTX_CF2_SHIFT     2
+#define TSTORM_CORE_CONN_AG_CTX_CF3_MASK      0x3       /* timer_stop_all */
+#define TSTORM_CORE_CONN_AG_CTX_CF3_SHIFT     4
+#define TSTORM_CORE_CONN_AG_CTX_CF4_MASK      0x3       /* cf4 */
+#define TSTORM_CORE_CONN_AG_CTX_CF4_SHIFT     6
+       u8 flags2;
+#define TSTORM_CORE_CONN_AG_CTX_CF5_MASK      0x3       /* cf5 */
+#define TSTORM_CORE_CONN_AG_CTX_CF5_SHIFT     0
+#define TSTORM_CORE_CONN_AG_CTX_CF6_MASK      0x3       /* cf6 */
+#define TSTORM_CORE_CONN_AG_CTX_CF6_SHIFT     2
+#define TSTORM_CORE_CONN_AG_CTX_CF7_MASK      0x3       /* cf7 */
+#define TSTORM_CORE_CONN_AG_CTX_CF7_SHIFT     4
+#define TSTORM_CORE_CONN_AG_CTX_CF8_MASK      0x3       /* cf8 */
+#define TSTORM_CORE_CONN_AG_CTX_CF8_SHIFT     6
+       u8 flags3;
+#define TSTORM_CORE_CONN_AG_CTX_CF9_MASK      0x3       /* cf9 */
+#define TSTORM_CORE_CONN_AG_CTX_CF9_SHIFT     0
+#define TSTORM_CORE_CONN_AG_CTX_CF10_MASK     0x3       /* cf10 */
+#define TSTORM_CORE_CONN_AG_CTX_CF10_SHIFT    2
+#define TSTORM_CORE_CONN_AG_CTX_CF0EN_MASK    0x1       /* cf0en */
+#define TSTORM_CORE_CONN_AG_CTX_CF0EN_SHIFT   4
+#define TSTORM_CORE_CONN_AG_CTX_CF1EN_MASK    0x1       /* cf1en */
+#define TSTORM_CORE_CONN_AG_CTX_CF1EN_SHIFT   5
+#define TSTORM_CORE_CONN_AG_CTX_CF2EN_MASK    0x1       /* cf2en */
+#define TSTORM_CORE_CONN_AG_CTX_CF2EN_SHIFT   6
+#define TSTORM_CORE_CONN_AG_CTX_CF3EN_MASK    0x1       /* cf3en */
+#define TSTORM_CORE_CONN_AG_CTX_CF3EN_SHIFT   7
+       u8 flags4;
+#define TSTORM_CORE_CONN_AG_CTX_CF4EN_MASK    0x1       /* cf4en */
+#define TSTORM_CORE_CONN_AG_CTX_CF4EN_SHIFT   0
+#define TSTORM_CORE_CONN_AG_CTX_CF5EN_MASK    0x1       /* cf5en */
+#define TSTORM_CORE_CONN_AG_CTX_CF5EN_SHIFT   1
+#define TSTORM_CORE_CONN_AG_CTX_CF6EN_MASK    0x1       /* cf6en */
+#define TSTORM_CORE_CONN_AG_CTX_CF6EN_SHIFT   2
+#define TSTORM_CORE_CONN_AG_CTX_CF7EN_MASK    0x1       /* cf7en */
+#define TSTORM_CORE_CONN_AG_CTX_CF7EN_SHIFT   3
+#define TSTORM_CORE_CONN_AG_CTX_CF8EN_MASK    0x1       /* cf8en */
+#define TSTORM_CORE_CONN_AG_CTX_CF8EN_SHIFT   4
+#define TSTORM_CORE_CONN_AG_CTX_CF9EN_MASK    0x1       /* cf9en */
+#define TSTORM_CORE_CONN_AG_CTX_CF9EN_SHIFT   5
+#define TSTORM_CORE_CONN_AG_CTX_CF10EN_MASK   0x1       /* cf10en */
+#define TSTORM_CORE_CONN_AG_CTX_CF10EN_SHIFT  6
+#define TSTORM_CORE_CONN_AG_CTX_RULE0EN_MASK  0x1       /* rule0en */
+#define TSTORM_CORE_CONN_AG_CTX_RULE0EN_SHIFT 7
+       u8 flags5;
+#define TSTORM_CORE_CONN_AG_CTX_RULE1EN_MASK  0x1       /* rule1en */
+#define TSTORM_CORE_CONN_AG_CTX_RULE1EN_SHIFT 0
+#define TSTORM_CORE_CONN_AG_CTX_RULE2EN_MASK  0x1       /* rule2en */
+#define TSTORM_CORE_CONN_AG_CTX_RULE2EN_SHIFT 1
+#define TSTORM_CORE_CONN_AG_CTX_RULE3EN_MASK  0x1       /* rule3en */
+#define TSTORM_CORE_CONN_AG_CTX_RULE3EN_SHIFT 2
+#define TSTORM_CORE_CONN_AG_CTX_RULE4EN_MASK  0x1       /* rule4en */
+#define TSTORM_CORE_CONN_AG_CTX_RULE4EN_SHIFT 3
+#define TSTORM_CORE_CONN_AG_CTX_RULE5EN_MASK  0x1       /* rule5en */
+#define TSTORM_CORE_CONN_AG_CTX_RULE5EN_SHIFT 4
+#define TSTORM_CORE_CONN_AG_CTX_RULE6EN_MASK  0x1       /* rule6en */
+#define TSTORM_CORE_CONN_AG_CTX_RULE6EN_SHIFT 5
+#define TSTORM_CORE_CONN_AG_CTX_RULE7EN_MASK  0x1       /* rule7en */
+#define TSTORM_CORE_CONN_AG_CTX_RULE7EN_SHIFT 6
+#define TSTORM_CORE_CONN_AG_CTX_RULE8EN_MASK  0x1       /* rule8en */
+#define TSTORM_CORE_CONN_AG_CTX_RULE8EN_SHIFT 7
+       __le32  reg0 /* reg0 */;
+       __le32  reg1 /* reg1 */;
+       __le32  reg2 /* reg2 */;
+       __le32  reg3 /* reg3 */;
+       __le32  reg4 /* reg4 */;
+       __le32  reg5 /* reg5 */;
+       __le32  reg6 /* reg6 */;
+       __le32  reg7 /* reg7 */;
+       __le32  reg8 /* reg8 */;
+       u8      byte2 /* byte2 */;
+       u8      byte3 /* byte3 */;
+       __le16  word0 /* word0 */;
+       u8      byte4 /* byte4 */;
+       u8      byte5 /* byte5 */;
+       __le16  word1 /* word1 */;
+       __le16  word2 /* conn_dpi */;
+       __le16  word3 /* word3 */;
+       __le32  reg9 /* reg9 */;
+       __le32  reg10 /* reg10 */;
+};
+
+struct ustorm_core_conn_ag_ctx {
+       u8      reserved /* cdu_validation */;
+       u8      byte1 /* state */;
+       u8      flags0;
+#define USTORM_CORE_CONN_AG_CTX_BIT0_MASK     0x1       /* exist_in_qm0 */
+#define USTORM_CORE_CONN_AG_CTX_BIT0_SHIFT    0
+#define USTORM_CORE_CONN_AG_CTX_BIT1_MASK     0x1       /* exist_in_qm1 */
+#define USTORM_CORE_CONN_AG_CTX_BIT1_SHIFT    1
+#define USTORM_CORE_CONN_AG_CTX_CF0_MASK      0x3       /* timer0cf */
+#define USTORM_CORE_CONN_AG_CTX_CF0_SHIFT     2
+#define USTORM_CORE_CONN_AG_CTX_CF1_MASK      0x3       /* timer1cf */
+#define USTORM_CORE_CONN_AG_CTX_CF1_SHIFT     4
+#define USTORM_CORE_CONN_AG_CTX_CF2_MASK      0x3       /* timer2cf */
+#define USTORM_CORE_CONN_AG_CTX_CF2_SHIFT     6
+       u8 flags1;
+#define USTORM_CORE_CONN_AG_CTX_CF3_MASK      0x3       /* timer_stop_all */
+#define USTORM_CORE_CONN_AG_CTX_CF3_SHIFT     0
+#define USTORM_CORE_CONN_AG_CTX_CF4_MASK      0x3       /* cf4 */
+#define USTORM_CORE_CONN_AG_CTX_CF4_SHIFT     2
+#define USTORM_CORE_CONN_AG_CTX_CF5_MASK      0x3       /* cf5 */
+#define USTORM_CORE_CONN_AG_CTX_CF5_SHIFT     4
+#define USTORM_CORE_CONN_AG_CTX_CF6_MASK      0x3       /* cf6 */
+#define USTORM_CORE_CONN_AG_CTX_CF6_SHIFT     6
+       u8 flags2;
+#define USTORM_CORE_CONN_AG_CTX_CF0EN_MASK    0x1       /* cf0en */
+#define USTORM_CORE_CONN_AG_CTX_CF0EN_SHIFT   0
+#define USTORM_CORE_CONN_AG_CTX_CF1EN_MASK    0x1       /* cf1en */
+#define USTORM_CORE_CONN_AG_CTX_CF1EN_SHIFT   1
+#define USTORM_CORE_CONN_AG_CTX_CF2EN_MASK    0x1       /* cf2en */
+#define USTORM_CORE_CONN_AG_CTX_CF2EN_SHIFT   2
+#define USTORM_CORE_CONN_AG_CTX_CF3EN_MASK    0x1       /* cf3en */
+#define USTORM_CORE_CONN_AG_CTX_CF3EN_SHIFT   3
+#define USTORM_CORE_CONN_AG_CTX_CF4EN_MASK    0x1       /* cf4en */
+#define USTORM_CORE_CONN_AG_CTX_CF4EN_SHIFT   4
+#define USTORM_CORE_CONN_AG_CTX_CF5EN_MASK    0x1       /* cf5en */
+#define USTORM_CORE_CONN_AG_CTX_CF5EN_SHIFT   5
+#define USTORM_CORE_CONN_AG_CTX_CF6EN_MASK    0x1       /* cf6en */
+#define USTORM_CORE_CONN_AG_CTX_CF6EN_SHIFT   6
+#define USTORM_CORE_CONN_AG_CTX_RULE0EN_MASK  0x1       /* rule0en */
+#define USTORM_CORE_CONN_AG_CTX_RULE0EN_SHIFT 7
+       u8 flags3;
+#define USTORM_CORE_CONN_AG_CTX_RULE1EN_MASK  0x1       /* rule1en */
+#define USTORM_CORE_CONN_AG_CTX_RULE1EN_SHIFT 0
+#define USTORM_CORE_CONN_AG_CTX_RULE2EN_MASK  0x1       /* rule2en */
+#define USTORM_CORE_CONN_AG_CTX_RULE2EN_SHIFT 1
+#define USTORM_CORE_CONN_AG_CTX_RULE3EN_MASK  0x1       /* rule3en */
+#define USTORM_CORE_CONN_AG_CTX_RULE3EN_SHIFT 2
+#define USTORM_CORE_CONN_AG_CTX_RULE4EN_MASK  0x1       /* rule4en */
+#define USTORM_CORE_CONN_AG_CTX_RULE4EN_SHIFT 3
+#define USTORM_CORE_CONN_AG_CTX_RULE5EN_MASK  0x1       /* rule5en */
+#define USTORM_CORE_CONN_AG_CTX_RULE5EN_SHIFT 4
+#define USTORM_CORE_CONN_AG_CTX_RULE6EN_MASK  0x1       /* rule6en */
+#define USTORM_CORE_CONN_AG_CTX_RULE6EN_SHIFT 5
+#define USTORM_CORE_CONN_AG_CTX_RULE7EN_MASK  0x1       /* rule7en */
+#define USTORM_CORE_CONN_AG_CTX_RULE7EN_SHIFT 6
+#define USTORM_CORE_CONN_AG_CTX_RULE8EN_MASK  0x1       /* rule8en */
+#define USTORM_CORE_CONN_AG_CTX_RULE8EN_SHIFT 7
+       u8      byte2 /* byte2 */;
+       u8      byte3 /* byte3 */;
+       __le16  word0 /* conn_dpi */;
+       __le16  word1 /* word1 */;
+       __le32  rx_producers /* reg0 */;
+       __le32  reg1 /* reg1 */;
+       __le32  reg2 /* reg2 */;
+       __le32  reg3 /* reg3 */;
+       __le16  word2 /* word2 */;
+       __le16  word3 /* word3 */;
+};
+
+struct ystorm_core_conn_ag_ctx {
+       u8      byte0 /* cdu_validation */;
+       u8      byte1 /* state */;
+       u8      flags0;
+#define YSTORM_CORE_CONN_AG_CTX_BIT0_MASK     0x1       /* exist_in_qm0 */
+#define YSTORM_CORE_CONN_AG_CTX_BIT0_SHIFT    0
+#define YSTORM_CORE_CONN_AG_CTX_BIT1_MASK     0x1       /* exist_in_qm1 */
+#define YSTORM_CORE_CONN_AG_CTX_BIT1_SHIFT    1
+#define YSTORM_CORE_CONN_AG_CTX_CF0_MASK      0x3       /* cf0 */
+#define YSTORM_CORE_CONN_AG_CTX_CF0_SHIFT     2
+#define YSTORM_CORE_CONN_AG_CTX_CF1_MASK      0x3       /* cf1 */
+#define YSTORM_CORE_CONN_AG_CTX_CF1_SHIFT     4
+#define YSTORM_CORE_CONN_AG_CTX_CF2_MASK      0x3       /* cf2 */
+#define YSTORM_CORE_CONN_AG_CTX_CF2_SHIFT     6
+       u8 flags1;
+#define YSTORM_CORE_CONN_AG_CTX_CF0EN_MASK    0x1       /* cf0en */
+#define YSTORM_CORE_CONN_AG_CTX_CF0EN_SHIFT   0
+#define YSTORM_CORE_CONN_AG_CTX_CF1EN_MASK    0x1       /* cf1en */
+#define YSTORM_CORE_CONN_AG_CTX_CF1EN_SHIFT   1
+#define YSTORM_CORE_CONN_AG_CTX_CF2EN_MASK    0x1       /* cf2en */
+#define YSTORM_CORE_CONN_AG_CTX_CF2EN_SHIFT   2
+#define YSTORM_CORE_CONN_AG_CTX_RULE0EN_MASK  0x1       /* rule0en */
+#define YSTORM_CORE_CONN_AG_CTX_RULE0EN_SHIFT 3
+#define YSTORM_CORE_CONN_AG_CTX_RULE1EN_MASK  0x1       /* rule1en */
+#define YSTORM_CORE_CONN_AG_CTX_RULE1EN_SHIFT 4
+#define YSTORM_CORE_CONN_AG_CTX_RULE2EN_MASK  0x1       /* rule2en */
+#define YSTORM_CORE_CONN_AG_CTX_RULE2EN_SHIFT 5
+#define YSTORM_CORE_CONN_AG_CTX_RULE3EN_MASK  0x1       /* rule3en */
+#define YSTORM_CORE_CONN_AG_CTX_RULE3EN_SHIFT 6
+#define YSTORM_CORE_CONN_AG_CTX_RULE4EN_MASK  0x1       /* rule4en */
+#define YSTORM_CORE_CONN_AG_CTX_RULE4EN_SHIFT 7
+       u8      byte2 /* byte2 */;
+       u8      byte3 /* byte3 */;
+       __le16  word0 /* word0 */;
+       __le32  reg0 /* reg0 */;
+       __le32  reg1 /* reg1 */;
+       __le16  word1 /* word1 */;
+       __le16  word2 /* word2 */;
+       __le16  word3 /* word3 */;
+       __le16  word4 /* word4 */;
+       __le32  reg2 /* reg2 */;
+       __le32  reg3 /* reg3 */;
+};
+
+/*********************************** Init ************************************/
+
+/* Width of GRC address in bits (addresses are specified in dwords) */
+#define GRC_ADDR_BITS                   23
+#define MAX_GRC_ADDR                    ((1 << GRC_ADDR_BITS) - 1)
+
+/* indicates an init that should be applied to any phase ID */
+#define ANY_PHASE_ID                    0xffff
+
+/* init pattern size in bytes */
+#define INIT_PATTERN_SIZE_BITS  4
+#define MAX_INIT_PATTERN_SIZE  BIT(INIT_PATTERN_SIZE_BITS)
+
+/* Max size in dwords of a zipped array */
+#define MAX_ZIPPED_SIZE                 8192
+
+/* Global PXP window */
+#define NUM_OF_PXP_WIN                  19
+#define PXP_WIN_DWORD_SIZE_BITS 10
+#define PXP_WIN_DWORD_SIZE             BIT(PXP_WIN_DWORD_SIZE_BITS)
+#define PXP_WIN_BYTE_SIZE_BITS  (PXP_WIN_DWORD_SIZE_BITS + 2)
+#define PXP_WIN_BYTE_SIZE               (PXP_WIN_DWORD_SIZE * 4)
+
+/********************************* GRC Dump **********************************/
+
+/* width of GRC dump register sequence length in bits */
+#define DUMP_SEQ_LEN_BITS                       8
+#define DUMP_SEQ_LEN_MAX_VAL            ((1 << DUMP_SEQ_LEN_BITS) - 1)
+
+/* width of GRC dump memory length in bits */
+#define DUMP_MEM_LEN_BITS                       18
+#define DUMP_MEM_LEN_MAX_VAL            ((1 << DUMP_MEM_LEN_BITS) - 1)
+
+/* width of register type ID in bits */
+#define REG_TYPE_ID_BITS                        6
+#define REG_TYPE_ID_MAX_VAL                     ((1 << REG_TYPE_ID_BITS) - 1)
+
+/* width of block ID in bits */
+#define BLOCK_ID_BITS                           8
+#define BLOCK_ID_MAX_VAL                        ((1 << BLOCK_ID_BITS) - 1)
+
+/******************************** Idle Check *********************************/
+
+/* max number of idle check predicate immediates */
+#define MAX_IDLE_CHK_PRED_IMM           3
+
+/* max number of idle check argument registers */
+#define MAX_IDLE_CHK_READ_REGS          3
+
+/* max number of idle check loops */
+#define MAX_IDLE_CHK_LOOPS                      0x10000
+
+/* max idle check address increment */
+#define MAX_IDLE_CHK_INCREMENT          0x10000
+
+/* inicates an undefined idle check line index */
+#define IDLE_CHK_UNDEFINED_LINE_IDX     0xffffff
+
+/* max number of register values following the idle check header */
+#define IDLE_CHK_MAX_DUMP_REGS          2
+
+/* arguments for IDLE_CHK_MACRO_TYPE_QM_RD_WR */
+#define IDLE_CHK_QM_RD_WR_PTR           0
+#define IDLE_CHK_QM_RD_WR_BANK          1
+
+/**************************************/
+/* HSI Functions constants and macros */
+/**************************************/
+
+/* Number of VLAN priorities */
+#define NUM_OF_VLAN_PRIORITIES                  8
+
+/* the MCP Trace meta data signautre is duplicated in the perl script that
+ * generats the NVRAM images.
+ */
+#define MCP_TRACE_META_IMAGE_SIGNATURE  0x669955aa
+
+/* Binary buffer header */
+struct bin_buffer_hdr {
+       u32     offset;
+       u32     length /* buffer length in bytes */;
+};
+
+/* binary buffer types */
+enum bin_buffer_type {
+       BIN_BUF_FW_VER_INFO /* fw_ver_info struct */,
+       BIN_BUF_INIT_CMD /* init commands */,
+       BIN_BUF_INIT_VAL /* init data */,
+       BIN_BUF_INIT_MODE_TREE /* init modes tree */,
+       BIN_BUF_IRO /* internal RAM offsets array */,
+       MAX_BIN_BUFFER_TYPE
+};
+
+/* Chip IDs */
+enum chip_ids {
+       CHIP_BB_A0 /* BB A0 chip ID */,
+       CHIP_BB_B0 /* BB B0 chip ID */,
+       CHIP_K2 /* AH chip ID */,
+       MAX_CHIP_IDS
+};
+
+enum idle_chk_severity_types {
+       IDLE_CHK_SEVERITY_ERROR /* idle check failure should cause an error */,
+       IDLE_CHK_SEVERITY_ERROR_NO_TRAFFIC,
+       IDLE_CHK_SEVERITY_WARNING,
+       MAX_IDLE_CHK_SEVERITY_TYPES
+};
+
+struct init_array_raw_hdr {
+       __le32 data;
+#define INIT_ARRAY_RAW_HDR_TYPE_MASK    0xF
+#define INIT_ARRAY_RAW_HDR_TYPE_SHIFT   0
+#define INIT_ARRAY_RAW_HDR_PARAMS_MASK  0xFFFFFFF       /* init array params */
+#define INIT_ARRAY_RAW_HDR_PARAMS_SHIFT 4
+};
+
+struct init_array_standard_hdr {
+       __le32 data;
+#define INIT_ARRAY_STANDARD_HDR_TYPE_MASK  0xF
+#define INIT_ARRAY_STANDARD_HDR_TYPE_SHIFT 0
+#define INIT_ARRAY_STANDARD_HDR_SIZE_MASK  0xFFFFFFF
+#define INIT_ARRAY_STANDARD_HDR_SIZE_SHIFT 4
+};
+
+struct init_array_zipped_hdr {
+       __le32 data;
+#define INIT_ARRAY_ZIPPED_HDR_TYPE_MASK         0xF
+#define INIT_ARRAY_ZIPPED_HDR_TYPE_SHIFT        0
+#define INIT_ARRAY_ZIPPED_HDR_ZIPPED_SIZE_MASK  0xFFFFFFF
+#define INIT_ARRAY_ZIPPED_HDR_ZIPPED_SIZE_SHIFT 4
+};
+
+struct init_array_pattern_hdr {
+       __le32 data;
+#define INIT_ARRAY_PATTERN_HDR_TYPE_MASK          0xF
+#define INIT_ARRAY_PATTERN_HDR_TYPE_SHIFT         0
+#define INIT_ARRAY_PATTERN_HDR_PATTERN_SIZE_MASK  0xF
+#define INIT_ARRAY_PATTERN_HDR_PATTERN_SIZE_SHIFT 4
+#define INIT_ARRAY_PATTERN_HDR_REPETITIONS_MASK   0xFFFFFF
+#define INIT_ARRAY_PATTERN_HDR_REPETITIONS_SHIFT  8
+};
+
+union init_array_hdr {
+       struct init_array_raw_hdr       raw /* raw init array header */;
+       struct init_array_standard_hdr  standard;
+       struct init_array_zipped_hdr    zipped /* zipped init array header */;
+       struct init_array_pattern_hdr   pattern /* pattern init array header */;
+};
+
+enum init_array_types {
+       INIT_ARR_STANDARD /* standard init array */,
+       INIT_ARR_ZIPPED /* zipped init array */,
+       INIT_ARR_PATTERN /* a repeated pattern */,
+       MAX_INIT_ARRAY_TYPES
+};
+
+/* init operation: callback */
+struct init_callback_op {
+       __le32  op_data;
+#define INIT_CALLBACK_OP_OP_MASK        0xF
+#define INIT_CALLBACK_OP_OP_SHIFT       0
+#define INIT_CALLBACK_OP_RESERVED_MASK  0xFFFFFFF
+#define INIT_CALLBACK_OP_RESERVED_SHIFT 4
+       __le16  callback_id /* Callback ID */;
+       __le16  block_id /* Blocks ID */;
+};
+
+/* init comparison types */
+enum init_comparison_types {
+       INIT_COMPARISON_EQ /* init value is included in the init command */,
+       INIT_COMPARISON_OR /* init value is all zeros */,
+       INIT_COMPARISON_AND /* init value is an array of values */,
+       MAX_INIT_COMPARISON_TYPES
+};
+
+/* init operation: delay */
+struct init_delay_op {
+       __le32  op_data;
+#define INIT_DELAY_OP_OP_MASK        0xF
+#define INIT_DELAY_OP_OP_SHIFT       0
+#define INIT_DELAY_OP_RESERVED_MASK  0xFFFFFFF
+#define INIT_DELAY_OP_RESERVED_SHIFT 4
+       __le32  delay /* delay in us */;
+};
+
+/* init operation: if_mode */
+struct init_if_mode_op {
+       __le32 op_data;
+#define INIT_IF_MODE_OP_OP_MASK          0xF
+#define INIT_IF_MODE_OP_OP_SHIFT         0
+#define INIT_IF_MODE_OP_RESERVED1_MASK   0xFFF
+#define INIT_IF_MODE_OP_RESERVED1_SHIFT  4
+#define INIT_IF_MODE_OP_CMD_OFFSET_MASK  0xFFFF
+#define INIT_IF_MODE_OP_CMD_OFFSET_SHIFT 16
+       __le16  reserved2;
+       __le16  modes_buf_offset;
+};
+
+/*  init operation: if_phase */
+struct init_if_phase_op {
+       __le32 op_data;
+#define INIT_IF_PHASE_OP_OP_MASK           0xF
+#define INIT_IF_PHASE_OP_OP_SHIFT          0
+#define INIT_IF_PHASE_OP_DMAE_ENABLE_MASK  0x1
+#define INIT_IF_PHASE_OP_DMAE_ENABLE_SHIFT 4
+#define INIT_IF_PHASE_OP_RESERVED1_MASK    0x7FF
+#define INIT_IF_PHASE_OP_RESERVED1_SHIFT   5
+#define INIT_IF_PHASE_OP_CMD_OFFSET_MASK   0xFFFF
+#define INIT_IF_PHASE_OP_CMD_OFFSET_SHIFT  16
+       __le32 phase_data;
+#define INIT_IF_PHASE_OP_PHASE_MASK        0xFF /* Init phase */
+#define INIT_IF_PHASE_OP_PHASE_SHIFT       0
+#define INIT_IF_PHASE_OP_RESERVED2_MASK    0xFF
+#define INIT_IF_PHASE_OP_RESERVED2_SHIFT   8
+#define INIT_IF_PHASE_OP_PHASE_ID_MASK     0xFFFF /* Init phase ID */
+#define INIT_IF_PHASE_OP_PHASE_ID_SHIFT    16
+};
+
+/* init mode operators */
+enum init_mode_ops {
+       INIT_MODE_OP_NOT /* init mode not operator */,
+       INIT_MODE_OP_OR /* init mode or operator */,
+       INIT_MODE_OP_AND /* init mode and operator */,
+       MAX_INIT_MODE_OPS
+};
+
+/* init operation: raw */
+struct init_raw_op {
+       __le32  op_data;
+#define INIT_RAW_OP_OP_MASK      0xF
+#define INIT_RAW_OP_OP_SHIFT     0
+#define INIT_RAW_OP_PARAM1_MASK  0xFFFFFFF      /* init param 1 */
+#define INIT_RAW_OP_PARAM1_SHIFT 4
+       __le32  param2 /* Init param 2 */;
+};
+
+/* init array params */
+struct init_op_array_params {
+       __le16  size /* array size in dwords */;
+       __le16  offset /* array start offset in dwords */;
+};
+
+/* Write init operation arguments */
+union init_write_args {
+       __le32                          inline_val;
+       __le32                          zeros_count;
+       __le32                          array_offset;
+       struct init_op_array_params     runtime;
+};
+
+/* init operation: write */
+struct init_write_op {
+       __le32 data;
+#define INIT_WRITE_OP_OP_MASK        0xF
+#define INIT_WRITE_OP_OP_SHIFT       0
+#define INIT_WRITE_OP_SOURCE_MASK    0x7
+#define INIT_WRITE_OP_SOURCE_SHIFT   4
+#define INIT_WRITE_OP_RESERVED_MASK  0x1
+#define INIT_WRITE_OP_RESERVED_SHIFT 7
+#define INIT_WRITE_OP_WIDE_BUS_MASK  0x1
+#define INIT_WRITE_OP_WIDE_BUS_SHIFT 8
+#define INIT_WRITE_OP_ADDRESS_MASK   0x7FFFFF
+#define INIT_WRITE_OP_ADDRESS_SHIFT  9
+       union init_write_args args /* Write init operation arguments */;
+};
+
+/* init operation: read */
+struct init_read_op {
+       __le32 op_data;
+#define INIT_READ_OP_OP_MASK         0xF
+#define INIT_READ_OP_OP_SHIFT        0
+#define INIT_READ_OP_POLL_COMP_MASK  0x7
+#define INIT_READ_OP_POLL_COMP_SHIFT 4
+#define INIT_READ_OP_RESERVED_MASK   0x1
+#define INIT_READ_OP_RESERVED_SHIFT  7
+#define INIT_READ_OP_POLL_MASK       0x1
+#define INIT_READ_OP_POLL_SHIFT      8
+#define INIT_READ_OP_ADDRESS_MASK    0x7FFFFF
+#define INIT_READ_OP_ADDRESS_SHIFT   9
+       __le32 expected_val;
+};
+
+/* Init operations union */
+union init_op {
+       struct init_raw_op      raw /* raw init operation */;
+       struct init_write_op    write /* write init operation */;
+       struct init_read_op     read /* read init operation */;
+       struct init_if_mode_op  if_mode /* if_mode init operation */;
+       struct init_if_phase_op if_phase /* if_phase init operation */;
+       struct init_callback_op callback /* callback init operation */;
+       struct init_delay_op    delay /* delay init operation */;
+};
+
+/* Init command operation types */
+enum init_op_types {
+       INIT_OP_READ /* GRC read init command */,
+       INIT_OP_WRITE /* GRC write init command */,
+       INIT_OP_IF_MODE,
+       INIT_OP_IF_PHASE,
+       INIT_OP_DELAY /* delay init command */,
+       INIT_OP_CALLBACK /* callback init command */,
+       MAX_INIT_OP_TYPES
+};
+
+/* init source types */
+enum init_source_types {
+       INIT_SRC_INLINE /* init value is included in the init command */,
+       INIT_SRC_ZEROS /* init value is all zeros */,
+       INIT_SRC_ARRAY /* init value is an array of values */,
+       INIT_SRC_RUNTIME /* init value is provided during runtime */,
+       MAX_INIT_SOURCE_TYPES
+};
+
+/* Internal RAM Offsets macro data */
+struct iro {
+       u32     base /* RAM field offset */;
+       u16     m1 /* multiplier 1 */;
+       u16     m2 /* multiplier 2 */;
+       u16     m3 /* multiplier 3 */;
+       u16     size /* RAM field size */;
+};
+
+/* QM per-port init parameters */
+struct init_qm_port_params {
+       u8      active /* Indicates if this port is active */;
+       u8      num_active_phys_tcs;
+       u16     num_pbf_cmd_lines;
+       u16     num_btb_blocks;
+       __le16  reserved;
+};
+
+/* QM per-PQ init parameters */
+struct init_qm_pq_params {
+       u8      vport_id /* VPORT ID */;
+       u8      tc_id /* TC ID */;
+       u8      wrr_group /* WRR group */;
+       u8      reserved;
+};
+
+/* QM per-vport init parameters */
+struct init_qm_vport_params {
+       u32     vport_rl;
+       u16     vport_wfq;
+       u16     first_tx_pq_id[NUM_OF_TCS];
+};
+
+/* Win 2 */
+#define GTT_BAR0_MAP_REG_IGU_CMD \
+       0x00f000UL
+/* Win 3 */
+#define GTT_BAR0_MAP_REG_TSDM_RAM \
+       0x010000UL
+/* Win 4 */
+#define GTT_BAR0_MAP_REG_MSDM_RAM \
+       0x011000UL
+/* Win 5 */
+#define GTT_BAR0_MAP_REG_MSDM_RAM_1024 \
+       0x012000UL
+/* Win 6 */
+#define GTT_BAR0_MAP_REG_USDM_RAM \
+       0x013000UL
+/* Win 7 */
+#define GTT_BAR0_MAP_REG_USDM_RAM_1024 \
+       0x014000UL
+/* Win 8 */
+#define GTT_BAR0_MAP_REG_USDM_RAM_2048 \
+       0x015000UL
+/* Win 9 */
+#define GTT_BAR0_MAP_REG_XSDM_RAM \
+       0x016000UL
+/* Win 10 */
+#define GTT_BAR0_MAP_REG_YSDM_RAM \
+       0x017000UL
+/* Win 11 */
+#define GTT_BAR0_MAP_REG_PSDM_RAM \
+       0x018000UL
+
+/**
+ * @brief qed_qm_pf_mem_size - prepare QM ILT sizes
+ *
+ * Returns the required host memory size in 4KB units.
+ * Must be called before all QM init HSI functions.
+ *
+ * @param pf_id                        - physical function ID
+ * @param num_pf_cids  - number of connections used by this PF
+ * @param num_vf_cids  - number of connections used by VFs of this PF
+ * @param num_tids             - number of tasks used by this PF
+ * @param num_pf_pqs   - number of PQs used by this PF
+ * @param num_vf_pqs   - number of PQs used by VFs of this PF
+ *
+ * @return The required host memory size in 4KB units.
+ */
+u32 qed_qm_pf_mem_size(u8      pf_id,
+                      u32      num_pf_cids,
+                      u32      num_vf_cids,
+                      u32      num_tids,
+                      u16      num_pf_pqs,
+                      u16      num_vf_pqs);
+
+struct qed_qm_common_rt_init_params {
+       u8                              max_ports_per_engine;
+       u8                              max_phys_tcs_per_port;
+       bool                            pf_rl_en;
+       bool                            pf_wfq_en;
+       bool                            vport_rl_en;
+       bool                            vport_wfq_en;
+       struct init_qm_port_params      *port_params;
+};
+
+/**
+ * @brief qed_qm_common_rt_init - Prepare QM runtime init values for the
+ * engine phase.
+ *
+ * @param p_hwfn
+ * @param max_ports_per_engine - max number of ports per engine in HW
+ * @param max_phys_tcs_per_port        - max number of physical TCs per port in HW
+ * @param pf_rl_en                             - enable per-PF rate limiters
+ * @param pf_wfq_en                            - enable per-PF WFQ
+ * @param vport_rl_en                  - enable per-VPORT rate limiters
+ * @param vport_wfq_en                 - enable per-VPORT WFQ
+ * @param port_params                  - array of size MAX_NUM_PORTS with
+ *                                             arameters for each port
+ *
+ * @return 0 on success, -1 on error.
+ */
+int qed_qm_common_rt_init(
+       struct qed_hwfn                         *p_hwfn,
+       struct qed_qm_common_rt_init_params     *p_params);
+
+struct qed_qm_pf_rt_init_params {
+       u8                              port_id;
+       u8                              pf_id;
+       u8                              max_phys_tcs_per_port;
+       bool                            is_first_pf;
+       u32                             num_pf_cids;
+       u32                             num_vf_cids;
+       u32                             num_tids;
+       u16                             start_pq;
+       u16                             num_pf_pqs;
+       u16                             num_vf_pqs;
+       u8                              start_vport;
+       u8                              num_vports;
+       u8                              pf_wfq;
+       u32                             pf_rl;
+       struct init_qm_pq_params        *pq_params;
+       struct init_qm_vport_params     *vport_params;
+};
+
+int qed_qm_pf_rt_init(struct qed_hwfn                  *p_hwfn,
+                     struct qed_ptt                    *p_ptt,
+                     struct qed_qm_pf_rt_init_params   *p_params);
+
+/**
+ * @brief qed_init_pf_rl  Initializes the rate limit of the specified PF
+ *
+ * @param p_hwfn
+ * @param p_ptt        - ptt window used for writing the registers
+ * @param pf_id        - PF ID
+ * @param pf_rl        - rate limit in Mb/sec units
+ *
+ * @return 0 on success, -1 on error.
+ */
+int qed_init_pf_rl(struct qed_hwfn     *p_hwfn,
+                  struct qed_ptt       *p_ptt,
+                  u8                   pf_id,
+                  u32                  pf_rl);
+
+/**
+ * @brief qed_init_vport_rl  Initializes the rate limit of the specified VPORT
+ *
+ * @param p_hwfn
+ * @param p_ptt                - ptt window used for writing the registers
+ * @param vport_id     - VPORT ID
+ * @param vport_rl     - rate limit in Mb/sec units
+ *
+ * @return 0 on success, -1 on error.
+ */
+
+int qed_init_vport_rl(struct qed_hwfn  *p_hwfn,
+                     struct qed_ptt    *p_ptt,
+                     u8                vport_id,
+                     u32               vport_rl);
+/**
+ * @brief qed_send_qm_stop_cmd  Sends a stop command to the QM
+ *
+ * @param p_hwfn
+ * @param p_ptt                 - ptt window used for writing the registers
+ * @param is_release_cmd - true for release, false for stop.
+ * @param is_tx_pq       - true for Tx PQs, false for Other PQs.
+ * @param start_pq       - first PQ ID to stop
+ * @param num_pqs        - Number of PQs to stop, starting from start_pq.
+ *
+ * @return bool, true if successful, false if timeout occurred while waiting
+ *                                     for QM command done.
+ */
+
+bool qed_send_qm_stop_cmd(struct qed_hwfn      *p_hwfn,
+                         struct qed_ptt        *p_ptt,
+                         bool                  is_release_cmd,
+                         bool                  is_tx_pq,
+                         u16                   start_pq,
+                         u16                   num_pqs);
+
+/* Ystorm flow control mode. Use enum fw_flow_ctrl_mode */
+#define YSTORM_FLOW_CONTROL_MODE_OFFSET                        (IRO[0].base)
+#define YSTORM_FLOW_CONTROL_MODE_SIZE                  (IRO[0].size)
+/* Tstorm port statistics */
+#define TSTORM_PORT_STAT_OFFSET(port_id)               (IRO[1].base + \
+                                                        ((port_id) * \
+                                                         IRO[1].m1))
+#define TSTORM_PORT_STAT_SIZE                          (IRO[1].size)
+/* Ustorm VF-PF Channel ready flag */
+#define USTORM_VF_PF_CHANNEL_READY_OFFSET(vf_id)       (IRO[2].base +  \
+                                                        ((vf_id) *     \
+                                                         IRO[2].m1))
+#define USTORM_VF_PF_CHANNEL_READY_SIZE                        (IRO[2].size)
+/* Ustorm Final flr cleanup ack */
+#define USTORM_FLR_FINAL_ACK_OFFSET                    (IRO[3].base)
+#define USTORM_FLR_FINAL_ACK_SIZE                      (IRO[3].size)
+/* Ustorm Event ring consumer */
+#define USTORM_EQE_CONS_OFFSET(pf_id)                  (IRO[4].base +  \
+                                                        ((pf_id) *     \
+                                                         IRO[4].m1))
+#define USTORM_EQE_CONS_SIZE                           (IRO[4].size)
+/* Ustorm Completion ring consumer */
+#define USTORM_CQ_CONS_OFFSET(global_queue_id)         (IRO[5].base +  \
+                                                        ((global_queue_id) * \
+                                                         IRO[5].m1))
+#define USTORM_CQ_CONS_SIZE                            (IRO[5].size)
+/* Xstorm Integration Test Data */
+#define XSTORM_INTEG_TEST_DATA_OFFSET                  (IRO[6].base)
+#define XSTORM_INTEG_TEST_DATA_SIZE                    (IRO[6].size)
+/* Ystorm Integration Test Data */
+#define YSTORM_INTEG_TEST_DATA_OFFSET                  (IRO[7].base)
+#define YSTORM_INTEG_TEST_DATA_SIZE                    (IRO[7].size)
+/* Pstorm Integration Test Data */
+#define PSTORM_INTEG_TEST_DATA_OFFSET                  (IRO[8].base)
+#define PSTORM_INTEG_TEST_DATA_SIZE                    (IRO[8].size)
+/* Tstorm Integration Test Data */
+#define TSTORM_INTEG_TEST_DATA_OFFSET                  (IRO[9].base)
+#define TSTORM_INTEG_TEST_DATA_SIZE                    (IRO[9].size)
+/* Mstorm Integration Test Data */
+#define MSTORM_INTEG_TEST_DATA_OFFSET                  (IRO[10].base)
+#define MSTORM_INTEG_TEST_DATA_SIZE                    (IRO[10].size)
+/* Ustorm Integration Test Data */
+#define USTORM_INTEG_TEST_DATA_OFFSET                  (IRO[11].base)
+#define USTORM_INTEG_TEST_DATA_SIZE                    (IRO[11].size)
+/* Tstorm producers */
+#define TSTORM_LL2_RX_PRODS_OFFSET(core_rx_queue_id)   (IRO[12].base + \
+                                                        ((core_rx_queue_id) * \
+                                                         IRO[12].m1))
+#define TSTORM_LL2_RX_PRODS_SIZE                       (IRO[12].size)
+/* Tstorm LiteL2 queue statistics */
+#define CORE_LL2_TSTORM_PER_QUEUE_STAT_OFFSET(core_rx_q_id) (IRO[13].base + \
+                                                            ((core_rx_q_id) * \
+                                                             IRO[13].m1))
+#define CORE_LL2_TSTORM_PER_QUEUE_STAT_SIZE            (IRO[13].size)
+/* Ustorm LiteL2 queue statistics */
+#define CORE_LL2_USTORM_PER_QUEUE_STAT_OFFSET(core_rx_q_id) (IRO[14].base + \
+                                                            ((core_rx_q_id) * \
+                                                             IRO[14].m1))
+#define CORE_LL2_USTORM_PER_QUEUE_STAT_SIZE            (IRO[14].size)
+/* Pstorm LiteL2 queue statistics */
+#define CORE_LL2_PSTORM_PER_QUEUE_STAT_OFFSET(core_txst_id) (IRO[15].base + \
+                                                            ((core_txst_id) * \
+                                                             IRO[15].m1))
+#define CORE_LL2_PSTORM_PER_QUEUE_STAT_SIZE            (IRO[15].size)
+/* Mstorm queue statistics */
+#define MSTORM_QUEUE_STAT_OFFSET(stat_counter_id) (IRO[16].base + \
+                                                  ((stat_counter_id) * \
+                                                   IRO[16].m1))
+#define MSTORM_QUEUE_STAT_SIZE                         (IRO[16].size)
+/* Mstorm producers */
+#define MSTORM_PRODS_OFFSET(queue_id)                  (IRO[17].base + \
+                                                        ((queue_id) *  \
+                                                         IRO[17].m1))
+#define MSTORM_PRODS_SIZE                              (IRO[17].size)
+/* TPA agregation timeout in us resolution (on ASIC) */
+#define MSTORM_TPA_TIMEOUT_US_OFFSET                   (IRO[18].base)
+#define MSTORM_TPA_TIMEOUT_US_SIZE                     (IRO[18].size)
+/* Ustorm queue statistics */
+#define USTORM_QUEUE_STAT_OFFSET(stat_counter_id)      (IRO[19].base + \
+                                                       ((stat_counter_id) * \
+                                                        IRO[19].m1))
+#define USTORM_QUEUE_STAT_SIZE                         (IRO[19].size)
+/* Ustorm queue zone */
+#define USTORM_ETH_QUEUE_ZONE_OFFSET(queue_id)         (IRO[20].base + \
+                                                        ((queue_id) *  \
+                                                         IRO[20].m1))
+#define USTORM_ETH_QUEUE_ZONE_SIZE                     (IRO[20].size)
+/* Pstorm queue statistics */
+#define PSTORM_QUEUE_STAT_OFFSET(stat_counter_id)      (IRO[21].base + \
+                                                        ((stat_counter_id) * \
+                                                         IRO[21].m1))
+#define PSTORM_QUEUE_STAT_SIZE                         (IRO[21].size)
+/* Tstorm last parser message */
+#define TSTORM_ETH_PRS_INPUT_OFFSET(pf_id)             (IRO[22].base + \
+                                                        ((pf_id) *     \
+                                                         IRO[22].m1))
+#define TSTORM_ETH_PRS_INPUT_SIZE                      (IRO[22].size)
+/* Ystorm queue zone */
+#define YSTORM_ETH_QUEUE_ZONE_OFFSET(queue_id)         (IRO[23].base + \
+                                                        ((queue_id) *  \
+                                                         IRO[23].m1))
+#define YSTORM_ETH_QUEUE_ZONE_SIZE                     (IRO[23].size)
+/* Ystorm cqe producer */
+#define YSTORM_TOE_CQ_PROD_OFFSET(rss_id)              (IRO[24].base + \
+                                                        ((rss_id) *    \
+                                                         IRO[24].m1))
+#define YSTORM_TOE_CQ_PROD_SIZE                                (IRO[24].size)
+/* Ustorm cqe producer */
+#define USTORM_TOE_CQ_PROD_OFFSET(rss_id)              (IRO[25].base + \
+                                                        ((rss_id) *    \
+                                                         IRO[25].m1))
+#define USTORM_TOE_CQ_PROD_SIZE                                (IRO[25].size)
+/* Ustorm grq producer */
+#define USTORM_TOE_GRQ_PROD_OFFSET(pf_id)              (IRO[26].base + \
+                                                        ((pf_id) *     \
+                                                         IRO[26].m1))
+#define USTORM_TOE_GRQ_PROD_SIZE                       (IRO[26].size)
+/* Tstorm cmdq-cons of given command queue-id */
+#define TSTORM_SCSI_CMDQ_CONS_OFFSET(cmdq_queue_id)    (IRO[27].base + \
+                                                        ((cmdq_queue_id) * \
+                                                         IRO[27].m1))
+#define TSTORM_SCSI_CMDQ_CONS_SIZE                     (IRO[27].size)
+/* Mstorm rq-cons of given queue-id */
+#define MSTORM_SCSI_RQ_CONS_OFFSET(rq_queue_id)                (IRO[28].base + \
+                                                        ((rq_queue_id) * \
+                                                         IRO[28].m1))
+#define MSTORM_SCSI_RQ_CONS_SIZE                       (IRO[28].size)
+/* Pstorm RoCE statistics */
+#define PSTORM_ROCE_STAT_OFFSET(stat_counter_id)       (IRO[29].base + \
+                                                        ((stat_counter_id) * \
+                                                         IRO[29].m1))
+#define PSTORM_ROCE_STAT_SIZE                          (IRO[29].size)
+/* Tstorm RoCE statistics */
+#define TSTORM_ROCE_STAT_OFFSET(stat_counter_id)       (IRO[30].base + \
+                                                        ((stat_counter_id) * \
+                                                         IRO[30].m1))
+#define TSTORM_ROCE_STAT_SIZE                          (IRO[30].size)
+
+static const struct iro iro_arr[31] = {
+       { 0x10,   0x0,   0x0,   0x0,   0x8     },
+       { 0x4448, 0x60,  0x0,   0x0,   0x60    },
+       { 0x498,  0x8,   0x0,   0x0,   0x4     },
+       { 0x494,  0x0,   0x0,   0x0,   0x4     },
+       { 0x10,   0x8,   0x0,   0x0,   0x2     },
+       { 0x90,   0x8,   0x0,   0x0,   0x2     },
+       { 0x4540, 0x0,   0x0,   0x0,   0xf8    },
+       { 0x39e0, 0x0,   0x0,   0x0,   0xf8    },
+       { 0x2598, 0x0,   0x0,   0x0,   0xf8    },
+       { 0x4350, 0x0,   0x0,   0x0,   0xf8    },
+       { 0x52d0, 0x0,   0x0,   0x0,   0xf8    },
+       { 0x7a48, 0x0,   0x0,   0x0,   0xf8    },
+       { 0x100,  0x8,   0x0,   0x0,   0x8     },
+       { 0x5808, 0x10,  0x0,   0x0,   0x10    },
+       { 0xb100, 0x30,  0x0,   0x0,   0x30    },
+       { 0x95c0, 0x30,  0x0,   0x0,   0x30    },
+       { 0x54f8, 0x40,  0x0,   0x0,   0x40    },
+       { 0x200,  0x10,  0x0,   0x0,   0x8     },
+       { 0x9e70, 0x0,   0x0,   0x0,   0x4     },
+       { 0x7ca0, 0x40,  0x0,   0x0,   0x30    },
+       { 0xd00,  0x8,   0x0,   0x0,   0x8     },
+       { 0x2790, 0x80,  0x0,   0x0,   0x38    },
+       { 0xa520, 0xf0,  0x0,   0x0,   0xf0    },
+       { 0x80,   0x8,   0x0,   0x0,   0x8     },
+       { 0xac0,  0x8,   0x0,   0x0,   0x8     },
+       { 0x2580, 0x8,   0x0,   0x0,   0x8     },
+       { 0x2500, 0x8,   0x0,   0x0,   0x8     },
+       { 0x440,  0x8,   0x0,   0x0,   0x2     },
+       { 0x1800, 0x8,   0x0,   0x0,   0x2     },
+       { 0x27c8, 0x80,  0x0,   0x0,   0x10    },
+       { 0x4710, 0x10,  0x0,   0x0,   0x10    },
+};
+
+/* Runtime array offsets */
+#define DORQ_REG_PF_MAX_ICID_0_RT_OFFSET                                0
+#define DORQ_REG_PF_MAX_ICID_1_RT_OFFSET                                1
+#define DORQ_REG_PF_MAX_ICID_2_RT_OFFSET                                2
+#define DORQ_REG_PF_MAX_ICID_3_RT_OFFSET                                3
+#define DORQ_REG_PF_MAX_ICID_4_RT_OFFSET                                4
+#define DORQ_REG_PF_MAX_ICID_5_RT_OFFSET                                5
+#define DORQ_REG_PF_MAX_ICID_6_RT_OFFSET                                6
+#define DORQ_REG_PF_MAX_ICID_7_RT_OFFSET                                7
+#define DORQ_REG_VF_MAX_ICID_0_RT_OFFSET                                8
+#define DORQ_REG_VF_MAX_ICID_1_RT_OFFSET                                9
+#define DORQ_REG_VF_MAX_ICID_2_RT_OFFSET                                10
+#define DORQ_REG_VF_MAX_ICID_3_RT_OFFSET                                11
+#define DORQ_REG_VF_MAX_ICID_4_RT_OFFSET                                12
+#define DORQ_REG_VF_MAX_ICID_5_RT_OFFSET                                13
+#define DORQ_REG_VF_MAX_ICID_6_RT_OFFSET                                14
+#define DORQ_REG_VF_MAX_ICID_7_RT_OFFSET                                15
+#define DORQ_REG_PF_WAKE_ALL_RT_OFFSET                                  16
+#define IGU_REG_PF_CONFIGURATION_RT_OFFSET                              17
+#define IGU_REG_VF_CONFIGURATION_RT_OFFSET                              18
+#define IGU_REG_ATTN_MSG_ADDR_L_RT_OFFSET                               19
+#define IGU_REG_ATTN_MSG_ADDR_H_RT_OFFSET                               20
+#define IGU_REG_LEADING_EDGE_LATCH_RT_OFFSET                            21
+#define IGU_REG_TRAILING_EDGE_LATCH_RT_OFFSET                           22
+#define CAU_REG_CQE_AGG_UNIT_SIZE_RT_OFFSET                             23
+#define CAU_REG_SB_VAR_MEMORY_RT_OFFSET                                 760
+#define CAU_REG_SB_VAR_MEMORY_RT_SIZE                                   736
+#define CAU_REG_SB_VAR_MEMORY_RT_OFFSET                                 760
+#define CAU_REG_SB_VAR_MEMORY_RT_SIZE                                   736
+#define CAU_REG_SB_ADDR_MEMORY_RT_OFFSET                                1496
+#define CAU_REG_SB_ADDR_MEMORY_RT_SIZE                                  736
+#define CAU_REG_PI_MEMORY_RT_OFFSET                                     2232
+#define CAU_REG_PI_MEMORY_RT_SIZE                                       4416
+#define PRS_REG_SEARCH_RESP_INITIATOR_TYPE_RT_OFFSET                    6648
+#define PRS_REG_TASK_ID_MAX_INITIATOR_PF_RT_OFFSET                      6649
+#define PRS_REG_TASK_ID_MAX_INITIATOR_VF_RT_OFFSET                      6650
+#define PRS_REG_TASK_ID_MAX_TARGET_PF_RT_OFFSET                         6651
+#define PRS_REG_TASK_ID_MAX_TARGET_VF_RT_OFFSET                         6652
+#define PRS_REG_SEARCH_TCP_RT_OFFSET                                    6653
+#define PRS_REG_SEARCH_FCOE_RT_OFFSET                                   6654
+#define PRS_REG_SEARCH_ROCE_RT_OFFSET                                   6655
+#define PRS_REG_ROCE_DEST_QP_MAX_VF_RT_OFFSET                           6656
+#define PRS_REG_ROCE_DEST_QP_MAX_PF_RT_OFFSET                           6657
+#define PRS_REG_SEARCH_OPENFLOW_RT_OFFSET                               6658
+#define PRS_REG_SEARCH_NON_IP_AS_OPENFLOW_RT_OFFSET                     6659
+#define PRS_REG_OPENFLOW_SUPPORT_ONLY_KNOWN_OVER_IP_RT_OFFSET           6660
+#define PRS_REG_OPENFLOW_SEARCH_KEY_MASK_RT_OFFSET                      6661
+#define PRS_REG_LIGHT_L2_ETHERTYPE_EN_RT_OFFSET                         6662
+#define SRC_REG_FIRSTFREE_RT_OFFSET                                     6663
+#define SRC_REG_FIRSTFREE_RT_SIZE                                       2
+#define SRC_REG_LASTFREE_RT_OFFSET                                      6665
+#define SRC_REG_LASTFREE_RT_SIZE                                        2
+#define SRC_REG_COUNTFREE_RT_OFFSET                                     6667
+#define SRC_REG_NUMBER_HASH_BITS_RT_OFFSET                              6668
+#define PSWRQ2_REG_CDUT_P_SIZE_RT_OFFSET                                6669
+#define PSWRQ2_REG_CDUC_P_SIZE_RT_OFFSET                                6670
+#define PSWRQ2_REG_TM_P_SIZE_RT_OFFSET                                  6671
+#define PSWRQ2_REG_QM_P_SIZE_RT_OFFSET                                  6672
+#define PSWRQ2_REG_SRC_P_SIZE_RT_OFFSET                                 6673
+#define PSWRQ2_REG_TM_FIRST_ILT_RT_OFFSET                               6674
+#define PSWRQ2_REG_TM_LAST_ILT_RT_OFFSET                                6675
+#define PSWRQ2_REG_QM_FIRST_ILT_RT_OFFSET                               6676
+#define PSWRQ2_REG_QM_LAST_ILT_RT_OFFSET                                6677
+#define PSWRQ2_REG_SRC_FIRST_ILT_RT_OFFSET                              6678
+#define PSWRQ2_REG_SRC_LAST_ILT_RT_OFFSET                               6679
+#define PSWRQ2_REG_CDUC_FIRST_ILT_RT_OFFSET                             6680
+#define PSWRQ2_REG_CDUC_LAST_ILT_RT_OFFSET                              6681
+#define PSWRQ2_REG_CDUT_FIRST_ILT_RT_OFFSET                             6682
+#define PSWRQ2_REG_CDUT_LAST_ILT_RT_OFFSET                              6683
+#define PSWRQ2_REG_TSDM_FIRST_ILT_RT_OFFSET                             6684
+#define PSWRQ2_REG_TSDM_LAST_ILT_RT_OFFSET                              6685
+#define PSWRQ2_REG_TM_NUMBER_OF_PF_BLOCKS_RT_OFFSET                     6686
+#define PSWRQ2_REG_CDUT_NUMBER_OF_PF_BLOCKS_RT_OFFSET                   6687
+#define PSWRQ2_REG_CDUC_NUMBER_OF_PF_BLOCKS_RT_OFFSET                   6688
+#define PSWRQ2_REG_TM_VF_BLOCKS_RT_OFFSET                               6689
+#define PSWRQ2_REG_CDUT_VF_BLOCKS_RT_OFFSET                             6690
+#define PSWRQ2_REG_CDUC_VF_BLOCKS_RT_OFFSET                             6691
+#define PSWRQ2_REG_TM_BLOCKS_FACTOR_RT_OFFSET                           6692
+#define PSWRQ2_REG_CDUT_BLOCKS_FACTOR_RT_OFFSET                         6693
+#define PSWRQ2_REG_CDUC_BLOCKS_FACTOR_RT_OFFSET                         6694
+#define PSWRQ2_REG_VF_BASE_RT_OFFSET                                    6695
+#define PSWRQ2_REG_VF_LAST_ILT_RT_OFFSET                                6696
+#define PSWRQ2_REG_WR_MBS0_RT_OFFSET                                    6697
+#define PSWRQ2_REG_RD_MBS0_RT_OFFSET                                    6698
+#define PSWRQ2_REG_DRAM_ALIGN_WR_RT_OFFSET                              6699
+#define PSWRQ2_REG_DRAM_ALIGN_RD_RT_OFFSET                              6700
+#define PSWRQ2_REG_ILT_MEMORY_RT_OFFSET                                 6701
+#define PSWRQ2_REG_ILT_MEMORY_RT_SIZE                                   22000
+#define PGLUE_REG_B_VF_BASE_RT_OFFSET                                   28701
+#define PGLUE_REG_B_CACHE_LINE_SIZE_RT_OFFSET                           28702
+#define PGLUE_REG_B_PF_BAR0_SIZE_RT_OFFSET                              28703
+#define PGLUE_REG_B_PF_BAR1_SIZE_RT_OFFSET                              28704
+#define PGLUE_REG_B_VF_BAR1_SIZE_RT_OFFSET                              28705
+#define TM_REG_VF_ENABLE_CONN_RT_OFFSET                                 28706
+#define TM_REG_PF_ENABLE_CONN_RT_OFFSET                                 28707
+#define TM_REG_PF_ENABLE_TASK_RT_OFFSET                                 28708
+#define TM_REG_GROUP_SIZE_RESOLUTION_CONN_RT_OFFSET                     28709
+#define TM_REG_GROUP_SIZE_RESOLUTION_TASK_RT_OFFSET                     28710
+#define TM_REG_CONFIG_CONN_MEM_RT_OFFSET                                28711
+#define TM_REG_CONFIG_CONN_MEM_RT_SIZE                                  416
+#define TM_REG_CONFIG_TASK_MEM_RT_OFFSET                                29127
+#define TM_REG_CONFIG_TASK_MEM_RT_SIZE                                  512
+#define QM_REG_MAXPQSIZE_0_RT_OFFSET                                    29639
+#define QM_REG_MAXPQSIZE_1_RT_OFFSET                                    29640
+#define QM_REG_MAXPQSIZE_2_RT_OFFSET                                    29641
+#define QM_REG_MAXPQSIZETXSEL_0_RT_OFFSET                               29642
+#define QM_REG_MAXPQSIZETXSEL_1_RT_OFFSET                               29643
+#define QM_REG_MAXPQSIZETXSEL_2_RT_OFFSET                               29644
+#define QM_REG_MAXPQSIZETXSEL_3_RT_OFFSET                               29645
+#define QM_REG_MAXPQSIZETXSEL_4_RT_OFFSET                               29646
+#define QM_REG_MAXPQSIZETXSEL_5_RT_OFFSET                               29647
+#define QM_REG_MAXPQSIZETXSEL_6_RT_OFFSET                               29648
+#define QM_REG_MAXPQSIZETXSEL_7_RT_OFFSET                               29649
+#define QM_REG_MAXPQSIZETXSEL_8_RT_OFFSET                               29650
+#define QM_REG_MAXPQSIZETXSEL_9_RT_OFFSET                               29651
+#define QM_REG_MAXPQSIZETXSEL_10_RT_OFFSET                              29652
+#define QM_REG_MAXPQSIZETXSEL_11_RT_OFFSET                              29653
+#define QM_REG_MAXPQSIZETXSEL_12_RT_OFFSET                              29654
+#define QM_REG_MAXPQSIZETXSEL_13_RT_OFFSET                              29655
+#define QM_REG_MAXPQSIZETXSEL_14_RT_OFFSET                              29656
+#define QM_REG_MAXPQSIZETXSEL_15_RT_OFFSET                              29657
+#define QM_REG_MAXPQSIZETXSEL_16_RT_OFFSET                              29658
+#define QM_REG_MAXPQSIZETXSEL_17_RT_OFFSET                              29659
+#define QM_REG_MAXPQSIZETXSEL_18_RT_OFFSET                              29660
+#define QM_REG_MAXPQSIZETXSEL_19_RT_OFFSET                              29661
+#define QM_REG_MAXPQSIZETXSEL_20_RT_OFFSET                              29662
+#define QM_REG_MAXPQSIZETXSEL_21_RT_OFFSET                              29663
+#define QM_REG_MAXPQSIZETXSEL_22_RT_OFFSET                              29664
+#define QM_REG_MAXPQSIZETXSEL_23_RT_OFFSET                              29665
+#define QM_REG_MAXPQSIZETXSEL_24_RT_OFFSET                              29666
+#define QM_REG_MAXPQSIZETXSEL_25_RT_OFFSET                              29667
+#define QM_REG_MAXPQSIZETXSEL_26_RT_OFFSET                              29668
+#define QM_REG_MAXPQSIZETXSEL_27_RT_OFFSET                              29669
+#define QM_REG_MAXPQSIZETXSEL_28_RT_OFFSET                              29670
+#define QM_REG_MAXPQSIZETXSEL_29_RT_OFFSET                              29671
+#define QM_REG_MAXPQSIZETXSEL_30_RT_OFFSET                              29672
+#define QM_REG_MAXPQSIZETXSEL_31_RT_OFFSET                              29673
+#define QM_REG_MAXPQSIZETXSEL_32_RT_OFFSET                              29674
+#define QM_REG_MAXPQSIZETXSEL_33_RT_OFFSET                              29675
+#define QM_REG_MAXPQSIZETXSEL_34_RT_OFFSET                              29676
+#define QM_REG_MAXPQSIZETXSEL_35_RT_OFFSET                              29677
+#define QM_REG_MAXPQSIZETXSEL_36_RT_OFFSET                              29678
+#define QM_REG_MAXPQSIZETXSEL_37_RT_OFFSET                              29679
+#define QM_REG_MAXPQSIZETXSEL_38_RT_OFFSET                              29680
+#define QM_REG_MAXPQSIZETXSEL_39_RT_OFFSET                              29681
+#define QM_REG_MAXPQSIZETXSEL_40_RT_OFFSET                              29682
+#define QM_REG_MAXPQSIZETXSEL_41_RT_OFFSET                              29683
+#define QM_REG_MAXPQSIZETXSEL_42_RT_OFFSET                              29684
+#define QM_REG_MAXPQSIZETXSEL_43_RT_OFFSET                              29685
+#define QM_REG_MAXPQSIZETXSEL_44_RT_OFFSET                              29686
+#define QM_REG_MAXPQSIZETXSEL_45_RT_OFFSET                              29687
+#define QM_REG_MAXPQSIZETXSEL_46_RT_OFFSET                              29688
+#define QM_REG_MAXPQSIZETXSEL_47_RT_OFFSET                              29689
+#define QM_REG_MAXPQSIZETXSEL_48_RT_OFFSET                              29690
+#define QM_REG_MAXPQSIZETXSEL_49_RT_OFFSET                              29691
+#define QM_REG_MAXPQSIZETXSEL_50_RT_OFFSET                              29692
+#define QM_REG_MAXPQSIZETXSEL_51_RT_OFFSET                              29693
+#define QM_REG_MAXPQSIZETXSEL_52_RT_OFFSET                              29694
+#define QM_REG_MAXPQSIZETXSEL_53_RT_OFFSET                              29695
+#define QM_REG_MAXPQSIZETXSEL_54_RT_OFFSET                              29696
+#define QM_REG_MAXPQSIZETXSEL_55_RT_OFFSET                              29697
+#define QM_REG_MAXPQSIZETXSEL_56_RT_OFFSET                              29698
+#define QM_REG_MAXPQSIZETXSEL_57_RT_OFFSET                              29699
+#define QM_REG_MAXPQSIZETXSEL_58_RT_OFFSET                              29700
+#define QM_REG_MAXPQSIZETXSEL_59_RT_OFFSET                              29701
+#define QM_REG_MAXPQSIZETXSEL_60_RT_OFFSET                              29702
+#define QM_REG_MAXPQSIZETXSEL_61_RT_OFFSET                              29703
+#define QM_REG_MAXPQSIZETXSEL_62_RT_OFFSET                              29704
+#define QM_REG_MAXPQSIZETXSEL_63_RT_OFFSET                              29705
+#define QM_REG_BASEADDROTHERPQ_RT_OFFSET                                29706
+#define QM_REG_BASEADDROTHERPQ_RT_SIZE                                  128
+#define QM_REG_VOQCRDLINE_RT_OFFSET                                     29834
+#define QM_REG_VOQCRDLINE_RT_SIZE                                       20
+#define QM_REG_VOQINITCRDLINE_RT_OFFSET                                 29854
+#define QM_REG_VOQINITCRDLINE_RT_SIZE                                   20
+#define QM_REG_AFULLQMBYPTHRPFWFQ_RT_OFFSET                             29874
+#define QM_REG_AFULLQMBYPTHRVPWFQ_RT_OFFSET                             29875
+#define QM_REG_AFULLQMBYPTHRPFRL_RT_OFFSET                              29876
+#define QM_REG_AFULLQMBYPTHRGLBLRL_RT_OFFSET                            29877
+#define QM_REG_AFULLOPRTNSTCCRDMASK_RT_OFFSET                           29878
+#define QM_REG_WRROTHERPQGRP_0_RT_OFFSET                                29879
+#define QM_REG_WRROTHERPQGRP_1_RT_OFFSET                                29880
+#define QM_REG_WRROTHERPQGRP_2_RT_OFFSET                                29881
+#define QM_REG_WRROTHERPQGRP_3_RT_OFFSET                                29882
+#define QM_REG_WRROTHERPQGRP_4_RT_OFFSET                                29883
+#define QM_REG_WRROTHERPQGRP_5_RT_OFFSET                                29884
+#define QM_REG_WRROTHERPQGRP_6_RT_OFFSET                                29885
+#define QM_REG_WRROTHERPQGRP_7_RT_OFFSET                                29886
+#define QM_REG_WRROTHERPQGRP_8_RT_OFFSET                                29887
+#define QM_REG_WRROTHERPQGRP_9_RT_OFFSET                                29888
+#define QM_REG_WRROTHERPQGRP_10_RT_OFFSET                               29889
+#define QM_REG_WRROTHERPQGRP_11_RT_OFFSET                               29890
+#define QM_REG_WRROTHERPQGRP_12_RT_OFFSET                               29891
+#define QM_REG_WRROTHERPQGRP_13_RT_OFFSET                               29892
+#define QM_REG_WRROTHERPQGRP_14_RT_OFFSET                               29893
+#define QM_REG_WRROTHERPQGRP_15_RT_OFFSET                               29894
+#define QM_REG_WRROTHERGRPWEIGHT_0_RT_OFFSET                            29895
+#define QM_REG_WRROTHERGRPWEIGHT_1_RT_OFFSET                            29896
+#define QM_REG_WRROTHERGRPWEIGHT_2_RT_OFFSET                            29897
+#define QM_REG_WRROTHERGRPWEIGHT_3_RT_OFFSET                            29898
+#define QM_REG_WRRTXGRPWEIGHT_0_RT_OFFSET                               29899
+#define QM_REG_WRRTXGRPWEIGHT_1_RT_OFFSET                               29900
+#define QM_REG_PQTX2PF_0_RT_OFFSET                                      29901
+#define QM_REG_PQTX2PF_1_RT_OFFSET                                      29902
+#define QM_REG_PQTX2PF_2_RT_OFFSET                                      29903
+#define QM_REG_PQTX2PF_3_RT_OFFSET                                      29904
+#define QM_REG_PQTX2PF_4_RT_OFFSET                                      29905
+#define QM_REG_PQTX2PF_5_RT_OFFSET                                      29906
+#define QM_REG_PQTX2PF_6_RT_OFFSET                                      29907
+#define QM_REG_PQTX2PF_7_RT_OFFSET                                      29908
+#define QM_REG_PQTX2PF_8_RT_OFFSET                                      29909
+#define QM_REG_PQTX2PF_9_RT_OFFSET                                      29910
+#define QM_REG_PQTX2PF_10_RT_OFFSET                                     29911
+#define QM_REG_PQTX2PF_11_RT_OFFSET                                     29912
+#define QM_REG_PQTX2PF_12_RT_OFFSET                                     29913
+#define QM_REG_PQTX2PF_13_RT_OFFSET                                     29914
+#define QM_REG_PQTX2PF_14_RT_OFFSET                                     29915
+#define QM_REG_PQTX2PF_15_RT_OFFSET                                     29916
+#define QM_REG_PQTX2PF_16_RT_OFFSET                                     29917
+#define QM_REG_PQTX2PF_17_RT_OFFSET                                     29918
+#define QM_REG_PQTX2PF_18_RT_OFFSET                                     29919
+#define QM_REG_PQTX2PF_19_RT_OFFSET                                     29920
+#define QM_REG_PQTX2PF_20_RT_OFFSET                                     29921
+#define QM_REG_PQTX2PF_21_RT_OFFSET                                     29922
+#define QM_REG_PQTX2PF_22_RT_OFFSET                                     29923
+#define QM_REG_PQTX2PF_23_RT_OFFSET                                     29924
+#define QM_REG_PQTX2PF_24_RT_OFFSET                                     29925
+#define QM_REG_PQTX2PF_25_RT_OFFSET                                     29926
+#define QM_REG_PQTX2PF_26_RT_OFFSET                                     29927
+#define QM_REG_PQTX2PF_27_RT_OFFSET                                     29928
+#define QM_REG_PQTX2PF_28_RT_OFFSET                                     29929
+#define QM_REG_PQTX2PF_29_RT_OFFSET                                     29930
+#define QM_REG_PQTX2PF_30_RT_OFFSET                                     29931
+#define QM_REG_PQTX2PF_31_RT_OFFSET                                     29932
+#define QM_REG_PQTX2PF_32_RT_OFFSET                                     29933
+#define QM_REG_PQTX2PF_33_RT_OFFSET                                     29934
+#define QM_REG_PQTX2PF_34_RT_OFFSET                                     29935
+#define QM_REG_PQTX2PF_35_RT_OFFSET                                     29936
+#define QM_REG_PQTX2PF_36_RT_OFFSET                                     29937
+#define QM_REG_PQTX2PF_37_RT_OFFSET                                     29938
+#define QM_REG_PQTX2PF_38_RT_OFFSET                                     29939
+#define QM_REG_PQTX2PF_39_RT_OFFSET                                     29940
+#define QM_REG_PQTX2PF_40_RT_OFFSET                                     29941
+#define QM_REG_PQTX2PF_41_RT_OFFSET                                     29942
+#define QM_REG_PQTX2PF_42_RT_OFFSET                                     29943
+#define QM_REG_PQTX2PF_43_RT_OFFSET                                     29944
+#define QM_REG_PQTX2PF_44_RT_OFFSET                                     29945
+#define QM_REG_PQTX2PF_45_RT_OFFSET                                     29946
+#define QM_REG_PQTX2PF_46_RT_OFFSET                                     29947
+#define QM_REG_PQTX2PF_47_RT_OFFSET                                     29948
+#define QM_REG_PQTX2PF_48_RT_OFFSET                                     29949
+#define QM_REG_PQTX2PF_49_RT_OFFSET                                     29950
+#define QM_REG_PQTX2PF_50_RT_OFFSET                                     29951
+#define QM_REG_PQTX2PF_51_RT_OFFSET                                     29952
+#define QM_REG_PQTX2PF_52_RT_OFFSET                                     29953
+#define QM_REG_PQTX2PF_53_RT_OFFSET                                     29954
+#define QM_REG_PQTX2PF_54_RT_OFFSET                                     29955
+#define QM_REG_PQTX2PF_55_RT_OFFSET                                     29956
+#define QM_REG_PQTX2PF_56_RT_OFFSET                                     29957
+#define QM_REG_PQTX2PF_57_RT_OFFSET                                     29958
+#define QM_REG_PQTX2PF_58_RT_OFFSET                                     29959
+#define QM_REG_PQTX2PF_59_RT_OFFSET                                     29960
+#define QM_REG_PQTX2PF_60_RT_OFFSET                                     29961
+#define QM_REG_PQTX2PF_61_RT_OFFSET                                     29962
+#define QM_REG_PQTX2PF_62_RT_OFFSET                                     29963
+#define QM_REG_PQTX2PF_63_RT_OFFSET                                     29964
+#define QM_REG_PQOTHER2PF_0_RT_OFFSET                                   29965
+#define QM_REG_PQOTHER2PF_1_RT_OFFSET                                   29966
+#define QM_REG_PQOTHER2PF_2_RT_OFFSET                                   29967
+#define QM_REG_PQOTHER2PF_3_RT_OFFSET                                   29968
+#define QM_REG_PQOTHER2PF_4_RT_OFFSET                                   29969
+#define QM_REG_PQOTHER2PF_5_RT_OFFSET                                   29970
+#define QM_REG_PQOTHER2PF_6_RT_OFFSET                                   29971
+#define QM_REG_PQOTHER2PF_7_RT_OFFSET                                   29972
+#define QM_REG_PQOTHER2PF_8_RT_OFFSET                                   29973
+#define QM_REG_PQOTHER2PF_9_RT_OFFSET                                   29974
+#define QM_REG_PQOTHER2PF_10_RT_OFFSET                                  29975
+#define QM_REG_PQOTHER2PF_11_RT_OFFSET                                  29976
+#define QM_REG_PQOTHER2PF_12_RT_OFFSET                                  29977
+#define QM_REG_PQOTHER2PF_13_RT_OFFSET                                  29978
+#define QM_REG_PQOTHER2PF_14_RT_OFFSET                                  29979
+#define QM_REG_PQOTHER2PF_15_RT_OFFSET                                  29980
+#define QM_REG_RLGLBLPERIOD_0_RT_OFFSET                                 29981
+#define QM_REG_RLGLBLPERIOD_1_RT_OFFSET                                 29982
+#define QM_REG_RLGLBLPERIODTIMER_0_RT_OFFSET                            29983
+#define QM_REG_RLGLBLPERIODTIMER_1_RT_OFFSET                            29984
+#define QM_REG_RLGLBLPERIODSEL_0_RT_OFFSET                              29985
+#define QM_REG_RLGLBLPERIODSEL_1_RT_OFFSET                              29986
+#define QM_REG_RLGLBLPERIODSEL_2_RT_OFFSET                              29987
+#define QM_REG_RLGLBLPERIODSEL_3_RT_OFFSET                              29988
+#define QM_REG_RLGLBLPERIODSEL_4_RT_OFFSET                              29989
+#define QM_REG_RLGLBLPERIODSEL_5_RT_OFFSET                              29990
+#define QM_REG_RLGLBLPERIODSEL_6_RT_OFFSET                              29991
+#define QM_REG_RLGLBLPERIODSEL_7_RT_OFFSET                              29992
+#define QM_REG_RLGLBLINCVAL_RT_OFFSET                                   29993
+#define QM_REG_RLGLBLINCVAL_RT_SIZE                                     256
+#define QM_REG_RLGLBLUPPERBOUND_RT_OFFSET                               30249
+#define QM_REG_RLGLBLUPPERBOUND_RT_SIZE                                 256
+#define QM_REG_RLGLBLCRD_RT_OFFSET                                      30505
+#define QM_REG_RLGLBLCRD_RT_SIZE                                        256
+#define QM_REG_RLGLBLENABLE_RT_OFFSET                                   30761
+#define QM_REG_RLPFPERIOD_RT_OFFSET                                     30762
+#define QM_REG_RLPFPERIODTIMER_RT_OFFSET                                30763
+#define QM_REG_RLPFINCVAL_RT_OFFSET                                     30764
+#define QM_REG_RLPFINCVAL_RT_SIZE                                       16
+#define QM_REG_RLPFUPPERBOUND_RT_OFFSET                                 30780
+#define QM_REG_RLPFUPPERBOUND_RT_SIZE                                   16
+#define QM_REG_RLPFCRD_RT_OFFSET                                        30796
+#define QM_REG_RLPFCRD_RT_SIZE                                          16
+#define QM_REG_RLPFENABLE_RT_OFFSET                                     30812
+#define QM_REG_RLPFVOQENABLE_RT_OFFSET                                  30813
+#define QM_REG_WFQPFWEIGHT_RT_OFFSET                                    30814
+#define QM_REG_WFQPFWEIGHT_RT_SIZE                                      16
+#define QM_REG_WFQPFUPPERBOUND_RT_OFFSET                                30830
+#define QM_REG_WFQPFUPPERBOUND_RT_SIZE                                  16
+#define QM_REG_WFQPFCRD_RT_OFFSET                                       30846
+#define QM_REG_WFQPFCRD_RT_SIZE                                         160
+#define QM_REG_WFQPFENABLE_RT_OFFSET                                    31006
+#define QM_REG_WFQVPENABLE_RT_OFFSET                                    31007
+#define QM_REG_BASEADDRTXPQ_RT_OFFSET                                   31008
+#define QM_REG_BASEADDRTXPQ_RT_SIZE                                     512
+#define QM_REG_TXPQMAP_RT_OFFSET                                        31520
+#define QM_REG_TXPQMAP_RT_SIZE                                          512
+#define QM_REG_WFQVPWEIGHT_RT_OFFSET                                    32032
+#define QM_REG_WFQVPWEIGHT_RT_SIZE                                      512
+#define QM_REG_WFQVPUPPERBOUND_RT_OFFSET                                32544
+#define QM_REG_WFQVPUPPERBOUND_RT_SIZE                                  512
+#define QM_REG_WFQVPCRD_RT_OFFSET                                       33056
+#define QM_REG_WFQVPCRD_RT_SIZE                                         512
+#define QM_REG_WFQVPMAP_RT_OFFSET                                       33568
+#define QM_REG_WFQVPMAP_RT_SIZE                                         512
+#define QM_REG_WFQPFCRD_MSB_RT_OFFSET                                   34080
+#define QM_REG_WFQPFCRD_MSB_RT_SIZE                                     160
+#define NIG_REG_LLH_CLS_TYPE_DUALMODE_RT_OFFSET                         34240
+#define NIG_REG_OUTER_TAG_VALUE_LIST0_RT_OFFSET                         34241
+#define NIG_REG_OUTER_TAG_VALUE_LIST1_RT_OFFSET                         34242
+#define NIG_REG_OUTER_TAG_VALUE_LIST2_RT_OFFSET                         34243
+#define NIG_REG_OUTER_TAG_VALUE_LIST3_RT_OFFSET                         34244
+#define NIG_REG_OUTER_TAG_VALUE_MASK_RT_OFFSET                          34245
+#define NIG_REG_LLH_FUNC_TAGMAC_CLS_TYPE_RT_OFFSET                      34246
+#define NIG_REG_LLH_FUNC_TAG_EN_RT_OFFSET                               34247
+#define NIG_REG_LLH_FUNC_TAG_EN_RT_SIZE                                 4
+#define NIG_REG_LLH_FUNC_TAG_HDR_SEL_RT_OFFSET                          34251
+#define NIG_REG_LLH_FUNC_TAG_HDR_SEL_RT_SIZE                            4
+#define NIG_REG_LLH_FUNC_TAG_VALUE_RT_OFFSET                            34255
+#define NIG_REG_LLH_FUNC_TAG_VALUE_RT_SIZE                              4
+#define NIG_REG_LLH_FUNC_NO_TAG_RT_OFFSET                               34259
+#define NIG_REG_LLH_FUNC_FILTER_VALUE_RT_OFFSET                         34260
+#define NIG_REG_LLH_FUNC_FILTER_VALUE_RT_SIZE                           32
+#define NIG_REG_LLH_FUNC_FILTER_EN_RT_OFFSET                            34292
+#define NIG_REG_LLH_FUNC_FILTER_EN_RT_SIZE                              16
+#define NIG_REG_LLH_FUNC_FILTER_MODE_RT_OFFSET                          34308
+#define NIG_REG_LLH_FUNC_FILTER_MODE_RT_SIZE                            16
+#define NIG_REG_LLH_FUNC_FILTER_PROTOCOL_TYPE_RT_OFFSET                 34324
+#define NIG_REG_LLH_FUNC_FILTER_PROTOCOL_TYPE_RT_SIZE                   16
+#define NIG_REG_LLH_FUNC_FILTER_HDR_SEL_RT_OFFSET                       34340
+#define NIG_REG_LLH_FUNC_FILTER_HDR_SEL_RT_SIZE                         16
+#define NIG_REG_TX_EDPM_CTRL_RT_OFFSET                                  34356
+#define CDU_REG_CID_ADDR_PARAMS_RT_OFFSET                               34357
+#define CDU_REG_SEGMENT0_PARAMS_RT_OFFSET                               34358
+#define CDU_REG_SEGMENT1_PARAMS_RT_OFFSET                               34359
+#define CDU_REG_PF_SEG0_TYPE_OFFSET_RT_OFFSET                           34360
+#define CDU_REG_PF_SEG1_TYPE_OFFSET_RT_OFFSET                           34361
+#define CDU_REG_PF_SEG2_TYPE_OFFSET_RT_OFFSET                           34362
+#define CDU_REG_PF_SEG3_TYPE_OFFSET_RT_OFFSET                           34363
+#define CDU_REG_PF_FL_SEG0_TYPE_OFFSET_RT_OFFSET                        34364
+#define CDU_REG_PF_FL_SEG1_TYPE_OFFSET_RT_OFFSET                        34365
+#define CDU_REG_PF_FL_SEG2_TYPE_OFFSET_RT_OFFSET                        34366
+#define CDU_REG_PF_FL_SEG3_TYPE_OFFSET_RT_OFFSET                        34367
+#define CDU_REG_VF_SEG_TYPE_OFFSET_RT_OFFSET                            34368
+#define CDU_REG_VF_FL_SEG_TYPE_OFFSET_RT_OFFSET                         34369
+#define PBF_REG_BTB_SHARED_AREA_SIZE_RT_OFFSET                          34370
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ0_RT_OFFSET                        34371
+#define PBF_REG_BTB_GUARANTEED_VOQ0_RT_OFFSET                           34372
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ0_RT_OFFSET                    34373
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ1_RT_OFFSET                        34374
+#define PBF_REG_BTB_GUARANTEED_VOQ1_RT_OFFSET                           34375
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ1_RT_OFFSET                    34376
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ2_RT_OFFSET                        34377
+#define PBF_REG_BTB_GUARANTEED_VOQ2_RT_OFFSET                           34378
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ2_RT_OFFSET                    34379
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ3_RT_OFFSET                        34380
+#define PBF_REG_BTB_GUARANTEED_VOQ3_RT_OFFSET                           34381
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ3_RT_OFFSET                    34382
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ4_RT_OFFSET                        34383
+#define PBF_REG_BTB_GUARANTEED_VOQ4_RT_OFFSET                           34384
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ4_RT_OFFSET                    34385
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ5_RT_OFFSET                        34386
+#define PBF_REG_BTB_GUARANTEED_VOQ5_RT_OFFSET                           34387
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ5_RT_OFFSET                    34388
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ6_RT_OFFSET                        34389
+#define PBF_REG_BTB_GUARANTEED_VOQ6_RT_OFFSET                           34390
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ6_RT_OFFSET                    34391
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ7_RT_OFFSET                        34392
+#define PBF_REG_BTB_GUARANTEED_VOQ7_RT_OFFSET                           34393
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ7_RT_OFFSET                    34394
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ8_RT_OFFSET                        34395
+#define PBF_REG_BTB_GUARANTEED_VOQ8_RT_OFFSET                           34396
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ8_RT_OFFSET                    34397
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ9_RT_OFFSET                        34398
+#define PBF_REG_BTB_GUARANTEED_VOQ9_RT_OFFSET                           34399
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ9_RT_OFFSET                    34400
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ10_RT_OFFSET                       34401
+#define PBF_REG_BTB_GUARANTEED_VOQ10_RT_OFFSET                          34402
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ10_RT_OFFSET                   34403
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ11_RT_OFFSET                       34404
+#define PBF_REG_BTB_GUARANTEED_VOQ11_RT_OFFSET                          34405
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ11_RT_OFFSET                   34406
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ12_RT_OFFSET                       34407
+#define PBF_REG_BTB_GUARANTEED_VOQ12_RT_OFFSET                          34408
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ12_RT_OFFSET                   34409
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ13_RT_OFFSET                       34410
+#define PBF_REG_BTB_GUARANTEED_VOQ13_RT_OFFSET                          34411
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ13_RT_OFFSET                   34412
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ14_RT_OFFSET                       34413
+#define PBF_REG_BTB_GUARANTEED_VOQ14_RT_OFFSET                          34414
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ14_RT_OFFSET                   34415
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ15_RT_OFFSET                       34416
+#define PBF_REG_BTB_GUARANTEED_VOQ15_RT_OFFSET                          34417
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ15_RT_OFFSET                   34418
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ16_RT_OFFSET                       34419
+#define PBF_REG_BTB_GUARANTEED_VOQ16_RT_OFFSET                          34420
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ16_RT_OFFSET                   34421
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ17_RT_OFFSET                       34422
+#define PBF_REG_BTB_GUARANTEED_VOQ17_RT_OFFSET                          34423
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ17_RT_OFFSET                   34424
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ18_RT_OFFSET                       34425
+#define PBF_REG_BTB_GUARANTEED_VOQ18_RT_OFFSET                          34426
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ18_RT_OFFSET                   34427
+#define PBF_REG_YCMD_QS_NUM_LINES_VOQ19_RT_OFFSET                       34428
+#define PBF_REG_BTB_GUARANTEED_VOQ19_RT_OFFSET                          34429
+#define PBF_REG_BTB_SHARED_AREA_SETUP_VOQ19_RT_OFFSET                   34430
+#define XCM_REG_CON_PHY_Q3_RT_OFFSET                                    34431
+
+#define RUNTIME_ARRAY_SIZE 34432
+
+/* The eth storm context for the Ystorm */
+struct ystorm_eth_conn_st_ctx {
+       __le32 reserved[4];
+};
+
+/* The eth storm context for the Pstorm */
+struct pstorm_eth_conn_st_ctx {
+       __le32 reserved[8];
+};
+
+/* The eth storm context for the Xstorm */
+struct xstorm_eth_conn_st_ctx {
+       __le32 reserved[60];
+};
+
+struct xstorm_eth_conn_ag_ctx {
+       u8      reserved0 /* cdu_validation */;
+       u8      eth_state /* state */;
+       u8      flags0;
+#define XSTORM_ETH_CONN_AG_CTX_EXIST_IN_QM0_MASK            0x1
+#define XSTORM_ETH_CONN_AG_CTX_EXIST_IN_QM0_SHIFT           0
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED1_MASK               0x1
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED1_SHIFT              1
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED2_MASK               0x1
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED2_SHIFT              2
+#define XSTORM_ETH_CONN_AG_CTX_EXIST_IN_QM3_MASK            0x1
+#define XSTORM_ETH_CONN_AG_CTX_EXIST_IN_QM3_SHIFT           3
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED3_MASK               0x1 /* bit4 */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED3_SHIFT              4
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED4_MASK               0x1
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED4_SHIFT              5
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED5_MASK               0x1 /* bit6 */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED5_SHIFT              6
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED6_MASK               0x1 /* bit7 */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED6_SHIFT              7
+       u8 flags1;
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED7_MASK               0x1 /* bit8 */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED7_SHIFT              0
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED8_MASK               0x1 /* bit9 */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED8_SHIFT              1
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED9_MASK               0x1 /* bit10 */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED9_SHIFT              2
+#define XSTORM_ETH_CONN_AG_CTX_BIT11_MASK                   0x1 /* bit11 */
+#define XSTORM_ETH_CONN_AG_CTX_BIT11_SHIFT                  3
+#define XSTORM_ETH_CONN_AG_CTX_BIT12_MASK                   0x1 /* bit12 */
+#define XSTORM_ETH_CONN_AG_CTX_BIT12_SHIFT                  4
+#define XSTORM_ETH_CONN_AG_CTX_BIT13_MASK                   0x1 /* bit13 */
+#define XSTORM_ETH_CONN_AG_CTX_BIT13_SHIFT                  5
+#define XSTORM_ETH_CONN_AG_CTX_TX_RULE_ACTIVE_MASK          0x1 /* bit14 */
+#define XSTORM_ETH_CONN_AG_CTX_TX_RULE_ACTIVE_SHIFT         6
+#define XSTORM_ETH_CONN_AG_CTX_DQ_CF_ACTIVE_MASK            0x1 /* bit15 */
+#define XSTORM_ETH_CONN_AG_CTX_DQ_CF_ACTIVE_SHIFT           7
+       u8 flags2;
+#define XSTORM_ETH_CONN_AG_CTX_CF0_MASK                     0x3 /* timer0cf */
+#define XSTORM_ETH_CONN_AG_CTX_CF0_SHIFT                    0
+#define XSTORM_ETH_CONN_AG_CTX_CF1_MASK                     0x3 /* timer1cf */
+#define XSTORM_ETH_CONN_AG_CTX_CF1_SHIFT                    2
+#define XSTORM_ETH_CONN_AG_CTX_CF2_MASK                     0x3 /* timer2cf */
+#define XSTORM_ETH_CONN_AG_CTX_CF2_SHIFT                    4
+#define XSTORM_ETH_CONN_AG_CTX_CF3_MASK                     0x3
+#define XSTORM_ETH_CONN_AG_CTX_CF3_SHIFT                    6
+       u8 flags3;
+#define XSTORM_ETH_CONN_AG_CTX_CF4_MASK                     0x3 /* cf4 */
+#define XSTORM_ETH_CONN_AG_CTX_CF4_SHIFT                    0
+#define XSTORM_ETH_CONN_AG_CTX_CF5_MASK                     0x3 /* cf5 */
+#define XSTORM_ETH_CONN_AG_CTX_CF5_SHIFT                    2
+#define XSTORM_ETH_CONN_AG_CTX_CF6_MASK                     0x3 /* cf6 */
+#define XSTORM_ETH_CONN_AG_CTX_CF6_SHIFT                    4
+#define XSTORM_ETH_CONN_AG_CTX_CF7_MASK                     0x3 /* cf7 */
+#define XSTORM_ETH_CONN_AG_CTX_CF7_SHIFT                    6
+       u8 flags4;
+#define XSTORM_ETH_CONN_AG_CTX_CF8_MASK                     0x3 /* cf8 */
+#define XSTORM_ETH_CONN_AG_CTX_CF8_SHIFT                    0
+#define XSTORM_ETH_CONN_AG_CTX_CF9_MASK                     0x3 /* cf9 */
+#define XSTORM_ETH_CONN_AG_CTX_CF9_SHIFT                    2
+#define XSTORM_ETH_CONN_AG_CTX_CF10_MASK                    0x3 /* cf10 */
+#define XSTORM_ETH_CONN_AG_CTX_CF10_SHIFT                   4
+#define XSTORM_ETH_CONN_AG_CTX_CF11_MASK                    0x3 /* cf11 */
+#define XSTORM_ETH_CONN_AG_CTX_CF11_SHIFT                   6
+       u8 flags5;
+#define XSTORM_ETH_CONN_AG_CTX_CF12_MASK                    0x3 /* cf12 */
+#define XSTORM_ETH_CONN_AG_CTX_CF12_SHIFT                   0
+#define XSTORM_ETH_CONN_AG_CTX_CF13_MASK                    0x3 /* cf13 */
+#define XSTORM_ETH_CONN_AG_CTX_CF13_SHIFT                   2
+#define XSTORM_ETH_CONN_AG_CTX_CF14_MASK                    0x3 /* cf14 */
+#define XSTORM_ETH_CONN_AG_CTX_CF14_SHIFT                   4
+#define XSTORM_ETH_CONN_AG_CTX_CF15_MASK                    0x3 /* cf15 */
+#define XSTORM_ETH_CONN_AG_CTX_CF15_SHIFT                   6
+       u8 flags6;
+#define XSTORM_ETH_CONN_AG_CTX_GO_TO_BD_CONS_CF_MASK        0x3 /* cf16 */
+#define XSTORM_ETH_CONN_AG_CTX_GO_TO_BD_CONS_CF_SHIFT       0
+#define XSTORM_ETH_CONN_AG_CTX_MULTI_UNICAST_CF_MASK        0x3
+#define XSTORM_ETH_CONN_AG_CTX_MULTI_UNICAST_CF_SHIFT       2
+#define XSTORM_ETH_CONN_AG_CTX_DQ_CF_MASK                   0x3 /* cf18 */
+#define XSTORM_ETH_CONN_AG_CTX_DQ_CF_SHIFT                  4
+#define XSTORM_ETH_CONN_AG_CTX_TERMINATE_CF_MASK            0x3 /* cf19 */
+#define XSTORM_ETH_CONN_AG_CTX_TERMINATE_CF_SHIFT           6
+       u8 flags7;
+#define XSTORM_ETH_CONN_AG_CTX_FLUSH_Q0_MASK                0x3 /* cf20 */
+#define XSTORM_ETH_CONN_AG_CTX_FLUSH_Q0_SHIFT               0
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED10_MASK              0x3 /* cf21 */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED10_SHIFT             2
+#define XSTORM_ETH_CONN_AG_CTX_SLOW_PATH_MASK               0x3 /* cf22 */
+#define XSTORM_ETH_CONN_AG_CTX_SLOW_PATH_SHIFT              4
+#define XSTORM_ETH_CONN_AG_CTX_CF0EN_MASK                   0x1 /* cf0en */
+#define XSTORM_ETH_CONN_AG_CTX_CF0EN_SHIFT                  6
+#define XSTORM_ETH_CONN_AG_CTX_CF1EN_MASK                   0x1 /* cf1en */
+#define XSTORM_ETH_CONN_AG_CTX_CF1EN_SHIFT                  7
+       u8 flags8;
+#define XSTORM_ETH_CONN_AG_CTX_CF2EN_MASK                   0x1 /* cf2en */
+#define XSTORM_ETH_CONN_AG_CTX_CF2EN_SHIFT                  0
+#define XSTORM_ETH_CONN_AG_CTX_CF3EN_MASK                   0x1 /* cf3en */
+#define XSTORM_ETH_CONN_AG_CTX_CF3EN_SHIFT                  1
+#define XSTORM_ETH_CONN_AG_CTX_CF4EN_MASK                   0x1 /* cf4en */
+#define XSTORM_ETH_CONN_AG_CTX_CF4EN_SHIFT                  2
+#define XSTORM_ETH_CONN_AG_CTX_CF5EN_MASK                   0x1 /* cf5en */
+#define XSTORM_ETH_CONN_AG_CTX_CF5EN_SHIFT                  3
+#define XSTORM_ETH_CONN_AG_CTX_CF6EN_MASK                   0x1 /* cf6en */
+#define XSTORM_ETH_CONN_AG_CTX_CF6EN_SHIFT                  4
+#define XSTORM_ETH_CONN_AG_CTX_CF7EN_MASK                   0x1 /* cf7en */
+#define XSTORM_ETH_CONN_AG_CTX_CF7EN_SHIFT                  5
+#define XSTORM_ETH_CONN_AG_CTX_CF8EN_MASK                   0x1 /* cf8en */
+#define XSTORM_ETH_CONN_AG_CTX_CF8EN_SHIFT                  6
+#define XSTORM_ETH_CONN_AG_CTX_CF9EN_MASK                   0x1 /* cf9en */
+#define XSTORM_ETH_CONN_AG_CTX_CF9EN_SHIFT                  7
+       u8 flags9;
+#define XSTORM_ETH_CONN_AG_CTX_CF10EN_MASK                  0x1 /* cf10en */
+#define XSTORM_ETH_CONN_AG_CTX_CF10EN_SHIFT                 0
+#define XSTORM_ETH_CONN_AG_CTX_CF11EN_MASK                  0x1 /* cf11en */
+#define XSTORM_ETH_CONN_AG_CTX_CF11EN_SHIFT                 1
+#define XSTORM_ETH_CONN_AG_CTX_CF12EN_MASK                  0x1 /* cf12en */
+#define XSTORM_ETH_CONN_AG_CTX_CF12EN_SHIFT                 2
+#define XSTORM_ETH_CONN_AG_CTX_CF13EN_MASK                  0x1 /* cf13en */
+#define XSTORM_ETH_CONN_AG_CTX_CF13EN_SHIFT                 3
+#define XSTORM_ETH_CONN_AG_CTX_CF14EN_MASK                  0x1 /* cf14en */
+#define XSTORM_ETH_CONN_AG_CTX_CF14EN_SHIFT                 4
+#define XSTORM_ETH_CONN_AG_CTX_CF15EN_MASK                  0x1 /* cf15en */
+#define XSTORM_ETH_CONN_AG_CTX_CF15EN_SHIFT                 5
+#define XSTORM_ETH_CONN_AG_CTX_GO_TO_BD_CONS_CF_EN_MASK     0x1 /* cf16en */
+#define XSTORM_ETH_CONN_AG_CTX_GO_TO_BD_CONS_CF_EN_SHIFT    6
+#define XSTORM_ETH_CONN_AG_CTX_MULTI_UNICAST_CF_EN_MASK     0x1
+#define XSTORM_ETH_CONN_AG_CTX_MULTI_UNICAST_CF_EN_SHIFT    7
+       u8 flags10;
+#define XSTORM_ETH_CONN_AG_CTX_DQ_CF_EN_MASK                0x1 /* cf18en */
+#define XSTORM_ETH_CONN_AG_CTX_DQ_CF_EN_SHIFT               0
+#define XSTORM_ETH_CONN_AG_CTX_TERMINATE_CF_EN_MASK         0x1 /* cf19en */
+#define XSTORM_ETH_CONN_AG_CTX_TERMINATE_CF_EN_SHIFT        1
+#define XSTORM_ETH_CONN_AG_CTX_FLUSH_Q0_EN_MASK             0x1 /* cf20en */
+#define XSTORM_ETH_CONN_AG_CTX_FLUSH_Q0_EN_SHIFT            2
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED11_MASK              0x1 /* cf21en */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED11_SHIFT             3
+#define XSTORM_ETH_CONN_AG_CTX_SLOW_PATH_EN_MASK            0x1 /* cf22en */
+#define XSTORM_ETH_CONN_AG_CTX_SLOW_PATH_EN_SHIFT           4
+#define XSTORM_ETH_CONN_AG_CTX_TPH_ENABLE_EN_RESERVED_MASK  0x1 /* cf23en */
+#define XSTORM_ETH_CONN_AG_CTX_TPH_ENABLE_EN_RESERVED_SHIFT 5
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED12_MASK              0x1 /* rule0en */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED12_SHIFT             6
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED13_MASK              0x1 /* rule1en */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED13_SHIFT             7
+       u8 flags11;
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED14_MASK              0x1 /* rule2en */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED14_SHIFT             0
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED15_MASK              0x1 /* rule3en */
+#define XSTORM_ETH_CONN_AG_CTX_RESERVED15_SHIFT             1
+#define XSTORM_ETH_CONN_AG_CTX_TX_DEC_RULE_EN_MASK          0x1 /* rule4en */
+#define XSTORM_ETH_CONN_AG_CTX_TX_DEC_RULE_EN_SHIFT         2
+#define XSTORM_ETH_CONN_AG_CTX_RULE5EN_MASK                 0x1 /* rule5en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE5EN_SHIFT                3
+#define XSTORM_ETH_CONN_AG_CTX_RULE6EN_MASK                 0x1 /* rule6en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE6EN_SHIFT                4
+#define XSTORM_ETH_CONN_AG_CTX_RULE7EN_MASK                 0x1 /* rule7en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE7EN_SHIFT                5
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED1_MASK            0x1 /* rule8en */
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED1_SHIFT           6
+#define XSTORM_ETH_CONN_AG_CTX_RULE9EN_MASK                 0x1 /* rule9en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE9EN_SHIFT                7
+       u8 flags12;
+#define XSTORM_ETH_CONN_AG_CTX_RULE10EN_MASK                0x1 /* rule10en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE10EN_SHIFT               0
+#define XSTORM_ETH_CONN_AG_CTX_RULE11EN_MASK                0x1 /* rule11en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE11EN_SHIFT               1
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED2_MASK            0x1 /* rule12en */
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED2_SHIFT           2
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED3_MASK            0x1 /* rule13en */
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED3_SHIFT           3
+#define XSTORM_ETH_CONN_AG_CTX_RULE14EN_MASK                0x1 /* rule14en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE14EN_SHIFT               4
+#define XSTORM_ETH_CONN_AG_CTX_RULE15EN_MASK                0x1 /* rule15en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE15EN_SHIFT               5
+#define XSTORM_ETH_CONN_AG_CTX_RULE16EN_MASK                0x1 /* rule16en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE16EN_SHIFT               6
+#define XSTORM_ETH_CONN_AG_CTX_RULE17EN_MASK                0x1 /* rule17en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE17EN_SHIFT               7
+       u8 flags13;
+#define XSTORM_ETH_CONN_AG_CTX_RULE18EN_MASK                0x1 /* rule18en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE18EN_SHIFT               0
+#define XSTORM_ETH_CONN_AG_CTX_RULE19EN_MASK                0x1 /* rule19en */
+#define XSTORM_ETH_CONN_AG_CTX_RULE19EN_SHIFT               1
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED4_MASK            0x1 /* rule20en */
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED4_SHIFT           2
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED5_MASK            0x1 /* rule21en */
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED5_SHIFT           3
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED6_MASK            0x1 /* rule22en */
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED6_SHIFT           4
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED7_MASK            0x1 /* rule23en */
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED7_SHIFT           5
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED8_MASK            0x1 /* rule24en */
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED8_SHIFT           6
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED9_MASK            0x1 /* rule25en */
+#define XSTORM_ETH_CONN_AG_CTX_A0_RESERVED9_SHIFT           7
+       u8 flags14;
+#define XSTORM_ETH_CONN_AG_CTX_EDPM_USE_EXT_HDR_MASK        0x1 /* bit16 */
+#define XSTORM_ETH_CONN_AG_CTX_EDPM_USE_EXT_HDR_SHIFT       0
+#define XSTORM_ETH_CONN_AG_CTX_EDPM_SEND_RAW_L3L4_MASK      0x1 /* bit17 */
+#define XSTORM_ETH_CONN_AG_CTX_EDPM_SEND_RAW_L3L4_SHIFT     1
+#define XSTORM_ETH_CONN_AG_CTX_EDPM_INBAND_PROP_HDR_MASK    0x1 /* bit18 */
+#define XSTORM_ETH_CONN_AG_CTX_EDPM_INBAND_PROP_HDR_SHIFT   2
+#define XSTORM_ETH_CONN_AG_CTX_EDPM_SEND_EXT_TUNNEL_MASK    0x1 /* bit19 */
+#define XSTORM_ETH_CONN_AG_CTX_EDPM_SEND_EXT_TUNNEL_SHIFT   3
+#define XSTORM_ETH_CONN_AG_CTX_L2_EDPM_ENABLE_MASK          0x1 /* bit20 */
+#define XSTORM_ETH_CONN_AG_CTX_L2_EDPM_ENABLE_SHIFT         4
+#define XSTORM_ETH_CONN_AG_CTX_ROCE_EDPM_ENABLE_MASK        0x1 /* bit21 */
+#define XSTORM_ETH_CONN_AG_CTX_ROCE_EDPM_ENABLE_SHIFT       5
+#define XSTORM_ETH_CONN_AG_CTX_TPH_ENABLE_MASK              0x3 /* cf23 */
+#define XSTORM_ETH_CONN_AG_CTX_TPH_ENABLE_SHIFT             6
+       u8      edpm_event_id /* byte2 */;
+       __le16  physical_q0 /* physical_q0 */;
+       __le16  word1 /* physical_q1 */;
+       __le16  edpm_num_bds /* physical_q2 */;
+       __le16  tx_bd_cons /* word3 */;
+       __le16  tx_bd_prod /* word4 */;
+       __le16  go_to_bd_cons /* word5 */;
+       __le16  conn_dpi /* conn_dpi */;
+       u8      byte3 /* byte3 */;
+       u8      byte4 /* byte4 */;
+       u8      byte5 /* byte5 */;
+       u8      byte6 /* byte6 */;
+       __le32  reg0 /* reg0 */;
+       __le32  reg1 /* reg1 */;
+       __le32  reg2 /* reg2 */;
+       __le32  reg3 /* reg3 */;
+       __le32  reg4 /* reg4 */;
+       __le32  reg5 /* cf_array0 */;
+       __le32  reg6 /* cf_array1 */;
+       __le16  word7 /* word7 */;
+       __le16  word8 /* word8 */;
+       __le16  word9 /* word9 */;
+       __le16  word10 /* word10 */;
+       __le32  reg7 /* reg7 */;
+       __le32  reg8 /* reg8 */;
+       __le32  reg9 /* reg9 */;
+       u8      byte7 /* byte7 */;
+       u8      byte8 /* byte8 */;
+       u8      byte9 /* byte9 */;
+       u8      byte10 /* byte10 */;
+       u8      byte11 /* byte11 */;
+       u8      byte12 /* byte12 */;
+       u8      byte13 /* byte13 */;
+       u8      byte14 /* byte14 */;
+       u8      byte15 /* byte15 */;
+       u8      byte16 /* byte16 */;
+       __le16  word11 /* word11 */;
+       __le32  reg10 /* reg10 */;
+       __le32  reg11 /* reg11 */;
+       __le32  reg12 /* reg12 */;
+       __le32  reg13 /* reg13 */;
+       __le32  reg14 /* reg14 */;
+       __le32  reg15 /* reg15 */;
+       __le32  reg16 /* reg16 */;
+       __le32  reg17 /* reg17 */;
+       __le32  reg18 /* reg18 */;
+       __le32  reg19 /* reg19 */;
+       __le16  word12 /* word12 */;
+       __le16  word13 /* word13 */;
+       __le16  word14 /* word14 */;
+       __le16  word15 /* word15 */;
+};
+
+/* The eth storm context for the Tstorm */
+struct tstorm_eth_conn_st_ctx {
+       __le32 reserved[4];
+};
+
+/* The eth storm context for the Mstorm */
+struct mstorm_eth_conn_st_ctx {
+       __le32 reserved[8];
+};
+
+/* The eth storm context for the Ustorm */
+struct ustorm_eth_conn_st_ctx {
+       __le32 reserved[40];
+};
+
+/* eth connection context */
+struct eth_conn_context {
+       struct ystorm_eth_conn_st_ctx   ystorm_st_context;
+       struct regpair                  ystorm_st_padding[2] /* padding */;
+       struct pstorm_eth_conn_st_ctx   pstorm_st_context;
+       struct regpair                  pstorm_st_padding[2] /* padding */;
+       struct xstorm_eth_conn_st_ctx   xstorm_st_context;
+       struct xstorm_eth_conn_ag_ctx   xstorm_ag_context;
+       struct tstorm_eth_conn_st_ctx   tstorm_st_context;
+       struct regpair                  tstorm_st_padding[2] /* padding */;
+       struct mstorm_eth_conn_st_ctx   mstorm_st_context;
+       struct ustorm_eth_conn_st_ctx   ustorm_st_context;
+};
+
+enum eth_filter_action {
+       ETH_FILTER_ACTION_REMOVE,
+       ETH_FILTER_ACTION_ADD,
+       ETH_FILTER_ACTION_REPLACE,
+       MAX_ETH_FILTER_ACTION
+};
+
+struct eth_filter_cmd {
+       u8      type /* Filter Type (MAC/VLAN/Pair/VNI) */;
+       u8      vport_id /* the vport id */;
+       u8      action /* filter command action: add/remove/replace */;
+       u8      reserved0;
+       __le32  vni;
+       __le16  mac_lsb;
+       __le16  mac_mid;
+       __le16  mac_msb;
+       __le16  vlan_id;
+};
+
+struct eth_filter_cmd_header {
+       u8      rx;
+       u8      tx;
+       u8      cmd_cnt;
+       u8      assert_on_error;
+       u8      reserved1[4];
+};
+
+enum eth_filter_type {
+       ETH_FILTER_TYPE_MAC,
+       ETH_FILTER_TYPE_VLAN,
+       ETH_FILTER_TYPE_PAIR,
+       ETH_FILTER_TYPE_INNER_MAC,
+       ETH_FILTER_TYPE_INNER_VLAN,
+       ETH_FILTER_TYPE_INNER_PAIR,
+       ETH_FILTER_TYPE_INNER_MAC_VNI_PAIR,
+       ETH_FILTER_TYPE_MAC_VNI_PAIR,
+       ETH_FILTER_TYPE_VNI,
+       MAX_ETH_FILTER_TYPE
+};
+
+enum eth_ramrod_cmd_id {
+       ETH_RAMROD_UNUSED,
+       ETH_RAMROD_VPORT_START /* VPort Start Ramrod */,
+       ETH_RAMROD_VPORT_UPDATE /* VPort Update Ramrod */,
+       ETH_RAMROD_VPORT_STOP /* VPort Stop Ramrod */,
+       ETH_RAMROD_RX_QUEUE_START /* RX Queue Start Ramrod */,
+       ETH_RAMROD_RX_QUEUE_STOP /* RX Queue Stop Ramrod */,
+       ETH_RAMROD_TX_QUEUE_START /* TX Queue Start Ramrod */,
+       ETH_RAMROD_TX_QUEUE_STOP /* TX Queue Stop Ramrod */,
+       ETH_RAMROD_FILTERS_UPDATE /* Add or Remove Mac/Vlan/Pair filters */,
+       ETH_RAMROD_RX_QUEUE_UPDATE /* RX Queue Update Ramrod */,
+       ETH_RAMROD_RESERVED,
+       ETH_RAMROD_RESERVED2,
+       ETH_RAMROD_RESERVED3,
+       ETH_RAMROD_RESERVED4,
+       ETH_RAMROD_RESERVED5,
+       ETH_RAMROD_RESERVED6,
+       ETH_RAMROD_RESERVED7,
+       ETH_RAMROD_RESERVED8,
+       MAX_ETH_RAMROD_CMD_ID
+};
+
+struct eth_vport_rss_config {
+       __le16 capabilities;
+#define ETH_VPORT_RSS_CONFIG_IPV4_CAPABILITY_MASK      0x1
+#define ETH_VPORT_RSS_CONFIG_IPV4_CAPABILITY_SHIFT       0
+#define ETH_VPORT_RSS_CONFIG_IPV6_CAPABILITY_MASK      0x1
+#define ETH_VPORT_RSS_CONFIG_IPV6_CAPABILITY_SHIFT       1
+#define ETH_VPORT_RSS_CONFIG_IPV4_TCP_CAPABILITY_MASK    0x1
+#define ETH_VPORT_RSS_CONFIG_IPV4_TCP_CAPABILITY_SHIFT   2
+#define ETH_VPORT_RSS_CONFIG_IPV6_TCP_CAPABILITY_MASK    0x1
+#define ETH_VPORT_RSS_CONFIG_IPV6_TCP_CAPABILITY_SHIFT   3
+#define ETH_VPORT_RSS_CONFIG_IPV4_UDP_CAPABILITY_MASK    0x1
+#define ETH_VPORT_RSS_CONFIG_IPV4_UDP_CAPABILITY_SHIFT   4
+#define ETH_VPORT_RSS_CONFIG_IPV6_UDP_CAPABILITY_MASK    0x1
+#define ETH_VPORT_RSS_CONFIG_IPV6_UDP_CAPABILITY_SHIFT   5
+#define ETH_VPORT_RSS_CONFIG_EN_5_TUPLE_CAPABILITY_MASK  0x1
+#define ETH_VPORT_RSS_CONFIG_EN_5_TUPLE_CAPABILITY_SHIFT 6
+#define ETH_VPORT_RSS_CONFIG_CALC_4TUP_TCP_FRAG_MASK     0x1
+#define ETH_VPORT_RSS_CONFIG_CALC_4TUP_TCP_FRAG_SHIFT    7
+#define ETH_VPORT_RSS_CONFIG_CALC_4TUP_UDP_FRAG_MASK     0x1
+#define ETH_VPORT_RSS_CONFIG_CALC_4TUP_UDP_FRAG_SHIFT    8
+#define ETH_VPORT_RSS_CONFIG_RESERVED0_MASK          0x7F
+#define ETH_VPORT_RSS_CONFIG_RESERVED0_SHIFT        9
+       u8      rss_id;
+       u8      rss_mode;
+       u8      update_rss_key;
+       u8      update_rss_ind_table;
+       u8      update_rss_capabilities;
+       u8      tbl_size;
+       __le32  reserved2[2];
+       __le16  indirection_table[ETH_RSS_IND_TABLE_ENTRIES_NUM];
+       __le32  rss_key[ETH_RSS_KEY_SIZE_REGS];
+       __le32  reserved3[2];
+};
+
+enum eth_vport_rss_mode {
+       ETH_VPORT_RSS_MODE_DISABLED,
+       ETH_VPORT_RSS_MODE_REGULAR,
+       MAX_ETH_VPORT_RSS_MODE
+};
+
+struct eth_vport_rx_mode {
+       __le16 state;
+#define ETH_VPORT_RX_MODE_UCAST_DROP_ALL_MASK    0x1
+#define ETH_VPORT_RX_MODE_UCAST_DROP_ALL_SHIFT  0
+#define ETH_VPORT_RX_MODE_UCAST_ACCEPT_ALL_MASK        0x1
+#define ETH_VPORT_RX_MODE_UCAST_ACCEPT_ALL_SHIFT       1
+#define ETH_VPORT_RX_MODE_UCAST_ACCEPT_UNMATCHED_MASK  0x1
+#define ETH_VPORT_RX_MODE_UCAST_ACCEPT_UNMATCHED_SHIFT 2
+#define ETH_VPORT_RX_MODE_MCAST_DROP_ALL_MASK    0x1
+#define ETH_VPORT_RX_MODE_MCAST_DROP_ALL_SHIFT  3
+#define ETH_VPORT_RX_MODE_MCAST_ACCEPT_ALL_MASK        0x1
+#define ETH_VPORT_RX_MODE_MCAST_ACCEPT_ALL_SHIFT       4
+#define ETH_VPORT_RX_MODE_BCAST_ACCEPT_ALL_MASK        0x1
+#define ETH_VPORT_RX_MODE_BCAST_ACCEPT_ALL_SHIFT       5
+#define ETH_VPORT_RX_MODE_RESERVED1_MASK              0x3FF
+#define ETH_VPORT_RX_MODE_RESERVED1_SHIFT            6
+       __le16 reserved2[3];
+};
+
+struct eth_vport_tpa_param {
+       u64     reserved[2];
+};
+
+struct eth_vport_tx_mode {
+       __le16 state;
+#define ETH_VPORT_TX_MODE_UCAST_DROP_ALL_MASK    0x1
+#define ETH_VPORT_TX_MODE_UCAST_DROP_ALL_SHIFT   0
+#define ETH_VPORT_TX_MODE_UCAST_ACCEPT_ALL_MASK  0x1
+#define ETH_VPORT_TX_MODE_UCAST_ACCEPT_ALL_SHIFT 1
+#define ETH_VPORT_TX_MODE_MCAST_DROP_ALL_MASK    0x1
+#define ETH_VPORT_TX_MODE_MCAST_DROP_ALL_SHIFT   2
+#define ETH_VPORT_TX_MODE_MCAST_ACCEPT_ALL_MASK  0x1
+#define ETH_VPORT_TX_MODE_MCAST_ACCEPT_ALL_SHIFT 3
+#define ETH_VPORT_TX_MODE_BCAST_ACCEPT_ALL_MASK  0x1
+#define ETH_VPORT_TX_MODE_BCAST_ACCEPT_ALL_SHIFT 4
+#define ETH_VPORT_TX_MODE_RESERVED1_MASK        0x7FF
+#define ETH_VPORT_TX_MODE_RESERVED1_SHIFT      5
+       __le16 reserved2[3];
+};
+
+struct rx_queue_start_ramrod_data {
+       __le16    rx_queue_id;
+       __le16    num_of_pbl_pages;
+       __le16    bd_max_bytes;
+       __le16    sb_id;
+       u8            sb_index;
+       u8            vport_id;
+       u8            default_rss_queue_flg;
+       u8            complete_cqe_flg;
+       u8            complete_event_flg;
+       u8            stats_counter_id;
+       u8            pin_context;
+       u8            pxp_tph_valid_bd;
+       u8            pxp_tph_valid_pkt;
+       u8            pxp_st_hint;
+       __le16    pxp_st_index;
+       u8            reserved[4];
+       struct regpair  cqe_pbl_addr;
+       struct regpair  bd_base;
+       struct regpair  sge_base;
+};
+
+struct rx_queue_stop_ramrod_data {
+       __le16  rx_queue_id;
+       u8      complete_cqe_flg;
+       u8      complete_event_flg;
+       u8      vport_id;
+       u8      reserved[3];
+};
+
+struct rx_queue_update_ramrod_data {
+       __le16    rx_queue_id;
+       u8            complete_cqe_flg;
+       u8            complete_event_flg;
+       u8            init_sge_ring_flg;
+       u8            vport_id;
+       u8            pxp_tph_valid_sge;
+       u8            pxp_st_hint;
+       __le16    pxp_st_index;
+       u8            reserved[6];
+       struct regpair  sge_base;
+};
+
+struct tx_queue_start_ramrod_data {
+       __le16  sb_id;
+       u8      sb_index;
+       u8      vport_id;
+       u8      tc;
+       u8      stats_counter_id;
+       __le16  qm_pq_id;
+       u8      flags;
+#define TX_QUEUE_START_RAMROD_DATA_DISABLE_OPPORTUNISTIC_MASK  0x1
+#define TX_QUEUE_START_RAMROD_DATA_DISABLE_OPPORTUNISTIC_SHIFT 0
+#define TX_QUEUE_START_RAMROD_DATA_TEST_MODE_PKT_DUP_MASK      0x1
+#define TX_QUEUE_START_RAMROD_DATA_TEST_MODE_PKT_DUP_SHIFT     1
+#define TX_QUEUE_START_RAMROD_DATA_TEST_MODE_TX_DEST_MASK      0x1
+#define TX_QUEUE_START_RAMROD_DATA_TEST_MODE_TX_DEST_SHIFT     2
+#define TX_QUEUE_START_RAMROD_DATA_RESERVED0_MASK            0x1F
+#define TX_QUEUE_START_RAMROD_DATA_RESERVED0_SHIFT          3
+       u8            pin_context;
+       u8            pxp_tph_valid_bd;
+       u8            pxp_tph_valid_pkt;
+       __le16    pxp_st_index;
+       u8            pxp_st_hint;
+       u8            reserved1[3];
+       __le16    queue_zone_id;
+       __le16    test_dup_count;
+       __le16    pbl_size;
+       struct regpair  pbl_base_addr;
+};
+
+struct tx_queue_stop_ramrod_data {
+       __le16 reserved[4];
+};
+
+struct vport_filter_update_ramrod_data {
+       struct eth_filter_cmd_header    filter_cmd_hdr;
+       struct eth_filter_cmd      filter_cmds[ETH_FILTER_RULES_COUNT];
+};
+
+struct vport_start_ramrod_data {
+       u8                            vport_id;
+       u8                            sw_fid;
+       __le16                    mtu;
+       u8                            drop_ttl0_en;
+       u8                            inner_vlan_removal_en;
+       struct eth_vport_rx_mode        rx_mode;
+       struct eth_vport_tx_mode        tx_mode;
+       struct eth_vport_tpa_param      tpa_param;
+       __le16                    sge_buff_size;
+       u8                            max_sges_num;
+       u8                            tx_switching_en;
+       u8                            anti_spoofing_en;
+       u8                            default_vlan_en;
+       u8                            handle_ptp_pkts;
+       u8                            silent_vlan_removal_en;
+       __le16                    default_vlan;
+       u8                            untagged;
+       u8                            reserved[7];
+};
+
+struct vport_stop_ramrod_data {
+       u8      vport_id;
+       u8      reserved[7];
+};
+
+struct vport_update_ramrod_data_cmn {
+       u8      vport_id;
+       u8      update_rx_active_flg;
+       u8      rx_active_flg;
+       u8      update_tx_active_flg;
+       u8      tx_active_flg;
+       u8      update_rx_mode_flg;
+       u8      update_tx_mode_flg;
+       u8      update_approx_mcast_flg;
+       u8      update_rss_flg;
+       u8      update_inner_vlan_removal_en_flg;
+       u8      inner_vlan_removal_en;
+       u8      update_tpa_param_flg;
+       u8      update_tpa_en_flg;
+       u8      update_sge_param_flg;
+       __le16  sge_buff_size;
+       u8      max_sges_num;
+       u8      update_tx_switching_en_flg;
+       u8      tx_switching_en;
+       u8      update_anti_spoofing_en_flg;
+       u8      anti_spoofing_en;
+       u8      update_handle_ptp_pkts;
+       u8      handle_ptp_pkts;
+       u8      update_default_vlan_en_flg;
+       u8      default_vlan_en;
+       u8      update_default_vlan_flg;
+       __le16  default_vlan;
+       u8      update_accept_any_vlan_flg;
+       u8      accept_any_vlan;
+       u8      silent_vlan_removal_en;
+       u8      reserved;
+};
+
+struct vport_update_ramrod_mcast {
+       __le32 bins[ETH_MULTICAST_MAC_BINS_IN_REGS];
+};
+
+struct vport_update_ramrod_data {
+       struct vport_update_ramrod_data_cmn     common;
+       struct eth_vport_rx_mode                rx_mode;
+       struct eth_vport_tx_mode                tx_mode;
+       struct eth_vport_tpa_param            tpa_param;
+       struct vport_update_ramrod_mcast        approx_mcast;
+       struct eth_vport_rss_config          rss_config;
+};
+
+struct mstorm_eth_conn_ag_ctx {
+       u8      byte0 /* cdu_validation */;
+       u8      byte1 /* state */;
+       u8      flags0;
+#define MSTORM_ETH_CONN_AG_CTX_EXIST_IN_QM0_MASK  0x1   /* exist_in_qm0 */
+#define MSTORM_ETH_CONN_AG_CTX_EXIST_IN_QM0_SHIFT 0
+#define MSTORM_ETH_CONN_AG_CTX_BIT1_MASK          0x1   /* exist_in_qm1 */
+#define MSTORM_ETH_CONN_AG_CTX_BIT1_SHIFT         1
+#define MSTORM_ETH_CONN_AG_CTX_CF0_MASK           0x3   /* cf0 */
+#define MSTORM_ETH_CONN_AG_CTX_CF0_SHIFT          2
+#define MSTORM_ETH_CONN_AG_CTX_CF1_MASK           0x3   /* cf1 */
+#define MSTORM_ETH_CONN_AG_CTX_CF1_SHIFT          4
+#define MSTORM_ETH_CONN_AG_CTX_CF2_MASK           0x3   /* cf2 */
+#define MSTORM_ETH_CONN_AG_CTX_CF2_SHIFT          6
+       u8 flags1;
+#define MSTORM_ETH_CONN_AG_CTX_CF0EN_MASK         0x1   /* cf0en */
+#define MSTORM_ETH_CONN_AG_CTX_CF0EN_SHIFT        0
+#define MSTORM_ETH_CONN_AG_CTX_CF1EN_MASK         0x1   /* cf1en */
+#define MSTORM_ETH_CONN_AG_CTX_CF1EN_SHIFT        1
+#define MSTORM_ETH_CONN_AG_CTX_CF2EN_MASK         0x1   /* cf2en */
+#define MSTORM_ETH_CONN_AG_CTX_CF2EN_SHIFT        2
+#define MSTORM_ETH_CONN_AG_CTX_RULE0EN_MASK       0x1   /* rule0en */
+#define MSTORM_ETH_CONN_AG_CTX_RULE0EN_SHIFT      3
+#define MSTORM_ETH_CONN_AG_CTX_RULE1EN_MASK       0x1   /* rule1en */
+#define MSTORM_ETH_CONN_AG_CTX_RULE1EN_SHIFT      4
+#define MSTORM_ETH_CONN_AG_CTX_RULE2EN_MASK       0x1   /* rule2en */
+#define MSTORM_ETH_CONN_AG_CTX_RULE2EN_SHIFT      5
+#define MSTORM_ETH_CONN_AG_CTX_RULE3EN_MASK       0x1   /* rule3en */
+#define MSTORM_ETH_CONN_AG_CTX_RULE3EN_SHIFT      6
+#define MSTORM_ETH_CONN_AG_CTX_RULE4EN_MASK       0x1   /* rule4en */
+#define MSTORM_ETH_CONN_AG_CTX_RULE4EN_SHIFT      7
+       __le16  word0 /* word0 */;
+       __le16  word1 /* word1 */;
+       __le32  reg0 /* reg0 */;
+       __le32  reg1 /* reg1 */;
+};
+
+struct tstorm_eth_conn_ag_ctx {
+       u8      byte0 /* cdu_validation */;
+       u8      byte1 /* state */;
+       u8      flags0;
+#define TSTORM_ETH_CONN_AG_CTX_BIT0_MASK      0x1       /* exist_in_qm0 */
+#define TSTORM_ETH_CONN_AG_CTX_BIT0_SHIFT     0
+#define TSTORM_ETH_CONN_AG_CTX_BIT1_MASK      0x1       /* exist_in_qm1 */
+#define TSTORM_ETH_CONN_AG_CTX_BIT1_SHIFT     1
+#define TSTORM_ETH_CONN_AG_CTX_BIT2_MASK      0x1       /* bit2 */
+#define TSTORM_ETH_CONN_AG_CTX_BIT2_SHIFT     2
+#define TSTORM_ETH_CONN_AG_CTX_BIT3_MASK      0x1       /* bit3 */
+#define TSTORM_ETH_CONN_AG_CTX_BIT3_SHIFT     3
+#define TSTORM_ETH_CONN_AG_CTX_BIT4_MASK      0x1       /* bit4 */
+#define TSTORM_ETH_CONN_AG_CTX_BIT4_SHIFT     4
+#define TSTORM_ETH_CONN_AG_CTX_BIT5_MASK      0x1       /* bit5 */
+#define TSTORM_ETH_CONN_AG_CTX_BIT5_SHIFT     5
+#define TSTORM_ETH_CONN_AG_CTX_CF0_MASK       0x3       /* timer0cf */
+#define TSTORM_ETH_CONN_AG_CTX_CF0_SHIFT      6
+       u8 flags1;
+#define TSTORM_ETH_CONN_AG_CTX_CF1_MASK       0x3       /* timer1cf */
+#define TSTORM_ETH_CONN_AG_CTX_CF1_SHIFT      0
+#define TSTORM_ETH_CONN_AG_CTX_CF2_MASK       0x3       /* timer2cf */
+#define TSTORM_ETH_CONN_AG_CTX_CF2_SHIFT      2
+#define TSTORM_ETH_CONN_AG_CTX_CF3_MASK       0x3       /* timer_stop_all */
+#define TSTORM_ETH_CONN_AG_CTX_CF3_SHIFT      4
+#define TSTORM_ETH_CONN_AG_CTX_CF4_MASK       0x3       /* cf4 */
+#define TSTORM_ETH_CONN_AG_CTX_CF4_SHIFT      6
+       u8 flags2;
+#define TSTORM_ETH_CONN_AG_CTX_CF5_MASK       0x3       /* cf5 */
+#define TSTORM_ETH_CONN_AG_CTX_CF5_SHIFT      0
+#define TSTORM_ETH_CONN_AG_CTX_CF6_MASK       0x3       /* cf6 */
+#define TSTORM_ETH_CONN_AG_CTX_CF6_SHIFT      2
+#define TSTORM_ETH_CONN_AG_CTX_CF7_MASK       0x3       /* cf7 */
+#define TSTORM_ETH_CONN_AG_CTX_CF7_SHIFT      4
+#define TSTORM_ETH_CONN_AG_CTX_CF8_MASK       0x3       /* cf8 */
+#define TSTORM_ETH_CONN_AG_CTX_CF8_SHIFT      6
+       u8 flags3;
+#define TSTORM_ETH_CONN_AG_CTX_CF9_MASK       0x3       /* cf9 */
+#define TSTORM_ETH_CONN_AG_CTX_CF9_SHIFT      0
+#define TSTORM_ETH_CONN_AG_CTX_CF10_MASK      0x3       /* cf10 */
+#define TSTORM_ETH_CONN_AG_CTX_CF10_SHIFT     2
+#define TSTORM_ETH_CONN_AG_CTX_CF0EN_MASK     0x1       /* cf0en */
+#define TSTORM_ETH_CONN_AG_CTX_CF0EN_SHIFT    4
+#define TSTORM_ETH_CONN_AG_CTX_CF1EN_MASK     0x1       /* cf1en */
+#define TSTORM_ETH_CONN_AG_CTX_CF1EN_SHIFT    5
+#define TSTORM_ETH_CONN_AG_CTX_CF2EN_MASK     0x1       /* cf2en */
+#define TSTORM_ETH_CONN_AG_CTX_CF2EN_SHIFT    6
+#define TSTORM_ETH_CONN_AG_CTX_CF3EN_MASK     0x1       /* cf3en */
+#define TSTORM_ETH_CONN_AG_CTX_CF3EN_SHIFT    7
+       u8 flags4;
+#define TSTORM_ETH_CONN_AG_CTX_CF4EN_MASK     0x1       /* cf4en */
+#define TSTORM_ETH_CONN_AG_CTX_CF4EN_SHIFT    0
+#define TSTORM_ETH_CONN_AG_CTX_CF5EN_MASK     0x1       /* cf5en */
+#define TSTORM_ETH_CONN_AG_CTX_CF5EN_SHIFT    1
+#define TSTORM_ETH_CONN_AG_CTX_CF6EN_MASK     0x1       /* cf6en */
+#define TSTORM_ETH_CONN_AG_CTX_CF6EN_SHIFT    2
+#define TSTORM_ETH_CONN_AG_CTX_CF7EN_MASK     0x1       /* cf7en */
+#define TSTORM_ETH_CONN_AG_CTX_CF7EN_SHIFT    3
+#define TSTORM_ETH_CONN_AG_CTX_CF8EN_MASK     0x1       /* cf8en */
+#define TSTORM_ETH_CONN_AG_CTX_CF8EN_SHIFT    4
+#define TSTORM_ETH_CONN_AG_CTX_CF9EN_MASK     0x1       /* cf9en */
+#define TSTORM_ETH_CONN_AG_CTX_CF9EN_SHIFT    5
+#define TSTORM_ETH_CONN_AG_CTX_CF10EN_MASK    0x1       /* cf10en */
+#define TSTORM_ETH_CONN_AG_CTX_CF10EN_SHIFT   6
+#define TSTORM_ETH_CONN_AG_CTX_RULE0EN_MASK   0x1       /* rule0en */
+#define TSTORM_ETH_CONN_AG_CTX_RULE0EN_SHIFT  7
+       u8 flags5;
+#define TSTORM_ETH_CONN_AG_CTX_RULE1EN_MASK   0x1       /* rule1en */
+#define TSTORM_ETH_CONN_AG_CTX_RULE1EN_SHIFT  0
+#define TSTORM_ETH_CONN_AG_CTX_RULE2EN_MASK   0x1       /* rule2en */
+#define TSTORM_ETH_CONN_AG_CTX_RULE2EN_SHIFT  1
+#define TSTORM_ETH_CONN_AG_CTX_RULE3EN_MASK   0x1       /* rule3en */
+#define TSTORM_ETH_CONN_AG_CTX_RULE3EN_SHIFT  2
+#define TSTORM_ETH_CONN_AG_CTX_RULE4EN_MASK   0x1       /* rule4en */
+#define TSTORM_ETH_CONN_AG_CTX_RULE4EN_SHIFT  3
+#define TSTORM_ETH_CONN_AG_CTX_RULE5EN_MASK   0x1       /* rule5en */
+#define TSTORM_ETH_CONN_AG_CTX_RULE5EN_SHIFT  4
+#define TSTORM_ETH_CONN_AG_CTX_RX_BD_EN_MASK  0x1       /* rule6en */
+#define TSTORM_ETH_CONN_AG_CTX_RX_BD_EN_SHIFT 5
+#define TSTORM_ETH_CONN_AG_CTX_RULE7EN_MASK   0x1       /* rule7en */
+#define TSTORM_ETH_CONN_AG_CTX_RULE7EN_SHIFT  6
+#define TSTORM_ETH_CONN_AG_CTX_RULE8EN_MASK   0x1       /* rule8en */
+#define TSTORM_ETH_CONN_AG_CTX_RULE8EN_SHIFT  7
+       __le32  reg0 /* reg0 */;
+       __le32  reg1 /* reg1 */;
+       __le32  reg2 /* reg2 */;
+       __le32  reg3 /* reg3 */;
+       __le32  reg4 /* reg4 */;
+       __le32  reg5 /* reg5 */;
+       __le32  reg6 /* reg6 */;
+       __le32  reg7 /* reg7 */;
+       __le32  reg8 /* reg8 */;
+       u8      byte2 /* byte2 */;
+       u8      byte3 /* byte3 */;
+       __le16  rx_bd_cons /* word0 */;
+       u8      byte4 /* byte4 */;
+       u8      byte5 /* byte5 */;
+       __le16  rx_bd_prod /* word1 */;
+       __le16  word2 /* conn_dpi */;
+       __le16  word3 /* word3 */;
+       __le32  reg9 /* reg9 */;
+       __le32  reg10 /* reg10 */;
+};
+
+struct ustorm_eth_conn_ag_ctx {
+       u8      byte0 /* cdu_validation */;
+       u8      byte1 /* state */;
+       u8      flags0;
+#define USTORM_ETH_CONN_AG_CTX_BIT0_MASK                  0x1
+#define USTORM_ETH_CONN_AG_CTX_BIT0_SHIFT                 0
+#define USTORM_ETH_CONN_AG_CTX_BIT1_MASK                  0x1
+#define USTORM_ETH_CONN_AG_CTX_BIT1_SHIFT                 1
+#define USTORM_ETH_CONN_AG_CTX_CF0_MASK                   0x3   /* timer0cf */
+#define USTORM_ETH_CONN_AG_CTX_CF0_SHIFT                  2
+#define USTORM_ETH_CONN_AG_CTX_CF1_MASK                   0x3   /* timer1cf */
+#define USTORM_ETH_CONN_AG_CTX_CF1_SHIFT                  4
+#define USTORM_ETH_CONN_AG_CTX_CF2_MASK                   0x3   /* timer2cf */
+#define USTORM_ETH_CONN_AG_CTX_CF2_SHIFT                  6
+       u8 flags1;
+#define USTORM_ETH_CONN_AG_CTX_CF3_MASK                   0x3
+#define USTORM_ETH_CONN_AG_CTX_CF3_SHIFT                  0
+#define USTORM_ETH_CONN_AG_CTX_TX_ARM_CF_MASK             0x3   /* cf4 */
+#define USTORM_ETH_CONN_AG_CTX_TX_ARM_CF_SHIFT            2
+#define USTORM_ETH_CONN_AG_CTX_RX_ARM_CF_MASK             0x3   /* cf5 */
+#define USTORM_ETH_CONN_AG_CTX_RX_ARM_CF_SHIFT            4
+#define USTORM_ETH_CONN_AG_CTX_TX_BD_CONS_UPD_CF_MASK     0x3   /* cf6 */
+#define USTORM_ETH_CONN_AG_CTX_TX_BD_CONS_UPD_CF_SHIFT    6
+       u8 flags2;
+#define USTORM_ETH_CONN_AG_CTX_CF0EN_MASK                 0x1   /* cf0en */
+#define USTORM_ETH_CONN_AG_CTX_CF0EN_SHIFT                0
+#define USTORM_ETH_CONN_AG_CTX_CF1EN_MASK                 0x1   /* cf1en */
+#define USTORM_ETH_CONN_AG_CTX_CF1EN_SHIFT                1
+#define USTORM_ETH_CONN_AG_CTX_CF2EN_MASK                 0x1   /* cf2en */
+#define USTORM_ETH_CONN_AG_CTX_CF2EN_SHIFT                2
+#define USTORM_ETH_CONN_AG_CTX_CF3EN_MASK                 0x1   /* cf3en */
+#define USTORM_ETH_CONN_AG_CTX_CF3EN_SHIFT                3
+#define USTORM_ETH_CONN_AG_CTX_TX_ARM_CF_EN_MASK          0x1   /* cf4en */
+#define USTORM_ETH_CONN_AG_CTX_TX_ARM_CF_EN_SHIFT         4
+#define USTORM_ETH_CONN_AG_CTX_RX_ARM_CF_EN_MASK          0x1   /* cf5en */
+#define USTORM_ETH_CONN_AG_CTX_RX_ARM_CF_EN_SHIFT         5
+#define USTORM_ETH_CONN_AG_CTX_TX_BD_CONS_UPD_CF_EN_MASK  0x1   /* cf6en */
+#define USTORM_ETH_CONN_AG_CTX_TX_BD_CONS_UPD_CF_EN_SHIFT 6
+#define USTORM_ETH_CONN_AG_CTX_RULE0EN_MASK               0x1   /* rule0en */
+#define USTORM_ETH_CONN_AG_CTX_RULE0EN_SHIFT              7
+       u8 flags3;
+#define USTORM_ETH_CONN_AG_CTX_RULE1EN_MASK               0x1   /* rule1en */
+#define USTORM_ETH_CONN_AG_CTX_RULE1EN_SHIFT              0
+#define USTORM_ETH_CONN_AG_CTX_RULE2EN_MASK               0x1   /* rule2en */
+#define USTORM_ETH_CONN_AG_CTX_RULE2EN_SHIFT              1
+#define USTORM_ETH_CONN_AG_CTX_RULE3EN_MASK               0x1   /* rule3en */
+#define USTORM_ETH_CONN_AG_CTX_RULE3EN_SHIFT              2
+#define USTORM_ETH_CONN_AG_CTX_RULE4EN_MASK               0x1   /* rule4en */
+#define USTORM_ETH_CONN_AG_CTX_RULE4EN_SHIFT              3
+#define USTORM_ETH_CONN_AG_CTX_RULE5EN_MASK               0x1   /* rule5en */
+#define USTORM_ETH_CONN_AG_CTX_RULE5EN_SHIFT              4
+#define USTORM_ETH_CONN_AG_CTX_RULE6EN_MASK               0x1   /* rule6en */
+#define USTORM_ETH_CONN_AG_CTX_RULE6EN_SHIFT              5
+#define USTORM_ETH_CONN_AG_CTX_RULE7EN_MASK               0x1   /* rule7en */
+#define USTORM_ETH_CONN_AG_CTX_RULE7EN_SHIFT              6
+#define USTORM_ETH_CONN_AG_CTX_RULE8EN_MASK               0x1   /* rule8en */
+#define USTORM_ETH_CONN_AG_CTX_RULE8EN_SHIFT              7
+       u8      byte2 /* byte2 */;
+       u8      byte3 /* byte3 */;
+       __le16  word0 /* conn_dpi */;
+       __le16  tx_bd_cons /* word1 */;
+       __le32  reg0 /* reg0 */;
+       __le32  reg1 /* reg1 */;
+       __le32  reg2 /* reg2 */;
+       __le32  reg3 /* reg3 */;
+       __le16  tx_drv_bd_cons /* word2 */;
+       __le16  rx_drv_cqe_cons /* word3 */;
+};
+
+struct xstorm_eth_hw_conn_ag_ctx {
+       u8      reserved0 /* cdu_validation */;
+       u8      eth_state /* state */;
+       u8      flags0;
+#define XSTORM_ETH_HW_CONN_AG_CTX_EXIST_IN_QM0_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_EXIST_IN_QM0_SHIFT           0
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED1_MASK               0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED1_SHIFT              1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED2_MASK               0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED2_SHIFT              2
+#define XSTORM_ETH_HW_CONN_AG_CTX_EXIST_IN_QM3_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_EXIST_IN_QM3_SHIFT           3
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED3_MASK               0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED3_SHIFT              4
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED4_MASK               0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED4_SHIFT              5
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED5_MASK               0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED5_SHIFT              6
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED6_MASK               0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED6_SHIFT              7
+       u8 flags1;
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED7_MASK               0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED7_SHIFT              0
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED8_MASK               0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED8_SHIFT              1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED9_MASK               0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED9_SHIFT              2
+#define XSTORM_ETH_HW_CONN_AG_CTX_BIT11_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_BIT11_SHIFT                  3
+#define XSTORM_ETH_HW_CONN_AG_CTX_BIT12_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_BIT12_SHIFT                  4
+#define XSTORM_ETH_HW_CONN_AG_CTX_BIT13_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_BIT13_SHIFT                  5
+#define XSTORM_ETH_HW_CONN_AG_CTX_TX_RULE_ACTIVE_MASK          0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_TX_RULE_ACTIVE_SHIFT         6
+#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_ACTIVE_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_ACTIVE_SHIFT           7
+       u8 flags2;
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF0_MASK                     0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF0_SHIFT                    0
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF1_MASK                     0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF1_SHIFT                    2
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF2_MASK                     0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF2_SHIFT                    4
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF3_MASK                     0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF3_SHIFT                    6
+       u8 flags3;
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF4_MASK                     0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF4_SHIFT                    0
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF5_MASK                     0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF5_SHIFT                    2
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF6_MASK                     0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF6_SHIFT                    4
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF7_MASK                     0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF7_SHIFT                    6
+       u8 flags4;
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF8_MASK                     0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF8_SHIFT                    0
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF9_MASK                     0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF9_SHIFT                    2
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF10_MASK                    0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF10_SHIFT                   4
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF11_MASK                    0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF11_SHIFT                   6
+       u8 flags5;
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF12_MASK                    0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF12_SHIFT                   0
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF13_MASK                    0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF13_SHIFT                   2
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF14_MASK                    0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF14_SHIFT                   4
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF15_MASK                    0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF15_SHIFT                   6
+       u8 flags6;
+#define XSTORM_ETH_HW_CONN_AG_CTX_GO_TO_BD_CONS_CF_MASK        0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_GO_TO_BD_CONS_CF_SHIFT       0
+#define XSTORM_ETH_HW_CONN_AG_CTX_MULTI_UNICAST_CF_MASK        0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_MULTI_UNICAST_CF_SHIFT       2
+#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_MASK                   0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_SHIFT                  4
+#define XSTORM_ETH_HW_CONN_AG_CTX_TERMINATE_CF_MASK            0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_TERMINATE_CF_SHIFT           6
+       u8 flags7;
+#define XSTORM_ETH_HW_CONN_AG_CTX_FLUSH_Q0_MASK                0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_FLUSH_Q0_SHIFT               0
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED10_MASK              0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED10_SHIFT             2
+#define XSTORM_ETH_HW_CONN_AG_CTX_SLOW_PATH_MASK               0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_SLOW_PATH_SHIFT              4
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF0EN_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF0EN_SHIFT                  6
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF1EN_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF1EN_SHIFT                  7
+       u8 flags8;
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF2EN_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF2EN_SHIFT                  0
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF3EN_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF3EN_SHIFT                  1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF4EN_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF4EN_SHIFT                  2
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF5EN_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF5EN_SHIFT                  3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF6EN_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF6EN_SHIFT                  4
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF7EN_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF7EN_SHIFT                  5
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF8EN_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF8EN_SHIFT                  6
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF9EN_MASK                   0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF9EN_SHIFT                  7
+       u8 flags9;
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF10EN_MASK                  0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF10EN_SHIFT                 0
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF11EN_MASK                  0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF11EN_SHIFT                 1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF12EN_MASK                  0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF12EN_SHIFT                 2
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF13EN_MASK                  0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF13EN_SHIFT                 3
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF14EN_MASK                  0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF14EN_SHIFT                 4
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF15EN_MASK                  0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_CF15EN_SHIFT                 5
+#define XSTORM_ETH_HW_CONN_AG_CTX_GO_TO_BD_CONS_CF_EN_MASK     0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_GO_TO_BD_CONS_CF_EN_SHIFT    6
+#define XSTORM_ETH_HW_CONN_AG_CTX_MULTI_UNICAST_CF_EN_MASK     0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_MULTI_UNICAST_CF_EN_SHIFT    7
+       u8 flags10;
+#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_EN_MASK                0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_EN_SHIFT               0
+#define XSTORM_ETH_HW_CONN_AG_CTX_TERMINATE_CF_EN_MASK         0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_TERMINATE_CF_EN_SHIFT        1
+#define XSTORM_ETH_HW_CONN_AG_CTX_FLUSH_Q0_EN_MASK             0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_FLUSH_Q0_EN_SHIFT            2
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED11_MASK              0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED11_SHIFT             3
+#define XSTORM_ETH_HW_CONN_AG_CTX_SLOW_PATH_EN_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_SLOW_PATH_EN_SHIFT           4
+#define XSTORM_ETH_HW_CONN_AG_CTX_TPH_ENABLE_EN_RESERVED_MASK  0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_TPH_ENABLE_EN_RESERVED_SHIFT 5
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED12_MASK              0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED12_SHIFT             6
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED13_MASK              0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED13_SHIFT             7
+       u8 flags11;
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED14_MASK              0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED14_SHIFT             0
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED15_MASK              0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED15_SHIFT             1
+#define XSTORM_ETH_HW_CONN_AG_CTX_TX_DEC_RULE_EN_MASK          0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_TX_DEC_RULE_EN_SHIFT         2
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE5EN_MASK                 0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE5EN_SHIFT                3
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE6EN_MASK                 0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE6EN_SHIFT                4
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE7EN_MASK                 0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE7EN_SHIFT                5
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED1_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED1_SHIFT           6
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE9EN_MASK                 0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE9EN_SHIFT                7
+       u8 flags12;
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE10EN_MASK                0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE10EN_SHIFT               0
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE11EN_MASK                0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE11EN_SHIFT               1
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED2_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED2_SHIFT           2
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED3_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED3_SHIFT           3
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE14EN_MASK                0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE14EN_SHIFT               4
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE15EN_MASK                0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE15EN_SHIFT               5
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE16EN_MASK                0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE16EN_SHIFT               6
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE17EN_MASK                0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE17EN_SHIFT               7
+       u8 flags13;
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE18EN_MASK                0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE18EN_SHIFT               0
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE19EN_MASK                0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_RULE19EN_SHIFT               1
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED4_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED4_SHIFT           2
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED5_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED5_SHIFT           3
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED6_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED6_SHIFT           4
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED7_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED7_SHIFT           5
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED8_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED8_SHIFT           6
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED9_MASK            0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED9_SHIFT           7
+       u8 flags14;
+#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_USE_EXT_HDR_MASK        0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_USE_EXT_HDR_SHIFT       0
+#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_SEND_RAW_L3L4_MASK      0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_SEND_RAW_L3L4_SHIFT     1
+#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_INBAND_PROP_HDR_MASK    0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_INBAND_PROP_HDR_SHIFT   2
+#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_SEND_EXT_TUNNEL_MASK    0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_SEND_EXT_TUNNEL_SHIFT   3
+#define XSTORM_ETH_HW_CONN_AG_CTX_L2_EDPM_ENABLE_MASK          0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_L2_EDPM_ENABLE_SHIFT         4
+#define XSTORM_ETH_HW_CONN_AG_CTX_ROCE_EDPM_ENABLE_MASK        0x1
+#define XSTORM_ETH_HW_CONN_AG_CTX_ROCE_EDPM_ENABLE_SHIFT       5
+#define XSTORM_ETH_HW_CONN_AG_CTX_TPH_ENABLE_MASK              0x3
+#define XSTORM_ETH_HW_CONN_AG_CTX_TPH_ENABLE_SHIFT             6
+       u8      edpm_event_id /* byte2 */;
+       __le16  physical_q0 /* physical_q0 */;
+       __le16  word1 /* physical_q1 */;
+       __le16  edpm_num_bds /* physical_q2 */;
+       __le16  tx_bd_cons /* word3 */;
+       __le16  tx_bd_prod /* word4 */;
+       __le16  go_to_bd_cons /* word5 */;
+       __le16  conn_dpi /* conn_dpi */;
+};
+
+#define VF_MAX_STATIC 192       /* In case of K2 */
+
+#define MCP_GLOB_PATH_MAX       2
+#define MCP_PORT_MAX            2       /* Global */
+#define MCP_GLOB_PORT_MAX       4       /* Global */
+#define MCP_GLOB_FUNC_MAX       16      /* Global */
+
+typedef u32 offsize_t;                  /* In DWORDS !!! */
+/* Offset from the beginning of the MCP scratchpad */
+#define OFFSIZE_OFFSET_SHIFT    0
+#define OFFSIZE_OFFSET_MASK     0x0000ffff
+/* Size of specific element (not the whole array if any) */
+#define OFFSIZE_SIZE_SHIFT      16
+#define OFFSIZE_SIZE_MASK       0xffff0000
+
+/* SECTION_OFFSET is calculating the offset in bytes out of offsize */
+#define SECTION_OFFSET(_offsize)        ((((_offsize &             \
+                                           OFFSIZE_OFFSET_MASK) >> \
+                                          OFFSIZE_OFFSET_SHIFT) << 2))
+
+/* QED_SECTION_SIZE is calculating the size in bytes out of offsize */
+#define QED_SECTION_SIZE(_offsize)              (((_offsize &           \
+                                                  OFFSIZE_SIZE_MASK) >> \
+                                                 OFFSIZE_SIZE_SHIFT) << 2)
+
+/* SECTION_ADDR returns the GRC addr of a section, given offsize and index
+ * within section.
+ */
+#define SECTION_ADDR(_offsize, idx)     (MCP_REG_SCRATCH +         \
+                                        SECTION_OFFSET(_offsize) + \
+                                        (QED_SECTION_SIZE(_offsize) * idx))
+
+/* SECTION_OFFSIZE_ADDR returns the GRC addr to the offsize address.
+ * Use offsetof, since the OFFSETUP collide with the firmware definition
+ */
+#define SECTION_OFFSIZE_ADDR(_pub_base, _section) (_pub_base +              \
+                                                  offsetof(struct           \
+                                                           mcp_public_data, \
+                                                           sections[_section]))
+/* PHY configuration */
+struct pmm_phy_cfg {
+       u32     speed;
+#define PMM_SPEED_AUTONEG   0
+
+       u32     pause;  /* bitmask */
+#define PMM_PAUSE_NONE          0x0
+#define PMM_PAUSE_AUTONEG       0x1
+#define PMM_PAUSE_RX            0x2
+#define PMM_PAUSE_TX            0x4
+
+       u32     adv_speed;  /* Default should be the speed_cap_mask */
+       u32     loopback_mode;
+#define PMM_LOOPBACK_NONE               0
+#define PMM_LOOPBACK_INT_PHY    1
+#define PMM_LOOPBACK_EXT_PHY    2
+#define PMM_LOOPBACK_EXT                3
+#define PMM_LOOPBACK_MAC                4
+
+       /* features */
+       u32 feature_config_flags;
+};
+
+struct port_mf_cfg {
+       u32     dynamic_cfg; /* device control channel */
+#define PORT_MF_CFG_OV_TAG_MASK              0x0000ffff
+#define PORT_MF_CFG_OV_TAG_SHIFT             0
+#define PORT_MF_CFG_OV_TAG_DEFAULT         PORT_MF_CFG_OV_TAG_MASK
+
+       u32     reserved[1];
+};
+
+/* DO NOT add new fields in the middle
+ * MUST be synced with struct pmm_stats_map
+ */
+struct pmm_stats {
+       u64     r64;    /* 0x00 (Offset 0x00 ) RX 64-byte frame counter*/
+       u64     r127;   /* 0x01 (Offset 0x08 ) RX 65 to 127 byte frame counter*/
+       u64     r255;
+       u64     r511;
+       u64     r1023;
+       u64     r1518;
+       u64     r1522;
+       u64     r2047;
+       u64     r4095;
+       u64     r9216;
+       u64     r16383;
+       u64     rfcs;   /* 0x0F (Offset 0x58 ) RX FCS error frame counter*/
+       u64     rxcf;   /* 0x10 (Offset 0x60 ) RX control frame counter*/
+       u64     rxpf;   /* 0x11 (Offset 0x68 ) RX pause frame counter*/
+       u64     rxpp;   /* 0x12 (Offset 0x70 ) RX PFC frame counter*/
+       u64     raln;   /* 0x16 (Offset 0x78 ) RX alignment error counter*/
+       u64     rfcr;   /* 0x19 (Offset 0x80 ) RX false carrier counter */
+       u64     rovr;   /* 0x1A (Offset 0x88 ) RX oversized frame counter*/
+       u64     rjbr;   /* 0x1B (Offset 0x90 ) RX jabber frame counter */
+       u64     rund;   /* 0x34 (Offset 0x98 ) RX undersized frame counter */
+       u64     rfrg;   /* 0x35 (Offset 0xa0 ) RX fragment counter */
+       u64     t64;    /* 0x40 (Offset 0xa8 ) TX 64-byte frame counter */
+       u64     t127;
+       u64     t255;
+       u64     t511;
+       u64     t1023;
+       u64     t1518;
+       u64     t2047;
+       u64     t4095;
+       u64     t9216;
+       u64     t16383;
+       u64     txpf;   /* 0x50 (Offset 0xf8 ) TX pause frame counter */
+       u64     txpp;   /* 0x51 (Offset 0x100) TX PFC frame counter */
+       u64     tlpiec;
+       u64     tncl;
+       u64     rbyte;  /* 0x3d (Offset 0x118) RX byte counter */
+       u64     rxuca;  /* 0x0c (Offset 0x120) RX UC frame counter */
+       u64     rxmca;  /* 0x0d (Offset 0x128) RX MC frame counter */
+       u64     rxbca;  /* 0x0e (Offset 0x130) RX BC frame counter */
+       u64     rxpok;
+       u64     tbyte;  /* 0x6f (Offset 0x140) TX byte counter */
+       u64     txuca;  /* 0x4d (Offset 0x148) TX UC frame counter */
+       u64     txmca;  /* 0x4e (Offset 0x150) TX MC frame counter */
+       u64     txbca;  /* 0x4f (Offset 0x158) TX BC frame counter */
+       u64     txcf;   /* 0x54 (Offset 0x160) TX control frame counter */
+};
+
+struct brb_stats {
+       u64     brb_truncate[8];
+       u64     brb_discard[8];
+};
+
+struct port_stats {
+       struct brb_stats        brb;
+       struct pmm_stats        pmm;
+};
+
+#define CMT_TEAM0 0
+#define CMT_TEAM1 1
+#define CMT_TEAM_MAX 2
+
+struct couple_mode_teaming {
+       u8 port_cmt[MCP_GLOB_PORT_MAX];
+#define PORT_CMT_IN_TEAM               BIT(0)
+
+#define PORT_CMT_PORT_ROLE             BIT(1)
+#define PORT_CMT_PORT_INACTIVE      (0 << 1)
+#define PORT_CMT_PORT_ACTIVE           BIT(1)
+
+#define PORT_CMT_TEAM_MASK             BIT(2)
+#define PORT_CMT_TEAM0              (0 << 2)
+#define PORT_CMT_TEAM1                 BIT(2)
+};
+
+/**************************************
+*     LLDP and DCBX HSI structures
+**************************************/
+#define LLDP_CHASSIS_ID_STAT_LEN 4
+#define LLDP_PORT_ID_STAT_LEN 4
+#define DCBX_MAX_APP_PROTOCOL           32
+#define MAX_SYSTEM_LLDP_TLV_DATA    32
+
+enum lldp_agent_e {
+       LLDP_NEAREST_BRIDGE = 0,
+       LLDP_NEAREST_NON_TPMR_BRIDGE,
+       LLDP_NEAREST_CUSTOMER_BRIDGE,
+       LLDP_MAX_LLDP_AGENTS
+};
+
+struct lldp_config_params_s {
+       u32 config;
+#define LLDP_CONFIG_TX_INTERVAL_MASK        0x000000ff
+#define LLDP_CONFIG_TX_INTERVAL_SHIFT       0
+#define LLDP_CONFIG_HOLD_MASK               0x00000f00
+#define LLDP_CONFIG_HOLD_SHIFT              8
+#define LLDP_CONFIG_MAX_CREDIT_MASK         0x0000f000
+#define LLDP_CONFIG_MAX_CREDIT_SHIFT        12
+#define LLDP_CONFIG_ENABLE_RX_MASK          0x40000000
+#define LLDP_CONFIG_ENABLE_RX_SHIFT         30
+#define LLDP_CONFIG_ENABLE_TX_MASK          0x80000000
+#define LLDP_CONFIG_ENABLE_TX_SHIFT         31
+       u32     local_chassis_id[LLDP_CHASSIS_ID_STAT_LEN];
+       u32     local_port_id[LLDP_PORT_ID_STAT_LEN];
+};
+
+struct lldp_status_params_s {
+       u32     prefix_seq_num;
+       u32     status; /* TBD */
+
+       /* Holds remote Chassis ID TLV header, subtype and 9B of payload. */
+       u32     peer_chassis_id[LLDP_CHASSIS_ID_STAT_LEN];
+
+       /* Holds remote Port ID TLV header, subtype and 9B of payload. */
+       u32     peer_port_id[LLDP_PORT_ID_STAT_LEN];
+       u32     suffix_seq_num;
+};
+
+struct dcbx_ets_feature {
+       u32 flags;
+#define DCBX_ETS_ENABLED_MASK                   0x00000001
+#define DCBX_ETS_ENABLED_SHIFT                  0
+#define DCBX_ETS_WILLING_MASK                   0x00000002
+#define DCBX_ETS_WILLING_SHIFT                  1
+#define DCBX_ETS_ERROR_MASK                     0x00000004
+#define DCBX_ETS_ERROR_SHIFT                    2
+#define DCBX_ETS_CBS_MASK                       0x00000008
+#define DCBX_ETS_CBS_SHIFT                      3
+#define DCBX_ETS_MAX_TCS_MASK                   0x000000f0
+#define DCBX_ETS_MAX_TCS_SHIFT                  4
+       u32     pri_tc_tbl[1];
+#define DCBX_ISCSI_OOO_TC                       4
+#define NIG_ETS_ISCSI_OOO_CLIENT_OFFSET         (DCBX_ISCSI_OOO_TC + 1)
+       u32     tc_bw_tbl[2];
+       u32     tc_tsa_tbl[2];
+#define DCBX_ETS_TSA_STRICT                     0
+#define DCBX_ETS_TSA_CBS                        1
+#define DCBX_ETS_TSA_ETS                        2
+};
+
+struct dcbx_app_priority_entry {
+       u32 entry;
+#define DCBX_APP_PRI_MAP_MASK       0x000000ff
+#define DCBX_APP_PRI_MAP_SHIFT      0
+#define DCBX_APP_PRI_0              0x01
+#define DCBX_APP_PRI_1              0x02
+#define DCBX_APP_PRI_2              0x04
+#define DCBX_APP_PRI_3              0x08
+#define DCBX_APP_PRI_4              0x10
+#define DCBX_APP_PRI_5              0x20
+#define DCBX_APP_PRI_6              0x40
+#define DCBX_APP_PRI_7              0x80
+#define DCBX_APP_SF_MASK            0x00000300
+#define DCBX_APP_SF_SHIFT           8
+#define DCBX_APP_SF_ETHTYPE         0
+#define DCBX_APP_SF_PORT            1
+#define DCBX_APP_PROTOCOL_ID_MASK   0xffff0000
+#define DCBX_APP_PROTOCOL_ID_SHIFT  16
+};
+
+/* FW structure in BE */
+struct dcbx_app_priority_feature {
+       u32 flags;
+#define DCBX_APP_ENABLED_MASK           0x00000001
+#define DCBX_APP_ENABLED_SHIFT          0
+#define DCBX_APP_WILLING_MASK           0x00000002
+#define DCBX_APP_WILLING_SHIFT          1
+#define DCBX_APP_ERROR_MASK             0x00000004
+#define DCBX_APP_ERROR_SHIFT            2
+/* Not in use
+ * #define DCBX_APP_DEFAULT_PRI_MASK       0x00000f00
+ * #define DCBX_APP_DEFAULT_PRI_SHIFT      8
+ */
+#define DCBX_APP_MAX_TCS_MASK           0x0000f000
+#define DCBX_APP_MAX_TCS_SHIFT          12
+#define DCBX_APP_NUM_ENTRIES_MASK       0x00ff0000
+#define DCBX_APP_NUM_ENTRIES_SHIFT      16
+       struct dcbx_app_priority_entry app_pri_tbl[DCBX_MAX_APP_PROTOCOL];
+};
+
+/* FW structure in BE */
+struct dcbx_features {
+       /* PG feature */
+       struct dcbx_ets_feature ets;
+
+       /* PFC feature */
+       u32                     pfc;
+#define DCBX_PFC_PRI_EN_BITMAP_MASK             0x000000ff
+#define DCBX_PFC_PRI_EN_BITMAP_SHIFT            0
+#define DCBX_PFC_PRI_EN_BITMAP_PRI_0            0x01
+#define DCBX_PFC_PRI_EN_BITMAP_PRI_1            0x02
+#define DCBX_PFC_PRI_EN_BITMAP_PRI_2            0x04
+#define DCBX_PFC_PRI_EN_BITMAP_PRI_3            0x08
+#define DCBX_PFC_PRI_EN_BITMAP_PRI_4            0x10
+#define DCBX_PFC_PRI_EN_BITMAP_PRI_5            0x20
+#define DCBX_PFC_PRI_EN_BITMAP_PRI_6            0x40
+#define DCBX_PFC_PRI_EN_BITMAP_PRI_7            0x80
+
+#define DCBX_PFC_FLAGS_MASK                     0x0000ff00
+#define DCBX_PFC_FLAGS_SHIFT                    8
+#define DCBX_PFC_CAPS_MASK                      0x00000f00
+#define DCBX_PFC_CAPS_SHIFT                     8
+#define DCBX_PFC_MBC_MASK                       0x00004000
+#define DCBX_PFC_MBC_SHIFT                      14
+#define DCBX_PFC_WILLING_MASK                   0x00008000
+#define DCBX_PFC_WILLING_SHIFT                  15
+#define DCBX_PFC_ENABLED_MASK                   0x00010000
+#define DCBX_PFC_ENABLED_SHIFT                  16
+#define DCBX_PFC_ERROR_MASK                     0x00020000
+#define DCBX_PFC_ERROR_SHIFT                    17
+
+       /* APP feature */
+       struct dcbx_app_priority_feature app;
+};
+
+struct dcbx_local_params {
+       u32 config;
+#define DCBX_CONFIG_VERSION_MASK            0x00000003
+#define DCBX_CONFIG_VERSION_SHIFT           0
+#define DCBX_CONFIG_VERSION_DISABLED        0
+#define DCBX_CONFIG_VERSION_IEEE            1
+#define DCBX_CONFIG_VERSION_CEE             2
+
+       u32                     flags;
+       struct dcbx_features    features;
+};
+
+struct dcbx_mib {
+       u32     prefix_seq_num;
+       u32     flags;
+       struct dcbx_features    features;
+       u32                     suffix_seq_num;
+};
+
+struct lldp_system_tlvs_buffer_s {
+       u16     valid;
+       u16     length;
+       u32     data[MAX_SYSTEM_LLDP_TLV_DATA];
+};
+
+/**************************************/
+/*                                    */
+/*     P U B L I C      G L O B A L   */
+/*                                    */
+/**************************************/
+struct public_global {
+       u32                             max_path;
+#define MAX_PATH_BIG_BEAR       2
+#define MAX_PATH_K2             1
+       u32                             max_ports;
+#define MODE_1P 1
+#define MODE_2P 2
+#define MODE_3P 3
+#define MODE_4P 4
+       u32                             debug_mb_offset;
+       u32                             phymod_dbg_mb_offset;
+       struct couple_mode_teaming      cmt;
+       s32                             internal_temperature;
+       u32                             mfw_ver;
+       u32                             running_bundle_id;
+};
+
+/**************************************/
+/*                                    */
+/*     P U B L I C      P A T H       */
+/*                                    */
+/**************************************/
+
+/****************************************************************************
+* Shared Memory 2 Region                                                   *
+****************************************************************************/
+/* The fw_flr_ack is actually built in the following way:                   */
+/* 8 bit:  PF ack                                                           */
+/* 128 bit: VF ack                                                           */
+/* 8 bit:  ios_dis_ack                                                      */
+/* In order to maintain endianity in the mailbox hsi, we want to keep using */
+/* u32. The fw must have the VF right after the PF since this is how it     */
+/* access arrays(it expects always the VF to reside after the PF, and that  */
+/* makes the calculation much easier for it. )                              */
+/* In order to answer both limitations, and keep the struct small, the code */
+/* will abuse the structure defined here to achieve the actual partition    */
+/* above                                                                    */
+/****************************************************************************/
+struct fw_flr_mb {
+       u32     aggint;
+       u32     opgen_addr;
+       u32     accum_ack;  /* 0..15:PF, 16..207:VF, 256..271:IOV_DIS */
+#define ACCUM_ACK_PF_BASE       0
+#define ACCUM_ACK_PF_SHIFT      0
+
+#define ACCUM_ACK_VF_BASE       8
+#define ACCUM_ACK_VF_SHIFT      3
+
+#define ACCUM_ACK_IOV_DIS_BASE  256
+#define ACCUM_ACK_IOV_DIS_SHIFT 8
+};
+
+struct public_path {
+       struct fw_flr_mb        flr_mb;
+       u32                     mcp_vf_disabled[VF_MAX_STATIC / 32];
+
+       u32                     process_kill;
+#define PROCESS_KILL_COUNTER_MASK               0x0000ffff
+#define PROCESS_KILL_COUNTER_SHIFT              0
+#define PROCESS_KILL_GLOB_AEU_BIT_MASK          0xffff0000
+#define PROCESS_KILL_GLOB_AEU_BIT_SHIFT         16
+#define GLOBAL_AEU_BIT(aeu_reg_id, aeu_bit) (aeu_reg_id * 32 + aeu_bit)
+};
+
+/**************************************/
+/*                                    */
+/*     P U B L I C      P O R T       */
+/*                                    */
+/**************************************/
+
+/****************************************************************************
+* Driver <-> FW Mailbox                                                    *
+****************************************************************************/
+
+struct public_port {
+       u32 validity_map;   /* 0x0 (4*2 = 0x8) */
+
+       /* validity bits */
+#define MCP_VALIDITY_PCI_CFG                    0x00100000
+#define MCP_VALIDITY_MB                         0x00200000
+#define MCP_VALIDITY_DEV_INFO                   0x00400000
+#define MCP_VALIDITY_RESERVED                   0x00000007
+
+       /* One licensing bit should be set */
+#define MCP_VALIDITY_LIC_KEY_IN_EFFECT_MASK     0x00000038
+#define MCP_VALIDITY_LIC_MANUF_KEY_IN_EFFECT    0x00000008
+#define MCP_VALIDITY_LIC_UPGRADE_KEY_IN_EFFECT  0x00000010
+#define MCP_VALIDITY_LIC_NO_KEY_IN_EFFECT       0x00000020
+
+       /* Active MFW */
+#define MCP_VALIDITY_ACTIVE_MFW_UNKNOWN         0x00000000
+#define MCP_VALIDITY_ACTIVE_MFW_MASK            0x000001c0
+#define MCP_VALIDITY_ACTIVE_MFW_NCSI            0x00000040
+#define MCP_VALIDITY_ACTIVE_MFW_NONE            0x000001c0
+
+       u32 link_status;
+#define LINK_STATUS_LINK_UP \
+       0x00000001
+#define LINK_STATUS_SPEED_AND_DUPLEX_MASK                       0x0000001e
+#define LINK_STATUS_SPEED_AND_DUPLEX_1000THD           BIT(1)
+#define LINK_STATUS_SPEED_AND_DUPLEX_1000TFD            (2 << 1)
+#define LINK_STATUS_SPEED_AND_DUPLEX_10G                        (3 << 1)
+#define LINK_STATUS_SPEED_AND_DUPLEX_20G                        (4 << 1)
+#define LINK_STATUS_SPEED_AND_DUPLEX_40G                        (5 << 1)
+#define LINK_STATUS_SPEED_AND_DUPLEX_50G                        (6 << 1)
+#define LINK_STATUS_SPEED_AND_DUPLEX_100G                       (7 << 1)
+#define LINK_STATUS_SPEED_AND_DUPLEX_25G                        (8 << 1)
+
+#define LINK_STATUS_AUTO_NEGOTIATE_ENABLED                      0x00000020
+
+#define LINK_STATUS_AUTO_NEGOTIATE_COMPLETE                     0x00000040
+#define LINK_STATUS_PARALLEL_DETECTION_USED                     0x00000080
+
+#define LINK_STATUS_PFC_ENABLED        \
+       0x00000100
+#define LINK_STATUS_LINK_PARTNER_1000TFD_CAPABLE        0x00000200
+#define LINK_STATUS_LINK_PARTNER_1000THD_CAPABLE        0x00000400
+#define LINK_STATUS_LINK_PARTNER_10G_CAPABLE            0x00000800
+#define LINK_STATUS_LINK_PARTNER_20G_CAPABLE            0x00001000
+#define LINK_STATUS_LINK_PARTNER_40G_CAPABLE            0x00002000
+#define LINK_STATUS_LINK_PARTNER_50G_CAPABLE            0x00004000
+#define LINK_STATUS_LINK_PARTNER_100G_CAPABLE           0x00008000
+#define LINK_STATUS_LINK_PARTNER_25G_CAPABLE            0x00010000
+
+#define LINK_STATUS_LINK_PARTNER_FLOW_CONTROL_MASK      0x000C0000
+#define LINK_STATUS_LINK_PARTNER_NOT_PAUSE_CAPABLE      (0 << 18)
+#define LINK_STATUS_LINK_PARTNER_SYMMETRIC_PAUSE       BIT(18)
+#define LINK_STATUS_LINK_PARTNER_ASYMMETRIC_PAUSE       (2 << 18)
+#define LINK_STATUS_LINK_PARTNER_BOTH_PAUSE                     (3 << 18)
+
+#define LINK_STATUS_SFP_TX_FAULT \
+       0x00100000
+#define LINK_STATUS_TX_FLOW_CONTROL_ENABLED                     0x00200000
+#define LINK_STATUS_RX_FLOW_CONTROL_ENABLED                     0x00400000
+
+       u32                     link_status1;
+       u32                     ext_phy_fw_version;
+       u32                     drv_phy_cfg_addr;
+
+       u32                     port_stx;
+
+       u32                     stat_nig_timer;
+
+       struct port_mf_cfg      port_mf_config;
+       struct port_stats       stats;
+
+       u32                     media_type;
+#define MEDIA_UNSPECIFIED       0x0
+#define MEDIA_SFPP_10G_FIBER    0x1
+#define MEDIA_XFP_FIBER         0x2
+#define MEDIA_DA_TWINAX         0x3
+#define MEDIA_BASE_T            0x4
+#define MEDIA_SFP_1G_FIBER      0x5
+#define MEDIA_KR                0xf0
+#define MEDIA_NOT_PRESENT       0xff
+
+       u32 lfa_status;
+#define LFA_LINK_FLAP_REASON_OFFSET             0
+#define LFA_LINK_FLAP_REASON_MASK               0x000000ff
+#define LFA_NO_REASON                                   (0 << 0)
+#define LFA_LINK_DOWN                                  BIT(0)
+#define LFA_FORCE_INIT                                  BIT(1)
+#define LFA_LOOPBACK_MISMATCH                           BIT(2)
+#define LFA_SPEED_MISMATCH                              BIT(3)
+#define LFA_FLOW_CTRL_MISMATCH                          BIT(4)
+#define LFA_ADV_SPEED_MISMATCH                          BIT(5)
+#define LINK_FLAP_AVOIDANCE_COUNT_OFFSET        8
+#define LINK_FLAP_AVOIDANCE_COUNT_MASK          0x0000ff00
+#define LINK_FLAP_COUNT_OFFSET                  16
+#define LINK_FLAP_COUNT_MASK                    0x00ff0000
+
+       u32                                     link_change_count;
+
+       /* LLDP params */
+       struct lldp_config_params_s             lldp_config_params[
+               LLDP_MAX_LLDP_AGENTS];
+       struct lldp_status_params_s             lldp_status_params[
+               LLDP_MAX_LLDP_AGENTS];
+       struct lldp_system_tlvs_buffer_s        system_lldp_tlvs_buf;
+
+       /* DCBX related MIB */
+       struct dcbx_local_params                local_admin_dcbx_mib;
+       struct dcbx_mib                         remote_dcbx_mib;
+       struct dcbx_mib                         operational_dcbx_mib;
+};
+
+/**************************************/
+/*                                    */
+/*     P U B L I C      F U N C       */
+/*                                    */
+/**************************************/
+
+struct public_func {
+       u32     iscsi_boot_signature;
+       u32     iscsi_boot_block_offset;
+
+       u32     reserved[8];
+
+       u32     config;
+
+       /* E/R/I/D */
+       /* function 0 of each port cannot be hidden */
+#define FUNC_MF_CFG_FUNC_HIDE                   0x00000001
+#define FUNC_MF_CFG_PAUSE_ON_HOST_RING          0x00000002
+#define FUNC_MF_CFG_PAUSE_ON_HOST_RING_SHIFT    0x00000001
+
+#define FUNC_MF_CFG_PROTOCOL_MASK               0x000000f0
+#define FUNC_MF_CFG_PROTOCOL_SHIFT              4
+#define FUNC_MF_CFG_PROTOCOL_ETHERNET           0x00000000
+#define FUNC_MF_CFG_PROTOCOL_ISCSI              0x00000010
+#define FUNC_MF_CFG_PROTOCOL_FCOE               0x00000020
+#define FUNC_MF_CFG_PROTOCOL_ROCE               0x00000030
+#define FUNC_MF_CFG_PROTOCOL_MAX                0x00000030
+
+       /* MINBW, MAXBW */
+       /* value range - 0..100, increments in 1 %  */
+#define FUNC_MF_CFG_MIN_BW_MASK                 0x0000ff00
+#define FUNC_MF_CFG_MIN_BW_SHIFT                8
+#define FUNC_MF_CFG_MIN_BW_DEFAULT              0x00000000
+#define FUNC_MF_CFG_MAX_BW_MASK                 0x00ff0000
+#define FUNC_MF_CFG_MAX_BW_SHIFT                16
+#define FUNC_MF_CFG_MAX_BW_DEFAULT              0x00640000
+
+       u32     status;
+#define FUNC_STATUS_VLINK_DOWN                  0x00000001
+
+       u32     mac_upper;  /* MAC */
+#define FUNC_MF_CFG_UPPERMAC_MASK               0x0000ffff
+#define FUNC_MF_CFG_UPPERMAC_SHIFT              0
+#define FUNC_MF_CFG_UPPERMAC_DEFAULT            FUNC_MF_CFG_UPPERMAC_MASK
+       u32     mac_lower;
+#define FUNC_MF_CFG_LOWERMAC_DEFAULT            0xffffffff
+
+       u32     fcoe_wwn_port_name_upper;
+       u32     fcoe_wwn_port_name_lower;
+
+       u32     fcoe_wwn_node_name_upper;
+       u32     fcoe_wwn_node_name_lower;
+
+       u32     ovlan_stag; /* tags */
+#define FUNC_MF_CFG_OV_STAG_MASK              0x0000ffff
+#define FUNC_MF_CFG_OV_STAG_SHIFT             0
+#define FUNC_MF_CFG_OV_STAG_DEFAULT           FUNC_MF_CFG_OV_STAG_MASK
+
+       u32     pf_allocation;  /* vf per pf */
+
+       u32     preserve_data;  /* Will be used bt CCM */
+
+       u32     driver_last_activity_ts;
+
+       u32     drv_ack_vf_disabled[VF_MAX_STATIC / 32]; /* 0x0044 */
+
+       u32     drv_id;
+#define DRV_ID_PDA_COMP_VER_MASK        0x0000ffff
+#define DRV_ID_PDA_COMP_VER_SHIFT       0
+
+#define DRV_ID_MCP_HSI_VER_MASK         0x00ff0000
+#define DRV_ID_MCP_HSI_VER_SHIFT        16
+#define DRV_ID_MCP_HSI_VER_CURRENT     BIT(DRV_ID_MCP_HSI_VER_SHIFT)
+
+#define DRV_ID_DRV_TYPE_MASK            0xff000000
+#define DRV_ID_DRV_TYPE_SHIFT           24
+#define DRV_ID_DRV_TYPE_UNKNOWN         (0 << DRV_ID_DRV_TYPE_SHIFT)
+#define DRV_ID_DRV_TYPE_LINUX          BIT(DRV_ID_DRV_TYPE_SHIFT)
+#define DRV_ID_DRV_TYPE_WINDOWS         (2 << DRV_ID_DRV_TYPE_SHIFT)
+#define DRV_ID_DRV_TYPE_DIAG            (3 << DRV_ID_DRV_TYPE_SHIFT)
+#define DRV_ID_DRV_TYPE_PREBOOT         (4 << DRV_ID_DRV_TYPE_SHIFT)
+#define DRV_ID_DRV_TYPE_SOLARIS         (5 << DRV_ID_DRV_TYPE_SHIFT)
+#define DRV_ID_DRV_TYPE_VMWARE          (6 << DRV_ID_DRV_TYPE_SHIFT)
+#define DRV_ID_DRV_TYPE_FREEBSD         (7 << DRV_ID_DRV_TYPE_SHIFT)
+#define DRV_ID_DRV_TYPE_AIX             (8 << DRV_ID_DRV_TYPE_SHIFT)
+};
+
+/**************************************/
+/*                                    */
+/*     P U B L I C       M B          */
+/*                                    */
+/**************************************/
+/* This is the only section that the driver can write to, and each */
+/* Basically each driver request to set feature parameters,
+ * will be done using a different command, which will be linked
+ * to a specific data structure from the union below.
+ * For huge strucuture, the common blank structure should be used.
+ */
+
+struct mcp_mac {
+       u32     mac_upper;  /* Upper 16 bits are always zeroes */
+       u32     mac_lower;
+};
+
+struct mcp_val64 {
+       u32     lo;
+       u32     hi;
+};
+
+struct mcp_file_att {
+       u32     nvm_start_addr;
+       u32     len;
+};
+
+#define MCP_DRV_VER_STR_SIZE 16
+#define MCP_DRV_VER_STR_SIZE_DWORD (MCP_DRV_VER_STR_SIZE / sizeof(u32))
+#define MCP_DRV_NVM_BUF_LEN 32
+struct drv_version_stc {
+       u32     version;
+       u8      name[MCP_DRV_VER_STR_SIZE - 4];
+};
+
+union drv_union_data {
+       u32                     ver_str[MCP_DRV_VER_STR_SIZE_DWORD];
+       struct mcp_mac          wol_mac;
+
+       struct pmm_phy_cfg      drv_phy_cfg;
+
+       struct mcp_val64        val64; /* For PHY / AVS commands */
+
+       u8                      raw_data[MCP_DRV_NVM_BUF_LEN];
+
+       struct mcp_file_att     file_att;
+
+       u32                     ack_vf_disabled[VF_MAX_STATIC / 32];
+
+       struct drv_version_stc  drv_version;
+};
+
+struct public_drv_mb {
+       u32 drv_mb_header;
+#define DRV_MSG_CODE_MASK                       0xffff0000
+#define DRV_MSG_CODE_LOAD_REQ                   0x10000000
+#define DRV_MSG_CODE_LOAD_DONE                  0x11000000
+#define DRV_MSG_CODE_UNLOAD_REQ                 0x20000000
+#define DRV_MSG_CODE_UNLOAD_DONE                0x21000000
+#define DRV_MSG_CODE_INIT_PHY                   0x22000000
+       /* Params - FORCE - Reinitialize the link regardless of LFA */
+       /*        - DONT_CARE - Don't flap the link if up */
+#define DRV_MSG_CODE_LINK_RESET                 0x23000000
+
+#define DRV_MSG_CODE_SET_LLDP                   0x24000000
+#define DRV_MSG_CODE_SET_DCBX                   0x25000000
+
+#define DRV_MSG_CODE_NIG_DRAIN                  0x30000000
+
+#define DRV_MSG_CODE_INITIATE_FLR               0x02000000
+#define DRV_MSG_CODE_VF_DISABLED_DONE           0xc0000000
+#define DRV_MSG_CODE_CFG_VF_MSIX                0xc0010000
+#define DRV_MSG_CODE_NVM_PUT_FILE_BEGIN         0x00010000
+#define DRV_MSG_CODE_NVM_PUT_FILE_DATA          0x00020000
+#define DRV_MSG_CODE_NVM_GET_FILE_ATT           0x00030000
+#define DRV_MSG_CODE_NVM_READ_NVRAM             0x00050000
+#define DRV_MSG_CODE_NVM_WRITE_NVRAM            0x00060000
+#define DRV_MSG_CODE_NVM_DEL_FILE               0x00080000
+#define DRV_MSG_CODE_MCP_RESET                  0x00090000
+#define DRV_MSG_CODE_SET_SECURE_MODE            0x000a0000
+#define DRV_MSG_CODE_PHY_RAW_READ               0x000b0000
+#define DRV_MSG_CODE_PHY_RAW_WRITE              0x000c0000
+#define DRV_MSG_CODE_PHY_CORE_READ              0x000d0000
+#define DRV_MSG_CODE_PHY_CORE_WRITE             0x000e0000
+#define DRV_MSG_CODE_SET_VERSION                0x000f0000
+
+#define DRV_MSG_SEQ_NUMBER_MASK                 0x0000ffff
+
+       u32 drv_mb_param;
+
+       /* UNLOAD_REQ params */
+#define DRV_MB_PARAM_UNLOAD_WOL_UNKNOWN         0x00000000
+#define DRV_MB_PARAM_UNLOAD_WOL_MCP             0x00000001
+#define DRV_MB_PARAM_UNLOAD_WOL_DISABLED        0x00000002
+#define DRV_MB_PARAM_UNLOAD_WOL_ENABLED         0x00000003
+
+       /* UNLOAD_DONE_params */
+#define DRV_MB_PARAM_UNLOAD_NON_D3_POWER        0x00000001
+
+       /* INIT_PHY params */
+#define DRV_MB_PARAM_INIT_PHY_FORCE             0x00000001
+#define DRV_MB_PARAM_INIT_PHY_DONT_CARE         0x00000002
+
+       /* LLDP / DCBX params*/
+#define DRV_MB_PARAM_LLDP_SEND_MASK             0x00000001
+#define DRV_MB_PARAM_LLDP_SEND_SHIFT            0
+#define DRV_MB_PARAM_LLDP_AGENT_MASK            0x00000006
+#define DRV_MB_PARAM_LLDP_AGENT_SHIFT           1
+#define DRV_MB_PARAM_DCBX_NOTIFY_MASK           0x00000008
+#define DRV_MB_PARAM_DCBX_NOTIFY_SHIFT          3
+
+#define DRV_MB_PARAM_NIG_DRAIN_PERIOD_MS_MASK   0x000000FF
+#define DRV_MB_PARAM_NIG_DRAIN_PERIOD_MS_SHIFT  0
+
+#define DRV_MB_PARAM_NVM_PUT_FILE_BEGIN_MFW     0x1
+#define DRV_MB_PARAM_NVM_PUT_FILE_BEGIN_IMAGE   0x2
+
+#define DRV_MB_PARAM_NVM_OFFSET_SHIFT           0
+#define DRV_MB_PARAM_NVM_OFFSET_MASK            0x00FFFFFF
+#define DRV_MB_PARAM_NVM_LEN_SHIFT              24
+#define DRV_MB_PARAM_NVM_LEN_MASK               0xFF000000
+
+#define DRV_MB_PARAM_PHY_ADDR_SHIFT             0
+#define DRV_MB_PARAM_PHY_ADDR_MASK              0x1FF0FFFF
+#define DRV_MB_PARAM_PHY_LANE_SHIFT             16
+#define DRV_MB_PARAM_PHY_LANE_MASK              0x000F0000
+#define DRV_MB_PARAM_PHY_SELECT_PORT_SHIFT      29
+#define DRV_MB_PARAM_PHY_SELECT_PORT_MASK       0x20000000
+#define DRV_MB_PARAM_PHY_PORT_SHIFT             30
+#define DRV_MB_PARAM_PHY_PORT_MASK              0xc0000000
+
+/* configure vf MSIX params*/
+#define DRV_MB_PARAM_CFG_VF_MSIX_VF_ID_SHIFT    0
+#define DRV_MB_PARAM_CFG_VF_MSIX_VF_ID_MASK     0x000000FF
+#define DRV_MB_PARAM_CFG_VF_MSIX_SB_NUM_SHIFT   8
+#define DRV_MB_PARAM_CFG_VF_MSIX_SB_NUM_MASK    0x0000FF00
+
+       u32 fw_mb_header;
+#define FW_MSG_CODE_MASK                        0xffff0000
+#define FW_MSG_CODE_DRV_LOAD_ENGINE             0x10100000
+#define FW_MSG_CODE_DRV_LOAD_PORT               0x10110000
+#define FW_MSG_CODE_DRV_LOAD_FUNCTION           0x10120000
+#define FW_MSG_CODE_DRV_LOAD_REFUSED_PDA        0x10200000
+#define FW_MSG_CODE_DRV_LOAD_REFUSED_HSI        0x10210000
+#define FW_MSG_CODE_DRV_LOAD_REFUSED_DIAG       0x10220000
+#define FW_MSG_CODE_DRV_LOAD_DONE               0x11100000
+#define FW_MSG_CODE_DRV_UNLOAD_ENGINE           0x20110000
+#define FW_MSG_CODE_DRV_UNLOAD_PORT             0x20120000
+#define FW_MSG_CODE_DRV_UNLOAD_FUNCTION         0x20130000
+#define FW_MSG_CODE_DRV_UNLOAD_DONE             0x21100000
+#define FW_MSG_CODE_INIT_PHY_DONE               0x21200000
+#define FW_MSG_CODE_INIT_PHY_ERR_INVALID_ARGS   0x21300000
+#define FW_MSG_CODE_LINK_RESET_DONE             0x23000000
+#define FW_MSG_CODE_SET_LLDP_DONE               0x24000000
+#define FW_MSG_CODE_SET_LLDP_UNSUPPORTED_AGENT  0x24010000
+#define FW_MSG_CODE_SET_DCBX_DONE               0x25000000
+#define FW_MSG_CODE_NIG_DRAIN_DONE              0x30000000
+#define FW_MSG_CODE_VF_DISABLED_DONE            0xb0000000
+#define FW_MSG_CODE_DRV_CFG_VF_MSIX_DONE        0xb0010000
+#define FW_MSG_CODE_FLR_ACK                     0x02000000
+#define FW_MSG_CODE_FLR_NACK                    0x02100000
+
+#define FW_MSG_CODE_NVM_OK                      0x00010000
+#define FW_MSG_CODE_NVM_INVALID_MODE            0x00020000
+#define FW_MSG_CODE_NVM_PREV_CMD_WAS_NOT_FINISHED       0x00030000
+#define FW_MSG_CODE_NVM_FAILED_TO_ALLOCATE_PAGE 0x00040000
+#define FW_MSG_CODE_NVM_INVALID_DIR_FOUND       0x00050000
+#define FW_MSG_CODE_NVM_PAGE_NOT_FOUND          0x00060000
+#define FW_MSG_CODE_NVM_FAILED_PARSING_BNDLE_HEADER 0x00070000
+#define FW_MSG_CODE_NVM_FAILED_PARSING_IMAGE_HEADER 0x00080000
+#define FW_MSG_CODE_NVM_PARSING_OUT_OF_SYNC     0x00090000
+#define FW_MSG_CODE_NVM_FAILED_UPDATING_DIR     0x000a0000
+#define FW_MSG_CODE_NVM_FAILED_TO_FREE_PAGE     0x000b0000
+#define FW_MSG_CODE_NVM_FILE_NOT_FOUND          0x000c0000
+#define FW_MSG_CODE_NVM_OPERATION_FAILED        0x000d0000
+#define FW_MSG_CODE_NVM_FAILED_UNALIGNED        0x000e0000
+#define FW_MSG_CODE_NVM_BAD_OFFSET              0x000f0000
+#define FW_MSG_CODE_NVM_BAD_SIGNATURE           0x00100000
+#define FW_MSG_CODE_NVM_FILE_READ_ONLY          0x00200000
+#define FW_MSG_CODE_NVM_UNKNOWN_FILE            0x00300000
+#define FW_MSG_CODE_NVM_PUT_FILE_FINISH_OK      0x00400000
+#define FW_MSG_CODE_MCP_RESET_REJECT            0x00600000
+#define FW_MSG_CODE_PHY_OK                      0x00110000
+#define FW_MSG_CODE_PHY_ERROR                   0x00120000
+#define FW_MSG_CODE_SET_SECURE_MODE_ERROR       0x00130000
+#define FW_MSG_CODE_SET_SECURE_MODE_OK          0x00140000
+#define FW_MSG_MODE_PHY_PRIVILEGE_ERROR         0x00150000
+
+#define FW_MSG_SEQ_NUMBER_MASK                  0x0000ffff
+
+       u32     fw_mb_param;
+
+       u32     drv_pulse_mb;
+#define DRV_PULSE_SEQ_MASK                      0x00007fff
+#define DRV_PULSE_SYSTEM_TIME_MASK              0xffff0000
+#define DRV_PULSE_ALWAYS_ALIVE                  0x00008000
+       u32 mcp_pulse_mb;
+#define MCP_PULSE_SEQ_MASK                      0x00007fff
+#define MCP_PULSE_ALWAYS_ALIVE                  0x00008000
+#define MCP_EVENT_MASK                          0xffff0000
+#define MCP_EVENT_OTHER_DRIVER_RESET_REQ        0x00010000
+
+       union drv_union_data union_data;
+};
+
+/* MFW - DRV MB */
+/**********************************************************************
+* Description
+*   Incremental Aggregative
+*   8-bit MFW counter per message
+*   8-bit ack-counter per message
+* Capabilities
+*   Provides up to 256 aggregative message per type
+*   Provides 4 message types in dword
+*   Message type pointers to byte offset
+*   Backward Compatibility by using sizeof for the counters.
+*   No lock requires for 32bit messages
+* Limitations:
+* In case of messages greater than 32bit, a dedicated mechanism(e.g lock)
+* is required to prevent data corruption.
+**********************************************************************/
+enum MFW_DRV_MSG_TYPE {
+       MFW_DRV_MSG_LINK_CHANGE,
+       MFW_DRV_MSG_FLR_FW_ACK_FAILED,
+       MFW_DRV_MSG_VF_DISABLED,
+       MFW_DRV_MSG_LLDP_DATA_UPDATED,
+       MFW_DRV_MSG_DCBX_REMOTE_MIB_UPDATED,
+       MFW_DRV_MSG_DCBX_OPERATIONAL_MIB_UPDATED,
+       MFW_DRV_MSG_ERROR_RECOVERY,
+       MFW_DRV_MSG_MAX
+};
+
+#define MFW_DRV_MSG_MAX_DWORDS(msgs)    (((msgs - 1) >> 2) + 1)
+#define MFW_DRV_MSG_DWORD(msg_id)       (msg_id >> 2)
+#define MFW_DRV_MSG_OFFSET(msg_id)      ((msg_id & 0x3) << 3)
+#define MFW_DRV_MSG_MASK(msg_id)        (0xff << MFW_DRV_MSG_OFFSET(msg_id))
+
+struct public_mfw_mb {
+       u32     sup_msgs;
+       u32     msg[MFW_DRV_MSG_MAX_DWORDS(MFW_DRV_MSG_MAX)];
+       u32     ack[MFW_DRV_MSG_MAX_DWORDS(MFW_DRV_MSG_MAX)];
+};
+
+/**************************************/
+/*                                    */
+/*     P U B L I C       D A T A      */
+/*                                    */
+/**************************************/
+enum public_sections {
+       PUBLIC_DRV_MB,          /* Points to the first drv_mb of path0 */
+       PUBLIC_MFW_MB,          /* Points to the first mfw_mb of path0 */
+       PUBLIC_GLOBAL,
+       PUBLIC_PATH,
+       PUBLIC_PORT,
+       PUBLIC_FUNC,
+       PUBLIC_MAX_SECTIONS
+};
+
+struct drv_ver_info_stc {
+       u32     ver;
+       u8      name[32];
+};
+
+struct mcp_public_data {
+       /* The sections fields is an array */
+       u32                     num_sections;
+       offsize_t               sections[PUBLIC_MAX_SECTIONS];
+       struct public_drv_mb    drv_mb[MCP_GLOB_FUNC_MAX];
+       struct public_mfw_mb    mfw_mb[MCP_GLOB_FUNC_MAX];
+       struct public_global    global;
+       struct public_path      path[MCP_GLOB_PATH_MAX];
+       struct public_port      port[MCP_GLOB_PORT_MAX];
+       struct public_func      func[MCP_GLOB_FUNC_MAX];
+       struct drv_ver_info_stc drv_info;
+};
+
+struct nvm_cfg_mac_address {
+       u32     mac_addr_hi;
+#define NVM_CFG_MAC_ADDRESS_HI_MASK                             0x0000FFFF
+#define NVM_CFG_MAC_ADDRESS_HI_OFFSET                           0
+
+       u32     mac_addr_lo;
+};
+
+/******************************************
+* nvm_cfg1 structs
+******************************************/
+
+struct nvm_cfg1_glob {
+       u32 generic_cont0;                                      /* 0x0 */
+#define NVM_CFG1_GLOB_BOARD_SWAP_MASK                           0x0000000F
+#define NVM_CFG1_GLOB_BOARD_SWAP_OFFSET                         0
+#define NVM_CFG1_GLOB_BOARD_SWAP_NONE                           0x0
+#define NVM_CFG1_GLOB_BOARD_SWAP_PATH                           0x1
+#define NVM_CFG1_GLOB_BOARD_SWAP_PORT                           0x2
+#define NVM_CFG1_GLOB_BOARD_SWAP_BOTH                           0x3
+#define NVM_CFG1_GLOB_MF_MODE_MASK                              0x00000FF0
+#define NVM_CFG1_GLOB_MF_MODE_OFFSET                            4
+#define NVM_CFG1_GLOB_MF_MODE_MF_ALLOWED                        0x0
+#define NVM_CFG1_GLOB_MF_MODE_FORCED_SF                         0x1
+#define NVM_CFG1_GLOB_MF_MODE_SPIO4                             0x2
+#define NVM_CFG1_GLOB_MF_MODE_NPAR1_0                           0x3
+#define NVM_CFG1_GLOB_MF_MODE_NPAR1_5                           0x4
+#define NVM_CFG1_GLOB_MF_MODE_NPAR2_0                           0x5
+#define NVM_CFG1_GLOB_MF_MODE_BD                                0x6
+#define NVM_CFG1_GLOB_MF_MODE_UFP                               0x7
+#define NVM_CFG1_GLOB_FAN_FAILURE_ENFORCEMENT_MASK              0x00001000
+#define NVM_CFG1_GLOB_FAN_FAILURE_ENFORCEMENT_OFFSET            12
+#define NVM_CFG1_GLOB_FAN_FAILURE_ENFORCEMENT_DISABLED          0x0
+#define NVM_CFG1_GLOB_FAN_FAILURE_ENFORCEMENT_ENABLED           0x1
+#define NVM_CFG1_GLOB_AVS_MARGIN_LOW_MASK                       0x001FE000
+#define NVM_CFG1_GLOB_AVS_MARGIN_LOW_OFFSET                     13
+#define NVM_CFG1_GLOB_AVS_MARGIN_HIGH_MASK                      0x1FE00000
+#define NVM_CFG1_GLOB_AVS_MARGIN_HIGH_OFFSET                    21
+#define NVM_CFG1_GLOB_ENABLE_SRIOV_MASK                         0x20000000
+#define NVM_CFG1_GLOB_ENABLE_SRIOV_OFFSET                       29
+#define NVM_CFG1_GLOB_ENABLE_SRIOV_DISABLED                     0x0
+#define NVM_CFG1_GLOB_ENABLE_SRIOV_ENABLED                      0x1
+#define NVM_CFG1_GLOB_ENABLE_ATC_MASK                           0x40000000
+#define NVM_CFG1_GLOB_ENABLE_ATC_OFFSET                         30
+#define NVM_CFG1_GLOB_ENABLE_ATC_DISABLED                       0x0
+#define NVM_CFG1_GLOB_ENABLE_ATC_ENABLED                        0x1
+#define NVM_CFG1_GLOB_CLOCK_SLOWDOWN_MASK                       0x80000000
+#define NVM_CFG1_GLOB_CLOCK_SLOWDOWN_OFFSET                     31
+#define NVM_CFG1_GLOB_CLOCK_SLOWDOWN_DISABLED                   0x0
+#define NVM_CFG1_GLOB_CLOCK_SLOWDOWN_ENABLED                    0x1
+
+       u32     engineering_change[3];                          /* 0x4 */
+
+       u32     manufacturing_id;                               /* 0x10 */
+
+       u32     serial_number[4];                               /* 0x14 */
+
+       u32     pcie_cfg;                                       /* 0x24 */
+#define NVM_CFG1_GLOB_PCI_GEN_MASK                              0x00000003
+#define NVM_CFG1_GLOB_PCI_GEN_OFFSET                            0
+#define NVM_CFG1_GLOB_PCI_GEN_PCI_GEN1                          0x0
+#define NVM_CFG1_GLOB_PCI_GEN_PCI_GEN2                          0x1
+#define NVM_CFG1_GLOB_PCI_GEN_PCI_GEN3                          0x2
+#define NVM_CFG1_GLOB_BEACON_WOL_ENABLED_MASK                   0x00000004
+#define NVM_CFG1_GLOB_BEACON_WOL_ENABLED_OFFSET                 2
+#define NVM_CFG1_GLOB_BEACON_WOL_ENABLED_DISABLED               0x0
+#define NVM_CFG1_GLOB_BEACON_WOL_ENABLED_ENABLED                0x1
+#define NVM_CFG1_GLOB_ASPM_SUPPORT_MASK                         0x00000018
+#define NVM_CFG1_GLOB_ASPM_SUPPORT_OFFSET                       3
+#define NVM_CFG1_GLOB_ASPM_SUPPORT_L0S_L1_ENABLED               0x0
+#define NVM_CFG1_GLOB_ASPM_SUPPORT_L0S_DISABLED                 0x1
+#define NVM_CFG1_GLOB_ASPM_SUPPORT_L1_DISABLED                  0x2
+#define NVM_CFG1_GLOB_ASPM_SUPPORT_L0S_L1_DISABLED              0x3
+#define NVM_CFG1_GLOB_PREVENT_PCIE_L1_MENTRY_MASK               0x00000020
+#define NVM_CFG1_GLOB_PREVENT_PCIE_L1_MENTRY_OFFSET             5
+#define NVM_CFG1_GLOB_PREVENT_PCIE_L1_MENTRY_DISABLED           0x0
+#define NVM_CFG1_GLOB_PREVENT_PCIE_L1_MENTRY_ENABLED            0x1
+#define NVM_CFG1_GLOB_PCIE_G2_TX_AMPLITUDE_MASK                 0x000003C0
+#define NVM_CFG1_GLOB_PCIE_G2_TX_AMPLITUDE_OFFSET               6
+#define NVM_CFG1_GLOB_PCIE_PREEMPHASIS_MASK                     0x00001C00
+#define NVM_CFG1_GLOB_PCIE_PREEMPHASIS_OFFSET                   10
+#define NVM_CFG1_GLOB_PCIE_PREEMPHASIS_HW                       0x0
+#define NVM_CFG1_GLOB_PCIE_PREEMPHASIS_0DB                      0x1
+#define NVM_CFG1_GLOB_PCIE_PREEMPHASIS_3_5DB                    0x2
+#define NVM_CFG1_GLOB_PCIE_PREEMPHASIS_6_0DB                    0x3
+#define NVM_CFG1_GLOB_WWN_NODE_PREFIX0_MASK                     0x001FE000
+#define NVM_CFG1_GLOB_WWN_NODE_PREFIX0_OFFSET                   13
+#define NVM_CFG1_GLOB_WWN_NODE_PREFIX1_MASK                     0x1FE00000
+#define NVM_CFG1_GLOB_WWN_NODE_PREFIX1_OFFSET                   21
+#define NVM_CFG1_GLOB_NCSI_PACKAGE_ID_MASK                      0x60000000
+#define NVM_CFG1_GLOB_NCSI_PACKAGE_ID_OFFSET                    29
+
+       u32 mgmt_traffic;                                       /* 0x28 */
+#define NVM_CFG1_GLOB_RESERVED60_MASK                           0x00000001
+#define NVM_CFG1_GLOB_RESERVED60_OFFSET                         0
+#define NVM_CFG1_GLOB_RESERVED60_100KHZ                         0x0
+#define NVM_CFG1_GLOB_RESERVED60_400KHZ                         0x1
+#define NVM_CFG1_GLOB_WWN_PORT_PREFIX0_MASK                     0x000001FE
+#define NVM_CFG1_GLOB_WWN_PORT_PREFIX0_OFFSET                   1
+#define NVM_CFG1_GLOB_WWN_PORT_PREFIX1_MASK                     0x0001FE00
+#define NVM_CFG1_GLOB_WWN_PORT_PREFIX1_OFFSET                   9
+#define NVM_CFG1_GLOB_SMBUS_ADDRESS_MASK                        0x01FE0000
+#define NVM_CFG1_GLOB_SMBUS_ADDRESS_OFFSET                      17
+#define NVM_CFG1_GLOB_SIDEBAND_MODE_MASK                        0x06000000
+#define NVM_CFG1_GLOB_SIDEBAND_MODE_OFFSET                      25
+#define NVM_CFG1_GLOB_SIDEBAND_MODE_DISABLED                    0x0
+#define NVM_CFG1_GLOB_SIDEBAND_MODE_RMII                        0x1
+#define NVM_CFG1_GLOB_SIDEBAND_MODE_SGMII                       0x2
+
+       u32 core_cfg;                                           /* 0x2C */
+#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_MASK                    0x000000FF
+#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_OFFSET                  0
+#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_2X40G                0x0
+#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_2X50G                0x1
+#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_1X100G               0x2
+#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_4X10G_F              0x3
+#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_4X10G_E              0x4
+#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_4X20G                0x5
+#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_1X40G                0xB
+#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_2X25G                0xC
+#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_DE_1X25G                0xD
+#define NVM_CFG1_GLOB_EAGLE_ENFORCE_TX_FIR_CFG_MASK             0x00000100
+#define NVM_CFG1_GLOB_EAGLE_ENFORCE_TX_FIR_CFG_OFFSET           8
+#define NVM_CFG1_GLOB_EAGLE_ENFORCE_TX_FIR_CFG_DISABLED         0x0
+#define NVM_CFG1_GLOB_EAGLE_ENFORCE_TX_FIR_CFG_ENABLED          0x1
+#define NVM_CFG1_GLOB_FALCON_ENFORCE_TX_FIR_CFG_MASK            0x00000200
+#define NVM_CFG1_GLOB_FALCON_ENFORCE_TX_FIR_CFG_OFFSET          9
+#define NVM_CFG1_GLOB_FALCON_ENFORCE_TX_FIR_CFG_DISABLED        0x0
+#define NVM_CFG1_GLOB_FALCON_ENFORCE_TX_FIR_CFG_ENABLED         0x1
+#define NVM_CFG1_GLOB_EAGLE_CORE_ADDR_MASK                      0x0003FC00
+#define NVM_CFG1_GLOB_EAGLE_CORE_ADDR_OFFSET                    10
+#define NVM_CFG1_GLOB_FALCON_CORE_ADDR_MASK                     0x03FC0000
+#define NVM_CFG1_GLOB_FALCON_CORE_ADDR_OFFSET                   18
+#define NVM_CFG1_GLOB_AVS_MODE_MASK                             0x1C000000
+#define NVM_CFG1_GLOB_AVS_MODE_OFFSET                           26
+#define NVM_CFG1_GLOB_AVS_MODE_CLOSE_LOOP                       0x0
+#define NVM_CFG1_GLOB_AVS_MODE_OPEN_LOOP                        0x1
+#define NVM_CFG1_GLOB_AVS_MODE_DISABLED                         0x3
+#define NVM_CFG1_GLOB_OVERRIDE_SECURE_MODE_MASK                 0x60000000
+#define NVM_CFG1_GLOB_OVERRIDE_SECURE_MODE_OFFSET               29
+#define NVM_CFG1_GLOB_OVERRIDE_SECURE_MODE_DISABLED             0x0
+#define NVM_CFG1_GLOB_OVERRIDE_SECURE_MODE_ENABLED              0x1
+
+       u32 e_lane_cfg1;                                        /* 0x30 */
+#define NVM_CFG1_GLOB_RX_LANE0_SWAP_MASK                        0x0000000F
+#define NVM_CFG1_GLOB_RX_LANE0_SWAP_OFFSET                      0
+#define NVM_CFG1_GLOB_RX_LANE1_SWAP_MASK                        0x000000F0
+#define NVM_CFG1_GLOB_RX_LANE1_SWAP_OFFSET                      4
+#define NVM_CFG1_GLOB_RX_LANE2_SWAP_MASK                        0x00000F00
+#define NVM_CFG1_GLOB_RX_LANE2_SWAP_OFFSET                      8
+#define NVM_CFG1_GLOB_RX_LANE3_SWAP_MASK                        0x0000F000
+#define NVM_CFG1_GLOB_RX_LANE3_SWAP_OFFSET                      12
+#define NVM_CFG1_GLOB_TX_LANE0_SWAP_MASK                        0x000F0000
+#define NVM_CFG1_GLOB_TX_LANE0_SWAP_OFFSET                      16
+#define NVM_CFG1_GLOB_TX_LANE1_SWAP_MASK                        0x00F00000
+#define NVM_CFG1_GLOB_TX_LANE1_SWAP_OFFSET                      20
+#define NVM_CFG1_GLOB_TX_LANE2_SWAP_MASK                        0x0F000000
+#define NVM_CFG1_GLOB_TX_LANE2_SWAP_OFFSET                      24
+#define NVM_CFG1_GLOB_TX_LANE3_SWAP_MASK                        0xF0000000
+#define NVM_CFG1_GLOB_TX_LANE3_SWAP_OFFSET                      28
+
+       u32 e_lane_cfg2;                                        /* 0x34 */
+#define NVM_CFG1_GLOB_RX_LANE0_POL_FLIP_MASK                    0x00000001
+#define NVM_CFG1_GLOB_RX_LANE0_POL_FLIP_OFFSET                  0
+#define NVM_CFG1_GLOB_RX_LANE1_POL_FLIP_MASK                    0x00000002
+#define NVM_CFG1_GLOB_RX_LANE1_POL_FLIP_OFFSET                  1
+#define NVM_CFG1_GLOB_RX_LANE2_POL_FLIP_MASK                    0x00000004
+#define NVM_CFG1_GLOB_RX_LANE2_POL_FLIP_OFFSET                  2
+#define NVM_CFG1_GLOB_RX_LANE3_POL_FLIP_MASK                    0x00000008
+#define NVM_CFG1_GLOB_RX_LANE3_POL_FLIP_OFFSET                  3
+#define NVM_CFG1_GLOB_TX_LANE0_POL_FLIP_MASK                    0x00000010
+#define NVM_CFG1_GLOB_TX_LANE0_POL_FLIP_OFFSET                  4
+#define NVM_CFG1_GLOB_TX_LANE1_POL_FLIP_MASK                    0x00000020
+#define NVM_CFG1_GLOB_TX_LANE1_POL_FLIP_OFFSET                  5
+#define NVM_CFG1_GLOB_TX_LANE2_POL_FLIP_MASK                    0x00000040
+#define NVM_CFG1_GLOB_TX_LANE2_POL_FLIP_OFFSET                  6
+#define NVM_CFG1_GLOB_TX_LANE3_POL_FLIP_MASK                    0x00000080
+#define NVM_CFG1_GLOB_TX_LANE3_POL_FLIP_OFFSET                  7
+#define NVM_CFG1_GLOB_SMBUS_MODE_MASK                           0x00000F00
+#define NVM_CFG1_GLOB_SMBUS_MODE_OFFSET                         8
+#define NVM_CFG1_GLOB_SMBUS_MODE_DISABLED                       0x0
+#define NVM_CFG1_GLOB_SMBUS_MODE_100KHZ                         0x1
+#define NVM_CFG1_GLOB_SMBUS_MODE_400KHZ                         0x2
+#define NVM_CFG1_GLOB_NCSI_MASK                                 0x0000F000
+#define NVM_CFG1_GLOB_NCSI_OFFSET                               12
+#define NVM_CFG1_GLOB_NCSI_DISABLED                             0x0
+#define NVM_CFG1_GLOB_NCSI_ENABLED                              0x1
+
+       u32 f_lane_cfg1;                                        /* 0x38 */
+#define NVM_CFG1_GLOB_RX_LANE0_SWAP_MASK                        0x0000000F
+#define NVM_CFG1_GLOB_RX_LANE0_SWAP_OFFSET                      0
+#define NVM_CFG1_GLOB_RX_LANE1_SWAP_MASK                        0x000000F0
+#define NVM_CFG1_GLOB_RX_LANE1_SWAP_OFFSET                      4
+#define NVM_CFG1_GLOB_RX_LANE2_SWAP_MASK                        0x00000F00
+#define NVM_CFG1_GLOB_RX_LANE2_SWAP_OFFSET                      8
+#define NVM_CFG1_GLOB_RX_LANE3_SWAP_MASK                        0x0000F000
+#define NVM_CFG1_GLOB_RX_LANE3_SWAP_OFFSET                      12
+#define NVM_CFG1_GLOB_TX_LANE0_SWAP_MASK                        0x000F0000
+#define NVM_CFG1_GLOB_TX_LANE0_SWAP_OFFSET                      16
+#define NVM_CFG1_GLOB_TX_LANE1_SWAP_MASK                        0x00F00000
+#define NVM_CFG1_GLOB_TX_LANE1_SWAP_OFFSET                      20
+#define NVM_CFG1_GLOB_TX_LANE2_SWAP_MASK                        0x0F000000
+#define NVM_CFG1_GLOB_TX_LANE2_SWAP_OFFSET                      24
+#define NVM_CFG1_GLOB_TX_LANE3_SWAP_MASK                        0xF0000000
+#define NVM_CFG1_GLOB_TX_LANE3_SWAP_OFFSET                      28
+
+       u32 f_lane_cfg2;                                        /* 0x3C */
+#define NVM_CFG1_GLOB_RX_LANE0_POL_FLIP_MASK                    0x00000001
+#define NVM_CFG1_GLOB_RX_LANE0_POL_FLIP_OFFSET                  0
+#define NVM_CFG1_GLOB_RX_LANE1_POL_FLIP_MASK                    0x00000002
+#define NVM_CFG1_GLOB_RX_LANE1_POL_FLIP_OFFSET                  1
+#define NVM_CFG1_GLOB_RX_LANE2_POL_FLIP_MASK                    0x00000004
+#define NVM_CFG1_GLOB_RX_LANE2_POL_FLIP_OFFSET                  2
+#define NVM_CFG1_GLOB_RX_LANE3_POL_FLIP_MASK                    0x00000008
+#define NVM_CFG1_GLOB_RX_LANE3_POL_FLIP_OFFSET                  3
+#define NVM_CFG1_GLOB_TX_LANE0_POL_FLIP_MASK                    0x00000010
+#define NVM_CFG1_GLOB_TX_LANE0_POL_FLIP_OFFSET                  4
+#define NVM_CFG1_GLOB_TX_LANE1_POL_FLIP_MASK                    0x00000020
+#define NVM_CFG1_GLOB_TX_LANE1_POL_FLIP_OFFSET                  5
+#define NVM_CFG1_GLOB_TX_LANE2_POL_FLIP_MASK                    0x00000040
+#define NVM_CFG1_GLOB_TX_LANE2_POL_FLIP_OFFSET                  6
+#define NVM_CFG1_GLOB_TX_LANE3_POL_FLIP_MASK                    0x00000080
+#define NVM_CFG1_GLOB_TX_LANE3_POL_FLIP_OFFSET                  7
+
+       u32 eagle_preemphasis;                                  /* 0x40 */
+#define NVM_CFG1_GLOB_LANE0_PREEMP_MASK                         0x000000FF
+#define NVM_CFG1_GLOB_LANE0_PREEMP_OFFSET                       0
+#define NVM_CFG1_GLOB_LANE1_PREEMP_MASK                         0x0000FF00
+#define NVM_CFG1_GLOB_LANE1_PREEMP_OFFSET                       8
+#define NVM_CFG1_GLOB_LANE2_PREEMP_MASK                         0x00FF0000
+#define NVM_CFG1_GLOB_LANE2_PREEMP_OFFSET                       16
+#define NVM_CFG1_GLOB_LANE3_PREEMP_MASK                         0xFF000000
+#define NVM_CFG1_GLOB_LANE3_PREEMP_OFFSET                       24
+
+       u32 eagle_driver_current;                               /* 0x44 */
+#define NVM_CFG1_GLOB_LANE0_AMP_MASK                            0x000000FF
+#define NVM_CFG1_GLOB_LANE0_AMP_OFFSET                          0
+#define NVM_CFG1_GLOB_LANE1_AMP_MASK                            0x0000FF00
+#define NVM_CFG1_GLOB_LANE1_AMP_OFFSET                          8
+#define NVM_CFG1_GLOB_LANE2_AMP_MASK                            0x00FF0000
+#define NVM_CFG1_GLOB_LANE2_AMP_OFFSET                          16
+#define NVM_CFG1_GLOB_LANE3_AMP_MASK                            0xFF000000
+#define NVM_CFG1_GLOB_LANE3_AMP_OFFSET                          24
+
+       u32 falcon_preemphasis;                                 /* 0x48 */
+#define NVM_CFG1_GLOB_LANE0_PREEMP_MASK                         0x000000FF
+#define NVM_CFG1_GLOB_LANE0_PREEMP_OFFSET                       0
+#define NVM_CFG1_GLOB_LANE1_PREEMP_MASK                         0x0000FF00
+#define NVM_CFG1_GLOB_LANE1_PREEMP_OFFSET                       8
+#define NVM_CFG1_GLOB_LANE2_PREEMP_MASK                         0x00FF0000
+#define NVM_CFG1_GLOB_LANE2_PREEMP_OFFSET                       16
+#define NVM_CFG1_GLOB_LANE3_PREEMP_MASK                         0xFF000000
+#define NVM_CFG1_GLOB_LANE3_PREEMP_OFFSET                       24
+
+       u32 falcon_driver_current;                              /* 0x4C */
+#define NVM_CFG1_GLOB_LANE0_AMP_MASK                            0x000000FF
+#define NVM_CFG1_GLOB_LANE0_AMP_OFFSET                          0
+#define NVM_CFG1_GLOB_LANE1_AMP_MASK                            0x0000FF00
+#define NVM_CFG1_GLOB_LANE1_AMP_OFFSET                          8
+#define NVM_CFG1_GLOB_LANE2_AMP_MASK                            0x00FF0000
+#define NVM_CFG1_GLOB_LANE2_AMP_OFFSET                          16
+#define NVM_CFG1_GLOB_LANE3_AMP_MASK                            0xFF000000
+#define NVM_CFG1_GLOB_LANE3_AMP_OFFSET                          24
+
+       u32     pci_id;                                         /* 0x50 */
+#define NVM_CFG1_GLOB_VENDOR_ID_MASK                            0x0000FFFF
+#define NVM_CFG1_GLOB_VENDOR_ID_OFFSET                          0
+
+       u32     pci_subsys_id;                                  /* 0x54 */
+#define NVM_CFG1_GLOB_SUBSYSTEM_VENDOR_ID_MASK                  0x0000FFFF
+#define NVM_CFG1_GLOB_SUBSYSTEM_VENDOR_ID_OFFSET                0
+#define NVM_CFG1_GLOB_SUBSYSTEM_DEVICE_ID_MASK                  0xFFFF0000
+#define NVM_CFG1_GLOB_SUBSYSTEM_DEVICE_ID_OFFSET                16
+
+       u32     bar;                                            /* 0x58 */
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_MASK                   0x0000000F
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_OFFSET                 0
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_DISABLED               0x0
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_2K                     0x1
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_4K                     0x2
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_8K                     0x3
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_16K                    0x4
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_32K                    0x5
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_64K                    0x6
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_128K                   0x7
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_256K                   0x8
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_512K                   0x9
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_1M                     0xA
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_2M                     0xB
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_4M                     0xC
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_8M                     0xD
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_16M                    0xE
+#define NVM_CFG1_GLOB_EXPANSION_ROM_SIZE_32M                    0xF
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_MASK                     0x000000F0
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_OFFSET                   4
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_DISABLED                 0x0
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_4K                       0x1
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_8K                       0x2
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_16K                      0x3
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_32K                      0x4
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_64K                      0x5
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_128K                     0x6
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_256K                     0x7
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_512K                     0x8
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_1M                       0x9
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_2M                       0xA
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_4M                       0xB
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_8M                       0xC
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_16M                      0xD
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_32M                      0xE
+#define NVM_CFG1_GLOB_VF_PCI_BAR2_SIZE_64M                      0xF
+#define NVM_CFG1_GLOB_BAR2_SIZE_MASK                            0x00000F00
+#define NVM_CFG1_GLOB_BAR2_SIZE_OFFSET                          8
+#define NVM_CFG1_GLOB_BAR2_SIZE_DISABLED                        0x0
+#define NVM_CFG1_GLOB_BAR2_SIZE_64K                             0x1
+#define NVM_CFG1_GLOB_BAR2_SIZE_128K                            0x2
+#define NVM_CFG1_GLOB_BAR2_SIZE_256K                            0x3
+#define NVM_CFG1_GLOB_BAR2_SIZE_512K                            0x4
+#define NVM_CFG1_GLOB_BAR2_SIZE_1M                              0x5
+#define NVM_CFG1_GLOB_BAR2_SIZE_2M                              0x6
+#define NVM_CFG1_GLOB_BAR2_SIZE_4M                              0x7
+#define NVM_CFG1_GLOB_BAR2_SIZE_8M                              0x8
+#define NVM_CFG1_GLOB_BAR2_SIZE_16M                             0x9
+#define NVM_CFG1_GLOB_BAR2_SIZE_32M                             0xA
+#define NVM_CFG1_GLOB_BAR2_SIZE_64M                             0xB
+#define NVM_CFG1_GLOB_BAR2_SIZE_128M                            0xC
+#define NVM_CFG1_GLOB_BAR2_SIZE_256M                            0xD
+#define NVM_CFG1_GLOB_BAR2_SIZE_512M                            0xE
+#define NVM_CFG1_GLOB_BAR2_SIZE_1G                              0xF
+
+       u32 eagle_txfir_main;                                   /* 0x5C */
+#define NVM_CFG1_GLOB_LANE0_TXFIR_MAIN_MASK                     0x000000FF
+#define NVM_CFG1_GLOB_LANE0_TXFIR_MAIN_OFFSET                   0
+#define NVM_CFG1_GLOB_LANE1_TXFIR_MAIN_MASK                     0x0000FF00
+#define NVM_CFG1_GLOB_LANE1_TXFIR_MAIN_OFFSET                   8
+#define NVM_CFG1_GLOB_LANE2_TXFIR_MAIN_MASK                     0x00FF0000
+#define NVM_CFG1_GLOB_LANE2_TXFIR_MAIN_OFFSET                   16
+#define NVM_CFG1_GLOB_LANE3_TXFIR_MAIN_MASK                     0xFF000000
+#define NVM_CFG1_GLOB_LANE3_TXFIR_MAIN_OFFSET                   24
+
+       u32 eagle_txfir_post;                                   /* 0x60 */
+#define NVM_CFG1_GLOB_LANE0_TXFIR_POST_MASK                     0x000000FF
+#define NVM_CFG1_GLOB_LANE0_TXFIR_POST_OFFSET                   0
+#define NVM_CFG1_GLOB_LANE1_TXFIR_POST_MASK                     0x0000FF00
+#define NVM_CFG1_GLOB_LANE1_TXFIR_POST_OFFSET                   8
+#define NVM_CFG1_GLOB_LANE2_TXFIR_POST_MASK                     0x00FF0000
+#define NVM_CFG1_GLOB_LANE2_TXFIR_POST_OFFSET                   16
+#define NVM_CFG1_GLOB_LANE3_TXFIR_POST_MASK                     0xFF000000
+#define NVM_CFG1_GLOB_LANE3_TXFIR_POST_OFFSET                   24
+
+       u32 falcon_txfir_main;                                  /* 0x64 */
+#define NVM_CFG1_GLOB_LANE0_TXFIR_MAIN_MASK                     0x000000FF
+#define NVM_CFG1_GLOB_LANE0_TXFIR_MAIN_OFFSET                   0
+#define NVM_CFG1_GLOB_LANE1_TXFIR_MAIN_MASK                     0x0000FF00
+#define NVM_CFG1_GLOB_LANE1_TXFIR_MAIN_OFFSET                   8
+#define NVM_CFG1_GLOB_LANE2_TXFIR_MAIN_MASK                     0x00FF0000
+#define NVM_CFG1_GLOB_LANE2_TXFIR_MAIN_OFFSET                   16
+#define NVM_CFG1_GLOB_LANE3_TXFIR_MAIN_MASK                     0xFF000000
+#define NVM_CFG1_GLOB_LANE3_TXFIR_MAIN_OFFSET                   24
+
+       u32 falcon_txfir_post;                                  /* 0x68 */
+#define NVM_CFG1_GLOB_LANE0_TXFIR_POST_MASK                     0x000000FF
+#define NVM_CFG1_GLOB_LANE0_TXFIR_POST_OFFSET                   0
+#define NVM_CFG1_GLOB_LANE1_TXFIR_POST_MASK                     0x0000FF00
+#define NVM_CFG1_GLOB_LANE1_TXFIR_POST_OFFSET                   8
+#define NVM_CFG1_GLOB_LANE2_TXFIR_POST_MASK                     0x00FF0000
+#define NVM_CFG1_GLOB_LANE2_TXFIR_POST_OFFSET                   16
+#define NVM_CFG1_GLOB_LANE3_TXFIR_POST_MASK                     0xFF000000
+#define NVM_CFG1_GLOB_LANE3_TXFIR_POST_OFFSET                   24
+
+       u32 manufacture_ver;                                    /* 0x6C */
+#define NVM_CFG1_GLOB_MANUF0_VER_MASK                           0x0000003F
+#define NVM_CFG1_GLOB_MANUF0_VER_OFFSET                         0
+#define NVM_CFG1_GLOB_MANUF1_VER_MASK                           0x00000FC0
+#define NVM_CFG1_GLOB_MANUF1_VER_OFFSET                         6
+#define NVM_CFG1_GLOB_MANUF2_VER_MASK                           0x0003F000
+#define NVM_CFG1_GLOB_MANUF2_VER_OFFSET                         12
+#define NVM_CFG1_GLOB_MANUF3_VER_MASK                           0x00FC0000
+#define NVM_CFG1_GLOB_MANUF3_VER_OFFSET                         18
+#define NVM_CFG1_GLOB_MANUF4_VER_MASK                           0x3F000000
+#define NVM_CFG1_GLOB_MANUF4_VER_OFFSET                         24
+
+       u32 manufacture_time;                                   /* 0x70 */
+#define NVM_CFG1_GLOB_MANUF0_TIME_MASK                          0x0000003F
+#define NVM_CFG1_GLOB_MANUF0_TIME_OFFSET                        0
+#define NVM_CFG1_GLOB_MANUF1_TIME_MASK                          0x00000FC0
+#define NVM_CFG1_GLOB_MANUF1_TIME_OFFSET                        6
+#define NVM_CFG1_GLOB_MANUF2_TIME_MASK                          0x0003F000
+#define NVM_CFG1_GLOB_MANUF2_TIME_OFFSET                        12
+
+       u32 led_global_settings;                                /* 0x74 */
+#define NVM_CFG1_GLOB_LED_SWAP_0_MASK                           0x0000000F
+#define NVM_CFG1_GLOB_LED_SWAP_0_OFFSET                         0
+#define NVM_CFG1_GLOB_LED_SWAP_1_MASK                           0x000000F0
+#define NVM_CFG1_GLOB_LED_SWAP_1_OFFSET                         4
+#define NVM_CFG1_GLOB_LED_SWAP_2_MASK                           0x00000F00
+#define NVM_CFG1_GLOB_LED_SWAP_2_OFFSET                         8
+#define NVM_CFG1_GLOB_LED_SWAP_3_MASK                           0x0000F000
+#define NVM_CFG1_GLOB_LED_SWAP_3_OFFSET                         12
+
+       u32     generic_cont1;                                  /* 0x78 */
+#define NVM_CFG1_GLOB_AVS_DAC_CODE_MASK                         0x000003FF
+#define NVM_CFG1_GLOB_AVS_DAC_CODE_OFFSET                       0
+
+       u32     mbi_version;                                    /* 0x7C */
+#define NVM_CFG1_GLOB_MBI_VERSION_0_MASK                        0x000000FF
+#define NVM_CFG1_GLOB_MBI_VERSION_0_OFFSET                      0
+#define NVM_CFG1_GLOB_MBI_VERSION_1_MASK                        0x0000FF00
+#define NVM_CFG1_GLOB_MBI_VERSION_1_OFFSET                      8
+#define NVM_CFG1_GLOB_MBI_VERSION_2_MASK                        0x00FF0000
+#define NVM_CFG1_GLOB_MBI_VERSION_2_OFFSET                      16
+
+       u32     mbi_date;                                       /* 0x80 */
+
+       u32     misc_sig;                                       /* 0x84 */
+
+       /*  Define the GPIO mapping to switch i2c mux */
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO_0_MASK                   0x000000FF
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO_0_OFFSET                 0
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO_1_MASK                   0x0000FF00
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO_1_OFFSET                 8
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__NA                      0x0
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO0                   0x1
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO1                   0x2
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO2                   0x3
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO3                   0x4
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO4                   0x5
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO5                   0x6
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO6                   0x7
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO7                   0x8
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO8                   0x9
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO9                   0xA
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO10                  0xB
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO11                  0xC
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO12                  0xD
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO13                  0xE
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO14                  0xF
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO15                  0x10
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO16                  0x11
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO17                  0x12
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO18                  0x13
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO19                  0x14
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO20                  0x15
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO21                  0x16
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO22                  0x17
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO23                  0x18
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO24                  0x19
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO25                  0x1A
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO26                  0x1B
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO27                  0x1C
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO28                  0x1D
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO29                  0x1E
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO30                  0x1F
+#define NVM_CFG1_GLOB_I2C_MUX_SEL_GPIO__GPIO31                  0x20
+
+       u32 reserved[46];                                       /* 0x88 */
+};
+
+struct nvm_cfg1_path {
+       u32 reserved[30];                                       /* 0x0 */
+};
+
+struct nvm_cfg1_port {
+       u32 power_dissipated;                                   /* 0x0 */
+#define NVM_CFG1_PORT_POWER_DIS_D0_MASK                         0x000000FF
+#define NVM_CFG1_PORT_POWER_DIS_D0_OFFSET                       0
+#define NVM_CFG1_PORT_POWER_DIS_D1_MASK                         0x0000FF00
+#define NVM_CFG1_PORT_POWER_DIS_D1_OFFSET                       8
+#define NVM_CFG1_PORT_POWER_DIS_D2_MASK                         0x00FF0000
+#define NVM_CFG1_PORT_POWER_DIS_D2_OFFSET                       16
+#define NVM_CFG1_PORT_POWER_DIS_D3_MASK                         0xFF000000
+#define NVM_CFG1_PORT_POWER_DIS_D3_OFFSET                       24
+
+       u32 power_consumed;                                     /* 0x4 */
+#define NVM_CFG1_PORT_POWER_CONS_D0_MASK                        0x000000FF
+#define NVM_CFG1_PORT_POWER_CONS_D0_OFFSET                      0
+#define NVM_CFG1_PORT_POWER_CONS_D1_MASK                        0x0000FF00
+#define NVM_CFG1_PORT_POWER_CONS_D1_OFFSET                      8
+#define NVM_CFG1_PORT_POWER_CONS_D2_MASK                        0x00FF0000
+#define NVM_CFG1_PORT_POWER_CONS_D2_OFFSET                      16
+#define NVM_CFG1_PORT_POWER_CONS_D3_MASK                        0xFF000000
+#define NVM_CFG1_PORT_POWER_CONS_D3_OFFSET                      24
+
+       u32 generic_cont0;                                      /* 0x8 */
+#define NVM_CFG1_PORT_LED_MODE_MASK                             0x000000FF
+#define NVM_CFG1_PORT_LED_MODE_OFFSET                           0
+#define NVM_CFG1_PORT_LED_MODE_MAC1                             0x0
+#define NVM_CFG1_PORT_LED_MODE_PHY1                             0x1
+#define NVM_CFG1_PORT_LED_MODE_PHY2                             0x2
+#define NVM_CFG1_PORT_LED_MODE_PHY3                             0x3
+#define NVM_CFG1_PORT_LED_MODE_MAC2                             0x4
+#define NVM_CFG1_PORT_LED_MODE_PHY4                             0x5
+#define NVM_CFG1_PORT_LED_MODE_PHY5                             0x6
+#define NVM_CFG1_PORT_LED_MODE_PHY6                             0x7
+#define NVM_CFG1_PORT_LED_MODE_MAC3                             0x8
+#define NVM_CFG1_PORT_LED_MODE_PHY7                             0x9
+#define NVM_CFG1_PORT_LED_MODE_PHY8                             0xA
+#define NVM_CFG1_PORT_LED_MODE_PHY9                             0xB
+#define NVM_CFG1_PORT_LED_MODE_MAC4                             0xC
+#define NVM_CFG1_PORT_LED_MODE_PHY10                            0xD
+#define NVM_CFG1_PORT_LED_MODE_PHY11                            0xE
+#define NVM_CFG1_PORT_LED_MODE_PHY12                            0xF
+#define NVM_CFG1_PORT_ROCE_PRIORITY_MASK                        0x0000FF00
+#define NVM_CFG1_PORT_ROCE_PRIORITY_OFFSET                      8
+#define NVM_CFG1_PORT_DCBX_MODE_MASK                            0x000F0000
+#define NVM_CFG1_PORT_DCBX_MODE_OFFSET                          16
+#define NVM_CFG1_PORT_DCBX_MODE_DISABLED                        0x0
+#define NVM_CFG1_PORT_DCBX_MODE_IEEE                            0x1
+#define NVM_CFG1_PORT_DCBX_MODE_CEE                             0x2
+#define NVM_CFG1_PORT_DCBX_MODE_DYNAMIC                         0x3
+
+       u32     pcie_cfg;                                       /* 0xC */
+#define NVM_CFG1_PORT_RESERVED15_MASK                           0x00000007
+#define NVM_CFG1_PORT_RESERVED15_OFFSET                         0
+
+       u32     features;                                       /* 0x10 */
+#define NVM_CFG1_PORT_ENABLE_WOL_ON_ACPI_PATTERN_MASK           0x00000001
+#define NVM_CFG1_PORT_ENABLE_WOL_ON_ACPI_PATTERN_OFFSET         0
+#define NVM_CFG1_PORT_ENABLE_WOL_ON_ACPI_PATTERN_DISABLED       0x0
+#define NVM_CFG1_PORT_ENABLE_WOL_ON_ACPI_PATTERN_ENABLED        0x1
+#define NVM_CFG1_PORT_MAGIC_PACKET_WOL_MASK                     0x00000002
+#define NVM_CFG1_PORT_MAGIC_PACKET_WOL_OFFSET                   1
+#define NVM_CFG1_PORT_MAGIC_PACKET_WOL_DISABLED                 0x0
+#define NVM_CFG1_PORT_MAGIC_PACKET_WOL_ENABLED                  0x1
+
+       u32 speed_cap_mask;                                     /* 0x14 */
+#define NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_MASK            0x0000FFFF
+#define NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_OFFSET          0
+#define NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_1G              0x1
+#define NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_10G             0x2
+#define NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_25G             0x8
+#define NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_40G             0x10
+#define NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_50G             0x20
+#define NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_100G            0x40
+#define NVM_CFG1_PORT_MFW_SPEED_CAPABILITY_MASK_MASK            0xFFFF0000
+#define NVM_CFG1_PORT_MFW_SPEED_CAPABILITY_MASK_OFFSET          16
+#define NVM_CFG1_PORT_MFW_SPEED_CAPABILITY_MASK_1G              0x1
+#define NVM_CFG1_PORT_MFW_SPEED_CAPABILITY_MASK_10G             0x2
+#define NVM_CFG1_PORT_MFW_SPEED_CAPABILITY_MASK_25G             0x8
+#define NVM_CFG1_PORT_MFW_SPEED_CAPABILITY_MASK_40G             0x10
+#define NVM_CFG1_PORT_MFW_SPEED_CAPABILITY_MASK_50G             0x20
+#define NVM_CFG1_PORT_MFW_SPEED_CAPABILITY_MASK_100G            0x40
+
+       u32 link_settings;                                      /* 0x18 */
+#define NVM_CFG1_PORT_DRV_LINK_SPEED_MASK                       0x0000000F
+#define NVM_CFG1_PORT_DRV_LINK_SPEED_OFFSET                     0
+#define NVM_CFG1_PORT_DRV_LINK_SPEED_AUTONEG                    0x0
+#define NVM_CFG1_PORT_DRV_LINK_SPEED_1G                         0x1
+#define NVM_CFG1_PORT_DRV_LINK_SPEED_10G                        0x2
+#define NVM_CFG1_PORT_DRV_LINK_SPEED_25G                        0x4
+#define NVM_CFG1_PORT_DRV_LINK_SPEED_40G                        0x5
+#define NVM_CFG1_PORT_DRV_LINK_SPEED_50G                        0x6
+#define NVM_CFG1_PORT_DRV_LINK_SPEED_100G                       0x7
+#define NVM_CFG1_PORT_DRV_FLOW_CONTROL_MASK                     0x00000070
+#define NVM_CFG1_PORT_DRV_FLOW_CONTROL_OFFSET                   4
+#define NVM_CFG1_PORT_DRV_FLOW_CONTROL_AUTONEG                  0x1
+#define NVM_CFG1_PORT_DRV_FLOW_CONTROL_RX                       0x2
+#define NVM_CFG1_PORT_DRV_FLOW_CONTROL_TX                       0x4
+#define NVM_CFG1_PORT_MFW_LINK_SPEED_MASK                       0x00000780
+#define NVM_CFG1_PORT_MFW_LINK_SPEED_OFFSET                     7
+#define NVM_CFG1_PORT_MFW_LINK_SPEED_AUTONEG                    0x0
+#define NVM_CFG1_PORT_MFW_LINK_SPEED_1G                         0x1
+#define NVM_CFG1_PORT_MFW_LINK_SPEED_10G                        0x2
+#define NVM_CFG1_PORT_MFW_LINK_SPEED_25G                        0x4
+#define NVM_CFG1_PORT_MFW_LINK_SPEED_40G                        0x5
+#define NVM_CFG1_PORT_MFW_LINK_SPEED_50G                        0x6
+#define NVM_CFG1_PORT_MFW_LINK_SPEED_100G                       0x7
+#define NVM_CFG1_PORT_MFW_FLOW_CONTROL_MASK                     0x00003800
+#define NVM_CFG1_PORT_MFW_FLOW_CONTROL_OFFSET                   11
+#define NVM_CFG1_PORT_MFW_FLOW_CONTROL_AUTONEG                  0x1
+#define NVM_CFG1_PORT_MFW_FLOW_CONTROL_RX                       0x2
+#define NVM_CFG1_PORT_MFW_FLOW_CONTROL_TX                       0x4
+#define NVM_CFG1_PORT_OPTIC_MODULE_VENDOR_ENFORCEMENT_MASK      0x00004000
+#define NVM_CFG1_PORT_OPTIC_MODULE_VENDOR_ENFORCEMENT_OFFSET    14
+#define NVM_CFG1_PORT_OPTIC_MODULE_VENDOR_ENFORCEMENT_DISABLED  0x0
+#define NVM_CFG1_PORT_OPTIC_MODULE_VENDOR_ENFORCEMENT_ENABLED   0x1
+
+       u32 phy_cfg;                                            /* 0x1C */
+#define NVM_CFG1_PORT_OPTIONAL_LINK_MODES_MASK                  0x0000FFFF
+#define NVM_CFG1_PORT_OPTIONAL_LINK_MODES_OFFSET                0
+#define NVM_CFG1_PORT_OPTIONAL_LINK_MODES_HIGIG                 0x1
+#define NVM_CFG1_PORT_OPTIONAL_LINK_MODES_SCRAMBLER             0x2
+#define NVM_CFG1_PORT_OPTIONAL_LINK_MODES_FIBER                 0x4
+#define NVM_CFG1_PORT_OPTIONAL_LINK_MODES_DISABLE_CL72_AN       0x8
+#define NVM_CFG1_PORT_OPTIONAL_LINK_MODES_DISABLE_FEC_AN        0x10
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_MASK                 0x00FF0000
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_OFFSET               16
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_BYPASS               0x0
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_KR                   0x2
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_KR2                  0x3
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_KR4                  0x4
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_XFI                  0x8
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_SFI                  0x9
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_1000X                0xB
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_SGMII                0xC
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_XLAUI                0xD
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_CAUI                 0xE
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_XLPPI                0xF
+#define NVM_CFG1_PORT_SERDES_NET_INTERFACE_CPPI                 0x10
+#define NVM_CFG1_PORT_AN_MODE_MASK                              0xFF000000
+#define NVM_CFG1_PORT_AN_MODE_OFFSET                            24
+#define NVM_CFG1_PORT_AN_MODE_NONE                              0x0
+#define NVM_CFG1_PORT_AN_MODE_CL73                              0x1
+#define NVM_CFG1_PORT_AN_MODE_CL37                              0x2
+#define NVM_CFG1_PORT_AN_MODE_CL73_BAM                          0x3
+#define NVM_CFG1_PORT_AN_MODE_CL37_BAM                          0x4
+#define NVM_CFG1_PORT_AN_MODE_HPAM                              0x5
+#define NVM_CFG1_PORT_AN_MODE_SGMII                             0x6
+
+       u32 mgmt_traffic;                                       /* 0x20 */
+#define NVM_CFG1_PORT_RESERVED61_MASK                           0x0000000F
+#define NVM_CFG1_PORT_RESERVED61_OFFSET                         0
+#define NVM_CFG1_PORT_RESERVED61_DISABLED                       0x0
+#define NVM_CFG1_PORT_RESERVED61_NCSI_OVER_RMII                 0x1
+#define NVM_CFG1_PORT_RESERVED61_NCSI_OVER_SMBUS                0x2
+
+       u32 ext_phy;                                            /* 0x24 */
+#define NVM_CFG1_PORT_EXTERNAL_PHY_TYPE_MASK                    0x000000FF
+#define NVM_CFG1_PORT_EXTERNAL_PHY_TYPE_OFFSET                  0
+#define NVM_CFG1_PORT_EXTERNAL_PHY_TYPE_NONE                    0x0
+#define NVM_CFG1_PORT_EXTERNAL_PHY_TYPE_BCM84844                0x1
+#define NVM_CFG1_PORT_EXTERNAL_PHY_ADDRESS_MASK                 0x0000FF00
+#define NVM_CFG1_PORT_EXTERNAL_PHY_ADDRESS_OFFSET               8
+
+       u32 mba_cfg1;                                           /* 0x28 */
+#define NVM_CFG1_PORT_MBA_MASK                                  0x00000001
+#define NVM_CFG1_PORT_MBA_OFFSET                                0
+#define NVM_CFG1_PORT_MBA_DISABLED                              0x0
+#define NVM_CFG1_PORT_MBA_ENABLED                               0x1
+#define NVM_CFG1_PORT_MBA_BOOT_TYPE_MASK                        0x00000006
+#define NVM_CFG1_PORT_MBA_BOOT_TYPE_OFFSET                      1
+#define NVM_CFG1_PORT_MBA_BOOT_TYPE_AUTO                        0x0
+#define NVM_CFG1_PORT_MBA_BOOT_TYPE_BBS                         0x1
+#define NVM_CFG1_PORT_MBA_BOOT_TYPE_INT18H                      0x2
+#define NVM_CFG1_PORT_MBA_BOOT_TYPE_INT19H                      0x3
+#define NVM_CFG1_PORT_MBA_DELAY_TIME_MASK                       0x00000078
+#define NVM_CFG1_PORT_MBA_DELAY_TIME_OFFSET                     3
+#define NVM_CFG1_PORT_MBA_SETUP_HOT_KEY_MASK                    0x00000080
+#define NVM_CFG1_PORT_MBA_SETUP_HOT_KEY_OFFSET                  7
+#define NVM_CFG1_PORT_MBA_SETUP_HOT_KEY_CTRL_S                  0x0
+#define NVM_CFG1_PORT_MBA_SETUP_HOT_KEY_CTRL_B                  0x1
+#define NVM_CFG1_PORT_MBA_HIDE_SETUP_PROMPT_MASK                0x00000100
+#define NVM_CFG1_PORT_MBA_HIDE_SETUP_PROMPT_OFFSET              8
+#define NVM_CFG1_PORT_MBA_HIDE_SETUP_PROMPT_DISABLED            0x0
+#define NVM_CFG1_PORT_MBA_HIDE_SETUP_PROMPT_ENABLED             0x1
+#define NVM_CFG1_PORT_RESERVED5_MASK                            0x0001FE00
+#define NVM_CFG1_PORT_RESERVED5_OFFSET                          9
+#define NVM_CFG1_PORT_RESERVED5_DISABLED                        0x0
+#define NVM_CFG1_PORT_RESERVED5_2K                              0x1
+#define NVM_CFG1_PORT_RESERVED5_4K                              0x2
+#define NVM_CFG1_PORT_RESERVED5_8K                              0x3
+#define NVM_CFG1_PORT_RESERVED5_16K                             0x4
+#define NVM_CFG1_PORT_RESERVED5_32K                             0x5
+#define NVM_CFG1_PORT_RESERVED5_64K                             0x6
+#define NVM_CFG1_PORT_RESERVED5_128K                            0x7
+#define NVM_CFG1_PORT_RESERVED5_256K                            0x8
+#define NVM_CFG1_PORT_RESERVED5_512K                            0x9
+#define NVM_CFG1_PORT_RESERVED5_1M                              0xA
+#define NVM_CFG1_PORT_RESERVED5_2M                              0xB
+#define NVM_CFG1_PORT_RESERVED5_4M                              0xC
+#define NVM_CFG1_PORT_RESERVED5_8M                              0xD
+#define NVM_CFG1_PORT_RESERVED5_16M                             0xE
+#define NVM_CFG1_PORT_RESERVED5_32M                             0xF
+#define NVM_CFG1_PORT_MBA_LINK_SPEED_MASK                       0x001E0000
+#define NVM_CFG1_PORT_MBA_LINK_SPEED_OFFSET                     17
+#define NVM_CFG1_PORT_MBA_LINK_SPEED_AUTONEG                    0x0
+#define NVM_CFG1_PORT_MBA_LINK_SPEED_1G                         0x1
+#define NVM_CFG1_PORT_MBA_LINK_SPEED_10G                        0x2
+#define NVM_CFG1_PORT_MBA_LINK_SPEED_25G                        0x4
+#define NVM_CFG1_PORT_MBA_LINK_SPEED_40G                        0x5
+#define NVM_CFG1_PORT_MBA_LINK_SPEED_50G                        0x6
+#define NVM_CFG1_PORT_MBA_LINK_SPEED_100G                       0x7
+#define NVM_CFG1_PORT_MBA_BOOT_RETRY_COUNT_MASK                 0x00E00000
+#define NVM_CFG1_PORT_MBA_BOOT_RETRY_COUNT_OFFSET               21
+
+       u32     mba_cfg2;                                       /* 0x2C */
+#define NVM_CFG1_PORT_MBA_VLAN_VALUE_MASK                       0x0000FFFF
+#define NVM_CFG1_PORT_MBA_VLAN_VALUE_OFFSET                     0
+#define NVM_CFG1_PORT_MBA_VLAN_MASK                             0x00010000
+#define NVM_CFG1_PORT_MBA_VLAN_OFFSET                           16
+
+       u32     vf_cfg;                                         /* 0x30 */
+#define NVM_CFG1_PORT_RESERVED8_MASK                            0x0000FFFF
+#define NVM_CFG1_PORT_RESERVED8_OFFSET                          0
+#define NVM_CFG1_PORT_RESERVED6_MASK                            0x000F0000
+#define NVM_CFG1_PORT_RESERVED6_OFFSET                          16
+#define NVM_CFG1_PORT_RESERVED6_DISABLED                        0x0
+#define NVM_CFG1_PORT_RESERVED6_4K                              0x1
+#define NVM_CFG1_PORT_RESERVED6_8K                              0x2
+#define NVM_CFG1_PORT_RESERVED6_16K                             0x3
+#define NVM_CFG1_PORT_RESERVED6_32K                             0x4
+#define NVM_CFG1_PORT_RESERVED6_64K                             0x5
+#define NVM_CFG1_PORT_RESERVED6_128K                            0x6
+#define NVM_CFG1_PORT_RESERVED6_256K                            0x7
+#define NVM_CFG1_PORT_RESERVED6_512K                            0x8
+#define NVM_CFG1_PORT_RESERVED6_1M                              0x9
+#define NVM_CFG1_PORT_RESERVED6_2M                              0xA
+#define NVM_CFG1_PORT_RESERVED6_4M                              0xB
+#define NVM_CFG1_PORT_RESERVED6_8M                              0xC
+#define NVM_CFG1_PORT_RESERVED6_16M                             0xD
+#define NVM_CFG1_PORT_RESERVED6_32M                             0xE
+#define NVM_CFG1_PORT_RESERVED6_64M                             0xF
+
+       struct nvm_cfg_mac_address      lldp_mac_address;       /* 0x34 */
+
+       u32                             led_port_settings;      /* 0x3C */
+#define NVM_CFG1_PORT_LANE_LED_SPD_0_SEL_MASK                   0x000000FF
+#define NVM_CFG1_PORT_LANE_LED_SPD_0_SEL_OFFSET                 0
+#define NVM_CFG1_PORT_LANE_LED_SPD_1_SEL_MASK                   0x0000FF00
+#define NVM_CFG1_PORT_LANE_LED_SPD_1_SEL_OFFSET                 8
+#define NVM_CFG1_PORT_LANE_LED_SPD_2_SEL_MASK                   0x00FF0000
+#define NVM_CFG1_PORT_LANE_LED_SPD_2_SEL_OFFSET                 16
+#define NVM_CFG1_PORT_LANE_LED_SPD__SEL_1G                      0x1
+#define NVM_CFG1_PORT_LANE_LED_SPD__SEL_10G                     0x2
+#define NVM_CFG1_PORT_LANE_LED_SPD__SEL_25G                     0x8
+#define NVM_CFG1_PORT_LANE_LED_SPD__SEL_40G                     0x10
+#define NVM_CFG1_PORT_LANE_LED_SPD__SEL_50G                     0x20
+#define NVM_CFG1_PORT_LANE_LED_SPD__SEL_100G                    0x40
+
+       u32 transceiver_00;                                     /* 0x40 */
+
+       /*  Define for mapping of transceiver signal module absent */
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_MASK                     0x000000FF
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_OFFSET                   0
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_NA                       0x0
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO0                    0x1
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO1                    0x2
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO2                    0x3
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO3                    0x4
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO4                    0x5
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO5                    0x6
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO6                    0x7
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO7                    0x8
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO8                    0x9
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO9                    0xA
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO10                   0xB
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO11                   0xC
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO12                   0xD
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO13                   0xE
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO14                   0xF
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO15                   0x10
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO16                   0x11
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO17                   0x12
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO18                   0x13
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO19                   0x14
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO20                   0x15
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO21                   0x16
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO22                   0x17
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO23                   0x18
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO24                   0x19
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO25                   0x1A
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO26                   0x1B
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO27                   0x1C
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO28                   0x1D
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO29                   0x1E
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO30                   0x1F
+#define NVM_CFG1_PORT_TRANS_MODULE_ABS_GPIO31                   0x20
+       /*  Define the GPIO mux settings  to switch i2c mux to this port */
+#define NVM_CFG1_PORT_I2C_MUX_SEL_VALUE_0_MASK                  0x00000F00
+#define NVM_CFG1_PORT_I2C_MUX_SEL_VALUE_0_OFFSET                8
+#define NVM_CFG1_PORT_I2C_MUX_SEL_VALUE_1_MASK                  0x0000F000
+#define NVM_CFG1_PORT_I2C_MUX_SEL_VALUE_1_OFFSET                12
+
+       u32 reserved[133];                                      /* 0x44 */
+};
+
+struct nvm_cfg1_func {
+       struct nvm_cfg_mac_address      mac_address;            /* 0x0 */
+
+       u32                             rsrv1;                  /* 0x8 */
+#define NVM_CFG1_FUNC_RESERVED1_MASK                            0x0000FFFF
+#define NVM_CFG1_FUNC_RESERVED1_OFFSET                          0
+#define NVM_CFG1_FUNC_RESERVED2_MASK                            0xFFFF0000
+#define NVM_CFG1_FUNC_RESERVED2_OFFSET                          16
+
+       u32                             rsrv2;                  /* 0xC */
+#define NVM_CFG1_FUNC_RESERVED3_MASK                            0x0000FFFF
+#define NVM_CFG1_FUNC_RESERVED3_OFFSET                          0
+#define NVM_CFG1_FUNC_RESERVED4_MASK                            0xFFFF0000
+#define NVM_CFG1_FUNC_RESERVED4_OFFSET                          16
+
+       u32                             device_id;              /* 0x10 */
+#define NVM_CFG1_FUNC_MF_VENDOR_DEVICE_ID_MASK                  0x0000FFFF
+#define NVM_CFG1_FUNC_MF_VENDOR_DEVICE_ID_OFFSET                0
+#define NVM_CFG1_FUNC_VENDOR_DEVICE_ID_MASK                     0xFFFF0000
+#define NVM_CFG1_FUNC_VENDOR_DEVICE_ID_OFFSET                   16
+
+       u32                             cmn_cfg;                /* 0x14 */
+#define NVM_CFG1_FUNC_MBA_BOOT_PROTOCOL_MASK                    0x00000007
+#define NVM_CFG1_FUNC_MBA_BOOT_PROTOCOL_OFFSET                  0
+#define NVM_CFG1_FUNC_MBA_BOOT_PROTOCOL_PXE                     0x0
+#define NVM_CFG1_FUNC_MBA_BOOT_PROTOCOL_RPL                     0x1
+#define NVM_CFG1_FUNC_MBA_BOOT_PROTOCOL_BOOTP                   0x2
+#define NVM_CFG1_FUNC_MBA_BOOT_PROTOCOL_ISCSI_BOOT              0x3
+#define NVM_CFG1_FUNC_MBA_BOOT_PROTOCOL_FCOE_BOOT               0x4
+#define NVM_CFG1_FUNC_MBA_BOOT_PROTOCOL_NONE                    0x7
+#define NVM_CFG1_FUNC_VF_PCI_DEVICE_ID_MASK                     0x0007FFF8
+#define NVM_CFG1_FUNC_VF_PCI_DEVICE_ID_OFFSET                   3
+#define NVM_CFG1_FUNC_PERSONALITY_MASK                          0x00780000
+#define NVM_CFG1_FUNC_PERSONALITY_OFFSET                        19
+#define NVM_CFG1_FUNC_PERSONALITY_ETHERNET                      0x0
+#define NVM_CFG1_FUNC_PERSONALITY_ISCSI                         0x1
+#define NVM_CFG1_FUNC_PERSONALITY_FCOE                          0x2
+#define NVM_CFG1_FUNC_PERSONALITY_ROCE                          0x3
+#define NVM_CFG1_FUNC_BANDWIDTH_WEIGHT_MASK                     0x7F800000
+#define NVM_CFG1_FUNC_BANDWIDTH_WEIGHT_OFFSET                   23
+#define NVM_CFG1_FUNC_PAUSE_ON_HOST_RING_MASK                   0x80000000
+#define NVM_CFG1_FUNC_PAUSE_ON_HOST_RING_OFFSET                 31
+#define NVM_CFG1_FUNC_PAUSE_ON_HOST_RING_DISABLED               0x0
+#define NVM_CFG1_FUNC_PAUSE_ON_HOST_RING_ENABLED                0x1
+
+       u32 pci_cfg;                                            /* 0x18 */
+#define NVM_CFG1_FUNC_NUMBER_OF_VFS_PER_PF_MASK                 0x0000007F
+#define NVM_CFG1_FUNC_NUMBER_OF_VFS_PER_PF_OFFSET               0
+#define NVM_CFG1_FUNC_RESERVESD12_MASK                          0x00003F80
+#define NVM_CFG1_FUNC_RESERVESD12_OFFSET                        7
+#define NVM_CFG1_FUNC_BAR1_SIZE_MASK                            0x0003C000
+#define NVM_CFG1_FUNC_BAR1_SIZE_OFFSET                          14
+#define NVM_CFG1_FUNC_BAR1_SIZE_DISABLED                        0x0
+#define NVM_CFG1_FUNC_BAR1_SIZE_64K                             0x1
+#define NVM_CFG1_FUNC_BAR1_SIZE_128K                            0x2
+#define NVM_CFG1_FUNC_BAR1_SIZE_256K                            0x3
+#define NVM_CFG1_FUNC_BAR1_SIZE_512K                            0x4
+#define NVM_CFG1_FUNC_BAR1_SIZE_1M                              0x5
+#define NVM_CFG1_FUNC_BAR1_SIZE_2M                              0x6
+#define NVM_CFG1_FUNC_BAR1_SIZE_4M                              0x7
+#define NVM_CFG1_FUNC_BAR1_SIZE_8M                              0x8
+#define NVM_CFG1_FUNC_BAR1_SIZE_16M                             0x9
+#define NVM_CFG1_FUNC_BAR1_SIZE_32M                             0xA
+#define NVM_CFG1_FUNC_BAR1_SIZE_64M                             0xB
+#define NVM_CFG1_FUNC_BAR1_SIZE_128M                            0xC
+#define NVM_CFG1_FUNC_BAR1_SIZE_256M                            0xD
+#define NVM_CFG1_FUNC_BAR1_SIZE_512M                            0xE
+#define NVM_CFG1_FUNC_BAR1_SIZE_1G                              0xF
+#define NVM_CFG1_FUNC_MAX_BANDWIDTH_MASK                        0x03FC0000
+#define NVM_CFG1_FUNC_MAX_BANDWIDTH_OFFSET                      18
+
+       struct nvm_cfg_mac_address      fcoe_node_wwn_mac_addr; /* 0x1C */
+
+       struct nvm_cfg_mac_address      fcoe_port_wwn_mac_addr; /* 0x24 */
+
+       u32                             reserved[9];            /* 0x2C */
+};
+
+struct nvm_cfg1 {
+       struct nvm_cfg1_glob    glob;                           /* 0x0 */
+
+       struct nvm_cfg1_path    path[MCP_GLOB_PATH_MAX];        /* 0x140 */
+
+       struct nvm_cfg1_port    port[MCP_GLOB_PORT_MAX];        /* 0x230 */
+
+       struct nvm_cfg1_func    func[MCP_GLOB_FUNC_MAX];        /* 0xB90 */
+};
+
+/******************************************
+* nvm_cfg structs
+******************************************/
+
+enum nvm_cfg_sections {
+       NVM_CFG_SECTION_NVM_CFG1,
+       NVM_CFG_SECTION_MAX
+};
+
+struct nvm_cfg {
+       u32             num_sections;
+       u32             sections_offset[NVM_CFG_SECTION_MAX];
+       struct nvm_cfg1 cfg1;
+};
+
+#define PORT_0          0
+#define PORT_1          1
+#define PORT_2          2
+#define PORT_3          3
+
+extern struct spad_layout g_spad;
+
+#define MCP_SPAD_SIZE                       0x00028000  /* 160 KB */
+
+#define SPAD_OFFSET(addr) (((u32)addr - (u32)CPU_SPAD_BASE))
+
+#define TO_OFFSIZE(_offset, _size)                             \
+       (u32)((((u32)(_offset) >> 2) << OFFSIZE_OFFSET_SHIFT) | \
+             (((u32)(_size) >> 2) << OFFSIZE_SIZE_SHIFT))
+
+enum spad_sections {
+       SPAD_SECTION_TRACE,
+       SPAD_SECTION_NVM_CFG,
+       SPAD_SECTION_PUBLIC,
+       SPAD_SECTION_PRIVATE,
+       SPAD_SECTION_MAX
+};
+
+struct spad_layout {
+       struct nvm_cfg          nvm_cfg;
+       struct mcp_public_data  public_data;
+};
+
+#define CRC_MAGIC_VALUE                     0xDEBB20E3
+#define CRC32_POLYNOMIAL                    0xEDB88320
+#define NVM_CRC_SIZE                            (sizeof(u32))
+
+enum nvm_sw_arbitrator {
+       NVM_SW_ARB_HOST,
+       NVM_SW_ARB_MCP,
+       NVM_SW_ARB_UART,
+       NVM_SW_ARB_RESERVED
+};
+
+/****************************************************************************
+* Boot Strap Region                                                        *
+****************************************************************************/
+struct legacy_bootstrap_region {
+       u32     magic_value;
+#define NVM_MAGIC_VALUE          0x669955aa
+       u32     sram_start_addr;
+       u32     code_len;               /* boot code length (in dwords) */
+       u32     code_start_addr;
+       u32     crc;                    /* 32-bit CRC */
+};
+
+/****************************************************************************
+* Directories Region                                                       *
+****************************************************************************/
+struct nvm_code_entry {
+       u32     image_type;             /* Image type */
+       u32     nvm_start_addr;         /* NVM address of the image */
+       u32     len;                    /* Include CRC */
+       u32     sram_start_addr;
+       u32     sram_run_addr;          /* Relevant in case of MIM only */
+};
+
+enum nvm_image_type {
+       NVM_TYPE_TIM1           = 0x01,
+       NVM_TYPE_TIM2           = 0x02,
+       NVM_TYPE_MIM1           = 0x03,
+       NVM_TYPE_MIM2           = 0x04,
+       NVM_TYPE_MBA            = 0x05,
+       NVM_TYPE_MODULES_PN     = 0x06,
+       NVM_TYPE_VPD            = 0x07,
+       NVM_TYPE_MFW_TRACE1     = 0x08,
+       NVM_TYPE_MFW_TRACE2     = 0x09,
+       NVM_TYPE_NVM_CFG1       = 0x0a,
+       NVM_TYPE_L2B            = 0x0b,
+       NVM_TYPE_DIR1           = 0x0c,
+       NVM_TYPE_EAGLE_FW1      = 0x0d,
+       NVM_TYPE_FALCON_FW1     = 0x0e,
+       NVM_TYPE_PCIE_FW1       = 0x0f,
+       NVM_TYPE_HW_SET         = 0x10,
+       NVM_TYPE_LIM            = 0x11,
+       NVM_TYPE_AVS_FW1        = 0x12,
+       NVM_TYPE_DIR2           = 0x13,
+       NVM_TYPE_CCM            = 0x14,
+       NVM_TYPE_EAGLE_FW2      = 0x15,
+       NVM_TYPE_FALCON_FW2     = 0x16,
+       NVM_TYPE_PCIE_FW2       = 0x17,
+       NVM_TYPE_AVS_FW2        = 0x18,
+
+       NVM_TYPE_MAX,
+};
+
+#define MAX_NVM_DIR_ENTRIES 200
+
+struct nvm_dir {
+       s32 seq;
+#define NVM_DIR_NEXT_MFW_MASK   0x00000001
+#define NVM_DIR_SEQ_MASK        0xfffffffe
+#define NVM_DIR_NEXT_MFW(seq) ((seq) & NVM_DIR_NEXT_MFW_MASK)
+
+#define IS_DIR_SEQ_VALID(seq) ((seq & NVM_DIR_SEQ_MASK) != NVM_DIR_SEQ_MASK)
+
+       u32                     num_images;
+       u32                     rsrv;
+       struct nvm_code_entry   code[1]; /* Up to MAX_NVM_DIR_ENTRIES */
+};
+
+#define NVM_DIR_SIZE(_num_images) (sizeof(struct nvm_dir) +             \
+                                  (_num_images -                        \
+                                   1) * sizeof(struct nvm_code_entry) + \
+                                  NVM_CRC_SIZE)
+
+struct nvm_vpd_image {
+       u32     format_revision;
+#define VPD_IMAGE_VERSION        1
+
+       /* This array length depends on the number of VPD fields */
+       u8      vpd_data[1];
+};
+
+/****************************************************************************
+* NVRAM FULL MAP                                                           *
+****************************************************************************/
+#define DIR_ID_1    (0)
+#define DIR_ID_2    (1)
+#define MAX_DIR_IDS (2)
+
+#define MFW_BUNDLE_1    (0)
+#define MFW_BUNDLE_2    (1)
+#define MAX_MFW_BUNDLES (2)
+
+#define FLASH_PAGE_SIZE 0x1000
+#define NVM_DIR_MAX_SIZE    (FLASH_PAGE_SIZE)           /* 4Kb */
+#define ASIC_MIM_MAX_SIZE   (300 * FLASH_PAGE_SIZE)     /* 1.2Mb */
+#define FPGA_MIM_MAX_SIZE   (25 * FLASH_PAGE_SIZE)      /* 60Kb */
+
+#define LIM_MAX_SIZE        ((2 *                                    \
+                             FLASH_PAGE_SIZE) -                      \
+                            sizeof(struct legacy_bootstrap_region) - \
+                            NVM_RSV_SIZE)
+#define LIM_OFFSET          (NVM_OFFSET(lim_image))
+#define NVM_RSV_SIZE            (44)
+#define MIM_MAX_SIZE(is_asic) ((is_asic) ? ASIC_MIM_MAX_SIZE : \
+                              FPGA_MIM_MAX_SIZE)
+#define MIM_OFFSET(idx, is_asic) (NVM_OFFSET(dir[MAX_MFW_BUNDLES]) + \
+                                 ((idx ==                           \
+                                   NVM_TYPE_MIM2) ? MIM_MAX_SIZE(is_asic) : 0))
+#define NVM_FIXED_AREA_SIZE(is_asic) (sizeof(struct nvm_image) + \
+                                     MIM_MAX_SIZE(is_asic) * 2)
+
+union nvm_dir_union {
+       struct nvm_dir  dir;
+       u8              page[FLASH_PAGE_SIZE];
+};
+
+/*                        Address
+ *  +-------------------+ 0x000000
+ *  |    Bootstrap:     |
+ *  | magic_number      |
+ *  | sram_start_addr   |
+ *  | code_len          |
+ *  | code_start_addr   |
+ *  | crc               |
+ *  +-------------------+ 0x000014
+ *  | rsrv              |
+ *  +-------------------+ 0x000040
+ *  | LIM               |
+ *  +-------------------+ 0x002000
+ *  | Dir1              |
+ *  +-------------------+ 0x003000
+ *  | Dir2              |
+ *  +-------------------+ 0x004000
+ *  | MIM1              |
+ *  +-------------------+ 0x130000
+ *  | MIM2              |
+ *  +-------------------+ 0x25C000
+ *  | Rest Images:      |
+ *  | TIM1/2            |
+ *  | MFW_TRACE1/2      |
+ *  | Eagle/Falcon FW   |
+ *  | PCIE/AVS FW       |
+ *  | MBA/CCM/L2B       |
+ *  | VPD               |
+ *  | optic_modules     |
+ *  |  ...              |
+ *  +-------------------+ 0x400000
+ */
+struct nvm_image {
+/*********** !!!  FIXED SECTIONS  !!! DO NOT MODIFY !!! **********************/
+       /* NVM Offset  (size) */
+       struct legacy_bootstrap_region  bootstrap;
+       u8                              rsrv[NVM_RSV_SIZE];
+       u8                              lim_image[LIM_MAX_SIZE];
+       union nvm_dir_union             dir[MAX_MFW_BUNDLES];
+
+       /* MIM1_IMAGE                              0x004000 (0x12c000) */
+       /* MIM2_IMAGE                              0x130000 (0x12c000) */
+/*********** !!!  FIXED SECTIONS  !!! DO NOT MODIFY !!! **********************/
+};                              /* 0x134 */
+
+#define NVM_OFFSET(f)  ((u32_t)((int_ptr_t)(&(((struct nvm_image *)0)->f))))
+
+struct hw_set_info {
+       u32     reg_type;
+#define GRC_REG_TYPE 1
+#define PHY_REG_TYPE 2
+#define PCI_REG_TYPE 4
+
+       u32     bank_num;
+       u32     pf_num;
+       u32     operation;
+#define READ_OP     1
+#define WRITE_OP    2
+#define RMW_SET_OP  3
+#define RMW_CLR_OP  4
+
+       u32     reg_addr;
+       u32     reg_data;
+
+       u32     reset_type;
+#define POR_RESET_TYPE BIT(0)
+#define HARD_RESET_TYPE        BIT(1)
+#define CORE_RESET_TYPE        BIT(2)
+#define MCP_RESET_TYPE BIT(3)
+#define PERSET_ASSERT  BIT(4)
+#define PERSET_DEASSERT        BIT(5)
+};
+
+struct hw_set_image {
+       u32                     format_version;
+#define HW_SET_IMAGE_VERSION        1
+       u32                     no_hw_sets;
+
+       /* This array length depends on the no_hw_sets */
+       struct hw_set_info      hw_sets[1];
+};
+
+#endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hw.c b/drivers/net/ethernet/qlogic/qed/qed_hw.c
new file mode 100644 (file)
index 0000000..ffa9927
--- /dev/null
@@ -0,0 +1,776 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/qed/qed_chain.h>
+#include "qed.h"
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_reg_addr.h"
+
+#define QED_BAR_ACQUIRE_TIMEOUT 1000
+
+/* Invalid values */
+#define QED_BAR_INVALID_OFFSET          (cpu_to_le32(-1))
+
+struct qed_ptt {
+       struct list_head        list_entry;
+       unsigned int            idx;
+       struct pxp_ptt_entry    pxp;
+};
+
+struct qed_ptt_pool {
+       struct list_head        free_list;
+       spinlock_t              lock; /* ptt synchronized access */
+       struct qed_ptt          ptts[PXP_EXTERNAL_BAR_PF_WINDOW_NUM];
+};
+
+int qed_ptt_pool_alloc(struct qed_hwfn *p_hwfn)
+{
+       struct qed_ptt_pool *p_pool = kmalloc(sizeof(*p_pool),
+                                             GFP_ATOMIC);
+       int i;
+
+       if (!p_pool)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&p_pool->free_list);
+       for (i = 0; i < PXP_EXTERNAL_BAR_PF_WINDOW_NUM; i++) {
+               p_pool->ptts[i].idx = i;
+               p_pool->ptts[i].pxp.offset = QED_BAR_INVALID_OFFSET;
+               p_pool->ptts[i].pxp.pretend.control = 0;
+               if (i >= RESERVED_PTT_MAX)
+                       list_add(&p_pool->ptts[i].list_entry,
+                                &p_pool->free_list);
+       }
+
+       p_hwfn->p_ptt_pool = p_pool;
+       spin_lock_init(&p_pool->lock);
+
+       return 0;
+}
+
+void qed_ptt_invalidate(struct qed_hwfn *p_hwfn)
+{
+       struct qed_ptt *p_ptt;
+       int i;
+
+       for (i = 0; i < PXP_EXTERNAL_BAR_PF_WINDOW_NUM; i++) {
+               p_ptt = &p_hwfn->p_ptt_pool->ptts[i];
+               p_ptt->pxp.offset = QED_BAR_INVALID_OFFSET;
+       }
+}
+
+void qed_ptt_pool_free(struct qed_hwfn *p_hwfn)
+{
+       kfree(p_hwfn->p_ptt_pool);
+       p_hwfn->p_ptt_pool = NULL;
+}
+
+struct qed_ptt *qed_ptt_acquire(struct qed_hwfn *p_hwfn)
+{
+       struct qed_ptt *p_ptt;
+       unsigned int i;
+
+       /* Take the free PTT from the list */
+       for (i = 0; i < QED_BAR_ACQUIRE_TIMEOUT; i++) {
+               spin_lock_bh(&p_hwfn->p_ptt_pool->lock);
+
+               if (!list_empty(&p_hwfn->p_ptt_pool->free_list)) {
+                       p_ptt = list_first_entry(&p_hwfn->p_ptt_pool->free_list,
+                                                struct qed_ptt, list_entry);
+                       list_del(&p_ptt->list_entry);
+
+                       spin_unlock_bh(&p_hwfn->p_ptt_pool->lock);
+
+                       DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
+                                  "allocated ptt %d\n", p_ptt->idx);
+                       return p_ptt;
+               }
+
+               spin_unlock_bh(&p_hwfn->p_ptt_pool->lock);
+               usleep_range(1000, 2000);
+       }
+
+       DP_NOTICE(p_hwfn, "PTT acquire timeout - failed to allocate PTT\n");
+       return NULL;
+}
+
+void qed_ptt_release(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt)
+{
+       spin_lock_bh(&p_hwfn->p_ptt_pool->lock);
+       list_add(&p_ptt->list_entry, &p_hwfn->p_ptt_pool->free_list);
+       spin_unlock_bh(&p_hwfn->p_ptt_pool->lock);
+}
+
+u32 qed_ptt_get_hw_addr(struct qed_hwfn *p_hwfn,
+                       struct qed_ptt *p_ptt)
+{
+       /* The HW is using DWORDS and we need to translate it to Bytes */
+       return le32_to_cpu(p_ptt->pxp.offset) << 2;
+}
+
+static u32 qed_ptt_config_addr(struct qed_ptt *p_ptt)
+{
+       return PXP_PF_WINDOW_ADMIN_PER_PF_START +
+              p_ptt->idx * sizeof(struct pxp_ptt_entry);
+}
+
+u32 qed_ptt_get_bar_addr(struct qed_ptt *p_ptt)
+{
+       return PXP_EXTERNAL_BAR_PF_WINDOW_START +
+              p_ptt->idx * PXP_EXTERNAL_BAR_PF_WINDOW_SINGLE_SIZE;
+}
+
+void qed_ptt_set_win(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt,
+                    u32 new_hw_addr)
+{
+       u32 prev_hw_addr;
+
+       prev_hw_addr = qed_ptt_get_hw_addr(p_hwfn, p_ptt);
+
+       if (new_hw_addr == prev_hw_addr)
+               return;
+
+       /* Update PTT entery in admin window */
+       DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
+                  "Updating PTT entry %d to offset 0x%x\n",
+                  p_ptt->idx, new_hw_addr);
+
+       /* The HW is using DWORDS and the address is in Bytes */
+       p_ptt->pxp.offset = cpu_to_le32(new_hw_addr >> 2);
+
+       REG_WR(p_hwfn,
+              qed_ptt_config_addr(p_ptt) +
+              offsetof(struct pxp_ptt_entry, offset),
+              le32_to_cpu(p_ptt->pxp.offset));
+}
+
+static u32 qed_set_ptt(struct qed_hwfn *p_hwfn,
+                      struct qed_ptt *p_ptt,
+                      u32 hw_addr)
+{
+       u32 win_hw_addr = qed_ptt_get_hw_addr(p_hwfn, p_ptt);
+       u32 offset;
+
+       offset = hw_addr - win_hw_addr;
+
+       /* Verify the address is within the window */
+       if (hw_addr < win_hw_addr ||
+           offset >= PXP_EXTERNAL_BAR_PF_WINDOW_SINGLE_SIZE) {
+               qed_ptt_set_win(p_hwfn, p_ptt, hw_addr);
+               offset = 0;
+       }
+
+       return qed_ptt_get_bar_addr(p_ptt) + offset;
+}
+
+struct qed_ptt *qed_get_reserved_ptt(struct qed_hwfn *p_hwfn,
+                                    enum reserved_ptts ptt_idx)
+{
+       if (ptt_idx >= RESERVED_PTT_MAX) {
+               DP_NOTICE(p_hwfn,
+                         "Requested PTT %d is out of range\n", ptt_idx);
+               return NULL;
+       }
+
+       return &p_hwfn->p_ptt_pool->ptts[ptt_idx];
+}
+
+void qed_wr(struct qed_hwfn *p_hwfn,
+           struct qed_ptt *p_ptt,
+           u32 hw_addr, u32 val)
+{
+       u32 bar_addr = qed_set_ptt(p_hwfn, p_ptt, hw_addr);
+
+       REG_WR(p_hwfn, bar_addr, val);
+       DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
+                  "bar_addr 0x%x, hw_addr 0x%x, val 0x%x\n",
+                  bar_addr, hw_addr, val);
+}
+
+u32 qed_rd(struct qed_hwfn *p_hwfn,
+          struct qed_ptt *p_ptt,
+          u32 hw_addr)
+{
+       u32 bar_addr = qed_set_ptt(p_hwfn, p_ptt, hw_addr);
+       u32 val = REG_RD(p_hwfn, bar_addr);
+
+       DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
+                  "bar_addr 0x%x, hw_addr 0x%x, val 0x%x\n",
+                  bar_addr, hw_addr, val);
+
+       return val;
+}
+
+static void qed_memcpy_hw(struct qed_hwfn *p_hwfn,
+                         struct qed_ptt *p_ptt,
+                         void *addr,
+                         u32 hw_addr,
+                         size_t n,
+                         bool to_device)
+{
+       u32 dw_count, *host_addr, hw_offset;
+       size_t quota, done = 0;
+       u32 __iomem *reg_addr;
+
+       while (done < n) {
+               quota = min_t(size_t, n - done,
+                             PXP_EXTERNAL_BAR_PF_WINDOW_SINGLE_SIZE);
+
+               qed_ptt_set_win(p_hwfn, p_ptt, hw_addr + done);
+               hw_offset = qed_ptt_get_bar_addr(p_ptt);
+
+               dw_count = quota / 4;
+               host_addr = (u32 *)((u8 *)addr + done);
+               reg_addr = (u32 __iomem *)REG_ADDR(p_hwfn, hw_offset);
+               if (to_device)
+                       while (dw_count--)
+                               DIRECT_REG_WR(reg_addr++, *host_addr++);
+               else
+                       while (dw_count--)
+                               *host_addr++ = DIRECT_REG_RD(reg_addr++);
+
+               done += quota;
+       }
+}
+
+void qed_memcpy_from(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt,
+                    void *dest, u32 hw_addr, size_t n)
+{
+       DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
+                  "hw_addr 0x%x, dest %p hw_addr 0x%x, size %lu\n",
+                  hw_addr, dest, hw_addr, (unsigned long)n);
+
+       qed_memcpy_hw(p_hwfn, p_ptt, dest, hw_addr, n, false);
+}
+
+void qed_memcpy_to(struct qed_hwfn *p_hwfn,
+                  struct qed_ptt *p_ptt,
+                  u32 hw_addr, void *src, size_t n)
+{
+       DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
+                  "hw_addr 0x%x, hw_addr 0x%x, src %p size %lu\n",
+                  hw_addr, hw_addr, src, (unsigned long)n);
+
+       qed_memcpy_hw(p_hwfn, p_ptt, src, hw_addr, n, true);
+}
+
+void qed_fid_pretend(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt,
+                    u16 fid)
+{
+       u16 control = 0;
+
+       SET_FIELD(control, PXP_PRETEND_CMD_IS_CONCRETE, 1);
+       SET_FIELD(control, PXP_PRETEND_CMD_PRETEND_FUNCTION, 1);
+
+       /* Every pretend undos previous pretends, including
+        * previous port pretend.
+        */
+       SET_FIELD(control, PXP_PRETEND_CMD_PORT, 0);
+       SET_FIELD(control, PXP_PRETEND_CMD_USE_PORT, 0);
+       SET_FIELD(control, PXP_PRETEND_CMD_PRETEND_PORT, 1);
+
+       if (!GET_FIELD(fid, PXP_CONCRETE_FID_VFVALID))
+               fid = GET_FIELD(fid, PXP_CONCRETE_FID_PFID);
+
+       p_ptt->pxp.pretend.control = cpu_to_le16(control);
+       p_ptt->pxp.pretend.fid.concrete_fid.fid = cpu_to_le16(fid);
+
+       REG_WR(p_hwfn,
+              qed_ptt_config_addr(p_ptt) +
+              offsetof(struct pxp_ptt_entry, pretend),
+              *(u32 *)&p_ptt->pxp.pretend);
+}
+
+void qed_port_pretend(struct qed_hwfn *p_hwfn,
+                     struct qed_ptt *p_ptt,
+                     u8 port_id)
+{
+       u16 control = 0;
+
+       SET_FIELD(control, PXP_PRETEND_CMD_PORT, port_id);
+       SET_FIELD(control, PXP_PRETEND_CMD_USE_PORT, 1);
+       SET_FIELD(control, PXP_PRETEND_CMD_PRETEND_PORT, 1);
+
+       p_ptt->pxp.pretend.control = cpu_to_le16(control);
+
+       REG_WR(p_hwfn,
+              qed_ptt_config_addr(p_ptt) +
+              offsetof(struct pxp_ptt_entry, pretend),
+              *(u32 *)&p_ptt->pxp.pretend);
+}
+
+void qed_port_unpretend(struct qed_hwfn *p_hwfn,
+                       struct qed_ptt *p_ptt)
+{
+       u16 control = 0;
+
+       SET_FIELD(control, PXP_PRETEND_CMD_PORT, 0);
+       SET_FIELD(control, PXP_PRETEND_CMD_USE_PORT, 0);
+       SET_FIELD(control, PXP_PRETEND_CMD_PRETEND_PORT, 1);
+
+       p_ptt->pxp.pretend.control = cpu_to_le16(control);
+
+       REG_WR(p_hwfn,
+              qed_ptt_config_addr(p_ptt) +
+              offsetof(struct pxp_ptt_entry, pretend),
+              *(u32 *)&p_ptt->pxp.pretend);
+}
+
+/* DMAE */
+static void qed_dmae_opcode(struct qed_hwfn *p_hwfn,
+                           const u8 is_src_type_grc,
+                           const u8 is_dst_type_grc,
+                           struct qed_dmae_params *p_params)
+{
+       u32 opcode = 0;
+       u16 opcodeB = 0;
+
+       /* Whether the source is the PCIe or the GRC.
+        * 0- The source is the PCIe
+        * 1- The source is the GRC.
+        */
+       opcode |= (is_src_type_grc ? DMAE_CMD_SRC_MASK_GRC
+                                  : DMAE_CMD_SRC_MASK_PCIE) <<
+                  DMAE_CMD_SRC_SHIFT;
+       opcode |= ((p_hwfn->rel_pf_id & DMAE_CMD_SRC_PF_ID_MASK) <<
+                  DMAE_CMD_SRC_PF_ID_SHIFT);
+
+       /* The destination of the DMA can be: 0-None 1-PCIe 2-GRC 3-None */
+       opcode |= (is_dst_type_grc ? DMAE_CMD_DST_MASK_GRC
+                                  : DMAE_CMD_DST_MASK_PCIE) <<
+                  DMAE_CMD_DST_SHIFT;
+       opcode |= ((p_hwfn->rel_pf_id & DMAE_CMD_DST_PF_ID_MASK) <<
+                  DMAE_CMD_DST_PF_ID_SHIFT);
+
+       /* Whether to write a completion word to the completion destination:
+        * 0-Do not write a completion word
+        * 1-Write the completion word
+        */
+       opcode |= (DMAE_CMD_COMP_WORD_EN_MASK << DMAE_CMD_COMP_WORD_EN_SHIFT);
+       opcode |= (DMAE_CMD_SRC_ADDR_RESET_MASK <<
+                  DMAE_CMD_SRC_ADDR_RESET_SHIFT);
+
+       if (p_params->flags & QED_DMAE_FLAG_COMPLETION_DST)
+               opcode |= (1 << DMAE_CMD_COMP_FUNC_SHIFT);
+
+       opcode |= (DMAE_CMD_ENDIANITY << DMAE_CMD_ENDIANITY_MODE_SHIFT);
+
+       opcode |= ((p_hwfn->port_id) << DMAE_CMD_PORT_ID_SHIFT);
+
+       /* reset source address in next go */
+       opcode |= (DMAE_CMD_SRC_ADDR_RESET_MASK <<
+                  DMAE_CMD_SRC_ADDR_RESET_SHIFT);
+
+       /* reset dest address in next go */
+       opcode |= (DMAE_CMD_DST_ADDR_RESET_MASK <<
+                  DMAE_CMD_DST_ADDR_RESET_SHIFT);
+
+       opcodeB |= (DMAE_CMD_SRC_VF_ID_MASK <<
+                   DMAE_CMD_SRC_VF_ID_SHIFT);
+
+       opcodeB |= (DMAE_CMD_DST_VF_ID_MASK <<
+                   DMAE_CMD_DST_VF_ID_SHIFT);
+
+       p_hwfn->dmae_info.p_dmae_cmd->opcode = cpu_to_le32(opcode);
+       p_hwfn->dmae_info.p_dmae_cmd->opcode_b = cpu_to_le16(opcodeB);
+}
+
+u32 qed_dmae_idx_to_go_cmd(u8 idx)
+{
+       /* All the DMAE 'go' registers form an array in internal memory */
+       return DMAE_REG_GO_C0 + (idx << 2);
+}
+
+static int
+qed_dmae_post_command(struct qed_hwfn *p_hwfn,
+                     struct qed_ptt *p_ptt)
+{
+       struct dmae_cmd *command = p_hwfn->dmae_info.p_dmae_cmd;
+       u8 idx_cmd = p_hwfn->dmae_info.channel, i;
+       int qed_status = 0;
+
+       /* verify address is not NULL */
+       if ((((command->dst_addr_lo == 0) && (command->dst_addr_hi == 0)) ||
+            ((command->src_addr_lo == 0) && (command->src_addr_hi == 0)))) {
+               DP_NOTICE(p_hwfn,
+                         "source or destination address 0 idx_cmd=%d\n"
+                         "opcode = [0x%08x,0x%04x] len=0x%x src=0x%x:%x dst=0x%x:%x\n",
+                          idx_cmd,
+                          le32_to_cpu(command->opcode),
+                          le16_to_cpu(command->opcode_b),
+                          le16_to_cpu(command->length),
+                          le32_to_cpu(command->src_addr_hi),
+                          le32_to_cpu(command->src_addr_lo),
+                          le32_to_cpu(command->dst_addr_hi),
+                          le32_to_cpu(command->dst_addr_lo));
+
+               return -EINVAL;
+       }
+
+       DP_VERBOSE(p_hwfn,
+                  NETIF_MSG_HW,
+                  "Posting DMAE command [idx %d]: opcode = [0x%08x,0x%04x] len=0x%x src=0x%x:%x dst=0x%x:%x\n",
+                  idx_cmd,
+                  le32_to_cpu(command->opcode),
+                  le16_to_cpu(command->opcode_b),
+                  le16_to_cpu(command->length),
+                  le32_to_cpu(command->src_addr_hi),
+                  le32_to_cpu(command->src_addr_lo),
+                  le32_to_cpu(command->dst_addr_hi),
+                  le32_to_cpu(command->dst_addr_lo));
+
+       /* Copy the command to DMAE - need to do it before every call
+        * for source/dest address no reset.
+        * The first 9 DWs are the command registers, the 10 DW is the
+        * GO register, and the rest are result registers
+        * (which are read only by the client).
+        */
+       for (i = 0; i < DMAE_CMD_SIZE; i++) {
+               u32 data = (i < DMAE_CMD_SIZE_TO_FILL) ?
+                          *(((u32 *)command) + i) : 0;
+
+               qed_wr(p_hwfn, p_ptt,
+                      DMAE_REG_CMD_MEM +
+                      (idx_cmd * DMAE_CMD_SIZE * sizeof(u32)) +
+                      (i * sizeof(u32)), data);
+       }
+
+       qed_wr(p_hwfn, p_ptt,
+              qed_dmae_idx_to_go_cmd(idx_cmd),
+              DMAE_GO_VALUE);
+
+       return qed_status;
+}
+
+int qed_dmae_info_alloc(struct qed_hwfn *p_hwfn)
+{
+       dma_addr_t *p_addr = &p_hwfn->dmae_info.completion_word_phys_addr;
+       struct dmae_cmd **p_cmd = &p_hwfn->dmae_info.p_dmae_cmd;
+       u32 **p_buff = &p_hwfn->dmae_info.p_intermediate_buffer;
+       u32 **p_comp = &p_hwfn->dmae_info.p_completion_word;
+
+       *p_comp = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                    sizeof(u32),
+                                    p_addr,
+                                    GFP_KERNEL);
+       if (!*p_comp) {
+               DP_NOTICE(p_hwfn, "Failed to allocate `p_completion_word'\n");
+               goto err;
+       }
+
+       p_addr = &p_hwfn->dmae_info.dmae_cmd_phys_addr;
+       *p_cmd = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                   sizeof(struct dmae_cmd),
+                                   p_addr, GFP_KERNEL);
+       if (!*p_cmd) {
+               DP_NOTICE(p_hwfn, "Failed to allocate `struct dmae_cmd'\n");
+               goto err;
+       }
+
+       p_addr = &p_hwfn->dmae_info.intermediate_buffer_phys_addr;
+       *p_buff = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                    sizeof(u32) * DMAE_MAX_RW_SIZE,
+                                    p_addr, GFP_KERNEL);
+       if (!*p_buff) {
+               DP_NOTICE(p_hwfn, "Failed to allocate `intermediate_buffer'\n");
+               goto err;
+       }
+
+       p_hwfn->dmae_info.channel = p_hwfn->rel_pf_id;
+
+       return 0;
+err:
+       qed_dmae_info_free(p_hwfn);
+       return -ENOMEM;
+}
+
+void qed_dmae_info_free(struct qed_hwfn *p_hwfn)
+{
+       dma_addr_t p_phys;
+
+       /* Just make sure no one is in the middle */
+       mutex_lock(&p_hwfn->dmae_info.mutex);
+
+       if (p_hwfn->dmae_info.p_completion_word) {
+               p_phys = p_hwfn->dmae_info.completion_word_phys_addr;
+               dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                                 sizeof(u32),
+                                 p_hwfn->dmae_info.p_completion_word,
+                                 p_phys);
+               p_hwfn->dmae_info.p_completion_word = NULL;
+       }
+
+       if (p_hwfn->dmae_info.p_dmae_cmd) {
+               p_phys = p_hwfn->dmae_info.dmae_cmd_phys_addr;
+               dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                                 sizeof(struct dmae_cmd),
+                                 p_hwfn->dmae_info.p_dmae_cmd,
+                                 p_phys);
+               p_hwfn->dmae_info.p_dmae_cmd = NULL;
+       }
+
+       if (p_hwfn->dmae_info.p_intermediate_buffer) {
+               p_phys = p_hwfn->dmae_info.intermediate_buffer_phys_addr;
+               dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                                 sizeof(u32) * DMAE_MAX_RW_SIZE,
+                                 p_hwfn->dmae_info.p_intermediate_buffer,
+                                 p_phys);
+               p_hwfn->dmae_info.p_intermediate_buffer = NULL;
+       }
+
+       mutex_unlock(&p_hwfn->dmae_info.mutex);
+}
+
+static int qed_dmae_operation_wait(struct qed_hwfn *p_hwfn)
+{
+       u32 wait_cnt = 0;
+       u32 wait_cnt_limit = 10000;
+
+       int qed_status = 0;
+
+       barrier();
+       while (*p_hwfn->dmae_info.p_completion_word != DMAE_COMPLETION_VAL) {
+               udelay(DMAE_MIN_WAIT_TIME);
+               if (++wait_cnt > wait_cnt_limit) {
+                       DP_NOTICE(p_hwfn->cdev,
+                                 "Timed-out waiting for operation to complete. Completion word is 0x%08x expected 0x%08x.\n",
+                                 *p_hwfn->dmae_info.p_completion_word,
+                                DMAE_COMPLETION_VAL);
+                       qed_status = -EBUSY;
+                       break;
+               }
+
+               /* to sync the completion_word since we are not
+                * using the volatile keyword for p_completion_word
+                */
+               barrier();
+       }
+
+       if (qed_status == 0)
+               *p_hwfn->dmae_info.p_completion_word = 0;
+
+       return qed_status;
+}
+
+static int qed_dmae_execute_sub_operation(struct qed_hwfn *p_hwfn,
+                                         struct qed_ptt *p_ptt,
+                                         u64 src_addr,
+                                         u64 dst_addr,
+                                         u8 src_type,
+                                         u8 dst_type,
+                                         u32 length)
+{
+       dma_addr_t phys = p_hwfn->dmae_info.intermediate_buffer_phys_addr;
+       struct dmae_cmd *cmd = p_hwfn->dmae_info.p_dmae_cmd;
+       int qed_status = 0;
+
+       switch (src_type) {
+       case QED_DMAE_ADDRESS_GRC:
+       case QED_DMAE_ADDRESS_HOST_PHYS:
+               cmd->src_addr_hi = cpu_to_le32(upper_32_bits(src_addr));
+               cmd->src_addr_lo = cpu_to_le32(lower_32_bits(src_addr));
+               break;
+       /* for virtual source addresses we use the intermediate buffer. */
+       case QED_DMAE_ADDRESS_HOST_VIRT:
+               cmd->src_addr_hi = cpu_to_le32(upper_32_bits(phys));
+               cmd->src_addr_lo = cpu_to_le32(lower_32_bits(phys));
+               memcpy(&p_hwfn->dmae_info.p_intermediate_buffer[0],
+                      (void *)(uintptr_t)src_addr,
+                      length * sizeof(u32));
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (dst_type) {
+       case QED_DMAE_ADDRESS_GRC:
+       case QED_DMAE_ADDRESS_HOST_PHYS:
+               cmd->dst_addr_hi = cpu_to_le32(upper_32_bits(dst_addr));
+               cmd->dst_addr_lo = cpu_to_le32(lower_32_bits(dst_addr));
+               break;
+       /* for virtual source addresses we use the intermediate buffer. */
+       case QED_DMAE_ADDRESS_HOST_VIRT:
+               cmd->dst_addr_hi = cpu_to_le32(upper_32_bits(phys));
+               cmd->dst_addr_lo = cpu_to_le32(lower_32_bits(phys));
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       cmd->length = cpu_to_le16((u16)length);
+
+       qed_dmae_post_command(p_hwfn, p_ptt);
+
+       qed_status = qed_dmae_operation_wait(p_hwfn);
+
+       if (qed_status) {
+               DP_NOTICE(p_hwfn,
+                         "qed_dmae_host2grc: Wait Failed. source_addr 0x%llx, grc_addr 0x%llx, size_in_dwords 0x%x\n",
+                         src_addr,
+                         dst_addr,
+                         length);
+               return qed_status;
+       }
+
+       if (dst_type == QED_DMAE_ADDRESS_HOST_VIRT)
+               memcpy((void *)(uintptr_t)(dst_addr),
+                      &p_hwfn->dmae_info.p_intermediate_buffer[0],
+                      length * sizeof(u32));
+
+       return 0;
+}
+
+static int qed_dmae_execute_command(struct qed_hwfn *p_hwfn,
+                                   struct qed_ptt *p_ptt,
+                                   u64 src_addr, u64 dst_addr,
+                                   u8 src_type, u8 dst_type,
+                                   u32 size_in_dwords,
+                                   struct qed_dmae_params *p_params)
+{
+       dma_addr_t phys = p_hwfn->dmae_info.completion_word_phys_addr;
+       u16 length_cur = 0, i = 0, cnt_split = 0, length_mod = 0;
+       struct dmae_cmd *cmd = p_hwfn->dmae_info.p_dmae_cmd;
+       u64 src_addr_split = 0, dst_addr_split = 0;
+       u16 length_limit = DMAE_MAX_RW_SIZE;
+       int qed_status = 0;
+       u32 offset = 0;
+
+       qed_dmae_opcode(p_hwfn,
+                       (src_type == QED_DMAE_ADDRESS_GRC),
+                       (dst_type == QED_DMAE_ADDRESS_GRC),
+                       p_params);
+
+       cmd->comp_addr_lo = cpu_to_le32(lower_32_bits(phys));
+       cmd->comp_addr_hi = cpu_to_le32(upper_32_bits(phys));
+       cmd->comp_val = cpu_to_le32(DMAE_COMPLETION_VAL);
+
+       /* Check if the grc_addr is valid like < MAX_GRC_OFFSET */
+       cnt_split = size_in_dwords / length_limit;
+       length_mod = size_in_dwords % length_limit;
+
+       src_addr_split = src_addr;
+       dst_addr_split = dst_addr;
+
+       for (i = 0; i <= cnt_split; i++) {
+               offset = length_limit * i;
+
+               if (!(p_params->flags & QED_DMAE_FLAG_RW_REPL_SRC)) {
+                       if (src_type == QED_DMAE_ADDRESS_GRC)
+                               src_addr_split = src_addr + offset;
+                       else
+                               src_addr_split = src_addr + (offset * 4);
+               }
+
+               if (dst_type == QED_DMAE_ADDRESS_GRC)
+                       dst_addr_split = dst_addr + offset;
+               else
+                       dst_addr_split = dst_addr + (offset * 4);
+
+               length_cur = (cnt_split == i) ? length_mod : length_limit;
+
+               /* might be zero on last iteration */
+               if (!length_cur)
+                       continue;
+
+               qed_status = qed_dmae_execute_sub_operation(p_hwfn,
+                                                           p_ptt,
+                                                           src_addr_split,
+                                                           dst_addr_split,
+                                                           src_type,
+                                                           dst_type,
+                                                           length_cur);
+               if (qed_status) {
+                       DP_NOTICE(p_hwfn,
+                                 "qed_dmae_execute_sub_operation Failed with error 0x%x. source_addr 0x%llx, destination addr 0x%llx, size_in_dwords 0x%x\n",
+                                 qed_status,
+                                 src_addr,
+                                 dst_addr,
+                                 length_cur);
+                       break;
+               }
+       }
+
+       return qed_status;
+}
+
+int qed_dmae_host2grc(struct qed_hwfn *p_hwfn,
+                     struct qed_ptt *p_ptt,
+                     u64 source_addr,
+                     u32 grc_addr,
+                     u32 size_in_dwords,
+                     u32 flags)
+{
+       u32 grc_addr_in_dw = grc_addr / sizeof(u32);
+       struct qed_dmae_params params;
+       int rc;
+
+       memset(&params, 0, sizeof(struct qed_dmae_params));
+       params.flags = flags;
+
+       mutex_lock(&p_hwfn->dmae_info.mutex);
+
+       rc = qed_dmae_execute_command(p_hwfn, p_ptt, source_addr,
+                                     grc_addr_in_dw,
+                                     QED_DMAE_ADDRESS_HOST_VIRT,
+                                     QED_DMAE_ADDRESS_GRC,
+                                     size_in_dwords, &params);
+
+       mutex_unlock(&p_hwfn->dmae_info.mutex);
+
+       return rc;
+}
+
+u16 qed_get_qm_pq(struct qed_hwfn *p_hwfn,
+                 enum protocol_type proto,
+                 union qed_qm_pq_params *p_params)
+{
+       u16 pq_id = 0;
+
+       if ((proto == PROTOCOLID_CORE || proto == PROTOCOLID_ETH) &&
+           !p_params) {
+               DP_NOTICE(p_hwfn,
+                         "Protocol %d received NULL PQ params\n",
+                         proto);
+               return 0;
+       }
+
+       switch (proto) {
+       case PROTOCOLID_CORE:
+               if (p_params->core.tc == LB_TC)
+                       pq_id = p_hwfn->qm_info.pure_lb_pq;
+               else
+                       pq_id = p_hwfn->qm_info.offload_pq;
+               break;
+       case PROTOCOLID_ETH:
+               pq_id = p_params->eth.tc;
+               break;
+       default:
+               pq_id = 0;
+       }
+
+       pq_id = CM_TX_PQ_BASE + pq_id + RESC_START(p_hwfn, QED_PQ);
+
+       return pq_id;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hw.h b/drivers/net/ethernet/qlogic/qed/qed_hw.h
new file mode 100644 (file)
index 0000000..e56d433
--- /dev/null
@@ -0,0 +1,263 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_HW_H
+#define _QED_HW_H
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "qed.h"
+#include "qed_dev_api.h"
+
+/* Forward decleration */
+struct qed_ptt;
+
+enum reserved_ptts {
+       RESERVED_PTT_EDIAG,
+       RESERVED_PTT_USER_SPACE,
+       RESERVED_PTT_MAIN,
+       RESERVED_PTT_DPC,
+       RESERVED_PTT_MAX
+};
+
+enum _dmae_cmd_dst_mask {
+       DMAE_CMD_DST_MASK_NONE  = 0,
+       DMAE_CMD_DST_MASK_PCIE  = 1,
+       DMAE_CMD_DST_MASK_GRC   = 2
+};
+
+enum _dmae_cmd_src_mask {
+       DMAE_CMD_SRC_MASK_PCIE  = 0,
+       DMAE_CMD_SRC_MASK_GRC   = 1
+};
+
+enum _dmae_cmd_crc_mask {
+       DMAE_CMD_COMP_CRC_EN_MASK_NONE  = 0,
+       DMAE_CMD_COMP_CRC_EN_MASK_SET   = 1
+};
+
+/* definitions for DMA constants */
+#define DMAE_GO_VALUE   0x1
+
+#define DMAE_COMPLETION_VAL     0xD1AE
+#define DMAE_CMD_ENDIANITY      0x2
+
+#define DMAE_CMD_SIZE   14
+#define DMAE_CMD_SIZE_TO_FILL   (DMAE_CMD_SIZE - 5)
+#define DMAE_MIN_WAIT_TIME      0x2
+#define DMAE_MAX_CLIENTS        32
+
+/**
+ * @brief qed_gtt_init - Initialize GTT windows
+ *
+ * @param p_hwfn
+ */
+void qed_gtt_init(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_ptt_invalidate - Forces all ptt entries to be re-configured
+ *
+ * @param p_hwfn
+ */
+void qed_ptt_invalidate(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_ptt_pool_alloc - Allocate and initialize PTT pool
+ *
+ * @param p_hwfn
+ *
+ * @return struct _qed_status - success (0), negative - error.
+ */
+int qed_ptt_pool_alloc(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_ptt_pool_free -
+ *
+ * @param p_hwfn
+ */
+void qed_ptt_pool_free(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_ptt_get_hw_addr - Get PTT's GRC/HW address
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ *
+ * @return u32
+ */
+u32 qed_ptt_get_hw_addr(struct qed_hwfn *p_hwfn,
+                       struct qed_ptt *p_ptt);
+
+/**
+ * @brief qed_ptt_get_bar_addr - Get PPT's external BAR address
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ *
+ * @return u32
+ */
+u32 qed_ptt_get_bar_addr(struct qed_ptt *p_ptt);
+
+/**
+ * @brief qed_ptt_set_win - Set PTT Window's GRC BAR address
+ *
+ * @param p_hwfn
+ * @param new_hw_addr
+ * @param p_ptt
+ */
+void qed_ptt_set_win(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt,
+                    u32 new_hw_addr);
+
+/**
+ * @brief qed_get_reserved_ptt - Get a specific reserved PTT
+ *
+ * @param p_hwfn
+ * @param ptt_idx
+ *
+ * @return struct qed_ptt *
+ */
+struct qed_ptt *qed_get_reserved_ptt(struct qed_hwfn *p_hwfn,
+                                    enum reserved_ptts ptt_idx);
+
+/**
+ * @brief qed_wr - Write value to BAR using the given ptt
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param val
+ * @param hw_addr
+ */
+void qed_wr(struct qed_hwfn *p_hwfn,
+           struct qed_ptt *p_ptt,
+           u32 hw_addr,
+           u32 val);
+
+/**
+ * @brief qed_rd - Read value from BAR using the given ptt
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param val
+ * @param hw_addr
+ */
+u32 qed_rd(struct qed_hwfn *p_hwfn,
+          struct qed_ptt *p_ptt,
+          u32 hw_addr);
+
+/**
+ * @brief qed_memcpy_from - copy n bytes from BAR using the given
+ *        ptt
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param dest
+ * @param hw_addr
+ * @param n
+ */
+void qed_memcpy_from(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt,
+                    void *dest,
+                    u32 hw_addr,
+                    size_t n);
+
+/**
+ * @brief qed_memcpy_to - copy n bytes to BAR using the given
+ *        ptt
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param hw_addr
+ * @param src
+ * @param n
+ */
+void qed_memcpy_to(struct qed_hwfn *p_hwfn,
+                  struct qed_ptt *p_ptt,
+                  u32 hw_addr,
+                  void *src,
+                  size_t n);
+/**
+ * @brief qed_fid_pretend - pretend to another function when
+ *        accessing the ptt window. There is no way to unpretend
+ *        a function. The only way to cancel a pretend is to
+ *        pretend back to the original function.
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param fid - fid field of pxp_pretend structure. Can contain
+ *            either pf / vf, port/path fields are don't care.
+ */
+void qed_fid_pretend(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt,
+                    u16 fid);
+
+/**
+ * @brief qed_port_pretend - pretend to another port when
+ *        accessing the ptt window
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param port_id - the port to pretend to
+ */
+void qed_port_pretend(struct qed_hwfn *p_hwfn,
+                     struct qed_ptt *p_ptt,
+                     u8 port_id);
+
+/**
+ * @brief qed_port_unpretend - cancel any previously set port
+ *        pretend
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ */
+void qed_port_unpretend(struct qed_hwfn *p_hwfn,
+                       struct qed_ptt *p_ptt);
+
+/**
+ * @brief qed_dmae_idx_to_go_cmd - map the idx to dmae cmd
+ * this is declared here since other files will require it.
+ * @param idx
+ */
+u32 qed_dmae_idx_to_go_cmd(u8 idx);
+
+/**
+ * @brief qed_dmae_info_alloc - Init the dmae_info structure
+ * which is part of p_hwfn.
+ * @param p_hwfn
+ */
+int qed_dmae_info_alloc(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_dmae_info_free - Free the dmae_info structure
+ * which is part of p_hwfn
+ *
+ * @param p_hwfn
+ */
+void qed_dmae_info_free(struct qed_hwfn *p_hwfn);
+
+union qed_qm_pq_params {
+       struct {
+               u8 tc;
+       }       core;
+
+       struct {
+               u8      is_vf;
+               u8      vf_id;
+               u8      tc;
+       }       eth;
+};
+
+u16 qed_get_qm_pq(struct qed_hwfn *p_hwfn,
+                 enum protocol_type proto,
+                 union qed_qm_pq_params *params);
+
+int qed_init_fw_data(struct qed_dev *cdev,
+                    const u8 *fw_data);
+#endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c b/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c
new file mode 100644 (file)
index 0000000..0b21a55
--- /dev/null
@@ -0,0 +1,798 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_init_ops.h"
+#include "qed_reg_addr.h"
+
+enum cminterface {
+       MCM_SEC,
+       MCM_PRI,
+       UCM_SEC,
+       UCM_PRI,
+       TCM_SEC,
+       TCM_PRI,
+       YCM_SEC,
+       YCM_PRI,
+       XCM_SEC,
+       XCM_PRI,
+       NUM_OF_CM_INTERFACES
+};
+
+/* general constants */
+#define QM_PQ_ELEMENT_SIZE                      4 /* in bytes */
+#define QM_PQ_MEM_4KB(pq_size) (pq_size ? DIV_ROUND_UP((pq_size + 1) * \
+                                                       QM_PQ_ELEMENT_SIZE, \
+                                                       0x1000) : 0)
+#define QM_PQ_SIZE_256B(pq_size)       (pq_size ? DIV_ROUND_UP(pq_size, \
+                                                               0x100) - 1 : 0)
+#define QM_INVALID_PQ_ID                        0xffff
+/* feature enable */
+#define QM_BYPASS_EN                            1
+#define QM_BYTE_CRD_EN                          1
+/* other PQ constants */
+#define QM_OTHER_PQS_PER_PF                     4
+/* WFQ constants */
+#define QM_WFQ_UPPER_BOUND             6250000
+#define QM_WFQ_VP_PQ_VOQ_SHIFT          0
+#define QM_WFQ_VP_PQ_PF_SHIFT           5
+#define QM_WFQ_INC_VAL(weight)          ((weight) * 0x9000)
+#define QM_WFQ_MAX_INC_VAL                      4375000
+#define QM_WFQ_INIT_CRD(inc_val)        (2 * (inc_val))
+/* RL constants */
+#define QM_RL_UPPER_BOUND                       6250000
+#define QM_RL_PERIOD                            5               /* in us */
+#define QM_RL_PERIOD_CLK_25M            (25 * QM_RL_PERIOD)
+#define QM_RL_INC_VAL(rate)            max_t(u32,      \
+                                             (((rate ? rate : 1000000) \
+                                               * QM_RL_PERIOD) / 8), 1)
+#define QM_RL_MAX_INC_VAL                       4375000
+/* AFullOprtnstcCrdMask constants */
+#define QM_OPPOR_LINE_VOQ_DEF           1
+#define QM_OPPOR_FW_STOP_DEF            0
+#define QM_OPPOR_PQ_EMPTY_DEF           1
+#define EAGLE_WORKAROUND_TC                     7
+/* Command Queue constants */
+#define PBF_CMDQ_PURE_LB_LINES                          150
+#define PBF_CMDQ_EAGLE_WORKAROUND_LINES         8
+#define PBF_CMDQ_LINES_RT_OFFSET(voq)           (               \
+               PBF_REG_YCMD_QS_NUM_LINES_VOQ0_RT_OFFSET + voq * \
+               (PBF_REG_YCMD_QS_NUM_LINES_VOQ1_RT_OFFSET -      \
+                PBF_REG_YCMD_QS_NUM_LINES_VOQ0_RT_OFFSET))
+#define PBF_BTB_GUARANTEED_RT_OFFSET(voq)       (            \
+               PBF_REG_BTB_GUARANTEED_VOQ0_RT_OFFSET + voq * \
+               (PBF_REG_BTB_GUARANTEED_VOQ1_RT_OFFSET -      \
+                PBF_REG_BTB_GUARANTEED_VOQ0_RT_OFFSET))
+#define QM_VOQ_LINE_CRD(pbf_cmd_lines)          ((((pbf_cmd_lines) - \
+                                                  4) *              \
+                                                 2) | QM_LINE_CRD_REG_SIGN_BIT)
+/* BTB: blocks constants (block size = 256B) */
+#define BTB_JUMBO_PKT_BLOCKS            38
+#define BTB_HEADROOM_BLOCKS                     BTB_JUMBO_PKT_BLOCKS
+#define BTB_EAGLE_WORKAROUND_BLOCKS     4
+#define BTB_PURE_LB_FACTOR                      10
+#define BTB_PURE_LB_RATIO                       7
+/* QM stop command constants */
+#define QM_STOP_PQ_MASK_WIDTH                   32
+#define QM_STOP_CMD_ADDR                                0x2
+#define QM_STOP_CMD_STRUCT_SIZE                 2
+#define QM_STOP_CMD_PAUSE_MASK_OFFSET   0
+#define QM_STOP_CMD_PAUSE_MASK_SHIFT    0
+#define QM_STOP_CMD_PAUSE_MASK_MASK             -1
+#define QM_STOP_CMD_GROUP_ID_OFFSET             1
+#define QM_STOP_CMD_GROUP_ID_SHIFT              16
+#define QM_STOP_CMD_GROUP_ID_MASK               15
+#define QM_STOP_CMD_PQ_TYPE_OFFSET              1
+#define QM_STOP_CMD_PQ_TYPE_SHIFT               24
+#define QM_STOP_CMD_PQ_TYPE_MASK                1
+#define QM_STOP_CMD_MAX_POLL_COUNT              100
+#define QM_STOP_CMD_POLL_PERIOD_US              500
+/* QM command macros */
+#define QM_CMD_STRUCT_SIZE(cmd)                        cmd ## \
+       _STRUCT_SIZE
+#define QM_CMD_SET_FIELD(var, cmd, field,                                \
+                        value)        SET_FIELD(var[cmd ## _ ## field ## \
+                                                    _OFFSET],            \
+                                                cmd ## _ ## field,       \
+                                                value)
+/* QM: VOQ macros */
+#define PHYS_VOQ(port, tc, max_phy_tcs_pr_port)        ((port) *       \
+                                                (max_phy_tcs_pr_port) \
+                                                + (tc))
+#define LB_VOQ(port)                           ( \
+               MAX_PHYS_VOQS + (port))
+#define VOQ(port, tc, max_phy_tcs_pr_port)     \
+       ((tc) <         \
+        LB_TC ? PHYS_VOQ(port,         \
+                         tc,                    \
+                         max_phy_tcs_pr_port) \
+               : LB_VOQ(port))
+/******************** INTERNAL IMPLEMENTATION *********************/
+/* Prepare PF RL enable/disable runtime init values */
+static void qed_enable_pf_rl(struct qed_hwfn *p_hwfn,
+                            bool pf_rl_en)
+{
+       STORE_RT_REG(p_hwfn, QM_REG_RLPFENABLE_RT_OFFSET, pf_rl_en ? 1 : 0);
+       if (pf_rl_en) {
+               /* enable RLs for all VOQs */
+               STORE_RT_REG(p_hwfn, QM_REG_RLPFVOQENABLE_RT_OFFSET,
+                            (1 << MAX_NUM_VOQS) - 1);
+               /* write RL period */
+               STORE_RT_REG(p_hwfn,
+                            QM_REG_RLPFPERIOD_RT_OFFSET,
+                            QM_RL_PERIOD_CLK_25M);
+               STORE_RT_REG(p_hwfn,
+                            QM_REG_RLPFPERIODTIMER_RT_OFFSET,
+                            QM_RL_PERIOD_CLK_25M);
+               /* set credit threshold for QM bypass flow */
+               if (QM_BYPASS_EN)
+                       STORE_RT_REG(p_hwfn,
+                                    QM_REG_AFULLQMBYPTHRPFRL_RT_OFFSET,
+                                    QM_RL_UPPER_BOUND);
+       }
+}
+
+/* Prepare PF WFQ enable/disable runtime init values */
+static void qed_enable_pf_wfq(struct qed_hwfn *p_hwfn,
+                             bool pf_wfq_en)
+{
+       STORE_RT_REG(p_hwfn, QM_REG_WFQPFENABLE_RT_OFFSET, pf_wfq_en ? 1 : 0);
+       /* set credit threshold for QM bypass flow */
+       if (pf_wfq_en && QM_BYPASS_EN)
+               STORE_RT_REG(p_hwfn,
+                            QM_REG_AFULLQMBYPTHRPFWFQ_RT_OFFSET,
+                            QM_WFQ_UPPER_BOUND);
+}
+
+/* Prepare VPORT RL enable/disable runtime init values */
+static void qed_enable_vport_rl(struct qed_hwfn *p_hwfn,
+                               bool vport_rl_en)
+{
+       STORE_RT_REG(p_hwfn, QM_REG_RLGLBLENABLE_RT_OFFSET,
+                    vport_rl_en ? 1 : 0);
+       if (vport_rl_en) {
+               /* write RL period (use timer 0 only) */
+               STORE_RT_REG(p_hwfn,
+                            QM_REG_RLGLBLPERIOD_0_RT_OFFSET,
+                            QM_RL_PERIOD_CLK_25M);
+               STORE_RT_REG(p_hwfn,
+                            QM_REG_RLGLBLPERIODTIMER_0_RT_OFFSET,
+                            QM_RL_PERIOD_CLK_25M);
+               /* set credit threshold for QM bypass flow */
+               if (QM_BYPASS_EN)
+                       STORE_RT_REG(p_hwfn,
+                                    QM_REG_AFULLQMBYPTHRGLBLRL_RT_OFFSET,
+                                    QM_RL_UPPER_BOUND);
+       }
+}
+
+/* Prepare VPORT WFQ enable/disable runtime init values */
+static void qed_enable_vport_wfq(struct qed_hwfn *p_hwfn,
+                                bool vport_wfq_en)
+{
+       STORE_RT_REG(p_hwfn, QM_REG_WFQVPENABLE_RT_OFFSET,
+                    vport_wfq_en ? 1 : 0);
+       /* set credit threshold for QM bypass flow */
+       if (vport_wfq_en && QM_BYPASS_EN)
+               STORE_RT_REG(p_hwfn,
+                            QM_REG_AFULLQMBYPTHRVPWFQ_RT_OFFSET,
+                            QM_WFQ_UPPER_BOUND);
+}
+
+/* Prepare runtime init values to allocate PBF command queue lines for
+ * the specified VOQ
+ */
+static void qed_cmdq_lines_voq_rt_init(struct qed_hwfn *p_hwfn,
+                                      u8 voq,
+                                      u16 cmdq_lines)
+{
+       u32 qm_line_crd;
+
+       /* In A0 - Limit the size of pbf queue so that only 511 commands with
+        * the minimum size of 4 (FCoE minimum size)
+        */
+       bool is_bb_a0 = QED_IS_BB_A0(p_hwfn->cdev);
+
+       if (is_bb_a0)
+               cmdq_lines = min_t(u32, cmdq_lines, 1022);
+       qm_line_crd = QM_VOQ_LINE_CRD(cmdq_lines);
+       OVERWRITE_RT_REG(p_hwfn, PBF_CMDQ_LINES_RT_OFFSET(voq),
+                        (u32)cmdq_lines);
+       STORE_RT_REG(p_hwfn, QM_REG_VOQCRDLINE_RT_OFFSET + voq, qm_line_crd);
+       STORE_RT_REG(p_hwfn, QM_REG_VOQINITCRDLINE_RT_OFFSET + voq,
+                    qm_line_crd);
+}
+
+/* Prepare runtime init values to allocate PBF command queue lines. */
+static void qed_cmdq_lines_rt_init(
+       struct qed_hwfn *p_hwfn,
+       u8 max_ports_per_engine,
+       u8 max_phys_tcs_per_port,
+       struct init_qm_port_params port_params[MAX_NUM_PORTS])
+{
+       u8 tc, voq, port_id;
+
+       /* clear PBF lines for all VOQs */
+       for (voq = 0; voq < MAX_NUM_VOQS; voq++)
+               STORE_RT_REG(p_hwfn, PBF_CMDQ_LINES_RT_OFFSET(voq), 0);
+       for (port_id = 0; port_id < max_ports_per_engine; port_id++) {
+               if (port_params[port_id].active) {
+                       u16 phys_lines, phys_lines_per_tc;
+                       u8 phys_tcs = port_params[port_id].num_active_phys_tcs;
+
+                       /* find #lines to divide between the active
+                        * physical TCs.
+                        */
+                       phys_lines = port_params[port_id].num_pbf_cmd_lines -
+                                    PBF_CMDQ_PURE_LB_LINES;
+                       /* find #lines per active physical TC */
+                       phys_lines_per_tc = phys_lines / phys_tcs;
+                       /* init registers per active TC */
+                       for (tc = 0; tc < phys_tcs; tc++) {
+                               voq = PHYS_VOQ(port_id, tc,
+                                              max_phys_tcs_per_port);
+                               qed_cmdq_lines_voq_rt_init(p_hwfn, voq,
+                                                          phys_lines_per_tc);
+                       }
+                       /* init registers for pure LB TC */
+                       qed_cmdq_lines_voq_rt_init(p_hwfn, LB_VOQ(port_id),
+                                                  PBF_CMDQ_PURE_LB_LINES);
+               }
+       }
+}
+
+static void qed_btb_blocks_rt_init(
+       struct qed_hwfn *p_hwfn,
+       u8 max_ports_per_engine,
+       u8 max_phys_tcs_per_port,
+       struct init_qm_port_params port_params[MAX_NUM_PORTS])
+{
+       u32 usable_blocks, pure_lb_blocks, phys_blocks;
+       u8 tc, voq, port_id;
+
+       for (port_id = 0; port_id < max_ports_per_engine; port_id++) {
+               u32 temp;
+               u8 phys_tcs;
+
+               if (!port_params[port_id].active)
+                       continue;
+
+               phys_tcs = port_params[port_id].num_active_phys_tcs;
+
+               /* subtract headroom blocks */
+               usable_blocks = port_params[port_id].num_btb_blocks -
+                               BTB_HEADROOM_BLOCKS;
+
+               /* find blocks per physical TC. use factor to avoid
+                * floating arithmethic.
+                */
+               pure_lb_blocks = (usable_blocks * BTB_PURE_LB_FACTOR) /
+                                (phys_tcs * BTB_PURE_LB_FACTOR +
+                                 BTB_PURE_LB_RATIO);
+               pure_lb_blocks = max_t(u32, BTB_JUMBO_PKT_BLOCKS,
+                                      pure_lb_blocks / BTB_PURE_LB_FACTOR);
+               phys_blocks = (usable_blocks - pure_lb_blocks) / phys_tcs;
+
+               /* init physical TCs */
+               for (tc = 0; tc < phys_tcs; tc++) {
+                       voq = PHYS_VOQ(port_id, tc, max_phys_tcs_per_port);
+                       STORE_RT_REG(p_hwfn, PBF_BTB_GUARANTEED_RT_OFFSET(voq),
+                                    phys_blocks);
+               }
+
+               /* init pure LB TC */
+               temp = LB_VOQ(port_id);
+               STORE_RT_REG(p_hwfn, PBF_BTB_GUARANTEED_RT_OFFSET(temp),
+                            pure_lb_blocks);
+       }
+}
+
+/* Prepare Tx PQ mapping runtime init values for the specified PF */
+static void qed_tx_pq_map_rt_init(
+       struct qed_hwfn *p_hwfn,
+       struct qed_ptt *p_ptt,
+       struct qed_qm_pf_rt_init_params *p_params,
+       u32 base_mem_addr_4kb)
+{
+       struct init_qm_vport_params *vport_params = p_params->vport_params;
+       u16 num_pqs = p_params->num_pf_pqs + p_params->num_vf_pqs;
+       u16 first_pq_group = p_params->start_pq / QM_PF_QUEUE_GROUP_SIZE;
+       u16 last_pq_group = (p_params->start_pq + num_pqs - 1) /
+                           QM_PF_QUEUE_GROUP_SIZE;
+       bool is_bb_a0 = QED_IS_BB_A0(p_hwfn->cdev);
+       u16 i, pq_id, pq_group;
+
+       /* a bit per Tx PQ indicating if the PQ is associated with a VF */
+       u32 tx_pq_vf_mask[MAX_QM_TX_QUEUES / QM_PF_QUEUE_GROUP_SIZE] = { 0 };
+       u32 tx_pq_vf_mask_width = is_bb_a0 ? 32 : QM_PF_QUEUE_GROUP_SIZE;
+       u32 num_tx_pq_vf_masks = MAX_QM_TX_QUEUES / tx_pq_vf_mask_width;
+       u32 pq_mem_4kb = QM_PQ_MEM_4KB(p_params->num_pf_cids);
+       u32 vport_pq_mem_4kb = QM_PQ_MEM_4KB(p_params->num_vf_cids);
+       u32 mem_addr_4kb = base_mem_addr_4kb;
+
+       /* set mapping from PQ group to PF */
+       for (pq_group = first_pq_group; pq_group <= last_pq_group; pq_group++)
+               STORE_RT_REG(p_hwfn, QM_REG_PQTX2PF_0_RT_OFFSET + pq_group,
+                            (u32)(p_params->pf_id));
+       /* set PQ sizes */
+       STORE_RT_REG(p_hwfn, QM_REG_MAXPQSIZE_0_RT_OFFSET,
+                    QM_PQ_SIZE_256B(p_params->num_pf_cids));
+       STORE_RT_REG(p_hwfn, QM_REG_MAXPQSIZE_1_RT_OFFSET,
+                    QM_PQ_SIZE_256B(p_params->num_vf_cids));
+
+       /* go over all Tx PQs */
+       for (i = 0, pq_id = p_params->start_pq; i < num_pqs; i++, pq_id++) {
+               u8 voq = VOQ(p_params->port_id, p_params->pq_params[i].tc_id,
+                            p_params->max_phys_tcs_per_port);
+               bool is_vf_pq = (i >= p_params->num_pf_pqs);
+               struct qm_rf_pq_map tx_pq_map;
+
+               /* update first Tx PQ of VPORT/TC */
+               u8 vport_id_in_pf = p_params->pq_params[i].vport_id -
+                                   p_params->start_vport;
+               u16 *pq_ids = &vport_params[vport_id_in_pf].first_tx_pq_id[0];
+               u16 first_tx_pq_id = pq_ids[p_params->pq_params[i].tc_id];
+
+               if (first_tx_pq_id == QM_INVALID_PQ_ID) {
+                       /* create new VP PQ */
+                       pq_ids[p_params->pq_params[i].tc_id] = pq_id;
+                       first_tx_pq_id = pq_id;
+                       /* map VP PQ to VOQ and PF */
+                       STORE_RT_REG(p_hwfn,
+                                    QM_REG_WFQVPMAP_RT_OFFSET +
+                                    first_tx_pq_id,
+                                    (voq << QM_WFQ_VP_PQ_VOQ_SHIFT) |
+                                    (p_params->pf_id <<
+                                     QM_WFQ_VP_PQ_PF_SHIFT));
+               }
+               /* fill PQ map entry */
+               memset(&tx_pq_map, 0, sizeof(tx_pq_map));
+               SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_PQ_VALID, 1);
+               SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_RL_VALID,
+                         is_vf_pq ? 1 : 0);
+               SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_VP_PQ_ID, first_tx_pq_id);
+               SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_RL_ID,
+                         is_vf_pq ? p_params->pq_params[i].vport_id : 0);
+               SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_VOQ, voq);
+               SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_WRR_WEIGHT_GROUP,
+                         p_params->pq_params[i].wrr_group);
+               /* write PQ map entry to CAM */
+               STORE_RT_REG(p_hwfn, QM_REG_TXPQMAP_RT_OFFSET + pq_id,
+                            *((u32 *)&tx_pq_map));
+               /* set base address */
+               STORE_RT_REG(p_hwfn,
+                            QM_REG_BASEADDRTXPQ_RT_OFFSET + pq_id,
+                            mem_addr_4kb);
+               /* check if VF PQ */
+               if (is_vf_pq) {
+                       /* if PQ is associated with a VF, add indication
+                        * to PQ VF mask
+                        */
+                       tx_pq_vf_mask[pq_id / tx_pq_vf_mask_width] |=
+                               (1 << (pq_id % tx_pq_vf_mask_width));
+                       mem_addr_4kb += vport_pq_mem_4kb;
+               } else {
+                       mem_addr_4kb += pq_mem_4kb;
+               }
+       }
+
+       /* store Tx PQ VF mask to size select register */
+       for (i = 0; i < num_tx_pq_vf_masks; i++) {
+               if (tx_pq_vf_mask[i]) {
+                       if (is_bb_a0) {
+                               u32 curr_mask = 0, addr;
+
+                               addr = QM_REG_MAXPQSIZETXSEL_0 + (i * 4);
+                               if (!p_params->is_first_pf)
+                                       curr_mask = qed_rd(p_hwfn, p_ptt,
+                                                          addr);
+
+                               addr = QM_REG_MAXPQSIZETXSEL_0_RT_OFFSET + i;
+
+                               STORE_RT_REG(p_hwfn, addr,
+                                            curr_mask | tx_pq_vf_mask[i]);
+                       } else {
+                               u32 addr;
+
+                               addr = QM_REG_MAXPQSIZETXSEL_0_RT_OFFSET + i;
+                               STORE_RT_REG(p_hwfn, addr,
+                                            tx_pq_vf_mask[i]);
+                       }
+               }
+       }
+}
+
+/* Prepare Other PQ mapping runtime init values for the specified PF */
+static void qed_other_pq_map_rt_init(struct qed_hwfn *p_hwfn,
+                                    u8 port_id,
+                                    u8 pf_id,
+                                    u32 num_pf_cids,
+                                    u32 num_tids,
+                                    u32 base_mem_addr_4kb)
+{
+       u16 i, pq_id;
+
+       /* a single other PQ group is used in each PF,
+        * where PQ group i is used in PF i.
+        */
+       u16 pq_group = pf_id;
+       u32 pq_size = num_pf_cids + num_tids;
+       u32 pq_mem_4kb = QM_PQ_MEM_4KB(pq_size);
+       u32 mem_addr_4kb = base_mem_addr_4kb;
+
+       /* map PQ group to PF */
+       STORE_RT_REG(p_hwfn, QM_REG_PQOTHER2PF_0_RT_OFFSET + pq_group,
+                    (u32)(pf_id));
+       /* set PQ sizes */
+       STORE_RT_REG(p_hwfn, QM_REG_MAXPQSIZE_2_RT_OFFSET,
+                    QM_PQ_SIZE_256B(pq_size));
+       /* set base address */
+       for (i = 0, pq_id = pf_id * QM_PF_QUEUE_GROUP_SIZE;
+            i < QM_OTHER_PQS_PER_PF; i++, pq_id++) {
+               STORE_RT_REG(p_hwfn,
+                            QM_REG_BASEADDROTHERPQ_RT_OFFSET + pq_id,
+                            mem_addr_4kb);
+               mem_addr_4kb += pq_mem_4kb;
+       }
+}
+
+/* Prepare PF WFQ runtime init values for the specified PF.
+ * Return -1 on error.
+ */
+static int qed_pf_wfq_rt_init(struct qed_hwfn *p_hwfn,
+                             struct qed_qm_pf_rt_init_params *p_params)
+{
+       u16 num_tx_pqs = p_params->num_pf_pqs + p_params->num_vf_pqs;
+       u32 crd_reg_offset;
+       u32 inc_val;
+       u16 i;
+
+       if (p_params->pf_id < MAX_NUM_PFS_BB)
+               crd_reg_offset = QM_REG_WFQPFCRD_RT_OFFSET;
+       else
+               crd_reg_offset = QM_REG_WFQPFCRD_MSB_RT_OFFSET +
+                                (p_params->pf_id % MAX_NUM_PFS_BB);
+
+       inc_val = QM_WFQ_INC_VAL(p_params->pf_wfq);
+       if (inc_val > QM_WFQ_MAX_INC_VAL) {
+               DP_NOTICE(p_hwfn, "Invalid PF WFQ weight configuration");
+               return -1;
+       }
+       STORE_RT_REG(p_hwfn, QM_REG_WFQPFWEIGHT_RT_OFFSET + p_params->pf_id,
+                    inc_val);
+       STORE_RT_REG(p_hwfn,
+                    QM_REG_WFQPFUPPERBOUND_RT_OFFSET + p_params->pf_id,
+                    QM_WFQ_UPPER_BOUND | QM_WFQ_CRD_REG_SIGN_BIT);
+
+       for (i = 0; i < num_tx_pqs; i++) {
+               u8 voq = VOQ(p_params->port_id, p_params->pq_params[i].tc_id,
+                            p_params->max_phys_tcs_per_port);
+
+               OVERWRITE_RT_REG(p_hwfn,
+                                crd_reg_offset + voq * MAX_NUM_PFS_BB,
+                                QM_WFQ_INIT_CRD(inc_val) |
+                                QM_WFQ_CRD_REG_SIGN_BIT);
+       }
+
+       return 0;
+}
+
+/* Prepare PF RL runtime init values for the specified PF.
+ * Return -1 on error.
+ */
+static int qed_pf_rl_rt_init(struct qed_hwfn *p_hwfn,
+                            u8 pf_id,
+                            u32 pf_rl)
+{
+       u32 inc_val = QM_RL_INC_VAL(pf_rl);
+
+       if (inc_val > QM_RL_MAX_INC_VAL) {
+               DP_NOTICE(p_hwfn, "Invalid PF rate limit configuration");
+               return -1;
+       }
+       STORE_RT_REG(p_hwfn, QM_REG_RLPFCRD_RT_OFFSET + pf_id,
+                    QM_RL_CRD_REG_SIGN_BIT);
+       STORE_RT_REG(p_hwfn, QM_REG_RLPFUPPERBOUND_RT_OFFSET + pf_id,
+                    QM_RL_UPPER_BOUND | QM_RL_CRD_REG_SIGN_BIT);
+       STORE_RT_REG(p_hwfn, QM_REG_RLPFINCVAL_RT_OFFSET + pf_id, inc_val);
+       return 0;
+}
+
+/* Prepare VPORT WFQ runtime init values for the specified VPORTs.
+ * Return -1 on error.
+ */
+static int qed_vp_wfq_rt_init(struct qed_hwfn *p_hwfn,
+                             u8 start_vport,
+                             u8 num_vports,
+                             struct init_qm_vport_params *vport_params)
+{
+       u8 tc, i, vport_id;
+       u32 inc_val;
+
+       /* go over all PF VPORTs */
+       for (i = 0, vport_id = start_vport; i < num_vports; i++, vport_id++) {
+               u32 temp = QM_REG_WFQVPUPPERBOUND_RT_OFFSET;
+               u16 *pq_ids = &vport_params[i].first_tx_pq_id[0];
+
+               if (!vport_params[i].vport_wfq)
+                       continue;
+
+               inc_val = QM_WFQ_INC_VAL(vport_params[i].vport_wfq);
+               if (inc_val > QM_WFQ_MAX_INC_VAL) {
+                       DP_NOTICE(p_hwfn,
+                                 "Invalid VPORT WFQ weight configuration");
+                       return -1;
+               }
+
+               /* each VPORT can have several VPORT PQ IDs for
+                * different TCs
+                */
+               for (tc = 0; tc < NUM_OF_TCS; tc++) {
+                       u16 vport_pq_id = pq_ids[tc];
+
+                       if (vport_pq_id != QM_INVALID_PQ_ID) {
+                               STORE_RT_REG(p_hwfn,
+                                            QM_REG_WFQVPWEIGHT_RT_OFFSET +
+                                            vport_pq_id, inc_val);
+                               STORE_RT_REG(p_hwfn, temp + vport_pq_id,
+                                            QM_WFQ_UPPER_BOUND |
+                                            QM_WFQ_CRD_REG_SIGN_BIT);
+                               STORE_RT_REG(p_hwfn,
+                                            QM_REG_WFQVPCRD_RT_OFFSET +
+                                            vport_pq_id,
+                                            QM_WFQ_INIT_CRD(inc_val) |
+                                            QM_WFQ_CRD_REG_SIGN_BIT);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int qed_vport_rl_rt_init(struct qed_hwfn *p_hwfn,
+                               u8 start_vport,
+                               u8 num_vports,
+                               struct init_qm_vport_params *vport_params)
+{
+       u8 i, vport_id;
+
+       /* go over all PF VPORTs */
+       for (i = 0, vport_id = start_vport; i < num_vports; i++, vport_id++) {
+               u32 inc_val = QM_RL_INC_VAL(vport_params[i].vport_rl);
+
+               if (inc_val > QM_RL_MAX_INC_VAL) {
+                       DP_NOTICE(p_hwfn,
+                                 "Invalid VPORT rate-limit configuration");
+                       return -1;
+               }
+
+               STORE_RT_REG(p_hwfn,
+                            QM_REG_RLGLBLCRD_RT_OFFSET + vport_id,
+                            QM_RL_CRD_REG_SIGN_BIT);
+               STORE_RT_REG(p_hwfn,
+                            QM_REG_RLGLBLUPPERBOUND_RT_OFFSET + vport_id,
+                            QM_RL_UPPER_BOUND | QM_RL_CRD_REG_SIGN_BIT);
+               STORE_RT_REG(p_hwfn,
+                            QM_REG_RLGLBLINCVAL_RT_OFFSET + vport_id,
+                            inc_val);
+       }
+
+       return 0;
+}
+
+static bool qed_poll_on_qm_cmd_ready(struct qed_hwfn *p_hwfn,
+                                    struct qed_ptt *p_ptt)
+{
+       u32 reg_val, i;
+
+       for (i = 0, reg_val = 0; i < QM_STOP_CMD_MAX_POLL_COUNT && reg_val == 0;
+            i++) {
+               udelay(QM_STOP_CMD_POLL_PERIOD_US);
+               reg_val = qed_rd(p_hwfn, p_ptt, QM_REG_SDMCMDREADY);
+       }
+
+       /* check if timeout while waiting for SDM command ready */
+       if (i == QM_STOP_CMD_MAX_POLL_COUNT) {
+               DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
+                          "Timeout when waiting for QM SDM command ready signal\n");
+               return false;
+       }
+
+       return true;
+}
+
+static bool qed_send_qm_cmd(struct qed_hwfn *p_hwfn,
+                           struct qed_ptt *p_ptt,
+                           u32 cmd_addr,
+                           u32 cmd_data_lsb,
+                           u32 cmd_data_msb)
+{
+       if (!qed_poll_on_qm_cmd_ready(p_hwfn, p_ptt))
+               return false;
+
+       qed_wr(p_hwfn, p_ptt, QM_REG_SDMCMDADDR, cmd_addr);
+       qed_wr(p_hwfn, p_ptt, QM_REG_SDMCMDDATALSB, cmd_data_lsb);
+       qed_wr(p_hwfn, p_ptt, QM_REG_SDMCMDDATAMSB, cmd_data_msb);
+       qed_wr(p_hwfn, p_ptt, QM_REG_SDMCMDGO, 1);
+       qed_wr(p_hwfn, p_ptt, QM_REG_SDMCMDGO, 0);
+
+       return qed_poll_on_qm_cmd_ready(p_hwfn, p_ptt);
+}
+
+/******************** INTERFACE IMPLEMENTATION *********************/
+u32 qed_qm_pf_mem_size(u8 pf_id,
+                      u32 num_pf_cids,
+                      u32 num_vf_cids,
+                      u32 num_tids,
+                      u16 num_pf_pqs,
+                      u16 num_vf_pqs)
+{
+       return QM_PQ_MEM_4KB(num_pf_cids) * num_pf_pqs +
+              QM_PQ_MEM_4KB(num_vf_cids) * num_vf_pqs +
+              QM_PQ_MEM_4KB(num_pf_cids + num_tids) * QM_OTHER_PQS_PER_PF;
+}
+
+int qed_qm_common_rt_init(
+       struct qed_hwfn *p_hwfn,
+       struct qed_qm_common_rt_init_params *p_params)
+{
+       /* init AFullOprtnstcCrdMask */
+       u32 mask = (QM_OPPOR_LINE_VOQ_DEF <<
+                   QM_RF_OPPORTUNISTIC_MASK_LINEVOQ_SHIFT) |
+                  (QM_BYTE_CRD_EN << QM_RF_OPPORTUNISTIC_MASK_BYTEVOQ_SHIFT) |
+                  (p_params->pf_wfq_en <<
+                   QM_RF_OPPORTUNISTIC_MASK_PFWFQ_SHIFT) |
+                  (p_params->vport_wfq_en <<
+                   QM_RF_OPPORTUNISTIC_MASK_VPWFQ_SHIFT) |
+                  (p_params->pf_rl_en <<
+                   QM_RF_OPPORTUNISTIC_MASK_PFRL_SHIFT) |
+                  (p_params->vport_rl_en <<
+                   QM_RF_OPPORTUNISTIC_MASK_VPQCNRL_SHIFT) |
+                  (QM_OPPOR_FW_STOP_DEF <<
+                   QM_RF_OPPORTUNISTIC_MASK_FWPAUSE_SHIFT) |
+                  (QM_OPPOR_PQ_EMPTY_DEF <<
+                   QM_RF_OPPORTUNISTIC_MASK_QUEUEEMPTY_SHIFT);
+
+       STORE_RT_REG(p_hwfn, QM_REG_AFULLOPRTNSTCCRDMASK_RT_OFFSET, mask);
+       qed_enable_pf_rl(p_hwfn, p_params->pf_rl_en);
+       qed_enable_pf_wfq(p_hwfn, p_params->pf_wfq_en);
+       qed_enable_vport_rl(p_hwfn, p_params->vport_rl_en);
+       qed_enable_vport_wfq(p_hwfn, p_params->vport_wfq_en);
+       qed_cmdq_lines_rt_init(p_hwfn,
+                              p_params->max_ports_per_engine,
+                              p_params->max_phys_tcs_per_port,
+                              p_params->port_params);
+       qed_btb_blocks_rt_init(p_hwfn,
+                              p_params->max_ports_per_engine,
+                              p_params->max_phys_tcs_per_port,
+                              p_params->port_params);
+       return 0;
+}
+
+int qed_qm_pf_rt_init(struct qed_hwfn *p_hwfn,
+                     struct qed_ptt *p_ptt,
+                     struct qed_qm_pf_rt_init_params *p_params)
+{
+       struct init_qm_vport_params *vport_params = p_params->vport_params;
+       u32 other_mem_size_4kb = QM_PQ_MEM_4KB(p_params->num_pf_cids +
+                                              p_params->num_tids) *
+                                QM_OTHER_PQS_PER_PF;
+       u8 tc, i;
+
+       /* clear first Tx PQ ID array for each VPORT */
+       for (i = 0; i < p_params->num_vports; i++)
+               for (tc = 0; tc < NUM_OF_TCS; tc++)
+                       vport_params[i].first_tx_pq_id[tc] = QM_INVALID_PQ_ID;
+
+       /* map Other PQs (if any) */
+       qed_other_pq_map_rt_init(p_hwfn, p_params->port_id, p_params->pf_id,
+                                p_params->num_pf_cids, p_params->num_tids, 0);
+
+       /* map Tx PQs */
+       qed_tx_pq_map_rt_init(p_hwfn, p_ptt, p_params, other_mem_size_4kb);
+
+       if (p_params->pf_wfq)
+               if (qed_pf_wfq_rt_init(p_hwfn, p_params))
+                       return -1;
+
+       if (qed_pf_rl_rt_init(p_hwfn, p_params->pf_id, p_params->pf_rl))
+               return -1;
+
+       if (qed_vp_wfq_rt_init(p_hwfn, p_params->start_vport,
+                              p_params->num_vports, vport_params))
+               return -1;
+
+       if (qed_vport_rl_rt_init(p_hwfn, p_params->start_vport,
+                                p_params->num_vports, vport_params))
+               return -1;
+
+       return 0;
+}
+
+int qed_init_pf_rl(struct qed_hwfn *p_hwfn,
+                  struct qed_ptt *p_ptt,
+                  u8 pf_id,
+                  u32 pf_rl)
+{
+       u32 inc_val = QM_RL_INC_VAL(pf_rl);
+
+       if (inc_val > QM_RL_MAX_INC_VAL) {
+               DP_NOTICE(p_hwfn, "Invalid PF rate limit configuration");
+               return -1;
+       }
+
+       qed_wr(p_hwfn, p_ptt,
+              QM_REG_RLPFCRD + pf_id * 4,
+              QM_RL_CRD_REG_SIGN_BIT);
+       qed_wr(p_hwfn, p_ptt, QM_REG_RLPFINCVAL + pf_id * 4, inc_val);
+
+       return 0;
+}
+
+int qed_init_vport_rl(struct qed_hwfn *p_hwfn,
+                     struct qed_ptt *p_ptt,
+                     u8 vport_id,
+                     u32 vport_rl)
+{
+       u32 inc_val = QM_RL_INC_VAL(vport_rl);
+
+       if (inc_val > QM_RL_MAX_INC_VAL) {
+               DP_NOTICE(p_hwfn, "Invalid VPORT rate-limit configuration");
+               return -1;
+       }
+
+       qed_wr(p_hwfn, p_ptt,
+              QM_REG_RLGLBLCRD + vport_id * 4,
+              QM_RL_CRD_REG_SIGN_BIT);
+       qed_wr(p_hwfn, p_ptt, QM_REG_RLGLBLINCVAL + vport_id * 4, inc_val);
+
+       return 0;
+}
+
+bool qed_send_qm_stop_cmd(struct qed_hwfn *p_hwfn,
+                         struct qed_ptt *p_ptt,
+                         bool is_release_cmd,
+                         bool is_tx_pq,
+                         u16 start_pq,
+                         u16 num_pqs)
+{
+       u32 cmd_arr[QM_CMD_STRUCT_SIZE(QM_STOP_CMD)] = { 0 };
+       u32 pq_mask = 0, last_pq = start_pq + num_pqs - 1, pq_id;
+
+       /* set command's PQ type */
+       QM_CMD_SET_FIELD(cmd_arr, QM_STOP_CMD, PQ_TYPE, is_tx_pq ? 0 : 1);
+
+       for (pq_id = start_pq; pq_id <= last_pq; pq_id++) {
+               /* set PQ bit in mask (stop command only) */
+               if (!is_release_cmd)
+                       pq_mask |= (1 << (pq_id % QM_STOP_PQ_MASK_WIDTH));
+
+               /* if last PQ or end of PQ mask, write command */
+               if ((pq_id == last_pq) ||
+                   (pq_id % QM_STOP_PQ_MASK_WIDTH ==
+                    (QM_STOP_PQ_MASK_WIDTH - 1))) {
+                       QM_CMD_SET_FIELD(cmd_arr, QM_STOP_CMD,
+                                        PAUSE_MASK, pq_mask);
+                       QM_CMD_SET_FIELD(cmd_arr, QM_STOP_CMD,
+                                        GROUP_ID,
+                                        pq_id / QM_STOP_PQ_MASK_WIDTH);
+                       if (!qed_send_qm_cmd(p_hwfn, p_ptt, QM_STOP_CMD_ADDR,
+                                            cmd_arr[0], cmd_arr[1]))
+                               return false;
+                       pq_mask = 0;
+               }
+       }
+
+       return true;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_init_ops.c b/drivers/net/ethernet/qlogic/qed/qed_init_ops.c
new file mode 100644 (file)
index 0000000..796f139
--- /dev/null
@@ -0,0 +1,531 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "qed.h"
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_init_ops.h"
+#include "qed_reg_addr.h"
+
+#define QED_INIT_MAX_POLL_COUNT 100
+#define QED_INIT_POLL_PERIOD_US 500
+
+static u32 pxp_global_win[] = {
+       0,
+       0,
+       0x1c02, /* win 2: addr=0x1c02000, size=4096 bytes */
+       0x1c80, /* win 3: addr=0x1c80000, size=4096 bytes */
+       0x1d00, /* win 4: addr=0x1d00000, size=4096 bytes */
+       0x1d01, /* win 5: addr=0x1d01000, size=4096 bytes */
+       0x1d80, /* win 6: addr=0x1d80000, size=4096 bytes */
+       0x1d81, /* win 7: addr=0x1d81000, size=4096 bytes */
+       0x1d82, /* win 8: addr=0x1d82000, size=4096 bytes */
+       0x1e00, /* win 9: addr=0x1e00000, size=4096 bytes */
+       0x1e80, /* win 10: addr=0x1e80000, size=4096 bytes */
+       0x1f00, /* win 11: addr=0x1f00000, size=4096 bytes */
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+};
+
+void qed_init_iro_array(struct qed_dev *cdev)
+{
+       cdev->iro_arr = iro_arr;
+}
+
+/* Runtime configuration helpers */
+void qed_init_clear_rt_data(struct qed_hwfn *p_hwfn)
+{
+       int i;
+
+       for (i = 0; i < RUNTIME_ARRAY_SIZE; i++)
+               p_hwfn->rt_data[i].b_valid = false;
+}
+
+void qed_init_store_rt_reg(struct qed_hwfn *p_hwfn,
+                          u32 rt_offset,
+                          u32 val)
+{
+       p_hwfn->rt_data[rt_offset].init_val = val;
+       p_hwfn->rt_data[rt_offset].b_valid = true;
+}
+
+void qed_init_store_rt_agg(struct qed_hwfn *p_hwfn,
+                          u32 rt_offset,
+                          u32 *val,
+                          size_t size)
+{
+       size_t i;
+
+       for (i = 0; i < size / sizeof(u32); i++) {
+               p_hwfn->rt_data[rt_offset + i].init_val = val[i];
+               p_hwfn->rt_data[rt_offset + i].b_valid = true;
+       }
+}
+
+static void qed_init_rt(struct qed_hwfn *p_hwfn,
+                       struct qed_ptt *p_ptt,
+                       u32 addr,
+                       u32 rt_offset,
+                       u32 size)
+{
+       struct qed_rt_data *rt_data = p_hwfn->rt_data + rt_offset;
+       u32 i;
+
+       for (i = 0; i < size; i++) {
+               if (!rt_data[i].b_valid)
+                       continue;
+               qed_wr(p_hwfn, p_ptt, addr + (i << 2), rt_data[i].init_val);
+       }
+}
+
+int qed_init_alloc(struct qed_hwfn *p_hwfn)
+{
+       struct qed_rt_data *rt_data;
+
+       rt_data = kzalloc(sizeof(*rt_data) * RUNTIME_ARRAY_SIZE, GFP_ATOMIC);
+       if (!rt_data)
+               return -ENOMEM;
+
+       p_hwfn->rt_data = rt_data;
+
+       return 0;
+}
+
+void qed_init_free(struct qed_hwfn *p_hwfn)
+{
+       kfree(p_hwfn->rt_data);
+       p_hwfn->rt_data = NULL;
+}
+
+static int qed_init_array_dmae(struct qed_hwfn *p_hwfn,
+                              struct qed_ptt *p_ptt,
+                              u32 addr,
+                              u32 dmae_data_offset,
+                              u32 size,
+                              const u32 *buf,
+                              bool b_must_dmae,
+                              bool b_can_dmae)
+{
+       int rc = 0;
+
+       /* Perform DMAE only for lengthy enough sections or for wide-bus */
+       if (!b_can_dmae || (!b_must_dmae && (size < 16))) {
+               const u32 *data = buf + dmae_data_offset;
+               u32 i;
+
+               for (i = 0; i < size; i++)
+                       qed_wr(p_hwfn, p_ptt, addr + (i << 2), data[i]);
+       } else {
+               rc = qed_dmae_host2grc(p_hwfn, p_ptt,
+                                      (uintptr_t)(buf + dmae_data_offset),
+                                      addr, size, 0);
+       }
+
+       return rc;
+}
+
+static int qed_init_fill_dmae(struct qed_hwfn *p_hwfn,
+                             struct qed_ptt *p_ptt,
+                             u32 addr,
+                             u32 fill,
+                             u32 fill_count)
+{
+       static u32 zero_buffer[DMAE_MAX_RW_SIZE];
+
+       memset(zero_buffer, 0, sizeof(u32) * DMAE_MAX_RW_SIZE);
+
+       /* invoke the DMAE virtual/physical buffer API with
+        * 1. DMAE init channel
+        * 2. addr,
+        * 3. p_hwfb->temp_data,
+        * 4. fill_count
+        */
+
+       return qed_dmae_host2grc(p_hwfn, p_ptt,
+                                (uintptr_t)(&zero_buffer[0]),
+                                addr, fill_count,
+                                QED_DMAE_FLAG_RW_REPL_SRC);
+}
+
+static void qed_init_fill(struct qed_hwfn *p_hwfn,
+                         struct qed_ptt *p_ptt,
+                         u32 addr,
+                         u32 fill,
+                         u32 fill_count)
+{
+       u32 i;
+
+       for (i = 0; i < fill_count; i++, addr += sizeof(u32))
+               qed_wr(p_hwfn, p_ptt, addr, fill);
+}
+
+static int qed_init_cmd_array(struct qed_hwfn *p_hwfn,
+                             struct qed_ptt *p_ptt,
+                             struct init_write_op *cmd,
+                             bool b_must_dmae,
+                             bool b_can_dmae)
+{
+       u32 data = le32_to_cpu(cmd->data);
+       u32 addr = GET_FIELD(data, INIT_WRITE_OP_ADDRESS) << 2;
+       u32 dmae_array_offset = le32_to_cpu(cmd->args.array_offset);
+       u32 offset, output_len, input_len, max_size;
+       struct qed_dev *cdev = p_hwfn->cdev;
+       union init_array_hdr *hdr;
+       const u32 *array_data;
+       int rc = 0;
+       u32 size;
+
+       array_data = cdev->fw_data->arr_data;
+
+       hdr = (union init_array_hdr *)(array_data +
+                                      dmae_array_offset);
+       data = le32_to_cpu(hdr->raw.data);
+       switch (GET_FIELD(data, INIT_ARRAY_RAW_HDR_TYPE)) {
+       case INIT_ARR_ZIPPED:
+               offset = dmae_array_offset + 1;
+               input_len = GET_FIELD(data,
+                                     INIT_ARRAY_ZIPPED_HDR_ZIPPED_SIZE);
+               max_size = MAX_ZIPPED_SIZE * 4;
+               memset(p_hwfn->unzip_buf, 0, max_size);
+
+               output_len = qed_unzip_data(p_hwfn, input_len,
+                                           (u8 *)&array_data[offset],
+                                           max_size, (u8 *)p_hwfn->unzip_buf);
+               if (output_len) {
+                       rc = qed_init_array_dmae(p_hwfn, p_ptt, addr, 0,
+                                                output_len,
+                                                p_hwfn->unzip_buf,
+                                                b_must_dmae, b_can_dmae);
+               } else {
+                       DP_NOTICE(p_hwfn, "Failed to unzip dmae data\n");
+                       rc = -EINVAL;
+               }
+               break;
+       case INIT_ARR_PATTERN:
+       {
+               u32 repeats = GET_FIELD(data,
+                                       INIT_ARRAY_PATTERN_HDR_REPETITIONS);
+               u32 i;
+
+               size = GET_FIELD(data, INIT_ARRAY_PATTERN_HDR_PATTERN_SIZE);
+
+               for (i = 0; i < repeats; i++, addr += size << 2) {
+                       rc = qed_init_array_dmae(p_hwfn, p_ptt, addr,
+                                                dmae_array_offset + 1,
+                                                size, array_data,
+                                                b_must_dmae, b_can_dmae);
+                       if (rc)
+                               break;
+               }
+               break;
+       }
+       case INIT_ARR_STANDARD:
+               size = GET_FIELD(data, INIT_ARRAY_STANDARD_HDR_SIZE);
+               rc = qed_init_array_dmae(p_hwfn, p_ptt, addr,
+                                        dmae_array_offset + 1,
+                                        size, array_data,
+                                        b_must_dmae, b_can_dmae);
+               break;
+       }
+
+       return rc;
+}
+
+/* init_ops write command */
+static int qed_init_cmd_wr(struct qed_hwfn *p_hwfn,
+                          struct qed_ptt *p_ptt,
+                          struct init_write_op *cmd,
+                          bool b_can_dmae)
+{
+       u32 data = le32_to_cpu(cmd->data);
+       u32 addr = GET_FIELD(data, INIT_WRITE_OP_ADDRESS) << 2;
+       bool b_must_dmae = GET_FIELD(data, INIT_WRITE_OP_WIDE_BUS);
+       union init_write_args *arg = &cmd->args;
+       int rc = 0;
+
+       /* Sanitize */
+       if (b_must_dmae && !b_can_dmae) {
+               DP_NOTICE(p_hwfn,
+                         "Need to write to %08x for Wide-bus but DMAE isn't allowed\n",
+                         addr);
+               return -EINVAL;
+       }
+
+       switch (GET_FIELD(data, INIT_WRITE_OP_SOURCE)) {
+       case INIT_SRC_INLINE:
+               qed_wr(p_hwfn, p_ptt, addr,
+                      le32_to_cpu(arg->inline_val));
+               break;
+       case INIT_SRC_ZEROS:
+               if (b_must_dmae ||
+                   (b_can_dmae && (le32_to_cpu(arg->zeros_count) >= 64)))
+                       rc = qed_init_fill_dmae(p_hwfn, p_ptt, addr, 0,
+                                               le32_to_cpu(arg->zeros_count));
+               else
+                       qed_init_fill(p_hwfn, p_ptt, addr, 0,
+                                     le32_to_cpu(arg->zeros_count));
+               break;
+       case INIT_SRC_ARRAY:
+               rc = qed_init_cmd_array(p_hwfn, p_ptt, cmd,
+                                       b_must_dmae, b_can_dmae);
+               break;
+       case INIT_SRC_RUNTIME:
+               qed_init_rt(p_hwfn, p_ptt, addr,
+                           le16_to_cpu(arg->runtime.offset),
+                           le16_to_cpu(arg->runtime.size));
+               break;
+       }
+
+       return rc;
+}
+
+static inline bool comp_eq(u32 val, u32 expected_val)
+{
+       return val == expected_val;
+}
+
+static inline bool comp_and(u32 val, u32 expected_val)
+{
+       return (val & expected_val) == expected_val;
+}
+
+static inline bool comp_or(u32 val, u32 expected_val)
+{
+       return (val | expected_val) > 0;
+}
+
+/* init_ops read/poll commands */
+static void qed_init_cmd_rd(struct qed_hwfn *p_hwfn,
+                           struct qed_ptt *p_ptt,
+                           struct init_read_op *cmd)
+{
+       u32 data = le32_to_cpu(cmd->op_data);
+       u32 addr = GET_FIELD(data, INIT_READ_OP_ADDRESS) << 2;
+
+       bool    (*comp_check)(u32       val,
+                             u32       expected_val);
+       u32     delay = QED_INIT_POLL_PERIOD_US, val;
+
+       val = qed_rd(p_hwfn, p_ptt, addr);
+
+       data = le32_to_cpu(cmd->op_data);
+       if (GET_FIELD(data, INIT_READ_OP_POLL)) {
+               int i;
+
+               switch (GET_FIELD(data, INIT_READ_OP_POLL_COMP)) {
+               case INIT_COMPARISON_EQ:
+                       comp_check = comp_eq;
+                       break;
+               case INIT_COMPARISON_OR:
+                       comp_check = comp_or;
+                       break;
+               case INIT_COMPARISON_AND:
+                       comp_check = comp_and;
+                       break;
+               default:
+                       comp_check = NULL;
+                       DP_ERR(p_hwfn, "Invalid poll comparison type %08x\n",
+                              data);
+                       return;
+               }
+
+               for (i = 0;
+                    i < QED_INIT_MAX_POLL_COUNT &&
+                    !comp_check(val, le32_to_cpu(cmd->expected_val));
+                    i++) {
+                       udelay(delay);
+                       val = qed_rd(p_hwfn, p_ptt, addr);
+               }
+
+               if (i == QED_INIT_MAX_POLL_COUNT)
+                       DP_ERR(p_hwfn,
+                              "Timeout when polling reg: 0x%08x [ Waiting-for: %08x Got: %08x (comparsion %08x)]\n",
+                              addr, le32_to_cpu(cmd->expected_val),
+                              val, data);
+       }
+}
+
+/* init_ops callbacks entry point */
+static void qed_init_cmd_cb(struct qed_hwfn *p_hwfn,
+                           struct qed_ptt *p_ptt,
+                           struct init_callback_op *p_cmd)
+{
+       DP_NOTICE(p_hwfn, "Currently init values have no need of callbacks\n");
+}
+
+static u8 qed_init_cmd_mode_match(struct qed_hwfn *p_hwfn,
+                                 u16 *offset,
+                                 int modes)
+{
+       struct qed_dev *cdev = p_hwfn->cdev;
+       const u8 *modes_tree_buf;
+       u8 arg1, arg2, tree_val;
+
+       modes_tree_buf = cdev->fw_data->modes_tree_buf;
+       tree_val = modes_tree_buf[(*offset)++];
+       switch (tree_val) {
+       case INIT_MODE_OP_NOT:
+               return qed_init_cmd_mode_match(p_hwfn, offset, modes) ^ 1;
+       case INIT_MODE_OP_OR:
+               arg1    = qed_init_cmd_mode_match(p_hwfn, offset, modes);
+               arg2    = qed_init_cmd_mode_match(p_hwfn, offset, modes);
+               return arg1 | arg2;
+       case INIT_MODE_OP_AND:
+               arg1    = qed_init_cmd_mode_match(p_hwfn, offset, modes);
+               arg2    = qed_init_cmd_mode_match(p_hwfn, offset, modes);
+               return arg1 & arg2;
+       default:
+               tree_val -= MAX_INIT_MODE_OPS;
+               return (modes & (1 << tree_val)) ? 1 : 0;
+       }
+}
+
+static u32 qed_init_cmd_mode(struct qed_hwfn *p_hwfn,
+                            struct init_if_mode_op *p_cmd,
+                            int modes)
+{
+       u16 offset = le16_to_cpu(p_cmd->modes_buf_offset);
+
+       if (qed_init_cmd_mode_match(p_hwfn, &offset, modes))
+               return 0;
+       else
+               return GET_FIELD(le32_to_cpu(p_cmd->op_data),
+                                INIT_IF_MODE_OP_CMD_OFFSET);
+}
+
+static u32 qed_init_cmd_phase(struct qed_hwfn *p_hwfn,
+                             struct init_if_phase_op *p_cmd,
+                             u32 phase,
+                             u32 phase_id)
+{
+       u32 data = le32_to_cpu(p_cmd->phase_data);
+       u32 op_data = le32_to_cpu(p_cmd->op_data);
+
+       if (!(GET_FIELD(data, INIT_IF_PHASE_OP_PHASE) == phase &&
+             (GET_FIELD(data, INIT_IF_PHASE_OP_PHASE_ID) == ANY_PHASE_ID ||
+              GET_FIELD(data, INIT_IF_PHASE_OP_PHASE_ID) == phase_id)))
+               return GET_FIELD(op_data, INIT_IF_PHASE_OP_CMD_OFFSET);
+       else
+               return 0;
+}
+
+int qed_init_run(struct qed_hwfn *p_hwfn,
+                struct qed_ptt *p_ptt,
+                int phase,
+                int phase_id,
+                int modes)
+{
+       struct qed_dev *cdev = p_hwfn->cdev;
+       u32 cmd_num, num_init_ops;
+       union init_op *init_ops;
+       bool b_dmae = false;
+       int rc = 0;
+
+       num_init_ops = cdev->fw_data->init_ops_size;
+       init_ops = cdev->fw_data->init_ops;
+
+       p_hwfn->unzip_buf = kzalloc(MAX_ZIPPED_SIZE * 4, GFP_ATOMIC);
+       if (!p_hwfn->unzip_buf) {
+               DP_NOTICE(p_hwfn, "Failed to allocate unzip buffer\n");
+               return -ENOMEM;
+       }
+
+       for (cmd_num = 0; cmd_num < num_init_ops; cmd_num++) {
+               union init_op *cmd = &init_ops[cmd_num];
+               u32 data = le32_to_cpu(cmd->raw.op_data);
+
+               switch (GET_FIELD(data, INIT_CALLBACK_OP_OP)) {
+               case INIT_OP_WRITE:
+                       rc = qed_init_cmd_wr(p_hwfn, p_ptt, &cmd->write,
+                                            b_dmae);
+                       break;
+               case INIT_OP_READ:
+                       qed_init_cmd_rd(p_hwfn, p_ptt, &cmd->read);
+                       break;
+               case INIT_OP_IF_MODE:
+                       cmd_num += qed_init_cmd_mode(p_hwfn, &cmd->if_mode,
+                                                    modes);
+                       break;
+               case INIT_OP_IF_PHASE:
+                       cmd_num += qed_init_cmd_phase(p_hwfn, &cmd->if_phase,
+                                                     phase, phase_id);
+                       b_dmae = GET_FIELD(data, INIT_IF_PHASE_OP_DMAE_ENABLE);
+                       break;
+               case INIT_OP_DELAY:
+                       /* qed_init_run is always invoked from
+                        * sleep-able context
+                        */
+                       udelay(le32_to_cpu(cmd->delay.delay));
+                       break;
+
+               case INIT_OP_CALLBACK:
+                       qed_init_cmd_cb(p_hwfn, p_ptt, &cmd->callback);
+                       break;
+               }
+
+               if (rc)
+                       break;
+       }
+
+       kfree(p_hwfn->unzip_buf);
+       return rc;
+}
+
+void qed_gtt_init(struct qed_hwfn *p_hwfn)
+{
+       u32 gtt_base;
+       u32 i;
+
+       /* Set the global windows */
+       gtt_base = PXP_PF_WINDOW_ADMIN_START + PXP_PF_WINDOW_ADMIN_GLOBAL_START;
+
+       for (i = 0; i < ARRAY_SIZE(pxp_global_win); i++)
+               if (pxp_global_win[i])
+                       REG_WR(p_hwfn, gtt_base + i * PXP_GLOBAL_ENTRY_SIZE,
+                              pxp_global_win[i]);
+}
+
+int qed_init_fw_data(struct qed_dev *cdev,
+                    const u8 *data)
+{
+       struct qed_fw_data *fw = cdev->fw_data;
+       struct bin_buffer_hdr *buf_hdr;
+       u32 offset, len;
+
+       if (!data) {
+               DP_NOTICE(cdev, "Invalid fw data\n");
+               return -EINVAL;
+       }
+
+       buf_hdr = (struct bin_buffer_hdr *)data;
+
+       offset = buf_hdr[BIN_BUF_INIT_CMD].offset;
+       fw->init_ops = (union init_op *)(data + offset);
+
+       offset = buf_hdr[BIN_BUF_INIT_VAL].offset;
+       fw->arr_data = (u32 *)(data + offset);
+
+       offset = buf_hdr[BIN_BUF_INIT_MODE_TREE].offset;
+       fw->modes_tree_buf = (u8 *)(data + offset);
+       len = buf_hdr[BIN_BUF_INIT_CMD].length;
+       fw->init_ops_size = len / sizeof(struct init_raw_op);
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_init_ops.h b/drivers/net/ethernet/qlogic/qed/qed_init_ops.h
new file mode 100644 (file)
index 0000000..1e83204
--- /dev/null
@@ -0,0 +1,110 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_INIT_OPS_H
+#define _QED_INIT_OPS_H
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include "qed.h"
+
+/**
+ * @brief qed_init_iro_array - init iro_arr.
+ *
+ *
+ * @param cdev
+ */
+void qed_init_iro_array(struct qed_dev *cdev);
+
+/**
+ * @brief qed_init_run - Run the init-sequence.
+ *
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param phase
+ * @param phase_id
+ * @param modes
+ * @return _qed_status_t
+ */
+int qed_init_run(struct qed_hwfn *p_hwfn,
+                struct qed_ptt *p_ptt,
+                int phase,
+                int phase_id,
+                int modes);
+
+/**
+ * @brief qed_init_hwfn_allocate - Allocate RT array, Store 'values' ptrs.
+ *
+ *
+ * @param p_hwfn
+ *
+ * @return _qed_status_t
+ */
+int qed_init_alloc(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_init_hwfn_deallocate
+ *
+ *
+ * @param p_hwfn
+ */
+void qed_init_free(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_init_clear_rt_data - Clears the runtime init array.
+ *
+ *
+ * @param p_hwfn
+ */
+void qed_init_clear_rt_data(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_init_store_rt_reg - Store a configuration value in the RT array.
+ *
+ *
+ * @param p_hwfn
+ * @param rt_offset
+ * @param val
+ */
+void qed_init_store_rt_reg(struct qed_hwfn *p_hwfn,
+                          u32 rt_offset,
+                          u32 val);
+
+#define STORE_RT_REG(hwfn, offset, val)        \
+       qed_init_store_rt_reg(hwfn, offset, val)
+
+#define OVERWRITE_RT_REG(hwfn, offset, val) \
+       qed_init_store_rt_reg(hwfn, offset, val)
+
+/**
+ * @brief
+ *
+ *
+ * @param p_hwfn
+ * @param rt_offset
+ * @param val
+ * @param size
+ */
+void qed_init_store_rt_agg(struct qed_hwfn *p_hwfn,
+                          u32 rt_offset,
+                          u32 *val,
+                          size_t size);
+
+#define STORE_RT_REG_AGG(hwfn, offset, val) \
+       qed_init_store_rt_agg(hwfn, offset, (u32 *)&val, sizeof(val))
+
+/**
+ * @brief
+ *      Initialize GTT global windows and set admin window
+ *      related params of GTT/PTT to default values.
+ *
+ * @param p_hwfn
+ */
+void qed_gtt_init(struct qed_hwfn *p_hwfn);
+#endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.c b/drivers/net/ethernet/qlogic/qed/qed_int.c
new file mode 100644 (file)
index 0000000..2e399b6
--- /dev/null
@@ -0,0 +1,1134 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "qed.h"
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_init_ops.h"
+#include "qed_int.h"
+#include "qed_mcp.h"
+#include "qed_reg_addr.h"
+#include "qed_sp.h"
+
+struct qed_pi_info {
+       qed_int_comp_cb_t       comp_cb;
+       void                    *cookie;
+};
+
+struct qed_sb_sp_info {
+       struct qed_sb_info      sb_info;
+
+       /* per protocol index data */
+       struct qed_pi_info      pi_info_arr[PIS_PER_SB];
+};
+
+#define SB_ATTN_ALIGNED_SIZE(p_hwfn) \
+       ALIGNED_TYPE_SIZE(struct atten_status_block, p_hwfn)
+
+#define ATTN_STATE_BITS (0xfff)
+#define ATTN_BITS_MASKABLE      (0x3ff)
+struct qed_sb_attn_info {
+       /* Virtual & Physical address of the SB */
+       struct atten_status_block       *sb_attn;
+       dma_addr_t                    sb_phys;
+
+       /* Last seen running index */
+       u16                          index;
+
+       /* Previously asserted attentions, which are still unasserted */
+       u16                          known_attn;
+
+       /* Cleanup address for the link's general hw attention */
+       u32                          mfw_attn_addr;
+};
+
+static inline u16 qed_attn_update_idx(struct qed_hwfn *p_hwfn,
+                                     struct qed_sb_attn_info   *p_sb_desc)
+{
+       u16     rc = 0;
+       u16     index;
+
+       /* Make certain HW write took affect */
+       mmiowb();
+
+       index = le16_to_cpu(p_sb_desc->sb_attn->sb_index);
+       if (p_sb_desc->index != index) {
+               p_sb_desc->index        = index;
+               rc                    = QED_SB_ATT_IDX;
+       }
+
+       /* Make certain we got a consistent view with HW */
+       mmiowb();
+
+       return rc;
+}
+
+/**
+ *  @brief qed_int_assertion - handles asserted attention bits
+ *
+ *  @param p_hwfn
+ *  @param asserted_bits newly asserted bits
+ *  @return int
+ */
+static int qed_int_assertion(struct qed_hwfn *p_hwfn,
+                            u16 asserted_bits)
+{
+       struct qed_sb_attn_info *sb_attn_sw = p_hwfn->p_sb_attn;
+       u32 igu_mask;
+
+       /* Mask the source of the attention in the IGU */
+       igu_mask = qed_rd(p_hwfn, p_hwfn->p_dpc_ptt,
+                         IGU_REG_ATTENTION_ENABLE);
+       DP_VERBOSE(p_hwfn, NETIF_MSG_INTR, "IGU mask: 0x%08x --> 0x%08x\n",
+                  igu_mask, igu_mask & ~(asserted_bits & ATTN_BITS_MASKABLE));
+       igu_mask &= ~(asserted_bits & ATTN_BITS_MASKABLE);
+       qed_wr(p_hwfn, p_hwfn->p_dpc_ptt, IGU_REG_ATTENTION_ENABLE, igu_mask);
+
+       DP_VERBOSE(p_hwfn, NETIF_MSG_INTR,
+                  "inner known ATTN state: 0x%04x --> 0x%04x\n",
+                  sb_attn_sw->known_attn,
+                  sb_attn_sw->known_attn | asserted_bits);
+       sb_attn_sw->known_attn |= asserted_bits;
+
+       /* Handle MCP events */
+       if (asserted_bits & 0x100) {
+               qed_mcp_handle_events(p_hwfn, p_hwfn->p_dpc_ptt);
+               /* Clean the MCP attention */
+               qed_wr(p_hwfn, p_hwfn->p_dpc_ptt,
+                      sb_attn_sw->mfw_attn_addr, 0);
+       }
+
+       DIRECT_REG_WR((u8 __iomem *)p_hwfn->regview +
+                     GTT_BAR0_MAP_REG_IGU_CMD +
+                     ((IGU_CMD_ATTN_BIT_SET_UPPER -
+                       IGU_CMD_INT_ACK_BASE) << 3),
+                     (u32)asserted_bits);
+
+       DP_VERBOSE(p_hwfn, NETIF_MSG_INTR, "set cmd IGU: 0x%04x\n",
+                  asserted_bits);
+
+       return 0;
+}
+
+/**
+ * @brief - handles deassertion of previously asserted attentions.
+ *
+ * @param p_hwfn
+ * @param deasserted_bits - newly deasserted bits
+ * @return int
+ *
+ */
+static int qed_int_deassertion(struct qed_hwfn  *p_hwfn,
+                              u16 deasserted_bits)
+{
+       struct qed_sb_attn_info *sb_attn_sw = p_hwfn->p_sb_attn;
+       u32 aeu_mask;
+
+       if (deasserted_bits != 0x100)
+               DP_ERR(p_hwfn, "Unexpected - non-link deassertion\n");
+
+       /* Clear IGU indication for the deasserted bits */
+       DIRECT_REG_WR((u8 __iomem *)p_hwfn->regview +
+                     GTT_BAR0_MAP_REG_IGU_CMD +
+                     ((IGU_CMD_ATTN_BIT_CLR_UPPER -
+                       IGU_CMD_INT_ACK_BASE) << 3),
+                     ~((u32)deasserted_bits));
+
+       /* Unmask deasserted attentions in IGU */
+       aeu_mask = qed_rd(p_hwfn, p_hwfn->p_dpc_ptt,
+                         IGU_REG_ATTENTION_ENABLE);
+       aeu_mask |= (deasserted_bits & ATTN_BITS_MASKABLE);
+       qed_wr(p_hwfn, p_hwfn->p_dpc_ptt, IGU_REG_ATTENTION_ENABLE, aeu_mask);
+
+       /* Clear deassertion from inner state */
+       sb_attn_sw->known_attn &= ~deasserted_bits;
+
+       return 0;
+}
+
+static int qed_int_attentions(struct qed_hwfn *p_hwfn)
+{
+       struct qed_sb_attn_info *p_sb_attn_sw = p_hwfn->p_sb_attn;
+       struct atten_status_block *p_sb_attn = p_sb_attn_sw->sb_attn;
+       u32 attn_bits = 0, attn_acks = 0;
+       u16 asserted_bits, deasserted_bits;
+       __le16 index;
+       int rc = 0;
+
+       /* Read current attention bits/acks - safeguard against attentions
+        * by guaranting work on a synchronized timeframe
+        */
+       do {
+               index = p_sb_attn->sb_index;
+               attn_bits = le32_to_cpu(p_sb_attn->atten_bits);
+               attn_acks = le32_to_cpu(p_sb_attn->atten_ack);
+       } while (index != p_sb_attn->sb_index);
+       p_sb_attn->sb_index = index;
+
+       /* Attention / Deassertion are meaningful (and in correct state)
+        * only when they differ and consistent with known state - deassertion
+        * when previous attention & current ack, and assertion when current
+        * attention with no previous attention
+        */
+       asserted_bits = (attn_bits & ~attn_acks & ATTN_STATE_BITS) &
+               ~p_sb_attn_sw->known_attn;
+       deasserted_bits = (~attn_bits & attn_acks & ATTN_STATE_BITS) &
+               p_sb_attn_sw->known_attn;
+
+       if ((asserted_bits & ~0x100) || (deasserted_bits & ~0x100)) {
+               DP_INFO(p_hwfn,
+                       "Attention: Index: 0x%04x, Bits: 0x%08x, Acks: 0x%08x, asserted: 0x%04x, De-asserted 0x%04x [Prev. known: 0x%04x]\n",
+                       index, attn_bits, attn_acks, asserted_bits,
+                       deasserted_bits, p_sb_attn_sw->known_attn);
+       } else if (asserted_bits == 0x100) {
+               DP_INFO(p_hwfn,
+                       "MFW indication via attention\n");
+       } else {
+               DP_VERBOSE(p_hwfn, NETIF_MSG_INTR,
+                          "MFW indication [deassertion]\n");
+       }
+
+       if (asserted_bits) {
+               rc = qed_int_assertion(p_hwfn, asserted_bits);
+               if (rc)
+                       return rc;
+       }
+
+       if (deasserted_bits) {
+               rc = qed_int_deassertion(p_hwfn, deasserted_bits);
+               if (rc)
+                       return rc;
+       }
+
+       return rc;
+}
+
+static void qed_sb_ack_attn(struct qed_hwfn *p_hwfn,
+                           void __iomem *igu_addr,
+                           u32 ack_cons)
+{
+       struct igu_prod_cons_update igu_ack = { 0 };
+
+       igu_ack.sb_id_and_flags =
+               ((ack_cons << IGU_PROD_CONS_UPDATE_SB_INDEX_SHIFT) |
+                (1 << IGU_PROD_CONS_UPDATE_UPDATE_FLAG_SHIFT) |
+                (IGU_INT_NOP << IGU_PROD_CONS_UPDATE_ENABLE_INT_SHIFT) |
+                (IGU_SEG_ACCESS_ATTN <<
+                 IGU_PROD_CONS_UPDATE_SEGMENT_ACCESS_SHIFT));
+
+       DIRECT_REG_WR(igu_addr, igu_ack.sb_id_and_flags);
+
+       /* Both segments (interrupts & acks) are written to same place address;
+        * Need to guarantee all commands will be received (in-order) by HW.
+        */
+       mmiowb();
+       barrier();
+}
+
+void qed_int_sp_dpc(unsigned long hwfn_cookie)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)hwfn_cookie;
+       struct qed_pi_info *pi_info = NULL;
+       struct qed_sb_attn_info *sb_attn;
+       struct qed_sb_info *sb_info;
+       int arr_size;
+       u16 rc = 0;
+
+       if (!p_hwfn) {
+               DP_ERR(p_hwfn->cdev, "DPC called - no hwfn!\n");
+               return;
+       }
+
+       if (!p_hwfn->p_sp_sb) {
+               DP_ERR(p_hwfn->cdev, "DPC called - no p_sp_sb\n");
+               return;
+       }
+
+       sb_info = &p_hwfn->p_sp_sb->sb_info;
+       arr_size = ARRAY_SIZE(p_hwfn->p_sp_sb->pi_info_arr);
+       if (!sb_info) {
+               DP_ERR(p_hwfn->cdev,
+                      "Status block is NULL - cannot ack interrupts\n");
+               return;
+       }
+
+       if (!p_hwfn->p_sb_attn) {
+               DP_ERR(p_hwfn->cdev, "DPC called - no p_sb_attn");
+               return;
+       }
+       sb_attn = p_hwfn->p_sb_attn;
+
+       DP_VERBOSE(p_hwfn, NETIF_MSG_INTR, "DPC Called! (hwfn %p %d)\n",
+                  p_hwfn, p_hwfn->my_id);
+
+       /* Disable ack for def status block. Required both for msix +
+        * inta in non-mask mode, in inta does no harm.
+        */
+       qed_sb_ack(sb_info, IGU_INT_DISABLE, 0);
+
+       /* Gather Interrupts/Attentions information */
+       if (!sb_info->sb_virt) {
+               DP_ERR(
+                       p_hwfn->cdev,
+                       "Interrupt Status block is NULL - cannot check for new interrupts!\n");
+       } else {
+               u32 tmp_index = sb_info->sb_ack;
+
+               rc = qed_sb_update_sb_idx(sb_info);
+               DP_VERBOSE(p_hwfn->cdev, NETIF_MSG_INTR,
+                          "Interrupt indices: 0x%08x --> 0x%08x\n",
+                          tmp_index, sb_info->sb_ack);
+       }
+
+       if (!sb_attn || !sb_attn->sb_attn) {
+               DP_ERR(
+                       p_hwfn->cdev,
+                       "Attentions Status block is NULL - cannot check for new attentions!\n");
+       } else {
+               u16 tmp_index = sb_attn->index;
+
+               rc |= qed_attn_update_idx(p_hwfn, sb_attn);
+               DP_VERBOSE(p_hwfn->cdev, NETIF_MSG_INTR,
+                          "Attention indices: 0x%08x --> 0x%08x\n",
+                          tmp_index, sb_attn->index);
+       }
+
+       /* Check if we expect interrupts at this time. if not just ack them */
+       if (!(rc & QED_SB_EVENT_MASK)) {
+               qed_sb_ack(sb_info, IGU_INT_ENABLE, 1);
+               return;
+       }
+
+       /* Check the validity of the DPC ptt. If not ack interrupts and fail */
+       if (!p_hwfn->p_dpc_ptt) {
+               DP_NOTICE(p_hwfn->cdev, "Failed to allocate PTT\n");
+               qed_sb_ack(sb_info, IGU_INT_ENABLE, 1);
+               return;
+       }
+
+       if (rc & QED_SB_ATT_IDX)
+               qed_int_attentions(p_hwfn);
+
+       if (rc & QED_SB_IDX) {
+               int pi;
+
+               /* Look for a free index */
+               for (pi = 0; pi < arr_size; pi++) {
+                       pi_info = &p_hwfn->p_sp_sb->pi_info_arr[pi];
+                       if (pi_info->comp_cb)
+                               pi_info->comp_cb(p_hwfn, pi_info->cookie);
+               }
+       }
+
+       if (sb_attn && (rc & QED_SB_ATT_IDX))
+               /* This should be done before the interrupts are enabled,
+                * since otherwise a new attention will be generated.
+                */
+               qed_sb_ack_attn(p_hwfn, sb_info->igu_addr, sb_attn->index);
+
+       qed_sb_ack(sb_info, IGU_INT_ENABLE, 1);
+}
+
+static void qed_int_sb_attn_free(struct qed_hwfn *p_hwfn)
+{
+       struct qed_dev *cdev   = p_hwfn->cdev;
+       struct qed_sb_attn_info *p_sb   = p_hwfn->p_sb_attn;
+
+       if (p_sb) {
+               if (p_sb->sb_attn)
+                       dma_free_coherent(&cdev->pdev->dev,
+                                         SB_ATTN_ALIGNED_SIZE(p_hwfn),
+                                         p_sb->sb_attn,
+                                         p_sb->sb_phys);
+               kfree(p_sb);
+       }
+}
+
+static void qed_int_sb_attn_setup(struct qed_hwfn *p_hwfn,
+                                 struct qed_ptt *p_ptt)
+{
+       struct qed_sb_attn_info *sb_info = p_hwfn->p_sb_attn;
+
+       memset(sb_info->sb_attn, 0, sizeof(*sb_info->sb_attn));
+
+       sb_info->index = 0;
+       sb_info->known_attn = 0;
+
+       /* Configure Attention Status Block in IGU */
+       qed_wr(p_hwfn, p_ptt, IGU_REG_ATTN_MSG_ADDR_L,
+              lower_32_bits(p_hwfn->p_sb_attn->sb_phys));
+       qed_wr(p_hwfn, p_ptt, IGU_REG_ATTN_MSG_ADDR_H,
+              upper_32_bits(p_hwfn->p_sb_attn->sb_phys));
+}
+
+static void qed_int_sb_attn_init(struct qed_hwfn *p_hwfn,
+                                struct qed_ptt *p_ptt,
+                                void *sb_virt_addr,
+                                dma_addr_t sb_phy_addr)
+{
+       struct qed_sb_attn_info *sb_info = p_hwfn->p_sb_attn;
+
+       sb_info->sb_attn = sb_virt_addr;
+       sb_info->sb_phys = sb_phy_addr;
+
+       /* Set the address of cleanup for the mcp attention */
+       sb_info->mfw_attn_addr = (p_hwfn->rel_pf_id << 3) +
+                                MISC_REG_AEU_GENERAL_ATTN_0;
+
+       qed_int_sb_attn_setup(p_hwfn, p_ptt);
+}
+
+static int qed_int_sb_attn_alloc(struct qed_hwfn *p_hwfn,
+                                struct qed_ptt *p_ptt)
+{
+       struct qed_dev *cdev = p_hwfn->cdev;
+       struct qed_sb_attn_info *p_sb;
+       void *p_virt;
+       dma_addr_t p_phys = 0;
+
+       /* SB struct */
+       p_sb = kmalloc(sizeof(*p_sb), GFP_ATOMIC);
+       if (!p_sb) {
+               DP_NOTICE(cdev, "Failed to allocate `struct qed_sb_attn_info'\n");
+               return -ENOMEM;
+       }
+
+       /* SB ring  */
+       p_virt = dma_alloc_coherent(&cdev->pdev->dev,
+                                   SB_ATTN_ALIGNED_SIZE(p_hwfn),
+                                   &p_phys, GFP_KERNEL);
+
+       if (!p_virt) {
+               DP_NOTICE(cdev, "Failed to allocate status block (attentions)\n");
+               kfree(p_sb);
+               return -ENOMEM;
+       }
+
+       /* Attention setup */
+       p_hwfn->p_sb_attn = p_sb;
+       qed_int_sb_attn_init(p_hwfn, p_ptt, p_virt, p_phys);
+
+       return 0;
+}
+
+/* coalescing timeout = timeset << (timer_res + 1) */
+#define QED_CAU_DEF_RX_USECS 24
+#define QED_CAU_DEF_TX_USECS 48
+
+void qed_init_cau_sb_entry(struct qed_hwfn *p_hwfn,
+                          struct cau_sb_entry *p_sb_entry,
+                          u8 pf_id,
+                          u16 vf_number,
+                          u8 vf_valid)
+{
+       u32 cau_state;
+
+       memset(p_sb_entry, 0, sizeof(*p_sb_entry));
+
+       SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_PF_NUMBER, pf_id);
+       SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_VF_NUMBER, vf_number);
+       SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_VF_VALID, vf_valid);
+       SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_SB_TIMESET0, 0x7F);
+       SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_SB_TIMESET1, 0x7F);
+
+       /* setting the time resultion to a fixed value ( = 1) */
+       SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_TIMER_RES0,
+                 QED_CAU_DEF_RX_TIMER_RES);
+       SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_TIMER_RES1,
+                 QED_CAU_DEF_TX_TIMER_RES);
+
+       cau_state = CAU_HC_DISABLE_STATE;
+
+       if (p_hwfn->cdev->int_coalescing_mode == QED_COAL_MODE_ENABLE) {
+               cau_state = CAU_HC_ENABLE_STATE;
+               if (!p_hwfn->cdev->rx_coalesce_usecs)
+                       p_hwfn->cdev->rx_coalesce_usecs =
+                               QED_CAU_DEF_RX_USECS;
+               if (!p_hwfn->cdev->tx_coalesce_usecs)
+                       p_hwfn->cdev->tx_coalesce_usecs =
+                               QED_CAU_DEF_TX_USECS;
+       }
+
+       SET_FIELD(p_sb_entry->data, CAU_SB_ENTRY_STATE0, cau_state);
+       SET_FIELD(p_sb_entry->data, CAU_SB_ENTRY_STATE1, cau_state);
+}
+
+void qed_int_cau_conf_sb(struct qed_hwfn *p_hwfn,
+                        struct qed_ptt *p_ptt,
+                        dma_addr_t sb_phys,
+                        u16 igu_sb_id,
+                        u16 vf_number,
+                        u8 vf_valid)
+{
+       struct cau_sb_entry sb_entry;
+       u32 val;
+
+       qed_init_cau_sb_entry(p_hwfn, &sb_entry, p_hwfn->rel_pf_id,
+                             vf_number, vf_valid);
+
+       if (p_hwfn->hw_init_done) {
+               val = CAU_REG_SB_ADDR_MEMORY + igu_sb_id * sizeof(u64);
+               qed_wr(p_hwfn, p_ptt, val, lower_32_bits(sb_phys));
+               qed_wr(p_hwfn, p_ptt, val + sizeof(u32),
+                      upper_32_bits(sb_phys));
+
+               val = CAU_REG_SB_VAR_MEMORY + igu_sb_id * sizeof(u64);
+               qed_wr(p_hwfn, p_ptt, val, sb_entry.data);
+               qed_wr(p_hwfn, p_ptt, val + sizeof(u32), sb_entry.params);
+       } else {
+               /* Initialize Status Block Address */
+               STORE_RT_REG_AGG(p_hwfn,
+                                CAU_REG_SB_ADDR_MEMORY_RT_OFFSET +
+                                igu_sb_id * 2,
+                                sb_phys);
+
+               STORE_RT_REG_AGG(p_hwfn,
+                                CAU_REG_SB_VAR_MEMORY_RT_OFFSET +
+                                igu_sb_id * 2,
+                                sb_entry);
+       }
+
+       /* Configure pi coalescing if set */
+       if (p_hwfn->cdev->int_coalescing_mode == QED_COAL_MODE_ENABLE) {
+               u8 timeset = p_hwfn->cdev->rx_coalesce_usecs >>
+                            (QED_CAU_DEF_RX_TIMER_RES + 1);
+               u8 num_tc = 1, i;
+
+               qed_int_cau_conf_pi(p_hwfn, p_ptt, igu_sb_id, RX_PI,
+                                   QED_COAL_RX_STATE_MACHINE,
+                                   timeset);
+
+               timeset = p_hwfn->cdev->tx_coalesce_usecs >>
+                         (QED_CAU_DEF_TX_TIMER_RES + 1);
+
+               for (i = 0; i < num_tc; i++) {
+                       qed_int_cau_conf_pi(p_hwfn, p_ptt,
+                                           igu_sb_id, TX_PI(i),
+                                           QED_COAL_TX_STATE_MACHINE,
+                                           timeset);
+               }
+       }
+}
+
+void qed_int_cau_conf_pi(struct qed_hwfn *p_hwfn,
+                        struct qed_ptt *p_ptt,
+                        u16 igu_sb_id,
+                        u32 pi_index,
+                        enum qed_coalescing_fsm coalescing_fsm,
+                        u8 timeset)
+{
+       struct cau_pi_entry pi_entry;
+       u32 sb_offset;
+       u32 pi_offset;
+
+       sb_offset = igu_sb_id * PIS_PER_SB;
+       memset(&pi_entry, 0, sizeof(struct cau_pi_entry));
+
+       SET_FIELD(pi_entry.prod, CAU_PI_ENTRY_PI_TIMESET, timeset);
+       if (coalescing_fsm == QED_COAL_RX_STATE_MACHINE)
+               SET_FIELD(pi_entry.prod, CAU_PI_ENTRY_FSM_SEL, 0);
+       else
+               SET_FIELD(pi_entry.prod, CAU_PI_ENTRY_FSM_SEL, 1);
+
+       pi_offset = sb_offset + pi_index;
+       if (p_hwfn->hw_init_done) {
+               qed_wr(p_hwfn, p_ptt,
+                      CAU_REG_PI_MEMORY + pi_offset * sizeof(u32),
+                      *((u32 *)&(pi_entry)));
+       } else {
+               STORE_RT_REG(p_hwfn,
+                            CAU_REG_PI_MEMORY_RT_OFFSET + pi_offset,
+                            *((u32 *)&(pi_entry)));
+       }
+}
+
+void qed_int_sb_setup(struct qed_hwfn *p_hwfn,
+                     struct qed_ptt *p_ptt,
+                     struct qed_sb_info *sb_info)
+{
+       /* zero status block and ack counter */
+       sb_info->sb_ack = 0;
+       memset(sb_info->sb_virt, 0, sizeof(*sb_info->sb_virt));
+
+       qed_int_cau_conf_sb(p_hwfn, p_ptt, sb_info->sb_phys,
+                           sb_info->igu_sb_id, 0, 0);
+}
+
+/**
+ * @brief qed_get_igu_sb_id - given a sw sb_id return the
+ *        igu_sb_id
+ *
+ * @param p_hwfn
+ * @param sb_id
+ *
+ * @return u16
+ */
+static u16 qed_get_igu_sb_id(struct qed_hwfn *p_hwfn,
+                            u16 sb_id)
+{
+       u16 igu_sb_id;
+
+       /* Assuming continuous set of IGU SBs dedicated for given PF */
+       if (sb_id == QED_SP_SB_ID)
+               igu_sb_id = p_hwfn->hw_info.p_igu_info->igu_dsb_id;
+       else
+               igu_sb_id = sb_id + p_hwfn->hw_info.p_igu_info->igu_base_sb;
+
+       DP_VERBOSE(p_hwfn, NETIF_MSG_INTR, "SB [%s] index is 0x%04x\n",
+                  (sb_id == QED_SP_SB_ID) ? "DSB" : "non-DSB", igu_sb_id);
+
+       return igu_sb_id;
+}
+
+int qed_int_sb_init(struct qed_hwfn *p_hwfn,
+                   struct qed_ptt *p_ptt,
+                   struct qed_sb_info *sb_info,
+                   void *sb_virt_addr,
+                   dma_addr_t sb_phy_addr,
+                   u16 sb_id)
+{
+       sb_info->sb_virt = sb_virt_addr;
+       sb_info->sb_phys = sb_phy_addr;
+
+       sb_info->igu_sb_id = qed_get_igu_sb_id(p_hwfn, sb_id);
+
+       if (sb_id != QED_SP_SB_ID) {
+               p_hwfn->sbs_info[sb_id] = sb_info;
+               p_hwfn->num_sbs++;
+       }
+
+       sb_info->cdev = p_hwfn->cdev;
+
+       /* The igu address will hold the absolute address that needs to be
+        * written to for a specific status block
+        */
+       sb_info->igu_addr = (u8 __iomem *)p_hwfn->regview +
+                                         GTT_BAR0_MAP_REG_IGU_CMD +
+                                         (sb_info->igu_sb_id << 3);
+
+       sb_info->flags |= QED_SB_INFO_INIT;
+
+       qed_int_sb_setup(p_hwfn, p_ptt, sb_info);
+
+       return 0;
+}
+
+int qed_int_sb_release(struct qed_hwfn *p_hwfn,
+                      struct qed_sb_info *sb_info,
+                      u16 sb_id)
+{
+       if (sb_id == QED_SP_SB_ID) {
+               DP_ERR(p_hwfn, "Do Not free sp sb using this function");
+               return -EINVAL;
+       }
+
+       /* zero status block and ack counter */
+       sb_info->sb_ack = 0;
+       memset(sb_info->sb_virt, 0, sizeof(*sb_info->sb_virt));
+
+       p_hwfn->sbs_info[sb_id] = NULL;
+       p_hwfn->num_sbs--;
+
+       return 0;
+}
+
+static void qed_int_sp_sb_free(struct qed_hwfn *p_hwfn)
+{
+       struct qed_sb_sp_info *p_sb = p_hwfn->p_sp_sb;
+
+       if (p_sb) {
+               if (p_sb->sb_info.sb_virt)
+                       dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                                         SB_ALIGNED_SIZE(p_hwfn),
+                                         p_sb->sb_info.sb_virt,
+                                         p_sb->sb_info.sb_phys);
+               kfree(p_sb);
+       }
+}
+
+static int qed_int_sp_sb_alloc(struct qed_hwfn *p_hwfn,
+                              struct qed_ptt *p_ptt)
+{
+       struct qed_sb_sp_info *p_sb;
+       dma_addr_t p_phys = 0;
+       void *p_virt;
+
+       /* SB struct */
+       p_sb = kmalloc(sizeof(*p_sb), GFP_ATOMIC);
+       if (!p_sb) {
+               DP_NOTICE(p_hwfn, "Failed to allocate `struct qed_sb_info'\n");
+               return -ENOMEM;
+       }
+
+       /* SB ring  */
+       p_virt = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                   SB_ALIGNED_SIZE(p_hwfn),
+                                   &p_phys, GFP_KERNEL);
+       if (!p_virt) {
+               DP_NOTICE(p_hwfn, "Failed to allocate status block\n");
+               kfree(p_sb);
+               return -ENOMEM;
+       }
+
+       /* Status Block setup */
+       p_hwfn->p_sp_sb = p_sb;
+       qed_int_sb_init(p_hwfn, p_ptt, &p_sb->sb_info, p_virt,
+                       p_phys, QED_SP_SB_ID);
+
+       memset(p_sb->pi_info_arr, 0, sizeof(p_sb->pi_info_arr));
+
+       return 0;
+}
+
+static void qed_int_sp_sb_setup(struct qed_hwfn *p_hwfn,
+                               struct qed_ptt *p_ptt)
+{
+       if (!p_hwfn)
+               return;
+
+       if (p_hwfn->p_sp_sb)
+               qed_int_sb_setup(p_hwfn, p_ptt, &p_hwfn->p_sp_sb->sb_info);
+       else
+               DP_NOTICE(p_hwfn->cdev,
+                         "Failed to setup Slow path status block - NULL pointer\n");
+
+       if (p_hwfn->p_sb_attn)
+               qed_int_sb_attn_setup(p_hwfn, p_ptt);
+       else
+               DP_NOTICE(p_hwfn->cdev,
+                         "Failed to setup attentions status block - NULL pointer\n");
+}
+
+int qed_int_register_cb(struct qed_hwfn *p_hwfn,
+                       qed_int_comp_cb_t comp_cb,
+                       void *cookie,
+                       u8 *sb_idx,
+                       __le16 **p_fw_cons)
+{
+       struct qed_sb_sp_info *p_sp_sb = p_hwfn->p_sp_sb;
+       int qed_status = -ENOMEM;
+       u8 pi;
+
+       /* Look for a free index */
+       for (pi = 0; pi < ARRAY_SIZE(p_sp_sb->pi_info_arr); pi++) {
+               if (!p_sp_sb->pi_info_arr[pi].comp_cb) {
+                       p_sp_sb->pi_info_arr[pi].comp_cb = comp_cb;
+                       p_sp_sb->pi_info_arr[pi].cookie = cookie;
+                       *sb_idx = pi;
+                       *p_fw_cons = &p_sp_sb->sb_info.sb_virt->pi_array[pi];
+                       qed_status = 0;
+                       break;
+               }
+       }
+
+       return qed_status;
+}
+
+int qed_int_unregister_cb(struct qed_hwfn *p_hwfn, u8 pi)
+{
+       struct qed_sb_sp_info *p_sp_sb = p_hwfn->p_sp_sb;
+       int qed_status = -ENOMEM;
+
+       if (p_sp_sb->pi_info_arr[pi].comp_cb) {
+               p_sp_sb->pi_info_arr[pi].comp_cb = NULL;
+               p_sp_sb->pi_info_arr[pi].cookie = NULL;
+               qed_status = 0;
+       }
+
+       return qed_status;
+}
+
+u16 qed_int_get_sp_sb_id(struct qed_hwfn *p_hwfn)
+{
+       return p_hwfn->p_sp_sb->sb_info.igu_sb_id;
+}
+
+void qed_int_igu_enable_int(struct qed_hwfn *p_hwfn,
+                           struct qed_ptt *p_ptt,
+                           enum qed_int_mode int_mode)
+{
+       u32 igu_pf_conf = IGU_PF_CONF_FUNC_EN | IGU_PF_CONF_ATTN_BIT_EN;
+
+       p_hwfn->cdev->int_mode = int_mode;
+       switch (p_hwfn->cdev->int_mode) {
+       case QED_INT_MODE_INTA:
+               igu_pf_conf |= IGU_PF_CONF_INT_LINE_EN;
+               igu_pf_conf |= IGU_PF_CONF_SINGLE_ISR_EN;
+               break;
+
+       case QED_INT_MODE_MSI:
+               igu_pf_conf |= IGU_PF_CONF_MSI_MSIX_EN;
+               igu_pf_conf |= IGU_PF_CONF_SINGLE_ISR_EN;
+               break;
+
+       case QED_INT_MODE_MSIX:
+               igu_pf_conf |= IGU_PF_CONF_MSI_MSIX_EN;
+               break;
+       case QED_INT_MODE_POLL:
+               break;
+       }
+
+       qed_wr(p_hwfn, p_ptt, IGU_REG_PF_CONFIGURATION, igu_pf_conf);
+}
+
+void qed_int_igu_enable(struct qed_hwfn *p_hwfn,
+                       struct qed_ptt *p_ptt,
+                       enum qed_int_mode int_mode)
+{
+       int i;
+
+       p_hwfn->b_int_enabled = 1;
+
+       /* Mask non-link attentions */
+       for (i = 0; i < 9; i++)
+               qed_wr(p_hwfn, p_ptt,
+                      MISC_REG_AEU_ENABLE1_IGU_OUT_0 + (i << 2), 0);
+
+       /* Enable interrupt Generation */
+       qed_int_igu_enable_int(p_hwfn, p_ptt, int_mode);
+
+       /* Configure AEU signal change to produce attentions for link */
+       qed_wr(p_hwfn, p_ptt, IGU_REG_LEADING_EDGE_LATCH, 0xfff);
+       qed_wr(p_hwfn, p_ptt, IGU_REG_TRAILING_EDGE_LATCH, 0xfff);
+
+       /* Flush the writes to IGU */
+       mmiowb();
+
+       /* Unmask AEU signals toward IGU */
+       qed_wr(p_hwfn, p_ptt, MISC_REG_AEU_MASK_ATTN_IGU, 0xff);
+}
+
+void qed_int_igu_disable_int(struct qed_hwfn *p_hwfn,
+                            struct qed_ptt *p_ptt)
+{
+       p_hwfn->b_int_enabled = 0;
+
+       qed_wr(p_hwfn, p_ptt, IGU_REG_PF_CONFIGURATION, 0);
+}
+
+#define IGU_CLEANUP_SLEEP_LENGTH                (1000)
+void qed_int_igu_cleanup_sb(struct qed_hwfn *p_hwfn,
+                           struct qed_ptt *p_ptt,
+                           u32 sb_id,
+                           bool cleanup_set,
+                           u16 opaque_fid
+                           )
+{
+       u32 pxp_addr = IGU_CMD_INT_ACK_BASE + sb_id;
+       u32 sleep_cnt = IGU_CLEANUP_SLEEP_LENGTH;
+       u32 data = 0;
+       u32 cmd_ctrl = 0;
+       u32 val = 0;
+       u32 sb_bit = 0;
+       u32 sb_bit_addr = 0;
+
+       /* Set the data field */
+       SET_FIELD(data, IGU_CLEANUP_CLEANUP_SET, cleanup_set ? 1 : 0);
+       SET_FIELD(data, IGU_CLEANUP_CLEANUP_TYPE, 0);
+       SET_FIELD(data, IGU_CLEANUP_COMMAND_TYPE, IGU_COMMAND_TYPE_SET);
+
+       /* Set the control register */
+       SET_FIELD(cmd_ctrl, IGU_CTRL_REG_PXP_ADDR, pxp_addr);
+       SET_FIELD(cmd_ctrl, IGU_CTRL_REG_FID, opaque_fid);
+       SET_FIELD(cmd_ctrl, IGU_CTRL_REG_TYPE, IGU_CTRL_CMD_TYPE_WR);
+
+       qed_wr(p_hwfn, p_ptt, IGU_REG_COMMAND_REG_32LSB_DATA, data);
+
+       barrier();
+
+       qed_wr(p_hwfn, p_ptt, IGU_REG_COMMAND_REG_CTRL, cmd_ctrl);
+
+       /* Flush the write to IGU */
+       mmiowb();
+
+       /* calculate where to read the status bit from */
+       sb_bit = 1 << (sb_id % 32);
+       sb_bit_addr = sb_id / 32 * sizeof(u32);
+
+       sb_bit_addr += IGU_REG_CLEANUP_STATUS_0;
+
+       /* Now wait for the command to complete */
+       do {
+               val = qed_rd(p_hwfn, p_ptt, sb_bit_addr);
+
+               if ((val & sb_bit) == (cleanup_set ? sb_bit : 0))
+                       break;
+
+               usleep_range(5000, 10000);
+       } while (--sleep_cnt);
+
+       if (!sleep_cnt)
+               DP_NOTICE(p_hwfn,
+                         "Timeout waiting for clear status 0x%08x [for sb %d]\n",
+                         val, sb_id);
+}
+
+void qed_int_igu_init_pure_rt_single(struct qed_hwfn *p_hwfn,
+                                    struct qed_ptt *p_ptt,
+                                    u32 sb_id,
+                                    u16 opaque,
+                                    bool b_set)
+{
+       int pi;
+
+       /* Set */
+       if (b_set)
+               qed_int_igu_cleanup_sb(p_hwfn, p_ptt, sb_id, 1, opaque);
+
+       /* Clear */
+       qed_int_igu_cleanup_sb(p_hwfn, p_ptt, sb_id, 0, opaque);
+
+       /* Clear the CAU for the SB */
+       for (pi = 0; pi < 12; pi++)
+               qed_wr(p_hwfn, p_ptt,
+                      CAU_REG_PI_MEMORY + (sb_id * 12 + pi) * 4, 0);
+}
+
+void qed_int_igu_init_pure_rt(struct qed_hwfn *p_hwfn,
+                             struct qed_ptt *p_ptt,
+                             bool b_set,
+                             bool b_slowpath)
+{
+       u32 igu_base_sb = p_hwfn->hw_info.p_igu_info->igu_base_sb;
+       u32 igu_sb_cnt = p_hwfn->hw_info.p_igu_info->igu_sb_cnt;
+       u32 sb_id = 0;
+       u32 val = 0;
+
+       val = qed_rd(p_hwfn, p_ptt, IGU_REG_BLOCK_CONFIGURATION);
+       val |= IGU_REG_BLOCK_CONFIGURATION_VF_CLEANUP_EN;
+       val &= ~IGU_REG_BLOCK_CONFIGURATION_PXP_TPH_INTERFACE_EN;
+       qed_wr(p_hwfn, p_ptt, IGU_REG_BLOCK_CONFIGURATION, val);
+
+       DP_VERBOSE(p_hwfn, NETIF_MSG_INTR,
+                  "IGU cleaning SBs [%d,...,%d]\n",
+                  igu_base_sb, igu_base_sb + igu_sb_cnt - 1);
+
+       for (sb_id = igu_base_sb; sb_id < igu_base_sb + igu_sb_cnt; sb_id++)
+               qed_int_igu_init_pure_rt_single(p_hwfn, p_ptt, sb_id,
+                                               p_hwfn->hw_info.opaque_fid,
+                                               b_set);
+
+       if (b_slowpath) {
+               sb_id = p_hwfn->hw_info.p_igu_info->igu_dsb_id;
+               DP_VERBOSE(p_hwfn, NETIF_MSG_INTR,
+                          "IGU cleaning slowpath SB [%d]\n", sb_id);
+               qed_int_igu_init_pure_rt_single(p_hwfn, p_ptt, sb_id,
+                                               p_hwfn->hw_info.opaque_fid,
+                                               b_set);
+       }
+}
+
+int qed_int_igu_read_cam(struct qed_hwfn *p_hwfn,
+                        struct qed_ptt *p_ptt)
+{
+       struct qed_igu_info *p_igu_info;
+       struct qed_igu_block *blk;
+       u32 val;
+       u16 sb_id;
+       u16 prev_sb_id = 0xFF;
+
+       p_hwfn->hw_info.p_igu_info = kzalloc(sizeof(*p_igu_info), GFP_ATOMIC);
+
+       if (!p_hwfn->hw_info.p_igu_info)
+               return -ENOMEM;
+
+       p_igu_info = p_hwfn->hw_info.p_igu_info;
+
+       /* Initialize base sb / sb cnt for PFs */
+       p_igu_info->igu_base_sb         = 0xffff;
+       p_igu_info->igu_sb_cnt          = 0;
+       p_igu_info->igu_dsb_id          = 0xffff;
+       p_igu_info->igu_base_sb_iov     = 0xffff;
+
+       for (sb_id = 0; sb_id < QED_MAPPING_MEMORY_SIZE(p_hwfn->cdev);
+            sb_id++) {
+               blk = &p_igu_info->igu_map.igu_blocks[sb_id];
+
+               val = qed_rd(p_hwfn, p_ptt,
+                            IGU_REG_MAPPING_MEMORY + sizeof(u32) * sb_id);
+
+               /* stop scanning when hit first invalid PF entry */
+               if (!GET_FIELD(val, IGU_MAPPING_LINE_VALID) &&
+                   GET_FIELD(val, IGU_MAPPING_LINE_PF_VALID))
+                       break;
+
+               blk->status = QED_IGU_STATUS_VALID;
+               blk->function_id = GET_FIELD(val,
+                                            IGU_MAPPING_LINE_FUNCTION_NUMBER);
+               blk->is_pf = GET_FIELD(val, IGU_MAPPING_LINE_PF_VALID);
+               blk->vector_number = GET_FIELD(val,
+                                              IGU_MAPPING_LINE_VECTOR_NUMBER);
+
+               DP_VERBOSE(p_hwfn, NETIF_MSG_INTR,
+                          "IGU_BLOCK[sb_id]:%x:func_id = %d is_pf = %d vector_num = 0x%x\n",
+                          val, blk->function_id, blk->is_pf,
+                          blk->vector_number);
+
+               if (blk->is_pf) {
+                       if (blk->function_id == p_hwfn->rel_pf_id) {
+                               blk->status |= QED_IGU_STATUS_PF;
+
+                               if (blk->vector_number == 0) {
+                                       if (p_igu_info->igu_dsb_id == 0xffff)
+                                               p_igu_info->igu_dsb_id = sb_id;
+                               } else {
+                                       if (p_igu_info->igu_base_sb ==
+                                           0xffff) {
+                                               p_igu_info->igu_base_sb = sb_id;
+                                       } else if (prev_sb_id != sb_id - 1) {
+                                               DP_NOTICE(p_hwfn->cdev,
+                                                         "consecutive igu vectors for HWFN %x broken",
+                                                         p_hwfn->rel_pf_id);
+                                               break;
+                                       }
+                                       prev_sb_id = sb_id;
+                                       /* we don't count the default */
+                                       (p_igu_info->igu_sb_cnt)++;
+                               }
+                       }
+               }
+       }
+
+       DP_VERBOSE(p_hwfn, NETIF_MSG_INTR,
+                  "IGU igu_base_sb=0x%x igu_sb_cnt=%d igu_dsb_id=0x%x\n",
+                  p_igu_info->igu_base_sb,
+                  p_igu_info->igu_sb_cnt,
+                  p_igu_info->igu_dsb_id);
+
+       if (p_igu_info->igu_base_sb == 0xffff ||
+           p_igu_info->igu_dsb_id == 0xffff ||
+           p_igu_info->igu_sb_cnt == 0) {
+               DP_NOTICE(p_hwfn,
+                         "IGU CAM returned invalid values igu_base_sb=0x%x igu_sb_cnt=%d igu_dsb_id=0x%x\n",
+                          p_igu_info->igu_base_sb,
+                          p_igu_info->igu_sb_cnt,
+                          p_igu_info->igu_dsb_id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * @brief Initialize igu runtime registers
+ *
+ * @param p_hwfn
+ */
+void qed_int_igu_init_rt(struct qed_hwfn *p_hwfn)
+{
+       u32 igu_pf_conf = 0;
+
+       igu_pf_conf |= IGU_PF_CONF_FUNC_EN;
+
+       STORE_RT_REG(p_hwfn, IGU_REG_PF_CONFIGURATION_RT_OFFSET, igu_pf_conf);
+}
+
+u64 qed_int_igu_read_sisr_reg(struct qed_hwfn *p_hwfn)
+{
+       u64 intr_status = 0;
+       u32 intr_status_lo = 0;
+       u32 intr_status_hi = 0;
+       u32 lsb_igu_cmd_addr = IGU_REG_SISR_MDPC_WMASK_LSB_UPPER -
+                              IGU_CMD_INT_ACK_BASE;
+       u32 msb_igu_cmd_addr = IGU_REG_SISR_MDPC_WMASK_MSB_UPPER -
+                              IGU_CMD_INT_ACK_BASE;
+
+       intr_status_lo = REG_RD(p_hwfn,
+                               GTT_BAR0_MAP_REG_IGU_CMD +
+                               lsb_igu_cmd_addr * 8);
+       intr_status_hi = REG_RD(p_hwfn,
+                               GTT_BAR0_MAP_REG_IGU_CMD +
+                               msb_igu_cmd_addr * 8);
+       intr_status = ((u64)intr_status_hi << 32) + (u64)intr_status_lo;
+
+       return intr_status;
+}
+
+static void qed_int_sp_dpc_setup(struct qed_hwfn *p_hwfn)
+{
+       tasklet_init(p_hwfn->sp_dpc,
+                    qed_int_sp_dpc, (unsigned long)p_hwfn);
+       p_hwfn->b_sp_dpc_enabled = true;
+}
+
+static int qed_int_sp_dpc_alloc(struct qed_hwfn *p_hwfn)
+{
+       p_hwfn->sp_dpc = kmalloc(sizeof(*p_hwfn->sp_dpc), GFP_ATOMIC);
+       if (!p_hwfn->sp_dpc)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void qed_int_sp_dpc_free(struct qed_hwfn *p_hwfn)
+{
+       kfree(p_hwfn->sp_dpc);
+}
+
+int qed_int_alloc(struct qed_hwfn *p_hwfn,
+                 struct qed_ptt *p_ptt)
+{
+       int rc = 0;
+
+       rc = qed_int_sp_dpc_alloc(p_hwfn);
+       if (rc) {
+               DP_ERR(p_hwfn->cdev, "Failed to allocate sp dpc mem\n");
+               return rc;
+       }
+       rc = qed_int_sp_sb_alloc(p_hwfn, p_ptt);
+       if (rc) {
+               DP_ERR(p_hwfn->cdev, "Failed to allocate sp sb mem\n");
+               return rc;
+       }
+       rc = qed_int_sb_attn_alloc(p_hwfn, p_ptt);
+       if (rc) {
+               DP_ERR(p_hwfn->cdev, "Failed to allocate sb attn mem\n");
+               return rc;
+       }
+       return rc;
+}
+
+void qed_int_free(struct qed_hwfn *p_hwfn)
+{
+       qed_int_sp_sb_free(p_hwfn);
+       qed_int_sb_attn_free(p_hwfn);
+       qed_int_sp_dpc_free(p_hwfn);
+}
+
+void qed_int_setup(struct qed_hwfn *p_hwfn,
+                  struct qed_ptt *p_ptt)
+{
+       qed_int_sp_sb_setup(p_hwfn, p_ptt);
+       qed_int_sp_dpc_setup(p_hwfn);
+}
+
+int qed_int_get_num_sbs(struct qed_hwfn *p_hwfn,
+                       int *p_iov_blks)
+{
+       struct qed_igu_info *info = p_hwfn->hw_info.p_igu_info;
+
+       if (!info)
+               return 0;
+
+       if (p_iov_blks)
+               *p_iov_blks = info->free_blks;
+
+       return info->igu_sb_cnt;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.h b/drivers/net/ethernet/qlogic/qed/qed_int.h
new file mode 100644 (file)
index 0000000..16b5751
--- /dev/null
@@ -0,0 +1,391 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_INT_H
+#define _QED_INT_H
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include "qed.h"
+
+/* Fields of IGU PF CONFIGRATION REGISTER */
+#define IGU_PF_CONF_FUNC_EN       (0x1 << 0)    /* function enable        */
+#define IGU_PF_CONF_MSI_MSIX_EN   (0x1 << 1)    /* MSI/MSIX enable        */
+#define IGU_PF_CONF_INT_LINE_EN   (0x1 << 2)    /* INT enable             */
+#define IGU_PF_CONF_ATTN_BIT_EN   (0x1 << 3)    /* attention enable       */
+#define IGU_PF_CONF_SINGLE_ISR_EN (0x1 << 4)    /* single ISR mode enable */
+#define IGU_PF_CONF_SIMD_MODE     (0x1 << 5)    /* simd all ones mode     */
+
+/* Igu control commands
+ */
+enum igu_ctrl_cmd {
+       IGU_CTRL_CMD_TYPE_RD,
+       IGU_CTRL_CMD_TYPE_WR,
+       MAX_IGU_CTRL_CMD
+};
+
+/* Control register for the IGU command register
+ */
+struct igu_ctrl_reg {
+       u32 ctrl_data;
+#define IGU_CTRL_REG_FID_MASK           0xFFFF  /* Opaque_FID   */
+#define IGU_CTRL_REG_FID_SHIFT          0
+#define IGU_CTRL_REG_PXP_ADDR_MASK      0xFFF   /* Command address */
+#define IGU_CTRL_REG_PXP_ADDR_SHIFT     16
+#define IGU_CTRL_REG_RESERVED_MASK      0x1
+#define IGU_CTRL_REG_RESERVED_SHIFT     28
+#define IGU_CTRL_REG_TYPE_MASK          0x1 /* use enum igu_ctrl_cmd */
+#define IGU_CTRL_REG_TYPE_SHIFT         31
+};
+
+enum qed_coalescing_fsm {
+       QED_COAL_RX_STATE_MACHINE,
+       QED_COAL_TX_STATE_MACHINE
+};
+
+/**
+ * @brief qed_int_cau_conf_pi - configure cau for a given
+ *        status block
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param igu_sb_id
+ * @param pi_index
+ * @param state
+ * @param timeset
+ */
+void qed_int_cau_conf_pi(struct qed_hwfn *p_hwfn,
+                        struct qed_ptt *p_ptt,
+                        u16 igu_sb_id,
+                        u32 pi_index,
+                        enum qed_coalescing_fsm coalescing_fsm,
+                        u8 timeset);
+
+/**
+ * @brief qed_int_igu_enable_int - enable device interrupts
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param int_mode - interrupt mode to use
+ */
+void qed_int_igu_enable_int(struct qed_hwfn *p_hwfn,
+                           struct qed_ptt *p_ptt,
+                           enum qed_int_mode int_mode);
+
+/**
+ * @brief qed_int_igu_disable_int - disable device interrupts
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ */
+void qed_int_igu_disable_int(struct qed_hwfn *p_hwfn,
+                            struct qed_ptt *p_ptt);
+
+/**
+ * @brief qed_int_igu_read_sisr_reg - Reads the single isr multiple dpc
+ *        register from igu.
+ *
+ * @param p_hwfn
+ *
+ * @return u64
+ */
+u64 qed_int_igu_read_sisr_reg(struct qed_hwfn *p_hwfn);
+
+#define QED_SP_SB_ID 0xffff
+/**
+ * @brief qed_int_sb_init - Initializes the sb_info structure.
+ *
+ * once the structure is initialized it can be passed to sb related functions.
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param sb_info      points to an uninitialized (but
+ *                     allocated) sb_info structure
+ * @param sb_virt_addr
+ * @param sb_phy_addr
+ * @param sb_id        the sb_id to be used (zero based in driver)
+ *                     should use QED_SP_SB_ID for SP Status block
+ *
+ * @return int
+ */
+int qed_int_sb_init(struct qed_hwfn *p_hwfn,
+                   struct qed_ptt *p_ptt,
+                   struct qed_sb_info *sb_info,
+                   void *sb_virt_addr,
+                   dma_addr_t sb_phy_addr,
+                   u16 sb_id);
+/**
+ * @brief qed_int_sb_setup - Setup the sb.
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param sb_info      initialized sb_info structure
+ */
+void qed_int_sb_setup(struct qed_hwfn *p_hwfn,
+                     struct qed_ptt *p_ptt,
+                     struct qed_sb_info *sb_info);
+
+/**
+ * @brief qed_int_sb_release - releases the sb_info structure.
+ *
+ * once the structure is released, it's memory can be freed
+ *
+ * @param p_hwfn
+ * @param sb_info      points to an allocated sb_info structure
+ * @param sb_id                the sb_id to be used (zero based in driver)
+ *                     should never be equal to QED_SP_SB_ID
+ *                     (SP Status block)
+ *
+ * @return int
+ */
+int qed_int_sb_release(struct qed_hwfn *p_hwfn,
+                      struct qed_sb_info *sb_info,
+                      u16 sb_id);
+
+/**
+ * @brief qed_int_sp_dpc - To be called when an interrupt is received on the
+ *        default status block.
+ *
+ * @param p_hwfn - pointer to hwfn
+ *
+ */
+void qed_int_sp_dpc(unsigned long hwfn_cookie);
+
+/**
+ * @brief qed_int_get_num_sbs - get the number of status
+ *        blocks configured for this funciton in the igu.
+ *
+ * @param p_hwfn
+ * @param p_iov_blks - configured free blks for vfs
+ *
+ * @return int - number of status blocks configured
+ */
+int qed_int_get_num_sbs(struct qed_hwfn *p_hwfn,
+                       int *p_iov_blks);
+
+/**
+ * @file
+ *
+ * @brief Interrupt handler
+ */
+
+#define QED_CAU_DEF_RX_TIMER_RES 0
+#define QED_CAU_DEF_TX_TIMER_RES 0
+
+#define QED_SB_ATT_IDX  0x0001
+#define QED_SB_EVENT_MASK       0x0003
+
+#define SB_ALIGNED_SIZE(p_hwfn)        \
+       ALIGNED_TYPE_SIZE(struct status_block, p_hwfn)
+
+struct qed_igu_block {
+       u8      status;
+#define QED_IGU_STATUS_FREE     0x01
+#define QED_IGU_STATUS_VALID    0x02
+#define QED_IGU_STATUS_PF       0x04
+
+       u8      vector_number;
+       u8      function_id;
+       u8      is_pf;
+};
+
+struct qed_igu_map {
+       struct qed_igu_block igu_blocks[MAX_TOT_SB_PER_PATH];
+};
+
+struct qed_igu_info {
+       struct qed_igu_map      igu_map;
+       u16                     igu_dsb_id;
+       u16                     igu_base_sb;
+       u16                     igu_base_sb_iov;
+       u16                     igu_sb_cnt;
+       u16                     igu_sb_cnt_iov;
+       u16                     free_blks;
+};
+
+/* TODO Names of function may change... */
+void qed_int_igu_init_pure_rt(struct qed_hwfn *p_hwfn,
+                             struct qed_ptt *p_ptt,
+                             bool b_set,
+                             bool b_slowpath);
+
+void qed_int_igu_init_rt(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_int_igu_read_cam - Reads the IGU CAM.
+ *     This function needs to be called during hardware
+ *     prepare. It reads the info from igu cam to know which
+ *     status block is the default / base status block etc.
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ *
+ * @return int
+ */
+int qed_int_igu_read_cam(struct qed_hwfn *p_hwfn,
+                        struct qed_ptt *p_ptt);
+
+typedef int (*qed_int_comp_cb_t)(struct qed_hwfn *p_hwfn,
+                                void *cookie);
+/**
+ * @brief qed_int_register_cb - Register callback func for
+ *      slowhwfn statusblock.
+ *
+ *     Every protocol that uses the slowhwfn status block
+ *     should register a callback function that will be called
+ *     once there is an update of the sp status block.
+ *
+ * @param p_hwfn
+ * @param comp_cb - function to be called when there is an
+ *                  interrupt on the sp sb
+ *
+ * @param cookie  - passed to the callback function
+ * @param sb_idx  - OUT parameter which gives the chosen index
+ *                  for this protocol.
+ * @param p_fw_cons  - pointer to the actual address of the
+ *                     consumer for this protocol.
+ *
+ * @return int
+ */
+int qed_int_register_cb(struct qed_hwfn *p_hwfn,
+                       qed_int_comp_cb_t comp_cb,
+                       void *cookie,
+                       u8 *sb_idx,
+                       __le16 **p_fw_cons);
+
+/**
+ * @brief qed_int_unregister_cb - Unregisters callback
+ *      function from sp sb.
+ *      Partner of qed_int_register_cb -> should be called
+ *      when no longer required.
+ *
+ * @param p_hwfn
+ * @param pi
+ *
+ * @return int
+ */
+int qed_int_unregister_cb(struct qed_hwfn *p_hwfn,
+                         u8 pi);
+
+/**
+ * @brief qed_int_get_sp_sb_id - Get the slowhwfn sb id.
+ *
+ * @param p_hwfn
+ *
+ * @return u16
+ */
+u16 qed_int_get_sp_sb_id(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief Status block cleanup. Should be called for each status
+ *        block that will be used -> both PF / VF
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param sb_id                - igu status block id
+ * @param cleanup_set  - set(1) / clear(0)
+ * @param opaque_fid    - the function for which to perform
+ *                     cleanup, for example a PF on behalf of
+ *                     its VFs.
+ */
+void qed_int_igu_cleanup_sb(struct qed_hwfn *p_hwfn,
+                           struct qed_ptt *p_ptt,
+                           u32 sb_id,
+                           bool cleanup_set,
+                           u16 opaque_fid);
+
+/**
+ * @brief Status block cleanup. Should be called for each status
+ *        block that will be used -> both PF / VF
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param sb_id                - igu status block id
+ * @param opaque       - opaque fid of the sb owner.
+ * @param cleanup_set  - set(1) / clear(0)
+ */
+void qed_int_igu_init_pure_rt_single(struct qed_hwfn *p_hwfn,
+                                    struct qed_ptt *p_ptt,
+                                    u32 sb_id,
+                                    u16 opaque,
+                                    bool b_set);
+
+/**
+ * @brief qed_int_cau_conf - configure cau for a given status
+ *        block
+ *
+ * @param p_hwfn
+ * @param ptt
+ * @param sb_phys
+ * @param igu_sb_id
+ * @param vf_number
+ * @param vf_valid
+ */
+void qed_int_cau_conf_sb(struct qed_hwfn *p_hwfn,
+                        struct qed_ptt *p_ptt,
+                        dma_addr_t sb_phys,
+                        u16 igu_sb_id,
+                        u16 vf_number,
+                        u8 vf_valid);
+
+/**
+ * @brief qed_int_alloc
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ *
+ * @return int
+ */
+int qed_int_alloc(struct qed_hwfn *p_hwfn,
+                 struct qed_ptt *p_ptt);
+
+/**
+ * @brief qed_int_free
+ *
+ * @param p_hwfn
+ */
+void qed_int_free(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_int_setup
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ */
+void qed_int_setup(struct qed_hwfn *p_hwfn,
+                  struct qed_ptt *p_ptt);
+
+/**
+ * @brief - Enable Interrupt & Attention for hw function
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param int_mode
+ */
+void qed_int_igu_enable(struct qed_hwfn *p_hwfn,
+                       struct qed_ptt *p_ptt,
+                       enum qed_int_mode int_mode);
+
+/**
+ * @brief - Initialize CAU status block entry
+ *
+ * @param p_hwfn
+ * @param p_sb_entry
+ * @param pf_id
+ * @param vf_number
+ * @param vf_valid
+ */
+void qed_init_cau_sb_entry(struct qed_hwfn *p_hwfn,
+                          struct cau_sb_entry *p_sb_entry,
+                          u8 pf_id,
+                          u16 vf_number,
+                          u8 vf_valid);
+
+#define QED_MAPPING_MEMORY_SIZE(dev)   (NUM_OF_SBS(dev))
+
+#endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c
new file mode 100644 (file)
index 0000000..f72036a
--- /dev/null
@@ -0,0 +1,1704 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <asm/param.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include "qed.h"
+#include <linux/qed/qed_chain.h>
+#include "qed_cxt.h"
+#include "qed_dev_api.h"
+#include <linux/qed/qed_eth_if.h>
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_int.h"
+#include "qed_reg_addr.h"
+#include "qed_sp.h"
+
+enum qed_rss_caps {
+       QED_RSS_IPV4            = 0x1,
+       QED_RSS_IPV6            = 0x2,
+       QED_RSS_IPV4_TCP        = 0x4,
+       QED_RSS_IPV6_TCP        = 0x8,
+       QED_RSS_IPV4_UDP        = 0x10,
+       QED_RSS_IPV6_UDP        = 0x20,
+};
+
+/* Should be the same as ETH_RSS_IND_TABLE_ENTRIES_NUM */
+#define QED_RSS_IND_TABLE_SIZE 128
+#define QED_RSS_KEY_SIZE 10 /* size in 32b chunks */
+
+struct qed_rss_params {
+       u8      update_rss_config;
+       u8      rss_enable;
+       u8      rss_eng_id;
+       u8      update_rss_capabilities;
+       u8      update_rss_ind_table;
+       u8      update_rss_key;
+       u8      rss_caps;
+       u8      rss_table_size_log;
+       u16     rss_ind_table[QED_RSS_IND_TABLE_SIZE];
+       u32     rss_key[QED_RSS_KEY_SIZE];
+};
+
+enum qed_filter_opcode {
+       QED_FILTER_ADD,
+       QED_FILTER_REMOVE,
+       QED_FILTER_MOVE,
+       QED_FILTER_REPLACE,     /* Delete all MACs and add new one instead */
+       QED_FILTER_FLUSH,       /* Removes all filters */
+};
+
+enum qed_filter_ucast_type {
+       QED_FILTER_MAC,
+       QED_FILTER_VLAN,
+       QED_FILTER_MAC_VLAN,
+       QED_FILTER_INNER_MAC,
+       QED_FILTER_INNER_VLAN,
+       QED_FILTER_INNER_PAIR,
+       QED_FILTER_INNER_MAC_VNI_PAIR,
+       QED_FILTER_MAC_VNI_PAIR,
+       QED_FILTER_VNI,
+};
+
+struct qed_filter_ucast {
+       enum qed_filter_opcode          opcode;
+       enum qed_filter_ucast_type      type;
+       u8                              is_rx_filter;
+       u8                              is_tx_filter;
+       u8                              vport_to_add_to;
+       u8                              vport_to_remove_from;
+       unsigned char                   mac[ETH_ALEN];
+       u8                              assert_on_error;
+       u16                             vlan;
+       u32                             vni;
+};
+
+struct qed_filter_mcast {
+       /* MOVE is not supported for multicast */
+       enum qed_filter_opcode  opcode;
+       u8                      vport_to_add_to;
+       u8                      vport_to_remove_from;
+       u8                      num_mc_addrs;
+#define QED_MAX_MC_ADDRS        64
+       unsigned char           mac[QED_MAX_MC_ADDRS][ETH_ALEN];
+};
+
+struct qed_filter_accept_flags {
+       u8      update_rx_mode_config;
+       u8      update_tx_mode_config;
+       u8      rx_accept_filter;
+       u8      tx_accept_filter;
+#define QED_ACCEPT_NONE         0x01
+#define QED_ACCEPT_UCAST_MATCHED        0x02
+#define QED_ACCEPT_UCAST_UNMATCHED      0x04
+#define QED_ACCEPT_MCAST_MATCHED        0x08
+#define QED_ACCEPT_MCAST_UNMATCHED      0x10
+#define QED_ACCEPT_BCAST                0x20
+};
+
+struct qed_sp_vport_update_params {
+       u16                             opaque_fid;
+       u8                              vport_id;
+       u8                              update_vport_active_rx_flg;
+       u8                              vport_active_rx_flg;
+       u8                              update_vport_active_tx_flg;
+       u8                              vport_active_tx_flg;
+       u8                              update_approx_mcast_flg;
+       unsigned long                   bins[8];
+       struct qed_rss_params           *rss_params;
+       struct qed_filter_accept_flags  accept_flags;
+};
+
+#define QED_MAX_SGES_NUM 16
+#define CRC32_POLY 0x1edc6f41
+
+static int qed_sp_vport_start(struct qed_hwfn *p_hwfn,
+                             u32 concrete_fid,
+                             u16 opaque_fid,
+                             u8 vport_id,
+                             u16 mtu,
+                             u8 drop_ttl0_flg,
+                             u8 inner_vlan_removal_en_flg)
+{
+       struct qed_sp_init_request_params params;
+       struct vport_start_ramrod_data *p_ramrod = NULL;
+       struct qed_spq_entry *p_ent =  NULL;
+       int rc = -EINVAL;
+       u16 rx_mode = 0;
+       u8 abs_vport_id = 0;
+
+       rc = qed_fw_vport(p_hwfn, vport_id, &abs_vport_id);
+       if (rc != 0)
+               return rc;
+
+       memset(&params, 0, sizeof(params));
+       params.ramrod_data_size = sizeof(*p_ramrod);
+       params.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                qed_spq_get_cid(p_hwfn),
+                                opaque_fid,
+                                ETH_RAMROD_VPORT_START,
+                                PROTOCOLID_ETH,
+                                &params);
+       if (rc)
+               return rc;
+
+       p_ramrod                = &p_ent->ramrod.vport_start;
+       p_ramrod->vport_id      = abs_vport_id;
+
+       p_ramrod->mtu                   = cpu_to_le16(mtu);
+       p_ramrod->inner_vlan_removal_en = inner_vlan_removal_en_flg;
+       p_ramrod->drop_ttl0_en          = drop_ttl0_flg;
+
+       SET_FIELD(rx_mode, ETH_VPORT_RX_MODE_UCAST_DROP_ALL, 1);
+       SET_FIELD(rx_mode, ETH_VPORT_RX_MODE_MCAST_DROP_ALL, 1);
+
+       p_ramrod->rx_mode.state = cpu_to_le16(rx_mode);
+
+       /* TPA related fields */
+       memset(&p_ramrod->tpa_param, 0,
+              sizeof(struct eth_vport_tpa_param));
+
+       /* Software Function ID in hwfn (PFs are 0 - 15, VFs are 16 - 135) */
+       p_ramrod->sw_fid = qed_concrete_to_sw_fid(p_hwfn->cdev,
+                                                 concrete_fid);
+
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
+
+static int
+qed_sp_vport_update_rss(struct qed_hwfn *p_hwfn,
+                       struct vport_update_ramrod_data *p_ramrod,
+                       struct qed_rss_params *p_params)
+{
+       struct eth_vport_rss_config *rss = &p_ramrod->rss_config;
+       u16 abs_l2_queue = 0, capabilities = 0;
+       int rc = 0, i;
+
+       if (!p_params) {
+               p_ramrod->common.update_rss_flg = 0;
+               return rc;
+       }
+
+       BUILD_BUG_ON(QED_RSS_IND_TABLE_SIZE !=
+                    ETH_RSS_IND_TABLE_ENTRIES_NUM);
+
+       rc = qed_fw_rss_eng(p_hwfn, p_params->rss_eng_id, &rss->rss_id);
+       if (rc)
+               return rc;
+
+       p_ramrod->common.update_rss_flg = p_params->update_rss_config;
+       rss->update_rss_capabilities = p_params->update_rss_capabilities;
+       rss->update_rss_ind_table = p_params->update_rss_ind_table;
+       rss->update_rss_key = p_params->update_rss_key;
+
+       rss->rss_mode = p_params->rss_enable ?
+                       ETH_VPORT_RSS_MODE_REGULAR :
+                       ETH_VPORT_RSS_MODE_DISABLED;
+
+       SET_FIELD(capabilities,
+                 ETH_VPORT_RSS_CONFIG_IPV4_CAPABILITY,
+                 !!(p_params->rss_caps & QED_RSS_IPV4));
+       SET_FIELD(capabilities,
+                 ETH_VPORT_RSS_CONFIG_IPV6_CAPABILITY,
+                 !!(p_params->rss_caps & QED_RSS_IPV6));
+       SET_FIELD(capabilities,
+                 ETH_VPORT_RSS_CONFIG_IPV4_TCP_CAPABILITY,
+                 !!(p_params->rss_caps & QED_RSS_IPV4_TCP));
+       SET_FIELD(capabilities,
+                 ETH_VPORT_RSS_CONFIG_IPV6_TCP_CAPABILITY,
+                 !!(p_params->rss_caps & QED_RSS_IPV6_TCP));
+       SET_FIELD(capabilities,
+                 ETH_VPORT_RSS_CONFIG_IPV4_UDP_CAPABILITY,
+                 !!(p_params->rss_caps & QED_RSS_IPV4_UDP));
+       SET_FIELD(capabilities,
+                 ETH_VPORT_RSS_CONFIG_IPV6_UDP_CAPABILITY,
+                 !!(p_params->rss_caps & QED_RSS_IPV6_UDP));
+       rss->tbl_size = p_params->rss_table_size_log;
+
+       rss->capabilities = cpu_to_le16(capabilities);
+
+       DP_VERBOSE(p_hwfn, NETIF_MSG_IFUP,
+                  "update rss flag %d, rss_mode = %d, update_caps = %d, capabilities = %d, update_ind = %d, update_rss_key = %d\n",
+                  p_ramrod->common.update_rss_flg,
+                  rss->rss_mode, rss->update_rss_capabilities,
+                  capabilities, rss->update_rss_ind_table,
+                  rss->update_rss_key);
+
+       for (i = 0; i < QED_RSS_IND_TABLE_SIZE; i++) {
+               rc = qed_fw_l2_queue(p_hwfn,
+                                    (u8)p_params->rss_ind_table[i],
+                                    &abs_l2_queue);
+               if (rc)
+                       return rc;
+
+               rss->indirection_table[i] = cpu_to_le16(abs_l2_queue);
+               DP_VERBOSE(p_hwfn, NETIF_MSG_IFUP, "i= %d, queue = %d\n",
+                          i, rss->indirection_table[i]);
+       }
+
+       for (i = 0; i < 10; i++)
+               rss->rss_key[i] = cpu_to_le32(p_params->rss_key[i]);
+
+       return rc;
+}
+
+static void
+qed_sp_update_accept_mode(struct qed_hwfn *p_hwfn,
+                         struct vport_update_ramrod_data *p_ramrod,
+                         struct qed_filter_accept_flags accept_flags)
+{
+       p_ramrod->common.update_rx_mode_flg =
+               accept_flags.update_rx_mode_config;
+
+       p_ramrod->common.update_tx_mode_flg =
+               accept_flags.update_tx_mode_config;
+
+       /* Set Rx mode accept flags */
+       if (p_ramrod->common.update_rx_mode_flg) {
+               u8 accept_filter = accept_flags.rx_accept_filter;
+               u16 state = 0;
+
+               SET_FIELD(state, ETH_VPORT_RX_MODE_UCAST_DROP_ALL,
+                         !(!!(accept_filter & QED_ACCEPT_UCAST_MATCHED) ||
+                           !!(accept_filter & QED_ACCEPT_UCAST_UNMATCHED)));
+
+               SET_FIELD(state, ETH_VPORT_RX_MODE_UCAST_ACCEPT_UNMATCHED,
+                         !!(accept_filter & QED_ACCEPT_UCAST_UNMATCHED));
+
+               SET_FIELD(state, ETH_VPORT_RX_MODE_MCAST_DROP_ALL,
+                         !(!!(accept_filter & QED_ACCEPT_MCAST_MATCHED) ||
+                           !!(accept_filter & QED_ACCEPT_MCAST_UNMATCHED)));
+
+               SET_FIELD(state, ETH_VPORT_RX_MODE_MCAST_ACCEPT_ALL,
+                         (!!(accept_filter & QED_ACCEPT_MCAST_MATCHED) &&
+                          !!(accept_filter & QED_ACCEPT_MCAST_UNMATCHED)));
+
+               SET_FIELD(state, ETH_VPORT_RX_MODE_BCAST_ACCEPT_ALL,
+                         !!(accept_filter & QED_ACCEPT_BCAST));
+
+               p_ramrod->rx_mode.state = cpu_to_le16(state);
+               DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                          "p_ramrod->rx_mode.state = 0x%x\n", state);
+       }
+
+       /* Set Tx mode accept flags */
+       if (p_ramrod->common.update_tx_mode_flg) {
+               u8 accept_filter = accept_flags.tx_accept_filter;
+               u16 state = 0;
+
+               SET_FIELD(state, ETH_VPORT_TX_MODE_UCAST_DROP_ALL,
+                         !!(accept_filter & QED_ACCEPT_NONE));
+
+               SET_FIELD(state, ETH_VPORT_TX_MODE_UCAST_ACCEPT_ALL,
+                         (!!(accept_filter & QED_ACCEPT_UCAST_MATCHED) &&
+                          !!(accept_filter & QED_ACCEPT_UCAST_UNMATCHED)));
+
+               SET_FIELD(state, ETH_VPORT_TX_MODE_MCAST_DROP_ALL,
+                         !!(accept_filter & QED_ACCEPT_NONE));
+
+               SET_FIELD(state, ETH_VPORT_TX_MODE_MCAST_ACCEPT_ALL,
+                         (!!(accept_filter & QED_ACCEPT_MCAST_MATCHED) &&
+                          !!(accept_filter & QED_ACCEPT_MCAST_UNMATCHED)));
+
+               SET_FIELD(state, ETH_VPORT_TX_MODE_BCAST_ACCEPT_ALL,
+                         !!(accept_filter & QED_ACCEPT_BCAST));
+
+               p_ramrod->tx_mode.state = cpu_to_le16(state);
+               DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                          "p_ramrod->tx_mode.state = 0x%x\n", state);
+       }
+}
+
+static void
+qed_sp_update_mcast_bin(struct qed_hwfn *p_hwfn,
+                       struct vport_update_ramrod_data *p_ramrod,
+                       struct qed_sp_vport_update_params *p_params)
+{
+       int i;
+
+       memset(&p_ramrod->approx_mcast.bins, 0,
+              sizeof(p_ramrod->approx_mcast.bins));
+
+       if (p_params->update_approx_mcast_flg) {
+               p_ramrod->common.update_approx_mcast_flg = 1;
+               for (i = 0; i < ETH_MULTICAST_MAC_BINS_IN_REGS; i++) {
+                       u32 *p_bins = (u32 *)p_params->bins;
+                       __le32 val = cpu_to_le32(p_bins[i]);
+
+                       p_ramrod->approx_mcast.bins[i] = val;
+               }
+       }
+}
+
+static int
+qed_sp_vport_update(struct qed_hwfn *p_hwfn,
+                   struct qed_sp_vport_update_params *p_params,
+                   enum spq_mode comp_mode,
+                   struct qed_spq_comp_cb *p_comp_data)
+{
+       struct qed_rss_params *p_rss_params = p_params->rss_params;
+       struct vport_update_ramrod_data_cmn *p_cmn;
+       struct qed_sp_init_request_params sp_params;
+       struct vport_update_ramrod_data *p_ramrod = NULL;
+       struct qed_spq_entry *p_ent = NULL;
+       u8 abs_vport_id = 0;
+       int rc = -EINVAL;
+
+       rc = qed_fw_vport(p_hwfn, p_params->vport_id, &abs_vport_id);
+       if (rc != 0)
+               return rc;
+
+       memset(&sp_params, 0, sizeof(sp_params));
+       sp_params.ramrod_data_size = sizeof(*p_ramrod);
+       sp_params.comp_mode = comp_mode;
+       sp_params.p_comp_data = p_comp_data;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                qed_spq_get_cid(p_hwfn),
+                                p_params->opaque_fid,
+                                ETH_RAMROD_VPORT_UPDATE,
+                                PROTOCOLID_ETH,
+                                &sp_params);
+       if (rc)
+               return rc;
+
+       /* Copy input params to ramrod according to FW struct */
+       p_ramrod = &p_ent->ramrod.vport_update;
+       p_cmn = &p_ramrod->common;
+
+       p_cmn->vport_id = abs_vport_id;
+       p_cmn->rx_active_flg = p_params->vport_active_rx_flg;
+       p_cmn->update_rx_active_flg = p_params->update_vport_active_rx_flg;
+       p_cmn->tx_active_flg = p_params->vport_active_tx_flg;
+       p_cmn->update_tx_active_flg = p_params->update_vport_active_tx_flg;
+
+       rc = qed_sp_vport_update_rss(p_hwfn, p_ramrod, p_rss_params);
+       if (rc) {
+               /* Return spq entry which is taken in qed_sp_init_request()*/
+               qed_spq_return_entry(p_hwfn, p_ent);
+               return rc;
+       }
+
+       /* Update mcast bins for VFs, PF doesn't use this functionality */
+       qed_sp_update_mcast_bin(p_hwfn, p_ramrod, p_params);
+
+       qed_sp_update_accept_mode(p_hwfn, p_ramrod, p_params->accept_flags);
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
+
+static int qed_sp_vport_stop(struct qed_hwfn *p_hwfn,
+                            u16 opaque_fid,
+                            u8 vport_id)
+{
+       struct qed_sp_init_request_params sp_params;
+       struct vport_stop_ramrod_data *p_ramrod;
+       struct qed_spq_entry *p_ent;
+       u8 abs_vport_id = 0;
+       int rc;
+
+       rc = qed_fw_vport(p_hwfn, vport_id, &abs_vport_id);
+       if (rc != 0)
+               return rc;
+
+       memset(&sp_params, 0, sizeof(sp_params));
+       sp_params.ramrod_data_size = sizeof(*p_ramrod);
+       sp_params.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                qed_spq_get_cid(p_hwfn),
+                                opaque_fid,
+                                ETH_RAMROD_VPORT_STOP,
+                                PROTOCOLID_ETH,
+                                &sp_params);
+       if (rc)
+               return rc;
+
+       p_ramrod = &p_ent->ramrod.vport_stop;
+       p_ramrod->vport_id = abs_vport_id;
+
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
+
+static int qed_filter_accept_cmd(struct qed_dev *cdev,
+                                u8 vport,
+                                struct qed_filter_accept_flags accept_flags,
+                                enum spq_mode comp_mode,
+                                struct qed_spq_comp_cb *p_comp_data)
+{
+       struct qed_sp_vport_update_params vport_update_params;
+       int i, rc;
+
+       /* Prepare and send the vport rx_mode change */
+       memset(&vport_update_params, 0, sizeof(vport_update_params));
+       vport_update_params.vport_id = vport;
+       vport_update_params.accept_flags = accept_flags;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               vport_update_params.opaque_fid = p_hwfn->hw_info.opaque_fid;
+
+               rc = qed_sp_vport_update(p_hwfn, &vport_update_params,
+                                        comp_mode, p_comp_data);
+               if (rc != 0) {
+                       DP_ERR(cdev, "Update rx_mode failed %d\n", rc);
+                       return rc;
+               }
+
+               DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                          "Accept filter configured, flags = [Rx]%x [Tx]%x\n",
+                          accept_flags.rx_accept_filter,
+                          accept_flags.tx_accept_filter);
+       }
+
+       return 0;
+}
+
+static int qed_sp_release_queue_cid(
+       struct qed_hwfn *p_hwfn,
+       struct qed_hw_cid_data *p_cid_data)
+{
+       if (!p_cid_data->b_cid_allocated)
+               return 0;
+
+       qed_cxt_release_cid(p_hwfn, p_cid_data->cid);
+
+       p_cid_data->b_cid_allocated = false;
+
+       return 0;
+}
+
+static int
+qed_sp_eth_rxq_start_ramrod(struct qed_hwfn *p_hwfn,
+                           u16 opaque_fid,
+                           u32 cid,
+                           struct qed_queue_start_common_params *params,
+                           u8 stats_id,
+                           u16 bd_max_bytes,
+                           dma_addr_t bd_chain_phys_addr,
+                           dma_addr_t cqe_pbl_addr,
+                           u16 cqe_pbl_size)
+{
+       struct rx_queue_start_ramrod_data *p_ramrod = NULL;
+       struct qed_sp_init_request_params sp_params;
+       struct qed_spq_entry *p_ent = NULL;
+       struct qed_hw_cid_data *p_rx_cid;
+       u16 abs_rx_q_id = 0;
+       u8 abs_vport_id = 0;
+       int rc = -EINVAL;
+
+       /* Store information for the stop */
+       p_rx_cid                = &p_hwfn->p_rx_cids[params->queue_id];
+       p_rx_cid->cid           = cid;
+       p_rx_cid->opaque_fid    = opaque_fid;
+       p_rx_cid->vport_id      = params->vport_id;
+
+       rc = qed_fw_vport(p_hwfn, params->vport_id, &abs_vport_id);
+       if (rc != 0)
+               return rc;
+
+       rc = qed_fw_l2_queue(p_hwfn, params->queue_id, &abs_rx_q_id);
+       if (rc != 0)
+               return rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                  "opaque_fid=0x%x, cid=0x%x, rx_qid=0x%x, vport_id=0x%x, sb_id=0x%x\n",
+                  opaque_fid, cid, params->queue_id, params->vport_id,
+                  params->sb);
+
+       memset(&sp_params, 0, sizeof(params));
+       sp_params.comp_mode = QED_SPQ_MODE_EBLOCK;
+       sp_params.ramrod_data_size = sizeof(*p_ramrod);
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                cid, opaque_fid,
+                                ETH_RAMROD_RX_QUEUE_START,
+                                PROTOCOLID_ETH,
+                                &sp_params);
+       if (rc)
+               return rc;
+
+       p_ramrod = &p_ent->ramrod.rx_queue_start;
+
+       p_ramrod->sb_id                 = cpu_to_le16(params->sb);
+       p_ramrod->sb_index              = params->sb_idx;
+       p_ramrod->vport_id              = abs_vport_id;
+       p_ramrod->stats_counter_id      = stats_id;
+       p_ramrod->rx_queue_id           = cpu_to_le16(abs_rx_q_id);
+       p_ramrod->complete_cqe_flg      = 0;
+       p_ramrod->complete_event_flg    = 1;
+
+       p_ramrod->bd_max_bytes  = cpu_to_le16(bd_max_bytes);
+       p_ramrod->bd_base.hi    = DMA_HI_LE(bd_chain_phys_addr);
+       p_ramrod->bd_base.lo    = DMA_LO_LE(bd_chain_phys_addr);
+
+       p_ramrod->num_of_pbl_pages      = cpu_to_le16(cqe_pbl_size);
+       p_ramrod->cqe_pbl_addr.hi       = DMA_HI_LE(cqe_pbl_addr);
+       p_ramrod->cqe_pbl_addr.lo       = DMA_LO_LE(cqe_pbl_addr);
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+
+       return rc;
+}
+
+static int
+qed_sp_eth_rx_queue_start(struct qed_hwfn *p_hwfn,
+                         u16 opaque_fid,
+                         struct qed_queue_start_common_params *params,
+                         u16 bd_max_bytes,
+                         dma_addr_t bd_chain_phys_addr,
+                         dma_addr_t cqe_pbl_addr,
+                         u16 cqe_pbl_size,
+                         void __iomem **pp_prod)
+{
+       struct qed_hw_cid_data *p_rx_cid;
+       u64 init_prod_val = 0;
+       u16 abs_l2_queue = 0;
+       u8 abs_stats_id = 0;
+       int rc;
+
+       rc = qed_fw_l2_queue(p_hwfn, params->queue_id, &abs_l2_queue);
+       if (rc != 0)
+               return rc;
+
+       rc = qed_fw_vport(p_hwfn, params->vport_id, &abs_stats_id);
+       if (rc != 0)
+               return rc;
+
+       *pp_prod = (u8 __iomem *)p_hwfn->regview +
+                                GTT_BAR0_MAP_REG_MSDM_RAM +
+                                MSTORM_PRODS_OFFSET(abs_l2_queue);
+
+       /* Init the rcq, rx bd and rx sge (if valid) producers to 0 */
+       __internal_ram_wr(p_hwfn, *pp_prod, sizeof(u64),
+                         (u32 *)(&init_prod_val));
+
+       /* Allocate a CID for the queue */
+       p_rx_cid = &p_hwfn->p_rx_cids[params->queue_id];
+       rc = qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_ETH,
+                                &p_rx_cid->cid);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "Failed to acquire cid\n");
+               return rc;
+       }
+       p_rx_cid->b_cid_allocated = true;
+
+       rc = qed_sp_eth_rxq_start_ramrod(p_hwfn,
+                                        opaque_fid,
+                                        p_rx_cid->cid,
+                                        params,
+                                        abs_stats_id,
+                                        bd_max_bytes,
+                                        bd_chain_phys_addr,
+                                        cqe_pbl_addr,
+                                        cqe_pbl_size);
+
+       if (rc != 0)
+               qed_sp_release_queue_cid(p_hwfn, p_rx_cid);
+
+       return rc;
+}
+
+static int qed_sp_eth_rx_queue_stop(struct qed_hwfn *p_hwfn,
+                                   u16 rx_queue_id,
+                                   bool eq_completion_only,
+                                   bool cqe_completion)
+{
+       struct qed_hw_cid_data *p_rx_cid = &p_hwfn->p_rx_cids[rx_queue_id];
+       struct rx_queue_stop_ramrod_data *p_ramrod = NULL;
+       struct qed_sp_init_request_params sp_params;
+       struct qed_spq_entry *p_ent = NULL;
+       u16 abs_rx_q_id = 0;
+       int rc = -EINVAL;
+
+       memset(&sp_params, 0, sizeof(sp_params));
+       sp_params.ramrod_data_size = sizeof(*p_ramrod);
+       sp_params.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                p_rx_cid->cid,
+                                p_rx_cid->opaque_fid,
+                                ETH_RAMROD_RX_QUEUE_STOP,
+                                PROTOCOLID_ETH,
+                                &sp_params);
+       if (rc)
+               return rc;
+
+       p_ramrod = &p_ent->ramrod.rx_queue_stop;
+
+       qed_fw_vport(p_hwfn, p_rx_cid->vport_id, &p_ramrod->vport_id);
+       qed_fw_l2_queue(p_hwfn, rx_queue_id, &abs_rx_q_id);
+       p_ramrod->rx_queue_id = cpu_to_le16(abs_rx_q_id);
+
+       /* Cleaning the queue requires the completion to arrive there.
+        * In addition, VFs require the answer to come as eqe to PF.
+        */
+       p_ramrod->complete_cqe_flg =
+               (!!(p_rx_cid->opaque_fid == p_hwfn->hw_info.opaque_fid) &&
+                !eq_completion_only) || cqe_completion;
+       p_ramrod->complete_event_flg =
+               !(p_rx_cid->opaque_fid == p_hwfn->hw_info.opaque_fid) ||
+               eq_completion_only;
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+       if (rc)
+               return rc;
+
+       return qed_sp_release_queue_cid(p_hwfn, p_rx_cid);
+}
+
+static int
+qed_sp_eth_txq_start_ramrod(struct qed_hwfn  *p_hwfn,
+                           u16  opaque_fid,
+                           u32  cid,
+                           struct qed_queue_start_common_params *p_params,
+                           u8  stats_id,
+                           dma_addr_t pbl_addr,
+                           u16 pbl_size,
+                           union qed_qm_pq_params *p_pq_params)
+{
+       struct tx_queue_start_ramrod_data *p_ramrod = NULL;
+       struct qed_sp_init_request_params sp_params;
+       struct qed_spq_entry *p_ent = NULL;
+       struct qed_hw_cid_data *p_tx_cid;
+       u8 abs_vport_id;
+       int rc = -EINVAL;
+       u16 pq_id;
+
+       /* Store information for the stop */
+       p_tx_cid = &p_hwfn->p_tx_cids[p_params->queue_id];
+       p_tx_cid->cid           = cid;
+       p_tx_cid->opaque_fid    = opaque_fid;
+
+       rc = qed_fw_vport(p_hwfn, p_params->vport_id, &abs_vport_id);
+       if (rc)
+               return rc;
+
+       memset(&sp_params, 0, sizeof(sp_params));
+       sp_params.ramrod_data_size = sizeof(*p_ramrod);
+       sp_params.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent, cid,
+                                opaque_fid,
+                                ETH_RAMROD_TX_QUEUE_START,
+                                PROTOCOLID_ETH,
+                                &sp_params);
+       if (rc)
+               return rc;
+
+       p_ramrod                = &p_ent->ramrod.tx_queue_start;
+       p_ramrod->vport_id      = abs_vport_id;
+
+       p_ramrod->sb_id                 = cpu_to_le16(p_params->sb);
+       p_ramrod->sb_index              = p_params->sb_idx;
+       p_ramrod->stats_counter_id      = stats_id;
+       p_ramrod->tc                    = p_pq_params->eth.tc;
+
+       p_ramrod->pbl_size              = cpu_to_le16(pbl_size);
+       p_ramrod->pbl_base_addr.hi      = DMA_HI_LE(pbl_addr);
+       p_ramrod->pbl_base_addr.lo      = DMA_LO_LE(pbl_addr);
+
+       pq_id                   = qed_get_qm_pq(p_hwfn,
+                                               PROTOCOLID_ETH,
+                                               p_pq_params);
+       p_ramrod->qm_pq_id      = cpu_to_le16(pq_id);
+
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
+
+static int
+qed_sp_eth_tx_queue_start(struct qed_hwfn *p_hwfn,
+                         u16 opaque_fid,
+                         struct qed_queue_start_common_params *p_params,
+                         dma_addr_t pbl_addr,
+                         u16 pbl_size,
+                         void __iomem **pp_doorbell)
+{
+       struct qed_hw_cid_data *p_tx_cid;
+       union qed_qm_pq_params pq_params;
+       u8 abs_stats_id = 0;
+       int rc;
+
+       rc = qed_fw_vport(p_hwfn, p_params->vport_id, &abs_stats_id);
+       if (rc)
+               return rc;
+
+       p_tx_cid = &p_hwfn->p_tx_cids[p_params->queue_id];
+       memset(p_tx_cid, 0, sizeof(*p_tx_cid));
+       memset(&pq_params, 0, sizeof(pq_params));
+
+       /* Allocate a CID for the queue */
+       rc = qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_ETH,
+                                &p_tx_cid->cid);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "Failed to acquire cid\n");
+               return rc;
+       }
+       p_tx_cid->b_cid_allocated = true;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                  "opaque_fid=0x%x, cid=0x%x, tx_qid=0x%x, vport_id=0x%x, sb_id=0x%x\n",
+                  opaque_fid, p_tx_cid->cid,
+                  p_params->queue_id, p_params->vport_id, p_params->sb);
+
+       rc = qed_sp_eth_txq_start_ramrod(p_hwfn,
+                                        opaque_fid,
+                                        p_tx_cid->cid,
+                                        p_params,
+                                        abs_stats_id,
+                                        pbl_addr,
+                                        pbl_size,
+                                        &pq_params);
+
+       *pp_doorbell = (u8 __iomem *)p_hwfn->doorbells +
+                                    qed_db_addr(p_tx_cid->cid, DQ_DEMS_LEGACY);
+
+       if (rc)
+               qed_sp_release_queue_cid(p_hwfn, p_tx_cid);
+
+       return rc;
+}
+
+static int qed_sp_eth_tx_queue_stop(struct qed_hwfn *p_hwfn,
+                                   u16 tx_queue_id)
+{
+       struct qed_hw_cid_data *p_tx_cid = &p_hwfn->p_tx_cids[tx_queue_id];
+       struct qed_sp_init_request_params sp_params;
+       struct qed_spq_entry *p_ent = NULL;
+       int rc = -EINVAL;
+
+       memset(&sp_params, 0, sizeof(sp_params));
+       sp_params.ramrod_data_size = sizeof(struct tx_queue_stop_ramrod_data);
+       sp_params.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                p_tx_cid->cid,
+                                p_tx_cid->opaque_fid,
+                                ETH_RAMROD_TX_QUEUE_STOP,
+                                PROTOCOLID_ETH,
+                                &sp_params);
+       if (rc)
+               return rc;
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+       if (rc)
+               return rc;
+
+       return qed_sp_release_queue_cid(p_hwfn, p_tx_cid);
+}
+
+static enum eth_filter_action
+qed_filter_action(enum qed_filter_opcode opcode)
+{
+       enum eth_filter_action action = MAX_ETH_FILTER_ACTION;
+
+       switch (opcode) {
+       case QED_FILTER_ADD:
+               action = ETH_FILTER_ACTION_ADD;
+               break;
+       case QED_FILTER_REMOVE:
+               action = ETH_FILTER_ACTION_REMOVE;
+               break;
+       case QED_FILTER_REPLACE:
+       case QED_FILTER_FLUSH:
+               action = ETH_FILTER_ACTION_REPLACE;
+               break;
+       default:
+               action = MAX_ETH_FILTER_ACTION;
+       }
+
+       return action;
+}
+
+static void qed_set_fw_mac_addr(__le16 *fw_msb,
+                               __le16 *fw_mid,
+                               __le16 *fw_lsb,
+                               u8 *mac)
+{
+       ((u8 *)fw_msb)[0] = mac[1];
+       ((u8 *)fw_msb)[1] = mac[0];
+       ((u8 *)fw_mid)[0] = mac[3];
+       ((u8 *)fw_mid)[1] = mac[2];
+       ((u8 *)fw_lsb)[0] = mac[5];
+       ((u8 *)fw_lsb)[1] = mac[4];
+}
+
+static int
+qed_filter_ucast_common(struct qed_hwfn *p_hwfn,
+                       u16 opaque_fid,
+                       struct qed_filter_ucast *p_filter_cmd,
+                       struct vport_filter_update_ramrod_data **pp_ramrod,
+                       struct qed_spq_entry **pp_ent,
+                       enum spq_mode comp_mode,
+                       struct qed_spq_comp_cb *p_comp_data)
+{
+       u8 vport_to_add_to = 0, vport_to_remove_from = 0;
+       struct vport_filter_update_ramrod_data *p_ramrod;
+       struct qed_sp_init_request_params sp_params;
+       struct eth_filter_cmd *p_first_filter;
+       struct eth_filter_cmd *p_second_filter;
+       enum eth_filter_action action;
+       int rc;
+
+       rc = qed_fw_vport(p_hwfn, p_filter_cmd->vport_to_remove_from,
+                         &vport_to_remove_from);
+       if (rc)
+               return rc;
+
+       rc = qed_fw_vport(p_hwfn, p_filter_cmd->vport_to_add_to,
+                         &vport_to_add_to);
+       if (rc)
+               return rc;
+
+       memset(&sp_params, 0, sizeof(sp_params));
+       sp_params.ramrod_data_size = sizeof(**pp_ramrod);
+       sp_params.comp_mode = comp_mode;
+       sp_params.p_comp_data = p_comp_data;
+
+       rc = qed_sp_init_request(p_hwfn, pp_ent,
+                                qed_spq_get_cid(p_hwfn),
+                                opaque_fid,
+                                ETH_RAMROD_FILTERS_UPDATE,
+                                PROTOCOLID_ETH,
+                                &sp_params);
+       if (rc)
+               return rc;
+
+       *pp_ramrod = &(*pp_ent)->ramrod.vport_filter_update;
+       p_ramrod = *pp_ramrod;
+       p_ramrod->filter_cmd_hdr.rx = p_filter_cmd->is_rx_filter ? 1 : 0;
+       p_ramrod->filter_cmd_hdr.tx = p_filter_cmd->is_tx_filter ? 1 : 0;
+
+       switch (p_filter_cmd->opcode) {
+       case QED_FILTER_FLUSH:
+               p_ramrod->filter_cmd_hdr.cmd_cnt = 0; break;
+       case QED_FILTER_MOVE:
+               p_ramrod->filter_cmd_hdr.cmd_cnt = 2; break;
+       default:
+               p_ramrod->filter_cmd_hdr.cmd_cnt = 1; break;
+       }
+
+       p_first_filter  = &p_ramrod->filter_cmds[0];
+       p_second_filter = &p_ramrod->filter_cmds[1];
+
+       switch (p_filter_cmd->type) {
+       case QED_FILTER_MAC:
+               p_first_filter->type = ETH_FILTER_TYPE_MAC; break;
+       case QED_FILTER_VLAN:
+               p_first_filter->type = ETH_FILTER_TYPE_VLAN; break;
+       case QED_FILTER_MAC_VLAN:
+               p_first_filter->type = ETH_FILTER_TYPE_PAIR; break;
+       case QED_FILTER_INNER_MAC:
+               p_first_filter->type = ETH_FILTER_TYPE_INNER_MAC; break;
+       case QED_FILTER_INNER_VLAN:
+               p_first_filter->type = ETH_FILTER_TYPE_INNER_VLAN; break;
+       case QED_FILTER_INNER_PAIR:
+               p_first_filter->type = ETH_FILTER_TYPE_INNER_PAIR; break;
+       case QED_FILTER_INNER_MAC_VNI_PAIR:
+               p_first_filter->type = ETH_FILTER_TYPE_INNER_MAC_VNI_PAIR;
+               break;
+       case QED_FILTER_MAC_VNI_PAIR:
+               p_first_filter->type = ETH_FILTER_TYPE_MAC_VNI_PAIR; break;
+       case QED_FILTER_VNI:
+               p_first_filter->type = ETH_FILTER_TYPE_VNI; break;
+       }
+
+       if ((p_first_filter->type == ETH_FILTER_TYPE_MAC) ||
+           (p_first_filter->type == ETH_FILTER_TYPE_PAIR) ||
+           (p_first_filter->type == ETH_FILTER_TYPE_INNER_MAC) ||
+           (p_first_filter->type == ETH_FILTER_TYPE_INNER_PAIR) ||
+           (p_first_filter->type == ETH_FILTER_TYPE_INNER_MAC_VNI_PAIR) ||
+           (p_first_filter->type == ETH_FILTER_TYPE_MAC_VNI_PAIR)) {
+               qed_set_fw_mac_addr(&p_first_filter->mac_msb,
+                                   &p_first_filter->mac_mid,
+                                   &p_first_filter->mac_lsb,
+                                   (u8 *)p_filter_cmd->mac);
+       }
+
+       if ((p_first_filter->type == ETH_FILTER_TYPE_VLAN) ||
+           (p_first_filter->type == ETH_FILTER_TYPE_PAIR) ||
+           (p_first_filter->type == ETH_FILTER_TYPE_INNER_VLAN) ||
+           (p_first_filter->type == ETH_FILTER_TYPE_INNER_PAIR))
+               p_first_filter->vlan_id = cpu_to_le16(p_filter_cmd->vlan);
+
+       if ((p_first_filter->type == ETH_FILTER_TYPE_INNER_MAC_VNI_PAIR) ||
+           (p_first_filter->type == ETH_FILTER_TYPE_MAC_VNI_PAIR) ||
+           (p_first_filter->type == ETH_FILTER_TYPE_VNI))
+               p_first_filter->vni = cpu_to_le32(p_filter_cmd->vni);
+
+       if (p_filter_cmd->opcode == QED_FILTER_MOVE) {
+               p_second_filter->type           = p_first_filter->type;
+               p_second_filter->mac_msb        = p_first_filter->mac_msb;
+               p_second_filter->mac_mid        = p_first_filter->mac_mid;
+               p_second_filter->mac_lsb        = p_first_filter->mac_lsb;
+               p_second_filter->vlan_id        = p_first_filter->vlan_id;
+               p_second_filter->vni            = p_first_filter->vni;
+
+               p_first_filter->action = ETH_FILTER_ACTION_REMOVE;
+
+               p_first_filter->vport_id = vport_to_remove_from;
+
+               p_second_filter->action         = ETH_FILTER_ACTION_ADD;
+               p_second_filter->vport_id       = vport_to_add_to;
+       } else {
+               action = qed_filter_action(p_filter_cmd->opcode);
+
+               if (action == MAX_ETH_FILTER_ACTION) {
+                       DP_NOTICE(p_hwfn,
+                                 "%d is not supported yet\n",
+                                 p_filter_cmd->opcode);
+                       return -EINVAL;
+               }
+
+               p_first_filter->action = action;
+               p_first_filter->vport_id = (p_filter_cmd->opcode ==
+                                           QED_FILTER_REMOVE) ?
+                                          vport_to_remove_from :
+                                          vport_to_add_to;
+       }
+
+       return 0;
+}
+
+static int qed_sp_eth_filter_ucast(struct qed_hwfn *p_hwfn,
+                                  u16 opaque_fid,
+                                  struct qed_filter_ucast *p_filter_cmd,
+                                  enum spq_mode comp_mode,
+                                  struct qed_spq_comp_cb *p_comp_data)
+{
+       struct vport_filter_update_ramrod_data  *p_ramrod       = NULL;
+       struct qed_spq_entry                    *p_ent          = NULL;
+       struct eth_filter_cmd_header            *p_header;
+       int                                     rc;
+
+       rc = qed_filter_ucast_common(p_hwfn, opaque_fid, p_filter_cmd,
+                                    &p_ramrod, &p_ent,
+                                    comp_mode, p_comp_data);
+       if (rc != 0) {
+               DP_ERR(p_hwfn, "Uni. filter command failed %d\n", rc);
+               return rc;
+       }
+       p_header = &p_ramrod->filter_cmd_hdr;
+       p_header->assert_on_error = p_filter_cmd->assert_on_error;
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+       if (rc != 0) {
+               DP_ERR(p_hwfn,
+                      "Unicast filter ADD command failed %d\n",
+                      rc);
+               return rc;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                  "Unicast filter configured, opcode = %s, type = %s, cmd_cnt = %d, is_rx_filter = %d, is_tx_filter = %d\n",
+                  (p_filter_cmd->opcode == QED_FILTER_ADD) ? "ADD" :
+                  ((p_filter_cmd->opcode == QED_FILTER_REMOVE) ?
+                  "REMOVE" :
+                  ((p_filter_cmd->opcode == QED_FILTER_MOVE) ?
+                   "MOVE" : "REPLACE")),
+                  (p_filter_cmd->type == QED_FILTER_MAC) ? "MAC" :
+                  ((p_filter_cmd->type == QED_FILTER_VLAN) ?
+                   "VLAN" : "MAC & VLAN"),
+                  p_ramrod->filter_cmd_hdr.cmd_cnt,
+                  p_filter_cmd->is_rx_filter,
+                  p_filter_cmd->is_tx_filter);
+       DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                  "vport_to_add_to = %d, vport_to_remove_from = %d, mac = %2x:%2x:%2x:%2x:%2x:%2x, vlan = %d\n",
+                  p_filter_cmd->vport_to_add_to,
+                  p_filter_cmd->vport_to_remove_from,
+                  p_filter_cmd->mac[0],
+                  p_filter_cmd->mac[1],
+                  p_filter_cmd->mac[2],
+                  p_filter_cmd->mac[3],
+                  p_filter_cmd->mac[4],
+                  p_filter_cmd->mac[5],
+                  p_filter_cmd->vlan);
+
+       return 0;
+}
+
+/*******************************************************************************
+ * Description:
+ *         Calculates crc 32 on a buffer
+ *         Note: crc32_length MUST be aligned to 8
+ * Return:
+ ******************************************************************************/
+static u32 qed_calc_crc32c(u8 *crc32_packet,
+                          u32 crc32_length,
+                          u32 crc32_seed,
+                          u8 complement)
+{
+       u32 byte = 0;
+       u32 bit = 0;
+       u8 msb = 0;
+       u8 current_byte = 0;
+       u32 crc32_result = crc32_seed;
+
+       if ((!crc32_packet) ||
+           (crc32_length == 0) ||
+           ((crc32_length % 8) != 0))
+               return crc32_result;
+       for (byte = 0; byte < crc32_length; byte++) {
+               current_byte = crc32_packet[byte];
+               for (bit = 0; bit < 8; bit++) {
+                       msb = (u8)(crc32_result >> 31);
+                       crc32_result = crc32_result << 1;
+                       if (msb != (0x1 & (current_byte >> bit))) {
+                               crc32_result = crc32_result ^ CRC32_POLY;
+                               crc32_result |= 1; /*crc32_result[0] = 1;*/
+                       }
+               }
+       }
+       return crc32_result;
+}
+
+static inline u32 qed_crc32c_le(u32 seed,
+                               u8 *mac,
+                               u32 len)
+{
+       u32 packet_buf[2] = { 0 };
+
+       memcpy((u8 *)(&packet_buf[0]), &mac[0], 6);
+       return qed_calc_crc32c((u8 *)packet_buf, 8, seed, 0);
+}
+
+static u8 qed_mcast_bin_from_mac(u8 *mac)
+{
+       u32 crc = qed_crc32c_le(ETH_MULTICAST_BIN_FROM_MAC_SEED,
+                               mac, ETH_ALEN);
+
+       return crc & 0xff;
+}
+
+static int
+qed_sp_eth_filter_mcast(struct qed_hwfn *p_hwfn,
+                       u16 opaque_fid,
+                       struct qed_filter_mcast *p_filter_cmd,
+                       enum spq_mode comp_mode,
+                       struct qed_spq_comp_cb *p_comp_data)
+{
+       unsigned long bins[ETH_MULTICAST_MAC_BINS_IN_REGS];
+       struct vport_update_ramrod_data *p_ramrod = NULL;
+       struct qed_sp_init_request_params sp_params;
+       struct qed_spq_entry *p_ent = NULL;
+       u8 abs_vport_id = 0;
+       int rc, i;
+
+       if (p_filter_cmd->opcode == QED_FILTER_ADD) {
+               rc = qed_fw_vport(p_hwfn, p_filter_cmd->vport_to_add_to,
+                                 &abs_vport_id);
+               if (rc)
+                       return rc;
+       } else {
+               rc = qed_fw_vport(p_hwfn, p_filter_cmd->vport_to_remove_from,
+                                 &abs_vport_id);
+               if (rc)
+                       return rc;
+       }
+
+       memset(&sp_params, 0, sizeof(sp_params));
+       sp_params.ramrod_data_size = sizeof(*p_ramrod);
+       sp_params.comp_mode = comp_mode;
+       sp_params.p_comp_data = p_comp_data;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                qed_spq_get_cid(p_hwfn),
+                                p_hwfn->hw_info.opaque_fid,
+                                ETH_RAMROD_VPORT_UPDATE,
+                                PROTOCOLID_ETH,
+                                &sp_params);
+
+       if (rc) {
+               DP_ERR(p_hwfn, "Multi-cast command failed %d\n", rc);
+               return rc;
+       }
+
+       p_ramrod = &p_ent->ramrod.vport_update;
+       p_ramrod->common.update_approx_mcast_flg = 1;
+
+       /* explicitly clear out the entire vector */
+       memset(&p_ramrod->approx_mcast.bins, 0,
+              sizeof(p_ramrod->approx_mcast.bins));
+       memset(bins, 0, sizeof(unsigned long) *
+              ETH_MULTICAST_MAC_BINS_IN_REGS);
+       /* filter ADD op is explicit set op and it removes
+        *  any existing filters for the vport
+        */
+       if (p_filter_cmd->opcode == QED_FILTER_ADD) {
+               for (i = 0; i < p_filter_cmd->num_mc_addrs; i++) {
+                       u32 bit;
+
+                       bit = qed_mcast_bin_from_mac(p_filter_cmd->mac[i]);
+                       __set_bit(bit, bins);
+               }
+
+               /* Convert to correct endianity */
+               for (i = 0; i < ETH_MULTICAST_MAC_BINS_IN_REGS; i++) {
+                       u32 *p_bins = (u32 *)bins;
+                       struct vport_update_ramrod_mcast *approx_mcast;
+
+                       approx_mcast = &p_ramrod->approx_mcast;
+                       approx_mcast->bins[i] = cpu_to_le32(p_bins[i]);
+               }
+       }
+
+       p_ramrod->common.vport_id = abs_vport_id;
+
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
+
+static int
+qed_filter_mcast_cmd(struct qed_dev *cdev,
+                    struct qed_filter_mcast *p_filter_cmd,
+                    enum spq_mode comp_mode,
+                    struct qed_spq_comp_cb *p_comp_data)
+{
+       int rc = 0;
+       int i;
+
+       /* only ADD and REMOVE operations are supported for multi-cast */
+       if ((p_filter_cmd->opcode != QED_FILTER_ADD &&
+            (p_filter_cmd->opcode != QED_FILTER_REMOVE)) ||
+           (p_filter_cmd->num_mc_addrs > QED_MAX_MC_ADDRS))
+               return -EINVAL;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               u16 opaque_fid;
+
+               if (rc != 0)
+                       break;
+
+               opaque_fid = p_hwfn->hw_info.opaque_fid;
+
+               rc = qed_sp_eth_filter_mcast(p_hwfn,
+                                            opaque_fid,
+                                            p_filter_cmd,
+                                            comp_mode,
+                                            p_comp_data);
+       }
+       return rc;
+}
+
+static int qed_filter_ucast_cmd(struct qed_dev *cdev,
+                               struct qed_filter_ucast *p_filter_cmd,
+                               enum spq_mode comp_mode,
+                               struct qed_spq_comp_cb *p_comp_data)
+{
+       int rc = 0;
+       int i;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+               u16 opaque_fid;
+
+               if (rc != 0)
+                       break;
+
+               opaque_fid = p_hwfn->hw_info.opaque_fid;
+
+               rc = qed_sp_eth_filter_ucast(p_hwfn,
+                                            opaque_fid,
+                                            p_filter_cmd,
+                                            comp_mode,
+                                            p_comp_data);
+       }
+
+       return rc;
+}
+
+static int qed_fill_eth_dev_info(struct qed_dev *cdev,
+                                struct qed_dev_eth_info *info)
+{
+       int i;
+
+       memset(info, 0, sizeof(*info));
+
+       info->num_tc = 1;
+
+       if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) {
+               for_each_hwfn(cdev, i)
+                       info->num_queues += FEAT_NUM(&cdev->hwfns[i],
+                                                    QED_PF_L2_QUE);
+               if (cdev->int_params.fp_msix_cnt)
+                       info->num_queues = min_t(u8, info->num_queues,
+                                                cdev->int_params.fp_msix_cnt);
+       } else {
+               info->num_queues = cdev->num_hwfns;
+       }
+
+       info->num_vlan_filters = RESC_NUM(&cdev->hwfns[0], QED_VLAN);
+       ether_addr_copy(info->port_mac,
+                       cdev->hwfns[0].hw_info.hw_mac_addr);
+
+       qed_fill_dev_info(cdev, &info->common);
+
+       return 0;
+}
+
+static void qed_register_eth_ops(struct qed_dev *cdev,
+                                struct qed_eth_cb_ops *ops,
+                                void *cookie)
+{
+       cdev->protocol_ops.eth  = ops;
+       cdev->ops_cookie        = cookie;
+}
+
+static int qed_start_vport(struct qed_dev *cdev,
+                          u8 vport_id,
+                          u16 mtu,
+                          u8 drop_ttl0_flg,
+                          u8 inner_vlan_removal_en_flg)
+{
+       int rc, i;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               rc = qed_sp_vport_start(p_hwfn,
+                                       p_hwfn->hw_info.concrete_fid,
+                                       p_hwfn->hw_info.opaque_fid,
+                                       vport_id,
+                                       mtu,
+                                       drop_ttl0_flg,
+                                       inner_vlan_removal_en_flg);
+
+               if (rc) {
+                       DP_ERR(cdev, "Failed to start VPORT\n");
+                       return rc;
+               }
+
+               qed_hw_start_fastpath(p_hwfn);
+
+               DP_VERBOSE(cdev, (QED_MSG_SPQ | NETIF_MSG_IFUP),
+                          "Started V-PORT %d with MTU %d\n",
+                          vport_id, mtu);
+       }
+
+       qed_reset_vport_stats(cdev);
+
+       return 0;
+}
+
+static int qed_stop_vport(struct qed_dev *cdev,
+                         u8 vport_id)
+{
+       int rc, i;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               rc = qed_sp_vport_stop(p_hwfn,
+                                      p_hwfn->hw_info.opaque_fid,
+                                      vport_id);
+
+               if (rc) {
+                       DP_ERR(cdev, "Failed to stop VPORT\n");
+                       return rc;
+               }
+       }
+       return 0;
+}
+
+static int qed_update_vport(struct qed_dev *cdev,
+                           struct qed_update_vport_params *params)
+{
+       struct qed_sp_vport_update_params sp_params;
+       struct qed_rss_params sp_rss_params;
+       int rc, i;
+
+       if (!cdev)
+               return -ENODEV;
+
+       memset(&sp_params, 0, sizeof(sp_params));
+       memset(&sp_rss_params, 0, sizeof(sp_rss_params));
+
+       /* Translate protocol params into sp params */
+       sp_params.vport_id = params->vport_id;
+       sp_params.update_vport_active_rx_flg =
+               params->update_vport_active_flg;
+       sp_params.update_vport_active_tx_flg =
+               params->update_vport_active_flg;
+       sp_params.vport_active_rx_flg = params->vport_active_flg;
+       sp_params.vport_active_tx_flg = params->vport_active_flg;
+
+       /* RSS - is a bit tricky, since upper-layer isn't familiar with hwfns.
+        * We need to re-fix the rss values per engine for CMT.
+        */
+       if (cdev->num_hwfns > 1 && params->update_rss_flg) {
+               struct qed_update_vport_rss_params *rss =
+                       &params->rss_params;
+               int k, max = 0;
+
+               /* Find largest entry, since it's possible RSS needs to
+                * be disabled [in case only 1 queue per-hwfn]
+                */
+               for (k = 0; k < QED_RSS_IND_TABLE_SIZE; k++)
+                       max = (max > rss->rss_ind_table[k]) ?
+                               max : rss->rss_ind_table[k];
+
+               /* Either fix RSS values or disable RSS */
+               if (cdev->num_hwfns < max + 1) {
+                       int divisor = (max + cdev->num_hwfns - 1) /
+                               cdev->num_hwfns;
+
+                       DP_VERBOSE(cdev, (QED_MSG_SPQ | NETIF_MSG_IFUP),
+                                  "CMT - fixing RSS values (modulo %02x)\n",
+                                  divisor);
+
+                       for (k = 0; k < QED_RSS_IND_TABLE_SIZE; k++)
+                               rss->rss_ind_table[k] =
+                                       rss->rss_ind_table[k] % divisor;
+               } else {
+                       DP_VERBOSE(cdev, (QED_MSG_SPQ | NETIF_MSG_IFUP),
+                                  "CMT - 1 queue per-hwfn; Disabling RSS\n");
+                       params->update_rss_flg = 0;
+               }
+       }
+
+       /* Now, update the RSS configuration for actual configuration */
+       if (params->update_rss_flg) {
+               sp_rss_params.update_rss_config = 1;
+               sp_rss_params.rss_enable = 1;
+               sp_rss_params.update_rss_capabilities = 1;
+               sp_rss_params.update_rss_ind_table = 1;
+               sp_rss_params.update_rss_key = 1;
+               sp_rss_params.rss_caps = QED_RSS_IPV4 |
+                                        QED_RSS_IPV6 |
+                                        QED_RSS_IPV4_TCP | QED_RSS_IPV6_TCP;
+               sp_rss_params.rss_table_size_log = 7; /* 2^7 = 128 */
+               memcpy(sp_rss_params.rss_ind_table,
+                      params->rss_params.rss_ind_table,
+                      QED_RSS_IND_TABLE_SIZE * sizeof(u16));
+               memcpy(sp_rss_params.rss_key, params->rss_params.rss_key,
+                      QED_RSS_KEY_SIZE * sizeof(u32));
+       }
+       sp_params.rss_params = &sp_rss_params;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               sp_params.opaque_fid = p_hwfn->hw_info.opaque_fid;
+               rc = qed_sp_vport_update(p_hwfn, &sp_params,
+                                        QED_SPQ_MODE_EBLOCK,
+                                        NULL);
+               if (rc) {
+                       DP_ERR(cdev, "Failed to update VPORT\n");
+                       return rc;
+               }
+
+               DP_VERBOSE(cdev, (QED_MSG_SPQ | NETIF_MSG_IFUP),
+                          "Updated V-PORT %d: active_flag %d [update %d]\n",
+                          params->vport_id, params->vport_active_flg,
+                          params->update_vport_active_flg);
+       }
+
+       return 0;
+}
+
+static int qed_start_rxq(struct qed_dev *cdev,
+                        struct qed_queue_start_common_params *params,
+                        u16 bd_max_bytes,
+                        dma_addr_t bd_chain_phys_addr,
+                        dma_addr_t cqe_pbl_addr,
+                        u16 cqe_pbl_size,
+                        void __iomem **pp_prod)
+{
+       int rc, hwfn_index;
+       struct qed_hwfn *p_hwfn;
+
+       hwfn_index = params->rss_id % cdev->num_hwfns;
+       p_hwfn = &cdev->hwfns[hwfn_index];
+
+       /* Fix queue ID in 100g mode */
+       params->queue_id /= cdev->num_hwfns;
+
+       rc = qed_sp_eth_rx_queue_start(p_hwfn,
+                                      p_hwfn->hw_info.opaque_fid,
+                                      params,
+                                      bd_max_bytes,
+                                      bd_chain_phys_addr,
+                                      cqe_pbl_addr,
+                                      cqe_pbl_size,
+                                      pp_prod);
+
+       if (rc) {
+               DP_ERR(cdev, "Failed to start RXQ#%d\n", params->queue_id);
+               return rc;
+       }
+
+       DP_VERBOSE(cdev, (QED_MSG_SPQ | NETIF_MSG_IFUP),
+                  "Started RX-Q %d [rss %d] on V-PORT %d and SB %d\n",
+                  params->queue_id, params->rss_id, params->vport_id,
+                  params->sb);
+
+       return 0;
+}
+
+static int qed_stop_rxq(struct qed_dev *cdev,
+                       struct qed_stop_rxq_params *params)
+{
+       int rc, hwfn_index;
+       struct qed_hwfn *p_hwfn;
+
+       hwfn_index      = params->rss_id % cdev->num_hwfns;
+       p_hwfn          = &cdev->hwfns[hwfn_index];
+
+       rc = qed_sp_eth_rx_queue_stop(p_hwfn,
+                                     params->rx_queue_id / cdev->num_hwfns,
+                                     params->eq_completion_only,
+                                     false);
+       if (rc) {
+               DP_ERR(cdev, "Failed to stop RXQ#%d\n", params->rx_queue_id);
+               return rc;
+       }
+
+       return 0;
+}
+
+static int qed_start_txq(struct qed_dev *cdev,
+                        struct qed_queue_start_common_params *p_params,
+                        dma_addr_t pbl_addr,
+                        u16 pbl_size,
+                        void __iomem **pp_doorbell)
+{
+       struct qed_hwfn *p_hwfn;
+       int rc, hwfn_index;
+
+       hwfn_index      = p_params->rss_id % cdev->num_hwfns;
+       p_hwfn          = &cdev->hwfns[hwfn_index];
+
+       /* Fix queue ID in 100g mode */
+       p_params->queue_id /= cdev->num_hwfns;
+
+       rc = qed_sp_eth_tx_queue_start(p_hwfn,
+                                      p_hwfn->hw_info.opaque_fid,
+                                      p_params,
+                                      pbl_addr,
+                                      pbl_size,
+                                      pp_doorbell);
+
+       if (rc) {
+               DP_ERR(cdev, "Failed to start TXQ#%d\n", p_params->queue_id);
+               return rc;
+       }
+
+       DP_VERBOSE(cdev, (QED_MSG_SPQ | NETIF_MSG_IFUP),
+                  "Started TX-Q %d [rss %d] on V-PORT %d and SB %d\n",
+                  p_params->queue_id, p_params->rss_id, p_params->vport_id,
+                  p_params->sb);
+
+       return 0;
+}
+
+#define QED_HW_STOP_RETRY_LIMIT (10)
+static int qed_fastpath_stop(struct qed_dev *cdev)
+{
+       qed_hw_stop_fastpath(cdev);
+
+       return 0;
+}
+
+static int qed_stop_txq(struct qed_dev *cdev,
+                       struct qed_stop_txq_params *params)
+{
+       struct qed_hwfn *p_hwfn;
+       int rc, hwfn_index;
+
+       hwfn_index      = params->rss_id % cdev->num_hwfns;
+       p_hwfn          = &cdev->hwfns[hwfn_index];
+
+       rc = qed_sp_eth_tx_queue_stop(p_hwfn,
+                                     params->tx_queue_id / cdev->num_hwfns);
+       if (rc) {
+               DP_ERR(cdev, "Failed to stop TXQ#%d\n", params->tx_queue_id);
+               return rc;
+       }
+
+       return 0;
+}
+
+static int qed_configure_filter_rx_mode(struct qed_dev *cdev,
+                                       enum qed_filter_rx_mode_type type)
+{
+       struct qed_filter_accept_flags accept_flags;
+
+       memset(&accept_flags, 0, sizeof(accept_flags));
+
+       accept_flags.update_rx_mode_config      = 1;
+       accept_flags.update_tx_mode_config      = 1;
+       accept_flags.rx_accept_filter           = QED_ACCEPT_UCAST_MATCHED |
+                                                 QED_ACCEPT_MCAST_MATCHED |
+                                                 QED_ACCEPT_BCAST;
+       accept_flags.tx_accept_filter = QED_ACCEPT_UCAST_MATCHED |
+                                       QED_ACCEPT_MCAST_MATCHED |
+                                       QED_ACCEPT_BCAST;
+
+       if (type == QED_FILTER_RX_MODE_TYPE_PROMISC)
+               accept_flags.rx_accept_filter |= QED_ACCEPT_UCAST_UNMATCHED |
+                                                QED_ACCEPT_MCAST_UNMATCHED;
+       else if (type == QED_FILTER_RX_MODE_TYPE_MULTI_PROMISC)
+               accept_flags.rx_accept_filter |= QED_ACCEPT_MCAST_UNMATCHED;
+
+       return qed_filter_accept_cmd(cdev, 0, accept_flags,
+                                    QED_SPQ_MODE_CB, NULL);
+}
+
+static int qed_configure_filter_ucast(struct qed_dev *cdev,
+                                     struct qed_filter_ucast_params *params)
+{
+       struct qed_filter_ucast ucast;
+
+       if (!params->vlan_valid && !params->mac_valid) {
+               DP_NOTICE(
+                       cdev,
+                       "Tried configuring a unicast filter, but both MAC and VLAN are not set\n");
+               return -EINVAL;
+       }
+
+       memset(&ucast, 0, sizeof(ucast));
+       switch (params->type) {
+       case QED_FILTER_XCAST_TYPE_ADD:
+               ucast.opcode = QED_FILTER_ADD;
+               break;
+       case QED_FILTER_XCAST_TYPE_DEL:
+               ucast.opcode = QED_FILTER_REMOVE;
+               break;
+       case QED_FILTER_XCAST_TYPE_REPLACE:
+               ucast.opcode = QED_FILTER_REPLACE;
+               break;
+       default:
+               DP_NOTICE(cdev, "Unknown unicast filter type %d\n",
+                         params->type);
+       }
+
+       if (params->vlan_valid && params->mac_valid) {
+               ucast.type = QED_FILTER_MAC_VLAN;
+               ether_addr_copy(ucast.mac, params->mac);
+               ucast.vlan = params->vlan;
+       } else if (params->mac_valid) {
+               ucast.type = QED_FILTER_MAC;
+               ether_addr_copy(ucast.mac, params->mac);
+       } else {
+               ucast.type = QED_FILTER_VLAN;
+               ucast.vlan = params->vlan;
+       }
+
+       ucast.is_rx_filter = true;
+       ucast.is_tx_filter = true;
+
+       return qed_filter_ucast_cmd(cdev, &ucast, QED_SPQ_MODE_CB, NULL);
+}
+
+static int qed_configure_filter_mcast(struct qed_dev *cdev,
+                                     struct qed_filter_mcast_params *params)
+{
+       struct qed_filter_mcast mcast;
+       int i;
+
+       memset(&mcast, 0, sizeof(mcast));
+       switch (params->type) {
+       case QED_FILTER_XCAST_TYPE_ADD:
+               mcast.opcode = QED_FILTER_ADD;
+               break;
+       case QED_FILTER_XCAST_TYPE_DEL:
+               mcast.opcode = QED_FILTER_REMOVE;
+               break;
+       default:
+               DP_NOTICE(cdev, "Unknown multicast filter type %d\n",
+                         params->type);
+       }
+
+       mcast.num_mc_addrs = params->num;
+       for (i = 0; i < mcast.num_mc_addrs; i++)
+               ether_addr_copy(mcast.mac[i], params->mac[i]);
+
+       return qed_filter_mcast_cmd(cdev, &mcast,
+                                   QED_SPQ_MODE_CB, NULL);
+}
+
+static int qed_configure_filter(struct qed_dev *cdev,
+                               struct qed_filter_params *params)
+{
+       enum qed_filter_rx_mode_type accept_flags;
+
+       switch (params->type) {
+       case QED_FILTER_TYPE_UCAST:
+               return qed_configure_filter_ucast(cdev, &params->filter.ucast);
+       case QED_FILTER_TYPE_MCAST:
+               return qed_configure_filter_mcast(cdev, &params->filter.mcast);
+       case QED_FILTER_TYPE_RX_MODE:
+               accept_flags = params->filter.accept_flags;
+               return qed_configure_filter_rx_mode(cdev, accept_flags);
+       default:
+               DP_NOTICE(cdev, "Unknown filter type %d\n",
+                         (int)params->type);
+               return -EINVAL;
+       }
+}
+
+static int qed_fp_cqe_completion(struct qed_dev *dev,
+                                u8 rss_id,
+                                struct eth_slow_path_rx_cqe *cqe)
+{
+       return qed_eth_cqe_completion(&dev->hwfns[rss_id % dev->num_hwfns],
+                                     cqe);
+}
+
+static const struct qed_eth_ops qed_eth_ops_pass = {
+       .common = &qed_common_ops_pass,
+       .fill_dev_info = &qed_fill_eth_dev_info,
+       .register_ops = &qed_register_eth_ops,
+       .vport_start = &qed_start_vport,
+       .vport_stop = &qed_stop_vport,
+       .vport_update = &qed_update_vport,
+       .q_rx_start = &qed_start_rxq,
+       .q_rx_stop = &qed_stop_rxq,
+       .q_tx_start = &qed_start_txq,
+       .q_tx_stop = &qed_stop_txq,
+       .filter_config = &qed_configure_filter,
+       .fastpath_stop = &qed_fastpath_stop,
+       .eth_cqe_completion = &qed_fp_cqe_completion,
+       .get_vport_stats = &qed_get_vport_stats,
+};
+
+const struct qed_eth_ops *qed_get_eth_ops(u32 version)
+{
+       if (version != QED_ETH_INTERFACE_VERSION) {
+               pr_notice("Cannot supply ethtool operations [%08x != %08x]\n",
+                         version, QED_ETH_INTERFACE_VERSION);
+               return NULL;
+       }
+
+       return &qed_eth_ops_pass;
+}
+EXPORT_SYMBOL(qed_get_eth_ops);
+
+void qed_put_eth_ops(void)
+{
+       /* TODO - reference count for module? */
+}
+EXPORT_SYMBOL(qed_put_eth_ops);
diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c
new file mode 100644 (file)
index 0000000..947c7af
--- /dev/null
@@ -0,0 +1,1169 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#include <linux/stddef.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/ethtool.h>
+#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/qed/qed_if.h>
+
+#include "qed.h"
+#include "qed_sp.h"
+#include "qed_dev_api.h"
+#include "qed_mcp.h"
+#include "qed_hw.h"
+
+static const char version[] =
+       "QLogic QL4xxx 40G/100G Ethernet Driver qed " DRV_MODULE_VERSION "\n";
+
+MODULE_DESCRIPTION("QLogic 25G/40G/50G/100G Core Module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+#define FW_FILE_VERSION                                \
+       __stringify(FW_MAJOR_VERSION) "."       \
+       __stringify(FW_MINOR_VERSION) "."       \
+       __stringify(FW_REVISION_VERSION) "."    \
+       __stringify(FW_ENGINEERING_VERSION)
+
+#define QED_FW_FILE_NAME       \
+       "qed/qed_init_values_zipped-" FW_FILE_VERSION ".bin"
+
+static int __init qed_init(void)
+{
+       pr_notice("qed_init called\n");
+
+       pr_info("%s", version);
+
+       return 0;
+}
+
+static void __exit qed_cleanup(void)
+{
+       pr_notice("qed_cleanup called\n");
+}
+
+module_init(qed_init);
+module_exit(qed_cleanup);
+
+/* Check if the DMA controller on the machine can properly handle the DMA
+ * addressing required by the device.
+*/
+static int qed_set_coherency_mask(struct qed_dev *cdev)
+{
+       struct device *dev = &cdev->pdev->dev;
+
+       if (dma_set_mask(dev, DMA_BIT_MASK(64)) == 0) {
+               if (dma_set_coherent_mask(dev, DMA_BIT_MASK(64)) != 0) {
+                       DP_NOTICE(cdev,
+                                 "Can't request 64-bit consistent allocations\n");
+                       return -EIO;
+               }
+       } else if (dma_set_mask(dev, DMA_BIT_MASK(32)) != 0) {
+               DP_NOTICE(cdev, "Can't request 64b/32b DMA addresses\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void qed_free_pci(struct qed_dev *cdev)
+{
+       struct pci_dev *pdev = cdev->pdev;
+
+       if (cdev->doorbells)
+               iounmap(cdev->doorbells);
+       if (cdev->regview)
+               iounmap(cdev->regview);
+       if (atomic_read(&pdev->enable_cnt) == 1)
+               pci_release_regions(pdev);
+
+       pci_disable_device(pdev);
+}
+
+/* Performs PCI initializations as well as initializing PCI-related parameters
+ * in the device structrue. Returns 0 in case of success.
+ */
+static int qed_init_pci(struct qed_dev *cdev,
+                       struct pci_dev *pdev)
+{
+       int rc;
+
+       cdev->pdev = pdev;
+
+       rc = pci_enable_device(pdev);
+       if (rc) {
+               DP_NOTICE(cdev, "Cannot enable PCI device\n");
+               goto err0;
+       }
+
+       if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+               DP_NOTICE(cdev, "No memory region found in bar #0\n");
+               rc = -EIO;
+               goto err1;
+       }
+
+       if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
+               DP_NOTICE(cdev, "No memory region found in bar #2\n");
+               rc = -EIO;
+               goto err1;
+       }
+
+       if (atomic_read(&pdev->enable_cnt) == 1) {
+               rc = pci_request_regions(pdev, "qed");
+               if (rc) {
+                       DP_NOTICE(cdev,
+                                 "Failed to request PCI memory resources\n");
+                       goto err1;
+               }
+               pci_set_master(pdev);
+               pci_save_state(pdev);
+       }
+
+       if (!pci_is_pcie(pdev)) {
+               DP_NOTICE(cdev, "The bus is not PCI Express\n");
+               rc = -EIO;
+               goto err2;
+       }
+
+       cdev->pci_params.pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+       if (cdev->pci_params.pm_cap == 0)
+               DP_NOTICE(cdev, "Cannot find power management capability\n");
+
+       rc = qed_set_coherency_mask(cdev);
+       if (rc)
+               goto err2;
+
+       cdev->pci_params.mem_start = pci_resource_start(pdev, 0);
+       cdev->pci_params.mem_end = pci_resource_end(pdev, 0);
+       cdev->pci_params.irq = pdev->irq;
+
+       cdev->regview = pci_ioremap_bar(pdev, 0);
+       if (!cdev->regview) {
+               DP_NOTICE(cdev, "Cannot map register space, aborting\n");
+               rc = -ENOMEM;
+               goto err2;
+       }
+
+       cdev->db_phys_addr = pci_resource_start(cdev->pdev, 2);
+       cdev->db_size = pci_resource_len(cdev->pdev, 2);
+       cdev->doorbells = ioremap_wc(cdev->db_phys_addr, cdev->db_size);
+       if (!cdev->doorbells) {
+               DP_NOTICE(cdev, "Cannot map doorbell space\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+
+err2:
+       pci_release_regions(pdev);
+err1:
+       pci_disable_device(pdev);
+err0:
+       return rc;
+}
+
+int qed_fill_dev_info(struct qed_dev *cdev,
+                     struct qed_dev_info *dev_info)
+{
+       struct qed_ptt  *ptt;
+
+       memset(dev_info, 0, sizeof(struct qed_dev_info));
+
+       dev_info->num_hwfns = cdev->num_hwfns;
+       dev_info->pci_mem_start = cdev->pci_params.mem_start;
+       dev_info->pci_mem_end = cdev->pci_params.mem_end;
+       dev_info->pci_irq = cdev->pci_params.irq;
+       dev_info->is_mf = IS_MF(&cdev->hwfns[0]);
+       ether_addr_copy(dev_info->hw_mac, cdev->hwfns[0].hw_info.hw_mac_addr);
+
+       dev_info->fw_major = FW_MAJOR_VERSION;
+       dev_info->fw_minor = FW_MINOR_VERSION;
+       dev_info->fw_rev = FW_REVISION_VERSION;
+       dev_info->fw_eng = FW_ENGINEERING_VERSION;
+       dev_info->mf_mode = cdev->mf_mode;
+
+       qed_mcp_get_mfw_ver(cdev, &dev_info->mfw_rev);
+
+       ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev));
+       if (ptt) {
+               qed_mcp_get_flash_size(QED_LEADING_HWFN(cdev), ptt,
+                                      &dev_info->flash_size);
+
+               qed_ptt_release(QED_LEADING_HWFN(cdev), ptt);
+       }
+
+       return 0;
+}
+
+static void qed_free_cdev(struct qed_dev *cdev)
+{
+       kfree((void *)cdev);
+}
+
+static struct qed_dev *qed_alloc_cdev(struct pci_dev *pdev)
+{
+       struct qed_dev *cdev;
+
+       cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+       if (!cdev)
+               return cdev;
+
+       qed_init_struct(cdev);
+
+       return cdev;
+}
+
+/* Sets the requested power state */
+static int qed_set_power_state(struct qed_dev *cdev,
+                              pci_power_t state)
+{
+       if (!cdev)
+               return -ENODEV;
+
+       DP_VERBOSE(cdev, NETIF_MSG_DRV, "Omitting Power state change\n");
+       return 0;
+}
+
+/* probing */
+static struct qed_dev *qed_probe(struct pci_dev *pdev,
+                                enum qed_protocol protocol,
+                                u32 dp_module,
+                                u8 dp_level)
+{
+       struct qed_dev *cdev;
+       int rc;
+
+       cdev = qed_alloc_cdev(pdev);
+       if (!cdev)
+               goto err0;
+
+       cdev->protocol = protocol;
+
+       qed_init_dp(cdev, dp_module, dp_level);
+
+       rc = qed_init_pci(cdev, pdev);
+       if (rc) {
+               DP_ERR(cdev, "init pci failed\n");
+               goto err1;
+       }
+       DP_INFO(cdev, "PCI init completed successfully\n");
+
+       rc = qed_hw_prepare(cdev, QED_PCI_DEFAULT);
+       if (rc) {
+               DP_ERR(cdev, "hw prepare failed\n");
+               goto err2;
+       }
+
+       DP_INFO(cdev, "qed_probe completed successffuly\n");
+
+       return cdev;
+
+err2:
+       qed_free_pci(cdev);
+err1:
+       qed_free_cdev(cdev);
+err0:
+       return NULL;
+}
+
+static void qed_remove(struct qed_dev *cdev)
+{
+       if (!cdev)
+               return;
+
+       qed_hw_remove(cdev);
+
+       qed_free_pci(cdev);
+
+       qed_set_power_state(cdev, PCI_D3hot);
+
+       qed_free_cdev(cdev);
+}
+
+static void qed_disable_msix(struct qed_dev *cdev)
+{
+       if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) {
+               pci_disable_msix(cdev->pdev);
+               kfree(cdev->int_params.msix_table);
+       } else if (cdev->int_params.out.int_mode == QED_INT_MODE_MSI) {
+               pci_disable_msi(cdev->pdev);
+       }
+
+       memset(&cdev->int_params.out, 0, sizeof(struct qed_int_param));
+}
+
+static int qed_enable_msix(struct qed_dev *cdev,
+                          struct qed_int_params *int_params)
+{
+       int i, rc, cnt;
+
+       cnt = int_params->in.num_vectors;
+
+       for (i = 0; i < cnt; i++)
+               int_params->msix_table[i].entry = i;
+
+       rc = pci_enable_msix_range(cdev->pdev, int_params->msix_table,
+                                  int_params->in.min_msix_cnt, cnt);
+       if (rc < cnt && rc >= int_params->in.min_msix_cnt &&
+           (rc % cdev->num_hwfns)) {
+               pci_disable_msix(cdev->pdev);
+
+               /* If fastpath is initialized, we need at least one interrupt
+                * per hwfn [and the slow path interrupts]. New requested number
+                * should be a multiple of the number of hwfns.
+                */
+               cnt = (rc / cdev->num_hwfns) * cdev->num_hwfns;
+               DP_NOTICE(cdev,
+                         "Trying to enable MSI-X with less vectors (%d out of %d)\n",
+                         cnt, int_params->in.num_vectors);
+               rc = pci_enable_msix_exact(cdev->pdev,
+                                          int_params->msix_table, cnt);
+               if (!rc)
+                       rc = cnt;
+       }
+
+       if (rc > 0) {
+               /* MSI-x configuration was achieved */
+               int_params->out.int_mode = QED_INT_MODE_MSIX;
+               int_params->out.num_vectors = rc;
+               rc = 0;
+       } else {
+               DP_NOTICE(cdev,
+                         "Failed to enable MSI-X [Requested %d vectors][rc %d]\n",
+                         cnt, rc);
+       }
+
+       return rc;
+}
+
+/* This function outputs the int mode and the number of enabled msix vector */
+static int qed_set_int_mode(struct qed_dev *cdev, bool force_mode)
+{
+       struct qed_int_params *int_params = &cdev->int_params;
+       struct msix_entry *tbl;
+       int rc = 0, cnt;
+
+       switch (int_params->in.int_mode) {
+       case QED_INT_MODE_MSIX:
+               /* Allocate MSIX table */
+               cnt = int_params->in.num_vectors;
+               int_params->msix_table = kcalloc(cnt, sizeof(*tbl), GFP_KERNEL);
+               if (!int_params->msix_table) {
+                       rc = -ENOMEM;
+                       goto out;
+               }
+
+               /* Enable MSIX */
+               rc = qed_enable_msix(cdev, int_params);
+               if (!rc)
+                       goto out;
+
+               DP_NOTICE(cdev, "Failed to enable MSI-X\n");
+               kfree(int_params->msix_table);
+               if (force_mode)
+                       goto out;
+               /* Fallthrough */
+
+       case QED_INT_MODE_MSI:
+               rc = pci_enable_msi(cdev->pdev);
+               if (!rc) {
+                       int_params->out.int_mode = QED_INT_MODE_MSI;
+                       goto out;
+               }
+
+               DP_NOTICE(cdev, "Failed to enable MSI\n");
+               if (force_mode)
+                       goto out;
+               /* Fallthrough */
+
+       case QED_INT_MODE_INTA:
+                       int_params->out.int_mode = QED_INT_MODE_INTA;
+                       rc = 0;
+                       goto out;
+       default:
+               DP_NOTICE(cdev, "Unknown int_mode value %d\n",
+                         int_params->in.int_mode);
+               rc = -EINVAL;
+       }
+
+out:
+       cdev->int_coalescing_mode = QED_COAL_MODE_ENABLE;
+
+       return rc;
+}
+
+static void qed_simd_handler_config(struct qed_dev *cdev, void *token,
+                                   int index, void(*handler)(void *))
+{
+       struct qed_hwfn *hwfn = &cdev->hwfns[index % cdev->num_hwfns];
+       int relative_idx = index / cdev->num_hwfns;
+
+       hwfn->simd_proto_handler[relative_idx].func = handler;
+       hwfn->simd_proto_handler[relative_idx].token = token;
+}
+
+static void qed_simd_handler_clean(struct qed_dev *cdev, int index)
+{
+       struct qed_hwfn *hwfn = &cdev->hwfns[index % cdev->num_hwfns];
+       int relative_idx = index / cdev->num_hwfns;
+
+       memset(&hwfn->simd_proto_handler[relative_idx], 0,
+              sizeof(struct qed_simd_fp_handler));
+}
+
+static irqreturn_t qed_msix_sp_int(int irq, void *tasklet)
+{
+       tasklet_schedule((struct tasklet_struct *)tasklet);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t qed_single_int(int irq, void *dev_instance)
+{
+       struct qed_dev *cdev = (struct qed_dev *)dev_instance;
+       struct qed_hwfn *hwfn;
+       irqreturn_t rc = IRQ_NONE;
+       u64 status;
+       int i, j;
+
+       for (i = 0; i < cdev->num_hwfns; i++) {
+               status = qed_int_igu_read_sisr_reg(&cdev->hwfns[i]);
+
+               if (!status)
+                       continue;
+
+               hwfn = &cdev->hwfns[i];
+
+               /* Slowpath interrupt */
+               if (unlikely(status & 0x1)) {
+                       tasklet_schedule(hwfn->sp_dpc);
+                       status &= ~0x1;
+                       rc = IRQ_HANDLED;
+               }
+
+               /* Fastpath interrupts */
+               for (j = 0; j < 64; j++) {
+                       if ((0x2ULL << j) & status) {
+                               hwfn->simd_proto_handler[j].func(
+                                       hwfn->simd_proto_handler[j].token);
+                               status &= ~(0x2ULL << j);
+                               rc = IRQ_HANDLED;
+                       }
+               }
+
+               if (unlikely(status))
+                       DP_VERBOSE(hwfn, NETIF_MSG_INTR,
+                                  "got an unknown interrupt status 0x%llx\n",
+                                  status);
+       }
+
+       return rc;
+}
+
+static int qed_slowpath_irq_req(struct qed_dev *cdev)
+{
+       int i = 0, rc = 0;
+
+       if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) {
+               /* Request all the slowpath MSI-X vectors */
+               for (i = 0; i < cdev->num_hwfns; i++) {
+                       snprintf(cdev->hwfns[i].name, NAME_SIZE,
+                                "sp-%d-%02x:%02x.%02x",
+                                i, cdev->pdev->bus->number,
+                                PCI_SLOT(cdev->pdev->devfn),
+                                cdev->hwfns[i].abs_pf_id);
+
+                       rc = request_irq(cdev->int_params.msix_table[i].vector,
+                                        qed_msix_sp_int, 0,
+                                        cdev->hwfns[i].name,
+                                        cdev->hwfns[i].sp_dpc);
+                       if (rc)
+                               break;
+
+                       DP_VERBOSE(&cdev->hwfns[i],
+                                  (NETIF_MSG_INTR | QED_MSG_SP),
+                                  "Requested slowpath MSI-X\n");
+               }
+
+               if (i != cdev->num_hwfns) {
+                       /* Free already request MSI-X vectors */
+                       for (i--; i >= 0; i--) {
+                               unsigned int vec =
+                                       cdev->int_params.msix_table[i].vector;
+                               synchronize_irq(vec);
+                               free_irq(cdev->int_params.msix_table[i].vector,
+                                        cdev->hwfns[i].sp_dpc);
+                       }
+               }
+       } else {
+               unsigned long flags = 0;
+
+               snprintf(cdev->name, NAME_SIZE, "%02x:%02x.%02x",
+                        cdev->pdev->bus->number, PCI_SLOT(cdev->pdev->devfn),
+                        PCI_FUNC(cdev->pdev->devfn));
+
+               if (cdev->int_params.out.int_mode == QED_INT_MODE_INTA)
+                       flags |= IRQF_SHARED;
+
+               rc = request_irq(cdev->pdev->irq, qed_single_int,
+                                flags, cdev->name, cdev);
+       }
+
+       return rc;
+}
+
+static void qed_slowpath_irq_free(struct qed_dev *cdev)
+{
+       int i;
+
+       if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) {
+               for_each_hwfn(cdev, i) {
+                       synchronize_irq(cdev->int_params.msix_table[i].vector);
+                       free_irq(cdev->int_params.msix_table[i].vector,
+                                cdev->hwfns[i].sp_dpc);
+               }
+       } else {
+               free_irq(cdev->pdev->irq, cdev);
+       }
+}
+
+static int qed_nic_stop(struct qed_dev *cdev)
+{
+       int i, rc;
+
+       rc = qed_hw_stop(cdev);
+
+       for (i = 0; i < cdev->num_hwfns; i++) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               if (p_hwfn->b_sp_dpc_enabled) {
+                       tasklet_disable(p_hwfn->sp_dpc);
+                       p_hwfn->b_sp_dpc_enabled = false;
+                       DP_VERBOSE(cdev, NETIF_MSG_IFDOWN,
+                                  "Disabled sp taskelt [hwfn %d] at %p\n",
+                                  i, p_hwfn->sp_dpc);
+               }
+       }
+
+       return rc;
+}
+
+static int qed_nic_reset(struct qed_dev *cdev)
+{
+       int rc;
+
+       rc = qed_hw_reset(cdev);
+       if (rc)
+               return rc;
+
+       qed_resc_free(cdev);
+
+       return 0;
+}
+
+static int qed_nic_setup(struct qed_dev *cdev)
+{
+       int rc;
+
+       rc = qed_resc_alloc(cdev);
+       if (rc)
+               return rc;
+
+       DP_INFO(cdev, "Allocated qed resources\n");
+
+       qed_resc_setup(cdev);
+
+       return rc;
+}
+
+static int qed_set_int_fp(struct qed_dev *cdev, u16 cnt)
+{
+       int limit = 0;
+
+       /* Mark the fastpath as free/used */
+       cdev->int_params.fp_initialized = cnt ? true : false;
+
+       if (cdev->int_params.out.int_mode != QED_INT_MODE_MSIX)
+               limit = cdev->num_hwfns * 63;
+       else if (cdev->int_params.fp_msix_cnt)
+               limit = cdev->int_params.fp_msix_cnt;
+
+       if (!limit)
+               return -ENOMEM;
+
+       return min_t(int, cnt, limit);
+}
+
+static int qed_get_int_fp(struct qed_dev *cdev, struct qed_int_info *info)
+{
+       memset(info, 0, sizeof(struct qed_int_info));
+
+       if (!cdev->int_params.fp_initialized) {
+               DP_INFO(cdev,
+                       "Protocol driver requested interrupt information, but its support is not yet configured\n");
+               return -EINVAL;
+       }
+
+       /* Need to expose only MSI-X information; Single IRQ is handled solely
+        * by qed.
+        */
+       if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) {
+               int msix_base = cdev->int_params.fp_msix_base;
+
+               info->msix_cnt = cdev->int_params.fp_msix_cnt;
+               info->msix = &cdev->int_params.msix_table[msix_base];
+       }
+
+       return 0;
+}
+
+static int qed_slowpath_setup_int(struct qed_dev *cdev,
+                                 enum qed_int_mode int_mode)
+{
+       int rc, i;
+       u8 num_vectors = 0;
+
+       memset(&cdev->int_params, 0, sizeof(struct qed_int_params));
+
+       cdev->int_params.in.int_mode = int_mode;
+       for_each_hwfn(cdev, i)
+               num_vectors +=  qed_int_get_num_sbs(&cdev->hwfns[i], NULL) + 1;
+       cdev->int_params.in.num_vectors = num_vectors;
+
+       /* We want a minimum of one slowpath and one fastpath vector per hwfn */
+       cdev->int_params.in.min_msix_cnt = cdev->num_hwfns * 2;
+
+       rc = qed_set_int_mode(cdev, false);
+       if (rc)  {
+               DP_ERR(cdev, "qed_slowpath_setup_int ERR\n");
+               return rc;
+       }
+
+       cdev->int_params.fp_msix_base = cdev->num_hwfns;
+       cdev->int_params.fp_msix_cnt = cdev->int_params.out.num_vectors -
+                                      cdev->num_hwfns;
+
+       return 0;
+}
+
+u32 qed_unzip_data(struct qed_hwfn *p_hwfn, u32 input_len,
+                  u8 *input_buf, u32 max_size, u8 *unzip_buf)
+{
+       int rc;
+
+       p_hwfn->stream->next_in = input_buf;
+       p_hwfn->stream->avail_in = input_len;
+       p_hwfn->stream->next_out = unzip_buf;
+       p_hwfn->stream->avail_out = max_size;
+
+       rc = zlib_inflateInit2(p_hwfn->stream, MAX_WBITS);
+
+       if (rc != Z_OK) {
+               DP_VERBOSE(p_hwfn, NETIF_MSG_DRV, "zlib init failed, rc = %d\n",
+                          rc);
+               return 0;
+       }
+
+       rc = zlib_inflate(p_hwfn->stream, Z_FINISH);
+       zlib_inflateEnd(p_hwfn->stream);
+
+       if (rc != Z_OK && rc != Z_STREAM_END) {
+               DP_VERBOSE(p_hwfn, NETIF_MSG_DRV, "FW unzip error: %s, rc=%d\n",
+                          p_hwfn->stream->msg, rc);
+               return 0;
+       }
+
+       return p_hwfn->stream->total_out / 4;
+}
+
+static int qed_alloc_stream_mem(struct qed_dev *cdev)
+{
+       int i;
+       void *workspace;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               p_hwfn->stream = kzalloc(sizeof(*p_hwfn->stream), GFP_KERNEL);
+               if (!p_hwfn->stream)
+                       return -ENOMEM;
+
+               workspace = vzalloc(zlib_inflate_workspacesize());
+               if (!workspace)
+                       return -ENOMEM;
+               p_hwfn->stream->workspace = workspace;
+       }
+
+       return 0;
+}
+
+static void qed_free_stream_mem(struct qed_dev *cdev)
+{
+       int i;
+
+       for_each_hwfn(cdev, i) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               if (!p_hwfn->stream)
+                       return;
+
+               vfree(p_hwfn->stream->workspace);
+               kfree(p_hwfn->stream);
+       }
+}
+
+static void qed_update_pf_params(struct qed_dev *cdev,
+                                struct qed_pf_params *params)
+{
+       int i;
+
+       for (i = 0; i < cdev->num_hwfns; i++) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+               p_hwfn->pf_params = *params;
+       }
+}
+
+static int qed_slowpath_start(struct qed_dev *cdev,
+                             struct qed_slowpath_params *params)
+{
+       struct qed_mcp_drv_version drv_version;
+       const u8 *data = NULL;
+       struct qed_hwfn *hwfn;
+       int rc;
+
+       rc = request_firmware(&cdev->firmware, QED_FW_FILE_NAME,
+                             &cdev->pdev->dev);
+       if (rc) {
+               DP_NOTICE(cdev,
+                         "Failed to find fw file - /lib/firmware/%s\n",
+                         QED_FW_FILE_NAME);
+               goto err;
+       }
+
+       rc = qed_nic_setup(cdev);
+       if (rc)
+               goto err;
+
+       rc = qed_slowpath_setup_int(cdev, params->int_mode);
+       if (rc)
+               goto err1;
+
+       /* Request the slowpath IRQ */
+       rc = qed_slowpath_irq_req(cdev);
+       if (rc)
+               goto err2;
+
+       /* Allocate stream for unzipping */
+       rc = qed_alloc_stream_mem(cdev);
+       if (rc) {
+               DP_NOTICE(cdev, "Failed to allocate stream memory\n");
+               goto err3;
+       }
+
+       /* Start the slowpath */
+       data = cdev->firmware->data;
+
+       rc = qed_hw_init(cdev, true, cdev->int_params.out.int_mode,
+                        true, data);
+       if (rc)
+               goto err3;
+
+       DP_INFO(cdev,
+               "HW initialization and function start completed successfully\n");
+
+       hwfn = QED_LEADING_HWFN(cdev);
+       drv_version.version = (params->drv_major << 24) |
+                             (params->drv_minor << 16) |
+                             (params->drv_rev << 8) |
+                             (params->drv_eng);
+       strlcpy(drv_version.name, params->name,
+               MCP_DRV_VER_STR_SIZE - 4);
+       rc = qed_mcp_send_drv_version(hwfn, hwfn->p_main_ptt,
+                                     &drv_version);
+       if (rc) {
+               DP_NOTICE(cdev, "Failed sending drv version command\n");
+               return rc;
+       }
+
+       return 0;
+
+err3:
+       qed_free_stream_mem(cdev);
+       qed_slowpath_irq_free(cdev);
+err2:
+       qed_disable_msix(cdev);
+err1:
+       qed_resc_free(cdev);
+err:
+       release_firmware(cdev->firmware);
+
+       return rc;
+}
+
+static int qed_slowpath_stop(struct qed_dev *cdev)
+{
+       if (!cdev)
+               return -ENODEV;
+
+       qed_free_stream_mem(cdev);
+
+       qed_nic_stop(cdev);
+       qed_slowpath_irq_free(cdev);
+
+       qed_disable_msix(cdev);
+       qed_nic_reset(cdev);
+
+       release_firmware(cdev->firmware);
+
+       return 0;
+}
+
+static void qed_set_id(struct qed_dev *cdev, char name[NAME_SIZE],
+                      char ver_str[VER_SIZE])
+{
+       int i;
+
+       memcpy(cdev->name, name, NAME_SIZE);
+       for_each_hwfn(cdev, i)
+               snprintf(cdev->hwfns[i].name, NAME_SIZE, "%s-%d", name, i);
+
+       memcpy(cdev->ver_str, ver_str, VER_SIZE);
+       cdev->drv_type = DRV_ID_DRV_TYPE_LINUX;
+}
+
+static u32 qed_sb_init(struct qed_dev *cdev,
+                      struct qed_sb_info *sb_info,
+                      void *sb_virt_addr,
+                      dma_addr_t sb_phy_addr, u16 sb_id,
+                      enum qed_sb_type type)
+{
+       struct qed_hwfn *p_hwfn;
+       int hwfn_index;
+       u16 rel_sb_id;
+       u8 n_hwfns;
+       u32 rc;
+
+       /* RoCE uses single engine and CMT uses two engines. When using both
+        * we force only a single engine. Storage uses only engine 0 too.
+        */
+       if (type == QED_SB_TYPE_L2_QUEUE)
+               n_hwfns = cdev->num_hwfns;
+       else
+               n_hwfns = 1;
+
+       hwfn_index = sb_id % n_hwfns;
+       p_hwfn = &cdev->hwfns[hwfn_index];
+       rel_sb_id = sb_id / n_hwfns;
+
+       DP_VERBOSE(cdev, NETIF_MSG_INTR,
+                  "hwfn [%d] <--[init]-- SB %04x [0x%04x upper]\n",
+                  hwfn_index, rel_sb_id, sb_id);
+
+       rc = qed_int_sb_init(p_hwfn, p_hwfn->p_main_ptt, sb_info,
+                            sb_virt_addr, sb_phy_addr, rel_sb_id);
+
+       return rc;
+}
+
+static u32 qed_sb_release(struct qed_dev *cdev,
+                         struct qed_sb_info *sb_info,
+                         u16 sb_id)
+{
+       struct qed_hwfn *p_hwfn;
+       int hwfn_index;
+       u16 rel_sb_id;
+       u32 rc;
+
+       hwfn_index = sb_id % cdev->num_hwfns;
+       p_hwfn = &cdev->hwfns[hwfn_index];
+       rel_sb_id = sb_id / cdev->num_hwfns;
+
+       DP_VERBOSE(cdev, NETIF_MSG_INTR,
+                  "hwfn [%d] <--[init]-- SB %04x [0x%04x upper]\n",
+                  hwfn_index, rel_sb_id, sb_id);
+
+       rc = qed_int_sb_release(p_hwfn, sb_info, rel_sb_id);
+
+       return rc;
+}
+
+static int qed_set_link(struct qed_dev *cdev,
+                       struct qed_link_params *params)
+{
+       struct qed_hwfn *hwfn;
+       struct qed_mcp_link_params *link_params;
+       struct qed_ptt *ptt;
+       int rc;
+
+       if (!cdev)
+               return -ENODEV;
+
+       /* The link should be set only once per PF */
+       hwfn = &cdev->hwfns[0];
+
+       ptt = qed_ptt_acquire(hwfn);
+       if (!ptt)
+               return -EBUSY;
+
+       link_params = qed_mcp_get_link_params(hwfn);
+       if (params->override_flags & QED_LINK_OVERRIDE_SPEED_AUTONEG)
+               link_params->speed.autoneg = params->autoneg;
+       if (params->override_flags & QED_LINK_OVERRIDE_SPEED_ADV_SPEEDS) {
+               link_params->speed.advertised_speeds = 0;
+               if ((params->adv_speeds & SUPPORTED_1000baseT_Half) ||
+                   (params->adv_speeds & SUPPORTED_1000baseT_Full))
+                       link_params->speed.advertised_speeds |=
+                               NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_1G;
+               if (params->adv_speeds & SUPPORTED_10000baseKR_Full)
+                       link_params->speed.advertised_speeds |=
+                               NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_10G;
+               if (params->adv_speeds & SUPPORTED_40000baseLR4_Full)
+                       link_params->speed.advertised_speeds |=
+                               NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_40G;
+               if (params->adv_speeds & 0)
+                       link_params->speed.advertised_speeds |=
+                               NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_50G;
+               if (params->adv_speeds & 0)
+                       link_params->speed.advertised_speeds |=
+                               NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_100G;
+       }
+       if (params->override_flags & QED_LINK_OVERRIDE_SPEED_FORCED_SPEED)
+               link_params->speed.forced_speed = params->forced_speed;
+
+       rc = qed_mcp_set_link(hwfn, ptt, params->link_up);
+
+       qed_ptt_release(hwfn, ptt);
+
+       return rc;
+}
+
+static int qed_get_port_type(u32 media_type)
+{
+       int port_type;
+
+       switch (media_type) {
+       case MEDIA_SFPP_10G_FIBER:
+       case MEDIA_SFP_1G_FIBER:
+       case MEDIA_XFP_FIBER:
+       case MEDIA_KR:
+               port_type = PORT_FIBRE;
+               break;
+       case MEDIA_DA_TWINAX:
+               port_type = PORT_DA;
+               break;
+       case MEDIA_BASE_T:
+               port_type = PORT_TP;
+               break;
+       case MEDIA_NOT_PRESENT:
+               port_type = PORT_NONE;
+               break;
+       case MEDIA_UNSPECIFIED:
+       default:
+               port_type = PORT_OTHER;
+               break;
+       }
+       return port_type;
+}
+
+static void qed_fill_link(struct qed_hwfn *hwfn,
+                         struct qed_link_output *if_link)
+{
+       struct qed_mcp_link_params params;
+       struct qed_mcp_link_state link;
+       struct qed_mcp_link_capabilities link_caps;
+       u32 media_type;
+
+       memset(if_link, 0, sizeof(*if_link));
+
+       /* Prepare source inputs */
+       memcpy(&params, qed_mcp_get_link_params(hwfn), sizeof(params));
+       memcpy(&link, qed_mcp_get_link_state(hwfn), sizeof(link));
+       memcpy(&link_caps, qed_mcp_get_link_capabilities(hwfn),
+              sizeof(link_caps));
+
+       /* Set the link parameters to pass to protocol driver */
+       if (link.link_up)
+               if_link->link_up = true;
+
+       /* TODO - at the moment assume supported and advertised speed equal */
+       if_link->supported_caps = SUPPORTED_FIBRE;
+       if (params.speed.autoneg)
+               if_link->supported_caps |= SUPPORTED_Autoneg;
+       if (params.pause.autoneg ||
+           (params.pause.forced_rx && params.pause.forced_tx))
+               if_link->supported_caps |= SUPPORTED_Asym_Pause;
+       if (params.pause.autoneg || params.pause.forced_rx ||
+           params.pause.forced_tx)
+               if_link->supported_caps |= SUPPORTED_Pause;
+
+       if_link->advertised_caps = if_link->supported_caps;
+       if (params.speed.advertised_speeds &
+           NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_1G)
+               if_link->advertised_caps |= SUPPORTED_1000baseT_Half |
+                                          SUPPORTED_1000baseT_Full;
+       if (params.speed.advertised_speeds &
+           NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_10G)
+               if_link->advertised_caps |= SUPPORTED_10000baseKR_Full;
+       if (params.speed.advertised_speeds &
+               NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_40G)
+               if_link->advertised_caps |= SUPPORTED_40000baseLR4_Full;
+       if (params.speed.advertised_speeds &
+               NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_50G)
+               if_link->advertised_caps |= 0;
+       if (params.speed.advertised_speeds &
+               NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_100G)
+               if_link->advertised_caps |= 0;
+
+       if (link_caps.speed_capabilities &
+           NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_1G)
+               if_link->supported_caps |= SUPPORTED_1000baseT_Half |
+                                          SUPPORTED_1000baseT_Full;
+       if (link_caps.speed_capabilities &
+           NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_10G)
+               if_link->supported_caps |= SUPPORTED_10000baseKR_Full;
+       if (link_caps.speed_capabilities &
+               NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_40G)
+               if_link->supported_caps |= SUPPORTED_40000baseLR4_Full;
+       if (link_caps.speed_capabilities &
+               NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_50G)
+               if_link->supported_caps |= 0;
+       if (link_caps.speed_capabilities &
+               NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_100G)
+               if_link->supported_caps |= 0;
+
+       if (link.link_up)
+               if_link->speed = link.speed;
+
+       /* TODO - fill duplex properly */
+       if_link->duplex = DUPLEX_FULL;
+       qed_mcp_get_media_type(hwfn->cdev, &media_type);
+       if_link->port = qed_get_port_type(media_type);
+
+       if_link->autoneg = params.speed.autoneg;
+
+       if (params.pause.autoneg)
+               if_link->pause_config |= QED_LINK_PAUSE_AUTONEG_ENABLE;
+       if (params.pause.forced_rx)
+               if_link->pause_config |= QED_LINK_PAUSE_RX_ENABLE;
+       if (params.pause.forced_tx)
+               if_link->pause_config |= QED_LINK_PAUSE_TX_ENABLE;
+
+       /* Link partner capabilities */
+       if (link.partner_adv_speed &
+           QED_LINK_PARTNER_SPEED_1G_HD)
+               if_link->lp_caps |= SUPPORTED_1000baseT_Half;
+       if (link.partner_adv_speed &
+           QED_LINK_PARTNER_SPEED_1G_FD)
+               if_link->lp_caps |= SUPPORTED_1000baseT_Full;
+       if (link.partner_adv_speed &
+           QED_LINK_PARTNER_SPEED_10G)
+               if_link->lp_caps |= SUPPORTED_10000baseKR_Full;
+       if (link.partner_adv_speed &
+           QED_LINK_PARTNER_SPEED_40G)
+               if_link->lp_caps |= SUPPORTED_40000baseLR4_Full;
+       if (link.partner_adv_speed &
+           QED_LINK_PARTNER_SPEED_50G)
+               if_link->lp_caps |= 0;
+       if (link.partner_adv_speed &
+           QED_LINK_PARTNER_SPEED_100G)
+               if_link->lp_caps |= 0;
+
+       if (link.an_complete)
+               if_link->lp_caps |= SUPPORTED_Autoneg;
+
+       if (link.partner_adv_pause)
+               if_link->lp_caps |= SUPPORTED_Pause;
+       if (link.partner_adv_pause == QED_LINK_PARTNER_ASYMMETRIC_PAUSE ||
+           link.partner_adv_pause == QED_LINK_PARTNER_BOTH_PAUSE)
+               if_link->lp_caps |= SUPPORTED_Asym_Pause;
+}
+
+static void qed_get_current_link(struct qed_dev *cdev,
+                                struct qed_link_output *if_link)
+{
+       qed_fill_link(&cdev->hwfns[0], if_link);
+}
+
+void qed_link_update(struct qed_hwfn *hwfn)
+{
+       void *cookie = hwfn->cdev->ops_cookie;
+       struct qed_common_cb_ops *op = hwfn->cdev->protocol_ops.common;
+       struct qed_link_output if_link;
+
+       qed_fill_link(hwfn, &if_link);
+
+       if (IS_LEAD_HWFN(hwfn) && cookie)
+               op->link_update(cookie, &if_link);
+}
+
+static int qed_drain(struct qed_dev *cdev)
+{
+       struct qed_hwfn *hwfn;
+       struct qed_ptt *ptt;
+       int i, rc;
+
+       for_each_hwfn(cdev, i) {
+               hwfn = &cdev->hwfns[i];
+               ptt = qed_ptt_acquire(hwfn);
+               if (!ptt) {
+                       DP_NOTICE(hwfn, "Failed to drain NIG; No PTT\n");
+                       return -EBUSY;
+               }
+               rc = qed_mcp_drain(hwfn, ptt);
+               if (rc)
+                       return rc;
+               qed_ptt_release(hwfn, ptt);
+       }
+
+       return 0;
+}
+
+const struct qed_common_ops qed_common_ops_pass = {
+       .probe = &qed_probe,
+       .remove = &qed_remove,
+       .set_power_state = &qed_set_power_state,
+       .set_id = &qed_set_id,
+       .update_pf_params = &qed_update_pf_params,
+       .slowpath_start = &qed_slowpath_start,
+       .slowpath_stop = &qed_slowpath_stop,
+       .set_fp_int = &qed_set_int_fp,
+       .get_fp_int = &qed_get_int_fp,
+       .sb_init = &qed_sb_init,
+       .sb_release = &qed_sb_release,
+       .simd_handler_config = &qed_simd_handler_config,
+       .simd_handler_clean = &qed_simd_handler_clean,
+       .set_link = &qed_set_link,
+       .get_link = &qed_get_current_link,
+       .drain = &qed_drain,
+       .update_msglvl = &qed_init_dp,
+       .chain_alloc = &qed_chain_alloc,
+       .chain_free = &qed_chain_free,
+};
+
+u32 qed_get_protocol_version(enum qed_protocol protocol)
+{
+       switch (protocol) {
+       case QED_PROTOCOL_ETH:
+               return QED_ETH_INTERFACE_VERSION;
+       default:
+               return 0;
+       }
+}
+EXPORT_SYMBOL(qed_get_protocol_version);
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
new file mode 100644 (file)
index 0000000..20d048c
--- /dev/null
@@ -0,0 +1,860 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "qed.h"
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_mcp.h"
+#include "qed_reg_addr.h"
+#define CHIP_MCP_RESP_ITER_US 10
+
+#define QED_DRV_MB_MAX_RETRIES (500 * 1000)    /* Account for 5 sec */
+#define QED_MCP_RESET_RETRIES  (50 * 1000)     /* Account for 500 msec */
+
+#define DRV_INNER_WR(_p_hwfn, _p_ptt, _ptr, _offset, _val)          \
+       qed_wr(_p_hwfn, _p_ptt, (_p_hwfn->mcp_info->_ptr + _offset), \
+              _val)
+
+#define DRV_INNER_RD(_p_hwfn, _p_ptt, _ptr, _offset) \
+       qed_rd(_p_hwfn, _p_ptt, (_p_hwfn->mcp_info->_ptr + _offset))
+
+#define DRV_MB_WR(_p_hwfn, _p_ptt, _field, _val)  \
+       DRV_INNER_WR(p_hwfn, _p_ptt, drv_mb_addr, \
+                    offsetof(struct public_drv_mb, _field), _val)
+
+#define DRV_MB_RD(_p_hwfn, _p_ptt, _field)        \
+       DRV_INNER_RD(_p_hwfn, _p_ptt, drv_mb_addr, \
+                    offsetof(struct public_drv_mb, _field))
+
+#define PDA_COMP (((FW_MAJOR_VERSION) + (FW_MINOR_VERSION << 8)) << \
+                 DRV_ID_PDA_COMP_VER_SHIFT)
+
+#define MCP_BYTES_PER_MBIT_SHIFT 17
+
+bool qed_mcp_is_init(struct qed_hwfn *p_hwfn)
+{
+       if (!p_hwfn->mcp_info || !p_hwfn->mcp_info->public_base)
+               return false;
+       return true;
+}
+
+void qed_mcp_cmd_port_init(struct qed_hwfn *p_hwfn,
+                          struct qed_ptt *p_ptt)
+{
+       u32 addr = SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info->public_base,
+                                       PUBLIC_PORT);
+       u32 mfw_mb_offsize = qed_rd(p_hwfn, p_ptt, addr);
+
+       p_hwfn->mcp_info->port_addr = SECTION_ADDR(mfw_mb_offsize,
+                                                  MFW_PORT(p_hwfn));
+       DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                  "port_addr = 0x%x, port_id 0x%02x\n",
+                  p_hwfn->mcp_info->port_addr, MFW_PORT(p_hwfn));
+}
+
+void qed_mcp_read_mb(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt)
+{
+       u32 length = MFW_DRV_MSG_MAX_DWORDS(p_hwfn->mcp_info->mfw_mb_length);
+       u32 tmp, i;
+
+       if (!p_hwfn->mcp_info->public_base)
+               return;
+
+       for (i = 0; i < length; i++) {
+               tmp = qed_rd(p_hwfn, p_ptt,
+                            p_hwfn->mcp_info->mfw_mb_addr +
+                            (i << 2) + sizeof(u32));
+
+               /* The MB data is actually BE; Need to force it to cpu */
+               ((u32 *)p_hwfn->mcp_info->mfw_mb_cur)[i] =
+                       be32_to_cpu((__force __be32)tmp);
+       }
+}
+
+int qed_mcp_free(struct qed_hwfn *p_hwfn)
+{
+       if (p_hwfn->mcp_info) {
+               kfree(p_hwfn->mcp_info->mfw_mb_cur);
+               kfree(p_hwfn->mcp_info->mfw_mb_shadow);
+       }
+       kfree(p_hwfn->mcp_info);
+
+       return 0;
+}
+
+static int qed_load_mcp_offsets(struct qed_hwfn *p_hwfn,
+                               struct qed_ptt *p_ptt)
+{
+       struct qed_mcp_info *p_info = p_hwfn->mcp_info;
+       u32 drv_mb_offsize, mfw_mb_offsize;
+       u32 mcp_pf_id = MCP_PF_ID(p_hwfn);
+
+       p_info->public_base = qed_rd(p_hwfn, p_ptt, MISC_REG_SHARED_MEM_ADDR);
+       if (!p_info->public_base)
+               return 0;
+
+       p_info->public_base |= GRCBASE_MCP;
+
+       /* Calculate the driver and MFW mailbox address */
+       drv_mb_offsize = qed_rd(p_hwfn, p_ptt,
+                               SECTION_OFFSIZE_ADDR(p_info->public_base,
+                                                    PUBLIC_DRV_MB));
+       p_info->drv_mb_addr = SECTION_ADDR(drv_mb_offsize, mcp_pf_id);
+       DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                  "drv_mb_offsiz = 0x%x, drv_mb_addr = 0x%x mcp_pf_id = 0x%x\n",
+                  drv_mb_offsize, p_info->drv_mb_addr, mcp_pf_id);
+
+       /* Set the MFW MB address */
+       mfw_mb_offsize = qed_rd(p_hwfn, p_ptt,
+                               SECTION_OFFSIZE_ADDR(p_info->public_base,
+                                                    PUBLIC_MFW_MB));
+       p_info->mfw_mb_addr = SECTION_ADDR(mfw_mb_offsize, mcp_pf_id);
+       p_info->mfw_mb_length = (u16)qed_rd(p_hwfn, p_ptt, p_info->mfw_mb_addr);
+
+       /* Get the current driver mailbox sequence before sending
+        * the first command
+        */
+       p_info->drv_mb_seq = DRV_MB_RD(p_hwfn, p_ptt, drv_mb_header) &
+                            DRV_MSG_SEQ_NUMBER_MASK;
+
+       /* Get current FW pulse sequence */
+       p_info->drv_pulse_seq = DRV_MB_RD(p_hwfn, p_ptt, drv_pulse_mb) &
+                               DRV_PULSE_SEQ_MASK;
+
+       p_info->mcp_hist = (u16)qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0);
+
+       return 0;
+}
+
+int qed_mcp_cmd_init(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt)
+{
+       struct qed_mcp_info *p_info;
+       u32 size;
+
+       /* Allocate mcp_info structure */
+       p_hwfn->mcp_info = kzalloc(sizeof(*p_hwfn->mcp_info), GFP_ATOMIC);
+       if (!p_hwfn->mcp_info)
+               goto err;
+       p_info = p_hwfn->mcp_info;
+
+       if (qed_load_mcp_offsets(p_hwfn, p_ptt) != 0) {
+               DP_NOTICE(p_hwfn, "MCP is not initialized\n");
+               /* Do not free mcp_info here, since public_base indicate that
+                * the MCP is not initialized
+                */
+               return 0;
+       }
+
+       size = MFW_DRV_MSG_MAX_DWORDS(p_info->mfw_mb_length) * sizeof(u32);
+       p_info->mfw_mb_cur = kzalloc(size, GFP_ATOMIC);
+       p_info->mfw_mb_shadow =
+               kzalloc(sizeof(u32) * MFW_DRV_MSG_MAX_DWORDS(
+                               p_info->mfw_mb_length), GFP_ATOMIC);
+       if (!p_info->mfw_mb_shadow || !p_info->mfw_mb_addr)
+               goto err;
+
+       /* Initialize the MFW mutex */
+       mutex_init(&p_info->mutex);
+
+       return 0;
+
+err:
+       DP_NOTICE(p_hwfn, "Failed to allocate mcp memory\n");
+       qed_mcp_free(p_hwfn);
+       return -ENOMEM;
+}
+
+int qed_mcp_reset(struct qed_hwfn *p_hwfn,
+                 struct qed_ptt *p_ptt)
+{
+       u32 seq = ++p_hwfn->mcp_info->drv_mb_seq;
+       u8 delay = CHIP_MCP_RESP_ITER_US;
+       u32 org_mcp_reset_seq, cnt = 0;
+       int rc = 0;
+
+       /* Set drv command along with the updated sequence */
+       org_mcp_reset_seq = qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0);
+       DRV_MB_WR(p_hwfn, p_ptt, drv_mb_header,
+                 (DRV_MSG_CODE_MCP_RESET | seq));
+
+       do {
+               /* Wait for MFW response */
+               udelay(delay);
+               /* Give the FW up to 500 second (50*1000*10usec) */
+       } while ((org_mcp_reset_seq == qed_rd(p_hwfn, p_ptt,
+                                             MISCS_REG_GENERIC_POR_0)) &&
+                (cnt++ < QED_MCP_RESET_RETRIES));
+
+       if (org_mcp_reset_seq !=
+           qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0)) {
+               DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                          "MCP was reset after %d usec\n", cnt * delay);
+       } else {
+               DP_ERR(p_hwfn, "Failed to reset MCP\n");
+               rc = -EAGAIN;
+       }
+
+       return rc;
+}
+
+static int qed_do_mcp_cmd(struct qed_hwfn *p_hwfn,
+                         struct qed_ptt *p_ptt,
+                         u32 cmd,
+                         u32 param,
+                         u32 *o_mcp_resp,
+                         u32 *o_mcp_param)
+{
+       u8 delay = CHIP_MCP_RESP_ITER_US;
+       u32 seq, cnt = 1, actual_mb_seq;
+       int rc = 0;
+
+       /* Get actual driver mailbox sequence */
+       actual_mb_seq = DRV_MB_RD(p_hwfn, p_ptt, drv_mb_header) &
+                       DRV_MSG_SEQ_NUMBER_MASK;
+
+       /* Use MCP history register to check if MCP reset occurred between
+        * init time and now.
+        */
+       if (p_hwfn->mcp_info->mcp_hist !=
+           qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0)) {
+               DP_VERBOSE(p_hwfn, QED_MSG_SP, "Rereading MCP offsets\n");
+               qed_load_mcp_offsets(p_hwfn, p_ptt);
+               qed_mcp_cmd_port_init(p_hwfn, p_ptt);
+       }
+       seq = ++p_hwfn->mcp_info->drv_mb_seq;
+
+       /* Set drv param */
+       DRV_MB_WR(p_hwfn, p_ptt, drv_mb_param, param);
+
+       /* Set drv command along with the updated sequence */
+       DRV_MB_WR(p_hwfn, p_ptt, drv_mb_header, (cmd | seq));
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                  "wrote command (%x) to MFW MB param 0x%08x\n",
+                  (cmd | seq), param);
+
+       do {
+               /* Wait for MFW response */
+               udelay(delay);
+               *o_mcp_resp = DRV_MB_RD(p_hwfn, p_ptt, fw_mb_header);
+
+               /* Give the FW up to 5 second (500*10ms) */
+       } while ((seq != (*o_mcp_resp & FW_MSG_SEQ_NUMBER_MASK)) &&
+                (cnt++ < QED_DRV_MB_MAX_RETRIES));
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                  "[after %d ms] read (%x) seq is (%x) from FW MB\n",
+                  cnt * delay, *o_mcp_resp, seq);
+
+       /* Is this a reply to our command? */
+       if (seq == (*o_mcp_resp & FW_MSG_SEQ_NUMBER_MASK)) {
+               *o_mcp_resp &= FW_MSG_CODE_MASK;
+               /* Get the MCP param */
+               *o_mcp_param = DRV_MB_RD(p_hwfn, p_ptt, fw_mb_param);
+       } else {
+               /* FW BUG! */
+               DP_ERR(p_hwfn, "MFW failed to respond!\n");
+               *o_mcp_resp = 0;
+               rc = -EAGAIN;
+       }
+       return rc;
+}
+
+int qed_mcp_cmd(struct qed_hwfn *p_hwfn,
+               struct qed_ptt *p_ptt,
+               u32 cmd,
+               u32 param,
+               u32 *o_mcp_resp,
+               u32 *o_mcp_param)
+{
+       int rc = 0;
+
+       /* MCP not initialized */
+       if (!qed_mcp_is_init(p_hwfn)) {
+               DP_NOTICE(p_hwfn, "MFW is not initialized !\n");
+               return -EBUSY;
+       }
+
+       /* Lock Mutex to ensure only single thread is
+        * accessing the MCP at one time
+        */
+       mutex_lock(&p_hwfn->mcp_info->mutex);
+       rc = qed_do_mcp_cmd(p_hwfn, p_ptt, cmd, param,
+                           o_mcp_resp, o_mcp_param);
+       /* Release Mutex */
+       mutex_unlock(&p_hwfn->mcp_info->mutex);
+
+       return rc;
+}
+
+static void qed_mcp_set_drv_ver(struct qed_dev *cdev,
+                               struct qed_hwfn *p_hwfn,
+                               struct qed_ptt *p_ptt)
+{
+       u32 i;
+
+       /* Copy version string to MCP */
+       for (i = 0; i < MCP_DRV_VER_STR_SIZE_DWORD; i++)
+               DRV_MB_WR(p_hwfn, p_ptt, union_data.ver_str[i],
+                         *(u32 *)&cdev->ver_str[i * sizeof(u32)]);
+}
+
+int qed_mcp_load_req(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt,
+                    u32 *p_load_code)
+{
+       struct qed_dev *cdev = p_hwfn->cdev;
+       u32 param;
+       int rc;
+
+       if (!qed_mcp_is_init(p_hwfn)) {
+               DP_NOTICE(p_hwfn, "MFW is not initialized !\n");
+               return -EBUSY;
+       }
+
+       /* Save driver's version to shmem */
+       qed_mcp_set_drv_ver(cdev, p_hwfn, p_ptt);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SP, "fw_seq 0x%08x, drv_pulse 0x%x\n",
+                  p_hwfn->mcp_info->drv_mb_seq,
+                  p_hwfn->mcp_info->drv_pulse_seq);
+
+       /* Load Request */
+       rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_LOAD_REQ,
+                        (PDA_COMP | DRV_ID_MCP_HSI_VER_CURRENT |
+                         cdev->drv_type),
+                        p_load_code, &param);
+
+       /* if mcp fails to respond we must abort */
+       if (rc) {
+               DP_ERR(p_hwfn, "MCP response failure, aborting\n");
+               return rc;
+       }
+
+       /* If MFW refused (e.g. other port is in diagnostic mode) we
+        * must abort. This can happen in the following cases:
+        * - Other port is in diagnostic mode
+        * - Previously loaded function on the engine is not compliant with
+        *   the requester.
+        * - MFW cannot cope with the requester's DRV_MFW_HSI_VERSION.
+        *      -
+        */
+       if (!(*p_load_code) ||
+           ((*p_load_code) == FW_MSG_CODE_DRV_LOAD_REFUSED_HSI) ||
+           ((*p_load_code) == FW_MSG_CODE_DRV_LOAD_REFUSED_PDA) ||
+           ((*p_load_code) == FW_MSG_CODE_DRV_LOAD_REFUSED_DIAG)) {
+               DP_ERR(p_hwfn, "MCP refused load request, aborting\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static void qed_mcp_handle_link_change(struct qed_hwfn *p_hwfn,
+                                      struct qed_ptt *p_ptt,
+                                      bool b_reset)
+{
+       struct qed_mcp_link_state *p_link;
+       u32 status = 0;
+
+       p_link = &p_hwfn->mcp_info->link_output;
+       memset(p_link, 0, sizeof(*p_link));
+       if (!b_reset) {
+               status = qed_rd(p_hwfn, p_ptt,
+                               p_hwfn->mcp_info->port_addr +
+                               offsetof(struct public_port, link_status));
+               DP_VERBOSE(p_hwfn, (NETIF_MSG_LINK | QED_MSG_SP),
+                          "Received link update [0x%08x] from mfw [Addr 0x%x]\n",
+                          status,
+                          (u32)(p_hwfn->mcp_info->port_addr +
+                                offsetof(struct public_port,
+                                         link_status)));
+       } else {
+               DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
+                          "Resetting link indications\n");
+               return;
+       }
+
+       p_link->link_up = !!(status & LINK_STATUS_LINK_UP);
+
+       p_link->full_duplex = true;
+       switch ((status & LINK_STATUS_SPEED_AND_DUPLEX_MASK)) {
+       case LINK_STATUS_SPEED_AND_DUPLEX_100G:
+               p_link->speed = 100000;
+               break;
+       case LINK_STATUS_SPEED_AND_DUPLEX_50G:
+               p_link->speed = 50000;
+               break;
+       case LINK_STATUS_SPEED_AND_DUPLEX_40G:
+               p_link->speed = 40000;
+               break;
+       case LINK_STATUS_SPEED_AND_DUPLEX_25G:
+               p_link->speed = 25000;
+               break;
+       case LINK_STATUS_SPEED_AND_DUPLEX_20G:
+               p_link->speed = 20000;
+               break;
+       case LINK_STATUS_SPEED_AND_DUPLEX_10G:
+               p_link->speed = 10000;
+               break;
+       case LINK_STATUS_SPEED_AND_DUPLEX_1000THD:
+               p_link->full_duplex = false;
+       /* Fall-through */
+       case LINK_STATUS_SPEED_AND_DUPLEX_1000TFD:
+               p_link->speed = 1000;
+               break;
+       default:
+               p_link->speed = 0;
+       }
+
+       /* Correct speed according to bandwidth allocation */
+       if (p_hwfn->mcp_info->func_info.bandwidth_max && p_link->speed) {
+               p_link->speed = p_link->speed *
+                               p_hwfn->mcp_info->func_info.bandwidth_max /
+                               100;
+               qed_init_pf_rl(p_hwfn, p_ptt, p_hwfn->rel_pf_id,
+                              p_link->speed);
+               DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
+                          "Configured MAX bandwidth to be %08x Mb/sec\n",
+                          p_link->speed);
+       }
+
+       p_link->an = !!(status & LINK_STATUS_AUTO_NEGOTIATE_ENABLED);
+       p_link->an_complete = !!(status &
+                                LINK_STATUS_AUTO_NEGOTIATE_COMPLETE);
+       p_link->parallel_detection = !!(status &
+                                       LINK_STATUS_PARALLEL_DETECTION_USED);
+       p_link->pfc_enabled = !!(status & LINK_STATUS_PFC_ENABLED);
+
+       p_link->partner_adv_speed |=
+               (status & LINK_STATUS_LINK_PARTNER_1000TFD_CAPABLE) ?
+               QED_LINK_PARTNER_SPEED_1G_FD : 0;
+       p_link->partner_adv_speed |=
+               (status & LINK_STATUS_LINK_PARTNER_1000THD_CAPABLE) ?
+               QED_LINK_PARTNER_SPEED_1G_HD : 0;
+       p_link->partner_adv_speed |=
+               (status & LINK_STATUS_LINK_PARTNER_10G_CAPABLE) ?
+               QED_LINK_PARTNER_SPEED_10G : 0;
+       p_link->partner_adv_speed |=
+               (status & LINK_STATUS_LINK_PARTNER_20G_CAPABLE) ?
+               QED_LINK_PARTNER_SPEED_20G : 0;
+       p_link->partner_adv_speed |=
+               (status & LINK_STATUS_LINK_PARTNER_40G_CAPABLE) ?
+               QED_LINK_PARTNER_SPEED_40G : 0;
+       p_link->partner_adv_speed |=
+               (status & LINK_STATUS_LINK_PARTNER_50G_CAPABLE) ?
+               QED_LINK_PARTNER_SPEED_50G : 0;
+       p_link->partner_adv_speed |=
+               (status & LINK_STATUS_LINK_PARTNER_100G_CAPABLE) ?
+               QED_LINK_PARTNER_SPEED_100G : 0;
+
+       p_link->partner_tx_flow_ctrl_en =
+               !!(status & LINK_STATUS_TX_FLOW_CONTROL_ENABLED);
+       p_link->partner_rx_flow_ctrl_en =
+               !!(status & LINK_STATUS_RX_FLOW_CONTROL_ENABLED);
+
+       switch (status & LINK_STATUS_LINK_PARTNER_FLOW_CONTROL_MASK) {
+       case LINK_STATUS_LINK_PARTNER_SYMMETRIC_PAUSE:
+               p_link->partner_adv_pause = QED_LINK_PARTNER_SYMMETRIC_PAUSE;
+               break;
+       case LINK_STATUS_LINK_PARTNER_ASYMMETRIC_PAUSE:
+               p_link->partner_adv_pause = QED_LINK_PARTNER_ASYMMETRIC_PAUSE;
+               break;
+       case LINK_STATUS_LINK_PARTNER_BOTH_PAUSE:
+               p_link->partner_adv_pause = QED_LINK_PARTNER_BOTH_PAUSE;
+               break;
+       default:
+               p_link->partner_adv_pause = 0;
+       }
+
+       p_link->sfp_tx_fault = !!(status & LINK_STATUS_SFP_TX_FAULT);
+
+       qed_link_update(p_hwfn);
+}
+
+int qed_mcp_set_link(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt,
+                    bool b_up)
+{
+       struct qed_mcp_link_params *params = &p_hwfn->mcp_info->link_input;
+       u32 param = 0, reply = 0, cmd;
+       struct pmm_phy_cfg phy_cfg;
+       int rc = 0;
+       u32 i;
+
+       if (!qed_mcp_is_init(p_hwfn)) {
+               DP_NOTICE(p_hwfn, "MFW is not initialized !\n");
+               return -EBUSY;
+       }
+
+       /* Set the shmem configuration according to params */
+       memset(&phy_cfg, 0, sizeof(phy_cfg));
+       cmd = b_up ? DRV_MSG_CODE_INIT_PHY : DRV_MSG_CODE_LINK_RESET;
+       if (!params->speed.autoneg)
+               phy_cfg.speed = params->speed.forced_speed;
+       phy_cfg.pause |= (params->pause.autoneg) ? PMM_PAUSE_AUTONEG : 0;
+       phy_cfg.pause |= (params->pause.forced_rx) ? PMM_PAUSE_RX : 0;
+       phy_cfg.pause |= (params->pause.forced_tx) ? PMM_PAUSE_TX : 0;
+       phy_cfg.adv_speed = params->speed.advertised_speeds;
+       phy_cfg.loopback_mode = params->loopback_mode;
+
+       /* Write the requested configuration to shmem */
+       for (i = 0; i < sizeof(phy_cfg); i += 4)
+               qed_wr(p_hwfn, p_ptt,
+                      p_hwfn->mcp_info->drv_mb_addr +
+                      offsetof(struct public_drv_mb, union_data) + i,
+                      ((u32 *)&phy_cfg)[i >> 2]);
+
+       if (b_up) {
+               DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
+                          "Configuring Link: Speed 0x%08x, Pause 0x%08x, adv_speed 0x%08x, loopback 0x%08x, features 0x%08x\n",
+                          phy_cfg.speed,
+                          phy_cfg.pause,
+                          phy_cfg.adv_speed,
+                          phy_cfg.loopback_mode,
+                          phy_cfg.feature_config_flags);
+       } else {
+               DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
+                          "Resetting link\n");
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SP, "fw_seq 0x%08x, drv_pulse 0x%x\n",
+                  p_hwfn->mcp_info->drv_mb_seq,
+                  p_hwfn->mcp_info->drv_pulse_seq);
+
+       /* Load Request */
+       rc = qed_mcp_cmd(p_hwfn, p_ptt, cmd, 0, &reply, &param);
+
+       /* if mcp fails to respond we must abort */
+       if (rc) {
+               DP_ERR(p_hwfn, "MCP response failure, aborting\n");
+               return rc;
+       }
+
+       /* Reset the link status if needed */
+       if (!b_up)
+               qed_mcp_handle_link_change(p_hwfn, p_ptt, true);
+
+       return 0;
+}
+
+int qed_mcp_handle_events(struct qed_hwfn *p_hwfn,
+                         struct qed_ptt *p_ptt)
+{
+       struct qed_mcp_info *info = p_hwfn->mcp_info;
+       int rc = 0;
+       bool found = false;
+       u16 i;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SP, "Received message from MFW\n");
+
+       /* Read Messages from MFW */
+       qed_mcp_read_mb(p_hwfn, p_ptt);
+
+       /* Compare current messages to old ones */
+       for (i = 0; i < info->mfw_mb_length; i++) {
+               if (info->mfw_mb_cur[i] == info->mfw_mb_shadow[i])
+                       continue;
+
+               found = true;
+
+               DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
+                          "Msg [%d] - old CMD 0x%02x, new CMD 0x%02x\n",
+                          i, info->mfw_mb_shadow[i], info->mfw_mb_cur[i]);
+
+               switch (i) {
+               case MFW_DRV_MSG_LINK_CHANGE:
+                       qed_mcp_handle_link_change(p_hwfn, p_ptt, false);
+                       break;
+               default:
+                       DP_NOTICE(p_hwfn, "Unimplemented MFW message %d\n", i);
+                       rc = -EINVAL;
+               }
+       }
+
+       /* ACK everything */
+       for (i = 0; i < MFW_DRV_MSG_MAX_DWORDS(info->mfw_mb_length); i++) {
+               __be32 val = cpu_to_be32(((u32 *)info->mfw_mb_cur)[i]);
+
+               /* MFW expect answer in BE, so we force write in that format */
+               qed_wr(p_hwfn, p_ptt,
+                      info->mfw_mb_addr + sizeof(u32) +
+                      MFW_DRV_MSG_MAX_DWORDS(info->mfw_mb_length) *
+                      sizeof(u32) + i * sizeof(u32),
+                      (__force u32)val);
+       }
+
+       if (!found) {
+               DP_NOTICE(p_hwfn,
+                         "Received an MFW message indication but no new message!\n");
+               rc = -EINVAL;
+       }
+
+       /* Copy the new mfw messages into the shadow */
+       memcpy(info->mfw_mb_shadow, info->mfw_mb_cur, info->mfw_mb_length);
+
+       return rc;
+}
+
+int qed_mcp_get_mfw_ver(struct qed_dev *cdev,
+                       u32 *p_mfw_ver)
+{
+       struct qed_hwfn *p_hwfn = &cdev->hwfns[0];
+       struct qed_ptt *p_ptt;
+       u32 global_offsize;
+
+       p_ptt = qed_ptt_acquire(p_hwfn);
+       if (!p_ptt)
+               return -EBUSY;
+
+       global_offsize = qed_rd(p_hwfn, p_ptt,
+                               SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info->
+                                                    public_base,
+                                                    PUBLIC_GLOBAL));
+       *p_mfw_ver = qed_rd(p_hwfn, p_ptt,
+                           SECTION_ADDR(global_offsize, 0) +
+                           offsetof(struct public_global, mfw_ver));
+
+       qed_ptt_release(p_hwfn, p_ptt);
+
+       return 0;
+}
+
+int qed_mcp_get_media_type(struct qed_dev *cdev,
+                          u32 *p_media_type)
+{
+       struct qed_hwfn *p_hwfn = &cdev->hwfns[0];
+       struct qed_ptt  *p_ptt;
+
+       if (!qed_mcp_is_init(p_hwfn)) {
+               DP_NOTICE(p_hwfn, "MFW is not initialized !\n");
+               return -EBUSY;
+       }
+
+       *p_media_type = MEDIA_UNSPECIFIED;
+
+       p_ptt = qed_ptt_acquire(p_hwfn);
+       if (!p_ptt)
+               return -EBUSY;
+
+       *p_media_type = qed_rd(p_hwfn, p_ptt, p_hwfn->mcp_info->port_addr +
+                              offsetof(struct public_port, media_type));
+
+       qed_ptt_release(p_hwfn, p_ptt);
+
+       return 0;
+}
+
+static u32 qed_mcp_get_shmem_func(struct qed_hwfn *p_hwfn,
+                                 struct qed_ptt *p_ptt,
+                                 struct public_func *p_data,
+                                 int pfid)
+{
+       u32 addr = SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info->public_base,
+                                       PUBLIC_FUNC);
+       u32 mfw_path_offsize = qed_rd(p_hwfn, p_ptt, addr);
+       u32 func_addr = SECTION_ADDR(mfw_path_offsize, pfid);
+       u32 i, size;
+
+       memset(p_data, 0, sizeof(*p_data));
+
+       size = min_t(u32, sizeof(*p_data),
+                    QED_SECTION_SIZE(mfw_path_offsize));
+       for (i = 0; i < size / sizeof(u32); i++)
+               ((u32 *)p_data)[i] = qed_rd(p_hwfn, p_ptt,
+                                           func_addr + (i << 2));
+
+       return size;
+}
+
+static int
+qed_mcp_get_shmem_proto(struct qed_hwfn *p_hwfn,
+                       struct public_func *p_info,
+                       enum qed_pci_personality *p_proto)
+{
+       int rc = 0;
+
+       switch (p_info->config & FUNC_MF_CFG_PROTOCOL_MASK) {
+       case FUNC_MF_CFG_PROTOCOL_ETHERNET:
+               *p_proto = QED_PCI_ETH;
+               break;
+       default:
+               rc = -EINVAL;
+       }
+
+       return rc;
+}
+
+int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn,
+                                struct qed_ptt *p_ptt)
+{
+       struct qed_mcp_function_info *info;
+       struct public_func shmem_info;
+
+       qed_mcp_get_shmem_func(p_hwfn, p_ptt, &shmem_info,
+                              MCP_PF_ID(p_hwfn));
+       info = &p_hwfn->mcp_info->func_info;
+
+       info->pause_on_host = (shmem_info.config &
+                              FUNC_MF_CFG_PAUSE_ON_HOST_RING) ? 1 : 0;
+
+       if (qed_mcp_get_shmem_proto(p_hwfn, &shmem_info,
+                                   &info->protocol)) {
+               DP_ERR(p_hwfn, "Unknown personality %08x\n",
+                      (u32)(shmem_info.config & FUNC_MF_CFG_PROTOCOL_MASK));
+               return -EINVAL;
+       }
+
+       if (p_hwfn->cdev->mf_mode != SF) {
+               info->bandwidth_min = (shmem_info.config &
+                                      FUNC_MF_CFG_MIN_BW_MASK) >>
+                                     FUNC_MF_CFG_MIN_BW_SHIFT;
+               if (info->bandwidth_min < 1 || info->bandwidth_min > 100) {
+                       DP_INFO(p_hwfn,
+                               "bandwidth minimum out of bounds [%02x]. Set to 1\n",
+                               info->bandwidth_min);
+                       info->bandwidth_min = 1;
+               }
+
+               info->bandwidth_max = (shmem_info.config &
+                                      FUNC_MF_CFG_MAX_BW_MASK) >>
+                                     FUNC_MF_CFG_MAX_BW_SHIFT;
+               if (info->bandwidth_max < 1 || info->bandwidth_max > 100) {
+                       DP_INFO(p_hwfn,
+                               "bandwidth maximum out of bounds [%02x]. Set to 100\n",
+                               info->bandwidth_max);
+                       info->bandwidth_max = 100;
+               }
+       }
+
+       if (shmem_info.mac_upper || shmem_info.mac_lower) {
+               info->mac[0] = (u8)(shmem_info.mac_upper >> 8);
+               info->mac[1] = (u8)(shmem_info.mac_upper);
+               info->mac[2] = (u8)(shmem_info.mac_lower >> 24);
+               info->mac[3] = (u8)(shmem_info.mac_lower >> 16);
+               info->mac[4] = (u8)(shmem_info.mac_lower >> 8);
+               info->mac[5] = (u8)(shmem_info.mac_lower);
+       } else {
+               DP_NOTICE(p_hwfn, "MAC is 0 in shmem\n");
+       }
+
+       info->wwn_port = (u64)shmem_info.fcoe_wwn_port_name_upper |
+                        (((u64)shmem_info.fcoe_wwn_port_name_lower) << 32);
+       info->wwn_node = (u64)shmem_info.fcoe_wwn_node_name_upper |
+                        (((u64)shmem_info.fcoe_wwn_node_name_lower) << 32);
+
+       info->ovlan = (u16)(shmem_info.ovlan_stag & FUNC_MF_CFG_OV_STAG_MASK);
+
+       DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_IFUP),
+                  "Read configuration from shmem: pause_on_host %02x protocol %02x BW [%02x - %02x] MAC %02x:%02x:%02x:%02x:%02x:%02x wwn port %llx node %llx ovlan %04x\n",
+               info->pause_on_host, info->protocol,
+               info->bandwidth_min, info->bandwidth_max,
+               info->mac[0], info->mac[1], info->mac[2],
+               info->mac[3], info->mac[4], info->mac[5],
+               info->wwn_port, info->wwn_node, info->ovlan);
+
+       return 0;
+}
+
+struct qed_mcp_link_params
+*qed_mcp_get_link_params(struct qed_hwfn *p_hwfn)
+{
+       if (!p_hwfn || !p_hwfn->mcp_info)
+               return NULL;
+       return &p_hwfn->mcp_info->link_input;
+}
+
+struct qed_mcp_link_state
+*qed_mcp_get_link_state(struct qed_hwfn *p_hwfn)
+{
+       if (!p_hwfn || !p_hwfn->mcp_info)
+               return NULL;
+       return &p_hwfn->mcp_info->link_output;
+}
+
+struct qed_mcp_link_capabilities
+*qed_mcp_get_link_capabilities(struct qed_hwfn *p_hwfn)
+{
+       if (!p_hwfn || !p_hwfn->mcp_info)
+               return NULL;
+       return &p_hwfn->mcp_info->link_capabilities;
+}
+
+int qed_mcp_drain(struct qed_hwfn *p_hwfn,
+                 struct qed_ptt *p_ptt)
+{
+       u32 resp = 0, param = 0;
+       int rc;
+
+       rc = qed_mcp_cmd(p_hwfn, p_ptt,
+                        DRV_MSG_CODE_NIG_DRAIN, 100,
+                        &resp, &param);
+
+       /* Wait for the drain to complete before returning */
+       msleep(120);
+
+       return rc;
+}
+
+int qed_mcp_get_flash_size(struct qed_hwfn *p_hwfn,
+                          struct qed_ptt *p_ptt,
+                          u32 *p_flash_size)
+{
+       u32 flash_size;
+
+       flash_size = qed_rd(p_hwfn, p_ptt, MCP_REG_NVM_CFG4);
+       flash_size = (flash_size & MCP_REG_NVM_CFG4_FLASH_SIZE) >>
+                     MCP_REG_NVM_CFG4_FLASH_SIZE_SHIFT;
+       flash_size = (1 << (flash_size + MCP_BYTES_PER_MBIT_SHIFT));
+
+       *p_flash_size = flash_size;
+
+       return 0;
+}
+
+int
+qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn,
+                        struct qed_ptt *p_ptt,
+                        struct qed_mcp_drv_version *p_ver)
+{
+       int rc = 0;
+       u32 param = 0, reply = 0, i;
+
+       if (!qed_mcp_is_init(p_hwfn)) {
+               DP_NOTICE(p_hwfn, "MFW is not initialized !\n");
+               return -EBUSY;
+       }
+
+       DRV_MB_WR(p_hwfn, p_ptt, union_data.drv_version.version,
+                 p_ver->version);
+       /* Copy version string to shmem */
+       for (i = 0; i < (MCP_DRV_VER_STR_SIZE - 4) / 4; i++) {
+               DRV_MB_WR(p_hwfn, p_ptt,
+                         union_data.drv_version.name[i * sizeof(u32)],
+                         *(u32 *)&p_ver->name[i * sizeof(u32)]);
+       }
+
+       rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_SET_VERSION, 0, &reply,
+                        &param);
+       if (rc) {
+               DP_ERR(p_hwfn, "MCP response failure, aborting\n");
+               return rc;
+       }
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
new file mode 100644 (file)
index 0000000..dbaae58
--- /dev/null
@@ -0,0 +1,369 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_MCP_H
+#define _QED_MCP_H
+
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include "qed_hsi.h"
+
+struct qed_mcp_link_speed_params {
+       bool    autoneg;
+       u32     advertised_speeds;      /* bitmask of DRV_SPEED_CAPABILITY */
+       u32     forced_speed;      /* In Mb/s */
+};
+
+struct qed_mcp_link_pause_params {
+       bool    autoneg;
+       bool    forced_rx;
+       bool    forced_tx;
+};
+
+struct qed_mcp_link_params {
+       struct qed_mcp_link_speed_params        speed;
+       struct qed_mcp_link_pause_params        pause;
+       u32                                  loopback_mode;
+};
+
+struct qed_mcp_link_capabilities {
+       u32 speed_capabilities;
+};
+
+struct qed_mcp_link_state {
+       bool    link_up;
+
+       u32     speed; /* In Mb/s */
+       bool    full_duplex;
+
+       bool    an;
+       bool    an_complete;
+       bool    parallel_detection;
+       bool    pfc_enabled;
+
+#define QED_LINK_PARTNER_SPEED_1G_HD    BIT(0)
+#define QED_LINK_PARTNER_SPEED_1G_FD    BIT(1)
+#define QED_LINK_PARTNER_SPEED_10G      BIT(2)
+#define QED_LINK_PARTNER_SPEED_20G      BIT(3)
+#define QED_LINK_PARTNER_SPEED_40G      BIT(4)
+#define QED_LINK_PARTNER_SPEED_50G      BIT(5)
+#define QED_LINK_PARTNER_SPEED_100G     BIT(6)
+       u32     partner_adv_speed;
+
+       bool    partner_tx_flow_ctrl_en;
+       bool    partner_rx_flow_ctrl_en;
+
+#define QED_LINK_PARTNER_SYMMETRIC_PAUSE (1)
+#define QED_LINK_PARTNER_ASYMMETRIC_PAUSE (2)
+#define QED_LINK_PARTNER_BOTH_PAUSE (3)
+       u8      partner_adv_pause;
+
+       bool    sfp_tx_fault;
+};
+
+struct qed_mcp_function_info {
+       u8                              pause_on_host;
+
+       enum qed_pci_personality        protocol;
+
+       u8                              bandwidth_min;
+       u8                              bandwidth_max;
+
+       u8                              mac[ETH_ALEN];
+
+       u64                             wwn_port;
+       u64                             wwn_node;
+
+#define QED_MCP_VLAN_UNSET              (0xffff)
+       u16                             ovlan;
+};
+
+struct qed_mcp_nvm_common {
+       u32     offset;
+       u32     param;
+       u32     resp;
+       u32     cmd;
+};
+
+struct qed_mcp_drv_version {
+       u32     version;
+       u8      name[MCP_DRV_VER_STR_SIZE - 4];
+};
+
+/**
+ * @brief - returns the link params of the hw function
+ *
+ * @param p_hwfn
+ *
+ * @returns pointer to link params
+ */
+struct qed_mcp_link_params *qed_mcp_get_link_params(struct qed_hwfn *);
+
+/**
+ * @brief - return the link state of the hw function
+ *
+ * @param p_hwfn
+ *
+ * @returns pointer to link state
+ */
+struct qed_mcp_link_state *qed_mcp_get_link_state(struct qed_hwfn *);
+
+/**
+ * @brief - return the link capabilities of the hw function
+ *
+ * @param p_hwfn
+ *
+ * @returns pointer to link capabilities
+ */
+struct qed_mcp_link_capabilities
+       *qed_mcp_get_link_capabilities(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief Request the MFW to set the the link according to 'link_input'.
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param b_up - raise link if `true'. Reset link if `false'.
+ *
+ * @return int
+ */
+int qed_mcp_set_link(struct qed_hwfn   *p_hwfn,
+                    struct qed_ptt     *p_ptt,
+                    bool               b_up);
+
+/**
+ * @brief Get the management firmware version value
+ *
+ * @param cdev       - qed dev pointer
+ * @param mfw_ver    - mfw version value
+ *
+ * @return int - 0 - operation was successul.
+ */
+int qed_mcp_get_mfw_ver(struct qed_dev *cdev,
+                       u32 *mfw_ver);
+
+/**
+ * @brief Get media type value of the port.
+ *
+ * @param cdev      - qed dev pointer
+ * @param mfw_ver    - media type value
+ *
+ * @return int -
+ *      0 - Operation was successul.
+ *      -EBUSY - Operation failed
+ */
+int qed_mcp_get_media_type(struct qed_dev      *cdev,
+                          u32                  *media_type);
+
+/**
+ * @brief General function for sending commands to the MCP
+ *        mailbox. It acquire mutex lock for the entire
+ *        operation, from sending the request until the MCP
+ *        response. Waiting for MCP response will be checked up
+ *        to 5 seconds every 5ms.
+ *
+ * @param p_hwfn     - hw function
+ * @param p_ptt      - PTT required for register access
+ * @param cmd        - command to be sent to the MCP.
+ * @param param      - Optional param
+ * @param o_mcp_resp - The MCP response code (exclude sequence).
+ * @param o_mcp_param- Optional parameter provided by the MCP
+ *                     response
+ * @return int - 0 - operation
+ * was successul.
+ */
+int qed_mcp_cmd(struct qed_hwfn *p_hwfn,
+               struct qed_ptt *p_ptt,
+               u32 cmd,
+               u32 param,
+               u32 *o_mcp_resp,
+               u32 *o_mcp_param);
+
+/**
+ * @brief - drains the nig, allowing completion to pass in case of pauses.
+ *          (Should be called only from sleepable context)
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ */
+int qed_mcp_drain(struct qed_hwfn *p_hwfn,
+                 struct qed_ptt *p_ptt);
+
+/**
+ * @brief Get the flash size value
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param p_flash_size  - flash size in bytes to be filled.
+ *
+ * @return int - 0 - operation was successul.
+ */
+int qed_mcp_get_flash_size(struct qed_hwfn     *p_hwfn,
+                          struct qed_ptt       *p_ptt,
+                          u32 *p_flash_size);
+
+/**
+ * @brief Send driver version to MFW
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param version - Version value
+ * @param name - Protocol driver name
+ *
+ * @return int - 0 - operation was successul.
+ */
+int
+qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn,
+                        struct qed_ptt *p_ptt,
+                        struct qed_mcp_drv_version *p_ver);
+
+/* Using hwfn number (and not pf_num) is required since in CMT mode,
+ * same pf_num may be used by two different hwfn
+ * TODO - this shouldn't really be in .h file, but until all fields
+ * required during hw-init will be placed in their correct place in shmem
+ * we need it in qed_dev.c [for readin the nvram reflection in shmem].
+ */
+#define MCP_PF_ID_BY_REL(p_hwfn, rel_pfid) (QED_IS_BB((p_hwfn)->cdev) ?               \
+                                           ((rel_pfid) |                      \
+                                            ((p_hwfn)->abs_pf_id & 1) << 3) : \
+                                           rel_pfid)
+#define MCP_PF_ID(p_hwfn) MCP_PF_ID_BY_REL(p_hwfn, (p_hwfn)->rel_pf_id)
+
+/* TODO - this is only correct as long as only BB is supported, and
+ * no port-swapping is implemented; Afterwards we'll need to fix it.
+ */
+#define MFW_PORT(_p_hwfn)       ((_p_hwfn)->abs_pf_id %        \
+                                ((_p_hwfn)->cdev->num_ports_in_engines * 2))
+struct qed_mcp_info {
+       struct mutex                            mutex; /* MCP access lock */
+       u32                                     public_base;
+       u32                                     drv_mb_addr;
+       u32                                     mfw_mb_addr;
+       u32                                     port_addr;
+       u16                                     drv_mb_seq;
+       u16                                     drv_pulse_seq;
+       struct qed_mcp_link_params              link_input;
+       struct qed_mcp_link_state               link_output;
+       struct qed_mcp_link_capabilities        link_capabilities;
+       struct qed_mcp_function_info            func_info;
+       u8                                      *mfw_mb_cur;
+       u8                                      *mfw_mb_shadow;
+       u16                                     mfw_mb_length;
+       u16                                     mcp_hist;
+};
+
+/**
+ * @brief Initialize the interface with the MCP
+ *
+ * @param p_hwfn - HW func
+ * @param p_ptt - PTT required for register access
+ *
+ * @return int
+ */
+int qed_mcp_cmd_init(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt);
+
+/**
+ * @brief Initialize the port interface with the MCP
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * Can only be called after `num_ports_in_engines' is set
+ */
+void qed_mcp_cmd_port_init(struct qed_hwfn *p_hwfn,
+                          struct qed_ptt *p_ptt);
+/**
+ * @brief Releases resources allocated during the init process.
+ *
+ * @param p_hwfn - HW func
+ * @param p_ptt - PTT required for register access
+ *
+ * @return int
+ */
+
+int qed_mcp_free(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief This function is called from the DPC context. After
+ * pointing PTT to the mfw mb, check for events sent by the MCP
+ * to the driver and ack them. In case a critical event
+ * detected, it will be handled here, otherwise the work will be
+ * queued to a sleepable work-queue.
+ *
+ * @param p_hwfn - HW function
+ * @param p_ptt - PTT required for register access
+ * @return int - 0 - operation
+ * was successul.
+ */
+int qed_mcp_handle_events(struct qed_hwfn *p_hwfn,
+                         struct qed_ptt *p_ptt);
+
+/**
+ * @brief Sends a LOAD_REQ to the MFW, and in case operation
+ *        succeed, returns whether this PF is the first on the
+ *        chip/engine/port or function. This function should be
+ *        called when driver is ready to accept MFW events after
+ *        Storms initializations are done.
+ *
+ * @param p_hwfn       - hw function
+ * @param p_ptt        - PTT required for register access
+ * @param p_load_code  - The MCP response param containing one
+ *      of the following:
+ *      FW_MSG_CODE_DRV_LOAD_ENGINE
+ *      FW_MSG_CODE_DRV_LOAD_PORT
+ *      FW_MSG_CODE_DRV_LOAD_FUNCTION
+ * @return int -
+ *      0 - Operation was successul.
+ *      -EBUSY - Operation failed
+ */
+int qed_mcp_load_req(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt,
+                    u32 *p_load_code);
+
+/**
+ * @brief Read the MFW mailbox into Current buffer.
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ */
+void qed_mcp_read_mb(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt);
+
+/**
+ * @brief - calls during init to read shmem of all function-related info.
+ *
+ * @param p_hwfn
+ *
+ * @param return 0 upon success.
+ */
+int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn,
+                                struct qed_ptt *p_ptt);
+
+/**
+ * @brief - Reset the MCP using mailbox command.
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ *
+ * @param return 0 upon success.
+ */
+int qed_mcp_reset(struct qed_hwfn *p_hwfn,
+                 struct qed_ptt *p_ptt);
+
+/**
+ * @brief indicates whether the MFW objects [under mcp_info] are accessible
+ *
+ * @param p_hwfn
+ *
+ * @return true iff MFW is running and mcp_info is initialized
+ */
+bool qed_mcp_is_init(struct qed_hwfn *p_hwfn);
+
+#endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h
new file mode 100644 (file)
index 0000000..7a5ce59
--- /dev/null
@@ -0,0 +1,366 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef REG_ADDR_H
+#define REG_ADDR_H
+
+#define  CDU_REG_CID_ADDR_PARAMS_CONTEXT_SIZE_SHIFT \
+       0
+
+#define  CDU_REG_CID_ADDR_PARAMS_CONTEXT_SIZE          ( \
+               0xfff << 0)
+
+#define  CDU_REG_CID_ADDR_PARAMS_BLOCK_WASTE_SHIFT \
+       12
+
+#define  CDU_REG_CID_ADDR_PARAMS_BLOCK_WASTE           ( \
+               0xfff << 12)
+
+#define  CDU_REG_CID_ADDR_PARAMS_NCIB_SHIFT \
+       24
+
+#define  CDU_REG_CID_ADDR_PARAMS_NCIB                  ( \
+               0xff << 24)
+
+#define  XSDM_REG_OPERATION_GEN \
+       0xf80408UL
+#define  NIG_REG_RX_BRB_OUT_EN \
+       0x500e18UL
+#define  NIG_REG_STORM_OUT_EN \
+       0x500e08UL
+#define  PSWRQ2_REG_L2P_VALIDATE_VFID \
+       0x240c50UL
+#define  PGLUE_B_REG_USE_CLIENTID_IN_TAG       \
+       0x2aae04UL
+#define  PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER       \
+       0x2aa16cUL
+#define  BAR0_MAP_REG_MSDM_RAM \
+       0x1d00000UL
+#define  BAR0_MAP_REG_USDM_RAM \
+       0x1d80000UL
+#define  BAR0_MAP_REG_PSDM_RAM \
+       0x1f00000UL
+#define  BAR0_MAP_REG_TSDM_RAM \
+       0x1c80000UL
+#define  NIG_REG_RX_LLH_BRB_GATE_DNTFWD_PERPF \
+       0x5011f4UL
+#define  PRS_REG_SEARCH_TCP \
+       0x1f0400UL
+#define  PRS_REG_SEARCH_UDP \
+       0x1f0404UL
+#define  PRS_REG_SEARCH_FCOE \
+       0x1f0408UL
+#define  PRS_REG_SEARCH_ROCE \
+       0x1f040cUL
+#define  PRS_REG_SEARCH_OPENFLOW       \
+       0x1f0434UL
+#define  TM_REG_PF_ENABLE_CONN \
+       0x2c043cUL
+#define  TM_REG_PF_ENABLE_TASK \
+       0x2c0444UL
+#define  TM_REG_PF_SCAN_ACTIVE_CONN \
+       0x2c04fcUL
+#define  TM_REG_PF_SCAN_ACTIVE_TASK \
+       0x2c0500UL
+#define  IGU_REG_LEADING_EDGE_LATCH \
+       0x18082cUL
+#define  IGU_REG_TRAILING_EDGE_LATCH \
+       0x180830UL
+#define  QM_REG_USG_CNT_PF_TX \
+       0x2f2eacUL
+#define  QM_REG_USG_CNT_PF_OTHER       \
+       0x2f2eb0UL
+#define  DORQ_REG_PF_DB_ENABLE \
+       0x100508UL
+#define  QM_REG_PF_EN \
+       0x2f2ea4UL
+#define  TCFC_REG_STRONG_ENABLE_PF \
+       0x2d0708UL
+#define  CCFC_REG_STRONG_ENABLE_PF \
+       0x2e0708UL
+#define  PGLUE_B_REG_PGL_ADDR_88_F0 \
+       0x2aa404UL
+#define  PGLUE_B_REG_PGL_ADDR_8C_F0 \
+       0x2aa408UL
+#define  PGLUE_B_REG_PGL_ADDR_90_F0 \
+       0x2aa40cUL
+#define  PGLUE_B_REG_PGL_ADDR_94_F0 \
+       0x2aa410UL
+#define  PGLUE_B_REG_WAS_ERROR_PF_31_0_CLR \
+       0x2aa138UL
+#define  PGLUE_B_REG_INTERNAL_PFID_ENABLE_TARGET_READ \
+       0x2aa174UL
+#define  MISC_REG_GEN_PURP_CR0 \
+       0x008c80UL
+#define  MCP_REG_SCRATCH       \
+       0xe20000UL
+#define  CNIG_REG_NW_PORT_MODE_BB_B0 \
+       0x218200UL
+#define  MISCS_REG_CHIP_NUM \
+       0x00976cUL
+#define  MISCS_REG_CHIP_REV \
+       0x009770UL
+#define  MISCS_REG_CMT_ENABLED_FOR_PAIR \
+       0x00971cUL
+#define  MISCS_REG_CHIP_TEST_REG       \
+       0x009778UL
+#define  MISCS_REG_CHIP_METAL \
+       0x009774UL
+#define  BRB_REG_HEADER_SIZE \
+       0x340804UL
+#define  BTB_REG_HEADER_SIZE \
+       0xdb0804UL
+#define  CAU_REG_LONG_TIMEOUT_THRESHOLD \
+       0x1c0708UL
+#define  CCFC_REG_ACTIVITY_COUNTER \
+       0x2e8800UL
+#define  CDU_REG_CID_ADDR_PARAMS       \
+       0x580900UL
+#define  DBG_REG_CLIENT_ENABLE \
+       0x010004UL
+#define  DMAE_REG_INIT \
+       0x00c000UL
+#define  DORQ_REG_IFEN \
+       0x100040UL
+#define  GRC_REG_TIMEOUT_EN \
+       0x050404UL
+#define  IGU_REG_BLOCK_CONFIGURATION \
+       0x180040UL
+#define  MCM_REG_INIT \
+       0x1200000UL
+#define  MCP2_REG_DBG_DWORD_ENABLE \
+       0x052404UL
+#define  MISC_REG_PORT_MODE \
+       0x008c00UL
+#define  MISCS_REG_CLK_100G_MODE       \
+       0x009070UL
+#define  MSDM_REG_ENABLE_IN1 \
+       0xfc0004UL
+#define  MSEM_REG_ENABLE_IN \
+       0x1800004UL
+#define  NIG_REG_CM_HDR \
+       0x500840UL
+#define  NCSI_REG_CONFIG       \
+       0x040200UL
+#define  PBF_REG_INIT \
+       0xd80000UL
+#define  PTU_REG_ATC_INIT_ARRAY \
+       0x560000UL
+#define  PCM_REG_INIT \
+       0x1100000UL
+#define  PGLUE_B_REG_ADMIN_PER_PF_REGION       \
+       0x2a9000UL
+#define  PRM_REG_DISABLE_PRM \
+       0x230000UL
+#define  PRS_REG_SOFT_RST \
+       0x1f0000UL
+#define  PSDM_REG_ENABLE_IN1 \
+       0xfa0004UL
+#define  PSEM_REG_ENABLE_IN \
+       0x1600004UL
+#define  PSWRQ_REG_DBG_SELECT \
+       0x280020UL
+#define  PSWRQ2_REG_CDUT_P_SIZE \
+       0x24000cUL
+#define  PSWHST_REG_DISCARD_INTERNAL_WRITES \
+       0x2a0040UL
+#define  PSWHST2_REG_DBGSYN_ALMOST_FULL_THR \
+       0x29e050UL
+#define  PSWRD_REG_DBG_SELECT \
+       0x29c040UL
+#define  PSWRD2_REG_CONF11 \
+       0x29d064UL
+#define  PSWWR_REG_USDM_FULL_TH \
+       0x29a040UL
+#define  PSWWR2_REG_CDU_FULL_TH2       \
+       0x29b040UL
+#define  QM_REG_MAXPQSIZE_0 \
+       0x2f0434UL
+#define  RSS_REG_RSS_INIT_EN \
+       0x238804UL
+#define  RDIF_REG_STOP_ON_ERROR \
+       0x300040UL
+#define  SRC_REG_SOFT_RST \
+       0x23874cUL
+#define  TCFC_REG_ACTIVITY_COUNTER \
+       0x2d8800UL
+#define  TCM_REG_INIT \
+       0x1180000UL
+#define  TM_REG_PXP_READ_DATA_FIFO_INIT \
+       0x2c0014UL
+#define  TSDM_REG_ENABLE_IN1 \
+       0xfb0004UL
+#define  TSEM_REG_ENABLE_IN \
+       0x1700004UL
+#define  TDIF_REG_STOP_ON_ERROR \
+       0x310040UL
+#define  UCM_REG_INIT \
+       0x1280000UL
+#define  UMAC_REG_IPG_HD_BKP_CNTL_BB_B0 \
+       0x051004UL
+#define  USDM_REG_ENABLE_IN1 \
+       0xfd0004UL
+#define  USEM_REG_ENABLE_IN \
+       0x1900004UL
+#define  XCM_REG_INIT \
+       0x1000000UL
+#define  XSDM_REG_ENABLE_IN1 \
+       0xf80004UL
+#define  XSEM_REG_ENABLE_IN \
+       0x1400004UL
+#define  YCM_REG_INIT \
+       0x1080000UL
+#define  YSDM_REG_ENABLE_IN1 \
+       0xf90004UL
+#define  YSEM_REG_ENABLE_IN \
+       0x1500004UL
+#define  XYLD_REG_SCBD_STRICT_PRIO \
+       0x4c0000UL
+#define  TMLD_REG_SCBD_STRICT_PRIO \
+       0x4d0000UL
+#define  MULD_REG_SCBD_STRICT_PRIO \
+       0x4e0000UL
+#define  YULD_REG_SCBD_STRICT_PRIO \
+       0x4c8000UL
+#define  MISC_REG_SHARED_MEM_ADDR \
+       0x008c20UL
+#define  DMAE_REG_GO_C0 \
+       0x00c048UL
+#define  DMAE_REG_GO_C1 \
+       0x00c04cUL
+#define  DMAE_REG_GO_C2 \
+       0x00c050UL
+#define  DMAE_REG_GO_C3 \
+       0x00c054UL
+#define  DMAE_REG_GO_C4 \
+       0x00c058UL
+#define  DMAE_REG_GO_C5 \
+       0x00c05cUL
+#define  DMAE_REG_GO_C6 \
+       0x00c060UL
+#define  DMAE_REG_GO_C7 \
+       0x00c064UL
+#define  DMAE_REG_GO_C8 \
+       0x00c068UL
+#define  DMAE_REG_GO_C9 \
+       0x00c06cUL
+#define  DMAE_REG_GO_C10       \
+       0x00c070UL
+#define  DMAE_REG_GO_C11       \
+       0x00c074UL
+#define  DMAE_REG_GO_C12       \
+       0x00c078UL
+#define  DMAE_REG_GO_C13       \
+       0x00c07cUL
+#define  DMAE_REG_GO_C14       \
+       0x00c080UL
+#define  DMAE_REG_GO_C15       \
+       0x00c084UL
+#define  DMAE_REG_GO_C16       \
+       0x00c088UL
+#define  DMAE_REG_GO_C17       \
+       0x00c08cUL
+#define  DMAE_REG_GO_C18       \
+       0x00c090UL
+#define  DMAE_REG_GO_C19       \
+       0x00c094UL
+#define  DMAE_REG_GO_C20       \
+       0x00c098UL
+#define  DMAE_REG_GO_C21       \
+       0x00c09cUL
+#define  DMAE_REG_GO_C22       \
+       0x00c0a0UL
+#define  DMAE_REG_GO_C23       \
+       0x00c0a4UL
+#define  DMAE_REG_GO_C24       \
+       0x00c0a8UL
+#define  DMAE_REG_GO_C25       \
+       0x00c0acUL
+#define  DMAE_REG_GO_C26       \
+       0x00c0b0UL
+#define  DMAE_REG_GO_C27       \
+       0x00c0b4UL
+#define  DMAE_REG_GO_C28       \
+       0x00c0b8UL
+#define  DMAE_REG_GO_C29       \
+       0x00c0bcUL
+#define  DMAE_REG_GO_C30       \
+       0x00c0c0UL
+#define  DMAE_REG_GO_C31       \
+       0x00c0c4UL
+#define  DMAE_REG_CMD_MEM \
+       0x00c800UL
+#define  QM_REG_MAXPQSIZETXSEL_0       \
+       0x2f0440UL
+#define  QM_REG_SDMCMDREADY \
+       0x2f1e10UL
+#define  QM_REG_SDMCMDADDR \
+       0x2f1e04UL
+#define  QM_REG_SDMCMDDATALSB \
+       0x2f1e08UL
+#define  QM_REG_SDMCMDDATAMSB \
+       0x2f1e0cUL
+#define  QM_REG_SDMCMDGO       \
+       0x2f1e14UL
+#define  QM_REG_RLPFCRD \
+       0x2f4d80UL
+#define  QM_REG_RLPFINCVAL \
+       0x2f4c80UL
+#define  QM_REG_RLGLBLCRD \
+       0x2f4400UL
+#define  QM_REG_RLGLBLINCVAL \
+       0x2f3400UL
+#define  IGU_REG_ATTENTION_ENABLE \
+       0x18083cUL
+#define  IGU_REG_ATTN_MSG_ADDR_L       \
+       0x180820UL
+#define  IGU_REG_ATTN_MSG_ADDR_H       \
+       0x180824UL
+#define  MISC_REG_AEU_GENERAL_ATTN_0 \
+       0x008400UL
+#define  CAU_REG_SB_ADDR_MEMORY \
+       0x1c8000UL
+#define  CAU_REG_SB_VAR_MEMORY \
+       0x1c6000UL
+#define  CAU_REG_PI_MEMORY \
+       0x1d0000UL
+#define  IGU_REG_PF_CONFIGURATION \
+       0x180800UL
+#define  MISC_REG_AEU_ENABLE1_IGU_OUT_0 \
+       0x00849cUL
+#define  MISC_REG_AEU_MASK_ATTN_IGU \
+       0x008494UL
+#define  IGU_REG_CLEANUP_STATUS_0 \
+       0x180980UL
+#define  IGU_REG_CLEANUP_STATUS_1 \
+       0x180a00UL
+#define  IGU_REG_CLEANUP_STATUS_2 \
+       0x180a80UL
+#define  IGU_REG_CLEANUP_STATUS_3 \
+       0x180b00UL
+#define  IGU_REG_CLEANUP_STATUS_4 \
+       0x180b80UL
+#define  IGU_REG_COMMAND_REG_32LSB_DATA \
+       0x180840UL
+#define  IGU_REG_COMMAND_REG_CTRL \
+       0x180848UL
+#define  IGU_REG_BLOCK_CONFIGURATION_VF_CLEANUP_EN     ( \
+               0x1 << 1)
+#define  IGU_REG_BLOCK_CONFIGURATION_PXP_TPH_INTERFACE_EN      ( \
+               0x1 << 0)
+#define  IGU_REG_MAPPING_MEMORY \
+       0x184000UL
+#define  MISCS_REG_GENERIC_POR_0       \
+       0x0096d4UL
+#define  MCP_REG_NVM_CFG4 \
+       0xe0642cUL
+#define  MCP_REG_NVM_CFG4_FLASH_SIZE   ( \
+               0x7 << 0)
+#define  MCP_REG_NVM_CFG4_FLASH_SIZE_SHIFT \
+       0
+#endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_sp.h b/drivers/net/ethernet/qlogic/qed/qed_sp.h
new file mode 100644 (file)
index 0000000..31a1f1e
--- /dev/null
@@ -0,0 +1,360 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_SP_H
+#define _QED_SP_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/qed/qed_chain.h>
+#include "qed.h"
+#include "qed_hsi.h"
+
+enum spq_mode {
+       QED_SPQ_MODE_BLOCK,     /* Client will poll a designated mem. address */
+       QED_SPQ_MODE_CB,        /* Client supplies a callback */
+       QED_SPQ_MODE_EBLOCK,    /* QED should block until completion */
+};
+
+struct qed_spq_comp_cb {
+       void    (*function)(struct qed_hwfn *,
+                           void *,
+                           union event_ring_data *,
+                           u8 fw_return_code);
+       void    *cookie;
+};
+
+/**
+ * @brief qed_eth_cqe_completion - handles the completion of a
+ *        ramrod on the cqe ring
+ *
+ * @param p_hwfn
+ * @param cqe
+ *
+ * @return int
+ */
+int qed_eth_cqe_completion(struct qed_hwfn *p_hwfn,
+                          struct eth_slow_path_rx_cqe *cqe);
+
+/**
+ *  @file
+ *
+ *  QED Slow-hwfn queue interface
+ */
+
+union ramrod_data {
+       struct pf_start_ramrod_data pf_start;
+       struct rx_queue_start_ramrod_data rx_queue_start;
+       struct rx_queue_update_ramrod_data rx_queue_update;
+       struct rx_queue_stop_ramrod_data rx_queue_stop;
+       struct tx_queue_start_ramrod_data tx_queue_start;
+       struct tx_queue_stop_ramrod_data tx_queue_stop;
+       struct vport_start_ramrod_data vport_start;
+       struct vport_stop_ramrod_data vport_stop;
+       struct vport_update_ramrod_data vport_update;
+       struct vport_filter_update_ramrod_data vport_filter_update;
+};
+
+#define EQ_MAX_CREDIT   0xffffffff
+
+enum spq_priority {
+       QED_SPQ_PRIORITY_NORMAL,
+       QED_SPQ_PRIORITY_HIGH,
+};
+
+union qed_spq_req_comp {
+       struct qed_spq_comp_cb  cb;
+       u64                     *done_addr;
+};
+
+struct qed_spq_comp_done {
+       u64     done;
+       u8      fw_return_code;
+};
+
+struct qed_spq_entry {
+       struct list_head                list;
+
+       u8                              flags;
+
+       /* HSI slow path element */
+       struct slow_path_element        elem;
+
+       union ramrod_data               ramrod;
+
+       enum spq_priority               priority;
+
+       /* pending queue for this entry */
+       struct list_head                *queue;
+
+       enum spq_mode                   comp_mode;
+       struct qed_spq_comp_cb          comp_cb;
+       struct qed_spq_comp_done        comp_done; /* SPQ_MODE_EBLOCK */
+};
+
+struct qed_eq {
+       struct qed_chain        chain;
+       u8                      eq_sb_index;    /* index within the SB */
+       __le16                  *p_fw_cons;     /* ptr to index value */
+};
+
+struct qed_consq {
+       struct qed_chain chain;
+};
+
+struct qed_spq {
+       spinlock_t              lock; /* SPQ lock */
+
+       struct list_head        unlimited_pending;
+       struct list_head        pending;
+       struct list_head        completion_pending;
+       struct list_head        free_pool;
+
+       struct qed_chain        chain;
+
+       /* allocated dma-able memory for spq entries (+ramrod data) */
+       dma_addr_t              p_phys;
+       struct qed_spq_entry    *p_virt;
+
+       /* Used as index for completions (returns on EQ by FW) */
+       u16                     echo_idx;
+
+       /* Statistics */
+       u32                     unlimited_pending_count;
+       u32                     normal_count;
+       u32                     high_count;
+       u32                     comp_sent_count;
+       u32                     comp_count;
+
+       u32                     cid;
+};
+
+/**
+ * @brief qed_spq_post - Posts a Slow hwfn request to FW, or lacking that
+ *        Pends it to the future list.
+ *
+ * @param p_hwfn
+ * @param p_req
+ *
+ * @return int
+ */
+int qed_spq_post(struct qed_hwfn *p_hwfn,
+                struct qed_spq_entry *p_ent,
+                u8 *fw_return_code);
+
+/**
+ * @brief qed_spq_allocate - Alloocates & initializes the SPQ and EQ.
+ *
+ * @param p_hwfn
+ *
+ * @return int
+ */
+int qed_spq_alloc(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_spq_setup - Reset the SPQ to its start state.
+ *
+ * @param p_hwfn
+ */
+void qed_spq_setup(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_spq_deallocate - Deallocates the given SPQ struct.
+ *
+ * @param p_hwfn
+ */
+void qed_spq_free(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_spq_get_entry - Obtain an entrry from the spq
+ *        free pool list.
+ *
+ *
+ *
+ * @param p_hwfn
+ * @param pp_ent
+ *
+ * @return int
+ */
+int
+qed_spq_get_entry(struct qed_hwfn *p_hwfn,
+                 struct qed_spq_entry **pp_ent);
+
+/**
+ * @brief qed_spq_return_entry - Return an entry to spq free
+ *                                 pool list
+ *
+ * @param p_hwfn
+ * @param p_ent
+ */
+void qed_spq_return_entry(struct qed_hwfn *p_hwfn,
+                         struct qed_spq_entry *p_ent);
+/**
+ * @brief qed_eq_allocate - Allocates & initializes an EQ struct
+ *
+ * @param p_hwfn
+ * @param num_elem number of elements in the eq
+ *
+ * @return struct qed_eq* - a newly allocated structure; NULL upon error.
+ */
+struct qed_eq *qed_eq_alloc(struct qed_hwfn *p_hwfn,
+                           u16 num_elem);
+
+/**
+ * @brief qed_eq_setup - Reset the SPQ to its start state.
+ *
+ * @param p_hwfn
+ * @param p_eq
+ */
+void qed_eq_setup(struct qed_hwfn *p_hwfn,
+                 struct qed_eq *p_eq);
+
+/**
+ * @brief qed_eq_deallocate - deallocates the given EQ struct.
+ *
+ * @param p_hwfn
+ * @param p_eq
+ */
+void qed_eq_free(struct qed_hwfn *p_hwfn,
+                struct qed_eq *p_eq);
+
+/**
+ * @brief qed_eq_prod_update - update the FW with default EQ producer
+ *
+ * @param p_hwfn
+ * @param prod
+ */
+void qed_eq_prod_update(struct qed_hwfn *p_hwfn,
+                       u16 prod);
+
+/**
+ * @brief qed_eq_completion - Completes currently pending EQ elements
+ *
+ * @param p_hwfn
+ * @param cookie
+ *
+ * @return int
+ */
+int qed_eq_completion(struct qed_hwfn *p_hwfn,
+                     void *cookie);
+
+/**
+ * @brief qed_spq_completion - Completes a single event
+ *
+ * @param p_hwfn
+ * @param echo - echo value from cookie (used for determining completion)
+ * @param p_data - data from cookie (used in callback function if applicable)
+ *
+ * @return int
+ */
+int qed_spq_completion(struct qed_hwfn *p_hwfn,
+                      __le16 echo,
+                      u8 fw_return_code,
+                      union event_ring_data *p_data);
+
+/**
+ * @brief qed_spq_get_cid - Given p_hwfn, return cid for the hwfn's SPQ
+ *
+ * @param p_hwfn
+ *
+ * @return u32 - SPQ CID
+ */
+u32 qed_spq_get_cid(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_consq_alloc - Allocates & initializes an ConsQ
+ *        struct
+ *
+ * @param p_hwfn
+ *
+ * @return struct qed_eq* - a newly allocated structure; NULL upon error.
+ */
+struct qed_consq *qed_consq_alloc(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_consq_setup - Reset the ConsQ to its start
+ *        state.
+ *
+ * @param p_hwfn
+ * @param p_eq
+ */
+void qed_consq_setup(struct qed_hwfn *p_hwfn,
+                    struct qed_consq *p_consq);
+
+/**
+ * @brief qed_consq_free - deallocates the given ConsQ struct.
+ *
+ * @param p_hwfn
+ * @param p_eq
+ */
+void qed_consq_free(struct qed_hwfn *p_hwfn,
+                   struct qed_consq *p_consq);
+
+/**
+ * @file
+ *
+ * @brief Slow-hwfn low-level commands (Ramrods) function definitions.
+ */
+
+#define QED_SP_EQ_COMPLETION  0x01
+#define QED_SP_CQE_COMPLETION 0x02
+
+struct qed_sp_init_request_params {
+       size_t                  ramrod_data_size;
+       enum spq_mode           comp_mode;
+       struct qed_spq_comp_cb *p_comp_data;
+};
+
+int qed_sp_init_request(struct qed_hwfn *p_hwfn,
+                       struct qed_spq_entry **pp_ent,
+                       u32 cid,
+                       u16 opaque_fid,
+                       u8 cmd,
+                       u8 protocol,
+                       struct qed_sp_init_request_params *p_params);
+
+/**
+ * @brief qed_sp_pf_start - PF Function Start Ramrod
+ *
+ * This ramrod is sent to initialize a physical function (PF). It will
+ * configure the function related parameters and write its completion to the
+ * event ring specified in the parameters.
+ *
+ * Ramrods complete on the common event ring for the PF. This ring is
+ * allocated by the driver on host memory and its parameters are written
+ * to the internal RAM of the UStorm by the Function Start Ramrod.
+ *
+ * @param p_hwfn
+ * @param mode
+ *
+ * @return int
+ */
+
+int qed_sp_pf_start(struct qed_hwfn *p_hwfn,
+                   enum mf_mode mode);
+
+/**
+ * @brief qed_sp_pf_stop - PF Function Stop Ramrod
+ *
+ * This ramrod is sent to close a Physical Function (PF). It is the last ramrod
+ * sent and the last completion written to the PFs Event Ring. This ramrod also
+ * deletes the context for the Slowhwfn connection on this PF.
+ *
+ * @note Not required for first packet.
+ *
+ * @param p_hwfn
+ *
+ * @return int
+ */
+
+int qed_sp_pf_stop(struct qed_hwfn *p_hwfn);
+
+#endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c b/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c
new file mode 100644 (file)
index 0000000..6f78791
--- /dev/null
@@ -0,0 +1,170 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include "qed.h"
+#include <linux/qed/qed_chain.h>
+#include "qed_cxt.h"
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_int.h"
+#include "qed_reg_addr.h"
+#include "qed_sp.h"
+
+int qed_sp_init_request(struct qed_hwfn *p_hwfn,
+                       struct qed_spq_entry **pp_ent,
+                       u32 cid,
+                       u16 opaque_fid,
+                       u8 cmd,
+                       u8 protocol,
+                       struct qed_sp_init_request_params *p_params)
+{
+       int rc = -EINVAL;
+       struct qed_spq_entry *p_ent = NULL;
+       u32 opaque_cid = opaque_fid << 16 | cid;
+
+       if (!pp_ent)
+               return -ENOMEM;
+
+       rc = qed_spq_get_entry(p_hwfn, pp_ent);
+
+       if (rc != 0)
+               return rc;
+
+       p_ent = *pp_ent;
+
+       p_ent->elem.hdr.cid             = cpu_to_le32(opaque_cid);
+       p_ent->elem.hdr.cmd_id          = cmd;
+       p_ent->elem.hdr.protocol_id     = protocol;
+
+       p_ent->priority         = QED_SPQ_PRIORITY_NORMAL;
+       p_ent->comp_mode        = p_params->comp_mode;
+       p_ent->comp_done.done   = 0;
+
+       switch (p_ent->comp_mode) {
+       case QED_SPQ_MODE_EBLOCK:
+               p_ent->comp_cb.cookie = &p_ent->comp_done;
+               break;
+
+       case QED_SPQ_MODE_BLOCK:
+               if (!p_params->p_comp_data)
+                       return -EINVAL;
+
+               p_ent->comp_cb.cookie = p_params->p_comp_data->cookie;
+               break;
+
+       case QED_SPQ_MODE_CB:
+               if (!p_params->p_comp_data)
+                       p_ent->comp_cb.function = NULL;
+               else
+                       p_ent->comp_cb = *p_params->p_comp_data;
+               break;
+
+       default:
+               DP_NOTICE(p_hwfn, "Unknown SPQE completion mode %d\n",
+                         p_ent->comp_mode);
+               return -EINVAL;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
+                  "Initialized: CID %08x cmd %02x protocol %02x data_addr %lu comp_mode [%s]\n",
+                  opaque_cid, cmd, protocol,
+                  (unsigned long)&p_ent->ramrod,
+                  D_TRINE(p_ent->comp_mode, QED_SPQ_MODE_EBLOCK,
+                          QED_SPQ_MODE_BLOCK, "MODE_EBLOCK", "MODE_BLOCK",
+                          "MODE_CB"));
+       if (p_params->ramrod_data_size)
+               memset(&p_ent->ramrod, 0, p_params->ramrod_data_size);
+
+       return 0;
+}
+
+int qed_sp_pf_start(struct qed_hwfn *p_hwfn,
+                   enum mf_mode mode)
+{
+       struct qed_sp_init_request_params params;
+       struct pf_start_ramrod_data *p_ramrod = NULL;
+       u16 sb = qed_int_get_sp_sb_id(p_hwfn);
+       u8 sb_index = p_hwfn->p_eq->eq_sb_index;
+       struct qed_spq_entry *p_ent = NULL;
+       int rc = -EINVAL;
+
+       /* update initial eq producer */
+       qed_eq_prod_update(p_hwfn,
+                          qed_chain_get_prod_idx(&p_hwfn->p_eq->chain));
+
+       memset(&params, 0, sizeof(params));
+       params.ramrod_data_size = sizeof(*p_ramrod);
+       params.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn,
+                                &p_ent,
+                                qed_spq_get_cid(p_hwfn),
+                                p_hwfn->hw_info.opaque_fid,
+                                COMMON_RAMROD_PF_START,
+                                PROTOCOLID_COMMON,
+                                &params);
+       if (rc)
+               return rc;
+
+       p_ramrod = &p_ent->ramrod.pf_start;
+
+       p_ramrod->event_ring_sb_id      = cpu_to_le16(sb);
+       p_ramrod->event_ring_sb_index   = sb_index;
+       p_ramrod->path_id               = QED_PATH_ID(p_hwfn);
+       p_ramrod->dont_log_ramrods      = 0;
+       p_ramrod->log_type_mask         = cpu_to_le16(0xf);
+       p_ramrod->mf_mode = mode;
+       p_ramrod->outer_tag = p_hwfn->hw_info.ovlan;
+
+       /* Place EQ address in RAMROD */
+       p_ramrod->event_ring_pbl_addr.hi =
+                       DMA_HI_LE(p_hwfn->p_eq->chain.pbl.p_phys_table);
+       p_ramrod->event_ring_pbl_addr.lo =
+                       DMA_LO_LE(p_hwfn->p_eq->chain.pbl.p_phys_table);
+       p_ramrod->event_ring_num_pages = (u8)p_hwfn->p_eq->chain.page_cnt;
+
+       p_ramrod->consolid_q_pbl_addr.hi =
+                       DMA_HI_LE(p_hwfn->p_consq->chain.pbl.p_phys_table);
+       p_ramrod->consolid_q_pbl_addr.lo =
+                       DMA_LO_LE(p_hwfn->p_consq->chain.pbl.p_phys_table);
+
+       p_hwfn->hw_info.personality = PERSONALITY_ETH;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
+                  "Setting event_ring_sb [id %04x index %02x], mf [%s] outer_tag [%d]\n",
+                  sb, sb_index,
+                  (p_ramrod->mf_mode == SF) ? "SF" : "Multi-Pf",
+                  p_ramrod->outer_tag);
+
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
+
+int qed_sp_pf_stop(struct qed_hwfn *p_hwfn)
+{
+       struct qed_sp_init_request_params params;
+       struct qed_spq_entry *p_ent = NULL;
+       int rc = -EINVAL;
+
+       memset(&params, 0, sizeof(params));
+       params.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent, qed_spq_get_cid(p_hwfn),
+                                p_hwfn->hw_info.opaque_fid,
+                                COMMON_RAMROD_PF_STOP, PROTOCOLID_COMMON,
+                                &params);
+       if (rc)
+               return rc;
+
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_spq.c b/drivers/net/ethernet/qlogic/qed/qed_spq.c
new file mode 100644 (file)
index 0000000..7c0b845
--- /dev/null
@@ -0,0 +1,860 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include "qed.h"
+#include "qed_cxt.h"
+#include "qed_dev_api.h"
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_int.h"
+#include "qed_mcp.h"
+#include "qed_reg_addr.h"
+#include "qed_sp.h"
+
+/***************************************************************************
+* Structures & Definitions
+***************************************************************************/
+
+#define SPQ_HIGH_PRI_RESERVE_DEFAULT    (1)
+#define SPQ_BLOCK_SLEEP_LENGTH          (1000)
+
+/***************************************************************************
+* Blocking Imp. (BLOCK/EBLOCK mode)
+***************************************************************************/
+static void qed_spq_blocking_cb(struct qed_hwfn *p_hwfn,
+                               void *cookie,
+                               union event_ring_data *data,
+                               u8 fw_return_code)
+{
+       struct qed_spq_comp_done *comp_done;
+
+       comp_done = (struct qed_spq_comp_done *)cookie;
+
+       comp_done->done                 = 0x1;
+       comp_done->fw_return_code       = fw_return_code;
+
+       /* make update visible to waiting thread */
+       smp_wmb();
+}
+
+static int qed_spq_block(struct qed_hwfn *p_hwfn,
+                        struct qed_spq_entry *p_ent,
+                        u8 *p_fw_ret)
+{
+       int sleep_count = SPQ_BLOCK_SLEEP_LENGTH;
+       struct qed_spq_comp_done *comp_done;
+       int rc;
+
+       comp_done = (struct qed_spq_comp_done *)p_ent->comp_cb.cookie;
+       while (sleep_count) {
+               /* validate we receive completion update */
+               smp_rmb();
+               if (comp_done->done == 1) {
+                       if (p_fw_ret)
+                               *p_fw_ret = comp_done->fw_return_code;
+                       return 0;
+               }
+               usleep_range(5000, 10000);
+               sleep_count--;
+       }
+
+       DP_INFO(p_hwfn, "Ramrod is stuck, requesting MCP drain\n");
+       rc = qed_mcp_drain(p_hwfn, p_hwfn->p_main_ptt);
+       if (rc != 0)
+               DP_NOTICE(p_hwfn, "MCP drain failed\n");
+
+       /* Retry after drain */
+       sleep_count = SPQ_BLOCK_SLEEP_LENGTH;
+       while (sleep_count) {
+               /* validate we receive completion update */
+               smp_rmb();
+               if (comp_done->done == 1) {
+                       if (p_fw_ret)
+                               *p_fw_ret = comp_done->fw_return_code;
+                       return 0;
+               }
+               usleep_range(5000, 10000);
+               sleep_count--;
+       }
+
+       if (comp_done->done == 1) {
+               if (p_fw_ret)
+                       *p_fw_ret = comp_done->fw_return_code;
+               return 0;
+       }
+
+       DP_NOTICE(p_hwfn, "Ramrod is stuck, MCP drain failed\n");
+
+       return -EBUSY;
+}
+
+/***************************************************************************
+* SPQ entries inner API
+***************************************************************************/
+static int
+qed_spq_fill_entry(struct qed_hwfn *p_hwfn,
+                  struct qed_spq_entry *p_ent)
+{
+       p_ent->elem.hdr.echo = 0;
+       p_hwfn->p_spq->echo_idx++;
+       p_ent->flags = 0;
+
+       switch (p_ent->comp_mode) {
+       case QED_SPQ_MODE_EBLOCK:
+       case QED_SPQ_MODE_BLOCK:
+               p_ent->comp_cb.function = qed_spq_blocking_cb;
+               break;
+       case QED_SPQ_MODE_CB:
+               break;
+       default:
+               DP_NOTICE(p_hwfn, "Unknown SPQE completion mode %d\n",
+                         p_ent->comp_mode);
+               return -EINVAL;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
+                  "Ramrod header: [CID 0x%08x CMD 0x%02x protocol 0x%02x] Data pointer: [%08x:%08x] Completion Mode: %s\n",
+                  p_ent->elem.hdr.cid,
+                  p_ent->elem.hdr.cmd_id,
+                  p_ent->elem.hdr.protocol_id,
+                  p_ent->elem.data_ptr.hi,
+                  p_ent->elem.data_ptr.lo,
+                  D_TRINE(p_ent->comp_mode, QED_SPQ_MODE_EBLOCK,
+                          QED_SPQ_MODE_BLOCK, "MODE_EBLOCK", "MODE_BLOCK",
+                          "MODE_CB"));
+
+       return 0;
+}
+
+/***************************************************************************
+* HSI access
+***************************************************************************/
+static void qed_spq_hw_initialize(struct qed_hwfn *p_hwfn,
+                                 struct qed_spq *p_spq)
+{
+       u16                             pq;
+       struct qed_cxt_info             cxt_info;
+       struct core_conn_context        *p_cxt;
+       union qed_qm_pq_params          pq_params;
+       int                             rc;
+
+       cxt_info.iid = p_spq->cid;
+
+       rc = qed_cxt_get_cid_info(p_hwfn, &cxt_info);
+
+       if (rc < 0) {
+               DP_NOTICE(p_hwfn, "Cannot find context info for cid=%d\n",
+                         p_spq->cid);
+               return;
+       }
+
+       p_cxt = cxt_info.p_cxt;
+
+       SET_FIELD(p_cxt->xstorm_ag_context.flags10,
+                 XSTORM_CORE_CONN_AG_CTX_DQ_CF_EN, 1);
+       SET_FIELD(p_cxt->xstorm_ag_context.flags1,
+                 XSTORM_CORE_CONN_AG_CTX_DQ_CF_ACTIVE, 1);
+       SET_FIELD(p_cxt->xstorm_ag_context.flags9,
+                 XSTORM_CORE_CONN_AG_CTX_CONSOLID_PROD_CF_EN, 1);
+
+       /* QM physical queue */
+       memset(&pq_params, 0, sizeof(pq_params));
+       pq_params.core.tc = LB_TC;
+       pq = qed_get_qm_pq(p_hwfn, PROTOCOLID_CORE, &pq_params);
+       p_cxt->xstorm_ag_context.physical_q0 = cpu_to_le16(pq);
+
+       p_cxt->xstorm_st_context.spq_base_lo =
+               DMA_LO_LE(p_spq->chain.p_phys_addr);
+       p_cxt->xstorm_st_context.spq_base_hi =
+               DMA_HI_LE(p_spq->chain.p_phys_addr);
+
+       p_cxt->xstorm_st_context.consolid_base_addr.lo =
+               DMA_LO_LE(p_hwfn->p_consq->chain.p_phys_addr);
+       p_cxt->xstorm_st_context.consolid_base_addr.hi =
+               DMA_HI_LE(p_hwfn->p_consq->chain.p_phys_addr);
+}
+
+static int qed_spq_hw_post(struct qed_hwfn *p_hwfn,
+                          struct qed_spq *p_spq,
+                          struct qed_spq_entry *p_ent)
+{
+       struct qed_chain                *p_chain = &p_hwfn->p_spq->chain;
+       struct slow_path_element        *elem;
+       struct core_db_data             db;
+
+       elem = qed_chain_produce(p_chain);
+       if (!elem) {
+               DP_NOTICE(p_hwfn, "Failed to produce from SPQ chain\n");
+               return -EINVAL;
+       }
+
+       *elem = p_ent->elem; /* struct assignment */
+
+       /* send a doorbell on the slow hwfn session */
+       memset(&db, 0, sizeof(db));
+       SET_FIELD(db.params, CORE_DB_DATA_DEST, DB_DEST_XCM);
+       SET_FIELD(db.params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_SET);
+       SET_FIELD(db.params, CORE_DB_DATA_AGG_VAL_SEL,
+                 DQ_XCM_CORE_SPQ_PROD_CMD);
+       db.agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
+
+       /* validate producer is up to-date */
+       rmb();
+
+       db.spq_prod = cpu_to_le16(qed_chain_get_prod_idx(p_chain));
+
+       /* do not reorder */
+       barrier();
+
+       DOORBELL(p_hwfn, qed_db_addr(p_spq->cid, DQ_DEMS_LEGACY), *(u32 *)&db);
+
+       /* make sure doorbell is rang */
+       mmiowb();
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
+                  "Doorbelled [0x%08x, CID 0x%08x] with Flags: %02x agg_params: %02x, prod: %04x\n",
+                  qed_db_addr(p_spq->cid, DQ_DEMS_LEGACY),
+                  p_spq->cid, db.params, db.agg_flags,
+                  qed_chain_get_prod_idx(p_chain));
+
+       return 0;
+}
+
+/***************************************************************************
+* Asynchronous events
+***************************************************************************/
+static int
+qed_async_event_completion(struct qed_hwfn *p_hwfn,
+                          struct event_ring_entry *p_eqe)
+{
+       DP_NOTICE(p_hwfn,
+                 "Unknown Async completion for protocol: %d\n",
+                  p_eqe->protocol_id);
+       return -EINVAL;
+}
+
+/***************************************************************************
+* EQ API
+***************************************************************************/
+void qed_eq_prod_update(struct qed_hwfn *p_hwfn,
+                       u16 prod)
+{
+       u32 addr = GTT_BAR0_MAP_REG_USDM_RAM +
+                  USTORM_EQE_CONS_OFFSET(p_hwfn->rel_pf_id);
+
+       REG_WR16(p_hwfn, addr, prod);
+
+       /* keep prod updates ordered */
+       mmiowb();
+}
+
+int qed_eq_completion(struct qed_hwfn *p_hwfn,
+                     void *cookie)
+
+{
+       struct qed_eq *p_eq = cookie;
+       struct qed_chain *p_chain = &p_eq->chain;
+       int rc = 0;
+
+       /* take a snapshot of the FW consumer */
+       u16 fw_cons_idx = le16_to_cpu(*p_eq->p_fw_cons);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SPQ, "fw_cons_idx %x\n", fw_cons_idx);
+
+       /* Need to guarantee the fw_cons index we use points to a usuable
+        * element (to comply with our chain), so our macros would comply
+        */
+       if ((fw_cons_idx & qed_chain_get_usable_per_page(p_chain)) ==
+           qed_chain_get_usable_per_page(p_chain))
+               fw_cons_idx += qed_chain_get_unusable_per_page(p_chain);
+
+       /* Complete current segment of eq entries */
+       while (fw_cons_idx != qed_chain_get_cons_idx(p_chain)) {
+               struct event_ring_entry *p_eqe = qed_chain_consume(p_chain);
+
+               if (!p_eqe) {
+                       rc = -EINVAL;
+                       break;
+               }
+
+               DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
+                          "op %x prot %x res0 %x echo %x fwret %x flags %x\n",
+                          p_eqe->opcode,
+                          p_eqe->protocol_id,
+                          p_eqe->reserved0,
+                          le16_to_cpu(p_eqe->echo),
+                          p_eqe->fw_return_code,
+                          p_eqe->flags);
+
+               if (GET_FIELD(p_eqe->flags, EVENT_RING_ENTRY_ASYNC)) {
+                       if (qed_async_event_completion(p_hwfn, p_eqe))
+                               rc = -EINVAL;
+               } else if (qed_spq_completion(p_hwfn,
+                                             p_eqe->echo,
+                                             p_eqe->fw_return_code,
+                                             &p_eqe->data)) {
+                       rc = -EINVAL;
+               }
+
+               qed_chain_recycle_consumed(p_chain);
+       }
+
+       qed_eq_prod_update(p_hwfn, qed_chain_get_prod_idx(p_chain));
+
+       return rc;
+}
+
+struct qed_eq *qed_eq_alloc(struct qed_hwfn *p_hwfn,
+                           u16 num_elem)
+{
+       struct qed_eq *p_eq;
+
+       /* Allocate EQ struct */
+       p_eq = kzalloc(sizeof(*p_eq), GFP_ATOMIC);
+       if (!p_eq) {
+               DP_NOTICE(p_hwfn, "Failed to allocate `struct qed_eq'\n");
+               return NULL;
+       }
+
+       /* Allocate and initialize EQ chain*/
+       if (qed_chain_alloc(p_hwfn->cdev,
+                           QED_CHAIN_USE_TO_PRODUCE,
+                           QED_CHAIN_MODE_PBL,
+                           num_elem,
+                           sizeof(union event_ring_element),
+                           &p_eq->chain)) {
+               DP_NOTICE(p_hwfn, "Failed to allocate eq chain\n");
+               goto eq_allocate_fail;
+       }
+
+       /* register EQ completion on the SP SB */
+       qed_int_register_cb(p_hwfn,
+                           qed_eq_completion,
+                           p_eq,
+                           &p_eq->eq_sb_index,
+                           &p_eq->p_fw_cons);
+
+       return p_eq;
+
+eq_allocate_fail:
+       qed_eq_free(p_hwfn, p_eq);
+       return NULL;
+}
+
+void qed_eq_setup(struct qed_hwfn *p_hwfn,
+                 struct qed_eq *p_eq)
+{
+       qed_chain_reset(&p_eq->chain);
+}
+
+void qed_eq_free(struct qed_hwfn *p_hwfn,
+                struct qed_eq *p_eq)
+{
+       if (!p_eq)
+               return;
+       qed_chain_free(p_hwfn->cdev, &p_eq->chain);
+       kfree(p_eq);
+}
+
+/***************************************************************************
+* CQE API - manipulate EQ functionality
+***************************************************************************/
+static int qed_cqe_completion(
+       struct qed_hwfn *p_hwfn,
+       struct eth_slow_path_rx_cqe *cqe,
+       enum protocol_type protocol)
+{
+       /* @@@tmp - it's possible we'll eventually want to handle some
+        * actual commands that can arrive here, but for now this is only
+        * used to complete the ramrod using the echo value on the cqe
+        */
+       return qed_spq_completion(p_hwfn, cqe->echo, 0, NULL);
+}
+
+int qed_eth_cqe_completion(struct qed_hwfn *p_hwfn,
+                          struct eth_slow_path_rx_cqe *cqe)
+{
+       int rc;
+
+       rc = qed_cqe_completion(p_hwfn, cqe, PROTOCOLID_ETH);
+       if (rc)
+               DP_NOTICE(p_hwfn,
+                         "Failed to handle RXQ CQE [cmd 0x%02x]\n",
+                         cqe->ramrod_cmd_id);
+
+       return rc;
+}
+
+/***************************************************************************
+* Slow hwfn Queue (spq)
+***************************************************************************/
+void qed_spq_setup(struct qed_hwfn *p_hwfn)
+{
+       struct qed_spq          *p_spq  = p_hwfn->p_spq;
+       struct qed_spq_entry    *p_virt = NULL;
+       dma_addr_t              p_phys  = 0;
+       unsigned int            i       = 0;
+
+       INIT_LIST_HEAD(&p_spq->pending);
+       INIT_LIST_HEAD(&p_spq->completion_pending);
+       INIT_LIST_HEAD(&p_spq->free_pool);
+       INIT_LIST_HEAD(&p_spq->unlimited_pending);
+       spin_lock_init(&p_spq->lock);
+
+       /* SPQ empty pool */
+       p_phys  = p_spq->p_phys + offsetof(struct qed_spq_entry, ramrod);
+       p_virt  = p_spq->p_virt;
+
+       for (i = 0; i < p_spq->chain.capacity; i++) {
+               p_virt->elem.data_ptr.hi = DMA_HI_LE(p_phys);
+               p_virt->elem.data_ptr.lo = DMA_LO_LE(p_phys);
+
+               list_add_tail(&p_virt->list, &p_spq->free_pool);
+
+               p_virt++;
+               p_phys += sizeof(struct qed_spq_entry);
+       }
+
+       /* Statistics */
+       p_spq->normal_count             = 0;
+       p_spq->comp_count               = 0;
+       p_spq->comp_sent_count          = 0;
+       p_spq->unlimited_pending_count  = 0;
+       p_spq->echo_idx                 = 0;
+
+       /* SPQ cid, cannot fail */
+       qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_CORE, &p_spq->cid);
+       qed_spq_hw_initialize(p_hwfn, p_spq);
+
+       /* reset the chain itself */
+       qed_chain_reset(&p_spq->chain);
+}
+
+int qed_spq_alloc(struct qed_hwfn *p_hwfn)
+{
+       struct qed_spq          *p_spq  = NULL;
+       dma_addr_t              p_phys  = 0;
+       struct qed_spq_entry    *p_virt = NULL;
+
+       /* SPQ struct */
+       p_spq =
+               kzalloc(sizeof(struct qed_spq), GFP_ATOMIC);
+       if (!p_spq) {
+               DP_NOTICE(p_hwfn, "Failed to allocate `struct qed_spq'\n");
+               return -ENOMEM;
+       }
+
+       /* SPQ ring  */
+       if (qed_chain_alloc(p_hwfn->cdev,
+                           QED_CHAIN_USE_TO_PRODUCE,
+                           QED_CHAIN_MODE_SINGLE,
+                           0,   /* N/A when the mode is SINGLE */
+                           sizeof(struct slow_path_element),
+                           &p_spq->chain)) {
+               DP_NOTICE(p_hwfn, "Failed to allocate spq chain\n");
+               goto spq_allocate_fail;
+       }
+
+       /* allocate and fill the SPQ elements (incl. ramrod data list) */
+       p_virt = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                   p_spq->chain.capacity *
+                                   sizeof(struct qed_spq_entry),
+                                   &p_phys,
+                                   GFP_KERNEL);
+
+       if (!p_virt)
+               goto spq_allocate_fail;
+
+       p_spq->p_virt = p_virt;
+       p_spq->p_phys = p_phys;
+       p_hwfn->p_spq = p_spq;
+
+       return 0;
+
+spq_allocate_fail:
+       qed_chain_free(p_hwfn->cdev, &p_spq->chain);
+       kfree(p_spq);
+       return -ENOMEM;
+}
+
+void qed_spq_free(struct qed_hwfn *p_hwfn)
+{
+       struct qed_spq *p_spq = p_hwfn->p_spq;
+
+       if (!p_spq)
+               return;
+
+       if (p_spq->p_virt)
+               dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                                 p_spq->chain.capacity *
+                                 sizeof(struct qed_spq_entry),
+                                 p_spq->p_virt,
+                                 p_spq->p_phys);
+
+       qed_chain_free(p_hwfn->cdev, &p_spq->chain);
+       ;
+       kfree(p_spq);
+}
+
+int
+qed_spq_get_entry(struct qed_hwfn *p_hwfn,
+                 struct qed_spq_entry **pp_ent)
+{
+       struct qed_spq *p_spq = p_hwfn->p_spq;
+       struct qed_spq_entry *p_ent = NULL;
+       int rc = 0;
+
+       spin_lock_bh(&p_spq->lock);
+
+       if (list_empty(&p_spq->free_pool)) {
+               p_ent = kzalloc(sizeof(*p_ent), GFP_ATOMIC);
+               if (!p_ent) {
+                       rc = -ENOMEM;
+                       goto out_unlock;
+               }
+               p_ent->queue = &p_spq->unlimited_pending;
+       } else {
+               p_ent = list_first_entry(&p_spq->free_pool,
+                                        struct qed_spq_entry,
+                                        list);
+               list_del(&p_ent->list);
+               p_ent->queue = &p_spq->pending;
+       }
+
+       *pp_ent = p_ent;
+
+out_unlock:
+       spin_unlock_bh(&p_spq->lock);
+       return rc;
+}
+
+/* Locked variant; Should be called while the SPQ lock is taken */
+static void __qed_spq_return_entry(struct qed_hwfn *p_hwfn,
+                                  struct qed_spq_entry *p_ent)
+{
+       list_add_tail(&p_ent->list, &p_hwfn->p_spq->free_pool);
+}
+
+void qed_spq_return_entry(struct qed_hwfn *p_hwfn,
+                         struct qed_spq_entry *p_ent)
+{
+       spin_lock_bh(&p_hwfn->p_spq->lock);
+       __qed_spq_return_entry(p_hwfn, p_ent);
+       spin_unlock_bh(&p_hwfn->p_spq->lock);
+}
+
+/**
+ * @brief qed_spq_add_entry - adds a new entry to the pending
+ *        list. Should be used while lock is being held.
+ *
+ * Addes an entry to the pending list is there is room (en empty
+ * element is available in the free_pool), or else places the
+ * entry in the unlimited_pending pool.
+ *
+ * @param p_hwfn
+ * @param p_ent
+ * @param priority
+ *
+ * @return int
+ */
+static int
+qed_spq_add_entry(struct qed_hwfn *p_hwfn,
+                 struct qed_spq_entry *p_ent,
+                 enum spq_priority priority)
+{
+       struct qed_spq *p_spq = p_hwfn->p_spq;
+
+       if (p_ent->queue == &p_spq->unlimited_pending) {
+               struct qed_spq_entry *p_en2;
+
+               if (list_empty(&p_spq->free_pool)) {
+                       list_add_tail(&p_ent->list, &p_spq->unlimited_pending);
+                       p_spq->unlimited_pending_count++;
+
+                       return 0;
+               }
+
+               p_en2 = list_first_entry(&p_spq->free_pool,
+                                        struct qed_spq_entry,
+                                        list);
+               list_del(&p_en2->list);
+
+               /* Strcut assignment */
+               *p_en2 = *p_ent;
+
+               kfree(p_ent);
+
+               p_ent = p_en2;
+       }
+
+       /* entry is to be placed in 'pending' queue */
+       switch (priority) {
+       case QED_SPQ_PRIORITY_NORMAL:
+               list_add_tail(&p_ent->list, &p_spq->pending);
+               p_spq->normal_count++;
+               break;
+       case QED_SPQ_PRIORITY_HIGH:
+               list_add(&p_ent->list, &p_spq->pending);
+               p_spq->high_count++;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/***************************************************************************
+* Accessor
+***************************************************************************/
+u32 qed_spq_get_cid(struct qed_hwfn *p_hwfn)
+{
+       if (!p_hwfn->p_spq)
+               return 0xffffffff;      /* illegal */
+       return p_hwfn->p_spq->cid;
+}
+
+/***************************************************************************
+* Posting new Ramrods
+***************************************************************************/
+static int qed_spq_post_list(struct qed_hwfn *p_hwfn,
+                            struct list_head *head,
+                            u32 keep_reserve)
+{
+       struct qed_spq *p_spq = p_hwfn->p_spq;
+       int rc;
+
+       while (qed_chain_get_elem_left(&p_spq->chain) > keep_reserve &&
+              !list_empty(head)) {
+               struct qed_spq_entry *p_ent =
+                       list_first_entry(head, struct qed_spq_entry, list);
+               list_del(&p_ent->list);
+               list_add_tail(&p_ent->list, &p_spq->completion_pending);
+               p_spq->comp_sent_count++;
+
+               rc = qed_spq_hw_post(p_hwfn, p_spq, p_ent);
+               if (rc) {
+                       list_del(&p_ent->list);
+                       __qed_spq_return_entry(p_hwfn, p_ent);
+                       return rc;
+               }
+       }
+
+       return 0;
+}
+
+static int qed_spq_pend_post(struct qed_hwfn *p_hwfn)
+{
+       struct qed_spq *p_spq = p_hwfn->p_spq;
+       struct qed_spq_entry *p_ent = NULL;
+
+       while (!list_empty(&p_spq->free_pool)) {
+               if (list_empty(&p_spq->unlimited_pending))
+                       break;
+
+               p_ent = list_first_entry(&p_spq->unlimited_pending,
+                                        struct qed_spq_entry,
+                                        list);
+               if (!p_ent)
+                       return -EINVAL;
+
+               list_del(&p_ent->list);
+
+               qed_spq_add_entry(p_hwfn, p_ent, p_ent->priority);
+       }
+
+       return qed_spq_post_list(p_hwfn, &p_spq->pending,
+                                SPQ_HIGH_PRI_RESERVE_DEFAULT);
+}
+
+int qed_spq_post(struct qed_hwfn *p_hwfn,
+                struct qed_spq_entry *p_ent,
+                u8 *fw_return_code)
+{
+       int rc = 0;
+       struct qed_spq *p_spq = p_hwfn ? p_hwfn->p_spq : NULL;
+       bool b_ret_ent = true;
+
+       if (!p_hwfn)
+               return -EINVAL;
+
+       if (!p_ent) {
+               DP_NOTICE(p_hwfn, "Got a NULL pointer\n");
+               return -EINVAL;
+       }
+
+       /* Complete the entry */
+       rc = qed_spq_fill_entry(p_hwfn, p_ent);
+
+       spin_lock_bh(&p_spq->lock);
+
+       /* Check return value after LOCK is taken for cleaner error flow */
+       if (rc)
+               goto spq_post_fail;
+
+       /* Add the request to the pending queue */
+       rc = qed_spq_add_entry(p_hwfn, p_ent, p_ent->priority);
+       if (rc)
+               goto spq_post_fail;
+
+       rc = qed_spq_pend_post(p_hwfn);
+       if (rc) {
+               /* Since it's possible that pending failed for a different
+                * entry [although unlikely], the failed entry was already
+                * dealt with; No need to return it here.
+                */
+               b_ret_ent = false;
+               goto spq_post_fail;
+       }
+
+       spin_unlock_bh(&p_spq->lock);
+
+       if (p_ent->comp_mode == QED_SPQ_MODE_EBLOCK) {
+               /* For entries in QED BLOCK mode, the completion code cannot
+                * perform the necessary cleanup - if it did, we couldn't
+                * access p_ent here to see whether it's successful or not.
+                * Thus, after gaining the answer perform the cleanup here.
+                */
+               rc = qed_spq_block(p_hwfn, p_ent, fw_return_code);
+               if (rc)
+                       goto spq_post_fail2;
+
+               /* return to pool */
+               qed_spq_return_entry(p_hwfn, p_ent);
+       }
+       return rc;
+
+spq_post_fail2:
+       spin_lock_bh(&p_spq->lock);
+       list_del(&p_ent->list);
+       qed_chain_return_produced(&p_spq->chain);
+
+spq_post_fail:
+       /* return to the free pool */
+       if (b_ret_ent)
+               __qed_spq_return_entry(p_hwfn, p_ent);
+       spin_unlock_bh(&p_spq->lock);
+
+       return rc;
+}
+
+int qed_spq_completion(struct qed_hwfn *p_hwfn,
+                      __le16 echo,
+                      u8 fw_return_code,
+                      union event_ring_data *p_data)
+{
+       struct qed_spq          *p_spq;
+       struct qed_spq_entry    *p_ent = NULL;
+       struct qed_spq_entry    *tmp;
+       struct qed_spq_entry    *found = NULL;
+       int                     rc;
+
+       if (!p_hwfn)
+               return -EINVAL;
+
+       p_spq = p_hwfn->p_spq;
+       if (!p_spq)
+               return -EINVAL;
+
+       spin_lock_bh(&p_spq->lock);
+       list_for_each_entry_safe(p_ent, tmp, &p_spq->completion_pending,
+                                list) {
+               if (p_ent->elem.hdr.echo == echo) {
+                       list_del(&p_ent->list);
+
+                       qed_chain_return_produced(&p_spq->chain);
+                       p_spq->comp_count++;
+                       found = p_ent;
+                       break;
+               }
+       }
+
+       /* Release lock before callback, as callback may post
+        * an additional ramrod.
+        */
+       spin_unlock_bh(&p_spq->lock);
+
+       if (!found) {
+               DP_NOTICE(p_hwfn,
+                         "Failed to find an entry this EQE completes\n");
+               return -EEXIST;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SPQ, "Complete: func %p cookie %p)\n",
+                  p_ent->comp_cb.function, p_ent->comp_cb.cookie);
+       if (found->comp_cb.function)
+               found->comp_cb.function(p_hwfn, found->comp_cb.cookie, p_data,
+                                       fw_return_code);
+
+       if (found->comp_mode != QED_SPQ_MODE_EBLOCK)
+               /* EBLOCK is responsible for freeing its own entry */
+               qed_spq_return_entry(p_hwfn, found);
+
+       /* Attempt to post pending requests */
+       spin_lock_bh(&p_spq->lock);
+       rc = qed_spq_pend_post(p_hwfn);
+       spin_unlock_bh(&p_spq->lock);
+
+       return rc;
+}
+
+struct qed_consq *qed_consq_alloc(struct qed_hwfn *p_hwfn)
+{
+       struct qed_consq *p_consq;
+
+       /* Allocate ConsQ struct */
+       p_consq = kzalloc(sizeof(*p_consq), GFP_ATOMIC);
+       if (!p_consq) {
+               DP_NOTICE(p_hwfn, "Failed to allocate `struct qed_consq'\n");
+               return NULL;
+       }
+
+       /* Allocate and initialize EQ chain*/
+       if (qed_chain_alloc(p_hwfn->cdev,
+                           QED_CHAIN_USE_TO_PRODUCE,
+                           QED_CHAIN_MODE_PBL,
+                           QED_CHAIN_PAGE_SIZE / 0x80,
+                           0x80,
+                           &p_consq->chain)) {
+               DP_NOTICE(p_hwfn, "Failed to allocate consq chain");
+               goto consq_allocate_fail;
+       }
+
+       return p_consq;
+
+consq_allocate_fail:
+       qed_consq_free(p_hwfn, p_consq);
+       return NULL;
+}
+
+void qed_consq_setup(struct qed_hwfn *p_hwfn,
+                    struct qed_consq *p_consq)
+{
+       qed_chain_reset(&p_consq->chain);
+}
+
+void qed_consq_free(struct qed_hwfn *p_hwfn,
+                   struct qed_consq *p_consq)
+{
+       if (!p_consq)
+               return;
+       qed_chain_free(p_hwfn->cdev, &p_consq->chain);
+       kfree(p_consq);
+}
diff --git a/drivers/net/ethernet/qlogic/qede/Makefile b/drivers/net/ethernet/qlogic/qede/Makefile
new file mode 100644 (file)
index 0000000..06ff90d
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_QEDE) := qede.o
+
+qede-y := qede_main.o qede_ethtool.o
diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h
new file mode 100644 (file)
index 0000000..ea00d5f
--- /dev/null
@@ -0,0 +1,285 @@
+/* QLogic qede NIC Driver
+* Copyright (c) 2015 QLogic Corporation
+*
+* This software is available under the terms of the GNU General Public License
+* (GPL) Version 2, available from the file COPYING in the main directory of
+* this source tree.
+*/
+
+#ifndef _QEDE_H_
+#define _QEDE_H_
+#include <linux/compiler.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/bitmap.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/qed/common_hsi.h>
+#include <linux/qed/eth_common.h>
+#include <linux/qed/qed_if.h>
+#include <linux/qed/qed_chain.h>
+#include <linux/qed/qed_eth_if.h>
+
+#define QEDE_MAJOR_VERSION             8
+#define QEDE_MINOR_VERSION             4
+#define QEDE_REVISION_VERSION          0
+#define QEDE_ENGINEERING_VERSION       0
+#define DRV_MODULE_VERSION __stringify(QEDE_MAJOR_VERSION) "." \
+               __stringify(QEDE_MINOR_VERSION) "."             \
+               __stringify(QEDE_REVISION_VERSION) "."          \
+               __stringify(QEDE_ENGINEERING_VERSION)
+
+#define QEDE_ETH_INTERFACE_VERSION     300
+
+#define DRV_MODULE_SYM         qede
+
+struct qede_stats {
+       u64 no_buff_discards;
+       u64 rx_ucast_bytes;
+       u64 rx_mcast_bytes;
+       u64 rx_bcast_bytes;
+       u64 rx_ucast_pkts;
+       u64 rx_mcast_pkts;
+       u64 rx_bcast_pkts;
+       u64 mftag_filter_discards;
+       u64 mac_filter_discards;
+       u64 tx_ucast_bytes;
+       u64 tx_mcast_bytes;
+       u64 tx_bcast_bytes;
+       u64 tx_ucast_pkts;
+       u64 tx_mcast_pkts;
+       u64 tx_bcast_pkts;
+       u64 tx_err_drop_pkts;
+       u64 coalesced_pkts;
+       u64 coalesced_events;
+       u64 coalesced_aborts_num;
+       u64 non_coalesced_pkts;
+       u64 coalesced_bytes;
+
+       /* port */
+       u64 rx_64_byte_packets;
+       u64 rx_127_byte_packets;
+       u64 rx_255_byte_packets;
+       u64 rx_511_byte_packets;
+       u64 rx_1023_byte_packets;
+       u64 rx_1518_byte_packets;
+       u64 rx_1522_byte_packets;
+       u64 rx_2047_byte_packets;
+       u64 rx_4095_byte_packets;
+       u64 rx_9216_byte_packets;
+       u64 rx_16383_byte_packets;
+       u64 rx_crc_errors;
+       u64 rx_mac_crtl_frames;
+       u64 rx_pause_frames;
+       u64 rx_pfc_frames;
+       u64 rx_align_errors;
+       u64 rx_carrier_errors;
+       u64 rx_oversize_packets;
+       u64 rx_jabbers;
+       u64 rx_undersize_packets;
+       u64 rx_fragments;
+       u64 tx_64_byte_packets;
+       u64 tx_65_to_127_byte_packets;
+       u64 tx_128_to_255_byte_packets;
+       u64 tx_256_to_511_byte_packets;
+       u64 tx_512_to_1023_byte_packets;
+       u64 tx_1024_to_1518_byte_packets;
+       u64 tx_1519_to_2047_byte_packets;
+       u64 tx_2048_to_4095_byte_packets;
+       u64 tx_4096_to_9216_byte_packets;
+       u64 tx_9217_to_16383_byte_packets;
+       u64 tx_pause_frames;
+       u64 tx_pfc_frames;
+       u64 tx_lpi_entry_count;
+       u64 tx_total_collisions;
+       u64 brb_truncates;
+       u64 brb_discards;
+       u64 tx_mac_ctrl_frames;
+};
+
+struct qede_dev {
+       struct qed_dev                  *cdev;
+       struct net_device               *ndev;
+       struct pci_dev                  *pdev;
+
+       u32                             dp_module;
+       u8                              dp_level;
+
+       const struct qed_eth_ops        *ops;
+
+       struct qed_dev_eth_info dev_info;
+#define QEDE_MAX_RSS_CNT(edev) ((edev)->dev_info.num_queues)
+#define QEDE_MAX_TSS_CNT(edev) ((edev)->dev_info.num_queues * \
+                                (edev)->dev_info.num_tc)
+
+       struct qede_fastpath            *fp_array;
+       u16                             num_rss;
+       u8                              num_tc;
+#define QEDE_RSS_CNT(edev)             ((edev)->num_rss)
+#define QEDE_TSS_CNT(edev)             ((edev)->num_rss *      \
+                                        (edev)->num_tc)
+#define QEDE_TSS_IDX(edev, txqidx)     ((txqidx) % (edev)->num_rss)
+#define QEDE_TC_IDX(edev, txqidx)      ((txqidx) / (edev)->num_rss)
+#define QEDE_TX_QUEUE(edev, txqidx)    \
+       (&(edev)->fp_array[QEDE_TSS_IDX((edev), (txqidx))].txqs[QEDE_TC_IDX( \
+                                                       (edev), (txqidx))])
+
+       struct qed_int_info             int_info;
+       unsigned char                   primary_mac[ETH_ALEN];
+
+       /* Smaller private varaiant of the RTNL lock */
+       struct mutex                    qede_lock;
+       u32                             state; /* Protected by qede_lock */
+       u16                             rx_buf_size;
+       /* L2 header size + 2*VLANs (8 bytes) + LLC SNAP (8 bytes) */
+#define ETH_OVERHEAD                   (ETH_HLEN + 8 + 8)
+       /* Max supported alignment is 256 (8 shift)
+        * minimal alignment shift 6 is optimal for 57xxx HW performance
+        */
+#define QEDE_RX_ALIGN_SHIFT            max(6, min(8, L1_CACHE_SHIFT))
+       /* We assume skb_build() uses sizeof(struct skb_shared_info) bytes
+        * at the end of skb->data, to avoid wasting a full cache line.
+        * This reduces memory use (skb->truesize).
+        */
+#define QEDE_FW_RX_ALIGN_END                                   \
+       max_t(u64, 1UL << QEDE_RX_ALIGN_SHIFT,                  \
+             SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+
+       struct qede_stats               stats;
+       struct qed_update_vport_rss_params      rss_params;
+       u16                     q_num_rx_buffers; /* Must be a power of two */
+       u16                     q_num_tx_buffers; /* Must be a power of two */
+
+       struct delayed_work             sp_task;
+       unsigned long                   sp_flags;
+};
+
+enum QEDE_STATE {
+       QEDE_STATE_CLOSED,
+       QEDE_STATE_OPEN,
+};
+
+#define HILO_U64(hi, lo)               ((((u64)(hi)) << 32) + (lo))
+
+#define        MAX_NUM_TC      8
+#define        MAX_NUM_PRI     8
+
+/* The driver supports the new build_skb() API:
+ * RX ring buffer contains pointer to kmalloc() data only,
+ * skb are built only after the frame was DMA-ed.
+ */
+struct sw_rx_data {
+       u8 *data;
+
+       DEFINE_DMA_UNMAP_ADDR(mapping);
+};
+
+struct qede_rx_queue {
+       __le16                  *hw_cons_ptr;
+       struct sw_rx_data       *sw_rx_ring;
+       u16                     sw_rx_cons;
+       u16                     sw_rx_prod;
+       struct qed_chain        rx_bd_ring;
+       struct qed_chain        rx_comp_ring;
+       void __iomem            *hw_rxq_prod_addr;
+
+       int                     rx_buf_size;
+
+       u16                     num_rx_buffers;
+       u16                     rxq_id;
+
+       u64                     rx_hw_errors;
+       u64                     rx_alloc_errors;
+};
+
+union db_prod {
+       struct eth_db_data data;
+       u32             raw;
+};
+
+struct sw_tx_bd {
+       struct sk_buff *skb;
+       u8 flags;
+/* Set on the first BD descriptor when there is a split BD */
+#define QEDE_TSO_SPLIT_BD              BIT(0)
+};
+
+struct qede_tx_queue {
+       int                     index; /* Queue index */
+       __le16                  *hw_cons_ptr;
+       struct sw_tx_bd         *sw_tx_ring;
+       u16                     sw_tx_cons;
+       u16                     sw_tx_prod;
+       struct qed_chain        tx_pbl;
+       void __iomem            *doorbell_addr;
+       union db_prod           tx_db;
+
+       u16                     num_tx_buffers;
+};
+
+#define BD_UNMAP_ADDR(bd)              HILO_U64(le32_to_cpu((bd)->addr.hi), \
+                                                le32_to_cpu((bd)->addr.lo))
+#define BD_SET_UNMAP_ADDR_LEN(bd, maddr, len)                          \
+       do {                                                            \
+               (bd)->addr.hi = cpu_to_le32(upper_32_bits(maddr));      \
+               (bd)->addr.lo = cpu_to_le32(lower_32_bits(maddr));      \
+               (bd)->nbytes = cpu_to_le16(len);                        \
+       } while (0)
+#define BD_UNMAP_LEN(bd)               (le16_to_cpu((bd)->nbytes))
+
+struct qede_fastpath {
+       struct qede_dev *edev;
+       u8                      rss_id;
+       struct napi_struct      napi;
+       struct qed_sb_info      *sb_info;
+       struct qede_rx_queue    *rxq;
+       struct qede_tx_queue    *txqs;
+
+#define VEC_NAME_SIZE  (sizeof(((struct net_device *)0)->name) + 8)
+       char    name[VEC_NAME_SIZE];
+};
+
+/* Debug print definitions */
+#define DP_NAME(edev) ((edev)->ndev->name)
+
+#define XMIT_PLAIN             0
+#define XMIT_L4_CSUM           BIT(0)
+#define XMIT_LSO               BIT(1)
+#define XMIT_ENC               BIT(2)
+
+#define QEDE_CSUM_ERROR                        BIT(0)
+#define QEDE_CSUM_UNNECESSARY          BIT(1)
+
+#define QEDE_SP_RX_MODE                1
+
+union qede_reload_args {
+       u16 mtu;
+};
+
+void qede_config_debug(uint debug, u32 *p_dp_module, u8 *p_dp_level);
+void qede_set_ethtool_ops(struct net_device *netdev);
+void qede_reload(struct qede_dev *edev,
+                void (*func)(struct qede_dev *edev,
+                             union qede_reload_args *args),
+                union qede_reload_args *args);
+int qede_change_mtu(struct net_device *dev, int new_mtu);
+void qede_fill_by_demand_stats(struct qede_dev *edev);
+
+#define RX_RING_SIZE_POW       13
+#define RX_RING_SIZE           BIT(RX_RING_SIZE_POW)
+#define NUM_RX_BDS_MAX         (RX_RING_SIZE - 1)
+#define NUM_RX_BDS_MIN         128
+#define NUM_RX_BDS_DEF         NUM_RX_BDS_MAX
+
+#define TX_RING_SIZE_POW       13
+#define TX_RING_SIZE           BIT(TX_RING_SIZE_POW)
+#define NUM_TX_BDS_MAX         (TX_RING_SIZE - 1)
+#define NUM_TX_BDS_MIN         128
+#define NUM_TX_BDS_DEF         NUM_TX_BDS_MAX
+
+#define        for_each_rss(i) for (i = 0; i < edev->num_rss; i++)
+
+#endif /* _QEDE_H_ */
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
new file mode 100644 (file)
index 0000000..3a36247
--- /dev/null
@@ -0,0 +1,385 @@
+/* QLogic qede NIC Driver
+* Copyright (c) 2015 QLogic Corporation
+*
+* This software is available under the terms of the GNU General Public License
+* (GPL) Version 2, available from the file COPYING in the main directory of
+* this source tree.
+*/
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/capability.h>
+#include "qede.h"
+
+#define QEDE_STAT_OFFSET(stat_name) (offsetof(struct qede_stats, stat_name))
+#define QEDE_STAT_STRING(stat_name) (#stat_name)
+#define _QEDE_STAT(stat_name, pf_only) \
+        {QEDE_STAT_OFFSET(stat_name), QEDE_STAT_STRING(stat_name), pf_only}
+#define QEDE_PF_STAT(stat_name)                _QEDE_STAT(stat_name, true)
+#define QEDE_STAT(stat_name)           _QEDE_STAT(stat_name, false)
+
+#define QEDE_RQSTAT_OFFSET(stat_name) \
+        (offsetof(struct qede_rx_queue, stat_name))
+#define QEDE_RQSTAT_STRING(stat_name) (#stat_name)
+#define QEDE_RQSTAT(stat_name) \
+        {QEDE_RQSTAT_OFFSET(stat_name), QEDE_RQSTAT_STRING(stat_name)}
+static const struct {
+       u64 offset;
+       char string[ETH_GSTRING_LEN];
+} qede_rqstats_arr[] = {
+       QEDE_RQSTAT(rx_hw_errors),
+       QEDE_RQSTAT(rx_alloc_errors),
+};
+
+#define QEDE_NUM_RQSTATS ARRAY_SIZE(qede_rqstats_arr)
+#define QEDE_RQSTATS_DATA(dev, sindex, rqindex) \
+       (*((u64 *)(((char *)(dev->fp_array[(rqindex)].rxq)) +\
+                   qede_rqstats_arr[(sindex)].offset)))
+static const struct {
+       u64 offset;
+       char string[ETH_GSTRING_LEN];
+       bool pf_only;
+} qede_stats_arr[] = {
+       QEDE_STAT(rx_ucast_bytes),
+       QEDE_STAT(rx_mcast_bytes),
+       QEDE_STAT(rx_bcast_bytes),
+       QEDE_STAT(rx_ucast_pkts),
+       QEDE_STAT(rx_mcast_pkts),
+       QEDE_STAT(rx_bcast_pkts),
+
+       QEDE_STAT(tx_ucast_bytes),
+       QEDE_STAT(tx_mcast_bytes),
+       QEDE_STAT(tx_bcast_bytes),
+       QEDE_STAT(tx_ucast_pkts),
+       QEDE_STAT(tx_mcast_pkts),
+       QEDE_STAT(tx_bcast_pkts),
+
+       QEDE_PF_STAT(rx_64_byte_packets),
+       QEDE_PF_STAT(rx_127_byte_packets),
+       QEDE_PF_STAT(rx_255_byte_packets),
+       QEDE_PF_STAT(rx_511_byte_packets),
+       QEDE_PF_STAT(rx_1023_byte_packets),
+       QEDE_PF_STAT(rx_1518_byte_packets),
+       QEDE_PF_STAT(rx_1522_byte_packets),
+       QEDE_PF_STAT(rx_2047_byte_packets),
+       QEDE_PF_STAT(rx_4095_byte_packets),
+       QEDE_PF_STAT(rx_9216_byte_packets),
+       QEDE_PF_STAT(rx_16383_byte_packets),
+       QEDE_PF_STAT(tx_64_byte_packets),
+       QEDE_PF_STAT(tx_65_to_127_byte_packets),
+       QEDE_PF_STAT(tx_128_to_255_byte_packets),
+       QEDE_PF_STAT(tx_256_to_511_byte_packets),
+       QEDE_PF_STAT(tx_512_to_1023_byte_packets),
+       QEDE_PF_STAT(tx_1024_to_1518_byte_packets),
+       QEDE_PF_STAT(tx_1519_to_2047_byte_packets),
+       QEDE_PF_STAT(tx_2048_to_4095_byte_packets),
+       QEDE_PF_STAT(tx_4096_to_9216_byte_packets),
+       QEDE_PF_STAT(tx_9217_to_16383_byte_packets),
+
+       QEDE_PF_STAT(rx_mac_crtl_frames),
+       QEDE_PF_STAT(tx_mac_ctrl_frames),
+       QEDE_PF_STAT(rx_pause_frames),
+       QEDE_PF_STAT(tx_pause_frames),
+       QEDE_PF_STAT(rx_pfc_frames),
+       QEDE_PF_STAT(tx_pfc_frames),
+
+       QEDE_PF_STAT(rx_crc_errors),
+       QEDE_PF_STAT(rx_align_errors),
+       QEDE_PF_STAT(rx_carrier_errors),
+       QEDE_PF_STAT(rx_oversize_packets),
+       QEDE_PF_STAT(rx_jabbers),
+       QEDE_PF_STAT(rx_undersize_packets),
+       QEDE_PF_STAT(rx_fragments),
+       QEDE_PF_STAT(tx_lpi_entry_count),
+       QEDE_PF_STAT(tx_total_collisions),
+       QEDE_PF_STAT(brb_truncates),
+       QEDE_PF_STAT(brb_discards),
+       QEDE_STAT(no_buff_discards),
+       QEDE_PF_STAT(mftag_filter_discards),
+       QEDE_PF_STAT(mac_filter_discards),
+       QEDE_STAT(tx_err_drop_pkts),
+
+       QEDE_STAT(coalesced_pkts),
+       QEDE_STAT(coalesced_events),
+       QEDE_STAT(coalesced_aborts_num),
+       QEDE_STAT(non_coalesced_pkts),
+       QEDE_STAT(coalesced_bytes),
+};
+
+#define QEDE_STATS_DATA(dev, index) \
+       (*((u64 *)(((char *)(dev)) + offsetof(struct qede_dev, stats) \
+                       + qede_stats_arr[(index)].offset)))
+
+#define QEDE_NUM_STATS ARRAY_SIZE(qede_stats_arr)
+
+static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf)
+{
+       int i, j, k;
+
+       for (i = 0, j = 0; i < QEDE_NUM_STATS; i++) {
+               strcpy(buf + j * ETH_GSTRING_LEN,
+                      qede_stats_arr[i].string);
+               j++;
+       }
+
+       for (k = 0; k < QEDE_NUM_RQSTATS; k++, j++)
+               strcpy(buf + j * ETH_GSTRING_LEN,
+                      qede_rqstats_arr[k].string);
+}
+
+static void qede_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
+{
+       struct qede_dev *edev = netdev_priv(dev);
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               qede_get_strings_stats(edev, buf);
+               break;
+       default:
+               DP_VERBOSE(edev, QED_MSG_DEBUG,
+                          "Unsupported stringset 0x%08x\n", stringset);
+       }
+}
+
+static void qede_get_ethtool_stats(struct net_device *dev,
+                                  struct ethtool_stats *stats, u64 *buf)
+{
+       struct qede_dev *edev = netdev_priv(dev);
+       int sidx, cnt = 0;
+       int qid;
+
+       qede_fill_by_demand_stats(edev);
+
+       mutex_lock(&edev->qede_lock);
+
+       for (sidx = 0; sidx < QEDE_NUM_STATS; sidx++)
+               buf[cnt++] = QEDE_STATS_DATA(edev, sidx);
+
+       for (sidx = 0; sidx < QEDE_NUM_RQSTATS; sidx++) {
+               buf[cnt] = 0;
+               for (qid = 0; qid < edev->num_rss; qid++)
+                       buf[cnt] += QEDE_RQSTATS_DATA(edev, sidx, qid);
+               cnt++;
+       }
+
+       mutex_unlock(&edev->qede_lock);
+}
+
+static int qede_get_sset_count(struct net_device *dev, int stringset)
+{
+       struct qede_dev *edev = netdev_priv(dev);
+       int num_stats = QEDE_NUM_STATS;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               return num_stats + QEDE_NUM_RQSTATS;
+
+       default:
+               DP_VERBOSE(edev, QED_MSG_DEBUG,
+                          "Unsupported stringset 0x%08x\n", stringset);
+               return -EINVAL;
+       }
+}
+
+static int qede_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct qede_dev *edev = netdev_priv(dev);
+       struct qed_link_output current_link;
+
+       memset(&current_link, 0, sizeof(current_link));
+       edev->ops->common->get_link(edev->cdev, &current_link);
+
+       cmd->supported = current_link.supported_caps;
+       cmd->advertising = current_link.advertised_caps;
+       if ((edev->state == QEDE_STATE_OPEN) && (current_link.link_up)) {
+               ethtool_cmd_speed_set(cmd, current_link.speed);
+               cmd->duplex = current_link.duplex;
+       } else {
+               cmd->duplex = DUPLEX_UNKNOWN;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+       }
+       cmd->port = current_link.port;
+       cmd->autoneg = (current_link.autoneg) ? AUTONEG_ENABLE :
+                                               AUTONEG_DISABLE;
+       cmd->lp_advertising = current_link.lp_caps;
+
+       return 0;
+}
+
+static int qede_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct qede_dev *edev = netdev_priv(dev);
+       struct qed_link_output current_link;
+       struct qed_link_params params;
+       u32 speed;
+
+       if (edev->dev_info.common.is_mf) {
+               DP_INFO(edev,
+                       "Link parameters can not be changed in MF mode\n");
+               return -EOPNOTSUPP;
+       }
+
+       memset(&current_link, 0, sizeof(current_link));
+       memset(&params, 0, sizeof(params));
+       edev->ops->common->get_link(edev->cdev, &current_link);
+
+       speed = ethtool_cmd_speed(cmd);
+       params.override_flags |= QED_LINK_OVERRIDE_SPEED_ADV_SPEEDS;
+       params.override_flags |= QED_LINK_OVERRIDE_SPEED_AUTONEG;
+       if (cmd->autoneg == AUTONEG_ENABLE) {
+               params.autoneg = true;
+               params.forced_speed = 0;
+               params.adv_speeds = cmd->advertising;
+       } else { /* forced speed */
+               params.override_flags |= QED_LINK_OVERRIDE_SPEED_FORCED_SPEED;
+               params.autoneg = false;
+               params.forced_speed = speed;
+               switch (speed) {
+               case SPEED_10000:
+                       if (!(current_link.supported_caps &
+                           SUPPORTED_10000baseKR_Full)) {
+                               DP_INFO(edev, "10G speed not supported\n");
+                               return -EINVAL;
+                       }
+                       params.adv_speeds = SUPPORTED_10000baseKR_Full;
+                       break;
+               case SPEED_40000:
+                       if (!(current_link.supported_caps &
+                           SUPPORTED_40000baseLR4_Full)) {
+                               DP_INFO(edev, "40G speed not supported\n");
+                               return -EINVAL;
+                       }
+                       params.adv_speeds = SUPPORTED_40000baseLR4_Full;
+                       break;
+               default:
+                       DP_INFO(edev, "Unsupported speed %u\n", speed);
+                       return -EINVAL;
+               }
+       }
+
+       params.link_up = true;
+       edev->ops->common->set_link(edev->cdev, &params);
+
+       return 0;
+}
+
+static void qede_get_drvinfo(struct net_device *ndev,
+                            struct ethtool_drvinfo *info)
+{
+       char mfw[ETHTOOL_FWVERS_LEN], storm[ETHTOOL_FWVERS_LEN];
+       struct qede_dev *edev = netdev_priv(ndev);
+
+       strlcpy(info->driver, "qede", sizeof(info->driver));
+       strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
+
+       snprintf(storm, ETHTOOL_FWVERS_LEN, "%d.%d.%d.%d",
+                edev->dev_info.common.fw_major,
+                edev->dev_info.common.fw_minor,
+                edev->dev_info.common.fw_rev,
+                edev->dev_info.common.fw_eng);
+
+       snprintf(mfw, ETHTOOL_FWVERS_LEN, "%d.%d.%d.%d",
+                (edev->dev_info.common.mfw_rev >> 24) & 0xFF,
+                (edev->dev_info.common.mfw_rev >> 16) & 0xFF,
+                (edev->dev_info.common.mfw_rev >> 8) & 0xFF,
+                edev->dev_info.common.mfw_rev & 0xFF);
+
+       if ((strlen(storm) + strlen(mfw) + strlen("mfw storm  ")) <
+           sizeof(info->fw_version)) {
+               snprintf(info->fw_version, sizeof(info->fw_version),
+                        "mfw %s storm %s", mfw, storm);
+       } else {
+               snprintf(info->fw_version, sizeof(info->fw_version),
+                        "%s %s", mfw, storm);
+       }
+
+       strlcpy(info->bus_info, pci_name(edev->pdev), sizeof(info->bus_info));
+}
+
+static u32 qede_get_msglevel(struct net_device *ndev)
+{
+       struct qede_dev *edev = netdev_priv(ndev);
+
+       return ((u32)edev->dp_level << QED_LOG_LEVEL_SHIFT) |
+              edev->dp_module;
+}
+
+static void qede_set_msglevel(struct net_device *ndev, u32 level)
+{
+       struct qede_dev *edev = netdev_priv(ndev);
+       u32 dp_module = 0;
+       u8 dp_level = 0;
+
+       qede_config_debug(level, &dp_module, &dp_level);
+
+       edev->dp_level = dp_level;
+       edev->dp_module = dp_module;
+       edev->ops->common->update_msglvl(edev->cdev,
+                                        dp_module, dp_level);
+}
+
+static u32 qede_get_link(struct net_device *dev)
+{
+       struct qede_dev *edev = netdev_priv(dev);
+       struct qed_link_output current_link;
+
+       memset(&current_link, 0, sizeof(current_link));
+       edev->ops->common->get_link(edev->cdev, &current_link);
+
+       return current_link.link_up;
+}
+
+static void qede_update_mtu(struct qede_dev *edev, union qede_reload_args *args)
+{
+       edev->ndev->mtu = args->mtu;
+}
+
+/* Netdevice NDOs */
+#define ETH_MAX_JUMBO_PACKET_SIZE      9600
+#define ETH_MIN_PACKET_SIZE            60
+int qede_change_mtu(struct net_device *ndev, int new_mtu)
+{
+       struct qede_dev *edev = netdev_priv(ndev);
+       union qede_reload_args args;
+
+       if ((new_mtu > ETH_MAX_JUMBO_PACKET_SIZE) ||
+           ((new_mtu + ETH_HLEN) < ETH_MIN_PACKET_SIZE)) {
+               DP_ERR(edev, "Can't support requested MTU size\n");
+               return -EINVAL;
+       }
+
+       DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+                  "Configuring MTU size of %d\n", new_mtu);
+
+       /* Set the mtu field and re-start the interface if needed*/
+       args.mtu = new_mtu;
+
+       if (netif_running(edev->ndev))
+               qede_reload(edev, &qede_update_mtu, &args);
+
+       qede_update_mtu(edev, &args);
+
+       return 0;
+}
+
+static const struct ethtool_ops qede_ethtool_ops = {
+       .get_settings = qede_get_settings,
+       .set_settings = qede_set_settings,
+       .get_drvinfo = qede_get_drvinfo,
+       .get_msglevel = qede_get_msglevel,
+       .set_msglevel = qede_set_msglevel,
+       .get_link = qede_get_link,
+       .get_strings = qede_get_strings,
+       .get_ethtool_stats = qede_get_ethtool_stats,
+       .get_sset_count = qede_get_sset_count,
+
+};
+
+void qede_set_ethtool_ops(struct net_device *dev)
+{
+       dev->ethtool_ops = &qede_ethtool_ops;
+}
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
new file mode 100644 (file)
index 0000000..f4657a2
--- /dev/null
@@ -0,0 +1,2584 @@
+/* QLogic qede NIC Driver
+* Copyright (c) 2015 QLogic Corporation
+*
+* This software is available under the terms of the GNU General Public License
+* (GPL) Version 2, available from the file COPYING in the main directory of
+* this source tree.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <asm/byteorder.h>
+#include <asm/param.h>
+#include <linux/io.h>
+#include <linux/netdev_features.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <net/vxlan.h>
+#include <linux/ip.h>
+#include <net/ipv6.h>
+#include <net/tcp.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/pkt_sched.h>
+#include <linux/ethtool.h>
+#include <linux/in.h>
+#include <linux/random.h>
+#include <net/ip6_checksum.h>
+#include <linux/bitops.h>
+
+#include "qede.h"
+
+static const char version[] = "QLogic QL4xxx 40G/100G Ethernet Driver qede "
+                             DRV_MODULE_VERSION "\n";
+
+MODULE_DESCRIPTION("QLogic 40G/100G Ethernet Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+static uint debug;
+module_param(debug, uint, 0);
+MODULE_PARM_DESC(debug, " Default debug msglevel");
+
+static const struct qed_eth_ops *qed_ops;
+
+#define CHIP_NUM_57980S_40             0x1634
+#define CHIP_NUM_57980S_10             0x1635
+#define CHIP_NUM_57980S_MF             0x1636
+#define CHIP_NUM_57980S_100            0x1644
+#define CHIP_NUM_57980S_50             0x1654
+#define CHIP_NUM_57980S_25             0x1656
+
+#ifndef PCI_DEVICE_ID_NX2_57980E
+#define PCI_DEVICE_ID_57980S_40                CHIP_NUM_57980S_40
+#define PCI_DEVICE_ID_57980S_10                CHIP_NUM_57980S_10
+#define PCI_DEVICE_ID_57980S_MF                CHIP_NUM_57980S_MF
+#define PCI_DEVICE_ID_57980S_100       CHIP_NUM_57980S_100
+#define PCI_DEVICE_ID_57980S_50                CHIP_NUM_57980S_50
+#define PCI_DEVICE_ID_57980S_25                CHIP_NUM_57980S_25
+#endif
+
+static const struct pci_device_id qede_pci_tbl[] = {
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_40), 0 },
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_10), 0 },
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_MF), 0 },
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_100), 0 },
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_50), 0 },
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_25), 0 },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, qede_pci_tbl);
+
+static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id);
+
+#define TX_TIMEOUT             (5 * HZ)
+
+static void qede_remove(struct pci_dev *pdev);
+static int qede_alloc_rx_buffer(struct qede_dev *edev,
+                               struct qede_rx_queue *rxq);
+static void qede_link_update(void *dev, struct qed_link_output *link);
+
+static struct pci_driver qede_pci_driver = {
+       .name = "qede",
+       .id_table = qede_pci_tbl,
+       .probe = qede_probe,
+       .remove = qede_remove,
+};
+
+static struct qed_eth_cb_ops qede_ll_ops = {
+       {
+               .link_update = qede_link_update,
+       },
+};
+
+static int qede_netdev_event(struct notifier_block *this, unsigned long event,
+                            void *ptr)
+{
+       struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
+       struct ethtool_drvinfo drvinfo;
+       struct qede_dev *edev;
+
+       /* Currently only support name change */
+       if (event != NETDEV_CHANGENAME)
+               goto done;
+
+       /* Check whether this is a qede device */
+       if (!ndev || !ndev->ethtool_ops || !ndev->ethtool_ops->get_drvinfo)
+               goto done;
+
+       memset(&drvinfo, 0, sizeof(drvinfo));
+       ndev->ethtool_ops->get_drvinfo(ndev, &drvinfo);
+       if (strcmp(drvinfo.driver, "qede"))
+               goto done;
+       edev = netdev_priv(ndev);
+
+       /* Notify qed of the name change */
+       if (!edev->ops || !edev->ops->common)
+               goto done;
+       edev->ops->common->set_id(edev->cdev, edev->ndev->name,
+                                 "qede");
+
+done:
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block qede_netdev_notifier = {
+       .notifier_call = qede_netdev_event,
+};
+
+static
+int __init qede_init(void)
+{
+       int ret;
+       u32 qed_ver;
+
+       pr_notice("qede_init: %s\n", version);
+
+       qed_ver = qed_get_protocol_version(QED_PROTOCOL_ETH);
+       if (qed_ver !=  QEDE_ETH_INTERFACE_VERSION) {
+               pr_notice("Version mismatch [%08x != %08x]\n",
+                         qed_ver,
+                         QEDE_ETH_INTERFACE_VERSION);
+               return -EINVAL;
+       }
+
+       qed_ops = qed_get_eth_ops(QEDE_ETH_INTERFACE_VERSION);
+       if (!qed_ops) {
+               pr_notice("Failed to get qed ethtool operations\n");
+               return -EINVAL;
+       }
+
+       /* Must register notifier before pci ops, since we might miss
+        * interface rename after pci probe and netdev registeration.
+        */
+       ret = register_netdevice_notifier(&qede_netdev_notifier);
+       if (ret) {
+               pr_notice("Failed to register netdevice_notifier\n");
+               qed_put_eth_ops();
+               return -EINVAL;
+       }
+
+       ret = pci_register_driver(&qede_pci_driver);
+       if (ret) {
+               pr_notice("Failed to register driver\n");
+               unregister_netdevice_notifier(&qede_netdev_notifier);
+               qed_put_eth_ops();
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void __exit qede_cleanup(void)
+{
+       pr_notice("qede_cleanup called\n");
+
+       unregister_netdevice_notifier(&qede_netdev_notifier);
+       pci_unregister_driver(&qede_pci_driver);
+       qed_put_eth_ops();
+}
+
+module_init(qede_init);
+module_exit(qede_cleanup);
+
+/* -------------------------------------------------------------------------
+ * START OF FAST-PATH
+ * -------------------------------------------------------------------------
+ */
+
+/* Unmap the data and free skb */
+static int qede_free_tx_pkt(struct qede_dev *edev,
+                           struct qede_tx_queue *txq,
+                           int *len)
+{
+       u16 idx = txq->sw_tx_cons & NUM_TX_BDS_MAX;
+       struct sk_buff *skb = txq->sw_tx_ring[idx].skb;
+       struct eth_tx_1st_bd *first_bd;
+       struct eth_tx_bd *tx_data_bd;
+       int bds_consumed = 0;
+       int nbds;
+       bool data_split = txq->sw_tx_ring[idx].flags & QEDE_TSO_SPLIT_BD;
+       int i, split_bd_len = 0;
+
+       if (unlikely(!skb)) {
+               DP_ERR(edev,
+                      "skb is null for txq idx=%d txq->sw_tx_cons=%d txq->sw_tx_prod=%d\n",
+                      idx, txq->sw_tx_cons, txq->sw_tx_prod);
+               return -1;
+       }
+
+       *len = skb->len;
+
+       first_bd = (struct eth_tx_1st_bd *)qed_chain_consume(&txq->tx_pbl);
+
+       bds_consumed++;
+
+       nbds = first_bd->data.nbds;
+
+       if (data_split) {
+               struct eth_tx_bd *split = (struct eth_tx_bd *)
+                       qed_chain_consume(&txq->tx_pbl);
+               split_bd_len = BD_UNMAP_LEN(split);
+               bds_consumed++;
+       }
+       dma_unmap_page(&edev->pdev->dev, BD_UNMAP_ADDR(first_bd),
+                      BD_UNMAP_LEN(first_bd) + split_bd_len, DMA_TO_DEVICE);
+
+       /* Unmap the data of the skb frags */
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++, bds_consumed++) {
+               tx_data_bd = (struct eth_tx_bd *)
+                       qed_chain_consume(&txq->tx_pbl);
+               dma_unmap_page(&edev->pdev->dev, BD_UNMAP_ADDR(tx_data_bd),
+                              BD_UNMAP_LEN(tx_data_bd), DMA_TO_DEVICE);
+       }
+
+       while (bds_consumed++ < nbds)
+               qed_chain_consume(&txq->tx_pbl);
+
+       /* Free skb */
+       dev_kfree_skb_any(skb);
+       txq->sw_tx_ring[idx].skb = NULL;
+       txq->sw_tx_ring[idx].flags = 0;
+
+       return 0;
+}
+
+/* Unmap the data and free skb when mapping failed during start_xmit */
+static void qede_free_failed_tx_pkt(struct qede_dev *edev,
+                                   struct qede_tx_queue *txq,
+                                   struct eth_tx_1st_bd *first_bd,
+                                   int nbd,
+                                   bool data_split)
+{
+       u16 idx = txq->sw_tx_prod & NUM_TX_BDS_MAX;
+       struct sk_buff *skb = txq->sw_tx_ring[idx].skb;
+       struct eth_tx_bd *tx_data_bd;
+       int i, split_bd_len = 0;
+
+       /* Return prod to its position before this skb was handled */
+       qed_chain_set_prod(&txq->tx_pbl,
+                          le16_to_cpu(txq->tx_db.data.bd_prod),
+                          first_bd);
+
+       first_bd = (struct eth_tx_1st_bd *)qed_chain_produce(&txq->tx_pbl);
+
+       if (data_split) {
+               struct eth_tx_bd *split = (struct eth_tx_bd *)
+                                         qed_chain_produce(&txq->tx_pbl);
+               split_bd_len = BD_UNMAP_LEN(split);
+               nbd--;
+       }
+
+       dma_unmap_page(&edev->pdev->dev, BD_UNMAP_ADDR(first_bd),
+                      BD_UNMAP_LEN(first_bd) + split_bd_len, DMA_TO_DEVICE);
+
+       /* Unmap the data of the skb frags */
+       for (i = 0; i < nbd; i++) {
+               tx_data_bd = (struct eth_tx_bd *)
+                       qed_chain_produce(&txq->tx_pbl);
+               if (tx_data_bd->nbytes)
+                       dma_unmap_page(&edev->pdev->dev,
+                                      BD_UNMAP_ADDR(tx_data_bd),
+                                      BD_UNMAP_LEN(tx_data_bd), DMA_TO_DEVICE);
+       }
+
+       /* Return again prod to its position before this skb was handled */
+       qed_chain_set_prod(&txq->tx_pbl,
+                          le16_to_cpu(txq->tx_db.data.bd_prod),
+                          first_bd);
+
+       /* Free skb */
+       dev_kfree_skb_any(skb);
+       txq->sw_tx_ring[idx].skb = NULL;
+       txq->sw_tx_ring[idx].flags = 0;
+}
+
+static u32 qede_xmit_type(struct qede_dev *edev,
+                         struct sk_buff *skb,
+                         int *ipv6_ext)
+{
+       u32 rc = XMIT_L4_CSUM;
+       __be16 l3_proto;
+
+       if (skb->ip_summed != CHECKSUM_PARTIAL)
+               return XMIT_PLAIN;
+
+       l3_proto = vlan_get_protocol(skb);
+       if (l3_proto == htons(ETH_P_IPV6) &&
+           (ipv6_hdr(skb)->nexthdr == NEXTHDR_IPV6))
+               *ipv6_ext = 1;
+
+       if (skb_is_gso(skb))
+               rc |= XMIT_LSO;
+
+       return rc;
+}
+
+static void qede_set_params_for_ipv6_ext(struct sk_buff *skb,
+                                        struct eth_tx_2nd_bd *second_bd,
+                                        struct eth_tx_3rd_bd *third_bd)
+{
+       u8 l4_proto;
+       u16 bd2_bits = 0, bd2_bits2 = 0;
+
+       bd2_bits2 |= (1 << ETH_TX_DATA_2ND_BD_IPV6_EXT_SHIFT);
+
+       bd2_bits |= ((((u8 *)skb_transport_header(skb) - skb->data) >> 1) &
+                    ETH_TX_DATA_2ND_BD_L4_HDR_START_OFFSET_W_MASK)
+                   << ETH_TX_DATA_2ND_BD_L4_HDR_START_OFFSET_W_SHIFT;
+
+       bd2_bits2 |= (ETH_L4_PSEUDO_CSUM_CORRECT_LENGTH <<
+                     ETH_TX_DATA_2ND_BD_L4_PSEUDO_CSUM_MODE_SHIFT);
+
+       if (vlan_get_protocol(skb) == htons(ETH_P_IPV6))
+               l4_proto = ipv6_hdr(skb)->nexthdr;
+       else
+               l4_proto = ip_hdr(skb)->protocol;
+
+       if (l4_proto == IPPROTO_UDP)
+               bd2_bits2 |= 1 << ETH_TX_DATA_2ND_BD_L4_UDP_SHIFT;
+
+       if (third_bd) {
+               third_bd->data.bitfields |=
+                       ((tcp_hdrlen(skb) / 4) &
+                        ETH_TX_DATA_3RD_BD_TCP_HDR_LEN_DW_MASK) <<
+                       ETH_TX_DATA_3RD_BD_TCP_HDR_LEN_DW_SHIFT;
+       }
+
+       second_bd->data.bitfields = cpu_to_le16(bd2_bits);
+       second_bd->data.bitfields2 = cpu_to_le16(bd2_bits2);
+}
+
+static int map_frag_to_bd(struct qede_dev *edev,
+                         skb_frag_t *frag,
+                         struct eth_tx_bd *bd)
+{
+       dma_addr_t mapping;
+
+       /* Map skb non-linear frag data for DMA */
+       mapping = skb_frag_dma_map(&edev->pdev->dev, frag, 0,
+                                  skb_frag_size(frag),
+                                  DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(&edev->pdev->dev, mapping))) {
+               DP_NOTICE(edev, "Unable to map frag - dropping packet\n");
+               return -ENOMEM;
+       }
+
+       /* Setup the data pointer of the frag data */
+       BD_SET_UNMAP_ADDR_LEN(bd, mapping, skb_frag_size(frag));
+
+       return 0;
+}
+
+/* Main transmit function */
+static
+netdev_tx_t qede_start_xmit(struct sk_buff *skb,
+                           struct net_device *ndev)
+{
+       struct qede_dev *edev = netdev_priv(ndev);
+       struct netdev_queue *netdev_txq;
+       struct qede_tx_queue *txq;
+       struct eth_tx_1st_bd *first_bd;
+       struct eth_tx_2nd_bd *second_bd = NULL;
+       struct eth_tx_3rd_bd *third_bd = NULL;
+       struct eth_tx_bd *tx_data_bd = NULL;
+       u16 txq_index;
+       u8 nbd = 0;
+       dma_addr_t mapping;
+       int rc, frag_idx = 0, ipv6_ext = 0;
+       u8 xmit_type;
+       u16 idx;
+       u16 hlen;
+       bool data_split;
+
+       /* Get tx-queue context and netdev index */
+       txq_index = skb_get_queue_mapping(skb);
+       WARN_ON(txq_index >= QEDE_TSS_CNT(edev));
+       txq = QEDE_TX_QUEUE(edev, txq_index);
+       netdev_txq = netdev_get_tx_queue(ndev, txq_index);
+
+       /* Current code doesn't support SKB linearization, since the max number
+        * of skb frags can be passed in the FW HSI.
+        */
+       BUILD_BUG_ON(MAX_SKB_FRAGS > ETH_TX_MAX_BDS_PER_NON_LSO_PACKET);
+
+       WARN_ON(qed_chain_get_elem_left(&txq->tx_pbl) <
+                              (MAX_SKB_FRAGS + 1));
+
+       xmit_type = qede_xmit_type(edev, skb, &ipv6_ext);
+
+       /* Fill the entry in the SW ring and the BDs in the FW ring */
+       idx = txq->sw_tx_prod & NUM_TX_BDS_MAX;
+       txq->sw_tx_ring[idx].skb = skb;
+       first_bd = (struct eth_tx_1st_bd *)
+                  qed_chain_produce(&txq->tx_pbl);
+       memset(first_bd, 0, sizeof(*first_bd));
+       first_bd->data.bd_flags.bitfields =
+               1 << ETH_TX_1ST_BD_FLAGS_START_BD_SHIFT;
+
+       /* Map skb linear data for DMA and set in the first BD */
+       mapping = dma_map_single(&edev->pdev->dev, skb->data,
+                                skb_headlen(skb), DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(&edev->pdev->dev, mapping))) {
+               DP_NOTICE(edev, "SKB mapping failed\n");
+               qede_free_failed_tx_pkt(edev, txq, first_bd, 0, false);
+               return NETDEV_TX_OK;
+       }
+       nbd++;
+       BD_SET_UNMAP_ADDR_LEN(first_bd, mapping, skb_headlen(skb));
+
+       /* In case there is IPv6 with extension headers or LSO we need 2nd and
+        * 3rd BDs.
+        */
+       if (unlikely((xmit_type & XMIT_LSO) | ipv6_ext)) {
+               second_bd = (struct eth_tx_2nd_bd *)
+                       qed_chain_produce(&txq->tx_pbl);
+               memset(second_bd, 0, sizeof(*second_bd));
+
+               nbd++;
+               third_bd = (struct eth_tx_3rd_bd *)
+                       qed_chain_produce(&txq->tx_pbl);
+               memset(third_bd, 0, sizeof(*third_bd));
+
+               nbd++;
+               /* We need to fill in additional data in second_bd... */
+               tx_data_bd = (struct eth_tx_bd *)second_bd;
+       }
+
+       if (skb_vlan_tag_present(skb)) {
+               first_bd->data.vlan = cpu_to_le16(skb_vlan_tag_get(skb));
+               first_bd->data.bd_flags.bitfields |=
+                       1 << ETH_TX_1ST_BD_FLAGS_VLAN_INSERTION_SHIFT;
+       }
+
+       /* Fill the parsing flags & params according to the requested offload */
+       if (xmit_type & XMIT_L4_CSUM) {
+               /* We don't re-calculate IP checksum as it is already done by
+                * the upper stack
+                */
+               first_bd->data.bd_flags.bitfields |=
+                       1 << ETH_TX_1ST_BD_FLAGS_L4_CSUM_SHIFT;
+
+               /* If the packet is IPv6 with extension header, indicate that
+                * to FW and pass few params, since the device cracker doesn't
+                * support parsing IPv6 with extension header/s.
+                */
+               if (unlikely(ipv6_ext))
+                       qede_set_params_for_ipv6_ext(skb, second_bd, third_bd);
+       }
+
+       if (xmit_type & XMIT_LSO) {
+               first_bd->data.bd_flags.bitfields |=
+                       (1 << ETH_TX_1ST_BD_FLAGS_LSO_SHIFT);
+               third_bd->data.lso_mss =
+                       cpu_to_le16(skb_shinfo(skb)->gso_size);
+
+               first_bd->data.bd_flags.bitfields |=
+               1 << ETH_TX_1ST_BD_FLAGS_IP_CSUM_SHIFT;
+               hlen = skb_transport_header(skb) +
+                      tcp_hdrlen(skb) - skb->data;
+
+               /* @@@TBD - if will not be removed need to check */
+               third_bd->data.bitfields |=
+                       (1 << ETH_TX_DATA_3RD_BD_HDR_NBD_SHIFT);
+
+               /* Make life easier for FW guys who can't deal with header and
+                * data on same BD. If we need to split, use the second bd...
+                */
+               if (unlikely(skb_headlen(skb) > hlen)) {
+                       DP_VERBOSE(edev, NETIF_MSG_TX_QUEUED,
+                                  "TSO split header size is %d (%x:%x)\n",
+                                  first_bd->nbytes, first_bd->addr.hi,
+                                  first_bd->addr.lo);
+
+                       mapping = HILO_U64(le32_to_cpu(first_bd->addr.hi),
+                                          le32_to_cpu(first_bd->addr.lo)) +
+                                          hlen;
+
+                       BD_SET_UNMAP_ADDR_LEN(tx_data_bd, mapping,
+                                             le16_to_cpu(first_bd->nbytes) -
+                                             hlen);
+
+                       /* this marks the BD as one that has no
+                        * individual mapping
+                        */
+                       txq->sw_tx_ring[idx].flags |= QEDE_TSO_SPLIT_BD;
+
+                       first_bd->nbytes = cpu_to_le16(hlen);
+
+                       tx_data_bd = (struct eth_tx_bd *)third_bd;
+                       data_split = true;
+               }
+       }
+
+       /* Handle fragmented skb */
+       /* special handle for frags inside 2nd and 3rd bds.. */
+       while (tx_data_bd && frag_idx < skb_shinfo(skb)->nr_frags) {
+               rc = map_frag_to_bd(edev,
+                                   &skb_shinfo(skb)->frags[frag_idx],
+                                   tx_data_bd);
+               if (rc) {
+                       qede_free_failed_tx_pkt(edev, txq, first_bd, nbd,
+                                               data_split);
+                       return NETDEV_TX_OK;
+               }
+
+               if (tx_data_bd == (struct eth_tx_bd *)second_bd)
+                       tx_data_bd = (struct eth_tx_bd *)third_bd;
+               else
+                       tx_data_bd = NULL;
+
+               frag_idx++;
+       }
+
+       /* map last frags into 4th, 5th .... */
+       for (; frag_idx < skb_shinfo(skb)->nr_frags; frag_idx++, nbd++) {
+               tx_data_bd = (struct eth_tx_bd *)
+                            qed_chain_produce(&txq->tx_pbl);
+
+               memset(tx_data_bd, 0, sizeof(*tx_data_bd));
+
+               rc = map_frag_to_bd(edev,
+                                   &skb_shinfo(skb)->frags[frag_idx],
+                                   tx_data_bd);
+               if (rc) {
+                       qede_free_failed_tx_pkt(edev, txq, first_bd, nbd,
+                                               data_split);
+                       return NETDEV_TX_OK;
+               }
+       }
+
+       /* update the first BD with the actual num BDs */
+       first_bd->data.nbds = nbd;
+
+       netdev_tx_sent_queue(netdev_txq, skb->len);
+
+       skb_tx_timestamp(skb);
+
+       /* Advance packet producer only before sending the packet since mapping
+        * of pages may fail.
+        */
+       txq->sw_tx_prod++;
+
+       /* 'next page' entries are counted in the producer value */
+       txq->tx_db.data.bd_prod =
+               cpu_to_le16(qed_chain_get_prod_idx(&txq->tx_pbl));
+
+       /* wmb makes sure that the BDs data is updated before updating the
+        * producer, otherwise FW may read old data from the BDs.
+        */
+       wmb();
+       barrier();
+       writel(txq->tx_db.raw, txq->doorbell_addr);
+
+       /* mmiowb is needed to synchronize doorbell writes from more than one
+        * processor. It guarantees that the write arrives to the device before
+        * the queue lock is released and another start_xmit is called (possibly
+        * on another CPU). Without this barrier, the next doorbell can bypass
+        * this doorbell. This is applicable to IA64/Altix systems.
+        */
+       mmiowb();
+
+       if (unlikely(qed_chain_get_elem_left(&txq->tx_pbl)
+                     < (MAX_SKB_FRAGS + 1))) {
+               netif_tx_stop_queue(netdev_txq);
+               DP_VERBOSE(edev, NETIF_MSG_TX_QUEUED,
+                          "Stop queue was called\n");
+               /* paired memory barrier is in qede_tx_int(), we have to keep
+                * ordering of set_bit() in netif_tx_stop_queue() and read of
+                * fp->bd_tx_cons
+                */
+               smp_mb();
+
+               if (qed_chain_get_elem_left(&txq->tx_pbl)
+                    >= (MAX_SKB_FRAGS + 1) &&
+                   (edev->state == QEDE_STATE_OPEN)) {
+                       netif_tx_wake_queue(netdev_txq);
+                       DP_VERBOSE(edev, NETIF_MSG_TX_QUEUED,
+                                  "Wake queue was called\n");
+               }
+       }
+
+       return NETDEV_TX_OK;
+}
+
+static int qede_txq_has_work(struct qede_tx_queue *txq)
+{
+       u16 hw_bd_cons;
+
+       /* Tell compiler that consumer and producer can change */
+       barrier();
+       hw_bd_cons = le16_to_cpu(*txq->hw_cons_ptr);
+       if (qed_chain_get_cons_idx(&txq->tx_pbl) == hw_bd_cons + 1)
+               return 0;
+
+       return hw_bd_cons != qed_chain_get_cons_idx(&txq->tx_pbl);
+}
+
+static int qede_tx_int(struct qede_dev *edev,
+                      struct qede_tx_queue *txq)
+{
+       struct netdev_queue *netdev_txq;
+       u16 hw_bd_cons;
+       unsigned int pkts_compl = 0, bytes_compl = 0;
+       int rc;
+
+       netdev_txq = netdev_get_tx_queue(edev->ndev, txq->index);
+
+       hw_bd_cons = le16_to_cpu(*txq->hw_cons_ptr);
+       barrier();
+
+       while (hw_bd_cons != qed_chain_get_cons_idx(&txq->tx_pbl)) {
+               int len = 0;
+
+               rc = qede_free_tx_pkt(edev, txq, &len);
+               if (rc) {
+                       DP_NOTICE(edev, "hw_bd_cons = %d, chain_cons=%d\n",
+                                 hw_bd_cons,
+                                 qed_chain_get_cons_idx(&txq->tx_pbl));
+                       break;
+               }
+
+               bytes_compl += len;
+               pkts_compl++;
+               txq->sw_tx_cons++;
+       }
+
+       netdev_tx_completed_queue(netdev_txq, pkts_compl, bytes_compl);
+
+       /* Need to make the tx_bd_cons update visible to start_xmit()
+        * before checking for netif_tx_queue_stopped().  Without the
+        * memory barrier, there is a small possibility that
+        * start_xmit() will miss it and cause the queue to be stopped
+        * forever.
+        * On the other hand we need an rmb() here to ensure the proper
+        * ordering of bit testing in the following
+        * netif_tx_queue_stopped(txq) call.
+        */
+       smp_mb();
+
+       if (unlikely(netif_tx_queue_stopped(netdev_txq))) {
+               /* Taking tx_lock is needed to prevent reenabling the queue
+                * while it's empty. This could have happen if rx_action() gets
+                * suspended in qede_tx_int() after the condition before
+                * netif_tx_wake_queue(), while tx_action (qede_start_xmit()):
+                *
+                * stops the queue->sees fresh tx_bd_cons->releases the queue->
+                * sends some packets consuming the whole queue again->
+                * stops the queue
+                */
+
+               __netif_tx_lock(netdev_txq, smp_processor_id());
+
+               if ((netif_tx_queue_stopped(netdev_txq)) &&
+                   (edev->state == QEDE_STATE_OPEN) &&
+                   (qed_chain_get_elem_left(&txq->tx_pbl)
+                     >= (MAX_SKB_FRAGS + 1))) {
+                       netif_tx_wake_queue(netdev_txq);
+                       DP_VERBOSE(edev, NETIF_MSG_TX_DONE,
+                                  "Wake queue was called\n");
+               }
+
+               __netif_tx_unlock(netdev_txq);
+       }
+
+       return 0;
+}
+
+static bool qede_has_rx_work(struct qede_rx_queue *rxq)
+{
+       u16 hw_comp_cons, sw_comp_cons;
+
+       /* Tell compiler that status block fields can change */
+       barrier();
+
+       hw_comp_cons = le16_to_cpu(*rxq->hw_cons_ptr);
+       sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring);
+
+       return hw_comp_cons != sw_comp_cons;
+}
+
+static bool qede_has_tx_work(struct qede_fastpath *fp)
+{
+       u8 tc;
+
+       for (tc = 0; tc < fp->edev->num_tc; tc++)
+               if (qede_txq_has_work(&fp->txqs[tc]))
+                       return true;
+       return false;
+}
+
+/* This function copies the Rx buffer from the CONS position to the PROD
+ * position, since we failed to allocate a new Rx buffer.
+ */
+static void qede_reuse_rx_data(struct qede_rx_queue *rxq)
+{
+       struct eth_rx_bd *rx_bd_cons = qed_chain_consume(&rxq->rx_bd_ring);
+       struct eth_rx_bd *rx_bd_prod = qed_chain_produce(&rxq->rx_bd_ring);
+       struct sw_rx_data *sw_rx_data_cons =
+               &rxq->sw_rx_ring[rxq->sw_rx_cons & NUM_RX_BDS_MAX];
+       struct sw_rx_data *sw_rx_data_prod =
+               &rxq->sw_rx_ring[rxq->sw_rx_prod & NUM_RX_BDS_MAX];
+
+       dma_unmap_addr_set(sw_rx_data_prod, mapping,
+                          dma_unmap_addr(sw_rx_data_cons, mapping));
+
+       sw_rx_data_prod->data = sw_rx_data_cons->data;
+       memcpy(rx_bd_prod, rx_bd_cons, sizeof(struct eth_rx_bd));
+
+       rxq->sw_rx_cons++;
+       rxq->sw_rx_prod++;
+}
+
+static inline void qede_update_rx_prod(struct qede_dev *edev,
+                                      struct qede_rx_queue *rxq)
+{
+       u16 bd_prod = qed_chain_get_prod_idx(&rxq->rx_bd_ring);
+       u16 cqe_prod = qed_chain_get_prod_idx(&rxq->rx_comp_ring);
+       struct eth_rx_prod_data rx_prods = {0};
+
+       /* Update producers */
+       rx_prods.bd_prod = cpu_to_le16(bd_prod);
+       rx_prods.cqe_prod = cpu_to_le16(cqe_prod);
+
+       /* Make sure that the BD and SGE data is updated before updating the
+        * producers since FW might read the BD/SGE right after the producer
+        * is updated.
+        */
+       wmb();
+
+       internal_ram_wr(rxq->hw_rxq_prod_addr, sizeof(rx_prods),
+                       (u32 *)&rx_prods);
+
+       /* mmiowb is needed to synchronize doorbell writes from more than one
+        * processor. It guarantees that the write arrives to the device before
+        * the napi lock is released and another qede_poll is called (possibly
+        * on another CPU). Without this barrier, the next doorbell can bypass
+        * this doorbell. This is applicable to IA64/Altix systems.
+        */
+       mmiowb();
+}
+
+static u32 qede_get_rxhash(struct qede_dev *edev,
+                          u8 bitfields,
+                          __le32 rss_hash,
+                          enum pkt_hash_types *rxhash_type)
+{
+       enum rss_hash_type htype;
+
+       htype = GET_FIELD(bitfields, ETH_FAST_PATH_RX_REG_CQE_RSS_HASH_TYPE);
+
+       if ((edev->ndev->features & NETIF_F_RXHASH) && htype) {
+               *rxhash_type = ((htype == RSS_HASH_TYPE_IPV4) ||
+                               (htype == RSS_HASH_TYPE_IPV6)) ?
+                               PKT_HASH_TYPE_L3 : PKT_HASH_TYPE_L4;
+               return le32_to_cpu(rss_hash);
+       }
+       *rxhash_type = PKT_HASH_TYPE_NONE;
+       return 0;
+}
+
+static void qede_set_skb_csum(struct sk_buff *skb, u8 csum_flag)
+{
+       skb_checksum_none_assert(skb);
+
+       if (csum_flag & QEDE_CSUM_UNNECESSARY)
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+static inline void qede_skb_receive(struct qede_dev *edev,
+                                   struct qede_fastpath *fp,
+                                   struct sk_buff *skb,
+                                   u16 vlan_tag)
+{
+       if (vlan_tag)
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+                                      vlan_tag);
+
+       napi_gro_receive(&fp->napi, skb);
+}
+
+static u8 qede_check_csum(u16 flag)
+{
+       u16 csum_flag = 0;
+       u8 csum = 0;
+
+       if ((PARSING_AND_ERR_FLAGS_L4CHKSMWASCALCULATED_MASK <<
+            PARSING_AND_ERR_FLAGS_L4CHKSMWASCALCULATED_SHIFT) & flag) {
+               csum_flag |= PARSING_AND_ERR_FLAGS_L4CHKSMERROR_MASK <<
+                            PARSING_AND_ERR_FLAGS_L4CHKSMERROR_SHIFT;
+               csum = QEDE_CSUM_UNNECESSARY;
+       }
+
+       csum_flag |= PARSING_AND_ERR_FLAGS_IPHDRERROR_MASK <<
+                    PARSING_AND_ERR_FLAGS_IPHDRERROR_SHIFT;
+
+       if (csum_flag & flag)
+               return QEDE_CSUM_ERROR;
+
+       return csum;
+}
+
+static int qede_rx_int(struct qede_fastpath *fp, int budget)
+{
+       struct qede_dev *edev = fp->edev;
+       struct qede_rx_queue *rxq = fp->rxq;
+
+       u16 hw_comp_cons, sw_comp_cons, sw_rx_index, parse_flag;
+       int rx_pkt = 0;
+       u8 csum_flag;
+
+       hw_comp_cons = le16_to_cpu(*rxq->hw_cons_ptr);
+       sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring);
+
+       /* Memory barrier to prevent the CPU from doing speculative reads of CQE
+        * / BD in the while-loop before reading hw_comp_cons. If the CQE is
+        * read before it is written by FW, then FW writes CQE and SB, and then
+        * the CPU reads the hw_comp_cons, it will use an old CQE.
+        */
+       rmb();
+
+       /* Loop to complete all indicated BDs */
+       while (sw_comp_cons != hw_comp_cons) {
+               struct eth_fast_path_rx_reg_cqe *fp_cqe;
+               enum pkt_hash_types rxhash_type;
+               enum eth_rx_cqe_type cqe_type;
+               struct sw_rx_data *sw_rx_data;
+               union eth_rx_cqe *cqe;
+               struct sk_buff *skb;
+               u16 len, pad;
+               u32 rx_hash;
+               u8 *data;
+
+               /* Get the CQE from the completion ring */
+               cqe = (union eth_rx_cqe *)
+                       qed_chain_consume(&rxq->rx_comp_ring);
+               cqe_type = cqe->fast_path_regular.type;
+
+               if (unlikely(cqe_type == ETH_RX_CQE_TYPE_SLOW_PATH)) {
+                       edev->ops->eth_cqe_completion(
+                                       edev->cdev, fp->rss_id,
+                                       (struct eth_slow_path_rx_cqe *)cqe);
+                       goto next_cqe;
+               }
+
+               /* Get the data from the SW ring */
+               sw_rx_index = rxq->sw_rx_cons & NUM_RX_BDS_MAX;
+               sw_rx_data = &rxq->sw_rx_ring[sw_rx_index];
+               data = sw_rx_data->data;
+
+               fp_cqe = &cqe->fast_path_regular;
+               len =  le16_to_cpu(fp_cqe->pkt_len);
+               pad = fp_cqe->placement_offset;
+
+               /* For every Rx BD consumed, we allocate a new BD so the BD ring
+                * is always with a fixed size. If allocation fails, we take the
+                * consumed BD and return it to the ring in the PROD position.
+                * The packet that was received on that BD will be dropped (and
+                * not passed to the upper stack).
+                */
+               if (likely(qede_alloc_rx_buffer(edev, rxq) == 0)) {
+                       dma_unmap_single(&edev->pdev->dev,
+                                        dma_unmap_addr(sw_rx_data, mapping),
+                                        rxq->rx_buf_size, DMA_FROM_DEVICE);
+
+                       /* If this is an error packet then drop it */
+                       parse_flag =
+                       le16_to_cpu(cqe->fast_path_regular.pars_flags.flags);
+                       csum_flag = qede_check_csum(parse_flag);
+                       if (csum_flag == QEDE_CSUM_ERROR) {
+                               DP_NOTICE(edev,
+                                         "CQE in CONS = %u has error, flags = %x, dropping incoming packet\n",
+                                         sw_comp_cons, parse_flag);
+                               rxq->rx_hw_errors++;
+                               kfree(data);
+                               goto next_rx;
+                       }
+
+                       skb = build_skb(data, 0);
+
+                       if (unlikely(!skb)) {
+                               DP_NOTICE(edev,
+                                         "Build_skb failed, dropping incoming packet\n");
+                               kfree(data);
+                               rxq->rx_alloc_errors++;
+                               goto next_rx;
+                       }
+
+                       skb_reserve(skb, pad);
+
+               } else {
+                       DP_NOTICE(edev,
+                                 "New buffer allocation failed, dropping incoming packet and reusing its buffer\n");
+                       qede_reuse_rx_data(rxq);
+                       rxq->rx_alloc_errors++;
+                       goto next_cqe;
+               }
+
+               sw_rx_data->data = NULL;
+
+               skb_put(skb, len);
+
+               skb->protocol = eth_type_trans(skb, edev->ndev);
+
+               rx_hash = qede_get_rxhash(edev, fp_cqe->bitfields,
+                                         fp_cqe->rss_hash,
+                                         &rxhash_type);
+
+               skb_set_hash(skb, rx_hash, rxhash_type);
+
+               qede_set_skb_csum(skb, csum_flag);
+
+               skb_record_rx_queue(skb, fp->rss_id);
+
+               qede_skb_receive(edev, fp, skb, le16_to_cpu(fp_cqe->vlan_tag));
+
+               qed_chain_consume(&rxq->rx_bd_ring);
+
+next_rx:
+               rxq->sw_rx_cons++;
+               rx_pkt++;
+
+next_cqe: /* don't consume bd rx buffer */
+               qed_chain_recycle_consumed(&rxq->rx_comp_ring);
+               sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring);
+               /* CR TPA - revisit how to handle budget in TPA perhaps
+                * increase on "end"
+                */
+               if (rx_pkt == budget)
+                       break;
+       } /* repeat while sw_comp_cons != hw_comp_cons... */
+
+       /* Update producers */
+       qede_update_rx_prod(edev, rxq);
+
+       return rx_pkt;
+}
+
+static int qede_poll(struct napi_struct *napi, int budget)
+{
+       int work_done = 0;
+       struct qede_fastpath *fp = container_of(napi, struct qede_fastpath,
+                                                napi);
+       struct qede_dev *edev = fp->edev;
+
+       while (1) {
+               u8 tc;
+
+               for (tc = 0; tc < edev->num_tc; tc++)
+                       if (qede_txq_has_work(&fp->txqs[tc]))
+                               qede_tx_int(edev, &fp->txqs[tc]);
+
+               if (qede_has_rx_work(fp->rxq)) {
+                       work_done += qede_rx_int(fp, budget - work_done);
+
+                       /* must not complete if we consumed full budget */
+                       if (work_done >= budget)
+                               break;
+               }
+
+               /* Fall out from the NAPI loop if needed */
+               if (!(qede_has_rx_work(fp->rxq) || qede_has_tx_work(fp))) {
+                       qed_sb_update_sb_idx(fp->sb_info);
+                       /* *_has_*_work() reads the status block,
+                        * thus we need to ensure that status block indices
+                        * have been actually read (qed_sb_update_sb_idx)
+                        * prior to this check (*_has_*_work) so that
+                        * we won't write the "newer" value of the status block
+                        * to HW (if there was a DMA right after
+                        * qede_has_rx_work and if there is no rmb, the memory
+                        * reading (qed_sb_update_sb_idx) may be postponed
+                        * to right before *_ack_sb). In this case there
+                        * will never be another interrupt until there is
+                        * another update of the status block, while there
+                        * is still unhandled work.
+                        */
+                       rmb();
+
+                       if (!(qede_has_rx_work(fp->rxq) ||
+                             qede_has_tx_work(fp))) {
+                               napi_complete(napi);
+                               /* Update and reenable interrupts */
+                               qed_sb_ack(fp->sb_info, IGU_INT_ENABLE,
+                                          1 /*update*/);
+                               break;
+                       }
+               }
+       }
+
+       return work_done;
+}
+
+static irqreturn_t qede_msix_fp_int(int irq, void *fp_cookie)
+{
+       struct qede_fastpath *fp = fp_cookie;
+
+       qed_sb_ack(fp->sb_info, IGU_INT_DISABLE, 0 /*do not update*/);
+
+       napi_schedule_irqoff(&fp->napi);
+       return IRQ_HANDLED;
+}
+
+/* -------------------------------------------------------------------------
+ * END OF FAST-PATH
+ * -------------------------------------------------------------------------
+ */
+
+static int qede_open(struct net_device *ndev);
+static int qede_close(struct net_device *ndev);
+static int qede_set_mac_addr(struct net_device *ndev, void *p);
+static void qede_set_rx_mode(struct net_device *ndev);
+static void qede_config_rx_mode(struct net_device *ndev);
+
+static int qede_set_ucast_rx_mac(struct qede_dev *edev,
+                                enum qed_filter_xcast_params_type opcode,
+                                unsigned char mac[ETH_ALEN])
+{
+       struct qed_filter_params filter_cmd;
+
+       memset(&filter_cmd, 0, sizeof(filter_cmd));
+       filter_cmd.type = QED_FILTER_TYPE_UCAST;
+       filter_cmd.filter.ucast.type = opcode;
+       filter_cmd.filter.ucast.mac_valid = 1;
+       ether_addr_copy(filter_cmd.filter.ucast.mac, mac);
+
+       return edev->ops->filter_config(edev->cdev, &filter_cmd);
+}
+
+void qede_fill_by_demand_stats(struct qede_dev *edev)
+{
+       struct qed_eth_stats stats;
+
+       edev->ops->get_vport_stats(edev->cdev, &stats);
+       edev->stats.no_buff_discards = stats.no_buff_discards;
+       edev->stats.rx_ucast_bytes = stats.rx_ucast_bytes;
+       edev->stats.rx_mcast_bytes = stats.rx_mcast_bytes;
+       edev->stats.rx_bcast_bytes = stats.rx_bcast_bytes;
+       edev->stats.rx_ucast_pkts = stats.rx_ucast_pkts;
+       edev->stats.rx_mcast_pkts = stats.rx_mcast_pkts;
+       edev->stats.rx_bcast_pkts = stats.rx_bcast_pkts;
+       edev->stats.mftag_filter_discards = stats.mftag_filter_discards;
+       edev->stats.mac_filter_discards = stats.mac_filter_discards;
+
+       edev->stats.tx_ucast_bytes = stats.tx_ucast_bytes;
+       edev->stats.tx_mcast_bytes = stats.tx_mcast_bytes;
+       edev->stats.tx_bcast_bytes = stats.tx_bcast_bytes;
+       edev->stats.tx_ucast_pkts = stats.tx_ucast_pkts;
+       edev->stats.tx_mcast_pkts = stats.tx_mcast_pkts;
+       edev->stats.tx_bcast_pkts = stats.tx_bcast_pkts;
+       edev->stats.tx_err_drop_pkts = stats.tx_err_drop_pkts;
+       edev->stats.coalesced_pkts = stats.tpa_coalesced_pkts;
+       edev->stats.coalesced_events = stats.tpa_coalesced_events;
+       edev->stats.coalesced_aborts_num = stats.tpa_aborts_num;
+       edev->stats.non_coalesced_pkts = stats.tpa_not_coalesced_pkts;
+       edev->stats.coalesced_bytes = stats.tpa_coalesced_bytes;
+
+       edev->stats.rx_64_byte_packets = stats.rx_64_byte_packets;
+       edev->stats.rx_127_byte_packets = stats.rx_127_byte_packets;
+       edev->stats.rx_255_byte_packets = stats.rx_255_byte_packets;
+       edev->stats.rx_511_byte_packets = stats.rx_511_byte_packets;
+       edev->stats.rx_1023_byte_packets = stats.rx_1023_byte_packets;
+       edev->stats.rx_1518_byte_packets = stats.rx_1518_byte_packets;
+       edev->stats.rx_1522_byte_packets = stats.rx_1522_byte_packets;
+       edev->stats.rx_2047_byte_packets = stats.rx_2047_byte_packets;
+       edev->stats.rx_4095_byte_packets = stats.rx_4095_byte_packets;
+       edev->stats.rx_9216_byte_packets = stats.rx_9216_byte_packets;
+       edev->stats.rx_16383_byte_packets = stats.rx_16383_byte_packets;
+       edev->stats.rx_crc_errors = stats.rx_crc_errors;
+       edev->stats.rx_mac_crtl_frames = stats.rx_mac_crtl_frames;
+       edev->stats.rx_pause_frames = stats.rx_pause_frames;
+       edev->stats.rx_pfc_frames = stats.rx_pfc_frames;
+       edev->stats.rx_align_errors = stats.rx_align_errors;
+       edev->stats.rx_carrier_errors = stats.rx_carrier_errors;
+       edev->stats.rx_oversize_packets = stats.rx_oversize_packets;
+       edev->stats.rx_jabbers = stats.rx_jabbers;
+       edev->stats.rx_undersize_packets = stats.rx_undersize_packets;
+       edev->stats.rx_fragments = stats.rx_fragments;
+       edev->stats.tx_64_byte_packets = stats.tx_64_byte_packets;
+       edev->stats.tx_65_to_127_byte_packets = stats.tx_65_to_127_byte_packets;
+       edev->stats.tx_128_to_255_byte_packets =
+                               stats.tx_128_to_255_byte_packets;
+       edev->stats.tx_256_to_511_byte_packets =
+                               stats.tx_256_to_511_byte_packets;
+       edev->stats.tx_512_to_1023_byte_packets =
+                               stats.tx_512_to_1023_byte_packets;
+       edev->stats.tx_1024_to_1518_byte_packets =
+                               stats.tx_1024_to_1518_byte_packets;
+       edev->stats.tx_1519_to_2047_byte_packets =
+                               stats.tx_1519_to_2047_byte_packets;
+       edev->stats.tx_2048_to_4095_byte_packets =
+                               stats.tx_2048_to_4095_byte_packets;
+       edev->stats.tx_4096_to_9216_byte_packets =
+                               stats.tx_4096_to_9216_byte_packets;
+       edev->stats.tx_9217_to_16383_byte_packets =
+                               stats.tx_9217_to_16383_byte_packets;
+       edev->stats.tx_pause_frames = stats.tx_pause_frames;
+       edev->stats.tx_pfc_frames = stats.tx_pfc_frames;
+       edev->stats.tx_lpi_entry_count = stats.tx_lpi_entry_count;
+       edev->stats.tx_total_collisions = stats.tx_total_collisions;
+       edev->stats.brb_truncates = stats.brb_truncates;
+       edev->stats.brb_discards = stats.brb_discards;
+       edev->stats.tx_mac_ctrl_frames = stats.tx_mac_ctrl_frames;
+}
+
+static struct rtnl_link_stats64 *qede_get_stats64(
+                           struct net_device *dev,
+                           struct rtnl_link_stats64 *stats)
+{
+       struct qede_dev *edev = netdev_priv(dev);
+
+       qede_fill_by_demand_stats(edev);
+
+       stats->rx_packets = edev->stats.rx_ucast_pkts +
+                           edev->stats.rx_mcast_pkts +
+                           edev->stats.rx_bcast_pkts;
+       stats->tx_packets = edev->stats.tx_ucast_pkts +
+                           edev->stats.tx_mcast_pkts +
+                           edev->stats.tx_bcast_pkts;
+
+       stats->rx_bytes = edev->stats.rx_ucast_bytes +
+                         edev->stats.rx_mcast_bytes +
+                         edev->stats.rx_bcast_bytes;
+
+       stats->tx_bytes = edev->stats.tx_ucast_bytes +
+                         edev->stats.tx_mcast_bytes +
+                         edev->stats.tx_bcast_bytes;
+
+       stats->tx_errors = edev->stats.tx_err_drop_pkts;
+       stats->multicast = edev->stats.rx_mcast_pkts +
+                          edev->stats.rx_bcast_pkts;
+
+       stats->rx_fifo_errors = edev->stats.no_buff_discards;
+
+       stats->collisions = edev->stats.tx_total_collisions;
+       stats->rx_crc_errors = edev->stats.rx_crc_errors;
+       stats->rx_frame_errors = edev->stats.rx_align_errors;
+
+       return stats;
+}
+
+static const struct net_device_ops qede_netdev_ops = {
+       .ndo_open = qede_open,
+       .ndo_stop = qede_close,
+       .ndo_start_xmit = qede_start_xmit,
+       .ndo_set_rx_mode = qede_set_rx_mode,
+       .ndo_set_mac_address = qede_set_mac_addr,
+       .ndo_validate_addr = eth_validate_addr,
+       .ndo_change_mtu = qede_change_mtu,
+       .ndo_get_stats64 = qede_get_stats64,
+};
+
+/* -------------------------------------------------------------------------
+ * START OF PROBE / REMOVE
+ * -------------------------------------------------------------------------
+ */
+
+static struct qede_dev *qede_alloc_etherdev(struct qed_dev *cdev,
+                                           struct pci_dev *pdev,
+                                           struct qed_dev_eth_info *info,
+                                           u32 dp_module,
+                                           u8 dp_level)
+{
+       struct net_device *ndev;
+       struct qede_dev *edev;
+
+       ndev = alloc_etherdev_mqs(sizeof(*edev),
+                                 info->num_queues,
+                                 info->num_queues);
+       if (!ndev) {
+               pr_err("etherdev allocation failed\n");
+               return NULL;
+       }
+
+       edev = netdev_priv(ndev);
+       edev->ndev = ndev;
+       edev->cdev = cdev;
+       edev->pdev = pdev;
+       edev->dp_module = dp_module;
+       edev->dp_level = dp_level;
+       edev->ops = qed_ops;
+       edev->q_num_rx_buffers = NUM_RX_BDS_DEF;
+       edev->q_num_tx_buffers = NUM_TX_BDS_DEF;
+
+       DP_INFO(edev, "Allocated netdev with 64 tx queues and 64 rx queues\n");
+
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       memset(&edev->stats, 0, sizeof(edev->stats));
+       memcpy(&edev->dev_info, info, sizeof(*info));
+
+       edev->num_tc = edev->dev_info.num_tc;
+
+       return edev;
+}
+
+static void qede_init_ndev(struct qede_dev *edev)
+{
+       struct net_device *ndev = edev->ndev;
+       struct pci_dev *pdev = edev->pdev;
+       u32 hw_features;
+
+       pci_set_drvdata(pdev, ndev);
+
+       ndev->mem_start = edev->dev_info.common.pci_mem_start;
+       ndev->base_addr = ndev->mem_start;
+       ndev->mem_end = edev->dev_info.common.pci_mem_end;
+       ndev->irq = edev->dev_info.common.pci_irq;
+
+       ndev->watchdog_timeo = TX_TIMEOUT;
+
+       ndev->netdev_ops = &qede_netdev_ops;
+
+       qede_set_ethtool_ops(ndev);
+
+       /* user-changeble features */
+       hw_features = NETIF_F_GRO | NETIF_F_SG |
+                     NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+                     NETIF_F_TSO | NETIF_F_TSO6;
+
+       ndev->vlan_features = hw_features | NETIF_F_RXHASH | NETIF_F_RXCSUM |
+                             NETIF_F_HIGHDMA;
+       ndev->features = hw_features | NETIF_F_RXHASH | NETIF_F_RXCSUM |
+                        NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HIGHDMA |
+                        NETIF_F_HW_VLAN_CTAG_TX;
+
+       ndev->hw_features = hw_features;
+
+       /* Set network device HW mac */
+       ether_addr_copy(edev->ndev->dev_addr, edev->dev_info.common.hw_mac);
+}
+
+/* This function converts from 32b param to two params of level and module
+ * Input 32b decoding:
+ * b31 - enable all NOTICE prints. NOTICE prints are for deviation from the
+ * 'happy' flow, e.g. memory allocation failed.
+ * b30 - enable all INFO prints. INFO prints are for major steps in the flow
+ * and provide important parameters.
+ * b29-b0 - per-module bitmap, where each bit enables VERBOSE prints of that
+ * module. VERBOSE prints are for tracking the specific flow in low level.
+ *
+ * Notice that the level should be that of the lowest required logs.
+ */
+void qede_config_debug(uint debug, u32 *p_dp_module, u8 *p_dp_level)
+{
+       *p_dp_level = QED_LEVEL_NOTICE;
+       *p_dp_module = 0;
+
+       if (debug & QED_LOG_VERBOSE_MASK) {
+               *p_dp_level = QED_LEVEL_VERBOSE;
+               *p_dp_module = (debug & 0x3FFFFFFF);
+       } else if (debug & QED_LOG_INFO_MASK) {
+               *p_dp_level = QED_LEVEL_INFO;
+       } else if (debug & QED_LOG_NOTICE_MASK) {
+               *p_dp_level = QED_LEVEL_NOTICE;
+       }
+}
+
+static void qede_free_fp_array(struct qede_dev *edev)
+{
+       if (edev->fp_array) {
+               struct qede_fastpath *fp;
+               int i;
+
+               for_each_rss(i) {
+                       fp = &edev->fp_array[i];
+
+                       kfree(fp->sb_info);
+                       kfree(fp->rxq);
+                       kfree(fp->txqs);
+               }
+               kfree(edev->fp_array);
+       }
+       edev->num_rss = 0;
+}
+
+static int qede_alloc_fp_array(struct qede_dev *edev)
+{
+       struct qede_fastpath *fp;
+       int i;
+
+       edev->fp_array = kcalloc(QEDE_RSS_CNT(edev),
+                                sizeof(*edev->fp_array), GFP_KERNEL);
+       if (!edev->fp_array) {
+               DP_NOTICE(edev, "fp array allocation failed\n");
+               goto err;
+       }
+
+       for_each_rss(i) {
+               fp = &edev->fp_array[i];
+
+               fp->sb_info = kcalloc(1, sizeof(*fp->sb_info), GFP_KERNEL);
+               if (!fp->sb_info) {
+                       DP_NOTICE(edev, "sb info struct allocation failed\n");
+                       goto err;
+               }
+
+               fp->rxq = kcalloc(1, sizeof(*fp->rxq), GFP_KERNEL);
+               if (!fp->rxq) {
+                       DP_NOTICE(edev, "RXQ struct allocation failed\n");
+                       goto err;
+               }
+
+               fp->txqs = kcalloc(edev->num_tc, sizeof(*fp->txqs), GFP_KERNEL);
+               if (!fp->txqs) {
+                       DP_NOTICE(edev, "TXQ array allocation failed\n");
+                       goto err;
+               }
+       }
+
+       return 0;
+err:
+       qede_free_fp_array(edev);
+       return -ENOMEM;
+}
+
+static void qede_sp_task(struct work_struct *work)
+{
+       struct qede_dev *edev = container_of(work, struct qede_dev,
+                                            sp_task.work);
+       mutex_lock(&edev->qede_lock);
+
+       if (edev->state == QEDE_STATE_OPEN) {
+               if (test_and_clear_bit(QEDE_SP_RX_MODE, &edev->sp_flags))
+                       qede_config_rx_mode(edev->ndev);
+       }
+
+       mutex_unlock(&edev->qede_lock);
+}
+
+static void qede_update_pf_params(struct qed_dev *cdev)
+{
+       struct qed_pf_params pf_params;
+
+       /* 16 rx + 16 tx */
+       memset(&pf_params, 0, sizeof(struct qed_pf_params));
+       pf_params.eth_pf_params.num_cons = 32;
+       qed_ops->common->update_pf_params(cdev, &pf_params);
+}
+
+enum qede_probe_mode {
+       QEDE_PROBE_NORMAL,
+};
+
+static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level,
+                       enum qede_probe_mode mode)
+{
+       struct qed_slowpath_params params;
+       struct qed_dev_eth_info dev_info;
+       struct qede_dev *edev;
+       struct qed_dev *cdev;
+       int rc;
+
+       if (unlikely(dp_level & QED_LEVEL_INFO))
+               pr_notice("Starting qede probe\n");
+
+       cdev = qed_ops->common->probe(pdev, QED_PROTOCOL_ETH,
+                                     dp_module, dp_level);
+       if (!cdev) {
+               rc = -ENODEV;
+               goto err0;
+       }
+
+       qede_update_pf_params(cdev);
+
+       /* Start the Slowpath-process */
+       memset(&params, 0, sizeof(struct qed_slowpath_params));
+       params.int_mode = QED_INT_MODE_MSIX;
+       params.drv_major = QEDE_MAJOR_VERSION;
+       params.drv_minor = QEDE_MINOR_VERSION;
+       params.drv_rev = QEDE_REVISION_VERSION;
+       params.drv_eng = QEDE_ENGINEERING_VERSION;
+       strlcpy(params.name, "qede LAN", QED_DRV_VER_STR_SIZE);
+       rc = qed_ops->common->slowpath_start(cdev, &params);
+       if (rc) {
+               pr_notice("Cannot start slowpath\n");
+               goto err1;
+       }
+
+       /* Learn information crucial for qede to progress */
+       rc = qed_ops->fill_dev_info(cdev, &dev_info);
+       if (rc)
+               goto err2;
+
+       edev = qede_alloc_etherdev(cdev, pdev, &dev_info, dp_module,
+                                  dp_level);
+       if (!edev) {
+               rc = -ENOMEM;
+               goto err2;
+       }
+
+       qede_init_ndev(edev);
+
+       rc = register_netdev(edev->ndev);
+       if (rc) {
+               DP_NOTICE(edev, "Cannot register net-device\n");
+               goto err3;
+       }
+
+       edev->ops->common->set_id(cdev, edev->ndev->name, DRV_MODULE_VERSION);
+
+       edev->ops->register_ops(cdev, &qede_ll_ops, edev);
+
+       INIT_DELAYED_WORK(&edev->sp_task, qede_sp_task);
+       mutex_init(&edev->qede_lock);
+
+       DP_INFO(edev, "Ending successfully qede probe\n");
+
+       return 0;
+
+err3:
+       free_netdev(edev->ndev);
+err2:
+       qed_ops->common->slowpath_stop(cdev);
+err1:
+       qed_ops->common->remove(cdev);
+err0:
+       return rc;
+}
+
+static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+       u32 dp_module = 0;
+       u8 dp_level = 0;
+
+       qede_config_debug(debug, &dp_module, &dp_level);
+
+       return __qede_probe(pdev, dp_module, dp_level,
+                           QEDE_PROBE_NORMAL);
+}
+
+enum qede_remove_mode {
+       QEDE_REMOVE_NORMAL,
+};
+
+static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode)
+{
+       struct net_device *ndev = pci_get_drvdata(pdev);
+       struct qede_dev *edev = netdev_priv(ndev);
+       struct qed_dev *cdev = edev->cdev;
+
+       DP_INFO(edev, "Starting qede_remove\n");
+
+       cancel_delayed_work_sync(&edev->sp_task);
+       unregister_netdev(ndev);
+
+       edev->ops->common->set_power_state(cdev, PCI_D0);
+
+       pci_set_drvdata(pdev, NULL);
+
+       free_netdev(ndev);
+
+       /* Use global ops since we've freed edev */
+       qed_ops->common->slowpath_stop(cdev);
+       qed_ops->common->remove(cdev);
+
+       pr_notice("Ending successfully qede_remove\n");
+}
+
+static void qede_remove(struct pci_dev *pdev)
+{
+       __qede_remove(pdev, QEDE_REMOVE_NORMAL);
+}
+
+/* -------------------------------------------------------------------------
+ * START OF LOAD / UNLOAD
+ * -------------------------------------------------------------------------
+ */
+
+static int qede_set_num_queues(struct qede_dev *edev)
+{
+       int rc;
+       u16 rss_num;
+
+       /* Setup queues according to possible resources*/
+       rss_num = netif_get_num_default_rss_queues() *
+                 edev->dev_info.common.num_hwfns;
+
+       rss_num = min_t(u16, QEDE_MAX_RSS_CNT(edev), rss_num);
+
+       rc = edev->ops->common->set_fp_int(edev->cdev, rss_num);
+       if (rc > 0) {
+               /* Managed to request interrupts for our queues */
+               edev->num_rss = rc;
+               DP_INFO(edev, "Managed %d [of %d] RSS queues\n",
+                       QEDE_RSS_CNT(edev), rss_num);
+               rc = 0;
+       }
+       return rc;
+}
+
+static void qede_free_mem_sb(struct qede_dev *edev,
+                            struct qed_sb_info *sb_info)
+{
+       if (sb_info->sb_virt)
+               dma_free_coherent(&edev->pdev->dev, sizeof(*sb_info->sb_virt),
+                                 (void *)sb_info->sb_virt, sb_info->sb_phys);
+}
+
+/* This function allocates fast-path status block memory */
+static int qede_alloc_mem_sb(struct qede_dev *edev,
+                            struct qed_sb_info *sb_info,
+                            u16 sb_id)
+{
+       struct status_block *sb_virt;
+       dma_addr_t sb_phys;
+       int rc;
+
+       sb_virt = dma_alloc_coherent(&edev->pdev->dev,
+                                    sizeof(*sb_virt),
+                                    &sb_phys, GFP_KERNEL);
+       if (!sb_virt) {
+               DP_ERR(edev, "Status block allocation failed\n");
+               return -ENOMEM;
+       }
+
+       rc = edev->ops->common->sb_init(edev->cdev, sb_info,
+                                       sb_virt, sb_phys, sb_id,
+                                       QED_SB_TYPE_L2_QUEUE);
+       if (rc) {
+               DP_ERR(edev, "Status block initialization failed\n");
+               dma_free_coherent(&edev->pdev->dev, sizeof(*sb_virt),
+                                 sb_virt, sb_phys);
+               return rc;
+       }
+
+       return 0;
+}
+
+static void qede_free_rx_buffers(struct qede_dev *edev,
+                                struct qede_rx_queue *rxq)
+{
+       u16 i;
+
+       for (i = rxq->sw_rx_cons; i != rxq->sw_rx_prod; i++) {
+               struct sw_rx_data *rx_buf;
+               u8 *data;
+
+               rx_buf = &rxq->sw_rx_ring[i & NUM_RX_BDS_MAX];
+               data = rx_buf->data;
+
+               dma_unmap_single(&edev->pdev->dev,
+                                dma_unmap_addr(rx_buf, mapping),
+                                rxq->rx_buf_size, DMA_FROM_DEVICE);
+
+               rx_buf->data = NULL;
+               kfree(data);
+       }
+}
+
+static void qede_free_mem_rxq(struct qede_dev *edev,
+                             struct qede_rx_queue *rxq)
+{
+       /* Free rx buffers */
+       qede_free_rx_buffers(edev, rxq);
+
+       /* Free the parallel SW ring */
+       kfree(rxq->sw_rx_ring);
+
+       /* Free the real RQ ring used by FW */
+       edev->ops->common->chain_free(edev->cdev, &rxq->rx_bd_ring);
+       edev->ops->common->chain_free(edev->cdev, &rxq->rx_comp_ring);
+}
+
+static int qede_alloc_rx_buffer(struct qede_dev *edev,
+                               struct qede_rx_queue *rxq)
+{
+       struct sw_rx_data *sw_rx_data;
+       struct eth_rx_bd *rx_bd;
+       dma_addr_t mapping;
+       u16 rx_buf_size;
+       u8 *data;
+
+       rx_buf_size = rxq->rx_buf_size;
+
+       data = kmalloc(rx_buf_size, GFP_ATOMIC);
+       if (unlikely(!data)) {
+               DP_NOTICE(edev, "Failed to allocate Rx data\n");
+               return -ENOMEM;
+       }
+
+       mapping = dma_map_single(&edev->pdev->dev, data,
+                                rx_buf_size, DMA_FROM_DEVICE);
+       if (unlikely(dma_mapping_error(&edev->pdev->dev, mapping))) {
+               kfree(data);
+               DP_NOTICE(edev, "Failed to map Rx buffer\n");
+               return -ENOMEM;
+       }
+
+       sw_rx_data = &rxq->sw_rx_ring[rxq->sw_rx_prod & NUM_RX_BDS_MAX];
+       sw_rx_data->data = data;
+
+       dma_unmap_addr_set(sw_rx_data, mapping, mapping);
+
+       /* Advance PROD and get BD pointer */
+       rx_bd = (struct eth_rx_bd *)qed_chain_produce(&rxq->rx_bd_ring);
+       WARN_ON(!rx_bd);
+       rx_bd->addr.hi = cpu_to_le32(upper_32_bits(mapping));
+       rx_bd->addr.lo = cpu_to_le32(lower_32_bits(mapping));
+
+       rxq->sw_rx_prod++;
+
+       return 0;
+}
+
+/* This function allocates all memory needed per Rx queue */
+static int qede_alloc_mem_rxq(struct qede_dev *edev,
+                             struct qede_rx_queue *rxq)
+{
+       int i, rc, size, num_allocated;
+
+       rxq->num_rx_buffers = edev->q_num_rx_buffers;
+
+       rxq->rx_buf_size = NET_IP_ALIGN +
+                          ETH_OVERHEAD +
+                          edev->ndev->mtu +
+                          QEDE_FW_RX_ALIGN_END;
+
+       /* Allocate the parallel driver ring for Rx buffers */
+       size = sizeof(*rxq->sw_rx_ring) * NUM_RX_BDS_MAX;
+       rxq->sw_rx_ring = kzalloc(size, GFP_KERNEL);
+       if (!rxq->sw_rx_ring) {
+               DP_ERR(edev, "Rx buffers ring allocation failed\n");
+               goto err;
+       }
+
+       /* Allocate FW Rx ring  */
+       rc = edev->ops->common->chain_alloc(edev->cdev,
+                                           QED_CHAIN_USE_TO_CONSUME_PRODUCE,
+                                           QED_CHAIN_MODE_NEXT_PTR,
+                                           NUM_RX_BDS_MAX,
+                                           sizeof(struct eth_rx_bd),
+                                           &rxq->rx_bd_ring);
+
+       if (rc)
+               goto err;
+
+       /* Allocate FW completion ring */
+       rc = edev->ops->common->chain_alloc(edev->cdev,
+                                           QED_CHAIN_USE_TO_CONSUME,
+                                           QED_CHAIN_MODE_PBL,
+                                           NUM_RX_BDS_MAX,
+                                           sizeof(union eth_rx_cqe),
+                                           &rxq->rx_comp_ring);
+       if (rc)
+               goto err;
+
+       /* Allocate buffers for the Rx ring */
+       for (i = 0; i < rxq->num_rx_buffers; i++) {
+               rc = qede_alloc_rx_buffer(edev, rxq);
+               if (rc)
+                       break;
+       }
+       num_allocated = i;
+       if (!num_allocated) {
+               DP_ERR(edev, "Rx buffers allocation failed\n");
+               goto err;
+       } else if (num_allocated < rxq->num_rx_buffers) {
+               DP_NOTICE(edev,
+                         "Allocated less buffers than desired (%d allocated)\n",
+                         num_allocated);
+       }
+
+       return 0;
+
+err:
+       qede_free_mem_rxq(edev, rxq);
+       return -ENOMEM;
+}
+
+static void qede_free_mem_txq(struct qede_dev *edev,
+                             struct qede_tx_queue *txq)
+{
+       /* Free the parallel SW ring */
+       kfree(txq->sw_tx_ring);
+
+       /* Free the real RQ ring used by FW */
+       edev->ops->common->chain_free(edev->cdev, &txq->tx_pbl);
+}
+
+/* This function allocates all memory needed per Tx queue */
+static int qede_alloc_mem_txq(struct qede_dev *edev,
+                             struct qede_tx_queue *txq)
+{
+       int size, rc;
+       union eth_tx_bd_types *p_virt;
+
+       txq->num_tx_buffers = edev->q_num_tx_buffers;
+
+       /* Allocate the parallel driver ring for Tx buffers */
+       size = sizeof(*txq->sw_tx_ring) * NUM_TX_BDS_MAX;
+       txq->sw_tx_ring = kzalloc(size, GFP_KERNEL);
+       if (!txq->sw_tx_ring) {
+               DP_NOTICE(edev, "Tx buffers ring allocation failed\n");
+               goto err;
+       }
+
+       rc = edev->ops->common->chain_alloc(edev->cdev,
+                                           QED_CHAIN_USE_TO_CONSUME_PRODUCE,
+                                           QED_CHAIN_MODE_PBL,
+                                           NUM_TX_BDS_MAX,
+                                           sizeof(*p_virt),
+                                           &txq->tx_pbl);
+       if (rc)
+               goto err;
+
+       return 0;
+
+err:
+       qede_free_mem_txq(edev, txq);
+       return -ENOMEM;
+}
+
+/* This function frees all memory of a single fp */
+static void qede_free_mem_fp(struct qede_dev *edev,
+                            struct qede_fastpath *fp)
+{
+       int tc;
+
+       qede_free_mem_sb(edev, fp->sb_info);
+
+       qede_free_mem_rxq(edev, fp->rxq);
+
+       for (tc = 0; tc < edev->num_tc; tc++)
+               qede_free_mem_txq(edev, &fp->txqs[tc]);
+}
+
+/* This function allocates all memory needed for a single fp (i.e. an entity
+ * which contains status block, one rx queue and multiple per-TC tx queues.
+ */
+static int qede_alloc_mem_fp(struct qede_dev *edev,
+                            struct qede_fastpath *fp)
+{
+       int rc, tc;
+
+       rc = qede_alloc_mem_sb(edev, fp->sb_info, fp->rss_id);
+       if (rc)
+               goto err;
+
+       rc = qede_alloc_mem_rxq(edev, fp->rxq);
+       if (rc)
+               goto err;
+
+       for (tc = 0; tc < edev->num_tc; tc++) {
+               rc = qede_alloc_mem_txq(edev, &fp->txqs[tc]);
+               if (rc)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       qede_free_mem_fp(edev, fp);
+       return -ENOMEM;
+}
+
+static void qede_free_mem_load(struct qede_dev *edev)
+{
+       int i;
+
+       for_each_rss(i) {
+               struct qede_fastpath *fp = &edev->fp_array[i];
+
+               qede_free_mem_fp(edev, fp);
+       }
+}
+
+/* This function allocates all qede memory at NIC load. */
+static int qede_alloc_mem_load(struct qede_dev *edev)
+{
+       int rc = 0, rss_id;
+
+       for (rss_id = 0; rss_id < QEDE_RSS_CNT(edev); rss_id++) {
+               struct qede_fastpath *fp = &edev->fp_array[rss_id];
+
+               rc = qede_alloc_mem_fp(edev, fp);
+               if (rc)
+                       break;
+       }
+
+       if (rss_id != QEDE_RSS_CNT(edev)) {
+               /* Failed allocating memory for all the queues */
+               if (!rss_id) {
+                       DP_ERR(edev,
+                              "Failed to allocate memory for the leading queue\n");
+                       rc = -ENOMEM;
+               } else {
+                       DP_NOTICE(edev,
+                                 "Failed to allocate memory for all of RSS queues\n Desired: %d queues, allocated: %d queues\n",
+                                 QEDE_RSS_CNT(edev), rss_id);
+               }
+               edev->num_rss = rss_id;
+       }
+
+       return 0;
+}
+
+/* This function inits fp content and resets the SB, RXQ and TXQ structures */
+static void qede_init_fp(struct qede_dev *edev)
+{
+       int rss_id, txq_index, tc;
+       struct qede_fastpath *fp;
+
+       for_each_rss(rss_id) {
+               fp = &edev->fp_array[rss_id];
+
+               fp->edev = edev;
+               fp->rss_id = rss_id;
+
+               memset((void *)&fp->napi, 0, sizeof(fp->napi));
+
+               memset((void *)fp->sb_info, 0, sizeof(*fp->sb_info));
+
+               memset((void *)fp->rxq, 0, sizeof(*fp->rxq));
+               fp->rxq->rxq_id = rss_id;
+
+               memset((void *)fp->txqs, 0, (edev->num_tc * sizeof(*fp->txqs)));
+               for (tc = 0; tc < edev->num_tc; tc++) {
+                       txq_index = tc * QEDE_RSS_CNT(edev) + rss_id;
+                       fp->txqs[tc].index = txq_index;
+               }
+
+               snprintf(fp->name, sizeof(fp->name), "%s-fp-%d",
+                        edev->ndev->name, rss_id);
+       }
+}
+
+static int qede_set_real_num_queues(struct qede_dev *edev)
+{
+       int rc = 0;
+
+       rc = netif_set_real_num_tx_queues(edev->ndev, QEDE_TSS_CNT(edev));
+       if (rc) {
+               DP_NOTICE(edev, "Failed to set real number of Tx queues\n");
+               return rc;
+       }
+       rc = netif_set_real_num_rx_queues(edev->ndev, QEDE_RSS_CNT(edev));
+       if (rc) {
+               DP_NOTICE(edev, "Failed to set real number of Rx queues\n");
+               return rc;
+       }
+
+       return 0;
+}
+
+static void qede_napi_disable_remove(struct qede_dev *edev)
+{
+       int i;
+
+       for_each_rss(i) {
+               napi_disable(&edev->fp_array[i].napi);
+
+               netif_napi_del(&edev->fp_array[i].napi);
+       }
+}
+
+static void qede_napi_add_enable(struct qede_dev *edev)
+{
+       int i;
+
+       /* Add NAPI objects */
+       for_each_rss(i) {
+               netif_napi_add(edev->ndev, &edev->fp_array[i].napi,
+                              qede_poll, NAPI_POLL_WEIGHT);
+               napi_enable(&edev->fp_array[i].napi);
+       }
+}
+
+static void qede_sync_free_irqs(struct qede_dev *edev)
+{
+       int i;
+
+       for (i = 0; i < edev->int_info.used_cnt; i++) {
+               if (edev->int_info.msix_cnt) {
+                       synchronize_irq(edev->int_info.msix[i].vector);
+                       free_irq(edev->int_info.msix[i].vector,
+                                &edev->fp_array[i]);
+               } else {
+                       edev->ops->common->simd_handler_clean(edev->cdev, i);
+               }
+       }
+
+       edev->int_info.used_cnt = 0;
+}
+
+static int qede_req_msix_irqs(struct qede_dev *edev)
+{
+       int i, rc;
+
+       /* Sanitize number of interrupts == number of prepared RSS queues */
+       if (QEDE_RSS_CNT(edev) > edev->int_info.msix_cnt) {
+               DP_ERR(edev,
+                      "Interrupt mismatch: %d RSS queues > %d MSI-x vectors\n",
+                      QEDE_RSS_CNT(edev), edev->int_info.msix_cnt);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < QEDE_RSS_CNT(edev); i++) {
+               rc = request_irq(edev->int_info.msix[i].vector,
+                                qede_msix_fp_int, 0, edev->fp_array[i].name,
+                                &edev->fp_array[i]);
+               if (rc) {
+                       DP_ERR(edev, "Request fp %d irq failed\n", i);
+                       qede_sync_free_irqs(edev);
+                       return rc;
+               }
+               DP_VERBOSE(edev, NETIF_MSG_INTR,
+                          "Requested fp irq for %s [entry %d]. Cookie is at %p\n",
+                          edev->fp_array[i].name, i,
+                          &edev->fp_array[i]);
+               edev->int_info.used_cnt++;
+       }
+
+       return 0;
+}
+
+static void qede_simd_fp_handler(void *cookie)
+{
+       struct qede_fastpath *fp = (struct qede_fastpath *)cookie;
+
+       napi_schedule_irqoff(&fp->napi);
+}
+
+static int qede_setup_irqs(struct qede_dev *edev)
+{
+       int i, rc = 0;
+
+       /* Learn Interrupt configuration */
+       rc = edev->ops->common->get_fp_int(edev->cdev, &edev->int_info);
+       if (rc)
+               return rc;
+
+       if (edev->int_info.msix_cnt) {
+               rc = qede_req_msix_irqs(edev);
+               if (rc)
+                       return rc;
+               edev->ndev->irq = edev->int_info.msix[0].vector;
+       } else {
+               const struct qed_common_ops *ops;
+
+               /* qed should learn receive the RSS ids and callbacks */
+               ops = edev->ops->common;
+               for (i = 0; i < QEDE_RSS_CNT(edev); i++)
+                       ops->simd_handler_config(edev->cdev,
+                                                &edev->fp_array[i], i,
+                                                qede_simd_fp_handler);
+               edev->int_info.used_cnt = QEDE_RSS_CNT(edev);
+       }
+       return 0;
+}
+
+static int qede_drain_txq(struct qede_dev *edev,
+                         struct qede_tx_queue *txq,
+                         bool allow_drain)
+{
+       int rc, cnt = 1000;
+
+       while (txq->sw_tx_cons != txq->sw_tx_prod) {
+               if (!cnt) {
+                       if (allow_drain) {
+                               DP_NOTICE(edev,
+                                         "Tx queue[%d] is stuck, requesting MCP to drain\n",
+                                         txq->index);
+                               rc = edev->ops->common->drain(edev->cdev);
+                               if (rc)
+                                       return rc;
+                               return qede_drain_txq(edev, txq, false);
+                       }
+                       DP_NOTICE(edev,
+                                 "Timeout waiting for tx queue[%d]: PROD=%d, CONS=%d\n",
+                                 txq->index, txq->sw_tx_prod,
+                                 txq->sw_tx_cons);
+                       return -ENODEV;
+               }
+               cnt--;
+               usleep_range(1000, 2000);
+               barrier();
+       }
+
+       /* FW finished processing, wait for HW to transmit all tx packets */
+       usleep_range(1000, 2000);
+
+       return 0;
+}
+
+static int qede_stop_queues(struct qede_dev *edev)
+{
+       struct qed_update_vport_params vport_update_params;
+       struct qed_dev *cdev = edev->cdev;
+       int rc, tc, i;
+
+       /* Disable the vport */
+       memset(&vport_update_params, 0, sizeof(vport_update_params));
+       vport_update_params.vport_id = 0;
+       vport_update_params.update_vport_active_flg = 1;
+       vport_update_params.vport_active_flg = 0;
+       vport_update_params.update_rss_flg = 0;
+
+       rc = edev->ops->vport_update(cdev, &vport_update_params);
+       if (rc) {
+               DP_ERR(edev, "Failed to update vport\n");
+               return rc;
+       }
+
+       /* Flush Tx queues. If needed, request drain from MCP */
+       for_each_rss(i) {
+               struct qede_fastpath *fp = &edev->fp_array[i];
+
+               for (tc = 0; tc < edev->num_tc; tc++) {
+                       struct qede_tx_queue *txq = &fp->txqs[tc];
+
+                       rc = qede_drain_txq(edev, txq, true);
+                       if (rc)
+                               return rc;
+               }
+       }
+
+       /* Stop all Queues in reverse order*/
+       for (i = QEDE_RSS_CNT(edev) - 1; i >= 0; i--) {
+               struct qed_stop_rxq_params rx_params;
+
+               /* Stop the Tx Queue(s)*/
+               for (tc = 0; tc < edev->num_tc; tc++) {
+                       struct qed_stop_txq_params tx_params;
+
+                       tx_params.rss_id = i;
+                       tx_params.tx_queue_id = tc * QEDE_RSS_CNT(edev) + i;
+                       rc = edev->ops->q_tx_stop(cdev, &tx_params);
+                       if (rc) {
+                               DP_ERR(edev, "Failed to stop TXQ #%d\n",
+                                      tx_params.tx_queue_id);
+                               return rc;
+                       }
+               }
+
+               /* Stop the Rx Queue*/
+               memset(&rx_params, 0, sizeof(rx_params));
+               rx_params.rss_id = i;
+               rx_params.rx_queue_id = i;
+
+               rc = edev->ops->q_rx_stop(cdev, &rx_params);
+               if (rc) {
+                       DP_ERR(edev, "Failed to stop RXQ #%d\n", i);
+                       return rc;
+               }
+       }
+
+       /* Stop the vport */
+       rc = edev->ops->vport_stop(cdev, 0);
+       if (rc)
+               DP_ERR(edev, "Failed to stop VPORT\n");
+
+       return rc;
+}
+
+static int qede_start_queues(struct qede_dev *edev)
+{
+       int rc, tc, i;
+       int vport_id = 0, drop_ttl0_flg = 1, vlan_removal_en = 1;
+       struct qed_dev *cdev = edev->cdev;
+       struct qed_update_vport_rss_params *rss_params = &edev->rss_params;
+       struct qed_update_vport_params vport_update_params;
+       struct qed_queue_start_common_params q_params;
+
+       if (!edev->num_rss) {
+               DP_ERR(edev,
+                      "Cannot update V-VPORT as active as there are no Rx queues\n");
+               return -EINVAL;
+       }
+
+       rc = edev->ops->vport_start(cdev, vport_id,
+                                   edev->ndev->mtu,
+                                   drop_ttl0_flg,
+                                   vlan_removal_en);
+
+       if (rc) {
+               DP_ERR(edev, "Start V-PORT failed %d\n", rc);
+               return rc;
+       }
+
+       DP_VERBOSE(edev, NETIF_MSG_IFUP,
+                  "Start vport ramrod passed, vport_id = %d, MTU = %d, vlan_removal_en = %d\n",
+                  vport_id, edev->ndev->mtu + 0xe, vlan_removal_en);
+
+       for_each_rss(i) {
+               struct qede_fastpath *fp = &edev->fp_array[i];
+               dma_addr_t phys_table = fp->rxq->rx_comp_ring.pbl.p_phys_table;
+
+               memset(&q_params, 0, sizeof(q_params));
+               q_params.rss_id = i;
+               q_params.queue_id = i;
+               q_params.vport_id = 0;
+               q_params.sb = fp->sb_info->igu_sb_id;
+               q_params.sb_idx = RX_PI;
+
+               rc = edev->ops->q_rx_start(cdev, &q_params,
+                                          fp->rxq->rx_buf_size,
+                                          fp->rxq->rx_bd_ring.p_phys_addr,
+                                          phys_table,
+                                          fp->rxq->rx_comp_ring.page_cnt,
+                                          &fp->rxq->hw_rxq_prod_addr);
+               if (rc) {
+                       DP_ERR(edev, "Start RXQ #%d failed %d\n", i, rc);
+                       return rc;
+               }
+
+               fp->rxq->hw_cons_ptr = &fp->sb_info->sb_virt->pi_array[RX_PI];
+
+               qede_update_rx_prod(edev, fp->rxq);
+
+               for (tc = 0; tc < edev->num_tc; tc++) {
+                       struct qede_tx_queue *txq = &fp->txqs[tc];
+                       int txq_index = tc * QEDE_RSS_CNT(edev) + i;
+
+                       memset(&q_params, 0, sizeof(q_params));
+                       q_params.rss_id = i;
+                       q_params.queue_id = txq_index;
+                       q_params.vport_id = 0;
+                       q_params.sb = fp->sb_info->igu_sb_id;
+                       q_params.sb_idx = TX_PI(tc);
+
+                       rc = edev->ops->q_tx_start(cdev, &q_params,
+                                                  txq->tx_pbl.pbl.p_phys_table,
+                                                  txq->tx_pbl.page_cnt,
+                                                  &txq->doorbell_addr);
+                       if (rc) {
+                               DP_ERR(edev, "Start TXQ #%d failed %d\n",
+                                      txq_index, rc);
+                               return rc;
+                       }
+
+                       txq->hw_cons_ptr =
+                               &fp->sb_info->sb_virt->pi_array[TX_PI(tc)];
+                       SET_FIELD(txq->tx_db.data.params,
+                                 ETH_DB_DATA_DEST, DB_DEST_XCM);
+                       SET_FIELD(txq->tx_db.data.params, ETH_DB_DATA_AGG_CMD,
+                                 DB_AGG_CMD_SET);
+                       SET_FIELD(txq->tx_db.data.params,
+                                 ETH_DB_DATA_AGG_VAL_SEL,
+                                 DQ_XCM_ETH_TX_BD_PROD_CMD);
+
+                       txq->tx_db.data.agg_flags = DQ_XCM_ETH_DQ_CF_CMD;
+               }
+       }
+
+       /* Prepare and send the vport enable */
+       memset(&vport_update_params, 0, sizeof(vport_update_params));
+       vport_update_params.vport_id = vport_id;
+       vport_update_params.update_vport_active_flg = 1;
+       vport_update_params.vport_active_flg = 1;
+
+       /* Fill struct with RSS params */
+       if (QEDE_RSS_CNT(edev) > 1) {
+               vport_update_params.update_rss_flg = 1;
+               for (i = 0; i < 128; i++)
+                       rss_params->rss_ind_table[i] =
+                       ethtool_rxfh_indir_default(i, QEDE_RSS_CNT(edev));
+               netdev_rss_key_fill(rss_params->rss_key,
+                                   sizeof(rss_params->rss_key));
+       } else {
+               memset(rss_params, 0, sizeof(*rss_params));
+       }
+       memcpy(&vport_update_params.rss_params, rss_params,
+              sizeof(*rss_params));
+
+       rc = edev->ops->vport_update(cdev, &vport_update_params);
+       if (rc) {
+               DP_ERR(edev, "Update V-PORT failed %d\n", rc);
+               return rc;
+       }
+
+       return 0;
+}
+
+static int qede_set_mcast_rx_mac(struct qede_dev *edev,
+                                enum qed_filter_xcast_params_type opcode,
+                                unsigned char *mac, int num_macs)
+{
+       struct qed_filter_params filter_cmd;
+       int i;
+
+       memset(&filter_cmd, 0, sizeof(filter_cmd));
+       filter_cmd.type = QED_FILTER_TYPE_MCAST;
+       filter_cmd.filter.mcast.type = opcode;
+       filter_cmd.filter.mcast.num = num_macs;
+
+       for (i = 0; i < num_macs; i++, mac += ETH_ALEN)
+               ether_addr_copy(filter_cmd.filter.mcast.mac[i], mac);
+
+       return edev->ops->filter_config(edev->cdev, &filter_cmd);
+}
+
+enum qede_unload_mode {
+       QEDE_UNLOAD_NORMAL,
+};
+
+static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode)
+{
+       struct qed_link_params link_params;
+       int rc;
+
+       DP_INFO(edev, "Starting qede unload\n");
+
+       mutex_lock(&edev->qede_lock);
+       edev->state = QEDE_STATE_CLOSED;
+
+       /* Close OS Tx */
+       netif_tx_disable(edev->ndev);
+       netif_carrier_off(edev->ndev);
+
+       /* Reset the link */
+       memset(&link_params, 0, sizeof(link_params));
+       link_params.link_up = false;
+       edev->ops->common->set_link(edev->cdev, &link_params);
+       rc = qede_stop_queues(edev);
+       if (rc) {
+               qede_sync_free_irqs(edev);
+               goto out;
+       }
+
+       DP_INFO(edev, "Stopped Queues\n");
+
+       edev->ops->fastpath_stop(edev->cdev);
+
+       /* Release the interrupts */
+       qede_sync_free_irqs(edev);
+       edev->ops->common->set_fp_int(edev->cdev, 0);
+
+       qede_napi_disable_remove(edev);
+
+       qede_free_mem_load(edev);
+       qede_free_fp_array(edev);
+
+out:
+       mutex_unlock(&edev->qede_lock);
+       DP_INFO(edev, "Ending qede unload\n");
+}
+
+enum qede_load_mode {
+       QEDE_LOAD_NORMAL,
+};
+
+static int qede_load(struct qede_dev *edev, enum qede_load_mode mode)
+{
+       struct qed_link_params link_params;
+       struct qed_link_output link_output;
+       int rc;
+
+       DP_INFO(edev, "Starting qede load\n");
+
+       rc = qede_set_num_queues(edev);
+       if (rc)
+               goto err0;
+
+       rc = qede_alloc_fp_array(edev);
+       if (rc)
+               goto err0;
+
+       qede_init_fp(edev);
+
+       rc = qede_alloc_mem_load(edev);
+       if (rc)
+               goto err1;
+       DP_INFO(edev, "Allocated %d RSS queues on %d TC/s\n",
+               QEDE_RSS_CNT(edev), edev->num_tc);
+
+       rc = qede_set_real_num_queues(edev);
+       if (rc)
+               goto err2;
+
+       qede_napi_add_enable(edev);
+       DP_INFO(edev, "Napi added and enabled\n");
+
+       rc = qede_setup_irqs(edev);
+       if (rc)
+               goto err3;
+       DP_INFO(edev, "Setup IRQs succeeded\n");
+
+       rc = qede_start_queues(edev);
+       if (rc)
+               goto err4;
+       DP_INFO(edev, "Start VPORT, RXQ and TXQ succeeded\n");
+
+       /* Add primary mac and set Rx filters */
+       ether_addr_copy(edev->primary_mac, edev->ndev->dev_addr);
+
+       mutex_lock(&edev->qede_lock);
+       edev->state = QEDE_STATE_OPEN;
+       mutex_unlock(&edev->qede_lock);
+
+       /* Ask for link-up using current configuration */
+       memset(&link_params, 0, sizeof(link_params));
+       link_params.link_up = true;
+       edev->ops->common->set_link(edev->cdev, &link_params);
+
+       /* Query whether link is already-up */
+       memset(&link_output, 0, sizeof(link_output));
+       edev->ops->common->get_link(edev->cdev, &link_output);
+       qede_link_update(edev, &link_output);
+
+       DP_INFO(edev, "Ending successfully qede load\n");
+
+       return 0;
+
+err4:
+       qede_sync_free_irqs(edev);
+       memset(&edev->int_info.msix_cnt, 0, sizeof(struct qed_int_info));
+err3:
+       qede_napi_disable_remove(edev);
+err2:
+       qede_free_mem_load(edev);
+err1:
+       edev->ops->common->set_fp_int(edev->cdev, 0);
+       qede_free_fp_array(edev);
+       edev->num_rss = 0;
+err0:
+       return rc;
+}
+
+void qede_reload(struct qede_dev *edev,
+                void (*func)(struct qede_dev *, union qede_reload_args *),
+                union qede_reload_args *args)
+{
+       qede_unload(edev, QEDE_UNLOAD_NORMAL);
+       /* Call function handler to update parameters
+        * needed for function load.
+        */
+       if (func)
+               func(edev, args);
+
+       qede_load(edev, QEDE_LOAD_NORMAL);
+
+       mutex_lock(&edev->qede_lock);
+       qede_config_rx_mode(edev->ndev);
+       mutex_unlock(&edev->qede_lock);
+}
+
+/* called with rtnl_lock */
+static int qede_open(struct net_device *ndev)
+{
+       struct qede_dev *edev = netdev_priv(ndev);
+
+       netif_carrier_off(ndev);
+
+       edev->ops->common->set_power_state(edev->cdev, PCI_D0);
+
+       return qede_load(edev, QEDE_LOAD_NORMAL);
+}
+
+static int qede_close(struct net_device *ndev)
+{
+       struct qede_dev *edev = netdev_priv(ndev);
+
+       qede_unload(edev, QEDE_UNLOAD_NORMAL);
+
+       return 0;
+}
+
+static void qede_link_update(void *dev, struct qed_link_output *link)
+{
+       struct qede_dev *edev = dev;
+
+       if (!netif_running(edev->ndev)) {
+               DP_VERBOSE(edev, NETIF_MSG_LINK, "Interface is not running\n");
+               return;
+       }
+
+       if (link->link_up) {
+               DP_NOTICE(edev, "Link is up\n");
+               netif_tx_start_all_queues(edev->ndev);
+               netif_carrier_on(edev->ndev);
+       } else {
+               DP_NOTICE(edev, "Link is down\n");
+               netif_tx_disable(edev->ndev);
+               netif_carrier_off(edev->ndev);
+       }
+}
+
+static int qede_set_mac_addr(struct net_device *ndev, void *p)
+{
+       struct qede_dev *edev = netdev_priv(ndev);
+       struct sockaddr *addr = p;
+       int rc;
+
+       ASSERT_RTNL(); /* @@@TBD To be removed */
+
+       DP_INFO(edev, "Set_mac_addr called\n");
+
+       if (!is_valid_ether_addr(addr->sa_data)) {
+               DP_NOTICE(edev, "The MAC address is not valid\n");
+               return -EFAULT;
+       }
+
+       ether_addr_copy(ndev->dev_addr, addr->sa_data);
+
+       if (!netif_running(ndev))  {
+               DP_NOTICE(edev, "The device is currently down\n");
+               return 0;
+       }
+
+       /* Remove the previous primary mac */
+       rc = qede_set_ucast_rx_mac(edev, QED_FILTER_XCAST_TYPE_DEL,
+                                  edev->primary_mac);
+       if (rc)
+               return rc;
+
+       /* Add MAC filter according to the new unicast HW MAC address */
+       ether_addr_copy(edev->primary_mac, ndev->dev_addr);
+       return qede_set_ucast_rx_mac(edev, QED_FILTER_XCAST_TYPE_ADD,
+                                     edev->primary_mac);
+}
+
+static int
+qede_configure_mcast_filtering(struct net_device *ndev,
+                              enum qed_filter_rx_mode_type *accept_flags)
+{
+       struct qede_dev *edev = netdev_priv(ndev);
+       unsigned char *mc_macs, *temp;
+       struct netdev_hw_addr *ha;
+       int rc = 0, mc_count;
+       size_t size;
+
+       size = 64 * ETH_ALEN;
+
+       mc_macs = kzalloc(size, GFP_KERNEL);
+       if (!mc_macs) {
+               DP_NOTICE(edev,
+                         "Failed to allocate memory for multicast MACs\n");
+               rc = -ENOMEM;
+               goto exit;
+       }
+
+       temp = mc_macs;
+
+       /* Remove all previously configured MAC filters */
+       rc = qede_set_mcast_rx_mac(edev, QED_FILTER_XCAST_TYPE_DEL,
+                                  mc_macs, 1);
+       if (rc)
+               goto exit;
+
+       netif_addr_lock_bh(ndev);
+
+       mc_count = netdev_mc_count(ndev);
+       if (mc_count < 64) {
+               netdev_for_each_mc_addr(ha, ndev) {
+                       ether_addr_copy(temp, ha->addr);
+                       temp += ETH_ALEN;
+               }
+       }
+
+       netif_addr_unlock_bh(ndev);
+
+       /* Check for all multicast @@@TBD resource allocation */
+       if ((ndev->flags & IFF_ALLMULTI) ||
+           (mc_count > 64)) {
+               if (*accept_flags == QED_FILTER_RX_MODE_TYPE_REGULAR)
+                       *accept_flags = QED_FILTER_RX_MODE_TYPE_MULTI_PROMISC;
+       } else {
+               /* Add all multicast MAC filters */
+               rc = qede_set_mcast_rx_mac(edev, QED_FILTER_XCAST_TYPE_ADD,
+                                          mc_macs, mc_count);
+       }
+
+exit:
+       kfree(mc_macs);
+       return rc;
+}
+
+static void qede_set_rx_mode(struct net_device *ndev)
+{
+       struct qede_dev *edev = netdev_priv(ndev);
+
+       DP_INFO(edev, "qede_set_rx_mode called\n");
+
+       if (edev->state != QEDE_STATE_OPEN) {
+               DP_INFO(edev,
+                       "qede_set_rx_mode called while interface is down\n");
+       } else {
+               set_bit(QEDE_SP_RX_MODE, &edev->sp_flags);
+               schedule_delayed_work(&edev->sp_task, 0);
+       }
+}
+
+/* Must be called with qede_lock held */
+static void qede_config_rx_mode(struct net_device *ndev)
+{
+       enum qed_filter_rx_mode_type accept_flags = QED_FILTER_TYPE_UCAST;
+       struct qede_dev *edev = netdev_priv(ndev);
+       struct qed_filter_params rx_mode;
+       unsigned char *uc_macs, *temp;
+       struct netdev_hw_addr *ha;
+       int rc, uc_count;
+       size_t size;
+
+       netif_addr_lock_bh(ndev);
+
+       uc_count = netdev_uc_count(ndev);
+       size = uc_count * ETH_ALEN;
+
+       uc_macs = kzalloc(size, GFP_ATOMIC);
+       if (!uc_macs) {
+               DP_NOTICE(edev, "Failed to allocate memory for unicast MACs\n");
+               netif_addr_unlock_bh(ndev);
+               return;
+       }
+
+       temp = uc_macs;
+       netdev_for_each_uc_addr(ha, ndev) {
+               ether_addr_copy(temp, ha->addr);
+               temp += ETH_ALEN;
+       }
+
+       netif_addr_unlock_bh(ndev);
+
+       /* Configure the struct for the Rx mode */
+       memset(&rx_mode, 0, sizeof(struct qed_filter_params));
+       rx_mode.type = QED_FILTER_TYPE_RX_MODE;
+
+       /* Remove all previous unicast secondary macs and multicast macs
+        * (configrue / leave the primary mac)
+        */
+       rc = qede_set_ucast_rx_mac(edev, QED_FILTER_XCAST_TYPE_REPLACE,
+                                  edev->primary_mac);
+       if (rc)
+               goto out;
+
+       /* Check for promiscuous */
+       if ((ndev->flags & IFF_PROMISC) ||
+           (uc_count > 15)) { /* @@@TBD resource allocation - 1 */
+               accept_flags = QED_FILTER_RX_MODE_TYPE_PROMISC;
+       } else {
+               /* Add MAC filters according to the unicast secondary macs */
+               int i;
+
+               temp = uc_macs;
+               for (i = 0; i < uc_count; i++) {
+                       rc = qede_set_ucast_rx_mac(edev,
+                                                  QED_FILTER_XCAST_TYPE_ADD,
+                                                  temp);
+                       if (rc)
+                               goto out;
+
+                       temp += ETH_ALEN;
+               }
+
+               rc = qede_configure_mcast_filtering(ndev, &accept_flags);
+               if (rc)
+                       goto out;
+       }
+
+       rx_mode.filter.accept_flags = accept_flags;
+       edev->ops->filter_config(edev->cdev, &rx_mode);
+out:
+       kfree(uc_macs);
+}
index 4847713211cafa2258b9511cda89f4232a623ebe..b09a6b80d10719c967994ed54618c50e25fdec0f 100644 (file)
@@ -1736,8 +1736,6 @@ static void ql_get_drvinfo(struct net_device *ndev,
                sizeof(drvinfo->version));
        strlcpy(drvinfo->bus_info, pci_name(qdev->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->regdump_len = 0;
-       drvinfo->eedump_len = 0;
 }
 
 static u32 ql_get_msglevel(struct net_device *ndev)
index d6696cfa11d256546d623a0fc331ae6934798a78..46bbea8e023c0194e408ec9e269456c9456136e2 100644 (file)
@@ -1092,7 +1092,7 @@ struct qlcnic_filter_hash {
 struct qlcnic_mailbox {
        struct workqueue_struct *work_q;
        struct qlcnic_adapter   *adapter;
-       struct qlcnic_mbx_ops   *ops;
+       const struct qlcnic_mbx_ops *ops;
        struct work_struct      work;
        struct completion       completion;
        struct list_head        cmd_q;
index 9f0bdd993955cab628d676d079ceb0d6bd37e805..37a731be7d399f6ae14d5c0e1f889599f9fd2d5d 100644 (file)
@@ -4048,7 +4048,7 @@ static void qlcnic_83xx_mailbox_worker(struct work_struct *work)
        struct qlcnic_mailbox *mbx = container_of(work, struct qlcnic_mailbox,
                                                  work);
        struct qlcnic_adapter *adapter = mbx->adapter;
-       struct qlcnic_mbx_ops *mbx_ops = mbx->ops;
+       const struct qlcnic_mbx_ops *mbx_ops = mbx->ops;
        struct device *dev = &adapter->pdev->dev;
        atomic_t *rsp_status = &mbx->rsp_status;
        struct list_head *head = &mbx->cmd_q;
@@ -4098,7 +4098,7 @@ static void qlcnic_83xx_mailbox_worker(struct work_struct *work)
        }
 }
 
-static struct qlcnic_mbx_ops qlcnic_83xx_mbx_ops = {
+static const struct qlcnic_mbx_ops qlcnic_83xx_mbx_ops = {
        .enqueue_cmd    = qlcnic_83xx_enqueue_mbx_cmd,
        .dequeue_cmd    = qlcnic_83xx_dequeue_mbx_cmd,
        .decode_resp    = qlcnic_83xx_decode_mbx_rsp,
index c3c514e332b5e5dfe67943e1155e8ff24e043c95..5dade1fd08b8656be02cfb9a79ea20176989a29f 100644 (file)
@@ -415,13 +415,6 @@ static void ql_get_drvinfo(struct net_device *ndev,
                 (qdev->fw_rev_id & 0x000000ff));
        strlcpy(drvinfo->bus_info, pci_name(qdev->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->n_stats = 0;
-       drvinfo->testinfo_len = 0;
-       if (!test_bit(QL_FRC_COREDUMP, &qdev->flags))
-               drvinfo->regdump_len = sizeof(struct ql_mpi_coredump);
-       else
-               drvinfo->regdump_len = sizeof(struct ql_reg_dump);
-       drvinfo->eedump_len = 0;
 }
 
 static void ql_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
index 686334f4588dcd928578ed83892fb4febac2d5ea..deae10d7426df0441ad5970f3f425a948c32abb3 100644 (file)
@@ -175,7 +175,7 @@ enum {
        LastFrag        = (1 << 28), /* Final segment of a packet */
        LargeSend       = (1 << 27), /* TCP Large Send Offload (TSO) */
        MSSShift        = 16,        /* MSS value position */
-       MSSMask         = 0xfff,     /* MSS value: 11 bits */
+       MSSMask         = 0x7ff,     /* MSS value: 11 bits */
        TxError         = (1 << 23), /* Tx error summary */
        RxError         = (1 << 20), /* Rx error summary */
        IPCS            = (1 << 18), /* Calculate IP checksum */
@@ -754,10 +754,16 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
        eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
        mss = skb_shinfo(skb)->gso_size;
 
+       if (mss > MSSMask) {
+               WARN_ONCE(1, "Net bug: GSO size %d too large for 8139CP\n",
+                         mss);
+               goto out_dma_error;
+       }
+
        opts2 = cpu_to_le32(cp_tx_vlan_tag(skb));
        opts1 = DescOwn;
        if (mss)
-               opts1 |= LargeSend | ((mss & MSSMask) << MSSShift);
+               opts1 |= LargeSend | (mss << MSSShift);
        else if (skb->ip_summed == CHECKSUM_PARTIAL) {
                const struct iphdr *ip = ip_hdr(skb);
                if (ip->protocol == IPPROTO_TCP)
@@ -1852,6 +1858,15 @@ static void cp_set_d3_state (struct cp_private *cp)
        pci_set_power_state (cp->pdev, PCI_D3hot);
 }
 
+static netdev_features_t cp_features_check(struct sk_buff *skb,
+                                          struct net_device *dev,
+                                          netdev_features_t features)
+{
+       if (skb_shinfo(skb)->gso_size > MSSMask)
+               features &= ~NETIF_F_TSO;
+
+       return vlan_features_check(skb, features);
+}
 static const struct net_device_ops cp_netdev_ops = {
        .ndo_open               = cp_open,
        .ndo_stop               = cp_close,
@@ -1864,6 +1879,7 @@ static const struct net_device_ops cp_netdev_ops = {
        .ndo_tx_timeout         = cp_tx_timeout,
        .ndo_set_features       = cp_set_features,
        .ndo_change_mtu         = cp_change_mtu,
+       .ndo_features_check     = cp_features_check,
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = cp_poll_controller,
@@ -1983,12 +1999,12 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
        dev->ethtool_ops = &cp_ethtool_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
-       dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+       dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
+               NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
 
        if (pci_using_dac)
                dev->features |= NETIF_F_HIGHDMA;
 
-       /* disabled by default until verified */
        dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
                NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
        dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
index 78bb4ceb1cdd364f3d044f746ada0ff0aef954e3..ef668d300800c61b18dc97af10afe2f78d20f870 100644 (file)
@@ -2388,7 +2388,6 @@ static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *
        strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
        strlcpy(info->version, DRV_VERSION, sizeof(info->version));
        strlcpy(info->bus_info, pci_name(tp->pci_dev), sizeof(info->bus_info));
-       info->regdump_len = tp->regs_len;
 }
 
 static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
index a157aaaaff6a183b161f5d7469fd8a7602be729d..0623fff932e4dea830a24d2c45ff484159feaccf 100644 (file)
@@ -766,6 +766,11 @@ struct ravb_ptp {
        struct ravb_ptp_perout perout[N_PER_OUT];
 };
 
+enum ravb_chip_id {
+       RCAR_GEN2,
+       RCAR_GEN3,
+};
+
 struct ravb_private {
        struct net_device *ndev;
        struct platform_device *pdev;
@@ -806,6 +811,8 @@ struct ravb_private {
        int msg_enable;
        int speed;
        int duplex;
+       int emac_irq;
+       enum ravb_chip_id chip_id;
 
        unsigned no_avb_link:1;
        unsigned avb_link_active_low:1;
index 450899e9cea22591328b2739b9d92291a7c55b2c..aa7b2083cb539c15c8b831e4c0703ab16375a0ae 100644 (file)
@@ -201,7 +201,7 @@ static void ravb_ring_free(struct net_device *ndev, int q)
        if (priv->rx_ring[q]) {
                ring_size = sizeof(struct ravb_ex_rx_desc) *
                            (priv->num_rx_ring[q] + 1);
-               dma_free_coherent(NULL, ring_size, priv->rx_ring[q],
+               dma_free_coherent(ndev->dev.parent, ring_size, priv->rx_ring[q],
                                  priv->rx_desc_dma[q]);
                priv->rx_ring[q] = NULL;
        }
@@ -209,7 +209,7 @@ static void ravb_ring_free(struct net_device *ndev, int q)
        if (priv->tx_ring[q]) {
                ring_size = sizeof(struct ravb_tx_desc) *
                            (priv->num_tx_ring[q] * NUM_TX_DESC + 1);
-               dma_free_coherent(NULL, ring_size, priv->tx_ring[q],
+               dma_free_coherent(ndev->dev.parent, ring_size, priv->tx_ring[q],
                                  priv->tx_desc_dma[q]);
                priv->tx_ring[q] = NULL;
        }
@@ -240,13 +240,13 @@ static void ravb_ring_format(struct net_device *ndev, int q)
                rx_desc = &priv->rx_ring[q][i];
                /* The size of the buffer should be on 16-byte boundary. */
                rx_desc->ds_cc = cpu_to_le16(ALIGN(PKT_BUF_SZ, 16));
-               dma_addr = dma_map_single(&ndev->dev, priv->rx_skb[q][i]->data,
+               dma_addr = dma_map_single(ndev->dev.parent, priv->rx_skb[q][i]->data,
                                          ALIGN(PKT_BUF_SZ, 16),
                                          DMA_FROM_DEVICE);
                /* We just set the data size to 0 for a failed mapping which
                 * should prevent DMA from happening...
                 */
-               if (dma_mapping_error(&ndev->dev, dma_addr))
+               if (dma_mapping_error(ndev->dev.parent, dma_addr))
                        rx_desc->ds_cc = cpu_to_le16(0);
                rx_desc->dptr = cpu_to_le32(dma_addr);
                rx_desc->die_dt = DT_FEMPTY;
@@ -309,7 +309,7 @@ static int ravb_ring_init(struct net_device *ndev, int q)
 
        /* Allocate all RX descriptors. */
        ring_size = sizeof(struct ravb_ex_rx_desc) * (priv->num_rx_ring[q] + 1);
-       priv->rx_ring[q] = dma_alloc_coherent(NULL, ring_size,
+       priv->rx_ring[q] = dma_alloc_coherent(ndev->dev.parent, ring_size,
                                              &priv->rx_desc_dma[q],
                                              GFP_KERNEL);
        if (!priv->rx_ring[q])
@@ -320,7 +320,7 @@ static int ravb_ring_init(struct net_device *ndev, int q)
        /* Allocate all TX descriptors. */
        ring_size = sizeof(struct ravb_tx_desc) *
                    (priv->num_tx_ring[q] * NUM_TX_DESC + 1);
-       priv->tx_ring[q] = dma_alloc_coherent(NULL, ring_size,
+       priv->tx_ring[q] = dma_alloc_coherent(ndev->dev.parent, ring_size,
                                              &priv->tx_desc_dma[q],
                                              GFP_KERNEL);
        if (!priv->tx_ring[q])
@@ -443,7 +443,7 @@ static int ravb_tx_free(struct net_device *ndev, int q)
                size = le16_to_cpu(desc->ds_tagl) & TX_DS;
                /* Free the original skb. */
                if (priv->tx_skb[q][entry / NUM_TX_DESC]) {
-                       dma_unmap_single(&ndev->dev, le32_to_cpu(desc->dptr),
+                       dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr),
                                         size, DMA_TO_DEVICE);
                        /* Last packet descriptor? */
                        if (entry % NUM_TX_DESC == NUM_TX_DESC - 1) {
@@ -546,7 +546,7 @@ static bool ravb_rx(struct net_device *ndev, int *quota, int q)
 
                        skb = priv->rx_skb[q][entry];
                        priv->rx_skb[q][entry] = NULL;
-                       dma_unmap_single(&ndev->dev, le32_to_cpu(desc->dptr),
+                       dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr),
                                         ALIGN(PKT_BUF_SZ, 16),
                                         DMA_FROM_DEVICE);
                        get_ts &= (q == RAVB_NC) ?
@@ -586,14 +586,14 @@ static bool ravb_rx(struct net_device *ndev, int *quota, int q)
                        if (!skb)
                                break;  /* Better luck next round. */
                        ravb_set_buffer_align(skb);
-                       dma_addr = dma_map_single(&ndev->dev, skb->data,
+                       dma_addr = dma_map_single(ndev->dev.parent, skb->data,
                                                  le16_to_cpu(desc->ds_cc),
                                                  DMA_FROM_DEVICE);
                        skb_checksum_none_assert(skb);
                        /* We just set the data size to 0 for a failed mapping
                         * which should prevent DMA  from happening...
                         */
-                       if (dma_mapping_error(&ndev->dev, dma_addr))
+                       if (dma_mapping_error(ndev->dev.parent, dma_addr))
                                desc->ds_cc = cpu_to_le16(0);
                        desc->dptr = cpu_to_le32(dma_addr);
                        priv->rx_skb[q][entry] = skb;
@@ -889,6 +889,22 @@ static int ravb_phy_init(struct net_device *ndev)
                return -ENOENT;
        }
 
+       /* This driver only support 10/100Mbit speeds on Gen3
+        * at this time.
+        */
+       if (priv->chip_id == RCAR_GEN3) {
+               int err;
+
+               err = phy_set_max_speed(phydev, SPEED_100);
+               if (err) {
+                       netdev_err(ndev, "failed to limit PHY to 100Mbit/s\n");
+                       phy_disconnect(phydev);
+                       return err;
+               }
+
+               netdev_info(ndev, "limited PHY to 100Mbit/s\n");
+       }
+
        netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n",
                    phydev->addr, phydev->irq, phydev->drv->name);
 
@@ -1197,6 +1213,15 @@ static int ravb_open(struct net_device *ndev)
                goto out_napi_off;
        }
 
+       if (priv->chip_id == RCAR_GEN3) {
+               error = request_irq(priv->emac_irq, ravb_interrupt,
+                                   IRQF_SHARED, ndev->name, ndev);
+               if (error) {
+                       netdev_err(ndev, "cannot request IRQ\n");
+                       goto out_free_irq;
+               }
+       }
+
        /* Device init */
        error = ravb_dmac_init(ndev);
        if (error)
@@ -1220,6 +1245,7 @@ out_ptp_stop:
        ravb_ptp_stop(ndev);
 out_free_irq:
        free_irq(ndev->irq, ndev);
+       free_irq(priv->emac_irq, ndev);
 out_napi_off:
        napi_disable(&priv->napi[RAVB_NC]);
        napi_disable(&priv->napi[RAVB_BE]);
@@ -1300,8 +1326,8 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                 entry / NUM_TX_DESC * DPTR_ALIGN;
        len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data;
        memcpy(buffer, skb->data, len);
-       dma_addr = dma_map_single(&ndev->dev, buffer, len, DMA_TO_DEVICE);
-       if (dma_mapping_error(&ndev->dev, dma_addr))
+       dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE);
+       if (dma_mapping_error(ndev->dev.parent, dma_addr))
                goto drop;
 
        desc = &priv->tx_ring[q][entry];
@@ -1310,8 +1336,8 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 
        buffer = skb->data + len;
        len = skb->len - len;
-       dma_addr = dma_map_single(&ndev->dev, buffer, len, DMA_TO_DEVICE);
-       if (dma_mapping_error(&ndev->dev, dma_addr))
+       dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE);
+       if (dma_mapping_error(ndev->dev.parent, dma_addr))
                goto unmap;
 
        desc++;
@@ -1323,7 +1349,7 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                ts_skb = kmalloc(sizeof(*ts_skb), GFP_ATOMIC);
                if (!ts_skb) {
                        desc--;
-                       dma_unmap_single(&ndev->dev, dma_addr, len,
+                       dma_unmap_single(ndev->dev.parent, dma_addr, len,
                                         DMA_TO_DEVICE);
                        goto unmap;
                }
@@ -1358,7 +1384,7 @@ exit:
        return NETDEV_TX_OK;
 
 unmap:
-       dma_unmap_single(&ndev->dev, le32_to_cpu(desc->dptr),
+       dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr),
                         le16_to_cpu(desc->ds_tagl), DMA_TO_DEVICE);
 drop:
        dev_kfree_skb_any(skb);
@@ -1625,10 +1651,20 @@ static int ravb_mdio_release(struct ravb_private *priv)
        return 0;
 }
 
+static const struct of_device_id ravb_match_table[] = {
+       { .compatible = "renesas,etheravb-r8a7790", .data = (void *)RCAR_GEN2 },
+       { .compatible = "renesas,etheravb-r8a7794", .data = (void *)RCAR_GEN2 },
+       { .compatible = "renesas,etheravb-r8a7795", .data = (void *)RCAR_GEN3 },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ravb_match_table);
+
 static int ravb_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
+       const struct of_device_id *match;
        struct ravb_private *priv;
+       enum ravb_chip_id chip_id;
        struct net_device *ndev;
        int error, irq, q;
        struct resource *res;
@@ -1657,7 +1693,14 @@ static int ravb_probe(struct platform_device *pdev)
        /* The Ether-specific entries in the device structure. */
        ndev->base_addr = res->start;
        ndev->dma = -1;
-       irq = platform_get_irq(pdev, 0);
+
+       match = of_match_device(of_match_ptr(ravb_match_table), &pdev->dev);
+       chip_id = (enum ravb_chip_id)match->data;
+
+       if (chip_id == RCAR_GEN3)
+               irq = platform_get_irq_byname(pdev, "ch22");
+       else
+               irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                error = irq;
                goto out_release;
@@ -1688,6 +1731,17 @@ static int ravb_probe(struct platform_device *pdev)
        priv->avb_link_active_low =
                of_property_read_bool(np, "renesas,ether-link-active-low");
 
+       if (chip_id == RCAR_GEN3) {
+               irq = platform_get_irq_byname(pdev, "ch24");
+               if (irq < 0) {
+                       error = irq;
+                       goto out_release;
+               }
+               priv->emac_irq = irq;
+       }
+
+       priv->chip_id = chip_id;
+
        /* Set function */
        ndev->netdev_ops = &ravb_netdev_ops;
        ndev->ethtool_ops = &ravb_ethtool_ops;
@@ -1708,10 +1762,10 @@ static int ravb_probe(struct platform_device *pdev)
 
        /* Allocate descriptor base address table */
        priv->desc_bat_size = sizeof(struct ravb_desc) * DBAT_ENTRY_NUM;
-       priv->desc_bat = dma_alloc_coherent(NULL, priv->desc_bat_size,
+       priv->desc_bat = dma_alloc_coherent(ndev->dev.parent, priv->desc_bat_size,
                                            &priv->desc_bat_dma, GFP_KERNEL);
        if (!priv->desc_bat) {
-               dev_err(&ndev->dev,
+               dev_err(&pdev->dev,
                        "Cannot allocate desc base address table (size %d bytes)\n",
                        priv->desc_bat_size);
                error = -ENOMEM;
@@ -1738,7 +1792,7 @@ static int ravb_probe(struct platform_device *pdev)
        /* MDIO bus init */
        error = ravb_mdio_init(priv);
        if (error) {
-               dev_err(&ndev->dev, "failed to initialize MDIO\n");
+               dev_err(&pdev->dev, "failed to initialize MDIO\n");
                goto out_dma_free;
        }
 
@@ -1763,7 +1817,7 @@ out_napi_del:
        netif_napi_del(&priv->napi[RAVB_BE]);
        ravb_mdio_release(priv);
 out_dma_free:
-       dma_free_coherent(NULL, priv->desc_bat_size, priv->desc_bat,
+       dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat,
                          priv->desc_bat_dma);
 out_release:
        if (ndev)
@@ -1779,7 +1833,7 @@ static int ravb_remove(struct platform_device *pdev)
        struct net_device *ndev = platform_get_drvdata(pdev);
        struct ravb_private *priv = netdev_priv(ndev);
 
-       dma_free_coherent(NULL, priv->desc_bat_size, priv->desc_bat,
+       dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat,
                          priv->desc_bat_dma);
        /* Set reset mode */
        ravb_write(ndev, CCC_OPC_RESET, CCC);
@@ -1818,13 +1872,6 @@ static const struct dev_pm_ops ravb_dev_pm_ops = {
 #define RAVB_PM_OPS NULL
 #endif
 
-static const struct of_device_id ravb_match_table[] = {
-       { .compatible = "renesas,etheravb-r8a7790" },
-       { .compatible = "renesas,etheravb-r8a7794" },
-       { }
-};
-MODULE_DEVICE_TABLE(of, ravb_match_table);
-
 static struct platform_driver ravb_driver = {
        .probe          = ravb_probe,
        .remove         = ravb_remove,
index 257ea713b4c1564fa680acfcf7ad760268ea0df6..6150a235b72ccaf8bf58dd862362b02fe7f0cfae 100644 (file)
@@ -1127,7 +1127,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
        struct sh_eth_txdesc *txdesc = NULL;
        int rx_ringsize = sizeof(*rxdesc) * mdp->num_rx_ring;
        int tx_ringsize = sizeof(*txdesc) * mdp->num_tx_ring;
-       int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1;
+       int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN + 32 - 1;
        dma_addr_t dma_addr;
 
        mdp->cur_rx = 0;
@@ -1148,8 +1148,8 @@ static void sh_eth_ring_format(struct net_device *ndev)
 
                /* RX descriptor */
                rxdesc = &mdp->rx_ring[i];
-               /* The size of the buffer is a multiple of 16 bytes. */
-               rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16);
+               /* The size of the buffer is a multiple of 32 bytes. */
+               rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 32);
                dma_addr = dma_map_single(&ndev->dev, skb->data,
                                          rxdesc->buffer_length,
                                          DMA_FROM_DEVICE);
@@ -1173,7 +1173,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
        mdp->dirty_rx = (u32) (i - mdp->num_rx_ring);
 
        /* Mark the last entry as wrapping the ring. */
-       rxdesc->status |= cpu_to_edmac(mdp, RD_RDEL);
+       rxdesc->status |= cpu_to_edmac(mdp, RD_RDLE);
 
        memset(mdp->tx_ring, 0, tx_ringsize);
 
@@ -1212,15 +1212,15 @@ static int sh_eth_ring_init(struct net_device *ndev)
                mdp->rx_buf_sz += NET_IP_ALIGN;
 
        /* Allocate RX and TX skb rings */
-       mdp->rx_skbuff = kmalloc_array(mdp->num_rx_ring,
-                                      sizeof(*mdp->rx_skbuff), GFP_KERNEL);
+       mdp->rx_skbuff = kcalloc(mdp->num_rx_ring, sizeof(*mdp->rx_skbuff),
+                                GFP_KERNEL);
        if (!mdp->rx_skbuff) {
                ret = -ENOMEM;
                return ret;
        }
 
-       mdp->tx_skbuff = kmalloc_array(mdp->num_tx_ring,
-                                      sizeof(*mdp->tx_skbuff), GFP_KERNEL);
+       mdp->tx_skbuff = kcalloc(mdp->num_tx_ring, sizeof(*mdp->tx_skbuff),
+                                GFP_KERNEL);
        if (!mdp->tx_skbuff) {
                ret = -ENOMEM;
                goto skb_ring_free;
@@ -1232,7 +1232,7 @@ static int sh_eth_ring_init(struct net_device *ndev)
                                          GFP_KERNEL);
        if (!mdp->rx_ring) {
                ret = -ENOMEM;
-               goto desc_ring_free;
+               goto skb_ring_free;
        }
 
        mdp->dirty_rx = 0;
@@ -1416,7 +1416,7 @@ static int sh_eth_txfree(struct net_device *ndev)
                if (txdesc->status & cpu_to_edmac(mdp, TD_TACT))
                        break;
                /* TACT bit must be checked before all the following reads */
-               rmb();
+               dma_rmb();
                netif_info(mdp, tx_done, ndev,
                           "tx entry %d status 0x%08x\n",
                           entry, edmac_to_cpu(mdp, txdesc->status));
@@ -1450,7 +1450,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
        struct sk_buff *skb;
        u16 pkt_len = 0;
        u32 desc_status;
-       int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1;
+       int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN + 32 - 1;
        dma_addr_t dma_addr;
 
        boguscnt = min(boguscnt, *quota);
@@ -1458,7 +1458,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
        rxdesc = &mdp->rx_ring[entry];
        while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) {
                /* RACT bit must be checked before all the following reads */
-               rmb();
+               dma_rmb();
                desc_status = edmac_to_cpu(mdp, rxdesc->status);
                pkt_len = rxdesc->frame_length;
 
@@ -1506,7 +1506,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                        if (mdp->cd->rpadir)
                                skb_reserve(skb, NET_IP_ALIGN);
                        dma_unmap_single(&ndev->dev, rxdesc->addr,
-                                        ALIGN(mdp->rx_buf_sz, 16),
+                                        ALIGN(mdp->rx_buf_sz, 32),
                                         DMA_FROM_DEVICE);
                        skb_put(skb, pkt_len);
                        skb->protocol = eth_type_trans(skb, ndev);
@@ -1524,8 +1524,8 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
        for (; mdp->cur_rx - mdp->dirty_rx > 0; mdp->dirty_rx++) {
                entry = mdp->dirty_rx % mdp->num_rx_ring;
                rxdesc = &mdp->rx_ring[entry];
-               /* The size of the buffer is 16 byte boundary. */
-               rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16);
+               /* The size of the buffer is 32 byte boundary. */
+               rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 32);
 
                if (mdp->rx_skbuff[entry] == NULL) {
                        skb = netdev_alloc_skb(ndev, skbuff_size);
@@ -1544,10 +1544,10 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                        skb_checksum_none_assert(skb);
                        rxdesc->addr = dma_addr;
                }
-               wmb(); /* RACT bit must be set after all the above writes */
+               dma_wmb(); /* RACT bit must be set after all the above writes */
                if (entry >= mdp->num_rx_ring - 1)
                        rxdesc->status |=
-                               cpu_to_edmac(mdp, RD_RACT | RD_RFP | RD_RDEL);
+                               cpu_to_edmac(mdp, RD_RACT | RD_RFP | RD_RDLE);
                else
                        rxdesc->status |=
                                cpu_to_edmac(mdp, RD_RACT | RD_RFP);
@@ -2403,7 +2403,7 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        }
        txdesc->buffer_length = skb->len;
 
-       wmb(); /* TACT bit must be set after all the above writes */
+       dma_wmb(); /* TACT bit must be set after all the above writes */
        if (entry >= mdp->num_tx_ring - 1)
                txdesc->status |= cpu_to_edmac(mdp, TD_TACT | TD_TDLE);
        else
index 06dbbe5201cbc915cf28401307cdaa5f0d2dc91f..50382b1c9ddcf2e5b656c98236c0e7364ee3c1f2 100644 (file)
@@ -285,7 +285,7 @@ enum DMAC_IM_BIT {
 
 /* Receive descriptor bit */
 enum RD_STS_BIT {
-       RD_RACT = 0x80000000, RD_RDEL = 0x40000000,
+       RD_RACT = 0x80000000, RD_RDLE = 0x40000000,
        RD_RFP1 = 0x20000000, RD_RFP0 = 0x10000000,
        RD_RFE = 0x08000000, RD_RFS10 = 0x00000200,
        RD_RFS9 = 0x00000100, RD_RFS8 = 0x00000080,
index 34ac41ac9e610b63e5bf3db276f64b215cce733c..32a80d2df7ffc161e86feab52ee850c2660afcf9 100644 (file)
@@ -152,8 +152,9 @@ struct rocker_fdb_tbl_entry {
        struct hlist_node entry;
        u32 key_crc32; /* key */
        bool learned;
+       unsigned long touched;
        struct rocker_fdb_tbl_key {
-               u32 pport;
+               struct rocker_port *rocker_port;
                u8 addr[ETH_ALEN];
                __be16 vlan_id;
        } key;
@@ -220,13 +221,13 @@ struct rocker_port {
        __be16 internal_vlan_id;
        int stp_state;
        u32 brport_flags;
+       unsigned long ageing_time;
        bool ctrls[ROCKER_CTRL_MAX];
        unsigned long vlan_bitmap[ROCKER_VLAN_BITMAP_LEN];
        struct napi_struct napi_tx;
        struct napi_struct napi_rx;
        struct rocker_dma_ring_info tx_ring;
        struct rocker_dma_ring_info rx_ring;
-       struct list_head trans_mem;
 };
 
 struct rocker {
@@ -246,6 +247,7 @@ struct rocker {
        u64 flow_tbl_next_cookie;
        DECLARE_HASHTABLE(group_tbl, 16);
        spinlock_t group_tbl_lock;              /* for group tbl accesses */
+       struct timer_list fdb_cleanup_timer;
        DECLARE_HASHTABLE(fdb_tbl, 16);
        spinlock_t fdb_tbl_lock;                /* for fdb tbl accesses */
        unsigned long internal_vlan_bitmap[ROCKER_INTERNAL_VLAN_BITMAP_LEN];
@@ -340,74 +342,63 @@ static bool rocker_port_is_ovsed(const struct rocker_port *rocker_port)
 #define ROCKER_OP_FLAG_REFRESH         BIT(3)
 
 static void *__rocker_port_mem_alloc(struct rocker_port *rocker_port,
-                                    enum switchdev_trans trans, int flags,
+                                    struct switchdev_trans *trans, int flags,
                                     size_t size)
 {
-       struct list_head *elem = NULL;
+       struct switchdev_trans_item *elem = NULL;
        gfp_t gfp_flags = (flags & ROCKER_OP_FLAG_NOWAIT) ?
                          GFP_ATOMIC : GFP_KERNEL;
 
        /* If in transaction prepare phase, allocate the memory
-        * and enqueue it on a per-port list.  If in transaction
-        * commit phase, dequeue the memory from the per-port list
+        * and enqueue it on a transaction.  If in transaction
+        * commit phase, dequeue the memory from the transaction
         * rather than re-allocating the memory.  The idea is the
         * driver code paths for prepare and commit are identical
         * so the memory allocated in the prepare phase is the
         * memory used in the commit phase.
         */
 
-       switch (trans) {
-       case SWITCHDEV_TRANS_PREPARE:
+       if (!trans) {
+               elem = kzalloc(size + sizeof(*elem), gfp_flags);
+       } else if (switchdev_trans_ph_prepare(trans)) {
                elem = kzalloc(size + sizeof(*elem), gfp_flags);
                if (!elem)
                        return NULL;
-               list_add_tail(elem, &rocker_port->trans_mem);
-               break;
-       case SWITCHDEV_TRANS_COMMIT:
-               BUG_ON(list_empty(&rocker_port->trans_mem));
-               elem = rocker_port->trans_mem.next;
-               list_del_init(elem);
-               break;
-       case SWITCHDEV_TRANS_NONE:
-               elem = kzalloc(size + sizeof(*elem), gfp_flags);
-               if (elem)
-                       INIT_LIST_HEAD(elem);
-               break;
-       default:
-               break;
+               switchdev_trans_item_enqueue(trans, elem, kfree, elem);
+       } else {
+               elem = switchdev_trans_item_dequeue(trans);
        }
 
        return elem ? elem + 1 : NULL;
 }
 
 static void *rocker_port_kzalloc(struct rocker_port *rocker_port,
-                                enum switchdev_trans trans, int flags,
+                                struct switchdev_trans *trans, int flags,
                                 size_t size)
 {
        return __rocker_port_mem_alloc(rocker_port, trans, flags, size);
 }
 
 static void *rocker_port_kcalloc(struct rocker_port *rocker_port,
-                                enum switchdev_trans trans, int flags,
+                                struct switchdev_trans *trans, int flags,
                                 size_t n, size_t size)
 {
        return __rocker_port_mem_alloc(rocker_port, trans, flags, n * size);
 }
 
-static void rocker_port_kfree(enum switchdev_trans trans, const void *mem)
+static void rocker_port_kfree(struct switchdev_trans *trans, const void *mem)
 {
-       struct list_head *elem;
+       struct switchdev_trans_item *elem;
 
        /* Frees are ignored if in transaction prepare phase.  The
         * memory remains on the per-port list until freed in the
         * commit phase.
         */
 
-       if (trans == SWITCHDEV_TRANS_PREPARE)
+       if (switchdev_trans_ph_prepare(trans))
                return;
 
-       elem = (struct list_head *)mem - 1;
-       BUG_ON(!list_empty(elem));
+       elem = (struct switchdev_trans_item *) mem - 1;
        kfree(elem);
 }
 
@@ -430,7 +421,7 @@ static void rocker_wait_init(struct rocker_wait *wait)
 }
 
 static struct rocker_wait *rocker_wait_create(struct rocker_port *rocker_port,
-                                             enum switchdev_trans trans,
+                                             struct switchdev_trans *trans,
                                              int flags)
 {
        struct rocker_wait *wait;
@@ -442,7 +433,7 @@ static struct rocker_wait *rocker_wait_create(struct rocker_port *rocker_port,
        return wait;
 }
 
-static void rocker_wait_destroy(enum switchdev_trans trans,
+static void rocker_wait_destroy(struct switchdev_trans *trans,
                                struct rocker_wait *wait)
 {
        rocker_port_kfree(trans, wait);
@@ -1408,7 +1399,7 @@ static irqreturn_t rocker_cmd_irq_handler(int irq, void *dev_id)
                wait = rocker_desc_cookie_ptr_get(desc_info);
                if (wait->nowait) {
                        rocker_desc_gen_clear(desc_info);
-                       rocker_wait_destroy(SWITCHDEV_TRANS_NONE, wait);
+                       rocker_wait_destroy(NULL, wait);
                } else {
                        rocker_wait_wake_up(wait);
                }
@@ -1463,7 +1454,7 @@ static int rocker_event_link_change(const struct rocker *rocker,
 }
 
 static int rocker_port_fdb(struct rocker_port *rocker_port,
-                          enum switchdev_trans trans,
+                          struct switchdev_trans *trans,
                           const unsigned char *addr,
                           __be16 vlan_id, int flags);
 
@@ -1496,8 +1487,7 @@ static int rocker_event_mac_vlan_seen(const struct rocker *rocker,
            rocker_port->stp_state != BR_STATE_FORWARDING)
                return 0;
 
-       return rocker_port_fdb(rocker_port, SWITCHDEV_TRANS_NONE,
-                              addr, vlan_id, flags);
+       return rocker_port_fdb(rocker_port, NULL, addr, vlan_id, flags);
 }
 
 static int rocker_event_process(const struct rocker *rocker,
@@ -1582,7 +1572,7 @@ typedef int (*rocker_cmd_proc_cb_t)(const struct rocker_port *rocker_port,
                                    void *priv);
 
 static int rocker_cmd_exec(struct rocker_port *rocker_port,
-                          enum switchdev_trans trans, int flags,
+                          struct switchdev_trans *trans, int flags,
                           rocker_cmd_prep_cb_t prepare, void *prepare_priv,
                           rocker_cmd_proc_cb_t process, void *process_priv)
 {
@@ -1615,7 +1605,7 @@ static int rocker_cmd_exec(struct rocker_port *rocker_port,
 
        rocker_desc_cookie_ptr_set(desc_info, wait);
 
-       if (trans != SWITCHDEV_TRANS_PREPARE)
+       if (!switchdev_trans_ph_prepare(trans))
                rocker_desc_head_set(rocker, &rocker->cmd_ring, desc_info);
 
        spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags);
@@ -1623,7 +1613,7 @@ static int rocker_cmd_exec(struct rocker_port *rocker_port,
        if (nowait)
                return 0;
 
-       if (trans != SWITCHDEV_TRANS_PREPARE)
+       if (!switchdev_trans_ph_prepare(trans))
                if (!rocker_wait_event_timeout(wait, HZ / 10))
                        return -EIO;
 
@@ -1875,7 +1865,7 @@ rocker_cmd_set_port_learning_prep(const struct rocker_port *rocker_port,
 static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port,
                                                struct ethtool_cmd *ecmd)
 {
-       return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0,
+       return rocker_cmd_exec(rocker_port, NULL, 0,
                               rocker_cmd_get_port_settings_prep, NULL,
                               rocker_cmd_get_port_settings_ethtool_proc,
                               ecmd);
@@ -1884,7 +1874,7 @@ static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port,
 static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port,
                                                unsigned char *macaddr)
 {
-       return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0,
+       return rocker_cmd_exec(rocker_port, NULL, 0,
                               rocker_cmd_get_port_settings_prep, NULL,
                               rocker_cmd_get_port_settings_macaddr_proc,
                               macaddr);
@@ -1893,7 +1883,7 @@ static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port,
 static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port,
                                                struct ethtool_cmd *ecmd)
 {
-       return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0,
+       return rocker_cmd_exec(rocker_port, NULL, 0,
                               rocker_cmd_set_port_settings_ethtool_prep,
                               ecmd, NULL, NULL);
 }
@@ -1901,7 +1891,7 @@ static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port,
 static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port,
                                                unsigned char *macaddr)
 {
-       return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0,
+       return rocker_cmd_exec(rocker_port, NULL, 0,
                               rocker_cmd_set_port_settings_macaddr_prep,
                               macaddr, NULL, NULL);
 }
@@ -1909,13 +1899,13 @@ static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port,
 static int rocker_cmd_set_port_settings_mtu(struct rocker_port *rocker_port,
                                            int mtu)
 {
-       return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0,
+       return rocker_cmd_exec(rocker_port, NULL, 0,
                               rocker_cmd_set_port_settings_mtu_prep,
                               &mtu, NULL, NULL);
 }
 
 static int rocker_port_set_learning(struct rocker_port *rocker_port,
-                                   enum switchdev_trans trans)
+                                   struct switchdev_trans *trans)
 {
        return rocker_cmd_exec(rocker_port, trans, 0,
                               rocker_cmd_set_port_learning_prep,
@@ -2433,7 +2423,7 @@ rocker_flow_tbl_find(const struct rocker *rocker,
 }
 
 static int rocker_flow_tbl_add(struct rocker_port *rocker_port,
-                              enum switchdev_trans trans, int flags,
+                              struct switchdev_trans *trans, int flags,
                               struct rocker_flow_tbl_entry *match)
 {
        struct rocker *rocker = rocker_port->rocker;
@@ -2449,7 +2439,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port,
 
        if (found) {
                match->cookie = found->cookie;
-               if (trans != SWITCHDEV_TRANS_PREPARE)
+               if (!switchdev_trans_ph_prepare(trans))
                        hash_del(&found->entry);
                rocker_port_kfree(trans, found);
                found = match;
@@ -2460,7 +2450,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port,
                found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD;
        }
 
-       if (trans != SWITCHDEV_TRANS_PREPARE)
+       if (!switchdev_trans_ph_prepare(trans))
                hash_add(rocker->flow_tbl, &found->entry, found->key_crc32);
 
        spin_unlock_irqrestore(&rocker->flow_tbl_lock, lock_flags);
@@ -2470,7 +2460,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port,
 }
 
 static int rocker_flow_tbl_del(struct rocker_port *rocker_port,
-                              enum switchdev_trans trans, int flags,
+                              struct switchdev_trans *trans, int flags,
                               struct rocker_flow_tbl_entry *match)
 {
        struct rocker *rocker = rocker_port->rocker;
@@ -2486,7 +2476,7 @@ static int rocker_flow_tbl_del(struct rocker_port *rocker_port,
        found = rocker_flow_tbl_find(rocker, match);
 
        if (found) {
-               if (trans != SWITCHDEV_TRANS_PREPARE)
+               if (!switchdev_trans_ph_prepare(trans))
                        hash_del(&found->entry);
                found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL;
        }
@@ -2506,7 +2496,7 @@ static int rocker_flow_tbl_del(struct rocker_port *rocker_port,
 }
 
 static int rocker_flow_tbl_do(struct rocker_port *rocker_port,
-                             enum switchdev_trans trans, int flags,
+                             struct switchdev_trans *trans, int flags,
                              struct rocker_flow_tbl_entry *entry)
 {
        if (flags & ROCKER_OP_FLAG_REMOVE)
@@ -2516,7 +2506,7 @@ static int rocker_flow_tbl_do(struct rocker_port *rocker_port,
 }
 
 static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port,
-                                  enum switchdev_trans trans, int flags,
+                                  struct switchdev_trans *trans, int flags,
                                   u32 in_pport, u32 in_pport_mask,
                                   enum rocker_of_dpa_table_id goto_tbl)
 {
@@ -2536,7 +2526,7 @@ static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port,
 }
 
 static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port,
-                               enum switchdev_trans trans, int flags,
+                               struct switchdev_trans *trans, int flags,
                                u32 in_pport, __be16 vlan_id,
                                __be16 vlan_id_mask,
                                enum rocker_of_dpa_table_id goto_tbl,
@@ -2562,7 +2552,7 @@ static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port,
 }
 
 static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port,
-                                   enum switchdev_trans trans,
+                                   struct switchdev_trans *trans,
                                    u32 in_pport, u32 in_pport_mask,
                                    __be16 eth_type, const u8 *eth_dst,
                                    const u8 *eth_dst_mask, __be16 vlan_id,
@@ -2599,7 +2589,7 @@ static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port,
 }
 
 static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port,
-                                 enum switchdev_trans trans, int flags,
+                                 struct switchdev_trans *trans, int flags,
                                  const u8 *eth_dst, const u8 *eth_dst_mask,
                                  __be16 vlan_id, u32 tunnel_id,
                                  enum rocker_of_dpa_table_id goto_tbl,
@@ -2653,7 +2643,7 @@ static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port,
 }
 
 static int rocker_flow_tbl_ucast4_routing(struct rocker_port *rocker_port,
-                                         enum switchdev_trans trans,
+                                         struct switchdev_trans *trans,
                                          __be16 eth_type, __be32 dst,
                                          __be32 dst_mask, u32 priority,
                                          enum rocker_of_dpa_table_id goto_tbl,
@@ -2679,7 +2669,7 @@ static int rocker_flow_tbl_ucast4_routing(struct rocker_port *rocker_port,
 }
 
 static int rocker_flow_tbl_acl(struct rocker_port *rocker_port,
-                              enum switchdev_trans trans, int flags,
+                              struct switchdev_trans *trans, int flags,
                               u32 in_pport, u32 in_pport_mask,
                               const u8 *eth_src, const u8 *eth_src_mask,
                               const u8 *eth_dst, const u8 *eth_dst_mask,
@@ -2744,7 +2734,7 @@ rocker_group_tbl_find(const struct rocker *rocker,
        return NULL;
 }
 
-static void rocker_group_tbl_entry_free(enum switchdev_trans trans,
+static void rocker_group_tbl_entry_free(struct switchdev_trans *trans,
                                        struct rocker_group_tbl_entry *entry)
 {
        switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) {
@@ -2759,7 +2749,7 @@ static void rocker_group_tbl_entry_free(enum switchdev_trans trans,
 }
 
 static int rocker_group_tbl_add(struct rocker_port *rocker_port,
-                               enum switchdev_trans trans, int flags,
+                               struct switchdev_trans *trans, int flags,
                                struct rocker_group_tbl_entry *match)
 {
        struct rocker *rocker = rocker_port->rocker;
@@ -2771,7 +2761,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port,
        found = rocker_group_tbl_find(rocker, match);
 
        if (found) {
-               if (trans != SWITCHDEV_TRANS_PREPARE)
+               if (!switchdev_trans_ph_prepare(trans))
                        hash_del(&found->entry);
                rocker_group_tbl_entry_free(trans, found);
                found = match;
@@ -2781,7 +2771,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port,
                found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD;
        }
 
-       if (trans != SWITCHDEV_TRANS_PREPARE)
+       if (!switchdev_trans_ph_prepare(trans))
                hash_add(rocker->group_tbl, &found->entry, found->group_id);
 
        spin_unlock_irqrestore(&rocker->group_tbl_lock, lock_flags);
@@ -2791,7 +2781,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port,
 }
 
 static int rocker_group_tbl_del(struct rocker_port *rocker_port,
-                               enum switchdev_trans trans, int flags,
+                               struct switchdev_trans *trans, int flags,
                                struct rocker_group_tbl_entry *match)
 {
        struct rocker *rocker = rocker_port->rocker;
@@ -2804,7 +2794,7 @@ static int rocker_group_tbl_del(struct rocker_port *rocker_port,
        found = rocker_group_tbl_find(rocker, match);
 
        if (found) {
-               if (trans != SWITCHDEV_TRANS_PREPARE)
+               if (!switchdev_trans_ph_prepare(trans))
                        hash_del(&found->entry);
                found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL;
        }
@@ -2824,7 +2814,7 @@ static int rocker_group_tbl_del(struct rocker_port *rocker_port,
 }
 
 static int rocker_group_tbl_do(struct rocker_port *rocker_port,
-                              enum switchdev_trans trans, int flags,
+                              struct switchdev_trans *trans, int flags,
                               struct rocker_group_tbl_entry *entry)
 {
        if (flags & ROCKER_OP_FLAG_REMOVE)
@@ -2834,7 +2824,7 @@ static int rocker_group_tbl_do(struct rocker_port *rocker_port,
 }
 
 static int rocker_group_l2_interface(struct rocker_port *rocker_port,
-                                    enum switchdev_trans trans, int flags,
+                                    struct switchdev_trans *trans, int flags,
                                     __be16 vlan_id, u32 out_pport,
                                     int pop_vlan)
 {
@@ -2851,7 +2841,7 @@ static int rocker_group_l2_interface(struct rocker_port *rocker_port,
 }
 
 static int rocker_group_l2_fan_out(struct rocker_port *rocker_port,
-                                  enum switchdev_trans trans,
+                                  struct switchdev_trans *trans,
                                   int flags, u8 group_count,
                                   const u32 *group_ids, u32 group_id)
 {
@@ -2876,7 +2866,7 @@ static int rocker_group_l2_fan_out(struct rocker_port *rocker_port,
 }
 
 static int rocker_group_l2_flood(struct rocker_port *rocker_port,
-                                enum switchdev_trans trans, int flags,
+                                struct switchdev_trans *trans, int flags,
                                 __be16 vlan_id, u8 group_count,
                                 const u32 *group_ids, u32 group_id)
 {
@@ -2886,7 +2876,7 @@ static int rocker_group_l2_flood(struct rocker_port *rocker_port,
 }
 
 static int rocker_group_l3_unicast(struct rocker_port *rocker_port,
-                                  enum switchdev_trans trans, int flags,
+                                  struct switchdev_trans *trans, int flags,
                                   u32 index, const u8 *src_mac, const u8 *dst_mac,
                                   __be16 vlan_id, bool ttl_check, u32 pport)
 {
@@ -2922,22 +2912,22 @@ rocker_neigh_tbl_find(const struct rocker *rocker, __be32 ip_addr)
 }
 
 static void _rocker_neigh_add(struct rocker *rocker,
-                             enum switchdev_trans trans,
+                             struct switchdev_trans *trans,
                              struct rocker_neigh_tbl_entry *entry)
 {
-       if (trans != SWITCHDEV_TRANS_COMMIT)
+       if (!switchdev_trans_ph_commit(trans))
                entry->index = rocker->neigh_tbl_next_index++;
-       if (trans == SWITCHDEV_TRANS_PREPARE)
+       if (switchdev_trans_ph_prepare(trans))
                return;
        entry->ref_count++;
        hash_add(rocker->neigh_tbl, &entry->entry,
                 be32_to_cpu(entry->ip_addr));
 }
 
-static void _rocker_neigh_del(enum switchdev_trans trans,
+static void _rocker_neigh_del(struct switchdev_trans *trans,
                              struct rocker_neigh_tbl_entry *entry)
 {
-       if (trans == SWITCHDEV_TRANS_PREPARE)
+       if (switchdev_trans_ph_prepare(trans))
                return;
        if (--entry->ref_count == 0) {
                hash_del(&entry->entry);
@@ -2946,19 +2936,19 @@ static void _rocker_neigh_del(enum switchdev_trans trans,
 }
 
 static void _rocker_neigh_update(struct rocker_neigh_tbl_entry *entry,
-                                enum switchdev_trans trans,
+                                struct switchdev_trans *trans,
                                 const u8 *eth_dst, bool ttl_check)
 {
        if (eth_dst) {
                ether_addr_copy(entry->eth_dst, eth_dst);
                entry->ttl_check = ttl_check;
-       } else if (trans != SWITCHDEV_TRANS_PREPARE) {
+       } else if (!switchdev_trans_ph_prepare(trans)) {
                entry->ref_count++;
        }
 }
 
 static int rocker_port_ipv4_neigh(struct rocker_port *rocker_port,
-                                 enum switchdev_trans trans,
+                                 struct switchdev_trans *trans,
                                  int flags, __be32 ip_addr, const u8 *eth_dst)
 {
        struct rocker *rocker = rocker_port->rocker;
@@ -3050,7 +3040,8 @@ err_out:
 }
 
 static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port,
-                                   enum switchdev_trans trans, __be32 ip_addr)
+                                   struct switchdev_trans *trans,
+                                   __be32 ip_addr)
 {
        struct net_device *dev = rocker_port->dev;
        struct neighbour *n = __ipv4_neigh_lookup(dev, (__force u32)ip_addr);
@@ -3078,7 +3069,7 @@ static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_ipv4_nh(struct rocker_port *rocker_port,
-                              enum switchdev_trans trans, int flags,
+                              struct switchdev_trans *trans, int flags,
                               __be32 ip_addr, u32 *index)
 {
        struct rocker *rocker = rocker_port->rocker;
@@ -3137,7 +3128,7 @@ static int rocker_port_ipv4_nh(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_vlan_flood_group(struct rocker_port *rocker_port,
-                                       enum switchdev_trans trans,
+                                       struct switchdev_trans *trans,
                                        int flags, __be16 vlan_id)
 {
        struct rocker_port *p;
@@ -3186,7 +3177,7 @@ no_ports_in_vlan:
 }
 
 static int rocker_port_vlan_l2_groups(struct rocker_port *rocker_port,
-                                     enum switchdev_trans trans, int flags,
+                                     struct switchdev_trans *trans, int flags,
                                      __be16 vlan_id, bool pop_vlan)
 {
        const struct rocker *rocker = rocker_port->rocker;
@@ -3292,7 +3283,7 @@ static struct rocker_ctrl {
 };
 
 static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port,
-                                    enum switchdev_trans trans, int flags,
+                                    struct switchdev_trans *trans, int flags,
                                     const struct rocker_ctrl *ctrl, __be16 vlan_id)
 {
        u32 in_pport = rocker_port->pport;
@@ -3325,7 +3316,8 @@ static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_ctrl_vlan_bridge(struct rocker_port *rocker_port,
-                                       enum switchdev_trans trans, int flags,
+                                       struct switchdev_trans *trans,
+                                       int flags,
                                        const struct rocker_ctrl *ctrl,
                                        __be16 vlan_id)
 {
@@ -3350,7 +3342,7 @@ static int rocker_port_ctrl_vlan_bridge(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_ctrl_vlan_term(struct rocker_port *rocker_port,
-                                     enum switchdev_trans trans, int flags,
+                                     struct switchdev_trans *trans, int flags,
                                      const struct rocker_ctrl *ctrl, __be16 vlan_id)
 {
        u32 in_pport_mask = 0xffffffff;
@@ -3374,7 +3366,7 @@ static int rocker_port_ctrl_vlan_term(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_ctrl_vlan(struct rocker_port *rocker_port,
-                                enum switchdev_trans trans, int flags,
+                                struct switchdev_trans *trans, int flags,
                                 const struct rocker_ctrl *ctrl, __be16 vlan_id)
 {
        if (ctrl->acl)
@@ -3392,7 +3384,7 @@ static int rocker_port_ctrl_vlan(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_ctrl_vlan_add(struct rocker_port *rocker_port,
-                                    enum switchdev_trans trans, int flags,
+                                    struct switchdev_trans *trans, int flags,
                                     __be16 vlan_id)
 {
        int err = 0;
@@ -3411,7 +3403,7 @@ static int rocker_port_ctrl_vlan_add(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_ctrl(struct rocker_port *rocker_port,
-                           enum switchdev_trans trans, int flags,
+                           struct switchdev_trans *trans, int flags,
                            const struct rocker_ctrl *ctrl)
 {
        u16 vid;
@@ -3430,7 +3422,7 @@ static int rocker_port_ctrl(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_vlan(struct rocker_port *rocker_port,
-                           enum switchdev_trans trans, int flags, u16 vid)
+                           struct switchdev_trans *trans, int flags, u16 vid)
 {
        enum rocker_of_dpa_table_id goto_tbl =
                ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC;
@@ -3487,14 +3479,14 @@ static int rocker_port_vlan(struct rocker_port *rocker_port,
                           "Error (%d) port VLAN table\n", err);
 
 err_out:
-       if (trans == SWITCHDEV_TRANS_PREPARE)
+       if (switchdev_trans_ph_prepare(trans))
                change_bit(ntohs(internal_vlan_id), rocker_port->vlan_bitmap);
 
        return err;
 }
 
 static int rocker_port_ig_tbl(struct rocker_port *rocker_port,
-                             enum switchdev_trans trans, int flags)
+                             struct switchdev_trans *trans, int flags)
 {
        enum rocker_of_dpa_table_id goto_tbl;
        u32 in_pport;
@@ -3522,7 +3514,7 @@ static int rocker_port_ig_tbl(struct rocker_port *rocker_port,
 struct rocker_fdb_learn_work {
        struct work_struct work;
        struct rocker_port *rocker_port;
-       enum switchdev_trans trans;
+       struct switchdev_trans *trans;
        int flags;
        u8 addr[ETH_ALEN];
        u16 vid;
@@ -3550,7 +3542,7 @@ static void rocker_port_fdb_learn_work(struct work_struct *work)
 }
 
 static int rocker_port_fdb_learn(struct rocker_port *rocker_port,
-                                enum switchdev_trans trans, int flags,
+                                struct switchdev_trans *trans, int flags,
                                 const u8 *addr, __be16 vlan_id)
 {
        struct rocker_fdb_learn_work *lw;
@@ -3592,7 +3584,7 @@ static int rocker_port_fdb_learn(struct rocker_port *rocker_port,
        ether_addr_copy(lw->addr, addr);
        lw->vid = rocker_port_vlan_to_vid(rocker_port, vlan_id);
 
-       if (trans == SWITCHDEV_TRANS_PREPARE)
+       if (switchdev_trans_ph_prepare(trans))
                rocker_port_kfree(trans, lw);
        else
                schedule_work(&lw->work);
@@ -3614,7 +3606,7 @@ rocker_fdb_tbl_find(const struct rocker *rocker,
 }
 
 static int rocker_port_fdb(struct rocker_port *rocker_port,
-                          enum switchdev_trans trans,
+                          struct switchdev_trans *trans,
                           const unsigned char *addr,
                           __be16 vlan_id, int flags)
 {
@@ -3629,7 +3621,8 @@ static int rocker_port_fdb(struct rocker_port *rocker_port,
                return -ENOMEM;
 
        fdb->learned = (flags & ROCKER_OP_FLAG_LEARNED);
-       fdb->key.pport = rocker_port->pport;
+       fdb->touched = jiffies;
+       fdb->key.rocker_port = rocker_port;
        ether_addr_copy(fdb->key.addr, addr);
        fdb->key.vlan_id = vlan_id;
        fdb->key_crc32 = crc32(~0, &fdb->key, sizeof(fdb->key));
@@ -3638,13 +3631,17 @@ static int rocker_port_fdb(struct rocker_port *rocker_port,
 
        found = rocker_fdb_tbl_find(rocker, fdb);
 
-       if (removing && found) {
-               rocker_port_kfree(trans, fdb);
-               if (trans != SWITCHDEV_TRANS_PREPARE)
-                       hash_del(&found->entry);
-       } else if (!removing && !found) {
-               if (trans != SWITCHDEV_TRANS_PREPARE)
-                       hash_add(rocker->fdb_tbl, &fdb->entry, fdb->key_crc32);
+       if (found) {
+               found->touched = jiffies;
+               if (removing) {
+                       rocker_port_kfree(trans, fdb);
+                       if (!switchdev_trans_ph_prepare(trans))
+                               hash_del(&found->entry);
+               }
+       } else if (!removing) {
+               if (!switchdev_trans_ph_prepare(trans))
+                       hash_add(rocker->fdb_tbl, &fdb->entry,
+                                fdb->key_crc32);
        }
 
        spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags);
@@ -3662,7 +3659,7 @@ static int rocker_port_fdb(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_fdb_flush(struct rocker_port *rocker_port,
-                                enum switchdev_trans trans, int flags)
+                                struct switchdev_trans *trans, int flags)
 {
        struct rocker *rocker = rocker_port->rocker;
        struct rocker_fdb_tbl_entry *found;
@@ -3675,12 +3672,12 @@ static int rocker_port_fdb_flush(struct rocker_port *rocker_port,
            rocker_port->stp_state == BR_STATE_FORWARDING)
                return 0;
 
-       flags |= ROCKER_OP_FLAG_REMOVE;
+       flags |= ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE;
 
        spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
 
        hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) {
-               if (found->key.pport != rocker_port->pport)
+               if (found->key.rocker_port != rocker_port)
                        continue;
                if (!found->learned)
                        continue;
@@ -3689,7 +3686,7 @@ static int rocker_port_fdb_flush(struct rocker_port *rocker_port,
                                            found->key.vlan_id);
                if (err)
                        goto err_out;
-               if (trans != SWITCHDEV_TRANS_PREPARE)
+               if (!switchdev_trans_ph_prepare(trans))
                        hash_del(&found->entry);
        }
 
@@ -3699,8 +3696,43 @@ err_out:
        return err;
 }
 
+static void rocker_fdb_cleanup(unsigned long data)
+{
+       struct rocker *rocker = (struct rocker *)data;
+       struct rocker_port *rocker_port;
+       struct rocker_fdb_tbl_entry *entry;
+       struct hlist_node *tmp;
+       unsigned long next_timer = jiffies + BR_MIN_AGEING_TIME;
+       unsigned long expires;
+       unsigned long lock_flags;
+       int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE |
+                   ROCKER_OP_FLAG_LEARNED;
+       int bkt;
+
+       spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
+
+       hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, entry, entry) {
+               if (!entry->learned)
+                       continue;
+               rocker_port = entry->key.rocker_port;
+               expires = entry->touched + rocker_port->ageing_time;
+               if (time_before_eq(expires, jiffies)) {
+                       rocker_port_fdb_learn(rocker_port, NULL,
+                                             flags, entry->key.addr,
+                                             entry->key.vlan_id);
+                       hash_del(&entry->entry);
+               } else if (time_before(expires, next_timer)) {
+                       next_timer = expires;
+               }
+       }
+
+       spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags);
+
+       mod_timer(&rocker->fdb_cleanup_timer, round_jiffies_up(next_timer));
+}
+
 static int rocker_port_router_mac(struct rocker_port *rocker_port,
-                                 enum switchdev_trans trans, int flags,
+                                 struct switchdev_trans *trans, int flags,
                                  __be16 vlan_id)
 {
        u32 in_pport_mask = 0xffffffff;
@@ -3733,7 +3765,7 @@ static int rocker_port_router_mac(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_fwding(struct rocker_port *rocker_port,
-                             enum switchdev_trans trans, int flags)
+                             struct switchdev_trans *trans, int flags)
 {
        bool pop_vlan;
        u32 out_pport;
@@ -3772,16 +3804,16 @@ static int rocker_port_fwding(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_stp_update(struct rocker_port *rocker_port,
-                                 enum switchdev_trans trans, int flags,
+                                 struct switchdev_trans *trans, int flags,
                                  u8 state)
 {
        bool want[ROCKER_CTRL_MAX] = { 0, };
        bool prev_ctrls[ROCKER_CTRL_MAX];
-       u8 prev_state;
+       u8 uninitialized_var(prev_state);
        int err;
        int i;
 
-       if (trans == SWITCHDEV_TRANS_PREPARE) {
+       if (switchdev_trans_ph_prepare(trans)) {
                memcpy(prev_ctrls, rocker_port->ctrls, sizeof(prev_ctrls));
                prev_state = rocker_port->stp_state;
        }
@@ -3833,7 +3865,7 @@ static int rocker_port_stp_update(struct rocker_port *rocker_port,
        err = rocker_port_fwding(rocker_port, trans, flags);
 
 err_out:
-       if (trans == SWITCHDEV_TRANS_PREPARE) {
+       if (switchdev_trans_ph_prepare(trans)) {
                memcpy(rocker_port->ctrls, prev_ctrls, sizeof(prev_ctrls));
                rocker_port->stp_state = prev_state;
        }
@@ -3842,7 +3874,7 @@ err_out:
 }
 
 static int rocker_port_fwd_enable(struct rocker_port *rocker_port,
-                                 enum switchdev_trans trans, int flags)
+                                 struct switchdev_trans *trans, int flags)
 {
        if (rocker_port_is_bridged(rocker_port))
                /* bridge STP will enable port */
@@ -3854,7 +3886,7 @@ static int rocker_port_fwd_enable(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_fwd_disable(struct rocker_port *rocker_port,
-                                  enum switchdev_trans trans, int flags)
+                                  struct switchdev_trans *trans, int flags)
 {
        if (rocker_port_is_bridged(rocker_port))
                /* bridge STP will disable port */
@@ -3952,7 +3984,7 @@ not_found:
 }
 
 static int rocker_port_fib_ipv4(struct rocker_port *rocker_port,
-                               enum switchdev_trans trans, __be32 dst,
+                               struct switchdev_trans *trans, __be32 dst,
                                int dst_len, const struct fib_info *fi,
                                u32 tb_id, int flags)
 {
@@ -4026,7 +4058,7 @@ static int rocker_port_open(struct net_device *dev)
                goto err_request_rx_irq;
        }
 
-       err = rocker_port_fwd_enable(rocker_port, SWITCHDEV_TRANS_NONE, 0);
+       err = rocker_port_fwd_enable(rocker_port, NULL, 0);
        if (err)
                goto err_fwd_enable;
 
@@ -4054,7 +4086,7 @@ static int rocker_port_stop(struct net_device *dev)
        rocker_port_set_enable(rocker_port, false);
        napi_disable(&rocker_port->napi_rx);
        napi_disable(&rocker_port->napi_tx);
-       rocker_port_fwd_disable(rocker_port, SWITCHDEV_TRANS_NONE,
+       rocker_port_fwd_disable(rocker_port, NULL,
                                ROCKER_OP_FLAG_NOWAIT);
        free_irq(rocker_msix_rx_vector(rocker_port), rocker_port);
        free_irq(rocker_msix_tx_vector(rocker_port), rocker_port);
@@ -4240,7 +4272,7 @@ static int rocker_port_get_phys_port_name(struct net_device *dev,
        struct port_name name = { .buf = buf, .len = len };
        int err;
 
-       err = rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0,
+       err = rocker_cmd_exec(rocker_port, NULL, 0,
                              rocker_cmd_get_port_settings_prep, NULL,
                              rocker_cmd_get_port_settings_phys_name_proc,
                              &name);
@@ -4265,7 +4297,7 @@ static void rocker_port_neigh_destroy(struct neighbour *n)
        int flags = ROCKER_OP_FLAG_REMOVE | ROCKER_OP_FLAG_NOWAIT;
        __be32 ip_addr = *(__be32 *)n->primary_key;
 
-       rocker_port_ipv4_neigh(rocker_port, SWITCHDEV_TRANS_NONE,
+       rocker_port_ipv4_neigh(rocker_port, NULL,
                               flags, ip_addr, n->ha);
 }
 
@@ -4297,11 +4329,11 @@ static int rocker_port_attr_get(struct net_device *dev,
        const struct rocker *rocker = rocker_port->rocker;
 
        switch (attr->id) {
-       case SWITCHDEV_ATTR_PORT_PARENT_ID:
+       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
                attr->u.ppid.id_len = sizeof(rocker->hw.id);
                memcpy(&attr->u.ppid.id, &rocker->hw.id, attr->u.ppid.id_len);
                break;
-       case SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS:
+       case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
                attr->u.brport_flags = rocker_port->brport_flags;
                break;
        default:
@@ -4311,18 +4343,8 @@ static int rocker_port_attr_get(struct net_device *dev,
        return 0;
 }
 
-static void rocker_port_trans_abort(const struct rocker_port *rocker_port)
-{
-       struct list_head *mem, *tmp;
-
-       list_for_each_safe(mem, tmp, &rocker_port->trans_mem) {
-               list_del(mem);
-               kfree(mem);
-       }
-}
-
 static int rocker_port_brport_flags_set(struct rocker_port *rocker_port,
-                                       enum switchdev_trans trans,
+                                       struct switchdev_trans *trans,
                                        unsigned long brport_flags)
 {
        unsigned long orig_flags;
@@ -4333,39 +4355,44 @@ static int rocker_port_brport_flags_set(struct rocker_port *rocker_port,
        if ((orig_flags ^ rocker_port->brport_flags) & BR_LEARNING)
                err = rocker_port_set_learning(rocker_port, trans);
 
-       if (trans == SWITCHDEV_TRANS_PREPARE)
+       if (switchdev_trans_ph_prepare(trans))
                rocker_port->brport_flags = orig_flags;
 
        return err;
 }
 
+static int rocker_port_bridge_ageing_time(struct rocker_port *rocker_port,
+                                         struct switchdev_trans *trans,
+                                         u32 ageing_time)
+{
+       if (!switchdev_trans_ph_prepare(trans)) {
+               rocker_port->ageing_time = clock_t_to_jiffies(ageing_time);
+               mod_timer(&rocker_port->rocker->fdb_cleanup_timer, jiffies);
+       }
+
+       return 0;
+}
+
 static int rocker_port_attr_set(struct net_device *dev,
-                               struct switchdev_attr *attr)
+                               const struct switchdev_attr *attr,
+                               struct switchdev_trans *trans)
 {
        struct rocker_port *rocker_port = netdev_priv(dev);
        int err = 0;
 
-       switch (attr->trans) {
-       case SWITCHDEV_TRANS_PREPARE:
-               BUG_ON(!list_empty(&rocker_port->trans_mem));
-               break;
-       case SWITCHDEV_TRANS_ABORT:
-               rocker_port_trans_abort(rocker_port);
-               return 0;
-       default:
-               break;
-       }
-
        switch (attr->id) {
-       case SWITCHDEV_ATTR_PORT_STP_STATE:
-               err = rocker_port_stp_update(rocker_port, attr->trans,
-                                            ROCKER_OP_FLAG_NOWAIT,
+       case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+               err = rocker_port_stp_update(rocker_port, trans, 0,
                                             attr->u.stp_state);
                break;
-       case SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS:
-               err = rocker_port_brport_flags_set(rocker_port, attr->trans,
+       case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+               err = rocker_port_brport_flags_set(rocker_port, trans,
                                                   attr->u.brport_flags);
                break;
+       case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
+               err = rocker_port_bridge_ageing_time(rocker_port, trans,
+                                                    attr->u.ageing_time);
+               break;
        default:
                err = -EOPNOTSUPP;
                break;
@@ -4375,7 +4402,8 @@ static int rocker_port_attr_set(struct net_device *dev,
 }
 
 static int rocker_port_vlan_add(struct rocker_port *rocker_port,
-                               enum switchdev_trans trans, u16 vid, u16 flags)
+                               struct switchdev_trans *trans,
+                               u16 vid, u16 flags)
 {
        int err;
 
@@ -4394,8 +4422,8 @@ static int rocker_port_vlan_add(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_vlans_add(struct rocker_port *rocker_port,
-                                enum switchdev_trans trans,
-                                const struct switchdev_obj_vlan *vlan)
+                                struct switchdev_trans *trans,
+                                const struct switchdev_obj_port_vlan *vlan)
 {
        u16 vid;
        int err;
@@ -4411,8 +4439,8 @@ static int rocker_port_vlans_add(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_fdb_add(struct rocker_port *rocker_port,
-                              enum switchdev_trans trans,
-                              const struct switchdev_obj_fdb *fdb)
+                              struct switchdev_trans *trans,
+                              const struct switchdev_obj_port_fdb *fdb)
 {
        __be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL);
        int flags = 0;
@@ -4424,36 +4452,27 @@ static int rocker_port_fdb_add(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_obj_add(struct net_device *dev,
-                              struct switchdev_obj *obj)
+                              const struct switchdev_obj *obj,
+                              struct switchdev_trans *trans)
 {
        struct rocker_port *rocker_port = netdev_priv(dev);
        const struct switchdev_obj_ipv4_fib *fib4;
        int err = 0;
 
-       switch (obj->trans) {
-       case SWITCHDEV_TRANS_PREPARE:
-               BUG_ON(!list_empty(&rocker_port->trans_mem));
-               break;
-       case SWITCHDEV_TRANS_ABORT:
-               rocker_port_trans_abort(rocker_port);
-               return 0;
-       default:
-               break;
-       }
-
        switch (obj->id) {
-       case SWITCHDEV_OBJ_PORT_VLAN:
-               err = rocker_port_vlans_add(rocker_port, obj->trans,
-                                           &obj->u.vlan);
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               err = rocker_port_vlans_add(rocker_port, trans,
+                                           SWITCHDEV_OBJ_PORT_VLAN(obj));
                break;
-       case SWITCHDEV_OBJ_IPV4_FIB:
-               fib4 = &obj->u.ipv4_fib;
-               err = rocker_port_fib_ipv4(rocker_port, obj->trans,
+       case SWITCHDEV_OBJ_ID_IPV4_FIB:
+               fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj);
+               err = rocker_port_fib_ipv4(rocker_port, trans,
                                           htonl(fib4->dst), fib4->dst_len,
-                                          fib4->fi, fib4->tb_id, 0);
+                                          &fib4->fi, fib4->tb_id, 0);
                break;
-       case SWITCHDEV_OBJ_PORT_FDB:
-               err = rocker_port_fdb_add(rocker_port, obj->trans, &obj->u.fdb);
+       case SWITCHDEV_OBJ_ID_PORT_FDB:
+               err = rocker_port_fdb_add(rocker_port, trans,
+                                         SWITCHDEV_OBJ_PORT_FDB(obj));
                break;
        default:
                err = -EOPNOTSUPP;
@@ -4468,17 +4487,17 @@ static int rocker_port_vlan_del(struct rocker_port *rocker_port,
 {
        int err;
 
-       err = rocker_port_router_mac(rocker_port, SWITCHDEV_TRANS_NONE,
+       err = rocker_port_router_mac(rocker_port, NULL,
                                     ROCKER_OP_FLAG_REMOVE, htons(vid));
        if (err)
                return err;
 
-       return rocker_port_vlan(rocker_port, SWITCHDEV_TRANS_NONE,
+       return rocker_port_vlan(rocker_port, NULL,
                                ROCKER_OP_FLAG_REMOVE, vid);
 }
 
 static int rocker_port_vlans_del(struct rocker_port *rocker_port,
-                                const struct switchdev_obj_vlan *vlan)
+                                const struct switchdev_obj_port_vlan *vlan)
 {
        u16 vid;
        int err;
@@ -4493,11 +4512,11 @@ static int rocker_port_vlans_del(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_fdb_del(struct rocker_port *rocker_port,
-                              enum switchdev_trans trans,
-                              const struct switchdev_obj_fdb *fdb)
+                              struct switchdev_trans *trans,
+                              const struct switchdev_obj_port_fdb *fdb)
 {
        __be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL);
-       int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE;
+       int flags = ROCKER_OP_FLAG_REMOVE;
 
        if (!rocker_port_is_bridged(rocker_port))
                return -EINVAL;
@@ -4506,25 +4525,27 @@ static int rocker_port_fdb_del(struct rocker_port *rocker_port,
 }
 
 static int rocker_port_obj_del(struct net_device *dev,
-                              struct switchdev_obj *obj)
+                              const struct switchdev_obj *obj)
 {
        struct rocker_port *rocker_port = netdev_priv(dev);
        const struct switchdev_obj_ipv4_fib *fib4;
        int err = 0;
 
        switch (obj->id) {
-       case SWITCHDEV_OBJ_PORT_VLAN:
-               err = rocker_port_vlans_del(rocker_port, &obj->u.vlan);
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               err = rocker_port_vlans_del(rocker_port,
+                                           SWITCHDEV_OBJ_PORT_VLAN(obj));
                break;
-       case SWITCHDEV_OBJ_IPV4_FIB:
-               fib4 = &obj->u.ipv4_fib;
-               err = rocker_port_fib_ipv4(rocker_port, SWITCHDEV_TRANS_NONE,
+       case SWITCHDEV_OBJ_ID_IPV4_FIB:
+               fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj);
+               err = rocker_port_fib_ipv4(rocker_port, NULL,
                                           htonl(fib4->dst), fib4->dst_len,
-                                          fib4->fi, fib4->tb_id,
+                                          &fib4->fi, fib4->tb_id,
                                           ROCKER_OP_FLAG_REMOVE);
                break;
-       case SWITCHDEV_OBJ_PORT_FDB:
-               err = rocker_port_fdb_del(rocker_port, obj->trans, &obj->u.fdb);
+       case SWITCHDEV_OBJ_ID_PORT_FDB:
+               err = rocker_port_fdb_del(rocker_port, NULL,
+                                         SWITCHDEV_OBJ_PORT_FDB(obj));
                break;
        default:
                err = -EOPNOTSUPP;
@@ -4535,10 +4556,10 @@ static int rocker_port_obj_del(struct net_device *dev,
 }
 
 static int rocker_port_fdb_dump(const struct rocker_port *rocker_port,
-                               struct switchdev_obj *obj)
+                               struct switchdev_obj_port_fdb *fdb,
+                               switchdev_obj_dump_cb_t *cb)
 {
        struct rocker *rocker = rocker_port->rocker;
-       struct switchdev_obj_fdb *fdb = &obj->u.fdb;
        struct rocker_fdb_tbl_entry *found;
        struct hlist_node *tmp;
        unsigned long lock_flags;
@@ -4547,13 +4568,13 @@ static int rocker_port_fdb_dump(const struct rocker_port *rocker_port,
 
        spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
        hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) {
-               if (found->key.pport != rocker_port->pport)
+               if (found->key.rocker_port != rocker_port)
                        continue;
-               fdb->addr = found->key.addr;
+               ether_addr_copy(fdb->addr, found->key.addr);
                fdb->ndm_state = NUD_REACHABLE;
                fdb->vid = rocker_port_vlan_to_vid(rocker_port,
                                                   found->key.vlan_id);
-               err = obj->cb(rocker_port->dev, obj);
+               err = cb(&fdb->obj);
                if (err)
                        break;
        }
@@ -4563,9 +4584,9 @@ static int rocker_port_fdb_dump(const struct rocker_port *rocker_port,
 }
 
 static int rocker_port_vlan_dump(const struct rocker_port *rocker_port,
-                                struct switchdev_obj *obj)
+                                struct switchdev_obj_port_vlan *vlan,
+                                switchdev_obj_dump_cb_t *cb)
 {
-       struct switchdev_obj_vlan *vlan = &obj->u.vlan;
        u16 vid;
        int err = 0;
 
@@ -4576,7 +4597,7 @@ static int rocker_port_vlan_dump(const struct rocker_port *rocker_port,
                if (rocker_vlan_id_is_internal(htons(vid)))
                        vlan->flags |= BRIDGE_VLAN_INFO_PVID;
                vlan->vid_begin = vlan->vid_end = vid;
-               err = obj->cb(rocker_port->dev, obj);
+               err = cb(&vlan->obj);
                if (err)
                        break;
        }
@@ -4585,17 +4606,20 @@ static int rocker_port_vlan_dump(const struct rocker_port *rocker_port,
 }
 
 static int rocker_port_obj_dump(struct net_device *dev,
-                               struct switchdev_obj *obj)
+                               struct switchdev_obj *obj,
+                               switchdev_obj_dump_cb_t *cb)
 {
        const struct rocker_port *rocker_port = netdev_priv(dev);
        int err = 0;
 
        switch (obj->id) {
-       case SWITCHDEV_OBJ_PORT_FDB:
-               err = rocker_port_fdb_dump(rocker_port, obj);
+       case SWITCHDEV_OBJ_ID_PORT_FDB:
+               err = rocker_port_fdb_dump(rocker_port,
+                                          SWITCHDEV_OBJ_PORT_FDB(obj), cb);
                break;
-       case SWITCHDEV_OBJ_PORT_VLAN:
-               err = rocker_port_vlan_dump(rocker_port, obj);
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               err = rocker_port_vlan_dump(rocker_port,
+                                           SWITCHDEV_OBJ_PORT_VLAN(obj), cb);
                break;
        default:
                err = -EOPNOTSUPP;
@@ -4738,7 +4762,7 @@ rocker_cmd_get_port_stats_ethtool_proc(const struct rocker_port *rocker_port,
 static int rocker_cmd_get_port_stats_ethtool(struct rocker_port *rocker_port,
                                             void *priv)
 {
-       return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0,
+       return rocker_cmd_exec(rocker_port, NULL, 0,
                               rocker_cmd_get_port_stats_prep, NULL,
                               rocker_cmd_get_port_stats_ethtool_proc,
                               priv);
@@ -4930,8 +4954,7 @@ static void rocker_remove_ports(const struct rocker *rocker)
                rocker_port = rocker->ports[i];
                if (!rocker_port)
                        continue;
-               rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE,
-                                  ROCKER_OP_FLAG_REMOVE);
+               rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE);
                unregister_netdev(rocker_port->dev);
                free_netdev(rocker_port->dev);
        }
@@ -4969,7 +4992,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
        rocker_port->port_number = port_number;
        rocker_port->pport = port_number + 1;
        rocker_port->brport_flags = BR_LEARNING | BR_LEARNING_SYNC;
-       INIT_LIST_HEAD(&rocker_port->trans_mem);
+       rocker_port->ageing_time = BR_DEFAULT_AGEING_TIME;
 
        rocker_port_dev_addr_init(rocker_port);
        dev->netdev_ops = &rocker_port_netdev_ops;
@@ -4992,9 +5015,9 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
 
        switchdev_port_fwd_mark_set(rocker_port->dev, NULL, false);
 
-       rocker_port_set_learning(rocker_port, SWITCHDEV_TRANS_NONE);
+       rocker_port_set_learning(rocker_port, NULL);
 
-       err = rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE, 0);
+       err = rocker_port_ig_tbl(rocker_port, NULL, 0);
        if (err) {
                netdev_err(rocker_port->dev, "install ig port table failed\n");
                goto err_port_ig_tbl;
@@ -5003,8 +5026,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
        rocker_port->internal_vlan_id =
                rocker_port_internal_vlan_id_get(rocker_port, dev->ifindex);
 
-       err = rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE,
-                                  untagged_vid, 0);
+       err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0);
        if (err) {
                netdev_err(rocker_port->dev, "install untagged VLAN failed\n");
                goto err_untagged_vlan;
@@ -5013,8 +5035,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
        return 0;
 
 err_untagged_vlan:
-       rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE,
-                          ROCKER_OP_FLAG_REMOVE);
+       rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE);
 err_port_ig_tbl:
        rocker->ports[port_number] = NULL;
        unregister_netdev(dev);
@@ -5183,6 +5204,10 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_init_tbls;
        }
 
+       setup_timer(&rocker->fdb_cleanup_timer, rocker_fdb_cleanup,
+                   (unsigned long) rocker);
+       mod_timer(&rocker->fdb_cleanup_timer, jiffies);
+
        err = rocker_probe_ports(rocker);
        if (err) {
                dev_err(&pdev->dev, "failed to probe ports\n");
@@ -5195,6 +5220,7 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        return 0;
 
 err_probe_ports:
+       del_timer_sync(&rocker->fdb_cleanup_timer);
        rocker_free_tbls(rocker);
 err_init_tbls:
        free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker);
@@ -5222,6 +5248,7 @@ static void rocker_remove(struct pci_dev *pdev)
 {
        struct rocker *rocker = pci_get_drvdata(pdev);
 
+       del_timer_sync(&rocker->fdb_cleanup_timer);
        rocker_free_tbls(rocker);
        rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET);
        rocker_remove_ports(rocker);
@@ -5275,8 +5302,7 @@ static int rocker_port_bridge_join(struct rocker_port *rocker_port,
        rocker_port->bridge_dev = bridge;
        switchdev_port_fwd_mark_set(rocker_port->dev, bridge, true);
 
-       return rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE,
-                                   untagged_vid, 0);
+       return rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0);
 }
 
 static int rocker_port_bridge_leave(struct rocker_port *rocker_port)
@@ -5298,14 +5324,12 @@ static int rocker_port_bridge_leave(struct rocker_port *rocker_port)
                                    false);
        rocker_port->bridge_dev = NULL;
 
-       err = rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE,
-                                  untagged_vid, 0);
+       err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0);
        if (err)
                return err;
 
        if (rocker_port->dev->flags & IFF_UP)
-               err = rocker_port_fwd_enable(rocker_port,
-                                            SWITCHDEV_TRANS_NONE, 0);
+               err = rocker_port_fwd_enable(rocker_port, NULL, 0);
 
        return err;
 }
@@ -5318,10 +5342,10 @@ static int rocker_port_ovs_changed(struct rocker_port *rocker_port,
 
        rocker_port->bridge_dev = master;
 
-       err = rocker_port_fwd_disable(rocker_port, SWITCHDEV_TRANS_NONE, 0);
+       err = rocker_port_fwd_disable(rocker_port, NULL, 0);
        if (err)
                return err;
-       err = rocker_port_fwd_enable(rocker_port, SWITCHDEV_TRANS_NONE, 0);
+       err = rocker_port_fwd_enable(rocker_port, NULL, 0);
 
        return err;
 }
@@ -5399,8 +5423,7 @@ static int rocker_neigh_update(struct net_device *dev, struct neighbour *n)
                    ROCKER_OP_FLAG_NOWAIT;
        __be32 ip_addr = *(__be32 *)n->primary_key;
 
-       return rocker_port_ipv4_neigh(rocker_port, SWITCHDEV_TRANS_NONE,
-                                     flags, ip_addr, n->ha);
+       return rocker_port_ipv4_neigh(rocker_port, NULL, flags, ip_addr, n->ha);
 }
 
 static int rocker_netevent_event(struct notifier_block *unused,
index ff649ebef637409629ffd7623d754449b8ab9f1a..bc6d21b471be4f4ae2f0c4c1cb26430e92c24803 100644 (file)
@@ -1604,6 +1604,22 @@ efx_ef10_mcdi_read_response(struct efx_nic *efx, efx_dword_t *outbuf,
        memcpy(outbuf, pdu + offset, outlen);
 }
 
+static void efx_ef10_mcdi_reboot_detected(struct efx_nic *efx)
+{
+       struct efx_ef10_nic_data *nic_data = efx->nic_data;
+
+       /* All our allocations have been reset */
+       efx_ef10_reset_mc_allocations(efx);
+
+       /* The datapath firmware might have been changed */
+       nic_data->must_check_datapath_caps = true;
+
+       /* MAC statistics have been cleared on the NIC; clear the local
+        * statistic that we update with efx_update_diff_stat().
+        */
+       nic_data->stats[EF10_STAT_port_rx_bad_bytes] = 0;
+}
+
 static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx)
 {
        struct efx_ef10_nic_data *nic_data = efx->nic_data;
@@ -1623,17 +1639,7 @@ static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx)
                return 0;
 
        nic_data->warm_boot_count = rc;
-
-       /* All our allocations have been reset */
-       efx_ef10_reset_mc_allocations(efx);
-
-       /* The datapath firmware might have been changed */
-       nic_data->must_check_datapath_caps = true;
-
-       /* MAC statistics have been cleared on the NIC; clear the local
-        * statistic that we update with efx_update_diff_stat().
-        */
-       nic_data->stats[EF10_STAT_port_rx_bad_bytes] = 0;
+       efx_ef10_mcdi_reboot_detected(efx);
 
        return -EIO;
 }
@@ -1849,7 +1855,9 @@ static void efx_ef10_tx_write(struct efx_tx_queue *tx_queue)
        unsigned int write_ptr;
        efx_qword_t *txd;
 
-       BUG_ON(tx_queue->write_count == tx_queue->insert_count);
+       tx_queue->xmit_more_available = false;
+       if (unlikely(tx_queue->write_count == tx_queue->insert_count))
+               return;
 
        do {
                write_ptr = tx_queue->write_count & tx_queue->ptr_mask;
@@ -4670,6 +4678,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = {
        .mcdi_poll_response = efx_ef10_mcdi_poll_response,
        .mcdi_read_response = efx_ef10_mcdi_read_response,
        .mcdi_poll_reboot = efx_ef10_mcdi_poll_reboot,
+       .mcdi_reboot_detected = efx_ef10_mcdi_reboot_detected,
        .irq_enable_master = efx_port_dummy_op_void,
        .irq_test_generate = efx_ef10_irq_test_generate,
        .irq_disable_non_ev = efx_port_dummy_op_void,
@@ -4774,6 +4783,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = {
        .mcdi_poll_response = efx_ef10_mcdi_poll_response,
        .mcdi_read_response = efx_ef10_mcdi_read_response,
        .mcdi_poll_reboot = efx_ef10_mcdi_poll_reboot,
+       .mcdi_reboot_detected = efx_ef10_mcdi_reboot_detected,
        .irq_enable_master = efx_port_dummy_op_void,
        .irq_test_generate = efx_ef10_irq_test_generate,
        .irq_disable_non_ev = efx_port_dummy_op_void,
index 974637d3ae2526427e82997203fdd811215e3692..6e11ee6173ce566e97a61ad99e929cfc49895d84 100644 (file)
@@ -2062,7 +2062,7 @@ static void efx_init_napi_channel(struct efx_channel *channel)
        netif_napi_add(channel->napi_dev, &channel->napi_str,
                       efx_poll, napi_weight);
        napi_hash_add(&channel->napi_str);
-       efx_channel_init_lock(channel);
+       efx_channel_busy_poll_init(channel);
 }
 
 static void efx_init_napi(struct efx_nic *efx)
@@ -2125,7 +2125,7 @@ static int efx_busy_poll(struct napi_struct *napi)
        if (!netif_running(efx->net_dev))
                return LL_FLUSH_FAILED;
 
-       if (!efx_channel_lock_poll(channel))
+       if (!efx_channel_try_lock_poll(channel))
                return LL_FLUSH_BUSY;
 
        old_rx_packets = channel->rx_queue.rx_packets;
index f08266f0eca2363ec11138d15598caaf5a800e4a..5a1c5a8f278ad1690e678b10cec82444ee45bb12 100644 (file)
@@ -321,7 +321,9 @@ void efx_farch_tx_write(struct efx_tx_queue *tx_queue)
        unsigned write_ptr;
        unsigned old_write_count = tx_queue->write_count;
 
-       BUG_ON(tx_queue->write_count == tx_queue->insert_count);
+       tx_queue->xmit_more_available = false;
+       if (unlikely(tx_queue->write_count == tx_queue->insert_count))
+               return;
 
        do {
                write_ptr = tx_queue->write_count & tx_queue->ptr_mask;
index 98d172b04f71815a1105b304ea5cadfa552687a2..41fb6b60a3f07e11444e0d62430aca937f7e84ea 100644 (file)
@@ -9,7 +9,7 @@
 
 #include <linux/delay.h>
 #include <linux/moduleparam.h>
-#include <asm/cmpxchg.h>
+#include <linux/atomic.h>
 #include "net_driver.h"
 #include "nic.h"
 #include "io.h"
@@ -1028,10 +1028,21 @@ static void efx_mcdi_ev_death(struct efx_nic *efx, int rc)
 
                /* Consume the status word since efx_mcdi_rpc_finish() won't */
                for (count = 0; count < MCDI_STATUS_DELAY_COUNT; ++count) {
-                       if (efx_mcdi_poll_reboot(efx))
+                       rc = efx_mcdi_poll_reboot(efx);
+                       if (rc)
                                break;
                        udelay(MCDI_STATUS_DELAY_US);
                }
+
+               /* On EF10, a CODE_MC_REBOOT event can be received without the
+                * reboot detection in efx_mcdi_poll_reboot() being triggered.
+                * If zero was returned from the final call to
+                * efx_mcdi_poll_reboot(), the MC reboot wasn't noticed but the
+                * MC has definitely rebooted so prepare for the reset.
+                */
+               if (!rc && efx->type->mcdi_reboot_detected)
+                       efx->type->mcdi_reboot_detected(efx);
+
                mcdi->new_epoch = true;
 
                /* Nobody was waiting for an MCDI request, so trigger a reset */
index c530e1c4cb4f1ef06155b93db8a10b4c77328da0..a8ddd122f68586f7b566033c03272cb0b548dec6 100644 (file)
@@ -219,6 +219,7 @@ struct efx_tx_buffer {
  * @tso_packets: Number of packets via the TSO xmit path
  * @pushes: Number of times the TX push feature has been used
  * @pio_packets: Number of times the TX PIO feature has been used
+ * @xmit_more_available: Are any packets waiting to be pushed to the NIC
  * @empty_read_count: If the completion path has seen the queue as empty
  *     and the transmission path has not yet checked this, the value of
  *     @read_count bitwise-added to %EFX_EMPTY_COUNT_VALID; otherwise 0.
@@ -253,6 +254,7 @@ struct efx_tx_queue {
        unsigned int tso_packets;
        unsigned int pushes;
        unsigned int pio_packets;
+       bool xmit_more_available;
        /* Statistics to supplement MAC stats */
        unsigned long tx_packets;
 
@@ -431,21 +433,8 @@ struct efx_channel {
        struct net_device *napi_dev;
        struct napi_struct napi_str;
 #ifdef CONFIG_NET_RX_BUSY_POLL
-       unsigned int state;
-       spinlock_t state_lock;
-#define EFX_CHANNEL_STATE_IDLE         0
-#define EFX_CHANNEL_STATE_NAPI         (1 << 0)  /* NAPI owns this channel */
-#define EFX_CHANNEL_STATE_POLL         (1 << 1)  /* poll owns this channel */
-#define EFX_CHANNEL_STATE_DISABLED     (1 << 2)  /* channel is disabled */
-#define EFX_CHANNEL_STATE_NAPI_YIELD   (1 << 3)  /* NAPI yielded this channel */
-#define EFX_CHANNEL_STATE_POLL_YIELD   (1 << 4)  /* poll yielded this channel */
-#define EFX_CHANNEL_OWNED \
-       (EFX_CHANNEL_STATE_NAPI | EFX_CHANNEL_STATE_POLL)
-#define EFX_CHANNEL_LOCKED \
-       (EFX_CHANNEL_OWNED | EFX_CHANNEL_STATE_DISABLED)
-#define EFX_CHANNEL_USER_PEND \
-       (EFX_CHANNEL_STATE_POLL | EFX_CHANNEL_STATE_POLL_YIELD)
-#endif /* CONFIG_NET_RX_BUSY_POLL */
+       unsigned long busy_poll_state;
+#endif
        struct efx_special_buffer eventq;
        unsigned int eventq_mask;
        unsigned int eventq_read_ptr;
@@ -480,98 +469,94 @@ struct efx_channel {
 };
 
 #ifdef CONFIG_NET_RX_BUSY_POLL
-static inline void efx_channel_init_lock(struct efx_channel *channel)
+enum efx_channel_busy_poll_state {
+       EFX_CHANNEL_STATE_IDLE = 0,
+       EFX_CHANNEL_STATE_NAPI = BIT(0),
+       EFX_CHANNEL_STATE_NAPI_REQ_BIT = 1,
+       EFX_CHANNEL_STATE_NAPI_REQ = BIT(1),
+       EFX_CHANNEL_STATE_POLL_BIT = 2,
+       EFX_CHANNEL_STATE_POLL = BIT(2),
+       EFX_CHANNEL_STATE_DISABLE_BIT = 3,
+};
+
+static inline void efx_channel_busy_poll_init(struct efx_channel *channel)
 {
-       spin_lock_init(&channel->state_lock);
+       WRITE_ONCE(channel->busy_poll_state, EFX_CHANNEL_STATE_IDLE);
 }
 
 /* Called from the device poll routine to get ownership of a channel. */
 static inline bool efx_channel_lock_napi(struct efx_channel *channel)
 {
-       bool rc = true;
-
-       spin_lock_bh(&channel->state_lock);
-       if (channel->state & EFX_CHANNEL_LOCKED) {
-               WARN_ON(channel->state & EFX_CHANNEL_STATE_NAPI);
-               channel->state |= EFX_CHANNEL_STATE_NAPI_YIELD;
-               rc = false;
-       } else {
-               /* we don't care if someone yielded */
-               channel->state = EFX_CHANNEL_STATE_NAPI;
+       unsigned long prev, old = READ_ONCE(channel->busy_poll_state);
+
+       while (1) {
+               switch (old) {
+               case EFX_CHANNEL_STATE_POLL:
+                       /* Ensure efx_channel_try_lock_poll() wont starve us */
+                       set_bit(EFX_CHANNEL_STATE_NAPI_REQ_BIT,
+                               &channel->busy_poll_state);
+                       /* fallthrough */
+               case EFX_CHANNEL_STATE_POLL | EFX_CHANNEL_STATE_NAPI_REQ:
+                       return false;
+               default:
+                       break;
+               }
+               prev = cmpxchg(&channel->busy_poll_state, old,
+                              EFX_CHANNEL_STATE_NAPI);
+               if (unlikely(prev != old)) {
+                       /* This is likely to mean we've just entered polling
+                        * state. Go back round to set the REQ bit.
+                        */
+                       old = prev;
+                       continue;
+               }
+               return true;
        }
-       spin_unlock_bh(&channel->state_lock);
-       return rc;
 }
 
 static inline void efx_channel_unlock_napi(struct efx_channel *channel)
 {
-       spin_lock_bh(&channel->state_lock);
-       WARN_ON(channel->state &
-               (EFX_CHANNEL_STATE_POLL | EFX_CHANNEL_STATE_NAPI_YIELD));
-
-       channel->state &= EFX_CHANNEL_STATE_DISABLED;
-       spin_unlock_bh(&channel->state_lock);
+       /* Make sure write has completed from efx_channel_lock_napi() */
+       smp_wmb();
+       WRITE_ONCE(channel->busy_poll_state, EFX_CHANNEL_STATE_IDLE);
 }
 
 /* Called from efx_busy_poll(). */
-static inline bool efx_channel_lock_poll(struct efx_channel *channel)
+static inline bool efx_channel_try_lock_poll(struct efx_channel *channel)
 {
-       bool rc = true;
-
-       spin_lock_bh(&channel->state_lock);
-       if ((channel->state & EFX_CHANNEL_LOCKED)) {
-               channel->state |= EFX_CHANNEL_STATE_POLL_YIELD;
-               rc = false;
-       } else {
-               /* preserve yield marks */
-               channel->state |= EFX_CHANNEL_STATE_POLL;
-       }
-       spin_unlock_bh(&channel->state_lock);
-       return rc;
+       return cmpxchg(&channel->busy_poll_state, EFX_CHANNEL_STATE_IDLE,
+                       EFX_CHANNEL_STATE_POLL) == EFX_CHANNEL_STATE_IDLE;
 }
 
-/* Returns true if NAPI tried to get the channel while it was locked. */
 static inline void efx_channel_unlock_poll(struct efx_channel *channel)
 {
-       spin_lock_bh(&channel->state_lock);
-       WARN_ON(channel->state & EFX_CHANNEL_STATE_NAPI);
-
-       /* will reset state to idle, unless channel is disabled */
-       channel->state &= EFX_CHANNEL_STATE_DISABLED;
-       spin_unlock_bh(&channel->state_lock);
+       clear_bit_unlock(EFX_CHANNEL_STATE_POLL_BIT, &channel->busy_poll_state);
 }
 
-/* True if a socket is polling, even if it did not get the lock. */
 static inline bool efx_channel_busy_polling(struct efx_channel *channel)
 {
-       WARN_ON(!(channel->state & EFX_CHANNEL_OWNED));
-       return channel->state & EFX_CHANNEL_USER_PEND;
+       return test_bit(EFX_CHANNEL_STATE_POLL_BIT, &channel->busy_poll_state);
 }
 
 static inline void efx_channel_enable(struct efx_channel *channel)
 {
-       spin_lock_bh(&channel->state_lock);
-       channel->state = EFX_CHANNEL_STATE_IDLE;
-       spin_unlock_bh(&channel->state_lock);
+       clear_bit_unlock(EFX_CHANNEL_STATE_DISABLE_BIT,
+                        &channel->busy_poll_state);
 }
 
-/* False if the channel is currently owned. */
+/* Stop further polling or napi access.
+ * Returns false if the channel is currently busy polling.
+ */
 static inline bool efx_channel_disable(struct efx_channel *channel)
 {
-       bool rc = true;
-
-       spin_lock_bh(&channel->state_lock);
-       if (channel->state & EFX_CHANNEL_OWNED)
-               rc = false;
-       channel->state |= EFX_CHANNEL_STATE_DISABLED;
-       spin_unlock_bh(&channel->state_lock);
-
-       return rc;
+       set_bit(EFX_CHANNEL_STATE_DISABLE_BIT, &channel->busy_poll_state);
+       /* Implicit barrier in efx_channel_busy_polling() */
+       return !efx_channel_busy_polling(channel);
 }
 
 #else /* CONFIG_NET_RX_BUSY_POLL */
 
-static inline void efx_channel_init_lock(struct efx_channel *channel)
+static inline void efx_channel_busy_poll_init(struct efx_channel *channel)
 {
 }
 
@@ -584,7 +569,7 @@ static inline void efx_channel_unlock_napi(struct efx_channel *channel)
 {
 }
 
-static inline bool efx_channel_lock_poll(struct efx_channel *channel)
+static inline bool efx_channel_try_lock_poll(struct efx_channel *channel)
 {
        return false;
 }
@@ -1277,6 +1262,7 @@ struct efx_nic_type {
        void (*mcdi_read_response)(struct efx_nic *efx, efx_dword_t *pdu,
                                   size_t pdu_offset, size_t pdu_len);
        int (*mcdi_poll_reboot)(struct efx_nic *efx);
+       void (*mcdi_reboot_detected)(struct efx_nic *efx);
        void (*irq_enable_master)(struct efx_nic *efx);
        void (*irq_test_generate)(struct efx_nic *efx);
        void (*irq_disable_non_ev)(struct efx_nic *efx);
index ad62615a93dcfe238fd23cf0487e5c6ea60330b7..c771e0af4e06d76028e7e8b85f36c09b9e19c149 100644 (file)
@@ -401,8 +401,8 @@ size_t efx_ptp_update_stats(struct efx_nic *efx, u64 *stats)
 /* For Siena platforms NIC time is s and ns */
 static void efx_ptp_ns_to_s_ns(s64 ns, u32 *nic_major, u32 *nic_minor)
 {
-       struct timespec ts = ns_to_timespec(ns);
-       *nic_major = ts.tv_sec;
+       struct timespec64 ts = ns_to_timespec64(ns);
+       *nic_major = (u32)ts.tv_sec;
        *nic_minor = ts.tv_nsec;
 }
 
@@ -431,8 +431,8 @@ static ktime_t efx_ptp_s_ns_to_ktime_correction(u32 nic_major, u32 nic_minor,
  */
 static void efx_ptp_ns_to_s27(s64 ns, u32 *nic_major, u32 *nic_minor)
 {
-       struct timespec ts = ns_to_timespec(ns);
-       u32 maj = ts.tv_sec;
+       struct timespec64 ts = ns_to_timespec64(ns);
+       u32 maj = (u32)ts.tv_sec;
        u32 min = (u32)(((u64)ts.tv_nsec * NS_TO_S27_MULT +
                         (1ULL << (NS_TO_S27_SHIFT - 1))) >> NS_TO_S27_SHIFT);
 
@@ -646,28 +646,28 @@ static void efx_ptp_send_times(struct efx_nic *efx,
                               struct pps_event_time *last_time)
 {
        struct pps_event_time now;
-       struct timespec limit;
+       struct timespec64 limit;
        struct efx_ptp_data *ptp = efx->ptp_data;
-       struct timespec start;
+       struct timespec64 start;
        int *mc_running = ptp->start.addr;
 
        pps_get_ts(&now);
        start = now.ts_real;
        limit = now.ts_real;
-       timespec_add_ns(&limit, SYNCHRONISE_PERIOD_NS);
+       timespec64_add_ns(&limit, SYNCHRONISE_PERIOD_NS);
 
        /* Write host time for specified period or until MC is done */
-       while ((timespec_compare(&now.ts_real, &limit) < 0) &&
+       while ((timespec64_compare(&now.ts_real, &limit) < 0) &&
               ACCESS_ONCE(*mc_running)) {
-               struct timespec update_time;
+               struct timespec64 update_time;
                unsigned int host_time;
 
                /* Don't update continuously to avoid saturating the PCIe bus */
                update_time = now.ts_real;
-               timespec_add_ns(&update_time, SYNCHRONISATION_GRANULARITY_NS);
+               timespec64_add_ns(&update_time, SYNCHRONISATION_GRANULARITY_NS);
                do {
                        pps_get_ts(&now);
-               } while ((timespec_compare(&now.ts_real, &update_time) < 0) &&
+               } while ((timespec64_compare(&now.ts_real, &update_time) < 0) &&
                         ACCESS_ONCE(*mc_running));
 
                /* Synchronise NIC with single word of time only */
@@ -723,7 +723,7 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
        struct efx_ptp_data *ptp = efx->ptp_data;
        u32 last_sec;
        u32 start_sec;
-       struct timespec delta;
+       struct timespec64 delta;
        ktime_t mc_time;
 
        if (number_readings == 0)
@@ -737,14 +737,14 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
         */
        for (i = 0; i < number_readings; i++) {
                s32 window, corrected;
-               struct timespec wait;
+               struct timespec64 wait;
 
                efx_ptp_read_timeset(
                        MCDI_ARRAY_STRUCT_PTR(synch_buf,
                                              PTP_OUT_SYNCHRONIZE_TIMESET, i),
                        &ptp->timeset[i]);
 
-               wait = ktime_to_timespec(
+               wait = ktime_to_timespec64(
                        ptp->nic_to_kernel_time(0, ptp->timeset[i].wait, 0));
                window = ptp->timeset[i].window;
                corrected = window - wait.tv_nsec;
@@ -803,7 +803,7 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
                                          ptp->timeset[last_good].minor, 0);
 
        /* Calculate delay from NIC top of second to last_time */
-       delta.tv_nsec += ktime_to_timespec(mc_time).tv_nsec;
+       delta.tv_nsec += ktime_to_timespec64(mc_time).tv_nsec;
 
        /* Set PPS timestamp to match NIC top of second */
        ptp->host_time_pps = *last_time;
index 1833a01465711d56d1b2e09b03090792929d3a1b..67f6afaa022f4e55e6d2632f90586c0d48a8e429 100644 (file)
@@ -431,8 +431,20 @@ finish_packet:
        efx_tx_maybe_stop_queue(tx_queue);
 
        /* Pass off to hardware */
-       if (!skb->xmit_more || netif_xmit_stopped(tx_queue->core_txq))
+       if (!skb->xmit_more || netif_xmit_stopped(tx_queue->core_txq)) {
+               struct efx_tx_queue *txq2 = efx_tx_queue_partner(tx_queue);
+
+               /* There could be packets left on the partner queue if those
+                * SKBs had skb->xmit_more set. If we do not push those they
+                * could be left for a long time and cause a netdev watchdog.
+                */
+               if (txq2->xmit_more_available)
+                       efx_nic_push_buffers(txq2);
+
                efx_nic_push_buffers(tx_queue);
+       } else {
+               tx_queue->xmit_more_available = skb->xmit_more;
+       }
 
        tx_queue->tx_packets++;
 
@@ -722,6 +734,7 @@ void efx_init_tx_queue(struct efx_tx_queue *tx_queue)
        tx_queue->read_count = 0;
        tx_queue->old_read_count = 0;
        tx_queue->empty_read_count = 0 | EFX_EMPTY_COUNT_VALID;
+       tx_queue->xmit_more_available = false;
 
        /* Set up TX descriptor ring */
        efx_nic_init_tx(tx_queue);
@@ -747,6 +760,7 @@ void efx_fini_tx_queue(struct efx_tx_queue *tx_queue)
 
                ++tx_queue->read_count;
        }
+       tx_queue->xmit_more_available = false;
        netdev_tx_reset_queue(tx_queue->core_txq);
 }
 
@@ -1302,8 +1316,20 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue,
        efx_tx_maybe_stop_queue(tx_queue);
 
        /* Pass off to hardware */
-       if (!skb->xmit_more || netif_xmit_stopped(tx_queue->core_txq))
+       if (!skb->xmit_more || netif_xmit_stopped(tx_queue->core_txq)) {
+               struct efx_tx_queue *txq2 = efx_tx_queue_partner(tx_queue);
+
+               /* There could be packets left on the partner queue if those
+                * SKBs had skb->xmit_more set. If we do not push those they
+                * could be left for a long time and cause a netdev watchdog.
+                */
+               if (txq2->xmit_more_available)
+                       efx_nic_push_buffers(txq2);
+
                efx_nic_push_buffers(tx_queue);
+       } else {
+               tx_queue->xmit_more_available = skb->xmit_more;
+       }
 
        tx_queue->tso_bursts++;
        return NETDEV_TX_OK;
index 630f0b7800e47e085c5ffb2db6ac23efef9ad08c..0e2fc1a844ab2b2453681750368e1cf9d288e779 100644 (file)
@@ -2018,10 +2018,18 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr,
        lp->cfg.flags |= SMC91X_USE_DMA;
 #  endif
        if (lp->cfg.flags & SMC91X_USE_DMA) {
-               int dma = pxa_request_dma(dev->name, DMA_PRIO_LOW,
-                                         smc_pxa_dma_irq, NULL);
-               if (dma >= 0)
-                       dev->dma = dma;
+               dma_cap_mask_t mask;
+               struct pxad_param param;
+
+               dma_cap_zero(mask);
+               dma_cap_set(DMA_SLAVE, mask);
+               param.prio = PXAD_PRIO_LOWEST;
+               param.drcmr = -1UL;
+
+               lp->dma_chan =
+                       dma_request_slave_channel_compat(mask, pxad_filter_fn,
+                                                        &param, &dev->dev,
+                                                        "data");
        }
 #endif
 
@@ -2032,8 +2040,8 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr,
                            version_string, revision_register & 0x0f,
                            lp->base, dev->irq);
 
-               if (dev->dma != (unsigned char)-1)
-                       pr_cont(" DMA %d", dev->dma);
+               if (lp->dma_chan)
+                       pr_cont(" DMA %p", lp->dma_chan);
 
                pr_cont("%s%s\n",
                        lp->cfg.flags & SMC91X_NOWAIT ? " [nowait]" : "",
@@ -2058,8 +2066,8 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr,
 
 err_out:
 #ifdef CONFIG_ARCH_PXA
-       if (retval && dev->dma != (unsigned char)-1)
-               pxa_free_dma(dev->dma);
+       if (retval && lp->dma_chan)
+               dma_release_channel(lp->dma_chan);
 #endif
        return retval;
 }
@@ -2370,6 +2378,7 @@ static int smc_drv_probe(struct platform_device *pdev)
                struct smc_local *lp = netdev_priv(ndev);
                lp->device = &pdev->dev;
                lp->physaddr = res->start;
+
        }
 #endif
 
@@ -2406,8 +2415,8 @@ static int smc_drv_remove(struct platform_device *pdev)
        free_irq(ndev->irq, ndev);
 
 #ifdef CONFIG_ARCH_PXA
-       if (ndev->dma != (unsigned char)-1)
-               pxa_free_dma(ndev->dma);
+       if (lp->dma_chan)
+               dma_release_channel(lp->dma_chan);
 #endif
        iounmap(lp->base);
 
index 3a18501d1068c36816554f953e367ff1439c2a36..a3c129e1e40ac1e2c6785ff30fd4af41cb68ecb2 100644 (file)
@@ -33,6 +33,7 @@
 #ifndef _SMC91X_H_
 #define _SMC91X_H_
 
+#include <linux/dmaengine.h>
 #include <linux/smc91x.h>
 
 /*
@@ -244,6 +245,7 @@ struct smc_local {
        u_long physaddr;
        struct device *device;
 #endif
+       struct dma_chan *dma_chan;
        void __iomem *base;
        void __iomem *datacs;
 
@@ -265,21 +267,47 @@ struct smc_local {
  * as RX which can overrun memory and lose packets.
  */
 #include <linux/dma-mapping.h>
-#include <mach/dma.h>
+#include <linux/dma/pxa-dma.h>
 
 #ifdef SMC_insl
 #undef SMC_insl
 #define SMC_insl(a, r, p, l) \
        smc_pxa_dma_insl(a, lp, r, dev->dma, p, l)
+static inline void
+smc_pxa_dma_inpump(struct smc_local *lp, u_char *buf, int len)
+{
+       dma_addr_t dmabuf;
+       struct dma_async_tx_descriptor *tx;
+       dma_cookie_t cookie;
+       enum dma_status status;
+       struct dma_tx_state state;
+
+       dmabuf = dma_map_single(lp->device, buf, len, DMA_FROM_DEVICE);
+       tx = dmaengine_prep_slave_single(lp->dma_chan, dmabuf, len,
+                                        DMA_DEV_TO_MEM, 0);
+       if (tx) {
+               cookie = dmaengine_submit(tx);
+               dma_async_issue_pending(lp->dma_chan);
+               do {
+                       status = dmaengine_tx_status(lp->dma_chan, cookie,
+                                                    &state);
+                       cpu_relax();
+               } while (status != DMA_COMPLETE && status != DMA_ERROR &&
+                        state.residue);
+               dmaengine_terminate_all(lp->dma_chan);
+       }
+       dma_unmap_single(lp->device, dmabuf, len, DMA_FROM_DEVICE);
+}
+
 static inline void
 smc_pxa_dma_insl(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma,
                 u_char *buf, int len)
 {
-       u_long physaddr = lp->physaddr;
-       dma_addr_t dmabuf;
+       struct dma_slave_config config;
+       int ret;
 
        /* fallback if no DMA available */
-       if (dma == (unsigned char)-1) {
+       if (!lp->dma_chan) {
                readsl(ioaddr + reg, buf, len);
                return;
        }
@@ -291,18 +319,22 @@ smc_pxa_dma_insl(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma,
                len--;
        }
 
+       memset(&config, 0, sizeof(config));
+       config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       config.src_addr = lp->physaddr + reg;
+       config.dst_addr = lp->physaddr + reg;
+       config.src_maxburst = 32;
+       config.dst_maxburst = 32;
+       ret = dmaengine_slave_config(lp->dma_chan, &config);
+       if (ret) {
+               dev_err(lp->device, "dma channel configuration failed: %d\n",
+                       ret);
+               return;
+       }
+
        len *= 4;
-       dmabuf = dma_map_single(lp->device, buf, len, DMA_FROM_DEVICE);
-       DCSR(dma) = DCSR_NODESC;
-       DTADR(dma) = dmabuf;
-       DSADR(dma) = physaddr + reg;
-       DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
-                    DCMD_WIDTH4 | (DCMD_LENGTH & len));
-       DCSR(dma) = DCSR_NODESC | DCSR_RUN;
-       while (!(DCSR(dma) & DCSR_STOPSTATE))
-               cpu_relax();
-       DCSR(dma) = 0;
-       dma_unmap_single(lp->device, dmabuf, len, DMA_FROM_DEVICE);
+       smc_pxa_dma_inpump(lp, buf, len);
 }
 #endif
 
@@ -314,11 +346,11 @@ static inline void
 smc_pxa_dma_insw(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma,
                 u_char *buf, int len)
 {
-       u_long physaddr = lp->physaddr;
-       dma_addr_t dmabuf;
+       struct dma_slave_config config;
+       int ret;
 
        /* fallback if no DMA available */
-       if (dma == (unsigned char)-1) {
+       if (!lp->dma_chan) {
                readsw(ioaddr + reg, buf, len);
                return;
        }
@@ -330,26 +362,25 @@ smc_pxa_dma_insw(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma,
                len--;
        }
 
+       memset(&config, 0, sizeof(config));
+       config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+       config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+       config.src_addr = lp->physaddr + reg;
+       config.dst_addr = lp->physaddr + reg;
+       config.src_maxburst = 32;
+       config.dst_maxburst = 32;
+       ret = dmaengine_slave_config(lp->dma_chan, &config);
+       if (ret) {
+               dev_err(lp->device, "dma channel configuration failed: %d\n",
+                       ret);
+               return;
+       }
+
        len *= 2;
-       dmabuf = dma_map_single(lp->device, buf, len, DMA_FROM_DEVICE);
-       DCSR(dma) = DCSR_NODESC;
-       DTADR(dma) = dmabuf;
-       DSADR(dma) = physaddr + reg;
-       DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
-                    DCMD_WIDTH2 | (DCMD_LENGTH & len));
-       DCSR(dma) = DCSR_NODESC | DCSR_RUN;
-       while (!(DCSR(dma) & DCSR_STOPSTATE))
-               cpu_relax();
-       DCSR(dma) = 0;
-       dma_unmap_single(lp->device, dmabuf, len, DMA_FROM_DEVICE);
+       smc_pxa_dma_inpump(lp, buf, len);
 }
 #endif
 
-static void
-smc_pxa_dma_irq(int dma, void *dummy)
-{
-       DCSR(dma) = 0;
-}
 #endif  /* CONFIG_ARCH_PXA */
 
 
index 3b4cd8a263deb4ae93e6487bdba0d717b009f329..c860c9007e49afda02165733ede46780da58abf3 100644 (file)
@@ -1052,6 +1052,7 @@ static int smsc911x_mii_probe(struct net_device *dev)
 #ifdef USE_PHY_WORK_AROUND
        if (smsc911x_phy_loopbacktest(dev) < 0) {
                SMSC_WARN(pdata, hw, "Failed Loop Back Test");
+               phy_disconnect(phydev);
                return -ENODEV;
        }
        SMSC_TRACE(pdata, hw, "Passed Loop Back Test");
index 771cda2a48b2a932daa17600446e29a0bb26823a..2e51b816a7e819268be678a49881b700b75c74a1 100644 (file)
@@ -721,10 +721,13 @@ static int stmmac_get_ts_info(struct net_device *dev,
 {
        struct stmmac_priv *priv = netdev_priv(dev);
 
-       if ((priv->hwts_tx_en) && (priv->hwts_rx_en)) {
+       if ((priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp)) {
 
-               info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+               info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
+                                       SOF_TIMESTAMPING_TX_HARDWARE |
+                                       SOF_TIMESTAMPING_RX_SOFTWARE |
                                        SOF_TIMESTAMPING_RX_HARDWARE |
+                                       SOF_TIMESTAMPING_SOFTWARE |
                                        SOF_TIMESTAMPING_RAW_HARDWARE;
 
                if (priv->ptp_clock)
index 925f2f8659b8f181fc9a447329180cbc4a12a5e9..64d8aa4e0cad6420bcb1fa4cd827ad823ba3601d 100644 (file)
@@ -424,7 +424,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
 {
        struct stmmac_priv *priv = netdev_priv(dev);
        struct hwtstamp_config config;
-       struct timespec now;
+       struct timespec64 now;
        u64 temp = 0;
        u32 ptp_v2 = 0;
        u32 tstamp_all = 0;
@@ -621,8 +621,10 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
                                             priv->default_addend);
 
                /* initialize system time */
-               getnstimeofday(&now);
-               priv->hw->ptp->init_systime(priv->ioaddr, now.tv_sec,
+               ktime_get_real_ts64(&now);
+
+               /* lower 32 bits of tv_sec are safe until y2106 */
+               priv->hw->ptp->init_systime(priv->ioaddr, (u32)now.tv_sec,
                                            now.tv_nsec);
        }
 
@@ -1945,7 +1947,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct stmmac_priv *priv = netdev_priv(dev);
        unsigned int txsize = priv->dma_tx_size;
-       unsigned int entry;
+       int entry;
        int i, csum_insertion = 0, is_jumbo = 0;
        int nfrags = skb_shinfo(skb)->nr_frags;
        struct dma_desc *desc, *first;
index 6ce973187225aec2668888009bfbc543975e8492..062bce9acde6f555794376cc25e24981033e0868 100644 (file)
@@ -4529,9 +4529,6 @@ static void cas_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info
        strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
        strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
        strlcpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info));
-       info->regdump_len = cp->casreg_len < CAS_MAX_REGS ?
-               cp->casreg_len : CAS_MAX_REGS;
-       info->n_stats = CAS_NUM_STAT_KEYS;
 }
 
 static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
index a9cac8413e49e8062875d93af33b8ea4d41061b6..14c9d1baa85cebb9c531ae3cf74d87d8e1cb06fb 100644 (file)
@@ -2182,11 +2182,6 @@ bdx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
        strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
        strlcpy(drvinfo->bus_info, pci_name(priv->pdev),
                sizeof(drvinfo->bus_info));
-
-       drvinfo->n_stats = ((priv->stats_flag) ? ARRAY_SIZE(bdx_stat_names) : 0);
-       drvinfo->testinfo_len = 0;
-       drvinfo->regdump_len = 0;
-       drvinfo->eedump_len = 0;
 }
 
 /*
index cba3d9fcb46535946c2c1b67ef660f0cca9d803f..77d26fe286c0916491b13b80ea8353d8a1af1ace 100644 (file)
@@ -899,7 +899,6 @@ static void cpmac_get_drvinfo(struct net_device *dev,
        strlcpy(info->driver, "cpmac", sizeof(info->driver));
        strlcpy(info->version, CPMAC_VERSION, sizeof(info->version));
        snprintf(info->bus_info, sizeof(info->bus_info), "%s", "cpmac");
-       info->regdump_len = 0;
 }
 
 static const struct ethtool_ops cpmac_ethtool_ops = {
index f59509486113150fb8162fa834ad8272e7823d4b..c08be62bceba6f2e2eb59e0da7ac9c3bdbe61813 100644 (file)
 
 #include "cpsw.h"
 
-#define AM33XX_CTRL_MAC_LO_REG(offset, id) ((offset) + 0x8 * (id))
-#define AM33XX_CTRL_MAC_HI_REG(offset, id) ((offset) + 0x8 * (id) + 0x4)
+#define CTRL_MAC_LO_REG(offset, id) ((offset) + 0x8 * (id))
+#define CTRL_MAC_HI_REG(offset, id) ((offset) + 0x8 * (id) + 0x4)
 
-int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave,
-                            u8 *mac_addr)
+static int davinci_emac_3517_get_macid(struct device *dev, u16 offset,
+                                      int slave, u8 *mac_addr)
+{
+       u32 macid_lsb;
+       u32 macid_msb;
+       struct regmap *syscon;
+
+       syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
+       if (IS_ERR(syscon)) {
+               if (PTR_ERR(syscon) == -ENODEV)
+                       return 0;
+               return PTR_ERR(syscon);
+       }
+
+       regmap_read(syscon, CTRL_MAC_LO_REG(offset, slave), &macid_lsb);
+       regmap_read(syscon, CTRL_MAC_HI_REG(offset, slave), &macid_msb);
+
+       mac_addr[0] = (macid_msb >> 16) & 0xff;
+       mac_addr[1] = (macid_msb >> 8)  & 0xff;
+       mac_addr[2] = macid_msb & 0xff;
+       mac_addr[3] = (macid_lsb >> 16) & 0xff;
+       mac_addr[4] = (macid_lsb >> 8)  & 0xff;
+       mac_addr[5] = macid_lsb & 0xff;
+
+       return 0;
+}
+
+static int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave,
+                                   u8 *mac_addr)
 {
        u32 macid_lo;
        u32 macid_hi;
@@ -36,10 +63,8 @@ int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave,
                return PTR_ERR(syscon);
        }
 
-       regmap_read(syscon, AM33XX_CTRL_MAC_LO_REG(offset, slave),
-                   &macid_lo);
-       regmap_read(syscon, AM33XX_CTRL_MAC_HI_REG(offset, slave),
-                   &macid_hi);
+       regmap_read(syscon, CTRL_MAC_LO_REG(offset, slave), &macid_lo);
+       regmap_read(syscon, CTRL_MAC_HI_REG(offset, slave), &macid_hi);
 
        mac_addr[5] = (macid_lo >> 8) & 0xff;
        mac_addr[4] = macid_lo & 0xff;
@@ -50,6 +75,27 @@ int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave,
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(cpsw_am33xx_cm_get_macid);
+
+int ti_cm_get_macid(struct device *dev, int slave, u8 *mac_addr)
+{
+       if (of_machine_is_compatible("ti,am33xx"))
+               return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr);
+
+       if (of_device_is_compatible(dev->of_node, "ti,am3517-emac"))
+               return davinci_emac_3517_get_macid(dev, 0x110, slave, mac_addr);
+
+       if (of_device_is_compatible(dev->of_node, "ti,dm816-emac"))
+               return cpsw_am33xx_cm_get_macid(dev, 0x30, slave, mac_addr);
+
+       if (of_machine_is_compatible("ti,am4372"))
+               return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr);
+
+       if (of_machine_is_compatible("ti,dra7"))
+               return davinci_emac_3517_get_macid(dev, 0x514, slave, mac_addr);
+
+       dev_err(dev, "incompatible machine/device type for reading mac address\n");
+       return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(ti_cm_get_macid);
 
 MODULE_LICENSE("GPL");
index 0ea78326cc2165d63b82eff89c5b90a49ecb9ac5..e9cc61e1ec742090157db6b792b1a6a9168e6635 100644 (file)
@@ -2,6 +2,8 @@
  *
  * Copyright (C) 2013 Texas Instruments
  *
+ * Module Author: Mugunthan V N <mugunthanvnm@ti.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.
@@ -13,7 +15,7 @@
  */
 
 #include <linux/platform_device.h>
-#include <linux/module.h>
+#include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/phy.h>
 #include <linux/of.h>
@@ -173,7 +175,6 @@ static const struct of_device_id cpsw_phy_sel_id_table[] = {
        },
        {}
 };
-MODULE_DEVICE_TABLE(of, cpsw_phy_sel_id_table);
 
 static int cpsw_phy_sel_probe(struct platform_device *pdev)
 {
@@ -214,7 +215,4 @@ static struct platform_driver cpsw_phy_sel_driver = {
                .of_match_table = cpsw_phy_sel_id_table,
        },
 };
-
-module_platform_driver(cpsw_phy_sel_driver);
-MODULE_AUTHOR("Mugunthan V N <mugunthanvnm@ti.com>");
-MODULE_LICENSE("GPL v2");
+builtin_platform_driver(cpsw_phy_sel_driver);
index 8fc90f1c872c6fe5afe2105aae62c54d8283800f..040fbc1e55080a4d025df2a2fae888da6151008c 100644 (file)
@@ -29,7 +29,9 @@
 #include <linux/workqueue.h>
 #include <linux/delay.h>
 #include <linux/pm_runtime.h>
+#include <linux/gpio.h>
 #include <linux/of.h>
+#include <linux/of_mdio.h>
 #include <linux/of_net.h>
 #include <linux/of_device.h>
 #include <linux/if_vlan.h>
@@ -365,6 +367,7 @@ struct cpsw_priv {
        spinlock_t                      lock;
        struct platform_device          *pdev;
        struct net_device               *ndev;
+       struct device_node              *phy_node;
        struct napi_struct              napi_rx;
        struct napi_struct              napi_tx;
        struct device                   *dev;
@@ -1145,7 +1148,11 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
                cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast,
                                   1 << slave_port, 0, 0, ALE_MCAST_FWD_2);
 
-       slave->phy = phy_connect(priv->ndev, slave->data->phy_id,
+       if (priv->phy_node)
+               slave->phy = of_phy_connect(priv->ndev, priv->phy_node,
+                                &cpsw_adjust_link, 0, slave->data->phy_if);
+       else
+               slave->phy = phy_connect(priv->ndev, slave->data->phy_id,
                                 &cpsw_adjust_link, slave->data->phy_if);
        if (IS_ERR(slave->phy)) {
                dev_err(priv->dev, "phy %s not found on slave %d\n",
@@ -1783,7 +1790,6 @@ static void cpsw_get_drvinfo(struct net_device *ndev,
        strlcpy(info->driver, "cpsw", sizeof(info->driver));
        strlcpy(info->version, "1.0", sizeof(info->version));
        strlcpy(info->bus_info, priv->pdev->name, sizeof(info->bus_info));
-       info->regdump_len = cpsw_get_regs_len(ndev);
 }
 
 static u32 cpsw_get_msglevel(struct net_device *ndev)
@@ -1934,11 +1940,12 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv,
        slave->port_vlan = data->dual_emac_res_vlan;
 }
 
-static int cpsw_probe_dt(struct cpsw_platform_data *data,
+static int cpsw_probe_dt(struct cpsw_priv *priv,
                         struct platform_device *pdev)
 {
        struct device_node *node = pdev->dev.of_node;
        struct device_node *slave_node;
+       struct cpsw_platform_data *data = &priv->data;
        int i = 0, ret;
        u32 prop;
 
@@ -2029,6 +2036,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                if (strcmp(slave_node->name, "slave"))
                        continue;
 
+               priv->phy_node = of_parse_phandle(slave_node, "phy-handle", 0);
                parp = of_get_property(slave_node, "phy_id", &lenp);
                if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) {
                        dev_err(&pdev->dev, "Missing slave[%d] phy_id property\n", i);
@@ -2044,7 +2052,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                }
                snprintf(slave_data->phy_id, sizeof(slave_data->phy_id),
                         PHY_ID_FMT, mdio->name, phyid);
-
                slave_data->phy_if = of_get_phy_mode(slave_node);
                if (slave_data->phy_if < 0) {
                        dev_err(&pdev->dev, "Missing or malformed slave[%d] phy-mode property\n",
@@ -2057,13 +2064,10 @@ no_phy_slave:
                if (mac_addr) {
                        memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN);
                } else {
-                       if (of_machine_is_compatible("ti,am33xx")) {
-                               ret = cpsw_am33xx_cm_get_macid(&pdev->dev,
-                                                       0x630, i,
-                                                       slave_data->mac_addr);
-                               if (ret)
-                                       return ret;
-                       }
+                       ret = ti_cm_get_macid(&pdev->dev, i,
+                                             slave_data->mac_addr);
+                       if (ret)
+                               return ret;
                }
                if (data->dual_emac) {
                        if (of_property_read_u32(slave_node, "dual_emac_res_vlan",
@@ -2207,6 +2211,7 @@ static int cpsw_probe(struct platform_device *pdev)
        void __iomem                    *ss_regs;
        struct resource                 *res, *ss_res;
        const struct of_device_id       *of_id;
+       struct gpio_descs               *mode;
        u32 slave_offset, sliver_offset, slave_size;
        int ret = 0, i;
        int irq;
@@ -2232,6 +2237,13 @@ static int cpsw_probe(struct platform_device *pdev)
                goto clean_ndev_ret;
        }
 
+       mode = devm_gpiod_get_array_optional(&pdev->dev, "mode", GPIOD_OUT_LOW);
+       if (IS_ERR(mode)) {
+               ret = PTR_ERR(mode);
+               dev_err(&pdev->dev, "gpio request failed, ret %d\n", ret);
+               goto clean_ndev_ret;
+       }
+
        /*
         * This may be required here for child devices.
         */
@@ -2240,7 +2252,7 @@ static int cpsw_probe(struct platform_device *pdev)
        /* Select default pin state */
        pinctrl_pm_select_default_state(&pdev->dev);
 
-       if (cpsw_probe_dt(&priv->data, pdev)) {
+       if (cpsw_probe_dt(priv, pdev)) {
                dev_err(&pdev->dev, "cpsw: platform data missing\n");
                ret = -ENODEV;
                goto clean_runtime_disable_ret;
@@ -2578,17 +2590,7 @@ static struct platform_driver cpsw_driver = {
        .remove = cpsw_remove,
 };
 
-static int __init cpsw_init(void)
-{
-       return platform_driver_register(&cpsw_driver);
-}
-late_initcall(cpsw_init);
-
-static void __exit cpsw_exit(void)
-{
-       platform_driver_unregister(&cpsw_driver);
-}
-module_exit(cpsw_exit);
+module_platform_driver(cpsw_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Cyril Chemparathy <cyril@ti.com>");
index ca90efafd15691c03c36a0fa146d69bb51e75ff0..442a7038e660c877b1cc7509efab7df214a25a87 100644 (file)
@@ -41,7 +41,6 @@ struct cpsw_platform_data {
 };
 
 void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave);
-int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave,
-                            u8 *mac_addr);
+int ti_cm_get_macid(struct device *dev, int slave, u8 *mac_addr);
 
 #endif /* __CPSW_H__ */
index a21c77bc1b27c7678ad54a619de608b0d4a2e44e..33bd3b902304f3e737ad08c616d43d9b75c86dce 100644 (file)
@@ -1861,8 +1861,12 @@ davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv)
        pdata->no_bd_ram = of_property_read_bool(np, "ti,davinci-no-bd-ram");
 
        priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
-       if (!priv->phy_node)
-               pdata->phy_id = NULL;
+       if (!priv->phy_node) {
+               if (!of_phy_is_fixed_link(np))
+                       pdata->phy_id = NULL;
+               else if (of_phy_register_fixed_link(np) >= 0)
+                       priv->phy_node = of_node_get(np);
+       }
 
        auxdata = pdev->dev.platform_data;
        if (auxdata) {
@@ -1882,51 +1886,13 @@ davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv)
        return  pdata;
 }
 
-static int davinci_emac_3517_get_macid(struct device *dev, u16 offset,
-                                      int slave, u8 *mac_addr)
-{
-       u32 macid_lsb;
-       u32 macid_msb;
-       struct regmap *syscon;
-
-       syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
-       if (IS_ERR(syscon)) {
-               if (PTR_ERR(syscon) == -ENODEV)
-                       return 0;
-               return PTR_ERR(syscon);
-       }
-
-       regmap_read(syscon, offset, &macid_lsb);
-       regmap_read(syscon, offset + 4, &macid_msb);
-
-       mac_addr[0] = (macid_msb >> 16) & 0xff;
-       mac_addr[1] = (macid_msb >> 8)  & 0xff;
-       mac_addr[2] = macid_msb & 0xff;
-       mac_addr[3] = (macid_lsb >> 16) & 0xff;
-       mac_addr[4] = (macid_lsb >> 8)  & 0xff;
-       mac_addr[5] = macid_lsb & 0xff;
-
-       return 0;
-}
-
 static int davinci_emac_try_get_mac(struct platform_device *pdev,
                                    int instance, u8 *mac_addr)
 {
-       int error = -EINVAL;
-
        if (!pdev->dev.of_node)
-               return error;
-
-       if (of_device_is_compatible(pdev->dev.of_node, "ti,am3517-emac"))
-               error = davinci_emac_3517_get_macid(&pdev->dev, 0x110,
-                                                   0, mac_addr);
-       else if (of_device_is_compatible(pdev->dev.of_node,
-                                        "ti,dm816-emac"))
-               error = cpsw_am33xx_cm_get_macid(&pdev->dev, 0x30,
-                                                instance,
-                                                mac_addr);
-
-       return error;
+               return -EINVAL;
+
+       return ti_cm_get_macid(&pdev->dev, instance, mac_addr);
 }
 
 /**
index 6bff8d82ceab7a428e73048504a5dd63f4a1c1bd..4e70e7586a0918a045008b7e54e807f0f11e2de9 100644 (file)
@@ -2637,8 +2637,10 @@ static void init_secondary_ports(struct gbe_priv *gbe_dev,
                        mac_phy_link = true;
 
                slave->open = true;
-               if (gbe_dev->num_slaves >= gbe_dev->max_num_slaves)
+               if (gbe_dev->num_slaves >= gbe_dev->max_num_slaves) {
+                       of_node_put(port);
                        break;
+               }
        }
 
        /* of_phy_connect() is needed only for MAC-PHY interface */
@@ -3137,8 +3139,10 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
                        continue;
                }
                gbe_dev->num_slaves++;
-               if (gbe_dev->num_slaves >= gbe_dev->max_num_slaves)
+               if (gbe_dev->num_slaves >= gbe_dev->max_num_slaves) {
+                       of_node_put(interface);
                        break;
+               }
        }
        of_node_put(interfaces);
 
index 691ec936e88d53601e3ed257278708ae823670c8..a274cd49afe954442bbd3bf647be71a92d0087ee 100644 (file)
@@ -791,7 +791,6 @@ static void tlan_get_drvinfo(struct net_device *dev,
                        sizeof(info->bus_info));
        else
                strlcpy(info->bus_info, "EISA", sizeof(info->bus_info));
-       info->eedump_len = TLAN_EEPROM_SIZE;
 }
 
 static int tlan_get_eeprom_len(struct net_device *dev)
index 8cf9d4f56bb2223893dd912e1d475a48fc30f683..415de1eaf6412665fbaba35b7e67742819ebacd5 100644 (file)
@@ -59,16 +59,15 @@ static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
 int temac_mdio_setup(struct temac_local *lp, struct device_node *np)
 {
        struct mii_bus *bus;
-       const u32 *bus_hz;
+       u32 bus_hz;
        int clk_div;
-       int rc, size;
+       int rc;
        struct resource res;
 
        /* Calculate a reasonable divisor for the clock rate */
        clk_div = 0x3f; /* worst-case default setting */
-       bus_hz = of_get_property(np, "clock-frequency", &size);
-       if (bus_hz && size >= sizeof(*bus_hz)) {
-               clk_div = (*bus_hz) / (2500 * 1000 * 2) - 1;
+       if (of_property_read_u32(np, "clock-frequency", &bus_hz) == 0) {
+               clk_div = bus_hz / (2500 * 1000 * 2) - 1;
                if (clk_div < 1)
                        clk_div = 1;
                if (clk_div > 0x3f)
index d95f9aae95e78ecc08b8a91bb67c267d10f81461..4684644703ccee1d4ad2c68439d48ac618b5c3b0 100644 (file)
@@ -1135,7 +1135,6 @@ static void axienet_ethtools_get_drvinfo(struct net_device *ndev,
 {
        strlcpy(ed->driver, DRIVER_NAME, sizeof(ed->driver));
        strlcpy(ed->version, DRIVER_VERSION, sizeof(ed->version));
-       ed->regdump_len = sizeof(u32) * AXIENET_REGS_N;
 }
 
 /**
index 2a5a16834c017c1c32e66591c7618ab47b9f8927..507bbb0355c23129c66a17a24717d6313e83d6e1 100644 (file)
@@ -129,7 +129,6 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
 {
        int ret;
        u32 clk_div, host_clock;
-       u32 *property_p;
        struct mii_bus *bus;
        struct resource res;
        struct device_node *np1;
@@ -168,8 +167,7 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
                clk_div = DEFAULT_CLOCK_DIVISOR;
                goto issue;
        }
-       property_p = (u32 *) of_get_property(np1, "clock-frequency", NULL);
-       if (!property_p) {
+       if (of_property_read_u32(np1, "clock-frequency", &host_clock)) {
                netdev_warn(lp->ndev, "clock-frequency property not found.\n");
                netdev_warn(lp->ndev,
                            "Setting MDIO clock divisor to default %d\n",
@@ -179,7 +177,6 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
                goto issue;
        }
 
-       host_clock = be32_to_cpup(property_p);
        clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1;
        /* If there is any remainder from the division of
         * fHOST / (MAX_MDIO_FREQ * 2), then we need to add
index 0119dd199276b620c46f20f7c239ca30299600a1..9c218e140c41ad7a546a6534e38796c34298895b 100644 (file)
@@ -105,8 +105,6 @@ static void fjes_get_drvinfo(struct net_device *netdev,
        strlcpy(drvinfo->fw_version, "none", sizeof(drvinfo->fw_version));
        snprintf(drvinfo->bus_info, sizeof(drvinfo->bus_info),
                 "platform:%s", plat_dev->name);
-       drvinfo->regdump_len = 0;
-       drvinfo->eedump_len = 0;
 }
 
 static int fjes_get_settings(struct net_device *netdev,
index cde29f8a37bfebe036273f3a76bbc24b0e034fff..de5c30c9f059203040cc47e83cce76ae9e5bfde6 100644 (file)
@@ -46,16 +46,27 @@ struct geneve_net {
 
 static int geneve_net_id;
 
+union geneve_addr {
+       struct sockaddr_in sin;
+       struct sockaddr_in6 sin6;
+       struct sockaddr sa;
+};
+
+static union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, };
+
 /* Pseudo network device */
 struct geneve_dev {
        struct hlist_node  hlist;       /* vni hash table */
        struct net         *net;        /* netns for packet i/o */
        struct net_device  *dev;        /* netdev for geneve tunnel */
-       struct geneve_sock *sock;       /* socket used for geneve tunnel */
+       struct geneve_sock *sock4;      /* IPv4 socket used for geneve tunnel */
+#if IS_ENABLED(CONFIG_IPV6)
+       struct geneve_sock *sock6;      /* IPv6 socket used for geneve tunnel */
+#endif
        u8                 vni[3];      /* virtual network ID for tunnel */
        u8                 ttl;         /* TTL override */
        u8                 tos;         /* TOS override */
-       struct sockaddr_in remote;      /* IPv4 address for link partner */
+       union geneve_addr  remote;      /* IP address for link partner */
        struct list_head   next;        /* geneve's per namespace list */
        __be16             dst_port;
        bool               collect_md;
@@ -103,12 +114,32 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
        vni_list_head = &gs->vni_list[hash];
        hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
                if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-                   addr == geneve->remote.sin_addr.s_addr)
+                   addr == geneve->remote.sin.sin_addr.s_addr)
                        return geneve;
        }
        return NULL;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs,
+                                        struct in6_addr addr6, u8 vni[])
+{
+       struct hlist_head *vni_list_head;
+       struct geneve_dev *geneve;
+       __u32 hash;
+
+       /* Find the device for this VNI */
+       hash = geneve_net_vni_hash(vni);
+       vni_list_head = &gs->vni_list[hash];
+       hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
+               if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
+                   ipv6_addr_equal(&addr6, &geneve->remote.sin6.sin6_addr))
+                       return geneve;
+       }
+       return NULL;
+}
+#endif
+
 static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
 {
        return (struct genevehdr *)(udp_hdr(skb) + 1);
@@ -121,24 +152,49 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
        struct metadata_dst *tun_dst = NULL;
        struct geneve_dev *geneve = NULL;
        struct pcpu_sw_netstats *stats;
-       struct iphdr *iph;
-       u8 *vni;
+       struct iphdr *iph = NULL;
        __be32 addr;
-       int err;
+       static u8 zero_vni[3];
+       u8 *vni;
+       int err = 0;
+       sa_family_t sa_family;
+#if IS_ENABLED(CONFIG_IPV6)
+       struct ipv6hdr *ip6h = NULL;
+       struct in6_addr addr6;
+       static struct in6_addr zero_addr6;
+#endif
 
-       iph = ip_hdr(skb); /* outer IP header... */
+       sa_family = gs->sock->sk->sk_family;
 
-       if (gs->collect_md) {
-               static u8 zero_vni[3];
+       if (sa_family == AF_INET) {
+               iph = ip_hdr(skb); /* outer IP header... */
 
-               vni = zero_vni;
-               addr = 0;
-       } else {
-               vni = gnvh->vni;
-               addr = iph->saddr;
-       }
+               if (gs->collect_md) {
+                       vni = zero_vni;
+                       addr = 0;
+               } else {
+                       vni = gnvh->vni;
 
-       geneve = geneve_lookup(gs, addr, vni);
+                       addr = iph->saddr;
+               }
+
+               geneve = geneve_lookup(gs, addr, vni);
+#if IS_ENABLED(CONFIG_IPV6)
+       } else if (sa_family == AF_INET6) {
+               ip6h = ipv6_hdr(skb); /* outer IPv6 header... */
+
+               if (gs->collect_md) {
+                       vni = zero_vni;
+                       addr6 = zero_addr6;
+               } else {
+                       vni = gnvh->vni;
+
+                       addr6 = ip6h->saddr;
+               }
+
+               geneve = geneve6_lookup(gs, addr6, vni);
+#endif
+       }
        if (!geneve)
                goto drop;
 
@@ -149,7 +205,7 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
                        (gnvh->oam ? TUNNEL_OAM : 0) |
                        (gnvh->critical ? TUNNEL_CRIT_OPT : 0);
 
-               tun_dst = udp_tun_rx_dst(skb, AF_INET, flags,
+               tun_dst = udp_tun_rx_dst(skb, sa_family, flags,
                                         vni_to_tunnel_id(gnvh->vni),
                                         gnvh->opt_len * 4);
                if (!tun_dst)
@@ -179,12 +235,25 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 
        skb_reset_network_header(skb);
 
-       err = IP_ECN_decapsulate(iph, skb);
+       if (iph)
+               err = IP_ECN_decapsulate(iph, skb);
+#if IS_ENABLED(CONFIG_IPV6)
+       if (ip6h)
+               err = IP6_ECN_decapsulate(ip6h, skb);
+#endif
 
        if (unlikely(err)) {
-               if (log_ecn_error)
-                       net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
-                                            &iph->saddr, iph->tos);
+               if (log_ecn_error) {
+                       if (iph)
+                               net_info_ratelimited("non-ECT from %pI4 "
+                                                    "with TOS=%#x\n",
+                                                    &iph->saddr, iph->tos);
+#if IS_ENABLED(CONFIG_IPV6)
+                       if (ip6h)
+                               net_info_ratelimited("non-ECT from %pI6\n",
+                                                    &ip6h->saddr);
+#endif
+               }
                if (err > 1) {
                        ++geneve->dev->stats.rx_frame_errors;
                        ++geneve->dev->stats.rx_errors;
@@ -284,6 +353,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
 
        if (ipv6) {
                udp_conf.family = AF_INET6;
+               udp_conf.ipv6_v6only = 1;
        } else {
                udp_conf.family = AF_INET;
                udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -458,9 +528,9 @@ static void geneve_notify_del_rx_port(struct geneve_sock *gs)
                udp_del_offload(&gs->udp_offloads);
 }
 
-static void geneve_sock_release(struct geneve_sock *gs)
+static void __geneve_sock_release(struct geneve_sock *gs)
 {
-       if (--gs->refcnt)
+       if (!gs || --gs->refcnt)
                return;
 
        list_del(&gs->list);
@@ -469,66 +539,117 @@ static void geneve_sock_release(struct geneve_sock *gs)
        kfree_rcu(gs, rcu);
 }
 
+static void geneve_sock_release(struct geneve_dev *geneve)
+{
+       __geneve_sock_release(geneve->sock4);
+#if IS_ENABLED(CONFIG_IPV6)
+       __geneve_sock_release(geneve->sock6);
+#endif
+}
+
 static struct geneve_sock *geneve_find_sock(struct geneve_net *gn,
+                                           sa_family_t family,
                                            __be16 dst_port)
 {
        struct geneve_sock *gs;
 
        list_for_each_entry(gs, &gn->sock_list, list) {
                if (inet_sk(gs->sock->sk)->inet_sport == dst_port &&
-                   inet_sk(gs->sock->sk)->sk.sk_family == AF_INET) {
+                   inet_sk(gs->sock->sk)->sk.sk_family == family) {
                        return gs;
                }
        }
        return NULL;
 }
 
-static int geneve_open(struct net_device *dev)
+static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
 {
-       struct geneve_dev *geneve = netdev_priv(dev);
        struct net *net = geneve->net;
        struct geneve_net *gn = net_generic(net, geneve_net_id);
        struct geneve_sock *gs;
        __u32 hash;
 
-       gs = geneve_find_sock(gn, geneve->dst_port);
+       gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->dst_port);
        if (gs) {
                gs->refcnt++;
                goto out;
        }
 
-       gs = geneve_socket_create(net, geneve->dst_port, false);
+       gs = geneve_socket_create(net, geneve->dst_port, ipv6);
        if (IS_ERR(gs))
                return PTR_ERR(gs);
 
 out:
        gs->collect_md = geneve->collect_md;
-       geneve->sock = gs;
+#if IS_ENABLED(CONFIG_IPV6)
+       if (ipv6)
+               geneve->sock6 = gs;
+       else
+#endif
+               geneve->sock4 = gs;
 
        hash = geneve_net_vni_hash(geneve->vni);
        hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]);
        return 0;
 }
 
+static int geneve_open(struct net_device *dev)
+{
+       struct geneve_dev *geneve = netdev_priv(dev);
+       bool ipv6 = geneve->remote.sa.sa_family == AF_INET6;
+       bool metadata = geneve->collect_md;
+       int ret = 0;
+
+       geneve->sock4 = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+       geneve->sock6 = NULL;
+       if (ipv6 || metadata)
+               ret = geneve_sock_add(geneve, true);
+#endif
+       if (!ret && (!ipv6 || metadata))
+               ret = geneve_sock_add(geneve, false);
+       if (ret < 0)
+               geneve_sock_release(geneve);
+
+       return ret;
+}
+
 static int geneve_stop(struct net_device *dev)
 {
        struct geneve_dev *geneve = netdev_priv(dev);
-       struct geneve_sock *gs = geneve->sock;
 
        if (!hlist_unhashed(&geneve->hlist))
                hlist_del_rcu(&geneve->hlist);
-       geneve_sock_release(gs);
+       geneve_sock_release(geneve);
        return 0;
 }
 
+static void geneve_build_header(struct genevehdr *geneveh,
+                               __be16 tun_flags, u8 vni[3],
+                               u8 options_len, u8 *options)
+{
+       geneveh->ver = GENEVE_VER;
+       geneveh->opt_len = options_len / 4;
+       geneveh->oam = !!(tun_flags & TUNNEL_OAM);
+       geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
+       geneveh->rsvd1 = 0;
+       memcpy(geneveh->vni, vni, 3);
+       geneveh->proto_type = htons(ETH_P_TEB);
+       geneveh->rsvd2 = 0;
+
+       memcpy(geneveh->options, options, options_len);
+}
+
 static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
                            __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
-                           bool csum)
+                           bool csum, bool xnet)
 {
        struct genevehdr *gnvh;
        int min_headroom;
        int err;
 
+       skb_scrub_packet(skb, xnet);
+
        min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
                        + GENEVE_BASE_HLEN + opt_len + sizeof(struct iphdr);
        err = skb_cow_head(skb, min_headroom);
@@ -544,15 +665,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
        }
 
        gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
-       gnvh->ver = GENEVE_VER;
-       gnvh->opt_len = opt_len / 4;
-       gnvh->oam = !!(tun_flags & TUNNEL_OAM);
-       gnvh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
-       gnvh->rsvd1 = 0;
-       memcpy(gnvh->vni, vni, 3);
-       gnvh->proto_type = htons(ETH_P_TEB);
-       gnvh->rsvd2 = 0;
-       memcpy(gnvh->options, opt, opt_len);
+       geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
 
        skb_set_inner_protocol(skb, htons(ETH_P_TEB));
        return 0;
@@ -562,10 +675,47 @@ free_rt:
        return err;
 }
 
-static struct rtable *geneve_get_rt(struct sk_buff *skb,
-                                   struct net_device *dev,
-                                   struct flowi4 *fl4,
-                                   struct ip_tunnel_info *info)
+#if IS_ENABLED(CONFIG_IPV6)
+static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
+                            __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
+                            bool csum, bool xnet)
+{
+       struct genevehdr *gnvh;
+       int min_headroom;
+       int err;
+
+       skb_scrub_packet(skb, xnet);
+
+       min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
+                       + GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr);
+       err = skb_cow_head(skb, min_headroom);
+       if (unlikely(err)) {
+               kfree_skb(skb);
+               goto free_dst;
+       }
+
+       skb = udp_tunnel_handle_offloads(skb, csum);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               goto free_dst;
+       }
+
+       gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
+       geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
+
+       skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+       return 0;
+
+free_dst:
+       dst_release(dst);
+       return err;
+}
+#endif
+
+static struct rtable *geneve_get_v4_rt(struct sk_buff *skb,
+                                      struct net_device *dev,
+                                      struct flowi4 *fl4,
+                                      struct ip_tunnel_info *info)
 {
        struct geneve_dev *geneve = netdev_priv(dev);
        struct rtable *rt = NULL;
@@ -588,24 +738,67 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
                }
 
                fl4->flowi4_tos = RT_TOS(tos);
-               fl4->daddr = geneve->remote.sin_addr.s_addr;
+               fl4->daddr = geneve->remote.sin.sin_addr.s_addr;
        }
 
        rt = ip_route_output_key(geneve->net, fl4);
        if (IS_ERR(rt)) {
                netdev_dbg(dev, "no route to %pI4\n", &fl4->daddr);
-               dev->stats.tx_carrier_errors++;
-               return rt;
+               return ERR_PTR(-ENETUNREACH);
        }
        if (rt->dst.dev == dev) { /* is this necessary? */
                netdev_dbg(dev, "circular route to %pI4\n", &fl4->daddr);
-               dev->stats.collisions++;
                ip_rt_put(rt);
-               return ERR_PTR(-EINVAL);
+               return ERR_PTR(-ELOOP);
        }
        return rt;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
+                                          struct net_device *dev,
+                                          struct flowi6 *fl6,
+                                          struct ip_tunnel_info *info)
+{
+       struct geneve_dev *geneve = netdev_priv(dev);
+       struct geneve_sock *gs6 = geneve->sock6;
+       struct dst_entry *dst = NULL;
+       __u8 prio;
+
+       memset(fl6, 0, sizeof(*fl6));
+       fl6->flowi6_mark = skb->mark;
+       fl6->flowi6_proto = IPPROTO_UDP;
+
+       if (info) {
+               fl6->daddr = info->key.u.ipv6.dst;
+               fl6->saddr = info->key.u.ipv6.src;
+               fl6->flowi6_tos = RT_TOS(info->key.tos);
+       } else {
+               prio = geneve->tos;
+               if (prio == 1) {
+                       const struct iphdr *iip = ip_hdr(skb);
+
+                       prio = ip_tunnel_get_dsfield(iip, skb);
+               }
+
+               fl6->flowi6_tos = RT_TOS(prio);
+               fl6->daddr = geneve->remote.sin6.sin6_addr;
+       }
+
+       if (ipv6_stub->ipv6_dst_lookup(geneve->net, gs6->sock->sk, &dst, fl6)) {
+               netdev_dbg(dev, "no route to %pI6\n", &fl6->daddr);
+               return ERR_PTR(-ENETUNREACH);
+       }
+       if (dst->dev == dev) { /* is this necessary? */
+               netdev_dbg(dev, "circular route to %pI6\n", &fl6->daddr);
+               dst_release(dst);
+               return ERR_PTR(-ELOOP);
+       }
+
+       return dst;
+}
+#endif
+
 /* Convert 64 bit tunnel ID to 24 bit VNI. */
 static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 {
@@ -620,23 +813,23 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 #endif
 }
 
-static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+                                  struct ip_tunnel_info *info)
 {
        struct geneve_dev *geneve = netdev_priv(dev);
-       struct geneve_sock *gs = geneve->sock;
-       struct ip_tunnel_info *info = NULL;
+       struct geneve_sock *gs4 = geneve->sock4;
        struct rtable *rt = NULL;
        const struct iphdr *iip; /* interior IP header */
+       int err = -EINVAL;
        struct flowi4 fl4;
        __u8 tos, ttl;
        __be16 sport;
        bool udp_csum;
        __be16 df;
-       int err;
+       bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
 
        if (geneve->collect_md) {
-               info = skb_tunnel_info(skb);
-               if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
+               if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
                        netdev_dbg(dev, "no tunnel metadata\n");
                        goto tx_error;
                }
@@ -644,10 +837,9 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
                        goto tx_error;
        }
 
-       rt = geneve_get_rt(skb, dev, &fl4, info);
+       rt = geneve_get_v4_rt(skb, dev, &fl4, info);
        if (IS_ERR(rt)) {
-               netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr);
-               dev->stats.tx_carrier_errors++;
+               err = PTR_ERR(rt);
                goto tx_error;
        }
 
@@ -667,7 +859,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 
                udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
                err = geneve_build_skb(rt, skb, key->tun_flags, vni,
-                                      info->options_len, opts, udp_csum);
+                                      info->options_len, opts, udp_csum, xnet);
                if (unlikely(err))
                        goto err;
 
@@ -677,7 +869,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
        } else {
                udp_csum = false;
                err = geneve_build_skb(rt, skb, 0, geneve->vni,
-                                      0, NULL, udp_csum);
+                                      0, NULL, udp_csum, xnet);
                if (unlikely(err))
                        goto err;
 
@@ -688,7 +880,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
                ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
                df = 0;
        }
-       err = udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, fl4.saddr, fl4.daddr,
+       err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
                                  tos, ttl, df, sport, geneve->dst_port,
                                  !net_eq(geneve->net, dev_net(geneve->dev)),
                                  !udp_csum);
@@ -699,9 +891,151 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 tx_error:
        dev_kfree_skb(skb);
 err:
-       dev->stats.tx_errors++;
+       if (err == -ELOOP)
+               dev->stats.collisions++;
+       else if (err == -ENETUNREACH)
+               dev->stats.tx_carrier_errors++;
+       else
+               dev->stats.tx_errors++;
+       return NETDEV_TX_OK;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+                                   struct ip_tunnel_info *info)
+{
+       struct geneve_dev *geneve = netdev_priv(dev);
+       struct geneve_sock *gs6 = geneve->sock6;
+       struct dst_entry *dst = NULL;
+       const struct iphdr *iip; /* interior IP header */
+       int err = -EINVAL;
+       struct flowi6 fl6;
+       __u8 prio, ttl;
+       __be16 sport;
+       bool udp_csum;
+       bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+
+       if (geneve->collect_md) {
+               if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
+                       netdev_dbg(dev, "no tunnel metadata\n");
+                       goto tx_error;
+               }
+       }
+
+       dst = geneve_get_v6_dst(skb, dev, &fl6, info);
+       if (IS_ERR(dst)) {
+               err = PTR_ERR(dst);
+               goto tx_error;
+       }
+
+       sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
+       skb_reset_mac_header(skb);
+
+       iip = ip_hdr(skb);
+
+       if (info) {
+               const struct ip_tunnel_key *key = &info->key;
+               u8 *opts = NULL;
+               u8 vni[3];
+
+               tunnel_id_to_vni(key->tun_id, vni);
+               if (key->tun_flags & TUNNEL_GENEVE_OPT)
+                       opts = ip_tunnel_info_opts(info);
+
+               udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+               err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
+                                       info->options_len, opts,
+                                       udp_csum, xnet);
+               if (unlikely(err))
+                       goto err;
+
+               prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
+               ttl = key->ttl;
+       } else {
+               udp_csum = false;
+               err = geneve6_build_skb(dst, skb, 0, geneve->vni,
+                                       0, NULL, udp_csum, xnet);
+               if (unlikely(err))
+                       goto err;
+
+               prio = ip_tunnel_ecn_encap(fl6.flowi6_tos, iip, skb);
+               ttl = geneve->ttl;
+               if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
+                       ttl = 1;
+               ttl = ttl ? : ip6_dst_hoplimit(dst);
+       }
+       err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
+                                  &fl6.saddr, &fl6.daddr, prio, ttl,
+                                  sport, geneve->dst_port, !udp_csum);
+
+       iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+       return NETDEV_TX_OK;
+
+tx_error:
+       dev_kfree_skb(skb);
+err:
+       if (err == -ELOOP)
+               dev->stats.collisions++;
+       else if (err == -ENETUNREACH)
+               dev->stats.tx_carrier_errors++;
+       else
+               dev->stats.tx_errors++;
        return NETDEV_TX_OK;
 }
+#endif
+
+static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct geneve_dev *geneve = netdev_priv(dev);
+       struct ip_tunnel_info *info = NULL;
+
+       if (geneve->collect_md)
+               info = skb_tunnel_info(skb);
+
+#if IS_ENABLED(CONFIG_IPV6)
+       if ((info && ip_tunnel_info_af(info) == AF_INET6) ||
+           (!info && geneve->remote.sa.sa_family == AF_INET6))
+               return geneve6_xmit_skb(skb, dev, info);
+#endif
+       return geneve_xmit_skb(skb, dev, info);
+}
+
+static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
+{
+       struct ip_tunnel_info *info = skb_tunnel_info(skb);
+       struct geneve_dev *geneve = netdev_priv(dev);
+       struct rtable *rt;
+       struct flowi4 fl4;
+#if IS_ENABLED(CONFIG_IPV6)
+       struct dst_entry *dst;
+       struct flowi6 fl6;
+#endif
+
+       if (ip_tunnel_info_af(info) == AF_INET) {
+               rt = geneve_get_v4_rt(skb, dev, &fl4, info);
+               if (IS_ERR(rt))
+                       return PTR_ERR(rt);
+
+               ip_rt_put(rt);
+               info->key.u.ipv4.src = fl4.saddr;
+#if IS_ENABLED(CONFIG_IPV6)
+       } else if (ip_tunnel_info_af(info) == AF_INET6) {
+               dst = geneve_get_v6_dst(skb, dev, &fl6, info);
+               if (IS_ERR(dst))
+                       return PTR_ERR(dst);
+
+               dst_release(dst);
+               info->key.u.ipv6.src = fl6.saddr;
+#endif
+       } else {
+               return -EINVAL;
+       }
+
+       info->key.tp_src = udp_flow_src_port(geneve->net, skb,
+                                            1, USHRT_MAX, true);
+       info->key.tp_dst = geneve->dst_port;
+       return 0;
+}
 
 static const struct net_device_ops geneve_netdev_ops = {
        .ndo_init               = geneve_init,
@@ -713,6 +1047,7 @@ static const struct net_device_ops geneve_netdev_ops = {
        .ndo_change_mtu         = eth_change_mtu,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_fill_metadata_dst  = geneve_fill_metadata_dst,
 };
 
 static void geneve_get_drvinfo(struct net_device *dev,
@@ -759,6 +1094,7 @@ static void geneve_setup(struct net_device *dev)
 static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
        [IFLA_GENEVE_ID]                = { .type = NLA_U32 },
        [IFLA_GENEVE_REMOTE]            = { .len = FIELD_SIZEOF(struct iphdr, daddr) },
+       [IFLA_GENEVE_REMOTE6]           = { .len = sizeof(struct in6_addr) },
        [IFLA_GENEVE_TTL]               = { .type = NLA_U8 },
        [IFLA_GENEVE_TOS]               = { .type = NLA_U8 },
        [IFLA_GENEVE_PORT]              = { .type = NLA_U16 },
@@ -790,7 +1126,7 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
 
 static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
                                          __be16 dst_port,
-                                         __be32 rem_addr,
+                                         union geneve_addr *remote,
                                          u8 vni[],
                                          bool *tun_on_same_port,
                                          bool *tun_collect_md)
@@ -806,7 +1142,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
                        *tun_on_same_port = true;
                }
                if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-                   rem_addr == geneve->remote.sin_addr.s_addr &&
+                   !memcmp(remote, &geneve->remote, sizeof(geneve->remote)) &&
                    dst_port == geneve->dst_port)
                        t = geneve;
        }
@@ -814,18 +1150,20 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 }
 
 static int geneve_configure(struct net *net, struct net_device *dev,
-                           __be32 rem_addr, __u32 vni, __u8 ttl, __u8 tos,
-                           __be16 dst_port, bool metadata)
+                           union geneve_addr *remote,
+                           __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
+                           bool metadata)
 {
        struct geneve_net *gn = net_generic(net, geneve_net_id);
        struct geneve_dev *t, *geneve = netdev_priv(dev);
        bool tun_collect_md, tun_on_same_port;
        int err;
 
-       if (metadata) {
-               if (rem_addr || vni || tos || ttl)
-                       return -EINVAL;
-       }
+       if (!remote)
+               return -EINVAL;
+       if (metadata &&
+           (remote->sa.sa_family != AF_UNSPEC || vni || tos || ttl))
+               return -EINVAL;
 
        geneve->net = net;
        geneve->dev = dev;
@@ -834,16 +1172,19 @@ static int geneve_configure(struct net *net, struct net_device *dev,
        geneve->vni[1] = (vni & 0x0000ff00) >> 8;
        geneve->vni[2] =  vni & 0x000000ff;
 
-       geneve->remote.sin_addr.s_addr = rem_addr;
-       if (IN_MULTICAST(ntohl(geneve->remote.sin_addr.s_addr)))
+       if ((remote->sa.sa_family == AF_INET &&
+            IN_MULTICAST(ntohl(remote->sin.sin_addr.s_addr))) ||
+           (remote->sa.sa_family == AF_INET6 &&
+            ipv6_addr_is_multicast(&remote->sin6.sin6_addr)))
                return -EINVAL;
+       geneve->remote = *remote;
 
        geneve->ttl = ttl;
        geneve->tos = tos;
        geneve->dst_port = dst_port;
        geneve->collect_md = metadata;
 
-       t = geneve_find_dev(gn, dst_port, rem_addr, geneve->vni,
+       t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
                            &tun_on_same_port, &tun_collect_md);
        if (t)
                return -EBUSY;
@@ -870,15 +1211,36 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
        __be16 dst_port = htons(GENEVE_UDP_PORT);
        __u8 ttl = 0, tos = 0;
        bool metadata = false;
-       __be32 rem_addr = 0;
+       union geneve_addr remote = geneve_remote_unspec;
        __u32 vni = 0;
 
+       if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6])
+               return -EINVAL;
+
+       if (data[IFLA_GENEVE_REMOTE]) {
+               remote.sa.sa_family = AF_INET;
+               remote.sin.sin_addr.s_addr =
+                       nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+       }
+
+       if (data[IFLA_GENEVE_REMOTE6]) {
+               if (!IS_ENABLED(CONFIG_IPV6))
+                       return -EPFNOSUPPORT;
+
+               remote.sa.sa_family = AF_INET6;
+               remote.sin6.sin6_addr =
+                       nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]);
+
+               if (ipv6_addr_type(&remote.sin6.sin6_addr) &
+                   IPV6_ADDR_LINKLOCAL) {
+                       netdev_dbg(dev, "link-local remote is unsupported\n");
+                       return -EINVAL;
+               }
+       }
+
        if (data[IFLA_GENEVE_ID])
                vni = nla_get_u32(data[IFLA_GENEVE_ID]);
 
-       if (data[IFLA_GENEVE_REMOTE])
-               rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
-
        if (data[IFLA_GENEVE_TTL])
                ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
 
@@ -891,8 +1253,8 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
        if (data[IFLA_GENEVE_COLLECT_METADATA])
                metadata = true;
 
-       return geneve_configure(net, dev, rem_addr, vni,
-                               ttl, tos, dst_port, metadata);
+       return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
+                               metadata);
 }
 
 static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -906,7 +1268,7 @@ static void geneve_dellink(struct net_device *dev, struct list_head *head)
 static size_t geneve_get_size(const struct net_device *dev)
 {
        return nla_total_size(sizeof(__u32)) +  /* IFLA_GENEVE_ID */
-               nla_total_size(sizeof(struct in_addr)) + /* IFLA_GENEVE_REMOTE */
+               nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
                nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TTL */
                nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
                nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
@@ -923,9 +1285,17 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
        if (nla_put_u32(skb, IFLA_GENEVE_ID, vni))
                goto nla_put_failure;
 
-       if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
-                           geneve->remote.sin_addr.s_addr))
-               goto nla_put_failure;
+       if (geneve->remote.sa.sa_family == AF_INET) {
+               if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
+                                   geneve->remote.sin.sin_addr.s_addr))
+                       goto nla_put_failure;
+#if IS_ENABLED(CONFIG_IPV6)
+       } else {
+               if (nla_put_in6_addr(skb, IFLA_GENEVE_REMOTE6,
+                                    &geneve->remote.sin6.sin6_addr))
+                       goto nla_put_failure;
+#endif
+       }
 
        if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) ||
            nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos))
@@ -971,7 +1341,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
        if (IS_ERR(dev))
                return dev;
 
-       err = geneve_configure(net, dev, 0, 0, 0, 0, htons(dst_port), true);
+       err = geneve_configure(net, dev, &geneve_remote_unspec,
+                              0, 0, 0, htons(dst_port), true);
        if (err) {
                free_netdev(dev);
                return ERR_PTR(err);
index 1dd5ab8e5054a4a192e75841c9220750f7fa9dec..ce5f1a21e6d735b7f68436c104a86e2f7bf63dcb 100644 (file)
@@ -32,10 +32,18 @@ config IEEE802154_AT86RF230
          This driver can also be built as a module. To do so, say M here.
          the module will be called 'at86rf230'.
 
+config IEEE802154_AT86RF230_DEBUGFS
+       depends on IEEE802154_AT86RF230
+       bool "AT86RF230 debugfs interface"
+       depends on DEBUG_FS
+       ---help---
+         This option compiles debugfs code for the at86rf230 driver.
+
 config IEEE802154_MRF24J40
        tristate "Microchip MRF24J40 transceiver driver"
        depends on IEEE802154_DRIVERS && MAC802154
        depends on SPI
+       select REGMAP_SPI
        ---help---
          Say Y here to enable the MRF24J20 SPI 802.15.4 wireless
          controller.
index 6422caac8d40d64644aa9c95f32b5661dd37003b..de6e4fa2d6aafd24cd53ce7b8a7eb5f272c3d804 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/skbuff.h>
 #include <linux/of_gpio.h>
 #include <linux/ieee802154.h>
+#include <linux/debugfs.h>
 
 #include <net/mac802154.h>
 #include <net/cfg802154.h>
@@ -80,7 +81,16 @@ struct at86rf230_state_change {
        u8 from_state;
        u8 to_state;
 
-       bool irq_enable;
+       bool free;
+};
+
+struct at86rf230_trac {
+       u64 success;
+       u64 success_data_pending;
+       u64 success_wait_for_ack;
+       u64 channel_access_failure;
+       u64 no_ack;
+       u64 invalid;
 };
 
 struct at86rf230_local {
@@ -95,14 +105,14 @@ struct at86rf230_local {
        struct completion state_complete;
        struct at86rf230_state_change state;
 
-       struct at86rf230_state_change irq;
-
        unsigned long cal_timeout;
        bool is_tx;
        bool is_tx_from_off;
        u8 tx_retry;
        struct sk_buff *tx_skb;
        struct at86rf230_state_change tx;
+
+       struct at86rf230_trac trac;
 };
 
 #define AT86RF2XX_NUMREGS 0x3F
@@ -110,8 +120,7 @@ struct at86rf230_local {
 static void
 at86rf230_async_state_change(struct at86rf230_local *lp,
                             struct at86rf230_state_change *ctx,
-                            const u8 state, void (*complete)(void *context),
-                            const bool irq_enable);
+                            const u8 state, void (*complete)(void *context));
 
 static inline void
 at86rf230_sleep(struct at86rf230_local *lp)
@@ -340,8 +349,10 @@ at86rf230_async_error_recover(void *context)
        struct at86rf230_local *lp = ctx->lp;
 
        lp->is_tx = 0;
-       at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL, false);
+       at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL);
        ieee802154_wake_queue(lp->hw);
+       if (ctx->free)
+               kfree(ctx);
 }
 
 static inline void
@@ -351,15 +362,14 @@ at86rf230_async_error(struct at86rf230_local *lp,
        dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
 
        at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
-                                    at86rf230_async_error_recover, false);
+                                    at86rf230_async_error_recover);
 }
 
 /* Generic function to get some register value in async mode */
 static void
-at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg,
+at86rf230_async_read_reg(struct at86rf230_local *lp, u8 reg,
                         struct at86rf230_state_change *ctx,
-                        void (*complete)(void *context),
-                        const bool irq_enable)
+                        void (*complete)(void *context))
 {
        int rc;
 
@@ -367,22 +377,24 @@ at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg,
 
        tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
        ctx->msg.complete = complete;
-       ctx->irq_enable = irq_enable;
        rc = spi_async(lp->spi, &ctx->msg);
-       if (rc) {
-               if (irq_enable)
-                       enable_irq(ctx->irq);
-
+       if (rc)
                at86rf230_async_error(lp, ctx, rc);
-       }
 }
 
-static inline u8 at86rf230_state_to_force(u8 state)
+static void
+at86rf230_async_write_reg(struct at86rf230_local *lp, u8 reg, u8 val,
+                         struct at86rf230_state_change *ctx,
+                         void (*complete)(void *context))
 {
-       if (state == STATE_TX_ON)
-               return STATE_FORCE_TX_ON;
-       else
-               return STATE_FORCE_TRX_OFF;
+       int rc;
+
+       ctx->buf[0] = (reg & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
+       ctx->buf[1] = val;
+       ctx->msg.complete = complete;
+       rc = spi_async(lp->spi, &ctx->msg);
+       if (rc)
+               at86rf230_async_error(lp, ctx, rc);
 }
 
 static void
@@ -426,12 +438,11 @@ at86rf230_async_state_assert(void *context)
                                u8 state = ctx->to_state;
 
                                if (lp->tx_retry >= AT86RF2XX_MAX_TX_RETRIES)
-                                       state = at86rf230_state_to_force(state);
+                                       state = STATE_FORCE_TRX_OFF;
                                lp->tx_retry++;
 
                                at86rf230_async_state_change(lp, ctx, state,
-                                                            ctx->complete,
-                                                            ctx->irq_enable);
+                                                            ctx->complete);
                                return;
                        }
                }
@@ -452,8 +463,7 @@ static enum hrtimer_restart at86rf230_async_state_timer(struct hrtimer *timer)
        struct at86rf230_local *lp = ctx->lp;
 
        at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
-                                at86rf230_async_state_assert,
-                                ctx->irq_enable);
+                                at86rf230_async_state_assert);
 
        return HRTIMER_NORESTART;
 }
@@ -558,14 +568,12 @@ at86rf230_async_state_change_start(void *context)
        struct at86rf230_local *lp = ctx->lp;
        u8 *buf = ctx->buf;
        const u8 trx_state = buf[1] & TRX_STATE_MASK;
-       int rc;
 
        /* Check for "possible" STATE_TRANSITION_IN_PROGRESS */
        if (trx_state == STATE_TRANSITION_IN_PROGRESS) {
                udelay(1);
                at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
-                                        at86rf230_async_state_change_start,
-                                        ctx->irq_enable);
+                                        at86rf230_async_state_change_start);
                return;
        }
 
@@ -582,31 +590,20 @@ at86rf230_async_state_change_start(void *context)
        /* Going into the next step for a state change which do a timing
         * relevant delay.
         */
-       buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
-       buf[1] = ctx->to_state;
-       ctx->msg.complete = at86rf230_async_state_delay;
-       rc = spi_async(lp->spi, &ctx->msg);
-       if (rc) {
-               if (ctx->irq_enable)
-                       enable_irq(ctx->irq);
-
-               at86rf230_async_error(lp, ctx, rc);
-       }
+       at86rf230_async_write_reg(lp, RG_TRX_STATE, ctx->to_state, ctx,
+                                 at86rf230_async_state_delay);
 }
 
 static void
 at86rf230_async_state_change(struct at86rf230_local *lp,
                             struct at86rf230_state_change *ctx,
-                            const u8 state, void (*complete)(void *context),
-                            const bool irq_enable)
+                            const u8 state, void (*complete)(void *context))
 {
        /* Initialization for the state change context */
        ctx->to_state = state;
        ctx->complete = complete;
-       ctx->irq_enable = irq_enable;
        at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
-                                at86rf230_async_state_change_start,
-                                irq_enable);
+                                at86rf230_async_state_change_start);
 }
 
 static void
@@ -628,8 +625,7 @@ at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state)
        unsigned long rc;
 
        at86rf230_async_state_change(lp, &lp->state, state,
-                                    at86rf230_sync_state_change_complete,
-                                    false);
+                                    at86rf230_sync_state_change_complete);
 
        rc = wait_for_completion_timeout(&lp->state_complete,
                                         msecs_to_jiffies(100));
@@ -647,9 +643,8 @@ at86rf230_tx_complete(void *context)
        struct at86rf230_state_change *ctx = context;
        struct at86rf230_local *lp = ctx->lp;
 
-       enable_irq(ctx->irq);
-
        ieee802154_xmit_complete(lp->hw, lp->tx_skb, false);
+       kfree(ctx);
 }
 
 static void
@@ -659,7 +654,7 @@ at86rf230_tx_on(void *context)
        struct at86rf230_local *lp = ctx->lp;
 
        at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON,
-                                    at86rf230_tx_complete, true);
+                                    at86rf230_tx_complete);
 }
 
 static void
@@ -667,28 +662,33 @@ at86rf230_tx_trac_check(void *context)
 {
        struct at86rf230_state_change *ctx = context;
        struct at86rf230_local *lp = ctx->lp;
-       const u8 *buf = ctx->buf;
-       const u8 trac = (buf[1] & 0xe0) >> 5;
 
-       /* If trac status is different than zero we need to do a state change
-        * to STATE_FORCE_TRX_OFF then STATE_RX_AACK_ON to recover the
-        * transceiver.
-        */
-       if (trac)
-               at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
-                                            at86rf230_tx_on, true);
-       else
-               at86rf230_tx_on(context);
-}
+       if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) {
+               u8 trac = TRAC_MASK(ctx->buf[1]);
 
-static void
-at86rf230_tx_trac_status(void *context)
-{
-       struct at86rf230_state_change *ctx = context;
-       struct at86rf230_local *lp = ctx->lp;
+               switch (trac) {
+               case TRAC_SUCCESS:
+                       lp->trac.success++;
+                       break;
+               case TRAC_SUCCESS_DATA_PENDING:
+                       lp->trac.success_data_pending++;
+                       break;
+               case TRAC_CHANNEL_ACCESS_FAILURE:
+                       lp->trac.channel_access_failure++;
+                       break;
+               case TRAC_NO_ACK:
+                       lp->trac.no_ack++;
+                       break;
+               case TRAC_INVALID:
+                       lp->trac.invalid++;
+                       break;
+               default:
+                       WARN_ONCE(1, "received tx trac status %d\n", trac);
+                       break;
+               }
+       }
 
-       at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
-                                at86rf230_tx_trac_check, true);
+       at86rf230_async_state_change(lp, ctx, STATE_TX_ON, at86rf230_tx_on);
 }
 
 static void
@@ -696,7 +696,6 @@ at86rf230_rx_read_frame_complete(void *context)
 {
        struct at86rf230_state_change *ctx = context;
        struct at86rf230_local *lp = ctx->lp;
-       u8 rx_local_buf[AT86RF2XX_MAX_BUF];
        const u8 *buf = ctx->buf;
        struct sk_buff *skb;
        u8 len, lqi;
@@ -708,63 +707,68 @@ at86rf230_rx_read_frame_complete(void *context)
        }
        lqi = buf[2 + len];
 
-       memcpy(rx_local_buf, buf + 2, len);
-       ctx->trx.len = 2;
-       enable_irq(ctx->irq);
-
        skb = dev_alloc_skb(IEEE802154_MTU);
        if (!skb) {
                dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n");
+               kfree(ctx);
                return;
        }
 
-       memcpy(skb_put(skb, len), rx_local_buf, len);
+       memcpy(skb_put(skb, len), buf + 2, len);
        ieee802154_rx_irqsafe(lp->hw, skb, lqi);
+       kfree(ctx);
 }
 
 static void
-at86rf230_rx_read_frame(void *context)
+at86rf230_rx_trac_check(void *context)
 {
        struct at86rf230_state_change *ctx = context;
        struct at86rf230_local *lp = ctx->lp;
        u8 *buf = ctx->buf;
        int rc;
 
+       if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) {
+               u8 trac = TRAC_MASK(buf[1]);
+
+               switch (trac) {
+               case TRAC_SUCCESS:
+                       lp->trac.success++;
+                       break;
+               case TRAC_SUCCESS_WAIT_FOR_ACK:
+                       lp->trac.success_wait_for_ack++;
+                       break;
+               case TRAC_INVALID:
+                       lp->trac.invalid++;
+                       break;
+               default:
+                       WARN_ONCE(1, "received rx trac status %d\n", trac);
+                       break;
+               }
+       }
+
        buf[0] = CMD_FB;
        ctx->trx.len = AT86RF2XX_MAX_BUF;
        ctx->msg.complete = at86rf230_rx_read_frame_complete;
        rc = spi_async(lp->spi, &ctx->msg);
        if (rc) {
                ctx->trx.len = 2;
-               enable_irq(ctx->irq);
                at86rf230_async_error(lp, ctx, rc);
        }
 }
 
 static void
-at86rf230_rx_trac_check(void *context)
+at86rf230_irq_trx_end(void *context)
 {
-       /* Possible check on trac status here. This could be useful to make
-        * some stats why receive is failed. Not used at the moment, but it's
-        * maybe timing relevant. Datasheet doesn't say anything about this.
-        * The programming guide say do it so.
-        */
-
-       at86rf230_rx_read_frame(context);
-}
+       struct at86rf230_state_change *ctx = context;
+       struct at86rf230_local *lp = ctx->lp;
 
-static void
-at86rf230_irq_trx_end(struct at86rf230_local *lp)
-{
        if (lp->is_tx) {
                lp->is_tx = 0;
-               at86rf230_async_state_change(lp, &lp->irq,
-                                            STATE_FORCE_TX_ON,
-                                            at86rf230_tx_trac_status,
-                                            true);
+               at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
+                                        at86rf230_tx_trac_check);
        } else {
-               at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
-                                        at86rf230_rx_trac_check, true);
+               at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
+                                        at86rf230_rx_trac_check);
        }
 }
 
@@ -774,32 +778,59 @@ at86rf230_irq_status(void *context)
        struct at86rf230_state_change *ctx = context;
        struct at86rf230_local *lp = ctx->lp;
        const u8 *buf = ctx->buf;
-       const u8 irq = buf[1];
+       u8 irq = buf[1];
+
+       enable_irq(lp->spi->irq);
 
        if (irq & IRQ_TRX_END) {
-               at86rf230_irq_trx_end(lp);
+               at86rf230_irq_trx_end(ctx);
        } else {
-               enable_irq(ctx->irq);
                dev_err(&lp->spi->dev, "not supported irq %02x received\n",
                        irq);
+               kfree(ctx);
        }
 }
 
+static void
+at86rf230_setup_spi_messages(struct at86rf230_local *lp,
+                            struct at86rf230_state_change *state)
+{
+       state->lp = lp;
+       state->irq = lp->spi->irq;
+       spi_message_init(&state->msg);
+       state->msg.context = state;
+       state->trx.len = 2;
+       state->trx.tx_buf = state->buf;
+       state->trx.rx_buf = state->buf;
+       spi_message_add_tail(&state->trx, &state->msg);
+       hrtimer_init(&state->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       state->timer.function = at86rf230_async_state_timer;
+}
+
 static irqreturn_t at86rf230_isr(int irq, void *data)
 {
        struct at86rf230_local *lp = data;
-       struct at86rf230_state_change *ctx = &lp->irq;
-       u8 *buf = ctx->buf;
+       struct at86rf230_state_change *ctx;
        int rc;
 
        disable_irq_nosync(irq);
 
-       buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
+       ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
+       if (!ctx) {
+               enable_irq(irq);
+               return IRQ_NONE;
+       }
+
+       at86rf230_setup_spi_messages(lp, ctx);
+       /* tell on error handling to free ctx */
+       ctx->free = true;
+
+       ctx->buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
        ctx->msg.complete = at86rf230_irq_status;
        rc = spi_async(lp->spi, &ctx->msg);
        if (rc) {
-               enable_irq(irq);
                at86rf230_async_error(lp, ctx, rc);
+               enable_irq(irq);
                return IRQ_NONE;
        }
 
@@ -811,21 +842,14 @@ at86rf230_write_frame_complete(void *context)
 {
        struct at86rf230_state_change *ctx = context;
        struct at86rf230_local *lp = ctx->lp;
-       u8 *buf = ctx->buf;
-       int rc;
 
        ctx->trx.len = 2;
 
-       if (gpio_is_valid(lp->slp_tr)) {
+       if (gpio_is_valid(lp->slp_tr))
                at86rf230_slp_tr_rising_edge(lp);
-       } else {
-               buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
-               buf[1] = STATE_BUSY_TX;
-               ctx->msg.complete = NULL;
-               rc = spi_async(lp->spi, &ctx->msg);
-               if (rc)
-                       at86rf230_async_error(lp, ctx, rc);
-       }
+       else
+               at86rf230_async_write_reg(lp, RG_TRX_STATE, STATE_BUSY_TX, ctx,
+                                         NULL);
 }
 
 static void
@@ -858,7 +882,7 @@ at86rf230_xmit_tx_on(void *context)
        struct at86rf230_local *lp = ctx->lp;
 
        at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
-                                    at86rf230_write_frame, false);
+                                    at86rf230_write_frame);
 }
 
 static void
@@ -871,12 +895,10 @@ at86rf230_xmit_start(void *context)
        if (lp->is_tx_from_off) {
                lp->is_tx_from_off = false;
                at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
-                                            at86rf230_write_frame,
-                                            false);
+                                            at86rf230_write_frame);
        } else {
                at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
-                                            at86rf230_xmit_tx_on,
-                                            false);
+                                            at86rf230_xmit_tx_on);
        }
 }
 
@@ -899,7 +921,7 @@ at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
        if (time_is_before_jiffies(lp->cal_timeout)) {
                lp->is_tx_from_off = true;
                at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF,
-                                            at86rf230_xmit_start, false);
+                                            at86rf230_xmit_start);
        } else {
                at86rf230_xmit_start(ctx);
        }
@@ -920,6 +942,10 @@ at86rf230_start(struct ieee802154_hw *hw)
 {
        struct at86rf230_local *lp = hw->priv;
 
+       /* reset trac stats on start */
+       if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS))
+               memset(&lp->trac, 0, sizeof(struct at86rf230_trac));
+
        at86rf230_awake(lp);
        enable_irq(lp->spi->irq);
 
@@ -1354,10 +1380,6 @@ static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim)
                return rc;
 
        irq_type = irq_get_trigger_type(lp->spi->irq);
-       if (irq_type == IRQ_TYPE_EDGE_RISING ||
-           irq_type == IRQ_TYPE_EDGE_FALLING)
-               dev_warn(&lp->spi->dev,
-                        "Using edge triggered irq's are not recommended!\n");
        if (irq_type == IRQ_TYPE_EDGE_FALLING ||
            irq_type == IRQ_TYPE_LEVEL_LOW)
                irq_pol = IRQ_ACTIVE_LOW;
@@ -1583,42 +1605,65 @@ not_supp:
        return rc;
 }
 
-static void
-at86rf230_setup_spi_messages(struct at86rf230_local *lp)
+#ifdef CONFIG_IEEE802154_AT86RF230_DEBUGFS
+static struct dentry *at86rf230_debugfs_root;
+
+static int at86rf230_stats_show(struct seq_file *file, void *offset)
+{
+       struct at86rf230_local *lp = file->private;
+
+       seq_printf(file, "SUCCESS:\t\t%8llu\n", lp->trac.success);
+       seq_printf(file, "SUCCESS_DATA_PENDING:\t%8llu\n",
+                  lp->trac.success_data_pending);
+       seq_printf(file, "SUCCESS_WAIT_FOR_ACK:\t%8llu\n",
+                  lp->trac.success_wait_for_ack);
+       seq_printf(file, "CHANNEL_ACCESS_FAILURE:\t%8llu\n",
+                  lp->trac.channel_access_failure);
+       seq_printf(file, "NO_ACK:\t\t\t%8llu\n", lp->trac.no_ack);
+       seq_printf(file, "INVALID:\t\t%8llu\n", lp->trac.invalid);
+       return 0;
+}
+
+static int at86rf230_stats_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, at86rf230_stats_show, inode->i_private);
+}
+
+static const struct file_operations at86rf230_stats_fops = {
+       .open           = at86rf230_stats_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int at86rf230_debugfs_init(struct at86rf230_local *lp)
+{
+       char debugfs_dir_name[DNAME_INLINE_LEN + 1] = "at86rf230-";
+       struct dentry *stats;
+
+       strncat(debugfs_dir_name, dev_name(&lp->spi->dev), DNAME_INLINE_LEN);
+
+       at86rf230_debugfs_root = debugfs_create_dir(debugfs_dir_name, NULL);
+       if (!at86rf230_debugfs_root)
+               return -ENOMEM;
+
+       stats = debugfs_create_file("trac_stats", S_IRUGO,
+                                   at86rf230_debugfs_root, lp,
+                                   &at86rf230_stats_fops);
+       if (!stats)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void at86rf230_debugfs_remove(void)
 {
-       lp->state.lp = lp;
-       lp->state.irq = lp->spi->irq;
-       spi_message_init(&lp->state.msg);
-       lp->state.msg.context = &lp->state;
-       lp->state.trx.len = 2;
-       lp->state.trx.tx_buf = lp->state.buf;
-       lp->state.trx.rx_buf = lp->state.buf;
-       spi_message_add_tail(&lp->state.trx, &lp->state.msg);
-       hrtimer_init(&lp->state.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-       lp->state.timer.function = at86rf230_async_state_timer;
-
-       lp->irq.lp = lp;
-       lp->irq.irq = lp->spi->irq;
-       spi_message_init(&lp->irq.msg);
-       lp->irq.msg.context = &lp->irq;
-       lp->irq.trx.len = 2;
-       lp->irq.trx.tx_buf = lp->irq.buf;
-       lp->irq.trx.rx_buf = lp->irq.buf;
-       spi_message_add_tail(&lp->irq.trx, &lp->irq.msg);
-       hrtimer_init(&lp->irq.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-       lp->irq.timer.function = at86rf230_async_state_timer;
-
-       lp->tx.lp = lp;
-       lp->tx.irq = lp->spi->irq;
-       spi_message_init(&lp->tx.msg);
-       lp->tx.msg.context = &lp->tx;
-       lp->tx.trx.len = 2;
-       lp->tx.trx.tx_buf = lp->tx.buf;
-       lp->tx.trx.rx_buf = lp->tx.buf;
-       spi_message_add_tail(&lp->tx.trx, &lp->tx.msg);
-       hrtimer_init(&lp->tx.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-       lp->tx.timer.function = at86rf230_async_state_timer;
+       debugfs_remove_recursive(at86rf230_debugfs_root);
 }
+#else
+static int at86rf230_debugfs_init(struct at86rf230_local *lp) { return 0; }
+static void at86rf230_debugfs_remove(void) { }
+#endif
 
 static int at86rf230_probe(struct spi_device *spi)
 {
@@ -1681,7 +1726,8 @@ static int at86rf230_probe(struct spi_device *spi)
                goto free_dev;
        }
 
-       at86rf230_setup_spi_messages(lp);
+       at86rf230_setup_spi_messages(lp, &lp->state);
+       at86rf230_setup_spi_messages(lp, &lp->tx);
 
        rc = at86rf230_detect_device(lp);
        if (rc < 0)
@@ -1715,12 +1761,18 @@ static int at86rf230_probe(struct spi_device *spi)
        /* going into sleep by default */
        at86rf230_sleep(lp);
 
-       rc = ieee802154_register_hw(lp->hw);
+       rc = at86rf230_debugfs_init(lp);
        if (rc)
                goto free_dev;
 
+       rc = ieee802154_register_hw(lp->hw);
+       if (rc)
+               goto free_debugfs;
+
        return rc;
 
+free_debugfs:
+       at86rf230_debugfs_remove();
 free_dev:
        ieee802154_free_hw(lp->hw);
 
@@ -1735,6 +1787,7 @@ static int at86rf230_remove(struct spi_device *spi)
        at86rf230_write_subreg(lp, SR_IRQ_MASK, 0);
        ieee802154_unregister_hw(lp->hw);
        ieee802154_free_hw(lp->hw);
+       at86rf230_debugfs_remove();
        dev_dbg(&spi->dev, "unregistered at86rf230\n");
 
        return 0;
index 1e6d1cc677f681cefa4087ed4d2696d3a79847a1..fd9c1f467f63a0f8636537e17e281aa4e169d7ed 100644 (file)
 #define STATE_TRANSITION_IN_PROGRESS 0x1F
 
 #define TRX_STATE_MASK         (0x1F)
+#define TRAC_MASK(x)           ((x & 0xe0) >> 5)
+
+#define TRAC_SUCCESS                   0
+#define TRAC_SUCCESS_DATA_PENDING      1
+#define TRAC_SUCCESS_WAIT_FOR_ACK      2
+#define TRAC_CHANNEL_ACCESS_FAILURE    3
+#define TRAC_NO_ACK                    5
+#define TRAC_INVALID                   7
 
 #endif /* !_AT86RF230_H */
index 80dfc725b8dc4d7efca4dbc23e891e13b423a839..199a94a9c8bcf83aeb78c5e694808a71227a1789 100644 (file)
@@ -559,6 +559,7 @@ static int atusb_get_and_show_chip(struct atusb *atusb)
 {
        struct usb_device *usb_dev = atusb->usb_dev;
        uint8_t man_id_0, man_id_1, part_num, version_num;
+       const char *chip;
 
        man_id_0 = atusb_read_reg(atusb, RG_MAN_ID_0);
        man_id_1 = atusb_read_reg(atusb, RG_MAN_ID_1);
@@ -574,14 +575,22 @@ static int atusb_get_and_show_chip(struct atusb *atusb)
                        man_id_1, man_id_0);
                goto fail;
        }
-       if (part_num != 3 && part_num != 2) {
+
+       switch (part_num) {
+       case 2:
+               chip = "AT86RF230";
+               break;
+       case 3:
+               chip = "AT86RF231";
+               break;
+       default:
                dev_err(&usb_dev->dev,
                        "unexpected transceiver, part 0x%02x version 0x%02x\n",
                        part_num, version_num);
                goto fail;
        }
 
-       dev_info(&usb_dev->dev, "ATUSB: AT86RF231 version %d\n", version_num);
+       dev_info(&usb_dev->dev, "ATUSB: %s version %d\n", chip, version_num);
 
        return 0;
 
index 997724b8e4343b3ab068a86463393e8008f6ee91..aca0fb3cccbf5f294e689daae6722638356badab 100644 (file)
 #include <linux/spi/spi.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/regmap.h>
 #include <linux/ieee802154.h>
+#include <linux/irq.h>
 #include <net/cfg802154.h>
 #include <net/mac802154.h>
 
 /* MRF24J40 Short Address Registers */
-#define REG_RXMCR    0x00  /* Receive MAC control */
-#define REG_PANIDL   0x01  /* PAN ID (low) */
-#define REG_PANIDH   0x02  /* PAN ID (high) */
-#define REG_SADRL    0x03  /* Short address (low) */
-#define REG_SADRH    0x04  /* Short address (high) */
-#define REG_EADR0    0x05  /* Long address (low) (high is EADR7) */
-#define REG_TXMCR    0x11  /* Transmit MAC control */
-#define REG_PACON0   0x16  /* Power Amplifier Control */
-#define REG_PACON1   0x17  /* Power Amplifier Control */
-#define REG_PACON2   0x18  /* Power Amplifier Control */
-#define REG_TXNCON   0x1B  /* Transmit Normal FIFO Control */
-#define REG_TXSTAT   0x24  /* TX MAC Status Register */
-#define REG_SOFTRST  0x2A  /* Soft Reset */
-#define REG_TXSTBL   0x2E  /* TX Stabilization */
-#define REG_INTSTAT  0x31  /* Interrupt Status */
-#define REG_INTCON   0x32  /* Interrupt Control */
-#define REG_GPIO     0x33  /* GPIO */
-#define REG_TRISGPIO 0x34  /* GPIO direction */
-#define REG_RFCTL    0x36  /* RF Control Mode Register */
-#define REG_BBREG1   0x39  /* Baseband Registers */
-#define REG_BBREG2   0x3A  /* */
-#define REG_BBREG6   0x3E  /* */
-#define REG_CCAEDTH  0x3F  /* Energy Detection Threshold */
+#define REG_RXMCR      0x00  /* Receive MAC control */
+#define BIT_PROMI      BIT(0)
+#define BIT_ERRPKT     BIT(1)
+#define BIT_NOACKRSP   BIT(5)
+#define BIT_PANCOORD   BIT(3)
+
+#define REG_PANIDL     0x01  /* PAN ID (low) */
+#define REG_PANIDH     0x02  /* PAN ID (high) */
+#define REG_SADRL      0x03  /* Short address (low) */
+#define REG_SADRH      0x04  /* Short address (high) */
+#define REG_EADR0      0x05  /* Long address (low) (high is EADR7) */
+#define REG_EADR1      0x06
+#define REG_EADR2      0x07
+#define REG_EADR3      0x08
+#define REG_EADR4      0x09
+#define REG_EADR5      0x0A
+#define REG_EADR6      0x0B
+#define REG_EADR7      0x0C
+#define REG_RXFLUSH    0x0D
+#define REG_ORDER      0x10
+#define REG_TXMCR      0x11  /* Transmit MAC control */
+#define TXMCR_MIN_BE_SHIFT             3
+#define TXMCR_MIN_BE_MASK              0x18
+#define TXMCR_CSMA_RETRIES_SHIFT       0
+#define TXMCR_CSMA_RETRIES_MASK                0x07
+
+#define REG_ACKTMOUT   0x12
+#define REG_ESLOTG1    0x13
+#define REG_SYMTICKL   0x14
+#define REG_SYMTICKH   0x15
+#define REG_PACON0     0x16  /* Power Amplifier Control */
+#define REG_PACON1     0x17  /* Power Amplifier Control */
+#define REG_PACON2     0x18  /* Power Amplifier Control */
+#define REG_TXBCON0    0x1A
+#define REG_TXNCON     0x1B  /* Transmit Normal FIFO Control */
+#define BIT_TXNTRIG    BIT(0)
+#define BIT_TXNACKREQ  BIT(2)
+
+#define REG_TXG1CON    0x1C
+#define REG_TXG2CON    0x1D
+#define REG_ESLOTG23   0x1E
+#define REG_ESLOTG45   0x1F
+#define REG_ESLOTG67   0x20
+#define REG_TXPEND     0x21
+#define REG_WAKECON    0x22
+#define REG_FROMOFFSET 0x23
+#define REG_TXSTAT     0x24  /* TX MAC Status Register */
+#define REG_TXBCON1    0x25
+#define REG_GATECLK    0x26
+#define REG_TXTIME     0x27
+#define REG_HSYMTMRL   0x28
+#define REG_HSYMTMRH   0x29
+#define REG_SOFTRST    0x2A  /* Soft Reset */
+#define REG_SECCON0    0x2C
+#define REG_SECCON1    0x2D
+#define REG_TXSTBL     0x2E  /* TX Stabilization */
+#define REG_RXSR       0x30
+#define REG_INTSTAT    0x31  /* Interrupt Status */
+#define BIT_TXNIF      BIT(0)
+#define BIT_RXIF       BIT(3)
+
+#define REG_INTCON     0x32  /* Interrupt Control */
+#define BIT_TXNIE      BIT(0)
+#define BIT_RXIE       BIT(3)
+
+#define REG_GPIO       0x33  /* GPIO */
+#define REG_TRISGPIO   0x34  /* GPIO direction */
+#define REG_SLPACK     0x35
+#define REG_RFCTL      0x36  /* RF Control Mode Register */
+#define BIT_RFRST      BIT(2)
+
+#define REG_SECCR2     0x37
+#define REG_BBREG0     0x38
+#define REG_BBREG1     0x39  /* Baseband Registers */
+#define BIT_RXDECINV   BIT(2)
+
+#define REG_BBREG2     0x3A  /* */
+#define BBREG2_CCA_MODE_SHIFT  6
+#define BBREG2_CCA_MODE_MASK   0xc0
+
+#define REG_BBREG3     0x3B
+#define REG_BBREG4     0x3C
+#define REG_BBREG6     0x3E  /* */
+#define REG_CCAEDTH    0x3F  /* Energy Detection Threshold */
 
 /* MRF24J40 Long Address Registers */
-#define REG_RFCON0     0x200  /* RF Control Registers */
-#define REG_RFCON1     0x201
-#define REG_RFCON2     0x202
-#define REG_RFCON3     0x203
-#define REG_RFCON5     0x205
-#define REG_RFCON6     0x206
-#define REG_RFCON7     0x207
-#define REG_RFCON8     0x208
-#define REG_RSSI       0x210
-#define REG_SLPCON0    0x211  /* Sleep Clock Control Registers */
-#define REG_SLPCON1    0x220
-#define REG_WAKETIMEL  0x222  /* Wake-up Time Match Value Low */
-#define REG_WAKETIMEH  0x223  /* Wake-up Time Match Value High */
-#define REG_TESTMODE   0x22F  /* Test mode */
-#define REG_RX_FIFO    0x300  /* Receive FIFO */
+#define REG_RFCON0     0x200  /* RF Control Registers */
+#define RFCON0_CH_SHIFT        4
+#define RFCON0_CH_MASK 0xf0
+#define RFOPT_RECOMMEND        3
+
+#define REG_RFCON1     0x201
+#define REG_RFCON2     0x202
+#define REG_RFCON3     0x203
+
+#define TXPWRL_MASK    0xc0
+#define TXPWRL_SHIFT   6
+#define TXPWRL_30      0x3
+#define TXPWRL_20      0x2
+#define TXPWRL_10      0x1
+#define TXPWRL_0       0x0
+
+#define TXPWRS_MASK    0x38
+#define TXPWRS_SHIFT   3
+#define TXPWRS_6_3     0x7
+#define TXPWRS_4_9     0x6
+#define TXPWRS_3_7     0x5
+#define TXPWRS_2_8     0x4
+#define TXPWRS_1_9     0x3
+#define TXPWRS_1_2     0x2
+#define TXPWRS_0_5     0x1
+#define TXPWRS_0       0x0
+
+#define REG_RFCON5     0x205
+#define REG_RFCON6     0x206
+#define REG_RFCON7     0x207
+#define REG_RFCON8     0x208
+#define REG_SLPCAL0    0x209
+#define REG_SLPCAL1    0x20A
+#define REG_SLPCAL2    0x20B
+#define REG_RFSTATE    0x20F
+#define REG_RSSI       0x210
+#define REG_SLPCON0    0x211  /* Sleep Clock Control Registers */
+#define BIT_INTEDGE    BIT(1)
+
+#define REG_SLPCON1    0x220
+#define REG_WAKETIMEL  0x222  /* Wake-up Time Match Value Low */
+#define REG_WAKETIMEH  0x223  /* Wake-up Time Match Value High */
+#define REG_REMCNTL    0x224
+#define REG_REMCNTH    0x225
+#define REG_MAINCNT0   0x226
+#define REG_MAINCNT1   0x227
+#define REG_MAINCNT2   0x228
+#define REG_MAINCNT3   0x229
+#define REG_TESTMODE   0x22F  /* Test mode */
+#define REG_ASSOEAR0   0x230
+#define REG_ASSOEAR1   0x231
+#define REG_ASSOEAR2   0x232
+#define REG_ASSOEAR3   0x233
+#define REG_ASSOEAR4   0x234
+#define REG_ASSOEAR5   0x235
+#define REG_ASSOEAR6   0x236
+#define REG_ASSOEAR7   0x237
+#define REG_ASSOSAR0   0x238
+#define REG_ASSOSAR1   0x239
+#define REG_UNONCE0    0x240
+#define REG_UNONCE1    0x241
+#define REG_UNONCE2    0x242
+#define REG_UNONCE3    0x243
+#define REG_UNONCE4    0x244
+#define REG_UNONCE5    0x245
+#define REG_UNONCE6    0x246
+#define REG_UNONCE7    0x247
+#define REG_UNONCE8    0x248
+#define REG_UNONCE9    0x249
+#define REG_UNONCE10   0x24A
+#define REG_UNONCE11   0x24B
+#define REG_UNONCE12   0x24C
+#define REG_RX_FIFO    0x300  /* Receive FIFO */
 
 /* Device configuration: Only channels 11-26 on page 0 are supported. */
 #define MRF24J40_CHAN_MIN 11
@@ -81,11 +202,52 @@ struct mrf24j40 {
        struct spi_device *spi;
        struct ieee802154_hw *hw;
 
-       struct mutex buffer_mutex; /* only used to protect buf */
-       struct completion tx_complete;
-       u8 *buf; /* 3 bytes. Used for SPI single-register transfers. */
+       struct regmap *regmap_short;
+       struct regmap *regmap_long;
+
+       /* for writing txfifo */
+       struct spi_message tx_msg;
+       u8 tx_hdr_buf[2];
+       struct spi_transfer tx_hdr_trx;
+       u8 tx_len_buf[2];
+       struct spi_transfer tx_len_trx;
+       struct spi_transfer tx_buf_trx;
+       struct sk_buff *tx_skb;
+
+       /* post transmit message to send frame out  */
+       struct spi_message tx_post_msg;
+       u8 tx_post_buf[2];
+       struct spi_transfer tx_post_trx;
+
+       /* for protect/unprotect/read length rxfifo */
+       struct spi_message rx_msg;
+       u8 rx_buf[3];
+       struct spi_transfer rx_trx;
+
+       /* receive handling */
+       struct spi_message rx_buf_msg;
+       u8 rx_addr_buf[2];
+       struct spi_transfer rx_addr_trx;
+       u8 rx_lqi_buf[2];
+       struct spi_transfer rx_lqi_trx;
+       u8 rx_fifo_buf[RX_FIFO_SIZE];
+       struct spi_transfer rx_fifo_buf_trx;
+
+       /* isr handling for reading intstat */
+       struct spi_message irq_msg;
+       u8 irq_buf[2];
+       struct spi_transfer irq_trx;
 };
 
+/* regmap information for short address register access */
+#define MRF24J40_SHORT_WRITE   0x01
+#define MRF24J40_SHORT_READ    0x00
+#define MRF24J40_SHORT_NUMREGS 0x3F
+
+/* regmap information for long address register access */
+#define MRF24J40_LONG_ACCESS   0x80
+#define MRF24J40_LONG_NUMREGS  0x38F
+
 /* Read/Write SPI Commands for Short and Long Address registers. */
 #define MRF24J40_READSHORT(reg) ((reg) << 1)
 #define MRF24J40_WRITESHORT(reg) ((reg) << 1 | 1)
@@ -97,118 +259,304 @@ struct mrf24j40 {
 
 #define printdev(X) (&X->spi->dev)
 
-static int write_short_reg(struct mrf24j40 *devrec, u8 reg, u8 value)
+static bool
+mrf24j40_short_reg_writeable(struct device *dev, unsigned int reg)
 {
-       int ret;
-       struct spi_message msg;
-       struct spi_transfer xfer = {
-               .len = 2,
-               .tx_buf = devrec->buf,
-               .rx_buf = devrec->buf,
-       };
+       switch (reg) {
+       case REG_RXMCR:
+       case REG_PANIDL:
+       case REG_PANIDH:
+       case REG_SADRL:
+       case REG_SADRH:
+       case REG_EADR0:
+       case REG_EADR1:
+       case REG_EADR2:
+       case REG_EADR3:
+       case REG_EADR4:
+       case REG_EADR5:
+       case REG_EADR6:
+       case REG_EADR7:
+       case REG_RXFLUSH:
+       case REG_ORDER:
+       case REG_TXMCR:
+       case REG_ACKTMOUT:
+       case REG_ESLOTG1:
+       case REG_SYMTICKL:
+       case REG_SYMTICKH:
+       case REG_PACON0:
+       case REG_PACON1:
+       case REG_PACON2:
+       case REG_TXBCON0:
+       case REG_TXNCON:
+       case REG_TXG1CON:
+       case REG_TXG2CON:
+       case REG_ESLOTG23:
+       case REG_ESLOTG45:
+       case REG_ESLOTG67:
+       case REG_TXPEND:
+       case REG_WAKECON:
+       case REG_FROMOFFSET:
+       case REG_TXBCON1:
+       case REG_GATECLK:
+       case REG_TXTIME:
+       case REG_HSYMTMRL:
+       case REG_HSYMTMRH:
+       case REG_SOFTRST:
+       case REG_SECCON0:
+       case REG_SECCON1:
+       case REG_TXSTBL:
+       case REG_RXSR:
+       case REG_INTCON:
+       case REG_TRISGPIO:
+       case REG_GPIO:
+       case REG_RFCTL:
+       case REG_SLPACK:
+       case REG_BBREG0:
+       case REG_BBREG1:
+       case REG_BBREG2:
+       case REG_BBREG3:
+       case REG_BBREG4:
+       case REG_BBREG6:
+       case REG_CCAEDTH:
+               return true;
+       default:
+               return false;
+       }
+}
 
-       spi_message_init(&msg);
-       spi_message_add_tail(&xfer, &msg);
+static bool
+mrf24j40_short_reg_readable(struct device *dev, unsigned int reg)
+{
+       bool rc;
+
+       /* all writeable are also readable */
+       rc = mrf24j40_short_reg_writeable(dev, reg);
+       if (rc)
+               return rc;
+
+       /* readonly regs */
+       switch (reg) {
+       case REG_TXSTAT:
+       case REG_INTSTAT:
+               return true;
+       default:
+               return false;
+       }
+}
 
-       mutex_lock(&devrec->buffer_mutex);
-       devrec->buf[0] = MRF24J40_WRITESHORT(reg);
-       devrec->buf[1] = value;
+static bool
+mrf24j40_short_reg_volatile(struct device *dev, unsigned int reg)
+{
+       /* can be changed during runtime */
+       switch (reg) {
+       case REG_TXSTAT:
+       case REG_INTSTAT:
+       case REG_RXFLUSH:
+       case REG_TXNCON:
+       case REG_SOFTRST:
+       case REG_RFCTL:
+       case REG_TXBCON0:
+       case REG_TXG1CON:
+       case REG_TXG2CON:
+       case REG_TXBCON1:
+       case REG_SECCON0:
+       case REG_RXSR:
+       case REG_SLPACK:
+       case REG_SECCR2:
+       case REG_BBREG6:
+       /* use them in spi_async and regmap so it's volatile */
+       case REG_BBREG1:
+               return true;
+       default:
+               return false;
+       }
+}
 
-       ret = spi_sync(devrec->spi, &msg);
-       if (ret)
-               dev_err(printdev(devrec),
-                       "SPI write Failed for short register 0x%hhx\n", reg);
+static bool
+mrf24j40_short_reg_precious(struct device *dev, unsigned int reg)
+{
+       /* don't clear irq line on read */
+       switch (reg) {
+       case REG_INTSTAT:
+               return true;
+       default:
+               return false;
+       }
+}
 
-       mutex_unlock(&devrec->buffer_mutex);
-       return ret;
+static const struct regmap_config mrf24j40_short_regmap = {
+       .name = "mrf24j40_short",
+       .reg_bits = 7,
+       .val_bits = 8,
+       .pad_bits = 1,
+       .write_flag_mask = MRF24J40_SHORT_WRITE,
+       .read_flag_mask = MRF24J40_SHORT_READ,
+       .cache_type = REGCACHE_RBTREE,
+       .max_register = MRF24J40_SHORT_NUMREGS,
+       .writeable_reg = mrf24j40_short_reg_writeable,
+       .readable_reg = mrf24j40_short_reg_readable,
+       .volatile_reg = mrf24j40_short_reg_volatile,
+       .precious_reg = mrf24j40_short_reg_precious,
+};
+
+static bool
+mrf24j40_long_reg_writeable(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case REG_RFCON0:
+       case REG_RFCON1:
+       case REG_RFCON2:
+       case REG_RFCON3:
+       case REG_RFCON5:
+       case REG_RFCON6:
+       case REG_RFCON7:
+       case REG_RFCON8:
+       case REG_SLPCAL2:
+       case REG_SLPCON0:
+       case REG_SLPCON1:
+       case REG_WAKETIMEL:
+       case REG_WAKETIMEH:
+       case REG_REMCNTL:
+       case REG_REMCNTH:
+       case REG_MAINCNT0:
+       case REG_MAINCNT1:
+       case REG_MAINCNT2:
+       case REG_MAINCNT3:
+       case REG_TESTMODE:
+       case REG_ASSOEAR0:
+       case REG_ASSOEAR1:
+       case REG_ASSOEAR2:
+       case REG_ASSOEAR3:
+       case REG_ASSOEAR4:
+       case REG_ASSOEAR5:
+       case REG_ASSOEAR6:
+       case REG_ASSOEAR7:
+       case REG_ASSOSAR0:
+       case REG_ASSOSAR1:
+       case REG_UNONCE0:
+       case REG_UNONCE1:
+       case REG_UNONCE2:
+       case REG_UNONCE3:
+       case REG_UNONCE4:
+       case REG_UNONCE5:
+       case REG_UNONCE6:
+       case REG_UNONCE7:
+       case REG_UNONCE8:
+       case REG_UNONCE9:
+       case REG_UNONCE10:
+       case REG_UNONCE11:
+       case REG_UNONCE12:
+               return true;
+       default:
+               return false;
+       }
 }
 
-static int read_short_reg(struct mrf24j40 *devrec, u8 reg, u8 *val)
+static bool
+mrf24j40_long_reg_readable(struct device *dev, unsigned int reg)
 {
-       int ret = -1;
-       struct spi_message msg;
-       struct spi_transfer xfer = {
-               .len = 2,
-               .tx_buf = devrec->buf,
-               .rx_buf = devrec->buf,
-       };
+       bool rc;
+
+       /* all writeable are also readable */
+       rc = mrf24j40_long_reg_writeable(dev, reg);
+       if (rc)
+               return rc;
+
+       /* readonly regs */
+       switch (reg) {
+       case REG_SLPCAL0:
+       case REG_SLPCAL1:
+       case REG_RFSTATE:
+       case REG_RSSI:
+               return true;
+       default:
+               return false;
+       }
+}
 
-       spi_message_init(&msg);
-       spi_message_add_tail(&xfer, &msg);
+static bool
+mrf24j40_long_reg_volatile(struct device *dev, unsigned int reg)
+{
+       /* can be changed during runtime */
+       switch (reg) {
+       case REG_SLPCAL0:
+       case REG_SLPCAL1:
+       case REG_SLPCAL2:
+       case REG_RFSTATE:
+       case REG_RSSI:
+       case REG_MAINCNT3:
+               return true;
+       default:
+               return false;
+       }
+}
 
-       mutex_lock(&devrec->buffer_mutex);
-       devrec->buf[0] = MRF24J40_READSHORT(reg);
-       devrec->buf[1] = 0;
+static const struct regmap_config mrf24j40_long_regmap = {
+       .name = "mrf24j40_long",
+       .reg_bits = 11,
+       .val_bits = 8,
+       .pad_bits = 5,
+       .write_flag_mask = MRF24J40_LONG_ACCESS,
+       .read_flag_mask = MRF24J40_LONG_ACCESS,
+       .cache_type = REGCACHE_RBTREE,
+       .max_register = MRF24J40_LONG_NUMREGS,
+       .writeable_reg = mrf24j40_long_reg_writeable,
+       .readable_reg = mrf24j40_long_reg_readable,
+       .volatile_reg = mrf24j40_long_reg_volatile,
+};
 
-       ret = spi_sync(devrec->spi, &msg);
-       if (ret)
-               dev_err(printdev(devrec),
-                       "SPI read Failed for short register 0x%hhx\n", reg);
-       else
-               *val = devrec->buf[1];
+static int mrf24j40_long_regmap_write(void *context, const void *data,
+                                     size_t count)
+{
+       struct spi_device *spi = context;
+       u8 buf[3];
 
-       mutex_unlock(&devrec->buffer_mutex);
-       return ret;
+       if (count > 3)
+               return -EINVAL;
+
+       /* regmap supports read/write mask only in frist byte
+        * long write access need to set the 12th bit, so we
+        * make special handling for write.
+        */
+       memcpy(buf, data, count);
+       buf[1] |= (1 << 4);
+
+       return spi_write(spi, buf, count);
 }
 
-static int read_long_reg(struct mrf24j40 *devrec, u16 reg, u8 *value)
+static int
+mrf24j40_long_regmap_read(void *context, const void *reg, size_t reg_size,
+                         void *val, size_t val_size)
 {
-       int ret;
-       u16 cmd;
-       struct spi_message msg;
-       struct spi_transfer xfer = {
-               .len = 3,
-               .tx_buf = devrec->buf,
-               .rx_buf = devrec->buf,
-       };
-
-       spi_message_init(&msg);
-       spi_message_add_tail(&xfer, &msg);
-
-       cmd = MRF24J40_READLONG(reg);
-       mutex_lock(&devrec->buffer_mutex);
-       devrec->buf[0] = cmd >> 8 & 0xff;
-       devrec->buf[1] = cmd & 0xff;
-       devrec->buf[2] = 0;
-
-       ret = spi_sync(devrec->spi, &msg);
-       if (ret)
-               dev_err(printdev(devrec),
-                       "SPI read Failed for long register 0x%hx\n", reg);
-       else
-               *value = devrec->buf[2];
+       struct spi_device *spi = context;
 
-       mutex_unlock(&devrec->buffer_mutex);
-       return ret;
+       return spi_write_then_read(spi, reg, reg_size, val, val_size);
 }
 
-static int write_long_reg(struct mrf24j40 *devrec, u16 reg, u8 val)
+static const struct regmap_bus mrf24j40_long_regmap_bus = {
+       .write = mrf24j40_long_regmap_write,
+       .read = mrf24j40_long_regmap_read,
+       .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+       .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static void write_tx_buf_complete(void *context)
 {
+       struct mrf24j40 *devrec = context;
+       __le16 fc = ieee802154_get_fc_from_skb(devrec->tx_skb);
+       u8 val = BIT_TXNTRIG;
        int ret;
-       u16 cmd;
-       struct spi_message msg;
-       struct spi_transfer xfer = {
-               .len = 3,
-               .tx_buf = devrec->buf,
-               .rx_buf = devrec->buf,
-       };
 
-       spi_message_init(&msg);
-       spi_message_add_tail(&xfer, &msg);
+       if (ieee802154_is_ackreq(fc))
+               val |= BIT_TXNACKREQ;
 
-       cmd = MRF24J40_WRITELONG(reg);
-       mutex_lock(&devrec->buffer_mutex);
-       devrec->buf[0] = cmd >> 8 & 0xff;
-       devrec->buf[1] = cmd & 0xff;
-       devrec->buf[2] = val;
+       devrec->tx_post_msg.complete = NULL;
+       devrec->tx_post_buf[0] = MRF24J40_WRITESHORT(REG_TXNCON);
+       devrec->tx_post_buf[1] = val;
 
-       ret = spi_sync(devrec->spi, &msg);
+       ret = spi_async(devrec->spi, &devrec->tx_post_msg);
        if (ret)
-               dev_err(printdev(devrec),
-                       "SPI write Failed for long register 0x%hx\n", reg);
-
-       mutex_unlock(&devrec->buffer_mutex);
-       return ret;
+               dev_err(printdev(devrec), "SPI write Failed for transmit buf\n");
 }
 
 /* This function relies on an undocumented write method. Once a write command
@@ -217,22 +565,8 @@ static int write_long_reg(struct mrf24j40 *devrec, u16 reg, u8 val)
 static int write_tx_buf(struct mrf24j40 *devrec, u16 reg,
                        const u8 *data, size_t length)
 {
-       int ret;
        u16 cmd;
-       u8 lengths[2];
-       struct spi_message msg;
-       struct spi_transfer addr_xfer = {
-               .len = 2,
-               .tx_buf = devrec->buf,
-       };
-       struct spi_transfer lengths_xfer = {
-               .len = 2,
-               .tx_buf = &lengths, /* TODO: Is DMA really required for SPI? */
-       };
-       struct spi_transfer data_xfer = {
-               .len = length,
-               .tx_buf = data,
-       };
+       int ret;
 
        /* Range check the length. 2 bytes are used for the length fields.*/
        if (length > TX_FIFO_SIZE-2) {
@@ -240,147 +574,29 @@ static int write_tx_buf(struct mrf24j40 *devrec, u16 reg,
                length = TX_FIFO_SIZE-2;
        }
 
-       spi_message_init(&msg);
-       spi_message_add_tail(&addr_xfer, &msg);
-       spi_message_add_tail(&lengths_xfer, &msg);
-       spi_message_add_tail(&data_xfer, &msg);
-
        cmd = MRF24J40_WRITELONG(reg);
-       mutex_lock(&devrec->buffer_mutex);
-       devrec->buf[0] = cmd >> 8 & 0xff;
-       devrec->buf[1] = cmd & 0xff;
-       lengths[0] = 0x0; /* Header Length. Set to 0 for now. TODO */
-       lengths[1] = length; /* Total length */
-
-       ret = spi_sync(devrec->spi, &msg);
+       devrec->tx_hdr_buf[0] = cmd >> 8 & 0xff;
+       devrec->tx_hdr_buf[1] = cmd & 0xff;
+       devrec->tx_len_buf[0] = 0x0; /* Header Length. Set to 0 for now. TODO */
+       devrec->tx_len_buf[1] = length; /* Total length */
+       devrec->tx_buf_trx.tx_buf = data;
+       devrec->tx_buf_trx.len = length;
+
+       ret = spi_async(devrec->spi, &devrec->tx_msg);
        if (ret)
                dev_err(printdev(devrec), "SPI write Failed for TX buf\n");
 
-       mutex_unlock(&devrec->buffer_mutex);
-       return ret;
-}
-
-static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec,
-                               u8 *data, u8 *len, u8 *lqi)
-{
-       u8 rx_len;
-       u8 addr[2];
-       u8 lqi_rssi[2];
-       u16 cmd;
-       int ret;
-       struct spi_message msg;
-       struct spi_transfer addr_xfer = {
-               .len = 2,
-               .tx_buf = &addr,
-       };
-       struct spi_transfer data_xfer = {
-               .len = 0x0, /* set below */
-               .rx_buf = data,
-       };
-       struct spi_transfer status_xfer = {
-               .len = 2,
-               .rx_buf = &lqi_rssi,
-       };
-
-       /* Get the length of the data in the RX FIFO. The length in this
-        * register exclues the 1-byte length field at the beginning. */
-       ret = read_long_reg(devrec, REG_RX_FIFO, &rx_len);
-       if (ret)
-               goto out;
-
-       /* Range check the RX FIFO length, accounting for the one-byte
-        * length field at the beginning. */
-       if (rx_len > RX_FIFO_SIZE-1) {
-               dev_err(printdev(devrec), "Invalid length read from device. Performing short read.\n");
-               rx_len = RX_FIFO_SIZE-1;
-       }
-
-       if (rx_len > *len) {
-               /* Passed in buffer wasn't big enough. Should never happen. */
-               dev_err(printdev(devrec), "Buffer not big enough. Performing short read\n");
-               rx_len = *len;
-       }
-
-       /* Set up the commands to read the data. */
-       cmd = MRF24J40_READLONG(REG_RX_FIFO+1);
-       addr[0] = cmd >> 8 & 0xff;
-       addr[1] = cmd & 0xff;
-       data_xfer.len = rx_len;
-
-       spi_message_init(&msg);
-       spi_message_add_tail(&addr_xfer, &msg);
-       spi_message_add_tail(&data_xfer, &msg);
-       spi_message_add_tail(&status_xfer, &msg);
-
-       ret = spi_sync(devrec->spi, &msg);
-       if (ret) {
-               dev_err(printdev(devrec), "SPI RX Buffer Read Failed.\n");
-               goto out;
-       }
-
-       *lqi = lqi_rssi[0];
-       *len = rx_len;
-
-#ifdef DEBUG
-       print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ",
-                      DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0);
-       pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n",
-                lqi_rssi[0], lqi_rssi[1]);
-#endif
-
-out:
        return ret;
 }
 
 static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
 {
        struct mrf24j40 *devrec = hw->priv;
-       u8 val;
-       int ret = 0;
 
        dev_dbg(printdev(devrec), "tx packet of %d bytes\n", skb->len);
+       devrec->tx_skb = skb;
 
-       ret = write_tx_buf(devrec, 0x000, skb->data, skb->len);
-       if (ret)
-               goto err;
-
-       reinit_completion(&devrec->tx_complete);
-
-       /* Set TXNTRIG bit of TXNCON to send packet */
-       ret = read_short_reg(devrec, REG_TXNCON, &val);
-       if (ret)
-               goto err;
-       val |= 0x1;
-       /* Set TXNACKREQ if the ACK bit is set in the packet. */
-       if (skb->data[0] & IEEE802154_FC_ACK_REQ)
-               val |= 0x4;
-       write_short_reg(devrec, REG_TXNCON, val);
-
-       /* Wait for the device to send the TX complete interrupt. */
-       ret = wait_for_completion_interruptible_timeout(
-                                               &devrec->tx_complete,
-                                               5 * HZ);
-       if (ret == -ERESTARTSYS)
-               goto err;
-       if (ret == 0) {
-               dev_warn(printdev(devrec), "Timeout waiting for TX interrupt\n");
-               ret = -ETIMEDOUT;
-               goto err;
-       }
-
-       /* Check for send error from the device. */
-       ret = read_short_reg(devrec, REG_TXSTAT, &val);
-       if (ret)
-               goto err;
-       if (val & 0x1) {
-               dev_dbg(printdev(devrec), "Error Sending. Retry count exceeded\n");
-               ret = -ECOMM; /* TODO: Better error code ? */
-       } else
-               dev_dbg(printdev(devrec), "Packet Sent\n");
-
-err:
-
-       return ret;
+       return write_tx_buf(devrec, 0x000, skb->data, skb->len);
 }
 
 static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level)
@@ -394,33 +610,23 @@ static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level)
 static int mrf24j40_start(struct ieee802154_hw *hw)
 {
        struct mrf24j40 *devrec = hw->priv;
-       u8 val;
-       int ret;
 
        dev_dbg(printdev(devrec), "start\n");
 
-       ret = read_short_reg(devrec, REG_INTCON, &val);
-       if (ret)
-               return ret;
-       val &= ~(0x1|0x8); /* Clear TXNIE and RXIE. Enable interrupts */
-       write_short_reg(devrec, REG_INTCON, val);
-
-       return 0;
+       /* Clear TXNIE and RXIE. Enable interrupts */
+       return regmap_update_bits(devrec->regmap_short, REG_INTCON,
+                                 BIT_TXNIE | BIT_RXIE, 0);
 }
 
 static void mrf24j40_stop(struct ieee802154_hw *hw)
 {
        struct mrf24j40 *devrec = hw->priv;
-       u8 val;
-       int ret;
 
        dev_dbg(printdev(devrec), "stop\n");
 
-       ret = read_short_reg(devrec, REG_INTCON, &val);
-       if (ret)
-               return;
-       val |= 0x1|0x8; /* Set TXNIE and RXIE. Disable Interrupts */
-       write_short_reg(devrec, REG_INTCON, val);
+       /* Set TXNIE and RXIE. Disable Interrupts */
+       regmap_update_bits(devrec->regmap_short, REG_INTCON,
+                          BIT_TXNIE | BIT_TXNIE, BIT_TXNIE | BIT_TXNIE);
 }
 
 static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
@@ -436,21 +642,23 @@ static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
        WARN_ON(channel > MRF24J40_CHAN_MAX);
 
        /* Set Channel TODO */
-       val = (channel-11) << 4 | 0x03;
-       write_long_reg(devrec, REG_RFCON0, val);
+       val = (channel - 11) << RFCON0_CH_SHIFT | RFOPT_RECOMMEND;
+       ret = regmap_update_bits(devrec->regmap_long, REG_RFCON0,
+                                RFCON0_CH_MASK, val);
+       if (ret)
+               return ret;
 
        /* RF Reset */
-       ret = read_short_reg(devrec, REG_RFCTL, &val);
+       ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST,
+                                BIT_RFRST);
        if (ret)
                return ret;
-       val |= 0x04;
-       write_short_reg(devrec, REG_RFCTL, val);
-       val &= ~0x04;
-       write_short_reg(devrec, REG_RFCTL, val);
 
-       udelay(SET_CHANNEL_DELAY_US); /* per datasheet */
+       ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST, 0);
+       if (!ret)
+               udelay(SET_CHANNEL_DELAY_US); /* per datasheet */
 
-       return 0;
+       return ret;
 }
 
 static int mrf24j40_filter(struct ieee802154_hw *hw,
@@ -468,8 +676,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
                addrh = le16_to_cpu(filt->short_addr) >> 8 & 0xff;
                addrl = le16_to_cpu(filt->short_addr) & 0xff;
 
-               write_short_reg(devrec, REG_SADRH, addrh);
-               write_short_reg(devrec, REG_SADRL, addrl);
+               regmap_write(devrec->regmap_short, REG_SADRH, addrh);
+               regmap_write(devrec->regmap_short, REG_SADRL, addrl);
                dev_dbg(printdev(devrec),
                        "Set short addr to %04hx\n", filt->short_addr);
        }
@@ -480,7 +688,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
 
                memcpy(addr, &filt->ieee_addr, 8);
                for (i = 0; i < 8; i++)
-                       write_short_reg(devrec, REG_EADR0 + i, addr[i]);
+                       regmap_write(devrec->regmap_short, REG_EADR0 + i,
+                                    addr[i]);
 
 #ifdef DEBUG
                pr_debug("Set long addr to: ");
@@ -496,8 +705,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
 
                panidh = le16_to_cpu(filt->pan_id) >> 8 & 0xff;
                panidl = le16_to_cpu(filt->pan_id) & 0xff;
-               write_short_reg(devrec, REG_PANIDH, panidh);
-               write_short_reg(devrec, REG_PANIDL, panidl);
+               regmap_write(devrec->regmap_short, REG_PANIDH, panidh);
+               regmap_write(devrec->regmap_short, REG_PANIDL, panidl);
 
                dev_dbg(printdev(devrec), "Set PANID to %04hx\n", filt->pan_id);
        }
@@ -507,14 +716,14 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
                u8 val;
                int ret;
 
-               ret = read_short_reg(devrec, REG_RXMCR, &val);
-               if (ret)
-                       return ret;
                if (filt->pan_coord)
-                       val |= 0x8;
+                       val = BIT_PANCOORD;
                else
-                       val &= ~0x8;
-               write_short_reg(devrec, REG_RXMCR, val);
+                       val = 0;
+               ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+                                        BIT_PANCOORD, val);
+               if (ret)
+                       return ret;
 
                /* REG_SLOTTED is maintained as default (unslotted/CSMA-CA).
                 * REG_ORDER is maintained as default (no beacon/superframe).
@@ -527,168 +736,392 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
        return 0;
 }
 
-static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
+static void mrf24j40_handle_rx_read_buf_unlock(struct mrf24j40 *devrec)
 {
-       u8 len = RX_FIFO_SIZE;
-       u8 lqi = 0;
-       u8 val;
-       int ret = 0;
-       int ret2;
-       struct sk_buff *skb;
+       int ret;
 
-       /* Turn off reception of packets off the air. This prevents the
-        * device from overwriting the buffer while we're reading it. */
-       ret = read_short_reg(devrec, REG_BBREG1, &val);
+       /* Turn back on reception of packets off the air. */
+       devrec->rx_msg.complete = NULL;
+       devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1);
+       devrec->rx_buf[1] = 0x00; /* CLR RXDECINV */
+       ret = spi_async(devrec->spi, &devrec->rx_msg);
        if (ret)
-               goto out;
-       val |= 4; /* SET RXDECINV */
-       write_short_reg(devrec, REG_BBREG1, val);
+               dev_err(printdev(devrec), "failed to unlock rx buffer\n");
+}
+
+static void mrf24j40_handle_rx_read_buf_complete(void *context)
+{
+       struct mrf24j40 *devrec = context;
+       u8 len = devrec->rx_buf[2];
+       u8 rx_local_buf[RX_FIFO_SIZE];
+       struct sk_buff *skb;
+
+       memcpy(rx_local_buf, devrec->rx_fifo_buf, len);
+       mrf24j40_handle_rx_read_buf_unlock(devrec);
 
-       skb = dev_alloc_skb(len);
+       skb = dev_alloc_skb(IEEE802154_MTU);
        if (!skb) {
-               ret = -ENOMEM;
-               goto out;
+               dev_err(printdev(devrec), "failed to allocate skb\n");
+               return;
        }
 
-       ret = mrf24j40_read_rx_buf(devrec, skb_put(skb, len), &len, &lqi);
-       if (ret < 0) {
-               dev_err(printdev(devrec), "Failure reading RX FIFO\n");
-               kfree_skb(skb);
-               ret = -EINVAL;
-               goto out;
+       memcpy(skb_put(skb, len), rx_local_buf, len);
+       ieee802154_rx_irqsafe(devrec->hw, skb, 0);
+
+#ifdef DEBUG
+        print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ", DUMP_PREFIX_OFFSET, 16, 1,
+                       rx_local_buf, len, 0);
+        pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n",
+                 devrec->rx_lqi_buf[0], devrec->rx_lqi_buf[1]);
+#endif
+}
+
+static void mrf24j40_handle_rx_read_buf(void *context)
+{
+       struct mrf24j40 *devrec = context;
+       u16 cmd;
+       int ret;
+
+       /* if length is invalid read the full MTU */
+       if (!ieee802154_is_valid_psdu_len(devrec->rx_buf[2]))
+               devrec->rx_buf[2] = IEEE802154_MTU;
+
+       cmd = MRF24J40_READLONG(REG_RX_FIFO + 1);
+       devrec->rx_addr_buf[0] = cmd >> 8 & 0xff;
+       devrec->rx_addr_buf[1] = cmd & 0xff;
+       devrec->rx_fifo_buf_trx.len = devrec->rx_buf[2];
+       ret = spi_async(devrec->spi, &devrec->rx_buf_msg);
+       if (ret) {
+               dev_err(printdev(devrec), "failed to read rx buffer\n");
+               mrf24j40_handle_rx_read_buf_unlock(devrec);
        }
+}
 
-       /* Cut off the checksum */
-       skb_trim(skb, len-2);
+static void mrf24j40_handle_rx_read_len(void *context)
+{
+       struct mrf24j40 *devrec = context;
+       u16 cmd;
+       int ret;
 
-       /* TODO: Other drivers call ieee20154_rx_irqsafe() here (eg: cc2040,
-        * also from a workqueue).  I think irqsafe is not necessary here.
-        * Can someone confirm? */
-       ieee802154_rx_irqsafe(devrec->hw, skb, lqi);
+       /* read the length of received frame */
+       devrec->rx_msg.complete = mrf24j40_handle_rx_read_buf;
+       devrec->rx_trx.len = 3;
+       cmd = MRF24J40_READLONG(REG_RX_FIFO);
+       devrec->rx_buf[0] = cmd >> 8 & 0xff;
+       devrec->rx_buf[1] = cmd & 0xff;
 
-       dev_dbg(printdev(devrec), "RX Handled\n");
+       ret = spi_async(devrec->spi, &devrec->rx_msg);
+       if (ret) {
+               dev_err(printdev(devrec), "failed to read rx buffer length\n");
+               mrf24j40_handle_rx_read_buf_unlock(devrec);
+       }
+}
 
-out:
-       /* Turn back on reception of packets off the air. */
-       ret2 = read_short_reg(devrec, REG_BBREG1, &val);
-       if (ret2)
-               return ret2;
-       val &= ~0x4; /* Clear RXDECINV */
-       write_short_reg(devrec, REG_BBREG1, val);
+static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
+{
+       /* Turn off reception of packets off the air. This prevents the
+        * device from overwriting the buffer while we're reading it.
+        */
+       devrec->rx_msg.complete = mrf24j40_handle_rx_read_len;
+       devrec->rx_trx.len = 2;
+       devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1);
+       devrec->rx_buf[1] = BIT_RXDECINV; /* SET RXDECINV */
+
+       return spi_async(devrec->spi, &devrec->rx_msg);
+}
+
+static int
+mrf24j40_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be,
+                    u8 retries)
+{
+       struct mrf24j40 *devrec = hw->priv;
+       u8 val;
+
+       /* min_be */
+       val = min_be << TXMCR_MIN_BE_SHIFT;
+       /* csma backoffs */
+       val |= retries << TXMCR_CSMA_RETRIES_SHIFT;
+
+       return regmap_update_bits(devrec->regmap_short, REG_TXMCR,
+                                 TXMCR_MIN_BE_MASK | TXMCR_CSMA_RETRIES_MASK,
+                                 val);
+}
+
+static int mrf24j40_set_cca_mode(struct ieee802154_hw *hw,
+                                const struct wpan_phy_cca *cca)
+{
+       struct mrf24j40 *devrec = hw->priv;
+       u8 val;
+
+       /* mapping 802.15.4 to driver spec */
+       switch (cca->mode) {
+       case NL802154_CCA_ENERGY:
+               val = 2;
+               break;
+       case NL802154_CCA_CARRIER:
+               val = 1;
+               break;
+       case NL802154_CCA_ENERGY_CARRIER:
+               switch (cca->opt) {
+               case NL802154_CCA_OPT_ENERGY_CARRIER_AND:
+                       val = 3;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_update_bits(devrec->regmap_short, REG_BBREG2,
+                                 BBREG2_CCA_MODE_MASK,
+                                 val << BBREG2_CCA_MODE_SHIFT);
+}
+
+/* array for representing ed levels */
+static const s32 mrf24j40_ed_levels[] = {
+       -9000, -8900, -8800, -8700, -8600, -8500, -8400, -8300, -8200, -8100,
+       -8000, -7900, -7800, -7700, -7600, -7500, -7400, -7300, -7200, -7100,
+       -7000, -6900, -6800, -6700, -6600, -6500, -6400, -6300, -6200, -6100,
+       -6000, -5900, -5800, -5700, -5600, -5500, -5400, -5300, -5200, -5100,
+       -5000, -4900, -4800, -4700, -4600, -4500, -4400, -4300, -4200, -4100,
+       -4000, -3900, -3800, -3700, -3600, -3500
+};
+
+/* map ed levels to register value */
+static const s32 mrf24j40_ed_levels_map[][2] = {
+       { -9000, 0 }, { -8900, 1 }, { -8800, 2 }, { -8700, 5 }, { -8600, 9 },
+       { -8500, 13 }, { -8400, 18 }, { -8300, 23 }, { -8200, 27 },
+       { -8100, 32 }, { -8000, 37 }, { -7900, 43 }, { -7800, 48 },
+       { -7700, 53 }, { -7600, 58 }, { -7500, 63 }, { -7400, 68 },
+       { -7300, 73 }, { -7200, 78 }, { -7100, 83 }, { -7000, 89 },
+       { -6900, 95 }, { -6800, 100 }, { -6700, 107 }, { -6600, 111 },
+       { -6500, 117 }, { -6400, 121 }, { -6300, 125 }, { -6200, 129 },
+       { -6100, 133 }, { -6000, 138 }, { -5900, 143 }, { -5800, 148 },
+       { -5700, 153 }, { -5600, 159 }, { -5500, 165 }, { -5400, 170 },
+       { -5300, 176 }, { -5200, 183 }, { -5100, 188 }, { -5000, 193 },
+       { -4900, 198 }, { -4800, 203 }, { -4700, 207 }, { -4600, 212 },
+       { -4500, 216 }, { -4400, 221 }, { -4300, 225 }, { -4200, 228 },
+       { -4100, 233 }, { -4000, 239 }, { -3900, 245 }, { -3800, 250 },
+       { -3700, 253 }, { -3600, 254 }, { -3500, 255 },
+};
+
+static int mrf24j40_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm)
+{
+       struct mrf24j40 *devrec = hw->priv;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mrf24j40_ed_levels_map); i++) {
+               if (mrf24j40_ed_levels_map[i][0] == mbm)
+                       return regmap_write(devrec->regmap_short, REG_CCAEDTH,
+                                           mrf24j40_ed_levels_map[i][1]);
+       }
+
+       return -EINVAL;
+}
+
+static const s32 mrf24j40ma_powers[] = {
+       0, -50, -120, -190, -280, -370, -490, -630, -1000, -1050, -1120, -1190,
+       -1280, -1370, -1490, -1630, -2000, -2050, -2120, -2190, -2280, -2370,
+       -2490, -2630, -3000, -3050, -3120, -3190, -3280, -3370, -3490, -3630,
+};
+
+static int mrf24j40_set_txpower(struct ieee802154_hw *hw, s32 mbm)
+{
+       struct mrf24j40 *devrec = hw->priv;
+       s32 small_scale;
+       u8 val;
+
+       if (0 >= mbm && mbm > -1000) {
+               val = TXPWRL_0 << TXPWRL_SHIFT;
+               small_scale = mbm;
+       } else if (-1000 >= mbm && mbm > -2000) {
+               val = TXPWRL_10 << TXPWRL_SHIFT;
+               small_scale = mbm + 1000;
+       } else if (-2000 >= mbm && mbm > -3000) {
+               val = TXPWRL_20 << TXPWRL_SHIFT;
+               small_scale = mbm + 2000;
+       } else if (-3000 >= mbm && mbm > -4000) {
+               val = TXPWRL_30 << TXPWRL_SHIFT;
+               small_scale = mbm + 3000;
+       } else {
+               return -EINVAL;
+       }
+
+       switch (small_scale) {
+       case 0:
+               val |= (TXPWRS_0 << TXPWRS_SHIFT);
+               break;
+       case -50:
+               val |= (TXPWRS_0_5 << TXPWRS_SHIFT);
+               break;
+       case -120:
+               val |= (TXPWRS_1_2 << TXPWRS_SHIFT);
+               break;
+       case -190:
+               val |= (TXPWRS_1_9 << TXPWRS_SHIFT);
+               break;
+       case -280:
+               val |= (TXPWRS_2_8 << TXPWRS_SHIFT);
+               break;
+       case -370:
+               val |= (TXPWRS_3_7 << TXPWRS_SHIFT);
+               break;
+       case -490:
+               val |= (TXPWRS_4_9 << TXPWRS_SHIFT);
+               break;
+       case -630:
+               val |= (TXPWRS_6_3 << TXPWRS_SHIFT);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_update_bits(devrec->regmap_long, REG_RFCON3,
+                                 TXPWRL_MASK | TXPWRS_MASK, val);
+}
+
+static int mrf24j40_set_promiscuous_mode(struct ieee802154_hw *hw, bool on)
+{
+       struct mrf24j40 *devrec = hw->priv;
+       int ret;
+
+       if (on) {
+               /* set PROMI, ERRPKT and NOACKRSP */
+               ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+                                        BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP,
+                                        BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP);
+       } else {
+               /* clear PROMI, ERRPKT and NOACKRSP */
+               ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+                                        BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP,
+                                        0);
+       }
 
        return ret;
 }
 
 static const struct ieee802154_ops mrf24j40_ops = {
        .owner = THIS_MODULE,
-       .xmit_sync = mrf24j40_tx,
+       .xmit_async = mrf24j40_tx,
        .ed = mrf24j40_ed,
        .start = mrf24j40_start,
        .stop = mrf24j40_stop,
        .set_channel = mrf24j40_set_channel,
        .set_hw_addr_filt = mrf24j40_filter,
+       .set_csma_params = mrf24j40_csma_params,
+       .set_cca_mode = mrf24j40_set_cca_mode,
+       .set_cca_ed_level = mrf24j40_set_cca_ed_level,
+       .set_txpower = mrf24j40_set_txpower,
+       .set_promiscuous_mode = mrf24j40_set_promiscuous_mode,
 };
 
-static irqreturn_t mrf24j40_isr(int irq, void *data)
+static void mrf24j40_intstat_complete(void *context)
 {
-       struct mrf24j40 *devrec = data;
-       u8 intstat;
-       int ret;
+       struct mrf24j40 *devrec = context;
+       u8 intstat = devrec->irq_buf[1];
 
-       /* Read the interrupt status */
-       ret = read_short_reg(devrec, REG_INTSTAT, &intstat);
-       if (ret)
-               goto out;
+       enable_irq(devrec->spi->irq);
 
        /* Check for TX complete */
-       if (intstat & 0x1)
-               complete(&devrec->tx_complete);
+       if (intstat & BIT_TXNIF)
+               ieee802154_xmit_complete(devrec->hw, devrec->tx_skb, false);
 
        /* Check for Rx */
-       if (intstat & 0x8)
+       if (intstat & BIT_RXIF)
                mrf24j40_handle_rx(devrec);
+}
+
+static irqreturn_t mrf24j40_isr(int irq, void *data)
+{
+       struct mrf24j40 *devrec = data;
+       int ret;
+
+       disable_irq_nosync(irq);
+
+       devrec->irq_buf[0] = MRF24J40_READSHORT(REG_INTSTAT);
+       /* Read the interrupt status */
+       ret = spi_async(devrec->spi, &devrec->irq_msg);
+       if (ret) {
+               enable_irq(irq);
+               return IRQ_NONE;
+       }
 
-out:
        return IRQ_HANDLED;
 }
 
 static int mrf24j40_hw_init(struct mrf24j40 *devrec)
 {
+       u32 irq_type;
        int ret;
-       u8 val;
 
        /* Initialize the device.
                From datasheet section 3.2: Initialization. */
-       ret = write_short_reg(devrec, REG_SOFTRST, 0x07);
+       ret = regmap_write(devrec->regmap_short, REG_SOFTRST, 0x07);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_PACON2, 0x98);
+       ret = regmap_write(devrec->regmap_short, REG_PACON2, 0x98);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_TXSTBL, 0x95);
+       ret = regmap_write(devrec->regmap_short, REG_TXSTBL, 0x95);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON0, 0x03);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON0, 0x03);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON1, 0x01);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON1, 0x01);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON2, 0x80);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON2, 0x80);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON6, 0x90);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON6, 0x90);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON7, 0x80);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON7, 0x80);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON8, 0x10);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON8, 0x10);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_SLPCON1, 0x21);
+       ret = regmap_write(devrec->regmap_long, REG_SLPCON1, 0x21);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_BBREG2, 0x80);
+       ret = regmap_write(devrec->regmap_short, REG_BBREG2, 0x80);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_CCAEDTH, 0x60);
+       ret = regmap_write(devrec->regmap_short, REG_CCAEDTH, 0x60);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_BBREG6, 0x40);
+       ret = regmap_write(devrec->regmap_short, REG_BBREG6, 0x40);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_RFCTL, 0x04);
+       ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x04);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_RFCTL, 0x0);
+       ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x0);
        if (ret)
                goto err_ret;
 
        udelay(192);
 
        /* Set RX Mode. RXMCR<1:0>: 0x0 normal, 0x1 promisc, 0x2 error */
-       ret = read_short_reg(devrec, REG_RXMCR, &val);
-       if (ret)
-               goto err_ret;
-
-       val &= ~0x3; /* Clear RX mode (normal) */
-
-       ret = write_short_reg(devrec, REG_RXMCR, val);
+       ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x03, 0x00);
        if (ret)
                goto err_ret;
 
@@ -696,22 +1129,39 @@ static int mrf24j40_hw_init(struct mrf24j40 *devrec)
                /* Enable external amplifier.
                 * From MRF24J40MC datasheet section 1.3: Operation.
                 */
-               read_long_reg(devrec, REG_TESTMODE, &val);
-               val |= 0x7; /* Configure GPIO 0-2 to control amplifier */
-               write_long_reg(devrec, REG_TESTMODE, val);
+               regmap_update_bits(devrec->regmap_long, REG_TESTMODE, 0x07,
+                                  0x07);
 
-               read_short_reg(devrec, REG_TRISGPIO, &val);
-               val |= 0x8; /* Set GPIO3 as output. */
-               write_short_reg(devrec, REG_TRISGPIO, val);
+               /* Set GPIO3 as output. */
+               regmap_update_bits(devrec->regmap_short, REG_TRISGPIO, 0x08,
+                                  0x08);
 
-               read_short_reg(devrec, REG_GPIO, &val);
-               val |= 0x8; /* Set GPIO3 HIGH to enable U5 voltage regulator */
-               write_short_reg(devrec, REG_GPIO, val);
+               /* Set GPIO3 HIGH to enable U5 voltage regulator */
+               regmap_update_bits(devrec->regmap_short, REG_GPIO, 0x08, 0x08);
 
                /* Reduce TX pwr to meet FCC requirements.
                 * From MRF24J40MC datasheet section 3.1.1
                 */
-               write_long_reg(devrec, REG_RFCON3, 0x28);
+               regmap_write(devrec->regmap_long, REG_RFCON3, 0x28);
+       }
+
+       irq_type = irq_get_trigger_type(devrec->spi->irq);
+       if (irq_type == IRQ_TYPE_EDGE_RISING ||
+           irq_type == IRQ_TYPE_EDGE_FALLING)
+               dev_warn(&devrec->spi->dev,
+                        "Using edge triggered irq's are not recommended, because it can cause races and result in a non-functional driver!\n");
+       switch (irq_type) {
+       case IRQ_TYPE_EDGE_RISING:
+       case IRQ_TYPE_LEVEL_HIGH:
+               /* set interrupt polarity to rising */
+               ret = regmap_update_bits(devrec->regmap_long, REG_SLPCON0,
+                                        BIT_INTEDGE, BIT_INTEDGE);
+               if (ret)
+                       goto err_ret;
+               break;
+       default:
+               /* default is falling edge */
+               break;
        }
 
        return 0;
@@ -720,67 +1170,178 @@ err_ret:
        return ret;
 }
 
-static int mrf24j40_probe(struct spi_device *spi)
+static void
+mrf24j40_setup_tx_spi_messages(struct mrf24j40 *devrec)
 {
-       int ret = -ENOMEM;
-       struct mrf24j40 *devrec;
+       spi_message_init(&devrec->tx_msg);
+       devrec->tx_msg.context = devrec;
+       devrec->tx_msg.complete = write_tx_buf_complete;
+       devrec->tx_hdr_trx.len = 2;
+       devrec->tx_hdr_trx.tx_buf = devrec->tx_hdr_buf;
+       spi_message_add_tail(&devrec->tx_hdr_trx, &devrec->tx_msg);
+       devrec->tx_len_trx.len = 2;
+       devrec->tx_len_trx.tx_buf = devrec->tx_len_buf;
+       spi_message_add_tail(&devrec->tx_len_trx, &devrec->tx_msg);
+       spi_message_add_tail(&devrec->tx_buf_trx, &devrec->tx_msg);
+
+       spi_message_init(&devrec->tx_post_msg);
+       devrec->tx_post_msg.context = devrec;
+       devrec->tx_post_trx.len = 2;
+       devrec->tx_post_trx.tx_buf = devrec->tx_post_buf;
+       spi_message_add_tail(&devrec->tx_post_trx, &devrec->tx_post_msg);
+}
 
-       dev_info(&spi->dev, "probe(). IRQ: %d\n", spi->irq);
+static void
+mrf24j40_setup_rx_spi_messages(struct mrf24j40 *devrec)
+{
+       spi_message_init(&devrec->rx_msg);
+       devrec->rx_msg.context = devrec;
+       devrec->rx_trx.len = 2;
+       devrec->rx_trx.tx_buf = devrec->rx_buf;
+       devrec->rx_trx.rx_buf = devrec->rx_buf;
+       spi_message_add_tail(&devrec->rx_trx, &devrec->rx_msg);
+
+       spi_message_init(&devrec->rx_buf_msg);
+       devrec->rx_buf_msg.context = devrec;
+       devrec->rx_buf_msg.complete = mrf24j40_handle_rx_read_buf_complete;
+       devrec->rx_addr_trx.len = 2;
+       devrec->rx_addr_trx.tx_buf = devrec->rx_addr_buf;
+       spi_message_add_tail(&devrec->rx_addr_trx, &devrec->rx_buf_msg);
+       devrec->rx_fifo_buf_trx.rx_buf = devrec->rx_fifo_buf;
+       spi_message_add_tail(&devrec->rx_fifo_buf_trx, &devrec->rx_buf_msg);
+       devrec->rx_lqi_trx.len = 2;
+       devrec->rx_lqi_trx.rx_buf = devrec->rx_lqi_buf;
+       spi_message_add_tail(&devrec->rx_lqi_trx, &devrec->rx_buf_msg);
+}
 
-       devrec = devm_kzalloc(&spi->dev, sizeof(struct mrf24j40), GFP_KERNEL);
-       if (!devrec)
-               goto err_ret;
-       devrec->buf = devm_kzalloc(&spi->dev, 3, GFP_KERNEL);
-       if (!devrec->buf)
-               goto err_ret;
+static void
+mrf24j40_setup_irq_spi_messages(struct mrf24j40 *devrec)
+{
+       spi_message_init(&devrec->irq_msg);
+       devrec->irq_msg.context = devrec;
+       devrec->irq_msg.complete = mrf24j40_intstat_complete;
+       devrec->irq_trx.len = 2;
+       devrec->irq_trx.tx_buf = devrec->irq_buf;
+       devrec->irq_trx.rx_buf = devrec->irq_buf;
+       spi_message_add_tail(&devrec->irq_trx, &devrec->irq_msg);
+}
+
+static void  mrf24j40_phy_setup(struct mrf24j40 *devrec)
+{
+       ieee802154_random_extended_addr(&devrec->hw->phy->perm_extended_addr);
+       devrec->hw->phy->current_channel = 11;
+
+       /* mrf24j40 supports max_minbe 0 - 3 */
+       devrec->hw->phy->supported.max_minbe = 3;
+       /* datasheet doesn't say anything about max_be, but we have min_be
+        * So we assume the max_be default.
+        */
+       devrec->hw->phy->supported.min_maxbe = 5;
+       devrec->hw->phy->supported.max_maxbe = 5;
+
+       devrec->hw->phy->cca.mode = NL802154_CCA_CARRIER;
+       devrec->hw->phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY) |
+                                              BIT(NL802154_CCA_CARRIER) |
+                                              BIT(NL802154_CCA_ENERGY_CARRIER);
+       devrec->hw->phy->supported.cca_opts = BIT(NL802154_CCA_OPT_ENERGY_CARRIER_AND);
+
+       devrec->hw->phy->cca_ed_level = -6900;
+       devrec->hw->phy->supported.cca_ed_levels = mrf24j40_ed_levels;
+       devrec->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(mrf24j40_ed_levels);
+
+       switch (spi_get_device_id(devrec->spi)->driver_data) {
+       case MRF24J40:
+       case MRF24J40MA:
+               devrec->hw->phy->supported.tx_powers = mrf24j40ma_powers;
+               devrec->hw->phy->supported.tx_powers_size = ARRAY_SIZE(mrf24j40ma_powers);
+               devrec->hw->phy->flags |= WPAN_PHY_FLAG_TXPOWER;
+               break;
+       default:
+               break;
+       }
+}
 
-       spi->mode = SPI_MODE_0; /* TODO: Is this appropriate for right here? */
-       if (spi->max_speed_hz > MAX_SPI_SPEED_HZ)
-               spi->max_speed_hz = MAX_SPI_SPEED_HZ;
+static int mrf24j40_probe(struct spi_device *spi)
+{
+       int ret = -ENOMEM, irq_type;
+       struct ieee802154_hw *hw;
+       struct mrf24j40 *devrec;
 
-       mutex_init(&devrec->buffer_mutex);
-       init_completion(&devrec->tx_complete);
-       devrec->spi = spi;
-       spi_set_drvdata(spi, devrec);
+       dev_info(&spi->dev, "probe(). IRQ: %d\n", spi->irq);
 
        /* Register with the 802154 subsystem */
 
-       devrec->hw = ieee802154_alloc_hw(0, &mrf24j40_ops);
-       if (!devrec->hw)
+       hw = ieee802154_alloc_hw(sizeof(*devrec), &mrf24j40_ops);
+       if (!hw)
                goto err_ret;
 
-       devrec->hw->priv = devrec;
-       devrec->hw->parent = &devrec->spi->dev;
+       devrec = hw->priv;
+       devrec->spi = spi;
+       spi_set_drvdata(spi, devrec);
+       devrec->hw = hw;
+       devrec->hw->parent = &spi->dev;
        devrec->hw->phy->supported.channels[0] = CHANNEL_MASK;
-       devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT;
+       devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT |
+                           IEEE802154_HW_CSMA_PARAMS |
+                           IEEE802154_HW_PROMISCUOUS;
+
+       devrec->hw->phy->flags = WPAN_PHY_FLAG_CCA_MODE |
+                                WPAN_PHY_FLAG_CCA_ED_LEVEL;
+
+       mrf24j40_setup_tx_spi_messages(devrec);
+       mrf24j40_setup_rx_spi_messages(devrec);
+       mrf24j40_setup_irq_spi_messages(devrec);
+
+       devrec->regmap_short = devm_regmap_init_spi(spi,
+                                                   &mrf24j40_short_regmap);
+       if (IS_ERR(devrec->regmap_short)) {
+               ret = PTR_ERR(devrec->regmap_short);
+               dev_err(&spi->dev, "Failed to allocate short register map: %d\n",
+                       ret);
+               goto err_register_device;
+       }
 
-       dev_dbg(printdev(devrec), "registered mrf24j40\n");
-       ret = ieee802154_register_hw(devrec->hw);
-       if (ret)
+       devrec->regmap_long = devm_regmap_init(&spi->dev,
+                                              &mrf24j40_long_regmap_bus,
+                                              spi, &mrf24j40_long_regmap);
+       if (IS_ERR(devrec->regmap_long)) {
+               ret = PTR_ERR(devrec->regmap_long);
+               dev_err(&spi->dev, "Failed to allocate long register map: %d\n",
+                       ret);
                goto err_register_device;
+       }
+
+       if (spi->max_speed_hz > MAX_SPI_SPEED_HZ) {
+               dev_warn(&spi->dev, "spi clock above possible maximum: %d",
+                        MAX_SPI_SPEED_HZ);
+               return -EINVAL;
+       }
 
        ret = mrf24j40_hw_init(devrec);
        if (ret)
-               goto err_hw_init;
+               goto err_register_device;
 
-       ret = devm_request_threaded_irq(&spi->dev,
-                                       spi->irq,
-                                       NULL,
-                                       mrf24j40_isr,
-                                       IRQF_TRIGGER_LOW|IRQF_ONESHOT,
-                                       dev_name(&spi->dev),
-                                       devrec);
+       mrf24j40_phy_setup(devrec);
 
+       /* request IRQF_TRIGGER_LOW as fallback default */
+       irq_type = irq_get_trigger_type(spi->irq);
+       if (!irq_type)
+               irq_type = IRQF_TRIGGER_LOW;
+
+       ret = devm_request_irq(&spi->dev, spi->irq, mrf24j40_isr,
+                              irq_type, dev_name(&spi->dev), devrec);
        if (ret) {
                dev_err(printdev(devrec), "Unable to get IRQ");
-               goto err_irq;
+               goto err_register_device;
        }
 
+       dev_dbg(printdev(devrec), "registered mrf24j40\n");
+       ret = ieee802154_register_hw(devrec->hw);
+       if (ret)
+               goto err_register_device;
+
        return 0;
 
-err_irq:
-err_hw_init:
-       ieee802154_unregister_hw(devrec->hw);
 err_register_device:
        ieee802154_free_hw(devrec->hw);
 err_ret:
@@ -801,6 +1362,14 @@ static int mrf24j40_remove(struct spi_device *spi)
        return 0;
 }
 
+static const struct of_device_id mrf24j40_of_match[] = {
+       { .compatible = "microchip,mrf24j40", .data = (void *)MRF24J40 },
+       { .compatible = "microchip,mrf24j40ma", .data = (void *)MRF24J40MA },
+       { .compatible = "microchip,mrf24j40mc", .data = (void *)MRF24J40MC },
+       { },
+};
+MODULE_DEVICE_TABLE(of, mrf24j40_of_match);
+
 static const struct spi_device_id mrf24j40_ids[] = {
        { "mrf24j40", MRF24J40 },
        { "mrf24j40ma", MRF24J40MA },
@@ -811,6 +1380,7 @@ MODULE_DEVICE_TABLE(spi, mrf24j40_ids);
 
 static struct spi_driver mrf24j40_driver = {
        .driver = {
+               .of_match_table = of_match_ptr(mrf24j40_of_match),
                .name = "mrf24j40",
                .owner = THIS_MODULE,
        },
index 207f62e8de9a93415cc76eb5fd75f987b3de53b6..d50887e3df6de3b41767ccbb3fb04a67c9d459af 100644 (file)
@@ -344,17 +344,18 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb)
 {
        const struct iphdr *ip4h = ip_hdr(skb);
        struct net_device *dev = skb->dev;
+       struct net *net = dev_net(dev);
        struct rtable *rt;
        int err, ret = NET_XMIT_DROP;
        struct flowi4 fl4 = {
-               .flowi4_oif = dev_get_iflink(dev),
+               .flowi4_oif = dev->ifindex,
                .flowi4_tos = RT_TOS(ip4h->tos),
                .flowi4_flags = FLOWI_FLAG_ANYSRC,
                .daddr = ip4h->daddr,
                .saddr = ip4h->saddr,
        };
 
-       rt = ip_route_output_flow(dev_net(dev), &fl4, NULL);
+       rt = ip_route_output_flow(net, &fl4, NULL);
        if (IS_ERR(rt))
                goto err;
 
@@ -364,7 +365,7 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb)
        }
        skb_dst_drop(skb);
        skb_dst_set(skb, &rt->dst);
-       err = ip_local_out(skb);
+       err = ip_local_out(net, skb->sk, skb);
        if (unlikely(net_xmit_eval(err)))
                dev->stats.tx_errors++;
        else
@@ -381,10 +382,11 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb)
 {
        const struct ipv6hdr *ip6h = ipv6_hdr(skb);
        struct net_device *dev = skb->dev;
+       struct net *net = dev_net(dev);
        struct dst_entry *dst;
        int err, ret = NET_XMIT_DROP;
        struct flowi6 fl6 = {
-               .flowi6_iif = skb->dev->ifindex,
+               .flowi6_iif = dev->ifindex,
                .daddr = ip6h->daddr,
                .saddr = ip6h->saddr,
                .flowi6_flags = FLOWI_FLAG_ANYSRC,
@@ -393,7 +395,7 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb)
                .flowi6_proto = ip6h->nexthdr,
        };
 
-       dst = ip6_route_output(dev_net(dev), NULL, &fl6);
+       dst = ip6_route_output(net, NULL, &fl6);
        if (dst->error) {
                ret = dst->error;
                dst_release(dst);
@@ -401,7 +403,7 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb)
        }
        skb_dst_drop(skb);
        skb_dst_set(skb, dst);
-       err = ip6_local_out(skb);
+       err = ip6_local_out(net, skb->sk, skb);
        if (unlikely(net_xmit_eval(err)))
                dev->stats.tx_errors++;
        else
index 100454662e4b36b4d7cad113bdd81cc538cb7304..6e8f616be48eff3557369a922334cd8e7f7617c8 100644 (file)
@@ -19,6 +19,9 @@
 #include <linux/etherdevice.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma/pxa-dma.h>
 #include <linux/gpio.h>
 #include <linux/slab.h>
 
 #include <net/irda/wrapper.h>
 #include <net/irda/irda_device.h>
 
-#include <mach/dma.h>
 #include <linux/platform_data/irda-pxaficp.h>
-#include <mach/regs-ost.h>
+#undef __REG
+#define __REG(x) ((x) & 0xffff)
 #include <mach/regs-uart.h>
 
-#define FICP           __REG(0x40800000)  /* Start of FICP area */
-#define ICCR0          __REG(0x40800000)  /* ICP Control Register 0 */
-#define ICCR1          __REG(0x40800004)  /* ICP Control Register 1 */
-#define ICCR2          __REG(0x40800008)  /* ICP Control Register 2 */
-#define ICDR           __REG(0x4080000c)  /* ICP Data Register */
-#define ICSR0          __REG(0x40800014)  /* ICP Status Register 0 */
-#define ICSR1          __REG(0x40800018)  /* ICP Status Register 1 */
+#define ICCR0          0x0000          /* ICP Control Register 0 */
+#define ICCR1          0x0004          /* ICP Control Register 1 */
+#define ICCR2          0x0008          /* ICP Control Register 2 */
+#define ICDR           0x000c          /* ICP Data Register */
+#define ICSR0          0x0014          /* ICP Status Register 0 */
+#define ICSR1          0x0018          /* ICP Status Register 1 */
 
 #define ICCR0_AME      (1 << 7)        /* Address match enable */
 #define ICCR0_TIE      (1 << 6)        /* Transmit FIFO interrupt enable */
@@ -56,9 +58,7 @@
 #define ICCR2_TRIG_16   (1 << 0)       /*      >= 16 bytes */
 #define ICCR2_TRIG_32   (2 << 0)       /*      >= 32 bytes */
 
-#ifdef CONFIG_PXA27x
 #define ICSR0_EOC      (1 << 6)        /* DMA End of Descriptor Chain */
-#endif
 #define ICSR0_FRE      (1 << 5)        /* Framing error */
 #define ICSR0_RFS      (1 << 4)        /* Receive FIFO service request */
 #define ICSR0_TFS      (1 << 3)        /* Transnit FIFO service request */
                 IrSR_RCVEIR_UART_MODE | \
                 IrSR_XMITIR_IR_MODE)
 
+/* macros for registers read/write */
+#define ficp_writel(irda, val, off)                                    \
+       do {                                                            \
+               dev_vdbg(irda->dev,                                     \
+                        "%s():%d ficp_writel(0x%x, %s)\n",             \
+                        __func__, __LINE__, (val), #off);              \
+               writel_relaxed((val), (irda)->irda_base + (off));       \
+       } while (0)
+
+#define ficp_readl(irda, off)                                          \
+       ({                                                              \
+               unsigned int _v;                                        \
+               _v = readl_relaxed((irda)->irda_base + (off));          \
+               dev_vdbg(irda->dev,                                     \
+                        "%s():%d ficp_readl(%s): 0x%x\n",              \
+                        __func__, __LINE__, #off, _v);                 \
+               _v;                                                     \
+       })
+
+#define stuart_writel(irda, val, off)                                  \
+       do {                                                            \
+               dev_vdbg(irda->dev,                                     \
+                        "%s():%d stuart_writel(0x%x, %s)\n",           \
+                        __func__, __LINE__, (val), #off);              \
+               writel_relaxed((val), (irda)->stuart_base + (off));     \
+       } while (0)
+
+#define stuart_readl(irda, off)                                                \
+       ({                                                              \
+               unsigned int _v;                                        \
+               _v = readl_relaxed((irda)->stuart_base + (off));        \
+               dev_vdbg(irda->dev,                                     \
+                        "%s():%d stuart_readl(%s): 0x%x\n",            \
+                        __func__, __LINE__, #off, _v);                 \
+               _v;                                                     \
+       })
+
 struct pxa_irda {
        int                     speed;
        int                     newspeed;
-       unsigned long           last_oscr;
+       unsigned long long      last_clk;
 
+       void __iomem            *stuart_base;
+       void __iomem            *irda_base;
        unsigned char           *dma_rx_buff;
        unsigned char           *dma_tx_buff;
        dma_addr_t              dma_rx_buff_phy;
        dma_addr_t              dma_tx_buff_phy;
        unsigned int            dma_tx_buff_len;
-       int                     txdma;
-       int                     rxdma;
+       struct dma_chan         *txdma;
+       struct dma_chan         *rxdma;
+       dma_cookie_t            rx_cookie;
+       dma_cookie_t            tx_cookie;
+       int                     drcmr_rx;
+       int                     drcmr_tx;
 
        int                     uart_irq;
        int                     icp_irq;
@@ -128,6 +171,8 @@ struct pxa_irda {
        struct clk              *cur_clk;
 };
 
+static int pxa_irda_set_speed(struct pxa_irda *si, int speed);
+
 static inline void pxa_irda_disable_clk(struct pxa_irda *si)
 {
        if (si->cur_clk)
@@ -151,22 +196,41 @@ static inline void pxa_irda_enable_sirclk(struct pxa_irda *si)
 #define IS_FIR(si)             ((si)->speed >= 4000000)
 #define IRDA_FRAME_SIZE_LIMIT  2047
 
+static void pxa_irda_fir_dma_rx_irq(void *data);
+static void pxa_irda_fir_dma_tx_irq(void *data);
+
 inline static void pxa_irda_fir_dma_rx_start(struct pxa_irda *si)
 {
-       DCSR(si->rxdma)  = DCSR_NODESC;
-       DSADR(si->rxdma) = __PREG(ICDR);
-       DTADR(si->rxdma) = si->dma_rx_buff_phy;
-       DCMD(si->rxdma) = DCMD_INCTRGADDR | DCMD_FLOWSRC |  DCMD_WIDTH1 | DCMD_BURST32 | IRDA_FRAME_SIZE_LIMIT;
-       DCSR(si->rxdma) |= DCSR_RUN;
+       struct dma_async_tx_descriptor *tx;
+
+       tx = dmaengine_prep_slave_single(si->rxdma, si->dma_rx_buff_phy,
+                                        IRDA_FRAME_SIZE_LIMIT, DMA_FROM_DEVICE,
+                                        DMA_PREP_INTERRUPT);
+       if (!tx) {
+               dev_err(si->dev, "prep_slave_sg() failed\n");
+               return;
+       }
+       tx->callback = pxa_irda_fir_dma_rx_irq;
+       tx->callback_param = si;
+       si->rx_cookie = dmaengine_submit(tx);
+       dma_async_issue_pending(si->rxdma);
 }
 
 inline static void pxa_irda_fir_dma_tx_start(struct pxa_irda *si)
 {
-       DCSR(si->txdma)  = DCSR_NODESC;
-       DSADR(si->txdma) = si->dma_tx_buff_phy;
-       DTADR(si->txdma) = __PREG(ICDR);
-       DCMD(si->txdma) = DCMD_INCSRCADDR | DCMD_FLOWTRG |  DCMD_ENDIRQEN | DCMD_WIDTH1 | DCMD_BURST32 | si->dma_tx_buff_len;
-       DCSR(si->txdma) |= DCSR_RUN;
+       struct dma_async_tx_descriptor *tx;
+
+       tx = dmaengine_prep_slave_single(si->txdma, si->dma_tx_buff_phy,
+                                        si->dma_tx_buff_len, DMA_TO_DEVICE,
+                                        DMA_PREP_INTERRUPT);
+       if (!tx) {
+               dev_err(si->dev, "prep_slave_sg() failed\n");
+               return;
+       }
+       tx->callback = pxa_irda_fir_dma_tx_irq;
+       tx->callback_param = si;
+       si->tx_cookie = dmaengine_submit(tx);
+       dma_async_issue_pending(si->rxdma);
 }
 
 /*
@@ -205,9 +269,9 @@ static int pxa_irda_set_speed(struct pxa_irda *si, int speed)
 
                if (IS_FIR(si)) {
                        /* stop RX DMA */
-                       DCSR(si->rxdma) &= ~DCSR_RUN;
+                       dmaengine_terminate_all(si->rxdma);
                        /* disable FICP */
-                       ICCR0 = 0;
+                       ficp_writel(si, 0, ICCR0);
                        pxa_irda_disable_clk(si);
 
                        /* set board transceiver to SIR mode */
@@ -218,17 +282,19 @@ static int pxa_irda_set_speed(struct pxa_irda *si, int speed)
                }
 
                /* disable STUART first */
-               STIER = 0;
+               stuart_writel(si, 0, STIER);
 
                /* access DLL & DLH */
-               STLCR |= LCR_DLAB;
-               STDLL = divisor & 0xff;
-               STDLH = divisor >> 8;
-               STLCR &= ~LCR_DLAB;
+               stuart_writel(si, stuart_readl(si, STLCR) | LCR_DLAB, STLCR);
+               stuart_writel(si, divisor & 0xff, STDLL);
+               stuart_writel(si, divisor >> 8, STDLH);
+               stuart_writel(si, stuart_readl(si, STLCR) & ~LCR_DLAB, STLCR);
 
                si->speed = speed;
-               STISR = IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6;
-               STIER = IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE;
+               stuart_writel(si, IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6,
+                             STISR);
+               stuart_writel(si, IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE,
+                             STIER);
 
                local_irq_restore(flags);
                break;
@@ -237,12 +303,12 @@ static int pxa_irda_set_speed(struct pxa_irda *si, int speed)
                local_irq_save(flags);
 
                /* disable STUART */
-               STIER = 0;
-               STISR = 0;
+               stuart_writel(si, 0, STIER);
+               stuart_writel(si, 0, STISR);
                pxa_irda_disable_clk(si);
 
                /* disable FICP first */
-               ICCR0 = 0;
+               ficp_writel(si, 0, ICCR0);
 
                /* set board transceiver to FIR mode */
                pxa_irda_set_mode(si, IR_FIRMODE);
@@ -252,7 +318,7 @@ static int pxa_irda_set_speed(struct pxa_irda *si, int speed)
 
                si->speed = speed;
                pxa_irda_fir_dma_rx_start(si);
-               ICCR0 = ICCR0_ITR | ICCR0_RXE;
+               ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0);
 
                local_irq_restore(flags);
                break;
@@ -271,13 +337,13 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id)
        struct pxa_irda *si = netdev_priv(dev);
        int iir, lsr, data;
 
-       iir = STIIR;
+       iir = stuart_readl(si, STIIR);
 
        switch  (iir & 0x0F) {
        case 0x06: /* Receiver Line Status */
-               lsr = STLSR;
+               lsr = stuart_readl(si, STLSR);
                while (lsr & LSR_FIFOE) {
-                       data = STRBR;
+                       data = stuart_readl(si, STRBR);
                        if (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) {
                                printk(KERN_DEBUG "pxa_ir: sir receiving error\n");
                                dev->stats.rx_errors++;
@@ -290,9 +356,9 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id)
                                async_unwrap_char(dev, &dev->stats,
                                                  &si->rx_buff, data);
                        }
-                       lsr = STLSR;
+                       lsr = stuart_readl(si, STLSR);
                }
-               si->last_oscr = readl_relaxed(OSCR);
+               si->last_clk = sched_clock();
                break;
 
        case 0x04: /* Received Data Available */
@@ -301,14 +367,16 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id)
        case 0x0C: /* Character Timeout Indication */
                do  {
                    dev->stats.rx_bytes++;
-                   async_unwrap_char(dev, &dev->stats, &si->rx_buff, STRBR);
-               } while (STLSR & LSR_DR);
-               si->last_oscr = readl_relaxed(OSCR);
+                   async_unwrap_char(dev, &dev->stats, &si->rx_buff,
+                                     stuart_readl(si, STRBR));
+               } while (stuart_readl(si, STLSR) & LSR_DR);
+               si->last_clk = sched_clock();
                break;
 
        case 0x02: /* Transmit FIFO Data Request */
-               while ((si->tx_buff.len) && (STLSR & LSR_TDRQ)) {
-                       STTHR = *si->tx_buff.data++;
+               while ((si->tx_buff.len) &&
+                      (stuart_readl(si, STLSR) & LSR_TDRQ)) {
+                       stuart_writel(si, *si->tx_buff.data++, STTHR);
                        si->tx_buff.len -= 1;
                }
 
@@ -317,9 +385,9 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id)
                        dev->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head;
 
                         /* We need to ensure that the transmitter has finished. */
-                       while ((STLSR & LSR_TEMT) == 0)
+                       while ((stuart_readl(si, STLSR) & LSR_TEMT) == 0)
                                cpu_relax();
-                       si->last_oscr = readl_relaxed(OSCR);
+                       si->last_clk = sched_clock();
 
                        /*
                        * Ok, we've finished transmitting.  Now enable
@@ -331,9 +399,11 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id)
                                si->newspeed = 0;
                        } else {
                                /* enable IR Receiver, disable IR Transmitter */
-                               STISR = IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6;
+                               stuart_writel(si, IrSR_IR_RECEIVE_ON |
+                                             IrSR_XMODE_PULSE_1_6, STISR);
                                /* enable STUART and receive interrupts */
-                               STIER = IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE;
+                               stuart_writel(si, IER_UUE | IER_RLSE |
+                                             IER_RAVIE | IER_RTIOE, STIER);
                        }
                        /* I'm hungry! */
                        netif_wake_queue(dev);
@@ -345,35 +415,32 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id)
 }
 
 /* FIR Receive DMA interrupt handler */
-static void pxa_irda_fir_dma_rx_irq(int channel, void *data)
+static void pxa_irda_fir_dma_rx_irq(void *data)
 {
-       int dcsr = DCSR(channel);
-
-       DCSR(channel) = dcsr & ~DCSR_RUN;
+       struct net_device *dev = data;
+       struct pxa_irda *si = netdev_priv(dev);
 
-       printk(KERN_DEBUG "pxa_ir: fir rx dma bus error %#x\n", dcsr);
+       dmaengine_terminate_all(si->rxdma);
+       netdev_dbg(dev, "pxa_ir: fir rx dma bus error\n");
 }
 
 /* FIR Transmit DMA interrupt handler */
-static void pxa_irda_fir_dma_tx_irq(int channel, void *data)
+static void pxa_irda_fir_dma_tx_irq(void *data)
 {
        struct net_device *dev = data;
        struct pxa_irda *si = netdev_priv(dev);
-       int dcsr;
-
-       dcsr = DCSR(channel);
-       DCSR(channel) = dcsr & ~DCSR_RUN;
 
-       if (dcsr & DCSR_ENDINTR)  {
+       dmaengine_terminate_all(si->txdma);
+       if (dmaengine_tx_status(si->txdma, si->tx_cookie, NULL) == DMA_ERROR) {
+               dev->stats.tx_errors++;
+       } else {
                dev->stats.tx_packets++;
                dev->stats.tx_bytes += si->dma_tx_buff_len;
-       } else {
-               dev->stats.tx_errors++;
        }
 
-       while (ICSR1 & ICSR1_TBY)
+       while (ficp_readl(si, ICSR1) & ICSR1_TBY)
                cpu_relax();
-       si->last_oscr = readl_relaxed(OSCR);
+       si->last_clk = sched_clock();
 
        /*
         * HACK: It looks like the TBY bit is dropped too soon.
@@ -387,11 +454,11 @@ static void pxa_irda_fir_dma_tx_irq(int channel, void *data)
        } else {
                int i = 64;
 
-               ICCR0 = 0;
+               ficp_writel(si, 0, ICCR0);
                pxa_irda_fir_dma_rx_start(si);
-               while ((ICSR1 & ICSR1_RNE) && i--)
-                       (void)ICDR;
-               ICCR0 = ICCR0_ITR | ICCR0_RXE;
+               while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--)
+                       ficp_readl(si, ICDR);
+               ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0);
 
                if (i < 0)
                        printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n");
@@ -403,15 +470,18 @@ static void pxa_irda_fir_dma_tx_irq(int channel, void *data)
 static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, int icsr0)
 {
        unsigned int len, stat, data;
+       struct dma_tx_state state;
 
        /* Get the current data position. */
-       len = DTADR(si->rxdma) - si->dma_rx_buff_phy;
+
+       dmaengine_tx_status(si->rxdma, si->rx_cookie, &state);
+       len = IRDA_FRAME_SIZE_LIMIT - state.residue;
 
        do {
                /* Read Status, and then Data.   */
-               stat = ICSR1;
+               stat = ficp_readl(si, ICSR1);
                rmb();
-               data = ICDR;
+               data = ficp_readl(si, ICDR);
 
                if (stat & (ICSR1_CRE | ICSR1_ROR)) {
                        dev->stats.rx_errors++;
@@ -429,7 +499,7 @@ static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, in
                /* If we hit the end of frame, there's no point in continuing. */
                if (stat & ICSR1_EOF)
                        break;
-       } while (ICSR0 & ICSR0_EIF);
+       } while (ficp_readl(si, ICSR0) & ICSR0_EIF);
 
        if (stat & ICSR1_EOF) {
                /* end of frame. */
@@ -472,9 +542,9 @@ static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id)
        int icsr0, i = 64;
 
        /* stop RX DMA */
-       DCSR(si->rxdma) &= ~DCSR_RUN;
-       si->last_oscr = readl_relaxed(OSCR);
-       icsr0 = ICSR0;
+       dmaengine_terminate_all(si->rxdma);
+       si->last_clk = sched_clock();
+       icsr0 = ficp_readl(si, ICSR0);
 
        if (icsr0 & (ICSR0_FRE | ICSR0_RAB)) {
                if (icsr0 & ICSR0_FRE) {
@@ -484,7 +554,7 @@ static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id)
                        printk(KERN_DEBUG "pxa_ir: fir receive abort\n");
                        dev->stats.rx_errors++;
                }
-               ICSR0 = icsr0 & (ICSR0_FRE | ICSR0_RAB);
+               ficp_writel(si, icsr0 & (ICSR0_FRE | ICSR0_RAB), ICSR0);
        }
 
        if (icsr0 & ICSR0_EIF) {
@@ -492,11 +562,11 @@ static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id)
                pxa_irda_fir_irq_eif(si, dev, icsr0);
        }
 
-       ICCR0 = 0;
+       ficp_writel(si, 0, ICCR0);
        pxa_irda_fir_dma_rx_start(si);
-       while ((ICSR1 & ICSR1_RNE) && i--)
-               (void)ICDR;
-       ICCR0 = ICCR0_ITR | ICCR0_RXE;
+       while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--)
+               ficp_readl(si, ICDR);
+       ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0);
 
        if (i < 0)
                printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n");
@@ -537,11 +607,12 @@ static int pxa_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
                si->tx_buff.len  = async_wrap_skb(skb, si->tx_buff.data, si->tx_buff.truesize);
 
                /* Disable STUART interrupts and switch to transmit mode. */
-               STIER = 0;
-               STISR = IrSR_IR_TRANSMIT_ON | IrSR_XMODE_PULSE_1_6;
+               stuart_writel(si, 0, STIER);
+               stuart_writel(si, IrSR_IR_TRANSMIT_ON | IrSR_XMODE_PULSE_1_6,
+                             STISR);
 
                /* enable STUART and transmit interrupts */
-               STIER = IER_UUE | IER_TIE;
+               stuart_writel(si, IER_UUE | IER_TIE, STIER);
        } else {
                unsigned long mtt = irda_get_mtt(skb);
 
@@ -549,15 +620,15 @@ static int pxa_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
                skb_copy_from_linear_data(skb, si->dma_tx_buff, skb->len);
 
                if (mtt)
-                       while ((unsigned)(readl_relaxed(OSCR) - si->last_oscr)/4 < mtt)
+                       while ((sched_clock() - si->last_clk) * 1000 < mtt)
                                cpu_relax();
 
                /* stop RX DMA,  disable FICP */
-               DCSR(si->rxdma) &= ~DCSR_RUN;
-               ICCR0 = 0;
+               dmaengine_terminate_all(si->rxdma);
+               ficp_writel(si, 0, ICCR0);
 
                pxa_irda_fir_dma_tx_start(si);
-               ICCR0 = ICCR0_ITR | ICCR0_TXE;
+               ficp_writel(si, ICCR0_ITR | ICCR0_TXE, ICCR0);
        }
 
        dev_kfree_skb(skb);
@@ -613,22 +684,18 @@ static int pxa_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
 static void pxa_irda_startup(struct pxa_irda *si)
 {
        /* Disable STUART interrupts */
-       STIER = 0;
+       stuart_writel(si, 0, STIER);
        /* enable STUART interrupt to the processor */
-       STMCR = MCR_OUT2;
+       stuart_writel(si, MCR_OUT2, STMCR);
        /* configure SIR frame format: StartBit - Data 7 ... Data 0 - Stop Bit */
-       STLCR = LCR_WLS0 | LCR_WLS1;
+       stuart_writel(si, LCR_WLS0 | LCR_WLS1, STLCR);
        /* enable FIFO, we use FIFO to improve performance */
-       STFCR = FCR_TRFIFOE | FCR_ITL_32;
+       stuart_writel(si, FCR_TRFIFOE | FCR_ITL_32, STFCR);
 
        /* disable FICP */
-       ICCR0 = 0;
+       ficp_writel(si, 0, ICCR0);
        /* configure FICP ICCR2 */
-       ICCR2 = ICCR2_TXP | ICCR2_TRIG_32;
-
-       /* configure DMAC */
-       DRCMR(17) = si->rxdma | DRCMR_MAPVLD;
-       DRCMR(18) = si->txdma | DRCMR_MAPVLD;
+       ficp_writel(si, ICCR2_TXP | ICCR2_TRIG_32, ICCR2);
 
        /* force SIR reinitialization */
        si->speed = 4000000;
@@ -644,22 +711,19 @@ static void pxa_irda_shutdown(struct pxa_irda *si)
        local_irq_save(flags);
 
        /* disable STUART and interrupt */
-       STIER = 0;
+       stuart_writel(si, 0, STIER);
        /* disable STUART SIR mode */
-       STISR = 0;
+       stuart_writel(si, 0, STISR);
 
        /* disable DMA */
-       DCSR(si->txdma) &= ~DCSR_RUN;
-       DCSR(si->rxdma) &= ~DCSR_RUN;
+       dmaengine_terminate_all(si->rxdma);
+       dmaengine_terminate_all(si->txdma);
        /* disable FICP */
-       ICCR0 = 0;
+       ficp_writel(si, 0, ICCR0);
 
        /* disable the STUART or FICP clocks */
        pxa_irda_disable_clk(si);
 
-       DRCMR(17) = 0;
-       DRCMR(18) = 0;
-
        local_irq_restore(flags);
 
        /* power off board transceiver */
@@ -671,6 +735,9 @@ static void pxa_irda_shutdown(struct pxa_irda *si)
 static int pxa_irda_start(struct net_device *dev)
 {
        struct pxa_irda *si = netdev_priv(dev);
+       dma_cap_mask_t mask;
+       struct dma_slave_config config;
+       struct pxad_param param;
        int err;
 
        si->speed = 9600;
@@ -690,14 +757,37 @@ static int pxa_irda_start(struct net_device *dev)
        disable_irq(si->icp_irq);
 
        err = -EBUSY;
-       si->rxdma = pxa_request_dma("FICP_RX",DMA_PRIO_LOW, pxa_irda_fir_dma_rx_irq, dev);
-       if (si->rxdma < 0)
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       param.prio = PXAD_PRIO_LOWEST;
+
+       memset(&config, 0, sizeof(config));
+       config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       config.src_addr = (dma_addr_t)si->irda_base + ICDR;
+       config.dst_addr = (dma_addr_t)si->irda_base + ICDR;
+       config.src_maxburst = 32;
+       config.dst_maxburst = 32;
+
+       param.drcmr = si->drcmr_rx;
+       si->rxdma = dma_request_slave_channel_compat(mask, pxad_filter_fn,
+                                                    &param, &dev->dev, "rx");
+       if (!si->rxdma)
                goto err_rx_dma;
 
-       si->txdma = pxa_request_dma("FICP_TX",DMA_PRIO_LOW, pxa_irda_fir_dma_tx_irq, dev);
-       if (si->txdma < 0)
+       param.drcmr = si->drcmr_tx;
+       si->txdma = dma_request_slave_channel_compat(mask, pxad_filter_fn,
+                                                    &param, &dev->dev, "tx");
+       if (!si->txdma)
                goto err_tx_dma;
 
+       err = dmaengine_slave_config(si->rxdma, &config);
+       if (err)
+               goto err_dma_rx_buff;
+       err = dmaengine_slave_config(si->txdma, &config);
+       if (err)
+               goto err_dma_rx_buff;
+
        err = -ENOMEM;
        si->dma_rx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT,
                                             &si->dma_rx_buff_phy, GFP_KERNEL);
@@ -737,9 +827,9 @@ err_irlap:
 err_dma_tx_buff:
        dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_rx_buff, si->dma_rx_buff_phy);
 err_dma_rx_buff:
-       pxa_free_dma(si->txdma);
+       dma_release_channel(si->txdma);
 err_tx_dma:
-       pxa_free_dma(si->rxdma);
+       dma_release_channel(si->rxdma);
 err_rx_dma:
        free_irq(si->icp_irq, dev);
 err_irq2:
@@ -766,8 +856,10 @@ static int pxa_irda_stop(struct net_device *dev)
        free_irq(si->uart_irq, dev);
        free_irq(si->icp_irq, dev);
 
-       pxa_free_dma(si->rxdma);
-       pxa_free_dma(si->txdma);
+       dmaengine_terminate_all(si->rxdma);
+       dmaengine_terminate_all(si->txdma);
+       dma_release_channel(si->rxdma);
+       dma_release_channel(si->txdma);
 
        if (si->dma_rx_buff)
                dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_tx_buff, si->dma_tx_buff_phy);
@@ -830,25 +922,33 @@ static const struct net_device_ops pxa_irda_netdev_ops = {
 static int pxa_irda_probe(struct platform_device *pdev)
 {
        struct net_device *dev;
+       struct resource *res;
        struct pxa_irda *si;
+       void __iomem *ficp, *stuart;
        unsigned int baudrate_mask;
        int err;
 
        if (!pdev->dev.platform_data)
                return -ENODEV;
 
-       err = request_mem_region(__PREG(STUART), 0x24, "IrDA") ? 0 : -EBUSY;
-       if (err)
-               goto err_mem_1;
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ficp = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ficp)) {
+               dev_err(&pdev->dev, "resource ficp not defined\n");
+               return PTR_ERR(ficp);
+       }
 
-       err = request_mem_region(__PREG(FICP), 0x1c, "IrDA") ? 0 : -EBUSY;
-       if (err)
-               goto err_mem_2;
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       stuart = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(stuart)) {
+               dev_err(&pdev->dev, "resource stuart not defined\n");
+               return PTR_ERR(stuart);
+       }
 
        dev = alloc_irdadev(sizeof(struct pxa_irda));
        if (!dev) {
                err = -ENOMEM;
-               goto err_mem_3;
+               goto err_mem_1;
        }
 
        SET_NETDEV_DEV(dev, &pdev->dev);
@@ -856,16 +956,25 @@ static int pxa_irda_probe(struct platform_device *pdev)
        si->dev = &pdev->dev;
        si->pdata = pdev->dev.platform_data;
 
+       si->irda_base = ficp;
+       si->stuart_base = stuart;
        si->uart_irq = platform_get_irq(pdev, 0);
        si->icp_irq = platform_get_irq(pdev, 1);
 
-       si->sir_clk = clk_get(&pdev->dev, "UARTCLK");
-       si->fir_clk = clk_get(&pdev->dev, "FICPCLK");
+       si->sir_clk = devm_clk_get(&pdev->dev, "UARTCLK");
+       si->fir_clk = devm_clk_get(&pdev->dev, "FICPCLK");
        if (IS_ERR(si->sir_clk) || IS_ERR(si->fir_clk)) {
                err = PTR_ERR(IS_ERR(si->sir_clk) ? si->sir_clk : si->fir_clk);
                goto err_mem_4;
        }
 
+       res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (res)
+               si->drcmr_rx = res->start;
+       res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (res)
+               si->drcmr_tx = res->start;
+
        /*
         * Initialise the SIR buffers
         */
@@ -925,15 +1034,7 @@ err_startup:
 err_mem_5:
                kfree(si->rx_buff.head);
 err_mem_4:
-               if (si->sir_clk && !IS_ERR(si->sir_clk))
-                       clk_put(si->sir_clk);
-               if (si->fir_clk && !IS_ERR(si->fir_clk))
-                       clk_put(si->fir_clk);
                free_netdev(dev);
-err_mem_3:
-               release_mem_region(__PREG(FICP), 0x1c);
-err_mem_2:
-               release_mem_region(__PREG(STUART), 0x24);
        }
 err_mem_1:
        return err;
@@ -952,14 +1053,9 @@ static int pxa_irda_remove(struct platform_device *_dev)
                        si->pdata->shutdown(si->dev);
                kfree(si->tx_buff.head);
                kfree(si->rx_buff.head);
-               clk_put(si->fir_clk);
-               clk_put(si->sir_clk);
                free_netdev(dev);
        }
 
-       release_mem_region(__PREG(STUART), 0x24);
-       release_mem_region(__PREG(FICP), 0x1c);
-
        return 0;
 }
 
index 47da43595ac271c570e8536a2e1a3b697a9dbd79..86f6c6292c2726c6991f305bc8794a5af2c648af 100644 (file)
@@ -412,7 +412,7 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
 
        port = macvlan_port_get_rcu(skb->dev);
        if (is_multicast_ether_addr(eth->h_dest)) {
-               skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN);
+               skb = ip_check_defrag(dev_net(skb->dev), skb, IP_DEFRAG_MACVLAN);
                if (!skb)
                        return RX_HANDLER_CONSUMED;
                eth = eth_hdr(skb);
index 248478c6f6e49522681a3eeb8a80fd8eaefd32fc..197c93937c2d577e56cf7fab8dcef07313bf75f4 100644 (file)
@@ -137,7 +137,7 @@ static const struct proto_ops macvtap_socket_ops;
 #define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \
                      NETIF_F_TSO6 | NETIF_F_UFO)
 #define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO)
-#define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG)
+#define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG | NETIF_F_FRAGLIST)
 
 static struct macvlan_dev *macvtap_get_vlan_rcu(const struct net_device *dev)
 {
index 11e3975485c1eaa377b7ee82a3d3eef5a3b00ffe..60994a83a0d68ca2e4c229fe16140b6017a9dd55 100644 (file)
@@ -69,20 +69,39 @@ config SMSC_PHY
        ---help---
          Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs
 
+config BCM_NET_PHYLIB
+       tristate
+
 config BROADCOM_PHY
        tristate "Drivers for Broadcom PHYs"
+       select BCM_NET_PHYLIB
        ---help---
          Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464,
          BCM5481 and BCM5482 PHYs.
 
+config BCM_CYGNUS_PHY
+       tristate "Drivers for Broadcom Cygnus SoC internal PHY"
+       depends on ARCH_BCM_CYGNUS || COMPILE_TEST
+       depends on MDIO_BCM_IPROC
+       select BCM_NET_PHYLIB
+       ---help---
+         This PHY driver is for the 1G internal PHYs of the Broadcom
+         Cygnus Family SoC.
+
+         Currently supports internal PHY's used in the BCM11300,
+         BCM11320, BCM11350, BCM11360, BCM58300, BCM58302,
+         BCM58303 & BCM58305 Broadcom Cygnus SoCs.
+
 config BCM63XX_PHY
        tristate "Drivers for Broadcom 63xx SOCs internal PHY"
        depends on BCM63XX
+       select BCM_NET_PHYLIB
        ---help---
          Currently supports the 6348 and 6358 PHYs.
 
 config BCM7XXX_PHY
        tristate "Drivers for Broadcom 7xxx SOCs internal PHYs"
+       select BCM_NET_PHYLIB
        ---help---
          Currently supports the BCM7366, BCM7439, BCM7445, and
          40nm and 65nm generation of BCM7xxx Set Top Box SoCs.
@@ -122,6 +141,11 @@ config MICREL_PHY
        ---help---
          Supports the KSZ9021, VSC8201, KS8001 PHYs.
 
+config DP83848_PHY
+       tristate "Driver for Texas Instruments DP83848 PHY"
+       ---help---
+         Supports the DP83848 PHY.
+
 config DP83867_PHY
        tristate "Drivers for Texas Instruments DP83867 Gigabit PHY"
        ---help---
@@ -223,6 +247,15 @@ config MDIO_BCM_UNIMAC
          This hardware can be found in the Broadcom GENET Ethernet MAC
          controllers as well as some Broadcom Ethernet switches such as the
          Starfighter 2 switches.
+
+config MDIO_BCM_IPROC
+       tristate "Broadcom iProc MDIO bus controller"
+       depends on ARCH_BCM_IPROC || COMPILE_TEST
+       depends on HAS_IOMEM && OF_MDIO
+       help
+         This module provides a driver for the MDIO busses found in the
+         Broadcom iProc SoC's.
+
 endif # PHYLIB
 
 config MICREL_KS8995MA
index 87f079c4b2c7ab16e5577b0b86fdd8509f9b7c8f..f31a4e25cf151a58efd594daeb2ec09724c39279 100644 (file)
@@ -12,10 +12,12 @@ obj-$(CONFIG_QSEMI_PHY)             += qsemi.o
 obj-$(CONFIG_SMSC_PHY)         += smsc.o
 obj-$(CONFIG_TERANETICS_PHY)   += teranetics.o
 obj-$(CONFIG_VITESSE_PHY)      += vitesse.o
+obj-$(CONFIG_BCM_NET_PHYLIB)   += bcm-phy-lib.o
 obj-$(CONFIG_BROADCOM_PHY)     += broadcom.o
 obj-$(CONFIG_BCM63XX_PHY)      += bcm63xx.o
 obj-$(CONFIG_BCM7XXX_PHY)      += bcm7xxx.o
 obj-$(CONFIG_BCM87XX_PHY)      += bcm87xx.o
+obj-$(CONFIG_BCM_CYGNUS_PHY)   += bcm-cygnus.o
 obj-$(CONFIG_ICPLUS_PHY)       += icplus.o
 obj-$(CONFIG_REALTEK_PHY)      += realtek.o
 obj-$(CONFIG_LSI_ET1011C_PHY)  += et1011c.o
@@ -24,6 +26,7 @@ obj-$(CONFIG_MDIO_BITBANG)    += mdio-bitbang.o
 obj-$(CONFIG_MDIO_GPIO)                += mdio-gpio.o
 obj-$(CONFIG_NATIONAL_PHY)     += national.o
 obj-$(CONFIG_DP83640_PHY)      += dp83640.o
+obj-$(CONFIG_DP83848_PHY)      += dp83848.o
 obj-$(CONFIG_DP83867_PHY)      += dp83867.o
 obj-$(CONFIG_STE10XP)          += ste10Xp.o
 obj-$(CONFIG_MICREL_PHY)       += micrel.o
@@ -38,3 +41,4 @@ obj-$(CONFIG_MDIO_SUN4I)      += mdio-sun4i.o
 obj-$(CONFIG_MDIO_MOXART)      += mdio-moxart.o
 obj-$(CONFIG_MDIO_BCM_UNIMAC)  += mdio-bcm-unimac.o
 obj-$(CONFIG_MICROCHIP_PHY)    += microchip.o
+obj-$(CONFIG_MDIO_BCM_IPROC)   += mdio-bcm-iproc.o
index d6111affbcb6cafd282aaae2c76a6f8fc7c704f3..f1936b7a7af69f1802568b76063e498eb5e54a2b 100644 (file)
@@ -171,20 +171,7 @@ static struct phy_driver aquantia_driver[] = {
 },
 };
 
-static int __init aquantia_init(void)
-{
-       return phy_drivers_register(aquantia_driver,
-                                   ARRAY_SIZE(aquantia_driver));
-}
-
-static void __exit aquantia_exit(void)
-{
-       return phy_drivers_unregister(aquantia_driver,
-                                     ARRAY_SIZE(aquantia_driver));
-}
-
-module_init(aquantia_init);
-module_exit(aquantia_exit);
+module_phy_driver(aquantia_driver);
 
 static struct mdio_device_id __maybe_unused aquantia_tbl[] = {
        { PHY_ID_AQ1202, 0xfffffff0 },
diff --git a/drivers/net/phy/bcm-cygnus.c b/drivers/net/phy/bcm-cygnus.c
new file mode 100644 (file)
index 0000000..49bbc68
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* Broadcom Cygnus SoC internal transceivers support. */
+#include "bcm-phy-lib.h"
+#include <linux/brcmphy.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+
+/* Broadcom Cygnus Phy specific registers */
+#define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0  0x91E5 /* VDAL Control register */
+
+static int bcm_cygnus_afe_config(struct phy_device *phydev)
+{
+       int rc;
+
+       /* ensure smdspclk is enabled */
+       rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 0x0c30);
+       if (rc < 0)
+               return rc;
+
+       /* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */
+       rc = bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8);
+       if (rc < 0)
+               return rc;
+
+       /* AFE_HPF_TRIM_OTHERS bit11=1, short cascode enable for all modes*/
+       rc = bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803);
+       if (rc < 0)
+               return rc;
+
+       /* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */
+       rc = bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740);
+       if (rc < 0)
+               return rc;
+
+       /* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */
+       rc = bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400);
+       if (rc < 0)
+               return rc;
+
+       /* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */
+       rc = bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004);
+       if (rc < 0)
+               return rc;
+
+       /* Adjust bias current trim to overcome digital offSet */
+       rc = phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x02);
+       if (rc < 0)
+               return rc;
+
+       /* make rcal=100, since rdb default is 000 */
+       rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB1, 0x10);
+       if (rc < 0)
+               return rc;
+
+       /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */
+       rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x10);
+       if (rc < 0)
+               return rc;
+
+       /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */
+       rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x00);
+
+       return 0;
+}
+
+static int bcm_cygnus_config_init(struct phy_device *phydev)
+{
+       int reg, rc;
+
+       reg = phy_read(phydev, MII_BCM54XX_ECR);
+       if (reg < 0)
+               return reg;
+
+       /* Mask interrupts globally. */
+       reg |= MII_BCM54XX_ECR_IM;
+       rc = phy_write(phydev, MII_BCM54XX_ECR, reg);
+       if (rc)
+               return rc;
+
+       /* Unmask events of interest */
+       reg = ~(MII_BCM54XX_INT_DUPLEX |
+               MII_BCM54XX_INT_SPEED |
+               MII_BCM54XX_INT_LINK);
+       rc = phy_write(phydev, MII_BCM54XX_IMR, reg);
+       if (rc)
+               return rc;
+
+       /* Apply AFE settings for the PHY */
+       rc = bcm_cygnus_afe_config(phydev);
+       if (rc)
+               return rc;
+
+       /* Advertise EEE */
+       rc = bcm_phy_enable_eee(phydev);
+       if (rc)
+               return rc;
+
+       /* Enable APD */
+       return bcm_phy_enable_apd(phydev, false);
+}
+
+static int bcm_cygnus_resume(struct phy_device *phydev)
+{
+       int rc;
+
+       genphy_resume(phydev);
+
+       /* Re-initialize the PHY to apply AFE work-arounds and
+        * configurations when coming out of suspend.
+        */
+       rc = bcm_cygnus_config_init(phydev);
+       if (rc)
+               return rc;
+
+       /* restart auto negotiation with the new settings */
+       return genphy_config_aneg(phydev);
+}
+
+static struct phy_driver bcm_cygnus_phy_driver[] = {
+{
+       .phy_id        = PHY_ID_BCM_CYGNUS,
+       .phy_id_mask   = 0xfffffff0,
+       .name          = "Broadcom Cygnus PHY",
+       .features      = PHY_GBIT_FEATURES |
+                       SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+       .config_init   = bcm_cygnus_config_init,
+       .config_aneg   = genphy_config_aneg,
+       .read_status   = genphy_read_status,
+       .ack_interrupt = bcm_phy_ack_intr,
+       .config_intr   = bcm_phy_config_intr,
+       .suspend       = genphy_suspend,
+       .resume        = bcm_cygnus_resume,
+} };
+
+static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = {
+       { PHY_ID_BCM_CYGNUS, 0xfffffff0, },
+       { }
+};
+MODULE_DEVICE_TABLE(mdio, bcm_cygnus_phy_tbl);
+
+module_phy_driver(bcm_cygnus_phy_driver);
+
+MODULE_DESCRIPTION("Broadcom Cygnus internal PHY driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom Corporation");
diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c
new file mode 100644 (file)
index 0000000..ddb377e
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "bcm-phy-lib.h"
+#include <linux/brcmphy.h>
+#include <linux/export.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+
+#define MII_BCM_CHANNEL_WIDTH     0x2000
+#define BCM_CL45VEN_EEE_ADV       0x3c
+
+int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
+{
+       int rc;
+
+       rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
+       if (rc < 0)
+               return rc;
+
+       return phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
+}
+EXPORT_SYMBOL_GPL(bcm_phy_write_exp);
+
+int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
+{
+       int val;
+
+       val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
+       if (val < 0)
+               return val;
+
+       val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
+
+       /* Restore default value.  It's O.K. if this write fails. */
+       phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
+
+       return val;
+}
+EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
+
+int bcm_phy_write_misc(struct phy_device *phydev,
+                      u16 reg, u16 chl, u16 val)
+{
+       int rc;
+       int tmp;
+
+       rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
+                      MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
+       if (rc < 0)
+               return rc;
+
+       tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
+       tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
+       rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
+       if (rc < 0)
+               return rc;
+
+       tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
+       rc = bcm_phy_write_exp(phydev, tmp, val);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(bcm_phy_write_misc);
+
+int bcm_phy_read_misc(struct phy_device *phydev,
+                     u16 reg, u16 chl)
+{
+       int rc;
+       int tmp;
+
+       rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
+                      MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
+       if (rc < 0)
+               return rc;
+
+       tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
+       tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
+       rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
+       if (rc < 0)
+               return rc;
+
+       tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
+       rc = bcm_phy_read_exp(phydev, tmp);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(bcm_phy_read_misc);
+
+int bcm_phy_ack_intr(struct phy_device *phydev)
+{
+       int reg;
+
+       /* Clear pending interrupts.  */
+       reg = phy_read(phydev, MII_BCM54XX_ISR);
+       if (reg < 0)
+               return reg;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(bcm_phy_ack_intr);
+
+int bcm_phy_config_intr(struct phy_device *phydev)
+{
+       int reg;
+
+       reg = phy_read(phydev, MII_BCM54XX_ECR);
+       if (reg < 0)
+               return reg;
+
+       if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+               reg &= ~MII_BCM54XX_ECR_IM;
+       else
+               reg |= MII_BCM54XX_ECR_IM;
+
+       return phy_write(phydev, MII_BCM54XX_ECR, reg);
+}
+EXPORT_SYMBOL_GPL(bcm_phy_config_intr);
+
+int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow)
+{
+       phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
+       return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
+}
+EXPORT_SYMBOL_GPL(bcm_phy_read_shadow);
+
+int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
+                        u16 val)
+{
+       return phy_write(phydev, MII_BCM54XX_SHD,
+                        MII_BCM54XX_SHD_WRITE |
+                        MII_BCM54XX_SHD_VAL(shadow) |
+                        MII_BCM54XX_SHD_DATA(val));
+}
+EXPORT_SYMBOL_GPL(bcm_phy_write_shadow);
+
+int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
+{
+       int val;
+
+       if (dll_pwr_down) {
+               val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
+               if (val < 0)
+                       return val;
+
+               val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
+               bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
+       }
+
+       val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
+       if (val < 0)
+               return val;
+
+       /* Clear APD bits */
+       val &= BCM_APD_CLR_MASK;
+
+       if (phydev->autoneg == AUTONEG_ENABLE)
+               val |= BCM54XX_SHD_APD_EN;
+       else
+               val |= BCM_NO_ANEG_APD_EN;
+
+       /* Enable energy detect single link pulse for easy wakeup */
+       val |= BCM_APD_SINGLELP_EN;
+
+       /* Enable Auto Power-Down (APD) for the PHY */
+       return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
+}
+EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
+
+int bcm_phy_enable_eee(struct phy_device *phydev)
+{
+       int val;
+
+       /* Enable EEE at PHY level */
+       val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
+                                   MDIO_MMD_AN, phydev->addr);
+       if (val < 0)
+               return val;
+
+       val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
+
+       phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
+                              MDIO_MMD_AN,  phydev->addr, (u32)val);
+
+       /* Advertise EEE */
+       val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
+                                   MDIO_MMD_AN, phydev->addr);
+       if (val < 0)
+               return val;
+
+       val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
+
+       phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
+                              MDIO_MMD_AN,  phydev->addr, (u32)val);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(bcm_phy_enable_eee);
+
+MODULE_DESCRIPTION("Broadcom PHY Library");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom Corporation");
diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h
new file mode 100644 (file)
index 0000000..b2091c8
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_BCM_PHY_LIB_H
+#define _LINUX_BCM_PHY_LIB_H
+
+#include <linux/phy.h>
+
+int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val);
+int bcm_phy_read_exp(struct phy_device *phydev, u16 reg);
+
+int bcm_phy_write_misc(struct phy_device *phydev,
+                      u16 reg, u16 chl, u16 value);
+int bcm_phy_read_misc(struct phy_device *phydev,
+                     u16 reg, u16 chl);
+
+int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
+                        u16 val);
+int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow);
+
+int bcm_phy_ack_intr(struct phy_device *phydev);
+int bcm_phy_config_intr(struct phy_device *phydev);
+
+int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down);
+
+int bcm_phy_enable_eee(struct phy_device *phydev);
+#endif /* _LINUX_BCM_PHY_LIB_H */
index 830ec31f952f290c0b4f3b5e58186ce5924ce55a..86b28052bf06e4228674085edbc4eeb599276617 100644 (file)
@@ -6,6 +6,7 @@
  *     as published by the Free Software Foundation; either version
  *     2 of the License, or (at your option) any later version.
  */
+#include "bcm-phy-lib.h"
 #include <linux/module.h>
 #include <linux/phy.h>
 
@@ -42,35 +43,6 @@ static int bcm63xx_config_init(struct phy_device *phydev)
        return phy_write(phydev, MII_BCM63XX_IR, reg);
 }
 
-static int bcm63xx_ack_interrupt(struct phy_device *phydev)
-{
-       int reg;
-
-       /* Clear pending interrupts.  */
-       reg = phy_read(phydev, MII_BCM63XX_IR);
-       if (reg < 0)
-               return reg;
-
-       return 0;
-}
-
-static int bcm63xx_config_intr(struct phy_device *phydev)
-{
-       int reg, err;
-
-       reg = phy_read(phydev, MII_BCM63XX_IR);
-       if (reg < 0)
-               return reg;
-
-       if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
-               reg &= ~MII_BCM63XX_IR_GMASK;
-       else
-               reg |= MII_BCM63XX_IR_GMASK;
-
-       err = phy_write(phydev, MII_BCM63XX_IR, reg);
-       return err;
-}
-
 static struct phy_driver bcm63xx_driver[] = {
 {
        .phy_id         = 0x00406000,
@@ -82,8 +54,8 @@ static struct phy_driver bcm63xx_driver[] = {
        .config_init    = bcm63xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
-       .ack_interrupt  = bcm63xx_ack_interrupt,
-       .config_intr    = bcm63xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 }, {
        /* same phy as above, with just a different OUI */
@@ -95,8 +67,8 @@ static struct phy_driver bcm63xx_driver[] = {
        .config_init    = bcm63xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
-       .ack_interrupt  = bcm63xx_ack_interrupt,
-       .config_intr    = bcm63xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 } };
 
index 6b701b3ded749642b6cdf2309e7b25d2b373ed37..03d4809a91268f26766d03e62bc7d3ff4ebde5b9 100644 (file)
 #include <linux/module.h>
 #include <linux/phy.h>
 #include <linux/delay.h>
+#include "bcm-phy-lib.h"
 #include <linux/bitops.h>
 #include <linux/brcmphy.h>
 #include <linux/mdio.h>
 
 /* Broadcom BCM7xxx internal PHY registers */
-#define MII_BCM7XXX_CHANNEL_WIDTH      0x2000
 
 /* 40nm only register definitions */
 #define MII_BCM7XXX_100TX_AUX_CTL      0x10
@@ -25,7 +25,6 @@
 #define MII_BCM7XXX_100TX_DISC         0x14
 #define MII_BCM7XXX_AUX_MODE           0x1d
 #define  MII_BCM7XX_64CLK_MDIO         BIT(12)
-#define MII_BCM7XXX_CORE_BASE1E                0x1e
 #define MII_BCM7XXX_TEST               0x1f
 #define  MII_BCM7XXX_SHD_MODE_2                BIT(2)
 
 #define AFE_VDAC_OTHERS_0              MISC_ADDR(0x39, 3)
 #define AFE_HPF_TRIM_OTHERS            MISC_ADDR(0x3a, 0)
 
-#define CORE_EXPB0                     0xb0
-
-static void phy_write_exp(struct phy_device *phydev,
-                                       u16 reg, u16 value)
-{
-       phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg);
-       phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
-}
-
-static void phy_write_misc(struct phy_device *phydev,
-                                       u16 reg, u16 chl, u16 value)
-{
-       int tmp;
-
-       phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
-
-       tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
-       tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
-       phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
-
-       tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg;
-       phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp);
-
-       phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
-}
-
 static void r_rc_cal_reset(struct phy_device *phydev)
 {
        /* Reset R_CAL/RC_CAL Engine */
-       phy_write_exp(phydev, 0x00b0, 0x0010);
+       bcm_phy_write_exp(phydev, 0x00b0, 0x0010);
 
        /* Disable Reset R_AL/RC_CAL Engine */
-       phy_write_exp(phydev, 0x00b0, 0x0000);
+       bcm_phy_write_exp(phydev, 0x00b0, 0x0000);
 }
 
 static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
@@ -86,38 +59,38 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
        /* Increase VCO range to prevent unlocking problem of PLL at low
         * temp
         */
-       phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
+       bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
 
        /* Change Ki to 011 */
-       phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
+       bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
 
        /* Disable loading of TVCO buffer to bandgap, set bandgap trim
         * to 111
         */
-       phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
+       bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
 
        /* Adjust bias current trim by -3 */
-       phy_write_misc(phydev, DSP_TAP10, 0x690b);
+       bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
 
        /* Switch to CORE_BASE1E */
-       phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd);
+       phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
 
        r_rc_cal_reset(phydev);
 
        /* write AFE_RXCONFIG_0 */
-       phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
+       bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
 
        /* write AFE_RXCONFIG_1 */
-       phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
+       bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
 
        /* write AFE_RX_LP_COUNTER */
-       phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
+       bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
 
        /* write AFE_HPF_TRIM_OTHERS */
-       phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
+       bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
 
        /* write AFTE_TX_CONFIG */
-       phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
+       bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
 
        return 0;
 }
@@ -125,36 +98,36 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
 static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)
 {
        /* AFE_RXCONFIG_0 */
-       phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15);
+       bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15);
 
        /* AFE_RXCONFIG_1 */
-       phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
+       bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
 
        /* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */
-       phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003);
+       bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003);
 
        /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */
-       phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
+       bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
 
        /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
-       phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
+       bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
 
        /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
-       phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
+       bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
 
        /* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */
-       phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020);
+       bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020);
 
        /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
         * offset for HT=0 code
         */
-       phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
+       bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
 
        /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
-       phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010);
+       phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
 
        /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
-       phy_write_misc(phydev, DSP_TAP10, 0x011b);
+       bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
 
        /* Reset R_CAL/RC_CAL engine */
        r_rc_cal_reset(phydev);
@@ -165,24 +138,24 @@ static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)
 static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)
 {
        /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */
-       phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
+       bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
 
        /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
-       phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
+       bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
 
        /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
-       phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
+       bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
 
        /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
         * offset for HT=0 code
         */
-       phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
+       bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
 
        /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
-       phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010);
+       phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
 
        /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
-       phy_write_misc(phydev, DSP_TAP10, 0x011b);
+       bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
 
        /* Reset R_CAL/RC_CAL engine */
        r_rc_cal_reset(phydev);
@@ -190,53 +163,6 @@ static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)
        return 0;
 }
 
-static int bcm7xxx_apd_enable(struct phy_device *phydev)
-{
-       int val;
-
-       /* Enable powering down of the DLL during auto-power down */
-       val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3);
-       if (val < 0)
-               return val;
-
-       val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
-       bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val);
-
-       /* Enable auto-power down */
-       val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD);
-       if (val < 0)
-               return val;
-
-       val |= BCM54XX_SHD_APD_EN;
-       return bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val);
-}
-
-static int bcm7xxx_eee_enable(struct phy_device *phydev)
-{
-       int val;
-
-       val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
-                                   MDIO_MMD_AN, phydev->addr);
-       if (val < 0)
-               return val;
-
-       /* Enable general EEE feature at the PHY level */
-       val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
-
-       phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
-                              MDIO_MMD_AN, phydev->addr, val);
-
-       /* Advertise supported modes */
-       val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
-                                   MDIO_MMD_AN, phydev->addr);
-
-       val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
-       phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
-                              MDIO_MMD_AN, phydev->addr, val);
-
-       return 0;
-}
-
 static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
 {
        u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
@@ -273,11 +199,11 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
        if (ret)
                return ret;
 
-       ret = bcm7xxx_eee_enable(phydev);
+       ret = bcm_phy_enable_eee(phydev);
        if (ret)
                return ret;
 
-       return bcm7xxx_apd_enable(phydev);
+       return bcm_phy_enable_apd(phydev, true);
 }
 
 static int bcm7xxx_28nm_resume(struct phy_device *phydev)
index 9c71295f2fefb5789693370c649c7ca9ceb34d92..07a6119121c38922aa50b3241e95d4604435a051 100644 (file)
@@ -14,6 +14,7 @@
  *     2 of the License, or (at your option) any later version.
  */
 
+#include "bcm-phy-lib.h"
 #include <linux/module.h>
 #include <linux/phy.h>
 #include <linux/brcmphy.h>
@@ -29,39 +30,6 @@ MODULE_DESCRIPTION("Broadcom PHY driver");
 MODULE_AUTHOR("Maciej W. Rozycki");
 MODULE_LICENSE("GPL");
 
-/* Indirect register access functions for the Expansion Registers */
-static int bcm54xx_exp_read(struct phy_device *phydev, u16 regnum)
-{
-       int val;
-
-       val = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
-       if (val < 0)
-               return val;
-
-       val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
-
-       /* Restore default value.  It's O.K. if this write fails. */
-       phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
-
-       return val;
-}
-
-static int bcm54xx_exp_write(struct phy_device *phydev, u16 regnum, u16 val)
-{
-       int ret;
-
-       ret = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
-       if (ret < 0)
-               return ret;
-
-       ret = phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
-
-       /* Restore default value.  It's O.K. if this write fails. */
-       phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
-
-       return ret;
-}
-
 static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
 {
        return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
@@ -72,28 +40,28 @@ static int bcm50610_a0_workaround(struct phy_device *phydev)
 {
        int err;
 
-       err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH0,
+       err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0,
                                MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
                                MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
        if (err < 0)
                return err;
 
-       err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH3,
-                                       MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
+       err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3,
+                               MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
        if (err < 0)
                return err;
 
-       err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75,
+       err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75,
                                MII_BCM54XX_EXP_EXP75_VDACCTRL);
        if (err < 0)
                return err;
 
-       err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP96,
+       err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96,
                                MII_BCM54XX_EXP_EXP96_MYST);
        if (err < 0)
                return err;
 
-       err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP97,
+       err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97,
                                MII_BCM54XX_EXP_EXP97_MYST);
 
        return err;
@@ -114,7 +82,7 @@ static int bcm54xx_phydsp_config(struct phy_device *phydev)
        if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
            BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
                /* Clear bit 9 to fix a phy interop issue. */
-               err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP08,
+               err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,
                                        MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
                if (err < 0)
                        goto error;
@@ -129,12 +97,12 @@ static int bcm54xx_phydsp_config(struct phy_device *phydev)
        if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
                int val;
 
-               val = bcm54xx_exp_read(phydev, MII_BCM54XX_EXP_EXP75);
+               val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);
                if (val < 0)
                        goto error;
 
                val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
-               err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, val);
+               err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val);
        }
 
 error:
@@ -159,7 +127,7 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
            BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M)
                return;
 
-       val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3);
+       val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
        if (val < 0)
                return;
 
@@ -190,9 +158,9 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
                val |= BCM54XX_SHD_SCR3_TRDDAPD;
 
        if (orig != val)
-               bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val);
+               bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
 
-       val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD);
+       val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
        if (val < 0)
                return;
 
@@ -204,7 +172,7 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
                val &= ~BCM54XX_SHD_APD_EN;
 
        if (orig != val)
-               bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val);
+               bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
 }
 
 static int bcm54xx_config_init(struct phy_device *phydev)
@@ -232,7 +200,7 @@ static int bcm54xx_config_init(struct phy_device *phydev)
        if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
             BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
            (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
-               bcm54xx_shadow_write(phydev, BCM54XX_SHD_RGMII_MODE, 0);
+               bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
 
        if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) ||
            (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) ||
@@ -254,8 +222,8 @@ static int bcm5482_config_init(struct phy_device *phydev)
                /*
                 * Enable secondary SerDes and its use as an LED source
                 */
-               reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_SSD);
-               bcm54xx_shadow_write(phydev, BCM5482_SHD_SSD,
+               reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_SSD);
+               bcm_phy_write_shadow(phydev, BCM5482_SHD_SSD,
                                     reg |
                                     BCM5482_SHD_SSD_LEDM |
                                     BCM5482_SHD_SSD_EN);
@@ -264,10 +232,10 @@ static int bcm5482_config_init(struct phy_device *phydev)
                 * Enable SGMII slave mode and auto-detection
                 */
                reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD;
-               err = bcm54xx_exp_read(phydev, reg);
+               err = bcm_phy_read_exp(phydev, reg);
                if (err < 0)
                        return err;
-               err = bcm54xx_exp_write(phydev, reg, err |
+               err = bcm_phy_write_exp(phydev, reg, err |
                                        BCM5482_SSD_SGMII_SLAVE_EN |
                                        BCM5482_SSD_SGMII_SLAVE_AD);
                if (err < 0)
@@ -277,10 +245,10 @@ static int bcm5482_config_init(struct phy_device *phydev)
                 * Disable secondary SerDes powerdown
                 */
                reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD;
-               err = bcm54xx_exp_read(phydev, reg);
+               err = bcm_phy_read_exp(phydev, reg);
                if (err < 0)
                        return err;
-               err = bcm54xx_exp_write(phydev, reg,
+               err = bcm_phy_write_exp(phydev, reg,
                                        err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);
                if (err < 0)
                        return err;
@@ -288,15 +256,15 @@ static int bcm5482_config_init(struct phy_device *phydev)
                /*
                 * Select 1000BASE-X register set (primary SerDes)
                 */
-               reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_MODE);
-               bcm54xx_shadow_write(phydev, BCM5482_SHD_MODE,
+               reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_MODE);
+               bcm_phy_write_shadow(phydev, BCM5482_SHD_MODE,
                                     reg | BCM5482_SHD_MODE_1000BX);
 
                /*
                 * LED1=ACTIVITYLED, LED3=LINKSPD[2]
                 * (Use LED1 as secondary SerDes ACTIVITY LED)
                 */
-               bcm54xx_shadow_write(phydev, BCM5482_SHD_LEDS1,
+               bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1,
                        BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) |
                        BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2));
 
@@ -334,35 +302,6 @@ static int bcm5482_read_status(struct phy_device *phydev)
        return err;
 }
 
-static int bcm54xx_ack_interrupt(struct phy_device *phydev)
-{
-       int reg;
-
-       /* Clear pending interrupts.  */
-       reg = phy_read(phydev, MII_BCM54XX_ISR);
-       if (reg < 0)
-               return reg;
-
-       return 0;
-}
-
-static int bcm54xx_config_intr(struct phy_device *phydev)
-{
-       int reg, err;
-
-       reg = phy_read(phydev, MII_BCM54XX_ECR);
-       if (reg < 0)
-               return reg;
-
-       if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
-               reg &= ~MII_BCM54XX_ECR_IM;
-       else
-               reg |= MII_BCM54XX_ECR_IM;
-
-       err = phy_write(phydev, MII_BCM54XX_ECR, reg);
-       return err;
-}
-
 static int bcm5481_config_aneg(struct phy_device *phydev)
 {
        int ret;
@@ -519,8 +458,8 @@ static struct phy_driver broadcom_drivers[] = {
        .config_init    = bcm54xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
-       .ack_interrupt  = bcm54xx_ack_interrupt,
-       .config_intr    = bcm54xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 }, {
        .phy_id         = PHY_ID_BCM5421,
@@ -532,8 +471,8 @@ static struct phy_driver broadcom_drivers[] = {
        .config_init    = bcm54xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
-       .ack_interrupt  = bcm54xx_ack_interrupt,
-       .config_intr    = bcm54xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 }, {
        .phy_id         = PHY_ID_BCM5461,
@@ -545,8 +484,8 @@ static struct phy_driver broadcom_drivers[] = {
        .config_init    = bcm54xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
-       .ack_interrupt  = bcm54xx_ack_interrupt,
-       .config_intr    = bcm54xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 }, {
        .phy_id         = PHY_ID_BCM54616S,
@@ -558,8 +497,8 @@ static struct phy_driver broadcom_drivers[] = {
        .config_init    = bcm54xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
-       .ack_interrupt  = bcm54xx_ack_interrupt,
-       .config_intr    = bcm54xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 }, {
        .phy_id         = PHY_ID_BCM5464,
@@ -571,8 +510,8 @@ static struct phy_driver broadcom_drivers[] = {
        .config_init    = bcm54xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
-       .ack_interrupt  = bcm54xx_ack_interrupt,
-       .config_intr    = bcm54xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 }, {
        .phy_id         = PHY_ID_BCM5481,
@@ -584,8 +523,8 @@ static struct phy_driver broadcom_drivers[] = {
        .config_init    = bcm54xx_config_init,
        .config_aneg    = bcm5481_config_aneg,
        .read_status    = genphy_read_status,
-       .ack_interrupt  = bcm54xx_ack_interrupt,
-       .config_intr    = bcm54xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 }, {
        .phy_id         = PHY_ID_BCM5482,
@@ -597,8 +536,8 @@ static struct phy_driver broadcom_drivers[] = {
        .config_init    = bcm5482_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = bcm5482_read_status,
-       .ack_interrupt  = bcm54xx_ack_interrupt,
-       .config_intr    = bcm54xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 }, {
        .phy_id         = PHY_ID_BCM50610,
@@ -610,8 +549,8 @@ static struct phy_driver broadcom_drivers[] = {
        .config_init    = bcm54xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
-       .ack_interrupt  = bcm54xx_ack_interrupt,
-       .config_intr    = bcm54xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 }, {
        .phy_id         = PHY_ID_BCM50610M,
@@ -623,8 +562,8 @@ static struct phy_driver broadcom_drivers[] = {
        .config_init    = bcm54xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
-       .ack_interrupt  = bcm54xx_ack_interrupt,
-       .config_intr    = bcm54xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 }, {
        .phy_id         = PHY_ID_BCM57780,
@@ -636,8 +575,8 @@ static struct phy_driver broadcom_drivers[] = {
        .config_init    = bcm54xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
-       .ack_interrupt  = bcm54xx_ack_interrupt,
-       .config_intr    = bcm54xx_config_intr,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
        .driver         = { .owner = THIS_MODULE },
 }, {
        .phy_id         = PHY_ID_BCMAC131,
index 185b03c08e16ce9140b2b0d91abdfacd5b9258a1..47b711739ba902cd300f53489a8d00aa87d1a660 100644 (file)
@@ -20,6 +20,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/crc32.h>
 #include <linux/ethtool.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
@@ -36,8 +37,6 @@
 
 #define DP83640_PHY_ID 0x20005ce1
 #define PAGESEL                0x13
-#define LAYER4         0x02
-#define LAYER2         0x01
 #define MAX_RXTS       64
 #define N_EXT_TS       6
 #define N_PER_OUT      7
@@ -68,6 +67,8 @@
 /* phyter seems to miss the mark by 16 ns */
 #define ADJTIME_FIX    16
 
+#define SKB_TIMESTAMP_TIMEOUT  2 /* jiffies */
+
 #if defined(__BIG_ENDIAN)
 #define ENDIAN_FLAG    0
 #elif defined(__LITTLE_ENDIAN)
@@ -110,7 +111,7 @@ struct dp83640_private {
        struct list_head list;
        struct dp83640_clock *clock;
        struct phy_device *phydev;
-       struct work_struct ts_work;
+       struct delayed_work ts_work;
        int hwts_tx_en;
        int hwts_rx_en;
        int layer;
@@ -284,7 +285,7 @@ static void phy2rxts(struct phy_rxts *p, struct rxts *rxts)
        rxts->seqid = p->seqid;
        rxts->msgtype = (p->msgtype >> 12) & 0xf;
        rxts->hash = p->msgtype & 0x0fff;
-       rxts->tmo = jiffies + 2;
+       rxts->tmo = jiffies + SKB_TIMESTAMP_TIMEOUT;
 }
 
 static u64 phy2txts(struct phy_txts *p)
@@ -787,9 +788,12 @@ static int decode_evnt(struct dp83640_private *dp83640,
        return parsed;
 }
 
+#define DP83640_PACKET_HASH_OFFSET     20
+#define DP83640_PACKET_HASH_LEN                10
+
 static int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts)
 {
-       u16 *seqid;
+       u16 *seqid, hash;
        unsigned int offset = 0;
        u8 *msgtype, *data = skb_mac_header(skb);
 
@@ -819,11 +823,19 @@ static int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts)
                msgtype = data + offset + OFF_PTP_CONTROL;
        else
                msgtype = data + offset;
+       if (rxts->msgtype != (*msgtype & 0xf))
+               return 0;
 
        seqid = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID);
+       if (rxts->seqid != ntohs(*seqid))
+               return 0;
+
+       hash = ether_crc(DP83640_PACKET_HASH_LEN,
+                        data + offset + DP83640_PACKET_HASH_OFFSET) >> 20;
+       if (rxts->hash != hash)
+               return 0;
 
-       return rxts->msgtype == (*msgtype & 0xf) &&
-               rxts->seqid   == ntohs(*seqid);
+       return 1;
 }
 
 static void decode_rxts(struct dp83640_private *dp83640,
@@ -1103,7 +1115,7 @@ static int dp83640_probe(struct phy_device *phydev)
                goto no_memory;
 
        dp83640->phydev = phydev;
-       INIT_WORK(&dp83640->ts_work, rx_timestamp_work);
+       INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work);
 
        INIT_LIST_HEAD(&dp83640->rxts);
        INIT_LIST_HEAD(&dp83640->rxpool);
@@ -1150,7 +1162,7 @@ static void dp83640_remove(struct phy_device *phydev)
                return;
 
        enable_status_frames(phydev, false);
-       cancel_work_sync(&dp83640->ts_work);
+       cancel_delayed_work_sync(&dp83640->ts_work);
 
        skb_queue_purge(&dp83640->rx_queue);
        skb_queue_purge(&dp83640->tx_queue);
@@ -1282,29 +1294,29 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
        case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
        case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
                dp83640->hwts_rx_en = 1;
-               dp83640->layer = LAYER4;
-               dp83640->version = 1;
+               dp83640->layer = PTP_CLASS_L4;
+               dp83640->version = PTP_CLASS_V1;
                break;
        case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
        case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
        case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
                dp83640->hwts_rx_en = 1;
-               dp83640->layer = LAYER4;
-               dp83640->version = 2;
+               dp83640->layer = PTP_CLASS_L4;
+               dp83640->version = PTP_CLASS_V2;
                break;
        case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
        case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
        case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
                dp83640->hwts_rx_en = 1;
-               dp83640->layer = LAYER2;
-               dp83640->version = 2;
+               dp83640->layer = PTP_CLASS_L2;
+               dp83640->version = PTP_CLASS_V2;
                break;
        case HWTSTAMP_FILTER_PTP_V2_EVENT:
        case HWTSTAMP_FILTER_PTP_V2_SYNC:
        case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
                dp83640->hwts_rx_en = 1;
-               dp83640->layer = LAYER4|LAYER2;
-               dp83640->version = 2;
+               dp83640->layer = PTP_CLASS_L4 | PTP_CLASS_L2;
+               dp83640->version = PTP_CLASS_V2;
                break;
        default:
                return -ERANGE;
@@ -1313,11 +1325,11 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
        txcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT;
        rxcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT;
 
-       if (dp83640->layer & LAYER2) {
+       if (dp83640->layer & PTP_CLASS_L2) {
                txcfg0 |= TX_L2_EN;
                rxcfg0 |= RX_L2_EN;
        }
-       if (dp83640->layer & LAYER4) {
+       if (dp83640->layer & PTP_CLASS_L4) {
                txcfg0 |= TX_IPV6_EN | TX_IPV4_EN;
                rxcfg0 |= RX_IPV6_EN | RX_IPV4_EN;
        }
@@ -1344,7 +1356,7 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
 static void rx_timestamp_work(struct work_struct *work)
 {
        struct dp83640_private *dp83640 =
-               container_of(work, struct dp83640_private, ts_work);
+               container_of(work, struct dp83640_private, ts_work.work);
        struct sk_buff *skb;
 
        /* Deliver expired packets. */
@@ -1361,7 +1373,7 @@ static void rx_timestamp_work(struct work_struct *work)
        }
 
        if (!skb_queue_empty(&dp83640->rx_queue))
-               schedule_work(&dp83640->ts_work);
+               schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT);
 }
 
 static bool dp83640_rxtstamp(struct phy_device *phydev,
@@ -1383,7 +1395,11 @@ static bool dp83640_rxtstamp(struct phy_device *phydev,
        if (!dp83640->hwts_rx_en)
                return false;
 
+       if ((type & dp83640->version) == 0 || (type & dp83640->layer) == 0)
+               return false;
+
        spin_lock_irqsave(&dp83640->rx_lock, flags);
+       prune_rx_ts(dp83640);
        list_for_each_safe(this, next, &dp83640->rxts) {
                rxts = list_entry(this, struct rxts, list);
                if (match(skb, type, rxts)) {
@@ -1400,9 +1416,11 @@ static bool dp83640_rxtstamp(struct phy_device *phydev,
 
        if (!shhwtstamps) {
                skb_info->ptp_type = type;
-               skb_info->tmo = jiffies + 2;
+               skb_info->tmo = jiffies + SKB_TIMESTAMP_TIMEOUT;
                skb_queue_tail(&dp83640->rx_queue, skb);
-               schedule_work(&dp83640->ts_work);
+               schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT);
+       } else {
+               netif_rx_ni(skb);
        }
 
        return true;
diff --git a/drivers/net/phy/dp83848.c b/drivers/net/phy/dp83848.c
new file mode 100644 (file)
index 0000000..5ce9bef
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Driver for the Texas Instruments DP83848 PHY
+ *
+ * Copyright (C) 2015 Texas Instruments 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.
+ *
+ * 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>
+#include <linux/phy.h>
+
+#define DP83848_PHY_ID                 0x20005c90
+
+/* Registers */
+#define DP83848_MICR                   0x11
+#define DP83848_MISR                   0x12
+
+/* MICR Register Fields */
+#define DP83848_MICR_INT_OE            BIT(0) /* Interrupt Output Enable */
+#define DP83848_MICR_INTEN             BIT(1) /* Interrupt Enable */
+
+/* MISR Register Fields */
+#define DP83848_MISR_RHF_INT_EN                BIT(0) /* Receive Error Counter */
+#define DP83848_MISR_FHF_INT_EN                BIT(1) /* False Carrier Counter */
+#define DP83848_MISR_ANC_INT_EN                BIT(2) /* Auto-negotiation complete */
+#define DP83848_MISR_DUP_INT_EN                BIT(3) /* Duplex Status */
+#define DP83848_MISR_SPD_INT_EN                BIT(4) /* Speed status */
+#define DP83848_MISR_LINK_INT_EN       BIT(5) /* Link status */
+#define DP83848_MISR_ED_INT_EN         BIT(6) /* Energy detect */
+#define DP83848_MISR_LQM_INT_EN                BIT(7) /* Link Quality Monitor */
+
+static int dp83848_ack_interrupt(struct phy_device *phydev)
+{
+       int err = phy_read(phydev, DP83848_MISR);
+
+       return err < 0 ? err : 0;
+}
+
+static int dp83848_config_intr(struct phy_device *phydev)
+{
+       int err;
+
+       if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+               err = phy_write(phydev, DP83848_MICR,
+                               DP83848_MICR_INT_OE |
+                               DP83848_MICR_INTEN);
+               if (err < 0)
+                       return err;
+
+               return phy_write(phydev, DP83848_MISR,
+                                DP83848_MISR_ANC_INT_EN |
+                                DP83848_MISR_DUP_INT_EN |
+                                DP83848_MISR_SPD_INT_EN |
+                                DP83848_MISR_LINK_INT_EN);
+       }
+
+       return phy_write(phydev, DP83848_MICR, 0x0);
+}
+
+static struct mdio_device_id __maybe_unused dp83848_tbl[] = {
+       { DP83848_PHY_ID, 0xfffffff0 },
+       { }
+};
+MODULE_DEVICE_TABLE(mdio, dp83848_tbl);
+
+static struct phy_driver dp83848_driver[] = {
+       {
+               .phy_id         = DP83848_PHY_ID,
+               .phy_id_mask    = 0xfffffff0,
+               .name           = "TI DP83848",
+               .features       = PHY_BASIC_FEATURES,
+               .flags          = PHY_HAS_INTERRUPT,
+
+               .soft_reset     = genphy_soft_reset,
+               .config_init    = genphy_config_init,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
+               .config_aneg    = genphy_config_aneg,
+               .read_status    = genphy_read_status,
+
+               /* IRQ related */
+               .ack_interrupt  = dp83848_ack_interrupt,
+               .config_intr    = dp83848_config_intr,
+
+               .driver         = { .owner = THIS_MODULE, },
+       },
+};
+module_phy_driver(dp83848_driver);
+
+MODULE_DESCRIPTION("Texas Instruments DP83848 PHY driver");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/mdio-bcm-iproc.c b/drivers/net/phy/mdio-bcm-iproc.c
new file mode 100644 (file)
index 0000000..c0b4e65
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+
+#define IPROC_GPHY_MDCDIV    0x1a
+
+#define MII_CTRL_OFFSET      0x000
+
+#define MII_CTRL_DIV_SHIFT   0
+#define MII_CTRL_PRE_SHIFT   7
+#define MII_CTRL_BUSY_SHIFT  8
+
+#define MII_DATA_OFFSET      0x004
+#define MII_DATA_MASK        0xffff
+#define MII_DATA_TA_SHIFT    16
+#define MII_DATA_TA_VAL      2
+#define MII_DATA_RA_SHIFT    18
+#define MII_DATA_PA_SHIFT    23
+#define MII_DATA_OP_SHIFT    28
+#define MII_DATA_OP_WRITE    1
+#define MII_DATA_OP_READ     2
+#define MII_DATA_SB_SHIFT    30
+
+struct iproc_mdio_priv {
+       struct mii_bus *mii_bus;
+       void __iomem *base;
+};
+
+static inline int iproc_mdio_wait_for_idle(void __iomem *base)
+{
+       u32 val;
+       unsigned int timeout = 1000; /* loop for 1s */
+
+       do {
+               val = readl(base + MII_CTRL_OFFSET);
+               if ((val & BIT(MII_CTRL_BUSY_SHIFT)) == 0)
+                       return 0;
+
+               usleep_range(1000, 2000);
+       } while (timeout--);
+
+       return -ETIMEDOUT;
+}
+
+static inline void iproc_mdio_config_clk(void __iomem *base)
+{
+       u32 val;
+
+       val = (IPROC_GPHY_MDCDIV << MII_CTRL_DIV_SHIFT) |
+                 BIT(MII_CTRL_PRE_SHIFT);
+       writel(val, base + MII_CTRL_OFFSET);
+}
+
+static int iproc_mdio_read(struct mii_bus *bus, int phy_id, int reg)
+{
+       struct iproc_mdio_priv *priv = bus->priv;
+       u32 cmd;
+       int rc;
+
+       rc = iproc_mdio_wait_for_idle(priv->base);
+       if (rc)
+               return rc;
+
+       iproc_mdio_config_clk(priv->base);
+
+       /* Prepare the read operation */
+       cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) |
+               (reg << MII_DATA_RA_SHIFT) |
+               (phy_id << MII_DATA_PA_SHIFT) |
+               BIT(MII_DATA_SB_SHIFT) |
+               (MII_DATA_OP_READ << MII_DATA_OP_SHIFT);
+
+       writel(cmd, priv->base + MII_DATA_OFFSET);
+
+       rc = iproc_mdio_wait_for_idle(priv->base);
+       if (rc)
+               return rc;
+
+       cmd = readl(priv->base + MII_DATA_OFFSET) & MII_DATA_MASK;
+
+       return cmd;
+}
+
+static int iproc_mdio_write(struct mii_bus *bus, int phy_id,
+                           int reg, u16 val)
+{
+       struct iproc_mdio_priv *priv = bus->priv;
+       u32 cmd;
+       int rc;
+
+       rc = iproc_mdio_wait_for_idle(priv->base);
+       if (rc)
+               return rc;
+
+       iproc_mdio_config_clk(priv->base);
+
+       /* Prepare the write operation */
+       cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) |
+               (reg << MII_DATA_RA_SHIFT) |
+               (phy_id << MII_DATA_PA_SHIFT) |
+               BIT(MII_DATA_SB_SHIFT) |
+               (MII_DATA_OP_WRITE << MII_DATA_OP_SHIFT) |
+               ((u32)(val) & MII_DATA_MASK);
+
+       writel(cmd, priv->base + MII_DATA_OFFSET);
+
+       rc = iproc_mdio_wait_for_idle(priv->base);
+       if (rc)
+               return rc;
+
+       return 0;
+}
+
+static int iproc_mdio_probe(struct platform_device *pdev)
+{
+       struct iproc_mdio_priv *priv;
+       struct mii_bus *bus;
+       struct resource *res;
+       int rc;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(priv->base)) {
+               dev_err(&pdev->dev, "failed to ioremap register\n");
+               return PTR_ERR(priv->base);
+       }
+
+       priv->mii_bus = mdiobus_alloc();
+       if (!priv->mii_bus) {
+               dev_err(&pdev->dev, "MDIO bus alloc failed\n");
+               return -ENOMEM;
+       }
+
+       bus = priv->mii_bus;
+       bus->priv = priv;
+       bus->name = "iProc MDIO bus";
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);
+       bus->parent = &pdev->dev;
+       bus->read = iproc_mdio_read;
+       bus->write = iproc_mdio_write;
+
+       rc = of_mdiobus_register(bus, pdev->dev.of_node);
+       if (rc) {
+               dev_err(&pdev->dev, "MDIO bus registration failed\n");
+               goto err_iproc_mdio;
+       }
+
+       platform_set_drvdata(pdev, priv);
+
+       dev_info(&pdev->dev, "Broadcom iProc MDIO bus at 0x%p\n", priv->base);
+
+       return 0;
+
+err_iproc_mdio:
+       mdiobus_free(bus);
+       return rc;
+}
+
+static int iproc_mdio_remove(struct platform_device *pdev)
+{
+       struct iproc_mdio_priv *priv = platform_get_drvdata(pdev);
+
+       mdiobus_unregister(priv->mii_bus);
+       mdiobus_free(priv->mii_bus);
+
+       return 0;
+}
+
+static const struct of_device_id iproc_mdio_of_match[] = {
+       { .compatible = "brcm,iproc-mdio", },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, iproc_mdio_of_match);
+
+static struct platform_driver iproc_mdio_driver = {
+       .driver = {
+               .name = "iproc-mdio",
+               .of_match_table = iproc_mdio_of_match,
+       },
+       .probe = iproc_mdio_probe,
+       .remove = iproc_mdio_remove,
+};
+
+module_platform_driver(iproc_mdio_driver);
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:iproc-mdio");
index 3bc9f03349f3a47d1c724cb50bd05e345f025f3e..95f51d7267b3da85d74506ab4ac867ea25eca570 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
-#include <linux/mdio-gpio.h>
+#include <linux/platform_data/mdio-gpio.h>
 
 #include <linux/of_gpio.h>
 #include <linux/of_mdio.h>
index 2377c1341172f6ac6fb56eeaf835b7fc577e1593..7fde454fbc4f1762b2c0bb35cf41f60580fd16ed 100644 (file)
@@ -113,12 +113,14 @@ static int mdio_mux_mmioreg_probe(struct platform_device *pdev)
                if (!iprop || len != sizeof(uint32_t)) {
                        dev_err(&pdev->dev, "mdio-mux child node %s is "
                                "missing a 'reg' property\n", np2->full_name);
+                       of_node_put(np2);
                        return -ENODEV;
                }
                if (be32_to_cpup(iprop) & ~s->mask) {
                        dev_err(&pdev->dev, "mdio-mux child node %s has "
                                "a 'reg' value with unmasked bits\n",
                                np2->full_name);
+                       of_node_put(np2);
                        return -ENODEV;
                }
        }
index 280c7c311f72442c4877815ece73c2a36745be49..908e8d4863429fb6c11f4a625449500f3be36ce1 100644 (file)
@@ -144,6 +144,7 @@ int mdio_mux_init(struct device *dev,
                        dev_err(dev,
                                "Error: Failed to allocate memory for child\n");
                        ret_val = -ENOMEM;
+                       of_node_put(child_bus_node);
                        break;
                }
                cb->bus_number = v;
index 12f44c53cc8ebca7cba92119c26b01b249e31846..88cb4592b6fbbc6cc1ed6812b0fad2d17885a17e 100644 (file)
@@ -371,6 +371,33 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
 }
 EXPORT_SYMBOL(mdiobus_scan);
 
+/**
+ * mdiobus_read_nested - Nested version of the mdiobus_read function
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+ * @regnum: register number to read
+ *
+ * In case of nested MDIO bus access avoid lockdep false positives by
+ * using mutex_lock_nested().
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ */
+int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum)
+{
+       int retval;
+
+       BUG_ON(in_interrupt());
+
+       mutex_lock_nested(&bus->mdio_lock, SINGLE_DEPTH_NESTING);
+       retval = bus->read(bus, addr, regnum);
+       mutex_unlock(&bus->mdio_lock);
+
+       return retval;
+}
+EXPORT_SYMBOL(mdiobus_read_nested);
+
 /**
  * mdiobus_read - Convenience function for reading a given MII mgmt register
  * @bus: the mii_bus struct
@@ -395,6 +422,34 @@ int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
 }
 EXPORT_SYMBOL(mdiobus_read);
 
+/**
+ * mdiobus_write_nested - Nested version of the mdiobus_write function
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+ * @regnum: register number to write
+ * @val: value to write to @regnum
+ *
+ * In case of nested MDIO bus access avoid lockdep false positives by
+ * using mutex_lock_nested().
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ */
+int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val)
+{
+       int err;
+
+       BUG_ON(in_interrupt());
+
+       mutex_lock_nested(&bus->mdio_lock, SINGLE_DEPTH_NESTING);
+       err = bus->write(bus, addr, regnum, val);
+       mutex_unlock(&bus->mdio_lock);
+
+       return err;
+}
+EXPORT_SYMBOL(mdiobus_write_nested);
+
 /**
  * mdiobus_write - Convenience function for writing a given MII mgmt register
  * @bus: the mii_bus struct
index 499185eaf413ba08b1447fbebf1f60080c643329..cf6312fafea545fbc3efb96e8ff6c63b35c7420e 100644 (file)
@@ -514,6 +514,27 @@ static int ksz8873mll_read_status(struct phy_device *phydev)
        return 0;
 }
 
+static int ksz9031_read_status(struct phy_device *phydev)
+{
+       int err;
+       int regval;
+
+       err = genphy_read_status(phydev);
+       if (err)
+               return err;
+
+       /* Make sure the PHY is not broken. Read idle error count,
+        * and reset the PHY if it is maxed out.
+        */
+       regval = phy_read(phydev, MII_STAT1000);
+       if ((regval & 0xFF) == 0xFF) {
+               phy_init_hw(phydev);
+               phydev->link = 0;
+       }
+
+       return 0;
+}
+
 static int ksz8873mll_config_aneg(struct phy_device *phydev)
 {
        return 0;
@@ -772,7 +793,7 @@ static struct phy_driver ksphy_driver[] = {
        .driver_data    = &ksz9021_type,
        .config_init    = ksz9031_config_init,
        .config_aneg    = genphy_config_aneg,
-       .read_status    = genphy_read_status,
+       .read_status    = ksz9031_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
        .suspend        = genphy_suspend,
index f761288abe660b7d49b0a9a7dbd92b11efdbbbba..0bfbabad44316cc85f3a8497be222d545e34abfb 100644 (file)
@@ -205,6 +205,37 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
 }
 EXPORT_SYMBOL(phy_device_create);
 
+/* get_phy_c45_devs_in_pkg - reads a MMD's devices in package registers.
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ * @dev_addr: MMD address in the PHY.
+ * @devices_in_package: where to store the devices in package information.
+ *
+ * Description: reads devices in package registers of a MMD at @dev_addr
+ * from PHY at @addr on @bus.
+ *
+ * Returns: 0 on success, -EIO on failure.
+ */
+static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
+                                  u32 *devices_in_package)
+{
+       int phy_reg, reg_addr;
+
+       reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS2;
+       phy_reg = mdiobus_read(bus, addr, reg_addr);
+       if (phy_reg < 0)
+               return -EIO;
+       *devices_in_package = (phy_reg & 0xffff) << 16;
+
+       reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS1;
+       phy_reg = mdiobus_read(bus, addr, reg_addr);
+       if (phy_reg < 0)
+               return -EIO;
+       *devices_in_package |= (phy_reg & 0xffff);
+
+       return 0;
+}
+
 /**
  * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs.
  * @bus: the target MII bus
@@ -223,38 +254,31 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id,
        int phy_reg;
        int i, reg_addr;
        const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
+       u32 *devs = &c45_ids->devices_in_package;
 
-       /* Find first non-zero Devices In package.  Device
-        * zero is reserved, so don't probe it.
+       /* Find first non-zero Devices In package. Device zero is reserved
+        * for 802.3 c45 complied PHYs, so don't probe it at first.
         */
-       for (i = 1;
-            i < num_ids && c45_ids->devices_in_package == 0;
-            i++) {
-retry:         reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS2;
-               phy_reg = mdiobus_read(bus, addr, reg_addr);
+       for (i = 1; i < num_ids && *devs == 0; i++) {
+               phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, devs);
                if (phy_reg < 0)
                        return -EIO;
-               c45_ids->devices_in_package = (phy_reg & 0xffff) << 16;
 
-               reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS1;
-               phy_reg = mdiobus_read(bus, addr, reg_addr);
-               if (phy_reg < 0)
-                       return -EIO;
-               c45_ids->devices_in_package |= (phy_reg & 0xffff);
-
-               if ((c45_ids->devices_in_package & 0x1fffffff) == 0x1fffffff) {
-                       if (i) {
-                               /*  If mostly Fs, there is no device there,
-                                *  then let's continue to probe more, as some
-                                *  10G PHYs have zero Devices In package,
-                                *  e.g. Cortina CS4315/CS4340 PHY.
-                                */
-                               i = 0;
-                               goto retry;
-                       } else {
-                               /* no device there, let's get out of here */
+               if ((*devs & 0x1fffffff) == 0x1fffffff) {
+                       /*  If mostly Fs, there is no device there,
+                        *  then let's continue to probe more, as some
+                        *  10G PHYs have zero Devices In package,
+                        *  e.g. Cortina CS4315/CS4340 PHY.
+                        */
+                       phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, devs);
+                       if (phy_reg < 0)
+                               return -EIO;
+                       /* no device there, let's get out of here */
+                       if ((*devs & 0x1fffffff) == 0x1fffffff) {
                                *phy_id = 0xffffffff;
                                return 0;
+                       } else {
+                               break;
                        }
                }
        }
@@ -1239,6 +1263,44 @@ static int gen10g_resume(struct phy_device *phydev)
        return 0;
 }
 
+static int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
+{
+       /* The default values for phydev->supported are provided by the PHY
+        * driver "features" member, we want to reset to sane defaults first
+        * before supporting higher speeds.
+        */
+       phydev->supported &= PHY_DEFAULT_FEATURES;
+
+       switch (max_speed) {
+       default:
+               return -ENOTSUPP;
+       case SPEED_1000:
+               phydev->supported |= PHY_1000BT_FEATURES;
+               /* fall through */
+       case SPEED_100:
+               phydev->supported |= PHY_100BT_FEATURES;
+               /* fall through */
+       case SPEED_10:
+               phydev->supported |= PHY_10BT_FEATURES;
+       }
+
+       return 0;
+}
+
+int phy_set_max_speed(struct phy_device *phydev, u32 max_speed)
+{
+       int err;
+
+       err = __set_phy_supported(phydev, max_speed);
+       if (err)
+               return err;
+
+       phydev->advertising = phydev->supported;
+
+       return 0;
+}
+EXPORT_SYMBOL(phy_set_max_speed);
+
 static void of_set_phy_supported(struct phy_device *phydev)
 {
        struct device_node *node = phydev->dev.of_node;
@@ -1250,25 +1312,8 @@ static void of_set_phy_supported(struct phy_device *phydev)
        if (!node)
                return;
 
-       if (!of_property_read_u32(node, "max-speed", &max_speed)) {
-               /* The default values for phydev->supported are provided by the PHY
-                * driver "features" member, we want to reset to sane defaults fist
-                * before supporting higher speeds.
-                */
-               phydev->supported &= PHY_DEFAULT_FEATURES;
-
-               switch (max_speed) {
-               default:
-                       return;
-
-               case SPEED_1000:
-                       phydev->supported |= PHY_1000BT_FEATURES;
-               case SPEED_100:
-                       phydev->supported |= PHY_100BT_FEATURES;
-               case SPEED_10:
-                       phydev->supported |= PHY_10BT_FEATURES;
-               }
-       }
+       if (!of_property_read_u32(node, "max-speed", &max_speed))
+               __set_phy_supported(phydev, max_speed);
 }
 
 /**
index 70b08958763a129fff47ad00a1db130c1334f254..dc2da87709185870f808ef626e8446893c658db6 100644 (file)
@@ -43,16 +43,25 @@ static int smsc_phy_ack_interrupt(struct phy_device *phydev)
 
 static int smsc_phy_config_init(struct phy_device *phydev)
 {
+       int __maybe_unused len;
+       struct device *dev __maybe_unused = &phydev->dev;
+       struct device_node *of_node __maybe_unused = dev->of_node;
        int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
+       int enable_energy = 1;
 
        if (rc < 0)
                return rc;
 
-       /* Enable energy detect mode for this SMSC Transceivers */
-       rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
-                      rc | MII_LAN83C185_EDPWRDOWN);
-       if (rc < 0)
-               return rc;
+       if (of_find_property(of_node, "smsc,disable-energy-detect", &len))
+               enable_energy = 0;
+
+       if (enable_energy) {
+               /* Enable energy detect mode for this SMSC Transceivers */
+               rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
+                              rc | MII_LAN83C185_EDPWRDOWN);
+               if (rc < 0)
+                       return rc;
+       }
 
        return smsc_phy_ack_interrupt(phydev);
 }
index 91e1bec6079fafc20665a3d26949250da2817a55..07463fcca2126fd8838bf0fefee228dd11fd31d6 100644 (file)
@@ -112,20 +112,7 @@ static struct phy_driver teranetics_driver[] = {
 },
 };
 
-static int __init teranetics_init(void)
-{
-       return phy_drivers_register(teranetics_driver,
-                                   ARRAY_SIZE(teranetics_driver));
-}
-
-static void __exit teranetics_exit(void)
-{
-       return phy_drivers_unregister(teranetics_driver,
-                                     ARRAY_SIZE(teranetics_driver));
-}
-
-module_init(teranetics_init);
-module_exit(teranetics_exit);
+module_phy_driver(teranetics_driver);
 
 static struct mdio_device_id __maybe_unused teranetics_tbl[] = {
        { PHY_ID_TN2020, 0xffffffff },
index ed00446759b2546d9026010d8b5148bc03ae2d95..9a863c6a6a33aa7883f6fa105fca917ae177e25c 100644 (file)
@@ -721,10 +721,8 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                        val &= 0xffff;
                }
                vj = slhc_init(val2+1, val+1);
-               if (!vj) {
-                       netdev_err(ppp->dev,
-                                  "PPP: no memory (VJ compressor)\n");
-                       err = -ENOMEM;
+               if (IS_ERR(vj)) {
+                       err = PTR_ERR(vj);
                        break;
                }
                ppp_lock(ppp);
index 2ed75060da50abe74a7f303010aad8c937a31b73..5e0b43283bce2c4f5251e5c5db982ca679526d07 100644 (file)
@@ -589,7 +589,7 @@ static int pppoe_release(struct socket *sock)
 
        po = pppox_sk(sk);
 
-       if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) {
+       if (po->pppoe_dev) {
                dev_put(po->pppoe_dev);
                po->pppoe_dev = NULL;
        }
index 686f37daa262b180f5efed77152335e62364b4ef..fc69e41d09506e96b38f067fa05175bd680fa36b 100644 (file)
@@ -169,6 +169,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
 {
        struct sock *sk = (struct sock *) chan->private;
        struct pppox_sock *po = pppox_sk(sk);
+       struct net *net = sock_net(sk);
        struct pptp_opt *opt = &po->proto.pptp;
        struct pptp_gre_header *hdr;
        unsigned int header_len = sizeof(*hdr);
@@ -187,7 +188,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        if (sk_pppox(po)->sk_state & PPPOX_DEAD)
                goto tx_error;
 
-       rt = ip_route_output_ports(sock_net(sk), &fl4, NULL,
+       rt = ip_route_output_ports(net, &fl4, NULL,
                                   opt->dst_addr.sin_addr.s_addr,
                                   opt->src_addr.sin_addr.s_addr,
                                   0, 0, IPPROTO_GRE,
@@ -279,10 +280,10 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        nf_reset(skb);
 
        skb->ip_summed = CHECKSUM_NONE;
-       ip_select_ident(sock_net(sk), skb, NULL);
+       ip_select_ident(net, skb, NULL);
        ip_send_check(iph);
 
-       ip_local_out(skb);
+       ip_local_out(net, skb->sk, skb);
        return 1;
 
 tx_error:
index 079f7adfcde5ef0fe7bf4637712ef3ac738c2f1e..27ed25252aac502838527ec48ba48eda754ed69c 100644 (file)
@@ -84,8 +84,9 @@ static long decode(unsigned char **cpp);
 static unsigned char * put16(unsigned char *cp, unsigned short x);
 static unsigned short pull16(unsigned char **cpp);
 
-/* Initialize compression data structure
+/* Allocate compression data structure
  *     slots must be in range 0 to 255 (zero meaning no compression)
+ * Returns pointer to structure or ERR_PTR() on error.
  */
 struct slcompress *
 slhc_init(int rslots, int tslots)
@@ -94,11 +95,14 @@ slhc_init(int rslots, int tslots)
        register struct cstate *ts;
        struct slcompress *comp;
 
+       if (rslots < 0 || rslots > 255 || tslots < 0 || tslots > 255)
+               return ERR_PTR(-EINVAL);
+
        comp = kzalloc(sizeof(struct slcompress), GFP_KERNEL);
        if (! comp)
                goto out_fail;
 
-       if ( rslots > 0  &&  rslots < 256 ) {
+       if (rslots > 0) {
                size_t rsize = rslots * sizeof(struct cstate);
                comp->rstate = kzalloc(rsize, GFP_KERNEL);
                if (! comp->rstate)
@@ -106,7 +110,7 @@ slhc_init(int rslots, int tslots)
                comp->rslot_limit = rslots - 1;
        }
 
-       if ( tslots > 0  &&  tslots < 256 ) {
+       if (tslots > 0) {
                size_t tsize = tslots * sizeof(struct cstate);
                comp->tstate = kzalloc(tsize, GFP_KERNEL);
                if (! comp->tstate)
@@ -141,7 +145,7 @@ out_free2:
 out_free:
        kfree(comp);
 out_fail:
-       return NULL;
+       return ERR_PTR(-ENOMEM);
 }
 
 
index 05387b1e2e95e53b1b3abaef034104ea2b26b163..a17d86a5773477fba7059600aa9a9291ef0106b6 100644 (file)
@@ -164,7 +164,7 @@ static int sl_alloc_bufs(struct slip *sl, int mtu)
        if (cbuff == NULL)
                goto err_exit;
        slcomp = slhc_init(16, 16);
-       if (slcomp == NULL)
+       if (IS_ERR(slcomp))
                goto err_exit;
 #endif
        spin_lock_bh(&sl->lock);
index 976aa97042972880679ae61fa6fa9f90f12b51b1..b1878faea3974f6ae984763109edae78c4cd3342 100644 (file)
@@ -858,7 +858,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
        if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
                goto drop;
 
-       if (skb->sk) {
+       if (skb->sk && sk_fullsock(skb->sk)) {
                sock_tx_timestamp(skb->sk, &skb_shinfo(skb)->tx_flags);
                sw_tx_timestamp(skb);
        }
index e66805eeffb45f014a9c61d20e0bd6f3b473098b..7f83504dfa69bba2c8db612d1d25196fa5b72d91 100644 (file)
@@ -109,6 +109,8 @@ config USB_RTL8152
 config USB_LAN78XX
        tristate "Microchip LAN78XX Based USB Ethernet Adapters"
        select MII
+       select PHYLIB
+       select MICROCHIP_PHY
        help
          This option adds support for Microchip LAN78XX based USB 2
          & USB 3 10/100/1000 Ethernet adapters.
@@ -542,7 +544,7 @@ config USB_NET_INT51X1
 
 config USB_CDC_PHONET
        tristate "CDC Phonet support"
-       depends on PHONET
+       depends on PHONET && USB_USBNET
        help
          Choose this option to support the Phonet interface to a Nokia
          cellular modem, as found on most Nokia handsets with the
index 5d049d00c2d752831070bbdbc149bcfaaca2cc1d..a2d3ea6efb20c311d01a3775973c45b8bfd6f7ba 100644 (file)
@@ -168,7 +168,7 @@ struct asix_data {
 struct asix_rx_fixup_info {
        struct sk_buff *ax_skb;
        u32 header;
-       u16 size;
+       u16 remaining;
        bool split_head;
 };
 
index 079069a060a62fdb42cb9dff9acbcd55148277b8..bd9acff1eb7bf277a434345659517683cc0fba45 100644 (file)
@@ -54,71 +54,101 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
                           struct asix_rx_fixup_info *rx)
 {
        int offset = 0;
+       u16 size;
+
+       /* When an Ethernet frame spans multiple URB socket buffers,
+        * do a sanity test for the Data header synchronisation.
+        * Attempt to detect the situation of the previous socket buffer having
+        * been truncated or a socket buffer was missing. These situations
+        * cause a discontinuity in the data stream and therefore need to avoid
+        * appending bad data to the end of the current netdev socket buffer.
+        * Also avoid unnecessarily discarding a good current netdev socket
+        * buffer.
+        */
+       if (rx->remaining && (rx->remaining + sizeof(u32) <= skb->len)) {
+               offset = ((rx->remaining + 1) & 0xfffe) + sizeof(u32);
+               rx->header = get_unaligned_le32(skb->data + offset);
+               offset = 0;
+
+               size = (u16)(rx->header & 0x7ff);
+               if (size != ((~rx->header >> 16) & 0x7ff)) {
+                       netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n",
+                                  rx->remaining);
+                       if (rx->ax_skb) {
+                               kfree_skb(rx->ax_skb);
+                               rx->ax_skb = NULL;
+                               /* Discard the incomplete netdev Ethernet frame
+                                * and assume the Data header is at the start of
+                                * the current URB socket buffer.
+                                */
+                       }
+                       rx->remaining = 0;
+               }
+       }
 
        while (offset + sizeof(u16) <= skb->len) {
-               u16 remaining = 0;
+               u16 copy_length;
                unsigned char *data;
 
-               if (!rx->size) {
-                       if ((skb->len - offset == sizeof(u16)) ||
-                           rx->split_head) {
-                               if(!rx->split_head) {
-                                       rx->header = get_unaligned_le16(
-                                                       skb->data + offset);
-                                       rx->split_head = true;
-                                       offset += sizeof(u16);
-                                       break;
-                               } else {
-                                       rx->header |= (get_unaligned_le16(
-                                                       skb->data + offset)
-                                                       << 16);
-                                       rx->split_head = false;
-                                       offset += sizeof(u16);
-                               }
+               if (!rx->remaining) {
+                       if (skb->len - offset == sizeof(u16)) {
+                               rx->header = get_unaligned_le16(
+                                               skb->data + offset);
+                               rx->split_head = true;
+                               offset += sizeof(u16);
+                               break;
+                       }
+
+                       if (rx->split_head == true) {
+                               rx->header |= (get_unaligned_le16(
+                                               skb->data + offset) << 16);
+                               rx->split_head = false;
+                               offset += sizeof(u16);
                        } else {
                                rx->header = get_unaligned_le32(skb->data +
                                                                offset);
                                offset += sizeof(u32);
                        }
 
-                       /* get the packet length */
-                       rx->size = (u16) (rx->header & 0x7ff);
-                       if (rx->size != ((~rx->header >> 16) & 0x7ff)) {
+                       /* take frame length from Data header 32-bit word */
+                       size = (u16)(rx->header & 0x7ff);
+                       if (size != ((~rx->header >> 16) & 0x7ff)) {
                                netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n",
                                           rx->header, offset);
-                               rx->size = 0;
                                return 0;
                        }
-                       rx->ax_skb = netdev_alloc_skb_ip_align(dev->net,
-                                                              rx->size);
-                       if (!rx->ax_skb) {
-                               rx->size = 0;
+                       if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) {
+                               netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
+                                          size);
                                return 0;
                        }
-               }
 
-               if (rx->size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) {
-                       netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
-                                  rx->size);
-                       kfree_skb(rx->ax_skb);
-                       rx->ax_skb = NULL;
-                       rx->size = 0U;
+                       /* Sometimes may fail to get a netdev socket buffer but
+                        * continue to process the URB socket buffer so that
+                        * synchronisation of the Ethernet frame Data header
+                        * word is maintained.
+                        */
+                       rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, size);
 
-                       return 0;
+                       rx->remaining = size;
                }
 
-               if (rx->size > skb->len - offset) {
-                       remaining = rx->size - (skb->len - offset);
-                       rx->size = skb->len - offset;
+               if (rx->remaining > skb->len - offset) {
+                       copy_length = skb->len - offset;
+                       rx->remaining -= copy_length;
+               } else {
+                       copy_length = rx->remaining;
+                       rx->remaining = 0;
                }
 
-               data = skb_put(rx->ax_skb, rx->size);
-               memcpy(data, skb->data + offset, rx->size);
-               if (!remaining)
-                       usbnet_skb_return(dev, rx->ax_skb);
+               if (rx->ax_skb) {
+                       data = skb_put(rx->ax_skb, copy_length);
+                       memcpy(data, skb->data + offset, copy_length);
+                       if (!rx->remaining)
+                               usbnet_skb_return(dev, rx->ax_skb);
+               }
 
-               offset += (rx->size + 1) & 0xfffe;
-               rx->size = remaining;
+               offset += (copy_length + 1) & 0xfffe;
        }
 
        if (skb->len != offset) {
@@ -558,7 +588,6 @@ void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
        usbnet_get_drvinfo(net, info);
        strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver));
        strlcpy(info->version, DRIVER_VERSION, sizeof(info->version));
-       info->eedump_len = AX_EEPROM_LEN;
 }
 
 int asix_set_mac_address(struct net_device *net, void *p)
index 415ce8b882c615f08d7368cdfa7bec68bef01fa7..ff2270ead2e68b8a381ab42f6309d919f8b9ed12 100644 (file)
@@ -340,32 +340,13 @@ static int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *i
        u8 *data;
        int phonet = 0;
        int len, err;
+       struct usb_cdc_parsed_header hdr;
 
        data = intf->altsetting->extra;
        len = intf->altsetting->extralen;
-       while (len >= 3) {
-               u8 dlen = data[0];
-               if (dlen < 3)
-                       return -EINVAL;
-
-               /* bDescriptorType */
-               if (data[1] == USB_DT_CS_INTERFACE) {
-                       /* bDescriptorSubType */
-                       switch (data[2]) {
-                       case USB_CDC_UNION_TYPE:
-                               if (union_header || dlen < 5)
-                                       break;
-                               union_header =
-                                       (struct usb_cdc_union_desc *)data;
-                               break;
-                       case 0xAB:
-                               phonet = 1;
-                               break;
-                       }
-               }
-               data += dlen;
-               len -= dlen;
-       }
+       cdc_parse_cdc_header(&hdr, intf, data, len);
+       union_header = hdr.usb_cdc_union_desc;
+       phonet = hdr.phonet_magic_present;
 
        if (!union_header || !phonet)
                return -EINVAL;
index 35a2bffe848ad150a3c6bf0e1d66365ae605ac77..c78d3cb1b4648f771a2b5835e9424cd08c74d182 100644 (file)
@@ -112,8 +112,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
        int                             rndis;
        bool                            android_rndis_quirk = false;
        struct usb_driver               *driver = driver_of(intf);
-       struct usb_cdc_mdlm_desc        *desc = NULL;
-       struct usb_cdc_mdlm_detail_desc *detail = NULL;
+       struct usb_cdc_parsed_header header;
 
        if (sizeof(dev->data) < sizeof(*info))
                return -EDOM;
@@ -155,156 +154,89 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
 
        memset(info, 0, sizeof(*info));
        info->control = intf;
-       while (len > 3) {
-               if (buf[1] != USB_DT_CS_INTERFACE)
-                       goto next_desc;
-
-               /* use bDescriptorSubType to identify the CDC descriptors.
-                * We expect devices with CDC header and union descriptors.
-                * For CDC Ethernet we need the ethernet descriptor.
-                * For RNDIS, ignore two (pointless) CDC modem descriptors
-                * in favor of a complicated OID-based RPC scheme doing what
-                * CDC Ethernet achieves with a simple descriptor.
-                */
-               switch (buf[2]) {
-               case USB_CDC_HEADER_TYPE:
-                       if (info->header) {
-                               dev_dbg(&intf->dev, "extra CDC header\n");
-                               goto bad_desc;
-                       }
-                       info->header = (void *) buf;
-                       if (info->header->bLength != sizeof(*info->header)) {
-                               dev_dbg(&intf->dev, "CDC header len %u\n",
-                                       info->header->bLength);
-                               goto bad_desc;
-                       }
-                       break;
-               case USB_CDC_ACM_TYPE:
-                       /* paranoia:  disambiguate a "real" vendor-specific
-                        * modem interface from an RNDIS non-modem.
-                        */
-                       if (rndis) {
-                               struct usb_cdc_acm_descriptor *acm;
-
-                               acm = (void *) buf;
-                               if (acm->bmCapabilities) {
-                                       dev_dbg(&intf->dev,
-                                               "ACM capabilities %02x, "
-                                               "not really RNDIS?\n",
-                                               acm->bmCapabilities);
-                                       goto bad_desc;
-                               }
-                       }
-                       break;
-               case USB_CDC_UNION_TYPE:
-                       if (info->u) {
-                               dev_dbg(&intf->dev, "extra CDC union\n");
-                               goto bad_desc;
-                       }
-                       info->u = (void *) buf;
-                       if (info->u->bLength != sizeof(*info->u)) {
-                               dev_dbg(&intf->dev, "CDC union len %u\n",
-                                       info->u->bLength);
-                               goto bad_desc;
-                       }
-
-                       /* we need a master/control interface (what we're
-                        * probed with) and a slave/data interface; union
-                        * descriptors sort this all out.
-                        */
-                       info->control = usb_ifnum_to_if(dev->udev,
-                                               info->u->bMasterInterface0);
-                       info->data = usb_ifnum_to_if(dev->udev,
-                                               info->u->bSlaveInterface0);
-                       if (!info->control || !info->data) {
-                               dev_dbg(&intf->dev,
-                                       "master #%u/%p slave #%u/%p\n",
-                                       info->u->bMasterInterface0,
-                                       info->control,
-                                       info->u->bSlaveInterface0,
-                                       info->data);
-                               /* fall back to hard-wiring for RNDIS */
-                               if (rndis) {
-                                       android_rndis_quirk = true;
-                                       goto next_desc;
-                               }
-                               goto bad_desc;
-                       }
-                       if (info->control != intf) {
-                               dev_dbg(&intf->dev, "bogus CDC Union\n");
-                               /* Ambit USB Cable Modem (and maybe others)
-                                * interchanges master and slave interface.
-                                */
-                               if (info->data == intf) {
-                                       info->data = info->control;
-                                       info->control = intf;
-                               } else
-                                       goto bad_desc;
-                       }
-
-                       /* some devices merge these - skip class check */
-                       if (info->control == info->data)
-                               goto next_desc;
-
-                       /* a data interface altsetting does the real i/o */
-                       d = &info->data->cur_altsetting->desc;
-                       if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
-                               dev_dbg(&intf->dev, "slave class %u\n",
-                                       d->bInterfaceClass);
-                               goto bad_desc;
-                       }
-                       break;
-               case USB_CDC_ETHERNET_TYPE:
-                       if (info->ether) {
-                               dev_dbg(&intf->dev, "extra CDC ether\n");
-                               goto bad_desc;
-                       }
-                       info->ether = (void *) buf;
-                       if (info->ether->bLength != sizeof(*info->ether)) {
-                               dev_dbg(&intf->dev, "CDC ether len %u\n",
-                                       info->ether->bLength);
-                               goto bad_desc;
-                       }
-                       dev->hard_mtu = le16_to_cpu(
-                                               info->ether->wMaxSegmentSize);
-                       /* because of Zaurus, we may be ignoring the host
-                        * side link address we were given.
-                        */
-                       break;
-               case USB_CDC_MDLM_TYPE:
-                       if (desc) {
-                               dev_dbg(&intf->dev, "extra MDLM descriptor\n");
-                               goto bad_desc;
-                       }
-
-                       desc = (void *)buf;
-
-                       if (desc->bLength != sizeof(*desc))
-                               goto bad_desc;
-
-                       if (memcmp(&desc->bGUID, mbm_guid, 16))
-                               goto bad_desc;
-                       break;
-               case USB_CDC_MDLM_DETAIL_TYPE:
-                       if (detail) {
-                               dev_dbg(&intf->dev, "extra MDLM detail descriptor\n");
-                               goto bad_desc;
-                       }
-
-                       detail = (void *)buf;
-
-                       if (detail->bGuidDescriptorType == 0) {
-                               if (detail->bLength < (sizeof(*detail) + 1))
-                                       goto bad_desc;
-                       } else
-                               goto bad_desc;
-                       break;
+
+       cdc_parse_cdc_header(&header, intf, buf, len);
+
+       info->u = header.usb_cdc_union_desc;
+       info->header = header.usb_cdc_header_desc;
+       info->ether = header.usb_cdc_ether_desc;
+       /* we need a master/control interface (what we're
+        * probed with) and a slave/data interface; union
+        * descriptors sort this all out.
+        */
+       info->control = usb_ifnum_to_if(dev->udev,
+       info->u->bMasterInterface0);
+       info->data = usb_ifnum_to_if(dev->udev,
+               info->u->bSlaveInterface0);
+       if (!info->control || !info->data) {
+               dev_dbg(&intf->dev,
+                       "master #%u/%p slave #%u/%p\n",
+                       info->u->bMasterInterface0,
+                       info->control,
+                       info->u->bSlaveInterface0,
+                       info->data);
+               /* fall back to hard-wiring for RNDIS */
+               if (rndis) {
+                       android_rndis_quirk = true;
+                       goto skip;
                }
-next_desc:
-               len -= buf[0];  /* bLength */
-               buf += buf[0];
+               goto bad_desc;
+       }
+       if (info->control != intf) {
+               dev_dbg(&intf->dev, "bogus CDC Union\n");
+               /* Ambit USB Cable Modem (and maybe others)
+                * interchanges master and slave interface.
+                */
+               if (info->data == intf) {
+                       info->data = info->control;
+                       info->control = intf;
+               } else
+                       goto bad_desc;
+       }
+
+       /* some devices merge these - skip class check */
+       if (info->control == info->data)
+               goto skip;
+
+       /* a data interface altsetting does the real i/o */
+       d = &info->data->cur_altsetting->desc;
+       if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
+               dev_dbg(&intf->dev, "slave class %u\n",
+                       d->bInterfaceClass);
+               goto bad_desc;
+       }
+skip:
+       if (    rndis &&
+               header.usb_cdc_acm_descriptor &&
+               header.usb_cdc_acm_descriptor->bmCapabilities) {
+                       dev_dbg(&intf->dev,
+                               "ACM capabilities %02x, not really RNDIS?\n",
+                               header.usb_cdc_acm_descriptor->bmCapabilities);
+                       goto bad_desc;
        }
 
+       if (header.usb_cdc_ether_desc) {
+               dev->hard_mtu = le16_to_cpu(info->ether->wMaxSegmentSize);
+               /* because of Zaurus, we may be ignoring the host
+                * side link address we were given.
+                */
+       }
+
+       if (header.usb_cdc_mdlm_desc &&
+               memcmp(header.usb_cdc_mdlm_desc->bGUID, mbm_guid, 16)) {
+               dev_dbg(&intf->dev, "GUID doesn't match\n");
+               goto bad_desc;
+       }
+
+       if (header.usb_cdc_mdlm_detail_desc &&
+               header.usb_cdc_mdlm_detail_desc->bLength <
+                       (sizeof(struct usb_cdc_mdlm_detail_desc) + 1)) {
+               dev_dbg(&intf->dev, "Descriptor too short\n");
+               goto bad_desc;
+       }
+
+
+
        /* Microsoft ActiveSync based and some regular RNDIS devices lack the
         * CDC descriptors, so we'll hard-wire the interfaces and not check
         * for descriptors.
index efc18e05af0aed216144e91b7a835342a1b30a59..bbde9884ab8ae7fb952cbf6583feff10c9ce1109 100644 (file)
@@ -342,7 +342,7 @@ static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci)
        in6_dev_put(in6_dev);
 
        /* ipv6_stub != NULL if in6_dev_get returned an inet6_dev */
-       ipv6_stub->ndisc_send_na(netdev, NULL, &iph->saddr, &msg->target,
+       ipv6_stub->ndisc_send_na(netdev, &iph->saddr, &msg->target,
                                 is_router /* router */,
                                 true /* solicited */,
                                 false /* override */,
index db40175b1a0b081dfcf5c8eb829d935618324d10..a187f08113ecbb77612dd11a2611d4649d79c47d 100644 (file)
@@ -698,6 +698,7 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
        int len;
        int temp;
        u8 iface_no;
+       struct usb_cdc_parsed_header hdr;
 
        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
        if (!ctx)
@@ -722,66 +723,14 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
        len = intf->cur_altsetting->extralen;
 
        /* parse through descriptors associated with control interface */
-       while ((len > 0) && (buf[0] > 2) && (buf[0] <= len)) {
-
-               if (buf[1] != USB_DT_CS_INTERFACE)
-                       goto advance;
-
-               switch (buf[2]) {
-               case USB_CDC_UNION_TYPE:
-                       if (buf[0] < sizeof(*union_desc))
-                               break;
-
-                       union_desc = (const struct usb_cdc_union_desc *)buf;
-                       /* the master must be the interface we are probing */
-                       if (intf->cur_altsetting->desc.bInterfaceNumber !=
-                           union_desc->bMasterInterface0) {
-                               dev_dbg(&intf->dev, "bogus CDC Union\n");
-                               goto error;
-                       }
-                       ctx->data = usb_ifnum_to_if(dev->udev,
-                                                   union_desc->bSlaveInterface0);
-                       break;
-
-               case USB_CDC_ETHERNET_TYPE:
-                       if (buf[0] < sizeof(*(ctx->ether_desc)))
-                               break;
-
-                       ctx->ether_desc =
-                                       (const struct usb_cdc_ether_desc *)buf;
-                       break;
-
-               case USB_CDC_NCM_TYPE:
-                       if (buf[0] < sizeof(*(ctx->func_desc)))
-                               break;
-
-                       ctx->func_desc = (const struct usb_cdc_ncm_desc *)buf;
-                       break;
-
-               case USB_CDC_MBIM_TYPE:
-                       if (buf[0] < sizeof(*(ctx->mbim_desc)))
-                               break;
-
-                       ctx->mbim_desc = (const struct usb_cdc_mbim_desc *)buf;
-                       break;
-
-               case USB_CDC_MBIM_EXTENDED_TYPE:
-                       if (buf[0] < sizeof(*(ctx->mbim_extended_desc)))
-                               break;
-
-                       ctx->mbim_extended_desc =
-                               (const struct usb_cdc_mbim_extended_desc *)buf;
-                       break;
-
-               default:
-                       break;
-               }
-advance:
-               /* advance to next descriptor */
-               temp = buf[0];
-               buf += temp;
-               len -= temp;
-       }
+       cdc_parse_cdc_header(&hdr, intf, buf, len);
+
+       ctx->data = usb_ifnum_to_if(dev->udev,
+                                   hdr.usb_cdc_union_desc->bSlaveInterface0);
+       ctx->ether_desc = hdr.usb_cdc_ether_desc;
+       ctx->func_desc = hdr.usb_cdc_ncm_desc;
+       ctx->mbim_desc = hdr.usb_cdc_mbim_desc;
+       ctx->mbim_extended_desc = hdr.usb_cdc_mbim_extended_desc;
 
        /* some buggy devices have an IAD but no CDC Union */
        if (!union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) {
index 6e9c344c7a201bc18fcfe1f3f1e67d01a4250d01..0b4bdd39106b0a73e954070a42ac87be22ac1821 100644 (file)
@@ -258,7 +258,6 @@ static void dm9601_get_drvinfo(struct net_device *net,
 {
        /* Inherit standard device info */
        usbnet_get_drvinfo(net, info);
-       info->eedump_len = DM_EEPROM_LEN;
 }
 
 static u32 dm9601_get_link(struct net_device *net)
index a39518fc93aadf82918fd944dcf88c6de5321f48..226668ead0d8c1ba9e6a0edf906a2122a5e74d2e 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
-#include <linux/mii.h>
 #include <linux/usb.h>
 #include <linux/crc32.h>
 #include <linux/signal.h>
 #include <linux/ipv6.h>
 #include <linux/mdio.h>
 #include <net/ip6_checksum.h>
+#include <linux/microchipphy.h>
 #include "lan78xx.h"
 
 #define DRIVER_AUTHOR  "WOOJUNG HUH <woojung.huh@microchip.com>"
 #define DRIVER_DESC    "LAN78XX USB 3.0 Gigabit Ethernet Devices"
 #define DRIVER_NAME    "lan78xx"
-#define DRIVER_VERSION "1.0.0"
+#define DRIVER_VERSION "1.0.1"
 
 #define TX_TIMEOUT_JIFFIES             (5 * HZ)
 #define THROTTLE_JIFFIES               (HZ / 8)
@@ -57,7 +57,6 @@
 #define DEFAULT_RX_CSUM_ENABLE         (true)
 #define DEFAULT_TSO_CSUM_ENABLE                (true)
 #define DEFAULT_VLAN_FILTER_ENABLE     (true)
-#define INTERNAL_PHY_ID                        (2)     /* 2: GMII */
 #define TX_OVERHEAD                    (8)
 #define RXW_PADDING                    2
 
@@ -275,10 +274,12 @@ struct lan78xx_net {
        struct timer_list       delay;
 
        unsigned long           data[5];
-       struct mii_if_info      mii;
 
        int                     link_on;
        u8                      mdix_ctrl;
+
+       u32                     devid;
+       struct mii_bus          *mdiobus;
 };
 
 /* use ethtool to change the level for any given device */
@@ -411,222 +412,6 @@ static inline u32 mii_access(int id, int index, int read)
        return ret;
 }
 
-static int lan78xx_mdio_read(struct net_device *netdev, int phy_id, int idx)
-{
-       struct lan78xx_net *dev = netdev_priv(netdev);
-       u32 val, addr;
-       int ret;
-
-       ret = usb_autopm_get_interface(dev->intf);
-       if (ret < 0)
-               return ret;
-
-       mutex_lock(&dev->phy_mutex);
-
-       /* confirm MII not busy */
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* set the address, index & direction (read from PHY) */
-       phy_id &= dev->mii.phy_id_mask;
-       idx &= dev->mii.reg_num_mask;
-       addr = mii_access(phy_id, idx, MII_READ);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       ret = lan78xx_read_reg(dev, MII_DATA, &val);
-
-       ret = (int)(val & 0xFFFF);
-
-done:
-       mutex_unlock(&dev->phy_mutex);
-       usb_autopm_put_interface(dev->intf);
-       return ret;
-}
-
-static void lan78xx_mdio_write(struct net_device *netdev, int phy_id,
-                              int idx, int regval)
-{
-       struct lan78xx_net *dev = netdev_priv(netdev);
-       u32 val, addr;
-       int ret;
-
-       if (usb_autopm_get_interface(dev->intf) < 0)
-               return;
-
-       mutex_lock(&dev->phy_mutex);
-
-       /* confirm MII not busy */
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       val = regval;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       /* set the address, index & direction (write to PHY) */
-       phy_id &= dev->mii.phy_id_mask;
-       idx &= dev->mii.reg_num_mask;
-       addr = mii_access(phy_id, idx, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-done:
-       mutex_unlock(&dev->phy_mutex);
-       usb_autopm_put_interface(dev->intf);
-}
-
-static void lan78xx_mmd_write(struct net_device *netdev, int phy_id,
-                             int mmddev, int mmdidx, int regval)
-{
-       struct lan78xx_net *dev = netdev_priv(netdev);
-       u32 val, addr;
-       int ret;
-
-       if (usb_autopm_get_interface(dev->intf) < 0)
-               return;
-
-       mutex_lock(&dev->phy_mutex);
-
-       /* confirm MII not busy */
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       mmddev &= 0x1F;
-
-       /* set up device address for MMD */
-       ret = lan78xx_write_reg(dev, MII_DATA, mmddev);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* select register of MMD */
-       val = mmdidx;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* select register data for MMD */
-       val = PHY_MMD_CTRL_OP_DNI_ | mmddev;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* write to MMD */
-       val = regval;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-done:
-       mutex_unlock(&dev->phy_mutex);
-       usb_autopm_put_interface(dev->intf);
-}
-
-static int lan78xx_mmd_read(struct net_device *netdev, int phy_id,
-                           int mmddev, int mmdidx)
-{
-       struct lan78xx_net *dev = netdev_priv(netdev);
-       u32 val, addr;
-       int ret;
-
-       ret = usb_autopm_get_interface(dev->intf);
-       if (ret < 0)
-               return ret;
-
-       mutex_lock(&dev->phy_mutex);
-
-       /* confirm MII not busy */
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* set up device address for MMD */
-       ret = lan78xx_write_reg(dev, MII_DATA, mmddev);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* select register of MMD */
-       val = mmdidx;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* select register data for MMD */
-       val = PHY_MMD_CTRL_OP_DNI_ | mmddev;
-       ret = lan78xx_write_reg(dev, MII_DATA, val);
-
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* set the address, index & direction (read from PHY) */
-       phy_id &= dev->mii.phy_id_mask;
-       addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_READ);
-       ret = lan78xx_write_reg(dev, MII_ACC, addr);
-
-       ret = lan78xx_phy_wait_not_busy(dev);
-       if (ret < 0)
-               goto done;
-
-       /* read from MMD */
-       ret = lan78xx_read_reg(dev, MII_DATA, &val);
-
-       ret = (int)(val & 0xFFFF);
-
-done:
-       mutex_unlock(&dev->phy_mutex);
-       usb_autopm_put_interface(dev->intf);
-       return ret;
-}
-
 static int lan78xx_wait_eeprom(struct lan78xx_net *dev)
 {
        unsigned long start_time = jiffies;
@@ -1047,14 +832,13 @@ static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex,
 
 static int lan78xx_link_reset(struct lan78xx_net *dev)
 {
-       struct mii_if_info *mii = &dev->mii;
+       struct phy_device *phydev = dev->net->phydev;
        struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
        int ladv, radv, ret;
        u32 buf;
 
        /* clear PHY interrupt status */
-       /* VTSE PHY */
-       ret = lan78xx_mdio_read(dev->net, mii->phy_id, PHY_VTSE_INT_STS);
+       ret = phy_read(phydev, LAN88XX_INT_STS);
        if (unlikely(ret < 0))
                return -EIO;
 
@@ -1063,7 +847,9 @@ static int lan78xx_link_reset(struct lan78xx_net *dev)
        if (unlikely(ret < 0))
                return -EIO;
 
-       if (!mii_link_ok(mii) && dev->link_on) {
+       phy_read_status(phydev);
+
+       if (!phydev->link && dev->link_on) {
                dev->link_on = false;
                netif_carrier_off(dev->net);
 
@@ -1075,13 +861,12 @@ static int lan78xx_link_reset(struct lan78xx_net *dev)
                ret = lan78xx_write_reg(dev, MAC_CR, buf);
                if (unlikely(ret < 0))
                        return -EIO;
-       } else if (mii_link_ok(mii) && !dev->link_on) {
+       } else if (phydev->link && !dev->link_on) {
                dev->link_on = true;
 
-               mii_check_media(mii, 1, 1);
-               mii_ethtool_gset(&dev->mii, &ecmd);
+               phy_ethtool_gset(phydev, &ecmd);
 
-               mii->mdio_read(mii->dev, mii->phy_id, PHY_VTSE_INT_STS);
+               ret = phy_read(phydev, LAN88XX_INT_STS);
 
                if (dev->udev->speed == USB_SPEED_SUPER) {
                        if (ethtool_cmd_speed(&ecmd) == 1000) {
@@ -1102,11 +887,11 @@ static int lan78xx_link_reset(struct lan78xx_net *dev)
                        }
                }
 
-               ladv = lan78xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE);
+               ladv = phy_read(phydev, MII_ADVERTISE);
                if (ladv < 0)
                        return ladv;
 
-               radv = lan78xx_mdio_read(dev->net, mii->phy_id, MII_LPA);
+               radv = phy_read(phydev, MII_LPA);
                if (radv < 0)
                        return radv;
 
@@ -1279,6 +1064,8 @@ static int lan78xx_set_wol(struct net_device *netdev,
 
        device_set_wakeup_enable(&dev->udev->dev, (bool)wol->wolopts);
 
+       phy_ethtool_set_wol(netdev->phydev, wol);
+
        usb_autopm_put_interface(dev->intf);
 
        return ret;
@@ -1287,49 +1074,39 @@ static int lan78xx_set_wol(struct net_device *netdev,
 static int lan78xx_get_eee(struct net_device *net, struct ethtool_eee *edata)
 {
        struct lan78xx_net *dev = netdev_priv(net);
+       struct phy_device *phydev = net->phydev;
        int ret;
        u32 buf;
-       u32 adv, lpadv;
 
        ret = usb_autopm_get_interface(dev->intf);
        if (ret < 0)
                return ret;
 
+       ret = phy_ethtool_get_eee(phydev, edata);
+       if (ret < 0)
+               goto exit;
+
        ret = lan78xx_read_reg(dev, MAC_CR, &buf);
        if (buf & MAC_CR_EEE_EN_) {
-               buf = lan78xx_mmd_read(dev->net, dev->mii.phy_id,
-                                      PHY_MMD_DEV_7, PHY_EEE_ADVERTISEMENT);
-               adv = mmd_eee_adv_to_ethtool_adv_t(buf);
-               buf = lan78xx_mmd_read(dev->net, dev->mii.phy_id,
-                                      PHY_MMD_DEV_7, PHY_EEE_LP_ADVERTISEMENT);
-               lpadv = mmd_eee_adv_to_ethtool_adv_t(buf);
-
                edata->eee_enabled = true;
-               edata->supported = true;
-               edata->eee_active = !!(adv & lpadv);
-               edata->advertised = adv;
-               edata->lp_advertised = lpadv;
+               edata->eee_active = !!(edata->advertised &
+                                      edata->lp_advertised);
                edata->tx_lpi_enabled = true;
                /* EEE_TX_LPI_REQ_DLY & tx_lpi_timer are same uSec unit */
                ret = lan78xx_read_reg(dev, EEE_TX_LPI_REQ_DLY, &buf);
                edata->tx_lpi_timer = buf;
        } else {
-               buf = lan78xx_mmd_read(dev->net, dev->mii.phy_id,
-                                      PHY_MMD_DEV_7, PHY_EEE_LP_ADVERTISEMENT);
-               lpadv = mmd_eee_adv_to_ethtool_adv_t(buf);
-
                edata->eee_enabled = false;
                edata->eee_active = false;
-               edata->supported = false;
-               edata->advertised = 0;
-               edata->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(lpadv);
                edata->tx_lpi_enabled = false;
                edata->tx_lpi_timer = 0;
        }
 
+       ret = 0;
+exit:
        usb_autopm_put_interface(dev->intf);
 
-       return 0;
+       return ret;
 }
 
 static int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata)
@@ -1347,9 +1124,10 @@ static int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata)
                buf |= MAC_CR_EEE_EN_;
                ret = lan78xx_write_reg(dev, MAC_CR, buf);
 
-               buf = ethtool_adv_to_mmd_eee_adv_t(edata->advertised);
-               lan78xx_mmd_write(dev->net, dev->mii.phy_id,
-                                 PHY_MMD_DEV_7, PHY_EEE_ADVERTISEMENT, buf);
+               phy_ethtool_set_eee(net->phydev, edata);
+
+               buf = (u32)edata->tx_lpi_timer;
+               ret = lan78xx_write_reg(dev, EEE_TX_LPI_REQ_DLY, buf);
        } else {
                ret = lan78xx_read_reg(dev, MAC_CR, &buf);
                buf &= ~MAC_CR_EEE_EN_;
@@ -1363,19 +1141,14 @@ static int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata)
 
 static u32 lan78xx_get_link(struct net_device *net)
 {
-       struct lan78xx_net *dev = netdev_priv(net);
+       phy_read_status(net->phydev);
 
-       return mii_link_ok(&dev->mii);
+       return net->phydev->link;
 }
 
 int lan78xx_nway_reset(struct net_device *net)
 {
-       struct lan78xx_net *dev = netdev_priv(net);
-
-       if ((!dev->mii.mdio_read) || (!dev->mii.mdio_write))
-               return -EOPNOTSUPP;
-
-       return mii_nway_restart(&dev->mii);
+       return phy_start_aneg(net->phydev);
 }
 
 static void lan78xx_get_drvinfo(struct net_device *net,
@@ -1402,36 +1175,78 @@ static void lan78xx_set_msglevel(struct net_device *net, u32 level)
        dev->msg_enable = level;
 }
 
+static int lan78xx_get_mdix_status(struct net_device *net)
+{
+       struct phy_device *phydev = net->phydev;
+       int buf;
+
+       phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_1);
+       buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL);
+       phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_0);
+
+       return buf;
+}
+
+static void lan78xx_set_mdix_status(struct net_device *net, __u8 mdix_ctrl)
+{
+       struct lan78xx_net *dev = netdev_priv(net);
+       struct phy_device *phydev = net->phydev;
+       int buf;
+
+       if (mdix_ctrl == ETH_TP_MDI) {
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_1);
+               buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL);
+               buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_;
+               phy_write(phydev, LAN88XX_EXT_MODE_CTRL,
+                         buf | LAN88XX_EXT_MODE_CTRL_MDI_);
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_0);
+       } else if (mdix_ctrl == ETH_TP_MDI_X) {
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_1);
+               buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL);
+               buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_;
+               phy_write(phydev, LAN88XX_EXT_MODE_CTRL,
+                         buf | LAN88XX_EXT_MODE_CTRL_MDI_X_);
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_0);
+       } else if (mdix_ctrl == ETH_TP_MDI_AUTO) {
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_1);
+               buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL);
+               buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_;
+               phy_write(phydev, LAN88XX_EXT_MODE_CTRL,
+                         buf | LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_);
+               phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS,
+                         LAN88XX_EXT_PAGE_SPACE_0);
+       }
+       dev->mdix_ctrl = mdix_ctrl;
+}
+
 static int lan78xx_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
 {
        struct lan78xx_net *dev = netdev_priv(net);
-       struct mii_if_info *mii = &dev->mii;
+       struct phy_device *phydev = net->phydev;
        int ret;
        int buf;
 
-       if ((!dev->mii.mdio_read) || (!dev->mii.mdio_write))
-               return -EOPNOTSUPP;
-
        ret = usb_autopm_get_interface(dev->intf);
        if (ret < 0)
                return ret;
 
-       ret = mii_ethtool_gset(&dev->mii, cmd);
+       ret = phy_ethtool_gset(phydev, cmd);
 
-       mii->mdio_write(mii->dev, mii->phy_id,
-                       PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_1);
-       buf = mii->mdio_read(mii->dev, mii->phy_id, PHY_EXT_MODE_CTRL);
-       mii->mdio_write(mii->dev, mii->phy_id,
-                       PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_0);
+       buf = lan78xx_get_mdix_status(net);
 
-       buf &= PHY_EXT_MODE_CTRL_MDIX_MASK_;
-       if (buf == PHY_EXT_MODE_CTRL_AUTO_MDIX_) {
+       buf &= LAN88XX_EXT_MODE_CTRL_MDIX_MASK_;
+       if (buf == LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_) {
                cmd->eth_tp_mdix = ETH_TP_MDI_AUTO;
                cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO;
-       } else if (buf == PHY_EXT_MODE_CTRL_MDI_) {
+       } else if (buf == LAN88XX_EXT_MODE_CTRL_MDI_) {
                cmd->eth_tp_mdix = ETH_TP_MDI;
                cmd->eth_tp_mdix_ctrl = ETH_TP_MDI;
-       } else if (buf == PHY_EXT_MODE_CTRL_MDI_X_) {
+       } else if (buf == LAN88XX_EXT_MODE_CTRL_MDI_X_) {
                cmd->eth_tp_mdix = ETH_TP_MDI_X;
                cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_X;
        }
@@ -1444,70 +1259,27 @@ static int lan78xx_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
 static int lan78xx_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
 {
        struct lan78xx_net *dev = netdev_priv(net);
-       struct mii_if_info *mii = &dev->mii;
+       struct phy_device *phydev = net->phydev;
        int ret = 0;
        int temp;
 
-       if ((!dev->mii.mdio_read) || (!dev->mii.mdio_write))
-               return -EOPNOTSUPP;
-
        ret = usb_autopm_get_interface(dev->intf);
        if (ret < 0)
                return ret;
 
        if (dev->mdix_ctrl != cmd->eth_tp_mdix_ctrl) {
-               if (cmd->eth_tp_mdix_ctrl == ETH_TP_MDI) {
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_1);
-                       temp = mii->mdio_read(mii->dev, mii->phy_id,
-                                       PHY_EXT_MODE_CTRL);
-                       temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_;
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_MODE_CTRL,
-                                       temp | PHY_EXT_MODE_CTRL_MDI_);
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_0);
-               } else if (cmd->eth_tp_mdix_ctrl == ETH_TP_MDI_X) {
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_1);
-                       temp = mii->mdio_read(mii->dev, mii->phy_id,
-                                       PHY_EXT_MODE_CTRL);
-                       temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_;
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_MODE_CTRL,
-                                       temp | PHY_EXT_MODE_CTRL_MDI_X_);
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_0);
-               } else if (cmd->eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) {
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_1);
-                       temp = mii->mdio_read(mii->dev, mii->phy_id,
-                                                       PHY_EXT_MODE_CTRL);
-                       temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_;
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_MODE_CTRL,
-                                       temp | PHY_EXT_MODE_CTRL_AUTO_MDIX_);
-                       mii->mdio_write(mii->dev, mii->phy_id,
-                                       PHY_EXT_GPIO_PAGE,
-                                       PHY_EXT_GPIO_PAGE_SPACE_0);
-               }
+               lan78xx_set_mdix_status(net, cmd->eth_tp_mdix_ctrl);
        }
 
        /* change speed & duplex */
-       ret = mii_ethtool_sset(&dev->mii, cmd);
+       ret = phy_ethtool_sset(phydev, cmd);
 
        if (!cmd->autoneg) {
                /* force link down */
-               temp = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
-               mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR,
-                               temp | BMCR_LOOPBACK);
+               temp = phy_read(phydev, MII_BMCR);
+               phy_write(phydev, MII_BMCR, temp | BMCR_LOOPBACK);
                mdelay(1);
-               mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, temp);
+               phy_write(phydev, MII_BMCR, temp);
        }
 
        usb_autopm_put_interface(dev->intf);
@@ -1537,12 +1309,10 @@ static const struct ethtool_ops lan78xx_ethtool_ops = {
 
 static int lan78xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
 {
-       struct lan78xx_net *dev = netdev_priv(netdev);
-
        if (!netif_running(netdev))
                return -EINVAL;
 
-       return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+       return phy_mii_ioctl(netdev->phydev, rq, cmd);
 }
 
 static void lan78xx_init_mac_address(struct lan78xx_net *dev)
@@ -1598,53 +1368,183 @@ static void lan78xx_init_mac_address(struct lan78xx_net *dev)
        ether_addr_copy(dev->net->dev_addr, addr);
 }
 
-static void lan78xx_mii_init(struct lan78xx_net *dev)
+/* MDIO read and write wrappers for phylib */
+static int lan78xx_mdiobus_read(struct mii_bus *bus, int phy_id, int idx)
 {
-       /* Initialize MII structure */
-       dev->mii.dev = dev->net;
-       dev->mii.mdio_read = lan78xx_mdio_read;
-       dev->mii.mdio_write = lan78xx_mdio_write;
-       dev->mii.phy_id_mask = 0x1f;
-       dev->mii.reg_num_mask = 0x1f;
-       dev->mii.phy_id = INTERNAL_PHY_ID;
-       dev->mii.supports_gmii = true;
+       struct lan78xx_net *dev = bus->priv;
+       u32 val, addr;
+       int ret;
+
+       ret = usb_autopm_get_interface(dev->intf);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&dev->phy_mutex);
+
+       /* confirm MII not busy */
+       ret = lan78xx_phy_wait_not_busy(dev);
+       if (ret < 0)
+               goto done;
+
+       /* set the address, index & direction (read from PHY) */
+       addr = mii_access(phy_id, idx, MII_READ);
+       ret = lan78xx_write_reg(dev, MII_ACC, addr);
+
+       ret = lan78xx_phy_wait_not_busy(dev);
+       if (ret < 0)
+               goto done;
+
+       ret = lan78xx_read_reg(dev, MII_DATA, &val);
+
+       ret = (int)(val & 0xFFFF);
+
+done:
+       mutex_unlock(&dev->phy_mutex);
+       usb_autopm_put_interface(dev->intf);
+       return ret;
+}
+
+static int lan78xx_mdiobus_write(struct mii_bus *bus, int phy_id, int idx,
+                                u16 regval)
+{
+       struct lan78xx_net *dev = bus->priv;
+       u32 val, addr;
+       int ret;
+
+       ret = usb_autopm_get_interface(dev->intf);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&dev->phy_mutex);
+
+       /* confirm MII not busy */
+       ret = lan78xx_phy_wait_not_busy(dev);
+       if (ret < 0)
+               goto done;
+
+       val = (u32)regval;
+       ret = lan78xx_write_reg(dev, MII_DATA, val);
+
+       /* set the address, index & direction (write to PHY) */
+       addr = mii_access(phy_id, idx, MII_WRITE);
+       ret = lan78xx_write_reg(dev, MII_ACC, addr);
+
+       ret = lan78xx_phy_wait_not_busy(dev);
+       if (ret < 0)
+               goto done;
+
+done:
+       mutex_unlock(&dev->phy_mutex);
+       usb_autopm_put_interface(dev->intf);
+       return 0;
+}
+
+static int lan78xx_mdio_init(struct lan78xx_net *dev)
+{
+       int ret;
+       int i;
+
+       dev->mdiobus = mdiobus_alloc();
+       if (!dev->mdiobus) {
+               netdev_err(dev->net, "can't allocate MDIO bus\n");
+               return -ENOMEM;
+       }
+
+       dev->mdiobus->priv = (void *)dev;
+       dev->mdiobus->read = lan78xx_mdiobus_read;
+       dev->mdiobus->write = lan78xx_mdiobus_write;
+       dev->mdiobus->name = "lan78xx-mdiobus";
+
+       snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
+                dev->udev->bus->busnum, dev->udev->devnum);
+
+       dev->mdiobus->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+       if (!dev->mdiobus->irq) {
+               ret = -ENOMEM;
+               goto exit1;
+       }
+
+       /* handle our own interrupt */
+       for (i = 0; i < PHY_MAX_ADDR; i++)
+               dev->mdiobus->irq[i] = PHY_IGNORE_INTERRUPT;
+
+       switch (dev->devid & ID_REV_CHIP_ID_MASK_) {
+       case 0x78000000:
+       case 0x78500000:
+               /* set to internal PHY id */
+               dev->mdiobus->phy_mask = ~(1 << 1);
+               break;
+       }
+
+       ret = mdiobus_register(dev->mdiobus);
+       if (ret) {
+               netdev_err(dev->net, "can't register MDIO bus\n");
+               goto exit2;
+       }
+
+       netdev_dbg(dev->net, "registered mdiobus bus %s\n", dev->mdiobus->id);
+       return 0;
+exit2:
+       kfree(dev->mdiobus->irq);
+exit1:
+       mdiobus_free(dev->mdiobus);
+       return ret;
+}
+
+static void lan78xx_remove_mdio(struct lan78xx_net *dev)
+{
+       mdiobus_unregister(dev->mdiobus);
+       kfree(dev->mdiobus->irq);
+       mdiobus_free(dev->mdiobus);
+}
+
+static void lan78xx_link_status_change(struct net_device *net)
+{
+       /* nothing to do */
 }
 
 static int lan78xx_phy_init(struct lan78xx_net *dev)
 {
-       int temp;
-       struct mii_if_info *mii = &dev->mii;
+       int ret;
+       struct phy_device *phydev = dev->net->phydev;
 
-       if ((!mii->mdio_write) || (!mii->mdio_read))
-               return -EOPNOTSUPP;
+       phydev = phy_find_first(dev->mdiobus);
+       if (!phydev) {
+               netdev_err(dev->net, "no PHY found\n");
+               return -EIO;
+       }
 
-       temp = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
-       temp |= ADVERTISE_ALL;
-       mii->mdio_write(mii->dev, mii->phy_id, MII_ADVERTISE,
-                       temp | ADVERTISE_CSMA |
-                       ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+       ret = phy_connect_direct(dev->net, phydev,
+                                lan78xx_link_status_change,
+                                PHY_INTERFACE_MODE_GMII);
+       if (ret) {
+               netdev_err(dev->net, "can't attach PHY to %s\n",
+                          dev->mdiobus->id);
+               return -EIO;
+       }
 
        /* set to AUTOMDIX */
-       mii->mdio_write(mii->dev, mii->phy_id,
-                       PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_1);
-       temp = mii->mdio_read(mii->dev, mii->phy_id, PHY_EXT_MODE_CTRL);
-       temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_;
-       mii->mdio_write(mii->dev, mii->phy_id, PHY_EXT_MODE_CTRL,
-                       temp | PHY_EXT_MODE_CTRL_AUTO_MDIX_);
-       mii->mdio_write(mii->dev, mii->phy_id,
-                       PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_0);
-       dev->mdix_ctrl = ETH_TP_MDI_AUTO;
-
-       /* MAC doesn't support 1000HD */
-       temp = mii->mdio_read(mii->dev, mii->phy_id, MII_CTRL1000);
-       mii->mdio_write(mii->dev, mii->phy_id, MII_CTRL1000,
-                       temp & ~ADVERTISE_1000HALF);
-
-       /* clear interrupt */
-       mii->mdio_read(mii->dev, mii->phy_id, PHY_VTSE_INT_STS);
-       mii->mdio_write(mii->dev, mii->phy_id, PHY_VTSE_INT_MASK,
-                       PHY_VTSE_INT_MASK_MDINTPIN_EN_ |
-                       PHY_VTSE_INT_MASK_LINK_CHANGE_);
+       lan78xx_set_mdix_status(dev->net, ETH_TP_MDI_AUTO);
+
+       /* MAC doesn't support 1000T Half */
+       phydev->supported &= ~SUPPORTED_1000baseT_Half;
+       phydev->supported |= (SUPPORTED_10baseT_Half |
+                             SUPPORTED_10baseT_Full |
+                             SUPPORTED_100baseT_Half |
+                             SUPPORTED_100baseT_Full |
+                             SUPPORTED_1000baseT_Full |
+                             SUPPORTED_Pause | SUPPORTED_Asym_Pause);
+       genphy_config_aneg(phydev);
+
+       /* Workaround to enable PHY interrupt.
+        * phy_start_interrupts() is API for requesting and enabling
+        * PHY interrupt. However, USB-to-Ethernet device can't use
+        * request_irq() called in phy_start_interrupts().
+        * Set PHY to PHY_HALTED and call phy_start()
+        * to make a call to phy_enable_interrupts()
+        */
+       phy_stop(phydev);
+       phy_start(phydev);
 
        netif_dbg(dev, ifup, dev->net, "phy initialised successfully");
 
@@ -1930,6 +1830,10 @@ static int lan78xx_reset(struct lan78xx_net *dev)
 
        lan78xx_init_mac_address(dev);
 
+       /* save DEVID for later usage */
+       ret = lan78xx_read_reg(dev, ID_REV, &buf);
+       dev->devid = buf;
+
        /* Respond to the IN token with a NAK */
        ret = lan78xx_read_reg(dev, USB_CFG0, &buf);
        buf |= USB_CFG_BIR_;
@@ -2002,23 +1906,12 @@ static int lan78xx_reset(struct lan78xx_net *dev)
                        netdev_warn(dev->net, "timeout waiting for PHY Reset");
                        return -EIO;
                }
-       } while (buf & PMT_CTL_PHY_RST_);
-
-       lan78xx_mii_init(dev);
-
-       ret = lan78xx_phy_init(dev);
+       } while ((buf & PMT_CTL_PHY_RST_) || !(buf & PMT_CTL_READY_));
 
        ret = lan78xx_read_reg(dev, MAC_CR, &buf);
-
-       buf |= MAC_CR_GMII_EN_;
        buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
-
        ret = lan78xx_write_reg(dev, MAC_CR, buf);
 
-       /* enable on PHY */
-       if (buf & MAC_CR_EEE_EN_)
-               lan78xx_mmd_write(dev->net, dev->mii.phy_id, 0x07, 0x3C, 0x06);
-
        /* enable PHY interrupts */
        ret = lan78xx_read_reg(dev, INT_EP_CTL, &buf);
        buf |= INT_ENP_PHY_INT;
@@ -2042,9 +1935,6 @@ static int lan78xx_reset(struct lan78xx_net *dev)
        buf |= FCT_RX_CTL_EN_;
        ret = lan78xx_write_reg(dev, FCT_RX_CTL, buf);
 
-       if (!mii_nway_restart(&dev->mii))
-               netif_dbg(dev, link, dev->net, "autoneg initiated");
-
        return 0;
 }
 
@@ -2061,6 +1951,10 @@ static int lan78xx_open(struct net_device *net)
        if (ret < 0)
                goto done;
 
+       ret = lan78xx_phy_init(dev);
+       if (ret < 0)
+               goto done;
+
        /* for Link Check */
        if (dev->urb_intr) {
                ret = usb_submit_urb(dev->urb_intr, GFP_KERNEL);
@@ -2115,6 +2009,10 @@ int lan78xx_stop(struct net_device *net)
 {
        struct lan78xx_net              *dev = netdev_priv(net);
 
+       phy_stop(net->phydev);
+       phy_disconnect(net->phydev);
+       net->phydev = NULL;
+
        clear_bit(EVENT_DEV_OPEN, &dev->flags);
        netif_stop_queue(net);
 
@@ -2395,6 +2293,8 @@ static int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf)
        /* Init all registers */
        ret = lan78xx_reset(dev);
 
+       lan78xx_mdio_init(dev);
+
        dev->net->flags |= IFF_MULTICAST;
 
        pdata->wol = WAKE_MAGIC;
@@ -2406,6 +2306,8 @@ static void lan78xx_unbind(struct lan78xx_net *dev, struct usb_interface *intf)
 {
        struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
 
+       lan78xx_remove_mdio(dev);
+
        if (pdata) {
                netif_dbg(dev, ifdown, dev->net, "free pdata");
                kfree(pdata);
@@ -2522,11 +2424,6 @@ static int lan78xx_rx(struct lan78xx_net *dev, struct sk_buff *skb)
                        skb_pull(skb, align_count);
        }
 
-       if (unlikely(skb->len < 0)) {
-               netdev_warn(dev->net, "invalid rx length<0 %d", skb->len);
-               return 0;
-       }
-
        return 1;
 }
 
@@ -3307,7 +3204,6 @@ int lan78xx_suspend(struct usb_interface *intf, pm_message_t message)
        int ret;
        int event;
 
-       ret = 0;
        event = message.event;
 
        if (!dev->suspend_count++) {
@@ -3389,6 +3285,7 @@ int lan78xx_suspend(struct usb_interface *intf, pm_message_t message)
                }
        }
 
+       ret = 0;
 out:
        return ret;
 }
@@ -3459,6 +3356,9 @@ int lan78xx_reset_resume(struct usb_interface *intf)
        struct lan78xx_net *dev = usb_get_intfdata(intf);
 
        lan78xx_reset(dev);
+
+       lan78xx_phy_init(dev);
+
        return lan78xx_resume(intf);
 }
 
index ae7562ee72ad01823a3df47721bb744d3152dec1..a93fb653e7c52f415bd0aae99f7cd2619c8cf47b 100644 (file)
 #define LTM_INACTIVE1_TIMER10_         (0x0000FFFF)
 
 #define MAC_CR                         (0x100)
-#define MAC_CR_GMII_EN_                        (0x00080000)
 #define MAC_CR_EEE_TX_CLK_STOP_EN_     (0x00040000)
 #define MAC_CR_EEE_EN_                 (0x00020000)
 #define MAC_CR_EEE_TLAR_EN_            (0x00010000)
 #define OTP_TPVSR_VAL                  (OTP_BASE_ADDR + 4 * 0x3A)
 #define OTP_TPVHR_VAL                  (OTP_BASE_ADDR + 4 * 0x3B)
 #define OTP_TPVSA_VAL                  (OTP_BASE_ADDR + 4 * 0x3C)
-
-#define PHY_ID1                                (0x02)
-#define PHY_ID2                                (0x03)
-
-#define PHY_DEV_ID_OUI_VTSE            (0x04001C)
-#define PHY_DEV_ID_MODEL_VTSE_8502     (0x23)
-
-#define PHY_AUTONEG_ADV                        (0x04)
-#define NWAY_AR_NEXT_PAGE_             (0x8000)
-#define NWAY_AR_REMOTE_FAULT_          (0x2000)
-#define NWAY_AR_ASM_DIR_               (0x0800)
-#define NWAY_AR_PAUSE_                 (0x0400)
-#define NWAY_AR_100T4_CAPS_            (0x0200)
-#define NWAY_AR_100TX_FD_CAPS_         (0x0100)
-#define NWAY_AR_SELECTOR_FIELD_                (0x001F)
-#define NWAY_AR_100TX_HD_CAPS_         (0x0080)
-#define NWAY_AR_10T_FD_CAPS_           (0x0040)
-#define NWAY_AR_10T_HD_CAPS_           (0x0020)
-#define NWAY_AR_ALL_CAPS_              (NWAY_AR_10T_HD_CAPS_ | \
-                                        NWAY_AR_10T_FD_CAPS_ | \
-                                        NWAY_AR_100TX_HD_CAPS_ | \
-                                        NWAY_AR_100TX_FD_CAPS_)
-#define NWAY_AR_PAUSE_MASK             (NWAY_AR_PAUSE_ | NWAY_AR_ASM_DIR_)
-
-#define PHY_LP_ABILITY                 (0x05)
-#define NWAY_LPAR_NEXT_PAGE_           (0x8000)
-#define NWAY_LPAR_ACKNOWLEDGE_         (0x4000)
-#define NWAY_LPAR_REMOTE_FAULT_                (0x2000)
-#define NWAY_LPAR_ASM_DIR_             (0x0800)
-#define NWAY_LPAR_PAUSE_               (0x0400)
-#define NWAY_LPAR_100T4_CAPS_          (0x0200)
-#define NWAY_LPAR_100TX_FD_CAPS_       (0x0100)
-#define NWAY_LPAR_100TX_HD_CAPS_       (0x0080)
-#define NWAY_LPAR_10T_FD_CAPS_         (0x0040)
-#define NWAY_LPAR_10T_HD_CAPS_         (0x0020)
-#define NWAY_LPAR_SELECTOR_FIELD_      (0x001F)
-
-#define PHY_AUTONEG_EXP                        (0x06)
-#define NWAY_ER_PAR_DETECT_FAULT_      (0x0010)
-#define NWAY_ER_LP_NEXT_PAGE_CAPS_     (0x0008)
-#define NWAY_ER_NEXT_PAGE_CAPS_                (0x0004)
-#define NWAY_ER_PAGE_RXD_              (0x0002)
-#define NWAY_ER_LP_NWAY_CAPS_          (0x0001)
-
-#define PHY_NEXT_PAGE_TX               (0x07)
-#define NPTX_NEXT_PAGE_                        (0x8000)
-#define NPTX_MSG_PAGE_                 (0x2000)
-#define NPTX_ACKNOWLDGE2_              (0x1000)
-#define NPTX_TOGGLE_                   (0x0800)
-#define NPTX_MSG_CODE_FIELD_           (0x0001)
-
-#define PHY_LP_NEXT_PAGE               (0x08)
-#define LP_RNPR_NEXT_PAGE_             (0x8000)
-#define LP_RNPR_ACKNOWLDGE_            (0x4000)
-#define LP_RNPR_MSG_PAGE_              (0x2000)
-#define LP_RNPR_ACKNOWLDGE2_           (0x1000)
-#define LP_RNPR_TOGGLE_                        (0x0800)
-#define LP_RNPR_MSG_CODE_FIELD_                (0x0001)
-
-#define PHY_1000T_CTRL                 (0x09)
-#define CR_1000T_TEST_MODE_4_          (0x8000)
-#define CR_1000T_TEST_MODE_3_          (0x6000)
-#define CR_1000T_TEST_MODE_2_          (0x4000)
-#define CR_1000T_TEST_MODE_1_          (0x2000)
-#define CR_1000T_MS_ENABLE_            (0x1000)
-#define CR_1000T_MS_VALUE_             (0x0800)
-#define CR_1000T_REPEATER_DTE_         (0x0400)
-#define CR_1000T_FD_CAPS_              (0x0200)
-#define CR_1000T_HD_CAPS_              (0x0100)
-#define CR_1000T_ASYM_PAUSE_           (0x0080)
-#define CR_1000T_TEST_MODE_NORMAL_     (0x0000)
-
-#define PHY_1000T_STATUS               (0x0A)
-#define SR_1000T_MS_CONFIG_FAULT_      (0x8000)
-#define SR_1000T_MS_CONFIG_RES_                (0x4000)
-#define SR_1000T_LOCAL_RX_STATUS_      (0x2000)
-#define SR_1000T_REMOTE_RX_STATUS_     (0x1000)
-#define SR_1000T_LP_FD_CAPS_           (0x0800)
-#define SR_1000T_LP_HD_CAPS_           (0x0400)
-#define SR_1000T_ASYM_PAUSE_DIR_       (0x0100)
-#define SR_1000T_IDLE_ERROR_CNT_       (0x00FF)
-#define SR_1000T_REMOTE_RX_STATUS_SHIFT                12
-#define SR_1000T_LOCAL_RX_STATUS_SHIFT         13
-#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT  5
-#define FFE_IDLE_ERR_COUNT_TIMEOUT_20          20
-#define FFE_IDLE_ERR_COUNT_TIMEOUT_100         100
-
-#define PHY_EXT_STATUS                 (0x0F)
-#define IEEE_ESR_1000X_FD_CAPS_                (0x8000)
-#define IEEE_ESR_1000X_HD_CAPS_                (0x4000)
-#define IEEE_ESR_1000T_FD_CAPS_                (0x2000)
-#define IEEE_ESR_1000T_HD_CAPS_                (0x1000)
-#define PHY_TX_POLARITY_MASK_          (0x0100)
-#define PHY_TX_NORMAL_POLARITY_                (0x0000)
-#define AUTO_POLARITY_DISABLE_         (0x0010)
-
-#define PHY_MMD_CTL                    (0x0D)
-#define PHY_MMD_CTRL_OP_MASK_          (0xC000)
-#define PHY_MMD_CTRL_OP_REG_           (0x0000)
-#define PHY_MMD_CTRL_OP_DNI_           (0x4000)
-#define PHY_MMD_CTRL_OP_DPIRW_         (0x8000)
-#define PHY_MMD_CTRL_OP_DPIWO_         (0xC000)
-#define PHY_MMD_CTRL_DEV_ADDR_MASK_    (0x001F)
-
-#define PHY_MMD_REG_DATA               (0x0E)
-
-/* VTSE Vendor Specific registers */
-#define PHY_VTSE_BYPASS                                (0x12)
-#define PHY_VTSE_BYPASS_DISABLE_PAIR_SWAP_     (0x0020)
-
-#define PHY_VTSE_INT_MASK                      (0x19)
-#define PHY_VTSE_INT_MASK_MDINTPIN_EN_         (0x8000)
-#define PHY_VTSE_INT_MASK_SPEED_CHANGE_                (0x4000)
-#define PHY_VTSE_INT_MASK_LINK_CHANGE_         (0x2000)
-#define PHY_VTSE_INT_MASK_FDX_CHANGE_          (0x1000)
-#define PHY_VTSE_INT_MASK_AUTONEG_ERR_         (0x0800)
-#define PHY_VTSE_INT_MASK_AUTONEG_DONE_                (0x0400)
-#define PHY_VTSE_INT_MASK_POE_DETECT_          (0x0200)
-#define PHY_VTSE_INT_MASK_SYMBOL_ERR_          (0x0100)
-#define PHY_VTSE_INT_MASK_FAST_LINK_FAIL_      (0x0080)
-#define PHY_VTSE_INT_MASK_WOL_EVENT_           (0x0040)
-#define PHY_VTSE_INT_MASK_EXTENDED_INT_                (0x0020)
-#define PHY_VTSE_INT_MASK_RESERVED_            (0x0010)
-#define PHY_VTSE_INT_MASK_FALSE_CARRIER_       (0x0008)
-#define PHY_VTSE_INT_MASK_LINK_SPEED_DS_       (0x0004)
-#define PHY_VTSE_INT_MASK_MASTER_SLAVE_DONE_   (0x0002)
-#define PHY_VTSE_INT_MASK_RX__ER_              (0x0001)
-
-#define PHY_VTSE_INT_STS                       (0x1A)
-#define PHY_VTSE_INT_STS_INT_ACTIVE_           (0x8000)
-#define PHY_VTSE_INT_STS_SPEED_CHANGE_         (0x4000)
-#define PHY_VTSE_INT_STS_LINK_CHANGE_          (0x2000)
-#define PHY_VTSE_INT_STS_FDX_CHANGE_           (0x1000)
-#define PHY_VTSE_INT_STS_AUTONEG_ERR_          (0x0800)
-#define PHY_VTSE_INT_STS_AUTONEG_DONE_         (0x0400)
-#define PHY_VTSE_INT_STS_POE_DETECT_           (0x0200)
-#define PHY_VTSE_INT_STS_SYMBOL_ERR_           (0x0100)
-#define PHY_VTSE_INT_STS_FAST_LINK_FAIL_       (0x0080)
-#define PHY_VTSE_INT_STS_WOL_EVENT_            (0x0040)
-#define PHY_VTSE_INT_STS_EXTENDED_INT_         (0x0020)
-#define PHY_VTSE_INT_STS_RESERVED_             (0x0010)
-#define PHY_VTSE_INT_STS_FALSE_CARRIER_                (0x0008)
-#define PHY_VTSE_INT_STS_LINK_SPEED_DS_                (0x0004)
-#define PHY_VTSE_INT_STS_MASTER_SLAVE_DONE_    (0x0002)
-#define PHY_VTSE_INT_STS_RX_ER_                        (0x0001)
-
-/* VTSE PHY registers */
-#define PHY_EXT_GPIO_PAGE              (0x1F)
-#define PHY_EXT_GPIO_PAGE_SPACE_0      (0x0000)
-#define PHY_EXT_GPIO_PAGE_SPACE_1      (0x0001)
-#define PHY_EXT_GPIO_PAGE_SPACE_2      (0x0002)
-
-/* Extended Register Page 1 space */
-#define PHY_EXT_MODE_CTRL              (0x13)
-#define PHY_EXT_MODE_CTRL_MDIX_MASK_   (0x000C)
-#define PHY_EXT_MODE_CTRL_AUTO_MDIX_   (0x0000)
-#define PHY_EXT_MODE_CTRL_MDI_         (0x0008)
-#define PHY_EXT_MODE_CTRL_MDI_X_       (0x000C)
-
-#define PHY_ANA_10BASE_T_HD            0x01
-#define PHY_ANA_10BASE_T_FD            0x02
-#define PHY_ANA_100BASE_TX_HD          0x04
-#define PHY_ANA_100BASE_TX_FD          0x08
-#define PHY_ANA_1000BASE_T_FD          0x10
-#define PHY_ANA_ALL_SUPPORTED_MEDIA    (PHY_ANA_10BASE_T_HD |   \
-                                        PHY_ANA_10BASE_T_FD |   \
-                                        PHY_ANA_100BASE_TX_HD | \
-                                        PHY_ANA_100BASE_TX_FD | \
-                                        PHY_ANA_1000BASE_T_FD)
-/* PHY MMD registers */
-#define PHY_MMD_DEV_3                          3
-
-#define PHY_EEE_PCS_STATUS                     (0x1)
-#define PHY_EEE_PCS_STATUS_TX_LPI_RCVD_                ((WORD)0x0800)
-#define PHY_EEE_PCS_STATUS_RX_LPI_RCVD_                ((WORD)0x0400)
-#define PHY_EEE_PCS_STATUS_TX_LPI_IND_         ((WORD)0x0200)
-#define PHY_EEE_PCS_STATUS_RX_LPI_IND_         ((WORD)0x0100)
-#define PHY_EEE_PCS_STATUS_PCS_RCV_LNK_STS_    ((WORD)0x0004)
-
-#define PHY_EEE_CAPABILITIES                   (0x14)
-#define PHY_EEE_CAPABILITIES_1000BT_EEE_       ((WORD)0x0004)
-#define PHY_EEE_CAPABILITIES_100BT_EEE_                ((WORD)0x0002)
-
-#define PHY_MMD_DEV_7                          7
-
-#define PHY_EEE_ADVERTISEMENT                  (0x3C)
-#define PHY_EEE_ADVERTISEMENT_1000BT_EEE_      ((WORD)0x0004)
-#define PHY_EEE_ADVERTISEMENT_100BT_EEE_       ((WORD)0x0002)
-
-#define PHY_EEE_LP_ADVERTISEMENT               (0x3D)
-#define PHY_EEE_1000BT_EEE_CAPABLE_            ((WORD)0x0004)
-#define PHY_EEE_100BT_EEE_CAPABLE_             ((WORD)0x0002)
 #endif /* _LAN78XX_H */
index 82d844a8ebd093e79c26ada9fc728fec8b10576e..4f345bd4e6e29558daf29c3d472d2c0768c3202f 100644 (file)
@@ -445,7 +445,6 @@ static int mcs7830_get_regs_len(struct net_device *net)
 static void mcs7830_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *drvinfo)
 {
        usbnet_get_drvinfo(net, drvinfo);
-       drvinfo->regdump_len = mcs7830_get_regs_len(net);
 }
 
 static void mcs7830_get_regs(struct net_device *net, struct ethtool_regs *regs, void *data)
index 355842b85ee906a434477b325f32199ec315eae8..c54719984c4bdd0da0e36dd54ab1430708483b0b 100644 (file)
@@ -229,11 +229,11 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
        u8 *buf = intf->cur_altsetting->extra;
        int len = intf->cur_altsetting->extralen;
        struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc;
-       struct usb_cdc_union_desc *cdc_union = NULL;
-       struct usb_cdc_ether_desc *cdc_ether = NULL;
-       u32 found = 0;
+       struct usb_cdc_union_desc *cdc_union;
+       struct usb_cdc_ether_desc *cdc_ether;
        struct usb_driver *driver = driver_of(intf);
        struct qmi_wwan_state *info = (void *)&dev->data;
+       struct usb_cdc_parsed_header hdr;
 
        BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) <
                      sizeof(struct qmi_wwan_state)));
@@ -243,63 +243,9 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
        info->data = intf;
 
        /* and a number of CDC descriptors */
-       while (len > 3) {
-               struct usb_descriptor_header *h = (void *)buf;
-
-               /* ignore any misplaced descriptors */
-               if (h->bDescriptorType != USB_DT_CS_INTERFACE)
-                       goto next_desc;
-
-               /* buf[2] is CDC descriptor subtype */
-               switch (buf[2]) {
-               case USB_CDC_HEADER_TYPE:
-                       if (found & 1 << USB_CDC_HEADER_TYPE) {
-                               dev_dbg(&intf->dev, "extra CDC header\n");
-                               goto err;
-                       }
-                       if (h->bLength != sizeof(struct usb_cdc_header_desc)) {
-                               dev_dbg(&intf->dev, "CDC header len %u\n",
-                                       h->bLength);
-                               goto err;
-                       }
-                       break;
-               case USB_CDC_UNION_TYPE:
-                       if (found & 1 << USB_CDC_UNION_TYPE) {
-                               dev_dbg(&intf->dev, "extra CDC union\n");
-                               goto err;
-                       }
-                       if (h->bLength != sizeof(struct usb_cdc_union_desc)) {
-                               dev_dbg(&intf->dev, "CDC union len %u\n",
-                                       h->bLength);
-                               goto err;
-                       }
-                       cdc_union = (struct usb_cdc_union_desc *)buf;
-                       break;
-               case USB_CDC_ETHERNET_TYPE:
-                       if (found & 1 << USB_CDC_ETHERNET_TYPE) {
-                               dev_dbg(&intf->dev, "extra CDC ether\n");
-                               goto err;
-                       }
-                       if (h->bLength != sizeof(struct usb_cdc_ether_desc)) {
-                               dev_dbg(&intf->dev, "CDC ether len %u\n",
-                                       h->bLength);
-                               goto err;
-                       }
-                       cdc_ether = (struct usb_cdc_ether_desc *)buf;
-                       break;
-               }
-
-               /* Remember which CDC functional descriptors we've seen.  Works
-                * for all types we care about, of which USB_CDC_ETHERNET_TYPE
-                * (0x0f) is the highest numbered
-                */
-               if (buf[2] < 32)
-                       found |= 1 << buf[2];
-
-next_desc:
-               len -= h->bLength;
-               buf += h->bLength;
-       }
+       cdc_parse_cdc_header(&hdr, intf, buf, len);
+       cdc_union = hdr.usb_cdc_union_desc;
+       cdc_ether = hdr.usb_cdc_ether_desc;
 
        /* Use separate control and data interfaces if we found a CDC Union */
        if (cdc_union) {
@@ -539,6 +485,10 @@ static const struct usb_device_id products[] = {
                                              USB_CDC_PROTO_NONE),
                .driver_info        = (unsigned long)&qmi_wwan_info,
        },
+       {       /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */
+               USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7),
+               .driver_info = (unsigned long)&qmi_wwan_info,
+       },
 
        /* 3. Combined interface devices matching on interface number */
        {QMI_FIXED_INTF(0x0408, 0xea42, 4)},    /* Yota / Megafon M100-1 */
@@ -765,6 +715,10 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x1199, 0x9056, 8)},    /* Sierra Wireless Modem */
        {QMI_FIXED_INTF(0x1199, 0x9057, 8)},
        {QMI_FIXED_INTF(0x1199, 0x9061, 8)},    /* Sierra Wireless Modem */
+       {QMI_FIXED_INTF(0x1199, 0x9070, 8)},    /* Sierra Wireless MC74xx/EM74xx */
+       {QMI_FIXED_INTF(0x1199, 0x9070, 10)},   /* Sierra Wireless MC74xx/EM74xx */
+       {QMI_FIXED_INTF(0x1199, 0x9071, 8)},    /* Sierra Wireless MC74xx/EM74xx */
+       {QMI_FIXED_INTF(0x1199, 0x9071, 10)},   /* Sierra Wireless MC74xx/EM74xx */
        {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)},    /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */
        {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)},    /* Alcatel L800MA */
        {QMI_FIXED_INTF(0x2357, 0x0201, 4)},    /* TP-LINK HSUPA Modem MA180 */
@@ -787,7 +741,6 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x413c, 0x81a9, 8)},    /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
        {QMI_FIXED_INTF(0x413c, 0x81b1, 8)},    /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
        {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)},    /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
-       {QMI_FIXED_INTF(0x03f0, 0x581d, 4)},    /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */
 
        /* 4. Gobi 1000 devices */
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},    /* Acer Gobi Modem Device */
index d9e7892262faa6a0543807d4d411163a5f59ac78..30033dbe666263f12ed05cbe7d2fe1e924b156e4 100644 (file)
@@ -2185,11 +2185,6 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                        skb_pull(skb, align_count);
        }
 
-       if (unlikely(skb->len < 0)) {
-               netdev_warn(dev->net, "invalid rx length<0 %d\n", skb->len);
-               return 0;
-       }
-
        return 1;
 }
 
index 26423adc35ee892c6a520cc243f8e71680a71eaf..66b3ab9f614eb07edb05757ee333e998f7841f7d 100644 (file)
@@ -1815,11 +1815,6 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                        skb_pull(skb, align_count);
        }
 
-       if (unlikely(skb->len < 0)) {
-               netdev_warn(dev->net, "invalid rx length<0 %d\n", skb->len);
-               return 0;
-       }
-
        return 1;
 }
 
index 953de13267df19d6fcefe60c2008f6222ca5775c..a50df0d8fb9abbd548ad6646e4a066a1211363c5 100644 (file)
@@ -470,14 +470,10 @@ static int sr_get_eeprom(struct net_device *net,
 static void sr_get_drvinfo(struct net_device *net,
                                 struct ethtool_drvinfo *info)
 {
-       struct usbnet *dev = netdev_priv(net);
-       struct sr_data *data = (struct sr_data *)&dev->data;
-
        /* Inherit standard device info */
        usbnet_get_drvinfo(net, info);
        strncpy(info->driver, DRIVER_NAME, sizeof(info->driver));
        strncpy(info->version, DRIVER_VERSION, sizeof(info->version));
-       info->eedump_len = data->eeprom_len;
 }
 
 static u32 sr_get_link(struct net_device *net)
index b4cf10781348effecf9ca1f6896c0aeab852bba2..060918f49fea82779a37d326a4d1729189ddad28 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/mii.h>
 #include <linux/usb.h>
 #include <linux/usb/usbnet.h>
+#include <linux/usb/cdc.h>
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/pm_runtime.h>
@@ -1962,6 +1963,143 @@ out:
        return err;
 }
 
+int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
+                               struct usb_interface *intf,
+                               u8 *buffer,
+                               int buflen)
+{
+       /* duplicates are ignored */
+       struct usb_cdc_union_desc *union_header = NULL;
+
+       /* duplicates are not tolerated */
+       struct usb_cdc_header_desc *header = NULL;
+       struct usb_cdc_ether_desc *ether = NULL;
+       struct usb_cdc_mdlm_detail_desc *detail = NULL;
+       struct usb_cdc_mdlm_desc *desc = NULL;
+
+       unsigned int elength;
+       int cnt = 0;
+
+       memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header));
+       hdr->phonet_magic_present = false;
+       while (buflen > 0) {
+               elength = buffer[0];
+               if (!elength) {
+                       dev_err(&intf->dev, "skipping garbage byte\n");
+                       elength = 1;
+                       goto next_desc;
+               }
+               if (buffer[1] != USB_DT_CS_INTERFACE) {
+                       dev_err(&intf->dev, "skipping garbage\n");
+                       goto next_desc;
+               }
+
+               switch (buffer[2]) {
+               case USB_CDC_UNION_TYPE: /* we've found it */
+                       if (elength < sizeof(struct usb_cdc_union_desc))
+                               goto next_desc;
+                       if (union_header) {
+                               dev_err(&intf->dev, "More than one union descriptor, skipping ...\n");
+                               goto next_desc;
+                       }
+                       union_header = (struct usb_cdc_union_desc *)buffer;
+                       break;
+               case USB_CDC_COUNTRY_TYPE:
+                       if (elength < sizeof(struct usb_cdc_country_functional_desc))
+                               goto next_desc;
+                       hdr->usb_cdc_country_functional_desc =
+                               (struct usb_cdc_country_functional_desc *)buffer;
+                       break;
+               case USB_CDC_HEADER_TYPE:
+                       if (elength != sizeof(struct usb_cdc_header_desc))
+                               goto next_desc;
+                       if (header)
+                               return -EINVAL;
+                       header = (struct usb_cdc_header_desc *)buffer;
+                       break;
+               case USB_CDC_ACM_TYPE:
+                       if (elength < sizeof(struct usb_cdc_acm_descriptor))
+                               goto next_desc;
+                       hdr->usb_cdc_acm_descriptor =
+                               (struct usb_cdc_acm_descriptor *)buffer;
+                       break;
+               case USB_CDC_ETHERNET_TYPE:
+                       if (elength != sizeof(struct usb_cdc_ether_desc))
+                               goto next_desc;
+                       if (ether)
+                               return -EINVAL;
+                       ether = (struct usb_cdc_ether_desc *)buffer;
+                       break;
+               case USB_CDC_CALL_MANAGEMENT_TYPE:
+                       if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor))
+                               goto next_desc;
+                       hdr->usb_cdc_call_mgmt_descriptor =
+                               (struct usb_cdc_call_mgmt_descriptor *)buffer;
+                       break;
+               case USB_CDC_DMM_TYPE:
+                       if (elength < sizeof(struct usb_cdc_dmm_desc))
+                               goto next_desc;
+                       hdr->usb_cdc_dmm_desc =
+                               (struct usb_cdc_dmm_desc *)buffer;
+                       break;
+               case USB_CDC_MDLM_TYPE:
+                       if (elength < sizeof(struct usb_cdc_mdlm_desc *))
+                               goto next_desc;
+                       if (desc)
+                               return -EINVAL;
+                       desc = (struct usb_cdc_mdlm_desc *)buffer;
+                       break;
+               case USB_CDC_MDLM_DETAIL_TYPE:
+                       if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *))
+                               goto next_desc;
+                       if (detail)
+                               return -EINVAL;
+                       detail = (struct usb_cdc_mdlm_detail_desc *)buffer;
+                       break;
+               case USB_CDC_NCM_TYPE:
+                       if (elength < sizeof(struct usb_cdc_ncm_desc))
+                               goto next_desc;
+                       hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer;
+                       break;
+               case USB_CDC_MBIM_TYPE:
+                       if (elength < sizeof(struct usb_cdc_mbim_desc))
+                               goto next_desc;
+
+                       hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer;
+                       break;
+               case USB_CDC_MBIM_EXTENDED_TYPE:
+                       if (elength < sizeof(struct usb_cdc_mbim_extended_desc))
+                               break;
+                       hdr->usb_cdc_mbim_extended_desc =
+                               (struct usb_cdc_mbim_extended_desc *)buffer;
+                       break;
+               case CDC_PHONET_MAGIC_NUMBER:
+                       hdr->phonet_magic_present = true;
+                       break;
+               default:
+                       /*
+                        * there are LOTS more CDC descriptors that
+                        * could legitimately be found here.
+                        */
+                       dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n",
+                                       buffer[2], elength);
+                       goto next_desc;
+               }
+               cnt++;
+next_desc:
+               buflen -= elength;
+               buffer += elength;
+       }
+       hdr->usb_cdc_union_desc = union_header;
+       hdr->usb_cdc_header_desc = header;
+       hdr->usb_cdc_mdlm_detail_desc = detail;
+       hdr->usb_cdc_mdlm_desc = desc;
+       hdr->usb_cdc_ether_desc = ether;
+       return cnt;
+}
+
+EXPORT_SYMBOL(cdc_parse_cdc_header);
+
 /*
  * The function can't be called inside suspend/resume callback,
  * otherwise deadlock will be caused.
index c1d0e7a9da04c36c58c43b6c8c432a1d8a8720f1..9ba11d7377539e7fc2e0e0337f4234f5a0801946 100644 (file)
@@ -183,16 +183,22 @@ vmxnet3_get_sset_count(struct net_device *netdev, int sset)
 }
 
 
-/* Should be multiple of 4 */
-#define NUM_TX_REGS    8
-#define NUM_RX_REGS    12
-
+/* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with
+ * the version 2 of the vmxnet3 support for ethtool(8) --register-dump.
+ * Therefore, if any registers are added, removed or modified, then a version
+ * bump and a corresponding change in the vmxnet3 support for ethtool(8)
+ * --register-dump would be required.
+ */
 static int
 vmxnet3_get_regs_len(struct net_device *netdev)
 {
        struct vmxnet3_adapter *adapter = netdev_priv(netdev);
-       return (adapter->num_tx_queues * NUM_TX_REGS * sizeof(u32) +
-               adapter->num_rx_queues * NUM_RX_REGS * sizeof(u32));
+
+       return ((9 /* BAR1 registers */ +
+               (1 + adapter->intr.num_intrs) +
+               (1 + adapter->num_tx_queues * 17 /* Tx queue registers */) +
+               (1 + adapter->num_rx_queues * 23 /* Rx queue registers */)) *
+               sizeof(u32));
 }
 
 
@@ -208,10 +214,6 @@ vmxnet3_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
 
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
-       drvinfo->n_stats = vmxnet3_get_sset_count(netdev, ETH_SS_STATS);
-       drvinfo->testinfo_len = 0;
-       drvinfo->eedump_len   = 0;
-       drvinfo->regdump_len  = vmxnet3_get_regs_len(netdev);
 }
 
 
@@ -342,6 +344,12 @@ vmxnet3_get_ethtool_stats(struct net_device *netdev,
 }
 
 
+/* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with
+ * the version 2 of the vmxnet3 support for ethtool(8) --register-dump.
+ * Therefore, if any registers are added, removed or modified, then a version
+ * bump and a corresponding change in the vmxnet3 support for ethtool(8)
+ * --register-dump would be required.
+ */
 static void
 vmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
 {
@@ -351,40 +359,90 @@ vmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
 
        memset(p, 0, vmxnet3_get_regs_len(netdev));
 
-       regs->version = 1;
+       regs->version = 2;
 
        /* Update vmxnet3_get_regs_len if we want to dump more registers */
 
-       /* make each ring use multiple of 16 bytes */
-       for (i = 0; i < adapter->num_tx_queues; i++) {
-               buf[j++] = adapter->tx_queue[i].tx_ring.next2fill;
-               buf[j++] = adapter->tx_queue[i].tx_ring.next2comp;
-               buf[j++] = adapter->tx_queue[i].tx_ring.gen;
-               buf[j++] = 0;
+       buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS);
+       buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS);
+       buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAL);
+       buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAH);
+       buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD);
+       buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACL);
+       buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACH);
+       buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ICR);
+       buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ECR);
+
+       buf[j++] = adapter->intr.num_intrs;
+       for (i = 0; i < adapter->intr.num_intrs; i++) {
+               buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_IMR
+                                                + i * VMXNET3_REG_ALIGN);
+       }
 
-               buf[j++] = adapter->tx_queue[i].comp_ring.next2proc;
-               buf[j++] = adapter->tx_queue[i].comp_ring.gen;
-               buf[j++] = adapter->tx_queue[i].stopped;
-               buf[j++] = 0;
+       buf[j++] = adapter->num_tx_queues;
+       for (i = 0; i < adapter->num_tx_queues; i++) {
+               struct vmxnet3_tx_queue *tq = &adapter->tx_queue[i];
+
+               buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_TXPROD +
+                                                i * VMXNET3_REG_ALIGN);
+
+               buf[j++] = VMXNET3_GET_ADDR_LO(tq->tx_ring.basePA);
+               buf[j++] = VMXNET3_GET_ADDR_HI(tq->tx_ring.basePA);
+               buf[j++] = tq->tx_ring.size;
+               buf[j++] = tq->tx_ring.next2fill;
+               buf[j++] = tq->tx_ring.next2comp;
+               buf[j++] = tq->tx_ring.gen;
+
+               buf[j++] = VMXNET3_GET_ADDR_LO(tq->data_ring.basePA);
+               buf[j++] = VMXNET3_GET_ADDR_HI(tq->data_ring.basePA);
+               buf[j++] = tq->data_ring.size;
+               /* transmit data ring buffer size */
+               buf[j++] = VMXNET3_HDR_COPY_SIZE;
+
+               buf[j++] = VMXNET3_GET_ADDR_LO(tq->comp_ring.basePA);
+               buf[j++] = VMXNET3_GET_ADDR_HI(tq->comp_ring.basePA);
+               buf[j++] = tq->comp_ring.size;
+               buf[j++] = tq->comp_ring.next2proc;
+               buf[j++] = tq->comp_ring.gen;
+
+               buf[j++] = tq->stopped;
        }
 
+       buf[j++] = adapter->num_rx_queues;
        for (i = 0; i < adapter->num_rx_queues; i++) {
-               buf[j++] = adapter->rx_queue[i].rx_ring[0].next2fill;
-               buf[j++] = adapter->rx_queue[i].rx_ring[0].next2comp;
-               buf[j++] = adapter->rx_queue[i].rx_ring[0].gen;
+               struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i];
+
+               buf[j++] =  VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_RXPROD +
+                                                 i * VMXNET3_REG_ALIGN);
+               buf[j++] =  VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_RXPROD2 +
+                                                 i * VMXNET3_REG_ALIGN);
+
+               buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[0].basePA);
+               buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[0].basePA);
+               buf[j++] = rq->rx_ring[0].size;
+               buf[j++] = rq->rx_ring[0].next2fill;
+               buf[j++] = rq->rx_ring[0].next2comp;
+               buf[j++] = rq->rx_ring[0].gen;
+
+               buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[1].basePA);
+               buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[1].basePA);
+               buf[j++] = rq->rx_ring[1].size;
+               buf[j++] = rq->rx_ring[1].next2fill;
+               buf[j++] = rq->rx_ring[1].next2comp;
+               buf[j++] = rq->rx_ring[1].gen;
+
+               /* receive data ring */
                buf[j++] = 0;
-
-               buf[j++] = adapter->rx_queue[i].rx_ring[1].next2fill;
-               buf[j++] = adapter->rx_queue[i].rx_ring[1].next2comp;
-               buf[j++] = adapter->rx_queue[i].rx_ring[1].gen;
                buf[j++] = 0;
-
-               buf[j++] = adapter->rx_queue[i].comp_ring.next2proc;
-               buf[j++] = adapter->rx_queue[i].comp_ring.gen;
                buf[j++] = 0;
                buf[j++] = 0;
-       }
 
+               buf[j++] = VMXNET3_GET_ADDR_LO(rq->comp_ring.basePA);
+               buf[j++] = VMXNET3_GET_ADDR_HI(rq->comp_ring.basePA);
+               buf[j++] = rq->comp_ring.size;
+               buf[j++] = rq->comp_ring.next2proc;
+               buf[j++] = rq->comp_ring.gen;
+       }
 }
 
 
index 2652245631d12f5016915721d5f93268cb4453c1..3f859a55c035d6ab3b53530c4345083424566404 100644 (file)
 /*
  * Version numbers
  */
-#define VMXNET3_DRIVER_VERSION_STRING   "1.4.2.0-k"
+#define VMXNET3_DRIVER_VERSION_STRING   "1.4.3.0-k"
 
 /* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */
-#define VMXNET3_DRIVER_VERSION_NUM      0x01040200
+#define VMXNET3_DRIVER_VERSION_NUM      0x01040300
 
 #if defined(CONFIG_PCI_MSI)
        /* RSS only makes sense if MSI-X is supported. */
index 488c6f50df736968cee60af48d0360924a9f2e2b..92fa3e1ea65cca564907a43a78a859b6063f7f65 100644 (file)
 #include <net/arp.h>
 #include <net/ip.h>
 #include <net/ip_fib.h>
+#include <net/ip6_fib.h>
 #include <net/ip6_route.h>
 #include <net/rtnetlink.h>
 #include <net/route.h>
 #include <net/addrconf.h>
-#include <net/vrf.h>
+#include <net/l3mdev.h>
+
+#define RT_FL_TOS(oldflp4) \
+       ((oldflp4)->flowi4_tos & (IPTOS_RT_MASK | RTO_ONLINK))
 
 #define DRV_NAME       "vrf"
 #define DRV_VERSION    "1.0"
 
-#define vrf_is_slave(dev)   ((dev)->flags & IFF_SLAVE)
-
 #define vrf_master_get_rcu(dev) \
        ((struct net_device *)rcu_dereference(dev->rx_handler_data))
 
+struct slave {
+       struct list_head        list;
+       struct net_device       *dev;
+};
+
+struct slave_queue {
+       struct list_head        all_slaves;
+};
+
+struct net_vrf {
+       struct slave_queue      queue;
+       struct rtable           *rth;
+       struct rt6_info         *rt6;
+       u32                     tb_id;
+};
+
 struct pcpu_dstats {
        u64                     tx_pkts;
        u64                     tx_bytes;
@@ -58,9 +76,9 @@ static struct dst_entry *vrf_ip_check(struct dst_entry *dst, u32 cookie)
        return dst;
 }
 
-static int vrf_ip_local_out(struct sk_buff *skb)
+static int vrf_ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-       return ip_local_out(skb);
+       return ip_local_out(net, sk, skb);
 }
 
 static unsigned int vrf_v4_mtu(const struct dst_entry *dst)
@@ -88,12 +106,56 @@ static struct dst_ops vrf_dst_ops = {
        .default_advmss = vrf_default_advmss,
 };
 
+/* neighbor handling is done with actual device; do not want
+ * to flip skb->dev for those ndisc packets. This really fails
+ * for multiple next protocols (e.g., NEXTHDR_HOP). But it is
+ * a start.
+ */
+#if IS_ENABLED(CONFIG_IPV6)
+static bool check_ipv6_frame(const struct sk_buff *skb)
+{
+       const struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb->data;
+       size_t hlen = sizeof(*ipv6h);
+       bool rc = true;
+
+       if (skb->len < hlen)
+               goto out;
+
+       if (ipv6h->nexthdr == NEXTHDR_ICMP) {
+               const struct icmp6hdr *icmph;
+
+               if (skb->len < hlen + sizeof(*icmph))
+                       goto out;
+
+               icmph = (struct icmp6hdr *)(skb->data + sizeof(*ipv6h));
+               switch (icmph->icmp6_type) {
+               case NDISC_ROUTER_SOLICITATION:
+               case NDISC_ROUTER_ADVERTISEMENT:
+               case NDISC_NEIGHBOUR_SOLICITATION:
+               case NDISC_NEIGHBOUR_ADVERTISEMENT:
+               case NDISC_REDIRECT:
+                       rc = false;
+                       break;
+               }
+       }
+
+out:
+       return rc;
+}
+#else
+static bool check_ipv6_frame(const struct sk_buff *skb)
+{
+       return false;
+}
+#endif
+
 static bool is_ip_rx_frame(struct sk_buff *skb)
 {
        switch (skb->protocol) {
        case htons(ETH_P_IP):
-       case htons(ETH_P_IPV6):
                return true;
+       case htons(ETH_P_IPV6):
+               return check_ipv6_frame(skb);
        }
        return false;
 }
@@ -153,12 +215,53 @@ static struct rtnl_link_stats64 *vrf_get_stats64(struct net_device *dev,
        return stats;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb,
+                                          struct net_device *dev)
+{
+       const struct ipv6hdr *iph = ipv6_hdr(skb);
+       struct net *net = dev_net(skb->dev);
+       struct flowi6 fl6 = {
+               /* needed to match OIF rule */
+               .flowi6_oif = dev->ifindex,
+               .flowi6_iif = LOOPBACK_IFINDEX,
+               .daddr = iph->daddr,
+               .saddr = iph->saddr,
+               .flowlabel = ip6_flowinfo(iph),
+               .flowi6_mark = skb->mark,
+               .flowi6_proto = iph->nexthdr,
+               .flowi6_flags = FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF,
+       };
+       int ret = NET_XMIT_DROP;
+       struct dst_entry *dst;
+       struct dst_entry *dst_null = &net->ipv6.ip6_null_entry->dst;
+
+       dst = ip6_route_output(net, NULL, &fl6);
+       if (dst == dst_null)
+               goto err;
+
+       skb_dst_drop(skb);
+       skb_dst_set(skb, dst);
+
+       ret = ip6_local_out(net, skb->sk, skb);
+       if (unlikely(net_xmit_eval(ret)))
+               dev->stats.tx_errors++;
+       else
+               ret = NET_XMIT_SUCCESS;
+
+       return ret;
+err:
+       vrf_tx_error(dev, skb);
+       return NET_XMIT_DROP;
+}
+#else
 static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb,
                                           struct net_device *dev)
 {
        vrf_tx_error(dev, skb);
        return NET_XMIT_DROP;
 }
+#endif
 
 static int vrf_send_v4_prep(struct sk_buff *skb, struct flowi4 *fl4,
                            struct net_device *vrf_dev)
@@ -193,7 +296,7 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb,
                .flowi4_oif = vrf_dev->ifindex,
                .flowi4_iif = LOOPBACK_IFINDEX,
                .flowi4_tos = RT_TOS(ip4h->tos),
-               .flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_VRFSRC |
+               .flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_L3MDEV_SRC |
                                FLOWI_FLAG_SKIP_NH_OIF,
                .daddr = ip4h->daddr,
        };
@@ -206,7 +309,7 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb,
                                               RT_SCOPE_LINK);
        }
 
-       ret = ip_local_out(skb);
+       ret = ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb);
        if (unlikely(net_xmit_eval(ret)))
                vrf_dev->stats.tx_errors++;
        else
@@ -253,8 +356,159 @@ static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev)
        return ret;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static struct dst_entry *vrf_ip6_check(struct dst_entry *dst, u32 cookie)
+{
+       return dst;
+}
+
+static struct dst_ops vrf_dst_ops6 = {
+       .family         = AF_INET6,
+       .local_out      = ip6_local_out,
+       .check          = vrf_ip6_check,
+       .mtu            = vrf_v4_mtu,
+       .destroy        = vrf_dst_destroy,
+       .default_advmss = vrf_default_advmss,
+};
+
+static int init_dst_ops6_kmem_cachep(void)
+{
+       vrf_dst_ops6.kmem_cachep = kmem_cache_create("vrf_ip6_dst_cache",
+                                                    sizeof(struct rt6_info),
+                                                    0,
+                                                    SLAB_HWCACHE_ALIGN,
+                                                    NULL);
+
+       if (!vrf_dst_ops6.kmem_cachep)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void free_dst_ops6_kmem_cachep(void)
+{
+       kmem_cache_destroy(vrf_dst_ops6.kmem_cachep);
+}
+
+static int vrf_input6(struct sk_buff *skb)
+{
+       skb->dev->stats.rx_errors++;
+       kfree_skb(skb);
+       return 0;
+}
+
+/* modelled after ip6_finish_output2 */
+static int vrf_finish_output6(struct net *net, struct sock *sk,
+                             struct sk_buff *skb)
+{
+       struct dst_entry *dst = skb_dst(skb);
+       struct net_device *dev = dst->dev;
+       struct neighbour *neigh;
+       struct in6_addr *nexthop;
+       int ret;
+
+       skb->protocol = htons(ETH_P_IPV6);
+       skb->dev = dev;
+
+       rcu_read_lock_bh();
+       nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr);
+       neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
+       if (unlikely(!neigh))
+               neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
+       if (!IS_ERR(neigh)) {
+               ret = dst_neigh_output(dst, neigh, skb);
+               rcu_read_unlock_bh();
+               return ret;
+       }
+       rcu_read_unlock_bh();
+
+       IP6_INC_STATS(dev_net(dst->dev),
+                     ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
+       kfree_skb(skb);
+       return -EINVAL;
+}
+
+/* modelled after ip6_output */
+static int vrf_output6(struct net *net, struct sock *sk, struct sk_buff *skb)
+{
+       return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
+                           net, sk, skb, NULL, skb_dst(skb)->dev,
+                           vrf_finish_output6,
+                           !(IP6CB(skb)->flags & IP6SKB_REROUTED));
+}
+
+static void vrf_rt6_destroy(struct net_vrf *vrf)
+{
+       dst_destroy(&vrf->rt6->dst);
+       free_percpu(vrf->rt6->rt6i_pcpu);
+       vrf->rt6 = NULL;
+}
+
+static int vrf_rt6_create(struct net_device *dev)
+{
+       struct net_vrf *vrf = netdev_priv(dev);
+       struct dst_entry *dst;
+       struct rt6_info *rt6;
+       int cpu;
+       int rc = -ENOMEM;
+
+       rt6 = dst_alloc(&vrf_dst_ops6, dev, 0,
+                       DST_OBSOLETE_NONE,
+                       (DST_HOST | DST_NOPOLICY | DST_NOXFRM));
+       if (!rt6)
+               goto out;
+
+       dst = &rt6->dst;
+
+       rt6->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_KERNEL);
+       if (!rt6->rt6i_pcpu) {
+               dst_destroy(dst);
+               goto out;
+       }
+       for_each_possible_cpu(cpu) {
+               struct rt6_info **p = per_cpu_ptr(rt6->rt6i_pcpu, cpu);
+               *p =  NULL;
+       }
+
+       memset(dst + 1, 0, sizeof(*rt6) - sizeof(*dst));
+
+       INIT_LIST_HEAD(&rt6->rt6i_siblings);
+       INIT_LIST_HEAD(&rt6->rt6i_uncached);
+
+       rt6->dst.input  = vrf_input6;
+       rt6->dst.output = vrf_output6;
+
+       rt6->rt6i_table = fib6_get_table(dev_net(dev), vrf->tb_id);
+
+       atomic_set(&rt6->dst.__refcnt, 2);
+
+       vrf->rt6 = rt6;
+       rc = 0;
+out:
+       return rc;
+}
+#else
+static int init_dst_ops6_kmem_cachep(void)
+{
+       return 0;
+}
+
+static void free_dst_ops6_kmem_cachep(void)
+{
+}
+
+static void vrf_rt6_destroy(struct net_vrf *vrf)
+{
+}
+
+static int vrf_rt6_create(struct net_device *dev)
+{
+       return 0;
+}
+#endif
+
 /* modelled after ip_finish_output2 */
-static int vrf_finish_output(struct sock *sk, struct sk_buff *skb)
+static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct rtable *rt = (struct rtable *)dst;
@@ -296,17 +550,17 @@ err:
        return ret;
 }
 
-static int vrf_output(struct sock *sk, struct sk_buff *skb)
+static int vrf_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct net_device *dev = skb_dst(skb)->dev;
 
-       IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
+       IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
 
        skb->dev = dev;
        skb->protocol = htons(ETH_P_IP);
 
-       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb,
-                           NULL, dev,
+       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
+                           net, sk, skb, NULL, dev,
                            vrf_finish_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
@@ -321,6 +575,7 @@ static void vrf_rtable_destroy(struct net_vrf *vrf)
 
 static struct rtable *vrf_rtable_create(struct net_device *dev)
 {
+       struct net_vrf *vrf = netdev_priv(dev);
        struct rtable *rth;
 
        rth = dst_alloc(&vrf_dst_ops, dev, 2,
@@ -336,6 +591,7 @@ static struct rtable *vrf_rtable_create(struct net_device *dev)
                rth->rt_pmtu    = 0;
                rth->rt_gateway = 0;
                rth->rt_uses_gateway = 0;
+               rth->rt_table_id = vrf->tb_id;
                INIT_LIST_HEAD(&rth->rt_uncached);
                rth->rt_uncached_list = NULL;
        }
@@ -392,18 +648,15 @@ static void __vrf_insert_slave(struct slave_queue *queue, struct slave *slave)
 
 static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 {
-       struct net_vrf_dev *vrf_ptr = kmalloc(sizeof(*vrf_ptr), GFP_KERNEL);
        struct slave *slave = kzalloc(sizeof(*slave), GFP_KERNEL);
        struct net_vrf *vrf = netdev_priv(dev);
        struct slave_queue *queue = &vrf->queue;
        int ret = -ENOMEM;
 
-       if (!slave || !vrf_ptr)
+       if (!slave)
                goto out_fail;
 
        slave->dev = port_dev;
-       vrf_ptr->ifindex = dev->ifindex;
-       vrf_ptr->tb_id = vrf->tb_id;
 
        /* register the packet handler for slave ports */
        ret = netdev_rx_handler_register(port_dev, vrf_handle_frame, dev);
@@ -418,9 +671,8 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
        if (ret < 0)
                goto out_unregister;
 
-       port_dev->flags |= IFF_SLAVE;
+       port_dev->priv_flags |= IFF_L3MDEV_SLAVE;
        __vrf_insert_slave(queue, slave);
-       rcu_assign_pointer(port_dev->vrf_ptr, vrf_ptr);
        cycle_netdev(port_dev);
 
        return 0;
@@ -428,14 +680,13 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 out_unregister:
        netdev_rx_handler_unregister(port_dev);
 out_fail:
-       kfree(vrf_ptr);
        kfree(slave);
        return ret;
 }
 
 static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 {
-       if (netif_is_vrf(port_dev) || vrf_is_slave(port_dev))
+       if (netif_is_l3_master(port_dev) || netif_is_l3_slave(port_dev))
                return -EINVAL;
 
        return do_vrf_add_slave(dev, port_dev);
@@ -444,21 +695,15 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 /* inverse of do_vrf_add_slave */
 static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
 {
-       struct net_vrf_dev *vrf_ptr = rtnl_dereference(port_dev->vrf_ptr);
        struct net_vrf *vrf = netdev_priv(dev);
        struct slave_queue *queue = &vrf->queue;
        struct slave *slave;
 
-       RCU_INIT_POINTER(port_dev->vrf_ptr, NULL);
-
        netdev_upper_dev_unlink(port_dev, dev);
-       port_dev->flags &= ~IFF_SLAVE;
+       port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
 
        netdev_rx_handler_unregister(port_dev);
 
-       /* after netdev_rx_handler_unregister for synchronize_rcu */
-       kfree(vrf_ptr);
-
        cycle_netdev(port_dev);
 
        slave = __vrf_find_slave_dev(queue, port_dev);
@@ -483,6 +728,7 @@ static void vrf_dev_uninit(struct net_device *dev)
        struct slave *slave, *next;
 
        vrf_rtable_destroy(vrf);
+       vrf_rt6_destroy(vrf);
 
        list_for_each_entry_safe(slave, next, head, list)
                vrf_del_slave(dev, slave->dev);
@@ -506,10 +752,15 @@ static int vrf_dev_init(struct net_device *dev)
        if (!vrf->rth)
                goto out_stats;
 
+       if (vrf_rt6_create(dev) != 0)
+               goto out_rth;
+
        dev->flags = IFF_MASTER | IFF_NOARP;
 
        return 0;
 
+out_rth:
+       vrf_rtable_destroy(vrf);
 out_stats:
        free_percpu(dev->dstats);
        dev->dstats = NULL;
@@ -526,6 +777,85 @@ static const struct net_device_ops vrf_netdev_ops = {
        .ndo_del_slave          = vrf_del_slave,
 };
 
+static u32 vrf_fib_table(const struct net_device *dev)
+{
+       struct net_vrf *vrf = netdev_priv(dev);
+
+       return vrf->tb_id;
+}
+
+static struct rtable *vrf_get_rtable(const struct net_device *dev,
+                                    const struct flowi4 *fl4)
+{
+       struct rtable *rth = NULL;
+
+       if (!(fl4->flowi4_flags & FLOWI_FLAG_L3MDEV_SRC)) {
+               struct net_vrf *vrf = netdev_priv(dev);
+
+               rth = vrf->rth;
+               atomic_inc(&rth->dst.__refcnt);
+       }
+
+       return rth;
+}
+
+/* called under rcu_read_lock */
+static void vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4)
+{
+       struct fib_result res = { .tclassid = 0 };
+       struct net *net = dev_net(dev);
+       u32 orig_tos = fl4->flowi4_tos;
+       u8 flags = fl4->flowi4_flags;
+       u8 scope = fl4->flowi4_scope;
+       u8 tos = RT_FL_TOS(fl4);
+
+       if (unlikely(!fl4->daddr))
+               return;
+
+       fl4->flowi4_flags |= FLOWI_FLAG_SKIP_NH_OIF;
+       fl4->flowi4_iif = LOOPBACK_IFINDEX;
+       fl4->flowi4_tos = tos & IPTOS_RT_MASK;
+       fl4->flowi4_scope = ((tos & RTO_ONLINK) ?
+                            RT_SCOPE_LINK : RT_SCOPE_UNIVERSE);
+
+       if (!fib_lookup(net, fl4, &res, 0)) {
+               if (res.type == RTN_LOCAL)
+                       fl4->saddr = res.fi->fib_prefsrc ? : fl4->daddr;
+               else
+                       fib_select_path(net, &res, fl4, -1);
+       }
+
+       fl4->flowi4_flags = flags;
+       fl4->flowi4_tos = orig_tos;
+       fl4->flowi4_scope = scope;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static struct dst_entry *vrf_get_rt6_dst(const struct net_device *dev,
+                                        const struct flowi6 *fl6)
+{
+       struct rt6_info *rt = NULL;
+
+       if (!(fl6->flowi6_flags & FLOWI_FLAG_L3MDEV_SRC)) {
+               struct net_vrf *vrf = netdev_priv(dev);
+
+               rt = vrf->rt6;
+               atomic_inc(&rt->dst.__refcnt);
+       }
+
+       return (struct dst_entry *)rt;
+}
+#endif
+
+static const struct l3mdev_ops vrf_l3mdev_ops = {
+       .l3mdev_fib_table       = vrf_fib_table,
+       .l3mdev_get_rtable      = vrf_get_rtable,
+       .l3mdev_get_saddr       = vrf_get_saddr,
+#if IS_ENABLED(CONFIG_IPV6)
+       .l3mdev_get_rt6_dst     = vrf_get_rt6_dst,
+#endif
+};
+
 static void vrf_get_drvinfo(struct net_device *dev,
                            struct ethtool_drvinfo *info)
 {
@@ -543,6 +873,7 @@ static void vrf_setup(struct net_device *dev)
 
        /* Initialize the device structure. */
        dev->netdev_ops = &vrf_netdev_ops;
+       dev->l3mdev_ops = &vrf_l3mdev_ops;
        dev->ethtool_ops = &vrf_ethtool_ops;
        dev->destructor = free_netdev;
 
@@ -569,10 +900,6 @@ static int vrf_validate(struct nlattr *tb[], struct nlattr *data[])
 
 static void vrf_dellink(struct net_device *dev, struct list_head *head)
 {
-       struct net_vrf_dev *vrf_ptr = rtnl_dereference(dev->vrf_ptr);
-
-       RCU_INIT_POINTER(dev->vrf_ptr, NULL);
-       kfree_rcu(vrf_ptr, rcu);
        unregister_netdevice_queue(dev, head);
 }
 
@@ -580,7 +907,6 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev,
                       struct nlattr *tb[], struct nlattr *data[])
 {
        struct net_vrf *vrf = netdev_priv(dev);
-       struct net_vrf_dev *vrf_ptr;
        int err;
 
        if (!data || !data[IFLA_VRF_TABLE])
@@ -588,26 +914,15 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev,
 
        vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]);
 
-       dev->priv_flags |= IFF_VRF_MASTER;
-
-       err = -ENOMEM;
-       vrf_ptr = kmalloc(sizeof(*dev->vrf_ptr), GFP_KERNEL);
-       if (!vrf_ptr)
-               goto out_fail;
-
-       vrf_ptr->ifindex = dev->ifindex;
-       vrf_ptr->tb_id = vrf->tb_id;
+       dev->priv_flags |= IFF_L3MDEV_MASTER;
 
        err = register_netdevice(dev);
        if (err < 0)
                goto out_fail;
 
-       rcu_assign_pointer(dev->vrf_ptr, vrf_ptr);
-
        return 0;
 
 out_fail:
-       kfree(vrf_ptr);
        free_netdev(dev);
        return err;
 }
@@ -651,10 +966,9 @@ static int vrf_device_event(struct notifier_block *unused,
 
        /* only care about unregister events to drop slave references */
        if (event == NETDEV_UNREGISTER) {
-               struct net_vrf_dev *vrf_ptr = rtnl_dereference(dev->vrf_ptr);
                struct net_device *vrf_dev;
 
-               if (!vrf_ptr || netif_is_vrf(dev))
+               if (!netif_is_l3_slave(dev))
                        goto out;
 
                vrf_dev = netdev_master_upper_dev_get(dev);
@@ -681,6 +995,10 @@ static int __init vrf_init_module(void)
        if (!vrf_dst_ops.kmem_cachep)
                return -ENOMEM;
 
+       rc = init_dst_ops6_kmem_cachep();
+       if (rc != 0)
+               goto error2;
+
        register_netdevice_notifier(&vrf_notifier_block);
 
        rc = rtnl_link_register(&vrf_link_ops);
@@ -691,6 +1009,8 @@ static int __init vrf_init_module(void)
 
 error:
        unregister_netdevice_notifier(&vrf_notifier_block);
+       free_dst_ops6_kmem_cachep();
+error2:
        kmem_cache_destroy(vrf_dst_ops.kmem_cachep);
        return rc;
 }
@@ -700,6 +1020,7 @@ static void __exit vrf_cleanup_module(void)
        rtnl_link_unregister(&vrf_link_ops);
        unregister_netdevice_notifier(&vrf_notifier_block);
        kmem_cache_destroy(vrf_dst_ops.kmem_cachep);
+       free_dst_ops6_kmem_cachep();
 }
 
 module_init(vrf_init_module);
index afdc65fd5bc57c9101c9ffcb4aeac7cfe2431a63..6369a5734d4c3e899e96ec74469b0af4b3bca865 100644 (file)
@@ -75,8 +75,7 @@ static struct rtnl_link_ops vxlan_link_ops;
 
 static const u8 all_zeros_mac[ETH_ALEN];
 
-static struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
-                                        bool no_share, u32 flags);
+static int vxlan_sock_add(struct vxlan_dev *vxlan);
 
 /* per-network namespace private data for this module */
 struct vxlan_net {
@@ -994,19 +993,30 @@ static bool vxlan_snoop(struct net_device *dev,
 static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev)
 {
        struct vxlan_dev *vxlan;
+       unsigned short family = dev->default_dst.remote_ip.sa.sa_family;
 
        /* The vxlan_sock is only used by dev, leaving group has
         * no effect on other vxlan devices.
         */
-       if (atomic_read(&dev->vn_sock->refcnt) == 1)
+       if (family == AF_INET && dev->vn4_sock &&
+           atomic_read(&dev->vn4_sock->refcnt) == 1)
                return false;
+#if IS_ENABLED(CONFIG_IPV6)
+       if (family == AF_INET6 && dev->vn6_sock &&
+           atomic_read(&dev->vn6_sock->refcnt) == 1)
+               return false;
+#endif
 
        list_for_each_entry(vxlan, &vn->vxlan_list, next) {
                if (!netif_running(vxlan->dev) || vxlan == dev)
                        continue;
 
-               if (vxlan->vn_sock != dev->vn_sock)
+               if (family == AF_INET && vxlan->vn4_sock != dev->vn4_sock)
+                       continue;
+#if IS_ENABLED(CONFIG_IPV6)
+               if (family == AF_INET6 && vxlan->vn6_sock != dev->vn6_sock)
                        continue;
+#endif
 
                if (!vxlan_addr_equal(&vxlan->default_dst.remote_ip,
                                      &dev->default_dst.remote_ip))
@@ -1022,15 +1032,16 @@ static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev)
        return false;
 }
 
-static void vxlan_sock_release(struct vxlan_sock *vs)
+static void __vxlan_sock_release(struct vxlan_sock *vs)
 {
-       struct sock *sk = vs->sock->sk;
-       struct net *net = sock_net(sk);
-       struct vxlan_net *vn = net_generic(net, vxlan_net_id);
+       struct vxlan_net *vn;
 
+       if (!vs)
+               return;
        if (!atomic_dec_and_test(&vs->refcnt))
                return;
 
+       vn = net_generic(sock_net(vs->sock->sk), vxlan_net_id);
        spin_lock(&vn->sock_lock);
        hlist_del_rcu(&vs->hlist);
        vxlan_notify_del_rx_port(vs);
@@ -1039,32 +1050,43 @@ static void vxlan_sock_release(struct vxlan_sock *vs)
        queue_work(vxlan_wq, &vs->del_work);
 }
 
+static void vxlan_sock_release(struct vxlan_dev *vxlan)
+{
+       __vxlan_sock_release(vxlan->vn4_sock);
+#if IS_ENABLED(CONFIG_IPV6)
+       __vxlan_sock_release(vxlan->vn6_sock);
+#endif
+}
+
 /* Update multicast group membership when first VNI on
  * multicast address is brought up
  */
 static int vxlan_igmp_join(struct vxlan_dev *vxlan)
 {
-       struct vxlan_sock *vs = vxlan->vn_sock;
-       struct sock *sk = vs->sock->sk;
+       struct sock *sk;
        union vxlan_addr *ip = &vxlan->default_dst.remote_ip;
        int ifindex = vxlan->default_dst.remote_ifindex;
        int ret = -EINVAL;
 
-       lock_sock(sk);
        if (ip->sa.sa_family == AF_INET) {
                struct ip_mreqn mreq = {
                        .imr_multiaddr.s_addr   = ip->sin.sin_addr.s_addr,
                        .imr_ifindex            = ifindex,
                };
 
+               sk = vxlan->vn4_sock->sock->sk;
+               lock_sock(sk);
                ret = ip_mc_join_group(sk, &mreq);
+               release_sock(sk);
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
+               sk = vxlan->vn6_sock->sock->sk;
+               lock_sock(sk);
                ret = ipv6_stub->ipv6_sock_mc_join(sk, ifindex,
                                                   &ip->sin6.sin6_addr);
+               release_sock(sk);
 #endif
        }
-       release_sock(sk);
 
        return ret;
 }
@@ -1072,27 +1094,30 @@ static int vxlan_igmp_join(struct vxlan_dev *vxlan)
 /* Inverse of vxlan_igmp_join when last VNI is brought down */
 static int vxlan_igmp_leave(struct vxlan_dev *vxlan)
 {
-       struct vxlan_sock *vs = vxlan->vn_sock;
-       struct sock *sk = vs->sock->sk;
+       struct sock *sk;
        union vxlan_addr *ip = &vxlan->default_dst.remote_ip;
        int ifindex = vxlan->default_dst.remote_ifindex;
        int ret = -EINVAL;
 
-       lock_sock(sk);
        if (ip->sa.sa_family == AF_INET) {
                struct ip_mreqn mreq = {
                        .imr_multiaddr.s_addr   = ip->sin.sin_addr.s_addr,
                        .imr_ifindex            = ifindex,
                };
 
+               sk = vxlan->vn4_sock->sock->sk;
+               lock_sock(sk);
                ret = ip_mc_leave_group(sk, &mreq);
+               release_sock(sk);
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
+               sk = vxlan->vn6_sock->sock->sk;
+               lock_sock(sk);
                ret = ipv6_stub->ipv6_sock_mc_drop(sk, ifindex,
                                                   &ip->sin6.sin6_addr);
+               release_sock(sk);
 #endif
        }
-       release_sock(sk);
 
        return ret;
 }
@@ -1873,8 +1898,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 {
        struct ip_tunnel_info *info;
        struct vxlan_dev *vxlan = netdev_priv(dev);
-       struct sock *sk = vxlan->vn_sock->sock->sk;
-       unsigned short family = vxlan_get_sk_family(vxlan->vn_sock);
+       struct sock *sk;
        struct rtable *rt = NULL;
        const struct iphdr *old_iph;
        struct flowi4 fl4;
@@ -1901,13 +1925,10 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                                  dev->name);
                        goto drop;
                }
-               if (family != ip_tunnel_info_af(info))
-                       goto drop;
-
                dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port;
                vni = be64_to_cpu(info->key.tun_id);
-               remote_ip.sa.sa_family = family;
-               if (family == AF_INET)
+               remote_ip.sa.sa_family = ip_tunnel_info_af(info);
+               if (remote_ip.sa.sa_family == AF_INET)
                        remote_ip.sin.sin_addr.s_addr = info->key.u.ipv4.dst;
                else
                        remote_ip.sin6.sin6_addr = info->key.u.ipv6.dst;
@@ -1952,6 +1973,10 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
        }
 
        if (dst->sa.sa_family == AF_INET) {
+               if (!vxlan->vn4_sock)
+                       goto drop;
+               sk = vxlan->vn4_sock->sock->sk;
+
                if (info && (info->key.tun_flags & TUNNEL_DONT_FRAGMENT))
                        df = htons(IP_DF);
 
@@ -2013,6 +2038,10 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                struct flowi6 fl6;
                u32 rt6i_flags;
 
+               if (!vxlan->vn6_sock)
+                       goto drop;
+               sk = vxlan->vn6_sock->sock->sk;
+
                memset(&fl6, 0, sizeof(fl6));
                fl6.flowi6_oif = rdst ? rdst->remote_ifindex : 0;
                fl6.daddr = dst->sin6.sin6_addr;
@@ -2204,7 +2233,6 @@ static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan)
        struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
        __u32 vni = vxlan->default_dst.remote_vni;
 
-       vxlan->vn_sock = vs;
        spin_lock(&vn->sock_lock);
        hlist_add_head_rcu(&vxlan->hlist, vni_head(vs, vni));
        spin_unlock(&vn->sock_lock);
@@ -2244,22 +2272,18 @@ static void vxlan_uninit(struct net_device *dev)
 static int vxlan_open(struct net_device *dev)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
-       struct vxlan_sock *vs;
-       int ret = 0;
-
-       vs = vxlan_sock_add(vxlan->net, vxlan->cfg.dst_port,
-                           vxlan->cfg.no_share, vxlan->flags);
-       if (IS_ERR(vs))
-               return PTR_ERR(vs);
+       int ret;
 
-       vxlan_vs_add_dev(vs, vxlan);
+       ret = vxlan_sock_add(vxlan);
+       if (ret < 0)
+               return ret;
 
        if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) {
                ret = vxlan_igmp_join(vxlan);
                if (ret == -EADDRINUSE)
                        ret = 0;
                if (ret) {
-                       vxlan_sock_release(vs);
+                       vxlan_sock_release(vxlan);
                        return ret;
                }
        }
@@ -2294,7 +2318,6 @@ static int vxlan_stop(struct net_device *dev)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
-       struct vxlan_sock *vs = vxlan->vn_sock;
        int ret = 0;
 
        if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip) &&
@@ -2304,7 +2327,7 @@ static int vxlan_stop(struct net_device *dev)
        del_timer_sync(&vxlan->age_timer);
 
        vxlan_flush(vxlan);
-       vxlan_sock_release(vs);
+       vxlan_sock_release(vxlan);
 
        return ret;
 }
@@ -2337,6 +2360,46 @@ static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
        return 0;
 }
 
+static int egress_ipv4_tun_info(struct net_device *dev, struct sk_buff *skb,
+                               struct ip_tunnel_info *info,
+                               __be16 sport, __be16 dport)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct rtable *rt;
+       struct flowi4 fl4;
+
+       memset(&fl4, 0, sizeof(fl4));
+       fl4.flowi4_tos = RT_TOS(info->key.tos);
+       fl4.flowi4_mark = skb->mark;
+       fl4.flowi4_proto = IPPROTO_UDP;
+       fl4.daddr = info->key.u.ipv4.dst;
+
+       rt = ip_route_output_key(vxlan->net, &fl4);
+       if (IS_ERR(rt))
+               return PTR_ERR(rt);
+       ip_rt_put(rt);
+
+       info->key.u.ipv4.src = fl4.saddr;
+       info->key.tp_src = sport;
+       info->key.tp_dst = dport;
+       return 0;
+}
+
+static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct ip_tunnel_info *info = skb_tunnel_info(skb);
+       __be16 sport, dport;
+
+       sport = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min,
+                                 vxlan->cfg.port_max, true);
+       dport = info->key.tp_dst ? : vxlan->cfg.dst_port;
+
+       if (ip_tunnel_info_af(info) == AF_INET)
+               return egress_ipv4_tun_info(dev, skb, info, sport, dport);
+       return -EINVAL;
+}
+
 static const struct net_device_ops vxlan_netdev_ops = {
        .ndo_init               = vxlan_init,
        .ndo_uninit             = vxlan_uninit,
@@ -2351,6 +2414,7 @@ static const struct net_device_ops vxlan_netdev_ops = {
        .ndo_fdb_add            = vxlan_fdb_add,
        .ndo_fdb_del            = vxlan_fdb_delete,
        .ndo_fdb_dump           = vxlan_fdb_dump,
+       .ndo_fill_metadata_dst  = vxlan_fill_metadata_dst,
 };
 
 /* Info for udev, that this is a virtual tunnel endpoint */
@@ -2540,14 +2604,13 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
 }
 
 /* Create new listen socket if needed */
-static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
-                                             u32 flags)
+static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
+                                             __be16 port, u32 flags)
 {
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        struct vxlan_sock *vs;
        struct socket *sock;
        unsigned int h;
-       bool ipv6 = !!(flags & VXLAN_F_IPV6);
        struct udp_tunnel_sock_cfg tunnel_cfg;
 
        vs = kzalloc(sizeof(*vs), GFP_KERNEL);
@@ -2592,27 +2655,53 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
        return vs;
 }
 
-static struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
-                                        bool no_share, u32 flags)
+static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
 {
-       struct vxlan_net *vn = net_generic(net, vxlan_net_id);
-       struct vxlan_sock *vs;
-       bool ipv6 = flags & VXLAN_F_IPV6;
+       struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
+       struct vxlan_sock *vs = NULL;
 
-       if (!no_share) {
+       if (!vxlan->cfg.no_share) {
                spin_lock(&vn->sock_lock);
-               vs = vxlan_find_sock(net, ipv6 ? AF_INET6 : AF_INET, port,
-                                    flags);
-               if (vs) {
-                       if (!atomic_add_unless(&vs->refcnt, 1, 0))
-                               vs = ERR_PTR(-EBUSY);
+               vs = vxlan_find_sock(vxlan->net, ipv6 ? AF_INET6 : AF_INET,
+                                    vxlan->cfg.dst_port, vxlan->flags);
+               if (vs && !atomic_add_unless(&vs->refcnt, 1, 0)) {
                        spin_unlock(&vn->sock_lock);
-                       return vs;
+                       return -EBUSY;
                }
                spin_unlock(&vn->sock_lock);
        }
+       if (!vs)
+               vs = vxlan_socket_create(vxlan->net, ipv6,
+                                        vxlan->cfg.dst_port, vxlan->flags);
+       if (IS_ERR(vs))
+               return PTR_ERR(vs);
+#if IS_ENABLED(CONFIG_IPV6)
+       if (ipv6)
+               vxlan->vn6_sock = vs;
+       else
+#endif
+               vxlan->vn4_sock = vs;
+       vxlan_vs_add_dev(vs, vxlan);
+       return 0;
+}
+
+static int vxlan_sock_add(struct vxlan_dev *vxlan)
+{
+       bool ipv6 = vxlan->flags & VXLAN_F_IPV6;
+       bool metadata = vxlan->flags & VXLAN_F_COLLECT_METADATA;
+       int ret = 0;
 
-       return vxlan_socket_create(net, port, flags);
+       vxlan->vn4_sock = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+       vxlan->vn6_sock = NULL;
+       if (ipv6 || metadata)
+               ret = __vxlan_sock_add(vxlan, true);
+#endif
+       if (!ret && (!ipv6 || metadata))
+               ret = __vxlan_sock_add(vxlan, false);
+       if (ret < 0)
+               vxlan_sock_release(vxlan);
+       return ret;
 }
 
 static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
@@ -2621,6 +2710,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
        struct vxlan_net *vn = net_generic(src_net, vxlan_net_id);
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_rdst *dst = &vxlan->default_dst;
+       unsigned short needed_headroom = ETH_HLEN;
        int err;
        bool use_ipv6 = false;
        __be16 default_port = vxlan->cfg.dst_port;
@@ -2640,6 +2730,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
                if (!IS_ENABLED(CONFIG_IPV6))
                        return -EPFNOSUPPORT;
                use_ipv6 = true;
+               vxlan->flags |= VXLAN_F_IPV6;
        }
 
        if (conf->remote_ifindex) {
@@ -2660,22 +2751,21 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
                                pr_info("IPv6 is disabled via sysctl\n");
                                return -EPERM;
                        }
-                       vxlan->flags |= VXLAN_F_IPV6;
                }
 #endif
 
                if (!conf->mtu)
                        dev->mtu = lowerdev->mtu - (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM);
 
-               dev->needed_headroom = lowerdev->hard_header_len +
-                                      (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM);
-       } else if (use_ipv6) {
-               vxlan->flags |= VXLAN_F_IPV6;
-               dev->needed_headroom = ETH_HLEN + VXLAN6_HEADROOM;
-       } else {
-               dev->needed_headroom = ETH_HLEN + VXLAN_HEADROOM;
+               needed_headroom = lowerdev->hard_header_len;
        }
 
+       if (use_ipv6 || conf->flags & VXLAN_F_COLLECT_METADATA)
+               needed_headroom += VXLAN6_HEADROOM;
+       else
+               needed_headroom += VXLAN_HEADROOM;
+       dev->needed_headroom = needed_headroom;
+
        memcpy(&vxlan->cfg, conf, sizeof(*conf));
        if (!vxlan->cfg.dst_port)
                vxlan->cfg.dst_port = default_port;
index a63ab2e831054b372b551788e9106ebf29bc925c..f9f94229bf1ba5902fbea46b6948540b0cc10c03 100644 (file)
@@ -214,8 +214,6 @@ config USB_NET_RNDIS_WLAN
 
          If you choose to build a module, it'll be called rndis_wlan.
 
-source "drivers/net/wireless/rtl818x/Kconfig"
-
 config ADM8211
        tristate "ADMtek ADM8211 support"
        depends on MAC80211 && PCI
@@ -243,6 +241,8 @@ config ADM8211
 
          Thanks to Infineon-ADMtek for their support of this driver.
 
+source "drivers/net/wireless/realtek/rtl818x/Kconfig"
+
 config MAC80211_HWSIM
        tristate "Simulated radio testing tool for mac80211"
        depends on MAC80211
@@ -278,7 +278,8 @@ source "drivers/net/wireless/orinoco/Kconfig"
 source "drivers/net/wireless/p54/Kconfig"
 source "drivers/net/wireless/rt2x00/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
-source "drivers/net/wireless/rtlwifi/Kconfig"
+source "drivers/net/wireless/realtek/rtlwifi/Kconfig"
+source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zd1211rw/Kconfig"
 source "drivers/net/wireless/mwifiex/Kconfig"
index 6b9e729dd8acbf0af1a545109b0b304d400e93d4..740fdd353c5ddc310a1b9c0e9f84fd69601a5831 100644 (file)
@@ -22,9 +22,7 @@ obj-$(CONFIG_HOSTAP)          += hostap/
 obj-$(CONFIG_B43)              += b43/
 obj-$(CONFIG_B43LEGACY)                += b43legacy/
 obj-$(CONFIG_ZD1211RW)         += zd1211rw/
-obj-$(CONFIG_RTL8180)          += rtl818x/
-obj-$(CONFIG_RTL8187)          += rtl818x/
-obj-$(CONFIG_RTLWIFI)          += rtlwifi/
+obj-$(CONFIG_WLAN)             += realtek/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)     += ray_cs.o
index d0c97c2200267bc52a0910a54b173fa1c574b494..17c40f06f13e585a96e5e0db137d25534eae1036 100644 (file)
@@ -1231,12 +1231,13 @@ struct airo_info {
        dma_addr_t              shared_dma;
        pm_message_t            power;
        SsidRid                 *SSID;
-       APListRid               *APList;
+       APListRid               APList;
 #define        PCI_SHARED_LEN          2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
        char                    proc_name[IFNAMSIZ];
 
        int                     wep_capable;
        int                     max_wep_idx;
+       int                     last_auth;
 
        /* WPA-related stuff */
        unsigned int bssListFirst;
@@ -1847,11 +1848,6 @@ static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock)
        return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock);
 }
 
-static int readAPListRid(struct airo_info *ai, APListRid *aplr)
-{
-       return PC4500_readrid(ai, RID_APLIST, aplr, sizeof(*aplr), 1);
-}
-
 static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock)
 {
        return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock);
@@ -2412,7 +2408,6 @@ void stop_airo_card( struct net_device *dev, int freeres )
 
        kfree(ai->flash);
        kfree(ai->rssi);
-       kfree(ai->APList);
        kfree(ai->SSID);
        if (freeres) {
                /* PCMCIA frees this stuff, so only for PCI and ISA */
@@ -2808,6 +2803,7 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
        init_waitqueue_head (&ai->thr_wait);
        ai->tfm = NULL;
        add_airo_dev(ai);
+       ai->APList.len = cpu_to_le16(sizeof(struct APListRid));
 
        if (airo_networks_allocate (ai))
                goto err_out_free;
@@ -3041,6 +3037,11 @@ static void airo_process_scan_results (struct airo_info *ai) {
        }
 
 out:
+       /* write APList back (we cleared it in airo_set_scan) */
+       disable_MAC(ai, 2);
+       writeAPListRid(ai, &ai->APList, 0);
+       enable_MAC(ai, 0);
+
        ai->scan_timeout = 0;
        clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
        up(&ai->sem);
@@ -3266,6 +3267,7 @@ static void airo_handle_link(struct airo_info *ai)
                        wake_up_interruptible(&ai->thr_wait);
                } else
                        airo_send_event(ai->dev);
+               netif_carrier_on(ai->dev);
        } else if (!scan_forceloss) {
                if (auto_wep && !ai->expires) {
                        ai->expires = RUN_AT(3*HZ);
@@ -3276,6 +3278,9 @@ static void airo_handle_link(struct airo_info *ai)
                eth_zero_addr(wrqu.ap_addr.sa_data);
                wrqu.ap_addr.sa_family = ARPHRD_ETHER;
                wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL);
+               netif_carrier_off(ai->dev);
+       } else {
+               netif_carrier_off(ai->dev);
        }
 }
 
@@ -3608,16 +3613,18 @@ static void disable_MAC( struct airo_info *ai, int lock ) {
         Cmd cmd;
        Resp rsp;
 
-       if (lock && down_interruptible(&ai->sem))
+       if (lock == 1 && down_interruptible(&ai->sem))
                return;
 
        if (test_bit(FLAG_ENABLED, &ai->flags)) {
+               if (lock != 2) /* lock == 2 means don't disable carrier */
+                       netif_carrier_off(ai->dev);
                memset(&cmd, 0, sizeof(cmd));
                cmd.cmd = MAC_DISABLE; // disable in case already enabled
                issuecommand(ai, &cmd, &rsp);
                clear_bit(FLAG_ENABLED, &ai->flags);
        }
-       if (lock)
+       if (lock == 1)
                up(&ai->sem);
 }
 
@@ -3786,6 +3793,16 @@ badrx:
        }
 }
 
+static inline void set_auth_type(struct airo_info *local, int auth_type)
+{
+       local->config.authType = auth_type;
+       /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT).
+        * Used by airo_set_auth()
+        */
+       if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT)
+               local->last_auth = auth_type;
+}
+
 static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
 {
        Cmd cmd;
@@ -3836,8 +3853,6 @@ static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
                tdsRssiRid rssi_rid;
                CapabilityRid cap_rid;
 
-               kfree(ai->APList);
-               ai->APList = NULL;
                kfree(ai->SSID);
                ai->SSID = NULL;
                // general configuration (read/modify/write)
@@ -3862,7 +3877,7 @@ static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
                                                "level scale");
                }
                ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
-               ai->config.authType = AUTH_OPEN;
+               set_auth_type(ai, AUTH_OPEN);
                ai->config.modulation = MOD_CCK;
 
                if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) &&
@@ -4880,13 +4895,13 @@ static void proc_config_on_close(struct inode *inode, struct file *file)
                        line += 5;
                        switch( line[0] ) {
                        case 's':
-                               ai->config.authType = AUTH_SHAREDKEY;
+                               set_auth_type(ai, AUTH_SHAREDKEY);
                                break;
                        case 'e':
-                               ai->config.authType = AUTH_ENCRYPT;
+                               set_auth_type(ai, AUTH_ENCRYPT);
                                break;
                        default:
-                               ai->config.authType = AUTH_OPEN;
+                               set_auth_type(ai, AUTH_OPEN);
                                break;
                        }
                        set_bit (FLAG_COMMIT, &ai->flags);
@@ -5114,31 +5129,31 @@ static void proc_APList_on_close( struct inode *inode, struct file *file ) {
        struct proc_data *data = file->private_data;
        struct net_device *dev = PDE_DATA(inode);
        struct airo_info *ai = dev->ml_priv;
-       APListRid APList_rid;
+       APListRid *APList_rid = &ai->APList;
        int i;
 
        if ( !data->writelen ) return;
 
-       memset( &APList_rid, 0, sizeof(APList_rid) );
-       APList_rid.len = cpu_to_le16(sizeof(APList_rid));
+       memset(APList_rid, 0, sizeof(*APList_rid));
+       APList_rid->len = cpu_to_le16(sizeof(*APList_rid));
 
        for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) {
                int j;
                for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) {
                        switch(j%3) {
                        case 0:
-                               APList_rid.ap[i][j/3]=
+                               APList_rid->ap[i][j/3]=
                                        hex_to_bin(data->wbuffer[j+i*6*3])<<4;
                                break;
                        case 1:
-                               APList_rid.ap[i][j/3]|=
+                               APList_rid->ap[i][j/3]|=
                                        hex_to_bin(data->wbuffer[j+i*6*3]);
                                break;
                        }
                }
        }
        disable_MAC(ai, 1);
-       writeAPListRid(ai, &APList_rid, 1);
+       writeAPListRid(ai, APList_rid, 1);
        enable_MAC(ai, 1);
 }
 
@@ -5392,7 +5407,7 @@ static int proc_APList_open( struct inode *inode, struct file *file ) {
        struct airo_info *ai = dev->ml_priv;
        int i;
        char *ptr;
-       APListRid APList_rid;
+       APListRid *APList_rid = &ai->APList;
 
        if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
                return -ENOMEM;
@@ -5410,13 +5425,12 @@ static int proc_APList_open( struct inode *inode, struct file *file ) {
        }
        data->on_close = proc_APList_on_close;
 
-       readAPListRid(ai, &APList_rid);
        ptr = data->rbuffer;
        for( i = 0; i < 4; i++ ) {
 // We end when we find a zero MAC
-               if ( !*(int*)APList_rid.ap[i] &&
-                    !*(int*)&APList_rid.ap[i][2]) break;
-               ptr += sprintf(ptr, "%pM\n", APList_rid.ap[i]);
+               if ( !*(int*)APList_rid->ap[i] &&
+                    !*(int*)&APList_rid->ap[i][2]) break;
+               ptr += sprintf(ptr, "%pM\n", APList_rid->ap[i]);
        }
        if (i==0) ptr += sprintf(ptr, "Not using specific APs\n");
 
@@ -5580,15 +5594,10 @@ static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
        Cmd cmd;
        Resp rsp;
 
-       if (!ai->APList)
-               ai->APList = kmalloc(sizeof(APListRid), GFP_KERNEL);
-       if (!ai->APList)
-               return -ENOMEM;
        if (!ai->SSID)
                ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL);
        if (!ai->SSID)
                return -ENOMEM;
-       readAPListRid(ai, ai->APList);
        readSsidRid(ai, ai->SSID);
        memset(&cmd, 0, sizeof(cmd));
        /* the lock will be released at the end of the resume callback */
@@ -5636,11 +5645,7 @@ static int airo_pci_resume(struct pci_dev *pdev)
                kfree(ai->SSID);
                ai->SSID = NULL;
        }
-       if (ai->APList) {
-               writeAPListRid(ai, ai->APList, 0);
-               kfree(ai->APList);
-               ai->APList = NULL;
-       }
+       writeAPListRid(ai, &ai->APList, 0);
        writeConfigRid(ai, 0);
        enable_MAC(ai, 0);
        ai->power = PMSG_ON;
@@ -5938,7 +5943,7 @@ static int airo_set_wap(struct net_device *dev,
        struct airo_info *local = dev->ml_priv;
        Cmd cmd;
        Resp rsp;
-       APListRid APList_rid;
+       APListRid *APList_rid = &local->APList;
 
        if (awrq->sa_family != ARPHRD_ETHER)
                return -EINVAL;
@@ -5951,11 +5956,11 @@ static int airo_set_wap(struct net_device *dev,
                issuecommand(local, &cmd, &rsp);
                up(&local->sem);
        } else {
-               memset(&APList_rid, 0, sizeof(APList_rid));
-               APList_rid.len = cpu_to_le16(sizeof(APList_rid));
-               memcpy(APList_rid.ap[0], awrq->sa_data, ETH_ALEN);
+               memset(APList_rid, 0, sizeof(*APList_rid));
+               APList_rid->len = cpu_to_le16(sizeof(*APList_rid));
+               memcpy(APList_rid->ap[0], awrq->sa_data, ETH_ALEN);
                disable_MAC(local, 1);
-               writeAPListRid(local, &APList_rid, 1);
+               writeAPListRid(local, APList_rid, 1);
                enable_MAC(local, 1);
        }
        return 0;
@@ -6368,9 +6373,8 @@ static int airo_set_encode(struct net_device *dev,
                 * should be enabled (user may turn it off later)
                 * This is also how "iwconfig ethX key on" works */
                if((index == current_index) && (key.len > 0) &&
-                  (local->config.authType == AUTH_OPEN)) {
-                       local->config.authType = AUTH_ENCRYPT;
-               }
+                  (local->config.authType == AUTH_OPEN))
+                       set_auth_type(local, AUTH_ENCRYPT);
        } else {
                /* Do we want to just set the transmit key index ? */
                int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
@@ -6389,12 +6393,12 @@ static int airo_set_encode(struct net_device *dev,
                }
        }
        /* Read the flags */
-       if(dwrq->flags & IW_ENCODE_DISABLED)
-               local->config.authType = AUTH_OPEN;     // disable encryption
+       if (dwrq->flags & IW_ENCODE_DISABLED)
+               set_auth_type(local, AUTH_OPEN);        /* disable encryption */
        if(dwrq->flags & IW_ENCODE_RESTRICTED)
-               local->config.authType = AUTH_SHAREDKEY;        // Only Both
-       if(dwrq->flags & IW_ENCODE_OPEN)
-               local->config.authType = AUTH_ENCRYPT;  // Only Wep
+               set_auth_type(local, AUTH_SHAREDKEY);   /* Only Both */
+       if (dwrq->flags & IW_ENCODE_OPEN)
+               set_auth_type(local, AUTH_ENCRYPT);     /* Only Wep */
        /* Commit the changes to flags if needed */
        if (local->config.authType != currentAuthType)
                set_bit (FLAG_COMMIT, &local->flags);
@@ -6549,12 +6553,12 @@ static int airo_set_encodeext(struct net_device *dev,
        }
 
        /* Read the flags */
-       if(encoding->flags & IW_ENCODE_DISABLED)
-               local->config.authType = AUTH_OPEN;     // disable encryption
+       if (encoding->flags & IW_ENCODE_DISABLED)
+               set_auth_type(local, AUTH_OPEN);        /* disable encryption */
        if(encoding->flags & IW_ENCODE_RESTRICTED)
-               local->config.authType = AUTH_SHAREDKEY;        // Only Both
-       if(encoding->flags & IW_ENCODE_OPEN)
-               local->config.authType = AUTH_ENCRYPT;  // Only Wep
+               set_auth_type(local, AUTH_SHAREDKEY);   /* Only Both */
+       if (encoding->flags & IW_ENCODE_OPEN)
+               set_auth_type(local, AUTH_ENCRYPT);
        /* Commit the changes to flags if needed */
        if (local->config.authType != currentAuthType)
                set_bit (FLAG_COMMIT, &local->flags);
@@ -6659,9 +6663,9 @@ static int airo_set_auth(struct net_device *dev,
                if (param->value) {
                        /* Only change auth type if unencrypted */
                        if (currentAuthType == AUTH_OPEN)
-                               local->config.authType = AUTH_ENCRYPT;
+                               set_auth_type(local, AUTH_ENCRYPT);
                } else {
-                       local->config.authType = AUTH_OPEN;
+                       set_auth_type(local, AUTH_OPEN);
                }
 
                /* Commit the changes to flags if needed */
@@ -6670,13 +6674,14 @@ static int airo_set_auth(struct net_device *dev,
                break;
 
        case IW_AUTH_80211_AUTH_ALG: {
-                       /* FIXME: What about AUTH_OPEN?  This API seems to
-                        * disallow setting our auth to AUTH_OPEN.
-                        */
                        if (param->value & IW_AUTH_ALG_SHARED_KEY) {
-                               local->config.authType = AUTH_SHAREDKEY;
+                               set_auth_type(local, AUTH_SHAREDKEY);
                        } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
-                               local->config.authType = AUTH_ENCRYPT;
+                               /* We don't know here if WEP open system or
+                                * unencrypted mode was requested - so use the
+                                * last mode (of these two) used last time
+                                */
+                               set_auth_type(local, local->last_auth);
                        } else
                                return -EINVAL;
 
@@ -7217,6 +7222,7 @@ static int airo_set_scan(struct net_device *dev,
        Cmd cmd;
        Resp rsp;
        int wake = 0;
+       APListRid APList_rid_empty;
 
        /* Note : you may have realised that, as this is a SET operation,
         * this is privileged and therefore a normal user can't
@@ -7234,6 +7240,13 @@ static int airo_set_scan(struct net_device *dev,
        if (ai->scan_timeout > 0)
                goto out;
 
+       /* Clear APList as it affects scan results */
+       memset(&APList_rid_empty, 0, sizeof(APList_rid_empty));
+       APList_rid_empty.len = cpu_to_le16(sizeof(APList_rid_empty));
+       disable_MAC(ai, 2);
+       writeAPListRid(ai, &APList_rid_empty, 0);
+       enable_MAC(ai, 0);
+
        /* Initiate a scan command */
        ai->scan_timeout = RUN_AT(3*HZ);
        memset(&cmd, 0, sizeof(cmd));
@@ -7489,10 +7502,8 @@ static int airo_config_commit(struct net_device *dev,
         * parameters. It's now time to commit them in the card */
        disable_MAC(local, 1);
        if (test_bit (FLAG_RESET, &local->flags)) {
-               APListRid APList_rid;
                SsidRid SSID_rid;
 
-               readAPListRid(local, &APList_rid);
                readSsidRid(local, &SSID_rid);
                if (test_bit(FLAG_MPI,&local->flags))
                        setup_card(local, dev->dev_addr, 1 );
@@ -7500,7 +7511,7 @@ static int airo_config_commit(struct net_device *dev,
                        reset_airo_card(dev);
                disable_MAC(local, 1);
                writeSsidRid(local, &SSID_rid, 1);
-               writeAPListRid(local, &APList_rid, 1);
+               writeAPListRid(local, &local->APList, 1);
        }
        if (down_interruptible(&local->sem))
                return -ERESTARTSYS;
index df7c7616533b08636374736911ca3f624983a47e..7d3231acfb24939bcee74cf9b6a38a3a26a82a67 100644 (file)
@@ -82,6 +82,16 @@ enum bmi_cmd_id {
 
 #define BMI_NVRAM_SEG_NAME_SZ 16
 
+#define BMI_PARAM_GET_EEPROM_BOARD_ID 0x10
+
+#define ATH10K_BMI_BOARD_ID_FROM_OTP_MASK   0x7c00
+#define ATH10K_BMI_BOARD_ID_FROM_OTP_LSB    10
+
+#define ATH10K_BMI_CHIP_ID_FROM_OTP_MASK    0x18000
+#define ATH10K_BMI_CHIP_ID_FROM_OTP_LSB     15
+
+#define ATH10K_BMI_BOARD_ID_STATUS_MASK 0xff
+
 struct bmi_cmd {
        __le32 id; /* enum bmi_cmd_id */
        union {
index cf28fbebaedcfc9b372d509c6a88fcbc2d808773..edf3629288bc49cb1832b41028efdd79b8b32546 100644 (file)
@@ -274,7 +274,7 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
 {
        struct ath10k *ar = ce_state->ar;
        struct ath10k_ce_ring *src_ring = ce_state->src_ring;
-       struct ce_desc *desc, *sdesc;
+       struct ce_desc *desc, sdesc;
        unsigned int nentries_mask = src_ring->nentries_mask;
        unsigned int sw_index = src_ring->sw_index;
        unsigned int write_index = src_ring->write_index;
@@ -294,7 +294,6 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
 
        desc = CE_SRC_RING_TO_DESC(src_ring->base_addr_owner_space,
                                   write_index);
-       sdesc = CE_SRC_RING_TO_DESC(src_ring->shadow_base, write_index);
 
        desc_flags |= SM(transfer_id, CE_DESC_FLAGS_META_DATA);
 
@@ -303,11 +302,11 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
        if (flags & CE_SEND_FLAG_BYTE_SWAP)
                desc_flags |= CE_DESC_FLAGS_BYTE_SWAP;
 
-       sdesc->addr   = __cpu_to_le32(buffer);
-       sdesc->nbytes = __cpu_to_le16(nbytes);
-       sdesc->flags  = __cpu_to_le16(desc_flags);
+       sdesc.addr   = __cpu_to_le32(buffer);
+       sdesc.nbytes = __cpu_to_le16(nbytes);
+       sdesc.flags  = __cpu_to_le16(desc_flags);
 
-       *desc = *sdesc;
+       *desc = sdesc;
 
        src_ring->per_transfer_context[write_index] = per_transfer_context;
 
@@ -413,7 +412,7 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr)
        lockdep_assert_held(&ar_pci->ce_lock);
 
        if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0)
-               return -EIO;
+               return -ENOSPC;
 
        desc->addr = __cpu_to_le32(paddr);
        desc->nbytes = 0;
@@ -579,17 +578,13 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
  * The caller takes responsibility for any necessary locking.
  */
 int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
-                                        void **per_transfer_contextp,
-                                        u32 *bufferp,
-                                        unsigned int *nbytesp,
-                                        unsigned int *transfer_idp)
+                                        void **per_transfer_contextp)
 {
        struct ath10k_ce_ring *src_ring = ce_state->src_ring;
        u32 ctrl_addr = ce_state->ctrl_addr;
        struct ath10k *ar = ce_state->ar;
        unsigned int nentries_mask = src_ring->nentries_mask;
        unsigned int sw_index = src_ring->sw_index;
-       struct ce_desc *sdesc, *sbase;
        unsigned int read_index;
 
        if (src_ring->hw_index == sw_index) {
@@ -614,15 +609,6 @@ int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
        if (read_index == sw_index)
                return -EIO;
 
-       sbase = src_ring->shadow_base;
-       sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index);
-
-       /* Return data from completed source descriptor */
-       *bufferp = __le32_to_cpu(sdesc->addr);
-       *nbytesp = __le16_to_cpu(sdesc->nbytes);
-       *transfer_idp = MS(__le16_to_cpu(sdesc->flags),
-                          CE_DESC_FLAGS_META_DATA);
-
        if (per_transfer_contextp)
                *per_transfer_contextp =
                        src_ring->per_transfer_context[sw_index];
@@ -697,10 +683,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
 }
 
 int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
-                                 void **per_transfer_contextp,
-                                 u32 *bufferp,
-                                 unsigned int *nbytesp,
-                                 unsigned int *transfer_idp)
+                                 void **per_transfer_contextp)
 {
        struct ath10k *ar = ce_state->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -708,9 +691,7 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
 
        spin_lock_bh(&ar_pci->ce_lock);
        ret = ath10k_ce_completed_send_next_nolock(ce_state,
-                                                  per_transfer_contextp,
-                                                  bufferp, nbytesp,
-                                                  transfer_idp);
+                                                  per_transfer_contextp);
        spin_unlock_bh(&ar_pci->ce_lock);
 
        return ret;
@@ -940,27 +921,6 @@ ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id,
                        src_ring->base_addr_ce_space_unaligned,
                        CE_DESC_RING_ALIGN);
 
-       /*
-        * Also allocate a shadow src ring in regular
-        * mem to use for faster access.
-        */
-       src_ring->shadow_base_unaligned =
-               kmalloc((nentries * sizeof(struct ce_desc) +
-                        CE_DESC_RING_ALIGN), GFP_KERNEL);
-       if (!src_ring->shadow_base_unaligned) {
-               dma_free_coherent(ar->dev,
-                                 (nentries * sizeof(struct ce_desc) +
-                                  CE_DESC_RING_ALIGN),
-                                 src_ring->base_addr_owner_space,
-                                 src_ring->base_addr_ce_space);
-               kfree(src_ring);
-               return ERR_PTR(-ENOMEM);
-       }
-
-       src_ring->shadow_base = PTR_ALIGN(
-                       src_ring->shadow_base_unaligned,
-                       CE_DESC_RING_ALIGN);
-
        return src_ring;
 }
 
@@ -1076,9 +1036,7 @@ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id)
 }
 
 int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
-                        const struct ce_attr *attr,
-                        void (*send_cb)(struct ath10k_ce_pipe *),
-                        void (*recv_cb)(struct ath10k_ce_pipe *))
+                        const struct ce_attr *attr)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
@@ -1104,10 +1062,10 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
        ce_state->src_sz_max = attr->src_sz_max;
 
        if (attr->src_nentries)
-               ce_state->send_cb = send_cb;
+               ce_state->send_cb = attr->send_cb;
 
        if (attr->dest_nentries)
-               ce_state->recv_cb = recv_cb;
+               ce_state->recv_cb = attr->recv_cb;
 
        if (attr->src_nentries) {
                ce_state->src_ring = ath10k_ce_alloc_src_ring(ar, ce_id, attr);
@@ -1141,7 +1099,6 @@ void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
        struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
 
        if (ce_state->src_ring) {
-               kfree(ce_state->src_ring->shadow_base_unaligned);
                dma_free_coherent(ar->dev,
                                  (ce_state->src_ring->nentries *
                                   sizeof(struct ce_desc) +
index 5c903e15dd65e6ad7da14caffb1e3a4fd8fe4bfc..47b734ce7ecf42112a582eaa2756ec3d6aca3868 100644 (file)
@@ -100,12 +100,6 @@ struct ath10k_ce_ring {
 
        /* CE address space */
        u32 base_addr_ce_space;
-       /*
-        * Start of shadow copy of descriptors, within regular memory.
-        * Aligned to descriptor-size boundary.
-        */
-       void *shadow_base_unaligned;
-       struct ce_desc *shadow_base;
 
        /* keep last */
        void *per_transfer_context[0];
@@ -192,16 +186,10 @@ int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
  * Pops 1 completed send buffer from Source ring.
  */
 int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
-                                 void **per_transfer_contextp,
-                                 u32 *bufferp,
-                                 unsigned int *nbytesp,
-                                 unsigned int *transfer_idp);
+                                 void **per_transfer_contextp);
 
 int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
-                                        void **per_transfer_contextp,
-                                        u32 *bufferp,
-                                        unsigned int *nbytesp,
-                                        unsigned int *transfer_idp);
+                                        void **per_transfer_contextp);
 
 /*==================CE Engine Initialization=======================*/
 
@@ -209,9 +197,7 @@ int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
                        const struct ce_attr *attr);
 void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id);
 int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
-                        const struct ce_attr *attr,
-                        void (*send_cb)(struct ath10k_ce_pipe *),
-                        void (*recv_cb)(struct ath10k_ce_pipe *));
+                        const struct ce_attr *attr);
 void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id);
 
 /*==================CE Engine Shutdown=======================*/
@@ -277,6 +263,9 @@ struct ce_attr {
 
        /* #entries in destination ring - Must be a power of 2 */
        unsigned int dest_nentries;
+
+       void (*send_cb)(struct ath10k_ce_pipe *);
+       void (*recv_cb)(struct ath10k_ce_pipe *);
 };
 
 #define SR_BA_ADDRESS          0x0000
index b87b98617073bc7222044092effa08caeebcd8cc..aa9bd92ac4edf7e5b78229a136ca499f8f7c15e3 100644 (file)
@@ -34,16 +34,19 @@ unsigned int ath10k_debug_mask;
 static unsigned int ath10k_cryptmode_param;
 static bool uart_print;
 static bool skip_otp;
+static bool rawmode;
 
 module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
 module_param_named(cryptmode, ath10k_cryptmode_param, uint, 0644);
 module_param(uart_print, bool, 0644);
 module_param(skip_otp, bool, 0644);
+module_param(rawmode, bool, 0644);
 
 MODULE_PARM_DESC(debug_mask, "Debugging mask");
 MODULE_PARM_DESC(uart_print, "Uart target debugging");
 MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode");
 MODULE_PARM_DESC(cryptmode, "Crypto mode: 0-hardware, 1-software");
+MODULE_PARM_DESC(rawmode, "Use raw 802.11 frame datapath");
 
 static const struct ath10k_hw_params ath10k_hw_params_list[] = {
        {
@@ -54,6 +57,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .has_shifted_cc_wraparound = true,
                .otp_exe_param = 0,
                .channel_counters_freq_hz = 88000,
+               .max_probe_resp_desc_thres = 0,
                .fw = {
                        .dir = QCA988X_HW_2_0_FW_DIR,
                        .fw = QCA988X_HW_2_0_FW_FILE,
@@ -70,6 +74,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .uart_pin = 6,
                .otp_exe_param = 0,
                .channel_counters_freq_hz = 88000,
+               .max_probe_resp_desc_thres = 0,
                .fw = {
                        .dir = QCA6174_HW_2_1_FW_DIR,
                        .fw = QCA6174_HW_2_1_FW_FILE,
@@ -86,6 +91,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .uart_pin = 6,
                .otp_exe_param = 0,
                .channel_counters_freq_hz = 88000,
+               .max_probe_resp_desc_thres = 0,
                .fw = {
                        .dir = QCA6174_HW_3_0_FW_DIR,
                        .fw = QCA6174_HW_3_0_FW_FILE,
@@ -102,6 +108,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .uart_pin = 6,
                .otp_exe_param = 0,
                .channel_counters_freq_hz = 88000,
+               .max_probe_resp_desc_thres = 0,
                .fw = {
                        /* uses same binaries as hw3.0 */
                        .dir = QCA6174_HW_3_0_FW_DIR,
@@ -120,6 +127,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .otp_exe_param = 0x00000700,
                .continuous_frag_desc = true,
                .channel_counters_freq_hz = 150000,
+               .max_probe_resp_desc_thres = 24,
                .fw = {
                        .dir = QCA99X0_HW_2_0_FW_DIR,
                        .fw = QCA99X0_HW_2_0_FW_FILE,
@@ -129,6 +137,21 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                        .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ,
                },
        },
+       {
+               .id = QCA9377_HW_1_0_DEV_VERSION,
+               .name = "qca9377 hw1.0",
+               .patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR,
+               .uart_pin = 7,
+               .otp_exe_param = 0,
+               .fw = {
+                       .dir = QCA9377_HW_1_0_FW_DIR,
+                       .fw = QCA9377_HW_1_0_FW_FILE,
+                       .otp = QCA9377_HW_1_0_OTP_FILE,
+                       .board = QCA9377_HW_1_0_BOARD_DATA_FILE,
+                       .board_size = QCA9377_BOARD_DATA_SZ,
+                       .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ,
+               },
+       },
 };
 
 static const char *const ath10k_core_fw_feature_str[] = {
@@ -142,12 +165,18 @@ static const char *const ath10k_core_fw_feature_str[] = {
        [ATH10K_FW_FEATURE_IGNORE_OTP_RESULT] = "ignore-otp",
        [ATH10K_FW_FEATURE_NO_NWIFI_DECAP_4ADDR_PADDING] = "no-4addr-pad",
        [ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT] = "skip-clock-init",
+       [ATH10K_FW_FEATURE_RAW_MODE_SUPPORT] = "raw-mode",
+       [ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA] = "adaptive-cca",
 };
 
 static unsigned int ath10k_core_get_fw_feature_str(char *buf,
                                                   size_t buf_len,
                                                   enum ath10k_fw_features feat)
 {
+       /* make sure that ath10k_core_fw_feature_str[] gets updated */
+       BUILD_BUG_ON(ARRAY_SIZE(ath10k_core_fw_feature_str) !=
+                    ATH10K_FW_FEATURE_COUNT);
+
        if (feat >= ARRAY_SIZE(ath10k_core_fw_feature_str) ||
            WARN_ON(!ath10k_core_fw_feature_str[feat])) {
                return scnprintf(buf, buf_len, "bit%d", feat);
@@ -435,6 +464,56 @@ out:
        return ret;
 }
 
+static int ath10k_core_get_board_id_from_otp(struct ath10k *ar)
+{
+       u32 result, address;
+       u8 board_id, chip_id;
+       int ret;
+
+       address = ar->hw_params.patch_load_addr;
+
+       if (!ar->otp_data || !ar->otp_len) {
+               ath10k_warn(ar,
+                           "failed to retrieve board id because of invalid otp\n");
+               return -ENODATA;
+       }
+
+       ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                  "boot upload otp to 0x%x len %zd for board id\n",
+                  address, ar->otp_len);
+
+       ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len);
+       if (ret) {
+               ath10k_err(ar, "could not write otp for board id check: %d\n",
+                          ret);
+               return ret;
+       }
+
+       ret = ath10k_bmi_execute(ar, address, BMI_PARAM_GET_EEPROM_BOARD_ID,
+                                &result);
+       if (ret) {
+               ath10k_err(ar, "could not execute otp for board id check: %d\n",
+                          ret);
+               return ret;
+       }
+
+       board_id = MS(result, ATH10K_BMI_BOARD_ID_FROM_OTP);
+       chip_id = MS(result, ATH10K_BMI_CHIP_ID_FROM_OTP);
+
+       ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                  "boot get otp board id result 0x%08x board_id %d chip_id %d\n",
+                  result, board_id, chip_id);
+
+       if ((result & ATH10K_BMI_BOARD_ID_STATUS_MASK) != 0)
+               return -EOPNOTSUPP;
+
+       ar->id.bmi_ids_valid = true;
+       ar->id.bmi_board_id = board_id;
+       ar->id.bmi_chip_id = chip_id;
+
+       return 0;
+}
+
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
        u32 result, address = ar->hw_params.patch_load_addr;
@@ -473,8 +552,8 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
        ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);
 
        if (!(skip_otp || test_bit(ATH10K_FW_FEATURE_IGNORE_OTP_RESULT,
-                                  ar->fw_features))
-           && result != 0) {
+                                  ar->fw_features)) &&
+           result != 0) {
                ath10k_err(ar, "otp calibration failed: %d", result);
                return -EINVAL;
        }
@@ -497,7 +576,7 @@ static int ath10k_download_fw(struct ath10k *ar, enum ath10k_firmware_mode mode)
                data_len = ar->firmware_len;
                mode_name = "normal";
                ret = ath10k_swap_code_seg_configure(ar,
-                               ATH10K_SWAP_CODE_SEG_BIN_TYPE_FW);
+                                                    ATH10K_SWAP_CODE_SEG_BIN_TYPE_FW);
                if (ret) {
                        ath10k_err(ar, "failed to configure fw code swap: %d\n",
                                   ret);
@@ -505,8 +584,8 @@ static int ath10k_download_fw(struct ath10k *ar, enum ath10k_firmware_mode mode)
                }
                break;
        case ATH10K_FIRMWARE_MODE_UTF:
-               data = ar->testmode.utf->data;
-               data_len = ar->testmode.utf->size;
+               data = ar->testmode.utf_firmware_data;
+               data_len = ar->testmode.utf_firmware_len;
                mode_name = "utf";
                break;
        default:
@@ -528,11 +607,18 @@ static int ath10k_download_fw(struct ath10k *ar, enum ath10k_firmware_mode mode)
        return ret;
 }
 
-static void ath10k_core_free_firmware_files(struct ath10k *ar)
+static void ath10k_core_free_board_files(struct ath10k *ar)
 {
        if (!IS_ERR(ar->board))
                release_firmware(ar->board);
 
+       ar->board = NULL;
+       ar->board_data = NULL;
+       ar->board_len = 0;
+}
+
+static void ath10k_core_free_firmware_files(struct ath10k *ar)
+{
        if (!IS_ERR(ar->otp))
                release_firmware(ar->otp);
 
@@ -544,10 +630,6 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar)
 
        ath10k_swap_code_seg_release(ar);
 
-       ar->board = NULL;
-       ar->board_data = NULL;
-       ar->board_len = 0;
-
        ar->otp = NULL;
        ar->otp_data = NULL;
        ar->otp_len = 0;
@@ -557,7 +639,6 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar)
        ar->firmware_len = 0;
 
        ar->cal_file = NULL;
-
 }
 
 static int ath10k_fetch_cal_file(struct ath10k *ar)
@@ -579,68 +660,251 @@ static int ath10k_fetch_cal_file(struct ath10k *ar)
        return 0;
 }
 
-static int ath10k_core_fetch_spec_board_file(struct ath10k *ar)
+static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar)
 {
-       char filename[100];
-
-       scnprintf(filename, sizeof(filename), "board-%s-%s.bin",
-                 ath10k_bus_str(ar->hif.bus), ar->spec_board_id);
+       if (!ar->hw_params.fw.board) {
+               ath10k_err(ar, "failed to find board file fw entry\n");
+               return -EINVAL;
+       }
 
-       ar->board = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, filename);
+       ar->board = ath10k_fetch_fw_file(ar,
+                                        ar->hw_params.fw.dir,
+                                        ar->hw_params.fw.board);
        if (IS_ERR(ar->board))
                return PTR_ERR(ar->board);
 
        ar->board_data = ar->board->data;
        ar->board_len = ar->board->size;
-       ar->spec_board_loaded = true;
 
        return 0;
 }
 
-static int ath10k_core_fetch_generic_board_file(struct ath10k *ar)
+static int ath10k_core_parse_bd_ie_board(struct ath10k *ar,
+                                        const void *buf, size_t buf_len,
+                                        const char *boardname)
 {
-       if (!ar->hw_params.fw.board) {
-               ath10k_err(ar, "failed to find board file fw entry\n");
-               return -EINVAL;
+       const struct ath10k_fw_ie *hdr;
+       bool name_match_found;
+       int ret, board_ie_id;
+       size_t board_ie_len;
+       const void *board_ie_data;
+
+       name_match_found = false;
+
+       /* go through ATH10K_BD_IE_BOARD_ elements */
+       while (buf_len > sizeof(struct ath10k_fw_ie)) {
+               hdr = buf;
+               board_ie_id = le32_to_cpu(hdr->id);
+               board_ie_len = le32_to_cpu(hdr->len);
+               board_ie_data = hdr->data;
+
+               buf_len -= sizeof(*hdr);
+               buf += sizeof(*hdr);
+
+               if (buf_len < ALIGN(board_ie_len, 4)) {
+                       ath10k_err(ar, "invalid ATH10K_BD_IE_BOARD length: %zu < %zu\n",
+                                  buf_len, ALIGN(board_ie_len, 4));
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               switch (board_ie_id) {
+               case ATH10K_BD_IE_BOARD_NAME:
+                       ath10k_dbg_dump(ar, ATH10K_DBG_BOOT, "board name", "",
+                                       board_ie_data, board_ie_len);
+
+                       if (board_ie_len != strlen(boardname))
+                               break;
+
+                       ret = memcmp(board_ie_data, boardname, strlen(boardname));
+                       if (ret)
+                               break;
+
+                       name_match_found = true;
+                       ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                                  "boot found match for name '%s'",
+                                  boardname);
+                       break;
+               case ATH10K_BD_IE_BOARD_DATA:
+                       if (!name_match_found)
+                               /* no match found */
+                               break;
+
+                       ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                                  "boot found board data for '%s'",
+                                  boardname);
+
+                       ar->board_data = board_ie_data;
+                       ar->board_len = board_ie_len;
+
+                       ret = 0;
+                       goto out;
+               default:
+                       ath10k_warn(ar, "unknown ATH10K_BD_IE_BOARD found: %d\n",
+                                   board_ie_id);
+                       break;
+               }
+
+               /* jump over the padding */
+               board_ie_len = ALIGN(board_ie_len, 4);
+
+               buf_len -= board_ie_len;
+               buf += board_ie_len;
        }
 
-       ar->board = ath10k_fetch_fw_file(ar,
-                                        ar->hw_params.fw.dir,
-                                        ar->hw_params.fw.board);
+       /* no match found */
+       ret = -ENOENT;
+
+out:
+       return ret;
+}
+
+static int ath10k_core_fetch_board_data_api_n(struct ath10k *ar,
+                                             const char *boardname,
+                                             const char *filename)
+{
+       size_t len, magic_len, ie_len;
+       struct ath10k_fw_ie *hdr;
+       const u8 *data;
+       int ret, ie_id;
+
+       ar->board = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, filename);
        if (IS_ERR(ar->board))
                return PTR_ERR(ar->board);
 
-       ar->board_data = ar->board->data;
-       ar->board_len = ar->board->size;
-       ar->spec_board_loaded = false;
+       data = ar->board->data;
+       len = ar->board->size;
+
+       /* magic has extra null byte padded */
+       magic_len = strlen(ATH10K_BOARD_MAGIC) + 1;
+       if (len < magic_len) {
+               ath10k_err(ar, "failed to find magic value in %s/%s, file too short: %zu\n",
+                          ar->hw_params.fw.dir, filename, len);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (memcmp(data, ATH10K_BOARD_MAGIC, magic_len)) {
+               ath10k_err(ar, "found invalid board magic\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* magic is padded to 4 bytes */
+       magic_len = ALIGN(magic_len, 4);
+       if (len < magic_len) {
+               ath10k_err(ar, "failed: %s/%s too small to contain board data, len: %zu\n",
+                          ar->hw_params.fw.dir, filename, len);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       data += magic_len;
+       len -= magic_len;
+
+       while (len > sizeof(struct ath10k_fw_ie)) {
+               hdr = (struct ath10k_fw_ie *)data;
+               ie_id = le32_to_cpu(hdr->id);
+               ie_len = le32_to_cpu(hdr->len);
+
+               len -= sizeof(*hdr);
+               data = hdr->data;
+
+               if (len < ALIGN(ie_len, 4)) {
+                       ath10k_err(ar, "invalid length for board ie_id %d ie_len %zu len %zu\n",
+                                  ie_id, ie_len, len);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               switch (ie_id) {
+               case ATH10K_BD_IE_BOARD:
+                       ret = ath10k_core_parse_bd_ie_board(ar, data, ie_len,
+                                                           boardname);
+                       if (ret == -ENOENT)
+                               /* no match found, continue */
+                               break;
+                       else if (ret)
+                               /* there was an error, bail out */
+                               goto err;
+
+                       /* board data found */
+                       goto out;
+               }
+
+               /* jump over the padding */
+               ie_len = ALIGN(ie_len, 4);
+
+               len -= ie_len;
+               data += ie_len;
+       }
+
+out:
+       if (!ar->board_data || !ar->board_len) {
+               ath10k_err(ar,
+                          "failed to fetch board data for %s from %s/%s\n",
+                          ar->hw_params.fw.dir, boardname, filename);
+               ret = -ENODATA;
+               goto err;
+       }
+
+       return 0;
+
+err:
+       ath10k_core_free_board_files(ar);
+       return ret;
+}
+
+static int ath10k_core_create_board_name(struct ath10k *ar, char *name,
+                                        size_t name_len)
+{
+       if (ar->id.bmi_ids_valid) {
+               scnprintf(name, name_len,
+                         "bus=%s,bmi-chip-id=%d,bmi-board-id=%d",
+                         ath10k_bus_str(ar->hif.bus),
+                         ar->id.bmi_chip_id,
+                         ar->id.bmi_board_id);
+               goto out;
+       }
+
+       scnprintf(name, name_len,
+                 "bus=%s,vendor=%04x,device=%04x,subsystem-vendor=%04x,subsystem-device=%04x",
+                 ath10k_bus_str(ar->hif.bus),
+                 ar->id.vendor, ar->id.device,
+                 ar->id.subsystem_vendor, ar->id.subsystem_device);
+
+out:
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using board name '%s'\n", name);
 
        return 0;
 }
 
 static int ath10k_core_fetch_board_file(struct ath10k *ar)
 {
+       char boardname[100];
        int ret;
 
-       if (strlen(ar->spec_board_id) > 0) {
-               ret = ath10k_core_fetch_spec_board_file(ar);
-               if (ret) {
-                       ath10k_info(ar, "failed to load spec board file, falling back to generic: %d\n",
-                                   ret);
-                       goto generic;
-               }
-
-               ath10k_dbg(ar, ATH10K_DBG_BOOT, "found specific board file for %s\n",
-                          ar->spec_board_id);
-               return 0;
+       ret = ath10k_core_create_board_name(ar, boardname, sizeof(boardname));
+       if (ret) {
+               ath10k_err(ar, "failed to create board name: %d", ret);
+               return ret;
        }
 
-generic:
-       ret = ath10k_core_fetch_generic_board_file(ar);
+       ar->bd_api = 2;
+       ret = ath10k_core_fetch_board_data_api_n(ar, boardname,
+                                                ATH10K_BOARD_API2_FILE);
+       if (!ret)
+               goto success;
+
+       ar->bd_api = 1;
+       ret = ath10k_core_fetch_board_data_api_1(ar);
        if (ret) {
-               ath10k_err(ar, "failed to fetch generic board data: %d\n", ret);
+               ath10k_err(ar, "failed to fetch board data\n");
                return ret;
        }
 
+success:
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "using board api %d\n", ar->bd_api);
        return 0;
 }
 
@@ -872,12 +1136,6 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
        /* calibration file is optional, don't check for any errors */
        ath10k_fetch_cal_file(ar);
 
-       ret = ath10k_core_fetch_board_file(ar);
-       if (ret) {
-               ath10k_err(ar, "failed to fetch board file: %d\n", ret);
-               return ret;
-       }
-
        ar->fw_api = 5;
        ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
 
@@ -1117,6 +1375,15 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar)
        ar->htt.max_num_amsdu = ATH10K_HTT_MAX_NUM_AMSDU_DEFAULT;
        ar->htt.max_num_ampdu = ATH10K_HTT_MAX_NUM_AMPDU_DEFAULT;
 
+       if (rawmode) {
+               if (!test_bit(ATH10K_FW_FEATURE_RAW_MODE_SUPPORT,
+                             ar->fw_features)) {
+                       ath10k_err(ar, "rawmode = 1 requires support from firmware");
+                       return -EINVAL;
+               }
+               set_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags);
+       }
+
        if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
                ar->wmi.rx_decap_mode = ATH10K_HW_TXRX_RAW;
 
@@ -1241,10 +1508,10 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
                goto err;
 
        /* Some of of qca988x solutions are having global reset issue
-         * during target initialization. Bypassing PLL setting before
-         * downloading firmware and letting the SoC run on REF_CLK is
-         * fixing the problem. Corresponding firmware change is also needed
-         * to set the clock source once the target is initialized.
+        * during target initialization. Bypassing PLL setting before
+        * downloading firmware and letting the SoC run on REF_CLK is
+        * fixing the problem. Corresponding firmware change is also needed
+        * to set the clock source once the target is initialized.
         */
        if (test_bit(ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT,
                     ar->fw_features)) {
@@ -1478,6 +1745,19 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
                goto err_power_down;
        }
 
+       ret = ath10k_core_get_board_id_from_otp(ar);
+       if (ret && ret != -EOPNOTSUPP) {
+               ath10k_err(ar, "failed to get board id from otp for qca99x0: %d\n",
+                          ret);
+               return ret;
+       }
+
+       ret = ath10k_core_fetch_board_file(ar);
+       if (ret) {
+               ath10k_err(ar, "failed to fetch board file: %d\n", ret);
+               goto err_free_firmware_files;
+       }
+
        ret = ath10k_core_init_firmware_features(ar);
        if (ret) {
                ath10k_err(ar, "fatal problem with firmware features: %d\n",
@@ -1605,6 +1885,7 @@ void ath10k_core_unregister(struct ath10k *ar)
        ath10k_testmode_destroy(ar);
 
        ath10k_core_free_firmware_files(ar);
+       ath10k_core_free_board_files(ar);
 
        ath10k_debug_unregister(ar);
 }
@@ -1635,6 +1916,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
                ar->hw_values = &qca988x_values;
                break;
        case ATH10K_HW_QCA6174:
+       case ATH10K_HW_QCA9377:
                ar->regs = &qca6174_regs;
                ar->hw_values = &qca6174_values;
                break;
@@ -1714,6 +1996,7 @@ void ath10k_core_destroy(struct ath10k *ar)
        destroy_workqueue(ar->workqueue_aux);
 
        ath10k_debug_destroy(ar);
+       ath10k_wmi_free_host_mem(ar);
        ath10k_mac_destroy(ar);
 }
 EXPORT_SYMBOL(ath10k_core_destroy);
index 12542144fe125630d5b5ee1baef8a806e6b8322f..4a230158990241f1a6ab27659f9a2817791c85d8 100644 (file)
@@ -214,6 +214,7 @@ struct ath10k_fw_stats_pdev {
        s32 hw_queued;
        s32 hw_reaped;
        s32 underrun;
+       u32 hw_paused;
        s32 tx_abort;
        s32 mpdus_requed;
        u32 tx_ko;
@@ -226,6 +227,16 @@ struct ath10k_fw_stats_pdev {
        u32 pdev_resets;
        u32 phy_underrun;
        u32 txop_ovf;
+       u32 seq_posted;
+       u32 seq_failed_queueing;
+       u32 seq_completed;
+       u32 seq_restarted;
+       u32 mu_seq_posted;
+       u32 mpdus_sw_flush;
+       u32 mpdus_hw_filter;
+       u32 mpdus_truncated;
+       u32 mpdus_ack_failed;
+       u32 mpdus_expired;
 
        /* PDEV RX stats */
        s32 mid_ppdu_route_change;
@@ -242,6 +253,7 @@ struct ath10k_fw_stats_pdev {
        s32 phy_errs;
        s32 phy_err_drop;
        s32 mpdu_errs;
+       s32 rx_ovfl_errs;
 };
 
 struct ath10k_fw_stats {
@@ -250,6 +262,30 @@ struct ath10k_fw_stats {
        struct list_head peers;
 };
 
+#define ATH10K_TPC_TABLE_TYPE_FLAG     1
+#define ATH10K_TPC_PREAM_TABLE_END     0xFFFF
+
+struct ath10k_tpc_table {
+       u32 pream_idx[WMI_TPC_RATE_MAX];
+       u8 rate_code[WMI_TPC_RATE_MAX];
+       char tpc_value[WMI_TPC_RATE_MAX][WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE];
+};
+
+struct ath10k_tpc_stats {
+       u32 reg_domain;
+       u32 chan_freq;
+       u32 phy_mode;
+       u32 twice_antenna_reduction;
+       u32 twice_max_rd_power;
+       s32 twice_antenna_gain;
+       u32 power_limit;
+       u32 num_tx_chain;
+       u32 ctl;
+       u32 rate_max;
+       u8 flag[WMI_TPC_FLAG];
+       struct ath10k_tpc_table tpc_table[WMI_TPC_FLAG];
+};
+
 struct ath10k_dfs_stats {
        u32 phy_errors;
        u32 pulses_total;
@@ -378,6 +414,11 @@ struct ath10k_debug {
        struct ath10k_dfs_stats dfs_stats;
        struct ath_dfs_pool_stats dfs_pool_stats;
 
+       /* used for tpc-dump storage, protected by data-lock */
+       struct ath10k_tpc_stats *tpc_stats;
+
+       struct completion tpc_complete;
+
        /* protected by conf_mutex */
        u32 fw_dbglog_mask;
        u32 fw_dbglog_level;
@@ -468,6 +509,9 @@ enum ath10k_fw_features {
         */
        ATH10K_FW_FEATURE_RAW_MODE_SUPPORT = 10,
 
+       /* Firmware Supports Adaptive CCA*/
+       ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA = 11,
+
        /* keep last */
        ATH10K_FW_FEATURE_COUNT,
 };
@@ -612,6 +656,11 @@ struct ath10k {
 
                u32 channel_counters_freq_hz;
 
+               /* Mgmt tx descriptors threshold for limiting probe response
+                * frames.
+                */
+               u32 max_probe_resp_desc_thres;
+
                struct ath10k_hw_params_fw {
                        const char *dir;
                        const char *fw;
@@ -642,10 +691,19 @@ struct ath10k {
                struct ath10k_swap_code_seg_info *firmware_swap_code_seg_info;
        } swap;
 
-       char spec_board_id[100];
-       bool spec_board_loaded;
+       struct {
+               u32 vendor;
+               u32 device;
+               u32 subsystem_vendor;
+               u32 subsystem_device;
+
+               bool bmi_ids_valid;
+               u8 bmi_board_id;
+               u8 bmi_chip_id;
+       } id;
 
        int fw_api;
+       int bd_api;
        enum ath10k_cal_mode cal_mode;
 
        struct {
@@ -687,8 +745,6 @@ struct ath10k {
        int num_started_vdevs;
 
        /* Protected by conf-mutex */
-       u8 supp_tx_chainmask;
-       u8 supp_rx_chainmask;
        u8 cfg_tx_chainmask;
        u8 cfg_rx_chainmask;
 
@@ -771,9 +827,12 @@ struct ath10k {
        struct {
                /* protected by conf_mutex */
                const struct firmware *utf;
+               char utf_version[32];
+               const void *utf_firmware_data;
+               size_t utf_firmware_len;
                DECLARE_BITMAP(orig_fw_features, ATH10K_FW_FEATURE_COUNT);
                enum ath10k_fw_wmi_op_version orig_wmi_op_version;
-
+               enum ath10k_fw_wmi_op_version op_version;
                /* protected by data_lock */
                bool utf_monitor;
        } testmode;
index bf033f46f8aaae1280baf478d66a5890720c962d..6cc1aa3449c88640359ef716e3b300b692154d24 100644 (file)
@@ -125,19 +125,25 @@ EXPORT_SYMBOL(ath10k_info);
 void ath10k_print_driver_info(struct ath10k *ar)
 {
        char fw_features[128] = {};
+       char boardinfo[100];
 
        ath10k_core_get_fw_features_str(ar, fw_features, sizeof(fw_features));
 
-       ath10k_info(ar, "%s (0x%08x, 0x%08x%s%s%s) fw %s api %d htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d features %s\n",
+       if (ar->id.bmi_ids_valid)
+               scnprintf(boardinfo, sizeof(boardinfo), "bmi %d:%d",
+                         ar->id.bmi_chip_id, ar->id.bmi_board_id);
+       else
+               scnprintf(boardinfo, sizeof(boardinfo), "sub %04x:%04x",
+                         ar->id.subsystem_vendor, ar->id.subsystem_device);
+
+       ath10k_info(ar, "%s (0x%08x, 0x%08x %s) fw %s fwapi %d bdapi %d htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d features %s\n",
                    ar->hw_params.name,
                    ar->target_version,
                    ar->chip_id,
-                   (strlen(ar->spec_board_id) > 0 ? ", " : ""),
-                   ar->spec_board_id,
-                   (strlen(ar->spec_board_id) > 0 && !ar->spec_board_loaded
-                    ? " fallback" : ""),
+                   boardinfo,
                    ar->hw->wiphy->fw_version,
                    ar->fw_api,
+                   ar->bd_api,
                    ar->htt.target_version_major,
                    ar->htt.target_version_minor,
                    ar->wmi.op_version,
@@ -285,28 +291,6 @@ static void ath10k_debug_fw_stats_reset(struct ath10k *ar)
        spin_unlock_bh(&ar->data_lock);
 }
 
-static size_t ath10k_debug_fw_stats_num_peers(struct list_head *head)
-{
-       struct ath10k_fw_stats_peer *i;
-       size_t num = 0;
-
-       list_for_each_entry(i, head, list)
-               ++num;
-
-       return num;
-}
-
-static size_t ath10k_debug_fw_stats_num_vdevs(struct list_head *head)
-{
-       struct ath10k_fw_stats_vdev *i;
-       size_t num = 0;
-
-       list_for_each_entry(i, head, list)
-               ++num;
-
-       return num;
-}
-
 void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
 {
        struct ath10k_fw_stats stats = {};
@@ -343,8 +327,8 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
                goto free;
        }
 
-       num_peers = ath10k_debug_fw_stats_num_peers(&ar->debug.fw_stats.peers);
-       num_vdevs = ath10k_debug_fw_stats_num_vdevs(&ar->debug.fw_stats.vdevs);
+       num_peers = ath10k_wmi_fw_stats_num_peers(&ar->debug.fw_stats.peers);
+       num_vdevs = ath10k_wmi_fw_stats_num_vdevs(&ar->debug.fw_stats.vdevs);
        is_start = (list_empty(&ar->debug.fw_stats.pdevs) &&
                    !list_empty(&stats.pdevs));
        is_end = (!list_empty(&ar->debug.fw_stats.pdevs) &&
@@ -429,240 +413,6 @@ static int ath10k_debug_fw_stats_request(struct ath10k *ar)
        return 0;
 }
 
-/* FIXME: How to calculate the buffer size sanely? */
-#define ATH10K_FW_STATS_BUF_SIZE (1024*1024)
-
-static void ath10k_fw_stats_fill(struct ath10k *ar,
-                                struct ath10k_fw_stats *fw_stats,
-                                char *buf)
-{
-       unsigned int len = 0;
-       unsigned int buf_len = ATH10K_FW_STATS_BUF_SIZE;
-       const struct ath10k_fw_stats_pdev *pdev;
-       const struct ath10k_fw_stats_vdev *vdev;
-       const struct ath10k_fw_stats_peer *peer;
-       size_t num_peers;
-       size_t num_vdevs;
-       int i;
-
-       spin_lock_bh(&ar->data_lock);
-
-       pdev = list_first_entry_or_null(&fw_stats->pdevs,
-                                       struct ath10k_fw_stats_pdev, list);
-       if (!pdev) {
-               ath10k_warn(ar, "failed to get pdev stats\n");
-               goto unlock;
-       }
-
-       num_peers = ath10k_debug_fw_stats_num_peers(&fw_stats->peers);
-       num_vdevs = ath10k_debug_fw_stats_num_vdevs(&fw_stats->vdevs);
-
-       len += scnprintf(buf + len, buf_len - len, "\n");
-       len += scnprintf(buf + len, buf_len - len, "%30s\n",
-                        "ath10k PDEV stats");
-       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
-                                "=================");
-
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Channel noise floor", pdev->ch_noise_floor);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
-                        "Channel TX power", pdev->chan_tx_power);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
-                        "TX frame count", pdev->tx_frame_count);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
-                        "RX frame count", pdev->rx_frame_count);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
-                        "RX clear count", pdev->rx_clear_count);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
-                        "Cycle count", pdev->cycle_count);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
-                        "PHY error count", pdev->phy_err_count);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
-                        "RTS bad count", pdev->rts_bad);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
-                        "RTS good count", pdev->rts_good);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
-                        "FCS bad count", pdev->fcs_bad);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
-                        "No beacon count", pdev->no_beacons);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
-                        "MIB int count", pdev->mib_int_count);
-
-       len += scnprintf(buf + len, buf_len - len, "\n");
-       len += scnprintf(buf + len, buf_len - len, "%30s\n",
-                        "ath10k PDEV TX stats");
-       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
-                                "=================");
-
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "HTT cookies queued", pdev->comp_queued);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "HTT cookies disp.", pdev->comp_delivered);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "MSDU queued", pdev->msdu_enqued);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "MPDU queued", pdev->mpdu_enqued);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "MSDUs dropped", pdev->wmm_drop);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Local enqued", pdev->local_enqued);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Local freed", pdev->local_freed);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "HW queued", pdev->hw_queued);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "PPDUs reaped", pdev->hw_reaped);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Num underruns", pdev->underrun);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "PPDUs cleaned", pdev->tx_abort);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "MPDUs requed", pdev->mpdus_requed);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Excessive retries", pdev->tx_ko);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "HW rate", pdev->data_rc);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Sched self tiggers", pdev->self_triggers);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Dropped due to SW retries",
-                        pdev->sw_retry_failure);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Illegal rate phy errors",
-                        pdev->illgl_rate_phy_err);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Pdev continous xretry", pdev->pdev_cont_xretry);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "TX timeout", pdev->pdev_tx_timeout);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "PDEV resets", pdev->pdev_resets);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "PHY underrun", pdev->phy_underrun);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "MPDU is more than txop limit", pdev->txop_ovf);
-
-       len += scnprintf(buf + len, buf_len - len, "\n");
-       len += scnprintf(buf + len, buf_len - len, "%30s\n",
-                        "ath10k PDEV RX stats");
-       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
-                                "=================");
-
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Mid PPDU route change",
-                        pdev->mid_ppdu_route_change);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Tot. number of statuses", pdev->status_rcvd);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Extra frags on rings 0", pdev->r0_frags);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Extra frags on rings 1", pdev->r1_frags);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Extra frags on rings 2", pdev->r2_frags);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Extra frags on rings 3", pdev->r3_frags);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "MSDUs delivered to HTT", pdev->htt_msdus);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "MPDUs delivered to HTT", pdev->htt_mpdus);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "MSDUs delivered to stack", pdev->loc_msdus);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "MPDUs delivered to stack", pdev->loc_mpdus);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Oversized AMSUs", pdev->oversize_amsdu);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "PHY errors", pdev->phy_errs);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "PHY errors drops", pdev->phy_err_drop);
-       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs);
-
-       len += scnprintf(buf + len, buf_len - len, "\n");
-       len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
-                        "ath10k VDEV stats", num_vdevs);
-       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
-                                "=================");
-
-       list_for_each_entry(vdev, &fw_stats->vdevs, list) {
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "vdev id", vdev->vdev_id);
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "beacon snr", vdev->beacon_snr);
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "data snr", vdev->data_snr);
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "num rx frames", vdev->num_rx_frames);
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "num rts fail", vdev->num_rts_fail);
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "num rts success", vdev->num_rts_success);
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "num rx err", vdev->num_rx_err);
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "num rx discard", vdev->num_rx_discard);
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "num tx not acked", vdev->num_tx_not_acked);
-
-               for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames); i++)
-                       len += scnprintf(buf + len, buf_len - len,
-                                       "%25s [%02d] %u\n",
-                                        "num tx frames", i,
-                                        vdev->num_tx_frames[i]);
-
-               for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_retries); i++)
-                       len += scnprintf(buf + len, buf_len - len,
-                                       "%25s [%02d] %u\n",
-                                        "num tx frames retries", i,
-                                        vdev->num_tx_frames_retries[i]);
-
-               for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_failures); i++)
-                       len += scnprintf(buf + len, buf_len - len,
-                                       "%25s [%02d] %u\n",
-                                        "num tx frames failures", i,
-                                        vdev->num_tx_frames_failures[i]);
-
-               for (i = 0 ; i < ARRAY_SIZE(vdev->tx_rate_history); i++)
-                       len += scnprintf(buf + len, buf_len - len,
-                                       "%25s [%02d] 0x%08x\n",
-                                        "tx rate history", i,
-                                        vdev->tx_rate_history[i]);
-
-               for (i = 0 ; i < ARRAY_SIZE(vdev->beacon_rssi_history); i++)
-                       len += scnprintf(buf + len, buf_len - len,
-                                       "%25s [%02d] %u\n",
-                                        "beacon rssi history", i,
-                                        vdev->beacon_rssi_history[i]);
-
-               len += scnprintf(buf + len, buf_len - len, "\n");
-       }
-
-       len += scnprintf(buf + len, buf_len - len, "\n");
-       len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
-                        "ath10k PEER stats", num_peers);
-       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
-                                "=================");
-
-       list_for_each_entry(peer, &fw_stats->peers, list) {
-               len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
-                                "Peer MAC address", peer->peer_macaddr);
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "Peer RSSI", peer->peer_rssi);
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "Peer TX rate", peer->peer_tx_rate);
-               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
-                                "Peer RX rate", peer->peer_rx_rate);
-               len += scnprintf(buf + len, buf_len - len, "\n");
-       }
-
-unlock:
-       spin_unlock_bh(&ar->data_lock);
-
-       if (len >= buf_len)
-               buf[len - 1] = 0;
-       else
-               buf[len] = 0;
-}
-
 static int ath10k_fw_stats_open(struct inode *inode, struct file *file)
 {
        struct ath10k *ar = inode->i_private;
@@ -688,7 +438,12 @@ static int ath10k_fw_stats_open(struct inode *inode, struct file *file)
                goto err_free;
        }
 
-       ath10k_fw_stats_fill(ar, &ar->debug.fw_stats, buf);
+       ret = ath10k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, buf);
+       if (ret) {
+               ath10k_warn(ar, "failed to fill fw stats: %d\n", ret);
+               goto err_free;
+       }
+
        file->private_data = buf;
 
        mutex_unlock(&ar->conf_mutex);
@@ -1843,6 +1598,233 @@ static const struct file_operations fops_nf_cal_period = {
        .llseek = default_llseek,
 };
 
+#define ATH10K_TPC_CONFIG_BUF_SIZE     (1024 * 1024)
+
+static int ath10k_debug_tpc_stats_request(struct ath10k *ar)
+{
+       int ret;
+       unsigned long time_left;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       reinit_completion(&ar->debug.tpc_complete);
+
+       ret = ath10k_wmi_pdev_get_tpc_config(ar, WMI_TPC_CONFIG_PARAM);
+       if (ret) {
+               ath10k_warn(ar, "failed to request tpc config: %d\n", ret);
+               return ret;
+       }
+
+       time_left = wait_for_completion_timeout(&ar->debug.tpc_complete,
+                                               1 * HZ);
+       if (time_left == 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+void ath10k_debug_tpc_stats_process(struct ath10k *ar,
+                                   struct ath10k_tpc_stats *tpc_stats)
+{
+       spin_lock_bh(&ar->data_lock);
+
+       kfree(ar->debug.tpc_stats);
+       ar->debug.tpc_stats = tpc_stats;
+       complete(&ar->debug.tpc_complete);
+
+       spin_unlock_bh(&ar->data_lock);
+}
+
+static void ath10k_tpc_stats_print(struct ath10k_tpc_stats *tpc_stats,
+                                  unsigned int j, char *buf, unsigned int *len)
+{
+       unsigned int i, buf_len;
+       static const char table_str[][5] = { "CDD",
+                                            "STBC",
+                                            "TXBF" };
+       static const char pream_str[][6] = { "CCK",
+                                            "OFDM",
+                                            "HT20",
+                                            "HT40",
+                                            "VHT20",
+                                            "VHT40",
+                                            "VHT80",
+                                            "HTCUP" };
+
+       buf_len = ATH10K_TPC_CONFIG_BUF_SIZE;
+       *len += scnprintf(buf + *len, buf_len - *len,
+                         "********************************\n");
+       *len += scnprintf(buf + *len, buf_len - *len,
+                         "******************* %s POWER TABLE ****************\n",
+                         table_str[j]);
+       *len += scnprintf(buf + *len, buf_len - *len,
+                         "********************************\n");
+       *len += scnprintf(buf + *len, buf_len - *len,
+                         "No.  Preamble Rate_code tpc_value1 tpc_value2 tpc_value3\n");
+
+       for (i = 0; i < tpc_stats->rate_max; i++) {
+               *len += scnprintf(buf + *len, buf_len - *len,
+                                 "%8d %s 0x%2x %s\n", i,
+                                 pream_str[tpc_stats->tpc_table[j].pream_idx[i]],
+                                 tpc_stats->tpc_table[j].rate_code[i],
+                                 tpc_stats->tpc_table[j].tpc_value[i]);
+       }
+
+       *len += scnprintf(buf + *len, buf_len - *len,
+                         "***********************************\n");
+}
+
+static void ath10k_tpc_stats_fill(struct ath10k *ar,
+                                 struct ath10k_tpc_stats *tpc_stats,
+                                 char *buf)
+{
+       unsigned int len, j, buf_len;
+
+       len = 0;
+       buf_len = ATH10K_TPC_CONFIG_BUF_SIZE;
+
+       spin_lock_bh(&ar->data_lock);
+
+       if (!tpc_stats) {
+               ath10k_warn(ar, "failed to get tpc stats\n");
+               goto unlock;
+       }
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len,
+                        "*************************************\n");
+       len += scnprintf(buf + len, buf_len - len,
+                        "TPC config for channel %4d mode %d\n",
+                        tpc_stats->chan_freq,
+                        tpc_stats->phy_mode);
+       len += scnprintf(buf + len, buf_len - len,
+                        "*************************************\n");
+       len += scnprintf(buf + len, buf_len - len,
+                        "CTL           =  0x%2x Reg. Domain            = %2d\n",
+                        tpc_stats->ctl,
+                        tpc_stats->reg_domain);
+       len += scnprintf(buf + len, buf_len - len,
+                        "Antenna Gain  = %2d Reg. Max Antenna Gain     =  %2d\n",
+                        tpc_stats->twice_antenna_gain,
+                        tpc_stats->twice_antenna_reduction);
+       len += scnprintf(buf + len, buf_len - len,
+                        "Power Limit   = %2d Reg. Max Power            = %2d\n",
+                        tpc_stats->power_limit,
+                        tpc_stats->twice_max_rd_power / 2);
+       len += scnprintf(buf + len, buf_len - len,
+                        "Num tx chains = %2d Num supported rates       = %2d\n",
+                        tpc_stats->num_tx_chain,
+                        tpc_stats->rate_max);
+
+       for (j = 0; j < tpc_stats->num_tx_chain ; j++) {
+               switch (j) {
+               case WMI_TPC_TABLE_TYPE_CDD:
+                       if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {
+                               len += scnprintf(buf + len, buf_len - len,
+                                                "CDD not supported\n");
+                               break;
+                       }
+
+                       ath10k_tpc_stats_print(tpc_stats, j, buf, &len);
+                       break;
+               case WMI_TPC_TABLE_TYPE_STBC:
+                       if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {
+                               len += scnprintf(buf + len, buf_len - len,
+                                                "STBC not supported\n");
+                               break;
+                       }
+
+                       ath10k_tpc_stats_print(tpc_stats, j, buf, &len);
+                       break;
+               case WMI_TPC_TABLE_TYPE_TXBF:
+                       if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {
+                               len += scnprintf(buf + len, buf_len - len,
+                                                "TXBF not supported\n***************************\n");
+                               break;
+                       }
+
+                       ath10k_tpc_stats_print(tpc_stats, j, buf, &len);
+                       break;
+               default:
+                       len += scnprintf(buf + len, buf_len - len,
+                                        "Invalid Type\n");
+                       break;
+               }
+       }
+
+unlock:
+       spin_unlock_bh(&ar->data_lock);
+
+       if (len >= buf_len)
+               buf[len - 1] = 0;
+       else
+               buf[len] = 0;
+}
+
+static int ath10k_tpc_stats_open(struct inode *inode, struct file *file)
+{
+       struct ath10k *ar = inode->i_private;
+       void *buf = NULL;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state != ATH10K_STATE_ON) {
+               ret = -ENETDOWN;
+               goto err_unlock;
+       }
+
+       buf = vmalloc(ATH10K_TPC_CONFIG_BUF_SIZE);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_unlock;
+       }
+
+       ret = ath10k_debug_tpc_stats_request(ar);
+       if (ret) {
+               ath10k_warn(ar, "failed to request tpc config stats: %d\n",
+                           ret);
+               goto err_free;
+       }
+
+       ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf);
+       file->private_data = buf;
+
+       mutex_unlock(&ar->conf_mutex);
+       return 0;
+
+err_free:
+       vfree(buf);
+
+err_unlock:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_tpc_stats_release(struct inode *inode, struct file *file)
+{
+       vfree(file->private_data);
+
+       return 0;
+}
+
+static ssize_t ath10k_tpc_stats_read(struct file *file, char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       const char *buf = file->private_data;
+       unsigned int len = strlen(buf);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_tpc_stats = {
+       .open = ath10k_tpc_stats_open,
+       .release = ath10k_tpc_stats_release,
+       .read = ath10k_tpc_stats_read,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 int ath10k_debug_start(struct ath10k *ar)
 {
        int ret;
@@ -2111,6 +2093,8 @@ void ath10k_debug_destroy(struct ath10k *ar)
        ar->debug.fw_crash_data = NULL;
 
        ath10k_debug_fw_stats_reset(ar);
+
+       kfree(ar->debug.tpc_stats);
 }
 
 int ath10k_debug_register(struct ath10k *ar)
@@ -2127,6 +2111,7 @@ int ath10k_debug_register(struct ath10k *ar)
        INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork,
                          ath10k_debug_htt_stats_dwork);
 
+       init_completion(&ar->debug.tpc_complete);
        init_completion(&ar->debug.fw_stats_complete);
 
        debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
@@ -2195,6 +2180,9 @@ int ath10k_debug_register(struct ath10k *ar)
        debugfs_create_file("quiet_period", S_IRUGO | S_IWUSR,
                            ar->debug.debugfs_phy, ar, &fops_quiet_period);
 
+       debugfs_create_file("tpc_stats", S_IRUSR,
+                           ar->debug.debugfs_phy, ar, &fops_tpc_stats);
+
        return 0;
 }
 
index 53bd6a19eab6215b6108077c7407de6f2bac1d30..7de780c4ec8da0c9235f280510eda3f7c068aaf4 100644 (file)
@@ -55,6 +55,9 @@ enum ath10k_dbg_aggr_mode {
        ATH10K_DBG_AGGR_MODE_MAX,
 };
 
+/* FIXME: How to calculate the buffer size sanely? */
+#define ATH10K_FW_STATS_BUF_SIZE (1024*1024)
+
 extern unsigned int ath10k_debug_mask;
 
 __printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...);
@@ -70,6 +73,8 @@ void ath10k_debug_destroy(struct ath10k *ar);
 int ath10k_debug_register(struct ath10k *ar);
 void ath10k_debug_unregister(struct ath10k *ar);
 void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_debug_tpc_stats_process(struct ath10k *ar,
+                                   struct ath10k_tpc_stats *tpc_stats);
 struct ath10k_fw_crash_data *
 ath10k_debug_get_new_fw_crash_data(struct ath10k *ar);
 
@@ -117,6 +122,12 @@ static inline void ath10k_debug_fw_stats_process(struct ath10k *ar,
 {
 }
 
+static inline void ath10k_debug_tpc_stats_process(struct ath10k *ar,
+                                                 struct ath10k_tpc_stats *tpc_stats)
+{
+       kfree(tpc_stats);
+}
+
 static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer,
                                           int len)
 {
index 0c92e0251e84f64fd0e8b53679deda60803749f9..89e7076c919fd61abc04a2648ad67b1b4e2820f7 100644 (file)
@@ -30,13 +30,6 @@ struct ath10k_hif_sg_item {
        u16 len;
 };
 
-struct ath10k_hif_cb {
-       int (*tx_completion)(struct ath10k *ar,
-                            struct sk_buff *wbuf);
-       int (*rx_completion)(struct ath10k *ar,
-                            struct sk_buff *wbuf);
-};
-
 struct ath10k_hif_ops {
        /* send a scatter-gather list to the target */
        int (*tx_sg)(struct ath10k *ar, u8 pipe_id,
@@ -65,8 +58,7 @@ struct ath10k_hif_ops {
        void (*stop)(struct ath10k *ar);
 
        int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
-                                  u8 *ul_pipe, u8 *dl_pipe,
-                                  int *ul_is_polled, int *dl_is_polled);
+                                  u8 *ul_pipe, u8 *dl_pipe);
 
        void (*get_default_pipe)(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe);
 
@@ -80,9 +72,6 @@ struct ath10k_hif_ops {
         */
        void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
 
-       void (*set_callbacks)(struct ath10k *ar,
-                             struct ath10k_hif_cb *callbacks);
-
        u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
 
        u32 (*read32)(struct ath10k *ar, u32 address);
@@ -142,13 +131,10 @@ static inline void ath10k_hif_stop(struct ath10k *ar)
 
 static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar,
                                                 u16 service_id,
-                                                u8 *ul_pipe, u8 *dl_pipe,
-                                                int *ul_is_polled,
-                                                int *dl_is_polled)
+                                                u8 *ul_pipe, u8 *dl_pipe)
 {
        return ar->hif.ops->map_service_to_pipe(ar, service_id,
-                                               ul_pipe, dl_pipe,
-                                               ul_is_polled, dl_is_polled);
+                                               ul_pipe, dl_pipe);
 }
 
 static inline void ath10k_hif_get_default_pipe(struct ath10k *ar,
@@ -163,12 +149,6 @@ static inline void ath10k_hif_send_complete_check(struct ath10k *ar,
        ar->hif.ops->send_complete_check(ar, pipe_id, force);
 }
 
-static inline void ath10k_hif_set_callbacks(struct ath10k *ar,
-                                           struct ath10k_hif_cb *callbacks)
-{
-       ar->hif.ops->set_callbacks(ar, callbacks);
-}
-
 static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
                                                   u8 pipe_id)
 {
index 32d9ff1b19dcdf2d6d2609e9f95844fb5d144e8d..5b3c6bcf959854ed33776c3223adb06cb4f748f2 100644 (file)
 /* Send */
 /********/
 
-static inline void ath10k_htc_send_complete_check(struct ath10k_htc_ep *ep,
-                                                 int force)
-{
-       /*
-        * Check whether HIF has any prior sends that have finished,
-        * have not had the post-processing done.
-        */
-       ath10k_hif_send_complete_check(ep->htc->ar, ep->ul_pipe_id, force);
-}
-
 static void ath10k_htc_control_tx_complete(struct ath10k *ar,
                                           struct sk_buff *skb)
 {
@@ -181,24 +171,22 @@ err_pull:
        return ret;
 }
 
-static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
-                                           struct sk_buff *skb)
+void ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb)
 {
        struct ath10k_htc *htc = &ar->htc;
        struct ath10k_skb_cb *skb_cb;
        struct ath10k_htc_ep *ep;
 
        if (WARN_ON_ONCE(!skb))
-               return 0;
+               return;
 
        skb_cb = ATH10K_SKB_CB(skb);
        ep = &htc->endpoint[skb_cb->eid];
 
        ath10k_htc_notify_tx_completion(ep, skb);
        /* the skb now belongs to the completion handler */
-
-       return 0;
 }
+EXPORT_SYMBOL(ath10k_htc_tx_completion_handler);
 
 /***********/
 /* Receive */
@@ -304,8 +292,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc,
        return status;
 }
 
-static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
-                                           struct sk_buff *skb)
+void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb)
 {
        int status = 0;
        struct ath10k_htc *htc = &ar->htc;
@@ -326,21 +313,11 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
                ath10k_warn(ar, "HTC Rx: invalid eid %d\n", eid);
                ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad header", "",
                                hdr, sizeof(*hdr));
-               status = -EINVAL;
                goto out;
        }
 
        ep = &htc->endpoint[eid];
 
-       /*
-        * If this endpoint that received a message from the target has
-        * a to-target HIF pipe whose send completions are polled rather
-        * than interrupt-driven, this is a good point to ask HIF to check
-        * whether it has any completed sends to handle.
-        */
-       if (ep->ul_is_polled)
-               ath10k_htc_send_complete_check(ep, 1);
-
        payload_len = __le16_to_cpu(hdr->len);
 
        if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) {
@@ -348,7 +325,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
                            payload_len + sizeof(*hdr));
                ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len", "",
                                hdr, sizeof(*hdr));
-               status = -EINVAL;
                goto out;
        }
 
@@ -358,7 +334,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
                           skb->len, payload_len);
                ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len",
                                "", hdr, sizeof(*hdr));
-               status = -EINVAL;
                goto out;
        }
 
@@ -374,7 +349,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
                    (trailer_len > payload_len)) {
                        ath10k_warn(ar, "Invalid trailer length: %d\n",
                                    trailer_len);
-                       status = -EPROTO;
                        goto out;
                }
 
@@ -407,7 +381,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
                                 * sending unsolicited messages on the ep 0
                                 */
                                ath10k_warn(ar, "HTC rx ctrl still processing\n");
-                               status = -EINVAL;
                                complete(&htc->ctl_resp);
                                goto out;
                        }
@@ -439,9 +412,8 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
        skb = NULL;
 out:
        kfree_skb(skb);
-
-       return status;
 }
+EXPORT_SYMBOL(ath10k_htc_rx_completion_handler);
 
 static void ath10k_htc_control_rx_complete(struct ath10k *ar,
                                           struct sk_buff *skb)
@@ -767,9 +739,7 @@ setup:
        status = ath10k_hif_map_service_to_pipe(htc->ar,
                                                ep->service_id,
                                                &ep->ul_pipe_id,
-                                               &ep->dl_pipe_id,
-                                               &ep->ul_is_polled,
-                                               &ep->dl_is_polled);
+                                               &ep->dl_pipe_id);
        if (status)
                return status;
 
@@ -778,10 +748,6 @@ setup:
                   htc_service_name(ep->service_id), ep->ul_pipe_id,
                   ep->dl_pipe_id, ep->eid);
 
-       ath10k_dbg(ar, ATH10K_DBG_BOOT,
-                  "boot htc ep %d ul polled %d dl polled %d\n",
-                  ep->eid, ep->ul_is_polled, ep->dl_is_polled);
-
        if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
                ep->tx_credit_flow_enabled = false;
                ath10k_dbg(ar, ATH10K_DBG_BOOT,
@@ -841,7 +807,6 @@ int ath10k_htc_start(struct ath10k_htc *htc)
 /* registered target arrival callback from the HIF layer */
 int ath10k_htc_init(struct ath10k *ar)
 {
-       struct ath10k_hif_cb htc_callbacks;
        struct ath10k_htc_ep *ep = NULL;
        struct ath10k_htc *htc = &ar->htc;
 
@@ -849,15 +814,11 @@ int ath10k_htc_init(struct ath10k *ar)
 
        ath10k_htc_reset_endpoint_states(htc);
 
-       /* setup HIF layer callbacks */
-       htc_callbacks.rx_completion = ath10k_htc_rx_completion_handler;
-       htc_callbacks.tx_completion = ath10k_htc_tx_completion_handler;
        htc->ar = ar;
 
        /* Get HIF default pipe for HTC message exchange */
        ep = &htc->endpoint[ATH10K_HTC_EP_0];
 
-       ath10k_hif_set_callbacks(ar, &htc_callbacks);
        ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id);
 
        init_completion(&htc->ctl_resp);
index 527179c0edced2f008e6cfa37daf79d3cf94ec0d..e70aa38e6e05dbb1611981d5c26b332d05ac0ce5 100644 (file)
@@ -312,8 +312,6 @@ struct ath10k_htc_ep {
        int max_ep_message_len;
        u8 ul_pipe_id;
        u8 dl_pipe_id;
-       int ul_is_polled; /* call HIF to get tx completions */
-       int dl_is_polled; /* call HIF to fetch rx (not implemented) */
 
        u8 seq_no; /* for debugging */
        int tx_credits;
@@ -355,5 +353,7 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
 int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
                    struct sk_buff *packet);
 struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size);
+void ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb);
 
 #endif
index 57318751289555b8e956ef2441c0f2ffafc83835..2bad50e520b5bd2338ab479b13127aefe2dc0cf5 100644 (file)
@@ -1485,9 +1485,9 @@ struct ath10k_htt {
        spinlock_t tx_lock;
        int max_num_pending_tx;
        int num_pending_tx;
+       int num_pending_mgmt_tx;
        struct idr pending_tx;
        wait_queue_head_t empty_tx_wq;
-       struct dma_pool *tx_pool;
 
        /* set if host-fw communication goes haywire
         * used to avoid further failures */
@@ -1508,6 +1508,11 @@ struct ath10k_htt {
                dma_addr_t paddr;
                struct htt_msdu_ext_desc *vaddr;
        } frag_desc;
+
+       struct {
+               dma_addr_t paddr;
+               struct ath10k_htt_txbuf *vaddr;
+       } txbuf;
 };
 
 #define RX_HTT_HDR_STATUS_LEN 64
@@ -1586,8 +1591,9 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
 int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
                                u8 max_subfrms_ampdu,
                                u8 max_subfrms_amsdu);
+void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb);
 
-void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc);
 int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb);
 void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
 int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
index 1b7a04366256febc8438e02a01eb979102cb1d39..6060dda4e9101ac73c92cafb597c1d71592bb00e 100644 (file)
@@ -643,6 +643,8 @@ struct amsdu_subframe_hdr {
        __be16 len;
 } __packed;
 
+#define GROUP_ID_IS_SU_MIMO(x) ((x) == 0 || (x) == 63)
+
 static void ath10k_htt_rx_h_rates(struct ath10k *ar,
                                  struct ieee80211_rx_status *status,
                                  struct htt_rx_desc *rxd)
@@ -650,6 +652,7 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
        struct ieee80211_supported_band *sband;
        u8 cck, rate, bw, sgi, mcs, nss;
        u8 preamble = 0;
+       u8 group_id;
        u32 info1, info2, info3;
 
        info1 = __le32_to_cpu(rxd->ppdu_start.info1);
@@ -692,10 +695,50 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
        case HTT_RX_VHT_WITH_TXBF:
                /* VHT-SIG-A1 in info2, VHT-SIG-A2 in info3
                   TODO check this */
-               mcs = (info3 >> 4) & 0x0F;
-               nss = ((info2 >> 10) & 0x07) + 1;
                bw = info2 & 3;
                sgi = info3 & 1;
+               group_id = (info2 >> 4) & 0x3F;
+
+               if (GROUP_ID_IS_SU_MIMO(group_id)) {
+                       mcs = (info3 >> 4) & 0x0F;
+                       nss = ((info2 >> 10) & 0x07) + 1;
+               } else {
+                       /* Hardware doesn't decode VHT-SIG-B into Rx descriptor
+                        * so it's impossible to decode MCS. Also since
+                        * firmware consumes Group Id Management frames host
+                        * has no knowledge regarding group/user position
+                        * mapping so it's impossible to pick the correct Nsts
+                        * from VHT-SIG-A1.
+                        *
+                        * Bandwidth and SGI are valid so report the rateinfo
+                        * on best-effort basis.
+                        */
+                       mcs = 0;
+                       nss = 1;
+               }
+
+               if (mcs > 0x09) {
+                       ath10k_warn(ar, "invalid MCS received %u\n", mcs);
+                       ath10k_warn(ar, "rxd %08x mpdu start %08x %08x msdu start %08x %08x ppdu start %08x %08x %08x %08x %08x\n",
+                                   __le32_to_cpu(rxd->attention.flags),
+                                   __le32_to_cpu(rxd->mpdu_start.info0),
+                                   __le32_to_cpu(rxd->mpdu_start.info1),
+                                   __le32_to_cpu(rxd->msdu_start.common.info0),
+                                   __le32_to_cpu(rxd->msdu_start.common.info1),
+                                   rxd->ppdu_start.info0,
+                                   __le32_to_cpu(rxd->ppdu_start.info1),
+                                   __le32_to_cpu(rxd->ppdu_start.info2),
+                                   __le32_to_cpu(rxd->ppdu_start.info3),
+                                   __le32_to_cpu(rxd->ppdu_start.info4));
+
+                       ath10k_warn(ar, "msdu end %08x mpdu end %08x\n",
+                                   __le32_to_cpu(rxd->msdu_end.common.info0),
+                                   __le32_to_cpu(rxd->mpdu_end.info0));
+
+                       ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL,
+                                       "rx desc msdu payload: ",
+                                       rxd->msdu_payload, 50);
+               }
 
                status->rate_idx = mcs;
                status->vht_nss = nss;
@@ -2082,6 +2125,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
        /* Free the indication buffer */
        dev_kfree_skb_any(skb);
 }
+EXPORT_SYMBOL(ath10k_htt_t2h_msg_handler);
 
 static void ath10k_htt_txrx_compl_task(unsigned long ptr)
 {
index 43aa5e2d1b87d2751be83d52fdbc3bc6c89b7b17..16823970dbfd33608486b6854b0ed9d1142b8576 100644 (file)
 #include "txrx.h"
 #include "debug.h"
 
-void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc)
 {
+       if (limit_mgmt_desc)
+               htt->num_pending_mgmt_tx--;
+
        htt->num_pending_tx--;
        if (htt->num_pending_tx == htt->max_num_pending_tx - 1)
                ath10k_mac_tx_unlock(htt->ar, ATH10K_TX_PAUSE_Q_FULL);
 }
 
-static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
+                                     bool limit_mgmt_desc)
 {
        spin_lock_bh(&htt->tx_lock);
-       __ath10k_htt_tx_dec_pending(htt);
+       __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
        spin_unlock_bh(&htt->tx_lock);
 }
 
-static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt)
+static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
+                                    bool limit_mgmt_desc, bool is_probe_resp)
 {
+       struct ath10k *ar = htt->ar;
        int ret = 0;
 
        spin_lock_bh(&htt->tx_lock);
@@ -47,6 +53,15 @@ static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt)
                goto exit;
        }
 
+       if (limit_mgmt_desc) {
+               if (is_probe_resp && (htt->num_pending_mgmt_tx >
+                   ar->hw_params.max_probe_resp_desc_thres)) {
+                       ret = -EBUSY;
+                       goto exit;
+               }
+               htt->num_pending_mgmt_tx++;
+       }
+
        htt->num_pending_tx++;
        if (htt->num_pending_tx == htt->max_num_pending_tx)
                ath10k_mac_tx_lock(htt->ar, ATH10K_TX_PAUSE_Q_FULL);
@@ -93,9 +108,12 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
        spin_lock_init(&htt->tx_lock);
        idr_init(&htt->pending_tx);
 
-       htt->tx_pool = dma_pool_create("ath10k htt tx pool", htt->ar->dev,
-                                      sizeof(struct ath10k_htt_txbuf), 4, 0);
-       if (!htt->tx_pool) {
+       size = htt->max_num_pending_tx * sizeof(struct ath10k_htt_txbuf);
+       htt->txbuf.vaddr = dma_alloc_coherent(ar->dev, size,
+                                                 &htt->txbuf.paddr,
+                                                 GFP_DMA);
+       if (!htt->txbuf.vaddr) {
+               ath10k_err(ar, "failed to alloc tx buffer\n");
                ret = -ENOMEM;
                goto free_idr_pending_tx;
        }
@@ -110,14 +128,17 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
        if (!htt->frag_desc.vaddr) {
                ath10k_warn(ar, "failed to alloc fragment desc memory\n");
                ret = -ENOMEM;
-               goto free_tx_pool;
+               goto free_txbuf;
        }
 
 skip_frag_desc_alloc:
        return 0;
 
-free_tx_pool:
-       dma_pool_destroy(htt->tx_pool);
+free_txbuf:
+       size = htt->max_num_pending_tx *
+                         sizeof(struct ath10k_htt_txbuf);
+       dma_free_coherent(htt->ar->dev, size, htt->txbuf.vaddr,
+                         htt->txbuf.paddr);
 free_idr_pending_tx:
        idr_destroy(&htt->pending_tx);
        return ret;
@@ -145,7 +166,13 @@ void ath10k_htt_tx_free(struct ath10k_htt *htt)
 
        idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar);
        idr_destroy(&htt->pending_tx);
-       dma_pool_destroy(htt->tx_pool);
+
+       if (htt->txbuf.vaddr) {
+               size = htt->max_num_pending_tx *
+                                 sizeof(struct ath10k_htt_txbuf);
+               dma_free_coherent(htt->ar->dev, size, htt->txbuf.vaddr,
+                                 htt->txbuf.paddr);
+       }
 
        if (htt->frag_desc.vaddr) {
                size = htt->max_num_pending_tx *
@@ -160,6 +187,12 @@ void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
        dev_kfree_skb_any(skb);
 }
 
+void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb)
+{
+       dev_kfree_skb_any(skb);
+}
+EXPORT_SYMBOL(ath10k_htt_hif_tx_complete);
+
 int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
 {
        struct ath10k *ar = htt->ar;
@@ -417,8 +450,19 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        int len = 0;
        int msdu_id = -1;
        int res;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
+       bool limit_mgmt_desc = false;
+       bool is_probe_resp = false;
+
+       if (ar->hw_params.max_probe_resp_desc_thres) {
+               limit_mgmt_desc = true;
+
+               if (ieee80211_is_probe_resp(hdr->frame_control))
+                       is_probe_resp = true;
+       }
+
+       res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp);
 
-       res = ath10k_htt_tx_inc_pending(htt);
        if (res)
                goto err;
 
@@ -428,9 +472,9 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        spin_lock_bh(&htt->tx_lock);
        res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
        spin_unlock_bh(&htt->tx_lock);
-       if (res < 0) {
+       if (res < 0)
                goto err_tx_dec;
-       }
+
        msdu_id = res;
 
        txdesc = ath10k_htc_alloc_skb(ar, len);
@@ -476,7 +520,7 @@ err_free_msdu_id:
        ath10k_htt_tx_free_msdu_id(htt, msdu_id);
        spin_unlock_bh(&htt->tx_lock);
 err_tx_dec:
-       ath10k_htt_tx_dec_pending(htt);
+       ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
 err:
        return res;
 }
@@ -495,32 +539,37 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        int res;
        u8 flags0 = 0;
        u16 msdu_id, flags1 = 0;
-       dma_addr_t paddr = 0;
        u32 frags_paddr = 0;
        struct htt_msdu_ext_desc *ext_desc = NULL;
+       bool limit_mgmt_desc = false;
+       bool is_probe_resp = false;
+
+       if (unlikely(ieee80211_is_mgmt(hdr->frame_control)) &&
+           ar->hw_params.max_probe_resp_desc_thres) {
+               limit_mgmt_desc = true;
+
+               if (ieee80211_is_probe_resp(hdr->frame_control))
+                       is_probe_resp = true;
+       }
 
-       res = ath10k_htt_tx_inc_pending(htt);
+       res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp);
        if (res)
                goto err;
 
        spin_lock_bh(&htt->tx_lock);
        res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
        spin_unlock_bh(&htt->tx_lock);
-       if (res < 0) {
+       if (res < 0)
                goto err_tx_dec;
-       }
+
        msdu_id = res;
 
        prefetch_len = min(htt->prefetch_len, msdu->len);
        prefetch_len = roundup(prefetch_len, 4);
 
-       skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC,
-                                          &paddr);
-       if (!skb_cb->htt.txbuf) {
-               res = -ENOMEM;
-               goto err_free_msdu_id;
-       }
-       skb_cb->htt.txbuf_paddr = paddr;
+       skb_cb->htt.txbuf = &htt->txbuf.vaddr[msdu_id];
+       skb_cb->htt.txbuf_paddr = htt->txbuf.paddr +
+               (sizeof(struct ath10k_htt_txbuf) * msdu_id);
 
        if ((ieee80211_is_action(hdr->frame_control) ||
             ieee80211_is_deauth(hdr->frame_control) ||
@@ -528,7 +577,8 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
             ieee80211_has_protected(hdr->frame_control)) {
                skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
        } else if (!skb_cb->htt.nohwcrypt &&
-                  skb_cb->txmode == ATH10K_HW_TXRX_RAW) {
+                  skb_cb->txmode == ATH10K_HW_TXRX_RAW &&
+                  ieee80211_has_protected(hdr->frame_control)) {
                skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
        }
 
@@ -537,7 +587,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        res = dma_mapping_error(dev, skb_cb->paddr);
        if (res) {
                res = -EIO;
-               goto err_free_txbuf;
+               goto err_free_msdu_id;
        }
 
        switch (skb_cb->txmode) {
@@ -669,16 +719,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
 
 err_unmap_msdu:
        dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
-err_free_txbuf:
-       dma_pool_free(htt->tx_pool,
-                     skb_cb->htt.txbuf,
-                     skb_cb->htt.txbuf_paddr);
 err_free_msdu_id:
        spin_lock_bh(&htt->tx_lock);
        ath10k_htt_tx_free_msdu_id(htt, msdu_id);
        spin_unlock_bh(&htt->tx_lock);
 err_tx_dec:
-       ath10k_htt_tx_dec_pending(htt);
+       ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
 err:
        return res;
 }
index 678d72af4a9df73976b7513d9af17888bc1ef7d1..39966a05c1cc20f5b69e7111d5ddb711a070c5e9 100644 (file)
@@ -84,6 +84,15 @@ enum qca6174_chip_id_rev {
 #define QCA99X0_HW_2_0_BOARD_DATA_FILE "board.bin"
 #define QCA99X0_HW_2_0_PATCH_LOAD_ADDR 0x1234
 
+/* QCA9377 1.0 definitions */
+#define QCA9377_HW_1_0_DEV_VERSION     0x05020001
+#define QCA9377_HW_1_0_CHIP_ID_REV     0x1
+#define QCA9377_HW_1_0_FW_DIR          ATH10K_FW_DIR "/QCA9377/hw1.0"
+#define QCA9377_HW_1_0_FW_FILE         "firmware.bin"
+#define QCA9377_HW_1_0_OTP_FILE        "otp.bin"
+#define QCA9377_HW_1_0_BOARD_DATA_FILE "board.bin"
+#define QCA9377_HW_1_0_PATCH_LOAD_ADDR 0x1234
+
 #define ATH10K_FW_API2_FILE            "firmware-2.bin"
 #define ATH10K_FW_API3_FILE            "firmware-3.bin"
 
@@ -94,9 +103,13 @@ enum qca6174_chip_id_rev {
 #define ATH10K_FW_API5_FILE            "firmware-5.bin"
 
 #define ATH10K_FW_UTF_FILE             "utf.bin"
+#define ATH10K_FW_UTF_API2_FILE                "utf-2.bin"
 
 /* includes also the null byte */
 #define ATH10K_FIRMWARE_MAGIC               "QCA-ATH10K"
+#define ATH10K_BOARD_MAGIC                  "QCA-ATH10K-BOARD"
+
+#define ATH10K_BOARD_API2_FILE         "board-2.bin"
 
 #define REG_DUMP_COUNT_QCA988X 60
 
@@ -159,10 +172,21 @@ enum ath10k_fw_htt_op_version {
        ATH10K_FW_HTT_OP_VERSION_MAX,
 };
 
+enum ath10k_bd_ie_type {
+       /* contains sub IEs of enum ath10k_bd_ie_board_type */
+       ATH10K_BD_IE_BOARD = 0,
+};
+
+enum ath10k_bd_ie_board_type {
+       ATH10K_BD_IE_BOARD_NAME = 0,
+       ATH10K_BD_IE_BOARD_DATA = 1,
+};
+
 enum ath10k_hw_rev {
        ATH10K_HW_QCA988X,
        ATH10K_HW_QCA6174,
        ATH10K_HW_QCA99X0,
+       ATH10K_HW_QCA9377,
 };
 
 struct ath10k_hw_regs {
@@ -215,6 +239,7 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
 #define QCA_REV_988X(ar) ((ar)->hw_rev == ATH10K_HW_QCA988X)
 #define QCA_REV_6174(ar) ((ar)->hw_rev == ATH10K_HW_QCA6174)
 #define QCA_REV_99X0(ar) ((ar)->hw_rev == ATH10K_HW_QCA99X0)
+#define QCA_REV_9377(ar) ((ar)->hw_rev == ATH10K_HW_QCA9377)
 
 /* Known pecularities:
  *  - raw appears in nwifi decap, raw and nwifi appear in ethernet decap
@@ -413,16 +438,6 @@ enum ath10k_hw_rate_cck {
 /* Number of Copy Engines supported */
 #define CE_COUNT ar->hw_values->ce_count
 
-/*
- * Total number of PCIe MSI interrupts requested for all interrupt sources.
- * PCIe standard forces this to be a power of 2.
- * Some Host OS's limit MSI requests that can be granted to 8
- * so for now we abide by this limit and avoid requesting more
- * than that.
- */
-#define MSI_NUM_REQUEST_LOG2   3
-#define MSI_NUM_REQUEST                (1<<MSI_NUM_REQUEST_LOG2)
-
 /*
  * Granted MSIs are assigned as follows:
  * Firmware uses the first
index 64674c955d44745976beb2ee9e9d1639fe067bf0..a7411fe90cc43ebcef7590538cb3d6cb73a6a13a 100644 (file)
@@ -197,9 +197,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
                return -EOPNOTSUPP;
        }
 
-       if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
+       if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
                key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
-       }
 
        if (cmd == DISABLE_KEY) {
                arg.key_cipher = WMI_CIPHER_NONE;
@@ -1070,6 +1069,7 @@ static bool ath10k_mac_monitor_vdev_is_needed(struct ath10k *ar)
                return false;
 
        return ar->monitor ||
+              ar->filter_flags & FIF_OTHER_BSS ||
               test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
 }
 
@@ -1110,7 +1110,8 @@ static int ath10k_monitor_recalc(struct ath10k *ar)
 
                        ret = ath10k_monitor_stop(ar);
                        if (ret)
-                               ath10k_warn(ar, "failed to stop disallowed monitor: %d\n", ret);
+                               ath10k_warn(ar, "failed to stop disallowed monitor: %d\n",
+                                           ret);
                                /* not serious */
                }
 
@@ -2083,7 +2084,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
        enum ieee80211_band band;
        const u8 *ht_mcs_mask;
        const u16 *vht_mcs_mask;
-       int i, n, max_nss;
+       int i, n;
+       u8 max_nss;
        u32 stbc;
 
        lockdep_assert_held(&ar->conf_mutex);
@@ -2168,7 +2170,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
                        arg->peer_ht_rates.rates[i] = i;
        } else {
                arg->peer_ht_rates.num_rates = n;
-               arg->peer_num_spatial_streams = max_nss;
+               arg->peer_num_spatial_streams = min(sta->rx_nss, max_nss);
        }
 
        ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
@@ -3617,9 +3619,6 @@ static int ath10k_start_scan(struct ath10k *ar,
        }
        spin_unlock_bh(&ar->data_lock);
 
-       /* Add a 200ms margin to account for event/command processing */
-       ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
-                                    msecs_to_jiffies(arg->max_scan_time+200));
        return 0;
 }
 
@@ -3737,13 +3736,8 @@ static int ath10k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
 
        mutex_lock(&ar->conf_mutex);
 
-       if (ar->cfg_tx_chainmask) {
-               *tx_ant = ar->cfg_tx_chainmask;
-               *rx_ant = ar->cfg_rx_chainmask;
-       } else {
-               *tx_ant = ar->supp_tx_chainmask;
-               *rx_ant = ar->supp_rx_chainmask;
-       }
+       *tx_ant = ar->cfg_tx_chainmask;
+       *rx_ant = ar->cfg_rx_chainmask;
 
        mutex_unlock(&ar->conf_mutex);
 
@@ -3763,6 +3757,169 @@ static void ath10k_check_chain_mask(struct ath10k *ar, u32 cm, const char *dbg)
                    dbg, cm);
 }
 
+static int ath10k_mac_get_vht_cap_bf_sts(struct ath10k *ar)
+{
+       int nsts = ar->vht_cap_info;
+
+       nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
+       nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+
+       /* If firmware does not deliver to host number of space-time
+        * streams supported, assume it support up to 4 BF STS and return
+        * the value for VHT CAP: nsts-1)
+        */
+       if (nsts == 0)
+               return 3;
+
+       return nsts;
+}
+
+static int ath10k_mac_get_vht_cap_bf_sound_dim(struct ath10k *ar)
+{
+       int sound_dim = ar->vht_cap_info;
+
+       sound_dim &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK;
+       sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+
+       /* If the sounding dimension is not advertised by the firmware,
+        * let's use a default value of 1
+        */
+       if (sound_dim == 0)
+               return 1;
+
+       return sound_dim;
+}
+
+static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
+{
+       struct ieee80211_sta_vht_cap vht_cap = {0};
+       u16 mcs_map;
+       u32 val;
+       int i;
+
+       vht_cap.vht_supported = 1;
+       vht_cap.cap = ar->vht_cap_info;
+
+       if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+                               IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
+               val = ath10k_mac_get_vht_cap_bf_sts(ar);
+               val <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+               val &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
+
+               vht_cap.cap |= val;
+       }
+
+       if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+                               IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) {
+               val = ath10k_mac_get_vht_cap_bf_sound_dim(ar);
+               val <<= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+               val &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK;
+
+               vht_cap.cap |= val;
+       }
+
+       mcs_map = 0;
+       for (i = 0; i < 8; i++) {
+               if ((i < ar->num_rf_chains) && (ar->cfg_tx_chainmask & BIT(i)))
+                       mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2);
+               else
+                       mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2);
+       }
+
+       vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
+       vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
+
+       return vht_cap;
+}
+
+static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar)
+{
+       int i;
+       struct ieee80211_sta_ht_cap ht_cap = {0};
+
+       if (!(ar->ht_cap_info & WMI_HT_CAP_ENABLED))
+               return ht_cap;
+
+       ht_cap.ht_supported = 1;
+       ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+       ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
+       ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
+       ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI)
+               ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_HT40_SGI)
+               ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) {
+               u32 smps;
+
+               smps   = WLAN_HT_CAP_SM_PS_DYNAMIC;
+               smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+               ht_cap.cap |= smps;
+       }
+
+       if (ar->ht_cap_info & WMI_HT_CAP_TX_STBC)
+               ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_RX_STBC) {
+               u32 stbc;
+
+               stbc   = ar->ht_cap_info;
+               stbc  &= WMI_HT_CAP_RX_STBC;
+               stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT;
+               stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT;
+               stbc  &= IEEE80211_HT_CAP_RX_STBC;
+
+               ht_cap.cap |= stbc;
+       }
+
+       if (ar->ht_cap_info & WMI_HT_CAP_LDPC)
+               ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_L_SIG_TXOP_PROT)
+               ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT;
+
+       /* max AMSDU is implicitly taken from vht_cap_info */
+       if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK)
+               ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+       for (i = 0; i < ar->num_rf_chains; i++) {
+               if (ar->cfg_rx_chainmask & BIT(i))
+                       ht_cap.mcs.rx_mask[i] = 0xFF;
+       }
+
+       ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
+
+       return ht_cap;
+}
+
+static void ath10k_mac_setup_ht_vht_cap(struct ath10k *ar)
+{
+       struct ieee80211_supported_band *band;
+       struct ieee80211_sta_vht_cap vht_cap;
+       struct ieee80211_sta_ht_cap ht_cap;
+
+       ht_cap = ath10k_get_ht_cap(ar);
+       vht_cap = ath10k_create_vht_cap(ar);
+
+       if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
+               band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
+               band->ht_cap = ht_cap;
+
+               /* Enable the VHT support at 2.4 GHz */
+               band->vht_cap = vht_cap;
+       }
+       if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) {
+               band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
+               band->ht_cap = ht_cap;
+               band->vht_cap = vht_cap;
+       }
+}
+
 static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant)
 {
        int ret;
@@ -3795,6 +3952,9 @@ static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant)
                return ret;
        }
 
+       /* Reload HT/VHT capability */
+       ath10k_mac_setup_ht_vht_cap(ar);
+
        return 0;
 }
 
@@ -3885,9 +4045,7 @@ static int ath10k_start(struct ieee80211_hw *hw)
                }
        }
 
-       if (ar->cfg_tx_chainmask)
-               __ath10k_set_antenna(ar, ar->cfg_tx_chainmask,
-                                    ar->cfg_rx_chainmask);
+       __ath10k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask);
 
        /*
         * By default FW set ARP frames ac to voice (6). In that case ARP
@@ -3906,6 +4064,18 @@ static int ath10k_start(struct ieee80211_hw *hw)
                goto err_core_stop;
        }
 
+       if (test_bit(ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA,
+                    ar->fw_features)) {
+               ret = ath10k_wmi_pdev_enable_adaptive_cca(ar, 1,
+                                                         WMI_CCA_DETECT_LEVEL_AUTO,
+                                                         WMI_CCA_DETECT_MARGIN_AUTO);
+               if (ret) {
+                       ath10k_warn(ar, "failed to enable adaptive cca: %d\n",
+                                   ret);
+                       goto err_core_stop;
+               }
+       }
+
        ret = ath10k_wmi_pdev_set_param(ar,
                                        ar->wmi.pdev_param->ani_enable, 1);
        if (ret) {
@@ -4068,17 +4238,21 @@ static int ath10k_mac_set_txbf_conf(struct ath10k_vif *arvif)
 {
        u32 value = 0;
        struct ath10k *ar = arvif->ar;
+       int nsts;
+       int sound_dim;
 
        if (ath10k_wmi_get_txbf_conf_scheme(ar) != WMI_TXBF_CONF_BEFORE_ASSOC)
                return 0;
 
+       nsts = ath10k_mac_get_vht_cap_bf_sts(ar);
        if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
                                IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE))
-               value |= SM((ar->num_rf_chains - 1), WMI_TXBF_STS_CAP_OFFSET);
+               value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET);
 
+       sound_dim = ath10k_mac_get_vht_cap_bf_sound_dim(ar);
        if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
                                IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE))
-               value |= SM((ar->num_rf_chains - 1), WMI_BF_SOUND_DIM_OFFSET);
+               value |= SM(sound_dim, WMI_BF_SOUND_DIM_OFFSET);
 
        if (!value)
                return 0;
@@ -4175,6 +4349,14 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        case NL80211_IFTYPE_ADHOC:
                arvif->vdev_type = WMI_VDEV_TYPE_IBSS;
                break;
+       case NL80211_IFTYPE_MESH_POINT:
+               if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
+                       ret = -EINVAL;
+                       ath10k_warn(ar, "must load driver with rawmode=1 to add mesh interfaces\n");
+                       goto err;
+               }
+               arvif->vdev_type = WMI_VDEV_TYPE_AP;
+               break;
        case NL80211_IFTYPE_AP:
                arvif->vdev_type = WMI_VDEV_TYPE_AP;
 
@@ -4215,6 +4397,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
         * become corrupted, e.g. have garbled IEs or out-of-date TIM bitmap.
         */
        if (vif->type == NL80211_IFTYPE_ADHOC ||
+           vif->type == NL80211_IFTYPE_MESH_POINT ||
            vif->type == NL80211_IFTYPE_AP) {
                arvif->beacon_buf = dma_zalloc_coherent(ar->dev,
                                                        IEEE80211_MAX_FRAME_LEN,
@@ -4554,6 +4737,13 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                if (ret)
                        ath10k_warn(ar, "failed to update beacon template: %d\n",
                                    ret);
+
+               if (ieee80211_vif_is_mesh(vif)) {
+                       /* mesh doesn't use SSID but firmware needs it */
+                       strncpy(arvif->u.ap.ssid, "mesh",
+                               sizeof(arvif->u.ap.ssid));
+                       arvif->u.ap.ssid_len = 4;
+               }
        }
 
        if (changed & BSS_CHANGED_AP_PROBE_RESP) {
@@ -4607,7 +4797,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                                                info->use_cts_prot ? 1 : 0);
                if (ret)
                        ath10k_warn(ar, "failed to set protection mode %d on vdev %i: %d\n",
-                                       info->use_cts_prot, arvif->vdev_id, ret);
+                                   info->use_cts_prot, arvif->vdev_id, ret);
        }
 
        if (changed & BSS_CHANGED_ERP_SLOT) {
@@ -4751,6 +4941,11 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw,
                spin_unlock_bh(&ar->data_lock);
        }
 
+       /* Add a 200ms margin to account for event/command processing */
+       ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
+                                    msecs_to_jiffies(arg.max_scan_time +
+                                                     200));
+
 exit:
        mutex_unlock(&ar->conf_mutex);
        return ret;
@@ -5293,6 +5488,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
        } else if (old_state == IEEE80211_STA_AUTH &&
                   new_state == IEEE80211_STA_ASSOC &&
                   (vif->type == NL80211_IFTYPE_AP ||
+                   vif->type == NL80211_IFTYPE_MESH_POINT ||
                    vif->type == NL80211_IFTYPE_ADHOC)) {
                /*
                 * New association.
@@ -5328,6 +5524,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
        } else if (old_state == IEEE80211_STA_ASSOC &&
                    new_state == IEEE80211_STA_AUTH &&
                    (vif->type == NL80211_IFTYPE_AP ||
+                    vif->type == NL80211_IFTYPE_MESH_POINT ||
                     vif->type == NL80211_IFTYPE_ADHOC)) {
                /*
                 * Disassociation.
@@ -5901,7 +6098,7 @@ ath10k_mac_bitrate_mask_get_single_rate(struct ath10k *ar,
 }
 
 static int ath10k_mac_set_fixed_rate_params(struct ath10k_vif *arvif,
-                                           u8 rate, u8 nss, u8 sgi)
+                                           u8 rate, u8 nss, u8 sgi, u8 ldpc)
 {
        struct ath10k *ar = arvif->ar;
        u32 vdev_param;
@@ -5934,6 +6131,13 @@ static int ath10k_mac_set_fixed_rate_params(struct ath10k_vif *arvif,
                return ret;
        }
 
+       vdev_param = ar->wmi.vdev_param->ldpc;
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ldpc);
+       if (ret) {
+               ath10k_warn(ar, "failed to set ldpc param %d: %d\n", ldpc, ret);
+               return ret;
+       }
+
        return 0;
 }
 
@@ -5997,6 +6201,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
        u8 rate;
        u8 nss;
        u8 sgi;
+       u8 ldpc;
        int single_nss;
        int ret;
 
@@ -6006,6 +6211,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
        band = def.chan->band;
        ht_mcs_mask = mask->control[band].ht_mcs;
        vht_mcs_mask = mask->control[band].vht_mcs;
+       ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC);
 
        sgi = mask->control[band].gi;
        if (sgi == NL80211_TXRATE_FORCE_LGI)
@@ -6044,7 +6250,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
 
        mutex_lock(&ar->conf_mutex);
 
-       ret = ath10k_mac_set_fixed_rate_params(arvif, rate, nss, sgi);
+       ret = ath10k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc);
        if (ret) {
                ath10k_warn(ar, "failed to set fixed rate params on vdev %i: %d\n",
                            arvif->vdev_id, ret);
@@ -6144,7 +6350,7 @@ static int ath10k_ampdu_action(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               enum ieee80211_ampdu_mlme_action action,
                               struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                              u8 buf_size)
+                              u8 buf_size, bool amsdu)
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
@@ -6203,8 +6409,8 @@ ath10k_mac_update_rx_channel(struct ath10k *ar,
        rcu_read_lock();
        if (!ctx && ath10k_mac_num_chanctxs(ar) == 1) {
                ieee80211_iter_chan_contexts_atomic(ar->hw,
-                                       ath10k_mac_get_any_chandef_iter,
-                                       &def);
+                                                   ath10k_mac_get_any_chandef_iter,
+                                                   &def);
 
                if (vifs)
                        def = &vifs[0].new_ctx->def;
@@ -6218,6 +6424,94 @@ ath10k_mac_update_rx_channel(struct ath10k *ar,
        rcu_read_unlock();
 }
 
+static void
+ath10k_mac_update_vif_chan(struct ath10k *ar,
+                          struct ieee80211_vif_chanctx_switch *vifs,
+                          int n_vifs)
+{
+       struct ath10k_vif *arvif;
+       int ret;
+       int i;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       /* First stop monitor interface. Some FW versions crash if there's a
+        * lone monitor interface.
+        */
+       if (ar->monitor_started)
+               ath10k_monitor_stop(ar);
+
+       for (i = 0; i < n_vifs; i++) {
+               arvif = ath10k_vif_to_arvif(vifs[i].vif);
+
+               ath10k_dbg(ar, ATH10K_DBG_MAC,
+                          "mac chanctx switch vdev_id %i freq %hu->%hu width %d->%d\n",
+                          arvif->vdev_id,
+                          vifs[i].old_ctx->def.chan->center_freq,
+                          vifs[i].new_ctx->def.chan->center_freq,
+                          vifs[i].old_ctx->def.width,
+                          vifs[i].new_ctx->def.width);
+
+               if (WARN_ON(!arvif->is_started))
+                       continue;
+
+               if (WARN_ON(!arvif->is_up))
+                       continue;
+
+               ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
+               if (ret) {
+                       ath10k_warn(ar, "failed to down vdev %d: %d\n",
+                                   arvif->vdev_id, ret);
+                       continue;
+               }
+       }
+
+       /* All relevant vdevs are downed and associated channel resources
+        * should be available for the channel switch now.
+        */
+
+       spin_lock_bh(&ar->data_lock);
+       ath10k_mac_update_rx_channel(ar, NULL, vifs, n_vifs);
+       spin_unlock_bh(&ar->data_lock);
+
+       for (i = 0; i < n_vifs; i++) {
+               arvif = ath10k_vif_to_arvif(vifs[i].vif);
+
+               if (WARN_ON(!arvif->is_started))
+                       continue;
+
+               if (WARN_ON(!arvif->is_up))
+                       continue;
+
+               ret = ath10k_mac_setup_bcn_tmpl(arvif);
+               if (ret)
+                       ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n",
+                                   ret);
+
+               ret = ath10k_mac_setup_prb_tmpl(arvif);
+               if (ret)
+                       ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n",
+                                   ret);
+
+               ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def);
+               if (ret) {
+                       ath10k_warn(ar, "failed to restart vdev %d: %d\n",
+                                   arvif->vdev_id, ret);
+                       continue;
+               }
+
+               ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
+                                        arvif->bssid);
+               if (ret) {
+                       ath10k_warn(ar, "failed to bring vdev up %d: %d\n",
+                                   arvif->vdev_id, ret);
+                       continue;
+               }
+       }
+
+       ath10k_monitor_recalc(ar);
+}
+
 static int
 ath10k_mac_op_add_chanctx(struct ieee80211_hw *hw,
                          struct ieee80211_chanctx_conf *ctx)
@@ -6264,12 +6558,52 @@ ath10k_mac_op_remove_chanctx(struct ieee80211_hw *hw,
        mutex_unlock(&ar->conf_mutex);
 }
 
+struct ath10k_mac_change_chanctx_arg {
+       struct ieee80211_chanctx_conf *ctx;
+       struct ieee80211_vif_chanctx_switch *vifs;
+       int n_vifs;
+       int next_vif;
+};
+
+static void
+ath10k_mac_change_chanctx_cnt_iter(void *data, u8 *mac,
+                                  struct ieee80211_vif *vif)
+{
+       struct ath10k_mac_change_chanctx_arg *arg = data;
+
+       if (rcu_access_pointer(vif->chanctx_conf) != arg->ctx)
+               return;
+
+       arg->n_vifs++;
+}
+
+static void
+ath10k_mac_change_chanctx_fill_iter(void *data, u8 *mac,
+                                   struct ieee80211_vif *vif)
+{
+       struct ath10k_mac_change_chanctx_arg *arg = data;
+       struct ieee80211_chanctx_conf *ctx;
+
+       ctx = rcu_access_pointer(vif->chanctx_conf);
+       if (ctx != arg->ctx)
+               return;
+
+       if (WARN_ON(arg->next_vif == arg->n_vifs))
+               return;
+
+       arg->vifs[arg->next_vif].vif = vif;
+       arg->vifs[arg->next_vif].old_ctx = ctx;
+       arg->vifs[arg->next_vif].new_ctx = ctx;
+       arg->next_vif++;
+}
+
 static void
 ath10k_mac_op_change_chanctx(struct ieee80211_hw *hw,
                             struct ieee80211_chanctx_conf *ctx,
                             u32 changed)
 {
        struct ath10k *ar = hw->priv;
+       struct ath10k_mac_change_chanctx_arg arg = { .ctx = ctx };
 
        mutex_lock(&ar->conf_mutex);
 
@@ -6283,6 +6617,30 @@ ath10k_mac_op_change_chanctx(struct ieee80211_hw *hw,
        if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL))
                goto unlock;
 
+       if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
+               ieee80211_iterate_active_interfaces_atomic(
+                                       hw,
+                                       IEEE80211_IFACE_ITER_NORMAL,
+                                       ath10k_mac_change_chanctx_cnt_iter,
+                                       &arg);
+               if (arg.n_vifs == 0)
+                       goto radar;
+
+               arg.vifs = kcalloc(arg.n_vifs, sizeof(arg.vifs[0]),
+                                  GFP_KERNEL);
+               if (!arg.vifs)
+                       goto radar;
+
+               ieee80211_iterate_active_interfaces_atomic(
+                                       hw,
+                                       IEEE80211_IFACE_ITER_NORMAL,
+                                       ath10k_mac_change_chanctx_fill_iter,
+                                       &arg);
+               ath10k_mac_update_vif_chan(ar, arg.vifs, arg.n_vifs);
+               kfree(arg.vifs);
+       }
+
+radar:
        ath10k_recalc_radar_detection(ar);
 
        /* FIXME: How to configure Rx chains properly? */
@@ -6402,91 +6760,13 @@ ath10k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw,
                                 enum ieee80211_chanctx_switch_mode mode)
 {
        struct ath10k *ar = hw->priv;
-       struct ath10k_vif *arvif;
-       int ret;
-       int i;
 
        mutex_lock(&ar->conf_mutex);
 
        ath10k_dbg(ar, ATH10K_DBG_MAC,
                   "mac chanctx switch n_vifs %d mode %d\n",
                   n_vifs, mode);
-
-       /* First stop monitor interface. Some FW versions crash if there's a
-        * lone monitor interface.
-        */
-       if (ar->monitor_started)
-               ath10k_monitor_stop(ar);
-
-       for (i = 0; i < n_vifs; i++) {
-               arvif = ath10k_vif_to_arvif(vifs[i].vif);
-
-               ath10k_dbg(ar, ATH10K_DBG_MAC,
-                          "mac chanctx switch vdev_id %i freq %hu->%hu width %d->%d\n",
-                          arvif->vdev_id,
-                          vifs[i].old_ctx->def.chan->center_freq,
-                          vifs[i].new_ctx->def.chan->center_freq,
-                          vifs[i].old_ctx->def.width,
-                          vifs[i].new_ctx->def.width);
-
-               if (WARN_ON(!arvif->is_started))
-                       continue;
-
-               if (WARN_ON(!arvif->is_up))
-                       continue;
-
-               ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
-               if (ret) {
-                       ath10k_warn(ar, "failed to down vdev %d: %d\n",
-                                   arvif->vdev_id, ret);
-                       continue;
-               }
-       }
-
-       /* All relevant vdevs are downed and associated channel resources
-        * should be available for the channel switch now.
-        */
-
-       spin_lock_bh(&ar->data_lock);
-       ath10k_mac_update_rx_channel(ar, NULL, vifs, n_vifs);
-       spin_unlock_bh(&ar->data_lock);
-
-       for (i = 0; i < n_vifs; i++) {
-               arvif = ath10k_vif_to_arvif(vifs[i].vif);
-
-               if (WARN_ON(!arvif->is_started))
-                       continue;
-
-               if (WARN_ON(!arvif->is_up))
-                       continue;
-
-               ret = ath10k_mac_setup_bcn_tmpl(arvif);
-               if (ret)
-                       ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n",
-                                   ret);
-
-               ret = ath10k_mac_setup_prb_tmpl(arvif);
-               if (ret)
-                       ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n",
-                                   ret);
-
-               ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def);
-               if (ret) {
-                       ath10k_warn(ar, "failed to restart vdev %d: %d\n",
-                                   arvif->vdev_id, ret);
-                       continue;
-               }
-
-               ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
-                                        arvif->bssid);
-               if (ret) {
-                       ath10k_warn(ar, "failed to bring vdev up %d: %d\n",
-                                   arvif->vdev_id, ret);
-                       continue;
-               }
-       }
-
-       ath10k_monitor_recalc(ar);
+       ath10k_mac_update_vif_chan(ar, vifs, n_vifs);
 
        mutex_unlock(&ar->conf_mutex);
        return 0;
@@ -6642,6 +6922,9 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = {
        {
        .max    = 7,
        .types  = BIT(NL80211_IFTYPE_AP)
+#ifdef CONFIG_MAC80211_MESH
+               | BIT(NL80211_IFTYPE_MESH_POINT)
+#endif
        },
 };
 
@@ -6649,6 +6932,9 @@ static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = {
        {
        .max    = 8,
        .types  = BIT(NL80211_IFTYPE_AP)
+#ifdef CONFIG_MAC80211_MESH
+               | BIT(NL80211_IFTYPE_MESH_POINT)
+#endif
        },
 };
 
@@ -6686,6 +6972,9 @@ static const struct ieee80211_iface_limit ath10k_tlv_if_limit[] = {
        {
                .max = 2,
                .types = BIT(NL80211_IFTYPE_AP) |
+#ifdef CONFIG_MAC80211_MESH
+                        BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
                         BIT(NL80211_IFTYPE_P2P_CLIENT) |
                         BIT(NL80211_IFTYPE_P2P_GO),
        },
@@ -6707,6 +6996,9 @@ static const struct ieee80211_iface_limit ath10k_tlv_qcs_if_limit[] = {
        {
                .max = 1,
                .types = BIT(NL80211_IFTYPE_AP) |
+#ifdef CONFIG_MAC80211_MESH
+                        BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
                         BIT(NL80211_IFTYPE_P2P_GO),
        },
        {
@@ -6773,6 +7065,9 @@ static const struct ieee80211_iface_limit ath10k_10_4_if_limits[] = {
        {
                .max    = 16,
                .types  = BIT(NL80211_IFTYPE_AP)
+#ifdef CONFIG_MAC80211_MESH
+                       | BIT(NL80211_IFTYPE_MESH_POINT)
+#endif
        },
 };
 
@@ -6792,111 +7087,6 @@ static const struct ieee80211_iface_combination ath10k_10_4_if_comb[] = {
        },
 };
 
-static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
-{
-       struct ieee80211_sta_vht_cap vht_cap = {0};
-       u16 mcs_map;
-       u32 val;
-       int i;
-
-       vht_cap.vht_supported = 1;
-       vht_cap.cap = ar->vht_cap_info;
-
-       if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
-                               IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
-               val = ar->num_rf_chains - 1;
-               val <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
-               val &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
-
-               vht_cap.cap |= val;
-       }
-
-       if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
-                               IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) {
-               val = ar->num_rf_chains - 1;
-               val <<= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
-               val &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK;
-
-               vht_cap.cap |= val;
-       }
-
-       mcs_map = 0;
-       for (i = 0; i < 8; i++) {
-               if (i < ar->num_rf_chains)
-                       mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i*2);
-               else
-                       mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2);
-       }
-
-       vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
-       vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
-
-       return vht_cap;
-}
-
-static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar)
-{
-       int i;
-       struct ieee80211_sta_ht_cap ht_cap = {0};
-
-       if (!(ar->ht_cap_info & WMI_HT_CAP_ENABLED))
-               return ht_cap;
-
-       ht_cap.ht_supported = 1;
-       ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
-       ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
-       ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-       ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
-       ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT;
-
-       if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI)
-               ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
-
-       if (ar->ht_cap_info & WMI_HT_CAP_HT40_SGI)
-               ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
-
-       if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) {
-               u32 smps;
-
-               smps   = WLAN_HT_CAP_SM_PS_DYNAMIC;
-               smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT;
-
-               ht_cap.cap |= smps;
-       }
-
-       if (ar->ht_cap_info & WMI_HT_CAP_TX_STBC)
-               ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
-
-       if (ar->ht_cap_info & WMI_HT_CAP_RX_STBC) {
-               u32 stbc;
-
-               stbc   = ar->ht_cap_info;
-               stbc  &= WMI_HT_CAP_RX_STBC;
-               stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT;
-               stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT;
-               stbc  &= IEEE80211_HT_CAP_RX_STBC;
-
-               ht_cap.cap |= stbc;
-       }
-
-       if (ar->ht_cap_info & WMI_HT_CAP_LDPC)
-               ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
-
-       if (ar->ht_cap_info & WMI_HT_CAP_L_SIG_TXOP_PROT)
-               ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT;
-
-       /* max AMSDU is implicitly taken from vht_cap_info */
-       if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK)
-               ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
-
-       for (i = 0; i < ar->num_rf_chains; i++)
-               ht_cap.mcs.rx_mask[i] = 0xFF;
-
-       ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
-
-       return ht_cap;
-}
-
 static void ath10k_get_arvif_iter(void *data, u8 *mac,
                                  struct ieee80211_vif *vif)
 {
@@ -6938,8 +7128,6 @@ int ath10k_mac_register(struct ath10k *ar)
                WLAN_CIPHER_SUITE_AES_CMAC,
        };
        struct ieee80211_supported_band *band;
-       struct ieee80211_sta_vht_cap vht_cap;
-       struct ieee80211_sta_ht_cap ht_cap;
        void *channels;
        int ret;
 
@@ -6947,9 +7135,6 @@ int ath10k_mac_register(struct ath10k *ar)
 
        SET_IEEE80211_DEV(ar->hw, ar->dev);
 
-       ht_cap = ath10k_get_ht_cap(ar);
-       vht_cap = ath10k_create_vht_cap(ar);
-
        BUILD_BUG_ON((ARRAY_SIZE(ath10k_2ghz_channels) +
                      ARRAY_SIZE(ath10k_5ghz_channels)) !=
                     ATH10K_NUM_CHANS);
@@ -6968,10 +7153,6 @@ int ath10k_mac_register(struct ath10k *ar)
                band->channels = channels;
                band->n_bitrates = ath10k_g_rates_size;
                band->bitrates = ath10k_g_rates;
-               band->ht_cap = ht_cap;
-
-               /* Enable the VHT support at 2.4 GHz */
-               band->vht_cap = vht_cap;
 
                ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band;
        }
@@ -6990,17 +7171,18 @@ int ath10k_mac_register(struct ath10k *ar)
                band->channels = channels;
                band->n_bitrates = ath10k_a_rates_size;
                band->bitrates = ath10k_a_rates;
-               band->ht_cap = ht_cap;
-               band->vht_cap = vht_cap;
                ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band;
        }
 
+       ath10k_mac_setup_ht_vht_cap(ar);
+
        ar->hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
-               BIT(NL80211_IFTYPE_AP);
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_MESH_POINT);
 
-       ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask;
-       ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask;
+       ar->hw->wiphy->available_antennas_rx = ar->cfg_rx_chainmask;
+       ar->hw->wiphy->available_antennas_tx = ar->cfg_tx_chainmask;
 
        if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features))
                ar->hw->wiphy->interface_modes |=
@@ -7146,7 +7328,7 @@ int ath10k_mac_register(struct ath10k *ar)
                            ath10k_reg_notifier);
        if (ret) {
                ath10k_err(ar, "failed to initialise regulatory: %i\n", ret);
-               goto err_free;
+               goto err_dfs_detector_exit;
        }
 
        ar->hw->wiphy->cipher_suites = cipher_suites;
@@ -7155,7 +7337,7 @@ int ath10k_mac_register(struct ath10k *ar)
        ret = ieee80211_register_hw(ar->hw);
        if (ret) {
                ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
-               goto err_free;
+               goto err_dfs_detector_exit;
        }
 
        if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
@@ -7169,10 +7351,16 @@ int ath10k_mac_register(struct ath10k *ar)
 
 err_unregister:
        ieee80211_unregister_hw(ar->hw);
+
+err_dfs_detector_exit:
+       if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector)
+               ar->dfs_detector->exit(ar->dfs_detector);
+
 err_free:
        kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
        kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
 
+       SET_IEEE80211_DEV(ar->hw, NULL);
        return ret;
 }
 
index 1046ab65b9ab31adda450f8b66a82c7a82cea1e9..3fca200b986ca75baa0de2568d36460be709d834 100644 (file)
@@ -61,12 +61,14 @@ MODULE_PARM_DESC(reset_mode, "0: auto, 1: warm only (default: 0)");
 #define QCA6164_2_1_DEVICE_ID  (0x0041)
 #define QCA6174_2_1_DEVICE_ID  (0x003e)
 #define QCA99X0_2_0_DEVICE_ID  (0x0040)
+#define QCA9377_1_0_DEVICE_ID  (0x0042)
 
 static const struct pci_device_id ath10k_pci_id_table[] = {
        { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */
        { PCI_VDEVICE(ATHEROS, QCA6164_2_1_DEVICE_ID) }, /* PCI-E QCA6164 V2.1 */
        { PCI_VDEVICE(ATHEROS, QCA6174_2_1_DEVICE_ID) }, /* PCI-E QCA6174 V2.1 */
        { PCI_VDEVICE(ATHEROS, QCA99X0_2_0_DEVICE_ID) }, /* PCI-E QCA99X0 V2 */
+       { PCI_VDEVICE(ATHEROS, QCA9377_1_0_DEVICE_ID) }, /* PCI-E QCA9377 V1 */
        {0}
 };
 
@@ -90,6 +92,7 @@ static const struct ath10k_pci_supp_chip ath10k_pci_supp_chips[] = {
        { QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_2_CHIP_ID_REV },
 
        { QCA99X0_2_0_DEVICE_ID, QCA99X0_HW_2_0_CHIP_ID_REV },
+       { QCA9377_1_0_DEVICE_ID, QCA9377_HW_1_0_CHIP_ID_REV },
 };
 
 static void ath10k_pci_buffer_cleanup(struct ath10k *ar);
@@ -104,6 +107,10 @@ static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe,
                               struct ath10k_ce_pipe *rx_pipe,
                               struct bmi_xfer *xfer);
 static int ath10k_pci_qca99x0_chip_reset(struct ath10k *ar);
+static void ath10k_pci_htc_tx_cb(struct ath10k_ce_pipe *ce_state);
+static void ath10k_pci_htc_rx_cb(struct ath10k_ce_pipe *ce_state);
+static void ath10k_pci_htt_tx_cb(struct ath10k_ce_pipe *ce_state);
+static void ath10k_pci_htt_rx_cb(struct ath10k_ce_pipe *ce_state);
 
 static const struct ce_attr host_ce_config_wlan[] = {
        /* CE0: host->target HTC control and raw streams */
@@ -112,6 +119,7 @@ static const struct ce_attr host_ce_config_wlan[] = {
                .src_nentries = 16,
                .src_sz_max = 256,
                .dest_nentries = 0,
+               .send_cb = ath10k_pci_htc_tx_cb,
        },
 
        /* CE1: target->host HTT + HTC control */
@@ -120,6 +128,7 @@ static const struct ce_attr host_ce_config_wlan[] = {
                .src_nentries = 0,
                .src_sz_max = 2048,
                .dest_nentries = 512,
+               .recv_cb = ath10k_pci_htc_rx_cb,
        },
 
        /* CE2: target->host WMI */
@@ -128,6 +137,7 @@ static const struct ce_attr host_ce_config_wlan[] = {
                .src_nentries = 0,
                .src_sz_max = 2048,
                .dest_nentries = 128,
+               .recv_cb = ath10k_pci_htc_rx_cb,
        },
 
        /* CE3: host->target WMI */
@@ -136,6 +146,7 @@ static const struct ce_attr host_ce_config_wlan[] = {
                .src_nentries = 32,
                .src_sz_max = 2048,
                .dest_nentries = 0,
+               .send_cb = ath10k_pci_htc_tx_cb,
        },
 
        /* CE4: host->target HTT */
@@ -144,14 +155,16 @@ static const struct ce_attr host_ce_config_wlan[] = {
                .src_nentries = CE_HTT_H2T_MSG_SRC_NENTRIES,
                .src_sz_max = 256,
                .dest_nentries = 0,
+               .send_cb = ath10k_pci_htt_tx_cb,
        },
 
-       /* CE5: unused */
+       /* CE5: target->host HTT (HIF->HTT) */
        {
                .flags = CE_ATTR_FLAGS,
                .src_nentries = 0,
-               .src_sz_max = 0,
-               .dest_nentries = 0,
+               .src_sz_max = 512,
+               .dest_nentries = 512,
+               .recv_cb = ath10k_pci_htt_rx_cb,
        },
 
        /* CE6: target autonomous hif_memcpy */
@@ -257,12 +270,12 @@ static const struct ce_pipe_config target_ce_config_wlan[] = {
 
        /* NB: 50% of src nentries, since tx has 2 frags */
 
-       /* CE5: unused */
+       /* CE5: target->host HTT (HIF->HTT) */
        {
                .pipenum = __cpu_to_le32(5),
-               .pipedir = __cpu_to_le32(PIPEDIR_OUT),
+               .pipedir = __cpu_to_le32(PIPEDIR_IN),
                .nentries = __cpu_to_le32(32),
-               .nbytes_max = __cpu_to_le32(2048),
+               .nbytes_max = __cpu_to_le32(512),
                .flags = __cpu_to_le32(CE_ATTR_FLAGS),
                .reserved = __cpu_to_le32(0),
        },
@@ -396,7 +409,7 @@ static const struct service_to_pipe target_service_to_ce_map_wlan[] = {
        {
                __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA_MSG),
                __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
-               __cpu_to_le32(1),
+               __cpu_to_le32(5),
        },
 
        /* (Additions here) */
@@ -452,8 +465,12 @@ static int ath10k_pci_wake_wait(struct ath10k *ar)
        int curr_delay = 5;
 
        while (tot_delay < PCIE_WAKE_TIMEOUT) {
-               if (ath10k_pci_is_awake(ar))
+               if (ath10k_pci_is_awake(ar)) {
+                       if (tot_delay > PCIE_WAKE_LATE_US)
+                               ath10k_warn(ar, "device wakeup took %d ms which is unusally long, otherwise it works normally.\n",
+                                           tot_delay / 1000);
                        return 0;
+               }
 
                udelay(curr_delay);
                tot_delay += curr_delay;
@@ -465,12 +482,53 @@ static int ath10k_pci_wake_wait(struct ath10k *ar)
        return -ETIMEDOUT;
 }
 
+static int ath10k_pci_force_wake(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&ar_pci->ps_lock, flags);
+
+       if (!ar_pci->ps_awake) {
+               iowrite32(PCIE_SOC_WAKE_V_MASK,
+                         ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                         PCIE_SOC_WAKE_ADDRESS);
+
+               ret = ath10k_pci_wake_wait(ar);
+               if (ret == 0)
+                       ar_pci->ps_awake = true;
+       }
+
+       spin_unlock_irqrestore(&ar_pci->ps_lock, flags);
+
+       return ret;
+}
+
+static void ath10k_pci_force_sleep(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       unsigned long flags;
+
+       spin_lock_irqsave(&ar_pci->ps_lock, flags);
+
+       iowrite32(PCIE_SOC_WAKE_RESET,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+       ar_pci->ps_awake = false;
+
+       spin_unlock_irqrestore(&ar_pci->ps_lock, flags);
+}
+
 static int ath10k_pci_wake(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        unsigned long flags;
        int ret = 0;
 
+       if (ar_pci->pci_ps == 0)
+               return ret;
+
        spin_lock_irqsave(&ar_pci->ps_lock, flags);
 
        ath10k_dbg(ar, ATH10K_DBG_PCI_PS, "pci ps wake refcount %lu awake %d\n",
@@ -502,6 +560,9 @@ static void ath10k_pci_sleep(struct ath10k *ar)
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        unsigned long flags;
 
+       if (ar_pci->pci_ps == 0)
+               return;
+
        spin_lock_irqsave(&ar_pci->ps_lock, flags);
 
        ath10k_dbg(ar, ATH10K_DBG_PCI_PS, "pci ps sleep refcount %lu awake %d\n",
@@ -544,6 +605,11 @@ static void ath10k_pci_sleep_sync(struct ath10k *ar)
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        unsigned long flags;
 
+       if (ar_pci->pci_ps == 0) {
+               ath10k_pci_force_sleep(ar);
+               return;
+       }
+
        del_timer_sync(&ar_pci->ps_timer);
 
        spin_lock_irqsave(&ar_pci->ps_lock, flags);
@@ -682,8 +748,6 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe)
        dma_addr_t paddr;
        int ret;
 
-       lockdep_assert_held(&ar_pci->ce_lock);
-
        skb = dev_alloc_skb(pipe->buf_sz);
        if (!skb)
                return -ENOMEM;
@@ -701,9 +765,10 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe)
 
        ATH10K_SKB_RXCB(skb)->paddr = paddr;
 
+       spin_lock_bh(&ar_pci->ce_lock);
        ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr);
+       spin_unlock_bh(&ar_pci->ce_lock);
        if (ret) {
-               ath10k_warn(ar, "failed to post pci rx buf: %d\n", ret);
                dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb),
                                 DMA_FROM_DEVICE);
                dev_kfree_skb_any(skb);
@@ -713,25 +778,27 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe)
        return 0;
 }
 
-static void __ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe)
+static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe)
 {
        struct ath10k *ar = pipe->hif_ce_state;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl;
        int ret, num;
 
-       lockdep_assert_held(&ar_pci->ce_lock);
-
        if (pipe->buf_sz == 0)
                return;
 
        if (!ce_pipe->dest_ring)
                return;
 
+       spin_lock_bh(&ar_pci->ce_lock);
        num = __ath10k_ce_rx_num_free_bufs(ce_pipe);
+       spin_unlock_bh(&ar_pci->ce_lock);
        while (num--) {
                ret = __ath10k_pci_rx_post_buf(pipe);
                if (ret) {
+                       if (ret == -ENOSPC)
+                               break;
                        ath10k_warn(ar, "failed to post pci rx buf: %d\n", ret);
                        mod_timer(&ar_pci->rx_post_retry, jiffies +
                                  ATH10K_PCI_RX_POST_RETRY_MS);
@@ -740,25 +807,13 @@ static void __ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe)
        }
 }
 
-static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe)
-{
-       struct ath10k *ar = pipe->hif_ce_state;
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-
-       spin_lock_bh(&ar_pci->ce_lock);
-       __ath10k_pci_rx_post_pipe(pipe);
-       spin_unlock_bh(&ar_pci->ce_lock);
-}
-
 static void ath10k_pci_rx_post(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        int i;
 
-       spin_lock_bh(&ar_pci->ce_lock);
        for (i = 0; i < CE_COUNT; i++)
-               __ath10k_pci_rx_post_pipe(&ar_pci->pipe_info[i]);
-       spin_unlock_bh(&ar_pci->ce_lock);
+               ath10k_pci_rx_post_pipe(&ar_pci->pipe_info[i]);
 }
 
 static void ath10k_pci_rx_replenish_retry(unsigned long ptr)
@@ -775,6 +830,7 @@ static u32 ath10k_pci_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr)
        switch (ar->hw_rev) {
        case ATH10K_HW_QCA988X:
        case ATH10K_HW_QCA6174:
+       case ATH10K_HW_QCA9377:
                val = (ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
                                          CORE_CTRL_ADDRESS) &
                       0x7ff) << 21;
@@ -858,9 +914,8 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
                        goto done;
 
                i = 0;
-               while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL, &buf,
-                                                           &completed_nbytes,
-                                                           &id) != 0) {
+               while (ath10k_ce_completed_send_next_nolock(ce_diag,
+                                                           NULL) != 0) {
                        mdelay(1);
                        if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
                                ret = -EBUSY;
@@ -868,16 +923,6 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
                        }
                }
 
-               if (nbytes != completed_nbytes) {
-                       ret = -EIO;
-                       goto done;
-               }
-
-               if (buf != (u32)address) {
-                       ret = -EIO;
-                       goto done;
-               }
-
                i = 0;
                while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf,
                                                            &completed_nbytes,
@@ -1031,9 +1076,8 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
                        goto done;
 
                i = 0;
-               while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL, &buf,
-                                                           &completed_nbytes,
-                                                           &id) != 0) {
+               while (ath10k_ce_completed_send_next_nolock(ce_diag,
+                                                           NULL) != 0) {
                        mdelay(1);
 
                        if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
@@ -1042,16 +1086,6 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
                        }
                }
 
-               if (nbytes != completed_nbytes) {
-                       ret = -EIO;
-                       goto done;
-               }
-
-               if (buf != ce_data) {
-                       ret = -EIO;
-                       goto done;
-               }
-
                i = 0;
                while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf,
                                                            &completed_nbytes,
@@ -1102,20 +1136,14 @@ static int ath10k_pci_diag_write32(struct ath10k *ar, u32 address, u32 value)
 }
 
 /* Called by lower (CE) layer when a send to Target completes. */
-static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state)
+static void ath10k_pci_htc_tx_cb(struct ath10k_ce_pipe *ce_state)
 {
        struct ath10k *ar = ce_state->ar;
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
        struct sk_buff_head list;
        struct sk_buff *skb;
-       u32 ce_data;
-       unsigned int nbytes;
-       unsigned int transfer_id;
 
        __skb_queue_head_init(&list);
-       while (ath10k_ce_completed_send_next(ce_state, (void **)&skb, &ce_data,
-                                            &nbytes, &transfer_id) == 0) {
+       while (ath10k_ce_completed_send_next(ce_state, (void **)&skb) == 0) {
                /* no need to call tx completion for NULL pointers */
                if (skb == NULL)
                        continue;
@@ -1124,16 +1152,16 @@ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state)
        }
 
        while ((skb = __skb_dequeue(&list)))
-               cb->tx_completion(ar, skb);
+               ath10k_htc_tx_completion_handler(ar, skb);
 }
 
-/* Called by lower (CE) layer when data is received from the Target. */
-static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
+static void ath10k_pci_process_rx_cb(struct ath10k_ce_pipe *ce_state,
+                                    void (*callback)(struct ath10k *ar,
+                                                     struct sk_buff *skb))
 {
        struct ath10k *ar = ce_state->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        struct ath10k_pci_pipe *pipe_info =  &ar_pci->pipe_info[ce_state->id];
-       struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
        struct sk_buff *skb;
        struct sk_buff_head list;
        void *transfer_context;
@@ -1168,12 +1196,52 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
                ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci rx: ",
                                skb->data, skb->len);
 
-               cb->rx_completion(ar, skb);
+               callback(ar, skb);
        }
 
        ath10k_pci_rx_post_pipe(pipe_info);
 }
 
+/* Called by lower (CE) layer when data is received from the Target. */
+static void ath10k_pci_htc_rx_cb(struct ath10k_ce_pipe *ce_state)
+{
+       ath10k_pci_process_rx_cb(ce_state, ath10k_htc_rx_completion_handler);
+}
+
+/* Called by lower (CE) layer when a send to HTT Target completes. */
+static void ath10k_pci_htt_tx_cb(struct ath10k_ce_pipe *ce_state)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct sk_buff *skb;
+
+       while (ath10k_ce_completed_send_next(ce_state, (void **)&skb) == 0) {
+               /* no need to call tx completion for NULL pointers */
+               if (!skb)
+                       continue;
+
+               dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
+                                skb->len, DMA_TO_DEVICE);
+               ath10k_htt_hif_tx_complete(ar, skb);
+       }
+}
+
+static void ath10k_pci_htt_rx_deliver(struct ath10k *ar, struct sk_buff *skb)
+{
+       skb_pull(skb, sizeof(struct ath10k_htc_hdr));
+       ath10k_htt_t2h_msg_handler(ar, skb);
+}
+
+/* Called by lower (CE) layer when HTT data is received from the Target. */
+static void ath10k_pci_htt_rx_cb(struct ath10k_ce_pipe *ce_state)
+{
+       /* CE4 polling needs to be done whenever CE pipe which transports
+        * HTT Rx (target->host) is processed.
+        */
+       ath10k_ce_per_engine_service(ce_state->ar, 4);
+
+       ath10k_pci_process_rx_cb(ce_state, ath10k_pci_htt_rx_deliver);
+}
+
 static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
                                struct ath10k_hif_sg_item *items, int n_items)
 {
@@ -1343,17 +1411,6 @@ static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
        ath10k_ce_per_engine_service(ar, pipe);
 }
 
-static void ath10k_pci_hif_set_callbacks(struct ath10k *ar,
-                                        struct ath10k_hif_cb *callbacks)
-{
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-
-       ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif set callbacks\n");
-
-       memcpy(&ar_pci->msg_callbacks_current, callbacks,
-              sizeof(ar_pci->msg_callbacks_current));
-}
-
 static void ath10k_pci_kill_tasklet(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -1368,10 +1425,8 @@ static void ath10k_pci_kill_tasklet(struct ath10k *ar)
        del_timer_sync(&ar_pci->rx_post_retry);
 }
 
-static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar,
-                                             u16 service_id, u8 *ul_pipe,
-                                             u8 *dl_pipe, int *ul_is_polled,
-                                             int *dl_is_polled)
+static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id,
+                                             u8 *ul_pipe, u8 *dl_pipe)
 {
        const struct service_to_pipe *entry;
        bool ul_set = false, dl_set = false;
@@ -1379,9 +1434,6 @@ static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar,
 
        ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif map service\n");
 
-       /* polling for received messages not supported */
-       *dl_is_polled = 0;
-
        for (i = 0; i < ARRAY_SIZE(target_service_to_ce_map_wlan); i++) {
                entry = &target_service_to_ce_map_wlan[i];
 
@@ -1415,25 +1467,17 @@ static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar,
        if (WARN_ON(!ul_set || !dl_set))
                return -ENOENT;
 
-       *ul_is_polled =
-               (host_ce_config_wlan[*ul_pipe].flags & CE_ATTR_DIS_INTR) != 0;
-
        return 0;
 }
 
 static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
                                            u8 *ul_pipe, u8 *dl_pipe)
 {
-       int ul_is_polled, dl_is_polled;
-
        ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif get default pipe\n");
 
        (void)ath10k_pci_hif_map_service_to_pipe(ar,
                                                 ATH10K_HTC_SVC_ID_RSVD_CTRL,
-                                                ul_pipe,
-                                                dl_pipe,
-                                                &ul_is_polled,
-                                                &dl_is_polled);
+                                                ul_pipe, dl_pipe);
 }
 
 static void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar)
@@ -1443,6 +1487,7 @@ static void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar)
        switch (ar->hw_rev) {
        case ATH10K_HW_QCA988X:
        case ATH10K_HW_QCA6174:
+       case ATH10K_HW_QCA9377:
                val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
                                        CORE_CTRL_ADDRESS);
                val &= ~CORE_CTRL_PCIE_REG_31_MASK;
@@ -1464,6 +1509,7 @@ static void ath10k_pci_irq_msi_fw_unmask(struct ath10k *ar)
        switch (ar->hw_rev) {
        case ATH10K_HW_QCA988X:
        case ATH10K_HW_QCA6174:
+       case ATH10K_HW_QCA9377:
                val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
                                        CORE_CTRL_ADDRESS);
                val |= CORE_CTRL_PCIE_REG_31_MASK;
@@ -1504,6 +1550,7 @@ static void ath10k_pci_irq_enable(struct ath10k *ar)
 static int ath10k_pci_hif_start(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
        ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
 
        ath10k_pci_irq_enable(ar);
@@ -1553,7 +1600,6 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe)
        struct ath10k_pci *ar_pci;
        struct ath10k_ce_pipe *ce_pipe;
        struct ath10k_ce_ring *ce_ring;
-       struct ce_desc *ce_desc;
        struct sk_buff *skb;
        int i;
 
@@ -1568,10 +1614,6 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe)
        if (!pci_pipe->buf_sz)
                return;
 
-       ce_desc = ce_ring->shadow_base;
-       if (WARN_ON(!ce_desc))
-               return;
-
        for (i = 0; i < ce_ring->nentries; i++) {
                skb = ce_ring->per_transfer_context[i];
                if (!skb)
@@ -1579,7 +1621,7 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe)
 
                ce_ring->per_transfer_context[i] = NULL;
 
-               ar_pci->msg_callbacks_current.tx_completion(ar, skb);
+               ath10k_htc_tx_completion_handler(ar, skb);
        }
 }
 
@@ -1745,12 +1787,8 @@ err_dma:
 static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state)
 {
        struct bmi_xfer *xfer;
-       u32 ce_data;
-       unsigned int nbytes;
-       unsigned int transfer_id;
 
-       if (ath10k_ce_completed_send_next(ce_state, (void **)&xfer, &ce_data,
-                                         &nbytes, &transfer_id))
+       if (ath10k_ce_completed_send_next(ce_state, (void **)&xfer))
                return;
 
        xfer->tx_done = true;
@@ -1840,6 +1878,8 @@ static int ath10k_pci_get_num_banks(struct ath10k *ar)
                        return 9;
                }
                break;
+       case QCA9377_1_0_DEVICE_ID:
+               return 2;
        }
 
        ath10k_warn(ar, "unknown number of banks, assuming 1\n");
@@ -1999,9 +2039,7 @@ static int ath10k_pci_alloc_pipes(struct ath10k *ar)
                pipe->pipe_num = i;
                pipe->hif_ce_state = ar;
 
-               ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i],
-                                          ath10k_pci_ce_send_done,
-                                          ath10k_pci_ce_recv_data);
+               ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i]);
                if (ret) {
                        ath10k_err(ar, "failed to allocate copy engine pipe %d: %d\n",
                                   i, ret);
@@ -2257,7 +2295,7 @@ static int ath10k_pci_qca6174_chip_reset(struct ath10k *ar)
        ret = ath10k_pci_wait_for_target_init(ar);
        if (ret) {
                ath10k_warn(ar, "failed to wait for target after cold reset: %d\n",
-                               ret);
+                           ret);
                return ret;
        }
 
@@ -2302,6 +2340,8 @@ static int ath10k_pci_chip_reset(struct ath10k *ar)
                return ath10k_pci_qca988x_chip_reset(ar);
        else if (QCA_REV_6174(ar))
                return ath10k_pci_qca6174_chip_reset(ar);
+       else if (QCA_REV_9377(ar))
+               return ath10k_pci_qca6174_chip_reset(ar);
        else if (QCA_REV_99X0(ar))
                return ath10k_pci_qca99x0_chip_reset(ar);
        else
@@ -2397,6 +2437,15 @@ static int ath10k_pci_hif_resume(struct ath10k *ar)
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        struct pci_dev *pdev = ar_pci->pdev;
        u32 val;
+       int ret = 0;
+
+       if (ar_pci->pci_ps == 0) {
+               ret = ath10k_pci_force_wake(ar);
+               if (ret) {
+                       ath10k_err(ar, "failed to wake up target: %d\n", ret);
+                       return ret;
+               }
+       }
 
        /* Suspend/Resume resets the PCI configuration space, so we have to
         * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
@@ -2407,7 +2456,7 @@ static int ath10k_pci_hif_resume(struct ath10k *ar)
        if ((val & 0x0000ff00) != 0)
                pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
 
-       return 0;
+       return ret;
 }
 #endif
 
@@ -2421,7 +2470,6 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
        .map_service_to_pipe    = ath10k_pci_hif_map_service_to_pipe,
        .get_default_pipe       = ath10k_pci_hif_get_default_pipe,
        .send_complete_check    = ath10k_pci_hif_send_complete_check,
-       .set_callbacks          = ath10k_pci_hif_set_callbacks,
        .get_free_queue_number  = ath10k_pci_hif_get_free_queue_number,
        .power_up               = ath10k_pci_hif_power_up,
        .power_down             = ath10k_pci_hif_power_down,
@@ -2501,6 +2549,16 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg)
 {
        struct ath10k *ar = arg;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       if (ar_pci->pci_ps == 0) {
+               ret = ath10k_pci_force_wake(ar);
+               if (ret) {
+                       ath10k_warn(ar, "failed to wake device up on irq: %d\n",
+                                   ret);
+                       return IRQ_NONE;
+               }
+       }
 
        if (ar_pci->num_msi_intrs == 0) {
                if (!ath10k_pci_irq_pending(ar))
@@ -2609,12 +2667,9 @@ static int ath10k_pci_request_irq(struct ath10k *ar)
                return ath10k_pci_request_irq_legacy(ar);
        case 1:
                return ath10k_pci_request_irq_msi(ar);
-       case MSI_NUM_REQUEST:
+       default:
                return ath10k_pci_request_irq_msix(ar);
        }
-
-       ath10k_warn(ar, "unknown irq configuration upon request\n");
-       return -EINVAL;
 }
 
 static void ath10k_pci_free_irq(struct ath10k *ar)
@@ -2657,7 +2712,7 @@ static int ath10k_pci_init_irq(struct ath10k *ar)
 
        /* Try MSI-X */
        if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO) {
-               ar_pci->num_msi_intrs = MSI_NUM_REQUEST;
+               ar_pci->num_msi_intrs = MSI_ASSIGN_CE_MAX + 1;
                ret = pci_enable_msi_range(ar_pci->pdev, ar_pci->num_msi_intrs,
                                           ar_pci->num_msi_intrs);
                if (ret > 0)
@@ -2705,18 +2760,13 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar)
        switch (ar_pci->num_msi_intrs) {
        case 0:
                ath10k_pci_deinit_irq_legacy(ar);
-               return 0;
-       case 1:
-               /* fall-through */
-       case MSI_NUM_REQUEST:
-               pci_disable_msi(ar_pci->pdev);
-               return 0;
+               break;
        default:
                pci_disable_msi(ar_pci->pdev);
+               break;
        }
 
-       ath10k_warn(ar, "unknown irq configuration upon deinit\n");
-       return -EINVAL;
+       return 0;
 }
 
 static int ath10k_pci_wait_for_target_init(struct ath10k *ar)
@@ -2908,17 +2958,25 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
        struct ath10k_pci *ar_pci;
        enum ath10k_hw_rev hw_rev;
        u32 chip_id;
+       bool pci_ps;
 
        switch (pci_dev->device) {
        case QCA988X_2_0_DEVICE_ID:
                hw_rev = ATH10K_HW_QCA988X;
+               pci_ps = false;
                break;
        case QCA6164_2_1_DEVICE_ID:
        case QCA6174_2_1_DEVICE_ID:
                hw_rev = ATH10K_HW_QCA6174;
+               pci_ps = true;
                break;
        case QCA99X0_2_0_DEVICE_ID:
                hw_rev = ATH10K_HW_QCA99X0;
+               pci_ps = false;
+               break;
+       case QCA9377_1_0_DEVICE_ID:
+               hw_rev = ATH10K_HW_QCA9377;
+               pci_ps = true;
                break;
        default:
                WARN_ON(1);
@@ -2932,19 +2990,21 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
                return -ENOMEM;
        }
 
-       ath10k_dbg(ar, ATH10K_DBG_PCI, "pci probe\n");
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "pci probe %04x:%04x %04x:%04x\n",
+                  pdev->vendor, pdev->device,
+                  pdev->subsystem_vendor, pdev->subsystem_device);
 
        ar_pci = ath10k_pci_priv(ar);
        ar_pci->pdev = pdev;
        ar_pci->dev = &pdev->dev;
        ar_pci->ar = ar;
        ar->dev_id = pci_dev->device;
+       ar_pci->pci_ps = pci_ps;
 
-       if (pdev->subsystem_vendor || pdev->subsystem_device)
-               scnprintf(ar->spec_board_id, sizeof(ar->spec_board_id),
-                         "%04x:%04x:%04x:%04x",
-                         pdev->vendor, pdev->device,
-                         pdev->subsystem_vendor, pdev->subsystem_device);
+       ar->id.vendor = pdev->vendor;
+       ar->id.device = pdev->device;
+       ar->id.subsystem_vendor = pdev->subsystem_vendor;
+       ar->id.subsystem_device = pdev->subsystem_device;
 
        spin_lock_init(&ar_pci->ce_lock);
        spin_lock_init(&ar_pci->ps_lock);
@@ -2970,6 +3030,14 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
        ath10k_pci_ce_deinit(ar);
        ath10k_pci_irq_disable(ar);
 
+       if (ar_pci->pci_ps == 0) {
+               ret = ath10k_pci_force_wake(ar);
+               if (ret) {
+                       ath10k_warn(ar, "failed to wake up device : %d\n", ret);
+                       goto err_free_pipes;
+               }
+       }
+
        ret = ath10k_pci_init_irq(ar);
        if (ret) {
                ath10k_err(ar, "failed to init irqs: %d\n", ret);
@@ -3098,13 +3166,20 @@ MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API3_FILE);
 MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API4_FILE);
 MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API5_FILE);
 MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_BOARD_API2_FILE);
 
 /* QCA6174 2.1 firmware files */
 MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" ATH10K_FW_API4_FILE);
 MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" ATH10K_FW_API5_FILE);
 MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" QCA6174_HW_2_1_BOARD_DATA_FILE);
+MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" ATH10K_BOARD_API2_FILE);
 
 /* QCA6174 3.1 firmware files */
 MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_FW_API4_FILE);
 MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_FW_API5_FILE);
 MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" QCA6174_HW_3_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_BOARD_API2_FILE);
+
+/* QCA9377 1.0 firmware files */
+MODULE_FIRMWARE(QCA9377_HW_1_0_FW_DIR "/" ATH10K_FW_API5_FILE);
+MODULE_FIRMWARE(QCA9377_HW_1_0_FW_DIR "/" QCA9377_HW_1_0_BOARD_DATA_FILE);
index 8d364fb8f743eb6f8ed488c88d83b8adf25b6536..f91bf333cb75e7cfb0fb974ccf5a80217876b3d9 100644 (file)
@@ -175,8 +175,6 @@ struct ath10k_pci {
 
        struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX];
 
-       struct ath10k_hif_cb msg_callbacks_current;
-
        /* Copy Engine used for Diagnostic Accesses */
        struct ath10k_ce_pipe *ce_diag;
 
@@ -221,6 +219,12 @@ struct ath10k_pci {
         * powersave register state changes.
         */
        bool ps_awake;
+
+       /* pci power save, disable for QCA988X and QCA99X0.
+        * Writing 'false' to this variable avoids frequent locking
+        * on MMIO read/write.
+        */
+       bool pci_ps;
 };
 
 static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
@@ -230,7 +234,8 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
 
 #define ATH10K_PCI_RX_POST_RETRY_MS 50
 #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
-#define PCIE_WAKE_TIMEOUT 10000        /* 10ms */
+#define PCIE_WAKE_TIMEOUT 30000        /* 30ms */
+#define PCIE_WAKE_LATE_US 10000        /* 10ms */
 
 #define BAR_NUM 0
 
index 768bef6290995b4e2b4c80c6c3e09e7b3f15893a..05a421bc322ae03a3adffaac03c732b10fadca00 100644 (file)
@@ -450,6 +450,9 @@ Fw Mode/SubMode Mask
 #define QCA6174_BOARD_DATA_SZ     8192
 #define QCA6174_BOARD_EXT_DATA_SZ 0
 
+#define QCA9377_BOARD_DATA_SZ     QCA6174_BOARD_DATA_SZ
+#define QCA9377_BOARD_EXT_DATA_SZ 0
+
 #define QCA99X0_BOARD_DATA_SZ    12288
 #define QCA99X0_BOARD_EXT_DATA_SZ 0
 
index b084f88da1028ef8920b4bdcc01460ed304713b3..1d5a2fdcbf56f1c9beacd08d6a5b201b2b97d752 100644 (file)
@@ -139,11 +139,181 @@ static int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[])
        return cfg80211_testmode_reply(skb);
 }
 
-static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
+static int ath10k_tm_fetch_utf_firmware_api_2(struct ath10k *ar)
+{
+       size_t len, magic_len, ie_len;
+       struct ath10k_fw_ie *hdr;
+       char filename[100];
+       __le32 *version;
+       const u8 *data;
+       int ie_id, ret;
+
+       snprintf(filename, sizeof(filename), "%s/%s",
+                ar->hw_params.fw.dir, ATH10K_FW_UTF_API2_FILE);
+
+       /* load utf firmware image */
+       ret = request_firmware(&ar->testmode.utf, filename, ar->dev);
+       if (ret) {
+               ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",
+                           filename, ret);
+               return ret;
+       }
+
+       data = ar->testmode.utf->data;
+       len = ar->testmode.utf->size;
+
+       /* FIXME: call release_firmware() in error cases */
+
+       /* magic also includes the null byte, check that as well */
+       magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1;
+
+       if (len < magic_len) {
+               ath10k_err(ar, "utf firmware file is too small to contain magic\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) {
+               ath10k_err(ar, "invalid firmware magic\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* jump over the padding */
+       magic_len = ALIGN(magic_len, 4);
+
+       len -= magic_len;
+       data += magic_len;
+
+       /* loop elements */
+       while (len > sizeof(struct ath10k_fw_ie)) {
+               hdr = (struct ath10k_fw_ie *)data;
+
+               ie_id = le32_to_cpu(hdr->id);
+               ie_len = le32_to_cpu(hdr->len);
+
+               len -= sizeof(*hdr);
+               data += sizeof(*hdr);
+
+               if (len < ie_len) {
+                       ath10k_err(ar, "invalid length for FW IE %d (%zu < %zu)\n",
+                                  ie_id, len, ie_len);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               switch (ie_id) {
+               case ATH10K_FW_IE_FW_VERSION:
+                       if (ie_len > sizeof(ar->testmode.utf_version) - 1)
+                               break;
+
+                       memcpy(ar->testmode.utf_version, data, ie_len);
+                       ar->testmode.utf_version[ie_len] = '\0';
+
+                       ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
+                                  "testmode found fw utf version %s\n",
+                                  ar->testmode.utf_version);
+                       break;
+               case ATH10K_FW_IE_TIMESTAMP:
+                       /* ignore timestamp, but don't warn about it either */
+                       break;
+               case ATH10K_FW_IE_FW_IMAGE:
+                       ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
+                                  "testmode found fw image ie (%zd B)\n",
+                                  ie_len);
+
+                       ar->testmode.utf_firmware_data = data;
+                       ar->testmode.utf_firmware_len = ie_len;
+                       break;
+               case ATH10K_FW_IE_WMI_OP_VERSION:
+                       if (ie_len != sizeof(u32))
+                               break;
+                       version = (__le32 *)data;
+                       ar->testmode.op_version = le32_to_cpup(version);
+                       ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode found fw ie wmi op version %d\n",
+                                  ar->testmode.op_version);
+                       break;
+               default:
+                       ath10k_warn(ar, "Unknown testmode FW IE: %u\n",
+                                   le32_to_cpu(hdr->id));
+                       break;
+               }
+               /* jump over the padding */
+               ie_len = ALIGN(ie_len, 4);
+
+               len -= ie_len;
+               data += ie_len;
+       }
+
+       if (!ar->testmode.utf_firmware_data || !ar->testmode.utf_firmware_len) {
+               ath10k_err(ar, "No ATH10K_FW_IE_FW_IMAGE found\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       return 0;
+
+err:
+       release_firmware(ar->testmode.utf);
+
+       return ret;
+}
+
+static int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar)
 {
        char filename[100];
        int ret;
 
+       snprintf(filename, sizeof(filename), "%s/%s",
+                ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE);
+
+       /* load utf firmware image */
+       ret = request_firmware(&ar->testmode.utf, filename, ar->dev);
+       if (ret) {
+               ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",
+                           filename, ret);
+               return ret;
+       }
+
+       /* We didn't find FW UTF API 1 ("utf.bin") does not advertise
+        * firmware features. Do an ugly hack where we force the firmware
+        * features to match with 10.1 branch so that wmi.c will use the
+        * correct WMI interface.
+        */
+
+       ar->testmode.op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
+       ar->testmode.utf_firmware_data = ar->testmode.utf->data;
+       ar->testmode.utf_firmware_len = ar->testmode.utf->size;
+
+       return 0;
+}
+
+static int ath10k_tm_fetch_firmware(struct ath10k *ar)
+{
+       int ret;
+
+       ret = ath10k_tm_fetch_utf_firmware_api_2(ar);
+       if (ret == 0) {
+               ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2");
+               return 0;
+       }
+
+       ret = ath10k_tm_fetch_utf_firmware_api_1(ar);
+       if (ret) {
+               ath10k_err(ar, "failed to fetch utf firmware binary: %d", ret);
+               return ret;
+       }
+
+       ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using utf api 1");
+
+       return 0;
+}
+
+static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
+{
+       const char *ver;
+       int ret;
+
        ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n");
 
        mutex_lock(&ar->conf_mutex);
@@ -165,36 +335,27 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
                goto err;
        }
 
-       snprintf(filename, sizeof(filename), "%s/%s",
-                ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE);
-
-       /* load utf firmware image */
-       ret = request_firmware(&ar->testmode.utf, filename, ar->dev);
+       ret = ath10k_tm_fetch_firmware(ar);
        if (ret) {
-               ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",
-                           filename, ret);
+               ath10k_err(ar, "failed to fetch UTF firmware: %d", ret);
                goto err;
        }
 
        spin_lock_bh(&ar->data_lock);
-
        ar->testmode.utf_monitor = true;
-
        spin_unlock_bh(&ar->data_lock);
-
        BUILD_BUG_ON(sizeof(ar->fw_features) !=
                     sizeof(ar->testmode.orig_fw_features));
 
        memcpy(ar->testmode.orig_fw_features, ar->fw_features,
               sizeof(ar->fw_features));
        ar->testmode.orig_wmi_op_version = ar->wmi.op_version;
-
-       /* utf.bin firmware image does not advertise firmware features. Do
-        * an ugly hack where we force the firmware features so that wmi.c
-        * will use the correct WMI interface.
-        */
        memset(ar->fw_features, 0, sizeof(ar->fw_features));
-       ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
+
+       ar->wmi.op_version = ar->testmode.op_version;
+
+       ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode wmi version %d\n",
+                  ar->wmi.op_version);
 
        ret = ath10k_hif_power_up(ar);
        if (ret) {
@@ -212,7 +373,12 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
 
        ar->state = ATH10K_STATE_UTF;
 
-       ath10k_info(ar, "UTF firmware started\n");
+       if (strlen(ar->testmode.utf_version) > 0)
+               ver = ar->testmode.utf_version;
+       else
+               ver = "API 1";
+
+       ath10k_info(ar, "UTF firmware %s started\n", ver);
 
        mutex_unlock(&ar->conf_mutex);
 
index 1a899d70dc5db5e6dea73280a42f06218285de77..60fe562e304110435a481f2e8fcf6181efb11688 100644 (file)
@@ -215,6 +215,6 @@ err_cooling_destroy:
 
 void ath10k_thermal_unregister(struct ath10k *ar)
 {
-       thermal_cooling_device_unregister(ar->thermal.cdev);
        sysfs_remove_link(&ar->dev->kobj, "cooling_device");
+       thermal_cooling_device_unregister(ar->thermal.cdev);
 }
index e4a9c4c8d0cb7e8f7a9a89372e6537d9efc9887e..6d1105ab4592b1cb6434b9fb6b98702288324b5f 100644 (file)
@@ -52,6 +52,9 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
        struct ieee80211_tx_info *info;
        struct ath10k_skb_cb *skb_cb;
        struct sk_buff *msdu;
+       struct ieee80211_hdr *hdr;
+       __le16 fc;
+       bool limit_mgmt_desc = false;
 
        ath10k_dbg(ar, ATH10K_DBG_HTT,
                   "htt tx completion msdu_id %u discard %d no_ack %d success %d\n",
@@ -72,21 +75,23 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
                spin_unlock_bh(&htt->tx_lock);
                return;
        }
+
+       hdr = (struct ieee80211_hdr *)msdu->data;
+       fc = hdr->frame_control;
+
+       if (unlikely(ieee80211_is_mgmt(fc)) &&
+           ar->hw_params.max_probe_resp_desc_thres)
+               limit_mgmt_desc = true;
+
        ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
-       __ath10k_htt_tx_dec_pending(htt);
+       __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
        if (htt->num_pending_tx == 0)
                wake_up(&htt->empty_tx_wq);
        spin_unlock_bh(&htt->tx_lock);
 
        skb_cb = ATH10K_SKB_CB(msdu);
-
        dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
 
-       if (skb_cb->htt.txbuf)
-               dma_pool_free(htt->tx_pool,
-                             skb_cb->htt.txbuf,
-                             skb_cb->htt.txbuf_paddr);
-
        ath10k_report_offchan_tx(htt->ar, msdu);
 
        info = IEEE80211_SKB_CB(msdu);
index 248ffc3d6620cfb9daeb9c35ce14d3ad4f021b84..8f4f6a892581c2ac7e976e08138e37740d0b0a33 100644 (file)
@@ -177,6 +177,15 @@ struct wmi_ops {
                                                const struct wmi_tdls_peer_capab_arg *cap,
                                                const struct wmi_channel_arg *chan);
        struct sk_buff *(*gen_adaptive_qcs)(struct ath10k *ar, bool enable);
+       struct sk_buff *(*gen_pdev_get_tpc_config)(struct ath10k *ar,
+                                                  u32 param);
+       void (*fw_stats_fill)(struct ath10k *ar,
+                             struct ath10k_fw_stats *fw_stats,
+                             char *buf);
+       struct sk_buff *(*gen_pdev_enable_adaptive_cca)(struct ath10k *ar,
+                                                       u8 enable,
+                                                       u32 detect_level,
+                                                       u32 detect_margin);
 };
 
 int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
@@ -1270,4 +1279,52 @@ ath10k_wmi_adaptive_qcs(struct ath10k *ar, bool enable)
        return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->adaptive_qcs_cmdid);
 }
 
+static inline int
+ath10k_wmi_pdev_get_tpc_config(struct ath10k *ar, u32 param)
+{
+       struct sk_buff *skb;
+
+       if (!ar->wmi.ops->gen_pdev_get_tpc_config)
+               return -EOPNOTSUPP;
+
+       skb = ar->wmi.ops->gen_pdev_get_tpc_config(ar, param);
+
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->pdev_get_tpc_config_cmdid);
+}
+
+static inline int
+ath10k_wmi_fw_stats_fill(struct ath10k *ar, struct ath10k_fw_stats *fw_stats,
+                        char *buf)
+{
+       if (!ar->wmi.ops->fw_stats_fill)
+               return -EOPNOTSUPP;
+
+       ar->wmi.ops->fw_stats_fill(ar, fw_stats, buf);
+       return 0;
+}
+
+static inline int
+ath10k_wmi_pdev_enable_adaptive_cca(struct ath10k *ar, u8 enable,
+                                   u32 detect_level, u32 detect_margin)
+{
+       struct sk_buff *skb;
+
+       if (!ar->wmi.ops->gen_pdev_enable_adaptive_cca)
+               return -EOPNOTSUPP;
+
+       skb = ar->wmi.ops->gen_pdev_enable_adaptive_cca(ar, enable,
+                                                       detect_level,
+                                                       detect_margin);
+
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->pdev_enable_adaptive_cca_cmdid);
+}
+
 #endif
index b5849b3fd2f0cd73941d490e8ae202014432e5f8..6fbd17b69469b34b0ca434c35a554ffad7b44564 100644 (file)
@@ -23,6 +23,7 @@
 #include "wmi-ops.h"
 #include "wmi-tlv.h"
 #include "p2p.h"
+#include "testmode.h"
 
 /***************/
 /* TLV helpers */
@@ -419,6 +420,7 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
 {
        struct wmi_cmd_hdr *cmd_hdr;
        enum wmi_tlv_event_id id;
+       bool consumed;
 
        cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
        id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
@@ -428,6 +430,18 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
 
        trace_ath10k_wmi_event(ar, id, skb->data, skb->len);
 
+       consumed = ath10k_tm_event_wmi(ar, id, skb);
+
+       /* Ready event must be handled normally also in UTF mode so that we
+        * know the UTF firmware has booted, others we are just bypass WMI
+        * events to testmode.
+        */
+       if (consumed && id != WMI_TLV_READY_EVENTID) {
+               ath10k_dbg(ar, ATH10K_DBG_WMI,
+                          "wmi tlv testmode consumed 0x%x\n", id);
+               goto out;
+       }
+
        switch (id) {
        case WMI_TLV_MGMT_RX_EVENTID:
                ath10k_wmi_event_mgmt_rx(ar, skb);
@@ -3468,6 +3482,7 @@ static const struct wmi_ops wmi_tlv_ops = {
        .gen_update_fw_tdls_state = ath10k_wmi_tlv_op_gen_update_fw_tdls_state,
        .gen_tdls_peer_update = ath10k_wmi_tlv_op_gen_tdls_peer_update,
        .gen_adaptive_qcs = ath10k_wmi_tlv_op_gen_adaptive_qcs,
+       .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill,
 };
 
 /************/
index ce01107ef37aaacd413b0bb98affbc0dc481afff..7569db0f69b5eed8f69d65c3e881f56bf54129b4 100644 (file)
@@ -148,6 +148,7 @@ static struct wmi_cmd_map wmi_cmd_map = {
        .gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID,
        .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID,
        .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
+       .pdev_enable_adaptive_cca_cmdid = WMI_CMD_UNSUPPORTED,
        .scan_update_request_cmdid = WMI_CMD_UNSUPPORTED,
        .vdev_standby_response_cmdid = WMI_CMD_UNSUPPORTED,
        .vdev_resume_response_cmdid = WMI_CMD_UNSUPPORTED,
@@ -313,6 +314,7 @@ static struct wmi_cmd_map wmi_10x_cmd_map = {
        .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID,
        .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID,
        .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
+       .pdev_enable_adaptive_cca_cmdid = WMI_CMD_UNSUPPORTED,
        .scan_update_request_cmdid = WMI_CMD_UNSUPPORTED,
        .vdev_standby_response_cmdid = WMI_CMD_UNSUPPORTED,
        .vdev_resume_response_cmdid = WMI_CMD_UNSUPPORTED,
@@ -477,6 +479,7 @@ static struct wmi_cmd_map wmi_10_2_4_cmd_map = {
        .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID,
        .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID,
        .pdev_get_temperature_cmdid = WMI_10_2_PDEV_GET_TEMPERATURE_CMDID,
+       .pdev_enable_adaptive_cca_cmdid = WMI_10_2_SET_CCA_PARAMS,
        .scan_update_request_cmdid = WMI_CMD_UNSUPPORTED,
        .vdev_standby_response_cmdid = WMI_CMD_UNSUPPORTED,
        .vdev_resume_response_cmdid = WMI_CMD_UNSUPPORTED,
@@ -1407,6 +1410,7 @@ static struct wmi_cmd_map wmi_10_2_cmd_map = {
        .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID,
        .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID,
        .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
+       .pdev_enable_adaptive_cca_cmdid = WMI_CMD_UNSUPPORTED,
        .scan_update_request_cmdid = WMI_CMD_UNSUPPORTED,
        .vdev_standby_response_cmdid = WMI_CMD_UNSUPPORTED,
        .vdev_resume_response_cmdid = WMI_CMD_UNSUPPORTED,
@@ -2475,6 +2479,47 @@ void ath10k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src,
        dst->txop_ovf = __le32_to_cpu(src->txop_ovf);
 }
 
+static void
+ath10k_wmi_10_4_pull_pdev_stats_tx(const struct wmi_10_4_pdev_stats_tx *src,
+                                  struct ath10k_fw_stats_pdev *dst)
+{
+       dst->comp_queued = __le32_to_cpu(src->comp_queued);
+       dst->comp_delivered = __le32_to_cpu(src->comp_delivered);
+       dst->msdu_enqued = __le32_to_cpu(src->msdu_enqued);
+       dst->mpdu_enqued = __le32_to_cpu(src->mpdu_enqued);
+       dst->wmm_drop = __le32_to_cpu(src->wmm_drop);
+       dst->local_enqued = __le32_to_cpu(src->local_enqued);
+       dst->local_freed = __le32_to_cpu(src->local_freed);
+       dst->hw_queued = __le32_to_cpu(src->hw_queued);
+       dst->hw_reaped = __le32_to_cpu(src->hw_reaped);
+       dst->underrun = __le32_to_cpu(src->underrun);
+       dst->tx_abort = __le32_to_cpu(src->tx_abort);
+       dst->mpdus_requed = __le32_to_cpu(src->mpdus_requed);
+       dst->tx_ko = __le32_to_cpu(src->tx_ko);
+       dst->data_rc = __le32_to_cpu(src->data_rc);
+       dst->self_triggers = __le32_to_cpu(src->self_triggers);
+       dst->sw_retry_failure = __le32_to_cpu(src->sw_retry_failure);
+       dst->illgl_rate_phy_err = __le32_to_cpu(src->illgl_rate_phy_err);
+       dst->pdev_cont_xretry = __le32_to_cpu(src->pdev_cont_xretry);
+       dst->pdev_tx_timeout = __le32_to_cpu(src->pdev_tx_timeout);
+       dst->pdev_resets = __le32_to_cpu(src->pdev_resets);
+       dst->phy_underrun = __le32_to_cpu(src->phy_underrun);
+       dst->txop_ovf = __le32_to_cpu(src->txop_ovf);
+       dst->hw_paused = __le32_to_cpu(src->hw_paused);
+       dst->seq_posted = __le32_to_cpu(src->seq_posted);
+       dst->seq_failed_queueing =
+               __le32_to_cpu(src->seq_failed_queueing);
+       dst->seq_completed = __le32_to_cpu(src->seq_completed);
+       dst->seq_restarted = __le32_to_cpu(src->seq_restarted);
+       dst->mu_seq_posted = __le32_to_cpu(src->mu_seq_posted);
+       dst->mpdus_sw_flush = __le32_to_cpu(src->mpdus_sw_flush);
+       dst->mpdus_hw_filter = __le32_to_cpu(src->mpdus_hw_filter);
+       dst->mpdus_truncated = __le32_to_cpu(src->mpdus_truncated);
+       dst->mpdus_ack_failed = __le32_to_cpu(src->mpdus_ack_failed);
+       dst->mpdus_hw_filter = __le32_to_cpu(src->mpdus_hw_filter);
+       dst->mpdus_expired = __le32_to_cpu(src->mpdus_expired);
+}
+
 void ath10k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src,
                                   struct ath10k_fw_stats_pdev *dst)
 {
@@ -2785,6 +2830,86 @@ static int ath10k_wmi_10_2_4_op_pull_fw_stats(struct ath10k *ar,
        return 0;
 }
 
+static int ath10k_wmi_10_4_op_pull_fw_stats(struct ath10k *ar,
+                                           struct sk_buff *skb,
+                                           struct ath10k_fw_stats *stats)
+{
+       const struct wmi_10_2_stats_event *ev = (void *)skb->data;
+       u32 num_pdev_stats;
+       u32 num_pdev_ext_stats;
+       u32 num_vdev_stats;
+       u32 num_peer_stats;
+       int i;
+
+       if (!skb_pull(skb, sizeof(*ev)))
+               return -EPROTO;
+
+       num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
+       num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats);
+       num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
+       num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
+
+       for (i = 0; i < num_pdev_stats; i++) {
+               const struct wmi_10_4_pdev_stats *src;
+               struct ath10k_fw_stats_pdev *dst;
+
+               src = (void *)skb->data;
+               if (!skb_pull(skb, sizeof(*src)))
+                       return -EPROTO;
+
+               dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+               if (!dst)
+                       continue;
+
+               ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
+               ath10k_wmi_10_4_pull_pdev_stats_tx(&src->tx, dst);
+               ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
+               dst->rx_ovfl_errs = __le32_to_cpu(src->rx_ovfl_errs);
+               ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst);
+
+               list_add_tail(&dst->list, &stats->pdevs);
+       }
+
+       for (i = 0; i < num_pdev_ext_stats; i++) {
+               const struct wmi_10_2_pdev_ext_stats *src;
+
+               src = (void *)skb->data;
+               if (!skb_pull(skb, sizeof(*src)))
+                       return -EPROTO;
+
+               /* FIXME: expose values to userspace
+                *
+                * Note: Even though this loop seems to do nothing it is
+                * required to parse following sub-structures properly.
+                */
+       }
+
+       /* fw doesn't implement vdev stats */
+
+       for (i = 0; i < num_peer_stats; i++) {
+               const struct wmi_10_4_peer_stats *src;
+               struct ath10k_fw_stats_peer *dst;
+
+               src = (void *)skb->data;
+               if (!skb_pull(skb, sizeof(*src)))
+                       return -EPROTO;
+
+               dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+               if (!dst)
+                       continue;
+
+               ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
+               dst->peer_rssi = __le32_to_cpu(src->peer_rssi);
+               dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate);
+               dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
+               /* FIXME: expose 10.4 specific values */
+
+               list_add_tail(&dst->list, &stats->peers);
+       }
+
+       return 0;
+}
+
 void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb)
 {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
@@ -3018,8 +3143,6 @@ static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif,
                        memcpy(skb_put(bcn, arvif->u.ap.noa_len),
                               arvif->u.ap.noa_data,
                               arvif->u.ap.noa_len);
-
-       return;
 }
 
 static int ath10k_wmi_op_pull_swba_ev(struct ath10k *ar, struct sk_buff *skb,
@@ -3507,7 +3630,7 @@ void ath10k_wmi_event_spectral_scan(struct ath10k *ar,
                                                          tsf);
                        if (res < 0) {
                                ath10k_dbg(ar, ATH10K_DBG_WMI, "failed to process fft report: %d\n",
-                                           res);
+                                          res);
                                return;
                        }
                        break;
@@ -3835,9 +3958,258 @@ void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb)
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n");
 }
 
+static u8 ath10k_tpc_config_get_rate(struct ath10k *ar,
+                                    struct wmi_pdev_tpc_config_event *ev,
+                                    u32 rate_idx, u32 num_chains,
+                                    u32 rate_code, u8 type)
+{
+       u8 tpc, num_streams, preamble, ch, stm_idx;
+
+       num_streams = ATH10K_HW_NSS(rate_code);
+       preamble = ATH10K_HW_PREAMBLE(rate_code);
+       ch = num_chains - 1;
+
+       tpc = min_t(u8, ev->rates_array[rate_idx], ev->max_reg_allow_pow[ch]);
+
+       if (__le32_to_cpu(ev->num_tx_chain) <= 1)
+               goto out;
+
+       if (preamble == WMI_RATE_PREAMBLE_CCK)
+               goto out;
+
+       stm_idx = num_streams - 1;
+       if (num_chains <= num_streams)
+               goto out;
+
+       switch (type) {
+       case WMI_TPC_TABLE_TYPE_STBC:
+               tpc = min_t(u8, tpc,
+                           ev->max_reg_allow_pow_agstbc[ch - 1][stm_idx]);
+               break;
+       case WMI_TPC_TABLE_TYPE_TXBF:
+               tpc = min_t(u8, tpc,
+                           ev->max_reg_allow_pow_agtxbf[ch - 1][stm_idx]);
+               break;
+       case WMI_TPC_TABLE_TYPE_CDD:
+               tpc = min_t(u8, tpc,
+                           ev->max_reg_allow_pow_agcdd[ch - 1][stm_idx]);
+               break;
+       default:
+               ath10k_warn(ar, "unknown wmi tpc table type: %d\n", type);
+               tpc = 0;
+               break;
+       }
+
+out:
+       return tpc;
+}
+
+static void ath10k_tpc_config_disp_tables(struct ath10k *ar,
+                                         struct wmi_pdev_tpc_config_event *ev,
+                                         struct ath10k_tpc_stats *tpc_stats,
+                                         u8 *rate_code, u16 *pream_table, u8 type)
+{
+       u32 i, j, pream_idx, flags;
+       u8 tpc[WMI_TPC_TX_N_CHAIN];
+       char tpc_value[WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE];
+       char buff[WMI_TPC_BUF_SIZE];
+
+       flags = __le32_to_cpu(ev->flags);
+
+       switch (type) {
+       case WMI_TPC_TABLE_TYPE_CDD:
+               if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD)) {
+                       ath10k_dbg(ar, ATH10K_DBG_WMI, "CDD not supported\n");
+                       tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG;
+                       return;
+               }
+               break;
+       case WMI_TPC_TABLE_TYPE_STBC:
+               if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_STBC)) {
+                       ath10k_dbg(ar, ATH10K_DBG_WMI, "STBC not supported\n");
+                       tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG;
+                       return;
+               }
+               break;
+       case WMI_TPC_TABLE_TYPE_TXBF:
+               if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_TXBF)) {
+                       ath10k_dbg(ar, ATH10K_DBG_WMI, "TXBF not supported\n");
+                       tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG;
+                       return;
+               }
+               break;
+       default:
+               ath10k_dbg(ar, ATH10K_DBG_WMI,
+                          "invalid table type in wmi tpc event: %d\n", type);
+               return;
+       }
+
+       pream_idx = 0;
+       for (i = 0; i < __le32_to_cpu(ev->rate_max); i++) {
+               memset(tpc_value, 0, sizeof(tpc_value));
+               memset(buff, 0, sizeof(buff));
+               if (i == pream_table[pream_idx])
+                       pream_idx++;
+
+               for (j = 0; j < WMI_TPC_TX_N_CHAIN; j++) {
+                       if (j >= __le32_to_cpu(ev->num_tx_chain))
+                               break;
+
+                       tpc[j] = ath10k_tpc_config_get_rate(ar, ev, i, j + 1,
+                                                           rate_code[i],
+                                                           type);
+                       snprintf(buff, sizeof(buff), "%8d ", tpc[j]);
+                       strncat(tpc_value, buff, strlen(buff));
+               }
+               tpc_stats->tpc_table[type].pream_idx[i] = pream_idx;
+               tpc_stats->tpc_table[type].rate_code[i] = rate_code[i];
+               memcpy(tpc_stats->tpc_table[type].tpc_value[i],
+                      tpc_value, sizeof(tpc_value));
+       }
+}
+
 void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
 {
-       ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n");
+       u32 i, j, pream_idx, num_tx_chain;
+       u8 rate_code[WMI_TPC_RATE_MAX], rate_idx;
+       u16 pream_table[WMI_TPC_PREAM_TABLE_MAX];
+       struct wmi_pdev_tpc_config_event *ev;
+       struct ath10k_tpc_stats *tpc_stats;
+
+       ev = (struct wmi_pdev_tpc_config_event *)skb->data;
+
+       tpc_stats = kzalloc(sizeof(*tpc_stats), GFP_ATOMIC);
+       if (!tpc_stats)
+               return;
+
+       /* Create the rate code table based on the chains supported */
+       rate_idx = 0;
+       pream_idx = 0;
+
+       /* Fill CCK rate code */
+       for (i = 0; i < 4; i++) {
+               rate_code[rate_idx] =
+                       ATH10K_HW_RATECODE(i, 0, WMI_RATE_PREAMBLE_CCK);
+               rate_idx++;
+       }
+       pream_table[pream_idx] = rate_idx;
+       pream_idx++;
+
+       /* Fill OFDM rate code */
+       for (i = 0; i < 8; i++) {
+               rate_code[rate_idx] =
+                       ATH10K_HW_RATECODE(i, 0, WMI_RATE_PREAMBLE_OFDM);
+               rate_idx++;
+       }
+       pream_table[pream_idx] = rate_idx;
+       pream_idx++;
+
+       num_tx_chain = __le32_to_cpu(ev->num_tx_chain);
+
+       /* Fill HT20 rate code */
+       for (i = 0; i < num_tx_chain; i++) {
+               for (j = 0; j < 8; j++) {
+                       rate_code[rate_idx] =
+                       ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_HT);
+                       rate_idx++;
+               }
+       }
+       pream_table[pream_idx] = rate_idx;
+       pream_idx++;
+
+       /* Fill HT40 rate code */
+       for (i = 0; i < num_tx_chain; i++) {
+               for (j = 0; j < 8; j++) {
+                       rate_code[rate_idx] =
+                       ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_HT);
+                       rate_idx++;
+               }
+       }
+       pream_table[pream_idx] = rate_idx;
+       pream_idx++;
+
+       /* Fill VHT20 rate code */
+       for (i = 0; i < __le32_to_cpu(ev->num_tx_chain); i++) {
+               for (j = 0; j < 10; j++) {
+                       rate_code[rate_idx] =
+                       ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_VHT);
+                       rate_idx++;
+               }
+       }
+       pream_table[pream_idx] = rate_idx;
+       pream_idx++;
+
+       /* Fill VHT40 rate code */
+       for (i = 0; i < num_tx_chain; i++) {
+               for (j = 0; j < 10; j++) {
+                       rate_code[rate_idx] =
+                       ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_VHT);
+                       rate_idx++;
+               }
+       }
+       pream_table[pream_idx] = rate_idx;
+       pream_idx++;
+
+       /* Fill VHT80 rate code */
+       for (i = 0; i < num_tx_chain; i++) {
+               for (j = 0; j < 10; j++) {
+                       rate_code[rate_idx] =
+                       ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_VHT);
+                       rate_idx++;
+               }
+       }
+       pream_table[pream_idx] = rate_idx;
+       pream_idx++;
+
+       rate_code[rate_idx++] =
+               ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_CCK);
+       rate_code[rate_idx++] =
+               ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_OFDM);
+       rate_code[rate_idx++] =
+               ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_CCK);
+       rate_code[rate_idx++] =
+               ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_OFDM);
+       rate_code[rate_idx++] =
+               ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_OFDM);
+
+       pream_table[pream_idx] = ATH10K_TPC_PREAM_TABLE_END;
+
+       tpc_stats->chan_freq = __le32_to_cpu(ev->chan_freq);
+       tpc_stats->phy_mode = __le32_to_cpu(ev->phy_mode);
+       tpc_stats->ctl = __le32_to_cpu(ev->ctl);
+       tpc_stats->reg_domain = __le32_to_cpu(ev->reg_domain);
+       tpc_stats->twice_antenna_gain = a_sle32_to_cpu(ev->twice_antenna_gain);
+       tpc_stats->twice_antenna_reduction =
+               __le32_to_cpu(ev->twice_antenna_reduction);
+       tpc_stats->power_limit = __le32_to_cpu(ev->power_limit);
+       tpc_stats->twice_max_rd_power = __le32_to_cpu(ev->twice_max_rd_power);
+       tpc_stats->num_tx_chain = __le32_to_cpu(ev->num_tx_chain);
+       tpc_stats->rate_max = __le32_to_cpu(ev->rate_max);
+
+       ath10k_tpc_config_disp_tables(ar, ev, tpc_stats,
+                                     rate_code, pream_table,
+                                     WMI_TPC_TABLE_TYPE_CDD);
+       ath10k_tpc_config_disp_tables(ar, ev,  tpc_stats,
+                                     rate_code, pream_table,
+                                     WMI_TPC_TABLE_TYPE_STBC);
+       ath10k_tpc_config_disp_tables(ar, ev, tpc_stats,
+                                     rate_code, pream_table,
+                                     WMI_TPC_TABLE_TYPE_TXBF);
+
+       ath10k_debug_tpc_stats_process(ar, tpc_stats);
+
+       ath10k_dbg(ar, ATH10K_DBG_WMI,
+                  "wmi event tpc config channel %d mode %d ctl %d regd %d gain %d %d limit %d max_power %d tx_chanins %d rates %d\n",
+                  __le32_to_cpu(ev->chan_freq),
+                  __le32_to_cpu(ev->phy_mode),
+                  __le32_to_cpu(ev->ctl),
+                  __le32_to_cpu(ev->reg_domain),
+                  a_sle32_to_cpu(ev->twice_antenna_gain),
+                  __le32_to_cpu(ev->twice_antenna_reduction),
+                  __le32_to_cpu(ev->power_limit),
+                  __le32_to_cpu(ev->twice_max_rd_power) / 2,
+                  __le32_to_cpu(ev->num_tx_chain),
+                  __le32_to_cpu(ev->rate_max));
 }
 
 void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb)
@@ -3917,6 +4289,53 @@ static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
        return 0;
 }
 
+static bool
+ath10k_wmi_is_host_mem_allocated(struct ath10k *ar,
+                                const struct wlan_host_mem_req **mem_reqs,
+                                u32 num_mem_reqs)
+{
+       u32 req_id, num_units, unit_size, num_unit_info;
+       u32 pool_size;
+       int i, j;
+       bool found;
+
+       if (ar->wmi.num_mem_chunks != num_mem_reqs)
+               return false;
+
+       for (i = 0; i < num_mem_reqs; ++i) {
+               req_id = __le32_to_cpu(mem_reqs[i]->req_id);
+               num_units = __le32_to_cpu(mem_reqs[i]->num_units);
+               unit_size = __le32_to_cpu(mem_reqs[i]->unit_size);
+               num_unit_info = __le32_to_cpu(mem_reqs[i]->num_unit_info);
+
+               if (num_unit_info & NUM_UNITS_IS_NUM_ACTIVE_PEERS) {
+                       if (ar->num_active_peers)
+                               num_units = ar->num_active_peers + 1;
+                       else
+                               num_units = ar->max_num_peers + 1;
+               } else if (num_unit_info & NUM_UNITS_IS_NUM_PEERS) {
+                       num_units = ar->max_num_peers + 1;
+               } else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS) {
+                       num_units = ar->max_num_vdevs + 1;
+               }
+
+               found = false;
+               for (j = 0; j < ar->wmi.num_mem_chunks; j++) {
+                       if (ar->wmi.mem_chunks[j].req_id == req_id) {
+                               pool_size = num_units * round_up(unit_size, 4);
+                               if (ar->wmi.mem_chunks[j].len == pool_size) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+               }
+               if (!found)
+                       return false;
+       }
+
+       return true;
+}
+
 static int
 ath10k_wmi_main_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb,
                                   struct wmi_svc_rdy_ev_arg *arg)
@@ -3997,6 +4416,7 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work)
        struct wmi_svc_rdy_ev_arg arg = {};
        u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
        int ret;
+       bool allocated;
 
        if (!skb) {
                ath10k_warn(ar, "invalid service ready event skb\n");
@@ -4040,8 +4460,10 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work)
                ar->num_rf_chains = ar->max_spatial_stream;
        }
 
-       ar->supp_tx_chainmask = (1 << ar->num_rf_chains) - 1;
-       ar->supp_rx_chainmask = (1 << ar->num_rf_chains) - 1;
+       if (!ar->cfg_tx_chainmask) {
+               ar->cfg_tx_chainmask = (1 << ar->num_rf_chains) - 1;
+               ar->cfg_rx_chainmask = (1 << ar->num_rf_chains) - 1;
+       }
 
        if (strlen(ar->hw->wiphy->fw_version) == 0) {
                snprintf(ar->hw->wiphy->fw_version,
@@ -4073,6 +4495,18 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work)
         * and WMI_SERVICE_IRAM_TIDS, etc.
         */
 
+       allocated = ath10k_wmi_is_host_mem_allocated(ar, arg.mem_reqs,
+                                                    num_mem_reqs);
+       if (allocated)
+               goto skip_mem_alloc;
+
+       /* Either this event is received during boot time or there is a change
+        * in memory requirement from firmware when compared to last request.
+        * Free any old memory and do a fresh allocation based on the current
+        * memory requirement.
+        */
+       ath10k_wmi_free_host_mem(ar);
+
        for (i = 0; i < num_mem_reqs; ++i) {
                req_id = __le32_to_cpu(arg.mem_reqs[i]->req_id);
                num_units = __le32_to_cpu(arg.mem_reqs[i]->num_units);
@@ -4108,6 +4542,7 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work)
                        return;
        }
 
+skip_mem_alloc:
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x fw_build 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n",
                   __le32_to_cpu(arg.min_tx_power),
@@ -4623,6 +5058,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb)
                ath10k_dbg(ar, ATH10K_DBG_WMI,
                           "received event id %d not implemented\n", id);
                break;
+       case WMI_10_4_UPDATE_STATS_EVENTID:
+               ath10k_wmi_event_update_stats(ar, skb);
+               break;
        default:
                ath10k_warn(ar, "Unknown eventid: %d\n", id);
                break;
@@ -5029,7 +5467,7 @@ static struct sk_buff *ath10k_wmi_10_4_op_gen_init(struct ath10k *ar)
        config.rx_timeout_pri[2] = __cpu_to_le32(TARGET_10_4_RX_TIMEOUT_LO_PRI);
        config.rx_timeout_pri[3] = __cpu_to_le32(TARGET_10_4_RX_TIMEOUT_HI_PRI);
 
-       config.rx_decap_mode        = __cpu_to_le32(TARGET_10_4_RX_DECAP_MODE);
+       config.rx_decap_mode        = __cpu_to_le32(ar->wmi.rx_decap_mode);
        config.scan_max_pending_req = __cpu_to_le32(TARGET_10_4_SCAN_MAX_REQS);
        config.bmiss_offload_max_vdev =
                        __cpu_to_le32(TARGET_10_4_BMISS_OFFLOAD_MAX_VDEV);
@@ -6295,6 +6733,505 @@ ath10k_wmi_op_gen_delba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
        return skb;
 }
 
+static struct sk_buff *
+ath10k_wmi_10_2_4_op_gen_pdev_get_tpc_config(struct ath10k *ar, u32 param)
+{
+       struct wmi_pdev_get_tpc_config_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
+
+       cmd = (struct wmi_pdev_get_tpc_config_cmd *)skb->data;
+       cmd->param = __cpu_to_le32(param);
+
+       ath10k_dbg(ar, ATH10K_DBG_WMI,
+                  "wmi pdev get tcp config param:%d\n", param);
+       return skb;
+}
+
+size_t ath10k_wmi_fw_stats_num_peers(struct list_head *head)
+{
+       struct ath10k_fw_stats_peer *i;
+       size_t num = 0;
+
+       list_for_each_entry(i, head, list)
+               ++num;
+
+       return num;
+}
+
+size_t ath10k_wmi_fw_stats_num_vdevs(struct list_head *head)
+{
+       struct ath10k_fw_stats_vdev *i;
+       size_t num = 0;
+
+       list_for_each_entry(i, head, list)
+               ++num;
+
+       return num;
+}
+
+static void
+ath10k_wmi_fw_pdev_base_stats_fill(const struct ath10k_fw_stats_pdev *pdev,
+                                  char *buf, u32 *length)
+{
+       u32 len = *length;
+       u32 buf_len = ATH10K_FW_STATS_BUF_SIZE;
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                       "ath10k PDEV stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                       "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "Channel noise floor", pdev->ch_noise_floor);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                       "Channel TX power", pdev->chan_tx_power);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                       "TX frame count", pdev->tx_frame_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                       "RX frame count", pdev->rx_frame_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                       "RX clear count", pdev->rx_clear_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                       "Cycle count", pdev->cycle_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                       "PHY error count", pdev->phy_err_count);
+
+       *length = len;
+}
+
+static void
+ath10k_wmi_fw_pdev_extra_stats_fill(const struct ath10k_fw_stats_pdev *pdev,
+                                   char *buf, u32 *length)
+{
+       u32 len = *length;
+       u32 buf_len = ATH10K_FW_STATS_BUF_SIZE;
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                       "RTS bad count", pdev->rts_bad);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                       "RTS good count", pdev->rts_good);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                       "FCS bad count", pdev->fcs_bad);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                       "No beacon count", pdev->no_beacons);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                       "MIB int count", pdev->mib_int_count);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       *length = len;
+}
+
+static void
+ath10k_wmi_fw_pdev_tx_stats_fill(const struct ath10k_fw_stats_pdev *pdev,
+                                char *buf, u32 *length)
+{
+       u32 len = *length;
+       u32 buf_len = ATH10K_FW_STATS_BUF_SIZE;
+
+       len += scnprintf(buf + len, buf_len - len, "\n%30s\n",
+                        "ath10k PDEV TX stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HTT cookies queued", pdev->comp_queued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HTT cookies disp.", pdev->comp_delivered);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDU queued", pdev->msdu_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU queued", pdev->mpdu_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs dropped", pdev->wmm_drop);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Local enqued", pdev->local_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Local freed", pdev->local_freed);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HW queued", pdev->hw_queued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PPDUs reaped", pdev->hw_reaped);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Num underruns", pdev->underrun);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PPDUs cleaned", pdev->tx_abort);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs requed", pdev->mpdus_requed);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Excessive retries", pdev->tx_ko);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HW rate", pdev->data_rc);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Sched self tiggers", pdev->self_triggers);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Dropped due to SW retries",
+                        pdev->sw_retry_failure);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Illegal rate phy errors",
+                        pdev->illgl_rate_phy_err);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Pdev continuous xretry", pdev->pdev_cont_xretry);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "TX timeout", pdev->pdev_tx_timeout);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PDEV resets", pdev->pdev_resets);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY underrun", pdev->phy_underrun);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU is more than txop limit", pdev->txop_ovf);
+       *length = len;
+}
+
+static void
+ath10k_wmi_fw_pdev_rx_stats_fill(const struct ath10k_fw_stats_pdev *pdev,
+                                char *buf, u32 *length)
+{
+       u32 len = *length;
+       u32 buf_len = ATH10K_FW_STATS_BUF_SIZE;
+
+       len += scnprintf(buf + len, buf_len - len, "\n%30s\n",
+                        "ath10k PDEV RX stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Mid PPDU route change",
+                        pdev->mid_ppdu_route_change);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Tot. number of statuses", pdev->status_rcvd);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 0", pdev->r0_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 1", pdev->r1_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 2", pdev->r2_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 3", pdev->r3_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs delivered to HTT", pdev->htt_msdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs delivered to HTT", pdev->htt_mpdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs delivered to stack", pdev->loc_msdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs delivered to stack", pdev->loc_mpdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Oversized AMSUs", pdev->oversize_amsdu);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY errors", pdev->phy_errs);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY errors drops", pdev->phy_err_drop);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs);
+       *length = len;
+}
+
+static void
+ath10k_wmi_fw_vdev_stats_fill(const struct ath10k_fw_stats_vdev *vdev,
+                             char *buf, u32 *length)
+{
+       u32 len = *length;
+       u32 buf_len = ATH10K_FW_STATS_BUF_SIZE;
+       int i;
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "vdev id", vdev->vdev_id);
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "beacon snr", vdev->beacon_snr);
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "data snr", vdev->data_snr);
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "num rx frames", vdev->num_rx_frames);
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "num rts fail", vdev->num_rts_fail);
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "num rts success", vdev->num_rts_success);
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "num rx err", vdev->num_rx_err);
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "num rx discard", vdev->num_rx_discard);
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "num tx not acked", vdev->num_tx_not_acked);
+
+       for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames); i++)
+               len += scnprintf(buf + len, buf_len - len,
+                               "%25s [%02d] %u\n",
+                               "num tx frames", i,
+                               vdev->num_tx_frames[i]);
+
+       for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_retries); i++)
+               len += scnprintf(buf + len, buf_len - len,
+                               "%25s [%02d] %u\n",
+                               "num tx frames retries", i,
+                               vdev->num_tx_frames_retries[i]);
+
+       for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_failures); i++)
+               len += scnprintf(buf + len, buf_len - len,
+                               "%25s [%02d] %u\n",
+                               "num tx frames failures", i,
+                               vdev->num_tx_frames_failures[i]);
+
+       for (i = 0 ; i < ARRAY_SIZE(vdev->tx_rate_history); i++)
+               len += scnprintf(buf + len, buf_len - len,
+                               "%25s [%02d] 0x%08x\n",
+                               "tx rate history", i,
+                               vdev->tx_rate_history[i]);
+
+       for (i = 0 ; i < ARRAY_SIZE(vdev->beacon_rssi_history); i++)
+               len += scnprintf(buf + len, buf_len - len,
+                               "%25s [%02d] %u\n",
+                               "beacon rssi history", i,
+                               vdev->beacon_rssi_history[i]);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       *length = len;
+}
+
+static void
+ath10k_wmi_fw_peer_stats_fill(const struct ath10k_fw_stats_peer *peer,
+                             char *buf, u32 *length)
+{
+       u32 len = *length;
+       u32 buf_len = ATH10K_FW_STATS_BUF_SIZE;
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
+                       "Peer MAC address", peer->peer_macaddr);
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "Peer RSSI", peer->peer_rssi);
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "Peer TX rate", peer->peer_tx_rate);
+       len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                       "Peer RX rate", peer->peer_rx_rate);
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       *length = len;
+}
+
+void ath10k_wmi_main_op_fw_stats_fill(struct ath10k *ar,
+                                     struct ath10k_fw_stats *fw_stats,
+                                     char *buf)
+{
+       u32 len = 0;
+       u32 buf_len = ATH10K_FW_STATS_BUF_SIZE;
+       const struct ath10k_fw_stats_pdev *pdev;
+       const struct ath10k_fw_stats_vdev *vdev;
+       const struct ath10k_fw_stats_peer *peer;
+       size_t num_peers;
+       size_t num_vdevs;
+
+       spin_lock_bh(&ar->data_lock);
+
+       pdev = list_first_entry_or_null(&fw_stats->pdevs,
+                                       struct ath10k_fw_stats_pdev, list);
+       if (!pdev) {
+               ath10k_warn(ar, "failed to get pdev stats\n");
+               goto unlock;
+       }
+
+       num_peers = ath10k_wmi_fw_stats_num_peers(&fw_stats->peers);
+       num_vdevs = ath10k_wmi_fw_stats_num_vdevs(&fw_stats->vdevs);
+
+       ath10k_wmi_fw_pdev_base_stats_fill(pdev, buf, &len);
+       ath10k_wmi_fw_pdev_tx_stats_fill(pdev, buf, &len);
+       ath10k_wmi_fw_pdev_rx_stats_fill(pdev, buf, &len);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+                        "ath10k VDEV stats", num_vdevs);
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       list_for_each_entry(vdev, &fw_stats->vdevs, list) {
+               ath10k_wmi_fw_vdev_stats_fill(vdev, buf, &len);
+       }
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+                        "ath10k PEER stats", num_peers);
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       list_for_each_entry(peer, &fw_stats->peers, list) {
+               ath10k_wmi_fw_peer_stats_fill(peer, buf, &len);
+       }
+
+unlock:
+       spin_unlock_bh(&ar->data_lock);
+
+       if (len >= buf_len)
+               buf[len - 1] = 0;
+       else
+               buf[len] = 0;
+}
+
+void ath10k_wmi_10x_op_fw_stats_fill(struct ath10k *ar,
+                                    struct ath10k_fw_stats *fw_stats,
+                                    char *buf)
+{
+       unsigned int len = 0;
+       unsigned int buf_len = ATH10K_FW_STATS_BUF_SIZE;
+       const struct ath10k_fw_stats_pdev *pdev;
+       const struct ath10k_fw_stats_vdev *vdev;
+       const struct ath10k_fw_stats_peer *peer;
+       size_t num_peers;
+       size_t num_vdevs;
+
+       spin_lock_bh(&ar->data_lock);
+
+       pdev = list_first_entry_or_null(&fw_stats->pdevs,
+                                       struct ath10k_fw_stats_pdev, list);
+       if (!pdev) {
+               ath10k_warn(ar, "failed to get pdev stats\n");
+               goto unlock;
+       }
+
+       num_peers = ath10k_wmi_fw_stats_num_peers(&fw_stats->peers);
+       num_vdevs = ath10k_wmi_fw_stats_num_vdevs(&fw_stats->vdevs);
+
+       ath10k_wmi_fw_pdev_base_stats_fill(pdev, buf, &len);
+       ath10k_wmi_fw_pdev_extra_stats_fill(pdev, buf, &len);
+       ath10k_wmi_fw_pdev_tx_stats_fill(pdev, buf, &len);
+       ath10k_wmi_fw_pdev_rx_stats_fill(pdev, buf, &len);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+                        "ath10k VDEV stats", num_vdevs);
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       list_for_each_entry(vdev, &fw_stats->vdevs, list) {
+               ath10k_wmi_fw_vdev_stats_fill(vdev, buf, &len);
+       }
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+                        "ath10k PEER stats", num_peers);
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       list_for_each_entry(peer, &fw_stats->peers, list) {
+               ath10k_wmi_fw_peer_stats_fill(peer, buf, &len);
+       }
+
+unlock:
+       spin_unlock_bh(&ar->data_lock);
+
+       if (len >= buf_len)
+               buf[len - 1] = 0;
+       else
+               buf[len] = 0;
+}
+
+static struct sk_buff *
+ath10k_wmi_op_gen_pdev_enable_adaptive_cca(struct ath10k *ar, u8 enable,
+                                          u32 detect_level, u32 detect_margin)
+{
+       struct wmi_pdev_set_adaptive_cca_params *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
+
+       cmd = (struct wmi_pdev_set_adaptive_cca_params *)skb->data;
+       cmd->enable = __cpu_to_le32(enable);
+       cmd->cca_detect_level = __cpu_to_le32(detect_level);
+       cmd->cca_detect_margin = __cpu_to_le32(detect_margin);
+
+       ath10k_dbg(ar, ATH10K_DBG_WMI,
+                  "wmi pdev set adaptive cca params enable:%d detection level:%d detection margin:%d\n",
+                  enable, detect_level, detect_margin);
+       return skb;
+}
+
+void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar,
+                                     struct ath10k_fw_stats *fw_stats,
+                                     char *buf)
+{
+       u32 len = 0;
+       u32 buf_len = ATH10K_FW_STATS_BUF_SIZE;
+       const struct ath10k_fw_stats_pdev *pdev;
+       const struct ath10k_fw_stats_vdev *vdev;
+       const struct ath10k_fw_stats_peer *peer;
+       size_t num_peers;
+       size_t num_vdevs;
+
+       spin_lock_bh(&ar->data_lock);
+
+       pdev = list_first_entry_or_null(&fw_stats->pdevs,
+                                       struct ath10k_fw_stats_pdev, list);
+       if (!pdev) {
+               ath10k_warn(ar, "failed to get pdev stats\n");
+               goto unlock;
+       }
+
+       num_peers = ath10k_wmi_fw_stats_num_peers(&fw_stats->peers);
+       num_vdevs = ath10k_wmi_fw_stats_num_vdevs(&fw_stats->vdevs);
+
+       ath10k_wmi_fw_pdev_base_stats_fill(pdev, buf, &len);
+       ath10k_wmi_fw_pdev_extra_stats_fill(pdev, buf, &len);
+       ath10k_wmi_fw_pdev_tx_stats_fill(pdev, buf, &len);
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "HW paused", pdev->hw_paused);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "Seqs posted", pdev->seq_posted);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "Seqs failed queueing", pdev->seq_failed_queueing);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "Seqs completed", pdev->seq_completed);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "Seqs restarted", pdev->seq_restarted);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "MU Seqs posted", pdev->mu_seq_posted);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "MPDUs SW flushed", pdev->mpdus_sw_flush);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "MPDUs HW filtered", pdev->mpdus_hw_filter);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "MPDUs truncated", pdev->mpdus_truncated);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "MPDUs receive no ACK", pdev->mpdus_ack_failed);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "MPDUs expired", pdev->mpdus_expired);
+
+       ath10k_wmi_fw_pdev_rx_stats_fill(pdev, buf, &len);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                       "Num Rx Overflow errors", pdev->rx_ovfl_errs);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+                       "ath10k VDEV stats", num_vdevs);
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                               "=================");
+
+       list_for_each_entry(vdev, &fw_stats->vdevs, list) {
+               ath10k_wmi_fw_vdev_stats_fill(vdev, buf, &len);
+       }
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+                       "ath10k PEER stats", num_peers);
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                               "=================");
+
+       list_for_each_entry(peer, &fw_stats->peers, list) {
+               ath10k_wmi_fw_peer_stats_fill(peer, buf, &len);
+       }
+
+unlock:
+       spin_unlock_bh(&ar->data_lock);
+
+       if (len >= buf_len)
+               buf[len - 1] = 0;
+       else
+               buf[len] = 0;
+}
+
 static const struct wmi_ops wmi_ops = {
        .rx = ath10k_wmi_op_rx,
        .map_svc = wmi_main_svc_map,
@@ -6353,10 +7290,12 @@ static const struct wmi_ops wmi_ops = {
        .gen_addba_send = ath10k_wmi_op_gen_addba_send,
        .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
        .gen_delba_send = ath10k_wmi_op_gen_delba_send,
+       .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill,
        /* .gen_bcn_tmpl not implemented */
        /* .gen_prb_tmpl not implemented */
        /* .gen_p2p_go_bcn_ie not implemented */
        /* .gen_adaptive_qcs not implemented */
+       /* .gen_pdev_enable_adaptive_cca not implemented */
 };
 
 static const struct wmi_ops wmi_10_1_ops = {
@@ -6418,10 +7357,12 @@ static const struct wmi_ops wmi_10_1_ops = {
        .gen_addba_send = ath10k_wmi_op_gen_addba_send,
        .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
        .gen_delba_send = ath10k_wmi_op_gen_delba_send,
+       .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill,
        /* .gen_bcn_tmpl not implemented */
        /* .gen_prb_tmpl not implemented */
        /* .gen_p2p_go_bcn_ie not implemented */
        /* .gen_adaptive_qcs not implemented */
+       /* .gen_pdev_enable_adaptive_cca not implemented */
 };
 
 static const struct wmi_ops wmi_10_2_ops = {
@@ -6484,6 +7425,8 @@ static const struct wmi_ops wmi_10_2_ops = {
        .gen_addba_send = ath10k_wmi_op_gen_addba_send,
        .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
        .gen_delba_send = ath10k_wmi_op_gen_delba_send,
+       .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill,
+       /* .gen_pdev_enable_adaptive_cca not implemented */
 };
 
 static const struct wmi_ops wmi_10_2_4_ops = {
@@ -6545,6 +7488,10 @@ static const struct wmi_ops wmi_10_2_4_ops = {
        .gen_addba_send = ath10k_wmi_op_gen_addba_send,
        .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
        .gen_delba_send = ath10k_wmi_op_gen_delba_send,
+       .gen_pdev_get_tpc_config = ath10k_wmi_10_2_4_op_gen_pdev_get_tpc_config,
+       .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill,
+       .gen_pdev_enable_adaptive_cca =
+               ath10k_wmi_op_gen_pdev_enable_adaptive_cca,
        /* .gen_bcn_tmpl not implemented */
        /* .gen_prb_tmpl not implemented */
        /* .gen_p2p_go_bcn_ie not implemented */
@@ -6555,6 +7502,7 @@ static const struct wmi_ops wmi_10_4_ops = {
        .rx = ath10k_wmi_10_4_op_rx,
        .map_svc = wmi_10_4_svc_map,
 
+       .pull_fw_stats = ath10k_wmi_10_4_op_pull_fw_stats,
        .pull_scan = ath10k_wmi_op_pull_scan_ev,
        .pull_mgmt_rx = ath10k_wmi_10_4_op_pull_mgmt_rx_ev,
        .pull_ch_info = ath10k_wmi_10_4_op_pull_ch_info_ev,
@@ -6604,9 +7552,11 @@ static const struct wmi_ops wmi_10_4_ops = {
        .gen_addba_send = ath10k_wmi_op_gen_addba_send,
        .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
        .gen_delba_send = ath10k_wmi_op_gen_delba_send,
+       .fw_stats_fill = ath10k_wmi_10_4_op_fw_stats_fill,
 
        /* shared with 10.2 */
        .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc,
+       .gen_request_stats = ath10k_wmi_op_gen_request_stats,
 };
 
 int ath10k_wmi_attach(struct ath10k *ar)
@@ -6660,15 +7610,10 @@ int ath10k_wmi_attach(struct ath10k *ar)
        return 0;
 }
 
-void ath10k_wmi_detach(struct ath10k *ar)
+void ath10k_wmi_free_host_mem(struct ath10k *ar)
 {
        int i;
 
-       cancel_work_sync(&ar->svc_rdy_work);
-
-       if (ar->svc_rdy_skb)
-               dev_kfree_skb(ar->svc_rdy_skb);
-
        /* free the host memory chunks requested by firmware */
        for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
                dma_free_coherent(ar->dev,
@@ -6679,3 +7624,11 @@ void ath10k_wmi_detach(struct ath10k *ar)
 
        ar->wmi.num_mem_chunks = 0;
 }
+
+void ath10k_wmi_detach(struct ath10k *ar)
+{
+       cancel_work_sync(&ar->svc_rdy_work);
+
+       if (ar->svc_rdy_skb)
+               dev_kfree_skb(ar->svc_rdy_skb);
+}
index 52d35032d53e28a8d04716195057c40c8e19f07d..72a4ef7095776b2d651ba594bd6411d86a860f3d 100644 (file)
@@ -73,6 +73,25 @@ struct wmi_cmd_hdr {
 #define HTC_PROTOCOL_VERSION    0x0002
 #define WMI_PROTOCOL_VERSION    0x0002
 
+/*
+ * There is no signed version of __le32, so for a temporary solution come
+ * up with our own version. The idea is from fs/ntfs/types.h.
+ *
+ * Use a_ prefix so that it doesn't conflict if we get proper support to
+ * linux/types.h.
+ */
+typedef __s32 __bitwise a_sle32;
+
+static inline a_sle32 a_cpu_to_sle32(s32 val)
+{
+       return (__force a_sle32)cpu_to_le32(val);
+}
+
+static inline s32 a_sle32_to_cpu(a_sle32 val)
+{
+       return le32_to_cpu((__force __le32)val);
+}
+
 enum wmi_service {
        WMI_SERVICE_BEACON_OFFLOAD = 0,
        WMI_SERVICE_SCAN_OFFLOAD,
@@ -753,6 +772,7 @@ struct wmi_cmd_map {
        u32 mu_cal_start_cmdid;
        u32 set_cca_params_cmdid;
        u32 pdev_bss_chan_info_request_cmdid;
+       u32 pdev_enable_adaptive_cca_cmdid;
 };
 
 /*
@@ -1362,6 +1382,9 @@ enum wmi_10_2_cmd_id {
        WMI_10_2_VDEV_ATF_REQUEST_CMDID,
        WMI_10_2_PEER_ATF_REQUEST_CMDID,
        WMI_10_2_PDEV_GET_TEMPERATURE_CMDID,
+       WMI_10_2_MU_CAL_START_CMDID,
+       WMI_10_2_SET_LTEU_CONFIG_CMDID,
+       WMI_10_2_SET_CCA_PARAMS,
        WMI_10_2_PDEV_UTF_CMDID = WMI_10_2_END_CMDID - 1,
 };
 
@@ -3642,8 +3665,18 @@ struct wmi_pdev_get_tpc_config_cmd {
        __le32 param;
 } __packed;
 
+#define WMI_TPC_CONFIG_PARAM           1
 #define WMI_TPC_RATE_MAX               160
 #define WMI_TPC_TX_N_CHAIN             4
+#define WMI_TPC_PREAM_TABLE_MAX                10
+#define WMI_TPC_FLAG                   3
+#define WMI_TPC_BUF_SIZE               10
+
+enum wmi_tpc_table_type {
+       WMI_TPC_TABLE_TYPE_CDD = 0,
+       WMI_TPC_TABLE_TYPE_STBC = 1,
+       WMI_TPC_TABLE_TYPE_TXBF = 2,
+};
 
 enum wmi_tpc_config_event_flag {
        WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD     = 0x1,
@@ -3657,7 +3690,7 @@ struct wmi_pdev_tpc_config_event {
        __le32 phy_mode;
        __le32 twice_antenna_reduction;
        __le32 twice_max_rd_power;
-       s32 twice_antenna_gain;
+       a_sle32 twice_antenna_gain;
        __le32 power_limit;
        __le32 rate_max;
        __le32 num_tx_chain;
@@ -3833,6 +3866,111 @@ struct wmi_pdev_stats_tx {
        __le32 txop_ovf;
 } __packed;
 
+struct wmi_10_4_pdev_stats_tx {
+       /* Num HTT cookies queued to dispatch list */
+       __le32 comp_queued;
+
+       /* Num HTT cookies dispatched */
+       __le32 comp_delivered;
+
+       /* Num MSDU queued to WAL */
+       __le32 msdu_enqued;
+
+       /* Num MPDU queue to WAL */
+       __le32 mpdu_enqued;
+
+       /* Num MSDUs dropped by WMM limit */
+       __le32 wmm_drop;
+
+       /* Num Local frames queued */
+       __le32 local_enqued;
+
+       /* Num Local frames done */
+       __le32 local_freed;
+
+       /* Num queued to HW */
+       __le32 hw_queued;
+
+       /* Num PPDU reaped from HW */
+       __le32 hw_reaped;
+
+       /* Num underruns */
+       __le32 underrun;
+
+       /* HW Paused. */
+       __le32  hw_paused;
+
+       /* Num PPDUs cleaned up in TX abort */
+       __le32 tx_abort;
+
+       /* Num MPDUs requed by SW */
+       __le32 mpdus_requed;
+
+       /* excessive retries */
+       __le32 tx_ko;
+
+       /* data hw rate code */
+       __le32 data_rc;
+
+       /* Scheduler self triggers */
+       __le32 self_triggers;
+
+       /* frames dropped due to excessive sw retries */
+       __le32 sw_retry_failure;
+
+       /* illegal rate phy errors  */
+       __le32 illgl_rate_phy_err;
+
+       /* wal pdev continuous xretry */
+       __le32 pdev_cont_xretry;
+
+       /* wal pdev tx timeouts */
+       __le32 pdev_tx_timeout;
+
+       /* wal pdev resets  */
+       __le32 pdev_resets;
+
+       /* frames dropped due to non-availability of stateless TIDs */
+       __le32 stateless_tid_alloc_failure;
+
+       __le32 phy_underrun;
+
+       /* MPDU is more than txop limit */
+       __le32 txop_ovf;
+
+       /* Number of Sequences posted */
+       __le32 seq_posted;
+
+       /* Number of Sequences failed queueing */
+       __le32 seq_failed_queueing;
+
+       /* Number of Sequences completed */
+       __le32 seq_completed;
+
+       /* Number of Sequences restarted */
+       __le32 seq_restarted;
+
+       /* Number of MU Sequences posted */
+       __le32 mu_seq_posted;
+
+       /* Num MPDUs flushed by SW, HWPAUSED,SW TXABORT(Reset,channel change) */
+       __le32 mpdus_sw_flush;
+
+       /* Num MPDUs filtered by HW, all filter condition (TTL expired) */
+       __le32 mpdus_hw_filter;
+
+       /* Num MPDUs truncated by PDG
+        * (TXOP, TBTT, PPDU_duration based on rate, dyn_bw)
+        */
+       __le32 mpdus_truncated;
+
+       /* Num MPDUs that was tried but didn't receive ACK or BA */
+       __le32 mpdus_ack_failed;
+
+       /* Num MPDUs that was dropped due to expiry. */
+       __le32 mpdus_expired;
+} __packed;
+
 struct wmi_pdev_stats_rx {
        /* Cnts any change in ring routing mid-ppdu */
        __le32 mid_ppdu_route_change;
@@ -4006,6 +4144,16 @@ struct wmi_10_2_pdev_stats {
        struct wmi_pdev_stats_extra extra;
 } __packed;
 
+struct wmi_10_4_pdev_stats {
+       struct wmi_pdev_stats_base base;
+       struct wmi_10_4_pdev_stats_tx tx;
+       struct wmi_pdev_stats_rx rx;
+       __le32 rx_ovfl_errs;
+       struct wmi_pdev_stats_mem mem;
+       __le32 sram_free_size;
+       struct wmi_pdev_stats_extra extra;
+} __packed;
+
 /*
  * VDEV statistics
  * TODO: add all VDEV stats here
@@ -4047,6 +4195,23 @@ struct wmi_10_2_4_peer_stats {
        __le32 unknown_value; /* FIXME: what is this word? */
 } __packed;
 
+struct wmi_10_4_peer_stats {
+       struct wmi_mac_addr peer_macaddr;
+       __le32 peer_rssi;
+       __le32 peer_rssi_seq_num;
+       __le32 peer_tx_rate;
+       __le32 peer_rx_rate;
+       __le32 current_per;
+       __le32 retries;
+       __le32 tx_rate_count;
+       __le32 max_4ms_frame_len;
+       __le32 total_sub_frames;
+       __le32 tx_bytes;
+       __le32 num_pkt_loss_overflow[4];
+       __le32 num_pkt_loss_excess_retry[4];
+       __le32 peer_rssi_changed;
+} __packed;
+
 struct wmi_10_2_pdev_ext_stats {
        __le32 rx_rssi_comb;
        __le32 rx_rssi[4];
@@ -4253,6 +4418,11 @@ enum wmi_rate_preamble {
        WMI_RATE_PREAMBLE_VHT,
 };
 
+#define ATH10K_HW_NSS(rate)            (1 + (((rate) >> 4) & 0x3))
+#define ATH10K_HW_PREAMBLE(rate)       (((rate) >> 6) & 0x3)
+#define ATH10K_HW_RATECODE(rate, nss, preamble)        \
+       (((preamble) << 6) | ((nss) << 4) | (rate))
+
 /* Value to disable fixed rate setting */
 #define WMI_FIXED_RATE_NONE    (0xff)
 
@@ -6060,13 +6230,24 @@ enum wmi_txbf_conf {
        WMI_TXBF_CONF_AFTER_ASSOC,
 };
 
+#define        WMI_CCA_DETECT_LEVEL_AUTO       0
+#define        WMI_CCA_DETECT_MARGIN_AUTO      0
+
+struct wmi_pdev_set_adaptive_cca_params {
+       __le32 enable;
+       __le32 cca_detect_level;
+       __le32 cca_detect_margin;
+} __packed;
+
 struct ath10k;
 struct ath10k_vif;
 struct ath10k_fw_stats_pdev;
 struct ath10k_fw_stats_peer;
+struct ath10k_fw_stats;
 
 int ath10k_wmi_attach(struct ath10k *ar);
 void ath10k_wmi_detach(struct ath10k *ar);
+void ath10k_wmi_free_host_mem(struct ath10k *ar);
 int ath10k_wmi_wait_for_service_ready(struct ath10k *ar);
 int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar);
 
@@ -6144,4 +6325,16 @@ void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb);
 int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb);
 int ath10k_wmi_op_pull_phyerr_ev(struct ath10k *ar, const void *phyerr_buf,
                                 int left_len, struct wmi_phyerr_ev_arg *arg);
+void ath10k_wmi_main_op_fw_stats_fill(struct ath10k *ar,
+                                     struct ath10k_fw_stats *fw_stats,
+                                     char *buf);
+void ath10k_wmi_10x_op_fw_stats_fill(struct ath10k *ar,
+                                    struct ath10k_fw_stats *fw_stats,
+                                    char *buf);
+size_t ath10k_wmi_fw_stats_num_peers(struct list_head *head);
+size_t ath10k_wmi_fw_stats_num_vdevs(struct list_head *head);
+void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar,
+                                     struct ath10k_fw_stats *fw_stats,
+                                     char *buf);
+
 #endif /* _WMI_H_ */
index a511ef3614b9ed098558d1d86a9fec88b2330235..81ac8c59f0ecd2c458350e579f141ba1819033d5 100644 (file)
@@ -2217,7 +2217,7 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
 
        /* enter / leave wow suspend on first vif always */
        first_vif = ath6kl_vif_first(ar);
-       if (WARN_ON(unlikely(!first_vif)) ||
+       if (WARN_ON(!first_vif) ||
            !ath6kl_cfg80211_ready(first_vif))
                return -EIO;
 
@@ -2297,7 +2297,7 @@ static int ath6kl_wow_resume(struct ath6kl *ar)
        int ret;
 
        vif = ath6kl_vif_first(ar);
-       if (WARN_ON(unlikely(!vif)) ||
+       if (WARN_ON(!vif) ||
            !ath6kl_cfg80211_ready(vif))
                return -EIO;
 
@@ -3231,6 +3231,15 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                                        wait, buf, len, no_cck);
 }
 
+static int ath6kl_get_antenna(struct wiphy *wiphy,
+                             u32 *tx_ant, u32 *rx_ant)
+{
+       struct ath6kl *ar = wiphy_priv(wiphy);
+       *tx_ant = ar->hw.tx_ant;
+       *rx_ant = ar->hw.rx_ant;
+       return 0;
+}
+
 static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
                                       struct wireless_dev *wdev,
                                       u16 frame_type, bool reg)
@@ -3312,7 +3321,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
        }
 
        /* fw uses seconds, also make sure that it's >0 */
-       interval = max_t(u16, 1, request->interval / 1000);
+       interval = max_t(u16, 1, request->scan_plans[0].interval);
 
        ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
                                  interval, interval,
@@ -3447,6 +3456,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
        .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
        .mgmt_tx = ath6kl_mgmt_tx,
        .mgmt_frame_register = ath6kl_mgmt_frame_register,
+       .get_antenna = ath6kl_get_antenna,
        .sched_scan_start = ath6kl_cfg80211_sscan_start,
        .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
        .set_bitrate_mask = ath6kl_cfg80211_set_bitrate,
@@ -3634,6 +3644,127 @@ void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
        ar->num_vif--;
 }
 
+static const char ath6kl_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
+       /* Common stats names used by many drivers. */
+       "tx_pkts_nic", "tx_bytes_nic", "rx_pkts_nic", "rx_bytes_nic",
+
+       /* TX stats. */
+       "d_tx_ucast_pkts", "d_tx_bcast_pkts",
+       "d_tx_ucast_bytes", "d_tx_bcast_bytes",
+       "d_tx_rts_ok", "d_tx_error", "d_tx_fail",
+       "d_tx_retry", "d_tx_multi_retry", "d_tx_rts_fail",
+       "d_tx_tkip_counter_measures",
+
+       /* RX Stats. */
+       "d_rx_ucast_pkts", "d_rx_ucast_rate", "d_rx_bcast_pkts",
+       "d_rx_ucast_bytes", "d_rx_bcast_bytes", "d_rx_frag_pkt",
+       "d_rx_error", "d_rx_crc_err", "d_rx_keycache_miss",
+       "d_rx_decrypt_crc_err", "d_rx_duplicate_frames",
+       "d_rx_mic_err", "d_rx_tkip_format_err", "d_rx_ccmp_format_err",
+       "d_rx_ccmp_replay_err",
+
+       /* Misc stats. */
+       "d_beacon_miss", "d_num_connects", "d_num_disconnects",
+       "d_beacon_avg_rssi", "d_arp_received", "d_arp_matched",
+       "d_arp_replied"
+};
+
+#define ATH6KL_STATS_LEN       ARRAY_SIZE(ath6kl_gstrings_sta_stats)
+
+static int ath6kl_get_sset_count(struct net_device *dev, int sset)
+{
+       int rv = 0;
+
+       if (sset == ETH_SS_STATS)
+               rv += ATH6KL_STATS_LEN;
+
+       if (rv == 0)
+               return -EOPNOTSUPP;
+       return rv;
+}
+
+static void ath6kl_get_stats(struct net_device *dev,
+                           struct ethtool_stats *stats,
+                           u64 *data)
+{
+       struct ath6kl_vif *vif = netdev_priv(dev);
+       struct ath6kl *ar = vif->ar;
+       int i = 0;
+       struct target_stats *tgt_stats;
+
+       memset(data, 0, sizeof(u64) * ATH6KL_STATS_LEN);
+
+       ath6kl_read_tgt_stats(ar, vif);
+
+       tgt_stats = &vif->target_stats;
+
+       data[i++] = tgt_stats->tx_ucast_pkt + tgt_stats->tx_bcast_pkt;
+       data[i++] = tgt_stats->tx_ucast_byte + tgt_stats->tx_bcast_byte;
+       data[i++] = tgt_stats->rx_ucast_pkt + tgt_stats->rx_bcast_pkt;
+       data[i++] = tgt_stats->rx_ucast_byte + tgt_stats->rx_bcast_byte;
+
+       data[i++] = tgt_stats->tx_ucast_pkt;
+       data[i++] = tgt_stats->tx_bcast_pkt;
+       data[i++] = tgt_stats->tx_ucast_byte;
+       data[i++] = tgt_stats->tx_bcast_byte;
+       data[i++] = tgt_stats->tx_rts_success_cnt;
+       data[i++] = tgt_stats->tx_err;
+       data[i++] = tgt_stats->tx_fail_cnt;
+       data[i++] = tgt_stats->tx_retry_cnt;
+       data[i++] = tgt_stats->tx_mult_retry_cnt;
+       data[i++] = tgt_stats->tx_rts_fail_cnt;
+       data[i++] = tgt_stats->tkip_cnter_measures_invoked;
+
+       data[i++] = tgt_stats->rx_ucast_pkt;
+       data[i++] = tgt_stats->rx_ucast_rate;
+       data[i++] = tgt_stats->rx_bcast_pkt;
+       data[i++] = tgt_stats->rx_ucast_byte;
+       data[i++] = tgt_stats->rx_bcast_byte;
+       data[i++] = tgt_stats->rx_frgment_pkt;
+       data[i++] = tgt_stats->rx_err;
+       data[i++] = tgt_stats->rx_crc_err;
+       data[i++] = tgt_stats->rx_key_cache_miss;
+       data[i++] = tgt_stats->rx_decrypt_err;
+       data[i++] = tgt_stats->rx_dupl_frame;
+       data[i++] = tgt_stats->tkip_local_mic_fail;
+       data[i++] = tgt_stats->tkip_fmt_err;
+       data[i++] = tgt_stats->ccmp_fmt_err;
+       data[i++] = tgt_stats->ccmp_replays;
+
+       data[i++] = tgt_stats->cs_bmiss_cnt;
+       data[i++] = tgt_stats->cs_connect_cnt;
+       data[i++] = tgt_stats->cs_discon_cnt;
+       data[i++] = tgt_stats->cs_ave_beacon_rssi;
+       data[i++] = tgt_stats->arp_received;
+       data[i++] = tgt_stats->arp_matched;
+       data[i++] = tgt_stats->arp_replied;
+
+       if (i !=  ATH6KL_STATS_LEN) {
+               WARN_ON_ONCE(1);
+               ath6kl_err("ethtool stats error, i: %d  STATS_LEN: %d\n",
+                          i, (int)ATH6KL_STATS_LEN);
+       }
+}
+
+/* These stats are per NIC, not really per vdev, so we just ignore dev. */
+static void ath6kl_get_strings(struct net_device *dev, u32 sset, u8 *data)
+{
+       int sz_sta_stats = 0;
+
+       if (sset == ETH_SS_STATS) {
+               sz_sta_stats = sizeof(ath6kl_gstrings_sta_stats);
+               memcpy(data, ath6kl_gstrings_sta_stats, sz_sta_stats);
+       }
+}
+
+static const struct ethtool_ops ath6kl_ethtool_ops = {
+       .get_drvinfo = cfg80211_get_drvinfo,
+       .get_link = ethtool_op_get_link,
+       .get_strings = ath6kl_get_strings,
+       .get_ethtool_stats = ath6kl_get_stats,
+       .get_sset_count = ath6kl_get_sset_count,
+};
+
 struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
                                          unsigned char name_assign_type,
                                          enum nl80211_iftype type,
@@ -3679,6 +3810,8 @@ struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
        if (ath6kl_cfg80211_vif_init(vif))
                goto err;
 
+       netdev_set_default_ethtool_ops(ndev, &ath6kl_ethtool_ops);
+
        if (register_netdevice(ndev))
                goto err;
 
@@ -3786,6 +3919,9 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
                ath6kl_band_2ghz.ht_cap.ht_supported = false;
                ath6kl_band_5ghz.ht_cap.cap = 0;
                ath6kl_band_5ghz.ht_cap.ht_supported = false;
+
+               if (ht)
+                       ath6kl_err("Firmware lacks RSN-CAP-OVERRIDE, so HT (802.11n) is disabled.");
        }
 
        if (test_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES,
@@ -3794,11 +3930,18 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
                ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
                ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
                ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff;
+               ar->hw.tx_ant = 2;
+               ar->hw.rx_ant = 2;
        } else {
                ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
                ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+               ar->hw.tx_ant = 1;
+               ar->hw.rx_ant = 1;
        }
 
+       wiphy->available_antennas_tx = ar->hw.tx_ant;
+       wiphy->available_antennas_rx = ar->hw.rx_ant;
+
        if (band_2gig)
                wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
        if (band_5gig)
index 2b78c863d03095b035991d3d123ea375b35a4cb6..5f3acfe6015e55b3f4b5f9e8ade1329b68384602 100644 (file)
@@ -782,6 +782,8 @@ struct ath6kl {
                u32 refclk_hz;
                u32 uarttx_pin;
                u32 testscript_addr;
+               u8 tx_ant;
+               u8 rx_ant;
                enum wmi_phy_cap cap;
 
                u32 flags;
index 81ba48d2938b2cfc3f11d22e6fb723a8f03f82e5..e2b7809d7886706994d1bffa30c5726e1bde6142 100644 (file)
@@ -98,6 +98,33 @@ void ath6kl_warn(const char *fmt, ...)
 }
 EXPORT_SYMBOL(ath6kl_warn);
 
+int ath6kl_read_tgt_stats(struct ath6kl *ar, struct ath6kl_vif *vif)
+{
+       long left;
+
+       if (down_interruptible(&ar->sem))
+               return -EBUSY;
+
+       set_bit(STATS_UPDATE_PEND, &vif->flags);
+
+       if (ath6kl_wmi_get_stats_cmd(ar->wmi, 0)) {
+               up(&ar->sem);
+               return -EIO;
+       }
+
+       left = wait_event_interruptible_timeout(ar->event_wq,
+                                               !test_bit(STATS_UPDATE_PEND,
+                                               &vif->flags), WMI_TIMEOUT);
+
+       up(&ar->sem);
+
+       if (left <= 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+EXPORT_SYMBOL(ath6kl_read_tgt_stats);
+
 #ifdef CONFIG_ATH6KL_DEBUG
 
 void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...)
@@ -544,42 +571,24 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
        char *buf;
        unsigned int len = 0, buf_len = 1500;
        int i;
-       long left;
        ssize_t ret_cnt;
+       int rv;
 
        vif = ath6kl_vif_first(ar);
        if (!vif)
                return -EIO;
 
-       tgt_stats = &vif->target_stats;
-
        buf = kzalloc(buf_len, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
-       if (down_interruptible(&ar->sem)) {
+       rv = ath6kl_read_tgt_stats(ar, vif);
+       if (rv < 0) {
                kfree(buf);
-               return -EBUSY;
+               return rv;
        }
 
-       set_bit(STATS_UPDATE_PEND, &vif->flags);
-
-       if (ath6kl_wmi_get_stats_cmd(ar->wmi, 0)) {
-               up(&ar->sem);
-               kfree(buf);
-               return -EIO;
-       }
-
-       left = wait_event_interruptible_timeout(ar->event_wq,
-                                               !test_bit(STATS_UPDATE_PEND,
-                                               &vif->flags), WMI_TIMEOUT);
-
-       up(&ar->sem);
-
-       if (left <= 0) {
-               kfree(buf);
-               return -ETIMEDOUT;
-       }
+       tgt_stats = &vif->target_stats;
 
        len += scnprintf(buf + len, buf_len - len, "\n");
        len += scnprintf(buf + len, buf_len - len, "%25s\n",
index 19106ed28961643870d36d95456ee80b3af6ec18..0614393dd7ae7c1f6f3de79bce6ecf3338082592 100644 (file)
@@ -59,6 +59,8 @@ enum ath6kl_war {
        ATH6KL_WAR_INVALID_RATE,
 };
 
+int ath6kl_read_tgt_stats(struct ath6kl *ar, struct ath6kl_vif *vif);
+
 #ifdef CONFIG_ATH6KL_DEBUG
 
 void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...);
index e481f14b98787e88354278d26e3c59615ff2851c..fffb65b3e6526428f1758620c219a9cbaf784e08 100644 (file)
@@ -1085,9 +1085,7 @@ static int htc_setup_tx_complete(struct htc_target *target)
        send_pkt->completion = NULL;
        ath6kl_htc_tx_prep_pkt(send_pkt, 0, 0, 0);
        status = ath6kl_htc_tx_issue(target, send_pkt);
-
-       if (send_pkt != NULL)
-               htc_reclaim_txctrl_buf(target, send_pkt);
+       htc_reclaim_txctrl_buf(target, send_pkt);
 
        return status;
 }
index 6e473fa4b13cae0df30a33fd9162729afaf76f6b..6ae0734f86e0f9e58053362307273921e9fdfd67 100644 (file)
@@ -715,6 +715,7 @@ static bool check_device_tree(struct ath6kl *ar)
                                   board_filename, ret);
                        continue;
                }
+               of_node_put(node);
                return true;
        }
        return false;
@@ -994,7 +995,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
                switch (ie_id) {
                case ATH6KL_FW_IE_FW_VERSION:
                        strlcpy(ar->wiphy->fw_version, data,
-                               sizeof(ar->wiphy->fw_version));
+                               min(sizeof(ar->wiphy->fw_version), ie_len+1));
 
                        ath6kl_dbg(ATH6KL_DBG_BOOT,
                                   "found fw version %s\n",
index 6314ae2e93e34d0523b763031230bed4943244b7..9d17a5375f6497c6e02ed368e332e7cd4fc27fe6 100644 (file)
 #define AR_PHY_CCA_MIN_GOOD_VAL_9271_2GHZ      -127
 #define AR_PHY_CCA_MAX_GOOD_VAL_9271_2GHZ      -116
 
-#define AR_PHY_CCA_NOM_VAL_9287_2GHZ           -120
+#define AR_PHY_CCA_NOM_VAL_9287_2GHZ           -112
 #define AR_PHY_CCA_MIN_GOOD_VAL_9287_2GHZ    -127
-#define AR_PHY_CCA_MAX_GOOD_VAL_9287_2GHZ    -110
+#define AR_PHY_CCA_MAX_GOOD_VAL_9287_2GHZ    -97
 
 #endif
index 174442beb9522d85f304be1cdc6ccbc6acf14c7d..0c391997a2f77709cf5f7531dbcdeadc338b4aed 100644 (file)
@@ -1249,7 +1249,8 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g)
                REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
                              AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0);
 
-       if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
+       if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
+           AR_SREV_9561(ah)) {
                if (is_2g)
                        REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
                                      AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR,
@@ -1640,7 +1641,8 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
 
 skip_tx_iqcal:
        if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) {
-               if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) {
+               if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah) ||
+                   AR_SREV_9561(ah)) {
                        for (i = 0; i < AR9300_MAX_CHAINS; i++) {
                                if (!(ah->rxchainmask & (1 << i)))
                                        continue;
index 79fd3b2dcbdef9fc117b99177cb44fe46a581ae4..8b238c15916df3281189b015a7df6a9034263161 100644 (file)
@@ -857,7 +857,7 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah)
                               qca956x_1p0_common_rx_gain_table);
                INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
                               qca956x_1p0_common_rx_gain_bounds);
-               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_xlna,
                               qca956x_1p0_xlna_only);
        } else if (AR_SREV_9580(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
@@ -942,7 +942,7 @@ static void ar9003_rx_gain_table_mode2(struct ath_hw *ah)
                               ar9462_2p1_baseband_core_mix_rxgain);
                INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_postamble,
                               ar9462_2p1_baseband_postamble_mix_rxgain);
-               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_xlna,
                               ar9462_2p1_baseband_postamble_5g_xlna);
        } else if (AR_SREV_9462_20(ah)) {
                INIT_INI_ARRAY(&ah->iniModesRxGain,
@@ -951,7 +951,7 @@ static void ar9003_rx_gain_table_mode2(struct ath_hw *ah)
                               ar9462_2p0_baseband_core_mix_rxgain);
                INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_postamble,
                               ar9462_2p0_baseband_postamble_mix_rxgain);
-               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_xlna,
                               ar9462_2p0_baseband_postamble_5g_xlna);
        }
 }
@@ -961,12 +961,12 @@ static void ar9003_rx_gain_table_mode3(struct ath_hw *ah)
        if (AR_SREV_9462_21(ah)) {
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                               ar9462_2p1_common_5g_xlna_only_rxgain);
-               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_xlna,
                               ar9462_2p1_baseband_postamble_5g_xlna);
        } else if (AR_SREV_9462_20(ah)) {
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                               ar9462_2p0_common_5g_xlna_only_rxgain);
-               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_xlna,
                               ar9462_2p0_baseband_postamble_5g_xlna);
        }
 }
index 1ad66b76749b7cbfd6d76fd40843744512308ef8..201425e7f9cb94f031ad47d6d2366543b870d985 100644 (file)
@@ -926,19 +926,18 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
                 */
                if ((ar9003_hw_get_rx_gain_idx(ah) == 2) ||
                    (ar9003_hw_get_rx_gain_idx(ah) == 3)) {
-                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_xlna,
                                        modesIndex, regWrites);
                }
-
-               if (AR_SREV_9561(ah) && (ar9003_hw_get_rx_gain_idx(ah) == 0))
-                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
-                                       modesIndex, regWrites);
        }
 
        if (AR_SREV_9550(ah) || AR_SREV_9561(ah))
                REG_WRITE_ARRAY(&ah->ini_modes_rx_gain_bounds, modesIndex,
                                regWrites);
 
+       if (AR_SREV_9561(ah) && (ar9003_hw_get_rx_gain_idx(ah) == 0))
+               REG_WRITE_ARRAY(&ah->ini_modes_rxgain_xlna,
+                               modesIndex, regWrites);
        /*
         * TXGAIN initvals.
         */
index c85c47978e1e48e4dda029b1f9d55e5c04accf11..b42f4a963ef40ff0e6e29997099ad7cd66a779c1 100644 (file)
@@ -635,6 +635,7 @@ struct ath9k_vif_iter_data {
        int nstations; /* number of station vifs */
        int nwds;      /* number of WDS vifs */
        int nadhocs;   /* number of adhoc vifs */
+       int nocbs;     /* number of OCB vifs */
        struct ieee80211_vif *primary_sta;
 };
 
index 3b289f93340513006ca42ed88e243eda7b42f004..84afcf78151f60b990cd142822d1ef9463fd243c 100644 (file)
@@ -207,6 +207,7 @@ static ssize_t read_file_phy_err(struct file *file, char __user *user_buf,
        PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR);
        PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE);
        PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR);
+
        PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING);
        PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY);
        PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL);
@@ -214,17 +215,24 @@ static ssize_t read_file_phy_err(struct file *file, char __user *user_buf,
        PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP);
        PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE);
        PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART);
-       PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT);
+
+       PHY_ERR("CCK-BLOCKER ERR", ATH9K_PHYERR_CCK_BLOCKER);
        PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING);
        PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC);
        PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL);
-       PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE);
-       PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART);
        PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL);
        PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP);
+       PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE);
+       PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART);
+
        PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR);
        PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL);
        PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL);
+       PHY_ERR("HT-ZLF ERR", ATH9K_PHYERR_HT_ZLF);
+
+       PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT);
+       PHY_ERR("GREEN-FIELD ERR", ATH9K_PHYERR_GREEN_FIELD);
+       PHY_ERR("SPECTRAL ERR", ATH9K_PHYERR_SPECTRAL);
 
        if (len > size)
                len = size;
index da32c8faad94f1b58f2157c046e5db1bdb04f6f3..6de64cface3c26d3ff4dba5c9b0b99305a8dd348 100644 (file)
@@ -741,8 +741,8 @@ static int read_file_misc(struct seq_file *file, void *data)
                           i++, (int)(ctx->assigned), iter_data.naps,
                           iter_data.nstations,
                           iter_data.nmeshes, iter_data.nwds);
-               seq_printf(file, " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
-                          iter_data.nadhocs, sc->cur_chan->nvifs,
+               seq_printf(file, " ADHOC: %i OCB: %i TOTAL: %hi BEACON-VIF: %hi\n",
+                          iter_data.nadhocs, iter_data.nocbs, sc->cur_chan->nvifs,
                           sc->nbcnvifs);
        }
 
index 10c02f5cbc5eb8ec45abbbbab4c11ad973aabbc1..165dd202c3654412db8dc79745d12fd48b78d29a 100644 (file)
 #include <asm/unaligned.h>
 #include "htc.h"
 
-/* identify firmware images */
-#define FIRMWARE_AR7010_1_1     "htc_7010.fw"
-#define FIRMWARE_AR9271         "htc_9271.fw"
-
-MODULE_FIRMWARE(FIRMWARE_AR7010_1_1);
-MODULE_FIRMWARE(FIRMWARE_AR9271);
+MODULE_FIRMWARE(HTC_7010_MODULE_FW);
+MODULE_FIRMWARE(HTC_9271_MODULE_FW);
 
 static struct usb_device_id ath9k_hif_usb_ids[] = {
        { USB_DEVICE(0x0cf3, 0x9271) }, /* Atheros */
@@ -1080,12 +1076,88 @@ static void ath9k_hif_usb_firmware_fail(struct hif_device_usb *hif_dev)
                device_unlock(parent);
 }
 
+static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context);
+
+/* taken from iwlwifi */
+static int ath9k_hif_request_firmware(struct hif_device_usb *hif_dev,
+                                     bool first)
+{
+       char index[8], *chip;
+       int ret;
+
+       if (first) {
+               if (htc_use_dev_fw) {
+                       hif_dev->fw_minor_index = FIRMWARE_MINOR_IDX_MAX + 1;
+                       sprintf(index, "%s", "dev");
+               } else {
+                       hif_dev->fw_minor_index = FIRMWARE_MINOR_IDX_MAX;
+                       sprintf(index, "%d", hif_dev->fw_minor_index);
+               }
+       } else {
+               hif_dev->fw_minor_index--;
+               sprintf(index, "%d", hif_dev->fw_minor_index);
+       }
+
+       /* test for FW 1.3 */
+       if (MAJOR_VERSION_REQ == 1 && hif_dev->fw_minor_index == 3) {
+               const char *filename;
+
+               if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info))
+                       filename = FIRMWARE_AR7010_1_1;
+               else
+                       filename = FIRMWARE_AR9271;
+
+               /* expected fw locations:
+                * - htc_9271.fw   (stable version 1.3, depricated)
+                */
+               snprintf(hif_dev->fw_name, sizeof(hif_dev->fw_name),
+                        "%s", filename);
+
+       } else if (hif_dev->fw_minor_index < FIRMWARE_MINOR_IDX_MIN) {
+               dev_err(&hif_dev->udev->dev, "no suitable firmware found!\n");
+
+               return -ENOENT;
+       } else {
+               if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info))
+                       chip = "7010";
+               else
+                       chip = "9271";
+
+               /* expected fw locations:
+                * - ath9k_htc/htc_9271-1.dev.0.fw (development version)
+                * - ath9k_htc/htc_9271-1.4.0.fw   (stable version)
+                */
+               snprintf(hif_dev->fw_name, sizeof(hif_dev->fw_name),
+                        "%s/htc_%s-%d.%s.0.fw", HTC_FW_PATH,
+                        chip, MAJOR_VERSION_REQ, index);
+       }
+
+       ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name,
+                                     &hif_dev->udev->dev, GFP_KERNEL,
+                                     hif_dev, ath9k_hif_usb_firmware_cb);
+       if (ret) {
+               dev_err(&hif_dev->udev->dev,
+                       "ath9k_htc: Async request for firmware %s failed\n",
+                       hif_dev->fw_name);
+               return ret;
+       }
+
+       dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n",
+                hif_dev->fw_name);
+
+       return ret;
+}
+
 static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context)
 {
        struct hif_device_usb *hif_dev = context;
        int ret;
 
        if (!fw) {
+               ret = ath9k_hif_request_firmware(hif_dev, false);
+               if (!ret)
+                       return;
+
                dev_err(&hif_dev->udev->dev,
                        "ath9k_htc: Failed to get firmware %s\n",
                        hif_dev->fw_name);
@@ -1215,27 +1287,11 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface,
 
        init_completion(&hif_dev->fw_done);
 
-       /* Find out which firmware to load */
-
-       if (IS_AR7010_DEVICE(id->driver_info))
-               hif_dev->fw_name = FIRMWARE_AR7010_1_1;
-       else
-               hif_dev->fw_name = FIRMWARE_AR9271;
-
-       ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name,
-                                     &hif_dev->udev->dev, GFP_KERNEL,
-                                     hif_dev, ath9k_hif_usb_firmware_cb);
-       if (ret) {
-               dev_err(&hif_dev->udev->dev,
-                       "ath9k_htc: Async request for firmware %s failed\n",
-                       hif_dev->fw_name);
+       ret = ath9k_hif_request_firmware(hif_dev, true);
+       if (ret)
                goto err_fw_req;
-       }
 
-       dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n",
-                hif_dev->fw_name);
-
-       return 0;
+       return ret;
 
 err_fw_req:
        usb_set_intfdata(interface, NULL);
index 51496e74b83eaf3521230421f3a07350c385e3d4..7c2ef7ecd98b4986f7818d862c772d70c219faa6 100644 (file)
 #ifndef HTC_USB_H
 #define HTC_USB_H
 
+/* old firmware images */
+#define FIRMWARE_AR7010_1_1     "htc_7010.fw"
+#define FIRMWARE_AR9271         "htc_9271.fw"
+
+/* supported Major FW version */
 #define MAJOR_VERSION_REQ 1
 #define MINOR_VERSION_REQ 3
+/* minimal and maximal supported Minor FW version. */
+#define FIRMWARE_MINOR_IDX_MAX  4
+#define FIRMWARE_MINOR_IDX_MIN  3
+#define HTC_FW_PATH    "ath9k_htc"
+
+#define HTC_9271_MODULE_FW  HTC_FW_PATH "/htc_9271-" \
+                       __stringify(MAJOR_VERSION_REQ) \
+                       "." __stringify(FIRMWARE_MINOR_IDX_MAX) ".0.fw"
+#define HTC_7010_MODULE_FW  HTC_FW_PATH "/htc_7010-" \
+                       __stringify(MAJOR_VERSION_REQ) \
+                       "." __stringify(FIRMWARE_MINOR_IDX_MAX) ".0.fw"
+
+extern int htc_use_dev_fw;
 
 #define IS_AR7010_DEVICE(_v) (((_v) == AR9280_USB) || ((_v) == AR9287_USB))
 
@@ -101,7 +119,8 @@ struct hif_device_usb {
        struct usb_anchor reg_in_submitted;
        struct usb_anchor mgmt_submitted;
        struct sk_buff *remain_skb;
-       const char *fw_name;
+       char fw_name[32];
+       int fw_minor_index;
        int rx_remain_len;
        int rx_pkt_len;
        int rx_transfer_len;
index 1e84882f8c5b35374df2a6460aada9bfac1bfa5a..8647ab77c019a30e7b8044b6147a62493626529b 100644 (file)
@@ -38,6 +38,10 @@ static int ath9k_ps_enable;
 module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
 MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
 
+int htc_use_dev_fw = 0;
+module_param_named(use_dev_fw, htc_use_dev_fw, int, 0444);
+MODULE_PARM_DESC(use_dev_fw, "Use development FW version");
+
 #ifdef CONFIG_MAC80211_LEDS
 int ath9k_htc_led_blink = 1;
 module_param_named(blink, ath9k_htc_led_blink, int, 0444);
@@ -736,7 +740,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
                BIT(NL80211_IFTYPE_AP) |
                BIT(NL80211_IFTYPE_P2P_GO) |
                BIT(NL80211_IFTYPE_P2P_CLIENT) |
-               BIT(NL80211_IFTYPE_MESH_POINT);
+               BIT(NL80211_IFTYPE_MESH_POINT) |
+               BIT(NL80211_IFTYPE_OCB);
 
        hw->wiphy->iface_combinations = &if_comb;
        hw->wiphy->n_iface_combinations = 1;
index 172a9ff4aaabc5a27d0a318f9cfe94d05f48c21a..a680a970b7f7ef065bc09b83e2c80c4844d24c94 100644 (file)
@@ -1659,7 +1659,7 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif,
                                  enum ieee80211_ampdu_mlme_action action,
                                  struct ieee80211_sta *sta,
-                                 u16 tid, u16 *ssn, u8 buf_size)
+                                 u16 tid, u16 *ssn, u8 buf_size, bool amsdu)
 {
        struct ath9k_htc_priv *priv = hw->priv;
        struct ath9k_htc_sta *ista;
index 1dd0339de372901d753fa70f975e4c5b0f492f21..bdfff46419313955aaafc0b37063fe9e4b63e270 100644 (file)
@@ -1241,6 +1241,7 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode)
                        break;
                }
                /* fall through */
+       case NL80211_IFTYPE_OCB:
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_AP:
                set |= AR_STA_ID1_STA_AP;
index e8454db17634b95450f643ada6f52db82a57aa84..4f0a3f6b0c52bab025b332332ef4e30f0a63b343 100644 (file)
@@ -919,7 +919,7 @@ struct ath_hw {
        struct ar5416IniArray iniCckfirJapan2484;
        struct ar5416IniArray iniModes_9271_ANI_reg;
        struct ar5416IniArray ini_radio_post_sys2ant;
-       struct ar5416IniArray ini_modes_rxgain_5g_xlna;
+       struct ar5416IniArray ini_modes_rxgain_xlna;
        struct ar5416IniArray ini_modes_rxgain_bb_core;
        struct ar5416IniArray ini_modes_rxgain_bb_postamble;
 
index 90eb75012e4f4818a03481d98cb9b137a89393fa..2e2b92ba96b8ba93203f72f3ecf98d875002b5a7 100644 (file)
@@ -855,7 +855,8 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                        BIT(NL80211_IFTYPE_STATION) |
                        BIT(NL80211_IFTYPE_ADHOC) |
                        BIT(NL80211_IFTYPE_MESH_POINT) |
-                       BIT(NL80211_IFTYPE_WDS);
+                       BIT(NL80211_IFTYPE_WDS) |
+                       BIT(NL80211_IFTYPE_OCB);
 
                if (ath9k_is_chanctx_enabled())
                        hw->wiphy->interface_modes |=
index e55fa11894b6cc5d90846eeed6bbf2b157e21187..7fbf7f965f61c21bbc22371a1ddbf847252379df 100644 (file)
@@ -209,21 +209,25 @@ enum ath9k_phyerr {
        ATH9K_PHYERR_OFDM_POWER_DROP      = 21,
        ATH9K_PHYERR_OFDM_SERVICE         = 22,
        ATH9K_PHYERR_OFDM_RESTART         = 23,
-       ATH9K_PHYERR_FALSE_RADAR_EXT      = 24,
 
+       ATH9K_PHYERR_CCK_BLOCKER          = 24,
        ATH9K_PHYERR_CCK_TIMING           = 25,
        ATH9K_PHYERR_CCK_HEADER_CRC       = 26,
        ATH9K_PHYERR_CCK_RATE_ILLEGAL     = 27,
+       ATH9K_PHYERR_CCK_LENGTH_ILLEGAL   = 28,
+       ATH9K_PHYERR_CCK_POWER_DROP       = 29,
        ATH9K_PHYERR_CCK_SERVICE          = 30,
        ATH9K_PHYERR_CCK_RESTART          = 31,
-       ATH9K_PHYERR_CCK_LENGTH_ILLEGAL   = 32,
-       ATH9K_PHYERR_CCK_POWER_DROP       = 33,
 
-       ATH9K_PHYERR_HT_CRC_ERROR         = 34,
-       ATH9K_PHYERR_HT_LENGTH_ILLEGAL    = 35,
-       ATH9K_PHYERR_HT_RATE_ILLEGAL      = 36,
+       ATH9K_PHYERR_HT_CRC_ERROR         = 32,
+       ATH9K_PHYERR_HT_LENGTH_ILLEGAL    = 33,
+       ATH9K_PHYERR_HT_RATE_ILLEGAL      = 34,
+       ATH9K_PHYERR_HT_ZLF               = 35,
+
+       ATH9K_PHYERR_FALSE_RADAR_EXT      = 36,
+       ATH9K_PHYERR_GREEN_FIELD          = 37,
+       ATH9K_PHYERR_SPECTRAL             = 38,
 
-       ATH9K_PHYERR_SPECTRAL             = 38,
        ATH9K_PHYERR_MAX                  = 39,
 };
 
index c27143ba9ffbe8e6d863c0353e3ef3effd29bd5e..d184e682e63642955960fbcfc8602dda4ac1997c 100644 (file)
@@ -938,6 +938,9 @@ static void ath9k_vif_iter(struct ath9k_vif_iter_data *iter_data,
                if (avp->assoc && !iter_data->primary_sta)
                        iter_data->primary_sta = vif;
                break;
+       case NL80211_IFTYPE_OCB:
+               iter_data->nocbs++;
+               break;
        case NL80211_IFTYPE_ADHOC:
                iter_data->nadhocs++;
                if (vif->bss_conf.enable_beacon)
@@ -1111,6 +1114,8 @@ void ath9k_calculate_summary_state(struct ath_softc *sc,
 
                if (iter_data.nmeshes)
                        ah->opmode = NL80211_IFTYPE_MESH_POINT;
+               else if (iter_data.nocbs)
+                       ah->opmode = NL80211_IFTYPE_OCB;
                else if (iter_data.nwds)
                        ah->opmode = NL80211_IFTYPE_AP;
                else if (iter_data.nadhocs)
@@ -1760,7 +1765,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
                ath9k_calculate_summary_state(sc, avp->chanctx);
        }
 
-       if (changed & BSS_CHANGED_IBSS) {
+       if ((changed & BSS_CHANGED_IBSS) ||
+             (changed & BSS_CHANGED_OCB)) {
                memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
                common->curaid = bss_conf->aid;
                ath9k_hw_write_associd(sc->sc_ah);
@@ -1856,7 +1862,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
                              enum ieee80211_ampdu_mlme_action action,
                              struct ieee80211_sta *sta,
-                             u16 tid, u16 *ssn, u8 buf_size)
+                             u16 tid, u16 *ssn, u8 buf_size, bool amsdu)
 {
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
index d3189daf99965e6059d1ce407c99600a822ed69f..994daf6c629798332ffe362efa5dd3481a93cdf1 100644 (file)
@@ -403,7 +403,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
            (sc->cur_chan->nvifs <= 1) &&
            !(sc->cur_chan->rxfilter & FIF_BCN_PRBRESP_PROMISC))
                rfilt |= ATH9K_RX_FILTER_MYBEACON;
-       else
+       else if (sc->sc_ah->opmode != NL80211_IFTYPE_OCB)
                rfilt |= ATH9K_RX_FILTER_BEACON;
 
        if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
index 170c209f99b85cfbbac6142cd625fdd5ddcb2ac8..19d3d64416bf66825eb113590474b4e4a68f8ec2 100644 (file)
@@ -1415,7 +1415,7 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif,
                                    enum ieee80211_ampdu_mlme_action action,
                                    struct ieee80211_sta *sta,
-                                   u16 tid, u16 *ssn, u8 buf_size)
+                                   u16 tid, u16 *ssn, u8 buf_size, bool amsdu)
 {
        struct ar9170 *ar = hw->priv;
        struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
index 924135b8e57543ea19060b802f122627a42ff904..d66533cbc38ab7a56993102d7187765e45dfd907 100644 (file)
@@ -453,7 +453,7 @@ static void carl9170_rx_phy_status(struct ar9170 *ar,
        /* post-process RSSI */
        for (i = 0; i < 7; i++)
                if (phy->rssi[i] & 0x80)
-                       phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f;
+                       phy->rssi[i] = ((~phy->rssi[i] & 0x7f) + 1) & 0x7f;
 
        /* TODO: we could do something with phy_errors */
        status->signal = ar->noise[0] + phy->rssi_combined;
index 656ce42b339a50d3b8bf9644fd5386055b707950..2303ef96299d5bfe6eed3624a5c8e44a0630f943 100644 (file)
 #include "dfs_pri_detector.h"
 #include "ath.h"
 
-/*
- * tolerated deviation of radar time stamp in usecs on both sides
- * TODO: this might need to be HW-dependent
- */
-#define PRI_TOLERANCE  16
-
 /**
  * struct radar_types - contains array of patterns defined for one DFS domain
  * @domain: DFS regulatory domain
@@ -121,7 +115,7 @@ static const struct radar_detector_specs jp_radar_ref_types[] = {
        JP_PATTERN(4, 0, 5, 150, 230, 1, 23, 50, false),
        JP_PATTERN(5, 6, 10, 200, 500, 1, 16, 50, false),
        JP_PATTERN(6, 11, 20, 200, 500, 1, 12, 50, false),
-       JP_PATTERN(7, 50, 100, 1000, 2000, 1, 20, 50, false),
+       JP_PATTERN(7, 50, 100, 1000, 2000, 1, 3, 50, false),
        JP_PATTERN(5, 0, 1, 333, 333, 1, 9, 50, false),
 };
 
@@ -290,10 +284,10 @@ dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
        if (cd == NULL)
                return false;
 
-       dpd->last_pulse_ts = event->ts;
        /* reset detector on time stamp wraparound, caused by TSF reset */
        if (event->ts < dpd->last_pulse_ts)
                dpd_reset(dpd);
+       dpd->last_pulse_ts = event->ts;
 
        /* do type individual pattern matching */
        for (i = 0; i < dpd->num_radar_types; i++) {
index 25a43d632f908dfe72fabd2a2bc6f92f59520a35..92be3530e9b5e4a36538b10594aab5f39a9c2847 100644 (file)
 #include <linux/list.h>
 #include <linux/nl80211.h>
 
+/* tolerated deviation of radar time stamp in usecs on both sides
+ * TODO: this might need to be HW-dependent
+ */
+#define PRI_TOLERANCE  16
+
 /**
  * struct ath_dfs_pool_stats - DFS Statistics for global pools
  */
index cc5c592fc4c007cb7fd0fc8c6c8d05427b274123..05b0464c6b92c35fb7ccf30c66578450e15d4e68 100644 (file)
@@ -25,6 +25,9 @@ struct ath_dfs_pool_stats global_dfs_pool_stats = {};
 
 #define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++)
 #define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--)
+#define GET_PRI_TO_USE(MIN, MAX, RUNTIME) \
+       (MIN + PRI_TOLERANCE == MAX - PRI_TOLERANCE ? \
+       MIN + PRI_TOLERANCE : RUNTIME)
 
 /**
  * struct pulse_elem - elements in pulse queue
@@ -243,7 +246,8 @@ static bool pseq_handler_create_sequences(struct pri_detector *pde,
                ps.count_falses = 0;
                ps.first_ts = p->ts;
                ps.last_ts = ts;
-               ps.pri = ts - p->ts;
+               ps.pri = GET_PRI_TO_USE(pde->rs->pri_min,
+                       pde->rs->pri_max, ts - p->ts);
                ps.dur = ps.pri * (pde->rs->ppb - 1)
                                + 2 * pde->rs->max_pri_tolerance;
 
index 086549b732b95097710276206bf63038ef315414..f8dfa05b290a72d4cb81c1e89bd03de6489e770e 100644 (file)
@@ -79,6 +79,7 @@ static int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch)
        struct wcn36xx_dxe_ctl *cur_ctl = NULL;
        int i;
 
+       spin_lock_init(&ch->lock);
        for (i = 0; i < ch->desc_num; i++) {
                cur_ctl = kzalloc(sizeof(*cur_ctl), GFP_KERNEL);
                if (!cur_ctl)
@@ -169,7 +170,7 @@ void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn)
        wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch);
 }
 
-static int wcn36xx_dxe_init_descs(struct wcn36xx_dxe_ch *wcn_ch)
+static int wcn36xx_dxe_init_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn_ch)
 {
        struct wcn36xx_dxe_desc *cur_dxe = NULL;
        struct wcn36xx_dxe_desc *prev_dxe = NULL;
@@ -178,7 +179,7 @@ static int wcn36xx_dxe_init_descs(struct wcn36xx_dxe_ch *wcn_ch)
        int i;
 
        size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc);
-       wcn_ch->cpu_addr = dma_alloc_coherent(NULL, size, &wcn_ch->dma_addr,
+       wcn_ch->cpu_addr = dma_alloc_coherent(dev, size, &wcn_ch->dma_addr,
                                              GFP_KERNEL);
        if (!wcn_ch->cpu_addr)
                return -ENOMEM;
@@ -270,7 +271,7 @@ static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch)
        return 0;
 }
 
-static int wcn36xx_dxe_fill_skb(struct wcn36xx_dxe_ctl *ctl)
+static int wcn36xx_dxe_fill_skb(struct device *dev, struct wcn36xx_dxe_ctl *ctl)
 {
        struct wcn36xx_dxe_desc *dxe = ctl->desc;
        struct sk_buff *skb;
@@ -279,7 +280,7 @@ static int wcn36xx_dxe_fill_skb(struct wcn36xx_dxe_ctl *ctl)
        if (skb == NULL)
                return -ENOMEM;
 
-       dxe->dst_addr_l = dma_map_single(NULL,
+       dxe->dst_addr_l = dma_map_single(dev,
                                         skb_tail_pointer(skb),
                                         WCN36XX_PKT_SIZE,
                                         DMA_FROM_DEVICE);
@@ -297,7 +298,7 @@ static int wcn36xx_dxe_ch_alloc_skb(struct wcn36xx *wcn,
        cur_ctl = wcn_ch->head_blk_ctl;
 
        for (i = 0; i < wcn_ch->desc_num; i++) {
-               wcn36xx_dxe_fill_skb(cur_ctl);
+               wcn36xx_dxe_fill_skb(wcn->dev, cur_ctl);
                cur_ctl = cur_ctl->next;
        }
 
@@ -345,7 +346,7 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status)
 
 static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
 {
-       struct wcn36xx_dxe_ctl *ctl = ch->tail_blk_ctl;
+       struct wcn36xx_dxe_ctl *ctl;
        struct ieee80211_tx_info *info;
        unsigned long flags;
 
@@ -354,23 +355,25 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
         * completely full head and tail are pointing to the same element
         * and while-do will not make any cycles.
         */
+       spin_lock_irqsave(&ch->lock, flags);
+       ctl = ch->tail_blk_ctl;
        do {
                if (ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)
                        break;
                if (ctl->skb) {
-                       dma_unmap_single(NULL, ctl->desc->src_addr_l,
+                       dma_unmap_single(wcn->dev, ctl->desc->src_addr_l,
                                         ctl->skb->len, DMA_TO_DEVICE);
                        info = IEEE80211_SKB_CB(ctl->skb);
                        if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) {
                                /* Keep frame until TX status comes */
                                ieee80211_free_txskb(wcn->hw, ctl->skb);
                        }
-                       spin_lock_irqsave(&ctl->skb_lock, flags);
+                       spin_lock(&ctl->skb_lock);
                        if (wcn->queues_stopped) {
                                wcn->queues_stopped = false;
                                ieee80211_wake_queues(wcn->hw);
                        }
-                       spin_unlock_irqrestore(&ctl->skb_lock, flags);
+                       spin_unlock(&ctl->skb_lock);
 
                        ctl->skb = NULL;
                }
@@ -379,6 +382,7 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
               !(ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK));
 
        ch->tail_blk_ctl = ctl;
+       spin_unlock_irqrestore(&ch->lock, flags);
 }
 
 static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
@@ -474,7 +478,7 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
        while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) {
                skb = ctl->skb;
                dma_addr = dxe->dst_addr_l;
-               wcn36xx_dxe_fill_skb(ctl);
+               wcn36xx_dxe_fill_skb(wcn->dev, ctl);
 
                switch (ch->ch_type) {
                case WCN36XX_DXE_CH_RX_L:
@@ -491,7 +495,7 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
                        wcn36xx_warn("Unknown channel\n");
                }
 
-               dma_unmap_single(NULL, dma_addr, WCN36XX_PKT_SIZE,
+               dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE,
                                 DMA_FROM_DEVICE);
                wcn36xx_rx_skb(wcn, skb);
                ctl = ctl->next;
@@ -540,7 +544,7 @@ int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn)
                16 - (WCN36XX_BD_CHUNK_SIZE % 8);
 
        s = wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H;
-       cpu_addr = dma_alloc_coherent(NULL, s, &wcn->mgmt_mem_pool.phy_addr,
+       cpu_addr = dma_alloc_coherent(wcn->dev, s, &wcn->mgmt_mem_pool.phy_addr,
                                      GFP_KERNEL);
        if (!cpu_addr)
                goto out_err;
@@ -555,7 +559,7 @@ int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn)
                16 - (WCN36XX_BD_CHUNK_SIZE % 8);
 
        s = wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L;
-       cpu_addr = dma_alloc_coherent(NULL, s, &wcn->data_mem_pool.phy_addr,
+       cpu_addr = dma_alloc_coherent(wcn->dev, s, &wcn->data_mem_pool.phy_addr,
                                      GFP_KERNEL);
        if (!cpu_addr)
                goto out_err;
@@ -574,13 +578,13 @@ out_err:
 void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn)
 {
        if (wcn->mgmt_mem_pool.virt_addr)
-               dma_free_coherent(NULL, wcn->mgmt_mem_pool.chunk_size *
+               dma_free_coherent(wcn->dev, wcn->mgmt_mem_pool.chunk_size *
                                  WCN36XX_DXE_CH_DESC_NUMB_TX_H,
                                  wcn->mgmt_mem_pool.virt_addr,
                                  wcn->mgmt_mem_pool.phy_addr);
 
        if (wcn->data_mem_pool.virt_addr) {
-               dma_free_coherent(NULL, wcn->data_mem_pool.chunk_size *
+               dma_free_coherent(wcn->dev, wcn->data_mem_pool.chunk_size *
                                  WCN36XX_DXE_CH_DESC_NUMB_TX_L,
                                  wcn->data_mem_pool.virt_addr,
                                  wcn->data_mem_pool.phy_addr);
@@ -596,12 +600,14 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
        struct wcn36xx_dxe_desc *desc = NULL;
        struct wcn36xx_dxe_ch *ch = NULL;
        unsigned long flags;
+       int ret;
 
        ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch;
 
+       spin_lock_irqsave(&ch->lock, flags);
        ctl = ch->head_blk_ctl;
 
-       spin_lock_irqsave(&ctl->next->skb_lock, flags);
+       spin_lock(&ctl->next->skb_lock);
 
        /*
         * If skb is not null that means that we reached the tail of the ring
@@ -611,10 +617,11 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
        if (NULL != ctl->next->skb) {
                ieee80211_stop_queues(wcn->hw);
                wcn->queues_stopped = true;
-               spin_unlock_irqrestore(&ctl->next->skb_lock, flags);
+               spin_unlock(&ctl->next->skb_lock);
+               spin_unlock_irqrestore(&ch->lock, flags);
                return -EBUSY;
        }
-       spin_unlock_irqrestore(&ctl->next->skb_lock, flags);
+       spin_unlock(&ctl->next->skb_lock);
 
        ctl->skb = NULL;
        desc = ctl->desc;
@@ -640,10 +647,11 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
        desc = ctl->desc;
        if (ctl->bd_cpu_addr) {
                wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto unlock;
        }
 
-       desc->src_addr_l = dma_map_single(NULL,
+       desc->src_addr_l = dma_map_single(wcn->dev,
                                          ctl->skb->data,
                                          ctl->skb->len,
                                          DMA_TO_DEVICE);
@@ -679,7 +687,10 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
                        ch->reg_ctrl, ch->def_ctrl);
        }
 
-       return 0;
+       ret = 0;
+unlock:
+       spin_unlock_irqrestore(&ch->lock, flags);
+       return ret;
 }
 
 int wcn36xx_dxe_init(struct wcn36xx *wcn)
@@ -696,7 +707,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
        /***************************************/
        /* Init descriptors for TX LOW channel */
        /***************************************/
-       wcn36xx_dxe_init_descs(&wcn->dxe_tx_l_ch);
+       wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_l_ch);
        wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool);
 
        /* Write channel head to a NEXT register */
@@ -714,7 +725,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
        /***************************************/
        /* Init descriptors for TX HIGH channel */
        /***************************************/
-       wcn36xx_dxe_init_descs(&wcn->dxe_tx_h_ch);
+       wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_h_ch);
        wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool);
 
        /* Write channel head to a NEXT register */
@@ -734,7 +745,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
        /***************************************/
        /* Init descriptors for RX LOW channel */
        /***************************************/
-       wcn36xx_dxe_init_descs(&wcn->dxe_rx_l_ch);
+       wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_l_ch);
 
        /* For RX we need to preallocated buffers */
        wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch);
@@ -764,7 +775,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
        /***************************************/
        /* Init descriptors for RX HIGH channel */
        /***************************************/
-       wcn36xx_dxe_init_descs(&wcn->dxe_rx_h_ch);
+       wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_h_ch);
 
        /* For RX we need to prealocat buffers */
        wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch);
index 35ee7e966bd2789f8ea9bb283598d13be4b35e62..3eca4f9594f2a3a3c638996fd13daae66e473283 100644 (file)
@@ -243,6 +243,7 @@ struct wcn36xx_dxe_ctl {
 };
 
 struct wcn36xx_dxe_ch {
+       spinlock_t                      lock;   /* protects head/tail ptrs */
        enum wcn36xx_dxe_ch_type        ch_type;
        void                            *cpu_addr;
        dma_addr_t                      dma_addr;
index 900e72a089d8cbfddacefea50b08a68b790b690f..7c169abdbafee95ccfe8f461e670fbb8ab5ca5eb 100644 (file)
@@ -859,7 +859,7 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
                    struct ieee80211_vif *vif,
                    enum ieee80211_ampdu_mlme_action action,
                    struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                   u8 buf_size)
+                   u8 buf_size, bool amsdu)
 {
        struct wcn36xx *wcn = hw->priv;
        struct wcn36xx_sta *sta_priv = NULL;
index ce8c0381825e674c2eca80510761ae186283073f..6dfedc8bd6a3d7efb1670e34cc1594663bca0582 100644 (file)
@@ -1,5 +1,6 @@
 config WIL6210
        tristate "Wilocity 60g WiFi card wil6210 support"
+       select WANT_DEV_COREDUMP
        depends on CFG80211
        depends on PCI
        default n
index 64b432625fbbdb5ad5c33d9920065937ac2f5572..fdf63d5fe82bfcd12592b8b716bf7716459cf92b 100644 (file)
@@ -17,6 +17,7 @@ wil6210-y += pmc.o
 wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
 wil6210-y += wil_platform.o
 wil6210-y += ethtool.o
+wil6210-y += wil_crash_dump.o
 
 # for tracing framework to find trace.h
 CFLAGS_trace.o := -I$(src)
index d1a1e160ef31132f35d56ace1e7dd12a13b267e6..97bc186f9728247a0048476065d17fe0021a3573 100644 (file)
@@ -1373,6 +1373,12 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
                                }
                        }
                        spin_unlock_bh(&p->tid_rx_lock);
+                       seq_printf(s,
+                                  "Rx invalid frame: non-data %lu, short %lu, large %lu\n",
+                                  p->stats.rx_non_data_frame,
+                                  p->stats.rx_short_frame,
+                                  p->stats.rx_large_frame);
+
                        seq_puts(s, "Rx/MCS:");
                        for (mcs = 0; mcs < ARRAY_SIZE(p->stats.rx_per_mcs);
                             mcs++)
index a371f036d0546388c3bcfa6317e5d9e4ca6857f8..50c136e843c4e71ffcfe69d2d0e40f0b4efb963f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -236,7 +236,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
 
                isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE |
                         BIT_DMA_EP_RX_ICR_RX_HTRSH);
-               if (likely(test_bit(wil_status_reset_done, wil->status))) {
+               if (likely(test_bit(wil_status_fwready, wil->status))) {
                        if (likely(test_bit(wil_status_napi_en, wil->status))) {
                                wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
                                need_unmask = false;
@@ -286,7 +286,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
                isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
                /* clear also all VRING interrupts */
                isr &= ~(BIT(25) - 1UL);
-               if (likely(test_bit(wil_status_reset_done, wil->status))) {
+               if (likely(test_bit(wil_status_fwready, wil->status))) {
                        wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
                        need_unmask = false;
                        napi_schedule(&wil->napi_tx);
@@ -347,7 +347,12 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
        wil6210_mask_irq_misc(wil);
 
        if (isr & ISR_MISC_FW_ERROR) {
-               wil_err(wil, "Firmware error detected\n");
+               u32 fw_assert_code = wil_r(wil, RGF_FW_ASSERT_CODE);
+               u32 ucode_assert_code = wil_r(wil, RGF_UCODE_ASSERT_CODE);
+
+               wil_err(wil,
+                       "Firmware error detected, assert codes FW 0x%08x, UCODE 0x%08x\n",
+                       fw_assert_code, ucode_assert_code);
                clear_bit(wil_status_fwready, wil->status);
                /*
                 * do not clear @isr here - we do 2-nd part in thread
@@ -359,7 +364,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
        if (isr & ISR_MISC_FW_READY) {
                wil_dbg_irq(wil, "IRQ: FW ready\n");
                wil_cache_mbox_regs(wil);
-               set_bit(wil_status_reset_done, wil->status);
+               set_bit(wil_status_mbox_ready, wil->status);
                /**
                 * Actual FW ready indicated by the
                 * WMI_FW_READY_EVENTID
@@ -386,6 +391,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
        wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
 
        if (isr & ISR_MISC_FW_ERROR) {
+               wil_fw_core_dump(wil);
                wil_notify_fw_error(wil);
                isr &= ~ISR_MISC_FW_ERROR;
                wil_fw_error_recovery(wil);
index 2fb04c51da53f2dd4fd58f7995eee85a1a93dcd9..bb69a5949aea62fabb506442e870430c309e66f8 100644 (file)
@@ -203,11 +203,13 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
         * - disconnect single STA, already disconnected
         * - disconnect all
         *
-        * For "disconnect all", there are 2 options:
+        * For "disconnect all", there are 3 options:
         * - bssid == NULL
+        * - bssid is broadcast address (ff:ff:ff:ff:ff:ff)
         * - bssid is our MAC address
         */
-       if (bssid && memcmp(ndev->dev_addr, bssid, ETH_ALEN)) {
+       if (bssid && !is_broadcast_ether_addr(bssid) &&
+           !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
                cid = wil_find_cid(wil, bssid);
                wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
                             bssid, cid, reason_code);
@@ -420,7 +422,7 @@ static void wil_connect_worker(struct work_struct *work)
                wil->sta[cid].status = wil_sta_connected;
                netif_tx_wake_all_queues(ndev);
        } else {
-               wil->sta[cid].status = wil_sta_unused;
+               wil_disconnect_cid(wil, cid, WLAN_REASON_UNSPECIFIED, true);
        }
 }
 
@@ -765,6 +767,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
        if (wil->hw_version == HW_VER_UNKNOWN)
                return -ENODEV;
 
+       set_bit(wil_status_resetting, wil->status);
+
        cancel_work_sync(&wil->disconnect_worker);
        wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
        wil_bcast_fini(wil);
@@ -851,6 +855,12 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
 void wil_fw_error_recovery(struct wil6210_priv *wil)
 {
        wil_dbg_misc(wil, "starting fw error recovery\n");
+
+       if (test_bit(wil_status_resetting, wil->status)) {
+               wil_info(wil, "Reset already in progress\n");
+               return;
+       }
+
        wil->recovery_state = fw_recovery_pending;
        schedule_work(&wil->fw_error_worker);
 }
index feff1ef10fb3d757fc0f00a0fde7a4e127016918..1a3142c332e1f68d3141372a6f5c228753ccb982 100644 (file)
@@ -260,6 +260,7 @@ static const struct pci_device_id wil6210_pcie_ids[] = {
 MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
 
 #ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 
 static int wil6210_suspend(struct device *dev, bool is_runtime)
 {
@@ -307,7 +308,6 @@ static int wil6210_resume(struct device *dev, bool is_runtime)
        return rc;
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int wil6210_pm_suspend(struct device *dev)
 {
        return wil6210_suspend(dev, false);
index 8a8cdc61b25ba46f44b97d6b5b698ed70ffecd60..5ca0307a3274dd7ab9cf3d31c950e856aeb343cc 100644 (file)
@@ -110,7 +110,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil,
         */
        for (i = 0; i < num_descriptors; i++) {
                struct vring_tx_desc *_d = &pmc->pring_va[i];
-               struct vring_tx_desc dd, *d = &dd;
+               struct vring_tx_desc dd = {}, *d = &dd;
                int j = 0;
 
                pmc->descriptors[i].va = dma_alloc_coherent(dev,
index 9238c1ac23dd0311509b6d769e00ee70ce18ec81..e3d1be82f314d32d84d06c82efe9717d1c61377a 100644 (file)
@@ -205,6 +205,32 @@ out:
        spin_unlock(&sta->tid_rx_lock);
 }
 
+/* process BAR frame, called in NAPI context */
+void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq)
+{
+       struct wil_sta_info *sta = &wil->sta[cid];
+       struct wil_tid_ampdu_rx *r;
+
+       spin_lock(&sta->tid_rx_lock);
+
+       r = sta->tid_rx[tid];
+       if (!r) {
+               wil_err(wil, "BAR for non-existing CID %d TID %d\n", cid, tid);
+               goto out;
+       }
+       if (seq_less(seq, r->head_seq_num)) {
+               wil_err(wil, "BAR Seq 0x%03x preceding head 0x%03x\n",
+                       seq, r->head_seq_num);
+               goto out;
+       }
+       wil_dbg_txrx(wil, "BAR: CID %d TID %d Seq 0x%03x head 0x%03x\n",
+                    cid, tid, seq, r->head_seq_num);
+       wil_release_reorder_frames(wil, r, seq);
+
+out:
+       spin_unlock(&sta->tid_rx_lock);
+}
+
 struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
                                                int size, u16 ssn)
 {
index 6229110d558a1a043566091859144b582c34c6c9..3bc9bc0efbaccacd4ea4ded29e859a92b358b624 100644 (file)
@@ -358,6 +358,13 @@ static void wil_rx_add_radiotap_header(struct wil6210_priv *wil,
        }
 }
 
+/* similar to ieee80211_ version, but FC contain only 1-st byte */
+static inline int wil_is_back_req(u8 fc)
+{
+       return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+              (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ);
+}
+
 /**
  * reap 1 frame from @swhead
  *
@@ -379,14 +386,16 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
        u16 dmalen;
        u8 ftype;
        int cid;
-       int i = (int)vring->swhead;
+       int i;
        struct wil_net_stats *stats;
 
        BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb));
 
+again:
        if (unlikely(wil_vring_is_empty(vring)))
                return NULL;
 
+       i = (int)vring->swhead;
        _d = &vring->va[i].rx;
        if (unlikely(!(_d->dma.status & RX_DMA_STATUS_DU))) {
                /* it is not error, we just reached end of Rx done area */
@@ -398,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
        wil_vring_advance_head(vring, 1);
        if (!skb) {
                wil_err(wil, "No Rx skb at [%d]\n", i);
-               return NULL;
+               goto again;
        }
        d = wil_skb_rxdesc(skb);
        *d = *_d;
@@ -409,13 +418,17 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
 
        trace_wil6210_rx(i, d);
        wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", i, dmalen);
-       wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
+       wil_hex_dump_txrx("RxD ", DUMP_PREFIX_NONE, 32, 4,
                          (const void *)d, sizeof(*d), false);
 
+       cid = wil_rxdesc_cid(d);
+       stats = &wil->sta[cid].stats;
+
        if (unlikely(dmalen > sz)) {
                wil_err(wil, "Rx size too large: %d bytes!\n", dmalen);
+               stats->rx_large_frame++;
                kfree_skb(skb);
-               return NULL;
+               goto again;
        }
        skb_trim(skb, dmalen);
 
@@ -424,8 +437,6 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
        wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
                          skb->data, skb_headlen(skb), false);
 
-       cid = wil_rxdesc_cid(d);
-       stats = &wil->sta[cid].stats;
        stats->last_mcs_rx = wil_rxdesc_mcs(d);
        if (stats->last_mcs_rx < ARRAY_SIZE(stats->rx_per_mcs))
                stats->rx_per_mcs[stats->last_mcs_rx]++;
@@ -437,24 +448,47 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
        /* no extra checks if in sniffer mode */
        if (ndev->type != ARPHRD_ETHER)
                return skb;
-       /*
-        * Non-data frames may be delivered through Rx DMA channel (ex: BAR)
+       /* Non-data frames may be delivered through Rx DMA channel (ex: BAR)
         * Driver should recognize it by frame type, that is found
         * in Rx descriptor. If type is not data, it is 802.11 frame as is
         */
        ftype = wil_rxdesc_ftype(d) << 2;
        if (unlikely(ftype != IEEE80211_FTYPE_DATA)) {
-               wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype);
-               /* TODO: process it */
+               u8 fc1 = wil_rxdesc_fc1(d);
+               int mid = wil_rxdesc_mid(d);
+               int tid = wil_rxdesc_tid(d);
+               u16 seq = wil_rxdesc_seq(d);
+
+               wil_dbg_txrx(wil,
+                            "Non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n",
+                            fc1, mid, cid, tid, seq);
+               stats->rx_non_data_frame++;
+               if (wil_is_back_req(fc1)) {
+                       wil_dbg_txrx(wil,
+                                    "BAR: MID %d CID %d TID %d Seq 0x%03x\n",
+                                    mid, cid, tid, seq);
+                       wil_rx_bar(wil, cid, tid, seq);
+               } else {
+                       /* print again all info. One can enable only this
+                        * without overhead for printing every Rx frame
+                        */
+                       wil_dbg_txrx(wil,
+                                    "Unhandled non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n",
+                                    fc1, mid, cid, tid, seq);
+                       wil_hex_dump_txrx("RxD ", DUMP_PREFIX_NONE, 32, 4,
+                                         (const void *)d, sizeof(*d), false);
+                       wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
+                                         skb->data, skb_headlen(skb), false);
+               }
                kfree_skb(skb);
-               return NULL;
+               goto again;
        }
 
        if (unlikely(skb->len < ETH_HLEN + snaplen)) {
                wil_err(wil, "Short frame, len = %d\n", skb->len);
-               /* TODO: process it (i.e. BAR) */
+               stats->rx_short_frame++;
                kfree_skb(skb);
-               return NULL;
+               goto again;
        }
 
        /* L4 IDENT is on when HW calculated checksum, check status
@@ -1208,6 +1242,7 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring,
        int tcp_hdr_len;
        int skb_net_hdr_len;
        int gso_type;
+       int rc = -EINVAL;
 
        wil_dbg_txrx(wil, "%s() %d bytes to vring %d\n",
                     __func__, skb->len, vring_index);
@@ -1299,8 +1334,9 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring,
                                     len, rem_data, descs_used);
 
                        if (descs_used == avail)  {
-                               wil_err(wil, "TSO: ring overflow\n");
-                               goto dma_error;
+                               wil_err_ratelimited(wil, "TSO: ring overflow\n");
+                               rc = -ENOMEM;
+                               goto mem_error;
                        }
 
                        lenmss = min_t(int, rem_data, len);
@@ -1322,8 +1358,10 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring,
                                headlen -= lenmss;
                        }
 
-                       if (unlikely(dma_mapping_error(dev, pa)))
-                               goto dma_error;
+                       if (unlikely(dma_mapping_error(dev, pa))) {
+                               wil_err(wil, "TSO: DMA map page error\n");
+                               goto mem_error;
+                       }
 
                        _desc = &vring->va[i].tx;
 
@@ -1422,8 +1460,8 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring,
        }
 
        /* advance swhead */
-       wil_dbg_txrx(wil, "TSO: Tx swhead %d -> %d\n", swhead, vring->swhead);
        wil_vring_advance_head(vring, descs_used);
+       wil_dbg_txrx(wil, "TSO: Tx swhead %d -> %d\n", swhead, vring->swhead);
 
        /* make sure all writes to descriptors (shared memory) are done before
         * committing them to HW
@@ -1433,8 +1471,7 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring,
        wil_w(wil, vring->hwtail, vring->swhead);
        return 0;
 
-dma_error:
-       wil_err(wil, "TSO: DMA map page error\n");
+mem_error:
        while (descs_used > 0) {
                struct wil_ctx *ctx;
 
@@ -1445,14 +1482,11 @@ dma_error:
                _desc->dma.status = TX_DMA_STATUS_DU;
                ctx = &vring->ctx[i];
                wil_txdesc_unmap(dev, d, ctx);
-               if (ctx->skb)
-                       dev_kfree_skb_any(ctx->skb);
                memset(ctx, 0, sizeof(*ctx));
                descs_used--;
        }
-
 err_exit:
-       return -EINVAL;
+       return rc;
 }
 
 static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
@@ -1528,8 +1562,11 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                _d = &vring->va[i].tx;
                pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
                                      DMA_TO_DEVICE);
-               if (unlikely(dma_mapping_error(dev, pa)))
+               if (unlikely(dma_mapping_error(dev, pa))) {
+                       wil_err(wil, "Tx[%2d] failed to map fragment\n",
+                               vring_index);
                        goto dma_error;
+               }
                vring->ctx[i].mapped_as = wil_mapped_as_page;
                wil_tx_desc_map(d, pa, len, vring_index);
                /* no need to check return code -
@@ -1589,9 +1626,6 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                _d->dma.status = TX_DMA_STATUS_DU;
                wil_txdesc_unmap(dev, d, ctx);
 
-               if (ctx->skb)
-                       dev_kfree_skb_any(ctx->skb);
-
                memset(ctx, 0, sizeof(*ctx));
        }
 
@@ -1633,7 +1667,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                goto drop;
        }
        if (unlikely(!test_bit(wil_status_fwconnected, wil->status))) {
-               wil_err(wil, "FW not connected\n");
+               wil_err_ratelimited(wil, "FW not connected\n");
                goto drop;
        }
        if (unlikely(wil->wdev->iftype == NL80211_IFTYPE_MONITOR)) {
index 82a8f9a030e7e9179db31f6be1cec985a801e8a3..ee7c7b4b9a178845e94530e0b63b3a62c4594456 100644 (file)
@@ -464,6 +464,12 @@ static inline int wil_rxdesc_subtype(struct vring_rx_desc *d)
        return WIL_GET_BITS(d->mac.d0, 12, 15);
 }
 
+/* 1-st byte (with frame type/subtype) of FC field */
+static inline u8 wil_rxdesc_fc1(struct vring_rx_desc *d)
+{
+       return (u8)(WIL_GET_BITS(d->mac.d0, 10, 15) << 2);
+}
+
 static inline int wil_rxdesc_seq(struct vring_rx_desc *d)
 {
        return WIL_GET_BITS(d->mac.d0, 16, 27);
@@ -501,6 +507,7 @@ static inline struct vring_rx_desc *wil_skb_rxdesc(struct sk_buff *skb)
 
 void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev);
 void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb);
+void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq);
 struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
                                                int size, u16 ssn);
 void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
index dd4ea926b8e31eb8721a8c8e2f2a209fd7f0017b..ade5f3b8274b00dc64406dc9b15aa90dc987216b 100644 (file)
@@ -246,6 +246,10 @@ struct RGF_ICR {
 #define RGF_USER_JTAG_DEV_ID   (0x880b34) /* device ID */
        #define JTAG_DEV_ID_SPARROW_B0  (0x2632072f)
 
+/* crash codes for FW/Ucode stored here */
+#define RGF_FW_ASSERT_CODE             (0x91f020)
+#define RGF_UCODE_ASSERT_CODE          (0x91f028)
+
 enum {
        HW_VER_UNKNOWN,
        HW_VER_SPARROW_B0, /* JTAG_DEV_ID_SPARROW_B0 */
@@ -398,13 +402,14 @@ struct vring_tx_data {
 };
 
 enum { /* for wil6210_priv.status */
-       wil_status_fwready = 0,
+       wil_status_fwready = 0, /* FW operational */
        wil_status_fwconnecting,
        wil_status_fwconnected,
        wil_status_dontscan,
-       wil_status_reset_done,
+       wil_status_mbox_ready, /* MBOX structures ready */
        wil_status_irqen, /* FIXME: interrupts enabled - for debug */
        wil_status_napi_en, /* NAPI enabled protected by wil->mutex */
+       wil_status_resetting, /* reset in progress */
        wil_status_last /* keep last */
 };
 
@@ -465,6 +470,9 @@ struct wil_net_stats {
        unsigned long   tx_bytes;
        unsigned long   tx_errors;
        unsigned long   rx_dropped;
+       unsigned long   rx_non_data_frame;
+       unsigned long   rx_short_frame;
+       unsigned long   rx_large_frame;
        u16 last_mcs_rx;
        u64 rx_per_mcs[WIL_MCS_MAX + 1];
 };
@@ -820,4 +828,6 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime);
 int wil_suspend(struct wil6210_priv *wil, bool is_runtime);
 int wil_resume(struct wil6210_priv *wil, bool is_runtime);
 
+void wil_fw_core_dump(struct wil6210_priv *wil);
+
 #endif /* __WIL6210_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
new file mode 100644 (file)
index 0000000..7e70934
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2015 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "wil6210.h"
+#include <linux/devcoredump.h>
+
+static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil,
+                                       u32 *out_dump_size, u32 *out_host_min)
+{
+       int i;
+       const struct fw_map *map;
+       u32 host_min, host_max, tmp_max;
+
+       if (!out_dump_size)
+               return -EINVAL;
+
+       /* calculate the total size of the unpacked crash dump */
+       BUILD_BUG_ON(ARRAY_SIZE(fw_mapping) == 0);
+       map = &fw_mapping[0];
+       host_min = map->host;
+       host_max = map->host + (map->to - map->from);
+
+       for (i = 1; i < ARRAY_SIZE(fw_mapping); i++) {
+               map = &fw_mapping[i];
+
+               if (map->host < host_min)
+                       host_min = map->host;
+
+               tmp_max = map->host + (map->to - map->from);
+               if (tmp_max > host_max)
+                       host_max = tmp_max;
+       }
+
+       *out_dump_size = host_max - host_min;
+       if (out_host_min)
+               *out_host_min = host_min;
+
+       return 0;
+}
+
+static int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest,
+                                 u32 size)
+{
+       int i;
+       const struct fw_map *map;
+       void *data;
+       u32 host_min, dump_size, offset, len;
+
+       if (wil_fw_get_crash_dump_bounds(wil, &dump_size, &host_min)) {
+               wil_err(wil, "%s: fail to obtain crash dump size\n", __func__);
+               return -EINVAL;
+       }
+
+       if (dump_size > size) {
+               wil_err(wil, "%s: not enough space for dump. Need %d have %d\n",
+                       __func__, dump_size, size);
+               return -EINVAL;
+       }
+
+       /* copy to crash dump area */
+       for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
+               map = &fw_mapping[i];
+
+               data = (void * __force)wil->csr + HOSTADDR(map->host);
+               len = map->to - map->from;
+               offset = map->host - host_min;
+
+               wil_dbg_misc(wil, "%s() - dump %s, size %d, offset %d\n",
+                            __func__, fw_mapping[i].name, len, offset);
+
+               wil_memcpy_fromio_32((void * __force)(dest + offset),
+                                    (const void __iomem * __force)data, len);
+       }
+
+       return 0;
+}
+
+void wil_fw_core_dump(struct wil6210_priv *wil)
+{
+       void *fw_dump_data;
+       u32 fw_dump_size;
+
+       if (wil_fw_get_crash_dump_bounds(wil, &fw_dump_size, NULL)) {
+               wil_err(wil, "%s: fail to get fw dump size\n", __func__);
+               return;
+       }
+
+       fw_dump_data = vzalloc(fw_dump_size);
+       if (!fw_dump_data)
+               return;
+
+       if (wil_fw_copy_crash_dump(wil, fw_dump_data, fw_dump_size)) {
+               vfree(fw_dump_data);
+               return;
+       }
+       /* fw_dump_data will be free in device coredump release function
+        * after 5 min
+        */
+       dev_coredumpv(wil_to_dev(wil), fw_dump_data, fw_dump_size, GFP_KERNEL);
+       wil_info(wil, "%s: fw core dumped, size %d bytes\n", __func__,
+                fw_dump_size);
+}
index 2f35d4c51f344332461a0b8e2f4b19cc2a6986a4..6ed26baca0e51bf4266c1edf04aabe801ece6376 100644 (file)
@@ -293,12 +293,6 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
        /* ignore MAC address, we already have it from the boot loader */
        snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version),
                 "%d", wil->fw_version);
-}
-
-static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
-                            int len)
-{
-       wil_dbg_wmi(wil, "WMI: got FW ready event\n");
 
        wil_set_recovery_state(wil, fw_recovery_idle);
        set_bit(wil_status_fwready, wil->status);
@@ -684,13 +678,22 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
        spin_unlock_bh(&sta->tid_rx_lock);
 }
 
+/**
+ * Some events are ignored for purpose; and need not be interpreted as
+ * "unhandled events"
+ */
+static void wmi_evt_ignore(struct wil6210_priv *wil, int id, void *d, int len)
+{
+       wil_dbg_wmi(wil, "Ignore event 0x%04x len %d\n", id, len);
+}
+
 static const struct {
        int eventid;
        void (*handler)(struct wil6210_priv *wil, int eventid,
                        void *data, int data_len);
 } wmi_evt_handlers[] = {
        {WMI_READY_EVENTID,             wmi_evt_ready},
-       {WMI_FW_READY_EVENTID,          wmi_evt_fw_ready},
+       {WMI_FW_READY_EVENTID,                  wmi_evt_ignore},
        {WMI_RX_MGMT_PACKET_EVENTID,    wmi_evt_rx_mgmt},
        {WMI_TX_MGMT_PACKET_EVENTID,            wmi_evt_tx_mgmt},
        {WMI_SCAN_COMPLETE_EVENTID,     wmi_evt_scan_complete},
@@ -701,6 +704,7 @@ static const struct {
        {WMI_RCP_ADDBA_REQ_EVENTID,     wmi_evt_addba_rx_req},
        {WMI_DELBA_EVENTID,             wmi_evt_delba},
        {WMI_VRING_EN_EVENTID,          wmi_evt_vring_en},
+       {WMI_DATA_PORT_OPEN_EVENTID,            wmi_evt_ignore},
 };
 
 /*
@@ -720,7 +724,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
        ulong flags;
        unsigned n;
 
-       if (!test_bit(wil_status_reset_done, wil->status)) {
+       if (!test_bit(wil_status_mbox_ready, wil->status)) {
                wil_err(wil, "Reset in progress. Cannot handle WMI event\n");
                return;
        }
@@ -1120,7 +1124,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
                        cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP);
                cmd.sniffer_cfg.phy_support =
                        cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
-                                   ? WMI_SNIFFER_CP : WMI_SNIFFER_DP);
+                                   ? WMI_SNIFFER_CP : WMI_SNIFFER_BOTH_PHYS);
        } else {
                /* Initialize offload (in non-sniffer mode).
                 * Linux IP stack always calculates IP checksum
index 759fb8d41fc9553b4cbd760f92668c9666b48d7a..fba856032ca550586a963a9477a6a28d06542c7b 100644 (file)
@@ -71,26 +71,6 @@ config B43_PCICORE_AUTOSELECT
        select SSB_DRIVER_PCICORE
        default y
 
-config B43_PCMCIA
-       bool "Broadcom 43xx PCMCIA device support"
-       depends on B43 && B43_SSB && SSB_PCMCIAHOST_POSSIBLE
-       select SSB_PCMCIAHOST
-       ---help---
-         Broadcom 43xx PCMCIA device support.
-
-         Support for 16bit PCMCIA devices.
-         Please note that most PC-CARD devices are _NOT_ 16bit PCMCIA
-         devices, but 32bit CardBUS devices. CardBUS devices are supported
-         out of the box by b43.
-
-         With this config option you can drive b43 cards in
-         CompactFlash formfactor in a PCMCIA adaptor.
-         CF b43 cards can sometimes be found in handheld PCs.
-
-         It's safe to select Y here, even if you don't have a B43 PCMCIA device.
-
-         If unsure, say N.
-
 config B43_SDIO
        bool "Broadcom 43xx SDIO device support"
        depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE
index c624d4d90e4f155cc46b108ea0ce44af161b7ea3..ddc4df46656fcb67b98ded5625931ca66f1eebd1 100644 (file)
@@ -21,7 +21,6 @@ b43-y                         += pio.o
 b43-y                          += rfkill.o
 b43-y                          += ppr.o
 b43-$(CONFIG_B43_LEDS)         += leds.o
-b43-$(CONFIG_B43_PCMCIA)       += pcmcia.o
 b43-$(CONFIG_B43_SDIO)         += sdio.o
 b43-$(CONFIG_B43_DEBUG)                += debugfs.o
 
index 71d3e9adbf3c02b4d5ff50843a2b4b06cd96b6a5..ec013fbd6a81cda4a0cce04a74b469c1f3cda532 100644 (file)
@@ -56,7 +56,6 @@
 #include "sysfs.h"
 #include "xmit.h"
 #include "lo.h"
-#include "pcmcia.h"
 #include "sdio.h"
 #include <linux/mmc/sdio_func.h>
 
@@ -5850,12 +5849,9 @@ static int __init b43_init(void)
        int err;
 
        b43_debugfs_init();
-       err = b43_pcmcia_init();
-       if (err)
-               goto err_dfs_exit;
        err = b43_sdio_init();
        if (err)
-               goto err_pcmcia_exit;
+               goto err_dfs_exit;
 #ifdef CONFIG_B43_BCMA
        err = bcma_driver_register(&b43_bcma_driver);
        if (err)
@@ -5878,8 +5874,6 @@ err_bcma_driver_exit:
 err_sdio_exit:
 #endif
        b43_sdio_exit();
-err_pcmcia_exit:
-       b43_pcmcia_exit();
 err_dfs_exit:
        b43_debugfs_exit();
        return err;
@@ -5894,7 +5888,6 @@ static void __exit b43_exit(void)
        bcma_driver_unregister(&b43_bcma_driver);
 #endif
        b43_sdio_exit();
-       b43_pcmcia_exit();
        b43_debugfs_exit();
 }
 
diff --git a/drivers/net/wireless/b43/pcmcia.c b/drivers/net/wireless/b43/pcmcia.c
deleted file mode 100644 (file)
index 55f2bd7..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
-
-  Broadcom B43 wireless driver
-
-  Copyright (c) 2007 Michael Buesch <m@bues.ch>
-
-  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; see the file COPYING.  If not, write to
-  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
-  Boston, MA 02110-1301, USA.
-
-*/
-
-#include "pcmcia.h"
-
-#include <linux/ssb/ssb.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/cisreg.h>
-
-
-static const struct pcmcia_device_id b43_pcmcia_tbl[] = {
-       PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448),
-       PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476),
-       PCMCIA_DEVICE_NULL,
-};
-
-MODULE_DEVICE_TABLE(pcmcia, b43_pcmcia_tbl);
-
-#ifdef CONFIG_PM
-static int b43_pcmcia_suspend(struct pcmcia_device *dev)
-{
-       struct ssb_bus *ssb = dev->priv;
-
-       return ssb_bus_suspend(ssb);
-}
-
-static int b43_pcmcia_resume(struct pcmcia_device *dev)
-{
-       struct ssb_bus *ssb = dev->priv;
-
-       return ssb_bus_resume(ssb);
-}
-#else /* CONFIG_PM */
-# define b43_pcmcia_suspend            NULL
-# define b43_pcmcia_resume             NULL
-#endif /* CONFIG_PM */
-
-static int b43_pcmcia_probe(struct pcmcia_device *dev)
-{
-       struct ssb_bus *ssb;
-       int err = -ENOMEM;
-       int res = 0;
-
-       ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
-       if (!ssb)
-               goto out_error;
-
-       err = -ENODEV;
-
-       dev->config_flags |= CONF_ENABLE_IRQ;
-
-       dev->resource[2]->flags |=  WIN_ENABLE | WIN_DATA_WIDTH_16 |
-                        WIN_USE_WAIT;
-       dev->resource[2]->start = 0;
-       dev->resource[2]->end = SSB_CORE_SIZE;
-       res = pcmcia_request_window(dev, dev->resource[2], 250);
-       if (res != 0)
-               goto err_kfree_ssb;
-
-       res = pcmcia_map_mem_page(dev, dev->resource[2], 0);
-       if (res != 0)
-               goto err_disable;
-
-       if (!dev->irq)
-               goto err_disable;
-
-       res = pcmcia_enable_device(dev);
-       if (res != 0)
-               goto err_disable;
-
-       err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start);
-       if (err)
-               goto err_disable;
-       dev->priv = ssb;
-
-       return 0;
-
-err_disable:
-       pcmcia_disable_device(dev);
-err_kfree_ssb:
-       kfree(ssb);
-out_error:
-       printk(KERN_ERR "b43-pcmcia: Initialization failed (%d, %d)\n",
-              res, err);
-       return err;
-}
-
-static void b43_pcmcia_remove(struct pcmcia_device *dev)
-{
-       struct ssb_bus *ssb = dev->priv;
-
-       ssb_bus_unregister(ssb);
-       pcmcia_disable_device(dev);
-       kfree(ssb);
-       dev->priv = NULL;
-}
-
-static struct pcmcia_driver b43_pcmcia_driver = {
-       .owner          = THIS_MODULE,
-       .name           = "b43-pcmcia",
-       .id_table       = b43_pcmcia_tbl,
-       .probe          = b43_pcmcia_probe,
-       .remove         = b43_pcmcia_remove,
-       .suspend        = b43_pcmcia_suspend,
-       .resume         = b43_pcmcia_resume,
-};
-
-/*
- * These are not module init/exit functions!
- * The module_pcmcia_driver() helper cannot be used here.
- */
-int b43_pcmcia_init(void)
-{
-       return pcmcia_register_driver(&b43_pcmcia_driver);
-}
-
-void b43_pcmcia_exit(void)
-{
-       pcmcia_unregister_driver(&b43_pcmcia_driver);
-}
diff --git a/drivers/net/wireless/b43/pcmcia.h b/drivers/net/wireless/b43/pcmcia.h
deleted file mode 100644 (file)
index 85f120a..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef B43_PCMCIA_H_
-#define B43_PCMCIA_H_
-
-#ifdef CONFIG_B43_PCMCIA
-
-int b43_pcmcia_init(void);
-void b43_pcmcia_exit(void);
-
-#else /* CONFIG_B43_PCMCIA */
-
-static inline int b43_pcmcia_init(void)
-{
-       return 0;
-}
-static inline void b43_pcmcia_exit(void)
-{
-}
-
-#endif /* CONFIG_B43_PCMCIA */
-#endif /* B43_PCMCIA_H_ */
index fe3dc126b149f8fd014f0bd18ff407d80af00003..ab42b1fea03c170e6728f5b71fe720aa61174053 100644 (file)
@@ -82,5 +82,6 @@ config BRCM_TRACING
 config BRCMDBG
        bool "Broadcom driver debug functions"
        depends on BRCMSMAC || BRCMFMAC
+       select WANT_DEV_COREDUMP
        ---help---
          Selecting this enables additional code for debug purposes.
index 8e0e91c4a0b1670526683ced476e7b366451752e..288c84e7c56b633c21f0e0b80dca1059416a91ad 100644 (file)
@@ -272,10 +272,11 @@ brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
 }
 
 static int
-brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
-                        struct sk_buff *pktbuf)
+brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
+                        struct sk_buff *pktbuf, struct brcmf_if **ifp)
 {
        struct brcmf_proto_bcdc_header *h;
+       struct brcmf_if *tmp_if;
 
        brcmf_dbg(BCDC, "Enter\n");
 
@@ -289,30 +290,21 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
        trace_brcmf_bcdchdr(pktbuf->data);
        h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
 
-       *ifidx = BCDC_GET_IF_IDX(h);
-       if (*ifidx >= BRCMF_MAX_IFS) {
-               brcmf_err("rx data ifnum out of range (%d)\n", *ifidx);
+       tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
+       if (!tmp_if) {
+               brcmf_dbg(INFO, "no matching ifp found\n");
                return -EBADE;
        }
-       /* The ifidx is the idx to map to matching netdev/ifp. When receiving
-        * events this is easy because it contains the bssidx which maps
-        * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
-        * bssidx 1 is used for p2p0 and no data can be received or
-        * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
-        */
-       if (*ifidx)
-               (*ifidx)++;
-
        if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
            BCDC_PROTO_VER) {
                brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
-                         brcmf_ifname(drvr, *ifidx), h->flags);
+                         brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
                return -EBADE;
        }
 
        if (h->flags & BCDC_FLAG_SUM_GOOD) {
                brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
-                         brcmf_ifname(drvr, *ifidx), h->flags);
+                         brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
                pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
        }
 
@@ -320,12 +312,14 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
 
        skb_pull(pktbuf, BCDC_HEADER_LEN);
        if (do_fws)
-               brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf);
+               brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);
        else
                skb_pull(pktbuf, h->data_offset << 2);
 
        if (pktbuf->len == 0)
                return -ENODATA;
+
+       *ifp = tmp_if;
        return 0;
 }
 
index 0445163991b72cf34e78b353825b6505e3775dd1..4e33f96b3dd1b98e638e647a14595e30201e08a0 100644 (file)
@@ -149,7 +149,7 @@ static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data)
 static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
                                    bool trump_sco)
 {
-       struct brcmf_if *ifp = btci->cfg->pub->iflist[0];
+       struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0);
 
        if (trump_sco && !btci->saved_regs_part2) {
                /* this should reduce eSCO agressive
@@ -468,7 +468,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
 {
        struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy);
        struct brcmf_btcoex_info *btci = cfg->btcoex;
-       struct brcmf_if *ifp = cfg->pub->iflist[0];
+       struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
 
        switch (mode) {
        case BRCMF_BTCOEX_DISABLED:
index 89e6a4dc105ecff9e0251d44cc576bc8e5f4facd..230cad788ace44ebe9bff08b3c92562bf0887159 100644 (file)
@@ -65,6 +65,8 @@ struct brcmf_bus_dcmd {
  * @rxctl: receive a control response message from dongle.
  * @gettxq: obtain a reference of bus transmit queue (optional).
  * @wowl_config: specify if dongle is configured for wowl when going to suspend
+ * @get_ramsize: obtain size of device memory.
+ * @get_memdump: obtain device memory dump in provided buffer.
  *
  * This structure provides an abstract interface towards the
  * bus specific driver. For control messages to common driver
@@ -79,6 +81,8 @@ struct brcmf_bus_ops {
        int (*rxctl)(struct device *dev, unsigned char *msg, uint len);
        struct pktq * (*gettxq)(struct device *dev);
        void (*wowl_config)(struct device *dev, bool enabled);
+       size_t (*get_ramsize)(struct device *dev);
+       int (*get_memdump)(struct device *dev, void *data, size_t len);
 };
 
 
@@ -185,6 +189,23 @@ void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled)
                bus->ops->wowl_config(bus->dev, enabled);
 }
 
+static inline size_t brcmf_bus_get_ramsize(struct brcmf_bus *bus)
+{
+       if (!bus->ops->get_ramsize)
+               return 0;
+
+       return bus->ops->get_ramsize(bus->dev);
+}
+
+static inline
+int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len)
+{
+       if (!bus->ops->get_memdump)
+               return -EOPNOTSUPP;
+
+       return bus->ops->get_memdump(bus->dev, data, len);
+}
+
 /*
  * interface functions from common layer
  */
index a293275c1b0bcffcc55265a55d091b3e17edf1af..deb5f78dcacc0d9ef188360d300a7b4f1bcfba19 100644 (file)
@@ -236,89 +236,6 @@ static int brcmf_roamoff;
 module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
 MODULE_PARM_DESC(roamoff, "do not use internal roaming engine");
 
-/* Quarter dBm units to mW
- * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
- * Table is offset so the last entry is largest mW value that fits in
- * a u16.
- */
-
-#define QDBM_OFFSET 153                /* Offset for first entry */
-#define QDBM_TABLE_LEN 40      /* Table size */
-
-/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
- * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
- */
-#define QDBM_TABLE_LOW_BOUND 6493      /* Low bound */
-
-/* Largest mW value that will round down to the last table entry,
- * QDBM_OFFSET + QDBM_TABLE_LEN-1.
- * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
- * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
- */
-#define QDBM_TABLE_HIGH_BOUND 64938    /* High bound */
-
-static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
-/* qdBm:       +0      +1      +2      +3      +4      +5      +6      +7 */
-/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
-/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
-/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
-/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
-/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
-};
-
-static u16 brcmf_qdbm_to_mw(u8 qdbm)
-{
-       uint factor = 1;
-       int idx = qdbm - QDBM_OFFSET;
-
-       if (idx >= QDBM_TABLE_LEN)
-               /* clamp to max u16 mW value */
-               return 0xFFFF;
-
-       /* scale the qdBm index up to the range of the table 0-40
-        * where an offset of 40 qdBm equals a factor of 10 mW.
-        */
-       while (idx < 0) {
-               idx += 40;
-               factor *= 10;
-       }
-
-       /* return the mW value scaled down to the correct factor of 10,
-        * adding in factor/2 to get proper rounding.
-        */
-       return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
-}
-
-static u8 brcmf_mw_to_qdbm(u16 mw)
-{
-       u8 qdbm;
-       int offset;
-       uint mw_uint = mw;
-       uint boundary;
-
-       /* handle boundary case */
-       if (mw_uint <= 1)
-               return 0;
-
-       offset = QDBM_OFFSET;
-
-       /* move mw into the range of the table */
-       while (mw_uint < QDBM_TABLE_LOW_BOUND) {
-               mw_uint *= 10;
-               offset -= 40;
-       }
-
-       for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
-               boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
-                                                   nqdBm_to_mW_map[qdbm]) / 2;
-               if (mw_uint < boundary)
-                       break;
-       }
-
-       qdbm += (u8) offset;
-
-       return qdbm;
-}
 
 static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
                               struct cfg80211_chan_def *ch)
@@ -860,6 +777,37 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
        s32 err = 0;
 
        brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type);
+
+       /* WAR: There are a number of p2p interface related problems which
+        * need to be handled initially (before doing the validate).
+        * wpa_supplicant tends to do iface changes on p2p device/client/go
+        * which are not always possible/allowed. However we need to return
+        * OK otherwise the wpa_supplicant wont start. The situation differs
+        * on configuration and setup (p2pon=1 module param). The first check
+        * is to see if the request is a change to station for p2p iface.
+        */
+       if ((type == NL80211_IFTYPE_STATION) &&
+           ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
+            (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
+            (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
+               brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
+               /* Now depending on whether module param p2pon=1 was used the
+                * response needs to be either 0 or EOPNOTSUPP. The reason is
+                * that if p2pon=1 is used, but a newer supplicant is used then
+                * we should return an error, as this combination wont work.
+                * In other situations 0 is returned and supplicant will start
+                * normally. It will give a trace in cfg80211, but it is the
+                * only way to get it working. Unfortunately this will result
+                * in situation where we wont support new supplicant in
+                * combination with module param p2pon=1, but that is the way
+                * it is. If the user tries this then unloading of driver might
+                * fail/lock.
+                */
+               if (cfg->p2p.p2pdev_dynamically)
+                       return -EOPNOTSUPP;
+               else
+                       return 0;
+       }
        err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
        if (err) {
                brcmf_err("iface validation failed: err=%d\n", err);
@@ -875,18 +823,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
                infra = 0;
                break;
        case NL80211_IFTYPE_STATION:
-               /* Ignore change for p2p IF. Unclear why supplicant does this */
-               if ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
-                   (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) {
-                       brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
-                       /* WAR: It is unexpected to get a change of VIF for P2P
-                        * IF, but it happens. The request can not be handled
-                        * but returning EPERM causes a crash. Returning 0
-                        * without setting ieee80211_ptr->iftype causes trace
-                        * (WARN_ON) but it works with wpa_supplicant
-                        */
-                       return 0;
-               }
                infra = 1;
                break;
        case NL80211_IFTYPE_AP:
@@ -904,7 +840,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
                        err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
                }
                if (!err) {
-                       set_bit(BRCMF_VIF_STATUS_AP_CREATING, &vif->sme_state);
                        brcmf_dbg(INFO, "IF Type = AP\n");
                }
        } else {
@@ -2017,16 +1952,14 @@ static s32
 brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
                            enum nl80211_tx_power_setting type, s32 mbm)
 {
-
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct net_device *ndev = cfg_to_ndev(cfg);
        struct brcmf_if *ifp = netdev_priv(ndev);
-       u16 txpwrmw;
-       s32 err = 0;
-       s32 disable = 0;
-       s32 dbm = MBM_TO_DBM(mbm);
+       s32 err;
+       s32 disable;
+       u32 qdbm = 127;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
        if (!check_vif_up(ifp->vif))
                return -EIO;
 
@@ -2035,12 +1968,20 @@ brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
                break;
        case NL80211_TX_POWER_LIMITED:
        case NL80211_TX_POWER_FIXED:
-               if (dbm < 0) {
+               if (mbm < 0) {
                        brcmf_err("TX_POWER_FIXED - dbm is negative\n");
                        err = -EINVAL;
                        goto done;
                }
+               qdbm =  MBM_TO_DBM(4 * mbm);
+               if (qdbm > 127)
+                       qdbm = 127;
+               qdbm |= WL_TXPWR_OVERRIDE;
                break;
+       default:
+               brcmf_err("Unsupported type %d\n", type);
+               err = -EINVAL;
+               goto done;
        }
        /* Make sure radio is off or on as far as software is concerned */
        disable = WL_RADIO_SW_DISABLE << 16;
@@ -2048,52 +1989,44 @@ brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
        if (err)
                brcmf_err("WLC_SET_RADIO error (%d)\n", err);
 
-       if (dbm > 0xffff)
-               txpwrmw = 0xffff;
-       else
-               txpwrmw = (u16) dbm;
-       err = brcmf_fil_iovar_int_set(ifp, "qtxpower",
-                                     (s32)brcmf_mw_to_qdbm(txpwrmw));
+       err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
        if (err)
                brcmf_err("qtxpower error (%d)\n", err);
-       cfg->conf->tx_power = dbm;
 
 done:
-       brcmf_dbg(TRACE, "Exit\n");
+       brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
        return err;
 }
 
-static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy,
-                                      struct wireless_dev *wdev,
-                                      s32 *dbm)
+static s32
+brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+                           s32 *dbm)
 {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
-       struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
-       s32 txpwrdbm;
-       u8 result;
-       s32 err = 0;
+       struct net_device *ndev = cfg_to_ndev(cfg);
+       struct brcmf_if *ifp = netdev_priv(ndev);
+       s32 qdbm = 0;
+       s32 err;
 
        brcmf_dbg(TRACE, "Enter\n");
        if (!check_vif_up(ifp->vif))
                return -EIO;
 
-       err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm);
+       err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
        if (err) {
                brcmf_err("error (%d)\n", err);
                goto done;
        }
-
-       result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
-       *dbm = (s32) brcmf_qdbm_to_mw(result);
+       *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
 
 done:
-       brcmf_dbg(TRACE, "Exit\n");
+       brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
        return err;
 }
 
 static s32
 brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
-                              u8 key_idx, bool unicast, bool multicast)
+                                 u8 key_idx, bool unicast, bool multicast)
 {
        struct brcmf_if *ifp = netdev_priv(ndev);
        u32 index;
@@ -2498,6 +2431,9 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
        struct brcmf_sta_info_le sta_info_le;
        u32 sta_flags;
        u32 is_tdls_peer;
+       s32 total_rssi;
+       s32 count_rssi;
+       u32 i;
 
        brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
        if (!check_vif_up(ifp->vif))
@@ -2544,13 +2480,13 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
                sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
                if (sinfo->tx_packets) {
                        sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
-                       sinfo->txrate.legacy = le32_to_cpu(sta_info_le.tx_rate);
-                       sinfo->txrate.legacy /= 100;
+                       sinfo->txrate.legacy =
+                               le32_to_cpu(sta_info_le.tx_rate) / 100;
                }
                if (sinfo->rx_packets) {
                        sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
-                       sinfo->rxrate.legacy = le32_to_cpu(sta_info_le.rx_rate);
-                       sinfo->rxrate.legacy /= 100;
+                       sinfo->rxrate.legacy =
+                               le32_to_cpu(sta_info_le.rx_rate) / 100;
                }
                if (le16_to_cpu(sta_info_le.ver) >= 4) {
                        sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
@@ -2558,12 +2494,61 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
                        sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
                        sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
                }
+               total_rssi = 0;
+               count_rssi = 0;
+               for (i = 0; i < BRCMF_ANT_MAX; i++) {
+                       if (sta_info_le.rssi[i]) {
+                               sinfo->chain_signal_avg[count_rssi] =
+                                       sta_info_le.rssi[i];
+                               sinfo->chain_signal[count_rssi] =
+                                       sta_info_le.rssi[i];
+                               total_rssi += sta_info_le.rssi[i];
+                               count_rssi++;
+                       }
+               }
+               if (count_rssi) {
+                       sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
+                       sinfo->chains = count_rssi;
+
+                       sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+                       total_rssi /= count_rssi;
+                       sinfo->signal = total_rssi;
+               }
        }
 done:
        brcmf_dbg(TRACE, "Exit\n");
        return err;
 }
 
+static int
+brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
+                           int idx, u8 *mac, struct station_info *sinfo)
+{
+       struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+       struct brcmf_if *ifp = netdev_priv(ndev);
+       s32 err;
+
+       brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
+
+       if (idx == 0) {
+               cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
+               err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
+                                            &cfg->assoclist,
+                                            sizeof(cfg->assoclist));
+               if (err) {
+                       brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
+                                 err);
+                       cfg->assoclist.count = 0;
+                       return -EOPNOTSUPP;
+               }
+       }
+       if (idx < le32_to_cpu(cfg->assoclist.count)) {
+               memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
+               return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
+       }
+       return -ENOENT;
+}
+
 static s32
 brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
                           bool enabled, s32 timeout)
@@ -4265,8 +4250,8 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
 
                brcmf_dbg(TRACE, "GO mode configuration complete\n");
        }
-       clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
        set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
+       brcmf_net_setcarrier(ifp, true);
 
 exit:
        if ((err) && (!mbss)) {
@@ -4330,8 +4315,8 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
        }
        brcmf_set_mpc(ifp, 1);
        brcmf_configure_arp_offload(ifp, true);
-       set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
        clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
+       brcmf_net_setcarrier(ifp, false);
 
        return err;
 }
@@ -4663,6 +4648,7 @@ static struct cfg80211_ops wl_cfg80211_ops = {
        .join_ibss = brcmf_cfg80211_join_ibss,
        .leave_ibss = brcmf_cfg80211_leave_ibss,
        .get_station = brcmf_cfg80211_get_station,
+       .dump_station = brcmf_cfg80211_dump_station,
        .set_tx_power = brcmf_cfg80211_set_tx_power,
        .get_tx_power = brcmf_cfg80211_get_tx_power,
        .add_key = brcmf_cfg80211_add_key,
@@ -4747,7 +4733,8 @@ void brcmf_cfg80211_free_netdev(struct net_device *ndev)
        ifp = netdev_priv(ndev);
        vif = ifp->vif;
 
-       brcmf_free_vif(vif);
+       if (vif)
+               brcmf_free_vif(vif);
        free_netdev(ndev);
 }
 
@@ -4983,7 +4970,7 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
                brcmf_dbg(CONN, "AP mode link down\n");
                complete(&cfg->vif_disabled);
                if (ifp->vif->mbss)
-                       brcmf_remove_interface(ifp->drvr, ifp->bssidx);
+                       brcmf_remove_interface(ifp);
                return 0;
        }
 
@@ -5039,6 +5026,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
                                &ifp->vif->sme_state);
                } else
                        brcmf_bss_connect_done(cfg, ndev, e, true);
+               brcmf_net_setcarrier(ifp, true);
        } else if (brcmf_is_linkdown(e)) {
                brcmf_dbg(CONN, "Linkdown\n");
                if (!brcmf_is_ibssmode(ifp->vif)) {
@@ -5048,6 +5036,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
                brcmf_init_prof(ndev_to_prof(ndev));
                if (ndev != cfg_to_ndev(cfg))
                        complete(&cfg->vif_disabled);
+               brcmf_net_setcarrier(ifp, false);
        } else if (brcmf_is_nonetwork(cfg, e)) {
                if (brcmf_is_ibssmode(ifp->vif))
                        clear_bit(BRCMF_VIF_STATUS_CONNECTING,
@@ -6211,9 +6200,10 @@ static void brcmf_free_wiphy(struct wiphy *wiphy)
 }
 
 struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
-                                                 struct device *busdev)
+                                                 struct device *busdev,
+                                                 bool p2pdev_forced)
 {
-       struct net_device *ndev = drvr->iflist[0]->ndev;
+       struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
        struct brcmf_cfg80211_info *cfg;
        struct wiphy *wiphy;
        struct brcmf_cfg80211_vif *vif;
@@ -6302,8 +6292,19 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
                else
                        *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
        }
+       /* p2p might require that "if-events" get processed by fweh. So
+        * activate the already registered event handlers now and activate
+        * the rest when initialization has completed. drvr->config needs to
+        * be assigned before activating events.
+        */
+       drvr->config = cfg;
+       err = brcmf_fweh_activate_events(ifp);
+       if (err) {
+               brcmf_err("FWEH activation failed (%d)\n", err);
+               goto wiphy_unreg_out;
+       }
 
-       err = brcmf_p2p_attach(cfg);
+       err = brcmf_p2p_attach(cfg, p2pdev_forced);
        if (err) {
                brcmf_err("P2P initilisation failed (%d)\n", err);
                goto wiphy_unreg_out;
@@ -6324,6 +6325,13 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
                                    brcmf_notify_tdls_peer_event);
        }
 
+       /* (re-) activate FWEH event handling */
+       err = brcmf_fweh_activate_events(ifp);
+       if (err) {
+               brcmf_err("FWEH activation failed (%d)\n", err);
+               goto wiphy_unreg_out;
+       }
+
        return cfg;
 
 wiphy_unreg_out:
@@ -6331,6 +6339,7 @@ wiphy_unreg_out:
 priv_out:
        wl_deinit_priv(cfg);
        brcmf_free_vif(vif);
+       ifp->vif = NULL;
 wiphy_out:
        brcmf_free_wiphy(wiphy);
        return NULL;
index d9e6d01b2b69511ebb4139399a81dff9c02f8641..6a878c8f883f9ee65afdf2948cfdfdcb5e2b6324 100644 (file)
@@ -143,7 +143,6 @@ struct brcmf_cfg80211_profile {
  * @BRCMF_VIF_STATUS_CONNECTING: connect/join in progress.
  * @BRCMF_VIF_STATUS_CONNECTED: connected/joined succesfully.
  * @BRCMF_VIF_STATUS_DISCONNECTING: disconnect/disable in progress.
- * @BRCMF_VIF_STATUS_AP_CREATING: interface configured for AP operation.
  * @BRCMF_VIF_STATUS_AP_CREATED: AP operation started.
  */
 enum brcmf_vif_status {
@@ -151,7 +150,6 @@ enum brcmf_vif_status {
        BRCMF_VIF_STATUS_CONNECTING,
        BRCMF_VIF_STATUS_CONNECTED,
        BRCMF_VIF_STATUS_DISCONNECTING,
-       BRCMF_VIF_STATUS_AP_CREATING,
        BRCMF_VIF_STATUS_AP_CREATED
 };
 
@@ -407,6 +405,7 @@ struct brcmf_cfg80211_info {
        struct brcmu_d11inf d11inf;
        bool wowl_enabled;
        u32 pre_wowl_pmmode;
+       struct brcmf_assoclist_le assoclist;
 };
 
 /**
@@ -469,7 +468,8 @@ brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg)
 }
 
 struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
-                                                 struct device *busdev);
+                                                 struct device *busdev,
+                                                 bool p2pdev_forced);
 void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg);
 s32 brcmf_cfg80211_up(struct net_device *ndev);
 s32 brcmf_cfg80211_down(struct net_device *ndev);
index 288f8314f2086f06c33992be2a4da8eaed7f7df9..f04833db2fd04eddeb7c33012198d4e769a2e9ef 100644 (file)
 /* ARM Cortex M3 core, ID 0x82a */
 #define BCM4329_CORE_ARM_BASE          0x18002000
 
+/* Max possibly supported memory size (limited by IO mapped memory) */
+#define BRCMF_CHIP_MAX_MEMSIZE         (4 * 1024 * 1024)
+
 #define CORE_SB(base, field) \
                (base + SBCONFIGOFF + offsetof(struct sbconfig, field))
 #define        SBCOREREV(sbidh) \
@@ -205,6 +208,7 @@ struct sbsocramregs {
 };
 
 #define SOCRAMREGOFFS(_f)      offsetof(struct sbsocramregs, _f)
+#define SYSMEMREGOFFS(_f)      offsetof(struct sbsocramregs, _f)
 
 #define ARMCR4_CAP             (0x04)
 #define ARMCR4_BANKIDX         (0x40)
@@ -513,6 +517,9 @@ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
                case BCMA_CORE_ARM_CR4:
                        cpu_found = true;
                        break;
+               case BCMA_CORE_ARM_CA7:
+                       cpu_found = true;
+                       break;
                default:
                        break;
                }
@@ -611,6 +618,29 @@ static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize,
        }
 }
 
+/** Return the SYS MEM size */
+static u32 brcmf_chip_sysmem_ramsize(struct brcmf_core_priv *sysmem)
+{
+       u32 memsize = 0;
+       u32 coreinfo;
+       u32 idx;
+       u32 nb;
+       u32 banksize;
+
+       if (!brcmf_chip_iscoreup(&sysmem->pub))
+               brcmf_chip_resetcore(&sysmem->pub, 0, 0, 0);
+
+       coreinfo = brcmf_chip_core_read32(sysmem, SYSMEMREGOFFS(coreinfo));
+       nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+
+       for (idx = 0; idx < nb; idx++) {
+               brcmf_chip_socram_banksize(sysmem, idx, &banksize);
+               memsize += banksize;
+       }
+
+       return memsize;
+}
+
 /** Return the TCM-RAM size of the ARMCR4 core. */
 static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4)
 {
@@ -644,6 +674,7 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
                return 0x198000;
        case BRCM_CC_4335_CHIP_ID:
        case BRCM_CC_4339_CHIP_ID:
+       case BRCM_CC_4350_CHIP_ID:
        case BRCM_CC_4354_CHIP_ID:
        case BRCM_CC_4356_CHIP_ID:
        case BRCM_CC_43567_CHIP_ID:
@@ -651,7 +682,11 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
        case BRCM_CC_43570_CHIP_ID:
        case BRCM_CC_4358_CHIP_ID:
        case BRCM_CC_43602_CHIP_ID:
+       case BRCM_CC_4371_CHIP_ID:
                return 0x180000;
+       case BRCM_CC_4365_CHIP_ID:
+       case BRCM_CC_4366_CHIP_ID:
+               return 0x200000;
        default:
                brcmf_err("unknown chip: %s\n", ci->pub.name);
                break;
@@ -674,10 +709,28 @@ static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
                        return -EINVAL;
                }
        } else {
-               mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_INTERNAL_MEM);
-               mem_core = container_of(mem, struct brcmf_core_priv, pub);
-               brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize,
-                                         &ci->pub.srsize);
+               mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_SYS_MEM);
+               if (mem) {
+                       mem_core = container_of(mem, struct brcmf_core_priv,
+                                               pub);
+                       ci->pub.ramsize = brcmf_chip_sysmem_ramsize(mem_core);
+                       ci->pub.rambase = brcmf_chip_tcm_rambase(ci);
+                       if (!ci->pub.rambase) {
+                               brcmf_err("RAM base not provided with ARM CA7 core\n");
+                               return -EINVAL;
+                       }
+               } else {
+                       mem = brcmf_chip_get_core(&ci->pub,
+                                                 BCMA_CORE_INTERNAL_MEM);
+                       if (!mem) {
+                               brcmf_err("No memory cores found\n");
+                               return -ENOMEM;
+                       }
+                       mem_core = container_of(mem, struct brcmf_core_priv,
+                                               pub);
+                       brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize,
+                                                 &ci->pub.srsize);
+               }
        }
        brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n",
                  ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize,
@@ -687,6 +740,12 @@ static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
                brcmf_err("RAM size is undetermined\n");
                return -ENOMEM;
        }
+
+       if (ci->pub.ramsize > BRCMF_CHIP_MAX_MEMSIZE) {
+               brcmf_err("RAM size is incorrect\n");
+               return -ENOMEM;
+       }
+
        return 0;
 }
 
@@ -899,13 +958,22 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci)
 
        /* assure chip is passive for core access */
        brcmf_chip_set_passive(&ci->pub);
+
+       /* Call bus specific reset function now. Cores have been determined
+        * but further access may require a chip specific reset at this point.
+        */
+       if (ci->ops->reset) {
+               ci->ops->reset(ci->ctx, &ci->pub);
+               brcmf_chip_set_passive(&ci->pub);
+       }
+
        return brcmf_chip_get_raminfo(ci);
 }
 
 static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id)
 {
        struct brcmf_core *core;
-       struct brcmf_core_priv *cr4;
+       struct brcmf_core_priv *cpu;
        u32 val;
 
 
@@ -918,10 +986,11 @@ static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id)
                brcmf_chip_coredisable(core, 0, 0);
                break;
        case BCMA_CORE_ARM_CR4:
-               cr4 = container_of(core, struct brcmf_core_priv, pub);
+       case BCMA_CORE_ARM_CA7:
+               cpu = container_of(core, struct brcmf_core_priv, pub);
 
                /* clear all IOCTL bits except HALT bit */
-               val = chip->ops->read32(chip->ctx, cr4->wrapbase + BCMA_IOCTL);
+               val = chip->ops->read32(chip->ctx, cpu->wrapbase + BCMA_IOCTL);
                val &= ARMCR4_BCMA_IOCTL_CPUHALT;
                brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT,
                                     ARMCR4_BCMA_IOCTL_CPUHALT);
@@ -1143,6 +1212,33 @@ static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec)
        return true;
 }
 
+static inline void
+brcmf_chip_ca7_set_passive(struct brcmf_chip_priv *chip)
+{
+       struct brcmf_core *core;
+
+       brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CA7);
+
+       core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
+       brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
+                                  D11_BCMA_IOCTL_PHYCLOCKEN,
+                            D11_BCMA_IOCTL_PHYCLOCKEN,
+                            D11_BCMA_IOCTL_PHYCLOCKEN);
+}
+
+static bool brcmf_chip_ca7_set_active(struct brcmf_chip_priv *chip, u32 rstvec)
+{
+       struct brcmf_core *core;
+
+       chip->ops->activate(chip->ctx, &chip->pub, rstvec);
+
+       /* restore ARM */
+       core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CA7);
+       brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0);
+
+       return true;
+}
+
 void brcmf_chip_set_passive(struct brcmf_chip *pub)
 {
        struct brcmf_chip_priv *chip;
@@ -1156,8 +1252,16 @@ void brcmf_chip_set_passive(struct brcmf_chip *pub)
                brcmf_chip_cr4_set_passive(chip);
                return;
        }
-
-       brcmf_chip_cm3_set_passive(chip);
+       arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7);
+       if (arm) {
+               brcmf_chip_ca7_set_passive(chip);
+               return;
+       }
+       arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3);
+       if (arm) {
+               brcmf_chip_cm3_set_passive(chip);
+               return;
+       }
 }
 
 bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec)
@@ -1171,8 +1275,14 @@ bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec)
        arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
        if (arm)
                return brcmf_chip_cr4_set_active(chip, rstvec);
+       arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7);
+       if (arm)
+               return brcmf_chip_ca7_set_active(chip, rstvec);
+       arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3);
+       if (arm)
+               return brcmf_chip_cm3_set_active(chip);
 
-       return brcmf_chip_cm3_set_active(chip);
+       return false;
 }
 
 bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
index 60dcb38fc77a3f59e1c2efcaefd3d337470d3282..f6b5feea23d20508abf4fc0e6670adf51b966d33 100644 (file)
@@ -73,6 +73,7 @@ struct brcmf_buscore_ops {
        u32 (*read32)(void *ctx, u32 addr);
        void (*write32)(void *ctx, u32 addr, u32 value);
        int (*prepare)(void *ctx);
+       int (*reset)(void *ctx, struct brcmf_chip *chip);
        int (*setup)(void *ctx, struct brcmf_chip *chip);
        void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec);
 };
index 0d39d80cee28792d99918a081a000e80ee1e0db1..21c7488b47329735a489647b306ede9240e2d0c9 100644 (file)
@@ -17,4 +17,7 @@
 
 extern const u8 ALLFFMAC[ETH_ALEN];
 
+/* Sets dongle media info (drv_version, mac address). */
+int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
+
 #endif /* BRCMFMAC_COMMON_H */
index fe9d3fbf5fe21e0c74c5852254c2682f3725ff18..b5ab98ee14455aea9774814128faa8b0177a0071 100644 (file)
@@ -33,6 +33,7 @@
 #include "feature.h"
 #include "proto.h"
 #include "pcie.h"
+#include "common.h"
 
 MODULE_AUTHOR("Broadcom Corporation");
 MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
@@ -53,6 +54,8 @@ MODULE_LICENSE("Dual BSD/GPL");
 #define BRCMF_RXREORDER_EXPIDX_VALID           0x08
 #define BRCMF_RXREORDER_NEW_HOLE               0x10
 
+#define BRCMF_BSSIDX_INVALID                   -1
+
 /* Error bits */
 int brcmf_msg_level;
 module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR);
@@ -60,10 +63,8 @@ MODULE_PARM_DESC(debug, "level of debug output");
 
 /* P2P0 enable */
 static int brcmf_p2p_enable;
-#ifdef CONFIG_BRCMDBG
 module_param_named(p2pon, brcmf_p2p_enable, int, 0);
-MODULE_PARM_DESC(p2pon, "enable p2p management functionality");
-#endif
+MODULE_PARM_DESC(p2pon, "enable legacy p2p management functionality");
 
 char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx)
 {
@@ -83,6 +84,24 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx)
        return "<if_none>";
 }
 
+struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
+{
+       struct brcmf_if *ifp;
+       s32 bssidx;
+
+       if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
+               brcmf_err("ifidx %d out of range\n", ifidx);
+               return NULL;
+       }
+
+       ifp = NULL;
+       bssidx = drvr->if2bss[ifidx];
+       if (bssidx >= 0)
+               ifp = drvr->iflist[bssidx];
+
+       return ifp;
+}
+
 static void _brcmf_set_multicast_list(struct work_struct *work)
 {
        struct brcmf_if *ifp;
@@ -520,17 +539,15 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb)
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_pub *drvr = bus_if->drvr;
        struct brcmf_skb_reorder_data *rd;
-       u8 ifidx;
        int ret;
 
        brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
 
        /* process and remove protocol-specific header */
-       ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb);
-       ifp = drvr->iflist[ifidx];
+       ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp);
 
        if (ret || !ifp || !ifp->ndev) {
-               if ((ret != -ENODATA) && ifp)
+               if (ret != -ENODATA && ifp)
                        ifp->stats.rx_errors++;
                brcmu_pkt_buf_free_skb(skb);
                return;
@@ -543,17 +560,11 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb)
                brcmf_netif_rx(ifp, skb);
 }
 
-void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
-                     bool success)
+void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
 {
-       struct brcmf_if *ifp;
        struct ethhdr *eh;
        u16 type;
 
-       ifp = drvr->iflist[ifidx];
-       if (!ifp)
-               goto done;
-
        eh = (struct ethhdr *)(txp->data);
        type = ntohs(eh->h_proto);
 
@@ -565,7 +576,7 @@ void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
 
        if (!success)
                ifp->stats.tx_errors++;
-done:
+
        brcmu_pkt_buf_free_skb(txp);
 }
 
@@ -573,17 +584,17 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_pub *drvr = bus_if->drvr;
-       u8 ifidx;
+       struct brcmf_if *ifp;
 
        /* await txstatus signal for firmware if active */
        if (brcmf_fws_fc_active(drvr->fws)) {
                if (!success)
                        brcmf_fws_bustxfail(drvr->fws, txp);
        } else {
-               if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp))
+               if (brcmf_proto_hdrpull(drvr, false, txp, &ifp))
                        brcmu_pkt_buf_free_skb(txp);
                else
-                       brcmf_txfinalize(drvr, txp, ifidx, success);
+                       brcmf_txfinalize(ifp, txp, success);
        }
 }
 
@@ -624,8 +635,7 @@ static int brcmf_netdev_stop(struct net_device *ndev)
 
        brcmf_cfg80211_down(ndev);
 
-       /* Set state and stop OS transmissions */
-       netif_stop_queue(ndev);
+       brcmf_net_setcarrier(ifp, false);
 
        return 0;
 }
@@ -659,8 +669,8 @@ static int brcmf_netdev_open(struct net_device *ndev)
                return -EIO;
        }
 
-       /* Allow transmit calls */
-       netif_start_queue(ndev);
+       /* Clear, carrier, set when connected or AP mode. */
+       netif_carrier_off(ndev);
        return 0;
 }
 
@@ -708,8 +718,6 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
        }
 
        brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
-
-       ndev->destructor = brcmf_cfg80211_free_netdev;
        return 0;
 
 fail:
@@ -719,6 +727,32 @@ fail:
        return -EBADE;
 }
 
+static void brcmf_net_detach(struct net_device *ndev)
+{
+       if (ndev->reg_state == NETREG_REGISTERED)
+               unregister_netdev(ndev);
+       else
+               brcmf_cfg80211_free_netdev(ndev);
+}
+
+void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on)
+{
+       struct net_device *ndev;
+
+       brcmf_dbg(TRACE, "Enter, idx=%d carrier=%d\n", ifp->bssidx, on);
+
+       ndev = ifp->ndev;
+       brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_DISCONNECTED, !on);
+       if (on) {
+               if (!netif_carrier_ok(ndev))
+                       netif_carrier_on(ndev);
+
+       } else {
+               if (netif_carrier_ok(ndev))
+                       netif_carrier_off(ndev);
+       }
+}
+
 static int brcmf_net_p2p_open(struct net_device *ndev)
 {
        brcmf_dbg(TRACE, "Enter\n");
@@ -778,7 +812,7 @@ fail:
 }
 
 struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
-                             char *name, u8 *mac_addr)
+                             bool is_p2pdev, char *name, u8 *mac_addr)
 {
        struct brcmf_if *ifp;
        struct net_device *ndev;
@@ -795,8 +829,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
                          ifp->ndev->name);
                if (ifidx) {
                        netif_stop_queue(ifp->ndev);
-                       unregister_netdev(ifp->ndev);
-                       free_netdev(ifp->ndev);
+                       brcmf_net_detach(ifp->ndev);
                        drvr->iflist[bssidx] = NULL;
                } else {
                        brcmf_err("ignore IF event\n");
@@ -804,7 +837,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
                }
        }
 
-       if (!brcmf_p2p_enable && bssidx == 1) {
+       if (!brcmf_p2p_enable && is_p2pdev) {
                /* this is P2P_DEVICE interface */
                brcmf_dbg(INFO, "allocate non-netdev interface\n");
                ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
@@ -813,13 +846,17 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
        } else {
                brcmf_dbg(INFO, "allocate netdev interface\n");
                /* Allocate netdev, including space for private structure */
-               ndev = alloc_netdev(sizeof(*ifp), name, NET_NAME_UNKNOWN,
-                                   ether_setup);
+               ndev = alloc_netdev(sizeof(*ifp), is_p2pdev ? "p2p%d" : name,
+                                   NET_NAME_UNKNOWN, ether_setup);
                if (!ndev)
                        return ERR_PTR(-ENOMEM);
 
+               ndev->destructor = brcmf_cfg80211_free_netdev;
                ifp = netdev_priv(ndev);
                ifp->ndev = ndev;
+               /* store mapping ifidx to bssidx */
+               if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID)
+                       drvr->if2bss[ifidx] = bssidx;
        }
 
        ifp->drvr = drvr;
@@ -850,6 +887,8 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
                return;
        }
        brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx);
+       if (drvr->if2bss[ifp->ifidx] == bssidx)
+               drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID;
        if (ifp->ndev) {
                if (bssidx == 0) {
                        if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
@@ -865,17 +904,28 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
                        cancel_work_sync(&ifp->setmacaddr_work);
                        cancel_work_sync(&ifp->multicast_work);
                }
-               /* unregister will take care of freeing it */
-               unregister_netdev(ifp->ndev);
+               brcmf_net_detach(ifp->ndev);
+       } else {
+               /* Only p2p device interfaces which get dynamically created
+                * end up here. In this case the p2p module should be informed
+                * about the removal of the interface within the firmware. If
+                * not then p2p commands towards the firmware will cause some
+                * serious troublesome side effects. The p2p module will clean
+                * up the ifp if needed.
+                */
+               brcmf_p2p_ifp_removed(ifp);
+               kfree(ifp);
        }
 }
 
-void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx)
+void brcmf_remove_interface(struct brcmf_if *ifp)
 {
-       if (drvr->iflist[bssidx]) {
-               brcmf_fws_del_interface(drvr->iflist[bssidx]);
-               brcmf_del_if(drvr, bssidx);
-       }
+       if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp))
+               return;
+       brcmf_dbg(TRACE, "Enter, bssidx=%d, ifidx=%d\n", ifp->bssidx,
+                 ifp->ifidx);
+       brcmf_fws_del_interface(ifp);
+       brcmf_del_if(ifp->drvr, ifp->bssidx);
 }
 
 int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
@@ -906,6 +956,7 @@ int brcmf_attach(struct device *dev)
 {
        struct brcmf_pub *drvr = NULL;
        int ret = 0;
+       int i;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -914,6 +965,9 @@ int brcmf_attach(struct device *dev)
        if (!drvr)
                return -ENOMEM;
 
+       for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++)
+               drvr->if2bss[i] = BRCMF_BSSIDX_INVALID;
+
        mutex_init(&drvr->proto_block);
 
        /* Link to bus module */
@@ -921,8 +975,8 @@ int brcmf_attach(struct device *dev)
        drvr->bus_if = dev_get_drvdata(dev);
        drvr->bus_if->drvr = drvr;
 
-       /* create device debugfs folder */
-       brcmf_debugfs_attach(drvr);
+       /* attach debug facilities */
+       brcmf_debug_attach(drvr);
 
        /* Attach and link in the protocol */
        ret = brcmf_proto_attach(drvr);
@@ -981,16 +1035,11 @@ int brcmf_bus_start(struct device *dev)
        brcmf_dbg(TRACE, "\n");
 
        /* add primary networking interface */
-       ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL);
+       ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL);
        if (IS_ERR(ifp))
                return PTR_ERR(ifp);
 
-       if (brcmf_p2p_enable)
-               p2p_ifp = brcmf_add_if(drvr, 1, 0, "p2p%d", NULL);
-       else
-               p2p_ifp = NULL;
-       if (IS_ERR(p2p_ifp))
-               p2p_ifp = NULL;
+       p2p_ifp = NULL;
 
        /* signal bus ready */
        brcmf_bus_change_state(bus_if, BRCMF_BUS_UP);
@@ -1017,39 +1066,37 @@ int brcmf_bus_start(struct device *dev)
 
        brcmf_fws_add_interface(ifp);
 
-       drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev);
+       drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev,
+                                            brcmf_p2p_enable);
        if (drvr->config == NULL) {
                ret = -ENOMEM;
                goto fail;
        }
 
-       ret = brcmf_fweh_activate_events(ifp);
-       if (ret < 0)
-               goto fail;
-
        ret = brcmf_net_attach(ifp, false);
+
+       if ((!ret) && (brcmf_p2p_enable)) {
+               p2p_ifp = drvr->iflist[1];
+               if (p2p_ifp)
+                       ret = brcmf_net_p2p_attach(p2p_ifp);
+       }
 fail:
        if (ret < 0) {
                brcmf_err("failed: %d\n", ret);
-               brcmf_cfg80211_detach(drvr->config);
+               if (drvr->config) {
+                       brcmf_cfg80211_detach(drvr->config);
+                       drvr->config = NULL;
+               }
                if (drvr->fws) {
                        brcmf_fws_del_interface(ifp);
                        brcmf_fws_deinit(drvr);
                }
-               if (drvr->iflist[0]) {
-                       free_netdev(ifp->ndev);
-                       drvr->iflist[0] = NULL;
-               }
-               if (p2p_ifp) {
-                       free_netdev(p2p_ifp->ndev);
-                       drvr->iflist[1] = NULL;
-               }
+               if (ifp)
+                       brcmf_net_detach(ifp->ndev);
+               if (p2p_ifp)
+                       brcmf_net_detach(p2p_ifp->ndev);
                return ret;
        }
-       if ((brcmf_p2p_enable) && (p2p_ifp))
-               if (brcmf_net_p2p_attach(p2p_ifp) < 0)
-                       brcmf_p2p_enable = 0;
-
        return 0;
 }
 
@@ -1105,7 +1152,7 @@ void brcmf_detach(struct device *dev)
 
        /* make sure primary interface removed last */
        for (i = BRCMF_MAX_IFS-1; i > -1; i--)
-               brcmf_remove_interface(drvr, i);
+               brcmf_remove_interface(drvr->iflist[i]);
 
        brcmf_cfg80211_detach(drvr->config);
 
@@ -1115,7 +1162,7 @@ void brcmf_detach(struct device *dev)
 
        brcmf_proto_detach(drvr);
 
-       brcmf_debugfs_detach(drvr);
+       brcmf_debug_detach(drvr);
        bus_if->drvr = NULL;
        kfree(drvr);
 }
index 746304121cdbf99bf02845462689737c21272c02..2f9101b2ad343e29dfd4689e3bb2e937a39426b6 100644 (file)
@@ -122,6 +122,7 @@ struct brcmf_pub {
        struct mac_address addresses[BRCMF_MAX_IFS];
 
        struct brcmf_if *iflist[BRCMF_MAX_IFS];
+       s32 if2bss[BRCMF_MAX_IFS];
 
        struct mutex proto_block;
        unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
@@ -153,10 +154,13 @@ struct brcmf_fws_mac_descriptor;
  *     netif stopped due to firmware signalling flow control.
  * @BRCMF_NETIF_STOP_REASON_FLOW:
  *     netif stopped due to flowring full.
+ * @BRCMF_NETIF_STOP_REASON_DISCONNECTED:
+ *     netif stopped due to not being connected (STA mode).
  */
 enum brcmf_netif_stop_reason {
-       BRCMF_NETIF_STOP_REASON_FWS_FC = 1,
-       BRCMF_NETIF_STOP_REASON_FLOW = 2
+       BRCMF_NETIF_STOP_REASON_FWS_FC = BIT(0),
+       BRCMF_NETIF_STOP_REASON_FLOW = BIT(1),
+       BRCMF_NETIF_STOP_REASON_DISCONNECTED = BIT(2)
 };
 
 /**
@@ -202,19 +206,16 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
 
 /* Return pointer to interface name */
 char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
-
+struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
 int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
 struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
-                             char *name, u8 *mac_addr);
-void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx);
+                             bool is_p2pdev, char *name, u8 *mac_addr);
+void brcmf_remove_interface(struct brcmf_if *ifp);
 int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
 void brcmf_txflowblock_if(struct brcmf_if *ifp,
                          enum brcmf_netif_stop_reason reason, bool state);
-void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
-                     bool success);
+void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
 void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
-
-/* Sets dongle media info (drv_version, mac address). */
-int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
+void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
 
 #endif /* BRCMFMAC_CORE_H */
index 2d6d0055385877087a467dfa1ab265538fdb7102..1299dccc78b4da22293677ec4cf0a1f763af2a19 100644 (file)
 #include <linux/debugfs.h>
 #include <linux/netdevice.h>
 #include <linux/module.h>
+#include <linux/devcoredump.h>
 
 #include <brcmu_wifi.h>
 #include <brcmu_utils.h>
 #include "core.h"
 #include "bus.h"
+#include "fweh.h"
 #include "debug.h"
 
 static struct dentry *root_folder;
 
+static int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
+                                     size_t len)
+{
+       void *dump;
+       size_t ramsize;
+
+       ramsize = brcmf_bus_get_ramsize(bus);
+       if (ramsize) {
+               dump = vzalloc(len + ramsize);
+               if (!dump)
+                       return -ENOMEM;
+               memcpy(dump, data, len);
+               brcmf_bus_get_memdump(bus, dump + len, ramsize);
+               dev_coredumpv(bus->dev, dump, len + ramsize, GFP_KERNEL);
+       }
+       return 0;
+}
+
+static int brcmf_debug_psm_watchdog_notify(struct brcmf_if *ifp,
+                                          const struct brcmf_event_msg *evtmsg,
+                                          void *data)
+{
+       brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
+
+       return brcmf_debug_create_memdump(ifp->drvr->bus_if, data,
+                                         evtmsg->datalen);
+}
+
 void brcmf_debugfs_init(void)
 {
        root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL);
@@ -41,7 +71,7 @@ void brcmf_debugfs_exit(void)
        root_folder = NULL;
 }
 
-int brcmf_debugfs_attach(struct brcmf_pub *drvr)
+int brcmf_debug_attach(struct brcmf_pub *drvr)
 {
        struct device *dev = drvr->bus_if->dev;
 
@@ -49,12 +79,18 @@ int brcmf_debugfs_attach(struct brcmf_pub *drvr)
                return -ENODEV;
 
        drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
+       if (IS_ERR(drvr->dbgfs_dir))
+               return PTR_ERR(drvr->dbgfs_dir);
 
-       return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
+
+       return brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
+                                  brcmf_debug_psm_watchdog_notify);
 }
 
-void brcmf_debugfs_detach(struct brcmf_pub *drvr)
+void brcmf_debug_detach(struct brcmf_pub *drvr)
 {
+       brcmf_fweh_unregister(drvr, BRCMF_E_PSM_WATCHDOG);
+
        if (!IS_ERR_OR_NULL(drvr->dbgfs_dir))
                debugfs_remove_recursive(drvr->dbgfs_dir);
 }
index eb0b8c47479d1f7acaadd1ca2f129e2fa07dbe2c..d0d9676f7f9de952692f40523e97409c104a8e91 100644 (file)
@@ -37,6 +37,7 @@
 #define BRCMF_SDIO_VAL         0x00020000
 #define BRCMF_MSGBUF_VAL       0x00040000
 #define BRCMF_PCIE_VAL         0x00080000
+#define BRCMF_FWCON_VAL                0x00100000
 
 /* set default print format */
 #undef pr_fmt
@@ -78,6 +79,7 @@ do {                                                          \
 #define BRCMF_GLOM_ON()                (brcmf_msg_level & BRCMF_GLOM_VAL)
 #define BRCMF_EVENT_ON()       (brcmf_msg_level & BRCMF_EVENT_VAL)
 #define BRCMF_FIL_ON()         (brcmf_msg_level & BRCMF_FIL_VAL)
+#define BRCMF_FWCON_ON()       (brcmf_msg_level & BRCMF_FWCON_VAL)
 
 #else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */
 
@@ -90,6 +92,7 @@ do {                                                          \
 #define BRCMF_GLOM_ON()                0
 #define BRCMF_EVENT_ON()       0
 #define BRCMF_FIL_ON()         0
+#define BRCMF_FWCON_ON()       0
 
 #endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */
 
@@ -106,8 +109,8 @@ struct brcmf_pub;
 #ifdef DEBUG
 void brcmf_debugfs_init(void);
 void brcmf_debugfs_exit(void);
-int brcmf_debugfs_attach(struct brcmf_pub *drvr);
-void brcmf_debugfs_detach(struct brcmf_pub *drvr);
+int brcmf_debug_attach(struct brcmf_pub *drvr);
+void brcmf_debug_detach(struct brcmf_pub *drvr);
 struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
 int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
                            int (*read_fn)(struct seq_file *seq, void *data));
@@ -118,11 +121,11 @@ static inline void brcmf_debugfs_init(void)
 static inline void brcmf_debugfs_exit(void)
 {
 }
-static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr)
+static inline int brcmf_debug_attach(struct brcmf_pub *drvr)
 {
        return 0;
 }
-static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr)
+static inline void brcmf_debug_detach(struct brcmf_pub *drvr)
 {
 }
 static inline
index 1e94e94e01dc91c829e45f2efd16559e494e086f..44bb3063669038fbfe0fee1b96a0cde929fae1e2 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <linux/netdevice.h>
+#include <linux/module.h>
 
 #include <brcm_hw_ids.h>
 #include "core.h"
 #include "fwil.h"
 #include "feature.h"
 
+
+/* Module param feature_disable (global for all devices) */
+static int brcmf_feature_disable;
+module_param_named(feature_disable, brcmf_feature_disable, int, 0);
+MODULE_PARM_DESC(feature_disable, "Disable features");
+
 /*
  * expand feature list to array of feature strings.
  */
@@ -121,7 +128,7 @@ static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp,
 
 void brcmf_feat_attach(struct brcmf_pub *drvr)
 {
-       struct brcmf_if *ifp = drvr->iflist[0];
+       struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
 
        brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
        brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
@@ -131,6 +138,12 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
                brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
        brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p");
 
+       if (brcmf_feature_disable) {
+               brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n",
+                         ifp->drvr->feat_flags, brcmf_feature_disable);
+               ifp->drvr->feat_flags &= ~brcmf_feature_disable;
+       }
+
        /* set chip related quirks */
        switch (drvr->bus_if->chip) {
        case BRCM_CC_43236_CHIP_ID:
index 971920f77b68eb1bcc02043d7a19f14c5a9e68b2..4248f3c80e78e10016805cf4caed296b801935e2 100644 (file)
@@ -29,7 +29,7 @@
 #define BRCMF_FW_NVRAM_PCIEDEV_LEN             10      /* pcie/1/4/ + \0 */
 
 char brcmf_firmware_path[BRCMF_FW_PATH_LEN];
-module_param_string(firmware_path, brcmf_firmware_path,
+module_param_string(alternative_fw_path, brcmf_firmware_path,
                    BRCMF_FW_PATH_LEN, 0440);
 
 enum nvram_parser_state {
index 8d1ab4ab5be809ca8fab1a4e37a31f1af8538391..2ca783fa50cfc71b5556ddf731186af4704a3e63 100644 (file)
@@ -221,7 +221,7 @@ static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid,
 
        bus_if = dev_get_drvdata(flow->dev);
        drvr = bus_if->drvr;
-       ifp = drvr->iflist[ifidx];
+       ifp = brcmf_get_ifp(drvr, ifidx);
        brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked);
 
        spin_unlock_irqrestore(&flow->block_lock, flags);
index 5551861a44bc427bca80d096f7809db6c2f12b42..95fd1c9675d172467a2f6c2c53fea0e5de2bbb06 100644 (file)
@@ -34,7 +34,7 @@ enum ring_status {
 };
 
 struct brcmf_flowring_ring {
-       u8 hash_id;
+       u16 hash_id;
        bool blocked;
        enum ring_status status;
        struct sk_buff_head skblist;
index ec62492ffa69fd3fefd6481b2ec2e152167b0e36..3878b6f6cfce4ffeb6b583059d1c3b01f0e29c7e 100644 (file)
@@ -179,25 +179,28 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
 {
        struct brcmf_if_event *ifevent = data;
        struct brcmf_if *ifp;
+       bool is_p2pdev;
        int err = 0;
 
        brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n",
                  ifevent->action, ifevent->ifidx, ifevent->bssidx,
                  ifevent->flags, ifevent->role);
 
-       /* The P2P Device interface event must not be ignored
-        * contrary to what firmware tells us. The only way to
-        * distinguish the P2P Device is by looking at the ifidx
-        * and bssidx received.
+       /* The P2P Device interface event must not be ignored contrary to what
+        * firmware tells us. Older firmware uses p2p noif, with sta role.
+        * This should be accepted when p2pdev_setup is ongoing. TDLS setup will
+        * use the same ifevent and should be ignored.
         */
-       if (!(ifevent->ifidx == 0 && ifevent->bssidx == 1) &&
-           (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
+       is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
+                    (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT ||
+                     ((ifevent->role == BRCMF_E_IF_ROLE_STA) &&
+                      (drvr->fweh.p2pdev_setup_ongoing))));
+       if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
                brcmf_dbg(EVENT, "event can be ignored\n");
                return;
        }
        if (ifevent->ifidx >= BRCMF_MAX_IFS) {
-               brcmf_err("invalid interface index: %u\n",
-                         ifevent->ifidx);
+               brcmf_err("invalid interface index: %u\n", ifevent->ifidx);
                return;
        }
 
@@ -207,10 +210,11 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
                brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
                          emsg->addr);
                ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx,
-                                  emsg->ifname, emsg->addr);
+                                  is_p2pdev, emsg->ifname, emsg->addr);
                if (IS_ERR(ifp))
                        return;
-               brcmf_fws_add_interface(ifp);
+               if (!is_p2pdev)
+                       brcmf_fws_add_interface(ifp);
                if (!drvr->fweh.evt_handler[BRCMF_E_IF])
                        if (brcmf_net_attach(ifp, false) < 0)
                                return;
@@ -222,7 +226,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
        err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
 
        if (ifp && ifevent->action == BRCMF_E_IF_DEL)
-               brcmf_remove_interface(drvr, ifevent->bssidx);
+               brcmf_remove_interface(ifp);
 }
 
 /**
@@ -297,8 +301,7 @@ static void brcmf_fweh_event_worker(struct work_struct *work)
                        goto event_free;
                }
 
-               if ((event->code == BRCMF_E_TDLS_PEER_EVENT) &&
-                   (emsg.bsscfgidx == 1))
+               if (event->code == BRCMF_E_TDLS_PEER_EVENT)
                        ifp = drvr->iflist[0];
                else
                        ifp = drvr->iflist[emsg.bsscfgidx];
@@ -314,6 +317,17 @@ event_free:
        }
 }
 
+/**
+ * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not).
+ *
+ * @ifp: ifp on which setup is taking place or finished.
+ * @ongoing: p2p device setup in progress (or not).
+ */
+void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
+{
+       ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing;
+}
+
 /**
  * brcmf_fweh_attach() - initialize firmware event handling.
  *
@@ -335,7 +349,7 @@ void brcmf_fweh_attach(struct brcmf_pub *drvr)
 void brcmf_fweh_detach(struct brcmf_pub *drvr)
 {
        struct brcmf_fweh_info *fweh = &drvr->fweh;
-       struct brcmf_if *ifp = drvr->iflist[0];
+       struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
        s8 eventmask[BRCMF_EVENTING_MASK_LEN];
 
        if (ifp) {
index 1326898d608ee8e96067d3c9c3e118bdbcab0314..d9a9428423825fe7a65279b244dbec29739258dc 100644 (file)
@@ -230,12 +230,14 @@ typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp,
 /**
  * struct brcmf_fweh_info - firmware event handling information.
  *
+ * @p2pdev_setup_ongoing: P2P device creation in progress.
  * @event_work: event worker.
  * @evt_q_lock: lock for event queue protection.
  * @event_q: event queue.
  * @evt_handler: registered event handlers.
  */
 struct brcmf_fweh_info {
+       bool p2pdev_setup_ongoing;
        struct work_struct event_work;
        spinlock_t evt_q_lock;
        struct list_head event_q;
@@ -255,6 +257,7 @@ void brcmf_fweh_unregister(struct brcmf_pub *drvr,
 int brcmf_fweh_activate_events(struct brcmf_if *ifp);
 void brcmf_fweh_process_event(struct brcmf_pub *drvr,
                              struct brcmf_event *event_packet);
+void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing);
 
 static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr,
                                          struct sk_buff *skb)
index 5434dcf64f7d8012206975ed743fe96145b924fb..b20fc0f82a4828a5111c8b4061af2928f56fc595 100644 (file)
@@ -72,6 +72,7 @@
 #define BRCMF_C_GET_BSS_INFO                   136
 #define BRCMF_C_GET_BANDLIST                   140
 #define BRCMF_C_SET_SCB_TIMEOUT                        158
+#define BRCMF_C_GET_ASSOCLIST                  159
 #define BRCMF_C_GET_PHYLIST                    180
 #define BRCMF_C_SET_SCAN_CHANNEL_TIME          185
 #define BRCMF_C_SET_SCAN_UNASSOC_TIME          187
index 297911f38fa0458e27ac1cfdd589dc01c4ef93b9..daa427b46712e8243be5365fca928183e2745fe3 100644 (file)
 #define BRCMF_COUNTRY_BUF_SZ           4
 #define BRCMF_ANT_MAX                  4
 
+#define BRCMF_MAX_ASSOCLIST            128
+
 /* join preference types for join_pref iovar */
 enum brcmf_join_pref_types {
        BRCMF_JOIN_PREF_RSSI = 1,
@@ -621,4 +623,15 @@ struct brcmf_rev_info_le {
        __le32 nvramrev;
 };
 
+/**
+ * struct brcmf_assoclist_le - request assoc list.
+ *
+ * @count: indicates number of stations.
+ * @mac: MAC addresses of stations.
+ */
+struct brcmf_assoclist_le {
+       __le32 count;
+       u8 mac[BRCMF_MAX_ASSOCLIST][ETH_ALEN];
+};
+
 #endif /* FWIL_TYPES_H_ */
index 5017eaa4af45fdd8f9dfd1034919c5042cce7cd8..086cac3f86d64bf41840f61e4c770dcb035363a5 100644 (file)
@@ -972,7 +972,7 @@ static void
 brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
                             u8 if_id)
 {
-       struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1];
+       struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);
 
        if (WARN_ON(!ifp))
                return;
@@ -1398,7 +1398,7 @@ done:
 }
 
 static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
-                                        struct sk_buff *skb, u8 ifidx,
+                                        struct sk_buff *skb,
                                         u32 genbit, u16 seq)
 {
        struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
@@ -1448,7 +1448,7 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
        struct sk_buff *skb;
        struct brcmf_skbuff_cb *skcb;
        struct brcmf_fws_mac_descriptor *entry = NULL;
-       u8 ifidx;
+       struct brcmf_if *ifp;
 
        brcmf_dbg(DATA, "flags %d\n", flags);
 
@@ -1497,15 +1497,16 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
        }
        brcmf_fws_macdesc_return_req_credit(skb);
 
-       if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) {
+       ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
+       if (ret) {
                brcmu_pkt_buf_free_skb(skb);
                return -EINVAL;
        }
        if (!remove_from_hanger)
-               ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx,
+               ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
                                                    genbit, seq);
        if (remove_from_hanger || ret)
-               brcmf_txfinalize(fws->drvr, skb, ifidx, true);
+               brcmf_txfinalize(ifp, skb, true);
 
        return 0;
 }
@@ -1615,11 +1616,10 @@ static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
        return 0;
 }
 
-int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
-                     struct sk_buff *skb)
+void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
 {
        struct brcmf_skb_reorder_data *rd;
-       struct brcmf_fws_info *fws = drvr->fws;
+       struct brcmf_fws_info *fws = ifp->drvr->fws;
        u8 *signal_data;
        s16 data_len;
        u8 type;
@@ -1629,20 +1629,20 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
        s32 err;
 
        brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
-                 ifidx, skb->len, signal_len);
+                 ifp->ifidx, skb->len, siglen);
 
-       WARN_ON(signal_len > skb->len);
+       WARN_ON(siglen > skb->len);
 
-       if (!signal_len)
-               return 0;
+       if (!siglen)
+               return;
        /* if flow control disabled, skip to packet data and leave */
        if ((!fws) || (!fws->fw_signals)) {
-               skb_pull(skb, signal_len);
-               return 0;
+               skb_pull(skb, siglen);
+               return;
        }
 
        fws->stats.header_pulls++;
-       data_len = signal_len;
+       data_len = siglen;
        signal_data = skb->data;
 
        status = BRCMF_FWS_RET_OK_NOSCHEDULE;
@@ -1730,14 +1730,12 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
        /* signalling processing result does
         * not affect the actual ethernet packet.
         */
-       skb_pull(skb, signal_len);
+       skb_pull(skb, siglen);
 
        /* this may be a signal-only packet
         */
        if (skb->len == 0)
                fws->stats.header_only_pkt++;
-
-       return 0;
 }
 
 static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
@@ -1848,7 +1846,7 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
                entry->transit_count--;
                if (entry->suppressed)
                        entry->suppr_transit_count--;
-               brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
+               (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);
                goto rollback;
        }
 
@@ -1904,7 +1902,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
        if (fws->avoid_queueing) {
                rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb);
                if (rc < 0)
-                       brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
+                       brcmf_txfinalize(ifp, skb, false);
                return rc;
        }
 
@@ -1928,7 +1926,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
                brcmf_fws_schedule_deq(fws);
        } else {
                brcmf_err("drop skb: no hanger slot\n");
-               brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
+               brcmf_txfinalize(ifp, skb, false);
                rc = -ENOMEM;
        }
        brcmf_fws_unlock(fws);
@@ -2008,8 +2006,9 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
                                ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
                                brcmf_fws_lock(fws);
                                if (ret < 0)
-                                       brcmf_txfinalize(drvr, skb, ifidx,
-                                                        false);
+                                       brcmf_txfinalize(brcmf_get_ifp(drvr,
+                                                                      ifidx),
+                                                        skb, false);
                                if (fws->bus_flow_blocked)
                                        break;
                        }
@@ -2117,6 +2116,7 @@ static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
 int brcmf_fws_init(struct brcmf_pub *drvr)
 {
        struct brcmf_fws_info *fws;
+       struct brcmf_if *ifp;
        u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
        int rc;
        u32 mode;
@@ -2176,21 +2176,22 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
         * continue. Set mode back to none indicating not enabled.
         */
        fws->fw_signals = true;
-       if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) {
+       ifp = brcmf_get_ifp(drvr, 0);
+       if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {
                brcmf_err("failed to set bdcv2 tlv signaling\n");
                fws->fcmode = BRCMF_FWS_FCMODE_NONE;
                fws->fw_signals = false;
        }
 
-       if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1))
+       if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))
                brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
 
        /* Enable seq number reuse, if supported */
-       if (brcmf_fil_iovar_int_get(drvr->iflist[0], "wlfc_mode", &mode) == 0) {
+       if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {
                if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {
                        mode = 0;
                        BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);
-                       if (brcmf_fil_iovar_int_set(drvr->iflist[0],
+                       if (brcmf_fil_iovar_int_set(ifp,
                                                    "wlfc_mode", mode) == 0) {
                                BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);
                        }
index 9fc860910bd8571abd9f42040b6918f9f7fb57e5..a36bac17eafdee9210b7a422740ac90d76298ef8 100644 (file)
@@ -21,8 +21,7 @@
 int brcmf_fws_init(struct brcmf_pub *drvr);
 void brcmf_fws_deinit(struct brcmf_pub *drvr);
 bool brcmf_fws_fc_active(struct brcmf_fws_info *fws);
-int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
-                     struct sk_buff *skb);
+void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb);
 int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb);
 
 void brcmf_fws_reset_interface(struct brcmf_if *ifp);
index 7b2136c9badb5ce984d776bb11aa317da6083f9e..44e618f9d89046b9350ce6fbc4eabb16f392458e 100644 (file)
@@ -522,7 +522,7 @@ static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx,
 
 
 static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws,
-                               u8 *ifidx, struct sk_buff *skb)
+                               struct sk_buff *skb, struct brcmf_if **ifp)
 {
        return -ENODEV;
 }
@@ -873,7 +873,8 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf)
        commonring = msgbuf->flowrings[flowid];
        atomic_dec(&commonring->outstanding_tx);
 
-       brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true);
+       brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx),
+                        skb, true);
 }
 
 
@@ -1081,15 +1082,7 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb,
 {
        struct brcmf_if *ifp;
 
-       /* The ifidx is the idx to map to matching netdev/ifp. When receiving
-        * events this is easy because it contains the bssidx which maps
-        * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
-        * bssidx 1 is used for p2p0 and no data can be received or
-        * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
-        */
-       if (ifidx)
-               (ifidx)++;
-       ifp = msgbuf->drvr->iflist[ifidx];
+       ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
        if (!ifp || !ifp->ndev) {
                brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
                brcmu_pkt_buf_free_skb(skb);
index a9ba775a24c1c1eb6c17f3717e76e994d849f325..d224b3dd72edea583d5a952913c61915fe60b253 100644 (file)
@@ -2084,11 +2084,13 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
        brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
 
        brcmf_cfg80211_arm_vif_event(p2p->cfg, p2p_vif);
+       brcmf_fweh_p2pdev_setup(pri_ifp, true);
 
        /* Initialize P2P Discovery in the firmware */
        err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
        if (err < 0) {
                brcmf_err("set p2p_disc error\n");
+               brcmf_fweh_p2pdev_setup(pri_ifp, false);
                brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
                goto fail;
        }
@@ -2097,6 +2099,7 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
        err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD,
                                                    msecs_to_jiffies(1500));
        brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
+       brcmf_fweh_p2pdev_setup(pri_ifp, false);
        if (!err) {
                brcmf_err("timeout occurred\n");
                err = -EIO;
@@ -2130,20 +2133,6 @@ fail:
        return ERR_PTR(err);
 }
 
-/**
- * brcmf_p2p_delete_p2pdev() - delete P2P_DEVICE virtual interface.
- *
- * @vif: virtual interface object to delete.
- */
-static void brcmf_p2p_delete_p2pdev(struct brcmf_p2p_info *p2p,
-                                   struct brcmf_cfg80211_vif *vif)
-{
-       cfg80211_unregister_wdev(&vif->wdev);
-       p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
-       brcmf_remove_interface(vif->ifp->drvr, vif->ifp->bssidx);
-       brcmf_free_vif(vif);
-}
-
 /**
  * brcmf_p2p_add_vif() - create a new P2P virtual interface.
  *
@@ -2255,6 +2244,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
        brcmf_dbg(TRACE, "delete P2P vif\n");
        vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
 
+       brcmf_cfg80211_arm_vif_event(cfg, vif);
        switch (vif->wdev.iftype) {
        case NL80211_IFTYPE_P2P_CLIENT:
                if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state))
@@ -2267,10 +2257,10 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
                break;
 
        case NL80211_IFTYPE_P2P_DEVICE:
+               if (!p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
+                       return 0;
                brcmf_p2p_cancel_remain_on_channel(vif->ifp);
                brcmf_p2p_deinit_discovery(p2p);
-               brcmf_p2p_delete_p2pdev(p2p, vif);
-               return 0;
        default:
                return -ENOTSUPP;
        }
@@ -2282,10 +2272,11 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
                wait_for_completion_timeout(&cfg->vif_disabled,
                                            msecs_to_jiffies(500));
 
-       brcmf_vif_clear_mgmt_ies(vif);
-
-       brcmf_cfg80211_arm_vif_event(cfg, vif);
-       err = brcmf_p2p_release_p2p_if(vif);
+       err = 0;
+       if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) {
+               brcmf_vif_clear_mgmt_ies(vif);
+               err = brcmf_p2p_release_p2p_if(vif);
+       }
        if (!err) {
                /* wait for firmware event */
                err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL,
@@ -2295,12 +2286,31 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
                else
                        err = 0;
        }
+       if (err)
+               brcmf_remove_interface(vif->ifp);
+
        brcmf_cfg80211_arm_vif_event(cfg, NULL);
-       p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL;
+       if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE)
+               p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL;
 
        return err;
 }
 
+void brcmf_p2p_ifp_removed(struct brcmf_if *ifp)
+{
+       struct brcmf_cfg80211_info *cfg;
+       struct brcmf_cfg80211_vif *vif;
+
+       brcmf_dbg(INFO, "P2P: device interface removed\n");
+       vif = ifp->vif;
+       cfg = wdev_to_cfg(&vif->wdev);
+       cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
+       rtnl_lock();
+       cfg80211_unregister_wdev(&vif->wdev);
+       rtnl_unlock();
+       brcmf_free_vif(vif);
+}
+
 int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev)
 {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
@@ -2324,87 +2334,49 @@ void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev)
        struct brcmf_cfg80211_vif *vif;
 
        vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
-       mutex_lock(&cfg->usr_sync);
-       (void)brcmf_p2p_deinit_discovery(p2p);
-       brcmf_abort_scanning(cfg);
-       clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
-       mutex_unlock(&cfg->usr_sync);
+       /* This call can be result of the unregister_wdev call. In that case
+        * we dont want to do anything anymore. Just return. The config vif
+        * will have been cleared at this point.
+        */
+       if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) {
+               mutex_lock(&cfg->usr_sync);
+               /* Set the discovery state to SCAN */
+               (void)brcmf_p2p_set_discover_state(vif->ifp,
+                                                  WL_P2P_DISC_ST_SCAN, 0, 0);
+               brcmf_abort_scanning(cfg);
+               clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
+               mutex_unlock(&cfg->usr_sync);
+       }
 }
 
 /**
  * brcmf_p2p_attach() - attach for P2P.
  *
  * @cfg: driver private data for cfg80211 interface.
+ * @p2pdev_forced: create p2p device interface at attach.
  */
-s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg)
+s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced)
 {
-       struct brcmf_if *pri_ifp;
-       struct brcmf_if *p2p_ifp;
-       struct brcmf_cfg80211_vif *p2p_vif;
        struct brcmf_p2p_info *p2p;
-       struct brcmf_pub *drvr;
-       s32 bssidx;
+       struct brcmf_if *pri_ifp;
        s32 err = 0;
+       void *err_ptr;
 
        p2p = &cfg->p2p;
        p2p->cfg = cfg;
 
-       drvr = cfg->pub;
-
-       pri_ifp = drvr->iflist[0];
-       p2p_ifp = drvr->iflist[1];
-
+       pri_ifp = brcmf_get_ifp(cfg->pub, 0);
        p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
 
-       if (p2p_ifp) {
-               p2p_vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_P2P_DEVICE,
-                                         false);
-               if (IS_ERR(p2p_vif)) {
-                       brcmf_err("could not create discovery vif\n");
-                       err = -ENOMEM;
-                       goto exit;
-               }
-
-               p2p_vif->ifp = p2p_ifp;
-               p2p_ifp->vif = p2p_vif;
-               p2p_vif->wdev.netdev = p2p_ifp->ndev;
-               p2p_ifp->ndev->ieee80211_ptr = &p2p_vif->wdev;
-               SET_NETDEV_DEV(p2p_ifp->ndev, wiphy_dev(cfg->wiphy));
-
-               p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
-
-               brcmf_p2p_generate_bss_mac(p2p, NULL);
-               memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
-               brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
-
-               /* Initialize P2P Discovery in the firmware */
-               err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
-               if (err < 0) {
-                       brcmf_err("set p2p_disc error\n");
-                       brcmf_free_vif(p2p_vif);
-                       goto exit;
-               }
-               /* obtain bsscfg index for P2P discovery */
-               err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx);
-               if (err < 0) {
-                       brcmf_err("retrieving discover bsscfg index failed\n");
-                       brcmf_free_vif(p2p_vif);
-                       goto exit;
-               }
-               /* Verify that firmware uses same bssidx as driver !! */
-               if (p2p_ifp->bssidx != bssidx) {
-                       brcmf_err("Incorrect bssidx=%d, compared to p2p_ifp->bssidx=%d\n",
-                                 bssidx, p2p_ifp->bssidx);
-                       brcmf_free_vif(p2p_vif);
-                       goto exit;
+       if (p2pdev_forced) {
+               err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL);
+               if (IS_ERR(err_ptr)) {
+                       brcmf_err("P2P device creation failed.\n");
+                       err = PTR_ERR(err_ptr);
                }
-
-               init_completion(&p2p->send_af_done);
-               INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
-               init_completion(&p2p->afx_hdl.act_frm_scan);
-               init_completion(&p2p->wait_next_af);
+       } else {
+               p2p->p2pdev_dynamically = true;
        }
-exit:
        return err;
 }
 
@@ -2421,10 +2393,7 @@ void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
        if (vif != NULL) {
                brcmf_p2p_cancel_remain_on_channel(vif->ifp);
                brcmf_p2p_deinit_discovery(p2p);
-               /* remove discovery interface */
-               rtnl_lock();
-               brcmf_p2p_delete_p2pdev(p2p, vif);
-               rtnl_unlock();
+               brcmf_remove_interface(vif->ifp);
        }
        /* just set it all to zero */
        memset(p2p, 0, sizeof(*p2p));
index 872f382d9e49b7531e05884d6f6b69e8fc760af0..5d49059021a9f0f58490dfd1ecab4808b480c1eb 100644 (file)
@@ -124,6 +124,7 @@ struct afx_hdl {
  * @wait_next_af: thread synchronizing struct.
  * @gon_req_action: about to send go negotiation requets frame.
  * @block_gon_req_tx: drop tx go negotiation requets frame.
+ * @p2pdev_dynamically: is p2p device if created by module param or supplicant.
  */
 struct brcmf_p2p_info {
        struct brcmf_cfg80211_info *cfg;
@@ -144,9 +145,10 @@ struct brcmf_p2p_info {
        struct completion wait_next_af;
        bool gon_req_action;
        bool block_gon_req_tx;
+       bool p2pdev_dynamically;
 };
 
-s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg);
+s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced);
 void brcmf_p2p_detach(struct brcmf_p2p_info *p2p);
 struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
                                       unsigned char name_assign_type,
@@ -155,6 +157,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
 int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev);
 int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg,
                       enum brcmf_fil_p2p_if_types if_type);
+void brcmf_p2p_ifp_removed(struct brcmf_if *ifp);
 int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev);
 void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev);
 int brcmf_p2p_scan_prep(struct wiphy *wiphy,
index 3a98c4306d1d603c8aa4098510956cad4b0df542..83d804221715f5a717597cbbe6b2624b4f9a7fbb 100644 (file)
@@ -47,12 +47,20 @@ enum brcmf_pcie_state {
 
 #define BRCMF_PCIE_43602_FW_NAME               "brcm/brcmfmac43602-pcie.bin"
 #define BRCMF_PCIE_43602_NVRAM_NAME            "brcm/brcmfmac43602-pcie.txt"
+#define BRCMF_PCIE_4350_FW_NAME                        "brcm/brcmfmac4350-pcie.bin"
+#define BRCMF_PCIE_4350_NVRAM_NAME             "brcm/brcmfmac4350-pcie.txt"
 #define BRCMF_PCIE_4356_FW_NAME                        "brcm/brcmfmac4356-pcie.bin"
 #define BRCMF_PCIE_4356_NVRAM_NAME             "brcm/brcmfmac4356-pcie.txt"
 #define BRCMF_PCIE_43570_FW_NAME               "brcm/brcmfmac43570-pcie.bin"
 #define BRCMF_PCIE_43570_NVRAM_NAME            "brcm/brcmfmac43570-pcie.txt"
 #define BRCMF_PCIE_4358_FW_NAME                        "brcm/brcmfmac4358-pcie.bin"
 #define BRCMF_PCIE_4358_NVRAM_NAME             "brcm/brcmfmac4358-pcie.txt"
+#define BRCMF_PCIE_4365_FW_NAME                        "brcm/brcmfmac4365b-pcie.bin"
+#define BRCMF_PCIE_4365_NVRAM_NAME             "brcm/brcmfmac4365b-pcie.txt"
+#define BRCMF_PCIE_4366_FW_NAME                        "brcm/brcmfmac4366b-pcie.bin"
+#define BRCMF_PCIE_4366_NVRAM_NAME             "brcm/brcmfmac4366b-pcie.txt"
+#define BRCMF_PCIE_4371_FW_NAME                        "brcm/brcmfmac4371-pcie.bin"
+#define BRCMF_PCIE_4371_NVRAM_NAME             "brcm/brcmfmac4371-pcie.txt"
 
 #define BRCMF_PCIE_FW_UP_TIMEOUT               2000 /* msec */
 
@@ -74,6 +82,8 @@ enum brcmf_pcie_state {
 #define BRCMF_PCIE_REG_INTMASK                 0x94
 #define BRCMF_PCIE_REG_SBMBX                   0x98
 
+#define BRCMF_PCIE_REG_LINK_STATUS_CTRL                0xBC
+
 #define BRCMF_PCIE_PCIE2REG_INTMASK            0x24
 #define BRCMF_PCIE_PCIE2REG_MAILBOXINT         0x48
 #define BRCMF_PCIE_PCIE2REG_MAILBOXMASK                0x4C
@@ -192,12 +202,20 @@ enum brcmf_pcie_state {
 
 MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME);
 MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4350_FW_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4350_NVRAM_NAME);
 MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME);
 MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME);
 MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME);
 MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME);
 MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME);
 MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4365_FW_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4365_NVRAM_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4366_FW_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4366_NVRAM_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4371_FW_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4371_NVRAM_NAME);
 
 
 struct brcmf_pcie_console {
@@ -434,6 +452,47 @@ brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
 }
 
 
+static void
+brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+                         void *dstaddr, u32 len)
+{
+       void __iomem *address = devinfo->tcm + mem_offset;
+       __le32 *dst32;
+       __le16 *dst16;
+       u8 *dst8;
+
+       if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) {
+               if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) {
+                       dst8 = (u8 *)dstaddr;
+                       while (len) {
+                               *dst8 = ioread8(address);
+                               address++;
+                               dst8++;
+                               len--;
+                       }
+               } else {
+                       len = len / 2;
+                       dst16 = (__le16 *)dstaddr;
+                       while (len) {
+                               *dst16 = cpu_to_le16(ioread16(address));
+                               address += 2;
+                               dst16++;
+                               len--;
+                       }
+               }
+       } else {
+               len = len / 4;
+               dst32 = (__le32 *)dstaddr;
+               while (len) {
+                       *dst32 = cpu_to_le32(ioread32(address));
+                       address += 4;
+                       dst32++;
+                       len--;
+               }
+       }
+}
+
+
 #define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \
                CHIPCREGOFFS(reg), value)
 
@@ -466,6 +525,7 @@ brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid)
 
 static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo)
 {
+       struct brcmf_core *core;
        u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD,
                             BRCMF_PCIE_CFGREG_PM_CSR,
                             BRCMF_PCIE_CFGREG_MSI_CAP,
@@ -484,32 +544,38 @@ static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo)
        if (!devinfo->ci)
                return;
 
+       /* Disable ASPM */
        brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
-       brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR,
-                              BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL);
-       lsc = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA);
+       pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
+                             &lsc);
        val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB);
-       brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, val);
+       pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
+                              val);
 
+       /* Watchdog reset */
        brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON);
        WRITECC32(devinfo, watchdog, 4);
        msleep(100);
 
+       /* Restore ASPM */
        brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
-       brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR,
-                              BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL);
-       brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, lsc);
-
-       brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
-       for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) {
-               brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR,
-                                      cfg_offset[i]);
-               val = brcmf_pcie_read_reg32(devinfo,
-                                           BRCMF_PCIE_PCIE2REG_CONFIGDATA);
-               brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n",
-                         cfg_offset[i], val);
-               brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA,
-                                      val);
+       pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
+                              lsc);
+
+       core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
+       if (core->rev <= 13) {
+               for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) {
+                       brcmf_pcie_write_reg32(devinfo,
+                                              BRCMF_PCIE_PCIE2REG_CONFIGADDR,
+                                              cfg_offset[i]);
+                       val = brcmf_pcie_read_reg32(devinfo,
+                               BRCMF_PCIE_PCIE2REG_CONFIGDATA);
+                       brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n",
+                                 cfg_offset[i], val);
+                       brcmf_pcie_write_reg32(devinfo,
+                                              BRCMF_PCIE_PCIE2REG_CONFIGDATA,
+                                              val);
+               }
        }
 }
 
@@ -519,8 +585,6 @@ static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo)
        u32 config;
 
        brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
-       if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0)
-               brcmf_pcie_reset_device(devinfo);
        /* BAR1 window may not be sized properly */
        brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
        brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0);
@@ -644,7 +708,7 @@ static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo)
        addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET;
        console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr);
 
-       brcmf_dbg(PCIE, "Console: base %x, buf %x, size %d\n",
+       brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n",
                  console->base_addr, console->buf_addr, console->bufsize);
 }
 
@@ -656,6 +720,9 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo)
        u8 ch;
        u32 newidx;
 
+       if (!BRCMF_FWCON_ON())
+               return;
+
        console = &devinfo->shared.console;
        addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET;
        newidx = brcmf_pcie_read_tcm32(devinfo, addr);
@@ -677,7 +744,7 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo)
                }
                if (ch == '\n') {
                        console->log_str[console->log_idx] = 0;
-                       brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str);
+                       pr_debug("CONSOLE: %s", console->log_str);
                        console->log_idx = 0;
                }
        }
@@ -1330,12 +1397,36 @@ static void brcmf_pcie_wowl_config(struct device *dev, bool enabled)
 }
 
 
+static size_t brcmf_pcie_get_ramsize(struct device *dev)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
+       struct brcmf_pciedev_info *devinfo = buspub->devinfo;
+
+       return devinfo->ci->ramsize - devinfo->ci->srsize;
+}
+
+
+static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
+       struct brcmf_pciedev_info *devinfo = buspub->devinfo;
+
+       brcmf_dbg(PCIE, "dump at 0x%08X: len=%zu\n", devinfo->ci->rambase, len);
+       brcmf_pcie_copy_dev_tomem(devinfo, devinfo->ci->rambase, data, len);
+       return 0;
+}
+
+
 static struct brcmf_bus_ops brcmf_pcie_bus_ops = {
        .txdata = brcmf_pcie_tx,
        .stop = brcmf_pcie_down,
        .txctl = brcmf_pcie_tx_ctlpkt,
        .rxctl = brcmf_pcie_rx_ctlpkt,
        .wowl_config = brcmf_pcie_wowl_config,
+       .get_ramsize = brcmf_pcie_get_ramsize,
+       .get_memdump = brcmf_pcie_get_memdump,
 };
 
 
@@ -1408,6 +1499,10 @@ static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo)
                fw_name = BRCMF_PCIE_43602_FW_NAME;
                nvram_name = BRCMF_PCIE_43602_NVRAM_NAME;
                break;
+       case BRCM_CC_4350_CHIP_ID:
+               fw_name = BRCMF_PCIE_4350_FW_NAME;
+               nvram_name = BRCMF_PCIE_4350_NVRAM_NAME;
+               break;
        case BRCM_CC_4356_CHIP_ID:
                fw_name = BRCMF_PCIE_4356_FW_NAME;
                nvram_name = BRCMF_PCIE_4356_NVRAM_NAME;
@@ -1422,6 +1517,18 @@ static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo)
                fw_name = BRCMF_PCIE_4358_FW_NAME;
                nvram_name = BRCMF_PCIE_4358_NVRAM_NAME;
                break;
+       case BRCM_CC_4365_CHIP_ID:
+               fw_name = BRCMF_PCIE_4365_FW_NAME;
+               nvram_name = BRCMF_PCIE_4365_NVRAM_NAME;
+               break;
+       case BRCM_CC_4366_CHIP_ID:
+               fw_name = BRCMF_PCIE_4366_FW_NAME;
+               nvram_name = BRCMF_PCIE_4366_NVRAM_NAME;
+               break;
+       case BRCM_CC_4371_CHIP_ID:
+               fw_name = BRCMF_PCIE_4371_FW_NAME;
+               nvram_name = BRCMF_PCIE_4371_NVRAM_NAME;
+               break;
        default:
                brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip);
                return -ENODEV;
@@ -1633,6 +1740,23 @@ static int brcmf_pcie_buscoreprep(void *ctx)
 }
 
 
+static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip)
+{
+       struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
+       u32 val;
+
+       devinfo->ci = chip;
+       brcmf_pcie_reset_device(devinfo);
+
+       val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
+       if (val != 0xffffffff)
+               brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
+                                      val);
+
+       return 0;
+}
+
+
 static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip,
                                        u32 rstvec)
 {
@@ -1644,6 +1768,7 @@ static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip,
 
 static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = {
        .prepare = brcmf_pcie_buscoreprep,
+       .reset = brcmf_pcie_buscore_reset,
        .activate = brcmf_pcie_buscore_activate,
        .read32 = brcmf_pcie_buscore_read32,
        .write32 = brcmf_pcie_buscore_write32,
@@ -1811,7 +1936,6 @@ brcmf_pcie_remove(struct pci_dev *pdev)
                brcmf_pcie_intr_disable(devinfo);
 
        brcmf_detach(&pdev->dev);
-       brcmf_pcie_reset_device(devinfo);
 
        kfree(bus->bus_priv.pcie);
        kfree(bus->msgbuf->flowrings);
@@ -1929,6 +2053,7 @@ cleanup:
        PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
 
 static struct pci_device_id brcmf_pcie_devid_table[] = {
+       BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID),
        BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID),
        BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
        BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
@@ -1937,6 +2062,13 @@ static struct pci_device_id brcmf_pcie_devid_table[] = {
        BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID),
        BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID),
        BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID),
+       BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID),
+       BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID),
+       BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID),
+       BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID),
+       BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID),
+       BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID),
+       BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID),
        { /* end: all zeroes */ }
 };
 
index 971172ff686c80fc16b61ab083372eab783c0ef3..d55119d3675516ec79dbcc66af54f202d9693bf8 100644 (file)
@@ -24,8 +24,8 @@ enum proto_addr_mode {
 
 
 struct brcmf_proto {
-       int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
-                      struct sk_buff *skb);
+       int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws,
+                      struct sk_buff *skb, struct brcmf_if **ifp);
        int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd,
                          void *buf, uint len);
        int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
@@ -46,9 +46,19 @@ int brcmf_proto_attach(struct brcmf_pub *drvr);
 void brcmf_proto_detach(struct brcmf_pub *drvr);
 
 static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws,
-                                     u8 *ifidx, struct sk_buff *skb)
+                                     struct sk_buff *skb,
+                                     struct brcmf_if **ifp)
 {
-       return drvr->proto->hdrpull(drvr, do_fws, ifidx, skb);
+       struct brcmf_if *tmp = NULL;
+
+       /* assure protocol is always called with
+        * non-null initialized pointer.
+        */
+       if (ifp)
+               *ifp = NULL;
+       else
+               ifp = &tmp;
+       return drvr->proto->hdrpull(drvr, do_fws, skb, ifp);
 }
 static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx,
                                         uint cmd, void *buf, uint len)
index f990e3d0e696e7f76b4ef501853eb6c5b7ff2831..7e74ac3ad81519491ac01460cd3b578e6fb3cc80 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <linux/types.h>
+#include <linux/atomic.h>
 #include <linux/kernel.h>
 #include <linux/kthread.h>
 #include <linux/printk.h>
@@ -123,6 +124,7 @@ struct rte_console {
 
 #define BRCMF_FIRSTREAD        (1 << 6)
 
+#define BRCMF_CONSOLE  10      /* watchdog interval to poll console */
 
 /* SBSDIO_DEVICE_CTL */
 
@@ -3204,6 +3206,8 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
        if (IS_ERR_OR_NULL(dentry))
                return;
 
+       bus->console_interval = BRCMF_CONSOLE;
+
        brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read);
        brcmf_debugfs_add_entry(drvr, "counters",
                                brcmf_debugfs_sdio_count_read);
@@ -3535,6 +3539,51 @@ done:
        return err;
 }
 
+static size_t brcmf_sdio_bus_get_ramsize(struct device *dev)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+       struct brcmf_sdio *bus = sdiodev->bus;
+
+       return bus->ci->ramsize - bus->ci->srsize;
+}
+
+static int brcmf_sdio_bus_get_memdump(struct device *dev, void *data,
+                                     size_t mem_size)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+       struct brcmf_sdio *bus = sdiodev->bus;
+       int err;
+       int address;
+       int offset;
+       int len;
+
+       brcmf_dbg(INFO, "dump at 0x%08x: size=%zu\n", bus->ci->rambase,
+                 mem_size);
+
+       address = bus->ci->rambase;
+       offset = err = 0;
+       sdio_claim_host(sdiodev->func[1]);
+       while (offset < mem_size) {
+               len = ((offset + MEMBLOCK) < mem_size) ? MEMBLOCK :
+                     mem_size - offset;
+               err = brcmf_sdiod_ramrw(sdiodev, false, address, data, len);
+               if (err) {
+                       brcmf_err("error %d on reading %d membytes at 0x%08x\n",
+                                 err, len, address);
+                       goto done;
+               }
+               data += len;
+               offset += len;
+               address += len;
+       }
+
+done:
+       sdio_release_host(sdiodev->func[1]);
+       return err;
+}
+
 void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus)
 {
        if (!bus->dpc_triggered) {
@@ -3613,7 +3662,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
        }
 #ifdef DEBUG
        /* Poll for console output periodically */
-       if (bus->sdiodev->state == BRCMF_SDIOD_DATA &&
+       if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() &&
            bus->console_interval != 0) {
                bus->console.count += BRCMF_WD_POLL_MS;
                if (bus->console.count >= bus->console_interval) {
@@ -3983,7 +4032,9 @@ static struct brcmf_bus_ops brcmf_sdio_bus_ops = {
        .txctl = brcmf_sdio_bus_txctl,
        .rxctl = brcmf_sdio_bus_rxctl,
        .gettxq = brcmf_sdio_bus_gettxq,
-       .wowl_config = brcmf_sdio_wowl_config
+       .wowl_config = brcmf_sdio_wowl_config,
+       .get_ramsize = brcmf_sdio_bus_get_ramsize,
+       .get_memdump = brcmf_sdio_bus_get_memdump,
 };
 
 static void brcmf_sdio_firmware_callback(struct device *dev,
index daba86d881bc1a4930f84ef8ebc5078bde4710e3..689e64d004bc56bee27f3edbae1f5c7899bbb204 100644 (file)
@@ -144,6 +144,7 @@ struct brcmf_usbdev_info {
 
        struct usb_device *usbdev;
        struct device *dev;
+       struct mutex dev_init_lock;
 
        int ctl_in_pipe, ctl_out_pipe;
        struct urb *ctl_urb; /* URB for control endpoint */
@@ -1204,6 +1205,8 @@ static void brcmf_usb_probe_phase2(struct device *dev,
        int ret;
 
        brcmf_dbg(USB, "Start fw downloading\n");
+
+       devinfo = bus->bus_priv.usb->devinfo;
        ret = check_file(fw->data);
        if (ret < 0) {
                brcmf_err("invalid firmware\n");
@@ -1211,7 +1214,6 @@ static void brcmf_usb_probe_phase2(struct device *dev,
                goto error;
        }
 
-       devinfo = bus->bus_priv.usb->devinfo;
        devinfo->image = fw->data;
        devinfo->image_len = fw->size;
 
@@ -1224,9 +1226,11 @@ static void brcmf_usb_probe_phase2(struct device *dev,
        if (ret)
                goto error;
 
+       mutex_unlock(&devinfo->dev_init_lock);
        return;
 error:
        brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret);
+       mutex_unlock(&devinfo->dev_init_lock);
        device_release_driver(dev);
 }
 
@@ -1264,6 +1268,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
                if (ret)
                        goto fail;
                /* we are done */
+               mutex_unlock(&devinfo->dev_init_lock);
                return 0;
        }
        bus->chip = bus_pub->devid;
@@ -1317,6 +1322,12 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
 
        devinfo->usbdev = usb;
        devinfo->dev = &usb->dev;
+       /* Take an init lock, to protect for disconnect while still loading.
+        * Necessary because of the asynchronous firmware load construction
+        */
+       mutex_init(&devinfo->dev_init_lock);
+       mutex_lock(&devinfo->dev_init_lock);
+
        usb_set_intfdata(intf, devinfo);
 
        /* Check that the device supports only one configuration */
@@ -1391,6 +1402,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
        return 0;
 
 fail:
+       mutex_unlock(&devinfo->dev_init_lock);
        kfree(devinfo);
        usb_set_intfdata(intf, NULL);
        return ret;
@@ -1403,8 +1415,19 @@ brcmf_usb_disconnect(struct usb_interface *intf)
 
        brcmf_dbg(USB, "Enter\n");
        devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf);
-       brcmf_usb_disconnect_cb(devinfo);
-       kfree(devinfo);
+
+       if (devinfo) {
+               mutex_lock(&devinfo->dev_init_lock);
+               /* Make sure that devinfo still exists. Firmware probe routines
+                * may have released the device and cleared the intfdata.
+                */
+               if (!usb_get_intfdata(intf))
+                       goto done;
+
+               brcmf_usb_disconnect_cb(devinfo);
+               kfree(devinfo);
+       }
+done:
        brcmf_dbg(USB, "Exit\n");
 }
 
index d2c5747e3ac9233731d70d8ff391fc8791beaebb..bec2dc1ca2e406bcf4ac0ee0d00fa27a704cf7f2 100644 (file)
@@ -820,7 +820,7 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw,
                    struct ieee80211_vif *vif,
                    enum ieee80211_ampdu_mlme_action action,
                    struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                   u8 buf_size)
+                   u8 buf_size, bool amsdu)
 {
        struct brcms_info *wl = hw->priv;
        struct scb *scb = &wl->wlc->pri_scb;
index 9728be0e704bc703d9e2bc1868e6656fe3729157..218cbc8bf3a7c329dda5022de8553e243707ce98 100644 (file)
@@ -4585,7 +4585,7 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core,
                wlc_hw->machwcap_backup = wlc_hw->machwcap;
 
                /* init tx fifo size */
-               WARN_ON((wlc_hw->corerev - XMTFIFOTBL_STARTREV) < 0 ||
+               WARN_ON(wlc_hw->corerev < XMTFIFOTBL_STARTREV ||
                        (wlc_hw->corerev - XMTFIFOTBL_STARTREV) >
                                ARRAY_SIZE(xmtfifo_sz));
                wlc_hw->xmtfifo_sz =
index 7a6daa37dc6b086853c033f7da3b2b2276b4fe65..aa06ea231db32eadae6bbe7c89fcb2825fabb228 100644 (file)
@@ -39,6 +39,7 @@
 #define BRCM_CC_4339_CHIP_ID           0x4339
 #define BRCM_CC_43430_CHIP_ID          43430
 #define BRCM_CC_4345_CHIP_ID           0x4345
+#define BRCM_CC_4350_CHIP_ID           0x4350
 #define BRCM_CC_4354_CHIP_ID           0x4354
 #define BRCM_CC_4356_CHIP_ID           0x4356
 #define BRCM_CC_43566_CHIP_ID          43566
@@ -47,6 +48,9 @@
 #define BRCM_CC_43570_CHIP_ID          43570
 #define BRCM_CC_4358_CHIP_ID           0x4358
 #define BRCM_CC_43602_CHIP_ID          43602
+#define BRCM_CC_4365_CHIP_ID           0x4365
+#define BRCM_CC_4366_CHIP_ID           0x4366
+#define BRCM_CC_4371_CHIP_ID           0x4371
 
 /* USB Device IDs */
 #define BRCM_USB_43143_DEVICE_ID       0xbd1e
@@ -56,6 +60,7 @@
 #define BRCM_USB_BCMFW_DEVICE_ID       0x0bdc
 
 /* PCIE Device IDs */
+#define BRCM_PCIE_4350_DEVICE_ID       0x43a3
 #define BRCM_PCIE_4354_DEVICE_ID       0x43df
 #define BRCM_PCIE_4356_DEVICE_ID       0x43ec
 #define BRCM_PCIE_43567_DEVICE_ID      0x43d3
 #define BRCM_PCIE_43602_2G_DEVICE_ID   0x43bb
 #define BRCM_PCIE_43602_5G_DEVICE_ID   0x43bc
 #define BRCM_PCIE_43602_RAW_DEVICE_ID  43602
+#define BRCM_PCIE_4365_DEVICE_ID       0x43ca
+#define BRCM_PCIE_4365_2G_DEVICE_ID    0x43cb
+#define BRCM_PCIE_4365_5G_DEVICE_ID    0x43cc
+#define BRCM_PCIE_4366_DEVICE_ID       0x43c3
+#define BRCM_PCIE_4366_2G_DEVICE_ID    0x43c4
+#define BRCM_PCIE_4366_5G_DEVICE_ID    0x43c5
+#define BRCM_PCIE_4371_DEVICE_ID       0x440d
+
 
 /* brcmsmac IDs */
 #define BCM4313_D11N2G_ID      0x4727  /* 4313 802.11n 2.4G device */
index b86500b4418f743ffb9363f6bcf70c8d91e94820..95a7fdb3cc1cd83fcda337527d153580789391b2 100644 (file)
@@ -2137,7 +2137,7 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw,
                        struct ieee80211_vif *vif,
                        enum ieee80211_ampdu_mlme_action action,
                        struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                       u8 buf_size)
+                       u8 buf_size, bool amsdu)
 {
        /* Aggregation is implemented fully in firmware,
         * including block ack negotiation. Do not allow
index b7e386b7662b668b8299a9ab52f3c22f2633dfe5..bebb3379017f6d40c3cd58b92ddfaabab1259f59 100644 (file)
@@ -111,7 +111,7 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw,
                        struct ieee80211_vif *vif,
                        enum ieee80211_ampdu_mlme_action action,
                        struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                       u8 buf_size);
+                       u8 buf_size, bool amsdu);
 
 void cw1200_suspend_resume(struct cw1200_common *priv,
                          struct wsm_suspend_resume *arg);
index 39f3e6f5cbcd230a49145d0bf0f589cc85d4abb7..ed0adaf1eec445defe056f8509adc6b54b708518 100644 (file)
@@ -10470,7 +10470,6 @@ static void ipw_ethtool_get_drvinfo(struct net_device *dev,
                 vers, date);
        strlcpy(info->bus_info, pci_name(p->pci_dev),
                sizeof(info->bus_info));
-       info->eedump_len = IPW_EEPROM_IMAGE_SIZE;
 }
 
 static u32 ipw_ethtool_get_link(struct net_device *dev)
index a6877dd6ba7352361f6fb0401a3fd0f4cdf7bac9..cef7f7d79cd9278e37a83ef0306254cf6fa138ad 100644 (file)
@@ -1091,8 +1091,6 @@ static const char *get_info_element_string(u16 id)
                MFIE_STRING(TIM);
                MFIE_STRING(IBSS_PARAMS);
                MFIE_STRING(COUNTRY);
-               MFIE_STRING(HP_PARAMS);
-               MFIE_STRING(HP_TABLE);
                MFIE_STRING(REQUEST);
                MFIE_STRING(CHALLENGE);
                MFIE_STRING(PWR_CONSTRAINT);
index 44fa422f255ea6505163e57029af556db94cb205..6656215a13a9239334b9eca21f510342bb28f0d7 100644 (file)
@@ -5984,7 +5984,7 @@ int
 il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        enum ieee80211_ampdu_mlme_action action,
                        struct ieee80211_sta *sta, u16 tid, u16 * ssn,
-                       u8 buf_size)
+                       u8 buf_size, bool amsdu)
 {
        struct il_priv *il = hw->priv;
        int ret = -EINVAL;
index 3a57f71b8ed57fbb98761901968ee4e4cc0110f8..8ab8706f942267fcd2467928b1cbdf248a05921f 100644 (file)
@@ -184,7 +184,7 @@ void il4965_mac_update_tkip_key(struct ieee80211_hw *hw,
 int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                            enum ieee80211_ampdu_mlme_action action,
                            struct ieee80211_sta *sta, u16 tid, u16 * ssn,
-                           u8 buf_size);
+                           u8 buf_size, bool amsdu);
 int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                       struct ieee80211_sta *sta);
 void
index aba095761ac669a99cc3553b1b2fb4d13f1a6574..6e949df399d69e725ff827eab9aefcfe90e726bd 100644 (file)
@@ -142,6 +142,7 @@ config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
 config IWLWIFI_DEVICE_TRACING
        bool "iwlwifi device access tracing"
        depends on EVENT_TRACING
+       default y
        help
          Say Y here to trace all commands, including TX frames and IO
          accesses, sent to the device. If you say yes, iwlwifi will
index 453f7c315ab525dcda15696676b16ef3b02f46f1..b3ad34e8bf5a023756fc51b261565ae99f0fee1a 100644 (file)
@@ -731,7 +731,7 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif,
                                   enum ieee80211_ampdu_mlme_action action,
                                   struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                                  u8 buf_size)
+                                  u8 buf_size, bool amsdu)
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
        int ret = -EINVAL;
index 3fb327d5a911aeba53e4ac71ad6bb2ad419d06b0..1a73c7a1da77d0e0fe16fbdc6fb866d6c65226ee 100644 (file)
 #define IWL7260_UCODE_API_MAX  17
 
 /* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK   12
-#define IWL3165_UCODE_API_OK   13
+#define IWL7260_UCODE_API_OK   13
 
 /* Lowest firmware API version supported */
-#define IWL7260_UCODE_API_MIN  12
-#define IWL3165_UCODE_API_MIN  13
+#define IWL7260_UCODE_API_MIN  13
 
 /* NVM versions */
 #define IWL7260_NVM_VERSION            0x0a1d
 
 static const struct iwl_base_params iwl7000_base_params = {
        .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000,
-       .num_of_queues = IWLAGN_NUM_QUEUES,
+       .num_of_queues = 31,
        .pll_cfg_val = 0,
        .shadow_ram_support = true,
        .led_compensation = 57,
@@ -269,11 +267,6 @@ const struct iwl_cfg iwl3165_2ac_cfg = {
        .name = "Intel(R) Dual Band Wireless AC 3165",
        .fw_name_pre = IWL7265D_FW_PRE,
        IWL_DEVICE_7000,
-       /* sparse doens't like the re-assignment but it is safe */
-#ifndef __CHECKER__
-       .ucode_api_ok = IWL3165_UCODE_API_OK,
-       .ucode_api_min = IWL3165_UCODE_API_MIN,
-#endif
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL3165_NVM_VERSION,
        .nvm_calib_ver = IWL3165_TX_POWER_VERSION,
index 197abe43ddc532eb418f8271fafaff0f7e0218b8..0116e5a4c39389ac4123100bf1486d96c11ad10a 100644 (file)
 #define IWL8000_UCODE_API_MAX  17
 
 /* Oldest version we won't warn about */
-#define IWL8000_UCODE_API_OK   12
+#define IWL8000_UCODE_API_OK   13
 
 /* Lowest firmware API version supported */
-#define IWL8000_UCODE_API_MIN  12
+#define IWL8000_UCODE_API_MIN  13
 
 /* NVM versions */
 #define IWL8000_NVM_VERSION            0x0a1d
 
 static const struct iwl_base_params iwl8000_base_params = {
        .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
-       .num_of_queues = IWLAGN_NUM_QUEUES,
+       .num_of_queues = 31,
        .pll_cfg_val = 0,
        .shadow_ram_support = true,
        .led_compensation = 57,
index 939fa229c038a1dc6beb4288a4cb7364e1238a40..910970858f98c2d04764269603c19fe9be8488cc 100644 (file)
@@ -223,13 +223,13 @@ struct iwl_tt_tx_backoff {
  * @support_tx_backoff: Support tx-backoff?
  */
 struct iwl_tt_params {
-       s32 ct_kill_entry;
-       s32 ct_kill_exit;
+       u32 ct_kill_entry;
+       u32 ct_kill_exit;
        u32 ct_kill_duration;
-       s32 dynamic_smps_entry;
-       s32 dynamic_smps_exit;
-       s32 tx_protection_entry;
-       s32 tx_protection_exit;
+       u32 dynamic_smps_entry;
+       u32 dynamic_smps_exit;
+       u32 tx_protection_entry;
+       u32 tx_protection_exit;
        struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE];
        bool support_ct_kill;
        bool support_dynamic_smps;
index a86aa5bcee7dd082105b18f56c01009aa910c53f..463cadfbfccb245612524f32d5e09db30be4a1ab 100644 (file)
@@ -450,7 +450,7 @@ static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data,
        u32 api_flags = le32_to_cpu(ucode_api->api_flags);
        int i;
 
-       if (api_index >= IWL_API_MAX_BITS / 32) {
+       if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) {
                IWL_ERR(drv, "api_index larger than supported by driver\n");
                /* don't return an error so we can load FW that has more bits */
                return 0;
@@ -472,7 +472,7 @@ static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data,
        u32 api_flags = le32_to_cpu(ucode_capa->api_capa);
        int i;
 
-       if (api_index >= IWL_CAPABILITIES_MAX_BITS / 32) {
+       if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) {
                IWL_ERR(drv, "api_index larger than supported by driver\n");
                /* don't return an error so we can load FW that has more bits */
                return 0;
index af5b3201492cb7a690ec993d784866b1630d91a5..9dbe19cbb4dd7233c57277fb91907d0b199dff04 100644 (file)
@@ -86,6 +86,8 @@
  *     Structured as &struct iwl_fw_error_dump_trigger_desc.
  * @IWL_FW_ERROR_DUMP_RB: the content of an RB structured as
  *     &struct iwl_fw_error_dump_rb
+ * @IWL_FW_ERROR_PAGING: UMAC's image memory segments which were
+ *     paged to the DRAM.
  */
 enum iwl_fw_error_dump_type {
        /* 0 is deprecated */
@@ -100,6 +102,7 @@ enum iwl_fw_error_dump_type {
        IWL_FW_ERROR_DUMP_MEM = 9,
        IWL_FW_ERROR_DUMP_ERROR_INFO = 10,
        IWL_FW_ERROR_DUMP_RB = 11,
+       IWL_FW_ERROR_DUMP_PAGING = 12,
 
        IWL_FW_ERROR_DUMP_MAX,
 };
@@ -239,6 +242,19 @@ struct iwl_fw_error_dump_rb {
        u8 data[];
 };
 
+/**
+ * struct iwl_fw_error_dump_paging - content of the UMAC's image page
+ *     block on DRAM
+ * @index: the index of the page block
+ * @reserved:
+ * @data: the content of the page block
+ */
+struct iwl_fw_error_dump_paging {
+       __le32 index;
+       __le32 reserved;
+       u8 data[];
+};
+
 /**
  * iwl_fw_error_next_data - advance fw error dump data pointer
  * @data: previous data block
index 84653e3d02bab52fc44fbc5b047aa3b5286b0154..08303db0000f3b5fca1a0e72f7a171ae26c59e1f 100644 (file)
@@ -247,36 +247,31 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t;
  * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
  *     longer than the passive one, which is essential for fragmented scan.
  * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source.
- * IWL_UCODE_TLV_API_HDC_PHASE_0: ucode supports finer configuration of LTR
- * @IWL_UCODE_TLV_API_TX_POWER_DEV: new API for tx power.
  * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header
- * @IWL_UCODE_TLV_API_SCD_CFG: This firmware can configure the scheduler
- *     through the dedicated host command.
- * @IWL_UCODE_TLV_API_SINGLE_SCAN_EBS: EBS is supported for single scans too.
- * @IWL_UCODE_TLV_API_ASYNC_DTM: Async temperature notifications are supported.
  * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params
- * @IWL_UCODE_TLV_API_STATS_V10: uCode supports/uses statistics API version 10
  * @IWL_UCODE_TLV_API_NEW_VERSION: new versioning format
  * @IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY: scan APIs use 8-level priority
  *     instead of 3.
  * @IWL_UCODE_TLV_API_TX_POWER_CHAIN: TX power API has larger command size
  *     (command version 3) that supports per-chain limits
+ *
+ * @NUM_IWL_UCODE_TLV_API: number of bits used
  */
 enum iwl_ucode_tlv_api {
        IWL_UCODE_TLV_API_BT_COEX_SPLIT         = (__force iwl_ucode_tlv_api_t)3,
        IWL_UCODE_TLV_API_FRAGMENTED_SCAN       = (__force iwl_ucode_tlv_api_t)8,
        IWL_UCODE_TLV_API_WIFI_MCC_UPDATE       = (__force iwl_ucode_tlv_api_t)9,
-       IWL_UCODE_TLV_API_HDC_PHASE_0           = (__force iwl_ucode_tlv_api_t)10,
-       IWL_UCODE_TLV_API_TX_POWER_DEV          = (__force iwl_ucode_tlv_api_t)11,
        IWL_UCODE_TLV_API_WIDE_CMD_HDR          = (__force iwl_ucode_tlv_api_t)14,
-       IWL_UCODE_TLV_API_SCD_CFG               = (__force iwl_ucode_tlv_api_t)15,
-       IWL_UCODE_TLV_API_SINGLE_SCAN_EBS       = (__force iwl_ucode_tlv_api_t)16,
-       IWL_UCODE_TLV_API_ASYNC_DTM             = (__force iwl_ucode_tlv_api_t)17,
        IWL_UCODE_TLV_API_LQ_SS_PARAMS          = (__force iwl_ucode_tlv_api_t)18,
-       IWL_UCODE_TLV_API_STATS_V10             = (__force iwl_ucode_tlv_api_t)19,
        IWL_UCODE_TLV_API_NEW_VERSION           = (__force iwl_ucode_tlv_api_t)20,
        IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY     = (__force iwl_ucode_tlv_api_t)24,
        IWL_UCODE_TLV_API_TX_POWER_CHAIN        = (__force iwl_ucode_tlv_api_t)27,
+
+       NUM_IWL_UCODE_TLV_API
+#ifdef __CHECKER__
+               /* sparse says it cannot increment the previous enum member */
+               = 128
+#endif
 };
 
 typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t;
@@ -311,6 +306,10 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t;
  *     is supported.
  * @IWL_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC
  * @IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT: supports gscan
+ * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement
+ * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts
+ *
+ * @NUM_IWL_UCODE_TLV_CAPA: number of bits used
  */
 enum iwl_ucode_tlv_capa {
        IWL_UCODE_TLV_CAPA_D0I3_SUPPORT                 = (__force iwl_ucode_tlv_capa_t)0,
@@ -333,6 +332,14 @@ enum iwl_ucode_tlv_capa {
        IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC                = (__force iwl_ucode_tlv_capa_t)29,
        IWL_UCODE_TLV_CAPA_BT_COEX_RRC                  = (__force iwl_ucode_tlv_capa_t)30,
        IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT                = (__force iwl_ucode_tlv_capa_t)31,
+       IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE         = (__force iwl_ucode_tlv_capa_t)64,
+       IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS            = (__force iwl_ucode_tlv_capa_t)65,
+
+       NUM_IWL_UCODE_TLV_CAPA
+#ifdef __CHECKER__
+               /* sparse says it cannot increment the previous enum member */
+               = 128
+#endif
 };
 
 /* The default calibrate table size if not specified by firmware file */
@@ -343,9 +350,6 @@ enum iwl_ucode_tlv_capa {
 /* The default max probe length if not specified by the firmware file */
 #define IWL_DEFAULT_MAX_PROBE_LENGTH   200
 
-#define IWL_API_MAX_BITS               64
-#define IWL_CAPABILITIES_MAX_BITS      64
-
 /*
  * For 16.0 uCode and above, there is no differentiation between sections,
  * just an offset to the HW address.
index 45e732150d28ab9de90d322528fc8429c2a26d97..84ec0cefb62a792cc2f341dadfdb1ebf41b11be7 100644 (file)
@@ -105,8 +105,8 @@ struct iwl_ucode_capabilities {
        u32 n_scan_channels;
        u32 standard_phy_calibration_size;
        u32 flags;
-       unsigned long _api[BITS_TO_LONGS(IWL_API_MAX_BITS)];
-       unsigned long _capa[BITS_TO_LONGS(IWL_CAPABILITIES_MAX_BITS)];
+       unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)];
+       unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)];
 };
 
 static inline bool
index 27c66e477833f7747d621fabe489b5d687a3120d..0bd9d4aad0c0eeecc23ac809a43287ae9bfb061b 100644 (file)
 #include "iwl-prph.h"
 #include "iwl-fh.h"
 
+void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val)
+{
+       trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val);
+       iwl_trans_write8(trans, ofs, val);
+}
+IWL_EXPORT_SYMBOL(iwl_write8);
+
+void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val)
+{
+       trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val);
+       iwl_trans_write32(trans, ofs, val);
+}
+IWL_EXPORT_SYMBOL(iwl_write32);
+
+u32 iwl_read32(struct iwl_trans *trans, u32 ofs)
+{
+       u32 val = iwl_trans_read32(trans, ofs);
+
+       trace_iwlwifi_dev_ioread32(trans->dev, ofs, val);
+       return val;
+}
+IWL_EXPORT_SYMBOL(iwl_read32);
+
 #define IWL_POLL_INTERVAL 10   /* microseconds */
 
 int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
index 705d12c079e8b2ef2492ca82a56aa92d64cfe09b..501d0560c0618de4ed763260ac499230e60ac510 100644 (file)
 #include "iwl-devtrace.h"
 #include "iwl-trans.h"
 
-static inline void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val)
-{
-       trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val);
-       iwl_trans_write8(trans, ofs, val);
-}
-
-static inline void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val)
-{
-       trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val);
-       iwl_trans_write32(trans, ofs, val);
-}
-
-static inline u32 iwl_read32(struct iwl_trans *trans, u32 ofs)
-{
-       u32 val = iwl_trans_read32(trans, ofs);
-       trace_iwlwifi_dev_ioread32(trans->dev, ofs, val);
-       return val;
-}
+void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val);
+void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val);
+u32 iwl_read32(struct iwl_trans *trans, u32 ofs);
 
 static inline void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask)
 {
index 3b8e85e51002560a0a860db0999ec947c29fb0d2..d82984912e04367a2cacedd635438605fdcba397 100644 (file)
@@ -580,13 +580,15 @@ static void iwl_set_hw_address_family_8000(struct device *dev,
        IWL_ERR_DEV(dev, "mac address is not found\n");
 }
 
+#define IWL_4165_DEVICE_ID 0x5501
+
 struct iwl_nvm_data *
 iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                   const __le16 *nvm_hw, const __le16 *nvm_sw,
                   const __le16 *nvm_calib, const __le16 *regulatory,
                   const __le16 *mac_override, const __le16 *phy_sku,
                   u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
-                  u32 mac_addr0, u32 mac_addr1)
+                  u32 mac_addr0, u32 mac_addr1, u32 hw_id)
 {
        struct iwl_nvm_data *data;
        u32 sku;
@@ -625,6 +627,17 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                                    (sku & NVM_SKU_CAP_11AC_ENABLE);
        data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE;
 
+       /*
+        * OTP 0x52 bug work around
+        * define antenna 1x1 according to MIMO disabled
+        */
+       if (hw_id == IWL_4165_DEVICE_ID && data->sku_cap_mimo_disabled) {
+               data->valid_tx_ant = ANT_B;
+               data->valid_rx_ant = ANT_B;
+               tx_chains = ANT_B;
+               rx_chains = ANT_B;
+       }
+
        data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw);
 
        if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
index 822ba52e0e5af537e42262ee4106af53d0b37f04..9f44d8188c5c1051df19825fb33be38547705066 100644 (file)
@@ -79,7 +79,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                   const __le16 *nvm_calib, const __le16 *regulatory,
                   const __le16 *mac_override, const __le16 *phy_sku,
                   u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
-                  u32 mac_addr0, u32 mac_addr1);
+                  u32 mac_addr0, u32 mac_addr1, u32 hw_id);
 
 /**
  * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW
index b47fe9d6b97abacdc2c72256f261c8ee520da22e..2a58d68332241a3ed3e8d081b4e71b73b0672dac 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015        Intel Deutschland GmbH
  *
  * 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
@@ -33,6 +34,7 @@
  *
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015        Intel Deutschland GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -108,7 +110,8 @@ struct iwl_cfg;
  * interact with it. The driver layer typically calls the start and stop
  * handlers, the transport layer calls the others.
  *
- * All the handlers MUST be implemented
+ * All the handlers MUST be implemented, except @rx_rss which can be left
+ * out *iff* the opmode will never run on hardware with multi-queue capability.
  *
  * @start: start the op_mode. The transport layer is already allocated.
  *     May sleep
@@ -116,6 +119,10 @@ struct iwl_cfg;
  *     May sleep
  * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the
  *     HCMD this Rx responds to. Can't sleep.
+ * @rx_rss: data queue RX notification to the op_mode, for (data) notifications
+ *     received on the RSS queue(s). The queue parameter indicates which of the
+ *     RSS queues received this frame; it will always be non-zero.
+ *     This method must not sleep.
  * @queue_full: notifies that a HW queue is full.
  *     Must be atomic and called with BH disabled.
  * @queue_not_full: notifies that a HW queue is not full any more.
@@ -146,6 +153,8 @@ struct iwl_op_mode_ops {
        void (*stop)(struct iwl_op_mode *op_mode);
        void (*rx)(struct iwl_op_mode *op_mode, struct napi_struct *napi,
                   struct iwl_rx_cmd_buffer *rxb);
+       void (*rx_rss)(struct iwl_op_mode *op_mode, struct napi_struct *napi,
+                      struct iwl_rx_cmd_buffer *rxb, unsigned int queue);
        void (*queue_full)(struct iwl_op_mode *op_mode, int queue);
        void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue);
        bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state);
@@ -186,6 +195,14 @@ static inline void iwl_op_mode_rx(struct iwl_op_mode *op_mode,
        return op_mode->ops->rx(op_mode, napi, rxb);
 }
 
+static inline void iwl_op_mode_rx_rss(struct iwl_op_mode *op_mode,
+                                     struct napi_struct *napi,
+                                     struct iwl_rx_cmd_buffer *rxb,
+                                     unsigned int queue)
+{
+       op_mode->ops->rx_rss(op_mode, napi, rxb, queue);
+}
+
 static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode,
                                          int queue)
 {
index 9f8bcefc04c5f726cff354f5a502dcf3539c97d2..71610968c3652a577f5f645f58d63ef4db200c8c 100644 (file)
@@ -87,6 +87,7 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
        trans->cfg = cfg;
        trans->ops = ops;
        trans->dev_cmd_headroom = dev_cmd_headroom;
+       trans->num_rx_queues = 1;
 
        snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
                 "iwl_cmd_pool:%s", dev_name(trans->dev));
index c829c505e1419cf3ce84356f9a07c4b85b8ae447..6f76525088f0e26e5400ff89acfd79471e436180 100644 (file)
@@ -386,6 +386,7 @@ static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r)
 #define IWL_MAX_HW_QUEUES              32
 #define IWL_MAX_TID_COUNT      8
 #define IWL_FRAME_LIMIT        64
+#define IWL_MAX_RX_HW_QUEUES   16
 
 /**
  * enum iwl_wowlan_status - WoWLAN image/device status
@@ -408,6 +409,7 @@ enum iwl_d3_status {
  * @STATUS_TRANS_GOING_IDLE: shutting down the trans, only special commands
  *     are sent
  * @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent
+ * @STATUS_TRANS_DEAD: trans is dead - avoid any read/write operation
  */
 enum iwl_trans_status {
        STATUS_SYNC_HCMD_ACTIVE,
@@ -418,6 +420,7 @@ enum iwl_trans_status {
        STATUS_FW_ERROR,
        STATUS_TRANS_GOING_IDLE,
        STATUS_TRANS_IDLE,
+       STATUS_TRANS_DEAD,
 };
 
 /**
@@ -654,6 +657,8 @@ enum iwl_d0i3_mode {
  * @hw_id_str: a string with info about HW ID. Set during transport allocation.
  * @pm_support: set to true in start_hw if link pm is supported
  * @ltr_enabled: set to true if the LTR is enabled
+ * @num_rx_queues: number of RX queues allocated by the transport;
+ *     the transport must set this before calling iwl_drv_start()
  * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only.
  *     The user should use iwl_trans_{alloc,free}_tx_cmd.
  * @dev_cmd_headroom: room needed for the transport's private use before the
@@ -693,6 +698,8 @@ struct iwl_trans {
        bool pm_support;
        bool ltr_enabled;
 
+       u8 num_rx_queues;
+
        /* The following fields are internal only */
        struct kmem_cache *dev_cmd_pool;
        size_t dev_cmd_headroom;
index b8ee3121fbd23be4336a4a7fafffc72f37c46b06..5c21231e195d3693855ee897834fb04411ccd2e2 100644 (file)
@@ -71,6 +71,9 @@
 #define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT     (100 * USEC_PER_MSEC)
 #define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT      (10 * USEC_PER_MSEC)
 #define IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT      (10 * USEC_PER_MSEC)
+#define IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT       (2 * 1024) /* defined in TU */
+#define IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT       (40 * 1024) /* defined in TU */
+#define IWL_MVM_P2P_LOWLATENCY_PS_ENABLE       0
 #define IWL_MVM_UAPSD_RX_DATA_TIMEOUT          (50 * USEC_PER_MSEC)
 #define IWL_MVM_UAPSD_TX_DATA_TIMEOUT          (50 * USEC_PER_MSEC)
 #define IWL_MVM_UAPSD_QUEUES           (IEEE80211_WMM_IE_STA_QOSINFO_AC_VO |\
 #define IWL_MVM_FW_BCAST_FILTER_PASS_ALL       0
 #define IWL_MVM_QUOTA_THRESHOLD                        4
 #define IWL_MVM_RS_RSSI_BASED_INIT_RATE         0
-#define IWL_MVM_RS_DISABLE_P2P_MIMO            0
+#define IWL_MVM_RS_80_20_FAR_RANGE_TWEAK       1
 #define IWL_MVM_TOF_IS_RESPONDER               0
 #define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE    1
 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE      2
index 576187611e614adc3a92ca50c4e1f2ab3a7bb7e1..85ae902df7c08d9d1d0ad4611e7947ace08bf0f7 100644 (file)
@@ -1165,6 +1165,9 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
 
+       /* make sure the d0i3 exit work is not pending */
+       flush_work(&mvm->d0i3_exit_work);
+
        ret = iwl_trans_suspend(mvm->trans);
        if (ret)
                return ret;
index 383a3162046c143129308f906269dbeba95bac16..7904b41a04c6d5a4536ca651e84e5d19d0d33e12 100644 (file)
@@ -511,7 +511,8 @@ static ssize_t iwl_dbgfs_tof_enable_write(struct ieee80211_vif *vif,
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm *mvm = mvmvif->mvm;
-       int value, ret = -EINVAL;
+       u32 value;
+       int ret = -EINVAL;
        char *data;
 
        mutex_lock(&mvm->mutex);
@@ -599,7 +600,8 @@ static ssize_t iwl_dbgfs_tof_responder_params_write(struct ieee80211_vif *vif,
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm *mvm = mvmvif->mvm;
-       int value, ret = 0;
+       u32 value;
+       int ret = 0;
        char *data;
 
        mutex_lock(&mvm->mutex);
@@ -713,11 +715,30 @@ static ssize_t iwl_dbgfs_tof_responder_params_write(struct ieee80211_vif *vif,
                goto out;
        }
 
-       data = iwl_dbgfs_is_match("ctrl_ch_position=", buf);
+       data = iwl_dbgfs_is_match("center_freq=", buf);
        if (data) {
+               struct iwl_tof_responder_config_cmd *cmd =
+                       &mvm->tof_data.responder_cfg;
+
                ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.ctrl_ch_position = value;
+               if (ret == 0 && value) {
+                       enum ieee80211_band band = (cmd->channel_num <= 14) ?
+                                                  IEEE80211_BAND_2GHZ :
+                                                  IEEE80211_BAND_5GHZ;
+                       struct ieee80211_channel chn = {
+                               .band = band,
+                               .center_freq = ieee80211_channel_to_frequency(
+                                       cmd->channel_num, band),
+                               };
+                       struct cfg80211_chan_def chandef = {
+                               .chan =  &chn,
+                               .center_freq1 =
+                                       ieee80211_channel_to_frequency(value,
+                                                                      band),
+                       };
+
+                       cmd->ctrl_ch_position = iwl_mvm_get_ctrl_pos(&chandef);
+               }
                goto out;
        }
 
@@ -822,7 +843,8 @@ static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif,
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm *mvm = mvmvif->mvm;
-       int value, ret = 0;
+       u32 value;
+       int ret = 0;
        char *data;
 
        mutex_lock(&mvm->mutex);
@@ -892,6 +914,7 @@ static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif,
                        goto out;
                }
                memcpy(mvm->tof_data.range_req.macaddr_template, mac, ETH_ALEN);
+               goto out;
        }
 
        data = iwl_dbgfs_is_match("macaddr_mask=", buf);
@@ -903,21 +926,22 @@ static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif,
                        goto out;
                }
                memcpy(mvm->tof_data.range_req.macaddr_mask, mac, ETH_ALEN);
+               goto out;
        }
 
        data = iwl_dbgfs_is_match("ap=", buf);
        if (data) {
-               struct iwl_tof_range_req_ap_entry ap;
+               struct iwl_tof_range_req_ap_entry ap = {};
                int size = sizeof(struct iwl_tof_range_req_ap_entry);
                u16 burst_period;
                u8 *mac = ap.bssid;
                unsigned int i;
 
-               if (sscanf(data, "%u %hhd %hhx %hhx"
+               if (sscanf(data, "%u %hhd %hhd %hhd"
                           "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx"
-                          "%hhx %hhx %hx"
-                          "%hhx %hhx %x"
-                          "%hhx %hhx %hhx %hhx",
+                          "%hhd %hhd %hd"
+                          "%hhd %hhd %d"
+                          "%hhx %hhd %hhd %hhd",
                           &i, &ap.channel_num, &ap.bandwidth,
                           &ap.ctrl_ch_position,
                           mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5,
@@ -944,12 +968,12 @@ static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif,
        data = iwl_dbgfs_is_match("send_range_request=", buf);
        if (data) {
                ret = kstrtou32(data, 10, &value);
-               if (ret == 0 && value) {
+               if (ret == 0 && value)
                        ret = iwl_mvm_tof_range_request_cmd(mvm, vif);
-                       goto out;
-               }
+               goto out;
        }
 
+       ret = -EINVAL;
 out:
        mutex_unlock(&mvm->mutex);
        return ret ?: count;
@@ -994,16 +1018,18 @@ static ssize_t iwl_dbgfs_tof_range_request_read(struct file *file,
                struct iwl_tof_range_req_ap_entry *ap = &cmd->ap[i];
 
                pos += scnprintf(buf + pos, bufsz - pos,
-                               "ap %.2d: channel_num=%hhx bw=%hhx"
-                               " control=%hhx bssid=%pM type=%hhx"
-                               " num_of_bursts=%hhx burst_period=%hx ftm=%hhx"
-                               " retries=%hhx tsf_delta=%x location_req=%hhx "
-                               " asap=%hhx enable=%hhx rssi=%hhx\n",
+                               "ap %.2d: channel_num=%hhd bw=%hhd"
+                               " control=%hhd bssid=%pM type=%hhd"
+                               " num_of_bursts=%hhd burst_period=%hd ftm=%hhd"
+                               " retries=%hhd tsf_delta=%d"
+                               " tsf_delta_direction=%hhd location_req=0x%hhx "
+                               " asap=%hhd enable=%hhd rssi=%hhd\n",
                                i, ap->channel_num, ap->bandwidth,
                                ap->ctrl_ch_position, ap->bssid,
                                ap->measure_type, ap->num_of_bursts,
                                ap->burst_period, ap->samples_per_burst,
                                ap->retries_per_sample, ap->tsf_delta,
+                               ap->tsf_delta_direction,
                                ap->location_req, ap->asap_mode,
                                ap->enable_dyn_ack, ap->rssi);
        }
@@ -1019,7 +1045,8 @@ static ssize_t iwl_dbgfs_tof_range_req_ext_write(struct ieee80211_vif *vif,
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm *mvm = mvmvif->mvm;
-       int value, ret = 0;
+       u32 value;
+       int ret = 0;
        char *data;
 
        mutex_lock(&mvm->mutex);
@@ -1071,12 +1098,12 @@ static ssize_t iwl_dbgfs_tof_range_req_ext_write(struct ieee80211_vif *vif,
        data = iwl_dbgfs_is_match("send_range_req_ext=", buf);
        if (data) {
                ret = kstrtou32(data, 10, &value);
-               if (ret == 0 && value) {
+               if (ret == 0 && value)
                        ret = iwl_mvm_tof_range_request_ext_cmd(mvm, vif);
-                       goto out;
-               }
+               goto out;
        }
 
+       ret = -EINVAL;
 out:
        mutex_unlock(&mvm->mutex);
        return ret ?: count;
@@ -1099,18 +1126,18 @@ static ssize_t iwl_dbgfs_tof_range_req_ext_read(struct file *file,
        mutex_lock(&mvm->mutex);
 
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "tsf_timer_offset_msec = %hx\n",
+                        "tsf_timer_offset_msec = %hd\n",
                         cmd->tsf_timer_offset_msec);
-       pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %hhx\n",
+       pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %hhd\n",
                         cmd->min_delta_ftm);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "ftm_format_and_bw20M = %hhx\n",
+                        "ftm_format_and_bw20M = %hhd\n",
                         cmd->ftm_format_and_bw20M);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "ftm_format_and_bw40M = %hhx\n",
+                        "ftm_format_and_bw40M = %hhd\n",
                         cmd->ftm_format_and_bw40M);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "ftm_format_and_bw80M = %hhx\n",
+                        "ftm_format_and_bw80M = %hhd\n",
                         cmd->ftm_format_and_bw80M);
 
        mutex_unlock(&mvm->mutex);
@@ -1123,8 +1150,8 @@ static ssize_t iwl_dbgfs_tof_range_abort_write(struct ieee80211_vif *vif,
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm *mvm = mvmvif->mvm;
-       int value, ret = 0;
-       int abort_id;
+       u32 value;
+       int abort_id, ret = 0;
        char *data;
 
        mutex_lock(&mvm->mutex);
@@ -1205,11 +1232,11 @@ static ssize_t iwl_dbgfs_tof_range_response_read(struct file *file,
                struct iwl_tof_range_rsp_ap_entry_ntfy *ap = &cmd->ap[i];
 
                pos += scnprintf(buf + pos, bufsz - pos,
-                               "ap %.2d: bssid=%pM status=%hhx bw=%hhx"
-                               " rtt=%x rtt_var=%x rtt_spread=%x"
-                               " rssi=%hhx  rssi_spread=%hhx"
-                               " range=%x range_var=%x"
-                               " time_stamp=%x\n",
+                               "ap %.2d: bssid=%pM status=%hhd bw=%hhd"
+                               " rtt=%d rtt_var=%d rtt_spread=%d"
+                               " rssi=%hhd  rssi_spread=%hhd"
+                               " range=%d range_var=%d"
+                               " time_stamp=%d\n",
                                i, ap->bssid, ap->measure_status,
                                ap->measure_bw,
                                ap->rtt, ap->rtt_variance, ap->rtt_spread,
@@ -1250,11 +1277,10 @@ static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
 {
        struct ieee80211_vif *vif = file->private_data;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       char buf[3];
+       char buf[2];
 
        buf[0] = mvmvif->low_latency ? '1' : '0';
        buf[1] = '\n';
-       buf[2] = '\0';
        return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
 }
 
index 7d69a556bcc820861692b2349d85d470eec69ca0..05928fb4021d5c23dd3801289094d4ad205d6bd2 100644 (file)
@@ -85,7 +85,7 @@ static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
        IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk);
 
        mutex_lock(&mvm->mutex);
-       ret =  iwl_mvm_flush_tx_path(mvm, scd_q_msk, true) ? : count;
+       ret =  iwl_mvm_flush_tx_path(mvm, scd_q_msk, 0) ? : count;
        mutex_unlock(&mvm->mutex);
 
        return ret;
@@ -1214,118 +1214,6 @@ static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
 
        return ret;
 }
-
-#define MAX_NUM_ND_MATCHSETS 10
-
-static ssize_t iwl_dbgfs_netdetect_write(struct iwl_mvm *mvm, char *buf,
-                                        size_t count, loff_t *ppos)
-{
-       const char *seps = ",\n";
-       char *buf_ptr = buf;
-       char *value_str = NULL;
-       int ret, i;
-
-       /* TODO: don't free if write is being called several times in one go */
-       if (mvm->nd_config) {
-               kfree(mvm->nd_config->match_sets);
-               kfree(mvm->nd_config);
-               mvm->nd_config = NULL;
-       }
-
-       mvm->nd_config = kzalloc(sizeof(*mvm->nd_config) +
-                                (11 * sizeof(struct ieee80211_channel *)),
-                                GFP_KERNEL);
-       if (!mvm->nd_config) {
-               ret = -ENOMEM;
-               goto out_free;
-       }
-
-       mvm->nd_config->n_channels = 11;
-       mvm->nd_config->scan_width = NL80211_BSS_CHAN_WIDTH_20;
-       mvm->nd_config->interval = 5;
-       mvm->nd_config->min_rssi_thold = -80;
-       for (i = 0; i < mvm->nd_config->n_channels; i++)
-               mvm->nd_config->channels[i] = &mvm->nvm_data->channels[i];
-
-       mvm->nd_config->match_sets =
-               kcalloc(MAX_NUM_ND_MATCHSETS,
-                       sizeof(*mvm->nd_config->match_sets),
-                       GFP_KERNEL);
-       if (!mvm->nd_config->match_sets) {
-               ret = -ENOMEM;
-               goto out_free;
-       }
-
-       while ((value_str = strsep(&buf_ptr, seps)) &&
-              strlen(value_str)) {
-               struct cfg80211_match_set *set;
-
-               if (mvm->nd_config->n_match_sets >= MAX_NUM_ND_MATCHSETS) {
-                       ret = -EINVAL;
-                       goto out_free;
-               }
-
-               set = &mvm->nd_config->match_sets[mvm->nd_config->n_match_sets];
-               set->ssid.ssid_len = strlen(value_str);
-
-               if (set->ssid.ssid_len > IEEE80211_MAX_SSID_LEN) {
-                       ret = -EINVAL;
-                       goto out_free;
-               }
-
-               memcpy(set->ssid.ssid, value_str, set->ssid.ssid_len);
-
-               mvm->nd_config->n_match_sets++;
-       }
-
-       ret = count;
-
-       if (mvm->nd_config->n_match_sets)
-               goto out;
-
-out_free:
-       if (mvm->nd_config)
-               kfree(mvm->nd_config->match_sets);
-       kfree(mvm->nd_config);
-       mvm->nd_config = NULL;
-out:
-       return ret;
-}
-
-static ssize_t
-iwl_dbgfs_netdetect_read(struct file *file, char __user *user_buf,
-                        size_t count, loff_t *ppos)
-{
-       struct iwl_mvm *mvm = file->private_data;
-       size_t bufsz, ret;
-       char *buf;
-       int i, n_match_sets, pos = 0;
-
-       n_match_sets = mvm->nd_config ? mvm->nd_config->n_match_sets : 0;
-
-       bufsz = n_match_sets * (IEEE80211_MAX_SSID_LEN + 1) + 1;
-       buf = kzalloc(bufsz, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       for (i = 0; i < n_match_sets; i++) {
-               if (pos +
-                   mvm->nd_config->match_sets[i].ssid.ssid_len + 2 > bufsz) {
-                       ret = -EIO;
-                       goto out;
-               }
-
-               memcpy(buf + pos, mvm->nd_config->match_sets[i].ssid.ssid,
-                      mvm->nd_config->match_sets[i].ssid.ssid_len);
-               pos += mvm->nd_config->match_sets[i].ssid.ssid_len;
-               buf[pos++] = '\n';
-       }
-
-       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-out:
-       kfree(buf);
-       return ret;
-}
 #endif
 
 #define PRINT_MVM_REF(ref) do {                                                \
@@ -1473,11 +1361,25 @@ out:
        return count;
 }
 
+static ssize_t
+iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf,
+                             size_t count, loff_t *ppos)
+{
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+       ret = iwl_mvm_send_cmd_pdu(mvm, ECHO_CMD, 0, 0, NULL);
+       mutex_unlock(&mvm->mutex);
+
+       return ret ?: count;
+}
+
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);
 
 /* Device wide debugfs entries */
 MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16);
 MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8);
+MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(set_nic_temperature, 64);
 MVM_DEBUGFS_READ_FILE_OPS(nic_temp);
@@ -1503,7 +1405,6 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
 
 #ifdef CONFIG_PM_SLEEP
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(netdetect, 384);
 #endif
 
 int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
@@ -1538,6 +1439,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR);
        if (!debugfs_create_bool("enable_scan_iteration_notif",
                                 S_IRUSR | S_IWUSR,
                                 mvm->debugfs_dir,
@@ -1572,7 +1474,6 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR,
                                mvm->debugfs_dir, &mvm->last_netdetect_scans))
                goto err;
-       MVM_DEBUGFS_ADD_FILE(netdetect, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
 #endif
 
        if (!debugfs_create_u8("low_latency_agg_frame_limit", S_IRUSR | S_IWUSR,
@@ -1594,6 +1495,9 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        if (!debugfs_create_blob("nvm_prod", S_IRUSR,
                                  mvm->debugfs_dir, &mvm->nvm_prod_blob))
                goto err;
+       if (!debugfs_create_blob("nvm_phy_sku", S_IRUSR,
+                                mvm->debugfs_dir, &mvm->nvm_phy_sku_blob))
+               goto err;
 
        /*
         * Create a symlink with mac80211. It will be removed when mac80211
index 7005fa4be74a18f536762c687529085452d89f49..c8f3e2536cbbe7a63a4ddd712740f0a4303c618f 100644 (file)
@@ -192,16 +192,10 @@ struct iwl_powertable_cmd {
 /**
  * enum iwl_device_power_flags - masks for device power command flags
  * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
- *     receiver and transmitter. '0' - does not allow. This flag should be
- *     always set to '1' unless one need to disable actual power down for debug
- *     purposes.
- * @DEVICE_POWER_FLAGS_CAM_MSK: '1' CAM (Continuous Active Mode) is set, meaning
- *     that power management is disabled. '0' Power management is enabled, one
- *     of power schemes is applied.
+ *     receiver and transmitter. '0' - does not allow.
 */
 enum iwl_device_power_flags {
        DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK   = BIT(0),
-       DEVICE_POWER_FLAGS_CAM_MSK              = BIT(13),
 };
 
 /**
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h
new file mode 100644 (file)
index 0000000..9b7e49d
--- /dev/null
@@ -0,0 +1,238 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015        Intel Deutschland GmbH
+ *
+ * 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015        Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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.
+ *  * Neither the name Intel Corporation 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER 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.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_rx_h__
+#define __fw_api_rx_h__
+
+#define IWL_RX_INFO_PHY_CNT 8
+#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1
+#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff
+#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00
+#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000
+#define IWL_RX_INFO_ENERGY_ANT_A_POS 0
+#define IWL_RX_INFO_ENERGY_ANT_B_POS 8
+#define IWL_RX_INFO_ENERGY_ANT_C_POS 16
+
+/**
+ * struct iwl_rx_phy_info - phy info
+ * (REPLY_RX_PHY_CMD = 0xc0)
+ * @non_cfg_phy_cnt: non configurable DSP phy data byte count
+ * @cfg_phy_cnt: configurable DSP phy data byte count
+ * @stat_id: configurable DSP phy data set ID
+ * @reserved1:
+ * @system_timestamp: GP2  at on air rise
+ * @timestamp: TSF at on air rise
+ * @beacon_time_stamp: beacon at on-air rise
+ * @phy_flags: general phy flags: band, modulation, ...
+ * @channel: channel number
+ * @non_cfg_phy_buf: for various implementations of non_cfg_phy
+ * @rate_n_flags: RATE_MCS_*
+ * @byte_count: frame's byte-count
+ * @frame_time: frame's time on the air, based on byte count and frame rate
+ *     calculation
+ * @mac_active_msk: what MACs were active when the frame was received
+ *
+ * Before each Rx, the device sends this data. It contains PHY information
+ * about the reception of the packet.
+ */
+struct iwl_rx_phy_info {
+       u8 non_cfg_phy_cnt;
+       u8 cfg_phy_cnt;
+       u8 stat_id;
+       u8 reserved1;
+       __le32 system_timestamp;
+       __le64 timestamp;
+       __le32 beacon_time_stamp;
+       __le16 phy_flags;
+       __le16 channel;
+       __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT];
+       __le32 rate_n_flags;
+       __le32 byte_count;
+       __le16 mac_active_msk;
+       __le16 frame_time;
+} __packed;
+
+/*
+ * TCP offload Rx assist info
+ *
+ * bits 0:3 - reserved
+ * bits 4:7 - MIC CRC length
+ * bits 8:12 - MAC header length
+ * bit 13 - Padding indication
+ * bit 14 - A-AMSDU indication
+ * bit 15 - Offload enabled
+ */
+enum iwl_csum_rx_assist_info {
+       CSUM_RXA_RESERVED_MASK  = 0x000f,
+       CSUM_RXA_MICSIZE_MASK   = 0x00f0,
+       CSUM_RXA_HEADERLEN_MASK = 0x1f00,
+       CSUM_RXA_PADD           = BIT(13),
+       CSUM_RXA_AMSDU          = BIT(14),
+       CSUM_RXA_ENA            = BIT(15)
+};
+
+/**
+ * struct iwl_rx_mpdu_res_start - phy info
+ * @assist: see CSUM_RX_ASSIST_ above
+ */
+struct iwl_rx_mpdu_res_start {
+       __le16 byte_count;
+       __le16 assist;
+} __packed; /* _RX_MPDU_RES_START_API_S_VER_2 */
+
+/**
+ * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags
+ * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band
+ * @RX_RES_PHY_FLAGS_MOD_CCK:
+ * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short
+ * @RX_RES_PHY_FLAGS_NARROW_BAND:
+ * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received
+ * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU
+ * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame
+ * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble
+ * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame
+ */
+enum iwl_rx_phy_flags {
+       RX_RES_PHY_FLAGS_BAND_24        = BIT(0),
+       RX_RES_PHY_FLAGS_MOD_CCK        = BIT(1),
+       RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2),
+       RX_RES_PHY_FLAGS_NARROW_BAND    = BIT(3),
+       RX_RES_PHY_FLAGS_ANTENNA        = (0x7 << 4),
+       RX_RES_PHY_FLAGS_ANTENNA_POS    = 4,
+       RX_RES_PHY_FLAGS_AGG            = BIT(7),
+       RX_RES_PHY_FLAGS_OFDM_HT        = BIT(8),
+       RX_RES_PHY_FLAGS_OFDM_GF        = BIT(9),
+       RX_RES_PHY_FLAGS_OFDM_VHT       = BIT(10),
+};
+
+/**
+ * enum iwl_mvm_rx_status - written by fw for each Rx packet
+ * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine
+ * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow
+ * @RX_MPDU_RES_STATUS_SRC_STA_FOUND:
+ * @RX_MPDU_RES_STATUS_KEY_VALID:
+ * @RX_MPDU_RES_STATUS_KEY_PARAM_OK:
+ * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed
+ * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked
+ *     in the driver.
+ * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine
+ * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR:  valid for alg = CCM_CMAC or
+ *     alg = CCM only. Checks replay attack for 11w frames. Relevant only if
+ *     %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set.
+ * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted
+ * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP
+ * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM
+ * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP
+ * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC
+ * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted
+ * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm
+ * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted
+ * @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP:
+ * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP:
+ * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT:
+ * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame
+ * @RX_MPDU_RES_STATUS_CSUM_DONE: checksum was done by the hw
+ * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors
+ * @RX_MPDU_RES_STATUS_HASH_INDEX_MSK:
+ * @RX_MPDU_RES_STATUS_STA_ID_MSK:
+ * @RX_MPDU_RES_STATUS_RRF_KILL:
+ * @RX_MPDU_RES_STATUS_FILTERING_MSK:
+ * @RX_MPDU_RES_STATUS2_FILTERING_MSK:
+ */
+enum iwl_mvm_rx_status {
+       RX_MPDU_RES_STATUS_CRC_OK                       = BIT(0),
+       RX_MPDU_RES_STATUS_OVERRUN_OK                   = BIT(1),
+       RX_MPDU_RES_STATUS_SRC_STA_FOUND                = BIT(2),
+       RX_MPDU_RES_STATUS_KEY_VALID                    = BIT(3),
+       RX_MPDU_RES_STATUS_KEY_PARAM_OK                 = BIT(4),
+       RX_MPDU_RES_STATUS_ICV_OK                       = BIT(5),
+       RX_MPDU_RES_STATUS_MIC_OK                       = BIT(6),
+       RX_MPDU_RES_STATUS_TTAK_OK                      = BIT(7),
+       RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR         = BIT(7),
+       RX_MPDU_RES_STATUS_SEC_NO_ENC                   = (0 << 8),
+       RX_MPDU_RES_STATUS_SEC_WEP_ENC                  = (1 << 8),
+       RX_MPDU_RES_STATUS_SEC_CCM_ENC                  = (2 << 8),
+       RX_MPDU_RES_STATUS_SEC_TKIP_ENC                 = (3 << 8),
+       RX_MPDU_RES_STATUS_SEC_EXT_ENC                  = (4 << 8),
+       RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC             = (6 << 8),
+       RX_MPDU_RES_STATUS_SEC_ENC_ERR                  = (7 << 8),
+       RX_MPDU_RES_STATUS_SEC_ENC_MSK                  = (7 << 8),
+       RX_MPDU_RES_STATUS_DEC_DONE                     = BIT(11),
+       RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP        = BIT(12),
+       RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP               = BIT(13),
+       RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT               = BIT(14),
+       RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME             = BIT(15),
+       RX_MPDU_RES_STATUS_CSUM_DONE                    = BIT(16),
+       RX_MPDU_RES_STATUS_CSUM_OK                      = BIT(17),
+       RX_MPDU_RES_STATUS_HASH_INDEX_MSK               = (0x3F0000),
+       RX_MPDU_RES_STATUS_STA_ID_MSK                   = (0x1f000000),
+       RX_MPDU_RES_STATUS_RRF_KILL                     = BIT(29),
+       RX_MPDU_RES_STATUS_FILTERING_MSK                = (0xc00000),
+       RX_MPDU_RES_STATUS2_FILTERING_MSK               = (0xc0000000),
+};
+
+#endif /* __fw_api_rx_h__ */
index 660cc1c93e192654345b96b5b4a6a9c9589dc746..3a657e4b60ac825600db8f8669a3e3a257e0b6ce 100644 (file)
@@ -101,6 +101,7 @@ struct iwl_ssid_ie {
 
 #define IWL_FULL_SCAN_MULTIPLIER 5
 #define IWL_FAST_SCHED_SCAN_ITERATIONS 3
+#define IWL_MAX_SCHED_SCAN_PLANS 2
 
 enum scan_framework_client {
        SCAN_CLIENT_SCHED_SCAN          = BIT(0),
@@ -359,7 +360,7 @@ struct iwl_scan_req_lmac {
        /* SCAN_REQ_PERIODIC_PARAMS_API_S */
        __le32 iter_num;
        __le32 delay;
-       struct iwl_scan_schedule_lmac schedule[2];
+       struct iwl_scan_schedule_lmac schedule[IWL_MAX_SCHED_SCAN_PLANS];
        struct iwl_scan_channel_opt channel_opt[2];
        u8 data[];
 } __packed;
@@ -582,7 +583,7 @@ struct iwl_scan_umac_schedule {
  */
 struct iwl_scan_req_umac_tail {
        /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */
-       struct iwl_scan_umac_schedule schedule[2];
+       struct iwl_scan_umac_schedule schedule[IWL_MAX_SCHED_SCAN_PLANS];
        __le16 delay;
        __le16 reserved;
        /* SCAN_PROBE_PARAMS_API_S_VER_1 */
index 709e28d8b1b09634aa1e427a2544ae0ea669df59..0c321f63ee4220fd95b2f43a085f2a16d06497ec 100644 (file)
@@ -219,32 +219,6 @@ struct mvm_statistics_bt_activity {
        __le32 lo_priority_rx_denied_cnt;
 } __packed;  /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */
 
-struct mvm_statistics_general_v5 {
-       __le32 radio_temperature;
-       __le32 radio_voltage;
-       struct mvm_statistics_dbg dbg;
-       __le32 sleep_time;
-       __le32 slots_out;
-       __le32 slots_idle;
-       __le32 ttl_timestamp;
-       struct mvm_statistics_div slow_div;
-       __le32 rx_enable_counter;
-       /*
-        * num_of_sos_states:
-        *  count the number of times we have to re-tune
-        *  in order to get out of bad PHY status
-        */
-       __le32 num_of_sos_states;
-       __le32 beacon_filtered;
-       __le32 missed_beacons;
-       __s8 beacon_filter_average_energy;
-       __s8 beacon_filter_reason;
-       __s8 beacon_filter_current_energy;
-       __s8 beacon_filter_reserved;
-       __le32 beacon_filter_delta_time;
-       struct mvm_statistics_bt_activity bt_activity;
-} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */
-
 struct mvm_statistics_general_v8 {
        __le32 radio_temperature;
        __le32 radio_voltage;
@@ -263,10 +237,10 @@ struct mvm_statistics_general_v8 {
        __le32 num_of_sos_states;
        __le32 beacon_filtered;
        __le32 missed_beacons;
-       __s8 beacon_filter_average_energy;
-       __s8 beacon_filter_reason;
-       __s8 beacon_filter_current_energy;
-       __s8 beacon_filter_reserved;
+       u8 beacon_filter_average_energy;
+       u8 beacon_filter_reason;
+       u8 beacon_filter_current_energy;
+       u8 beacon_filter_reserved;
        __le32 beacon_filter_delta_time;
        struct mvm_statistics_bt_activity bt_activity;
        __le64 rx_time;
@@ -293,13 +267,6 @@ struct mvm_statistics_rx {
  * STATISTICS_CMD (0x9c), below.
  */
 
-struct iwl_notif_statistics_v8 {
-       __le32 flag;
-       struct mvm_statistics_rx rx;
-       struct mvm_statistics_tx tx;
-       struct mvm_statistics_general_v5 general;
-} __packed; /* STATISTICS_NTFY_API_S_VER_8 */
-
 struct iwl_notif_statistics_v10 {
        __le32 flag;
        struct mvm_statistics_rx rx;
index 4af7513adda22e3a08a59361313659ba0ff8d450..181590fbd3b340c712d3f0d1edb70b118a7c9b89 100644 (file)
@@ -67,6 +67,7 @@
 #define __fw_api_h__
 
 #include "fw-api-rs.h"
+#include "fw-api-rx.h"
 #include "fw-api-tx.h"
 #include "fw-api-sta.h"
 #include "fw-api-mac.h"
@@ -100,6 +101,7 @@ enum iwl_mvm_tx_fifo {
 enum {
        MVM_ALIVE = 0x1,
        REPLY_ERROR = 0x2,
+       ECHO_CMD = 0x3,
 
        INIT_COMPLETE_NOTIF = 0x4,
 
@@ -266,6 +268,16 @@ enum {
        REPLY_MAX = 0xff,
 };
 
+enum iwl_phy_ops_subcmd_ids {
+       CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0,
+       DTS_MEASUREMENT_NOTIF_WIDE = 0xFF,
+};
+
+/* command groups */
+enum {
+       PHY_OPS_GROUP = 0x4,
+};
+
 /**
  * struct iwl_cmd_response - generic response struct for most commands
  * @status: status of the command asked, changes for each one
@@ -1070,190 +1082,6 @@ struct iwl_hs20_roc_res {
        __le32 status;
 } __packed; /* HOT_SPOT_RSP_API_S_VER_1 */
 
-#define IWL_RX_INFO_PHY_CNT 8
-#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1
-#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff
-#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00
-#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000
-#define IWL_RX_INFO_ENERGY_ANT_A_POS 0
-#define IWL_RX_INFO_ENERGY_ANT_B_POS 8
-#define IWL_RX_INFO_ENERGY_ANT_C_POS 16
-
-#define IWL_RX_INFO_AGC_IDX 1
-#define IWL_RX_INFO_RSSI_AB_IDX 2
-#define IWL_OFDM_AGC_A_MSK 0x0000007f
-#define IWL_OFDM_AGC_A_POS 0
-#define IWL_OFDM_AGC_B_MSK 0x00003f80
-#define IWL_OFDM_AGC_B_POS 7
-#define IWL_OFDM_AGC_CODE_MSK 0x3fe00000
-#define IWL_OFDM_AGC_CODE_POS 20
-#define IWL_OFDM_RSSI_INBAND_A_MSK 0x00ff
-#define IWL_OFDM_RSSI_A_POS 0
-#define IWL_OFDM_RSSI_ALLBAND_A_MSK 0xff00
-#define IWL_OFDM_RSSI_ALLBAND_A_POS 8
-#define IWL_OFDM_RSSI_INBAND_B_MSK 0xff0000
-#define IWL_OFDM_RSSI_B_POS 16
-#define IWL_OFDM_RSSI_ALLBAND_B_MSK 0xff000000
-#define IWL_OFDM_RSSI_ALLBAND_B_POS 24
-
-/**
- * struct iwl_rx_phy_info - phy info
- * (REPLY_RX_PHY_CMD = 0xc0)
- * @non_cfg_phy_cnt: non configurable DSP phy data byte count
- * @cfg_phy_cnt: configurable DSP phy data byte count
- * @stat_id: configurable DSP phy data set ID
- * @reserved1:
- * @system_timestamp: GP2  at on air rise
- * @timestamp: TSF at on air rise
- * @beacon_time_stamp: beacon at on-air rise
- * @phy_flags: general phy flags: band, modulation, ...
- * @channel: channel number
- * @non_cfg_phy_buf: for various implementations of non_cfg_phy
- * @rate_n_flags: RATE_MCS_*
- * @byte_count: frame's byte-count
- * @frame_time: frame's time on the air, based on byte count and frame rate
- *     calculation
- * @mac_active_msk: what MACs were active when the frame was received
- *
- * Before each Rx, the device sends this data. It contains PHY information
- * about the reception of the packet.
- */
-struct iwl_rx_phy_info {
-       u8 non_cfg_phy_cnt;
-       u8 cfg_phy_cnt;
-       u8 stat_id;
-       u8 reserved1;
-       __le32 system_timestamp;
-       __le64 timestamp;
-       __le32 beacon_time_stamp;
-       __le16 phy_flags;
-       __le16 channel;
-       __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT];
-       __le32 rate_n_flags;
-       __le32 byte_count;
-       __le16 mac_active_msk;
-       __le16 frame_time;
-} __packed;
-
-/*
- * TCP offload Rx assist info
- *
- * bits 0:3 - reserved
- * bits 4:7 - MIC CRC length
- * bits 8:12 - MAC header length
- * bit 13 - Padding indication
- * bit 14 - A-AMSDU indication
- * bit 15 - Offload enabled
- */
-enum iwl_csum_rx_assist_info {
-       CSUM_RXA_RESERVED_MASK  = 0x000f,
-       CSUM_RXA_MICSIZE_MASK   = 0x00f0,
-       CSUM_RXA_HEADERLEN_MASK = 0x1f00,
-       CSUM_RXA_PADD           = BIT(13),
-       CSUM_RXA_AMSDU          = BIT(14),
-       CSUM_RXA_ENA            = BIT(15)
-};
-
-/**
- * struct iwl_rx_mpdu_res_start - phy info
- * @assist: see CSUM_RX_ASSIST_ above
- */
-struct iwl_rx_mpdu_res_start {
-       __le16 byte_count;
-       __le16 assist;
-} __packed; /* _RX_MPDU_RES_START_API_S_VER_2 */
-
-/**
- * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags
- * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band
- * @RX_RES_PHY_FLAGS_MOD_CCK:
- * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short
- * @RX_RES_PHY_FLAGS_NARROW_BAND:
- * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received
- * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU
- * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame
- * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble
- * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame
- */
-enum iwl_rx_phy_flags {
-       RX_RES_PHY_FLAGS_BAND_24        = BIT(0),
-       RX_RES_PHY_FLAGS_MOD_CCK        = BIT(1),
-       RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2),
-       RX_RES_PHY_FLAGS_NARROW_BAND    = BIT(3),
-       RX_RES_PHY_FLAGS_ANTENNA        = (0x7 << 4),
-       RX_RES_PHY_FLAGS_ANTENNA_POS    = 4,
-       RX_RES_PHY_FLAGS_AGG            = BIT(7),
-       RX_RES_PHY_FLAGS_OFDM_HT        = BIT(8),
-       RX_RES_PHY_FLAGS_OFDM_GF        = BIT(9),
-       RX_RES_PHY_FLAGS_OFDM_VHT       = BIT(10),
-};
-
-/**
- * enum iwl_mvm_rx_status - written by fw for each Rx packet
- * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine
- * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow
- * @RX_MPDU_RES_STATUS_SRC_STA_FOUND:
- * @RX_MPDU_RES_STATUS_KEY_VALID:
- * @RX_MPDU_RES_STATUS_KEY_PARAM_OK:
- * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed
- * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked
- *     in the driver.
- * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine
- * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR:  valid for alg = CCM_CMAC or
- *     alg = CCM only. Checks replay attack for 11w frames. Relevant only if
- *     %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set.
- * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted
- * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP
- * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM
- * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP
- * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC
- * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted
- * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm
- * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted
- * @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP:
- * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP:
- * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT:
- * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame
- * @RX_MPDU_RES_STATUS_CSUM_DONE: checksum was done by the hw
- * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors
- * @RX_MPDU_RES_STATUS_HASH_INDEX_MSK:
- * @RX_MPDU_RES_STATUS_STA_ID_MSK:
- * @RX_MPDU_RES_STATUS_RRF_KILL:
- * @RX_MPDU_RES_STATUS_FILTERING_MSK:
- * @RX_MPDU_RES_STATUS2_FILTERING_MSK:
- */
-enum iwl_mvm_rx_status {
-       RX_MPDU_RES_STATUS_CRC_OK                       = BIT(0),
-       RX_MPDU_RES_STATUS_OVERRUN_OK                   = BIT(1),
-       RX_MPDU_RES_STATUS_SRC_STA_FOUND                = BIT(2),
-       RX_MPDU_RES_STATUS_KEY_VALID                    = BIT(3),
-       RX_MPDU_RES_STATUS_KEY_PARAM_OK                 = BIT(4),
-       RX_MPDU_RES_STATUS_ICV_OK                       = BIT(5),
-       RX_MPDU_RES_STATUS_MIC_OK                       = BIT(6),
-       RX_MPDU_RES_STATUS_TTAK_OK                      = BIT(7),
-       RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR         = BIT(7),
-       RX_MPDU_RES_STATUS_SEC_NO_ENC                   = (0 << 8),
-       RX_MPDU_RES_STATUS_SEC_WEP_ENC                  = (1 << 8),
-       RX_MPDU_RES_STATUS_SEC_CCM_ENC                  = (2 << 8),
-       RX_MPDU_RES_STATUS_SEC_TKIP_ENC                 = (3 << 8),
-       RX_MPDU_RES_STATUS_SEC_EXT_ENC                  = (4 << 8),
-       RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC             = (6 << 8),
-       RX_MPDU_RES_STATUS_SEC_ENC_ERR                  = (7 << 8),
-       RX_MPDU_RES_STATUS_SEC_ENC_MSK                  = (7 << 8),
-       RX_MPDU_RES_STATUS_DEC_DONE                     = BIT(11),
-       RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP        = BIT(12),
-       RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP               = BIT(13),
-       RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT               = BIT(14),
-       RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME             = BIT(15),
-       RX_MPDU_RES_STATUS_CSUM_DONE                    = BIT(16),
-       RX_MPDU_RES_STATUS_CSUM_OK                      = BIT(17),
-       RX_MPDU_RES_STATUS_HASH_INDEX_MSK               = (0x3F0000),
-       RX_MPDU_RES_STATUS_STA_ID_MSK                   = (0x1f000000),
-       RX_MPDU_RES_STATUS_RRF_KILL                     = BIT(29),
-       RX_MPDU_RES_STATUS_FILTERING_MSK                = (0xc00000),
-       RX_MPDU_RES_STATUS2_FILTERING_MSK               = (0xc0000000),
-};
-
 /**
  * struct iwl_radio_version_notif - information on the radio version
  * ( RADIO_VERSION_NOTIFICATION = 0x68 )
@@ -1695,6 +1523,69 @@ struct iwl_dts_measurement_cmd {
        __le32 flags;
 } __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_CMD_S */
 
+/**
+* enum iwl_dts_control_measurement_mode - DTS measurement type
+* @DTS_AUTOMATIC: Automatic mode (full SW control). Provide temperature read
+*                 back (latest value. Not waiting for new value). Use automatic
+*                 SW DTS configuration.
+* @DTS_REQUEST_READ: Request DTS read. Configure DTS with manual settings,
+*                    trigger DTS reading and provide read back temperature read
+*                    when available.
+* @DTS_OVER_WRITE: over-write the DTS temperatures in the SW until next read
+* @DTS_DIRECT_WITHOUT_MEASURE: DTS returns its latest temperature result,
+*                              without measurement trigger.
+*/
+enum iwl_dts_control_measurement_mode {
+       DTS_AUTOMATIC                   = 0,
+       DTS_REQUEST_READ                = 1,
+       DTS_OVER_WRITE                  = 2,
+       DTS_DIRECT_WITHOUT_MEASURE      = 3,
+};
+
+/**
+* enum iwl_dts_used - DTS to use or used for measurement in the DTS request
+* @DTS_USE_TOP: Top
+* @DTS_USE_CHAIN_A: chain A
+* @DTS_USE_CHAIN_B: chain B
+* @DTS_USE_CHAIN_C: chain C
+* @XTAL_TEMPERATURE - read temperature from xtal
+*/
+enum iwl_dts_used {
+       DTS_USE_TOP             = 0,
+       DTS_USE_CHAIN_A         = 1,
+       DTS_USE_CHAIN_B         = 2,
+       DTS_USE_CHAIN_C         = 3,
+       XTAL_TEMPERATURE        = 4,
+};
+
+/**
+* enum iwl_dts_bit_mode - bit-mode to use in DTS request read mode
+* @DTS_BIT6_MODE: bit 6 mode
+* @DTS_BIT8_MODE: bit 8 mode
+*/
+enum iwl_dts_bit_mode {
+       DTS_BIT6_MODE   = 0,
+       DTS_BIT8_MODE   = 1,
+};
+
+/**
+ * iwl_ext_dts_measurement_cmd - request extended DTS temperature measurements
+ * @control_mode: see &enum iwl_dts_control_measurement_mode
+ * @temperature: used when over write DTS mode is selected
+ * @sensor: set temperature sensor to use. See &enum iwl_dts_used
+ * @avg_factor: average factor to DTS in request DTS read mode
+ * @bit_mode: value defines the DTS bit mode to use. See &enum iwl_dts_bit_mode
+ * @step_duration: step duration for the DTS
+ */
+struct iwl_ext_dts_measurement_cmd {
+       __le32 control_mode;
+       __le32 temperature;
+       __le32 sensor;
+       __le32 avg_factor;
+       __le32 bit_mode;
+       __le32 step_duration;
+} __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */
+
 /**
  * iwl_dts_measurement_notif - notification received with the measurements
  *
index 5c7f7cc9ffcc2aa1d81cbf1ff5b2d33f46133727..d906fa13ba9710a3e9250cd481d735aa71f767de 100644 (file)
@@ -616,12 +616,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
         * will be empty.
         */
 
-       for (i = 0; i < IWL_MAX_HW_QUEUES; i++) {
-               if (i < mvm->first_agg_queue && i != IWL_MVM_CMD_QUEUE)
-                       mvm->queue_to_mac80211[i] = i;
-               else
-                       mvm->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
-       }
+       memset(&mvm->queue_info, 0, sizeof(mvm->queue_info));
+       mvm->queue_info[IWL_MVM_CMD_QUEUE].hw_queue_refcount = 1;
 
        for (i = 0; i < IEEE80211_MAX_QUEUES; i++)
                atomic_set(&mvm->mac80211_queue_stop_count[i], 0);
@@ -940,19 +936,6 @@ int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
        return ret;
 }
 
-static int iwl_mvm_config_ltr_v1(struct iwl_mvm *mvm)
-{
-       struct iwl_ltr_config_cmd_v1 cmd_v1 = {
-               .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
-       };
-
-       if (!mvm->trans->ltr_enabled)
-               return 0;
-
-       return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
-                                   sizeof(cmd_v1), &cmd_v1);
-}
-
 static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
 {
        struct iwl_ltr_config_cmd cmd = {
@@ -962,9 +945,6 @@ static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
        if (!mvm->trans->ltr_enabled)
                return 0;
 
-       if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_HDC_PHASE_0))
-               return iwl_mvm_config_ltr_v1(mvm);
-
        return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
                                    sizeof(cmd), &cmd);
 }
index 3424315dd876de13f9a64da6b7703f51103ce87e..ad7ad720d2e768a17145e1f04a9f294a5fda8e2f 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
  *
  * 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
@@ -33,6 +34,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -484,16 +486,18 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        switch (vif->type) {
        case NL80211_IFTYPE_P2P_DEVICE:
                iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE,
-                                     IWL_MVM_TX_FIFO_VO, wdg_timeout);
+                                     IWL_MVM_OFFCHANNEL_QUEUE,
+                                     IWL_MVM_TX_FIFO_VO, 0, wdg_timeout);
                break;
        case NL80211_IFTYPE_AP:
-               iwl_mvm_enable_ac_txq(mvm, vif->cab_queue,
-                                     IWL_MVM_TX_FIFO_MCAST, wdg_timeout);
+               iwl_mvm_enable_ac_txq(mvm, vif->cab_queue, vif->cab_queue,
+                                     IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout);
                /* fall through */
        default:
                for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
                        iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac],
-                                             iwl_mvm_ac_to_tx_fifo[ac],
+                                             vif->hw_queue[ac],
+                                             iwl_mvm_ac_to_tx_fifo[ac], 0,
                                              wdg_timeout);
                break;
        }
@@ -509,14 +513,19 @@ void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 
        switch (vif->type) {
        case NL80211_IFTYPE_P2P_DEVICE:
-               iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, 0);
+               iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE,
+                                   IWL_MVM_OFFCHANNEL_QUEUE, IWL_MAX_TID_COUNT,
+                                   0);
                break;
        case NL80211_IFTYPE_AP:
-               iwl_mvm_disable_txq(mvm, vif->cab_queue, 0);
+               iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue,
+                                   IWL_MAX_TID_COUNT, 0);
                /* fall through */
        default:
                for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
-                       iwl_mvm_disable_txq(mvm, vif->hw_queue[ac], 0);
+                       iwl_mvm_disable_txq(mvm, vif->hw_queue[ac],
+                                           vif->hw_queue[ac],
+                                           IWL_MAX_TID_COUNT, 0);
        }
 }
 
@@ -834,6 +843,9 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
        ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval);
        ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid);
 
+       if (vif->probe_req_reg && vif->bss_conf.assoc && vif->p2p)
+               cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
+
        return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
 }
 
@@ -1128,6 +1140,7 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
                                   struct ieee80211_vif *vif,
                                   u32 action)
 {
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mac_ctx_cmd cmd = {};
 
        WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p);
@@ -1137,10 +1150,16 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
 
        /*
         * pass probe requests and beacons from other APs (needed
-        * for ht protection)
+        * for ht protection); when there're no any associated station
+        * don't ask FW to pass beacons to prevent unnecessary wake-ups.
         */
-       cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST |
-                                       MAC_FILTER_IN_BEACON);
+       cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
+       if (mvmvif->ap_assoc_sta_count) {
+               cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
+               IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n");
+       } else {
+               IWL_DEBUG_HC(mvm, "No need to receive beacons\n");
+       }
 
        /* Fill the data specific for ap mode */
        iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap,
index 7c2944a72470b92acdca39a6f7c13b845b34842a..1fb684693040eac8130cf62c348b0bf96dd4f005 100644 (file)
@@ -572,6 +572,14 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        /* we create the 802.11 header and zero length SSID IE. */
        hw->wiphy->max_sched_scan_ie_len =
                SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
+       hw->wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS;
+       hw->wiphy->max_sched_scan_plan_interval = U16_MAX;
+
+       /*
+        * the firmware uses u8 for num of iterations, but 0xff is saved for
+        * infinite loop, so the maximum number of iterations is actually 254.
+        */
+       hw->wiphy->max_sched_scan_plan_iterations = 254;
 
        hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
                               NL80211_FEATURE_LOW_PRIORITY_SCAN |
@@ -820,7 +828,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif,
                                    enum ieee80211_ampdu_mlme_action action,
                                    struct ieee80211_sta *sta, u16 tid,
-                                   u16 *ssn, u8 buf_size)
+                                   u16 *ssn, u8 buf_size, bool amsdu)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
@@ -1129,6 +1137,12 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 
        lockdep_assert_held(&mvm->mutex);
 
+       /* there's no point in fw dump if the bus is dead */
+       if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) {
+               IWL_ERR(mvm, "Skip fw error dump since bus is dead\n");
+               return;
+       }
+
        if (mvm->fw_dump_trig &&
            mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)
                monitor_dump_only = true;
@@ -1192,6 +1206,13 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        if (sram2_len)
                file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len;
 
+       /* Make room for fw's virtual image pages, if it exists */
+       if (mvm->fw->img[mvm->cur_ucode].paging_mem_size)
+               file_len += mvm->num_of_paging_blk *
+                       (sizeof(*dump_data) +
+                        sizeof(struct iwl_fw_error_dump_paging) +
+                        PAGING_BLOCK_SIZE);
+
        /* If we only want a monitor dump, reset the file length */
        if (monitor_dump_only) {
                file_len = sizeof(*dump_file) + sizeof(*dump_data) +
@@ -1302,6 +1323,26 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
                                         dump_mem->data, IWL8260_ICCM_LEN);
        }
 
+       /* Dump fw's virtual image */
+       if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) {
+               u32 i;
+
+               for (i = 1; i < mvm->num_of_paging_blk + 1; i++) {
+                       struct iwl_fw_error_dump_paging *paging;
+                       struct page *pages =
+                               mvm->fw_paging_db[i].fw_paging_block;
+
+                       dump_data = iwl_fw_error_next_data(dump_data);
+                       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
+                       dump_data->len = cpu_to_le32(sizeof(*paging) +
+                                                    PAGING_BLOCK_SIZE);
+                       paging = (void *)dump_data->data;
+                       paging->index = cpu_to_le32(i);
+                       memcpy(paging->data, page_address(pages),
+                              PAGING_BLOCK_SIZE);
+               }
+       }
+
 dump_trans_data:
        fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans,
                                                       mvm->fw_dump_trig);
@@ -1577,20 +1618,6 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
        return NULL;
 }
 
-static int iwl_mvm_set_tx_power_old(struct iwl_mvm *mvm,
-                                   struct ieee80211_vif *vif, s8 tx_power)
-{
-       /* FW is in charge of regulatory enforcement */
-       struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
-               .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id,
-               .pwr_restriction = cpu_to_le16(tx_power),
-       };
-
-       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0,
-                                   sizeof(reduce_txpwr_cmd),
-                                   &reduce_txpwr_cmd);
-}
-
 static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                                s16 tx_power)
 {
@@ -1602,9 +1629,6 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        };
        int len = sizeof(cmd);
 
-       if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TX_POWER_DEV))
-               return iwl_mvm_set_tx_power_old(mvm, vif, tx_power);
-
        if (tx_power == IWL_DEFAULT_MAX_TX_POWER)
                cmd.v2.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER);
 
@@ -1771,7 +1795,7 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
                 * Flush them here.
                 */
                mutex_lock(&mvm->mutex);
-               iwl_mvm_flush_tx_path(mvm, tfd_msk, true);
+               iwl_mvm_flush_tx_path(mvm, tfd_msk, 0);
                mutex_unlock(&mvm->mutex);
 
                /*
@@ -1972,6 +1996,27 @@ out:
        *total_flags = 0;
 }
 
+static void iwl_mvm_config_iface_filter(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       unsigned int filter_flags,
+                                       unsigned int changed_flags)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       /* We support only filter for probe requests */
+       if (!(changed_flags & FIF_PROBE_REQ))
+               return;
+
+       /* Supported only for p2p client interfaces */
+       if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc ||
+           !vif->p2p)
+               return;
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+       mutex_unlock(&mvm->mutex);
+}
+
 #ifdef CONFIG_IWLWIFI_BCAST_FILTERING
 struct iwl_bcast_iter_data {
        struct iwl_mvm *mvm;
@@ -2319,6 +2364,8 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        if (vif->type == NL80211_IFTYPE_AP)
                iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
 
+       mvmvif->ap_assoc_sta_count = 0;
+
        /* Add the mac context */
        ret = iwl_mvm_mac_ctxt_add(mvm, vif);
        if (ret)
@@ -2614,6 +2661,7 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
                                       struct ieee80211_sta *sta)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 
        /*
@@ -2628,6 +2676,12 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
        if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id]))
                rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
                                   ERR_PTR(-ENOENT));
+
+       if (mvm_sta->vif->type == NL80211_IFTYPE_AP) {
+               mvmvif->ap_assoc_sta_count--;
+               iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+       }
+
        mutex_unlock(&mvm->mutex);
 }
 
@@ -3906,7 +3960,7 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
        }
 
        if (drop) {
-               if (iwl_mvm_flush_tx_path(mvm, msk, true))
+               if (iwl_mvm_flush_tx_path(mvm, msk, 0))
                        IWL_ERR(mvm, "flush request fail\n");
                mutex_unlock(&mvm->mutex);
        } else {
@@ -4143,6 +4197,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
        .config = iwl_mvm_mac_config,
        .prepare_multicast = iwl_mvm_prepare_multicast,
        .configure_filter = iwl_mvm_configure_filter,
+       .config_iface_filter = iwl_mvm_config_iface_filter,
        .bss_info_changed = iwl_mvm_bss_info_changed,
        .hw_scan = iwl_mvm_mac_hw_scan,
        .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan,
index c754051a4ceacf138d4e6399c9cc679d8a9979c0..c6327cd1d071a862a61fced402b1376e5812d86c 100644 (file)
@@ -82,7 +82,6 @@
 #include "constants.h"
 #include "tof.h"
 
-#define IWL_INVALID_MAC80211_QUEUE     0xff
 #define IWL_MVM_MAX_ADDRESSES          5
 /* RSSI offset for WkP */
 #define IWL_RSSI_OFFSET 50
@@ -323,11 +322,11 @@ enum iwl_bt_force_ant_mode {
 struct iwl_mvm_vif_bf_data {
        bool bf_enabled;
        bool ba_enabled;
-       s8 ave_beacon_signal;
-       s8 last_cqm_event;
-       s8 bt_coex_min_thold;
-       s8 bt_coex_max_thold;
-       s8 last_bt_coex_event;
+       int ave_beacon_signal;
+       int last_cqm_event;
+       int bt_coex_min_thold;
+       int bt_coex_max_thold;
+       int last_bt_coex_event;
 };
 
 /**
@@ -338,6 +337,8 @@ struct iwl_mvm_vif_bf_data {
  * @bssid: BSSID for this (client) interface
  * @associated: indicates that we're currently associated, used only for
  *     managing the firmware state in iwl_mvm_bss_info_changed_station()
+ * @ap_assoc_sta_count: count of stations associated to us - valid only
+ *     if VIF type is AP
  * @uploaded: indicates the MAC context has been added to the device
  * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface
  *     should get quota etc.
@@ -367,6 +368,7 @@ struct iwl_mvm_vif {
 
        u8 bssid[ETH_ALEN];
        bool associated;
+       u8 ap_assoc_sta_count;
 
        bool uploaded;
        bool ap_ibss_active;
@@ -602,7 +604,14 @@ struct iwl_mvm {
                u64 on_time_scan;
        } radio_stats, accu_radio_stats;
 
-       u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
+       struct {
+               /* Map to HW queue */
+               u32 hw_queue_to_mac80211;
+               u8 hw_queue_refcount;
+               bool setup_reserved;
+               u16 tid_bitmap; /* Bitmap of the TIDs mapped to this queue */
+       } queue_info[IWL_MAX_HW_QUEUES];
+       spinlock_t queue_info_lock; /* For syncing queue mgmt operations */
        atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES];
 
        const char *nvm_file_name;
@@ -679,6 +688,7 @@ struct iwl_mvm {
        struct debugfs_blob_wrapper nvm_sw_blob;
        struct debugfs_blob_wrapper nvm_calib_blob;
        struct debugfs_blob_wrapper nvm_prod_blob;
+       struct debugfs_blob_wrapper nvm_phy_sku_blob;
 
        struct iwl_mvm_frame_stats drv_rx_stats;
        spinlock_t drv_stats_lock;
@@ -912,6 +922,12 @@ static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm)
                           IWL_UCODE_TLV_CAPA_D0I3_SUPPORT);
 }
 
+static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm)
+{
+       return fw_has_capa(&mvm->fw->ucode_capa,
+                          IWL_UCODE_TLV_CAPA_DQA_SUPPORT);
+}
+
 static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm)
 {
        bool nvm_lar = mvm->nvm_data->lar_enabled;
@@ -939,11 +955,6 @@ static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm)
                           IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC);
 }
 
-static inline bool iwl_mvm_is_scd_cfg_supported(struct iwl_mvm *mvm)
-{
-       return fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_SCD_CFG);
-}
-
 static inline bool iwl_mvm_bt_is_plcr_supported(struct iwl_mvm *mvm)
 {
        return fw_has_capa(&mvm->fw->ucode_capa,
@@ -964,6 +975,12 @@ static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm)
                           IWL_UCODE_TLV_CAPA_CSUM_SUPPORT);
 }
 
+static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm)
+{
+       /* firmware flag isn't defined yet */
+       return false;
+}
+
 extern const u8 iwl_mvm_ac_to_tx_fifo[];
 
 struct iwl_rate_info {
@@ -1019,7 +1036,7 @@ const char *iwl_mvm_get_tx_fail_reason(u32 status);
 #else
 static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; }
 #endif
-int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync);
+int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags);
 void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm);
 
 static inline void iwl_mvm_set_tx_cmd_ccmp(struct ieee80211_tx_info *info,
@@ -1136,7 +1153,6 @@ void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif);
 unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
                                         struct ieee80211_vif *exclude_vif);
-
 /* Bindings */
 int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -1349,14 +1365,20 @@ static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif)
 }
 
 /* hw scheduler queue config */
-void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn,
-                       const struct iwl_trans_txq_scd_cfg *cfg,
+void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+                       u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg,
                        unsigned int wdg_timeout);
-void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, u8 flags);
+/*
+ * Disable a TXQ.
+ * Note that in non-DQA mode the %mac80211_queue and %tid params are ignored.
+ */
+void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+                        u8 tid, u8 flags);
+int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq);
 
 static inline
-void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue,
-                          u8 fifo, unsigned int wdg_timeout)
+void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+                          u8 fifo, u16 ssn, unsigned int wdg_timeout)
 {
        struct iwl_trans_txq_scd_cfg cfg = {
                .fifo = fifo,
@@ -1365,13 +1387,13 @@ void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue,
                .frame_limit = IWL_FRAME_LIMIT,
        };
 
-       iwl_mvm_enable_txq(mvm, queue, 0, &cfg, wdg_timeout);
+       iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout);
 }
 
 static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue,
-                                         int fifo, int sta_id, int tid,
-                                         int frame_limit, u16 ssn,
-                                         unsigned int wdg_timeout)
+                                         int mac80211_queue, int fifo,
+                                         int sta_id, int tid, int frame_limit,
+                                         u16 ssn, unsigned int wdg_timeout)
 {
        struct iwl_trans_txq_scd_cfg cfg = {
                .fifo = fifo,
@@ -1381,7 +1403,7 @@ static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue,
                .aggregate = true,
        };
 
-       iwl_mvm_enable_txq(mvm, queue, ssn, &cfg, wdg_timeout);
+       iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout);
 }
 
 /* Thermal management and CT-kill */
index 328187da7541dff2233c1f020c559522e7c4f8a7..2ee0f6fe56a1f58071247ca872479f587516668d 100644 (file)
@@ -316,7 +316,8 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
        return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
                                  regulatory, mac_override, phy_sku,
                                  mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant,
-                                 lar_enabled, mac_addr0, mac_addr1);
+                                 lar_enabled, mac_addr0, mac_addr1,
+                                 mvm->trans->hw_id);
 }
 
 #define MAX_NVM_FILE_LEN       16384
@@ -482,6 +483,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
                        ret = -ENOMEM;
                        break;
                }
+               kfree(mvm->nvm_sections[section_id].data);
                mvm->nvm_sections[section_id].data = temp;
                mvm->nvm_sections[section_id].length = section_size;
 
@@ -563,6 +565,10 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
                                mvm->nvm_prod_blob.data = temp;
                                mvm->nvm_prod_blob.size  = ret;
                                break;
+                       case NVM_SECTION_TYPE_PHY_SKU:
+                               mvm->nvm_phy_sku_blob.data = temp;
+                               mvm->nvm_phy_sku_blob.size  = ret;
+                               break;
                        default:
                                if (section == mvm->cfg->nvm_hw_section_num) {
                                        mvm->nvm_hw_blob.data = temp;
index f0cb092f980ec26b26e9ca3c6cadf813f39de853..13c97f665ba889eb3f5824341cc8937190d665a8 100644 (file)
@@ -89,6 +89,7 @@ MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
 MODULE_LICENSE("GPL");
 
 static const struct iwl_op_mode_ops iwl_mvm_ops;
+static const struct iwl_op_mode_ops iwl_mvm_ops_mq;
 
 struct iwl_mvm_mod_params iwlmvm_mod_params = {
        .power_scheme = IWL_POWER_SCHEME_BPS,
@@ -222,7 +223,6 @@ struct iwl_rx_handlers {
  * called from a worker with mvm->mutex held.
  */
 static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
-       RX_HANDLER(REPLY_RX_PHY_CMD, iwl_mvm_rx_rx_phy_cmd, false),
        RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false),
        RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
 
@@ -257,6 +257,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
        RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
                   iwl_mvm_power_uapsd_misbehaving_ap_notif, false),
        RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true),
+       RX_HANDLER_GRP(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE,
+                      iwl_mvm_temp_notif, true),
 
        RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif,
                   true),
@@ -271,6 +273,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
 static const char *const iwl_mvm_cmd_strings[REPLY_MAX + 1] = {
        CMD(MVM_ALIVE),
        CMD(REPLY_ERROR),
+       CMD(ECHO_CMD),
        CMD(INIT_COMPLETE_NOTIF),
        CMD(PHY_CONTEXT_CMD),
        CMD(MGMT_MCAST_KEY),
@@ -422,7 +425,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size;
 
        op_mode = hw->priv;
-       op_mode->ops = &iwl_mvm_ops;
 
        mvm = IWL_OP_MODE_GET_MVM(op_mode);
        mvm->dev = trans->dev;
@@ -431,6 +433,15 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        mvm->fw = fw;
        mvm->hw = hw;
 
+       if (iwl_mvm_has_new_rx_api(mvm)) {
+               op_mode->ops = &iwl_mvm_ops_mq;
+       } else {
+               op_mode->ops = &iwl_mvm_ops;
+
+               if (WARN_ON(trans->num_rx_queues > 1))
+                       goto out_free;
+       }
+
        mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0;
 
        mvm->aux_queue = 15;
@@ -451,6 +462,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        INIT_LIST_HEAD(&mvm->aux_roc_te_list);
        INIT_LIST_HEAD(&mvm->async_handlers_list);
        spin_lock_init(&mvm->time_event_lock);
+       spin_lock_init(&mvm->queue_info_lock);
 
        INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
        INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
@@ -618,6 +630,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
        kfree(mvm->d3_resume_sram);
        if (mvm->nd_config) {
                kfree(mvm->nd_config->match_sets);
+               kfree(mvm->nd_config->scan_plans);
                kfree(mvm->nd_config);
                mvm->nd_config = NULL;
        }
@@ -717,18 +730,11 @@ static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm,
        }
 }
 
-static void iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode,
-                               struct napi_struct *napi,
-                               struct iwl_rx_cmd_buffer *rxb)
+static void iwl_mvm_rx_common(struct iwl_mvm *mvm,
+                             struct iwl_rx_cmd_buffer *rxb,
+                             struct iwl_rx_packet *pkt)
 {
-       struct iwl_rx_packet *pkt = rxb_addr(rxb);
-       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-       u8 i;
-
-       if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) {
-               iwl_mvm_rx_rx_mpdu(mvm, napi, rxb);
-               return;
-       }
+       int i;
 
        iwl_mvm_rx_check_trigger(mvm, pkt);
 
@@ -768,40 +774,84 @@ static void iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode,
        }
 }
 
+static void iwl_mvm_rx(struct iwl_op_mode *op_mode,
+                      struct napi_struct *napi,
+                      struct iwl_rx_cmd_buffer *rxb)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+       if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD))
+               iwl_mvm_rx_rx_mpdu(mvm, napi, rxb);
+       else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD)
+               iwl_mvm_rx_rx_phy_cmd(mvm, rxb);
+       else
+               iwl_mvm_rx_common(mvm, rxb, pkt);
+}
+
+static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode,
+                         struct napi_struct *napi,
+                         struct iwl_rx_cmd_buffer *rxb)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+       if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD))
+               iwl_mvm_rx_rx_mpdu(mvm, napi, rxb);
+       else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD)
+               iwl_mvm_rx_rx_phy_cmd(mvm, rxb);
+       else
+               iwl_mvm_rx_common(mvm, rxb, pkt);
+}
+
 static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
 {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-       int mq = mvm->queue_to_mac80211[queue];
+       unsigned long mq;
+       int q;
 
-       if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
-               return;
+       spin_lock_bh(&mvm->queue_info_lock);
+       mq = mvm->queue_info[queue].hw_queue_to_mac80211;
+       spin_unlock_bh(&mvm->queue_info_lock);
 
-       if (atomic_inc_return(&mvm->mac80211_queue_stop_count[mq]) > 1) {
-               IWL_DEBUG_TX_QUEUES(mvm,
-                                   "queue %d (mac80211 %d) already stopped\n",
-                                   queue, mq);
+       if (WARN_ON_ONCE(!mq))
                return;
-       }
 
-       ieee80211_stop_queue(mvm->hw, mq);
+       for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) {
+               if (atomic_inc_return(&mvm->mac80211_queue_stop_count[q]) > 1) {
+                       IWL_DEBUG_TX_QUEUES(mvm,
+                                           "queue %d (mac80211 %d) already stopped\n",
+                                           queue, q);
+                       continue;
+               }
+
+               ieee80211_stop_queue(mvm->hw, q);
+       }
 }
 
 static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
 {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-       int mq = mvm->queue_to_mac80211[queue];
+       unsigned long mq;
+       int q;
 
-       if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
-               return;
+       spin_lock_bh(&mvm->queue_info_lock);
+       mq = mvm->queue_info[queue].hw_queue_to_mac80211;
+       spin_unlock_bh(&mvm->queue_info_lock);
 
-       if (atomic_dec_return(&mvm->mac80211_queue_stop_count[mq]) > 0) {
-               IWL_DEBUG_TX_QUEUES(mvm,
-                                   "queue %d (mac80211 %d) still stopped\n",
-                                   queue, mq);
+       if (WARN_ON_ONCE(!mq))
                return;
-       }
 
-       ieee80211_wake_queue(mvm->hw, mq);
+       for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) {
+               if (atomic_dec_return(&mvm->mac80211_queue_stop_count[q]) > 0) {
+                       IWL_DEBUG_TX_QUEUES(mvm,
+                                           "queue %d (mac80211 %d) still stopped\n",
+                                           queue, q);
+                       continue;
+               }
+
+               ieee80211_wake_queue(mvm->hw, q);
+       }
 }
 
 void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state)
@@ -1146,12 +1196,17 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
        /* make sure we have no running tx while configuring the seqno */
        synchronize_net();
 
-       iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, &d0i3_iter_data);
-       ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags,
-                                  sizeof(wowlan_config_cmd),
-                                  &wowlan_config_cmd);
-       if (ret)
-               return ret;
+       /* configure wowlan configuration only if needed */
+       if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) {
+               iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd,
+                                       &d0i3_iter_data);
+
+               ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags,
+                                          sizeof(wowlan_config_cmd),
+                                          &wowlan_config_cmd);
+               if (ret)
+                       return ret;
+       }
 
        return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD,
                                    flags | CMD_MAKE_TRANS_IDLE,
@@ -1258,7 +1313,7 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
        };
        struct iwl_wowlan_status *status;
        int ret;
-       u32 handled_reasons, wakeup_reasons;
+       u32 handled_reasons, wakeup_reasons = 0;
        __le16 *qos_seq = NULL;
 
        mutex_lock(&mvm->mutex);
@@ -1290,6 +1345,9 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
 out:
        iwl_mvm_d0i3_enable_tx(mvm, qos_seq);
 
+       IWL_DEBUG_INFO(mvm, "d0i3 exit completed (wakeup reasons: 0x%x)\n",
+                      wakeup_reasons);
+
        /* qos_seq might point inside resp_pkt, so free it only now */
        if (get_status_cmd.resp_pkt)
                iwl_free_resp(&get_status_cmd);
@@ -1339,17 +1397,38 @@ int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
        return _iwl_mvm_exit_d0i3(mvm);
 }
 
+#define IWL_MVM_COMMON_OPS                                     \
+       /* these could be differentiated */                     \
+       .queue_full = iwl_mvm_stop_sw_queue,                    \
+       .queue_not_full = iwl_mvm_wake_sw_queue,                \
+       .hw_rf_kill = iwl_mvm_set_hw_rfkill_state,              \
+       .free_skb = iwl_mvm_free_skb,                           \
+       .nic_error = iwl_mvm_nic_error,                         \
+       .cmd_queue_full = iwl_mvm_cmd_queue_full,               \
+       .nic_config = iwl_mvm_nic_config,                       \
+       .enter_d0i3 = iwl_mvm_enter_d0i3,                       \
+       .exit_d0i3 = iwl_mvm_exit_d0i3,                         \
+       /* as we only register one, these MUST be common! */    \
+       .start = iwl_op_mode_mvm_start,                         \
+       .stop = iwl_op_mode_mvm_stop
+
 static const struct iwl_op_mode_ops iwl_mvm_ops = {
-       .start = iwl_op_mode_mvm_start,
-       .stop = iwl_op_mode_mvm_stop,
-       .rx = iwl_mvm_rx_dispatch,
-       .queue_full = iwl_mvm_stop_sw_queue,
-       .queue_not_full = iwl_mvm_wake_sw_queue,
-       .hw_rf_kill = iwl_mvm_set_hw_rfkill_state,
-       .free_skb = iwl_mvm_free_skb,
-       .nic_error = iwl_mvm_nic_error,
-       .cmd_queue_full = iwl_mvm_cmd_queue_full,
-       .nic_config = iwl_mvm_nic_config,
-       .enter_d0i3 = iwl_mvm_enter_d0i3,
-       .exit_d0i3 = iwl_mvm_exit_d0i3,
+       IWL_MVM_COMMON_OPS,
+       .rx = iwl_mvm_rx,
+};
+
+static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode,
+                             struct napi_struct *napi,
+                             struct iwl_rx_cmd_buffer *rxb,
+                             unsigned int queue)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+       iwl_mvm_rx_rx_mpdu(mvm, napi, rxb);
+}
+
+static const struct iwl_op_mode_ops iwl_mvm_ops_mq = {
+       IWL_MVM_COMMON_OPS,
+       .rx = iwl_mvm_rx_mq,
+       .rx_rss = iwl_mvm_rx_mq_rss,
 };
index 4645877882a6af6ee51a87838f9f3b57e3ab0054..bed9696ee410d0ab4694761c87069ebde558900e 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015        Intel Deutschland GmbH
  *
  * 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
@@ -33,6 +34,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015        Intel Deutschland GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -306,13 +308,51 @@ static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif)
        return radar_detect;
 }
 
+static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm,
+                                          struct ieee80211_vif *vif,
+                                          struct iwl_mac_power_cmd *cmd,
+                                          bool host_awake)
+{
+       int dtimper = vif->bss_conf.dtim_period ?: 1;
+       int skip;
+
+       /* disable, in case we're supposed to override */
+       cmd->skip_dtim_periods = 0;
+       cmd->flags &= ~cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+
+       if (iwl_mvm_power_is_radar(vif))
+               return;
+
+       if (dtimper >= 10)
+               return;
+
+       /* TODO: check that multicast wake lock is off */
+
+       if (host_awake) {
+               if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_LP)
+                       return;
+               skip = 2;
+       } else {
+               int dtimper_tu = dtimper * vif->bss_conf.beacon_int;
+
+               if (WARN_ON(!dtimper_tu))
+                       return;
+               /* configure skip over dtim up to 306TU - 314 msec */
+               skip = max_t(u8, 1, 306 / dtimper_tu);
+       }
+
+       /* the firmware really expects "look at every X DTIMs", so add 1 */
+       cmd->skip_dtim_periods = 1 + skip;
+       cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+}
+
 static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif,
-                                   struct iwl_mac_power_cmd *cmd)
+                                   struct iwl_mac_power_cmd *cmd,
+                                   bool host_awake)
 {
        int dtimper, bi;
        int keep_alive;
-       bool radar_detect = false;
        struct iwl_mvm_vif *mvmvif __maybe_unused =
                iwl_mvm_vif_from_mac80211(vif);
 
@@ -337,8 +377,13 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
 
-       if (!vif->bss_conf.ps || !mvmvif->pm_enabled ||
-           (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p))
+       if (!vif->bss_conf.ps || !mvmvif->pm_enabled)
+               return;
+
+       if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p &&
+           (!fw_has_capa(&mvm->fw->ucode_capa,
+                        IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS) ||
+            !IWL_MVM_P2P_LOWLATENCY_PS_ENABLE))
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@@ -350,27 +395,25 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
        }
 
-       /* Check if radar detection is required on current channel */
-       radar_detect = iwl_mvm_power_is_radar(vif);
+       iwl_mvm_power_config_skip_dtim(mvm, vif, cmd, host_awake);
 
-       /* Check skip over DTIM conditions */
-       if (!radar_detect && (dtimper < 10) &&
-           (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
-            mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
-               cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
-               cmd->skip_dtim_periods = 3;
-       }
-
-       if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
+       if (!host_awake) {
                cmd->rx_data_timeout =
-                       cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT);
+                       cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
                cmd->tx_data_timeout =
-                       cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT);
+                       cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
+       } else if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p &&
+                  fw_has_capa(&mvm->fw->ucode_capa,
+                              IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS)) {
+               cmd->tx_data_timeout =
+                       cpu_to_le32(IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT);
+               cmd->rx_data_timeout =
+                       cpu_to_le32(IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT);
        } else {
                cmd->rx_data_timeout =
-                       cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
+                       cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT);
                cmd->tx_data_timeout =
-                       cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
+                       cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT);
        }
 
        if (iwl_mvm_power_allow_uapsd(mvm, vif))
@@ -427,7 +470,8 @@ static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
 {
        struct iwl_mac_power_cmd cmd = {};
 
-       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+       iwl_mvm_power_build_cmd(mvm, vif, &cmd,
+                               mvm->cur_ucode != IWL_UCODE_WOWLAN);
        iwl_mvm_power_log(mvm, &cmd);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd));
@@ -440,14 +484,14 @@ static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
 int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
 {
        struct iwl_device_power_cmd cmd = {
-               .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
+               .flags = 0,
        };
 
        if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
                mvm->ps_disabled = true;
 
-       if (mvm->ps_disabled)
-               cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK);
+       if (!mvm->ps_disabled)
+               cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 :
@@ -963,25 +1007,8 @@ int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
        if (!vif->bss_conf.assoc)
                return 0;
 
-       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
-       if (enable) {
-               /* configure skip over dtim up to 306TU - 314 msec */
-               int dtimper = vif->bss_conf.dtim_period ?: 1;
-               int dtimper_tu = dtimper * vif->bss_conf.beacon_int;
-               bool radar_detect = iwl_mvm_power_is_radar(vif);
+       iwl_mvm_power_build_cmd(mvm, vif, &cmd, !enable);
 
-               if (WARN_ON(!dtimper_tu))
-                       return 0;
-
-               /* Check skip over DTIM conditions */
-               /* TODO: check that multicast wake lock is off */
-               if (!radar_detect && (dtimper < 10)) {
-                       cmd.skip_dtim_periods = 306 / dtimper_tu;
-                       if (cmd.skip_dtim_periods)
-                               cmd.flags |= cpu_to_le16(
-                                       POWER_FLAGS_SKIP_OVER_DTIM_MSK);
-               }
-       }
        iwl_mvm_power_log(mvm, &cmd);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd));
index 5ae9c8aa868fa445063e956a26ac1444caf5d8dd..d1ad10391b473fb0ff288f0311d9b7bd6f14921d 100644 (file)
@@ -177,9 +177,6 @@ static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 
        mvmsta = iwl_mvm_sta_from_mac80211(sta);
        mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
-       if (IWL_MVM_RS_DISABLE_P2P_MIMO &&
-           iwl_mvm_vif_low_latency(mvmvif) && mvmsta->vif->p2p)
-               return false;
 
        if (mvm->nvm_data->sku_cap_mimo_disabled)
                return false;
@@ -524,14 +521,56 @@ static const char *rs_pretty_lq_type(enum iwl_table_type type)
        return lq_types[type];
 }
 
+static char *rs_pretty_rate(const struct rs_rate *rate)
+{
+       static char buf[40];
+       static const char * const legacy_rates[] = {
+               [IWL_RATE_1M_INDEX] = "1M",
+               [IWL_RATE_2M_INDEX] = "2M",
+               [IWL_RATE_5M_INDEX] = "5.5M",
+               [IWL_RATE_11M_INDEX] = "11M",
+               [IWL_RATE_6M_INDEX] = "6M",
+               [IWL_RATE_9M_INDEX] = "9M",
+               [IWL_RATE_12M_INDEX] = "12M",
+               [IWL_RATE_18M_INDEX] = "18M",
+               [IWL_RATE_24M_INDEX] = "24M",
+               [IWL_RATE_36M_INDEX] = "36M",
+               [IWL_RATE_48M_INDEX] = "48M",
+               [IWL_RATE_54M_INDEX] = "54M",
+       };
+       static const char *const ht_vht_rates[] = {
+               [IWL_RATE_MCS_0_INDEX] = "MCS0",
+               [IWL_RATE_MCS_1_INDEX] = "MCS1",
+               [IWL_RATE_MCS_2_INDEX] = "MCS2",
+               [IWL_RATE_MCS_3_INDEX] = "MCS3",
+               [IWL_RATE_MCS_4_INDEX] = "MCS4",
+               [IWL_RATE_MCS_5_INDEX] = "MCS5",
+               [IWL_RATE_MCS_6_INDEX] = "MCS6",
+               [IWL_RATE_MCS_7_INDEX] = "MCS7",
+               [IWL_RATE_MCS_8_INDEX] = "MCS8",
+               [IWL_RATE_MCS_9_INDEX] = "MCS9",
+       };
+       const char *rate_str;
+
+       if (is_type_legacy(rate->type))
+               rate_str = legacy_rates[rate->index];
+       else if (is_type_ht(rate->type) || is_type_vht(rate->type))
+               rate_str = ht_vht_rates[rate->index];
+       else
+               rate_str = "BAD_RATE";
+
+       sprintf(buf, "(%s|%s|%s)", rs_pretty_lq_type(rate->type),
+               rs_pretty_ant(rate->ant), rate_str);
+       return buf;
+}
+
 static inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate,
                                const char *prefix)
 {
        IWL_DEBUG_RATE(mvm,
-                      "%s: (%s: %d) ANT: %s BW: %d SGI: %d LDPC: %d STBC: %d\n",
-                      prefix, rs_pretty_lq_type(rate->type),
-                      rate->index, rs_pretty_ant(rate->ant),
-                      rate->bw, rate->sgi, rate->ldpc, rate->stbc);
+                      "%s: %s BW: %d SGI: %d LDPC: %d STBC: %d\n",
+                      prefix, rs_pretty_rate(rate), rate->bw,
+                      rate->sgi, rate->ldpc, rate->stbc);
 }
 
 static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window)
@@ -562,8 +601,8 @@ static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
 }
 
 static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
-                                     struct iwl_lq_sta *lq_data, u8 tid,
-                                     struct ieee80211_sta *sta)
+                                    struct iwl_lq_sta *lq_data, u8 tid,
+                                    struct ieee80211_sta *sta)
 {
        int ret = -EAGAIN;
 
@@ -1485,7 +1524,7 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm,
        u32 target_tpt;
        int rate_idx;
 
-       if (success_ratio > IWL_MVM_RS_SR_NO_DECREASE) {
+       if (success_ratio >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) {
                target_tpt = 100 * expected_current_tpt;
                IWL_DEBUG_RATE(mvm,
                               "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n",
@@ -1493,7 +1532,7 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm,
        } else {
                target_tpt = lq_sta->last_tpt;
                IWL_DEBUG_RATE(mvm,
-                              "SR %d not thag good. Find rate exceeding ACTUAL_TPT %d\n",
+                              "SR %d not that good. Find rate exceeding ACTUAL_TPT %d\n",
                               success_ratio, target_tpt);
        }
 
@@ -1622,6 +1661,51 @@ static void rs_update_rate_tbl(struct iwl_mvm *mvm,
        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
 }
 
+static bool rs_tweak_rate_tbl(struct iwl_mvm *mvm,
+                             struct ieee80211_sta *sta,
+                             struct iwl_lq_sta *lq_sta,
+                             struct iwl_scale_tbl_info *tbl,
+                             enum rs_action scale_action)
+{
+       if (sta->bandwidth != IEEE80211_STA_RX_BW_80)
+               return false;
+
+       if (!is_vht_siso(&tbl->rate))
+               return false;
+
+       if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_80) &&
+           (tbl->rate.index == IWL_RATE_MCS_0_INDEX) &&
+           (scale_action == RS_ACTION_DOWNSCALE)) {
+               tbl->rate.bw = RATE_MCS_CHAN_WIDTH_20;
+               tbl->rate.index = IWL_RATE_MCS_4_INDEX;
+               IWL_DEBUG_RATE(mvm, "Switch 80Mhz SISO MCS0 -> 20Mhz MCS4\n");
+               goto tweaked;
+       }
+
+       /* Go back to 80Mhz MCS1 only if we've established that 20Mhz MCS5 is
+        * sustainable, i.e. we're past the test window. We can't go back
+        * if MCS5 is just tested as this will happen always after switching
+        * to 20Mhz MCS4 because the rate stats are cleared.
+        */
+       if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_20) &&
+           (((tbl->rate.index == IWL_RATE_MCS_5_INDEX) &&
+            (scale_action == RS_ACTION_STAY)) ||
+            ((tbl->rate.index > IWL_RATE_MCS_5_INDEX) &&
+             (scale_action == RS_ACTION_UPSCALE)))) {
+               tbl->rate.bw = RATE_MCS_CHAN_WIDTH_80;
+               tbl->rate.index = IWL_RATE_MCS_1_INDEX;
+               IWL_DEBUG_RATE(mvm, "Switch 20Mhz SISO MCS5 -> 80Mhz MCS1\n");
+               goto tweaked;
+       }
+
+       return false;
+
+tweaked:
+       rs_set_expected_tpt_table(lq_sta, tbl);
+       rs_rate_scale_clear_tbl_windows(mvm, tbl);
+       return true;
+}
+
 static enum rs_column rs_get_next_column(struct iwl_mvm *mvm,
                                         struct iwl_lq_sta *lq_sta,
                                         struct ieee80211_sta *sta,
@@ -2174,9 +2258,9 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
        if ((fail_count < IWL_MVM_RS_RATE_MIN_FAILURE_TH) &&
            (window->success_counter < IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) {
                IWL_DEBUG_RATE(mvm,
-                              "(%s: %d): Test Window: succ %d total %d\n",
-                              rs_pretty_lq_type(rate->type),
-                              index, window->success_counter, window->counter);
+                              "%s: Test Window: succ %d total %d\n",
+                              rs_pretty_rate(rate),
+                              window->success_counter, window->counter);
 
                /* Can't calculate this yet; not enough history */
                window->average_tpt = IWL_INVALID_VALUE;
@@ -2253,8 +2337,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                high_tpt = tbl->win[high].average_tpt;
 
        IWL_DEBUG_RATE(mvm,
-                      "(%s: %d): cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n",
-                      rs_pretty_lq_type(rate->type), index, current_tpt, sr,
+                      "%s: cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n",
+                      rs_pretty_rate(rate), current_tpt, sr,
                       low, high, low_tpt, high_tpt);
 
        scale_action = rs_get_rate_action(mvm, tbl, sr, low, high,
@@ -2305,6 +2389,8 @@ lq_update:
        /* Replace uCode's rate table for the destination station. */
        if (update_lq) {
                tbl->rate.index = index;
+               if (IWL_MVM_RS_80_20_FAR_RANGE_TWEAK)
+                       rs_tweak_rate_tbl(mvm, sta, lq_sta, tbl, scale_action);
                rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
        }
 
@@ -2542,7 +2628,6 @@ static struct rs_rate *rs_get_optimal_rate(struct iwl_mvm *mvm,
                }
        }
 
-       rs_dump_rate(mvm, rate, "OPTIMAL RATE");
        return rate;
 }
 
@@ -2983,9 +3068,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        else
                rs_vht_init(mvm, sta, lq_sta, vht_cap);
 
-       if (IWL_MVM_RS_DISABLE_P2P_MIMO && sta_priv->vif->p2p)
-               lq_sta->active_mimo2_rate = 0;
-
        lq_sta->max_legacy_rate_idx =
                rs_get_max_rate_from_mask(lq_sta->active_legacy_rate);
        lq_sta->max_siso_rate_idx =
index c37c10a423ce16b985ce620d2162d58c9c736fad..5b58f5320e8df39c049a69b7d44d781e85dc8ad5 100644 (file)
@@ -202,7 +202,6 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
                        return -1;
 
                stats->flag |= RX_FLAG_DECRYPTED;
-               IWL_DEBUG_WEP(mvm, "hw decrypted CCMP successfully\n");
                *crypt_len = IEEE80211_CCMP_HDR_LEN;
                return 0;
 
@@ -299,13 +298,6 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
                return;
        }
 
-       if ((unlikely(phy_info->cfg_phy_cnt > 20))) {
-               IWL_DEBUG_DROP(mvm, "dsp size out of range [0,20]: %d\n",
-                              phy_info->cfg_phy_cnt);
-               kfree_skb(skb);
-               return;
-       }
-
        /*
         * Keep packets with CRC errors (and with overrun) for monitor mode
         * (otherwise the firmware discards them) but mark them as bad.
@@ -354,8 +346,8 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
        /* This is fine since we don't support multiple AP interfaces */
        sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL);
        if (sta) {
-               struct iwl_mvm_sta *mvmsta;
-               mvmsta = iwl_mvm_sta_from_mac80211(sta);
+               struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
                rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
 
                if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) &&
@@ -459,7 +451,7 @@ static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm,
 struct iwl_mvm_stat_data {
        struct iwl_mvm *mvm;
        __le32 mac_id;
-       __s8 beacon_filter_average_energy;
+       u8 beacon_filter_average_energy;
        struct mvm_statistics_general_v8 *general;
 };
 
@@ -577,56 +569,33 @@ iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
 void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
                                  struct iwl_rx_packet *pkt)
 {
-       size_t v8_len = sizeof(struct iwl_notif_statistics_v8);
-       size_t v10_len = sizeof(struct iwl_notif_statistics_v10);
+       struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data;
        struct iwl_mvm_stat_data data = {
                .mvm = mvm,
        };
        u32 temperature;
 
-       if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STATS_V10)) {
-               struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data;
-
-               if (iwl_rx_packet_payload_len(pkt) != v10_len)
-                       goto invalid;
+       if (iwl_rx_packet_payload_len(pkt) != sizeof(*stats))
+               goto invalid;
 
-               temperature = le32_to_cpu(stats->general.radio_temperature);
-               data.mac_id = stats->rx.general.mac_id;
-               data.beacon_filter_average_energy =
-                       stats->general.beacon_filter_average_energy;
+       temperature = le32_to_cpu(stats->general.radio_temperature);
+       data.mac_id = stats->rx.general.mac_id;
+       data.beacon_filter_average_energy =
+               stats->general.beacon_filter_average_energy;
 
-               iwl_mvm_update_rx_statistics(mvm, &stats->rx);
+       iwl_mvm_update_rx_statistics(mvm, &stats->rx);
 
-               mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time);
-               mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time);
-               mvm->radio_stats.on_time_rf =
-                       le64_to_cpu(stats->general.on_time_rf);
-               mvm->radio_stats.on_time_scan =
-                       le64_to_cpu(stats->general.on_time_scan);
+       mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time);
+       mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time);
+       mvm->radio_stats.on_time_rf =
+               le64_to_cpu(stats->general.on_time_rf);
+       mvm->radio_stats.on_time_scan =
+               le64_to_cpu(stats->general.on_time_scan);
 
-               data.general = &stats->general;
-       } else {
-               struct iwl_notif_statistics_v8 *stats = (void *)&pkt->data;
-
-               if (iwl_rx_packet_payload_len(pkt) != v8_len)
-                       goto invalid;
-
-               temperature = le32_to_cpu(stats->general.radio_temperature);
-               data.mac_id = stats->rx.general.mac_id;
-               data.beacon_filter_average_energy =
-                       stats->general.beacon_filter_average_energy;
-
-               iwl_mvm_update_rx_statistics(mvm, &stats->rx);
-       }
+       data.general = &stats->general;
 
        iwl_mvm_rx_stats_check_trigger(mvm, pkt);
 
-       /* Only handle rx statistics temperature changes if async temp
-        * notifications are not supported
-        */
-       if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_ASYNC_DTM))
-               iwl_mvm_tt_temp_changed(mvm, temperature);
-
        ieee80211_iterate_active_interfaces(mvm->hw,
                                            IEEE80211_IFACE_ITER_NORMAL,
                                            iwl_mvm_stat_iterator,
index 56559d4d34ad1d0b70eecdccebdee2a5828921e6..d6e0c1b5c20cf31e3b8860f9afb8361a0d44c443 100644 (file)
@@ -131,7 +131,6 @@ struct iwl_mvm_scan_params {
        int n_ssids;
        struct cfg80211_ssid *ssids;
        struct ieee80211_channel **channels;
-       u16 interval; /* interval between scans (in secs) */
        u32 flags;
        u8 *mac_addr;
        u8 *mac_addr_mask;
@@ -140,7 +139,8 @@ struct iwl_mvm_scan_params {
        int n_match_sets;
        struct iwl_scan_probe_req preq;
        struct cfg80211_match_set *match_sets;
-       u8 iterations[2];
+       int n_scan_plans;
+       struct cfg80211_sched_scan_plan *scan_plans;
 };
 
 static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm)
@@ -474,7 +474,7 @@ iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
        int ret;
 
        if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES))
-                       return -EIO;
+               return -EIO;
 
        if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL)
                blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN;
@@ -737,8 +737,7 @@ static inline bool iwl_mvm_scan_fits(struct iwl_mvm *mvm, int n_ssids,
 }
 
 static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm,
-                                       struct ieee80211_vif *vif,
-                                       int n_iterations)
+                                       struct ieee80211_vif *vif)
 {
        const struct iwl_ucode_capabilities *capa = &mvm->fw->ucode_capa;
 
@@ -750,16 +749,9 @@ static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm,
         */
        return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) &&
                mvm->last_ebs_successful &&
-               (n_iterations > 1 ||
-                fw_has_api(capa, IWL_UCODE_TLV_API_SINGLE_SCAN_EBS)) &&
                vif->type != NL80211_IFTYPE_P2P_DEVICE);
 }
 
-static int iwl_mvm_scan_total_iterations(struct iwl_mvm_scan_params *params)
-{
-       return params->iterations[0] + params->iterations[1];
-}
-
 static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm,
                                   struct iwl_mvm_scan_params *params)
 {
@@ -798,12 +790,15 @@ static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) *
                         mvm->fw->ucode_capa.n_scan_channels);
        u32 ssid_bitmap = 0;
-       int n_iterations = iwl_mvm_scan_total_iterations(params);
+       int i;
 
        lockdep_assert_held(&mvm->mutex);
 
        memset(cmd, 0, ksize(cmd));
 
+       if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))
+               return -EINVAL;
+
        iwl_mvm_scan_lmac_dwell(mvm, cmd, params);
 
        cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm);
@@ -823,14 +818,26 @@ static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        /* this API uses bits 1-20 instead of 0-19 */
        ssid_bitmap <<= 1;
 
-       cmd->schedule[0].delay = cpu_to_le16(params->interval);
-       cmd->schedule[0].iterations = params->iterations[0];
-       cmd->schedule[0].full_scan_mul = 1;
-       cmd->schedule[1].delay = cpu_to_le16(params->interval);
-       cmd->schedule[1].iterations = params->iterations[1];
-       cmd->schedule[1].full_scan_mul = 1;
+       for (i = 0; i < params->n_scan_plans; i++) {
+               struct cfg80211_sched_scan_plan *scan_plan =
+                       &params->scan_plans[i];
+
+               cmd->schedule[i].delay =
+                       cpu_to_le16(scan_plan->interval);
+               cmd->schedule[i].iterations = scan_plan->iterations;
+               cmd->schedule[i].full_scan_mul = 1;
+       }
+
+       /*
+        * If the number of iterations of the last scan plan is set to
+        * zero, it should run infinitely. However, this is not always the case.
+        * For example, when regular scan is requested the driver sets one scan
+        * plan with one iteration.
+        */
+       if (!cmd->schedule[i - 1].iterations)
+               cmd->schedule[i - 1].iterations = 0xff;
 
-       if (iwl_mvm_scan_use_ebs(mvm, vif, n_iterations)) {
+       if (iwl_mvm_scan_use_ebs(mvm, vif)) {
                cmd->channel_opt[0].flags =
                        cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
                                    IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
@@ -894,7 +901,6 @@ static __le32 iwl_mvm_scan_config_rates(struct iwl_mvm *mvm)
 
 int iwl_mvm_config_scan(struct iwl_mvm *mvm)
 {
-
        struct iwl_scan_config *scan_config;
        struct ieee80211_supported_band *band;
        int num_channels =
@@ -970,6 +976,12 @@ static int iwl_mvm_scan_uid_by_status(struct iwl_mvm *mvm, int status)
        return -ENOENT;
 }
 
+static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params)
+{
+       return params->n_scan_plans == 1 &&
+               params->scan_plans[0].iterations == 1;
+}
+
 static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
                                    struct iwl_scan_req_umac *cmd,
                                    struct iwl_mvm_scan_params *params)
@@ -982,7 +994,7 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
        cmd->scan_priority =
                iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6);
 
-       if (iwl_mvm_scan_total_iterations(params) == 1)
+       if (iwl_mvm_is_regular_scan(params))
                cmd->ooc_priority =
                        iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6);
        else
@@ -1029,7 +1041,7 @@ static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm,
        else
                flags |= IWL_UMAC_SCAN_GEN_FLAGS_MATCH;
 
-       if (iwl_mvm_scan_total_iterations(params) > 1)
+       if (!iwl_mvm_is_regular_scan(params))
                flags |= IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC;
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -1047,12 +1059,14 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data +
                sizeof(struct iwl_scan_channel_cfg_umac) *
                        mvm->fw->ucode_capa.n_scan_channels;
-       int uid;
+       int uid, i;
        u32 ssid_bitmap = 0;
-       int n_iterations = iwl_mvm_scan_total_iterations(params);
 
        lockdep_assert_held(&mvm->mutex);
 
+       if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))
+               return -EINVAL;
+
        uid = iwl_mvm_scan_uid_by_status(mvm, 0);
        if (uid < 0)
                return uid;
@@ -1069,7 +1083,7 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        if (type == IWL_MVM_SCAN_SCHED)
                cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE);
 
-       if (iwl_mvm_scan_use_ebs(mvm, vif, n_iterations))
+       if (iwl_mvm_scan_use_ebs(mvm, vif))
                cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS |
                                     IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
                                     IWL_SCAN_CHANNEL_FLAG_CACHE_ADD;
@@ -1081,12 +1095,23 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        iwl_mvm_umac_scan_cfg_channels(mvm, params->channels,
                                       params->n_channels, ssid_bitmap, cmd);
 
-       /* With UMAC we use only one schedule for now, so use the sum
-        * of the iterations (with a a maximum of 255).
+       for (i = 0; i < params->n_scan_plans; i++) {
+               struct cfg80211_sched_scan_plan *scan_plan =
+                       &params->scan_plans[i];
+
+               sec_part->schedule[i].iter_count = scan_plan->iterations;
+               sec_part->schedule[i].interval =
+                       cpu_to_le16(scan_plan->interval);
+       }
+
+       /*
+        * If the number of iterations of the last scan plan is set to
+        * zero, it should run infinitely. However, this is not always the case.
+        * For example, when regular scan is requested the driver sets one scan
+        * plan with one iteration.
         */
-       sec_part->schedule[0].iter_count =
-               (n_iterations > 255) ? 255 : n_iterations;
-       sec_part->schedule[0].interval = cpu_to_le16(params->interval);
+       if (!sec_part->schedule[i - 1].iter_count)
+               sec_part->schedule[i - 1].iter_count = 0xff;
 
        sec_part->delay = cpu_to_le16(params->delay);
        sec_part->preq = params->preq;
@@ -1152,6 +1177,7 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        };
        struct iwl_mvm_scan_params params = {};
        int ret;
+       struct cfg80211_sched_scan_plan scan_plan = { .iterations = 1 };
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -1164,8 +1190,6 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        if (ret)
                return ret;
 
-       iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
-
        /* we should have failed registration if scan_cmd was NULL */
        if (WARN_ON(!mvm->scan_cmd))
                return -ENOMEM;
@@ -1177,7 +1201,6 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        params.flags = req->flags;
        params.n_channels = req->n_channels;
        params.delay = 0;
-       params.interval = 0;
        params.ssids = req->ssids;
        params.channels = req->channels;
        params.mac_addr = req->mac_addr;
@@ -1187,8 +1210,8 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        params.n_match_sets = 0;
        params.match_sets = NULL;
 
-       params.iterations[0] = 1;
-       params.iterations[1] = 0;
+       params.scan_plans = &scan_plan;
+       params.n_scan_plans = 1;
 
        params.type = iwl_mvm_get_scan_type(mvm, vif, &params);
 
@@ -1207,21 +1230,20 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return ret;
 
        ret = iwl_mvm_send_cmd(mvm, &hcmd);
-       if (!ret) {
-               IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n");
-               mvm->scan_status |= IWL_MVM_SCAN_REGULAR;
-       } else {
+       if (ret) {
                /* If the scan failed, it usually means that the FW was unable
                 * to allocate the time events. Warn on it, but maybe we
                 * should try to send the command again with different params.
                 */
                IWL_ERR(mvm, "Scan failed! ret %d\n", ret);
+               return ret;
        }
 
-       if (ret)
-               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+       IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n");
+       mvm->scan_status |= IWL_MVM_SCAN_REGULAR;
+       iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
 
-       return ret;
+       return 0;
 }
 
 int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
@@ -1267,20 +1289,14 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
        params.pass_all =  iwl_mvm_scan_pass_all(mvm, req);
        params.n_match_sets = req->n_match_sets;
        params.match_sets = req->match_sets;
+       if (!req->n_scan_plans)
+               return -EINVAL;
 
-       params.iterations[0] = 0;
-       params.iterations[1] = 0xff;
+       params.n_scan_plans = req->n_scan_plans;
+       params.scan_plans = req->scan_plans;
 
        params.type = iwl_mvm_get_scan_type(mvm, vif, &params);
 
-       if (req->interval > U16_MAX) {
-               IWL_DEBUG_SCAN(mvm,
-                              "interval value is > 16-bits, set to max possible\n");
-               params.interval = U16_MAX;
-       } else {
-               params.interval = req->interval / MSEC_PER_SEC;
-       }
-
        /* In theory, LMAC scans can handle a 32-bit delay, but since
         * waiting for over 18 hours to start the scan is a bit silly
         * and to keep it aligned with UMAC scans (which only support
index df216cd0c98f4659d1c00d7d07c8adb31c8244c6..300a249486e4df2398738b62c53a7870f87a757b 100644 (file)
@@ -234,7 +234,9 @@ static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm,
        /* Found a place for all queues - enable them */
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac],
-                                     iwl_mvm_ac_to_tx_fifo[ac], wdg_timeout);
+                                     mvmsta->hw_queue[ac],
+                                     iwl_mvm_ac_to_tx_fifo[ac], 0,
+                                     wdg_timeout);
                mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]);
        }
 
@@ -253,7 +255,7 @@ static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm,
        /* disable the TDLS STA-specific queues */
        sta_msk = mvmsta->tfd_queue_msk;
        for_each_set_bit(i, &sta_msk, sizeof(sta_msk) * BITS_PER_BYTE)
-               iwl_mvm_disable_txq(mvm, i, 0);
+               iwl_mvm_disable_txq(mvm, i, i, IWL_MAX_TID_COUNT, 0);
 }
 
 int iwl_mvm_add_sta(struct iwl_mvm *mvm,
@@ -275,6 +277,11 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
        if (sta_id == IWL_MVM_STATION_COUNT)
                return -ENOSPC;
 
+       if (vif->type == NL80211_IFTYPE_AP) {
+               mvmvif->ap_assoc_sta_count++;
+               iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+       }
+
        spin_lock_init(&mvm_sta->lock);
 
        mvm_sta->sta_id = sta_id;
@@ -287,7 +294,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
 
        /* HW restart, don't assume the memory has been zeroed */
        atomic_set(&mvm->pending_frames[sta_id], 0);
-       mvm_sta->tid_disable_agg = 0;
+       mvm_sta->tid_disable_agg = 0xffff; /* No aggs at first */
        mvm_sta->tfd_queue_msk = 0;
 
        /* allocate new queues for a TDLS station */
@@ -467,7 +474,8 @@ void iwl_mvm_sta_drained_wk(struct work_struct *wk)
                        unsigned long i, msk = mvm->tfd_drained[sta_id];
 
                        for_each_set_bit(i, &msk, sizeof(msk) * BITS_PER_BYTE)
-                               iwl_mvm_disable_txq(mvm, i, 0);
+                               iwl_mvm_disable_txq(mvm, i, i,
+                                                   IWL_MAX_TID_COUNT, 0);
 
                        mvm->tfd_drained[sta_id] = 0;
                        IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n",
@@ -494,7 +502,7 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
                if (ret)
                        return ret;
                /* flush its queues here since we are freeing mvm_sta */
-               ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
+               ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, 0);
                if (ret)
                        return ret;
                ret = iwl_trans_wait_tx_queue_empty(mvm->trans,
@@ -646,8 +654,8 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
        lockdep_assert_held(&mvm->mutex);
 
        /* Map Aux queue to fifo - needs to happen before adding Aux station */
-       iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue,
-                             IWL_MVM_TX_FIFO_MCAST, wdg_timeout);
+       iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue,
+                             IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout);
 
        /* Allocate aux station and assign to it the aux queue */
        ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue),
@@ -918,6 +926,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
        struct iwl_mvm_tid_data *tid_data;
        int txq_id;
+       int ret;
 
        if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
                return -EINVAL;
@@ -930,17 +939,6 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        lockdep_assert_held(&mvm->mutex);
 
-       for (txq_id = mvm->first_agg_queue;
-            txq_id <= mvm->last_agg_queue; txq_id++)
-               if (mvm->queue_to_mac80211[txq_id] ==
-                   IWL_INVALID_MAC80211_QUEUE)
-                       break;
-
-       if (txq_id > mvm->last_agg_queue) {
-               IWL_ERR(mvm, "Failed to allocate agg queue\n");
-               return -EIO;
-       }
-
        spin_lock_bh(&mvmsta->lock);
 
        /* possible race condition - we entered D0i3 while starting agg */
@@ -950,8 +948,18 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return -EIO;
        }
 
-       /* the new tx queue is still connected to the same mac80211 queue */
-       mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_mac80211_ac[tid]];
+       spin_lock_bh(&mvm->queue_info_lock);
+
+       txq_id = iwl_mvm_find_free_queue(mvm, mvm->first_agg_queue,
+                                        mvm->last_agg_queue);
+       if (txq_id < 0) {
+               ret = txq_id;
+               spin_unlock_bh(&mvm->queue_info_lock);
+               IWL_ERR(mvm, "Failed to allocate agg queue\n");
+               goto release_locks;
+       }
+       mvm->queue_info[txq_id].setup_reserved = true;
+       spin_unlock_bh(&mvm->queue_info_lock);
 
        tid_data = &mvmsta->tid_data[tid];
        tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
@@ -970,9 +978,12 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                tid_data->state = IWL_EMPTYING_HW_QUEUE_ADDBA;
        }
 
+       ret = 0;
+
+release_locks:
        spin_unlock_bh(&mvmsta->lock);
 
-       return 0;
+       return ret;
 }
 
 int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@@ -1000,13 +1011,19 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
 
-       iwl_mvm_enable_agg_txq(mvm, queue, fifo, mvmsta->sta_id, tid,
-                              buf_size, ssn, wdg_timeout);
+       iwl_mvm_enable_agg_txq(mvm, queue,
+                              vif->hw_queue[tid_to_mac80211_ac[tid]], fifo,
+                              mvmsta->sta_id, tid, buf_size, ssn, wdg_timeout);
 
        ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true);
        if (ret)
                return -EIO;
 
+       /* No need to mark as reserved */
+       spin_lock_bh(&mvm->queue_info_lock);
+       mvm->queue_info[queue].setup_reserved = false;
+       spin_unlock_bh(&mvm->queue_info_lock);
+
        /*
         * Even though in theory the peer could have different
         * aggregation reorder buffer sizes for different sessions,
@@ -1051,6 +1068,11 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        mvmsta->agg_tids &= ~BIT(tid);
 
+       /* No need to mark as reserved anymore */
+       spin_lock_bh(&mvm->queue_info_lock);
+       mvm->queue_info[txq_id].setup_reserved = false;
+       spin_unlock_bh(&mvm->queue_info_lock);
+
        switch (tid_data->state) {
        case IWL_AGG_ON:
                tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
@@ -1068,14 +1090,15 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
                tid_data->ssn = 0xffff;
                tid_data->state = IWL_AGG_OFF;
-               mvm->queue_to_mac80211[txq_id] = IWL_INVALID_MAC80211_QUEUE;
                spin_unlock_bh(&mvmsta->lock);
 
                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 
                iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false);
 
-               iwl_mvm_disable_txq(mvm, txq_id, 0);
+               iwl_mvm_disable_txq(mvm, txq_id,
+                                   vif->hw_queue[tid_to_mac80211_ac[tid]], tid,
+                                   0);
                return 0;
        case IWL_AGG_STARTING:
        case IWL_EMPTYING_HW_QUEUE_ADDBA:
@@ -1086,7 +1109,6 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
                /* No barriers since we are under mutex */
                lockdep_assert_held(&mvm->mutex);
-               mvm->queue_to_mac80211[txq_id] = IWL_INVALID_MAC80211_QUEUE;
 
                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                tid_data->state = IWL_AGG_OFF;
@@ -1127,9 +1149,14 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        mvmsta->agg_tids &= ~BIT(tid);
        spin_unlock_bh(&mvmsta->lock);
 
+       /* No need to mark as reserved */
+       spin_lock_bh(&mvm->queue_info_lock);
+       mvm->queue_info[txq_id].setup_reserved = false;
+       spin_unlock_bh(&mvm->queue_info_lock);
+
        if (old_state >= IWL_AGG_ON) {
                iwl_mvm_drain_sta(mvm, mvmsta, true);
-               if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true))
+               if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0))
                        IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
                iwl_trans_wait_tx_queue_empty(mvm->trans,
                                              mvmsta->tfd_queue_msk);
@@ -1137,12 +1164,11 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
                iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false);
 
-               iwl_mvm_disable_txq(mvm, tid_data->txq_id, 0);
+               iwl_mvm_disable_txq(mvm, tid_data->txq_id,
+                                   vif->hw_queue[tid_to_mac80211_ac[tid]], tid,
+                                   0);
        }
 
-       mvm->queue_to_mac80211[tid_data->txq_id] =
-                               IWL_INVALID_MAC80211_QUEUE;
-
        return 0;
 }
 
index dbd7d544575de68a3972588bb117a8fa5b560ef3..7530eb23035d3ea315c8d6671eebce16d0ec44c6 100644 (file)
@@ -129,7 +129,7 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk)
         * issue as it will have to complete before the next command is
         * executed, and a new time event means a new command.
         */
-       iwl_mvm_flush_tx_path(mvm, queues, false);
+       iwl_mvm_flush_tx_path(mvm, queues, CMD_ASYNC);
 }
 
 static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
index 380972f8fb8281647b3c278d8b7b51dec306a008..4007f1d421dd6db1249f47122b933b751c300c0a 100644 (file)
@@ -178,12 +178,14 @@ int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm,
        if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
                return -EINVAL;
 
-       if (vif->p2p || vif->type != NL80211_IFTYPE_AP) {
+       if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
+           !mvmvif->ap_ibss_active) {
                IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
                return -EIO;
        }
 
        cmd->sta_id = mvmvif->bcast_sta.sta_id;
+       memcpy(cmd->bssid, vif->addr, ETH_ALEN);
        return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
                                                    IWL_ALWAYS_LONG_GROUP, 0),
                                    0, sizeof(*cmd), cmd);
index 50ae8adaaa6e378dc6959f24d605265e39dcaae6..9beebc33cb8ddda93d28a3595b41396f0fb4b6e4 100644 (file)
@@ -60,7 +60,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  *****************************************************************************/
-#ifndef __tof
+#ifndef __tof_h__
 #define __tof_h__
 
 #include "fw-api-tof.h"
index fe7145c2c98acaa47bb19fe9dbbe4145f07491bd..cadfc046059799065db59d797954ff893a2c580a 100644 (file)
@@ -176,17 +176,34 @@ static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm)
        struct iwl_dts_measurement_cmd cmd = {
                .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),
        };
+       struct iwl_ext_dts_measurement_cmd extcmd = {
+               .control_mode = cpu_to_le32(DTS_AUTOMATIC),
+       };
+       u32 cmdid;
+
+       if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR))
+               cmdid = iwl_cmd_id(CMD_DTS_MEASUREMENT_TRIGGER_WIDE,
+                                  PHY_OPS_GROUP, 0);
+       else
+               cmdid = CMD_DTS_MEASUREMENT_TRIGGER;
 
-       return iwl_mvm_send_cmd_pdu(mvm, CMD_DTS_MEASUREMENT_TRIGGER, 0,
-                                   sizeof(cmd), &cmd);
+       if (!fw_has_capa(&mvm->fw->ucode_capa,
+                        IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE))
+               return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(cmd), &cmd);
+
+       return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(extcmd), &extcmd);
 }
 
 int iwl_mvm_get_temp(struct iwl_mvm *mvm)
 {
        struct iwl_notification_wait wait_temp_notif;
-       static const u16 temp_notif[] = { DTS_MEASUREMENT_NOTIFICATION };
+       static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP,
+                                           DTS_MEASUREMENT_NOTIF_WIDE) };
        int ret, temp;
 
+       if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR))
+               temp_notif[0] = DTS_MEASUREMENT_NOTIFICATION;
+
        lockdep_assert_held(&mvm->mutex);
 
        iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
index 6df5aada4f161d6b533dae2b07e01146b53798ef..c652a66be80353532a215f6b728d6d847d50825d 100644 (file)
@@ -560,15 +560,10 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
                IWL_DEBUG_TX_QUEUES(mvm,
                                    "Can continue DELBA flow ssn = next_recl = %d\n",
                                    tid_data->next_reclaimed);
-               iwl_mvm_disable_txq(mvm, tid_data->txq_id, CMD_ASYNC);
+               iwl_mvm_disable_txq(mvm, tid_data->txq_id,
+                                   vif->hw_queue[tid_to_mac80211_ac[tid]], tid,
+                                   CMD_ASYNC);
                tid_data->state = IWL_AGG_OFF;
-               /*
-                * we can't hold the mutex - but since we are after a sequence
-                * point (call to iwl_mvm_disable_txq(), so we don't even need
-                * a memory barrier.
-                */
-               mvm->queue_to_mac80211[tid_data->txq_id] =
-                                       IWL_INVALID_MAC80211_QUEUE;
                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                break;
 
@@ -1104,7 +1099,7 @@ out:
  * 2) flush the Tx path
  * 3) wait for the transport queues to be empty
  */
-int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync)
+int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags)
 {
        int ret;
        struct iwl_tx_path_flush_cmd flush_cmd = {
@@ -1112,8 +1107,6 @@ int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync)
                .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH),
        };
 
-       u32 flags = sync ? 0 : CMD_ASYNC;
-
        ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags,
                                   sizeof(flush_cmd), &flush_cmd);
        if (ret)
index a7d434256423382af219c1ab9221efdfe686ea69..ad0f16909e2e243483334b9d9cb274c72a78be50 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright (C) 2015 Intel Deutschland GmbH
  *
  * 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
@@ -657,45 +658,143 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
        if (mvm->support_umac_log)
                iwl_mvm_dump_umac_error_log(mvm);
 }
-void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn,
-                       const struct iwl_trans_txq_scd_cfg *cfg,
+
+int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq)
+{
+       int i;
+
+       lockdep_assert_held(&mvm->queue_info_lock);
+
+       for (i = minq; i <= maxq; i++)
+               if (mvm->queue_info[i].hw_queue_refcount == 0 &&
+                   !mvm->queue_info[i].setup_reserved)
+                       return i;
+
+       return -ENOSPC;
+}
+
+void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+                       u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg,
                        unsigned int wdg_timeout)
 {
-       struct iwl_scd_txq_cfg_cmd cmd = {
-               .scd_queue = queue,
-               .enable = 1,
-               .window = cfg->frame_limit,
-               .sta_id = cfg->sta_id,
-               .ssn = cpu_to_le16(ssn),
-               .tx_fifo = cfg->fifo,
-               .aggregate = cfg->aggregate,
-               .tid = cfg->tid,
-       };
+       bool enable_queue = true;
 
-       if (!iwl_mvm_is_scd_cfg_supported(mvm)) {
-               iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, cfg,
-                                        wdg_timeout);
+       spin_lock_bh(&mvm->queue_info_lock);
+
+       /* Make sure this TID isn't already enabled */
+       if (mvm->queue_info[queue].tid_bitmap & BIT(cfg->tid)) {
+               spin_unlock_bh(&mvm->queue_info_lock);
+               IWL_ERR(mvm, "Trying to enable TXQ with existing TID %d\n",
+                       cfg->tid);
                return;
        }
 
-       iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, wdg_timeout);
-       WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd),
-            "Failed to configure queue %d on FIFO %d\n", queue, cfg->fifo);
+       /* Update mappings and refcounts */
+       mvm->queue_info[queue].hw_queue_to_mac80211 |= BIT(mac80211_queue);
+       mvm->queue_info[queue].hw_queue_refcount++;
+       if (mvm->queue_info[queue].hw_queue_refcount > 1)
+               enable_queue = false;
+       mvm->queue_info[queue].tid_bitmap |= BIT(cfg->tid);
+
+       IWL_DEBUG_TX_QUEUES(mvm,
+                           "Enabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n",
+                           queue, mvm->queue_info[queue].hw_queue_refcount,
+                           mvm->queue_info[queue].hw_queue_to_mac80211);
+
+       spin_unlock_bh(&mvm->queue_info_lock);
+
+       /* Send the enabling command if we need to */
+       if (enable_queue) {
+               struct iwl_scd_txq_cfg_cmd cmd = {
+                       .scd_queue = queue,
+                       .enable = 1,
+                       .window = cfg->frame_limit,
+                       .sta_id = cfg->sta_id,
+                       .ssn = cpu_to_le16(ssn),
+                       .tx_fifo = cfg->fifo,
+                       .aggregate = cfg->aggregate,
+                       .tid = cfg->tid,
+               };
+
+               iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL,
+                                        wdg_timeout);
+               WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd),
+                                         &cmd),
+                    "Failed to configure queue %d on FIFO %d\n", queue,
+                    cfg->fifo);
+       }
 }
 
-void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, u8 flags)
+void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+                        u8 tid, u8 flags)
 {
        struct iwl_scd_txq_cfg_cmd cmd = {
                .scd_queue = queue,
                .enable = 0,
        };
+       bool remove_mac_queue = true;
        int ret;
 
-       if (!iwl_mvm_is_scd_cfg_supported(mvm)) {
-               iwl_trans_txq_disable(mvm->trans, queue, true);
+       spin_lock_bh(&mvm->queue_info_lock);
+
+       if (WARN_ON(mvm->queue_info[queue].hw_queue_refcount == 0)) {
+               spin_unlock_bh(&mvm->queue_info_lock);
+               return;
+       }
+
+       mvm->queue_info[queue].tid_bitmap &= ~BIT(tid);
+
+       /*
+        * If there is another TID with the same AC - don't remove the MAC queue
+        * from the mapping
+        */
+       if (tid < IWL_MAX_TID_COUNT) {
+               unsigned long tid_bitmap =
+                       mvm->queue_info[queue].tid_bitmap;
+               int ac = tid_to_mac80211_ac[tid];
+               int i;
+
+               for_each_set_bit(i, &tid_bitmap, IWL_MAX_TID_COUNT) {
+                       if (tid_to_mac80211_ac[i] == ac)
+                               remove_mac_queue = false;
+               }
+       }
+
+       if (remove_mac_queue)
+               mvm->queue_info[queue].hw_queue_to_mac80211 &=
+                       ~BIT(mac80211_queue);
+       mvm->queue_info[queue].hw_queue_refcount--;
+
+       cmd.enable = mvm->queue_info[queue].hw_queue_refcount ? 1 : 0;
+
+       IWL_DEBUG_TX_QUEUES(mvm,
+                           "Disabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n",
+                           queue,
+                           mvm->queue_info[queue].hw_queue_refcount,
+                           mvm->queue_info[queue].hw_queue_to_mac80211);
+
+       /* If the queue is still enabled - nothing left to do in this func */
+       if (cmd.enable) {
+               spin_unlock_bh(&mvm->queue_info_lock);
                return;
        }
 
+       /* Make sure queue info is correct even though we overwrite it */
+       WARN(mvm->queue_info[queue].hw_queue_refcount ||
+            mvm->queue_info[queue].tid_bitmap ||
+            mvm->queue_info[queue].hw_queue_to_mac80211,
+            "TXQ #%d info out-of-sync - refcount=%d, mac map=0x%x, tid=0x%x\n",
+            queue, mvm->queue_info[queue].hw_queue_refcount,
+            mvm->queue_info[queue].hw_queue_to_mac80211,
+            mvm->queue_info[queue].tid_bitmap);
+
+       /* If we are here - the queue is freed and we can zero out these vals */
+       mvm->queue_info[queue].hw_queue_refcount = 0;
+       mvm->queue_info[queue].tid_bitmap = 0;
+       mvm->queue_info[queue].hw_queue_to_mac80211 = 0;
+
+       spin_unlock_bh(&mvm->queue_info_lock);
+
        iwl_trans_txq_disable(mvm->trans, queue, false);
        ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags,
                                   sizeof(cmd), &cmd);
index 6ba7d300b08f35e1ce05fdf8b3fcab058819f150..90283453073c1684b4332445e0028b65482af179 100644 (file)
@@ -592,10 +592,8 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)
 
                do {
                        ret = iwl_pcie_set_hw_ready(trans);
-                       if (ret >= 0) {
-                               ret = 0;
-                               goto out;
-                       }
+                       if (ret >= 0)
+                               return 0;
 
                        usleep_range(200, 1000);
                        t += 200;
@@ -605,10 +603,6 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)
 
        IWL_ERR(trans, "Couldn't prepare the card\n");
 
-out:
-       iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
-                     CSR_RESET_LINK_PWR_MGMT_DISABLED);
-
        return ret;
 }
 
index 520bef80747f295bea9f98e4ca2573bbc5dde48a..ee46f4647fbc4803d629047462b4f696fcfea2f6 100644 (file)
@@ -1819,7 +1819,7 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
                                       enum ieee80211_ampdu_mlme_action action,
                                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                                      u8 buf_size)
+                                      u8 buf_size, bool amsdu)
 {
        switch (action) {
        case IEEE80211_AMPDU_TX_START:
@@ -2190,9 +2190,8 @@ static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
                                   struct genl_info *info)
 {
        if (info)
-               genl_notify(&hwsim_genl_family, mcast_skb,
-                           genl_info_net(info), info->snd_portid,
-                           HWSIM_MCGRP_CONFIG, info->nlhdr, GFP_KERNEL);
+               genl_notify(&hwsim_genl_family, mcast_skb, info,
+                           HWSIM_MCGRP_CONFIG, GFP_KERNEL);
        else
                genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0,
                                  HWSIM_MCGRP_CONFIG, GFP_KERNEL);
index 169384b48b27d785cb43cb3199dedc89b736db84..f715eee398510df829a61ba0f431610d1ab32baa 100644 (file)
@@ -335,7 +335,8 @@ static int mt7601u_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 static int
 mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                  enum ieee80211_ampdu_mlme_action action,
-                 struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size)
+                 struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size,
+                 bool amsdu)
 {
        struct mt7601u_dev *dev = hw->priv;
        struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
index f7c717253a664aaa7170b3216890ebd1b22ad295..aa498e0d2204b82b53ecd39a1da4aed9bf5ba9ba 100644 (file)
@@ -173,7 +173,6 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
        int pad = 0, aggr_num = 0, ret;
        struct mwifiex_tx_param tx_param;
        struct txpd *ptx_pd = NULL;
-       struct timeval tv;
        int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN;
 
        skb_src = skb_peek(&pra_list->skb_head);
@@ -202,9 +201,9 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
                tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
        tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT;
        skb_aggr->priority = skb_src->priority;
+       skb_aggr->tstamp = skb_src->tstamp;
 
-       do_gettimeofday(&tv);
-       skb_aggr->tstamp = timeval_to_ktime(tv);
+       skb_aggr->tstamp = ktime_get_real();
 
        do {
                /* Check if AMSDU can accommodate this MSDU */
@@ -258,8 +257,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
        }
 
        if (adapter->iface_type == MWIFIEX_USB) {
-               adapter->data_sent = true;
-               ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA,
+               ret = adapter->if_ops.host_to_card(adapter, priv->usb_port,
                                                   skb_aggr, NULL);
        } else {
                if (skb_src)
@@ -299,16 +297,12 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
                mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
                break;
        case -1:
-               if (adapter->iface_type != MWIFIEX_PCIE)
-                       adapter->data_sent = false;
                mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n",
                            __func__, ret);
                adapter->dbg.num_tx_host_to_card_failure++;
                mwifiex_write_data_complete(adapter, skb_aggr, 1, ret);
                return 0;
        case -EINPROGRESS:
-               if (adapter->iface_type != MWIFIEX_PCIE)
-                       adapter->data_sent = false;
                break;
        case 0:
                mwifiex_write_data_complete(adapter, skb_aggr, 1, ret);
index 2906cd543532aa514de2e2c6ed14bd19167fbdc0..b3970a8c9e48b9b0677b6ca0d36d5b57680d3715 100644 (file)
@@ -615,10 +615,10 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
            ((end_win > start_win) && ((seq_num > end_win) ||
                                       (seq_num < start_win)))) {
                end_win = seq_num;
-               if (((seq_num - win_size) + 1) >= 0)
+               if (((end_win - win_size) + 1) >= 0)
                        start_win = (end_win - win_size) + 1;
                else
-                       start_win = (MAX_TID_VALUE - (win_size - seq_num)) + 1;
+                       start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1;
                mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
        }
 
index 317d99189556ab1c3025bf11afd7b61449fd124e..279167ddd2935467f62e50208b05fc0f9562623e 100644 (file)
@@ -33,12 +33,12 @@ config MWIFIEX_PCIE
          mwifiex_pcie.
 
 config MWIFIEX_USB
-       tristate "Marvell WiFi-Ex Driver for USB8766/8797/8897/8997"
+       tristate "Marvell WiFi-Ex Driver for USB8766/8797/8997"
        depends on MWIFIEX && USB
        select FW_LOADER
        ---help---
          This adds support for wireless adapters based on Marvell
-         8797/8897/8997 chipset with USB interface.
+         8797/8997 chipset with USB interface.
 
          If you choose to build it as a module, it will be called
          mwifiex_usb.
index ff63cb5632eb089a817422dddfa9dda0069b2478..4073116e6e9fa8915c9fd4d35d932ccd5f3f2a08 100644 (file)
@@ -1821,6 +1821,10 @@ static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
                return -1;
        }
 
+       if (netif_carrier_ok(priv->netdev))
+               netif_carrier_off(priv->netdev);
+       mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
+
        return 0;
 }
 
@@ -1925,6 +1929,10 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
        if (mwifiex_set_mgmt_ies(priv, &params->beacon))
                return -1;
 
+       if (!netif_carrier_ok(priv->netdev))
+               netif_carrier_on(priv->netdev);
+       mwifiex_wake_up_net_dev_queue(priv->netdev, priv->adapter);
+
        memcpy(&priv->bss_cfg, bss_cfg, sizeof(priv->bss_cfg));
        kfree(bss_cfg);
        return 0;
@@ -1994,8 +2002,10 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
                                  CFG80211_BSS_FTYPE_UNKNOWN,
                                  bss_info.bssid, 0, WLAN_CAPABILITY_IBSS,
                                  0, ie_buf, ie_len, 0, GFP_KERNEL);
-       cfg80211_put_bss(priv->wdev.wiphy, bss);
-       memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN);
+       if (bss) {
+               cfg80211_put_bss(priv->wdev.wiphy, bss);
+               ether_addr_copy(priv->cfg_bssid, bss_info.bssid);
+       }
 
        return 0;
 }
@@ -2372,7 +2382,7 @@ mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
  * CFG802.11 operation handler for scan request.
  *
  * This function issues a scan request to the firmware based upon
- * the user specified scan configuration. On successfull completion,
+ * the user specified scan configuration. On successful completion,
  * it also informs the results.
  */
 static int
@@ -2859,14 +2869,14 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
        case NL80211_IFTYPE_UNSPECIFIED:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
-               adapter->curr_iface_comb.sta_intf++;
+               adapter->curr_iface_comb.sta_intf--;
                break;
        case NL80211_IFTYPE_AP:
-               adapter->curr_iface_comb.uap_intf++;
+               adapter->curr_iface_comb.uap_intf--;
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_P2P_GO:
-               adapter->curr_iface_comb.p2p_intf++;
+               adapter->curr_iface_comb.p2p_intf--;
                break;
        default:
                mwifiex_dbg(adapter, ERROR,
index 5a0636d43a1b9722b4be1e95b7528aa0ae6368f9..9824d8dd2b4447f11304557a44f3c7ea2d629d76 100644 (file)
@@ -731,7 +731,7 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
                (struct mwifiex_private *) file->private_data;
        unsigned long addr = get_zeroed_page(GFP_KERNEL);
        char *buf = (char *) addr;
-       int pos = 0, ret = 0, i;
+       int pos, ret, i;
        u8 value[MAX_EEPROM_DATA];
 
        if (!buf)
@@ -739,7 +739,7 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
 
        if (saved_offset == -1) {
                /* No command has been given */
-               pos += snprintf(buf, PAGE_SIZE, "0");
+               pos = snprintf(buf, PAGE_SIZE, "0");
                goto done;
        }
 
@@ -748,17 +748,17 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
                                  (u16) saved_bytes, value);
        if (ret) {
                ret = -EINVAL;
-               goto done;
+               goto out_free;
        }
 
-       pos += snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes);
+       pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes);
 
        for (i = 0; i < saved_bytes; i++)
-               pos += snprintf(buf + strlen(buf), PAGE_SIZE, "%d ", value[i]);
-
-       ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+               pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]);
 
 done:
+       ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+out_free:
        free_page(addr);
        return ret;
 }
@@ -856,6 +856,56 @@ mwifiex_hscfg_read(struct file *file, char __user *ubuf,
        return ret;
 }
 
+static ssize_t
+mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf,
+                           size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv = file->private_data;
+       char buf[3];
+       bool timeshare_coex;
+       int ret;
+       unsigned int len;
+
+       if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15)
+               return -EOPNOTSUPP;
+
+       ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX,
+                              HostCmd_ACT_GEN_GET, 0, &timeshare_coex, true);
+       if (ret)
+               return ret;
+
+       len = sprintf(buf, "%d\n", timeshare_coex);
+       return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static ssize_t
+mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf,
+                            size_t count, loff_t *ppos)
+{
+       bool timeshare_coex;
+       struct mwifiex_private *priv = file->private_data;
+       char kbuf[16];
+       int ret;
+
+       if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15)
+               return -EOPNOTSUPP;
+
+       memset(kbuf, 0, sizeof(kbuf));
+
+       if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count)))
+               return -EFAULT;
+
+       if (strtobool(kbuf, &timeshare_coex))
+               return -EINVAL;
+
+       ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX,
+                              HostCmd_ACT_GEN_SET, 0, &timeshare_coex, true);
+       if (ret)
+               return ret;
+       else
+               return count;
+}
+
 #define MWIFIEX_DFS_ADD_FILE(name) do {                                 \
        if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir,        \
                        priv, &mwifiex_dfs_##name##_fops))              \
@@ -892,6 +942,7 @@ MWIFIEX_DFS_FILE_OPS(memrw);
 MWIFIEX_DFS_FILE_OPS(hscfg);
 MWIFIEX_DFS_FILE_OPS(histogram);
 MWIFIEX_DFS_FILE_OPS(debug_mask);
+MWIFIEX_DFS_FILE_OPS(timeshare_coex);
 
 /*
  * This function creates the debug FS directory structure and the files.
@@ -918,6 +969,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv)
        MWIFIEX_DFS_ADD_FILE(hscfg);
        MWIFIEX_DFS_ADD_FILE(histogram);
        MWIFIEX_DFS_ADD_FILE(debug_mask);
+       MWIFIEX_DFS_ADD_FILE(timeshare_coex);
 }
 
 /*
index 3ec2ac82e394a158e6c346d75e73a29715e7ec29..1e1e81a0a8d4989876433b7cb31756d401cb40cf 100644 (file)
@@ -101,9 +101,13 @@ enum KEY_TYPE_ID {
 #define FIRMWARE_READY_SDIO                            0xfedc
 #define FIRMWARE_READY_PCIE                            0xfedcba00
 
+#define MWIFIEX_COEX_MODE_TIMESHARE                    0x01
+#define MWIFIEX_COEX_MODE_SPATIAL                      0x82
+
 enum mwifiex_usb_ep {
        MWIFIEX_USB_EP_CMD_EVENT = 1,
        MWIFIEX_USB_EP_DATA = 2,
+       MWIFIEX_USB_EP_DATA_CH2 = 3,
 };
 
 enum MWIFIEX_802_11_PRIVACY_FILTER {
@@ -162,6 +166,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define TLV_TYPE_CHANRPT_11H_BASIC  (PROPRIETARY_TLV_BASE_ID + 91)
 #define TLV_TYPE_UAP_RETRY_LIMIT    (PROPRIETARY_TLV_BASE_ID + 93)
 #define TLV_TYPE_WAPI_IE            (PROPRIETARY_TLV_BASE_ID + 94)
+#define TLV_TYPE_ROBUST_COEX        (PROPRIETARY_TLV_BASE_ID + 96)
 #define TLV_TYPE_UAP_MGMT_FRAME     (PROPRIETARY_TLV_BASE_ID + 104)
 #define TLV_TYPE_MGMT_IE            (PROPRIETARY_TLV_BASE_ID + 105)
 #define TLV_TYPE_AUTO_DS_PARAM      (PROPRIETARY_TLV_BASE_ID + 113)
@@ -173,6 +178,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define TLV_TYPE_COALESCE_RULE      (PROPRIETARY_TLV_BASE_ID + 154)
 #define TLV_TYPE_KEY_PARAM_V2       (PROPRIETARY_TLV_BASE_ID + 156)
 #define TLV_TYPE_MULTI_CHAN_INFO    (PROPRIETARY_TLV_BASE_ID + 183)
+#define TLV_TYPE_MC_GROUP_INFO      (PROPRIETARY_TLV_BASE_ID + 184)
 #define TLV_TYPE_TDLS_IDLE_TIMEOUT  (PROPRIETARY_TLV_BASE_ID + 194)
 #define TLV_TYPE_SCAN_CHANNEL_GAP   (PROPRIETARY_TLV_BASE_ID + 197)
 #define TLV_TYPE_API_REV            (PROPRIETARY_TLV_BASE_ID + 199)
@@ -352,6 +358,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HostCmd_CMD_AMSDU_AGGR_CTRL                   0x00df
 #define HostCmd_CMD_TXPWR_CFG                         0x00d1
 #define HostCmd_CMD_TX_RATE_CFG                       0x00d6
+#define HostCmd_CMD_ROBUST_COEX                       0x00e0
 #define HostCmd_CMD_802_11_PS_MODE_ENH                0x00e4
 #define HostCmd_CMD_802_11_HS_CFG_ENH                 0x00e5
 #define HostCmd_CMD_P2P_MODE_CFG                      0x00eb
@@ -1875,6 +1882,11 @@ struct mwifiex_ie_types_btcoex_aggr_win_size {
        u8 reserved;
 } __packed;
 
+struct mwifiex_ie_types_robust_coex {
+       struct mwifiex_ie_types_header header;
+       __le32 mode;
+} __packed;
+
 struct host_cmd_ds_version_ext {
        u8 version_str_sel;
        char version_str[128];
@@ -1984,6 +1996,22 @@ struct mwifiex_ie_types_multi_chan_info {
        u8 tlv_buffer[0];
 } __packed;
 
+struct mwifiex_ie_types_mc_group_info {
+       struct mwifiex_ie_types_header header;
+       u8 chan_group_id;
+       u8 chan_buf_weight;
+       u8 band_config;
+       u8 chan_num;
+       u32 chan_time;
+       u32 reserved;
+       union {
+               u8 sdio_func_num;
+               u8 usb_ep_num;
+       } hid_num;
+       u8 intf_num;
+       u8 bss_type_numlist[0];
+} __packed;
+
 struct meas_rpt_map {
        u8 rssi:3;
        u8 unmeasured:1;
@@ -2060,6 +2088,11 @@ struct host_cmd_ds_multi_chan_policy {
        __le16 policy;
 } __packed;
 
+struct host_cmd_ds_robust_coex {
+       __le16 action;
+       __le16 reserved;
+} __packed;
+
 struct host_cmd_ds_command {
        __le16 command;
        __le16 size;
@@ -2129,6 +2162,7 @@ struct host_cmd_ds_command {
                struct host_cmd_ds_chan_rpt_req chan_rpt_req;
                struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg;
                struct host_cmd_ds_multi_chan_policy mc_policy;
+               struct host_cmd_ds_robust_coex coex;
        } params;
 } __packed;
 
index 5d3ae63baea4c1950203563f712bda2f83c04988..de74a7773fb6b22db78bafb8fb7f547753d939b7 100644 (file)
@@ -78,6 +78,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
        priv->media_connected = false;
        eth_broadcast_addr(priv->curr_addr);
        priv->port_open = false;
+       priv->usb_port = MWIFIEX_USB_EP_DATA;
        priv->pkt_tx_ctrl = 0;
        priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
        priv->data_rate = 0;    /* Initially indicate the rate as auto */
index 278dc94eaecbb28e12a075526572c342e52f31cf..969ca1e1f3e9f805d26edd3ed9f6ea8d839fc9b7 100644 (file)
@@ -294,9 +294,15 @@ process_start:
                        /* We have tried to wakeup the card already */
                        if (adapter->pm_wakeup_fw_try)
                                break;
-                       if (adapter->ps_state != PS_STATE_AWAKE ||
-                           adapter->tx_lock_flag)
+                       if (adapter->ps_state != PS_STATE_AWAKE)
                                break;
+                       if (adapter->tx_lock_flag) {
+                               if (adapter->iface_type == MWIFIEX_USB) {
+                                       if (!adapter->usb_mc_setup)
+                                               break;
+                               } else
+                                       break;
+                       }
 
                        if ((!adapter->scan_chan_gap_enabled &&
                             adapter->scan_processing) || adapter->data_sent ||
@@ -345,11 +351,18 @@ process_start:
                 */
                if ((adapter->ps_state == PS_STATE_SLEEP) ||
                    (adapter->ps_state == PS_STATE_PRE_SLEEP) ||
-                   (adapter->ps_state == PS_STATE_SLEEP_CFM) ||
-                   adapter->tx_lock_flag){
+                   (adapter->ps_state == PS_STATE_SLEEP_CFM)) {
                        continue;
                }
 
+               if (adapter->tx_lock_flag) {
+                       if (adapter->iface_type == MWIFIEX_USB) {
+                               if (!adapter->usb_mc_setup)
+                                       continue;
+                       } else
+                               continue;
+               }
+
                if (!adapter->cmd_sent && !adapter->curr_cmd &&
                    mwifiex_is_send_cmd_allowed
                    (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
@@ -359,6 +372,13 @@ process_start:
                        }
                }
 
+               /** If USB Multi channel setup ongoing,
+                *  wait for ready to tx data.
+                */
+               if (adapter->iface_type == MWIFIEX_USB &&
+                   adapter->usb_mc_setup)
+                       continue;
+
                if ((adapter->scan_chan_gap_enabled ||
                     !adapter->scan_processing) &&
                    !adapter->data_sent &&
@@ -928,6 +948,32 @@ mwifiex_tx_timeout(struct net_device *dev)
        }
 }
 
+void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter)
+{
+       struct usb_card_rec *card = adapter->card;
+       struct mwifiex_private *priv;
+       u16 tx_buf_size;
+       int i, ret;
+
+       card->mc_resync_flag = true;
+       for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
+               if (atomic_read(&card->port[i].tx_data_urb_pending)) {
+                       mwifiex_dbg(adapter, WARN, "pending data urb in sys\n");
+                       return;
+               }
+       }
+
+       card->mc_resync_flag = false;
+       tx_buf_size = 0xffff;
+       priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+       ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
+                              HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false);
+       if (ret)
+               mwifiex_dbg(adapter, ERROR,
+                           "send reconfig tx buf size cmd err\n");
+}
+EXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync);
+
 void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter)
 {
        void *p;
@@ -963,8 +1009,10 @@ void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter)
                cardp = (struct usb_card_rec *)adapter->card;
                p += sprintf(p, "tx_cmd_urb_pending = %d\n",
                             atomic_read(&cardp->tx_cmd_urb_pending));
-               p += sprintf(p, "tx_data_urb_pending = %d\n",
-                            atomic_read(&cardp->tx_data_urb_pending));
+               p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n",
+                            atomic_read(&cardp->port[0].tx_data_urb_pending));
+               p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n",
+                            atomic_read(&cardp->port[1].tx_data_urb_pending));
                p += sprintf(p, "rx_cmd_urb_pending = %d\n",
                             atomic_read(&cardp->rx_cmd_urb_pending));
                p += sprintf(p, "rx_data_urb_pending = %d\n",
@@ -1151,6 +1199,7 @@ static const struct net_device_ops mwifiex_netdev_ops = {
        .ndo_stop = mwifiex_close,
        .ndo_start_xmit = mwifiex_hard_start_xmit,
        .ndo_set_mac_address = mwifiex_set_mac_address,
+       .ndo_validate_addr = eth_validate_addr,
        .ndo_tx_timeout = mwifiex_tx_timeout,
        .ndo_get_stats = mwifiex_get_stats,
        .ndo_set_rx_mode = mwifiex_set_multicast_list,
@@ -1447,6 +1496,26 @@ exit_sem_err:
 }
 EXPORT_SYMBOL_GPL(mwifiex_remove_card);
 
+void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask,
+                 const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+
+       if (!adapter->dev || !(adapter->debug_mask & mask))
+               return;
+
+       va_start(args, fmt);
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       dev_info(adapter->dev, "%pV", &vaf);
+
+       va_end(args);
+}
+EXPORT_SYMBOL_GPL(_mwifiex_dbg);
+
 /*
  * This function initializes the module.
  *
index 6b9512140e7aa6b21d36e55d128486cf29210e74..3959f1c97f4ea588dbc7f0b379659496a96fea4e 100644 (file)
@@ -48,6 +48,9 @@
 
 extern const char driver_version[];
 
+struct mwifiex_adapter;
+struct mwifiex_private;
+
 enum {
        MWIFIEX_ASYNC_CMD,
        MWIFIEX_SYNC_CMD
@@ -180,12 +183,11 @@ enum MWIFIEX_DEBUG_LEVEL {
                                        MWIFIEX_DBG_FATAL | \
                                        MWIFIEX_DBG_ERROR)
 
-#define mwifiex_dbg(adapter, dbg_mask, fmt, args...)           \
-do {                                                           \
-       if ((adapter)->debug_mask & MWIFIEX_DBG_##dbg_mask)     \
-               if ((adapter)->dev)                             \
-                       dev_info((adapter)->dev, fmt, ## args); \
-} while (0)
+__printf(3, 4)
+void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask,
+                 const char *fmt, ...);
+#define mwifiex_dbg(adapter, mask, fmt, ...)                           \
+       _mwifiex_dbg(adapter, MWIFIEX_DBG_##mask, fmt, ##__VA_ARGS__)
 
 #define DEBUG_DUMP_DATA_MAX_LEN                128
 #define mwifiex_dbg_dump(adapter, dbg_mask, str, buf, len)     \
@@ -506,9 +508,6 @@ enum mwifiex_iface_work_flags {
        MWIFIEX_IFACE_WORK_CARD_RESET,
 };
 
-struct mwifiex_adapter;
-struct mwifiex_private;
-
 struct mwifiex_private {
        struct mwifiex_adapter *adapter;
        u8 bss_type;
@@ -520,6 +519,7 @@ struct mwifiex_private {
        u8 curr_addr[ETH_ALEN];
        u8 media_connected;
        u8 port_open;
+       u8 usb_port;
        u32 num_tx_timeout;
        /* track consecutive timeout */
        u8 tx_timeout_cnt;
@@ -816,6 +816,8 @@ struct mwifiex_if_ops {
        void (*iface_work)(struct work_struct *work);
        void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter);
        void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *);
+       void (*multi_port_resync)(struct mwifiex_adapter *);
+       bool (*is_port_ready)(struct mwifiex_private *);
 };
 
 struct mwifiex_adapter {
@@ -861,6 +863,8 @@ struct mwifiex_adapter {
        u8 more_task_flag;
        u16 tx_buf_size;
        u16 curr_tx_buf_size;
+       /* sdio single port rx aggregation capability */
+       bool host_disable_sdio_rx_aggr;
        bool sdio_rx_aggr_enable;
        u16 sdio_rx_block_size;
        u32 ioport;
@@ -988,6 +992,8 @@ struct mwifiex_adapter {
        u8 coex_rx_win_size;
        bool drcs_enabled;
        u8 active_scan_triggered;
+       bool usb_mc_status;
+       bool usb_mc_setup;
 };
 
 void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter);
@@ -1561,6 +1567,7 @@ void mwifiex_process_tx_pause_event(struct mwifiex_private *priv,
                                    struct sk_buff *event);
 void mwifiex_process_multi_chan_event(struct mwifiex_private *priv,
                                      struct sk_buff *event_skb);
+void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter);
 
 #ifdef CONFIG_DEBUG_FS
 void mwifiex_debugfs_init(void);
index 408b6846071655cbd87385fcbb1eb1f41b0fc181..21192b6f9c64fd8013edeb3dbef9585d8c210b88 100644 (file)
@@ -1815,7 +1815,6 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
        if (!card->evt_buf_list[rdptr]) {
                skb_push(skb, INTF_HEADER_LEN);
                skb_put(skb, MAX_EVENT_SIZE - skb->len);
-               memset(skb->data, 0, MAX_EVENT_SIZE);
                if (mwifiex_map_pci_memory(adapter, skb,
                                           MAX_EVENT_SIZE,
                                           PCI_DMA_FROMDEVICE))
index 5847863a2d6bec573956ff88c42a16c3495333b3..c20017ced5667c796abec645ae5cc8ca620b530f 100644 (file)
@@ -1839,14 +1839,18 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
                                            bssid, timestamp,
                                            cap_info_bitmap, beacon_period,
                                            ie_buf, ie_len, rssi, GFP_KERNEL);
-                       bss_priv = (struct mwifiex_bss_priv *)bss->priv;
-                       bss_priv->band = band;
-                       bss_priv->fw_tsf = fw_tsf;
-                       if (priv->media_connected &&
-                           !memcmp(bssid, priv->curr_bss_params.bss_descriptor
-                                   .mac_address, ETH_ALEN))
-                               mwifiex_update_curr_bss_params(priv, bss);
-                       cfg80211_put_bss(priv->wdev.wiphy, bss);
+                       if (bss) {
+                               bss_priv = (struct mwifiex_bss_priv *)bss->priv;
+                               bss_priv->band = band;
+                               bss_priv->fw_tsf = fw_tsf;
+                               if (priv->media_connected &&
+                                   !memcmp(bssid, priv->curr_bss_params.
+                                           bss_descriptor.mac_address,
+                                           ETH_ALEN))
+                                       mwifiex_update_curr_bss_params(priv,
+                                                                      bss);
+                               cfg80211_put_bss(priv->wdev.wiphy, bss);
+                       }
 
                        if ((chan->flags & IEEE80211_CHAN_RADAR) ||
                            (chan->flags & IEEE80211_CHAN_NO_IR)) {
@@ -1889,7 +1893,7 @@ mwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv)
        u8 id = 0;
        struct mwifiex_user_scan_cfg  *user_scan_cfg;
 
-       if (adapter->active_scan_triggered) {
+       if (adapter->active_scan_triggered || !priv->scan_request) {
                adapter->active_scan_triggered = false;
                return 0;
        }
index 5d05c6fe642985cd377eefec707d75112f0240ea..78a8474e1a3dce80ae83c42cb5feb67e0ff65020 100644 (file)
@@ -1606,8 +1606,9 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
                                (rx_len + MWIFIEX_SDIO_BLOCK_SIZE -
                                 1) / MWIFIEX_SDIO_BLOCK_SIZE;
                        if (rx_len <= INTF_HEADER_LEN ||
-                           (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
-                            card->mpa_rx.buf_size) {
+                           (card->mpa_rx.enabled &&
+                            ((rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
+                             card->mpa_rx.buf_size))) {
                                mwifiex_dbg(adapter, ERROR,
                                            "invalid rx_len=%d\n",
                                            rx_len);
@@ -1925,6 +1926,8 @@ error:
        if (ret) {
                kfree(card->mpa_tx.buf);
                kfree(card->mpa_rx.buf);
+               card->mpa_tx.buf_size = 0;
+               card->mpa_rx.buf_size = 0;
        }
 
        return ret;
@@ -2055,16 +2058,26 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
        ret = mwifiex_alloc_sdio_mpa_buffers(adapter,
                                             card->mp_tx_agg_buf_size,
                                             card->mp_rx_agg_buf_size);
-       if (ret) {
-               mwifiex_dbg(adapter, ERROR,
-                           "failed to alloc sdio mp-a buffers\n");
-               kfree(card->mp_regs);
-               return -1;
+
+       /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */
+       if (ret && (card->mp_tx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX ||
+                   card->mp_rx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX)) {
+               /* Disable rx single port aggregation */
+               adapter->host_disable_sdio_rx_aggr = true;
+
+               ret = mwifiex_alloc_sdio_mpa_buffers
+                       (adapter, MWIFIEX_MP_AGGR_BUF_SIZE_32K,
+                        MWIFIEX_MP_AGGR_BUF_SIZE_32K);
+               if (ret) {
+                       /* Disable multi port aggregation */
+                       card->mpa_tx.enabled = 0;
+                       card->mpa_rx.enabled = 0;
+               }
        }
 
        adapter->auto_tdls = card->can_auto_tdls;
        adapter->ext_scan = card->can_ext_scan;
-       return ret;
+       return 0;
 }
 
 /*
index a49a80dd773edbf02fe99d433d9eddee48349c76..e486867a4c67520fc2a8a8520368009975fd8878 100644 (file)
@@ -1531,6 +1531,33 @@ mwifiex_cmd_set_mc_policy(struct mwifiex_private *priv,
        return 0;
 }
 
+static int mwifiex_cmd_robust_coex(struct mwifiex_private *priv,
+                                  struct host_cmd_ds_command *cmd,
+                                  u16 cmd_action, bool *is_timeshare)
+{
+       struct host_cmd_ds_robust_coex *coex = &cmd->params.coex;
+       struct mwifiex_ie_types_robust_coex *coex_tlv;
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_ROBUST_COEX);
+       cmd->size = cpu_to_le16(sizeof(*coex) + sizeof(*coex_tlv) + S_DS_GEN);
+
+       coex->action = cpu_to_le16(cmd_action);
+       coex_tlv = (struct mwifiex_ie_types_robust_coex *)
+                               ((u8 *)coex + sizeof(*coex));
+       coex_tlv->header.type = cpu_to_le16(TLV_TYPE_ROBUST_COEX);
+       coex_tlv->header.len = cpu_to_le16(sizeof(coex_tlv->mode));
+
+       if (coex->action == HostCmd_ACT_GEN_GET)
+               return 0;
+
+       if (*is_timeshare)
+               coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_TIMESHARE);
+       else
+               coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_SPATIAL);
+
+       return 0;
+}
+
 static int
 mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv,
                         struct host_cmd_ds_command *cmd,
@@ -2040,6 +2067,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
                ret = mwifiex_cmd_set_mc_policy(priv, cmd_ptr, cmd_action,
                                                data_buf);
                break;
+       case HostCmd_CMD_ROBUST_COEX:
+               ret = mwifiex_cmd_robust_coex(priv, cmd_ptr, cmd_action,
+                                             data_buf);
+               break;
        default:
                mwifiex_dbg(priv->adapter, ERROR,
                            "PREP_CMD: unknown cmd- %#x\n", cmd_no);
@@ -2125,7 +2156,8 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init)
 
                /** Set SDIO Single Port RX Aggr Info */
                if (priv->adapter->iface_type == MWIFIEX_SDIO &&
-                   ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info)) {
+                   ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) &&
+                   !priv->adapter->host_disable_sdio_rx_aggr) {
                        sdio_sp_rx_aggr_enable = true;
                        ret = mwifiex_send_cmd(priv,
                                               HostCmd_CMD_SDIO_SP_RX_AGGR_CFG,
index 87b69d8ad120e0e3f51ffb7edfbf39ff6f1d6dbf..9ac7aa2431b41533ab68f8495693c36c41f40801 100644 (file)
@@ -1007,6 +1007,28 @@ static int mwifiex_ret_sdio_rx_aggr_cfg(struct mwifiex_private *priv,
        return 0;
 }
 
+static int mwifiex_ret_robust_coex(struct mwifiex_private *priv,
+                                  struct host_cmd_ds_command *resp,
+                                  bool *is_timeshare)
+{
+       struct host_cmd_ds_robust_coex *coex = &resp->params.coex;
+       struct mwifiex_ie_types_robust_coex *coex_tlv;
+       u16 action = le16_to_cpu(coex->action);
+       u32 mode;
+
+       coex_tlv = (struct mwifiex_ie_types_robust_coex
+                   *)((u8 *)coex + sizeof(struct host_cmd_ds_robust_coex));
+       if (action == HostCmd_ACT_GEN_GET) {
+               mode = le32_to_cpu(coex_tlv->mode);
+               if (mode == MWIFIEX_COEX_MODE_TIMESHARE)
+                       *is_timeshare = true;
+               else
+                       *is_timeshare = false;
+       }
+
+       return 0;
+}
+
 /*
  * This function handles the command responses.
  *
@@ -1128,6 +1150,17 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
                ret = mwifiex_ret_11n_addba_resp(priv, resp);
                break;
        case HostCmd_CMD_RECONFIGURE_TX_BUFF:
+               if (0xffff == (u16)le16_to_cpu(resp->params.tx_buf.buff_size)) {
+                       if (adapter->iface_type == MWIFIEX_USB &&
+                           adapter->usb_mc_setup) {
+                               if (adapter->if_ops.multi_port_resync)
+                                       adapter->if_ops.
+                                               multi_port_resync(adapter);
+                               adapter->usb_mc_setup = false;
+                               adapter->tx_lock_flag = false;
+                       }
+                       break;
+               }
                adapter->tx_buf_size = (u16) le16_to_cpu(resp->params.
                                                             tx_buf.buff_size);
                adapter->tx_buf_size = (adapter->tx_buf_size
@@ -1202,6 +1235,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
                break;
        case HostCmd_CMD_TDLS_CONFIG:
                break;
+       case HostCmd_CMD_ROBUST_COEX:
+               ret = mwifiex_ret_robust_coex(priv, resp, data_buf);
+               break;
        default:
                mwifiex_dbg(adapter, ERROR,
                            "CMD_RESP: unknown cmd response %#x\n",
index 3d18c585e5436769061a3de767e17c22674206c7..ff3ee9dfbbd54f51dbd42f8ff5478d78bc63013b 100644 (file)
@@ -313,24 +313,78 @@ void mwifiex_process_multi_chan_event(struct mwifiex_private *priv,
                                      struct sk_buff *event_skb)
 {
        struct mwifiex_ie_types_multi_chan_info *chan_info;
-       u16 status;
+       struct mwifiex_ie_types_mc_group_info *grp_info;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct mwifiex_ie_types_header *tlv;
+       u16 tlv_buf_left, tlv_type, tlv_len;
+       int intf_num, bss_type, bss_num, i;
+       struct mwifiex_private *intf_priv;
 
+       tlv_buf_left = event_skb->len - sizeof(u32);
        chan_info = (void *)event_skb->data + sizeof(u32);
 
-       if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO) {
-               mwifiex_dbg(priv->adapter, ERROR,
+       if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO ||
+           tlv_buf_left < sizeof(struct mwifiex_ie_types_multi_chan_info)) {
+               mwifiex_dbg(adapter, ERROR,
                            "unknown TLV in chan_info event\n");
                return;
        }
 
-       status = le16_to_cpu(chan_info->status);
+       adapter->usb_mc_status = le16_to_cpu(chan_info->status);
+       mwifiex_dbg(adapter, EVENT, "multi chan operation %s\n",
+                   adapter->usb_mc_status ? "started" : "over");
 
-       if (status) {
-               mwifiex_dbg(priv->adapter, EVENT,
-                           "multi-channel operation started\n");
-       } else {
-               mwifiex_dbg(priv->adapter, EVENT,
-                           "multi-channel operation over\n");
+       tlv_buf_left -= sizeof(struct mwifiex_ie_types_multi_chan_info);
+       tlv = (struct mwifiex_ie_types_header *)chan_info->tlv_buffer;
+
+       while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) {
+               tlv_type = le16_to_cpu(tlv->type);
+               tlv_len  = le16_to_cpu(tlv->len);
+               if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) >
+                   tlv_buf_left) {
+                       mwifiex_dbg(adapter, ERROR, "wrong tlv: tlvLen=%d,\t"
+                                   "tlvBufLeft=%d\n", tlv_len, tlv_buf_left);
+                       break;
+               }
+               if (tlv_type != TLV_TYPE_MC_GROUP_INFO) {
+                       mwifiex_dbg(adapter, ERROR, "wrong tlv type: 0x%x\n",
+                                   tlv_type);
+                       break;
+               }
+
+               grp_info = (struct mwifiex_ie_types_mc_group_info *)tlv;
+               intf_num = grp_info->intf_num;
+               for (i = 0; i < intf_num; i++) {
+                       bss_type = grp_info->bss_type_numlist[i] >> 4;
+                       bss_num = grp_info->bss_type_numlist[i] & BSS_NUM_MASK;
+                       intf_priv = mwifiex_get_priv_by_id(adapter, bss_num,
+                                                          bss_type);
+                       if (!intf_priv) {
+                               mwifiex_dbg(adapter, ERROR,
+                                           "Invalid bss_type bss_num\t"
+                                           "in multi channel event\n");
+                               continue;
+                       }
+                       if (adapter->iface_type == MWIFIEX_USB) {
+                               u8 ep;
+
+                               ep = grp_info->hid_num.usb_ep_num;
+                               if (ep == MWIFIEX_USB_EP_DATA ||
+                                   ep == MWIFIEX_USB_EP_DATA_CH2)
+                                       intf_priv->usb_port = ep;
+                       }
+               }
+
+               tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) +
+                               tlv_len;
+               tlv = (void *)((u8 *)tlv + tlv_len +
+                              sizeof(struct mwifiex_ie_types_header));
+       }
+
+       if (adapter->iface_type == MWIFIEX_USB) {
+               adapter->tx_lock_flag = true;
+               adapter->usb_mc_setup = true;
+               mwifiex_multi_chan_resync(adapter);
        }
 }
 
@@ -562,7 +616,9 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
                adapter->tx_lock_flag = false;
                if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) {
                        if (mwifiex_check_last_packet_indication(priv)) {
-                               if (adapter->data_sent) {
+                               if (adapter->data_sent ||
+                                   (adapter->if_ops.is_port_ready &&
+                                    !adapter->if_ops.is_port_ready(priv))) {
                                        adapter->ps_state = PS_STATE_AWAKE;
                                        adapter->pm_wakeup_card_req = false;
                                        adapter->pm_wakeup_fw_try = false;
index 355ac5904fac1174e0ee200dcda012f06313a963..f6683ea6bd5ddfb64a64c0168ea26db346c45281 100644 (file)
@@ -153,6 +153,10 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags)
        if (adapter->data_sent)
                return -1;
 
+       if (adapter->if_ops.is_port_ready &&
+           !adapter->if_ops.is_port_ready(priv))
+               return -1;
+
        skb = dev_alloc_skb(data_len);
        if (!skb)
                return -1;
@@ -174,7 +178,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags)
        local_tx_pd->bss_type = priv->bss_type;
 
        if (adapter->iface_type == MWIFIEX_USB) {
-               ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA,
+               ret = adapter->if_ops.host_to_card(adapter, priv->usb_port,
                                                   skb, NULL);
        } else {
                skb_push(skb, INTF_HEADER_LEN);
@@ -191,7 +195,6 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags)
                adapter->dbg.num_tx_host_to_card_failure++;
                break;
        case -1:
-               adapter->data_sent = false;
                dev_kfree_skb_any(skb);
                mwifiex_dbg(adapter, ERROR,
                            "%s: host_to_card failed: ret=%d\n",
index b3e163de98995ba62767ab904f636853e2847ac3..9275f9c3f869bbb0bc43be93f7480e8cf3ffba9f 100644 (file)
@@ -204,6 +204,12 @@ mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac,
                return -1;
        }
 
+       if (!(le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info))) {
+               mwifiex_dbg(priv->adapter, WARN,
+                           "TDLS peer doesn't support ht capabilities\n");
+               return 0;
+       }
+
        pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2);
        *pos++ = WLAN_EID_HT_OPERATION;
        *pos++ = sizeof(struct ieee80211_ht_operation);
@@ -252,6 +258,12 @@ static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv,
                return -1;
        }
 
+       if (!(le32_to_cpu(sta_ptr->tdls_cap.vhtcap.vht_cap_info))) {
+               mwifiex_dbg(adapter, WARN,
+                           "TDLS peer doesn't support vht capabilities\n");
+               return 0;
+       }
+
        if (!mwifiex_is_bss_in_11ac_mode(priv)) {
                if (sta_ptr->tdls_cap.extcap.ext_capab[7] &
                   WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) {
index 8b1e5b5d47feee82a634a8de5856a94a135884fe..bf6182b646a5436ad083623fa2c2980ce8019f28 100644 (file)
@@ -115,9 +115,8 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
                if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
                        local_tx_pd = (struct txpd *)(head_ptr + hroom);
                if (adapter->iface_type == MWIFIEX_USB) {
-                       adapter->data_sent = true;
                        ret = adapter->if_ops.host_to_card(adapter,
-                                                          MWIFIEX_USB_EP_DATA,
+                                                          priv->usb_port,
                                                           skb, NULL);
                } else {
                        ret = adapter->if_ops.host_to_card(adapter,
@@ -130,7 +129,7 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
 
        switch (ret) {
        case -ENOSR:
-               mwifiex_dbg(adapter, ERROR, "data: -ENOSR is returned\n");
+               mwifiex_dbg(adapter, DATA, "data: -ENOSR is returned\n");
                break;
        case -EBUSY:
                if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
@@ -142,8 +141,6 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
                mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
                break;
        case -1:
-               if (adapter->iface_type != MWIFIEX_PCIE)
-                       adapter->data_sent = false;
                mwifiex_dbg(adapter, ERROR,
                            "mwifiex_write_data_async failed: 0x%X\n",
                            ret);
@@ -151,8 +148,6 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
                mwifiex_write_data_complete(adapter, skb, 0, ret);
                break;
        case -EINPROGRESS:
-               if (adapter->iface_type != MWIFIEX_PCIE)
-                       adapter->data_sent = false;
                break;
        case 0:
                mwifiex_write_data_complete(adapter, skb, 0, ret);
@@ -193,9 +188,8 @@ static int mwifiex_host_to_card(struct mwifiex_adapter *adapter,
        }
 
        if (adapter->iface_type == MWIFIEX_USB) {
-               adapter->data_sent = true;
                ret = adapter->if_ops.host_to_card(adapter,
-                                                  MWIFIEX_USB_EP_DATA,
+                                                  priv->usb_port,
                                                   skb, NULL);
        } else {
                ret = adapter->if_ops.host_to_card(adapter,
@@ -222,16 +216,12 @@ static int mwifiex_host_to_card(struct mwifiex_adapter *adapter,
                mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
                break;
        case -1:
-               if (adapter->iface_type != MWIFIEX_PCIE)
-                       adapter->data_sent = false;
                mwifiex_dbg(adapter, ERROR,
                            "mwifiex_write_data_async failed: 0x%X\n", ret);
                adapter->dbg.num_tx_host_to_card_failure++;
                mwifiex_write_data_complete(adapter, skb, 0, ret);
                break;
        case -EINPROGRESS:
-               if (adapter->iface_type != MWIFIEX_PCIE)
-                       adapter->data_sent = false;
                break;
        case 0:
                mwifiex_write_data_complete(adapter, skb, 0, ret);
@@ -306,9 +296,6 @@ int mwifiex_write_data_complete(struct mwifiex_adapter *adapter,
        if (!priv)
                goto done;
 
-       if (adapter->iface_type == MWIFIEX_USB)
-               adapter->data_sent = false;
-
        mwifiex_set_trans_start(priv->netdev);
        if (!status) {
                priv->stats.tx_packets++;
index 4d5a6e3b6361700c9bcab06201a70cd29d38b66e..759a6ada5b0fb544b9ac384dac8aae7c182bc877 100644 (file)
@@ -846,22 +846,6 @@ int mwifiex_config_start_uap(struct mwifiex_private *priv,
 {
        enum state_11d_t state_11d;
 
-       if (mwifiex_del_mgmt_ies(priv))
-               mwifiex_dbg(priv->adapter, ERROR,
-                           "Failed to delete mgmt IEs!\n");
-
-       if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
-                            HostCmd_ACT_GEN_SET, 0, NULL, true)) {
-               mwifiex_dbg(priv->adapter, ERROR, "Failed to stop the BSS\n");
-               return -1;
-       }
-
-       if (mwifiex_send_cmd(priv, HOST_CMD_APCMD_SYS_RESET,
-                            HostCmd_ACT_GEN_SET, 0, NULL, true)) {
-               mwifiex_dbg(priv->adapter, ERROR, "Failed to reset BSS\n");
-               return -1;
-       }
-
        if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
                             HostCmd_ACT_GEN_SET,
                             UAP_BSS_PARAMS_I, bss_cfg, false)) {
index 46c972a650a43c2918bb7703c98eeb877ccb79e6..86ff54296f39b6c8f2c9855cfb43fabd070976f8 100644 (file)
@@ -179,19 +179,12 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
        case EVENT_UAP_BSS_IDLE:
                priv->media_connected = false;
                priv->port_open = false;
-               if (netif_carrier_ok(priv->netdev))
-                       netif_carrier_off(priv->netdev);
-               mwifiex_stop_net_dev_queue(priv->netdev, adapter);
-
                mwifiex_clean_txrx(priv);
                mwifiex_del_all_sta_list(priv);
                break;
        case EVENT_UAP_BSS_ACTIVE:
                priv->media_connected = true;
                priv->port_open = true;
-               if (!netif_carrier_ok(priv->netdev))
-                       netif_carrier_on(priv->netdev);
-               mwifiex_wake_up_net_dev_queue(priv->netdev, adapter);
                break;
        case EVENT_UAP_BSS_START:
                mwifiex_dbg(adapter, EVENT,
@@ -269,7 +262,9 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
                adapter->tx_lock_flag = false;
                if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) {
                        if (mwifiex_check_last_packet_indication(priv)) {
-                               if (adapter->data_sent) {
+                               if (adapter->data_sent ||
+                                   (adapter->if_ops.is_port_ready &&
+                                    !adapter->if_ops.is_port_ready(priv))) {
                                        adapter->ps_state = PS_STATE_AWAKE;
                                        adapter->pm_wakeup_card_req = false;
                                        adapter->pm_wakeup_fw_try = false;
index 87667418af5ff528d2100f8674c293fc564067ab..74d5d7238633ede9ab43a6ca89d8cdfd1b798de8 100644 (file)
@@ -31,7 +31,8 @@
  */
 static bool
 mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv,
-                                 struct list_head *ra_list_head)
+                                 struct list_head *ra_list_head,
+                                 int tid)
 {
        struct mwifiex_ra_list_tbl *ra_list;
        struct sk_buff *skb, *tmp;
@@ -49,7 +50,10 @@ mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv,
                                __skb_unlink(skb, &ra_list->skb_head);
                                mwifiex_write_data_complete(adapter, skb, 0,
                                                            -1);
-                               atomic_dec(&priv->wmm.tx_pkts_queued);
+                               if (ra_list->tx_paused)
+                                       priv->wmm.pkts_paused[tid]--;
+                               else
+                                       atomic_dec(&priv->wmm.tx_pkts_queued);
                                pkt_deleted = true;
                        }
                        if ((atomic_read(&adapter->pending_bridged_pkts) <=
@@ -77,7 +81,7 @@ static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv)
                if (priv->del_list_idx == MAX_NUM_TID)
                        priv->del_list_idx = 0;
                ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list;
-               if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list)) {
+               if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) {
                        priv->del_list_idx++;
                        break;
                }
index 5e789b2e06ea658d78f9997041a7c9853de171ae..e43aff932360ef3e99801956b5bca6b8e8649303 100644 (file)
@@ -42,11 +42,6 @@ static struct usb_device_id mwifiex_usb_table[] = {
        {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8801_PID_2,
                                       USB_CLASS_VENDOR_SPEC,
                                       USB_SUBCLASS_VENDOR_SPEC, 0xff)},
-       /* 8897 */
-       {USB_DEVICE(USB8XXX_VID, USB8897_PID_1)},
-       {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8897_PID_2,
-                                      USB_CLASS_VENDOR_SPEC,
-                                      USB_SUBCLASS_VENDOR_SPEC, 0xff)},
        /* 8997 */
        {USB_DEVICE(USB8XXX_VID, USB8997_PID_1)},
        {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8997_PID_2,
@@ -264,6 +259,8 @@ static void mwifiex_usb_tx_complete(struct urb *urb)
        struct urb_context *context = (struct urb_context *)(urb->context);
        struct mwifiex_adapter *adapter = context->adapter;
        struct usb_card_rec *card = adapter->card;
+       struct usb_tx_data_port *port;
+       int i;
 
        mwifiex_dbg(adapter, INFO,
                    "%s: status: %d\n", __func__, urb->status);
@@ -276,11 +273,22 @@ static void mwifiex_usb_tx_complete(struct urb *urb)
        } else {
                mwifiex_dbg(adapter, DATA,
                            "%s: DATA\n", __func__);
-               atomic_dec(&card->tx_data_urb_pending);
+               for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
+                       port = &card->port[i];
+                       if (context->ep == port->tx_data_ep) {
+                               atomic_dec(&port->tx_data_urb_pending);
+                               port->block_status = false;
+                               break;
+                       }
+               }
+               adapter->data_sent = false;
                mwifiex_write_data_complete(adapter, context->skb, 0,
                                            urb->status ? -1 : 0);
        }
 
+       if (card->mc_resync_flag)
+               mwifiex_multi_chan_resync(adapter);
+
        mwifiex_queue_main_work(adapter);
 
        return;
@@ -327,7 +335,8 @@ static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size)
 
 static void mwifiex_usb_free(struct usb_card_rec *card)
 {
-       int i;
+       struct usb_tx_data_port *port;
+       int i, j;
 
        if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb)
                usb_kill_urb(card->rx_cmd.urb);
@@ -345,9 +354,12 @@ static void mwifiex_usb_free(struct usb_card_rec *card)
                card->rx_data_list[i].urb = NULL;
        }
 
-       for (i = 0; i < MWIFIEX_TX_DATA_URB; i++) {
-               usb_free_urb(card->tx_data_list[i].urb);
-               card->tx_data_list[i].urb = NULL;
+       for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
+               port = &card->port[i];
+               for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) {
+                       usb_free_urb(port->tx_data_list[j].urb);
+                       port->tx_data_list[j].urb = NULL;
+               }
        }
 
        usb_free_urb(card->tx_cmd.urb);
@@ -386,14 +398,12 @@ static int mwifiex_usb_probe(struct usb_interface *intf,
        case USB8766_PID_1:
        case USB8797_PID_1:
        case USB8801_PID_1:
-       case USB8897_PID_1:
        case USB8997_PID_1:
                card->usb_boot_state = USB8XXX_FW_DNLD;
                break;
        case USB8766_PID_2:
        case USB8797_PID_2:
        case USB8801_PID_2:
-       case USB8897_PID_2:
        case USB8997_PID_2:
                card->usb_boot_state = USB8XXX_FW_READY;
                break;
@@ -437,8 +447,18 @@ static int mwifiex_usb_probe(struct usb_interface *intf,
                        pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n",
                                 le16_to_cpu(epd->wMaxPacketSize),
                                 epd->bEndpointAddress);
-                       card->tx_data_ep = usb_endpoint_num(epd);
-                       atomic_set(&card->tx_data_urb_pending, 0);
+                       card->port[0].tx_data_ep = usb_endpoint_num(epd);
+                       atomic_set(&card->port[0].tx_data_urb_pending, 0);
+               }
+               if (usb_endpoint_dir_out(epd) &&
+                   usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA_CH2 &&
+                   usb_endpoint_xfer_bulk(epd)) {
+                       pr_debug("info: bulk OUT chan2:\t"
+                                "max pkt size: %d, addr: %d\n",
+                                le16_to_cpu(epd->wMaxPacketSize),
+                                epd->bEndpointAddress);
+                       card->port[1].tx_data_ep = usb_endpoint_num(epd);
+                       atomic_set(&card->port[1].tx_data_urb_pending, 0);
                }
                if (usb_endpoint_dir_out(epd) &&
                    usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT &&
@@ -480,7 +500,8 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct usb_card_rec *card = usb_get_intfdata(intf);
        struct mwifiex_adapter *adapter;
-       int i;
+       struct usb_tx_data_port *port;
+       int i, j;
 
        if (!card || !card->adapter) {
                pr_err("%s: card or card->adapter is NULL\n", __func__);
@@ -511,9 +532,13 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message)
                        if (card->rx_data_list[i].urb)
                                usb_kill_urb(card->rx_data_list[i].urb);
 
-       for (i = 0; i < MWIFIEX_TX_DATA_URB; i++)
-               if (card->tx_data_list[i].urb)
-                       usb_kill_urb(card->tx_data_list[i].urb);
+       for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
+               port = &card->port[i];
+               for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) {
+                       if (port->tx_data_list[j].urb)
+                               usb_kill_urb(port->tx_data_list[j].urb);
+               }
+       }
 
        if (card->tx_cmd.urb)
                usb_kill_urb(card->tx_cmd.urb);
@@ -625,7 +650,8 @@ static struct usb_driver mwifiex_usb_driver = {
 static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter)
 {
        struct usb_card_rec *card = (struct usb_card_rec *)adapter->card;
-       int i;
+       struct usb_tx_data_port *port;
+       int i, j;
 
        card->tx_cmd.adapter = adapter;
        card->tx_cmd.ep = card->tx_cmd_ep;
@@ -637,17 +663,25 @@ static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter)
                return -ENOMEM;
        }
 
-       card->tx_data_ix = 0;
-
-       for (i = 0; i < MWIFIEX_TX_DATA_URB; i++) {
-               card->tx_data_list[i].adapter = adapter;
-               card->tx_data_list[i].ep = card->tx_data_ep;
-
-               card->tx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL);
-               if (!card->tx_data_list[i].urb) {
-                       mwifiex_dbg(adapter, ERROR,
-                                   "tx_data_list[] urb allocation failed\n");
-                       return -ENOMEM;
+       for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
+               port = &card->port[i];
+               if (!port->tx_data_ep)
+                       continue;
+               port->tx_data_ix = 0;
+               if (port->tx_data_ep == MWIFIEX_USB_EP_DATA)
+                       port->block_status = false;
+               else
+                       port->block_status = true;
+               for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) {
+                       port->tx_data_list[j].adapter = adapter;
+                       port->tx_data_list[j].ep = port->tx_data_ep;
+                       port->tx_data_list[j].urb =
+                                       usb_alloc_urb(0, GFP_KERNEL);
+                       if (!port->tx_data_list[j].urb) {
+                               mwifiex_dbg(adapter, ERROR,
+                                           "urb allocation failed\n");
+                               return -ENOMEM;
+                       }
                }
        }
 
@@ -736,15 +770,89 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf,
        return ret;
 }
 
+static void mwifiex_usb_port_resync(struct mwifiex_adapter *adapter)
+{
+       struct usb_card_rec *card = adapter->card;
+       u8 active_port = MWIFIEX_USB_EP_DATA;
+       struct mwifiex_private *priv = NULL;
+       int i;
+
+       if (adapter->usb_mc_status) {
+               for (i = 0; i < adapter->priv_num; i++) {
+                       priv = adapter->priv[i];
+                       if (!priv)
+                               continue;
+                       if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP &&
+                            !priv->bss_started) ||
+                           (priv->bss_role == MWIFIEX_BSS_ROLE_STA &&
+                            !priv->media_connected))
+                               priv->usb_port = MWIFIEX_USB_EP_DATA;
+               }
+               for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++)
+                       card->port[i].block_status = false;
+       } else {
+               for (i = 0; i < adapter->priv_num; i++) {
+                       priv = adapter->priv[i];
+                       if (!priv)
+                               continue;
+                       if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP &&
+                            priv->bss_started) ||
+                           (priv->bss_role == MWIFIEX_BSS_ROLE_STA &&
+                            priv->media_connected)) {
+                               active_port = priv->usb_port;
+                               break;
+                       }
+               }
+               for (i = 0; i < adapter->priv_num; i++) {
+                       priv = adapter->priv[i];
+                       if (priv)
+                               priv->usb_port = active_port;
+               }
+               for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
+                       if (active_port == card->port[i].tx_data_ep)
+                               card->port[i].block_status = false;
+                       else
+                               card->port[i].block_status = true;
+               }
+       }
+}
+
+static bool mwifiex_usb_is_port_ready(struct mwifiex_private *priv)
+{
+       struct usb_card_rec *card = priv->adapter->card;
+       int idx;
+
+       for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) {
+               if (priv->usb_port == card->port[idx].tx_data_ep)
+                       return !card->port[idx].block_status;
+       }
+
+       return false;
+}
+
+static inline u8 mwifiex_usb_data_sent(struct mwifiex_adapter *adapter)
+{
+       struct usb_card_rec *card = adapter->card;
+       int i;
+
+       for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++)
+               if (!card->port[i].block_status)
+                       return false;
+
+       return true;
+}
+
 /* This function write a command/data packet to card. */
 static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep,
                                    struct sk_buff *skb,
                                    struct mwifiex_tx_param *tx_param)
 {
        struct usb_card_rec *card = adapter->card;
-       struct urb_context *context;
+       struct urb_context *context = NULL;
+       struct usb_tx_data_port *port = NULL;
        u8 *data = (u8 *)skb->data;
        struct urb *tx_urb;
+       int idx, ret;
 
        if (adapter->is_suspended) {
                mwifiex_dbg(adapter, ERROR,
@@ -757,19 +865,31 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep,
                return -1;
        }
 
-       if (ep == card->tx_data_ep &&
-           atomic_read(&card->tx_data_urb_pending) >= MWIFIEX_TX_DATA_URB) {
-               return -EBUSY;
-       }
-
        mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep);
 
        if (ep == card->tx_cmd_ep) {
                context = &card->tx_cmd;
        } else {
-               if (card->tx_data_ix >= MWIFIEX_TX_DATA_URB)
-                       card->tx_data_ix = 0;
-               context = &card->tx_data_list[card->tx_data_ix++];
+               for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) {
+                       if (ep == card->port[idx].tx_data_ep) {
+                               port = &card->port[idx];
+                               if (atomic_read(&port->tx_data_urb_pending)
+                                   >= MWIFIEX_TX_DATA_URB) {
+                                       port->block_status = true;
+                                       ret = -EBUSY;
+                                       goto done;
+                               }
+                               if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB)
+                                       port->tx_data_ix = 0;
+                               context =
+                                       &port->tx_data_list[port->tx_data_ix++];
+                               break;
+                       }
+               }
+               if (!port) {
+                       mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n");
+                       return -1;
+               }
        }
 
        context->adapter = adapter;
@@ -786,7 +906,7 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep,
        if (ep == card->tx_cmd_ep)
                atomic_inc(&card->tx_cmd_urb_pending);
        else
-               atomic_inc(&card->tx_data_urb_pending);
+               atomic_inc(&port->tx_data_urb_pending);
 
        if (usb_submit_urb(tx_urb, GFP_ATOMIC)) {
                mwifiex_dbg(adapter, ERROR,
@@ -794,22 +914,32 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep,
                if (ep == card->tx_cmd_ep) {
                        atomic_dec(&card->tx_cmd_urb_pending);
                } else {
-                       atomic_dec(&card->tx_data_urb_pending);
-                       if (card->tx_data_ix)
-                               card->tx_data_ix--;
+                       atomic_dec(&port->tx_data_urb_pending);
+                       port->block_status = false;
+                       if (port->tx_data_ix)
+                               port->tx_data_ix--;
                        else
-                               card->tx_data_ix = MWIFIEX_TX_DATA_URB;
+                               port->tx_data_ix = MWIFIEX_TX_DATA_URB;
                }
 
                return -1;
        } else {
-               if (ep == card->tx_data_ep &&
-                   atomic_read(&card->tx_data_urb_pending) ==
-                                                       MWIFIEX_TX_DATA_URB)
-                       return -ENOSR;
+               if (ep != card->tx_cmd_ep &&
+                   atomic_read(&port->tx_data_urb_pending) ==
+                                                       MWIFIEX_TX_DATA_URB) {
+                       port->block_status = true;
+                       ret = -ENOSR;
+                       goto done;
+               }
        }
 
        return -EINPROGRESS;
+
+done:
+       if (ep != card->tx_cmd_ep)
+               adapter->data_sent = mwifiex_usb_data_sent(adapter);
+
+       return ret;
 }
 
 /* This function register usb device and initialize parameter. */
@@ -827,12 +957,6 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
                strcpy(adapter->fw_name, USB8997_DEFAULT_FW_NAME);
                adapter->ext_scan = true;
                break;
-       case USB8897_PID_1:
-       case USB8897_PID_2:
-               adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K;
-               strcpy(adapter->fw_name, USB8897_DEFAULT_FW_NAME);
-               adapter->ext_scan = true;
-               break;
        case USB8766_PID_1:
        case USB8766_PID_2:
                adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
@@ -853,6 +977,9 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
                break;
        }
 
+       adapter->usb_mc_status = false;
+       adapter->usb_mc_setup = false;
+
        return 0;
 }
 
@@ -1082,6 +1209,8 @@ static struct mwifiex_if_ops usb_ops = {
        .event_complete =       mwifiex_usb_cmd_event_complete,
        .host_to_card =         mwifiex_usb_host_to_card,
        .submit_rem_rx_urbs =   mwifiex_usb_submit_rem_rx_urbs,
+       .multi_port_resync =    mwifiex_usb_port_resync,
+       .is_port_ready =        mwifiex_usb_is_port_ready,
 };
 
 /* This function initializes the USB driver module.
@@ -1135,5 +1264,4 @@ MODULE_LICENSE("GPL v2");
 MODULE_FIRMWARE(USB8766_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(USB8801_DEFAULT_FW_NAME);
-MODULE_FIRMWARE(USB8897_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(USB8997_DEFAULT_FW_NAME);
index f0051f8c8981eb4d7bd1614999a2db9238a66fd2..b4e9246bbcdc9080d3b9bddec7e0f3aab1d16fa1 100644 (file)
 #define USB8766_PID_2          0x2042
 #define USB8797_PID_1          0x2043
 #define USB8797_PID_2          0x2044
-#define USB8897_PID_1          0x2045
-#define USB8897_PID_2          0x2046
 #define USB8801_PID_1          0x2049
 #define USB8801_PID_2          0x204a
-#define USB8997_PID_1          0x204d
+#define USB8997_PID_1          0x2052
 #define USB8997_PID_2          0x204e
 
 
@@ -40,6 +38,7 @@
 #define USB8XXX_FW_READY       2
 #define USB8XXX_FW_MAX_RETRY   3
 
+#define MWIFIEX_TX_DATA_PORT   2
 #define MWIFIEX_TX_DATA_URB    6
 #define MWIFIEX_RX_DATA_URB    6
 #define MWIFIEX_USB_TIMEOUT    100
@@ -47,7 +46,6 @@
 #define USB8766_DEFAULT_FW_NAME        "mrvl/usb8766_uapsta.bin"
 #define USB8797_DEFAULT_FW_NAME        "mrvl/usb8797_uapsta.bin"
 #define USB8801_DEFAULT_FW_NAME        "mrvl/usb8801_uapsta.bin"
-#define USB8897_DEFAULT_FW_NAME        "mrvl/usb8897_uapsta.bin"
 #define USB8997_DEFAULT_FW_NAME        "mrvl/usb8997_uapsta.bin"
 
 #define FW_DNLD_TX_BUF_SIZE    620
@@ -64,6 +62,14 @@ struct urb_context {
        u8 ep;
 };
 
+struct usb_tx_data_port {
+       u8 tx_data_ep;
+       u8 block_status;
+       atomic_t tx_data_urb_pending;
+       int tx_data_ix;
+       struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB];
+};
+
 struct usb_card_rec {
        struct mwifiex_adapter *adapter;
        struct usb_device *udev;
@@ -75,14 +81,12 @@ struct usb_card_rec {
        u8 usb_boot_state;
        u8 rx_data_ep;
        atomic_t rx_data_urb_pending;
-       u8 tx_data_ep;
        u8 tx_cmd_ep;
-       atomic_t tx_data_urb_pending;
        atomic_t tx_cmd_urb_pending;
        int bulk_out_maxpktsize;
        struct urb_context tx_cmd;
-       int tx_data_ix;
-       struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB];
+       u8 mc_resync_flag;
+       struct usb_tx_data_port port[MWIFIEX_TX_DATA_PORT];
 };
 
 struct fw_header {
index 173d3663c2e042bfe44e680e04acf86cf43f7c30..acccd6734e3b332cb172789e9b210c077465371f 100644 (file)
@@ -117,22 +117,15 @@ mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra)
  */
 static u8 mwifiex_get_random_ba_threshold(void)
 {
-       u32 sec, usec;
-       struct timeval ba_tstamp;
-       u8 ba_threshold;
-
+       u64 ns;
        /* setup ba_packet_threshold here random number between
         * [BA_SETUP_PACKET_OFFSET,
         * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1]
         */
+       ns = ktime_get_ns();
+       ns += (ns >> 32) + (ns >> 16);
 
-       do_gettimeofday(&ba_tstamp);
-       sec = (ba_tstamp.tv_sec & 0xFFFF) + (ba_tstamp.tv_sec >> 16);
-       usec = (ba_tstamp.tv_usec & 0xFFFF) + (ba_tstamp.tv_usec >> 16);
-       ba_threshold = (((sec << 16) + usec) % BA_SETUP_MAX_PACKET_THRESHOLD)
-                                                     + BA_SETUP_PACKET_OFFSET;
-
-       return ba_threshold;
+       return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET;
 }
 
 /*
@@ -160,7 +153,6 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra)
                ra_list->tdls_link = false;
                ra_list->ba_status = BA_SETUP_NONE;
                ra_list->amsdu_in_ampdu = false;
-               ra_list->tx_paused = false;
                if (!mwifiex_queuing_ra_based(priv)) {
                        if (mwifiex_is_tdls_link_setup
                                (mwifiex_get_tdls_link_status(priv, ra))) {
@@ -173,6 +165,8 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra)
                } else {
                        spin_lock_irqsave(&priv->sta_list_spinlock, flags);
                        node = mwifiex_get_sta_entry(priv, ra);
+                       if (node)
+                               ra_list->tx_paused = node->tx_pause;
                        ra_list->is_11n_enabled =
                                      mwifiex_is_sta_11n_enabled(priv, node);
                        if (ra_list->is_11n_enabled)
@@ -451,7 +445,21 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter)
 
 int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter)
 {
-       return atomic_read(&adapter->bypass_tx_pending) ? false : true;
+       struct mwifiex_private *priv;
+       int i;
+
+       for (i = 0; i < adapter->priv_num; i++) {
+               priv = adapter->priv[i];
+               if (!priv)
+                       continue;
+               if (adapter->if_ops.is_port_ready &&
+                   !adapter->if_ops.is_port_ready(priv))
+                       continue;
+               if (!skb_queue_empty(&priv->bypass_txq))
+                       return false;
+       }
+
+       return true;
 }
 
 /*
@@ -465,9 +473,14 @@ mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter)
 
        for (i = 0; i < adapter->priv_num; ++i) {
                priv = adapter->priv[i];
-               if (priv && !priv->port_open)
+               if (!priv)
+                       continue;
+               if (!priv->port_open)
                        continue;
-               if (priv && atomic_read(&priv->wmm.tx_pkts_queued))
+               if (adapter->if_ops.is_port_ready &&
+                   !adapter->if_ops.is_port_ready(priv))
+                       continue;
+               if (atomic_read(&priv->wmm.tx_pkts_queued))
                        return false;
        }
 
@@ -671,7 +684,7 @@ void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv,
                        if (!memcmp(ra_list->ra, mac, ETH_ALEN))
                                continue;
 
-                       if (ra_list && ra_list->tx_paused != tx_pause) {
+                       if (ra_list->tx_paused != tx_pause) {
                                pkt_cnt += ra_list->total_pkt_count;
                                ra_list->tx_paused = tx_pause;
                                if (tx_pause)
@@ -737,7 +750,11 @@ mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr)
                if (!ra_list)
                        continue;
                mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list);
-               atomic_sub(ra_list->total_pkt_count, &priv->wmm.tx_pkts_queued);
+               if (ra_list->tx_paused)
+                       priv->wmm.pkts_paused[i] -= ra_list->total_pkt_count;
+               else
+                       atomic_sub(ra_list->total_pkt_count,
+                                  &priv->wmm.tx_pkts_queued);
                list_del(&ra_list->list);
                kfree(ra_list);
        }
@@ -1086,6 +1103,10 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
                            (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0))
                                continue;
 
+                       if (adapter->if_ops.is_port_ready &&
+                           !adapter->if_ops.is_port_ready(priv_tmp))
+                               continue;
+
                        /* iterate over the WMM queues of the BSS */
                        hqp = &priv_tmp->wmm.highest_queued_prio;
                        for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) {
@@ -1321,8 +1342,7 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv,
        spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags);
 
        if (adapter->iface_type == MWIFIEX_USB) {
-               adapter->data_sent = true;
-               ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA,
+               ret = adapter->if_ops.host_to_card(adapter, priv->usb_port,
                                                   skb, NULL);
        } else {
                tx_param.next_pkt_len =
@@ -1351,15 +1371,11 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv,
                                       ra_list_flags);
                break;
        case -1:
-               if (adapter->iface_type != MWIFIEX_PCIE)
-                       adapter->data_sent = false;
                mwifiex_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret);
                adapter->dbg.num_tx_host_to_card_failure++;
                mwifiex_write_data_complete(adapter, skb, 0, ret);
                break;
        case -EINPROGRESS:
-               if (adapter->iface_type != MWIFIEX_PCIE)
-                       adapter->data_sent = false;
                break;
        case 0:
                mwifiex_write_data_complete(adapter, skb, 0, ret);
@@ -1467,6 +1483,13 @@ void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter)
        for (i = 0; i < adapter->priv_num; ++i) {
                priv = adapter->priv[i];
 
+               if (!priv)
+                       continue;
+
+               if (adapter->if_ops.is_port_ready &&
+                   !adapter->if_ops.is_port_ready(priv))
+                       continue;
+
                if (skb_queue_empty(&priv->bypass_txq))
                        continue;
 
index 9420fc61c2e6ab09aeba53d01930be1ebc9fd0f3..30e3aaae32e2288ed1f7541b6da7a42eb6a8729a 100644 (file)
@@ -5423,7 +5423,7 @@ static int
 mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                   enum ieee80211_ampdu_mlme_action action,
                   struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                  u8 buf_size)
+                  u8 buf_size, bool amsdu)
 {
 
        int i, rc = 0;
index a9e94b6db5b7e961d25ec2c58a2eb50d46bc621d..0f6ea316e38ed419ba0ef5ca88d14284a8f84029 100644 (file)
@@ -220,7 +220,7 @@ static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed)
        if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
                /* Set fragmentation */
                if (priv->has_mwo) {
-                       if (wiphy->frag_threshold < 0)
+                       if (wiphy->frag_threshold == -1)
                                frag_value = 0;
                        else {
                                printk(KERN_WARNING "%s: Fixed fragmentation "
@@ -230,7 +230,7 @@ static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed)
                                frag_value = 1;
                        }
                } else {
-                       if (wiphy->frag_threshold < 0)
+                       if (wiphy->frag_threshold == -1)
                                frag_value = 2346;
                        else if ((wiphy->frag_threshold < 257) ||
                                 (wiphy->frag_threshold > 2347))
@@ -252,7 +252,7 @@ static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed)
                 * the upper limit.
                 */
 
-               if (wiphy->rts_threshold < 0)
+               if (wiphy->rts_threshold == -1)
                        rts_value = 2347;
                else if (wiphy->rts_threshold > 2347)
                        err = -EINVAL;
index 26a57d773d3031147e5985cc004b3d80937d4081..f2cd513d54b2c21673552470efd6083d3bc5dcbe 100644 (file)
@@ -1576,6 +1576,7 @@ static int ezusb_probe(struct usb_interface *interface,
                                ezusb_hard_reset, NULL);
        if (!priv) {
                err("Couldn't allocate orinocodev");
+               retval = -ENOMEM;
                goto exit;
        }
 
diff --git a/drivers/net/wireless/realtek/Makefile b/drivers/net/wireless/realtek/Makefile
new file mode 100644 (file)
index 0000000..9c78deb
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for the Linux Wireless network device drivers for Realtek units
+#
+
+obj-$(CONFIG_RTL8180)          += rtl818x/
+obj-$(CONFIG_RTL8187)          += rtl818x/
+obj-$(CONFIG_RTLWIFI)          += rtlwifi/
+obj-$(CONFIG_RTL8XXXU)         += rtl8xxxu/
+
similarity index 69%
rename from drivers/net/wireless/rtl818x/rtl8180/Makefile
rename to drivers/net/wireless/realtek/rtl818x/rtl8180/Makefile
index 21005bd8b43c973da6ebd24556da848032727bcf..2966681efaef41845209d1b0bc055a0521da2368 100644 (file)
@@ -2,4 +2,4 @@ rtl818x_pci-objs        := dev.o rtl8225.o sa2400.o max2820.o grf5101.o rtl8225se.o
 
 obj-$(CONFIG_RTL8180)  += rtl818x_pci.o
 
-ccflags-y += -Idrivers/net/wireless/rtl818x
+ccflags-y += -Idrivers/net/wireless/realtek/rtl818x
similarity index 62%
rename from drivers/net/wireless/rtl818x/rtl8187/Makefile
rename to drivers/net/wireless/realtek/rtl818x/rtl8187/Makefile
index 7b6299268ecf346abd9c814bab564d9b7a56b7d3..ff074912a095215255af28c9810e658bc45c9bfe 100644 (file)
@@ -2,4 +2,4 @@ rtl8187-objs            := dev.o rtl8225.o leds.o rfkill.o
 
 obj-$(CONFIG_RTL8187)  += rtl8187.o
 
-ccflags-y += -Idrivers/net/wireless/rtl818x
+ccflags-y += -Idrivers/net/wireless/realtek/rtl818x
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/Kconfig b/drivers/net/wireless/realtek/rtl8xxxu/Kconfig
new file mode 100644 (file)
index 0000000..dd4d626
--- /dev/null
@@ -0,0 +1,34 @@
+#
+# RTL8XXXU Wireless LAN device configuration
+#
+config RTL8XXXU
+       tristate "RTL8723AU/RTL8188[CR]U/RTL819[12]CU (mac80211) support"
+       depends on MAC80211 && USB
+       ---help---
+         This is an alternative driver for various Realtek RTL8XXX
+         parts written to utilize the Linux mac80211 stack.
+         The driver is known to work with a number of RTL8723AU,
+         RL8188CU, RTL8188RU, RTL8191CU, and RTL8192CU devices
+
+         This driver is under development and has a limited feature
+         set. In particular it does not yet support 40MHz channels
+         and power management. However it should have a smaller
+         memory footprint than the vendor drivers and benetifs
+         from the in kernel mac80211 stack.
+
+         It can coexist with drivers from drivers/staging/rtl8723au,
+         drivers/staging/rtl8192u, and drivers/net/wireless/rtlwifi,
+         but you will need to control which module you wish to load.
+
+         To compile this driver as a module, choose M here: the module will
+         be called r8xxxu.  If unsure, say N.
+
+config RTL8XXXU_UNTESTED
+       bool "Include support for untested Realtek 8xxx USB devices (EXPERIMENTAL)"
+       depends on RTL8XXXU
+       ---help---
+         This option enables detection of Realtek 8723/8188/8191/8192 WiFi
+         USB devices which have not been tested directly by the driver
+         author or reported to be working by third parties.
+
+         Please report your results!
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/Makefile b/drivers/net/wireless/realtek/rtl8xxxu/Makefile
new file mode 100644 (file)
index 0000000..5dea3bb
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_RTL8XXXU) += rtl8xxxu.o
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c
new file mode 100644 (file)
index 0000000..6aed923
--- /dev/null
@@ -0,0 +1,5993 @@
+/*
+ * RTL8XXXU mac80211 USB driver
+ *
+ * Copyright (c) 2014 - 2015 Jes Sorensen <Jes.Sorensen@redhat.com>
+ *
+ * Portions, notably calibration code:
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This driver was written as a replacement for the vendor provided
+ * rtl8723au driver. As the Realtek 8xxx chips are very similar in
+ * their programming interface, I have started adding support for
+ * additional 8xxx chips like the 8192cu, 8188cus, etc.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/wireless.h>
+#include <linux/firmware.h>
+#include <linux/moduleparam.h>
+#include <net/mac80211.h>
+#include "rtl8xxxu.h"
+#include "rtl8xxxu_regs.h"
+
+#define DRIVER_NAME "rtl8xxxu"
+
+static int rtl8xxxu_debug;
+static bool rtl8xxxu_ht40_2g;
+
+MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@redhat.com>");
+MODULE_DESCRIPTION("RTL8XXXu USB mac80211 Wireless LAN Driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("rtlwifi/rtl8723aufw_A.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8723aufw_B.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8723aufw_B_NoBT.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8192cufw_A.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8192cufw_B.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8192cufw_TMSC.bin");
+
+module_param_named(debug, rtl8xxxu_debug, int, 0600);
+MODULE_PARM_DESC(debug, "Set debug mask");
+module_param_named(ht40_2g, rtl8xxxu_ht40_2g, bool, 0600);
+MODULE_PARM_DESC(ht40_2g, "Enable HT40 support on the 2.4GHz band");
+
+#define USB_VENDOR_ID_REALTEK          0x0bda
+/* Minimum IEEE80211_MAX_FRAME_LEN */
+#define RTL_RX_BUFFER_SIZE             IEEE80211_MAX_FRAME_LEN
+#define RTL8XXXU_RX_URBS               32
+#define RTL8XXXU_RX_URB_PENDING_WATER  8
+#define RTL8XXXU_TX_URBS               64
+#define RTL8XXXU_TX_URB_LOW_WATER      25
+#define RTL8XXXU_TX_URB_HIGH_WATER     32
+
+static int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv,
+                                 struct rtl8xxxu_rx_urb *rx_urb);
+
+static struct ieee80211_rate rtl8xxxu_rates[] = {
+       { .bitrate = 10, .hw_value = DESC_RATE_1M, .flags = 0 },
+       { .bitrate = 20, .hw_value = DESC_RATE_2M, .flags = 0 },
+       { .bitrate = 55, .hw_value = DESC_RATE_5_5M, .flags = 0 },
+       { .bitrate = 110, .hw_value = DESC_RATE_11M, .flags = 0 },
+       { .bitrate = 60, .hw_value = DESC_RATE_6M, .flags = 0 },
+       { .bitrate = 90, .hw_value = DESC_RATE_9M, .flags = 0 },
+       { .bitrate = 120, .hw_value = DESC_RATE_12M, .flags = 0 },
+       { .bitrate = 180, .hw_value = DESC_RATE_18M, .flags = 0 },
+       { .bitrate = 240, .hw_value = DESC_RATE_24M, .flags = 0 },
+       { .bitrate = 360, .hw_value = DESC_RATE_36M, .flags = 0 },
+       { .bitrate = 480, .hw_value = DESC_RATE_48M, .flags = 0 },
+       { .bitrate = 540, .hw_value = DESC_RATE_54M, .flags = 0 },
+};
+
+static struct ieee80211_channel rtl8xxxu_channels_2g[] = {
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2412,
+         .hw_value = 1, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2417,
+         .hw_value = 2, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2422,
+         .hw_value = 3, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2427,
+         .hw_value = 4, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2432,
+         .hw_value = 5, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2437,
+         .hw_value = 6, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2442,
+         .hw_value = 7, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2447,
+         .hw_value = 8, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2452,
+         .hw_value = 9, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2457,
+         .hw_value = 10, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2462,
+         .hw_value = 11, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2467,
+         .hw_value = 12, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2472,
+         .hw_value = 13, .max_power = 30 },
+       { .band = IEEE80211_BAND_2GHZ, .center_freq = 2484,
+         .hw_value = 14, .max_power = 30 }
+};
+
+static struct ieee80211_supported_band rtl8xxxu_supported_band = {
+       .channels = rtl8xxxu_channels_2g,
+       .n_channels = ARRAY_SIZE(rtl8xxxu_channels_2g),
+       .bitrates = rtl8xxxu_rates,
+       .n_bitrates = ARRAY_SIZE(rtl8xxxu_rates),
+};
+
+static struct rtl8xxxu_reg8val rtl8723a_mac_init_table[] = {
+       {0x420, 0x80}, {0x423, 0x00}, {0x430, 0x00}, {0x431, 0x00},
+       {0x432, 0x00}, {0x433, 0x01}, {0x434, 0x04}, {0x435, 0x05},
+       {0x436, 0x06}, {0x437, 0x07}, {0x438, 0x00}, {0x439, 0x00},
+       {0x43a, 0x00}, {0x43b, 0x01}, {0x43c, 0x04}, {0x43d, 0x05},
+       {0x43e, 0x06}, {0x43f, 0x07}, {0x440, 0x5d}, {0x441, 0x01},
+       {0x442, 0x00}, {0x444, 0x15}, {0x445, 0xf0}, {0x446, 0x0f},
+       {0x447, 0x00}, {0x458, 0x41}, {0x459, 0xa8}, {0x45a, 0x72},
+       {0x45b, 0xb9}, {0x460, 0x66}, {0x461, 0x66}, {0x462, 0x08},
+       {0x463, 0x03}, {0x4c8, 0xff}, {0x4c9, 0x08}, {0x4cc, 0xff},
+       {0x4cd, 0xff}, {0x4ce, 0x01}, {0x500, 0x26}, {0x501, 0xa2},
+       {0x502, 0x2f}, {0x503, 0x00}, {0x504, 0x28}, {0x505, 0xa3},
+       {0x506, 0x5e}, {0x507, 0x00}, {0x508, 0x2b}, {0x509, 0xa4},
+       {0x50a, 0x5e}, {0x50b, 0x00}, {0x50c, 0x4f}, {0x50d, 0xa4},
+       {0x50e, 0x00}, {0x50f, 0x00}, {0x512, 0x1c}, {0x514, 0x0a},
+       {0x515, 0x10}, {0x516, 0x0a}, {0x517, 0x10}, {0x51a, 0x16},
+       {0x524, 0x0f}, {0x525, 0x4f}, {0x546, 0x40}, {0x547, 0x00},
+       {0x550, 0x10}, {0x551, 0x10}, {0x559, 0x02}, {0x55a, 0x02},
+       {0x55d, 0xff}, {0x605, 0x30}, {0x608, 0x0e}, {0x609, 0x2a},
+       {0x652, 0x20}, {0x63c, 0x0a}, {0x63d, 0x0a}, {0x63e, 0x0e},
+       {0x63f, 0x0e}, {0x66e, 0x05}, {0x700, 0x21}, {0x701, 0x43},
+       {0x702, 0x65}, {0x703, 0x87}, {0x708, 0x21}, {0x709, 0x43},
+       {0x70a, 0x65}, {0x70b, 0x87}, {0xffff, 0xff},
+};
+
+static struct rtl8xxxu_reg32val rtl8723a_phy_1t_init_table[] = {
+       {0x800, 0x80040000}, {0x804, 0x00000003},
+       {0x808, 0x0000fc00}, {0x80c, 0x0000000a},
+       {0x810, 0x10001331}, {0x814, 0x020c3d10},
+       {0x818, 0x02200385}, {0x81c, 0x00000000},
+       {0x820, 0x01000100}, {0x824, 0x00390004},
+       {0x828, 0x00000000}, {0x82c, 0x00000000},
+       {0x830, 0x00000000}, {0x834, 0x00000000},
+       {0x838, 0x00000000}, {0x83c, 0x00000000},
+       {0x840, 0x00010000}, {0x844, 0x00000000},
+       {0x848, 0x00000000}, {0x84c, 0x00000000},
+       {0x850, 0x00000000}, {0x854, 0x00000000},
+       {0x858, 0x569a569a}, {0x85c, 0x001b25a4},
+       {0x860, 0x66f60110}, {0x864, 0x061f0130},
+       {0x868, 0x00000000}, {0x86c, 0x32323200},
+       {0x870, 0x07000760}, {0x874, 0x22004000},
+       {0x878, 0x00000808}, {0x87c, 0x00000000},
+       {0x880, 0xc0083070}, {0x884, 0x000004d5},
+       {0x888, 0x00000000}, {0x88c, 0xccc000c0},
+       {0x890, 0x00000800}, {0x894, 0xfffffffe},
+       {0x898, 0x40302010}, {0x89c, 0x00706050},
+       {0x900, 0x00000000}, {0x904, 0x00000023},
+       {0x908, 0x00000000}, {0x90c, 0x81121111},
+       {0xa00, 0x00d047c8}, {0xa04, 0x80ff000c},
+       {0xa08, 0x8c838300}, {0xa0c, 0x2e68120f},
+       {0xa10, 0x9500bb78}, {0xa14, 0x11144028},
+       {0xa18, 0x00881117}, {0xa1c, 0x89140f00},
+       {0xa20, 0x1a1b0000}, {0xa24, 0x090e1317},
+       {0xa28, 0x00000204}, {0xa2c, 0x00d30000},
+       {0xa70, 0x101fbf00}, {0xa74, 0x00000007},
+       {0xa78, 0x00000900},
+       {0xc00, 0x48071d40}, {0xc04, 0x03a05611},
+       {0xc08, 0x000000e4}, {0xc0c, 0x6c6c6c6c},
+       {0xc10, 0x08800000}, {0xc14, 0x40000100},
+       {0xc18, 0x08800000}, {0xc1c, 0x40000100},
+       {0xc20, 0x00000000}, {0xc24, 0x00000000},
+       {0xc28, 0x00000000}, {0xc2c, 0x00000000},
+       {0xc30, 0x69e9ac44}, {0xc34, 0x469652af},
+       {0xc38, 0x49795994}, {0xc3c, 0x0a97971c},
+       {0xc40, 0x1f7c403f}, {0xc44, 0x000100b7},
+       {0xc48, 0xec020107}, {0xc4c, 0x007f037f},
+       {0xc50, 0x69543420}, {0xc54, 0x43bc0094},
+       {0xc58, 0x69543420}, {0xc5c, 0x433c0094},
+       {0xc60, 0x00000000}, {0xc64, 0x7112848b},
+       {0xc68, 0x47c00bff}, {0xc6c, 0x00000036},
+       {0xc70, 0x2c7f000d}, {0xc74, 0x018610db},
+       {0xc78, 0x0000001f}, {0xc7c, 0x00b91612},
+       {0xc80, 0x40000100}, {0xc84, 0x20f60000},
+       {0xc88, 0x40000100}, {0xc8c, 0x20200000},
+       {0xc90, 0x00121820}, {0xc94, 0x00000000},
+       {0xc98, 0x00121820}, {0xc9c, 0x00007f7f},
+       {0xca0, 0x00000000}, {0xca4, 0x00000080},
+       {0xca8, 0x00000000}, {0xcac, 0x00000000},
+       {0xcb0, 0x00000000}, {0xcb4, 0x00000000},
+       {0xcb8, 0x00000000}, {0xcbc, 0x28000000},
+       {0xcc0, 0x00000000}, {0xcc4, 0x00000000},
+       {0xcc8, 0x00000000}, {0xccc, 0x00000000},
+       {0xcd0, 0x00000000}, {0xcd4, 0x00000000},
+       {0xcd8, 0x64b22427}, {0xcdc, 0x00766932},
+       {0xce0, 0x00222222}, {0xce4, 0x00000000},
+       {0xce8, 0x37644302}, {0xcec, 0x2f97d40c},
+       {0xd00, 0x00080740}, {0xd04, 0x00020401},
+       {0xd08, 0x0000907f}, {0xd0c, 0x20010201},
+       {0xd10, 0xa0633333}, {0xd14, 0x3333bc43},
+       {0xd18, 0x7a8f5b6b}, {0xd2c, 0xcc979975},
+       {0xd30, 0x00000000}, {0xd34, 0x80608000},
+       {0xd38, 0x00000000}, {0xd3c, 0x00027293},
+       {0xd40, 0x00000000}, {0xd44, 0x00000000},
+       {0xd48, 0x00000000}, {0xd4c, 0x00000000},
+       {0xd50, 0x6437140a}, {0xd54, 0x00000000},
+       {0xd58, 0x00000000}, {0xd5c, 0x30032064},
+       {0xd60, 0x4653de68}, {0xd64, 0x04518a3c},
+       {0xd68, 0x00002101}, {0xd6c, 0x2a201c16},
+       {0xd70, 0x1812362e}, {0xd74, 0x322c2220},
+       {0xd78, 0x000e3c24}, {0xe00, 0x2a2a2a2a},
+       {0xe04, 0x2a2a2a2a}, {0xe08, 0x03902a2a},
+       {0xe10, 0x2a2a2a2a}, {0xe14, 0x2a2a2a2a},
+       {0xe18, 0x2a2a2a2a}, {0xe1c, 0x2a2a2a2a},
+       {0xe28, 0x00000000}, {0xe30, 0x1000dc1f},
+       {0xe34, 0x10008c1f}, {0xe38, 0x02140102},
+       {0xe3c, 0x681604c2}, {0xe40, 0x01007c00},
+       {0xe44, 0x01004800}, {0xe48, 0xfb000000},
+       {0xe4c, 0x000028d1}, {0xe50, 0x1000dc1f},
+       {0xe54, 0x10008c1f}, {0xe58, 0x02140102},
+       {0xe5c, 0x28160d05}, {0xe60, 0x00000008},
+       {0xe68, 0x001b25a4}, {0xe6c, 0x631b25a0},
+       {0xe70, 0x631b25a0}, {0xe74, 0x081b25a0},
+       {0xe78, 0x081b25a0}, {0xe7c, 0x081b25a0},
+       {0xe80, 0x081b25a0}, {0xe84, 0x631b25a0},
+       {0xe88, 0x081b25a0}, {0xe8c, 0x631b25a0},
+       {0xed0, 0x631b25a0}, {0xed4, 0x631b25a0},
+       {0xed8, 0x631b25a0}, {0xedc, 0x001b25a0},
+       {0xee0, 0x001b25a0}, {0xeec, 0x6b1b25a0},
+       {0xf14, 0x00000003}, {0xf4c, 0x00000000},
+       {0xf00, 0x00000300},
+       {0xffff, 0xffffffff},
+};
+
+static struct rtl8xxxu_reg32val rtl8192cu_phy_2t_init_table[] = {
+       {0x024, 0x0011800f}, {0x028, 0x00ffdb83},
+       {0x800, 0x80040002}, {0x804, 0x00000003},
+       {0x808, 0x0000fc00}, {0x80c, 0x0000000a},
+       {0x810, 0x10000330}, {0x814, 0x020c3d10},
+       {0x818, 0x02200385}, {0x81c, 0x00000000},
+       {0x820, 0x01000100}, {0x824, 0x00390004},
+       {0x828, 0x01000100}, {0x82c, 0x00390004},
+       {0x830, 0x27272727}, {0x834, 0x27272727},
+       {0x838, 0x27272727}, {0x83c, 0x27272727},
+       {0x840, 0x00010000}, {0x844, 0x00010000},
+       {0x848, 0x27272727}, {0x84c, 0x27272727},
+       {0x850, 0x00000000}, {0x854, 0x00000000},
+       {0x858, 0x569a569a}, {0x85c, 0x0c1b25a4},
+       {0x860, 0x66e60230}, {0x864, 0x061f0130},
+       {0x868, 0x27272727}, {0x86c, 0x2b2b2b27},
+       {0x870, 0x07000700}, {0x874, 0x22184000},
+       {0x878, 0x08080808}, {0x87c, 0x00000000},
+       {0x880, 0xc0083070}, {0x884, 0x000004d5},
+       {0x888, 0x00000000}, {0x88c, 0xcc0000c0},
+       {0x890, 0x00000800}, {0x894, 0xfffffffe},
+       {0x898, 0x40302010}, {0x89c, 0x00706050},
+       {0x900, 0x00000000}, {0x904, 0x00000023},
+       {0x908, 0x00000000}, {0x90c, 0x81121313},
+       {0xa00, 0x00d047c8}, {0xa04, 0x80ff000c},
+       {0xa08, 0x8c838300}, {0xa0c, 0x2e68120f},
+       {0xa10, 0x9500bb78}, {0xa14, 0x11144028},
+       {0xa18, 0x00881117}, {0xa1c, 0x89140f00},
+       {0xa20, 0x1a1b0000}, {0xa24, 0x090e1317},
+       {0xa28, 0x00000204}, {0xa2c, 0x00d30000},
+       {0xa70, 0x101fbf00}, {0xa74, 0x00000007},
+       {0xc00, 0x48071d40}, {0xc04, 0x03a05633},
+       {0xc08, 0x000000e4}, {0xc0c, 0x6c6c6c6c},
+       {0xc10, 0x08800000}, {0xc14, 0x40000100},
+       {0xc18, 0x08800000}, {0xc1c, 0x40000100},
+       {0xc20, 0x00000000}, {0xc24, 0x00000000},
+       {0xc28, 0x00000000}, {0xc2c, 0x00000000},
+       {0xc30, 0x69e9ac44}, {0xc34, 0x469652cf},
+       {0xc38, 0x49795994}, {0xc3c, 0x0a97971c},
+       {0xc40, 0x1f7c403f}, {0xc44, 0x000100b7},
+       {0xc48, 0xec020107}, {0xc4c, 0x007f037f},
+       {0xc50, 0x69543420}, {0xc54, 0x43bc0094},
+       {0xc58, 0x69543420}, {0xc5c, 0x433c0094},
+       {0xc60, 0x00000000}, {0xc64, 0x5116848b},
+       {0xc68, 0x47c00bff}, {0xc6c, 0x00000036},
+       {0xc70, 0x2c7f000d}, {0xc74, 0x2186115b},
+       {0xc78, 0x0000001f}, {0xc7c, 0x00b99612},
+       {0xc80, 0x40000100}, {0xc84, 0x20f60000},
+       {0xc88, 0x40000100}, {0xc8c, 0xa0e40000},
+       {0xc90, 0x00121820}, {0xc94, 0x00000000},
+       {0xc98, 0x00121820}, {0xc9c, 0x00007f7f},
+       {0xca0, 0x00000000}, {0xca4, 0x00000080},
+       {0xca8, 0x00000000}, {0xcac, 0x00000000},
+       {0xcb0, 0x00000000}, {0xcb4, 0x00000000},
+       {0xcb8, 0x00000000}, {0xcbc, 0x28000000},
+       {0xcc0, 0x00000000}, {0xcc4, 0x00000000},
+       {0xcc8, 0x00000000}, {0xccc, 0x00000000},
+       {0xcd0, 0x00000000}, {0xcd4, 0x00000000},
+       {0xcd8, 0x64b22427}, {0xcdc, 0x00766932},
+       {0xce0, 0x00222222}, {0xce4, 0x00000000},
+       {0xce8, 0x37644302}, {0xcec, 0x2f97d40c},
+       {0xd00, 0x00080740}, {0xd04, 0x00020403},
+       {0xd08, 0x0000907f}, {0xd0c, 0x20010201},
+       {0xd10, 0xa0633333}, {0xd14, 0x3333bc43},
+       {0xd18, 0x7a8f5b6b}, {0xd2c, 0xcc979975},
+       {0xd30, 0x00000000}, {0xd34, 0x80608000},
+       {0xd38, 0x00000000}, {0xd3c, 0x00027293},
+       {0xd40, 0x00000000}, {0xd44, 0x00000000},
+       {0xd48, 0x00000000}, {0xd4c, 0x00000000},
+       {0xd50, 0x6437140a}, {0xd54, 0x00000000},
+       {0xd58, 0x00000000}, {0xd5c, 0x30032064},
+       {0xd60, 0x4653de68}, {0xd64, 0x04518a3c},
+       {0xd68, 0x00002101}, {0xd6c, 0x2a201c16},
+       {0xd70, 0x1812362e}, {0xd74, 0x322c2220},
+       {0xd78, 0x000e3c24}, {0xe00, 0x2a2a2a2a},
+       {0xe04, 0x2a2a2a2a}, {0xe08, 0x03902a2a},
+       {0xe10, 0x2a2a2a2a}, {0xe14, 0x2a2a2a2a},
+       {0xe18, 0x2a2a2a2a}, {0xe1c, 0x2a2a2a2a},
+       {0xe28, 0x00000000}, {0xe30, 0x1000dc1f},
+       {0xe34, 0x10008c1f}, {0xe38, 0x02140102},
+       {0xe3c, 0x681604c2}, {0xe40, 0x01007c00},
+       {0xe44, 0x01004800}, {0xe48, 0xfb000000},
+       {0xe4c, 0x000028d1}, {0xe50, 0x1000dc1f},
+       {0xe54, 0x10008c1f}, {0xe58, 0x02140102},
+       {0xe5c, 0x28160d05}, {0xe60, 0x00000010},
+       {0xe68, 0x001b25a4}, {0xe6c, 0x63db25a4},
+       {0xe70, 0x63db25a4}, {0xe74, 0x0c1b25a4},
+       {0xe78, 0x0c1b25a4}, {0xe7c, 0x0c1b25a4},
+       {0xe80, 0x0c1b25a4}, {0xe84, 0x63db25a4},
+       {0xe88, 0x0c1b25a4}, {0xe8c, 0x63db25a4},
+       {0xed0, 0x63db25a4}, {0xed4, 0x63db25a4},
+       {0xed8, 0x63db25a4}, {0xedc, 0x001b25a4},
+       {0xee0, 0x001b25a4}, {0xeec, 0x6fdb25a4},
+       {0xf14, 0x00000003}, {0xf4c, 0x00000000},
+       {0xf00, 0x00000300},
+       {0xffff, 0xffffffff},
+};
+
+static struct rtl8xxxu_reg32val rtl8188ru_phy_1t_highpa_table[] = {
+       {0x024, 0x0011800f}, {0x028, 0x00ffdb83},
+       {0x040, 0x000c0004}, {0x800, 0x80040000},
+       {0x804, 0x00000001}, {0x808, 0x0000fc00},
+       {0x80c, 0x0000000a}, {0x810, 0x10005388},
+       {0x814, 0x020c3d10}, {0x818, 0x02200385},
+       {0x81c, 0x00000000}, {0x820, 0x01000100},
+       {0x824, 0x00390204}, {0x828, 0x00000000},
+       {0x82c, 0x00000000}, {0x830, 0x00000000},
+       {0x834, 0x00000000}, {0x838, 0x00000000},
+       {0x83c, 0x00000000}, {0x840, 0x00010000},
+       {0x844, 0x00000000}, {0x848, 0x00000000},
+       {0x84c, 0x00000000}, {0x850, 0x00000000},
+       {0x854, 0x00000000}, {0x858, 0x569a569a},
+       {0x85c, 0x001b25a4}, {0x860, 0x66e60230},
+       {0x864, 0x061f0130}, {0x868, 0x00000000},
+       {0x86c, 0x20202000}, {0x870, 0x03000300},
+       {0x874, 0x22004000}, {0x878, 0x00000808},
+       {0x87c, 0x00ffc3f1}, {0x880, 0xc0083070},
+       {0x884, 0x000004d5}, {0x888, 0x00000000},
+       {0x88c, 0xccc000c0}, {0x890, 0x00000800},
+       {0x894, 0xfffffffe}, {0x898, 0x40302010},
+       {0x89c, 0x00706050}, {0x900, 0x00000000},
+       {0x904, 0x00000023}, {0x908, 0x00000000},
+       {0x90c, 0x81121111}, {0xa00, 0x00d047c8},
+       {0xa04, 0x80ff000c}, {0xa08, 0x8c838300},
+       {0xa0c, 0x2e68120f}, {0xa10, 0x9500bb78},
+       {0xa14, 0x11144028}, {0xa18, 0x00881117},
+       {0xa1c, 0x89140f00}, {0xa20, 0x15160000},
+       {0xa24, 0x070b0f12}, {0xa28, 0x00000104},
+       {0xa2c, 0x00d30000}, {0xa70, 0x101fbf00},
+       {0xa74, 0x00000007}, {0xc00, 0x48071d40},
+       {0xc04, 0x03a05611}, {0xc08, 0x000000e4},
+       {0xc0c, 0x6c6c6c6c}, {0xc10, 0x08800000},
+       {0xc14, 0x40000100}, {0xc18, 0x08800000},
+       {0xc1c, 0x40000100}, {0xc20, 0x00000000},
+       {0xc24, 0x00000000}, {0xc28, 0x00000000},
+       {0xc2c, 0x00000000}, {0xc30, 0x69e9ac44},
+       {0xc34, 0x469652cf}, {0xc38, 0x49795994},
+       {0xc3c, 0x0a97971c}, {0xc40, 0x1f7c403f},
+       {0xc44, 0x000100b7}, {0xc48, 0xec020107},
+       {0xc4c, 0x007f037f}, {0xc50, 0x6954342e},
+       {0xc54, 0x43bc0094}, {0xc58, 0x6954342f},
+       {0xc5c, 0x433c0094}, {0xc60, 0x00000000},
+       {0xc64, 0x5116848b}, {0xc68, 0x47c00bff},
+       {0xc6c, 0x00000036}, {0xc70, 0x2c46000d},
+       {0xc74, 0x018610db}, {0xc78, 0x0000001f},
+       {0xc7c, 0x00b91612}, {0xc80, 0x24000090},
+       {0xc84, 0x20f60000}, {0xc88, 0x24000090},
+       {0xc8c, 0x20200000}, {0xc90, 0x00121820},
+       {0xc94, 0x00000000}, {0xc98, 0x00121820},
+       {0xc9c, 0x00007f7f}, {0xca0, 0x00000000},
+       {0xca4, 0x00000080}, {0xca8, 0x00000000},
+       {0xcac, 0x00000000}, {0xcb0, 0x00000000},
+       {0xcb4, 0x00000000}, {0xcb8, 0x00000000},
+       {0xcbc, 0x28000000}, {0xcc0, 0x00000000},
+       {0xcc4, 0x00000000}, {0xcc8, 0x00000000},
+       {0xccc, 0x00000000}, {0xcd0, 0x00000000},
+       {0xcd4, 0x00000000}, {0xcd8, 0x64b22427},
+       {0xcdc, 0x00766932}, {0xce0, 0x00222222},
+       {0xce4, 0x00000000}, {0xce8, 0x37644302},
+       {0xcec, 0x2f97d40c}, {0xd00, 0x00080740},
+       {0xd04, 0x00020401}, {0xd08, 0x0000907f},
+       {0xd0c, 0x20010201}, {0xd10, 0xa0633333},
+       {0xd14, 0x3333bc43}, {0xd18, 0x7a8f5b6b},
+       {0xd2c, 0xcc979975}, {0xd30, 0x00000000},
+       {0xd34, 0x80608000}, {0xd38, 0x00000000},
+       {0xd3c, 0x00027293}, {0xd40, 0x00000000},
+       {0xd44, 0x00000000}, {0xd48, 0x00000000},
+       {0xd4c, 0x00000000}, {0xd50, 0x6437140a},
+       {0xd54, 0x00000000}, {0xd58, 0x00000000},
+       {0xd5c, 0x30032064}, {0xd60, 0x4653de68},
+       {0xd64, 0x04518a3c}, {0xd68, 0x00002101},
+       {0xd6c, 0x2a201c16}, {0xd70, 0x1812362e},
+       {0xd74, 0x322c2220}, {0xd78, 0x000e3c24},
+       {0xe00, 0x24242424}, {0xe04, 0x24242424},
+       {0xe08, 0x03902024}, {0xe10, 0x24242424},
+       {0xe14, 0x24242424}, {0xe18, 0x24242424},
+       {0xe1c, 0x24242424}, {0xe28, 0x00000000},
+       {0xe30, 0x1000dc1f}, {0xe34, 0x10008c1f},
+       {0xe38, 0x02140102}, {0xe3c, 0x681604c2},
+       {0xe40, 0x01007c00}, {0xe44, 0x01004800},
+       {0xe48, 0xfb000000}, {0xe4c, 0x000028d1},
+       {0xe50, 0x1000dc1f}, {0xe54, 0x10008c1f},
+       {0xe58, 0x02140102}, {0xe5c, 0x28160d05},
+       {0xe60, 0x00000008}, {0xe68, 0x001b25a4},
+       {0xe6c, 0x631b25a0}, {0xe70, 0x631b25a0},
+       {0xe74, 0x081b25a0}, {0xe78, 0x081b25a0},
+       {0xe7c, 0x081b25a0}, {0xe80, 0x081b25a0},
+       {0xe84, 0x631b25a0}, {0xe88, 0x081b25a0},
+       {0xe8c, 0x631b25a0}, {0xed0, 0x631b25a0},
+       {0xed4, 0x631b25a0}, {0xed8, 0x631b25a0},
+       {0xedc, 0x001b25a0}, {0xee0, 0x001b25a0},
+       {0xeec, 0x6b1b25a0}, {0xee8, 0x31555448},
+       {0xf14, 0x00000003}, {0xf4c, 0x00000000},
+       {0xf00, 0x00000300},
+       {0xffff, 0xffffffff},
+};
+
+static struct rtl8xxxu_reg32val rtl8xxx_agc_standard_table[] = {
+       {0xc78, 0x7b000001}, {0xc78, 0x7b010001},
+       {0xc78, 0x7b020001}, {0xc78, 0x7b030001},
+       {0xc78, 0x7b040001}, {0xc78, 0x7b050001},
+       {0xc78, 0x7a060001}, {0xc78, 0x79070001},
+       {0xc78, 0x78080001}, {0xc78, 0x77090001},
+       {0xc78, 0x760a0001}, {0xc78, 0x750b0001},
+       {0xc78, 0x740c0001}, {0xc78, 0x730d0001},
+       {0xc78, 0x720e0001}, {0xc78, 0x710f0001},
+       {0xc78, 0x70100001}, {0xc78, 0x6f110001},
+       {0xc78, 0x6e120001}, {0xc78, 0x6d130001},
+       {0xc78, 0x6c140001}, {0xc78, 0x6b150001},
+       {0xc78, 0x6a160001}, {0xc78, 0x69170001},
+       {0xc78, 0x68180001}, {0xc78, 0x67190001},
+       {0xc78, 0x661a0001}, {0xc78, 0x651b0001},
+       {0xc78, 0x641c0001}, {0xc78, 0x631d0001},
+       {0xc78, 0x621e0001}, {0xc78, 0x611f0001},
+       {0xc78, 0x60200001}, {0xc78, 0x49210001},
+       {0xc78, 0x48220001}, {0xc78, 0x47230001},
+       {0xc78, 0x46240001}, {0xc78, 0x45250001},
+       {0xc78, 0x44260001}, {0xc78, 0x43270001},
+       {0xc78, 0x42280001}, {0xc78, 0x41290001},
+       {0xc78, 0x402a0001}, {0xc78, 0x262b0001},
+       {0xc78, 0x252c0001}, {0xc78, 0x242d0001},
+       {0xc78, 0x232e0001}, {0xc78, 0x222f0001},
+       {0xc78, 0x21300001}, {0xc78, 0x20310001},
+       {0xc78, 0x06320001}, {0xc78, 0x05330001},
+       {0xc78, 0x04340001}, {0xc78, 0x03350001},
+       {0xc78, 0x02360001}, {0xc78, 0x01370001},
+       {0xc78, 0x00380001}, {0xc78, 0x00390001},
+       {0xc78, 0x003a0001}, {0xc78, 0x003b0001},
+       {0xc78, 0x003c0001}, {0xc78, 0x003d0001},
+       {0xc78, 0x003e0001}, {0xc78, 0x003f0001},
+       {0xc78, 0x7b400001}, {0xc78, 0x7b410001},
+       {0xc78, 0x7b420001}, {0xc78, 0x7b430001},
+       {0xc78, 0x7b440001}, {0xc78, 0x7b450001},
+       {0xc78, 0x7a460001}, {0xc78, 0x79470001},
+       {0xc78, 0x78480001}, {0xc78, 0x77490001},
+       {0xc78, 0x764a0001}, {0xc78, 0x754b0001},
+       {0xc78, 0x744c0001}, {0xc78, 0x734d0001},
+       {0xc78, 0x724e0001}, {0xc78, 0x714f0001},
+       {0xc78, 0x70500001}, {0xc78, 0x6f510001},
+       {0xc78, 0x6e520001}, {0xc78, 0x6d530001},
+       {0xc78, 0x6c540001}, {0xc78, 0x6b550001},
+       {0xc78, 0x6a560001}, {0xc78, 0x69570001},
+       {0xc78, 0x68580001}, {0xc78, 0x67590001},
+       {0xc78, 0x665a0001}, {0xc78, 0x655b0001},
+       {0xc78, 0x645c0001}, {0xc78, 0x635d0001},
+       {0xc78, 0x625e0001}, {0xc78, 0x615f0001},
+       {0xc78, 0x60600001}, {0xc78, 0x49610001},
+       {0xc78, 0x48620001}, {0xc78, 0x47630001},
+       {0xc78, 0x46640001}, {0xc78, 0x45650001},
+       {0xc78, 0x44660001}, {0xc78, 0x43670001},
+       {0xc78, 0x42680001}, {0xc78, 0x41690001},
+       {0xc78, 0x406a0001}, {0xc78, 0x266b0001},
+       {0xc78, 0x256c0001}, {0xc78, 0x246d0001},
+       {0xc78, 0x236e0001}, {0xc78, 0x226f0001},
+       {0xc78, 0x21700001}, {0xc78, 0x20710001},
+       {0xc78, 0x06720001}, {0xc78, 0x05730001},
+       {0xc78, 0x04740001}, {0xc78, 0x03750001},
+       {0xc78, 0x02760001}, {0xc78, 0x01770001},
+       {0xc78, 0x00780001}, {0xc78, 0x00790001},
+       {0xc78, 0x007a0001}, {0xc78, 0x007b0001},
+       {0xc78, 0x007c0001}, {0xc78, 0x007d0001},
+       {0xc78, 0x007e0001}, {0xc78, 0x007f0001},
+       {0xc78, 0x3800001e}, {0xc78, 0x3801001e},
+       {0xc78, 0x3802001e}, {0xc78, 0x3803001e},
+       {0xc78, 0x3804001e}, {0xc78, 0x3805001e},
+       {0xc78, 0x3806001e}, {0xc78, 0x3807001e},
+       {0xc78, 0x3808001e}, {0xc78, 0x3c09001e},
+       {0xc78, 0x3e0a001e}, {0xc78, 0x400b001e},
+       {0xc78, 0x440c001e}, {0xc78, 0x480d001e},
+       {0xc78, 0x4c0e001e}, {0xc78, 0x500f001e},
+       {0xc78, 0x5210001e}, {0xc78, 0x5611001e},
+       {0xc78, 0x5a12001e}, {0xc78, 0x5e13001e},
+       {0xc78, 0x6014001e}, {0xc78, 0x6015001e},
+       {0xc78, 0x6016001e}, {0xc78, 0x6217001e},
+       {0xc78, 0x6218001e}, {0xc78, 0x6219001e},
+       {0xc78, 0x621a001e}, {0xc78, 0x621b001e},
+       {0xc78, 0x621c001e}, {0xc78, 0x621d001e},
+       {0xc78, 0x621e001e}, {0xc78, 0x621f001e},
+       {0xffff, 0xffffffff}
+};
+
+static struct rtl8xxxu_reg32val rtl8xxx_agc_highpa_table[] = {
+       {0xc78, 0x7b000001}, {0xc78, 0x7b010001},
+       {0xc78, 0x7b020001}, {0xc78, 0x7b030001},
+       {0xc78, 0x7b040001}, {0xc78, 0x7b050001},
+       {0xc78, 0x7b060001}, {0xc78, 0x7b070001},
+       {0xc78, 0x7b080001}, {0xc78, 0x7a090001},
+       {0xc78, 0x790a0001}, {0xc78, 0x780b0001},
+       {0xc78, 0x770c0001}, {0xc78, 0x760d0001},
+       {0xc78, 0x750e0001}, {0xc78, 0x740f0001},
+       {0xc78, 0x73100001}, {0xc78, 0x72110001},
+       {0xc78, 0x71120001}, {0xc78, 0x70130001},
+       {0xc78, 0x6f140001}, {0xc78, 0x6e150001},
+       {0xc78, 0x6d160001}, {0xc78, 0x6c170001},
+       {0xc78, 0x6b180001}, {0xc78, 0x6a190001},
+       {0xc78, 0x691a0001}, {0xc78, 0x681b0001},
+       {0xc78, 0x671c0001}, {0xc78, 0x661d0001},
+       {0xc78, 0x651e0001}, {0xc78, 0x641f0001},
+       {0xc78, 0x63200001}, {0xc78, 0x62210001},
+       {0xc78, 0x61220001}, {0xc78, 0x60230001},
+       {0xc78, 0x46240001}, {0xc78, 0x45250001},
+       {0xc78, 0x44260001}, {0xc78, 0x43270001},
+       {0xc78, 0x42280001}, {0xc78, 0x41290001},
+       {0xc78, 0x402a0001}, {0xc78, 0x262b0001},
+       {0xc78, 0x252c0001}, {0xc78, 0x242d0001},
+       {0xc78, 0x232e0001}, {0xc78, 0x222f0001},
+       {0xc78, 0x21300001}, {0xc78, 0x20310001},
+       {0xc78, 0x06320001}, {0xc78, 0x05330001},
+       {0xc78, 0x04340001}, {0xc78, 0x03350001},
+       {0xc78, 0x02360001}, {0xc78, 0x01370001},
+       {0xc78, 0x00380001}, {0xc78, 0x00390001},
+       {0xc78, 0x003a0001}, {0xc78, 0x003b0001},
+       {0xc78, 0x003c0001}, {0xc78, 0x003d0001},
+       {0xc78, 0x003e0001}, {0xc78, 0x003f0001},
+       {0xc78, 0x7b400001}, {0xc78, 0x7b410001},
+       {0xc78, 0x7b420001}, {0xc78, 0x7b430001},
+       {0xc78, 0x7b440001}, {0xc78, 0x7b450001},
+       {0xc78, 0x7b460001}, {0xc78, 0x7b470001},
+       {0xc78, 0x7b480001}, {0xc78, 0x7a490001},
+       {0xc78, 0x794a0001}, {0xc78, 0x784b0001},
+       {0xc78, 0x774c0001}, {0xc78, 0x764d0001},
+       {0xc78, 0x754e0001}, {0xc78, 0x744f0001},
+       {0xc78, 0x73500001}, {0xc78, 0x72510001},
+       {0xc78, 0x71520001}, {0xc78, 0x70530001},
+       {0xc78, 0x6f540001}, {0xc78, 0x6e550001},
+       {0xc78, 0x6d560001}, {0xc78, 0x6c570001},
+       {0xc78, 0x6b580001}, {0xc78, 0x6a590001},
+       {0xc78, 0x695a0001}, {0xc78, 0x685b0001},
+       {0xc78, 0x675c0001}, {0xc78, 0x665d0001},
+       {0xc78, 0x655e0001}, {0xc78, 0x645f0001},
+       {0xc78, 0x63600001}, {0xc78, 0x62610001},
+       {0xc78, 0x61620001}, {0xc78, 0x60630001},
+       {0xc78, 0x46640001}, {0xc78, 0x45650001},
+       {0xc78, 0x44660001}, {0xc78, 0x43670001},
+       {0xc78, 0x42680001}, {0xc78, 0x41690001},
+       {0xc78, 0x406a0001}, {0xc78, 0x266b0001},
+       {0xc78, 0x256c0001}, {0xc78, 0x246d0001},
+       {0xc78, 0x236e0001}, {0xc78, 0x226f0001},
+       {0xc78, 0x21700001}, {0xc78, 0x20710001},
+       {0xc78, 0x06720001}, {0xc78, 0x05730001},
+       {0xc78, 0x04740001}, {0xc78, 0x03750001},
+       {0xc78, 0x02760001}, {0xc78, 0x01770001},
+       {0xc78, 0x00780001}, {0xc78, 0x00790001},
+       {0xc78, 0x007a0001}, {0xc78, 0x007b0001},
+       {0xc78, 0x007c0001}, {0xc78, 0x007d0001},
+       {0xc78, 0x007e0001}, {0xc78, 0x007f0001},
+       {0xc78, 0x3800001e}, {0xc78, 0x3801001e},
+       {0xc78, 0x3802001e}, {0xc78, 0x3803001e},
+       {0xc78, 0x3804001e}, {0xc78, 0x3805001e},
+       {0xc78, 0x3806001e}, {0xc78, 0x3807001e},
+       {0xc78, 0x3808001e}, {0xc78, 0x3c09001e},
+       {0xc78, 0x3e0a001e}, {0xc78, 0x400b001e},
+       {0xc78, 0x440c001e}, {0xc78, 0x480d001e},
+       {0xc78, 0x4c0e001e}, {0xc78, 0x500f001e},
+       {0xc78, 0x5210001e}, {0xc78, 0x5611001e},
+       {0xc78, 0x5a12001e}, {0xc78, 0x5e13001e},
+       {0xc78, 0x6014001e}, {0xc78, 0x6015001e},
+       {0xc78, 0x6016001e}, {0xc78, 0x6217001e},
+       {0xc78, 0x6218001e}, {0xc78, 0x6219001e},
+       {0xc78, 0x621a001e}, {0xc78, 0x621b001e},
+       {0xc78, 0x621c001e}, {0xc78, 0x621d001e},
+       {0xc78, 0x621e001e}, {0xc78, 0x621f001e},
+       {0xffff, 0xffffffff}
+};
+
+static struct rtl8xxxu_rfregval rtl8723au_radioa_1t_init_table[] = {
+       {0x00, 0x00030159}, {0x01, 0x00031284},
+       {0x02, 0x00098000}, {0x03, 0x00039c63},
+       {0x04, 0x000210e7}, {0x09, 0x0002044f},
+       {0x0a, 0x0001a3f1}, {0x0b, 0x00014787},
+       {0x0c, 0x000896fe}, {0x0d, 0x0000e02c},
+       {0x0e, 0x00039ce7}, {0x0f, 0x00000451},
+       {0x19, 0x00000000}, {0x1a, 0x00030355},
+       {0x1b, 0x00060a00}, {0x1c, 0x000fc378},
+       {0x1d, 0x000a1250}, {0x1e, 0x0000024f},
+       {0x1f, 0x00000000}, {0x20, 0x0000b614},
+       {0x21, 0x0006c000}, {0x22, 0x00000000},
+       {0x23, 0x00001558}, {0x24, 0x00000060},
+       {0x25, 0x00000483}, {0x26, 0x0004f000},
+       {0x27, 0x000ec7d9}, {0x28, 0x00057730},
+       {0x29, 0x00004783}, {0x2a, 0x00000001},
+       {0x2b, 0x00021334}, {0x2a, 0x00000000},
+       {0x2b, 0x00000054}, {0x2a, 0x00000001},
+       {0x2b, 0x00000808}, {0x2b, 0x00053333},
+       {0x2c, 0x0000000c}, {0x2a, 0x00000002},
+       {0x2b, 0x00000808}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000003},
+       {0x2b, 0x00000808}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000004},
+       {0x2b, 0x00000808}, {0x2b, 0x0006b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000005},
+       {0x2b, 0x00000808}, {0x2b, 0x00073333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000006},
+       {0x2b, 0x00000709}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000007},
+       {0x2b, 0x00000709}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000008},
+       {0x2b, 0x0000060a}, {0x2b, 0x0004b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000009},
+       {0x2b, 0x0000060a}, {0x2b, 0x00053333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000a},
+       {0x2b, 0x0000060a}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000b},
+       {0x2b, 0x0000060a}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000c},
+       {0x2b, 0x0000060a}, {0x2b, 0x0006b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000d},
+       {0x2b, 0x0000060a}, {0x2b, 0x00073333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000e},
+       {0x2b, 0x0000050b}, {0x2b, 0x00066666},
+       {0x2c, 0x0000001a}, {0x2a, 0x000e0000},
+       {0x10, 0x0004000f}, {0x11, 0x000e31fc},
+       {0x10, 0x0006000f}, {0x11, 0x000ff9f8},
+       {0x10, 0x0002000f}, {0x11, 0x000203f9},
+       {0x10, 0x0003000f}, {0x11, 0x000ff500},
+       {0x10, 0x00000000}, {0x11, 0x00000000},
+       {0x10, 0x0008000f}, {0x11, 0x0003f100},
+       {0x10, 0x0009000f}, {0x11, 0x00023100},
+       {0x12, 0x00032000}, {0x12, 0x00071000},
+       {0x12, 0x000b0000}, {0x12, 0x000fc000},
+       {0x13, 0x000287b3}, {0x13, 0x000244b7},
+       {0x13, 0x000204ab}, {0x13, 0x0001c49f},
+       {0x13, 0x00018493}, {0x13, 0x0001429b},
+       {0x13, 0x00010299}, {0x13, 0x0000c29c},
+       {0x13, 0x000081a0}, {0x13, 0x000040ac},
+       {0x13, 0x00000020}, {0x14, 0x0001944c},
+       {0x14, 0x00059444}, {0x14, 0x0009944c},
+       {0x14, 0x000d9444}, {0x15, 0x0000f474},
+       {0x15, 0x0004f477}, {0x15, 0x0008f455},
+       {0x15, 0x000cf455}, {0x16, 0x00000339},
+       {0x16, 0x00040339}, {0x16, 0x00080339},
+       {0x16, 0x000c0366}, {0x00, 0x00010159},
+       {0x18, 0x0000f401}, {0xfe, 0x00000000},
+       {0xfe, 0x00000000}, {0x1f, 0x00000003},
+       {0xfe, 0x00000000}, {0xfe, 0x00000000},
+       {0x1e, 0x00000247}, {0x1f, 0x00000000},
+       {0x00, 0x00030159},
+       {0xff, 0xffffffff}
+};
+
+static struct rtl8xxxu_rfregval rtl8192cu_radioa_2t_init_table[] = {
+       {0x00, 0x00030159}, {0x01, 0x00031284},
+       {0x02, 0x00098000}, {0x03, 0x00018c63},
+       {0x04, 0x000210e7}, {0x09, 0x0002044f},
+       {0x0a, 0x0001adb1}, {0x0b, 0x00054867},
+       {0x0c, 0x0008992e}, {0x0d, 0x0000e52c},
+       {0x0e, 0x00039ce7}, {0x0f, 0x00000451},
+       {0x19, 0x00000000}, {0x1a, 0x00010255},
+       {0x1b, 0x00060a00}, {0x1c, 0x000fc378},
+       {0x1d, 0x000a1250}, {0x1e, 0x0004445f},
+       {0x1f, 0x00080001}, {0x20, 0x0000b614},
+       {0x21, 0x0006c000}, {0x22, 0x00000000},
+       {0x23, 0x00001558}, {0x24, 0x00000060},
+       {0x25, 0x00000483}, {0x26, 0x0004f000},
+       {0x27, 0x000ec7d9}, {0x28, 0x000577c0},
+       {0x29, 0x00004783}, {0x2a, 0x00000001},
+       {0x2b, 0x00021334}, {0x2a, 0x00000000},
+       {0x2b, 0x00000054}, {0x2a, 0x00000001},
+       {0x2b, 0x00000808}, {0x2b, 0x00053333},
+       {0x2c, 0x0000000c}, {0x2a, 0x00000002},
+       {0x2b, 0x00000808}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000003},
+       {0x2b, 0x00000808}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000004},
+       {0x2b, 0x00000808}, {0x2b, 0x0006b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000005},
+       {0x2b, 0x00000808}, {0x2b, 0x00073333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000006},
+       {0x2b, 0x00000709}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000007},
+       {0x2b, 0x00000709}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000008},
+       {0x2b, 0x0000060a}, {0x2b, 0x0004b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000009},
+       {0x2b, 0x0000060a}, {0x2b, 0x00053333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000a},
+       {0x2b, 0x0000060a}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000b},
+       {0x2b, 0x0000060a}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000c},
+       {0x2b, 0x0000060a}, {0x2b, 0x0006b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000d},
+       {0x2b, 0x0000060a}, {0x2b, 0x00073333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000e},
+       {0x2b, 0x0000050b}, {0x2b, 0x00066666},
+       {0x2c, 0x0000001a}, {0x2a, 0x000e0000},
+       {0x10, 0x0004000f}, {0x11, 0x000e31fc},
+       {0x10, 0x0006000f}, {0x11, 0x000ff9f8},
+       {0x10, 0x0002000f}, {0x11, 0x000203f9},
+       {0x10, 0x0003000f}, {0x11, 0x000ff500},
+       {0x10, 0x00000000}, {0x11, 0x00000000},
+       {0x10, 0x0008000f}, {0x11, 0x0003f100},
+       {0x10, 0x0009000f}, {0x11, 0x00023100},
+       {0x12, 0x00032000}, {0x12, 0x00071000},
+       {0x12, 0x000b0000}, {0x12, 0x000fc000},
+       {0x13, 0x000287b3}, {0x13, 0x000244b7},
+       {0x13, 0x000204ab}, {0x13, 0x0001c49f},
+       {0x13, 0x00018493}, {0x13, 0x0001429b},
+       {0x13, 0x00010299}, {0x13, 0x0000c29c},
+       {0x13, 0x000081a0}, {0x13, 0x000040ac},
+       {0x13, 0x00000020}, {0x14, 0x0001944c},
+       {0x14, 0x00059444}, {0x14, 0x0009944c},
+       {0x14, 0x000d9444}, {0x15, 0x0000f424},
+       {0x15, 0x0004f424}, {0x15, 0x0008f424},
+       {0x15, 0x000cf424}, {0x16, 0x000e0330},
+       {0x16, 0x000a0330}, {0x16, 0x00060330},
+       {0x16, 0x00020330}, {0x00, 0x00010159},
+       {0x18, 0x0000f401}, {0xfe, 0x00000000},
+       {0xfe, 0x00000000}, {0x1f, 0x00080003},
+       {0xfe, 0x00000000}, {0xfe, 0x00000000},
+       {0x1e, 0x00044457}, {0x1f, 0x00080000},
+       {0x00, 0x00030159},
+       {0xff, 0xffffffff}
+};
+
+static struct rtl8xxxu_rfregval rtl8192cu_radiob_2t_init_table[] = {
+       {0x00, 0x00030159}, {0x01, 0x00031284},
+       {0x02, 0x00098000}, {0x03, 0x00018c63},
+       {0x04, 0x000210e7}, {0x09, 0x0002044f},
+       {0x0a, 0x0001adb1}, {0x0b, 0x00054867},
+       {0x0c, 0x0008992e}, {0x0d, 0x0000e52c},
+       {0x0e, 0x00039ce7}, {0x0f, 0x00000451},
+       {0x12, 0x00032000}, {0x12, 0x00071000},
+       {0x12, 0x000b0000}, {0x12, 0x000fc000},
+       {0x13, 0x000287af}, {0x13, 0x000244b7},
+       {0x13, 0x000204ab}, {0x13, 0x0001c49f},
+       {0x13, 0x00018493}, {0x13, 0x00014297},
+       {0x13, 0x00010295}, {0x13, 0x0000c298},
+       {0x13, 0x0000819c}, {0x13, 0x000040a8},
+       {0x13, 0x0000001c}, {0x14, 0x0001944c},
+       {0x14, 0x00059444}, {0x14, 0x0009944c},
+       {0x14, 0x000d9444}, {0x15, 0x0000f424},
+       {0x15, 0x0004f424}, {0x15, 0x0008f424},
+       {0x15, 0x000cf424}, {0x16, 0x000e0330},
+       {0x16, 0x000a0330}, {0x16, 0x00060330},
+       {0x16, 0x00020330},
+       {0xff, 0xffffffff}
+};
+
+static struct rtl8xxxu_rfregval rtl8192cu_radioa_1t_init_table[] = {
+       {0x00, 0x00030159}, {0x01, 0x00031284},
+       {0x02, 0x00098000}, {0x03, 0x00018c63},
+       {0x04, 0x000210e7}, {0x09, 0x0002044f},
+       {0x0a, 0x0001adb1}, {0x0b, 0x00054867},
+       {0x0c, 0x0008992e}, {0x0d, 0x0000e52c},
+       {0x0e, 0x00039ce7}, {0x0f, 0x00000451},
+       {0x19, 0x00000000}, {0x1a, 0x00010255},
+       {0x1b, 0x00060a00}, {0x1c, 0x000fc378},
+       {0x1d, 0x000a1250}, {0x1e, 0x0004445f},
+       {0x1f, 0x00080001}, {0x20, 0x0000b614},
+       {0x21, 0x0006c000}, {0x22, 0x00000000},
+       {0x23, 0x00001558}, {0x24, 0x00000060},
+       {0x25, 0x00000483}, {0x26, 0x0004f000},
+       {0x27, 0x000ec7d9}, {0x28, 0x000577c0},
+       {0x29, 0x00004783}, {0x2a, 0x00000001},
+       {0x2b, 0x00021334}, {0x2a, 0x00000000},
+       {0x2b, 0x00000054}, {0x2a, 0x00000001},
+       {0x2b, 0x00000808}, {0x2b, 0x00053333},
+       {0x2c, 0x0000000c}, {0x2a, 0x00000002},
+       {0x2b, 0x00000808}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000003},
+       {0x2b, 0x00000808}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000004},
+       {0x2b, 0x00000808}, {0x2b, 0x0006b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000005},
+       {0x2b, 0x00000808}, {0x2b, 0x00073333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000006},
+       {0x2b, 0x00000709}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000007},
+       {0x2b, 0x00000709}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000008},
+       {0x2b, 0x0000060a}, {0x2b, 0x0004b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000009},
+       {0x2b, 0x0000060a}, {0x2b, 0x00053333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000a},
+       {0x2b, 0x0000060a}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000b},
+       {0x2b, 0x0000060a}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000c},
+       {0x2b, 0x0000060a}, {0x2b, 0x0006b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000d},
+       {0x2b, 0x0000060a}, {0x2b, 0x00073333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000e},
+       {0x2b, 0x0000050b}, {0x2b, 0x00066666},
+       {0x2c, 0x0000001a}, {0x2a, 0x000e0000},
+       {0x10, 0x0004000f}, {0x11, 0x000e31fc},
+       {0x10, 0x0006000f}, {0x11, 0x000ff9f8},
+       {0x10, 0x0002000f}, {0x11, 0x000203f9},
+       {0x10, 0x0003000f}, {0x11, 0x000ff500},
+       {0x10, 0x00000000}, {0x11, 0x00000000},
+       {0x10, 0x0008000f}, {0x11, 0x0003f100},
+       {0x10, 0x0009000f}, {0x11, 0x00023100},
+       {0x12, 0x00032000}, {0x12, 0x00071000},
+       {0x12, 0x000b0000}, {0x12, 0x000fc000},
+       {0x13, 0x000287b3}, {0x13, 0x000244b7},
+       {0x13, 0x000204ab}, {0x13, 0x0001c49f},
+       {0x13, 0x00018493}, {0x13, 0x0001429b},
+       {0x13, 0x00010299}, {0x13, 0x0000c29c},
+       {0x13, 0x000081a0}, {0x13, 0x000040ac},
+       {0x13, 0x00000020}, {0x14, 0x0001944c},
+       {0x14, 0x00059444}, {0x14, 0x0009944c},
+       {0x14, 0x000d9444}, {0x15, 0x0000f405},
+       {0x15, 0x0004f405}, {0x15, 0x0008f405},
+       {0x15, 0x000cf405}, {0x16, 0x000e0330},
+       {0x16, 0x000a0330}, {0x16, 0x00060330},
+       {0x16, 0x00020330}, {0x00, 0x00010159},
+       {0x18, 0x0000f401}, {0xfe, 0x00000000},
+       {0xfe, 0x00000000}, {0x1f, 0x00080003},
+       {0xfe, 0x00000000}, {0xfe, 0x00000000},
+       {0x1e, 0x00044457}, {0x1f, 0x00080000},
+       {0x00, 0x00030159},
+       {0xff, 0xffffffff}
+};
+
+static struct rtl8xxxu_rfregval rtl8188ru_radioa_1t_highpa_table[] = {
+       {0x00, 0x00030159}, {0x01, 0x00031284},
+       {0x02, 0x00098000}, {0x03, 0x00018c63},
+       {0x04, 0x000210e7}, {0x09, 0x0002044f},
+       {0x0a, 0x0001adb0}, {0x0b, 0x00054867},
+       {0x0c, 0x0008992e}, {0x0d, 0x0000e529},
+       {0x0e, 0x00039ce7}, {0x0f, 0x00000451},
+       {0x19, 0x00000000}, {0x1a, 0x00000255},
+       {0x1b, 0x00060a00}, {0x1c, 0x000fc378},
+       {0x1d, 0x000a1250}, {0x1e, 0x0004445f},
+       {0x1f, 0x00080001}, {0x20, 0x0000b614},
+       {0x21, 0x0006c000}, {0x22, 0x0000083c},
+       {0x23, 0x00001558}, {0x24, 0x00000060},
+       {0x25, 0x00000483}, {0x26, 0x0004f000},
+       {0x27, 0x000ec7d9}, {0x28, 0x000977c0},
+       {0x29, 0x00004783}, {0x2a, 0x00000001},
+       {0x2b, 0x00021334}, {0x2a, 0x00000000},
+       {0x2b, 0x00000054}, {0x2a, 0x00000001},
+       {0x2b, 0x00000808}, {0x2b, 0x00053333},
+       {0x2c, 0x0000000c}, {0x2a, 0x00000002},
+       {0x2b, 0x00000808}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000003},
+       {0x2b, 0x00000808}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000004},
+       {0x2b, 0x00000808}, {0x2b, 0x0006b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000005},
+       {0x2b, 0x00000808}, {0x2b, 0x00073333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000006},
+       {0x2b, 0x00000709}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000007},
+       {0x2b, 0x00000709}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000008},
+       {0x2b, 0x0000060a}, {0x2b, 0x0004b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x00000009},
+       {0x2b, 0x0000060a}, {0x2b, 0x00053333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000a},
+       {0x2b, 0x0000060a}, {0x2b, 0x0005b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000b},
+       {0x2b, 0x0000060a}, {0x2b, 0x00063333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000c},
+       {0x2b, 0x0000060a}, {0x2b, 0x0006b333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000d},
+       {0x2b, 0x0000060a}, {0x2b, 0x00073333},
+       {0x2c, 0x0000000d}, {0x2a, 0x0000000e},
+       {0x2b, 0x0000050b}, {0x2b, 0x00066666},
+       {0x2c, 0x0000001a}, {0x2a, 0x000e0000},
+       {0x10, 0x0004000f}, {0x11, 0x000e31fc},
+       {0x10, 0x0006000f}, {0x11, 0x000ff9f8},
+       {0x10, 0x0002000f}, {0x11, 0x000203f9},
+       {0x10, 0x0003000f}, {0x11, 0x000ff500},
+       {0x10, 0x00000000}, {0x11, 0x00000000},
+       {0x10, 0x0008000f}, {0x11, 0x0003f100},
+       {0x10, 0x0009000f}, {0x11, 0x00023100},
+       {0x12, 0x000d8000}, {0x12, 0x00090000},
+       {0x12, 0x00051000}, {0x12, 0x00012000},
+       {0x13, 0x00028fb4}, {0x13, 0x00024fa8},
+       {0x13, 0x000207a4}, {0x13, 0x0001c3b0},
+       {0x13, 0x000183a4}, {0x13, 0x00014398},
+       {0x13, 0x000101a4}, {0x13, 0x0000c198},
+       {0x13, 0x000080a4}, {0x13, 0x00004098},
+       {0x13, 0x00000000}, {0x14, 0x0001944c},
+       {0x14, 0x00059444}, {0x14, 0x0009944c},
+       {0x14, 0x000d9444}, {0x15, 0x0000f405},
+       {0x15, 0x0004f405}, {0x15, 0x0008f405},
+       {0x15, 0x000cf405}, {0x16, 0x000e0330},
+       {0x16, 0x000a0330}, {0x16, 0x00060330},
+       {0x16, 0x00020330}, {0x00, 0x00010159},
+       {0x18, 0x0000f401}, {0xfe, 0x00000000},
+       {0xfe, 0x00000000}, {0x1f, 0x00080003},
+       {0xfe, 0x00000000}, {0xfe, 0x00000000},
+       {0x1e, 0x00044457}, {0x1f, 0x00080000},
+       {0x00, 0x00030159},
+       {0xff, 0xffffffff}
+};
+
+static struct rtl8xxxu_rfregs rtl8xxxu_rfregs[] = {
+       {       /* RF_A */
+               .hssiparm1 = REG_FPGA0_XA_HSSI_PARM1,
+               .hssiparm2 = REG_FPGA0_XA_HSSI_PARM2,
+               .lssiparm = REG_FPGA0_XA_LSSI_PARM,
+               .hspiread = REG_HSPI_XA_READBACK,
+               .lssiread = REG_FPGA0_XA_LSSI_READBACK,
+               .rf_sw_ctrl = REG_FPGA0_XA_RF_SW_CTRL,
+       },
+       {       /* RF_B */
+               .hssiparm1 = REG_FPGA0_XB_HSSI_PARM1,
+               .hssiparm2 = REG_FPGA0_XB_HSSI_PARM2,
+               .lssiparm = REG_FPGA0_XB_LSSI_PARM,
+               .hspiread = REG_HSPI_XB_READBACK,
+               .lssiread = REG_FPGA0_XB_LSSI_READBACK,
+               .rf_sw_ctrl = REG_FPGA0_XB_RF_SW_CTRL,
+       },
+};
+
+static const u32 rtl8723au_iqk_phy_iq_bb_reg[RTL8XXXU_BB_REGS] = {
+       REG_OFDM0_XA_RX_IQ_IMBALANCE,
+       REG_OFDM0_XB_RX_IQ_IMBALANCE,
+       REG_OFDM0_ENERGY_CCA_THRES,
+       REG_OFDM0_AGCR_SSI_TABLE,
+       REG_OFDM0_XA_TX_IQ_IMBALANCE,
+       REG_OFDM0_XB_TX_IQ_IMBALANCE,
+       REG_OFDM0_XC_TX_AFE,
+       REG_OFDM0_XD_TX_AFE,
+       REG_OFDM0_RX_IQ_EXT_ANTA
+};
+
+static u8 rtl8xxxu_read8(struct rtl8xxxu_priv *priv, u16 addr)
+{
+       struct usb_device *udev = priv->udev;
+       int len;
+       u8 data;
+
+       mutex_lock(&priv->usb_buf_mutex);
+       len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+                             REALTEK_USB_CMD_REQ, REALTEK_USB_READ,
+                             addr, 0, &priv->usb_buf.val8, sizeof(u8),
+                             RTW_USB_CONTROL_MSG_TIMEOUT);
+       data = priv->usb_buf.val8;
+       mutex_unlock(&priv->usb_buf_mutex);
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_READ)
+               dev_info(&udev->dev, "%s(%04x)   = 0x%02x, len %i\n",
+                        __func__, addr, data, len);
+       return data;
+}
+
+static u16 rtl8xxxu_read16(struct rtl8xxxu_priv *priv, u16 addr)
+{
+       struct usb_device *udev = priv->udev;
+       int len;
+       u16 data;
+
+       mutex_lock(&priv->usb_buf_mutex);
+       len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+                             REALTEK_USB_CMD_REQ, REALTEK_USB_READ,
+                             addr, 0, &priv->usb_buf.val16, sizeof(u16),
+                             RTW_USB_CONTROL_MSG_TIMEOUT);
+       data = le16_to_cpu(priv->usb_buf.val16);
+       mutex_unlock(&priv->usb_buf_mutex);
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_READ)
+               dev_info(&udev->dev, "%s(%04x)  = 0x%04x, len %i\n",
+                        __func__, addr, data, len);
+       return data;
+}
+
+static u32 rtl8xxxu_read32(struct rtl8xxxu_priv *priv, u16 addr)
+{
+       struct usb_device *udev = priv->udev;
+       int len;
+       u32 data;
+
+       mutex_lock(&priv->usb_buf_mutex);
+       len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+                             REALTEK_USB_CMD_REQ, REALTEK_USB_READ,
+                             addr, 0, &priv->usb_buf.val32, sizeof(u32),
+                             RTW_USB_CONTROL_MSG_TIMEOUT);
+       data = le32_to_cpu(priv->usb_buf.val32);
+       mutex_unlock(&priv->usb_buf_mutex);
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_READ)
+               dev_info(&udev->dev, "%s(%04x)  = 0x%08x, len %i\n",
+                        __func__, addr, data, len);
+       return data;
+}
+
+static int rtl8xxxu_write8(struct rtl8xxxu_priv *priv, u16 addr, u8 val)
+{
+       struct usb_device *udev = priv->udev;
+       int ret;
+
+       mutex_lock(&priv->usb_buf_mutex);
+       priv->usb_buf.val8 = val;
+       ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                             REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
+                             addr, 0, &priv->usb_buf.val8, sizeof(u8),
+                             RTW_USB_CONTROL_MSG_TIMEOUT);
+
+       mutex_unlock(&priv->usb_buf_mutex);
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_WRITE)
+               dev_info(&udev->dev, "%s(%04x) = 0x%02x\n",
+                        __func__, addr, val);
+       return ret;
+}
+
+static int rtl8xxxu_write16(struct rtl8xxxu_priv *priv, u16 addr, u16 val)
+{
+       struct usb_device *udev = priv->udev;
+       int ret;
+
+       mutex_lock(&priv->usb_buf_mutex);
+       priv->usb_buf.val16 = cpu_to_le16(val);
+       ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                             REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
+                             addr, 0, &priv->usb_buf.val16, sizeof(u16),
+                             RTW_USB_CONTROL_MSG_TIMEOUT);
+       mutex_unlock(&priv->usb_buf_mutex);
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_WRITE)
+               dev_info(&udev->dev, "%s(%04x) = 0x%04x\n",
+                        __func__, addr, val);
+       return ret;
+}
+
+static int rtl8xxxu_write32(struct rtl8xxxu_priv *priv, u16 addr, u32 val)
+{
+       struct usb_device *udev = priv->udev;
+       int ret;
+
+       mutex_lock(&priv->usb_buf_mutex);
+       priv->usb_buf.val32 = cpu_to_le32(val);
+       ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                             REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
+                             addr, 0, &priv->usb_buf.val32, sizeof(u32),
+                             RTW_USB_CONTROL_MSG_TIMEOUT);
+       mutex_unlock(&priv->usb_buf_mutex);
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_WRITE)
+               dev_info(&udev->dev, "%s(%04x) = 0x%08x\n",
+                        __func__, addr, val);
+       return ret;
+}
+
+static int
+rtl8xxxu_writeN(struct rtl8xxxu_priv *priv, u16 addr, u8 *buf, u16 len)
+{
+       struct usb_device *udev = priv->udev;
+       int blocksize = priv->fops->writeN_block_size;
+       int ret, i, count, remainder;
+
+       count = len / blocksize;
+       remainder = len % blocksize;
+
+       for (i = 0; i < count; i++) {
+               ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                     REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
+                                     addr, 0, buf, blocksize,
+                                     RTW_USB_CONTROL_MSG_TIMEOUT);
+               if (ret != blocksize)
+                       goto write_error;
+
+               addr += blocksize;
+               buf += blocksize;
+       }
+
+       if (remainder) {
+               ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                     REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
+                                     addr, 0, buf, remainder,
+                                     RTW_USB_CONTROL_MSG_TIMEOUT);
+               if (ret != remainder)
+                       goto write_error;
+       }
+
+       return len;
+
+write_error:
+       dev_info(&udev->dev,
+                "%s: Failed to write block at addr: %04x size: %04x\n",
+                __func__, addr, blocksize);
+       return -EAGAIN;
+}
+
+static u32 rtl8xxxu_read_rfreg(struct rtl8xxxu_priv *priv,
+                              enum rtl8xxxu_rfpath path, u8 reg)
+{
+       u32 hssia, val32, retval;
+
+       hssia = rtl8xxxu_read32(priv, REG_FPGA0_XA_HSSI_PARM2);
+       if (path != RF_A)
+               val32 = rtl8xxxu_read32(priv, rtl8xxxu_rfregs[path].hssiparm2);
+       else
+               val32 = hssia;
+
+       val32 &= ~FPGA0_HSSI_PARM2_ADDR_MASK;
+       val32 |= (reg << FPGA0_HSSI_PARM2_ADDR_SHIFT);
+       val32 |= FPGA0_HSSI_PARM2_EDGE_READ;
+       hssia &= ~FPGA0_HSSI_PARM2_EDGE_READ;
+       rtl8xxxu_write32(priv, REG_FPGA0_XA_HSSI_PARM2, hssia);
+
+       udelay(10);
+
+       rtl8xxxu_write32(priv, rtl8xxxu_rfregs[path].hssiparm2, val32);
+       udelay(100);
+
+       hssia |= FPGA0_HSSI_PARM2_EDGE_READ;
+       rtl8xxxu_write32(priv, REG_FPGA0_XA_HSSI_PARM2, hssia);
+       udelay(10);
+
+       val32 = rtl8xxxu_read32(priv, rtl8xxxu_rfregs[path].hssiparm1);
+       if (val32 & FPGA0_HSSI_PARM1_PI)
+               retval = rtl8xxxu_read32(priv, rtl8xxxu_rfregs[path].hspiread);
+       else
+               retval = rtl8xxxu_read32(priv, rtl8xxxu_rfregs[path].lssiread);
+
+       retval &= 0xfffff;
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_RFREG_READ)
+               dev_info(&priv->udev->dev, "%s(%02x) = 0x%06x\n",
+                        __func__, reg, retval);
+       return retval;
+}
+
+static int rtl8xxxu_write_rfreg(struct rtl8xxxu_priv *priv,
+                               enum rtl8xxxu_rfpath path, u8 reg, u32 data)
+{
+       int ret, retval;
+       u32 dataaddr;
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_RFREG_WRITE)
+               dev_info(&priv->udev->dev, "%s(%02x) = 0x%06x\n",
+                        __func__, reg, data);
+
+       data &= FPGA0_LSSI_PARM_DATA_MASK;
+       dataaddr = (reg << FPGA0_LSSI_PARM_ADDR_SHIFT) | data;
+
+       /* Use XB for path B */
+       ret = rtl8xxxu_write32(priv, rtl8xxxu_rfregs[path].lssiparm, dataaddr);
+       if (ret != sizeof(dataaddr))
+               retval = -EIO;
+       else
+               retval = 0;
+
+       udelay(1);
+
+       return retval;
+}
+
+static int rtl8723a_h2c_cmd(struct rtl8xxxu_priv *priv, struct h2c_cmd *h2c)
+{
+       struct device *dev = &priv->udev->dev;
+       int mbox_nr, retry, retval = 0;
+       int mbox_reg, mbox_ext_reg;
+       u8 val8;
+
+       mutex_lock(&priv->h2c_mutex);
+
+       mbox_nr = priv->next_mbox;
+       mbox_reg = REG_HMBOX_0 + (mbox_nr * 4);
+       mbox_ext_reg = REG_HMBOX_EXT_0 + (mbox_nr * 2);
+
+       /*
+        * MBOX ready?
+        */
+       retry = 100;
+       do {
+               val8 = rtl8xxxu_read8(priv, REG_HMTFR);
+               if (!(val8 & BIT(mbox_nr)))
+                       break;
+       } while (retry--);
+
+       if (!retry) {
+               dev_dbg(dev, "%s: Mailbox busy\n", __func__);
+               retval = -EBUSY;
+               goto error;
+       }
+
+       /*
+        * Need to swap as it's being swapped again by rtl8xxxu_write16/32()
+        */
+       if (h2c->cmd.cmd & H2C_EXT) {
+               rtl8xxxu_write16(priv, mbox_ext_reg,
+                                le16_to_cpu(h2c->raw.ext));
+               if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C)
+                       dev_info(dev, "H2C_EXT %04x\n",
+                                le16_to_cpu(h2c->raw.ext));
+       }
+       rtl8xxxu_write32(priv, mbox_reg, le32_to_cpu(h2c->raw.data));
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C)
+               dev_info(dev, "H2C %08x\n", le32_to_cpu(h2c->raw.data));
+
+       priv->next_mbox = (mbox_nr + 1) % H2C_MAX_MBOX;
+
+error:
+       mutex_unlock(&priv->h2c_mutex);
+       return retval;
+}
+
+static void rtl8723a_enable_rf(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+       u32 val32;
+
+       val8 = rtl8xxxu_read8(priv, REG_SPS0_CTRL);
+       val8 |= BIT(0) | BIT(3);
+       rtl8xxxu_write8(priv, REG_SPS0_CTRL, val8);
+
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_XAB_RF_PARM);
+       val32 &= ~(BIT(4) | BIT(5));
+       val32 |= BIT(3);
+       if (priv->rf_paths == 2) {
+               val32 &= ~(BIT(20) | BIT(21));
+               val32 |= BIT(19);
+       }
+       rtl8xxxu_write32(priv, REG_FPGA0_XAB_RF_PARM, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_TRX_PATH_ENABLE);
+       val32 &= ~OFDM_RF_PATH_TX_MASK;
+       if (priv->tx_paths == 2)
+               val32 |= OFDM_RF_PATH_TX_A | OFDM_RF_PATH_TX_B;
+       else if (priv->rtlchip == 0x8192c || priv->rtlchip == 0x8191c)
+               val32 |= OFDM_RF_PATH_TX_B;
+       else
+               val32 |= OFDM_RF_PATH_TX_A;
+       rtl8xxxu_write32(priv, REG_OFDM0_TRX_PATH_ENABLE, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
+       val32 &= ~FPGA_RF_MODE_JAPAN;
+       rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
+
+       if (priv->rf_paths == 2)
+               rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, 0x63db25a0);
+       else
+               rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, 0x631b25a0);
+
+       rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_AC, 0x32d95);
+       if (priv->rf_paths == 2)
+               rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_AC, 0x32d95);
+
+       rtl8xxxu_write8(priv, REG_TXPAUSE, 0x00);
+}
+
+static void rtl8723a_disable_rf(struct rtl8xxxu_priv *priv)
+{
+       u8 sps0;
+       u32 val32;
+
+       rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff);
+
+       sps0 = rtl8xxxu_read8(priv, REG_SPS0_CTRL);
+
+       /* RF RX code for preamble power saving */
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_XAB_RF_PARM);
+       val32 &= ~(BIT(3) | BIT(4) | BIT(5));
+       if (priv->rf_paths == 2)
+               val32 &= ~(BIT(19) | BIT(20) | BIT(21));
+       rtl8xxxu_write32(priv, REG_FPGA0_XAB_RF_PARM, val32);
+
+       /* Disable TX for four paths */
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_TRX_PATH_ENABLE);
+       val32 &= ~OFDM_RF_PATH_TX_MASK;
+       rtl8xxxu_write32(priv, REG_OFDM0_TRX_PATH_ENABLE, val32);
+
+       /* Enable power saving */
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
+       val32 |= FPGA_RF_MODE_JAPAN;
+       rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
+
+       /* AFE control register to power down bits [30:22] */
+       if (priv->rf_paths == 2)
+               rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, 0x00db25a0);
+       else
+               rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, 0x001b25a0);
+
+       /* Power down RF module */
+       rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_AC, 0);
+       if (priv->rf_paths == 2)
+               rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_AC, 0);
+
+       sps0 &= ~(BIT(0) | BIT(3));
+       rtl8xxxu_write8(priv, REG_SPS0_CTRL, sps0);
+}
+
+
+static void rtl8723a_stop_tx_beacon(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+
+       val8 = rtl8xxxu_read8(priv, REG_FWHW_TXQ_CTRL + 2);
+       val8 &= ~BIT(6);
+       rtl8xxxu_write8(priv, REG_FWHW_TXQ_CTRL + 2, val8);
+
+       rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 1, 0x64);
+       val8 = rtl8xxxu_read8(priv, REG_TBTT_PROHIBIT + 2);
+       val8 &= ~BIT(0);
+       rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 2, val8);
+}
+
+
+/*
+ * The rtl8723a has 3 channel groups for it's efuse settings. It only
+ * supports the 2.4GHz band, so channels 1 - 14:
+ *  group 0: channels 1 - 3
+ *  group 1: channels 4 - 9
+ *  group 2: channels 10 - 14
+ *
+ * Note: We index from 0 in the code
+ */
+static int rtl8723a_channel_to_group(int channel)
+{
+       int group;
+
+       if (channel < 4)
+               group = 0;
+       else if (channel < 10)
+               group = 1;
+       else
+               group = 2;
+
+       return group;
+}
+
+static void rtl8723au_config_channel(struct ieee80211_hw *hw)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       u32 val32, rsr;
+       u8 val8, opmode;
+       bool ht = true;
+       int sec_ch_above, channel;
+       int i;
+
+       opmode = rtl8xxxu_read8(priv, REG_BW_OPMODE);
+       rsr = rtl8xxxu_read32(priv, REG_RESPONSE_RATE_SET);
+       channel = hw->conf.chandef.chan->hw_value;
+
+       switch (hw->conf.chandef.width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               ht = false;
+       case NL80211_CHAN_WIDTH_20:
+               opmode |= BW_OPMODE_20MHZ;
+               rtl8xxxu_write8(priv, REG_BW_OPMODE, opmode);
+
+               val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
+               val32 &= ~FPGA_RF_MODE;
+               rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE);
+               val32 &= ~FPGA_RF_MODE;
+               rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_FPGA0_ANALOG2);
+               val32 |= FPGA0_ANALOG2_20MHZ;
+               rtl8xxxu_write32(priv, REG_FPGA0_ANALOG2, val32);
+               break;
+       case NL80211_CHAN_WIDTH_40:
+               if (hw->conf.chandef.center_freq1 >
+                   hw->conf.chandef.chan->center_freq) {
+                       sec_ch_above = 1;
+                       channel += 2;
+               } else {
+                       sec_ch_above = 0;
+                       channel -= 2;
+               }
+
+               opmode &= ~BW_OPMODE_20MHZ;
+               rtl8xxxu_write8(priv, REG_BW_OPMODE, opmode);
+               rsr &= ~RSR_RSC_BANDWIDTH_40M;
+               if (sec_ch_above)
+                       rsr |= RSR_RSC_UPPER_SUB_CHANNEL;
+               else
+                       rsr |= RSR_RSC_LOWER_SUB_CHANNEL;
+               rtl8xxxu_write32(priv, REG_RESPONSE_RATE_SET, rsr);
+
+               val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
+               val32 |= FPGA_RF_MODE;
+               rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE);
+               val32 |= FPGA_RF_MODE;
+               rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32);
+
+               /*
+                * Set Control channel to upper or lower. These settings
+                * are required only for 40MHz
+                */
+               val32 = rtl8xxxu_read32(priv, REG_CCK0_SYSTEM);
+               val32 &= ~CCK0_SIDEBAND;
+               if (!sec_ch_above)
+                       val32 |= CCK0_SIDEBAND;
+               rtl8xxxu_write32(priv, REG_CCK0_SYSTEM, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_OFDM1_LSTF);
+               val32 &= ~OFDM_LSTF_PRIME_CH_MASK; /* 0xc00 */
+               if (sec_ch_above)
+                       val32 |= OFDM_LSTF_PRIME_CH_LOW;
+               else
+                       val32 |= OFDM_LSTF_PRIME_CH_HIGH;
+               rtl8xxxu_write32(priv, REG_OFDM1_LSTF, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_FPGA0_ANALOG2);
+               val32 &= ~FPGA0_ANALOG2_20MHZ;
+               rtl8xxxu_write32(priv, REG_FPGA0_ANALOG2, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_FPGA0_POWER_SAVE);
+               val32 &= ~(FPGA0_PS_LOWER_CHANNEL | FPGA0_PS_UPPER_CHANNEL);
+               if (sec_ch_above)
+                       val32 |= FPGA0_PS_UPPER_CHANNEL;
+               else
+                       val32 |= FPGA0_PS_LOWER_CHANNEL;
+               rtl8xxxu_write32(priv, REG_FPGA0_POWER_SAVE, val32);
+               break;
+
+       default:
+               break;
+       }
+
+       for (i = RF_A; i < priv->rf_paths; i++) {
+               val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG);
+               val32 &= ~MODE_AG_CHANNEL_MASK;
+               val32 |= channel;
+               rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32);
+       }
+
+       if (ht)
+               val8 = 0x0e;
+       else
+               val8 = 0x0a;
+
+       rtl8xxxu_write8(priv, REG_SIFS_CCK + 1, val8);
+       rtl8xxxu_write8(priv, REG_SIFS_OFDM + 1, val8);
+
+       rtl8xxxu_write16(priv, REG_R2T_SIFS, 0x0808);
+       rtl8xxxu_write16(priv, REG_T2T_SIFS, 0x0a0a);
+
+       for (i = RF_A; i < priv->rf_paths; i++) {
+               val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG);
+               if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_40)
+                       val32 &= ~MODE_AG_CHANNEL_20MHZ;
+               else
+                       val32 |= MODE_AG_CHANNEL_20MHZ;
+               rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32);
+       }
+}
+
+static void
+rtl8723a_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40)
+{
+       u8 cck[RTL8723A_MAX_RF_PATHS], ofdm[RTL8723A_MAX_RF_PATHS];
+       u8 ofdmbase[RTL8723A_MAX_RF_PATHS], mcsbase[RTL8723A_MAX_RF_PATHS];
+       u32 val32, ofdm_a, ofdm_b, mcs_a, mcs_b;
+       u8 val8;
+       int group, i;
+
+       group = rtl8723a_channel_to_group(channel);
+
+       cck[0] = priv->cck_tx_power_index_A[group];
+       cck[1] = priv->cck_tx_power_index_B[group];
+
+       ofdm[0] = priv->ht40_1s_tx_power_index_A[group];
+       ofdm[1] = priv->ht40_1s_tx_power_index_B[group];
+
+       ofdmbase[0] = ofdm[0] + priv->ofdm_tx_power_index_diff[group].a;
+       ofdmbase[1] = ofdm[1] + priv->ofdm_tx_power_index_diff[group].b;
+
+       mcsbase[0] = ofdm[0];
+       mcsbase[1] = ofdm[1];
+       if (!ht40) {
+               mcsbase[0] += priv->ht20_tx_power_index_diff[group].a;
+               mcsbase[1] += priv->ht20_tx_power_index_diff[group].b;
+       }
+
+       if (priv->tx_paths > 1) {
+               if (ofdm[0] > priv->ht40_2s_tx_power_index_diff[group].a)
+                       ofdm[0] -=  priv->ht40_2s_tx_power_index_diff[group].a;
+               if (ofdm[1] > priv->ht40_2s_tx_power_index_diff[group].b)
+                       ofdm[1] -=  priv->ht40_2s_tx_power_index_diff[group].b;
+       }
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_CHANNEL)
+               dev_info(&priv->udev->dev,
+                        "%s: Setting TX power CCK A: %02x, "
+                        "CCK B: %02x, OFDM A: %02x, OFDM B: %02x\n",
+                        __func__, cck[0], cck[1], ofdm[0], ofdm[1]);
+
+       for (i = 0; i < RTL8723A_MAX_RF_PATHS; i++) {
+               if (cck[i] > RF6052_MAX_TX_PWR)
+                       cck[i] = RF6052_MAX_TX_PWR;
+               if (ofdm[i] > RF6052_MAX_TX_PWR)
+                       ofdm[i] = RF6052_MAX_TX_PWR;
+       }
+
+       val32 = rtl8xxxu_read32(priv, REG_TX_AGC_A_CCK1_MCS32);
+       val32 &= 0xffff00ff;
+       val32 |= (cck[0] << 8);
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_CCK1_MCS32, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11);
+       val32 &= 0xff;
+       val32 |= ((cck[0] << 8) | (cck[0] << 16) | (cck[0] << 24));
+       rtl8xxxu_write32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11);
+       val32 &= 0xffffff00;
+       val32 |= cck[1];
+       rtl8xxxu_write32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_TX_AGC_B_CCK1_55_MCS32);
+       val32 &= 0xff;
+       val32 |= ((cck[1] << 8) | (cck[1] << 16) | (cck[1] << 24));
+       rtl8xxxu_write32(priv, REG_TX_AGC_B_CCK1_55_MCS32, val32);
+
+       ofdm_a = ofdmbase[0] | ofdmbase[0] << 8 |
+               ofdmbase[0] << 16 | ofdmbase[0] << 24;
+       ofdm_b = ofdmbase[1] | ofdmbase[1] << 8 |
+               ofdmbase[1] << 16 | ofdmbase[1] << 24;
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE18_06, ofdm_a);
+       rtl8xxxu_write32(priv, REG_TX_AGC_B_RATE18_06, ofdm_b);
+
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE54_24, ofdm_a);
+       rtl8xxxu_write32(priv, REG_TX_AGC_B_RATE54_24, ofdm_b);
+
+       mcs_a = mcsbase[0] | mcsbase[0] << 8 |
+               mcsbase[0] << 16 | mcsbase[0] << 24;
+       mcs_b = mcsbase[1] | mcsbase[1] << 8 |
+               mcsbase[1] << 16 | mcsbase[1] << 24;
+
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS03_MCS00, mcs_a);
+       rtl8xxxu_write32(priv, REG_TX_AGC_B_MCS03_MCS00, mcs_b);
+
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS07_MCS04, mcs_a);
+       rtl8xxxu_write32(priv, REG_TX_AGC_B_MCS07_MCS04, mcs_b);
+
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS11_MCS08, mcs_a);
+       rtl8xxxu_write32(priv, REG_TX_AGC_B_MCS11_MCS08, mcs_b);
+
+       rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS15_MCS12, mcs_a);
+       for (i = 0; i < 3; i++) {
+               if (i != 2)
+                       val8 = (mcsbase[0] > 8) ? (mcsbase[0] - 8) : 0;
+               else
+                       val8 = (mcsbase[0] > 6) ? (mcsbase[0] - 6) : 0;
+               rtl8xxxu_write8(priv, REG_OFDM0_XC_TX_IQ_IMBALANCE + i, val8);
+       }
+       rtl8xxxu_write32(priv, REG_TX_AGC_B_MCS15_MCS12, mcs_b);
+       for (i = 0; i < 3; i++) {
+               if (i != 2)
+                       val8 = (mcsbase[1] > 8) ? (mcsbase[1] - 8) : 0;
+               else
+                       val8 = (mcsbase[1] > 6) ? (mcsbase[1] - 6) : 0;
+               rtl8xxxu_write8(priv, REG_OFDM0_XD_TX_IQ_IMBALANCE + i, val8);
+       }
+}
+
+static void rtl8xxxu_set_linktype(struct rtl8xxxu_priv *priv,
+                                 enum nl80211_iftype linktype)
+{
+       u16 val8;
+
+       val8 = rtl8xxxu_read16(priv, REG_MSR);
+       val8 &= ~MSR_LINKTYPE_MASK;
+
+       switch (linktype) {
+       case NL80211_IFTYPE_UNSPECIFIED:
+               val8 |= MSR_LINKTYPE_NONE;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               val8 |= MSR_LINKTYPE_ADHOC;
+               break;
+       case NL80211_IFTYPE_STATION:
+               val8 |= MSR_LINKTYPE_STATION;
+               break;
+       case NL80211_IFTYPE_AP:
+               val8 |= MSR_LINKTYPE_AP;
+               break;
+       default:
+               goto out;
+       }
+
+       rtl8xxxu_write8(priv, REG_MSR, val8);
+out:
+       return;
+}
+
+static void
+rtl8xxxu_set_retry(struct rtl8xxxu_priv *priv, u16 short_retry, u16 long_retry)
+{
+       u16 val16;
+
+       val16 = ((short_retry << RETRY_LIMIT_SHORT_SHIFT) &
+                RETRY_LIMIT_SHORT_MASK) |
+               ((long_retry << RETRY_LIMIT_LONG_SHIFT) &
+                RETRY_LIMIT_LONG_MASK);
+
+       rtl8xxxu_write16(priv, REG_RETRY_LIMIT, val16);
+}
+
+static void
+rtl8xxxu_set_spec_sifs(struct rtl8xxxu_priv *priv, u16 cck, u16 ofdm)
+{
+       u16 val16;
+
+       val16 = ((cck << SPEC_SIFS_CCK_SHIFT) & SPEC_SIFS_CCK_MASK) |
+               ((ofdm << SPEC_SIFS_OFDM_SHIFT) & SPEC_SIFS_OFDM_MASK);
+
+       rtl8xxxu_write16(priv, REG_SPEC_SIFS, val16);
+}
+
+static void rtl8xxxu_print_chipinfo(struct rtl8xxxu_priv *priv)
+{
+       struct device *dev = &priv->udev->dev;
+       char *cut;
+
+       switch (priv->chip_cut) {
+       case 0:
+               cut = "A";
+               break;
+       case 1:
+               cut = "B";
+               break;
+       default:
+               cut = "unknown";
+       }
+
+       dev_info(dev,
+                "RTL%s rev %s (%s) %iT%iR, TX queues %i, WiFi=%i, BT=%i, GPS=%i, HI PA=%i\n",
+                priv->chip_name, cut, priv->vendor_umc ? "UMC" : "TSMC",
+                priv->tx_paths, priv->rx_paths, priv->ep_tx_count,
+                priv->has_wifi, priv->has_bluetooth, priv->has_gps,
+                priv->hi_pa);
+
+       dev_info(dev, "RTL%s MAC: %pM\n", priv->chip_name, priv->mac_addr);
+}
+
+static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv)
+{
+       struct device *dev = &priv->udev->dev;
+       u32 val32, bonding;
+       u16 val16;
+
+       val32 = rtl8xxxu_read32(priv, REG_SYS_CFG);
+       priv->chip_cut = (val32 & SYS_CFG_CHIP_VERSION_MASK) >>
+               SYS_CFG_CHIP_VERSION_SHIFT;
+       if (val32 & SYS_CFG_TRP_VAUX_EN) {
+               dev_info(dev, "Unsupported test chip\n");
+               return -ENOTSUPP;
+       }
+
+       if (val32 & SYS_CFG_BT_FUNC) {
+               sprintf(priv->chip_name, "8723AU");
+               priv->rf_paths = 1;
+               priv->rx_paths = 1;
+               priv->tx_paths = 1;
+               priv->rtlchip = 0x8723a;
+
+               val32 = rtl8xxxu_read32(priv, REG_MULTI_FUNC_CTRL);
+               if (val32 & MULTI_WIFI_FUNC_EN)
+                       priv->has_wifi = 1;
+               if (val32 & MULTI_BT_FUNC_EN)
+                       priv->has_bluetooth = 1;
+               if (val32 & MULTI_GPS_FUNC_EN)
+                       priv->has_gps = 1;
+       } else if (val32 & SYS_CFG_TYPE_ID) {
+               bonding = rtl8xxxu_read32(priv, REG_HPON_FSM);
+               bonding &= HPON_FSM_BONDING_MASK;
+               if (bonding == HPON_FSM_BONDING_1T2R) {
+                       sprintf(priv->chip_name, "8191CU");
+                       priv->rf_paths = 2;
+                       priv->rx_paths = 2;
+                       priv->tx_paths = 1;
+                       priv->rtlchip = 0x8191c;
+               } else {
+                       sprintf(priv->chip_name, "8192CU");
+                       priv->rf_paths = 2;
+                       priv->rx_paths = 2;
+                       priv->tx_paths = 2;
+                       priv->rtlchip = 0x8192c;
+               }
+               priv->has_wifi = 1;
+       } else {
+               sprintf(priv->chip_name, "8188CU");
+               priv->rf_paths = 1;
+               priv->rx_paths = 1;
+               priv->tx_paths = 1;
+               priv->rtlchip = 0x8188c;
+               priv->has_wifi = 1;
+       }
+
+       if (val32 & SYS_CFG_VENDOR_ID)
+               priv->vendor_umc = 1;
+
+       val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS);
+       priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28;
+
+       val16 = rtl8xxxu_read16(priv, REG_NORMAL_SIE_EP_TX);
+       if (val16 & NORMAL_SIE_EP_TX_HIGH_MASK) {
+               priv->ep_tx_high_queue = 1;
+               priv->ep_tx_count++;
+       }
+
+       if (val16 & NORMAL_SIE_EP_TX_NORMAL_MASK) {
+               priv->ep_tx_normal_queue = 1;
+               priv->ep_tx_count++;
+       }
+
+       if (val16 & NORMAL_SIE_EP_TX_LOW_MASK) {
+               priv->ep_tx_low_queue = 1;
+               priv->ep_tx_count++;
+       }
+
+       /*
+        * Fallback for devices that do not provide REG_NORMAL_SIE_EP_TX
+        */
+       if (!priv->ep_tx_count) {
+               switch (priv->nr_out_eps) {
+               case 3:
+                       priv->ep_tx_low_queue = 1;
+                       priv->ep_tx_count++;
+               case 2:
+                       priv->ep_tx_normal_queue = 1;
+                       priv->ep_tx_count++;
+               case 1:
+                       priv->ep_tx_high_queue = 1;
+                       priv->ep_tx_count++;
+                       break;
+               default:
+                       dev_info(dev, "Unsupported USB TX end-points\n");
+                       return -ENOTSUPP;
+               }
+       }
+
+       return 0;
+}
+
+static int rtl8723au_parse_efuse(struct rtl8xxxu_priv *priv)
+{
+       if (priv->efuse_wifi.efuse8723.rtl_id != cpu_to_le16(0x8129))
+               return -EINVAL;
+
+       ether_addr_copy(priv->mac_addr, priv->efuse_wifi.efuse8723.mac_addr);
+
+       memcpy(priv->cck_tx_power_index_A,
+              priv->efuse_wifi.efuse8723.cck_tx_power_index_A,
+              sizeof(priv->cck_tx_power_index_A));
+       memcpy(priv->cck_tx_power_index_B,
+              priv->efuse_wifi.efuse8723.cck_tx_power_index_B,
+              sizeof(priv->cck_tx_power_index_B));
+
+       memcpy(priv->ht40_1s_tx_power_index_A,
+              priv->efuse_wifi.efuse8723.ht40_1s_tx_power_index_A,
+              sizeof(priv->ht40_1s_tx_power_index_A));
+       memcpy(priv->ht40_1s_tx_power_index_B,
+              priv->efuse_wifi.efuse8723.ht40_1s_tx_power_index_B,
+              sizeof(priv->ht40_1s_tx_power_index_B));
+
+       memcpy(priv->ht20_tx_power_index_diff,
+              priv->efuse_wifi.efuse8723.ht20_tx_power_index_diff,
+              sizeof(priv->ht20_tx_power_index_diff));
+       memcpy(priv->ofdm_tx_power_index_diff,
+              priv->efuse_wifi.efuse8723.ofdm_tx_power_index_diff,
+              sizeof(priv->ofdm_tx_power_index_diff));
+
+       memcpy(priv->ht40_max_power_offset,
+              priv->efuse_wifi.efuse8723.ht40_max_power_offset,
+              sizeof(priv->ht40_max_power_offset));
+       memcpy(priv->ht20_max_power_offset,
+              priv->efuse_wifi.efuse8723.ht20_max_power_offset,
+              sizeof(priv->ht20_max_power_offset));
+
+       dev_info(&priv->udev->dev, "Vendor: %.7s\n",
+                priv->efuse_wifi.efuse8723.vendor_name);
+       dev_info(&priv->udev->dev, "Product: %.41s\n",
+                priv->efuse_wifi.efuse8723.device_name);
+       return 0;
+}
+
+#ifdef CONFIG_RTL8XXXU_UNTESTED
+
+static int rtl8192cu_parse_efuse(struct rtl8xxxu_priv *priv)
+{
+       int i;
+
+       if (priv->efuse_wifi.efuse8192.rtl_id != cpu_to_le16(0x8129))
+               return -EINVAL;
+
+       ether_addr_copy(priv->mac_addr, priv->efuse_wifi.efuse8192.mac_addr);
+
+       memcpy(priv->cck_tx_power_index_A,
+              priv->efuse_wifi.efuse8192.cck_tx_power_index_A,
+              sizeof(priv->cck_tx_power_index_A));
+       memcpy(priv->cck_tx_power_index_B,
+              priv->efuse_wifi.efuse8192.cck_tx_power_index_B,
+              sizeof(priv->cck_tx_power_index_B));
+
+       memcpy(priv->ht40_1s_tx_power_index_A,
+              priv->efuse_wifi.efuse8192.ht40_1s_tx_power_index_A,
+              sizeof(priv->ht40_1s_tx_power_index_A));
+       memcpy(priv->ht40_1s_tx_power_index_B,
+              priv->efuse_wifi.efuse8192.ht40_1s_tx_power_index_B,
+              sizeof(priv->ht40_1s_tx_power_index_B));
+       memcpy(priv->ht40_2s_tx_power_index_diff,
+              priv->efuse_wifi.efuse8192.ht40_2s_tx_power_index_diff,
+              sizeof(priv->ht40_2s_tx_power_index_diff));
+
+       memcpy(priv->ht20_tx_power_index_diff,
+              priv->efuse_wifi.efuse8192.ht20_tx_power_index_diff,
+              sizeof(priv->ht20_tx_power_index_diff));
+       memcpy(priv->ofdm_tx_power_index_diff,
+              priv->efuse_wifi.efuse8192.ofdm_tx_power_index_diff,
+              sizeof(priv->ofdm_tx_power_index_diff));
+
+       memcpy(priv->ht40_max_power_offset,
+              priv->efuse_wifi.efuse8192.ht40_max_power_offset,
+              sizeof(priv->ht40_max_power_offset));
+       memcpy(priv->ht20_max_power_offset,
+              priv->efuse_wifi.efuse8192.ht20_max_power_offset,
+              sizeof(priv->ht20_max_power_offset));
+
+       dev_info(&priv->udev->dev, "Vendor: %.7s\n",
+                priv->efuse_wifi.efuse8192.vendor_name);
+       dev_info(&priv->udev->dev, "Product: %.20s\n",
+                priv->efuse_wifi.efuse8192.device_name);
+
+       if (priv->efuse_wifi.efuse8192.rf_regulatory & 0x20) {
+               sprintf(priv->chip_name, "8188RU");
+               priv->hi_pa = 1;
+       }
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_EFUSE) {
+               unsigned char *raw = priv->efuse_wifi.raw;
+
+               dev_info(&priv->udev->dev,
+                        "%s: dumping efuse (0x%02zx bytes):\n",
+                        __func__, sizeof(struct rtl8192cu_efuse));
+               for (i = 0; i < sizeof(struct rtl8192cu_efuse); i += 8) {
+                       dev_info(&priv->udev->dev, "%02x: "
+                                "%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
+                                raw[i], raw[i + 1], raw[i + 2],
+                                raw[i + 3], raw[i + 4], raw[i + 5],
+                                raw[i + 6], raw[i + 7]);
+               }
+       }
+       return 0;
+}
+
+#endif
+
+static int
+rtl8xxxu_read_efuse8(struct rtl8xxxu_priv *priv, u16 offset, u8 *data)
+{
+       int i;
+       u8 val8;
+       u32 val32;
+
+       /* Write Address */
+       rtl8xxxu_write8(priv, REG_EFUSE_CTRL + 1, offset & 0xff);
+       val8 = rtl8xxxu_read8(priv, REG_EFUSE_CTRL + 2);
+       val8 &= 0xfc;
+       val8 |= (offset >> 8) & 0x03;
+       rtl8xxxu_write8(priv, REG_EFUSE_CTRL + 2, val8);
+
+       val8 = rtl8xxxu_read8(priv, REG_EFUSE_CTRL + 3);
+       rtl8xxxu_write8(priv, REG_EFUSE_CTRL + 3, val8 & 0x7f);
+
+       /* Poll for data read */
+       val32 = rtl8xxxu_read32(priv, REG_EFUSE_CTRL);
+       for (i = 0; i < RTL8XXXU_MAX_REG_POLL; i++) {
+               val32 = rtl8xxxu_read32(priv, REG_EFUSE_CTRL);
+               if (val32 & BIT(31))
+                       break;
+       }
+
+       if (i == RTL8XXXU_MAX_REG_POLL)
+               return -EIO;
+
+       udelay(50);
+       val32 = rtl8xxxu_read32(priv, REG_EFUSE_CTRL);
+
+       *data = val32 & 0xff;
+       return 0;
+}
+
+static int rtl8xxxu_read_efuse(struct rtl8xxxu_priv *priv)
+{
+       struct device *dev = &priv->udev->dev;
+       int i, ret = 0;
+       u8 val8, word_mask, header, extheader;
+       u16 val16, efuse_addr, offset;
+       u32 val32;
+
+       val16 = rtl8xxxu_read16(priv, REG_9346CR);
+       if (val16 & EEPROM_ENABLE)
+               priv->has_eeprom = 1;
+       if (val16 & EEPROM_BOOT)
+               priv->boot_eeprom = 1;
+
+       val32 = rtl8xxxu_read32(priv, REG_EFUSE_TEST);
+       val32 = (val32 & ~EFUSE_SELECT_MASK) | EFUSE_WIFI_SELECT;
+       rtl8xxxu_write32(priv, REG_EFUSE_TEST, val32);
+
+       dev_dbg(dev, "Booting from %s\n",
+               priv->boot_eeprom ? "EEPROM" : "EFUSE");
+
+       rtl8xxxu_write8(priv, REG_EFUSE_ACCESS, EFUSE_ACCESS_ENABLE);
+
+       /*  1.2V Power: From VDDON with Power Cut(0x0000[15]), default valid */
+       val16 = rtl8xxxu_read16(priv, REG_SYS_ISO_CTRL);
+       if (!(val16 & SYS_ISO_PWC_EV12V)) {
+               val16 |= SYS_ISO_PWC_EV12V;
+               rtl8xxxu_write16(priv, REG_SYS_ISO_CTRL, val16);
+       }
+       /*  Reset: 0x0000[28], default valid */
+       val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+       if (!(val16 & SYS_FUNC_ELDR)) {
+               val16 |= SYS_FUNC_ELDR;
+               rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
+       }
+
+       /*
+        * Clock: Gated(0x0008[5]) 8M(0x0008[1]) clock from ANA, default valid
+        */
+       val16 = rtl8xxxu_read16(priv, REG_SYS_CLKR);
+       if (!(val16 & SYS_CLK_LOADER_ENABLE) || !(val16 & SYS_CLK_ANA8M)) {
+               val16 |= (SYS_CLK_LOADER_ENABLE | SYS_CLK_ANA8M);
+               rtl8xxxu_write16(priv, REG_SYS_CLKR, val16);
+       }
+
+       /* Default value is 0xff */
+       memset(priv->efuse_wifi.raw, 0xff, EFUSE_MAP_LEN_8723A);
+
+       efuse_addr = 0;
+       while (efuse_addr < EFUSE_REAL_CONTENT_LEN_8723A) {
+               ret = rtl8xxxu_read_efuse8(priv, efuse_addr++, &header);
+               if (ret || header == 0xff)
+                       goto exit;
+
+               if ((header & 0x1f) == 0x0f) {  /* extended header */
+                       offset = (header & 0xe0) >> 5;
+
+                       ret = rtl8xxxu_read_efuse8(priv, efuse_addr++,
+                                                  &extheader);
+                       if (ret)
+                               goto exit;
+                       /* All words disabled */
+                       if ((extheader & 0x0f) == 0x0f)
+                               continue;
+
+                       offset |= ((extheader & 0xf0) >> 1);
+                       word_mask = extheader & 0x0f;
+               } else {
+                       offset = (header >> 4) & 0x0f;
+                       word_mask = header & 0x0f;
+               }
+
+               if (offset < EFUSE_MAX_SECTION_8723A) {
+                       u16 map_addr;
+                       /* Get word enable value from PG header */
+
+                       /* We have 8 bits to indicate validity */
+                       map_addr = offset * 8;
+                       if (map_addr >= EFUSE_MAP_LEN_8723A) {
+                               dev_warn(dev, "%s: Illegal map_addr (%04x), "
+                                        "efuse corrupt!\n",
+                                        __func__, map_addr);
+                               ret = -EINVAL;
+                               goto exit;
+                       }
+                       for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
+                               /* Check word enable condition in the section */
+                               if (!(word_mask & BIT(i))) {
+                                       ret = rtl8xxxu_read_efuse8(priv,
+                                                                  efuse_addr++,
+                                                                  &val8);
+                                       if (ret)
+                                               goto exit;
+                                       priv->efuse_wifi.raw[map_addr++] = val8;
+
+                                       ret = rtl8xxxu_read_efuse8(priv,
+                                                                  efuse_addr++,
+                                                                  &val8);
+                                       if (ret)
+                                               goto exit;
+                                       priv->efuse_wifi.raw[map_addr++] = val8;
+                               } else
+                                       map_addr += 2;
+                       }
+               } else {
+                       dev_warn(dev,
+                                "%s: Illegal offset (%04x), efuse corrupt!\n",
+                                __func__, offset);
+                       ret = -EINVAL;
+                       goto exit;
+               }
+       }
+
+exit:
+       rtl8xxxu_write8(priv, REG_EFUSE_ACCESS, EFUSE_ACCESS_DISABLE);
+
+       return ret;
+}
+
+static int rtl8xxxu_start_firmware(struct rtl8xxxu_priv *priv)
+{
+       struct device *dev = &priv->udev->dev;
+       int ret = 0, i;
+       u32 val32;
+
+       /* Poll checksum report */
+       for (i = 0; i < RTL8XXXU_FIRMWARE_POLL_MAX; i++) {
+               val32 = rtl8xxxu_read32(priv, REG_MCU_FW_DL);
+               if (val32 & MCU_FW_DL_CSUM_REPORT)
+                       break;
+       }
+
+       if (i == RTL8XXXU_FIRMWARE_POLL_MAX) {
+               dev_warn(dev, "Firmware checksum poll timed out\n");
+               ret = -EAGAIN;
+               goto exit;
+       }
+
+       val32 = rtl8xxxu_read32(priv, REG_MCU_FW_DL);
+       val32 |= MCU_FW_DL_READY;
+       val32 &= ~MCU_WINT_INIT_READY;
+       rtl8xxxu_write32(priv, REG_MCU_FW_DL, val32);
+
+       /* Wait for firmware to become ready */
+       for (i = 0; i < RTL8XXXU_FIRMWARE_POLL_MAX; i++) {
+               val32 = rtl8xxxu_read32(priv, REG_MCU_FW_DL);
+               if (val32 & MCU_WINT_INIT_READY)
+                       break;
+
+               udelay(100);
+       }
+
+       if (i == RTL8XXXU_FIRMWARE_POLL_MAX) {
+               dev_warn(dev, "Firmware failed to start\n");
+               ret = -EAGAIN;
+               goto exit;
+       }
+
+exit:
+       return ret;
+}
+
+static int rtl8xxxu_download_firmware(struct rtl8xxxu_priv *priv)
+{
+       int pages, remainder, i, ret;
+       u8 val8;
+       u16 val16;
+       u32 val32;
+       u8 *fwptr;
+
+       val8 = rtl8xxxu_read8(priv, REG_SYS_FUNC + 1);
+       val8 |= 4;
+       rtl8xxxu_write8(priv, REG_SYS_FUNC + 1, val8);
+
+       /* 8051 enable */
+       val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+       rtl8xxxu_write16(priv, REG_SYS_FUNC, val16 | SYS_FUNC_CPU_ENABLE);
+
+       /* MCU firmware download enable */
+       val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL);
+       rtl8xxxu_write8(priv, REG_MCU_FW_DL, val8 | MCU_FW_DL_ENABLE);
+
+       /* 8051 reset */
+       val32 = rtl8xxxu_read32(priv, REG_MCU_FW_DL);
+       rtl8xxxu_write32(priv, REG_MCU_FW_DL, val32 & ~BIT(19));
+
+       /* Reset firmware download checksum */
+       val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL);
+       rtl8xxxu_write8(priv, REG_MCU_FW_DL, val8 | MCU_FW_DL_CSUM_REPORT);
+
+       pages = priv->fw_size / RTL_FW_PAGE_SIZE;
+       remainder = priv->fw_size % RTL_FW_PAGE_SIZE;
+
+       fwptr = priv->fw_data->data;
+
+       for (i = 0; i < pages; i++) {
+               val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL + 2) & 0xF8;
+               rtl8xxxu_write8(priv, REG_MCU_FW_DL + 2, val8 | i);
+
+               ret = rtl8xxxu_writeN(priv, REG_FW_START_ADDRESS,
+                                     fwptr, RTL_FW_PAGE_SIZE);
+               if (ret != RTL_FW_PAGE_SIZE) {
+                       ret = -EAGAIN;
+                       goto fw_abort;
+               }
+
+               fwptr += RTL_FW_PAGE_SIZE;
+       }
+
+       if (remainder) {
+               val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL + 2) & 0xF8;
+               rtl8xxxu_write8(priv, REG_MCU_FW_DL + 2, val8 | i);
+               ret = rtl8xxxu_writeN(priv, REG_FW_START_ADDRESS,
+                                     fwptr, remainder);
+               if (ret != remainder) {
+                       ret = -EAGAIN;
+                       goto fw_abort;
+               }
+       }
+
+       ret = 0;
+fw_abort:
+       /* MCU firmware download disable */
+       val16 = rtl8xxxu_read16(priv, REG_MCU_FW_DL);
+       rtl8xxxu_write16(priv, REG_MCU_FW_DL,
+                        val16 & (~MCU_FW_DL_ENABLE & 0xff));
+
+       return ret;
+}
+
+static int rtl8xxxu_load_firmware(struct rtl8xxxu_priv *priv, char *fw_name)
+{
+       struct device *dev = &priv->udev->dev;
+       const struct firmware *fw;
+       int ret = 0;
+       u16 signature;
+
+       dev_info(dev, "%s: Loading firmware %s\n", DRIVER_NAME, fw_name);
+       if (request_firmware(&fw, fw_name, &priv->udev->dev)) {
+               dev_warn(dev, "request_firmware(%s) failed\n", fw_name);
+               ret = -EAGAIN;
+               goto exit;
+       }
+       if (!fw) {
+               dev_warn(dev, "Firmware data not available\n");
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       priv->fw_data = kmemdup(fw->data, fw->size, GFP_KERNEL);
+       priv->fw_size = fw->size - sizeof(struct rtl8xxxu_firmware_header);
+
+       signature = le16_to_cpu(priv->fw_data->signature);
+       switch (signature & 0xfff0) {
+       case 0x92c0:
+       case 0x88c0:
+       case 0x2300:
+               break;
+       default:
+               ret = -EINVAL;
+               dev_warn(dev, "%s: Invalid firmware signature: 0x%04x\n",
+                        __func__, signature);
+       }
+
+       dev_info(dev, "Firmware revision %i.%i (signature 0x%04x)\n",
+                le16_to_cpu(priv->fw_data->major_version),
+                priv->fw_data->minor_version, signature);
+
+exit:
+       release_firmware(fw);
+       return ret;
+}
+
+static int rtl8723au_load_firmware(struct rtl8xxxu_priv *priv)
+{
+       char *fw_name;
+       int ret;
+
+       switch (priv->chip_cut) {
+       case 0:
+               fw_name = "rtlwifi/rtl8723aufw_A.bin";
+               break;
+       case 1:
+               if (priv->enable_bluetooth)
+                       fw_name = "rtlwifi/rtl8723aufw_B.bin";
+               else
+                       fw_name = "rtlwifi/rtl8723aufw_B_NoBT.bin";
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = rtl8xxxu_load_firmware(priv, fw_name);
+       return ret;
+}
+
+#ifdef CONFIG_RTL8XXXU_UNTESTED
+
+static int rtl8192cu_load_firmware(struct rtl8xxxu_priv *priv)
+{
+       char *fw_name;
+       int ret;
+
+       if (!priv->vendor_umc)
+               fw_name = "rtlwifi/rtl8192cufw_TMSC.bin";
+       else if (priv->chip_cut || priv->rtlchip == 0x8192c)
+               fw_name = "rtlwifi/rtl8192cufw_B.bin";
+       else
+               fw_name = "rtlwifi/rtl8192cufw_A.bin";
+
+       ret = rtl8xxxu_load_firmware(priv, fw_name);
+
+       return ret;
+}
+
+#endif
+
+static void rtl8xxxu_firmware_self_reset(struct rtl8xxxu_priv *priv)
+{
+       u16 val16;
+       int i = 100;
+
+       /* Inform 8051 to perform reset */
+       rtl8xxxu_write8(priv, REG_HMTFR + 3, 0x20);
+
+       for (i = 100; i > 0; i--) {
+               val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+
+               if (!(val16 & SYS_FUNC_CPU_ENABLE)) {
+                       dev_dbg(&priv->udev->dev,
+                               "%s: Firmware self reset success!\n", __func__);
+                       break;
+               }
+               udelay(50);
+       }
+
+       if (!i) {
+               /* Force firmware reset */
+               val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+               val16 &= ~SYS_FUNC_CPU_ENABLE;
+               rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
+       }
+}
+
+static int
+rtl8xxxu_init_mac(struct rtl8xxxu_priv *priv, struct rtl8xxxu_reg8val *array)
+{
+       int i, ret;
+       u16 reg;
+       u8 val;
+
+       for (i = 0; ; i++) {
+               reg = array[i].reg;
+               val = array[i].val;
+
+               if (reg == 0xffff && val == 0xff)
+                       break;
+
+               ret = rtl8xxxu_write8(priv, reg, val);
+               if (ret != 1) {
+                       dev_warn(&priv->udev->dev,
+                                "Failed to initialize MAC\n");
+                       return -EAGAIN;
+               }
+       }
+
+       rtl8xxxu_write8(priv, REG_MAX_AGGR_NUM, 0x0a);
+
+       return 0;
+}
+
+static int rtl8xxxu_init_phy_regs(struct rtl8xxxu_priv *priv,
+                                 struct rtl8xxxu_reg32val *array)
+{
+       int i, ret;
+       u16 reg;
+       u32 val;
+
+       for (i = 0; ; i++) {
+               reg = array[i].reg;
+               val = array[i].val;
+
+               if (reg == 0xffff && val == 0xffffffff)
+                       break;
+
+               ret = rtl8xxxu_write32(priv, reg, val);
+               if (ret != sizeof(val)) {
+                       dev_warn(&priv->udev->dev,
+                                "Failed to initialize PHY\n");
+                       return -EAGAIN;
+               }
+               udelay(1);
+       }
+
+       return 0;
+}
+
+/*
+ * Most of this is black magic retrieved from the old rtl8723au driver
+ */
+static int rtl8xxxu_init_phy_bb(struct rtl8xxxu_priv *priv)
+{
+       u8 val8, ldoa15, ldov12d, lpldo, ldohci12;
+       u32 val32;
+
+       /*
+        * Todo: The vendor driver maintains a table of PHY register
+        *       addresses, which is initialized here. Do we need this?
+        */
+
+       val8 = rtl8xxxu_read8(priv, REG_AFE_PLL_CTRL);
+       udelay(2);
+       val8 |= AFE_PLL_320_ENABLE;
+       rtl8xxxu_write8(priv, REG_AFE_PLL_CTRL, val8);
+       udelay(2);
+
+       rtl8xxxu_write8(priv, REG_AFE_PLL_CTRL + 1, 0xff);
+       udelay(2);
+
+       val8 = rtl8xxxu_read8(priv, REG_SYS_FUNC);
+       val8 |= SYS_FUNC_BB_GLB_RSTN | SYS_FUNC_BBRSTB;
+       rtl8xxxu_write8(priv, REG_SYS_FUNC, val8);
+
+       /* AFE_XTAL_RF_GATE (bit 14) if addressing as 32 bit register */
+       val32 = rtl8xxxu_read32(priv, REG_AFE_XTAL_CTRL);
+       val32 &= ~AFE_XTAL_RF_GATE;
+       if (priv->has_bluetooth)
+               val32 &= ~AFE_XTAL_BT_GATE;
+       rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, val32);
+
+       /* 6. 0x1f[7:0] = 0x07 */
+       val8 = RF_ENABLE | RF_RSTB | RF_SDMRSTB;
+       rtl8xxxu_write8(priv, REG_RF_CTRL, val8);
+
+       if (priv->hi_pa)
+               rtl8xxxu_init_phy_regs(priv, rtl8188ru_phy_1t_highpa_table);
+       else if (priv->tx_paths == 2)
+               rtl8xxxu_init_phy_regs(priv, rtl8192cu_phy_2t_init_table);
+       else
+               rtl8xxxu_init_phy_regs(priv, rtl8723a_phy_1t_init_table);
+
+
+       if (priv->rtlchip == 0x8188c && priv->hi_pa &&
+           priv->vendor_umc && priv->chip_cut == 1)
+               rtl8xxxu_write8(priv, REG_OFDM0_AGC_PARM1 + 2, 0x50);
+
+       if (priv->tx_paths == 1 && priv->rx_paths == 2) {
+               /*
+                * For 1T2R boards, patch the registers.
+                *
+                * It looks like 8191/2 1T2R boards use path B for TX
+                */
+               val32 = rtl8xxxu_read32(priv, REG_FPGA0_TX_INFO);
+               val32 &= ~(BIT(0) | BIT(1));
+               val32 |= BIT(1);
+               rtl8xxxu_write32(priv, REG_FPGA0_TX_INFO, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_FPGA1_TX_INFO);
+               val32 &= ~0x300033;
+               val32 |= 0x200022;
+               rtl8xxxu_write32(priv, REG_FPGA1_TX_INFO, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_CCK0_AFE_SETTING);
+               val32 &= 0xff000000;
+               val32 |= 0x45000000;
+               rtl8xxxu_write32(priv, REG_CCK0_AFE_SETTING, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_OFDM0_TRX_PATH_ENABLE);
+               val32 &= ~(OFDM_RF_PATH_RX_MASK | OFDM_RF_PATH_TX_MASK);
+               val32 |= (OFDM_RF_PATH_RX_A | OFDM_RF_PATH_RX_B |
+                         OFDM_RF_PATH_TX_B);
+               rtl8xxxu_write32(priv, REG_OFDM0_TRX_PATH_ENABLE, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_OFDM0_AGC_PARM1);
+               val32 &= ~(BIT(4) | BIT(5));
+               val32 |= BIT(4);
+               rtl8xxxu_write32(priv, REG_OFDM0_AGC_PARM1, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_TX_CCK_RFON);
+               val32 &= ~(BIT(27) | BIT(26));
+               val32 |= BIT(27);
+               rtl8xxxu_write32(priv, REG_TX_CCK_RFON, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_TX_CCK_BBON);
+               val32 &= ~(BIT(27) | BIT(26));
+               val32 |= BIT(27);
+               rtl8xxxu_write32(priv, REG_TX_CCK_BBON, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_TX_OFDM_RFON);
+               val32 &= ~(BIT(27) | BIT(26));
+               val32 |= BIT(27);
+               rtl8xxxu_write32(priv, REG_TX_OFDM_RFON, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_TX_OFDM_BBON);
+               val32 &= ~(BIT(27) | BIT(26));
+               val32 |= BIT(27);
+               rtl8xxxu_write32(priv, REG_TX_OFDM_BBON, val32);
+
+               val32 = rtl8xxxu_read32(priv, REG_TX_TO_TX);
+               val32 &= ~(BIT(27) | BIT(26));
+               val32 |= BIT(27);
+               rtl8xxxu_write32(priv, REG_TX_TO_TX, val32);
+       }
+
+       if (priv->hi_pa)
+               rtl8xxxu_init_phy_regs(priv, rtl8xxx_agc_highpa_table);
+       else
+               rtl8xxxu_init_phy_regs(priv, rtl8xxx_agc_standard_table);
+
+       if (priv->rtlchip == 0x8723a &&
+           priv->efuse_wifi.efuse8723.version >= 0x01) {
+               val32 = rtl8xxxu_read32(priv, REG_MAC_PHY_CTRL);
+
+               val8 = priv->efuse_wifi.efuse8723.xtal_k & 0x3f;
+               val32 &= 0xff000fff;
+               val32 |= ((val8 | (val8 << 6)) << 12);
+
+               rtl8xxxu_write32(priv, REG_MAC_PHY_CTRL, val32);
+       }
+
+       ldoa15 = LDOA15_ENABLE | LDOA15_OBUF;
+       ldov12d = LDOV12D_ENABLE | BIT(2) | (2 << LDOV12D_VADJ_SHIFT);
+       ldohci12 = 0x57;
+       lpldo = 1;
+       val32 = (lpldo << 24) | (ldohci12 << 16) | (ldov12d << 8) | ldoa15;
+
+       rtl8xxxu_write32(priv, REG_LDOA15_CTRL, val32);
+
+       return 0;
+}
+
+static int rtl8xxxu_init_rf_regs(struct rtl8xxxu_priv *priv,
+                                struct rtl8xxxu_rfregval *array,
+                                enum rtl8xxxu_rfpath path)
+{
+       int i, ret;
+       u8 reg;
+       u32 val;
+
+       for (i = 0; ; i++) {
+               reg = array[i].reg;
+               val = array[i].val;
+
+               if (reg == 0xff && val == 0xffffffff)
+                       break;
+
+               switch (reg) {
+               case 0xfe:
+                       msleep(50);
+                       continue;
+               case 0xfd:
+                       mdelay(5);
+                       continue;
+               case 0xfc:
+                       mdelay(1);
+                       continue;
+               case 0xfb:
+                       udelay(50);
+                       continue;
+               case 0xfa:
+                       udelay(5);
+                       continue;
+               case 0xf9:
+                       udelay(1);
+                       continue;
+               }
+
+               reg &= 0x3f;
+
+               ret = rtl8xxxu_write_rfreg(priv, path, reg, val);
+               if (ret) {
+                       dev_warn(&priv->udev->dev,
+                                "Failed to initialize RF\n");
+                       return -EAGAIN;
+               }
+               udelay(1);
+       }
+
+       return 0;
+}
+
+static int rtl8xxxu_init_phy_rf(struct rtl8xxxu_priv *priv,
+                               struct rtl8xxxu_rfregval *table,
+                               enum rtl8xxxu_rfpath path)
+{
+       u32 val32;
+       u16 val16, rfsi_rfenv;
+       u16 reg_sw_ctrl, reg_int_oe, reg_hssi_parm2;
+
+       switch (path) {
+       case RF_A:
+               reg_sw_ctrl = REG_FPGA0_XA_RF_SW_CTRL;
+               reg_int_oe = REG_FPGA0_XA_RF_INT_OE;
+               reg_hssi_parm2 = REG_FPGA0_XA_HSSI_PARM2;
+               break;
+       case RF_B:
+               reg_sw_ctrl = REG_FPGA0_XB_RF_SW_CTRL;
+               reg_int_oe = REG_FPGA0_XB_RF_INT_OE;
+               reg_hssi_parm2 = REG_FPGA0_XB_HSSI_PARM2;
+               break;
+       default:
+               dev_err(&priv->udev->dev, "%s:Unsupported RF path %c\n",
+                       __func__, path + 'A');
+               return -EINVAL;
+       }
+       /* For path B, use XB */
+       rfsi_rfenv = rtl8xxxu_read16(priv, reg_sw_ctrl);
+       rfsi_rfenv &= FPGA0_RF_RFENV;
+
+       /*
+        * These two we might be able to optimize into one
+        */
+       val32 = rtl8xxxu_read32(priv, reg_int_oe);
+       val32 |= BIT(20);       /* 0x10 << 16 */
+       rtl8xxxu_write32(priv, reg_int_oe, val32);
+       udelay(1);
+
+       val32 = rtl8xxxu_read32(priv, reg_int_oe);
+       val32 |= BIT(4);
+       rtl8xxxu_write32(priv, reg_int_oe, val32);
+       udelay(1);
+
+       /*
+        * These two we might be able to optimize into one
+        */
+       val32 = rtl8xxxu_read32(priv, reg_hssi_parm2);
+       val32 &= ~FPGA0_HSSI_3WIRE_ADDR_LEN;
+       rtl8xxxu_write32(priv, reg_hssi_parm2, val32);
+       udelay(1);
+
+       val32 = rtl8xxxu_read32(priv, reg_hssi_parm2);
+       val32 &= ~FPGA0_HSSI_3WIRE_DATA_LEN;
+       rtl8xxxu_write32(priv, reg_hssi_parm2, val32);
+       udelay(1);
+
+       rtl8xxxu_init_rf_regs(priv, table, path);
+
+       /* For path B, use XB */
+       val16 = rtl8xxxu_read16(priv, reg_sw_ctrl);
+       val16 &= ~FPGA0_RF_RFENV;
+       val16 |= rfsi_rfenv;
+       rtl8xxxu_write16(priv, reg_sw_ctrl, val16);
+
+       return 0;
+}
+
+static int rtl8xxxu_llt_write(struct rtl8xxxu_priv *priv, u8 address, u8 data)
+{
+       int ret = -EBUSY;
+       int count = 0;
+       u32 value;
+
+       value = LLT_OP_WRITE | address << 8 | data;
+
+       rtl8xxxu_write32(priv, REG_LLT_INIT, value);
+
+       do {
+               value = rtl8xxxu_read32(priv, REG_LLT_INIT);
+               if ((value & LLT_OP_MASK) == LLT_OP_INACTIVE) {
+                       ret = 0;
+                       break;
+               }
+       } while (count++ < 20);
+
+       return ret;
+}
+
+static int rtl8xxxu_init_llt_table(struct rtl8xxxu_priv *priv, u8 last_tx_page)
+{
+       int ret;
+       int i;
+
+       for (i = 0; i < last_tx_page; i++) {
+               ret = rtl8xxxu_llt_write(priv, i, i + 1);
+               if (ret)
+                       goto exit;
+       }
+
+       ret = rtl8xxxu_llt_write(priv, last_tx_page, 0xff);
+       if (ret)
+               goto exit;
+
+       /* Mark remaining pages as a ring buffer */
+       for (i = last_tx_page + 1; i < 0xff; i++) {
+               ret = rtl8xxxu_llt_write(priv, i, (i + 1));
+               if (ret)
+                       goto exit;
+       }
+
+       /*  Let last entry point to the start entry of ring buffer */
+       ret = rtl8xxxu_llt_write(priv, 0xff, last_tx_page + 1);
+       if (ret)
+               goto exit;
+
+exit:
+       return ret;
+}
+
+static int rtl8xxxu_init_queue_priority(struct rtl8xxxu_priv *priv)
+{
+       u16 val16, hi, lo;
+       u16 hiq, mgq, bkq, beq, viq, voq;
+       int hip, mgp, bkp, bep, vip, vop;
+       int ret = 0;
+
+       switch (priv->ep_tx_count) {
+       case 1:
+               if (priv->ep_tx_high_queue) {
+                       hi = TRXDMA_QUEUE_HIGH;
+               } else if (priv->ep_tx_low_queue) {
+                       hi = TRXDMA_QUEUE_LOW;
+               } else if (priv->ep_tx_normal_queue) {
+                       hi = TRXDMA_QUEUE_NORMAL;
+               } else {
+                       hi = 0;
+                       ret = -EINVAL;
+               }
+
+               hiq = hi;
+               mgq = hi;
+               bkq = hi;
+               beq = hi;
+               viq = hi;
+               voq = hi;
+
+               hip = 0;
+               mgp = 0;
+               bkp = 0;
+               bep = 0;
+               vip = 0;
+               vop = 0;
+               break;
+       case 2:
+               if (priv->ep_tx_high_queue && priv->ep_tx_low_queue) {
+                       hi = TRXDMA_QUEUE_HIGH;
+                       lo = TRXDMA_QUEUE_LOW;
+               } else if (priv->ep_tx_normal_queue && priv->ep_tx_low_queue) {
+                       hi = TRXDMA_QUEUE_NORMAL;
+                       lo = TRXDMA_QUEUE_LOW;
+               } else if (priv->ep_tx_high_queue && priv->ep_tx_normal_queue) {
+                       hi = TRXDMA_QUEUE_HIGH;
+                       lo = TRXDMA_QUEUE_NORMAL;
+               } else {
+                       ret = -EINVAL;
+                       hi = 0;
+                       lo = 0;
+               }
+
+               hiq = hi;
+               mgq = hi;
+               bkq = lo;
+               beq = lo;
+               viq = hi;
+               voq = hi;
+
+               hip = 0;
+               mgp = 0;
+               bkp = 1;
+               bep = 1;
+               vip = 0;
+               vop = 0;
+               break;
+       case 3:
+               beq = TRXDMA_QUEUE_LOW;
+               bkq = TRXDMA_QUEUE_LOW;
+               viq = TRXDMA_QUEUE_NORMAL;
+               voq = TRXDMA_QUEUE_HIGH;
+               mgq = TRXDMA_QUEUE_HIGH;
+               hiq = TRXDMA_QUEUE_HIGH;
+
+               hip = hiq ^ 3;
+               mgp = mgq ^ 3;
+               bkp = bkq ^ 3;
+               bep = beq ^ 3;
+               vip = viq ^ 3;
+               vop = viq ^ 3;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       /*
+        * None of the vendor drivers are configuring the beacon
+        * queue here .... why?
+        */
+       if (!ret) {
+               val16 = rtl8xxxu_read16(priv, REG_TRXDMA_CTRL);
+               val16 &= 0x7;
+               val16 |= (voq << TRXDMA_CTRL_VOQ_SHIFT) |
+                       (viq << TRXDMA_CTRL_VIQ_SHIFT) |
+                       (beq << TRXDMA_CTRL_BEQ_SHIFT) |
+                       (bkq << TRXDMA_CTRL_BKQ_SHIFT) |
+                       (mgq << TRXDMA_CTRL_MGQ_SHIFT) |
+                       (hiq << TRXDMA_CTRL_HIQ_SHIFT);
+               rtl8xxxu_write16(priv, REG_TRXDMA_CTRL, val16);
+
+               priv->pipe_out[TXDESC_QUEUE_VO] =
+                       usb_sndbulkpipe(priv->udev, priv->out_ep[vop]);
+               priv->pipe_out[TXDESC_QUEUE_VI] =
+                       usb_sndbulkpipe(priv->udev, priv->out_ep[vip]);
+               priv->pipe_out[TXDESC_QUEUE_BE] =
+                       usb_sndbulkpipe(priv->udev, priv->out_ep[bep]);
+               priv->pipe_out[TXDESC_QUEUE_BK] =
+                       usb_sndbulkpipe(priv->udev, priv->out_ep[bkp]);
+               priv->pipe_out[TXDESC_QUEUE_BEACON] =
+                       usb_sndbulkpipe(priv->udev, priv->out_ep[0]);
+               priv->pipe_out[TXDESC_QUEUE_MGNT] =
+                       usb_sndbulkpipe(priv->udev, priv->out_ep[mgp]);
+               priv->pipe_out[TXDESC_QUEUE_HIGH] =
+                       usb_sndbulkpipe(priv->udev, priv->out_ep[hip]);
+               priv->pipe_out[TXDESC_QUEUE_CMD] =
+                       usb_sndbulkpipe(priv->udev, priv->out_ep[0]);
+       }
+
+       return ret;
+}
+
+static void rtl8xxxu_fill_iqk_matrix_a(struct rtl8xxxu_priv *priv,
+                                      bool iqk_ok, int result[][8],
+                                      int candidate, bool tx_only)
+{
+       u32 oldval, x, tx0_a, reg;
+       int y, tx0_c;
+       u32 val32;
+
+       if (!iqk_ok)
+               return;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_TX_IQ_IMBALANCE);
+       oldval = val32 >> 22;
+
+       x = result[candidate][0];
+       if ((x & 0x00000200) != 0)
+               x = x | 0xfffffc00;
+       tx0_a = (x * oldval) >> 8;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_TX_IQ_IMBALANCE);
+       val32 &= ~0x3ff;
+       val32 |= tx0_a;
+       rtl8xxxu_write32(priv, REG_OFDM0_XA_TX_IQ_IMBALANCE, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_ENERGY_CCA_THRES);
+       val32 &= ~BIT(31);
+       if ((x * oldval >> 7) & 0x1)
+               val32 |= BIT(31);
+       rtl8xxxu_write32(priv, REG_OFDM0_ENERGY_CCA_THRES, val32);
+
+       y = result[candidate][1];
+       if ((y & 0x00000200) != 0)
+               y = y | 0xfffffc00;
+       tx0_c = (y * oldval) >> 8;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XC_TX_AFE);
+       val32 &= ~0xf0000000;
+       val32 |= (((tx0_c & 0x3c0) >> 6) << 28);
+       rtl8xxxu_write32(priv, REG_OFDM0_XC_TX_AFE, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_TX_IQ_IMBALANCE);
+       val32 &= ~0x003f0000;
+       val32 |= ((tx0_c & 0x3f) << 16);
+       rtl8xxxu_write32(priv, REG_OFDM0_XA_TX_IQ_IMBALANCE, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_ENERGY_CCA_THRES);
+       val32 &= ~BIT(29);
+       if ((y * oldval >> 7) & 0x1)
+               val32 |= BIT(29);
+       rtl8xxxu_write32(priv, REG_OFDM0_ENERGY_CCA_THRES, val32);
+
+       if (tx_only) {
+               dev_dbg(&priv->udev->dev, "%s: only TX\n", __func__);
+               return;
+       }
+
+       reg = result[candidate][2];
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_RX_IQ_IMBALANCE);
+       val32 &= ~0x3ff;
+       val32 |= (reg & 0x3ff);
+       rtl8xxxu_write32(priv, REG_OFDM0_XA_RX_IQ_IMBALANCE, val32);
+
+       reg = result[candidate][3] & 0x3F;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_RX_IQ_IMBALANCE);
+       val32 &= ~0xfc00;
+       val32 |= ((reg << 10) & 0xfc00);
+       rtl8xxxu_write32(priv, REG_OFDM0_XA_RX_IQ_IMBALANCE, val32);
+
+       reg = (result[candidate][3] >> 6) & 0xF;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_RX_IQ_EXT_ANTA);
+       val32 &= ~0xf0000000;
+       val32 |= (reg << 28);
+       rtl8xxxu_write32(priv, REG_OFDM0_RX_IQ_EXT_ANTA, val32);
+}
+
+static void rtl8xxxu_fill_iqk_matrix_b(struct rtl8xxxu_priv *priv,
+                                      bool iqk_ok, int result[][8],
+                                      int candidate, bool tx_only)
+{
+       u32 oldval, x, tx1_a, reg;
+       int y, tx1_c;
+       u32 val32;
+
+       if (!iqk_ok)
+               return;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XB_TX_IQ_IMBALANCE);
+       oldval = val32 >> 22;
+
+       x = result[candidate][4];
+       if ((x & 0x00000200) != 0)
+               x = x | 0xfffffc00;
+       tx1_a = (x * oldval) >> 8;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XB_TX_IQ_IMBALANCE);
+       val32 &= ~0x3ff;
+       val32 |= tx1_a;
+       rtl8xxxu_write32(priv, REG_OFDM0_XB_TX_IQ_IMBALANCE, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_ENERGY_CCA_THRES);
+       val32 &= ~BIT(27);
+       if ((x * oldval >> 7) & 0x1)
+               val32 |= BIT(27);
+       rtl8xxxu_write32(priv, REG_OFDM0_ENERGY_CCA_THRES, val32);
+
+       y = result[candidate][5];
+       if ((y & 0x00000200) != 0)
+               y = y | 0xfffffc00;
+       tx1_c = (y * oldval) >> 8;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XD_TX_AFE);
+       val32 &= ~0xf0000000;
+       val32 |= (((tx1_c & 0x3c0) >> 6) << 28);
+       rtl8xxxu_write32(priv, REG_OFDM0_XD_TX_AFE, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XB_TX_IQ_IMBALANCE);
+       val32 &= ~0x003f0000;
+       val32 |= ((tx1_c & 0x3f) << 16);
+       rtl8xxxu_write32(priv, REG_OFDM0_XB_TX_IQ_IMBALANCE, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_ENERGY_CCA_THRES);
+       val32 &= ~BIT(25);
+       if ((y * oldval >> 7) & 0x1)
+               val32 |= BIT(25);
+       rtl8xxxu_write32(priv, REG_OFDM0_ENERGY_CCA_THRES, val32);
+
+       if (tx_only) {
+               dev_dbg(&priv->udev->dev, "%s: only TX\n", __func__);
+               return;
+       }
+
+       reg = result[candidate][6];
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XB_RX_IQ_IMBALANCE);
+       val32 &= ~0x3ff;
+       val32 |= (reg & 0x3ff);
+       rtl8xxxu_write32(priv, REG_OFDM0_XB_RX_IQ_IMBALANCE, val32);
+
+       reg = result[candidate][7] & 0x3f;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XB_RX_IQ_IMBALANCE);
+       val32 &= ~0xfc00;
+       val32 |= ((reg << 10) & 0xfc00);
+       rtl8xxxu_write32(priv, REG_OFDM0_XB_RX_IQ_IMBALANCE, val32);
+
+       reg = (result[candidate][7] >> 6) & 0xf;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_AGCR_SSI_TABLE);
+       val32 &= ~0x0000f000;
+       val32 |= (reg << 12);
+       rtl8xxxu_write32(priv, REG_OFDM0_AGCR_SSI_TABLE, val32);
+}
+
+#define MAX_TOLERANCE          5
+
+static bool rtl8xxxu_simularity_compare(struct rtl8xxxu_priv *priv,
+                                       int result[][8], int c1, int c2)
+{
+       u32 i, j, diff, simubitmap, bound = 0;
+       int candidate[2] = {-1, -1};    /* for path A and path B */
+       bool retval = true;
+
+       if (priv->tx_paths > 1)
+               bound = 8;
+       else
+               bound = 4;
+
+       simubitmap = 0;
+
+       for (i = 0; i < bound; i++) {
+               diff = (result[c1][i] > result[c2][i]) ?
+                       (result[c1][i] - result[c2][i]) :
+                       (result[c2][i] - result[c1][i]);
+               if (diff > MAX_TOLERANCE) {
+                       if ((i == 2 || i == 6) && !simubitmap) {
+                               if (result[c1][i] + result[c1][i + 1] == 0)
+                                       candidate[(i / 4)] = c2;
+                               else if (result[c2][i] + result[c2][i + 1] == 0)
+                                       candidate[(i / 4)] = c1;
+                               else
+                                       simubitmap = simubitmap | (1 << i);
+                       } else {
+                               simubitmap = simubitmap | (1 << i);
+                       }
+               }
+       }
+
+       if (simubitmap == 0) {
+               for (i = 0; i < (bound / 4); i++) {
+                       if (candidate[i] >= 0) {
+                               for (j = i * 4; j < (i + 1) * 4 - 2; j++)
+                                       result[3][j] = result[candidate[i]][j];
+                               retval = false;
+                       }
+               }
+               return retval;
+       } else if (!(simubitmap & 0x0f)) {
+               /* path A OK */
+               for (i = 0; i < 4; i++)
+                       result[3][i] = result[c1][i];
+       } else if (!(simubitmap & 0xf0) && priv->tx_paths > 1) {
+               /* path B OK */
+               for (i = 4; i < 8; i++)
+                       result[3][i] = result[c1][i];
+       }
+
+       return false;
+}
+
+static void
+rtl8xxxu_save_mac_regs(struct rtl8xxxu_priv *priv, const u32 *reg, u32 *backup)
+{
+       int i;
+
+       for (i = 0; i < (RTL8XXXU_MAC_REGS - 1); i++)
+               backup[i] = rtl8xxxu_read8(priv, reg[i]);
+
+       backup[i] = rtl8xxxu_read32(priv, reg[i]);
+}
+
+static void rtl8xxxu_restore_mac_regs(struct rtl8xxxu_priv *priv,
+                                     const u32 *reg, u32 *backup)
+{
+       int i;
+
+       for (i = 0; i < (RTL8XXXU_MAC_REGS - 1); i++)
+               rtl8xxxu_write8(priv, reg[i], backup[i]);
+
+       rtl8xxxu_write32(priv, reg[i], backup[i]);
+}
+
+static void rtl8xxxu_save_regs(struct rtl8xxxu_priv *priv, const u32 *regs,
+                              u32 *backup, int count)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+               backup[i] = rtl8xxxu_read32(priv, regs[i]);
+}
+
+static void rtl8xxxu_restore_regs(struct rtl8xxxu_priv *priv, const u32 *regs,
+                                 u32 *backup, int count)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+               rtl8xxxu_write32(priv, regs[i], backup[i]);
+}
+
+
+static void rtl8xxxu_path_adda_on(struct rtl8xxxu_priv *priv, const u32 *regs,
+                                 bool path_a_on)
+{
+       u32 path_on;
+       int i;
+
+       path_on = path_a_on ? 0x04db25a4 : 0x0b1b25a4;
+       if (priv->tx_paths == 1) {
+               path_on = 0x0bdb25a0;
+               rtl8xxxu_write32(priv, regs[0], 0x0b1b25a0);
+       } else {
+               rtl8xxxu_write32(priv, regs[0], path_on);
+       }
+
+       for (i = 1 ; i < RTL8XXXU_ADDA_REGS ; i++)
+               rtl8xxxu_write32(priv, regs[i], path_on);
+}
+
+static void rtl8xxxu_mac_calibration(struct rtl8xxxu_priv *priv,
+                                    const u32 *regs, u32 *backup)
+{
+       int i = 0;
+
+       rtl8xxxu_write8(priv, regs[i], 0x3f);
+
+       for (i = 1 ; i < (RTL8XXXU_MAC_REGS - 1); i++)
+               rtl8xxxu_write8(priv, regs[i], (u8)(backup[i] & ~BIT(3)));
+
+       rtl8xxxu_write8(priv, regs[i], (u8)(backup[i] & ~BIT(5)));
+}
+
+static int rtl8xxxu_iqk_path_a(struct rtl8xxxu_priv *priv)
+{
+       u32 reg_eac, reg_e94, reg_e9c, reg_ea4, val32;
+       int result = 0;
+
+       /* path-A IQK setting */
+       rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x10008c1f);
+       rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x10008c1f);
+       rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x82140102);
+
+       val32 = (priv->rf_paths > 1) ? 0x28160202 :
+               /*IS_81xxC_VENDOR_UMC_B_CUT(pHalData->VersionID)?0x28160202: */
+               0x28160502;
+       rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, val32);
+
+       /* path-B IQK setting */
+       if (priv->rf_paths > 1) {
+               rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x10008c22);
+               rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x10008c22);
+               rtl8xxxu_write32(priv, REG_TX_IQK_PI_B, 0x82140102);
+               rtl8xxxu_write32(priv, REG_RX_IQK_PI_B, 0x28160202);
+       }
+
+       /* LO calibration setting */
+       rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x001028d1);
+
+       /* One shot, path A LOK & IQK */
+       rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000);
+       rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000);
+
+       mdelay(1);
+
+       /* Check failed */
+       reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2);
+       reg_e94 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_A);
+       reg_e9c = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_A);
+       reg_ea4 = rtl8xxxu_read32(priv, REG_RX_POWER_BEFORE_IQK_A_2);
+
+       if (!(reg_eac & BIT(28)) &&
+           ((reg_e94 & 0x03ff0000) != 0x01420000) &&
+           ((reg_e9c & 0x03ff0000) != 0x00420000))
+               result |= 0x01;
+       else    /* If TX not OK, ignore RX */
+               goto out;
+
+       /* If TX is OK, check whether RX is OK */
+       if (!(reg_eac & BIT(27)) &&
+           ((reg_ea4 & 0x03ff0000) != 0x01320000) &&
+           ((reg_eac & 0x03ff0000) != 0x00360000))
+               result |= 0x02;
+       else
+               dev_warn(&priv->udev->dev, "%s: Path A RX IQK failed!\n",
+                        __func__);
+out:
+       return result;
+}
+
+static int rtl8xxxu_iqk_path_b(struct rtl8xxxu_priv *priv)
+{
+       u32 reg_eac, reg_eb4, reg_ebc, reg_ec4, reg_ecc;
+       int result = 0;
+
+       /* One shot, path B LOK & IQK */
+       rtl8xxxu_write32(priv, REG_IQK_AGC_CONT, 0x00000002);
+       rtl8xxxu_write32(priv, REG_IQK_AGC_CONT, 0x00000000);
+
+       mdelay(1);
+
+       /* Check failed */
+       reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2);
+       reg_eb4 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_B);
+       reg_ebc = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_B);
+       reg_ec4 = rtl8xxxu_read32(priv, REG_RX_POWER_BEFORE_IQK_B_2);
+       reg_ecc = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_B_2);
+
+       if (!(reg_eac & BIT(31)) &&
+           ((reg_eb4 & 0x03ff0000) != 0x01420000) &&
+           ((reg_ebc & 0x03ff0000) != 0x00420000))
+               result |= 0x01;
+       else
+               goto out;
+
+       if (!(reg_eac & BIT(30)) &&
+           (((reg_ec4 & 0x03ff0000) >> 16) != 0x132) &&
+           (((reg_ecc & 0x03ff0000) >> 16) != 0x36))
+               result |= 0x02;
+       else
+               dev_warn(&priv->udev->dev, "%s: Path B RX IQK failed!\n",
+                        __func__);
+out:
+       return result;
+}
+
+static void rtl8xxxu_phy_iqcalibrate(struct rtl8xxxu_priv *priv,
+                                    int result[][8], int t)
+{
+       struct device *dev = &priv->udev->dev;
+       u32 i, val32;
+       int path_a_ok, path_b_ok;
+       int retry = 2;
+       const u32 adda_regs[RTL8XXXU_ADDA_REGS] = {
+               REG_FPGA0_XCD_SWITCH_CTRL, REG_BLUETOOTH,
+               REG_RX_WAIT_CCA, REG_TX_CCK_RFON,
+               REG_TX_CCK_BBON, REG_TX_OFDM_RFON,
+               REG_TX_OFDM_BBON, REG_TX_TO_RX,
+               REG_TX_TO_TX, REG_RX_CCK,
+               REG_RX_OFDM, REG_RX_WAIT_RIFS,
+               REG_RX_TO_RX, REG_STANDBY,
+               REG_SLEEP, REG_PMPD_ANAEN
+       };
+       const u32 iqk_mac_regs[RTL8XXXU_MAC_REGS] = {
+               REG_TXPAUSE, REG_BEACON_CTRL,
+               REG_BEACON_CTRL_1, REG_GPIO_MUXCFG
+       };
+       const u32 iqk_bb_regs[RTL8XXXU_BB_REGS] = {
+               REG_OFDM0_TRX_PATH_ENABLE, REG_OFDM0_TR_MUX_PAR,
+               REG_FPGA0_XCD_RF_SW_CTRL, REG_CONFIG_ANT_A, REG_CONFIG_ANT_B,
+               REG_FPGA0_XAB_RF_SW_CTRL, REG_FPGA0_XA_RF_INT_OE,
+               REG_FPGA0_XB_RF_INT_OE, REG_FPGA0_RF_MODE
+       };
+
+       /*
+        * Note: IQ calibration must be performed after loading
+        *       PHY_REG.txt , and radio_a, radio_b.txt
+        */
+
+       if (t == 0) {
+               /* Save ADDA parameters, turn Path A ADDA on */
+               rtl8xxxu_save_regs(priv, adda_regs, priv->adda_backup,
+                                  RTL8XXXU_ADDA_REGS);
+               rtl8xxxu_save_mac_regs(priv, iqk_mac_regs, priv->mac_backup);
+               rtl8xxxu_save_regs(priv, iqk_bb_regs,
+                                  priv->bb_backup, RTL8XXXU_BB_REGS);
+       }
+
+       rtl8xxxu_path_adda_on(priv, adda_regs, true);
+
+       if (t == 0) {
+               val32 = rtl8xxxu_read32(priv, REG_FPGA0_XA_HSSI_PARM1);
+               if (val32 & FPGA0_HSSI_PARM1_PI)
+                       priv->pi_enabled = 1;
+       }
+
+       if (!priv->pi_enabled) {
+               /* Switch BB to PI mode to do IQ Calibration. */
+               rtl8xxxu_write32(priv, REG_FPGA0_XA_HSSI_PARM1, 0x01000100);
+               rtl8xxxu_write32(priv, REG_FPGA0_XB_HSSI_PARM1, 0x01000100);
+       }
+
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
+       val32 &= ~FPGA_RF_MODE_CCK;
+       rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
+
+       rtl8xxxu_write32(priv, REG_OFDM0_TRX_PATH_ENABLE, 0x03a05600);
+       rtl8xxxu_write32(priv, REG_OFDM0_TR_MUX_PAR, 0x000800e4);
+       rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_SW_CTRL, 0x22204000);
+
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_XAB_RF_SW_CTRL);
+       val32 |= (FPGA0_RF_PAPE | (FPGA0_RF_PAPE << FPGA0_RF_BD_CTRL_SHIFT));
+       rtl8xxxu_write32(priv, REG_FPGA0_XAB_RF_SW_CTRL, val32);
+
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_XA_RF_INT_OE);
+       val32 &= ~BIT(10);
+       rtl8xxxu_write32(priv, REG_FPGA0_XA_RF_INT_OE, val32);
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_XB_RF_INT_OE);
+       val32 &= ~BIT(10);
+       rtl8xxxu_write32(priv, REG_FPGA0_XB_RF_INT_OE, val32);
+
+       if (priv->tx_paths > 1) {
+               rtl8xxxu_write32(priv, REG_FPGA0_XA_LSSI_PARM, 0x00010000);
+               rtl8xxxu_write32(priv, REG_FPGA0_XB_LSSI_PARM, 0x00010000);
+       }
+
+       /* MAC settings */
+       rtl8xxxu_mac_calibration(priv, iqk_mac_regs, priv->mac_backup);
+
+       /* Page B init */
+       rtl8xxxu_write32(priv, REG_CONFIG_ANT_A, 0x00080000);
+
+       if (priv->tx_paths > 1)
+               rtl8xxxu_write32(priv, REG_CONFIG_ANT_B, 0x00080000);
+
+       /* IQ calibration setting */
+       rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x80800000);
+       rtl8xxxu_write32(priv, REG_TX_IQK, 0x01007c00);
+       rtl8xxxu_write32(priv, REG_RX_IQK, 0x01004800);
+
+       for (i = 0; i < retry; i++) {
+               path_a_ok = rtl8xxxu_iqk_path_a(priv);
+               if (path_a_ok == 0x03) {
+                       val32 = rtl8xxxu_read32(priv,
+                                               REG_TX_POWER_BEFORE_IQK_A);
+                       result[t][0] = (val32 >> 16) & 0x3ff;
+                       val32 = rtl8xxxu_read32(priv,
+                                               REG_TX_POWER_AFTER_IQK_A);
+                       result[t][1] = (val32 >> 16) & 0x3ff;
+                       val32 = rtl8xxxu_read32(priv,
+                                               REG_RX_POWER_BEFORE_IQK_A_2);
+                       result[t][2] = (val32 >> 16) & 0x3ff;
+                       val32 = rtl8xxxu_read32(priv,
+                                               REG_RX_POWER_AFTER_IQK_A_2);
+                       result[t][3] = (val32 >> 16) & 0x3ff;
+                       break;
+               } else if (i == (retry - 1) && path_a_ok == 0x01) {
+                       /* TX IQK OK */
+                       dev_dbg(dev, "%s: Path A IQK Only Tx Success!!\n",
+                               __func__);
+
+                       val32 = rtl8xxxu_read32(priv,
+                                               REG_TX_POWER_BEFORE_IQK_A);
+                       result[t][0] = (val32 >> 16) & 0x3ff;
+                       val32 = rtl8xxxu_read32(priv,
+                                               REG_TX_POWER_AFTER_IQK_A);
+                       result[t][1] = (val32 >> 16) & 0x3ff;
+               }
+       }
+
+       if (!path_a_ok)
+               dev_dbg(dev, "%s: Path A IQK failed!\n", __func__);
+
+       if (priv->tx_paths > 1) {
+               /*
+                * Path A into standby
+                */
+               rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x0);
+               rtl8xxxu_write32(priv, REG_FPGA0_XA_LSSI_PARM, 0x00010000);
+               rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x80800000);
+
+               /* Turn Path B ADDA on */
+               rtl8xxxu_path_adda_on(priv, adda_regs, false);
+
+               for (i = 0; i < retry; i++) {
+                       path_b_ok = rtl8xxxu_iqk_path_b(priv);
+                       if (path_b_ok == 0x03) {
+                               val32 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_B);
+                               result[t][4] = (val32 >> 16) & 0x3ff;
+                               val32 = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_B);
+                               result[t][5] = (val32 >> 16) & 0x3ff;
+                               val32 = rtl8xxxu_read32(priv, REG_RX_POWER_BEFORE_IQK_B_2);
+                               result[t][6] = (val32 >> 16) & 0x3ff;
+                               val32 = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_B_2);
+                               result[t][7] = (val32 >> 16) & 0x3ff;
+                               break;
+                       } else if (i == (retry - 1) && path_b_ok == 0x01) {
+                               /* TX IQK OK */
+                               val32 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_B);
+                               result[t][4] = (val32 >> 16) & 0x3ff;
+                               val32 = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_B);
+                               result[t][5] = (val32 >> 16) & 0x3ff;
+                       }
+               }
+
+               if (!path_b_ok)
+                       dev_dbg(dev, "%s: Path B IQK failed!\n", __func__);
+       }
+
+       /* Back to BB mode, load original value */
+       rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0);
+
+       if (t) {
+               if (!priv->pi_enabled) {
+                       /*
+                        * Switch back BB to SI mode after finishing
+                        * IQ Calibration
+                        */
+                       val32 = 0x01000000;
+                       rtl8xxxu_write32(priv, REG_FPGA0_XA_HSSI_PARM1, val32);
+                       rtl8xxxu_write32(priv, REG_FPGA0_XB_HSSI_PARM1, val32);
+               }
+
+               /* Reload ADDA power saving parameters */
+               rtl8xxxu_restore_regs(priv, adda_regs, priv->adda_backup,
+                                     RTL8XXXU_ADDA_REGS);
+
+               /* Reload MAC parameters */
+               rtl8xxxu_restore_mac_regs(priv, iqk_mac_regs, priv->mac_backup);
+
+               /* Reload BB parameters */
+               rtl8xxxu_restore_regs(priv, iqk_bb_regs,
+                                     priv->bb_backup, RTL8XXXU_BB_REGS);
+
+               /* Restore RX initial gain */
+               rtl8xxxu_write32(priv, REG_FPGA0_XA_LSSI_PARM, 0x00032ed3);
+
+               if (priv->tx_paths > 1) {
+                       rtl8xxxu_write32(priv, REG_FPGA0_XB_LSSI_PARM,
+                                        0x00032ed3);
+               }
+
+               /* Load 0xe30 IQC default value */
+               rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x01008c00);
+               rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x01008c00);
+       }
+}
+
+static void rtl8723a_phy_iq_calibrate(struct rtl8xxxu_priv *priv)
+{
+       struct device *dev = &priv->udev->dev;
+       int result[4][8];       /* last is final result */
+       int i, candidate;
+       bool path_a_ok, path_b_ok;
+       u32 reg_e94, reg_e9c, reg_ea4, reg_eac;
+       u32 reg_eb4, reg_ebc, reg_ec4, reg_ecc;
+       s32 reg_tmp = 0;
+       bool simu;
+
+       memset(result, 0, sizeof(result));
+       candidate = -1;
+
+       path_a_ok = false;
+       path_b_ok = false;
+
+       rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
+
+       for (i = 0; i < 3; i++) {
+               rtl8xxxu_phy_iqcalibrate(priv, result, i);
+
+               if (i == 1) {
+                       simu = rtl8xxxu_simularity_compare(priv, result, 0, 1);
+                       if (simu) {
+                               candidate = 0;
+                               break;
+                       }
+               }
+
+               if (i == 2) {
+                       simu = rtl8xxxu_simularity_compare(priv, result, 0, 2);
+                       if (simu) {
+                               candidate = 0;
+                               break;
+                       }
+
+                       simu = rtl8xxxu_simularity_compare(priv, result, 1, 2);
+                       if (simu) {
+                               candidate = 1;
+                       } else {
+                               for (i = 0; i < 8; i++)
+                                       reg_tmp += result[3][i];
+
+                               if (reg_tmp)
+                                       candidate = 3;
+                               else
+                                       candidate = -1;
+                       }
+               }
+       }
+
+       for (i = 0; i < 4; i++) {
+               reg_e94 = result[i][0];
+               reg_e9c = result[i][1];
+               reg_ea4 = result[i][2];
+               reg_eac = result[i][3];
+               reg_eb4 = result[i][4];
+               reg_ebc = result[i][5];
+               reg_ec4 = result[i][6];
+               reg_ecc = result[i][7];
+       }
+
+       if (candidate >= 0) {
+               reg_e94 = result[candidate][0];
+               priv->rege94 =  reg_e94;
+               reg_e9c = result[candidate][1];
+               priv->rege9c = reg_e9c;
+               reg_ea4 = result[candidate][2];
+               reg_eac = result[candidate][3];
+               reg_eb4 = result[candidate][4];
+               priv->regeb4 = reg_eb4;
+               reg_ebc = result[candidate][5];
+               priv->regebc = reg_ebc;
+               reg_ec4 = result[candidate][6];
+               reg_ecc = result[candidate][7];
+               dev_dbg(dev, "%s: candidate is %x\n", __func__, candidate);
+               dev_dbg(dev,
+                       "%s: e94 =%x e9c=%x ea4=%x eac=%x eb4=%x ebc=%x ec4=%x "
+                       "ecc=%x\n ", __func__, reg_e94, reg_e9c,
+                       reg_ea4, reg_eac, reg_eb4, reg_ebc, reg_ec4, reg_ecc);
+               path_a_ok = true;
+               path_b_ok = true;
+       } else {
+               reg_e94 = reg_eb4 = priv->rege94 = priv->regeb4 = 0x100;
+               reg_e9c = reg_ebc = priv->rege9c = priv->regebc = 0x0;
+       }
+
+       if (reg_e94 && candidate >= 0)
+               rtl8xxxu_fill_iqk_matrix_a(priv, path_a_ok, result,
+                                          candidate, (reg_ea4 == 0));
+
+       if (priv->tx_paths > 1 && reg_eb4)
+               rtl8xxxu_fill_iqk_matrix_b(priv, path_b_ok, result,
+                                          candidate, (reg_ec4 == 0));
+
+       rtl8xxxu_save_regs(priv, rtl8723au_iqk_phy_iq_bb_reg,
+                          priv->bb_recovery_backup, RTL8XXXU_BB_REGS);
+}
+
+static void rtl8723a_phy_lc_calibrate(struct rtl8xxxu_priv *priv)
+{
+       u32 val32;
+       u32 rf_amode, rf_bmode = 0, lstf;
+
+       /* Check continuous TX and Packet TX */
+       lstf = rtl8xxxu_read32(priv, REG_OFDM1_LSTF);
+
+       if (lstf & OFDM_LSTF_MASK) {
+               /* Disable all continuous TX */
+               val32 = lstf & ~OFDM_LSTF_MASK;
+               rtl8xxxu_write32(priv, REG_OFDM1_LSTF, val32);
+
+               /* Read original RF mode Path A */
+               rf_amode = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_AC);
+
+               /* Set RF mode to standby Path A */
+               rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_AC,
+                                    (rf_amode & 0x8ffff) | 0x10000);
+
+               /* Path-B */
+               if (priv->tx_paths > 1) {
+                       rf_bmode = rtl8xxxu_read_rfreg(priv, RF_B,
+                                                      RF6052_REG_AC);
+
+                       rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_AC,
+                                            (rf_bmode & 0x8ffff) | 0x10000);
+               }
+       } else {
+               /*  Deal with Packet TX case */
+               /*  block all queues */
+               rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff);
+       }
+
+       /* Start LC calibration */
+       val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_MODE_AG);
+       val32 |= 0x08000;
+       rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_MODE_AG, val32);
+
+       msleep(100);
+
+       /* Restore original parameters */
+       if (lstf & OFDM_LSTF_MASK) {
+               /* Path-A */
+               rtl8xxxu_write32(priv, REG_OFDM1_LSTF, lstf);
+               rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_AC, rf_amode);
+
+               /* Path-B */
+               if (priv->tx_paths > 1)
+                       rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_AC,
+                                            rf_bmode);
+       } else /*  Deal with Packet TX case */
+               rtl8xxxu_write8(priv, REG_TXPAUSE, 0x00);
+}
+
+static int rtl8xxxu_set_mac(struct rtl8xxxu_priv *priv)
+{
+       int i;
+       u16 reg;
+
+       reg = REG_MACID;
+
+       for (i = 0; i < ETH_ALEN; i++)
+               rtl8xxxu_write8(priv, reg + i, priv->mac_addr[i]);
+
+       return 0;
+}
+
+static int rtl8xxxu_set_bssid(struct rtl8xxxu_priv *priv, const u8 *bssid)
+{
+       int i;
+       u16 reg;
+
+       dev_dbg(&priv->udev->dev, "%s: (%pM)\n", __func__, bssid);
+
+       reg = REG_BSSID;
+
+       for (i = 0; i < ETH_ALEN; i++)
+               rtl8xxxu_write8(priv, reg + i, bssid[i]);
+
+       return 0;
+}
+
+static void
+rtl8xxxu_set_ampdu_factor(struct rtl8xxxu_priv *priv, u8 ampdu_factor)
+{
+       u8 vals[4] = { 0x41, 0xa8, 0x72, 0xb9 };
+       u8 max_agg = 0xf;
+       int i;
+
+       ampdu_factor = 1 << (ampdu_factor + 2);
+       if (ampdu_factor > max_agg)
+               ampdu_factor = max_agg;
+
+       for (i = 0; i < 4; i++) {
+               if ((vals[i] & 0xf0) > (ampdu_factor << 4))
+                       vals[i] = (vals[i] & 0x0f) | (ampdu_factor << 4);
+
+               if ((vals[i] & 0x0f) > ampdu_factor)
+                       vals[i] = (vals[i] & 0xf0) | ampdu_factor;
+
+               rtl8xxxu_write8(priv, REG_AGGLEN_LMT + i, vals[i]);
+       }
+}
+
+static void rtl8xxxu_set_ampdu_min_space(struct rtl8xxxu_priv *priv, u8 density)
+{
+       u8 val8;
+
+       val8 = rtl8xxxu_read8(priv, REG_AMPDU_MIN_SPACE);
+       val8 &= 0xf8;
+       val8 |= density;
+       rtl8xxxu_write8(priv, REG_AMPDU_MIN_SPACE, val8);
+}
+
+static int rtl8xxxu_active_to_emu(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+       int count, ret;
+
+       /* Start of rtl8723AU_card_enable_flow */
+       /* Act to Cardemu sequence*/
+       /* Turn off RF */
+       rtl8xxxu_write8(priv, REG_RF_CTRL, 0);
+
+       /* 0x004E[7] = 0, switch DPDT_SEL_P output from register 0x0065[2] */
+       val8 = rtl8xxxu_read8(priv, REG_LEDCFG2);
+       val8 &= ~LEDCFG2_DPDT_SELECT;
+       rtl8xxxu_write8(priv, REG_LEDCFG2, val8);
+
+       /* 0x0005[1] = 1 turn off MAC by HW state machine*/
+       val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+       val8 |= BIT(1);
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+       for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
+               val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+               if ((val8 & BIT(1)) == 0)
+                       break;
+               udelay(10);
+       }
+
+       if (!count) {
+               dev_warn(&priv->udev->dev, "%s: Disabling MAC timed out\n",
+                        __func__);
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       /* 0x0000[5] = 1 analog Ips to digital, 1:isolation */
+       val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL);
+       val8 |= SYS_ISO_ANALOG_IPS;
+       rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8);
+
+       /* 0x0020[0] = 0 disable LDOA12 MACRO block*/
+       val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL);
+       val8 &= ~LDOA15_ENABLE;
+       rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8);
+
+exit:
+       return ret;
+}
+
+static int rtl8xxxu_active_to_lps(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+       u8 val32;
+       int count, ret;
+
+       rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff);
+
+       /*
+        * Poll - wait for RX packet to complete
+        */
+       for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
+               val32 = rtl8xxxu_read32(priv, 0x5f8);
+               if (!val32)
+                       break;
+               udelay(10);
+       }
+
+       if (!count) {
+               dev_warn(&priv->udev->dev,
+                        "%s: RX poll timed out (0x05f8)\n", __func__);
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       /* Disable CCK and OFDM, clock gated */
+       val8 = rtl8xxxu_read8(priv, REG_SYS_FUNC);
+       val8 &= ~SYS_FUNC_BBRSTB;
+       rtl8xxxu_write8(priv, REG_SYS_FUNC, val8);
+
+       udelay(2);
+
+       /* Reset baseband */
+       val8 = rtl8xxxu_read8(priv, REG_SYS_FUNC);
+       val8 &= ~SYS_FUNC_BB_GLB_RSTN;
+       rtl8xxxu_write8(priv, REG_SYS_FUNC, val8);
+
+       /* Reset MAC TRX */
+       val8 = rtl8xxxu_read8(priv, REG_CR);
+       val8 = CR_HCI_TXDMA_ENABLE | CR_HCI_RXDMA_ENABLE;
+       rtl8xxxu_write8(priv, REG_CR, val8);
+
+       /* Reset MAC TRX */
+       val8 = rtl8xxxu_read8(priv, REG_CR + 1);
+       val8 &= ~BIT(1); /* CR_SECURITY_ENABLE */
+       rtl8xxxu_write8(priv, REG_CR + 1, val8);
+
+       /* Respond TX OK to scheduler */
+       val8 = rtl8xxxu_read8(priv, REG_DUAL_TSF_RST);
+       val8 |= DUAL_TSF_TX_OK;
+       rtl8xxxu_write8(priv, REG_DUAL_TSF_RST, val8);
+
+exit:
+       return ret;
+}
+
+static void rtl8xxxu_disabled_to_emu(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+
+       /* Clear suspend enable and power down enable*/
+       val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+       val8 &= ~(BIT(3) | BIT(7));
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+       /* 0x48[16] = 0 to disable GPIO9 as EXT WAKEUP*/
+       val8 = rtl8xxxu_read8(priv, REG_GPIO_INTM + 2);
+       val8 &= ~BIT(0);
+       rtl8xxxu_write8(priv, REG_GPIO_INTM + 2, val8);
+
+       /* 0x04[12:11] = 11 enable WL suspend*/
+       val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+       val8 &= ~(BIT(3) | BIT(4));
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+}
+
+static int rtl8xxxu_emu_to_active(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+       u32 val32;
+       int count, ret = 0;
+
+       /* 0x20[0] = 1 enable LDOA12 MACRO block for all interface*/
+       val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL);
+       val8 |= LDOA15_ENABLE;
+       rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8);
+
+       /* 0x67[0] = 0 to disable BT_GPS_SEL pins*/
+       val8 = rtl8xxxu_read8(priv, 0x0067);
+       val8 &= ~BIT(4);
+       rtl8xxxu_write8(priv, 0x0067, val8);
+
+       mdelay(1);
+
+       /* 0x00[5] = 0 release analog Ips to digital, 1:isolation */
+       val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL);
+       val8 &= ~SYS_ISO_ANALOG_IPS;
+       rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8);
+
+       /* disable SW LPS 0x04[10]= 0 */
+       val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+       val8 &= ~BIT(2);
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+       /* wait till 0x04[17] = 1 power ready*/
+       for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
+               val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+               if (val32 & BIT(17))
+                       break;
+
+               udelay(10);
+       }
+
+       if (!count) {
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       /* We should be able to optimize the following three entries into one */
+
+       /* release WLON reset 0x04[16]= 1*/
+       val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 2);
+       val8 |= BIT(0);
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 2, val8);
+
+       /* disable HWPDN 0x04[15]= 0*/
+       val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+       val8 &= ~BIT(7);
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+       /* disable WL suspend*/
+       val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+       val8 &= ~(BIT(3) | BIT(4));
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+       /* set, then poll until 0 */
+       val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+       val32 |= APS_FSMCO_MAC_ENABLE;
+       rtl8xxxu_write32(priv, REG_APS_FSMCO, val32);
+
+       for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
+               val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+               if ((val32 & APS_FSMCO_MAC_ENABLE) == 0) {
+                       ret = 0;
+                       break;
+               }
+               udelay(10);
+       }
+
+       if (!count) {
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       /* 0x4C[23] = 0x4E[7] = 1, switch DPDT_SEL_P output from WL BB */
+       /*
+        * Note: Vendor driver actually clears this bit, despite the
+        * documentation claims it's being set!
+        */
+       val8 = rtl8xxxu_read8(priv, REG_LEDCFG2);
+       val8 |= LEDCFG2_DPDT_SELECT;
+       val8 &= ~LEDCFG2_DPDT_SELECT;
+       rtl8xxxu_write8(priv, REG_LEDCFG2, val8);
+
+exit:
+       return ret;
+}
+
+static int rtl8xxxu_emu_to_disabled(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+
+       /* 0x0007[7:0] = 0x20 SOP option to disable BG/MB */
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 3, 0x20);
+
+       /* 0x04[12:11] = 01 enable WL suspend */
+       val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+       val8 &= ~BIT(4);
+       val8 |= BIT(3);
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+       val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+       val8 |= BIT(7);
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+       /* 0x48[16] = 1 to enable GPIO9 as EXT wakeup */
+       val8 = rtl8xxxu_read8(priv, REG_GPIO_INTM + 2);
+       val8 |= BIT(0);
+       rtl8xxxu_write8(priv, REG_GPIO_INTM + 2, val8);
+
+       return 0;
+}
+
+static int rtl8723au_power_on(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+       u16 val16;
+       u32 val32;
+       int ret;
+
+       /*
+        * RSV_CTRL 0x001C[7:0] = 0x00, unlock ISO/CLK/Power control register
+        */
+       rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0);
+
+       rtl8xxxu_disabled_to_emu(priv);
+
+       ret = rtl8xxxu_emu_to_active(priv);
+       if (ret)
+               goto exit;
+
+       /*
+        * 0x0004[19] = 1, reset 8051
+        */
+       val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 2);
+       val8 |= BIT(3);
+       rtl8xxxu_write8(priv, REG_APS_FSMCO + 2, val8);
+
+       /*
+        * Enable MAC DMA/WMAC/SCHEDULE/SEC block
+        * Set CR bit10 to enable 32k calibration.
+        */
+       val16 = rtl8xxxu_read16(priv, REG_CR);
+       val16 |= (CR_HCI_TXDMA_ENABLE | CR_HCI_RXDMA_ENABLE |
+                 CR_TXDMA_ENABLE | CR_RXDMA_ENABLE |
+                 CR_PROTOCOL_ENABLE | CR_SCHEDULE_ENABLE |
+                 CR_MAC_TX_ENABLE | CR_MAC_RX_ENABLE |
+                 CR_SECURITY_ENABLE | CR_CALTIMER_ENABLE);
+       rtl8xxxu_write16(priv, REG_CR, val16);
+
+       /* For EFuse PG */
+       val32 = rtl8xxxu_read32(priv, REG_EFUSE_CTRL);
+       val32 &= ~(BIT(28) | BIT(29) | BIT(30));
+       val32 |= (0x06 << 28);
+       rtl8xxxu_write32(priv, REG_EFUSE_CTRL, val32);
+exit:
+       return ret;
+}
+
+#ifdef CONFIG_RTL8XXXU_UNTESTED
+
+static int rtl8192cu_power_on(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+       u16 val16;
+       u32 val32;
+       int i;
+
+       for (i = 100; i; i--) {
+               val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO);
+               if (val8 & APS_FSMCO_PFM_ALDN)
+                       break;
+       }
+
+       if (!i) {
+               pr_info("%s: Poll failed\n", __func__);
+               return -ENODEV;
+       }
+
+       /*
+        * RSV_CTRL 0x001C[7:0] = 0x00, unlock ISO/CLK/Power control register
+        */
+       rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0);
+       rtl8xxxu_write8(priv, REG_SPS0_CTRL, 0x2b);
+       udelay(100);
+
+       val8 = rtl8xxxu_read8(priv, REG_LDOV12D_CTRL);
+       if (!(val8 & LDOV12D_ENABLE)) {
+               pr_info("%s: Enabling LDOV12D (%02x)\n", __func__, val8);
+               val8 |= LDOV12D_ENABLE;
+               rtl8xxxu_write8(priv, REG_LDOV12D_CTRL, val8);
+
+               udelay(100);
+
+               val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL);
+               val8 &= ~SYS_ISO_MD2PP;
+               rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8);
+       }
+
+       /*
+        * Auto enable WLAN
+        */
+       val16 = rtl8xxxu_read16(priv, REG_APS_FSMCO);
+       val16 |= APS_FSMCO_MAC_ENABLE;
+       rtl8xxxu_write16(priv, REG_APS_FSMCO, val16);
+
+       for (i = 1000; i; i--) {
+               val16 = rtl8xxxu_read16(priv, REG_APS_FSMCO);
+               if (!(val16 & APS_FSMCO_MAC_ENABLE))
+                       break;
+       }
+       if (!i) {
+               pr_info("%s: FSMCO_MAC_ENABLE poll failed\n", __func__);
+               return -EBUSY;
+       }
+
+       /*
+        * Enable radio, GPIO, LED
+        */
+       val16 = APS_FSMCO_HW_SUSPEND | APS_FSMCO_ENABLE_POWERDOWN |
+               APS_FSMCO_PFM_ALDN;
+       rtl8xxxu_write16(priv, REG_APS_FSMCO, val16);
+
+       /*
+        * Release RF digital isolation
+        */
+       val16 = rtl8xxxu_read16(priv, REG_SYS_ISO_CTRL);
+       val16 &= ~SYS_ISO_DIOR;
+       rtl8xxxu_write16(priv, REG_SYS_ISO_CTRL, val16);
+
+       val8 = rtl8xxxu_read8(priv, REG_APSD_CTRL);
+       val8 &= ~APSD_CTRL_OFF;
+       rtl8xxxu_write8(priv, REG_APSD_CTRL, val8);
+       for (i = 200; i; i--) {
+               val8 = rtl8xxxu_read8(priv, REG_APSD_CTRL);
+               if (!(val8 & APSD_CTRL_OFF_STATUS))
+                       break;
+       }
+
+       if (!i) {
+               pr_info("%s: APSD_CTRL poll failed\n", __func__);
+               return -EBUSY;
+       }
+
+       /*
+        * Enable MAC DMA/WMAC/SCHEDULE/SEC block
+        */
+       val16 = rtl8xxxu_read16(priv, REG_CR);
+       val16 |= CR_HCI_TXDMA_ENABLE | CR_HCI_RXDMA_ENABLE |
+               CR_TXDMA_ENABLE | CR_RXDMA_ENABLE | CR_PROTOCOL_ENABLE |
+               CR_SCHEDULE_ENABLE | CR_MAC_TX_ENABLE | CR_MAC_RX_ENABLE;
+       rtl8xxxu_write16(priv, REG_CR, val16);
+
+       /*
+        * Workaround for 8188RU LNA power leakage problem.
+        */
+       if (priv->rtlchip == 0x8188c && priv->hi_pa) {
+               val32 = rtl8xxxu_read32(priv, REG_FPGA0_XCD_RF_PARM);
+               val32 &= ~BIT(1);
+               rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_PARM, val32);
+       }
+       return 0;
+}
+
+#endif
+
+static void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv)
+{
+       u8 val8;
+       u16 val16;
+       u32 val32;
+
+       /*
+        * Workaround for 8188RU LNA power leakage problem.
+        */
+       if (priv->rtlchip == 0x8188c && priv->hi_pa) {
+               val32 = rtl8xxxu_read32(priv, REG_FPGA0_XCD_RF_PARM);
+               val32 |= BIT(1);
+               rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_PARM, val32);
+       }
+
+       rtl8xxxu_active_to_lps(priv);
+
+       /* Turn off RF */
+       rtl8xxxu_write8(priv, REG_RF_CTRL, 0x00);
+
+       /* Reset Firmware if running in RAM */
+       if (rtl8xxxu_read8(priv, REG_MCU_FW_DL) & MCU_FW_RAM_SEL)
+               rtl8xxxu_firmware_self_reset(priv);
+
+       /* Reset MCU */
+       val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+       val16 &= ~SYS_FUNC_CPU_ENABLE;
+       rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
+
+       /* Reset MCU ready status */
+       rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00);
+
+       rtl8xxxu_active_to_emu(priv);
+       rtl8xxxu_emu_to_disabled(priv);
+
+       /* Reset MCU IO Wrapper */
+       val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
+       val8 &= ~BIT(0);
+       rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
+       val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
+       val8 |= BIT(0);
+       rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
+       /* RSV_CTRL 0x1C[7:0] = 0x0e  lock ISO/CLK/Power control register */
+       rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0e);
+}
+
+static void rtl8xxxu_init_bt(struct rtl8xxxu_priv *priv)
+{
+       if (!priv->has_bluetooth)
+               return;
+}
+
+static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       struct device *dev = &priv->udev->dev;
+       struct rtl8xxxu_rfregval *rftable;
+       bool macpower;
+       int ret;
+       u8 val8;
+       u16 val16;
+       u32 val32;
+
+       /* Check if MAC is already powered on */
+       val8 = rtl8xxxu_read8(priv, REG_CR);
+
+       /*
+        * Fix 92DU-VC S3 hang with the reason is that secondary mac is not
+        * initialized. First MAC returns 0xea, second MAC returns 0x00
+        */
+       if (val8 == 0xea)
+               macpower = false;
+       else
+               macpower = true;
+
+       ret = priv->fops->power_on(priv);
+       if (ret < 0) {
+               dev_warn(dev, "%s: Failed power on\n", __func__);
+               goto exit;
+       }
+
+       dev_dbg(dev, "%s: macpower %i\n", __func__, macpower);
+       if (!macpower) {
+               ret = rtl8xxxu_init_llt_table(priv, TX_TOTAL_PAGE_NUM);
+               if (ret) {
+                       dev_warn(dev, "%s: LLT table init failed\n", __func__);
+                       goto exit;
+               }
+       }
+
+       ret = rtl8xxxu_download_firmware(priv);
+       dev_dbg(dev, "%s: download_fiwmare %i\n", __func__, ret);
+       if (ret)
+               goto exit;
+       ret = rtl8xxxu_start_firmware(priv);
+       dev_dbg(dev, "%s: start_fiwmare %i\n", __func__, ret);
+       if (ret)
+               goto exit;
+
+       ret = rtl8xxxu_init_mac(priv, rtl8723a_mac_init_table);
+       dev_dbg(dev, "%s: init_mac %i\n", __func__, ret);
+       if (ret)
+               goto exit;
+
+       ret = rtl8xxxu_init_phy_bb(priv);
+       dev_dbg(dev, "%s: init_phy_bb %i\n", __func__, ret);
+       if (ret)
+               goto exit;
+
+       switch(priv->rtlchip) {
+       case 0x8723a:
+               rftable = rtl8723au_radioa_1t_init_table;
+               ret = rtl8xxxu_init_phy_rf(priv, rftable, RF_A);
+               break;
+       case 0x8188c:
+               if (priv->hi_pa)
+                       rftable = rtl8188ru_radioa_1t_highpa_table;
+               else
+                       rftable = rtl8192cu_radioa_1t_init_table;
+               ret = rtl8xxxu_init_phy_rf(priv, rftable, RF_A);
+               break;
+       case 0x8191c:
+               rftable = rtl8192cu_radioa_1t_init_table;
+               ret = rtl8xxxu_init_phy_rf(priv, rftable, RF_A);
+               break;
+       case 0x8192c:
+               rftable = rtl8192cu_radioa_2t_init_table;
+               ret = rtl8xxxu_init_phy_rf(priv, rftable, RF_A);
+               if (ret)
+                       break;
+               rftable = rtl8192cu_radiob_2t_init_table;
+               ret = rtl8xxxu_init_phy_rf(priv, rftable, RF_B);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       if (ret)
+               goto exit;
+
+       /* Reduce 80M spur */
+       rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, 0x0381808d);
+       rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83);
+       rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff82);
+       rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83);
+
+       /* RFSW Control - clear bit 14 ?? */
+       rtl8xxxu_write32(priv, REG_FPGA0_TX_INFO, 0x00000003);
+       /* 0x07000760 */
+       val32 = FPGA0_RF_TRSW | FPGA0_RF_TRSWB | FPGA0_RF_ANTSW |
+               FPGA0_RF_ANTSWB | FPGA0_RF_PAPE |
+               ((FPGA0_RF_ANTSW | FPGA0_RF_ANTSWB | FPGA0_RF_PAPE) <<
+                FPGA0_RF_BD_CTRL_SHIFT);
+       rtl8xxxu_write32(priv, REG_FPGA0_XAB_RF_SW_CTRL, val32);
+       /* 0x860[6:5]= 00 - why? - this sets antenna B */
+       rtl8xxxu_write32(priv, REG_FPGA0_XA_RF_INT_OE, 0x66F60210);
+
+       priv->rf_mode_ag[0] = rtl8xxxu_read_rfreg(priv, RF_A,
+                                                 RF6052_REG_MODE_AG);
+
+       dev_dbg(dev, "%s: macpower %i\n", __func__, macpower);
+       if (!macpower) {
+               if (priv->ep_tx_normal_queue)
+                       val8 = TX_PAGE_NUM_NORM_PQ;
+               else
+                       val8 = 0;
+
+               rtl8xxxu_write8(priv, REG_RQPN_NPQ, val8);
+
+               val32 = (TX_PAGE_NUM_PUBQ << RQPN_NORM_PQ_SHIFT) | RQPN_LOAD;
+
+               if (priv->ep_tx_high_queue)
+                       val32 |= (TX_PAGE_NUM_HI_PQ << RQPN_HI_PQ_SHIFT);
+               if (priv->ep_tx_low_queue)
+                       val32 |= (TX_PAGE_NUM_LO_PQ << RQPN_LO_PQ_SHIFT);
+
+               rtl8xxxu_write32(priv, REG_RQPN, val32);
+
+               /*
+                * Set TX buffer boundary
+                */
+               val8 = TX_TOTAL_PAGE_NUM + 1;
+               rtl8xxxu_write8(priv, REG_TXPKTBUF_BCNQ_BDNY, val8);
+               rtl8xxxu_write8(priv, REG_TXPKTBUF_MGQ_BDNY, val8);
+               rtl8xxxu_write8(priv, REG_TXPKTBUF_WMAC_LBK_BF_HD, val8);
+               rtl8xxxu_write8(priv, REG_TRXFF_BNDY, val8);
+               rtl8xxxu_write8(priv, REG_TDECTRL + 1, val8);
+       }
+
+       ret = rtl8xxxu_init_queue_priority(priv);
+       dev_dbg(dev, "%s: init_queue_priority %i\n", __func__, ret);
+       if (ret)
+               goto exit;
+
+       /*
+        * Set RX page boundary
+        */
+       rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, 0x27ff);
+       /*
+        * Transfer page size is always 128
+        */
+       val8 = (PBP_PAGE_SIZE_128 << PBP_PAGE_SIZE_RX_SHIFT) |
+               (PBP_PAGE_SIZE_128 << PBP_PAGE_SIZE_TX_SHIFT);
+       rtl8xxxu_write8(priv, REG_PBP, val8);
+
+       /*
+        * Unit in 8 bytes, not obvious what it is used for
+        */
+       rtl8xxxu_write8(priv, REG_RX_DRVINFO_SZ, 4);
+
+       /*
+        * Enable all interrupts - not obvious USB needs to do this
+        */
+       rtl8xxxu_write32(priv, REG_HISR, 0xffffffff);
+       rtl8xxxu_write32(priv, REG_HIMR, 0xffffffff);
+
+       rtl8xxxu_set_mac(priv);
+       rtl8xxxu_set_linktype(priv, NL80211_IFTYPE_STATION);
+
+       /*
+        * Configure initial WMAC settings
+        */
+       val32 = RCR_ACCEPT_PHYS_MATCH | RCR_ACCEPT_MCAST | RCR_ACCEPT_BCAST |
+               /* RCR_CHECK_BSSID_MATCH | RCR_CHECK_BSSID_BEACON | */
+               RCR_ACCEPT_MGMT_FRAME | RCR_HTC_LOC_CTRL |
+               RCR_APPEND_PHYSTAT | RCR_APPEND_ICV | RCR_APPEND_MIC;
+       rtl8xxxu_write32(priv, REG_RCR, val32);
+
+       /*
+        * Accept all multicast
+        */
+       rtl8xxxu_write32(priv, REG_MAR, 0xffffffff);
+       rtl8xxxu_write32(priv, REG_MAR + 4, 0xffffffff);
+
+       /*
+        * Init adaptive controls
+        */
+       val32 = rtl8xxxu_read32(priv, REG_RESPONSE_RATE_SET);
+       val32 &= ~RESPONSE_RATE_BITMAP_ALL;
+       val32 |= RESPONSE_RATE_RRSR_CCK_ONLY_1M;
+       rtl8xxxu_write32(priv, REG_RESPONSE_RATE_SET, val32);
+
+       /* CCK = 0x0a, OFDM = 0x10 */
+       rtl8xxxu_set_spec_sifs(priv, 0x10, 0x10);
+       rtl8xxxu_set_retry(priv, 0x30, 0x30);
+       rtl8xxxu_set_spec_sifs(priv, 0x0a, 0x10);
+
+       /*
+        * Init EDCA
+        */
+       rtl8xxxu_write16(priv, REG_MAC_SPEC_SIFS, 0x100a);
+
+       /* Set CCK SIFS */
+       rtl8xxxu_write16(priv, REG_SIFS_CCK, 0x100a);
+
+       /* Set OFDM SIFS */
+       rtl8xxxu_write16(priv, REG_SIFS_OFDM, 0x100a);
+
+       /* TXOP */
+       rtl8xxxu_write32(priv, REG_EDCA_BE_PARAM, 0x005ea42b);
+       rtl8xxxu_write32(priv, REG_EDCA_BK_PARAM, 0x0000a44f);
+       rtl8xxxu_write32(priv, REG_EDCA_VI_PARAM, 0x005ea324);
+       rtl8xxxu_write32(priv, REG_EDCA_VO_PARAM, 0x002fa226);
+
+       /* Set data auto rate fallback retry count */
+       rtl8xxxu_write32(priv, REG_DARFRC, 0x00000000);
+       rtl8xxxu_write32(priv, REG_DARFRC + 4, 0x10080404);
+       rtl8xxxu_write32(priv, REG_RARFRC, 0x04030201);
+       rtl8xxxu_write32(priv, REG_RARFRC + 4, 0x08070605);
+
+       val8 = rtl8xxxu_read8(priv, REG_FWHW_TXQ_CTRL);
+       val8 |= FWHW_TXQ_CTRL_AMPDU_RETRY;
+       rtl8xxxu_write8(priv, REG_FWHW_TXQ_CTRL, val8);
+
+       /*  Set ACK timeout */
+       rtl8xxxu_write8(priv, REG_ACKTO, 0x40);
+
+       /*
+        * Initialize beacon parameters
+        */
+       val16 = BEACON_DISABLE_TSF_UPDATE | (BEACON_DISABLE_TSF_UPDATE << 8);
+       rtl8xxxu_write16(priv, REG_BEACON_CTRL, val16);
+       rtl8xxxu_write16(priv, REG_TBTT_PROHIBIT, 0x6404);
+       rtl8xxxu_write8(priv, REG_DRIVER_EARLY_INT, DRIVER_EARLY_INT_TIME);
+       rtl8xxxu_write8(priv, REG_BEACON_DMA_TIME, BEACON_DMA_ATIME_INT_TIME);
+       rtl8xxxu_write16(priv, REG_BEACON_TCFG, 0x660F);
+
+       /*
+        * Enable CCK and OFDM block
+        */
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
+       val32 |= (FPGA_RF_MODE_CCK | FPGA_RF_MODE_OFDM);
+       rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
+
+       /*
+        * Invalidate all CAM entries - bit 30 is undocumented
+        */
+       rtl8xxxu_write32(priv, REG_CAM_CMD, CAM_CMD_POLLING | BIT(30));
+
+       /*
+        * Start out with default power levels for channel 6, 20MHz
+        */
+       rtl8723a_set_tx_power(priv, 1, false);
+
+       /* Let the 8051 take control of antenna setting */
+       val8 = rtl8xxxu_read8(priv, REG_LEDCFG2);
+       val8 |= LEDCFG2_DPDT_SELECT;
+       rtl8xxxu_write8(priv, REG_LEDCFG2, val8);
+
+       rtl8xxxu_write8(priv, REG_HWSEQ_CTRL, 0xff);
+
+       /* Disable BAR - not sure if this has any effect on USB */
+       rtl8xxxu_write32(priv, REG_BAR_MODE_CTRL, 0x0201ffff);
+
+       rtl8xxxu_write16(priv, REG_FAST_EDCA_CTRL, 0);
+
+       /*
+        * Not sure if we should get into this at all
+        */
+       if (priv->iqk_initialized) {
+               rtl8xxxu_restore_regs(priv, rtl8723au_iqk_phy_iq_bb_reg,
+                                     priv->bb_recovery_backup,
+                                     RTL8XXXU_BB_REGS);
+       } else {
+               rtl8723a_phy_iq_calibrate(priv);
+               priv->iqk_initialized = true;
+       }
+
+       /*
+        * This should enable thermal meter
+        */
+       rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_T_METER, 0x60);
+
+       rtl8723a_phy_lc_calibrate(priv);
+
+       /* fix USB interface interference issue */
+       rtl8xxxu_write8(priv, 0xfe40, 0xe0);
+       rtl8xxxu_write8(priv, 0xfe41, 0x8d);
+       rtl8xxxu_write8(priv, 0xfe42, 0x80);
+       rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, 0xfd0320);
+
+       /* Solve too many protocol error on USB bus */
+       /* Can't do this for 8188/8192 UMC A cut parts */
+       rtl8xxxu_write8(priv, 0xfe40, 0xe6);
+       rtl8xxxu_write8(priv, 0xfe41, 0x94);
+       rtl8xxxu_write8(priv, 0xfe42, 0x80);
+
+       rtl8xxxu_write8(priv, 0xfe40, 0xe0);
+       rtl8xxxu_write8(priv, 0xfe41, 0x19);
+       rtl8xxxu_write8(priv, 0xfe42, 0x80);
+
+       rtl8xxxu_write8(priv, 0xfe40, 0xe5);
+       rtl8xxxu_write8(priv, 0xfe41, 0x91);
+       rtl8xxxu_write8(priv, 0xfe42, 0x80);
+
+       rtl8xxxu_write8(priv, 0xfe40, 0xe2);
+       rtl8xxxu_write8(priv, 0xfe41, 0x81);
+       rtl8xxxu_write8(priv, 0xfe42, 0x80);
+
+       /* Init BT hw config. */
+       rtl8xxxu_init_bt(priv);
+
+       /*
+        * Not sure if we really need to save these parameters, but the
+        * vendor driver does
+        */
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_XA_HSSI_PARM2);
+       if (val32 & FPGA0_HSSI_PARM2_CCK_HIGH_PWR)
+               priv->path_a_hi_power = 1;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_TRX_PATH_ENABLE);
+       priv->path_a_rf_paths = val32 & OFDM_RF_PATH_RX_MASK;
+
+       val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_AGC_CORE1);
+       priv->path_a_ig_value = val32 & OFDM0_X_AGC_CORE1_IGI_MASK;
+
+       /* Set NAV_UPPER to 30000us */
+       val8 = ((30000 + NAV_UPPER_UNIT - 1) / NAV_UPPER_UNIT);
+       rtl8xxxu_write8(priv, REG_NAV_UPPER, val8);
+
+       /*
+        * 2011/03/09 MH debug only, UMC-B cut pass 2500 S5 test,
+        * but we need to fin root cause.
+        */
+       val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
+       if ((val32 & 0xff000000) != 0x83000000) {
+               val32 |= FPGA_RF_MODE_CCK;
+               rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
+       }
+
+       val32 = rtl8xxxu_read32(priv, REG_FWHW_TXQ_CTRL);
+       val32 |= FWHW_TXQ_CTRL_XMIT_MGMT_ACK;
+       /* ack for xmit mgmt frames. */
+       rtl8xxxu_write32(priv, REG_FWHW_TXQ_CTRL, val32);
+
+exit:
+       return ret;
+}
+
+static void rtl8xxxu_disable_device(struct ieee80211_hw *hw)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+
+       rtl8xxxu_power_off(priv);
+}
+
+static void rtl8xxxu_cam_write(struct rtl8xxxu_priv *priv,
+                              struct ieee80211_key_conf *key, const u8 *mac)
+{
+       u32 cmd, val32, addr, ctrl;
+       int j, i, tmp_debug;
+
+       tmp_debug = rtl8xxxu_debug;
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_KEY)
+               rtl8xxxu_debug |= RTL8XXXU_DEBUG_REG_WRITE;
+
+       /*
+        * This is a bit of a hack - the lower bits of the cipher
+        * suite selector happens to match the cipher index in the CAM
+        */
+       addr = key->keyidx << CAM_CMD_KEY_SHIFT;
+       ctrl = (key->cipher & 0x0f) << 2 | key->keyidx | CAM_WRITE_VALID;
+
+       for (j = 5; j >= 0; j--) {
+               switch (j) {
+               case 0:
+                       val32 = ctrl | (mac[0] << 16) | (mac[1] << 24);
+                       break;
+               case 1:
+                       val32 = mac[2] | (mac[3] << 8) |
+                               (mac[4] << 16) | (mac[5] << 24);
+                       break;
+               default:
+                       i = (j - 2) << 2;
+                       val32 = key->key[i] | (key->key[i + 1] << 8) |
+                               key->key[i + 2] << 16 | key->key[i + 3] << 24;
+                       break;
+               }
+
+               rtl8xxxu_write32(priv, REG_CAM_WRITE, val32);
+               cmd = CAM_CMD_POLLING | CAM_CMD_WRITE | (addr + j);
+               rtl8xxxu_write32(priv, REG_CAM_CMD, cmd);
+               udelay(100);
+       }
+
+       rtl8xxxu_debug = tmp_debug;
+}
+
+static void rtl8xxxu_sw_scan_start(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif, const u8* mac)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       u8 val8;
+
+       val8 = rtl8xxxu_read8(priv, REG_BEACON_CTRL);
+       val8 |= BEACON_DISABLE_TSF_UPDATE;
+       rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8);
+}
+
+static void rtl8xxxu_sw_scan_complete(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       u8 val8;
+
+       val8 = rtl8xxxu_read8(priv, REG_BEACON_CTRL);
+       val8 &= ~BEACON_DISABLE_TSF_UPDATE;
+       rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8);
+}
+
+static void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv,
+                                     u32 ramask, int sgi)
+{
+       struct h2c_cmd h2c;
+
+       h2c.ramask.cmd = H2C_SET_RATE_MASK;
+       h2c.ramask.mask_lo = cpu_to_le16(ramask & 0xffff);
+       h2c.ramask.mask_hi = cpu_to_le16(ramask >> 16);
+
+       h2c.ramask.arg = 0x80;
+       if (sgi)
+               h2c.ramask.arg |= 0x20;
+
+       dev_dbg(&priv->udev->dev, "%s: rate mask %08x, arg %02x\n", __func__,
+               ramask, h2c.ramask.arg);
+       rtl8723a_h2c_cmd(priv, &h2c);
+}
+
+static void rtl8xxxu_set_basic_rates(struct rtl8xxxu_priv *priv, u32 rate_cfg)
+{
+       u32 val32;
+       u8 rate_idx = 0;
+
+       rate_cfg &= RESPONSE_RATE_BITMAP_ALL;
+
+       val32 = rtl8xxxu_read32(priv, REG_RESPONSE_RATE_SET);
+       val32 &= ~RESPONSE_RATE_BITMAP_ALL;
+       val32 |= rate_cfg;
+       rtl8xxxu_write32(priv, REG_RESPONSE_RATE_SET, val32);
+
+       dev_dbg(&priv->udev->dev, "%s: rates %08x\n", __func__, rate_cfg);
+
+       while (rate_cfg) {
+               rate_cfg = (rate_cfg >> 1);
+               rate_idx++;
+       }
+       rtl8xxxu_write8(priv, REG_INIRTS_RATE_SEL, rate_idx);
+}
+
+static void
+rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                         struct ieee80211_bss_conf *bss_conf, u32 changed)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       struct device *dev = &priv->udev->dev;
+       struct ieee80211_sta *sta;
+       u32 val32;
+       u8 val8;
+
+       if (changed & BSS_CHANGED_ASSOC) {
+               struct h2c_cmd h2c;
+
+               dev_dbg(dev, "Changed ASSOC: %i!\n", bss_conf->assoc);
+
+               memset(&h2c, 0, sizeof(struct h2c_cmd));
+               rtl8xxxu_set_linktype(priv, vif->type);
+
+               if (bss_conf->assoc) {
+                       u32 ramask;
+                       int sgi = 0;
+
+                       rcu_read_lock();
+                       sta = ieee80211_find_sta(vif, bss_conf->bssid);
+                       if (!sta) {
+                               dev_info(dev, "%s: ASSOC no sta found\n",
+                                        __func__);
+                               rcu_read_unlock();
+                               goto error;
+                       }
+
+                       if (sta->ht_cap.ht_supported)
+                               dev_info(dev, "%s: HT supported\n", __func__);
+                       if (sta->vht_cap.vht_supported)
+                               dev_info(dev, "%s: VHT supported\n", __func__);
+
+                       /* TODO: Set bits 28-31 for rate adaptive id */
+                       ramask = (sta->supp_rates[0] & 0xfff) |
+                               sta->ht_cap.mcs.rx_mask[0] << 12 |
+                               sta->ht_cap.mcs.rx_mask[1] << 20;
+                       if (sta->ht_cap.cap &
+                           (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20))
+                               sgi = 1;
+                       rcu_read_unlock();
+
+                       rtl8xxxu_update_rate_mask(priv, ramask, sgi);
+
+                       val32 = rtl8xxxu_read32(priv, REG_RCR);
+                       val32 |= RCR_CHECK_BSSID_MATCH | RCR_CHECK_BSSID_BEACON;
+                       rtl8xxxu_write32(priv, REG_RCR, val32);
+
+                       /* Enable RX of data frames */
+                       rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0xffff);
+
+                       rtl8xxxu_write8(priv, REG_BCN_MAX_ERR, 0xff);
+
+                       rtl8723a_stop_tx_beacon(priv);
+
+                       /* joinbss sequence */
+                       rtl8xxxu_write16(priv, REG_BCN_PSR_RPT,
+                                        0xc000 | bss_conf->aid);
+
+                       h2c.joinbss.data = H2C_JOIN_BSS_CONNECT;
+               } else {
+                       val32 = rtl8xxxu_read32(priv, REG_RCR);
+                       val32 &= ~(RCR_CHECK_BSSID_MATCH |
+                                  RCR_CHECK_BSSID_BEACON);
+                       rtl8xxxu_write32(priv, REG_RCR, val32);
+
+                       val8 = rtl8xxxu_read8(priv, REG_BEACON_CTRL);
+                       val8 |= BEACON_DISABLE_TSF_UPDATE;
+                       rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8);
+
+                       /* Disable RX of data frames */
+                       rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0x0000);
+                       h2c.joinbss.data = H2C_JOIN_BSS_DISCONNECT;
+               }
+               h2c.joinbss.cmd = H2C_JOIN_BSS_REPORT;
+               rtl8723a_h2c_cmd(priv, &h2c);
+       }
+
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               dev_dbg(dev, "Changed ERP_PREAMBLE: Use short preamble %i\n",
+                       bss_conf->use_short_preamble);
+               val32 = rtl8xxxu_read32(priv, REG_RESPONSE_RATE_SET);
+               if (bss_conf->use_short_preamble)
+                       val32 |= RSR_ACK_SHORT_PREAMBLE;
+               else
+                       val32 &= ~RSR_ACK_SHORT_PREAMBLE;
+               rtl8xxxu_write32(priv, REG_RESPONSE_RATE_SET, val32);
+       }
+
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               dev_dbg(dev, "Changed ERP_SLOT: short_slot_time %i\n",
+                       bss_conf->use_short_slot);
+
+               if (bss_conf->use_short_slot)
+                       val8 = 9;
+               else
+                       val8 = 20;
+               rtl8xxxu_write8(priv, REG_SLOT, val8);
+       }
+
+       if (changed & BSS_CHANGED_BSSID) {
+               dev_dbg(dev, "Changed BSSID!\n");
+               rtl8xxxu_set_bssid(priv, bss_conf->bssid);
+       }
+
+       if (changed & BSS_CHANGED_BASIC_RATES) {
+               dev_dbg(dev, "Changed BASIC_RATES!\n");
+               rtl8xxxu_set_basic_rates(priv, bss_conf->basic_rates);
+       }
+error:
+       return;
+}
+
+static u32 rtl8xxxu_80211_to_rtl_queue(u32 queue)
+{
+       u32 rtlqueue;
+
+       switch (queue) {
+       case IEEE80211_AC_VO:
+               rtlqueue = TXDESC_QUEUE_VO;
+               break;
+       case IEEE80211_AC_VI:
+               rtlqueue = TXDESC_QUEUE_VI;
+               break;
+       case IEEE80211_AC_BE:
+               rtlqueue = TXDESC_QUEUE_BE;
+               break;
+       case IEEE80211_AC_BK:
+               rtlqueue = TXDESC_QUEUE_BK;
+               break;
+       default:
+               rtlqueue = TXDESC_QUEUE_BE;
+       }
+
+       return rtlqueue;
+}
+
+static u32 rtl8xxxu_queue_select(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       u32 queue;
+
+       if (ieee80211_is_mgmt(hdr->frame_control))
+               queue = TXDESC_QUEUE_MGNT;
+       else
+               queue = rtl8xxxu_80211_to_rtl_queue(skb_get_queue_mapping(skb));
+
+       return queue;
+}
+
+static void rtl8xxxu_calc_tx_desc_csum(struct rtl8xxxu_tx_desc *tx_desc)
+{
+       __le16 *ptr = (__le16 *)tx_desc;
+       u16 csum = 0;
+       int i;
+
+       /*
+        * Clear csum field before calculation, as the csum field is
+        * in the middle of the struct.
+        */
+       tx_desc->csum = cpu_to_le16(0);
+
+       for (i = 0; i < (sizeof(struct rtl8xxxu_tx_desc) / sizeof(u16)); i++)
+               csum = csum ^ le16_to_cpu(ptr[i]);
+
+       tx_desc->csum |= cpu_to_le16(csum);
+}
+
+static void rtl8xxxu_free_tx_resources(struct rtl8xxxu_priv *priv)
+{
+       struct rtl8xxxu_tx_urb *tx_urb, *tmp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->tx_urb_lock, flags);
+       list_for_each_entry_safe(tx_urb, tmp, &priv->tx_urb_free_list, list) {
+               list_del(&tx_urb->list);
+               priv->tx_urb_free_count--;
+               usb_free_urb(&tx_urb->urb);
+       }
+       spin_unlock_irqrestore(&priv->tx_urb_lock, flags);
+}
+
+static struct rtl8xxxu_tx_urb *
+rtl8xxxu_alloc_tx_urb(struct rtl8xxxu_priv *priv)
+{
+       struct rtl8xxxu_tx_urb *tx_urb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->tx_urb_lock, flags);
+       tx_urb = list_first_entry_or_null(&priv->tx_urb_free_list,
+                                         struct rtl8xxxu_tx_urb, list);
+       if (tx_urb) {
+               list_del(&tx_urb->list);
+               priv->tx_urb_free_count--;
+               if (priv->tx_urb_free_count < RTL8XXXU_TX_URB_LOW_WATER &&
+                   !priv->tx_stopped) {
+                       priv->tx_stopped = true;
+                       ieee80211_stop_queues(priv->hw);
+               }
+       }
+
+       spin_unlock_irqrestore(&priv->tx_urb_lock, flags);
+
+       return tx_urb;
+}
+
+static void rtl8xxxu_free_tx_urb(struct rtl8xxxu_priv *priv,
+                                struct rtl8xxxu_tx_urb *tx_urb)
+{
+       unsigned long flags;
+
+       INIT_LIST_HEAD(&tx_urb->list);
+
+       spin_lock_irqsave(&priv->tx_urb_lock, flags);
+
+       list_add(&tx_urb->list, &priv->tx_urb_free_list);
+       priv->tx_urb_free_count++;
+       if (priv->tx_urb_free_count > RTL8XXXU_TX_URB_HIGH_WATER &&
+           priv->tx_stopped) {
+               priv->tx_stopped = false;
+               ieee80211_wake_queues(priv->hw);
+       }
+
+       spin_unlock_irqrestore(&priv->tx_urb_lock, flags);
+}
+
+static void rtl8xxxu_tx_complete(struct urb *urb)
+{
+       struct sk_buff *skb = (struct sk_buff *)urb->context;
+       struct ieee80211_tx_info *tx_info;
+       struct ieee80211_hw *hw;
+       struct rtl8xxxu_tx_urb *tx_urb =
+               container_of(urb, struct rtl8xxxu_tx_urb, urb);
+
+       tx_info = IEEE80211_SKB_CB(skb);
+       hw = tx_info->rate_driver_data[0];
+
+       skb_pull(skb, sizeof(struct rtl8xxxu_tx_desc));
+
+       ieee80211_tx_info_clear_status(tx_info);
+       tx_info->status.rates[0].idx = -1;
+       tx_info->status.rates[0].count = 0;
+
+       if (!urb->status)
+               tx_info->flags |= IEEE80211_TX_STAT_ACK;
+
+       ieee80211_tx_status_irqsafe(hw, skb);
+
+       rtl8xxxu_free_tx_urb(hw->priv, tx_urb);
+}
+
+static void rtl8xxxu_dump_action(struct device *dev,
+                                struct ieee80211_hdr *hdr)
+{
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)hdr;
+       u16 cap, timeout;
+
+       if (!(rtl8xxxu_debug & RTL8XXXU_DEBUG_ACTION))
+               return;
+
+       switch (mgmt->u.action.u.addba_resp.action_code) {
+       case WLAN_ACTION_ADDBA_RESP:
+               cap = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
+               timeout = le16_to_cpu(mgmt->u.action.u.addba_resp.timeout);
+               dev_info(dev, "WLAN_ACTION_ADDBA_RESP: "
+                        "timeout %i, tid %02x, buf_size %02x, policy %02x, "
+                        "status %02x\n",
+                        timeout,
+                        (cap & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2,
+                        (cap & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6,
+                        (cap >> 1) & 0x1,
+                        le16_to_cpu(mgmt->u.action.u.addba_resp.status));
+               break;
+       case WLAN_ACTION_ADDBA_REQ:
+               cap = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
+               timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
+               dev_info(dev, "WLAN_ACTION_ADDBA_REQ: "
+                        "timeout %i, tid %02x, buf_size %02x, policy %02x\n",
+                        timeout,
+                        (cap & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2,
+                        (cap & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6,
+                        (cap >> 1) & 0x1);
+               break;
+       default:
+               dev_info(dev, "action frame %02x\n",
+                        mgmt->u.action.u.addba_resp.action_code);
+               break;
+       }
+}
+
+static void rtl8xxxu_tx(struct ieee80211_hw *hw,
+                       struct ieee80211_tx_control *control,
+                       struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info);
+       struct rtl8xxxu_priv *priv = hw->priv;
+       struct rtl8xxxu_tx_desc *tx_desc;
+       struct rtl8xxxu_tx_urb *tx_urb;
+       struct ieee80211_sta *sta = NULL;
+       struct ieee80211_vif *vif = tx_info->control.vif;
+       struct device *dev = &priv->udev->dev;
+       u32 queue, rate;
+       u16 pktlen = skb->len;
+       u16 seq_number;
+       u16 rate_flag = tx_info->control.rates[0].flags;
+       int ret;
+
+       if (skb_headroom(skb) < sizeof(struct rtl8xxxu_tx_desc)) {
+               dev_warn(dev,
+                        "%s: Not enough headroom (%i) for tx descriptor\n",
+                        __func__, skb_headroom(skb));
+               goto error;
+       }
+
+       if (unlikely(skb->len > (65535 - sizeof(struct rtl8xxxu_tx_desc)))) {
+               dev_warn(dev, "%s: Trying to send over-sized skb (%i)\n",
+                        __func__, skb->len);
+               goto error;
+       }
+
+       tx_urb = rtl8xxxu_alloc_tx_urb(priv);
+       if (!tx_urb) {
+               dev_warn(dev, "%s: Unable to allocate tx urb\n", __func__);
+               goto error;
+       }
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_TX)
+               dev_info(dev, "%s: TX rate: %d (%d), pkt size %d\n",
+                        __func__, tx_rate->bitrate, tx_rate->hw_value, pktlen);
+
+       if (ieee80211_is_action(hdr->frame_control))
+               rtl8xxxu_dump_action(dev, hdr);
+
+       tx_info->rate_driver_data[0] = hw;
+
+       if (control && control->sta)
+               sta = control->sta;
+
+       tx_desc = (struct rtl8xxxu_tx_desc *)
+               skb_push(skb, sizeof(struct rtl8xxxu_tx_desc));
+
+       memset(tx_desc, 0, sizeof(struct rtl8xxxu_tx_desc));
+       tx_desc->pkt_size = cpu_to_le16(pktlen);
+       tx_desc->pkt_offset = sizeof(struct rtl8xxxu_tx_desc);
+
+       tx_desc->txdw0 =
+               TXDESC_OWN | TXDESC_FIRST_SEGMENT | TXDESC_LAST_SEGMENT;
+       if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) ||
+           is_broadcast_ether_addr(ieee80211_get_DA(hdr)))
+               tx_desc->txdw0 |= TXDESC_BROADMULTICAST;
+
+       queue = rtl8xxxu_queue_select(hw, skb);
+       tx_desc->txdw1 = cpu_to_le32(queue << TXDESC_QUEUE_SHIFT);
+
+       if (tx_info->control.hw_key) {
+               switch (tx_info->control.hw_key->cipher) {
+               case WLAN_CIPHER_SUITE_WEP40:
+               case WLAN_CIPHER_SUITE_WEP104:
+               case WLAN_CIPHER_SUITE_TKIP:
+                       tx_desc->txdw1 |= cpu_to_le32(TXDESC_SEC_RC4);
+                       break;
+               case WLAN_CIPHER_SUITE_CCMP:
+                       tx_desc->txdw1 |= cpu_to_le32(TXDESC_SEC_AES);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
+       tx_desc->txdw3 = cpu_to_le32((u32)seq_number << TXDESC_SEQ_SHIFT);
+
+       if (rate_flag & IEEE80211_TX_RC_MCS)
+               rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0;
+       else
+               rate = tx_rate->hw_value;
+       tx_desc->txdw5 = cpu_to_le32(rate);
+
+       if (ieee80211_is_data(hdr->frame_control))
+               tx_desc->txdw5 |= cpu_to_le32(0x0001ff00);
+
+       /* (tx_info->flags & IEEE80211_TX_CTL_AMPDU) && */
+       if (ieee80211_is_data_qos(hdr->frame_control) && sta) {
+               if (sta->ht_cap.ht_supported) {
+                       u32 ampdu, val32;
+
+                       ampdu = (u32)sta->ht_cap.ampdu_density;
+                       val32 = ampdu << TXDESC_AMPDU_DENSITY_SHIFT;
+                       tx_desc->txdw2 |= cpu_to_le32(val32);
+                       tx_desc->txdw1 |= cpu_to_le32(TXDESC_AGG_ENABLE);
+               } else
+                       tx_desc->txdw1 |= cpu_to_le32(TXDESC_BK);
+       } else
+               tx_desc->txdw1 |= cpu_to_le32(TXDESC_BK);
+
+       if (ieee80211_is_data_qos(hdr->frame_control))
+               tx_desc->txdw4 |= cpu_to_le32(TXDESC_QOS);
+       if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ||
+           (sta && vif && vif->bss_conf.use_short_preamble))
+               tx_desc->txdw4 |= cpu_to_le32(TXDESC_SHORT_PREAMBLE);
+       if (rate_flag & IEEE80211_TX_RC_SHORT_GI ||
+           (ieee80211_is_data_qos(hdr->frame_control) &&
+            sta && sta->ht_cap.cap &
+            (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20))) {
+               tx_desc->txdw5 |= cpu_to_le32(TXDESC_SHORT_GI);
+       }
+       if (ieee80211_is_mgmt(hdr->frame_control)) {
+               tx_desc->txdw5 = cpu_to_le32(tx_rate->hw_value);
+               tx_desc->txdw4 |= cpu_to_le32(TXDESC_USE_DRIVER_RATE);
+               tx_desc->txdw5 |= cpu_to_le32(6 << TXDESC_RETRY_LIMIT_SHIFT);
+               tx_desc->txdw5 |= cpu_to_le32(TXDESC_RETRY_LIMIT_ENABLE);
+       }
+
+       if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) {
+               /* Use RTS rate 24M - does the mac80211 tell us which to use? */
+               tx_desc->txdw4 |= cpu_to_le32(DESC_RATE_24M);
+               tx_desc->txdw4 |= cpu_to_le32(TXDESC_RTS_CTS_ENABLE);
+               tx_desc->txdw4 |= cpu_to_le32(TXDESC_HW_RTS_ENABLE);
+       }
+
+       rtl8xxxu_calc_tx_desc_csum(tx_desc);
+
+       usb_fill_bulk_urb(&tx_urb->urb, priv->udev, priv->pipe_out[queue],
+                         skb->data, skb->len, rtl8xxxu_tx_complete, skb);
+
+       usb_anchor_urb(&tx_urb->urb, &priv->tx_anchor);
+       ret = usb_submit_urb(&tx_urb->urb, GFP_ATOMIC);
+       if (ret) {
+               usb_unanchor_urb(&tx_urb->urb);
+               rtl8xxxu_free_tx_urb(priv, tx_urb);
+               goto error;
+       }
+       return;
+error:
+       dev_kfree_skb(skb);
+}
+
+static void rtl8xxxu_rx_parse_phystats(struct rtl8xxxu_priv *priv,
+                                      struct ieee80211_rx_status *rx_status,
+                                      struct rtl8xxxu_rx_desc *rx_desc,
+                                      struct rtl8723au_phy_stats *phy_stats)
+{
+       if (phy_stats->sgi_en)
+               rx_status->flag |= RX_FLAG_SHORT_GI;
+
+       if (rx_desc->rxmcs < DESC_RATE_6M) {
+               /*
+                * Handle PHY stats for CCK rates
+                */
+               u8 cck_agc_rpt = phy_stats->cck_agc_rpt_ofdm_cfosho_a;
+
+               switch (cck_agc_rpt & 0xc0) {
+               case 0xc0:
+                       rx_status->signal = -46 - (cck_agc_rpt & 0x3e);
+                       break;
+               case 0x80:
+                       rx_status->signal = -26 - (cck_agc_rpt & 0x3e);
+                       break;
+               case 0x40:
+                       rx_status->signal = -12 - (cck_agc_rpt & 0x3e);
+                       break;
+               case 0x00:
+                       rx_status->signal = 16 - (cck_agc_rpt & 0x3e);
+                       break;
+               }
+       } else {
+               rx_status->signal =
+                       (phy_stats->cck_sig_qual_ofdm_pwdb_all >> 1) - 110;
+       }
+}
+
+static void rtl8xxxu_free_rx_resources(struct rtl8xxxu_priv *priv)
+{
+       struct rtl8xxxu_rx_urb *rx_urb, *tmp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->rx_urb_lock, flags);
+
+       list_for_each_entry_safe(rx_urb, tmp,
+                                &priv->rx_urb_pending_list, list) {
+               list_del(&rx_urb->list);
+               priv->rx_urb_pending_count--;
+               usb_free_urb(&rx_urb->urb);
+       }
+
+       spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
+}
+
+static void rtl8xxxu_queue_rx_urb(struct rtl8xxxu_priv *priv,
+                                 struct rtl8xxxu_rx_urb *rx_urb)
+{
+       struct sk_buff *skb;
+       unsigned long flags;
+       int pending = 0;
+
+       spin_lock_irqsave(&priv->rx_urb_lock, flags);
+
+       if (!priv->shutdown) {
+               list_add_tail(&rx_urb->list, &priv->rx_urb_pending_list);
+               priv->rx_urb_pending_count++;
+               pending = priv->rx_urb_pending_count;
+       } else {
+               skb = (struct sk_buff *)rx_urb->urb.context;
+               dev_kfree_skb(skb);
+               usb_free_urb(&rx_urb->urb);
+       }
+
+       spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
+
+       if (pending > RTL8XXXU_RX_URB_PENDING_WATER)
+               schedule_work(&priv->rx_urb_wq);
+}
+
+static void rtl8xxxu_rx_urb_work(struct work_struct *work)
+{
+       struct rtl8xxxu_priv *priv;
+       struct rtl8xxxu_rx_urb *rx_urb, *tmp;
+       struct list_head local;
+       struct sk_buff *skb;
+       unsigned long flags;
+       int ret;
+
+       priv = container_of(work, struct rtl8xxxu_priv, rx_urb_wq);
+       INIT_LIST_HEAD(&local);
+
+       spin_lock_irqsave(&priv->rx_urb_lock, flags);
+
+       list_splice_init(&priv->rx_urb_pending_list, &local);
+       priv->rx_urb_pending_count = 0;
+
+       spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
+
+       list_for_each_entry_safe(rx_urb, tmp, &local, list) {
+               list_del_init(&rx_urb->list);
+               ret = rtl8xxxu_submit_rx_urb(priv, rx_urb);
+               /*
+                * If out of memory or temporary error, put it back on the
+                * queue and try again. Otherwise the device is dead/gone
+                * and we should drop it.
+                */
+               switch (ret) {
+               case 0:
+                       break;
+               case -ENOMEM:
+               case -EAGAIN:
+                       rtl8xxxu_queue_rx_urb(priv, rx_urb);
+                       break;
+               default:
+                       pr_info("failed to requeue urb %i\n", ret);
+                       skb = (struct sk_buff *)rx_urb->urb.context;
+                       dev_kfree_skb(skb);
+                       usb_free_urb(&rx_urb->urb);
+               }
+       }
+}
+
+static void rtl8xxxu_rx_complete(struct urb *urb)
+{
+       struct rtl8xxxu_rx_urb *rx_urb =
+               container_of(urb, struct rtl8xxxu_rx_urb, urb);
+       struct ieee80211_hw *hw = rx_urb->hw;
+       struct rtl8xxxu_priv *priv = hw->priv;
+       struct sk_buff *skb = (struct sk_buff *)urb->context;
+       struct rtl8xxxu_rx_desc *rx_desc = (struct rtl8xxxu_rx_desc *)skb->data;
+       struct rtl8723au_phy_stats *phy_stats;
+       struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_mgmt *mgmt;
+       struct device *dev = &priv->udev->dev;
+       __le32 *_rx_desc_le = (__le32 *)skb->data;
+       u32 *_rx_desc = (u32 *)skb->data;
+       int cnt, len, drvinfo_sz, desc_shift, i;
+
+       for (i = 0; i < (sizeof(struct rtl8xxxu_rx_desc) / sizeof(u32)); i++)
+               _rx_desc[i] = le32_to_cpu(_rx_desc_le[i]);
+
+       cnt = rx_desc->frag;
+       len = rx_desc->pktlen;
+       drvinfo_sz = rx_desc->drvinfo_sz * 8;
+       desc_shift = rx_desc->shift;
+       skb_put(skb, urb->actual_length);
+
+       if (urb->status == 0) {
+               skb_pull(skb, sizeof(struct rtl8xxxu_rx_desc));
+               phy_stats = (struct rtl8723au_phy_stats *)skb->data;
+
+               skb_pull(skb, drvinfo_sz + desc_shift);
+
+               mgmt = (struct ieee80211_mgmt *)skb->data;
+
+               memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
+
+               if (rx_desc->phy_stats)
+                       rtl8xxxu_rx_parse_phystats(priv, rx_status,
+                                                  rx_desc, phy_stats);
+
+               rx_status->freq = hw->conf.chandef.chan->center_freq;
+               rx_status->band = hw->conf.chandef.chan->band;
+
+               rx_status->mactime = le32_to_cpu(rx_desc->tsfl);
+               rx_status->flag |= RX_FLAG_MACTIME_START;
+
+               if (!rx_desc->swdec)
+                       rx_status->flag |= RX_FLAG_DECRYPTED;
+               if (rx_desc->crc32)
+                       rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+               if (rx_desc->bw)
+                       rx_status->flag |= RX_FLAG_40MHZ;
+
+               if (rx_desc->rxht) {
+                       rx_status->flag |= RX_FLAG_HT;
+                       rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0;
+               } else {
+                       rx_status->rate_idx = rx_desc->rxmcs;
+               }
+
+               ieee80211_rx_irqsafe(hw, skb);
+               skb = NULL;
+               rx_urb->urb.context = NULL;
+               rtl8xxxu_queue_rx_urb(priv, rx_urb);
+       } else {
+               dev_dbg(dev, "%s: status %i\n", __func__, urb->status);
+               goto cleanup;
+       }
+       return;
+
+cleanup:
+       usb_free_urb(urb);
+       dev_kfree_skb(skb);
+       return;
+}
+
+static int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv,
+                                 struct rtl8xxxu_rx_urb *rx_urb)
+{
+       struct sk_buff *skb;
+       int skb_size;
+       int ret;
+
+       skb_size = sizeof(struct rtl8xxxu_rx_desc) + RTL_RX_BUFFER_SIZE;
+       skb = __netdev_alloc_skb(NULL, skb_size, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       memset(skb->data, 0, sizeof(struct rtl8xxxu_rx_desc));
+       usb_fill_bulk_urb(&rx_urb->urb, priv->udev, priv->pipe_in, skb->data,
+                         skb_size, rtl8xxxu_rx_complete, skb);
+       usb_anchor_urb(&rx_urb->urb, &priv->rx_anchor);
+       ret = usb_submit_urb(&rx_urb->urb, GFP_ATOMIC);
+       if (ret)
+               usb_unanchor_urb(&rx_urb->urb);
+       return ret;
+}
+
+static void rtl8xxxu_int_complete(struct urb *urb)
+{
+       struct rtl8xxxu_priv *priv = (struct rtl8xxxu_priv *)urb->context;
+       struct device *dev = &priv->udev->dev;
+       int ret;
+
+       dev_dbg(dev, "%s: status %i\n", __func__, urb->status);
+       if (urb->status == 0) {
+               usb_anchor_urb(urb, &priv->int_anchor);
+               ret = usb_submit_urb(urb, GFP_ATOMIC);
+               if (ret)
+                       usb_unanchor_urb(urb);
+       } else {
+               dev_info(dev, "%s: Error %i\n", __func__, urb->status);
+       }
+}
+
+
+static int rtl8xxxu_submit_int_urb(struct ieee80211_hw *hw)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       struct urb *urb;
+       u32 val32;
+       int ret;
+
+       urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!urb)
+               return -ENOMEM;
+
+       usb_fill_int_urb(urb, priv->udev, priv->pipe_interrupt,
+                        priv->int_buf, USB_INTR_CONTENT_LENGTH,
+                        rtl8xxxu_int_complete, priv, 1);
+       usb_anchor_urb(urb, &priv->int_anchor);
+       ret = usb_submit_urb(urb, GFP_KERNEL);
+       if (ret) {
+               usb_unanchor_urb(urb);
+               goto error;
+       }
+
+       val32 = rtl8xxxu_read32(priv, REG_USB_HIMR);
+       val32 |= USB_HIMR_CPWM;
+       rtl8xxxu_write32(priv, REG_USB_HIMR, val32);
+
+error:
+       return ret;
+}
+
+static int rtl8xxxu_add_interface(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       int ret;
+       u8 val8;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               rtl8723a_stop_tx_beacon(priv);
+
+               val8 = rtl8xxxu_read8(priv, REG_BEACON_CTRL);
+               val8 |= BEACON_ATIM | BEACON_FUNCTION_ENABLE |
+                       BEACON_DISABLE_TSF_UPDATE;
+               rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8);
+               ret = 0;
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+       }
+
+       rtl8xxxu_set_linktype(priv, vif->type);
+
+       return ret;
+}
+
+static void rtl8xxxu_remove_interface(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+
+       dev_dbg(&priv->udev->dev, "%s\n", __func__);
+}
+
+static int rtl8xxxu_config(struct ieee80211_hw *hw, u32 changed)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       struct device *dev = &priv->udev->dev;
+       u16 val16;
+       int ret = 0, channel;
+       bool ht40;
+
+       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_CHANNEL)
+               dev_info(dev,
+                        "%s: channel: %i (changed %08x chandef.width %02x)\n",
+                        __func__, hw->conf.chandef.chan->hw_value,
+                        changed, hw->conf.chandef.width);
+
+       if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+               val16 = ((hw->conf.long_frame_max_tx_count <<
+                         RETRY_LIMIT_LONG_SHIFT) & RETRY_LIMIT_LONG_MASK) |
+                       ((hw->conf.short_frame_max_tx_count <<
+                         RETRY_LIMIT_SHORT_SHIFT) & RETRY_LIMIT_SHORT_MASK);
+               rtl8xxxu_write16(priv, REG_RETRY_LIMIT, val16);
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               switch (hw->conf.chandef.width) {
+               case NL80211_CHAN_WIDTH_20_NOHT:
+               case NL80211_CHAN_WIDTH_20:
+                       ht40 = false;
+                       break;
+               case NL80211_CHAN_WIDTH_40:
+                       ht40 = true;
+                       break;
+               default:
+                       ret = -ENOTSUPP;
+                       goto exit;
+               }
+
+               channel = hw->conf.chandef.chan->hw_value;
+
+               rtl8723a_set_tx_power(priv, channel, ht40);
+
+               rtl8723au_config_channel(hw);
+       }
+
+exit:
+       return ret;
+}
+
+static int rtl8xxxu_conf_tx(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif, u16 queue,
+                           const struct ieee80211_tx_queue_params *param)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       struct device *dev = &priv->udev->dev;
+       u32 val32;
+       u8 aifs, acm_ctrl, acm_bit;
+
+       aifs = param->aifs;
+
+       val32 = aifs |
+               fls(param->cw_min) << EDCA_PARAM_ECW_MIN_SHIFT |
+               fls(param->cw_max) << EDCA_PARAM_ECW_MAX_SHIFT |
+               (u32)param->txop << EDCA_PARAM_TXOP_SHIFT;
+
+       acm_ctrl = rtl8xxxu_read8(priv, REG_ACM_HW_CTRL);
+       dev_dbg(dev,
+               "%s: IEEE80211 queue %02x val %08x, acm %i, acm_ctrl %02x\n",
+               __func__, queue, val32, param->acm, acm_ctrl);
+
+       switch (queue) {
+       case IEEE80211_AC_VO:
+               acm_bit = ACM_HW_CTRL_VO;
+               rtl8xxxu_write32(priv, REG_EDCA_VO_PARAM, val32);
+               break;
+       case IEEE80211_AC_VI:
+               acm_bit = ACM_HW_CTRL_VI;
+               rtl8xxxu_write32(priv, REG_EDCA_VI_PARAM, val32);
+               break;
+       case IEEE80211_AC_BE:
+               acm_bit = ACM_HW_CTRL_BE;
+               rtl8xxxu_write32(priv, REG_EDCA_BE_PARAM, val32);
+               break;
+       case IEEE80211_AC_BK:
+               acm_bit = ACM_HW_CTRL_BK;
+               rtl8xxxu_write32(priv, REG_EDCA_BK_PARAM, val32);
+               break;
+       default:
+               acm_bit = 0;
+               break;
+       }
+
+       if (param->acm)
+               acm_ctrl |= acm_bit;
+       else
+               acm_ctrl &= ~acm_bit;
+       rtl8xxxu_write8(priv, REG_ACM_HW_CTRL, acm_ctrl);
+
+       return 0;
+}
+
+static void rtl8xxxu_configure_filter(struct ieee80211_hw *hw,
+                                     unsigned int changed_flags,
+                                     unsigned int *total_flags, u64 multicast)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+
+       dev_dbg(&priv->udev->dev, "%s: changed_flags %08x, total_flags %08x\n",
+               __func__, changed_flags, *total_flags);
+
+       *total_flags &= (FIF_ALLMULTI | FIF_CONTROL | FIF_BCN_PRBRESP_PROMISC);
+}
+
+static int rtl8xxxu_set_rts_threshold(struct ieee80211_hw *hw, u32 rts)
+{
+       if (rts > 2347)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+                           struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta,
+                           struct ieee80211_key_conf *key)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       struct device *dev = &priv->udev->dev;
+       u8 mac_addr[ETH_ALEN];
+       u8 val8;
+       u16 val16;
+       u32 val32;
+       int retval = -EOPNOTSUPP;
+
+       dev_dbg(dev, "%s: cmd %02x, cipher %08x, index %i\n",
+               __func__, cmd, key->cipher, key->keyidx);
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return -EOPNOTSUPP;
+
+       if (key->keyidx > 3)
+               return -EOPNOTSUPP;
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+               dev_dbg(dev, "%s: pairwise key\n", __func__);
+               ether_addr_copy(mac_addr, sta->addr);
+       } else {
+               dev_dbg(dev, "%s: group key\n", __func__);
+               eth_broadcast_addr(mac_addr);
+       }
+
+       val16 = rtl8xxxu_read16(priv, REG_CR);
+       val16 |= CR_SECURITY_ENABLE;
+       rtl8xxxu_write16(priv, REG_CR, val16);
+
+       val8 = SEC_CFG_TX_SEC_ENABLE | SEC_CFG_TXBC_USE_DEFKEY |
+               SEC_CFG_RX_SEC_ENABLE | SEC_CFG_RXBC_USE_DEFKEY;
+       val8 |= SEC_CFG_TX_USE_DEFKEY | SEC_CFG_RX_USE_DEFKEY;
+       rtl8xxxu_write8(priv, REG_SECURITY_CFG, val8);
+
+       switch (cmd) {
+       case SET_KEY:
+               key->hw_key_idx = key->keyidx;
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+               rtl8xxxu_cam_write(priv, key, mac_addr);
+               retval = 0;
+               break;
+       case DISABLE_KEY:
+               rtl8xxxu_write32(priv, REG_CAM_WRITE, 0x00000000);
+               val32 = CAM_CMD_POLLING | CAM_CMD_WRITE |
+                       key->keyidx << CAM_CMD_KEY_SHIFT;
+               rtl8xxxu_write32(priv, REG_CAM_CMD, val32);
+               retval = 0;
+               break;
+       default:
+               dev_warn(dev, "%s: Unsupported command %02x\n", __func__, cmd);
+       }
+
+       return retval;
+}
+
+static int
+rtl8xxxu_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                     enum ieee80211_ampdu_mlme_action action,
+                     struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size,
+                     bool amsdu)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       struct device *dev = &priv->udev->dev;
+       u8 ampdu_factor, ampdu_density;
+
+       switch (action) {
+       case IEEE80211_AMPDU_TX_START:
+               dev_info(dev, "%s: IEEE80211_AMPDU_TX_START\n", __func__);
+               ampdu_factor = sta->ht_cap.ampdu_factor;
+               ampdu_density = sta->ht_cap.ampdu_density;
+               rtl8xxxu_set_ampdu_factor(priv, ampdu_factor);
+               rtl8xxxu_set_ampdu_min_space(priv, ampdu_density);
+               dev_dbg(dev,
+                       "Changed HT: ampdu_factor %02x, ampdu_density %02x\n",
+                       ampdu_factor, ampdu_density);
+               break;
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+               dev_info(dev, "%s: IEEE80211_AMPDU_TX_STOP_FLUSH\n", __func__);
+               rtl8xxxu_set_ampdu_factor(priv, 0);
+               rtl8xxxu_set_ampdu_min_space(priv, 0);
+               break;
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+               dev_info(dev, "%s: IEEE80211_AMPDU_TX_STOP_FLUSH_CONT\n",
+                        __func__);
+               rtl8xxxu_set_ampdu_factor(priv, 0);
+               rtl8xxxu_set_ampdu_min_space(priv, 0);
+               break;
+       case IEEE80211_AMPDU_RX_START:
+               dev_info(dev, "%s: IEEE80211_AMPDU_RX_START\n", __func__);
+               break;
+       case IEEE80211_AMPDU_RX_STOP:
+               dev_info(dev, "%s: IEEE80211_AMPDU_RX_STOP\n", __func__);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int rtl8xxxu_start(struct ieee80211_hw *hw)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       struct rtl8xxxu_rx_urb *rx_urb;
+       struct rtl8xxxu_tx_urb *tx_urb;
+       unsigned long flags;
+       int ret, i;
+
+       ret = 0;
+
+       init_usb_anchor(&priv->rx_anchor);
+       init_usb_anchor(&priv->tx_anchor);
+       init_usb_anchor(&priv->int_anchor);
+
+       rtl8723a_enable_rf(priv);
+       ret = rtl8xxxu_submit_int_urb(hw);
+       if (ret)
+               goto exit;
+
+       for (i = 0; i < RTL8XXXU_TX_URBS; i++) {
+               tx_urb = kmalloc(sizeof(struct rtl8xxxu_tx_urb), GFP_KERNEL);
+               if (!tx_urb) {
+                       if (!i)
+                               ret = -ENOMEM;
+
+                       goto error_out;
+               }
+               usb_init_urb(&tx_urb->urb);
+               INIT_LIST_HEAD(&tx_urb->list);
+               tx_urb->hw = hw;
+               list_add(&tx_urb->list, &priv->tx_urb_free_list);
+               priv->tx_urb_free_count++;
+       }
+
+       priv->tx_stopped = false;
+
+       spin_lock_irqsave(&priv->rx_urb_lock, flags);
+       priv->shutdown = false;
+       spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
+
+       for (i = 0; i < RTL8XXXU_RX_URBS; i++) {
+               rx_urb = kmalloc(sizeof(struct rtl8xxxu_rx_urb), GFP_KERNEL);
+               if (!rx_urb) {
+                       if (!i)
+                               ret = -ENOMEM;
+
+                       goto error_out;
+               }
+               usb_init_urb(&rx_urb->urb);
+               INIT_LIST_HEAD(&rx_urb->list);
+               rx_urb->hw = hw;
+
+               ret = rtl8xxxu_submit_rx_urb(priv, rx_urb);
+       }
+exit:
+       /*
+        * Disable all data frames
+        */
+       rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0x0000);
+       /*
+        * Accept all mgmt frames
+        */
+       rtl8xxxu_write16(priv, REG_RXFLTMAP0, 0xffff);
+
+       rtl8xxxu_write32(priv, REG_OFDM0_XA_AGC_CORE1, 0x6954341e);
+
+       return ret;
+
+error_out:
+       rtl8xxxu_free_tx_resources(priv);
+       /*
+        * Disable all data and mgmt frames
+        */
+       rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0x0000);
+       rtl8xxxu_write16(priv, REG_RXFLTMAP0, 0x0000);
+
+       return ret;
+}
+
+static void rtl8xxxu_stop(struct ieee80211_hw *hw)
+{
+       struct rtl8xxxu_priv *priv = hw->priv;
+       unsigned long flags;
+
+       rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff);
+
+       rtl8xxxu_write16(priv, REG_RXFLTMAP0, 0x0000);
+       rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0x0000);
+
+       spin_lock_irqsave(&priv->rx_urb_lock, flags);
+       priv->shutdown = true;
+       spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
+
+       usb_kill_anchored_urbs(&priv->rx_anchor);
+       usb_kill_anchored_urbs(&priv->tx_anchor);
+       usb_kill_anchored_urbs(&priv->int_anchor);
+
+       rtl8723a_disable_rf(priv);
+
+       /*
+        * Disable interrupts
+        */
+       rtl8xxxu_write32(priv, REG_USB_HIMR, 0);
+
+       rtl8xxxu_free_rx_resources(priv);
+       rtl8xxxu_free_tx_resources(priv);
+}
+
+static const struct ieee80211_ops rtl8xxxu_ops = {
+       .tx = rtl8xxxu_tx,
+       .add_interface = rtl8xxxu_add_interface,
+       .remove_interface = rtl8xxxu_remove_interface,
+       .config = rtl8xxxu_config,
+       .conf_tx = rtl8xxxu_conf_tx,
+       .bss_info_changed = rtl8xxxu_bss_info_changed,
+       .configure_filter = rtl8xxxu_configure_filter,
+       .set_rts_threshold = rtl8xxxu_set_rts_threshold,
+       .start = rtl8xxxu_start,
+       .stop = rtl8xxxu_stop,
+       .sw_scan_start = rtl8xxxu_sw_scan_start,
+       .sw_scan_complete = rtl8xxxu_sw_scan_complete,
+       .set_key = rtl8xxxu_set_key,
+       .ampdu_action = rtl8xxxu_ampdu_action,
+};
+
+static int rtl8xxxu_parse_usb(struct rtl8xxxu_priv *priv,
+                             struct usb_interface *interface)
+{
+       struct usb_interface_descriptor *interface_desc;
+       struct usb_host_interface *host_interface;
+       struct usb_endpoint_descriptor *endpoint;
+       struct device *dev = &priv->udev->dev;
+       int i, j = 0, endpoints;
+       u8 dir, xtype, num;
+       int ret = 0;
+
+       host_interface = &interface->altsetting[0];
+       interface_desc = &host_interface->desc;
+       endpoints = interface_desc->bNumEndpoints;
+
+       for (i = 0; i < endpoints; i++) {
+               endpoint = &host_interface->endpoint[i].desc;
+
+               dir = endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+               num = usb_endpoint_num(endpoint);
+               xtype = usb_endpoint_type(endpoint);
+               if (rtl8xxxu_debug & RTL8XXXU_DEBUG_USB)
+                       dev_dbg(dev,
+                               "%s: endpoint: dir %02x, # %02x, type %02x\n",
+                               __func__, dir, num, xtype);
+               if (usb_endpoint_dir_in(endpoint) &&
+                   usb_endpoint_xfer_bulk(endpoint)) {
+                       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_USB)
+                               dev_dbg(dev, "%s: in endpoint num %i\n",
+                                       __func__, num);
+
+                       if (priv->pipe_in) {
+                               dev_warn(dev,
+                                        "%s: Too many IN pipes\n", __func__);
+                               ret = -EINVAL;
+                               goto exit;
+                       }
+
+                       priv->pipe_in = usb_rcvbulkpipe(priv->udev, num);
+               }
+
+               if (usb_endpoint_dir_in(endpoint) &&
+                   usb_endpoint_xfer_int(endpoint)) {
+                       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_USB)
+                               dev_dbg(dev, "%s: interrupt endpoint num %i\n",
+                                       __func__, num);
+
+                       if (priv->pipe_interrupt) {
+                               dev_warn(dev, "%s: Too many INTERRUPT pipes\n",
+                                        __func__);
+                               ret = -EINVAL;
+                               goto exit;
+                       }
+
+                       priv->pipe_interrupt = usb_rcvintpipe(priv->udev, num);
+               }
+
+               if (usb_endpoint_dir_out(endpoint) &&
+                   usb_endpoint_xfer_bulk(endpoint)) {
+                       if (rtl8xxxu_debug & RTL8XXXU_DEBUG_USB)
+                               dev_dbg(dev, "%s: out endpoint num %i\n",
+                                       __func__, num);
+                       if (j >= RTL8XXXU_OUT_ENDPOINTS) {
+                               dev_warn(dev,
+                                        "%s: Too many OUT pipes\n", __func__);
+                               ret = -EINVAL;
+                               goto exit;
+                       }
+                       priv->out_ep[j++] = num;
+               }
+       }
+exit:
+       priv->nr_out_eps = j;
+       return ret;
+}
+
+static int rtl8xxxu_probe(struct usb_interface *interface,
+                         const struct usb_device_id *id)
+{
+       struct rtl8xxxu_priv *priv;
+       struct ieee80211_hw *hw;
+       struct usb_device *udev;
+       struct ieee80211_supported_band *sband;
+       int ret = 0;
+       int untested = 1;
+
+       udev = usb_get_dev(interface_to_usbdev(interface));
+
+       switch (id->idVendor) {
+       case USB_VENDOR_ID_REALTEK:
+               switch(id->idProduct) {
+               case 0x1724:
+               case 0x8176:
+               case 0x8178:
+               case 0x817f:
+                       untested = 0;
+                       break;
+               }
+               break;
+       case 0x7392:
+               if (id->idProduct == 0x7811)
+                       untested = 0;
+               break;
+       default:
+               break;
+       }
+
+       if (untested) {
+               rtl8xxxu_debug = RTL8XXXU_DEBUG_EFUSE;
+               dev_info(&udev->dev,
+                        "This Realtek USB WiFi dongle (0x%04x:0x%04x) is untested!\n",
+                        id->idVendor, id->idProduct);
+               dev_info(&udev->dev,
+                        "Please report results to Jes.Sorensen@gmail.com\n");
+       }
+
+       hw = ieee80211_alloc_hw(sizeof(struct rtl8xxxu_priv), &rtl8xxxu_ops);
+       if (!hw) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       priv = hw->priv;
+       priv->hw = hw;
+       priv->udev = udev;
+       priv->fops = (struct rtl8xxxu_fileops *)id->driver_info;
+       mutex_init(&priv->usb_buf_mutex);
+       mutex_init(&priv->h2c_mutex);
+       INIT_LIST_HEAD(&priv->tx_urb_free_list);
+       spin_lock_init(&priv->tx_urb_lock);
+       INIT_LIST_HEAD(&priv->rx_urb_pending_list);
+       spin_lock_init(&priv->rx_urb_lock);
+       INIT_WORK(&priv->rx_urb_wq, rtl8xxxu_rx_urb_work);
+
+       usb_set_intfdata(interface, hw);
+
+       ret = rtl8xxxu_parse_usb(priv, interface);
+       if (ret)
+               goto exit;
+
+       ret = rtl8xxxu_identify_chip(priv);
+       if (ret) {
+               dev_err(&udev->dev, "Fatal - failed to identify chip\n");
+               goto exit;
+       }
+
+       ret = rtl8xxxu_read_efuse(priv);
+       if (ret) {
+               dev_err(&udev->dev, "Fatal - failed to read EFuse\n");
+               goto exit;
+       }
+
+       ret = priv->fops->parse_efuse(priv);
+       if (ret) {
+               dev_err(&udev->dev, "Fatal - failed to parse EFuse\n");
+               goto exit;
+       }
+
+       rtl8xxxu_print_chipinfo(priv);
+
+       ret = priv->fops->load_firmware(priv);
+       if (ret) {
+               dev_err(&udev->dev, "Fatal - failed to load firmware\n");
+               goto exit;
+       }
+
+       ret = rtl8xxxu_init_device(hw);
+
+       hw->wiphy->max_scan_ssids = 1;
+       hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+       hw->queues = 4;
+
+       sband = &rtl8xxxu_supported_band;
+       sband->ht_cap.ht_supported = true;
+       sband->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+       sband->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
+       sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40;
+       memset(&sband->ht_cap.mcs, 0, sizeof(sband->ht_cap.mcs));
+       sband->ht_cap.mcs.rx_mask[0] = 0xff;
+       sband->ht_cap.mcs.rx_mask[4] = 0x01;
+       if (priv->rf_paths > 1) {
+               sband->ht_cap.mcs.rx_mask[1] = 0xff;
+               sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+       }
+       sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+       /*
+        * Some APs will negotiate HT20_40 in a noisy environment leading
+        * to miserable performance. Rather than defaulting to this, only
+        * enable it if explicitly requested at module load time.
+        */
+       if (rtl8xxxu_ht40_2g) {
+               dev_info(&udev->dev, "Enabling HT_20_40 on the 2.4GHz band\n");
+               sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       }
+       hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
+
+       hw->wiphy->rts_threshold = 2347;
+
+       SET_IEEE80211_DEV(priv->hw, &interface->dev);
+       SET_IEEE80211_PERM_ADDR(hw, priv->mac_addr);
+
+       hw->extra_tx_headroom = sizeof(struct rtl8xxxu_tx_desc);
+       ieee80211_hw_set(hw, SIGNAL_DBM);
+       /*
+        * The firmware handles rate control
+        */
+       ieee80211_hw_set(hw, HAS_RATE_CONTROL);
+       ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+
+       ret = ieee80211_register_hw(priv->hw);
+       if (ret) {
+               dev_err(&udev->dev, "%s: Failed to register: %i\n",
+                       __func__, ret);
+               goto exit;
+       }
+
+exit:
+       if (ret < 0)
+               usb_put_dev(udev);
+       return ret;
+}
+
+static void rtl8xxxu_disconnect(struct usb_interface *interface)
+{
+       struct rtl8xxxu_priv *priv;
+       struct ieee80211_hw *hw;
+
+       hw = usb_get_intfdata(interface);
+       priv = hw->priv;
+
+       rtl8xxxu_disable_device(hw);
+       usb_set_intfdata(interface, NULL);
+
+       dev_info(&priv->udev->dev, "disconnecting\n");
+
+       ieee80211_unregister_hw(hw);
+
+       kfree(priv->fw_data);
+       mutex_destroy(&priv->usb_buf_mutex);
+       mutex_destroy(&priv->h2c_mutex);
+
+       usb_put_dev(priv->udev);
+       ieee80211_free_hw(hw);
+}
+
+static struct rtl8xxxu_fileops rtl8723au_fops = {
+       .parse_efuse = rtl8723au_parse_efuse,
+       .load_firmware = rtl8723au_load_firmware,
+       .power_on = rtl8723au_power_on,
+       .writeN_block_size = 1024,
+};
+
+#ifdef CONFIG_RTL8XXXU_UNTESTED
+
+static struct rtl8xxxu_fileops rtl8192cu_fops = {
+       .parse_efuse = rtl8192cu_parse_efuse,
+       .load_firmware = rtl8192cu_load_firmware,
+       .power_on = rtl8192cu_power_on,
+       .writeN_block_size = 128,
+};
+
+#endif
+
+static struct usb_device_id dev_table[] = {
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8724, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8723au_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x1724, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8723au_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x0724, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8723au_fops},
+#ifdef CONFIG_RTL8XXXU_UNTESTED
+/* Still supported by rtlwifi */
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8176, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8178, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x817f, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+/* Tested by Larry Finger */
+{USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x7811, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+/* Currently untested 8188 series devices */
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8191, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8170, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8177, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x817a, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x817b, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x817d, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x817e, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x818a, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x317f, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x1058, 0x0631, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x04bb, 0x094c, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x050d, 0x1102, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x06f8, 0xe033, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x07b8, 0x8189, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9041, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x17ba, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x1e1e, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x5088, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0df6, 0x0052, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0df6, 0x005c, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0eb0, 0x9071, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x103c, 0x1629, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x13d3, 0x3357, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3308, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x330b, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2019, 0x4902, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2019, 0xab2a, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2019, 0xab2e, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2019, 0xed17, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x20f4, 0x648b, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x4855, 0x0090, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x4856, 0x0091, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0xcdab, 0x8010, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x317f, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops}, /* Netcore 8188RU */
+{USB_DEVICE_AND_INTERFACE_INFO(0x04f2, 0xaff7, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x04f2, 0xaff9, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x04f2, 0xaffa, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x04f2, 0xaff8, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x04f2, 0xaffb, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x04f2, 0xaffc, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2019, 0x1201, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+/* Currently untested 8192 series devices */
+{USB_DEVICE_AND_INTERFACE_INFO(0x04bb, 0x0950, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x050d, 0x1004, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x050d, 0x2102, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x050d, 0x2103, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0586, 0x341f, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x06f8, 0xe035, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x17ab, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0df6, 0x0061, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0df6, 0x0070, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0789, 0x016d, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x07aa, 0x0056, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x07b8, 0x8178, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9021, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0xf001, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x2e2e, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0e66, 0x0019, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x0e66, 0x0020, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3307, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3309, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x330a, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2019, 0xab2b, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x20f4, 0x624d, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0100, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x4855, 0x0091, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x7822, 0xff, 0xff, 0xff),
+       .driver_info = (unsigned long)&rtl8192cu_fops},
+#endif
+{ }
+};
+
+static struct usb_driver rtl8xxxu_driver = {
+       .name = DRIVER_NAME,
+       .probe = rtl8xxxu_probe,
+       .disconnect = rtl8xxxu_disconnect,
+       .id_table = dev_table,
+       .disable_hub_initiated_lpm = 1,
+};
+
+static int __init rtl8xxxu_module_init(void)
+{
+       int res;
+
+       res = usb_register(&rtl8xxxu_driver);
+       if (res < 0)
+               pr_err(DRIVER_NAME ": usb_register() failed (%i)\n", res);
+
+       return res;
+}
+
+static void __exit rtl8xxxu_module_exit(void)
+{
+       usb_deregister(&rtl8xxxu_driver);
+}
+
+
+MODULE_DEVICE_TABLE(usb, dev_table);
+
+module_init(rtl8xxxu_module_init);
+module_exit(rtl8xxxu_module_exit);
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
new file mode 100644 (file)
index 0000000..f2a1bac
--- /dev/null
@@ -0,0 +1,676 @@
+/*
+ * Copyright (c) 2014 - 2015 Jes Sorensen <Jes.Sorensen@redhat.com>
+ *
+ * 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.
+ *
+ * Register definitions taken from original Realtek rtl8723au driver
+ */
+
+#include <asm/byteorder.h>
+
+#define RTL8XXXU_DEBUG_REG_WRITE       0x01
+#define RTL8XXXU_DEBUG_REG_READ                0x02
+#define RTL8XXXU_DEBUG_RFREG_WRITE     0x04
+#define RTL8XXXU_DEBUG_RFREG_READ      0x08
+#define RTL8XXXU_DEBUG_CHANNEL         0x10
+#define RTL8XXXU_DEBUG_TX              0x20
+#define RTL8XXXU_DEBUG_TX_DUMP         0x40
+#define RTL8XXXU_DEBUG_RX              0x80
+#define RTL8XXXU_DEBUG_RX_DUMP         0x100
+#define RTL8XXXU_DEBUG_USB             0x200
+#define RTL8XXXU_DEBUG_KEY             0x400
+#define RTL8XXXU_DEBUG_H2C             0x800
+#define RTL8XXXU_DEBUG_ACTION          0x1000
+#define RTL8XXXU_DEBUG_EFUSE           0x2000
+
+#define RTW_USB_CONTROL_MSG_TIMEOUT    500
+#define RTL8XXXU_MAX_REG_POLL          500
+#define        USB_INTR_CONTENT_LENGTH         56
+
+#define RTL8XXXU_OUT_ENDPOINTS         3
+
+#define REALTEK_USB_READ               0xc0
+#define REALTEK_USB_WRITE              0x40
+#define REALTEK_USB_CMD_REQ            0x05
+#define REALTEK_USB_CMD_IDX            0x00
+
+#define TX_TOTAL_PAGE_NUM              0xf8
+/* (HPQ + LPQ + NPQ + PUBQ) = TX_TOTAL_PAGE_NUM */
+#define TX_PAGE_NUM_PUBQ               0xe7
+#define TX_PAGE_NUM_HI_PQ              0x0c
+#define TX_PAGE_NUM_LO_PQ              0x02
+#define TX_PAGE_NUM_NORM_PQ            0x02
+
+#define RTL_FW_PAGE_SIZE               4096
+#define RTL8XXXU_FIRMWARE_POLL_MAX     1000
+
+#define RTL8723A_CHANNEL_GROUPS                3
+#define RTL8723A_MAX_RF_PATHS          2
+#define RF6052_MAX_TX_PWR              0x3f
+
+#define EFUSE_MAP_LEN_8723A            256
+#define EFUSE_MAX_SECTION_8723A                32
+#define EFUSE_REAL_CONTENT_LEN_8723A   512
+#define EFUSE_BT_MAP_LEN_8723A         1024
+#define EFUSE_MAX_WORD_UNIT            4
+
+struct rtl8xxxu_rx_desc {
+#ifdef __LITTLE_ENDIAN
+       u32 pktlen:14;
+       u32 crc32:1;
+       u32 icverr:1;
+       u32 drvinfo_sz:4;
+       u32 security:3;
+       u32 qos:1;
+       u32 shift:2;
+       u32 phy_stats:1;
+       u32 swdec:1;
+       u32 ls:1;
+       u32 fs:1;
+       u32 eor:1;
+       u32 own:1;
+
+       u32 macid:5;
+       u32 tid:4;
+       u32 hwrsvd:4;
+       u32 amsdu:1;
+       u32 paggr:1;
+       u32 faggr:1;
+       u32 a1fit:4;
+       u32 a2fit:4;
+       u32 pam:1;
+       u32 pwr:1;
+       u32 md:1;
+       u32 mf:1;
+       u32 type:2;
+       u32 mc:1;
+       u32 bc:1;
+
+       u32 seq:12;
+       u32 frag:4;
+       u32 nextpktlen:14;
+       u32 nextind:1;
+       u32 reserved0:1;
+
+       u32 rxmcs:6;
+       u32 rxht:1;
+       u32 gf:1;
+       u32 splcp:1;
+       u32 bw:1;
+       u32 htc:1;
+       u32 eosp:1;
+       u32 bssidfit:2;
+       u32 reserved1:16;
+       u32 unicastwake:1;
+       u32 magicwake:1;
+
+       u32 pattern0match:1;
+       u32 pattern1match:1;
+       u32 pattern2match:1;
+       u32 pattern3match:1;
+       u32 pattern4match:1;
+       u32 pattern5match:1;
+       u32 pattern6match:1;
+       u32 pattern7match:1;
+       u32 pattern8match:1;
+       u32 pattern9match:1;
+       u32 patternamatch:1;
+       u32 patternbmatch:1;
+       u32 patterncmatch:1;
+       u32 reserved2:19;
+#else
+       u32 own:1;
+       u32 eor:1;
+       u32 fs:1;
+       u32 ls:1;
+       u32 swdec:1;
+       u32 phy_stats:1;
+       u32 shift:2;
+       u32 qos:1;
+       u32 security:3;
+       u32 drvinfo_sz:4;
+       u32 icverr:1;
+       u32 crc32:1;
+       u32 pktlen:14;
+
+       u32 bc:1;
+       u32 mc:1;
+       u32 type:2;
+       u32 mf:1;
+       u32 md:1;
+       u32 pwr:1;
+       u32 pam:1;
+       u32 a2fit:4;
+       u32 a1fit:4;
+       u32 faggr:1;
+       u32 paggr:1;
+       u32 amsdu:1;
+       u32 hwrsvd:4;
+       u32 tid:4;
+       u32 macid:5;
+
+       u32 reserved0:1;
+       u32 nextind:1;
+       u32 nextpktlen:14;
+       u32 frag:4;
+       u32 seq:12;
+
+       u32 magicwake:1;
+       u32 unicastwake:1;
+       u32 reserved1:16;
+       u32 bssidfit:2;
+       u32 eosp:1;
+       u32 htc:1;
+       u32 bw:1;
+       u32 splcp:1;
+       u32 gf:1;
+       u32 rxht:1;
+       u32 rxmcs:6;
+
+       u32 reserved2:19;
+       u32 patterncmatch:1;
+       u32 patternbmatch:1;
+       u32 patternamatch:1;
+       u32 pattern9match:1;
+       u32 pattern8match:1;
+       u32 pattern7match:1;
+       u32 pattern6match:1;
+       u32 pattern5match:1;
+       u32 pattern4match:1;
+       u32 pattern3match:1;
+       u32 pattern2match:1;
+       u32 pattern1match:1;
+       u32 pattern0match:1;
+#endif
+       __le32 tsfl;
+#if 0
+       u32 bassn:12;
+       u32 bavld:1;
+       u32 reserved3:19;
+#endif
+};
+
+struct rtl8xxxu_tx_desc {
+       __le16 pkt_size;
+       u8 pkt_offset;
+       u8 txdw0;
+       __le32 txdw1;
+       __le32 txdw2;
+       __le32 txdw3;
+       __le32 txdw4;
+       __le32 txdw5;
+       __le32 txdw6;
+       __le16 csum;
+       __le16 txdw7;
+};
+
+/*  CCK Rates, TxHT = 0 */
+#define DESC_RATE_1M                   0x00
+#define DESC_RATE_2M                   0x01
+#define DESC_RATE_5_5M                 0x02
+#define DESC_RATE_11M                  0x03
+
+/*  OFDM Rates, TxHT = 0 */
+#define DESC_RATE_6M                   0x04
+#define DESC_RATE_9M                   0x05
+#define DESC_RATE_12M                  0x06
+#define DESC_RATE_18M                  0x07
+#define DESC_RATE_24M                  0x08
+#define DESC_RATE_36M                  0x09
+#define DESC_RATE_48M                  0x0a
+#define DESC_RATE_54M                  0x0b
+
+/*  MCS Rates, TxHT = 1 */
+#define DESC_RATE_MCS0                 0x0c
+#define DESC_RATE_MCS1                 0x0d
+#define DESC_RATE_MCS2                 0x0e
+#define DESC_RATE_MCS3                 0x0f
+#define DESC_RATE_MCS4                 0x10
+#define DESC_RATE_MCS5                 0x11
+#define DESC_RATE_MCS6                 0x12
+#define DESC_RATE_MCS7                 0x13
+#define DESC_RATE_MCS8                 0x14
+#define DESC_RATE_MCS9                 0x15
+#define DESC_RATE_MCS10                        0x16
+#define DESC_RATE_MCS11                        0x17
+#define DESC_RATE_MCS12                        0x18
+#define DESC_RATE_MCS13                        0x19
+#define DESC_RATE_MCS14                        0x1a
+#define DESC_RATE_MCS15                        0x1b
+#define DESC_RATE_MCS15_SG             0x1c
+#define DESC_RATE_MCS32                        0x20
+
+#define TXDESC_OFFSET_SZ               0
+#define TXDESC_OFFSET_SHT              16
+#if 0
+#define TXDESC_BMC                     BIT(24)
+#define TXDESC_LSG                     BIT(26)
+#define TXDESC_FSG                     BIT(27)
+#define TXDESC_OWN                     BIT(31)
+#else
+#define TXDESC_BROADMULTICAST          BIT(0)
+#define TXDESC_LAST_SEGMENT            BIT(2)
+#define TXDESC_FIRST_SEGMENT           BIT(3)
+#define TXDESC_OWN                     BIT(7)
+#endif
+
+/* Word 1 */
+#define TXDESC_PKT_OFFSET_SZ           0
+#define TXDESC_AGG_ENABLE              BIT(5)
+#define TXDESC_BK                      BIT(6)
+#define TXDESC_QUEUE_SHIFT             8
+#define TXDESC_QUEUE_MASK              0x1f00
+#define TXDESC_QUEUE_BK                        0x2
+#define TXDESC_QUEUE_BE                        0x0
+#define TXDESC_QUEUE_VI                        0x5
+#define TXDESC_QUEUE_VO                        0x7
+#define TXDESC_QUEUE_BEACON            0x10
+#define TXDESC_QUEUE_HIGH              0x11
+#define TXDESC_QUEUE_MGNT              0x12
+#define TXDESC_QUEUE_CMD               0x13
+#define TXDESC_QUEUE_MAX               (TXDESC_QUEUE_CMD + 1)
+
+#define DESC_RATE_ID_SHIFT             16
+#define DESC_RATE_ID_MASK              0xf
+#define TXDESC_NAVUSEHDR               BIT(20)
+#define TXDESC_SEC_RC4                 0x00400000
+#define TXDESC_SEC_AES                 0x00c00000
+#define TXDESC_PKT_OFFSET_SHIFT                26
+#define TXDESC_AGG_EN                  BIT(29)
+#define TXDESC_HWPC                    BIT(31)
+
+/* Word 2 */
+#define TXDESC_ACK_REPORT              BIT(19)
+#define TXDESC_AMPDU_DENSITY_SHIFT     20
+
+/* Word 3 */
+#define TXDESC_SEQ_SHIFT               16
+#define TXDESC_SEQ_MASK                        0x0fff0000
+
+/* Word 4 */
+#define TXDESC_QOS                     BIT(6)
+#define TXDESC_HW_SEQ_ENABLE           BIT(7)
+#define TXDESC_USE_DRIVER_RATE         BIT(8)
+#define TXDESC_DISABLE_DATA_FB         BIT(10)
+#define TXDESC_CTS_SELF_ENABLE         BIT(11)
+#define TXDESC_RTS_CTS_ENABLE          BIT(12)
+#define TXDESC_HW_RTS_ENABLE           BIT(13)
+#define TXDESC_PRIME_CH_OFF_LOWER      BIT(20)
+#define TXDESC_PRIME_CH_OFF_UPPER      BIT(21)
+#define TXDESC_SHORT_PREAMBLE          BIT(24)
+#define TXDESC_DATA_BW                 BIT(25)
+#define TXDESC_RTS_DATA_BW             BIT(27)
+#define TXDESC_RTS_PRIME_CH_OFF_LOWER  BIT(28)
+#define TXDESC_RTS_PRIME_CH_OFF_UPPER  BIT(29)
+
+/* Word 5 */
+#define TXDESC_RTS_RATE_SHIFT          0
+#define TXDESC_RTS_RATE_MASK           0x3f
+#define TXDESC_SHORT_GI                        BIT(6)
+#define TXDESC_CCX_TAG                 BIT(7)
+#define TXDESC_RETRY_LIMIT_ENABLE      BIT(17)
+#define TXDESC_RETRY_LIMIT_SHIFT       18
+#define TXDESC_RETRY_LIMIT_MASK                0x00fc0000
+
+/* Word 6 */
+#define TXDESC_MAX_AGG_SHIFT           11
+
+struct phy_rx_agc_info {
+#ifdef __LITTLE_ENDIAN
+       u8      gain:7, trsw:1;
+#else
+       u8      trsw:1, gain:7;
+#endif
+};
+
+struct rtl8723au_phy_stats {
+       struct phy_rx_agc_info path_agc[RTL8723A_MAX_RF_PATHS];
+       u8      ch_corr[RTL8723A_MAX_RF_PATHS];
+       u8      cck_sig_qual_ofdm_pwdb_all;
+       u8      cck_agc_rpt_ofdm_cfosho_a;
+       u8      cck_rpt_b_ofdm_cfosho_b;
+       u8      reserved_1;
+       u8      noise_power_db_msb;
+       u8      path_cfotail[RTL8723A_MAX_RF_PATHS];
+       u8      pcts_mask[RTL8723A_MAX_RF_PATHS];
+       s8      stream_rxevm[RTL8723A_MAX_RF_PATHS];
+       u8      path_rxsnr[RTL8723A_MAX_RF_PATHS];
+       u8      noise_power_db_lsb;
+       u8      reserved_2[3];
+       u8      stream_csi[RTL8723A_MAX_RF_PATHS];
+       u8      stream_target_csi[RTL8723A_MAX_RF_PATHS];
+       s8      sig_evm;
+       u8      reserved_3;
+
+#ifdef __LITTLE_ENDIAN
+       u8      antsel_rx_keep_2:1;     /* ex_intf_flg:1; */
+       u8      sgi_en:1;
+       u8      rxsc:2;
+       u8      idle_long:1;
+       u8      r_ant_train_en:1;
+       u8      antenna_select_b:1;
+       u8      antenna_select:1;
+#else  /*  _BIG_ENDIAN_ */
+       u8      antenna_select:1;
+       u8      antenna_select_b:1;
+       u8      r_ant_train_en:1;
+       u8      idle_long:1;
+       u8      rxsc:2;
+       u8      sgi_en:1;
+       u8      antsel_rx_keep_2:1;     /* ex_intf_flg:1; */
+#endif
+};
+
+/*
+ * Regs to backup
+ */
+#define RTL8XXXU_ADDA_REGS             16
+#define RTL8XXXU_MAC_REGS              4
+#define RTL8XXXU_BB_REGS               9
+
+struct rtl8xxxu_firmware_header {
+       __le16  signature;              /*  92C0: test chip; 92C,
+                                           88C0: test chip;
+                                           88C1: MP A-cut;
+                                           92C1: MP A-cut */
+       u8      category;               /*  AP/NIC and USB/PCI */
+       u8      function;
+
+       __le16  major_version;          /*  FW Version */
+       u8      minor_version;          /*  FW Subversion, default 0x00 */
+       u8      reserved1;
+
+       u8      month;                  /*  Release time Month field */
+       u8      date;                   /*  Release time Date field */
+       u8      hour;                   /*  Release time Hour field */
+       u8      minute;                 /*  Release time Minute field */
+
+       __le16  ramcodesize;            /*  Size of RAM code */
+       u16     reserved2;
+
+       __le32  svn_idx;                /*  SVN entry index */
+       u32     reserved3;
+
+       u32     reserved4;
+       u32     reserved5;
+
+       u8      data[0];
+};
+
+/*
+ * The 8723au has 3 channel groups: 1-3, 4-9, and 10-14
+ */
+struct rtl8723au_idx {
+#ifdef __LITTLE_ENDIAN
+       int     a:4;
+       int     b:4;
+#else
+       int     b:4;
+       int     a:4;
+#endif
+} __attribute__((packed));
+
+struct rtl8723au_efuse {
+       __le16 rtl_id;
+       u8 res0[0xe];
+       u8 cck_tx_power_index_A[3];     /* 0x10 */
+       u8 cck_tx_power_index_B[3];
+       u8 ht40_1s_tx_power_index_A[3]; /* 0x16 */
+       u8 ht40_1s_tx_power_index_B[3];
+       /*
+        * The following entries are half-bytes split as:
+        * bits 0-3: path A, bits 4-7: path B, all values 4 bits signed
+        */
+       struct rtl8723au_idx ht20_tx_power_index_diff[3];
+       struct rtl8723au_idx ofdm_tx_power_index_diff[3];
+       struct rtl8723au_idx ht40_max_power_offset[3];
+       struct rtl8723au_idx ht20_max_power_offset[3];
+       u8 channel_plan;                /* 0x28 */
+       u8 tssi_a;
+       u8 thermal_meter;
+       u8 rf_regulatory;
+       u8 rf_option_2;
+       u8 rf_option_3;
+       u8 rf_option_4;
+       u8 res7;
+       u8 version                      /* 0x30 */;
+       u8 customer_id_major;
+       u8 customer_id_minor;
+       u8 xtal_k;
+       u8 chipset;                     /* 0x34 */
+       u8 res8[0x82];
+       u8 vid;                         /* 0xb7 */
+       u8 res9;
+       u8 pid;                         /* 0xb9 */
+       u8 res10[0x0c];
+       u8 mac_addr[ETH_ALEN];          /* 0xc6 */
+       u8 res11[2];
+       u8 vendor_name[7];
+       u8 res12[2];
+       u8 device_name[0x29];           /* 0xd7 */
+};
+
+struct rtl8192cu_efuse {
+       __le16 rtl_id;
+       __le16 hpon;
+       u8 res0[2];
+       __le16 clk;
+       __le16 testr;
+       __le16 vid;
+       __le16 did;
+       __le16 svid;
+       __le16 smid;                                            /* 0x10 */
+       u8 res1[4];
+       u8 mac_addr[ETH_ALEN];                                  /* 0x16 */
+       u8 res2[2];
+       u8 vendor_name[7];
+       u8 res3[3];
+       u8 device_name[0x14];                                   /* 0x28 */
+       u8 res4[0x1e];                                          /* 0x3c */
+       u8 cck_tx_power_index_A[3];                             /* 0x5a */
+       u8 cck_tx_power_index_B[3];
+       u8 ht40_1s_tx_power_index_A[3];                         /* 0x60 */
+       u8 ht40_1s_tx_power_index_B[3];
+       /*
+        * The following entries are half-bytes split as:
+        * bits 0-3: path A, bits 4-7: path B, all values 4 bits signed
+        */
+       struct rtl8723au_idx ht40_2s_tx_power_index_diff[3];
+       struct rtl8723au_idx ht20_tx_power_index_diff[3];       /* 0x69 */
+       struct rtl8723au_idx ofdm_tx_power_index_diff[3];
+       struct rtl8723au_idx ht40_max_power_offset[3];          /* 0x6f */
+       struct rtl8723au_idx ht20_max_power_offset[3];
+       u8 channel_plan;                                        /* 0x75 */
+       u8 tssi_a;
+       u8 tssi_b;
+       u8 thermal_meter;       /* xtal_k */                    /* 0x78 */
+       u8 rf_regulatory;
+       u8 rf_option_2;
+       u8 rf_option_3;
+       u8 rf_option_4;
+       u8 res5[1];                                             /* 0x7d */
+       u8 version;
+       u8 customer_id;
+};
+
+struct rtl8xxxu_reg8val {
+       u16 reg;
+       u8 val;
+};
+
+struct rtl8xxxu_reg32val {
+       u16 reg;
+       u32 val;
+};
+
+struct rtl8xxxu_rfregval {
+       u8 reg;
+       u32 val;
+};
+
+enum rtl8xxxu_rfpath {
+       RF_A = 0,
+       RF_B = 1,
+};
+
+struct rtl8xxxu_rfregs {
+       u16 hssiparm1;
+       u16 hssiparm2;
+       u16 lssiparm;
+       u16 hspiread;
+       u16 lssiread;
+       u16 rf_sw_ctrl;
+};
+
+#define H2C_MAX_MBOX                   4
+#define H2C_EXT                                BIT(7)
+#define H2C_SET_POWER_MODE             1
+#define H2C_JOIN_BSS_REPORT            2
+#define  H2C_JOIN_BSS_DISCONNECT       0
+#define  H2C_JOIN_BSS_CONNECT          1
+#define H2C_SET_RSSI                   5
+#define H2C_SET_RATE_MASK              (6 | H2C_EXT)
+
+struct h2c_cmd {
+       union {
+               struct {
+                       u8 cmd;
+                       u8 data[5];
+               } __packed cmd;
+               struct {
+                       __le32 data;
+                       __le16 ext;
+               } __packed raw;
+               struct {
+                       u8 cmd;
+                       u8 data;
+                       u8 pad[4];
+               } __packed joinbss;
+               struct {
+                       u8 cmd;
+                       __le16 mask_hi;
+                       u8 arg;
+                       __le16 mask_lo;
+               } __packed ramask;
+       };
+};
+
+struct rtl8xxxu_fileops;
+
+struct rtl8xxxu_priv {
+       struct ieee80211_hw *hw;
+       struct usb_device *udev;
+       struct rtl8xxxu_fileops *fops;
+
+       spinlock_t tx_urb_lock;
+       struct list_head tx_urb_free_list;
+       int tx_urb_free_count;
+       bool tx_stopped;
+
+       spinlock_t rx_urb_lock;
+       struct list_head rx_urb_pending_list;
+       int rx_urb_pending_count;
+       bool shutdown;
+       struct work_struct rx_urb_wq;
+
+       u8 mac_addr[ETH_ALEN];
+       char chip_name[8];
+       u8 cck_tx_power_index_A[3];     /* 0x10 */
+       u8 cck_tx_power_index_B[3];
+       u8 ht40_1s_tx_power_index_A[3]; /* 0x16 */
+       u8 ht40_1s_tx_power_index_B[3];
+       /*
+        * The following entries are half-bytes split as:
+        * bits 0-3: path A, bits 4-7: path B, all values 4 bits signed
+        */
+       struct rtl8723au_idx ht40_2s_tx_power_index_diff[3];
+       struct rtl8723au_idx ht20_tx_power_index_diff[3];
+       struct rtl8723au_idx ofdm_tx_power_index_diff[3];
+       struct rtl8723au_idx ht40_max_power_offset[3];
+       struct rtl8723au_idx ht20_max_power_offset[3];
+       u32 chip_cut:4;
+       u32 rom_rev:4;
+       u32 has_wifi:1;
+       u32 has_bluetooth:1;
+       u32 enable_bluetooth:1;
+       u32 has_gps:1;
+       u32 hi_pa:1;
+       u32 vendor_umc:1;
+       u32 has_polarity_ctrl:1;
+       u32 has_eeprom:1;
+       u32 boot_eeprom:1;
+       u32 ep_tx_high_queue:1;
+       u32 ep_tx_normal_queue:1;
+       u32 ep_tx_low_queue:1;
+       u32 path_a_hi_power:1;
+       u32 path_a_rf_paths:4;
+       unsigned int pipe_interrupt;
+       unsigned int pipe_in;
+       unsigned int pipe_out[TXDESC_QUEUE_MAX];
+       u8 out_ep[RTL8XXXU_OUT_ENDPOINTS];
+       u8 path_a_ig_value;
+       u8 ep_tx_count;
+       u8 rf_paths;
+       u8 rx_paths;
+       u8 tx_paths;
+       u32 rf_mode_ag[2];
+       u32 rege94;
+       u32 rege9c;
+       u32 regeb4;
+       u32 regebc;
+       int next_mbox;
+       int nr_out_eps;
+
+       struct mutex h2c_mutex;
+
+       struct usb_anchor rx_anchor;
+       struct usb_anchor tx_anchor;
+       struct usb_anchor int_anchor;
+       struct rtl8xxxu_firmware_header *fw_data;
+       size_t fw_size;
+       struct mutex usb_buf_mutex;
+       union {
+               __le32 val32;
+               __le16 val16;
+               u8 val8;
+       } usb_buf;
+       union {
+               u8 raw[EFUSE_MAP_LEN_8723A];
+               struct rtl8723au_efuse efuse8723;
+               struct rtl8192cu_efuse efuse8192;
+       } efuse_wifi;
+       u32 adda_backup[RTL8XXXU_ADDA_REGS];
+       u32 mac_backup[RTL8XXXU_MAC_REGS];
+       u32 bb_backup[RTL8XXXU_BB_REGS];
+       u32 bb_recovery_backup[RTL8XXXU_BB_REGS];
+       u32 rtlchip;
+       u8 pi_enabled:1;
+       u8 iqk_initialized:1;
+       u8 int_buf[USB_INTR_CONTENT_LENGTH];
+};
+
+struct rtl8xxxu_rx_urb {
+       struct urb urb;
+       struct ieee80211_hw *hw;
+       struct list_head list;
+};
+
+struct rtl8xxxu_tx_urb {
+       struct urb urb;
+       struct ieee80211_hw *hw;
+       struct list_head list;
+};
+
+struct rtl8xxxu_fileops {
+       int (*parse_efuse) (struct rtl8xxxu_priv *priv);
+       int (*load_firmware) (struct rtl8xxxu_priv *priv);
+       int (*power_on) (struct rtl8xxxu_priv *priv);
+       int writeN_block_size;
+};
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h
new file mode 100644 (file)
index 0000000..23208f7
--- /dev/null
@@ -0,0 +1,981 @@
+/*
+ * Copyright (c) 2014 - 2015 Jes Sorensen <Jes.Sorensen@redhat.com>
+ *
+ * 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.
+ *
+ * Register definitions taken from original Realtek rtl8723au driver
+ */
+
+/* 0x0000 ~ 0x00FF     System Configuration */
+#define REG_SYS_ISO_CTRL               0x0000
+#define  SYS_ISO_MD2PP                 BIT(0)
+#define  SYS_ISO_ANALOG_IPS            BIT(5)
+#define  SYS_ISO_DIOR                  BIT(9)
+#define  SYS_ISO_PWC_EV25V             BIT(14)
+#define  SYS_ISO_PWC_EV12V             BIT(15)
+
+#define REG_SYS_FUNC                   0x0002
+#define  SYS_FUNC_BBRSTB               BIT(0)
+#define  SYS_FUNC_BB_GLB_RSTN          BIT(1)
+#define  SYS_FUNC_USBA                 BIT(2)
+#define  SYS_FUNC_UPLL                 BIT(3)
+#define  SYS_FUNC_USBD                 BIT(4)
+#define  SYS_FUNC_DIO_PCIE             BIT(5)
+#define  SYS_FUNC_PCIEA                        BIT(6)
+#define  SYS_FUNC_PPLL                 BIT(7)
+#define  SYS_FUNC_PCIED                        BIT(8)
+#define  SYS_FUNC_DIOE                 BIT(9)
+#define  SYS_FUNC_CPU_ENABLE           BIT(10)
+#define  SYS_FUNC_DCORE                        BIT(11)
+#define  SYS_FUNC_ELDR                 BIT(12)
+#define  SYS_FUNC_DIO_RF               BIT(13)
+#define  SYS_FUNC_HWPDN                        BIT(14)
+#define  SYS_FUNC_MREGEN               BIT(15)
+
+#define REG_APS_FSMCO                  0x0004
+#define  APS_FSMCO_PFM_ALDN            BIT(1)
+#define  APS_FSMCO_PFM_WOWL            BIT(3)
+#define  APS_FSMCO_ENABLE_POWERDOWN    BIT(4)
+#define  APS_FSMCO_MAC_ENABLE          BIT(8)
+#define  APS_FSMCO_MAC_OFF             BIT(9)
+#define  APS_FSMCO_HW_SUSPEND          BIT(11)
+#define  APS_FSMCO_PCIE                        BIT(12)
+#define  APS_FSMCO_HW_POWERDOWN                BIT(15)
+#define  APS_FSMCO_WLON_RESET          BIT(16)
+
+#define REG_SYS_CLKR                   0x0008
+#define  SYS_CLK_ANAD16V_ENABLE                BIT(0)
+#define  SYS_CLK_ANA8M                 BIT(1)
+#define  SYS_CLK_MACSLP                        BIT(4)
+#define  SYS_CLK_LOADER_ENABLE         BIT(5)
+#define  SYS_CLK_80M_SSC_DISABLE       BIT(7)
+#define  SYS_CLK_80M_SSC_ENABLE_HO     BIT(8)
+#define  SYS_CLK_PHY_SSC_RSTB          BIT(9)
+#define  SYS_CLK_SEC_CLK_ENABLE                BIT(10)
+#define  SYS_CLK_MAC_CLK_ENABLE                BIT(11)
+#define  SYS_CLK_ENABLE                        BIT(12)
+#define  SYS_CLK_RING_CLK_ENABLE       BIT(13)
+
+#define REG_9346CR                     0x000a
+#define  EEPROM_BOOT                   BIT(4)
+#define  EEPROM_ENABLE                 BIT(5)
+
+#define REG_EE_VPD                     0x000c
+#define REG_AFE_MISC                   0x0010
+#define REG_SPS0_CTRL                  0x0011
+#define REG_SPS_OCP_CFG                        0x0018
+#define REG_RSV_CTRL                   0x001c
+
+#define REG_RF_CTRL                    0x001f
+#define  RF_ENABLE                     BIT(0)
+#define  RF_RSTB                       BIT(1)
+#define  RF_SDMRSTB                    BIT(2)
+
+#define REG_LDOA15_CTRL                        0x0020
+#define  LDOA15_ENABLE                 BIT(0)
+#define  LDOA15_STANDBY                        BIT(1)
+#define  LDOA15_OBUF                   BIT(2)
+#define  LDOA15_REG_VOS                        BIT(3)
+#define  LDOA15_VOADJ_SHIFT            4
+
+#define REG_LDOV12D_CTRL               0x0021
+#define  LDOV12D_ENABLE                        BIT(0)
+#define  LDOV12D_STANDBY               BIT(1)
+#define  LDOV12D_VADJ_SHIFT            4
+
+#define REG_LDOHCI12_CTRL              0x0022
+
+#define REG_LPLDO_CTRL                 0x0023
+#define  LPLDO_HSM                     BIT(2)
+#define  LPLDO_LSM_DIS                 BIT(3)
+
+#define REG_AFE_XTAL_CTRL              0x0024
+#define  AFE_XTAL_ENABLE               BIT(0)
+#define  AFE_XTAL_B_SELECT             BIT(1)
+#define  AFE_XTAL_GATE_USB             BIT(8)
+#define  AFE_XTAL_GATE_AFE             BIT(11)
+#define  AFE_XTAL_RF_GATE              BIT(14)
+#define  AFE_XTAL_GATE_DIG             BIT(17)
+#define  AFE_XTAL_BT_GATE              BIT(20)
+
+#define REG_AFE_PLL_CTRL               0x0028
+#define  AFE_PLL_ENABLE                        BIT(0)
+#define  AFE_PLL_320_ENABLE            BIT(1)
+#define  APE_PLL_FREF_SELECT           BIT(2)
+#define  AFE_PLL_EDGE_SELECT           BIT(3)
+#define  AFE_PLL_WDOGB                 BIT(4)
+#define  AFE_PLL_LPF_ENABLE            BIT(5)
+
+#define REG_MAC_PHY_CTRL               0x002c
+
+#define REG_EFUSE_CTRL                 0x0030
+#define REG_EFUSE_TEST                 0x0034
+#define  EFUSE_TRPT                    BIT(7)
+       /*  00: Wifi Efuse, 01: BT Efuse0, 10: BT Efuse1, 11: BT Efuse2 */
+#define  EFUSE_CELL_SEL                        (BIT(8) | BIT(9))
+#define  EFUSE_LDOE25_ENABLE           BIT(31)
+#define  EFUSE_SELECT_MASK             0x0300
+#define  EFUSE_WIFI_SELECT             0x0000
+#define  EFUSE_BT0_SELECT              0x0100
+#define  EFUSE_BT1_SELECT              0x0200
+#define  EFUSE_BT2_SELECT              0x0300
+
+#define  EFUSE_ACCESS_ENABLE           0x69    /* RTL8723 only */
+#define  EFUSE_ACCESS_DISABLE          0x00    /* RTL8723 only */
+
+#define REG_PWR_DATA                   0x0038
+#define REG_CAL_TIMER                  0x003c
+#define REG_ACLK_MON                   0x003e
+#define REG_GPIO_MUXCFG                        0x0040
+#define REG_GPIO_IO_SEL                        0x0042
+#define REG_MAC_PINMUX_CFG             0x0043
+#define REG_GPIO_PIN_CTRL              0x0044
+#define REG_GPIO_INTM                  0x0048
+#define REG_LEDCFG0                    0x004c
+#define REG_LEDCFG1                    0x004d
+#define REG_LEDCFG2                    0x004e
+#define  LEDCFG2_DPDT_SELECT           BIT(7)
+#define REG_LEDCFG3                    0x004f
+#define REG_LEDCFG                     REG_LEDCFG2
+#define REG_FSIMR                      0x0050
+#define REG_FSISR                      0x0054
+#define REG_HSIMR                      0x0058
+#define REG_HSISR                      0x005c
+/*  RTL8723 WIFI/BT/GPS Multi-Function GPIO Pin Control. */
+#define REG_GPIO_PIN_CTRL_2            0x0060
+/*  RTL8723 WIFI/BT/GPS Multi-Function GPIO Select. */
+#define REG_GPIO_IO_SEL_2              0x0062
+
+/*  RTL8723 only WIFI/BT/GPS Multi-Function control source. */
+#define REG_MULTI_FUNC_CTRL            0x0068
+
+#define  MULTI_FN_WIFI_HW_PWRDOWN_EN   BIT(0)  /* Enable GPIO[9] as WiFi HW
+                                                  powerdown source */
+#define  MULTI_FN_WIFI_HW_PWRDOWN_SL   BIT(1)  /* WiFi HW powerdown polarity
+                                                  control */
+#define  MULTI_WIFI_FUNC_EN            BIT(2)  /* WiFi function enable */
+
+#define  MULTI_WIFI_HW_ROF_EN          BIT(3)  /* Enable GPIO[9] as WiFi RF HW
+                                                  powerdown source */
+#define  MULTI_BT_HW_PWRDOWN_EN                BIT(16) /* Enable GPIO[11] as BT HW
+                                                  powerdown source */
+#define  MULTI_BT_HW_PWRDOWN_SL                BIT(17) /* BT HW powerdown polarity
+                                                  control */
+#define  MULTI_BT_FUNC_EN              BIT(18) /* BT function enable */
+#define  MULTI_BT_HW_ROF_EN            BIT(19) /* Enable GPIO[11] as BT/GPS
+                                                  RF HW powerdown source */
+#define  MULTI_GPS_HW_PWRDOWN_EN       BIT(20) /* Enable GPIO[10] as GPS HW
+                                                  powerdown source */
+#define  MULTI_GPS_HW_PWRDOWN_SL       BIT(21) /* GPS HW powerdown polarity
+                                                  control */
+#define  MULTI_GPS_FUNC_EN             BIT(22) /* GPS function enable */
+
+#define REG_MCU_FW_DL                  0x0080
+#define  MCU_FW_DL_ENABLE              BIT(0)
+#define  MCU_FW_DL_READY               BIT(1)
+#define  MCU_FW_DL_CSUM_REPORT         BIT(2)
+#define  MCU_MAC_INIT_READY            BIT(3)
+#define  MCU_BB_INIT_READY             BIT(4)
+#define  MCU_RF_INIT_READY             BIT(5)
+#define  MCU_WINT_INIT_READY           BIT(6)
+#define  MCU_FW_RAM_SEL                        BIT(7)  /* 1: RAM, 0:ROM */
+#define  MCU_CP_RESET                  BIT(23)
+
+#define REG_HMBOX_EXT_0                        0x0088
+#define REG_HMBOX_EXT_1                        0x008a
+#define REG_HMBOX_EXT_2                        0x008c
+#define REG_HMBOX_EXT_3                        0x008e
+/*  Host suspend counter on FPGA platform */
+#define REG_HOST_SUSP_CNT              0x00bc
+/*  Efuse access protection for RTL8723 */
+#define REG_EFUSE_ACCESS               0x00cf
+#define REG_BIST_SCAN                  0x00d0
+#define REG_BIST_RPT                   0x00d4
+#define REG_BIST_ROM_RPT               0x00d8
+#define REG_USB_SIE_INTF               0x00e0
+#define REG_PCIE_MIO_INTF              0x00e4
+#define REG_PCIE_MIO_INTD              0x00e8
+#define REG_HPON_FSM                   0x00ec
+#define  HPON_FSM_BONDING_MASK         (BIT(22) | BIT(23))
+#define  HPON_FSM_BONDING_1T2R         BIT(22)
+#define REG_SYS_CFG                    0x00f0
+#define  SYS_CFG_XCLK_VLD              BIT(0)
+#define  SYS_CFG_ACLK_VLD              BIT(1)
+#define  SYS_CFG_UCLK_VLD              BIT(2)
+#define  SYS_CFG_PCLK_VLD              BIT(3)
+#define  SYS_CFG_PCIRSTB               BIT(4)
+#define  SYS_CFG_V15_VLD               BIT(5)
+#define  SYS_CFG_TRP_B15V_EN           BIT(7)
+#define  SYS_CFG_SIC_IDLE              BIT(8)
+#define  SYS_CFG_BD_MAC2               BIT(9)
+#define  SYS_CFG_BD_MAC1               BIT(10)
+#define  SYS_CFG_IC_MACPHY_MODE                BIT(11)
+#define  SYS_CFG_CHIP_VER              (BIT(12) | BIT(13) | BIT(14) | BIT(15))
+#define  SYS_CFG_BT_FUNC               BIT(16)
+#define  SYS_CFG_VENDOR_ID             BIT(19)
+#define  SYS_CFG_PAD_HWPD_IDN          BIT(22)
+#define  SYS_CFG_TRP_VAUX_EN           BIT(23)
+#define  SYS_CFG_TRP_BT_EN             BIT(24)
+#define  SYS_CFG_BD_PKG_SEL            BIT(25)
+#define  SYS_CFG_BD_HCI_SEL            BIT(26)
+#define  SYS_CFG_TYPE_ID               BIT(27)
+#define  SYS_CFG_RTL_ID                        BIT(23) /*  TestChip ID,
+                                                   1:Test(RLE); 0:MP(RL) */
+#define  SYS_CFG_SPS_SEL               BIT(24) /*  1:LDO regulator mode;
+                                                   0:Switching regulator mode*/
+#define  SYS_CFG_CHIP_VERSION_MASK     0xf000  /* Bit 12 - 15 */
+#define  SYS_CFG_CHIP_VERSION_SHIFT    12
+
+#define REG_GPIO_OUTSTS                        0x00f4  /*  For RTL8723 only. */
+#define  GPIO_EFS_HCI_SEL              (BIT(0) | BIT(1))
+#define  GPIO_PAD_HCI_SEL              (BIT(2) | BIT(3))
+#define  GPIO_HCI_SEL                  (BIT(4) | BIT(5))
+#define  GPIO_PKG_SEL_HCI              BIT(6)
+#define  GPIO_FEN_GPS                  BIT(7)
+#define  GPIO_FEN_BT                   BIT(8)
+#define  GPIO_FEN_WL                   BIT(9)
+#define  GPIO_FEN_PCI                  BIT(10)
+#define  GPIO_FEN_USB                  BIT(11)
+#define  GPIO_BTRF_HWPDN_N             BIT(12)
+#define  GPIO_WLRF_HWPDN_N             BIT(13)
+#define  GPIO_PDN_BT_N                 BIT(14)
+#define  GPIO_PDN_GPS_N                        BIT(15)
+#define  GPIO_BT_CTL_HWPDN             BIT(16)
+#define  GPIO_GPS_CTL_HWPDN            BIT(17)
+#define  GPIO_PPHY_SUSB                        BIT(20)
+#define  GPIO_UPHY_SUSB                        BIT(21)
+#define  GPIO_PCI_SUSEN                        BIT(22)
+#define  GPIO_USB_SUSEN                        BIT(23)
+#define  GPIO_RF_RL_ID                 (BIT(31) | BIT(30) | BIT(29) | BIT(28))
+
+/* 0x0100 ~ 0x01FF     MACTOP General Configuration */
+#define REG_CR                         0x0100
+#define  CR_HCI_TXDMA_ENABLE           BIT(0)
+#define  CR_HCI_RXDMA_ENABLE           BIT(1)
+#define  CR_TXDMA_ENABLE               BIT(2)
+#define  CR_RXDMA_ENABLE               BIT(3)
+#define  CR_PROTOCOL_ENABLE            BIT(4)
+#define  CR_SCHEDULE_ENABLE            BIT(5)
+#define  CR_MAC_TX_ENABLE              BIT(6)
+#define  CR_MAC_RX_ENABLE              BIT(7)
+#define  CR_SW_BEACON_ENABLE           BIT(8)
+#define  CR_SECURITY_ENABLE            BIT(9)
+#define  CR_CALTIMER_ENABLE            BIT(10)
+
+/* Media Status Register */
+#define REG_MSR                                0x0102
+#define  MSR_LINKTYPE_MASK             0x3
+#define  MSR_LINKTYPE_NONE             0x0
+#define  MSR_LINKTYPE_ADHOC            0x1
+#define  MSR_LINKTYPE_STATION          0x2
+#define  MSR_LINKTYPE_AP               0x3
+
+#define REG_PBP                                0x0104
+#define  PBP_PAGE_SIZE_RX_SHIFT                0
+#define  PBP_PAGE_SIZE_TX_SHIFT                4
+#define  PBP_PAGE_SIZE_64              0x0
+#define  PBP_PAGE_SIZE_128             0x1
+#define  PBP_PAGE_SIZE_256             0x2
+#define  PBP_PAGE_SIZE_512             0x3
+#define  PBP_PAGE_SIZE_1024            0x4
+
+#define REG_TRXDMA_CTRL                        0x010c
+#define  TRXDMA_CTRL_VOQ_SHIFT         4
+#define  TRXDMA_CTRL_VIQ_SHIFT         6
+#define  TRXDMA_CTRL_BEQ_SHIFT         8
+#define  TRXDMA_CTRL_BKQ_SHIFT         10
+#define  TRXDMA_CTRL_MGQ_SHIFT         12
+#define  TRXDMA_CTRL_HIQ_SHIFT         14
+#define  TRXDMA_QUEUE_LOW              1
+#define  TRXDMA_QUEUE_NORMAL           2
+#define  TRXDMA_QUEUE_HIGH             3
+
+#define REG_TRXFF_BNDY                 0x0114
+#define REG_TRXFF_STATUS               0x0118
+#define REG_RXFF_PTR                   0x011c
+#define REG_HIMR                       0x0120
+#define REG_HISR                       0x0124
+#define REG_HIMRE                      0x0128
+#define REG_HISRE                      0x012c
+#define REG_CPWM                       0x012f
+#define REG_FWIMR                      0x0130
+#define REG_FWISR                      0x0134
+#define REG_PKTBUF_DBG_CTRL            0x0140
+#define REG_PKTBUF_DBG_DATA_L          0x0144
+#define REG_PKTBUF_DBG_DATA_H          0x0148
+
+#define REG_TC0_CTRL                   0x0150
+#define REG_TC1_CTRL                   0x0154
+#define REG_TC2_CTRL                   0x0158
+#define REG_TC3_CTRL                   0x015c
+#define REG_TC4_CTRL                   0x0160
+#define REG_TCUNIT_BASE                        0x0164
+#define REG_MBIST_START                        0x0174
+#define REG_MBIST_DONE                 0x0178
+#define REG_MBIST_FAIL                 0x017c
+#define REG_C2HEVT_MSG_NORMAL          0x01a0
+#define REG_C2HEVT_CLEAR               0x01af
+#define REG_C2HEVT_MSG_TEST            0x01b8
+#define REG_MCUTST_1                   0x01c0
+#define REG_FMTHR                      0x01c8
+#define REG_HMTFR                      0x01cc
+#define REG_HMBOX_0                    0x01d0
+#define REG_HMBOX_1                    0x01d4
+#define REG_HMBOX_2                    0x01d8
+#define REG_HMBOX_3                    0x01dc
+
+#define REG_LLT_INIT                   0x01e0
+#define  LLT_OP_INACTIVE               0x0
+#define  LLT_OP_WRITE                  (0x1 << 30)
+#define  LLT_OP_READ                   (0x2 << 30)
+#define  LLT_OP_MASK                   (0x3 << 30)
+
+#define REG_BB_ACCEESS_CTRL            0x01e8
+#define REG_BB_ACCESS_DATA             0x01ec
+
+/* 0x0200 ~ 0x027F     TXDMA Configuration */
+#define REG_RQPN                       0x0200
+#define  RQPN_HI_PQ_SHIFT              0
+#define  RQPN_LO_PQ_SHIFT              8
+#define  RQPN_NORM_PQ_SHIFT            16
+#define  RQPN_LOAD                     BIT(31)
+
+#define REG_FIFOPAGE                   0x0204
+#define REG_TDECTRL                    0x0208
+#define REG_TXDMA_OFFSET_CHK           0x020c
+#define REG_TXDMA_STATUS               0x0210
+#define REG_RQPN_NPQ                   0x0214
+
+/* 0x0280 ~ 0x02FF     RXDMA Configuration */
+#define REG_RXDMA_AGG_PG_TH            0x0280
+#define REG_RXPKT_NUM                  0x0284
+#define REG_RXDMA_STATUS               0x0288
+
+#define REG_RF_BB_CMD_ADDR             0x02c0
+#define REG_RF_BB_CMD_DATA             0x02c4
+
+/*  spec version 11 */
+/* 0x0400 ~ 0x047F     Protocol Configuration */
+#define REG_VOQ_INFORMATION            0x0400
+#define REG_VIQ_INFORMATION            0x0404
+#define REG_BEQ_INFORMATION            0x0408
+#define REG_BKQ_INFORMATION            0x040c
+#define REG_MGQ_INFORMATION            0x0410
+#define REG_HGQ_INFORMATION            0x0414
+#define REG_BCNQ_INFORMATION           0x0418
+
+#define REG_CPU_MGQ_INFORMATION                0x041c
+#define REG_FWHW_TXQ_CTRL              0x0420
+#define  FWHW_TXQ_CTRL_AMPDU_RETRY     BIT(7)
+#define  FWHW_TXQ_CTRL_XMIT_MGMT_ACK   BIT(12)
+
+#define REG_HWSEQ_CTRL                 0x0423
+#define REG_TXPKTBUF_BCNQ_BDNY         0x0424
+#define REG_TXPKTBUF_MGQ_BDNY          0x0425
+#define REG_LIFETIME_EN                        0x0426
+#define REG_MULTI_BCNQ_OFFSET          0x0427
+
+#define REG_SPEC_SIFS                  0x0428
+#define  SPEC_SIFS_CCK_MASK            0x00ff
+#define  SPEC_SIFS_CCK_SHIFT           0
+#define  SPEC_SIFS_OFDM_MASK           0xff00
+#define  SPEC_SIFS_OFDM_SHIFT          8
+
+#define REG_RETRY_LIMIT                        0x042a
+#define  RETRY_LIMIT_LONG_SHIFT                0
+#define  RETRY_LIMIT_LONG_MASK         0x003f
+#define  RETRY_LIMIT_SHORT_SHIFT       8
+#define  RETRY_LIMIT_SHORT_MASK                0x3f00
+
+#define REG_DARFRC                     0x0430
+#define REG_RARFRC                     0x0438
+#define REG_RESPONSE_RATE_SET          0x0440
+#define  RESPONSE_RATE_BITMAP_ALL      0xfffff
+#define  RESPONSE_RATE_RRSR_CCK_ONLY_1M        0xffff1
+#define  RSR_1M                                BIT(0)
+#define  RSR_2M                                BIT(1)
+#define  RSR_5_5M                      BIT(2)
+#define  RSR_11M                       BIT(3)
+#define  RSR_6M                                BIT(4)
+#define  RSR_9M                                BIT(5)
+#define  RSR_12M                       BIT(6)
+#define  RSR_18M                       BIT(7)
+#define  RSR_24M                       BIT(8)
+#define  RSR_36M                       BIT(9)
+#define  RSR_48M                       BIT(10)
+#define  RSR_54M                       BIT(11)
+#define  RSR_MCS0                      BIT(12)
+#define  RSR_MCS1                      BIT(13)
+#define  RSR_MCS2                      BIT(14)
+#define  RSR_MCS3                      BIT(15)
+#define  RSR_MCS4                      BIT(16)
+#define  RSR_MCS5                      BIT(17)
+#define  RSR_MCS6                      BIT(18)
+#define  RSR_MCS7                      BIT(19)
+#define  RSR_RSC_LOWER_SUB_CHANNEL     BIT(21) /* 0x200000 */
+#define  RSR_RSC_UPPER_SUB_CHANNEL     BIT(22) /* 0x400000 */
+#define  RSR_RSC_BANDWIDTH_40M         (RSR_RSC_UPPER_SUB_CHANNEL | \
+                                        RSR_RSC_LOWER_SUB_CHANNEL)
+#define  RSR_ACK_SHORT_PREAMBLE                BIT(23)
+
+#define REG_ARFR0                      0x0444
+#define REG_ARFR1                      0x0448
+#define REG_ARFR2                      0x044c
+#define REG_ARFR3                      0x0450
+#define REG_AGGLEN_LMT                 0x0458
+#define REG_AMPDU_MIN_SPACE            0x045c
+#define REG_TXPKTBUF_WMAC_LBK_BF_HD    0x045d
+#define REG_FAST_EDCA_CTRL             0x0460
+#define REG_RD_RESP_PKT_TH             0x0463
+#define REG_INIRTS_RATE_SEL            0x0480
+#define REG_INIDATA_RATE_SEL           0x0484
+
+#define REG_POWER_STATUS               0x04a4
+#define REG_POWER_STAGE1               0x04b4
+#define REG_POWER_STAGE2               0x04b8
+#define REG_PKT_VO_VI_LIFE_TIME                0x04c0
+#define REG_PKT_BE_BK_LIFE_TIME                0x04c2
+#define REG_STBC_SETTING               0x04c4
+#define REG_PROT_MODE_CTRL             0x04c8
+#define REG_MAX_AGGR_NUM               0x04ca
+#define REG_RTS_MAX_AGGR_NUM           0x04cb
+#define REG_BAR_MODE_CTRL              0x04cc
+#define REG_RA_TRY_RATE_AGG_LMT                0x04cf
+#define REG_NQOS_SEQ                   0x04dc
+#define REG_QOS_SEQ                    0x04de
+#define REG_NEED_CPU_HANDLE            0x04e0
+#define REG_PKT_LOSE_RPT               0x04e1
+#define REG_PTCL_ERR_STATUS            0x04e2
+#define REG_DUMMY                      0x04fc
+
+/* 0x0500 ~ 0x05FF     EDCA Configuration */
+#define REG_EDCA_VO_PARAM              0x0500
+#define REG_EDCA_VI_PARAM              0x0504
+#define REG_EDCA_BE_PARAM              0x0508
+#define REG_EDCA_BK_PARAM              0x050c
+#define  EDCA_PARAM_ECW_MIN_SHIFT      8
+#define  EDCA_PARAM_ECW_MAX_SHIFT      12
+#define  EDCA_PARAM_TXOP_SHIFT         16
+#define REG_BEACON_TCFG                        0x0510
+#define REG_PIFS                       0x0512
+#define REG_RDG_PIFS                   0x0513
+#define REG_SIFS_CCK                   0x0514
+#define REG_SIFS_OFDM                  0x0516
+#define REG_TSFTR_SYN_OFFSET           0x0518
+#define REG_AGGR_BREAK_TIME            0x051a
+#define REG_SLOT                       0x051b
+#define REG_TX_PTCL_CTRL               0x0520
+#define REG_TXPAUSE                    0x0522
+#define REG_DIS_TXREQ_CLR              0x0523
+#define REG_RD_CTRL                    0x0524
+#define REG_TBTT_PROHIBIT              0x0540
+#define REG_RD_NAV_NXT                 0x0544
+#define REG_NAV_PROT_LEN               0x0546
+
+#define REG_BEACON_CTRL                        0x0550
+#define REG_BEACON_CTRL_1              0x0551
+#define  BEACON_ATIM                   BIT(0)
+#define  BEACON_CTRL_MBSSID            BIT(1)
+#define  BEACON_CTRL_TX_BEACON_RPT     BIT(2)
+#define  BEACON_FUNCTION_ENABLE                BIT(3)
+#define  BEACON_DISABLE_TSF_UPDATE     BIT(4)
+
+#define REG_MBID_NUM                   0x0552
+#define REG_DUAL_TSF_RST               0x0553
+#define  DUAL_TSF_RESET_TSF0           BIT(0)
+#define  DUAL_TSF_RESET_TSF1           BIT(1)
+#define  DUAL_TSF_RESET_P2P            BIT(4)
+#define  DUAL_TSF_TX_OK                        BIT(5)
+
+/*  The same as REG_MBSSID_BCN_SPACE */
+#define REG_BCN_INTERVAL               0x0554
+#define REG_MBSSID_BCN_SPACE           0x0554
+
+#define REG_DRIVER_EARLY_INT           0x0558
+#define  DRIVER_EARLY_INT_TIME         5
+
+#define REG_BEACON_DMA_TIME            0x0559
+#define  BEACON_DMA_ATIME_INT_TIME     2
+
+#define REG_ATIMWND                    0x055a
+#define REG_BCN_MAX_ERR                        0x055d
+#define REG_RXTSF_OFFSET_CCK           0x055e
+#define REG_RXTSF_OFFSET_OFDM          0x055f
+#define REG_TSFTR                      0x0560
+#define REG_TSFTR1                     0x0568
+#define REG_INIT_TSFTR                 0x0564
+#define REG_ATIMWND_1                  0x0570
+#define REG_PSTIMER                    0x0580
+#define REG_TIMER0                     0x0584
+#define REG_TIMER1                     0x0588
+#define REG_ACM_HW_CTRL                        0x05c0
+#define  ACM_HW_CTRL_BK                        BIT(0)
+#define  ACM_HW_CTRL_BE                        BIT(1)
+#define  ACM_HW_CTRL_VI                        BIT(2)
+#define  ACM_HW_CTRL_VO                        BIT(3)
+#define REG_ACM_RST_CTRL               0x05c1
+#define REG_ACMAVG                     0x05c2
+#define REG_VO_ADMTIME                 0x05c4
+#define REG_VI_ADMTIME                 0x05c6
+#define REG_BE_ADMTIME                 0x05c8
+#define REG_EDCA_RANDOM_GEN            0x05cc
+#define REG_SCH_TXCMD                  0x05d0
+
+/* define REG_FW_TSF_SYNC_CNT          0x04a0 */
+#define REG_FW_RESET_TSF_CNT_1         0x05fc
+#define REG_FW_RESET_TSF_CNT_0         0x05fd
+#define REG_FW_BCN_DIS_CNT             0x05fe
+
+/* 0x0600 ~ 0x07FF  WMAC Configuration */
+#define REG_APSD_CTRL                  0x0600
+#define  APSD_CTRL_OFF                 BIT(6)
+#define  APSD_CTRL_OFF_STATUS          BIT(7)
+#define REG_BW_OPMODE                  0x0603
+#define  BW_OPMODE_20MHZ               BIT(2)
+#define  BW_OPMODE_5G                  BIT(1)
+#define  BW_OPMODE_11J                 BIT(0)
+
+#define REG_TCR                                0x0604
+
+/* Receive Configuration Register */
+#define REG_RCR                                0x0608
+#define  RCR_ACCEPT_AP                 BIT(0)  /* Accept all unicast packet */
+#define  RCR_ACCEPT_PHYS_MATCH         BIT(1)  /* Accept phys match packet */
+#define  RCR_ACCEPT_MCAST              BIT(2)
+#define  RCR_ACCEPT_BCAST              BIT(3)
+#define  RCR_ACCEPT_ADDR3              BIT(4)  /* Accept address 3 match
+                                                packet */
+#define  RCR_ACCEPT_PM                 BIT(5)  /* Accept power management
+                                                packet */
+#define  RCR_CHECK_BSSID_MATCH         BIT(6)  /* Accept BSSID match packet */
+#define  RCR_CHECK_BSSID_BEACON                BIT(7)  /* Accept BSSID match packet
+                                                (Rx beacon, probe rsp) */
+#define  RCR_ACCEPT_CRC32              BIT(8)  /* Accept CRC32 error packet */
+#define  RCR_ACCEPT_ICV                        BIT(9)  /* Accept ICV error packet */
+#define  RCR_ACCEPT_DATA_FRAME         BIT(11)
+#define  RCR_ACCEPT_CTRL_FRAME         BIT(12)
+#define  RCR_ACCEPT_MGMT_FRAME         BIT(13)
+#define  RCR_HTC_LOC_CTRL              BIT(14) /* MFC<--HTC=1 MFC-->HTC=0 */
+#define  RCR_MFBEN                     BIT(22)
+#define  RCR_LSIGEN                    BIT(23)
+#define  RCR_MULTI_BSSID_ENABLE                BIT(24) /* Enable Multiple BssId */
+#define  RCR_ACCEPT_BA_SSN             BIT(27) /* Accept BA SSN */
+#define  RCR_APPEND_PHYSTAT            BIT(28)
+#define  RCR_APPEND_ICV                        BIT(29)
+#define  RCR_APPEND_MIC                        BIT(30)
+#define  RCR_APPEND_FCS                        BIT(31) /* WMAC append FCS after */
+
+#define REG_RX_PKT_LIMIT               0x060c
+#define REG_RX_DLK_TIME                        0x060d
+#define REG_RX_DRVINFO_SZ              0x060f
+
+#define REG_MACID                      0x0610
+#define REG_BSSID                      0x0618
+#define REG_MAR                                0x0620
+#define REG_MBIDCAMCFG                 0x0628
+
+#define REG_USTIME_EDCA                        0x0638
+#define REG_MAC_SPEC_SIFS              0x063a
+
+/*  20100719 Joseph: Hardware register definition change. (HW datasheet v54) */
+       /*  [15:8]SIFS_R2T_OFDM, [7:0]SIFS_R2T_CCK */
+#define REG_R2T_SIFS                   0x063c
+       /*  [15:8]SIFS_T2T_OFDM, [7:0]SIFS_T2T_CCK */
+#define REG_T2T_SIFS                   0x063e
+#define REG_ACKTO                      0x0640
+#define REG_CTS2TO                     0x0641
+#define REG_EIFS                       0x0642
+
+/* WMA, BA, CCX */
+#define REG_NAV_CTRL                   0x0650
+/* In units of 128us */
+#define REG_NAV_UPPER                  0x0652
+#define  NAV_UPPER_UNIT                        128
+
+#define REG_BACAMCMD                   0x0654
+#define REG_BACAMCONTENT               0x0658
+#define REG_LBDLY                      0x0660
+#define REG_FWDLY                      0x0661
+#define REG_RXERR_RPT                  0x0664
+#define REG_WMAC_TRXPTCL_CTL           0x0668
+
+/*  Security */
+#define REG_CAM_CMD                    0x0670
+#define  CAM_CMD_POLLING               BIT(31)
+#define  CAM_CMD_WRITE                 BIT(16)
+#define  CAM_CMD_KEY_SHIFT             3
+#define REG_CAM_WRITE                  0x0674
+#define  CAM_WRITE_VALID               BIT(15)
+#define REG_CAM_READ                   0x0678
+#define REG_CAM_DEBUG                  0x067c
+#define REG_SECURITY_CFG               0x0680
+#define  SEC_CFG_TX_USE_DEFKEY         BIT(0)
+#define  SEC_CFG_RX_USE_DEFKEY         BIT(1)
+#define  SEC_CFG_TX_SEC_ENABLE         BIT(2)
+#define  SEC_CFG_RX_SEC_ENABLE         BIT(3)
+#define  SEC_CFG_SKBYA2                        BIT(4)
+#define  SEC_CFG_NO_SKMC               BIT(5)
+#define  SEC_CFG_TXBC_USE_DEFKEY       BIT(6)
+#define  SEC_CFG_RXBC_USE_DEFKEY       BIT(7)
+
+/*  Power */
+#define REG_WOW_CTRL                   0x0690
+#define REG_PSSTATUS                   0x0691
+#define REG_PS_RX_INFO                 0x0692
+#define REG_LPNAV_CTRL                 0x0694
+#define REG_WKFMCAM_CMD                        0x0698
+#define REG_WKFMCAM_RWD                        0x069c
+#define REG_RXFLTMAP0                  0x06a0
+#define REG_RXFLTMAP1                  0x06a2
+#define REG_RXFLTMAP2                  0x06a4
+#define REG_BCN_PSR_RPT                        0x06a8
+#define REG_CALB32K_CTRL               0x06ac
+#define REG_PKT_MON_CTRL               0x06b4
+#define REG_BT_COEX_TABLE              0x06c0
+#define REG_WMAC_RESP_TXINFO           0x06d8
+
+#define REG_MACID1                     0x0700
+#define REG_BSSID1                     0x0708
+
+#define REG_FPGA0_RF_MODE              0x0800
+#define  FPGA_RF_MODE                  BIT(0)
+#define  FPGA_RF_MODE_JAPAN            BIT(1)
+#define  FPGA_RF_MODE_CCK              BIT(24)
+#define  FPGA_RF_MODE_OFDM             BIT(25)
+
+#define REG_FPGA0_TX_INFO              0x0804
+#define REG_FPGA0_PSD_FUNC             0x0808
+#define REG_FPGA0_TX_GAIN              0x080c
+#define REG_FPGA0_RF_TIMING1           0x0810
+#define REG_FPGA0_RF_TIMING2           0x0814
+#define REG_FPGA0_POWER_SAVE           0x0818
+#define  FPGA0_PS_LOWER_CHANNEL                BIT(26)
+#define  FPGA0_PS_UPPER_CHANNEL                BIT(27)
+
+#define REG_FPGA0_XA_HSSI_PARM1                0x0820  /* RF 3 wire register */
+#define  FPGA0_HSSI_PARM1_PI           BIT(8)
+#define REG_FPGA0_XA_HSSI_PARM2                0x0824
+#define REG_FPGA0_XB_HSSI_PARM1                0x0828
+#define REG_FPGA0_XB_HSSI_PARM2                0x082c
+#define  FPGA0_HSSI_3WIRE_DATA_LEN     0x800
+#define  FPGA0_HSSI_3WIRE_ADDR_LEN     0x400
+#define  FPGA0_HSSI_PARM2_ADDR_SHIFT   23
+#define  FPGA0_HSSI_PARM2_ADDR_MASK    0x7f800000      /* 0xff << 23 */
+#define  FPGA0_HSSI_PARM2_CCK_HIGH_PWR BIT(9)
+#define  FPGA0_HSSI_PARM2_EDGE_READ    BIT(31)
+
+#define REG_TX_AGC_B_RATE18_06         0x0830
+#define REG_TX_AGC_B_RATE54_24         0x0834
+#define REG_TX_AGC_B_CCK1_55_MCS32     0x0838
+#define REG_TX_AGC_B_MCS03_MCS00       0x083c
+
+#define REG_FPGA0_XA_LSSI_PARM         0x0840
+#define REG_FPGA0_XB_LSSI_PARM         0x0844
+#define  FPGA0_LSSI_PARM_ADDR_SHIFT    20
+#define  FPGA0_LSSI_PARM_ADDR_MASK     0x0ff00000
+#define  FPGA0_LSSI_PARM_DATA_MASK     0x000fffff
+
+#define REG_TX_AGC_B_MCS07_MCS04       0x0848
+#define REG_TX_AGC_B_MCS11_MCS08       0x084c
+
+#define REG_FPGA0_XCD_SWITCH_CTRL      0x085c
+
+#define REG_FPGA0_XA_RF_INT_OE         0x0860  /* RF Channel switch */
+#define REG_FPGA0_XB_RF_INT_OE         0x0864
+#define  FPGA0_INT_OE_ANTENNA_AB_OPEN  0x000
+#define  FPGA0_INT_OE_ANTENNA_A                BIT(8)
+#define  FPGA0_INT_OE_ANTENNA_B                BIT(9)
+#define  FPGA0_INT_OE_ANTENNA_MASK     (FPGA0_INT_OE_ANTENNA_A | \
+                                        FPGA0_INT_OE_ANTENNA_B)
+
+#define REG_TX_AGC_B_MCS15_MCS12       0x0868
+#define REG_TX_AGC_B_CCK11_A_CCK2_11   0x086c
+
+#define REG_FPGA0_XAB_RF_SW_CTRL       0x0870
+#define REG_FPGA0_XA_RF_SW_CTRL                0x0870  /* 16 bit */
+#define REG_FPGA0_XB_RF_SW_CTRL                0x0872  /* 16 bit */
+#define REG_FPGA0_XCD_RF_SW_CTRL       0x0874
+#define REG_FPGA0_XC_RF_SW_CTRL                0x0874  /* 16 bit */
+#define REG_FPGA0_XD_RF_SW_CTRL                0x0876  /* 16 bit */
+#define  FPGA0_RF_3WIRE_DATA           BIT(0)
+#define  FPGA0_RF_3WIRE_CLOC           BIT(1)
+#define  FPGA0_RF_3WIRE_LOAD           BIT(2)
+#define  FPGA0_RF_3WIRE_RW             BIT(3)
+#define  FPGA0_RF_3WIRE_MASK           0xf
+#define  FPGA0_RF_RFENV                        BIT(4)
+#define  FPGA0_RF_TRSW                 BIT(5)  /* Useless now */
+#define  FPGA0_RF_TRSWB                        BIT(6)
+#define  FPGA0_RF_ANTSW                        BIT(8)
+#define  FPGA0_RF_ANTSWB               BIT(9)
+#define  FPGA0_RF_PAPE                 BIT(10)
+#define  FPGA0_RF_PAPE5G               BIT(11)
+#define  FPGA0_RF_BD_CTRL_SHIFT                16
+
+#define REG_FPGA0_XAB_RF_PARM          0x0878  /* Antenna select path in ODM */
+#define REG_FPGA0_XA_RF_PARM           0x0878  /* 16 bit */
+#define REG_FPGA0_XB_RF_PARM           0x087a  /* 16 bit */
+#define REG_FPGA0_XCD_RF_PARM          0x087c
+#define REG_FPGA0_XC_RF_PARM           0x087c  /* 16 bit */
+#define REG_FPGA0_XD_RF_PARM           0x087e  /* 16 bit */
+#define  FPGA0_RF_PARM_RFA_ENABLE      BIT(1)
+#define  FPGA0_RF_PARM_RFB_ENABLE      BIT(17)
+#define  FPGA0_RF_PARM_CLK_GATE                BIT(31)
+
+#define REG_FPGA0_ANALOG1              0x0880
+#define REG_FPGA0_ANALOG2              0x0884
+#define  FPGA0_ANALOG2_20MHZ           BIT(10)
+#define REG_FPGA0_ANALOG3              0x0888
+#define REG_FPGA0_ANALOG4              0x088c
+
+#define REG_FPGA0_XA_LSSI_READBACK     0x08a0  /* Tranceiver LSSI Readback */
+#define REG_FPGA0_XB_LSSI_READBACK     0x08a4
+#define REG_HSPI_XA_READBACK           0x08b8  /* Transceiver A HSPI read */
+#define REG_HSPI_XB_READBACK           0x08bc  /* Transceiver B HSPI read */
+
+#define REG_FPGA1_RF_MODE              0x0900
+
+#define REG_FPGA1_TX_INFO              0x090c
+
+#define REG_CCK0_SYSTEM                        0x0a00
+#define  CCK0_SIDEBAND                 BIT(4)
+
+#define REG_CCK0_AFE_SETTING           0x0a04
+
+#define REG_CONFIG_ANT_A               0x0b68
+#define REG_CONFIG_ANT_B               0x0b6c
+
+#define REG_OFDM0_TRX_PATH_ENABLE      0x0c04
+#define OFDM_RF_PATH_RX_MASK           0x0f
+#define OFDM_RF_PATH_RX_A              BIT(0)
+#define OFDM_RF_PATH_RX_B              BIT(1)
+#define OFDM_RF_PATH_RX_C              BIT(2)
+#define OFDM_RF_PATH_RX_D              BIT(3)
+#define OFDM_RF_PATH_TX_MASK           0xf0
+#define OFDM_RF_PATH_TX_A              BIT(4)
+#define OFDM_RF_PATH_TX_B              BIT(5)
+#define OFDM_RF_PATH_TX_C              BIT(6)
+#define OFDM_RF_PATH_TX_D              BIT(7)
+
+#define REG_OFDM0_TR_MUX_PAR           0x0c08
+
+#define REG_OFDM0_XA_RX_IQ_IMBALANCE   0x0c14
+#define REG_OFDM0_XB_RX_IQ_IMBALANCE   0x0c1c
+
+#define REG_OFDM0_ENERGY_CCA_THRES     0x0c4c
+
+#define REG_OFDM0_XA_AGC_CORE1         0x0c50
+#define REG_OFDM0_XA_AGC_CORE2         0x0c54
+#define REG_OFDM0_XB_AGC_CORE1         0x0c58
+#define REG_OFDM0_XB_AGC_CORE2         0x0c5c
+#define REG_OFDM0_XC_AGC_CORE1         0x0c60
+#define REG_OFDM0_XC_AGC_CORE2         0x0c64
+#define REG_OFDM0_XD_AGC_CORE1         0x0c68
+#define REG_OFDM0_XD_AGC_CORE2         0x0c6c
+#define  OFDM0_X_AGC_CORE1_IGI_MASK    0x0000007F
+
+#define REG_OFDM0_AGC_PARM1            0x0c70
+
+#define REG_OFDM0_AGCR_SSI_TABLE       0x0c78
+
+#define REG_OFDM0_XA_TX_IQ_IMBALANCE   0x0c80
+#define REG_OFDM0_XB_TX_IQ_IMBALANCE   0x0c88
+#define REG_OFDM0_XC_TX_IQ_IMBALANCE   0x0c90
+#define REG_OFDM0_XD_TX_IQ_IMBALANCE   0x0c98
+
+#define REG_OFDM0_XC_TX_AFE            0x0c94
+#define REG_OFDM0_XD_TX_AFE            0x0c9c
+
+#define REG_OFDM0_RX_IQ_EXT_ANTA       0x0ca0
+
+#define REG_OFDM1_LSTF                 0x0d00
+#define  OFDM_LSTF_PRIME_CH_LOW                BIT(10)
+#define  OFDM_LSTF_PRIME_CH_HIGH       BIT(11)
+#define  OFDM_LSTF_PRIME_CH_MASK       (OFDM_LSTF_PRIME_CH_LOW | \
+                                        OFDM_LSTF_PRIME_CH_HIGH)
+#define  OFDM_LSTF_CONTINUE_TX         BIT(28)
+#define  OFDM_LSTF_SINGLE_CARRIER      BIT(29)
+#define  OFDM_LSTF_SINGLE_TONE         BIT(30)
+#define  OFDM_LSTF_MASK                        0x70000000
+
+#define REG_OFDM1_TRX_PATH_ENABLE      0x0d04
+
+#define REG_TX_AGC_A_RATE18_06         0x0e00
+#define REG_TX_AGC_A_RATE54_24         0x0e04
+#define REG_TX_AGC_A_CCK1_MCS32                0x0e08
+#define REG_TX_AGC_A_MCS03_MCS00       0x0e10
+#define REG_TX_AGC_A_MCS07_MCS04       0x0e14
+#define REG_TX_AGC_A_MCS11_MCS08       0x0e18
+#define REG_TX_AGC_A_MCS15_MCS12       0x0e1c
+
+#define REG_FPGA0_IQK                  0x0e28
+
+#define REG_TX_IQK_TONE_A              0x0e30
+#define REG_RX_IQK_TONE_A              0x0e34
+#define REG_TX_IQK_PI_A                        0x0e38
+#define REG_RX_IQK_PI_A                        0x0e3c
+
+#define REG_TX_IQK                     0x0e40
+#define REG_RX_IQK                     0x0e44
+#define REG_IQK_AGC_PTS                        0x0e48
+#define REG_IQK_AGC_RSP                        0x0e4c
+#define REG_TX_IQK_TONE_B              0x0e50
+#define REG_RX_IQK_TONE_B              0x0e54
+#define REG_TX_IQK_PI_B                        0x0e58
+#define REG_RX_IQK_PI_B                        0x0e5c
+#define REG_IQK_AGC_CONT               0x0e60
+
+#define REG_BLUETOOTH                  0x0e6c
+#define REG_RX_WAIT_CCA                        0x0e70
+#define REG_TX_CCK_RFON                        0x0e74
+#define REG_TX_CCK_BBON                        0x0e78
+#define REG_TX_OFDM_RFON               0x0e7c
+#define REG_TX_OFDM_BBON               0x0e80
+#define REG_TX_TO_RX                   0x0e84
+#define REG_TX_TO_TX                   0x0e88
+#define REG_RX_CCK                     0x0e8c
+
+#define REG_TX_POWER_BEFORE_IQK_A      0x0e94
+#define REG_TX_POWER_AFTER_IQK_A       0x0e9c
+
+#define REG_RX_POWER_BEFORE_IQK_A      0x0ea0
+#define REG_RX_POWER_BEFORE_IQK_A_2    0x0ea4
+#define REG_RX_POWER_AFTER_IQK_A       0x0ea8
+#define REG_RX_POWER_AFTER_IQK_A_2     0x0eac
+
+#define REG_TX_POWER_BEFORE_IQK_B      0x0eb4
+#define REG_TX_POWER_AFTER_IQK_B       0x0ebc
+
+#define REG_RX_POWER_BEFORE_IQK_B      0x0ec0
+#define REG_RX_POWER_BEFORE_IQK_B_2    0x0ec4
+#define REG_RX_POWER_AFTER_IQK_B       0x0ec8
+#define REG_RX_POWER_AFTER_IQK_B_2     0x0ecc
+
+#define REG_RX_OFDM                    0x0ed0
+#define REG_RX_WAIT_RIFS               0x0ed4
+#define REG_RX_TO_RX                   0x0ed8
+#define REG_STANDBY                    0x0edc
+#define REG_SLEEP                      0x0ee0
+#define REG_PMPD_ANAEN                 0x0eec
+
+#define REG_FW_START_ADDRESS           0x1000
+
+#define REG_USB_INFO                   0xfe17
+#define REG_USB_HIMR                   0xfe38
+#define  USB_HIMR_TIMEOUT2             BIT(31)
+#define  USB_HIMR_TIMEOUT1             BIT(30)
+#define  USB_HIMR_PSTIMEOUT            BIT(29)
+#define  USB_HIMR_GTINT4               BIT(28)
+#define  USB_HIMR_GTINT3               BIT(27)
+#define  USB_HIMR_TXBCNERR             BIT(26)
+#define  USB_HIMR_TXBCNOK              BIT(25)
+#define  USB_HIMR_TSF_BIT32_TOGGLE     BIT(24)
+#define  USB_HIMR_BCNDMAINT3           BIT(23)
+#define  USB_HIMR_BCNDMAINT2           BIT(22)
+#define  USB_HIMR_BCNDMAINT1           BIT(21)
+#define  USB_HIMR_BCNDMAINT0           BIT(20)
+#define  USB_HIMR_BCNDOK3              BIT(19)
+#define  USB_HIMR_BCNDOK2              BIT(18)
+#define  USB_HIMR_BCNDOK1              BIT(17)
+#define  USB_HIMR_BCNDOK0              BIT(16)
+#define  USB_HIMR_HSISR_IND            BIT(15)
+#define  USB_HIMR_BCNDMAINT_E          BIT(14)
+/* RSVD        BIT(13) */
+#define  USB_HIMR_CTW_END              BIT(12)
+/* RSVD        BIT(11) */
+#define  USB_HIMR_C2HCMD               BIT(10)
+#define  USB_HIMR_CPWM2                        BIT(9)
+#define  USB_HIMR_CPWM                 BIT(8)
+#define  USB_HIMR_HIGHDOK              BIT(7)  /*  High Queue DMA OK
+                                                   Interrupt */
+#define  USB_HIMR_MGNTDOK              BIT(6)  /*  Management Queue DMA OK
+                                                   Interrupt */
+#define  USB_HIMR_BKDOK                        BIT(5)  /*  AC_BK DMA OK Interrupt */
+#define  USB_HIMR_BEDOK                        BIT(4)  /*  AC_BE DMA OK Interrupt */
+#define  USB_HIMR_VIDOK                        BIT(3)  /*  AC_VI DMA OK Interrupt */
+#define  USB_HIMR_VODOK                        BIT(2)  /*  AC_VO DMA Interrupt */
+#define  USB_HIMR_RDU                  BIT(1)  /*  Receive Descriptor
+                                                   Unavailable */
+#define  USB_HIMR_ROK                  BIT(0)  /*  Receive DMA OK Interrupt */
+
+#define REG_USB_SPECIAL_OPTION         0xfe55
+#define REG_USB_DMA_AGG_TO             0xfe5b
+#define REG_USB_AGG_TO                 0xfe5c
+#define REG_USB_AGG_TH                 0xfe5d
+
+#define REG_NORMAL_SIE_VID             0xfe60  /* 0xfe60 - 0xfe61 */
+#define REG_NORMAL_SIE_PID             0xfe62  /* 0xfe62 - 0xfe63 */
+#define REG_NORMAL_SIE_OPTIONAL                0xfe64
+#define REG_NORMAL_SIE_EP              0xfe65  /* 0xfe65 - 0xfe67 */
+#define REG_NORMAL_SIE_EP_TX           0xfe66
+#define  NORMAL_SIE_EP_TX_HIGH_MASK    0x000f
+#define  NORMAL_SIE_EP_TX_NORMAL_MASK  0x00f0
+#define  NORMAL_SIE_EP_TX_LOW_MASK     0x0f00
+
+#define REG_NORMAL_SIE_PHY             0xfe68  /* 0xfe68 - 0xfe6b */
+#define REG_NORMAL_SIE_OPTIONAL2       0xfe6c
+#define REG_NORMAL_SIE_GPS_EP          0xfe6d  /* RTL8723 only */
+#define REG_NORMAL_SIE_MAC_ADDR                0xfe70  /* 0xfe70 - 0xfe75 */
+#define REG_NORMAL_SIE_STRING          0xfe80  /* 0xfe80 - 0xfedf */
+
+/* RF6052 registers */
+#define RF6052_REG_AC                  0x00
+#define RF6052_REG_IQADJ_G1            0x01
+#define RF6052_REG_IQADJ_G2            0x02
+#define RF6052_REG_BS_PA_APSET_G1_G4   0x03
+#define RF6052_REG_BS_PA_APSET_G5_G8   0x04
+#define RF6052_REG_POW_TRSW            0x05
+#define RF6052_REG_GAIN_RX             0x06
+#define RF6052_REG_GAIN_TX             0x07
+#define RF6052_REG_TXM_IDAC            0x08
+#define RF6052_REG_IPA_G               0x09
+#define RF6052_REG_TXBIAS_G            0x0a
+#define RF6052_REG_TXPA_AG             0x0b
+#define RF6052_REG_IPA_A               0x0c
+#define RF6052_REG_TXBIAS_A            0x0d
+#define RF6052_REG_BS_PA_APSET_G9_G11  0x0e
+#define RF6052_REG_BS_IQGEN            0x0f
+#define RF6052_REG_MODE1               0x10
+#define RF6052_REG_MODE2               0x11
+#define RF6052_REG_RX_AGC_HP           0x12
+#define RF6052_REG_TX_AGC              0x13
+#define RF6052_REG_BIAS                        0x14
+#define RF6052_REG_IPA                 0x15
+#define RF6052_REG_TXBIAS              0x16
+#define RF6052_REG_POW_ABILITY         0x17
+#define RF6052_REG_MODE_AG             0x18    /* RF channel and BW switch */
+#define  MODE_AG_CHANNEL_MASK          0x3ff
+#define  MODE_AG_CHANNEL_20MHZ         BIT(10)
+
+#define RF6052_REG_TOP                 0x19
+#define RF6052_REG_RX_G1               0x1a
+#define RF6052_REG_RX_G2               0x1b
+#define RF6052_REG_RX_BB2              0x1c
+#define RF6052_REG_RX_BB1              0x1d
+#define RF6052_REG_RCK1                        0x1e
+#define RF6052_REG_RCK2                        0x1f
+#define RF6052_REG_TX_G1               0x20
+#define RF6052_REG_TX_G2               0x21
+#define RF6052_REG_TX_G3               0x22
+#define RF6052_REG_TX_BB1              0x23
+#define RF6052_REG_T_METER             0x24
+#define RF6052_REG_SYN_G1              0x25    /* RF TX Power control */
+#define RF6052_REG_SYN_G2              0x26    /* RF TX Power control */
+#define RF6052_REG_SYN_G3              0x27    /* RF TX Power control */
+#define RF6052_REG_SYN_G4              0x28    /* RF TX Power control */
+#define RF6052_REG_SYN_G5              0x29    /* RF TX Power control */
+#define RF6052_REG_SYN_G6              0x2a    /* RF TX Power control */
+#define RF6052_REG_SYN_G7              0x2b    /* RF TX Power control */
+#define RF6052_REG_SYN_G8              0x2c    /* RF TX Power control */
+
+#define RF6052_REG_RCK_OS              0x30    /* RF TX PA control */
+
+#define RF6052_REG_TXPA_G1             0x31    /* RF TX PA control */
+#define RF6052_REG_TXPA_G2             0x32    /* RF TX PA control */
+#define RF6052_REG_TXPA_G3             0x33    /* RF TX PA control */
similarity index 99%
rename from drivers/net/wireless/rtlwifi/core.c
rename to drivers/net/wireless/realtek/rtlwifi/core.c
index 585d0883c7e58760eed503de64ced513c8331c82..c925a4dff5995b6934bfc5edcb28a45ec8bbe4c7 100644 (file)
@@ -1373,7 +1373,7 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               enum ieee80211_ampdu_mlme_action action,
                               struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                              u8 buf_size)
+                              u8 buf_size, bool amsdu)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
 
similarity index 99%
rename from drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
rename to drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c
index 25db369b5d18c4e62a9862015f572d18affdf53d..34ce06441d1b625ebacb2cd74641ffc95f820bb4 100644 (file)
@@ -1946,6 +1946,14 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
                rtl_write_word(rtlpriv, REG_RXFLTMAP2, *(u16 *)val);
                mac->rx_data_filter = *(u16 *)val;
                break;
+       case HW_VAR_KEEP_ALIVE:{
+                       u8 array[2];
+                       array[0] = 0xff;
+                       array[1] = *((u8 *)val);
+                       rtl92c_fill_h2c_cmd(hw, H2C_92C_KEEP_ALIVE_CTRL, 2,
+                                           array);
+                       break;
+               }
        default:
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
                         "switch case not processed\n");
index 71a825c750cfc5523fb28d40286cb22888b1bb26..a13d1f2b5912e09e31a4e1b2834ee5eb2d9825bc 100644 (file)
@@ -1236,7 +1236,7 @@ static int set_rts_threshold(struct usbnet *usbdev, u32 rts_threshold)
 
        netdev_dbg(usbdev->net, "%s(): %i\n", __func__, rts_threshold);
 
-       if (rts_threshold < 0 || rts_threshold > 2347)
+       if (rts_threshold == -1 || rts_threshold > 2347)
                rts_threshold = 2347;
 
        tmp = cpu_to_le32(rts_threshold);
index 7e804324bfa786fd60dd5f07b4cb19d80145658d..b5bcc933a2a683df412139b1204fee320096a889 100644 (file)
@@ -664,6 +664,7 @@ static int rsi_mac80211_set_key(struct ieee80211_hw *hw,
  * @tid: Traffic identifier.
  * @ssn: Pointer to ssn value.
  * @buf_size: Buffer size (for kernel version > 2.6.38).
+ * @amsdu: is AMSDU in AMPDU allowed
  *
  * Return: status: 0 on success, negative error code on failure.
  */
@@ -673,7 +674,8 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw,
                                     struct ieee80211_sta *sta,
                                     unsigned short tid,
                                     unsigned short *ssn,
-                                    unsigned char buf_size)
+                                    unsigned char buf_size,
+                                    bool amsdu)
 {
        int status = -EOPNOTSUPP;
        struct rsi_hw *adapter = hw->priv;
index 9524564f873bc8c71e736451a3032a089faa61c1..9733b31a780d380fd2bfd550efe155943b0fdb34 100644 (file)
@@ -7937,7 +7937,7 @@ EXPORT_SYMBOL_GPL(rt2800_get_tsf);
 int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        enum ieee80211_ampdu_mlme_action action,
                        struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                       u8 buf_size)
+                       u8 buf_size, bool amsdu)
 {
        struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv;
        int ret = 0;
index 1609b8a7f7ebcbc801ff3eb71cd27acd7074a968..440790b92b19e2927fd9b9d7446cbd3c8eef4f30 100644 (file)
@@ -220,7 +220,7 @@ u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
 int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        enum ieee80211_ampdu_mlme_action action,
                        struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                       u8 buf_size);
+                       u8 buf_size, bool amsdu);
 int rt2800_get_survey(struct ieee80211_hw *hw, int idx,
                      struct survey_info *survey);
 void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev);
index 48a2cad2947784608eacbe3fb4b884fef3f4d583..7e8bb1198ae9f749dfc3e419f1ae652ffa7a0b8d 100644 (file)
@@ -266,7 +266,7 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
                if (beacon_diff > beacon_int)
                        beacon_diff = 0;
 
-               autowake_timeout = (conf->max_sleep_period * beacon_int) - beacon_diff;
+               autowake_timeout = (conf->ps_dtim_period * beacon_int) - beacon_diff;
                queue_delayed_work(rt2x00dev->workqueue,
                                   &rt2x00dev->autowakeup_work,
                                   autowake_timeout - 15);
index 7c355fff2c5ea50616407545639b32aa7d558d8f..ebed13af9852beac98f26392c3a98e8ab57ff01f 100644 (file)
@@ -350,7 +350,8 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
        cfg->bss_type = SCAN_BSS_TYPE_ANY;
        /* currently NL80211 supports only a single interval */
        for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
-               cfg->intervals[i] = cpu_to_le32(req->interval);
+               cfg->intervals[i] = cpu_to_le32(req->scan_plans[0].interval *
+                                               MSEC_PER_SEC);
 
        cfg->ssid_len = 0;
        ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
index abbf054fb6da892bee387e18345c41e842c01bd2..50cce42089a5eb0358620a46d46d0990af47e6e7 100644 (file)
@@ -2115,3 +2115,4 @@ MODULE_PARM_DESC(num_rx_desc_param,
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
 MODULE_FIRMWARE(WL18XX_FW_NAME);
+MODULE_FIRMWARE(WL18XX_CONF_FILE_NAME);
index c938c494c785703b6f3b4a6a1fed48530028627c..bc15aa2c3efad73582de0fab4a98f6836280272c 100644 (file)
@@ -228,13 +228,15 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
        wl18xx_adjust_channels(cmd, cmd_channels);
 
        if (c->num_short_intervals && c->long_interval &&
-           c->long_interval > req->interval) {
-               cmd->short_cycles_msec = cpu_to_le16(req->interval);
+           c->long_interval > req->scan_plans[0].interval * MSEC_PER_SEC) {
+               cmd->short_cycles_msec =
+                       cpu_to_le16(req->scan_plans[0].interval * MSEC_PER_SEC);
                cmd->long_cycles_msec = cpu_to_le16(c->long_interval);
                cmd->short_cycles_count = c->num_short_intervals;
        } else {
                cmd->short_cycles_msec = 0;
-               cmd->long_cycles_msec = cpu_to_le16(req->interval);
+               cmd->long_cycles_msec =
+                       cpu_to_le16(req->scan_plans[0].interval * MSEC_PER_SEC);
                cmd->short_cycles_count = 0;
        }
        wl1271_debug(DEBUG_SCAN, "short_interval: %d, long_interval: %d, num_short: %d",
index e819369d8f8f43a2ce37a5260c3c8c017c7ad5eb..ec7f6af3fab26bebcac055892937332e0254c1fb 100644 (file)
@@ -5263,7 +5263,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif,
                                  enum ieee80211_ampdu_mlme_action action,
                                  struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                                 u8 buf_size)
+                                 u8 buf_size, bool amsdu)
 {
        struct wl1271 *wl = hw->priv;
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
index a1b6040e6491219544be4054343ff00f6c555db1..906be6aa4eb6fb38d6d39db30841a3ac33280bf7 100644 (file)
@@ -318,7 +318,7 @@ struct wl1271 {
        bool watchdog_recovery;
 
        /* Reg domain last configuration */
-       u32 reg_ch_conf_last[2];
+       u32 reg_ch_conf_last[2]  __aligned(8);
        /* Reg domain pending configuration */
        u32 reg_ch_conf_pending[2];
 
index f821a97d78278feed765d08d886a4665d8795bd5..441b158d04f73c0a7d3a1dbe61cc37a4913eec2a 100644 (file)
@@ -1706,19 +1706,19 @@ static void xennet_destroy_queues(struct netfront_info *info)
 }
 
 static int xennet_create_queues(struct netfront_info *info,
-                               unsigned int num_queues)
+                               unsigned int *num_queues)
 {
        unsigned int i;
        int ret;
 
-       info->queues = kcalloc(num_queues, sizeof(struct netfront_queue),
+       info->queues = kcalloc(*num_queues, sizeof(struct netfront_queue),
                               GFP_KERNEL);
        if (!info->queues)
                return -ENOMEM;
 
        rtnl_lock();
 
-       for (i = 0; i < num_queues; i++) {
+       for (i = 0; i < *num_queues; i++) {
                struct netfront_queue *queue = &info->queues[i];
 
                queue->id = i;
@@ -1728,7 +1728,7 @@ static int xennet_create_queues(struct netfront_info *info,
                if (ret < 0) {
                        dev_warn(&info->netdev->dev,
                                 "only created %d queues\n", i);
-                       num_queues = i;
+                       *num_queues = i;
                        break;
                }
 
@@ -1738,11 +1738,11 @@ static int xennet_create_queues(struct netfront_info *info,
                        napi_enable(&queue->napi);
        }
 
-       netif_set_real_num_tx_queues(info->netdev, num_queues);
+       netif_set_real_num_tx_queues(info->netdev, *num_queues);
 
        rtnl_unlock();
 
-       if (num_queues == 0) {
+       if (*num_queues == 0) {
                dev_err(&info->netdev->dev, "no queues\n");
                return -EINVAL;
        }
@@ -1788,7 +1788,7 @@ static int talk_to_netback(struct xenbus_device *dev,
        if (info->queues)
                xennet_destroy_queues(info);
 
-       err = xennet_create_queues(info, num_queues);
+       err = xennet_create_queues(info, &num_queues);
        if (err < 0)
                goto destroy_ring;
 
@@ -1819,19 +1819,22 @@ again:
                goto destroy_ring;
        }
 
-       if (num_queues == 1) {
-               err = write_queue_xenstore_keys(&info->queues[0], &xbt, 0); /* flat */
-               if (err)
-                       goto abort_transaction_no_dev_fatal;
-       } else {
+       if (xenbus_exists(XBT_NIL,
+                         info->xbdev->otherend, "multi-queue-max-queues")) {
                /* Write the number of queues */
-               err = xenbus_printf(xbt, dev->nodename, "multi-queue-num-queues",
-                                   "%u", num_queues);
+               err = xenbus_printf(xbt, dev->nodename,
+                                   "multi-queue-num-queues", "%u", num_queues);
                if (err) {
                        message = "writing multi-queue-num-queues";
                        goto abort_transaction_no_dev_fatal;
                }
+       }
 
+       if (num_queues == 1) {
+               err = write_queue_xenstore_keys(&info->queues[0], &xbt, 0); /* flat */
+               if (err)
+                       goto abort_transaction_no_dev_fatal;
+       } else {
                /* Write the keys for each queue */
                for (i = 0; i < num_queues; ++i) {
                        queue = &info->queues[i];
index 6639cd1cae368a676fb54b957b6472c3b187c539..0d6003dee3af6fb23ad2224dc7870958dde72f1f 100644 (file)
@@ -68,6 +68,7 @@ config NFC_PORT100
 
          If unsure, say N.
 
+source "drivers/nfc/fdp/Kconfig"
 source "drivers/nfc/pn544/Kconfig"
 source "drivers/nfc/microread/Kconfig"
 source "drivers/nfc/nfcmrvl/Kconfig"
index 2757fe1b8aa52b0047babfb363204c32aaf84e94..e3621416a48ef2d180a945daaec0635277f28b39 100644 (file)
@@ -2,6 +2,7 @@
 # Makefile for nfc devices
 #
 
+obj-$(CONFIG_NFC_FDP)          += fdp/
 obj-$(CONFIG_NFC_PN544)                += pn544/
 obj-$(CONFIG_NFC_MICROREAD)    += microread/
 obj-$(CONFIG_NFC_PN533)                += pn533.o
diff --git a/drivers/nfc/fdp/Kconfig b/drivers/nfc/fdp/Kconfig
new file mode 100644 (file)
index 0000000..fbccd9d
--- /dev/null
@@ -0,0 +1,23 @@
+config NFC_FDP
+       tristate "Intel FDP NFC driver"
+       depends on NFC_NCI
+       select CRC_CCITT
+       default n
+       ---help---
+         Intel Fields Peak NFC controller core driver.
+         This is a driver based on the NCI NFC kernel layers.
+
+         To compile this driver as a module, choose m here. The module will
+         be called fdp.
+         Say N if unsure.
+
+config NFC_FDP_I2C
+       tristate "NFC FDP i2c support"
+       depends on NFC_FDP && I2C
+       ---help---
+         This module adds support for the Intel Fields Peak NFC controller
+         i2c interface.
+         Select this if your platform is using the i2c bus.
+
+         If you choose to build a module, it'll be called fdp_i2c.
+         Say N if unsure.
diff --git a/drivers/nfc/fdp/Makefile b/drivers/nfc/fdp/Makefile
new file mode 100644 (file)
index 0000000..e79d51b
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for FDP NCI based NFC driver
+#
+
+obj-$(CONFIG_NFC_FDP)     += fdp.o
+obj-$(CONFIG_NFC_FDP_I2C) += fdp_i2c.o
+
+fdp_i2c-objs  = i2c.o
+
diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c
new file mode 100644 (file)
index 0000000..ccb07a1
--- /dev/null
@@ -0,0 +1,817 @@
+/* -------------------------------------------------------------------------
+ * Copyright (C) 2014-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 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/module.h>
+#include <linux/nfc.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <net/nfc/nci_core.h>
+
+#include "fdp.h"
+
+#define FDP_OTP_PATCH_NAME                     "otp.bin"
+#define FDP_RAM_PATCH_NAME                     "ram.bin"
+#define FDP_FW_HEADER_SIZE                     576
+#define FDP_FW_UPDATE_SLEEP                    1000
+
+#define NCI_GET_VERSION_TIMEOUT                        8000
+#define NCI_PATCH_REQUEST_TIMEOUT              8000
+#define FDP_PATCH_CONN_DEST                    0xC2
+#define FDP_PATCH_CONN_PARAM_TYPE              0xA0
+
+#define NCI_PATCH_TYPE_RAM                     0x00
+#define NCI_PATCH_TYPE_OTP                     0x01
+#define NCI_PATCH_TYPE_EOT                     0xFF
+
+#define NCI_PARAM_ID_FW_RAM_VERSION            0xA0
+#define NCI_PARAM_ID_FW_OTP_VERSION            0xA1
+#define NCI_PARAM_ID_OTP_LIMITED_VERSION       0xC5
+#define NCI_PARAM_ID_KEY_INDEX_ID              0xC6
+
+#define NCI_GID_PROP                           0x0F
+#define NCI_OP_PROP_PATCH_OID                  0x08
+#define NCI_OP_PROP_SET_PDATA_OID              0x23
+
+struct fdp_nci_info {
+       struct nfc_phy_ops *phy_ops;
+       struct fdp_i2c_phy *phy;
+       struct nci_dev *ndev;
+
+       const struct firmware *otp_patch;
+       const struct firmware *ram_patch;
+       u32 otp_patch_version;
+       u32 ram_patch_version;
+
+       u32 otp_version;
+       u32 ram_version;
+       u32 limited_otp_version;
+       u8 key_index;
+
+       u8 *fw_vsc_cfg;
+       u8 clock_type;
+       u32 clock_freq;
+
+       atomic_t data_pkt_counter;
+       void (*data_pkt_counter_cb)(struct nci_dev *ndev);
+       u8 setup_patch_sent;
+       u8 setup_patch_ntf;
+       u8 setup_patch_status;
+       u8 setup_reset_ntf;
+       wait_queue_head_t setup_wq;
+};
+
+static u8 nci_core_get_config_otp_ram_version[5] = {
+       0x04,
+       NCI_PARAM_ID_FW_RAM_VERSION,
+       NCI_PARAM_ID_FW_OTP_VERSION,
+       NCI_PARAM_ID_OTP_LIMITED_VERSION,
+       NCI_PARAM_ID_KEY_INDEX_ID
+};
+
+struct nci_core_get_config_rsp {
+       u8 status;
+       u8 count;
+       u8 data[0];
+};
+
+static int fdp_nci_create_conn(struct nci_dev *ndev)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct core_conn_create_dest_spec_params param;
+       int r;
+
+       /* proprietary destination specific paramerer without value */
+       param.type = FDP_PATCH_CONN_PARAM_TYPE;
+       param.length = 0x00;
+
+       r = nci_core_conn_create(info->ndev, FDP_PATCH_CONN_DEST, 1,
+                                sizeof(param), &param);
+       if (r)
+               return r;
+
+       return nci_get_conn_info_by_id(ndev, 0);
+}
+
+static inline int fdp_nci_get_versions(struct nci_dev *ndev)
+{
+       return nci_core_cmd(ndev, NCI_OP_CORE_GET_CONFIG_CMD,
+                           sizeof(nci_core_get_config_otp_ram_version),
+                           (__u8 *) &nci_core_get_config_otp_ram_version);
+}
+
+static inline int fdp_nci_patch_cmd(struct nci_dev *ndev, u8 type)
+{
+       return nci_prop_cmd(ndev, NCI_OP_PROP_PATCH_OID, sizeof(type), &type);
+}
+
+static inline int fdp_nci_set_production_data(struct nci_dev *ndev, u8 len,
+                                             char *data)
+{
+       return nci_prop_cmd(ndev, NCI_OP_PROP_SET_PDATA_OID, len, data);
+}
+
+static int fdp_nci_set_clock(struct nci_dev *ndev, u8 clock_type,
+                            u32 clock_freq)
+{
+       u32 fc = 13560;
+       u32 nd, num, delta;
+       char data[9];
+
+       nd = (24 * fc) / clock_freq;
+       delta = 24 * fc - nd * clock_freq;
+       num = (32768 * delta) / clock_freq;
+
+       data[0] = 0x00;
+       data[1] = 0x00;
+       data[2] = 0x00;
+
+       data[3] = 0x10;
+       data[4] = 0x04;
+       data[5] = num & 0xFF;
+       data[6] = (num >> 8) & 0xff;
+       data[7] = nd;
+       data[8] = clock_type;
+
+       return fdp_nci_set_production_data(ndev, 9, data);
+}
+
+static void fdp_nci_send_patch_cb(struct nci_dev *ndev)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+
+       info->setup_patch_sent = 1;
+       wake_up(&info->setup_wq);
+}
+
+/**
+ * Register a packet sent counter and a callback
+ *
+ * We have no other way of knowing when all firmware packets were sent out
+ * on the i2c bus. We need to know that in order to close the connection and
+ * send the patch end message.
+ */
+static void fdp_nci_set_data_pkt_counter(struct nci_dev *ndev,
+                                 void (*cb)(struct nci_dev *ndev), int count)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+
+       dev_dbg(dev, "NCI data pkt counter %d\n", count);
+       atomic_set(&info->data_pkt_counter, count);
+       info->data_pkt_counter_cb = cb;
+}
+
+/**
+ * The device is expecting a stream of packets. All packets need to
+ * have the PBF flag set to 0x0 (last packet) even if the firmware
+ * file is segmented and there are multiple packets. If we give the
+ * whole firmware to nci_send_data it will segment it and it will set
+ * the PBF flag to 0x01 so we need to do the segmentation here.
+ *
+ * The firmware will be analyzed and applied when we send NCI_OP_PROP_PATCH_CMD
+ * command with NCI_PATCH_TYPE_EOT parameter. The device will send a
+ * NFCC_PATCH_NTF packaet and a NCI_OP_CORE_RESET_NTF packet.
+ */
+static int fdp_nci_send_patch(struct nci_dev *ndev, u8 conn_id, u8 type)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       const struct firmware *fw;
+       struct sk_buff *skb;
+       unsigned long len;
+       u8 max_size, payload_size;
+       int rc = 0;
+
+       if ((type == NCI_PATCH_TYPE_OTP && !info->otp_patch) ||
+           (type == NCI_PATCH_TYPE_RAM && !info->ram_patch))
+               return -EINVAL;
+
+       if (type == NCI_PATCH_TYPE_OTP)
+               fw = info->otp_patch;
+       else
+               fw = info->ram_patch;
+
+       max_size = nci_conn_max_data_pkt_payload_size(ndev, conn_id);
+       if (max_size <= 0)
+               return -EINVAL;
+
+       len = fw->size;
+
+       fdp_nci_set_data_pkt_counter(ndev, fdp_nci_send_patch_cb,
+                                    DIV_ROUND_UP(fw->size, max_size));
+
+       while (len) {
+
+               payload_size = min_t(unsigned long, (unsigned long) max_size,
+                                    len);
+
+               skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + payload_size),
+                                   GFP_KERNEL);
+               if (!skb) {
+                       fdp_nci_set_data_pkt_counter(ndev, NULL, 0);
+                       return -ENOMEM;
+               }
+
+
+               skb_reserve(skb, NCI_CTRL_HDR_SIZE);
+
+               memcpy(skb_put(skb, payload_size), fw->data + (fw->size - len),
+                      payload_size);
+
+               rc = nci_send_data(ndev, conn_id, skb);
+
+               if (rc) {
+                       fdp_nci_set_data_pkt_counter(ndev, NULL, 0);
+                       return rc;
+               }
+
+               len -= payload_size;
+       }
+
+       return rc;
+}
+
+static int fdp_nci_open(struct nci_dev *ndev)
+{
+       int r;
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       r = info->phy_ops->enable(info->phy);
+
+       return r;
+}
+
+static int fdp_nci_close(struct nci_dev *ndev)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+       return 0;
+}
+
+static int fdp_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (atomic_dec_and_test(&info->data_pkt_counter))
+               info->data_pkt_counter_cb(ndev);
+
+       return info->phy_ops->write(info->phy, skb);
+}
+
+int fdp_nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+       return nci_recv_frame(ndev, skb);
+}
+EXPORT_SYMBOL(fdp_nci_recv_frame);
+
+static int fdp_nci_request_firmware(struct nci_dev *ndev)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+       u8 *data;
+       int r;
+
+       r = request_firmware(&info->ram_patch, FDP_RAM_PATCH_NAME, dev);
+       if (r < 0) {
+               nfc_err(dev, "RAM patch request error\n");
+               goto error;
+       }
+
+       data = (u8 *) info->ram_patch->data;
+       info->ram_patch_version =
+               data[FDP_FW_HEADER_SIZE] |
+               (data[FDP_FW_HEADER_SIZE + 1] << 8) |
+               (data[FDP_FW_HEADER_SIZE + 2] << 16) |
+               (data[FDP_FW_HEADER_SIZE + 3] << 24);
+
+       dev_dbg(dev, "RAM patch version: %d, size: %d\n",
+                 info->ram_patch_version, (int) info->ram_patch->size);
+
+
+       r = request_firmware(&info->otp_patch, FDP_OTP_PATCH_NAME, dev);
+       if (r < 0) {
+               nfc_err(dev, "OTP patch request error\n");
+               goto out;
+       }
+
+       data = (u8 *) info->otp_patch->data;
+       info->otp_patch_version =
+               data[FDP_FW_HEADER_SIZE] |
+               (data[FDP_FW_HEADER_SIZE + 1] << 8) |
+               (data[FDP_FW_HEADER_SIZE+2] << 16) |
+               (data[FDP_FW_HEADER_SIZE+3] << 24);
+
+       dev_dbg(dev, "OTP patch version: %d, size: %d\n",
+                info->otp_patch_version, (int) info->otp_patch->size);
+out:
+       return 0;
+error:
+       return r;
+}
+
+static void fdp_nci_release_firmware(struct nci_dev *ndev)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+
+       if (info->otp_patch) {
+               release_firmware(info->otp_patch);
+               info->otp_patch = NULL;
+       }
+
+       if (info->ram_patch) {
+               release_firmware(info->ram_patch);
+               info->otp_patch = NULL;
+       }
+}
+
+static int fdp_nci_patch_otp(struct nci_dev *ndev)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+       u8 conn_id;
+       int r = 0;
+
+       if (info->otp_version >= info->otp_patch_version)
+               goto out;
+
+       info->setup_patch_sent = 0;
+       info->setup_reset_ntf = 0;
+       info->setup_patch_ntf = 0;
+
+       /* Patch init request */
+       r = fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_OTP);
+       if (r)
+               goto out;
+
+       /* Patch data connection creation */
+       conn_id = fdp_nci_create_conn(ndev);
+       if (conn_id < 0) {
+               r = conn_id;
+               goto out;
+       }
+
+       /* Send the patch over the data connection */
+       r = fdp_nci_send_patch(ndev, conn_id, NCI_PATCH_TYPE_OTP);
+       if (r)
+               goto out;
+
+       /* Wait for all the packets to be send over i2c */
+       wait_event_interruptible(info->setup_wq,
+                                info->setup_patch_sent == 1);
+
+       /* make sure that the NFCC processed the last data packet */
+       msleep(FDP_FW_UPDATE_SLEEP);
+
+       /* Close the data connection */
+       r = nci_core_conn_close(info->ndev, conn_id);
+       if (r)
+               goto out;
+
+       /* Patch finish message */
+       if (fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_EOT)) {
+               nfc_err(dev, "OTP patch error 0x%x\n", r);
+               r = -EINVAL;
+               goto out;
+       }
+
+       /* If the patch notification didn't arrive yet, wait for it */
+       wait_event_interruptible(info->setup_wq, info->setup_patch_ntf);
+
+       /* Check if the patching was successful */
+       r = info->setup_patch_status;
+       if (r) {
+               nfc_err(dev, "OTP patch error 0x%x\n", r);
+               r = -EINVAL;
+               goto out;
+       }
+
+       /*
+        * We need to wait for the reset notification before we
+        * can continue
+        */
+       wait_event_interruptible(info->setup_wq, info->setup_reset_ntf);
+
+out:
+       return r;
+}
+
+static int fdp_nci_patch_ram(struct nci_dev *ndev)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+       u8 conn_id;
+       int r = 0;
+
+       if (info->ram_version >= info->ram_patch_version)
+               goto out;
+
+       info->setup_patch_sent = 0;
+       info->setup_reset_ntf = 0;
+       info->setup_patch_ntf = 0;
+
+       /* Patch init request */
+       r = fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_RAM);
+       if (r)
+               goto out;
+
+       /* Patch data connection creation */
+       conn_id = fdp_nci_create_conn(ndev);
+       if (conn_id < 0) {
+               r = conn_id;
+               goto out;
+       }
+
+       /* Send the patch over the data connection */
+       r = fdp_nci_send_patch(ndev, conn_id, NCI_PATCH_TYPE_RAM);
+       if (r)
+               goto out;
+
+       /* Wait for all the packets to be send over i2c */
+       wait_event_interruptible(info->setup_wq,
+                                info->setup_patch_sent == 1);
+
+       /* make sure that the NFCC processed the last data packet */
+       msleep(FDP_FW_UPDATE_SLEEP);
+
+       /* Close the data connection */
+       r = nci_core_conn_close(info->ndev, conn_id);
+       if (r)
+               goto out;
+
+       /* Patch finish message */
+       if (fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_EOT)) {
+               nfc_err(dev, "RAM patch error 0x%x\n", r);
+               r = -EINVAL;
+               goto out;
+       }
+
+       /* If the patch notification didn't arrive yet, wait for it */
+       wait_event_interruptible(info->setup_wq, info->setup_patch_ntf);
+
+       /* Check if the patching was successful */
+       r = info->setup_patch_status;
+       if (r) {
+               nfc_err(dev, "RAM patch error 0x%x\n", r);
+               r = -EINVAL;
+               goto out;
+       }
+
+       /*
+        * We need to wait for the reset notification before we
+        * can continue
+        */
+       wait_event_interruptible(info->setup_wq, info->setup_reset_ntf);
+
+out:
+       return r;
+}
+
+static int fdp_nci_setup(struct nci_dev *ndev)
+{
+       /* Format: total length followed by an NCI packet */
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+       int r;
+       u8 patched = 0;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       r = nci_core_init(ndev);
+       if (r)
+               goto error;
+
+       /* Get RAM and OTP version */
+       r = fdp_nci_get_versions(ndev);
+       if (r)
+               goto error;
+
+       /* Load firmware from disk */
+       r = fdp_nci_request_firmware(ndev);
+       if (r)
+               goto error;
+
+       /* Update OTP */
+       if (info->otp_version < info->otp_patch_version) {
+               r = fdp_nci_patch_otp(ndev);
+               if (r)
+                       goto error;
+               patched = 1;
+       }
+
+       /* Update RAM */
+       if (info->ram_version < info->ram_patch_version) {
+               r = fdp_nci_patch_ram(ndev);
+               if (r)
+                       goto error;
+               patched = 1;
+       }
+
+       /* Release the firmware buffers */
+       fdp_nci_release_firmware(ndev);
+
+       /* If a patch was applied the new version is checked */
+       if (patched) {
+               r = nci_core_init(ndev);
+               if (r)
+                       goto error;
+
+               r = fdp_nci_get_versions(ndev);
+               if (r)
+                       goto error;
+
+               if (info->otp_version != info->otp_patch_version ||
+                   info->ram_version != info->ram_patch_version) {
+                       nfc_err(dev, "Firmware update failed");
+                       r = -EINVAL;
+                       goto error;
+               }
+       }
+
+       /*
+        * We initialized the devices but the NFC subsystem expects
+        * it to not be initialized.
+        */
+       return nci_core_reset(ndev);
+
+error:
+       fdp_nci_release_firmware(ndev);
+       nfc_err(dev, "Setup error %d\n", r);
+       return r;
+}
+
+static int fdp_nci_post_setup(struct nci_dev *ndev)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+       int r;
+
+       /* Check if the device has VSC */
+       if (info->fw_vsc_cfg && info->fw_vsc_cfg[0]) {
+
+               /* Set the vendor specific configuration */
+               r = fdp_nci_set_production_data(ndev, info->fw_vsc_cfg[3],
+                                               &info->fw_vsc_cfg[4]);
+               if (r) {
+                       nfc_err(dev, "Vendor specific config set error %d\n",
+                               r);
+                       return r;
+               }
+       }
+
+       /* Set clock type and frequency */
+       r = fdp_nci_set_clock(ndev, info->clock_type, info->clock_freq);
+       if (r) {
+               nfc_err(dev, "Clock set error %d\n", r);
+               return r;
+       }
+
+       /*
+        * In order to apply the VSC FDP needs a reset
+        */
+       r = nci_core_reset(ndev);
+       if (r)
+               return r;
+
+       /**
+        * The nci core was initialized when post setup was called
+        * so we leave it like that
+        */
+       return nci_core_init(ndev);
+}
+
+static int fdp_nci_core_reset_ntf_packet(struct nci_dev *ndev,
+                                         struct sk_buff *skb)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+       info->setup_reset_ntf = 1;
+       wake_up(&info->setup_wq);
+
+       return 0;
+}
+
+static int fdp_nci_prop_patch_ntf_packet(struct nci_dev *ndev,
+                                         struct sk_buff *skb)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+       info->setup_patch_ntf = 1;
+       info->setup_patch_status = skb->data[0];
+       wake_up(&info->setup_wq);
+
+       return 0;
+}
+
+static int fdp_nci_prop_patch_rsp_packet(struct nci_dev *ndev,
+                                         struct sk_buff *skb)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+       u8 status = skb->data[0];
+
+       dev_dbg(dev, "%s: status 0x%x\n", __func__, status);
+       nci_req_complete(ndev, status);
+
+       return 0;
+}
+
+static int fdp_nci_prop_set_production_data_rsp_packet(struct nci_dev *ndev,
+                                                       struct sk_buff *skb)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+       u8 status = skb->data[0];
+
+       dev_dbg(dev, "%s: status 0x%x\n", __func__, status);
+       nci_req_complete(ndev, status);
+
+       return 0;
+}
+
+static int fdp_nci_core_get_config_rsp_packet(struct nci_dev *ndev,
+                                               struct sk_buff *skb)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+       struct nci_core_get_config_rsp *rsp = (void *) skb->data;
+       u8 i, *p;
+
+       if (rsp->status == NCI_STATUS_OK) {
+
+               p = rsp->data;
+               for (i = 0; i < 4; i++) {
+
+                       switch (*p++) {
+                       case NCI_PARAM_ID_FW_RAM_VERSION:
+                               p++;
+                               info->ram_version = le32_to_cpup((__le32 *) p);
+                               p += 4;
+                               break;
+                       case NCI_PARAM_ID_FW_OTP_VERSION:
+                               p++;
+                               info->otp_version = le32_to_cpup((__le32 *) p);
+                               p += 4;
+                               break;
+                       case NCI_PARAM_ID_OTP_LIMITED_VERSION:
+                               p++;
+                               info->otp_version = le32_to_cpup((__le32 *) p);
+                               p += 4;
+                               break;
+                       case NCI_PARAM_ID_KEY_INDEX_ID:
+                               p++;
+                               info->key_index = *p++;
+                       }
+               }
+       }
+
+       dev_dbg(dev, "OTP version %d\n", info->otp_version);
+       dev_dbg(dev, "RAM version %d\n", info->ram_version);
+       dev_dbg(dev, "key index %d\n", info->key_index);
+       dev_dbg(dev, "%s: status 0x%x\n", __func__, rsp->status);
+
+       nci_req_complete(ndev, rsp->status);
+
+       return 0;
+}
+
+static struct nci_driver_ops fdp_core_ops[] = {
+       {
+               .opcode = NCI_OP_CORE_GET_CONFIG_RSP,
+               .rsp = fdp_nci_core_get_config_rsp_packet,
+       },
+       {
+               .opcode = NCI_OP_CORE_RESET_NTF,
+               .ntf = fdp_nci_core_reset_ntf_packet,
+       },
+};
+
+static struct nci_driver_ops fdp_prop_ops[] = {
+       {
+               .opcode = nci_opcode_pack(NCI_GID_PROP, NCI_OP_PROP_PATCH_OID),
+               .rsp = fdp_nci_prop_patch_rsp_packet,
+               .ntf = fdp_nci_prop_patch_ntf_packet,
+       },
+       {
+               .opcode = nci_opcode_pack(NCI_GID_PROP,
+                                         NCI_OP_PROP_SET_PDATA_OID),
+               .rsp = fdp_nci_prop_set_production_data_rsp_packet,
+       },
+};
+
+struct nci_ops nci_ops = {
+       .open = fdp_nci_open,
+       .close = fdp_nci_close,
+       .send = fdp_nci_send,
+       .setup = fdp_nci_setup,
+       .post_setup = fdp_nci_post_setup,
+       .prop_ops = fdp_prop_ops,
+       .n_prop_ops = ARRAY_SIZE(fdp_prop_ops),
+       .core_ops = fdp_core_ops,
+       .n_core_ops = ARRAY_SIZE(fdp_core_ops),
+};
+
+int fdp_nci_probe(struct fdp_i2c_phy *phy, struct nfc_phy_ops *phy_ops,
+                       struct nci_dev **ndevp, int tx_headroom,
+                       int tx_tailroom, u8 clock_type, u32 clock_freq,
+                       u8 *fw_vsc_cfg)
+{
+       struct device *dev = &phy->i2c_dev->dev;
+       struct fdp_nci_info *info;
+       struct nci_dev *ndev;
+       u32 protocols;
+       int r;
+
+       info = kzalloc(sizeof(struct fdp_nci_info), GFP_KERNEL);
+       if (!info) {
+               r = -ENOMEM;
+               goto err_info_alloc;
+       }
+
+       info->phy = phy;
+       info->phy_ops = phy_ops;
+       info->clock_type = clock_type;
+       info->clock_freq = clock_freq;
+       info->fw_vsc_cfg = fw_vsc_cfg;
+
+       init_waitqueue_head(&info->setup_wq);
+
+       protocols = NFC_PROTO_JEWEL_MASK |
+                   NFC_PROTO_MIFARE_MASK |
+                   NFC_PROTO_FELICA_MASK |
+                   NFC_PROTO_ISO14443_MASK |
+                   NFC_PROTO_ISO14443_B_MASK |
+                   NFC_PROTO_NFC_DEP_MASK |
+                   NFC_PROTO_ISO15693_MASK;
+
+       ndev = nci_allocate_device(&nci_ops, protocols, tx_headroom,
+                                  tx_tailroom);
+       if (!ndev) {
+               nfc_err(dev, "Cannot allocate nfc ndev\n");
+               r = -ENOMEM;
+               goto err_alloc_ndev;
+       }
+
+       r = nci_register_device(ndev);
+       if (r)
+               goto err_regdev;
+
+       *ndevp = ndev;
+       info->ndev = ndev;
+
+       nci_set_drvdata(ndev, info);
+
+       return 0;
+
+err_regdev:
+       nci_free_device(ndev);
+err_alloc_ndev:
+       kfree(info);
+err_info_alloc:
+       return r;
+}
+EXPORT_SYMBOL(fdp_nci_probe);
+
+void fdp_nci_remove(struct nci_dev *ndev)
+{
+       struct fdp_nci_info *info = nci_get_drvdata(ndev);
+       struct device *dev = &info->phy->i2c_dev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       nci_unregister_device(ndev);
+       nci_free_device(ndev);
+       kfree(info);
+}
+EXPORT_SYMBOL(fdp_nci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NFC NCI driver for Intel Fields Peak NFC controller");
+MODULE_AUTHOR("Robert Dolca <robert.dolca@intel.com>");
diff --git a/drivers/nfc/fdp/fdp.h b/drivers/nfc/fdp/fdp.h
new file mode 100644 (file)
index 0000000..0bd36c0
--- /dev/null
@@ -0,0 +1,38 @@
+/* -------------------------------------------------------------------------
+ * Copyright (C) 2014-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 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.
+ * -------------------------------------------------------------------------
+ */
+
+#ifndef __LOCAL_FDP_H_
+#define __LOCAL_FDP_H_
+
+#include <net/nfc/nci_core.h>
+#include <linux/gpio/consumer.h>
+
+struct fdp_i2c_phy {
+       struct i2c_client *i2c_dev;
+       struct gpio_desc *power_gpio;
+       struct nci_dev *ndev;
+
+       /* < 0 if i2c error occurred */
+       int hard_fault;
+       uint16_t next_read_size;
+};
+
+int fdp_nci_probe(struct fdp_i2c_phy *phy, struct nfc_phy_ops *phy_ops,
+                 struct nci_dev **ndev, int tx_headroom, int tx_tailroom,
+                 u8 clock_type, u32 clock_freq, u8 *fw_vsc_cfg);
+void fdp_nci_remove(struct nci_dev *ndev);
+int fdp_nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
+
+#endif /* __LOCAL_FDP_H_ */
diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c
new file mode 100644 (file)
index 0000000..532db28
--- /dev/null
@@ -0,0 +1,388 @@
+/* -------------------------------------------------------------------------
+ * Copyright (C) 2014-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 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/module.h>
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/nfc.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <net/nfc/nfc.h>
+#include <net/nfc/nci_core.h>
+
+#include "fdp.h"
+
+#define FDP_I2C_DRIVER_NAME    "fdp_nci_i2c"
+
+#define FDP_DP_POWER_GPIO_NAME "power"
+#define FDP_DP_CLOCK_TYPE_NAME "clock-type"
+#define FDP_DP_CLOCK_FREQ_NAME "clock-freq"
+#define FDP_DP_FW_VSC_CFG_NAME "fw-vsc-cfg"
+
+#define FDP_FRAME_HEADROOM     2
+#define FDP_FRAME_TAILROOM     1
+
+#define FDP_NCI_I2C_MIN_PAYLOAD        5
+#define FDP_NCI_I2C_MAX_PAYLOAD        261
+
+#define FDP_POWER_OFF          0
+#define FDP_POWER_ON           1
+
+#define fdp_nci_i2c_dump_skb(dev, prefix, skb)                         \
+       print_hex_dump(KERN_DEBUG, prefix": ", DUMP_PREFIX_OFFSET,      \
+                      16, 1, (skb)->data, (skb)->len, 0)
+
+static void fdp_nci_i2c_reset(struct fdp_i2c_phy *phy)
+{
+       /* Reset RST/WakeUP for at least 100 micro-second */
+       gpiod_set_value_cansleep(phy->power_gpio, FDP_POWER_OFF);
+       usleep_range(1000, 4000);
+       gpiod_set_value_cansleep(phy->power_gpio, FDP_POWER_ON);
+       usleep_range(10000, 14000);
+}
+
+static int fdp_nci_i2c_enable(void *phy_id)
+{
+       struct fdp_i2c_phy *phy = phy_id;
+
+       dev_dbg(&phy->i2c_dev->dev, "%s\n", __func__);
+       fdp_nci_i2c_reset(phy);
+
+       return 0;
+}
+
+static void fdp_nci_i2c_disable(void *phy_id)
+{
+       struct fdp_i2c_phy *phy = phy_id;
+
+       dev_dbg(&phy->i2c_dev->dev, "%s\n", __func__);
+       fdp_nci_i2c_reset(phy);
+}
+
+static void fdp_nci_i2c_add_len_lrc(struct sk_buff *skb)
+{
+       u8 lrc = 0;
+       u16 len, i;
+
+       /* Add length header */
+       len = skb->len;
+       *skb_push(skb, 1) = len & 0xff;
+       *skb_push(skb, 1) = len >> 8;
+
+       /* Compute and add lrc */
+       for (i = 0; i < len + 2; i++)
+               lrc ^= skb->data[i];
+
+       *skb_put(skb, 1) = lrc;
+}
+
+static void fdp_nci_i2c_remove_len_lrc(struct sk_buff *skb)
+{
+       skb_pull(skb, FDP_FRAME_HEADROOM);
+       skb_trim(skb, skb->len - FDP_FRAME_TAILROOM);
+}
+
+static int fdp_nci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+       struct fdp_i2c_phy *phy = phy_id;
+       struct i2c_client *client = phy->i2c_dev;
+       int r;
+
+       if (phy->hard_fault != 0)
+               return phy->hard_fault;
+
+       fdp_nci_i2c_add_len_lrc(skb);
+       fdp_nci_i2c_dump_skb(&client->dev, "fdp_wr", skb);
+
+       r = i2c_master_send(client, skb->data, skb->len);
+       if (r == -EREMOTEIO) {  /* Retry, chip was in standby */
+               usleep_range(1000, 4000);
+               r = i2c_master_send(client, skb->data, skb->len);
+       }
+
+       if (r < 0 || r != skb->len)
+               dev_dbg(&client->dev, "%s: error err=%d len=%d\n",
+                       __func__, r, skb->len);
+
+       if (r >= 0) {
+               if (r != skb->len) {
+                       phy->hard_fault = r;
+                       r = -EREMOTEIO;
+               } else {
+                       r = 0;
+               }
+       }
+
+       fdp_nci_i2c_remove_len_lrc(skb);
+
+       return r;
+}
+
+static struct nfc_phy_ops i2c_phy_ops = {
+       .write = fdp_nci_i2c_write,
+       .enable = fdp_nci_i2c_enable,
+       .disable = fdp_nci_i2c_disable,
+};
+
+static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb)
+{
+       int r, len;
+       u8 tmp[FDP_NCI_I2C_MAX_PAYLOAD], lrc, k;
+       u16 i;
+       struct i2c_client *client = phy->i2c_dev;
+
+       *skb = NULL;
+
+       /* Read the length packet and the data packet */
+       for (k = 0; k < 2; k++) {
+
+               len = phy->next_read_size;
+
+               r = i2c_master_recv(client, tmp, len);
+               if (r != len) {
+                       dev_dbg(&client->dev, "%s: i2c recv err: %d\n",
+                               __func__, r);
+                       goto flush;
+               }
+
+               /* Check packet integruty */
+               for (lrc = i = 0; i < r; i++)
+                       lrc ^= tmp[i];
+
+               /*
+                * LRC check failed. This may due to transmission error or
+                * desynchronization between driver and FDP. Drop the paquet
+                * and force resynchronization
+                */
+               if (lrc) {
+                       dev_dbg(&client->dev, "%s: corrupted packet\n",
+                               __func__);
+                       phy->next_read_size = 5;
+                       goto flush;
+               }
+
+               /* Packet that contains a length */
+               if (tmp[0] == 0 && tmp[1] == 0) {
+                       phy->next_read_size = (tmp[2] << 8) + tmp[3] + 3;
+               } else {
+                       phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
+
+                       *skb = alloc_skb(len, GFP_KERNEL);
+                       if (*skb == NULL) {
+                               r = -ENOMEM;
+                               goto flush;
+                       }
+
+                       memcpy(skb_put(*skb, len), tmp, len);
+                       fdp_nci_i2c_dump_skb(&client->dev, "fdp_rd", *skb);
+
+                       fdp_nci_i2c_remove_len_lrc(*skb);
+               }
+       }
+
+       return 0;
+
+flush:
+       /* Flush the remaining data */
+       if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0)
+               r = -EREMOTEIO;
+
+       return r;
+}
+
+static irqreturn_t fdp_nci_i2c_irq_thread_fn(int irq, void *phy_id)
+{
+       struct fdp_i2c_phy *phy = phy_id;
+       struct i2c_client *client;
+       struct sk_buff *skb;
+       int r;
+
+       client = phy->i2c_dev;
+       dev_dbg(&client->dev, "%s\n", __func__);
+
+       if (!phy || irq != phy->i2c_dev->irq) {
+               WARN_ON_ONCE(1);
+               return IRQ_NONE;
+       }
+
+       r = fdp_nci_i2c_read(phy, &skb);
+
+       if (r == -EREMOTEIO)
+               return IRQ_HANDLED;
+       else if (r == -ENOMEM || r == -EBADMSG)
+               return IRQ_HANDLED;
+
+       if (skb != NULL)
+               fdp_nci_recv_frame(phy->ndev, skb);
+
+       return IRQ_HANDLED;
+}
+
+static void fdp_nci_i2c_read_device_properties(struct device *dev,
+                                              u8 *clock_type, u32 *clock_freq,
+                                              u8 **fw_vsc_cfg)
+{
+       int r;
+       u8 len;
+
+       r = device_property_read_u8(dev, FDP_DP_CLOCK_TYPE_NAME, clock_type);
+       if (r) {
+               dev_dbg(dev, "Using default clock type");
+               *clock_type = 0;
+       }
+
+       r = device_property_read_u32(dev, FDP_DP_CLOCK_FREQ_NAME, clock_freq);
+       if (r) {
+               dev_dbg(dev, "Using default clock frequency\n");
+               *clock_freq = 26000;
+       }
+
+       if (device_property_present(dev, FDP_DP_FW_VSC_CFG_NAME)) {
+               r = device_property_read_u8(dev, FDP_DP_FW_VSC_CFG_NAME,
+                                           &len);
+
+               if (r || len <= 0)
+                       goto vsc_read_err;
+
+               /* Add 1 to the length to inclue the length byte itself */
+               len++;
+
+               *fw_vsc_cfg = devm_kmalloc(dev,
+                                          len * sizeof(**fw_vsc_cfg),
+                                          GFP_KERNEL);
+
+               r = device_property_read_u8_array(dev, FDP_DP_FW_VSC_CFG_NAME,
+                                                 *fw_vsc_cfg, len);
+
+               if (r) {
+                       devm_kfree(dev, fw_vsc_cfg);
+                       goto vsc_read_err;
+               }
+       } else {
+vsc_read_err:
+               dev_dbg(dev, "FW vendor specific commands not present\n");
+               *fw_vsc_cfg = NULL;
+       }
+
+       dev_dbg(dev, "Clock type: %d, clock frequency: %d, VSC: %s",
+               *clock_type, *clock_freq, *fw_vsc_cfg != NULL ? "yes" : "no");
+}
+
+static int fdp_nci_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       struct fdp_i2c_phy *phy;
+       struct device *dev = &client->dev;
+       u8 *fw_vsc_cfg;
+       u8 clock_type;
+       u32 clock_freq;
+       int r = 0;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               nfc_err(dev, "No I2C_FUNC_I2C support\n");
+               return -ENODEV;
+       }
+
+       phy = devm_kzalloc(dev, sizeof(struct fdp_i2c_phy),
+                          GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       phy->i2c_dev = client;
+       phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
+       i2c_set_clientdata(client, phy);
+
+       /* Checking if we have an irq */
+       if (client->irq <= 0) {
+               dev_err(dev, "IRQ not present\n");
+               return -ENODEV;
+       }
+
+       r = request_threaded_irq(client->irq, NULL, fdp_nci_i2c_irq_thread_fn,
+                                IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                FDP_I2C_DRIVER_NAME, phy);
+
+       if (r < 0) {
+               nfc_err(&client->dev, "Unable to register IRQ handler\n");
+               return r;
+       }
+
+       /* Requesting the power gpio */
+       phy->power_gpio = devm_gpiod_get(dev, FDP_DP_POWER_GPIO_NAME,
+                                        GPIOD_OUT_LOW);
+
+       if (IS_ERR(phy->power_gpio)) {
+               nfc_err(dev, "Power GPIO request failed\n");
+               return PTR_ERR(phy->power_gpio);
+       }
+
+       /* read device properties to get the clock and production settings */
+       fdp_nci_i2c_read_device_properties(dev, &clock_type, &clock_freq,
+                                          &fw_vsc_cfg);
+
+       /* Call the NFC specific probe function */
+       r = fdp_nci_probe(phy, &i2c_phy_ops, &phy->ndev,
+                         FDP_FRAME_HEADROOM, FDP_FRAME_TAILROOM,
+                         clock_type, clock_freq, fw_vsc_cfg);
+       if (r < 0) {
+               nfc_err(dev, "NCI probing error\n");
+               return r;
+       }
+
+       dev_dbg(dev, "I2C driver loaded\n");
+       return 0;
+}
+
+static int fdp_nci_i2c_remove(struct i2c_client *client)
+{
+       struct fdp_i2c_phy *phy = i2c_get_clientdata(client);
+
+       dev_dbg(&client->dev, "%s\n", __func__);
+
+       fdp_nci_remove(phy->ndev);
+       fdp_nci_i2c_disable(phy);
+
+       return 0;
+}
+
+static struct i2c_device_id fdp_nci_i2c_id_table[] = {
+       {"int339a", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, fdp_nci_i2c_id_table);
+
+static const struct acpi_device_id fdp_nci_i2c_acpi_match[] = {
+       {"INT339A", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(acpi, fdp_nci_i2c_acpi_match);
+
+static struct i2c_driver fdp_nci_i2c_driver = {
+       .driver = {
+                  .name = FDP_I2C_DRIVER_NAME,
+                  .acpi_match_table = ACPI_PTR(fdp_nci_i2c_acpi_match),
+                 },
+       .id_table = fdp_nci_i2c_id_table,
+       .probe = fdp_nci_i2c_probe,
+       .remove = fdp_nci_i2c_remove,
+};
+module_i2c_driver(fdp_nci_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("I2C driver for Intel Fields Peak NFC controller");
+MODULE_AUTHOR("Robert Dolca <robert.dolca@intel.com>");
index 951d5542f6bc749fea4977d58d19ffd8df2791ba..2c6dbc9f678175d56a25f1bd4651b9772c2138fe 100644 (file)
@@ -1,20 +1,15 @@
 config NFC_MICROREAD
-       tristate "Inside Secure microread NFC driver"
-       depends on NFC_HCI
+       tristate
        select CRC_CCITT
-       default n
        ---help---
          This module contains the main code for Inside Secure microread
          NFC chipsets. It implements the chipset HCI logic and hooks into
          the NFC kernel APIs. Physical layers will register against it.
 
-         To compile this driver as a module, choose m here. The module will
-         be called microread.
-         Say N if unsure.
-
 config NFC_MICROREAD_I2C
-       tristate "NFC Microread i2c support"
-       depends on NFC_MICROREAD && I2C && NFC_SHDLC
+       tristate "Inside Secure Microread device support (I2C)"
+       depends on NFC_HCI && I2C && NFC_SHDLC
+       select NFC_MICROREAD
        ---help---
          This module adds support for the i2c interface of adapters using
          Inside microread chipsets.  Select this if your platform is using
@@ -24,8 +19,9 @@ config NFC_MICROREAD_I2C
          Say N if unsure.
 
 config NFC_MICROREAD_MEI
-       tristate "NFC Microread MEI support"
-       depends on NFC_MICROREAD && NFC_MEI_PHY
+       tristate "Inside Secure Microread device support (MEI)"
+       depends on NFC_HCI && NFC_MEI_PHY
+       select NFC_MICROREAD
        ---help---
          This module adds support for the mei interface of adapters using
          Inside microread chipsets.  Select this if your microread chipset
index 796be241144069494966db68c996945da42b6d9d..444ca94697d96e213d69020f14e4535e0366f3e4 100644 (file)
@@ -1,18 +1,15 @@
 config NFC_MRVL
-       tristate "Marvell NFC driver support"
-       depends on NFC_NCI
+       tristate
        help
          The core driver to support Marvell NFC devices.
 
          This driver is required if you want to support
          Marvell NFC device 8897.
 
-         Say Y here to compile Marvell NFC driver into the kernel or
-         say M to compile it as module.
-
 config NFC_MRVL_USB
        tristate "Marvell NFC-over-USB driver"
-       depends on NFC_MRVL && USB
+       depends on NFC_NCI && USB
+       select NFC_MRVL
        help
          Marvell NFC-over-USB driver.
 
@@ -24,7 +21,8 @@ config NFC_MRVL_USB
 
 config NFC_MRVL_UART
        tristate "Marvell NFC-over-UART driver"
-       depends on NFC_MRVL && NFC_NCI_UART
+       depends on NFC_NCI && NFC_NCI_UART
+       select NFC_MRVL
        help
          Marvell NFC-over-UART driver.
 
@@ -32,3 +30,25 @@ config NFC_MRVL_UART
 
          Say Y here to compile support for Marvell NFC-over-UART driver
          into the kernel or say M to compile it as module.
+
+config NFC_MRVL_I2C
+       tristate "Marvell NFC-over-I2C driver"
+       depends on NFC_MRVL && I2C
+       help
+         Marvell NFC-over-I2C driver.
+
+         This driver provides support for Marvell NFC-over-I2C devices.
+
+         Say Y here to compile support for Marvell NFC-over-I2C driver
+         into the kernel or say M to compile it as module.
+
+config NFC_MRVL_SPI
+       tristate "Marvell NFC-over-SPI driver"
+       depends on NFC_MRVL && SPI
+       help
+         Marvell NFC-over-SPI driver.
+
+         This driver provides support for Marvell NFC-over-SPI devices.
+
+         Say Y here to compile support for Marvell NFC-over-SPI driver
+         into the kernel or say M to compile it as module.
index 775196274d1fae99738b2bd3372e4ade48f1584f..fa07c7806492ff8284e103a130b8df94fcbfb6bf 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for NFCMRVL NCI based NFC driver
 #
 
-nfcmrvl-y += main.o
+nfcmrvl-y += main.o fw_dnld.o
 obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o
 
 nfcmrvl_usb-y += usb.o
@@ -10,3 +10,9 @@ obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o
 
 nfcmrvl_uart-y += uart.o
 obj-$(CONFIG_NFC_MRVL_UART) += nfcmrvl_uart.o
+
+nfcmrvl_i2c-y += i2c.o
+obj-$(CONFIG_NFC_MRVL_I2C) += nfcmrvl_i2c.o
+
+nfcmrvl_spi-y += spi.o
+obj-$(CONFIG_NFC_MRVL_SPI) += nfcmrvl_spi.o
diff --git a/drivers/nfc/nfcmrvl/fw_dnld.c b/drivers/nfc/nfcmrvl/fw_dnld.c
new file mode 100644 (file)
index 0000000..bfa7713
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * Marvell NFC driver: Firmware downloader
+ *
+ * Copyright (C) 2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include <linux/module.h>
+#include <linux/unaligned/access_ok.h>
+#include <linux/firmware.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include "nfcmrvl.h"
+
+#define FW_DNLD_TIMEOUT                        15000
+
+#define NCI_OP_PROPRIETARY_BOOT_CMD    nci_opcode_pack(NCI_GID_PROPRIETARY, \
+                                                       NCI_OP_PROP_BOOT_CMD)
+
+/* FW download states */
+
+enum {
+       STATE_RESET = 0,
+       STATE_INIT,
+       STATE_SET_REF_CLOCK,
+       STATE_SET_HI_CONFIG,
+       STATE_OPEN_LC,
+       STATE_FW_DNLD,
+       STATE_CLOSE_LC,
+       STATE_BOOT
+};
+
+enum {
+       SUBSTATE_WAIT_COMMAND = 0,
+       SUBSTATE_WAIT_ACK_CREDIT,
+       SUBSTATE_WAIT_NACK_CREDIT,
+       SUBSTATE_WAIT_DATA_CREDIT,
+};
+
+/*
+** Patterns for responses
+*/
+
+static const uint8_t nci_pattern_core_reset_ntf[] = {
+       0x60, 0x00, 0x02, 0xA0, 0x01
+};
+
+static const uint8_t nci_pattern_core_init_rsp[] = {
+       0x40, 0x01, 0x11
+};
+
+static const uint8_t nci_pattern_core_set_config_rsp[] = {
+       0x40, 0x02, 0x02, 0x00, 0x00
+};
+
+static const uint8_t nci_pattern_core_conn_create_rsp[] = {
+       0x40, 0x04, 0x04, 0x00
+};
+
+static const uint8_t nci_pattern_core_conn_close_rsp[] = {
+       0x40, 0x05, 0x01, 0x00
+};
+
+static const uint8_t nci_pattern_core_conn_credits_ntf[] = {
+       0x60, 0x06, 0x03, 0x01, NCI_CORE_LC_CONNID_PROP_FW_DL, 0x01
+};
+
+static const uint8_t nci_pattern_proprietary_boot_rsp[] = {
+       0x4F, 0x3A, 0x01, 0x00
+};
+
+static struct sk_buff *alloc_lc_skb(struct nfcmrvl_private *priv, uint8_t plen)
+{
+       struct sk_buff *skb;
+       struct nci_data_hdr *hdr;
+
+       skb = nci_skb_alloc(priv->ndev, (NCI_DATA_HDR_SIZE + plen), GFP_KERNEL);
+       if (!skb) {
+               pr_err("no memory for data\n");
+               return NULL;
+       }
+
+       hdr = (struct nci_data_hdr *) skb_put(skb, NCI_DATA_HDR_SIZE);
+       hdr->conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
+       hdr->rfu = 0;
+       hdr->plen = plen;
+
+       nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
+       nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST);
+
+       return skb;
+}
+
+static void fw_dnld_over(struct nfcmrvl_private *priv, u32 error)
+{
+       if (priv->fw_dnld.fw) {
+               release_firmware(priv->fw_dnld.fw);
+               priv->fw_dnld.fw = NULL;
+               priv->fw_dnld.header = NULL;
+               priv->fw_dnld.binary_config = NULL;
+       }
+
+       atomic_set(&priv->ndev->cmd_cnt, 0);
+       del_timer_sync(&priv->ndev->cmd_timer);
+
+       del_timer_sync(&priv->fw_dnld.timer);
+
+       nfc_info(priv->dev, "FW loading over (%d)]\n", error);
+
+       if (error != 0) {
+               /* failed, halt the chip to avoid power consumption */
+               nfcmrvl_chip_halt(priv);
+       }
+
+       nfc_fw_download_done(priv->ndev->nfc_dev, priv->fw_dnld.name, error);
+}
+
+static void fw_dnld_timeout(unsigned long arg)
+{
+       struct nfcmrvl_private *priv = (struct nfcmrvl_private *) arg;
+
+       nfc_err(priv->dev, "FW loading timeout");
+       priv->fw_dnld.state = STATE_RESET;
+       fw_dnld_over(priv, -ETIMEDOUT);
+}
+
+static int process_state_reset(struct nfcmrvl_private *priv,
+                              struct sk_buff *skb)
+{
+       if (sizeof(nci_pattern_core_reset_ntf) != skb->len ||
+           memcmp(skb->data, nci_pattern_core_reset_ntf,
+                  sizeof(nci_pattern_core_reset_ntf)))
+               return -EINVAL;
+
+       nfc_info(priv->dev, "BootROM reset, start fw download\n");
+
+       /* Start FW download state machine */
+       priv->fw_dnld.state = STATE_INIT;
+       nci_send_cmd(priv->ndev, NCI_OP_CORE_INIT_CMD, 0, NULL);
+
+       return 0;
+}
+
+static int process_state_init(struct nfcmrvl_private *priv, struct sk_buff *skb)
+{
+       struct nci_core_set_config_cmd cmd;
+
+       if (sizeof(nci_pattern_core_init_rsp) >= skb->len ||
+           memcmp(skb->data, nci_pattern_core_init_rsp,
+                  sizeof(nci_pattern_core_init_rsp)))
+               return -EINVAL;
+
+       cmd.num_params = 1;
+       cmd.param.id = NFCMRVL_PROP_REF_CLOCK;
+       cmd.param.len = 4;
+       memcpy(cmd.param.val, &priv->fw_dnld.header->ref_clock, 4);
+
+       nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len,
+                    &cmd);
+
+       priv->fw_dnld.state = STATE_SET_REF_CLOCK;
+       return 0;
+}
+
+static void create_lc(struct nfcmrvl_private *priv)
+{
+       uint8_t param[2] = { NCI_CORE_LC_PROP_FW_DL, 0x0 };
+
+       priv->fw_dnld.state = STATE_OPEN_LC;
+       nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CREATE_CMD, 2, param);
+}
+
+static int process_state_set_ref_clock(struct nfcmrvl_private *priv,
+                                      struct sk_buff *skb)
+{
+       struct nci_core_set_config_cmd cmd;
+
+       if (sizeof(nci_pattern_core_set_config_rsp) != skb->len ||
+           memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len))
+               return -EINVAL;
+
+       cmd.num_params = 1;
+       cmd.param.id = NFCMRVL_PROP_SET_HI_CONFIG;
+
+       switch (priv->phy) {
+       case NFCMRVL_PHY_UART:
+               cmd.param.len = 5;
+               memcpy(cmd.param.val,
+                      &priv->fw_dnld.binary_config->uart.baudrate,
+                      4);
+               cmd.param.val[4] =
+                       priv->fw_dnld.binary_config->uart.flow_control;
+               break;
+       case NFCMRVL_PHY_I2C:
+               cmd.param.len = 5;
+               memcpy(cmd.param.val,
+                      &priv->fw_dnld.binary_config->i2c.clk,
+                      4);
+               cmd.param.val[4] = 0;
+               break;
+       case NFCMRVL_PHY_SPI:
+               cmd.param.len = 5;
+               memcpy(cmd.param.val,
+                      &priv->fw_dnld.binary_config->spi.clk,
+                      4);
+               cmd.param.val[4] = 0;
+               break;
+       default:
+               create_lc(priv);
+               return 0;
+       }
+
+       priv->fw_dnld.state = STATE_SET_HI_CONFIG;
+       nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len,
+                    &cmd);
+       return 0;
+}
+
+static int process_state_set_hi_config(struct nfcmrvl_private *priv,
+                                      struct sk_buff *skb)
+{
+       if (sizeof(nci_pattern_core_set_config_rsp) != skb->len ||
+           memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len))
+               return -EINVAL;
+
+       create_lc(priv);
+       return 0;
+}
+
+static int process_state_open_lc(struct nfcmrvl_private *priv,
+                                struct sk_buff *skb)
+{
+       if (sizeof(nci_pattern_core_conn_create_rsp) >= skb->len ||
+           memcmp(skb->data, nci_pattern_core_conn_create_rsp,
+                  sizeof(nci_pattern_core_conn_create_rsp)))
+               return -EINVAL;
+
+       priv->fw_dnld.state = STATE_FW_DNLD;
+       priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
+       priv->fw_dnld.offset = priv->fw_dnld.binary_config->offset;
+       return 0;
+}
+
+static int process_state_fw_dnld(struct nfcmrvl_private *priv,
+                                struct sk_buff *skb)
+{
+       uint16_t len;
+       uint16_t comp_len;
+       struct sk_buff *out_skb;
+
+       switch (priv->fw_dnld.substate) {
+       case SUBSTATE_WAIT_COMMAND:
+               /*
+                * Command format:
+                * B0..2: NCI header
+                * B3   : Helper command (0xA5)
+                * B4..5: le16 data size
+                * B6..7: le16 data size complement (~)
+                * B8..N: payload
+                */
+
+               /* Remove NCI HDR */
+               skb_pull(skb, 3);
+               if (skb->data[0] != HELPER_CMD_PACKET_FORMAT || skb->len != 5) {
+                       nfc_err(priv->dev, "bad command");
+                       return -EINVAL;
+               }
+               skb_pull(skb, 1);
+               memcpy(&len, skb->data, 2);
+               skb_pull(skb, 2);
+               memcpy(&comp_len, skb->data, 2);
+               skb_pull(skb, 2);
+               len = get_unaligned_le16(&len);
+               comp_len = get_unaligned_le16(&comp_len);
+               if (((~len) & 0xFFFF) != comp_len) {
+                       nfc_err(priv->dev, "bad len complement: %x %x %x",
+                               len, comp_len, (~len & 0xFFFF));
+                       out_skb = alloc_lc_skb(priv, 1);
+                       if (!out_skb)
+                               return -ENOMEM;
+                       *skb_put(out_skb, 1) = 0xBF;
+                       nci_send_frame(priv->ndev, out_skb);
+                       priv->fw_dnld.substate = SUBSTATE_WAIT_NACK_CREDIT;
+                       return 0;
+               }
+               priv->fw_dnld.chunk_len = len;
+               out_skb = alloc_lc_skb(priv, 1);
+               if (!out_skb)
+                       return -ENOMEM;
+               *skb_put(out_skb, 1) = HELPER_ACK_PACKET_FORMAT;
+               nci_send_frame(priv->ndev, out_skb);
+               priv->fw_dnld.substate = SUBSTATE_WAIT_ACK_CREDIT;
+               break;
+
+       case SUBSTATE_WAIT_ACK_CREDIT:
+               if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
+                   memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
+                          skb->len)) {
+                       nfc_err(priv->dev, "bad packet: waiting for credit");
+                       return -EINVAL;
+               }
+               if (priv->fw_dnld.chunk_len == 0) {
+                       /* FW Loading is done */
+                       uint8_t conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
+
+                       priv->fw_dnld.state = STATE_CLOSE_LC;
+                       nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CLOSE_CMD,
+                                    1, &conn_id);
+               } else {
+                       out_skb = alloc_lc_skb(priv, priv->fw_dnld.chunk_len);
+                       if (!out_skb)
+                               return -ENOMEM;
+                       memcpy(skb_put(out_skb, priv->fw_dnld.chunk_len),
+                              ((uint8_t *)priv->fw_dnld.fw->data) +
+                              priv->fw_dnld.offset,
+                              priv->fw_dnld.chunk_len);
+                       nci_send_frame(priv->ndev, out_skb);
+                       priv->fw_dnld.substate = SUBSTATE_WAIT_DATA_CREDIT;
+               }
+               break;
+
+       case SUBSTATE_WAIT_DATA_CREDIT:
+               if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
+                   memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
+                           skb->len)) {
+                       nfc_err(priv->dev, "bad packet: waiting for credit");
+                       return -EINVAL;
+               }
+               priv->fw_dnld.offset += priv->fw_dnld.chunk_len;
+               priv->fw_dnld.chunk_len = 0;
+               priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
+               break;
+
+       case SUBSTATE_WAIT_NACK_CREDIT:
+               if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
+                   memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
+                           skb->len)) {
+                       nfc_err(priv->dev, "bad packet: waiting for credit");
+                       return -EINVAL;
+               }
+               priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
+               break;
+       }
+       return 0;
+}
+
+static int process_state_close_lc(struct nfcmrvl_private *priv,
+                                 struct sk_buff *skb)
+{
+       if (sizeof(nci_pattern_core_conn_close_rsp) != skb->len ||
+           memcmp(skb->data, nci_pattern_core_conn_close_rsp, skb->len))
+               return -EINVAL;
+
+       priv->fw_dnld.state = STATE_BOOT;
+       nci_send_cmd(priv->ndev, NCI_OP_PROPRIETARY_BOOT_CMD, 0, NULL);
+       return 0;
+}
+
+static int process_state_boot(struct nfcmrvl_private *priv, struct sk_buff *skb)
+{
+       if (sizeof(nci_pattern_proprietary_boot_rsp) != skb->len ||
+           memcmp(skb->data, nci_pattern_proprietary_boot_rsp, skb->len))
+               return -EINVAL;
+
+       /*
+        * Update HI config to use the right configuration for the next
+        * data exchanges.
+        */
+       priv->if_ops->nci_update_config(priv,
+                                       &priv->fw_dnld.binary_config->config);
+
+       if (priv->fw_dnld.binary_config == &priv->fw_dnld.header->helper) {
+               /*
+                * This is the case where an helper was needed and we have
+                * uploaded it. Now we have to wait the next RESET NTF to start
+                * FW download.
+                */
+               priv->fw_dnld.state = STATE_RESET;
+               priv->fw_dnld.binary_config = &priv->fw_dnld.header->firmware;
+               nfc_info(priv->dev, "FW loading: helper loaded");
+       } else {
+               nfc_info(priv->dev, "FW loading: firmware loaded");
+               fw_dnld_over(priv, 0);
+       }
+       return 0;
+}
+
+static void fw_dnld_rx_work(struct work_struct *work)
+{
+       int ret;
+       struct sk_buff *skb;
+       struct nfcmrvl_fw_dnld *fw_dnld = container_of(work,
+                                                      struct nfcmrvl_fw_dnld,
+                                                      rx_work);
+       struct nfcmrvl_private *priv = container_of(fw_dnld,
+                                                   struct nfcmrvl_private,
+                                                   fw_dnld);
+
+       while ((skb = skb_dequeue(&fw_dnld->rx_q))) {
+               nfc_send_to_raw_sock(priv->ndev->nfc_dev, skb,
+                                    RAW_PAYLOAD_NCI, NFC_DIRECTION_RX);
+               switch (fw_dnld->state) {
+               case STATE_RESET:
+                       ret = process_state_reset(priv, skb);
+                       break;
+               case STATE_INIT:
+                       ret = process_state_init(priv, skb);
+                       break;
+               case STATE_SET_REF_CLOCK:
+                       ret = process_state_set_ref_clock(priv, skb);
+                       break;
+               case STATE_SET_HI_CONFIG:
+                       ret = process_state_set_hi_config(priv, skb);
+                       break;
+               case STATE_OPEN_LC:
+                       ret = process_state_open_lc(priv, skb);
+                       break;
+               case STATE_FW_DNLD:
+                       ret = process_state_fw_dnld(priv, skb);
+                       break;
+               case STATE_CLOSE_LC:
+                       ret = process_state_close_lc(priv, skb);
+                       break;
+               case STATE_BOOT:
+                       ret = process_state_boot(priv, skb);
+                       break;
+               default:
+                       ret = -EFAULT;
+               }
+
+               kfree_skb(skb);
+
+               if (ret != 0) {
+                       nfc_err(priv->dev, "FW loading error");
+                       fw_dnld_over(priv, ret);
+                       break;
+               }
+       }
+}
+
+int    nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv)
+{
+       char name[32];
+
+       INIT_WORK(&priv->fw_dnld.rx_work, fw_dnld_rx_work);
+       snprintf(name, sizeof(name), "%s_nfcmrvl_fw_dnld_rx_wq",
+                dev_name(priv->dev));
+       priv->fw_dnld.rx_wq = create_singlethread_workqueue(name);
+       if (!priv->fw_dnld.rx_wq)
+               return -ENOMEM;
+       skb_queue_head_init(&priv->fw_dnld.rx_q);
+       return 0;
+}
+
+void   nfcmrvl_fw_dnld_deinit(struct nfcmrvl_private *priv)
+{
+       destroy_workqueue(priv->fw_dnld.rx_wq);
+}
+
+void   nfcmrvl_fw_dnld_recv_frame(struct nfcmrvl_private *priv,
+                                  struct sk_buff *skb)
+{
+       /* Allow next command */
+       atomic_set(&priv->ndev->cmd_cnt, 1);
+       del_timer_sync(&priv->ndev->cmd_timer);
+
+       /* Queue and trigger rx work */
+       skb_queue_tail(&priv->fw_dnld.rx_q, skb);
+       queue_work(priv->fw_dnld.rx_wq, &priv->fw_dnld.rx_work);
+}
+
+void nfcmrvl_fw_dnld_abort(struct nfcmrvl_private *priv)
+{
+       fw_dnld_over(priv, -EHOSTDOWN);
+}
+
+int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name)
+{
+       struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+       struct nfcmrvl_fw_dnld *fw_dnld = &priv->fw_dnld;
+
+       if (!priv->support_fw_dnld)
+               return -ENOTSUPP;
+
+       if (!firmware_name || !firmware_name[0])
+               return -EINVAL;
+
+       strcpy(fw_dnld->name, firmware_name);
+
+       /*
+        * Retrieve FW binary file and parse it to initialize FW download
+        * state machine.
+        */
+
+       /* Retrieve FW binary */
+       if (request_firmware(&fw_dnld->fw, firmware_name, priv->dev) < 0) {
+               nfc_err(priv->dev, "failed to retrieve FW %s", firmware_name);
+               return -ENOENT;
+       }
+
+       fw_dnld->header = (const struct nfcmrvl_fw *) priv->fw_dnld.fw->data;
+
+       if (fw_dnld->header->magic != NFCMRVL_FW_MAGIC ||
+           fw_dnld->header->phy != priv->phy) {
+               nfc_err(priv->dev, "bad firmware binary %s magic=0x%x phy=%d",
+                       firmware_name, fw_dnld->header->magic,
+                       fw_dnld->header->phy);
+               release_firmware(fw_dnld->fw);
+               fw_dnld->header = NULL;
+               return -EINVAL;
+       }
+
+       if (fw_dnld->header->helper.offset != 0) {
+               nfc_info(priv->dev, "loading helper");
+               fw_dnld->binary_config = &fw_dnld->header->helper;
+       } else {
+               nfc_info(priv->dev, "loading firmware");
+               fw_dnld->binary_config = &fw_dnld->header->firmware;
+       }
+
+       /* Configure a timer for timeout */
+       setup_timer(&priv->fw_dnld.timer, fw_dnld_timeout,
+                   (unsigned long) priv);
+       mod_timer(&priv->fw_dnld.timer,
+                 jiffies + msecs_to_jiffies(FW_DNLD_TIMEOUT));
+
+       /* Ronfigure HI to be sure that it is the bootrom values */
+       priv->if_ops->nci_update_config(priv,
+                                       &fw_dnld->header->bootrom.config);
+
+       /* Allow first command */
+       atomic_set(&priv->ndev->cmd_cnt, 1);
+
+       /* First, reset the chip */
+       priv->fw_dnld.state = STATE_RESET;
+       nfcmrvl_chip_reset(priv);
+
+       /* Now wait for CORE_RESET_NTF or timeout */
+
+       return 0;
+}
diff --git a/drivers/nfc/nfcmrvl/fw_dnld.h b/drivers/nfc/nfcmrvl/fw_dnld.h
new file mode 100644 (file)
index 0000000..ee4a339
--- /dev/null
@@ -0,0 +1,98 @@
+/**
+ * Marvell NFC driver: Firmware downloader
+ *
+ * Copyright (C) 2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#ifndef __NFCMRVL_FW_DNLD_H__
+#define __NFCMRVL_FW_DNLD_H__
+
+#include <linux/workqueue.h>
+
+#define NFCMRVL_FW_MAGIC               0x88888888
+
+#define NCI_OP_PROP_BOOT_CMD           0x3A
+
+#define NCI_CORE_LC_PROP_FW_DL         0xFD
+#define NCI_CORE_LC_CONNID_PROP_FW_DL  0x02
+
+#define HELPER_CMD_ENTRY_POINT         0x04
+#define HELPER_CMD_PACKET_FORMAT       0xA5
+#define HELPER_ACK_PACKET_FORMAT       0x5A
+#define HELPER_RETRY_REQUESTED         (1 << 15)
+
+struct nfcmrvl_private;
+
+struct nfcmrvl_fw_uart_config {
+       uint8_t flow_control;
+       uint32_t baudrate;
+} __packed;
+
+struct nfcmrvl_fw_i2c_config {
+       uint32_t clk;
+} __packed;
+
+struct nfcmrvl_fw_spi_config {
+       uint32_t clk;
+} __packed;
+
+struct nfcmrvl_fw_binary_config {
+       uint32_t offset;
+       union {
+               void *config;
+               struct nfcmrvl_fw_uart_config uart;
+               struct nfcmrvl_fw_i2c_config i2c;
+               struct nfcmrvl_fw_spi_config spi;
+               uint8_t reserved[64];
+       };
+} __packed;
+
+struct nfcmrvl_fw {
+       uint32_t magic;
+       uint32_t ref_clock;
+       uint32_t phy;
+       struct nfcmrvl_fw_binary_config bootrom;
+       struct nfcmrvl_fw_binary_config helper;
+       struct nfcmrvl_fw_binary_config firmware;
+       uint8_t reserved[64];
+} __packed;
+
+struct nfcmrvl_fw_dnld {
+       char name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+       const struct firmware *fw;
+
+       const struct nfcmrvl_fw *header;
+       const struct nfcmrvl_fw_binary_config *binary_config;
+
+       int state;
+       int substate;
+       int offset;
+       int chunk_len;
+
+       struct workqueue_struct *rx_wq;
+       struct work_struct rx_work;
+       struct sk_buff_head rx_q;
+
+       struct timer_list timer;
+};
+
+int nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv);
+void nfcmrvl_fw_dnld_deinit(struct nfcmrvl_private *priv);
+void nfcmrvl_fw_dnld_abort(struct nfcmrvl_private *priv);
+int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name);
+void nfcmrvl_fw_dnld_recv_frame(struct nfcmrvl_private *priv,
+                               struct sk_buff *skb);
+
+#endif
diff --git a/drivers/nfc/nfcmrvl/i2c.c b/drivers/nfc/nfcmrvl/i2c.c
new file mode 100644 (file)
index 0000000..78b7aa8
--- /dev/null
@@ -0,0 +1,290 @@
+/**
+ * Marvell NFC-over-I2C driver: I2C interface related functions
+ *
+ * Copyright (C) 2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/nfc.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include "nfcmrvl.h"
+
+struct nfcmrvl_i2c_drv_data {
+       unsigned long flags;
+       struct device *dev;
+       struct i2c_client *i2c;
+       struct nfcmrvl_private *priv;
+};
+
+static int nfcmrvl_i2c_read(struct nfcmrvl_i2c_drv_data *drv_data,
+                           struct sk_buff **skb)
+{
+       int ret;
+       struct nci_ctrl_hdr nci_hdr;
+
+       /* Read NCI header to know the payload size */
+       ret = i2c_master_recv(drv_data->i2c, (u8 *)&nci_hdr, NCI_CTRL_HDR_SIZE);
+       if (ret != NCI_CTRL_HDR_SIZE) {
+               nfc_err(&drv_data->i2c->dev, "cannot read NCI header\n");
+               return -EBADMSG;
+       }
+
+       if (nci_hdr.plen > NCI_MAX_PAYLOAD_SIZE) {
+               nfc_err(&drv_data->i2c->dev, "invalid packet payload size\n");
+               return -EBADMSG;
+       }
+
+       *skb = nci_skb_alloc(drv_data->priv->ndev,
+                            nci_hdr.plen + NCI_CTRL_HDR_SIZE, GFP_KERNEL);
+       if (!*skb)
+               return -ENOMEM;
+
+       /* Copy NCI header into the SKB */
+       memcpy(skb_put(*skb, NCI_CTRL_HDR_SIZE), &nci_hdr, NCI_CTRL_HDR_SIZE);
+
+       if (nci_hdr.plen) {
+               /* Read the NCI payload */
+               ret = i2c_master_recv(drv_data->i2c,
+                                     skb_put(*skb, nci_hdr.plen),
+                                     nci_hdr.plen);
+
+               if (ret != nci_hdr.plen) {
+                       nfc_err(&drv_data->i2c->dev,
+                               "Invalid frame payload length: %u (expected %u)\n",
+                               ret, nci_hdr.plen);
+                       kfree_skb(*skb);
+                       return -EBADMSG;
+               }
+       }
+
+       return 0;
+}
+
+static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *drv_data_ptr)
+{
+       struct nfcmrvl_i2c_drv_data *drv_data = drv_data_ptr;
+       struct sk_buff *skb = NULL;
+       int ret;
+
+       if (!drv_data->priv)
+               return IRQ_HANDLED;
+
+       if (test_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags))
+               return IRQ_HANDLED;
+
+       ret = nfcmrvl_i2c_read(drv_data, &skb);
+
+       switch (ret) {
+       case -EREMOTEIO:
+               set_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags);
+               break;
+       case -ENOMEM:
+       case -EBADMSG:
+               nfc_err(&drv_data->i2c->dev, "read failed %d\n", ret);
+               break;
+       default:
+               if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
+                       nfc_err(&drv_data->i2c->dev, "corrupted RX packet\n");
+               break;
+       }
+       return IRQ_HANDLED;
+}
+
+static int nfcmrvl_i2c_nci_open(struct nfcmrvl_private *priv)
+{
+       struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data;
+
+       if (!drv_data)
+               return -ENODEV;
+
+       return 0;
+}
+
+static int nfcmrvl_i2c_nci_close(struct nfcmrvl_private *priv)
+{
+       return 0;
+}
+
+static int nfcmrvl_i2c_nci_send(struct nfcmrvl_private *priv,
+                               struct sk_buff *skb)
+{
+       struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data;
+       int ret;
+
+       if (test_bit(NFCMRVL_PHY_ERROR, &priv->flags))
+               return -EREMOTEIO;
+
+       ret = i2c_master_send(drv_data->i2c, skb->data, skb->len);
+
+       /* Retry if chip was in standby */
+       if (ret == -EREMOTEIO) {
+               nfc_info(drv_data->dev, "chip may sleep, retry\n");
+               usleep_range(6000, 10000);
+               ret = i2c_master_send(drv_data->i2c, skb->data, skb->len);
+       }
+
+       if (ret >= 0) {
+               if (ret != skb->len) {
+                       nfc_err(drv_data->dev,
+                               "Invalid length sent: %u (expected %u)\n",
+                               ret, skb->len);
+                       ret = -EREMOTEIO;
+               } else
+                       ret = 0;
+               kfree_skb(skb);
+       }
+
+       return ret;
+}
+
+static void nfcmrvl_i2c_nci_update_config(struct nfcmrvl_private *priv,
+                                         const void *param)
+{
+}
+
+static struct nfcmrvl_if_ops i2c_ops = {
+       .nci_open = nfcmrvl_i2c_nci_open,
+       .nci_close = nfcmrvl_i2c_nci_close,
+       .nci_send = nfcmrvl_i2c_nci_send,
+       .nci_update_config = nfcmrvl_i2c_nci_update_config,
+};
+
+static int nfcmrvl_i2c_parse_dt(struct device_node *node,
+                               struct nfcmrvl_platform_data *pdata)
+{
+       int ret;
+
+       ret = nfcmrvl_parse_dt(node, pdata);
+       if (ret < 0) {
+               pr_err("Failed to get generic entries\n");
+               return ret;
+       }
+
+       if (of_find_property(node, "i2c-int-falling", NULL))
+               pdata->irq_polarity = IRQF_TRIGGER_FALLING;
+       else
+               pdata->irq_polarity = IRQF_TRIGGER_RISING;
+
+       ret = irq_of_parse_and_map(node, 0);
+       if (ret < 0) {
+               pr_err("Unable to get irq, error: %d\n", ret);
+               return ret;
+       }
+       pdata->irq = ret;
+
+       return 0;
+}
+
+static int nfcmrvl_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       struct nfcmrvl_i2c_drv_data *drv_data;
+       struct nfcmrvl_platform_data *pdata;
+       struct nfcmrvl_platform_data config;
+       int ret;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
+               return -ENODEV;
+       }
+
+       drv_data = devm_kzalloc(&client->dev, sizeof(*drv_data), GFP_KERNEL);
+       if (!drv_data)
+               return -ENOMEM;
+
+       drv_data->i2c = client;
+       drv_data->dev = &client->dev;
+       drv_data->priv = NULL;
+
+       i2c_set_clientdata(client, drv_data);
+
+       pdata = client->dev.platform_data;
+
+       if (!pdata && client->dev.of_node)
+               if (nfcmrvl_i2c_parse_dt(client->dev.of_node, &config) == 0)
+                       pdata = &config;
+
+       if (!pdata)
+               return -EINVAL;
+
+       /* Request the read IRQ */
+       ret = devm_request_threaded_irq(&drv_data->i2c->dev, pdata->irq,
+                                       NULL, nfcmrvl_i2c_int_irq_thread_fn,
+                                       pdata->irq_polarity | IRQF_ONESHOT,
+                                       "nfcmrvl_i2c_int", drv_data);
+       if (ret < 0) {
+               nfc_err(&drv_data->i2c->dev,
+                       "Unable to register IRQ handler\n");
+               return ret;
+       }
+
+       drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_I2C,
+                                                 drv_data, &i2c_ops,
+                                                 &drv_data->i2c->dev, pdata);
+
+       if (IS_ERR(drv_data->priv))
+               return PTR_ERR(drv_data->priv);
+
+       drv_data->priv->support_fw_dnld = true;
+
+       return 0;
+}
+
+static int nfcmrvl_i2c_remove(struct i2c_client *client)
+{
+       struct nfcmrvl_i2c_drv_data *drv_data = i2c_get_clientdata(client);
+
+       nfcmrvl_nci_unregister_dev(drv_data->priv);
+
+       return 0;
+}
+
+
+static const struct of_device_id of_nfcmrvl_i2c_match[] = {
+       { .compatible = "marvell,nfc-i2c", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, of_nfcmrvl_i2c_match);
+
+static struct i2c_device_id nfcmrvl_i2c_id_table[] = {
+       { "nfcmrvl_i2c", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, nfcmrvl_i2c_id_table);
+
+static struct i2c_driver nfcmrvl_i2c_driver = {
+       .probe = nfcmrvl_i2c_probe,
+       .id_table = nfcmrvl_i2c_id_table,
+       .remove = nfcmrvl_i2c_remove,
+       .driver = {
+               .name           = "nfcmrvl_i2c",
+               .owner          = THIS_MODULE,
+               .of_match_table = of_match_ptr(of_nfcmrvl_i2c_match),
+       },
+};
+
+module_i2c_driver(nfcmrvl_i2c_driver);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell NFC-over-I2C driver");
+MODULE_LICENSE("GPL v2");
index 4a8866d62941202ee94a697f4ec78729772b8758..8079ae0de21ebac54153e7202bb469ae413ed765 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell NFC driver: major functions
  *
- * Copyright (C) 2014, Marvell International Ltd.
+ * Copyright (C) 2014-2015 Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -25,8 +25,6 @@
 #include <net/nfc/nci_core.h>
 #include "nfcmrvl.h"
 
-#define VERSION "1.0"
-
 static int nfcmrvl_nci_open(struct nci_dev *ndev)
 {
        struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
@@ -35,6 +33,9 @@ static int nfcmrvl_nci_open(struct nci_dev *ndev)
        if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
                return 0;
 
+       /* Reset possible fault of previous session */
+       clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
+
        err = priv->if_ops->nci_open(priv);
 
        if (err)
@@ -63,9 +64,6 @@ static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
 
        skb->dev = (void *)ndev;
 
-       if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
-               return -EBUSY;
-
        if (priv->config.hci_muxed) {
                unsigned char *hdr;
                unsigned char len = skb->len;
@@ -88,21 +86,30 @@ static int nfcmrvl_nci_setup(struct nci_dev *ndev)
        return 0;
 }
 
+static int nfcmrvl_nci_fw_download(struct nci_dev *ndev,
+                                  const char *firmware_name)
+{
+       return nfcmrvl_fw_dnld_start(ndev, firmware_name);
+}
+
 static struct nci_ops nfcmrvl_nci_ops = {
        .open = nfcmrvl_nci_open,
        .close = nfcmrvl_nci_close,
        .send = nfcmrvl_nci_send,
        .setup = nfcmrvl_nci_setup,
+       .fw_download = nfcmrvl_nci_fw_download,
 };
 
-struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
+struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
+                               void *drv_data,
                                struct nfcmrvl_if_ops *ops,
                                struct device *dev,
                                struct nfcmrvl_platform_data *pdata)
 {
        struct nfcmrvl_private *priv;
        int rc;
-       int headroom = 0;
+       int headroom;
+       int tailroom;
        u32 protocols;
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -112,6 +119,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
        priv->drv_data = drv_data;
        priv->if_ops = ops;
        priv->dev = dev;
+       priv->phy = phy;
 
        memcpy(&priv->config, pdata, sizeof(*pdata));
 
@@ -124,8 +132,14 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
                        nfc_err(dev, "failed to request reset_n io\n");
        }
 
+       if (phy == NFCMRVL_PHY_SPI) {
+               headroom = NCI_SPI_HDR_LEN;
+               tailroom = 1;
+       } else
+               headroom = tailroom = 0;
+
        if (priv->config.hci_muxed)
-               headroom = NFCMRVL_HCI_EVENT_HEADER_SIZE;
+               headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE;
 
        protocols = NFC_PROTO_JEWEL_MASK
                | NFC_PROTO_MIFARE_MASK
@@ -136,7 +150,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
                | NFC_PROTO_NFC_DEP_MASK;
 
        priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols,
-                                        headroom, 0);
+                                        headroom, tailroom);
        if (!priv->ndev) {
                nfc_err(dev, "nci_allocate_device failed\n");
                rc = -ENOMEM;
@@ -145,18 +159,26 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
 
        nci_set_drvdata(priv->ndev, priv);
 
-       nfcmrvl_chip_reset(priv);
-
        rc = nci_register_device(priv->ndev);
        if (rc) {
                nfc_err(dev, "nci_register_device failed %d\n", rc);
-               nci_free_device(priv->ndev);
-               goto error;
+               goto error_free_dev;
+       }
+
+       /* Ensure that controller is powered off */
+       nfcmrvl_chip_halt(priv);
+
+       rc = nfcmrvl_fw_dnld_init(priv);
+       if (rc) {
+               nfc_err(dev, "failed to initialize FW download %d\n", rc);
+               goto error_free_dev;
        }
 
        nfc_info(dev, "registered with nci successfully\n");
        return priv;
 
+error_free_dev:
+       nci_free_device(priv->ndev);
 error:
        kfree(priv);
        return ERR_PTR(rc);
@@ -167,6 +189,11 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
 {
        struct nci_dev *ndev = priv->ndev;
 
+       if (priv->ndev->nfc_dev->fw_download_in_progress)
+               nfcmrvl_fw_dnld_abort(priv);
+
+       nfcmrvl_fw_dnld_deinit(priv);
+
        nci_unregister_device(ndev);
        nci_free_device(ndev);
        kfree(priv);
@@ -187,6 +214,11 @@ int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
                }
        }
 
+       if (priv->ndev->nfc_dev->fw_download_in_progress) {
+               nfcmrvl_fw_dnld_recv_frame(priv, skb);
+               return 0;
+       }
+
        if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
                nci_recv_frame(priv->ndev, skb);
        else {
@@ -201,10 +233,8 @@ EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame);
 
 void nfcmrvl_chip_reset(struct nfcmrvl_private *priv)
 {
-       /*
-        * This function does not take care if someone is using the device.
-        * To be improved.
-        */
+       /* Reset possible fault of previous session */
+       clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
 
        if (priv->config.reset_n_io) {
                nfc_info(priv->dev, "reset the chip\n");
@@ -215,6 +245,12 @@ void nfcmrvl_chip_reset(struct nfcmrvl_private *priv)
                nfc_info(priv->dev, "no reset available on this interface\n");
 }
 
+void nfcmrvl_chip_halt(struct nfcmrvl_private *priv)
+{
+       if (priv->config.reset_n_io)
+               gpio_set_value(priv->config.reset_n_io, 0);
+}
+
 #ifdef CONFIG_OF
 
 int nfcmrvl_parse_dt(struct device_node *node,
@@ -252,6 +288,5 @@ int nfcmrvl_parse_dt(struct device_node *node,
 EXPORT_SYMBOL_GPL(nfcmrvl_parse_dt);
 
 MODULE_AUTHOR("Marvell International Ltd.");
-MODULE_DESCRIPTION("Marvell NFC driver ver " VERSION);
-MODULE_VERSION(VERSION);
+MODULE_DESCRIPTION("Marvell NFC driver");
 MODULE_LICENSE("GPL v2");
index e5a7e5464f2e543be22254585c6936fa05d97123..de68ff45e49a4e7c228cb15f5e46c1e378736943 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * Marvell NFC driver
  *
- * Copyright (C) 2014, Marvell International Ltd.
+ * Copyright (C) 2014-2015, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
 
 #include <linux/platform_data/nfcmrvl.h>
 
+#include "fw_dnld.h"
+
 /* Define private flags: */
 #define NFCMRVL_NCI_RUNNING                    1
+#define NFCMRVL_PHY_ERROR                      2
 
 #define NFCMRVL_EXT_COEX_ID                    0xE0
 #define NFCMRVL_NOT_ALLOWED_ID                 0xE1
@@ -37,6 +40,8 @@
 */
 
 #define NFCMRVL_PB_BAIL_OUT                    0x11
+#define NFCMRVL_PROP_REF_CLOCK                 0xF0
+#define NFCMRVL_PROP_SET_HI_CONFIG             0xF1
 
 /*
 ** HCI defines
 enum nfcmrvl_phy {
        NFCMRVL_PHY_USB         = 0,
        NFCMRVL_PHY_UART        = 1,
+       NFCMRVL_PHY_I2C         = 2,
+       NFCMRVL_PHY_SPI         = 3,
 };
 
-
 struct nfcmrvl_private {
 
        unsigned long flags;
@@ -62,8 +68,15 @@ struct nfcmrvl_private {
        /* Platform configuration */
        struct nfcmrvl_platform_data config;
 
+       /* Parent dev */
        struct nci_dev *ndev;
 
+       /* FW download context */
+       struct nfcmrvl_fw_dnld fw_dnld;
+
+       /* FW download support */
+       bool support_fw_dnld;
+
        /*
        ** PHY related information
        */
@@ -82,17 +95,21 @@ struct nfcmrvl_if_ops {
        int (*nci_open) (struct nfcmrvl_private *priv);
        int (*nci_close) (struct nfcmrvl_private *priv);
        int (*nci_send) (struct nfcmrvl_private *priv, struct sk_buff *skb);
+       void (*nci_update_config)(struct nfcmrvl_private *priv,
+                                 const void *param);
 };
 
 void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv);
 int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb);
-struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
+struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
+                               void *drv_data,
                                struct nfcmrvl_if_ops *ops,
                                struct device *dev,
                                struct nfcmrvl_platform_data *pdata);
 
 
 void nfcmrvl_chip_reset(struct nfcmrvl_private *priv);
+void nfcmrvl_chip_halt(struct nfcmrvl_private *priv);
 
 int nfcmrvl_parse_dt(struct device_node *node,
                     struct nfcmrvl_platform_data *pdata);
diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c
new file mode 100644 (file)
index 0000000..a7faa0b
--- /dev/null
@@ -0,0 +1,228 @@
+/**
+ * Marvell NFC-over-SPI driver: SPI interface related functions
+ *
+ * Copyright (C) 2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/nfc.h>
+#include <linux/gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include "nfcmrvl.h"
+
+#define SPI_WAIT_HANDSHAKE     1
+
+struct nfcmrvl_spi_drv_data {
+       unsigned long flags;
+       struct spi_device *spi;
+       struct nci_spi *nci_spi;
+       struct completion handshake_completion;
+       struct nfcmrvl_private *priv;
+};
+
+static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr)
+{
+       struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr;
+       struct sk_buff *skb;
+
+       /*
+        * Special case where we are waiting for SPI_INT deassertion to start a
+        * transfer.
+        */
+       if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) {
+               complete(&drv_data->handshake_completion);
+               return IRQ_HANDLED;
+       }
+
+       /* Normal case, SPI_INT deasserted by slave to trigger a master read */
+
+       skb = nci_spi_read(drv_data->nci_spi);
+       if (!skb) {
+               nfc_err(&drv_data->spi->dev, "failed to read spi packet");
+               return IRQ_HANDLED;
+       }
+
+       if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
+               nfc_err(&drv_data->spi->dev, "corrupted RX packet");
+
+       return IRQ_HANDLED;
+}
+
+static int nfcmrvl_spi_nci_open(struct nfcmrvl_private *priv)
+{
+       return 0;
+}
+
+static int nfcmrvl_spi_nci_close(struct nfcmrvl_private *priv)
+{
+       return 0;
+}
+
+static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv,
+                               struct sk_buff *skb)
+{
+       struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
+       int err;
+
+       /* Reinit completion for slave handshake */
+       reinit_completion(&drv_data->handshake_completion);
+       set_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags);
+
+       /*
+        * Append a dummy byte at the end of SPI frame. This is due to a
+        * specific DMA implementation in the controller
+        */
+       skb_put(skb, 1);
+
+       /* Send the SPI packet */
+       err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion,
+                          skb);
+       if (err != 0) {
+               nfc_err(priv->dev, "spi_send failed %d", err);
+               kfree_skb(skb);
+       }
+       return err;
+}
+
+static void nfcmrvl_spi_nci_update_config(struct nfcmrvl_private *priv,
+                                         const void *param)
+{
+       struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data;
+       const struct nfcmrvl_fw_spi_config *config = param;
+
+       drv_data->nci_spi->xfer_speed_hz = config->clk;
+}
+
+static struct nfcmrvl_if_ops spi_ops = {
+       .nci_open = nfcmrvl_spi_nci_open,
+       .nci_close = nfcmrvl_spi_nci_close,
+       .nci_send = nfcmrvl_spi_nci_send,
+       .nci_update_config = nfcmrvl_spi_nci_update_config,
+};
+
+static int nfcmrvl_spi_parse_dt(struct device_node *node,
+                               struct nfcmrvl_platform_data *pdata)
+{
+       int ret;
+
+       ret = nfcmrvl_parse_dt(node, pdata);
+       if (ret < 0) {
+               pr_err("Failed to get generic entries\n");
+               return ret;
+       }
+
+       ret = irq_of_parse_and_map(node, 0);
+       if (ret < 0) {
+               pr_err("Unable to get irq, error: %d\n", ret);
+               return ret;
+       }
+       pdata->irq = ret;
+
+       return 0;
+}
+
+static int nfcmrvl_spi_probe(struct spi_device *spi)
+{
+       struct nfcmrvl_platform_data *pdata;
+       struct nfcmrvl_platform_data config;
+       struct nfcmrvl_spi_drv_data *drv_data;
+       int ret = 0;
+
+       drv_data = devm_kzalloc(&spi->dev, sizeof(*drv_data), GFP_KERNEL);
+       if (!drv_data)
+               return -ENOMEM;
+
+       drv_data->spi = spi;
+       drv_data->priv = NULL;
+       spi_set_drvdata(spi, drv_data);
+
+       pdata = spi->dev.platform_data;
+
+       if (!pdata && spi->dev.of_node)
+               if (nfcmrvl_spi_parse_dt(spi->dev.of_node, &config) == 0)
+                       pdata = &config;
+
+       if (!pdata)
+               return -EINVAL;
+
+       ret = devm_request_threaded_irq(&drv_data->spi->dev, pdata->irq,
+                                       NULL, nfcmrvl_spi_int_irq_thread_fn,
+                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                       "nfcmrvl_spi_int", drv_data);
+       if (ret < 0) {
+               nfc_err(&drv_data->spi->dev, "Unable to register IRQ handler");
+               return -ENODEV;
+       }
+
+       drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_SPI,
+                                                 drv_data, &spi_ops,
+                                                 &drv_data->spi->dev,
+                                                 pdata);
+       if (IS_ERR(drv_data->priv))
+               return PTR_ERR(drv_data->priv);
+
+       drv_data->priv->support_fw_dnld = true;
+
+       drv_data->nci_spi = nci_spi_allocate_spi(drv_data->spi, 0, 10,
+                                                drv_data->priv->ndev);
+
+       /* Init completion for slave handshake */
+       init_completion(&drv_data->handshake_completion);
+       return 0;
+}
+
+static int nfcmrvl_spi_remove(struct spi_device *spi)
+{
+       struct nfcmrvl_spi_drv_data *drv_data = spi_get_drvdata(spi);
+
+       nfcmrvl_nci_unregister_dev(drv_data->priv);
+       return 0;
+}
+
+static const struct of_device_id of_nfcmrvl_spi_match[] = {
+       { .compatible = "marvell,nfc-spi", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, of_nfcmrvl_spi_match);
+
+static const struct spi_device_id nfcmrvl_spi_id_table[] = {
+       { "nfcmrvl_spi", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(spi, nfcmrvl_spi_id_table);
+
+static struct spi_driver nfcmrvl_spi_driver = {
+       .probe          = nfcmrvl_spi_probe,
+       .remove         = nfcmrvl_spi_remove,
+       .id_table       = nfcmrvl_spi_id_table,
+       .driver         = {
+               .name           = "nfcmrvl_spi",
+               .owner          = THIS_MODULE,
+               .of_match_table = of_match_ptr(of_nfcmrvl_spi_match),
+       },
+};
+
+module_spi_driver(nfcmrvl_spi_driver);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell NFC-over-SPI driver");
+MODULE_LICENSE("GPL v2");
index 61442d6528a604ac6bdd916ea3b71249d2692350..f3d041c4f249e05dced87bd76786bc8a612fd915 100644 (file)
@@ -50,10 +50,21 @@ static int nfcmrvl_uart_nci_send(struct nfcmrvl_private *priv,
        return nu->ops.send(nu, skb);
 }
 
+static void nfcmrvl_uart_nci_update_config(struct nfcmrvl_private *priv,
+                                          const void *param)
+{
+       struct nci_uart *nu = priv->drv_data;
+       const struct nfcmrvl_fw_uart_config *config = param;
+
+       nci_uart_set_config(nu, le32_to_cpu(config->baudrate),
+                           config->flow_control);
+}
+
 static struct nfcmrvl_if_ops uart_ops = {
        .nci_open = nfcmrvl_uart_nci_open,
        .nci_close = nfcmrvl_uart_nci_close,
        .nci_send = nfcmrvl_uart_nci_send,
+       .nci_update_config = nfcmrvl_uart_nci_update_config
 };
 
 #ifdef CONFIG_OF
@@ -64,9 +75,13 @@ static int nfcmrvl_uart_parse_dt(struct device_node *node,
        struct device_node *matched_node;
        int ret;
 
-       matched_node = of_find_compatible_node(node, NULL, "mrvl,nfc-uart");
-       if (!matched_node)
-               return -ENODEV;
+       matched_node = of_find_compatible_node(node, NULL, "marvell,nfc-uart");
+       if (!matched_node) {
+               matched_node = of_find_compatible_node(node, NULL,
+                                                      "mrvl,nfc-uart");
+               if (!matched_node)
+                       return -ENODEV;
+       }
 
        ret = nfcmrvl_parse_dt(matched_node, pdata);
        if (ret < 0) {
@@ -127,11 +142,12 @@ static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
                pdata = &config;
        }
 
-       priv = nfcmrvl_nci_register_dev(nu, &uart_ops, nu->tty->dev, pdata);
+       priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_UART, nu, &uart_ops,
+                                       nu->tty->dev, pdata);
        if (IS_ERR(priv))
                return PTR_ERR(priv);
 
-       priv->phy = NFCMRVL_PHY_UART;
+       priv->support_fw_dnld = true;
 
        nu->drv_data = priv;
        nu->ndev = priv->ndev;
index 7d1fe436c9f643345f2e14ba6eb5387ec7991f54..585a0f20835b19e19f583b0829d20738d6be0754 100644 (file)
@@ -23,8 +23,6 @@
 #include <net/nfc/nci_core.h>
 #include "nfcmrvl.h"
 
-#define VERSION "1.0"
-
 static struct usb_device_id nfcmrvl_table[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046,
                                        USB_CLASS_VENDOR_SPEC, 4, 1) },
@@ -342,13 +340,14 @@ static int nfcmrvl_probe(struct usb_interface *intf,
        init_usb_anchor(&drv_data->bulk_anchor);
        init_usb_anchor(&drv_data->deferred);
 
-       priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops,
+       priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops,
                                        &drv_data->udev->dev, &config);
        if (IS_ERR(priv))
                return PTR_ERR(priv);
 
        drv_data->priv = priv;
-       drv_data->priv->phy = NFCMRVL_PHY_USB;
+       drv_data->priv->support_fw_dnld = false;
+
        priv->dev = &drv_data->udev->dev;
 
        usb_set_intfdata(intf, drv_data);
@@ -469,6 +468,5 @@ static struct usb_driver nfcmrvl_usb_driver = {
 module_usb_driver(nfcmrvl_usb_driver);
 
 MODULE_AUTHOR("Marvell International Ltd.");
-MODULE_DESCRIPTION("Marvell NFC-over-USB driver ver " VERSION);
-MODULE_VERSION(VERSION);
+MODULE_DESCRIPTION("Marvell NFC-over-USB driver");
 MODULE_LICENSE("GPL v2");
index 93111fa8d2829735ec1ddea6c09c6da86bbb9459..26ac9e5fa1ab256277e543548484268c033243fc 100644 (file)
@@ -246,7 +246,7 @@ static int nfcsim_activate_target(struct nfc_dev *nfc_dev,
 }
 
 static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev,
-                                    struct nfc_target *target)
+                                    struct nfc_target *target, u8 mode)
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
index ce2e2cf54fbc8b86a5dd674e819dd2b85908d978..f81e500e765061fd7d3cf22196260d3647155607 100644 (file)
@@ -497,7 +497,7 @@ static struct nci_ops nfcwilink_ops = {
 
 static int nfcwilink_probe(struct platform_device *pdev)
 {
-       static struct nfcwilink *drv;
+       struct nfcwilink *drv;
        int rc;
        __u32 protocols;
 
index 8979636d48eaa117e933ec85b0850bf6e22dab97..2e4b004a96aaa130cdb539abf86d338211f611f3 100644 (file)
@@ -109,7 +109,8 @@ static struct nci_ops nxp_nci_ops = {
 };
 
 int nxp_nci_probe(void *phy_id, struct device *pdev,
-                 struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload,
+                 const struct nxp_nci_phy_ops *phy_ops,
+                 unsigned int max_payload,
                  struct nci_dev **ndev)
 {
        struct nxp_nci_info *info;
index fac80c691914b6625a9eab504b8baca75833c0dc..df4333c7ee0f813d5e813299c2751a6db0caabee 100644 (file)
@@ -106,7 +106,7 @@ static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb)
        return r;
 }
 
-static struct nxp_nci_phy_ops i2c_phy_ops = {
+static const struct nxp_nci_phy_ops i2c_phy_ops = {
        .set_mode = nxp_nci_i2c_set_mode,
        .write = nxp_nci_i2c_write,
 };
index f1fecc4e24578d299c71a678f2dd54b21400944f..20408cbff4f10c2ac9d001bee50f9dfe9f26c2b4 100644 (file)
@@ -68,7 +68,7 @@ struct nxp_nci_info {
 
        enum nxp_nci_mode mode;
 
-       struct nxp_nci_phy_ops *phy_ops;
+       const struct nxp_nci_phy_ops *phy_ops;
        unsigned int max_payload;
 
        struct mutex info_lock;
@@ -82,7 +82,8 @@ void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
 void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result);
 
 int nxp_nci_probe(void *phy_id, struct device *pdev,
-                 struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload,
+                 const struct nxp_nci_phy_ops *phy_ops,
+                 unsigned int max_payload,
                  struct nci_dev **ndev);
 void nxp_nci_remove(struct nci_dev *ndev);
 
index a03e4eb5fe2978c24d10899d249ddc2373968db6..bb3d5ea9869c63e061761edd1d460dc41a36a01f 100644 (file)
@@ -2263,7 +2263,7 @@ static int pn533_activate_target(struct nfc_dev *nfc_dev,
 }
 
 static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
-                                   struct nfc_target *target)
+                                   struct nfc_target *target, u8 mode)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
        struct sk_buff *skb;
index ccf06f5f6ebbd8b02d730b95993c5e3718193417..2b8bde39540d212cd9984ee6e073898044c964b4 100644 (file)
@@ -1,20 +1,15 @@
 config NFC_PN544
-       tristate "NXP PN544 NFC driver"
-       depends on NFC_HCI
+       tristate
        select CRC_CCITT
-       default n
        ---help---
          NXP PN544 core driver.
          This is a driver based on the HCI NFC kernel layers and
          will thus not work with NXP libnfc library.
 
-         To compile this driver as a module, choose m here. The module will
-         be called pn544.
-         Say N if unsure.
-
 config NFC_PN544_I2C
-       tristate "NFC PN544 i2c support"
-       depends on NFC_PN544 && I2C && NFC_SHDLC
+       tristate "NXP PN544 device support (I2C)"
+       depends on NFC_HCI && I2C && NFC_SHDLC
+       select NFC_PN544
        ---help---
          This module adds support for the NXP pn544 i2c interface.
          Select this if your platform is using the i2c bus.
@@ -23,8 +18,9 @@ config NFC_PN544_I2C
          Say N if unsure.
 
 config NFC_PN544_MEI
-       tristate "NFC PN544 MEI support"
-       depends on NFC_PN544 && NFC_MEI_PHY
+       tristate "NXP PN544 device support (MEI)"
+       depends on NFC_HCI && NFC_MEI_PHY
+       select NFC_PN544
        ---help---
          This module adds support for the mei interface of adapters using
          NXP pn544 chipsets.  Select this if your pn544 chipset
index 7e3b255b3f997e14f0ade72b81ebfff214f54e1a..1eef9199486e556d12f15d3f2c498e7057adc9d1 100644 (file)
@@ -1,5 +1,6 @@
 config NFC_S3FWRN5
        tristate
+       select CRYPTO
        ---help---
          Core driver for Samsung S3FWRN5 NFC chip. Contains core utilities
          of chip. It's intended to be used by PHYs to avoid duplicating lots
index 3381c34faf623d260386f8277f1a8248c3e3f953..ddfa7be7dd0525ba8173439af3f98de2fb9609c8 100644 (file)
@@ -7,5 +7,3 @@ s3fwrn5_i2c-objs = i2c.o
 
 obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5.o
 obj-$(CONFIG_NFC_S3FWRN5_I2C) += s3fwrn5_i2c.o
-
-ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
index b4dd7dd47473c03c7b891e86702ec2060e10c11d..c61d8a308da456f8bbdda345a190e5abf85fbe45 100644 (file)
@@ -258,7 +258,7 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client,
        if (ret < 0)
                return ret;
 
-       ret = request_threaded_irq(phy->i2c_dev->irq, NULL,
+       ret = devm_request_threaded_irq(&client->dev, phy->i2c_dev->irq, NULL,
                s3fwrn5_i2c_irq_thread_fn, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
                S3FWRN5_I2C_DRIVER_NAME, phy);
        if (ret)
index ace0071c533995a51ac84b79f8f4e7af08ad0acc..075e4e877b3316ee3e9f9c45c73c8c1cb355ed9f 100644 (file)
@@ -31,7 +31,7 @@ static int s3fwrn5_nci_prop_rsp(struct nci_dev *ndev, struct sk_buff *skb)
        return 0;
 }
 
-static struct nci_prop_ops s3fwrn5_nci_prop_ops[] = {
+static struct nci_driver_ops s3fwrn5_nci_prop_ops[] = {
        {
                .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
                                NCI_PROP_AGAIN),
@@ -79,7 +79,7 @@ static struct nci_prop_ops s3fwrn5_nci_prop_ops[] = {
        },
 };
 
-void s3fwrn5_nci_get_prop_ops(struct nci_prop_ops **ops, size_t *n)
+void s3fwrn5_nci_get_prop_ops(struct nci_driver_ops **ops, size_t *n)
 {
        *ops = s3fwrn5_nci_prop_ops;
        *n = ARRAY_SIZE(s3fwrn5_nci_prop_ops);
index 0e68d439dde62344f149039604cddaabad50a35a..60c7fb575b66ca6ea00489f9cf2370d7b5d9401b 100644 (file)
@@ -83,7 +83,7 @@ struct nci_prop_fw_cfg_rsp {
 
 #define NCI_PROP_WR_RESET      0x2f
 
-void s3fwrn5_nci_get_prop_ops(struct nci_prop_ops **ops, size_t *n);
+void s3fwrn5_nci_get_prop_ops(struct nci_driver_ops **ops, size_t *n);
 int s3fwrn5_nci_rf_configure(struct s3fwrn5_info *info, const char *fw_name);
 
 #endif /* __LOCAL_S3FWRN5_NCI_H_ */
index 348ce76f2177546a138d8e0fd07358ac18d8ed80..439b2fa8654ae73deb2b98ea7e198fc1b25df978 100644 (file)
@@ -1,8 +1,8 @@
 #
-# Makefile for ST21NFCB NCI based NFC driver
+# Makefile for ST_NCI NCI based NFC driver
 #
 
-st-nci-objs = ndlc.o core.o st-nci_se.o
+st-nci-objs = ndlc.o core.o se.o vendor_cmds.o
 obj-$(CONFIG_NFC_ST_NCI)     += st-nci.o
 
 st-nci_i2c-objs = i2c.o
index c419d3943973744201236476018f8db35589ca61..c693128ee6fb90825abcecee9a436f31dafe0a9a 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/delay.h>
 
 #include "st-nci.h"
-#include "st-nci_se.h"
 
 #define DRIVER_DESC "NCI NFC driver for ST_NCI"
 
@@ -98,7 +97,7 @@ static int st_nci_prop_rsp_packet(struct nci_dev *ndev,
        return 0;
 }
 
-static struct nci_prop_ops st_nci_prop_ops[] = {
+static struct nci_driver_ops st_nci_prop_ops[] = {
        {
                .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
                                          ST_NCI_CORE_PROP),
@@ -124,7 +123,7 @@ static struct nci_ops st_nci_ops = {
 };
 
 int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
-                      int phy_tailroom)
+                int phy_tailroom, struct st_nci_se_status *se_status)
 {
        struct st_nci_info *info;
        int r;
@@ -153,14 +152,23 @@ int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
 
        nci_set_drvdata(ndlc->ndev, info);
 
+       r = st_nci_vendor_cmds_init(ndlc->ndev);
+       if (r) {
+               pr_err("Cannot register proprietary vendor cmds\n");
+               goto err_reg_dev;
+       }
+
        r = nci_register_device(ndlc->ndev);
        if (r) {
                pr_err("Cannot register nfc device to nci core\n");
-               nci_free_device(ndlc->ndev);
-               return r;
+               goto err_reg_dev;
        }
 
-       return st_nci_se_init(ndlc->ndev);
+       return st_nci_se_init(ndlc->ndev, se_status);
+
+err_reg_dev:
+       nci_free_device(ndlc->ndev);
+       return r;
 }
 EXPORT_SYMBOL_GPL(st_nci_probe);
 
index 707ed2eb593638208df4420c7dc9a93abd4399ad..15e3ce2d274c7691cf8c31595cd2482a86b636be 100644 (file)
 #include <linux/nfc.h>
 #include <linux/platform_data/st-nci.h>
 
-#include "ndlc.h"
+#include "st-nci.h"
 
 #define DRIVER_DESC "NCI NFC driver for ST_NCI"
 
 /* ndlc header */
-#define ST_NCI_FRAME_HEADROOM  1
+#define ST_NCI_FRAME_HEADROOM 1
 #define ST_NCI_FRAME_TAILROOM 0
 
 #define ST_NCI_I2C_MIN_SIZE 4   /* PCB(1) + NCI Packet header(3) */
@@ -50,16 +50,13 @@ struct st_nci_i2c_phy {
        struct i2c_client *i2c_dev;
        struct llt_ndlc *ndlc;
 
+       bool irq_active;
+
        unsigned int gpio_reset;
        unsigned int irq_polarity;
-};
 
-#define I2C_DUMP_SKB(info, skb)                                        \
-do {                                                           \
-       pr_debug("%s:\n", info);                                \
-       print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \
-                      16, 1, (skb)->data, (skb)->len, 0);      \
-} while (0)
+       struct st_nci_se_status se_status;
+};
 
 static int st_nci_i2c_enable(void *phy_id)
 {
@@ -70,8 +67,10 @@ static int st_nci_i2c_enable(void *phy_id)
        gpio_set_value(phy->gpio_reset, 1);
        usleep_range(80000, 85000);
 
-       if (phy->ndlc->powered == 0)
+       if (phy->ndlc->powered == 0 && phy->irq_active == 0) {
                enable_irq(phy->i2c_dev->irq);
+               phy->irq_active = true;
+       }
 
        return 0;
 }
@@ -81,6 +80,7 @@ static void st_nci_i2c_disable(void *phy_id)
        struct st_nci_i2c_phy *phy = phy_id;
 
        disable_irq_nosync(phy->i2c_dev->irq);
+       phy->irq_active = false;
 }
 
 /*
@@ -94,8 +94,6 @@ static int st_nci_i2c_write(void *phy_id, struct sk_buff *skb)
        struct st_nci_i2c_phy *phy = phy_id;
        struct i2c_client *client = phy->i2c_dev;
 
-       I2C_DUMP_SKB("st_nci_i2c_write", skb);
-
        if (phy->ndlc->hard_fault != 0)
                return phy->ndlc->hard_fault;
 
@@ -166,8 +164,6 @@ static int st_nci_i2c_read(struct st_nci_i2c_phy *phy,
        skb_put(*skb, len);
        memcpy((*skb)->data + ST_NCI_I2C_MIN_SIZE, buf, len);
 
-       I2C_DUMP_SKB("i2c frame read", *skb);
-
        return 0;
 }
 
@@ -245,6 +241,11 @@ static int st_nci_i2c_of_request_resources(struct i2c_client *client)
 
        phy->irq_polarity = irq_get_trigger_type(client->irq);
 
+       phy->se_status.is_ese_present =
+                               of_property_read_bool(pp, "ese-present");
+       phy->se_status.is_uicc_present =
+                               of_property_read_bool(pp, "uicc-present");
+
        return 0;
 }
 #else
@@ -277,6 +278,9 @@ static int st_nci_i2c_request_resources(struct i2c_client *client)
                return r;
        }
 
+       phy->se_status.is_ese_present = pdata->is_ese_present;
+       phy->se_status.is_uicc_present = pdata->is_uicc_present;
+
        return 0;
 }
 
@@ -326,12 +330,13 @@ static int st_nci_i2c_probe(struct i2c_client *client,
 
        r = ndlc_probe(phy, &i2c_phy_ops, &client->dev,
                        ST_NCI_FRAME_HEADROOM, ST_NCI_FRAME_TAILROOM,
-                       &phy->ndlc);
+                       &phy->ndlc, &phy->se_status);
        if (r < 0) {
                nfc_err(&client->dev, "Unable to register ndlc layer\n");
                return r;
        }
 
+       phy->irq_active = true;
        r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
                                st_nci_irq_thread_fn,
                                phy->irq_polarity | IRQF_ONESHOT,
index d2cf84e680c655d19d6e09f44ee73024b1630304..0884b11001ef03e3df439362ca0e6060e4701419 100644 (file)
@@ -19,8 +19,8 @@
 #include <linux/sched.h>
 #include <net/nfc/nci_core.h>
 
-#include "ndlc.h"
 #include "st-nci.h"
+#include "ndlc.h"
 
 #define NDLC_TIMER_T1          100
 #define NDLC_TIMER_T1_WAIT     400
@@ -266,7 +266,8 @@ static void ndlc_t2_timeout(unsigned long data)
 }
 
 int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev,
-              int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id)
+              int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id,
+              struct st_nci_se_status *se_status)
 {
        struct llt_ndlc *ndlc;
 
@@ -296,7 +297,7 @@ int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev,
 
        INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work);
 
-       return st_nci_probe(ndlc, phy_headroom, phy_tailroom);
+       return st_nci_probe(ndlc, phy_headroom, phy_tailroom, se_status);
 }
 EXPORT_SYMBOL(ndlc_probe);
 
index 6361005ef0037e5a5ad1f3ccb758f200ce230484..bdf78ffd5bb749a22e980838b03cb6c8ecc9290b 100644 (file)
@@ -22,6 +22,8 @@
 #include <linux/skbuff.h>
 #include <net/nfc/nfc.h>
 
+struct st_nci_se_status;
+
 /* Low Level Transport description */
 struct llt_ndlc {
        struct nci_dev *ndev;
@@ -55,6 +57,7 @@ void ndlc_close(struct llt_ndlc *ndlc);
 int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb);
 void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb);
 int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev,
-       int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id);
+              int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id,
+              struct st_nci_se_status *se_status);
 void ndlc_remove(struct llt_ndlc *ndlc);
 #endif /* __LOCAL_NDLC_H__ */
similarity index 82%
rename from drivers/nfc/st-nci/st-nci_se.c
rename to drivers/nfc/st-nci/se.c
index c742ef65a05a50c72f4142e0fed570980e01aa39..dbab722a06546ab0c2dbbdba9d87e0fb12561132 100644 (file)
@@ -23,7 +23,6 @@
 #include <net/nfc/nci_core.h>
 
 #include "st-nci.h"
-#include "st-nci_se.h"
 
 struct st_nci_pipe_info {
        u8 pipe_state;
@@ -40,7 +39,6 @@ struct st_nci_pipe_info {
 #define ST_NCI_ESE_HOST_ID            0xc0
 
 /* Gates */
-#define ST_NCI_DEVICE_MGNT_GATE       0x01
 #define ST_NCI_APDU_READER_GATE       0xf0
 #define ST_NCI_CONNECTIVITY_GATE      0x41
 
@@ -64,7 +62,7 @@ struct st_nci_pipe_info {
 
 #define ST_NCI_EVT_SE_HARD_RESET               0x20
 #define ST_NCI_EVT_TRANSMIT_DATA               0x10
-#define ST_NCI_EVT_WTX_REQUEST         0x11
+#define ST_NCI_EVT_WTX_REQUEST                 0x11
 #define ST_NCI_EVT_SE_SOFT_RESET               0x11
 #define ST_NCI_EVT_SE_END_OF_APDU_TRANSFER     0x21
 #define ST_NCI_EVT_HOT_PLUG                    0x03
@@ -113,6 +111,11 @@ static struct nci_hci_gate st_nci_gates[] = {
        {ST_NCI_DEVICE_MGNT_GATE, ST_NCI_DEVICE_MGNT_PIPE,
                                        ST_NCI_HOST_CONTROLLER_ID},
 
+       {NCI_HCI_IDENTITY_MGMT_GATE, NCI_HCI_INVALID_PIPE,
+                                       ST_NCI_HOST_CONTROLLER_ID},
+       {NCI_HCI_LOOPBACK_GATE, NCI_HCI_INVALID_PIPE,
+                                       ST_NCI_HOST_CONTROLLER_ID},
+
        /* Secure element pipes are created by secure element host */
        {ST_NCI_CONNECTIVITY_GATE, NCI_HCI_DO_NOT_OPEN_PIPE,
                                        ST_NCI_HOST_CONTROLLER_ID},
@@ -226,27 +229,32 @@ int st_nci_hci_load_session(struct nci_dev *ndev)
                        continue;
                }
 
-               for (j = 0; (j < ARRAY_SIZE(st_nci_gates)) &&
+               for (j = 3; (j < ARRAY_SIZE(st_nci_gates)) &&
                     (st_nci_gates[j].gate != dm_pipe_info->dst_gate_id); j++)
                        ;
 
                if (j < ARRAY_SIZE(st_nci_gates) &&
                    st_nci_gates[j].gate == dm_pipe_info->dst_gate_id &&
                    ST_NCI_DM_IS_PIPE_OPEN(dm_pipe_info->pipe_state)) {
-                       st_nci_gates[j].pipe = pipe_info[2];
+                       ndev->hci_dev->init_data.gates[j].pipe = pipe_info[2];
 
                        ndev->hci_dev->gate2pipe[st_nci_gates[j].gate] =
-                                               st_nci_gates[j].pipe;
-                       ndev->hci_dev->pipes[st_nci_gates[j].pipe].gate =
+                                               pipe_info[2];
+                       ndev->hci_dev->pipes[pipe_info[2]].gate =
                                                st_nci_gates[j].gate;
-                       ndev->hci_dev->pipes[st_nci_gates[j].pipe].host =
+                       ndev->hci_dev->pipes[pipe_info[2]].host =
                                                dm_pipe_info->src_host_id;
                }
                kfree_skb(skb_pipe_info);
        }
 
-       memcpy(ndev->hci_dev->init_data.gates, st_nci_gates,
-              sizeof(st_nci_gates));
+       /*
+        * 3 gates have a well known pipe ID. Only NCI_HCI_LINK_MGMT_GATE
+        * is not yet open at this stage.
+        */
+       r = nci_hci_connect_gate(ndev, ST_NCI_HOST_CONTROLLER_ID,
+                                NCI_HCI_LINK_MGMT_GATE,
+                                NCI_HCI_LINK_MGMT_PIPE);
 
        kfree_skb(skb_pipe_list);
        return r;
@@ -272,6 +280,8 @@ static void st_nci_hci_admin_event_received(struct nci_dev *ndev,
                        }
                }
        break;
+       default:
+               nfc_err(&ndev->nfc_dev->dev, "Unexpected event on admin gate\n");
        }
 }
 
@@ -295,6 +305,9 @@ static int st_nci_hci_apdu_reader_event_received(struct nci_dev *ndev,
                mod_timer(&info->se_info.bwi_timer, jiffies +
                          msecs_to_jiffies(info->se_info.wt_timeout));
        break;
+       default:
+               nfc_err(&ndev->nfc_dev->dev, "Unexpected event on apdu reader gate\n");
+               return 1;
        }
 
        kfree_skb(skb);
@@ -349,6 +362,7 @@ static int st_nci_hci_connectivity_event_received(struct nci_dev *ndev,
                r = nfc_se_transaction(ndev->nfc_dev, host, transaction);
                break;
        default:
+               nfc_err(&ndev->nfc_dev->dev, "Unexpected event on connectivity gate\n");
                return 1;
        }
        kfree_skb(skb);
@@ -369,8 +383,10 @@ void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
                st_nci_hci_apdu_reader_event_received(ndev, event, skb);
        break;
        case ST_NCI_CONNECTIVITY_GATE:
-               st_nci_hci_connectivity_event_received(ndev, host, event,
-                                                        skb);
+               st_nci_hci_connectivity_event_received(ndev, host, event, skb);
+       break;
+       case NCI_HCI_LOOPBACK_GATE:
+               st_nci_hci_loopback_event_received(ndev, event, skb);
        break;
        }
 }
@@ -403,15 +419,11 @@ void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
 }
 EXPORT_SYMBOL_GPL(st_nci_hci_cmd_received);
 
-/*
- * Remarks: On some early st_nci firmware, nci_nfcee_mode_set(0)
- * is rejected
- */
 static int st_nci_control_se(struct nci_dev *ndev, u8 se_idx,
-                                  u8 state)
+                            u8 state)
 {
        struct st_nci_info *info = nci_get_drvdata(ndev);
-       int r;
+       int r, i;
        struct sk_buff *sk_host_list;
        u8 host_id;
 
@@ -433,7 +445,7 @@ static int st_nci_control_se(struct nci_dev *ndev, u8 se_idx,
         * retrieve a relevant host list.
         */
        reinit_completion(&info->se_info.req_completion);
-       r = nci_nfcee_mode_set(ndev, se_idx, NCI_NFCEE_ENABLE);
+       r = nci_nfcee_mode_set(ndev, se_idx, state);
        if (r != NCI_STATUS_OK)
                return r;
 
@@ -449,14 +461,19 @@ static int st_nci_control_se(struct nci_dev *ndev, u8 se_idx,
         * There is no possible synchronization to prevent this.
         * Adding a small delay is the only way to solve the issue.
         */
-       usleep_range(3000, 5000);
+       if (info->se_info.se_status->is_ese_present &&
+           info->se_info.se_status->is_uicc_present)
+               usleep_range(15000, 20000);
 
        r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE,
                        NCI_HCI_ADMIN_PARAM_HOST_LIST, &sk_host_list);
        if (r != NCI_HCI_ANY_OK)
                return r;
 
-       host_id = sk_host_list->data[sk_host_list->len - 1];
+       for (i = 0; i < sk_host_list->len &&
+               sk_host_list->data[i] != se_idx; i++)
+               ;
+       host_id = sk_host_list->data[i];
        kfree_skb(sk_host_list);
        if (state == ST_NCI_SE_MODE_ON && host_id == se_idx)
                return se_idx;
@@ -472,11 +489,20 @@ int st_nci_disable_se(struct nci_dev *ndev, u32 se_idx)
 
        pr_debug("st_nci_disable_se\n");
 
-       if (se_idx == NFC_SE_EMBEDDED) {
-               r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
-                               ST_NCI_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0);
-               if (r < 0)
-                       return r;
+       /*
+        * According to upper layer, se_idx == NFC_SE_UICC when
+        * info->se_info.se_status->is_uicc_enable is true should never happen
+        * Same for eSE.
+        */
+       r = st_nci_control_se(ndev, se_idx, ST_NCI_SE_MODE_OFF);
+       if (r < 0) {
+               /* Do best effort to release SWP */
+               if (se_idx == NFC_SE_EMBEDDED) {
+                       r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
+                                       ST_NCI_EVT_SE_END_OF_APDU_TRANSFER,
+                                       NULL, 0);
+               }
+               return r;
        }
 
        return 0;
@@ -489,11 +515,25 @@ int st_nci_enable_se(struct nci_dev *ndev, u32 se_idx)
 
        pr_debug("st_nci_enable_se\n");
 
-       if (se_idx == ST_NCI_HCI_HOST_ID_ESE) {
+       /*
+        * According to upper layer, se_idx == NFC_SE_UICC when
+        * info->se_info.se_status->is_uicc_enable is true should never happen.
+        * Same for eSE.
+        */
+       r = st_nci_control_se(ndev, se_idx, ST_NCI_SE_MODE_ON);
+       if (r == ST_NCI_HCI_HOST_ID_ESE) {
+               st_nci_se_get_atr(ndev);
                r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
                                ST_NCI_EVT_SE_SOFT_RESET, NULL, 0);
-               if (r < 0)
-                       return r;
+       }
+
+       if (r < 0) {
+               /*
+                * The activation procedure failed, the secure element
+                * is not connected. Remove from the list.
+                */
+               nfc_remove_se(ndev->nfc_dev, se_idx);
+               return r;
        }
 
        return 0;
@@ -502,6 +542,7 @@ EXPORT_SYMBOL_GPL(st_nci_enable_se);
 
 static int st_nci_hci_network_init(struct nci_dev *ndev)
 {
+       struct st_nci_info *info = nci_get_drvdata(ndev);
        struct core_conn_create_dest_spec_params *dest_params;
        struct dest_spec_params spec_params;
        struct nci_conn_info    *conn_info;
@@ -532,6 +573,7 @@ static int st_nci_hci_network_init(struct nci_dev *ndev)
        if (!conn_info)
                goto free_dest_params;
 
+       ndev->hci_dev->init_data.gate_count = ARRAY_SIZE(st_nci_gates);
        memcpy(ndev->hci_dev->init_data.gates, st_nci_gates,
               sizeof(st_nci_gates));
 
@@ -553,10 +595,17 @@ static int st_nci_hci_network_init(struct nci_dev *ndev)
        if (r != NCI_HCI_ANY_OK)
                goto free_dest_params;
 
-       r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id,
-                              NCI_NFCEE_ENABLE);
-       if (r != NCI_STATUS_OK)
-               goto free_dest_params;
+       /*
+        * In factory mode, we prevent secure elements activation
+        * by disabling nfcee on the current HCI connection id.
+        * HCI will be used here only for proprietary commands.
+        */
+       if (test_bit(ST_NCI_FACTORY_MODE, &info->flags))
+               r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id,
+                                      NCI_NFCEE_DISABLE);
+       else
+               r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id,
+                                      NCI_NFCEE_ENABLE);
 
 free_dest_params:
        kfree(dest_params);
@@ -567,9 +616,10 @@ exit:
 
 int st_nci_discover_se(struct nci_dev *ndev)
 {
-       u8 param[2];
-       int r;
+       u8 white_list[2];
+       int r, wl_size = 0;
        int se_count = 0;
+       struct st_nci_info *info = nci_get_drvdata(ndev);
 
        pr_debug("st_nci_discover_se\n");
 
@@ -577,29 +627,37 @@ int st_nci_discover_se(struct nci_dev *ndev)
        if (r != 0)
                return r;
 
-       param[0] = ST_NCI_UICC_HOST_ID;
-       param[1] = ST_NCI_HCI_HOST_ID_ESE;
-       r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE,
-                               NCI_HCI_ADMIN_PARAM_WHITELIST,
-                               param, sizeof(param));
-       if (r != NCI_HCI_ANY_OK)
-               return r;
+       if (test_bit(ST_NCI_FACTORY_MODE, &info->flags))
+               return 0;
+
+       if (info->se_info.se_status->is_ese_present &&
+           info->se_info.se_status->is_uicc_present) {
+               white_list[wl_size++] = ST_NCI_UICC_HOST_ID;
+               white_list[wl_size++] = ST_NCI_ESE_HOST_ID;
+       } else if (!info->se_info.se_status->is_ese_present &&
+                  info->se_info.se_status->is_uicc_present) {
+               white_list[wl_size++] = ST_NCI_UICC_HOST_ID;
+       } else if (info->se_info.se_status->is_ese_present &&
+                  !info->se_info.se_status->is_uicc_present) {
+               white_list[wl_size++] = ST_NCI_ESE_HOST_ID;
+       }
 
-       r = st_nci_control_se(ndev, ST_NCI_UICC_HOST_ID,
-                               ST_NCI_SE_MODE_ON);
-       if (r == ST_NCI_UICC_HOST_ID) {
+       if (wl_size) {
+               r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE,
+                                     NCI_HCI_ADMIN_PARAM_WHITELIST,
+                                     white_list, wl_size);
+               if (r != NCI_HCI_ANY_OK)
+                       return r;
+       }
+
+       if (info->se_info.se_status->is_uicc_present) {
                nfc_add_se(ndev->nfc_dev, ST_NCI_UICC_HOST_ID, NFC_SE_UICC);
                se_count++;
        }
 
-       /* Try to enable eSE in order to check availability */
-       r = st_nci_control_se(ndev, ST_NCI_HCI_HOST_ID_ESE,
-                               ST_NCI_SE_MODE_ON);
-       if (r == ST_NCI_HCI_HOST_ID_ESE) {
-               nfc_add_se(ndev->nfc_dev, ST_NCI_HCI_HOST_ID_ESE,
-                          NFC_SE_EMBEDDED);
+       if (info->se_info.se_status->is_ese_present) {
+               nfc_add_se(ndev->nfc_dev, ST_NCI_ESE_HOST_ID, NFC_SE_EMBEDDED);
                se_count++;
-               st_nci_se_get_atr(ndev);
        }
 
        return !se_count;
@@ -672,7 +730,7 @@ static void st_nci_se_activation_timeout(unsigned long data)
        complete(&info->se_info.req_completion);
 }
 
-int st_nci_se_init(struct nci_dev *ndev)
+int st_nci_se_init(struct nci_dev *ndev, struct st_nci_se_status *se_status)
 {
        struct st_nci_info *info = nci_get_drvdata(ndev);
 
@@ -694,6 +752,8 @@ int st_nci_se_init(struct nci_dev *ndev)
        info->se_info.wt_timeout =
                ST_NCI_BWI_TO_TIMEOUT(ST_NCI_ATR_DEFAULT_BWI);
 
+       info->se_info.se_status = se_status;
+
        return 0;
 }
 EXPORT_SYMBOL(st_nci_se_init);
index 598a58c4d6d1349632ed5b306d33d5163780a6cb..cf7ad8121e1139b9c14376fbf2afaf113ab00cf3 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/nfc.h>
+#include <net/nfc/nci.h>
 #include <linux/platform_data/st-nci.h>
 
-#include "ndlc.h"
+#include "st-nci.h"
 
 #define DRIVER_DESC "NCI NFC driver for ST_NCI"
 
@@ -50,16 +51,13 @@ struct st_nci_spi_phy {
        struct spi_device *spi_dev;
        struct llt_ndlc *ndlc;
 
+       bool irq_active;
+
        unsigned int gpio_reset;
        unsigned int irq_polarity;
-};
 
-#define SPI_DUMP_SKB(info, skb)                                        \
-do {                                                           \
-       pr_debug("%s:\n", info);                                \
-       print_hex_dump(KERN_DEBUG, "spi: ", DUMP_PREFIX_OFFSET, \
-                      16, 1, (skb)->data, (skb)->len, 0);      \
-} while (0)
+       struct st_nci_se_status se_status;
+};
 
 static int st_nci_spi_enable(void *phy_id)
 {
@@ -70,8 +68,10 @@ static int st_nci_spi_enable(void *phy_id)
        gpio_set_value(phy->gpio_reset, 1);
        usleep_range(80000, 85000);
 
-       if (phy->ndlc->powered == 0)
+       if (phy->ndlc->powered == 0 && phy->irq_active == 0) {
                enable_irq(phy->spi_dev->irq);
+               phy->irq_active = true;
+       }
 
        return 0;
 }
@@ -81,6 +81,7 @@ static void st_nci_spi_disable(void *phy_id)
        struct st_nci_spi_phy *phy = phy_id;
 
        disable_irq_nosync(phy->spi_dev->irq);
+       phy->irq_active = false;
 }
 
 /*
@@ -94,15 +95,14 @@ static int st_nci_spi_write(void *phy_id, struct sk_buff *skb)
        struct st_nci_spi_phy *phy = phy_id;
        struct spi_device *dev = phy->spi_dev;
        struct sk_buff *skb_rx;
-       u8 buf[ST_NCI_SPI_MAX_SIZE];
+       u8 buf[ST_NCI_SPI_MAX_SIZE + NCI_DATA_HDR_SIZE +
+              ST_NCI_FRAME_HEADROOM + ST_NCI_FRAME_TAILROOM];
        struct spi_transfer spi_xfer = {
                .tx_buf = skb->data,
                .rx_buf = buf,
                .len = skb->len,
        };
 
-       SPI_DUMP_SKB("st_nci_spi_write", skb);
-
        if (phy->ndlc->hard_fault != 0)
                return phy->ndlc->hard_fault;
 
@@ -179,8 +179,6 @@ static int st_nci_spi_read(struct st_nci_spi_phy *phy,
        skb_put(*skb, len);
        memcpy((*skb)->data + ST_NCI_SPI_MIN_SIZE, buf, len);
 
-       SPI_DUMP_SKB("spi frame read", *skb);
-
        return 0;
 }
 
@@ -258,6 +256,11 @@ static int st_nci_spi_of_request_resources(struct spi_device *dev)
 
        phy->irq_polarity = irq_get_trigger_type(dev->irq);
 
+       phy->se_status.is_ese_present =
+                               of_property_read_bool(pp, "ese-present");
+       phy->se_status.is_uicc_present =
+                               of_property_read_bool(pp, "uicc-present");
+
        return 0;
 }
 #else
@@ -290,6 +293,9 @@ static int st_nci_spi_request_resources(struct spi_device *dev)
                return r;
        }
 
+       phy->se_status.is_ese_present = pdata->is_ese_present;
+       phy->se_status.is_uicc_present = pdata->is_uicc_present;
+
        return 0;
 }
 
@@ -340,12 +346,13 @@ static int st_nci_spi_probe(struct spi_device *dev)
 
        r = ndlc_probe(phy, &spi_phy_ops, &dev->dev,
                        ST_NCI_FRAME_HEADROOM, ST_NCI_FRAME_TAILROOM,
-                       &phy->ndlc);
+                       &phy->ndlc, &phy->se_status);
        if (r < 0) {
                nfc_err(&dev->dev, "Unable to register ndlc layer\n");
                return r;
        }
 
+       phy->irq_active = true;
        r = devm_request_threaded_irq(&dev->dev, dev->irq, NULL,
                                st_nci_irq_thread_fn,
                                phy->irq_polarity | IRQF_ONESHOT,
index 850a2395deb78d145071547c47b0c56a2befec74..8b9f77b0249cba10bafcc9512a1ea01a18d8a50a 100644 (file)
@@ -19,7 +19,6 @@
 #ifndef __LOCAL_ST_NCI_H_
 #define __LOCAL_ST_NCI_H_
 
-#include "st-nci_se.h"
 #include "ndlc.h"
 
 /* Define private flags: */
 #define ST_NCI_CORE_PROP                0x01
 #define ST_NCI_SET_NFC_MODE             0x02
 
+/*
+ * ref ISO7816-3 chap 8.1. the initial character TS is followed by a
+ * sequence of at most 32 characters.
+ */
+#define ST_NCI_ESE_MAX_LENGTH  33
+#define ST_NCI_HCI_HOST_ID_ESE 0xc0
+
+#define ST_NCI_DEVICE_MGNT_GATE                0x01
+
+#define ST_NCI_VENDOR_OUI 0x0080E1 /* STMicroelectronics */
+#define ST_NCI_FACTORY_MODE 2
+
 struct nci_mode_set_cmd {
        u8 cmd_type;
        u8 mode;
@@ -37,14 +48,116 @@ struct nci_mode_set_rsp {
        u8 status;
 } __packed;
 
+struct st_nci_se_status {
+       bool is_ese_present;
+       bool is_uicc_present;
+};
+
+struct st_nci_se_info {
+       struct st_nci_se_status *se_status;
+       u8 atr[ST_NCI_ESE_MAX_LENGTH];
+       struct completion req_completion;
+
+       struct timer_list bwi_timer;
+       int wt_timeout; /* in msecs */
+       bool bwi_active;
+
+       struct timer_list se_active_timer;
+       bool se_active;
+
+       bool xch_error;
+
+       se_io_cb_t cb;
+       void *cb_context;
+};
+
+/**
+ * enum nfc_vendor_cmds - supported nfc vendor commands
+ *
+ * @FACTORY_MODE: Allow to set the driver into a mode where no secure element
+ *     are activated. It does not consider any NFC_ATTR_VENDOR_DATA.
+ * @HCI_CLEAR_ALL_PIPES: Allow to execute a HCI clear all pipes command.
+ *     It does not consider any NFC_ATTR_VENDOR_DATA.
+ * @HCI_DM_PUT_DATA: Allow to configure specific CLF registry as for example
+ *     RF trimmings or low level drivers configurations (I2C, SPI, SWP).
+ * @HCI_DM_UPDATE_AID: Allow to configure an AID routing into the CLF routing
+ *     table following RF technology, CLF mode or protocol.
+ * @HCI_DM_GET_INFO: Allow to retrieve CLF information.
+ * @HCI_DM_GET_DATA: Allow to retrieve CLF configurable data such as low
+ *     level drivers configurations or RF trimmings.
+ * @HCI_DM_DIRECT_LOAD: Allow to load a firmware into the CLF. A complete
+ *     packet can be more than 8KB.
+ * @HCI_DM_RESET: Allow to run a CLF reset in order to "commit" CLF
+ *     configuration changes without CLF power off.
+ * @HCI_GET_PARAM: Allow to retrieve an HCI CLF parameter (for example the
+ *     white list).
+ * @HCI_DM_FIELD_GENERATOR: Allow to generate different kind of RF
+ *     technology. When using this command to anti-collision is done.
+ * @HCI_LOOPBACK: Allow to echo a command and test the Dh to CLF
+ *     connectivity.
+ * @HCI_DM_VDC_MEASUREMENT_VALUE: Allow to measure the field applied on the
+ *     CLF antenna. A value between 0 and 0x0f is returned. 0 is maximum.
+ * @HCI_DM_FWUPD_START: Allow to put CLF into firmware update mode. It is a
+ *     specific CLF command as there is no GPIO for this.
+ * @HCI_DM_FWUPD_END:  Allow to complete firmware update.
+ * @HCI_DM_VDC_VALUE_COMPARISON: Allow to compare the field applied on the
+ *     CLF antenna to a reference value.
+ * @MANUFACTURER_SPECIFIC: Allow to retrieve manufacturer specific data
+ *     received during a NCI_CORE_INIT_CMD.
+ */
+enum nfc_vendor_cmds {
+       FACTORY_MODE,
+       HCI_CLEAR_ALL_PIPES,
+       HCI_DM_PUT_DATA,
+       HCI_DM_UPDATE_AID,
+       HCI_DM_GET_INFO,
+       HCI_DM_GET_DATA,
+       HCI_DM_DIRECT_LOAD,
+       HCI_DM_RESET,
+       HCI_GET_PARAM,
+       HCI_DM_FIELD_GENERATOR,
+       HCI_LOOPBACK,
+       HCI_DM_FWUPD_START,
+       HCI_DM_FWUPD_END,
+       HCI_DM_VDC_MEASUREMENT_VALUE,
+       HCI_DM_VDC_VALUE_COMPARISON,
+       MANUFACTURER_SPECIFIC,
+};
+
+struct st_nci_vendor_info {
+       struct completion req_completion;
+       struct sk_buff *rx_skb;
+};
+
 struct st_nci_info {
        struct llt_ndlc *ndlc;
        unsigned long flags;
+
        struct st_nci_se_info se_info;
+       struct st_nci_vendor_info vendor_info;
 };
 
 void st_nci_remove(struct nci_dev *ndev);
 int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
-               int phy_tailroom);
+                int phy_tailroom, struct st_nci_se_status *se_status);
+
+int st_nci_se_init(struct nci_dev *ndev, struct st_nci_se_status *se_status);
+void st_nci_se_deinit(struct nci_dev *ndev);
+
+int st_nci_discover_se(struct nci_dev *ndev);
+int st_nci_enable_se(struct nci_dev *ndev, u32 se_idx);
+int st_nci_disable_se(struct nci_dev *ndev, u32 se_idx);
+int st_nci_se_io(struct nci_dev *ndev, u32 se_idx,
+                               u8 *apdu, size_t apdu_length,
+                               se_io_cb_t cb, void *cb_context);
+int st_nci_hci_load_session(struct nci_dev *ndev);
+void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
+                                       u8 event, struct sk_buff *skb);
+void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
+                                               struct sk_buff *skb);
+
+void st_nci_hci_loopback_event_received(struct nci_dev *ndev, u8 event,
+                                       struct sk_buff *skb);
+int st_nci_vendor_cmds_init(struct nci_dev *ndev);
 
 #endif /* __LOCAL_ST_NCI_H_ */
diff --git a/drivers/nfc/st-nci/st-nci_se.h b/drivers/nfc/st-nci/st-nci_se.h
deleted file mode 100644 (file)
index ea66e87..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Secure Element Driver for STMicroelectronics NFC NCI Chip
- *
- * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
- *
- * 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 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/>.
- */
-#ifndef __LOCAL_ST_NCI_SE_H_
-#define __LOCAL_ST_NCI_SE_H_
-
-/*
- * ref ISO7816-3 chap 8.1. the initial character TS is followed by a
- * sequence of at most 32 characters.
- */
-#define ST_NCI_ESE_MAX_LENGTH  33
-#define ST_NCI_HCI_HOST_ID_ESE 0xc0
-
-struct st_nci_se_info {
-       u8 atr[ST_NCI_ESE_MAX_LENGTH];
-       struct completion req_completion;
-
-       struct timer_list bwi_timer;
-       int wt_timeout; /* in msecs */
-       bool bwi_active;
-
-       struct timer_list se_active_timer;
-       bool se_active;
-
-       bool xch_error;
-
-       se_io_cb_t cb;
-       void *cb_context;
-};
-
-int st_nci_se_init(struct nci_dev *ndev);
-void st_nci_se_deinit(struct nci_dev *ndev);
-
-int st_nci_discover_se(struct nci_dev *ndev);
-int st_nci_enable_se(struct nci_dev *ndev, u32 se_idx);
-int st_nci_disable_se(struct nci_dev *ndev, u32 se_idx);
-int st_nci_se_io(struct nci_dev *ndev, u32 se_idx,
-               u8 *apdu, size_t apdu_length,
-               se_io_cb_t cb, void *cb_context);
-int st_nci_hci_load_session(struct nci_dev *ndev);
-void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
-                       u8 event, struct sk_buff *skb);
-void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
-                       struct sk_buff *skb);
-
-
-#endif /* __LOCAL_ST_NCI_SE_H_ */
diff --git a/drivers/nfc/st-nci/vendor_cmds.c b/drivers/nfc/st-nci/vendor_cmds.c
new file mode 100644 (file)
index 0000000..b5debce
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * Proprietary commands extension for STMicroelectronics NFC NCI Chip
+ *
+ * Copyright (C) 2014-2015  STMicroelectronics SAS. All rights reserved.
+ *
+ * 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 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/>.
+ */
+
+#include <net/genetlink.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/delay.h>
+#include <net/nfc/nci_core.h>
+
+#include "st-nci.h"
+
+#define ST_NCI_HCI_DM_GETDATA                  0x10
+#define ST_NCI_HCI_DM_PUTDATA                  0x11
+#define ST_NCI_HCI_DM_LOAD                     0x12
+#define ST_NCI_HCI_DM_GETINFO                  0x13
+#define ST_NCI_HCI_DM_FWUPD_START              0x14
+#define ST_NCI_HCI_DM_FWUPD_STOP               0x15
+#define ST_NCI_HCI_DM_UPDATE_AID               0x20
+#define ST_NCI_HCI_DM_RESET                    0x3e
+
+#define ST_NCI_HCI_DM_FIELD_GENERATOR          0x32
+#define ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE    0x33
+#define ST_NCI_HCI_DM_VDC_VALUE_COMPARISON     0x34
+
+#define ST_NCI_FACTORY_MODE_ON                 1
+#define ST_NCI_FACTORY_MODE_OFF                        0
+
+#define ST_NCI_EVT_POST_DATA                   0x02
+
+struct get_param_data {
+       u8 gate;
+       u8 data;
+} __packed;
+
+static int st_nci_factory_mode(struct nfc_dev *dev, void *data,
+                              size_t data_len)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+       struct st_nci_info *info = nci_get_drvdata(ndev);
+
+       if (data_len != 1)
+               return -EINVAL;
+
+       pr_debug("factory mode: %x\n", ((u8 *)data)[0]);
+
+       switch (((u8 *)data)[0]) {
+       case ST_NCI_FACTORY_MODE_ON:
+               test_and_set_bit(ST_NCI_FACTORY_MODE, &info->flags);
+       break;
+       case ST_NCI_FACTORY_MODE_OFF:
+               clear_bit(ST_NCI_FACTORY_MODE, &info->flags);
+       break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int st_nci_hci_clear_all_pipes(struct nfc_dev *dev, void *data,
+                                     size_t data_len)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       return nci_hci_clear_all_pipes(ndev);
+}
+
+static int st_nci_hci_dm_put_data(struct nfc_dev *dev, void *data,
+                                 size_t data_len)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+                               ST_NCI_HCI_DM_PUTDATA, data,
+                               data_len, NULL);
+}
+
+static int st_nci_hci_dm_update_aid(struct nfc_dev *dev, void *data,
+                                   size_t data_len)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+                       ST_NCI_HCI_DM_UPDATE_AID, data, data_len, NULL);
+}
+
+static int st_nci_hci_dm_get_info(struct nfc_dev *dev, void *data,
+                                 size_t data_len)
+{
+       int r;
+       struct sk_buff *msg, *skb;
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETINFO,
+                            data, data_len, &skb);
+       if (r)
+               goto exit;
+
+       msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+                                            HCI_DM_GET_INFO, skb->len);
+       if (!msg) {
+               r = -ENOMEM;
+               goto free_skb;
+       }
+
+       if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+               kfree_skb(msg);
+               r = -ENOBUFS;
+               goto free_skb;
+       }
+
+       r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+       kfree_skb(skb);
+exit:
+       return r;
+}
+
+static int st_nci_hci_dm_get_data(struct nfc_dev *dev, void *data,
+                                 size_t data_len)
+{
+       int r;
+       struct sk_buff *msg, *skb;
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETDATA,
+                            data, data_len, &skb);
+       if (r)
+               goto exit;
+
+       msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+                                            HCI_DM_GET_DATA, skb->len);
+       if (!msg) {
+               r = -ENOMEM;
+               goto free_skb;
+       }
+
+       if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+               kfree_skb(msg);
+               r = -ENOBUFS;
+               goto free_skb;
+       }
+
+       r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+       kfree_skb(skb);
+exit:
+       return r;
+}
+
+static int st_nci_hci_dm_fwupd_start(struct nfc_dev *dev, void *data,
+                                    size_t data_len)
+{
+       int r;
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       dev->fw_download_in_progress = true;
+       r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+                       ST_NCI_HCI_DM_FWUPD_START, data, data_len, NULL);
+       if (r)
+               dev->fw_download_in_progress = false;
+
+       return r;
+}
+
+static int st_nci_hci_dm_fwupd_end(struct nfc_dev *dev, void *data,
+                                  size_t data_len)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+                       ST_NCI_HCI_DM_FWUPD_STOP, data, data_len, NULL);
+}
+
+static int st_nci_hci_dm_direct_load(struct nfc_dev *dev, void *data,
+                                    size_t data_len)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       if (dev->fw_download_in_progress) {
+               dev->fw_download_in_progress = false;
+               return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+                               ST_NCI_HCI_DM_LOAD, data, data_len, NULL);
+       }
+       return -EPROTO;
+}
+
+static int st_nci_hci_dm_reset(struct nfc_dev *dev, void *data,
+                              size_t data_len)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+                       ST_NCI_HCI_DM_RESET, data, data_len, NULL);
+       msleep(200);
+
+       return 0;
+}
+
+static int st_nci_hci_get_param(struct nfc_dev *dev, void *data,
+                               size_t data_len)
+{
+       int r;
+       struct sk_buff *msg, *skb;
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+       struct get_param_data *param = (struct get_param_data *)data;
+
+       if (data_len < sizeof(struct get_param_data))
+               return -EPROTO;
+
+       r = nci_hci_get_param(ndev, param->gate, param->data, &skb);
+       if (r)
+               goto exit;
+
+       msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+                                            HCI_GET_PARAM, skb->len);
+       if (!msg) {
+               r = -ENOMEM;
+               goto free_skb;
+       }
+
+       if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+               kfree_skb(msg);
+               r = -ENOBUFS;
+               goto free_skb;
+       }
+
+       r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+       kfree_skb(skb);
+exit:
+       return r;
+}
+
+static int st_nci_hci_dm_field_generator(struct nfc_dev *dev, void *data,
+                                        size_t data_len)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+                               ST_NCI_HCI_DM_FIELD_GENERATOR, data, data_len, NULL);
+}
+
+static int st_nci_hci_dm_vdc_measurement_value(struct nfc_dev *dev, void *data,
+                                              size_t data_len)
+{
+       int r;
+       struct sk_buff *msg, *skb;
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       if (data_len != 4)
+               return -EPROTO;
+
+       r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+                            ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE,
+                            data, data_len, &skb);
+       if (r)
+               goto exit;
+
+       msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+                               HCI_DM_VDC_MEASUREMENT_VALUE, skb->len);
+       if (!msg) {
+               r = -ENOMEM;
+               goto free_skb;
+       }
+
+       if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+               kfree_skb(msg);
+               r = -ENOBUFS;
+               goto free_skb;
+       }
+
+       r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+       kfree_skb(skb);
+exit:
+       return r;
+}
+
+static int st_nci_hci_dm_vdc_value_comparison(struct nfc_dev *dev, void *data,
+                                             size_t data_len)
+{
+       int r;
+       struct sk_buff *msg, *skb;
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       if (data_len != 2)
+               return -EPROTO;
+
+       r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
+                            ST_NCI_HCI_DM_VDC_VALUE_COMPARISON,
+                            data, data_len, &skb);
+       if (r)
+               goto exit;
+
+       msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+                                       HCI_DM_VDC_VALUE_COMPARISON, skb->len);
+       if (!msg) {
+               r = -ENOMEM;
+               goto free_skb;
+       }
+
+       if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+               kfree_skb(msg);
+               r = -ENOBUFS;
+               goto free_skb;
+       }
+
+       r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+       kfree_skb(skb);
+exit:
+       return r;
+}
+
+void st_nci_hci_loopback_event_received(struct nci_dev *ndev, u8 event,
+                                       struct sk_buff *skb)
+{
+       struct st_nci_info *info = nci_get_drvdata(ndev);
+
+       switch (event) {
+       case ST_NCI_EVT_POST_DATA:
+               info->vendor_info.rx_skb = skb;
+       break;
+       default:
+               nfc_err(&ndev->nfc_dev->dev, "Unexpected event on loopback gate\n");
+       }
+       complete(&info->vendor_info.req_completion);
+}
+EXPORT_SYMBOL(st_nci_hci_loopback_event_received);
+
+static int st_nci_hci_loopback(struct nfc_dev *dev, void *data,
+                              size_t data_len)
+{
+       int r;
+       struct sk_buff *msg;
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+       struct st_nci_info *info = nci_get_drvdata(ndev);
+
+       if (data_len <= 0)
+               return -EPROTO;
+
+       reinit_completion(&info->vendor_info.req_completion);
+       info->vendor_info.rx_skb = NULL;
+
+       r = nci_hci_send_event(ndev, NCI_HCI_LOOPBACK_GATE,
+                              ST_NCI_EVT_POST_DATA, data, data_len);
+       if (r != data_len) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       wait_for_completion_interruptible(&info->vendor_info.req_completion);
+
+       if (!info->vendor_info.rx_skb ||
+           info->vendor_info.rx_skb->len != data_len) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       msg = nfc_vendor_cmd_alloc_reply_skb(ndev->nfc_dev,
+                                       ST_NCI_VENDOR_OUI,
+                                       HCI_LOOPBACK,
+                                       info->vendor_info.rx_skb->len);
+       if (!msg) {
+               r = -ENOMEM;
+               goto free_skb;
+       }
+
+       if (nla_put(msg, NFC_ATTR_VENDOR_DATA, info->vendor_info.rx_skb->len,
+                   info->vendor_info.rx_skb->data)) {
+               kfree_skb(msg);
+               r = -ENOBUFS;
+               goto free_skb;
+       }
+
+       r = nfc_vendor_cmd_reply(msg);
+free_skb:
+       kfree_skb(info->vendor_info.rx_skb);
+exit:
+       return r;
+}
+
+static int st_nci_manufacturer_specific(struct nfc_dev *dev, void *data,
+                                       size_t data_len)
+{
+       struct sk_buff *msg;
+       struct nci_dev *ndev = nfc_get_drvdata(dev);
+
+       msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
+                                       MANUFACTURER_SPECIFIC,
+                                       sizeof(ndev->manufact_specific_info));
+       if (!msg)
+               return -ENOMEM;
+
+       if (nla_put(msg, NFC_ATTR_VENDOR_DATA, sizeof(ndev->manufact_specific_info),
+                   &ndev->manufact_specific_info)) {
+               kfree_skb(msg);
+               return -ENOBUFS;
+       }
+
+       return nfc_vendor_cmd_reply(msg);
+}
+
+static struct nfc_vendor_cmd st_nci_vendor_cmds[] = {
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = FACTORY_MODE,
+               .doit = st_nci_factory_mode,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_CLEAR_ALL_PIPES,
+               .doit = st_nci_hci_clear_all_pipes,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_DM_PUT_DATA,
+               .doit = st_nci_hci_dm_put_data,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_DM_UPDATE_AID,
+               .doit = st_nci_hci_dm_update_aid,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_DM_GET_INFO,
+               .doit = st_nci_hci_dm_get_info,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_DM_GET_DATA,
+               .doit = st_nci_hci_dm_get_data,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_DM_DIRECT_LOAD,
+               .doit = st_nci_hci_dm_direct_load,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_DM_RESET,
+               .doit = st_nci_hci_dm_reset,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_GET_PARAM,
+               .doit = st_nci_hci_get_param,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_DM_FIELD_GENERATOR,
+               .doit = st_nci_hci_dm_field_generator,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_DM_FWUPD_START,
+               .doit = st_nci_hci_dm_fwupd_start,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_DM_FWUPD_END,
+               .doit = st_nci_hci_dm_fwupd_end,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_LOOPBACK,
+               .doit = st_nci_hci_loopback,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_DM_VDC_MEASUREMENT_VALUE,
+               .doit = st_nci_hci_dm_vdc_measurement_value,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = HCI_DM_VDC_VALUE_COMPARISON,
+               .doit = st_nci_hci_dm_vdc_value_comparison,
+       },
+       {
+               .vendor_id = ST_NCI_VENDOR_OUI,
+               .subcmd = MANUFACTURER_SPECIFIC,
+               .doit = st_nci_manufacturer_specific,
+       },
+};
+
+int st_nci_vendor_cmds_init(struct nci_dev *ndev)
+{
+       struct st_nci_info *info = nci_get_drvdata(ndev);
+
+       init_completion(&info->vendor_info.req_completion);
+       return nfc_set_vendor_cmds(ndev->nfc_dev, st_nci_vendor_cmds,
+                                  sizeof(st_nci_vendor_cmds));
+}
+EXPORT_SYMBOL(st_nci_vendor_cmds_init);
index 97edab4bbdf8bb7c0a3ac1a21fdf45e4e59a4270..ded6489c3eeb64137204ae4e0fd2076faa060fb1 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for ST21NFCA HCI based NFC driver
 #
 
-st21nfca_hci-objs = st21nfca.o st21nfca_dep.o st21nfca_se.o
+st21nfca_hci-objs = core.o dep.o se.o vendor_cmds.o
 obj-$(CONFIG_NFC_ST21NFCA)     += st21nfca_hci.o
 
 st21nfca_i2c-objs  = i2c.o
similarity index 97%
rename from drivers/nfc/st21nfca/st21nfca.c
rename to drivers/nfc/st21nfca/core.c
index 051286562fab3d72fb80bf822704cac7d3817c01..dd8b150fbffa0203eecde797693d8b1d08b4a10d 100644 (file)
@@ -22,8 +22,6 @@
 #include <net/nfc/llc.h>
 
 #include "st21nfca.h"
-#include "st21nfca_dep.h"
-#include "st21nfca_se.h"
 
 #define DRIVER_DESC "HCI NFC driver for ST21NFCA"
 
@@ -87,12 +85,13 @@ static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES);
 
 static struct nfc_hci_gate st21nfca_gates[] = {
        {NFC_HCI_ADMIN_GATE, NFC_HCI_ADMIN_PIPE},
+       {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_LINK_MGMT_PIPE},
+       {ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE},
+
        {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE},
        {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE},
-       {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_LINK_MGMT_PIPE},
        {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE},
        {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE},
-       {ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE},
        {ST21NFCA_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE},
        {ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE},
        {ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
@@ -163,7 +162,6 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
                r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
                                        ST21NFCA_DM_GETINFO, pipe_info,
                                        sizeof(pipe_info), &skb_pipe_info);
-
                if (r)
                        continue;
 
@@ -185,43 +183,33 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
                        continue;
                }
 
-               for (j = 0; (j < ARRAY_SIZE(st21nfca_gates)) &&
+               for (j = 3; (j < ARRAY_SIZE(st21nfca_gates)) &&
                        (st21nfca_gates[j].gate != info->dst_gate_id) ; j++)
                        ;
 
                if (j < ARRAY_SIZE(st21nfca_gates) &&
                        st21nfca_gates[j].gate == info->dst_gate_id &&
                        ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) {
-                       st21nfca_gates[j].pipe = pipe_info[2];
+                       hdev->init_data.gates[j].pipe = pipe_info[2];
 
                        hdev->gate2pipe[st21nfca_gates[j].gate] =
-                                                       st21nfca_gates[j].pipe;
-                       hdev->pipes[st21nfca_gates[j].pipe].gate =
-                                                       st21nfca_gates[j].gate;
-                       hdev->pipes[st21nfca_gates[j].pipe].dest_host =
-                                                       info->src_host_id;
+                                               pipe_info[2];
+                       hdev->pipes[pipe_info[2]].gate =
+                                               st21nfca_gates[j].gate;
+                       hdev->pipes[pipe_info[2]].dest_host =
+                                               info->src_host_id;
                }
                kfree_skb(skb_pipe_info);
        }
 
        /*
-        * 3 gates have a well known pipe ID.
-        * They will never appear in the pipe list
+        * 3 gates have a well known pipe ID. Only NFC_HCI_LINK_MGMT_GATE
+        * is not yet open at this stage.
         */
-       if (skb_pipe_list->len + 3 < ARRAY_SIZE(st21nfca_gates)) {
-               for (i = skb_pipe_list->len + 3;
-                               i < ARRAY_SIZE(st21nfca_gates) - 2; i++) {
-                       r = nfc_hci_connect_gate(hdev,
-                                       NFC_HCI_HOST_CONTROLLER_ID,
-                                       st21nfca_gates[i].gate,
-                                       st21nfca_gates[i].pipe);
-                       if (r < 0)
-                               goto free_list;
-               }
-       }
+       r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID,
+                                NFC_HCI_LINK_MGMT_GATE,
+                                NFC_HCI_LINK_MGMT_PIPE);
 
-       memcpy(hdev->init_data.gates, st21nfca_gates, sizeof(st21nfca_gates));
-free_list:
        kfree_skb(skb_pipe_list);
        return r;
 }
@@ -905,6 +893,8 @@ static int st21nfca_admin_event_received(struct nfc_hci_dev *hdev, u8 event,
                        }
                }
        break;
+       default:
+               nfc_err(&hdev->ndev->dev, "Unexpected event on admin gate\n");
        }
        kfree_skb(skb);
        return 0;
@@ -933,6 +923,8 @@ static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe,
                                                        event, skb);
        case ST21NFCA_APDU_READER_GATE:
                return st21nfca_apdu_reader_event_received(hdev, event, skb);
+       case NFC_HCI_LOOPBACK_GATE:
+               return st21nfca_hci_loopback_event_received(hdev, event, skb);
        default:
                return 1;
        }
@@ -993,7 +985,6 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
         * persistent info to discriminate 2 identical chips
         */
        dev_num = find_first_zero_bit(dev_mask, ST21NFCA_NUM_DEVICES);
-
        if (dev_num >= ST21NFCA_NUM_DEVICES)
                return -ENODEV;
 
@@ -1035,6 +1026,7 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
        *hdev = info->hdev;
        st21nfca_dep_init(info->hdev);
        st21nfca_se_init(info->hdev);
+       st21nfca_vendor_cmds_init(info->hdev);
 
        return 0;
 
similarity index 99%
rename from drivers/nfc/st21nfca/st21nfca_dep.c
rename to drivers/nfc/st21nfca/dep.c
index 8882181d65de7c71c3f5940d9aee422d5a57ecd1..798a32bbac5ddca479592857c74753bf84a5faf6 100644 (file)
@@ -17,7 +17,6 @@
 #include <net/nfc/hci.h>
 
 #include "st21nfca.h"
-#include "st21nfca_dep.h"
 
 #define ST21NFCA_NFCIP1_INITIATOR 0x00
 #define ST21NFCA_NFCIP1_REQ 0xd4
@@ -436,6 +435,7 @@ int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
                        return r;
                return 0;
        default:
+               nfc_err(&hdev->ndev->dev, "Unexpected event on card f gate\n");
                return 1;
        }
        kfree_skb(skb);
index a32143951616fde13b5828938f3408b5209130cd..a98da33e680a462abb4623708c19346763f0dee3 100644 (file)
@@ -94,6 +94,7 @@ struct st21nfca_i2c_phy {
        int hard_fault;
        struct mutex phy_lock;
 };
+
 static u8 len_seq[] = { 16, 24, 12, 29 };
 static u16 wait_tab[] = { 2, 3, 5, 15, 20, 40};
 
similarity index 96%
rename from drivers/nfc/st21nfca/st21nfca_se.c
rename to drivers/nfc/st21nfca/se.c
index 3197e9bb66f73c53d940a3292b2140a2e7e72171..c79d99b24c9616f3426295e409bae19efbf1e422 100644 (file)
 #include <net/nfc/hci.h>
 
 #include "st21nfca.h"
-#include "st21nfca_se.h"
 
 #define ST21NFCA_EVT_UICC_ACTIVATE             0x10
-#define ST21NFCA_EVT_UICC_DEACTIVATE   0x13
+#define ST21NFCA_EVT_UICC_DEACTIVATE           0x13
 #define ST21NFCA_EVT_SE_HARD_RESET             0x20
 #define ST21NFCA_EVT_SE_SOFT_RESET             0x11
 #define ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER   0x21
@@ -101,7 +100,7 @@ static int st21nfca_hci_control_se(struct nfc_hci_dev *hdev, u32 se_idx,
                                u8 state)
 {
        struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
-       int r;
+       int r, i;
        struct sk_buff *sk_host_list;
        u8 se_event, host_id;
 
@@ -149,7 +148,10 @@ static int st21nfca_hci_control_se(struct nfc_hci_dev *hdev, u32 se_idx,
        if (r < 0)
                return r;
 
-       host_id = sk_host_list->data[sk_host_list->len - 1];
+       for (i = 0; i < sk_host_list->len &&
+               sk_host_list->data[i] != se_idx; i++)
+               ;
+       host_id = sk_host_list->data[i];
        kfree_skb(sk_host_list);
 
        if (state == ST21NFCA_SE_MODE_ON && host_id == se_idx)
@@ -165,6 +167,9 @@ int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev)
        struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
        int se_count = 0;
 
+       if (test_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks))
+               return 0;
+
        if (info->se_status->is_uicc_present) {
                nfc_add_se(hdev->ndev, NFC_HCI_UICC_HOST_ID, NFC_SE_UICC);
                se_count++;
@@ -189,7 +194,6 @@ int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx)
         * Same for eSE.
         */
        r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_ON);
-
        if (r == ST21NFCA_ESE_HOST_ID) {
                st21nfca_se_get_atr(hdev);
                r = nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE,
@@ -340,6 +344,7 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
                r = nfc_se_transaction(hdev->ndev, host, transaction);
                break;
        default:
+               nfc_err(&hdev->ndev->dev, "Unexpected event on connectivity gate\n");
                return 1;
        }
        kfree_skb(skb);
@@ -371,6 +376,9 @@ int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev,
                mod_timer(&info->se_info.bwi_timer, jiffies +
                                msecs_to_jiffies(info->se_info.wt_timeout));
                break;
+       default:
+               nfc_err(&hdev->ndev->dev, "Unexpected event on apdu reader gate\n");
+               return 1;
        }
 
 exit:
index 15a78d330a9f32a21d3a58aaeff1a095ad84a0cd..94ffb0501e872bf01c43bd319c29120e9c27b199 100644 (file)
@@ -18,9 +18,8 @@
 #define __LOCAL_ST21NFCA_H_
 
 #include <net/nfc/hci.h>
-
-#include "st21nfca_dep.h"
-#include "st21nfca_se.h"
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
 
 #define HCI_MODE 0
 
 #define ST21NFCA_HCI_LLC_MAX_SIZE       (ST21NFCA_HCI_LLC_LEN_CRC + 1 + \
                                        ST21NFCA_HCI_LLC_MAX_PAYLOAD)
 
+/* Reader RF commands */
+#define ST21NFCA_WR_XCHG_DATA           0x10
+
+#define ST21NFCA_DEVICE_MGNT_GATE       0x01
+#define ST21NFCA_RF_READER_F_GATE       0x14
+#define ST21NFCA_RF_CARD_F_GATE                0x24
+#define ST21NFCA_APDU_READER_GATE      0xf0
+#define ST21NFCA_CONNECTIVITY_GATE     0x41
+
+/*
+ * ref ISO7816-3 chap 8.1. the initial character TS is followed by a
+ * sequence of at most 32 characters.
+ */
+#define ST21NFCA_ESE_MAX_LENGTH                33
+#define ST21NFCA_ESE_HOST_ID           0xc0
+
 #define DRIVER_DESC "HCI NFC driver for ST21NFCA"
 
-#define ST21NFCA_HCI_MODE 0
+#define ST21NFCA_HCI_MODE              0
+#define ST21NFCA_NUM_DEVICES           256
 
-#define ST21NFCA_NUM_DEVICES 256
+#define ST21NFCA_VENDOR_OUI            0x0080E1 /* STMicroelectronics */
+#define ST21NFCA_FACTORY_MODE          2
 
 struct st21nfca_se_status {
        bool is_ese_present;
        bool is_uicc_present;
 };
 
-int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
-                      char *llc_name, int phy_headroom, int phy_tailroom,
-                      int phy_payload, struct nfc_hci_dev **hdev,
-                          struct st21nfca_se_status *se_status);
-void st21nfca_hci_remove(struct nfc_hci_dev *hdev);
-
 enum st21nfca_state {
        ST21NFCA_ST_COLD,
        ST21NFCA_ST_READY,
 };
 
+/**
+ * enum nfc_vendor_cmds - supported nfc vendor commands
+ *
+ * @FACTORY_MODE: Allow to set the driver into a mode where no secure element
+ *     are activated. It does not consider any NFC_ATTR_VENDOR_DATA.
+ * @HCI_CLEAR_ALL_PIPES: Allow to execute a HCI clear all pipes command.
+ *     It does not consider any NFC_ATTR_VENDOR_DATA.
+ * @HCI_DM_PUT_DATA: Allow to configure specific CLF registry as for example
+ *     RF trimmings or low level drivers configurations (I2C, SPI, SWP).
+ * @HCI_DM_UPDATE_AID: Allow to configure an AID routing into the CLF routing
+ *     table following RF technology, CLF mode or protocol.
+ * @HCI_DM_GET_INFO: Allow to retrieve CLF information.
+ * @HCI_DM_GET_DATA: Allow to retrieve CLF configurable data such as low
+ *     level drivers configurations or RF trimmings.
+ * @HCI_DM_LOAD: Allow to load a firmware into the CLF. A complete
+ *     packet can be more than 8KB.
+ * @HCI_DM_RESET: Allow to run a CLF reset in order to "commit" CLF
+ *     configuration changes without CLF power off.
+ * @HCI_GET_PARAM: Allow to retrieve an HCI CLF parameter (for example the
+ *     white list).
+ * @HCI_DM_FIELD_GENERATOR: Allow to generate different kind of RF
+ *     technology. When using this command to anti-collision is done.
+ * @HCI_LOOPBACK: Allow to echo a command and test the Dh to CLF
+ *     connectivity.
+ */
+enum nfc_vendor_cmds {
+       FACTORY_MODE,
+       HCI_CLEAR_ALL_PIPES,
+       HCI_DM_PUT_DATA,
+       HCI_DM_UPDATE_AID,
+       HCI_DM_GET_INFO,
+       HCI_DM_GET_DATA,
+       HCI_DM_LOAD,
+       HCI_DM_RESET,
+       HCI_GET_PARAM,
+       HCI_DM_FIELD_GENERATOR,
+       HCI_LOOPBACK,
+};
+
+struct st21nfca_vendor_info {
+       struct completion req_completion;
+       struct sk_buff *rx_skb;
+};
+
+struct st21nfca_dep_info {
+       struct sk_buff *tx_pending;
+       struct work_struct tx_work;
+       u8 curr_nfc_dep_pni;
+       u32 idx;
+       u8 to;
+       u8 did;
+       u8 bsi;
+       u8 bri;
+       u8 lri;
+} __packed;
+
+struct st21nfca_se_info {
+       u8 atr[ST21NFCA_ESE_MAX_LENGTH];
+       struct completion req_completion;
+
+       struct timer_list bwi_timer;
+       int wt_timeout; /* in msecs */
+       bool bwi_active;
+
+       struct timer_list se_active_timer;
+       bool se_active;
+       int expected_pipes;
+       int count_pipes;
+
+       bool xch_error;
+
+       se_io_cb_t cb;
+       void *cb_context;
+};
+
 struct st21nfca_hci_info {
        struct nfc_phy_ops *phy_ops;
        void *phy_id;
@@ -85,15 +171,41 @@ struct st21nfca_hci_info {
 
        struct st21nfca_dep_info dep_info;
        struct st21nfca_se_info se_info;
+       struct st21nfca_vendor_info vendor_info;
 };
 
-/* Reader RF commands */
-#define ST21NFCA_WR_XCHG_DATA           0x10
+int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
+                      char *llc_name, int phy_headroom, int phy_tailroom,
+                      int phy_payload, struct nfc_hci_dev **hdev,
+                      struct st21nfca_se_status *se_status);
+void st21nfca_hci_remove(struct nfc_hci_dev *hdev);
 
-#define ST21NFCA_DEVICE_MGNT_GATE       0x01
-#define ST21NFCA_RF_READER_F_GATE       0x14
-#define ST21NFCA_RF_CARD_F_GATE                        0x24
-#define ST21NFCA_APDU_READER_GATE              0xf0
-#define ST21NFCA_CONNECTIVITY_GATE             0x41
+int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
+                               u8 event, struct sk_buff *skb);
+int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb);
+
+int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len);
+int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb);
+void st21nfca_dep_init(struct nfc_hci_dev *hdev);
+void st21nfca_dep_deinit(struct nfc_hci_dev *hdev);
+
+int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
+                                       u8 event, struct sk_buff *skb);
+int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev,
+                                       u8 event, struct sk_buff *skb);
+
+int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev);
+int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx);
+int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx);
+int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx,
+               u8 *apdu, size_t apdu_length,
+               se_io_cb_t cb, void *cb_context);
+
+void st21nfca_se_init(struct nfc_hci_dev *hdev);
+void st21nfca_se_deinit(struct nfc_hci_dev *hdev);
+
+int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *ndev, u8 event,
+                                        struct sk_buff *skb);
+int st21nfca_vendor_cmds_init(struct nfc_hci_dev *ndev);
 
 #endif /* __LOCAL_ST21NFCA_H_ */
diff --git a/drivers/nfc/st21nfca/st21nfca_dep.h b/drivers/nfc/st21nfca/st21nfca_dep.h
deleted file mode 100644 (file)
index baf4664..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
- *
- * 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 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/>.
- */
-
-#ifndef __ST21NFCA_DEP_H
-#define __ST21NFCA_DEP_H
-
-#include <linux/skbuff.h>
-#include <linux/workqueue.h>
-
-struct st21nfca_dep_info {
-       struct sk_buff *tx_pending;
-       struct work_struct tx_work;
-       u8 curr_nfc_dep_pni;
-       u32 idx;
-       u8 to;
-       u8 did;
-       u8 bsi;
-       u8 bri;
-       u8 lri;
-} __packed;
-
-int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
-                               u8 event, struct sk_buff *skb);
-int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb);
-
-int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len);
-int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb);
-void st21nfca_dep_init(struct nfc_hci_dev *hdev);
-void st21nfca_dep_deinit(struct nfc_hci_dev *hdev);
-#endif /* __ST21NFCA_DEP_H */
diff --git a/drivers/nfc/st21nfca/st21nfca_se.h b/drivers/nfc/st21nfca/st21nfca_se.h
deleted file mode 100644 (file)
index b172cfc..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
- *
- * 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 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/>.
- */
-
-#ifndef __ST21NFCA_SE_H
-#define __ST21NFCA_SE_H
-
-#include <linux/skbuff.h>
-#include <linux/workqueue.h>
-
-/*
- * ref ISO7816-3 chap 8.1. the initial character TS is followed by a
- * sequence of at most 32 characters.
- */
-#define ST21NFCA_ESE_MAX_LENGTH                        33
-#define ST21NFCA_ESE_HOST_ID                   0xc0
-
-struct st21nfca_se_info {
-       u8 atr[ST21NFCA_ESE_MAX_LENGTH];
-       struct completion req_completion;
-
-       struct timer_list bwi_timer;
-       int wt_timeout; /* in msecs */
-       bool bwi_active;
-
-       struct timer_list se_active_timer;
-       bool se_active;
-       int expected_pipes;
-       int count_pipes;
-
-       bool xch_error;
-
-       se_io_cb_t cb;
-       void *cb_context;
-};
-
-int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
-                                       u8 event, struct sk_buff *skb);
-int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev,
-                                       u8 event, struct sk_buff *skb);
-
-int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev);
-int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx);
-int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx);
-int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx,
-               u8 *apdu, size_t apdu_length,
-               se_io_cb_t cb, void *cb_context);
-
-void st21nfca_se_init(struct nfc_hci_dev *hdev);
-void st21nfca_se_deinit(struct nfc_hci_dev *hdev);
-#endif /* __ST21NFCA_SE_H */
diff --git a/drivers/nfc/st21nfca/vendor_cmds.c b/drivers/nfc/st21nfca/vendor_cmds.c
new file mode 100644 (file)
index 0000000..ab765e5
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Proprietary commands extension for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014-2015  STMicroelectronics SAS. All rights reserved.
+ *
+ * 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 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/>.
+ */
+
+#include <net/genetlink.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "st21nfca.h"
+
+#define ST21NFCA_HCI_DM_GETDATA                        0x10
+#define ST21NFCA_HCI_DM_PUTDATA                        0x11
+#define ST21NFCA_HCI_DM_LOAD                   0x12
+#define ST21NFCA_HCI_DM_GETINFO                        0x13
+#define ST21NFCA_HCI_DM_UPDATE_AID             0x20
+#define ST21NFCA_HCI_DM_RESET                  0x3e
+
+#define ST21NFCA_HCI_DM_FIELD_GENERATOR                0x32
+
+#define ST21NFCA_FACTORY_MODE_ON               1
+#define ST21NFCA_FACTORY_MODE_OFF              0
+
+#define ST21NFCA_EVT_POST_DATA                 0x02
+
+struct get_param_data {
+       u8 gate;
+       u8 data;
+} __packed;
+
+static int st21nfca_factory_mode(struct nfc_dev *dev, void *data,
+                              size_t data_len)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+       if (data_len != 1)
+               return -EINVAL;
+
+       pr_debug("factory mode: %x\n", ((u8 *)data)[0]);
+
+       switch (((u8 *)data)[0]) {
+       case ST21NFCA_FACTORY_MODE_ON:
+               test_and_set_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks);
+       break;
+       case ST21NFCA_FACTORY_MODE_OFF:
+               clear_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks);
+       break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int st21nfca_hci_clear_all_pipes(struct nfc_dev *dev, void *data,
+                                     size_t data_len)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+       return nfc_hci_disconnect_all_gates(hdev);
+}
+
+static int st21nfca_hci_dm_put_data(struct nfc_dev *dev, void *data,
+                                 size_t data_len)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+       return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                               ST21NFCA_HCI_DM_PUTDATA, data,
+                               data_len, NULL);
+}
+
+static int st21nfca_hci_dm_update_aid(struct nfc_dev *dev, void *data,
+                                   size_t data_len)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+       return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                       ST21NFCA_HCI_DM_UPDATE_AID, data, data_len, NULL);
+}
+
+static int st21nfca_hci_dm_get_info(struct nfc_dev *dev, void *data,
+                                   size_t data_len)
+{
+       int r;
+       struct sk_buff *msg, *skb;
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+       r = nfc_hci_send_cmd(hdev,
+                            ST21NFCA_DEVICE_MGNT_GATE,
+                            ST21NFCA_HCI_DM_GETINFO,
+                            data, data_len, &skb);
+       if (r)
+               goto exit;
+
+       msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
+                                            HCI_DM_GET_INFO, skb->len);
+       if (!msg) {
+               r = -ENOMEM;
+               goto free_skb;
+       }
+
+       if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+               kfree_skb(msg);
+               r = -ENOBUFS;
+               goto free_skb;
+       }
+
+       r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+       kfree_skb(skb);
+exit:
+       return r;
+}
+
+static int st21nfca_hci_dm_get_data(struct nfc_dev *dev, void *data,
+                                   size_t data_len)
+{
+       int r;
+       struct sk_buff *msg, *skb;
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+       r = nfc_hci_send_cmd(hdev,
+                            ST21NFCA_DEVICE_MGNT_GATE,
+                            ST21NFCA_HCI_DM_GETDATA,
+                            data, data_len, &skb);
+       if (r)
+               goto exit;
+
+       msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
+                                            HCI_DM_GET_DATA, skb->len);
+       if (!msg) {
+               r = -ENOMEM;
+               goto free_skb;
+       }
+
+       if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+               kfree_skb(msg);
+               r = -ENOBUFS;
+               goto free_skb;
+       }
+
+       r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+       kfree_skb(skb);
+exit:
+       return r;
+}
+
+static int st21nfca_hci_dm_load(struct nfc_dev *dev, void *data,
+                               size_t data_len)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+       return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                               ST21NFCA_HCI_DM_LOAD, data, data_len, NULL);
+}
+
+static int st21nfca_hci_dm_reset(struct nfc_dev *dev, void *data,
+                                size_t data_len)
+{
+       int r;
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+       r = nfc_hci_send_cmd_async(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                       ST21NFCA_HCI_DM_RESET, data, data_len, NULL, NULL);
+       if (r < 0)
+               return r;
+
+       r = nfc_llc_stop(hdev->llc);
+       if (r < 0)
+               return r;
+
+       return nfc_llc_start(hdev->llc);
+}
+
+static int st21nfca_hci_get_param(struct nfc_dev *dev, void *data,
+                                 size_t data_len)
+{
+       int r;
+       struct sk_buff *msg, *skb;
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+       struct get_param_data *param = (struct get_param_data *)data;
+
+       if (data_len < sizeof(struct get_param_data))
+               return -EPROTO;
+
+       r = nfc_hci_get_param(hdev, param->gate, param->data, &skb);
+       if (r)
+               goto exit;
+
+       msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
+                                            HCI_GET_PARAM, skb->len);
+       if (!msg) {
+               r = -ENOMEM;
+               goto free_skb;
+       }
+
+       if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
+               kfree_skb(msg);
+               r = -ENOBUFS;
+               goto free_skb;
+       }
+
+       r = nfc_vendor_cmd_reply(msg);
+
+free_skb:
+       kfree_skb(skb);
+exit:
+       return r;
+}
+
+static int st21nfca_hci_dm_field_generator(struct nfc_dev *dev, void *data,
+                                          size_t data_len)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+
+       return nfc_hci_send_cmd(hdev,
+                               ST21NFCA_DEVICE_MGNT_GATE,
+                               ST21NFCA_HCI_DM_FIELD_GENERATOR,
+                               data, data_len, NULL);
+}
+
+int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *hdev, u8 event,
+                                        struct sk_buff *skb)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       switch (event) {
+       case ST21NFCA_EVT_POST_DATA:
+               info->vendor_info.rx_skb = skb;
+       break;
+       default:
+               nfc_err(&hdev->ndev->dev, "Unexpected event on loopback gate\n");
+       }
+       complete(&info->vendor_info.req_completion);
+       return 0;
+}
+EXPORT_SYMBOL(st21nfca_hci_loopback_event_received);
+
+static int st21nfca_hci_loopback(struct nfc_dev *dev, void *data,
+                                size_t data_len)
+{
+       int r;
+       struct sk_buff *msg;
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       if (data_len <= 0)
+               return -EPROTO;
+
+       reinit_completion(&info->vendor_info.req_completion);
+       info->vendor_info.rx_skb = NULL;
+
+       r = nfc_hci_send_event(hdev, NFC_HCI_LOOPBACK_GATE,
+                              ST21NFCA_EVT_POST_DATA, data, data_len);
+       if (r < 0) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       wait_for_completion_interruptible(&info->vendor_info.req_completion);
+       if (!info->vendor_info.rx_skb ||
+           info->vendor_info.rx_skb->len != data_len) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       msg = nfc_vendor_cmd_alloc_reply_skb(hdev->ndev,
+                                       ST21NFCA_VENDOR_OUI,
+                                       HCI_LOOPBACK,
+                                       info->vendor_info.rx_skb->len);
+       if (!msg) {
+               r = -ENOMEM;
+               goto free_skb;
+       }
+
+       if (nla_put(msg, NFC_ATTR_VENDOR_DATA, info->vendor_info.rx_skb->len,
+                   info->vendor_info.rx_skb->data)) {
+               kfree_skb(msg);
+               r = -ENOBUFS;
+               goto free_skb;
+       }
+
+       r = nfc_vendor_cmd_reply(msg);
+free_skb:
+       kfree_skb(info->vendor_info.rx_skb);
+exit:
+       return r;
+}
+
+static struct nfc_vendor_cmd st21nfca_vendor_cmds[] = {
+       {
+               .vendor_id = ST21NFCA_VENDOR_OUI,
+               .subcmd = FACTORY_MODE,
+               .doit = st21nfca_factory_mode,
+       },
+       {
+               .vendor_id = ST21NFCA_VENDOR_OUI,
+               .subcmd = HCI_CLEAR_ALL_PIPES,
+               .doit = st21nfca_hci_clear_all_pipes,
+       },
+       {
+               .vendor_id = ST21NFCA_VENDOR_OUI,
+               .subcmd = HCI_DM_PUT_DATA,
+               .doit = st21nfca_hci_dm_put_data,
+       },
+       {
+               .vendor_id = ST21NFCA_VENDOR_OUI,
+               .subcmd = HCI_DM_UPDATE_AID,
+               .doit = st21nfca_hci_dm_update_aid,
+       },
+       {
+               .vendor_id = ST21NFCA_VENDOR_OUI,
+               .subcmd = HCI_DM_GET_INFO,
+               .doit = st21nfca_hci_dm_get_info,
+       },
+       {
+               .vendor_id = ST21NFCA_VENDOR_OUI,
+               .subcmd = HCI_DM_GET_DATA,
+               .doit = st21nfca_hci_dm_get_data,
+       },
+       {
+               .vendor_id = ST21NFCA_VENDOR_OUI,
+               .subcmd = HCI_DM_LOAD,
+               .doit = st21nfca_hci_dm_load,
+       },
+       {
+               .vendor_id = ST21NFCA_VENDOR_OUI,
+               .subcmd = HCI_DM_RESET,
+               .doit = st21nfca_hci_dm_reset,
+       },
+       {
+               .vendor_id = ST21NFCA_VENDOR_OUI,
+               .subcmd = HCI_GET_PARAM,
+               .doit = st21nfca_hci_get_param,
+       },
+       {
+               .vendor_id = ST21NFCA_VENDOR_OUI,
+               .subcmd = HCI_DM_FIELD_GENERATOR,
+               .doit = st21nfca_hci_dm_field_generator,
+       },
+       {
+               .vendor_id = ST21NFCA_VENDOR_OUI,
+               .subcmd = HCI_LOOPBACK,
+               .doit = st21nfca_hci_loopback,
+       },
+};
+
+int st21nfca_vendor_cmds_init(struct nfc_hci_dev *hdev)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       init_completion(&info->vendor_info.req_completion);
+       return nfc_set_vendor_cmds(hdev->ndev, st21nfca_vendor_cmds,
+                                  sizeof(st21nfca_vendor_cmds));
+}
+EXPORT_SYMBOL(st21nfca_vendor_cmds_init);
index 70b0707fd9a98514b5785dbe3a6fc467f8206d07..123aa981c9d87844932b769cd3949ef6a1416a4c 100644 (file)
@@ -2211,6 +2211,12 @@ static const struct dev_pm_ops trf7970a_pm_ops = {
                        trf7970a_pm_runtime_resume, NULL)
 };
 
+static const struct of_device_id trf7970a_of_match[] = {
+       { .compatible = "ti,trf7970a", },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, trf7970a_of_match);
+
 static const struct spi_device_id trf7970a_id_table[] = {
        { "trf7970a", 0 },
        { }
@@ -2223,6 +2229,7 @@ static struct spi_driver trf7970a_spi_driver = {
        .id_table       = trf7970a_id_table,
        .driver         = {
                .name   = "trf7970a",
+               .of_match_table = of_match_ptr(trf7970a_of_match),
                .owner  = THIS_MODULE,
                .pm     = &trf7970a_pm_ops,
        },
index 55317fa9c9dca32557c40b1735ac2c2bec5d4714..0baf626da56ac5b142d75367a7cfdb52747c24ce 100644 (file)
@@ -579,22 +579,187 @@ err:
        }
 }
 
+static u32 __of_msi_map_rid(struct device *dev, struct device_node **np,
+                           u32 rid_in)
+{
+       struct device *parent_dev;
+       struct device_node *msi_controller_node;
+       struct device_node *msi_np = *np;
+       u32 map_mask, masked_rid, rid_base, msi_base, rid_len, phandle;
+       int msi_map_len;
+       bool matched;
+       u32 rid_out = rid_in;
+       const __be32 *msi_map = NULL;
+
+       /*
+        * Walk up the device parent links looking for one with a
+        * "msi-map" property.
+        */
+       for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
+               if (!parent_dev->of_node)
+                       continue;
+
+               msi_map = of_get_property(parent_dev->of_node,
+                                         "msi-map", &msi_map_len);
+               if (!msi_map)
+                       continue;
+
+               if (msi_map_len % (4 * sizeof(__be32))) {
+                       dev_err(parent_dev, "Error: Bad msi-map length: %d\n",
+                               msi_map_len);
+                       return rid_out;
+               }
+               /* We have a good parent_dev and msi_map, let's use them. */
+               break;
+       }
+       if (!msi_map)
+               return rid_out;
+
+       /* The default is to select all bits. */
+       map_mask = 0xffffffff;
+
+       /*
+        * Can be overridden by "msi-map-mask" property.  If
+        * of_property_read_u32() fails, the default is used.
+        */
+       of_property_read_u32(parent_dev->of_node, "msi-map-mask", &map_mask);
+
+       masked_rid = map_mask & rid_in;
+       matched = false;
+       while (!matched && msi_map_len >= 4 * sizeof(__be32)) {
+               rid_base = be32_to_cpup(msi_map + 0);
+               phandle = be32_to_cpup(msi_map + 1);
+               msi_base = be32_to_cpup(msi_map + 2);
+               rid_len = be32_to_cpup(msi_map + 3);
+
+               msi_controller_node = of_find_node_by_phandle(phandle);
+
+               matched = (masked_rid >= rid_base &&
+                          masked_rid < rid_base + rid_len);
+               if (msi_np)
+                       matched &= msi_np == msi_controller_node;
+
+               if (matched && !msi_np) {
+                       *np = msi_np = msi_controller_node;
+                       break;
+               }
+
+               of_node_put(msi_controller_node);
+               msi_map_len -= 4 * sizeof(__be32);
+               msi_map += 4;
+       }
+       if (!matched)
+               return rid_out;
+
+       rid_out = masked_rid + msi_base;
+       dev_dbg(dev,
+               "msi-map at: %s, using mask %08x, rid-base: %08x, msi-base: %08x, length: %08x, rid: %08x -> %08x\n",
+               dev_name(parent_dev), map_mask, rid_base, msi_base,
+               rid_len, rid_in, rid_out);
+
+       return rid_out;
+}
+
 /**
- * of_msi_configure - Set the msi_domain field of a device
- * @dev: device structure to associate with an MSI irq domain
- * @np: device node for that device
+ * of_msi_map_rid - Map a MSI requester ID for a device.
+ * @dev: device for which the mapping is to be done.
+ * @msi_np: device node of the expected msi controller.
+ * @rid_in: unmapped MSI requester ID for the device.
+ *
+ * Walk up the device hierarchy looking for devices with a "msi-map"
+ * property.  If found, apply the mapping to @rid_in.
+ *
+ * Returns the mapped MSI requester ID.
  */
-void of_msi_configure(struct device *dev, struct device_node *np)
+u32 of_msi_map_rid(struct device *dev, struct device_node *msi_np, u32 rid_in)
+{
+       return __of_msi_map_rid(dev, &msi_np, rid_in);
+}
+
+static struct irq_domain *__of_get_msi_domain(struct device_node *np,
+                                             enum irq_domain_bus_token token)
+{
+       struct irq_domain *d;
+
+       d = irq_find_matching_host(np, token);
+       if (!d)
+               d = irq_find_host(np);
+
+       return d;
+}
+
+/**
+ * of_msi_map_get_device_domain - Use msi-map to find the relevant MSI domain
+ * @dev: device for which the mapping is to be done.
+ * @rid: Requester ID for the device.
+ *
+ * Walk up the device hierarchy looking for devices with a "msi-map"
+ * property.
+ *
+ * Returns: the MSI domain for this device (or NULL on failure)
+ */
+struct irq_domain *of_msi_map_get_device_domain(struct device *dev, u32 rid)
+{
+       struct device_node *np = NULL;
+
+       __of_msi_map_rid(dev, &np, rid);
+       return __of_get_msi_domain(np, DOMAIN_BUS_PCI_MSI);
+}
+
+/**
+ * of_msi_get_domain - Use msi-parent to find the relevant MSI domain
+ * @dev: device for which the domain is requested
+ * @np: device node for @dev
+ * @token: bus type for this domain
+ *
+ * Parse the msi-parent property (both the simple and the complex
+ * versions), and returns the corresponding MSI domain.
+ *
+ * Returns: the MSI domain for this device (or NULL on failure).
+ */
+struct irq_domain *of_msi_get_domain(struct device *dev,
+                                    struct device_node *np,
+                                    enum irq_domain_bus_token token)
 {
        struct device_node *msi_np;
        struct irq_domain *d;
 
+       /* Check for a single msi-parent property */
        msi_np = of_parse_phandle(np, "msi-parent", 0);
-       if (!msi_np)
-               return;
+       if (msi_np && !of_property_read_bool(msi_np, "#msi-cells")) {
+               d = __of_get_msi_domain(msi_np, token);
+               if (!d)
+                       of_node_put(msi_np);
+               return d;
+       }
 
-       d = irq_find_matching_host(msi_np, DOMAIN_BUS_PLATFORM_MSI);
-       if (!d)
-               d = irq_find_host(msi_np);
-       dev_set_msi_domain(dev, d);
+       if (token == DOMAIN_BUS_PLATFORM_MSI) {
+               /* Check for the complex msi-parent version */
+               struct of_phandle_args args;
+               int index = 0;
+
+               while (!of_parse_phandle_with_args(np, "msi-parent",
+                                                  "#msi-cells",
+                                                  index, &args)) {
+                       d = __of_get_msi_domain(args.np, token);
+                       if (d)
+                               return d;
+
+                       of_node_put(args.np);
+                       index++;
+               }
+       }
+
+       return NULL;
+}
+
+/**
+ * of_msi_configure - Set the msi_domain field of a device
+ * @dev: device structure to associate with an MSI irq domain
+ * @np: device node for that device
+ */
+void of_msi_configure(struct device *dev, struct device_node *np)
+{
+       dev_set_msi_domain(dev,
+                          of_msi_get_domain(dev, np, DOMAIN_BUS_PLATFORM_MSI));
 }
index e491681daf22681c7bd541ad75d078edfa32ea1c..a6456b5782692a5d5559b9ef59226d2fbce99e3f 100644 (file)
@@ -256,7 +256,7 @@ static int xgene_allocate_domains(struct xgene_msi *msi)
        if (!msi->inner_domain)
                return -ENOMEM;
 
-       msi->msi_domain = pci_msi_create_irq_domain(msi->node,
+       msi->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(msi->node),
                                                    &xgene_msi_domain_info,
                                                    msi->inner_domain);
 
index 4a7da3c3e0353c3c746e9b10be9a093c8d085920..45a51486d080a54fa987eaa230106df5895b3b09 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/irqdomain.h>
+#include <linux/of_irq.h>
 
 #include "pci.h"
 
@@ -1250,8 +1251,8 @@ static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info)
 }
 
 /**
- * pci_msi_create_irq_domain - Creat a MSI interrupt domain
- * @node:      Optional device-tree node of the interrupt controller
+ * pci_msi_create_irq_domain - Create a MSI interrupt domain
+ * @fwnode:    Optional fwnode of the interrupt controller
  * @info:      MSI domain info
  * @parent:    Parent irq domain
  *
@@ -1260,7 +1261,7 @@ static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info)
  * Returns:
  * A domain pointer or NULL in case of failure.
  */
-struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
+struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
                                             struct msi_domain_info *info,
                                             struct irq_domain *parent)
 {
@@ -1271,7 +1272,7 @@ struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
        if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
                pci_msi_domain_update_chip_ops(info);
 
-       domain = msi_create_irq_domain(node, info, parent);
+       domain = msi_create_irq_domain(fwnode, info, parent);
        if (!domain)
                return NULL;
 
@@ -1307,14 +1308,14 @@ void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev)
 
 /**
  * pci_msi_create_default_irq_domain - Create a default MSI interrupt domain
- * @node:      Optional device-tree node of the interrupt controller
+ * @fwnode:    Optional fwnode of the interrupt controller
  * @info:      MSI domain info
  * @parent:    Parent irq domain
  *
  * Returns: A domain pointer or NULL in case of failure. If successful
  * the default PCI/MSI irqdomain pointer is updated.
  */
-struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node,
+struct irq_domain *pci_msi_create_default_irq_domain(struct fwnode_handle *fwnode,
                struct msi_domain_info *info, struct irq_domain *parent)
 {
        struct irq_domain *domain;
@@ -1324,11 +1325,59 @@ struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node,
                pr_err("PCI: default irq domain for PCI MSI has already been created.\n");
                domain = NULL;
        } else {
-               domain = pci_msi_create_irq_domain(node, info, parent);
+               domain = pci_msi_create_irq_domain(fwnode, info, parent);
                pci_msi_default_domain = domain;
        }
        mutex_unlock(&pci_msi_domain_lock);
 
        return domain;
 }
+
+static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data)
+{
+       u32 *pa = data;
+
+       *pa = alias;
+       return 0;
+}
+/**
+ * pci_msi_domain_get_msi_rid - Get the MSI requester id (RID)
+ * @domain:    The interrupt domain
+ * @pdev:      The PCI device.
+ *
+ * The RID for a device is formed from the alias, with a firmware
+ * supplied mapping applied
+ *
+ * Returns: The RID.
+ */
+u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
+{
+       struct device_node *of_node;
+       u32 rid = 0;
+
+       pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
+
+       of_node = irq_domain_get_of_node(domain);
+       if (of_node)
+               rid = of_msi_map_rid(&pdev->dev, of_node, rid);
+
+       return rid;
+}
+
+/**
+ * pci_msi_get_device_domain - Get the MSI domain for a given PCI device
+ * @pdev:      The PCI device
+ *
+ * Use the firmware data to find a device-specific MSI domain
+ * (i.e. not one that is ste as a default).
+ *
+ * Returns: The coresponding MSI domain or NULL if none has been found.
+ */
+struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev)
+{
+       u32 rid = 0;
+
+       pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
+       return of_msi_map_get_device_domain(&pdev->dev, rid);
+}
 #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
index 2e99a500cb83fe48ac065333fad036186935aa4f..e112da11630ee46803fc14d02f979c079c70ba79 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/of.h>
+#include <linux/of_irq.h>
 #include <linux/of_pci.h>
 #include "pci.h"
 
@@ -64,27 +65,25 @@ struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus)
 struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus)
 {
 #ifdef CONFIG_IRQ_DOMAIN
-       struct device_node *np;
        struct irq_domain *d;
 
        if (!bus->dev.of_node)
                return NULL;
 
        /* Start looking for a phandle to an MSI controller. */
-       np = of_parse_phandle(bus->dev.of_node, "msi-parent", 0);
+       d = of_msi_get_domain(&bus->dev, bus->dev.of_node, DOMAIN_BUS_PCI_MSI);
+       if (d)
+               return d;
 
        /*
         * If we don't have an msi-parent property, look for a domain
         * directly attached to the host bridge.
         */
-       if (!np)
-               np = bus->dev.of_node;
-
-       d = irq_find_matching_host(np, DOMAIN_BUS_PCI_MSI);
+       d = irq_find_matching_host(bus->dev.of_node, DOMAIN_BUS_PCI_MSI);
        if (d)
                return d;
 
-       return irq_find_host(np);
+       return irq_find_host(bus->dev.of_node);
 #else
        return NULL;
 #endif
index 312f23a8429cd9331b45afaf72a278e84bd41d83..92618686604cb9d314aa1e6bf833363cfbaaa1b5 100644 (file)
@@ -216,7 +216,7 @@ static ssize_t numa_node_store(struct device *dev,
        if (ret)
                return ret;
 
-       if (!node_online(node))
+       if (node >= MAX_NUMNODES || !node_online(node))
                return -EINVAL;
 
        add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
index 8361d27e5ecad82ff9767c3d55a019a66f884046..f14a970b61fa59bf00b6e97474da3ec024ba315e 100644 (file)
@@ -1622,15 +1622,48 @@ static void pci_init_capabilities(struct pci_dev *dev)
        pci_enable_acs(dev);
 }
 
+/*
+ * This is the equivalent of pci_host_bridge_msi_domain that acts on
+ * devices. Firmware interfaces that can select the MSI domain on a
+ * per-device basis should be called from here.
+ */
+static struct irq_domain *pci_dev_msi_domain(struct pci_dev *dev)
+{
+       struct irq_domain *d;
+
+       /*
+        * If a domain has been set through the pcibios_add_device
+        * callback, then this is the one (platform code knows best).
+        */
+       d = dev_get_msi_domain(&dev->dev);
+       if (d)
+               return d;
+
+       /*
+        * Let's see if we have a firmware interface able to provide
+        * the domain.
+        */
+       d = pci_msi_get_device_domain(dev);
+       if (d)
+               return d;
+
+       return NULL;
+}
+
 static void pci_set_msi_domain(struct pci_dev *dev)
 {
+       struct irq_domain *d;
+
        /*
-        * If no domain has been set through the pcibios_add_device
-        * callback, inherit the default from the bus device.
+        * If the platform or firmware interfaces cannot supply a
+        * device-specific MSI domain, then inherit the default domain
+        * from the host bridge itself.
         */
-       if (!dev_get_msi_domain(&dev->dev))
-               dev_set_msi_domain(&dev->dev,
-                                  dev_get_msi_domain(&dev->bus->dev));
+       d = pci_dev_msi_domain(dev);
+       if (!d)
+               d = dev_get_msi_domain(&dev->bus->dev);
+
+       dev_set_msi_domain(&dev->dev, d);
 }
 
 void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
index 6e0d9fa8e1d13f8d1ad0eec73b1040f594637df7..c7a05996d5c1a841a65e515e746d1008d3d47489 100644 (file)
@@ -17,8 +17,7 @@
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/spinlock.h>
-
-#include <asm/cmpxchg.h>
+#include <linux/atomic.h>
 
 #define USBHS_LPSTS                    0x02
 #define USBHS_UGCTRL                   0x80
index 84dd2ed47a928e57effa8297597592733c703100..b422e4ed73f4266f7a8d13db4ec1096395163fc3 100644 (file)
@@ -67,6 +67,19 @@ config PINCTRL_AT91
        help
          Say Y here to enable the at91 pinctrl driver
 
+config PINCTRL_AT91PIO4
+       bool "AT91 PIO4 pinctrl driver"
+       depends on OF
+       depends on ARCH_AT91
+       select PINMUX
+       select GENERIC_PINCONF
+       select GPIOLIB
+       select GPIOLIB_IRQCHIP
+       select OF_GPIO
+       help
+         Say Y here to enable the at91 pinctrl/gpio driver for Atmel PIO4
+         controller available on sama5d2 SoC.
+
 config PINCTRL_AMD
        bool "AMD GPIO pin control"
        depends on GPIOLIB
index cad077c43fb7313af8b81bb9ca9a8a83f48e5224..738cb4929a49428890f543df5eab92976d40dbe7 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_PINCTRL_AS3722)  += pinctrl-as3722.o
 obj-$(CONFIG_PINCTRL_BF54x)    += pinctrl-adi2-bf54x.o
 obj-$(CONFIG_PINCTRL_BF60x)    += pinctrl-adi2-bf60x.o
 obj-$(CONFIG_PINCTRL_AT91)     += pinctrl-at91.o
+obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o
 obj-$(CONFIG_PINCTRL_AMD)      += pinctrl-amd.o
 obj-$(CONFIG_PINCTRL_DIGICOLOR)        += pinctrl-digicolor.o
 obj-$(CONFIG_PINCTRL_FALCON)   += pinctrl-falcon.o
@@ -50,6 +51,6 @@ obj-$(CONFIG_PINCTRL_SAMSUNG) += samsung/
 obj-$(CONFIG_PINCTRL_SH_PFC)   += sh-pfc/
 obj-$(CONFIG_PLAT_SPEAR)       += spear/
 obj-$(CONFIG_ARCH_SUNXI)       += sunxi/
-obj-$(CONFIG_ARCH_UNIPHIER)    += uniphier/
+obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/
 obj-$(CONFIG_ARCH_VT8500)      += vt8500/
 obj-$(CONFIG_ARCH_MEDIATEK)    += mediatek/
index 8efa235ca1c96fa70af1ed1cecd86c5a53736221..a1ea565fcd46490e58d48e4363318c2ce6970ebc 100644 (file)
@@ -330,16 +330,6 @@ static inline void bcm2835_pinctrl_fsel_set(
        bcm2835_gpio_wr(pc, FSEL_REG(pin), val);
 }
 
-static int bcm2835_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void bcm2835_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static int bcm2835_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
 {
        return pinctrl_gpio_direction_input(chip->base + offset);
@@ -375,8 +365,8 @@ static int bcm2835_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
 static struct gpio_chip bcm2835_gpio_chip = {
        .label = MODULE_NAME,
        .owner = THIS_MODULE,
-       .request = bcm2835_gpio_request,
-       .free = bcm2835_gpio_free,
+       .request = gpiochip_generic_request,
+       .free = gpiochip_generic_free,
        .direction_input = bcm2835_gpio_direction_input,
        .direction_output = bcm2835_gpio_direction_output,
        .get = bcm2835_gpio_get,
index 1ca783098e47463bd3c9a5f267f5fb00eb68ef88..12a48f498b75847926ab7bc96ecd78fdd248971a 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/of_device.h>
 #include <linux/of_irq.h>
 #include <linux/pinctrl/pinctrl.h>
-#include <linux/pinctrl/pinmux.h>
 #include <linux/pinctrl/pinconf.h>
 #include <linux/pinctrl/pinconf-generic.h>
 
@@ -596,127 +595,6 @@ static const struct pinconf_ops cygnus_pconf_ops = {
        .pin_config_set = cygnus_pin_config_set,
 };
 
-/*
- * Map a GPIO in the local gpio_chip pin space to a pin in the Cygnus IOMUX
- * pinctrl pin space
- */
-struct cygnus_gpio_pin_range {
-       unsigned offset;
-       unsigned pin_base;
-       unsigned num_pins;
-};
-
-#define CYGNUS_PINRANGE(o, p, n) { .offset = o, .pin_base = p, .num_pins = n }
-
-/*
- * Pin mapping table for mapping local GPIO pins to Cygnus IOMUX pinctrl pins
- */
-static const struct cygnus_gpio_pin_range cygnus_gpio_pintable[] = {
-       CYGNUS_PINRANGE(0, 42, 1),
-       CYGNUS_PINRANGE(1, 44, 3),
-       CYGNUS_PINRANGE(4, 48, 1),
-       CYGNUS_PINRANGE(5, 50, 3),
-       CYGNUS_PINRANGE(8, 126, 1),
-       CYGNUS_PINRANGE(9, 155, 1),
-       CYGNUS_PINRANGE(10, 152, 1),
-       CYGNUS_PINRANGE(11, 154, 1),
-       CYGNUS_PINRANGE(12, 153, 1),
-       CYGNUS_PINRANGE(13, 127, 3),
-       CYGNUS_PINRANGE(16, 140, 1),
-       CYGNUS_PINRANGE(17, 145, 7),
-       CYGNUS_PINRANGE(24, 130, 10),
-       CYGNUS_PINRANGE(34, 141, 4),
-       CYGNUS_PINRANGE(38, 54, 1),
-       CYGNUS_PINRANGE(39, 56, 3),
-       CYGNUS_PINRANGE(42, 60, 3),
-       CYGNUS_PINRANGE(45, 64, 3),
-       CYGNUS_PINRANGE(48, 68, 2),
-       CYGNUS_PINRANGE(50, 84, 6),
-       CYGNUS_PINRANGE(56, 94, 6),
-       CYGNUS_PINRANGE(62, 72, 1),
-       CYGNUS_PINRANGE(63, 70, 1),
-       CYGNUS_PINRANGE(64, 80, 1),
-       CYGNUS_PINRANGE(65, 74, 3),
-       CYGNUS_PINRANGE(68, 78, 1),
-       CYGNUS_PINRANGE(69, 82, 1),
-       CYGNUS_PINRANGE(70, 156, 17),
-       CYGNUS_PINRANGE(87, 104, 12),
-       CYGNUS_PINRANGE(99, 102, 2),
-       CYGNUS_PINRANGE(101, 90, 4),
-       CYGNUS_PINRANGE(105, 116, 6),
-       CYGNUS_PINRANGE(111, 100, 2),
-       CYGNUS_PINRANGE(113, 122, 4),
-       CYGNUS_PINRANGE(123, 11, 1),
-       CYGNUS_PINRANGE(124, 38, 4),
-       CYGNUS_PINRANGE(128, 43, 1),
-       CYGNUS_PINRANGE(129, 47, 1),
-       CYGNUS_PINRANGE(130, 49, 1),
-       CYGNUS_PINRANGE(131, 53, 1),
-       CYGNUS_PINRANGE(132, 55, 1),
-       CYGNUS_PINRANGE(133, 59, 1),
-       CYGNUS_PINRANGE(134, 63, 1),
-       CYGNUS_PINRANGE(135, 67, 1),
-       CYGNUS_PINRANGE(136, 71, 1),
-       CYGNUS_PINRANGE(137, 73, 1),
-       CYGNUS_PINRANGE(138, 77, 1),
-       CYGNUS_PINRANGE(139, 79, 1),
-       CYGNUS_PINRANGE(140, 81, 1),
-       CYGNUS_PINRANGE(141, 83, 1),
-       CYGNUS_PINRANGE(142, 10, 1)
-};
-
-/*
- * The Cygnus IOMUX controller mainly supports group based mux configuration,
- * but certain pins can be muxed to GPIO individually. Only the ASIU GPIO
- * controller can support this, so it's an optional configuration
- *
- * Return -ENODEV means no support and that's fine
- */
-static int cygnus_gpio_pinmux_add_range(struct cygnus_gpio *chip)
-{
-       struct device_node *node = chip->dev->of_node;
-       struct device_node *pinmux_node;
-       struct platform_device *pinmux_pdev;
-       struct gpio_chip *gc = &chip->gc;
-       int i, ret = 0;
-
-       /* parse DT to find the phandle to the pinmux controller */
-       pinmux_node = of_parse_phandle(node, "pinmux", 0);
-       if (!pinmux_node)
-               return -ENODEV;
-
-       pinmux_pdev = of_find_device_by_node(pinmux_node);
-       /* no longer need the pinmux node */
-       of_node_put(pinmux_node);
-       if (!pinmux_pdev) {
-               dev_err(chip->dev, "failed to get pinmux device\n");
-               return -EINVAL;
-       }
-
-       /* now need to create the mapping between local GPIO and PINMUX pins */
-       for (i = 0; i < ARRAY_SIZE(cygnus_gpio_pintable); i++) {
-               ret = gpiochip_add_pin_range(gc, dev_name(&pinmux_pdev->dev),
-                                            cygnus_gpio_pintable[i].offset,
-                                            cygnus_gpio_pintable[i].pin_base,
-                                            cygnus_gpio_pintable[i].num_pins);
-               if (ret) {
-                       dev_err(chip->dev, "unable to add GPIO pin range\n");
-                       goto err_put_device;
-               }
-       }
-
-       chip->pinmux_is_supported = true;
-
-       /* no need for pinmux_pdev device reference anymore */
-       put_device(&pinmux_pdev->dev);
-       return 0;
-
-err_put_device:
-       put_device(&pinmux_pdev->dev);
-       gpiochip_remove_pin_ranges(gc);
-       return ret;
-}
-
 /*
  * Cygnus GPIO controller supports some PINCONF related configurations such as
  * pull up, pull down, and drive strength, when the pin is configured to GPIO
@@ -851,18 +729,15 @@ static int cygnus_gpio_probe(struct platform_device *pdev)
        gc->set = cygnus_gpio_set;
        gc->get = cygnus_gpio_get;
 
+       chip->pinmux_is_supported = of_property_read_bool(dev->of_node,
+                                                       "gpio-ranges");
+
        ret = gpiochip_add(gc);
        if (ret < 0) {
                dev_err(dev, "unable to add GPIO chip\n");
                return ret;
        }
 
-       ret = cygnus_gpio_pinmux_add_range(chip);
-       if (ret && ret != -ENODEV) {
-               dev_err(dev, "unable to add GPIO pin range\n");
-               goto err_rm_gpiochip;
-       }
-
        ret = cygnus_gpio_register_pinconf(chip);
        if (ret) {
                dev_err(dev, "unable to register pinconf\n");
index b18322bc7bf9d0bdd220d7b38968b0b7e8938c8b..8fe6ad7795dc32b7f6a4549339760b6700b3eae9 100644 (file)
@@ -1,4 +1,4 @@
-if ARCH_BERLIN
+if (ARCH_BERLIN || COMPILE_TEST)
 
 config PINCTRL_BERLIN
        bool
@@ -6,15 +6,23 @@ config PINCTRL_BERLIN
        select REGMAP_MMIO
 
 config PINCTRL_BERLIN_BG2
-       bool
+       def_bool MACH_BERLIN_BG2
+       depends on OF
        select PINCTRL_BERLIN
 
 config PINCTRL_BERLIN_BG2CD
-       bool
+       def_bool MACH_BERLIN_BG2CD
+       depends on OF
        select PINCTRL_BERLIN
 
 config PINCTRL_BERLIN_BG2Q
-       bool
+       def_bool MACH_BERLIN_BG2Q
+       depends on OF
+       select PINCTRL_BERLIN
+
+config PINCTRL_BERLIN_BG4CT
+       bool "Marvell berlin4ct pin controller driver"
+       depends on OF
        select PINCTRL_BERLIN
 
 endif
index deb0c6baf3161fdc8d6183c2fbbe0d2fbe27cf69..06f94029ad66715995288cf842b58e14cac573a9 100644 (file)
@@ -2,3 +2,4 @@ obj-$(CONFIG_PINCTRL_BERLIN)            += berlin.o
 obj-$(CONFIG_PINCTRL_BERLIN_BG2)       += berlin-bg2.o
 obj-$(CONFIG_PINCTRL_BERLIN_BG2CD)     += berlin-bg2cd.o
 obj-$(CONFIG_PINCTRL_BERLIN_BG2Q)      += berlin-bg2q.o
+obj-$(CONFIG_PINCTRL_BERLIN_BG4CT)     += berlin-bg4ct.o
index 274c5535b53155a60757151f97d7cf7e469ae453..fabe728ae268305400bde48abba549e33f49ec94 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2014 Marvell Technology Group Ltd.
  *
- * Antoine TÃ\83©nart <antoine.tenart@free-electrons.com>
+ * Antoine Ténart <antoine.tenart@free-electrons.com>
  *
  * This file is licensed under the terms of the GNU General Public
  * License version 2. This program is licensed "as is" without any
@@ -246,6 +246,6 @@ static struct platform_driver berlin2_pinctrl_driver = {
 };
 module_platform_driver(berlin2_pinctrl_driver);
 
-MODULE_AUTHOR("Antoine TÃ\83©nart <antoine.tenart@free-electrons.com>");
+MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>");
 MODULE_DESCRIPTION("Marvell Berlin BG2 pinctrl driver");
 MODULE_LICENSE("GPL");
index 0cb793a3552a437162c6176e30231c1ffb51a318..ad8c7586137312fd77780b83a1b4b4795f837e0f 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2014 Marvell Technology Group Ltd.
  *
- * Antoine TÃ\83©nart <antoine.tenart@free-electrons.com>
+ * Antoine Ténart <antoine.tenart@free-electrons.com>
  *
  * This file is licensed under the terms of the GNU General Public
  * License version 2. This program is licensed "as is" without any
 
 static const struct berlin_desc_group berlin2cd_soc_pinctrl_groups[] = {
        /* G */
-       BERLIN_PINCTRL_GROUP("G0", 0x00, 0x1, 0x00,
+       BERLIN_PINCTRL_GROUP("G0", 0x00, 0x3, 0x00,
                BERLIN_PINCTRL_FUNCTION(0x0, "jtag"),
                BERLIN_PINCTRL_FUNCTION(0x1, "gpio"),
                BERLIN_PINCTRL_FUNCTION(0x2, "led"),
                BERLIN_PINCTRL_FUNCTION(0x3, "pwm")),
-       BERLIN_PINCTRL_GROUP("G1", 0x00, 0x2, 0x01,
+       BERLIN_PINCTRL_GROUP("G1", 0x00, 0x3, 0x03,
                BERLIN_PINCTRL_FUNCTION(0x0, "gpio"),
                BERLIN_PINCTRL_FUNCTION(0x1, "sd0"),
                BERLIN_PINCTRL_FUNCTION(0x6, "usb0_dbg"),
                BERLIN_PINCTRL_FUNCTION(0x7, "usb1_dbg")),
-       BERLIN_PINCTRL_GROUP("G2", 0x00, 0x2, 0x02,
+       BERLIN_PINCTRL_GROUP("G2", 0x00, 0x3, 0x06,
                BERLIN_PINCTRL_FUNCTION(0x0, "gpio"),
                BERLIN_PINCTRL_FUNCTION(0x1, "sd0"),
                BERLIN_PINCTRL_FUNCTION(0x2, "fe"),
                BERLIN_PINCTRL_FUNCTION(0x3, "pll"),
                BERLIN_PINCTRL_FUNCTION(0x6, "usb0_dbg"),
                BERLIN_PINCTRL_FUNCTION(0x7, "usb1_dbg")),
-       BERLIN_PINCTRL_GROUP("G3", 0x00, 0x2, 0x04,
+       BERLIN_PINCTRL_GROUP("G3", 0x00, 0x3, 0x09,
                BERLIN_PINCTRL_FUNCTION(0x0, "gpio"),
                BERLIN_PINCTRL_FUNCTION(0x1, "sd0"),
                BERLIN_PINCTRL_FUNCTION(0x2, "twsi2"),
@@ -44,7 +44,7 @@ static const struct berlin_desc_group berlin2cd_soc_pinctrl_groups[] = {
                BERLIN_PINCTRL_FUNCTION(0x4, "fe"),
                BERLIN_PINCTRL_FUNCTION(0x6, "usb0_dbg"),
                BERLIN_PINCTRL_FUNCTION(0x7, "usb1_dbg")),
-       BERLIN_PINCTRL_GROUP("G4", 0x00, 0x2, 0x06,
+       BERLIN_PINCTRL_GROUP("G4", 0x00, 0x3, 0x0c,
                BERLIN_PINCTRL_FUNCTION(0x0, "gpio"),
                BERLIN_PINCTRL_FUNCTION(0x1, "sd0"),
                BERLIN_PINCTRL_FUNCTION(0x2, "twsi3"),
@@ -52,7 +52,7 @@ static const struct berlin_desc_group berlin2cd_soc_pinctrl_groups[] = {
                BERLIN_PINCTRL_FUNCTION(0x4, "pwm"),
                BERLIN_PINCTRL_FUNCTION(0x6, "usb0_dbg"),
                BERLIN_PINCTRL_FUNCTION(0x7, "usb1_dbg")),
-       BERLIN_PINCTRL_GROUP("G5", 0x00, 0x3, 0x08,
+       BERLIN_PINCTRL_GROUP("G5", 0x00, 0x3, 0x0f,
                BERLIN_PINCTRL_FUNCTION(0x0, "gpio"),
                BERLIN_PINCTRL_FUNCTION(0x1, "sd0"),
                BERLIN_PINCTRL_FUNCTION(0x2, "twsi3"),
@@ -60,64 +60,66 @@ static const struct berlin_desc_group berlin2cd_soc_pinctrl_groups[] = {
                BERLIN_PINCTRL_FUNCTION(0x4, "pwm"),
                BERLIN_PINCTRL_FUNCTION(0x6, "usb0_dbg"),
                BERLIN_PINCTRL_FUNCTION(0x7, "usb1_dbg")),
-       BERLIN_PINCTRL_GROUP("G6", 0x00, 0x2, 0x0b,
+       BERLIN_PINCTRL_GROUP("G6", 0x00, 0x3, 0x12,
                BERLIN_PINCTRL_FUNCTION(0x0, "uart0"),  /* RX/TX */
                BERLIN_PINCTRL_FUNCTION(0x1, "gpio")),
-       BERLIN_PINCTRL_GROUP("G7", 0x00, 0x3, 0x0d,
+       BERLIN_PINCTRL_GROUP("G7", 0x00, 0x3, 0x15,
                BERLIN_PINCTRL_FUNCTION(0x0, "eddc"),
                BERLIN_PINCTRL_FUNCTION(0x1, "twsi1"),
                BERLIN_PINCTRL_FUNCTION(0x2, "gpio")),
-       BERLIN_PINCTRL_GROUP("G8", 0x00, 0x3, 0x10,
+       BERLIN_PINCTRL_GROUP("G8", 0x00, 0x3, 0x18,
                BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SS0n */
                BERLIN_PINCTRL_FUNCTION(0x1, "gpio")),
-       BERLIN_PINCTRL_GROUP("G9", 0x00, 0x3, 0x13,
+       BERLIN_PINCTRL_GROUP("G9", 0x00, 0x3, 0x1b,
                BERLIN_PINCTRL_FUNCTION(0x0, "gpio"),
                BERLIN_PINCTRL_FUNCTION(0x1, "spi1"), /* SS1n/SS2n */
-               BERLIN_PINCTRL_FUNCTION(0x2, "twsi0")),
-       BERLIN_PINCTRL_GROUP("G10", 0x00, 0x2, 0x16,
+               BERLIN_PINCTRL_FUNCTION(0x3, "twsi0")),
+       BERLIN_PINCTRL_GROUP("G10", 0x00, 0x2, 0x1e,
                BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* CLK */
                BERLIN_PINCTRL_FUNCTION(0x1, "gpio")),
-       BERLIN_PINCTRL_GROUP("G11", 0x00, 0x2, 0x18,
+       BERLIN_PINCTRL_GROUP("G11", 0x04, 0x2, 0x00,
                BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SDI/SDO */
                BERLIN_PINCTRL_FUNCTION(0x1, "gpio")),
-       BERLIN_PINCTRL_GROUP("G12", 0x00, 0x3, 0x1a,
+       BERLIN_PINCTRL_GROUP("G12", 0x04, 0x3, 0x02,
                BERLIN_PINCTRL_FUNCTION(0x0, "usb1"),
                BERLIN_PINCTRL_FUNCTION(0x1, "gpio")),
-       BERLIN_PINCTRL_GROUP("G13", 0x04, 0x3, 0x00,
+       BERLIN_PINCTRL_GROUP("G13", 0x04, 0x3, 0x05,
                BERLIN_PINCTRL_FUNCTION(0x0, "nand"),
                BERLIN_PINCTRL_FUNCTION(0x1, "usb0_dbg"),
                BERLIN_PINCTRL_FUNCTION(0x2, "usb1_dbg")),
-       BERLIN_PINCTRL_GROUP("G14", 0x04, 0x1, 0x03,
+       BERLIN_PINCTRL_GROUP("G14", 0x04, 0x1, 0x08,
                BERLIN_PINCTRL_FUNCTION(0x0, "nand"),
                BERLIN_PINCTRL_FUNCTION(0x1, "gpio")),
-       BERLIN_PINCTRL_GROUP("G15", 0x04, 0x2, 0x04,
+       BERLIN_PINCTRL_GROUP("G15", 0x04, 0x3, 0x09,
                BERLIN_PINCTRL_FUNCTION(0x0, "jtag"),
                BERLIN_PINCTRL_FUNCTION(0x1, "gpio")),
-       BERLIN_PINCTRL_GROUP("G16", 0x04, 0x3, 0x06,
+       BERLIN_PINCTRL_GROUP("G16", 0x04, 0x3, 0x0c,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G17", 0x04, 0x3, 0x09,
+       BERLIN_PINCTRL_GROUP("G17", 0x04, 0x3, 0x0f,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G18", 0x04, 0x1, 0x0c,
+       BERLIN_PINCTRL_GROUP("G18", 0x04, 0x2, 0x12,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G19", 0x04, 0x1, 0x0d,
+       BERLIN_PINCTRL_GROUP("G19", 0x04, 0x2, 0x14,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G20", 0x04, 0x1, 0x0e,
+       BERLIN_PINCTRL_GROUP("G20", 0x04, 0x2, 0x16,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G21", 0x04, 0x3, 0x0f,
+       BERLIN_PINCTRL_GROUP("G21", 0x04, 0x3, 0x18,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G22", 0x04, 0x3, 0x12,
+       BERLIN_PINCTRL_GROUP("G22", 0x04, 0x3, 0x1b,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G23", 0x04, 0x3, 0x15,
+       BERLIN_PINCTRL_GROUP("G23", 0x08, 0x3, 0x00,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G24", 0x04, 0x2, 0x18,
+       BERLIN_PINCTRL_GROUP("G24", 0x08, 0x2, 0x03,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G25", 0x04, 0x2, 0x1a,
+       BERLIN_PINCTRL_GROUP("G25", 0x08, 0x2, 0x05,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G26", 0x04, 0x1, 0x1c,
+       BERLIN_PINCTRL_GROUP("G26", 0x08, 0x1, 0x07,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G27", 0x04, 0x1, 0x1d,
+       BERLIN_PINCTRL_GROUP("G27", 0x08, 0x2, 0x08,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
-       BERLIN_PINCTRL_GROUP("G28", 0x04, 0x2, 0x1e,
+       BERLIN_PINCTRL_GROUP("G28", 0x08, 0x3, 0x0a,
+               BERLIN_PINCTRL_FUNCTION_UNKNOWN),
+       BERLIN_PINCTRL_GROUP("G29", 0x08, 0x3, 0x0d,
                BERLIN_PINCTRL_FUNCTION_UNKNOWN),
 };
 
@@ -189,6 +191,6 @@ static struct platform_driver berlin2cd_pinctrl_driver = {
 };
 module_platform_driver(berlin2cd_pinctrl_driver);
 
-MODULE_AUTHOR("Antoine TÃ\83©nart <antoine.tenart@free-electrons.com>");
+MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>");
 MODULE_DESCRIPTION("Marvell Berlin BG2CD pinctrl driver");
 MODULE_LICENSE("GPL");
index a466054a8206e31f15edab4c85d933d8652812b8..cd171aea8ca8da2f6ae7aac096e90ee61ba644b9 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2014 Marvell Technology Group Ltd.
  *
- * Antoine TÃ\83©nart <antoine.tenart@free-electrons.com>
+ * Antoine Ténart <antoine.tenart@free-electrons.com>
  *
  * This file is licensed under the terms of the GNU General Public
  * License version 2. This program is licensed "as is" without any
@@ -408,6 +408,6 @@ static struct platform_driver berlin2q_pinctrl_driver = {
 };
 module_platform_driver(berlin2q_pinctrl_driver);
 
-MODULE_AUTHOR("Antoine TÃ\83©nart <antoine.tenart@free-electrons.com>");
+MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>");
 MODULE_DESCRIPTION("Marvell Berlin BG2Q pinctrl driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/pinctrl/berlin/berlin-bg4ct.c b/drivers/pinctrl/berlin/berlin-bg4ct.c
new file mode 100644 (file)
index 0000000..0917204
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ * Marvell berlin4ct pinctrl driver
+ *
+ * Copyright (C) 2015 Marvell Technology Group Ltd.
+ *
+ * Author: Jisheng Zhang <jszhang@marvell.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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "berlin.h"
+
+static const struct berlin_desc_group berlin4ct_soc_pinctrl_groups[] = {
+       BERLIN_PINCTRL_GROUP("EMMC_RSTn", 0x0, 0x3, 0x00,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "emmc"), /* RSTn */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio")), /* GPIO47 */
+       BERLIN_PINCTRL_GROUP("NAND_IO0", 0x0, 0x3, 0x03,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* IO0 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* RXD0 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sd1"), /* CLK */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO0 */
+       BERLIN_PINCTRL_GROUP("NAND_IO1", 0x0, 0x3, 0x06,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* IO1 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* RXD1 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sd1"), /* CDn */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO1 */
+       BERLIN_PINCTRL_GROUP("NAND_IO2", 0x0, 0x3, 0x09,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* IO2 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* RXD2 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sd1"), /* DAT0 */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO2 */
+       BERLIN_PINCTRL_GROUP("NAND_IO3", 0x0, 0x3, 0x0c,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* IO3 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* RXD3 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sd1"), /* DAT1 */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO3 */
+       BERLIN_PINCTRL_GROUP("NAND_IO4", 0x0, 0x3, 0x0f,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* IO4 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* RXC */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sd1"), /* DAT2 */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO4 */
+       BERLIN_PINCTRL_GROUP("NAND_IO5", 0x0, 0x3, 0x12,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* IO5 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* RXCTL */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sd1"), /* DAT3 */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO5 */
+       BERLIN_PINCTRL_GROUP("NAND_IO6", 0x0, 0x3, 0x15,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* IO6 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* MDC */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sd1"), /* CMD */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO6 */
+       BERLIN_PINCTRL_GROUP("NAND_IO7", 0x0, 0x3, 0x18,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* IO7 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* MDIO */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sd1"), /* WP */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO7 */
+       BERLIN_PINCTRL_GROUP("NAND_ALE", 0x0, 0x3, 0x1b,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* ALE */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* TXD0 */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO8 */
+       BERLIN_PINCTRL_GROUP("NAND_CLE", 0x4, 0x3, 0x00,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* CLE */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* TXD1 */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO9 */
+       BERLIN_PINCTRL_GROUP("NAND_WEn", 0x4, 0x3, 0x03,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* WEn */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* TXD2 */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO10 */
+       BERLIN_PINCTRL_GROUP("NAND_REn", 0x4, 0x3, 0x06,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* REn */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* TXD3 */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO11 */
+       BERLIN_PINCTRL_GROUP("NAND_WPn", 0x4, 0x3, 0x09,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* WPn */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO12 */
+       BERLIN_PINCTRL_GROUP("NAND_CEn", 0x4, 0x3, 0x0c,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* CEn */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* TXC */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO13 */
+       BERLIN_PINCTRL_GROUP("NAND_RDY", 0x4, 0x3, 0x0f,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* RDY */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "rgmii"), /* TXCTL */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO14 */
+       BERLIN_PINCTRL_GROUP("SD0_CLK", 0x4, 0x3, 0x12,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO29 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* CLK*/
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts4"), /* CLK */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG8 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG8 */
+       BERLIN_PINCTRL_GROUP("SD0_DAT0", 0x4, 0x3, 0x15,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO30 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* DAT0 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts4"), /* SOP */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG9 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG9 */
+       BERLIN_PINCTRL_GROUP("SD0_DAT1", 0x4, 0x3, 0x18,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO31 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* DAT1 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts4"), /* SD */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG10 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG10 */
+       BERLIN_PINCTRL_GROUP("SD0_DAT2", 0x4, 0x3, 0x1b,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO32 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* DAT2 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts4"), /* VALD */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG11 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG11 */
+       BERLIN_PINCTRL_GROUP("SD0_DAT3", 0x8, 0x3, 0x00,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO33 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* DAT3 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts5"), /* CLK */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG12 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG12 */
+       BERLIN_PINCTRL_GROUP("SD0_CDn", 0x8, 0x3, 0x03,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO34 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* CDn */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts5"), /* SOP */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG13 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG13 */
+       BERLIN_PINCTRL_GROUP("SD0_CMD", 0x8, 0x3, 0x06,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO35 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* CMD */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts5"), /* SD */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG14 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG14 */
+       BERLIN_PINCTRL_GROUP("SD0_WP", 0x8, 0x3, 0x09,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO36 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* WP */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts5"), /* VALD */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG15 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG15 */
+       BERLIN_PINCTRL_GROUP("STS0_CLK", 0x8, 0x3, 0x0c,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO21 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sts0"), /* CLK */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "cpupll"), /* CLKO */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG0 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG0 */
+       BERLIN_PINCTRL_GROUP("STS0_SOP", 0x8, 0x3, 0x0f,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO22 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sts0"), /* SOP */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "syspll"), /* CLKO */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG1 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG1 */
+       BERLIN_PINCTRL_GROUP("STS0_SD", 0x8, 0x3, 0x12,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO23 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sts0"), /* SD */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "mempll"), /* CLKO */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG2 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG2 */
+       BERLIN_PINCTRL_GROUP("STS0_VALD", 0x8, 0x3, 0x15,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO24 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sts0"), /* VALD */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG3 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG3 */
+       BERLIN_PINCTRL_GROUP("STS1_CLK", 0x8, 0x3, 0x18,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO25 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sts1"), /* CLK */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "pwm0"),
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG4 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG4 */
+       BERLIN_PINCTRL_GROUP("STS1_SOP", 0x8, 0x3, 0x1b,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO26 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sts1"), /* SOP */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "pwm1"),
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG5 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG5 */
+       BERLIN_PINCTRL_GROUP("STS1_SD", 0xc, 0x3, 0x00,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO27 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sts1"), /* SD */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "pwm2"),
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG6 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG6 */
+       BERLIN_PINCTRL_GROUP("STS1_VALD", 0xc, 0x3, 0x03,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO28 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sts1"), /* VALD */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "pwm3"),
+                       BERLIN_PINCTRL_FUNCTION(0x5, "v4g"), /* DBG7 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "phy")), /* DBG7 */
+       BERLIN_PINCTRL_GROUP("SCRD0_RST", 0xc, 0x3, 0x06,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO15 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "scrd0"), /* RST */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sd1a")), /* CLK */
+       BERLIN_PINCTRL_GROUP("SCRD0_DCLK", 0xc, 0x3, 0x09,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO16 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "scrd0"), /* DCLK */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sd1a")), /* CMD */
+       BERLIN_PINCTRL_GROUP("SCRD0_GPIO0", 0xc, 0x3, 0x0c,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO17 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "scrd0"), /* SCRD0 GPIO0 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sif"), /* DIO */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sd1a")), /* DAT0 */
+       BERLIN_PINCTRL_GROUP("SCRD0_GPIO1", 0xc, 0x3, 0x0f,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO18 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "scrd0"), /* SCRD0 GPIO1 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sif"), /* CLK */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sd1a")), /* DAT1 */
+       BERLIN_PINCTRL_GROUP("SCRD0_DIO", 0xc, 0x3, 0x12,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO19 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "scrd0"), /* DIO */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sif"), /* DEN */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sd1a")), /* DAT2 */
+       BERLIN_PINCTRL_GROUP("SCRD0_CRD_PRES", 0xc, 0x3, 0x15,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO20 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "scrd0"), /* crd pres */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "sd1a")), /* DAT3 */
+       BERLIN_PINCTRL_GROUP("SPI1_SS0n", 0xc, 0x3, 0x18,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SS0n */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO37 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts2")), /* CLK */
+       BERLIN_PINCTRL_GROUP("SPI1_SS1n", 0xc, 0x3, 0x1b,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SS1n */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO38 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts2"), /* SOP */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "pwm1")),
+       BERLIN_PINCTRL_GROUP("SPI1_SS2n", 0x10, 0x3, 0x00,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SS2n */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO39 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts2"), /* SD */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "pwm0")),
+       BERLIN_PINCTRL_GROUP("SPI1_SS3n", 0x10, 0x3, 0x03,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SS3n */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO40 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts2")), /* VALD */
+       BERLIN_PINCTRL_GROUP("SPI1_SCLK", 0x10, 0x3, 0x06,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SCLK */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO41 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts3")), /* CLK */
+       BERLIN_PINCTRL_GROUP("SPI1_SDO", 0x10, 0x3, 0x09,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SDO */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO42 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts3")), /* SOP */
+       BERLIN_PINCTRL_GROUP("SPI1_SDI", 0x10, 0x3, 0x0c,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SDI */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO43 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts3")), /* SD */
+       BERLIN_PINCTRL_GROUP("USB0_DRV_VBUS", 0x10, 0x3, 0x0f,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO44 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "usb0"), /* VBUS */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "sts3")), /* VALD */
+       BERLIN_PINCTRL_GROUP("TW0_SCL", 0x10, 0x3, 0x12,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO45 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "tw0")), /* SCL */
+       BERLIN_PINCTRL_GROUP("TW0_SDA", 0x10, 0x3, 0x15,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO46 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "tw0")), /* SDA */
+};
+
+static const struct berlin_desc_group berlin4ct_avio_pinctrl_groups[] = {
+       BERLIN_PINCTRL_GROUP("TX_EDDC_SCL", 0x0, 0x3, 0x00,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO0 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "tx_eddc"), /* SCL */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "tw1")), /* SCL */
+       BERLIN_PINCTRL_GROUP("TX_EDDC_SDA", 0x0, 0x3, 0x03,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO1 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "tx_eddc"), /* SDA */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "tw1")), /* SDA */
+       BERLIN_PINCTRL_GROUP("I2S1_LRCKO", 0x0, 0x3, 0x06,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO2 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "i2s1"), /* LRCKO */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sts6"), /* CLK */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "adac"), /* DBG0 */
+                       BERLIN_PINCTRL_FUNCTION(0x6, "sd1b"), /* CLK */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "avio")), /* DBG0 */
+       BERLIN_PINCTRL_GROUP("I2S1_BCLKO", 0x0, 0x3, 0x09,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO3 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "i2s1"), /* BCLKO */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sts6"), /* SOP */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "adac"), /* DBG1 */
+                       BERLIN_PINCTRL_FUNCTION(0x6, "sd1b"), /* CMD */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "avio")), /* DBG1 */
+       BERLIN_PINCTRL_GROUP("I2S1_DO", 0x0, 0x3, 0x0c,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO4 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "i2s1"), /* DO */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sts6"), /* SD */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "adac"), /* DBG2 */
+                       BERLIN_PINCTRL_FUNCTION(0x6, "sd1b"), /* DAT0 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "avio")), /* DBG2 */
+       BERLIN_PINCTRL_GROUP("I2S1_MCLK", 0x0, 0x3, 0x0f,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO5 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "i2s1"), /* MCLK */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sts6"), /* VALD */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "adac_test"), /* MCLK */
+                       BERLIN_PINCTRL_FUNCTION(0x6, "sd1b"), /* DAT1 */
+                       BERLIN_PINCTRL_FUNCTION(0x7, "avio")), /* DBG3 */
+       BERLIN_PINCTRL_GROUP("SPDIFO", 0x0, 0x3, 0x12,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO6 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "spdifo"),
+                       BERLIN_PINCTRL_FUNCTION(0x2, "avpll"), /* CLKO */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "adac")), /* DBG3 */
+       BERLIN_PINCTRL_GROUP("I2S2_MCLK", 0x0, 0x3, 0x15,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO7 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* MCLK */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "hdmi"), /* FBCLK */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "pdm")), /* CLKO */
+       BERLIN_PINCTRL_GROUP("I2S2_LRCKI", 0x0, 0x3, 0x18,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO8 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* LRCKI */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "pwm0"),
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sts7"), /* CLK */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "adac_test"), /* LRCK */
+                       BERLIN_PINCTRL_FUNCTION(0x6, "sd1b")), /* DAT2 */
+       BERLIN_PINCTRL_GROUP("I2S2_BCLKI", 0x0, 0x3, 0x1b,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO9 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* BCLKI */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "pwm1"),
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sts7"), /* SOP */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "adac_test"), /* BCLK */
+                       BERLIN_PINCTRL_FUNCTION(0x6, "sd1b")), /* DAT3 */
+       BERLIN_PINCTRL_GROUP("I2S2_DI0", 0x4, 0x3, 0x00,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO10 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI0 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "pwm2"),
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sts7"), /* SD */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "adac_test"), /* SDIN */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "pdm"), /* DI0 */
+                       BERLIN_PINCTRL_FUNCTION(0x6, "sd1b")), /* CDn */
+       BERLIN_PINCTRL_GROUP("I2S2_DI1", 0x4, 0x3, 0x03,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* AVIO GPIO11 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI1 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "pwm3"),
+                       BERLIN_PINCTRL_FUNCTION(0x3, "sts7"), /* VALD */
+                       BERLIN_PINCTRL_FUNCTION(0x4, "adac_test"), /* PWMCLK */
+                       BERLIN_PINCTRL_FUNCTION(0x5, "pdm"), /* DI1 */
+                       BERLIN_PINCTRL_FUNCTION(0x6, "sd1b")), /* WP */
+};
+
+static const struct berlin_desc_group berlin4ct_sysmgr_pinctrl_groups[] = {
+       BERLIN_PINCTRL_GROUP("SM_TW2_SCL", 0x0, 0x3, 0x00,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO19 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "tw2")), /* SCL */
+       BERLIN_PINCTRL_GROUP("SM_TW2_SDA", 0x0, 0x3, 0x03,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO20 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "tw2")), /* SDA */
+       BERLIN_PINCTRL_GROUP("SM_TW3_SCL", 0x0, 0x3, 0x06,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO21 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "tw3")), /* SCL */
+       BERLIN_PINCTRL_GROUP("SM_TW3_SDA", 0x0, 0x3, 0x09,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO22 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "tw3")), /* SDA */
+       BERLIN_PINCTRL_GROUP("SM_TMS", 0x0, 0x3, 0x0c,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TMS */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* SM GPIO0 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "pwm0")),
+       BERLIN_PINCTRL_GROUP("SM_TDI", 0x0, 0x3, 0x0f,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TDI */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* SM GPIO1 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "pwm1")),
+       BERLIN_PINCTRL_GROUP("SM_TDO", 0x0, 0x3, 0x12,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TDO */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio")), /* SM GPIO2 */
+       BERLIN_PINCTRL_GROUP("SM_URT0_TXD", 0x0, 0x3, 0x15,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "uart0"), /* TXD */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio")), /* SM GPIO3 */
+       BERLIN_PINCTRL_GROUP("SM_URT0_RXD", 0x0, 0x3, 0x18,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "uart0"), /* RXD */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio")), /* SM GPIO4 */
+       BERLIN_PINCTRL_GROUP("SM_URT1_TXD", 0x0, 0x3, 0x1b,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO5 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "uart1"), /* TXD */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "eth1"), /* RXCLK */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "pwm2"),
+                       BERLIN_PINCTRL_FUNCTION(0x4, "timer0"),
+                       BERLIN_PINCTRL_FUNCTION(0x5, "clk_25m")),
+       BERLIN_PINCTRL_GROUP("SM_URT1_RXD", 0x4, 0x3, 0x00,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO6 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "uart1"), /* RXD */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "pwm3"),
+                       BERLIN_PINCTRL_FUNCTION(0x4, "timer1")),
+       BERLIN_PINCTRL_GROUP("SM_SPI2_SS0n", 0x4, 0x3, 0x03,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "spi2"), /* SS0 n*/
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio")), /* SM GPIO7 */
+       BERLIN_PINCTRL_GROUP("SM_SPI2_SS1n", 0x4, 0x3, 0x06,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO8 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "spi2")), /* SS1n */
+       BERLIN_PINCTRL_GROUP("SM_SPI2_SS2n", 0x4, 0x3, 0x09,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO9 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "spi2"), /* SS2n */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "eth1"), /* MDC */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "pwm0"),
+                       BERLIN_PINCTRL_FUNCTION(0x4, "timer0"),
+                       BERLIN_PINCTRL_FUNCTION(0x5, "clk_25m")),
+       BERLIN_PINCTRL_GROUP("SM_SPI2_SS3n", 0x4, 0x3, 0x0c,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO10 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "spi2"), /* SS3n */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "eth1"), /* MDIO */
+                       BERLIN_PINCTRL_FUNCTION(0x3, "pwm1"),
+                       BERLIN_PINCTRL_FUNCTION(0x4, "timer1")),
+       BERLIN_PINCTRL_GROUP("SM_SPI2_SDO", 0x4, 0x3, 0x0f,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "spi2"), /* SDO */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio")), /* SM GPIO11 */
+       BERLIN_PINCTRL_GROUP("SM_SPI2_SDI", 0x4, 0x3, 0x12,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "spi2"), /* SDI */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio")), /* SM GPIO12 */
+       BERLIN_PINCTRL_GROUP("SM_SPI2_SCLK", 0x4, 0x3, 0x15,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "spi2"), /* SCLK */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio")), /* SM GPIO13 */
+       BERLIN_PINCTRL_GROUP("SM_FE_LED0", 0x4, 0x3, 0x18,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO14 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "led")), /* LED0 */
+       BERLIN_PINCTRL_GROUP("SM_FE_LED1", 0x4, 0x3, 0x1b,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "pwr"),
+                       BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* SM GPIO 15 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "led")), /* LED1 */
+       BERLIN_PINCTRL_GROUP("SM_FE_LED2", 0x8, 0x3, 0x00,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO16 */
+                       BERLIN_PINCTRL_FUNCTION(0x2, "led")), /* LED2 */
+       BERLIN_PINCTRL_GROUP("SM_HDMI_HPD", 0x8, 0x3, 0x03,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO17 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "hdmi")), /* HPD */
+       BERLIN_PINCTRL_GROUP("SM_HDMI_CEC", 0x8, 0x3, 0x06,
+                       BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* SM GPIO18 */
+                       BERLIN_PINCTRL_FUNCTION(0x1, "hdmi")), /* CEC */
+};
+
+static const struct berlin_pinctrl_desc berlin4ct_soc_pinctrl_data = {
+       .groups = berlin4ct_soc_pinctrl_groups,
+       .ngroups = ARRAY_SIZE(berlin4ct_soc_pinctrl_groups),
+};
+
+static const struct berlin_pinctrl_desc berlin4ct_avio_pinctrl_data = {
+       .groups = berlin4ct_avio_pinctrl_groups,
+       .ngroups = ARRAY_SIZE(berlin4ct_avio_pinctrl_groups),
+};
+
+static const struct berlin_pinctrl_desc berlin4ct_sysmgr_pinctrl_data = {
+       .groups = berlin4ct_sysmgr_pinctrl_groups,
+       .ngroups = ARRAY_SIZE(berlin4ct_sysmgr_pinctrl_groups),
+};
+
+static const struct of_device_id berlin4ct_pinctrl_match[] = {
+       {
+               .compatible = "marvell,berlin4ct-soc-pinctrl",
+               .data = &berlin4ct_soc_pinctrl_data,
+       },
+       {
+               .compatible = "marvell,berlin4ct-avio-pinctrl",
+               .data = &berlin4ct_avio_pinctrl_data,
+       },
+       {
+               .compatible = "marvell,berlin4ct-system-pinctrl",
+               .data = &berlin4ct_sysmgr_pinctrl_data,
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, berlin4ct_pinctrl_match);
+
+static int berlin4ct_pinctrl_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *match =
+               of_match_device(berlin4ct_pinctrl_match, &pdev->dev);
+       struct regmap_config *rmconfig;
+       struct regmap *regmap;
+       struct resource *res;
+       void __iomem *base;
+
+       rmconfig = devm_kzalloc(&pdev->dev, sizeof(*rmconfig), GFP_KERNEL);
+       if (!rmconfig)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       rmconfig->reg_bits = 32,
+       rmconfig->val_bits = 32,
+       rmconfig->reg_stride = 4,
+       rmconfig->max_register = resource_size(res);
+
+       regmap = devm_regmap_init_mmio(&pdev->dev, base, rmconfig);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       return berlin_pinctrl_probe_regmap(pdev, match->data, regmap);
+}
+
+static struct platform_driver berlin4ct_pinctrl_driver = {
+       .probe  = berlin4ct_pinctrl_probe,
+       .driver = {
+               .name = "berlin4ct-pinctrl",
+               .of_match_table = berlin4ct_pinctrl_match,
+       },
+};
+module_platform_driver(berlin4ct_pinctrl_driver);
+
+MODULE_AUTHOR("Jisheng Zhang <jszhang@marvell.com>");
+MODULE_DESCRIPTION("Marvell berlin4ct pinctrl driver");
+MODULE_LICENSE("GPL");
index f49580617055a25d6f3caa55189b473b880eb850..46f2b4818da3ae3a1c736b172916ab000252d3a2 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2014 Marvell Technology Group Ltd.
  *
- * Antoine TÃ\83©nart <antoine.tenart@free-electrons.com>
+ * Antoine Ténart <antoine.tenart@free-electrons.com>
  *
  * This file is licensed under the terms of the GNU General Public
  * License version 2. This program is licensed "as is" without any
@@ -292,20 +292,14 @@ static struct pinctrl_desc berlin_pctrl_desc = {
        .owner          = THIS_MODULE,
 };
 
-int berlin_pinctrl_probe(struct platform_device *pdev,
-                        const struct berlin_pinctrl_desc *desc)
+int berlin_pinctrl_probe_regmap(struct platform_device *pdev,
+                               const struct berlin_pinctrl_desc *desc,
+                               struct regmap *regmap)
 {
        struct device *dev = &pdev->dev;
-       struct device_node *parent_np = of_get_parent(dev->of_node);
        struct berlin_pinctrl *pctrl;
-       struct regmap *regmap;
        int ret;
 
-       regmap = syscon_node_to_regmap(parent_np);
-       of_node_put(parent_np);
-       if (IS_ERR(regmap))
-               return PTR_ERR(regmap);
-
        pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL);
        if (!pctrl)
                return -ENOMEM;
@@ -330,3 +324,17 @@ int berlin_pinctrl_probe(struct platform_device *pdev,
 
        return 0;
 }
+
+int berlin_pinctrl_probe(struct platform_device *pdev,
+                        const struct berlin_pinctrl_desc *desc)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *parent_np = of_get_parent(dev->of_node);
+       struct regmap *regmap = syscon_node_to_regmap(parent_np);
+
+       of_node_put(parent_np);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       return berlin_pinctrl_probe_regmap(pdev, desc, regmap);
+}
index e1aa8414519418944ea09c92071d073238264821..e9b30f95b03e3c72c68d0dd1ffa1957d930416c9 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2014 Marvell Technology Group Ltd.
  *
- * Antoine TÃ\83©nart <antoine.tenart@free-electrons.com>
+ * Antoine Ténart <antoine.tenart@free-electrons.com>
  *
  * This file is licensed under the terms of the GNU General Public
  * License version 2. This program is licensed "as is" without any
@@ -58,4 +58,8 @@ struct berlin_pinctrl_function {
 int berlin_pinctrl_probe(struct platform_device *pdev,
                         const struct berlin_pinctrl_desc *desc);
 
+int berlin_pinctrl_probe_regmap(struct platform_device *pdev,
+                               const struct berlin_pinctrl_desc *desc,
+                               struct regmap *regmap);
+
 #endif /* __PINCTRL_BERLIN_H */
index 9638a00c67c2bd9a3735a632c99d01e9c2625e5d..2686a4450dfc3261029d560d2ce9838191fbf39c 100644 (file)
@@ -1240,6 +1240,38 @@ int pinctrl_force_default(struct pinctrl_dev *pctldev)
 }
 EXPORT_SYMBOL_GPL(pinctrl_force_default);
 
+/**
+ * pinctrl_init_done() - tell pinctrl probe is done
+ *
+ * We'll use this time to switch the pins from "init" to "default" unless the
+ * driver selected some other state.
+ *
+ * @dev: device to that's done probing
+ */
+int pinctrl_init_done(struct device *dev)
+{
+       struct dev_pin_info *pins = dev->pins;
+       int ret;
+
+       if (!pins)
+               return 0;
+
+       if (IS_ERR(pins->init_state))
+               return 0; /* No such state */
+
+       if (pins->p->state != pins->init_state)
+               return 0; /* Not at init anyway */
+
+       if (IS_ERR(pins->default_state))
+               return 0; /* No default state */
+
+       ret = pinctrl_select_state(pins->p, pins->default_state);
+       if (ret)
+               dev_err(dev, "failed to activate default pinctrl state\n");
+
+       return ret;
+}
+
 #ifdef CONFIG_PM
 
 /**
index d7b98ba36825b2a7b7008c46a558ceeb02b6120f..a5bb939873789710d0b00dfcda9805bd287ce92d 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/of_address.h>
 #include <linux/pinctrl/machine.h>
 #include <linux/pinctrl/pinconf.h>
 #include <linux/pinctrl/pinctrl.h>
@@ -39,6 +40,7 @@ struct imx_pinctrl {
        struct device *dev;
        struct pinctrl_dev *pctl;
        void __iomem *base;
+       void __iomem *input_sel_base;
        const struct imx_pinctrl_soc_info *info;
 };
 
@@ -254,7 +256,12 @@ static int imx_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
                         * Regular select input register can never be at offset
                         * 0, and we only print register value for regular case.
                         */
-                       writel(pin->input_val, ipctl->base + pin->input_reg);
+                       if (ipctl->input_sel_base)
+                               writel(pin->input_val, ipctl->input_sel_base +
+                                               pin->input_reg);
+                       else
+                               writel(pin->input_val, ipctl->base +
+                                               pin->input_reg);
                        dev_dbg(ipctl->dev,
                                "==>select_input: offset 0x%x val 0x%x\n",
                                pin->input_reg, pin->input_val);
@@ -542,6 +549,9 @@ static int imx_pinctrl_parse_groups(struct device_node *np,
                struct imx_pin_reg *pin_reg;
                struct imx_pin *pin = &grp->pins[i];
 
+               if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg)
+                       mux_reg = -1;
+
                if (info->flags & SHARE_MUX_CONF_REG) {
                        conf_reg = mux_reg;
                } else {
@@ -550,7 +560,7 @@ static int imx_pinctrl_parse_groups(struct device_node *np,
                                conf_reg = -1;
                }
 
-               pin_id = mux_reg ? mux_reg / 4 : conf_reg / 4;
+               pin_id = (mux_reg != -1) ? mux_reg / 4 : conf_reg / 4;
                pin_reg = &info->pin_regs[pin_id];
                pin->pin = pin_id;
                grp->pin_ids[i] = pin_id;
@@ -580,7 +590,6 @@ static int imx_pinctrl_parse_functions(struct device_node *np,
        struct device_node *child;
        struct imx_pmx_func *func;
        struct imx_pin_group *grp;
-       static u32 grp_index;
        u32 i = 0;
 
        dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name);
@@ -599,7 +608,7 @@ static int imx_pinctrl_parse_functions(struct device_node *np,
 
        for_each_child_of_node(np, child) {
                func->groups[i] = child->name;
-               grp = &info->groups[grp_index++];
+               grp = &info->groups[info->group_index++];
                imx_pinctrl_parse_groups(child, grp, info, i++);
        }
 
@@ -683,6 +692,8 @@ static int imx_pinctrl_probe_dt(struct platform_device *pdev,
 int imx_pinctrl_probe(struct platform_device *pdev,
                      struct imx_pinctrl_soc_info *info)
 {
+       struct device_node *dev_np = pdev->dev.of_node;
+       struct device_node *np;
        struct imx_pinctrl *ipctl;
        struct resource *res;
        int ret, i;
@@ -713,6 +724,23 @@ int imx_pinctrl_probe(struct platform_device *pdev,
        if (IS_ERR(ipctl->base))
                return PTR_ERR(ipctl->base);
 
+       if (of_property_read_bool(dev_np, "fsl,input-sel")) {
+               np = of_parse_phandle(dev_np, "fsl,input-sel", 0);
+               if (np) {
+                       ipctl->input_sel_base = of_iomap(np, 0);
+                       if (IS_ERR(ipctl->input_sel_base)) {
+                               of_node_put(np);
+                               dev_err(&pdev->dev,
+                                       "iomuxc input select base address not found\n");
+                               return PTR_ERR(ipctl->input_sel_base);
+                       }
+               } else {
+                       dev_err(&pdev->dev, "iomuxc fsl,input-sel property not found\n");
+                       return -EINVAL;
+               }
+               of_node_put(np);
+       }
+
        imx_pinctrl_desc.name = dev_name(&pdev->dev);
        imx_pinctrl_desc.pins = info->pins;
        imx_pinctrl_desc.npins = info->npins;
index 49e55d39f7c8aba924f250dad6563148e4219b35..2a592f657c184918ab1e7fbb6d9a4d862f0a2ab5 100644 (file)
@@ -78,12 +78,14 @@ struct imx_pinctrl_soc_info {
        struct imx_pin_reg *pin_regs;
        struct imx_pin_group *groups;
        unsigned int ngroups;
+       unsigned int group_index;
        struct imx_pmx_func *functions;
        unsigned int nfunctions;
        unsigned int flags;
 };
 
 #define SHARE_MUX_CONF_REG     0x1
+#define ZERO_OFFSET_VALID      0x2
 
 #define NO_MUX         0x0
 #define NO_PAD         0x0
index 1fa7530530dd94f25efb29a315076fec0b880871..16dc925117de1ad63cb4fd36251eac746e5bc8a2 100644 (file)
@@ -174,6 +174,17 @@ enum imx7d_pads {
        MX7D_PAD_ENET1_COL = 154,
 };
 
+enum imx7d_lpsr_pads {
+       MX7D_PAD_GPIO1_IO00 = 0,
+       MX7D_PAD_GPIO1_IO01 = 1,
+       MX7D_PAD_GPIO1_IO02 = 2,
+       MX7D_PAD_GPIO1_IO03 = 3,
+       MX7D_PAD_GPIO1_IO04 = 4,
+       MX7D_PAD_GPIO1_IO05 = 5,
+       MX7D_PAD_GPIO1_IO06 = 6,
+       MX7D_PAD_GPIO1_IO07 = 7,
+};
+
 /* Pad names for the pinmux subsystem */
 static const struct pinctrl_pin_desc imx7d_pinctrl_pads[] = {
        IMX_PINCTRL_PIN(MX7D_PAD_RESERVE0),
@@ -333,13 +344,32 @@ static const struct pinctrl_pin_desc imx7d_pinctrl_pads[] = {
        IMX_PINCTRL_PIN(MX7D_PAD_ENET1_COL),
 };
 
+/* Pad names for the pinmux subsystem */
+static const struct pinctrl_pin_desc imx7d_lpsr_pinctrl_pads[] = {
+       IMX_PINCTRL_PIN(MX7D_PAD_GPIO1_IO00),
+       IMX_PINCTRL_PIN(MX7D_PAD_GPIO1_IO01),
+       IMX_PINCTRL_PIN(MX7D_PAD_GPIO1_IO02),
+       IMX_PINCTRL_PIN(MX7D_PAD_GPIO1_IO03),
+       IMX_PINCTRL_PIN(MX7D_PAD_GPIO1_IO04),
+       IMX_PINCTRL_PIN(MX7D_PAD_GPIO1_IO05),
+       IMX_PINCTRL_PIN(MX7D_PAD_GPIO1_IO06),
+       IMX_PINCTRL_PIN(MX7D_PAD_GPIO1_IO07),
+};
+
 static struct imx_pinctrl_soc_info imx7d_pinctrl_info = {
        .pins = imx7d_pinctrl_pads,
        .npins = ARRAY_SIZE(imx7d_pinctrl_pads),
 };
 
+static struct imx_pinctrl_soc_info imx7d_lpsr_pinctrl_info = {
+       .pins = imx7d_lpsr_pinctrl_pads,
+       .npins = ARRAY_SIZE(imx7d_lpsr_pinctrl_pads),
+       .flags = ZERO_OFFSET_VALID,
+};
+
 static struct of_device_id imx7d_pinctrl_of_match[] = {
        { .compatible = "fsl,imx7d-iomuxc", .data = &imx7d_pinctrl_info, },
+       { .compatible = "fsl,imx7d-iomuxc-lpsr", .data = &imx7d_lpsr_pinctrl_info },
        { /* sentinel */ }
 };
 
index f64eecb24755d778c250925bd377408981b4195d..6bbda6b4ab50d9e930544e4bfd72a67904261f85 100644 (file)
@@ -474,7 +474,7 @@ static int mxs_pinctrl_probe_dt(struct platform_device *pdev,
                        f->name = fn = child->name;
                }
                f->ngroups++;
-       };
+       }
 
        /* Get groups for each function */
        idxf = 0;
index fe5e07db0a958258196da3d636c1121779e5245e..4d2efad6553c2d422c182f4c2bb2da3b850caa56 100644 (file)
@@ -34,6 +34,14 @@ config PINCTRL_INTEL
        select GPIOLIB
        select GPIOLIB_IRQCHIP
 
+config PINCTRL_BROXTON
+       tristate "Intel Broxton pinctrl and GPIO driver"
+       depends on ACPI
+       select PINCTRL_INTEL
+       help
+         Broxton pinctrl driver provides an interface that allows
+         configuring of SoC pins and using them as GPIOs.
+
 config PINCTRL_SUNRISEPOINT
        tristate "Intel Sunrisepoint pinctrl and GPIO driver"
        depends on ACPI
index fee756e1255bb30aa64f2428d324501c82ddefdd..03bc68e3546cad35f8aa765222bb10a240735778 100644 (file)
@@ -3,4 +3,5 @@
 obj-$(CONFIG_PINCTRL_BAYTRAIL)         += pinctrl-baytrail.o
 obj-$(CONFIG_PINCTRL_CHERRYVIEW)       += pinctrl-cherryview.o
 obj-$(CONFIG_PINCTRL_INTEL)            += pinctrl-intel.o
+obj-$(CONFIG_PINCTRL_BROXTON)          += pinctrl-broxton.o
 obj-$(CONFIG_PINCTRL_SUNRISEPOINT)     += pinctrl-sunrisepoint.o
index f79ea430f651996a2f148e141319861e285c2d28..b59ce75b1947a2327804ca075769cddc9eba2801 100644 (file)
@@ -696,6 +696,7 @@ static int byt_gpio_resume(struct device *dev)
 }
 #endif
 
+#ifdef CONFIG_PM
 static int byt_gpio_runtime_suspend(struct device *dev)
 {
        return 0;
@@ -705,6 +706,7 @@ static int byt_gpio_runtime_resume(struct device *dev)
 {
        return 0;
 }
+#endif
 
 static const struct dev_pm_ops byt_gpio_pm_ops = {
        SET_LATE_SYSTEM_SLEEP_PM_OPS(byt_gpio_suspend, byt_gpio_resume)
diff --git a/drivers/pinctrl/intel/pinctrl-broxton.c b/drivers/pinctrl/intel/pinctrl-broxton.c
new file mode 100644 (file)
index 0000000..e42d5d4
--- /dev/null
@@ -0,0 +1,1065 @@
+/*
+ * Intel Broxton SoC pinctrl/GPIO driver
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@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/acpi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-intel.h"
+
+#define BXT_PAD_OWN    0x020
+#define BXT_HOSTSW_OWN 0x080
+#define BXT_PADCFGLOCK 0x090
+#define BXT_GPI_IE     0x110
+
+#define BXT_COMMUNITY(s, e)                            \
+       {                                               \
+               .padown_offset = BXT_PAD_OWN,           \
+               .padcfglock_offset = BXT_PADCFGLOCK,    \
+               .hostown_offset = BXT_HOSTSW_OWN,       \
+               .ie_offset = BXT_GPI_IE,                \
+               .pin_base = (s),                        \
+               .npins = ((e) - (s) + 1),               \
+       }
+
+/* BXT */
+static const struct pinctrl_pin_desc bxt_north_pins[] = {
+       PINCTRL_PIN(0, "GPIO_0"),
+       PINCTRL_PIN(1, "GPIO_1"),
+       PINCTRL_PIN(2, "GPIO_2"),
+       PINCTRL_PIN(3, "GPIO_3"),
+       PINCTRL_PIN(4, "GPIO_4"),
+       PINCTRL_PIN(5, "GPIO_5"),
+       PINCTRL_PIN(6, "GPIO_6"),
+       PINCTRL_PIN(7, "GPIO_7"),
+       PINCTRL_PIN(8, "GPIO_8"),
+       PINCTRL_PIN(9, "GPIO_9"),
+       PINCTRL_PIN(10, "GPIO_10"),
+       PINCTRL_PIN(11, "GPIO_11"),
+       PINCTRL_PIN(12, "GPIO_12"),
+       PINCTRL_PIN(13, "GPIO_13"),
+       PINCTRL_PIN(14, "GPIO_14"),
+       PINCTRL_PIN(15, "GPIO_15"),
+       PINCTRL_PIN(16, "GPIO_16"),
+       PINCTRL_PIN(17, "GPIO_17"),
+       PINCTRL_PIN(18, "GPIO_18"),
+       PINCTRL_PIN(19, "GPIO_19"),
+       PINCTRL_PIN(20, "GPIO_20"),
+       PINCTRL_PIN(21, "GPIO_21"),
+       PINCTRL_PIN(22, "GPIO_22"),
+       PINCTRL_PIN(23, "GPIO_23"),
+       PINCTRL_PIN(24, "GPIO_24"),
+       PINCTRL_PIN(25, "GPIO_25"),
+       PINCTRL_PIN(26, "GPIO_26"),
+       PINCTRL_PIN(27, "GPIO_27"),
+       PINCTRL_PIN(28, "GPIO_28"),
+       PINCTRL_PIN(29, "GPIO_29"),
+       PINCTRL_PIN(30, "GPIO_30"),
+       PINCTRL_PIN(31, "GPIO_31"),
+       PINCTRL_PIN(32, "GPIO_32"),
+       PINCTRL_PIN(33, "GPIO_33"),
+       PINCTRL_PIN(34, "PWM0"),
+       PINCTRL_PIN(35, "PWM1"),
+       PINCTRL_PIN(36, "PWM2"),
+       PINCTRL_PIN(37, "PWM3"),
+       PINCTRL_PIN(38, "LPSS_UART0_RXD"),
+       PINCTRL_PIN(39, "LPSS_UART0_TXD"),
+       PINCTRL_PIN(40, "LPSS_UART0_RTS_B"),
+       PINCTRL_PIN(41, "LPSS_UART0_CTS_B"),
+       PINCTRL_PIN(42, "LPSS_UART1_RXD"),
+       PINCTRL_PIN(43, "LPSS_UART1_TXD"),
+       PINCTRL_PIN(44, "LPSS_UART1_RTS_B"),
+       PINCTRL_PIN(45, "LPSS_UART1_CTS_B"),
+       PINCTRL_PIN(46, "LPSS_UART2_RXD"),
+       PINCTRL_PIN(47, "LPSS_UART2_TXD"),
+       PINCTRL_PIN(48, "LPSS_UART2_RTS_B"),
+       PINCTRL_PIN(49, "LPSS_UART2_CTS_B"),
+       PINCTRL_PIN(50, "ISH_UART0_RXD"),
+       PINCTRL_PIN(51, "ISH_UART0_TXT"),
+       PINCTRL_PIN(52, "ISH_UART0_RTS_B"),
+       PINCTRL_PIN(53, "ISH_UART0_CTS_B"),
+       PINCTRL_PIN(54, "ISH_UART1_RXD"),
+       PINCTRL_PIN(55, "ISH_UART1_TXT"),
+       PINCTRL_PIN(56, "ISH_UART1_RTS_B"),
+       PINCTRL_PIN(57, "ISH_UART1_CTS_B"),
+       PINCTRL_PIN(58, "ISH_UART2_RXD"),
+       PINCTRL_PIN(59, "ISH_UART2_TXD"),
+       PINCTRL_PIN(60, "ISH_UART2_RTS_B"),
+       PINCTRL_PIN(61, "ISH_UART2_CTS_B"),
+       PINCTRL_PIN(62, "GP_CAMERASB00"),
+       PINCTRL_PIN(63, "GP_CAMERASB01"),
+       PINCTRL_PIN(64, "GP_CAMERASB02"),
+       PINCTRL_PIN(65, "GP_CAMERASB03"),
+       PINCTRL_PIN(66, "GP_CAMERASB04"),
+       PINCTRL_PIN(67, "GP_CAMERASB05"),
+       PINCTRL_PIN(68, "GP_CAMERASB06"),
+       PINCTRL_PIN(69, "GP_CAMERASB07"),
+       PINCTRL_PIN(70, "GP_CAMERASB08"),
+       PINCTRL_PIN(71, "GP_CAMERASB09"),
+       PINCTRL_PIN(72, "GP_CAMERASB10"),
+       PINCTRL_PIN(73, "GP_CAMERASB11"),
+       PINCTRL_PIN(74, "TCK"),
+       PINCTRL_PIN(75, "TRST_B"),
+       PINCTRL_PIN(76, "TMS"),
+       PINCTRL_PIN(77, "TDI"),
+       PINCTRL_PIN(78, "CX_PMODE"),
+       PINCTRL_PIN(79, "CX_PREQ_B"),
+       PINCTRL_PIN(80, "JTAGX"),
+       PINCTRL_PIN(81, "CX_PRDY_B"),
+       PINCTRL_PIN(82, "TDO"),
+};
+
+static const unsigned bxt_north_pwm0_pins[] = { 34 };
+static const unsigned bxt_north_pwm1_pins[] = { 35 };
+static const unsigned bxt_north_pwm2_pins[] = { 36 };
+static const unsigned bxt_north_pwm3_pins[] = { 37 };
+static const unsigned bxt_north_uart0_pins[] = { 38, 39, 40, 41 };
+static const unsigned bxt_north_uart1_pins[] = { 42, 43, 44, 45 };
+static const unsigned bxt_north_uart2_pins[] = { 46, 47, 48, 49 };
+static const unsigned bxt_north_uart0b_pins[] = { 50, 51, 52, 53 };
+static const unsigned bxt_north_uart1b_pins[] = { 54, 55, 56, 57 };
+static const unsigned bxt_north_uart2b_pins[] = { 58, 59, 60, 61 };
+static const unsigned bxt_north_uart3_pins[] = { 58, 59, 60, 61 };
+
+static const struct intel_pingroup bxt_north_groups[] = {
+       PIN_GROUP("pwm0_grp", bxt_north_pwm0_pins, 1),
+       PIN_GROUP("pwm1_grp", bxt_north_pwm1_pins, 1),
+       PIN_GROUP("pwm2_grp", bxt_north_pwm2_pins, 1),
+       PIN_GROUP("pwm3_grp", bxt_north_pwm3_pins, 1),
+       PIN_GROUP("uart0_grp", bxt_north_uart0_pins, 1),
+       PIN_GROUP("uart1_grp", bxt_north_uart1_pins, 1),
+       PIN_GROUP("uart2_grp", bxt_north_uart2_pins, 1),
+       PIN_GROUP("uart0b_grp", bxt_north_uart0b_pins, 2),
+       PIN_GROUP("uart1b_grp", bxt_north_uart1b_pins, 2),
+       PIN_GROUP("uart2b_grp", bxt_north_uart2b_pins, 2),
+       PIN_GROUP("uart3_grp", bxt_north_uart3_pins, 3),
+};
+
+static const char * const bxt_north_pwm0_groups[] = { "pwm0_grp" };
+static const char * const bxt_north_pwm1_groups[] = { "pwm1_grp" };
+static const char * const bxt_north_pwm2_groups[] = { "pwm2_grp" };
+static const char * const bxt_north_pwm3_groups[] = { "pwm3_grp" };
+static const char * const bxt_north_uart0_groups[] = {
+       "uart0_grp", "uart0b_grp",
+};
+static const char * const bxt_north_uart1_groups[] = {
+       "uart1_grp", "uart1b_grp",
+};
+static const char * const bxt_north_uart2_groups[] = {
+       "uart2_grp", "uart2b_grp",
+};
+static const char * const bxt_north_uart3_groups[] = { "uart3_grp" };
+
+static const struct intel_function bxt_north_functions[] = {
+       FUNCTION("pwm0", bxt_north_pwm0_groups),
+       FUNCTION("pwm1", bxt_north_pwm1_groups),
+       FUNCTION("pwm2", bxt_north_pwm2_groups),
+       FUNCTION("pwm3", bxt_north_pwm3_groups),
+       FUNCTION("uart0", bxt_north_uart0_groups),
+       FUNCTION("uart1", bxt_north_uart1_groups),
+       FUNCTION("uart2", bxt_north_uart2_groups),
+       FUNCTION("uart3", bxt_north_uart3_groups),
+};
+
+static const struct intel_community bxt_north_communities[] = {
+       BXT_COMMUNITY(0, 82),
+};
+
+static const struct intel_pinctrl_soc_data bxt_north_soc_data = {
+       .uid = "1",
+       .pins = bxt_north_pins,
+       .npins = ARRAY_SIZE(bxt_north_pins),
+       .groups = bxt_north_groups,
+       .ngroups = ARRAY_SIZE(bxt_north_groups),
+       .functions = bxt_north_functions,
+       .nfunctions = ARRAY_SIZE(bxt_north_functions),
+       .communities = bxt_north_communities,
+       .ncommunities = ARRAY_SIZE(bxt_north_communities),
+};
+
+static const struct pinctrl_pin_desc bxt_northwest_pins[] = {
+       PINCTRL_PIN(0, "PMC_SPI_FS0"),
+       PINCTRL_PIN(1, "PMC_SPI_FS1"),
+       PINCTRL_PIN(2, "PMC_SPI_FS2"),
+       PINCTRL_PIN(3, "PMC_SPI_RXD"),
+       PINCTRL_PIN(4, "PMC_SPI_TXD"),
+       PINCTRL_PIN(5, "PMC_SPI_CLK"),
+       PINCTRL_PIN(6, "PMC_UART_RXD"),
+       PINCTRL_PIN(7, "PMC_UART_TXD"),
+       PINCTRL_PIN(8, "PMIC_PWRGOOD"),
+       PINCTRL_PIN(9, "PMIC_RESET_B"),
+       PINCTRL_PIN(10, "RTC_CLK"),
+       PINCTRL_PIN(11, "PMIC_SDWN_B"),
+       PINCTRL_PIN(12, "PMIC_BCUDISW2"),
+       PINCTRL_PIN(13, "PMIC_BCUDISCRIT"),
+       PINCTRL_PIN(14, "PMIC_THERMTRIP_B"),
+       PINCTRL_PIN(15, "PMIC_STDBY"),
+       PINCTRL_PIN(16, "SVID0_ALERT_B"),
+       PINCTRL_PIN(17, "SVID0_DATA"),
+       PINCTRL_PIN(18, "SVID0_CLK"),
+       PINCTRL_PIN(19, "PMIC_I2C_SCL"),
+       PINCTRL_PIN(20, "PMIC_I2C_SDA"),
+       PINCTRL_PIN(21, "AVS_I2S1_MCLK"),
+       PINCTRL_PIN(22, "AVS_I2S1_BCLK"),
+       PINCTRL_PIN(23, "AVS_I2S1_WS_SYNC"),
+       PINCTRL_PIN(24, "AVS_I2S1_SDI"),
+       PINCTRL_PIN(25, "AVS_I2S1_SDO"),
+       PINCTRL_PIN(26, "AVS_M_CLK_A1"),
+       PINCTRL_PIN(27, "AVS_M_CLK_B1"),
+       PINCTRL_PIN(28, "AVS_M_DATA_1"),
+       PINCTRL_PIN(29, "AVS_M_CLK_AB2"),
+       PINCTRL_PIN(30, "AVS_M_DATA_2"),
+       PINCTRL_PIN(31, "AVS_I2S2_MCLK"),
+       PINCTRL_PIN(32, "AVS_I2S2_BCLK"),
+       PINCTRL_PIN(33, "AVS_I2S2_WS_SYNC"),
+       PINCTRL_PIN(34, "AVS_I2S2_SDI"),
+       PINCTRL_PIN(35, "AVS_I2S2_SDOK"),
+       PINCTRL_PIN(36, "AVS_I2S3_BCLK"),
+       PINCTRL_PIN(37, "AVS_I2S3_WS_SYNC"),
+       PINCTRL_PIN(38, "AVS_I2S3_SDI"),
+       PINCTRL_PIN(39, "AVS_I2S3_SDO"),
+       PINCTRL_PIN(40, "AVS_I2S4_BCLK"),
+       PINCTRL_PIN(41, "AVS_I2S4_WS_SYNC"),
+       PINCTRL_PIN(42, "AVS_I2S4_SDI"),
+       PINCTRL_PIN(43, "AVS_I2S4_SDO"),
+       PINCTRL_PIN(44, "PROCHOT_B"),
+       PINCTRL_PIN(45, "FST_SPI_CS0_B"),
+       PINCTRL_PIN(46, "FST_SPI_CS1_B"),
+       PINCTRL_PIN(47, "FST_SPI_MOSI_IO0"),
+       PINCTRL_PIN(48, "FST_SPI_MISO_IO1"),
+       PINCTRL_PIN(49, "FST_SPI_IO2"),
+       PINCTRL_PIN(50, "FST_SPI_IO3"),
+       PINCTRL_PIN(51, "FST_SPI_CLK"),
+       PINCTRL_PIN(52, "FST_SPI_CLK_FB"),
+       PINCTRL_PIN(53, "GP_SSP_0_CLK"),
+       PINCTRL_PIN(54, "GP_SSP_0_FS0"),
+       PINCTRL_PIN(55, "GP_SSP_0_FS1"),
+       PINCTRL_PIN(56, "GP_SSP_0_FS2"),
+       PINCTRL_PIN(57, "GP_SSP_0_RXD"),
+       PINCTRL_PIN(58, "GP_SSP_0_TXD"),
+       PINCTRL_PIN(59, "GP_SSP_1_CLK"),
+       PINCTRL_PIN(60, "GP_SSP_1_FS0"),
+       PINCTRL_PIN(61, "GP_SSP_1_FS1"),
+       PINCTRL_PIN(62, "GP_SSP_1_FS2"),
+       PINCTRL_PIN(63, "GP_SSP_1_FS3"),
+       PINCTRL_PIN(64, "GP_SSP_1_RXD"),
+       PINCTRL_PIN(65, "GP_SSP_1_TXD"),
+       PINCTRL_PIN(66, "GP_SSP_2_CLK"),
+       PINCTRL_PIN(67, "GP_SSP_2_FS0"),
+       PINCTRL_PIN(68, "GP_SSP_2_FS1"),
+       PINCTRL_PIN(69, "GP_SSP_2_FS2"),
+       PINCTRL_PIN(70, "GP_SSP_2_RXD"),
+       PINCTRL_PIN(71, "GP_SSP_2_TXD"),
+};
+
+static const unsigned bxt_northwest_ssp0_pins[] = { 53, 54, 55, 56, 57, 58 };
+static const unsigned bxt_northwest_ssp1_pins[] = {
+       59, 60, 61, 62, 63, 64, 65
+};
+static const unsigned bxt_northwest_ssp2_pins[] = { 66, 67, 68, 69, 70, 71 };
+static const unsigned bxt_northwest_uart3_pins[] = { 67, 68, 69, 70 };
+
+static const struct intel_pingroup bxt_northwest_groups[] = {
+       PIN_GROUP("ssp0_grp", bxt_northwest_ssp0_pins, 1),
+       PIN_GROUP("ssp1_grp", bxt_northwest_ssp1_pins, 1),
+       PIN_GROUP("ssp2_grp", bxt_northwest_ssp2_pins, 1),
+       PIN_GROUP("uart3_grp", bxt_northwest_uart3_pins, 2),
+};
+
+static const char * const bxt_northwest_ssp0_groups[] = { "ssp0_grp" };
+static const char * const bxt_northwest_ssp1_groups[] = { "ssp1_grp" };
+static const char * const bxt_northwest_ssp2_groups[] = { "ssp2_grp" };
+static const char * const bxt_northwest_uart3_groups[] = { "uart3_grp" };
+
+static const struct intel_function bxt_northwest_functions[] = {
+       FUNCTION("ssp0", bxt_northwest_ssp0_groups),
+       FUNCTION("ssp1", bxt_northwest_ssp1_groups),
+       FUNCTION("ssp2", bxt_northwest_ssp2_groups),
+       FUNCTION("uart3", bxt_northwest_uart3_groups),
+};
+
+static const struct intel_community bxt_northwest_communities[] = {
+       BXT_COMMUNITY(0, 71),
+};
+
+static const struct intel_pinctrl_soc_data bxt_northwest_soc_data = {
+       .uid = "2",
+       .pins = bxt_northwest_pins,
+       .npins = ARRAY_SIZE(bxt_northwest_pins),
+       .groups = bxt_northwest_groups,
+       .ngroups = ARRAY_SIZE(bxt_northwest_groups),
+       .functions = bxt_northwest_functions,
+       .nfunctions = ARRAY_SIZE(bxt_northwest_functions),
+       .communities = bxt_northwest_communities,
+       .ncommunities = ARRAY_SIZE(bxt_northwest_communities),
+};
+
+static const struct pinctrl_pin_desc bxt_west_pins[] = {
+       PINCTRL_PIN(0, "LPSS_I2C0_SDA"),
+       PINCTRL_PIN(1, "LPSS_I2C0_SCL"),
+       PINCTRL_PIN(2, "LPSS_I2C1_SDA"),
+       PINCTRL_PIN(3, "LPSS_I2C1_SCL"),
+       PINCTRL_PIN(4, "LPSS_I2C2_SDA"),
+       PINCTRL_PIN(5, "LPSS_I2C2_SCL"),
+       PINCTRL_PIN(6, "LPSS_I2C3_SDA"),
+       PINCTRL_PIN(7, "LPSS_I2C3_SCL"),
+       PINCTRL_PIN(8, "LPSS_I2C4_SDA"),
+       PINCTRL_PIN(9, "LPSS_I2C4_SCL"),
+       PINCTRL_PIN(10, "LPSS_I2C5_SDA"),
+       PINCTRL_PIN(11, "LPSS_I2C5_SCL"),
+       PINCTRL_PIN(12, "LPSS_I2C6_SDA"),
+       PINCTRL_PIN(13, "LPSS_I2C6_SCL"),
+       PINCTRL_PIN(14, "LPSS_I2C7_SDA"),
+       PINCTRL_PIN(15, "LPSS_I2C7_SCL"),
+       PINCTRL_PIN(16, "ISH_I2C0_SDA"),
+       PINCTRL_PIN(17, "ISH_I2C0_SCL"),
+       PINCTRL_PIN(18, "ISH_I2C1_SDA"),
+       PINCTRL_PIN(19, "ISH_I2C1_SCL"),
+       PINCTRL_PIN(20, "ISH_I2C2_SDA"),
+       PINCTRL_PIN(21, "ISH_I2C2_SCL"),
+       PINCTRL_PIN(22, "ISH_GPIO_0"),
+       PINCTRL_PIN(23, "ISH_GPIO_1"),
+       PINCTRL_PIN(24, "ISH_GPIO_2"),
+       PINCTRL_PIN(25, "ISH_GPIO_3"),
+       PINCTRL_PIN(26, "ISH_GPIO_4"),
+       PINCTRL_PIN(27, "ISH_GPIO_5"),
+       PINCTRL_PIN(28, "ISH_GPIO_6"),
+       PINCTRL_PIN(29, "ISH_GPIO_7"),
+       PINCTRL_PIN(30, "ISH_GPIO_8"),
+       PINCTRL_PIN(31, "ISH_GPIO_9"),
+       PINCTRL_PIN(32, "MODEM_CLKREQ"),
+       PINCTRL_PIN(33, "DGCLKDBG_PMC_0"),
+       PINCTRL_PIN(34, "DGCLKDBG_PMC_1"),
+       PINCTRL_PIN(35, "DGCLKDBG_PMC_2"),
+       PINCTRL_PIN(36, "DGCLKDBG_ICLK_0"),
+       PINCTRL_PIN(37, "DGCLKDBG_ICLK_1"),
+       PINCTRL_PIN(38, "OSC_CLK_OUT_0"),
+       PINCTRL_PIN(39, "OSC_CLK_OUT_1"),
+       PINCTRL_PIN(40, "OSC_CLK_OUT_2"),
+       PINCTRL_PIN(41, "OSC_CLK_OUT_3"),
+};
+
+static const unsigned bxt_west_i2c0_pins[] = { 0, 1 };
+static const unsigned bxt_west_i2c1_pins[] = { 2, 3 };
+static const unsigned bxt_west_i2c2_pins[] = { 4, 5 };
+static const unsigned bxt_west_i2c3_pins[] = { 6, 7 };
+static const unsigned bxt_west_i2c4_pins[] = { 8, 9 };
+static const unsigned bxt_west_i2c5_pins[] = { 10, 11 };
+static const unsigned bxt_west_i2c6_pins[] = { 12, 13 };
+static const unsigned bxt_west_i2c7_pins[] = { 14, 15 };
+static const unsigned bxt_west_i2c5b_pins[] = { 16, 17 };
+static const unsigned bxt_west_i2c6b_pins[] = { 18, 19 };
+static const unsigned bxt_west_i2c7b_pins[] = { 20, 21 };
+
+static const struct intel_pingroup bxt_west_groups[] = {
+       PIN_GROUP("i2c0_grp", bxt_west_i2c0_pins, 1),
+       PIN_GROUP("i2c1_grp", bxt_west_i2c1_pins, 1),
+       PIN_GROUP("i2c2_grp", bxt_west_i2c2_pins, 1),
+       PIN_GROUP("i2c3_grp", bxt_west_i2c3_pins, 1),
+       PIN_GROUP("i2c4_grp", bxt_west_i2c4_pins, 1),
+       PIN_GROUP("i2c5_grp", bxt_west_i2c5_pins, 1),
+       PIN_GROUP("i2c6_grp", bxt_west_i2c6_pins, 1),
+       PIN_GROUP("i2c7_grp", bxt_west_i2c7_pins, 1),
+       PIN_GROUP("i2c5b_grp", bxt_west_i2c5b_pins, 2),
+       PIN_GROUP("i2c6b_grp", bxt_west_i2c6b_pins, 2),
+       PIN_GROUP("i2c7b_grp", bxt_west_i2c7b_pins, 2),
+};
+
+static const char * const bxt_west_i2c0_groups[] = { "i2c0_grp" };
+static const char * const bxt_west_i2c1_groups[] = { "i2c1_grp" };
+static const char * const bxt_west_i2c2_groups[] = { "i2c2_grp" };
+static const char * const bxt_west_i2c3_groups[] = { "i2c3_grp" };
+static const char * const bxt_west_i2c4_groups[] = { "i2c4_grp" };
+static const char * const bxt_west_i2c5_groups[] = { "i2c5_grp", "i2c5b_grp" };
+static const char * const bxt_west_i2c6_groups[] = { "i2c6_grp", "i2c6b_grp" };
+static const char * const bxt_west_i2c7_groups[] = { "i2c7_grp", "i2c7b_grp" };
+
+static const struct intel_function bxt_west_functions[] = {
+       FUNCTION("i2c0", bxt_west_i2c0_groups),
+       FUNCTION("i2c1", bxt_west_i2c1_groups),
+       FUNCTION("i2c2", bxt_west_i2c2_groups),
+       FUNCTION("i2c3", bxt_west_i2c3_groups),
+       FUNCTION("i2c4", bxt_west_i2c4_groups),
+       FUNCTION("i2c5", bxt_west_i2c5_groups),
+       FUNCTION("i2c6", bxt_west_i2c6_groups),
+       FUNCTION("i2c7", bxt_west_i2c7_groups),
+};
+
+static const struct intel_community bxt_west_communities[] = {
+       BXT_COMMUNITY(0, 41),
+};
+
+static const struct intel_pinctrl_soc_data bxt_west_soc_data = {
+       .uid = "3",
+       .pins = bxt_west_pins,
+       .npins = ARRAY_SIZE(bxt_west_pins),
+       .groups = bxt_west_groups,
+       .ngroups = ARRAY_SIZE(bxt_west_groups),
+       .functions = bxt_west_functions,
+       .nfunctions = ARRAY_SIZE(bxt_west_functions),
+       .communities = bxt_west_communities,
+       .ncommunities = ARRAY_SIZE(bxt_west_communities),
+};
+
+static const struct pinctrl_pin_desc bxt_southwest_pins[] = {
+       PINCTRL_PIN(0, "EMMC0_CLK"),
+       PINCTRL_PIN(1, "EMMC0_D0"),
+       PINCTRL_PIN(2, "EMMC0_D1"),
+       PINCTRL_PIN(3, "EMMC0_D2"),
+       PINCTRL_PIN(4, "EMMC0_D3"),
+       PINCTRL_PIN(5, "EMMC0_D4"),
+       PINCTRL_PIN(6, "EMMC0_D5"),
+       PINCTRL_PIN(7, "EMMC0_D6"),
+       PINCTRL_PIN(8, "EMMC0_D7"),
+       PINCTRL_PIN(9, "EMMC0_CMD"),
+       PINCTRL_PIN(10, "SDIO_CLK"),
+       PINCTRL_PIN(11, "SDIO_D0"),
+       PINCTRL_PIN(12, "SDIO_D1"),
+       PINCTRL_PIN(13, "SDIO_D2"),
+       PINCTRL_PIN(14, "SDIO_D3"),
+       PINCTRL_PIN(15, "SDIO_CMD"),
+       PINCTRL_PIN(16, "SDCARD_CLK"),
+       PINCTRL_PIN(17, "SDCARD_D0"),
+       PINCTRL_PIN(18, "SDCARD_D1"),
+       PINCTRL_PIN(19, "SDCARD_D2"),
+       PINCTRL_PIN(20, "SDCARD_D3"),
+       PINCTRL_PIN(21, "SDCARD_CD_B"),
+       PINCTRL_PIN(22, "SDCARD_CMD"),
+       PINCTRL_PIN(23, "SDCARD_LVL_CLK_FB"),
+       PINCTRL_PIN(24, "SDCARD_LVL_CMD_DIR"),
+       PINCTRL_PIN(25, "SDCARD_LVL_DAT_DIR"),
+       PINCTRL_PIN(26, "EMMC0_STROBE"),
+       PINCTRL_PIN(27, "SDIO_PWR_DOWN_B"),
+       PINCTRL_PIN(28, "SDCARD_PWR_DOWN_B"),
+       PINCTRL_PIN(29, "SDCARD_LVL_SEL"),
+       PINCTRL_PIN(30, "SDCARD_LVL_WP"),
+};
+
+static const unsigned bxt_southwest_emmc0_pins[] = {
+       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 26,
+};
+static const unsigned bxt_southwest_sdio_pins[] = {
+       10, 11, 12, 13, 14, 15, 27,
+};
+static const unsigned bxt_southwest_sdcard_pins[] = {
+       16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 28, 29, 30,
+};
+
+static const struct intel_pingroup bxt_southwest_groups[] = {
+       PIN_GROUP("emmc0_grp", bxt_southwest_emmc0_pins, 1),
+       PIN_GROUP("sdio_grp", bxt_southwest_sdio_pins, 1),
+       PIN_GROUP("sdcard_grp", bxt_southwest_sdcard_pins, 1),
+};
+
+static const char * const bxt_southwest_emmc0_groups[] = { "emmc0_grp" };
+static const char * const bxt_southwest_sdio_groups[] = { "sdio_grp" };
+static const char * const bxt_southwest_sdcard_groups[] = { "sdcard_grp" };
+
+static const struct intel_function bxt_southwest_functions[] = {
+       FUNCTION("emmc0", bxt_southwest_emmc0_groups),
+       FUNCTION("sdio", bxt_southwest_sdio_groups),
+       FUNCTION("sdcard", bxt_southwest_sdcard_groups),
+};
+
+static const struct intel_community bxt_southwest_communities[] = {
+       BXT_COMMUNITY(0, 30),
+};
+
+static const struct intel_pinctrl_soc_data bxt_southwest_soc_data = {
+       .uid = "4",
+       .pins = bxt_southwest_pins,
+       .npins = ARRAY_SIZE(bxt_southwest_pins),
+       .groups = bxt_southwest_groups,
+       .ngroups = ARRAY_SIZE(bxt_southwest_groups),
+       .functions = bxt_southwest_functions,
+       .nfunctions = ARRAY_SIZE(bxt_southwest_functions),
+       .communities = bxt_southwest_communities,
+       .ncommunities = ARRAY_SIZE(bxt_southwest_communities),
+};
+
+static const struct pinctrl_pin_desc bxt_south_pins[] = {
+       PINCTRL_PIN(0, "HV_DDI0_DDC_SDA"),
+       PINCTRL_PIN(1, "HV_DDI0_DDC_SCL"),
+       PINCTRL_PIN(2, "HV_DDI1_DDC_SDA"),
+       PINCTRL_PIN(3, "HV_DDI1_DDC_SCL"),
+       PINCTRL_PIN(4, "DBI_SDA"),
+       PINCTRL_PIN(5, "DBI_SCL"),
+       PINCTRL_PIN(6, "PANEL0_VDDEN"),
+       PINCTRL_PIN(7, "PANEL0_BKLTEN"),
+       PINCTRL_PIN(8, "PANEL0_BKLTCTL"),
+       PINCTRL_PIN(9, "PANEL1_VDDEN"),
+       PINCTRL_PIN(10, "PANEL1_BKLTEN"),
+       PINCTRL_PIN(11, "PANEL1_BKLTCTL"),
+       PINCTRL_PIN(12, "DBI_CSX"),
+       PINCTRL_PIN(13, "DBI_RESX"),
+       PINCTRL_PIN(14, "GP_INTD_DSI_TE1"),
+       PINCTRL_PIN(15, "GP_INTD_DSI_TE2"),
+       PINCTRL_PIN(16, "USB_OC0_B"),
+       PINCTRL_PIN(17, "USB_OC1_B"),
+       PINCTRL_PIN(18, "MEX_WAKE0_B"),
+       PINCTRL_PIN(19, "MEX_WAKE1_B"),
+};
+
+static const struct intel_community bxt_south_communities[] = {
+       BXT_COMMUNITY(0, 19),
+};
+
+static const struct intel_pinctrl_soc_data bxt_south_soc_data = {
+       .uid = "5",
+       .pins = bxt_south_pins,
+       .npins = ARRAY_SIZE(bxt_south_pins),
+       .communities = bxt_south_communities,
+       .ncommunities = ARRAY_SIZE(bxt_south_communities),
+};
+
+static const struct intel_pinctrl_soc_data *bxt_pinctrl_soc_data[] = {
+       &bxt_north_soc_data,
+       &bxt_northwest_soc_data,
+       &bxt_west_soc_data,
+       &bxt_southwest_soc_data,
+       &bxt_south_soc_data,
+       NULL,
+};
+
+/* APL */
+static const struct pinctrl_pin_desc apl_north_pins[] = {
+       PINCTRL_PIN(0, "GPIO_0"),
+       PINCTRL_PIN(1, "GPIO_1"),
+       PINCTRL_PIN(2, "GPIO_2"),
+       PINCTRL_PIN(3, "GPIO_3"),
+       PINCTRL_PIN(4, "GPIO_4"),
+       PINCTRL_PIN(5, "GPIO_5"),
+       PINCTRL_PIN(6, "GPIO_6"),
+       PINCTRL_PIN(7, "GPIO_7"),
+       PINCTRL_PIN(8, "GPIO_8"),
+       PINCTRL_PIN(9, "GPIO_9"),
+       PINCTRL_PIN(10, "GPIO_10"),
+       PINCTRL_PIN(11, "GPIO_11"),
+       PINCTRL_PIN(12, "GPIO_12"),
+       PINCTRL_PIN(13, "GPIO_13"),
+       PINCTRL_PIN(14, "GPIO_14"),
+       PINCTRL_PIN(15, "GPIO_15"),
+       PINCTRL_PIN(16, "GPIO_16"),
+       PINCTRL_PIN(17, "GPIO_17"),
+       PINCTRL_PIN(18, "GPIO_18"),
+       PINCTRL_PIN(19, "GPIO_19"),
+       PINCTRL_PIN(20, "GPIO_20"),
+       PINCTRL_PIN(21, "GPIO_21"),
+       PINCTRL_PIN(22, "GPIO_22"),
+       PINCTRL_PIN(23, "GPIO_23"),
+       PINCTRL_PIN(24, "GPIO_24"),
+       PINCTRL_PIN(25, "GPIO_25"),
+       PINCTRL_PIN(26, "GPIO_26"),
+       PINCTRL_PIN(27, "GPIO_27"),
+       PINCTRL_PIN(28, "GPIO_28"),
+       PINCTRL_PIN(29, "GPIO_29"),
+       PINCTRL_PIN(30, "GPIO_30"),
+       PINCTRL_PIN(31, "GPIO_31"),
+       PINCTRL_PIN(32, "GPIO_32"),
+       PINCTRL_PIN(33, "GPIO_33"),
+       PINCTRL_PIN(34, "PWM0"),
+       PINCTRL_PIN(35, "PWM1"),
+       PINCTRL_PIN(36, "PWM2"),
+       PINCTRL_PIN(37, "PWM3"),
+       PINCTRL_PIN(38, "LPSS_UART0_RXD"),
+       PINCTRL_PIN(39, "LPSS_UART0_TXD"),
+       PINCTRL_PIN(40, "LPSS_UART0_RTS_B"),
+       PINCTRL_PIN(41, "LPSS_UART0_CTS_B"),
+       PINCTRL_PIN(42, "LPSS_UART1_RXD"),
+       PINCTRL_PIN(43, "LPSS_UART1_TXD"),
+       PINCTRL_PIN(44, "LPSS_UART1_RTS_B"),
+       PINCTRL_PIN(45, "LPSS_UART1_CTS_B"),
+       PINCTRL_PIN(46, "LPSS_UART2_RXD"),
+       PINCTRL_PIN(47, "LPSS_UART2_TXD"),
+       PINCTRL_PIN(48, "LPSS_UART2_RTS_B"),
+       PINCTRL_PIN(49, "LPSS_UART2_CTS_B"),
+       PINCTRL_PIN(50, "GP_CAMERASB00"),
+       PINCTRL_PIN(51, "GP_CAMERASB01"),
+       PINCTRL_PIN(52, "GP_CAMERASB02"),
+       PINCTRL_PIN(53, "GP_CAMERASB03"),
+       PINCTRL_PIN(54, "GP_CAMERASB04"),
+       PINCTRL_PIN(55, "GP_CAMERASB05"),
+       PINCTRL_PIN(56, "GP_CAMERASB06"),
+       PINCTRL_PIN(57, "GP_CAMERASB07"),
+       PINCTRL_PIN(58, "GP_CAMERASB08"),
+       PINCTRL_PIN(59, "GP_CAMERASB09"),
+       PINCTRL_PIN(60, "GP_CAMERASB10"),
+       PINCTRL_PIN(61, "GP_CAMERASB11"),
+       PINCTRL_PIN(62, "TCK"),
+       PINCTRL_PIN(63, "TRST_B"),
+       PINCTRL_PIN(64, "TMS"),
+       PINCTRL_PIN(65, "TDI"),
+       PINCTRL_PIN(66, "CX_PMODE"),
+       PINCTRL_PIN(67, "CX_PREQ_B"),
+       PINCTRL_PIN(68, "JTAGX"),
+       PINCTRL_PIN(69, "CX_PRDY_B"),
+       PINCTRL_PIN(70, "TDO"),
+       PINCTRL_PIN(71, "CNV_BRI_DT"),
+       PINCTRL_PIN(72, "CNV_BRI_RSP"),
+       PINCTRL_PIN(73, "CNV_RGI_DT"),
+       PINCTRL_PIN(74, "CNV_RGI_RSP"),
+       PINCTRL_PIN(75, "SVID0_ALERT_B"),
+       PINCTRL_PIN(76, "SVID0_DATA"),
+       PINCTRL_PIN(77, "SVID0_CLK"),
+};
+
+static const unsigned apl_north_pwm0_pins[] = { 34 };
+static const unsigned apl_north_pwm1_pins[] = { 35 };
+static const unsigned apl_north_pwm2_pins[] = { 36 };
+static const unsigned apl_north_pwm3_pins[] = { 37 };
+static const unsigned apl_north_uart0_pins[] = { 38, 39, 40, 41 };
+static const unsigned apl_north_uart1_pins[] = { 42, 43, 44, 45 };
+static const unsigned apl_north_uart2_pins[] = { 46, 47, 48, 49 };
+
+static const struct intel_pingroup apl_north_groups[] = {
+       PIN_GROUP("pwm0_grp", apl_north_pwm0_pins, 1),
+       PIN_GROUP("pwm1_grp", apl_north_pwm1_pins, 1),
+       PIN_GROUP("pwm2_grp", apl_north_pwm2_pins, 1),
+       PIN_GROUP("pwm3_grp", apl_north_pwm3_pins, 1),
+       PIN_GROUP("uart0_grp", apl_north_uart0_pins, 1),
+       PIN_GROUP("uart1_grp", apl_north_uart1_pins, 1),
+       PIN_GROUP("uart2_grp", apl_north_uart2_pins, 1),
+};
+
+static const char * const apl_north_pwm0_groups[] = { "pwm0_grp" };
+static const char * const apl_north_pwm1_groups[] = { "pwm1_grp" };
+static const char * const apl_north_pwm2_groups[] = { "pwm2_grp" };
+static const char * const apl_north_pwm3_groups[] = { "pwm3_grp" };
+static const char * const apl_north_uart0_groups[] = { "uart0_grp" };
+static const char * const apl_north_uart1_groups[] = { "uart1_grp" };
+static const char * const apl_north_uart2_groups[] = { "uart2_grp" };
+
+static const struct intel_function apl_north_functions[] = {
+       FUNCTION("pwm0", apl_north_pwm0_groups),
+       FUNCTION("pwm1", apl_north_pwm1_groups),
+       FUNCTION("pwm2", apl_north_pwm2_groups),
+       FUNCTION("pwm3", apl_north_pwm3_groups),
+       FUNCTION("uart0", apl_north_uart0_groups),
+       FUNCTION("uart1", apl_north_uart1_groups),
+       FUNCTION("uart2", apl_north_uart2_groups),
+};
+
+static const struct intel_community apl_north_communities[] = {
+       BXT_COMMUNITY(0, 77),
+};
+
+static const struct intel_pinctrl_soc_data apl_north_soc_data = {
+       .uid = "1",
+       .pins = apl_north_pins,
+       .npins = ARRAY_SIZE(apl_north_pins),
+       .groups = apl_north_groups,
+       .ngroups = ARRAY_SIZE(apl_north_groups),
+       .functions = apl_north_functions,
+       .nfunctions = ARRAY_SIZE(apl_north_functions),
+       .communities = apl_north_communities,
+       .ncommunities = ARRAY_SIZE(apl_north_communities),
+};
+
+static const struct pinctrl_pin_desc apl_northwest_pins[] = {
+       PINCTRL_PIN(0, "HV_DDI0_DDC_SDA"),
+       PINCTRL_PIN(1, "HV_DDI0_DDC_SCL"),
+       PINCTRL_PIN(2, "HV_DDI1_DDC_SDA"),
+       PINCTRL_PIN(3, "HV_DDI1_DDC_SCL"),
+       PINCTRL_PIN(4, "DBI_SDA"),
+       PINCTRL_PIN(5, "DBI_SCL"),
+       PINCTRL_PIN(6, "PANEL0_VDDEN"),
+       PINCTRL_PIN(7, "PANEL0_BKLTEN"),
+       PINCTRL_PIN(8, "PANEL0_BKLTCTL"),
+       PINCTRL_PIN(9, "PANEL1_VDDEN"),
+       PINCTRL_PIN(10, "PANEL1_BKLTEN"),
+       PINCTRL_PIN(11, "PANEL1_BKLTCTL"),
+       PINCTRL_PIN(12, "DBI_CSX"),
+       PINCTRL_PIN(13, "DBI_RESX"),
+       PINCTRL_PIN(14, "GP_INTD_DSI_TE1"),
+       PINCTRL_PIN(15, "GP_INTD_DSI_TE2"),
+       PINCTRL_PIN(16, "USB_OC0_B"),
+       PINCTRL_PIN(17, "USB_OC1_B"),
+       PINCTRL_PIN(18, "PMC_SPI_FS0"),
+       PINCTRL_PIN(19, "PMC_SPI_FS1"),
+       PINCTRL_PIN(20, "PMC_SPI_FS2"),
+       PINCTRL_PIN(21, "PMC_SPI_RXD"),
+       PINCTRL_PIN(22, "PMC_SPI_TXD"),
+       PINCTRL_PIN(23, "PMC_SPI_CLK"),
+       PINCTRL_PIN(24, "PMIC_PWRGOOD"),
+       PINCTRL_PIN(25, "PMIC_RESET_B"),
+       PINCTRL_PIN(26, "PMIC_SDWN_B"),
+       PINCTRL_PIN(27, "PMIC_BCUDISW2"),
+       PINCTRL_PIN(28, "PMIC_BCUDISCRIT"),
+       PINCTRL_PIN(29, "PMIC_THERMTRIP_B"),
+       PINCTRL_PIN(30, "PMIC_STDBY"),
+       PINCTRL_PIN(31, "PROCHOT_B"),
+       PINCTRL_PIN(32, "PMIC_I2C_SCL"),
+       PINCTRL_PIN(33, "PMIC_I2C_SDA"),
+       PINCTRL_PIN(34, "AVS_I2S1_MCLK"),
+       PINCTRL_PIN(35, "AVS_I2S1_BCLK"),
+       PINCTRL_PIN(36, "AVS_I2S1_WS_SYNC"),
+       PINCTRL_PIN(37, "AVS_I2S1_SDI"),
+       PINCTRL_PIN(38, "AVS_I2S1_SDO"),
+       PINCTRL_PIN(39, "AVS_M_CLK_A1"),
+       PINCTRL_PIN(40, "AVS_M_CLK_B1"),
+       PINCTRL_PIN(41, "AVS_M_DATA_1"),
+       PINCTRL_PIN(42, "AVS_M_CLK_AB2"),
+       PINCTRL_PIN(43, "AVS_M_DATA_2"),
+       PINCTRL_PIN(44, "AVS_I2S2_MCLK"),
+       PINCTRL_PIN(45, "AVS_I2S2_BCLK"),
+       PINCTRL_PIN(46, "AVS_I2S2_WS_SYNC"),
+       PINCTRL_PIN(47, "AVS_I2S2_SDI"),
+       PINCTRL_PIN(48, "AVS_I2S2_SDO"),
+       PINCTRL_PIN(49, "AVS_I2S3_BCLK"),
+       PINCTRL_PIN(50, "AVS_I2S3_WS_SYNC"),
+       PINCTRL_PIN(51, "AVS_I2S3_SDI"),
+       PINCTRL_PIN(52, "AVS_I2S3_SDO"),
+       PINCTRL_PIN(53, "FST_SPI_CS0_B"),
+       PINCTRL_PIN(54, "FST_SPI_CS1_B"),
+       PINCTRL_PIN(55, "FST_SPI_MOSI_IO0"),
+       PINCTRL_PIN(56, "FST_SPI_MISO_IO1"),
+       PINCTRL_PIN(57, "FST_SPI_IO2"),
+       PINCTRL_PIN(58, "FST_SPI_IO3"),
+       PINCTRL_PIN(59, "FST_SPI_CLK"),
+       PINCTRL_PIN(60, "FST_SPI_CLK_FB"),
+       PINCTRL_PIN(61, "GP_SSP_0_CLK"),
+       PINCTRL_PIN(62, "GP_SSP_0_FS0"),
+       PINCTRL_PIN(63, "GP_SSP_0_FS1"),
+       PINCTRL_PIN(64, "GP_SSP_0_RXD"),
+       PINCTRL_PIN(65, "GP_SSP_0_TXD"),
+       PINCTRL_PIN(66, "GP_SSP_1_CLK"),
+       PINCTRL_PIN(67, "GP_SSP_1_FS0"),
+       PINCTRL_PIN(68, "GP_SSP_1_FS1"),
+       PINCTRL_PIN(69, "GP_SSP_1_RXD"),
+       PINCTRL_PIN(70, "GP_SSP_1_TXD"),
+       PINCTRL_PIN(71, "GP_SSP_2_CLK"),
+       PINCTRL_PIN(72, "GP_SSP_2_FS0"),
+       PINCTRL_PIN(73, "GP_SSP_2_FS1"),
+       PINCTRL_PIN(74, "GP_SSP_2_FS2"),
+       PINCTRL_PIN(75, "GP_SSP_2_RXD"),
+       PINCTRL_PIN(76, "GP_SSP_2_TXD"),
+};
+
+static const unsigned apl_northwest_ssp0_pins[] = { 61, 62, 63, 64, 65 };
+static const unsigned apl_northwest_ssp1_pins[] = { 66, 67, 68, 69, 70 };
+static const unsigned apl_northwest_ssp2_pins[] = { 71, 72, 73, 74, 75, 76 };
+static const unsigned apl_northwest_uart3_pins[] = { 67, 68, 69, 70 };
+
+static const struct intel_pingroup apl_northwest_groups[] = {
+       PIN_GROUP("ssp0_grp", apl_northwest_ssp0_pins, 1),
+       PIN_GROUP("ssp1_grp", apl_northwest_ssp1_pins, 1),
+       PIN_GROUP("ssp2_grp", apl_northwest_ssp2_pins, 1),
+       PIN_GROUP("uart3_grp", apl_northwest_uart3_pins, 2),
+};
+
+static const char * const apl_northwest_ssp0_groups[] = { "ssp0_grp" };
+static const char * const apl_northwest_ssp1_groups[] = { "ssp1_grp" };
+static const char * const apl_northwest_ssp2_groups[] = { "ssp2_grp" };
+static const char * const apl_northwest_uart3_groups[] = { "uart3_grp" };
+
+static const struct intel_function apl_northwest_functions[] = {
+       FUNCTION("ssp0", apl_northwest_ssp0_groups),
+       FUNCTION("ssp1", apl_northwest_ssp1_groups),
+       FUNCTION("ssp2", apl_northwest_ssp2_groups),
+       FUNCTION("uart3", apl_northwest_uart3_groups),
+};
+
+static const struct intel_community apl_northwest_communities[] = {
+       BXT_COMMUNITY(0, 76),
+};
+
+static const struct intel_pinctrl_soc_data apl_northwest_soc_data = {
+       .uid = "2",
+       .pins = apl_northwest_pins,
+       .npins = ARRAY_SIZE(apl_northwest_pins),
+       .groups = apl_northwest_groups,
+       .ngroups = ARRAY_SIZE(apl_northwest_groups),
+       .functions = apl_northwest_functions,
+       .nfunctions = ARRAY_SIZE(apl_northwest_functions),
+       .communities = apl_northwest_communities,
+       .ncommunities = ARRAY_SIZE(apl_northwest_communities),
+};
+
+static const struct pinctrl_pin_desc apl_west_pins[] = {
+       PINCTRL_PIN(0, "LPSS_I2C0_SDA"),
+       PINCTRL_PIN(1, "LPSS_I2C0_SCL"),
+       PINCTRL_PIN(2, "LPSS_I2C1_SDA"),
+       PINCTRL_PIN(3, "LPSS_I2C1_SCL"),
+       PINCTRL_PIN(4, "LPSS_I2C2_SDA"),
+       PINCTRL_PIN(5, "LPSS_I2C2_SCL"),
+       PINCTRL_PIN(6, "LPSS_I2C3_SDA"),
+       PINCTRL_PIN(7, "LPSS_I2C3_SCL"),
+       PINCTRL_PIN(8, "LPSS_I2C4_SDA"),
+       PINCTRL_PIN(9, "LPSS_I2C4_SCL"),
+       PINCTRL_PIN(10, "LPSS_I2C5_SDA"),
+       PINCTRL_PIN(11, "LPSS_I2C5_SCL"),
+       PINCTRL_PIN(12, "LPSS_I2C6_SDA"),
+       PINCTRL_PIN(13, "LPSS_I2C6_SCL"),
+       PINCTRL_PIN(14, "LPSS_I2C7_SDA"),
+       PINCTRL_PIN(15, "LPSS_I2C7_SCL"),
+       PINCTRL_PIN(16, "ISH_GPIO_0"),
+       PINCTRL_PIN(17, "ISH_GPIO_1"),
+       PINCTRL_PIN(18, "ISH_GPIO_2"),
+       PINCTRL_PIN(19, "ISH_GPIO_3"),
+       PINCTRL_PIN(20, "ISH_GPIO_4"),
+       PINCTRL_PIN(21, "ISH_GPIO_5"),
+       PINCTRL_PIN(22, "ISH_GPIO_6"),
+       PINCTRL_PIN(23, "ISH_GPIO_7"),
+       PINCTRL_PIN(24, "ISH_GPIO_8"),
+       PINCTRL_PIN(25, "ISH_GPIO_9"),
+       PINCTRL_PIN(26, "PCIE_CLKREQ0_B"),
+       PINCTRL_PIN(27, "PCIE_CLKREQ1_B"),
+       PINCTRL_PIN(28, "PCIE_CLKREQ2_B"),
+       PINCTRL_PIN(29, "PCIE_CLKREQ3_B"),
+       PINCTRL_PIN(30, "OSC_CLK_OUT_0"),
+       PINCTRL_PIN(31, "OSC_CLK_OUT_1"),
+       PINCTRL_PIN(32, "OSC_CLK_OUT_2"),
+       PINCTRL_PIN(33, "OSC_CLK_OUT_3"),
+       PINCTRL_PIN(34, "OSC_CLK_OUT_4"),
+       PINCTRL_PIN(35, "PMU_AC_PRESENT"),
+       PINCTRL_PIN(36, "PMU_BATLOW_B"),
+       PINCTRL_PIN(37, "PMU_PLTRST_B"),
+       PINCTRL_PIN(38, "PMU_PWRBTN_B"),
+       PINCTRL_PIN(39, "PMU_RESETBUTTON_B"),
+       PINCTRL_PIN(40, "PMU_SLP_S0_B"),
+       PINCTRL_PIN(41, "PMU_SLP_S3_B"),
+       PINCTRL_PIN(42, "PMU_SLP_S4_B"),
+       PINCTRL_PIN(43, "PMU_SUSCLK"),
+       PINCTRL_PIN(44, "PMU_WAKE_B"),
+       PINCTRL_PIN(45, "SUS_STAT_B"),
+       PINCTRL_PIN(46, "SUSPWRDNACK"),
+};
+
+static const unsigned apl_west_i2c0_pins[] = { 0, 1 };
+static const unsigned apl_west_i2c1_pins[] = { 2, 3 };
+static const unsigned apl_west_i2c2_pins[] = { 4, 5 };
+static const unsigned apl_west_i2c3_pins[] = { 6, 7 };
+static const unsigned apl_west_i2c4_pins[] = { 8, 9 };
+static const unsigned apl_west_i2c5_pins[] = { 10, 11 };
+static const unsigned apl_west_i2c6_pins[] = { 12, 13 };
+static const unsigned apl_west_i2c7_pins[] = { 14, 15 };
+static const unsigned apl_west_uart2_pins[] = { 20, 21, 22, 34 };
+
+static const struct intel_pingroup apl_west_groups[] = {
+       PIN_GROUP("i2c0_grp", apl_west_i2c0_pins, 1),
+       PIN_GROUP("i2c1_grp", apl_west_i2c1_pins, 1),
+       PIN_GROUP("i2c2_grp", apl_west_i2c2_pins, 1),
+       PIN_GROUP("i2c3_grp", apl_west_i2c3_pins, 1),
+       PIN_GROUP("i2c4_grp", apl_west_i2c4_pins, 1),
+       PIN_GROUP("i2c5_grp", apl_west_i2c5_pins, 1),
+       PIN_GROUP("i2c6_grp", apl_west_i2c6_pins, 1),
+       PIN_GROUP("i2c7_grp", apl_west_i2c7_pins, 1),
+       PIN_GROUP("uart2_grp", apl_west_uart2_pins, 3),
+};
+
+static const char * const apl_west_i2c0_groups[] = { "i2c0_grp" };
+static const char * const apl_west_i2c1_groups[] = { "i2c1_grp" };
+static const char * const apl_west_i2c2_groups[] = { "i2c2_grp" };
+static const char * const apl_west_i2c3_groups[] = { "i2c3_grp" };
+static const char * const apl_west_i2c4_groups[] = { "i2c4_grp" };
+static const char * const apl_west_i2c5_groups[] = { "i2c5_grp" };
+static const char * const apl_west_i2c6_groups[] = { "i2c6_grp" };
+static const char * const apl_west_i2c7_groups[] = { "i2c7_grp" };
+static const char * const apl_west_uart2_groups[] = { "uart2_grp" };
+
+static const struct intel_function apl_west_functions[] = {
+       FUNCTION("i2c0", apl_west_i2c0_groups),
+       FUNCTION("i2c1", apl_west_i2c1_groups),
+       FUNCTION("i2c2", apl_west_i2c2_groups),
+       FUNCTION("i2c3", apl_west_i2c3_groups),
+       FUNCTION("i2c4", apl_west_i2c4_groups),
+       FUNCTION("i2c5", apl_west_i2c5_groups),
+       FUNCTION("i2c6", apl_west_i2c6_groups),
+       FUNCTION("i2c7", apl_west_i2c7_groups),
+       FUNCTION("uart2", apl_west_uart2_groups),
+};
+
+static const struct intel_community apl_west_communities[] = {
+       BXT_COMMUNITY(0, 46),
+};
+
+static const struct intel_pinctrl_soc_data apl_west_soc_data = {
+       .uid = "3",
+       .pins = apl_west_pins,
+       .npins = ARRAY_SIZE(apl_west_pins),
+       .groups = apl_west_groups,
+       .ngroups = ARRAY_SIZE(apl_west_groups),
+       .functions = apl_west_functions,
+       .nfunctions = ARRAY_SIZE(apl_west_functions),
+       .communities = apl_west_communities,
+       .ncommunities = ARRAY_SIZE(apl_west_communities),
+};
+
+static const struct pinctrl_pin_desc apl_southwest_pins[] = {
+       PINCTRL_PIN(0, "PCIE_WAKE0_B"),
+       PINCTRL_PIN(1, "PCIE_WAKE1_B"),
+       PINCTRL_PIN(2, "PCIE_WAKE2_B"),
+       PINCTRL_PIN(3, "PCIE_WAKE3_B"),
+       PINCTRL_PIN(4, "EMMC0_CLK"),
+       PINCTRL_PIN(5, "EMMC0_D0"),
+       PINCTRL_PIN(6, "EMMC0_D1"),
+       PINCTRL_PIN(7, "EMMC0_D2"),
+       PINCTRL_PIN(8, "EMMC0_D3"),
+       PINCTRL_PIN(9, "EMMC0_D4"),
+       PINCTRL_PIN(10, "EMMC0_D5"),
+       PINCTRL_PIN(11, "EMMC0_D6"),
+       PINCTRL_PIN(12, "EMMC0_D7"),
+       PINCTRL_PIN(13, "EMMC0_CMD"),
+       PINCTRL_PIN(14, "SDIO_CLK"),
+       PINCTRL_PIN(15, "SDIO_D0"),
+       PINCTRL_PIN(16, "SDIO_D1"),
+       PINCTRL_PIN(17, "SDIO_D2"),
+       PINCTRL_PIN(18, "SDIO_D3"),
+       PINCTRL_PIN(19, "SDIO_CMD"),
+       PINCTRL_PIN(20, "SDCARD_CLK"),
+       PINCTRL_PIN(21, "SDCARD_CLK_FB"),
+       PINCTRL_PIN(22, "SDCARD_D0"),
+       PINCTRL_PIN(23, "SDCARD_D1"),
+       PINCTRL_PIN(24, "SDCARD_D2"),
+       PINCTRL_PIN(25, "SDCARD_D3"),
+       PINCTRL_PIN(26, "SDCARD_CD_B"),
+       PINCTRL_PIN(27, "SDCARD_CMD"),
+       PINCTRL_PIN(28, "SDCARD_LVL_WP"),
+       PINCTRL_PIN(29, "EMMC0_STROBE"),
+       PINCTRL_PIN(30, "SDIO_PWR_DOWN_B"),
+       PINCTRL_PIN(31, "SMB_ALERTB"),
+       PINCTRL_PIN(32, "SMB_CLK"),
+       PINCTRL_PIN(33, "SMB_DATA"),
+       PINCTRL_PIN(34, "LPC_ILB_SERIRQ"),
+       PINCTRL_PIN(35, "LPC_CLKOUT0"),
+       PINCTRL_PIN(36, "LPC_CLKOUT1"),
+       PINCTRL_PIN(37, "LPC_AD0"),
+       PINCTRL_PIN(38, "LPC_AD1"),
+       PINCTRL_PIN(39, "LPC_AD2"),
+       PINCTRL_PIN(40, "LPC_AD3"),
+       PINCTRL_PIN(41, "LPC_CLKRUNB"),
+       PINCTRL_PIN(42, "LPC_FRAMEB"),
+};
+
+static const unsigned apl_southwest_emmc0_pins[] = {
+       4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 29,
+};
+static const unsigned apl_southwest_sdio_pins[] = {
+       14, 15, 16, 17, 18, 19, 30,
+};
+static const unsigned apl_southwest_sdcard_pins[] = {
+       20, 21, 22, 23, 24, 25, 26, 27, 28,
+};
+static const unsigned apl_southwest_i2c7_pins[] = { 32, 33 };
+
+static const struct intel_pingroup apl_southwest_groups[] = {
+       PIN_GROUP("emmc0_grp", apl_southwest_emmc0_pins, 1),
+       PIN_GROUP("sdio_grp", apl_southwest_sdio_pins, 1),
+       PIN_GROUP("sdcard_grp", apl_southwest_sdcard_pins, 1),
+       PIN_GROUP("i2c7_grp", apl_southwest_i2c7_pins, 2),
+};
+
+static const char * const apl_southwest_emmc0_groups[] = { "emmc0_grp" };
+static const char * const apl_southwest_sdio_groups[] = { "sdio_grp" };
+static const char * const apl_southwest_sdcard_groups[] = { "sdcard_grp" };
+static const char * const apl_southwest_i2c7_groups[] = { "i2c7_grp" };
+
+static const struct intel_function apl_southwest_functions[] = {
+       FUNCTION("emmc0", apl_southwest_emmc0_groups),
+       FUNCTION("sdio", apl_southwest_sdio_groups),
+       FUNCTION("sdcard", apl_southwest_sdcard_groups),
+       FUNCTION("i2c7", apl_southwest_i2c7_groups),
+};
+
+static const struct intel_community apl_southwest_communities[] = {
+       BXT_COMMUNITY(0, 42),
+};
+
+static const struct intel_pinctrl_soc_data apl_southwest_soc_data = {
+       .uid = "4",
+       .pins = apl_southwest_pins,
+       .npins = ARRAY_SIZE(apl_southwest_pins),
+       .groups = apl_southwest_groups,
+       .ngroups = ARRAY_SIZE(apl_southwest_groups),
+       .functions = apl_southwest_functions,
+       .nfunctions = ARRAY_SIZE(apl_southwest_functions),
+       .communities = apl_southwest_communities,
+       .ncommunities = ARRAY_SIZE(apl_southwest_communities),
+};
+
+static const struct intel_pinctrl_soc_data *apl_pinctrl_soc_data[] = {
+       &apl_north_soc_data,
+       &apl_northwest_soc_data,
+       &apl_west_soc_data,
+       &apl_southwest_soc_data,
+       NULL,
+};
+
+static const struct acpi_device_id bxt_pinctrl_acpi_match[] = {
+       { "INT3452", (kernel_ulong_t)apl_pinctrl_soc_data },
+       { "INT34D1", (kernel_ulong_t)bxt_pinctrl_soc_data },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, bxt_pinctrl_acpi_match);
+
+static int bxt_pinctrl_probe(struct platform_device *pdev)
+{
+       const struct intel_pinctrl_soc_data *soc_data = NULL;
+       const struct intel_pinctrl_soc_data **soc_table;
+       const struct acpi_device_id *id;
+       struct acpi_device *adev;
+       int i;
+
+       adev = ACPI_COMPANION(&pdev->dev);
+       if (!adev)
+               return -ENODEV;
+
+       id = acpi_match_device(bxt_pinctrl_acpi_match, &pdev->dev);
+       if (!id)
+               return -ENODEV;
+
+       soc_table = (const struct intel_pinctrl_soc_data **)id->driver_data;
+
+       for (i = 0; soc_table[i]; i++) {
+               if (!strcmp(adev->pnp.unique_id, soc_table[i]->uid)) {
+                       soc_data = soc_table[i];
+                       break;
+               }
+       }
+
+       if (!soc_data)
+               return -ENODEV;
+
+       return intel_pinctrl_probe(pdev, soc_data);
+}
+
+static const struct dev_pm_ops bxt_pinctrl_pm_ops = {
+       SET_LATE_SYSTEM_SLEEP_PM_OPS(intel_pinctrl_suspend,
+                                    intel_pinctrl_resume)
+};
+
+static struct platform_driver bxt_pinctrl_driver = {
+       .probe = bxt_pinctrl_probe,
+       .remove = intel_pinctrl_remove,
+       .driver = {
+               .name = "broxton-pinctrl",
+               .acpi_match_table = bxt_pinctrl_acpi_match,
+               .pm = &bxt_pinctrl_pm_ops,
+       },
+};
+
+static int __init bxt_pinctrl_init(void)
+{
+       return platform_driver_register(&bxt_pinctrl_driver);
+}
+subsys_initcall(bxt_pinctrl_init);
+
+static void __exit bxt_pinctrl_exit(void)
+{
+       platform_driver_unregister(&bxt_pinctrl_driver);
+}
+module_exit(bxt_pinctrl_exit);
+
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_DESCRIPTION("Intel Broxton SoC pinctrl/GPIO driver");
+MODULE_LICENSE("GPL v2");
index 270c127e03ea72f759d74758d3d81b00d60e52bb..84936bae6e5ee07f741477cd185509f86bb816a7 100644 (file)
@@ -1149,16 +1149,6 @@ static struct pinctrl_desc chv_pinctrl_desc = {
        .owner = THIS_MODULE,
 };
 
-static int chv_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void chv_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static unsigned chv_gpio_offset_to_pin(struct chv_pinctrl *pctrl,
                                       unsigned offset)
 {
@@ -1238,8 +1228,8 @@ static int chv_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
 
 static const struct gpio_chip chv_gpio_chip = {
        .owner = THIS_MODULE,
-       .request = chv_gpio_request,
-       .free = chv_gpio_free,
+       .request = gpiochip_generic_request,
+       .free = gpiochip_generic_free,
        .get_direction = chv_gpio_get_direction,
        .direction_input = chv_gpio_direction_input,
        .direction_output = chv_gpio_direction_output,
index 54848b8decef7735d8b009c6431a970f94204488..392e28d3f48d0bee1a4087154e904c7dfdc0faa7 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/acpi.h>
 #include <linux/gpio.h>
 #include <linux/gpio/driver.h>
@@ -159,8 +160,7 @@ static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned pin)
        return !(readl(padown) & PADOWN_MASK(padno));
 }
 
-static bool intel_pad_reserved_for_acpi(struct intel_pinctrl *pctrl,
-                                       unsigned pin)
+static bool intel_pad_acpi_mode(struct intel_pinctrl *pctrl, unsigned pin)
 {
        const struct intel_community *community;
        unsigned padno, gpp, offset;
@@ -216,7 +216,6 @@ static bool intel_pad_locked(struct intel_pinctrl *pctrl, unsigned pin)
 static bool intel_pad_usable(struct intel_pinctrl *pctrl, unsigned pin)
 {
        return intel_pad_owned_by_host(pctrl, pin) &&
-               !intel_pad_reserved_for_acpi(pctrl, pin) &&
                !intel_pad_locked(pctrl, pin);
 }
 
@@ -269,7 +268,7 @@ static void intel_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
        seq_printf(s, "0x%08x 0x%08x", cfg0, cfg1);
 
        locked = intel_pad_locked(pctrl, pin);
-       acpi = intel_pad_reserved_for_acpi(pctrl, pin);
+       acpi = intel_pad_acpi_mode(pctrl, pin);
 
        if (locked || acpi) {
                seq_puts(s, " [");
@@ -597,16 +596,6 @@ static const struct pinctrl_desc intel_pinctrl_desc = {
        .owner = THIS_MODULE,
 };
 
-static int intel_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void intel_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static int intel_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
        struct intel_pinctrl *pctrl = gpiochip_to_pinctrl(chip);
@@ -654,8 +643,8 @@ static int intel_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
 
 static const struct gpio_chip intel_gpio_chip = {
        .owner = THIS_MODULE,
-       .request = intel_gpio_request,
-       .free = intel_gpio_free,
+       .request = gpiochip_generic_request,
+       .free = gpiochip_generic_free,
        .direction_input = intel_gpio_direction_input,
        .direction_output = intel_gpio_direction_output,
        .get = intel_gpio_get,
@@ -736,6 +725,16 @@ static int intel_gpio_irq_type(struct irq_data *d, unsigned type)
        if (!reg)
                return -EINVAL;
 
+       /*
+        * If the pin is in ACPI mode it is still usable as a GPIO but it
+        * cannot be used as IRQ because GPI_IS status bit will not be
+        * updated by the host controller hardware.
+        */
+       if (intel_pad_acpi_mode(pctrl, pin)) {
+               dev_warn(pctrl->dev, "pin %u cannot be used as IRQ\n", pin);
+               return -EPERM;
+       }
+
        spin_lock_irqsave(&pctrl->lock, flags);
 
        value = readl(reg);
@@ -803,9 +802,11 @@ static int intel_gpio_irq_wake(struct irq_data *d, unsigned int on)
        return 0;
 }
 
-static void intel_gpio_community_irq_handler(struct gpio_chip *gc,
+static irqreturn_t intel_gpio_community_irq_handler(struct intel_pinctrl *pctrl,
        const struct intel_community *community)
 {
+       struct gpio_chip *gc = &pctrl->chip;
+       irqreturn_t ret = IRQ_NONE;
        int gpp;
 
        for (gpp = 0; gpp < community->ngpps; gpp++) {
@@ -832,24 +833,28 @@ static void intel_gpio_community_irq_handler(struct gpio_chip *gc,
                        irq = irq_find_mapping(gc->irqdomain,
                                               community->pin_base + padno);
                        generic_handle_irq(irq);
+
+                       ret |= IRQ_HANDLED;
                }
        }
+
+       return ret;
 }
 
-static void intel_gpio_irq_handler(struct irq_desc *desc)
+static irqreturn_t intel_gpio_irq(int irq, void *data)
 {
-       struct gpio_chip *gc = irq_desc_get_handler_data(desc);
-       struct intel_pinctrl *pctrl = gpiochip_to_pinctrl(gc);
-       struct irq_chip *chip = irq_desc_get_chip(desc);
+       const struct intel_community *community;
+       struct intel_pinctrl *pctrl = data;
+       irqreturn_t ret = IRQ_NONE;
        int i;
 
-       chained_irq_enter(chip, desc);
-
        /* Need to check all communities for pending interrupts */
-       for (i = 0; i < pctrl->ncommunities; i++)
-               intel_gpio_community_irq_handler(gc, &pctrl->communities[i]);
+       for (i = 0; i < pctrl->ncommunities; i++) {
+               community = &pctrl->communities[i];
+               ret |= intel_gpio_community_irq_handler(pctrl, community);
+       }
 
-       chained_irq_exit(chip, desc);
+       return ret;
 }
 
 static struct irq_chip intel_gpio_irqchip = {
@@ -861,26 +866,6 @@ static struct irq_chip intel_gpio_irqchip = {
        .irq_set_wake = intel_gpio_irq_wake,
 };
 
-static void intel_gpio_irq_init(struct intel_pinctrl *pctrl)
-{
-       size_t i;
-
-       for (i = 0; i < pctrl->ncommunities; i++) {
-               const struct intel_community *community;
-               void __iomem *base;
-               unsigned gpp;
-
-               community = &pctrl->communities[i];
-               base = community->regs;
-
-               for (gpp = 0; gpp < community->ngpps; gpp++) {
-                       /* Mask and clear all interrupts */
-                       writel(0, base + community->ie_offset + gpp * 4);
-                       writel(0xffff, base + GPI_IS + gpp * 4);
-               }
-       }
-}
-
 static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq)
 {
        int ret;
@@ -902,21 +887,36 @@ static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq)
                                     0, 0, pctrl->soc->npins);
        if (ret) {
                dev_err(pctrl->dev, "failed to add GPIO pin range\n");
-               gpiochip_remove(&pctrl->chip);
-               return ret;
+               goto fail;
+       }
+
+       /*
+        * We need to request the interrupt here (instead of providing chip
+        * to the irq directly) because on some platforms several GPIO
+        * controllers share the same interrupt line.
+        */
+       ret = devm_request_irq(pctrl->dev, irq, intel_gpio_irq, IRQF_SHARED,
+                              dev_name(pctrl->dev), pctrl);
+       if (ret) {
+               dev_err(pctrl->dev, "failed to request interrupt\n");
+               goto fail;
        }
 
        ret = gpiochip_irqchip_add(&pctrl->chip, &intel_gpio_irqchip, 0,
                                   handle_simple_irq, IRQ_TYPE_NONE);
        if (ret) {
                dev_err(pctrl->dev, "failed to add irqchip\n");
-               gpiochip_remove(&pctrl->chip);
-               return ret;
+               goto fail;
        }
 
        gpiochip_set_chained_irqchip(&pctrl->chip, &intel_gpio_irqchip, irq,
-                                    intel_gpio_irq_handler);
+                                    NULL);
        return 0;
+
+fail:
+       gpiochip_remove(&pctrl->chip);
+
+       return ret;
 }
 
 static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl)
@@ -1087,6 +1087,26 @@ int intel_pinctrl_suspend(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(intel_pinctrl_suspend);
 
+static void intel_gpio_irq_init(struct intel_pinctrl *pctrl)
+{
+       size_t i;
+
+       for (i = 0; i < pctrl->ncommunities; i++) {
+               const struct intel_community *community;
+               void __iomem *base;
+               unsigned gpp;
+
+               community = &pctrl->communities[i];
+               base = community->regs;
+
+               for (gpp = 0; gpp < community->ngpps; gpp++) {
+                       /* Mask and clear all interrupts */
+                       writel(0, base + community->ie_offset + gpp * 4);
+                       writel(0xffff, base + GPI_IS + gpp * 4);
+               }
+       }
+}
+
 int intel_pinctrl_resume(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
index 1b22f96ba839af38d920d40f0f63309d2141a2b5..f307f1d27d646fcd3954a9d497528fffbf16dc2b 100644 (file)
@@ -723,16 +723,6 @@ static const struct pinmux_ops mtk_pmx_ops = {
        .gpio_set_direction     = mtk_pmx_gpio_set_direction,
 };
 
-static int mtk_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void mtk_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static int mtk_gpio_direction_input(struct gpio_chip *chip,
                                        unsigned offset)
 {
@@ -899,7 +889,7 @@ static int mtk_eint_flip_edge(struct mtk_pinctrl *pctl, int hwirq)
        int start_level, curr_level;
        unsigned int reg_offset;
        const struct mtk_eint_offsets *eint_offsets = &(pctl->devdata->eint_offsets);
-       u32 mask = 1 << (hwirq & 0x1f);
+       u32 mask = BIT(hwirq & 0x1f);
        u32 port = (hwirq >> 5) & eint_offsets->port_mask;
        void __iomem *reg = pctl->eint_reg_base + (port << 2);
        const struct mtk_desc_pin *pin;
@@ -1005,8 +995,8 @@ static int mtk_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
 
 static struct gpio_chip mtk_gpio_chip = {
        .owner                  = THIS_MODULE,
-       .request                = mtk_gpio_request,
-       .free                   = mtk_gpio_free,
+       .request                = gpiochip_generic_request,
+       .free                   = gpiochip_generic_free,
        .direction_input        = mtk_gpio_direction_input,
        .direction_output       = mtk_gpio_direction_output,
        .get                    = mtk_gpio_get,
@@ -1436,7 +1426,7 @@ int mtk_pctrl_init(struct platform_device *pdev,
                irq_set_chip_and_handler(virq, &mtk_pinctrl_irq_chip,
                        handle_level_irq);
                irq_set_chip_data(virq, pctl);
-       };
+       }
 
        irq_set_chained_handler_and_data(irq, mtk_eint_irq_handler, pctl);
        return 0;
index 97681fac082e71f449874a4a5a21a68b07e27fc2..b59fbb4b1fb1e5ba4325eeae1a18ffb91e7f841e 100644 (file)
@@ -654,25 +654,11 @@ static inline void abx500_gpio_dbg_show_one(struct seq_file *s,
 #define abx500_gpio_dbg_show   NULL
 #endif
 
-static int abx500_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       int gpio = chip->base + offset;
-
-       return pinctrl_request_gpio(gpio);
-}
-
-static void abx500_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       int gpio = chip->base + offset;
-
-       pinctrl_free_gpio(gpio);
-}
-
 static struct gpio_chip abx500gpio_chip = {
        .label                  = "abx500-gpio",
        .owner                  = THIS_MODULE,
-       .request                = abx500_gpio_request,
-       .free                   = abx500_gpio_free,
+       .request                = gpiochip_generic_request,
+       .free                   = gpiochip_generic_free,
        .direction_input        = abx500_gpio_direction_input,
        .get                    = abx500_gpio_get,
        .direction_output       = abx500_gpio_direction_output,
index 96cf03908e93cc1462435b1aebc6c178c4a620f5..eebfae0c9b7c84a6eacc4ad450d0765e602a011f 100644 (file)
@@ -884,24 +884,6 @@ static void nmk_gpio_latent_irq_handler(struct irq_desc *desc)
 
 /* I/O Functions */
 
-static int nmk_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       /*
-        * Map back to global GPIO space and request muxing, the direction
-        * parameter does not matter for this controller.
-        */
-       int gpio = chip->base + offset;
-
-       return pinctrl_request_gpio(gpio);
-}
-
-static void nmk_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       int gpio = chip->base + offset;
-
-       pinctrl_free_gpio(gpio);
-}
-
 static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned offset)
 {
        struct nmk_gpio_chip *nmk_chip =
@@ -1267,8 +1249,8 @@ static int nmk_gpio_probe(struct platform_device *dev)
        spin_lock_init(&nmk_chip->lock);
 
        chip = &nmk_chip->chip;
-       chip->request = nmk_gpio_request;
-       chip->free = nmk_gpio_free;
+       chip->request = gpiochip_generic_request;
+       chip->free = gpiochip_generic_free;
        chip->direction_input = nmk_gpio_make_input;
        chip->get = nmk_gpio_get_input;
        chip->direction_output = nmk_gpio_make_output;
index e63ad9fbd388a79c1afe0f1b4a2ff99e19a11574..099a3442ff4214b2ab55497a93d49774e89d1c65 100644 (file)
 
 #ifdef CONFIG_DEBUG_FS
 static const struct pin_config_item conf_items[] = {
+       PCONFDUMP(PIN_CONFIG_BIAS_BUS_HOLD, "input bias bus hold", NULL, false),
        PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL, false),
        PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL, false),
-       PCONFDUMP(PIN_CONFIG_BIAS_BUS_HOLD, "input bias bus hold", NULL, false),
-       PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL, false),
        PCONFDUMP(PIN_CONFIG_BIAS_PULL_DOWN, "input bias pull down", NULL, false),
        PCONFDUMP(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT,
                                "input bias pull to pin specific state", NULL, false),
-       PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL, false),
+       PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL, false),
        PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL, false),
        PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL, false),
+       PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL, false),
        PCONFDUMP(PIN_CONFIG_DRIVE_STRENGTH, "output drive strength", "mA", true),
+       PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "usec", true),
        PCONFDUMP(PIN_CONFIG_INPUT_ENABLE, "input enabled", NULL, false),
-       PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT_ENABLE, "input schmitt enabled", NULL, false),
        PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL, false),
-       PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "usec", true),
-       PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector", true),
-       PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL, true),
+       PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT_ENABLE, "input schmitt enabled", NULL, false),
        PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode", true),
        PCONFDUMP(PIN_CONFIG_OUTPUT, "pin output", "level", true),
+       PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector", true),
+       PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL, true),
 };
 
 static void pinconf_generic_dump_one(struct pinctrl_dev *pctldev,
@@ -150,27 +150,28 @@ EXPORT_SYMBOL_GPL(pinconf_generic_dump_config);
 
 #ifdef CONFIG_OF
 static const struct pinconf_generic_params dt_params[] = {
+       { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 },
        { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
        { "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 },
-       { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 },
        { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
-       { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
        { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 },
-       { "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 },
+       { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
        { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 },
        { "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 },
+       { "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 },
        { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
-       { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
+       { "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 },
        { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
-       { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
+       { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
+       { "input-schmitt", PIN_CONFIG_INPUT_SCHMITT, 0 },
        { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
-       { "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 },
-       { "power-source", PIN_CONFIG_POWER_SOURCE, 0 },
-       { "low-power-enable", PIN_CONFIG_LOW_POWER_MODE, 1 },
+       { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
        { "low-power-disable", PIN_CONFIG_LOW_POWER_MODE, 0 },
-       { "output-low", PIN_CONFIG_OUTPUT, 0, },
+       { "low-power-enable", PIN_CONFIG_LOW_POWER_MODE, 1 },
        { "output-high", PIN_CONFIG_OUTPUT, 1, },
-       { "slew-rate", PIN_CONFIG_SLEW_RATE, 0},
+       { "output-low", PIN_CONFIG_OUTPUT, 0, },
+       { "power-source", PIN_CONFIG_POWER_SOURCE, 0 },
+       { "slew-rate", PIN_CONFIG_SLEW_RATE, 0 },
 };
 
 /**
index 29a7bb17a42f52b85cf82bbb6dc01c2311ffa0b1..4dd7722f993552a4c8cd698cdc2f8d224dc337c4 100644 (file)
@@ -411,7 +411,7 @@ static int pinconf_dbg_config_print(struct seq_file *s, void *d)
        const struct pinctrl_map *found = NULL;
        struct pinctrl_dev *pctldev;
        struct dbg_cfg *dbg = &pinconf_dbg_conf;
-       int i, j;
+       int i;
 
        mutex_lock(&pinctrl_maps_mutex);
 
@@ -424,13 +424,10 @@ static int pinconf_dbg_config_print(struct seq_file *s, void *d)
                if (strcmp(map->name, dbg->state_name))
                        continue;
 
-               for (j = 0; j < map->data.configs.num_configs; j++) {
-                       if (!strcmp(map->data.configs.group_or_pin,
-                                       dbg->pin_name)) {
-                               /* We found the right pin / state */
-                               found = map;
-                               break;
-                       }
+               if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) {
+                       /* We found the right pin */
+                       found = map;
+                       break;
                }
        }
 
index f6be68518c87d78195d3a9505b8eab5377804227..fd342dffe4dc766cefe93a29b3408476753b7316 100644 (file)
@@ -713,16 +713,6 @@ static struct pinctrl_desc adi_pinmux_desc = {
        .owner = THIS_MODULE,
 };
 
-static int adi_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void adi_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static int adi_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
 {
        struct gpio_port *port;
@@ -994,8 +984,8 @@ static int adi_gpio_probe(struct platform_device *pdev)
        port->chip.get                  = adi_gpio_get_value;
        port->chip.direction_output     = adi_gpio_direction_output;
        port->chip.set                  = adi_gpio_set_value;
-       port->chip.request              = adi_gpio_request;
-       port->chip.free                 = adi_gpio_free;
+       port->chip.request              = gpiochip_generic_request,
+       port->chip.free                 = gpiochip_generic_free,
        port->chip.to_irq               = adi_gpio_to_irq;
        if (pdata->port_gpio_base > 0)
                port->chip.base         = pdata->port_gpio_base;
index 4747e08f5389c36698c3916453e5284d77f8e476..56af28b95a44f627d145c6f91a52765787f2ae52 100644 (file)
@@ -536,21 +536,11 @@ static int as3722_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
        return as3722_irq_get_virq(as_pci->as3722, offset);
 }
 
-static int as3722_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void as3722_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static const struct gpio_chip as3722_gpio_chip = {
        .label                  = "as3722-gpio",
        .owner                  = THIS_MODULE,
-       .request                = as3722_gpio_request,
-       .free                   = as3722_gpio_free,
+       .request                = gpiochip_generic_request,
+       .free                   = gpiochip_generic_free,
        .get                    = as3722_gpio_get,
        .set                    = as3722_gpio_set,
        .direction_input        = as3722_gpio_direction_input,
diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c
new file mode 100644 (file)
index 0000000..33edd07
--- /dev/null
@@ -0,0 +1,1094 @@
+/*
+ * Driver for the Atmel PIO4 controller
+ *
+ * Copyright (C) 2015 Atmel,
+ *               2015 Ludovic Desroches <ludovic.desroches@atmel.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/clk.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/slab.h>
+#include "core.h"
+#include "pinconf.h"
+#include "pinctrl-utils.h"
+
+/*
+ * Warning:
+ * In order to not introduce confusion between Atmel PIO groups and pinctrl
+ * framework groups, Atmel PIO groups will be called banks, line is kept to
+ * designed the pin id into this bank.
+ */
+
+#define ATMEL_PIO_MSKR         0x0000
+#define ATMEL_PIO_CFGR         0x0004
+#define                ATMEL_PIO_CFGR_FUNC_MASK        GENMASK(2, 0)
+#define                ATMEL_PIO_DIR_MASK              BIT(8)
+#define                ATMEL_PIO_PUEN_MASK             BIT(9)
+#define                ATMEL_PIO_PDEN_MASK             BIT(10)
+#define                ATMEL_PIO_IFEN_MASK             BIT(12)
+#define                ATMEL_PIO_IFSCEN_MASK           BIT(13)
+#define                ATMEL_PIO_OPD_MASK              BIT(14)
+#define                ATMEL_PIO_SCHMITT_MASK          BIT(15)
+#define                ATMEL_PIO_CFGR_EVTSEL_MASK      GENMASK(26, 24)
+#define                ATMEL_PIO_CFGR_EVTSEL_FALLING   (0 << 24)
+#define                ATMEL_PIO_CFGR_EVTSEL_RISING    (1 << 24)
+#define                ATMEL_PIO_CFGR_EVTSEL_BOTH      (2 << 24)
+#define                ATMEL_PIO_CFGR_EVTSEL_LOW       (3 << 24)
+#define                ATMEL_PIO_CFGR_EVTSEL_HIGH      (4 << 24)
+#define ATMEL_PIO_PDSR         0x0008
+#define ATMEL_PIO_LOCKSR       0x000C
+#define ATMEL_PIO_SODR         0x0010
+#define ATMEL_PIO_CODR         0x0014
+#define ATMEL_PIO_ODSR         0x0018
+#define ATMEL_PIO_IER          0x0020
+#define ATMEL_PIO_IDR          0x0024
+#define ATMEL_PIO_IMR          0x0028
+#define ATMEL_PIO_ISR          0x002C
+#define ATMEL_PIO_IOFR         0x003C
+
+#define ATMEL_PIO_NPINS_PER_BANK       32
+#define ATMEL_PIO_BANK(pin_id)         (pin_id / ATMEL_PIO_NPINS_PER_BANK)
+#define ATMEL_PIO_LINE(pin_id)         (pin_id % ATMEL_PIO_NPINS_PER_BANK)
+#define ATMEL_PIO_BANK_OFFSET          0x40
+
+#define ATMEL_GET_PIN_NO(pinfunc)      ((pinfunc) & 0xff)
+#define ATMEL_GET_PIN_FUNC(pinfunc)    ((pinfunc >> 16) & 0xf)
+#define ATMEL_GET_PIN_IOSET(pinfunc)   ((pinfunc >> 20) & 0xf)
+
+struct atmel_pioctrl_data {
+       unsigned nbanks;
+};
+
+struct atmel_group {
+       const char *name;
+       u32 pin;
+};
+
+struct atmel_pin {
+       unsigned pin_id;
+       unsigned mux;
+       unsigned ioset;
+       unsigned bank;
+       unsigned line;
+       const char *device;
+};
+
+/**
+ * struct atmel_pioctrl - Atmel PIO controller (pinmux + gpio)
+ * @reg_base: base address of the controller.
+ * @clk: clock of the controller.
+ * @nbanks: number of PIO groups, it can vary depending on the SoC.
+ * @pinctrl_dev: pinctrl device registered.
+ * @groups: groups table to provide group name and pin in the group to pinctrl.
+ * @group_names: group names table to provide all the group/pin names to
+ *     pinctrl or gpio.
+ * @pins: pins table used for both pinctrl and gpio. pin_id, bank and line
+ *     fields are set at probe time. Other ones are set when parsing dt
+ *     pinctrl.
+ * @npins: number of pins.
+ * @gpio_chip: gpio chip registered.
+ * @irq_domain: irq domain for the gpio controller.
+ * @irqs: table containing the hw irq number of the bank. The index of the
+ *     table is the bank id.
+ * @dev: device entry for the Atmel PIO controller.
+ * @node: node of the Atmel PIO controller.
+ */
+struct atmel_pioctrl {
+       void __iomem            *reg_base;
+       struct clk              *clk;
+       unsigned                nbanks;
+       struct pinctrl_dev      *pinctrl_dev;
+       struct atmel_group      *groups;
+       const char * const      *group_names;
+       struct atmel_pin        **pins;
+       unsigned                npins;
+       struct gpio_chip        *gpio_chip;
+       struct irq_domain       *irq_domain;
+       int                     *irqs;
+       unsigned                *pm_wakeup_sources;
+       unsigned                *pm_suspend_backup;
+       struct device           *dev;
+       struct device_node      *node;
+};
+
+static const char * const atmel_functions[] = {
+       "GPIO", "A", "B", "C", "D", "E", "F", "G"
+};
+
+/* --- GPIO --- */
+static unsigned int atmel_gpio_read(struct atmel_pioctrl *atmel_pioctrl,
+                                   unsigned int bank, unsigned int reg)
+{
+       return readl_relaxed(atmel_pioctrl->reg_base
+                            + ATMEL_PIO_BANK_OFFSET * bank + reg);
+}
+
+static void atmel_gpio_write(struct atmel_pioctrl *atmel_pioctrl,
+                            unsigned int bank, unsigned int reg,
+                            unsigned int val)
+{
+       writel_relaxed(val, atmel_pioctrl->reg_base
+                      + ATMEL_PIO_BANK_OFFSET * bank + reg);
+}
+
+static void atmel_gpio_irq_ack(struct irq_data *d)
+{
+       /*
+        * Nothing to do, interrupt is cleared when reading the status
+        * register.
+        */
+}
+
+static int atmel_gpio_irq_set_type(struct irq_data *d, unsigned type)
+{
+       struct atmel_pioctrl *atmel_pioctrl = irq_data_get_irq_chip_data(d);
+       struct atmel_pin *pin = atmel_pioctrl->pins[d->hwirq];
+       unsigned reg;
+
+       atmel_gpio_write(atmel_pioctrl, pin->bank, ATMEL_PIO_MSKR,
+                        BIT(pin->line));
+       reg = atmel_gpio_read(atmel_pioctrl, pin->bank, ATMEL_PIO_CFGR);
+       reg &= (~ATMEL_PIO_CFGR_EVTSEL_MASK);
+
+       switch (type) {
+       case IRQ_TYPE_EDGE_RISING:
+               irq_set_handler_locked(d, handle_edge_irq);
+               reg |= ATMEL_PIO_CFGR_EVTSEL_RISING;
+               break;
+       case IRQ_TYPE_EDGE_FALLING:
+               irq_set_handler_locked(d, handle_edge_irq);
+               reg |= ATMEL_PIO_CFGR_EVTSEL_FALLING;
+               break;
+       case IRQ_TYPE_EDGE_BOTH:
+               irq_set_handler_locked(d, handle_edge_irq);
+               reg |= ATMEL_PIO_CFGR_EVTSEL_BOTH;
+               break;
+       case IRQ_TYPE_LEVEL_LOW:
+               irq_set_handler_locked(d, handle_level_irq);
+               reg |= ATMEL_PIO_CFGR_EVTSEL_LOW;
+               break;
+       case IRQ_TYPE_LEVEL_HIGH:
+               irq_set_handler_locked(d, handle_level_irq);
+               reg |= ATMEL_PIO_CFGR_EVTSEL_HIGH;
+               break;
+       case IRQ_TYPE_NONE:
+       default:
+               return -EINVAL;
+       }
+
+       atmel_gpio_write(atmel_pioctrl, pin->bank, ATMEL_PIO_CFGR, reg);
+
+       return 0;
+}
+
+static void atmel_gpio_irq_mask(struct irq_data *d)
+{
+       struct atmel_pioctrl *atmel_pioctrl = irq_data_get_irq_chip_data(d);
+       struct atmel_pin *pin = atmel_pioctrl->pins[d->hwirq];
+
+       atmel_gpio_write(atmel_pioctrl, pin->bank, ATMEL_PIO_IDR,
+                        BIT(pin->line));
+}
+
+static void atmel_gpio_irq_unmask(struct irq_data *d)
+{
+       struct atmel_pioctrl *atmel_pioctrl = irq_data_get_irq_chip_data(d);
+       struct atmel_pin *pin = atmel_pioctrl->pins[d->hwirq];
+
+       atmel_gpio_write(atmel_pioctrl, pin->bank, ATMEL_PIO_IER,
+                        BIT(pin->line));
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int atmel_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+       struct atmel_pioctrl *atmel_pioctrl = irq_data_get_irq_chip_data(d);
+       int bank = ATMEL_PIO_BANK(d->hwirq);
+       int line = ATMEL_PIO_LINE(d->hwirq);
+
+       /* The gpio controller has one interrupt line per bank. */
+       irq_set_irq_wake(atmel_pioctrl->irqs[bank], on);
+
+       if (on)
+               atmel_pioctrl->pm_wakeup_sources[bank] |= BIT(line);
+       else
+               atmel_pioctrl->pm_wakeup_sources[bank] &= ~(BIT(line));
+
+       return 0;
+}
+#else
+#define atmel_gpio_irq_set_wake NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct irq_chip atmel_gpio_irq_chip = {
+       .name           = "GPIO",
+       .irq_ack        = atmel_gpio_irq_ack,
+       .irq_mask       = atmel_gpio_irq_mask,
+       .irq_unmask     = atmel_gpio_irq_unmask,
+       .irq_set_type   = atmel_gpio_irq_set_type,
+       .irq_set_wake   = atmel_gpio_irq_set_wake,
+};
+
+static void atmel_gpio_irq_handler(struct irq_desc *desc)
+{
+       unsigned int irq = irq_desc_get_irq(desc);
+       struct atmel_pioctrl *atmel_pioctrl = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       unsigned long isr;
+       int n, bank = -1;
+
+       /* Find from which bank is the irq received. */
+       for (n = 0; n < atmel_pioctrl->nbanks; n++) {
+               if (atmel_pioctrl->irqs[n] == irq) {
+                       bank = n;
+                       break;
+               }
+       }
+
+       if (bank < 0) {
+               dev_err(atmel_pioctrl->dev,
+                       "no bank associated to irq %u\n", irq);
+               return;
+       }
+
+       chained_irq_enter(chip, desc);
+
+       for (;;) {
+               isr = (unsigned long)atmel_gpio_read(atmel_pioctrl, bank,
+                                                    ATMEL_PIO_ISR);
+               isr &= (unsigned long)atmel_gpio_read(atmel_pioctrl, bank,
+                                                     ATMEL_PIO_IMR);
+               if (!isr)
+                       break;
+
+               for_each_set_bit(n, &isr, BITS_PER_LONG)
+                       generic_handle_irq(gpio_to_irq(bank *
+                                       ATMEL_PIO_NPINS_PER_BANK + n));
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static int atmel_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+       struct atmel_pioctrl *atmel_pioctrl = dev_get_drvdata(chip->dev);
+       struct atmel_pin *pin = atmel_pioctrl->pins[offset];
+       unsigned reg;
+
+       atmel_gpio_write(atmel_pioctrl, pin->bank, ATMEL_PIO_MSKR,
+                        BIT(pin->line));
+       reg = atmel_gpio_read(atmel_pioctrl, pin->bank, ATMEL_PIO_CFGR);
+       reg &= ~ATMEL_PIO_DIR_MASK;
+       atmel_gpio_write(atmel_pioctrl, pin->bank, ATMEL_PIO_CFGR, reg);
+
+       return 0;
+}
+
+static int atmel_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct atmel_pioctrl *atmel_pioctrl = dev_get_drvdata(chip->dev);
+       struct atmel_pin *pin = atmel_pioctrl->pins[offset];
+       unsigned reg;
+
+       reg = atmel_gpio_read(atmel_pioctrl, pin->bank, ATMEL_PIO_PDSR);
+
+       return !!(reg & BIT(pin->line));
+}
+
+static int atmel_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+                                      int value)
+{
+       struct atmel_pioctrl *atmel_pioctrl = dev_get_drvdata(chip->dev);
+       struct atmel_pin *pin = atmel_pioctrl->pins[offset];
+       unsigned reg;
+
+       atmel_gpio_write(atmel_pioctrl, pin->bank,
+                        value ? ATMEL_PIO_SODR : ATMEL_PIO_CODR,
+                        BIT(pin->line));
+
+       atmel_gpio_write(atmel_pioctrl, pin->bank, ATMEL_PIO_MSKR,
+                        BIT(pin->line));
+       reg = atmel_gpio_read(atmel_pioctrl, pin->bank, ATMEL_PIO_CFGR);
+       reg |= ATMEL_PIO_DIR_MASK;
+       atmel_gpio_write(atmel_pioctrl, pin->bank, ATMEL_PIO_CFGR, reg);
+
+       return 0;
+}
+
+static void atmel_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+       struct atmel_pioctrl *atmel_pioctrl = dev_get_drvdata(chip->dev);
+       struct atmel_pin *pin = atmel_pioctrl->pins[offset];
+
+       atmel_gpio_write(atmel_pioctrl, pin->bank,
+                        val ? ATMEL_PIO_SODR : ATMEL_PIO_CODR,
+                        BIT(pin->line));
+}
+
+static int atmel_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+       struct atmel_pioctrl *atmel_pioctrl = dev_get_drvdata(chip->dev);
+
+       return irq_find_mapping(atmel_pioctrl->irq_domain, offset);
+}
+
+static struct gpio_chip atmel_gpio_chip = {
+       .direction_input        = atmel_gpio_direction_input,
+       .get                    = atmel_gpio_get,
+       .direction_output       = atmel_gpio_direction_output,
+       .set                    = atmel_gpio_set,
+       .to_irq                 = atmel_gpio_to_irq,
+       .base                   = 0,
+};
+
+/* --- PINCTRL --- */
+static unsigned int atmel_pin_config_read(struct pinctrl_dev *pctldev,
+                                         unsigned pin_id)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+       unsigned bank = atmel_pioctrl->pins[pin_id]->bank;
+       unsigned line = atmel_pioctrl->pins[pin_id]->line;
+       void __iomem *addr = atmel_pioctrl->reg_base
+                            + bank * ATMEL_PIO_BANK_OFFSET;
+
+       writel_relaxed(BIT(line), addr + ATMEL_PIO_MSKR);
+       /* Have to set MSKR first, to access the right pin CFGR. */
+       wmb();
+
+       return readl_relaxed(addr + ATMEL_PIO_CFGR);
+}
+
+static void atmel_pin_config_write(struct pinctrl_dev *pctldev,
+                                  unsigned pin_id, u32 conf)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+       unsigned bank = atmel_pioctrl->pins[pin_id]->bank;
+       unsigned line = atmel_pioctrl->pins[pin_id]->line;
+       void __iomem *addr = atmel_pioctrl->reg_base
+                            + bank * ATMEL_PIO_BANK_OFFSET;
+
+       writel_relaxed(BIT(line), addr + ATMEL_PIO_MSKR);
+       /* Have to set MSKR first, to access the right pin CFGR. */
+       wmb();
+       writel_relaxed(conf, addr + ATMEL_PIO_CFGR);
+}
+
+static int atmel_pctl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+
+       return atmel_pioctrl->npins;
+}
+
+static const char *atmel_pctl_get_group_name(struct pinctrl_dev *pctldev,
+                                            unsigned selector)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+
+       return atmel_pioctrl->groups[selector].name;
+}
+
+static int atmel_pctl_get_group_pins(struct pinctrl_dev *pctldev,
+                                    unsigned selector, const unsigned **pins,
+                                    unsigned *num_pins)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+
+       *pins = (unsigned *)&atmel_pioctrl->groups[selector].pin;
+       *num_pins = 1;
+
+       return 0;
+}
+
+struct atmel_group *atmel_pctl_find_group_by_pin(struct pinctrl_dev *pctldev,
+                                                unsigned pin)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+       int i;
+
+       for (i = 0; i < atmel_pioctrl->npins; i++) {
+               struct atmel_group *grp = atmel_pioctrl->groups + i;
+
+               if (grp->pin == pin)
+                       return grp;
+       }
+
+       return NULL;
+}
+
+static int atmel_pctl_xlate_pinfunc(struct pinctrl_dev *pctldev,
+                                   struct device_node *np,
+                                   u32 pinfunc, const char **grp_name,
+                                   const char **func_name)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+       unsigned pin_id, func_id;
+       struct atmel_group *grp;
+
+       pin_id = ATMEL_GET_PIN_NO(pinfunc);
+       func_id = ATMEL_GET_PIN_FUNC(pinfunc);
+
+       if (func_id >= ARRAY_SIZE(atmel_functions))
+               return -EINVAL;
+
+       *func_name = atmel_functions[func_id];
+
+       grp = atmel_pctl_find_group_by_pin(pctldev, pin_id);
+       if (!grp)
+               return -EINVAL;
+       *grp_name = grp->name;
+
+       atmel_pioctrl->pins[pin_id]->mux = func_id;
+       atmel_pioctrl->pins[pin_id]->ioset = ATMEL_GET_PIN_IOSET(pinfunc);
+       /* Want the device name not the group one. */
+       if (np->parent == atmel_pioctrl->node)
+               atmel_pioctrl->pins[pin_id]->device = np->name;
+       else
+               atmel_pioctrl->pins[pin_id]->device = np->parent->name;
+
+       return 0;
+}
+
+static int atmel_pctl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+                                       struct device_node *np,
+                                       struct pinctrl_map **map,
+                                       unsigned *reserved_maps,
+                                       unsigned *num_maps)
+{
+       unsigned num_pins, num_configs, reserve;
+       unsigned long *configs;
+       struct property *pins;
+       bool has_config;
+       u32 pinfunc;
+       int ret, i;
+
+       pins = of_find_property(np, "pinmux", NULL);
+       if (!pins)
+               return -EINVAL;
+
+       ret = pinconf_generic_parse_dt_config(np, pctldev, &configs,
+                                             &num_configs);
+       if (ret < 0) {
+               dev_err(pctldev->dev, "%s: could not parse node property\n",
+                       of_node_full_name(np));
+               return ret;
+       }
+
+       if (num_configs)
+               has_config = true;
+
+       num_pins = pins->length / sizeof(u32);
+       if (!num_pins) {
+               dev_err(pctldev->dev, "no pins found in node %s\n",
+                       of_node_full_name(np));
+               return -EINVAL;
+       }
+
+       /*
+        * Reserve maps, at least there is a mux map and an optional conf
+        * map for each pin.
+        */
+       reserve = 1;
+       if (has_config && num_pins >= 1)
+               reserve++;
+       reserve *= num_pins;
+       ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps,
+                                       reserve);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < num_pins; i++) {
+               const char *group, *func;
+
+               ret = of_property_read_u32_index(np, "pinmux", i, &pinfunc);
+               if (ret)
+                       return ret;
+
+               ret = atmel_pctl_xlate_pinfunc(pctldev, np, pinfunc, &group,
+                                              &func);
+               if (ret)
+                       return ret;
+
+               pinctrl_utils_add_map_mux(pctldev, map, reserved_maps, num_maps,
+                                         group, func);
+
+               if (has_config) {
+                       ret = pinctrl_utils_add_map_configs(pctldev, map,
+                                       reserved_maps, num_maps, group,
+                                       configs, num_configs,
+                                       PIN_MAP_TYPE_CONFIGS_GROUP);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int atmel_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,
+                                    struct device_node *np_config,
+                                    struct pinctrl_map **map,
+                                    unsigned *num_maps)
+{
+       struct device_node *np;
+       unsigned reserved_maps;
+       int ret;
+
+       *map = NULL;
+       *num_maps = 0;
+       reserved_maps = 0;
+
+       /*
+        * If all the pins of a device have the same configuration (or no one),
+        * it is useless to add a subnode, so directly parse node referenced by
+        * phandle.
+        */
+       ret = atmel_pctl_dt_subnode_to_map(pctldev, np_config, map,
+                                          &reserved_maps, num_maps);
+       if (ret) {
+               for_each_child_of_node(np_config, np) {
+                       ret = atmel_pctl_dt_subnode_to_map(pctldev, np, map,
+                                                   &reserved_maps, num_maps);
+                       if (ret < 0)
+                               break;
+               }
+       }
+
+       if (ret < 0) {
+               pinctrl_utils_dt_free_map(pctldev, *map, *num_maps);
+               dev_err(pctldev->dev, "can't create maps for node %s\n",
+                       np_config->full_name);
+       }
+
+       return ret;
+}
+
+static const struct pinctrl_ops atmel_pctlops = {
+       .get_groups_count       = atmel_pctl_get_groups_count,
+       .get_group_name         = atmel_pctl_get_group_name,
+       .get_group_pins         = atmel_pctl_get_group_pins,
+       .dt_node_to_map         = atmel_pctl_dt_node_to_map,
+       .dt_free_map            = pinctrl_utils_dt_free_map,
+};
+
+static int atmel_pmx_get_functions_count(struct pinctrl_dev *pctldev)
+{
+       return ARRAY_SIZE(atmel_functions);
+}
+
+static const char *atmel_pmx_get_function_name(struct pinctrl_dev *pctldev,
+                                              unsigned selector)
+{
+       return atmel_functions[selector];
+}
+
+static int atmel_pmx_get_function_groups(struct pinctrl_dev *pctldev,
+                                        unsigned selector,
+                                        const char * const **groups,
+                                        unsigned * const num_groups)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+
+       *groups = atmel_pioctrl->group_names;
+       *num_groups = atmel_pioctrl->npins;
+
+       return 0;
+}
+
+static int atmel_pmx_set_mux(struct pinctrl_dev *pctldev,
+                            unsigned function,
+                            unsigned group)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+       unsigned pin;
+       u32 conf;
+
+       dev_dbg(pctldev->dev, "enable function %s group %s\n",
+               atmel_functions[function], atmel_pioctrl->groups[group].name);
+
+       pin = atmel_pioctrl->groups[group].pin;
+       conf = atmel_pin_config_read(pctldev, pin);
+       conf &= (~ATMEL_PIO_CFGR_FUNC_MASK);
+       conf |= (function & ATMEL_PIO_CFGR_FUNC_MASK);
+       dev_dbg(pctldev->dev, "pin: %u, conf: 0x%08x\n", pin, conf);
+       atmel_pin_config_write(pctldev, pin, conf);
+
+       return 0;
+}
+
+static const struct pinmux_ops atmel_pmxops = {
+       .get_functions_count    = atmel_pmx_get_functions_count,
+       .get_function_name      = atmel_pmx_get_function_name,
+       .get_function_groups    = atmel_pmx_get_function_groups,
+       .set_mux                = atmel_pmx_set_mux,
+};
+
+static int atmel_conf_pin_config_group_get(struct pinctrl_dev *pctldev,
+                                          unsigned group,
+                                          unsigned long *config)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+       unsigned param = pinconf_to_config_param(*config), arg = 0;
+       struct atmel_group *grp = atmel_pioctrl->groups + group;
+       unsigned pin_id = grp->pin;
+       u32 res;
+
+       res = atmel_pin_config_read(pctldev, pin_id);
+
+       switch (param) {
+       case PIN_CONFIG_BIAS_PULL_UP:
+               if (!(res & ATMEL_PIO_PUEN_MASK))
+                       return -EINVAL;
+               arg = 1;
+               break;
+       case PIN_CONFIG_BIAS_PULL_DOWN:
+               if ((res & ATMEL_PIO_PUEN_MASK) ||
+                   (!(res & ATMEL_PIO_PDEN_MASK)))
+                       return -EINVAL;
+               arg = 1;
+               break;
+       case PIN_CONFIG_BIAS_DISABLE:
+               if ((res & ATMEL_PIO_PUEN_MASK) ||
+                   ((res & ATMEL_PIO_PDEN_MASK)))
+                       return -EINVAL;
+               arg = 1;
+               break;
+       case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+               if (!(res & ATMEL_PIO_OPD_MASK))
+                       return -EINVAL;
+               arg = 1;
+               break;
+       case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+               if (!(res & ATMEL_PIO_SCHMITT_MASK))
+                       return -EINVAL;
+               arg = 1;
+               break;
+       default:
+               return -ENOTSUPP;
+       }
+
+       *config = pinconf_to_config_packed(param, arg);
+       return 0;
+}
+
+static int atmel_conf_pin_config_group_set(struct pinctrl_dev *pctldev,
+                                          unsigned group,
+                                          unsigned long *configs,
+                                          unsigned num_configs)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+       struct atmel_group *grp = atmel_pioctrl->groups + group;
+       unsigned bank, pin, pin_id = grp->pin;
+       u32 mask, conf = 0;
+       int i;
+
+       conf = atmel_pin_config_read(pctldev, pin_id);
+
+       for (i = 0; i < num_configs; i++) {
+               unsigned param = pinconf_to_config_param(configs[i]);
+               unsigned arg = pinconf_to_config_argument(configs[i]);
+
+               dev_dbg(pctldev->dev, "%s: pin=%u, config=0x%lx\n",
+                       __func__, pin_id, configs[i]);
+
+               switch (param) {
+               case PIN_CONFIG_BIAS_DISABLE:
+                       conf &= (~ATMEL_PIO_PUEN_MASK);
+                       conf &= (~ATMEL_PIO_PDEN_MASK);
+                       break;
+               case PIN_CONFIG_BIAS_PULL_UP:
+                       conf |= ATMEL_PIO_PUEN_MASK;
+                       break;
+               case PIN_CONFIG_BIAS_PULL_DOWN:
+                       conf |= ATMEL_PIO_PDEN_MASK;
+                       break;
+               case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+                       if (arg == 0)
+                               conf &= (~ATMEL_PIO_OPD_MASK);
+                       else
+                               conf |= ATMEL_PIO_OPD_MASK;
+                       break;
+               case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+                       if (arg == 0)
+                               conf |= ATMEL_PIO_SCHMITT_MASK;
+                       else
+                               conf &= (~ATMEL_PIO_SCHMITT_MASK);
+                       break;
+               case PIN_CONFIG_INPUT_DEBOUNCE:
+                       if (arg == 0) {
+                               conf &= (~ATMEL_PIO_IFEN_MASK);
+                               conf &= (~ATMEL_PIO_IFSCEN_MASK);
+                       } else {
+                               /*
+                                * We don't care about the debounce value for several reasons:
+                                * - can't have different debounce periods inside a same group,
+                                * - the register to configure this period is a secure register.
+                                * The debouncing filter can filter a pulse with a duration of less
+                                * than 1/2 slow clock period.
+                                */
+                               conf |= ATMEL_PIO_IFEN_MASK;
+                               conf |= ATMEL_PIO_IFSCEN_MASK;
+                       }
+                       break;
+               case PIN_CONFIG_OUTPUT:
+                       conf |= ATMEL_PIO_DIR_MASK;
+                       bank = ATMEL_PIO_BANK(pin_id);
+                       pin = ATMEL_PIO_LINE(pin_id);
+                       mask = 1 << pin;
+
+                       if (arg == 0) {
+                               writel_relaxed(mask, atmel_pioctrl->reg_base +
+                                       bank * ATMEL_PIO_BANK_OFFSET +
+                                       ATMEL_PIO_CODR);
+                       } else {
+                               writel_relaxed(mask, atmel_pioctrl->reg_base +
+                                       bank * ATMEL_PIO_BANK_OFFSET +
+                                       ATMEL_PIO_SODR);
+                       }
+                       break;
+               default:
+                       dev_warn(pctldev->dev,
+                                "unsupported configuration parameter: %u\n",
+                                param);
+                       continue;
+               }
+       }
+
+       dev_dbg(pctldev->dev, "%s: reg=0x%08x\n", __func__, conf);
+       atmel_pin_config_write(pctldev, pin_id, conf);
+
+       return 0;
+}
+
+static void atmel_conf_pin_config_dbg_show(struct pinctrl_dev *pctldev,
+                                          struct seq_file *s, unsigned pin_id)
+{
+       struct atmel_pioctrl *atmel_pioctrl = pinctrl_dev_get_drvdata(pctldev);
+       u32 conf;
+
+       if (!atmel_pioctrl->pins[pin_id]->device)
+               return;
+
+       if (atmel_pioctrl->pins[pin_id])
+               seq_printf(s, " (%s, ioset %u) ",
+                          atmel_pioctrl->pins[pin_id]->device,
+                          atmel_pioctrl->pins[pin_id]->ioset);
+
+       conf = atmel_pin_config_read(pctldev, pin_id);
+       if (conf & ATMEL_PIO_PUEN_MASK)
+               seq_printf(s, "%s ", "pull-up");
+       if (conf & ATMEL_PIO_PDEN_MASK)
+               seq_printf(s, "%s ", "pull-down");
+       if (conf & ATMEL_PIO_IFEN_MASK)
+               seq_printf(s, "%s ", "debounce");
+       if (conf & ATMEL_PIO_OPD_MASK)
+               seq_printf(s, "%s ", "open-drain");
+       if (conf & ATMEL_PIO_SCHMITT_MASK)
+               seq_printf(s, "%s ", "schmitt");
+}
+
+static const struct pinconf_ops atmel_confops = {
+       .pin_config_group_get   = atmel_conf_pin_config_group_get,
+       .pin_config_group_set   = atmel_conf_pin_config_group_set,
+       .pin_config_dbg_show    = atmel_conf_pin_config_dbg_show,
+};
+
+static struct pinctrl_desc atmel_pinctrl_desc = {
+       .name           = "atmel_pinctrl",
+       .confops        = &atmel_confops,
+       .pctlops        = &atmel_pctlops,
+       .pmxops         = &atmel_pmxops,
+};
+
+static int atmel_pctrl_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct atmel_pioctrl *atmel_pioctrl = platform_get_drvdata(pdev);
+       int i;
+
+       /*
+        * For each bank, save IMR to restore it later and disable all GPIO
+        * interrupts excepting the ones marked as wakeup sources.
+        */
+       for (i = 0; i < atmel_pioctrl->nbanks; i++) {
+               atmel_pioctrl->pm_suspend_backup[i] =
+                       atmel_gpio_read(atmel_pioctrl, i, ATMEL_PIO_IMR);
+               atmel_gpio_write(atmel_pioctrl, i, ATMEL_PIO_IDR,
+                                ~atmel_pioctrl->pm_wakeup_sources[i]);
+       }
+
+       return 0;
+}
+
+static int atmel_pctrl_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct atmel_pioctrl *atmel_pioctrl = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < atmel_pioctrl->nbanks; i++)
+               atmel_gpio_write(atmel_pioctrl, i, ATMEL_PIO_IER,
+                                atmel_pioctrl->pm_suspend_backup[i]);
+
+       return 0;
+}
+
+static const struct dev_pm_ops atmel_pctrl_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(atmel_pctrl_suspend, atmel_pctrl_resume)
+};
+
+/*
+ * The number of banks can be different from a SoC to another one.
+ * We can have up to 16 banks.
+ */
+static const struct atmel_pioctrl_data atmel_sama5d2_pioctrl_data = {
+       .nbanks         = 4,
+};
+
+static const struct of_device_id atmel_pctrl_of_match[] = {
+       {
+               .compatible = "atmel,sama5d2-pinctrl",
+               .data = &atmel_sama5d2_pioctrl_data,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, atmel_pctrl_of_match);
+
+static int atmel_pinctrl_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct pinctrl_pin_desc *pin_desc;
+       const char **group_names;
+       const struct of_device_id *match;
+       int i, ret;
+       struct resource *res;
+       struct atmel_pioctrl *atmel_pioctrl;
+       struct atmel_pioctrl_data *atmel_pioctrl_data;
+
+       atmel_pioctrl = devm_kzalloc(dev, sizeof(*atmel_pioctrl), GFP_KERNEL);
+       if (!atmel_pioctrl)
+               return -ENOMEM;
+       atmel_pioctrl->dev = dev;
+       atmel_pioctrl->node = dev->of_node;
+       platform_set_drvdata(pdev, atmel_pioctrl);
+
+       match = of_match_node(atmel_pctrl_of_match, dev->of_node);
+       if (!match) {
+               dev_err(dev, "unknown compatible string\n");
+               return -ENODEV;
+       }
+       atmel_pioctrl_data = (struct atmel_pioctrl_data *)match->data;
+       atmel_pioctrl->nbanks = atmel_pioctrl_data->nbanks;
+       atmel_pioctrl->npins = atmel_pioctrl->nbanks * ATMEL_PIO_NPINS_PER_BANK;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "unable to get atmel pinctrl resource\n");
+               return -EINVAL;
+       }
+       atmel_pioctrl->reg_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(atmel_pioctrl->reg_base))
+               return -EINVAL;
+
+       atmel_pioctrl->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(atmel_pioctrl->clk)) {
+               dev_err(dev, "failed to get clock\n");
+               return PTR_ERR(atmel_pioctrl->clk);
+       }
+
+       atmel_pioctrl->pins = devm_kzalloc(dev, sizeof(*atmel_pioctrl->pins)
+                       * atmel_pioctrl->npins, GFP_KERNEL);
+       if (!atmel_pioctrl->pins)
+               return -ENOMEM;
+
+       pin_desc = devm_kzalloc(dev, sizeof(*pin_desc)
+                       * atmel_pioctrl->npins, GFP_KERNEL);
+       if (!pin_desc)
+               return -ENOMEM;
+       atmel_pinctrl_desc.pins = pin_desc;
+       atmel_pinctrl_desc.npins = atmel_pioctrl->npins;
+
+       /* One pin is one group since a pin can achieve all functions. */
+       group_names = devm_kzalloc(dev, sizeof(*group_names)
+                       * atmel_pioctrl->npins, GFP_KERNEL);
+       if (!group_names)
+               return -ENOMEM;
+       atmel_pioctrl->group_names = group_names;
+
+       atmel_pioctrl->groups = devm_kzalloc(&pdev->dev,
+                       sizeof(*atmel_pioctrl->groups) * atmel_pioctrl->npins,
+                       GFP_KERNEL);
+       if (!atmel_pioctrl->groups)
+               return -ENOMEM;
+       for (i = 0 ; i < atmel_pioctrl->npins; i++) {
+               struct atmel_group *group = atmel_pioctrl->groups + i;
+               unsigned bank = ATMEL_PIO_BANK(i);
+               unsigned line = ATMEL_PIO_LINE(i);
+
+               atmel_pioctrl->pins[i] = devm_kzalloc(dev,
+                               sizeof(**atmel_pioctrl->pins), GFP_KERNEL);
+               if (!atmel_pioctrl->pins[i])
+                       return -ENOMEM;
+
+               atmel_pioctrl->pins[i]->pin_id = i;
+               atmel_pioctrl->pins[i]->bank = bank;
+               atmel_pioctrl->pins[i]->line = line;
+
+               pin_desc[i].number = i;
+               /* Pin naming convention: P(bank_name)(bank_pin_number). */
+               pin_desc[i].name = kasprintf(GFP_KERNEL, "P%c%d",
+                                            bank + 'A', line);
+
+               group->name = group_names[i] = pin_desc[i].name;
+               group->pin = pin_desc[i].number;
+
+               dev_dbg(dev, "pin_id=%u, bank=%u, line=%u", i, bank, line);
+       }
+
+       atmel_pioctrl->gpio_chip = &atmel_gpio_chip;
+       atmel_pioctrl->gpio_chip->of_node = dev->of_node;
+       atmel_pioctrl->gpio_chip->ngpio = atmel_pioctrl->npins;
+       atmel_pioctrl->gpio_chip->label = dev_name(dev);
+       atmel_pioctrl->gpio_chip->dev = dev;
+       atmel_pioctrl->gpio_chip->names = atmel_pioctrl->group_names;
+
+       atmel_pioctrl->pm_wakeup_sources = devm_kzalloc(dev,
+                       sizeof(*atmel_pioctrl->pm_wakeup_sources)
+                       * atmel_pioctrl->nbanks, GFP_KERNEL);
+       if (!atmel_pioctrl->pm_wakeup_sources)
+               return -ENOMEM;
+
+       atmel_pioctrl->pm_suspend_backup = devm_kzalloc(dev,
+                       sizeof(*atmel_pioctrl->pm_suspend_backup)
+                       * atmel_pioctrl->nbanks, GFP_KERNEL);
+       if (!atmel_pioctrl->pm_suspend_backup)
+               return -ENOMEM;
+
+       atmel_pioctrl->irqs = devm_kzalloc(dev, sizeof(*atmel_pioctrl->irqs)
+                       * atmel_pioctrl->nbanks, GFP_KERNEL);
+       if (!atmel_pioctrl->irqs)
+               return -ENOMEM;
+
+       /* There is one controller but each bank has its own irq line. */
+       for (i = 0; i < atmel_pioctrl->nbanks; i++) {
+               res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+               if (!res) {
+                       dev_err(dev, "missing irq resource for group %c\n",
+                               'A' + i);
+                       return -EINVAL;
+               }
+               atmel_pioctrl->irqs[i] = res->start;
+               irq_set_chained_handler(res->start, atmel_gpio_irq_handler);
+               irq_set_handler_data(res->start, atmel_pioctrl);
+               dev_dbg(dev, "bank %i: hwirq=%u\n", i, res->start);
+       }
+
+       atmel_pioctrl->irq_domain = irq_domain_add_linear(dev->of_node,
+                       atmel_pioctrl->gpio_chip->ngpio,
+                       &irq_domain_simple_ops, NULL);
+       if (!atmel_pioctrl->irq_domain) {
+               dev_err(dev, "can't add the irq domain\n");
+               return -ENODEV;
+       }
+       atmel_pioctrl->irq_domain->name = "atmel gpio";
+
+       for (i = 0; i < atmel_pioctrl->npins; i++) {
+               int irq = irq_create_mapping(atmel_pioctrl->irq_domain, i);
+
+               irq_set_chip_and_handler(irq, &atmel_gpio_irq_chip,
+                                        handle_simple_irq);
+               irq_set_chip_data(irq, atmel_pioctrl);
+               dev_dbg(dev,
+                       "atmel gpio irq domain: hwirq: %d, linux irq: %d\n",
+                       i, irq);
+       }
+
+       ret = clk_prepare_enable(atmel_pioctrl->clk);
+       if (ret) {
+               dev_err(dev, "failed to prepare and enable clock\n");
+               goto clk_prepare_enable_error;
+       }
+
+       atmel_pioctrl->pinctrl_dev = pinctrl_register(&atmel_pinctrl_desc,
+                                                     &pdev->dev,
+                                                     atmel_pioctrl);
+       if (!atmel_pioctrl->pinctrl_dev) {
+               dev_err(dev, "pinctrl registration failed\n");
+               goto pinctrl_register_error;
+       }
+
+       ret = gpiochip_add(atmel_pioctrl->gpio_chip);
+       if (ret) {
+               dev_err(dev, "failed to add gpiochip\n");
+               goto gpiochip_add_error;
+       }
+
+       ret = gpiochip_add_pin_range(atmel_pioctrl->gpio_chip, dev_name(dev),
+                                    0, 0, atmel_pioctrl->gpio_chip->ngpio);
+       if (ret) {
+               dev_err(dev, "failed to add gpio pin range\n");
+               goto gpiochip_add_pin_range_error;
+       }
+
+       dev_info(&pdev->dev, "atmel pinctrl initialized\n");
+
+       return 0;
+
+clk_prepare_enable_error:
+       irq_domain_remove(atmel_pioctrl->irq_domain);
+pinctrl_register_error:
+       clk_disable_unprepare(atmel_pioctrl->clk);
+gpiochip_add_error:
+       pinctrl_unregister(atmel_pioctrl->pinctrl_dev);
+gpiochip_add_pin_range_error:
+       gpiochip_remove(atmel_pioctrl->gpio_chip);
+
+       return ret;
+}
+
+int atmel_pinctrl_remove(struct platform_device *pdev)
+{
+       struct atmel_pioctrl *atmel_pioctrl = platform_get_drvdata(pdev);
+
+       irq_domain_remove(atmel_pioctrl->irq_domain);
+       clk_disable_unprepare(atmel_pioctrl->clk);
+       pinctrl_unregister(atmel_pioctrl->pinctrl_dev);
+       gpiochip_remove(atmel_pioctrl->gpio_chip);
+
+       return 0;
+}
+
+static struct platform_driver atmel_pinctrl_driver = {
+       .driver = {
+               .name = "pinctrl-at91-pio4",
+               .of_match_table = atmel_pctrl_of_match,
+               .pm = &atmel_pctrl_pm_ops,
+       },
+       .probe = atmel_pinctrl_probe,
+       .remove = atmel_pinctrl_remove,
+};
+module_platform_driver(atmel_pinctrl_driver);
+
+MODULE_AUTHOR(Ludovic Desroches <ludovic.desroches@atmel.com>);
+MODULE_DESCRIPTION("Atmel PIO4 pinctrl driver");
+MODULE_LICENSE("GPL v2");
index b0fde0f385e6864c4fd962751fab9bb51dfbfa13..0d2fc0cff35ee6216ef5e62a77376e63cb2675e7 100644 (file)
@@ -1122,8 +1122,10 @@ static int at91_pinctrl_parse_functions(struct device_node *np,
                func->groups[i] = child->name;
                grp = &info->groups[grp_index++];
                ret = at91_pinctrl_parse_groups(child, grp, info, i++);
-               if (ret)
+               if (ret) {
+                       of_node_put(child);
                        return ret;
+               }
        }
 
        return 0;
@@ -1196,6 +1198,7 @@ static int at91_pinctrl_probe_dt(struct platform_device *pdev,
                ret = at91_pinctrl_parse_functions(child, info, i++);
                if (ret) {
                        dev_err(&pdev->dev, "failed to parse function\n");
+                       of_node_put(child);
                        return ret;
                }
        }
@@ -1277,28 +1280,6 @@ static int at91_pinctrl_remove(struct platform_device *pdev)
        return 0;
 }
 
-static int at91_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       /*
-        * Map back to global GPIO space and request muxing, the direction
-        * parameter does not matter for this controller.
-        */
-       int gpio = chip->base + offset;
-       int bank = chip->base / chip->ngpio;
-
-       dev_dbg(chip->dev, "%s:%d pio%c%d(%d)\n", __func__, __LINE__,
-                'A' + bank, offset, gpio);
-
-       return pinctrl_request_gpio(gpio);
-}
-
-static void at91_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       int gpio = chip->base + offset;
-
-       pinctrl_free_gpio(gpio);
-}
-
 static int at91_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
 {
        struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
@@ -1684,8 +1665,8 @@ static int at91_gpio_of_irq_setup(struct platform_device *pdev,
 
 /* This structure is replicated for each GPIO block allocated at probe time */
 static struct gpio_chip at91_gpio_template = {
-       .request                = at91_gpio_request,
-       .free                   = at91_gpio_free,
+       .request                = gpiochip_generic_request,
+       .free                   = gpiochip_generic_free,
        .get_direction          = at91_gpio_get_direction,
        .direction_input        = at91_gpio_direction_input,
        .get                    = at91_gpio_get,
index 9c9b88934bcc904de8b63f20778ef12985ad7ed5..813eb7c771ecb5a38e33eedcb5d0a92d4c9edde4 100644 (file)
@@ -217,24 +217,6 @@ static inline struct u300_gpio *to_u300_gpio(struct gpio_chip *chip)
        return container_of(chip, struct u300_gpio, chip);
 }
 
-static int u300_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       /*
-        * Map back to global GPIO space and request muxing, the direction
-        * parameter does not matter for this controller.
-        */
-       int gpio = chip->base + offset;
-
-       return pinctrl_request_gpio(gpio);
-}
-
-static void u300_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       int gpio = chip->base + offset;
-
-       pinctrl_free_gpio(gpio);
-}
-
 static int u300_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
        struct u300_gpio *gpio = to_u300_gpio(chip);
@@ -417,8 +399,8 @@ int u300_gpio_config_set(struct gpio_chip *chip, unsigned offset,
 static struct gpio_chip u300_gpio_chip = {
        .label                  = "u300-gpio-chip",
        .owner                  = THIS_MODULE,
-       .request                = u300_gpio_request,
-       .free                   = u300_gpio_free,
+       .request                = gpiochip_generic_request,
+       .free                   = gpiochip_generic_free,
        .get                    = u300_gpio_get,
        .set                    = u300_gpio_set,
        .direction_input        = u300_gpio_direction_input,
index 11f8b835d3b64f61fde81bc77c2b5ecf931fd351..38a7799f8257e1fe18756d208b7dfb776775f39b 100644 (file)
@@ -169,16 +169,6 @@ static struct pinmux_ops dc_pmxops = {
        .gpio_request_enable    = dc_pmx_request_gpio,
 };
 
-static int dc_gpio_request(struct gpio_chip *chip, unsigned gpio)
-{
-       return pinctrl_request_gpio(chip->base + gpio);
-}
-
-static void dc_gpio_free(struct gpio_chip *chip, unsigned gpio)
-{
-       pinctrl_free_gpio(chip->base + gpio);
-}
-
 static int dc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
 {
        struct dc_pinmap *pmap = container_of(chip, struct dc_pinmap, chip);
@@ -255,8 +245,8 @@ static int dc_gpiochip_add(struct dc_pinmap *pmap, struct device_node *np)
 
        chip->label             = DRIVER_NAME;
        chip->dev               = pmap->dev;
-       chip->request           = dc_gpio_request;
-       chip->free              = dc_gpio_free;
+       chip->request           = gpiochip_generic_request;
+       chip->free              = gpiochip_generic_free;
        chip->direction_input   = dc_gpio_direction_input;
        chip->direction_output  = dc_gpio_direction_output;
        chip->get               = dc_gpio_get;
index 952b1c62388773eb5594b03c7b0a639061c8f4b9..85c9046c690e2c44ab0da44571ac75a252da9708 100644 (file)
@@ -1171,16 +1171,6 @@ static struct pinctrl_desc pistachio_pinctrl_desc = {
        .confops = &pistachio_pinconf_ops,
 };
 
-static int pistachio_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void pistachio_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static int pistachio_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
 {
        struct pistachio_gpio_bank *bank = gc_to_bank(chip);
@@ -1332,8 +1322,8 @@ static void pistachio_gpio_irq_handler(struct irq_desc *desc)
                .npins = _npins,                                        \
                .gpio_chip = {                                          \
                        .label = "GPIO" #_bank,                         \
-                       .request = pistachio_gpio_request,              \
-                       .free = pistachio_gpio_free,                    \
+                       .request = gpiochip_generic_request,            \
+                       .free = gpiochip_generic_free,                  \
                        .get_direction = pistachio_gpio_get_direction,  \
                        .direction_input = pistachio_gpio_direction_input, \
                        .direction_output = pistachio_gpio_direction_output, \
index 88bb707e107ad8d9b8f14832d5edd4788a8b5cf6..a0651128e23acc68fbe139dc10da3e859da2b9a1 100644 (file)
@@ -1374,16 +1374,6 @@ static int rockchip_pinctrl_register(struct platform_device *pdev,
  * GPIO handling
  */
 
-static int rockchip_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void rockchip_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
 {
        struct rockchip_pin_bank *bank = gc_to_pin_bank(gc);
@@ -1461,8 +1451,8 @@ static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
 }
 
 static const struct gpio_chip rockchip_gpiolib_chip = {
-       .request = rockchip_gpio_request,
-       .free = rockchip_gpio_free,
+       .request = gpiochip_generic_request,
+       .free = gpiochip_generic_free,
        .set = rockchip_gpio_set,
        .get = rockchip_gpio_get,
        .direction_input = rockchip_gpio_direction_input,
@@ -2089,6 +2079,21 @@ static struct rockchip_pin_ctrl rk2928_pin_ctrl = {
                .pull_calc_reg          = rk2928_calc_pull_reg_and_bit,
 };
 
+static struct rockchip_pin_bank rk3036_pin_banks[] = {
+       PIN_BANK(0, 32, "gpio0"),
+       PIN_BANK(1, 32, "gpio1"),
+       PIN_BANK(2, 32, "gpio2"),
+};
+
+static struct rockchip_pin_ctrl rk3036_pin_ctrl = {
+               .pin_banks              = rk3036_pin_banks,
+               .nr_banks               = ARRAY_SIZE(rk3036_pin_banks),
+               .label                  = "RK3036-GPIO",
+               .type                   = RK2928,
+               .grf_mux_offset         = 0xa8,
+               .pull_calc_reg          = rk2928_calc_pull_reg_and_bit,
+};
+
 static struct rockchip_pin_bank rk3066a_pin_banks[] = {
        PIN_BANK(0, 32, "gpio0"),
        PIN_BANK(1, 32, "gpio1"),
@@ -2207,6 +2212,8 @@ static struct rockchip_pin_ctrl rk3368_pin_ctrl = {
 static const struct of_device_id rockchip_pinctrl_dt_match[] = {
        { .compatible = "rockchip,rk2928-pinctrl",
                .data = (void *)&rk2928_pin_ctrl },
+       { .compatible = "rockchip,rk3036-pinctrl",
+               .data = (void *)&rk3036_pin_ctrl },
        { .compatible = "rockchip,rk3066a-pinctrl",
                .data = (void *)&rk3066a_pin_ctrl },
        { .compatible = "rockchip,rk3066b-pinctrl",
index 389526e704fb0eb68125e31cee1363d78c3caf8b..b58d3f29148a402120a2993cc57a2d0eee1df563 100644 (file)
@@ -742,16 +742,6 @@ static void st_gpio_direction(struct st_gpio_bank *bank,
        }
 }
 
-static int st_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void st_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static int st_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
        struct st_gpio_bank *bank = gpio_chip_to_bank(chip);
@@ -1490,8 +1480,8 @@ static void st_gpio_irqmux_handler(struct irq_desc *desc)
 }
 
 static struct gpio_chip st_gpio_template = {
-       .request                = st_gpio_request,
-       .free                   = st_gpio_free,
+       .request                = gpiochip_generic_request,
+       .free                   = gpiochip_generic_free,
        .get                    = st_gpio_get,
        .set                    = st_gpio_set,
        .direction_input        = st_gpio_direction_input,
index 2651d04bd1beb8a59b9487fad6d2da1acc4ac18c..84a43e61295267aa5dfe07fc155095ac5ef390d3 100644 (file)
@@ -760,24 +760,15 @@ static const char * const tegra124_pcie_groups[] = {
        "pcie-2",
        "pcie-3",
        "pcie-4",
-       "sata-0",
 };
 
 static const char * const tegra124_usb3_groups[] = {
        "pcie-0",
        "pcie-1",
-       "pcie-2",
-       "pcie-3",
-       "pcie-4",
        "sata-0",
 };
 
 static const char * const tegra124_sata_groups[] = {
-       "pcie-0",
-       "pcie-1",
-       "pcie-2",
-       "pcie-3",
-       "pcie-4",
        "sata-0",
 };
 
index c349911708ef8e98b868dc0ed39ceec9b72f3605..b89ad3c0c731318ca6d5ce4896d1cbab0ef2a771 100644 (file)
@@ -668,7 +668,7 @@ static int tz1090_pdc_pinconf_reg(struct pinctrl_dev *pctldev,
                break;
        default:
                return -ENOTSUPP;
-       };
+       }
 
        /* Only input bias parameters supported */
        *reg = REG_GPIO_CONTROL2;
@@ -801,7 +801,7 @@ static int tz1090_pdc_pinconf_group_reg(struct pinctrl_dev *pctldev,
                break;
        default:
                return -ENOTSUPP;
-       };
+       }
 
        /* Calculate field information */
        *mask = (BIT(*width) - 1) << *shift;
index 6d07a2f64d978439d06ec15e47f5c65ab83945b6..5425299d759dcb044d2fbc9220d9628f8a15651d 100644 (file)
@@ -1661,7 +1661,7 @@ static int tz1090_pinconf_reg(struct pinctrl_dev *pctldev,
                break;
        default:
                return -ENOTSUPP;
-       };
+       }
 
        /* Only input bias parameters supported */
        pu = &tz1090_pinconf_pullup[pin];
@@ -1790,7 +1790,7 @@ static int tz1090_pinconf_group_reg(struct pinctrl_dev *pctldev,
                break;
        default:
                return -ENOTSUPP;
-       };
+       }
 
        /* Calculate field information */
        *shift = g->slw_bit * *width;
index 779950c62e53d238e7a4c35d7290ca66475fed86..ae724bdab3d324cbf9018e8f2bdaf6fc44103f02 100644 (file)
@@ -682,28 +682,14 @@ static int xway_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, int val)
        return 0;
 }
 
-static int xway_gpio_req(struct gpio_chip *chip, unsigned offset)
-{
-       int gpio = chip->base + offset;
-
-       return pinctrl_request_gpio(gpio);
-}
-
-static void xway_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       int gpio = chip->base + offset;
-
-       pinctrl_free_gpio(gpio);
-}
-
 static struct gpio_chip xway_chip = {
        .label = "gpio-xway",
        .direction_input = xway_gpio_dir_in,
        .direction_output = xway_gpio_dir_out,
        .get = xway_gpio_get,
        .set = xway_gpio_set,
-       .request = xway_gpio_req,
-       .free = xway_gpio_free,
+       .request = gpiochip_generic_request,
+       .free = gpiochip_generic_free,
        .base = -1,
 };
 
index 5aafea8c6590a9625930e784f027e61c43b7a0ba..d57b5eca7b98350ecc2e9b708f05e7457a26db16 100644 (file)
@@ -3,7 +3,7 @@
  *
  *  Copyright (C) 2014 Xilinx
  *
- *  SÃ\83¶ren Brinkmann <soren.brinkmann@xilinx.com>
+ *  Sören Brinkmann <soren.brinkmann@xilinx.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
@@ -1230,8 +1230,18 @@ static struct platform_driver zynq_pinctrl_driver = {
        .remove = zynq_pinctrl_remove,
 };
 
-module_platform_driver(zynq_pinctrl_driver);
+static int __init zynq_pinctrl_init(void)
+{
+       return platform_driver_register(&zynq_pinctrl_driver);
+}
+arch_initcall(zynq_pinctrl_init);
+
+static void __exit zynq_pinctrl_exit(void)
+{
+       platform_driver_unregister(&zynq_pinctrl_driver);
+}
+module_exit(zynq_pinctrl_exit);
 
-MODULE_AUTHOR("SÃ\83¶ren Brinkmann <soren.brinkmann@xilinx.com>");
+MODULE_AUTHOR("Sören Brinkmann <soren.brinkmann@xilinx.com>");
 MODULE_DESCRIPTION("Xilinx Zynq pinctrl driver");
 MODULE_LICENSE("GPL");
index a0c7407c1cac486b8609283f0b06350e124468da..146264a41ec8844876e81ecd77eda16588c80950 100644 (file)
@@ -458,18 +458,6 @@ static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
        spin_unlock_irqrestore(&pctrl->lock, flags);
 }
 
-static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       int gpio = chip->base + offset;
-       return pinctrl_request_gpio(gpio);
-}
-
-static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       int gpio = chip->base + offset;
-       return pinctrl_free_gpio(gpio);
-}
-
 #ifdef CONFIG_DEBUG_FS
 #include <linux/seq_file.h>
 
@@ -527,8 +515,8 @@ static struct gpio_chip msm_gpio_template = {
        .direction_output = msm_gpio_direction_output,
        .get              = msm_gpio_get,
        .set              = msm_gpio_set,
-       .request          = msm_gpio_request,
-       .free             = msm_gpio_free,
+       .request          = gpiochip_generic_request,
+       .free             = gpiochip_generic_free,
        .dbg_show         = msm_gpio_dbg_show,
 };
 
index bd1e24598e12b88b69f329cf574c63c156217153..6c42ca14d2fd315d9e43faca2a48315815d53bd8 100644 (file)
@@ -546,16 +546,6 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
        pmic_gpio_config_set(state->ctrl, pin, &config, 1);
 }
 
-static int pmic_gpio_request(struct gpio_chip *chip, unsigned base)
-{
-       return pinctrl_request_gpio(chip->base + base);
-}
-
-static void pmic_gpio_free(struct gpio_chip *chip, unsigned base)
-{
-       pinctrl_free_gpio(chip->base + base);
-}
-
 static int pmic_gpio_of_xlate(struct gpio_chip *chip,
                              const struct of_phandle_args *gpio_desc,
                              u32 *flags)
@@ -595,8 +585,8 @@ static const struct gpio_chip pmic_gpio_gpio_template = {
        .direction_output       = pmic_gpio_direction_output,
        .get                    = pmic_gpio_get,
        .set                    = pmic_gpio_set,
-       .request                = pmic_gpio_request,
-       .free                   = pmic_gpio_free,
+       .request                = gpiochip_generic_request,
+       .free                   = gpiochip_generic_free,
        .of_xlate               = pmic_gpio_of_xlate,
        .to_irq                 = pmic_gpio_to_irq,
        .dbg_show               = pmic_gpio_dbg_show,
index e3be3ce2cada2ee50b9ca0f64e93b266c26ea5ba..9ce0e30e33e81b024ecee8169d86b165ce1d69d7 100644 (file)
@@ -604,16 +604,6 @@ static void pmic_mpp_set(struct gpio_chip *chip, unsigned pin, int value)
        pmic_mpp_config_set(state->ctrl, pin, &config, 1);
 }
 
-static int pmic_mpp_request(struct gpio_chip *chip, unsigned base)
-{
-       return pinctrl_request_gpio(chip->base + base);
-}
-
-static void pmic_mpp_free(struct gpio_chip *chip, unsigned base)
-{
-       pinctrl_free_gpio(chip->base + base);
-}
-
 static int pmic_mpp_of_xlate(struct gpio_chip *chip,
                             const struct of_phandle_args *gpio_desc,
                             u32 *flags)
@@ -653,8 +643,8 @@ static const struct gpio_chip pmic_mpp_gpio_template = {
        .direction_output       = pmic_mpp_direction_output,
        .get                    = pmic_mpp_get,
        .set                    = pmic_mpp_set,
-       .request                = pmic_mpp_request,
-       .free                   = pmic_mpp_free,
+       .request                = gpiochip_generic_request,
+       .free                   = gpiochip_generic_free,
        .of_xlate               = pmic_mpp_of_xlate,
        .to_irq                 = pmic_mpp_to_irq,
        .dbg_show               = pmic_mpp_dbg_show,
index e1a3721bc8e5814fbef5a39184170407436cb193..d809c9eaa3231817512bdda858ad208d0f1138e4 100644 (file)
@@ -584,7 +584,7 @@ static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
 }
 
 #else
-#define msm_gpio_dbg_show NULL
+#define pm8xxx_gpio_dbg_show NULL
 #endif
 
 static struct gpio_chip pm8xxx_gpio_template = {
index 6652b8d7f707aefc5656edd348f95e0d139c21da..8982027de8e8b528f026f38d58ccdf692901635c 100644 (file)
@@ -639,7 +639,7 @@ static void pm8xxx_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
 }
 
 #else
-#define msm_mpp_dbg_show NULL
+#define pm8xxx_mpp_dbg_show NULL
 #endif
 
 static struct gpio_chip pm8xxx_mpp_template = {
index 9ce0b8619d4c6eb90715dd06ae50921266074307..82dc109f7ed46629405d7663f2b853c50ed647e5 100644 (file)
@@ -284,7 +284,7 @@ static void exynos5440_dt_free_map(struct pinctrl_dev *pctldev,
                        if (!idx)
                                kfree(map[idx].data.configs.group_or_pin);
                }
-       };
+       }
 
        kfree(map);
 }
index c760bf43d116cfaa83370d640deefd38abb9f407..3f622ccd8eabd74a3f0df889298254dbcdee1510 100644 (file)
@@ -888,19 +888,9 @@ static int samsung_pinctrl_register(struct platform_device *pdev,
        return 0;
 }
 
-static int samsung_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void samsung_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static const struct gpio_chip samsung_gpiolib_chip = {
-       .request = samsung_gpio_request,
-       .free = samsung_gpio_free,
+       .request = gpiochip_generic_request,
+       .free = gpiochip_generic_free,
        .set = samsung_gpio_set,
        .get = samsung_gpio_get,
        .direction_input = samsung_gpio_direction_input,
index 8e024c9c9115c448fbcd2d7b8bfbb7a413620c7d..35d6e95fa21fbbdd6cdf8bb9847bffad13ad88ce 100644 (file)
@@ -65,6 +65,11 @@ config PINCTRL_PFC_R8A7794
        depends on ARCH_R8A7794
        select PINCTRL_SH_PFC
 
+config PINCTRL_PFC_R8A7795
+       def_bool y
+       depends on ARCH_R8A7795
+       select PINCTRL_SH_PFC
+
 config PINCTRL_PFC_SH7203
        def_bool y
        depends on CPU_SUBTYPE_SH7203
index ea2a60ef122a664a30fa5921c0916e5152067c6c..173305fa3811755a845ae39a81a11f2610ea1552 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_PINCTRL_PFC_R8A7790)     += pfc-r8a7790.o
 obj-$(CONFIG_PINCTRL_PFC_R8A7791)      += pfc-r8a7791.o
 obj-$(CONFIG_PINCTRL_PFC_R8A7793)      += pfc-r8a7791.o
 obj-$(CONFIG_PINCTRL_PFC_R8A7794)      += pfc-r8a7794.o
+obj-$(CONFIG_PINCTRL_PFC_R8A7795)      += pfc-r8a7795.o
 obj-$(CONFIG_PINCTRL_PFC_SH7203)       += pfc-sh7203.o
 obj-$(CONFIG_PINCTRL_PFC_SH7264)       += pfc-sh7264.o
 obj-$(CONFIG_PINCTRL_PFC_SH7269)       += pfc-sh7269.o
index fb9c4480523431c878a1098df47c92b019ddefd1..181ea98a63b7ab02d367581b4027997ea0f6a574 100644 (file)
@@ -272,7 +272,7 @@ static int sh_pfc_get_config_reg(struct sh_pfc *pfc, u16 enum_id,
 static int sh_pfc_mark_to_enum(struct sh_pfc *pfc, u16 mark, int pos,
                              u16 *enum_idp)
 {
-       const u16 *data = pfc->info->gpio_data;
+       const u16 *data = pfc->info->pinmux_data;
        unsigned int k;
 
        if (pos) {
@@ -280,7 +280,7 @@ static int sh_pfc_mark_to_enum(struct sh_pfc *pfc, u16 mark, int pos,
                return pos + 1;
        }
 
-       for (k = 0; k < pfc->info->gpio_data_size; k++) {
+       for (k = 0; k < pfc->info->pinmux_data_size; k++) {
                if (data[k] == mark) {
                        *enum_idp = data[k + 1];
                        return k + 1;
@@ -489,6 +489,12 @@ static const struct of_device_id sh_pfc_of_table[] = {
                .data = &r8a7794_pinmux_info,
        },
 #endif
+#ifdef CONFIG_PINCTRL_PFC_R8A7795
+       {
+               .compatible = "renesas,pfc-r8a7795",
+               .data = &r8a7795_pinmux_info,
+       },
+#endif
 #ifdef CONFIG_PINCTRL_PFC_SH73A0
        {
                .compatible = "renesas,pfc-sh73a0",
@@ -587,12 +593,6 @@ static int sh_pfc_remove(struct platform_device *pdev)
 }
 
 static const struct platform_device_id sh_pfc_id_table[] = {
-#ifdef CONFIG_PINCTRL_PFC_R8A7778
-       { "pfc-r8a7778", (kernel_ulong_t)&r8a7778_pinmux_info },
-#endif
-#ifdef CONFIG_PINCTRL_PFC_R8A7779
-       { "pfc-r8a7779", (kernel_ulong_t)&r8a7779_pinmux_info },
-#endif
 #ifdef CONFIG_PINCTRL_PFC_SH7203
        { "pfc-sh7203", (kernel_ulong_t)&sh7203_pinmux_info },
 #endif
index 4c3c37bf7161804d4481f1860f8d0c7c6687eddc..62f53b22ae85004628672533a446f6fade5a1a32 100644 (file)
@@ -46,7 +46,9 @@ struct sh_pfc {
        unsigned int nr_gpio_pins;
 
        struct sh_pfc_chip *gpio;
+#ifdef CONFIG_SUPERH
        struct sh_pfc_chip *func;
+#endif
 
        struct sh_pfc_pinctrl *pinctrl;
 };
@@ -73,6 +75,7 @@ extern const struct sh_pfc_soc_info r8a7790_pinmux_info;
 extern const struct sh_pfc_soc_info r8a7791_pinmux_info;
 extern const struct sh_pfc_soc_info r8a7793_pinmux_info;
 extern const struct sh_pfc_soc_info r8a7794_pinmux_info;
+extern const struct sh_pfc_soc_info r8a7795_pinmux_info;
 extern const struct sh_pfc_soc_info sh7203_pinmux_info;
 extern const struct sh_pfc_soc_info sh7264_pinmux_info;
 extern const struct sh_pfc_soc_info sh7269_pinmux_info;
index ba353735ecf2be9a1ca39c29e516c850beaa1ce1..db3f09aa8993cdda59a2cec8369d2d13047a230e 100644 (file)
@@ -219,10 +219,7 @@ static int gpio_pin_to_irq(struct gpio_chip *gc, unsigned offset)
        return -ENOSYS;
 
 found:
-       if (pfc->num_irqs)
-               return pfc->irqs[i];
-       else
-               return pfc->info->gpio_irq[i].irq;
+       return pfc->irqs[i];
 }
 
 static int gpio_pin_setup(struct sh_pfc_chip *chip)
@@ -261,6 +258,7 @@ static int gpio_pin_setup(struct sh_pfc_chip *chip)
  * Function GPIOs
  */
 
+#ifdef CONFIG_SUPERH
 static int gpio_function_request(struct gpio_chip *gc, unsigned offset)
 {
        static bool __print_once;
@@ -286,17 +284,12 @@ static int gpio_function_request(struct gpio_chip *gc, unsigned offset)
        return ret;
 }
 
-static void gpio_function_free(struct gpio_chip *gc, unsigned offset)
-{
-}
-
 static int gpio_function_setup(struct sh_pfc_chip *chip)
 {
        struct sh_pfc *pfc = chip->pfc;
        struct gpio_chip *gc = &chip->gpio_chip;
 
        gc->request = gpio_function_request;
-       gc->free = gpio_function_free;
 
        gc->label = pfc->info->name;
        gc->owner = THIS_MODULE;
@@ -305,6 +298,7 @@ static int gpio_function_setup(struct sh_pfc_chip *chip)
 
        return 0;
 }
+#endif
 
 /* -----------------------------------------------------------------------------
  * Register/unregister
@@ -344,7 +338,6 @@ int sh_pfc_register_gpiochip(struct sh_pfc *pfc)
        struct sh_pfc_chip *chip;
        phys_addr_t address;
        unsigned int i;
-       int ret;
 
        if (pfc->info->data_regs == NULL)
                return 0;
@@ -367,7 +360,7 @@ int sh_pfc_register_gpiochip(struct sh_pfc *pfc)
                return 0;
 
        /* If we have IRQ resources make sure their number is correct. */
-       if (pfc->num_irqs && pfc->num_irqs != pfc->info->gpio_irq_size) {
+       if (pfc->num_irqs != pfc->info->gpio_irq_size) {
                dev_err(pfc->dev, "invalid number of IRQ resources\n");
                return -EINVAL;
        }
@@ -379,20 +372,26 @@ int sh_pfc_register_gpiochip(struct sh_pfc *pfc)
 
        pfc->gpio = chip;
 
-       /* Register the GPIO to pin mappings. As pins with GPIO ports must come
-        * first in the ranges, skip the pins without GPIO ports by stopping at
-        * the first range that contains such a pin.
+       if (IS_ENABLED(CONFIG_OF) && pfc->dev->of_node)
+               return 0;
+
+#ifdef CONFIG_SUPERH
+       /*
+        * Register the GPIO to pin mappings. As pins with GPIO ports
+        * must come first in the ranges, skip the pins without GPIO
+        * ports by stopping at the first range that contains such a
+        * pin.
         */
        for (i = 0; i < pfc->nr_ranges; ++i) {
                const struct sh_pfc_pin_range *range = &pfc->ranges[i];
+               int ret;
 
                if (range->start >= pfc->nr_gpio_pins)
                        break;
 
                ret = gpiochip_add_pin_range(&chip->gpio_chip,
-                                            dev_name(pfc->dev),
-                                            range->start, range->start,
-                                            range->end - range->start + 1);
+                       dev_name(pfc->dev), range->start, range->start,
+                       range->end - range->start + 1);
                if (ret < 0)
                        return ret;
        }
@@ -406,6 +405,7 @@ int sh_pfc_register_gpiochip(struct sh_pfc *pfc)
                return PTR_ERR(chip);
 
        pfc->func = chip;
+#endif /* CONFIG_SUPERH */
 
        return 0;
 }
@@ -413,7 +413,8 @@ int sh_pfc_register_gpiochip(struct sh_pfc *pfc)
 int sh_pfc_unregister_gpiochip(struct sh_pfc *pfc)
 {
        gpiochip_remove(&pfc->gpio->gpio_chip);
+#ifdef CONFIG_SUPERH
        gpiochip_remove(&pfc->func->gpio_chip);
-
+#endif
        return 0;
 }
index 849c6943ed308ee00ee15784bb3454667575a914..02118ab336fcbd6c7b91bc7058e0460a0834e5c1 100644 (file)
@@ -1706,6 +1706,6 @@ const struct sh_pfc_soc_info emev2_pinmux_info = {
 
        .cfg_regs       = pinmux_config_regs,
 
-       .gpio_data      = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data    = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index ba18d2e65e6745630811fb8110cfb34efd5637e0..d9d9228b15faf6f32c8677c1316abfc1d3a0af5b 100644 (file)
@@ -2603,64 +2603,64 @@ static const struct pinmux_data_reg pinmux_data_regs[] = {
 };
 
 static const struct pinmux_irq pinmux_irqs[] = {
-       PINMUX_IRQ(irq_pin(0), 0),
-       PINMUX_IRQ(irq_pin(1), 1),
-       PINMUX_IRQ(irq_pin(2), 2),
-       PINMUX_IRQ(irq_pin(3), 3),
-       PINMUX_IRQ(irq_pin(4), 4),
-       PINMUX_IRQ(irq_pin(5), 5),
-       PINMUX_IRQ(irq_pin(6), 6),
-       PINMUX_IRQ(irq_pin(7), 7),
-       PINMUX_IRQ(irq_pin(8), 8),
-       PINMUX_IRQ(irq_pin(9), 9),
-       PINMUX_IRQ(irq_pin(10), 10),
-       PINMUX_IRQ(irq_pin(11), 11),
-       PINMUX_IRQ(irq_pin(12), 12),
-       PINMUX_IRQ(irq_pin(13), 13),
-       PINMUX_IRQ(irq_pin(14), 14),
-       PINMUX_IRQ(irq_pin(15), 15),
-       PINMUX_IRQ(irq_pin(16), 320),
-       PINMUX_IRQ(irq_pin(17), 321),
-       PINMUX_IRQ(irq_pin(18), 85),
-       PINMUX_IRQ(irq_pin(19), 84),
-       PINMUX_IRQ(irq_pin(20), 160),
-       PINMUX_IRQ(irq_pin(21), 161),
-       PINMUX_IRQ(irq_pin(22), 162),
-       PINMUX_IRQ(irq_pin(23), 163),
-       PINMUX_IRQ(irq_pin(24), 175),
-       PINMUX_IRQ(irq_pin(25), 176),
-       PINMUX_IRQ(irq_pin(26), 177),
-       PINMUX_IRQ(irq_pin(27), 178),
-       PINMUX_IRQ(irq_pin(28), 322),
-       PINMUX_IRQ(irq_pin(29), 323),
-       PINMUX_IRQ(irq_pin(30), 324),
-       PINMUX_IRQ(irq_pin(31), 192),
-       PINMUX_IRQ(irq_pin(32), 193),
-       PINMUX_IRQ(irq_pin(33), 194),
-       PINMUX_IRQ(irq_pin(34), 195),
-       PINMUX_IRQ(irq_pin(35), 196),
-       PINMUX_IRQ(irq_pin(36), 197),
-       PINMUX_IRQ(irq_pin(37), 198),
-       PINMUX_IRQ(irq_pin(38), 199),
-       PINMUX_IRQ(irq_pin(39), 200),
-       PINMUX_IRQ(irq_pin(40), 66),
-       PINMUX_IRQ(irq_pin(41), 102),
-       PINMUX_IRQ(irq_pin(42), 103),
-       PINMUX_IRQ(irq_pin(43), 109),
-       PINMUX_IRQ(irq_pin(44), 110),
-       PINMUX_IRQ(irq_pin(45), 111),
-       PINMUX_IRQ(irq_pin(46), 112),
-       PINMUX_IRQ(irq_pin(47), 113),
-       PINMUX_IRQ(irq_pin(48), 114),
-       PINMUX_IRQ(irq_pin(49), 115),
-       PINMUX_IRQ(irq_pin(50), 301),
-       PINMUX_IRQ(irq_pin(51), 290),
-       PINMUX_IRQ(irq_pin(52), 296),
-       PINMUX_IRQ(irq_pin(53), 325),
-       PINMUX_IRQ(irq_pin(54), 326),
-       PINMUX_IRQ(irq_pin(55), 327),
-       PINMUX_IRQ(irq_pin(56), 328),
-       PINMUX_IRQ(irq_pin(57), 329),
+       PINMUX_IRQ(0),          /* IRQ0 */
+       PINMUX_IRQ(1),          /* IRQ1 */
+       PINMUX_IRQ(2),          /* IRQ2 */
+       PINMUX_IRQ(3),          /* IRQ3 */
+       PINMUX_IRQ(4),          /* IRQ4 */
+       PINMUX_IRQ(5),          /* IRQ5 */
+       PINMUX_IRQ(6),          /* IRQ6 */
+       PINMUX_IRQ(7),          /* IRQ7 */
+       PINMUX_IRQ(8),          /* IRQ8 */
+       PINMUX_IRQ(9),          /* IRQ9 */
+       PINMUX_IRQ(10),         /* IRQ10 */
+       PINMUX_IRQ(11),         /* IRQ11 */
+       PINMUX_IRQ(12),         /* IRQ12 */
+       PINMUX_IRQ(13),         /* IRQ13 */
+       PINMUX_IRQ(14),         /* IRQ14 */
+       PINMUX_IRQ(15),         /* IRQ15 */
+       PINMUX_IRQ(320),        /* IRQ16 */
+       PINMUX_IRQ(321),        /* IRQ17 */
+       PINMUX_IRQ(85),         /* IRQ18 */
+       PINMUX_IRQ(84),         /* IRQ19 */
+       PINMUX_IRQ(160),        /* IRQ20 */
+       PINMUX_IRQ(161),        /* IRQ21 */
+       PINMUX_IRQ(162),        /* IRQ22 */
+       PINMUX_IRQ(163),        /* IRQ23 */
+       PINMUX_IRQ(175),        /* IRQ24 */
+       PINMUX_IRQ(176),        /* IRQ25 */
+       PINMUX_IRQ(177),        /* IRQ26 */
+       PINMUX_IRQ(178),        /* IRQ27 */
+       PINMUX_IRQ(322),        /* IRQ28 */
+       PINMUX_IRQ(323),        /* IRQ29 */
+       PINMUX_IRQ(324),        /* IRQ30 */
+       PINMUX_IRQ(192),        /* IRQ31 */
+       PINMUX_IRQ(193),        /* IRQ32 */
+       PINMUX_IRQ(194),        /* IRQ33 */
+       PINMUX_IRQ(195),        /* IRQ34 */
+       PINMUX_IRQ(196),        /* IRQ35 */
+       PINMUX_IRQ(197),        /* IRQ36 */
+       PINMUX_IRQ(198),        /* IRQ37 */
+       PINMUX_IRQ(199),        /* IRQ38 */
+       PINMUX_IRQ(200),        /* IRQ39 */
+       PINMUX_IRQ(66),         /* IRQ40 */
+       PINMUX_IRQ(102),        /* IRQ41 */
+       PINMUX_IRQ(103),        /* IRQ42 */
+       PINMUX_IRQ(109),        /* IRQ43 */
+       PINMUX_IRQ(110),        /* IRQ44 */
+       PINMUX_IRQ(111),        /* IRQ45 */
+       PINMUX_IRQ(112),        /* IRQ46 */
+       PINMUX_IRQ(113),        /* IRQ47 */
+       PINMUX_IRQ(114),        /* IRQ48 */
+       PINMUX_IRQ(115),        /* IRQ49 */
+       PINMUX_IRQ(301),        /* IRQ50 */
+       PINMUX_IRQ(290),        /* IRQ51 */
+       PINMUX_IRQ(296),        /* IRQ52 */
+       PINMUX_IRQ(325),        /* IRQ53 */
+       PINMUX_IRQ(326),        /* IRQ54 */
+       PINMUX_IRQ(327),        /* IRQ55 */
+       PINMUX_IRQ(328),        /* IRQ56 */
+       PINMUX_IRQ(329),        /* IRQ57 */
 };
 
 #define PORTCR_PULMD_OFF (0 << 6)
@@ -2734,11 +2734,11 @@ const struct sh_pfc_soc_info r8a73a4_pinmux_info = {
        .functions = pinmux_functions,
        .nr_functions = ARRAY_SIZE(pinmux_functions),
 
-       .cfg_regs       = pinmux_config_regs,
-       .data_regs      = pinmux_data_regs,
+       .cfg_regs = pinmux_config_regs,
+       .data_regs = pinmux_data_regs,
 
-       .gpio_data      = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 
        .gpio_irq = pinmux_irqs,
        .gpio_irq_size = ARRAY_SIZE(pinmux_irqs),
index 82ef1862dd1ba808f22a951419efca45945b3422..279e9dd442e4472989e32cdc9126d23c88593693 100644 (file)
@@ -3651,38 +3651,38 @@ static const struct pinmux_data_reg pinmux_data_regs[] = {
 };
 
 static const struct pinmux_irq pinmux_irqs[] = {
-       PINMUX_IRQ(irq_pin(0), 2,   13),        /* IRQ0A */
-       PINMUX_IRQ(irq_pin(1), 20),             /* IRQ1A */
-       PINMUX_IRQ(irq_pin(2), 11,  12),        /* IRQ2A */
-       PINMUX_IRQ(irq_pin(3), 10,  14),        /* IRQ3A */
-       PINMUX_IRQ(irq_pin(4), 15,  172),       /* IRQ4A */
-       PINMUX_IRQ(irq_pin(5), 0,   1),         /* IRQ5A */
-       PINMUX_IRQ(irq_pin(6), 121, 173),       /* IRQ6A */
-       PINMUX_IRQ(irq_pin(7), 120, 209),       /* IRQ7A */
-       PINMUX_IRQ(irq_pin(8), 119),            /* IRQ8A */
-       PINMUX_IRQ(irq_pin(9), 118, 210),       /* IRQ9A */
-       PINMUX_IRQ(irq_pin(10), 19),            /* IRQ10A */
-       PINMUX_IRQ(irq_pin(11), 104),           /* IRQ11A */
-       PINMUX_IRQ(irq_pin(12), 42,  97),       /* IRQ12A */
-       PINMUX_IRQ(irq_pin(13), 64,  98),       /* IRQ13A */
-       PINMUX_IRQ(irq_pin(14), 63,  99),       /* IRQ14A */
-       PINMUX_IRQ(irq_pin(15), 62,  100),      /* IRQ15A */
-       PINMUX_IRQ(irq_pin(16), 68,  211),      /* IRQ16A */
-       PINMUX_IRQ(irq_pin(17), 69),            /* IRQ17A */
-       PINMUX_IRQ(irq_pin(18), 70),            /* IRQ18A */
-       PINMUX_IRQ(irq_pin(19), 71),            /* IRQ19A */
-       PINMUX_IRQ(irq_pin(20), 67),            /* IRQ20A */
-       PINMUX_IRQ(irq_pin(21), 202),           /* IRQ21A */
-       PINMUX_IRQ(irq_pin(22), 95),            /* IRQ22A */
-       PINMUX_IRQ(irq_pin(23), 96),            /* IRQ23A */
-       PINMUX_IRQ(irq_pin(24), 180),           /* IRQ24A */
-       PINMUX_IRQ(irq_pin(25), 38),            /* IRQ25A */
-       PINMUX_IRQ(irq_pin(26), 58,  81),       /* IRQ26A */
-       PINMUX_IRQ(irq_pin(27), 57,  168),      /* IRQ27A */
-       PINMUX_IRQ(irq_pin(28), 56,  169),      /* IRQ28A */
-       PINMUX_IRQ(irq_pin(29), 50,  170),      /* IRQ29A */
-       PINMUX_IRQ(irq_pin(30), 49,  171),      /* IRQ30A */
-       PINMUX_IRQ(irq_pin(31), 41,  167),      /* IRQ31A */
+       PINMUX_IRQ(2,   13),    /* IRQ0A */
+       PINMUX_IRQ(20),         /* IRQ1A */
+       PINMUX_IRQ(11,  12),    /* IRQ2A */
+       PINMUX_IRQ(10,  14),    /* IRQ3A */
+       PINMUX_IRQ(15,  172),   /* IRQ4A */
+       PINMUX_IRQ(0,   1),     /* IRQ5A */
+       PINMUX_IRQ(121, 173),   /* IRQ6A */
+       PINMUX_IRQ(120, 209),   /* IRQ7A */
+       PINMUX_IRQ(119),        /* IRQ8A */
+       PINMUX_IRQ(118, 210),   /* IRQ9A */
+       PINMUX_IRQ(19),         /* IRQ10A */
+       PINMUX_IRQ(104),        /* IRQ11A */
+       PINMUX_IRQ(42,  97),    /* IRQ12A */
+       PINMUX_IRQ(64,  98),    /* IRQ13A */
+       PINMUX_IRQ(63,  99),    /* IRQ14A */
+       PINMUX_IRQ(62,  100),   /* IRQ15A */
+       PINMUX_IRQ(68,  211),   /* IRQ16A */
+       PINMUX_IRQ(69),         /* IRQ17A */
+       PINMUX_IRQ(70),         /* IRQ18A */
+       PINMUX_IRQ(71),         /* IRQ19A */
+       PINMUX_IRQ(67),         /* IRQ20A */
+       PINMUX_IRQ(202),        /* IRQ21A */
+       PINMUX_IRQ(95),         /* IRQ22A */
+       PINMUX_IRQ(96),         /* IRQ23A */
+       PINMUX_IRQ(180),        /* IRQ24A */
+       PINMUX_IRQ(38),         /* IRQ25A */
+       PINMUX_IRQ(58,  81),    /* IRQ26A */
+       PINMUX_IRQ(57,  168),   /* IRQ27A */
+       PINMUX_IRQ(56,  169),   /* IRQ28A */
+       PINMUX_IRQ(50,  170),   /* IRQ29A */
+       PINMUX_IRQ(49,  171),   /* IRQ30A */
+       PINMUX_IRQ(41,  167),   /* IRQ31A */
 };
 
 #define PORTnCR_PULMD_OFF      (0 << 6)
@@ -3774,8 +3774,8 @@ const struct sh_pfc_soc_info r8a7740_pinmux_info = {
        .cfg_regs       = pinmux_config_regs,
        .data_regs      = pinmux_data_regs,
 
-       .gpio_data      = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data    = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 
        .gpio_irq       = pinmux_irqs,
        .gpio_irq_size  = ARRAY_SIZE(pinmux_irqs),
index c7d610d1f3efb8054a3358a2f6c087795d5705f7..bbd35dc1a0c4c35a512a501d5f7d3d3a3b67ea8c 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) 2013  Renesas Solutions Corp.
  * Copyright (C) 2013  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  * Copyright (C) 2013  Cogent Embedded, Inc.
+ * Copyright (C) 2015  Ulrich Hecht
  *
  * based on
  * Copyright (C) 2011  Renesas Solutions Corp.
  * GNU General Public License for more details.
  */
 
-#include <linux/platform_data/gpio-rcar.h>
+#include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include "core.h"
 #include "sh_pfc.h"
 
-#define PORT_GP_27(bank, fn, sfx)                                      \
-       PORT_GP_1(bank, 0,  fn, sfx), PORT_GP_1(bank, 1,  fn, sfx),     \
-       PORT_GP_1(bank, 2,  fn, sfx), PORT_GP_1(bank, 3,  fn, sfx),     \
-       PORT_GP_1(bank, 4,  fn, sfx), PORT_GP_1(bank, 5,  fn, sfx),     \
-       PORT_GP_1(bank, 6,  fn, sfx), PORT_GP_1(bank, 7,  fn, sfx),     \
-       PORT_GP_1(bank, 8,  fn, sfx), PORT_GP_1(bank, 9,  fn, sfx),     \
-       PORT_GP_1(bank, 10, fn, sfx), PORT_GP_1(bank, 11, fn, sfx),     \
-       PORT_GP_1(bank, 12, fn, sfx), PORT_GP_1(bank, 13, fn, sfx),     \
-       PORT_GP_1(bank, 14, fn, sfx), PORT_GP_1(bank, 15, fn, sfx),     \
-       PORT_GP_1(bank, 16, fn, sfx), PORT_GP_1(bank, 17, fn, sfx),     \
-       PORT_GP_1(bank, 18, fn, sfx), PORT_GP_1(bank, 19, fn, sfx),     \
-       PORT_GP_1(bank, 20, fn, sfx), PORT_GP_1(bank, 21, fn, sfx),     \
-       PORT_GP_1(bank, 22, fn, sfx), PORT_GP_1(bank, 23, fn, sfx),     \
-       PORT_GP_1(bank, 24, fn, sfx), PORT_GP_1(bank, 25, fn, sfx),     \
-       PORT_GP_1(bank, 26, fn, sfx)
+#define PORT_GP_PUP_1(bank, pin, fn, sfx)      \
+       PORT_GP_CFG_1(bank, pin, fn, sfx, SH_PFC_PIN_CFG_PULL_UP)
+
+#define PORT_GP_PUP_27(bank, fn, sfx)                                  \
+       PORT_GP_PUP_1(bank, 0,  fn, sfx), PORT_GP_PUP_1(bank, 1,  fn, sfx),     \
+       PORT_GP_PUP_1(bank, 2,  fn, sfx), PORT_GP_PUP_1(bank, 3,  fn, sfx),     \
+       PORT_GP_PUP_1(bank, 4,  fn, sfx), PORT_GP_PUP_1(bank, 5,  fn, sfx),     \
+       PORT_GP_PUP_1(bank, 6,  fn, sfx), PORT_GP_PUP_1(bank, 7,  fn, sfx),     \
+       PORT_GP_PUP_1(bank, 8,  fn, sfx), PORT_GP_PUP_1(bank, 9,  fn, sfx),     \
+       PORT_GP_PUP_1(bank, 10, fn, sfx), PORT_GP_PUP_1(bank, 11, fn, sfx),     \
+       PORT_GP_PUP_1(bank, 12, fn, sfx), PORT_GP_PUP_1(bank, 13, fn, sfx),     \
+       PORT_GP_PUP_1(bank, 14, fn, sfx), PORT_GP_PUP_1(bank, 15, fn, sfx),     \
+       PORT_GP_PUP_1(bank, 16, fn, sfx), PORT_GP_PUP_1(bank, 17, fn, sfx),     \
+       PORT_GP_PUP_1(bank, 18, fn, sfx), PORT_GP_PUP_1(bank, 19, fn, sfx),     \
+       PORT_GP_PUP_1(bank, 20, fn, sfx), PORT_GP_PUP_1(bank, 21, fn, sfx),     \
+       PORT_GP_PUP_1(bank, 22, fn, sfx), PORT_GP_PUP_1(bank, 23, fn, sfx),     \
+       PORT_GP_PUP_1(bank, 24, fn, sfx), PORT_GP_PUP_1(bank, 25, fn, sfx),     \
+       PORT_GP_PUP_1(bank, 26, fn, sfx)
 
 #define CPU_ALL_PORT(fn, sfx)          \
-       PORT_GP_32(0, fn, sfx),         \
-       PORT_GP_32(1, fn, sfx),         \
-       PORT_GP_32(2, fn, sfx),         \
-       PORT_GP_32(3, fn, sfx),         \
-       PORT_GP_27(4, fn, sfx)
+       PORT_GP_CFG_32(0, fn, sfx, SH_PFC_PIN_CFG_PULL_UP),             \
+       PORT_GP_CFG_32(1, fn, sfx, SH_PFC_PIN_CFG_PULL_UP),             \
+       PORT_GP_CFG_32(2, fn, sfx, SH_PFC_PIN_CFG_PULL_UP),             \
+       PORT_GP_CFG_32(3, fn, sfx, SH_PFC_PIN_CFG_PULL_UP),             \
+       PORT_GP_PUP_27(4, fn, sfx)
 
 enum {
        PINMUX_RESERVED = 0,
@@ -2905,8 +2911,222 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = {
        { },
 };
 
+#define PUPR0  0x100
+#define PUPR1  0x104
+#define PUPR2  0x108
+#define PUPR3  0x10c
+#define PUPR4  0x110
+#define PUPR5  0x114
+
+static const struct {
+       u16 reg : 11;
+       u16 bit : 5;
+} pullups[] = {
+       [RCAR_GP_PIN(0,  6)] = { PUPR0,  0 },   /* A0 */
+       [RCAR_GP_PIN(0,  7)] = { PUPR0,  1 },   /* A1 */
+       [RCAR_GP_PIN(0,  8)] = { PUPR0,  2 },   /* A2 */
+       [RCAR_GP_PIN(0,  9)] = { PUPR0,  3 },   /* A3 */
+       [RCAR_GP_PIN(0, 10)] = { PUPR0,  4 },   /* A4 */
+       [RCAR_GP_PIN(0, 11)] = { PUPR0,  5 },   /* A5 */
+       [RCAR_GP_PIN(0, 12)] = { PUPR0,  6 },   /* A6 */
+       [RCAR_GP_PIN(0, 13)] = { PUPR0,  7 },   /* A7 */
+       [RCAR_GP_PIN(0, 14)] = { PUPR0,  8 },   /* A8 */
+       [RCAR_GP_PIN(0, 15)] = { PUPR0,  9 },   /* A9 */
+       [RCAR_GP_PIN(0, 16)] = { PUPR0, 10 },   /* A10 */
+       [RCAR_GP_PIN(0, 17)] = { PUPR0, 11 },   /* A11 */
+       [RCAR_GP_PIN(0, 18)] = { PUPR0, 12 },   /* A12 */
+       [RCAR_GP_PIN(0, 19)] = { PUPR0, 13 },   /* A13 */
+       [RCAR_GP_PIN(0, 20)] = { PUPR0, 14 },   /* A14 */
+       [RCAR_GP_PIN(0, 21)] = { PUPR0, 15 },   /* A15 */
+       [RCAR_GP_PIN(0, 22)] = { PUPR0, 16 },   /* A16 */
+       [RCAR_GP_PIN(0, 23)] = { PUPR0, 17 },   /* A17 */
+       [RCAR_GP_PIN(0, 24)] = { PUPR0, 18 },   /* A18 */
+       [RCAR_GP_PIN(0, 25)] = { PUPR0, 19 },   /* A19 */
+       [RCAR_GP_PIN(0, 26)] = { PUPR0, 20 },   /* A20 */
+       [RCAR_GP_PIN(0, 27)] = { PUPR0, 21 },   /* A21 */
+       [RCAR_GP_PIN(0, 28)] = { PUPR0, 22 },   /* A22 */
+       [RCAR_GP_PIN(0, 29)] = { PUPR0, 23 },   /* A23 */
+       [RCAR_GP_PIN(0, 30)] = { PUPR0, 24 },   /* A24 */
+       [RCAR_GP_PIN(0, 31)] = { PUPR0, 25 },   /* A25 */
+       [RCAR_GP_PIN(1,  3)] = { PUPR0, 26 },   /* /EX_CS0 */
+       [RCAR_GP_PIN(1,  4)] = { PUPR0, 27 },   /* /EX_CS1 */
+       [RCAR_GP_PIN(1,  5)] = { PUPR0, 28 },   /* /EX_CS2 */
+       [RCAR_GP_PIN(1,  6)] = { PUPR0, 29 },   /* /EX_CS3 */
+       [RCAR_GP_PIN(1,  7)] = { PUPR0, 30 },   /* /EX_CS4 */
+       [RCAR_GP_PIN(1,  8)] = { PUPR0, 31 },   /* /EX_CS5 */
+
+       [RCAR_GP_PIN(0,  0)] = { PUPR1,  0 },   /* /PRESETOUT   */
+       [RCAR_GP_PIN(0,  5)] = { PUPR1,  1 },   /* /BS          */
+       [RCAR_GP_PIN(1,  0)] = { PUPR1,  2 },   /* RD//WR       */
+       [RCAR_GP_PIN(1,  1)] = { PUPR1,  3 },   /* /WE0         */
+       [RCAR_GP_PIN(1,  2)] = { PUPR1,  4 },   /* /WE1         */
+       [RCAR_GP_PIN(1, 11)] = { PUPR1,  5 },   /* EX_WAIT0     */
+       [RCAR_GP_PIN(1,  9)] = { PUPR1,  6 },   /* DREQ0        */
+       [RCAR_GP_PIN(1, 10)] = { PUPR1,  7 },   /* DACK0        */
+       [RCAR_GP_PIN(1, 12)] = { PUPR1,  8 },   /* IRQ0         */
+       [RCAR_GP_PIN(1, 13)] = { PUPR1,  9 },   /* IRQ1         */
+
+       [RCAR_GP_PIN(1, 22)] = { PUPR2,  0 },   /* DU0_DR0      */
+       [RCAR_GP_PIN(1, 23)] = { PUPR2,  1 },   /* DU0_DR1      */
+       [RCAR_GP_PIN(1, 24)] = { PUPR2,  2 },   /* DU0_DR2      */
+       [RCAR_GP_PIN(1, 25)] = { PUPR2,  3 },   /* DU0_DR3      */
+       [RCAR_GP_PIN(1, 26)] = { PUPR2,  4 },   /* DU0_DR4      */
+       [RCAR_GP_PIN(1, 27)] = { PUPR2,  5 },   /* DU0_DR5      */
+       [RCAR_GP_PIN(1, 28)] = { PUPR2,  6 },   /* DU0_DR6      */
+       [RCAR_GP_PIN(1, 29)] = { PUPR2,  7 },   /* DU0_DR7      */
+       [RCAR_GP_PIN(1, 30)] = { PUPR2,  8 },   /* DU0_DG0      */
+       [RCAR_GP_PIN(1, 31)] = { PUPR2,  9 },   /* DU0_DG1      */
+       [RCAR_GP_PIN(2,  0)] = { PUPR2, 10 },   /* DU0_DG2      */
+       [RCAR_GP_PIN(2,  1)] = { PUPR2, 11 },   /* DU0_DG3      */
+       [RCAR_GP_PIN(2,  2)] = { PUPR2, 12 },   /* DU0_DG4      */
+       [RCAR_GP_PIN(2,  3)] = { PUPR2, 13 },   /* DU0_DG5      */
+       [RCAR_GP_PIN(2,  4)] = { PUPR2, 14 },   /* DU0_DG6      */
+       [RCAR_GP_PIN(2,  5)] = { PUPR2, 15 },   /* DU0_DG7      */
+       [RCAR_GP_PIN(2,  6)] = { PUPR2, 16 },   /* DU0_DB0      */
+       [RCAR_GP_PIN(2,  7)] = { PUPR2, 17 },   /* DU0_DB1      */
+       [RCAR_GP_PIN(2,  8)] = { PUPR2, 18 },   /* DU0_DB2      */
+       [RCAR_GP_PIN(2,  9)] = { PUPR2, 19 },   /* DU0_DB3      */
+       [RCAR_GP_PIN(2, 10)] = { PUPR2, 20 },   /* DU0_DB4      */
+       [RCAR_GP_PIN(2, 11)] = { PUPR2, 21 },   /* DU0_DB5      */
+       [RCAR_GP_PIN(2, 12)] = { PUPR2, 22 },   /* DU0_DB6      */
+       [RCAR_GP_PIN(2, 13)] = { PUPR2, 23 },   /* DU0_DB7      */
+       [RCAR_GP_PIN(2, 14)] = { PUPR2, 24 },   /* DU0_DOTCLKIN */
+       [RCAR_GP_PIN(2, 15)] = { PUPR2, 25 },   /* DU0_DOTCLKOUT0 */
+       [RCAR_GP_PIN(2, 17)] = { PUPR2, 26 },   /* DU0_HSYNC    */
+       [RCAR_GP_PIN(2, 18)] = { PUPR2, 27 },   /* DU0_VSYNC    */
+       [RCAR_GP_PIN(2, 19)] = { PUPR2, 28 },   /* DU0_EXODDF   */
+       [RCAR_GP_PIN(2, 20)] = { PUPR2, 29 },   /* DU0_DISP     */
+       [RCAR_GP_PIN(2, 21)] = { PUPR2, 30 },   /* DU0_CDE      */
+       [RCAR_GP_PIN(2, 16)] = { PUPR2, 31 },   /* DU0_DOTCLKOUT1 */
+
+       [RCAR_GP_PIN(3, 24)] = { PUPR3,  0 },   /* VI0_CLK      */
+       [RCAR_GP_PIN(3, 25)] = { PUPR3,  1 },   /* VI0_CLKENB   */
+       [RCAR_GP_PIN(3, 26)] = { PUPR3,  2 },   /* VI0_FIELD    */
+       [RCAR_GP_PIN(3, 27)] = { PUPR3,  3 },   /* /VI0_HSYNC   */
+       [RCAR_GP_PIN(3, 28)] = { PUPR3,  4 },   /* /VI0_VSYNC   */
+       [RCAR_GP_PIN(3, 29)] = { PUPR3,  5 },   /* VI0_DATA0    */
+       [RCAR_GP_PIN(3, 30)] = { PUPR3,  6 },   /* VI0_DATA1    */
+       [RCAR_GP_PIN(3, 31)] = { PUPR3,  7 },   /* VI0_DATA2    */
+       [RCAR_GP_PIN(4,  0)] = { PUPR3,  8 },   /* VI0_DATA3    */
+       [RCAR_GP_PIN(4,  1)] = { PUPR3,  9 },   /* VI0_DATA4    */
+       [RCAR_GP_PIN(4,  2)] = { PUPR3, 10 },   /* VI0_DATA5    */
+       [RCAR_GP_PIN(4,  3)] = { PUPR3, 11 },   /* VI0_DATA6    */
+       [RCAR_GP_PIN(4,  4)] = { PUPR3, 12 },   /* VI0_DATA7    */
+       [RCAR_GP_PIN(4,  5)] = { PUPR3, 13 },   /* VI0_G2       */
+       [RCAR_GP_PIN(4,  6)] = { PUPR3, 14 },   /* VI0_G3       */
+       [RCAR_GP_PIN(4,  7)] = { PUPR3, 15 },   /* VI0_G4       */
+       [RCAR_GP_PIN(4,  8)] = { PUPR3, 16 },   /* VI0_G5       */
+       [RCAR_GP_PIN(4, 21)] = { PUPR3, 17 },   /* VI1_DATA12   */
+       [RCAR_GP_PIN(4, 22)] = { PUPR3, 18 },   /* VI1_DATA13   */
+       [RCAR_GP_PIN(4, 23)] = { PUPR3, 19 },   /* VI1_DATA14   */
+       [RCAR_GP_PIN(4, 24)] = { PUPR3, 20 },   /* VI1_DATA15   */
+       [RCAR_GP_PIN(4,  9)] = { PUPR3, 21 },   /* ETH_REF_CLK  */
+       [RCAR_GP_PIN(4, 10)] = { PUPR3, 22 },   /* ETH_TXD0     */
+       [RCAR_GP_PIN(4, 11)] = { PUPR3, 23 },   /* ETH_TXD1     */
+       [RCAR_GP_PIN(4, 12)] = { PUPR3, 24 },   /* ETH_CRS_DV   */
+       [RCAR_GP_PIN(4, 13)] = { PUPR3, 25 },   /* ETH_TX_EN    */
+       [RCAR_GP_PIN(4, 14)] = { PUPR3, 26 },   /* ETH_RX_ER    */
+       [RCAR_GP_PIN(4, 15)] = { PUPR3, 27 },   /* ETH_RXD0     */
+       [RCAR_GP_PIN(4, 16)] = { PUPR3, 28 },   /* ETH_RXD1     */
+       [RCAR_GP_PIN(4, 17)] = { PUPR3, 29 },   /* ETH_MDC      */
+       [RCAR_GP_PIN(4, 18)] = { PUPR3, 30 },   /* ETH_MDIO     */
+       [RCAR_GP_PIN(4, 19)] = { PUPR3, 31 },   /* ETH_LINK     */
+
+       [RCAR_GP_PIN(3,  6)] = { PUPR4,  0 },   /* SSI_SCK012   */
+       [RCAR_GP_PIN(3,  7)] = { PUPR4,  1 },   /* SSI_WS012    */
+       [RCAR_GP_PIN(3, 10)] = { PUPR4,  2 },   /* SSI_SDATA0   */
+       [RCAR_GP_PIN(3,  9)] = { PUPR4,  3 },   /* SSI_SDATA1   */
+       [RCAR_GP_PIN(3,  8)] = { PUPR4,  4 },   /* SSI_SDATA2   */
+       [RCAR_GP_PIN(3,  2)] = { PUPR4,  5 },   /* SSI_SCK34    */
+       [RCAR_GP_PIN(3,  3)] = { PUPR4,  6 },   /* SSI_WS34     */
+       [RCAR_GP_PIN(3,  5)] = { PUPR4,  7 },   /* SSI_SDATA3   */
+       [RCAR_GP_PIN(3,  4)] = { PUPR4,  8 },   /* SSI_SDATA4   */
+       [RCAR_GP_PIN(2, 31)] = { PUPR4,  9 },   /* SSI_SCK5     */
+       [RCAR_GP_PIN(3,  0)] = { PUPR4, 10 },   /* SSI_WS5      */
+       [RCAR_GP_PIN(3,  1)] = { PUPR4, 11 },   /* SSI_SDATA5   */
+       [RCAR_GP_PIN(2, 28)] = { PUPR4, 12 },   /* SSI_SCK6     */
+       [RCAR_GP_PIN(2, 29)] = { PUPR4, 13 },   /* SSI_WS6      */
+       [RCAR_GP_PIN(2, 30)] = { PUPR4, 14 },   /* SSI_SDATA6   */
+       [RCAR_GP_PIN(2, 24)] = { PUPR4, 15 },   /* SSI_SCK78    */
+       [RCAR_GP_PIN(2, 25)] = { PUPR4, 16 },   /* SSI_WS78     */
+       [RCAR_GP_PIN(2, 27)] = { PUPR4, 17 },   /* SSI_SDATA7   */
+       [RCAR_GP_PIN(2, 26)] = { PUPR4, 18 },   /* SSI_SDATA8   */
+       [RCAR_GP_PIN(3, 23)] = { PUPR4, 19 },   /* TCLK0        */
+       [RCAR_GP_PIN(3, 11)] = { PUPR4, 20 },   /* SD0_CLK      */
+       [RCAR_GP_PIN(3, 12)] = { PUPR4, 21 },   /* SD0_CMD      */
+       [RCAR_GP_PIN(3, 13)] = { PUPR4, 22 },   /* SD0_DAT0     */
+       [RCAR_GP_PIN(3, 14)] = { PUPR4, 23 },   /* SD0_DAT1     */
+       [RCAR_GP_PIN(3, 15)] = { PUPR4, 24 },   /* SD0_DAT2     */
+       [RCAR_GP_PIN(3, 16)] = { PUPR4, 25 },   /* SD0_DAT3     */
+       [RCAR_GP_PIN(3, 17)] = { PUPR4, 26 },   /* SD0_CD       */
+       [RCAR_GP_PIN(3, 18)] = { PUPR4, 27 },   /* SD0_WP       */
+       [RCAR_GP_PIN(2, 22)] = { PUPR4, 28 },   /* AUDIO_CLKA   */
+       [RCAR_GP_PIN(2, 23)] = { PUPR4, 29 },   /* AUDIO_CLKB   */
+       [RCAR_GP_PIN(1, 14)] = { PUPR4, 30 },   /* IRQ2         */
+       [RCAR_GP_PIN(1, 15)] = { PUPR4, 31 },   /* IRQ3         */
+
+       [RCAR_GP_PIN(0,  1)] = { PUPR5,  0 },   /* PENC0        */
+       [RCAR_GP_PIN(0,  2)] = { PUPR5,  1 },   /* PENC1        */
+       [RCAR_GP_PIN(0,  3)] = { PUPR5,  2 },   /* USB_OVC0     */
+       [RCAR_GP_PIN(0,  4)] = { PUPR5,  3 },   /* USB_OVC1     */
+       [RCAR_GP_PIN(1, 16)] = { PUPR5,  4 },   /* SCIF_CLK     */
+       [RCAR_GP_PIN(1, 17)] = { PUPR5,  5 },   /* TX0          */
+       [RCAR_GP_PIN(1, 18)] = { PUPR5,  6 },   /* RX0          */
+       [RCAR_GP_PIN(1, 19)] = { PUPR5,  7 },   /* SCK0         */
+       [RCAR_GP_PIN(1, 20)] = { PUPR5,  8 },   /* /CTS0        */
+       [RCAR_GP_PIN(1, 21)] = { PUPR5,  9 },   /* /RTS0        */
+       [RCAR_GP_PIN(3, 19)] = { PUPR5, 10 },   /* HSPI_CLK0    */
+       [RCAR_GP_PIN(3, 20)] = { PUPR5, 11 },   /* /HSPI_CS0    */
+       [RCAR_GP_PIN(3, 21)] = { PUPR5, 12 },   /* HSPI_RX0     */
+       [RCAR_GP_PIN(3, 22)] = { PUPR5, 13 },   /* HSPI_TX0     */
+       [RCAR_GP_PIN(4, 20)] = { PUPR5, 14 },   /* ETH_MAGIC    */
+       [RCAR_GP_PIN(4, 25)] = { PUPR5, 15 },   /* AVS1         */
+       [RCAR_GP_PIN(4, 26)] = { PUPR5, 16 },   /* AVS2         */
+};
+
+static unsigned int r8a7778_pinmux_get_bias(struct sh_pfc *pfc,
+                                           unsigned int pin)
+{
+       void __iomem *addr;
+
+       if (WARN_ON_ONCE(!pullups[pin].reg))
+               return PIN_CONFIG_BIAS_DISABLE;
+
+       addr = pfc->windows->virt + pullups[pin].reg;
+
+       if (ioread32(addr) & BIT(pullups[pin].bit))
+               return PIN_CONFIG_BIAS_PULL_UP;
+       else
+               return PIN_CONFIG_BIAS_DISABLE;
+}
+
+static void r8a7778_pinmux_set_bias(struct sh_pfc *pfc, unsigned int pin,
+                                  unsigned int bias)
+{
+       void __iomem *addr;
+       u32 value;
+       u32 bit;
+
+       if (WARN_ON_ONCE(!pullups[pin].reg))
+               return;
+
+       addr = pfc->windows->virt + pullups[pin].reg;
+       bit = BIT(pullups[pin].bit);
+
+       value = ioread32(addr) & ~bit;
+       if (bias == PIN_CONFIG_BIAS_PULL_UP)
+               value |= bit;
+       iowrite32(value, addr);
+}
+
+static const struct sh_pfc_soc_operations r8a7778_pfc_ops = {
+       .get_bias = r8a7778_pinmux_get_bias,
+       .set_bias = r8a7778_pinmux_set_bias,
+};
+
 const struct sh_pfc_soc_info r8a7778_pinmux_info = {
        .name = "r8a7778_pfc",
+       .ops  = &r8a7778_pfc_ops,
 
        .unlock_reg = 0xfffc0000, /* PMMR */
 
@@ -2923,6 +3143,6 @@ const struct sh_pfc_soc_info r8a7778_pinmux_info = {
 
        .cfg_regs = pinmux_config_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index f5c01e1e2615f3af907d1a9b2ef64d2ccd72acd7..ed4e0788035c50b4f51985e1d4eb91e0345c2452 100644 (file)
@@ -20,7 +20,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/platform_data/gpio-rcar.h>
 
 #include "sh_pfc.h"
 
@@ -620,18 +619,18 @@ static const u16 pinmux_data[] = {
        PINMUX_DATA(USB_PENC1_MARK, FN_USB_PENC1),
 
        PINMUX_IPSR_DATA(IP0_2_0, USB_PENC2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_2_0, SCK0, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP0_2_0, SCK0, SEL_SCIF0_0),
        PINMUX_IPSR_DATA(IP0_2_0, PWM1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_2_0, PWMFSW0, SEL_PWMFSW_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_2_0, SCIF_CLK, SEL_SCIF_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_2_0, TCLK0_C, SEL_TMU0_2),
+       PINMUX_IPSR_MSEL(IP0_2_0, PWMFSW0, SEL_PWMFSW_0),
+       PINMUX_IPSR_MSEL(IP0_2_0, SCIF_CLK, SEL_SCIF_0),
+       PINMUX_IPSR_MSEL(IP0_2_0, TCLK0_C, SEL_TMU0_2),
        PINMUX_IPSR_DATA(IP0_5_3, BS),
        PINMUX_IPSR_DATA(IP0_5_3, SD1_DAT2),
        PINMUX_IPSR_DATA(IP0_5_3, MMC0_D2),
        PINMUX_IPSR_DATA(IP0_5_3, FD2),
        PINMUX_IPSR_DATA(IP0_5_3, ATADIR0),
        PINMUX_IPSR_DATA(IP0_5_3, SDSELF),
-       PINMUX_IPSR_MODSEL_DATA(IP0_5_3, HCTS1, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP0_5_3, HCTS1, SEL_HSCIF1_0),
        PINMUX_IPSR_DATA(IP0_5_3, TX4_C),
        PINMUX_IPSR_DATA(IP0_7_6, A0),
        PINMUX_IPSR_DATA(IP0_7_6, SD1_DAT3),
@@ -641,37 +640,37 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP0_9_8, TX5_D),
        PINMUX_IPSR_DATA(IP0_9_8, HSPI_TX2_B),
        PINMUX_IPSR_DATA(IP0_11_10, A21),
-       PINMUX_IPSR_MODSEL_DATA(IP0_11_10, SCK5_D, SEL_SCIF5_3),
-       PINMUX_IPSR_MODSEL_DATA(IP0_11_10, HSPI_CLK2_B, SEL_HSPI2_1),
+       PINMUX_IPSR_MSEL(IP0_11_10, SCK5_D, SEL_SCIF5_3),
+       PINMUX_IPSR_MSEL(IP0_11_10, HSPI_CLK2_B, SEL_HSPI2_1),
        PINMUX_IPSR_DATA(IP0_13_12, A22),
-       PINMUX_IPSR_MODSEL_DATA(IP0_13_12, RX5_D, SEL_SCIF5_3),
-       PINMUX_IPSR_MODSEL_DATA(IP0_13_12, HSPI_RX2_B, SEL_HSPI2_1),
+       PINMUX_IPSR_MSEL(IP0_13_12, RX5_D, SEL_SCIF5_3),
+       PINMUX_IPSR_MSEL(IP0_13_12, HSPI_RX2_B, SEL_HSPI2_1),
        PINMUX_IPSR_DATA(IP0_13_12, VI1_R0),
        PINMUX_IPSR_DATA(IP0_15_14, A23),
        PINMUX_IPSR_DATA(IP0_15_14, FCLE),
-       PINMUX_IPSR_MODSEL_DATA(IP0_15_14, HSPI_CLK2, SEL_HSPI2_0),
+       PINMUX_IPSR_MSEL(IP0_15_14, HSPI_CLK2, SEL_HSPI2_0),
        PINMUX_IPSR_DATA(IP0_15_14, VI1_R1),
        PINMUX_IPSR_DATA(IP0_18_16, A24),
        PINMUX_IPSR_DATA(IP0_18_16, SD1_CD),
        PINMUX_IPSR_DATA(IP0_18_16, MMC0_D4),
        PINMUX_IPSR_DATA(IP0_18_16, FD4),
-       PINMUX_IPSR_MODSEL_DATA(IP0_18_16, HSPI_CS2, SEL_HSPI2_0),
+       PINMUX_IPSR_MSEL(IP0_18_16, HSPI_CS2, SEL_HSPI2_0),
        PINMUX_IPSR_DATA(IP0_18_16, VI1_R2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_18_16, SSI_WS78_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP0_18_16, SSI_WS78_B, SEL_SSI7_1),
        PINMUX_IPSR_DATA(IP0_22_19, A25),
        PINMUX_IPSR_DATA(IP0_22_19, SD1_WP),
        PINMUX_IPSR_DATA(IP0_22_19, MMC0_D5),
        PINMUX_IPSR_DATA(IP0_22_19, FD5),
-       PINMUX_IPSR_MODSEL_DATA(IP0_22_19, HSPI_RX2, SEL_HSPI2_0),
+       PINMUX_IPSR_MSEL(IP0_22_19, HSPI_RX2, SEL_HSPI2_0),
        PINMUX_IPSR_DATA(IP0_22_19, VI1_R3),
        PINMUX_IPSR_DATA(IP0_22_19, TX5_B),
-       PINMUX_IPSR_MODSEL_DATA(IP0_22_19, SSI_SDATA7_B, SEL_SSI7_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_22_19, CTS0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP0_22_19, SSI_SDATA7_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP0_22_19, CTS0_B, SEL_SCIF0_1),
        PINMUX_IPSR_DATA(IP0_24_23, CLKOUT),
        PINMUX_IPSR_DATA(IP0_24_23, TX3C_IRDA_TX_C),
        PINMUX_IPSR_DATA(IP0_24_23, PWM0_B),
        PINMUX_IPSR_DATA(IP0_25, CS0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_25, HSPI_CS2_B, SEL_HSPI2_1),
+       PINMUX_IPSR_MSEL(IP0_25, HSPI_CS2_B, SEL_HSPI2_1),
        PINMUX_IPSR_DATA(IP0_27_26, CS1_A26),
        PINMUX_IPSR_DATA(IP0_27_26, HSPI_TX2),
        PINMUX_IPSR_DATA(IP0_27_26, SDSELF_B),
@@ -679,11 +678,11 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP0_30_28, FWE),
        PINMUX_IPSR_DATA(IP0_30_28, ATAG0),
        PINMUX_IPSR_DATA(IP0_30_28, VI1_R7),
-       PINMUX_IPSR_MODSEL_DATA(IP0_30_28, HRTS1, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_30_28, RX4_C, SEL_SCIF4_2),
+       PINMUX_IPSR_MSEL(IP0_30_28, HRTS1, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP0_30_28, RX4_C, SEL_SCIF4_2),
 
        PINMUX_IPSR_DATA(IP1_1_0, EX_CS0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_1_0, RX3_C_IRDA_RX_C, SEL_SCIF3_2),
+       PINMUX_IPSR_MSEL(IP1_1_0, RX3_C_IRDA_RX_C, SEL_SCIF3_2),
        PINMUX_IPSR_DATA(IP1_1_0, MMC0_D6),
        PINMUX_IPSR_DATA(IP1_1_0, FD6),
        PINMUX_IPSR_DATA(IP1_3_2, EX_CS1),
@@ -700,45 +699,45 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP1_10_7, FRE),
        PINMUX_IPSR_DATA(IP1_10_7, ATACS10),
        PINMUX_IPSR_DATA(IP1_10_7, VI1_R4),
-       PINMUX_IPSR_MODSEL_DATA(IP1_10_7, RX5_B, SEL_SCIF5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_10_7, HSCK1, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_10_7, SSI_SDATA8_B, SEL_SSI8_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_10_7, RTS0_B_TANS_B, SEL_SCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_10_7, SSI_SDATA9, SEL_SSI9_0),
+       PINMUX_IPSR_MSEL(IP1_10_7, RX5_B, SEL_SCIF5_1),
+       PINMUX_IPSR_MSEL(IP1_10_7, HSCK1, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP1_10_7, SSI_SDATA8_B, SEL_SSI8_1),
+       PINMUX_IPSR_MSEL(IP1_10_7, RTS0_B_TANS_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP1_10_7, SSI_SDATA9, SEL_SSI9_0),
        PINMUX_IPSR_DATA(IP1_14_11, EX_CS4),
        PINMUX_IPSR_DATA(IP1_14_11, SD1_DAT0),
        PINMUX_IPSR_DATA(IP1_14_11, MMC0_D0),
        PINMUX_IPSR_DATA(IP1_14_11, FD0),
        PINMUX_IPSR_DATA(IP1_14_11, ATARD0),
        PINMUX_IPSR_DATA(IP1_14_11, VI1_R5),
-       PINMUX_IPSR_MODSEL_DATA(IP1_14_11, SCK5_B, SEL_SCIF5_1),
+       PINMUX_IPSR_MSEL(IP1_14_11, SCK5_B, SEL_SCIF5_1),
        PINMUX_IPSR_DATA(IP1_14_11, HTX1),
        PINMUX_IPSR_DATA(IP1_14_11, TX2_E),
        PINMUX_IPSR_DATA(IP1_14_11, TX0_B),
-       PINMUX_IPSR_MODSEL_DATA(IP1_14_11, SSI_SCK9, SEL_SSI9_0),
+       PINMUX_IPSR_MSEL(IP1_14_11, SSI_SCK9, SEL_SSI9_0),
        PINMUX_IPSR_DATA(IP1_18_15, EX_CS5),
        PINMUX_IPSR_DATA(IP1_18_15, SD1_DAT1),
        PINMUX_IPSR_DATA(IP1_18_15, MMC0_D1),
        PINMUX_IPSR_DATA(IP1_18_15, FD1),
        PINMUX_IPSR_DATA(IP1_18_15, ATAWR0),
        PINMUX_IPSR_DATA(IP1_18_15, VI1_R6),
-       PINMUX_IPSR_MODSEL_DATA(IP1_18_15, HRX1, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_18_15, RX2_E, SEL_SCIF2_4),
-       PINMUX_IPSR_MODSEL_DATA(IP1_18_15, RX0_B, SEL_SCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_18_15, SSI_WS9, SEL_SSI9_0),
+       PINMUX_IPSR_MSEL(IP1_18_15, HRX1, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP1_18_15, RX2_E, SEL_SCIF2_4),
+       PINMUX_IPSR_MSEL(IP1_18_15, RX0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP1_18_15, SSI_WS9, SEL_SSI9_0),
        PINMUX_IPSR_DATA(IP1_20_19, MLB_CLK),
        PINMUX_IPSR_DATA(IP1_20_19, PWM2),
-       PINMUX_IPSR_MODSEL_DATA(IP1_20_19, SCK4, SEL_SCIF4_0),
+       PINMUX_IPSR_MSEL(IP1_20_19, SCK4, SEL_SCIF4_0),
        PINMUX_IPSR_DATA(IP1_22_21, MLB_SIG),
        PINMUX_IPSR_DATA(IP1_22_21, PWM3),
        PINMUX_IPSR_DATA(IP1_22_21, TX4),
        PINMUX_IPSR_DATA(IP1_24_23, MLB_DAT),
        PINMUX_IPSR_DATA(IP1_24_23, PWM4),
-       PINMUX_IPSR_MODSEL_DATA(IP1_24_23, RX4, SEL_SCIF4_0),
+       PINMUX_IPSR_MSEL(IP1_24_23, RX4, SEL_SCIF4_0),
        PINMUX_IPSR_DATA(IP1_28_25, HTX0),
        PINMUX_IPSR_DATA(IP1_28_25, TX1),
        PINMUX_IPSR_DATA(IP1_28_25, SDATA),
-       PINMUX_IPSR_MODSEL_DATA(IP1_28_25, CTS0_C, SEL_SCIF0_2),
+       PINMUX_IPSR_MSEL(IP1_28_25, CTS0_C, SEL_SCIF0_2),
        PINMUX_IPSR_DATA(IP1_28_25, SUB_TCK),
        PINMUX_IPSR_DATA(IP1_28_25, CC5_STATE2),
        PINMUX_IPSR_DATA(IP1_28_25, CC5_STATE10),
@@ -746,39 +745,39 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP1_28_25, CC5_STATE26),
        PINMUX_IPSR_DATA(IP1_28_25, CC5_STATE34),
 
-       PINMUX_IPSR_MODSEL_DATA(IP2_3_0, HRX0, SEL_HSCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_3_0, RX1, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP2_3_0, HRX0, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP2_3_0, RX1, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP2_3_0, SCKZ),
-       PINMUX_IPSR_MODSEL_DATA(IP2_3_0, RTS0_C_TANS_C, SEL_SCIF0_2),
+       PINMUX_IPSR_MSEL(IP2_3_0, RTS0_C_TANS_C, SEL_SCIF0_2),
        PINMUX_IPSR_DATA(IP2_3_0, SUB_TDI),
        PINMUX_IPSR_DATA(IP2_3_0, CC5_STATE3),
        PINMUX_IPSR_DATA(IP2_3_0, CC5_STATE11),
        PINMUX_IPSR_DATA(IP2_3_0, CC5_STATE19),
        PINMUX_IPSR_DATA(IP2_3_0, CC5_STATE27),
        PINMUX_IPSR_DATA(IP2_3_0, CC5_STATE35),
-       PINMUX_IPSR_MODSEL_DATA(IP2_7_4, HSCK0, SEL_HSCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_7_4, SCK1, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP2_7_4, HSCK0, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP2_7_4, SCK1, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP2_7_4, MTS),
        PINMUX_IPSR_DATA(IP2_7_4, PWM5),
-       PINMUX_IPSR_MODSEL_DATA(IP2_7_4, SCK0_C, SEL_SCIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP2_7_4, SSI_SDATA9_B, SEL_SSI9_1),
+       PINMUX_IPSR_MSEL(IP2_7_4, SCK0_C, SEL_SCIF0_2),
+       PINMUX_IPSR_MSEL(IP2_7_4, SSI_SDATA9_B, SEL_SSI9_1),
        PINMUX_IPSR_DATA(IP2_7_4, SUB_TDO),
        PINMUX_IPSR_DATA(IP2_7_4, CC5_STATE0),
        PINMUX_IPSR_DATA(IP2_7_4, CC5_STATE8),
        PINMUX_IPSR_DATA(IP2_7_4, CC5_STATE16),
        PINMUX_IPSR_DATA(IP2_7_4, CC5_STATE24),
        PINMUX_IPSR_DATA(IP2_7_4, CC5_STATE32),
-       PINMUX_IPSR_MODSEL_DATA(IP2_11_8, HCTS0, SEL_HSCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_11_8, CTS1, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP2_11_8, HCTS0, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP2_11_8, CTS1, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP2_11_8, STM),
        PINMUX_IPSR_DATA(IP2_11_8, PWM0_D),
-       PINMUX_IPSR_MODSEL_DATA(IP2_11_8, RX0_C, SEL_SCIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP2_11_8, SCIF_CLK_C, SEL_SCIF_2),
+       PINMUX_IPSR_MSEL(IP2_11_8, RX0_C, SEL_SCIF0_2),
+       PINMUX_IPSR_MSEL(IP2_11_8, SCIF_CLK_C, SEL_SCIF_2),
        PINMUX_IPSR_DATA(IP2_11_8, SUB_TRST),
-       PINMUX_IPSR_MODSEL_DATA(IP2_11_8, TCLK1_B, SEL_TMU1_1),
+       PINMUX_IPSR_MSEL(IP2_11_8, TCLK1_B, SEL_TMU1_1),
        PINMUX_IPSR_DATA(IP2_11_8, CC5_OSCOUT),
-       PINMUX_IPSR_MODSEL_DATA(IP2_15_12, HRTS0, SEL_HSCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_15_12, RTS1_TANS, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP2_15_12, HRTS0, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP2_15_12, RTS1_TANS, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP2_15_12, MDATA),
        PINMUX_IPSR_DATA(IP2_15_12, TX0_C),
        PINMUX_IPSR_DATA(IP2_15_12, SUB_TMS),
@@ -789,17 +788,17 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP2_15_12, CC5_STATE33),
        PINMUX_IPSR_DATA(IP2_18_16, DU0_DR0),
        PINMUX_IPSR_DATA(IP2_18_16, LCDOUT0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_18_16, DREQ0, SEL_EXBUS0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_18_16, GPS_CLK_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP2_18_16, DREQ0, SEL_EXBUS0_0),
+       PINMUX_IPSR_MSEL(IP2_18_16, GPS_CLK_B, SEL_GPS_1),
        PINMUX_IPSR_DATA(IP2_18_16, AUDATA0),
        PINMUX_IPSR_DATA(IP2_18_16, TX5_C),
        PINMUX_IPSR_DATA(IP2_21_19, DU0_DR1),
        PINMUX_IPSR_DATA(IP2_21_19, LCDOUT1),
        PINMUX_IPSR_DATA(IP2_21_19, DACK0),
        PINMUX_IPSR_DATA(IP2_21_19, DRACK0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_21_19, GPS_SIGN_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP2_21_19, GPS_SIGN_B, SEL_GPS_1),
        PINMUX_IPSR_DATA(IP2_21_19, AUDATA1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_21_19, RX5_C, SEL_SCIF5_2),
+       PINMUX_IPSR_MSEL(IP2_21_19, RX5_C, SEL_SCIF5_2),
        PINMUX_IPSR_DATA(IP2_22, DU0_DR2),
        PINMUX_IPSR_DATA(IP2_22, LCDOUT2),
        PINMUX_IPSR_DATA(IP2_23, DU0_DR3),
@@ -814,14 +813,14 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP2_27, LCDOUT7),
        PINMUX_IPSR_DATA(IP2_30_28, DU0_DG0),
        PINMUX_IPSR_DATA(IP2_30_28, LCDOUT8),
-       PINMUX_IPSR_MODSEL_DATA(IP2_30_28, DREQ1, SEL_EXBUS1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_30_28, SCL2, SEL_I2C2_0),
+       PINMUX_IPSR_MSEL(IP2_30_28, DREQ1, SEL_EXBUS1_0),
+       PINMUX_IPSR_MSEL(IP2_30_28, SCL2, SEL_I2C2_0),
        PINMUX_IPSR_DATA(IP2_30_28, AUDATA2),
 
        PINMUX_IPSR_DATA(IP3_2_0, DU0_DG1),
        PINMUX_IPSR_DATA(IP3_2_0, LCDOUT9),
        PINMUX_IPSR_DATA(IP3_2_0, DACK1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_2_0, SDA2, SEL_I2C2_0),
+       PINMUX_IPSR_MSEL(IP3_2_0, SDA2, SEL_I2C2_0),
        PINMUX_IPSR_DATA(IP3_2_0, AUDATA3),
        PINMUX_IPSR_DATA(IP3_3, DU0_DG2),
        PINMUX_IPSR_DATA(IP3_3, LCDOUT10),
@@ -838,16 +837,16 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP3_11_9, DU0_DB0),
        PINMUX_IPSR_DATA(IP3_11_9, LCDOUT16),
        PINMUX_IPSR_DATA(IP3_11_9, EX_WAIT1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_9, SCL1, SEL_I2C1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_9, TCLK1, SEL_TMU1_0),
+       PINMUX_IPSR_MSEL(IP3_11_9, SCL1, SEL_I2C1_0),
+       PINMUX_IPSR_MSEL(IP3_11_9, TCLK1, SEL_TMU1_0),
        PINMUX_IPSR_DATA(IP3_11_9, AUDATA4),
        PINMUX_IPSR_DATA(IP3_14_12, DU0_DB1),
        PINMUX_IPSR_DATA(IP3_14_12, LCDOUT17),
        PINMUX_IPSR_DATA(IP3_14_12, EX_WAIT2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_14_12, SDA1, SEL_I2C1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_14_12, GPS_MAG_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP3_14_12, SDA1, SEL_I2C1_0),
+       PINMUX_IPSR_MSEL(IP3_14_12, GPS_MAG_B, SEL_GPS_1),
        PINMUX_IPSR_DATA(IP3_14_12, AUDATA5),
-       PINMUX_IPSR_MODSEL_DATA(IP3_14_12, SCK5_C, SEL_SCIF5_2),
+       PINMUX_IPSR_MSEL(IP3_14_12, SCK5_C, SEL_SCIF5_2),
        PINMUX_IPSR_DATA(IP3_15, DU0_DB2),
        PINMUX_IPSR_DATA(IP3_15, LCDOUT18),
        PINMUX_IPSR_DATA(IP3_16, DU0_DB3),
@@ -863,14 +862,14 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP3_22_21, DU0_DOTCLKIN),
        PINMUX_IPSR_DATA(IP3_22_21, QSTVA_QVS),
        PINMUX_IPSR_DATA(IP3_22_21, TX3_D_IRDA_TX_D),
-       PINMUX_IPSR_MODSEL_DATA(IP3_22_21, SCL3_B, SEL_I2C3_1),
+       PINMUX_IPSR_MSEL(IP3_22_21, SCL3_B, SEL_I2C3_1),
        PINMUX_IPSR_DATA(IP3_23, DU0_DOTCLKOUT0),
        PINMUX_IPSR_DATA(IP3_23, QCLK),
        PINMUX_IPSR_DATA(IP3_26_24, DU0_DOTCLKOUT1),
        PINMUX_IPSR_DATA(IP3_26_24, QSTVB_QVE),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, RX3_D_IRDA_RX_D, SEL_SCIF3_3),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, SDA3_B, SEL_I2C3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, SDA2_C, SEL_I2C2_2),
+       PINMUX_IPSR_MSEL(IP3_26_24, RX3_D_IRDA_RX_D, SEL_SCIF3_3),
+       PINMUX_IPSR_MSEL(IP3_26_24, SDA3_B, SEL_I2C3_1),
+       PINMUX_IPSR_MSEL(IP3_26_24, SDA2_C, SEL_I2C2_2),
        PINMUX_IPSR_DATA(IP3_26_24, DACK0_B),
        PINMUX_IPSR_DATA(IP3_26_24, DRACK0_B),
        PINMUX_IPSR_DATA(IP3_27, DU0_EXHSYNC_DU0_HSYNC),
@@ -881,34 +880,34 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP3_31_29, QCPV_QDE),
        PINMUX_IPSR_DATA(IP3_31_29, CAN1_TX),
        PINMUX_IPSR_DATA(IP3_31_29, TX2_C),
-       PINMUX_IPSR_MODSEL_DATA(IP3_31_29, SCL2_C, SEL_I2C2_2),
+       PINMUX_IPSR_MSEL(IP3_31_29, SCL2_C, SEL_I2C2_2),
        PINMUX_IPSR_DATA(IP3_31_29, REMOCON),
 
        PINMUX_IPSR_DATA(IP4_1_0, DU0_DISP),
        PINMUX_IPSR_DATA(IP4_1_0, QPOLA),
-       PINMUX_IPSR_MODSEL_DATA(IP4_1_0, CAN_CLK_C, SEL_CANCLK_2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_1_0, SCK2_C, SEL_SCIF2_2),
+       PINMUX_IPSR_MSEL(IP4_1_0, CAN_CLK_C, SEL_CANCLK_2),
+       PINMUX_IPSR_MSEL(IP4_1_0, SCK2_C, SEL_SCIF2_2),
        PINMUX_IPSR_DATA(IP4_4_2, DU0_CDE),
        PINMUX_IPSR_DATA(IP4_4_2, QPOLB),
        PINMUX_IPSR_DATA(IP4_4_2, CAN1_RX),
-       PINMUX_IPSR_MODSEL_DATA(IP4_4_2, RX2_C, SEL_SCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_4_2, DREQ0_B, SEL_EXBUS0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_4_2, SSI_SCK78_B, SEL_SSI7_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_4_2, SCK0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP4_4_2, RX2_C, SEL_SCIF2_2),
+       PINMUX_IPSR_MSEL(IP4_4_2, DREQ0_B, SEL_EXBUS0_1),
+       PINMUX_IPSR_MSEL(IP4_4_2, SSI_SCK78_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP4_4_2, SCK0_B, SEL_SCIF0_1),
        PINMUX_IPSR_DATA(IP4_7_5, DU1_DR0),
        PINMUX_IPSR_DATA(IP4_7_5, VI2_DATA0_VI2_B0),
        PINMUX_IPSR_DATA(IP4_7_5, PWM6),
        PINMUX_IPSR_DATA(IP4_7_5, SD3_CLK),
        PINMUX_IPSR_DATA(IP4_7_5, TX3_E_IRDA_TX_E),
        PINMUX_IPSR_DATA(IP4_7_5, AUDCK),
-       PINMUX_IPSR_MODSEL_DATA(IP4_7_5, PWMFSW0_B, SEL_PWMFSW_1),
+       PINMUX_IPSR_MSEL(IP4_7_5, PWMFSW0_B, SEL_PWMFSW_1),
        PINMUX_IPSR_DATA(IP4_10_8, DU1_DR1),
        PINMUX_IPSR_DATA(IP4_10_8, VI2_DATA1_VI2_B1),
        PINMUX_IPSR_DATA(IP4_10_8, PWM0),
        PINMUX_IPSR_DATA(IP4_10_8, SD3_CMD),
-       PINMUX_IPSR_MODSEL_DATA(IP4_10_8, RX3_E_IRDA_RX_E, SEL_SCIF3_4),
+       PINMUX_IPSR_MSEL(IP4_10_8, RX3_E_IRDA_RX_E, SEL_SCIF3_4),
        PINMUX_IPSR_DATA(IP4_10_8, AUDSYNC),
-       PINMUX_IPSR_MODSEL_DATA(IP4_10_8, CTS0_D, SEL_SCIF0_3),
+       PINMUX_IPSR_MSEL(IP4_10_8, CTS0_D, SEL_SCIF0_3),
        PINMUX_IPSR_DATA(IP4_11, DU1_DR2),
        PINMUX_IPSR_DATA(IP4_11, VI2_G0),
        PINMUX_IPSR_DATA(IP4_12, DU1_DR3),
@@ -923,18 +922,18 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP4_16, VI2_G5),
        PINMUX_IPSR_DATA(IP4_19_17, DU1_DG0),
        PINMUX_IPSR_DATA(IP4_19_17, VI2_DATA2_VI2_B2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_19_17, SCL1_B, SEL_I2C1_1),
+       PINMUX_IPSR_MSEL(IP4_19_17, SCL1_B, SEL_I2C1_1),
        PINMUX_IPSR_DATA(IP4_19_17, SD3_DAT2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_19_17, SCK3_E, SEL_SCIF3_4),
+       PINMUX_IPSR_MSEL(IP4_19_17, SCK3_E, SEL_SCIF3_4),
        PINMUX_IPSR_DATA(IP4_19_17, AUDATA6),
        PINMUX_IPSR_DATA(IP4_19_17, TX0_D),
        PINMUX_IPSR_DATA(IP4_22_20, DU1_DG1),
        PINMUX_IPSR_DATA(IP4_22_20, VI2_DATA3_VI2_B3),
-       PINMUX_IPSR_MODSEL_DATA(IP4_22_20, SDA1_B, SEL_I2C1_1),
+       PINMUX_IPSR_MSEL(IP4_22_20, SDA1_B, SEL_I2C1_1),
        PINMUX_IPSR_DATA(IP4_22_20, SD3_DAT3),
-       PINMUX_IPSR_MODSEL_DATA(IP4_22_20, SCK5, SEL_SCIF5_0),
+       PINMUX_IPSR_MSEL(IP4_22_20, SCK5, SEL_SCIF5_0),
        PINMUX_IPSR_DATA(IP4_22_20, AUDATA7),
-       PINMUX_IPSR_MODSEL_DATA(IP4_22_20, RX0_D, SEL_SCIF0_3),
+       PINMUX_IPSR_MSEL(IP4_22_20, RX0_D, SEL_SCIF0_3),
        PINMUX_IPSR_DATA(IP4_23, DU1_DG2),
        PINMUX_IPSR_DATA(IP4_23, VI2_G6),
        PINMUX_IPSR_DATA(IP4_24, DU1_DG3),
@@ -949,17 +948,17 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP4_28, VI2_R3),
        PINMUX_IPSR_DATA(IP4_31_29, DU1_DB0),
        PINMUX_IPSR_DATA(IP4_31_29, VI2_DATA4_VI2_B4),
-       PINMUX_IPSR_MODSEL_DATA(IP4_31_29, SCL2_B, SEL_I2C2_1),
+       PINMUX_IPSR_MSEL(IP4_31_29, SCL2_B, SEL_I2C2_1),
        PINMUX_IPSR_DATA(IP4_31_29, SD3_DAT0),
        PINMUX_IPSR_DATA(IP4_31_29, TX5),
-       PINMUX_IPSR_MODSEL_DATA(IP4_31_29, SCK0_D, SEL_SCIF0_3),
+       PINMUX_IPSR_MSEL(IP4_31_29, SCK0_D, SEL_SCIF0_3),
 
        PINMUX_IPSR_DATA(IP5_2_0, DU1_DB1),
        PINMUX_IPSR_DATA(IP5_2_0, VI2_DATA5_VI2_B5),
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, SDA2_B, SEL_I2C2_1),
+       PINMUX_IPSR_MSEL(IP5_2_0, SDA2_B, SEL_I2C2_1),
        PINMUX_IPSR_DATA(IP5_2_0, SD3_DAT1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, RX5, SEL_SCIF5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, RTS0_D_TANS_D, SEL_SCIF0_3),
+       PINMUX_IPSR_MSEL(IP5_2_0, RX5, SEL_SCIF5_0),
+       PINMUX_IPSR_MSEL(IP5_2_0, RTS0_D_TANS_D, SEL_SCIF0_3),
        PINMUX_IPSR_DATA(IP5_3, DU1_DB2),
        PINMUX_IPSR_DATA(IP5_3, VI2_R4),
        PINMUX_IPSR_DATA(IP5_4, DU1_DB3),
@@ -969,16 +968,16 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP5_6, DU1_DB5),
        PINMUX_IPSR_DATA(IP5_6, VI2_R7),
        PINMUX_IPSR_DATA(IP5_7, DU1_DB6),
-       PINMUX_IPSR_MODSEL_DATA(IP5_7, SCL2_D, SEL_I2C2_3),
+       PINMUX_IPSR_MSEL(IP5_7, SCL2_D, SEL_I2C2_3),
        PINMUX_IPSR_DATA(IP5_8, DU1_DB7),
-       PINMUX_IPSR_MODSEL_DATA(IP5_8, SDA2_D, SEL_I2C2_3),
+       PINMUX_IPSR_MSEL(IP5_8, SDA2_D, SEL_I2C2_3),
        PINMUX_IPSR_DATA(IP5_10_9, DU1_DOTCLKIN),
        PINMUX_IPSR_DATA(IP5_10_9, VI2_CLKENB),
-       PINMUX_IPSR_MODSEL_DATA(IP5_10_9, HSPI_CS1, SEL_HSPI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_10_9, SCL1_D, SEL_I2C1_3),
+       PINMUX_IPSR_MSEL(IP5_10_9, HSPI_CS1, SEL_HSPI1_0),
+       PINMUX_IPSR_MSEL(IP5_10_9, SCL1_D, SEL_I2C1_3),
        PINMUX_IPSR_DATA(IP5_12_11, DU1_DOTCLKOUT),
        PINMUX_IPSR_DATA(IP5_12_11, VI2_FIELD),
-       PINMUX_IPSR_MODSEL_DATA(IP5_12_11, SDA1_D, SEL_I2C1_3),
+       PINMUX_IPSR_MSEL(IP5_12_11, SDA1_D, SEL_I2C1_3),
        PINMUX_IPSR_DATA(IP5_14_13, DU1_EXHSYNC_DU1_HSYNC),
        PINMUX_IPSR_DATA(IP5_14_13, VI2_HSYNC),
        PINMUX_IPSR_DATA(IP5_14_13, VI3_HSYNC),
@@ -995,26 +994,26 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP5_20_17, AUDIO_CLKC),
        PINMUX_IPSR_DATA(IP5_20_17, TX2_D),
        PINMUX_IPSR_DATA(IP5_20_17, SPEEDIN),
-       PINMUX_IPSR_MODSEL_DATA(IP5_20_17, GPS_SIGN_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP5_20_17, GPS_SIGN_D, SEL_GPS_3),
        PINMUX_IPSR_DATA(IP5_23_21, DU1_DISP),
        PINMUX_IPSR_DATA(IP5_23_21, VI2_DATA6_VI2_B6),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_21, TCLK0, SEL_TMU0_0),
+       PINMUX_IPSR_MSEL(IP5_23_21, TCLK0, SEL_TMU0_0),
        PINMUX_IPSR_DATA(IP5_23_21, QSTVA_B_QVS_B),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_21, HSPI_CLK1, SEL_HSPI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_21, SCK2_D, SEL_SCIF2_3),
+       PINMUX_IPSR_MSEL(IP5_23_21, HSPI_CLK1, SEL_HSPI1_0),
+       PINMUX_IPSR_MSEL(IP5_23_21, SCK2_D, SEL_SCIF2_3),
        PINMUX_IPSR_DATA(IP5_23_21, AUDIO_CLKOUT_B),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_21, GPS_MAG_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP5_23_21, GPS_MAG_D, SEL_GPS_3),
        PINMUX_IPSR_DATA(IP5_27_24, DU1_CDE),
        PINMUX_IPSR_DATA(IP5_27_24, VI2_DATA7_VI2_B7),
-       PINMUX_IPSR_MODSEL_DATA(IP5_27_24, RX3_B_IRDA_RX_B, SEL_SCIF3_1),
+       PINMUX_IPSR_MSEL(IP5_27_24, RX3_B_IRDA_RX_B, SEL_SCIF3_1),
        PINMUX_IPSR_DATA(IP5_27_24, SD3_WP),
-       PINMUX_IPSR_MODSEL_DATA(IP5_27_24, HSPI_RX1, SEL_HSPI1_0),
+       PINMUX_IPSR_MSEL(IP5_27_24, HSPI_RX1, SEL_HSPI1_0),
        PINMUX_IPSR_DATA(IP5_27_24, VI1_FIELD),
        PINMUX_IPSR_DATA(IP5_27_24, VI3_FIELD),
        PINMUX_IPSR_DATA(IP5_27_24, AUDIO_CLKOUT),
-       PINMUX_IPSR_MODSEL_DATA(IP5_27_24, RX2_D, SEL_SCIF2_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_27_24, GPS_CLK_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP5_27_24, GPS_CLK_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP5_27_24, RX2_D, SEL_SCIF2_3),
+       PINMUX_IPSR_MSEL(IP5_27_24, GPS_CLK_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP5_27_24, GPS_CLK_D, SEL_GPS_3),
        PINMUX_IPSR_DATA(IP5_28, AUDIO_CLKA),
        PINMUX_IPSR_DATA(IP5_28, CAN_TXCLK),
        PINMUX_IPSR_DATA(IP5_30_29, AUDIO_CLKB),
@@ -1039,82 +1038,82 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP6_11_9, SSI_SCK34),
        PINMUX_IPSR_DATA(IP6_11_9, CAN_DEBUGOUT6),
        PINMUX_IPSR_DATA(IP6_11_9, CAN0_TX_B),
-       PINMUX_IPSR_MODSEL_DATA(IP6_11_9, IERX, SEL_IE_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_11_9, SSI_SCK9_C, SEL_SSI9_2),
+       PINMUX_IPSR_MSEL(IP6_11_9, IERX, SEL_IE_0),
+       PINMUX_IPSR_MSEL(IP6_11_9, SSI_SCK9_C, SEL_SSI9_2),
        PINMUX_IPSR_DATA(IP6_14_12, SSI_WS34),
        PINMUX_IPSR_DATA(IP6_14_12, CAN_DEBUGOUT7),
-       PINMUX_IPSR_MODSEL_DATA(IP6_14_12, CAN0_RX_B, SEL_CAN0_1),
+       PINMUX_IPSR_MSEL(IP6_14_12, CAN0_RX_B, SEL_CAN0_1),
        PINMUX_IPSR_DATA(IP6_14_12, IETX),
-       PINMUX_IPSR_MODSEL_DATA(IP6_14_12, SSI_WS9_C, SEL_SSI9_2),
+       PINMUX_IPSR_MSEL(IP6_14_12, SSI_WS9_C, SEL_SSI9_2),
        PINMUX_IPSR_DATA(IP6_17_15, SSI_SDATA3),
        PINMUX_IPSR_DATA(IP6_17_15, PWM0_C),
        PINMUX_IPSR_DATA(IP6_17_15, CAN_DEBUGOUT8),
-       PINMUX_IPSR_MODSEL_DATA(IP6_17_15, CAN_CLK_B, SEL_CANCLK_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_17_15, IECLK, SEL_IE_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_17_15, SCIF_CLK_B, SEL_SCIF_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_17_15, TCLK0_B, SEL_TMU0_1),
+       PINMUX_IPSR_MSEL(IP6_17_15, CAN_CLK_B, SEL_CANCLK_1),
+       PINMUX_IPSR_MSEL(IP6_17_15, IECLK, SEL_IE_0),
+       PINMUX_IPSR_MSEL(IP6_17_15, SCIF_CLK_B, SEL_SCIF_1),
+       PINMUX_IPSR_MSEL(IP6_17_15, TCLK0_B, SEL_TMU0_1),
        PINMUX_IPSR_DATA(IP6_19_18, SSI_SDATA4),
        PINMUX_IPSR_DATA(IP6_19_18, CAN_DEBUGOUT9),
-       PINMUX_IPSR_MODSEL_DATA(IP6_19_18, SSI_SDATA9_C, SEL_SSI9_2),
+       PINMUX_IPSR_MSEL(IP6_19_18, SSI_SDATA9_C, SEL_SSI9_2),
        PINMUX_IPSR_DATA(IP6_22_20, SSI_SCK5),
        PINMUX_IPSR_DATA(IP6_22_20, ADICLK),
        PINMUX_IPSR_DATA(IP6_22_20, CAN_DEBUGOUT10),
-       PINMUX_IPSR_MODSEL_DATA(IP6_22_20, SCK3, SEL_SCIF3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_22_20, TCLK0_D, SEL_TMU0_3),
+       PINMUX_IPSR_MSEL(IP6_22_20, SCK3, SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP6_22_20, TCLK0_D, SEL_TMU0_3),
        PINMUX_IPSR_DATA(IP6_24_23, SSI_WS5),
-       PINMUX_IPSR_MODSEL_DATA(IP6_24_23, ADICS_SAMP, SEL_ADI_0),
+       PINMUX_IPSR_MSEL(IP6_24_23, ADICS_SAMP, SEL_ADI_0),
        PINMUX_IPSR_DATA(IP6_24_23, CAN_DEBUGOUT11),
        PINMUX_IPSR_DATA(IP6_24_23, TX3_IRDA_TX),
        PINMUX_IPSR_DATA(IP6_26_25, SSI_SDATA5),
-       PINMUX_IPSR_MODSEL_DATA(IP6_26_25, ADIDATA, SEL_ADI_0),
+       PINMUX_IPSR_MSEL(IP6_26_25, ADIDATA, SEL_ADI_0),
        PINMUX_IPSR_DATA(IP6_26_25, CAN_DEBUGOUT12),
-       PINMUX_IPSR_MODSEL_DATA(IP6_26_25, RX3_IRDA_RX, SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP6_26_25, RX3_IRDA_RX, SEL_SCIF3_0),
        PINMUX_IPSR_DATA(IP6_30_29, SSI_SCK6),
        PINMUX_IPSR_DATA(IP6_30_29, ADICHS0),
        PINMUX_IPSR_DATA(IP6_30_29, CAN0_TX),
-       PINMUX_IPSR_MODSEL_DATA(IP6_30_29, IERX_B, SEL_IE_1),
+       PINMUX_IPSR_MSEL(IP6_30_29, IERX_B, SEL_IE_1),
 
        PINMUX_IPSR_DATA(IP7_1_0, SSI_WS6),
        PINMUX_IPSR_DATA(IP7_1_0, ADICHS1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_1_0, CAN0_RX, SEL_CAN0_0),
+       PINMUX_IPSR_MSEL(IP7_1_0, CAN0_RX, SEL_CAN0_0),
        PINMUX_IPSR_DATA(IP7_1_0, IETX_B),
        PINMUX_IPSR_DATA(IP7_3_2, SSI_SDATA6),
        PINMUX_IPSR_DATA(IP7_3_2, ADICHS2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_3_2, CAN_CLK, SEL_CANCLK_0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_3_2, IECLK_B, SEL_IE_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_6_4, SSI_SCK78, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP7_3_2, CAN_CLK, SEL_CANCLK_0),
+       PINMUX_IPSR_MSEL(IP7_3_2, IECLK_B, SEL_IE_1),
+       PINMUX_IPSR_MSEL(IP7_6_4, SSI_SCK78, SEL_SSI7_0),
        PINMUX_IPSR_DATA(IP7_6_4, CAN_DEBUGOUT13),
-       PINMUX_IPSR_MODSEL_DATA(IP7_6_4, IRQ0_B, SEL_INT0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_6_4, SSI_SCK9_B, SEL_SSI9_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_6_4, HSPI_CLK1_C, SEL_HSPI1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_9_7, SSI_WS78, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP7_6_4, IRQ0_B, SEL_INT0_1),
+       PINMUX_IPSR_MSEL(IP7_6_4, SSI_SCK9_B, SEL_SSI9_1),
+       PINMUX_IPSR_MSEL(IP7_6_4, HSPI_CLK1_C, SEL_HSPI1_2),
+       PINMUX_IPSR_MSEL(IP7_9_7, SSI_WS78, SEL_SSI7_0),
        PINMUX_IPSR_DATA(IP7_9_7, CAN_DEBUGOUT14),
-       PINMUX_IPSR_MODSEL_DATA(IP7_9_7, IRQ1_B, SEL_INT1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_9_7, SSI_WS9_B, SEL_SSI9_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_9_7, HSPI_CS1_C, SEL_HSPI1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_12_10, SSI_SDATA7, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP7_9_7, IRQ1_B, SEL_INT1_1),
+       PINMUX_IPSR_MSEL(IP7_9_7, SSI_WS9_B, SEL_SSI9_1),
+       PINMUX_IPSR_MSEL(IP7_9_7, HSPI_CS1_C, SEL_HSPI1_2),
+       PINMUX_IPSR_MSEL(IP7_12_10, SSI_SDATA7, SEL_SSI7_0),
        PINMUX_IPSR_DATA(IP7_12_10, CAN_DEBUGOUT15),
-       PINMUX_IPSR_MODSEL_DATA(IP7_12_10, IRQ2_B, SEL_INT2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_12_10, TCLK1_C, SEL_TMU1_2),
+       PINMUX_IPSR_MSEL(IP7_12_10, IRQ2_B, SEL_INT2_1),
+       PINMUX_IPSR_MSEL(IP7_12_10, TCLK1_C, SEL_TMU1_2),
        PINMUX_IPSR_DATA(IP7_12_10, HSPI_TX1_C),
-       PINMUX_IPSR_MODSEL_DATA(IP7_14_13, SSI_SDATA8, SEL_SSI8_0),
+       PINMUX_IPSR_MSEL(IP7_14_13, SSI_SDATA8, SEL_SSI8_0),
        PINMUX_IPSR_DATA(IP7_14_13, VSP),
-       PINMUX_IPSR_MODSEL_DATA(IP7_14_13, IRQ3_B, SEL_INT3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_14_13, HSPI_RX1_C, SEL_HSPI1_2),
+       PINMUX_IPSR_MSEL(IP7_14_13, IRQ3_B, SEL_INT3_1),
+       PINMUX_IPSR_MSEL(IP7_14_13, HSPI_RX1_C, SEL_HSPI1_2),
        PINMUX_IPSR_DATA(IP7_16_15, SD0_CLK),
        PINMUX_IPSR_DATA(IP7_16_15, ATACS01),
-       PINMUX_IPSR_MODSEL_DATA(IP7_16_15, SCK1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP7_16_15, SCK1_B, SEL_SCIF1_1),
        PINMUX_IPSR_DATA(IP7_18_17, SD0_CMD),
        PINMUX_IPSR_DATA(IP7_18_17, ATACS11),
        PINMUX_IPSR_DATA(IP7_18_17, TX1_B),
        PINMUX_IPSR_DATA(IP7_18_17, CC5_TDO),
        PINMUX_IPSR_DATA(IP7_20_19, SD0_DAT0),
        PINMUX_IPSR_DATA(IP7_20_19, ATADIR1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_20_19, RX1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP7_20_19, RX1_B, SEL_SCIF1_1),
        PINMUX_IPSR_DATA(IP7_20_19, CC5_TRST),
        PINMUX_IPSR_DATA(IP7_22_21, SD0_DAT1),
        PINMUX_IPSR_DATA(IP7_22_21, ATAG1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_22_21, SCK2_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP7_22_21, SCK2_B, SEL_SCIF2_1),
        PINMUX_IPSR_DATA(IP7_22_21, CC5_TMS),
        PINMUX_IPSR_DATA(IP7_24_23, SD0_DAT2),
        PINMUX_IPSR_DATA(IP7_24_23, ATARD1),
@@ -1122,17 +1121,17 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP7_24_23, CC5_TCK),
        PINMUX_IPSR_DATA(IP7_26_25, SD0_DAT3),
        PINMUX_IPSR_DATA(IP7_26_25, ATAWR1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_26_25, RX2_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP7_26_25, RX2_B, SEL_SCIF2_1),
        PINMUX_IPSR_DATA(IP7_26_25, CC5_TDI),
        PINMUX_IPSR_DATA(IP7_28_27, SD0_CD),
-       PINMUX_IPSR_MODSEL_DATA(IP7_28_27, DREQ2, SEL_EXBUS2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_28_27, RTS1_B_TANS_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP7_28_27, DREQ2, SEL_EXBUS2_0),
+       PINMUX_IPSR_MSEL(IP7_28_27, RTS1_B_TANS_B, SEL_SCIF1_1),
        PINMUX_IPSR_DATA(IP7_30_29, SD0_WP),
        PINMUX_IPSR_DATA(IP7_30_29, DACK2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_30_29, CTS1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP7_30_29, CTS1_B, SEL_SCIF1_1),
 
        PINMUX_IPSR_DATA(IP8_3_0, HSPI_CLK0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_3_0, CTS0, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP8_3_0, CTS0, SEL_SCIF0_0),
        PINMUX_IPSR_DATA(IP8_3_0, USB_OVC0),
        PINMUX_IPSR_DATA(IP8_3_0, AD_CLK),
        PINMUX_IPSR_DATA(IP8_3_0, CC5_STATE4),
@@ -1141,7 +1140,7 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP8_3_0, CC5_STATE28),
        PINMUX_IPSR_DATA(IP8_3_0, CC5_STATE36),
        PINMUX_IPSR_DATA(IP8_7_4, HSPI_CS0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_7_4, RTS0_TANS, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP8_7_4, RTS0_TANS, SEL_SCIF0_0),
        PINMUX_IPSR_DATA(IP8_7_4, USB_OVC1),
        PINMUX_IPSR_DATA(IP8_7_4, AD_DI),
        PINMUX_IPSR_DATA(IP8_7_4, CC5_STATE5),
@@ -1159,7 +1158,7 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP8_11_8, CC5_STATE30),
        PINMUX_IPSR_DATA(IP8_11_8, CC5_STATE38),
        PINMUX_IPSR_DATA(IP8_15_12, HSPI_RX0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_15_12, RX0, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP8_15_12, RX0, SEL_SCIF0_0),
        PINMUX_IPSR_DATA(IP8_15_12, CAN_STEP0),
        PINMUX_IPSR_DATA(IP8_15_12, AD_NCS),
        PINMUX_IPSR_DATA(IP8_15_12, CC5_STATE7),
@@ -1181,25 +1180,25 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP8_22_21, HTX1_B),
        PINMUX_IPSR_DATA(IP8_22_21, MT1_SYNC),
        PINMUX_IPSR_DATA(IP8_24_23, VI0_FIELD),
-       PINMUX_IPSR_MODSEL_DATA(IP8_24_23, RX1_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP8_24_23, HRX1_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP8_24_23, RX1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP8_24_23, HRX1_B, SEL_HSCIF1_1),
        PINMUX_IPSR_DATA(IP8_27_25, VI0_HSYNC),
-       PINMUX_IPSR_MODSEL_DATA(IP8_27_25, VI0_DATA0_B_VI0_B0_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_27_25, CTS1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP8_27_25, VI0_DATA0_B_VI0_B0_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP8_27_25, CTS1_C, SEL_SCIF1_2),
        PINMUX_IPSR_DATA(IP8_27_25, TX4_D),
        PINMUX_IPSR_DATA(IP8_27_25, MMC1_CMD),
-       PINMUX_IPSR_MODSEL_DATA(IP8_27_25, HSCK1_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP8_27_25, HSCK1_B, SEL_HSCIF1_1),
        PINMUX_IPSR_DATA(IP8_30_28, VI0_VSYNC),
-       PINMUX_IPSR_MODSEL_DATA(IP8_30_28, VI0_DATA1_B_VI0_B1_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_30_28, RTS1_C_TANS_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP8_30_28, RX4_D, SEL_SCIF4_3),
-       PINMUX_IPSR_MODSEL_DATA(IP8_30_28, PWMFSW0_C, SEL_PWMFSW_2),
+       PINMUX_IPSR_MSEL(IP8_30_28, VI0_DATA1_B_VI0_B1_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP8_30_28, RTS1_C_TANS_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP8_30_28, RX4_D, SEL_SCIF4_3),
+       PINMUX_IPSR_MSEL(IP8_30_28, PWMFSW0_C, SEL_PWMFSW_2),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_1_0, VI0_DATA0_VI0_B0, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_1_0, HRTS1_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP9_1_0, VI0_DATA0_VI0_B0, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP9_1_0, HRTS1_B, SEL_HSCIF1_1),
        PINMUX_IPSR_DATA(IP9_1_0, MT1_VCXO),
-       PINMUX_IPSR_MODSEL_DATA(IP9_3_2, VI0_DATA1_VI0_B1, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_3_2, HCTS1_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP9_3_2, VI0_DATA1_VI0_B1, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP9_3_2, HCTS1_B, SEL_HSCIF1_1),
        PINMUX_IPSR_DATA(IP9_3_2, MT1_PWM),
        PINMUX_IPSR_DATA(IP9_4, VI0_DATA2_VI0_B2),
        PINMUX_IPSR_DATA(IP9_4, MMC1_D0),
@@ -1216,12 +1215,12 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP9_11_10, MMC1_D5),
        PINMUX_IPSR_DATA(IP9_11_10, ARM_TRACEDATA_1),
        PINMUX_IPSR_DATA(IP9_13_12, VI0_G0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_13_12, SSI_SCK78_C, SEL_SSI7_2),
-       PINMUX_IPSR_MODSEL_DATA(IP9_13_12, IRQ0, SEL_INT0_0),
+       PINMUX_IPSR_MSEL(IP9_13_12, SSI_SCK78_C, SEL_SSI7_2),
+       PINMUX_IPSR_MSEL(IP9_13_12, IRQ0, SEL_INT0_0),
        PINMUX_IPSR_DATA(IP9_13_12, ARM_TRACEDATA_2),
        PINMUX_IPSR_DATA(IP9_15_14, VI0_G1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_14, SSI_WS78_C, SEL_SSI7_2),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_14, IRQ1, SEL_INT1_0),
+       PINMUX_IPSR_MSEL(IP9_15_14, SSI_WS78_C, SEL_SSI7_2),
+       PINMUX_IPSR_MSEL(IP9_15_14, IRQ1, SEL_INT1_0),
        PINMUX_IPSR_DATA(IP9_15_14, ARM_TRACEDATA_3),
        PINMUX_IPSR_DATA(IP9_18_16, VI0_G2),
        PINMUX_IPSR_DATA(IP9_18_16, ETH_TXD1),
@@ -1235,29 +1234,29 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP9_21_19, TS_SDAT0),
        PINMUX_IPSR_DATA(IP9_23_22, VI0_G4),
        PINMUX_IPSR_DATA(IP9_23_22, ETH_TX_EN),
-       PINMUX_IPSR_MODSEL_DATA(IP9_23_22, SD2_DAT0_B, SEL_SD2_1),
+       PINMUX_IPSR_MSEL(IP9_23_22, SD2_DAT0_B, SEL_SD2_1),
        PINMUX_IPSR_DATA(IP9_23_22, ARM_TRACEDATA_6),
        PINMUX_IPSR_DATA(IP9_25_24, VI0_G5),
        PINMUX_IPSR_DATA(IP9_25_24, ETH_RX_ER),
-       PINMUX_IPSR_MODSEL_DATA(IP9_25_24, SD2_DAT1_B, SEL_SD2_1),
+       PINMUX_IPSR_MSEL(IP9_25_24, SD2_DAT1_B, SEL_SD2_1),
        PINMUX_IPSR_DATA(IP9_25_24, ARM_TRACEDATA_7),
        PINMUX_IPSR_DATA(IP9_27_26, VI0_G6),
        PINMUX_IPSR_DATA(IP9_27_26, ETH_RXD0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_27_26, SD2_DAT2_B, SEL_SD2_1),
+       PINMUX_IPSR_MSEL(IP9_27_26, SD2_DAT2_B, SEL_SD2_1),
        PINMUX_IPSR_DATA(IP9_27_26, ARM_TRACEDATA_8),
        PINMUX_IPSR_DATA(IP9_29_28, VI0_G7),
        PINMUX_IPSR_DATA(IP9_29_28, ETH_RXD1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_29_28, SD2_DAT3_B, SEL_SD2_1),
+       PINMUX_IPSR_MSEL(IP9_29_28, SD2_DAT3_B, SEL_SD2_1),
        PINMUX_IPSR_DATA(IP9_29_28, ARM_TRACEDATA_9),
 
        PINMUX_IPSR_DATA(IP10_2_0, VI0_R0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, SSI_SDATA7_C, SEL_SSI7_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, SCK1_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, DREQ1_B, SEL_EXBUS1_0),
+       PINMUX_IPSR_MSEL(IP10_2_0, SSI_SDATA7_C, SEL_SSI7_2),
+       PINMUX_IPSR_MSEL(IP10_2_0, SCK1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP10_2_0, DREQ1_B, SEL_EXBUS1_0),
        PINMUX_IPSR_DATA(IP10_2_0, ARM_TRACEDATA_10),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, DREQ0_C, SEL_EXBUS0_2),
+       PINMUX_IPSR_MSEL(IP10_2_0, DREQ0_C, SEL_EXBUS0_2),
        PINMUX_IPSR_DATA(IP10_5_3, VI0_R1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, SSI_SDATA8_C, SEL_SSI8_2),
+       PINMUX_IPSR_MSEL(IP10_5_3, SSI_SDATA8_C, SEL_SSI8_2),
        PINMUX_IPSR_DATA(IP10_5_3, DACK1_B),
        PINMUX_IPSR_DATA(IP10_5_3, ARM_TRACEDATA_11),
        PINMUX_IPSR_DATA(IP10_5_3, DACK0_C),
@@ -1265,74 +1264,74 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP10_8_6, VI0_R2),
        PINMUX_IPSR_DATA(IP10_8_6, ETH_LINK),
        PINMUX_IPSR_DATA(IP10_8_6, SD2_CLK_B),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, IRQ2, SEL_INT2_0),
+       PINMUX_IPSR_MSEL(IP10_8_6, IRQ2, SEL_INT2_0),
        PINMUX_IPSR_DATA(IP10_8_6, ARM_TRACEDATA_12),
        PINMUX_IPSR_DATA(IP10_11_9, VI0_R3),
        PINMUX_IPSR_DATA(IP10_11_9, ETH_MAGIC),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, SD2_CMD_B, SEL_SD2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, IRQ3, SEL_INT3_0),
+       PINMUX_IPSR_MSEL(IP10_11_9, SD2_CMD_B, SEL_SD2_1),
+       PINMUX_IPSR_MSEL(IP10_11_9, IRQ3, SEL_INT3_0),
        PINMUX_IPSR_DATA(IP10_11_9, ARM_TRACEDATA_13),
        PINMUX_IPSR_DATA(IP10_14_12, VI0_R4),
        PINMUX_IPSR_DATA(IP10_14_12, ETH_REFCLK),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, SD2_CD_B, SEL_SD2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, HSPI_CLK1_B, SEL_HSPI1_1),
+       PINMUX_IPSR_MSEL(IP10_14_12, SD2_CD_B, SEL_SD2_1),
+       PINMUX_IPSR_MSEL(IP10_14_12, HSPI_CLK1_B, SEL_HSPI1_1),
        PINMUX_IPSR_DATA(IP10_14_12, ARM_TRACEDATA_14),
        PINMUX_IPSR_DATA(IP10_14_12, MT1_CLK),
        PINMUX_IPSR_DATA(IP10_14_12, TS_SCK0),
        PINMUX_IPSR_DATA(IP10_17_15, VI0_R5),
        PINMUX_IPSR_DATA(IP10_17_15, ETH_TXD0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_17_15, SD2_WP_B, SEL_SD2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_17_15, HSPI_CS1_B, SEL_HSPI1_1),
+       PINMUX_IPSR_MSEL(IP10_17_15, SD2_WP_B, SEL_SD2_1),
+       PINMUX_IPSR_MSEL(IP10_17_15, HSPI_CS1_B, SEL_HSPI1_1),
        PINMUX_IPSR_DATA(IP10_17_15, ARM_TRACEDATA_15),
        PINMUX_IPSR_DATA(IP10_17_15, MT1_D),
        PINMUX_IPSR_DATA(IP10_17_15, TS_SDEN0),
        PINMUX_IPSR_DATA(IP10_20_18, VI0_R6),
        PINMUX_IPSR_DATA(IP10_20_18, ETH_MDC),
-       PINMUX_IPSR_MODSEL_DATA(IP10_20_18, DREQ2_C, SEL_EXBUS2_2),
+       PINMUX_IPSR_MSEL(IP10_20_18, DREQ2_C, SEL_EXBUS2_2),
        PINMUX_IPSR_DATA(IP10_20_18, HSPI_TX1_B),
        PINMUX_IPSR_DATA(IP10_20_18, TRACECLK),
        PINMUX_IPSR_DATA(IP10_20_18, MT1_BEN),
-       PINMUX_IPSR_MODSEL_DATA(IP10_20_18, PWMFSW0_D, SEL_PWMFSW_3),
+       PINMUX_IPSR_MSEL(IP10_20_18, PWMFSW0_D, SEL_PWMFSW_3),
        PINMUX_IPSR_DATA(IP10_23_21, VI0_R7),
        PINMUX_IPSR_DATA(IP10_23_21, ETH_MDIO),
        PINMUX_IPSR_DATA(IP10_23_21, DACK2_C),
-       PINMUX_IPSR_MODSEL_DATA(IP10_23_21, HSPI_RX1_B, SEL_HSPI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_23_21, SCIF_CLK_D, SEL_SCIF_3),
+       PINMUX_IPSR_MSEL(IP10_23_21, HSPI_RX1_B, SEL_HSPI1_1),
+       PINMUX_IPSR_MSEL(IP10_23_21, SCIF_CLK_D, SEL_SCIF_3),
        PINMUX_IPSR_DATA(IP10_23_21, TRACECTL),
        PINMUX_IPSR_DATA(IP10_23_21, MT1_PEN),
        PINMUX_IPSR_DATA(IP10_25_24, VI1_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP10_25_24, SIM_D, SEL_SIM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_25_24, SDA3, SEL_I2C3_0),
+       PINMUX_IPSR_MSEL(IP10_25_24, SIM_D, SEL_SIM_0),
+       PINMUX_IPSR_MSEL(IP10_25_24, SDA3, SEL_I2C3_0),
        PINMUX_IPSR_DATA(IP10_28_26, VI1_HSYNC),
        PINMUX_IPSR_DATA(IP10_28_26, VI3_CLK),
        PINMUX_IPSR_DATA(IP10_28_26, SSI_SCK4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_28_26, GPS_SIGN_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_28_26, PWMFSW0_E, SEL_PWMFSW_4),
+       PINMUX_IPSR_MSEL(IP10_28_26, GPS_SIGN_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP10_28_26, PWMFSW0_E, SEL_PWMFSW_4),
        PINMUX_IPSR_DATA(IP10_31_29, VI1_VSYNC),
        PINMUX_IPSR_DATA(IP10_31_29, AUDIO_CLKOUT_C),
        PINMUX_IPSR_DATA(IP10_31_29, SSI_WS4),
        PINMUX_IPSR_DATA(IP10_31_29, SIM_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP10_31_29, GPS_MAG_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP10_31_29, GPS_MAG_C, SEL_GPS_2),
        PINMUX_IPSR_DATA(IP10_31_29, SPV_TRST),
-       PINMUX_IPSR_MODSEL_DATA(IP10_31_29, SCL3, SEL_I2C3_0),
+       PINMUX_IPSR_MSEL(IP10_31_29, SCL3, SEL_I2C3_0),
 
        PINMUX_IPSR_DATA(IP11_2_0, VI1_DATA0_VI1_B0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_2_0, SD2_DAT0, SEL_SD2_0),
+       PINMUX_IPSR_MSEL(IP11_2_0, SD2_DAT0, SEL_SD2_0),
        PINMUX_IPSR_DATA(IP11_2_0, SIM_RST),
        PINMUX_IPSR_DATA(IP11_2_0, SPV_TCK),
        PINMUX_IPSR_DATA(IP11_2_0, ADICLK_B),
        PINMUX_IPSR_DATA(IP11_5_3, VI1_DATA1_VI1_B1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_5_3, SD2_DAT1, SEL_SD2_0),
+       PINMUX_IPSR_MSEL(IP11_5_3, SD2_DAT1, SEL_SD2_0),
        PINMUX_IPSR_DATA(IP11_5_3, MT0_CLK),
        PINMUX_IPSR_DATA(IP11_5_3, SPV_TMS),
-       PINMUX_IPSR_MODSEL_DATA(IP11_5_3, ADICS_B_SAMP_B, SEL_ADI_1),
+       PINMUX_IPSR_MSEL(IP11_5_3, ADICS_B_SAMP_B, SEL_ADI_1),
        PINMUX_IPSR_DATA(IP11_8_6, VI1_DATA2_VI1_B2),
-       PINMUX_IPSR_MODSEL_DATA(IP11_8_6, SD2_DAT2, SEL_SD2_0),
+       PINMUX_IPSR_MSEL(IP11_8_6, SD2_DAT2, SEL_SD2_0),
        PINMUX_IPSR_DATA(IP11_8_6, MT0_D),
        PINMUX_IPSR_DATA(IP11_8_6, SPVTDI),
-       PINMUX_IPSR_MODSEL_DATA(IP11_8_6, ADIDATA_B, SEL_ADI_1),
+       PINMUX_IPSR_MSEL(IP11_8_6, ADIDATA_B, SEL_ADI_1),
        PINMUX_IPSR_DATA(IP11_11_9, VI1_DATA3_VI1_B3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_11_9, SD2_DAT3, SEL_SD2_0),
+       PINMUX_IPSR_MSEL(IP11_11_9, SD2_DAT3, SEL_SD2_0),
        PINMUX_IPSR_DATA(IP11_11_9, MT0_BEN),
        PINMUX_IPSR_DATA(IP11_11_9, SPV_TDO),
        PINMUX_IPSR_DATA(IP11_11_9, ADICHS0_B),
@@ -1340,74 +1339,74 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP11_14_12, SD2_CLK),
        PINMUX_IPSR_DATA(IP11_14_12, MT0_PEN),
        PINMUX_IPSR_DATA(IP11_14_12, SPA_TRST),
-       PINMUX_IPSR_MODSEL_DATA(IP11_14_12, HSPI_CLK1_D, SEL_HSPI1_3),
+       PINMUX_IPSR_MSEL(IP11_14_12, HSPI_CLK1_D, SEL_HSPI1_3),
        PINMUX_IPSR_DATA(IP11_14_12, ADICHS1_B),
        PINMUX_IPSR_DATA(IP11_17_15, VI1_DATA5_VI1_B5),
-       PINMUX_IPSR_MODSEL_DATA(IP11_17_15, SD2_CMD, SEL_SD2_0),
+       PINMUX_IPSR_MSEL(IP11_17_15, SD2_CMD, SEL_SD2_0),
        PINMUX_IPSR_DATA(IP11_17_15, MT0_SYNC),
        PINMUX_IPSR_DATA(IP11_17_15, SPA_TCK),
-       PINMUX_IPSR_MODSEL_DATA(IP11_17_15, HSPI_CS1_D, SEL_HSPI1_3),
+       PINMUX_IPSR_MSEL(IP11_17_15, HSPI_CS1_D, SEL_HSPI1_3),
        PINMUX_IPSR_DATA(IP11_17_15, ADICHS2_B),
        PINMUX_IPSR_DATA(IP11_20_18, VI1_DATA6_VI1_B6),
-       PINMUX_IPSR_MODSEL_DATA(IP11_20_18, SD2_CD, SEL_SD2_0),
+       PINMUX_IPSR_MSEL(IP11_20_18, SD2_CD, SEL_SD2_0),
        PINMUX_IPSR_DATA(IP11_20_18, MT0_VCXO),
        PINMUX_IPSR_DATA(IP11_20_18, SPA_TMS),
        PINMUX_IPSR_DATA(IP11_20_18, HSPI_TX1_D),
        PINMUX_IPSR_DATA(IP11_23_21, VI1_DATA7_VI1_B7),
-       PINMUX_IPSR_MODSEL_DATA(IP11_23_21, SD2_WP, SEL_SD2_0),
+       PINMUX_IPSR_MSEL(IP11_23_21, SD2_WP, SEL_SD2_0),
        PINMUX_IPSR_DATA(IP11_23_21, MT0_PWM),
        PINMUX_IPSR_DATA(IP11_23_21, SPA_TDI),
-       PINMUX_IPSR_MODSEL_DATA(IP11_23_21, HSPI_RX1_D, SEL_HSPI1_3),
+       PINMUX_IPSR_MSEL(IP11_23_21, HSPI_RX1_D, SEL_HSPI1_3),
        PINMUX_IPSR_DATA(IP11_26_24, VI1_G0),
        PINMUX_IPSR_DATA(IP11_26_24, VI3_DATA0),
        PINMUX_IPSR_DATA(IP11_26_24, TS_SCK1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_26_24, DREQ2_B, SEL_EXBUS2_1),
+       PINMUX_IPSR_MSEL(IP11_26_24, DREQ2_B, SEL_EXBUS2_1),
        PINMUX_IPSR_DATA(IP11_26_24, TX2),
        PINMUX_IPSR_DATA(IP11_26_24, SPA_TDO),
-       PINMUX_IPSR_MODSEL_DATA(IP11_26_24, HCTS0_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP11_26_24, HCTS0_B, SEL_HSCIF0_1),
        PINMUX_IPSR_DATA(IP11_29_27, VI1_G1),
        PINMUX_IPSR_DATA(IP11_29_27, VI3_DATA1),
        PINMUX_IPSR_DATA(IP11_29_27, SSI_SCK1),
        PINMUX_IPSR_DATA(IP11_29_27, TS_SDEN1),
        PINMUX_IPSR_DATA(IP11_29_27, DACK2_B),
-       PINMUX_IPSR_MODSEL_DATA(IP11_29_27, RX2, SEL_SCIF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_29_27, HRTS0_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP11_29_27, RX2, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP11_29_27, HRTS0_B, SEL_HSCIF0_1),
 
        PINMUX_IPSR_DATA(IP12_2_0, VI1_G2),
        PINMUX_IPSR_DATA(IP12_2_0, VI3_DATA2),
        PINMUX_IPSR_DATA(IP12_2_0, SSI_WS1),
        PINMUX_IPSR_DATA(IP12_2_0, TS_SPSYNC1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_2_0, SCK2, SEL_SCIF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_2_0, HSCK0_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP12_2_0, SCK2, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP12_2_0, HSCK0_B, SEL_HSCIF0_1),
        PINMUX_IPSR_DATA(IP12_5_3, VI1_G3),
        PINMUX_IPSR_DATA(IP12_5_3, VI3_DATA3),
        PINMUX_IPSR_DATA(IP12_5_3, SSI_SCK2),
        PINMUX_IPSR_DATA(IP12_5_3, TS_SDAT1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_5_3, SCL1_C, SEL_I2C1_2),
+       PINMUX_IPSR_MSEL(IP12_5_3, SCL1_C, SEL_I2C1_2),
        PINMUX_IPSR_DATA(IP12_5_3, HTX0_B),
        PINMUX_IPSR_DATA(IP12_8_6, VI1_G4),
        PINMUX_IPSR_DATA(IP12_8_6, VI3_DATA4),
        PINMUX_IPSR_DATA(IP12_8_6, SSI_WS2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_8_6, SDA1_C, SEL_I2C1_2),
+       PINMUX_IPSR_MSEL(IP12_8_6, SDA1_C, SEL_I2C1_2),
        PINMUX_IPSR_DATA(IP12_8_6, SIM_RST_B),
-       PINMUX_IPSR_MODSEL_DATA(IP12_8_6, HRX0_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP12_8_6, HRX0_B, SEL_HSCIF0_1),
        PINMUX_IPSR_DATA(IP12_11_9, VI1_G5),
        PINMUX_IPSR_DATA(IP12_11_9, VI3_DATA5),
-       PINMUX_IPSR_MODSEL_DATA(IP12_11_9, GPS_CLK, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP12_11_9, GPS_CLK, SEL_GPS_0),
        PINMUX_IPSR_DATA(IP12_11_9, FSE),
        PINMUX_IPSR_DATA(IP12_11_9, TX4_B),
-       PINMUX_IPSR_MODSEL_DATA(IP12_11_9, SIM_D_B, SEL_SIM_1),
+       PINMUX_IPSR_MSEL(IP12_11_9, SIM_D_B, SEL_SIM_1),
        PINMUX_IPSR_DATA(IP12_14_12, VI1_G6),
        PINMUX_IPSR_DATA(IP12_14_12, VI3_DATA6),
-       PINMUX_IPSR_MODSEL_DATA(IP12_14_12, GPS_SIGN, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP12_14_12, GPS_SIGN, SEL_GPS_0),
        PINMUX_IPSR_DATA(IP12_14_12, FRB),
-       PINMUX_IPSR_MODSEL_DATA(IP12_14_12, RX4_B, SEL_SCIF4_1),
+       PINMUX_IPSR_MSEL(IP12_14_12, RX4_B, SEL_SCIF4_1),
        PINMUX_IPSR_DATA(IP12_14_12, SIM_CLK_B),
        PINMUX_IPSR_DATA(IP12_17_15, VI1_G7),
        PINMUX_IPSR_DATA(IP12_17_15, VI3_DATA7),
-       PINMUX_IPSR_MODSEL_DATA(IP12_17_15, GPS_MAG, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP12_17_15, GPS_MAG, SEL_GPS_0),
        PINMUX_IPSR_DATA(IP12_17_15, FCE),
-       PINMUX_IPSR_MODSEL_DATA(IP12_17_15, SCK4_B, SEL_SCIF4_1),
+       PINMUX_IPSR_MSEL(IP12_17_15, SCK4_B, SEL_SCIF4_1),
 };
 
 static const struct sh_pfc_pin pinmux_pins[] = {
@@ -3868,6 +3867,6 @@ const struct sh_pfc_soc_info r8a7779_pinmux_info = {
 
        .cfg_regs = pinmux_config_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index fc344a7c2b5382189fff1ca014a45d00ad394c7d..d9924b0d53b789c36cf47fd7c5c133b12c32c729 100644 (file)
@@ -22,7 +22,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/platform_data/gpio-rcar.h>
 
 #include "core.h"
 #include "sh_pfc.h"
@@ -818,103 +817,103 @@ static const u16 pinmux_data[] = {
        PINMUX_DATA(DU_DOTCLKIN2_MARK, FN_DU_DOTCLKIN2),
 
        PINMUX_IPSR_DATA(IP0_2_0, D0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_2_0, MSIOF3_SCK_B, SEL_SOF3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_2_0, VI3_DATA0, SEL_VI3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_2_0, VI0_G4, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_2_0, VI0_G4_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP0_2_0, MSIOF3_SCK_B, SEL_SOF3_1),
+       PINMUX_IPSR_MSEL(IP0_2_0, VI3_DATA0, SEL_VI3_0),
+       PINMUX_IPSR_MSEL(IP0_2_0, VI0_G4, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP0_2_0, VI0_G4_B, SEL_VI0_1),
        PINMUX_IPSR_DATA(IP0_5_3, D1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_5_3, MSIOF3_SYNC_B, SEL_SOF3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_5_3, VI3_DATA1, SEL_VI3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_5_3, VI0_G5, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_5_3, VI0_G5_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP0_5_3, MSIOF3_SYNC_B, SEL_SOF3_1),
+       PINMUX_IPSR_MSEL(IP0_5_3, VI3_DATA1, SEL_VI3_0),
+       PINMUX_IPSR_MSEL(IP0_5_3, VI0_G5, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP0_5_3, VI0_G5_B, SEL_VI0_1),
        PINMUX_IPSR_DATA(IP0_8_6, D2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_8_6, MSIOF3_RXD_B, SEL_SOF3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_8_6, VI3_DATA2, SEL_VI3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_8_6, VI0_G6, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_8_6, VI0_G6_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP0_8_6, MSIOF3_RXD_B, SEL_SOF3_1),
+       PINMUX_IPSR_MSEL(IP0_8_6, VI3_DATA2, SEL_VI3_0),
+       PINMUX_IPSR_MSEL(IP0_8_6, VI0_G6, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP0_8_6, VI0_G6_B, SEL_VI0_1),
        PINMUX_IPSR_DATA(IP0_11_9, D3),
-       PINMUX_IPSR_MODSEL_DATA(IP0_11_9, MSIOF3_TXD_B, SEL_SOF3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_11_9, VI3_DATA3, SEL_VI3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_11_9, VI0_G7, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_11_9, VI0_G7_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP0_11_9, MSIOF3_TXD_B, SEL_SOF3_1),
+       PINMUX_IPSR_MSEL(IP0_11_9, VI3_DATA3, SEL_VI3_0),
+       PINMUX_IPSR_MSEL(IP0_11_9, VI0_G7, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP0_11_9, VI0_G7_B, SEL_VI0_1),
        PINMUX_IPSR_DATA(IP0_15_12, D4),
-       PINMUX_IPSR_MODSEL_DATA(IP0_15_12, SCIFB1_RXD_F, SEL_SCIFB1_5),
-       PINMUX_IPSR_MODSEL_DATA(IP0_15_12, SCIFB0_RXD_C, SEL_SCIFB_2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_15_12, VI3_DATA4, SEL_VI3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_15_12, VI0_R0, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_15_12, VI0_R0_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_15_12, RX0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP0_15_12, SCIFB1_RXD_F, SEL_SCIFB1_5),
+       PINMUX_IPSR_MSEL(IP0_15_12, SCIFB0_RXD_C, SEL_SCIFB_2),
+       PINMUX_IPSR_MSEL(IP0_15_12, VI3_DATA4, SEL_VI3_0),
+       PINMUX_IPSR_MSEL(IP0_15_12, VI0_R0, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP0_15_12, VI0_R0_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP0_15_12, RX0_B, SEL_SCIF0_1),
        PINMUX_IPSR_DATA(IP0_19_16, D5),
-       PINMUX_IPSR_MODSEL_DATA(IP0_19_16, SCIFB1_TXD_F, SEL_SCIFB1_5),
-       PINMUX_IPSR_MODSEL_DATA(IP0_19_16, SCIFB0_TXD_C, SEL_SCIFB_2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_19_16, VI3_DATA5, SEL_VI3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_19_16, VI0_R1, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_19_16, VI0_R1_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_19_16, TX0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP0_19_16, SCIFB1_TXD_F, SEL_SCIFB1_5),
+       PINMUX_IPSR_MSEL(IP0_19_16, SCIFB0_TXD_C, SEL_SCIFB_2),
+       PINMUX_IPSR_MSEL(IP0_19_16, VI3_DATA5, SEL_VI3_0),
+       PINMUX_IPSR_MSEL(IP0_19_16, VI0_R1, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP0_19_16, VI0_R1_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP0_19_16, TX0_B, SEL_SCIF0_1),
        PINMUX_IPSR_DATA(IP0_22_20, D6),
-       PINMUX_IPSR_MODSEL_DATA(IP0_22_20, IIC2_SCL_C, SEL_IIC2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_22_20, VI3_DATA6, SEL_VI3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_22_20, VI0_R2, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_22_20, VI0_R2_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_22_20, I2C2_SCL_C, SEL_I2C2_2),
+       PINMUX_IPSR_MSEL(IP0_22_20, IIC2_SCL_C, SEL_IIC2_2),
+       PINMUX_IPSR_MSEL(IP0_22_20, VI3_DATA6, SEL_VI3_0),
+       PINMUX_IPSR_MSEL(IP0_22_20, VI0_R2, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP0_22_20, VI0_R2_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP0_22_20, I2C2_SCL_C, SEL_I2C2_2),
        PINMUX_IPSR_DATA(IP0_26_23, D7),
-       PINMUX_IPSR_MODSEL_DATA(IP0_26_23, AD_DI_B, SEL_ADI_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_26_23, IIC2_SDA_C, SEL_IIC2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_26_23, VI3_DATA7, SEL_VI3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_26_23, VI0_R3, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_26_23, VI0_R3_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_26_23, I2C2_SDA_C, SEL_I2C2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_26_23, TCLK1, SEL_TMU1_0),
+       PINMUX_IPSR_MSEL(IP0_26_23, AD_DI_B, SEL_ADI_1),
+       PINMUX_IPSR_MSEL(IP0_26_23, IIC2_SDA_C, SEL_IIC2_2),
+       PINMUX_IPSR_MSEL(IP0_26_23, VI3_DATA7, SEL_VI3_0),
+       PINMUX_IPSR_MSEL(IP0_26_23, VI0_R3, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP0_26_23, VI0_R3_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP0_26_23, I2C2_SDA_C, SEL_I2C2_2),
+       PINMUX_IPSR_MSEL(IP0_26_23, TCLK1, SEL_TMU1_0),
        PINMUX_IPSR_DATA(IP0_30_27, D8),
-       PINMUX_IPSR_MODSEL_DATA(IP0_30_27, SCIFA1_SCK_C, SEL_SCIFA1_2),
+       PINMUX_IPSR_MSEL(IP0_30_27, SCIFA1_SCK_C, SEL_SCIFA1_2),
        PINMUX_IPSR_DATA(IP0_30_27, AVB_TXD0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_30_27, VI0_G0, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_30_27, VI0_G0_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_30_27, VI2_DATA0_VI2_B0, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP0_30_27, VI0_G0, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP0_30_27, VI0_G0_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP0_30_27, VI2_DATA0_VI2_B0, SEL_VI2_0),
 
        PINMUX_IPSR_DATA(IP1_3_0, D9),
-       PINMUX_IPSR_MODSEL_DATA(IP1_3_0, SCIFA1_RXD_C, SEL_SCIFA1_2),
+       PINMUX_IPSR_MSEL(IP1_3_0, SCIFA1_RXD_C, SEL_SCIFA1_2),
        PINMUX_IPSR_DATA(IP1_3_0, AVB_TXD1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_3_0, VI0_G1, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_3_0, VI0_G1_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_3_0, VI2_DATA1_VI2_B1, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP1_3_0, VI0_G1, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP1_3_0, VI0_G1_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP1_3_0, VI2_DATA1_VI2_B1, SEL_VI2_0),
        PINMUX_IPSR_DATA(IP1_7_4, D10),
-       PINMUX_IPSR_MODSEL_DATA(IP1_7_4, SCIFA1_TXD_C, SEL_SCIFA1_2),
+       PINMUX_IPSR_MSEL(IP1_7_4, SCIFA1_TXD_C, SEL_SCIFA1_2),
        PINMUX_IPSR_DATA(IP1_7_4, AVB_TXD2),
-       PINMUX_IPSR_MODSEL_DATA(IP1_7_4, VI0_G2, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_7_4, VI0_G2_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_7_4, VI2_DATA2_VI2_B2, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP1_7_4, VI0_G2, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP1_7_4, VI0_G2_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP1_7_4, VI2_DATA2_VI2_B2, SEL_VI2_0),
        PINMUX_IPSR_DATA(IP1_11_8, D11),
-       PINMUX_IPSR_MODSEL_DATA(IP1_11_8, SCIFA1_CTS_N_C, SEL_SCIFA1_2),
+       PINMUX_IPSR_MSEL(IP1_11_8, SCIFA1_CTS_N_C, SEL_SCIFA1_2),
        PINMUX_IPSR_DATA(IP1_11_8, AVB_TXD3),
-       PINMUX_IPSR_MODSEL_DATA(IP1_11_8, VI0_G3, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_11_8, VI0_G3_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_11_8, VI2_DATA3_VI2_B3, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP1_11_8, VI0_G3, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP1_11_8, VI0_G3_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP1_11_8, VI2_DATA3_VI2_B3, SEL_VI2_0),
        PINMUX_IPSR_DATA(IP1_14_12, D12),
-       PINMUX_IPSR_MODSEL_DATA(IP1_14_12, SCIFA1_RTS_N_C, SEL_SCIFA1_2),
+       PINMUX_IPSR_MSEL(IP1_14_12, SCIFA1_RTS_N_C, SEL_SCIFA1_2),
        PINMUX_IPSR_DATA(IP1_14_12, AVB_TXD4),
-       PINMUX_IPSR_MODSEL_DATA(IP1_14_12, VI0_HSYNC_N, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_14_12, VI0_HSYNC_N_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_14_12, VI2_DATA4_VI2_B4, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP1_14_12, VI0_HSYNC_N, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP1_14_12, VI0_HSYNC_N_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP1_14_12, VI2_DATA4_VI2_B4, SEL_VI2_0),
        PINMUX_IPSR_DATA(IP1_17_15, D13),
        PINMUX_IPSR_DATA(IP1_17_15, AVB_TXD5),
-       PINMUX_IPSR_MODSEL_DATA(IP1_17_15, VI0_VSYNC_N, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_17_15, VI0_VSYNC_N_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_17_15, VI2_DATA5_VI2_B5, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP1_17_15, VI0_VSYNC_N, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP1_17_15, VI0_VSYNC_N_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP1_17_15, VI2_DATA5_VI2_B5, SEL_VI2_0),
        PINMUX_IPSR_DATA(IP1_21_18, D14),
-       PINMUX_IPSR_MODSEL_DATA(IP1_21_18, SCIFB1_RXD_C, SEL_SCIFB1_2),
+       PINMUX_IPSR_MSEL(IP1_21_18, SCIFB1_RXD_C, SEL_SCIFB1_2),
        PINMUX_IPSR_DATA(IP1_21_18, AVB_TXD6),
-       PINMUX_IPSR_MODSEL_DATA(IP1_21_18, RX1_B, SEL_SCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_21_18, VI0_CLKENB, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_21_18, VI0_CLKENB_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_21_18, VI2_DATA6_VI2_B6, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP1_21_18, RX1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP1_21_18, VI0_CLKENB, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP1_21_18, VI0_CLKENB_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP1_21_18, VI2_DATA6_VI2_B6, SEL_VI2_0),
        PINMUX_IPSR_DATA(IP1_25_22, D15),
-       PINMUX_IPSR_MODSEL_DATA(IP1_25_22, SCIFB1_TXD_C, SEL_SCIFB1_2),
+       PINMUX_IPSR_MSEL(IP1_25_22, SCIFB1_TXD_C, SEL_SCIFB1_2),
        PINMUX_IPSR_DATA(IP1_25_22, AVB_TXD7),
-       PINMUX_IPSR_MODSEL_DATA(IP1_25_22, TX1_B, SEL_SCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_25_22, VI0_FIELD, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_25_22, VI0_FIELD_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_25_22, VI2_DATA7_VI2_B7, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP1_25_22, TX1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP1_25_22, VI0_FIELD, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP1_25_22, VI0_FIELD_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP1_25_22, VI2_DATA7_VI2_B7, SEL_VI2_0),
        PINMUX_IPSR_DATA(IP1_27_26, A0),
        PINMUX_IPSR_DATA(IP1_27_26, PWM3),
        PINMUX_IPSR_DATA(IP1_29_28, A1),
@@ -922,512 +921,512 @@ static const u16 pinmux_data[] = {
 
        PINMUX_IPSR_DATA(IP2_2_0, A2),
        PINMUX_IPSR_DATA(IP2_2_0, PWM5),
-       PINMUX_IPSR_MODSEL_DATA(IP2_2_0, MSIOF1_SS1_B, SEL_SOF1_1),
+       PINMUX_IPSR_MSEL(IP2_2_0, MSIOF1_SS1_B, SEL_SOF1_1),
        PINMUX_IPSR_DATA(IP2_5_3, A3),
        PINMUX_IPSR_DATA(IP2_5_3, PWM6),
-       PINMUX_IPSR_MODSEL_DATA(IP2_5_3, MSIOF1_SS2_B, SEL_SOF1_1),
+       PINMUX_IPSR_MSEL(IP2_5_3, MSIOF1_SS2_B, SEL_SOF1_1),
        PINMUX_IPSR_DATA(IP2_8_6, A4),
-       PINMUX_IPSR_MODSEL_DATA(IP2_8_6, MSIOF1_TXD_B, SEL_SOF1_1),
+       PINMUX_IPSR_MSEL(IP2_8_6, MSIOF1_TXD_B, SEL_SOF1_1),
        PINMUX_IPSR_DATA(IP2_8_6, TPU0TO0),
        PINMUX_IPSR_DATA(IP2_11_9, A5),
-       PINMUX_IPSR_MODSEL_DATA(IP2_11_9, SCIFA1_TXD_B, SEL_SCIFA1_1),
+       PINMUX_IPSR_MSEL(IP2_11_9, SCIFA1_TXD_B, SEL_SCIFA1_1),
        PINMUX_IPSR_DATA(IP2_11_9, TPU0TO1),
        PINMUX_IPSR_DATA(IP2_14_12, A6),
-       PINMUX_IPSR_MODSEL_DATA(IP2_14_12, SCIFA1_RTS_N_B, SEL_SCIFA1_1),
+       PINMUX_IPSR_MSEL(IP2_14_12, SCIFA1_RTS_N_B, SEL_SCIFA1_1),
        PINMUX_IPSR_DATA(IP2_14_12, TPU0TO2),
        PINMUX_IPSR_DATA(IP2_17_15, A7),
-       PINMUX_IPSR_MODSEL_DATA(IP2_17_15, SCIFA1_SCK_B, SEL_SCIFA1_1),
+       PINMUX_IPSR_MSEL(IP2_17_15, SCIFA1_SCK_B, SEL_SCIFA1_1),
        PINMUX_IPSR_DATA(IP2_17_15, AUDIO_CLKOUT_B),
        PINMUX_IPSR_DATA(IP2_17_15, TPU0TO3),
        PINMUX_IPSR_DATA(IP2_21_18, A8),
-       PINMUX_IPSR_MODSEL_DATA(IP2_21_18, SCIFA1_RXD_B, SEL_SCIFA1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_21_18, SSI_SCK5_B, SEL_SSI5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_21_18, VI0_R4, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_21_18, VI0_R4_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_21_18, SCIFB2_RXD_C, SEL_SCIFB2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP2_21_18, RX2_B, SEL_SCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_21_18, VI2_DATA0_VI2_B0_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP2_21_18, SCIFA1_RXD_B, SEL_SCIFA1_1),
+       PINMUX_IPSR_MSEL(IP2_21_18, SSI_SCK5_B, SEL_SSI5_1),
+       PINMUX_IPSR_MSEL(IP2_21_18, VI0_R4, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP2_21_18, VI0_R4_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP2_21_18, SCIFB2_RXD_C, SEL_SCIFB2_2),
+       PINMUX_IPSR_MSEL(IP2_21_18, RX2_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP2_21_18, VI2_DATA0_VI2_B0_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP2_25_22, A9),
-       PINMUX_IPSR_MODSEL_DATA(IP2_25_22, SCIFA1_CTS_N_B, SEL_SCIFA1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_25_22, SSI_WS5_B, SEL_SSI5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_25_22, VI0_R5, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_25_22, VI0_R5_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_25_22, SCIFB2_TXD_C, SEL_SCIFB2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP2_25_22, TX2_B, SEL_SCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_25_22, VI2_DATA1_VI2_B1_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP2_25_22, SCIFA1_CTS_N_B, SEL_SCIFA1_1),
+       PINMUX_IPSR_MSEL(IP2_25_22, SSI_WS5_B, SEL_SSI5_1),
+       PINMUX_IPSR_MSEL(IP2_25_22, VI0_R5, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP2_25_22, VI0_R5_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP2_25_22, SCIFB2_TXD_C, SEL_SCIFB2_2),
+       PINMUX_IPSR_MSEL(IP2_25_22, TX2_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP2_25_22, VI2_DATA1_VI2_B1_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP2_28_26, A10),
-       PINMUX_IPSR_MODSEL_DATA(IP2_28_26, SSI_SDATA5_B, SEL_SSI5_1),
+       PINMUX_IPSR_MSEL(IP2_28_26, SSI_SDATA5_B, SEL_SSI5_1),
        PINMUX_IPSR_DATA(IP2_28_26, MSIOF2_SYNC),
-       PINMUX_IPSR_MODSEL_DATA(IP2_28_26, VI0_R6, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_28_26, VI0_R6_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_28_26, VI2_DATA2_VI2_B2_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP2_28_26, VI0_R6, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP2_28_26, VI0_R6_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP2_28_26, VI2_DATA2_VI2_B2_B, SEL_VI2_1),
 
        PINMUX_IPSR_DATA(IP3_3_0, A11),
-       PINMUX_IPSR_MODSEL_DATA(IP3_3_0, SCIFB2_CTS_N_B, SEL_SCIFB2_1),
+       PINMUX_IPSR_MSEL(IP3_3_0, SCIFB2_CTS_N_B, SEL_SCIFB2_1),
        PINMUX_IPSR_DATA(IP3_3_0, MSIOF2_SCK),
-       PINMUX_IPSR_MODSEL_DATA(IP3_3_0, VI1_R0, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_3_0, VI1_R0_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP3_3_0, VI1_R0, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP3_3_0, VI1_R0_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP3_3_0, VI2_G0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_3_0, VI2_DATA3_VI2_B3_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP3_3_0, VI2_DATA3_VI2_B3_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP3_7_4, A12),
-       PINMUX_IPSR_MODSEL_DATA(IP3_7_4, SCIFB2_RXD_B, SEL_SCIFB2_1),
+       PINMUX_IPSR_MSEL(IP3_7_4, SCIFB2_RXD_B, SEL_SCIFB2_1),
        PINMUX_IPSR_DATA(IP3_7_4, MSIOF2_TXD),
-       PINMUX_IPSR_MODSEL_DATA(IP3_7_4, VI1_R1, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_7_4, VI1_R1_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP3_7_4, VI1_R1, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP3_7_4, VI1_R1_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP3_7_4, VI2_G1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_7_4, VI2_DATA4_VI2_B4_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP3_7_4, VI2_DATA4_VI2_B4_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP3_11_8, A13),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_8, SCIFB2_RTS_N_B, SEL_SCIFB2_1),
+       PINMUX_IPSR_MSEL(IP3_11_8, SCIFB2_RTS_N_B, SEL_SCIFB2_1),
        PINMUX_IPSR_DATA(IP3_11_8, EX_WAIT2),
        PINMUX_IPSR_DATA(IP3_11_8, MSIOF2_RXD),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_8, VI1_R2, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_8, VI1_R2_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP3_11_8, VI1_R2, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP3_11_8, VI1_R2_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP3_11_8, VI2_G2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_8, VI2_DATA5_VI2_B5_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP3_11_8, VI2_DATA5_VI2_B5_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP3_14_12, A14),
-       PINMUX_IPSR_MODSEL_DATA(IP3_14_12, SCIFB2_TXD_B, SEL_SCIFB2_1),
+       PINMUX_IPSR_MSEL(IP3_14_12, SCIFB2_TXD_B, SEL_SCIFB2_1),
        PINMUX_IPSR_DATA(IP3_14_12, ATACS11_N),
        PINMUX_IPSR_DATA(IP3_14_12, MSIOF2_SS1),
        PINMUX_IPSR_DATA(IP3_17_15, A15),
-       PINMUX_IPSR_MODSEL_DATA(IP3_17_15, SCIFB2_SCK_B, SEL_SCIFB2_1),
+       PINMUX_IPSR_MSEL(IP3_17_15, SCIFB2_SCK_B, SEL_SCIFB2_1),
        PINMUX_IPSR_DATA(IP3_17_15, ATARD1_N),
        PINMUX_IPSR_DATA(IP3_17_15, MSIOF2_SS2),
        PINMUX_IPSR_DATA(IP3_19_18, A16),
        PINMUX_IPSR_DATA(IP3_19_18, ATAWR1_N),
        PINMUX_IPSR_DATA(IP3_22_20, A17),
-       PINMUX_IPSR_MODSEL_DATA(IP3_22_20, AD_DO_B, SEL_ADI_1),
+       PINMUX_IPSR_MSEL(IP3_22_20, AD_DO_B, SEL_ADI_1),
        PINMUX_IPSR_DATA(IP3_22_20, ATADIR1_N),
        PINMUX_IPSR_DATA(IP3_25_23, A18),
-       PINMUX_IPSR_MODSEL_DATA(IP3_25_23, AD_CLK_B, SEL_ADI_1),
+       PINMUX_IPSR_MSEL(IP3_25_23, AD_CLK_B, SEL_ADI_1),
        PINMUX_IPSR_DATA(IP3_25_23, ATAG1_N),
        PINMUX_IPSR_DATA(IP3_28_26, A19),
-       PINMUX_IPSR_MODSEL_DATA(IP3_28_26, AD_NCS_N_B, SEL_ADI_1),
+       PINMUX_IPSR_MSEL(IP3_28_26, AD_NCS_N_B, SEL_ADI_1),
        PINMUX_IPSR_DATA(IP3_28_26, ATACS01_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_28_26, EX_WAIT0_B, SEL_LBS_1),
+       PINMUX_IPSR_MSEL(IP3_28_26, EX_WAIT0_B, SEL_LBS_1),
        PINMUX_IPSR_DATA(IP3_31_29, A20),
        PINMUX_IPSR_DATA(IP3_31_29, SPCLK),
-       PINMUX_IPSR_MODSEL_DATA(IP3_31_29, VI1_R3, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_31_29, VI1_R3_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP3_31_29, VI1_R3, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP3_31_29, VI1_R3_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP3_31_29, VI2_G4),
 
        PINMUX_IPSR_DATA(IP4_2_0, A21),
        PINMUX_IPSR_DATA(IP4_2_0, MOSI_IO0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_2_0, VI1_R4, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_2_0, VI1_R4_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP4_2_0, VI1_R4, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP4_2_0, VI1_R4_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP4_2_0, VI2_G5),
        PINMUX_IPSR_DATA(IP4_5_3, A22),
        PINMUX_IPSR_DATA(IP4_5_3, MISO_IO1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_5_3, VI1_R5, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_5_3, VI1_R5_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP4_5_3, VI1_R5, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP4_5_3, VI1_R5_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP4_5_3, VI2_G6),
        PINMUX_IPSR_DATA(IP4_8_6, A23),
        PINMUX_IPSR_DATA(IP4_8_6, IO2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_8_6, VI1_G7, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_8_6, VI1_G7_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP4_8_6, VI1_G7, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP4_8_6, VI1_G7_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP4_8_6, VI2_G7),
        PINMUX_IPSR_DATA(IP4_11_9, A24),
        PINMUX_IPSR_DATA(IP4_11_9, IO3),
-       PINMUX_IPSR_MODSEL_DATA(IP4_11_9, VI1_R7, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_11_9, VI1_R7_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_11_9, VI2_CLKENB, SEL_VI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_11_9, VI2_CLKENB_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP4_11_9, VI1_R7, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP4_11_9, VI1_R7_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP4_11_9, VI2_CLKENB, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP4_11_9, VI2_CLKENB_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP4_14_12, A25),
        PINMUX_IPSR_DATA(IP4_14_12, SSL),
-       PINMUX_IPSR_MODSEL_DATA(IP4_14_12, VI1_G6, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_14_12, VI1_G6_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_14_12, VI2_FIELD, SEL_VI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_14_12, VI2_FIELD_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP4_14_12, VI1_G6, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP4_14_12, VI1_G6_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP4_14_12, VI2_FIELD, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP4_14_12, VI2_FIELD_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP4_17_15, CS0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP4_17_15, VI1_R6, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_17_15, VI1_R6_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP4_17_15, VI1_R6, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP4_17_15, VI1_R6_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP4_17_15, VI2_G3),
-       PINMUX_IPSR_MODSEL_DATA(IP4_17_15, MSIOF0_SS2_B, SEL_SOF0_1),
+       PINMUX_IPSR_MSEL(IP4_17_15, MSIOF0_SS2_B, SEL_SOF0_1),
        PINMUX_IPSR_DATA(IP4_20_18, CS1_N_A26),
        PINMUX_IPSR_DATA(IP4_20_18, SPEEDIN),
-       PINMUX_IPSR_MODSEL_DATA(IP4_20_18, VI0_R7, SEL_VI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_20_18, VI0_R7_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_20_18, VI2_CLK, SEL_VI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_20_18, VI2_CLK_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP4_20_18, VI0_R7, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP4_20_18, VI0_R7_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP4_20_18, VI2_CLK, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP4_20_18, VI2_CLK_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP4_23_21, EX_CS0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP4_23_21, HRX1_B, SEL_HSCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_23_21, VI1_G5, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_23_21, VI1_G5_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP4_23_21, HRX1_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP4_23_21, VI1_G5, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP4_23_21, VI1_G5_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP4_23_21, VI2_R0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_23_21, HTX0_B, SEL_HSCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_23_21, MSIOF0_SS1_B, SEL_SOF0_1),
+       PINMUX_IPSR_MSEL(IP4_23_21, HTX0_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP4_23_21, MSIOF0_SS1_B, SEL_SOF0_1),
        PINMUX_IPSR_DATA(IP4_26_24, EX_CS1_N),
        PINMUX_IPSR_DATA(IP4_26_24, GPS_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP4_26_24, HCTS1_N_B, SEL_HSCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_26_24, VI1_FIELD, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_26_24, VI1_FIELD_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP4_26_24, HCTS1_N_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP4_26_24, VI1_FIELD, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP4_26_24, VI1_FIELD_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP4_26_24, VI2_R1),
        PINMUX_IPSR_DATA(IP4_29_27, EX_CS2_N),
        PINMUX_IPSR_DATA(IP4_29_27, GPS_SIGN),
-       PINMUX_IPSR_MODSEL_DATA(IP4_29_27, HRTS1_N_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP4_29_27, HRTS1_N_B, SEL_HSCIF1_1),
        PINMUX_IPSR_DATA(IP4_29_27, VI3_CLKENB),
-       PINMUX_IPSR_MODSEL_DATA(IP4_29_27, VI1_G0, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_29_27, VI1_G0_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP4_29_27, VI1_G0, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP4_29_27, VI1_G0_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP4_29_27, VI2_R2),
 
        PINMUX_IPSR_DATA(IP5_2_0, EX_CS3_N),
        PINMUX_IPSR_DATA(IP5_2_0, GPS_MAG),
        PINMUX_IPSR_DATA(IP5_2_0, VI3_FIELD),
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, VI1_G1, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, VI1_G1_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP5_2_0, VI1_G1, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP5_2_0, VI1_G1_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP5_2_0, VI2_R3),
        PINMUX_IPSR_DATA(IP5_5_3, EX_CS4_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, MSIOF1_SCK_B, SEL_SOF1_1),
+       PINMUX_IPSR_MSEL(IP5_5_3, MSIOF1_SCK_B, SEL_SOF1_1),
        PINMUX_IPSR_DATA(IP5_5_3, VI3_HSYNC_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, VI2_HSYNC_N, SEL_VI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, IIC1_SCL, SEL_IIC1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, VI2_HSYNC_N_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP5_5_3, VI2_HSYNC_N, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP5_5_3, IIC1_SCL, SEL_IIC1_0),
+       PINMUX_IPSR_MSEL(IP5_5_3, VI2_HSYNC_N_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP5_5_3, INTC_EN0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, I2C1_SCL, SEL_I2C1_0),
+       PINMUX_IPSR_MSEL(IP5_5_3, I2C1_SCL, SEL_I2C1_0),
        PINMUX_IPSR_DATA(IP5_9_6, EX_CS5_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_9_6, CAN0_RX, SEL_CAN0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_9_6, MSIOF1_RXD_B, SEL_SOF1_1),
+       PINMUX_IPSR_MSEL(IP5_9_6, CAN0_RX, SEL_CAN0_0),
+       PINMUX_IPSR_MSEL(IP5_9_6, MSIOF1_RXD_B, SEL_SOF1_1),
        PINMUX_IPSR_DATA(IP5_9_6, VI3_VSYNC_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_9_6, VI1_G2, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_9_6, VI1_G2_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP5_9_6, VI1_G2, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP5_9_6, VI1_G2_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP5_9_6, VI2_R4),
-       PINMUX_IPSR_MODSEL_DATA(IP5_9_6, IIC1_SDA, SEL_IIC1_0),
+       PINMUX_IPSR_MSEL(IP5_9_6, IIC1_SDA, SEL_IIC1_0),
        PINMUX_IPSR_DATA(IP5_9_6, INTC_EN1_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_9_6, I2C1_SDA, SEL_I2C1_0),
+       PINMUX_IPSR_MSEL(IP5_9_6, I2C1_SDA, SEL_I2C1_0),
        PINMUX_IPSR_DATA(IP5_12_10, BS_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_12_10, IETX, SEL_IEB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_12_10, HTX1_B, SEL_HSCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_12_10, CAN1_TX, SEL_CAN1_0),
+       PINMUX_IPSR_MSEL(IP5_12_10, IETX, SEL_IEB_0),
+       PINMUX_IPSR_MSEL(IP5_12_10, HTX1_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP5_12_10, CAN1_TX, SEL_CAN1_0),
        PINMUX_IPSR_DATA(IP5_12_10, DRACK0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_12_10, IETX_C, SEL_IEB_2),
+       PINMUX_IPSR_MSEL(IP5_12_10, IETX_C, SEL_IEB_2),
        PINMUX_IPSR_DATA(IP5_14_13, RD_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_14_13, CAN0_TX, SEL_CAN0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_14_13, SCIFA0_SCK_B, SEL_SCFA_1),
+       PINMUX_IPSR_MSEL(IP5_14_13, CAN0_TX, SEL_CAN0_0),
+       PINMUX_IPSR_MSEL(IP5_14_13, SCIFA0_SCK_B, SEL_SCFA_1),
        PINMUX_IPSR_DATA(IP5_17_15, RD_WR_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_17_15, VI1_G3, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_17_15, VI1_G3_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP5_17_15, VI1_G3, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP5_17_15, VI1_G3_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP5_17_15, VI2_R5),
-       PINMUX_IPSR_MODSEL_DATA(IP5_17_15, SCIFA0_RXD_B, SEL_SCFA_1),
+       PINMUX_IPSR_MSEL(IP5_17_15, SCIFA0_RXD_B, SEL_SCFA_1),
        PINMUX_IPSR_DATA(IP5_17_15, INTC_IRQ4_N),
        PINMUX_IPSR_DATA(IP5_20_18, WE0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_20_18, IECLK, SEL_IEB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_20_18, CAN_CLK, SEL_CANCLK_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_20_18, VI2_VSYNC_N, SEL_VI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_20_18, SCIFA0_TXD_B, SEL_SCFA_1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_20_18, VI2_VSYNC_N_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP5_20_18, IECLK, SEL_IEB_0),
+       PINMUX_IPSR_MSEL(IP5_20_18, CAN_CLK, SEL_CANCLK_0),
+       PINMUX_IPSR_MSEL(IP5_20_18, VI2_VSYNC_N, SEL_VI2_0),
+       PINMUX_IPSR_MSEL(IP5_20_18, SCIFA0_TXD_B, SEL_SCFA_1),
+       PINMUX_IPSR_MSEL(IP5_20_18, VI2_VSYNC_N_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP5_23_21, WE1_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_21, IERX, SEL_IEB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_21, CAN1_RX, SEL_CAN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_21, VI1_G4, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_21, VI1_G4_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP5_23_21, IERX, SEL_IEB_0),
+       PINMUX_IPSR_MSEL(IP5_23_21, CAN1_RX, SEL_CAN1_0),
+       PINMUX_IPSR_MSEL(IP5_23_21, VI1_G4, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP5_23_21, VI1_G4_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP5_23_21, VI2_R6),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_21, SCIFA0_CTS_N_B, SEL_SCFA_1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_21, IERX_C, SEL_IEB_2),
-       PINMUX_IPSR_MODSEL_DATA(IP5_26_24, EX_WAIT0, SEL_LBS_0),
+       PINMUX_IPSR_MSEL(IP5_23_21, SCIFA0_CTS_N_B, SEL_SCFA_1),
+       PINMUX_IPSR_MSEL(IP5_23_21, IERX_C, SEL_IEB_2),
+       PINMUX_IPSR_MSEL(IP5_26_24, EX_WAIT0, SEL_LBS_0),
        PINMUX_IPSR_DATA(IP5_26_24, IRQ3),
        PINMUX_IPSR_DATA(IP5_26_24, INTC_IRQ3_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_26_24, VI3_CLK, SEL_VI3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_26_24, SCIFA0_RTS_N_B, SEL_SCFA_1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_26_24, HRX0_B, SEL_HSCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_26_24, MSIOF0_SCK_B, SEL_SOF0_1),
+       PINMUX_IPSR_MSEL(IP5_26_24, VI3_CLK, SEL_VI3_0),
+       PINMUX_IPSR_MSEL(IP5_26_24, SCIFA0_RTS_N_B, SEL_SCFA_1),
+       PINMUX_IPSR_MSEL(IP5_26_24, HRX0_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP5_26_24, MSIOF0_SCK_B, SEL_SOF0_1),
        PINMUX_IPSR_DATA(IP5_29_27, DREQ0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP5_29_27, VI1_HSYNC_N, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_29_27, VI1_HSYNC_N_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP5_29_27, VI1_HSYNC_N, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP5_29_27, VI1_HSYNC_N_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP5_29_27, VI2_R7),
-       PINMUX_IPSR_MODSEL_DATA(IP5_29_27, SSI_SCK78_C, SEL_SSI7_2),
-       PINMUX_IPSR_MODSEL_DATA(IP5_29_27, SSI_WS78_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP5_29_27, SSI_SCK78_C, SEL_SSI7_2),
+       PINMUX_IPSR_MSEL(IP5_29_27, SSI_WS78_B, SEL_SSI7_1),
 
        PINMUX_IPSR_DATA(IP6_2_0, DACK0),
        PINMUX_IPSR_DATA(IP6_2_0, IRQ0),
        PINMUX_IPSR_DATA(IP6_2_0, INTC_IRQ0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, SSI_SCK6_B, SEL_SSI6_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, VI1_VSYNC_N, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, VI1_VSYNC_N_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, SSI_WS78_C, SEL_SSI7_2),
+       PINMUX_IPSR_MSEL(IP6_2_0, SSI_SCK6_B, SEL_SSI6_1),
+       PINMUX_IPSR_MSEL(IP6_2_0, VI1_VSYNC_N, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP6_2_0, VI1_VSYNC_N_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP6_2_0, SSI_WS78_C, SEL_SSI7_2),
        PINMUX_IPSR_DATA(IP6_5_3, DREQ1_N),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, VI1_CLKENB, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, VI1_CLKENB_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, SSI_SDATA7_C, SEL_SSI7_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, SSI_SCK78_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP6_5_3, VI1_CLKENB, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP6_5_3, VI1_CLKENB_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP6_5_3, SSI_SDATA7_C, SEL_SSI7_2),
+       PINMUX_IPSR_MSEL(IP6_5_3, SSI_SCK78_B, SEL_SSI7_1),
        PINMUX_IPSR_DATA(IP6_8_6, DACK1),
        PINMUX_IPSR_DATA(IP6_8_6, IRQ1),
        PINMUX_IPSR_DATA(IP6_8_6, INTC_IRQ1_N),
-       PINMUX_IPSR_MODSEL_DATA(IP6_8_6, SSI_WS6_B, SEL_SSI6_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_8_6, SSI_SDATA8_C, SEL_SSI8_2),
+       PINMUX_IPSR_MSEL(IP6_8_6, SSI_WS6_B, SEL_SSI6_1),
+       PINMUX_IPSR_MSEL(IP6_8_6, SSI_SDATA8_C, SEL_SSI8_2),
        PINMUX_IPSR_DATA(IP6_10_9, DREQ2_N),
-       PINMUX_IPSR_MODSEL_DATA(IP6_10_9, HSCK1_B, SEL_HSCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_10_9, HCTS0_N_B, SEL_HSCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_10_9, MSIOF0_TXD_B, SEL_SOF0_1),
+       PINMUX_IPSR_MSEL(IP6_10_9, HSCK1_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP6_10_9, HCTS0_N_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP6_10_9, MSIOF0_TXD_B, SEL_SOF0_1),
        PINMUX_IPSR_DATA(IP6_13_11, DACK2),
        PINMUX_IPSR_DATA(IP6_13_11, IRQ2),
        PINMUX_IPSR_DATA(IP6_13_11, INTC_IRQ2_N),
-       PINMUX_IPSR_MODSEL_DATA(IP6_13_11, SSI_SDATA6_B, SEL_SSI6_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_13_11, HRTS0_N_B, SEL_HSCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_13_11, MSIOF0_RXD_B, SEL_SOF0_1),
+       PINMUX_IPSR_MSEL(IP6_13_11, SSI_SDATA6_B, SEL_SSI6_1),
+       PINMUX_IPSR_MSEL(IP6_13_11, HRTS0_N_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP6_13_11, MSIOF0_RXD_B, SEL_SOF0_1),
        PINMUX_IPSR_DATA(IP6_16_14, ETH_CRS_DV),
-       PINMUX_IPSR_MODSEL_DATA(IP6_16_14, STP_ISCLK_0_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_16_14, TS_SDEN0_D, SEL_TSIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP6_16_14, GLO_Q0_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_16_14, IIC2_SCL_E, SEL_IIC2_4),
-       PINMUX_IPSR_MODSEL_DATA(IP6_16_14, I2C2_SCL_E, SEL_I2C2_4),
+       PINMUX_IPSR_MSEL(IP6_16_14, STP_ISCLK_0_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP6_16_14, TS_SDEN0_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP6_16_14, GLO_Q0_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP6_16_14, IIC2_SCL_E, SEL_IIC2_4),
+       PINMUX_IPSR_MSEL(IP6_16_14, I2C2_SCL_E, SEL_I2C2_4),
        PINMUX_IPSR_DATA(IP6_19_17, ETH_RX_ER),
-       PINMUX_IPSR_MODSEL_DATA(IP6_19_17, STP_ISD_0_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_19_17, TS_SPSYNC0_D, SEL_TSIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP6_19_17, GLO_Q1_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_19_17, IIC2_SDA_E, SEL_IIC2_4),
-       PINMUX_IPSR_MODSEL_DATA(IP6_19_17, I2C2_SDA_E, SEL_I2C2_4),
+       PINMUX_IPSR_MSEL(IP6_19_17, STP_ISD_0_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP6_19_17, TS_SPSYNC0_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP6_19_17, GLO_Q1_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP6_19_17, IIC2_SDA_E, SEL_IIC2_4),
+       PINMUX_IPSR_MSEL(IP6_19_17, I2C2_SDA_E, SEL_I2C2_4),
        PINMUX_IPSR_DATA(IP6_22_20, ETH_RXD0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_22_20, STP_ISEN_0_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_22_20, TS_SDAT0_D, SEL_TSIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP6_22_20, GLO_I0_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_22_20, SCIFB1_SCK_G, SEL_SCIFB1_6),
-       PINMUX_IPSR_MODSEL_DATA(IP6_22_20, SCK1_E, SEL_SCIF1_4),
+       PINMUX_IPSR_MSEL(IP6_22_20, STP_ISEN_0_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP6_22_20, TS_SDAT0_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP6_22_20, GLO_I0_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP6_22_20, SCIFB1_SCK_G, SEL_SCIFB1_6),
+       PINMUX_IPSR_MSEL(IP6_22_20, SCK1_E, SEL_SCIF1_4),
        PINMUX_IPSR_DATA(IP6_25_23, ETH_RXD1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_25_23, HRX0_E, SEL_HSCIF0_4),
-       PINMUX_IPSR_MODSEL_DATA(IP6_25_23, STP_ISSYNC_0_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_25_23, TS_SCK0_D, SEL_TSIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP6_25_23, GLO_I1_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_25_23, SCIFB1_RXD_G, SEL_SCIFB1_6),
-       PINMUX_IPSR_MODSEL_DATA(IP6_25_23, RX1_E, SEL_SCIF1_4),
+       PINMUX_IPSR_MSEL(IP6_25_23, HRX0_E, SEL_HSCIF0_4),
+       PINMUX_IPSR_MSEL(IP6_25_23, STP_ISSYNC_0_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP6_25_23, TS_SCK0_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP6_25_23, GLO_I1_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP6_25_23, SCIFB1_RXD_G, SEL_SCIFB1_6),
+       PINMUX_IPSR_MSEL(IP6_25_23, RX1_E, SEL_SCIF1_4),
        PINMUX_IPSR_DATA(IP6_28_26, ETH_LINK),
-       PINMUX_IPSR_MODSEL_DATA(IP6_28_26, HTX0_E, SEL_HSCIF0_4),
-       PINMUX_IPSR_MODSEL_DATA(IP6_28_26, STP_IVCXO27_0_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_28_26, SCIFB1_TXD_G, SEL_SCIFB1_6),
-       PINMUX_IPSR_MODSEL_DATA(IP6_28_26, TX1_E, SEL_SCIF1_4),
+       PINMUX_IPSR_MSEL(IP6_28_26, HTX0_E, SEL_HSCIF0_4),
+       PINMUX_IPSR_MSEL(IP6_28_26, STP_IVCXO27_0_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP6_28_26, SCIFB1_TXD_G, SEL_SCIFB1_6),
+       PINMUX_IPSR_MSEL(IP6_28_26, TX1_E, SEL_SCIF1_4),
        PINMUX_IPSR_DATA(IP6_31_29, ETH_REF_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP6_31_29, HCTS0_N_E, SEL_HSCIF0_4),
-       PINMUX_IPSR_MODSEL_DATA(IP6_31_29, STP_IVCXO27_1_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_31_29, HRX0_F, SEL_HSCIF0_5),
+       PINMUX_IPSR_MSEL(IP6_31_29, HCTS0_N_E, SEL_HSCIF0_4),
+       PINMUX_IPSR_MSEL(IP6_31_29, STP_IVCXO27_1_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP6_31_29, HRX0_F, SEL_HSCIF0_5),
 
        PINMUX_IPSR_DATA(IP7_2_0, ETH_MDIO),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, HRTS0_N_E, SEL_HSCIF0_4),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, SIM0_D_C, SEL_SIM_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, HCTS0_N_F, SEL_HSCIF0_5),
+       PINMUX_IPSR_MSEL(IP7_2_0, HRTS0_N_E, SEL_HSCIF0_4),
+       PINMUX_IPSR_MSEL(IP7_2_0, SIM0_D_C, SEL_SIM_2),
+       PINMUX_IPSR_MSEL(IP7_2_0, HCTS0_N_F, SEL_HSCIF0_5),
        PINMUX_IPSR_DATA(IP7_5_3, ETH_TXD1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, HTX0_F, SEL_HSCIF0_5),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, BPFCLK_G, SEL_FM_6),
+       PINMUX_IPSR_MSEL(IP7_5_3, HTX0_F, SEL_HSCIF0_5),
+       PINMUX_IPSR_MSEL(IP7_5_3, BPFCLK_G, SEL_FM_6),
        PINMUX_IPSR_DATA(IP7_7_6, ETH_TX_EN),
-       PINMUX_IPSR_MODSEL_DATA(IP7_7_6, SIM0_CLK_C, SEL_SIM_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_7_6, HRTS0_N_F, SEL_HSCIF0_5),
+       PINMUX_IPSR_MSEL(IP7_7_6, SIM0_CLK_C, SEL_SIM_2),
+       PINMUX_IPSR_MSEL(IP7_7_6, HRTS0_N_F, SEL_HSCIF0_5),
        PINMUX_IPSR_DATA(IP7_9_8, ETH_MAGIC),
-       PINMUX_IPSR_MODSEL_DATA(IP7_9_8, SIM0_RST_C, SEL_SIM_2),
+       PINMUX_IPSR_MSEL(IP7_9_8, SIM0_RST_C, SEL_SIM_2),
        PINMUX_IPSR_DATA(IP7_12_10, ETH_TXD0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_12_10, STP_ISCLK_1_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_12_10, TS_SDEN1_C, SEL_TSIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_12_10, GLO_SCLK_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP7_12_10, STP_ISCLK_1_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP7_12_10, TS_SDEN1_C, SEL_TSIF1_2),
+       PINMUX_IPSR_MSEL(IP7_12_10, GLO_SCLK_C, SEL_GPS_2),
        PINMUX_IPSR_DATA(IP7_15_13, ETH_MDC),
-       PINMUX_IPSR_MODSEL_DATA(IP7_15_13, STP_ISD_1_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_15_13, TS_SPSYNC1_C, SEL_TSIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_15_13, GLO_SDATA_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP7_15_13, STP_ISD_1_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP7_15_13, TS_SPSYNC1_C, SEL_TSIF1_2),
+       PINMUX_IPSR_MSEL(IP7_15_13, GLO_SDATA_C, SEL_GPS_2),
        PINMUX_IPSR_DATA(IP7_18_16, PWM0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_18_16, SCIFA2_SCK_C, SEL_SCIFA2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_18_16, STP_ISEN_1_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_18_16, TS_SDAT1_C, SEL_TSIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_18_16, GLO_SS_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP7_18_16, SCIFA2_SCK_C, SEL_SCIFA2_2),
+       PINMUX_IPSR_MSEL(IP7_18_16, STP_ISEN_1_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP7_18_16, TS_SDAT1_C, SEL_TSIF1_2),
+       PINMUX_IPSR_MSEL(IP7_18_16, GLO_SS_C, SEL_GPS_2),
        PINMUX_IPSR_DATA(IP7_21_19, PWM1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_21_19, SCIFA2_TXD_C, SEL_SCIFA2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_21_19, STP_ISSYNC_1_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_21_19, TS_SCK1_C, SEL_TSIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_21_19, GLO_RFON_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP7_21_19, SCIFA2_TXD_C, SEL_SCIFA2_2),
+       PINMUX_IPSR_MSEL(IP7_21_19, STP_ISSYNC_1_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP7_21_19, TS_SCK1_C, SEL_TSIF1_2),
+       PINMUX_IPSR_MSEL(IP7_21_19, GLO_RFON_C, SEL_GPS_2),
        PINMUX_IPSR_DATA(IP7_21_19, PCMOE_N),
        PINMUX_IPSR_DATA(IP7_24_22, PWM2),
        PINMUX_IPSR_DATA(IP7_24_22, PWMFSW0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_24_22, SCIFA2_RXD_C, SEL_SCIFA2_2),
+       PINMUX_IPSR_MSEL(IP7_24_22, SCIFA2_RXD_C, SEL_SCIFA2_2),
        PINMUX_IPSR_DATA(IP7_24_22, PCMWE_N),
-       PINMUX_IPSR_MODSEL_DATA(IP7_24_22, IECLK_C, SEL_IEB_2),
+       PINMUX_IPSR_MSEL(IP7_24_22, IECLK_C, SEL_IEB_2),
        PINMUX_IPSR_DATA(IP7_26_25, DU_DOTCLKIN1),
        PINMUX_IPSR_DATA(IP7_26_25, AUDIO_CLKC),
        PINMUX_IPSR_DATA(IP7_26_25, AUDIO_CLKOUT_C),
-       PINMUX_IPSR_MODSEL_DATA(IP7_28_27, VI0_CLK, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP7_28_27, VI0_CLK, SEL_VI0_0),
        PINMUX_IPSR_DATA(IP7_28_27, ATACS00_N),
        PINMUX_IPSR_DATA(IP7_28_27, AVB_RXD1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_30_29, VI0_DATA0_VI0_B0, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP7_30_29, VI0_DATA0_VI0_B0, SEL_VI0_0),
        PINMUX_IPSR_DATA(IP7_30_29, ATACS10_N),
        PINMUX_IPSR_DATA(IP7_30_29, AVB_RXD2),
 
-       PINMUX_IPSR_MODSEL_DATA(IP8_1_0, VI0_DATA1_VI0_B1, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP8_1_0, VI0_DATA1_VI0_B1, SEL_VI0_0),
        PINMUX_IPSR_DATA(IP8_1_0, ATARD0_N),
        PINMUX_IPSR_DATA(IP8_1_0, AVB_RXD3),
-       PINMUX_IPSR_MODSEL_DATA(IP8_3_2, VI0_DATA2_VI0_B2, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP8_3_2, VI0_DATA2_VI0_B2, SEL_VI0_0),
        PINMUX_IPSR_DATA(IP8_3_2, ATAWR0_N),
        PINMUX_IPSR_DATA(IP8_3_2, AVB_RXD4),
-       PINMUX_IPSR_MODSEL_DATA(IP8_5_4, VI0_DATA3_VI0_B3, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP8_5_4, VI0_DATA3_VI0_B3, SEL_VI0_0),
        PINMUX_IPSR_DATA(IP8_5_4, ATADIR0_N),
        PINMUX_IPSR_DATA(IP8_5_4, AVB_RXD5),
-       PINMUX_IPSR_MODSEL_DATA(IP8_7_6, VI0_DATA4_VI0_B4, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP8_7_6, VI0_DATA4_VI0_B4, SEL_VI0_0),
        PINMUX_IPSR_DATA(IP8_7_6, ATAG0_N),
        PINMUX_IPSR_DATA(IP8_7_6, AVB_RXD6),
-       PINMUX_IPSR_MODSEL_DATA(IP8_9_8, VI0_DATA5_VI0_B5, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP8_9_8, VI0_DATA5_VI0_B5, SEL_VI0_0),
        PINMUX_IPSR_DATA(IP8_9_8, EX_WAIT1),
        PINMUX_IPSR_DATA(IP8_9_8, AVB_RXD7),
-       PINMUX_IPSR_MODSEL_DATA(IP8_11_10, VI0_DATA6_VI0_B6, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP8_11_10, VI0_DATA6_VI0_B6, SEL_VI0_0),
        PINMUX_IPSR_DATA(IP8_11_10, AVB_RX_ER),
-       PINMUX_IPSR_MODSEL_DATA(IP8_13_12, VI0_DATA7_VI0_B7, SEL_VI0_0),
+       PINMUX_IPSR_MSEL(IP8_13_12, VI0_DATA7_VI0_B7, SEL_VI0_0),
        PINMUX_IPSR_DATA(IP8_13_12, AVB_RX_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP8_15_14, VI1_CLK, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP8_15_14, VI1_CLK, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP8_15_14, AVB_RX_DV),
-       PINMUX_IPSR_MODSEL_DATA(IP8_17_16, VI1_DATA0_VI1_B0, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_17_16, SCIFA1_SCK_D, SEL_SCIFA1_3),
+       PINMUX_IPSR_MSEL(IP8_17_16, VI1_DATA0_VI1_B0, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP8_17_16, SCIFA1_SCK_D, SEL_SCIFA1_3),
        PINMUX_IPSR_DATA(IP8_17_16, AVB_CRS),
-       PINMUX_IPSR_MODSEL_DATA(IP8_19_18, VI1_DATA1_VI1_B1, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_19_18, SCIFA1_RXD_D, SEL_SCIFA1_3),
+       PINMUX_IPSR_MSEL(IP8_19_18, VI1_DATA1_VI1_B1, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP8_19_18, SCIFA1_RXD_D, SEL_SCIFA1_3),
        PINMUX_IPSR_DATA(IP8_19_18, AVB_MDC),
-       PINMUX_IPSR_MODSEL_DATA(IP8_21_20, VI1_DATA2_VI1_B2, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_21_20, SCIFA1_TXD_D, SEL_SCIFA1_3),
+       PINMUX_IPSR_MSEL(IP8_21_20, VI1_DATA2_VI1_B2, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP8_21_20, SCIFA1_TXD_D, SEL_SCIFA1_3),
        PINMUX_IPSR_DATA(IP8_21_20, AVB_MDIO),
-       PINMUX_IPSR_MODSEL_DATA(IP8_23_22, VI1_DATA3_VI1_B3, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_23_22, SCIFA1_CTS_N_D, SEL_SCIFA1_3),
+       PINMUX_IPSR_MSEL(IP8_23_22, VI1_DATA3_VI1_B3, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP8_23_22, SCIFA1_CTS_N_D, SEL_SCIFA1_3),
        PINMUX_IPSR_DATA(IP8_23_22, AVB_GTX_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP8_25_24, VI1_DATA4_VI1_B4, SEL_VI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_25_24, SCIFA1_RTS_N_D, SEL_SCIFA1_3),
+       PINMUX_IPSR_MSEL(IP8_25_24, VI1_DATA4_VI1_B4, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP8_25_24, SCIFA1_RTS_N_D, SEL_SCIFA1_3),
        PINMUX_IPSR_DATA(IP8_25_24, AVB_MAGIC),
-       PINMUX_IPSR_MODSEL_DATA(IP8_26, VI1_DATA5_VI1_B5, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP8_26, VI1_DATA5_VI1_B5, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP8_26, AVB_PHY_INT),
-       PINMUX_IPSR_MODSEL_DATA(IP8_27, VI1_DATA6_VI1_B6, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP8_27, VI1_DATA6_VI1_B6, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP8_27, AVB_GTXREFCLK),
        PINMUX_IPSR_DATA(IP8_28, SD0_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP8_28, VI1_DATA0_VI1_B0_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP8_28, VI1_DATA0_VI1_B0_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP8_30_29, SD0_CMD),
-       PINMUX_IPSR_MODSEL_DATA(IP8_30_29, SCIFB1_SCK_B, SEL_SCIFB1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_30_29, VI1_DATA1_VI1_B1_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP8_30_29, SCIFB1_SCK_B, SEL_SCIFB1_1),
+       PINMUX_IPSR_MSEL(IP8_30_29, VI1_DATA1_VI1_B1_B, SEL_VI1_1),
 
        PINMUX_IPSR_DATA(IP9_1_0, SD0_DAT0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_1_0, SCIFB1_RXD_B, SEL_SCIFB1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_1_0, VI1_DATA2_VI1_B2_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP9_1_0, SCIFB1_RXD_B, SEL_SCIFB1_1),
+       PINMUX_IPSR_MSEL(IP9_1_0, VI1_DATA2_VI1_B2_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP9_3_2, SD0_DAT1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_3_2, SCIFB1_TXD_B, SEL_SCIFB1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_3_2, VI1_DATA3_VI1_B3_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP9_3_2, SCIFB1_TXD_B, SEL_SCIFB1_1),
+       PINMUX_IPSR_MSEL(IP9_3_2, VI1_DATA3_VI1_B3_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP9_5_4, SD0_DAT2),
-       PINMUX_IPSR_MODSEL_DATA(IP9_5_4, SCIFB1_CTS_N_B, SEL_SCIFB1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_5_4, VI1_DATA4_VI1_B4_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP9_5_4, SCIFB1_CTS_N_B, SEL_SCIFB1_1),
+       PINMUX_IPSR_MSEL(IP9_5_4, VI1_DATA4_VI1_B4_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP9_7_6, SD0_DAT3),
-       PINMUX_IPSR_MODSEL_DATA(IP9_7_6, SCIFB1_RTS_N_B, SEL_SCIFB1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_7_6, VI1_DATA5_VI1_B5_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP9_7_6, SCIFB1_RTS_N_B, SEL_SCIFB1_1),
+       PINMUX_IPSR_MSEL(IP9_7_6, VI1_DATA5_VI1_B5_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP9_11_8, SD0_CD),
        PINMUX_IPSR_DATA(IP9_11_8, MMC0_D6),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_8, TS_SDEN0_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP9_11_8, TS_SDEN0_B, SEL_TSIF0_1),
        PINMUX_IPSR_DATA(IP9_11_8, USB0_EXTP),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_8, GLO_SCLK, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_8, VI1_DATA6_VI1_B6_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_8, IIC1_SCL_B, SEL_IIC1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_8, I2C1_SCL_B, SEL_I2C1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_8, VI2_DATA6_VI2_B6_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP9_11_8, GLO_SCLK, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP9_11_8, VI1_DATA6_VI1_B6_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP9_11_8, IIC1_SCL_B, SEL_IIC1_1),
+       PINMUX_IPSR_MSEL(IP9_11_8, I2C1_SCL_B, SEL_I2C1_1),
+       PINMUX_IPSR_MSEL(IP9_11_8, VI2_DATA6_VI2_B6_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP9_15_12, SD0_WP),
        PINMUX_IPSR_DATA(IP9_15_12, MMC0_D7),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_12, TS_SPSYNC0_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP9_15_12, TS_SPSYNC0_B, SEL_TSIF0_1),
        PINMUX_IPSR_DATA(IP9_15_12, USB0_IDIN),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_12, GLO_SDATA, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_12, VI1_DATA7_VI1_B7_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_12, IIC1_SDA_B, SEL_IIC1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_12, I2C1_SDA_B, SEL_I2C1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_12, VI2_DATA7_VI2_B7_B, SEL_VI2_1),
+       PINMUX_IPSR_MSEL(IP9_15_12, GLO_SDATA, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP9_15_12, VI1_DATA7_VI1_B7_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP9_15_12, IIC1_SDA_B, SEL_IIC1_1),
+       PINMUX_IPSR_MSEL(IP9_15_12, I2C1_SDA_B, SEL_I2C1_1),
+       PINMUX_IPSR_MSEL(IP9_15_12, VI2_DATA7_VI2_B7_B, SEL_VI2_1),
        PINMUX_IPSR_DATA(IP9_17_16, SD1_CLK),
        PINMUX_IPSR_DATA(IP9_17_16, AVB_TX_EN),
        PINMUX_IPSR_DATA(IP9_19_18, SD1_CMD),
        PINMUX_IPSR_DATA(IP9_19_18, AVB_TX_ER),
-       PINMUX_IPSR_MODSEL_DATA(IP9_19_18, SCIFB0_SCK_B, SEL_SCIFB_1),
+       PINMUX_IPSR_MSEL(IP9_19_18, SCIFB0_SCK_B, SEL_SCIFB_1),
        PINMUX_IPSR_DATA(IP9_21_20, SD1_DAT0),
        PINMUX_IPSR_DATA(IP9_21_20, AVB_TX_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP9_21_20, SCIFB0_RXD_B, SEL_SCIFB_1),
+       PINMUX_IPSR_MSEL(IP9_21_20, SCIFB0_RXD_B, SEL_SCIFB_1),
        PINMUX_IPSR_DATA(IP9_23_22, SD1_DAT1),
        PINMUX_IPSR_DATA(IP9_23_22, AVB_LINK),
-       PINMUX_IPSR_MODSEL_DATA(IP9_23_22, SCIFB0_TXD_B, SEL_SCIFB_1),
+       PINMUX_IPSR_MSEL(IP9_23_22, SCIFB0_TXD_B, SEL_SCIFB_1),
        PINMUX_IPSR_DATA(IP9_25_24, SD1_DAT2),
        PINMUX_IPSR_DATA(IP9_25_24, AVB_COL),
-       PINMUX_IPSR_MODSEL_DATA(IP9_25_24, SCIFB0_CTS_N_B, SEL_SCIFB_1),
+       PINMUX_IPSR_MSEL(IP9_25_24, SCIFB0_CTS_N_B, SEL_SCIFB_1),
        PINMUX_IPSR_DATA(IP9_27_26, SD1_DAT3),
        PINMUX_IPSR_DATA(IP9_27_26, AVB_RXD0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_27_26, SCIFB0_RTS_N_B, SEL_SCIFB_1),
+       PINMUX_IPSR_MSEL(IP9_27_26, SCIFB0_RTS_N_B, SEL_SCIFB_1),
        PINMUX_IPSR_DATA(IP9_31_28, SD1_CD),
        PINMUX_IPSR_DATA(IP9_31_28, MMC1_D6),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_28, TS_SDEN1, SEL_TSIF1_0),
+       PINMUX_IPSR_MSEL(IP9_31_28, TS_SDEN1, SEL_TSIF1_0),
        PINMUX_IPSR_DATA(IP9_31_28, USB1_EXTP),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_28, GLO_SS, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_28, VI0_CLK_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_28, IIC2_SCL_D, SEL_IIC2_3),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_28, I2C2_SCL_D, SEL_I2C2_3),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_28, SIM0_CLK_B, SEL_SIM_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_28, VI3_CLK_B, SEL_VI3_1),
+       PINMUX_IPSR_MSEL(IP9_31_28, GLO_SS, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP9_31_28, VI0_CLK_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP9_31_28, IIC2_SCL_D, SEL_IIC2_3),
+       PINMUX_IPSR_MSEL(IP9_31_28, I2C2_SCL_D, SEL_I2C2_3),
+       PINMUX_IPSR_MSEL(IP9_31_28, SIM0_CLK_B, SEL_SIM_1),
+       PINMUX_IPSR_MSEL(IP9_31_28, VI3_CLK_B, SEL_VI3_1),
 
        PINMUX_IPSR_DATA(IP10_3_0, SD1_WP),
        PINMUX_IPSR_DATA(IP10_3_0, MMC1_D7),
-       PINMUX_IPSR_MODSEL_DATA(IP10_3_0, TS_SPSYNC1, SEL_TSIF1_0),
+       PINMUX_IPSR_MSEL(IP10_3_0, TS_SPSYNC1, SEL_TSIF1_0),
        PINMUX_IPSR_DATA(IP10_3_0, USB1_IDIN),
-       PINMUX_IPSR_MODSEL_DATA(IP10_3_0, GLO_RFON, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_3_0, VI1_CLK_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_3_0, IIC2_SDA_D, SEL_IIC2_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_3_0, I2C2_SDA_D, SEL_I2C2_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_3_0, SIM0_D_B, SEL_SIM_1),
+       PINMUX_IPSR_MSEL(IP10_3_0, GLO_RFON, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP10_3_0, VI1_CLK_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP10_3_0, IIC2_SDA_D, SEL_IIC2_3),
+       PINMUX_IPSR_MSEL(IP10_3_0, I2C2_SDA_D, SEL_I2C2_3),
+       PINMUX_IPSR_MSEL(IP10_3_0, SIM0_D_B, SEL_SIM_1),
        PINMUX_IPSR_DATA(IP10_6_4, SD2_CLK),
        PINMUX_IPSR_DATA(IP10_6_4, MMC0_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP10_6_4, SIM0_CLK, SEL_SIM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_6_4, VI0_DATA0_VI0_B0_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_6_4, TS_SDEN0_C, SEL_TSIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_6_4, GLO_SCLK_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_6_4, VI3_DATA0_B, SEL_VI3_1),
+       PINMUX_IPSR_MSEL(IP10_6_4, SIM0_CLK, SEL_SIM_0),
+       PINMUX_IPSR_MSEL(IP10_6_4, VI0_DATA0_VI0_B0_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP10_6_4, TS_SDEN0_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP10_6_4, GLO_SCLK_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_6_4, VI3_DATA0_B, SEL_VI3_1),
        PINMUX_IPSR_DATA(IP10_10_7, SD2_CMD),
        PINMUX_IPSR_DATA(IP10_10_7, MMC0_CMD),
-       PINMUX_IPSR_MODSEL_DATA(IP10_10_7, SIM0_D, SEL_SIM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_10_7, VI0_DATA1_VI0_B1_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_10_7, SCIFB1_SCK_E, SEL_SCIFB1_4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_10_7, SCK1_D, SEL_SCIF1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_10_7, TS_SPSYNC0_C, SEL_TSIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_10_7, GLO_SDATA_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_10_7, VI3_DATA1_B, SEL_VI3_1),
+       PINMUX_IPSR_MSEL(IP10_10_7, SIM0_D, SEL_SIM_0),
+       PINMUX_IPSR_MSEL(IP10_10_7, VI0_DATA1_VI0_B1_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP10_10_7, SCIFB1_SCK_E, SEL_SCIFB1_4),
+       PINMUX_IPSR_MSEL(IP10_10_7, SCK1_D, SEL_SCIF1_3),
+       PINMUX_IPSR_MSEL(IP10_10_7, TS_SPSYNC0_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP10_10_7, GLO_SDATA_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_10_7, VI3_DATA1_B, SEL_VI3_1),
        PINMUX_IPSR_DATA(IP10_14_11, SD2_DAT0),
        PINMUX_IPSR_DATA(IP10_14_11, MMC0_D0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_11, FMCLK_B, SEL_FM_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_11, VI0_DATA2_VI0_B2_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_11, SCIFB1_RXD_E, SEL_SCIFB1_4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_11, RX1_D, SEL_SCIF1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_11, TS_SDAT0_C, SEL_TSIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_11, GLO_SS_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_11, VI3_DATA2_B, SEL_VI3_1),
+       PINMUX_IPSR_MSEL(IP10_14_11, FMCLK_B, SEL_FM_1),
+       PINMUX_IPSR_MSEL(IP10_14_11, VI0_DATA2_VI0_B2_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP10_14_11, SCIFB1_RXD_E, SEL_SCIFB1_4),
+       PINMUX_IPSR_MSEL(IP10_14_11, RX1_D, SEL_SCIF1_3),
+       PINMUX_IPSR_MSEL(IP10_14_11, TS_SDAT0_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP10_14_11, GLO_SS_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_14_11, VI3_DATA2_B, SEL_VI3_1),
        PINMUX_IPSR_DATA(IP10_18_15, SD2_DAT1),
        PINMUX_IPSR_DATA(IP10_18_15, MMC0_D1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_15, FMIN_B, SEL_FM_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_15, VI0_DATA3_VI0_B3_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_15, SCIFB1_TXD_E, SEL_SCIFB1_4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_15, TX1_D, SEL_SCIF1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_15, TS_SCK0_C, SEL_TSIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_15, GLO_RFON_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_15, VI3_DATA3_B, SEL_VI3_1),
+       PINMUX_IPSR_MSEL(IP10_18_15, FMIN_B, SEL_FM_1),
+       PINMUX_IPSR_MSEL(IP10_18_15, VI0_DATA3_VI0_B3_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP10_18_15, SCIFB1_TXD_E, SEL_SCIFB1_4),
+       PINMUX_IPSR_MSEL(IP10_18_15, TX1_D, SEL_SCIF1_3),
+       PINMUX_IPSR_MSEL(IP10_18_15, TS_SCK0_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP10_18_15, GLO_RFON_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_18_15, VI3_DATA3_B, SEL_VI3_1),
        PINMUX_IPSR_DATA(IP10_22_19, SD2_DAT2),
        PINMUX_IPSR_DATA(IP10_22_19, MMC0_D2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_22_19, BPFCLK_B, SEL_FM_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_22_19, VI0_DATA4_VI0_B4_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_22_19, HRX0_D, SEL_HSCIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_22_19, TS_SDEN1_B, SEL_TSIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_22_19, GLO_Q0_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_22_19, VI3_DATA4_B, SEL_VI3_1),
+       PINMUX_IPSR_MSEL(IP10_22_19, BPFCLK_B, SEL_FM_1),
+       PINMUX_IPSR_MSEL(IP10_22_19, VI0_DATA4_VI0_B4_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP10_22_19, HRX0_D, SEL_HSCIF0_3),
+       PINMUX_IPSR_MSEL(IP10_22_19, TS_SDEN1_B, SEL_TSIF1_1),
+       PINMUX_IPSR_MSEL(IP10_22_19, GLO_Q0_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_22_19, VI3_DATA4_B, SEL_VI3_1),
        PINMUX_IPSR_DATA(IP10_25_23, SD2_DAT3),
        PINMUX_IPSR_DATA(IP10_25_23, MMC0_D3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_25_23, SIM0_RST, SEL_SIM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_25_23, VI0_DATA5_VI0_B5_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_25_23, HTX0_D, SEL_HSCIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_25_23, TS_SPSYNC1_B, SEL_TSIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_25_23, GLO_Q1_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_25_23, VI3_DATA5_B, SEL_VI3_1),
+       PINMUX_IPSR_MSEL(IP10_25_23, SIM0_RST, SEL_SIM_0),
+       PINMUX_IPSR_MSEL(IP10_25_23, VI0_DATA5_VI0_B5_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP10_25_23, HTX0_D, SEL_HSCIF0_3),
+       PINMUX_IPSR_MSEL(IP10_25_23, TS_SPSYNC1_B, SEL_TSIF1_1),
+       PINMUX_IPSR_MSEL(IP10_25_23, GLO_Q1_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_25_23, VI3_DATA5_B, SEL_VI3_1),
        PINMUX_IPSR_DATA(IP10_29_26, SD2_CD),
        PINMUX_IPSR_DATA(IP10_29_26, MMC0_D4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_26, TS_SDAT0_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP10_29_26, TS_SDAT0_B, SEL_TSIF0_1),
        PINMUX_IPSR_DATA(IP10_29_26, USB2_EXTP),
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_26, GLO_I0, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_26, VI0_DATA6_VI0_B6_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_26, HCTS0_N_D, SEL_HSCIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_26, TS_SDAT1_B, SEL_TSIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_26, GLO_I0_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_26, VI3_DATA6_B, SEL_VI3_1),
+       PINMUX_IPSR_MSEL(IP10_29_26, GLO_I0, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP10_29_26, VI0_DATA6_VI0_B6_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP10_29_26, HCTS0_N_D, SEL_HSCIF0_3),
+       PINMUX_IPSR_MSEL(IP10_29_26, TS_SDAT1_B, SEL_TSIF1_1),
+       PINMUX_IPSR_MSEL(IP10_29_26, GLO_I0_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_29_26, VI3_DATA6_B, SEL_VI3_1),
 
        PINMUX_IPSR_DATA(IP11_3_0, SD2_WP),
        PINMUX_IPSR_DATA(IP11_3_0, MMC0_D5),
-       PINMUX_IPSR_MODSEL_DATA(IP11_3_0, TS_SCK0_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP11_3_0, TS_SCK0_B, SEL_TSIF0_1),
        PINMUX_IPSR_DATA(IP11_3_0, USB2_IDIN),
-       PINMUX_IPSR_MODSEL_DATA(IP11_3_0, GLO_I1, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_3_0, VI0_DATA7_VI0_B7_B, SEL_VI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_3_0, HRTS0_N_D, SEL_HSCIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_3_0, TS_SCK1_B, SEL_TSIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_3_0, GLO_I1_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_3_0, VI3_DATA7_B, SEL_VI3_1),
+       PINMUX_IPSR_MSEL(IP11_3_0, GLO_I1, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP11_3_0, VI0_DATA7_VI0_B7_B, SEL_VI0_1),
+       PINMUX_IPSR_MSEL(IP11_3_0, HRTS0_N_D, SEL_HSCIF0_3),
+       PINMUX_IPSR_MSEL(IP11_3_0, TS_SCK1_B, SEL_TSIF1_1),
+       PINMUX_IPSR_MSEL(IP11_3_0, GLO_I1_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP11_3_0, VI3_DATA7_B, SEL_VI3_1),
        PINMUX_IPSR_DATA(IP11_4, SD3_CLK),
        PINMUX_IPSR_DATA(IP11_4, MMC1_CLK),
        PINMUX_IPSR_DATA(IP11_6_5, SD3_CMD),
@@ -1447,298 +1446,298 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP11_14_13, SCKZ),
        PINMUX_IPSR_DATA(IP11_17_15, SD3_CD),
        PINMUX_IPSR_DATA(IP11_17_15, MMC1_D4),
-       PINMUX_IPSR_MODSEL_DATA(IP11_17_15, TS_SDAT1, SEL_TSIF1_0),
+       PINMUX_IPSR_MSEL(IP11_17_15, TS_SDAT1, SEL_TSIF1_0),
        PINMUX_IPSR_DATA(IP11_17_15, VSP),
-       PINMUX_IPSR_MODSEL_DATA(IP11_17_15, GLO_Q0, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_17_15, SIM0_RST_B, SEL_SIM_1),
+       PINMUX_IPSR_MSEL(IP11_17_15, GLO_Q0, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP11_17_15, SIM0_RST_B, SEL_SIM_1),
        PINMUX_IPSR_DATA(IP11_21_18, SD3_WP),
        PINMUX_IPSR_DATA(IP11_21_18, MMC1_D5),
-       PINMUX_IPSR_MODSEL_DATA(IP11_21_18, TS_SCK1, SEL_TSIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_21_18, GLO_Q1, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_21_18, FMIN_C, SEL_FM_2),
-       PINMUX_IPSR_MODSEL_DATA(IP11_21_18, FMIN_E, SEL_FM_4),
-       PINMUX_IPSR_MODSEL_DATA(IP11_21_18, FMIN_F, SEL_FM_5),
+       PINMUX_IPSR_MSEL(IP11_21_18, TS_SCK1, SEL_TSIF1_0),
+       PINMUX_IPSR_MSEL(IP11_21_18, GLO_Q1, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP11_21_18, FMIN_C, SEL_FM_2),
+       PINMUX_IPSR_MSEL(IP11_21_18, FMIN_E, SEL_FM_4),
+       PINMUX_IPSR_MSEL(IP11_21_18, FMIN_F, SEL_FM_5),
        PINMUX_IPSR_DATA(IP11_23_22, MLB_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP11_23_22, IIC2_SCL_B, SEL_IIC2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_23_22, I2C2_SCL_B, SEL_I2C2_1),
+       PINMUX_IPSR_MSEL(IP11_23_22, IIC2_SCL_B, SEL_IIC2_1),
+       PINMUX_IPSR_MSEL(IP11_23_22, I2C2_SCL_B, SEL_I2C2_1),
        PINMUX_IPSR_DATA(IP11_26_24, MLB_SIG),
-       PINMUX_IPSR_MODSEL_DATA(IP11_26_24, SCIFB1_RXD_D, SEL_SCIFB1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_26_24, RX1_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP11_26_24, IIC2_SDA_B, SEL_IIC2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_26_24, I2C2_SDA_B, SEL_I2C2_1),
+       PINMUX_IPSR_MSEL(IP11_26_24, SCIFB1_RXD_D, SEL_SCIFB1_3),
+       PINMUX_IPSR_MSEL(IP11_26_24, RX1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP11_26_24, IIC2_SDA_B, SEL_IIC2_1),
+       PINMUX_IPSR_MSEL(IP11_26_24, I2C2_SDA_B, SEL_I2C2_1),
        PINMUX_IPSR_DATA(IP11_29_27, MLB_DAT),
-       PINMUX_IPSR_MODSEL_DATA(IP11_29_27, SCIFB1_TXD_D, SEL_SCIFB1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_29_27, TX1_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP11_29_27, BPFCLK_C, SEL_FM_2),
+       PINMUX_IPSR_MSEL(IP11_29_27, SCIFB1_TXD_D, SEL_SCIFB1_3),
+       PINMUX_IPSR_MSEL(IP11_29_27, TX1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP11_29_27, BPFCLK_C, SEL_FM_2),
        PINMUX_IPSR_DATA(IP11_31_30, SSI_SCK0129),
-       PINMUX_IPSR_MODSEL_DATA(IP11_31_30, CAN_CLK_B, SEL_CANCLK_1),
+       PINMUX_IPSR_MSEL(IP11_31_30, CAN_CLK_B, SEL_CANCLK_1),
        PINMUX_IPSR_DATA(IP11_31_30, MOUT0),
 
        PINMUX_IPSR_DATA(IP12_1_0, SSI_WS0129),
-       PINMUX_IPSR_MODSEL_DATA(IP12_1_0, CAN0_TX_B, SEL_CAN0_1),
+       PINMUX_IPSR_MSEL(IP12_1_0, CAN0_TX_B, SEL_CAN0_1),
        PINMUX_IPSR_DATA(IP12_1_0, MOUT1),
        PINMUX_IPSR_DATA(IP12_3_2, SSI_SDATA0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_3_2, CAN0_RX_B, SEL_CAN0_1),
+       PINMUX_IPSR_MSEL(IP12_3_2, CAN0_RX_B, SEL_CAN0_1),
        PINMUX_IPSR_DATA(IP12_3_2, MOUT2),
        PINMUX_IPSR_DATA(IP12_5_4, SSI_SDATA1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_5_4, CAN1_TX_B, SEL_CAN1_1),
+       PINMUX_IPSR_MSEL(IP12_5_4, CAN1_TX_B, SEL_CAN1_1),
        PINMUX_IPSR_DATA(IP12_5_4, MOUT5),
        PINMUX_IPSR_DATA(IP12_7_6, SSI_SDATA2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_7_6, CAN1_RX_B, SEL_CAN1_1),
+       PINMUX_IPSR_MSEL(IP12_7_6, CAN1_RX_B, SEL_CAN1_1),
        PINMUX_IPSR_DATA(IP12_7_6, SSI_SCK1),
        PINMUX_IPSR_DATA(IP12_7_6, MOUT6),
        PINMUX_IPSR_DATA(IP12_10_8, SSI_SCK34),
        PINMUX_IPSR_DATA(IP12_10_8, STP_OPWM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_10_8, SCIFB0_SCK, SEL_SCIFB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_10_8, MSIOF1_SCK, SEL_SOF1_0),
+       PINMUX_IPSR_MSEL(IP12_10_8, SCIFB0_SCK, SEL_SCIFB_0),
+       PINMUX_IPSR_MSEL(IP12_10_8, MSIOF1_SCK, SEL_SOF1_0),
        PINMUX_IPSR_DATA(IP12_10_8, CAN_DEBUG_HW_TRIGGER),
        PINMUX_IPSR_DATA(IP12_13_11, SSI_WS34),
-       PINMUX_IPSR_MODSEL_DATA(IP12_13_11, STP_IVCXO27_0, SEL_SSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_13_11, SCIFB0_RXD, SEL_SCIFB_0),
+       PINMUX_IPSR_MSEL(IP12_13_11, STP_IVCXO27_0, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP12_13_11, SCIFB0_RXD, SEL_SCIFB_0),
        PINMUX_IPSR_DATA(IP12_13_11, MSIOF1_SYNC),
        PINMUX_IPSR_DATA(IP12_13_11, CAN_STEP0),
        PINMUX_IPSR_DATA(IP12_16_14, SSI_SDATA3),
-       PINMUX_IPSR_MODSEL_DATA(IP12_16_14, STP_ISCLK_0, SEL_SSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_16_14, SCIFB0_TXD, SEL_SCIFB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_16_14, MSIOF1_SS1, SEL_SOF1_0),
+       PINMUX_IPSR_MSEL(IP12_16_14, STP_ISCLK_0, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP12_16_14, SCIFB0_TXD, SEL_SCIFB_0),
+       PINMUX_IPSR_MSEL(IP12_16_14, MSIOF1_SS1, SEL_SOF1_0),
        PINMUX_IPSR_DATA(IP12_16_14, CAN_TXCLK),
        PINMUX_IPSR_DATA(IP12_19_17, SSI_SCK4),
-       PINMUX_IPSR_MODSEL_DATA(IP12_19_17, STP_ISD_0, SEL_SSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_19_17, SCIFB0_CTS_N, SEL_SCIFB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_19_17, MSIOF1_SS2, SEL_SOF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_19_17, SSI_SCK5_C, SEL_SSI5_2),
+       PINMUX_IPSR_MSEL(IP12_19_17, STP_ISD_0, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP12_19_17, SCIFB0_CTS_N, SEL_SCIFB_0),
+       PINMUX_IPSR_MSEL(IP12_19_17, MSIOF1_SS2, SEL_SOF1_0),
+       PINMUX_IPSR_MSEL(IP12_19_17, SSI_SCK5_C, SEL_SSI5_2),
        PINMUX_IPSR_DATA(IP12_19_17, CAN_DEBUGOUT0),
        PINMUX_IPSR_DATA(IP12_22_20, SSI_WS4),
-       PINMUX_IPSR_MODSEL_DATA(IP12_22_20, STP_ISEN_0, SEL_SSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_22_20, SCIFB0_RTS_N, SEL_SCIFB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_22_20, MSIOF1_TXD, SEL_SOF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_22_20, SSI_WS5_C, SEL_SSI5_2),
+       PINMUX_IPSR_MSEL(IP12_22_20, STP_ISEN_0, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP12_22_20, SCIFB0_RTS_N, SEL_SCIFB_0),
+       PINMUX_IPSR_MSEL(IP12_22_20, MSIOF1_TXD, SEL_SOF1_0),
+       PINMUX_IPSR_MSEL(IP12_22_20, SSI_WS5_C, SEL_SSI5_2),
        PINMUX_IPSR_DATA(IP12_22_20, CAN_DEBUGOUT1),
        PINMUX_IPSR_DATA(IP12_24_23, SSI_SDATA4),
-       PINMUX_IPSR_MODSEL_DATA(IP12_24_23, STP_ISSYNC_0, SEL_SSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_24_23, MSIOF1_RXD, SEL_SOF1_0),
+       PINMUX_IPSR_MSEL(IP12_24_23, STP_ISSYNC_0, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP12_24_23, MSIOF1_RXD, SEL_SOF1_0),
        PINMUX_IPSR_DATA(IP12_24_23, CAN_DEBUGOUT2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_27_25, SSI_SCK5, SEL_SSI5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_27_25, SCIFB1_SCK, SEL_SCIFB1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_27_25, IERX_B, SEL_IEB_1),
+       PINMUX_IPSR_MSEL(IP12_27_25, SSI_SCK5, SEL_SSI5_0),
+       PINMUX_IPSR_MSEL(IP12_27_25, SCIFB1_SCK, SEL_SCIFB1_0),
+       PINMUX_IPSR_MSEL(IP12_27_25, IERX_B, SEL_IEB_1),
        PINMUX_IPSR_DATA(IP12_27_25, DU2_EXHSYNC_DU2_HSYNC),
        PINMUX_IPSR_DATA(IP12_27_25, QSTH_QHS),
        PINMUX_IPSR_DATA(IP12_27_25, CAN_DEBUGOUT3),
-       PINMUX_IPSR_MODSEL_DATA(IP12_30_28, SSI_WS5, SEL_SSI5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_30_28, SCIFB1_RXD, SEL_SCIFB1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_30_28, IECLK_B, SEL_IEB_1),
+       PINMUX_IPSR_MSEL(IP12_30_28, SSI_WS5, SEL_SSI5_0),
+       PINMUX_IPSR_MSEL(IP12_30_28, SCIFB1_RXD, SEL_SCIFB1_0),
+       PINMUX_IPSR_MSEL(IP12_30_28, IECLK_B, SEL_IEB_1),
        PINMUX_IPSR_DATA(IP12_30_28, DU2_EXVSYNC_DU2_VSYNC),
        PINMUX_IPSR_DATA(IP12_30_28, QSTB_QHE),
        PINMUX_IPSR_DATA(IP12_30_28, CAN_DEBUGOUT4),
 
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, SSI_SDATA5, SEL_SSI5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, SCIFB1_TXD, SEL_SCIFB1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, IETX_B, SEL_IEB_1),
+       PINMUX_IPSR_MSEL(IP13_2_0, SSI_SDATA5, SEL_SSI5_0),
+       PINMUX_IPSR_MSEL(IP13_2_0, SCIFB1_TXD, SEL_SCIFB1_0),
+       PINMUX_IPSR_MSEL(IP13_2_0, IETX_B, SEL_IEB_1),
        PINMUX_IPSR_DATA(IP13_2_0, DU2_DR2),
        PINMUX_IPSR_DATA(IP13_2_0, LCDOUT2),
        PINMUX_IPSR_DATA(IP13_2_0, CAN_DEBUGOUT5),
-       PINMUX_IPSR_MODSEL_DATA(IP13_6_3, SSI_SCK6, SEL_SSI6_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_6_3, SCIFB1_CTS_N, SEL_SCIFB1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_6_3, BPFCLK_D, SEL_FM_3),
+       PINMUX_IPSR_MSEL(IP13_6_3, SSI_SCK6, SEL_SSI6_0),
+       PINMUX_IPSR_MSEL(IP13_6_3, SCIFB1_CTS_N, SEL_SCIFB1_0),
+       PINMUX_IPSR_MSEL(IP13_6_3, BPFCLK_D, SEL_FM_3),
        PINMUX_IPSR_DATA(IP13_6_3, DU2_DR3),
        PINMUX_IPSR_DATA(IP13_6_3, LCDOUT3),
        PINMUX_IPSR_DATA(IP13_6_3, CAN_DEBUGOUT6),
-       PINMUX_IPSR_MODSEL_DATA(IP13_6_3, BPFCLK_F, SEL_FM_5),
-       PINMUX_IPSR_MODSEL_DATA(IP13_9_7, SSI_WS6, SEL_SSI6_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_9_7, SCIFB1_RTS_N, SEL_SCIFB1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_9_7, CAN0_TX_D, SEL_CAN0_3),
+       PINMUX_IPSR_MSEL(IP13_6_3, BPFCLK_F, SEL_FM_5),
+       PINMUX_IPSR_MSEL(IP13_9_7, SSI_WS6, SEL_SSI6_0),
+       PINMUX_IPSR_MSEL(IP13_9_7, SCIFB1_RTS_N, SEL_SCIFB1_0),
+       PINMUX_IPSR_MSEL(IP13_9_7, CAN0_TX_D, SEL_CAN0_3),
        PINMUX_IPSR_DATA(IP13_9_7, DU2_DR4),
        PINMUX_IPSR_DATA(IP13_9_7, LCDOUT4),
        PINMUX_IPSR_DATA(IP13_9_7, CAN_DEBUGOUT7),
-       PINMUX_IPSR_MODSEL_DATA(IP13_12_10, SSI_SDATA6, SEL_SSI6_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_12_10, FMIN_D, SEL_FM_3),
+       PINMUX_IPSR_MSEL(IP13_12_10, SSI_SDATA6, SEL_SSI6_0),
+       PINMUX_IPSR_MSEL(IP13_12_10, FMIN_D, SEL_FM_3),
        PINMUX_IPSR_DATA(IP13_12_10, DU2_DR5),
        PINMUX_IPSR_DATA(IP13_12_10, LCDOUT5),
        PINMUX_IPSR_DATA(IP13_12_10, CAN_DEBUGOUT8),
-       PINMUX_IPSR_MODSEL_DATA(IP13_15_13, SSI_SCK78, SEL_SSI7_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_15_13, STP_IVCXO27_1, SEL_SSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_15_13, SCK1, SEL_SCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_15_13, SCIFA1_SCK, SEL_SCIFA1_0),
+       PINMUX_IPSR_MSEL(IP13_15_13, SSI_SCK78, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP13_15_13, STP_IVCXO27_1, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP13_15_13, SCK1, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP13_15_13, SCIFA1_SCK, SEL_SCIFA1_0),
        PINMUX_IPSR_DATA(IP13_15_13, DU2_DR6),
        PINMUX_IPSR_DATA(IP13_15_13, LCDOUT6),
        PINMUX_IPSR_DATA(IP13_15_13, CAN_DEBUGOUT9),
-       PINMUX_IPSR_MODSEL_DATA(IP13_18_16, SSI_WS78, SEL_SSI7_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_18_16, STP_ISCLK_1, SEL_SSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_18_16, SCIFB2_SCK, SEL_SCIFB2_0),
+       PINMUX_IPSR_MSEL(IP13_18_16, SSI_WS78, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP13_18_16, STP_ISCLK_1, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP13_18_16, SCIFB2_SCK, SEL_SCIFB2_0),
        PINMUX_IPSR_DATA(IP13_18_16, SCIFA2_CTS_N),
        PINMUX_IPSR_DATA(IP13_18_16, DU2_DR7),
        PINMUX_IPSR_DATA(IP13_18_16, LCDOUT7),
        PINMUX_IPSR_DATA(IP13_18_16, CAN_DEBUGOUT10),
-       PINMUX_IPSR_MODSEL_DATA(IP13_22_19, SSI_SDATA7, SEL_SSI7_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_22_19, STP_ISD_1, SEL_SSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_22_19, SCIFB2_RXD, SEL_SCIFB2_0),
+       PINMUX_IPSR_MSEL(IP13_22_19, SSI_SDATA7, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP13_22_19, STP_ISD_1, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP13_22_19, SCIFB2_RXD, SEL_SCIFB2_0),
        PINMUX_IPSR_DATA(IP13_22_19, SCIFA2_RTS_N),
        PINMUX_IPSR_DATA(IP13_22_19, TCLK2),
        PINMUX_IPSR_DATA(IP13_22_19, QSTVA_QVS),
        PINMUX_IPSR_DATA(IP13_22_19, CAN_DEBUGOUT11),
-       PINMUX_IPSR_MODSEL_DATA(IP13_22_19, BPFCLK_E, SEL_FM_4),
-       PINMUX_IPSR_MODSEL_DATA(IP13_22_19, SSI_SDATA7_B, SEL_SSI7_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_22_19, FMIN_G, SEL_FM_6),
-       PINMUX_IPSR_MODSEL_DATA(IP13_25_23, SSI_SDATA8, SEL_SSI8_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_25_23, STP_ISEN_1, SEL_SSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_25_23, SCIFB2_TXD, SEL_SCIFB2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_25_23, CAN0_TX_C, SEL_CAN0_2),
+       PINMUX_IPSR_MSEL(IP13_22_19, BPFCLK_E, SEL_FM_4),
+       PINMUX_IPSR_MSEL(IP13_22_19, SSI_SDATA7_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP13_22_19, FMIN_G, SEL_FM_6),
+       PINMUX_IPSR_MSEL(IP13_25_23, SSI_SDATA8, SEL_SSI8_0),
+       PINMUX_IPSR_MSEL(IP13_25_23, STP_ISEN_1, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP13_25_23, SCIFB2_TXD, SEL_SCIFB2_0),
+       PINMUX_IPSR_MSEL(IP13_25_23, CAN0_TX_C, SEL_CAN0_2),
        PINMUX_IPSR_DATA(IP13_25_23, CAN_DEBUGOUT12),
-       PINMUX_IPSR_MODSEL_DATA(IP13_25_23, SSI_SDATA8_B, SEL_SSI8_1),
+       PINMUX_IPSR_MSEL(IP13_25_23, SSI_SDATA8_B, SEL_SSI8_1),
        PINMUX_IPSR_DATA(IP13_28_26, SSI_SDATA9),
-       PINMUX_IPSR_MODSEL_DATA(IP13_28_26, STP_ISSYNC_1, SEL_SSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_28_26, SCIFB2_CTS_N, SEL_SCIFB2_0),
+       PINMUX_IPSR_MSEL(IP13_28_26, STP_ISSYNC_1, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP13_28_26, SCIFB2_CTS_N, SEL_SCIFB2_0),
        PINMUX_IPSR_DATA(IP13_28_26, SSI_WS1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_28_26, SSI_SDATA5_C, SEL_SSI5_2),
+       PINMUX_IPSR_MSEL(IP13_28_26, SSI_SDATA5_C, SEL_SSI5_2),
        PINMUX_IPSR_DATA(IP13_28_26, CAN_DEBUGOUT13),
        PINMUX_IPSR_DATA(IP13_30_29, AUDIO_CLKA),
-       PINMUX_IPSR_MODSEL_DATA(IP13_30_29, SCIFB2_RTS_N, SEL_SCIFB2_0),
+       PINMUX_IPSR_MSEL(IP13_30_29, SCIFB2_RTS_N, SEL_SCIFB2_0),
        PINMUX_IPSR_DATA(IP13_30_29, CAN_DEBUGOUT14),
 
        PINMUX_IPSR_DATA(IP14_2_0, AUDIO_CLKB),
-       PINMUX_IPSR_MODSEL_DATA(IP14_2_0, SCIF_CLK, SEL_SCIFCLK_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_2_0, CAN0_RX_D, SEL_CAN0_3),
+       PINMUX_IPSR_MSEL(IP14_2_0, SCIF_CLK, SEL_SCIFCLK_0),
+       PINMUX_IPSR_MSEL(IP14_2_0, CAN0_RX_D, SEL_CAN0_3),
        PINMUX_IPSR_DATA(IP14_2_0, DVC_MUTE),
-       PINMUX_IPSR_MODSEL_DATA(IP14_2_0, CAN0_RX_C, SEL_CAN0_2),
+       PINMUX_IPSR_MSEL(IP14_2_0, CAN0_RX_C, SEL_CAN0_2),
        PINMUX_IPSR_DATA(IP14_2_0, CAN_DEBUGOUT15),
        PINMUX_IPSR_DATA(IP14_2_0, REMOCON),
-       PINMUX_IPSR_MODSEL_DATA(IP14_5_3, SCIFA0_SCK, SEL_SCFA_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_5_3, HSCK1, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP14_5_3, SCIFA0_SCK, SEL_SCFA_0),
+       PINMUX_IPSR_MSEL(IP14_5_3, HSCK1, SEL_HSCIF1_0),
        PINMUX_IPSR_DATA(IP14_5_3, SCK0),
        PINMUX_IPSR_DATA(IP14_5_3, MSIOF3_SS2),
        PINMUX_IPSR_DATA(IP14_5_3, DU2_DG2),
        PINMUX_IPSR_DATA(IP14_5_3, LCDOUT10),
-       PINMUX_IPSR_MODSEL_DATA(IP14_5_3, IIC1_SDA_C, SEL_IIC1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP14_5_3, I2C1_SDA_C, SEL_I2C1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP14_8_6, SCIFA0_RXD, SEL_SCFA_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_8_6, HRX1, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_8_6, RX0, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP14_5_3, IIC1_SDA_C, SEL_IIC1_2),
+       PINMUX_IPSR_MSEL(IP14_5_3, I2C1_SDA_C, SEL_I2C1_2),
+       PINMUX_IPSR_MSEL(IP14_8_6, SCIFA0_RXD, SEL_SCFA_0),
+       PINMUX_IPSR_MSEL(IP14_8_6, HRX1, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP14_8_6, RX0, SEL_SCIF0_0),
        PINMUX_IPSR_DATA(IP14_8_6, DU2_DR0),
        PINMUX_IPSR_DATA(IP14_8_6, LCDOUT0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_11_9, SCIFA0_TXD, SEL_SCFA_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_11_9, HTX1, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_11_9, TX0, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP14_11_9, SCIFA0_TXD, SEL_SCFA_0),
+       PINMUX_IPSR_MSEL(IP14_11_9, HTX1, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP14_11_9, TX0, SEL_SCIF0_0),
        PINMUX_IPSR_DATA(IP14_11_9, DU2_DR1),
        PINMUX_IPSR_DATA(IP14_11_9, LCDOUT1),
-       PINMUX_IPSR_MODSEL_DATA(IP14_15_12, SCIFA0_CTS_N, SEL_SCFA_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_15_12, HCTS1_N, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP14_15_12, SCIFA0_CTS_N, SEL_SCFA_0),
+       PINMUX_IPSR_MSEL(IP14_15_12, HCTS1_N, SEL_HSCIF1_0),
        PINMUX_IPSR_DATA(IP14_15_12, CTS0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP14_15_12, MSIOF3_SYNC, SEL_SOF3_0),
+       PINMUX_IPSR_MSEL(IP14_15_12, MSIOF3_SYNC, SEL_SOF3_0),
        PINMUX_IPSR_DATA(IP14_15_12, DU2_DG3),
        PINMUX_IPSR_DATA(IP14_15_12, LCDOUT11),
        PINMUX_IPSR_DATA(IP14_15_12, PWM0_B),
-       PINMUX_IPSR_MODSEL_DATA(IP14_15_12, IIC1_SCL_C, SEL_IIC1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP14_15_12, I2C1_SCL_C, SEL_I2C1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP14_18_16, SCIFA0_RTS_N, SEL_SCFA_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_18_16, HRTS1_N, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP14_15_12, IIC1_SCL_C, SEL_IIC1_2),
+       PINMUX_IPSR_MSEL(IP14_15_12, I2C1_SCL_C, SEL_I2C1_2),
+       PINMUX_IPSR_MSEL(IP14_18_16, SCIFA0_RTS_N, SEL_SCFA_0),
+       PINMUX_IPSR_MSEL(IP14_18_16, HRTS1_N, SEL_HSCIF1_0),
        PINMUX_IPSR_DATA(IP14_18_16, RTS0_N),
        PINMUX_IPSR_DATA(IP14_18_16, MSIOF3_SS1),
        PINMUX_IPSR_DATA(IP14_18_16, DU2_DG0),
        PINMUX_IPSR_DATA(IP14_18_16, LCDOUT8),
        PINMUX_IPSR_DATA(IP14_18_16, PWM1_B),
-       PINMUX_IPSR_MODSEL_DATA(IP14_21_19, SCIFA1_RXD, SEL_SCIFA1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_21_19, AD_DI, SEL_ADI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_21_19, RX1, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP14_21_19, SCIFA1_RXD, SEL_SCIFA1_0),
+       PINMUX_IPSR_MSEL(IP14_21_19, AD_DI, SEL_ADI_0),
+       PINMUX_IPSR_MSEL(IP14_21_19, RX1, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP14_21_19, DU2_EXODDF_DU2_ODDF_DISP_CDE),
        PINMUX_IPSR_DATA(IP14_21_19, QCPV_QDE),
-       PINMUX_IPSR_MODSEL_DATA(IP14_24_22, SCIFA1_TXD, SEL_SCIFA1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_24_22, AD_DO, SEL_ADI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_24_22, TX1, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP14_24_22, SCIFA1_TXD, SEL_SCIFA1_0),
+       PINMUX_IPSR_MSEL(IP14_24_22, AD_DO, SEL_ADI_0),
+       PINMUX_IPSR_MSEL(IP14_24_22, TX1, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP14_24_22, DU2_DG1),
        PINMUX_IPSR_DATA(IP14_24_22, LCDOUT9),
-       PINMUX_IPSR_MODSEL_DATA(IP14_27_25, SCIFA1_CTS_N, SEL_SCIFA1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_27_25, AD_CLK, SEL_ADI_0),
+       PINMUX_IPSR_MSEL(IP14_27_25, SCIFA1_CTS_N, SEL_SCIFA1_0),
+       PINMUX_IPSR_MSEL(IP14_27_25, AD_CLK, SEL_ADI_0),
        PINMUX_IPSR_DATA(IP14_27_25, CTS1_N),
-       PINMUX_IPSR_MODSEL_DATA(IP14_27_25, MSIOF3_RXD, SEL_SOF3_0),
+       PINMUX_IPSR_MSEL(IP14_27_25, MSIOF3_RXD, SEL_SOF3_0),
        PINMUX_IPSR_DATA(IP14_27_25, DU0_DOTCLKOUT),
        PINMUX_IPSR_DATA(IP14_27_25, QCLK),
-       PINMUX_IPSR_MODSEL_DATA(IP14_30_28, SCIFA1_RTS_N, SEL_SCIFA1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_30_28, AD_NCS_N, SEL_ADI_0),
+       PINMUX_IPSR_MSEL(IP14_30_28, SCIFA1_RTS_N, SEL_SCIFA1_0),
+       PINMUX_IPSR_MSEL(IP14_30_28, AD_NCS_N, SEL_ADI_0),
        PINMUX_IPSR_DATA(IP14_30_28, RTS1_N),
-       PINMUX_IPSR_MODSEL_DATA(IP14_30_28, MSIOF3_TXD, SEL_SOF3_0),
+       PINMUX_IPSR_MSEL(IP14_30_28, MSIOF3_TXD, SEL_SOF3_0),
        PINMUX_IPSR_DATA(IP14_30_28, DU1_DOTCLKOUT),
        PINMUX_IPSR_DATA(IP14_30_28, QSTVB_QVE),
-       PINMUX_IPSR_MODSEL_DATA(IP14_30_28, HRTS0_N_C, SEL_HSCIF0_2),
+       PINMUX_IPSR_MSEL(IP14_30_28, HRTS0_N_C, SEL_HSCIF0_2),
 
-       PINMUX_IPSR_MODSEL_DATA(IP15_2_0, SCIFA2_SCK, SEL_SCIFA2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_2_0, FMCLK, SEL_FM_0),
+       PINMUX_IPSR_MSEL(IP15_2_0, SCIFA2_SCK, SEL_SCIFA2_0),
+       PINMUX_IPSR_MSEL(IP15_2_0, FMCLK, SEL_FM_0),
        PINMUX_IPSR_DATA(IP15_2_0, SCK2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_2_0, MSIOF3_SCK, SEL_SOF3_0),
+       PINMUX_IPSR_MSEL(IP15_2_0, MSIOF3_SCK, SEL_SOF3_0),
        PINMUX_IPSR_DATA(IP15_2_0, DU2_DG7),
        PINMUX_IPSR_DATA(IP15_2_0, LCDOUT15),
-       PINMUX_IPSR_MODSEL_DATA(IP15_2_0, SCIF_CLK_B, SEL_SCIFCLK_1),
-       PINMUX_IPSR_MODSEL_DATA(IP15_5_3, SCIFA2_RXD, SEL_SCIFA2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_5_3, FMIN, SEL_FM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_5_3, TX2, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP15_2_0, SCIF_CLK_B, SEL_SCIFCLK_1),
+       PINMUX_IPSR_MSEL(IP15_5_3, SCIFA2_RXD, SEL_SCIFA2_0),
+       PINMUX_IPSR_MSEL(IP15_5_3, FMIN, SEL_FM_0),
+       PINMUX_IPSR_MSEL(IP15_5_3, TX2, SEL_SCIF2_0),
        PINMUX_IPSR_DATA(IP15_5_3, DU2_DB0),
        PINMUX_IPSR_DATA(IP15_5_3, LCDOUT16),
-       PINMUX_IPSR_MODSEL_DATA(IP15_5_3, IIC2_SCL, SEL_IIC2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_5_3, I2C2_SCL, SEL_I2C2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_8_6, SCIFA2_TXD, SEL_SCIFA2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_8_6, BPFCLK, SEL_FM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_8_6, RX2, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP15_5_3, IIC2_SCL, SEL_IIC2_0),
+       PINMUX_IPSR_MSEL(IP15_5_3, I2C2_SCL, SEL_I2C2_0),
+       PINMUX_IPSR_MSEL(IP15_8_6, SCIFA2_TXD, SEL_SCIFA2_0),
+       PINMUX_IPSR_MSEL(IP15_8_6, BPFCLK, SEL_FM_0),
+       PINMUX_IPSR_MSEL(IP15_8_6, RX2, SEL_SCIF2_0),
        PINMUX_IPSR_DATA(IP15_8_6, DU2_DB1),
        PINMUX_IPSR_DATA(IP15_8_6, LCDOUT17),
-       PINMUX_IPSR_MODSEL_DATA(IP15_8_6, IIC2_SDA, SEL_IIC2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_8_6, I2C2_SDA, SEL_I2C2_0),
+       PINMUX_IPSR_MSEL(IP15_8_6, IIC2_SDA, SEL_IIC2_0),
+       PINMUX_IPSR_MSEL(IP15_8_6, I2C2_SDA, SEL_I2C2_0),
        PINMUX_IPSR_DATA(IP15_11_9, HSCK0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_11_9, TS_SDEN0, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP15_11_9, TS_SDEN0, SEL_TSIF0_0),
        PINMUX_IPSR_DATA(IP15_11_9, DU2_DG4),
        PINMUX_IPSR_DATA(IP15_11_9, LCDOUT12),
-       PINMUX_IPSR_MODSEL_DATA(IP15_11_9, HCTS0_N_C, SEL_HSCIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_13_12, HRX0, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP15_11_9, HCTS0_N_C, SEL_HSCIF0_2),
+       PINMUX_IPSR_MSEL(IP15_13_12, HRX0, SEL_HSCIF0_0),
        PINMUX_IPSR_DATA(IP15_13_12, DU2_DB2),
        PINMUX_IPSR_DATA(IP15_13_12, LCDOUT18),
-       PINMUX_IPSR_MODSEL_DATA(IP15_15_14, HTX0, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP15_15_14, HTX0, SEL_HSCIF0_0),
        PINMUX_IPSR_DATA(IP15_15_14, DU2_DB3),
        PINMUX_IPSR_DATA(IP15_15_14, LCDOUT19),
-       PINMUX_IPSR_MODSEL_DATA(IP15_17_16, HCTS0_N, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP15_17_16, HCTS0_N, SEL_HSCIF0_0),
        PINMUX_IPSR_DATA(IP15_17_16, SSI_SCK9),
        PINMUX_IPSR_DATA(IP15_17_16, DU2_DB4),
        PINMUX_IPSR_DATA(IP15_17_16, LCDOUT20),
-       PINMUX_IPSR_MODSEL_DATA(IP15_19_18, HRTS0_N, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP15_19_18, HRTS0_N, SEL_HSCIF0_0),
        PINMUX_IPSR_DATA(IP15_19_18, SSI_WS9),
        PINMUX_IPSR_DATA(IP15_19_18, DU2_DB5),
        PINMUX_IPSR_DATA(IP15_19_18, LCDOUT21),
-       PINMUX_IPSR_MODSEL_DATA(IP15_22_20, MSIOF0_SCK, SEL_SOF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_22_20, TS_SDAT0, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP15_22_20, MSIOF0_SCK, SEL_SOF0_0),
+       PINMUX_IPSR_MSEL(IP15_22_20, TS_SDAT0, SEL_TSIF0_0),
        PINMUX_IPSR_DATA(IP15_22_20, ADICLK),
        PINMUX_IPSR_DATA(IP15_22_20, DU2_DB6),
        PINMUX_IPSR_DATA(IP15_22_20, LCDOUT22),
        PINMUX_IPSR_DATA(IP15_25_23, MSIOF0_SYNC),
-       PINMUX_IPSR_MODSEL_DATA(IP15_25_23, TS_SCK0, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP15_25_23, TS_SCK0, SEL_TSIF0_0),
        PINMUX_IPSR_DATA(IP15_25_23, SSI_SCK2),
        PINMUX_IPSR_DATA(IP15_25_23, ADIDATA),
        PINMUX_IPSR_DATA(IP15_25_23, DU2_DB7),
        PINMUX_IPSR_DATA(IP15_25_23, LCDOUT23),
-       PINMUX_IPSR_MODSEL_DATA(IP15_25_23, HRX0_C, SEL_SCIFA2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP15_27_26, MSIOF0_SS1, SEL_SOF0_0),
+       PINMUX_IPSR_MSEL(IP15_25_23, HRX0_C, SEL_SCIFA2_1),
+       PINMUX_IPSR_MSEL(IP15_27_26, MSIOF0_SS1, SEL_SOF0_0),
        PINMUX_IPSR_DATA(IP15_27_26, ADICHS0),
        PINMUX_IPSR_DATA(IP15_27_26, DU2_DG5),
        PINMUX_IPSR_DATA(IP15_27_26, LCDOUT13),
-       PINMUX_IPSR_MODSEL_DATA(IP15_29_28, MSIOF0_TXD, SEL_SOF0_0),
+       PINMUX_IPSR_MSEL(IP15_29_28, MSIOF0_TXD, SEL_SOF0_0),
        PINMUX_IPSR_DATA(IP15_29_28, ADICHS1),
        PINMUX_IPSR_DATA(IP15_29_28, DU2_DG6),
        PINMUX_IPSR_DATA(IP15_29_28, LCDOUT14),
 
-       PINMUX_IPSR_MODSEL_DATA(IP16_2_0, MSIOF0_SS2, SEL_SOF0_0),
+       PINMUX_IPSR_MSEL(IP16_2_0, MSIOF0_SS2, SEL_SOF0_0),
        PINMUX_IPSR_DATA(IP16_2_0, AUDIO_CLKOUT),
        PINMUX_IPSR_DATA(IP16_2_0, ADICHS2),
        PINMUX_IPSR_DATA(IP16_2_0, DU2_DISP),
        PINMUX_IPSR_DATA(IP16_2_0, QPOLA),
-       PINMUX_IPSR_MODSEL_DATA(IP16_2_0, HTX0_C, SEL_HSCIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP16_2_0, SCIFA2_TXD_B, SEL_SCIFA2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP16_5_3, MSIOF0_RXD, SEL_SOF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP16_5_3, TS_SPSYNC0, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP16_2_0, HTX0_C, SEL_HSCIF0_2),
+       PINMUX_IPSR_MSEL(IP16_2_0, SCIFA2_TXD_B, SEL_SCIFA2_1),
+       PINMUX_IPSR_MSEL(IP16_5_3, MSIOF0_RXD, SEL_SOF0_0),
+       PINMUX_IPSR_MSEL(IP16_5_3, TS_SPSYNC0, SEL_TSIF0_0),
        PINMUX_IPSR_DATA(IP16_5_3, SSI_WS2),
        PINMUX_IPSR_DATA(IP16_5_3, ADICS_SAMP),
        PINMUX_IPSR_DATA(IP16_5_3, DU2_CDE),
        PINMUX_IPSR_DATA(IP16_5_3, QPOLB),
-       PINMUX_IPSR_MODSEL_DATA(IP16_5_3, SCIFA2_RXD_B, SEL_HSCIF0_2),
+       PINMUX_IPSR_MSEL(IP16_5_3, SCIFA2_RXD_B, SEL_HSCIF0_2),
        PINMUX_IPSR_DATA(IP16_6, USB1_PWEN),
        PINMUX_IPSR_DATA(IP16_6, AUDIO_CLKOUT_D),
        PINMUX_IPSR_DATA(IP16_7, USB1_OVC),
-       PINMUX_IPSR_MODSEL_DATA(IP16_7, TCLK1_B, SEL_TMU1_1),
+       PINMUX_IPSR_MSEL(IP16_7, TCLK1_B, SEL_TMU1_1),
 
        PINMUX_DATA(IIC0_SCL_MARK, FN_SEL_IIC0_0),
        PINMUX_DATA(IIC0_SDA_MARK, FN_SEL_IIC0_0),
@@ -3624,25 +3623,6 @@ static const unsigned int usb2_pins[] = {
 static const unsigned int usb2_mux[] = {
        USB2_PWEN_MARK, USB2_OVC_MARK,
 };
-
-union vin_data {
-       unsigned int data24[24];
-       unsigned int data20[20];
-       unsigned int data16[16];
-       unsigned int data12[12];
-       unsigned int data10[10];
-       unsigned int data8[8];
-       unsigned int data4[4];
-};
-
-#define VIN_DATA_PIN_GROUP(n, s)                               \
-       {                                                       \
-               .name = #n#s,                                   \
-               .pins = n##_pins.data##s,                       \
-               .mux = n##_mux.data##s,                         \
-               .nr_pins = ARRAY_SIZE(n##_pins.data##s),        \
-       }
-
 /* - VIN0 ------------------------------------------------------------------- */
 static const union vin_data vin0_data_pins = {
        .data24 = {
@@ -5719,6 +5699,6 @@ const struct sh_pfc_soc_info r8a7790_pinmux_info = {
 
        .cfg_regs = pinmux_config_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index 25e8117f5a1ac04ad738cf3d26e0038f2759e56a..87a4f44147c1d5bcd9c286e86475b0e8370f9a86 100644 (file)
@@ -9,7 +9,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/platform_data/gpio-rcar.h>
 
 #include "core.h"
 #include "sh_pfc.h"
@@ -824,459 +823,459 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP0_14, D14),
        PINMUX_IPSR_DATA(IP0_15, D15),
        PINMUX_IPSR_DATA(IP0_18_16, A0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_18_16, ATAWR0_N_C, SEL_LBS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_18_16, MSIOF0_SCK_B, SEL_SOF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_18_16, SCL0_C, SEL_IIC0_2),
+       PINMUX_IPSR_MSEL(IP0_18_16, ATAWR0_N_C, SEL_LBS_2),
+       PINMUX_IPSR_MSEL(IP0_18_16, MSIOF0_SCK_B, SEL_SOF0_1),
+       PINMUX_IPSR_MSEL(IP0_18_16, SCL0_C, SEL_IIC0_2),
        PINMUX_IPSR_DATA(IP0_18_16, PWM2_B),
        PINMUX_IPSR_DATA(IP0_20_19, A1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_20_19, MSIOF0_SYNC_B, SEL_SOF0_1),
+       PINMUX_IPSR_MSEL(IP0_20_19, MSIOF0_SYNC_B, SEL_SOF0_1),
        PINMUX_IPSR_DATA(IP0_22_21, A2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_22_21, MSIOF0_SS1_B, SEL_SOF0_1),
+       PINMUX_IPSR_MSEL(IP0_22_21, MSIOF0_SS1_B, SEL_SOF0_1),
        PINMUX_IPSR_DATA(IP0_24_23, A3),
-       PINMUX_IPSR_MODSEL_DATA(IP0_24_23, MSIOF0_SS2_B, SEL_SOF0_1),
+       PINMUX_IPSR_MSEL(IP0_24_23, MSIOF0_SS2_B, SEL_SOF0_1),
        PINMUX_IPSR_DATA(IP0_26_25, A4),
-       PINMUX_IPSR_MODSEL_DATA(IP0_26_25, MSIOF0_TXD_B, SEL_SOF0_1),
+       PINMUX_IPSR_MSEL(IP0_26_25, MSIOF0_TXD_B, SEL_SOF0_1),
        PINMUX_IPSR_DATA(IP0_28_27, A5),
-       PINMUX_IPSR_MODSEL_DATA(IP0_28_27, MSIOF0_RXD_B, SEL_SOF0_1),
+       PINMUX_IPSR_MSEL(IP0_28_27, MSIOF0_RXD_B, SEL_SOF0_1),
        PINMUX_IPSR_DATA(IP0_30_29, A6),
-       PINMUX_IPSR_MODSEL_DATA(IP0_30_29, MSIOF1_SCK, SEL_SOF1_0),
+       PINMUX_IPSR_MSEL(IP0_30_29, MSIOF1_SCK, SEL_SOF1_0),
 
        /* IPSR1 */
        PINMUX_IPSR_DATA(IP1_1_0, A7),
-       PINMUX_IPSR_MODSEL_DATA(IP1_1_0, MSIOF1_SYNC, SEL_SOF1_0),
+       PINMUX_IPSR_MSEL(IP1_1_0, MSIOF1_SYNC, SEL_SOF1_0),
        PINMUX_IPSR_DATA(IP1_3_2, A8),
-       PINMUX_IPSR_MODSEL_DATA(IP1_3_2, MSIOF1_SS1, SEL_SOF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_3_2, SCL0, SEL_IIC0_0),
+       PINMUX_IPSR_MSEL(IP1_3_2, MSIOF1_SS1, SEL_SOF1_0),
+       PINMUX_IPSR_MSEL(IP1_3_2, SCL0, SEL_IIC0_0),
        PINMUX_IPSR_DATA(IP1_5_4, A9),
-       PINMUX_IPSR_MODSEL_DATA(IP1_5_4, MSIOF1_SS2, SEL_SOF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_5_4, SDA0, SEL_IIC0_0),
+       PINMUX_IPSR_MSEL(IP1_5_4, MSIOF1_SS2, SEL_SOF1_0),
+       PINMUX_IPSR_MSEL(IP1_5_4, SDA0, SEL_IIC0_0),
        PINMUX_IPSR_DATA(IP1_7_6, A10),
-       PINMUX_IPSR_MODSEL_DATA(IP1_7_6, MSIOF1_TXD, SEL_SOF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_7_6, MSIOF1_TXD_D, SEL_SOF1_3),
+       PINMUX_IPSR_MSEL(IP1_7_6, MSIOF1_TXD, SEL_SOF1_0),
+       PINMUX_IPSR_MSEL(IP1_7_6, MSIOF1_TXD_D, SEL_SOF1_3),
        PINMUX_IPSR_DATA(IP1_10_8, A11),
-       PINMUX_IPSR_MODSEL_DATA(IP1_10_8, MSIOF1_RXD, SEL_SOF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_10_8, SCL3_D, SEL_IIC3_3),
-       PINMUX_IPSR_MODSEL_DATA(IP1_10_8, MSIOF1_RXD_D, SEL_SOF1_3),
+       PINMUX_IPSR_MSEL(IP1_10_8, MSIOF1_RXD, SEL_SOF1_0),
+       PINMUX_IPSR_MSEL(IP1_10_8, SCL3_D, SEL_IIC3_3),
+       PINMUX_IPSR_MSEL(IP1_10_8, MSIOF1_RXD_D, SEL_SOF1_3),
        PINMUX_IPSR_DATA(IP1_13_11, A12),
-       PINMUX_IPSR_MODSEL_DATA(IP1_13_11, FMCLK, SEL_FM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_13_11, SDA3_D, SEL_IIC3_3),
-       PINMUX_IPSR_MODSEL_DATA(IP1_13_11, MSIOF1_SCK_D, SEL_SOF1_3),
+       PINMUX_IPSR_MSEL(IP1_13_11, FMCLK, SEL_FM_0),
+       PINMUX_IPSR_MSEL(IP1_13_11, SDA3_D, SEL_IIC3_3),
+       PINMUX_IPSR_MSEL(IP1_13_11, MSIOF1_SCK_D, SEL_SOF1_3),
        PINMUX_IPSR_DATA(IP1_16_14, A13),
-       PINMUX_IPSR_MODSEL_DATA(IP1_16_14, ATAG0_N_C, SEL_LBS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP1_16_14, BPFCLK, SEL_FM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_16_14, MSIOF1_SS1_D, SEL_SOF1_3),
+       PINMUX_IPSR_MSEL(IP1_16_14, ATAG0_N_C, SEL_LBS_2),
+       PINMUX_IPSR_MSEL(IP1_16_14, BPFCLK, SEL_FM_0),
+       PINMUX_IPSR_MSEL(IP1_16_14, MSIOF1_SS1_D, SEL_SOF1_3),
        PINMUX_IPSR_DATA(IP1_19_17, A14),
-       PINMUX_IPSR_MODSEL_DATA(IP1_19_17, ATADIR0_N_C, SEL_LBS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP1_19_17, FMIN, SEL_FM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_19_17, FMIN_C, SEL_FM_2),
-       PINMUX_IPSR_MODSEL_DATA(IP1_19_17, MSIOF1_SYNC_D, SEL_SOF1_3),
+       PINMUX_IPSR_MSEL(IP1_19_17, ATADIR0_N_C, SEL_LBS_2),
+       PINMUX_IPSR_MSEL(IP1_19_17, FMIN, SEL_FM_0),
+       PINMUX_IPSR_MSEL(IP1_19_17, FMIN_C, SEL_FM_2),
+       PINMUX_IPSR_MSEL(IP1_19_17, MSIOF1_SYNC_D, SEL_SOF1_3),
        PINMUX_IPSR_DATA(IP1_22_20, A15),
-       PINMUX_IPSR_MODSEL_DATA(IP1_22_20, BPFCLK_C, SEL_FM_2),
+       PINMUX_IPSR_MSEL(IP1_22_20, BPFCLK_C, SEL_FM_2),
        PINMUX_IPSR_DATA(IP1_25_23, A16),
-       PINMUX_IPSR_MODSEL_DATA(IP1_25_23, DREQ2_B, SEL_LBS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_25_23, FMCLK_C, SEL_FM_2),
-       PINMUX_IPSR_MODSEL_DATA(IP1_25_23, SCIFA1_SCK_B, SEL_SCIFA1_1),
+       PINMUX_IPSR_MSEL(IP1_25_23, DREQ2_B, SEL_LBS_1),
+       PINMUX_IPSR_MSEL(IP1_25_23, FMCLK_C, SEL_FM_2),
+       PINMUX_IPSR_MSEL(IP1_25_23, SCIFA1_SCK_B, SEL_SCIFA1_1),
        PINMUX_IPSR_DATA(IP1_28_26, A17),
-       PINMUX_IPSR_MODSEL_DATA(IP1_28_26, DACK2_B, SEL_LBS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_28_26, SDA0_C, SEL_IIC0_2),
+       PINMUX_IPSR_MSEL(IP1_28_26, DACK2_B, SEL_LBS_1),
+       PINMUX_IPSR_MSEL(IP1_28_26, SDA0_C, SEL_IIC0_2),
        PINMUX_IPSR_DATA(IP1_31_29, A18),
-       PINMUX_IPSR_MODSEL_DATA(IP1_31_29, DREQ1, SEL_LBS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_31_29, SCIFA1_RXD_C, SEL_SCIFA1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP1_31_29, SCIFB1_RXD_C, SEL_SCIFB1_2),
+       PINMUX_IPSR_MSEL(IP1_31_29, DREQ1, SEL_LBS_0),
+       PINMUX_IPSR_MSEL(IP1_31_29, SCIFA1_RXD_C, SEL_SCIFA1_2),
+       PINMUX_IPSR_MSEL(IP1_31_29, SCIFB1_RXD_C, SEL_SCIFB1_2),
 
        /* IPSR2 */
        PINMUX_IPSR_DATA(IP2_2_0, A19),
        PINMUX_IPSR_DATA(IP2_2_0, DACK1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_2_0, SCIFA1_TXD_C, SEL_SCIFA1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP2_2_0, SCIFB1_TXD_C, SEL_SCIFB1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP2_2_0, SCIFB1_SCK_B, SEL_SCIFB1_0),
+       PINMUX_IPSR_MSEL(IP2_2_0, SCIFA1_TXD_C, SEL_SCIFA1_2),
+       PINMUX_IPSR_MSEL(IP2_2_0, SCIFB1_TXD_C, SEL_SCIFB1_2),
+       PINMUX_IPSR_MSEL(IP2_2_0, SCIFB1_SCK_B, SEL_SCIFB1_1),
        PINMUX_IPSR_DATA(IP2_2_0, A20),
-       PINMUX_IPSR_MODSEL_DATA(IP2_4_3, SPCLK, SEL_QSP_0),
+       PINMUX_IPSR_MSEL(IP2_4_3, SPCLK, SEL_QSP_0),
        PINMUX_IPSR_DATA(IP2_6_5, A21),
-       PINMUX_IPSR_MODSEL_DATA(IP2_6_5, ATAWR0_N_B, SEL_LBS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_6_5, MOSI_IO0, SEL_QSP_0),
+       PINMUX_IPSR_MSEL(IP2_6_5, ATAWR0_N_B, SEL_LBS_1),
+       PINMUX_IPSR_MSEL(IP2_6_5, MOSI_IO0, SEL_QSP_0),
        PINMUX_IPSR_DATA(IP2_9_7, A22),
-       PINMUX_IPSR_MODSEL_DATA(IP2_9_7, MISO_IO1, SEL_QSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_9_7, FMCLK_B, SEL_FM_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_9_7, TX0, SEL_SCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_9_7, SCIFA0_TXD, SEL_SCFA_0),
+       PINMUX_IPSR_MSEL(IP2_9_7, MISO_IO1, SEL_QSP_0),
+       PINMUX_IPSR_MSEL(IP2_9_7, FMCLK_B, SEL_FM_1),
+       PINMUX_IPSR_MSEL(IP2_9_7, TX0, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP2_9_7, SCIFA0_TXD, SEL_SCFA_0),
        PINMUX_IPSR_DATA(IP2_12_10, A23),
-       PINMUX_IPSR_MODSEL_DATA(IP2_12_10, IO2, SEL_QSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_12_10, BPFCLK_B, SEL_FM_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_12_10, RX0, SEL_SCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_12_10, SCIFA0_RXD, SEL_SCFA_0),
+       PINMUX_IPSR_MSEL(IP2_12_10, IO2, SEL_QSP_0),
+       PINMUX_IPSR_MSEL(IP2_12_10, BPFCLK_B, SEL_FM_1),
+       PINMUX_IPSR_MSEL(IP2_12_10, RX0, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP2_12_10, SCIFA0_RXD, SEL_SCFA_0),
        PINMUX_IPSR_DATA(IP2_15_13, A24),
-       PINMUX_IPSR_MODSEL_DATA(IP2_15_13, DREQ2, SEL_LBS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_15_13, IO3, SEL_QSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_15_13, TX1, SEL_SCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_15_13, SCIFA1_TXD, SEL_SCIFA1_0),
+       PINMUX_IPSR_MSEL(IP2_15_13, DREQ2, SEL_LBS_0),
+       PINMUX_IPSR_MSEL(IP2_15_13, IO3, SEL_QSP_0),
+       PINMUX_IPSR_MSEL(IP2_15_13, TX1, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP2_15_13, SCIFA1_TXD, SEL_SCIFA1_0),
        PINMUX_IPSR_DATA(IP2_18_16, A25),
-       PINMUX_IPSR_MODSEL_DATA(IP2_18_16, DACK2, SEL_LBS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_18_16, SSL, SEL_QSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_18_16, DREQ1_C, SEL_LBS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP2_18_16, RX1, SEL_SCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_18_16, SCIFA1_RXD, SEL_SCIFA1_0),
+       PINMUX_IPSR_MSEL(IP2_18_16, DACK2, SEL_LBS_0),
+       PINMUX_IPSR_MSEL(IP2_18_16, SSL, SEL_QSP_0),
+       PINMUX_IPSR_MSEL(IP2_18_16, DREQ1_C, SEL_LBS_2),
+       PINMUX_IPSR_MSEL(IP2_18_16, RX1, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP2_18_16, SCIFA1_RXD, SEL_SCIFA1_0),
        PINMUX_IPSR_DATA(IP2_20_19, CS0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP2_20_19, ATAG0_N_B, SEL_LBS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_20_19, SCL1, SEL_IIC1_0),
+       PINMUX_IPSR_MSEL(IP2_20_19, ATAG0_N_B, SEL_LBS_1),
+       PINMUX_IPSR_MSEL(IP2_20_19, SCL1, SEL_IIC1_0),
        PINMUX_IPSR_DATA(IP2_22_21, CS1_N_A26),
-       PINMUX_IPSR_MODSEL_DATA(IP2_22_21, ATADIR0_N_B, SEL_LBS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_22_21, SDA1, SEL_IIC1_0),
+       PINMUX_IPSR_MSEL(IP2_22_21, ATADIR0_N_B, SEL_LBS_1),
+       PINMUX_IPSR_MSEL(IP2_22_21, SDA1, SEL_IIC1_0),
        PINMUX_IPSR_DATA(IP2_24_23, EX_CS1_N),
-       PINMUX_IPSR_MODSEL_DATA(IP2_24_23, MSIOF2_SCK, SEL_SOF2_0),
+       PINMUX_IPSR_MSEL(IP2_24_23, MSIOF2_SCK, SEL_SOF2_0),
        PINMUX_IPSR_DATA(IP2_26_25, EX_CS2_N),
-       PINMUX_IPSR_MODSEL_DATA(IP2_26_25, ATAWR0_N, SEL_LBS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_26_25, MSIOF2_SYNC, SEL_SOF2_0),
+       PINMUX_IPSR_MSEL(IP2_26_25, ATAWR0_N, SEL_LBS_0),
+       PINMUX_IPSR_MSEL(IP2_26_25, MSIOF2_SYNC, SEL_SOF2_0),
        PINMUX_IPSR_DATA(IP2_29_27, EX_CS3_N),
-       PINMUX_IPSR_MODSEL_DATA(IP2_29_27, ATADIR0_N, SEL_LBS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_29_27, MSIOF2_TXD, SEL_SOF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_29_27, ATAG0_N, SEL_LBS_0),
+       PINMUX_IPSR_MSEL(IP2_29_27, ATADIR0_N, SEL_LBS_0),
+       PINMUX_IPSR_MSEL(IP2_29_27, MSIOF2_TXD, SEL_SOF2_0),
+       PINMUX_IPSR_MSEL(IP2_29_27, ATAG0_N, SEL_LBS_0),
        PINMUX_IPSR_DATA(IP2_29_27, EX_WAIT1),
 
        /* IPSR3 */
        PINMUX_IPSR_DATA(IP3_2_0, EX_CS4_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_2_0, ATARD0_N, SEL_LBS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_2_0, MSIOF2_RXD, SEL_SOF2_0),
+       PINMUX_IPSR_MSEL(IP3_2_0, ATARD0_N, SEL_LBS_0),
+       PINMUX_IPSR_MSEL(IP3_2_0, MSIOF2_RXD, SEL_SOF2_0),
        PINMUX_IPSR_DATA(IP3_2_0, EX_WAIT2),
        PINMUX_IPSR_DATA(IP3_5_3, EX_CS5_N),
        PINMUX_IPSR_DATA(IP3_5_3, ATACS00_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_5_3, MSIOF2_SS1, SEL_SOF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_5_3, HRX1_B, SEL_HSCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_5_3, SCIFB1_RXD_B, SEL_SCIFB1_1),
+       PINMUX_IPSR_MSEL(IP3_5_3, MSIOF2_SS1, SEL_SOF2_0),
+       PINMUX_IPSR_MSEL(IP3_5_3, HRX1_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP3_5_3, SCIFB1_RXD_B, SEL_SCIFB1_1),
        PINMUX_IPSR_DATA(IP3_5_3, PWM1),
        PINMUX_IPSR_DATA(IP3_5_3, TPU_TO1),
        PINMUX_IPSR_DATA(IP3_8_6, BS_N),
        PINMUX_IPSR_DATA(IP3_8_6, ATACS10_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_8_6, MSIOF2_SS2, SEL_SOF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_8_6, HTX1_B, SEL_HSCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_8_6, SCIFB1_TXD_B, SEL_SCIFB1_1),
+       PINMUX_IPSR_MSEL(IP3_8_6, MSIOF2_SS2, SEL_SOF2_0),
+       PINMUX_IPSR_MSEL(IP3_8_6, HTX1_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP3_8_6, SCIFB1_TXD_B, SEL_SCIFB1_1),
        PINMUX_IPSR_DATA(IP3_8_6, PWM2),
        PINMUX_IPSR_DATA(IP3_8_6, TPU_TO2),
        PINMUX_IPSR_DATA(IP3_11_9, RD_WR_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_9, HRX2_B, SEL_HSCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_9, FMIN_B, SEL_FM_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_9, SCIFB0_RXD_B, SEL_SCIFB_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_9, DREQ1_D, SEL_LBS_1),
+       PINMUX_IPSR_MSEL(IP3_11_9, HRX2_B, SEL_HSCIF2_1),
+       PINMUX_IPSR_MSEL(IP3_11_9, FMIN_B, SEL_FM_1),
+       PINMUX_IPSR_MSEL(IP3_11_9, SCIFB0_RXD_B, SEL_SCIFB_1),
+       PINMUX_IPSR_MSEL(IP3_11_9, DREQ1_D, SEL_LBS_1),
        PINMUX_IPSR_DATA(IP3_13_12, WE0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_13_12, HCTS2_N_B, SEL_HSCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_13_12, SCIFB0_TXD_B, SEL_SCIFB_1),
+       PINMUX_IPSR_MSEL(IP3_13_12, HCTS2_N_B, SEL_HSCIF2_1),
+       PINMUX_IPSR_MSEL(IP3_13_12, SCIFB0_TXD_B, SEL_SCIFB_1),
        PINMUX_IPSR_DATA(IP3_15_14, WE1_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_15_14, ATARD0_N_B, SEL_LBS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_15_14, HTX2_B, SEL_HSCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_15_14, SCIFB0_RTS_N_B, SEL_SCIFB_1),
+       PINMUX_IPSR_MSEL(IP3_15_14, ATARD0_N_B, SEL_LBS_1),
+       PINMUX_IPSR_MSEL(IP3_15_14, HTX2_B, SEL_HSCIF2_1),
+       PINMUX_IPSR_MSEL(IP3_15_14, SCIFB0_RTS_N_B, SEL_SCIFB_1),
        PINMUX_IPSR_DATA(IP3_17_16, EX_WAIT0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_17_16, HRTS2_N_B, SEL_HSCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_17_16, SCIFB0_CTS_N_B, SEL_SCIFB_1),
+       PINMUX_IPSR_MSEL(IP3_17_16, HRTS2_N_B, SEL_HSCIF2_1),
+       PINMUX_IPSR_MSEL(IP3_17_16, SCIFB0_CTS_N_B, SEL_SCIFB_1),
        PINMUX_IPSR_DATA(IP3_19_18, DREQ0),
        PINMUX_IPSR_DATA(IP3_19_18, PWM3),
        PINMUX_IPSR_DATA(IP3_19_18, TPU_TO3),
        PINMUX_IPSR_DATA(IP3_21_20, DACK0),
        PINMUX_IPSR_DATA(IP3_21_20, DRACK0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_21_20, REMOCON, SEL_RCN_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_24_22, SPEEDIN, SEL_RSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_24_22, HSCK0_C, SEL_HSCIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_24_22, HSCK2_C, SEL_HSCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_24_22, SCIFB0_SCK_B, SEL_SCIFB_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_24_22, SCIFB2_SCK_B, SEL_SCIFB2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_24_22, DREQ2_C, SEL_LBS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_30_28, HTX2_C, SEL_HSCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_27_25, SSI_SCK0129, SEL_SSI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_27_25, HRX0_C, SEL_HSCIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_27_25, HRX2_C, SEL_HSCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_27_25, SCIFB0_RXD_C, SEL_SCIFB_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_27_25, SCIFB2_RXD_C, SEL_SCIFB2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_30_28, SSI_WS0129, SEL_SSI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_30_28, HTX0_C, SEL_HSCIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_30_28, HTX2_C, SEL_HSCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_30_28, SCIFB0_TXD_C, SEL_SCIFB_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_30_28, SCIFB2_TXD_C, SEL_SCIFB2_2),
+       PINMUX_IPSR_MSEL(IP3_21_20, REMOCON, SEL_RCN_0),
+       PINMUX_IPSR_MSEL(IP3_24_22, SPEEDIN, SEL_RSP_0),
+       PINMUX_IPSR_MSEL(IP3_24_22, HSCK0_C, SEL_HSCIF0_2),
+       PINMUX_IPSR_MSEL(IP3_24_22, HSCK2_C, SEL_HSCIF2_2),
+       PINMUX_IPSR_MSEL(IP3_24_22, SCIFB0_SCK_B, SEL_SCIFB_1),
+       PINMUX_IPSR_MSEL(IP3_24_22, SCIFB2_SCK_B, SEL_SCIFB2_1),
+       PINMUX_IPSR_MSEL(IP3_24_22, DREQ2_C, SEL_LBS_2),
+       PINMUX_IPSR_MSEL(IP3_30_28, HTX2_C, SEL_HSCIF2_2),
+       PINMUX_IPSR_MSEL(IP3_27_25, SSI_SCK0129, SEL_SSI0_0),
+       PINMUX_IPSR_MSEL(IP3_27_25, HRX0_C, SEL_HSCIF0_2),
+       PINMUX_IPSR_MSEL(IP3_27_25, HRX2_C, SEL_HSCIF2_2),
+       PINMUX_IPSR_MSEL(IP3_27_25, SCIFB0_RXD_C, SEL_SCIFB_2),
+       PINMUX_IPSR_MSEL(IP3_27_25, SCIFB2_RXD_C, SEL_SCIFB2_2),
+       PINMUX_IPSR_MSEL(IP3_30_28, SSI_WS0129, SEL_SSI0_0),
+       PINMUX_IPSR_MSEL(IP3_30_28, HTX0_C, SEL_HSCIF0_2),
+       PINMUX_IPSR_MSEL(IP3_30_28, HTX2_C, SEL_HSCIF2_2),
+       PINMUX_IPSR_MSEL(IP3_30_28, SCIFB0_TXD_C, SEL_SCIFB_2),
+       PINMUX_IPSR_MSEL(IP3_30_28, SCIFB2_TXD_C, SEL_SCIFB2_2),
 
        /* IPSR4 */
-       PINMUX_IPSR_MODSEL_DATA(IP4_1_0, SSI_SDATA0, SEL_SSI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_1_0, SCL0_B, SEL_IIC0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_1_0, SCL7_B, SEL_IIC7_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_1_0, MSIOF2_SCK_C, SEL_SOF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_4_2, SSI_SCK1, SEL_SSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_4_2, SDA0_B, SEL_IIC0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_4_2, SDA7_B, SEL_IIC7_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_4_2, MSIOF2_SYNC_C, SEL_SOF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_4_2, GLO_I0_D, SEL_GPS_3),
-       PINMUX_IPSR_MODSEL_DATA(IP4_7_5, SSI_WS1, SEL_SSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_7_5, SCL1_B, SEL_IIC1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_7_5, SCL8_B, SEL_IIC8_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_7_5, MSIOF2_TXD_C, SEL_SOF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_7_5, GLO_I1_D, SEL_GPS_3),
-       PINMUX_IPSR_MODSEL_DATA(IP4_9_8, SSI_SDATA1, SEL_SSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_9_8, SDA1_B, SEL_IIC1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_9_8, SDA8_B, SEL_IIC8_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_9_8, MSIOF2_RXD_C, SEL_SOF2_2),
+       PINMUX_IPSR_MSEL(IP4_1_0, SSI_SDATA0, SEL_SSI0_0),
+       PINMUX_IPSR_MSEL(IP4_1_0, SCL0_B, SEL_IIC0_1),
+       PINMUX_IPSR_MSEL(IP4_1_0, SCL7_B, SEL_IIC7_1),
+       PINMUX_IPSR_MSEL(IP4_1_0, MSIOF2_SCK_C, SEL_SOF2_2),
+       PINMUX_IPSR_MSEL(IP4_4_2, SSI_SCK1, SEL_SSI1_0),
+       PINMUX_IPSR_MSEL(IP4_4_2, SDA0_B, SEL_IIC0_1),
+       PINMUX_IPSR_MSEL(IP4_4_2, SDA7_B, SEL_IIC7_1),
+       PINMUX_IPSR_MSEL(IP4_4_2, MSIOF2_SYNC_C, SEL_SOF2_2),
+       PINMUX_IPSR_MSEL(IP4_4_2, GLO_I0_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP4_7_5, SSI_WS1, SEL_SSI1_0),
+       PINMUX_IPSR_MSEL(IP4_7_5, SCL1_B, SEL_IIC1_1),
+       PINMUX_IPSR_MSEL(IP4_7_5, SCL8_B, SEL_IIC8_1),
+       PINMUX_IPSR_MSEL(IP4_7_5, MSIOF2_TXD_C, SEL_SOF2_2),
+       PINMUX_IPSR_MSEL(IP4_7_5, GLO_I1_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP4_9_8, SSI_SDATA1, SEL_SSI1_0),
+       PINMUX_IPSR_MSEL(IP4_9_8, SDA1_B, SEL_IIC1_1),
+       PINMUX_IPSR_MSEL(IP4_9_8, SDA8_B, SEL_IIC8_1),
+       PINMUX_IPSR_MSEL(IP4_9_8, MSIOF2_RXD_C, SEL_SOF2_2),
        PINMUX_IPSR_DATA(IP4_12_10, SSI_SCK2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_12_10, SCL2, SEL_IIC2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_12_10, GPS_CLK_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_12_10, GLO_Q0_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP4_12_10, SCL2, SEL_IIC2_0),
+       PINMUX_IPSR_MSEL(IP4_12_10, GPS_CLK_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP4_12_10, GLO_Q0_D, SEL_GPS_3),
        PINMUX_IPSR_DATA(IP4_15_13, SSI_WS2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_15_13, SDA2, SEL_IIC2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_15_13, GPS_SIGN_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_15_13, RX2_E, SEL_SCIF2_4),
-       PINMUX_IPSR_MODSEL_DATA(IP4_15_13, GLO_Q1_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP4_15_13, SDA2, SEL_IIC2_0),
+       PINMUX_IPSR_MSEL(IP4_15_13, GPS_SIGN_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP4_15_13, RX2_E, SEL_SCIF2_4),
+       PINMUX_IPSR_MSEL(IP4_15_13, GLO_Q1_D, SEL_GPS_3),
        PINMUX_IPSR_DATA(IP4_18_16, SSI_SDATA2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_18_16, GPS_MAG_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_18_16, TX2_E, SEL_SCIF2_4),
+       PINMUX_IPSR_MSEL(IP4_18_16, GPS_MAG_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP4_18_16, TX2_E, SEL_SCIF2_4),
        PINMUX_IPSR_DATA(IP4_19, SSI_SCK34),
        PINMUX_IPSR_DATA(IP4_20, SSI_WS34),
        PINMUX_IPSR_DATA(IP4_21, SSI_SDATA3),
        PINMUX_IPSR_DATA(IP4_23_22, SSI_SCK4),
-       PINMUX_IPSR_MODSEL_DATA(IP4_23_22, GLO_SS_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP4_23_22, GLO_SS_D, SEL_GPS_3),
        PINMUX_IPSR_DATA(IP4_25_24, SSI_WS4),
-       PINMUX_IPSR_MODSEL_DATA(IP4_25_24, GLO_RFON_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP4_25_24, GLO_RFON_D, SEL_GPS_3),
        PINMUX_IPSR_DATA(IP4_27_26, SSI_SDATA4),
-       PINMUX_IPSR_MODSEL_DATA(IP4_27_26, MSIOF2_SCK_D, SEL_SOF2_3),
+       PINMUX_IPSR_MSEL(IP4_27_26, MSIOF2_SCK_D, SEL_SOF2_3),
        PINMUX_IPSR_DATA(IP4_30_28, SSI_SCK5),
-       PINMUX_IPSR_MODSEL_DATA(IP4_30_28, MSIOF1_SCK_C, SEL_SOF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_30_28, TS_SDATA0, SEL_TSIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_30_28, GLO_I0, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_30_28, MSIOF2_SYNC_D, SEL_SOF2_3),
+       PINMUX_IPSR_MSEL(IP4_30_28, MSIOF1_SCK_C, SEL_SOF1_2),
+       PINMUX_IPSR_MSEL(IP4_30_28, TS_SDATA0, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP4_30_28, GLO_I0, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP4_30_28, MSIOF2_SYNC_D, SEL_SOF2_3),
        PINMUX_IPSR_DATA(IP4_30_28, VI1_R2_B),
 
        /* IPSR5 */
        PINMUX_IPSR_DATA(IP5_2_0, SSI_WS5),
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, MSIOF1_SYNC_C, SEL_SOF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, TS_SCK0, SEL_TSIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, GLO_I1, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, MSIOF2_TXD_D, SEL_SOF2_3),
+       PINMUX_IPSR_MSEL(IP5_2_0, MSIOF1_SYNC_C, SEL_SOF1_2),
+       PINMUX_IPSR_MSEL(IP5_2_0, TS_SCK0, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP5_2_0, GLO_I1, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP5_2_0, MSIOF2_TXD_D, SEL_SOF2_3),
        PINMUX_IPSR_DATA(IP5_2_0, VI1_R3_B),
        PINMUX_IPSR_DATA(IP5_5_3, SSI_SDATA5),
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, MSIOF1_TXD_C, SEL_SOF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, TS_SDEN0, SEL_TSIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, GLO_Q0, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, MSIOF2_SS1_D, SEL_SOF2_3),
+       PINMUX_IPSR_MSEL(IP5_5_3, MSIOF1_TXD_C, SEL_SOF1_2),
+       PINMUX_IPSR_MSEL(IP5_5_3, TS_SDEN0, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP5_5_3, GLO_Q0, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP5_5_3, MSIOF2_SS1_D, SEL_SOF2_3),
        PINMUX_IPSR_DATA(IP5_5_3, VI1_R4_B),
        PINMUX_IPSR_DATA(IP5_8_6, SSI_SCK6),
-       PINMUX_IPSR_MODSEL_DATA(IP5_8_6, MSIOF1_RXD_C, SEL_SOF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP5_8_6, TS_SPSYNC0, SEL_TSIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_8_6, GLO_Q1, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_8_6, MSIOF2_RXD_D, SEL_SOF2_3),
+       PINMUX_IPSR_MSEL(IP5_8_6, MSIOF1_RXD_C, SEL_SOF1_2),
+       PINMUX_IPSR_MSEL(IP5_8_6, TS_SPSYNC0, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP5_8_6, GLO_Q1, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP5_8_6, MSIOF2_RXD_D, SEL_SOF2_3),
        PINMUX_IPSR_DATA(IP5_8_6, VI1_R5_B),
        PINMUX_IPSR_DATA(IP5_11_9, SSI_WS6),
-       PINMUX_IPSR_MODSEL_DATA(IP5_11_9, GLO_SCLK, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_11_9, MSIOF2_SS2_D, SEL_SOF2_3),
+       PINMUX_IPSR_MSEL(IP5_11_9, GLO_SCLK, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP5_11_9, MSIOF2_SS2_D, SEL_SOF2_3),
        PINMUX_IPSR_DATA(IP5_11_9, VI1_R6_B),
        PINMUX_IPSR_DATA(IP5_14_12, SSI_SDATA6),
-       PINMUX_IPSR_MODSEL_DATA(IP5_14_12, STP_IVCXO27_0_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_14_12, GLO_SDATA, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP5_14_12, STP_IVCXO27_0_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP5_14_12, GLO_SDATA, SEL_GPS_0),
        PINMUX_IPSR_DATA(IP5_14_12, VI1_R7_B),
-       PINMUX_IPSR_MODSEL_DATA(IP5_16_15, SSI_SCK78, SEL_SSI7_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_16_15, STP_ISCLK_0_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_16_15, GLO_SS, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_19_17, SSI_WS78, SEL_SSI7_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_19_17, TX0_D, SEL_SCIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_19_17, STP_ISD_0_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_19_17, GLO_RFON, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_21_20, SSI_SDATA7, SEL_SSI7_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_21_20, RX0_D, SEL_SCIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_21_20, STP_ISEN_0_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_22, SSI_SDATA8, SEL_SSI8_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_22, TX1_D, SEL_SCIF1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_23_22, STP_ISSYNC_0_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_25_24, SSI_SCK9, SEL_SSI9_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_25_24, RX1_D, SEL_SCIF1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_25_24, GLO_SCLK_D, SEL_GPS_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_28_26, SSI_WS9, SEL_SSI9_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_28_26, TX3_D, SEL_SCIF3_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_28_26, CAN0_TX_D, SEL_CAN0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_28_26, GLO_SDATA_D, SEL_GPS_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_31_29, SSI_SDATA9, SEL_SSI9_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_31_29, RX3_D, SEL_SCIF3_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_31_29, CAN0_RX_D, SEL_CAN0_3),
+       PINMUX_IPSR_MSEL(IP5_16_15, SSI_SCK78, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP5_16_15, STP_ISCLK_0_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP5_16_15, GLO_SS, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP5_19_17, SSI_WS78, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP5_19_17, TX0_D, SEL_SCIF0_3),
+       PINMUX_IPSR_MSEL(IP5_19_17, STP_ISD_0_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP5_19_17, GLO_RFON, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP5_21_20, SSI_SDATA7, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP5_21_20, RX0_D, SEL_SCIF0_3),
+       PINMUX_IPSR_MSEL(IP5_21_20, STP_ISEN_0_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP5_23_22, SSI_SDATA8, SEL_SSI8_0),
+       PINMUX_IPSR_MSEL(IP5_23_22, TX1_D, SEL_SCIF1_3),
+       PINMUX_IPSR_MSEL(IP5_23_22, STP_ISSYNC_0_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP5_25_24, SSI_SCK9, SEL_SSI9_0),
+       PINMUX_IPSR_MSEL(IP5_25_24, RX1_D, SEL_SCIF1_3),
+       PINMUX_IPSR_MSEL(IP5_25_24, GLO_SCLK_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP5_28_26, SSI_WS9, SEL_SSI9_0),
+       PINMUX_IPSR_MSEL(IP5_28_26, TX3_D, SEL_SCIF3_3),
+       PINMUX_IPSR_MSEL(IP5_28_26, CAN0_TX_D, SEL_CAN0_3),
+       PINMUX_IPSR_MSEL(IP5_28_26, GLO_SDATA_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP5_31_29, SSI_SDATA9, SEL_SSI9_0),
+       PINMUX_IPSR_MSEL(IP5_31_29, RX3_D, SEL_SCIF3_3),
+       PINMUX_IPSR_MSEL(IP5_31_29, CAN0_RX_D, SEL_CAN0_3),
 
        /* IPSR6 */
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, AUDIO_CLKB, SEL_ADG_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, STP_OPWM_0_B, SEL_SSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, MSIOF1_SCK_B, SEL_SOF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, SCIF_CLK, SEL_SCIF_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, BPFCLK_E, SEL_FM_4),
+       PINMUX_IPSR_MSEL(IP6_2_0, AUDIO_CLKB, SEL_ADG_0),
+       PINMUX_IPSR_MSEL(IP6_2_0, STP_OPWM_0_B, SEL_SSP_1),
+       PINMUX_IPSR_MSEL(IP6_2_0, MSIOF1_SCK_B, SEL_SOF1_1),
+       PINMUX_IPSR_MSEL(IP6_2_0, SCIF_CLK, SEL_SCIF_0),
+       PINMUX_IPSR_MSEL(IP6_2_0, BPFCLK_E, SEL_FM_4),
        PINMUX_IPSR_DATA(IP6_5_3, AUDIO_CLKC),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, SCIFB0_SCK_C, SEL_SCIFB_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, MSIOF1_SYNC_B, SEL_SOF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, RX2, SEL_SCIF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, SCIFA2_RXD, SEL_SCIFA2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, FMIN_E, SEL_FM_4),
+       PINMUX_IPSR_MSEL(IP6_5_3, SCIFB0_SCK_C, SEL_SCIFB_2),
+       PINMUX_IPSR_MSEL(IP6_5_3, MSIOF1_SYNC_B, SEL_SOF1_1),
+       PINMUX_IPSR_MSEL(IP6_5_3, RX2, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP6_5_3, SCIFA2_RXD, SEL_SCIFA2_0),
+       PINMUX_IPSR_MSEL(IP6_5_3, FMIN_E, SEL_FM_4),
        PINMUX_IPSR_DATA(IP6_7_6, AUDIO_CLKOUT),
-       PINMUX_IPSR_MODSEL_DATA(IP6_7_6, MSIOF1_SS1_B, SEL_SOF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, TX2, SEL_SCIF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_7_6, SCIFA2_TXD, SEL_SCIFA2_0),
+       PINMUX_IPSR_MSEL(IP6_7_6, MSIOF1_SS1_B, SEL_SOF1_1),
+       PINMUX_IPSR_MSEL(IP6_5_3, TX2, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP6_7_6, SCIFA2_TXD, SEL_SCIFA2_0),
        PINMUX_IPSR_DATA(IP6_9_8, IRQ0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_9_8, SCIFB1_RXD_D, SEL_SCIFB1_3),
+       PINMUX_IPSR_MSEL(IP6_9_8, SCIFB1_RXD_D, SEL_SCIFB1_3),
        PINMUX_IPSR_DATA(IP6_9_8, INTC_IRQ0_N),
        PINMUX_IPSR_DATA(IP6_11_10, IRQ1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_11_10, SCIFB1_SCK_C, SEL_SCIFB1_2),
+       PINMUX_IPSR_MSEL(IP6_11_10, SCIFB1_SCK_C, SEL_SCIFB1_2),
        PINMUX_IPSR_DATA(IP6_11_10, INTC_IRQ1_N),
        PINMUX_IPSR_DATA(IP6_13_12, IRQ2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_13_12, SCIFB1_TXD_D, SEL_SCIFB1_3),
+       PINMUX_IPSR_MSEL(IP6_13_12, SCIFB1_TXD_D, SEL_SCIFB1_3),
        PINMUX_IPSR_DATA(IP6_13_12, INTC_IRQ2_N),
        PINMUX_IPSR_DATA(IP6_15_14, IRQ3),
-       PINMUX_IPSR_MODSEL_DATA(IP6_15_14, SCL4_C, SEL_IIC4_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_15_14, MSIOF2_TXD_E, SEL_SOF2_4),
+       PINMUX_IPSR_MSEL(IP6_15_14, SCL4_C, SEL_IIC4_2),
+       PINMUX_IPSR_MSEL(IP6_15_14, MSIOF2_TXD_E, SEL_SOF2_4),
        PINMUX_IPSR_DATA(IP6_15_14, INTC_IRQ4_N),
        PINMUX_IPSR_DATA(IP6_18_16, IRQ4),
-       PINMUX_IPSR_MODSEL_DATA(IP6_18_16, HRX1_C, SEL_HSCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_18_16, SDA4_C, SEL_IIC4_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_18_16, MSIOF2_RXD_E, SEL_SOF2_4),
+       PINMUX_IPSR_MSEL(IP6_18_16, HRX1_C, SEL_HSCIF1_2),
+       PINMUX_IPSR_MSEL(IP6_18_16, SDA4_C, SEL_IIC4_2),
+       PINMUX_IPSR_MSEL(IP6_18_16, MSIOF2_RXD_E, SEL_SOF2_4),
        PINMUX_IPSR_DATA(IP6_18_16, INTC_IRQ4_N),
        PINMUX_IPSR_DATA(IP6_20_19, IRQ5),
-       PINMUX_IPSR_MODSEL_DATA(IP6_20_19, HTX1_C, SEL_HSCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_20_19, SCL1_E, SEL_IIC1_4),
-       PINMUX_IPSR_MODSEL_DATA(IP6_20_19, MSIOF2_SCK_E, SEL_SOF2_4),
+       PINMUX_IPSR_MSEL(IP6_20_19, HTX1_C, SEL_HSCIF1_2),
+       PINMUX_IPSR_MSEL(IP6_20_19, SCL1_E, SEL_IIC1_4),
+       PINMUX_IPSR_MSEL(IP6_20_19, MSIOF2_SCK_E, SEL_SOF2_4),
        PINMUX_IPSR_DATA(IP6_23_21, IRQ6),
-       PINMUX_IPSR_MODSEL_DATA(IP6_23_21, HSCK1_C, SEL_HSCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_23_21, MSIOF1_SS2_B, SEL_SOF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_23_21, SDA1_E, SEL_IIC1_4),
-       PINMUX_IPSR_MODSEL_DATA(IP6_23_21, MSIOF2_SYNC_E, SEL_SOF2_4),
+       PINMUX_IPSR_MSEL(IP6_23_21, HSCK1_C, SEL_HSCIF1_2),
+       PINMUX_IPSR_MSEL(IP6_23_21, MSIOF1_SS2_B, SEL_SOF1_1),
+       PINMUX_IPSR_MSEL(IP6_23_21, SDA1_E, SEL_IIC1_4),
+       PINMUX_IPSR_MSEL(IP6_23_21, MSIOF2_SYNC_E, SEL_SOF2_4),
        PINMUX_IPSR_DATA(IP6_26_24, IRQ7),
-       PINMUX_IPSR_MODSEL_DATA(IP6_26_24, HCTS1_N_C, SEL_HSCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_26_24, MSIOF1_TXD_B, SEL_SOF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_26_24, GPS_CLK_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_26_24, GPS_CLK_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP6_26_24, HCTS1_N_C, SEL_HSCIF1_2),
+       PINMUX_IPSR_MSEL(IP6_26_24, MSIOF1_TXD_B, SEL_SOF1_1),
+       PINMUX_IPSR_MSEL(IP6_26_24, GPS_CLK_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP6_26_24, GPS_CLK_D, SEL_GPS_3),
        PINMUX_IPSR_DATA(IP6_29_27, IRQ8),
-       PINMUX_IPSR_MODSEL_DATA(IP6_29_27, HRTS1_N_C, SEL_HSCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_29_27, MSIOF1_RXD_B, SEL_SOF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_29_27, GPS_SIGN_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_29_27, GPS_SIGN_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP6_29_27, HRTS1_N_C, SEL_HSCIF1_2),
+       PINMUX_IPSR_MSEL(IP6_29_27, MSIOF1_RXD_B, SEL_SOF1_1),
+       PINMUX_IPSR_MSEL(IP6_29_27, GPS_SIGN_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP6_29_27, GPS_SIGN_D, SEL_GPS_3),
 
        /* IPSR7 */
        PINMUX_IPSR_DATA(IP7_2_0, IRQ9),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, DU1_DOTCLKIN_B, SEL_DIS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, CAN_CLK_D, SEL_CANCLK_3),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, GPS_MAG_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, SCIF_CLK_B, SEL_SCIF_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, GPS_MAG_D, SEL_GPS_3),
+       PINMUX_IPSR_MSEL(IP7_2_0, DU1_DOTCLKIN_B, SEL_DIS_1),
+       PINMUX_IPSR_MSEL(IP7_2_0, CAN_CLK_D, SEL_CANCLK_3),
+       PINMUX_IPSR_MSEL(IP7_2_0, GPS_MAG_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP7_2_0, SCIF_CLK_B, SEL_SCIF_1),
+       PINMUX_IPSR_MSEL(IP7_2_0, GPS_MAG_D, SEL_GPS_3),
        PINMUX_IPSR_DATA(IP7_5_3, DU1_DR0),
        PINMUX_IPSR_DATA(IP7_5_3, LCDOUT0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, VI1_DATA0_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, TX0_B, SEL_SCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, SCIFA0_TXD_B, SEL_SCFA_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, MSIOF2_SCK_B, SEL_SOF2_1),
+       PINMUX_IPSR_MSEL(IP7_5_3, VI1_DATA0_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP7_5_3, TX0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP7_5_3, SCIFA0_TXD_B, SEL_SCFA_1),
+       PINMUX_IPSR_MSEL(IP7_5_3, MSIOF2_SCK_B, SEL_SOF2_1),
        PINMUX_IPSR_DATA(IP7_8_6, DU1_DR1),
        PINMUX_IPSR_DATA(IP7_8_6, LCDOUT1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, VI1_DATA1_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, RX0_B, SEL_SCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, SCIFA0_RXD_B, SEL_SCFA_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, MSIOF2_SYNC_B, SEL_SOF2_1),
+       PINMUX_IPSR_MSEL(IP7_8_6, VI1_DATA1_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP7_8_6, RX0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP7_8_6, SCIFA0_RXD_B, SEL_SCFA_1),
+       PINMUX_IPSR_MSEL(IP7_8_6, MSIOF2_SYNC_B, SEL_SOF2_1),
        PINMUX_IPSR_DATA(IP7_10_9, DU1_DR2),
        PINMUX_IPSR_DATA(IP7_10_9, LCDOUT2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_10_9, SSI_SCK0129_B, SEL_SSI0_1),
+       PINMUX_IPSR_MSEL(IP7_10_9, SSI_SCK0129_B, SEL_SSI0_1),
        PINMUX_IPSR_DATA(IP7_12_11, DU1_DR3),
        PINMUX_IPSR_DATA(IP7_12_11, LCDOUT3),
-       PINMUX_IPSR_MODSEL_DATA(IP7_12_11, SSI_WS0129_B, SEL_SSI0_1),
+       PINMUX_IPSR_MSEL(IP7_12_11, SSI_WS0129_B, SEL_SSI0_1),
        PINMUX_IPSR_DATA(IP7_14_13, DU1_DR4),
        PINMUX_IPSR_DATA(IP7_14_13, LCDOUT4),
-       PINMUX_IPSR_MODSEL_DATA(IP7_14_13, SSI_SDATA0_B, SEL_SSI0_1),
+       PINMUX_IPSR_MSEL(IP7_14_13, SSI_SDATA0_B, SEL_SSI0_1),
        PINMUX_IPSR_DATA(IP7_16_15, DU1_DR5),
        PINMUX_IPSR_DATA(IP7_16_15, LCDOUT5),
-       PINMUX_IPSR_MODSEL_DATA(IP7_16_15, SSI_SCK1_B, SEL_SSI1_1),
+       PINMUX_IPSR_MSEL(IP7_16_15, SSI_SCK1_B, SEL_SSI1_1),
        PINMUX_IPSR_DATA(IP7_18_17, DU1_DR6),
        PINMUX_IPSR_DATA(IP7_18_17, LCDOUT6),
-       PINMUX_IPSR_MODSEL_DATA(IP7_18_17, SSI_WS1_B, SEL_SSI1_1),
+       PINMUX_IPSR_MSEL(IP7_18_17, SSI_WS1_B, SEL_SSI1_1),
        PINMUX_IPSR_DATA(IP7_20_19, DU1_DR7),
        PINMUX_IPSR_DATA(IP7_20_19, LCDOUT7),
-       PINMUX_IPSR_MODSEL_DATA(IP7_20_19, SSI_SDATA1_B, SEL_SSI1_1),
+       PINMUX_IPSR_MSEL(IP7_20_19, SSI_SDATA1_B, SEL_SSI1_1),
        PINMUX_IPSR_DATA(IP7_23_21, DU1_DG0),
        PINMUX_IPSR_DATA(IP7_23_21, LCDOUT8),
-       PINMUX_IPSR_MODSEL_DATA(IP7_23_21, VI1_DATA2_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_23_21, TX1_B, SEL_SCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_23_21, SCIFA1_TXD_B, SEL_SCIFA1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_23_21, MSIOF2_SS1_B, SEL_SOF2_1),
+       PINMUX_IPSR_MSEL(IP7_23_21, VI1_DATA2_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP7_23_21, TX1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP7_23_21, SCIFA1_TXD_B, SEL_SCIFA1_1),
+       PINMUX_IPSR_MSEL(IP7_23_21, MSIOF2_SS1_B, SEL_SOF2_1),
        PINMUX_IPSR_DATA(IP7_26_24, DU1_DG1),
        PINMUX_IPSR_DATA(IP7_26_24, LCDOUT9),
-       PINMUX_IPSR_MODSEL_DATA(IP7_26_24, VI1_DATA3_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_26_24, RX1_B, SEL_SCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_26_24, SCIFA1_RXD_B, SEL_SCIFA1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_26_24, MSIOF2_SS2_B, SEL_SOF2_1),
+       PINMUX_IPSR_MSEL(IP7_26_24, VI1_DATA3_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP7_26_24, RX1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP7_26_24, SCIFA1_RXD_B, SEL_SCIFA1_1),
+       PINMUX_IPSR_MSEL(IP7_26_24, MSIOF2_SS2_B, SEL_SOF2_1),
        PINMUX_IPSR_DATA(IP7_29_27, DU1_DG2),
        PINMUX_IPSR_DATA(IP7_29_27, LCDOUT10),
-       PINMUX_IPSR_MODSEL_DATA(IP7_29_27, VI1_DATA4_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP7_29_27, VI1_DATA4_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP7_29_27, SCIF1_SCK_B),
-       PINMUX_IPSR_MODSEL_DATA(IP7_29_27, SCIFA1_SCK, SEL_SCIFA1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_29_27, SSI_SCK78_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP7_29_27, SCIFA1_SCK, SEL_SCIFA1_0),
+       PINMUX_IPSR_MSEL(IP7_29_27, SSI_SCK78_B, SEL_SSI7_1),
 
        /* IPSR8 */
        PINMUX_IPSR_DATA(IP8_2_0, DU1_DG3),
        PINMUX_IPSR_DATA(IP8_2_0, LCDOUT11),
-       PINMUX_IPSR_MODSEL_DATA(IP8_2_0, VI1_DATA5_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_2_0, SSI_WS78_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP8_2_0, VI1_DATA5_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP8_2_0, SSI_WS78_B, SEL_SSI7_1),
        PINMUX_IPSR_DATA(IP8_5_3, DU1_DG4),
        PINMUX_IPSR_DATA(IP8_5_3, LCDOUT12),
-       PINMUX_IPSR_MODSEL_DATA(IP8_5_3, VI1_DATA6_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_5_3, HRX0_B, SEL_HSCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_5_3, SCIFB2_RXD_B, SEL_SCIFB2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_5_3, SSI_SDATA7_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP8_5_3, VI1_DATA6_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP8_5_3, HRX0_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP8_5_3, SCIFB2_RXD_B, SEL_SCIFB2_1),
+       PINMUX_IPSR_MSEL(IP8_5_3, SSI_SDATA7_B, SEL_SSI7_1),
        PINMUX_IPSR_DATA(IP8_8_6, DU1_DG5),
        PINMUX_IPSR_DATA(IP8_8_6, LCDOUT13),
-       PINMUX_IPSR_MODSEL_DATA(IP8_8_6, VI1_DATA7_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_8_6, HCTS0_N_B, SEL_HSCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_8_6, SCIFB2_TXD_B, SEL_SCIFB2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_8_6, SSI_SDATA8_B, SEL_SSI8_1),
+       PINMUX_IPSR_MSEL(IP8_8_6, VI1_DATA7_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP8_8_6, HCTS0_N_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP8_8_6, SCIFB2_TXD_B, SEL_SCIFB2_1),
+       PINMUX_IPSR_MSEL(IP8_8_6, SSI_SDATA8_B, SEL_SSI8_1),
        PINMUX_IPSR_DATA(IP8_11_9, DU1_DG6),
        PINMUX_IPSR_DATA(IP8_11_9, LCDOUT14),
-       PINMUX_IPSR_MODSEL_DATA(IP8_11_9, HRTS0_N_B, SEL_HSCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_11_9, SCIFB2_CTS_N_B, SEL_SCIFB2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_11_9, SSI_SCK9_B, SEL_SSI9_1),
+       PINMUX_IPSR_MSEL(IP8_11_9, HRTS0_N_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP8_11_9, SCIFB2_CTS_N_B, SEL_SCIFB2_1),
+       PINMUX_IPSR_MSEL(IP8_11_9, SSI_SCK9_B, SEL_SSI9_1),
        PINMUX_IPSR_DATA(IP8_14_12, DU1_DG7),
        PINMUX_IPSR_DATA(IP8_14_12, LCDOUT15),
-       PINMUX_IPSR_MODSEL_DATA(IP8_14_12, HTX0_B, SEL_HSCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_14_12, SCIFB2_RTS_N_B, SEL_SCIFB2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_14_12, SSI_WS9_B, SEL_SSI9_1),
+       PINMUX_IPSR_MSEL(IP8_14_12, HTX0_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP8_14_12, SCIFB2_RTS_N_B, SEL_SCIFB2_1),
+       PINMUX_IPSR_MSEL(IP8_14_12, SSI_WS9_B, SEL_SSI9_1),
        PINMUX_IPSR_DATA(IP8_17_15, DU1_DB0),
        PINMUX_IPSR_DATA(IP8_17_15, LCDOUT16),
-       PINMUX_IPSR_MODSEL_DATA(IP8_17_15, VI1_CLK_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_17_15, TX2_B, SEL_SCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_17_15, SCIFA2_TXD_B, SEL_SCIFA2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_17_15, MSIOF2_TXD_B, SEL_SOF2_1),
+       PINMUX_IPSR_MSEL(IP8_17_15, VI1_CLK_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP8_17_15, TX2_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP8_17_15, SCIFA2_TXD_B, SEL_SCIFA2_1),
+       PINMUX_IPSR_MSEL(IP8_17_15, MSIOF2_TXD_B, SEL_SOF2_1),
        PINMUX_IPSR_DATA(IP8_20_18, DU1_DB1),
        PINMUX_IPSR_DATA(IP8_20_18, LCDOUT17),
-       PINMUX_IPSR_MODSEL_DATA(IP8_20_18, VI1_HSYNC_N_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_20_18, RX2_B, SEL_SCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_20_18, SCIFA2_RXD_B, SEL_SCIFA2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_20_18, MSIOF2_RXD_B, SEL_SOF2_1),
+       PINMUX_IPSR_MSEL(IP8_20_18, VI1_HSYNC_N_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP8_20_18, RX2_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP8_20_18, SCIFA2_RXD_B, SEL_SCIFA2_1),
+       PINMUX_IPSR_MSEL(IP8_20_18, MSIOF2_RXD_B, SEL_SOF2_1),
        PINMUX_IPSR_DATA(IP8_23_21, DU1_DB2),
        PINMUX_IPSR_DATA(IP8_23_21, LCDOUT18),
-       PINMUX_IPSR_MODSEL_DATA(IP8_23_21, VI1_VSYNC_N_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP8_23_21, VI1_VSYNC_N_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP8_23_21, SCIF2_SCK_B),
-       PINMUX_IPSR_MODSEL_DATA(IP8_23_21, SCIFA2_SCK, SEL_SCIFA2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_23_21, SSI_SDATA9_B, SEL_SSI9_1),
+       PINMUX_IPSR_MSEL(IP8_23_21, SCIFA2_SCK, SEL_SCIFA2_1),
+       PINMUX_IPSR_MSEL(IP8_23_21, SSI_SDATA9_B, SEL_SSI9_1),
        PINMUX_IPSR_DATA(IP8_25_24, DU1_DB3),
        PINMUX_IPSR_DATA(IP8_25_24, LCDOUT19),
-       PINMUX_IPSR_MODSEL_DATA(IP8_25_24, VI1_CLKENB_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP8_25_24, VI1_CLKENB_B, SEL_VI1_1),
        PINMUX_IPSR_DATA(IP8_27_26, DU1_DB4),
        PINMUX_IPSR_DATA(IP8_27_26, LCDOUT20),
-       PINMUX_IPSR_MODSEL_DATA(IP8_27_26, VI1_FIELD_B, SEL_VI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_27_26, CAN1_RX, SEL_CAN1_0),
+       PINMUX_IPSR_MSEL(IP8_27_26, VI1_FIELD_B, SEL_VI1_1),
+       PINMUX_IPSR_MSEL(IP8_27_26, CAN1_RX, SEL_CAN1_0),
        PINMUX_IPSR_DATA(IP8_30_28, DU1_DB5),
        PINMUX_IPSR_DATA(IP8_30_28, LCDOUT21),
-       PINMUX_IPSR_MODSEL_DATA(IP8_30_28, TX3, SEL_SCIF3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_30_28, SCIFA3_TXD, SEL_SCIFA3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_30_28, CAN1_TX, SEL_CAN1_0),
+       PINMUX_IPSR_MSEL(IP8_30_28, TX3, SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP8_30_28, SCIFA3_TXD, SEL_SCIFA3_0),
+       PINMUX_IPSR_MSEL(IP8_30_28, CAN1_TX, SEL_CAN1_0),
 
        /* IPSR9 */
        PINMUX_IPSR_DATA(IP9_2_0, DU1_DB6),
        PINMUX_IPSR_DATA(IP9_2_0, LCDOUT22),
-       PINMUX_IPSR_MODSEL_DATA(IP9_2_0, SCL3_C, SEL_IIC3_2),
-       PINMUX_IPSR_MODSEL_DATA(IP9_2_0, RX3, SEL_SCIF3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_2_0, SCIFA3_RXD, SEL_SCIFA3_0),
+       PINMUX_IPSR_MSEL(IP9_2_0, SCL3_C, SEL_IIC3_2),
+       PINMUX_IPSR_MSEL(IP9_2_0, RX3, SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP9_2_0, SCIFA3_RXD, SEL_SCIFA3_0),
        PINMUX_IPSR_DATA(IP9_5_3, DU1_DB7),
        PINMUX_IPSR_DATA(IP9_5_3, LCDOUT23),
-       PINMUX_IPSR_MODSEL_DATA(IP9_5_3, SDA3_C, SEL_IIC3_2),
-       PINMUX_IPSR_MODSEL_DATA(IP9_5_3, SCIF3_SCK, SEL_SCIF3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_5_3, SCIFA3_SCK, SEL_SCIFA3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_6, DU1_DOTCLKIN, SEL_DIS_0),
+       PINMUX_IPSR_MSEL(IP9_5_3, SDA3_C, SEL_IIC3_2),
+       PINMUX_IPSR_MSEL(IP9_5_3, SCIF3_SCK, SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP9_5_3, SCIFA3_SCK, SEL_SCIFA3_0),
+       PINMUX_IPSR_MSEL(IP9_6, DU1_DOTCLKIN, SEL_DIS_0),
        PINMUX_IPSR_DATA(IP9_6, QSTVA_QVS),
        PINMUX_IPSR_DATA(IP9_7, DU1_DOTCLKOUT0),
        PINMUX_IPSR_DATA(IP9_7, QCLK),
        PINMUX_IPSR_DATA(IP9_10_8, DU1_DOTCLKOUT1),
        PINMUX_IPSR_DATA(IP9_10_8, QSTVB_QVE),
-       PINMUX_IPSR_MODSEL_DATA(IP9_10_8, CAN0_TX, SEL_CAN0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_10_8, TX3_B, SEL_SCIF3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_10_8, SCL2_B, SEL_IIC2_1),
+       PINMUX_IPSR_MSEL(IP9_10_8, CAN0_TX, SEL_CAN0_0),
+       PINMUX_IPSR_MSEL(IP9_10_8, TX3_B, SEL_SCIF3_1),
+       PINMUX_IPSR_MSEL(IP9_10_8, SCL2_B, SEL_IIC2_1),
        PINMUX_IPSR_DATA(IP9_10_8, PWM4),
        PINMUX_IPSR_DATA(IP9_11, DU1_EXHSYNC_DU1_HSYNC),
        PINMUX_IPSR_DATA(IP9_11, QSTH_QHS),
@@ -1284,280 +1283,280 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP9_12, QSTB_QHE),
        PINMUX_IPSR_DATA(IP9_15_13, DU1_EXODDF_DU1_ODDF_DISP_CDE),
        PINMUX_IPSR_DATA(IP9_15_13, QCPV_QDE),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_13, CAN0_RX, SEL_CAN0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_13, RX3_B, SEL_SCIF3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_13, SDA2_B, SEL_IIC2_1),
+       PINMUX_IPSR_MSEL(IP9_15_13, CAN0_RX, SEL_CAN0_0),
+       PINMUX_IPSR_MSEL(IP9_15_13, RX3_B, SEL_SCIF3_1),
+       PINMUX_IPSR_MSEL(IP9_15_13, SDA2_B, SEL_IIC2_1),
        PINMUX_IPSR_DATA(IP9_16, DU1_DISP),
        PINMUX_IPSR_DATA(IP9_16, QPOLA),
        PINMUX_IPSR_DATA(IP9_18_17, DU1_CDE),
        PINMUX_IPSR_DATA(IP9_18_17, QPOLB),
        PINMUX_IPSR_DATA(IP9_18_17, PWM4_B),
        PINMUX_IPSR_DATA(IP9_20_19, VI0_CLKENB),
-       PINMUX_IPSR_MODSEL_DATA(IP9_20_19, TX4, SEL_SCIF4_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_20_19, SCIFA4_TXD, SEL_SCIFA4_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_20_19, TS_SDATA0_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP9_20_19, TX4, SEL_SCIF4_0),
+       PINMUX_IPSR_MSEL(IP9_20_19, SCIFA4_TXD, SEL_SCIFA4_0),
+       PINMUX_IPSR_MSEL(IP9_20_19, TS_SDATA0_D, SEL_TSIF0_3),
        PINMUX_IPSR_DATA(IP9_22_21, VI0_FIELD),
-       PINMUX_IPSR_MODSEL_DATA(IP9_22_21, RX4, SEL_SCIF4_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_22_21, SCIFA4_RXD, SEL_SCIFA4_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_22_21, TS_SCK0_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP9_22_21, RX4, SEL_SCIF4_0),
+       PINMUX_IPSR_MSEL(IP9_22_21, SCIFA4_RXD, SEL_SCIFA4_0),
+       PINMUX_IPSR_MSEL(IP9_22_21, TS_SCK0_D, SEL_TSIF0_3),
        PINMUX_IPSR_DATA(IP9_24_23, VI0_HSYNC_N),
-       PINMUX_IPSR_MODSEL_DATA(IP9_24_23, TX5, SEL_SCIF5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_24_23, SCIFA5_TXD, SEL_SCIFA5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_24_23, TS_SDEN0_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP9_24_23, TX5, SEL_SCIF5_0),
+       PINMUX_IPSR_MSEL(IP9_24_23, SCIFA5_TXD, SEL_SCIFA5_0),
+       PINMUX_IPSR_MSEL(IP9_24_23, TS_SDEN0_D, SEL_TSIF0_3),
        PINMUX_IPSR_DATA(IP9_26_25, VI0_VSYNC_N),
-       PINMUX_IPSR_MODSEL_DATA(IP9_26_25, RX5, SEL_SCIF5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_26_25, SCIFA5_RXD, SEL_SCIFA5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_26_25, TS_SPSYNC0_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP9_26_25, RX5, SEL_SCIF5_0),
+       PINMUX_IPSR_MSEL(IP9_26_25, SCIFA5_RXD, SEL_SCIFA5_0),
+       PINMUX_IPSR_MSEL(IP9_26_25, TS_SPSYNC0_D, SEL_TSIF0_3),
        PINMUX_IPSR_DATA(IP9_28_27, VI0_DATA3_VI0_B3),
-       PINMUX_IPSR_MODSEL_DATA(IP9_28_27, SCIF3_SCK_B, SEL_SCIF3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_28_27, SCIFA3_SCK_B, SEL_SCIFA3_1),
+       PINMUX_IPSR_MSEL(IP9_28_27, SCIF3_SCK_B, SEL_SCIF3_1),
+       PINMUX_IPSR_MSEL(IP9_28_27, SCIFA3_SCK_B, SEL_SCIFA3_1),
        PINMUX_IPSR_DATA(IP9_31_29, VI0_G0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_29, SCL8, SEL_IIC8_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_29, STP_IVCXO27_0_C, SEL_SSP_2),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_29, SCL4, SEL_IIC4_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_29, HCTS2_N, SEL_HSCIF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_31_29, SCIFB2_CTS_N, SEL_SCIFB2_0),
+       PINMUX_IPSR_MSEL(IP9_31_29, SCL8, SEL_IIC8_0),
+       PINMUX_IPSR_MSEL(IP9_31_29, STP_IVCXO27_0_C, SEL_SSP_2),
+       PINMUX_IPSR_MSEL(IP9_31_29, SCL4, SEL_IIC4_0),
+       PINMUX_IPSR_MSEL(IP9_31_29, HCTS2_N, SEL_HSCIF2_0),
+       PINMUX_IPSR_MSEL(IP9_31_29, SCIFB2_CTS_N, SEL_SCIFB2_0),
        PINMUX_IPSR_DATA(IP9_31_29, ATAWR1_N),
 
        /* IPSR10 */
        PINMUX_IPSR_DATA(IP10_2_0, VI0_G1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, SDA8, SEL_IIC8_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, STP_ISCLK_0_C, SEL_SSP_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, SDA4, SEL_IIC4_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, HRTS2_N, SEL_HSCIF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, SCIFB2_RTS_N, SEL_SCIFB2_0),
+       PINMUX_IPSR_MSEL(IP10_2_0, SDA8, SEL_IIC8_0),
+       PINMUX_IPSR_MSEL(IP10_2_0, STP_ISCLK_0_C, SEL_SSP_2),
+       PINMUX_IPSR_MSEL(IP10_2_0, SDA4, SEL_IIC4_0),
+       PINMUX_IPSR_MSEL(IP10_2_0, HRTS2_N, SEL_HSCIF2_0),
+       PINMUX_IPSR_MSEL(IP10_2_0, SCIFB2_RTS_N, SEL_SCIFB2_0),
        PINMUX_IPSR_DATA(IP10_2_0, ATADIR1_N),
        PINMUX_IPSR_DATA(IP10_5_3, VI0_G2),
        PINMUX_IPSR_DATA(IP10_5_3, VI2_HSYNC_N),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, STP_ISD_0_C, SEL_SSP_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, SCL3_B, SEL_IIC3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, HSCK2, SEL_HSCIF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, SCIFB2_SCK, SEL_SCIFB2_0),
+       PINMUX_IPSR_MSEL(IP10_5_3, STP_ISD_0_C, SEL_SSP_2),
+       PINMUX_IPSR_MSEL(IP10_5_3, SCL3_B, SEL_IIC3_1),
+       PINMUX_IPSR_MSEL(IP10_5_3, HSCK2, SEL_HSCIF2_0),
+       PINMUX_IPSR_MSEL(IP10_5_3, SCIFB2_SCK, SEL_SCIFB2_0),
        PINMUX_IPSR_DATA(IP10_5_3, ATARD1_N),
        PINMUX_IPSR_DATA(IP10_8_6, VI0_G3),
        PINMUX_IPSR_DATA(IP10_8_6, VI2_VSYNC_N),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, STP_ISEN_0_C, SEL_SSP_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, SDA3_B, SEL_IIC3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, HRX2, SEL_HSCIF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, SCIFB2_RXD, SEL_SCIFB2_0),
+       PINMUX_IPSR_MSEL(IP10_8_6, STP_ISEN_0_C, SEL_SSP_2),
+       PINMUX_IPSR_MSEL(IP10_8_6, SDA3_B, SEL_IIC3_1),
+       PINMUX_IPSR_MSEL(IP10_8_6, HRX2, SEL_HSCIF2_0),
+       PINMUX_IPSR_MSEL(IP10_8_6, SCIFB2_RXD, SEL_SCIFB2_0),
        PINMUX_IPSR_DATA(IP10_8_6, ATACS01_N),
        PINMUX_IPSR_DATA(IP10_11_9, VI0_G4),
        PINMUX_IPSR_DATA(IP10_11_9, VI2_CLKENB),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, STP_ISSYNC_0_C, SEL_SSP_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, HTX2, SEL_HSCIF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, SCIFB2_TXD, SEL_SCIFB2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, SCIFB0_SCK_D, SEL_SCIFB_3),
+       PINMUX_IPSR_MSEL(IP10_11_9, STP_ISSYNC_0_C, SEL_SSP_2),
+       PINMUX_IPSR_MSEL(IP10_11_9, HTX2, SEL_HSCIF2_0),
+       PINMUX_IPSR_MSEL(IP10_11_9, SCIFB2_TXD, SEL_SCIFB2_0),
+       PINMUX_IPSR_MSEL(IP10_11_9, SCIFB0_SCK_D, SEL_SCIFB_3),
        PINMUX_IPSR_DATA(IP10_14_12, VI0_G5),
        PINMUX_IPSR_DATA(IP10_14_12, VI2_FIELD),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, STP_OPWM_0_C, SEL_SSP_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, FMCLK_D, SEL_FM_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, CAN0_TX_E, SEL_CAN0_4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, HTX1_D, SEL_HSCIF1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, SCIFB0_TXD_D, SEL_SCIFB_3),
+       PINMUX_IPSR_MSEL(IP10_14_12, STP_OPWM_0_C, SEL_SSP_2),
+       PINMUX_IPSR_MSEL(IP10_14_12, FMCLK_D, SEL_FM_3),
+       PINMUX_IPSR_MSEL(IP10_14_12, CAN0_TX_E, SEL_CAN0_4),
+       PINMUX_IPSR_MSEL(IP10_14_12, HTX1_D, SEL_HSCIF1_3),
+       PINMUX_IPSR_MSEL(IP10_14_12, SCIFB0_TXD_D, SEL_SCIFB_3),
        PINMUX_IPSR_DATA(IP10_16_15, VI0_G6),
        PINMUX_IPSR_DATA(IP10_16_15, VI2_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP10_16_15, BPFCLK_D, SEL_FM_3),
+       PINMUX_IPSR_MSEL(IP10_16_15, BPFCLK_D, SEL_FM_3),
        PINMUX_IPSR_DATA(IP10_18_17, VI0_G7),
        PINMUX_IPSR_DATA(IP10_18_17, VI2_DATA0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_17, FMIN_D, SEL_FM_3),
+       PINMUX_IPSR_MSEL(IP10_18_17, FMIN_D, SEL_FM_3),
        PINMUX_IPSR_DATA(IP10_21_19, VI0_R0),
        PINMUX_IPSR_DATA(IP10_21_19, VI2_DATA1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_21_19, GLO_I0_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_21_19, TS_SDATA0_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP10_21_19, GLO_I0_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_21_19, TS_SDATA0_C, SEL_TSIF0_2),
        PINMUX_IPSR_DATA(IP10_21_19, ATACS11_N),
        PINMUX_IPSR_DATA(IP10_24_22, VI0_R1),
        PINMUX_IPSR_DATA(IP10_24_22, VI2_DATA2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_24_22, GLO_I1_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_24_22, TS_SCK0_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP10_24_22, GLO_I1_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_24_22, TS_SCK0_C, SEL_TSIF0_2),
        PINMUX_IPSR_DATA(IP10_24_22, ATAG1_N),
        PINMUX_IPSR_DATA(IP10_26_25, VI0_R2),
        PINMUX_IPSR_DATA(IP10_26_25, VI2_DATA3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_26_25, GLO_Q0_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_26_25, TS_SDEN0_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP10_26_25, GLO_Q0_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_26_25, TS_SDEN0_C, SEL_TSIF0_2),
        PINMUX_IPSR_DATA(IP10_28_27, VI0_R3),
        PINMUX_IPSR_DATA(IP10_28_27, VI2_DATA4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_28_27, GLO_Q1_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_28_27, TS_SPSYNC0_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP10_28_27, GLO_Q1_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_28_27, TS_SPSYNC0_C, SEL_TSIF0_2),
        PINMUX_IPSR_DATA(IP10_31_29, VI0_R4),
        PINMUX_IPSR_DATA(IP10_31_29, VI2_DATA5),
-       PINMUX_IPSR_MODSEL_DATA(IP10_31_29, GLO_SCLK_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_31_29, TX0_C, SEL_SCIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_31_29, SCL1_D, SEL_IIC1_3),
+       PINMUX_IPSR_MSEL(IP10_31_29, GLO_SCLK_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP10_31_29, TX0_C, SEL_SCIF0_2),
+       PINMUX_IPSR_MSEL(IP10_31_29, SCL1_D, SEL_IIC1_3),
 
        /* IPSR11 */
        PINMUX_IPSR_DATA(IP11_2_0, VI0_R5),
        PINMUX_IPSR_DATA(IP11_2_0, VI2_DATA6),
-       PINMUX_IPSR_MODSEL_DATA(IP11_2_0, GLO_SDATA_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_2_0, RX0_C, SEL_SCIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP11_2_0, SDA1_D, SEL_IIC1_3),
+       PINMUX_IPSR_MSEL(IP11_2_0, GLO_SDATA_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP11_2_0, RX0_C, SEL_SCIF0_2),
+       PINMUX_IPSR_MSEL(IP11_2_0, SDA1_D, SEL_IIC1_3),
        PINMUX_IPSR_DATA(IP11_5_3, VI0_R6),
        PINMUX_IPSR_DATA(IP11_5_3, VI2_DATA7),
-       PINMUX_IPSR_MODSEL_DATA(IP11_5_3, GLO_SS_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_5_3, TX1_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP11_5_3, SCL4_B, SEL_IIC4_1),
+       PINMUX_IPSR_MSEL(IP11_5_3, GLO_SS_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP11_5_3, TX1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP11_5_3, SCL4_B, SEL_IIC4_1),
        PINMUX_IPSR_DATA(IP11_8_6, VI0_R7),
-       PINMUX_IPSR_MODSEL_DATA(IP11_8_6, GLO_RFON_B, SEL_GPS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_8_6, RX1_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP11_8_6, CAN0_RX_E, SEL_CAN0_4),
-       PINMUX_IPSR_MODSEL_DATA(IP11_8_6, SDA4_B, SEL_IIC4_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_8_6, HRX1_D, SEL_HSCIF1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_8_6, SCIFB0_RXD_D, SEL_SCIFB_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_11_9, VI1_HSYNC_N, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_8_6, GLO_RFON_B, SEL_GPS_1),
+       PINMUX_IPSR_MSEL(IP11_8_6, RX1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP11_8_6, CAN0_RX_E, SEL_CAN0_4),
+       PINMUX_IPSR_MSEL(IP11_8_6, SDA4_B, SEL_IIC4_1),
+       PINMUX_IPSR_MSEL(IP11_8_6, HRX1_D, SEL_HSCIF1_3),
+       PINMUX_IPSR_MSEL(IP11_8_6, SCIFB0_RXD_D, SEL_SCIFB_3),
+       PINMUX_IPSR_MSEL(IP11_11_9, VI1_HSYNC_N, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_11_9, AVB_RXD0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_11_9, TS_SDATA0_B, SEL_TSIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_11_9, TX4_B, SEL_SCIF4_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_11_9, SCIFA4_TXD_B, SEL_SCIFA4_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_14_12, VI1_VSYNC_N, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_11_9, TS_SDATA0_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP11_11_9, TX4_B, SEL_SCIF4_1),
+       PINMUX_IPSR_MSEL(IP11_11_9, SCIFA4_TXD_B, SEL_SCIFA4_1),
+       PINMUX_IPSR_MSEL(IP11_14_12, VI1_VSYNC_N, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_14_12, AVB_RXD1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_14_12, TS_SCK0_B, SEL_TSIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_14_12, RX4_B, SEL_SCIF4_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_14_12, SCIFA4_RXD_B, SEL_SCIFA4_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_16_15, VI1_CLKENB, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_14_12, TS_SCK0_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP11_14_12, RX4_B, SEL_SCIF4_1),
+       PINMUX_IPSR_MSEL(IP11_14_12, SCIFA4_RXD_B, SEL_SCIFA4_1),
+       PINMUX_IPSR_MSEL(IP11_16_15, VI1_CLKENB, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_16_15, AVB_RXD2),
-       PINMUX_IPSR_MODSEL_DATA(IP11_16_15, TS_SDEN0_B, SEL_TSIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_18_17, VI1_FIELD, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_16_15, TS_SDEN0_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP11_18_17, VI1_FIELD, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_18_17, AVB_RXD3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_18_17, TS_SPSYNC0_B, SEL_TSIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_19, VI1_CLK, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_18_17, TS_SPSYNC0_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP11_19, VI1_CLK, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_19, AVB_RXD4),
-       PINMUX_IPSR_MODSEL_DATA(IP11_20, VI1_DATA0, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_20, VI1_DATA0, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_20, AVB_RXD5),
-       PINMUX_IPSR_MODSEL_DATA(IP11_21, VI1_DATA1, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_21, VI1_DATA1, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_21, AVB_RXD6),
-       PINMUX_IPSR_MODSEL_DATA(IP11_22, VI1_DATA2, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_22, VI1_DATA2, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_22, AVB_RXD7),
-       PINMUX_IPSR_MODSEL_DATA(IP11_23, VI1_DATA3, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_23, VI1_DATA3, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_23, AVB_RX_ER),
-       PINMUX_IPSR_MODSEL_DATA(IP11_24, VI1_DATA4, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_24, VI1_DATA4, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_24, AVB_MDIO),
-       PINMUX_IPSR_MODSEL_DATA(IP11_25, VI1_DATA5, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_25, VI1_DATA5, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_25, AVB_RX_DV),
-       PINMUX_IPSR_MODSEL_DATA(IP11_26, VI1_DATA6, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_26, VI1_DATA6, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_26, AVB_MAGIC),
-       PINMUX_IPSR_MODSEL_DATA(IP11_27, VI1_DATA7, SEL_VI1_0),
+       PINMUX_IPSR_MSEL(IP11_27, VI1_DATA7, SEL_VI1_0),
        PINMUX_IPSR_DATA(IP11_27, AVB_MDC),
        PINMUX_IPSR_DATA(IP11_29_28, ETH_MDIO),
        PINMUX_IPSR_DATA(IP11_29_28, AVB_RX_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP11_29_28, SCL2_C, SEL_IIC2_2),
+       PINMUX_IPSR_MSEL(IP11_29_28, SCL2_C, SEL_IIC2_2),
        PINMUX_IPSR_DATA(IP11_31_30, ETH_CRS_DV),
        PINMUX_IPSR_DATA(IP11_31_30, AVB_LINK),
-       PINMUX_IPSR_MODSEL_DATA(IP11_31_30, SDA2_C, SEL_IIC2_2),
+       PINMUX_IPSR_MSEL(IP11_31_30, SDA2_C, SEL_IIC2_2),
 
        /* IPSR12 */
        PINMUX_IPSR_DATA(IP12_1_0, ETH_RX_ER),
        PINMUX_IPSR_DATA(IP12_1_0, AVB_CRS),
-       PINMUX_IPSR_MODSEL_DATA(IP12_1_0, SCL3, SEL_IIC3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_1_0, SCL7, SEL_IIC7_0),
+       PINMUX_IPSR_MSEL(IP12_1_0, SCL3, SEL_IIC3_0),
+       PINMUX_IPSR_MSEL(IP12_1_0, SCL7, SEL_IIC7_0),
        PINMUX_IPSR_DATA(IP12_3_2, ETH_RXD0),
        PINMUX_IPSR_DATA(IP12_3_2, AVB_PHY_INT),
-       PINMUX_IPSR_MODSEL_DATA(IP12_3_2, SDA3, SEL_IIC3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_3_2, SDA7, SEL_IIC7_0),
+       PINMUX_IPSR_MSEL(IP12_3_2, SDA3, SEL_IIC3_0),
+       PINMUX_IPSR_MSEL(IP12_3_2, SDA7, SEL_IIC7_0),
        PINMUX_IPSR_DATA(IP12_6_4, ETH_RXD1),
        PINMUX_IPSR_DATA(IP12_6_4, AVB_GTXREFCLK),
-       PINMUX_IPSR_MODSEL_DATA(IP12_6_4, CAN0_TX_C, SEL_CAN0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_6_4, SCL2_D, SEL_IIC2_3),
-       PINMUX_IPSR_MODSEL_DATA(IP12_6_4, MSIOF1_RXD_E, SEL_SOF1_4),
+       PINMUX_IPSR_MSEL(IP12_6_4, CAN0_TX_C, SEL_CAN0_2),
+       PINMUX_IPSR_MSEL(IP12_6_4, SCL2_D, SEL_IIC2_3),
+       PINMUX_IPSR_MSEL(IP12_6_4, MSIOF1_RXD_E, SEL_SOF1_4),
        PINMUX_IPSR_DATA(IP12_9_7, ETH_LINK),
        PINMUX_IPSR_DATA(IP12_9_7, AVB_TXD0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_9_7, CAN0_RX_C, SEL_CAN0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_9_7, SDA2_D, SEL_IIC2_3),
-       PINMUX_IPSR_MODSEL_DATA(IP12_9_7, MSIOF1_SCK_E, SEL_SOF1_4),
+       PINMUX_IPSR_MSEL(IP12_9_7, CAN0_RX_C, SEL_CAN0_2),
+       PINMUX_IPSR_MSEL(IP12_9_7, SDA2_D, SEL_IIC2_3),
+       PINMUX_IPSR_MSEL(IP12_9_7, MSIOF1_SCK_E, SEL_SOF1_4),
        PINMUX_IPSR_DATA(IP12_12_10, ETH_REFCLK),
        PINMUX_IPSR_DATA(IP12_12_10, AVB_TXD1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_12_10, SCIFA3_RXD_B, SEL_SCIFA3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_12_10, CAN1_RX_C, SEL_CAN1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_12_10, MSIOF1_SYNC_E, SEL_SOF1_4),
+       PINMUX_IPSR_MSEL(IP12_12_10, SCIFA3_RXD_B, SEL_SCIFA3_1),
+       PINMUX_IPSR_MSEL(IP12_12_10, CAN1_RX_C, SEL_CAN1_2),
+       PINMUX_IPSR_MSEL(IP12_12_10, MSIOF1_SYNC_E, SEL_SOF1_4),
        PINMUX_IPSR_DATA(IP12_15_13, ETH_TXD1),
        PINMUX_IPSR_DATA(IP12_15_13, AVB_TXD2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_15_13, SCIFA3_TXD_B, SEL_SCIFA3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_15_13, CAN1_TX_C, SEL_CAN1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_15_13, MSIOF1_TXD_E, SEL_SOF1_4),
+       PINMUX_IPSR_MSEL(IP12_15_13, SCIFA3_TXD_B, SEL_SCIFA3_1),
+       PINMUX_IPSR_MSEL(IP12_15_13, CAN1_TX_C, SEL_CAN1_2),
+       PINMUX_IPSR_MSEL(IP12_15_13, MSIOF1_TXD_E, SEL_SOF1_4),
        PINMUX_IPSR_DATA(IP12_17_16, ETH_TX_EN),
        PINMUX_IPSR_DATA(IP12_17_16, AVB_TXD3),
-       PINMUX_IPSR_MODSEL_DATA(IP12_17_16, TCLK1_B, SEL_TMU1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_17_16, CAN_CLK_B, SEL_CANCLK_1),
+       PINMUX_IPSR_MSEL(IP12_17_16, TCLK1_B, SEL_TMU1_0),
+       PINMUX_IPSR_MSEL(IP12_17_16, CAN_CLK_B, SEL_CANCLK_1),
        PINMUX_IPSR_DATA(IP12_19_18, ETH_MAGIC),
        PINMUX_IPSR_DATA(IP12_19_18, AVB_TXD4),
-       PINMUX_IPSR_MODSEL_DATA(IP12_19_18, IETX_C, SEL_IEB_2),
+       PINMUX_IPSR_MSEL(IP12_19_18, IETX_C, SEL_IEB_2),
        PINMUX_IPSR_DATA(IP12_21_20, ETH_TXD0),
        PINMUX_IPSR_DATA(IP12_21_20, AVB_TXD5),
-       PINMUX_IPSR_MODSEL_DATA(IP12_21_20, IECLK_C, SEL_IEB_2),
+       PINMUX_IPSR_MSEL(IP12_21_20, IECLK_C, SEL_IEB_2),
        PINMUX_IPSR_DATA(IP12_23_22, ETH_MDC),
        PINMUX_IPSR_DATA(IP12_23_22, AVB_TXD6),
-       PINMUX_IPSR_MODSEL_DATA(IP12_23_22, IERX_C, SEL_IEB_2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_26_24, STP_IVCXO27_0, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP12_23_22, IERX_C, SEL_IEB_2),
+       PINMUX_IPSR_MSEL(IP12_26_24, STP_IVCXO27_0, SEL_SSP_0),
        PINMUX_IPSR_DATA(IP12_26_24, AVB_TXD7),
-       PINMUX_IPSR_MODSEL_DATA(IP12_26_24, SCIFB2_TXD_D, SEL_SCIFB2_3),
-       PINMUX_IPSR_MODSEL_DATA(IP12_26_24, ADIDATA_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_26_24, MSIOF0_SYNC_C, SEL_SOF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_29_27, STP_ISCLK_0, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP12_26_24, SCIFB2_TXD_D, SEL_SCIFB2_3),
+       PINMUX_IPSR_MSEL(IP12_26_24, ADIDATA_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP12_26_24, MSIOF0_SYNC_C, SEL_SOF0_2),
+       PINMUX_IPSR_MSEL(IP12_29_27, STP_ISCLK_0, SEL_SSP_0),
        PINMUX_IPSR_DATA(IP12_29_27, AVB_TX_EN),
-       PINMUX_IPSR_MODSEL_DATA(IP12_29_27, SCIFB2_RXD_D, SEL_SCIFB2_3),
-       PINMUX_IPSR_MODSEL_DATA(IP12_29_27, ADICS_SAMP_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_29_27, MSIOF0_SCK_C, SEL_SOF0_2),
+       PINMUX_IPSR_MSEL(IP12_29_27, SCIFB2_RXD_D, SEL_SCIFB2_3),
+       PINMUX_IPSR_MSEL(IP12_29_27, ADICS_SAMP_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP12_29_27, MSIOF0_SCK_C, SEL_SOF0_2),
 
        /* IPSR13 */
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, STP_ISD_0, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP13_2_0, STP_ISD_0, SEL_SSP_0),
        PINMUX_IPSR_DATA(IP13_2_0, AVB_TX_ER),
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, SCIFB2_SCK_C, SEL_SCIFB2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, ADICLK_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, MSIOF0_SS1_C, SEL_SOF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP13_4_3, STP_ISEN_0, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP13_2_0, SCIFB2_SCK_C, SEL_SCIFB2_2),
+       PINMUX_IPSR_MSEL(IP13_2_0, ADICLK_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP13_2_0, MSIOF0_SS1_C, SEL_SOF0_2),
+       PINMUX_IPSR_MSEL(IP13_4_3, STP_ISEN_0, SEL_SSP_0),
        PINMUX_IPSR_DATA(IP13_4_3, AVB_TX_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP13_4_3, ADICHS0_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_4_3, MSIOF0_SS2_C, SEL_SOF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP13_6_5, STP_ISSYNC_0, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP13_4_3, ADICHS0_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP13_4_3, MSIOF0_SS2_C, SEL_SOF0_2),
+       PINMUX_IPSR_MSEL(IP13_6_5, STP_ISSYNC_0, SEL_SSP_0),
        PINMUX_IPSR_DATA(IP13_6_5, AVB_COL),
-       PINMUX_IPSR_MODSEL_DATA(IP13_6_5, ADICHS1_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_6_5, MSIOF0_RXD_C, SEL_SOF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP13_9_7, STP_OPWM_0, SEL_SSP_0),
+       PINMUX_IPSR_MSEL(IP13_6_5, ADICHS1_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP13_6_5, MSIOF0_RXD_C, SEL_SOF0_2),
+       PINMUX_IPSR_MSEL(IP13_9_7, STP_OPWM_0, SEL_SSP_0),
        PINMUX_IPSR_DATA(IP13_9_7, AVB_GTX_CLK),
        PINMUX_IPSR_DATA(IP13_9_7, PWM0_B),
-       PINMUX_IPSR_MODSEL_DATA(IP13_9_7, ADICHS2_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_9_7, MSIOF0_TXD_C, SEL_SOF0_2),
+       PINMUX_IPSR_MSEL(IP13_9_7, ADICHS2_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP13_9_7, MSIOF0_TXD_C, SEL_SOF0_2),
        PINMUX_IPSR_DATA(IP13_10, SD0_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP13_10, SPCLK_B, SEL_QSP_1),
+       PINMUX_IPSR_MSEL(IP13_10, SPCLK_B, SEL_QSP_1),
        PINMUX_IPSR_DATA(IP13_11, SD0_CMD),
-       PINMUX_IPSR_MODSEL_DATA(IP13_11, MOSI_IO0_B, SEL_QSP_1),
+       PINMUX_IPSR_MSEL(IP13_11, MOSI_IO0_B, SEL_QSP_1),
        PINMUX_IPSR_DATA(IP13_12, SD0_DATA0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_12, MISO_IO1_B, SEL_QSP_1),
+       PINMUX_IPSR_MSEL(IP13_12, MISO_IO1_B, SEL_QSP_1),
        PINMUX_IPSR_DATA(IP13_13, SD0_DATA1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_13, IO2_B, SEL_QSP_1),
+       PINMUX_IPSR_MSEL(IP13_13, IO2_B, SEL_QSP_1),
        PINMUX_IPSR_DATA(IP13_14, SD0_DATA2),
-       PINMUX_IPSR_MODSEL_DATA(IP13_14, IO3_B, SEL_QSP_1),
+       PINMUX_IPSR_MSEL(IP13_14, IO3_B, SEL_QSP_1),
        PINMUX_IPSR_DATA(IP13_15, SD0_DATA3),
-       PINMUX_IPSR_MODSEL_DATA(IP13_15, SSL_B, SEL_QSP_1),
+       PINMUX_IPSR_MSEL(IP13_15, SSL_B, SEL_QSP_1),
        PINMUX_IPSR_DATA(IP13_18_16, SD0_CD),
-       PINMUX_IPSR_MODSEL_DATA(IP13_18_16, MMC_D6_B, SEL_MMC_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_18_16, SIM0_RST_B, SEL_SIM_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_18_16, CAN0_RX_F, SEL_CAN0_5),
-       PINMUX_IPSR_MODSEL_DATA(IP13_18_16, SCIFA5_TXD_B, SEL_SCIFA5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_18_16, TX3_C, SEL_SCIF3_2),
+       PINMUX_IPSR_MSEL(IP13_18_16, MMC_D6_B, SEL_MMC_1),
+       PINMUX_IPSR_MSEL(IP13_18_16, SIM0_RST_B, SEL_SIM_1),
+       PINMUX_IPSR_MSEL(IP13_18_16, CAN0_RX_F, SEL_CAN0_5),
+       PINMUX_IPSR_MSEL(IP13_18_16, SCIFA5_TXD_B, SEL_SCIFA5_1),
+       PINMUX_IPSR_MSEL(IP13_18_16, TX3_C, SEL_SCIF3_2),
        PINMUX_IPSR_DATA(IP13_21_19, SD0_WP),
-       PINMUX_IPSR_MODSEL_DATA(IP13_21_19, MMC_D7_B, SEL_MMC_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_21_19, SIM0_D_B, SEL_SIM_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_21_19, CAN0_TX_F, SEL_CAN0_5),
-       PINMUX_IPSR_MODSEL_DATA(IP13_21_19, SCIFA5_RXD_B, SEL_SCIFA5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_21_19, RX3_C, SEL_SCIF3_2),
+       PINMUX_IPSR_MSEL(IP13_21_19, MMC_D7_B, SEL_MMC_1),
+       PINMUX_IPSR_MSEL(IP13_21_19, SIM0_D_B, SEL_SIM_1),
+       PINMUX_IPSR_MSEL(IP13_21_19, CAN0_TX_F, SEL_CAN0_5),
+       PINMUX_IPSR_MSEL(IP13_21_19, SCIFA5_RXD_B, SEL_SCIFA5_1),
+       PINMUX_IPSR_MSEL(IP13_21_19, RX3_C, SEL_SCIF3_2),
        PINMUX_IPSR_DATA(IP13_22, SD1_CMD),
-       PINMUX_IPSR_MODSEL_DATA(IP13_22, REMOCON_B, SEL_RCN_1),
+       PINMUX_IPSR_MSEL(IP13_22, REMOCON_B, SEL_RCN_1),
        PINMUX_IPSR_DATA(IP13_24_23, SD1_DATA0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_24_23, SPEEDIN_B, SEL_RSP_1),
+       PINMUX_IPSR_MSEL(IP13_24_23, SPEEDIN_B, SEL_RSP_1),
        PINMUX_IPSR_DATA(IP13_25, SD1_DATA1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_25, IETX_B, SEL_IEB_1),
+       PINMUX_IPSR_MSEL(IP13_25, IETX_B, SEL_IEB_1),
        PINMUX_IPSR_DATA(IP13_26, SD1_DATA2),
-       PINMUX_IPSR_MODSEL_DATA(IP13_26, IECLK_B, SEL_IEB_1),
+       PINMUX_IPSR_MSEL(IP13_26, IECLK_B, SEL_IEB_1),
        PINMUX_IPSR_DATA(IP13_27, SD1_DATA3),
-       PINMUX_IPSR_MODSEL_DATA(IP13_27, IERX_B, SEL_IEB_1),
+       PINMUX_IPSR_MSEL(IP13_27, IERX_B, SEL_IEB_1),
        PINMUX_IPSR_DATA(IP13_30_28, SD1_CD),
        PINMUX_IPSR_DATA(IP13_30_28, PWM0),
        PINMUX_IPSR_DATA(IP13_30_28, TPU_TO0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_30_28, SCL1_C, SEL_IIC1_2),
+       PINMUX_IPSR_MSEL(IP13_30_28, SCL1_C, SEL_IIC1_2),
 
        /* IPSR14 */
        PINMUX_IPSR_DATA(IP14_1_0, SD1_WP),
        PINMUX_IPSR_DATA(IP14_1_0, PWM1_B),
-       PINMUX_IPSR_MODSEL_DATA(IP14_1_0, SDA1_C, SEL_IIC1_2),
+       PINMUX_IPSR_MSEL(IP14_1_0, SDA1_C, SEL_IIC1_2),
        PINMUX_IPSR_DATA(IP14_2, SD2_CLK),
        PINMUX_IPSR_DATA(IP14_2, MMC_CLK),
        PINMUX_IPSR_DATA(IP14_3, SD2_CMD),
@@ -1572,123 +1571,123 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP14_7, MMC_D3),
        PINMUX_IPSR_DATA(IP14_10_8, SD2_CD),
        PINMUX_IPSR_DATA(IP14_10_8, MMC_D4),
-       PINMUX_IPSR_MODSEL_DATA(IP14_10_8, SCL8_C, SEL_IIC8_2),
-       PINMUX_IPSR_MODSEL_DATA(IP14_10_8, TX5_B, SEL_SCIF5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP14_10_8, SCIFA5_TXD_C, SEL_SCIFA5_2),
+       PINMUX_IPSR_MSEL(IP14_10_8, SCL8_C, SEL_IIC8_2),
+       PINMUX_IPSR_MSEL(IP14_10_8, TX5_B, SEL_SCIF5_1),
+       PINMUX_IPSR_MSEL(IP14_10_8, SCIFA5_TXD_C, SEL_SCIFA5_2),
        PINMUX_IPSR_DATA(IP14_13_11, SD2_WP),
        PINMUX_IPSR_DATA(IP14_13_11, MMC_D5),
-       PINMUX_IPSR_MODSEL_DATA(IP14_13_11, SDA8_C, SEL_IIC8_2),
-       PINMUX_IPSR_MODSEL_DATA(IP14_13_11, RX5_B, SEL_SCIF5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP14_13_11, SCIFA5_RXD_C, SEL_SCIFA5_2),
-       PINMUX_IPSR_MODSEL_DATA(IP14_16_14, MSIOF0_SCK, SEL_SOF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_16_14, RX2_C, SEL_SCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP14_16_14, ADIDATA, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_16_14, VI1_CLK_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP14_13_11, SDA8_C, SEL_IIC8_2),
+       PINMUX_IPSR_MSEL(IP14_13_11, RX5_B, SEL_SCIF5_1),
+       PINMUX_IPSR_MSEL(IP14_13_11, SCIFA5_RXD_C, SEL_SCIFA5_2),
+       PINMUX_IPSR_MSEL(IP14_16_14, MSIOF0_SCK, SEL_SOF0_0),
+       PINMUX_IPSR_MSEL(IP14_16_14, RX2_C, SEL_SCIF2_2),
+       PINMUX_IPSR_MSEL(IP14_16_14, ADIDATA, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP14_16_14, VI1_CLK_C, SEL_VI1_2),
        PINMUX_IPSR_DATA(IP14_16_14, VI1_G0_B),
-       PINMUX_IPSR_MODSEL_DATA(IP14_19_17, MSIOF0_SYNC, SEL_SOF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_19_17, TX2_C, SEL_SCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP14_19_17, ADICS_SAMP, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_19_17, VI1_CLKENB_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP14_19_17, MSIOF0_SYNC, SEL_SOF0_0),
+       PINMUX_IPSR_MSEL(IP14_19_17, TX2_C, SEL_SCIF2_2),
+       PINMUX_IPSR_MSEL(IP14_19_17, ADICS_SAMP, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP14_19_17, VI1_CLKENB_C, SEL_VI1_2),
        PINMUX_IPSR_DATA(IP14_19_17, VI1_G1_B),
-       PINMUX_IPSR_MODSEL_DATA(IP14_22_20, MSIOF0_TXD, SEL_SOF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_22_20, ADICLK, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_22_20, VI1_FIELD_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP14_22_20, MSIOF0_TXD, SEL_SOF0_0),
+       PINMUX_IPSR_MSEL(IP14_22_20, ADICLK, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP14_22_20, VI1_FIELD_C, SEL_VI1_2),
        PINMUX_IPSR_DATA(IP14_22_20, VI1_G2_B),
-       PINMUX_IPSR_MODSEL_DATA(IP14_25_23, MSIOF0_RXD, SEL_SOF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_25_23, ADICHS0, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_25_23, VI1_DATA0_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP14_25_23, MSIOF0_RXD, SEL_SOF0_0),
+       PINMUX_IPSR_MSEL(IP14_25_23, ADICHS0, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP14_25_23, VI1_DATA0_C, SEL_VI1_2),
        PINMUX_IPSR_DATA(IP14_25_23, VI1_G3_B),
-       PINMUX_IPSR_MODSEL_DATA(IP14_28_26, MSIOF0_SS1, SEL_SOF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_28_26, MMC_D6, SEL_MMC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_28_26, ADICHS1, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_28_26, TX0_E, SEL_SCIF0_4),
-       PINMUX_IPSR_MODSEL_DATA(IP14_28_26, VI1_HSYNC_N_C, SEL_VI1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP14_28_26, SCL7_C, SEL_IIC7_2),
+       PINMUX_IPSR_MSEL(IP14_28_26, MSIOF0_SS1, SEL_SOF0_0),
+       PINMUX_IPSR_MSEL(IP14_28_26, MMC_D6, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP14_28_26, ADICHS1, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP14_28_26, TX0_E, SEL_SCIF0_4),
+       PINMUX_IPSR_MSEL(IP14_28_26, VI1_HSYNC_N_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP14_28_26, SCL7_C, SEL_IIC7_2),
        PINMUX_IPSR_DATA(IP14_28_26, VI1_G4_B),
-       PINMUX_IPSR_MODSEL_DATA(IP14_31_29, MSIOF0_SS2, SEL_SOF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_31_29, MMC_D7, SEL_MMC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_31_29, ADICHS2, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP14_31_29, RX0_E, SEL_SCIF0_4),
-       PINMUX_IPSR_MODSEL_DATA(IP14_31_29, VI1_VSYNC_N_C, SEL_VI1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP14_31_29, SDA7_C, SEL_IIC7_2),
+       PINMUX_IPSR_MSEL(IP14_31_29, MSIOF0_SS2, SEL_SOF0_0),
+       PINMUX_IPSR_MSEL(IP14_31_29, MMC_D7, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP14_31_29, ADICHS2, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP14_31_29, RX0_E, SEL_SCIF0_4),
+       PINMUX_IPSR_MSEL(IP14_31_29, VI1_VSYNC_N_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP14_31_29, SDA7_C, SEL_IIC7_2),
        PINMUX_IPSR_DATA(IP14_31_29, VI1_G5_B),
 
        /* IPSR15 */
-       PINMUX_IPSR_MODSEL_DATA(IP15_1_0, SIM0_RST, SEL_SIM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_1_0, IETX, SEL_IEB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_1_0, CAN1_TX_D, SEL_CAN1_3),
+       PINMUX_IPSR_MSEL(IP15_1_0, SIM0_RST, SEL_SIM_0),
+       PINMUX_IPSR_MSEL(IP15_1_0, IETX, SEL_IEB_0),
+       PINMUX_IPSR_MSEL(IP15_1_0, CAN1_TX_D, SEL_CAN1_3),
        PINMUX_IPSR_DATA(IP15_3_2, SIM0_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP15_3_2, IECLK, SEL_IEB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_3_2, CAN_CLK_C, SEL_CANCLK_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_5_4, SIM0_D, SEL_SIM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_5_4, IERX, SEL_IEB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_5_4, CAN1_RX_D, SEL_CAN1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP15_8_6, GPS_CLK, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_8_6, DU1_DOTCLKIN_C, SEL_DIS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_8_6, AUDIO_CLKB_B, SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP15_3_2, IECLK, SEL_IEB_0),
+       PINMUX_IPSR_MSEL(IP15_3_2, CAN_CLK_C, SEL_CANCLK_2),
+       PINMUX_IPSR_MSEL(IP15_5_4, SIM0_D, SEL_SIM_0),
+       PINMUX_IPSR_MSEL(IP15_5_4, IERX, SEL_IEB_0),
+       PINMUX_IPSR_MSEL(IP15_5_4, CAN1_RX_D, SEL_CAN1_3),
+       PINMUX_IPSR_MSEL(IP15_8_6, GPS_CLK, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP15_8_6, DU1_DOTCLKIN_C, SEL_DIS_2),
+       PINMUX_IPSR_MSEL(IP15_8_6, AUDIO_CLKB_B, SEL_ADG_1),
        PINMUX_IPSR_DATA(IP15_8_6, PWM5_B),
-       PINMUX_IPSR_MODSEL_DATA(IP15_8_6, SCIFA3_TXD_C, SEL_SCIFA3_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_11_9, GPS_SIGN, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_11_9, TX4_C, SEL_SCIF4_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_11_9, SCIFA4_TXD_C, SEL_SCIFA4_2),
+       PINMUX_IPSR_MSEL(IP15_8_6, SCIFA3_TXD_C, SEL_SCIFA3_2),
+       PINMUX_IPSR_MSEL(IP15_11_9, GPS_SIGN, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP15_11_9, TX4_C, SEL_SCIF4_2),
+       PINMUX_IPSR_MSEL(IP15_11_9, SCIFA4_TXD_C, SEL_SCIFA4_2),
        PINMUX_IPSR_DATA(IP15_11_9, PWM5),
        PINMUX_IPSR_DATA(IP15_11_9, VI1_G6_B),
-       PINMUX_IPSR_MODSEL_DATA(IP15_11_9, SCIFA3_RXD_C, SEL_SCIFA3_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_14_12, GPS_MAG, SEL_GPS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_14_12, RX4_C, SEL_SCIF4_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_14_12, SCIFA4_RXD_C, SEL_SCIFA4_2),
+       PINMUX_IPSR_MSEL(IP15_11_9, SCIFA3_RXD_C, SEL_SCIFA3_2),
+       PINMUX_IPSR_MSEL(IP15_14_12, GPS_MAG, SEL_GPS_0),
+       PINMUX_IPSR_MSEL(IP15_14_12, RX4_C, SEL_SCIF4_2),
+       PINMUX_IPSR_MSEL(IP15_14_12, SCIFA4_RXD_C, SEL_SCIFA4_2),
        PINMUX_IPSR_DATA(IP15_14_12, PWM6),
        PINMUX_IPSR_DATA(IP15_14_12, VI1_G7_B),
-       PINMUX_IPSR_MODSEL_DATA(IP15_14_12, SCIFA3_SCK_C, SEL_SCIFA3_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_17_15, HCTS0_N, SEL_HSCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_17_15, SCIFB0_CTS_N, SEL_SCIFB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_17_15, GLO_I0_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_17_15, TCLK1, SEL_TMU1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_17_15, VI1_DATA1_C, SEL_VI1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_20_18, HRTS0_N, SEL_HSCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_20_18, SCIFB0_RTS_N, SEL_SCIFB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_20_18, GLO_I1_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_20_18, VI1_DATA2_C, SEL_VI1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_23_21, HSCK0, SEL_HSCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_23_21, SCIFB0_SCK, SEL_SCIFB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_23_21, GLO_Q0_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_23_21, CAN_CLK, SEL_CANCLK_0),
+       PINMUX_IPSR_MSEL(IP15_14_12, SCIFA3_SCK_C, SEL_SCIFA3_2),
+       PINMUX_IPSR_MSEL(IP15_17_15, HCTS0_N, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP15_17_15, SCIFB0_CTS_N, SEL_SCIFB_0),
+       PINMUX_IPSR_MSEL(IP15_17_15, GLO_I0_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP15_17_15, TCLK1, SEL_TMU1_0),
+       PINMUX_IPSR_MSEL(IP15_17_15, VI1_DATA1_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP15_20_18, HRTS0_N, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP15_20_18, SCIFB0_RTS_N, SEL_SCIFB_0),
+       PINMUX_IPSR_MSEL(IP15_20_18, GLO_I1_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP15_20_18, VI1_DATA2_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP15_23_21, HSCK0, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP15_23_21, SCIFB0_SCK, SEL_SCIFB_0),
+       PINMUX_IPSR_MSEL(IP15_23_21, GLO_Q0_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP15_23_21, CAN_CLK, SEL_CANCLK_0),
        PINMUX_IPSR_DATA(IP15_23_21, TCLK2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_23_21, VI1_DATA3_C, SEL_VI1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_26_24, HRX0, SEL_HSCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_26_24, SCIFB0_RXD, SEL_SCIFB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_26_24, GLO_Q1_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_26_24, CAN0_RX_B, SEL_CAN0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP15_26_24, VI1_DATA4_C, SEL_VI1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_29_27, HTX0, SEL_HSCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_29_27, SCIFB0_TXD, SEL_SCIFB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP15_29_27, GLO_SCLK_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP15_29_27, CAN0_TX_B, SEL_CAN0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP15_29_27, VI1_DATA5_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP15_23_21, VI1_DATA3_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP15_26_24, HRX0, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP15_26_24, SCIFB0_RXD, SEL_SCIFB_0),
+       PINMUX_IPSR_MSEL(IP15_26_24, GLO_Q1_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP15_26_24, CAN0_RX_B, SEL_CAN0_1),
+       PINMUX_IPSR_MSEL(IP15_26_24, VI1_DATA4_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP15_29_27, HTX0, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP15_29_27, SCIFB0_TXD, SEL_SCIFB_0),
+       PINMUX_IPSR_MSEL(IP15_29_27, GLO_SCLK_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP15_29_27, CAN0_TX_B, SEL_CAN0_1),
+       PINMUX_IPSR_MSEL(IP15_29_27, VI1_DATA5_C, SEL_VI1_2),
 
        /* IPSR16 */
-       PINMUX_IPSR_MODSEL_DATA(IP16_2_0, HRX1, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP16_2_0, SCIFB1_RXD, SEL_SCIFB1_0),
+       PINMUX_IPSR_MSEL(IP16_2_0, HRX1, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP16_2_0, SCIFB1_RXD, SEL_SCIFB1_0),
        PINMUX_IPSR_DATA(IP16_2_0, VI1_R0_B),
-       PINMUX_IPSR_MODSEL_DATA(IP16_2_0, GLO_SDATA_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP16_2_0, VI1_DATA6_C, SEL_VI1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP16_5_3, HTX1, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP16_5_3, SCIFB1_TXD, SEL_SCIFB1_0),
+       PINMUX_IPSR_MSEL(IP16_2_0, GLO_SDATA_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP16_2_0, VI1_DATA6_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP16_5_3, HTX1, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP16_5_3, SCIFB1_TXD, SEL_SCIFB1_0),
        PINMUX_IPSR_DATA(IP16_5_3, VI1_R1_B),
-       PINMUX_IPSR_MODSEL_DATA(IP16_5_3, GLO_SS_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP16_5_3, VI1_DATA7_C, SEL_VI1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP16_7_6, HSCK1, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP16_7_6, SCIFB1_SCK, SEL_SCIFB1_0),
+       PINMUX_IPSR_MSEL(IP16_5_3, GLO_SS_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP16_5_3, VI1_DATA7_C, SEL_VI1_2),
+       PINMUX_IPSR_MSEL(IP16_7_6, HSCK1, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP16_7_6, SCIFB1_SCK, SEL_SCIFB1_0),
        PINMUX_IPSR_DATA(IP16_7_6, MLB_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP16_7_6, GLO_RFON_C, SEL_GPS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP16_9_8, HCTS1_N, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP16_7_6, GLO_RFON_C, SEL_GPS_2),
+       PINMUX_IPSR_MSEL(IP16_9_8, HCTS1_N, SEL_HSCIF1_0),
        PINMUX_IPSR_DATA(IP16_9_8, SCIFB1_CTS_N),
        PINMUX_IPSR_DATA(IP16_9_8, MLB_SIG),
-       PINMUX_IPSR_MODSEL_DATA(IP16_9_8, CAN1_TX_B, SEL_CAN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP16_11_10, HRTS1_N, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP16_9_8, CAN1_TX_B, SEL_CAN1_1),
+       PINMUX_IPSR_MSEL(IP16_11_10, HRTS1_N, SEL_HSCIF1_0),
        PINMUX_IPSR_DATA(IP16_11_10, SCIFB1_RTS_N),
        PINMUX_IPSR_DATA(IP16_11_10, MLB_DAT),
-       PINMUX_IPSR_MODSEL_DATA(IP16_11_10, CAN1_RX_B, SEL_CAN1_1),
+       PINMUX_IPSR_MSEL(IP16_11_10, CAN1_RX_B, SEL_CAN1_1),
 };
 
 static const struct sh_pfc_pin pinmux_pins[] = {
@@ -3986,24 +3985,6 @@ static const unsigned int usb1_mux[] = {
        USB1_PWEN_MARK,
        USB1_OVC_MARK,
 };
-
-union vin_data {
-       unsigned int data24[24];
-       unsigned int data20[20];
-       unsigned int data16[16];
-       unsigned int data12[12];
-       unsigned int data10[10];
-       unsigned int data8[8];
-};
-
-#define VIN_DATA_PIN_GROUP(n, s)                               \
-       {                                                       \
-               .name = #n#s,                                   \
-               .pins = n##_pins.data##s,                       \
-               .mux = n##_mux.data##s,                         \
-               .nr_pins = ARRAY_SIZE(n##_pins.data##s),        \
-       }
-
 /* - VIN0 ------------------------------------------------------------------- */
 static const union vin_data vin0_data_pins = {
        .data24 = {
@@ -6337,8 +6318,8 @@ const struct sh_pfc_soc_info r8a7791_pinmux_info = {
 
        .cfg_regs = pinmux_config_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
 #endif
 
@@ -6358,7 +6339,7 @@ const struct sh_pfc_soc_info r8a7793_pinmux_info = {
 
        .cfg_regs = pinmux_config_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
 #endif
index 5248685dbb4e55ef73c44aebd6c2c887a839a434..086f6798b1294564e425e0ab93c129fab16831ea 100644 (file)
@@ -11,7 +11,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/platform_data/gpio-rcar.h>
 
 #include "core.h"
 #include "sh_pfc.h"
@@ -644,10 +643,10 @@ static const u16 pinmux_data[] = {
 
        /* IPSR0 */
        PINMUX_IPSR_DATA(IP0_0, SD1_CD),
-       PINMUX_IPSR_MODSEL_DATA(IP0_0, CAN0_RX, SEL_CAN0_0),
+       PINMUX_IPSR_MSEL(IP0_0, CAN0_RX, SEL_CAN0_0),
        PINMUX_IPSR_DATA(IP0_9_8, SD1_WP),
        PINMUX_IPSR_DATA(IP0_9_8, IRQ7),
-       PINMUX_IPSR_MODSEL_DATA(IP0_9_8, CAN0_TX, SEL_CAN0_0),
+       PINMUX_IPSR_MSEL(IP0_9_8, CAN0_TX, SEL_CAN0_0),
        PINMUX_IPSR_DATA(IP0_10, MMC_CLK),
        PINMUX_IPSR_DATA(IP0_10, SD2_CLK),
        PINMUX_IPSR_DATA(IP0_11, MMC_CMD),
@@ -665,68 +664,68 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP0_17, MMC_D5),
        PINMUX_IPSR_DATA(IP0_17, SD2_WP),
        PINMUX_IPSR_DATA(IP0_19_18, MMC_D6),
-       PINMUX_IPSR_MODSEL_DATA(IP0_19_18, SCIF0_RXD, SEL_SCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_19_18, I2C2_SCL_B, SEL_I2C02_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_19_18, CAN1_RX, SEL_CAN1_0),
+       PINMUX_IPSR_MSEL(IP0_19_18, SCIF0_RXD, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP0_19_18, I2C2_SCL_B, SEL_I2C02_1),
+       PINMUX_IPSR_MSEL(IP0_19_18, CAN1_RX, SEL_CAN1_0),
        PINMUX_IPSR_DATA(IP0_21_20, MMC_D7),
-       PINMUX_IPSR_MODSEL_DATA(IP0_21_20, SCIF0_TXD, SEL_SCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_21_20, I2C2_SDA_B, SEL_I2C02_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_21_20, CAN1_TX, SEL_CAN1_0),
+       PINMUX_IPSR_MSEL(IP0_21_20, SCIF0_TXD, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP0_21_20, I2C2_SDA_B, SEL_I2C02_1),
+       PINMUX_IPSR_MSEL(IP0_21_20, CAN1_TX, SEL_CAN1_0),
        PINMUX_IPSR_DATA(IP0_23_22, D0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_23_22, SCIFA3_SCK_B, SEL_SCIFA3_1),
+       PINMUX_IPSR_MSEL(IP0_23_22, SCIFA3_SCK_B, SEL_SCIFA3_1),
        PINMUX_IPSR_DATA(IP0_23_22, IRQ4),
        PINMUX_IPSR_DATA(IP0_24, D1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_24, SCIFA3_RXD_B, SEL_SCIFA3_1),
+       PINMUX_IPSR_MSEL(IP0_24, SCIFA3_RXD_B, SEL_SCIFA3_1),
        PINMUX_IPSR_DATA(IP0_25, D2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_25, SCIFA3_TXD_B, SEL_SCIFA3_1),
+       PINMUX_IPSR_MSEL(IP0_25, SCIFA3_TXD_B, SEL_SCIFA3_1),
        PINMUX_IPSR_DATA(IP0_27_26, D3),
-       PINMUX_IPSR_MODSEL_DATA(IP0_27_26, I2C3_SCL_B, SEL_I2C03_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_27_26, SCIF5_RXD_B, SEL_SCIF5_1),
+       PINMUX_IPSR_MSEL(IP0_27_26, I2C3_SCL_B, SEL_I2C03_1),
+       PINMUX_IPSR_MSEL(IP0_27_26, SCIF5_RXD_B, SEL_SCIF5_1),
        PINMUX_IPSR_DATA(IP0_29_28, D4),
-       PINMUX_IPSR_MODSEL_DATA(IP0_29_28, I2C3_SDA_B, SEL_I2C03_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_29_28, SCIF5_TXD_B, SEL_SCIF5_1),
+       PINMUX_IPSR_MSEL(IP0_29_28, I2C3_SDA_B, SEL_I2C03_1),
+       PINMUX_IPSR_MSEL(IP0_29_28, SCIF5_TXD_B, SEL_SCIF5_1),
        PINMUX_IPSR_DATA(IP0_31_30, D5),
-       PINMUX_IPSR_MODSEL_DATA(IP0_31_30, SCIF4_RXD_B, SEL_SCIF4_1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_31_30, I2C0_SCL_D, SEL_I2C00_3),
+       PINMUX_IPSR_MSEL(IP0_31_30, SCIF4_RXD_B, SEL_SCIF4_1),
+       PINMUX_IPSR_MSEL(IP0_31_30, I2C0_SCL_D, SEL_I2C00_3),
 
        /* IPSR1 */
        PINMUX_IPSR_DATA(IP1_1_0, D6),
-       PINMUX_IPSR_MODSEL_DATA(IP1_1_0, SCIF4_TXD_B, SEL_SCIF4_1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_1_0, I2C0_SDA_D, SEL_I2C00_3),
+       PINMUX_IPSR_MSEL(IP1_1_0, SCIF4_TXD_B, SEL_SCIF4_1),
+       PINMUX_IPSR_MSEL(IP1_1_0, I2C0_SDA_D, SEL_I2C00_3),
        PINMUX_IPSR_DATA(IP1_3_2, D7),
        PINMUX_IPSR_DATA(IP1_3_2, IRQ3),
-       PINMUX_IPSR_MODSEL_DATA(IP1_3_2, TCLK1, SEL_TMU_0),
+       PINMUX_IPSR_MSEL(IP1_3_2, TCLK1, SEL_TMU_0),
        PINMUX_IPSR_DATA(IP1_3_2, PWM6_B),
        PINMUX_IPSR_DATA(IP1_5_4, D8),
        PINMUX_IPSR_DATA(IP1_5_4, HSCIF2_HRX),
-       PINMUX_IPSR_MODSEL_DATA(IP1_5_4, I2C1_SCL_B, SEL_I2C01_1),
+       PINMUX_IPSR_MSEL(IP1_5_4, I2C1_SCL_B, SEL_I2C01_1),
        PINMUX_IPSR_DATA(IP1_7_6, D9),
        PINMUX_IPSR_DATA(IP1_7_6, HSCIF2_HTX),
-       PINMUX_IPSR_MODSEL_DATA(IP1_7_6, I2C1_SDA_B, SEL_I2C01_1),
+       PINMUX_IPSR_MSEL(IP1_7_6, I2C1_SDA_B, SEL_I2C01_1),
        PINMUX_IPSR_DATA(IP1_10_8, D10),
        PINMUX_IPSR_DATA(IP1_10_8, HSCIF2_HSCK),
-       PINMUX_IPSR_MODSEL_DATA(IP1_10_8, SCIF1_SCK_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP1_10_8, SCIF1_SCK_C, SEL_SCIF1_2),
        PINMUX_IPSR_DATA(IP1_10_8, IRQ6),
        PINMUX_IPSR_DATA(IP1_10_8, PWM5_C),
        PINMUX_IPSR_DATA(IP1_12_11, D11),
        PINMUX_IPSR_DATA(IP1_12_11, HSCIF2_HCTS_N),
-       PINMUX_IPSR_MODSEL_DATA(IP1_12_11, SCIF1_RXD_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP1_12_11, I2C1_SCL_D, SEL_I2C01_3),
+       PINMUX_IPSR_MSEL(IP1_12_11, SCIF1_RXD_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP1_12_11, I2C1_SCL_D, SEL_I2C01_3),
        PINMUX_IPSR_DATA(IP1_14_13, D12),
        PINMUX_IPSR_DATA(IP1_14_13, HSCIF2_HRTS_N),
-       PINMUX_IPSR_MODSEL_DATA(IP1_14_13, SCIF1_TXD_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP1_14_13, I2C1_SDA_D, SEL_I2C01_3),
+       PINMUX_IPSR_MSEL(IP1_14_13, SCIF1_TXD_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP1_14_13, I2C1_SDA_D, SEL_I2C01_3),
        PINMUX_IPSR_DATA(IP1_17_15, D13),
-       PINMUX_IPSR_MODSEL_DATA(IP1_17_15, SCIFA1_SCK, SEL_SCIFA1_0),
+       PINMUX_IPSR_MSEL(IP1_17_15, SCIFA1_SCK, SEL_SCIFA1_0),
        PINMUX_IPSR_DATA(IP1_17_15, TANS1),
        PINMUX_IPSR_DATA(IP1_17_15, PWM2_C),
-       PINMUX_IPSR_MODSEL_DATA(IP1_17_15, TCLK2_B, SEL_TMU_1),
+       PINMUX_IPSR_MSEL(IP1_17_15, TCLK2_B, SEL_TMU_1),
        PINMUX_IPSR_DATA(IP1_19_18, D14),
-       PINMUX_IPSR_MODSEL_DATA(IP1_19_18, SCIFA1_RXD, SEL_SCIFA1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_19_18, IIC0_SCL_B, SEL_IIC00_1),
+       PINMUX_IPSR_MSEL(IP1_19_18, SCIFA1_RXD, SEL_SCIFA1_0),
+       PINMUX_IPSR_MSEL(IP1_19_18, IIC0_SCL_B, SEL_IIC00_1),
        PINMUX_IPSR_DATA(IP1_21_20, D15),
-       PINMUX_IPSR_MODSEL_DATA(IP1_21_20, SCIFA1_TXD, SEL_SCIFA1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_21_20, IIC0_SDA_B, SEL_IIC00_1),
+       PINMUX_IPSR_MSEL(IP1_21_20, SCIFA1_TXD, SEL_SCIFA1_0),
+       PINMUX_IPSR_MSEL(IP1_21_20, IIC0_SDA_B, SEL_IIC00_1),
        PINMUX_IPSR_DATA(IP1_23_22, A0),
        PINMUX_IPSR_DATA(IP1_23_22, SCIFB1_SCK),
        PINMUX_IPSR_DATA(IP1_23_22, PWM3_B),
@@ -742,58 +741,58 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP1_29_28, TPUTO3_C),
        PINMUX_IPSR_DATA(IP1_31_30, A6),
        PINMUX_IPSR_DATA(IP1_31_30, SCIFB0_CTS_N),
-       PINMUX_IPSR_MODSEL_DATA(IP1_31_30, SCIFA4_RXD_B, SEL_SCIFA4_1),
+       PINMUX_IPSR_MSEL(IP1_31_30, SCIFA4_RXD_B, SEL_SCIFA4_1),
        PINMUX_IPSR_DATA(IP1_31_30, TPUTO2_C),
 
        /* IPSR2 */
        PINMUX_IPSR_DATA(IP2_1_0, A7),
        PINMUX_IPSR_DATA(IP2_1_0, SCIFB0_RTS_N),
-       PINMUX_IPSR_MODSEL_DATA(IP2_1_0, SCIFA4_TXD_B, SEL_SCIFA4_1),
+       PINMUX_IPSR_MSEL(IP2_1_0, SCIFA4_TXD_B, SEL_SCIFA4_1),
        PINMUX_IPSR_DATA(IP2_3_2, A8),
-       PINMUX_IPSR_MODSEL_DATA(IP2_3_2, MSIOF1_RXD, SEL_MSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_3_2, SCIFA0_RXD_B, SEL_SCIFA0_1),
+       PINMUX_IPSR_MSEL(IP2_3_2, MSIOF1_RXD, SEL_MSI1_0),
+       PINMUX_IPSR_MSEL(IP2_3_2, SCIFA0_RXD_B, SEL_SCIFA0_1),
        PINMUX_IPSR_DATA(IP2_5_4, A9),
-       PINMUX_IPSR_MODSEL_DATA(IP2_5_4, MSIOF1_TXD, SEL_MSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_5_4, SCIFA0_TXD_B, SEL_SCIFA0_1),
+       PINMUX_IPSR_MSEL(IP2_5_4, MSIOF1_TXD, SEL_MSI1_0),
+       PINMUX_IPSR_MSEL(IP2_5_4, SCIFA0_TXD_B, SEL_SCIFA0_1),
        PINMUX_IPSR_DATA(IP2_7_6, A10),
-       PINMUX_IPSR_MODSEL_DATA(IP2_7_6, MSIOF1_SCK, SEL_MSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_7_6, IIC1_SCL_B, SEL_IIC01_1),
+       PINMUX_IPSR_MSEL(IP2_7_6, MSIOF1_SCK, SEL_MSI1_0),
+       PINMUX_IPSR_MSEL(IP2_7_6, IIC1_SCL_B, SEL_IIC01_1),
        PINMUX_IPSR_DATA(IP2_9_8, A11),
-       PINMUX_IPSR_MODSEL_DATA(IP2_9_8, MSIOF1_SYNC, SEL_MSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_9_8, IIC1_SDA_B, SEL_IIC01_1),
+       PINMUX_IPSR_MSEL(IP2_9_8, MSIOF1_SYNC, SEL_MSI1_0),
+       PINMUX_IPSR_MSEL(IP2_9_8, IIC1_SDA_B, SEL_IIC01_1),
        PINMUX_IPSR_DATA(IP2_11_10, A12),
-       PINMUX_IPSR_MODSEL_DATA(IP2_11_10, MSIOF1_SS1, SEL_MSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_11_10, SCIFA5_RXD_B, SEL_SCIFA5_1),
+       PINMUX_IPSR_MSEL(IP2_11_10, MSIOF1_SS1, SEL_MSI1_0),
+       PINMUX_IPSR_MSEL(IP2_11_10, SCIFA5_RXD_B, SEL_SCIFA5_1),
        PINMUX_IPSR_DATA(IP2_13_12, A13),
-       PINMUX_IPSR_MODSEL_DATA(IP2_13_12, MSIOF1_SS2, SEL_MSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_13_12, SCIFA5_TXD_B, SEL_SCIFA5_1),
+       PINMUX_IPSR_MSEL(IP2_13_12, MSIOF1_SS2, SEL_MSI1_0),
+       PINMUX_IPSR_MSEL(IP2_13_12, SCIFA5_TXD_B, SEL_SCIFA5_1),
        PINMUX_IPSR_DATA(IP2_15_14, A14),
-       PINMUX_IPSR_MODSEL_DATA(IP2_15_14, MSIOF2_RXD, SEL_MSI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_15_14, HSCIF0_HRX_B, SEL_HSCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_15_14, DREQ1_N, SEL_LBS_0),
+       PINMUX_IPSR_MSEL(IP2_15_14, MSIOF2_RXD, SEL_MSI2_0),
+       PINMUX_IPSR_MSEL(IP2_15_14, HSCIF0_HRX_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP2_15_14, DREQ1_N, SEL_LBS_0),
        PINMUX_IPSR_DATA(IP2_17_16, A15),
-       PINMUX_IPSR_MODSEL_DATA(IP2_17_16, MSIOF2_TXD, SEL_MSI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_17_16, HSCIF0_HTX_B, SEL_HSCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_17_16, DACK1, SEL_LBS_0),
+       PINMUX_IPSR_MSEL(IP2_17_16, MSIOF2_TXD, SEL_MSI2_0),
+       PINMUX_IPSR_MSEL(IP2_17_16, HSCIF0_HTX_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP2_17_16, DACK1, SEL_LBS_0),
        PINMUX_IPSR_DATA(IP2_20_18, A16),
-       PINMUX_IPSR_MODSEL_DATA(IP2_20_18, MSIOF2_SCK, SEL_MSI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_20_18, HSCIF0_HSCK_B, SEL_HSCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_20_18, SPEEDIN, SEL_RSP_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_20_18, VSP, SEL_SPDM_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_20_18, CAN_CLK_C, SEL_CAN_2),
+       PINMUX_IPSR_MSEL(IP2_20_18, MSIOF2_SCK, SEL_MSI2_0),
+       PINMUX_IPSR_MSEL(IP2_20_18, HSCIF0_HSCK_B, SEL_HSCIF0_1),
+       PINMUX_IPSR_MSEL(IP2_20_18, SPEEDIN, SEL_RSP_0),
+       PINMUX_IPSR_MSEL(IP2_20_18, VSP, SEL_SPDM_0),
+       PINMUX_IPSR_MSEL(IP2_20_18, CAN_CLK_C, SEL_CAN_2),
        PINMUX_IPSR_DATA(IP2_20_18, TPUTO2_B),
        PINMUX_IPSR_DATA(IP2_23_21, A17),
-       PINMUX_IPSR_MODSEL_DATA(IP2_23_21, MSIOF2_SYNC, SEL_MSI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_23_21, SCIF4_RXD_E, SEL_SCIF4_4),
-       PINMUX_IPSR_MODSEL_DATA(IP2_23_21, CAN1_RX_B, SEL_CAN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_23_21, AVB_AVTP_CAPTURE_B, SEL_AVB_1),
+       PINMUX_IPSR_MSEL(IP2_23_21, MSIOF2_SYNC, SEL_MSI2_0),
+       PINMUX_IPSR_MSEL(IP2_23_21, SCIF4_RXD_E, SEL_SCIF4_4),
+       PINMUX_IPSR_MSEL(IP2_23_21, CAN1_RX_B, SEL_CAN1_1),
+       PINMUX_IPSR_MSEL(IP2_23_21, AVB_AVTP_CAPTURE_B, SEL_AVB_1),
        PINMUX_IPSR_DATA(IP2_26_24, A18),
-       PINMUX_IPSR_MODSEL_DATA(IP2_26_24, MSIOF2_SS1, SEL_MSI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_26_24, SCIF4_TXD_E, SEL_SCIF4_4),
-       PINMUX_IPSR_MODSEL_DATA(IP2_26_24, CAN1_TX_B, SEL_CAN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_26_24, AVB_AVTP_MATCH_B, SEL_AVB_1),
+       PINMUX_IPSR_MSEL(IP2_26_24, MSIOF2_SS1, SEL_MSI2_0),
+       PINMUX_IPSR_MSEL(IP2_26_24, SCIF4_TXD_E, SEL_SCIF4_4),
+       PINMUX_IPSR_MSEL(IP2_26_24, CAN1_TX_B, SEL_CAN1_1),
+       PINMUX_IPSR_MSEL(IP2_26_24, AVB_AVTP_MATCH_B, SEL_AVB_1),
        PINMUX_IPSR_DATA(IP2_29_27, A19),
-       PINMUX_IPSR_MODSEL_DATA(IP2_29_27, MSIOF2_SS2, SEL_MSI2_0),
+       PINMUX_IPSR_MSEL(IP2_29_27, MSIOF2_SS2, SEL_MSI2_0),
        PINMUX_IPSR_DATA(IP2_29_27, PWM4),
        PINMUX_IPSR_DATA(IP2_29_27, TPUTO2),
        PINMUX_IPSR_DATA(IP2_29_27, MOUT0),
@@ -831,42 +830,42 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP3_14_13, VI1_DATA11),
        PINMUX_IPSR_DATA(IP3_17_15, EX_CS2_N),
        PINMUX_IPSR_DATA(IP3_17_15, PWM0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_17_15, SCIF4_RXD_C, SEL_SCIF4_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_17_15, TS_SDATA_B, SEL_TSIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_17_15, RIF0_SYNC, SEL_DR0_0),
+       PINMUX_IPSR_MSEL(IP3_17_15, SCIF4_RXD_C, SEL_SCIF4_2),
+       PINMUX_IPSR_MSEL(IP3_17_15, TS_SDATA_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP3_17_15, RIF0_SYNC, SEL_DR0_0),
        PINMUX_IPSR_DATA(IP3_17_15, TPUTO3),
        PINMUX_IPSR_DATA(IP3_17_15, SCIFB2_TXD),
-       PINMUX_IPSR_MODSEL_DATA(IP3_17_15, SDATA_B, SEL_FSN_1),
+       PINMUX_IPSR_MSEL(IP3_17_15, SDATA_B, SEL_FSN_1),
        PINMUX_IPSR_DATA(IP3_20_18, EX_CS3_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_20_18, SCIFA2_SCK, SEL_SCIFA2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_20_18, SCIF4_TXD_C, SEL_SCIF4_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_20_18, TS_SCK_B, SEL_TSIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_20_18, RIF0_CLK, SEL_DR0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_20_18, BPFCLK, SEL_DARC_0),
+       PINMUX_IPSR_MSEL(IP3_20_18, SCIFA2_SCK, SEL_SCIFA2_0),
+       PINMUX_IPSR_MSEL(IP3_20_18, SCIF4_TXD_C, SEL_SCIF4_2),
+       PINMUX_IPSR_MSEL(IP3_20_18, TS_SCK_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP3_20_18, RIF0_CLK, SEL_DR0_0),
+       PINMUX_IPSR_MSEL(IP3_20_18, BPFCLK, SEL_DARC_0),
        PINMUX_IPSR_DATA(IP3_20_18, SCIFB2_SCK),
-       PINMUX_IPSR_MODSEL_DATA(IP3_20_18, MDATA_B, SEL_FSN_1),
+       PINMUX_IPSR_MSEL(IP3_20_18, MDATA_B, SEL_FSN_1),
        PINMUX_IPSR_DATA(IP3_23_21, EX_CS4_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_23_21, SCIFA2_RXD, SEL_SCIFA2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_23_21, I2C2_SCL_E, SEL_I2C02_4),
-       PINMUX_IPSR_MODSEL_DATA(IP3_23_21, TS_SDEN_B, SEL_TSIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_23_21, RIF0_D0, SEL_DR0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_23_21, FMCLK, SEL_DARC_0),
+       PINMUX_IPSR_MSEL(IP3_23_21, SCIFA2_RXD, SEL_SCIFA2_0),
+       PINMUX_IPSR_MSEL(IP3_23_21, I2C2_SCL_E, SEL_I2C02_4),
+       PINMUX_IPSR_MSEL(IP3_23_21, TS_SDEN_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP3_23_21, RIF0_D0, SEL_DR0_0),
+       PINMUX_IPSR_MSEL(IP3_23_21, FMCLK, SEL_DARC_0),
        PINMUX_IPSR_DATA(IP3_23_21, SCIFB2_CTS_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_23_21, SCKZ_B, SEL_FSN_1),
+       PINMUX_IPSR_MSEL(IP3_23_21, SCKZ_B, SEL_FSN_1),
        PINMUX_IPSR_DATA(IP3_26_24, EX_CS5_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, SCIFA2_TXD, SEL_SCIFA2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, I2C2_SDA_E, SEL_I2C02_4),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, TS_SPSYNC_B, SEL_TSIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, RIF0_D1, SEL_DR1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, FMIN, SEL_DARC_0),
+       PINMUX_IPSR_MSEL(IP3_26_24, SCIFA2_TXD, SEL_SCIFA2_0),
+       PINMUX_IPSR_MSEL(IP3_26_24, I2C2_SDA_E, SEL_I2C02_4),
+       PINMUX_IPSR_MSEL(IP3_26_24, TS_SPSYNC_B, SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP3_26_24, RIF0_D1, SEL_DR1_0),
+       PINMUX_IPSR_MSEL(IP3_26_24, FMIN, SEL_DARC_0),
        PINMUX_IPSR_DATA(IP3_26_24, SCIFB2_RTS_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, STM_N_B, SEL_FSN_1),
+       PINMUX_IPSR_MSEL(IP3_26_24, STM_N_B, SEL_FSN_1),
        PINMUX_IPSR_DATA(IP3_29_27, BS_N),
        PINMUX_IPSR_DATA(IP3_29_27, DRACK0),
        PINMUX_IPSR_DATA(IP3_29_27, PWM1_C),
        PINMUX_IPSR_DATA(IP3_29_27, TPUTO0_C),
        PINMUX_IPSR_DATA(IP3_29_27, ATACS01_N),
-       PINMUX_IPSR_MODSEL_DATA(IP3_29_27, MTS_N_B, SEL_FSN_1),
+       PINMUX_IPSR_MSEL(IP3_29_27, MTS_N_B, SEL_FSN_1),
        PINMUX_IPSR_DATA(IP3_30, RD_N),
        PINMUX_IPSR_DATA(IP3_30, ATACS11_N),
        PINMUX_IPSR_DATA(IP3_31, RD_WR_N),
@@ -874,18 +873,18 @@ static const u16 pinmux_data[] = {
 
        /* IPSR4 */
        PINMUX_IPSR_DATA(IP4_1_0, EX_WAIT0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_1_0, CAN_CLK_B, SEL_CAN_1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_1_0, SCIF_CLK, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP4_1_0, CAN_CLK_B, SEL_CAN_1),
+       PINMUX_IPSR_MSEL(IP4_1_0, SCIF_CLK, SEL_SCIF0_0),
        PINMUX_IPSR_DATA(IP4_1_0, PWMFSW0),
        PINMUX_IPSR_DATA(IP4_4_2, DU0_DR0),
        PINMUX_IPSR_DATA(IP4_4_2, LCDOUT16),
-       PINMUX_IPSR_MODSEL_DATA(IP4_4_2, SCIF5_RXD_C, SEL_SCIF5_2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_4_2, I2C2_SCL_D, SEL_I2C02_3),
+       PINMUX_IPSR_MSEL(IP4_4_2, SCIF5_RXD_C, SEL_SCIF5_2),
+       PINMUX_IPSR_MSEL(IP4_4_2, I2C2_SCL_D, SEL_I2C02_3),
        PINMUX_IPSR_DATA(IP4_4_2, CC50_STATE0),
        PINMUX_IPSR_DATA(IP4_7_5, DU0_DR1),
        PINMUX_IPSR_DATA(IP4_7_5, LCDOUT17),
-       PINMUX_IPSR_MODSEL_DATA(IP4_7_5, SCIF5_TXD_C, SEL_SCIF5_2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_7_5, I2C2_SDA_D, SEL_I2C02_3),
+       PINMUX_IPSR_MSEL(IP4_7_5, SCIF5_TXD_C, SEL_SCIF5_2),
+       PINMUX_IPSR_MSEL(IP4_7_5, I2C2_SDA_D, SEL_I2C02_3),
        PINMUX_IPSR_DATA(IP4_9_8, CC50_STATE1),
        PINMUX_IPSR_DATA(IP4_9_8, DU0_DR2),
        PINMUX_IPSR_DATA(IP4_9_8, LCDOUT18),
@@ -907,13 +906,13 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP4_19_18, CC50_STATE7),
        PINMUX_IPSR_DATA(IP4_22_20, DU0_DG0),
        PINMUX_IPSR_DATA(IP4_22_20, LCDOUT8),
-       PINMUX_IPSR_MODSEL_DATA(IP4_22_20, SCIFA0_RXD_C, SEL_SCIFA0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_22_20, I2C3_SCL_D, SEL_I2C03_3),
+       PINMUX_IPSR_MSEL(IP4_22_20, SCIFA0_RXD_C, SEL_SCIFA0_2),
+       PINMUX_IPSR_MSEL(IP4_22_20, I2C3_SCL_D, SEL_I2C03_3),
        PINMUX_IPSR_DATA(IP4_22_20, CC50_STATE8),
        PINMUX_IPSR_DATA(IP4_25_23, DU0_DG1),
        PINMUX_IPSR_DATA(IP4_25_23, LCDOUT9),
-       PINMUX_IPSR_MODSEL_DATA(IP4_25_23, SCIFA0_TXD_C, SEL_SCIFA0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_25_23, I2C3_SDA_D, SEL_I2C03_3),
+       PINMUX_IPSR_MSEL(IP4_25_23, SCIFA0_TXD_C, SEL_SCIFA0_2),
+       PINMUX_IPSR_MSEL(IP4_25_23, I2C3_SDA_D, SEL_I2C03_3),
        PINMUX_IPSR_DATA(IP4_25_23, CC50_STATE9),
        PINMUX_IPSR_DATA(IP4_27_26, DU0_DG2),
        PINMUX_IPSR_DATA(IP4_27_26, LCDOUT10),
@@ -937,15 +936,15 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP5_5_4, CC50_STATE15),
        PINMUX_IPSR_DATA(IP5_8_6, DU0_DB0),
        PINMUX_IPSR_DATA(IP5_8_6, LCDOUT0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_8_6, SCIFA4_RXD_C, SEL_SCIFA4_2),
-       PINMUX_IPSR_MODSEL_DATA(IP5_8_6, I2C4_SCL_D, SEL_I2C04_3),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, CAN0_RX_C, SEL_CAN0_2),
+       PINMUX_IPSR_MSEL(IP5_8_6, SCIFA4_RXD_C, SEL_SCIFA4_2),
+       PINMUX_IPSR_MSEL(IP5_8_6, I2C4_SCL_D, SEL_I2C04_3),
+       PINMUX_IPSR_MSEL(IP7_8_6, CAN0_RX_C, SEL_CAN0_2),
        PINMUX_IPSR_DATA(IP5_8_6, CC50_STATE16),
        PINMUX_IPSR_DATA(IP5_11_9, DU0_DB1),
        PINMUX_IPSR_DATA(IP5_11_9, LCDOUT1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_11_9, SCIFA4_TXD_C, SEL_SCIFA4_2),
-       PINMUX_IPSR_MODSEL_DATA(IP5_11_9, I2C4_SDA_D, SEL_I2C04_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_11_9, CAN0_TX_C, SEL_CAN0_2),
+       PINMUX_IPSR_MSEL(IP5_11_9, SCIFA4_TXD_C, SEL_SCIFA4_2),
+       PINMUX_IPSR_MSEL(IP5_11_9, I2C4_SDA_D, SEL_I2C04_3),
+       PINMUX_IPSR_MSEL(IP5_11_9, CAN0_TX_C, SEL_CAN0_2),
        PINMUX_IPSR_DATA(IP5_11_9, CC50_STATE17),
        PINMUX_IPSR_DATA(IP5_13_12, DU0_DB2),
        PINMUX_IPSR_DATA(IP5_13_12, LCDOUT2),
@@ -1010,501 +1009,501 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP6_16, VI0_DATA7_VI0_B7),
        PINMUX_IPSR_DATA(IP6_16, AVB_RXD6),
        PINMUX_IPSR_DATA(IP6_19_17, VI0_CLKENB),
-       PINMUX_IPSR_MODSEL_DATA(IP6_19_17, I2C3_SCL, SEL_I2C03_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_19_17, SCIFA5_RXD_C, SEL_SCIFA5_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_19_17, IETX_C, SEL_IEB_2),
+       PINMUX_IPSR_MSEL(IP6_19_17, I2C3_SCL, SEL_I2C03_0),
+       PINMUX_IPSR_MSEL(IP6_19_17, SCIFA5_RXD_C, SEL_SCIFA5_2),
+       PINMUX_IPSR_MSEL(IP6_19_17, IETX_C, SEL_IEB_2),
        PINMUX_IPSR_DATA(IP6_19_17, AVB_RXD7),
        PINMUX_IPSR_DATA(IP6_22_20, VI0_FIELD),
-       PINMUX_IPSR_MODSEL_DATA(IP6_22_20, I2C3_SDA, SEL_I2C03_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_22_20, SCIFA5_TXD_C, SEL_SCIFA5_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_22_20, IECLK_C, SEL_IEB_2),
+       PINMUX_IPSR_MSEL(IP6_22_20, I2C3_SDA, SEL_I2C03_0),
+       PINMUX_IPSR_MSEL(IP6_22_20, SCIFA5_TXD_C, SEL_SCIFA5_2),
+       PINMUX_IPSR_MSEL(IP6_22_20, IECLK_C, SEL_IEB_2),
        PINMUX_IPSR_DATA(IP6_22_20, AVB_RX_ER),
        PINMUX_IPSR_DATA(IP6_25_23, VI0_HSYNC_N),
-       PINMUX_IPSR_MODSEL_DATA(IP6_25_23, SCIF0_RXD_B, SEL_SCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_25_23, I2C0_SCL_C, SEL_I2C00_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_25_23, IERX_C, SEL_IEB_2),
+       PINMUX_IPSR_MSEL(IP6_25_23, SCIF0_RXD_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP6_25_23, I2C0_SCL_C, SEL_I2C00_2),
+       PINMUX_IPSR_MSEL(IP6_25_23, IERX_C, SEL_IEB_2),
        PINMUX_IPSR_DATA(IP6_25_23, AVB_COL),
        PINMUX_IPSR_DATA(IP6_28_26, VI0_VSYNC_N),
-       PINMUX_IPSR_MODSEL_DATA(IP6_28_26, SCIF0_TXD_B, SEL_SCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_28_26, I2C0_SDA_C, SEL_I2C00_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_28_26, AUDIO_CLKOUT_B, SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP6_28_26, SCIF0_TXD_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP6_28_26, I2C0_SDA_C, SEL_I2C00_2),
+       PINMUX_IPSR_MSEL(IP6_28_26, AUDIO_CLKOUT_B, SEL_ADG_1),
        PINMUX_IPSR_DATA(IP6_28_26, AVB_TX_EN),
-       PINMUX_IPSR_MODSEL_DATA(IP6_31_29, ETH_MDIO, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP6_31_29, ETH_MDIO, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP6_31_29, VI0_G0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_31_29, MSIOF2_RXD_B, SEL_MSI2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_31_29, IIC0_SCL_D, SEL_IIC00_3),
+       PINMUX_IPSR_MSEL(IP6_31_29, MSIOF2_RXD_B, SEL_MSI2_1),
+       PINMUX_IPSR_MSEL(IP6_31_29, IIC0_SCL_D, SEL_IIC00_3),
        PINMUX_IPSR_DATA(IP6_31_29, AVB_TX_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP6_31_29, ADIDATA, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_31_29, AD_DI, SEL_ADI_0),
+       PINMUX_IPSR_MSEL(IP6_31_29, ADIDATA, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP6_31_29, AD_DI, SEL_ADI_0),
 
        /* IPSR7 */
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, ETH_CRS_DV, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP7_2_0, ETH_CRS_DV, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP7_2_0, VI0_G1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, MSIOF2_TXD_B, SEL_MSI2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, IIC0_SDA_D, SEL_IIC00_3),
+       PINMUX_IPSR_MSEL(IP7_2_0, MSIOF2_TXD_B, SEL_MSI2_1),
+       PINMUX_IPSR_MSEL(IP7_2_0, IIC0_SDA_D, SEL_IIC00_3),
        PINMUX_IPSR_DATA(IP7_2_0, AVB_TXD0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, ADICS_SAMP, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, AD_DO, SEL_ADI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, ETH_RX_ER, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP7_2_0, ADICS_SAMP, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP7_2_0, AD_DO, SEL_ADI_0),
+       PINMUX_IPSR_MSEL(IP7_5_3, ETH_RX_ER, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP7_5_3, VI0_G2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, MSIOF2_SCK_B, SEL_MSI2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, CAN0_RX_B, SEL_CAN0_1),
+       PINMUX_IPSR_MSEL(IP7_5_3, MSIOF2_SCK_B, SEL_MSI2_1),
+       PINMUX_IPSR_MSEL(IP7_5_3, CAN0_RX_B, SEL_CAN0_1),
        PINMUX_IPSR_DATA(IP7_5_3, AVB_TXD1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, ADICLK, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, AD_CLK, SEL_ADI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, ETH_RXD0, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP7_5_3, ADICLK, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP7_5_3, AD_CLK, SEL_ADI_0),
+       PINMUX_IPSR_MSEL(IP7_8_6, ETH_RXD0, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP7_8_6, VI0_G3),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, MSIOF2_SYNC_B, SEL_MSI2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, CAN0_TX_B, SEL_CAN0_1),
+       PINMUX_IPSR_MSEL(IP7_8_6, MSIOF2_SYNC_B, SEL_MSI2_1),
+       PINMUX_IPSR_MSEL(IP7_8_6, CAN0_TX_B, SEL_CAN0_1),
        PINMUX_IPSR_DATA(IP7_8_6, AVB_TXD2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, ADICHS0, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, AD_NCS_N, SEL_ADI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_11_9, ETH_RXD1, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP7_8_6, ADICHS0, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP7_8_6, AD_NCS_N, SEL_ADI_0),
+       PINMUX_IPSR_MSEL(IP7_11_9, ETH_RXD1, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP7_11_9, VI0_G4),
-       PINMUX_IPSR_MODSEL_DATA(IP7_11_9, MSIOF2_SS1_B, SEL_MSI2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_11_9, SCIF4_RXD_D, SEL_SCIF4_3),
+       PINMUX_IPSR_MSEL(IP7_11_9, MSIOF2_SS1_B, SEL_MSI2_1),
+       PINMUX_IPSR_MSEL(IP7_11_9, SCIF4_RXD_D, SEL_SCIF4_3),
        PINMUX_IPSR_DATA(IP7_11_9, AVB_TXD3),
-       PINMUX_IPSR_MODSEL_DATA(IP7_11_9, ADICHS1, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_14_12, ETH_LINK, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP7_11_9, ADICHS1, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP7_14_12, ETH_LINK, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP7_14_12, VI0_G5),
-       PINMUX_IPSR_MODSEL_DATA(IP7_14_12, MSIOF2_SS2_B, SEL_MSI2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_14_12, SCIF4_TXD_D, SEL_SCIF4_3),
+       PINMUX_IPSR_MSEL(IP7_14_12, MSIOF2_SS2_B, SEL_MSI2_1),
+       PINMUX_IPSR_MSEL(IP7_14_12, SCIF4_TXD_D, SEL_SCIF4_3),
        PINMUX_IPSR_DATA(IP7_14_12, AVB_TXD4),
-       PINMUX_IPSR_MODSEL_DATA(IP7_14_12, ADICHS2, SEL_RAD_0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_17_15, ETH_REFCLK, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP7_14_12, ADICHS2, SEL_RAD_0),
+       PINMUX_IPSR_MSEL(IP7_17_15, ETH_REFCLK, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP7_17_15, VI0_G6),
-       PINMUX_IPSR_MODSEL_DATA(IP7_17_15, SCIF2_SCK_C, SEL_SCIF2_2),
+       PINMUX_IPSR_MSEL(IP7_17_15, SCIF2_SCK_C, SEL_SCIF2_2),
        PINMUX_IPSR_DATA(IP7_17_15, AVB_TXD5),
-       PINMUX_IPSR_MODSEL_DATA(IP7_17_15, SSI_SCK5_B, SEL_SSI5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_20_18, ETH_TXD1, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP7_17_15, SSI_SCK5_B, SEL_SSI5_1),
+       PINMUX_IPSR_MSEL(IP7_20_18, ETH_TXD1, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP7_20_18, VI0_G7),
-       PINMUX_IPSR_MODSEL_DATA(IP7_20_18, SCIF2_RXD_C, SEL_SCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_20_18, IIC1_SCL_D, SEL_IIC01_3),
+       PINMUX_IPSR_MSEL(IP7_20_18, SCIF2_RXD_C, SEL_SCIF2_2),
+       PINMUX_IPSR_MSEL(IP7_20_18, IIC1_SCL_D, SEL_IIC01_3),
        PINMUX_IPSR_DATA(IP7_20_18, AVB_TXD6),
-       PINMUX_IPSR_MODSEL_DATA(IP7_20_18, SSI_WS5_B, SEL_SSI5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_23_21, ETH_TX_EN, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP7_20_18, SSI_WS5_B, SEL_SSI5_1),
+       PINMUX_IPSR_MSEL(IP7_23_21, ETH_TX_EN, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP7_23_21, VI0_R0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_23_21, SCIF2_TXD_C, SEL_SCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_23_21, IIC1_SDA_D, SEL_IIC01_3),
+       PINMUX_IPSR_MSEL(IP7_23_21, SCIF2_TXD_C, SEL_SCIF2_2),
+       PINMUX_IPSR_MSEL(IP7_23_21, IIC1_SDA_D, SEL_IIC01_3),
        PINMUX_IPSR_DATA(IP7_23_21, AVB_TXD7),
-       PINMUX_IPSR_MODSEL_DATA(IP7_23_21, SSI_SDATA5_B, SEL_SSI5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_26_24, ETH_MAGIC, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP7_23_21, SSI_SDATA5_B, SEL_SSI5_1),
+       PINMUX_IPSR_MSEL(IP7_26_24, ETH_MAGIC, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP7_26_24, VI0_R1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_26_24, SCIF3_SCK_B, SEL_SCIF3_1),
+       PINMUX_IPSR_MSEL(IP7_26_24, SCIF3_SCK_B, SEL_SCIF3_1),
        PINMUX_IPSR_DATA(IP7_26_24, AVB_TX_ER),
-       PINMUX_IPSR_MODSEL_DATA(IP7_26_24, SSI_SCK6_B, SEL_SSI6_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_29_27, ETH_TXD0, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP7_26_24, SSI_SCK6_B, SEL_SSI6_1),
+       PINMUX_IPSR_MSEL(IP7_29_27, ETH_TXD0, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP7_29_27, VI0_R2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_29_27, SCIF3_RXD_B, SEL_SCIF3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_29_27, I2C4_SCL_E, SEL_I2C04_4),
+       PINMUX_IPSR_MSEL(IP7_29_27, SCIF3_RXD_B, SEL_SCIF3_1),
+       PINMUX_IPSR_MSEL(IP7_29_27, I2C4_SCL_E, SEL_I2C04_4),
        PINMUX_IPSR_DATA(IP7_29_27, AVB_GTX_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP7_29_27, SSI_WS6_B, SEL_SSI6_1),
+       PINMUX_IPSR_MSEL(IP7_29_27, SSI_WS6_B, SEL_SSI6_1),
        PINMUX_IPSR_DATA(IP7_31, DREQ0_N),
        PINMUX_IPSR_DATA(IP7_31, SCIFB1_RXD),
 
        /* IPSR8 */
-       PINMUX_IPSR_MODSEL_DATA(IP8_2_0, ETH_MDC, SEL_ETH_0),
+       PINMUX_IPSR_MSEL(IP8_2_0, ETH_MDC, SEL_ETH_0),
        PINMUX_IPSR_DATA(IP8_2_0, VI0_R3),
-       PINMUX_IPSR_MODSEL_DATA(IP8_2_0, SCIF3_TXD_B, SEL_SCIF3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_2_0, I2C4_SDA_E, SEL_I2C04_4),
+       PINMUX_IPSR_MSEL(IP8_2_0, SCIF3_TXD_B, SEL_SCIF3_1),
+       PINMUX_IPSR_MSEL(IP8_2_0, I2C4_SDA_E, SEL_I2C04_4),
        PINMUX_IPSR_DATA(IP8_2_0, AVB_MDC),
-       PINMUX_IPSR_MODSEL_DATA(IP8_2_0, SSI_SDATA6_B, SEL_SSI6_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_5_3, HSCIF0_HRX, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP8_2_0, SSI_SDATA6_B, SEL_SSI6_1),
+       PINMUX_IPSR_MSEL(IP8_5_3, HSCIF0_HRX, SEL_HSCIF0_0),
        PINMUX_IPSR_DATA(IP8_5_3, VI0_R4),
-       PINMUX_IPSR_MODSEL_DATA(IP8_5_3, I2C1_SCL_C, SEL_I2C01_2),
-       PINMUX_IPSR_MODSEL_DATA(IP8_5_3, AUDIO_CLKA_B, SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP8_5_3, I2C1_SCL_C, SEL_I2C01_2),
+       PINMUX_IPSR_MSEL(IP8_5_3, AUDIO_CLKA_B, SEL_ADG_1),
        PINMUX_IPSR_DATA(IP8_5_3, AVB_MDIO),
-       PINMUX_IPSR_MODSEL_DATA(IP8_5_3, SSI_SCK78_B, SEL_SSI7_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_8_6, HSCIF0_HTX, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP8_5_3, SSI_SCK78_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP8_8_6, HSCIF0_HTX, SEL_HSCIF0_0),
        PINMUX_IPSR_DATA(IP8_8_6, VI0_R5),
-       PINMUX_IPSR_MODSEL_DATA(IP8_8_6, I2C1_SDA_C, SEL_I2C01_2),
-       PINMUX_IPSR_MODSEL_DATA(IP8_8_6, AUDIO_CLKB_B, SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP8_8_6, I2C1_SDA_C, SEL_I2C01_2),
+       PINMUX_IPSR_MSEL(IP8_8_6, AUDIO_CLKB_B, SEL_ADG_1),
        PINMUX_IPSR_DATA(IP8_5_3, AVB_LINK),
-       PINMUX_IPSR_MODSEL_DATA(IP8_8_6, SSI_WS78_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP8_8_6, SSI_WS78_B, SEL_SSI7_1),
        PINMUX_IPSR_DATA(IP8_11_9, HSCIF0_HCTS_N),
        PINMUX_IPSR_DATA(IP8_11_9, VI0_R6),
-       PINMUX_IPSR_MODSEL_DATA(IP8_11_9, SCIF0_RXD_D, SEL_SCIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP8_11_9, I2C0_SCL_E, SEL_I2C00_4),
+       PINMUX_IPSR_MSEL(IP8_11_9, SCIF0_RXD_D, SEL_SCIF0_3),
+       PINMUX_IPSR_MSEL(IP8_11_9, I2C0_SCL_E, SEL_I2C00_4),
        PINMUX_IPSR_DATA(IP8_11_9, AVB_MAGIC),
-       PINMUX_IPSR_MODSEL_DATA(IP8_11_9, SSI_SDATA7_B, SEL_SSI7_1),
+       PINMUX_IPSR_MSEL(IP8_11_9, SSI_SDATA7_B, SEL_SSI7_1),
        PINMUX_IPSR_DATA(IP8_14_12, HSCIF0_HRTS_N),
        PINMUX_IPSR_DATA(IP8_14_12, VI0_R7),
-       PINMUX_IPSR_MODSEL_DATA(IP8_14_12, SCIF0_TXD_D, SEL_SCIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP8_14_12, I2C0_SDA_E, SEL_I2C00_4),
+       PINMUX_IPSR_MSEL(IP8_14_12, SCIF0_TXD_D, SEL_SCIF0_3),
+       PINMUX_IPSR_MSEL(IP8_14_12, I2C0_SDA_E, SEL_I2C00_4),
        PINMUX_IPSR_DATA(IP8_14_12, AVB_PHY_INT),
-       PINMUX_IPSR_MODSEL_DATA(IP8_14_12, SSI_SDATA8_B, SEL_SSI8_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_16_15, HSCIF0_HSCK, SEL_HSCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_16_15, SCIF_CLK_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP8_14_12, SSI_SDATA8_B, SEL_SSI8_1),
+       PINMUX_IPSR_MSEL(IP8_16_15, HSCIF0_HSCK, SEL_HSCIF0_0),
+       PINMUX_IPSR_MSEL(IP8_16_15, SCIF_CLK_B, SEL_SCIF0_1),
        PINMUX_IPSR_DATA(IP8_16_15, AVB_CRS),
-       PINMUX_IPSR_MODSEL_DATA(IP8_16_15, AUDIO_CLKC_B, SEL_ADG_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_19_17, I2C0_SCL, SEL_I2C00_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_19_17, SCIF0_RXD_C, SEL_SCIF0_2),
+       PINMUX_IPSR_MSEL(IP8_16_15, AUDIO_CLKC_B, SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP8_19_17, I2C0_SCL, SEL_I2C00_0),
+       PINMUX_IPSR_MSEL(IP8_19_17, SCIF0_RXD_C, SEL_SCIF0_2),
        PINMUX_IPSR_DATA(IP8_19_17, PWM5),
-       PINMUX_IPSR_MODSEL_DATA(IP8_19_17, TCLK1_B, SEL_TMU_1),
+       PINMUX_IPSR_MSEL(IP8_19_17, TCLK1_B, SEL_TMU_1),
        PINMUX_IPSR_DATA(IP8_19_17, AVB_GTXREFCLK),
-       PINMUX_IPSR_MODSEL_DATA(IP8_19_17, CAN1_RX_D, SEL_CAN1_3),
+       PINMUX_IPSR_MSEL(IP8_19_17, CAN1_RX_D, SEL_CAN1_3),
        PINMUX_IPSR_DATA(IP8_19_17, TPUTO0_B),
-       PINMUX_IPSR_MODSEL_DATA(IP8_22_20, I2C0_SDA, SEL_I2C00_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_22_20, SCIF0_TXD_C, SEL_SCIF0_2),
+       PINMUX_IPSR_MSEL(IP8_22_20, I2C0_SDA, SEL_I2C00_0),
+       PINMUX_IPSR_MSEL(IP8_22_20, SCIF0_TXD_C, SEL_SCIF0_2),
        PINMUX_IPSR_DATA(IP8_22_20, TPUTO0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_22_20, CAN_CLK, SEL_CAN_0),
+       PINMUX_IPSR_MSEL(IP8_22_20, CAN_CLK, SEL_CAN_0),
        PINMUX_IPSR_DATA(IP8_22_20, DVC_MUTE),
-       PINMUX_IPSR_MODSEL_DATA(IP8_22_20, CAN1_TX_D, SEL_CAN1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP8_25_23, I2C1_SCL, SEL_I2C01_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_25_23, SCIF4_RXD, SEL_SCIF4_0),
+       PINMUX_IPSR_MSEL(IP8_22_20, CAN1_TX_D, SEL_CAN1_3),
+       PINMUX_IPSR_MSEL(IP8_25_23, I2C1_SCL, SEL_I2C01_0),
+       PINMUX_IPSR_MSEL(IP8_25_23, SCIF4_RXD, SEL_SCIF4_0),
        PINMUX_IPSR_DATA(IP8_25_23, PWM5_B),
        PINMUX_IPSR_DATA(IP8_25_23, DU1_DR0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_25_23, RIF1_SYNC_B, SEL_DR2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_25_23, TS_SDATA_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP8_25_23, RIF1_SYNC_B, SEL_DR2_1),
+       PINMUX_IPSR_MSEL(IP8_25_23, TS_SDATA_D, SEL_TSIF0_3),
        PINMUX_IPSR_DATA(IP8_25_23, TPUTO1_B),
-       PINMUX_IPSR_MODSEL_DATA(IP8_28_26, I2C1_SDA, SEL_I2C01_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_28_26, SCIF4_TXD, SEL_SCIF4_0),
+       PINMUX_IPSR_MSEL(IP8_28_26, I2C1_SDA, SEL_I2C01_0),
+       PINMUX_IPSR_MSEL(IP8_28_26, SCIF4_TXD, SEL_SCIF4_0),
        PINMUX_IPSR_DATA(IP8_28_26, IRQ5),
        PINMUX_IPSR_DATA(IP8_28_26, DU1_DR1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_28_26, RIF1_CLK_B, SEL_DR2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_28_26, TS_SCK_D, SEL_TSIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP8_28_26, BPFCLK_C, SEL_DARC_2),
+       PINMUX_IPSR_MSEL(IP8_28_26, RIF1_CLK_B, SEL_DR2_1),
+       PINMUX_IPSR_MSEL(IP8_28_26, TS_SCK_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP8_28_26, BPFCLK_C, SEL_DARC_2),
        PINMUX_IPSR_DATA(IP8_31_29, MSIOF0_RXD),
-       PINMUX_IPSR_MODSEL_DATA(IP8_31_29, SCIF5_RXD, SEL_SCIF5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_31_29, I2C2_SCL_C, SEL_I2C02_2),
+       PINMUX_IPSR_MSEL(IP8_31_29, SCIF5_RXD, SEL_SCIF5_0),
+       PINMUX_IPSR_MSEL(IP8_31_29, I2C2_SCL_C, SEL_I2C02_2),
        PINMUX_IPSR_DATA(IP8_31_29, DU1_DR2),
-       PINMUX_IPSR_MODSEL_DATA(IP8_31_29, RIF1_D0_B, SEL_DR2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_31_29, TS_SDEN_D, SEL_TSIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP8_31_29, FMCLK_C, SEL_DARC_2),
-       PINMUX_IPSR_MODSEL_DATA(IP8_31_29, RDS_CLK, SEL_RDS_0),
+       PINMUX_IPSR_MSEL(IP8_31_29, RIF1_D0_B, SEL_DR2_1),
+       PINMUX_IPSR_MSEL(IP8_31_29, TS_SDEN_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP8_31_29, FMCLK_C, SEL_DARC_2),
+       PINMUX_IPSR_MSEL(IP8_31_29, RDS_CLK, SEL_RDS_0),
 
        /* IPSR9 */
        PINMUX_IPSR_DATA(IP9_2_0, MSIOF0_TXD),
-       PINMUX_IPSR_MODSEL_DATA(IP9_2_0, SCIF5_TXD, SEL_SCIF5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_2_0, I2C2_SDA_C, SEL_I2C02_2),
+       PINMUX_IPSR_MSEL(IP9_2_0, SCIF5_TXD, SEL_SCIF5_0),
+       PINMUX_IPSR_MSEL(IP9_2_0, I2C2_SDA_C, SEL_I2C02_2),
        PINMUX_IPSR_DATA(IP9_2_0, DU1_DR3),
-       PINMUX_IPSR_MODSEL_DATA(IP9_2_0, RIF1_D1_B, SEL_DR3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_2_0, TS_SPSYNC_D, SEL_TSIF0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP9_2_0, FMIN_C, SEL_DARC_2),
-       PINMUX_IPSR_MODSEL_DATA(IP9_2_0, RDS_DATA, SEL_RDS_0),
+       PINMUX_IPSR_MSEL(IP9_2_0, RIF1_D1_B, SEL_DR3_1),
+       PINMUX_IPSR_MSEL(IP9_2_0, TS_SPSYNC_D, SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP9_2_0, FMIN_C, SEL_DARC_2),
+       PINMUX_IPSR_MSEL(IP9_2_0, RDS_DATA, SEL_RDS_0),
        PINMUX_IPSR_DATA(IP9_5_3, MSIOF0_SCK),
        PINMUX_IPSR_DATA(IP9_5_3, IRQ0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_5_3, TS_SDATA, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP9_5_3, TS_SDATA, SEL_TSIF0_0),
        PINMUX_IPSR_DATA(IP9_5_3, DU1_DR4),
-       PINMUX_IPSR_MODSEL_DATA(IP9_5_3, RIF1_SYNC, SEL_DR2_0),
+       PINMUX_IPSR_MSEL(IP9_5_3, RIF1_SYNC, SEL_DR2_0),
        PINMUX_IPSR_DATA(IP9_5_3, TPUTO1_C),
        PINMUX_IPSR_DATA(IP9_8_6, MSIOF0_SYNC),
        PINMUX_IPSR_DATA(IP9_8_6, PWM1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_8_6, TS_SCK, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP9_8_6, TS_SCK, SEL_TSIF0_0),
        PINMUX_IPSR_DATA(IP9_8_6, DU1_DR5),
-       PINMUX_IPSR_MODSEL_DATA(IP9_8_6, RIF1_CLK, SEL_DR2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_8_6, BPFCLK_B, SEL_DARC_1),
+       PINMUX_IPSR_MSEL(IP9_8_6, RIF1_CLK, SEL_DR2_0),
+       PINMUX_IPSR_MSEL(IP9_8_6, BPFCLK_B, SEL_DARC_1),
        PINMUX_IPSR_DATA(IP9_11_9, MSIOF0_SS1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_9, SCIFA0_RXD, SEL_SCIFA0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_9, TS_SDEN, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP9_11_9, SCIFA0_RXD, SEL_SCIFA0_0),
+       PINMUX_IPSR_MSEL(IP9_11_9, TS_SDEN, SEL_TSIF0_0),
        PINMUX_IPSR_DATA(IP9_11_9, DU1_DR6),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_9, RIF1_D0, SEL_DR2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_9, FMCLK_B, SEL_DARC_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_9, RDS_CLK_B, SEL_RDS_1),
+       PINMUX_IPSR_MSEL(IP9_11_9, RIF1_D0, SEL_DR2_0),
+       PINMUX_IPSR_MSEL(IP9_11_9, FMCLK_B, SEL_DARC_1),
+       PINMUX_IPSR_MSEL(IP9_11_9, RDS_CLK_B, SEL_RDS_1),
        PINMUX_IPSR_DATA(IP9_14_12, MSIOF0_SS2),
-       PINMUX_IPSR_MODSEL_DATA(IP9_14_12, SCIFA0_TXD, SEL_SCIFA0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_14_12, TS_SPSYNC, SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP9_14_12, SCIFA0_TXD, SEL_SCIFA0_0),
+       PINMUX_IPSR_MSEL(IP9_14_12, TS_SPSYNC, SEL_TSIF0_0),
        PINMUX_IPSR_DATA(IP9_14_12, DU1_DR7),
-       PINMUX_IPSR_MODSEL_DATA(IP9_14_12, RIF1_D1, SEL_DR3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_14_12, FMIN_B, SEL_DARC_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_14_12, RDS_DATA_B, SEL_RDS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_16_15, HSCIF1_HRX, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_16_15, I2C4_SCL, SEL_I2C04_0),
+       PINMUX_IPSR_MSEL(IP9_14_12, RIF1_D1, SEL_DR3_0),
+       PINMUX_IPSR_MSEL(IP9_14_12, FMIN_B, SEL_DARC_1),
+       PINMUX_IPSR_MSEL(IP9_14_12, RDS_DATA_B, SEL_RDS_1),
+       PINMUX_IPSR_MSEL(IP9_16_15, HSCIF1_HRX, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP9_16_15, I2C4_SCL, SEL_I2C04_0),
        PINMUX_IPSR_DATA(IP9_16_15, PWM6),
        PINMUX_IPSR_DATA(IP9_16_15, DU1_DG0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_18_17, HSCIF1_HTX, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_18_17, I2C4_SDA, SEL_I2C04_0),
+       PINMUX_IPSR_MSEL(IP9_18_17, HSCIF1_HTX, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP9_18_17, I2C4_SDA, SEL_I2C04_0),
        PINMUX_IPSR_DATA(IP9_18_17, TPUTO1),
        PINMUX_IPSR_DATA(IP9_18_17, DU1_DG1),
        PINMUX_IPSR_DATA(IP9_21_19, HSCIF1_HSCK),
        PINMUX_IPSR_DATA(IP9_21_19, PWM2),
-       PINMUX_IPSR_MODSEL_DATA(IP9_21_19, IETX, SEL_IEB_0),
+       PINMUX_IPSR_MSEL(IP9_21_19, IETX, SEL_IEB_0),
        PINMUX_IPSR_DATA(IP9_21_19, DU1_DG2),
-       PINMUX_IPSR_MODSEL_DATA(IP9_21_19, REMOCON_B, SEL_RCN_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_21_19, SPEEDIN_B, SEL_RSP_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_21_19, VSP_B, SEL_SPDM_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_24_22, HSCIF1_HCTS_N, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_24_22, SCIFA4_RXD, SEL_SCIFA4_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_24_22, IECLK, SEL_IEB_0),
+       PINMUX_IPSR_MSEL(IP9_21_19, REMOCON_B, SEL_RCN_1),
+       PINMUX_IPSR_MSEL(IP9_21_19, SPEEDIN_B, SEL_RSP_1),
+       PINMUX_IPSR_MSEL(IP9_21_19, VSP_B, SEL_SPDM_1),
+       PINMUX_IPSR_MSEL(IP9_24_22, HSCIF1_HCTS_N, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP9_24_22, SCIFA4_RXD, SEL_SCIFA4_0),
+       PINMUX_IPSR_MSEL(IP9_24_22, IECLK, SEL_IEB_0),
        PINMUX_IPSR_DATA(IP9_24_22, DU1_DG3),
-       PINMUX_IPSR_MODSEL_DATA(IP9_24_22, SSI_SCK1_B, SEL_SSI1_1),
+       PINMUX_IPSR_MSEL(IP9_24_22, SSI_SCK1_B, SEL_SSI1_1),
        PINMUX_IPSR_DATA(IP9_24_22, CAN_DEBUG_HW_TRIGGER),
        PINMUX_IPSR_DATA(IP9_24_22, CC50_STATE32),
-       PINMUX_IPSR_MODSEL_DATA(IP9_27_25, HSCIF1_HRTS_N, SEL_HSCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_27_25, SCIFA4_TXD, SEL_SCIFA4_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_27_25, IERX, SEL_IEB_0),
+       PINMUX_IPSR_MSEL(IP9_27_25, HSCIF1_HRTS_N, SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP9_27_25, SCIFA4_TXD, SEL_SCIFA4_0),
+       PINMUX_IPSR_MSEL(IP9_27_25, IERX, SEL_IEB_0),
        PINMUX_IPSR_DATA(IP9_27_25, DU1_DG4),
-       PINMUX_IPSR_MODSEL_DATA(IP9_27_25, SSI_WS1_B, SEL_SSI1_1),
+       PINMUX_IPSR_MSEL(IP9_27_25, SSI_WS1_B, SEL_SSI1_1),
        PINMUX_IPSR_DATA(IP9_27_25, CAN_STEP0),
        PINMUX_IPSR_DATA(IP9_27_25, CC50_STATE33),
-       PINMUX_IPSR_MODSEL_DATA(IP9_30_28, SCIF1_SCK, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP9_30_28, SCIF1_SCK, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP9_30_28, PWM3),
-       PINMUX_IPSR_MODSEL_DATA(IP9_30_28, TCLK2, SEL_TMU_0),
+       PINMUX_IPSR_MSEL(IP9_30_28, TCLK2, SEL_TMU_0),
        PINMUX_IPSR_DATA(IP9_30_28, DU1_DG5),
-       PINMUX_IPSR_MODSEL_DATA(IP9_30_28, SSI_SDATA1_B, SEL_SSI1_1),
+       PINMUX_IPSR_MSEL(IP9_30_28, SSI_SDATA1_B, SEL_SSI1_1),
        PINMUX_IPSR_DATA(IP9_30_28, CAN_TXCLK),
        PINMUX_IPSR_DATA(IP9_30_28, CC50_STATE34),
 
        /* IPSR10 */
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, SCIF1_RXD, SEL_SCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, IIC0_SCL, SEL_IIC00_0),
+       PINMUX_IPSR_MSEL(IP10_2_0, SCIF1_RXD, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP10_2_0, IIC0_SCL, SEL_IIC00_0),
        PINMUX_IPSR_DATA(IP10_2_0, DU1_DG6),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, SSI_SCK2_B, SEL_SSI2_1),
+       PINMUX_IPSR_MSEL(IP10_2_0, SSI_SCK2_B, SEL_SSI2_1),
        PINMUX_IPSR_DATA(IP10_2_0, CAN_DEBUGOUT0),
        PINMUX_IPSR_DATA(IP10_2_0, CC50_STATE35),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, SCIF1_TXD, SEL_SCIF1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, IIC0_SDA, SEL_IIC00_0),
+       PINMUX_IPSR_MSEL(IP10_5_3, SCIF1_TXD, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP10_5_3, IIC0_SDA, SEL_IIC00_0),
        PINMUX_IPSR_DATA(IP10_5_3, DU1_DG7),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, SSI_WS2_B, SEL_SSI2_1),
+       PINMUX_IPSR_MSEL(IP10_5_3, SSI_WS2_B, SEL_SSI2_1),
        PINMUX_IPSR_DATA(IP10_5_3, CAN_DEBUGOUT1),
        PINMUX_IPSR_DATA(IP10_5_3, CC50_STATE36),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, SCIF2_RXD, SEL_SCIF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, IIC1_SCL, SEL_IIC01_0),
+       PINMUX_IPSR_MSEL(IP10_8_6, SCIF2_RXD, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP10_8_6, IIC1_SCL, SEL_IIC01_0),
        PINMUX_IPSR_DATA(IP10_8_6, DU1_DB0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, SSI_SDATA2_B, SEL_SSI2_1),
+       PINMUX_IPSR_MSEL(IP10_8_6, SSI_SDATA2_B, SEL_SSI2_1),
        PINMUX_IPSR_DATA(IP10_8_6, USB0_EXTLP),
        PINMUX_IPSR_DATA(IP10_8_6, CAN_DEBUGOUT2),
        PINMUX_IPSR_DATA(IP10_8_6, CC50_STATE37),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, SCIF2_TXD, SEL_SCIF2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, IIC1_SDA, SEL_IIC01_0),
+       PINMUX_IPSR_MSEL(IP10_11_9, SCIF2_TXD, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP10_11_9, IIC1_SDA, SEL_IIC01_0),
        PINMUX_IPSR_DATA(IP10_11_9, DU1_DB1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, SSI_SCK9_B, SEL_SSI9_1),
+       PINMUX_IPSR_MSEL(IP10_11_9, SSI_SCK9_B, SEL_SSI9_1),
        PINMUX_IPSR_DATA(IP10_11_9, USB0_OVC1),
        PINMUX_IPSR_DATA(IP10_11_9, CAN_DEBUGOUT3),
        PINMUX_IPSR_DATA(IP10_11_9, CC50_STATE38),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, SCIF2_SCK, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP10_14_12, SCIF2_SCK, SEL_SCIF2_0),
        PINMUX_IPSR_DATA(IP10_14_12, IRQ1),
        PINMUX_IPSR_DATA(IP10_14_12, DU1_DB2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, SSI_WS9_B, SEL_SSI9_1),
+       PINMUX_IPSR_MSEL(IP10_14_12, SSI_WS9_B, SEL_SSI9_1),
        PINMUX_IPSR_DATA(IP10_14_12, USB0_IDIN),
        PINMUX_IPSR_DATA(IP10_14_12, CAN_DEBUGOUT4),
        PINMUX_IPSR_DATA(IP10_14_12, CC50_STATE39),
-       PINMUX_IPSR_MODSEL_DATA(IP10_17_15, SCIF3_SCK, SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP10_17_15, SCIF3_SCK, SEL_SCIF3_0),
        PINMUX_IPSR_DATA(IP10_17_15, IRQ2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_17_15, BPFCLK_D, SEL_DARC_3),
+       PINMUX_IPSR_MSEL(IP10_17_15, BPFCLK_D, SEL_DARC_3),
        PINMUX_IPSR_DATA(IP10_17_15, DU1_DB3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_17_15, SSI_SDATA9_B, SEL_SSI9_1),
+       PINMUX_IPSR_MSEL(IP10_17_15, SSI_SDATA9_B, SEL_SSI9_1),
        PINMUX_IPSR_DATA(IP10_17_15, TANS2),
        PINMUX_IPSR_DATA(IP10_17_15, CAN_DEBUGOUT5),
        PINMUX_IPSR_DATA(IP10_17_15, CC50_OSCOUT),
-       PINMUX_IPSR_MODSEL_DATA(IP10_20_18, SCIF3_RXD, SEL_SCIF3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_20_18, I2C1_SCL_E, SEL_I2C01_4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_20_18, FMCLK_D, SEL_DARC_3),
+       PINMUX_IPSR_MSEL(IP10_20_18, SCIF3_RXD, SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP10_20_18, I2C1_SCL_E, SEL_I2C01_4),
+       PINMUX_IPSR_MSEL(IP10_20_18, FMCLK_D, SEL_DARC_3),
        PINMUX_IPSR_DATA(IP10_20_18, DU1_DB4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_20_18, AUDIO_CLKA_C, SEL_ADG_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_20_18, SSI_SCK4_B, SEL_SSI4_1),
+       PINMUX_IPSR_MSEL(IP10_20_18, AUDIO_CLKA_C, SEL_ADG_2),
+       PINMUX_IPSR_MSEL(IP10_20_18, SSI_SCK4_B, SEL_SSI4_1),
        PINMUX_IPSR_DATA(IP10_20_18, CAN_DEBUGOUT6),
-       PINMUX_IPSR_MODSEL_DATA(IP10_20_18, RDS_CLK_C, SEL_RDS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_23_21, SCIF3_TXD, SEL_SCIF3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_23_21, I2C1_SDA_E, SEL_I2C01_4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_23_21, FMIN_D, SEL_DARC_3),
+       PINMUX_IPSR_MSEL(IP10_20_18, RDS_CLK_C, SEL_RDS_2),
+       PINMUX_IPSR_MSEL(IP10_23_21, SCIF3_TXD, SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP10_23_21, I2C1_SDA_E, SEL_I2C01_4),
+       PINMUX_IPSR_MSEL(IP10_23_21, FMIN_D, SEL_DARC_3),
        PINMUX_IPSR_DATA(IP10_23_21, DU1_DB5),
-       PINMUX_IPSR_MODSEL_DATA(IP10_23_21, AUDIO_CLKB_C, SEL_ADG_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_23_21, SSI_WS4_B, SEL_SSI4_1),
+       PINMUX_IPSR_MSEL(IP10_23_21, AUDIO_CLKB_C, SEL_ADG_2),
+       PINMUX_IPSR_MSEL(IP10_23_21, SSI_WS4_B, SEL_SSI4_1),
        PINMUX_IPSR_DATA(IP10_23_21, CAN_DEBUGOUT7),
-       PINMUX_IPSR_MODSEL_DATA(IP10_23_21, RDS_DATA_C, SEL_RDS_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_26_24, I2C2_SCL, SEL_I2C02_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_26_24, SCIFA5_RXD, SEL_SCIFA5_0),
+       PINMUX_IPSR_MSEL(IP10_23_21, RDS_DATA_C, SEL_RDS_2),
+       PINMUX_IPSR_MSEL(IP10_26_24, I2C2_SCL, SEL_I2C02_0),
+       PINMUX_IPSR_MSEL(IP10_26_24, SCIFA5_RXD, SEL_SCIFA5_0),
        PINMUX_IPSR_DATA(IP10_26_24, DU1_DB6),
-       PINMUX_IPSR_MODSEL_DATA(IP10_26_24, AUDIO_CLKC_C, SEL_ADG_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_26_24, SSI_SDATA4_B, SEL_SSI4_1),
+       PINMUX_IPSR_MSEL(IP10_26_24, AUDIO_CLKC_C, SEL_ADG_2),
+       PINMUX_IPSR_MSEL(IP10_26_24, SSI_SDATA4_B, SEL_SSI4_1),
        PINMUX_IPSR_DATA(IP10_26_24, CAN_DEBUGOUT8),
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_27, I2C2_SDA, SEL_I2C02_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_27, SCIFA5_TXD, SEL_SCIFA5_0),
+       PINMUX_IPSR_MSEL(IP10_29_27, I2C2_SDA, SEL_I2C02_0),
+       PINMUX_IPSR_MSEL(IP10_29_27, SCIFA5_TXD, SEL_SCIFA5_0),
        PINMUX_IPSR_DATA(IP10_29_27, DU1_DB7),
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_27, AUDIO_CLKOUT_C, SEL_ADG_2),
+       PINMUX_IPSR_MSEL(IP10_29_27, AUDIO_CLKOUT_C, SEL_ADG_2),
        PINMUX_IPSR_DATA(IP10_29_27, CAN_DEBUGOUT9),
-       PINMUX_IPSR_MODSEL_DATA(IP10_31_30, SSI_SCK5, SEL_SSI5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_31_30, SCIFA3_SCK, SEL_SCIFA3_0),
+       PINMUX_IPSR_MSEL(IP10_31_30, SSI_SCK5, SEL_SSI5_0),
+       PINMUX_IPSR_MSEL(IP10_31_30, SCIFA3_SCK, SEL_SCIFA3_0),
        PINMUX_IPSR_DATA(IP10_31_30, DU1_DOTCLKIN),
        PINMUX_IPSR_DATA(IP10_31_30, CAN_DEBUGOUT10),
 
        /* IPSR11 */
-       PINMUX_IPSR_MODSEL_DATA(IP11_2_0, SSI_WS5, SEL_SSI5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_2_0, SCIFA3_RXD, SEL_SCIFA3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_2_0, I2C3_SCL_C, SEL_I2C03_2),
+       PINMUX_IPSR_MSEL(IP11_2_0, SSI_WS5, SEL_SSI5_0),
+       PINMUX_IPSR_MSEL(IP11_2_0, SCIFA3_RXD, SEL_SCIFA3_0),
+       PINMUX_IPSR_MSEL(IP11_2_0, I2C3_SCL_C, SEL_I2C03_2),
        PINMUX_IPSR_DATA(IP11_2_0, DU1_DOTCLKOUT0),
        PINMUX_IPSR_DATA(IP11_2_0, CAN_DEBUGOUT11),
-       PINMUX_IPSR_MODSEL_DATA(IP11_5_3, SSI_SDATA5, SEL_SSI5_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_5_3, SCIFA3_TXD, SEL_SCIFA3_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_5_3, I2C3_SDA_C, SEL_I2C03_2),
+       PINMUX_IPSR_MSEL(IP11_5_3, SSI_SDATA5, SEL_SSI5_0),
+       PINMUX_IPSR_MSEL(IP11_5_3, SCIFA3_TXD, SEL_SCIFA3_0),
+       PINMUX_IPSR_MSEL(IP11_5_3, I2C3_SDA_C, SEL_I2C03_2),
        PINMUX_IPSR_DATA(IP11_5_3, DU1_DOTCLKOUT1),
        PINMUX_IPSR_DATA(IP11_5_3, CAN_DEBUGOUT12),
-       PINMUX_IPSR_MODSEL_DATA(IP11_7_6, SSI_SCK6, SEL_SSI6_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_7_6, SCIFA1_SCK_B, SEL_SCIFA1_1),
+       PINMUX_IPSR_MSEL(IP11_7_6, SSI_SCK6, SEL_SSI6_0),
+       PINMUX_IPSR_MSEL(IP11_7_6, SCIFA1_SCK_B, SEL_SCIFA1_1),
        PINMUX_IPSR_DATA(IP11_7_6, DU1_EXHSYNC_DU1_HSYNC),
        PINMUX_IPSR_DATA(IP11_7_6, CAN_DEBUGOUT13),
-       PINMUX_IPSR_MODSEL_DATA(IP11_10_8, SSI_WS6, SEL_SSI6_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_10_8, SCIFA1_RXD_B, SEL_SCIFA1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_10_8, I2C4_SCL_C, SEL_I2C04_2),
+       PINMUX_IPSR_MSEL(IP11_10_8, SSI_WS6, SEL_SSI6_0),
+       PINMUX_IPSR_MSEL(IP11_10_8, SCIFA1_RXD_B, SEL_SCIFA1_1),
+       PINMUX_IPSR_MSEL(IP11_10_8, I2C4_SCL_C, SEL_I2C04_2),
        PINMUX_IPSR_DATA(IP11_10_8, DU1_EXVSYNC_DU1_VSYNC),
        PINMUX_IPSR_DATA(IP11_10_8, CAN_DEBUGOUT14),
-       PINMUX_IPSR_MODSEL_DATA(IP11_13_11, SSI_SDATA6, SEL_SSI6_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_13_11, SCIFA1_TXD_B, SEL_SCIFA1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_13_11, I2C4_SDA_C, SEL_I2C04_2),
+       PINMUX_IPSR_MSEL(IP11_13_11, SSI_SDATA6, SEL_SSI6_0),
+       PINMUX_IPSR_MSEL(IP11_13_11, SCIFA1_TXD_B, SEL_SCIFA1_1),
+       PINMUX_IPSR_MSEL(IP11_13_11, I2C4_SDA_C, SEL_I2C04_2),
        PINMUX_IPSR_DATA(IP11_13_11, DU1_EXODDF_DU1_ODDF_DISP_CDE),
        PINMUX_IPSR_DATA(IP11_13_11, CAN_DEBUGOUT15),
-       PINMUX_IPSR_MODSEL_DATA(IP11_15_14, SSI_SCK78, SEL_SSI7_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_15_14, SCIFA2_SCK_B, SEL_SCIFA2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_15_14, IIC0_SDA_C, SEL_IIC00_2),
+       PINMUX_IPSR_MSEL(IP11_15_14, SSI_SCK78, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP11_15_14, SCIFA2_SCK_B, SEL_SCIFA2_1),
+       PINMUX_IPSR_MSEL(IP11_15_14, IIC0_SDA_C, SEL_IIC00_2),
        PINMUX_IPSR_DATA(IP11_15_14, DU1_DISP),
-       PINMUX_IPSR_MODSEL_DATA(IP11_17_16, SSI_WS78, SEL_SSI7_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_17_16, SCIFA2_RXD_B, SEL_SCIFA2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_17_16, IIC0_SCL_C, SEL_IIC00_2),
+       PINMUX_IPSR_MSEL(IP11_17_16, SSI_WS78, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP11_17_16, SCIFA2_RXD_B, SEL_SCIFA2_1),
+       PINMUX_IPSR_MSEL(IP11_17_16, IIC0_SCL_C, SEL_IIC00_2),
        PINMUX_IPSR_DATA(IP11_17_16, DU1_CDE),
-       PINMUX_IPSR_MODSEL_DATA(IP11_20_18, SSI_SDATA7, SEL_SSI7_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_20_18, SCIFA2_TXD_B, SEL_SCIFA2_1),
+       PINMUX_IPSR_MSEL(IP11_20_18, SSI_SDATA7, SEL_SSI7_0),
+       PINMUX_IPSR_MSEL(IP11_20_18, SCIFA2_TXD_B, SEL_SCIFA2_1),
        PINMUX_IPSR_DATA(IP11_20_18, IRQ8),
-       PINMUX_IPSR_MODSEL_DATA(IP11_20_18, AUDIO_CLKA_D, SEL_ADG_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_20_18, CAN_CLK_D, SEL_CAN_3),
+       PINMUX_IPSR_MSEL(IP11_20_18, AUDIO_CLKA_D, SEL_ADG_3),
+       PINMUX_IPSR_MSEL(IP11_20_18, CAN_CLK_D, SEL_CAN_3),
        PINMUX_IPSR_DATA(IP11_20_18, PCMOE_N),
        PINMUX_IPSR_DATA(IP11_23_21, SSI_SCK0129),
-       PINMUX_IPSR_MODSEL_DATA(IP11_23_21, MSIOF1_RXD_B, SEL_MSI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_23_21, SCIF5_RXD_D, SEL_SCIF5_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_23_21, ADIDATA_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_23_21, AD_DI_B, SEL_ADI_1),
+       PINMUX_IPSR_MSEL(IP11_23_21, MSIOF1_RXD_B, SEL_MSI1_1),
+       PINMUX_IPSR_MSEL(IP11_23_21, SCIF5_RXD_D, SEL_SCIF5_3),
+       PINMUX_IPSR_MSEL(IP11_23_21, ADIDATA_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP11_23_21, AD_DI_B, SEL_ADI_1),
        PINMUX_IPSR_DATA(IP11_23_21, PCMWE_N),
        PINMUX_IPSR_DATA(IP11_26_24, SSI_WS0129),
-       PINMUX_IPSR_MODSEL_DATA(IP11_26_24, MSIOF1_TXD_B, SEL_MSI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_26_24, SCIF5_TXD_D, SEL_SCIF5_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_26_24, ADICS_SAMP_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_26_24, AD_DO_B, SEL_ADI_1),
+       PINMUX_IPSR_MSEL(IP11_26_24, MSIOF1_TXD_B, SEL_MSI1_1),
+       PINMUX_IPSR_MSEL(IP11_26_24, SCIF5_TXD_D, SEL_SCIF5_3),
+       PINMUX_IPSR_MSEL(IP11_26_24, ADICS_SAMP_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP11_26_24, AD_DO_B, SEL_ADI_1),
        PINMUX_IPSR_DATA(IP11_29_27, SSI_SDATA0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_29_27, MSIOF1_SCK_B, SEL_MSI1_1),
+       PINMUX_IPSR_MSEL(IP11_29_27, MSIOF1_SCK_B, SEL_MSI1_1),
        PINMUX_IPSR_DATA(IP11_29_27, PWM0_B),
-       PINMUX_IPSR_MODSEL_DATA(IP11_29_27, ADICLK_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_29_27, AD_CLK_B, SEL_ADI_1),
+       PINMUX_IPSR_MSEL(IP11_29_27, ADICLK_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP11_29_27, AD_CLK_B, SEL_ADI_1),
 
        /* IPSR12 */
        PINMUX_IPSR_DATA(IP12_2_0, SSI_SCK34),
-       PINMUX_IPSR_MODSEL_DATA(IP12_2_0, MSIOF1_SYNC_B, SEL_MSI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_2_0, SCIFA1_SCK_C, SEL_SCIFA1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_2_0, ADICHS0_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_2_0, AD_NCS_N_B, SEL_ADI_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_2_0, DREQ1_N_B, SEL_LBS_1),
+       PINMUX_IPSR_MSEL(IP12_2_0, MSIOF1_SYNC_B, SEL_MSI1_1),
+       PINMUX_IPSR_MSEL(IP12_2_0, SCIFA1_SCK_C, SEL_SCIFA1_2),
+       PINMUX_IPSR_MSEL(IP12_2_0, ADICHS0_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP12_2_0, AD_NCS_N_B, SEL_ADI_1),
+       PINMUX_IPSR_MSEL(IP12_2_0, DREQ1_N_B, SEL_LBS_1),
        PINMUX_IPSR_DATA(IP12_5_3, SSI_WS34),
-       PINMUX_IPSR_MODSEL_DATA(IP12_5_3, MSIOF1_SS1_B, SEL_MSI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_5_3, SCIFA1_RXD_C, SEL_SCIFA1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_5_3, ADICHS1_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_5_3, CAN1_RX_C, SEL_CAN1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_5_3, DACK1_B, SEL_LBS_1),
+       PINMUX_IPSR_MSEL(IP12_5_3, MSIOF1_SS1_B, SEL_MSI1_1),
+       PINMUX_IPSR_MSEL(IP12_5_3, SCIFA1_RXD_C, SEL_SCIFA1_2),
+       PINMUX_IPSR_MSEL(IP12_5_3, ADICHS1_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP12_5_3, CAN1_RX_C, SEL_CAN1_2),
+       PINMUX_IPSR_MSEL(IP12_5_3, DACK1_B, SEL_LBS_1),
        PINMUX_IPSR_DATA(IP12_8_6, SSI_SDATA3),
-       PINMUX_IPSR_MODSEL_DATA(IP12_8_6, MSIOF1_SS2_B, SEL_MSI1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_8_6, SCIFA1_TXD_C, SEL_SCIFA1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_8_6, ADICHS2_B, SEL_RAD_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_8_6, CAN1_TX_C, SEL_CAN1_2),
+       PINMUX_IPSR_MSEL(IP12_8_6, MSIOF1_SS2_B, SEL_MSI1_1),
+       PINMUX_IPSR_MSEL(IP12_8_6, SCIFA1_TXD_C, SEL_SCIFA1_2),
+       PINMUX_IPSR_MSEL(IP12_8_6, ADICHS2_B, SEL_RAD_1),
+       PINMUX_IPSR_MSEL(IP12_8_6, CAN1_TX_C, SEL_CAN1_2),
        PINMUX_IPSR_DATA(IP12_8_6, DREQ2_N),
-       PINMUX_IPSR_MODSEL_DATA(IP12_10_9, SSI_SCK4, SEL_SSI4_0),
+       PINMUX_IPSR_MSEL(IP12_10_9, SSI_SCK4, SEL_SSI4_0),
        PINMUX_IPSR_DATA(IP12_10_9, MLB_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP12_10_9, IETX_B, SEL_IEB_1),
+       PINMUX_IPSR_MSEL(IP12_10_9, IETX_B, SEL_IEB_1),
        PINMUX_IPSR_DATA(IP12_10_9, IRD_TX),
-       PINMUX_IPSR_MODSEL_DATA(IP12_12_11, SSI_WS4, SEL_SSI4_0),
+       PINMUX_IPSR_MSEL(IP12_12_11, SSI_WS4, SEL_SSI4_0),
        PINMUX_IPSR_DATA(IP12_12_11, MLB_SIG),
-       PINMUX_IPSR_MODSEL_DATA(IP12_12_11, IECLK_B, SEL_IEB_1),
+       PINMUX_IPSR_MSEL(IP12_12_11, IECLK_B, SEL_IEB_1),
        PINMUX_IPSR_DATA(IP12_12_11, IRD_RX),
-       PINMUX_IPSR_MODSEL_DATA(IP12_14_13, SSI_SDATA4, SEL_SSI4_0),
+       PINMUX_IPSR_MSEL(IP12_14_13, SSI_SDATA4, SEL_SSI4_0),
        PINMUX_IPSR_DATA(IP12_14_13, MLB_DAT),
-       PINMUX_IPSR_MODSEL_DATA(IP12_14_13, IERX_B, SEL_IEB_1),
+       PINMUX_IPSR_MSEL(IP12_14_13, IERX_B, SEL_IEB_1),
        PINMUX_IPSR_DATA(IP12_14_13, IRD_SCK),
-       PINMUX_IPSR_MODSEL_DATA(IP12_17_15, SSI_SDATA8, SEL_SSI8_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_17_15, SCIF1_SCK_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP12_17_15, SSI_SDATA8, SEL_SSI8_0),
+       PINMUX_IPSR_MSEL(IP12_17_15, SCIF1_SCK_B, SEL_SCIF1_1),
        PINMUX_IPSR_DATA(IP12_17_15, PWM1_B),
        PINMUX_IPSR_DATA(IP12_17_15, IRQ9),
-       PINMUX_IPSR_MODSEL_DATA(IP12_17_15, REMOCON, SEL_RCN_0),
+       PINMUX_IPSR_MSEL(IP12_17_15, REMOCON, SEL_RCN_0),
        PINMUX_IPSR_DATA(IP12_17_15, DACK2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_17_15, ETH_MDIO_B, SEL_ETH_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_20_18, SSI_SCK1, SEL_SSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_20_18, SCIF1_RXD_B, SEL_SCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_20_18, IIC1_SCL_C, SEL_IIC01_2),
+       PINMUX_IPSR_MSEL(IP12_17_15, ETH_MDIO_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP12_20_18, SSI_SCK1, SEL_SSI1_0),
+       PINMUX_IPSR_MSEL(IP12_20_18, SCIF1_RXD_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP12_20_18, IIC1_SCL_C, SEL_IIC01_2),
        PINMUX_IPSR_DATA(IP12_20_18, VI1_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP12_20_18, CAN0_RX_D, SEL_CAN0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP12_20_18, AVB_AVTP_CAPTURE, SEL_AVB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_20_18, ETH_CRS_DV_B, SEL_ETH_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_23_21, SSI_WS1, SEL_SSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_23_21, SCIF1_TXD_B, SEL_SCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_23_21, IIC1_SDA_C, SEL_IIC01_2),
+       PINMUX_IPSR_MSEL(IP12_20_18, CAN0_RX_D, SEL_CAN0_3),
+       PINMUX_IPSR_MSEL(IP12_20_18, AVB_AVTP_CAPTURE, SEL_AVB_0),
+       PINMUX_IPSR_MSEL(IP12_20_18, ETH_CRS_DV_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP12_23_21, SSI_WS1, SEL_SSI1_0),
+       PINMUX_IPSR_MSEL(IP12_23_21, SCIF1_TXD_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP12_23_21, IIC1_SDA_C, SEL_IIC01_2),
        PINMUX_IPSR_DATA(IP12_23_21, VI1_DATA0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_23_21, CAN0_TX_D, SEL_CAN0_3),
-       PINMUX_IPSR_MODSEL_DATA(IP12_23_21, AVB_AVTP_MATCH, SEL_AVB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_23_21, ETH_RX_ER_B, SEL_ETH_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_26_24, SSI_SDATA1, SEL_SSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_26_24, HSCIF1_HRX_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP12_23_21, CAN0_TX_D, SEL_CAN0_3),
+       PINMUX_IPSR_MSEL(IP12_23_21, AVB_AVTP_MATCH, SEL_AVB_0),
+       PINMUX_IPSR_MSEL(IP12_23_21, ETH_RX_ER_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP12_26_24, SSI_SDATA1, SEL_SSI1_0),
+       PINMUX_IPSR_MSEL(IP12_26_24, HSCIF1_HRX_B, SEL_HSCIF1_1),
        PINMUX_IPSR_DATA(IP12_26_24, VI1_DATA1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_26_24, SDATA, SEL_FSN_0),
+       PINMUX_IPSR_MSEL(IP12_26_24, SDATA, SEL_FSN_0),
        PINMUX_IPSR_DATA(IP12_26_24, ATAG0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP12_26_24, ETH_RXD0_B, SEL_ETH_1),
-       PINMUX_IPSR_MODSEL_DATA(IP12_29_27, SSI_SCK2, SEL_SSI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP12_29_27, HSCIF1_HTX_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP12_26_24, ETH_RXD0_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP12_29_27, SSI_SCK2, SEL_SSI2_0),
+       PINMUX_IPSR_MSEL(IP12_29_27, HSCIF1_HTX_B, SEL_HSCIF1_1),
        PINMUX_IPSR_DATA(IP12_29_27, VI1_DATA2),
-       PINMUX_IPSR_MODSEL_DATA(IP12_29_27, MDATA, SEL_FSN_0),
+       PINMUX_IPSR_MSEL(IP12_29_27, MDATA, SEL_FSN_0),
        PINMUX_IPSR_DATA(IP12_29_27, ATAWR0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP12_29_27, ETH_RXD1_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP12_29_27, ETH_RXD1_B, SEL_ETH_1),
 
        /* IPSR13 */
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, SSI_WS2, SEL_SSI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, HSCIF1_HCTS_N_B, SEL_HSCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, SCIFA0_RXD_D, SEL_SCIFA0_3),
+       PINMUX_IPSR_MSEL(IP13_2_0, SSI_WS2, SEL_SSI2_0),
+       PINMUX_IPSR_MSEL(IP13_2_0, HSCIF1_HCTS_N_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP13_2_0, SCIFA0_RXD_D, SEL_SCIFA0_3),
        PINMUX_IPSR_DATA(IP13_2_0, VI1_DATA3),
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, SCKZ, SEL_FSN_0),
+       PINMUX_IPSR_MSEL(IP13_2_0, SCKZ, SEL_FSN_0),
        PINMUX_IPSR_DATA(IP13_2_0, ATACS00_N),
-       PINMUX_IPSR_MODSEL_DATA(IP13_2_0, ETH_LINK_B, SEL_ETH_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_5_3, SSI_SDATA2, SEL_SSI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_5_3, HSCIF1_HRTS_N_B, SEL_HSCIF1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_5_3, SCIFA0_TXD_D, SEL_SCIFA0_3),
+       PINMUX_IPSR_MSEL(IP13_2_0, ETH_LINK_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP13_5_3, SSI_SDATA2, SEL_SSI2_0),
+       PINMUX_IPSR_MSEL(IP13_5_3, HSCIF1_HRTS_N_B, SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP13_5_3, SCIFA0_TXD_D, SEL_SCIFA0_3),
        PINMUX_IPSR_DATA(IP13_5_3, VI1_DATA4),
-       PINMUX_IPSR_MODSEL_DATA(IP13_5_3, STM_N, SEL_FSN_0),
+       PINMUX_IPSR_MSEL(IP13_5_3, STM_N, SEL_FSN_0),
        PINMUX_IPSR_DATA(IP13_5_3, ATACS10_N),
-       PINMUX_IPSR_MODSEL_DATA(IP13_5_3, ETH_REFCLK_B, SEL_ETH_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_8_6, SSI_SCK9, SEL_SSI9_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_8_6, SCIF2_SCK_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP13_5_3, ETH_REFCLK_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP13_8_6, SSI_SCK9, SEL_SSI9_0),
+       PINMUX_IPSR_MSEL(IP13_8_6, SCIF2_SCK_B, SEL_SCIF2_1),
        PINMUX_IPSR_DATA(IP13_8_6, PWM2_B),
        PINMUX_IPSR_DATA(IP13_8_6, VI1_DATA5),
-       PINMUX_IPSR_MODSEL_DATA(IP13_8_6, MTS_N, SEL_FSN_0),
+       PINMUX_IPSR_MSEL(IP13_8_6, MTS_N, SEL_FSN_0),
        PINMUX_IPSR_DATA(IP13_8_6, EX_WAIT1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_8_6, ETH_TXD1_B, SEL_ETH_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_11_9, SSI_WS9, SEL_SSI9_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_11_9, SCIF2_RXD_B, SEL_SCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_11_9, I2C3_SCL_E, SEL_I2C03_4),
+       PINMUX_IPSR_MSEL(IP13_8_6, ETH_TXD1_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP13_11_9, SSI_WS9, SEL_SSI9_0),
+       PINMUX_IPSR_MSEL(IP13_11_9, SCIF2_RXD_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP13_11_9, I2C3_SCL_E, SEL_I2C03_4),
        PINMUX_IPSR_DATA(IP13_11_9, VI1_DATA6),
        PINMUX_IPSR_DATA(IP13_11_9, ATARD0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP13_11_9, ETH_TX_EN_B, SEL_ETH_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_14_12, SSI_SDATA9, SEL_SSI9_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_14_12, SCIF2_TXD_B, SEL_SCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_14_12, I2C3_SDA_E, SEL_I2C03_4),
+       PINMUX_IPSR_MSEL(IP13_11_9, ETH_TX_EN_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP13_14_12, SSI_SDATA9, SEL_SSI9_0),
+       PINMUX_IPSR_MSEL(IP13_14_12, SCIF2_TXD_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP13_14_12, I2C3_SDA_E, SEL_I2C03_4),
        PINMUX_IPSR_DATA(IP13_14_12, VI1_DATA7),
        PINMUX_IPSR_DATA(IP13_14_12, ATADIR0_N),
-       PINMUX_IPSR_MODSEL_DATA(IP13_14_12, ETH_MAGIC_B, SEL_ETH_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_17_15, AUDIO_CLKA, SEL_ADG_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_17_15, I2C0_SCL_B, SEL_I2C00_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_17_15, SCIFA4_RXD_D, SEL_SCIFA4_3),
+       PINMUX_IPSR_MSEL(IP13_14_12, ETH_MAGIC_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP13_17_15, AUDIO_CLKA, SEL_ADG_0),
+       PINMUX_IPSR_MSEL(IP13_17_15, I2C0_SCL_B, SEL_I2C00_1),
+       PINMUX_IPSR_MSEL(IP13_17_15, SCIFA4_RXD_D, SEL_SCIFA4_3),
        PINMUX_IPSR_DATA(IP13_17_15, VI1_CLKENB),
-       PINMUX_IPSR_MODSEL_DATA(IP13_17_15, TS_SDATA_C, SEL_TSIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP13_17_15, RIF0_SYNC_B, SEL_DR0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_17_15, ETH_TXD0_B, SEL_ETH_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_20_18, AUDIO_CLKB, SEL_ADG_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_20_18, I2C0_SDA_B, SEL_I2C00_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_20_18, SCIFA4_TXD_D, SEL_SCIFA4_3),
+       PINMUX_IPSR_MSEL(IP13_17_15, TS_SDATA_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP13_17_15, RIF0_SYNC_B, SEL_DR0_1),
+       PINMUX_IPSR_MSEL(IP13_17_15, ETH_TXD0_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP13_20_18, AUDIO_CLKB, SEL_ADG_0),
+       PINMUX_IPSR_MSEL(IP13_20_18, I2C0_SDA_B, SEL_I2C00_1),
+       PINMUX_IPSR_MSEL(IP13_20_18, SCIFA4_TXD_D, SEL_SCIFA4_3),
        PINMUX_IPSR_DATA(IP13_20_18, VI1_FIELD),
-       PINMUX_IPSR_MODSEL_DATA(IP13_20_18, TS_SCK_C, SEL_TSIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP13_20_18, RIF0_CLK_B, SEL_DR0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_20_18, BPFCLK_E, SEL_DARC_4),
-       PINMUX_IPSR_MODSEL_DATA(IP13_20_18, ETH_MDC_B, SEL_ETH_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_23_21, AUDIO_CLKC, SEL_ADG_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_23_21, I2C4_SCL_B, SEL_I2C04_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_23_21, SCIFA5_RXD_D, SEL_SCIFA5_3),
+       PINMUX_IPSR_MSEL(IP13_20_18, TS_SCK_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP13_20_18, RIF0_CLK_B, SEL_DR0_1),
+       PINMUX_IPSR_MSEL(IP13_20_18, BPFCLK_E, SEL_DARC_4),
+       PINMUX_IPSR_MSEL(IP13_20_18, ETH_MDC_B, SEL_ETH_1),
+       PINMUX_IPSR_MSEL(IP13_23_21, AUDIO_CLKC, SEL_ADG_0),
+       PINMUX_IPSR_MSEL(IP13_23_21, I2C4_SCL_B, SEL_I2C04_1),
+       PINMUX_IPSR_MSEL(IP13_23_21, SCIFA5_RXD_D, SEL_SCIFA5_3),
        PINMUX_IPSR_DATA(IP13_23_21, VI1_HSYNC_N),
-       PINMUX_IPSR_MODSEL_DATA(IP13_23_21, TS_SDEN_C, SEL_TSIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP13_23_21, RIF0_D0_B, SEL_DR0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_23_21, FMCLK_E, SEL_DARC_4),
-       PINMUX_IPSR_MODSEL_DATA(IP13_23_21, RDS_CLK_D, SEL_RDS_3),
-       PINMUX_IPSR_MODSEL_DATA(IP13_26_24, AUDIO_CLKOUT, SEL_ADG_0),
-       PINMUX_IPSR_MODSEL_DATA(IP13_26_24, I2C4_SDA_B, SEL_I2C04_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_26_24, SCIFA5_TXD_D, SEL_SCIFA5_3),
+       PINMUX_IPSR_MSEL(IP13_23_21, TS_SDEN_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP13_23_21, RIF0_D0_B, SEL_DR0_1),
+       PINMUX_IPSR_MSEL(IP13_23_21, FMCLK_E, SEL_DARC_4),
+       PINMUX_IPSR_MSEL(IP13_23_21, RDS_CLK_D, SEL_RDS_3),
+       PINMUX_IPSR_MSEL(IP13_26_24, AUDIO_CLKOUT, SEL_ADG_0),
+       PINMUX_IPSR_MSEL(IP13_26_24, I2C4_SDA_B, SEL_I2C04_1),
+       PINMUX_IPSR_MSEL(IP13_26_24, SCIFA5_TXD_D, SEL_SCIFA5_3),
        PINMUX_IPSR_DATA(IP13_26_24, VI1_VSYNC_N),
-       PINMUX_IPSR_MODSEL_DATA(IP13_26_24, TS_SPSYNC_C, SEL_TSIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP13_26_24, RIF0_D1_B, SEL_DR1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP13_26_24, FMIN_E, SEL_DARC_4),
-       PINMUX_IPSR_MODSEL_DATA(IP13_26_24, RDS_DATA_D, SEL_RDS_3),
+       PINMUX_IPSR_MSEL(IP13_26_24, TS_SPSYNC_C, SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP13_26_24, RIF0_D1_B, SEL_DR1_1),
+       PINMUX_IPSR_MSEL(IP13_26_24, FMIN_E, SEL_DARC_4),
+       PINMUX_IPSR_MSEL(IP13_26_24, RDS_DATA_D, SEL_RDS_3),
 };
 
 static const struct sh_pfc_pin pinmux_pins[] = {
@@ -2197,13 +2196,6 @@ static const unsigned int scif0_data_pins[] = {
 static const unsigned int scif0_data_mux[] = {
        SCIF0_RXD_MARK, SCIF0_TXD_MARK,
 };
-static const unsigned int scif0_clk_pins[] = {
-       /* SCK */
-       RCAR_GP_PIN(1, 23),
-};
-static const unsigned int scif0_clk_mux[] = {
-       SCIF_CLK_MARK,
-};
 static const unsigned int scif0_data_b_pins[] = {
        /* RX, TX */
        RCAR_GP_PIN(3, 11), RCAR_GP_PIN(3, 12),
@@ -2211,13 +2203,6 @@ static const unsigned int scif0_data_b_pins[] = {
 static const unsigned int scif0_data_b_mux[] = {
        SCIF0_RXD_B_MARK, SCIF0_TXD_B_MARK,
 };
-static const unsigned int scif0_clk_b_pins[] = {
-       /* SCK */
-       RCAR_GP_PIN(3, 29),
-};
-static const unsigned int scif0_clk_b_mux[] = {
-       SCIF_CLK_B_MARK,
-};
 static const unsigned int scif0_data_c_pins[] = {
        /* RX, TX */
        RCAR_GP_PIN(3, 30), RCAR_GP_PIN(3, 31),
@@ -2788,6 +2773,146 @@ static const unsigned int usb1_mux[] = {
        USB1_PWEN_MARK,
        USB1_OVC_MARK,
 };
+/* - VIN0 ------------------------------------------------------------------- */
+static const union vin_data vin0_data_pins = {
+       .data24 = {
+               /* B */
+               RCAR_GP_PIN(3, 1), RCAR_GP_PIN(3, 2),
+               RCAR_GP_PIN(3, 3), 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),
+               /* G */
+               RCAR_GP_PIN(3, 13), RCAR_GP_PIN(3, 14),
+               RCAR_GP_PIN(3, 15), RCAR_GP_PIN(3, 16),
+               RCAR_GP_PIN(3, 17), RCAR_GP_PIN(3, 18),
+               RCAR_GP_PIN(3, 19), RCAR_GP_PIN(3, 20),
+               /* R */
+               RCAR_GP_PIN(3, 21), RCAR_GP_PIN(3, 22),
+               RCAR_GP_PIN(3, 23), RCAR_GP_PIN(3, 24),
+               RCAR_GP_PIN(3, 25), RCAR_GP_PIN(3, 26),
+               RCAR_GP_PIN(3, 27), RCAR_GP_PIN(3, 28),
+       },
+};
+static const union vin_data vin0_data_mux = {
+       .data24 = {
+               /* B */
+               VI0_DATA0_VI0_B0_MARK, VI0_DATA1_VI0_B1_MARK,
+               VI0_DATA2_VI0_B2_MARK, VI0_DATA3_VI0_B3_MARK,
+               VI0_DATA4_VI0_B4_MARK, VI0_DATA5_VI0_B5_MARK,
+               VI0_DATA6_VI0_B6_MARK, VI0_DATA7_VI0_B7_MARK,
+               /* G */
+               VI0_G0_MARK, VI0_G1_MARK,
+               VI0_G2_MARK, VI0_G3_MARK,
+               VI0_G4_MARK, VI0_G5_MARK,
+               VI0_G6_MARK, VI0_G7_MARK,
+               /* R */
+               VI0_R0_MARK, VI0_R1_MARK,
+               VI0_R2_MARK, VI0_R3_MARK,
+               VI0_R4_MARK, VI0_R5_MARK,
+               VI0_R6_MARK, VI0_R7_MARK,
+       },
+};
+static const unsigned int vin0_data18_pins[] = {
+       /* B */
+       RCAR_GP_PIN(3, 3), 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),
+       /* G */
+       RCAR_GP_PIN(3, 15), RCAR_GP_PIN(3, 16),
+       RCAR_GP_PIN(3, 17), RCAR_GP_PIN(3, 18),
+       RCAR_GP_PIN(3, 19), RCAR_GP_PIN(3, 20),
+       /* R */
+       RCAR_GP_PIN(3, 23), RCAR_GP_PIN(3, 24),
+       RCAR_GP_PIN(3, 25), RCAR_GP_PIN(3, 26),
+       RCAR_GP_PIN(3, 27), RCAR_GP_PIN(3, 28),
+};
+static const unsigned int vin0_data18_mux[] = {
+       /* B */
+       VI0_DATA2_VI0_B2_MARK, VI0_DATA3_VI0_B3_MARK,
+       VI0_DATA4_VI0_B4_MARK, VI0_DATA5_VI0_B5_MARK,
+       VI0_DATA6_VI0_B6_MARK, VI0_DATA7_VI0_B7_MARK,
+       /* G */
+       VI0_G2_MARK, VI0_G3_MARK,
+       VI0_G4_MARK, VI0_G5_MARK,
+       VI0_G6_MARK, VI0_G7_MARK,
+       /* R */
+       VI0_R2_MARK, VI0_R3_MARK,
+       VI0_R4_MARK, VI0_R5_MARK,
+       VI0_R6_MARK, VI0_R7_MARK,
+};
+static const unsigned int vin0_sync_pins[] = {
+       RCAR_GP_PIN(3, 11), /* HSYNC */
+       RCAR_GP_PIN(3, 12), /* VSYNC */
+};
+static const unsigned int vin0_sync_mux[] = {
+       VI0_HSYNC_N_MARK,
+       VI0_VSYNC_N_MARK,
+};
+static const unsigned int vin0_field_pins[] = {
+       RCAR_GP_PIN(3, 10),
+};
+static const unsigned int vin0_field_mux[] = {
+       VI0_FIELD_MARK,
+};
+static const unsigned int vin0_clkenb_pins[] = {
+       RCAR_GP_PIN(3, 9),
+};
+static const unsigned int vin0_clkenb_mux[] = {
+       VI0_CLKENB_MARK,
+};
+static const unsigned int vin0_clk_pins[] = {
+       RCAR_GP_PIN(3, 0),
+};
+static const unsigned int vin0_clk_mux[] = {
+       VI0_CLK_MARK,
+};
+/* - VIN1 ------------------------------------------------------------------- */
+static const union vin_data vin1_data_pins = {
+       .data12 = {
+               RCAR_GP_PIN(5, 12), RCAR_GP_PIN(5, 13),
+               RCAR_GP_PIN(5, 14), RCAR_GP_PIN(5, 15),
+               RCAR_GP_PIN(5, 16), RCAR_GP_PIN(5, 17),
+               RCAR_GP_PIN(5, 18), RCAR_GP_PIN(5, 19),
+               RCAR_GP_PIN(1, 10), RCAR_GP_PIN(1, 11),
+               RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
+       },
+};
+static const union vin_data 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[] = {
+       RCAR_GP_PIN(5, 22), /* HSYNC */
+       RCAR_GP_PIN(5, 23), /* VSYNC */
+};
+static const unsigned int vin1_sync_mux[] = {
+       VI1_HSYNC_N_MARK,
+       VI1_VSYNC_N_MARK,
+};
+static const unsigned int vin1_field_pins[] = {
+       RCAR_GP_PIN(5, 21),
+};
+static const unsigned int vin1_field_mux[] = {
+       VI1_FIELD_MARK,
+};
+static const unsigned int vin1_clkenb_pins[] = {
+       RCAR_GP_PIN(5, 20),
+};
+static const unsigned int vin1_clkenb_mux[] = {
+       VI1_CLKENB_MARK,
+};
+static const unsigned int vin1_clk_pins[] = {
+       RCAR_GP_PIN(5, 11),
+};
+static const unsigned int vin1_clk_mux[] = {
+       VI1_CLK_MARK,
+};
 
 static const struct sh_pfc_pin_group pinmux_groups[] = {
        SH_PFC_PIN_GROUP(eth_link),
@@ -2884,9 +3009,7 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
        SH_PFC_PIN_GROUP(qspi_data2),
        SH_PFC_PIN_GROUP(qspi_data4),
        SH_PFC_PIN_GROUP(scif0_data),
-       SH_PFC_PIN_GROUP(scif0_clk),
        SH_PFC_PIN_GROUP(scif0_data_b),
-       SH_PFC_PIN_GROUP(scif0_clk_b),
        SH_PFC_PIN_GROUP(scif0_data_c),
        SH_PFC_PIN_GROUP(scif0_data_d),
        SH_PFC_PIN_GROUP(scif1_data),
@@ -2965,6 +3088,24 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
        SH_PFC_PIN_GROUP(sdhi2_wp),
        SH_PFC_PIN_GROUP(usb0),
        SH_PFC_PIN_GROUP(usb1),
+       VIN_DATA_PIN_GROUP(vin0_data, 24),
+       VIN_DATA_PIN_GROUP(vin0_data, 20),
+       SH_PFC_PIN_GROUP(vin0_data18),
+       VIN_DATA_PIN_GROUP(vin0_data, 16),
+       VIN_DATA_PIN_GROUP(vin0_data, 12),
+       VIN_DATA_PIN_GROUP(vin0_data, 10),
+       VIN_DATA_PIN_GROUP(vin0_data, 8),
+       SH_PFC_PIN_GROUP(vin0_sync),
+       SH_PFC_PIN_GROUP(vin0_field),
+       SH_PFC_PIN_GROUP(vin0_clkenb),
+       SH_PFC_PIN_GROUP(vin0_clk),
+       VIN_DATA_PIN_GROUP(vin1_data, 12),
+       VIN_DATA_PIN_GROUP(vin1_data, 10),
+       VIN_DATA_PIN_GROUP(vin1_data, 8),
+       SH_PFC_PIN_GROUP(vin1_sync),
+       SH_PFC_PIN_GROUP(vin1_field),
+       SH_PFC_PIN_GROUP(vin1_clkenb),
+       SH_PFC_PIN_GROUP(vin1_clk),
 };
 
 static const char * const eth_groups[] = {
@@ -3107,9 +3248,7 @@ static const char * const qspi_groups[] = {
 
 static const char * const scif0_groups[] = {
        "scif0_data",
-       "scif0_clk",
        "scif0_data_b",
-       "scif0_clk_b",
        "scif0_data_c",
        "scif0_data_d",
 };
@@ -3247,6 +3386,30 @@ static const char * const usb1_groups[] = {
        "usb1",
 };
 
+static const char * const vin0_groups[] = {
+       "vin0_data24",
+       "vin0_data20",
+       "vin0_data18",
+       "vin0_data16",
+       "vin0_data12",
+       "vin0_data10",
+       "vin0_data8",
+       "vin0_sync",
+       "vin0_field",
+       "vin0_clkenb",
+       "vin0_clk",
+};
+
+static const char * const vin1_groups[] = {
+       "vin1_data12",
+       "vin1_data10",
+       "vin1_data8",
+       "vin1_sync",
+       "vin1_field",
+       "vin1_clkenb",
+       "vin1_clk",
+};
+
 static const struct sh_pfc_function pinmux_functions[] = {
        SH_PFC_FUNCTION(eth),
        SH_PFC_FUNCTION(hscif0),
@@ -3283,6 +3446,8 @@ static const struct sh_pfc_function pinmux_functions[] = {
        SH_PFC_FUNCTION(sdhi2),
        SH_PFC_FUNCTION(usb0),
        SH_PFC_FUNCTION(usb1),
+       SH_PFC_FUNCTION(vin0),
+       SH_PFC_FUNCTION(vin1),
 };
 
 static const struct pinmux_cfg_reg pinmux_config_regs[] = {
@@ -4232,6 +4397,6 @@ const struct sh_pfc_soc_info r8a7794_pinmux_info = {
 
        .cfg_regs = pinmux_config_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
new file mode 100644 (file)
index 0000000..7ddb2ad
--- /dev/null
@@ -0,0 +1,2816 @@
+/*
+ * R-Car Gen3 processor support - PFC hardware block.
+ *
+ * Copyright (C) 2015  Renesas Electronics Corporation
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+
+#include "core.h"
+#include "sh_pfc.h"
+
+#define PORT_GP_3(bank, fn, sfx)                                       \
+       PORT_GP_1(bank, 0,  fn, sfx), PORT_GP_1(bank, 1,  fn, sfx),     \
+       PORT_GP_1(bank, 2,  fn, sfx), PORT_GP_1(bank, 3,  fn, sfx)
+
+#define PORT_GP_14(bank, fn, sfx)                                      \
+       PORT_GP_3(bank, fn, sfx),                                       \
+       PORT_GP_1(bank, 4,  fn, sfx), PORT_GP_1(bank, 5,  fn, sfx),     \
+       PORT_GP_1(bank, 6,  fn, sfx), PORT_GP_1(bank, 7,  fn, sfx),     \
+       PORT_GP_1(bank, 8,  fn, sfx), PORT_GP_1(bank, 9,  fn, sfx),     \
+       PORT_GP_1(bank, 10, fn, sfx), PORT_GP_1(bank, 11, fn, sfx),     \
+       PORT_GP_1(bank, 12, fn, sfx), PORT_GP_1(bank, 13, fn, sfx),     \
+       PORT_GP_1(bank, 14, fn, sfx)
+
+#define PORT_GP_15(bank, fn, sfx)                                      \
+       PORT_GP_14(bank, fn, sfx),   PORT_GP_1(bank, 15, fn, sfx)
+
+#define PORT_GP_17(bank, fn, sfx)                                      \
+       PORT_GP_15(bank, fn, sfx),                                      \
+       PORT_GP_1(bank, 16, fn, sfx), PORT_GP_1(bank, 17, fn, sfx)
+
+#define PORT_GP_25(bank, fn, sfx)                                      \
+       PORT_GP_17(bank, fn, sfx),                                      \
+       PORT_GP_1(bank, 18, fn, sfx), PORT_GP_1(bank, 19, fn, sfx),     \
+       PORT_GP_1(bank, 20, fn, sfx), PORT_GP_1(bank, 21, fn, sfx),     \
+       PORT_GP_1(bank, 22, fn, sfx), PORT_GP_1(bank, 23, fn, sfx),     \
+       PORT_GP_1(bank, 24, fn, sfx), PORT_GP_1(bank, 25, fn, sfx)
+
+#define PORT_GP_27(bank, fn, sfx)                                      \
+       PORT_GP_25(bank, fn, sfx),                                      \
+       PORT_GP_1(bank, 26, fn, sfx), PORT_GP_1(bank, 27, fn, sfx)
+
+#define CPU_ALL_PORT(fn, sfx)                                          \
+       PORT_GP_15(0, fn, sfx),                                         \
+       PORT_GP_27(1, fn, sfx),                                         \
+       PORT_GP_14(2, fn, sfx),                                         \
+       PORT_GP_15(3, fn, sfx),                                         \
+       PORT_GP_17(4, fn, sfx),                                         \
+       PORT_GP_25(5, fn, sfx),                                         \
+       PORT_GP_32(6, fn, sfx),                                         \
+       PORT_GP_3(7, fn, sfx)
+/*
+ * F_() : just information
+ * FM() : macro for FN_xxx / xxx_MARK
+ */
+
+/* GPSR0 */
+#define GPSR0_15       F_(D15,                 IP7_11_8)
+#define GPSR0_14       F_(D14,                 IP7_7_4)
+#define GPSR0_13       F_(D13,                 IP7_3_0)
+#define GPSR0_12       F_(D12,                 IP6_31_28)
+#define GPSR0_11       F_(D11,                 IP6_27_24)
+#define GPSR0_10       F_(D10,                 IP6_23_20)
+#define GPSR0_9                F_(D9,                  IP6_19_16)
+#define GPSR0_8                F_(D8,                  IP6_15_12)
+#define GPSR0_7                F_(D7,                  IP6_11_8)
+#define GPSR0_6                F_(D6,                  IP6_7_4)
+#define GPSR0_5                F_(D5,                  IP6_3_0)
+#define GPSR0_4                F_(D4,                  IP5_31_28)
+#define GPSR0_3                F_(D3,                  IP5_27_24)
+#define GPSR0_2                F_(D2,                  IP5_23_20)
+#define GPSR0_1                F_(D1,                  IP5_19_16)
+#define GPSR0_0                F_(D0,                  IP5_15_12)
+
+/* GPSR1 */
+#define GPSR1_27       F_(EX_WAIT0_A,          IP5_11_8)
+#define GPSR1_26       F_(WE1_N,               IP5_7_4)
+#define GPSR1_25       F_(WE0_N,               IP5_3_0)
+#define GPSR1_24       F_(RD_WR_N,             IP4_31_28)
+#define GPSR1_23       F_(RD_N,                IP4_27_24)
+#define GPSR1_22       F_(BS_N,                IP4_23_20)
+#define GPSR1_21       F_(CS1_N_A26,           IP4_19_16)
+#define GPSR1_20       F_(CS0_N,               IP4_15_12)
+#define GPSR1_19       F_(A19,                 IP4_11_8)
+#define GPSR1_18       F_(A18,                 IP4_7_4)
+#define GPSR1_17       F_(A17,                 IP4_3_0)
+#define GPSR1_16       F_(A16,                 IP3_31_28)
+#define GPSR1_15       F_(A15,                 IP3_27_24)
+#define GPSR1_14       F_(A14,                 IP3_23_20)
+#define GPSR1_13       F_(A13,                 IP3_19_16)
+#define GPSR1_12       F_(A12,                 IP3_15_12)
+#define GPSR1_11       F_(A11,                 IP3_11_8)
+#define GPSR1_10       F_(A10,                 IP3_7_4)
+#define GPSR1_9                F_(A9,                  IP3_3_0)
+#define GPSR1_8                F_(A8,                  IP2_31_28)
+#define GPSR1_7                F_(A7,                  IP2_27_24)
+#define GPSR1_6                F_(A6,                  IP2_23_20)
+#define GPSR1_5                F_(A5,                  IP2_19_16)
+#define GPSR1_4                F_(A4,                  IP2_15_12)
+#define GPSR1_3                F_(A3,                  IP2_11_8)
+#define GPSR1_2                F_(A2,                  IP2_7_4)
+#define GPSR1_1                F_(A1,                  IP2_3_0)
+#define GPSR1_0                F_(A0,                  IP1_31_28)
+
+/* GPSR2 */
+#define GPSR2_14       F_(AVB_AVTP_CAPTURE_A,  IP0_23_20)
+#define GPSR2_13       F_(AVB_AVTP_MATCH_A,    IP0_19_16)
+#define GPSR2_12       F_(AVB_LINK,            IP0_15_12)
+#define GPSR2_11       F_(AVB_PHY_INT,         IP0_11_8)
+#define GPSR2_10       F_(AVB_MAGIC,           IP0_7_4)
+#define GPSR2_9                F_(AVB_MDC,             IP0_3_0)
+#define GPSR2_8                F_(PWM2_A,              IP1_27_24)
+#define GPSR2_7                F_(PWM1_A,              IP1_23_20)
+#define GPSR2_6                F_(PWM0,                IP1_19_16)
+#define GPSR2_5                F_(IRQ5,                IP1_15_12)
+#define GPSR2_4                F_(IRQ4,                IP1_11_8)
+#define GPSR2_3                F_(IRQ3,                IP1_7_4)
+#define GPSR2_2                F_(IRQ2,                IP1_3_0)
+#define GPSR2_1                F_(IRQ1,                IP0_31_28)
+#define GPSR2_0                F_(IRQ0,                IP0_27_24)
+
+/* GPSR3 */
+#define GPSR3_15       F_(SD1_WP,              IP10_23_20)
+#define GPSR3_14       F_(SD1_CD,              IP10_19_16)
+#define GPSR3_13       F_(SD0_WP,              IP10_15_12)
+#define GPSR3_12       F_(SD0_CD,              IP10_11_8)
+#define GPSR3_11       F_(SD1_DAT3,            IP8_31_28)
+#define GPSR3_10       F_(SD1_DAT2,            IP8_27_24)
+#define GPSR3_9                F_(SD1_DAT1,            IP8_23_20)
+#define GPSR3_8                F_(SD1_DAT0,            IP8_19_16)
+#define GPSR3_7                F_(SD1_CMD,             IP8_15_12)
+#define GPSR3_6                F_(SD1_CLK,             IP8_11_8)
+#define GPSR3_5                F_(SD0_DAT3,            IP8_7_4)
+#define GPSR3_4                F_(SD0_DAT2,            IP8_3_0)
+#define GPSR3_3                F_(SD0_DAT1,            IP7_31_28)
+#define GPSR3_2                F_(SD0_DAT0,            IP7_27_24)
+#define GPSR3_1                F_(SD0_CMD,             IP7_23_20)
+#define GPSR3_0                F_(SD0_CLK,             IP7_19_16)
+
+/* GPSR4 */
+#define GPSR4_17       FM(SD3_DS)
+#define GPSR4_16       F_(SD3_DAT7,            IP10_7_4)
+#define GPSR4_15       F_(SD3_DAT6,            IP10_3_0)
+#define GPSR4_14       F_(SD3_DAT5,            IP9_31_28)
+#define GPSR4_13       F_(SD3_DAT4,            IP9_27_24)
+#define GPSR4_12       FM(SD3_DAT3)
+#define GPSR4_11       FM(SD3_DAT2)
+#define GPSR4_10       FM(SD3_DAT1)
+#define GPSR4_9                FM(SD3_DAT0)
+#define GPSR4_8                FM(SD3_CMD)
+#define GPSR4_7                FM(SD3_CLK)
+#define GPSR4_6                F_(SD2_DS,              IP9_23_20)
+#define GPSR4_5                F_(SD2_DAT3,            IP9_19_16)
+#define GPSR4_4                F_(SD2_DAT2,            IP9_15_12)
+#define GPSR4_3                F_(SD2_DAT1,            IP9_11_8)
+#define GPSR4_2                F_(SD2_DAT0,            IP9_7_4)
+#define GPSR4_1                FM(SD2_CMD)
+#define GPSR4_0                F_(SD2_CLK,             IP9_3_0)
+
+/* GPSR5 */
+#define GPSR5_25       F_(MLB_DAT,             IP13_19_16)
+#define GPSR5_24       F_(MLB_SIG,             IP13_15_12)
+#define GPSR5_23       F_(MLB_CLK,             IP13_11_8)
+#define GPSR5_22       FM(MSIOF0_RXD)
+#define GPSR5_21       F_(MSIOF0_SS2,          IP13_7_4)
+#define GPSR5_20       FM(MSIOF0_TXD)
+#define GPSR5_19       F_(MSIOF0_SS1,          IP13_3_0)
+#define GPSR5_18       F_(MSIOF0_SYNC,         IP12_31_28)
+#define GPSR5_17       FM(MSIOF0_SCK)
+#define GPSR5_16       F_(HRTS0_N,             IP12_27_24)
+#define GPSR5_15       F_(HCTS0_N,             IP12_23_20)
+#define GPSR5_14       F_(HTX0,                IP12_19_16)
+#define GPSR5_13       F_(HRX0,                IP12_15_12)
+#define GPSR5_12       F_(HSCK0,               IP12_11_8)
+#define GPSR5_11       F_(RX2_A,               IP12_7_4)
+#define GPSR5_10       F_(TX2_A,               IP12_3_0)
+#define GPSR5_9                F_(SCK2,                IP11_31_28)
+#define GPSR5_8                F_(RTS1_N_TANS,         IP11_27_24)
+#define GPSR5_7                F_(CTS1_N,              IP11_23_20)
+#define GPSR5_6                F_(TX1_A,               IP11_19_16)
+#define GPSR5_5                F_(RX1_A,               IP11_15_12)
+#define GPSR5_4                F_(RTS0_N_TANS,         IP11_11_8)
+#define GPSR5_3                F_(CTS0_N,              IP11_7_4)
+#define GPSR5_2                F_(TX0,                 IP11_3_0)
+#define GPSR5_1                F_(RX0,                 IP10_31_28)
+#define GPSR5_0                F_(SCK0,                IP10_27_24)
+
+/* GPSR6 */
+#define GPSR6_31       F_(USB31_OVC,           IP17_7_4)
+#define GPSR6_30       F_(USB31_PWEN,          IP17_3_0)
+#define GPSR6_29       F_(USB30_OVC,           IP16_31_28)
+#define GPSR6_28       F_(USB30_PWEN,          IP16_27_24)
+#define GPSR6_27       F_(USB1_OVC,            IP16_23_20)
+#define GPSR6_26       F_(USB1_PWEN,           IP16_19_16)
+#define GPSR6_25       F_(USB0_OVC,            IP16_15_12)
+#define GPSR6_24       F_(USB0_PWEN,           IP16_11_8)
+#define GPSR6_23       F_(AUDIO_CLKB_B,        IP16_7_4)
+#define GPSR6_22       F_(AUDIO_CLKA_A,        IP16_3_0)
+#define GPSR6_21       F_(SSI_SDATA9_A,        IP15_31_28)
+#define GPSR6_20       F_(SSI_SDATA8,          IP15_27_24)
+#define GPSR6_19       F_(SSI_SDATA7,          IP15_23_20)
+#define GPSR6_18       F_(SSI_WS78,            IP15_19_16)
+#define GPSR6_17       F_(SSI_SCK78,           IP15_15_12)
+#define GPSR6_16       F_(SSI_SDATA6,          IP15_11_8)
+#define GPSR6_15       F_(SSI_WS6,             IP15_7_4)
+#define GPSR6_14       F_(SSI_SCK6,            IP15_3_0)
+#define GPSR6_13       FM(SSI_SDATA5)
+#define GPSR6_12       FM(SSI_WS5)
+#define GPSR6_11       FM(SSI_SCK5)
+#define GPSR6_10       F_(SSI_SDATA4,          IP14_31_28)
+#define GPSR6_9                F_(SSI_WS4,             IP14_27_24)
+#define GPSR6_8                F_(SSI_SCK4,            IP14_23_20)
+#define GPSR6_7                F_(SSI_SDATA3,          IP14_19_16)
+#define GPSR6_6                F_(SSI_WS34,            IP14_15_12)
+#define GPSR6_5                F_(SSI_SCK34,           IP14_11_8)
+#define GPSR6_4                F_(SSI_SDATA2_A,        IP14_7_4)
+#define GPSR6_3                F_(SSI_SDATA1_A,        IP14_3_0)
+#define GPSR6_2                F_(SSI_SDATA0,          IP13_31_28)
+#define GPSR6_1                F_(SSI_WS0129,          IP13_27_24)
+#define GPSR6_0                F_(SSI_SCK0129,         IP13_23_20)
+
+/* GPSR7 */
+#define GPSR7_3                FM(HDMI1_CEC)
+#define GPSR7_2                FM(HDMI0_CEC)
+#define GPSR7_1                FM(AVS2)
+#define GPSR7_0                FM(AVS1)
+
+
+/* IPSRx */            /* 0 */                 /* 1 */         /* 2 */                 /* 3 */                         /* 4 */         /* 5 */         /* 6 */                 /* 7 */         /* 8 */                 /* 9 */         /* A */         /* B */         /* C - F */
+#define IP0_3_0                FM(AVB_MDC)             F_(0, 0)        FM(MSIOF2_SS2_C)        F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_7_4                FM(AVB_MAGIC)           F_(0, 0)        FM(MSIOF2_SS1_C)        FM(SCK4_A)                      F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_11_8       FM(AVB_PHY_INT)         F_(0, 0)        FM(MSIOF2_SYNC_C)       FM(RX4_A)                       F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_15_12      FM(AVB_LINK)            F_(0, 0)        FM(MSIOF2_SCK_C)        FM(TX4_A)                       F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_19_16      FM(AVB_AVTP_MATCH_A)    F_(0, 0)        FM(MSIOF2_RXD_C)        FM(CTS4_N_A)                    F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_23_20      FM(AVB_AVTP_CAPTURE_A)  F_(0, 0)        FM(MSIOF2_TXD_C)        FM(RTS4_N_TANS_A)               F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_27_24      FM(IRQ0)                FM(QPOLB)       F_(0, 0)                FM(DU_CDE)                      FM(VI4_DATA0_B) FM(CAN0_TX_B)   FM(CANFD0_TX_B)         F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_31_28      FM(IRQ1)                FM(QPOLA)       F_(0, 0)                FM(DU_DISP)                     FM(VI4_DATA1_B) FM(CAN0_RX_B)   FM(CANFD0_RX_B)         F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_3_0                FM(IRQ2)                FM(QCPV_QDE)    F_(0, 0)                FM(DU_EXODDF_DU_ODDF_DISP_CDE)  FM(VI4_DATA2_B) F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                FM(PWM3_B)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_7_4                FM(IRQ3)                FM(QSTVB_QVE)   FM(A25)                 FM(DU_DOTCLKOUT1)               FM(VI4_DATA3_B) F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                FM(PWM4_B)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_11_8       FM(IRQ4)                FM(QSTH_QHS)    FM(A24)                 FM(DU_EXHSYNC_DU_HSYNC)         FM(VI4_DATA4_B) F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                FM(PWM5_B)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_15_12      FM(IRQ5)                FM(QSTB_QHE)    FM(A23)                 FM(DU_EXVSYNC_DU_VSYNC)         FM(VI4_DATA5_B) F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                FM(PWM6_B)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_19_16      FM(PWM0)                FM(AVB_AVTP_PPS)FM(A22)                 F_(0, 0)                        FM(VI4_DATA6_B) F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                FM(IECLK_B)     F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_23_20      FM(PWM1_A)              F_(0, 0)        FM(A21)                 FM(HRX3_D)                      FM(VI4_DATA7_B) F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                FM(IERX_B)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_27_24      FM(PWM2_A)              F_(0, 0)        FM(A20)                 FM(HTX3_D)                      F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                FM(IETX_B)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_31_28      FM(A0)                  FM(LCDOUT16)    FM(MSIOF3_SYNC_B)       F_(0, 0)                        FM(VI4_DATA8)   F_(0, 0)        FM(DU_DB0)              F_(0, 0)        F_(0, 0)                FM(PWM3_A)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_3_0                FM(A1)                  FM(LCDOUT17)    FM(MSIOF3_TXD_B)        F_(0, 0)                        FM(VI4_DATA9)   F_(0, 0)        FM(DU_DB1)              F_(0, 0)        F_(0, 0)                FM(PWM4_A)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_7_4                FM(A2)                  FM(LCDOUT18)    FM(MSIOF3_SCK_B)        F_(0, 0)                        FM(VI4_DATA10)  F_(0, 0)        FM(DU_DB2)              F_(0, 0)        F_(0, 0)                FM(PWM5_A)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_11_8       FM(A3)                  FM(LCDOUT19)    FM(MSIOF3_RXD_B)        F_(0, 0)                        FM(VI4_DATA11)  F_(0, 0)        FM(DU_DB3)              F_(0, 0)        F_(0, 0)                FM(PWM6_A)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+
+/* IPSRx */            /* 0 */                 /* 1 */         /* 2 */                 /* 3 */                         /* 4 */         /* 5 */         /* 6 */                 /* 7 */         /* 8 */                 /* 9 */         /* A */         /* B */         /* C - F */
+#define IP2_15_12      FM(A4)                  FM(LCDOUT20)    FM(MSIOF3_SS1_B)        F_(0, 0)                        FM(VI4_DATA12)  FM(VI5_DATA12)  FM(DU_DB4)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_19_16      FM(A5)                  FM(LCDOUT21)    FM(MSIOF3_SS2_B)        FM(SCK4_B)                      FM(VI4_DATA13)  FM(VI5_DATA13)  FM(DU_DB5)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_23_20      FM(A6)                  FM(LCDOUT22)    FM(MSIOF2_SS1_A)        FM(RX4_B)                       FM(VI4_DATA14)  FM(VI5_DATA14)  FM(DU_DB6)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_27_24      FM(A7)                  FM(LCDOUT23)    FM(MSIOF2_SS2_A)        FM(TX4_B)                       FM(VI4_DATA15)  FM(VI5_DATA15)  FM(DU_DB7)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_31_28      FM(A8)                  FM(RX3_B)       FM(MSIOF2_SYNC_A)       FM(HRX4_B)                      F_(0, 0)        F_(0, 0)        F_(0, 0)                FM(SDA6_A)      FM(AVB_AVTP_MATCH_B)    FM(PWM1_B)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_3_0                FM(A9)                  F_(0, 0)        FM(MSIOF2_SCK_A)        FM(CTS4_N_B)                    F_(0, 0)        FM(VI5_VSYNC_N) F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_7_4                FM(A10)                 F_(0, 0)        FM(MSIOF2_RXD_A)        FM(RTS4_N_TANS_B)               F_(0, 0)        FM(VI5_HSYNC_N) F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_11_8       FM(A11)                 FM(TX3_B)       FM(MSIOF2_TXD_A)        FM(HTX4_B)                      FM(HSCK4)       FM(VI5_FIELD)   F_(0, 0)                FM(SCL6_A)      FM(AVB_AVTP_CAPTURE_B)  FM(PWM2_B)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_15_12      FM(A12)                 FM(LCDOUT12)    FM(MSIOF3_SCK_C)        F_(0, 0)                        FM(HRX4_A)      FM(VI5_DATA8)   FM(DU_DG4)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_19_16      FM(A13)                 FM(LCDOUT13)    FM(MSIOF3_SYNC_C)       F_(0, 0)                        FM(HTX4_A)      FM(VI5_DATA9)   FM(DU_DG5)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_23_20      FM(A14)                 FM(LCDOUT14)    FM(MSIOF3_RXD_C)        F_(0, 0)                        FM(HCTS4_N)     FM(VI5_DATA10)  FM(DU_DG6)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_27_24      FM(A15)                 FM(LCDOUT15)    FM(MSIOF3_TXD_C)        F_(0, 0)                        FM(HRTS4_N)     FM(VI5_DATA11)  FM(DU_DG7)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_31_28      FM(A16)                 FM(LCDOUT8)     F_(0, 0)                F_(0, 0)                        FM(VI4_FIELD)   F_(0, 0)        FM(DU_DG0)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_3_0                FM(A17)                 FM(LCDOUT9)     F_(0, 0)                F_(0, 0)                        FM(VI4_VSYNC_N) F_(0, 0)        FM(DU_DG1)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_7_4                FM(A18)                 FM(LCDOUT10)    F_(0, 0)                F_(0, 0)                        FM(VI4_HSYNC_N) F_(0, 0)        FM(DU_DG2)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_11_8       FM(A19)                 FM(LCDOUT11)    F_(0, 0)                F_(0, 0)                        FM(VI4_CLKENB)  F_(0, 0)        FM(DU_DG3)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_15_12      FM(CS0_N)               F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        FM(VI5_CLKENB)  F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_19_16      FM(CS1_N_A26)           F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        FM(VI5_CLK)     F_(0, 0)                FM(EX_WAIT0_B)  F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_23_20      FM(BS_N)                FM(QSTVA_QVS)   FM(MSIOF3_SCK_D)        FM(SCK3)                        FM(HSCK3)       F_(0, 0)        F_(0, 0)                F_(0, 0)        FM(CAN1_TX)             FM(CANFD1_TX)   FM(IETX_A)      F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_27_24      FM(RD_N)                F_(0, 0)        FM(MSIOF3_SYNC_D)       FM(RX3_A)                       FM(HRX3_A)      F_(0, 0)        F_(0, 0)                F_(0, 0)        FM(CAN0_TX_A)           FM(CANFD0_TX_A) F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_31_28      FM(RD_WR_N)             F_(0, 0)        FM(MSIOF3_RXD_D)        FM(TX3_A)                       FM(HTX3_A)      F_(0, 0)        F_(0, 0)                F_(0, 0)        FM(CAN0_RX_A)           FM(CANFD0_RX_A) F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_3_0                FM(WE0_N)               F_(0, 0)        FM(MSIOF3_TXD_D)        FM(CTS3_N)                      FM(HCTS3_N)     F_(0, 0)        F_(0, 0)                FM(SCL6_B)      FM(CAN_CLK)             F_(0, 0)        FM(IECLK_A)     F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_7_4                FM(WE1_N)               F_(0, 0)        FM(MSIOF3_SS1_D)        FM(RTS3_N_TANS)                 FM(HRTS3_N)     F_(0, 0)        F_(0, 0)                FM(SDA6_B)      FM(CAN1_RX)             FM(CANFD1_RX)   FM(IERX_A)      F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_11_8       FM(EX_WAIT0_A)          FM(QCLK)        F_(0, 0)                F_(0, 0)                        FM(VI4_CLK)     F_(0, 0)        FM(DU_DOTCLKOUT0)       F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_15_12      FM(D0)                  FM(MSIOF2_SS1_B)FM(MSIOF3_SCK_A)        F_(0, 0)                        FM(VI4_DATA16)  FM(VI5_DATA0)   F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_19_16      FM(D1)                  FM(MSIOF2_SS2_B)FM(MSIOF3_SYNC_A)       F_(0, 0)                        FM(VI4_DATA17)  FM(VI5_DATA1)   F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_23_20      FM(D2)                  F_(0, 0)        FM(MSIOF3_RXD_A)        F_(0, 0)                        FM(VI4_DATA18)  FM(VI5_DATA2)   F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_27_24      FM(D3)                  F_(0, 0)        FM(MSIOF3_TXD_A)        F_(0, 0)                        FM(VI4_DATA19)  FM(VI5_DATA3)   F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_31_28      FM(D4)                  FM(MSIOF2_SCK_B)F_(0, 0)                F_(0, 0)                        FM(VI4_DATA20)  FM(VI5_DATA4)   F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_3_0                FM(D5)                  FM(MSIOF2_SYNC_B)F_(0, 0)               F_(0, 0)                        FM(VI4_DATA21)  FM(VI5_DATA5)   F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_7_4                FM(D6)                  FM(MSIOF2_RXD_B)F_(0, 0)                F_(0, 0)                        FM(VI4_DATA22)  FM(VI5_DATA6)   F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_11_8       FM(D7)                  FM(MSIOF2_TXD_B)F_(0, 0)                F_(0, 0)                        FM(VI4_DATA23)  FM(VI5_DATA7)   F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_15_12      FM(D8)                  FM(LCDOUT0)     FM(MSIOF2_SCK_D)        FM(SCK4_C)                      FM(VI4_DATA0_A) F_(0, 0)        FM(DU_DR0)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_19_16      FM(D9)                  FM(LCDOUT1)     FM(MSIOF2_SYNC_D)       F_(0, 0)                        FM(VI4_DATA1_A) F_(0, 0)        FM(DU_DR1)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_23_20      FM(D10)                 FM(LCDOUT2)     FM(MSIOF2_RXD_D)        FM(HRX3_B)                      FM(VI4_DATA2_A) FM(CTS4_N_C)    FM(DU_DR2)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_27_24      FM(D11)                 FM(LCDOUT3)     FM(MSIOF2_TXD_D)        FM(HTX3_B)                      FM(VI4_DATA3_A) FM(RTS4_N_TANS_C)FM(DU_DR3)             F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_31_28      FM(D12)                 FM(LCDOUT4)     FM(MSIOF2_SS1_D)        FM(RX4_C)                       FM(VI4_DATA4_A) F_(0, 0)        FM(DU_DR4)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_3_0                FM(D13)                 FM(LCDOUT5)     FM(MSIOF2_SS2_D)        FM(TX4_C)                       FM(VI4_DATA5_A) F_(0, 0)        FM(DU_DR5)              F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_7_4                FM(D14)                 FM(LCDOUT6)     FM(MSIOF3_SS1_A)        FM(HRX3_C)                      FM(VI4_DATA6_A) F_(0, 0)        FM(DU_DR6)              FM(SCL6_C)      F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_11_8       FM(D15)                 FM(LCDOUT7)     FM(MSIOF3_SS2_A)        FM(HTX3_C)                      FM(VI4_DATA7_A) F_(0, 0)        FM(DU_DR7)              FM(SDA6_C)      F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_15_12      FM(FSCLKST)             F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_19_16      FM(SD0_CLK)             F_(0, 0)        FM(MSIOF1_SCK_E)        F_(0, 0)                        F_(0, 0)        F_(0, 0)        FM(STP_OPWM_0_B)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+
+/* IPSRx */            /* 0 */                 /* 1 */         /* 2 */                 /* 3 */                         /* 4 */         /* 5 */         /* 6 */                 /* 7 */         /* 8 */                 /* 9 */         /* A */         /* B */         /* C - F */
+#define IP7_23_20      FM(SD0_CMD)             F_(0, 0)        FM(MSIOF1_SYNC_E)       F_(0, 0)                        F_(0, 0)        F_(0, 0)        FM(STP_IVCXO27_0_B)     F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_27_24      FM(SD0_DAT0)            F_(0, 0)        FM(MSIOF1_RXD_E)        F_(0, 0)                        F_(0, 0)        FM(TS_SCK0_B)   FM(STP_ISCLK_0_B)       F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_31_28      FM(SD0_DAT1)            F_(0, 0)        FM(MSIOF1_TXD_E)        F_(0, 0)                        F_(0, 0)        FM(TS_SPSYNC0_B)FM(STP_ISSYNC_0_B)      F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_3_0                FM(SD0_DAT2)            F_(0, 0)        FM(MSIOF1_SS1_E)        F_(0, 0)                        F_(0, 0)        FM(TS_SDAT0_B)  FM(STP_ISD_0_B)         F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_7_4                FM(SD0_DAT3)            F_(0, 0)        FM(MSIOF1_SS2_E)        F_(0, 0)                        F_(0, 0)        FM(TS_SDEN0_B)  FM(STP_ISEN_0_B)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_11_8       FM(SD1_CLK)             F_(0, 0)        FM(MSIOF1_SCK_G)        F_(0, 0)                        F_(0, 0)        FM(SIM0_CLK_A)  F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_15_12      FM(SD1_CMD)             F_(0, 0)        FM(MSIOF1_SYNC_G)       F_(0, 0)                        F_(0, 0)        FM(SIM0_D_A)    FM(STP_IVCXO27_1_B)     F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_19_16      FM(SD1_DAT0)            FM(SD2_DAT4)    FM(MSIOF1_RXD_G)        F_(0, 0)                        F_(0, 0)        FM(TS_SCK1_B)   FM(STP_ISCLK_1_B)       F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_23_20      FM(SD1_DAT1)            FM(SD2_DAT5)    FM(MSIOF1_TXD_G)        F_(0, 0)                        F_(0, 0)        FM(TS_SPSYNC1_B)FM(STP_ISSYNC_1_B)      F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_27_24      FM(SD1_DAT2)            FM(SD2_DAT6)    FM(MSIOF1_SS1_G)        F_(0, 0)                        F_(0, 0)        FM(TS_SDAT1_B)  FM(STP_ISD_1_B)         F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_31_28      FM(SD1_DAT3)            FM(SD2_DAT7)    FM(MSIOF1_SS2_G)        F_(0, 0)                        F_(0, 0)        FM(TS_SDEN1_B)  FM(STP_ISEN_1_B)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_3_0                FM(SD2_CLK)             F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_7_4                FM(SD2_DAT0)            F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_11_8       FM(SD2_DAT1)            F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_15_12      FM(SD2_DAT2)            F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_19_16      FM(SD2_DAT3)            F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_23_20      FM(SD2_DS)              F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        FM(SATA_DEVSLP_B)       F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_27_24      FM(SD3_DAT4)            FM(SD2_CD_A)    F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_31_28      FM(SD3_DAT5)            FM(SD2_WP_A)    F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_3_0       FM(SD3_DAT6)            FM(SD3_CD)      F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_7_4       FM(SD3_DAT7)            FM(SD3_WP)      F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_11_8      FM(SD0_CD)              F_(0, 0)        F_(0, 0)                F_(0, 0)                        FM(SCL2_B)      FM(SIM0_RST_A)  F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_15_12     FM(SD0_WP)              F_(0, 0)        F_(0, 0)                F_(0, 0)                        FM(SDA2_B)      F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_19_16     FM(SD1_CD)              F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        FM(SIM0_CLK_B)  F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_23_20     FM(SD1_WP)              F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        FM(SIM0_D_B)    F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_27_24     FM(SCK0)                FM(HSCK1_B)     FM(MSIOF1_SS2_B)        FM(AUDIO_CLKC_B)                FM(SDA2_A)      FM(SIM0_RST_B)  FM(STP_OPWM_0_C)        FM(RIF0_CLK_B)  F_(0, 0)                FM(ADICHS2)     F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_31_28     FM(RX0)                 FM(HRX1_B)      F_(0, 0)                F_(0, 0)                        F_(0, 0)        FM(TS_SCK0_C)   FM(STP_ISCLK_0_C)       FM(RIF0_D0_B)   F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_3_0       FM(TX0)                 FM(HTX1_B)      F_(0, 0)                F_(0, 0)                        F_(0, 0)        FM(TS_SPSYNC0_C)FM(STP_ISSYNC_0_C)      FM(RIF0_D1_B)   F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_7_4       FM(CTS0_N)              FM(HCTS1_N_B)   FM(MSIOF1_SYNC_B)       F_(0, 0)                        F_(0, 0)        FM(TS_SPSYNC1_C)FM(STP_ISSYNC_1_C)      FM(RIF1_SYNC_B) FM(AUDIO_CLKOUT_C)      FM(ADICS_SAMP)  F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_11_8      FM(RTS0_N_TANS)         FM(HRTS1_N_B)   FM(MSIOF1_SS1_B)        FM(AUDIO_CLKA_B)                FM(SCL2_A)      F_(0, 0)        FM(STP_IVCXO27_1_C)     FM(RIF0_SYNC_B) F_(0, 0)                FM(ADICHS1)     F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_15_12     FM(RX1_A)               FM(HRX1_A)      F_(0, 0)                F_(0, 0)                        F_(0, 0)        FM(TS_SDAT0_C)  FM(STP_ISD_0_C)         FM(RIF1_CLK_C)  F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_19_16     FM(TX1_A)               FM(HTX1_A)      F_(0, 0)                F_(0, 0)                        F_(0, 0)        FM(TS_SDEN0_C)  FM(STP_ISEN_0_C)        FM(RIF1_D0_C)   F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_23_20     FM(CTS1_N)              FM(HCTS1_N_A)   FM(MSIOF1_RXD_B)        F_(0, 0)                        F_(0, 0)        FM(TS_SDEN1_C)  FM(STP_ISEN_1_C)        FM(RIF1_D0_B)   F_(0, 0)                FM(ADIDATA)     F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_27_24     FM(RTS1_N_TANS)         FM(HRTS1_N_A)   FM(MSIOF1_TXD_B)        F_(0, 0)                        F_(0, 0)        FM(TS_SDAT1_C)  FM(STP_ISD_1_C)         FM(RIF1_D1_B)   F_(0, 0)                FM(ADICHS0)     F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_31_28     FM(SCK2)                FM(SCIF_CLK_B)  FM(MSIOF1_SCK_B)        F_(0, 0)                        F_(0, 0)        FM(TS_SCK1_C)   FM(STP_ISCLK_1_C)       FM(RIF1_CLK_B)  F_(0, 0)                FM(ADICLK)      F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_3_0       FM(TX2_A)               F_(0, 0)        F_(0, 0)                FM(SD2_CD_B)                    FM(SCL1_A)      F_(0, 0)        FM(FMCLK_A)             FM(RIF1_D1_C)   F_(0, 0)                FM(FSO_CFE_0_B) F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_7_4       FM(RX2_A)               F_(0, 0)        F_(0, 0)                FM(SD2_WP_B)                    FM(SDA1_A)      F_(0, 0)        FM(FMIN_A)              FM(RIF1_SYNC_C) F_(0, 0)                FM(FSO_CFE_1_B) F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_11_8      FM(HSCK0)               F_(0, 0)        FM(MSIOF1_SCK_D)        FM(AUDIO_CLKB_A)                FM(SSI_SDATA1_B)FM(TS_SCK0_D)   FM(STP_ISCLK_0_D)       FM(RIF0_CLK_C)  F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_15_12     FM(HRX0)                F_(0, 0)        FM(MSIOF1_RXD_D)        F_(0, 0)                        FM(SSI_SDATA2_B)FM(TS_SDEN0_D)  FM(STP_ISEN_0_D)        FM(RIF0_D0_C)   F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_19_16     FM(HTX0)                F_(0, 0)        FM(MSIOF1_TXD_D)        F_(0, 0)                        FM(SSI_SDATA9_B)FM(TS_SDAT0_D)  FM(STP_ISD_0_D)         FM(RIF0_D1_C)   F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_23_20     FM(HCTS0_N)             FM(RX2_B)       FM(MSIOF1_SYNC_D)       F_(0, 0)                        FM(SSI_SCK9_A)  FM(TS_SPSYNC0_D)FM(STP_ISSYNC_0_D)      FM(RIF0_SYNC_C) FM(AUDIO_CLKOUT1_A)     F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_27_24     FM(HRTS0_N)             FM(TX2_B)       FM(MSIOF1_SS1_D)        F_(0, 0)                        FM(SSI_WS9_A)   F_(0, 0)        FM(STP_IVCXO27_0_D)     FM(BPFCLK_A)    FM(AUDIO_CLKOUT2_A)     F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+
+/* IPSRx */            /* 0 */                 /* 1 */         /* 2 */                 /* 3 */                         /* 4 */         /* 5 */         /* 6 */                 /* 7 */         /* 8 */                 /* 9 */         /* A */         /* B */         /* C - F */
+#define IP12_31_28     FM(MSIOF0_SYNC)         F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        FM(AUDIO_CLKOUT_A)      F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_3_0       FM(MSIOF0_SS1)          FM(RX5)         F_(0, 0)                FM(AUDIO_CLKA_C)                FM(SSI_SCK2_A)  F_(0, 0)        FM(STP_IVCXO27_0_C)     F_(0, 0)        FM(AUDIO_CLKOUT3_A)     F_(0, 0)        FM(TCLK1_B)     F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_7_4       FM(MSIOF0_SS2)          FM(TX5)         FM(MSIOF1_SS2_D)        FM(AUDIO_CLKC_A)                FM(SSI_WS2_A)   F_(0, 0)        FM(STP_OPWM_0_D)        F_(0, 0)        FM(AUDIO_CLKOUT_D)      F_(0, 0)        FM(SPEEDIN_B)   F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_11_8      FM(MLB_CLK)             F_(0, 0)        FM(MSIOF1_SCK_F)        F_(0, 0)                        FM(SCL1_B)      F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_15_12     FM(MLB_SIG)             FM(RX1_B)       FM(MSIOF1_SYNC_F)       F_(0, 0)                        FM(SDA1_B)      F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_19_16     FM(MLB_DAT)             FM(TX1_B)       FM(MSIOF1_RXD_F)        F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_23_20     FM(SSI_SCK0129)         F_(0, 0)        FM(MSIOF1_TXD_F)        F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_27_24     FM(SSI_WS0129)          F_(0, 0)        FM(MSIOF1_SS1_F)        F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_31_28     FM(SSI_SDATA0)          F_(0, 0)        FM(MSIOF1_SS2_F)        F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_3_0       FM(SSI_SDATA1_A)        F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_7_4       FM(SSI_SDATA2_A)        F_(0, 0)        F_(0, 0)                F_(0, 0)                        FM(SSI_SCK1_B)  F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_11_8      FM(SSI_SCK34)           F_(0, 0)        FM(MSIOF1_SS1_A)        F_(0, 0)                        F_(0, 0)        F_(0, 0)        FM(STP_OPWM_0_A)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_15_12     FM(SSI_WS34)            FM(HCTS2_N_A)   FM(MSIOF1_SS2_A)        F_(0, 0)                        F_(0, 0)        F_(0, 0)        FM(STP_IVCXO27_0_A)     F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_19_16     FM(SSI_SDATA3)          FM(HRTS2_N_A)   FM(MSIOF1_TXD_A)        F_(0, 0)                        F_(0, 0)        FM(TS_SCK0_A)   FM(STP_ISCLK_0_A)       FM(RIF0_D1_A)   FM(RIF2_D0_A)           F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_23_20     FM(SSI_SCK4)            FM(HRX2_A)      FM(MSIOF1_SCK_A)        F_(0, 0)                        F_(0, 0)        FM(TS_SDAT0_A)  FM(STP_ISD_0_A)         FM(RIF0_CLK_A)  FM(RIF2_CLK_A)          F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_27_24     FM(SSI_WS4)             FM(HTX2_A)      FM(MSIOF1_SYNC_A)       F_(0, 0)                        F_(0, 0)        FM(TS_SDEN0_A)  FM(STP_ISEN_0_A)        FM(RIF0_SYNC_A) FM(RIF2_SYNC_A)         F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_31_28     FM(SSI_SDATA4)          FM(HSCK2_A)     FM(MSIOF1_RXD_A)        F_(0, 0)                        F_(0, 0)        FM(TS_SPSYNC0_A)FM(STP_ISSYNC_0_A)      FM(RIF0_D0_A)   FM(RIF2_D1_A)           F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_3_0       FM(SSI_SCK6)            FM(USB2_PWEN)   F_(0, 0)                FM(SIM0_RST_D)                  F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_7_4       FM(SSI_WS6)             FM(USB2_OVC)    F_(0, 0)                FM(SIM0_D_D)                    F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_11_8      FM(SSI_SDATA6)          F_(0, 0)        F_(0, 0)                FM(SIM0_CLK_D)                  F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        FM(SATA_DEVSLP_A)       F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_15_12     FM(SSI_SCK78)           FM(HRX2_B)      FM(MSIOF1_SCK_C)        F_(0, 0)                        F_(0, 0)        FM(TS_SCK1_A)   FM(STP_ISCLK_1_A)       FM(RIF1_CLK_A)  FM(RIF3_CLK_A)          F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_19_16     FM(SSI_WS78)            FM(HTX2_B)      FM(MSIOF1_SYNC_C)       F_(0, 0)                        F_(0, 0)        FM(TS_SDAT1_A)  FM(STP_ISD_1_A)         FM(RIF1_SYNC_A) FM(RIF3_SYNC_A)         F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_23_20     FM(SSI_SDATA7)          FM(HCTS2_N_B)   FM(MSIOF1_RXD_C)        F_(0, 0)                        F_(0, 0)        FM(TS_SDEN1_A)  FM(STP_ISEN_1_A)        FM(RIF1_D0_A)   FM(RIF3_D0_A)           F_(0, 0)        FM(TCLK2_A)     F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_27_24     FM(SSI_SDATA8)          FM(HRTS2_N_B)   FM(MSIOF1_TXD_C)        F_(0, 0)                        F_(0, 0)        FM(TS_SPSYNC1_A)FM(STP_ISSYNC_1_A)      FM(RIF1_D1_A)   FM(RIF3_D1_A)           F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_31_28     FM(SSI_SDATA9_A)        FM(HSCK2_B)     FM(MSIOF1_SS1_C)        FM(HSCK1_A)                     FM(SSI_WS1_B)   FM(SCK1)        FM(STP_IVCXO27_1_A)     FM(SCK5)        F_(0, 0)                F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_3_0       FM(AUDIO_CLKA_A)        F_(0, 0)        F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)                F_(0, 0)        F_(0, 0)        FM(CC5_OSCOUT)  F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_7_4       FM(AUDIO_CLKB_B)        FM(SCIF_CLK_A)  F_(0, 0)                F_(0, 0)                        F_(0, 0)        F_(0, 0)        FM(STP_IVCXO27_1_D)     FM(REMOCON_A)   F_(0, 0)                F_(0, 0)        FM(TCLK1_A)     F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_11_8      FM(USB0_PWEN)           F_(0, 0)        F_(0, 0)                FM(SIM0_RST_C)                  F_(0, 0)        FM(TS_SCK1_D)   FM(STP_ISCLK_1_D)       FM(BPFCLK_B)    FM(RIF3_CLK_B)          F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_15_12     FM(USB0_OVC)            F_(0, 0)        F_(0, 0)                FM(SIM0_D_C)                    F_(0, 0)        FM(TS_SDAT1_D)  FM(STP_ISD_1_D)         F_(0, 0)        FM(RIF3_SYNC_B)         F_(0, 0)        F_(0, 0)        F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_19_16     FM(USB1_PWEN)           F_(0, 0)        F_(0, 0)                FM(SIM0_CLK_C)                  FM(SSI_SCK1_A)  FM(TS_SCK0_E)   FM(STP_ISCLK_0_E)       FM(FMCLK_B)     FM(RIF2_CLK_B)          F_(0, 0)        FM(SPEEDIN_A)   F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_23_20     FM(USB1_OVC)            F_(0, 0)        FM(MSIOF1_SS2_C)        F_(0, 0)                        FM(SSI_WS1_A)   FM(TS_SDAT0_E)  FM(STP_ISD_0_E)         FM(FMIN_B)      FM(RIF2_SYNC_B)         F_(0, 0)        FM(REMOCON_B)   F_(0, 0)        F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_27_24     FM(USB30_PWEN)          F_(0, 0)        F_(0, 0)                FM(AUDIO_CLKOUT_B)              FM(SSI_SCK2_B)  FM(TS_SDEN1_D)  FM(STP_ISEN_1_D)        FM(STP_OPWM_0_E)FM(RIF3_D0_B)           F_(0, 0)        FM(TCLK2_B)     FM(TPU0TO0)     F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_31_28     FM(USB30_OVC)           F_(0, 0)        F_(0, 0)                FM(AUDIO_CLKOUT1_B)             FM(SSI_WS2_B)   FM(TS_SPSYNC1_D)FM(STP_ISSYNC_1_D)      FM(STP_IVCXO27_0_E)FM(RIF3_D1_B)        F_(0, 0)        FM(FSO_TOE_B)   FM(TPU0TO1)     F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP17_3_0       FM(USB31_PWEN)          F_(0, 0)        F_(0, 0)                FM(AUDIO_CLKOUT2_B)             FM(SSI_SCK9_B)  FM(TS_SDEN0_E)  FM(STP_ISEN_0_E)        F_(0, 0)        FM(RIF2_D0_B)           F_(0, 0)        F_(0, 0)        FM(TPU0TO2)     F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP17_7_4       FM(USB31_OVC)           F_(0, 0)        F_(0, 0)                FM(AUDIO_CLKOUT3_B)             FM(SSI_WS9_B)   FM(TS_SPSYNC0_E)FM(STP_ISSYNC_0_E)      F_(0, 0)        FM(RIF2_D1_B)           F_(0, 0)        F_(0, 0)        FM(TPU0TO3)     F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+
+#define PINMUX_GPSR    \
+\
+                                                                                               GPSR6_31 \
+                                                                                               GPSR6_30 \
+                                                                                               GPSR6_29 \
+                                                                                               GPSR6_28 \
+               GPSR1_27                                                                        GPSR6_27 \
+               GPSR1_26                                                                        GPSR6_26 \
+               GPSR1_25                                                        GPSR5_25        GPSR6_25 \
+               GPSR1_24                                                        GPSR5_24        GPSR6_24 \
+               GPSR1_23                                                        GPSR5_23        GPSR6_23 \
+               GPSR1_22                                                        GPSR5_22        GPSR6_22 \
+               GPSR1_21                                                        GPSR5_21        GPSR6_21 \
+               GPSR1_20                                                        GPSR5_20        GPSR6_20 \
+               GPSR1_19                                                        GPSR5_19        GPSR6_19 \
+               GPSR1_18                                                        GPSR5_18        GPSR6_18 \
+               GPSR1_17                                        GPSR4_17        GPSR5_17        GPSR6_17 \
+               GPSR1_16                                        GPSR4_16        GPSR5_16        GPSR6_16 \
+GPSR0_15       GPSR1_15                        GPSR3_15        GPSR4_15        GPSR5_15        GPSR6_15 \
+GPSR0_14       GPSR1_14        GPSR2_14        GPSR3_14        GPSR4_14        GPSR5_14        GPSR6_14 \
+GPSR0_13       GPSR1_13        GPSR2_13        GPSR3_13        GPSR4_13        GPSR5_13        GPSR6_13 \
+GPSR0_12       GPSR1_12        GPSR2_12        GPSR3_12        GPSR4_12        GPSR5_12        GPSR6_12 \
+GPSR0_11       GPSR1_11        GPSR2_11        GPSR3_11        GPSR4_11        GPSR5_11        GPSR6_11 \
+GPSR0_10       GPSR1_10        GPSR2_10        GPSR3_10        GPSR4_10        GPSR5_10        GPSR6_10 \
+GPSR0_9                GPSR1_9         GPSR2_9         GPSR3_9         GPSR4_9         GPSR5_9         GPSR6_9 \
+GPSR0_8                GPSR1_8         GPSR2_8         GPSR3_8         GPSR4_8         GPSR5_8         GPSR6_8 \
+GPSR0_7                GPSR1_7         GPSR2_7         GPSR3_7         GPSR4_7         GPSR5_7         GPSR6_7 \
+GPSR0_6                GPSR1_6         GPSR2_6         GPSR3_6         GPSR4_6         GPSR5_6         GPSR6_6 \
+GPSR0_5                GPSR1_5         GPSR2_5         GPSR3_5         GPSR4_5         GPSR5_5         GPSR6_5 \
+GPSR0_4                GPSR1_4         GPSR2_4         GPSR3_4         GPSR4_4         GPSR5_4         GPSR6_4 \
+GPSR0_3                GPSR1_3         GPSR2_3         GPSR3_3         GPSR4_3         GPSR5_3         GPSR6_3         GPSR7_3 \
+GPSR0_2                GPSR1_2         GPSR2_2         GPSR3_2         GPSR4_2         GPSR5_2         GPSR6_2         GPSR7_2 \
+GPSR0_1                GPSR1_1         GPSR2_1         GPSR3_1         GPSR4_1         GPSR5_1         GPSR6_1         GPSR7_1 \
+GPSR0_0                GPSR1_0         GPSR2_0         GPSR3_0         GPSR4_0         GPSR5_0         GPSR6_0         GPSR7_0
+
+#define PINMUX_IPSR                            \
+\
+FM(IP0_3_0)    IP0_3_0         FM(IP1_3_0)     IP1_3_0         FM(IP2_3_0)     IP2_3_0         FM(IP3_3_0)     IP3_3_0 \
+FM(IP0_7_4)    IP0_7_4         FM(IP1_7_4)     IP1_7_4         FM(IP2_7_4)     IP2_7_4         FM(IP3_7_4)     IP3_7_4 \
+FM(IP0_11_8)   IP0_11_8        FM(IP1_11_8)    IP1_11_8        FM(IP2_11_8)    IP2_11_8        FM(IP3_11_8)    IP3_11_8 \
+FM(IP0_15_12)  IP0_15_12       FM(IP1_15_12)   IP1_15_12       FM(IP2_15_12)   IP2_15_12       FM(IP3_15_12)   IP3_15_12 \
+FM(IP0_19_16)  IP0_19_16       FM(IP1_19_16)   IP1_19_16       FM(IP2_19_16)   IP2_19_16       FM(IP3_19_16)   IP3_19_16 \
+FM(IP0_23_20)  IP0_23_20       FM(IP1_23_20)   IP1_23_20       FM(IP2_23_20)   IP2_23_20       FM(IP3_23_20)   IP3_23_20 \
+FM(IP0_27_24)  IP0_27_24       FM(IP1_27_24)   IP1_27_24       FM(IP2_27_24)   IP2_27_24       FM(IP3_27_24)   IP3_27_24 \
+FM(IP0_31_28)  IP0_31_28       FM(IP1_31_28)   IP1_31_28       FM(IP2_31_28)   IP2_31_28       FM(IP3_31_28)   IP3_31_28 \
+\
+FM(IP4_3_0)    IP4_3_0         FM(IP5_3_0)     IP5_3_0         FM(IP6_3_0)     IP6_3_0         FM(IP7_3_0)     IP7_3_0 \
+FM(IP4_7_4)    IP4_7_4         FM(IP5_7_4)     IP5_7_4         FM(IP6_7_4)     IP6_7_4         FM(IP7_7_4)     IP7_7_4 \
+FM(IP4_11_8)   IP4_11_8        FM(IP5_11_8)    IP5_11_8        FM(IP6_11_8)    IP6_11_8        FM(IP7_11_8)    IP7_11_8 \
+FM(IP4_15_12)  IP4_15_12       FM(IP5_15_12)   IP5_15_12       FM(IP6_15_12)   IP6_15_12       FM(IP7_15_12)   IP7_15_12 \
+FM(IP4_19_16)  IP4_19_16       FM(IP5_19_16)   IP5_19_16       FM(IP6_19_16)   IP6_19_16       FM(IP7_19_16)   IP7_19_16 \
+FM(IP4_23_20)  IP4_23_20       FM(IP5_23_20)   IP5_23_20       FM(IP6_23_20)   IP6_23_20       FM(IP7_23_20)   IP7_23_20 \
+FM(IP4_27_24)  IP4_27_24       FM(IP5_27_24)   IP5_27_24       FM(IP6_27_24)   IP6_27_24       FM(IP7_27_24)   IP7_27_24 \
+FM(IP4_31_28)  IP4_31_28       FM(IP5_31_28)   IP5_31_28       FM(IP6_31_28)   IP6_31_28       FM(IP7_31_28)   IP7_31_28 \
+\
+FM(IP8_3_0)    IP8_3_0         FM(IP9_3_0)     IP9_3_0         FM(IP10_3_0)    IP10_3_0        FM(IP11_3_0)    IP11_3_0 \
+FM(IP8_7_4)    IP8_7_4         FM(IP9_7_4)     IP9_7_4         FM(IP10_7_4)    IP10_7_4        FM(IP11_7_4)    IP11_7_4 \
+FM(IP8_11_8)   IP8_11_8        FM(IP9_11_8)    IP9_11_8        FM(IP10_11_8)   IP10_11_8       FM(IP11_11_8)   IP11_11_8 \
+FM(IP8_15_12)  IP8_15_12       FM(IP9_15_12)   IP9_15_12       FM(IP10_15_12)  IP10_15_12      FM(IP11_15_12)  IP11_15_12 \
+FM(IP8_19_16)  IP8_19_16       FM(IP9_19_16)   IP9_19_16       FM(IP10_19_16)  IP10_19_16      FM(IP11_19_16)  IP11_19_16 \
+FM(IP8_23_20)  IP8_23_20       FM(IP9_23_20)   IP9_23_20       FM(IP10_23_20)  IP10_23_20      FM(IP11_23_20)  IP11_23_20 \
+FM(IP8_27_24)  IP8_27_24       FM(IP9_27_24)   IP9_27_24       FM(IP10_27_24)  IP10_27_24      FM(IP11_27_24)  IP11_27_24 \
+FM(IP8_31_28)  IP8_31_28       FM(IP9_31_28)   IP9_31_28       FM(IP10_31_28)  IP10_31_28      FM(IP11_31_28)  IP11_31_28 \
+\
+FM(IP12_3_0)   IP12_3_0        FM(IP13_3_0)    IP13_3_0        FM(IP14_3_0)    IP14_3_0        FM(IP15_3_0)    IP15_3_0 \
+FM(IP12_7_4)   IP12_7_4        FM(IP13_7_4)    IP13_7_4        FM(IP14_7_4)    IP14_7_4        FM(IP15_7_4)    IP15_7_4 \
+FM(IP12_11_8)  IP12_11_8       FM(IP13_11_8)   IP13_11_8       FM(IP14_11_8)   IP14_11_8       FM(IP15_11_8)   IP15_11_8 \
+FM(IP12_15_12) IP12_15_12      FM(IP13_15_12)  IP13_15_12      FM(IP14_15_12)  IP14_15_12      FM(IP15_15_12)  IP15_15_12 \
+FM(IP12_19_16) IP12_19_16      FM(IP13_19_16)  IP13_19_16      FM(IP14_19_16)  IP14_19_16      FM(IP15_19_16)  IP15_19_16 \
+FM(IP12_23_20) IP12_23_20      FM(IP13_23_20)  IP13_23_20      FM(IP14_23_20)  IP14_23_20      FM(IP15_23_20)  IP15_23_20 \
+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 \
+\
+FM(IP16_3_0)   IP16_3_0        FM(IP17_3_0)    IP17_3_0 \
+FM(IP16_7_4)   IP16_7_4        FM(IP17_7_4)    IP17_7_4 \
+FM(IP16_11_8)  IP16_11_8 \
+FM(IP16_15_12) IP16_15_12 \
+FM(IP16_19_16) IP16_19_16 \
+FM(IP16_23_20) IP16_23_20 \
+FM(IP16_27_24) IP16_27_24 \
+FM(IP16_31_28) IP16_31_28
+
+/* MOD_SEL0 */                 /* 0 */                 /* 1 */                 /* 2 */                 /* 3 */                 /* 4 */                 /* 5 */                 /* 6 */                 /* 7 */
+#define MOD_SEL0_30_29         FM(SEL_MSIOF3_0)        FM(SEL_MSIOF3_1)        FM(SEL_MSIOF3_2)        FM(SEL_MSIOF3_3)
+#define MOD_SEL0_28_27         FM(SEL_MSIOF2_0)        FM(SEL_MSIOF2_1)        FM(SEL_MSIOF2_2)        FM(SEL_MSIOF2_3)
+#define MOD_SEL0_26_25_24      FM(SEL_MSIOF1_0)        FM(SEL_MSIOF1_1)        FM(SEL_MSIOF1_2)        FM(SEL_MSIOF1_3)        FM(SEL_MSIOF1_4)        FM(SEL_MSIOF1_5)        FM(SEL_MSIOF1_6)        F_(0, 0)
+#define MOD_SEL0_23            FM(SEL_LBSC_0)          FM(SEL_LBSC_1)
+#define MOD_SEL0_22            FM(SEL_IEBUS_0)         FM(SEL_IEBUS_1)
+#define MOD_SEL0_21_20         FM(SEL_I2C6_0)          FM(SEL_I2C6_1)          FM(SEL_I2C6_2)          F_(0, 0)
+#define MOD_SEL0_19            FM(SEL_I2C2_0)          FM(SEL_I2C2_1)
+#define MOD_SEL0_18            FM(SEL_I2C1_0)          FM(SEL_I2C1_1)
+#define MOD_SEL0_17            FM(SEL_HSCIF4_0)        FM(SEL_HSCIF4_1)
+#define MOD_SEL0_16_15         FM(SEL_HSCIF3_0)        FM(SEL_HSCIF3_1)        FM(SEL_HSCIF3_2)        FM(SEL_HSCIF3_3)
+#define MOD_SEL0_14            FM(SEL_HSCIF2_0)        FM(SEL_HSCIF2_1)
+#define MOD_SEL0_13            FM(SEL_HSCIF1_0)        FM(SEL_HSCIF1_1)
+#define MOD_SEL0_12            FM(SEL_FSO_0)           FM(SEL_FSO_1)
+#define MOD_SEL0_11            FM(SEL_FM_0)            FM(SEL_FM_1)
+#define MOD_SEL0_10            FM(SEL_ETHERAVB_0)      FM(SEL_ETHERAVB_1)
+#define MOD_SEL0_9             FM(SEL_DRIF3_0)         FM(SEL_DRIF3_1)
+#define MOD_SEL0_8             FM(SEL_DRIF2_0)         FM(SEL_DRIF2_1)
+#define MOD_SEL0_7_6           FM(SEL_DRIF1_0)         FM(SEL_DRIF1_1)         FM(SEL_DRIF1_2)         F_(0, 0)
+#define MOD_SEL0_5_4           FM(SEL_DRIF0_0)         FM(SEL_DRIF0_1)         FM(SEL_DRIF0_2)         F_(0, 0)
+#define MOD_SEL0_3             FM(SEL_CANFD0_0)        FM(SEL_CANFD0_1)
+#define MOD_SEL0_2_1           FM(SEL_ADG_0)           FM(SEL_ADG_1)           FM(SEL_ADG_2)           FM(SEL_ADG_3)
+
+/* MOD_SEL1 */                 /* 0 */                 /* 1 */                 /* 2 */                 /* 3 */                 /* 4 */                 /* 5 */                 /* 6 */                 /* 7 */
+#define MOD_SEL1_31_30         FM(SEL_TSIF1_0)         FM(SEL_TSIF1_1)         FM(SEL_TSIF1_2)         FM(SEL_TSIF1_3)
+#define MOD_SEL1_29_28_27      FM(SEL_TSIF0_0)         FM(SEL_TSIF0_1)         FM(SEL_TSIF0_2)         FM(SEL_TSIF0_3)         FM(SEL_TSIF0_4)         F_(0, 0)                F_(0, 0)                F_(0, 0)
+#define MOD_SEL1_26            FM(SEL_TIMER_TMU_0)     FM(SEL_TIMER_TMU_1)
+#define MOD_SEL1_25_24         FM(SEL_SSP1_1_0)        FM(SEL_SSP1_1_1)        FM(SEL_SSP1_1_2)        FM(SEL_SSP1_1_3)
+#define MOD_SEL1_23_22_21      FM(SEL_SSP1_0_0)        FM(SEL_SSP1_0_1)        FM(SEL_SSP1_0_2)        FM(SEL_SSP1_0_3)        FM(SEL_SSP1_0_4)        F_(0, 0)                F_(0, 0)                F_(0, 0)
+#define MOD_SEL1_20            FM(SEL_SSI_0)           FM(SEL_SSI_1)
+#define MOD_SEL1_19            FM(SEL_SPEED_PULSE_0)   FM(SEL_SPEED_PULSE_1)
+#define MOD_SEL1_18_17         FM(SEL_SIMCARD_0)       FM(SEL_SIMCARD_1)       FM(SEL_SIMCARD_2)       FM(SEL_SIMCARD_3)
+#define MOD_SEL1_16            FM(SEL_SDHI2_0)         FM(SEL_SDHI2_1)
+#define MOD_SEL1_15_14         FM(SEL_SCIF4_0)         FM(SEL_SCIF4_1)         FM(SEL_SCIF4_2)         F_(0, 0)
+#define MOD_SEL1_13            FM(SEL_SCIF3_0)         FM(SEL_SCIF3_1)
+#define MOD_SEL1_12            FM(SEL_SCIF2_0)         FM(SEL_SCIF2_1)
+#define MOD_SEL1_11            FM(SEL_SCIF1_0)         FM(SEL_SCIF1_1)
+#define MOD_SEL1_10            FM(SEL_SCIF_0)          FM(SEL_SCIF_1)
+#define MOD_SEL1_9             FM(SEL_REMOCON_0)       FM(SEL_REMOCON_1)
+#define MOD_SEL1_6             FM(SEL_RCAN0_0)         FM(SEL_RCAN0_1)
+#define MOD_SEL1_5             FM(SEL_PWM6_0)          FM(SEL_PWM6_1)
+#define MOD_SEL1_4             FM(SEL_PWM5_0)          FM(SEL_PWM5_1)
+#define MOD_SEL1_3             FM(SEL_PWM4_0)          FM(SEL_PWM4_1)
+#define MOD_SEL1_2             FM(SEL_PWM3_0)          FM(SEL_PWM3_1)
+#define MOD_SEL1_1             FM(SEL_PWM2_0)          FM(SEL_PWM2_1)
+#define MOD_SEL1_0             FM(SEL_PWM1_0)          FM(SEL_PWM1_1)
+
+/* MOD_SEL2 */                 /* 0 */                 /* 1 */                 /* 2 */                 /* 3 */
+#define MOD_SEL2_31            FM(I2C_SEL_5_0)         FM(I2C_SEL_5_1)
+#define MOD_SEL2_30            FM(I2C_SEL_3_0)         FM(I2C_SEL_3_1)
+#define MOD_SEL2_29            FM(I2C_SEL_0_0)         FM(I2C_SEL_0_1)
+#define MOD_SEL2_2_1           FM(SEL_VSP_0)           FM(SEL_VSP_1)           FM(SEL_VSP_2)           FM(SEL_VSP_3)
+#define MOD_SEL2_0             FM(SEL_VIN4_0)          FM(SEL_VIN4_1)
+
+#define PINMUX_MOD_SELS\
+\
+                       MOD_SEL1_31_30          MOD_SEL2_31 \
+MOD_SEL0_30_29                                 MOD_SEL2_30 \
+                       MOD_SEL1_29_28_27       MOD_SEL2_29 \
+MOD_SEL0_28_27 \
+\
+MOD_SEL0_26_25_24      MOD_SEL1_26 \
+                       MOD_SEL1_25_24 \
+\
+MOD_SEL0_23            MOD_SEL1_23_22_21 \
+MOD_SEL0_22 \
+MOD_SEL0_21_20 \
+                       MOD_SEL1_20 \
+MOD_SEL0_19            MOD_SEL1_19 \
+MOD_SEL0_18            MOD_SEL1_18_17 \
+MOD_SEL0_17 \
+MOD_SEL0_16_15         MOD_SEL1_16 \
+                       MOD_SEL1_15_14 \
+MOD_SEL0_14 \
+MOD_SEL0_13            MOD_SEL1_13 \
+MOD_SEL0_12            MOD_SEL1_12 \
+MOD_SEL0_11            MOD_SEL1_11 \
+MOD_SEL0_10            MOD_SEL1_10 \
+MOD_SEL0_9             MOD_SEL1_9 \
+MOD_SEL0_8 \
+MOD_SEL0_7_6 \
+                       MOD_SEL1_6 \
+MOD_SEL0_5_4           MOD_SEL1_5 \
+                       MOD_SEL1_4 \
+MOD_SEL0_3             MOD_SEL1_3 \
+MOD_SEL0_2_1           MOD_SEL1_2              MOD_SEL2_2_1 \
+                       MOD_SEL1_1 \
+                       MOD_SEL1_0              MOD_SEL2_0
+
+
+enum {
+       PINMUX_RESERVED = 0,
+
+       PINMUX_DATA_BEGIN,
+       GP_ALL(DATA),
+       PINMUX_DATA_END,
+
+#define F_(x, y)
+#define FM(x)  FN_##x,
+       PINMUX_FUNCTION_BEGIN,
+       GP_ALL(FN),
+       PINMUX_GPSR
+       PINMUX_IPSR
+       PINMUX_MOD_SELS
+       PINMUX_FUNCTION_END,
+#undef F_
+#undef FM
+
+#define F_(x, y)
+#define FM(x)  x##_MARK,
+       PINMUX_MARK_BEGIN,
+       PINMUX_GPSR
+       PINMUX_IPSR
+       PINMUX_MOD_SELS
+       PINMUX_MARK_END,
+#undef F_
+#undef FM
+};
+
+static const u16 pinmux_data[] = {
+       PINMUX_DATA_GP_ALL(),
+
+       /* IPSR0 */
+       PINMUX_IPSR_DATA(IP0_3_0,       AVB_MDC),
+       PINMUX_IPSR_MSEL(IP0_3_0,       MSIOF2_SS2_C,           SEL_MSIOF2_2),
+
+       PINMUX_IPSR_DATA(IP0_7_4,       AVB_MAGIC),
+       PINMUX_IPSR_MSEL(IP0_7_4,       MSIOF2_SS1_C,           SEL_MSIOF2_2),
+       PINMUX_IPSR_MSEL(IP0_7_4,       SCK4_A,                 SEL_SCIF4_0),
+
+       PINMUX_IPSR_DATA(IP0_11_8,      AVB_PHY_INT),
+       PINMUX_IPSR_MSEL(IP0_11_8,      MSIOF2_SYNC_C,          SEL_MSIOF2_2),
+       PINMUX_IPSR_MSEL(IP0_11_8,      RX4_A,                  SEL_SCIF4_0),
+
+       PINMUX_IPSR_DATA(IP0_15_12,     AVB_LINK),
+       PINMUX_IPSR_MSEL(IP0_15_12,     MSIOF2_SCK_C,           SEL_MSIOF2_2),
+       PINMUX_IPSR_MSEL(IP0_15_12,     TX4_A,                  SEL_SCIF4_0),
+
+       PINMUX_IPSR_MSEL(IP0_19_16,     AVB_AVTP_MATCH_A,       SEL_ETHERAVB_0),
+       PINMUX_IPSR_MSEL(IP0_19_16,     MSIOF2_RXD_C,           SEL_MSIOF2_2),
+       PINMUX_IPSR_MSEL(IP0_19_16,     CTS4_N_A,               SEL_SCIF4_0),
+
+       PINMUX_IPSR_MSEL(IP0_23_20,     AVB_AVTP_CAPTURE_A,     SEL_ETHERAVB_0),
+       PINMUX_IPSR_MSEL(IP0_23_20,     MSIOF2_TXD_C,           SEL_MSIOF2_2),
+       PINMUX_IPSR_MSEL(IP0_23_20,     RTS4_N_TANS_A,          SEL_SCIF4_0),
+
+       PINMUX_IPSR_DATA(IP0_27_24,     IRQ0),
+       PINMUX_IPSR_DATA(IP0_27_24,     QPOLB),
+       PINMUX_IPSR_DATA(IP0_27_24,     DU_CDE),
+       PINMUX_IPSR_MSEL(IP0_27_24,     VI4_DATA0_B,            SEL_VIN4_1),
+       PINMUX_IPSR_MSEL(IP0_27_24,     CAN0_TX_B,              SEL_RCAN0_1),
+       PINMUX_IPSR_MSEL(IP0_27_24,     CANFD0_TX_B,            SEL_CANFD0_1),
+
+       PINMUX_IPSR_DATA(IP0_31_28,     IRQ1),
+       PINMUX_IPSR_DATA(IP0_31_28,     QPOLA),
+       PINMUX_IPSR_DATA(IP0_31_28,     DU_DISP),
+       PINMUX_IPSR_MSEL(IP0_31_28,     VI4_DATA1_B,            SEL_VIN4_1),
+       PINMUX_IPSR_MSEL(IP0_31_28,     CAN0_RX_B,              SEL_RCAN0_1),
+       PINMUX_IPSR_MSEL(IP0_31_28,     CANFD0_RX_B,            SEL_CANFD0_1),
+
+       /* IPSR1 */
+       PINMUX_IPSR_DATA(IP1_3_0,       IRQ2),
+       PINMUX_IPSR_DATA(IP1_3_0,       QCPV_QDE),
+       PINMUX_IPSR_DATA(IP1_3_0,       DU_EXODDF_DU_ODDF_DISP_CDE),
+       PINMUX_IPSR_MSEL(IP1_3_0,       VI4_DATA2_B,            SEL_VIN4_1),
+       PINMUX_IPSR_MSEL(IP1_3_0,       PWM3_B,                 SEL_PWM3_1),
+
+       PINMUX_IPSR_DATA(IP1_7_4,       IRQ3),
+       PINMUX_IPSR_DATA(IP1_7_4,       QSTVB_QVE),
+       PINMUX_IPSR_DATA(IP1_7_4,       A25),
+       PINMUX_IPSR_DATA(IP1_7_4,       DU_DOTCLKOUT1),
+       PINMUX_IPSR_MSEL(IP1_7_4,       VI4_DATA3_B,            SEL_VIN4_1),
+       PINMUX_IPSR_MSEL(IP1_7_4,       PWM4_B,                 SEL_PWM4_1),
+
+       PINMUX_IPSR_DATA(IP1_11_8,      IRQ4),
+       PINMUX_IPSR_DATA(IP1_11_8,      QSTH_QHS),
+       PINMUX_IPSR_DATA(IP1_11_8,      A24),
+       PINMUX_IPSR_DATA(IP1_11_8,      DU_EXHSYNC_DU_HSYNC),
+       PINMUX_IPSR_MSEL(IP1_11_8,      VI4_DATA4_B,            SEL_VIN4_1),
+       PINMUX_IPSR_MSEL(IP1_11_8,      PWM5_B,                 SEL_PWM5_1),
+
+       PINMUX_IPSR_DATA(IP1_15_12,     IRQ5),
+       PINMUX_IPSR_DATA(IP1_15_12,     QSTB_QHE),
+       PINMUX_IPSR_DATA(IP1_15_12,     A23),
+       PINMUX_IPSR_DATA(IP1_15_12,     DU_EXVSYNC_DU_VSYNC),
+       PINMUX_IPSR_MSEL(IP1_15_12,     VI4_DATA5_B,            SEL_VIN4_1),
+       PINMUX_IPSR_MSEL(IP1_15_12,     PWM6_B,                 SEL_PWM6_1),
+
+       PINMUX_IPSR_DATA(IP1_19_16,     PWM0),
+       PINMUX_IPSR_DATA(IP1_19_16,     AVB_AVTP_PPS),
+       PINMUX_IPSR_DATA(IP1_19_16,     A22),
+       PINMUX_IPSR_MSEL(IP1_19_16,     VI4_DATA6_B,            SEL_VIN4_1),
+       PINMUX_IPSR_MSEL(IP1_19_16,     IECLK_B,                SEL_IEBUS_1),
+
+       PINMUX_IPSR_MSEL(IP1_23_20,     PWM1_A,                 SEL_PWM1_0),
+       PINMUX_IPSR_DATA(IP1_23_20,     A21),
+       PINMUX_IPSR_MSEL(IP1_23_20,     HRX3_D,                 SEL_HSCIF3_3),
+       PINMUX_IPSR_MSEL(IP1_23_20,     VI4_DATA7_B,            SEL_VIN4_1),
+       PINMUX_IPSR_MSEL(IP1_23_20,     IERX_B,                 SEL_IEBUS_1),
+
+       PINMUX_IPSR_MSEL(IP1_27_24,     PWM2_A,                 SEL_PWM2_0),
+       PINMUX_IPSR_DATA(IP1_27_24,     A20),
+       PINMUX_IPSR_MSEL(IP1_27_24,     HTX3_D,                 SEL_HSCIF3_3),
+       PINMUX_IPSR_MSEL(IP1_27_24,     IETX_B,                 SEL_IEBUS_1),
+
+       PINMUX_IPSR_DATA(IP1_31_28,     A0),
+       PINMUX_IPSR_DATA(IP1_31_28,     LCDOUT16),
+       PINMUX_IPSR_MSEL(IP1_31_28,     MSIOF3_SYNC_B,          SEL_MSIOF3_1),
+       PINMUX_IPSR_DATA(IP1_31_28,     VI4_DATA8),
+       PINMUX_IPSR_DATA(IP1_31_28,     DU_DB0),
+       PINMUX_IPSR_MSEL(IP1_31_28,     PWM3_A,                 SEL_PWM3_0),
+
+       /* IPSR2 */
+       PINMUX_IPSR_DATA(IP2_3_0,       A1),
+       PINMUX_IPSR_DATA(IP2_3_0,       LCDOUT17),
+       PINMUX_IPSR_MSEL(IP2_3_0,       MSIOF3_TXD_B,           SEL_MSIOF3_1),
+       PINMUX_IPSR_DATA(IP2_3_0,       VI4_DATA9),
+       PINMUX_IPSR_DATA(IP2_3_0,       DU_DB1),
+       PINMUX_IPSR_MSEL(IP2_3_0,       PWM4_A,                 SEL_PWM4_0),
+
+       PINMUX_IPSR_DATA(IP2_7_4,       A2),
+       PINMUX_IPSR_DATA(IP2_7_4,       LCDOUT18),
+       PINMUX_IPSR_MSEL(IP2_7_4,       MSIOF3_SCK_B,           SEL_MSIOF3_1),
+       PINMUX_IPSR_DATA(IP2_7_4,       VI4_DATA10),
+       PINMUX_IPSR_DATA(IP2_7_4,       DU_DB2),
+       PINMUX_IPSR_MSEL(IP2_7_4,       PWM5_A,                 SEL_PWM5_0),
+
+       PINMUX_IPSR_DATA(IP2_11_8,      A3),
+       PINMUX_IPSR_DATA(IP2_11_8,      LCDOUT19),
+       PINMUX_IPSR_MSEL(IP2_11_8,      MSIOF3_RXD_B,           SEL_MSIOF3_1),
+       PINMUX_IPSR_DATA(IP2_11_8,      VI4_DATA11),
+       PINMUX_IPSR_DATA(IP2_11_8,      DU_DB3),
+       PINMUX_IPSR_MSEL(IP2_11_8,      PWM6_A,                 SEL_PWM6_0),
+
+       PINMUX_IPSR_DATA(IP2_15_12,     A4),
+       PINMUX_IPSR_DATA(IP2_15_12,     LCDOUT20),
+       PINMUX_IPSR_MSEL(IP2_15_12,     MSIOF3_SS1_B,           SEL_MSIOF3_1),
+       PINMUX_IPSR_DATA(IP2_15_12,     VI4_DATA12),
+       PINMUX_IPSR_DATA(IP2_15_12,     VI5_DATA12),
+       PINMUX_IPSR_DATA(IP2_15_12,     DU_DB4),
+
+       PINMUX_IPSR_DATA(IP2_19_16,     A5),
+       PINMUX_IPSR_DATA(IP2_19_16,     LCDOUT21),
+       PINMUX_IPSR_MSEL(IP2_19_16,     MSIOF3_SS2_B,           SEL_MSIOF3_1),
+       PINMUX_IPSR_MSEL(IP2_19_16,     SCK4_B,                 SEL_SCIF4_1),
+       PINMUX_IPSR_DATA(IP2_19_16,     VI4_DATA13),
+       PINMUX_IPSR_DATA(IP2_19_16,     VI5_DATA13),
+       PINMUX_IPSR_DATA(IP2_19_16,     DU_DB5),
+
+       PINMUX_IPSR_DATA(IP2_23_20,     A6),
+       PINMUX_IPSR_DATA(IP2_23_20,     LCDOUT22),
+       PINMUX_IPSR_MSEL(IP2_23_20,     MSIOF2_SS1_A,           SEL_MSIOF2_0),
+       PINMUX_IPSR_MSEL(IP2_23_20,     RX4_B,                  SEL_SCIF4_1),
+       PINMUX_IPSR_DATA(IP2_23_20,     VI4_DATA14),
+       PINMUX_IPSR_DATA(IP2_23_20,     VI5_DATA14),
+       PINMUX_IPSR_DATA(IP2_23_20,     DU_DB6),
+
+       PINMUX_IPSR_DATA(IP2_27_24,     A7),
+       PINMUX_IPSR_DATA(IP2_27_24,     LCDOUT23),
+       PINMUX_IPSR_MSEL(IP2_27_24,     MSIOF2_SS2_A,           SEL_MSIOF2_0),
+       PINMUX_IPSR_MSEL(IP2_27_24,     TX4_B,                  SEL_SCIF4_1),
+       PINMUX_IPSR_DATA(IP2_27_24,     VI4_DATA15),
+       PINMUX_IPSR_DATA(IP2_27_24,     VI5_DATA15),
+       PINMUX_IPSR_DATA(IP2_27_24,     DU_DB7),
+
+       PINMUX_IPSR_DATA(IP2_31_28,     A8),
+       PINMUX_IPSR_MSEL(IP2_31_28,     RX3_B,                  SEL_SCIF3_1),
+       PINMUX_IPSR_MSEL(IP2_31_28,     MSIOF2_SYNC_A,          SEL_MSIOF2_0),
+       PINMUX_IPSR_MSEL(IP2_31_28,     HRX4_B,                 SEL_HSCIF4_1),
+       PINMUX_IPSR_MSEL(IP2_31_28,     SDA6_A,                 SEL_I2C6_0),
+       PINMUX_IPSR_MSEL(IP2_31_28,     AVB_AVTP_MATCH_B,       SEL_ETHERAVB_1),
+       PINMUX_IPSR_MSEL(IP2_31_28,     PWM1_B,                 SEL_PWM1_1),
+
+       /* IPSR3 */
+       PINMUX_IPSR_DATA(IP3_3_0,       A9),
+       PINMUX_IPSR_MSEL(IP3_3_0,       MSIOF2_SCK_A,           SEL_MSIOF2_0),
+       PINMUX_IPSR_MSEL(IP3_3_0,       CTS4_N_B,               SEL_SCIF4_1),
+       PINMUX_IPSR_DATA(IP3_3_0,       VI5_VSYNC_N),
+
+       PINMUX_IPSR_DATA(IP3_7_4,       A10),
+       PINMUX_IPSR_MSEL(IP3_7_4,       MSIOF2_RXD_A,           SEL_MSIOF2_0),
+       PINMUX_IPSR_MSEL(IP3_7_4,       RTS4_N_TANS_B,          SEL_SCIF4_1),
+       PINMUX_IPSR_DATA(IP3_7_4,       VI5_HSYNC_N),
+
+       PINMUX_IPSR_DATA(IP3_11_8,      A11),
+       PINMUX_IPSR_MSEL(IP3_11_8,      TX3_B,                  SEL_SCIF3_1),
+       PINMUX_IPSR_MSEL(IP3_11_8,      MSIOF2_TXD_A,           SEL_MSIOF2_0),
+       PINMUX_IPSR_MSEL(IP3_11_8,      HTX4_B,                 SEL_HSCIF4_1),
+       PINMUX_IPSR_DATA(IP3_11_8,      HSCK4),
+       PINMUX_IPSR_DATA(IP3_11_8,      VI5_FIELD),
+       PINMUX_IPSR_MSEL(IP3_11_8,      SCL6_A,                 SEL_I2C6_0),
+       PINMUX_IPSR_MSEL(IP3_11_8,      AVB_AVTP_CAPTURE_B,     SEL_ETHERAVB_1),
+       PINMUX_IPSR_MSEL(IP3_11_8,      PWM2_B,                 SEL_PWM2_1),
+
+       PINMUX_IPSR_DATA(IP3_15_12,     A12),
+       PINMUX_IPSR_DATA(IP3_15_12,     LCDOUT12),
+       PINMUX_IPSR_MSEL(IP3_15_12,     MSIOF3_SCK_C,           SEL_MSIOF3_2),
+       PINMUX_IPSR_MSEL(IP3_15_12,     HRX4_A,                 SEL_HSCIF4_0),
+       PINMUX_IPSR_DATA(IP3_15_12,     VI5_DATA8),
+       PINMUX_IPSR_DATA(IP3_15_12,     DU_DG4),
+
+       PINMUX_IPSR_DATA(IP3_19_16,     A13),
+       PINMUX_IPSR_DATA(IP3_19_16,     LCDOUT13),
+       PINMUX_IPSR_MSEL(IP3_19_16,     MSIOF3_SYNC_C,          SEL_MSIOF3_2),
+       PINMUX_IPSR_MSEL(IP3_19_16,     HTX4_A,                 SEL_HSCIF4_0),
+       PINMUX_IPSR_DATA(IP3_19_16,     VI5_DATA9),
+       PINMUX_IPSR_DATA(IP3_19_16,     DU_DG5),
+
+       PINMUX_IPSR_DATA(IP3_23_20,     A14),
+       PINMUX_IPSR_DATA(IP3_23_20,     LCDOUT14),
+       PINMUX_IPSR_MSEL(IP3_23_20,     MSIOF3_RXD_C,           SEL_MSIOF3_2),
+       PINMUX_IPSR_DATA(IP3_23_20,     HCTS4_N),
+       PINMUX_IPSR_DATA(IP3_23_20,     VI5_DATA10),
+       PINMUX_IPSR_DATA(IP3_23_20,     DU_DG6),
+
+       PINMUX_IPSR_DATA(IP3_27_24,     A15),
+       PINMUX_IPSR_DATA(IP3_27_24,     LCDOUT15),
+       PINMUX_IPSR_MSEL(IP3_27_24,     MSIOF3_TXD_C,           SEL_MSIOF3_2),
+       PINMUX_IPSR_DATA(IP3_27_24,     HRTS4_N),
+       PINMUX_IPSR_DATA(IP3_27_24,     VI5_DATA11),
+       PINMUX_IPSR_DATA(IP3_27_24,     DU_DG7),
+
+       PINMUX_IPSR_DATA(IP3_31_28,     A16),
+       PINMUX_IPSR_DATA(IP3_31_28,     LCDOUT8),
+       PINMUX_IPSR_DATA(IP3_31_28,     VI4_FIELD),
+       PINMUX_IPSR_DATA(IP3_31_28,     DU_DG0),
+
+       /* IPSR4 */
+       PINMUX_IPSR_DATA(IP4_3_0,       A17),
+       PINMUX_IPSR_DATA(IP4_3_0,       LCDOUT9),
+       PINMUX_IPSR_DATA(IP4_3_0,       VI4_VSYNC_N),
+       PINMUX_IPSR_DATA(IP4_3_0,       DU_DG1),
+
+       PINMUX_IPSR_DATA(IP4_7_4,       A18),
+       PINMUX_IPSR_DATA(IP4_7_4,       LCDOUT10),
+       PINMUX_IPSR_DATA(IP4_7_4,       VI4_HSYNC_N),
+       PINMUX_IPSR_DATA(IP4_7_4,       DU_DG2),
+
+       PINMUX_IPSR_DATA(IP4_11_8,      A19),
+       PINMUX_IPSR_DATA(IP4_11_8,      LCDOUT11),
+       PINMUX_IPSR_DATA(IP4_11_8,      VI4_CLKENB),
+       PINMUX_IPSR_DATA(IP4_11_8,      DU_DG3),
+
+       PINMUX_IPSR_DATA(IP4_15_12,     CS0_N),
+       PINMUX_IPSR_DATA(IP4_15_12,     VI5_CLKENB),
+
+       PINMUX_IPSR_DATA(IP4_19_16,     CS1_N_A26),
+       PINMUX_IPSR_DATA(IP4_19_16,     VI5_CLK),
+       PINMUX_IPSR_MSEL(IP4_19_16,     EX_WAIT0_B,             SEL_LBSC_1),
+
+       PINMUX_IPSR_DATA(IP4_23_20,     BS_N),
+       PINMUX_IPSR_DATA(IP4_23_20,     QSTVA_QVS),
+       PINMUX_IPSR_MSEL(IP4_23_20,     MSIOF3_SCK_D,           SEL_MSIOF3_3),
+       PINMUX_IPSR_DATA(IP4_23_20,     SCK3),
+       PINMUX_IPSR_DATA(IP4_23_20,     HSCK3),
+       PINMUX_IPSR_DATA(IP4_23_20,     CAN1_TX),
+       PINMUX_IPSR_DATA(IP4_23_20,     CANFD1_TX),
+       PINMUX_IPSR_MSEL(IP4_23_20,     IETX_A,                 SEL_IEBUS_0),
+
+       PINMUX_IPSR_DATA(IP4_27_24,     RD_N),
+       PINMUX_IPSR_MSEL(IP4_27_24,     MSIOF3_SYNC_D,          SEL_MSIOF3_3),
+       PINMUX_IPSR_MSEL(IP4_27_24,     RX3_A,                  SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP4_27_24,     HRX3_A,                 SEL_HSCIF3_0),
+       PINMUX_IPSR_MSEL(IP4_27_24,     CAN0_TX_A,              SEL_RCAN0_0),
+       PINMUX_IPSR_MSEL(IP4_27_24,     CANFD0_TX_A,            SEL_CANFD0_0),
+
+       PINMUX_IPSR_DATA(IP4_31_28,     RD_WR_N),
+       PINMUX_IPSR_MSEL(IP4_31_28,     MSIOF3_RXD_D,           SEL_MSIOF3_3),
+       PINMUX_IPSR_MSEL(IP4_31_28,     TX3_A,                  SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP4_31_28,     HTX3_A,                 SEL_HSCIF3_0),
+       PINMUX_IPSR_MSEL(IP4_31_28,     CAN0_RX_A,              SEL_RCAN0_0),
+       PINMUX_IPSR_MSEL(IP4_31_28,     CANFD0_RX_A,            SEL_CANFD0_0),
+
+       /* IPSR5 */
+       PINMUX_IPSR_DATA(IP5_3_0,       WE0_N),
+       PINMUX_IPSR_MSEL(IP5_3_0,       MSIOF3_TXD_D,           SEL_MSIOF3_3),
+       PINMUX_IPSR_DATA(IP5_3_0,       CTS3_N),
+       PINMUX_IPSR_DATA(IP5_3_0,       HCTS3_N),
+       PINMUX_IPSR_MSEL(IP5_3_0,       SCL6_B,                 SEL_I2C6_1),
+       PINMUX_IPSR_DATA(IP5_3_0,       CAN_CLK),
+       PINMUX_IPSR_MSEL(IP5_3_0,       IECLK_A,                SEL_IEBUS_0),
+
+       PINMUX_IPSR_DATA(IP5_7_4,       WE1_N),
+       PINMUX_IPSR_MSEL(IP5_7_4,       MSIOF3_SS1_D,           SEL_MSIOF3_3),
+       PINMUX_IPSR_DATA(IP5_7_4,       RTS3_N_TANS),
+       PINMUX_IPSR_DATA(IP5_7_4,       HRTS3_N),
+       PINMUX_IPSR_MSEL(IP5_7_4,       SDA6_B,                 SEL_I2C6_1),
+       PINMUX_IPSR_DATA(IP5_7_4,       CAN1_RX),
+       PINMUX_IPSR_DATA(IP5_7_4,       CANFD1_RX),
+       PINMUX_IPSR_MSEL(IP5_7_4,       IERX_A,                 SEL_IEBUS_0),
+
+       PINMUX_IPSR_MSEL(IP5_11_8,      EX_WAIT0_A,             SEL_LBSC_0),
+       PINMUX_IPSR_DATA(IP5_11_8,      QCLK),
+       PINMUX_IPSR_DATA(IP5_11_8,      VI4_CLK),
+       PINMUX_IPSR_DATA(IP5_11_8,      DU_DOTCLKOUT0),
+
+       PINMUX_IPSR_DATA(IP5_15_12,     D0),
+       PINMUX_IPSR_MSEL(IP5_15_12,     MSIOF2_SS1_B,           SEL_MSIOF2_1),
+       PINMUX_IPSR_MSEL(IP5_15_12,     MSIOF3_SCK_A,           SEL_MSIOF3_0),
+       PINMUX_IPSR_DATA(IP5_15_12,     VI4_DATA16),
+       PINMUX_IPSR_DATA(IP5_15_12,     VI5_DATA0),
+
+       PINMUX_IPSR_DATA(IP5_19_16,     D1),
+       PINMUX_IPSR_MSEL(IP5_19_16,     MSIOF2_SS2_B,           SEL_MSIOF2_1),
+       PINMUX_IPSR_MSEL(IP5_19_16,     MSIOF3_SYNC_A,          SEL_MSIOF3_0),
+       PINMUX_IPSR_DATA(IP5_19_16,     VI4_DATA17),
+       PINMUX_IPSR_DATA(IP5_19_16,     VI5_DATA1),
+
+       PINMUX_IPSR_DATA(IP5_23_20,     D2),
+       PINMUX_IPSR_MSEL(IP5_23_20,     MSIOF3_RXD_A,           SEL_MSIOF3_0),
+       PINMUX_IPSR_DATA(IP5_23_20,     VI4_DATA18),
+       PINMUX_IPSR_DATA(IP5_23_20,     VI5_DATA2),
+
+       PINMUX_IPSR_DATA(IP5_27_24,     D3),
+       PINMUX_IPSR_MSEL(IP5_27_24,     MSIOF3_TXD_A,           SEL_MSIOF3_0),
+       PINMUX_IPSR_DATA(IP5_27_24,     VI4_DATA19),
+       PINMUX_IPSR_DATA(IP5_27_24,     VI5_DATA3),
+
+       PINMUX_IPSR_DATA(IP5_31_28,     D4),
+       PINMUX_IPSR_MSEL(IP5_31_28,     MSIOF2_SCK_B,           SEL_MSIOF2_1),
+       PINMUX_IPSR_DATA(IP5_31_28,     VI4_DATA20),
+       PINMUX_IPSR_DATA(IP5_31_28,     VI5_DATA4),
+
+       /* IPSR6 */
+       PINMUX_IPSR_DATA(IP6_3_0,       D5),
+       PINMUX_IPSR_MSEL(IP6_3_0,       MSIOF2_SYNC_B,          SEL_MSIOF2_1),
+       PINMUX_IPSR_DATA(IP6_3_0,       VI4_DATA21),
+       PINMUX_IPSR_DATA(IP6_3_0,       VI5_DATA5),
+
+       PINMUX_IPSR_DATA(IP6_7_4,       D6),
+       PINMUX_IPSR_MSEL(IP6_7_4,       MSIOF2_RXD_B,           SEL_MSIOF2_1),
+       PINMUX_IPSR_DATA(IP6_7_4,       VI4_DATA22),
+       PINMUX_IPSR_DATA(IP6_7_4,       VI5_DATA6),
+
+       PINMUX_IPSR_DATA(IP6_11_8,      D7),
+       PINMUX_IPSR_MSEL(IP6_11_8,      MSIOF2_TXD_B,           SEL_MSIOF2_1),
+       PINMUX_IPSR_DATA(IP6_11_8,      VI4_DATA23),
+       PINMUX_IPSR_DATA(IP6_11_8,      VI5_DATA7),
+
+       PINMUX_IPSR_DATA(IP6_15_12,     D8),
+       PINMUX_IPSR_DATA(IP6_15_12,     LCDOUT0),
+       PINMUX_IPSR_MSEL(IP6_15_12,     MSIOF2_SCK_D,           SEL_MSIOF2_3),
+       PINMUX_IPSR_MSEL(IP6_15_12,     SCK4_C,                 SEL_SCIF4_2),
+       PINMUX_IPSR_MSEL(IP6_15_12,     VI4_DATA0_A,            SEL_VIN4_0),
+       PINMUX_IPSR_DATA(IP6_15_12,     DU_DR0),
+
+       PINMUX_IPSR_DATA(IP6_19_16,     D9),
+       PINMUX_IPSR_DATA(IP6_19_16,     LCDOUT1),
+       PINMUX_IPSR_MSEL(IP6_19_16,     MSIOF2_SYNC_D,          SEL_MSIOF2_3),
+       PINMUX_IPSR_MSEL(IP6_19_16,     VI4_DATA1_A,            SEL_VIN4_0),
+       PINMUX_IPSR_DATA(IP6_19_16,     DU_DR1),
+
+       PINMUX_IPSR_DATA(IP6_23_20,     D10),
+       PINMUX_IPSR_DATA(IP6_23_20,     LCDOUT2),
+       PINMUX_IPSR_MSEL(IP6_23_20,     MSIOF2_RXD_D,           SEL_MSIOF2_3),
+       PINMUX_IPSR_MSEL(IP6_23_20,     HRX3_B,                 SEL_HSCIF3_1),
+       PINMUX_IPSR_MSEL(IP6_23_20,     VI4_DATA2_A,            SEL_VIN4_0),
+       PINMUX_IPSR_MSEL(IP6_23_20,     CTS4_N_C,               SEL_SCIF4_2),
+       PINMUX_IPSR_DATA(IP6_23_20,     DU_DR2),
+
+       PINMUX_IPSR_DATA(IP6_27_24,     D11),
+       PINMUX_IPSR_DATA(IP6_27_24,     LCDOUT3),
+       PINMUX_IPSR_MSEL(IP6_27_24,     MSIOF2_TXD_D,           SEL_MSIOF2_3),
+       PINMUX_IPSR_MSEL(IP6_27_24,     HTX3_B,                 SEL_HSCIF3_1),
+       PINMUX_IPSR_MSEL(IP6_27_24,     VI4_DATA3_A,            SEL_VIN4_0),
+       PINMUX_IPSR_MSEL(IP6_27_24,     RTS4_N_TANS_C,          SEL_SCIF4_2),
+       PINMUX_IPSR_DATA(IP6_27_24,     DU_DR3),
+
+       PINMUX_IPSR_DATA(IP6_31_28,     D12),
+       PINMUX_IPSR_DATA(IP6_31_28,     LCDOUT4),
+       PINMUX_IPSR_MSEL(IP6_31_28,     MSIOF2_SS1_D,           SEL_MSIOF2_3),
+       PINMUX_IPSR_MSEL(IP6_31_28,     RX4_C,                  SEL_SCIF4_2),
+       PINMUX_IPSR_MSEL(IP6_31_28,     VI4_DATA4_A,            SEL_VIN4_0),
+       PINMUX_IPSR_DATA(IP6_31_28,     DU_DR4),
+
+       /* IPSR7 */
+       PINMUX_IPSR_DATA(IP7_3_0,       D13),
+       PINMUX_IPSR_DATA(IP7_3_0,       LCDOUT5),
+       PINMUX_IPSR_MSEL(IP7_3_0,       MSIOF2_SS2_D,           SEL_MSIOF2_3),
+       PINMUX_IPSR_MSEL(IP7_3_0,       TX4_C,                  SEL_SCIF4_2),
+       PINMUX_IPSR_MSEL(IP7_3_0,       VI4_DATA5_A,            SEL_VIN4_0),
+       PINMUX_IPSR_DATA(IP7_3_0,       DU_DR5),
+
+       PINMUX_IPSR_DATA(IP7_7_4,       D14),
+       PINMUX_IPSR_DATA(IP7_7_4,       LCDOUT6),
+       PINMUX_IPSR_MSEL(IP7_7_4,       MSIOF3_SS1_A,           SEL_MSIOF3_0),
+       PINMUX_IPSR_MSEL(IP7_7_4,       HRX3_C,                 SEL_HSCIF3_2),
+       PINMUX_IPSR_MSEL(IP7_7_4,       VI4_DATA6_A,            SEL_VIN4_0),
+       PINMUX_IPSR_DATA(IP7_7_4,       DU_DR6),
+       PINMUX_IPSR_MSEL(IP7_7_4,       SCL6_C,                 SEL_I2C6_2),
+
+       PINMUX_IPSR_DATA(IP7_11_8,      D15),
+       PINMUX_IPSR_DATA(IP7_11_8,      LCDOUT7),
+       PINMUX_IPSR_MSEL(IP7_11_8,      MSIOF3_SS2_A,           SEL_MSIOF3_0),
+       PINMUX_IPSR_MSEL(IP7_11_8,      HTX3_C,                 SEL_HSCIF3_2),
+       PINMUX_IPSR_MSEL(IP7_11_8,      VI4_DATA7_A,            SEL_VIN4_0),
+       PINMUX_IPSR_DATA(IP7_11_8,      DU_DR7),
+       PINMUX_IPSR_MSEL(IP7_11_8,      SDA6_C,                 SEL_I2C6_2),
+
+       PINMUX_IPSR_DATA(IP7_15_12,     FSCLKST),
+
+       PINMUX_IPSR_DATA(IP7_19_16,     SD0_CLK),
+       PINMUX_IPSR_MSEL(IP7_19_16,     MSIOF1_SCK_E,           SEL_MSIOF1_4),
+       PINMUX_IPSR_MSEL(IP7_19_16,     STP_OPWM_0_B,           SEL_SSP1_0_1),
+
+       PINMUX_IPSR_DATA(IP7_23_20,     SD0_CMD),
+       PINMUX_IPSR_MSEL(IP7_23_20,     MSIOF1_SYNC_E,          SEL_MSIOF1_4),
+       PINMUX_IPSR_MSEL(IP7_23_20,     STP_IVCXO27_0_B,        SEL_SSP1_0_1),
+
+       PINMUX_IPSR_DATA(IP7_27_24,     SD0_DAT0),
+       PINMUX_IPSR_MSEL(IP7_27_24,     MSIOF1_RXD_E,           SEL_MSIOF1_4),
+       PINMUX_IPSR_MSEL(IP7_27_24,     TS_SCK0_B,              SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP7_27_24,     STP_ISCLK_0_B,          SEL_SSP1_0_1),
+
+       PINMUX_IPSR_DATA(IP7_31_28,     SD0_DAT1),
+       PINMUX_IPSR_MSEL(IP7_31_28,     MSIOF1_TXD_E,           SEL_MSIOF1_4),
+       PINMUX_IPSR_MSEL(IP7_31_28,     TS_SPSYNC0_B,           SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP7_31_28,     STP_ISSYNC_0_B,         SEL_SSP1_0_1),
+
+       /* IPSR8 */
+       PINMUX_IPSR_DATA(IP8_3_0,       SD0_DAT2),
+       PINMUX_IPSR_MSEL(IP8_3_0,       MSIOF1_SS1_E,           SEL_MSIOF1_4),
+       PINMUX_IPSR_MSEL(IP8_3_0,       TS_SDAT0_B,             SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP8_3_0,       STP_ISD_0_B,            SEL_SSP1_0_1),
+
+       PINMUX_IPSR_DATA(IP8_7_4,       SD0_DAT3),
+       PINMUX_IPSR_MSEL(IP8_7_4,       MSIOF1_SS2_E,           SEL_MSIOF1_4),
+       PINMUX_IPSR_MSEL(IP8_7_4,       TS_SDEN0_B,             SEL_TSIF0_1),
+       PINMUX_IPSR_MSEL(IP8_7_4,       STP_ISEN_0_B,           SEL_SSP1_0_1),
+
+       PINMUX_IPSR_DATA(IP8_11_8,      SD1_CLK),
+       PINMUX_IPSR_MSEL(IP8_11_8,      MSIOF1_SCK_G,           SEL_MSIOF1_6),
+       PINMUX_IPSR_MSEL(IP8_11_8,      SIM0_CLK_A,             SEL_SIMCARD_0),
+
+       PINMUX_IPSR_DATA(IP8_15_12,     SD1_CMD),
+       PINMUX_IPSR_MSEL(IP8_15_12,     MSIOF1_SYNC_G,          SEL_MSIOF1_6),
+       PINMUX_IPSR_MSEL(IP8_15_12,     SIM0_D_A,               SEL_SIMCARD_0),
+       PINMUX_IPSR_MSEL(IP8_15_12,     STP_IVCXO27_1_B,        SEL_SSP1_1_1),
+
+       PINMUX_IPSR_DATA(IP8_19_16,     SD1_DAT0),
+       PINMUX_IPSR_DATA(IP8_19_16,     SD2_DAT4),
+       PINMUX_IPSR_MSEL(IP8_19_16,     MSIOF1_RXD_G,           SEL_MSIOF1_6),
+       PINMUX_IPSR_MSEL(IP8_19_16,     TS_SCK1_B,              SEL_TSIF1_1),
+       PINMUX_IPSR_MSEL(IP8_19_16,     STP_ISCLK_1_B,          SEL_SSP1_1_1),
+
+       PINMUX_IPSR_DATA(IP8_23_20,     SD1_DAT1),
+       PINMUX_IPSR_DATA(IP8_23_20,     SD2_DAT5),
+       PINMUX_IPSR_MSEL(IP8_23_20,     MSIOF1_TXD_G,           SEL_MSIOF1_6),
+       PINMUX_IPSR_MSEL(IP8_23_20,     TS_SPSYNC1_B,           SEL_TSIF1_1),
+       PINMUX_IPSR_MSEL(IP8_23_20,     STP_ISSYNC_1_B,         SEL_SSP1_1_1),
+
+       PINMUX_IPSR_DATA(IP8_27_24,     SD1_DAT2),
+       PINMUX_IPSR_DATA(IP8_27_24,     SD2_DAT6),
+       PINMUX_IPSR_MSEL(IP8_27_24,     MSIOF1_SS1_G,           SEL_MSIOF1_6),
+       PINMUX_IPSR_MSEL(IP8_27_24,     TS_SDAT1_B,             SEL_TSIF1_1),
+       PINMUX_IPSR_MSEL(IP8_27_24,     STP_ISD_1_B,            SEL_SSP1_1_1),
+
+       PINMUX_IPSR_DATA(IP8_31_28,     SD1_DAT3),
+       PINMUX_IPSR_DATA(IP8_31_28,     SD2_DAT7),
+       PINMUX_IPSR_MSEL(IP8_31_28,     MSIOF1_SS2_G,           SEL_MSIOF1_6),
+       PINMUX_IPSR_MSEL(IP8_31_28,     TS_SDEN1_B,             SEL_TSIF1_1),
+       PINMUX_IPSR_MSEL(IP8_31_28,     STP_ISEN_1_B,           SEL_SSP1_1_1),
+
+       /* IPSR9 */
+       PINMUX_IPSR_DATA(IP9_3_0,       SD2_CLK),
+
+       PINMUX_IPSR_DATA(IP9_7_4,       SD2_DAT0),
+
+       PINMUX_IPSR_DATA(IP9_11_8,      SD2_DAT1),
+
+       PINMUX_IPSR_DATA(IP9_15_12,     SD2_DAT2),
+
+       PINMUX_IPSR_DATA(IP9_19_16,     SD2_DAT3),
+
+       PINMUX_IPSR_DATA(IP9_23_20,     SD2_DS),
+       PINMUX_IPSR_MSEL(IP9_23_20,     SATA_DEVSLP_B,          SEL_SCIF_1),
+
+       PINMUX_IPSR_DATA(IP9_27_24,     SD3_DAT4),
+       PINMUX_IPSR_MSEL(IP9_27_24,     SD2_CD_A,               SEL_SDHI2_0),
+
+       PINMUX_IPSR_DATA(IP9_31_28,     SD3_DAT5),
+       PINMUX_IPSR_MSEL(IP9_31_28,     SD2_WP_A,               SEL_SDHI2_0),
+
+       /* IPSR10 */
+       PINMUX_IPSR_DATA(IP10_3_0,      SD3_DAT6),
+       PINMUX_IPSR_DATA(IP10_3_0,      SD3_CD),
+
+       PINMUX_IPSR_DATA(IP10_7_4,      SD3_DAT7),
+       PINMUX_IPSR_DATA(IP10_7_4,      SD3_WP),
+
+       PINMUX_IPSR_DATA(IP10_11_8,     SD0_CD),
+       PINMUX_IPSR_MSEL(IP10_11_8,     SCL2_B,                 SEL_I2C2_1),
+       PINMUX_IPSR_MSEL(IP10_11_8,     SIM0_RST_A,             SEL_SIMCARD_0),
+
+       PINMUX_IPSR_DATA(IP10_15_12,    SD0_WP),
+       PINMUX_IPSR_MSEL(IP10_15_12,    SDA2_B,                 SEL_I2C2_1),
+
+       PINMUX_IPSR_DATA(IP10_19_16,    SD1_CD),
+       PINMUX_IPSR_MSEL(IP10_19_16,    SIM0_CLK_B,             SEL_SIMCARD_1),
+
+       PINMUX_IPSR_DATA(IP10_23_20,    SD1_WP),
+       PINMUX_IPSR_MSEL(IP10_23_20,    SIM0_D_B,               SEL_SIMCARD_1),
+
+       PINMUX_IPSR_DATA(IP10_27_24,    SCK0),
+       PINMUX_IPSR_MSEL(IP10_27_24,    HSCK1_B,                SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP10_27_24,    MSIOF1_SS2_B,           SEL_MSIOF1_1),
+       PINMUX_IPSR_MSEL(IP10_27_24,    AUDIO_CLKC_B,           SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP10_27_24,    SDA2_A,                 SEL_I2C2_0),
+       PINMUX_IPSR_MSEL(IP10_27_24,    SIM0_RST_B,             SEL_SIMCARD_1),
+       PINMUX_IPSR_MSEL(IP10_27_24,    STP_OPWM_0_C,           SEL_SSP1_0_2),
+       PINMUX_IPSR_MSEL(IP10_27_24,    RIF0_CLK_B,             SEL_DRIF0_1),
+       PINMUX_IPSR_DATA(IP10_27_24,    ADICHS2),
+
+       PINMUX_IPSR_DATA(IP10_31_28,    RX0),
+       PINMUX_IPSR_MSEL(IP10_31_28,    HRX1_B,                 SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP10_31_28,    TS_SCK0_C,              SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP10_31_28,    STP_ISCLK_0_C,          SEL_SSP1_0_2),
+       PINMUX_IPSR_MSEL(IP10_31_28,    RIF0_D0_B,              SEL_DRIF0_1),
+
+       /* IPSR11 */
+       PINMUX_IPSR_DATA(IP11_3_0,      TX0),
+       PINMUX_IPSR_MSEL(IP11_3_0,      HTX1_B,                 SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP11_3_0,      TS_SPSYNC0_C,           SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP11_3_0,      STP_ISSYNC_0_C,         SEL_SSP1_0_2),
+       PINMUX_IPSR_MSEL(IP11_3_0,      RIF0_D1_B,              SEL_DRIF0_1),
+
+       PINMUX_IPSR_DATA(IP11_7_4,      CTS0_N),
+       PINMUX_IPSR_MSEL(IP11_7_4,      HCTS1_N_B,              SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP11_7_4,      MSIOF1_SYNC_B,          SEL_MSIOF1_1),
+       PINMUX_IPSR_MSEL(IP11_7_4,      TS_SPSYNC1_C,           SEL_TSIF1_2),
+       PINMUX_IPSR_MSEL(IP11_7_4,      STP_ISSYNC_1_C,         SEL_SSP1_1_2),
+       PINMUX_IPSR_MSEL(IP11_7_4,      RIF1_SYNC_B,            SEL_DRIF1_1),
+       PINMUX_IPSR_MSEL(IP11_7_4,      AUDIO_CLKOUT_C,         SEL_ADG_2),
+       PINMUX_IPSR_DATA(IP11_7_4,      ADICS_SAMP),
+
+       PINMUX_IPSR_DATA(IP11_11_8,     RTS0_N_TANS),
+       PINMUX_IPSR_MSEL(IP11_11_8,     HRTS1_N_B,              SEL_HSCIF1_1),
+       PINMUX_IPSR_MSEL(IP11_11_8,     MSIOF1_SS1_B,           SEL_MSIOF1_1),
+       PINMUX_IPSR_MSEL(IP11_11_8,     AUDIO_CLKA_B,           SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP11_11_8,     SCL2_A,                 SEL_I2C2_0),
+       PINMUX_IPSR_MSEL(IP11_11_8,     STP_IVCXO27_1_C,        SEL_SSP1_1_2),
+       PINMUX_IPSR_MSEL(IP11_11_8,     RIF0_SYNC_B,            SEL_DRIF0_1),
+       PINMUX_IPSR_DATA(IP11_11_8,     ADICHS1),
+
+       PINMUX_IPSR_MSEL(IP11_15_12,    RX1_A,                  SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP11_15_12,    HRX1_A,                 SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP11_15_12,    TS_SDAT0_C,             SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP11_15_12,    STP_ISD_0_C,            SEL_SSP1_0_2),
+       PINMUX_IPSR_MSEL(IP11_15_12,    RIF1_CLK_C,             SEL_DRIF1_2),
+
+       PINMUX_IPSR_MSEL(IP11_19_16,    TX1_A,                  SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP11_19_16,    HTX1_A,                 SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP11_19_16,    TS_SDEN0_C,             SEL_TSIF0_2),
+       PINMUX_IPSR_MSEL(IP11_19_16,    STP_ISEN_0_C,           SEL_SSP1_0_2),
+       PINMUX_IPSR_MSEL(IP11_19_16,    RIF1_D0_C,              SEL_DRIF1_2),
+
+       PINMUX_IPSR_DATA(IP11_23_20,    CTS1_N),
+       PINMUX_IPSR_MSEL(IP11_23_20,    HCTS1_N_A,              SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP11_23_20,    MSIOF1_RXD_B,           SEL_MSIOF1_1),
+       PINMUX_IPSR_MSEL(IP11_23_20,    TS_SDEN1_C,             SEL_TSIF1_2),
+       PINMUX_IPSR_MSEL(IP11_23_20,    STP_ISEN_1_C,           SEL_SSP1_1_2),
+       PINMUX_IPSR_MSEL(IP11_23_20,    RIF1_D0_B,              SEL_DRIF1_1),
+       PINMUX_IPSR_DATA(IP11_23_20,    ADIDATA),
+
+       PINMUX_IPSR_DATA(IP11_27_24,    RTS1_N_TANS),
+       PINMUX_IPSR_MSEL(IP11_27_24,    HRTS1_N_A,              SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP11_27_24,    MSIOF1_TXD_B,           SEL_MSIOF1_1),
+       PINMUX_IPSR_MSEL(IP11_27_24,    TS_SDAT1_C,             SEL_TSIF1_2),
+       PINMUX_IPSR_MSEL(IP11_27_24,    STP_ISD_1_C,            SEL_SSP1_1_2),
+       PINMUX_IPSR_MSEL(IP11_27_24,    RIF1_D1_B,              SEL_DRIF1_1),
+       PINMUX_IPSR_DATA(IP11_27_24,    ADICHS0),
+
+       PINMUX_IPSR_DATA(IP11_31_28,    SCK2),
+       PINMUX_IPSR_MSEL(IP11_31_28,    SCIF_CLK_B,             SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP11_31_28,    MSIOF1_SCK_B,           SEL_MSIOF1_1),
+       PINMUX_IPSR_MSEL(IP11_31_28,    TS_SCK1_C,              SEL_TSIF1_2),
+       PINMUX_IPSR_MSEL(IP11_31_28,    STP_ISCLK_1_C,          SEL_SSP1_1_2),
+       PINMUX_IPSR_MSEL(IP11_31_28,    RIF1_CLK_B,             SEL_DRIF1_1),
+       PINMUX_IPSR_DATA(IP11_31_28,    ADICLK),
+
+       /* IPSR12 */
+       PINMUX_IPSR_MSEL(IP12_3_0,      TX2_A,                  SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP12_3_0,      SD2_CD_B,               SEL_SDHI2_1),
+       PINMUX_IPSR_MSEL(IP12_3_0,      SCL1_A,                 SEL_I2C1_0),
+       PINMUX_IPSR_MSEL(IP12_3_0,      FMCLK_A,                SEL_FM_0),
+       PINMUX_IPSR_MSEL(IP12_3_0,      RIF1_D1_C,              SEL_DRIF1_2),
+       PINMUX_IPSR_MSEL(IP12_3_0,      FSO_CFE_0_B,            SEL_FSO_1),
+
+       PINMUX_IPSR_MSEL(IP12_7_4,      RX2_A,                  SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP12_7_4,      SD2_WP_B,               SEL_SDHI2_1),
+       PINMUX_IPSR_MSEL(IP12_7_4,      SDA1_A,                 SEL_I2C1_0),
+       PINMUX_IPSR_MSEL(IP12_7_4,      FMIN_A,                 SEL_FM_0),
+       PINMUX_IPSR_MSEL(IP12_7_4,      RIF1_SYNC_C,            SEL_DRIF1_2),
+       PINMUX_IPSR_MSEL(IP12_7_4,      FSO_CFE_1_B,            SEL_FSO_1),
+
+       PINMUX_IPSR_DATA(IP12_11_8,     HSCK0),
+       PINMUX_IPSR_MSEL(IP12_11_8,     MSIOF1_SCK_D,           SEL_MSIOF1_3),
+       PINMUX_IPSR_MSEL(IP12_11_8,     AUDIO_CLKB_A,           SEL_ADG_0),
+       PINMUX_IPSR_MSEL(IP12_11_8,     SSI_SDATA1_B,           SEL_SSI_1),
+       PINMUX_IPSR_MSEL(IP12_11_8,     TS_SCK0_D,              SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP12_11_8,     STP_ISCLK_0_D,          SEL_SSP1_0_3),
+       PINMUX_IPSR_MSEL(IP12_11_8,     RIF0_CLK_C,             SEL_DRIF0_2),
+
+       PINMUX_IPSR_DATA(IP12_15_12,    HRX0),
+       PINMUX_IPSR_MSEL(IP12_15_12,    MSIOF1_RXD_D,           SEL_MSIOF1_3),
+       PINMUX_IPSR_MSEL(IP12_15_12,    SSI_SDATA2_B,           SEL_SSI_1),
+       PINMUX_IPSR_MSEL(IP12_15_12,    TS_SDEN0_D,             SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP12_15_12,    STP_ISEN_0_D,           SEL_SSP1_0_3),
+       PINMUX_IPSR_MSEL(IP12_15_12,    RIF0_D0_C,              SEL_DRIF0_2),
+
+       PINMUX_IPSR_DATA(IP12_19_16,    HTX0),
+       PINMUX_IPSR_MSEL(IP12_19_16,    MSIOF1_TXD_D,           SEL_MSIOF1_3),
+       PINMUX_IPSR_MSEL(IP12_19_16,    SSI_SDATA9_B,           SEL_SSI_1),
+       PINMUX_IPSR_MSEL(IP12_19_16,    TS_SDAT0_D,             SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP12_19_16,    STP_ISD_0_D,            SEL_SSP1_0_3),
+       PINMUX_IPSR_MSEL(IP12_19_16,    RIF0_D1_C,              SEL_DRIF0_2),
+
+       PINMUX_IPSR_DATA(IP12_23_20,    HCTS0_N),
+       PINMUX_IPSR_MSEL(IP12_23_20,    RX2_B,                  SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP12_23_20,    MSIOF1_SYNC_D,          SEL_MSIOF1_3),
+       PINMUX_IPSR_MSEL(IP12_23_20,    SSI_SCK9_A,             SEL_SSI_0),
+       PINMUX_IPSR_MSEL(IP12_23_20,    TS_SPSYNC0_D,           SEL_TSIF0_3),
+       PINMUX_IPSR_MSEL(IP12_23_20,    STP_ISSYNC_0_D,         SEL_SSP1_0_3),
+       PINMUX_IPSR_MSEL(IP12_23_20,    RIF0_SYNC_C,            SEL_DRIF0_2),
+       PINMUX_IPSR_MSEL(IP12_23_20,    AUDIO_CLKOUT1_A,        SEL_ADG_0),
+
+       PINMUX_IPSR_DATA(IP12_27_24,    HRTS0_N),
+       PINMUX_IPSR_MSEL(IP12_27_24,    TX2_B,                  SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP12_27_24,    MSIOF1_SS1_D,           SEL_MSIOF1_3),
+       PINMUX_IPSR_MSEL(IP12_27_24,    SSI_WS9_A,              SEL_SSI_0),
+       PINMUX_IPSR_MSEL(IP12_27_24,    STP_IVCXO27_0_D,        SEL_SSP1_0_3),
+       PINMUX_IPSR_MSEL(IP12_27_24,    BPFCLK_A,               SEL_FM_0),
+       PINMUX_IPSR_MSEL(IP12_27_24,    AUDIO_CLKOUT2_A,        SEL_ADG_0),
+
+       PINMUX_IPSR_DATA(IP12_31_28,    MSIOF0_SYNC),
+       PINMUX_IPSR_MSEL(IP12_31_28,    AUDIO_CLKOUT_A,         SEL_ADG_0),
+
+       /* IPSR13 */
+       PINMUX_IPSR_DATA(IP13_3_0,      MSIOF0_SS1),
+       PINMUX_IPSR_DATA(IP13_3_0,      RX5),
+       PINMUX_IPSR_MSEL(IP13_3_0,      AUDIO_CLKA_C,           SEL_ADG_2),
+       PINMUX_IPSR_MSEL(IP13_3_0,      SSI_SCK2_A,             SEL_SSI_0),
+       PINMUX_IPSR_MSEL(IP13_3_0,      STP_IVCXO27_0_C,        SEL_SSP1_0_2),
+       PINMUX_IPSR_MSEL(IP13_3_0,      AUDIO_CLKOUT3_A,        SEL_ADG_0),
+       PINMUX_IPSR_MSEL(IP13_3_0,      TCLK1_B,                SEL_TIMER_TMU_1),
+
+       PINMUX_IPSR_DATA(IP13_7_4,      MSIOF0_SS2),
+       PINMUX_IPSR_DATA(IP13_7_4,      TX5),
+       PINMUX_IPSR_MSEL(IP13_7_4,      MSIOF1_SS2_D,           SEL_MSIOF1_3),
+       PINMUX_IPSR_MSEL(IP13_7_4,      AUDIO_CLKC_A,           SEL_ADG_0),
+       PINMUX_IPSR_MSEL(IP13_7_4,      SSI_WS2_A,              SEL_SSI_0),
+       PINMUX_IPSR_MSEL(IP13_7_4,      STP_OPWM_0_D,           SEL_SSP1_0_3),
+       PINMUX_IPSR_MSEL(IP13_7_4,      AUDIO_CLKOUT_D,         SEL_ADG_3),
+       PINMUX_IPSR_MSEL(IP13_7_4,      SPEEDIN_B,              SEL_SPEED_PULSE_1),
+
+       PINMUX_IPSR_DATA(IP13_11_8,     MLB_CLK),
+       PINMUX_IPSR_MSEL(IP13_11_8,     MSIOF1_SCK_F,           SEL_MSIOF1_5),
+       PINMUX_IPSR_MSEL(IP13_11_8,     SCL1_B,                 SEL_I2C1_1),
+
+       PINMUX_IPSR_DATA(IP13_15_12,    MLB_SIG),
+       PINMUX_IPSR_MSEL(IP13_15_12,    RX1_B,                  SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP13_15_12,    MSIOF1_SYNC_F,          SEL_MSIOF1_5),
+       PINMUX_IPSR_MSEL(IP13_15_12,    SDA1_B,                 SEL_I2C1_1),
+
+       PINMUX_IPSR_DATA(IP13_19_16,    MLB_DAT),
+       PINMUX_IPSR_MSEL(IP13_19_16,    TX1_B,                  SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP13_19_16,    MSIOF1_RXD_F,           SEL_MSIOF1_5),
+
+       PINMUX_IPSR_DATA(IP13_23_20,    SSI_SCK0129),
+       PINMUX_IPSR_MSEL(IP13_23_20,    MSIOF1_TXD_F,           SEL_MSIOF1_5),
+
+       PINMUX_IPSR_DATA(IP13_27_24,    SSI_WS0129),
+       PINMUX_IPSR_MSEL(IP13_27_24,    MSIOF1_SS1_F,           SEL_MSIOF1_5),
+
+       PINMUX_IPSR_DATA(IP13_31_28,    SSI_SDATA0),
+       PINMUX_IPSR_MSEL(IP13_31_28,    MSIOF1_SS2_F,           SEL_MSIOF1_5),
+
+       /* IPSR14 */
+       PINMUX_IPSR_MSEL(IP14_3_0,      SSI_SDATA1_A,           SEL_SSI_0),
+
+       PINMUX_IPSR_MSEL(IP14_7_4,      SSI_SDATA2_A,           SEL_SSI_0),
+       PINMUX_IPSR_MSEL(IP14_7_4,      SSI_SCK1_B,             SEL_SSI_1),
+
+       PINMUX_IPSR_DATA(IP14_11_8,     SSI_SCK34),
+       PINMUX_IPSR_MSEL(IP14_11_8,     MSIOF1_SS1_A,           SEL_MSIOF1_0),
+       PINMUX_IPSR_MSEL(IP14_11_8,     STP_OPWM_0_A,           SEL_SSP1_0_0),
+
+       PINMUX_IPSR_DATA(IP14_15_12,    SSI_WS34),
+       PINMUX_IPSR_MSEL(IP14_15_12,    HCTS2_N_A,              SEL_HSCIF2_0),
+       PINMUX_IPSR_MSEL(IP14_15_12,    MSIOF1_SS2_A,           SEL_MSIOF1_0),
+       PINMUX_IPSR_MSEL(IP14_15_12,    STP_IVCXO27_0_A,        SEL_SSP1_0_0),
+
+       PINMUX_IPSR_DATA(IP14_19_16,    SSI_SDATA3),
+       PINMUX_IPSR_MSEL(IP14_19_16,    HRTS2_N_A,              SEL_HSCIF2_0),
+       PINMUX_IPSR_MSEL(IP14_19_16,    MSIOF1_TXD_A,           SEL_MSIOF1_0),
+       PINMUX_IPSR_MSEL(IP14_19_16,    TS_SCK0_A,              SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP14_19_16,    STP_ISCLK_0_A,          SEL_SSP1_0_0),
+       PINMUX_IPSR_MSEL(IP14_19_16,    RIF0_D1_A,              SEL_DRIF0_0),
+       PINMUX_IPSR_MSEL(IP14_19_16,    RIF2_D0_A,              SEL_DRIF2_0),
+
+       PINMUX_IPSR_DATA(IP14_23_20,    SSI_SCK4),
+       PINMUX_IPSR_MSEL(IP14_23_20,    HRX2_A,                 SEL_HSCIF2_0),
+       PINMUX_IPSR_MSEL(IP14_23_20,    MSIOF1_SCK_A,           SEL_MSIOF1_0),
+       PINMUX_IPSR_MSEL(IP14_23_20,    TS_SDAT0_A,             SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP14_23_20,    STP_ISD_0_A,            SEL_SSP1_0_0),
+       PINMUX_IPSR_MSEL(IP14_23_20,    RIF0_CLK_A,             SEL_DRIF0_0),
+       PINMUX_IPSR_MSEL(IP14_23_20,    RIF2_CLK_A,             SEL_DRIF2_0),
+
+       PINMUX_IPSR_DATA(IP14_27_24,    SSI_WS4),
+       PINMUX_IPSR_MSEL(IP14_27_24,    HTX2_A,                 SEL_HSCIF2_0),
+       PINMUX_IPSR_MSEL(IP14_27_24,    MSIOF1_SYNC_A,          SEL_MSIOF1_0),
+       PINMUX_IPSR_MSEL(IP14_27_24,    TS_SDEN0_A,             SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP14_27_24,    STP_ISEN_0_A,           SEL_SSP1_0_0),
+       PINMUX_IPSR_MSEL(IP14_27_24,    RIF0_SYNC_A,            SEL_DRIF0_0),
+       PINMUX_IPSR_MSEL(IP14_27_24,    RIF2_SYNC_A,            SEL_DRIF2_0),
+
+       PINMUX_IPSR_DATA(IP14_31_28,    SSI_SDATA4),
+       PINMUX_IPSR_MSEL(IP14_31_28,    HSCK2_A,                SEL_HSCIF2_0),
+       PINMUX_IPSR_MSEL(IP14_31_28,    MSIOF1_RXD_A,           SEL_MSIOF1_0),
+       PINMUX_IPSR_MSEL(IP14_31_28,    TS_SPSYNC0_A,           SEL_TSIF0_0),
+       PINMUX_IPSR_MSEL(IP14_31_28,    STP_ISSYNC_0_A,         SEL_SSP1_0_0),
+       PINMUX_IPSR_MSEL(IP14_31_28,    RIF0_D0_A,              SEL_DRIF0_0),
+       PINMUX_IPSR_MSEL(IP14_31_28,    RIF2_D1_A,              SEL_DRIF2_0),
+
+       /* IPSR15 */
+       PINMUX_IPSR_DATA(IP15_3_0,      SSI_SCK6),
+       PINMUX_IPSR_DATA(IP15_3_0,      USB2_PWEN),
+       PINMUX_IPSR_MSEL(IP15_3_0,      SIM0_RST_D,             SEL_SIMCARD_3),
+
+       PINMUX_IPSR_DATA(IP15_7_4,      SSI_WS6),
+       PINMUX_IPSR_DATA(IP15_7_4,      USB2_OVC),
+       PINMUX_IPSR_MSEL(IP15_7_4,      SIM0_D_D,               SEL_SIMCARD_3),
+
+       PINMUX_IPSR_DATA(IP15_11_8,     SSI_SDATA6),
+       PINMUX_IPSR_MSEL(IP15_11_8,     SIM0_CLK_D,             SEL_SIMCARD_3),
+       PINMUX_IPSR_MSEL(IP15_11_8,     SATA_DEVSLP_A,          SEL_SCIF_0),
+
+       PINMUX_IPSR_DATA(IP15_15_12,    SSI_SCK78),
+       PINMUX_IPSR_MSEL(IP15_15_12,    HRX2_B,                 SEL_HSCIF2_1),
+       PINMUX_IPSR_MSEL(IP15_15_12,    MSIOF1_SCK_C,           SEL_MSIOF1_2),
+       PINMUX_IPSR_MSEL(IP15_15_12,    TS_SCK1_A,              SEL_TSIF1_0),
+       PINMUX_IPSR_MSEL(IP15_15_12,    STP_ISCLK_1_A,          SEL_SSP1_1_0),
+       PINMUX_IPSR_MSEL(IP15_15_12,    RIF1_CLK_A,             SEL_DRIF1_0),
+       PINMUX_IPSR_MSEL(IP15_15_12,    RIF3_CLK_A,             SEL_DRIF3_0),
+
+       PINMUX_IPSR_DATA(IP15_19_16,    SSI_WS78),
+       PINMUX_IPSR_MSEL(IP15_19_16,    HTX2_B,                 SEL_HSCIF2_1),
+       PINMUX_IPSR_MSEL(IP15_19_16,    MSIOF1_SYNC_C,          SEL_MSIOF1_2),
+       PINMUX_IPSR_MSEL(IP15_19_16,    TS_SDAT1_A,             SEL_TSIF1_0),
+       PINMUX_IPSR_MSEL(IP15_19_16,    STP_ISD_1_A,            SEL_SSP1_1_0),
+       PINMUX_IPSR_MSEL(IP15_19_16,    RIF1_SYNC_A,            SEL_DRIF1_0),
+       PINMUX_IPSR_MSEL(IP15_19_16,    RIF3_SYNC_A,            SEL_DRIF3_0),
+
+       PINMUX_IPSR_DATA(IP15_23_20,    SSI_SDATA7),
+       PINMUX_IPSR_MSEL(IP15_23_20,    HCTS2_N_B,              SEL_HSCIF2_1),
+       PINMUX_IPSR_MSEL(IP15_23_20,    MSIOF1_RXD_C,           SEL_MSIOF1_2),
+       PINMUX_IPSR_MSEL(IP15_23_20,    TS_SDEN1_A,             SEL_TSIF1_0),
+       PINMUX_IPSR_MSEL(IP15_23_20,    STP_ISEN_1_A,           SEL_SSP1_1_0),
+       PINMUX_IPSR_MSEL(IP15_23_20,    RIF1_D0_A,              SEL_DRIF1_0),
+       PINMUX_IPSR_MSEL(IP15_23_20,    RIF3_D0_A,              SEL_DRIF3_0),
+       PINMUX_IPSR_MSEL(IP15_23_20,    TCLK2_A,                SEL_TIMER_TMU_0),
+
+       PINMUX_IPSR_DATA(IP15_27_24,    SSI_SDATA8),
+       PINMUX_IPSR_MSEL(IP15_27_24,    HRTS2_N_B,              SEL_HSCIF2_1),
+       PINMUX_IPSR_MSEL(IP15_27_24,    MSIOF1_TXD_C,           SEL_MSIOF1_2),
+       PINMUX_IPSR_MSEL(IP15_27_24,    TS_SPSYNC1_A,           SEL_TSIF1_0),
+       PINMUX_IPSR_MSEL(IP15_27_24,    STP_ISSYNC_1_A,         SEL_SSP1_1_0),
+       PINMUX_IPSR_MSEL(IP15_27_24,    RIF1_D1_A,              SEL_DRIF1_0),
+       PINMUX_IPSR_MSEL(IP15_27_24,    RIF3_D1_A,              SEL_DRIF3_0),
+
+       PINMUX_IPSR_MSEL(IP15_31_28,    SSI_SDATA9_A,           SEL_SSI_0),
+       PINMUX_IPSR_MSEL(IP15_31_28,    HSCK2_B,                SEL_HSCIF2_1),
+       PINMUX_IPSR_MSEL(IP15_31_28,    MSIOF1_SS1_C,           SEL_MSIOF1_2),
+       PINMUX_IPSR_MSEL(IP15_31_28,    HSCK1_A,                SEL_HSCIF1_0),
+       PINMUX_IPSR_MSEL(IP15_31_28,    SSI_WS1_B,              SEL_SSI_1),
+       PINMUX_IPSR_DATA(IP15_31_28,    SCK1),
+       PINMUX_IPSR_MSEL(IP15_31_28,    STP_IVCXO27_1_A,        SEL_SSP1_1_0),
+       PINMUX_IPSR_DATA(IP15_31_28,    SCK5),
+
+       /* IPSR16 */
+       PINMUX_IPSR_MSEL(IP16_3_0,      AUDIO_CLKA_A,           SEL_ADG_0),
+       PINMUX_IPSR_DATA(IP16_3_0,      CC5_OSCOUT),
+
+       PINMUX_IPSR_MSEL(IP16_7_4,      AUDIO_CLKB_B,           SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP16_7_4,      SCIF_CLK_A,             SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP16_7_4,      STP_IVCXO27_1_D,        SEL_SSP1_1_3),
+       PINMUX_IPSR_MSEL(IP16_7_4,      REMOCON_A,              SEL_REMOCON_0),
+       PINMUX_IPSR_MSEL(IP16_7_4,      TCLK1_A,                SEL_TIMER_TMU_0),
+
+       PINMUX_IPSR_DATA(IP16_11_8,     USB0_PWEN),
+       PINMUX_IPSR_MSEL(IP16_11_8,     SIM0_RST_C,             SEL_SIMCARD_2),
+       PINMUX_IPSR_MSEL(IP16_11_8,     TS_SCK1_D,              SEL_TSIF1_3),
+       PINMUX_IPSR_MSEL(IP16_11_8,     STP_ISCLK_1_D,          SEL_SSP1_1_3),
+       PINMUX_IPSR_MSEL(IP16_11_8,     BPFCLK_B,               SEL_FM_1),
+       PINMUX_IPSR_MSEL(IP16_11_8,     RIF3_CLK_B,             SEL_DRIF3_1),
+
+       PINMUX_IPSR_DATA(IP16_15_12,    USB0_OVC),
+       PINMUX_IPSR_MSEL(IP16_11_8,     SIM0_D_C,               SEL_SIMCARD_2),
+       PINMUX_IPSR_MSEL(IP16_11_8,     TS_SDAT1_D,             SEL_TSIF1_3),
+       PINMUX_IPSR_MSEL(IP16_11_8,     STP_ISD_1_D,            SEL_SSP1_1_3),
+       PINMUX_IPSR_MSEL(IP16_11_8,     RIF3_SYNC_B,            SEL_DRIF3_1),
+
+       PINMUX_IPSR_DATA(IP16_19_16,    USB1_PWEN),
+       PINMUX_IPSR_MSEL(IP16_19_16,    SIM0_CLK_C,             SEL_SIMCARD_2),
+       PINMUX_IPSR_MSEL(IP16_19_16,    SSI_SCK1_A,             SEL_SSI_0),
+       PINMUX_IPSR_MSEL(IP16_19_16,    TS_SCK0_E,              SEL_TSIF0_4),
+       PINMUX_IPSR_MSEL(IP16_19_16,    STP_ISCLK_0_E,          SEL_SSP1_0_4),
+       PINMUX_IPSR_MSEL(IP16_19_16,    FMCLK_B,                SEL_FM_1),
+       PINMUX_IPSR_MSEL(IP16_19_16,    RIF2_CLK_B,             SEL_DRIF2_1),
+       PINMUX_IPSR_MSEL(IP16_19_16,    SPEEDIN_A,              SEL_SPEED_PULSE_0),
+
+       PINMUX_IPSR_DATA(IP16_23_20,    USB1_OVC),
+       PINMUX_IPSR_MSEL(IP16_23_20,    MSIOF1_SS2_C,           SEL_MSIOF1_2),
+       PINMUX_IPSR_MSEL(IP16_23_20,    SSI_WS1_A,              SEL_SSI_0),
+       PINMUX_IPSR_MSEL(IP16_23_20,    TS_SDAT0_E,             SEL_TSIF0_4),
+       PINMUX_IPSR_MSEL(IP16_23_20,    STP_ISD_0_E,            SEL_SSP1_0_4),
+       PINMUX_IPSR_MSEL(IP16_23_20,    FMIN_B,                 SEL_FM_1),
+       PINMUX_IPSR_MSEL(IP16_23_20,    RIF2_SYNC_B,            SEL_DRIF2_1),
+       PINMUX_IPSR_MSEL(IP16_23_20,    REMOCON_B,              SEL_REMOCON_1),
+
+       PINMUX_IPSR_DATA(IP16_27_24,    USB30_PWEN),
+       PINMUX_IPSR_MSEL(IP16_27_24,    AUDIO_CLKOUT_B,         SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP16_27_24,    SSI_SCK2_B,             SEL_SSI_1),
+       PINMUX_IPSR_MSEL(IP16_27_24,    TS_SDEN1_D,             SEL_TSIF1_3),
+       PINMUX_IPSR_MSEL(IP16_27_24,    STP_ISEN_1_D,           SEL_SSP1_1_2),
+       PINMUX_IPSR_MSEL(IP16_27_24,    STP_OPWM_0_E,           SEL_SSP1_0_4),
+       PINMUX_IPSR_MSEL(IP16_27_24,    RIF3_D0_B,              SEL_DRIF3_1),
+       PINMUX_IPSR_MSEL(IP16_27_24,    TCLK2_B,                SEL_TIMER_TMU_1),
+       PINMUX_IPSR_DATA(IP16_27_24,    TPU0TO0),
+
+       PINMUX_IPSR_DATA(IP16_31_28,    USB30_OVC),
+       PINMUX_IPSR_MSEL(IP16_31_28,    AUDIO_CLKOUT1_B,        SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP16_31_28,    SSI_WS2_B,              SEL_SSI_1),
+       PINMUX_IPSR_MSEL(IP16_31_28,    TS_SPSYNC1_D,           SEL_TSIF1_3),
+       PINMUX_IPSR_MSEL(IP16_31_28,    STP_ISSYNC_1_D,         SEL_SSP1_1_3),
+       PINMUX_IPSR_MSEL(IP16_31_28,    STP_IVCXO27_0_E,        SEL_SSP1_0_4),
+       PINMUX_IPSR_MSEL(IP16_31_28,    RIF3_D1_B,              SEL_DRIF3_1),
+       PINMUX_IPSR_MSEL(IP16_31_28,    FSO_TOE_B,              SEL_FSO_1),
+       PINMUX_IPSR_DATA(IP16_31_28,    TPU0TO1),
+
+       /* IPSR17 */
+       PINMUX_IPSR_DATA(IP17_3_0,      USB31_PWEN),
+       PINMUX_IPSR_MSEL(IP17_3_0,      AUDIO_CLKOUT2_B,        SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP17_3_0,      SSI_SCK9_B,             SEL_SSI_1),
+       PINMUX_IPSR_MSEL(IP17_3_0,      TS_SDEN0_E,             SEL_TSIF0_4),
+       PINMUX_IPSR_MSEL(IP17_3_0,      STP_ISEN_0_E,           SEL_SSP1_0_4),
+       PINMUX_IPSR_MSEL(IP17_3_0,      RIF2_D0_B,              SEL_DRIF2_1),
+       PINMUX_IPSR_DATA(IP17_3_0,      TPU0TO2),
+
+       PINMUX_IPSR_DATA(IP17_7_4,      USB31_OVC),
+       PINMUX_IPSR_MSEL(IP17_7_4,      AUDIO_CLKOUT3_B,        SEL_ADG_1),
+       PINMUX_IPSR_MSEL(IP17_7_4,      SSI_WS9_B,              SEL_SSI_1),
+       PINMUX_IPSR_MSEL(IP17_7_4,      TS_SPSYNC0_E,           SEL_TSIF0_4),
+       PINMUX_IPSR_MSEL(IP17_7_4,      STP_ISSYNC_0_E,         SEL_SSP1_0_4),
+       PINMUX_IPSR_MSEL(IP17_7_4,      RIF2_D1_B,              SEL_DRIF2_1),
+       PINMUX_IPSR_DATA(IP17_7_4,      TPU0TO3),
+
+       /* I2C */
+       PINMUX_IPSR_NOGP(0,             I2C_SEL_0_1),
+       PINMUX_IPSR_NOGP(0,             I2C_SEL_3_1),
+       PINMUX_IPSR_NOGP(0,             I2C_SEL_5_1),
+};
+
+static const struct sh_pfc_pin pinmux_pins[] = {
+       PINMUX_GPIO_GP_ALL(),
+};
+
+/* - AUDIO CLOCK ------------------------------------------------------------ */
+static const unsigned int audio_clk_a_a_pins[] = {
+       /* CLK A */
+       RCAR_GP_PIN(6, 22),
+};
+static const unsigned int audio_clk_a_a_mux[] = {
+       AUDIO_CLKA_A_MARK,
+};
+static const unsigned int audio_clk_a_b_pins[] = {
+       /* CLK A */
+       RCAR_GP_PIN(5, 4),
+};
+static const unsigned int audio_clk_a_b_mux[] = {
+       AUDIO_CLKA_B_MARK,
+};
+static const unsigned int audio_clk_a_c_pins[] = {
+       /* CLK A */
+       RCAR_GP_PIN(5, 19),
+};
+static const unsigned int audio_clk_a_c_mux[] = {
+       AUDIO_CLKA_C_MARK,
+};
+static const unsigned int audio_clk_b_a_pins[] = {
+       /* CLK B */
+       RCAR_GP_PIN(5, 12),
+};
+static const unsigned int audio_clk_b_a_mux[] = {
+       AUDIO_CLKB_A_MARK,
+};
+static const unsigned int audio_clk_b_b_pins[] = {
+       /* CLK B */
+       RCAR_GP_PIN(6, 23),
+};
+static const unsigned int audio_clk_b_b_mux[] = {
+       AUDIO_CLKB_B_MARK,
+};
+static const unsigned int audio_clk_c_a_pins[] = {
+       /* CLK C */
+       RCAR_GP_PIN(5, 21),
+};
+static const unsigned int audio_clk_c_a_mux[] = {
+       AUDIO_CLKC_A_MARK,
+};
+static const unsigned int audio_clk_c_b_pins[] = {
+       /* CLK C */
+       RCAR_GP_PIN(5, 0),
+};
+static const unsigned int audio_clk_c_b_mux[] = {
+       AUDIO_CLKC_B_MARK,
+};
+static const unsigned int audio_clkout_a_pins[] = {
+       /* CLKOUT */
+       RCAR_GP_PIN(5, 18),
+};
+static const unsigned int audio_clkout_a_mux[] = {
+       AUDIO_CLKOUT_A_MARK,
+};
+static const unsigned int audio_clkout_b_pins[] = {
+       /* CLKOUT */
+       RCAR_GP_PIN(6, 28),
+};
+static const unsigned int audio_clkout_b_mux[] = {
+       AUDIO_CLKOUT_B_MARK,
+};
+static const unsigned int audio_clkout_c_pins[] = {
+       /* CLKOUT */
+       RCAR_GP_PIN(5, 3),
+};
+static const unsigned int audio_clkout_c_mux[] = {
+       AUDIO_CLKOUT_C_MARK,
+};
+static const unsigned int audio_clkout_d_pins[] = {
+       /* CLKOUT */
+       RCAR_GP_PIN(5, 21),
+};
+static const unsigned int audio_clkout_d_mux[] = {
+       AUDIO_CLKOUT_D_MARK,
+};
+static const unsigned int audio_clkout1_a_pins[] = {
+       /* CLKOUT1 */
+       RCAR_GP_PIN(5, 15),
+};
+static const unsigned int audio_clkout1_a_mux[] = {
+       AUDIO_CLKOUT1_A_MARK,
+};
+static const unsigned int audio_clkout1_b_pins[] = {
+       /* CLKOUT1 */
+       RCAR_GP_PIN(6, 29),
+};
+static const unsigned int audio_clkout1_b_mux[] = {
+       AUDIO_CLKOUT1_B_MARK,
+};
+static const unsigned int audio_clkout2_a_pins[] = {
+       /* CLKOUT2 */
+       RCAR_GP_PIN(5, 16),
+};
+static const unsigned int audio_clkout2_a_mux[] = {
+       AUDIO_CLKOUT2_A_MARK,
+};
+static const unsigned int audio_clkout2_b_pins[] = {
+       /* CLKOUT2 */
+       RCAR_GP_PIN(6, 30),
+};
+static const unsigned int audio_clkout2_b_mux[] = {
+       AUDIO_CLKOUT2_B_MARK,
+};
+
+static const unsigned int audio_clkout3_a_pins[] = {
+       /* CLKOUT3 */
+       RCAR_GP_PIN(5, 19),
+};
+static const unsigned int audio_clkout3_a_mux[] = {
+       AUDIO_CLKOUT3_A_MARK,
+};
+static const unsigned int audio_clkout3_b_pins[] = {
+       /* CLKOUT3 */
+       RCAR_GP_PIN(6, 31),
+};
+static const unsigned int audio_clkout3_b_mux[] = {
+       AUDIO_CLKOUT3_B_MARK,
+};
+
+/* - EtherAVB --------------------------------------------------------------- */
+static const unsigned int avb_link_pins[] = {
+       /* AVB_LINK */
+       RCAR_GP_PIN(2, 12),
+};
+static const unsigned int avb_link_mux[] = {
+       AVB_LINK_MARK,
+};
+static const unsigned int avb_magic_pins[] = {
+       /* AVB_MAGIC_ */
+       RCAR_GP_PIN(2, 10),
+};
+static const unsigned int avb_magic_mux[] = {
+       AVB_MAGIC_MARK,
+};
+static const unsigned int avb_phy_int_pins[] = {
+       /* AVB_PHY_INT */
+       RCAR_GP_PIN(2, 11),
+};
+static const unsigned int avb_phy_int_mux[] = {
+       AVB_PHY_INT_MARK,
+};
+static const unsigned int avb_mdc_pins[] = {
+       /* AVB_MDC */
+       RCAR_GP_PIN(2, 9),
+};
+static const unsigned int avb_mdc_mux[] = {
+       AVB_MDC_MARK,
+};
+static const unsigned int avb_avtp_pps_pins[] = {
+       /* AVB_AVTP_PPS */
+       RCAR_GP_PIN(2, 6),
+};
+static const unsigned int avb_avtp_pps_mux[] = {
+       AVB_AVTP_PPS_MARK,
+};
+static const unsigned int avb_avtp_match_a_pins[] = {
+       /* AVB_AVTP_MATCH_A */
+       RCAR_GP_PIN(2, 13),
+};
+static const unsigned int avb_avtp_match_a_mux[] = {
+       AVB_AVTP_MATCH_A_MARK,
+};
+static const unsigned int avb_avtp_capture_a_pins[] = {
+       /* AVB_AVTP_CAPTURE_A */
+       RCAR_GP_PIN(2, 14),
+};
+static const unsigned int avb_avtp_capture_a_mux[] = {
+       AVB_AVTP_CAPTURE_A_MARK,
+};
+static const unsigned int avb_avtp_match_b_pins[] = {
+       /*  AVB_AVTP_MATCH_B */
+       RCAR_GP_PIN(1, 8),
+};
+static const unsigned int avb_avtp_match_b_mux[] = {
+       AVB_AVTP_MATCH_B_MARK,
+};
+static const unsigned int avb_avtp_capture_b_pins[] = {
+       /* AVB_AVTP_CAPTURE_B */
+       RCAR_GP_PIN(1, 11),
+};
+static const unsigned int avb_avtp_capture_b_mux[] = {
+       AVB_AVTP_CAPTURE_B_MARK,
+};
+
+/* - I2C -------------------------------------------------------------------- */
+static const unsigned int i2c1_a_pins[] = {
+       /* SDA, SCL */
+       RCAR_GP_PIN(5, 11), RCAR_GP_PIN(5, 10),
+};
+static const unsigned int i2c1_a_mux[] = {
+       SDA1_A_MARK, SCL1_A_MARK,
+};
+static const unsigned int i2c1_b_pins[] = {
+       /* SDA, SCL */
+       RCAR_GP_PIN(5, 24), RCAR_GP_PIN(5, 23),
+};
+static const unsigned int i2c1_b_mux[] = {
+       SDA1_B_MARK, SCL1_B_MARK,
+};
+static const unsigned int i2c2_a_pins[] = {
+       /* SDA, SCL */
+       RCAR_GP_PIN(5, 0), RCAR_GP_PIN(5, 4),
+};
+static const unsigned int i2c2_a_mux[] = {
+       SDA2_A_MARK, SCL2_A_MARK,
+};
+static const unsigned int i2c2_b_pins[] = {
+       /* SDA, SCL */
+       RCAR_GP_PIN(3, 13), RCAR_GP_PIN(3, 12),
+};
+static const unsigned int i2c2_b_mux[] = {
+       SDA2_B_MARK, SCL2_B_MARK,
+};
+static const unsigned int i2c6_a_pins[] = {
+       /* SDA, SCL */
+       RCAR_GP_PIN(1, 8), RCAR_GP_PIN(1, 11),
+};
+static const unsigned int i2c6_a_mux[] = {
+       SDA6_A_MARK, SCL6_A_MARK,
+};
+static const unsigned int i2c6_b_pins[] = {
+       /* SDA, SCL */
+       RCAR_GP_PIN(1, 26), RCAR_GP_PIN(1, 25),
+};
+static const unsigned int i2c6_b_mux[] = {
+       SDA6_B_MARK, SCL6_B_MARK,
+};
+static const unsigned int i2c6_c_pins[] = {
+       /* SDA, SCL */
+       RCAR_GP_PIN(0, 15), RCAR_GP_PIN(0, 14),
+};
+static const unsigned int i2c6_c_mux[] = {
+       SDA6_C_MARK, SCL6_C_MARK,
+};
+
+/* - SCIF0 ------------------------------------------------------------------ */
+static const unsigned int scif0_data_pins[] = {
+       /* RX, TX */
+       RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2),
+};
+static const unsigned int scif0_data_mux[] = {
+       RX0_MARK, TX0_MARK,
+};
+static const unsigned int scif0_clk_pins[] = {
+       /* SCK */
+       RCAR_GP_PIN(5, 0),
+};
+static const unsigned int scif0_clk_mux[] = {
+       SCK0_MARK,
+};
+static const unsigned int scif0_ctrl_pins[] = {
+       /* RTS, CTS */
+       RCAR_GP_PIN(5, 4), RCAR_GP_PIN(5, 3),
+};
+static const unsigned int scif0_ctrl_mux[] = {
+       RTS0_N_TANS_MARK, CTS0_N_MARK,
+};
+/* - SCIF1 ------------------------------------------------------------------ */
+static const unsigned int scif1_data_a_pins[] = {
+       /* RX, TX */
+       RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 6),
+};
+static const unsigned int scif1_data_a_mux[] = {
+       RX1_A_MARK, TX1_A_MARK,
+};
+static const unsigned int scif1_clk_pins[] = {
+       /* SCK */
+       RCAR_GP_PIN(6, 21),
+};
+static const unsigned int scif1_clk_mux[] = {
+       SCK1_MARK,
+};
+static const unsigned int scif1_ctrl_pins[] = {
+       /* RTS, CTS */
+       RCAR_GP_PIN(5, 8), RCAR_GP_PIN(5, 7),
+};
+static const unsigned int scif1_ctrl_mux[] = {
+       RTS1_N_TANS_MARK, CTS1_N_MARK,
+};
+
+static const unsigned int scif1_data_b_pins[] = {
+       /* RX, TX */
+       RCAR_GP_PIN(5, 24), RCAR_GP_PIN(5, 25),
+};
+static const unsigned int scif1_data_b_mux[] = {
+       RX1_B_MARK, TX1_B_MARK,
+};
+/* - SCIF2 ------------------------------------------------------------------ */
+static const unsigned int scif2_data_a_pins[] = {
+       /* RX, TX */
+       RCAR_GP_PIN(5, 11), RCAR_GP_PIN(5, 10),
+};
+static const unsigned int scif2_data_a_mux[] = {
+       RX2_A_MARK, TX2_A_MARK,
+};
+static const unsigned int scif2_clk_pins[] = {
+       /* SCK */
+       RCAR_GP_PIN(5, 9),
+};
+static const unsigned int scif2_clk_mux[] = {
+       SCK2_MARK,
+};
+static const unsigned int scif2_data_b_pins[] = {
+       /* RX, TX */
+       RCAR_GP_PIN(5, 15), RCAR_GP_PIN(5, 16),
+};
+static const unsigned int scif2_data_b_mux[] = {
+       RX2_B_MARK, TX2_B_MARK,
+};
+/* - SCIF3 ------------------------------------------------------------------ */
+static const unsigned int scif3_data_a_pins[] = {
+       /* RX, TX */
+       RCAR_GP_PIN(1, 23), RCAR_GP_PIN(1, 24),
+};
+static const unsigned int scif3_data_a_mux[] = {
+       RX3_A_MARK, TX3_A_MARK,
+};
+static const unsigned int scif3_clk_pins[] = {
+       /* SCK */
+       RCAR_GP_PIN(1, 22),
+};
+static const unsigned int scif3_clk_mux[] = {
+       SCK3_MARK,
+};
+static const unsigned int scif3_ctrl_pins[] = {
+       /* RTS, CTS */
+       RCAR_GP_PIN(1, 26), RCAR_GP_PIN(1, 25),
+};
+static const unsigned int scif3_ctrl_mux[] = {
+       RTS3_N_TANS_MARK, CTS3_N_MARK,
+};
+static const unsigned int scif3_data_b_pins[] = {
+       /* RX, TX */
+       RCAR_GP_PIN(1, 8), RCAR_GP_PIN(1, 11),
+};
+static const unsigned int scif3_data_b_mux[] = {
+       RX3_B_MARK, TX3_B_MARK,
+};
+/* - SCIF4 ------------------------------------------------------------------ */
+static const unsigned int scif4_data_a_pins[] = {
+       /* RX, TX */
+       RCAR_GP_PIN(2, 11), RCAR_GP_PIN(2, 12),
+};
+static const unsigned int scif4_data_a_mux[] = {
+       RX4_A_MARK, TX4_A_MARK,
+};
+static const unsigned int scif4_clk_a_pins[] = {
+       /* SCK */
+       RCAR_GP_PIN(2, 10),
+};
+static const unsigned int scif4_clk_a_mux[] = {
+       SCK4_A_MARK,
+};
+static const unsigned int scif4_ctrl_a_pins[] = {
+       /* RTS, CTS */
+       RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 13),
+};
+static const unsigned int scif4_ctrl_a_mux[] = {
+       RTS4_N_TANS_A_MARK, CTS4_N_A_MARK,
+};
+static const unsigned int scif4_data_b_pins[] = {
+       /* RX, TX */
+       RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+};
+static const unsigned int scif4_data_b_mux[] = {
+       RX4_B_MARK, TX4_B_MARK,
+};
+static const unsigned int scif4_clk_b_pins[] = {
+       /* SCK */
+       RCAR_GP_PIN(1, 5),
+};
+static const unsigned int scif4_clk_b_mux[] = {
+       SCK4_B_MARK,
+};
+static const unsigned int scif4_ctrl_b_pins[] = {
+       /* RTS, CTS */
+       RCAR_GP_PIN(1, 10), RCAR_GP_PIN(1, 9),
+};
+static const unsigned int scif4_ctrl_b_mux[] = {
+       RTS4_N_TANS_B_MARK, CTS4_N_B_MARK,
+};
+static const unsigned int scif4_data_c_pins[] = {
+       /* RX, TX */
+       RCAR_GP_PIN(0, 12), RCAR_GP_PIN(0, 13),
+};
+static const unsigned int scif4_data_c_mux[] = {
+       RX4_C_MARK, TX4_C_MARK,
+};
+static const unsigned int scif4_clk_c_pins[] = {
+       /* SCK */
+       RCAR_GP_PIN(0, 8),
+};
+static const unsigned int scif4_clk_c_mux[] = {
+       SCK4_C_MARK,
+};
+static const unsigned int scif4_ctrl_c_pins[] = {
+       /* RTS, CTS */
+       RCAR_GP_PIN(0, 11), RCAR_GP_PIN(0, 10),
+};
+static const unsigned int scif4_ctrl_c_mux[] = {
+       RTS4_N_TANS_C_MARK, CTS4_N_C_MARK,
+};
+/* - SCIF5 ------------------------------------------------------------------ */
+static const unsigned int scif5_data_pins[] = {
+       /* RX, TX */
+       RCAR_GP_PIN(5, 19), RCAR_GP_PIN(5, 21),
+};
+static const unsigned int scif5_data_mux[] = {
+       RX5_MARK, TX5_MARK,
+};
+static const unsigned int scif5_clk_pins[] = {
+       /* SCK */
+       RCAR_GP_PIN(6, 21),
+};
+static const unsigned int scif5_clk_mux[] = {
+       SCK5_MARK,
+};
+
+/* - SSI -------------------------------------------------------------------- */
+static const unsigned int ssi0_data_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(6, 2),
+};
+static const unsigned int ssi0_data_mux[] = {
+       SSI_SDATA0_MARK,
+};
+static const unsigned int ssi01239_ctrl_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(6, 0), RCAR_GP_PIN(6, 1),
+};
+static const unsigned int ssi01239_ctrl_mux[] = {
+       SSI_SCK0129_MARK, SSI_WS0129_MARK,
+};
+static const unsigned int ssi1_data_a_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(6, 3),
+};
+static const unsigned int ssi1_data_a_mux[] = {
+       SSI_SDATA1_A_MARK,
+};
+static const unsigned int ssi1_data_b_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(5, 12),
+};
+static const unsigned int ssi1_data_b_mux[] = {
+       SSI_SDATA1_B_MARK,
+};
+static const unsigned int ssi1_ctrl_a_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(6, 26), RCAR_GP_PIN(6, 27),
+};
+static const unsigned int ssi1_ctrl_a_mux[] = {
+       SSI_SCK1_A_MARK, SSI_WS1_A_MARK,
+};
+static const unsigned int ssi1_ctrl_b_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(6, 4), RCAR_GP_PIN(6, 21),
+};
+static const unsigned int ssi1_ctrl_b_mux[] = {
+       SSI_SCK1_B_MARK, SSI_WS1_B_MARK,
+};
+static const unsigned int ssi2_data_a_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(6, 4),
+};
+static const unsigned int ssi2_data_a_mux[] = {
+       SSI_SDATA2_A_MARK,
+};
+static const unsigned int ssi2_data_b_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(5, 13),
+};
+static const unsigned int ssi2_data_b_mux[] = {
+       SSI_SDATA2_B_MARK,
+};
+static const unsigned int ssi2_ctrl_a_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(5, 19), RCAR_GP_PIN(5, 21),
+};
+static const unsigned int ssi2_ctrl_a_mux[] = {
+       SSI_SCK2_A_MARK, SSI_WS2_A_MARK,
+};
+static const unsigned int ssi2_ctrl_b_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(6, 28), RCAR_GP_PIN(6, 29),
+};
+static const unsigned int ssi2_ctrl_b_mux[] = {
+       SSI_SCK2_B_MARK, SSI_WS2_B_MARK,
+};
+static const unsigned int ssi3_data_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(6, 7),
+};
+static const unsigned int ssi3_data_mux[] = {
+       SSI_SDATA3_MARK,
+};
+static const unsigned int ssi34_ctrl_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(6, 5), RCAR_GP_PIN(6, 6),
+};
+static const unsigned int ssi34_ctrl_mux[] = {
+       SSI_SCK34_MARK, SSI_WS34_MARK,
+};
+static const unsigned int ssi4_data_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(6, 10),
+};
+static const unsigned int ssi4_data_mux[] = {
+       SSI_SDATA4_MARK,
+};
+static const unsigned int ssi4_ctrl_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(6, 8), RCAR_GP_PIN(6, 9),
+};
+static const unsigned int ssi4_ctrl_mux[] = {
+       SSI_SCK4_MARK, SSI_WS4_MARK,
+};
+static const unsigned int ssi5_data_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(6, 13),
+};
+static const unsigned int ssi5_data_mux[] = {
+       SSI_SDATA5_MARK,
+};
+static const unsigned int ssi5_ctrl_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(6, 11), RCAR_GP_PIN(6, 12),
+};
+static const unsigned int ssi5_ctrl_mux[] = {
+       SSI_SCK5_MARK, SSI_WS5_MARK,
+};
+static const unsigned int ssi6_data_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(6, 16),
+};
+static const unsigned int ssi6_data_mux[] = {
+       SSI_SDATA6_MARK,
+};
+static const unsigned int ssi6_ctrl_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(6, 14), RCAR_GP_PIN(6, 15),
+};
+static const unsigned int ssi6_ctrl_mux[] = {
+       SSI_SCK6_MARK, SSI_WS6_MARK,
+};
+static const unsigned int ssi7_data_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(6, 19),
+};
+static const unsigned int ssi7_data_mux[] = {
+       SSI_SDATA7_MARK,
+};
+static const unsigned int ssi78_ctrl_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(6, 17), RCAR_GP_PIN(6, 18),
+};
+static const unsigned int ssi78_ctrl_mux[] = {
+       SSI_SCK78_MARK, SSI_WS78_MARK,
+};
+static const unsigned int ssi8_data_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(6, 20),
+};
+static const unsigned int ssi8_data_mux[] = {
+       SSI_SDATA8_MARK,
+};
+static const unsigned int ssi9_data_a_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(6, 21),
+};
+static const unsigned int ssi9_data_a_mux[] = {
+       SSI_SDATA9_A_MARK,
+};
+static const unsigned int ssi9_data_b_pins[] = {
+       /* SDATA */
+       RCAR_GP_PIN(5, 14),
+};
+static const unsigned int ssi9_data_b_mux[] = {
+       SSI_SDATA9_B_MARK,
+};
+static const unsigned int ssi9_ctrl_a_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(5, 15), RCAR_GP_PIN(5, 16),
+};
+static const unsigned int ssi9_ctrl_a_mux[] = {
+       SSI_SCK9_A_MARK, SSI_WS9_A_MARK,
+};
+static const unsigned int ssi9_ctrl_b_pins[] = {
+       /* SCK, WS */
+       RCAR_GP_PIN(6, 30), RCAR_GP_PIN(6, 31),
+};
+static const unsigned int ssi9_ctrl_b_mux[] = {
+       SSI_SCK9_B_MARK, SSI_WS9_B_MARK,
+};
+
+static const struct sh_pfc_pin_group pinmux_groups[] = {
+       SH_PFC_PIN_GROUP(audio_clk_a_a),
+       SH_PFC_PIN_GROUP(audio_clk_a_b),
+       SH_PFC_PIN_GROUP(audio_clk_a_c),
+       SH_PFC_PIN_GROUP(audio_clk_b_a),
+       SH_PFC_PIN_GROUP(audio_clk_b_b),
+       SH_PFC_PIN_GROUP(audio_clk_c_a),
+       SH_PFC_PIN_GROUP(audio_clk_c_b),
+       SH_PFC_PIN_GROUP(audio_clkout_a),
+       SH_PFC_PIN_GROUP(audio_clkout_b),
+       SH_PFC_PIN_GROUP(audio_clkout_c),
+       SH_PFC_PIN_GROUP(audio_clkout_d),
+       SH_PFC_PIN_GROUP(audio_clkout1_a),
+       SH_PFC_PIN_GROUP(audio_clkout1_b),
+       SH_PFC_PIN_GROUP(audio_clkout2_a),
+       SH_PFC_PIN_GROUP(audio_clkout2_b),
+       SH_PFC_PIN_GROUP(audio_clkout3_a),
+       SH_PFC_PIN_GROUP(audio_clkout3_b),
+       SH_PFC_PIN_GROUP(avb_link),
+       SH_PFC_PIN_GROUP(avb_magic),
+       SH_PFC_PIN_GROUP(avb_phy_int),
+       SH_PFC_PIN_GROUP(avb_mdc),
+       SH_PFC_PIN_GROUP(avb_avtp_pps),
+       SH_PFC_PIN_GROUP(avb_avtp_match_a),
+       SH_PFC_PIN_GROUP(avb_avtp_capture_a),
+       SH_PFC_PIN_GROUP(avb_avtp_match_b),
+       SH_PFC_PIN_GROUP(avb_avtp_capture_b),
+       SH_PFC_PIN_GROUP(i2c1_a),
+       SH_PFC_PIN_GROUP(i2c1_b),
+       SH_PFC_PIN_GROUP(i2c2_a),
+       SH_PFC_PIN_GROUP(i2c2_b),
+       SH_PFC_PIN_GROUP(i2c6_a),
+       SH_PFC_PIN_GROUP(i2c6_b),
+       SH_PFC_PIN_GROUP(i2c6_c),
+       SH_PFC_PIN_GROUP(scif0_data),
+       SH_PFC_PIN_GROUP(scif0_clk),
+       SH_PFC_PIN_GROUP(scif0_ctrl),
+       SH_PFC_PIN_GROUP(scif1_data_a),
+       SH_PFC_PIN_GROUP(scif1_clk),
+       SH_PFC_PIN_GROUP(scif1_ctrl),
+       SH_PFC_PIN_GROUP(scif1_data_b),
+       SH_PFC_PIN_GROUP(scif2_data_a),
+       SH_PFC_PIN_GROUP(scif2_clk),
+       SH_PFC_PIN_GROUP(scif2_data_b),
+       SH_PFC_PIN_GROUP(scif3_data_a),
+       SH_PFC_PIN_GROUP(scif3_clk),
+       SH_PFC_PIN_GROUP(scif3_ctrl),
+       SH_PFC_PIN_GROUP(scif3_data_b),
+       SH_PFC_PIN_GROUP(scif4_data_a),
+       SH_PFC_PIN_GROUP(scif4_clk_a),
+       SH_PFC_PIN_GROUP(scif4_ctrl_a),
+       SH_PFC_PIN_GROUP(scif4_data_b),
+       SH_PFC_PIN_GROUP(scif4_clk_b),
+       SH_PFC_PIN_GROUP(scif4_ctrl_b),
+       SH_PFC_PIN_GROUP(scif4_data_c),
+       SH_PFC_PIN_GROUP(scif4_clk_c),
+       SH_PFC_PIN_GROUP(scif4_ctrl_c),
+       SH_PFC_PIN_GROUP(scif5_data),
+       SH_PFC_PIN_GROUP(scif5_clk),
+       SH_PFC_PIN_GROUP(ssi0_data),
+       SH_PFC_PIN_GROUP(ssi01239_ctrl),
+       SH_PFC_PIN_GROUP(ssi1_data_a),
+       SH_PFC_PIN_GROUP(ssi1_data_b),
+       SH_PFC_PIN_GROUP(ssi1_ctrl_a),
+       SH_PFC_PIN_GROUP(ssi1_ctrl_b),
+       SH_PFC_PIN_GROUP(ssi2_data_a),
+       SH_PFC_PIN_GROUP(ssi2_data_b),
+       SH_PFC_PIN_GROUP(ssi2_ctrl_a),
+       SH_PFC_PIN_GROUP(ssi2_ctrl_b),
+       SH_PFC_PIN_GROUP(ssi3_data),
+       SH_PFC_PIN_GROUP(ssi34_ctrl),
+       SH_PFC_PIN_GROUP(ssi4_data),
+       SH_PFC_PIN_GROUP(ssi4_ctrl),
+       SH_PFC_PIN_GROUP(ssi5_data),
+       SH_PFC_PIN_GROUP(ssi5_ctrl),
+       SH_PFC_PIN_GROUP(ssi6_data),
+       SH_PFC_PIN_GROUP(ssi6_ctrl),
+       SH_PFC_PIN_GROUP(ssi7_data),
+       SH_PFC_PIN_GROUP(ssi78_ctrl),
+       SH_PFC_PIN_GROUP(ssi8_data),
+       SH_PFC_PIN_GROUP(ssi9_data_a),
+       SH_PFC_PIN_GROUP(ssi9_data_b),
+       SH_PFC_PIN_GROUP(ssi9_ctrl_a),
+       SH_PFC_PIN_GROUP(ssi9_ctrl_b),
+};
+
+static const char * const audio_clk_groups[] = {
+       "audio_clk_a_a",
+       "audio_clk_a_b",
+       "audio_clk_a_c",
+       "audio_clk_b_a",
+       "audio_clk_b_b",
+       "audio_clk_c_a",
+       "audio_clk_c_b",
+       "audio_clkout_a",
+       "audio_clkout_b",
+       "audio_clkout_c",
+       "audio_clkout_d",
+       "audio_clkout1_a",
+       "audio_clkout1_b",
+       "audio_clkout2_a",
+       "audio_clkout2_b",
+       "audio_clkout3_a",
+       "audio_clkout3_b",
+};
+
+static const char * const avb_groups[] = {
+       "avb_link",
+       "avb_magic",
+       "avb_phy_int",
+       "avb_mdc",
+       "avb_avtp_pps",
+       "avb_avtp_match_a",
+       "avb_avtp_capture_a",
+       "avb_avtp_match_b",
+       "avb_avtp_capture_b",
+};
+
+static const char * const i2c1_groups[] = {
+       "i2c1_a",
+       "i2c1_b",
+};
+
+static const char * const i2c2_groups[] = {
+       "i2c2_a",
+       "i2c2_b",
+};
+
+static const char * const i2c6_groups[] = {
+       "i2c6_a",
+       "i2c6_b",
+       "i2c6_c",
+};
+
+static const char * const scif0_groups[] = {
+       "scif0_data",
+       "scif0_clk",
+       "scif0_ctrl",
+};
+
+static const char * const scif1_groups[] = {
+       "scif1_data_a",
+       "scif1_clk",
+       "scif1_ctrl",
+       "scif1_data_b",
+};
+
+static const char * const scif2_groups[] = {
+       "scif2_data_a",
+       "scif2_clk",
+       "scif2_data_b",
+};
+
+static const char * const scif3_groups[] = {
+       "scif3_data_a",
+       "scif3_clk",
+       "scif3_ctrl",
+       "scif3_data_b",
+};
+
+static const char * const scif4_groups[] = {
+       "scif4_data_a",
+       "scif4_clk_a",
+       "scif4_ctrl_a",
+       "scif4_data_b",
+       "scif4_clk_b",
+       "scif4_ctrl_b",
+       "scif4_data_c",
+       "scif4_clk_c",
+       "scif4_ctrl_c",
+};
+
+static const char * const scif5_groups[] = {
+       "scif5_data",
+       "scif5_clk",
+};
+
+static const char * const ssi_groups[] = {
+       "ssi0_data",
+       "ssi01239_ctrl",
+       "ssi1_data_a",
+       "ssi1_data_b",
+       "ssi1_ctrl_a",
+       "ssi1_ctrl_b",
+       "ssi2_data_a",
+       "ssi2_data_b",
+       "ssi2_ctrl_a",
+       "ssi2_ctrl_b",
+       "ssi3_data",
+       "ssi34_ctrl",
+       "ssi4_data",
+       "ssi4_ctrl",
+       "ssi5_data",
+       "ssi5_ctrl",
+       "ssi6_data",
+       "ssi6_ctrl",
+       "ssi7_data",
+       "ssi78_ctrl",
+       "ssi8_data",
+       "ssi9_data_a",
+       "ssi9_data_b",
+       "ssi9_ctrl_a",
+       "ssi9_ctrl_b",
+};
+
+static const struct sh_pfc_function pinmux_functions[] = {
+       SH_PFC_FUNCTION(audio_clk),
+       SH_PFC_FUNCTION(avb),
+       SH_PFC_FUNCTION(i2c1),
+       SH_PFC_FUNCTION(i2c2),
+       SH_PFC_FUNCTION(i2c6),
+       SH_PFC_FUNCTION(scif0),
+       SH_PFC_FUNCTION(scif1),
+       SH_PFC_FUNCTION(scif2),
+       SH_PFC_FUNCTION(scif3),
+       SH_PFC_FUNCTION(scif4),
+       SH_PFC_FUNCTION(scif5),
+       SH_PFC_FUNCTION(ssi),
+};
+
+static const struct pinmux_cfg_reg pinmux_config_regs[] = {
+#define F_(x, y)       FN_##y
+#define FM(x)          FN_##x
+       { PINMUX_CFG_REG("GPSR0", 0xe6060100, 32, 1) {
+               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, 0,
+               GP_0_15_FN,     GPSR0_15,
+               GP_0_14_FN,     GPSR0_14,
+               GP_0_13_FN,     GPSR0_13,
+               GP_0_12_FN,     GPSR0_12,
+               GP_0_11_FN,     GPSR0_11,
+               GP_0_10_FN,     GPSR0_10,
+               GP_0_9_FN,      GPSR0_9,
+               GP_0_8_FN,      GPSR0_8,
+               GP_0_7_FN,      GPSR0_7,
+               GP_0_6_FN,      GPSR0_6,
+               GP_0_5_FN,      GPSR0_5,
+               GP_0_4_FN,      GPSR0_4,
+               GP_0_3_FN,      GPSR0_3,
+               GP_0_2_FN,      GPSR0_2,
+               GP_0_1_FN,      GPSR0_1,
+               GP_0_0_FN,      GPSR0_0, }
+       },
+       { PINMUX_CFG_REG("GPSR1", 0xe6060104, 32, 1) {
+               0, 0,
+               0, 0,
+               0, 0,
+               0, 0,
+               GP_1_27_FN,     GPSR1_27,
+               GP_1_26_FN,     GPSR1_26,
+               GP_1_25_FN,     GPSR1_25,
+               GP_1_24_FN,     GPSR1_24,
+               GP_1_23_FN,     GPSR1_23,
+               GP_1_22_FN,     GPSR1_22,
+               GP_1_21_FN,     GPSR1_21,
+               GP_1_20_FN,     GPSR1_20,
+               GP_1_19_FN,     GPSR1_19,
+               GP_1_18_FN,     GPSR1_18,
+               GP_1_17_FN,     GPSR1_17,
+               GP_1_16_FN,     GPSR1_16,
+               GP_1_15_FN,     GPSR1_15,
+               GP_1_14_FN,     GPSR1_14,
+               GP_1_13_FN,     GPSR1_13,
+               GP_1_12_FN,     GPSR1_12,
+               GP_1_11_FN,     GPSR1_11,
+               GP_1_10_FN,     GPSR1_10,
+               GP_1_9_FN,      GPSR1_9,
+               GP_1_8_FN,      GPSR1_8,
+               GP_1_7_FN,      GPSR1_7,
+               GP_1_6_FN,      GPSR1_6,
+               GP_1_5_FN,      GPSR1_5,
+               GP_1_4_FN,      GPSR1_4,
+               GP_1_3_FN,      GPSR1_3,
+               GP_1_2_FN,      GPSR1_2,
+               GP_1_1_FN,      GPSR1_1,
+               GP_1_0_FN,      GPSR1_0, }
+       },
+       { PINMUX_CFG_REG("GPSR2", 0xe6060108, 32, 1) {
+               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, 0,
+               0, 0,
+               GP_2_14_FN,     GPSR2_14,
+               GP_2_13_FN,     GPSR2_13,
+               GP_2_12_FN,     GPSR2_12,
+               GP_2_11_FN,     GPSR2_11,
+               GP_2_10_FN,     GPSR2_10,
+               GP_2_9_FN,      GPSR2_9,
+               GP_2_8_FN,      GPSR2_8,
+               GP_2_7_FN,      GPSR2_7,
+               GP_2_6_FN,      GPSR2_6,
+               GP_2_5_FN,      GPSR2_5,
+               GP_2_4_FN,      GPSR2_4,
+               GP_2_3_FN,      GPSR2_3,
+               GP_2_2_FN,      GPSR2_2,
+               GP_2_1_FN,      GPSR2_1,
+               GP_2_0_FN,      GPSR2_0, }
+       },
+       { PINMUX_CFG_REG("GPSR3", 0xe606010c, 32, 1) {
+               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, 0,
+               GP_3_15_FN,     GPSR3_15,
+               GP_3_14_FN,     GPSR3_14,
+               GP_3_13_FN,     GPSR3_13,
+               GP_3_12_FN,     GPSR3_12,
+               GP_3_11_FN,     GPSR3_11,
+               GP_3_10_FN,     GPSR3_10,
+               GP_3_9_FN,      GPSR3_9,
+               GP_3_8_FN,      GPSR3_8,
+               GP_3_7_FN,      GPSR3_7,
+               GP_3_6_FN,      GPSR3_6,
+               GP_3_5_FN,      GPSR3_5,
+               GP_3_4_FN,      GPSR3_4,
+               GP_3_3_FN,      GPSR3_3,
+               GP_3_2_FN,      GPSR3_2,
+               GP_3_1_FN,      GPSR3_1,
+               GP_3_0_FN,      GPSR3_0, }
+       },
+       { PINMUX_CFG_REG("GPSR4", 0xe6060110, 32, 1) {
+               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,
+               GP_4_17_FN,     GPSR4_17,
+               GP_4_16_FN,     GPSR4_16,
+               GP_4_15_FN,     GPSR4_15,
+               GP_4_14_FN,     GPSR4_14,
+               GP_4_13_FN,     GPSR4_13,
+               GP_4_12_FN,     GPSR4_12,
+               GP_4_11_FN,     GPSR4_11,
+               GP_4_10_FN,     GPSR4_10,
+               GP_4_9_FN,      GPSR4_9,
+               GP_4_8_FN,      GPSR4_8,
+               GP_4_7_FN,      GPSR4_7,
+               GP_4_6_FN,      GPSR4_6,
+               GP_4_5_FN,      GPSR4_5,
+               GP_4_4_FN,      GPSR4_4,
+               GP_4_3_FN,      GPSR4_3,
+               GP_4_2_FN,      GPSR4_2,
+               GP_4_1_FN,      GPSR4_1,
+               GP_4_0_FN,      GPSR4_0, }
+       },
+       { PINMUX_CFG_REG("GPSR5", 0xe6060114, 32, 1) {
+               0, 0,
+               0, 0,
+               0, 0,
+               0, 0,
+               0, 0,
+               0, 0,
+               GP_5_25_FN,     GPSR5_25,
+               GP_5_24_FN,     GPSR5_24,
+               GP_5_23_FN,     GPSR5_23,
+               GP_5_22_FN,     GPSR5_22,
+               GP_5_21_FN,     GPSR5_21,
+               GP_5_20_FN,     GPSR5_20,
+               GP_5_19_FN,     GPSR5_19,
+               GP_5_18_FN,     GPSR5_18,
+               GP_5_17_FN,     GPSR5_17,
+               GP_5_16_FN,     GPSR5_16,
+               GP_5_15_FN,     GPSR5_15,
+               GP_5_14_FN,     GPSR5_14,
+               GP_5_13_FN,     GPSR5_13,
+               GP_5_12_FN,     GPSR5_12,
+               GP_5_11_FN,     GPSR5_11,
+               GP_5_10_FN,     GPSR5_10,
+               GP_5_9_FN,      GPSR5_9,
+               GP_5_8_FN,      GPSR5_8,
+               GP_5_7_FN,      GPSR5_7,
+               GP_5_6_FN,      GPSR5_6,
+               GP_5_5_FN,      GPSR5_5,
+               GP_5_4_FN,      GPSR5_4,
+               GP_5_3_FN,      GPSR5_3,
+               GP_5_2_FN,      GPSR5_2,
+               GP_5_1_FN,      GPSR5_1,
+               GP_5_0_FN,      GPSR5_0, }
+       },
+       { PINMUX_CFG_REG("GPSR6", 0xe6060118, 32, 1) {
+               GP_6_31_FN,     GPSR6_31,
+               GP_6_30_FN,     GPSR6_30,
+               GP_6_29_FN,     GPSR6_29,
+               GP_6_28_FN,     GPSR6_28,
+               GP_6_27_FN,     GPSR6_27,
+               GP_6_26_FN,     GPSR6_26,
+               GP_6_25_FN,     GPSR6_25,
+               GP_6_24_FN,     GPSR6_24,
+               GP_6_23_FN,     GPSR6_23,
+               GP_6_22_FN,     GPSR6_22,
+               GP_6_21_FN,     GPSR6_21,
+               GP_6_20_FN,     GPSR6_20,
+               GP_6_19_FN,     GPSR6_19,
+               GP_6_18_FN,     GPSR6_18,
+               GP_6_17_FN,     GPSR6_17,
+               GP_6_16_FN,     GPSR6_16,
+               GP_6_15_FN,     GPSR6_15,
+               GP_6_14_FN,     GPSR6_14,
+               GP_6_13_FN,     GPSR6_13,
+               GP_6_12_FN,     GPSR6_12,
+               GP_6_11_FN,     GPSR6_11,
+               GP_6_10_FN,     GPSR6_10,
+               GP_6_9_FN,      GPSR6_9,
+               GP_6_8_FN,      GPSR6_8,
+               GP_6_7_FN,      GPSR6_7,
+               GP_6_6_FN,      GPSR6_6,
+               GP_6_5_FN,      GPSR6_5,
+               GP_6_4_FN,      GPSR6_4,
+               GP_6_3_FN,      GPSR6_3,
+               GP_6_2_FN,      GPSR6_2,
+               GP_6_1_FN,      GPSR6_1,
+               GP_6_0_FN,      GPSR6_0, }
+       },
+       { PINMUX_CFG_REG("GPSR7", 0xe606011c, 32, 1) {
+               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, 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,
+               GP_7_3_FN, GPSR7_3,
+               GP_7_2_FN, GPSR7_2,
+               GP_7_1_FN, GPSR7_1,
+               GP_7_0_FN, GPSR7_0, }
+       },
+#undef F_
+#undef FM
+
+#define F_(x, y)       x,
+#define FM(x)          FN_##x,
+       { PINMUX_CFG_REG("IPSR0", 0xe6060200, 32, 4) {
+               IP0_31_28
+               IP0_27_24
+               IP0_23_20
+               IP0_19_16
+               IP0_15_12
+               IP0_11_8
+               IP0_7_4
+               IP0_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR1", 0xe6060204, 32, 4) {
+               IP1_31_28
+               IP1_27_24
+               IP1_23_20
+               IP1_19_16
+               IP1_15_12
+               IP1_11_8
+               IP1_7_4
+               IP1_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR2", 0xe6060208, 32, 4) {
+               IP2_31_28
+               IP2_27_24
+               IP2_23_20
+               IP2_19_16
+               IP2_15_12
+               IP2_11_8
+               IP2_7_4
+               IP2_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR3", 0xe606020c, 32, 4) {
+               IP3_31_28
+               IP3_27_24
+               IP3_23_20
+               IP3_19_16
+               IP3_15_12
+               IP3_11_8
+               IP3_7_4
+               IP3_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR4", 0xe6060210, 32, 4) {
+               IP4_31_28
+               IP4_27_24
+               IP4_23_20
+               IP4_19_16
+               IP4_15_12
+               IP4_11_8
+               IP4_7_4
+               IP4_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR5", 0xe6060214, 32, 4) {
+               IP5_31_28
+               IP5_27_24
+               IP5_23_20
+               IP5_19_16
+               IP5_15_12
+               IP5_11_8
+               IP5_7_4
+               IP5_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR6", 0xe6060218, 32, 4) {
+               IP6_31_28
+               IP6_27_24
+               IP6_23_20
+               IP6_19_16
+               IP6_15_12
+               IP6_11_8
+               IP6_7_4
+               IP6_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR7", 0xe606021c, 32, 4) {
+               IP7_31_28
+               IP7_27_24
+               IP7_23_20
+               IP7_19_16
+               IP7_15_12
+               IP7_11_8
+               IP7_7_4
+               IP7_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR8", 0xe6060220, 32, 4) {
+               IP8_31_28
+               IP8_27_24
+               IP8_23_20
+               IP8_19_16
+               IP8_15_12
+               IP8_11_8
+               IP8_7_4
+               IP8_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR9", 0xe6060224, 32, 4) {
+               IP9_31_28
+               IP9_27_24
+               IP9_23_20
+               IP9_19_16
+               IP9_15_12
+               IP9_11_8
+               IP9_7_4
+               IP9_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR10", 0xe6060228, 32, 4) {
+               IP10_31_28
+               IP10_27_24
+               IP10_23_20
+               IP10_19_16
+               IP10_15_12
+               IP10_11_8
+               IP10_7_4
+               IP10_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR11", 0xe606022c, 32, 4) {
+               IP11_31_28
+               IP11_27_24
+               IP11_23_20
+               IP11_19_16
+               IP11_15_12
+               IP11_11_8
+               IP11_7_4
+               IP11_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR12", 0xe6060230, 32, 4) {
+               IP12_31_28
+               IP12_27_24
+               IP12_23_20
+               IP12_19_16
+               IP12_15_12
+               IP12_11_8
+               IP12_7_4
+               IP12_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR13", 0xe6060234, 32, 4) {
+               IP13_31_28
+               IP13_27_24
+               IP13_23_20
+               IP13_19_16
+               IP13_15_12
+               IP13_11_8
+               IP13_7_4
+               IP13_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR14", 0xe6060238, 32, 4) {
+               IP14_31_28
+               IP14_27_24
+               IP14_23_20
+               IP14_19_16
+               IP14_15_12
+               IP14_11_8
+               IP14_7_4
+               IP14_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR15", 0xe606023c, 32, 4) {
+               IP15_31_28
+               IP15_27_24
+               IP15_23_20
+               IP15_19_16
+               IP15_15_12
+               IP15_11_8
+               IP15_7_4
+               IP15_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR16", 0xe6060240, 32, 4) {
+               IP16_31_28
+               IP16_27_24
+               IP16_23_20
+               IP16_19_16
+               IP16_15_12
+               IP16_11_8
+               IP16_7_4
+               IP16_3_0 }
+       },
+       { PINMUX_CFG_REG("IPSR17", 0xe6060244, 32, 4) {
+               /* IP17_31_28 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               /* IP17_27_24 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               /* IP17_23_20 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               /* IP17_19_16 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               /* IP17_15_12 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               /* IP17_11_8  */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               IP17_7_4
+               IP17_3_0 }
+       },
+#undef F_
+#undef FM
+
+#define F_(x, y)       x,
+#define FM(x)          FN_##x,
+       { PINMUX_CFG_REG_VAR("MOD_SEL0", 0xe6060500, 32,
+                            1, 2, 2, 3, 1, 1, 2, 1, 1, 1,
+                            2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1) {
+               0, 0, /* RESERVED 31 */
+               MOD_SEL0_30_29
+               MOD_SEL0_28_27
+               MOD_SEL0_26_25_24
+               MOD_SEL0_23
+               MOD_SEL0_22
+               MOD_SEL0_21_20
+               MOD_SEL0_19
+               MOD_SEL0_18
+               MOD_SEL0_17
+               MOD_SEL0_16_15
+               MOD_SEL0_14
+               MOD_SEL0_13
+               MOD_SEL0_12
+               MOD_SEL0_11
+               MOD_SEL0_10
+               MOD_SEL0_9
+               MOD_SEL0_8
+               MOD_SEL0_7_6
+               MOD_SEL0_5_4
+               MOD_SEL0_3
+               MOD_SEL0_2_1
+               0, 0, /* RESERVED 0 */ }
+       },
+       { PINMUX_CFG_REG_VAR("MOD_SEL1", 0xe6060504, 32,
+                            2, 3, 1, 2, 3, 1, 1, 2, 1,
+                            2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1) {
+               MOD_SEL1_31_30
+               MOD_SEL1_29_28_27
+               MOD_SEL1_26
+               MOD_SEL1_25_24
+               MOD_SEL1_23_22_21
+               MOD_SEL1_20
+               MOD_SEL1_19
+               MOD_SEL1_18_17
+               MOD_SEL1_16
+               MOD_SEL1_15_14
+               MOD_SEL1_13
+               MOD_SEL1_12
+               MOD_SEL1_11
+               MOD_SEL1_10
+               MOD_SEL1_9
+               0, 0, 0, 0, /* RESERVED 8, 7 */
+               MOD_SEL1_6
+               MOD_SEL1_5
+               MOD_SEL1_4
+               MOD_SEL1_3
+               MOD_SEL1_2
+               MOD_SEL1_1
+               MOD_SEL1_0 }
+       },
+       { PINMUX_CFG_REG_VAR("MOD_SEL2", 0xe6060508, 32,
+                            1, 1, 1, 1, 4, 4, 4,
+                            4, 4, 4, 1, 2, 1) {
+               MOD_SEL2_31
+               MOD_SEL2_30
+               MOD_SEL2_29
+               /* RESERVED 28 */
+               0, 0,
+               /* RESERVED 27, 26, 25, 24 */
+               0, 0, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0,
+               /* RESERVED 23, 22, 21, 20 */
+               0, 0, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0,
+               /* RESERVED 19, 18, 17, 16 */
+               0, 0, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0,
+               /* RESERVED 15, 14, 13, 12 */
+               0, 0, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0,
+               /* RESERVED 11, 10, 9, 8 */
+               0, 0, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0,
+               /* RESERVED 7, 6, 5, 4 */
+               0, 0, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0,
+               /* RESERVED 3 */
+               0, 0,
+               MOD_SEL2_2_1
+               MOD_SEL2_0 }
+       },
+       { },
+};
+
+const struct sh_pfc_soc_info r8a7795_pinmux_info = {
+       .name = "r8a77950_pfc",
+       .unlock_reg = 0xe6060000, /* PMMR */
+
+       .function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END },
+
+       .pins = pinmux_pins,
+       .nr_pins = ARRAY_SIZE(pinmux_pins),
+       .groups = pinmux_groups,
+       .nr_groups = ARRAY_SIZE(pinmux_groups),
+       .functions = pinmux_functions,
+       .nr_functions = ARRAY_SIZE(pinmux_functions),
+
+       .cfg_regs = pinmux_config_regs,
+
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
+};
index 3bda7bafd0ab5a55ff81c20d1183d72934bda8d6..61b27ec48876ece871726a644b64b11d7bc47d58 100644 (file)
@@ -1587,6 +1587,6 @@ const struct sh_pfc_soc_info sh7203_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index e1cb6dc0502851734828e7edf3acb01106474891..8070765311dbf7a488fe5edd9d3d7ce92e2f331a 100644 (file)
@@ -2126,6 +2126,6 @@ const struct sh_pfc_soc_info sh7264_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index 7a11320ad96d391adcb02b6c17d942241ecc2c37..a50d22bef1f444517523c6bce95dc091a7d42b9a 100644 (file)
@@ -2830,6 +2830,6 @@ const struct sh_pfc_soc_info sh7269_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index 097526576f88181f24188cc6ff3aa5305e5d1e2e..6a69c8c5d9433d7ed8af781e29c41b86a52c98ed 100644 (file)
@@ -3649,38 +3649,38 @@ static const struct pinmux_data_reg pinmux_data_regs[] = {
 };
 
 static const struct pinmux_irq pinmux_irqs[] = {
-       PINMUX_IRQ(irq_pin(0), 11),
-       PINMUX_IRQ(irq_pin(1), 10),
-       PINMUX_IRQ(irq_pin(2), 149),
-       PINMUX_IRQ(irq_pin(3), 224),
-       PINMUX_IRQ(irq_pin(4), 159),
-       PINMUX_IRQ(irq_pin(5), 227),
-       PINMUX_IRQ(irq_pin(6), 147),
-       PINMUX_IRQ(irq_pin(7), 150),
-       PINMUX_IRQ(irq_pin(8), 223),
-       PINMUX_IRQ(irq_pin(9), 56, 308),
-       PINMUX_IRQ(irq_pin(10), 54),
-       PINMUX_IRQ(irq_pin(11), 238),
-       PINMUX_IRQ(irq_pin(12), 156),
-       PINMUX_IRQ(irq_pin(13), 239),
-       PINMUX_IRQ(irq_pin(14), 251),
-       PINMUX_IRQ(irq_pin(15), 0),
-       PINMUX_IRQ(irq_pin(16), 249),
-       PINMUX_IRQ(irq_pin(17), 234),
-       PINMUX_IRQ(irq_pin(18), 13),
-       PINMUX_IRQ(irq_pin(19), 9),
-       PINMUX_IRQ(irq_pin(20), 14),
-       PINMUX_IRQ(irq_pin(21), 15),
-       PINMUX_IRQ(irq_pin(22), 40),
-       PINMUX_IRQ(irq_pin(23), 53),
-       PINMUX_IRQ(irq_pin(24), 118),
-       PINMUX_IRQ(irq_pin(25), 164),
-       PINMUX_IRQ(irq_pin(26), 115),
-       PINMUX_IRQ(irq_pin(27), 116),
-       PINMUX_IRQ(irq_pin(28), 117),
-       PINMUX_IRQ(irq_pin(29), 28),
-       PINMUX_IRQ(irq_pin(30), 27),
-       PINMUX_IRQ(irq_pin(31), 26),
+       PINMUX_IRQ(11),         /* IRQ0 */
+       PINMUX_IRQ(10),         /* IRQ1 */
+       PINMUX_IRQ(149),        /* IRQ2 */
+       PINMUX_IRQ(224),        /* IRQ3 */
+       PINMUX_IRQ(159),        /* IRQ4 */
+       PINMUX_IRQ(227),        /* IRQ5 */
+       PINMUX_IRQ(147),        /* IRQ6 */
+       PINMUX_IRQ(150),        /* IRQ7 */
+       PINMUX_IRQ(223),        /* IRQ8 */
+       PINMUX_IRQ(56, 308),    /* IRQ9 */
+       PINMUX_IRQ(54),         /* IRQ10 */
+       PINMUX_IRQ(238),        /* IRQ11 */
+       PINMUX_IRQ(156),        /* IRQ12 */
+       PINMUX_IRQ(239),        /* IRQ13 */
+       PINMUX_IRQ(251),        /* IRQ14 */
+       PINMUX_IRQ(0),          /* IRQ15 */
+       PINMUX_IRQ(249),        /* IRQ16 */
+       PINMUX_IRQ(234),        /* IRQ17 */
+       PINMUX_IRQ(13),         /* IRQ18 */
+       PINMUX_IRQ(9),          /* IRQ19 */
+       PINMUX_IRQ(14),         /* IRQ20 */
+       PINMUX_IRQ(15),         /* IRQ21 */
+       PINMUX_IRQ(40),         /* IRQ22 */
+       PINMUX_IRQ(53),         /* IRQ23 */
+       PINMUX_IRQ(118),        /* IRQ24 */
+       PINMUX_IRQ(164),        /* IRQ25 */
+       PINMUX_IRQ(115),        /* IRQ26 */
+       PINMUX_IRQ(116),        /* IRQ27 */
+       PINMUX_IRQ(117),        /* IRQ28 */
+       PINMUX_IRQ(28),         /* IRQ29 */
+       PINMUX_IRQ(27),         /* IRQ30 */
+       PINMUX_IRQ(26),         /* IRQ31 */
 };
 
 /* -----------------------------------------------------------------------------
@@ -3865,8 +3865,8 @@ const struct sh_pfc_soc_info sh73a0_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 
        .gpio_irq = pinmux_irqs,
        .gpio_irq_size = ARRAY_SIZE(pinmux_irqs),
index 13d05f88bc01c78db572a70924a821f376245931..e07a82df42c83a8f9cd498f219e5c58aaf19f087 100644 (file)
@@ -1201,6 +1201,6 @@ const struct sh_pfc_soc_info sh7720_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index 914d872c37a40a2198ea5a26c7e875771e19814d..29c69133b0ef88d0e217941cfb0ff12be43ecb91 100644 (file)
@@ -1741,6 +1741,6 @@ const struct sh_pfc_soc_info sh7722_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index 4eb7eae2e6d0406573c325b254b0aefdaa77f067..8ea18df034927948518ca087f0b7e0b3a3d43fdb 100644 (file)
@@ -1893,6 +1893,6 @@ const struct sh_pfc_soc_info sh7723_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index 74a1a7f1317c7ce097c24abcb85da143a6097f09..7f6c36c1a8fa8db510e70367c125c0d14d3830f8 100644 (file)
@@ -2175,6 +2175,6 @@ const struct sh_pfc_soc_info sh7724_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index e53dd1cb1625d6105f04ef38848c8d6d3f06a65d..e7deb51de7dc4fd4c76bc1b7483ac2127e1da850 100644 (file)
@@ -598,502 +598,502 @@ static const u16 pinmux_data[] = {
        /* IPSR0 */
        PINMUX_IPSR_DATA(IP0_1_0, A0),
        PINMUX_IPSR_DATA(IP0_1_0, ST0_CLKIN),
-       PINMUX_IPSR_MODSEL_DATA(IP0_1_0, LCD_DATA0_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_1_0, TCLKA_C, SEL_MTU2_CLK_1),
+       PINMUX_IPSR_MSEL(IP0_1_0, LCD_DATA0_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_1_0, TCLKA_C, SEL_MTU2_CLK_1),
 
        PINMUX_IPSR_DATA(IP0_3_2, A1),
        PINMUX_IPSR_DATA(IP0_3_2, ST0_REQ),
-       PINMUX_IPSR_MODSEL_DATA(IP0_3_2, LCD_DATA1_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_3_2, TCLKB_C, SEL_MTU2_CLK_1),
+       PINMUX_IPSR_MSEL(IP0_3_2, LCD_DATA1_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_3_2, TCLKB_C, SEL_MTU2_CLK_1),
 
        PINMUX_IPSR_DATA(IP0_5_4, A2),
        PINMUX_IPSR_DATA(IP0_5_4, ST0_SYC),
-       PINMUX_IPSR_MODSEL_DATA(IP0_5_4, LCD_DATA2_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_5_4, TCLKC_C, SEL_MTU2_CLK_1),
+       PINMUX_IPSR_MSEL(IP0_5_4, LCD_DATA2_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_5_4, TCLKC_C, SEL_MTU2_CLK_1),
 
        PINMUX_IPSR_DATA(IP0_7_6, A3),
        PINMUX_IPSR_DATA(IP0_7_6, ST0_VLD),
-       PINMUX_IPSR_MODSEL_DATA(IP0_7_6, LCD_DATA3_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_7_6, TCLKD_C, SEL_MTU2_CLK_1),
+       PINMUX_IPSR_MSEL(IP0_7_6, LCD_DATA3_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_7_6, TCLKD_C, SEL_MTU2_CLK_1),
 
        PINMUX_IPSR_DATA(IP0_9_8, A4),
        PINMUX_IPSR_DATA(IP0_9_8, ST0_D0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_9_8, LCD_DATA4_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_9_8, TIOC0A_C, SEL_MTU2_CH0_1),
+       PINMUX_IPSR_MSEL(IP0_9_8, LCD_DATA4_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_9_8, TIOC0A_C, SEL_MTU2_CH0_1),
 
        PINMUX_IPSR_DATA(IP0_11_10, A5),
        PINMUX_IPSR_DATA(IP0_11_10, ST0_D1),
-       PINMUX_IPSR_MODSEL_DATA(IP0_11_10, LCD_DATA5_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_11_10, TIOC0B_C, SEL_MTU2_CH0_1),
+       PINMUX_IPSR_MSEL(IP0_11_10, LCD_DATA5_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_11_10, TIOC0B_C, SEL_MTU2_CH0_1),
 
        PINMUX_IPSR_DATA(IP0_13_12, A6),
        PINMUX_IPSR_DATA(IP0_13_12, ST0_D2),
-       PINMUX_IPSR_MODSEL_DATA(IP0_13_12, LCD_DATA6_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_13_12, TIOC0C_C, SEL_MTU2_CH0_1),
+       PINMUX_IPSR_MSEL(IP0_13_12, LCD_DATA6_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_13_12, TIOC0C_C, SEL_MTU2_CH0_1),
 
        PINMUX_IPSR_DATA(IP0_15_14, A7),
        PINMUX_IPSR_DATA(IP0_15_14, ST0_D3),
-       PINMUX_IPSR_MODSEL_DATA(IP0_15_14, LCD_DATA7_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_15_14, TIOC0D_C, SEL_MTU2_CH0_1),
+       PINMUX_IPSR_MSEL(IP0_15_14, LCD_DATA7_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_15_14, TIOC0D_C, SEL_MTU2_CH0_1),
 
        PINMUX_IPSR_DATA(IP0_17_16, A8),
        PINMUX_IPSR_DATA(IP0_17_16, ST0_D4),
-       PINMUX_IPSR_MODSEL_DATA(IP0_17_16, LCD_DATA8_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_17_16, TIOC1A_C, SEL_MTU2_CH1_2),
+       PINMUX_IPSR_MSEL(IP0_17_16, LCD_DATA8_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_17_16, TIOC1A_C, SEL_MTU2_CH1_2),
 
        PINMUX_IPSR_DATA(IP0_19_18, A9),
        PINMUX_IPSR_DATA(IP0_19_18, ST0_D5),
-       PINMUX_IPSR_MODSEL_DATA(IP0_19_18, LCD_DATA9_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_19_18, TIOC1B_C, SEL_MTU2_CH1_2),
+       PINMUX_IPSR_MSEL(IP0_19_18, LCD_DATA9_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_19_18, TIOC1B_C, SEL_MTU2_CH1_2),
 
        PINMUX_IPSR_DATA(IP0_21_20, A10),
        PINMUX_IPSR_DATA(IP0_21_20, ST0_D6),
-       PINMUX_IPSR_MODSEL_DATA(IP0_21_20, LCD_DATA10_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_21_20, TIOC2A_C, SEL_MTU2_CH2_2),
+       PINMUX_IPSR_MSEL(IP0_21_20, LCD_DATA10_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_21_20, TIOC2A_C, SEL_MTU2_CH2_2),
 
        PINMUX_IPSR_DATA(IP0_23_22, A11),
        PINMUX_IPSR_DATA(IP0_23_22, ST0_D7),
-       PINMUX_IPSR_MODSEL_DATA(IP0_23_22, LCD_DATA11_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_23_22, TIOC2B_C, SEL_MTU2_CH2_2),
+       PINMUX_IPSR_MSEL(IP0_23_22, LCD_DATA11_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_23_22, TIOC2B_C, SEL_MTU2_CH2_2),
 
        PINMUX_IPSR_DATA(IP0_25_24, A12),
-       PINMUX_IPSR_MODSEL_DATA(IP0_25_24, LCD_DATA12_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_25_24, TIOC3A_C, SEL_MTU2_CH3_1),
+       PINMUX_IPSR_MSEL(IP0_25_24, LCD_DATA12_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_25_24, TIOC3A_C, SEL_MTU2_CH3_1),
 
        PINMUX_IPSR_DATA(IP0_27_26, A13),
-       PINMUX_IPSR_MODSEL_DATA(IP0_27_26, LCD_DATA13_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_27_26, TIOC3B_C, SEL_MTU2_CH3_1),
+       PINMUX_IPSR_MSEL(IP0_27_26, LCD_DATA13_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_27_26, TIOC3B_C, SEL_MTU2_CH3_1),
 
        PINMUX_IPSR_DATA(IP0_29_28, A14),
-       PINMUX_IPSR_MODSEL_DATA(IP0_29_28, LCD_DATA14_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_29_28, TIOC3C_C, SEL_MTU2_CH3_1),
+       PINMUX_IPSR_MSEL(IP0_29_28, LCD_DATA14_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_29_28, TIOC3C_C, SEL_MTU2_CH3_1),
 
        PINMUX_IPSR_DATA(IP0_31_30, A15),
        PINMUX_IPSR_DATA(IP0_31_30, ST0_VCO_CLKIN),
-       PINMUX_IPSR_MODSEL_DATA(IP0_31_30, LCD_DATA15_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP0_31_30, TIOC3D_C, SEL_MTU2_CH3_1),
+       PINMUX_IPSR_MSEL(IP0_31_30, LCD_DATA15_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP0_31_30, TIOC3D_C, SEL_MTU2_CH3_1),
 
 
        /* IPSR1 */
        PINMUX_IPSR_DATA(IP1_1_0, A16),
        PINMUX_IPSR_DATA(IP1_1_0, ST0_PWM),
-       PINMUX_IPSR_MODSEL_DATA(IP1_1_0, LCD_DON_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_1_0, TIOC4A_C, SEL_MTU2_CH4_1),
+       PINMUX_IPSR_MSEL(IP1_1_0, LCD_DON_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP1_1_0, TIOC4A_C, SEL_MTU2_CH4_1),
 
        PINMUX_IPSR_DATA(IP1_3_2, A17),
        PINMUX_IPSR_DATA(IP1_3_2, ST1_VCO_CLKIN),
-       PINMUX_IPSR_MODSEL_DATA(IP1_3_2, LCD_CL1_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_3_2, TIOC4B_C, SEL_MTU2_CH4_1),
+       PINMUX_IPSR_MSEL(IP1_3_2, LCD_CL1_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP1_3_2, TIOC4B_C, SEL_MTU2_CH4_1),
 
        PINMUX_IPSR_DATA(IP1_5_4, A18),
        PINMUX_IPSR_DATA(IP1_5_4, ST1_PWM),
-       PINMUX_IPSR_MODSEL_DATA(IP1_5_4, LCD_CL2_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_5_4, TIOC4C_C, SEL_MTU2_CH4_1),
+       PINMUX_IPSR_MSEL(IP1_5_4, LCD_CL2_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP1_5_4, TIOC4C_C, SEL_MTU2_CH4_1),
 
        PINMUX_IPSR_DATA(IP1_7_6, A19),
        PINMUX_IPSR_DATA(IP1_7_6, ST1_CLKIN),
-       PINMUX_IPSR_MODSEL_DATA(IP1_7_6, LCD_CLK_A, SEL_LCDC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_7_6, TIOC4D_C, SEL_MTU2_CH4_1),
+       PINMUX_IPSR_MSEL(IP1_7_6, LCD_CLK_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP1_7_6, TIOC4D_C, SEL_MTU2_CH4_1),
 
        PINMUX_IPSR_DATA(IP1_9_8, A20),
        PINMUX_IPSR_DATA(IP1_9_8, ST1_REQ),
-       PINMUX_IPSR_MODSEL_DATA(IP1_9_8, LCD_FLM_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP1_9_8, LCD_FLM_A, SEL_LCDC_0),
 
        PINMUX_IPSR_DATA(IP1_11_10, A21),
        PINMUX_IPSR_DATA(IP1_11_10, ST1_SYC),
-       PINMUX_IPSR_MODSEL_DATA(IP1_11_10, LCD_VCPWC_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP1_11_10, LCD_VCPWC_A, SEL_LCDC_0),
 
        PINMUX_IPSR_DATA(IP1_13_12, A22),
        PINMUX_IPSR_DATA(IP1_13_12, ST1_VLD),
-       PINMUX_IPSR_MODSEL_DATA(IP1_13_12, LCD_VEPWC_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP1_13_12, LCD_VEPWC_A, SEL_LCDC_0),
 
        PINMUX_IPSR_DATA(IP1_15_14, A23),
        PINMUX_IPSR_DATA(IP1_15_14, ST1_D0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_15_14, LCD_M_DISP_A, SEL_LCDC_0),
+       PINMUX_IPSR_MSEL(IP1_15_14, LCD_M_DISP_A, SEL_LCDC_0),
 
        PINMUX_IPSR_DATA(IP1_17_16, A24),
-       PINMUX_IPSR_MODSEL_DATA(IP1_17_16, RX2_D, SEL_SCIF2_3),
+       PINMUX_IPSR_MSEL(IP1_17_16, RX2_D, SEL_SCIF2_3),
        PINMUX_IPSR_DATA(IP1_17_16, ST1_D1),
 
        PINMUX_IPSR_DATA(IP1_19_18, A25),
-       PINMUX_IPSR_MODSEL_DATA(IP1_17_16, RX2_D, SEL_SCIF2_3),
+       PINMUX_IPSR_MSEL(IP1_17_16, RX2_D, SEL_SCIF2_3),
        PINMUX_IPSR_DATA(IP1_17_16, ST1_D2),
 
        PINMUX_IPSR_DATA(IP1_22_20, D0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_22_20, SD0_DAT0_A, SEL_SDHI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_22_20, MMC_D0_A, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP1_22_20, SD0_DAT0_A, SEL_SDHI0_0),
+       PINMUX_IPSR_MSEL(IP1_22_20, MMC_D0_A, SEL_MMC_0),
        PINMUX_IPSR_DATA(IP1_22_20, ST1_D3),
-       PINMUX_IPSR_MODSEL_DATA(IP1_22_20, FD0_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP1_22_20, FD0_A, SEL_FLCTL_0),
 
        PINMUX_IPSR_DATA(IP1_25_23, D1),
-       PINMUX_IPSR_MODSEL_DATA(IP1_25_23, SD0_DAT0_A, SEL_SDHI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_25_23, MMC_D1_A, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP1_25_23, SD0_DAT0_A, SEL_SDHI0_0),
+       PINMUX_IPSR_MSEL(IP1_25_23, MMC_D1_A, SEL_MMC_0),
        PINMUX_IPSR_DATA(IP1_25_23, ST1_D4),
-       PINMUX_IPSR_MODSEL_DATA(IP1_25_23, FD1_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP1_25_23, FD1_A, SEL_FLCTL_0),
 
        PINMUX_IPSR_DATA(IP1_28_26, D2),
-       PINMUX_IPSR_MODSEL_DATA(IP1_28_26, SD0_DAT0_A, SEL_SDHI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_28_26, MMC_D2_A, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP1_28_26, SD0_DAT0_A, SEL_SDHI0_0),
+       PINMUX_IPSR_MSEL(IP1_28_26, MMC_D2_A, SEL_MMC_0),
        PINMUX_IPSR_DATA(IP1_28_26, ST1_D5),
-       PINMUX_IPSR_MODSEL_DATA(IP1_28_26, FD2_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP1_28_26, FD2_A, SEL_FLCTL_0),
 
        PINMUX_IPSR_DATA(IP1_31_29, D3),
-       PINMUX_IPSR_MODSEL_DATA(IP1_31_29, SD0_DAT0_A, SEL_SDHI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP1_31_29, MMC_D3_A, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP1_31_29, SD0_DAT0_A, SEL_SDHI0_0),
+       PINMUX_IPSR_MSEL(IP1_31_29, MMC_D3_A, SEL_MMC_0),
        PINMUX_IPSR_DATA(IP1_31_29, ST1_D6),
-       PINMUX_IPSR_MODSEL_DATA(IP1_31_29, FD3_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP1_31_29, FD3_A, SEL_FLCTL_0),
 
        /* IPSR2 */
        PINMUX_IPSR_DATA(IP2_2_0, D4),
-       PINMUX_IPSR_MODSEL_DATA(IP2_2_0, SD0_CD_A, SEL_SDHI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_2_0, MMC_D4_A, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP2_2_0, SD0_CD_A, SEL_SDHI0_0),
+       PINMUX_IPSR_MSEL(IP2_2_0, MMC_D4_A, SEL_MMC_0),
        PINMUX_IPSR_DATA(IP2_2_0, ST1_D7),
-       PINMUX_IPSR_MODSEL_DATA(IP2_2_0, FD4_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP2_2_0, FD4_A, SEL_FLCTL_0),
 
        PINMUX_IPSR_DATA(IP2_4_3, D5),
-       PINMUX_IPSR_MODSEL_DATA(IP2_4_3, SD0_WP_A, SEL_SDHI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_4_3, MMC_D5_A, SEL_MMC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_4_3, FD5_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP2_4_3, SD0_WP_A, SEL_SDHI0_0),
+       PINMUX_IPSR_MSEL(IP2_4_3, MMC_D5_A, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP2_4_3, FD5_A, SEL_FLCTL_0),
 
        PINMUX_IPSR_DATA(IP2_7_5, D6),
-       PINMUX_IPSR_MODSEL_DATA(IP2_7_5, RSPI_RSPCK_A, SEL_RSPI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_7_5, MMC_D6_A, SEL_MMC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_7_5, QSPCLK_A, SEL_RQSPI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_7_5, FD6_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP2_7_5, RSPI_RSPCK_A, SEL_RSPI_0),
+       PINMUX_IPSR_MSEL(IP2_7_5, MMC_D6_A, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP2_7_5, QSPCLK_A, SEL_RQSPI_0),
+       PINMUX_IPSR_MSEL(IP2_7_5, FD6_A, SEL_FLCTL_0),
 
        PINMUX_IPSR_DATA(IP2_10_8, D7),
-       PINMUX_IPSR_MODSEL_DATA(IP2_10_8, RSPI_SSL_A, SEL_RSPI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_10_8, MMC_D7_A, SEL_MMC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_10_8, QSSL_A, SEL_RQSPI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_10_8, FD7_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP2_10_8, RSPI_SSL_A, SEL_RSPI_0),
+       PINMUX_IPSR_MSEL(IP2_10_8, MMC_D7_A, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP2_10_8, QSSL_A, SEL_RQSPI_0),
+       PINMUX_IPSR_MSEL(IP2_10_8, FD7_A, SEL_FLCTL_0),
 
        PINMUX_IPSR_DATA(IP2_13_11, D8),
-       PINMUX_IPSR_MODSEL_DATA(IP2_13_11, SD0_CLK_A, SEL_SDHI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_13_11, MMC_CLK_A, SEL_MMC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_13_11, QIO2_A, SEL_RQSPI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_13_11, FCE_A, SEL_FLCTL_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_13_11, ET0_GTX_CLK_B, SEL_ET0_1),
+       PINMUX_IPSR_MSEL(IP2_13_11, SD0_CLK_A, SEL_SDHI0_0),
+       PINMUX_IPSR_MSEL(IP2_13_11, MMC_CLK_A, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP2_13_11, QIO2_A, SEL_RQSPI_0),
+       PINMUX_IPSR_MSEL(IP2_13_11, FCE_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP2_13_11, ET0_GTX_CLK_B, SEL_ET0_1),
 
        PINMUX_IPSR_DATA(IP2_16_14, D9),
-       PINMUX_IPSR_MODSEL_DATA(IP2_16_14, SD0_CMD_A, SEL_SDHI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_16_14, MMC_CMD_A, SEL_MMC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_16_14, QIO3_A, SEL_RQSPI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_16_14, FCLE_A, SEL_FLCTL_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_16_14, ET0_ETXD1_B, SEL_ET0_1),
+       PINMUX_IPSR_MSEL(IP2_16_14, SD0_CMD_A, SEL_SDHI0_0),
+       PINMUX_IPSR_MSEL(IP2_16_14, MMC_CMD_A, SEL_MMC_0),
+       PINMUX_IPSR_MSEL(IP2_16_14, QIO3_A, SEL_RQSPI_0),
+       PINMUX_IPSR_MSEL(IP2_16_14, FCLE_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP2_16_14, ET0_ETXD1_B, SEL_ET0_1),
 
        PINMUX_IPSR_DATA(IP2_19_17, D10),
-       PINMUX_IPSR_MODSEL_DATA(IP2_19_17, RSPI_MOSI_A, SEL_RSPI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_19_17, QMO_QIO0_A, SEL_RQSPI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_19_17, FALE_A, SEL_FLCTL_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_19_17, ET0_ETXD2_B, SEL_ET0_1),
+       PINMUX_IPSR_MSEL(IP2_19_17, RSPI_MOSI_A, SEL_RSPI_0),
+       PINMUX_IPSR_MSEL(IP2_19_17, QMO_QIO0_A, SEL_RQSPI_0),
+       PINMUX_IPSR_MSEL(IP2_19_17, FALE_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP2_19_17, ET0_ETXD2_B, SEL_ET0_1),
 
        PINMUX_IPSR_DATA(IP2_22_20, D11),
-       PINMUX_IPSR_MODSEL_DATA(IP2_22_20, RSPI_MISO_A, SEL_RSPI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_22_20, QMI_QIO1_A, SEL_RQSPI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_22_20, FRE_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP2_22_20, RSPI_MISO_A, SEL_RSPI_0),
+       PINMUX_IPSR_MSEL(IP2_22_20, QMI_QIO1_A, SEL_RQSPI_0),
+       PINMUX_IPSR_MSEL(IP2_22_20, FRE_A, SEL_FLCTL_0),
 
        PINMUX_IPSR_DATA(IP2_24_23, D12),
-       PINMUX_IPSR_MODSEL_DATA(IP2_24_23, FWE_A, SEL_FLCTL_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_24_23, ET0_ETXD5_B, SEL_ET0_1),
+       PINMUX_IPSR_MSEL(IP2_24_23, FWE_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP2_24_23, ET0_ETXD5_B, SEL_ET0_1),
 
        PINMUX_IPSR_DATA(IP2_27_25, D13),
-       PINMUX_IPSR_MODSEL_DATA(IP2_27_25, RX2_B, SEL_SCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_27_25, FRB_A, SEL_FLCTL_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_27_25, ET0_ETXD6_B, SEL_ET0_1),
+       PINMUX_IPSR_MSEL(IP2_27_25, RX2_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP2_27_25, FRB_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP2_27_25, ET0_ETXD6_B, SEL_ET0_1),
 
        PINMUX_IPSR_DATA(IP2_30_28, D14),
-       PINMUX_IPSR_MODSEL_DATA(IP2_30_28, TX2_B, SEL_SCIF2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP2_30_28, FSE_A, SEL_FLCTL_0),
-       PINMUX_IPSR_MODSEL_DATA(IP2_30_28, ET0_TX_CLK_B, SEL_ET0_1),
+       PINMUX_IPSR_MSEL(IP2_30_28, TX2_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP2_30_28, FSE_A, SEL_FLCTL_0),
+       PINMUX_IPSR_MSEL(IP2_30_28, ET0_TX_CLK_B, SEL_ET0_1),
 
        /* IPSR3 */
        PINMUX_IPSR_DATA(IP3_1_0, D15),
-       PINMUX_IPSR_MODSEL_DATA(IP3_1_0, SCK2_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP3_1_0, SCK2_B, SEL_SCIF2_1),
 
        PINMUX_IPSR_DATA(IP3_2, CS1_A26),
-       PINMUX_IPSR_MODSEL_DATA(IP3_2, QIO3_B, SEL_RQSPI_1),
+       PINMUX_IPSR_MSEL(IP3_2, QIO3_B, SEL_RQSPI_1),
 
        PINMUX_IPSR_DATA(IP3_5_3, EX_CS1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_5_3, RX3_B, SEL_SCIF2_1),
+       PINMUX_IPSR_MSEL(IP3_5_3, RX3_B, SEL_SCIF2_1),
        PINMUX_IPSR_DATA(IP3_5_3, ATACS0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_5_3, QIO2_B, SEL_RQSPI_1),
+       PINMUX_IPSR_MSEL(IP3_5_3, QIO2_B, SEL_RQSPI_1),
        PINMUX_IPSR_DATA(IP3_5_3, ET0_ETXD0),
 
        PINMUX_IPSR_DATA(IP3_8_6, EX_CS2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_8_6, TX3_B, SEL_SCIF3_1),
+       PINMUX_IPSR_MSEL(IP3_8_6, TX3_B, SEL_SCIF3_1),
        PINMUX_IPSR_DATA(IP3_8_6, ATACS1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_8_6, QSPCLK_B, SEL_RQSPI_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_8_6, ET0_GTX_CLK_A, SEL_ET0_0),
+       PINMUX_IPSR_MSEL(IP3_8_6, QSPCLK_B, SEL_RQSPI_1),
+       PINMUX_IPSR_MSEL(IP3_8_6, ET0_GTX_CLK_A, SEL_ET0_0),
 
        PINMUX_IPSR_DATA(IP3_11_9, EX_CS3),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_9, SD1_CD_A, SEL_SDHI1_0),
+       PINMUX_IPSR_MSEL(IP3_11_9, SD1_CD_A, SEL_SDHI1_0),
        PINMUX_IPSR_DATA(IP3_11_9, ATARD),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_9, QMO_QIO0_B, SEL_RQSPI_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_11_9, ET0_ETXD1_A, SEL_ET0_0),
+       PINMUX_IPSR_MSEL(IP3_11_9, QMO_QIO0_B, SEL_RQSPI_1),
+       PINMUX_IPSR_MSEL(IP3_11_9, ET0_ETXD1_A, SEL_ET0_0),
 
        PINMUX_IPSR_DATA(IP3_14_12, EX_CS4),
-       PINMUX_IPSR_MODSEL_DATA(IP3_14_12, SD1_WP_A, SEL_SDHI1_0),
+       PINMUX_IPSR_MSEL(IP3_14_12, SD1_WP_A, SEL_SDHI1_0),
        PINMUX_IPSR_DATA(IP3_14_12, ATAWR),
-       PINMUX_IPSR_MODSEL_DATA(IP3_14_12, QMI_QIO1_B, SEL_RQSPI_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_14_12, ET0_ETXD2_A, SEL_ET0_0),
+       PINMUX_IPSR_MSEL(IP3_14_12, QMI_QIO1_B, SEL_RQSPI_1),
+       PINMUX_IPSR_MSEL(IP3_14_12, ET0_ETXD2_A, SEL_ET0_0),
 
        PINMUX_IPSR_DATA(IP3_17_15, EX_CS5),
-       PINMUX_IPSR_MODSEL_DATA(IP3_17_15, SD1_CMD_A, SEL_SDHI1_0),
+       PINMUX_IPSR_MSEL(IP3_17_15, SD1_CMD_A, SEL_SDHI1_0),
        PINMUX_IPSR_DATA(IP3_17_15, ATADIR),
-       PINMUX_IPSR_MODSEL_DATA(IP3_17_15, QSSL_B, SEL_RQSPI_1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_17_15, ET0_ETXD3_A, SEL_ET0_0),
+       PINMUX_IPSR_MSEL(IP3_17_15, QSSL_B, SEL_RQSPI_1),
+       PINMUX_IPSR_MSEL(IP3_17_15, ET0_ETXD3_A, SEL_ET0_0),
 
        PINMUX_IPSR_DATA(IP3_19_18, RD_WR),
        PINMUX_IPSR_DATA(IP3_19_18, TCLK0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_19_18, CAN_CLK_B, SEL_RCAN_CLK_1),
+       PINMUX_IPSR_MSEL(IP3_19_18, CAN_CLK_B, SEL_RCAN_CLK_1),
        PINMUX_IPSR_DATA(IP3_19_18, ET0_ETXD4),
 
        PINMUX_IPSR_DATA(IP3_20, EX_WAIT0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_20, TCLK1_B, SEL_TMU_1),
+       PINMUX_IPSR_MSEL(IP3_20, TCLK1_B, SEL_TMU_1),
 
        PINMUX_IPSR_DATA(IP3_23_21, EX_WAIT1),
-       PINMUX_IPSR_MODSEL_DATA(IP3_23_21, SD1_DAT0_A, SEL_SDHI1_0),
+       PINMUX_IPSR_MSEL(IP3_23_21, SD1_DAT0_A, SEL_SDHI1_0),
        PINMUX_IPSR_DATA(IP3_23_21, DREQ2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_23_21, CAN1_TX_C, SEL_RCAN1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_23_21, ET0_LINK_C, SEL_ET0_CTL_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_23_21, ET0_ETXD5_A, SEL_ET0_0),
+       PINMUX_IPSR_MSEL(IP3_23_21, CAN1_TX_C, SEL_RCAN1_2),
+       PINMUX_IPSR_MSEL(IP3_23_21, ET0_LINK_C, SEL_ET0_CTL_2),
+       PINMUX_IPSR_MSEL(IP3_23_21, ET0_ETXD5_A, SEL_ET0_0),
 
        PINMUX_IPSR_DATA(IP3_26_24, EX_WAIT2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, SD1_DAT1_A, SEL_SDHI1_0),
+       PINMUX_IPSR_MSEL(IP3_26_24, SD1_DAT1_A, SEL_SDHI1_0),
        PINMUX_IPSR_DATA(IP3_26_24, DACK2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, CAN1_RX_C, SEL_RCAN1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, ET0_MAGIC_C, SEL_ET0_CTL_2),
-       PINMUX_IPSR_MODSEL_DATA(IP3_26_24, ET0_ETXD6_A, SEL_ET0_0),
+       PINMUX_IPSR_MSEL(IP3_26_24, CAN1_RX_C, SEL_RCAN1_2),
+       PINMUX_IPSR_MSEL(IP3_26_24, ET0_MAGIC_C, SEL_ET0_CTL_2),
+       PINMUX_IPSR_MSEL(IP3_26_24, ET0_ETXD6_A, SEL_ET0_0),
 
        PINMUX_IPSR_DATA(IP3_29_27, DRACK0),
-       PINMUX_IPSR_MODSEL_DATA(IP3_29_27, SD1_DAT2_A, SEL_SDHI1_0),
+       PINMUX_IPSR_MSEL(IP3_29_27, SD1_DAT2_A, SEL_SDHI1_0),
        PINMUX_IPSR_DATA(IP3_29_27, ATAG),
-       PINMUX_IPSR_MODSEL_DATA(IP3_29_27, TCLK1_A, SEL_TMU_0),
+       PINMUX_IPSR_MSEL(IP3_29_27, TCLK1_A, SEL_TMU_0),
        PINMUX_IPSR_DATA(IP3_29_27, ET0_ETXD7),
 
        /* IPSR4 */
-       PINMUX_IPSR_MODSEL_DATA(IP4_2_0, HCTS0_A, SEL_HSCIF_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_2_0, CTS1_A, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP4_2_0, HCTS0_A, SEL_HSCIF_0),
+       PINMUX_IPSR_MSEL(IP4_2_0, CTS1_A, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP4_2_0, VI0_FIELD),
-       PINMUX_IPSR_MODSEL_DATA(IP4_2_0, RMII0_RXD1_A, SEL_RMII_0),
+       PINMUX_IPSR_MSEL(IP4_2_0, RMII0_RXD1_A, SEL_RMII_0),
        PINMUX_IPSR_DATA(IP4_2_0, ET0_ERXD7),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_5_3, HRTS0_A, SEL_HSCIF_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_5_3, RTS1_A, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP4_5_3, HRTS0_A, SEL_HSCIF_0),
+       PINMUX_IPSR_MSEL(IP4_5_3, RTS1_A, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP4_5_3, VI0_HSYNC),
-       PINMUX_IPSR_MODSEL_DATA(IP4_5_3, RMII0_TXD_EN_A, SEL_RMII_0),
+       PINMUX_IPSR_MSEL(IP4_5_3, RMII0_TXD_EN_A, SEL_RMII_0),
        PINMUX_IPSR_DATA(IP4_5_3, ET0_RX_DV),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_8_6, HSCK0_A, SEL_HSCIF_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_8_6, SCK1_A, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP4_8_6, HSCK0_A, SEL_HSCIF_0),
+       PINMUX_IPSR_MSEL(IP4_8_6, SCK1_A, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP4_8_6, VI0_VSYNC),
-       PINMUX_IPSR_MODSEL_DATA(IP4_8_6, RMII0_RX_ER_A, SEL_RMII_0),
+       PINMUX_IPSR_MSEL(IP4_8_6, RMII0_RX_ER_A, SEL_RMII_0),
        PINMUX_IPSR_DATA(IP4_8_6, ET0_RX_ER),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_11_9, HRX0_A, SEL_HSCIF_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_11_9, RX1_A, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP4_11_9, HRX0_A, SEL_HSCIF_0),
+       PINMUX_IPSR_MSEL(IP4_11_9, RX1_A, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP4_11_9, VI0_DATA0_VI0_B0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_11_9, RMII0_CRS_DV_A, SEL_RMII_0),
+       PINMUX_IPSR_MSEL(IP4_11_9, RMII0_CRS_DV_A, SEL_RMII_0),
        PINMUX_IPSR_DATA(IP4_11_9, ET0_CRS),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_14_12, HTX0_A, SEL_HSCIF_0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_14_12, TX1_A, SEL_SCIF1_0),
+       PINMUX_IPSR_MSEL(IP4_14_12, HTX0_A, SEL_HSCIF_0),
+       PINMUX_IPSR_MSEL(IP4_14_12, TX1_A, SEL_SCIF1_0),
        PINMUX_IPSR_DATA(IP4_14_12, VI0_DATA1_VI0_B1),
-       PINMUX_IPSR_MODSEL_DATA(IP4_14_12, RMII0_MDC_A, SEL_RMII_0),
+       PINMUX_IPSR_MSEL(IP4_14_12, RMII0_MDC_A, SEL_RMII_0),
        PINMUX_IPSR_DATA(IP4_14_12, ET0_COL),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_17_15, CTS0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP4_17_15, CTS0_B, SEL_SCIF0_1),
        PINMUX_IPSR_DATA(IP4_17_15, VI0_DATA2_VI0_B2),
-       PINMUX_IPSR_MODSEL_DATA(IP4_17_15, RMII0_MDIO_A, SEL_RMII_0),
+       PINMUX_IPSR_MSEL(IP4_17_15, RMII0_MDIO_A, SEL_RMII_0),
        PINMUX_IPSR_DATA(IP4_17_15, ET0_MDC),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_19_18, RTS0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP4_19_18, RTS0_B, SEL_SCIF0_1),
        PINMUX_IPSR_DATA(IP4_19_18, VI0_DATA3_VI0_B3),
-       PINMUX_IPSR_MODSEL_DATA(IP4_19_18, ET0_MDIO_A, SEL_ET0_0),
+       PINMUX_IPSR_MSEL(IP4_19_18, ET0_MDIO_A, SEL_ET0_0),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_21_20, SCK1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP4_21_20, SCK1_B, SEL_SCIF1_1),
        PINMUX_IPSR_DATA(IP4_21_20, VI0_DATA4_VI0_B4),
-       PINMUX_IPSR_MODSEL_DATA(IP4_21_20, ET0_LINK_A, SEL_ET0_CTL_0),
+       PINMUX_IPSR_MSEL(IP4_21_20, ET0_LINK_A, SEL_ET0_CTL_0),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_23_22, RX1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP4_23_22, RX1_B, SEL_SCIF1_1),
        PINMUX_IPSR_DATA(IP4_23_22, VI0_DATA5_VI0_B5),
-       PINMUX_IPSR_MODSEL_DATA(IP4_23_22, ET0_MAGIC_A, SEL_ET0_CTL_0),
+       PINMUX_IPSR_MSEL(IP4_23_22, ET0_MAGIC_A, SEL_ET0_CTL_0),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_25_24, TX1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP4_25_24, TX1_B, SEL_SCIF1_1),
        PINMUX_IPSR_DATA(IP4_25_24, VI0_DATA6_VI0_G0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_25_24, ET0_PHY_INT_A, SEL_ET0_CTL_0),
+       PINMUX_IPSR_MSEL(IP4_25_24, ET0_PHY_INT_A, SEL_ET0_CTL_0),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_27_26, CTS1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP4_27_26, CTS1_B, SEL_SCIF1_1),
        PINMUX_IPSR_DATA(IP4_27_26, VI0_DATA7_VI0_G1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_29_28, RTS1_B, SEL_SCIF1_1),
+       PINMUX_IPSR_MSEL(IP4_29_28, RTS1_B, SEL_SCIF1_1),
        PINMUX_IPSR_DATA(IP4_29_28, VI0_G2),
 
-       PINMUX_IPSR_MODSEL_DATA(IP4_31_30, SCK2_A, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP4_31_30, SCK2_A, SEL_SCIF2_0),
        PINMUX_IPSR_DATA(IP4_31_30, VI0_G3),
 
        /* IPSR5 */
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, SD2_CLK_A, SEL_SDHI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, RX2_A, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP5_2_0, SD2_CLK_A, SEL_SDHI2_0),
+       PINMUX_IPSR_MSEL(IP5_2_0, RX2_A, SEL_SCIF2_0),
        PINMUX_IPSR_DATA(IP5_2_0, VI0_G4),
-       PINMUX_IPSR_MODSEL_DATA(IP5_2_0, ET0_RX_CLK_B, SEL_ET0_1),
+       PINMUX_IPSR_MSEL(IP5_2_0, ET0_RX_CLK_B, SEL_ET0_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, SD2_CMD_A, SEL_SDHI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, TX2_A, SEL_SCIF2_0),
+       PINMUX_IPSR_MSEL(IP5_5_3, SD2_CMD_A, SEL_SDHI2_0),
+       PINMUX_IPSR_MSEL(IP5_5_3, TX2_A, SEL_SCIF2_0),
        PINMUX_IPSR_DATA(IP5_5_3, VI0_G5),
-       PINMUX_IPSR_MODSEL_DATA(IP5_5_3, ET0_ERXD2_B, SEL_ET0_1),
+       PINMUX_IPSR_MSEL(IP5_5_3, ET0_ERXD2_B, SEL_ET0_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP5_8_6, SD2_DAT0_A, SEL_SDHI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_8_6, RX3_A, SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP5_8_6, SD2_DAT0_A, SEL_SDHI2_0),
+       PINMUX_IPSR_MSEL(IP5_8_6, RX3_A, SEL_SCIF3_0),
        PINMUX_IPSR_DATA(IP4_8_6, VI0_R0),
-       PINMUX_IPSR_MODSEL_DATA(IP4_8_6, ET0_ERXD2_B, SEL_ET0_1),
+       PINMUX_IPSR_MSEL(IP4_8_6, ET0_ERXD2_B, SEL_ET0_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP5_11_9, SD2_DAT1_A, SEL_SDHI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_11_9, TX3_A, SEL_SCIF3_0),
+       PINMUX_IPSR_MSEL(IP5_11_9, SD2_DAT1_A, SEL_SDHI2_0),
+       PINMUX_IPSR_MSEL(IP5_11_9, TX3_A, SEL_SCIF3_0),
        PINMUX_IPSR_DATA(IP5_11_9, VI0_R1),
-       PINMUX_IPSR_MODSEL_DATA(IP5_11_9, ET0_MDIO_B, SEL_ET0_1),
+       PINMUX_IPSR_MSEL(IP5_11_9, ET0_MDIO_B, SEL_ET0_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP5_14_12, SD2_DAT2_A, SEL_SDHI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_14_12, RX4_A, SEL_SCIF4_0),
+       PINMUX_IPSR_MSEL(IP5_14_12, SD2_DAT2_A, SEL_SDHI2_0),
+       PINMUX_IPSR_MSEL(IP5_14_12, RX4_A, SEL_SCIF4_0),
        PINMUX_IPSR_DATA(IP5_14_12, VI0_R2),
-       PINMUX_IPSR_MODSEL_DATA(IP5_14_12, ET0_LINK_B, SEL_ET0_CTL_1),
+       PINMUX_IPSR_MSEL(IP5_14_12, ET0_LINK_B, SEL_ET0_CTL_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP5_17_15, SD2_DAT3_A, SEL_SDHI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_17_15, TX4_A, SEL_SCIF4_0),
+       PINMUX_IPSR_MSEL(IP5_17_15, SD2_DAT3_A, SEL_SDHI2_0),
+       PINMUX_IPSR_MSEL(IP5_17_15, TX4_A, SEL_SCIF4_0),
        PINMUX_IPSR_DATA(IP5_17_15, VI0_R3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_17_15, ET0_MAGIC_B, SEL_ET0_CTL_1),
+       PINMUX_IPSR_MSEL(IP5_17_15, ET0_MAGIC_B, SEL_ET0_CTL_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP5_20_18, SD2_CD_A, SEL_SDHI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_20_18, RX5_A, SEL_SCIF5_0),
+       PINMUX_IPSR_MSEL(IP5_20_18, SD2_CD_A, SEL_SDHI2_0),
+       PINMUX_IPSR_MSEL(IP5_20_18, RX5_A, SEL_SCIF5_0),
        PINMUX_IPSR_DATA(IP5_20_18, VI0_R4),
-       PINMUX_IPSR_MODSEL_DATA(IP5_20_18, ET0_PHY_INT_B, SEL_ET0_CTL_1),
+       PINMUX_IPSR_MSEL(IP5_20_18, ET0_PHY_INT_B, SEL_ET0_CTL_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP5_22_21, SD2_WP_A, SEL_SDHI2_0),
-       PINMUX_IPSR_MODSEL_DATA(IP5_22_21, TX5_A, SEL_SCIF5_0),
+       PINMUX_IPSR_MSEL(IP5_22_21, SD2_WP_A, SEL_SDHI2_0),
+       PINMUX_IPSR_MSEL(IP5_22_21, TX5_A, SEL_SCIF5_0),
        PINMUX_IPSR_DATA(IP5_22_21, VI0_R5),
 
        PINMUX_IPSR_DATA(IP5_24_23, REF125CK),
        PINMUX_IPSR_DATA(IP5_24_23, ADTRG),
-       PINMUX_IPSR_MODSEL_DATA(IP5_24_23, RX5_C, SEL_SCIF5_2),
+       PINMUX_IPSR_MSEL(IP5_24_23, RX5_C, SEL_SCIF5_2),
        PINMUX_IPSR_DATA(IP5_26_25, REF50CK),
-       PINMUX_IPSR_MODSEL_DATA(IP5_26_25, CTS1_E, SEL_SCIF1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP5_26_25, HCTS0_D, SEL_HSCIF_3),
+       PINMUX_IPSR_MSEL(IP5_26_25, CTS1_E, SEL_SCIF1_3),
+       PINMUX_IPSR_MSEL(IP5_26_25, HCTS0_D, SEL_HSCIF_3),
 
        /* IPSR6 */
        PINMUX_IPSR_DATA(IP6_2_0, DU0_DR0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, SCIF_CLK_B, SEL_SCIF_CLK_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, HRX0_D, SEL_HSCIF_3),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, IETX_A, SEL_IEBUS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_2_0, TCLKA_A, SEL_MTU2_CLK_0),
+       PINMUX_IPSR_MSEL(IP6_2_0, SCIF_CLK_B, SEL_SCIF_CLK_1),
+       PINMUX_IPSR_MSEL(IP6_2_0, HRX0_D, SEL_HSCIF_3),
+       PINMUX_IPSR_MSEL(IP6_2_0, IETX_A, SEL_IEBUS_0),
+       PINMUX_IPSR_MSEL(IP6_2_0, TCLKA_A, SEL_MTU2_CLK_0),
        PINMUX_IPSR_DATA(IP6_2_0, HIFD00),
 
        PINMUX_IPSR_DATA(IP6_5_3, DU0_DR1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, SCK0_B, SEL_SCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, HTX0_D, SEL_HSCIF_3),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, IERX_A, SEL_IEBUS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_5_3, TCLKB_A, SEL_MTU2_CLK_0),
+       PINMUX_IPSR_MSEL(IP6_5_3, SCK0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP6_5_3, HTX0_D, SEL_HSCIF_3),
+       PINMUX_IPSR_MSEL(IP6_5_3, IERX_A, SEL_IEBUS_0),
+       PINMUX_IPSR_MSEL(IP6_5_3, TCLKB_A, SEL_MTU2_CLK_0),
        PINMUX_IPSR_DATA(IP6_5_3, HIFD01),
 
        PINMUX_IPSR_DATA(IP6_7_6, DU0_DR2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_7_6, RX0_B, SEL_SCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_7_6, TCLKC_A, SEL_MTU2_CLK_0),
+       PINMUX_IPSR_MSEL(IP6_7_6, RX0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP6_7_6, TCLKC_A, SEL_MTU2_CLK_0),
        PINMUX_IPSR_DATA(IP6_7_6, HIFD02),
 
        PINMUX_IPSR_DATA(IP6_9_8, DU0_DR3),
-       PINMUX_IPSR_MODSEL_DATA(IP6_9_8, TX0_B, SEL_SCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_9_8, TCLKD_A, SEL_MTU2_CLK_0),
+       PINMUX_IPSR_MSEL(IP6_9_8, TX0_B, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP6_9_8, TCLKD_A, SEL_MTU2_CLK_0),
        PINMUX_IPSR_DATA(IP6_9_8, HIFD03),
 
        PINMUX_IPSR_DATA(IP6_11_10, DU0_DR4),
-       PINMUX_IPSR_MODSEL_DATA(IP6_11_10, CTS0_C, SEL_SCIF0_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_11_10, TIOC0A_A, SEL_MTU2_CH0_0),
+       PINMUX_IPSR_MSEL(IP6_11_10, CTS0_C, SEL_SCIF0_2),
+       PINMUX_IPSR_MSEL(IP6_11_10, TIOC0A_A, SEL_MTU2_CH0_0),
        PINMUX_IPSR_DATA(IP6_11_10, HIFD04),
 
        PINMUX_IPSR_DATA(IP6_13_12, DU0_DR5),
-       PINMUX_IPSR_MODSEL_DATA(IP6_13_12, RTS0_C, SEL_SCIF0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_13_12, TIOC0B_A, SEL_MTU2_CH0_0),
+       PINMUX_IPSR_MSEL(IP6_13_12, RTS0_C, SEL_SCIF0_1),
+       PINMUX_IPSR_MSEL(IP6_13_12, TIOC0B_A, SEL_MTU2_CH0_0),
        PINMUX_IPSR_DATA(IP6_13_12, HIFD05),
 
        PINMUX_IPSR_DATA(IP6_15_14, DU0_DR6),
-       PINMUX_IPSR_MODSEL_DATA(IP6_15_14, SCK1_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_15_14, TIOC0C_A, SEL_MTU2_CH0_0),
+       PINMUX_IPSR_MSEL(IP6_15_14, SCK1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP6_15_14, TIOC0C_A, SEL_MTU2_CH0_0),
        PINMUX_IPSR_DATA(IP6_15_14, HIFD06),
 
        PINMUX_IPSR_DATA(IP6_17_16, DU0_DR7),
-       PINMUX_IPSR_MODSEL_DATA(IP6_17_16, RX1_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_17_16, TIOC0D_A, SEL_MTU2_CH0_0),
+       PINMUX_IPSR_MSEL(IP6_17_16, RX1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP6_17_16, TIOC0D_A, SEL_MTU2_CH0_0),
        PINMUX_IPSR_DATA(IP6_17_16, HIFD07),
 
        PINMUX_IPSR_DATA(IP6_20_18, DU0_DG0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_20_18, TX1_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_20_18, HSCK0_D, SEL_HSCIF_3),
-       PINMUX_IPSR_MODSEL_DATA(IP6_20_18, IECLK_A, SEL_IEBUS_0),
-       PINMUX_IPSR_MODSEL_DATA(IP6_20_18, TIOC1A_A, SEL_MTU2_CH1_0),
+       PINMUX_IPSR_MSEL(IP6_20_18, TX1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP6_20_18, HSCK0_D, SEL_HSCIF_3),
+       PINMUX_IPSR_MSEL(IP6_20_18, IECLK_A, SEL_IEBUS_0),
+       PINMUX_IPSR_MSEL(IP6_20_18, TIOC1A_A, SEL_MTU2_CH1_0),
        PINMUX_IPSR_DATA(IP6_20_18, HIFD08),
 
        PINMUX_IPSR_DATA(IP6_23_21, DU0_DG1),
-       PINMUX_IPSR_MODSEL_DATA(IP6_23_21, CTS1_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP6_23_21, HRTS0_D, SEL_HSCIF_3),
-       PINMUX_IPSR_MODSEL_DATA(IP6_23_21, TIOC1B_A, SEL_MTU2_CH1_0),
+       PINMUX_IPSR_MSEL(IP6_23_21, CTS1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP6_23_21, HRTS0_D, SEL_HSCIF_3),
+       PINMUX_IPSR_MSEL(IP6_23_21, TIOC1B_A, SEL_MTU2_CH1_0),
        PINMUX_IPSR_DATA(IP6_23_21, HIFD09),
 
        /* IPSR7 */
        PINMUX_IPSR_DATA(IP7_2_0, DU0_DG2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, RTS1_C, SEL_SCIF1_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, RMII0_MDC_B, SEL_RMII_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_2_0, TIOC2A_A, SEL_MTU2_CH2_0),
+       PINMUX_IPSR_MSEL(IP7_2_0, RTS1_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP7_2_0, RMII0_MDC_B, SEL_RMII_1),
+       PINMUX_IPSR_MSEL(IP7_2_0, TIOC2A_A, SEL_MTU2_CH2_0),
        PINMUX_IPSR_DATA(IP7_2_0, HIFD10),
 
        PINMUX_IPSR_DATA(IP7_5_3, DU0_DG3),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, SCK2_C, SEL_SCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, RMII0_MDIO_B, SEL_RMII_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_5_3, TIOC2B_A, SEL_MTU2_CH2_0),
+       PINMUX_IPSR_MSEL(IP7_5_3, SCK2_C, SEL_SCIF2_2),
+       PINMUX_IPSR_MSEL(IP7_5_3, RMII0_MDIO_B, SEL_RMII_1),
+       PINMUX_IPSR_MSEL(IP7_5_3, TIOC2B_A, SEL_MTU2_CH2_0),
        PINMUX_IPSR_DATA(IP7_5_3, HIFD11),
 
        PINMUX_IPSR_DATA(IP7_8_6, DU0_DG4),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, RX2_C, SEL_SCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, RMII0_CRS_DV_B, SEL_RMII_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_8_6, TIOC3A_A, SEL_MTU2_CH3_0),
+       PINMUX_IPSR_MSEL(IP7_8_6, RX2_C, SEL_SCIF2_2),
+       PINMUX_IPSR_MSEL(IP7_8_6, RMII0_CRS_DV_B, SEL_RMII_1),
+       PINMUX_IPSR_MSEL(IP7_8_6, TIOC3A_A, SEL_MTU2_CH3_0),
        PINMUX_IPSR_DATA(IP7_8_6, HIFD12),
 
        PINMUX_IPSR_DATA(IP7_11_9, DU0_DG5),
-       PINMUX_IPSR_MODSEL_DATA(IP7_11_9, TX2_C, SEL_SCIF2_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_11_9, RMII0_RX_ER_B, SEL_RMII_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_11_9, TIOC3B_A, SEL_MTU2_CH3_0),
+       PINMUX_IPSR_MSEL(IP7_11_9, TX2_C, SEL_SCIF2_2),
+       PINMUX_IPSR_MSEL(IP7_11_9, RMII0_RX_ER_B, SEL_RMII_1),
+       PINMUX_IPSR_MSEL(IP7_11_9, TIOC3B_A, SEL_MTU2_CH3_0),
        PINMUX_IPSR_DATA(IP7_11_9, HIFD13),
 
        PINMUX_IPSR_DATA(IP7_14_12, DU0_DG6),
-       PINMUX_IPSR_MODSEL_DATA(IP7_14_12, RX3_C, SEL_SCIF3_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_14_12, RMII0_RXD0_B, SEL_RMII_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_14_12, TIOC3C_A, SEL_MTU2_CH3_0),
+       PINMUX_IPSR_MSEL(IP7_14_12, RX3_C, SEL_SCIF3_2),
+       PINMUX_IPSR_MSEL(IP7_14_12, RMII0_RXD0_B, SEL_RMII_1),
+       PINMUX_IPSR_MSEL(IP7_14_12, TIOC3C_A, SEL_MTU2_CH3_0),
        PINMUX_IPSR_DATA(IP7_14_12, HIFD14),
 
        PINMUX_IPSR_DATA(IP7_17_15, DU0_DG7),
-       PINMUX_IPSR_MODSEL_DATA(IP7_17_15, TX3_C, SEL_SCIF3_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_17_15, RMII0_RXD1_B, SEL_RMII_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_17_15, TIOC3D_A, SEL_MTU2_CH3_0),
+       PINMUX_IPSR_MSEL(IP7_17_15, TX3_C, SEL_SCIF3_2),
+       PINMUX_IPSR_MSEL(IP7_17_15, RMII0_RXD1_B, SEL_RMII_1),
+       PINMUX_IPSR_MSEL(IP7_17_15, TIOC3D_A, SEL_MTU2_CH3_0),
        PINMUX_IPSR_DATA(IP7_17_15, HIFD15),
 
        PINMUX_IPSR_DATA(IP7_20_18, DU0_DB0),
-       PINMUX_IPSR_MODSEL_DATA(IP7_20_18, RX4_C, SEL_SCIF4_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_20_18, RMII0_TXD_EN_B, SEL_RMII_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_20_18, TIOC4A_A, SEL_MTU2_CH4_0),
+       PINMUX_IPSR_MSEL(IP7_20_18, RX4_C, SEL_SCIF4_2),
+       PINMUX_IPSR_MSEL(IP7_20_18, RMII0_TXD_EN_B, SEL_RMII_1),
+       PINMUX_IPSR_MSEL(IP7_20_18, TIOC4A_A, SEL_MTU2_CH4_0),
        PINMUX_IPSR_DATA(IP7_20_18, HIFCS),
 
        PINMUX_IPSR_DATA(IP7_23_21, DU0_DB1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_23_21, TX4_C, SEL_SCIF4_2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_23_21, RMII0_TXD0_B, SEL_RMII_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_23_21, TIOC4B_A, SEL_MTU2_CH4_0),
+       PINMUX_IPSR_MSEL(IP7_23_21, TX4_C, SEL_SCIF4_2),
+       PINMUX_IPSR_MSEL(IP7_23_21, RMII0_TXD0_B, SEL_RMII_1),
+       PINMUX_IPSR_MSEL(IP7_23_21, TIOC4B_A, SEL_MTU2_CH4_0),
        PINMUX_IPSR_DATA(IP7_23_21, HIFWR),
 
        PINMUX_IPSR_DATA(IP7_26_24, DU0_DB2),
-       PINMUX_IPSR_MODSEL_DATA(IP7_26_24, RX5_B, SEL_SCIF5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_26_24, RMII0_TXD1_B, SEL_RMII_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_26_24, TIOC4C_A, SEL_MTU2_CH4_0),
+       PINMUX_IPSR_MSEL(IP7_26_24, RX5_B, SEL_SCIF5_1),
+       PINMUX_IPSR_MSEL(IP7_26_24, RMII0_TXD1_B, SEL_RMII_1),
+       PINMUX_IPSR_MSEL(IP7_26_24, TIOC4C_A, SEL_MTU2_CH4_0),
 
        PINMUX_IPSR_DATA(IP7_28_27, DU0_DB3),
-       PINMUX_IPSR_MODSEL_DATA(IP7_28_27, TX5_B, SEL_SCIF5_1),
-       PINMUX_IPSR_MODSEL_DATA(IP7_28_27, TIOC4D_A, SEL_MTU2_CH4_0),
+       PINMUX_IPSR_MSEL(IP7_28_27, TX5_B, SEL_SCIF5_1),
+       PINMUX_IPSR_MSEL(IP7_28_27, TIOC4D_A, SEL_MTU2_CH4_0),
        PINMUX_IPSR_DATA(IP7_28_27, HIFRD),
 
        PINMUX_IPSR_DATA(IP7_30_29, DU0_DB4),
@@ -1107,251 +1107,251 @@ static const u16 pinmux_data[] = {
        PINMUX_IPSR_DATA(IP8_3_2, HIFRDY),
 
        PINMUX_IPSR_DATA(IP8_5_4, DU0_DB7),
-       PINMUX_IPSR_MODSEL_DATA(IP8_5_4, SSI_SCK0_B, SEL_SSI0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_5_4, HIFEBL_B, SEL_HIF_1),
+       PINMUX_IPSR_MSEL(IP8_5_4, SSI_SCK0_B, SEL_SSI0_1),
+       PINMUX_IPSR_MSEL(IP8_5_4, HIFEBL_B, SEL_HIF_1),
 
        PINMUX_IPSR_DATA(IP8_7_6, DU0_DOTCLKIN),
-       PINMUX_IPSR_MODSEL_DATA(IP8_7_6, HSPI_CS0_C, SEL_HSPI_2),
-       PINMUX_IPSR_MODSEL_DATA(IP8_7_6, SSI_WS0_B, SEL_SSI0_1),
+       PINMUX_IPSR_MSEL(IP8_7_6, HSPI_CS0_C, SEL_HSPI_2),
+       PINMUX_IPSR_MSEL(IP8_7_6, SSI_WS0_B, SEL_SSI0_1),
 
        PINMUX_IPSR_DATA(IP8_9_8, DU0_DOTCLKOUT),
-       PINMUX_IPSR_MODSEL_DATA(IP8_9_8, HSPI_CLK0_C, SEL_HSPI_2),
-       PINMUX_IPSR_MODSEL_DATA(IP8_9_8, SSI_SDATA0_B, SEL_SSI0_1),
+       PINMUX_IPSR_MSEL(IP8_9_8, HSPI_CLK0_C, SEL_HSPI_2),
+       PINMUX_IPSR_MSEL(IP8_9_8, SSI_SDATA0_B, SEL_SSI0_1),
 
        PINMUX_IPSR_DATA(IP8_11_10, DU0_EXHSYNC_DU0_HSYNC),
-       PINMUX_IPSR_MODSEL_DATA(IP8_11_10, HSPI_TX0_C, SEL_HSPI_2),
-       PINMUX_IPSR_MODSEL_DATA(IP8_11_10, SSI_SCK1_B, SEL_SSI1_1),
+       PINMUX_IPSR_MSEL(IP8_11_10, HSPI_TX0_C, SEL_HSPI_2),
+       PINMUX_IPSR_MSEL(IP8_11_10, SSI_SCK1_B, SEL_SSI1_1),
 
        PINMUX_IPSR_DATA(IP8_13_12, DU0_EXVSYNC_DU0_VSYNC),
-       PINMUX_IPSR_MODSEL_DATA(IP8_13_12, HSPI_RX0_C, SEL_HSPI_2),
-       PINMUX_IPSR_MODSEL_DATA(IP8_13_12, SSI_WS1_B, SEL_SSI1_1),
+       PINMUX_IPSR_MSEL(IP8_13_12, HSPI_RX0_C, SEL_HSPI_2),
+       PINMUX_IPSR_MSEL(IP8_13_12, SSI_WS1_B, SEL_SSI1_1),
 
        PINMUX_IPSR_DATA(IP8_15_14, DU0_EXODDF_DU0_ODDF),
-       PINMUX_IPSR_MODSEL_DATA(IP8_15_14, CAN0_RX_B, SEL_RCAN0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_15_14, HSCK0_B, SEL_HSCIF_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_15_14, SSI_SDATA1_B, SEL_SSI1_1),
+       PINMUX_IPSR_MSEL(IP8_15_14, CAN0_RX_B, SEL_RCAN0_1),
+       PINMUX_IPSR_MSEL(IP8_15_14, HSCK0_B, SEL_HSCIF_1),
+       PINMUX_IPSR_MSEL(IP8_15_14, SSI_SDATA1_B, SEL_SSI1_1),
 
        PINMUX_IPSR_DATA(IP8_17_16, DU0_DISP),
-       PINMUX_IPSR_MODSEL_DATA(IP8_17_16, CAN0_TX_B, SEL_RCAN0_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_17_16, HRX0_B, SEL_HSCIF_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_17_16, AUDIO_CLKA_B, SEL_AUDIO_CLKA_1),
+       PINMUX_IPSR_MSEL(IP8_17_16, CAN0_TX_B, SEL_RCAN0_1),
+       PINMUX_IPSR_MSEL(IP8_17_16, HRX0_B, SEL_HSCIF_1),
+       PINMUX_IPSR_MSEL(IP8_17_16, AUDIO_CLKA_B, SEL_AUDIO_CLKA_1),
 
        PINMUX_IPSR_DATA(IP8_19_18, DU0_CDE),
-       PINMUX_IPSR_MODSEL_DATA(IP8_19_18, HTX0_B, SEL_HSCIF_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_19_18, AUDIO_CLKB_B, SEL_AUDIO_CLKB_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_19_18, LCD_VCPWC_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP8_19_18, HTX0_B, SEL_HSCIF_1),
+       PINMUX_IPSR_MSEL(IP8_19_18, AUDIO_CLKB_B, SEL_AUDIO_CLKB_1),
+       PINMUX_IPSR_MSEL(IP8_19_18, LCD_VCPWC_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP8_22_20, IRQ0_A, SEL_INTC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_22_20, HSPI_TX_B, SEL_HSPI_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_22_20, RX3_E, SEL_SCIF3_4),
+       PINMUX_IPSR_MSEL(IP8_22_20, IRQ0_A, SEL_INTC_0),
+       PINMUX_IPSR_MSEL(IP8_22_20, HSPI_TX_B, SEL_HSPI_1),
+       PINMUX_IPSR_MSEL(IP8_22_20, RX3_E, SEL_SCIF3_4),
        PINMUX_IPSR_DATA(IP8_22_20, ET0_ERXD0),
 
-       PINMUX_IPSR_MODSEL_DATA(IP8_25_23, IRQ1_A, SEL_INTC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_25_23, HSPI_RX_B, SEL_HSPI_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_25_23, TX3_E, SEL_SCIF3_4),
+       PINMUX_IPSR_MSEL(IP8_25_23, IRQ1_A, SEL_INTC_0),
+       PINMUX_IPSR_MSEL(IP8_25_23, HSPI_RX_B, SEL_HSPI_1),
+       PINMUX_IPSR_MSEL(IP8_25_23, TX3_E, SEL_SCIF3_4),
        PINMUX_IPSR_DATA(IP8_25_23, ET0_ERXD1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP8_27_26, IRQ2_A, SEL_INTC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_27_26, CTS0_A, SEL_SCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_27_26, HCTS0_B, SEL_HSCIF_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_27_26, ET0_ERXD2_A, SEL_ET0_0),
+       PINMUX_IPSR_MSEL(IP8_27_26, IRQ2_A, SEL_INTC_0),
+       PINMUX_IPSR_MSEL(IP8_27_26, CTS0_A, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP8_27_26, HCTS0_B, SEL_HSCIF_1),
+       PINMUX_IPSR_MSEL(IP8_27_26, ET0_ERXD2_A, SEL_ET0_0),
 
-       PINMUX_IPSR_MODSEL_DATA(IP8_29_28, IRQ3_A, SEL_INTC_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_29_28, RTS0_A, SEL_SCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP8_29_28, HRTS0_B, SEL_HSCIF_1),
-       PINMUX_IPSR_MODSEL_DATA(IP8_29_28, ET0_ERXD3_A, SEL_ET0_0),
+       PINMUX_IPSR_MSEL(IP8_29_28, IRQ3_A, SEL_INTC_0),
+       PINMUX_IPSR_MSEL(IP8_29_28, RTS0_A, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP8_29_28, HRTS0_B, SEL_HSCIF_1),
+       PINMUX_IPSR_MSEL(IP8_29_28, ET0_ERXD3_A, SEL_ET0_0),
 
        /* IPSR9 */
-       PINMUX_IPSR_MODSEL_DATA(IP9_1_0, VI1_CLK_A, SEL_VIN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_1_0, FD0_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_1_0, LCD_DATA0_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_1_0, VI1_CLK_A, SEL_VIN1_0),
+       PINMUX_IPSR_MSEL(IP9_1_0, FD0_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP9_1_0, LCD_DATA0_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_3_2, VI1_0_A, SEL_VIN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_3_2, FD1_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_3_2, LCD_DATA1_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_3_2, VI1_0_A, SEL_VIN1_0),
+       PINMUX_IPSR_MSEL(IP9_3_2, FD1_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP9_3_2, LCD_DATA1_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_5_4, VI1_1_A, SEL_VIN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_5_4, FD2_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_5_4, LCD_DATA2_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_5_4, VI1_1_A, SEL_VIN1_0),
+       PINMUX_IPSR_MSEL(IP9_5_4, FD2_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP9_5_4, LCD_DATA2_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_7_6, VI1_2_A, SEL_VIN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_7_6, FD3_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_7_6, LCD_DATA3_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_7_6, VI1_2_A, SEL_VIN1_0),
+       PINMUX_IPSR_MSEL(IP9_7_6, FD3_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP9_7_6, LCD_DATA3_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_9_8, VI1_3_A, SEL_VIN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_9_8, FD4_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_9_8, LCD_DATA4_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_9_8, VI1_3_A, SEL_VIN1_0),
+       PINMUX_IPSR_MSEL(IP9_9_8, FD4_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP9_9_8, LCD_DATA4_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_10, VI1_4_A, SEL_VIN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_10, FD5_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_11_10, LCD_DATA5_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_11_10, VI1_4_A, SEL_VIN1_0),
+       PINMUX_IPSR_MSEL(IP9_11_10, FD5_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP9_11_10, LCD_DATA5_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_13_12, VI1_5_A, SEL_VIN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_13_12, FD6_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_13_12, LCD_DATA6_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_13_12, VI1_5_A, SEL_VIN1_0),
+       PINMUX_IPSR_MSEL(IP9_13_12, FD6_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP9_13_12, LCD_DATA6_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_14, VI1_6_A, SEL_VIN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_14, FD7_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_15_14, LCD_DATA7_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_15_14, VI1_6_A, SEL_VIN1_0),
+       PINMUX_IPSR_MSEL(IP9_15_14, FD7_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP9_15_14, LCD_DATA7_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_17_16, VI1_7_A, SEL_VIN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_17_16, FCE_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_17_16, LCD_DATA8_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_17_16, VI1_7_A, SEL_VIN1_0),
+       PINMUX_IPSR_MSEL(IP9_17_16, FCE_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP9_17_16, LCD_DATA8_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_19_18, SSI_SCK0_A, SEL_SSI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_19_18, TIOC1A_B, SEL_MTU2_CH1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_19_18, LCD_DATA9_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_19_18, SSI_SCK0_A, SEL_SSI0_0),
+       PINMUX_IPSR_MSEL(IP9_19_18, TIOC1A_B, SEL_MTU2_CH1_1),
+       PINMUX_IPSR_MSEL(IP9_19_18, LCD_DATA9_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_21_20, SSI_WS0_A, SEL_SSI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_21_20, TIOC1B_B, SEL_MTU2_CH1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_21_20, LCD_DATA10_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_21_20, SSI_WS0_A, SEL_SSI0_0),
+       PINMUX_IPSR_MSEL(IP9_21_20, TIOC1B_B, SEL_MTU2_CH1_1),
+       PINMUX_IPSR_MSEL(IP9_21_20, LCD_DATA10_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_23_22, SSI_SDATA0_A, SEL_SSI0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_23_22, VI1_0_B, SEL_VIN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_23_22, TIOC2A_B, SEL_MTU2_CH2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_23_22, LCD_DATA11_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_23_22, SSI_SDATA0_A, SEL_SSI0_0),
+       PINMUX_IPSR_MSEL(IP9_23_22, VI1_0_B, SEL_VIN1_1),
+       PINMUX_IPSR_MSEL(IP9_23_22, TIOC2A_B, SEL_MTU2_CH2_1),
+       PINMUX_IPSR_MSEL(IP9_23_22, LCD_DATA11_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_25_24, SSI_SCK1_A, SEL_SSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_25_24, VI1_1_B, SEL_VIN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_25_24, TIOC2B_B, SEL_MTU2_CH2_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_25_24, LCD_DATA12_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_25_24, SSI_SCK1_A, SEL_SSI1_0),
+       PINMUX_IPSR_MSEL(IP9_25_24, VI1_1_B, SEL_VIN1_1),
+       PINMUX_IPSR_MSEL(IP9_25_24, TIOC2B_B, SEL_MTU2_CH2_1),
+       PINMUX_IPSR_MSEL(IP9_25_24, LCD_DATA12_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_27_26, SSI_WS1_A, SEL_SSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_27_26, VI1_2_B, SEL_VIN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_27_26, LCD_DATA13_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_27_26, SSI_WS1_A, SEL_SSI1_0),
+       PINMUX_IPSR_MSEL(IP9_27_26, VI1_2_B, SEL_VIN1_1),
+       PINMUX_IPSR_MSEL(IP9_27_26, LCD_DATA13_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP9_29_28, SSI_SDATA1_A, SEL_SSI1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP9_29_28, VI1_3_B, SEL_VIN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP9_29_28, LCD_DATA14_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP9_29_28, SSI_SDATA1_A, SEL_SSI1_0),
+       PINMUX_IPSR_MSEL(IP9_29_28, VI1_3_B, SEL_VIN1_1),
+       PINMUX_IPSR_MSEL(IP9_29_28, LCD_DATA14_B, SEL_LCDC_1),
 
        /* IPSE10 */
        PINMUX_IPSR_DATA(IP10_2_0, SSI_SCK23),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, VI1_4_B, SEL_VIN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, RX1_D, SEL_SCIF1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, FCLE_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_2_0, LCD_DATA15_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP10_2_0, VI1_4_B, SEL_VIN1_1),
+       PINMUX_IPSR_MSEL(IP10_2_0, RX1_D, SEL_SCIF1_3),
+       PINMUX_IPSR_MSEL(IP10_2_0, FCLE_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP10_2_0, LCD_DATA15_B, SEL_LCDC_1),
 
        PINMUX_IPSR_DATA(IP10_5_3, SSI_WS23),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, VI1_5_B, SEL_VIN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, TX1_D, SEL_SCIF1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, HSCK0_C, SEL_HSCIF_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, FALE_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_5_3, LCD_DON_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP10_5_3, VI1_5_B, SEL_VIN1_1),
+       PINMUX_IPSR_MSEL(IP10_5_3, TX1_D, SEL_SCIF1_3),
+       PINMUX_IPSR_MSEL(IP10_5_3, HSCK0_C, SEL_HSCIF_2),
+       PINMUX_IPSR_MSEL(IP10_5_3, FALE_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP10_5_3, LCD_DON_B, SEL_LCDC_1),
 
        PINMUX_IPSR_DATA(IP10_8_6, SSI_SDATA2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, VI1_6_B, SEL_VIN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, HRX0_C, SEL_HSCIF_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, FRE_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_8_6, LCD_CL1_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP10_8_6, VI1_6_B, SEL_VIN1_1),
+       PINMUX_IPSR_MSEL(IP10_8_6, HRX0_C, SEL_HSCIF_2),
+       PINMUX_IPSR_MSEL(IP10_8_6, FRE_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP10_8_6, LCD_CL1_B, SEL_LCDC_1),
 
        PINMUX_IPSR_DATA(IP10_11_9, SSI_SDATA3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, VI1_7_B, SEL_VIN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, HTX0_C, SEL_HSCIF_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, FWE_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_11_9, LCD_CL2_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP10_11_9, VI1_7_B, SEL_VIN1_1),
+       PINMUX_IPSR_MSEL(IP10_11_9, HTX0_C, SEL_HSCIF_2),
+       PINMUX_IPSR_MSEL(IP10_11_9, FWE_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP10_11_9, LCD_CL2_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, AUDIO_CLKA_A, SEL_AUDIO_CLKA_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, VI1_CLK_B, SEL_VIN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, SCK1_D, SEL_SCIF1_3),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, IECLK_B, SEL_IEBUS_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_14_12, LCD_FLM_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP10_14_12, AUDIO_CLKA_A, SEL_AUDIO_CLKA_0),
+       PINMUX_IPSR_MSEL(IP10_14_12, VI1_CLK_B, SEL_VIN1_1),
+       PINMUX_IPSR_MSEL(IP10_14_12, SCK1_D, SEL_SCIF1_3),
+       PINMUX_IPSR_MSEL(IP10_14_12, IECLK_B, SEL_IEBUS_1),
+       PINMUX_IPSR_MSEL(IP10_14_12, LCD_FLM_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP10_15, AUDIO_CLKB_A, SEL_AUDIO_CLKB_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_15, LCD_CLK_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP10_15, AUDIO_CLKB_A, SEL_AUDIO_CLKB_0),
+       PINMUX_IPSR_MSEL(IP10_15, LCD_CLK_B, SEL_LCDC_1),
 
        PINMUX_IPSR_DATA(IP10_18_16, AUDIO_CLKC),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_16, SCK1_E, SEL_SCIF1_4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_16, HCTS0_C, SEL_HSCIF_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_16, FRB_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_18_16, LCD_VEPWC_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP10_18_16, SCK1_E, SEL_SCIF1_4),
+       PINMUX_IPSR_MSEL(IP10_18_16, HCTS0_C, SEL_HSCIF_2),
+       PINMUX_IPSR_MSEL(IP10_18_16, FRB_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP10_18_16, LCD_VEPWC_B, SEL_LCDC_1),
 
        PINMUX_IPSR_DATA(IP10_21_19, AUDIO_CLKOUT),
-       PINMUX_IPSR_MODSEL_DATA(IP10_21_19, TX1_E, SEL_SCIF1_4),
-       PINMUX_IPSR_MODSEL_DATA(IP10_21_19, HRTS0_C, SEL_HSCIF_2),
-       PINMUX_IPSR_MODSEL_DATA(IP10_21_19, FSE_B, SEL_FLCTL_1),
-       PINMUX_IPSR_MODSEL_DATA(IP10_21_19, LCD_M_DISP_B, SEL_LCDC_1),
+       PINMUX_IPSR_MSEL(IP10_21_19, TX1_E, SEL_SCIF1_4),
+       PINMUX_IPSR_MSEL(IP10_21_19, HRTS0_C, SEL_HSCIF_2),
+       PINMUX_IPSR_MSEL(IP10_21_19, FSE_B, SEL_FLCTL_1),
+       PINMUX_IPSR_MSEL(IP10_21_19, LCD_M_DISP_B, SEL_LCDC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP10_22, CAN_CLK_A, SEL_RCAN_CLK_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_22, RX4_D, SEL_SCIF4_3),
+       PINMUX_IPSR_MSEL(IP10_22, CAN_CLK_A, SEL_RCAN_CLK_0),
+       PINMUX_IPSR_MSEL(IP10_22, RX4_D, SEL_SCIF4_3),
 
-       PINMUX_IPSR_MODSEL_DATA(IP10_24_23, CAN0_TX_A, SEL_RCAN0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_24_23, TX4_D, SEL_SCIF4_3),
+       PINMUX_IPSR_MSEL(IP10_24_23, CAN0_TX_A, SEL_RCAN0_0),
+       PINMUX_IPSR_MSEL(IP10_24_23, TX4_D, SEL_SCIF4_3),
        PINMUX_IPSR_DATA(IP10_24_23, MLB_CLK),
 
-       PINMUX_IPSR_MODSEL_DATA(IP10_25, CAN1_RX_A, SEL_RCAN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_25, IRQ1_B, SEL_INTC_1),
+       PINMUX_IPSR_MSEL(IP10_25, CAN1_RX_A, SEL_RCAN1_0),
+       PINMUX_IPSR_MSEL(IP10_25, IRQ1_B, SEL_INTC_1),
 
-       PINMUX_IPSR_MODSEL_DATA(IP10_27_26, CAN0_RX_A, SEL_RCAN0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_27_26, IRQ0_B, SEL_INTC_1),
+       PINMUX_IPSR_MSEL(IP10_27_26, CAN0_RX_A, SEL_RCAN0_0),
+       PINMUX_IPSR_MSEL(IP10_27_26, IRQ0_B, SEL_INTC_1),
        PINMUX_IPSR_DATA(IP10_27_26, MLB_SIG),
 
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_28, CAN1_TX_A, SEL_RCAN1_0),
-       PINMUX_IPSR_MODSEL_DATA(IP10_29_28, TX5_C, SEL_SCIF1_2),
+       PINMUX_IPSR_MSEL(IP10_29_28, CAN1_TX_A, SEL_RCAN1_0),
+       PINMUX_IPSR_MSEL(IP10_29_28, TX5_C, SEL_SCIF1_2),
        PINMUX_IPSR_DATA(IP10_29_28, MLB_DAT),
 
        /* IPSR11 */
        PINMUX_IPSR_DATA(IP11_0, SCL1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_0, SCIF_CLK_C, SEL_SCIF_CLK_2),
+       PINMUX_IPSR_MSEL(IP11_0, SCIF_CLK_C, SEL_SCIF_CLK_2),
 
        PINMUX_IPSR_DATA(IP11_1, SDA1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_0, RX1_E, SEL_SCIF1_4),
+       PINMUX_IPSR_MSEL(IP11_0, RX1_E, SEL_SCIF1_4),
 
        PINMUX_IPSR_DATA(IP11_2, SDA0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_2, HIFEBL_A, SEL_HIF_0),
+       PINMUX_IPSR_MSEL(IP11_2, HIFEBL_A, SEL_HIF_0),
 
        PINMUX_IPSR_DATA(IP11_3, SDSELF),
-       PINMUX_IPSR_MODSEL_DATA(IP11_3, RTS1_E, SEL_SCIF1_3),
+       PINMUX_IPSR_MSEL(IP11_3, RTS1_E, SEL_SCIF1_3),
 
-       PINMUX_IPSR_MODSEL_DATA(IP11_6_4, SCIF_CLK_A, SEL_SCIF_CLK_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_6_4, HSPI_CLK_A, SEL_HSPI_0),
+       PINMUX_IPSR_MSEL(IP11_6_4, SCIF_CLK_A, SEL_SCIF_CLK_0),
+       PINMUX_IPSR_MSEL(IP11_6_4, HSPI_CLK_A, SEL_HSPI_0),
        PINMUX_IPSR_DATA(IP11_6_4, VI0_CLK),
-       PINMUX_IPSR_MODSEL_DATA(IP11_6_4, RMII0_TXD0_A, SEL_RMII_0),
+       PINMUX_IPSR_MSEL(IP11_6_4, RMII0_TXD0_A, SEL_RMII_0),
        PINMUX_IPSR_DATA(IP11_6_4, ET0_ERXD4),
 
-       PINMUX_IPSR_MODSEL_DATA(IP11_9_7, SCK0_A, SEL_SCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_9_7, HSPI_CS_A, SEL_HSPI_0),
+       PINMUX_IPSR_MSEL(IP11_9_7, SCK0_A, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP11_9_7, HSPI_CS_A, SEL_HSPI_0),
        PINMUX_IPSR_DATA(IP11_9_7, VI0_CLKENB),
-       PINMUX_IPSR_MODSEL_DATA(IP11_9_7, RMII0_TXD1_A, SEL_RMII_0),
+       PINMUX_IPSR_MSEL(IP11_9_7, RMII0_TXD1_A, SEL_RMII_0),
        PINMUX_IPSR_DATA(IP11_9_7, ET0_ERXD5),
 
-       PINMUX_IPSR_MODSEL_DATA(IP11_11_10, RX0_A, SEL_SCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_11_10, HSPI_RX_A, SEL_HSPI_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_11_10, RMII0_RXD0_A, SEL_RMII_0),
+       PINMUX_IPSR_MSEL(IP11_11_10, RX0_A, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP11_11_10, HSPI_RX_A, SEL_HSPI_0),
+       PINMUX_IPSR_MSEL(IP11_11_10, RMII0_RXD0_A, SEL_RMII_0),
        PINMUX_IPSR_DATA(IP11_11_10, ET0_ERXD6),
 
-       PINMUX_IPSR_MODSEL_DATA(IP11_12, TX0_A, SEL_SCIF0_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_12, HSPI_TX_A, SEL_HSPI_0),
+       PINMUX_IPSR_MSEL(IP11_12, TX0_A, SEL_SCIF0_0),
+       PINMUX_IPSR_MSEL(IP11_12, HSPI_TX_A, SEL_HSPI_0),
 
        PINMUX_IPSR_DATA(IP11_15_13, PENC1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_15_13, TX3_D, SEL_SCIF3_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_15_13, CAN1_TX_B,  SEL_RCAN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_15_13, TX5_D, SEL_SCIF5_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_15_13, IETX_B, SEL_IEBUS_1),
+       PINMUX_IPSR_MSEL(IP11_15_13, TX3_D, SEL_SCIF3_3),
+       PINMUX_IPSR_MSEL(IP11_15_13, CAN1_TX_B,  SEL_RCAN1_1),
+       PINMUX_IPSR_MSEL(IP11_15_13, TX5_D, SEL_SCIF5_3),
+       PINMUX_IPSR_MSEL(IP11_15_13, IETX_B, SEL_IEBUS_1),
 
        PINMUX_IPSR_DATA(IP11_18_16, USB_OVC1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_18_16, RX3_D, SEL_SCIF3_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_18_16, CAN1_RX_B, SEL_RCAN1_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_18_16, RX5_D, SEL_SCIF5_3),
-       PINMUX_IPSR_MODSEL_DATA(IP11_18_16, IERX_B, SEL_IEBUS_1),
+       PINMUX_IPSR_MSEL(IP11_18_16, RX3_D, SEL_SCIF3_3),
+       PINMUX_IPSR_MSEL(IP11_18_16, CAN1_RX_B, SEL_RCAN1_1),
+       PINMUX_IPSR_MSEL(IP11_18_16, RX5_D, SEL_SCIF5_3),
+       PINMUX_IPSR_MSEL(IP11_18_16, IERX_B, SEL_IEBUS_1),
 
        PINMUX_IPSR_DATA(IP11_20_19, DREQ0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_20_19, SD1_CLK_A, SEL_SDHI1_0),
+       PINMUX_IPSR_MSEL(IP11_20_19, SD1_CLK_A, SEL_SDHI1_0),
        PINMUX_IPSR_DATA(IP11_20_19, ET0_TX_EN),
 
        PINMUX_IPSR_DATA(IP11_22_21, DACK0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_22_21, SD1_DAT3_A, SEL_SDHI1_0),
+       PINMUX_IPSR_MSEL(IP11_22_21, SD1_DAT3_A, SEL_SDHI1_0),
        PINMUX_IPSR_DATA(IP11_22_21, ET0_TX_ER),
 
        PINMUX_IPSR_DATA(IP11_25_23, DREQ1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_25_23, HSPI_CLK_B, SEL_HSPI_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_25_23, RX4_B, SEL_SCIF4_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_25_23, ET0_PHY_INT_C, SEL_ET0_CTL_0),
-       PINMUX_IPSR_MODSEL_DATA(IP11_25_23, ET0_TX_CLK_A, SEL_ET0_0),
+       PINMUX_IPSR_MSEL(IP11_25_23, HSPI_CLK_B, SEL_HSPI_1),
+       PINMUX_IPSR_MSEL(IP11_25_23, RX4_B, SEL_SCIF4_1),
+       PINMUX_IPSR_MSEL(IP11_25_23, ET0_PHY_INT_C, SEL_ET0_CTL_0),
+       PINMUX_IPSR_MSEL(IP11_25_23, ET0_TX_CLK_A, SEL_ET0_0),
 
        PINMUX_IPSR_DATA(IP11_27_26, DACK1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_27_26, HSPI_CS_B, SEL_HSPI_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_27_26, TX4_B, SEL_SCIF3_1),
-       PINMUX_IPSR_MODSEL_DATA(IP11_27_26, ET0_RX_CLK_A, SEL_ET0_0),
+       PINMUX_IPSR_MSEL(IP11_27_26, HSPI_CS_B, SEL_HSPI_1),
+       PINMUX_IPSR_MSEL(IP11_27_26, TX4_B, SEL_SCIF3_1),
+       PINMUX_IPSR_MSEL(IP11_27_26, ET0_RX_CLK_A, SEL_ET0_0),
 
        PINMUX_IPSR_DATA(IP11_28, PRESETOUT),
        PINMUX_IPSR_DATA(IP11_28, ST_CLKOUT),
@@ -2445,6 +2445,6 @@ const struct sh_pfc_soc_info sh7734_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index 625661a88c52703931e6c574dde016f379886a70..0555a1fe076ed353c4627239534a702e1b810c5c 100644 (file)
@@ -2238,6 +2238,6 @@ const struct sh_pfc_soc_info sh7757_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index b38dd7e3e375806c8cba0e3e809758fe0bd534de..1934cbec39651cb7426cadf005ec397e555e6e53 100644 (file)
@@ -1269,6 +1269,6 @@ const struct sh_pfc_soc_info sh7785_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index 6cb4e0aaf20b0217598b884eeececa57f58f80f5..c98585d80de8df4e176cdcd1f9636d77bded3bb2 100644 (file)
@@ -813,6 +813,6 @@ const struct sh_pfc_soc_info sh7786_pinmux_info = {
        .cfg_regs = pinmux_config_regs,
        .data_regs = pinmux_data_regs,
 
-       .gpio_data = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
 };
index a3fcb2284d91117437eb1afc3fc6189f078bd3b7..3f60c900645eebd51a8af4f4fe44a269a25ebecd 100644 (file)
@@ -554,8 +554,8 @@ const struct sh_pfc_soc_info shx3_pinmux_info = {
        .nr_pins        = ARRAY_SIZE(pinmux_pins),
        .func_gpios     = pinmux_func_gpios,
        .nr_func_gpios  = ARRAY_SIZE(pinmux_func_gpios),
-       .gpio_data      = pinmux_data,
-       .gpio_data_size = ARRAY_SIZE(pinmux_data),
+       .pinmux_data    = pinmux_data,
+       .pinmux_data_size = ARRAY_SIZE(pinmux_data),
        .cfg_regs       = pinmux_config_regs,
        .data_regs      = pinmux_data_regs,
 };
index 15afd49fd4e33b5e72a9fe4b8c618d5ef4d2aa3a..7b373d43d981899f1d85e5eab6846025235f1d89 100644 (file)
@@ -52,6 +52,29 @@ struct sh_pfc_pin_group {
        unsigned int nr_pins;
 };
 
+/*
+ * Using union vin_data saves memory occupied by the VIN data pins.
+ * VIN_DATA_PIN_GROUP() is  a macro  used  to describe the VIN pin groups
+ * in this case.
+ */
+#define VIN_DATA_PIN_GROUP(n, s)                               \
+       {                                                       \
+               .name = #n#s,                                   \
+               .pins = n##_pins.data##s,                       \
+               .mux = n##_mux.data##s,                         \
+               .nr_pins = ARRAY_SIZE(n##_pins.data##s),        \
+       }
+
+union vin_data {
+       unsigned int data24[24];
+       unsigned int data20[20];
+       unsigned int data16[16];
+       unsigned int data12[12];
+       unsigned int data10[10];
+       unsigned int data8[8];
+       unsigned int data4[4];
+};
+
 #define SH_PFC_FUNCTION(n)                             \
        {                                               \
                .name = #n,                             \
@@ -98,17 +121,11 @@ struct pinmux_data_reg {
        .enum_ids = (const u16 [r_width]) \
 
 struct pinmux_irq {
-       int irq;
        const short *gpios;
 };
 
-#ifdef CONFIG_ARCH_MULTIPLATFORM
-#define PINMUX_IRQ(irq_nr, ids...)                        \
+#define PINMUX_IRQ(ids...)                        \
        { .gpios = (const short []) { ids, -1 } }
-#else
-#define PINMUX_IRQ(irq_nr, ids...)                        \
-       { .irq = irq_nr, .gpios = (const short []) { ids, -1 } }
-#endif
 
 struct pinmux_range {
        u16 begin;
@@ -143,14 +160,16 @@ struct sh_pfc_soc_info {
        const struct sh_pfc_function *functions;
        unsigned int nr_functions;
 
+#ifdef CONFIG_SUPERH
        const struct pinmux_func *func_gpios;
        unsigned int nr_func_gpios;
+#endif
 
        const struct pinmux_cfg_reg *cfg_regs;
        const struct pinmux_data_reg *data_regs;
 
-       const u16 *gpio_data;
-       unsigned int gpio_data_size;
+       const u16 *pinmux_data;
+       unsigned int pinmux_data_size;
 
        const struct pinmux_irq *gpio_irq;
        unsigned int gpio_irq_size;
@@ -163,7 +182,7 @@ struct sh_pfc_soc_info {
  */
 
 /*
- * sh_pfc_soc_info gpio_data array macros
+ * sh_pfc_soc_info pinmux_data array macros
  */
 
 #define PINMUX_DATA(data_or_mark, ids...)      data_or_mark, ids, 0
@@ -177,33 +196,33 @@ struct sh_pfc_soc_info {
 #define PINMUX_IPSR_NOFN(ipsr, fn, ms)                                 \
        PINMUX_DATA(fn##_MARK, FN_##ipsr, FN_##ms)
 #define PINMUX_IPSR_MSEL(ipsr, fn, ms)                                 \
-       PINMUX_DATA(fn##_MARK, FN_##fn, FN_##ipsr, FN_##ms)
-#define PINMUX_IPSR_MODSEL_DATA(ipsr, fn, ms)                          \
        PINMUX_DATA(fn##_MARK, FN_##ms, FN_##ipsr, FN_##fn)
 
 /*
  * GP port style (32 ports banks)
  */
 
-#define PORT_GP_1(bank, pin, fn, sfx) fn(bank, pin, GP_##bank##_##pin, sfx)
-
-#define PORT_GP_32(bank, fn, sfx)                                      \
-       PORT_GP_1(bank, 0,  fn, sfx), PORT_GP_1(bank, 1,  fn, sfx),     \
-       PORT_GP_1(bank, 2,  fn, sfx), PORT_GP_1(bank, 3,  fn, sfx),     \
-       PORT_GP_1(bank, 4,  fn, sfx), PORT_GP_1(bank, 5,  fn, sfx),     \
-       PORT_GP_1(bank, 6,  fn, sfx), PORT_GP_1(bank, 7,  fn, sfx),     \
-       PORT_GP_1(bank, 8,  fn, sfx), PORT_GP_1(bank, 9,  fn, sfx),     \
-       PORT_GP_1(bank, 10, fn, sfx), PORT_GP_1(bank, 11, fn, sfx),     \
-       PORT_GP_1(bank, 12, fn, sfx), PORT_GP_1(bank, 13, fn, sfx),     \
-       PORT_GP_1(bank, 14, fn, sfx), PORT_GP_1(bank, 15, fn, sfx),     \
-       PORT_GP_1(bank, 16, fn, sfx), PORT_GP_1(bank, 17, fn, sfx),     \
-       PORT_GP_1(bank, 18, fn, sfx), PORT_GP_1(bank, 19, fn, sfx),     \
-       PORT_GP_1(bank, 20, fn, sfx), PORT_GP_1(bank, 21, fn, sfx),     \
-       PORT_GP_1(bank, 22, fn, sfx), PORT_GP_1(bank, 23, fn, sfx),     \
-       PORT_GP_1(bank, 24, fn, sfx), PORT_GP_1(bank, 25, fn, sfx),     \
-       PORT_GP_1(bank, 26, fn, sfx), PORT_GP_1(bank, 27, fn, sfx),     \
-       PORT_GP_1(bank, 28, fn, sfx), PORT_GP_1(bank, 29, fn, sfx),     \
-       PORT_GP_1(bank, 30, fn, sfx), PORT_GP_1(bank, 31, fn, sfx)
+#define PORT_GP_CFG_1(bank, pin, fn, sfx, cfg) fn(bank, pin, GP_##bank##_##pin, sfx, cfg)
+#define PORT_GP_1(bank, pin, fn, sfx)  PORT_GP_CFG_1(bank, pin, fn, sfx, 0)
+
+#define PORT_GP_CFG_32(bank, fn, sfx, cfg)                             \
+       PORT_GP_CFG_1(bank, 0,  fn, sfx, cfg), PORT_GP_CFG_1(bank, 1,  fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 2,  fn, sfx, cfg), PORT_GP_CFG_1(bank, 3,  fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 4,  fn, sfx, cfg), PORT_GP_CFG_1(bank, 5,  fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 6,  fn, sfx, cfg), PORT_GP_CFG_1(bank, 7,  fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 8,  fn, sfx, cfg), PORT_GP_CFG_1(bank, 9,  fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 10, fn, sfx, cfg), PORT_GP_CFG_1(bank, 11, fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 12, fn, sfx, cfg), PORT_GP_CFG_1(bank, 13, fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 14, fn, sfx, cfg), PORT_GP_CFG_1(bank, 15, fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 16, fn, sfx, cfg), PORT_GP_CFG_1(bank, 17, fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 18, fn, sfx, cfg), PORT_GP_CFG_1(bank, 19, fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 20, fn, sfx, cfg), PORT_GP_CFG_1(bank, 21, fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 22, fn, sfx, cfg), PORT_GP_CFG_1(bank, 23, fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 24, fn, sfx, cfg), PORT_GP_CFG_1(bank, 25, fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 26, fn, sfx, cfg), PORT_GP_CFG_1(bank, 27, fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 28, fn, sfx, cfg), PORT_GP_CFG_1(bank, 29, fn, sfx, cfg),   \
+       PORT_GP_CFG_1(bank, 30, fn, sfx, cfg), PORT_GP_CFG_1(bank, 31, fn, sfx, cfg)
+#define PORT_GP_32(bank, fn, sfx)      PORT_GP_CFG_32(bank, fn, sfx, 0)
 
 #define PORT_GP_32_REV(bank, fn, sfx)                                  \
        PORT_GP_1(bank, 31, fn, sfx), PORT_GP_1(bank, 30, fn, sfx),     \
@@ -224,20 +243,21 @@ struct sh_pfc_soc_info {
        PORT_GP_1(bank, 1,  fn, sfx), PORT_GP_1(bank, 0,  fn, sfx)
 
 /* GP_ALL(suffix) - Expand to a list of GP_#_#_suffix */
-#define _GP_ALL(bank, pin, name, sfx)  name##_##sfx
+#define _GP_ALL(bank, pin, name, sfx, cfg)     name##_##sfx
 #define GP_ALL(str)                    CPU_ALL_PORT(_GP_ALL, str)
 
 /* PINMUX_GPIO_GP_ALL - Expand to a list of sh_pfc_pin entries */
-#define _GP_GPIO(bank, _pin, _name, sfx)                               \
+#define _GP_GPIO(bank, _pin, _name, sfx, cfg)                          \
        {                                                               \
                .pin = (bank * 32) + _pin,                              \
                .name = __stringify(_name),                             \
                .enum_id = _name##_DATA,                                \
+               .configs = cfg,                                         \
        }
 #define PINMUX_GPIO_GP_ALL()           CPU_ALL_PORT(_GP_GPIO, unused)
 
 /* PINMUX_DATA_GP_ALL -  Expand to a list of name_DATA, name_FN marks */
-#define _GP_DATA(bank, pin, name, sfx) PINMUX_DATA(name##_DATA, name##_FN)
+#define _GP_DATA(bank, pin, name, sfx, cfg)    PINMUX_DATA(name##_DATA, name##_FN)
 #define PINMUX_DATA_GP_ALL()           CPU_ALL_PORT(_GP_DATA, unused)
 
 /*
@@ -326,4 +346,9 @@ struct sh_pfc_soc_info {
                }                                                       \
        }
 
+/*
+ * GPIO number helper macro for R-Car
+ */
+#define RCAR_GP_PIN(bank, pin)         (((bank) * 32) + (pin))
+
 #endif /* __SH_PFC_H */
index 0d24d9e4b70c9583899d72aa02f072a8a0bdcd0d..829018c812bdda3690f7449626cecccb2de8874b 100644 (file)
@@ -544,6 +544,11 @@ static const struct pinctrl_pin_desc atlas7_ioc_pads[] = {
        PINCTRL_PIN(156, "lvds_tx0d1n"),
        PINCTRL_PIN(157, "lvds_tx0d0p"),
        PINCTRL_PIN(158, "lvds_tx0d0n"),
+       PINCTRL_PIN(159, "jtag_tdo"),
+       PINCTRL_PIN(160, "jtag_tms"),
+       PINCTRL_PIN(161, "jtag_tck"),
+       PINCTRL_PIN(162, "jtag_tdi"),
+       PINCTRL_PIN(163, "jtag_trstn"),
 };
 
 struct atlas7_pad_config atlas7_ioc_pad_confs[] = {
@@ -708,6 +713,11 @@ struct atlas7_pad_config atlas7_ioc_pad_confs[] = {
        PADCONF(156, 7, 0x130, 0x270, -1, 0x480, 28, 14, 0, 7),
        PADCONF(157, 7, 0x138, 0x278, -1, 0x480, 0, 0, 0, 8),
        PADCONF(158, 7, 0x138, 0x278, -1, 0x480, 4, 2, 0, 9),
+       PADCONF(159, 5, 0x140, 0x280, 0x380, -1, 0, 0, 0, 0),
+       PADCONF(160, 6, 0x140, 0x280, 0x380, -1, 4, 2, 2, 0),
+       PADCONF(161, 5, 0x140, 0x280, 0x380, -1, 8, 4, 4, 0),
+       PADCONF(162, 6, 0x140, 0x280, 0x380, -1, 12, 6, 6, 0),
+       PADCONF(163, 6, 0x140, 0x280, 0x380, -1, 16, 8, 8, 0),
 };
 
 /* pin list of each pin group */
@@ -724,12 +734,15 @@ static const unsigned int sp_rgmii_gpio_pins[] = { 97, 98, 99, 100, 101, 102,
                141, 142, 143, 144, 145, 146, 147, 148, };
 static const unsigned int lvds_gpio_pins[] = { 157, 158, 155, 156, 153, 154,
                151, 152, 149, 150, };
-static const unsigned int uart_nand_gpio_pins[] = { 44, 43, 42, 41, 40, 39,
-               38, 37, 46, 47, 48, 49, 50, 52, 51, 45, 133, 134, 135, 136,
-               137, 138, 139, 140, };
+static const unsigned int jtag_uart_nand_gpio_pins[] = { 44, 43, 42, 41, 40,
+               39, 38, 37, 46, 47, 48, 49, 50, 52, 51, 45, 133, 134, 135,
+               136, 137, 138, 139, 140, 159, 160, 161, 162, 163, };
 static const unsigned int rtc_gpio_pins[] = { 0, 1, 2, 3, 4, 10, 11, 12, 13,
-               14, 15, 16, 17, };
+               14, 15, 16, 17, 9, };
 static const unsigned int audio_ac97_pins[] = { 113, 118, 115, 114, };
+static const unsigned int audio_digmic_pins0[] = { 51, };
+static const unsigned int audio_digmic_pins1[] = { 122, };
+static const unsigned int audio_digmic_pins2[] = { 161, };
 static const unsigned int audio_func_dbg_pins[] = { 141, 144, 44, 43, 42, 41,
                40, 39, 38, 37, 74, 75, 76, 77, 78, 79, 81, 113, 114, 118,
                115, 49, 50, 142, 143, 80, };
@@ -737,16 +750,49 @@ static const unsigned int audio_i2s_pins[] = { 118, 115, 116, 117, 112, 113,
                114, };
 static const unsigned int audio_i2s_2ch_pins[] = { 118, 115, 112, 113, 114, };
 static const unsigned int audio_i2s_extclk_pins[] = { 112, };
-static const unsigned int audio_uart0_pins[] = { 143, 142, 141, 144, };
-static const unsigned int audio_uart1_pins[] = { 147, 146, 145, 148, };
-static const unsigned int audio_uart2_pins0[] = { 20, 21, 19, 18, };
-static const unsigned int audio_uart2_pins1[] = { 109, 110, 101, 111, };
-static const unsigned int c_can_trnsvr_pins[] = { 1, };
-static const unsigned int c0_can_pins0[] = { 11, 10, };
-static const unsigned int c0_can_pins1[] = { 2, 3, };
-static const unsigned int c1_can_pins0[] = { 138, 137, };
-static const unsigned int c1_can_pins1[] = { 147, 146, };
-static const unsigned int c1_can_pins2[] = { 2, 3, };
+static const unsigned int audio_spdif_out_pins0[] = { 112, };
+static const unsigned int audio_spdif_out_pins1[] = { 116, };
+static const unsigned int audio_spdif_out_pins2[] = { 142, };
+static const unsigned int audio_uart0_basic_pins[] = { 143, 142, 141, 144, };
+static const unsigned int audio_uart0_urfs_pins0[] = { 117, };
+static const unsigned int audio_uart0_urfs_pins1[] = { 139, };
+static const unsigned int audio_uart0_urfs_pins2[] = { 163, };
+static const unsigned int audio_uart0_urfs_pins3[] = { 162, };
+static const unsigned int audio_uart1_basic_pins[] = { 147, 146, 145, 148, };
+static const unsigned int audio_uart1_urfs_pins0[] = { 117, };
+static const unsigned int audio_uart1_urfs_pins1[] = { 140, };
+static const unsigned int audio_uart1_urfs_pins2[] = { 163, };
+static const unsigned int audio_uart2_urfs_pins0[] = { 139, };
+static const unsigned int audio_uart2_urfs_pins1[] = { 163, };
+static const unsigned int audio_uart2_urfs_pins2[] = { 96, };
+static const unsigned int audio_uart2_urxd_pins0[] = { 20, };
+static const unsigned int audio_uart2_urxd_pins1[] = { 109, };
+static const unsigned int audio_uart2_urxd_pins2[] = { 93, };
+static const unsigned int audio_uart2_usclk_pins0[] = { 19, };
+static const unsigned int audio_uart2_usclk_pins1[] = { 101, };
+static const unsigned int audio_uart2_usclk_pins2[] = { 91, };
+static const unsigned int audio_uart2_utfs_pins0[] = { 18, };
+static const unsigned int audio_uart2_utfs_pins1[] = { 111, };
+static const unsigned int audio_uart2_utfs_pins2[] = { 94, };
+static const unsigned int audio_uart2_utxd_pins0[] = { 21, };
+static const unsigned int audio_uart2_utxd_pins1[] = { 110, };
+static const unsigned int audio_uart2_utxd_pins2[] = { 92, };
+static const unsigned int c_can_trnsvr_en_pins0[] = { 2, };
+static const unsigned int c_can_trnsvr_en_pins1[] = { 0, };
+static const unsigned int c_can_trnsvr_intr_pins[] = { 1, };
+static const unsigned int c_can_trnsvr_stb_n_pins[] = { 3, };
+static const unsigned int c0_can_rxd_trnsv0_pins[] = { 11, };
+static const unsigned int c0_can_rxd_trnsv1_pins[] = { 2, };
+static const unsigned int c0_can_txd_trnsv0_pins[] = { 10, };
+static const unsigned int c0_can_txd_trnsv1_pins[] = { 3, };
+static const unsigned int c1_can_rxd_pins0[] = { 138, };
+static const unsigned int c1_can_rxd_pins1[] = { 147, };
+static const unsigned int c1_can_rxd_pins2[] = { 2, };
+static const unsigned int c1_can_rxd_pins3[] = { 162, };
+static const unsigned int c1_can_txd_pins0[] = { 137, };
+static const unsigned int c1_can_txd_pins1[] = { 146, };
+static const unsigned int c1_can_txd_pins2[] = { 3, };
+static const unsigned int c1_can_txd_pins3[] = { 161, };
 static const unsigned int ca_audio_lpc_pins[] = { 62, 63, 64, 65, 66, 67, 68,
                69, 70, 71, };
 static const unsigned int ca_bt_lpc_pins[] = { 85, 86, 87, 88, 89, 90, };
@@ -804,7 +850,29 @@ static const unsigned int gn_trg_shutdown_pins2[] = { 117, };
 static const unsigned int gn_trg_shutdown_pins3[] = { 123, };
 static const unsigned int i2c0_pins[] = { 128, 127, };
 static const unsigned int i2c1_pins[] = { 126, 125, };
-static const unsigned int jtag_pins0[] = { 125, 4, 2, 0, 1, 3, };
+static const unsigned int i2s0_pins[] = { 91, 93, 94, 92, };
+static const unsigned int i2s1_basic_pins[] = { 95, 96, };
+static const unsigned int i2s1_rxd0_pins0[] = { 61, };
+static const unsigned int i2s1_rxd0_pins1[] = { 131, };
+static const unsigned int i2s1_rxd0_pins2[] = { 129, };
+static const unsigned int i2s1_rxd0_pins3[] = { 117, };
+static const unsigned int i2s1_rxd0_pins4[] = { 83, };
+static const unsigned int i2s1_rxd1_pins0[] = { 72, };
+static const unsigned int i2s1_rxd1_pins1[] = { 132, };
+static const unsigned int i2s1_rxd1_pins2[] = { 130, };
+static const unsigned int i2s1_rxd1_pins3[] = { 118, };
+static const unsigned int i2s1_rxd1_pins4[] = { 84, };
+static const unsigned int jtag_jt_dbg_nsrst_pins[] = { 125, };
+static const unsigned int jtag_ntrst_pins0[] = { 4, };
+static const unsigned int jtag_ntrst_pins1[] = { 163, };
+static const unsigned int jtag_swdiotms_pins0[] = { 2, };
+static const unsigned int jtag_swdiotms_pins1[] = { 160, };
+static const unsigned int jtag_tck_pins0[] = { 0, };
+static const unsigned int jtag_tck_pins1[] = { 161, };
+static const unsigned int jtag_tdi_pins0[] = { 1, };
+static const unsigned int jtag_tdi_pins1[] = { 162, };
+static const unsigned int jtag_tdo_pins0[] = { 3, };
+static const unsigned int jtag_tdo_pins1[] = { 159, };
 static const unsigned int ks_kas_spi_pins0[] = { 141, 144, 143, 142, };
 static const unsigned int ld_ldd_pins[] = { 57, 58, 59, 60, 61, 62, 63, 64,
                65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 80,
@@ -821,7 +889,7 @@ static const unsigned int nd_df_pins[] = { 44, 43, 42, 41, 40, 39, 38, 37,
                47, 46, 52, 51, 45, 49, 50, 48, 124, };
 static const unsigned int nd_df_nowp_pins[] = { 44, 43, 42, 41, 40, 39, 38,
                37, 47, 46, 52, 51, 45, 49, 50, 48, };
-static const unsigned int ps_pins[] = { 120, 119, };
+static const unsigned int ps_pins[] = { 120, 119, 121, };
 static const unsigned int pwc_core_on_pins[] = { 8, };
 static const unsigned int pwc_ext_on_pins[] = { 6, };
 static const unsigned int pwc_gpio3_clk_pins[] = { 3, };
@@ -836,18 +904,26 @@ static const unsigned int pwc_wakeup_src3_pins[] = { 3, };
 static const unsigned int pw_cko0_pins0[] = { 123, };
 static const unsigned int pw_cko0_pins1[] = { 101, };
 static const unsigned int pw_cko0_pins2[] = { 82, };
+static const unsigned int pw_cko0_pins3[] = { 162, };
 static const unsigned int pw_cko1_pins0[] = { 124, };
 static const unsigned int pw_cko1_pins1[] = { 110, };
+static const unsigned int pw_cko1_pins2[] = { 163, };
 static const unsigned int pw_i2s01_clk_pins0[] = { 125, };
 static const unsigned int pw_i2s01_clk_pins1[] = { 117, };
-static const unsigned int pw_pwm0_pins[] = { 119, };
-static const unsigned int pw_pwm1_pins[] = { 120, };
+static const unsigned int pw_i2s01_clk_pins2[] = { 132, };
+static const unsigned int pw_pwm0_pins0[] = { 119, };
+static const unsigned int pw_pwm0_pins1[] = { 159, };
+static const unsigned int pw_pwm1_pins0[] = { 120, };
+static const unsigned int pw_pwm1_pins1[] = { 160, };
+static const unsigned int pw_pwm1_pins2[] = { 131, };
 static const unsigned int pw_pwm2_pins0[] = { 121, };
 static const unsigned int pw_pwm2_pins1[] = { 98, };
+static const unsigned int pw_pwm2_pins2[] = { 161, };
 static const unsigned int pw_pwm3_pins0[] = { 122, };
 static const unsigned int pw_pwm3_pins1[] = { 73, };
 static const unsigned int pw_pwm_cpu_vol_pins0[] = { 121, };
 static const unsigned int pw_pwm_cpu_vol_pins1[] = { 98, };
+static const unsigned int pw_pwm_cpu_vol_pins2[] = { 161, };
 static const unsigned int pw_backlight_pins0[] = { 122, };
 static const unsigned int pw_backlight_pins1[] = { 73, };
 static const unsigned int rg_eth_mac_pins[] = { 108, 103, 104, 105, 106, 107,
@@ -863,8 +939,11 @@ static const unsigned int sd1_pins[] = { 48, 49, 44, 43, 42, 41, 40, 39, 38,
                37, };
 static const unsigned int sd1_4bit_pins0[] = { 48, 49, 44, 43, 42, 41, };
 static const unsigned int sd1_4bit_pins1[] = { 48, 49, 40, 39, 38, 37, };
-static const unsigned int sd2_pins0[] = { 124, 31, 32, 33, 34, 35, 36, 123, };
-static const unsigned int sd2_no_cdb_pins0[] = { 31, 32, 33, 34, 35, 36, 123, };
+static const unsigned int sd2_basic_pins[] = { 31, 32, 33, 34, 35, 36, };
+static const unsigned int sd2_cdb_pins0[] = { 124, };
+static const unsigned int sd2_cdb_pins1[] = { 161, };
+static const unsigned int sd2_wpb_pins0[] = { 123, };
+static const unsigned int sd2_wpb_pins1[] = { 163, };
 static const unsigned int sd3_pins[] = { 85, 86, 87, 88, 89, 90, };
 static const unsigned int sd5_pins[] = { 91, 92, 93, 94, 95, 96, };
 static const unsigned int sd6_pins0[] = { 79, 78, 74, 75, 76, 77, };
@@ -877,19 +956,39 @@ static const unsigned int tpiu_trace_pins[] = { 53, 56, 57, 58, 59, 60, 61,
 static const unsigned int uart0_pins[] = { 121, 120, 134, 133, };
 static const unsigned int uart0_nopause_pins[] = { 134, 133, };
 static const unsigned int uart1_pins[] = { 136, 135, };
-static const unsigned int uart2_pins[] = { 11, 10, };
-static const unsigned int uart3_pins0[] = { 125, 126, 138, 137, };
-static const unsigned int uart3_pins1[] = { 111, 109, 84, 83, };
-static const unsigned int uart3_pins2[] = { 140, 139, 138, 137, };
-static const unsigned int uart3_pins3[] = { 139, 140, 84, 83, };
-static const unsigned int uart3_nopause_pins0[] = { 138, 137, };
-static const unsigned int uart3_nopause_pins1[] = { 84, 83, };
-static const unsigned int uart4_pins0[] = { 122, 123, 140, 139, };
-static const unsigned int uart4_pins1[] = { 100, 99, 140, 139, };
-static const unsigned int uart4_pins2[] = { 117, 116, 140, 139, };
-static const unsigned int uart4_nopause_pins[] = { 140, 139, };
-static const unsigned int usb0_drvvbus_pins[] = { 51, };
-static const unsigned int usb1_drvvbus_pins[] = { 134, };
+static const unsigned int uart2_cts_pins0[] = { 132, };
+static const unsigned int uart2_cts_pins1[] = { 162, };
+static const unsigned int uart2_rts_pins0[] = { 131, };
+static const unsigned int uart2_rts_pins1[] = { 161, };
+static const unsigned int uart2_rxd_pins0[] = { 11, };
+static const unsigned int uart2_rxd_pins1[] = { 160, };
+static const unsigned int uart2_rxd_pins2[] = { 130, };
+static const unsigned int uart2_txd_pins0[] = { 10, };
+static const unsigned int uart2_txd_pins1[] = { 159, };
+static const unsigned int uart2_txd_pins2[] = { 129, };
+static const unsigned int uart3_cts_pins0[] = { 125, };
+static const unsigned int uart3_cts_pins1[] = { 111, };
+static const unsigned int uart3_cts_pins2[] = { 140, };
+static const unsigned int uart3_rts_pins0[] = { 126, };
+static const unsigned int uart3_rts_pins1[] = { 109, };
+static const unsigned int uart3_rts_pins2[] = { 139, };
+static const unsigned int uart3_rxd_pins0[] = { 138, };
+static const unsigned int uart3_rxd_pins1[] = { 84, };
+static const unsigned int uart3_rxd_pins2[] = { 162, };
+static const unsigned int uart3_txd_pins0[] = { 137, };
+static const unsigned int uart3_txd_pins1[] = { 83, };
+static const unsigned int uart3_txd_pins2[] = { 161, };
+static const unsigned int uart4_basic_pins[] = { 140, 139, };
+static const unsigned int uart4_cts_pins0[] = { 122, };
+static const unsigned int uart4_cts_pins1[] = { 100, };
+static const unsigned int uart4_cts_pins2[] = { 117, };
+static const unsigned int uart4_rts_pins0[] = { 123, };
+static const unsigned int uart4_rts_pins1[] = { 99, };
+static const unsigned int uart4_rts_pins2[] = { 116, };
+static const unsigned int usb0_drvvbus_pins0[] = { 51, };
+static const unsigned int usb0_drvvbus_pins1[] = { 162, };
+static const unsigned int usb1_drvvbus_pins0[] = { 134, };
+static const unsigned int usb1_drvvbus_pins1[] = { 163, };
 static const unsigned int visbus_dout_pins[] = { 57, 58, 59, 60, 61, 62, 63,
                64, 65, 66, 67, 68, 69, 70, 71, 72, 53, 54, 55, 56, 85, 86,
                87, 88, 89, 90, 91, 92, 93, 94, 95, 96, };
@@ -910,23 +1009,59 @@ struct atlas7_pin_group altas7_pin_groups[] = {
        GROUP("sdio_i2s_gpio_grp", sdio_i2s_gpio_pins),
        GROUP("sp_rgmii_gpio_grp", sp_rgmii_gpio_pins),
        GROUP("lvds_gpio_grp", lvds_gpio_pins),
-       GROUP("uart_nand_gpio_grp", uart_nand_gpio_pins),
+       GROUP("jtag_uart_nand_gpio_grp", jtag_uart_nand_gpio_pins),
        GROUP("rtc_gpio_grp", rtc_gpio_pins),
        GROUP("audio_ac97_grp", audio_ac97_pins),
+       GROUP("audio_digmic_grp0", audio_digmic_pins0),
+       GROUP("audio_digmic_grp1", audio_digmic_pins1),
+       GROUP("audio_digmic_grp2", audio_digmic_pins2),
        GROUP("audio_func_dbg_grp", audio_func_dbg_pins),
        GROUP("audio_i2s_grp", audio_i2s_pins),
        GROUP("audio_i2s_2ch_grp", audio_i2s_2ch_pins),
        GROUP("audio_i2s_extclk_grp", audio_i2s_extclk_pins),
-       GROUP("audio_uart0_grp", audio_uart0_pins),
-       GROUP("audio_uart1_grp", audio_uart1_pins),
-       GROUP("audio_uart2_grp0", audio_uart2_pins0),
-       GROUP("audio_uart2_grp1", audio_uart2_pins1),
-       GROUP("c_can_trnsvr_grp", c_can_trnsvr_pins),
-       GROUP("c0_can_grp0", c0_can_pins0),
-       GROUP("c0_can_grp1", c0_can_pins1),
-       GROUP("c1_can_grp0", c1_can_pins0),
-       GROUP("c1_can_grp1", c1_can_pins1),
-       GROUP("c1_can_grp2", c1_can_pins2),
+       GROUP("audio_spdif_out_grp0", audio_spdif_out_pins0),
+       GROUP("audio_spdif_out_grp1", audio_spdif_out_pins1),
+       GROUP("audio_spdif_out_grp2", audio_spdif_out_pins2),
+       GROUP("audio_uart0_basic_grp", audio_uart0_basic_pins),
+       GROUP("audio_uart0_urfs_grp0", audio_uart0_urfs_pins0),
+       GROUP("audio_uart0_urfs_grp1", audio_uart0_urfs_pins1),
+       GROUP("audio_uart0_urfs_grp2", audio_uart0_urfs_pins2),
+       GROUP("audio_uart0_urfs_grp3", audio_uart0_urfs_pins3),
+       GROUP("audio_uart1_basic_grp", audio_uart1_basic_pins),
+       GROUP("audio_uart1_urfs_grp0", audio_uart1_urfs_pins0),
+       GROUP("audio_uart1_urfs_grp1", audio_uart1_urfs_pins1),
+       GROUP("audio_uart1_urfs_grp2", audio_uart1_urfs_pins2),
+       GROUP("audio_uart2_urfs_grp0", audio_uart2_urfs_pins0),
+       GROUP("audio_uart2_urfs_grp1", audio_uart2_urfs_pins1),
+       GROUP("audio_uart2_urfs_grp2", audio_uart2_urfs_pins2),
+       GROUP("audio_uart2_urxd_grp0", audio_uart2_urxd_pins0),
+       GROUP("audio_uart2_urxd_grp1", audio_uart2_urxd_pins1),
+       GROUP("audio_uart2_urxd_grp2", audio_uart2_urxd_pins2),
+       GROUP("audio_uart2_usclk_grp0", audio_uart2_usclk_pins0),
+       GROUP("audio_uart2_usclk_grp1", audio_uart2_usclk_pins1),
+       GROUP("audio_uart2_usclk_grp2", audio_uart2_usclk_pins2),
+       GROUP("audio_uart2_utfs_grp0", audio_uart2_utfs_pins0),
+       GROUP("audio_uart2_utfs_grp1", audio_uart2_utfs_pins1),
+       GROUP("audio_uart2_utfs_grp2", audio_uart2_utfs_pins2),
+       GROUP("audio_uart2_utxd_grp0", audio_uart2_utxd_pins0),
+       GROUP("audio_uart2_utxd_grp1", audio_uart2_utxd_pins1),
+       GROUP("audio_uart2_utxd_grp2", audio_uart2_utxd_pins2),
+       GROUP("c_can_trnsvr_en_grp0", c_can_trnsvr_en_pins0),
+       GROUP("c_can_trnsvr_en_grp1", c_can_trnsvr_en_pins1),
+       GROUP("c_can_trnsvr_intr_grp", c_can_trnsvr_intr_pins),
+       GROUP("c_can_trnsvr_stb_n_grp", c_can_trnsvr_stb_n_pins),
+       GROUP("c0_can_rxd_trnsv0_grp", c0_can_rxd_trnsv0_pins),
+       GROUP("c0_can_rxd_trnsv1_grp", c0_can_rxd_trnsv1_pins),
+       GROUP("c0_can_txd_trnsv0_grp", c0_can_txd_trnsv0_pins),
+       GROUP("c0_can_txd_trnsv1_grp", c0_can_txd_trnsv1_pins),
+       GROUP("c1_can_rxd_grp0", c1_can_rxd_pins0),
+       GROUP("c1_can_rxd_grp1", c1_can_rxd_pins1),
+       GROUP("c1_can_rxd_grp2", c1_can_rxd_pins2),
+       GROUP("c1_can_rxd_grp3", c1_can_rxd_pins3),
+       GROUP("c1_can_txd_grp0", c1_can_txd_pins0),
+       GROUP("c1_can_txd_grp1", c1_can_txd_pins1),
+       GROUP("c1_can_txd_grp2", c1_can_txd_pins2),
+       GROUP("c1_can_txd_grp3", c1_can_txd_pins3),
        GROUP("ca_audio_lpc_grp", ca_audio_lpc_pins),
        GROUP("ca_bt_lpc_grp", ca_bt_lpc_pins),
        GROUP("ca_coex_grp", ca_coex_pins),
@@ -977,7 +1112,29 @@ struct atlas7_pin_group altas7_pin_groups[] = {
        GROUP("gn_trg_shutdown_grp3", gn_trg_shutdown_pins3),
        GROUP("i2c0_grp", i2c0_pins),
        GROUP("i2c1_grp", i2c1_pins),
-       GROUP("jtag_grp0", jtag_pins0),
+       GROUP("i2s0_grp", i2s0_pins),
+       GROUP("i2s1_basic_grp", i2s1_basic_pins),
+       GROUP("i2s1_rxd0_grp0", i2s1_rxd0_pins0),
+       GROUP("i2s1_rxd0_grp1", i2s1_rxd0_pins1),
+       GROUP("i2s1_rxd0_grp2", i2s1_rxd0_pins2),
+       GROUP("i2s1_rxd0_grp3", i2s1_rxd0_pins3),
+       GROUP("i2s1_rxd0_grp4", i2s1_rxd0_pins4),
+       GROUP("i2s1_rxd1_grp0", i2s1_rxd1_pins0),
+       GROUP("i2s1_rxd1_grp1", i2s1_rxd1_pins1),
+       GROUP("i2s1_rxd1_grp2", i2s1_rxd1_pins2),
+       GROUP("i2s1_rxd1_grp3", i2s1_rxd1_pins3),
+       GROUP("i2s1_rxd1_grp4", i2s1_rxd1_pins4),
+       GROUP("jtag_jt_dbg_nsrst_grp", jtag_jt_dbg_nsrst_pins),
+       GROUP("jtag_ntrst_grp0", jtag_ntrst_pins0),
+       GROUP("jtag_ntrst_grp1", jtag_ntrst_pins1),
+       GROUP("jtag_swdiotms_grp0", jtag_swdiotms_pins0),
+       GROUP("jtag_swdiotms_grp1", jtag_swdiotms_pins1),
+       GROUP("jtag_tck_grp0", jtag_tck_pins0),
+       GROUP("jtag_tck_grp1", jtag_tck_pins1),
+       GROUP("jtag_tdi_grp0", jtag_tdi_pins0),
+       GROUP("jtag_tdi_grp1", jtag_tdi_pins1),
+       GROUP("jtag_tdo_grp0", jtag_tdo_pins0),
+       GROUP("jtag_tdo_grp1", jtag_tdo_pins1),
        GROUP("ks_kas_spi_grp0", ks_kas_spi_pins0),
        GROUP("ld_ldd_grp", ld_ldd_pins),
        GROUP("ld_ldd_16bit_grp", ld_ldd_16bit_pins),
@@ -1002,18 +1159,26 @@ struct atlas7_pin_group altas7_pin_groups[] = {
        GROUP("pw_cko0_grp0", pw_cko0_pins0),
        GROUP("pw_cko0_grp1", pw_cko0_pins1),
        GROUP("pw_cko0_grp2", pw_cko0_pins2),
+       GROUP("pw_cko0_grp3", pw_cko0_pins3),
        GROUP("pw_cko1_grp0", pw_cko1_pins0),
        GROUP("pw_cko1_grp1", pw_cko1_pins1),
+       GROUP("pw_cko1_grp2", pw_cko1_pins2),
        GROUP("pw_i2s01_clk_grp0", pw_i2s01_clk_pins0),
        GROUP("pw_i2s01_clk_grp1", pw_i2s01_clk_pins1),
-       GROUP("pw_pwm0_grp", pw_pwm0_pins),
-       GROUP("pw_pwm1_grp", pw_pwm1_pins),
+       GROUP("pw_i2s01_clk_grp2", pw_i2s01_clk_pins2),
+       GROUP("pw_pwm0_grp0", pw_pwm0_pins0),
+       GROUP("pw_pwm0_grp1", pw_pwm0_pins1),
+       GROUP("pw_pwm1_grp0", pw_pwm1_pins0),
+       GROUP("pw_pwm1_grp1", pw_pwm1_pins1),
+       GROUP("pw_pwm1_grp2", pw_pwm1_pins2),
        GROUP("pw_pwm2_grp0", pw_pwm2_pins0),
        GROUP("pw_pwm2_grp1", pw_pwm2_pins1),
+       GROUP("pw_pwm2_grp2", pw_pwm2_pins2),
        GROUP("pw_pwm3_grp0", pw_pwm3_pins0),
        GROUP("pw_pwm3_grp1", pw_pwm3_pins1),
        GROUP("pw_pwm_cpu_vol_grp0", pw_pwm_cpu_vol_pins0),
        GROUP("pw_pwm_cpu_vol_grp1", pw_pwm_cpu_vol_pins1),
+       GROUP("pw_pwm_cpu_vol_grp2", pw_pwm_cpu_vol_pins2),
        GROUP("pw_backlight_grp0", pw_backlight_pins0),
        GROUP("pw_backlight_grp1", pw_backlight_pins1),
        GROUP("rg_eth_mac_grp", rg_eth_mac_pins),
@@ -1026,8 +1191,11 @@ struct atlas7_pin_group altas7_pin_groups[] = {
        GROUP("sd1_grp", sd1_pins),
        GROUP("sd1_4bit_grp0", sd1_4bit_pins0),
        GROUP("sd1_4bit_grp1", sd1_4bit_pins1),
-       GROUP("sd2_grp0", sd2_pins0),
-       GROUP("sd2_no_cdb_grp0", sd2_no_cdb_pins0),
+       GROUP("sd2_basic_grp", sd2_basic_pins),
+       GROUP("sd2_cdb_grp0", sd2_cdb_pins0),
+       GROUP("sd2_cdb_grp1", sd2_cdb_pins1),
+       GROUP("sd2_wpb_grp0", sd2_wpb_pins0),
+       GROUP("sd2_wpb_grp1", sd2_wpb_pins1),
        GROUP("sd3_grp", sd3_pins),
        GROUP("sd5_grp", sd5_pins),
        GROUP("sd6_grp0", sd6_pins0),
@@ -1039,19 +1207,39 @@ struct atlas7_pin_group altas7_pin_groups[] = {
        GROUP("uart0_grp", uart0_pins),
        GROUP("uart0_nopause_grp", uart0_nopause_pins),
        GROUP("uart1_grp", uart1_pins),
-       GROUP("uart2_grp", uart2_pins),
-       GROUP("uart3_grp0", uart3_pins0),
-       GROUP("uart3_grp1", uart3_pins1),
-       GROUP("uart3_grp2", uart3_pins2),
-       GROUP("uart3_grp3", uart3_pins3),
-       GROUP("uart3_nopause_grp0", uart3_nopause_pins0),
-       GROUP("uart3_nopause_grp1", uart3_nopause_pins1),
-       GROUP("uart4_grp0", uart4_pins0),
-       GROUP("uart4_grp1", uart4_pins1),
-       GROUP("uart4_grp2", uart4_pins2),
-       GROUP("uart4_nopause_grp", uart4_nopause_pins),
-       GROUP("usb0_drvvbus_grp", usb0_drvvbus_pins),
-       GROUP("usb1_drvvbus_grp", usb1_drvvbus_pins),
+       GROUP("uart2_cts_grp0", uart2_cts_pins0),
+       GROUP("uart2_cts_grp1", uart2_cts_pins1),
+       GROUP("uart2_rts_grp0", uart2_rts_pins0),
+       GROUP("uart2_rts_grp1", uart2_rts_pins1),
+       GROUP("uart2_rxd_grp0", uart2_rxd_pins0),
+       GROUP("uart2_rxd_grp1", uart2_rxd_pins1),
+       GROUP("uart2_rxd_grp2", uart2_rxd_pins2),
+       GROUP("uart2_txd_grp0", uart2_txd_pins0),
+       GROUP("uart2_txd_grp1", uart2_txd_pins1),
+       GROUP("uart2_txd_grp2", uart2_txd_pins2),
+       GROUP("uart3_cts_grp0", uart3_cts_pins0),
+       GROUP("uart3_cts_grp1", uart3_cts_pins1),
+       GROUP("uart3_cts_grp2", uart3_cts_pins2),
+       GROUP("uart3_rts_grp0", uart3_rts_pins0),
+       GROUP("uart3_rts_grp1", uart3_rts_pins1),
+       GROUP("uart3_rts_grp2", uart3_rts_pins2),
+       GROUP("uart3_rxd_grp0", uart3_rxd_pins0),
+       GROUP("uart3_rxd_grp1", uart3_rxd_pins1),
+       GROUP("uart3_rxd_grp2", uart3_rxd_pins2),
+       GROUP("uart3_txd_grp0", uart3_txd_pins0),
+       GROUP("uart3_txd_grp1", uart3_txd_pins1),
+       GROUP("uart3_txd_grp2", uart3_txd_pins2),
+       GROUP("uart4_basic_grp", uart4_basic_pins),
+       GROUP("uart4_cts_grp0", uart4_cts_pins0),
+       GROUP("uart4_cts_grp1", uart4_cts_pins1),
+       GROUP("uart4_cts_grp2", uart4_cts_pins2),
+       GROUP("uart4_rts_grp0", uart4_rts_pins0),
+       GROUP("uart4_rts_grp1", uart4_rts_pins1),
+       GROUP("uart4_rts_grp2", uart4_rts_pins2),
+       GROUP("usb0_drvvbus_grp0", usb0_drvvbus_pins0),
+       GROUP("usb0_drvvbus_grp1", usb0_drvvbus_pins1),
+       GROUP("usb1_drvvbus_grp0", usb1_drvvbus_pins0),
+       GROUP("usb1_drvvbus_grp1", usb1_drvvbus_pins1),
        GROUP("visbus_dout_grp", visbus_dout_pins),
        GROUP("vi_vip1_grp", vi_vip1_pins),
        GROUP("vi_vip1_ext_grp", vi_vip1_ext_pins),
@@ -1065,23 +1253,90 @@ static const char * const lcd_vip_gpio_grp[] = { "lcd_vip_gpio_grp", };
 static const char * const sdio_i2s_gpio_grp[] = { "sdio_i2s_gpio_grp", };
 static const char * const sp_rgmii_gpio_grp[] = { "sp_rgmii_gpio_grp", };
 static const char * const lvds_gpio_grp[] = { "lvds_gpio_grp", };
-static const char * const uart_nand_gpio_grp[] = { "uart_nand_gpio_grp", };
+static const char * const jtag_uart_nand_gpio_grp[] = {
+                               "jtag_uart_nand_gpio_grp", };
 static const char * const rtc_gpio_grp[] = { "rtc_gpio_grp", };
 static const char * const audio_ac97_grp[] = { "audio_ac97_grp", };
+static const char * const audio_digmic_grp0[] = { "audio_digmic_grp0", };
+static const char * const audio_digmic_grp1[] = { "audio_digmic_grp1", };
+static const char * const audio_digmic_grp2[] = { "audio_digmic_grp2", };
 static const char * const audio_func_dbg_grp[] = { "audio_func_dbg_grp", };
 static const char * const audio_i2s_grp[] = { "audio_i2s_grp", };
 static const char * const audio_i2s_2ch_grp[] = { "audio_i2s_2ch_grp", };
 static const char * const audio_i2s_extclk_grp[] = { "audio_i2s_extclk_grp", };
-static const char * const audio_uart0_grp[] = { "audio_uart0_grp", };
-static const char * const audio_uart1_grp[] = { "audio_uart1_grp", };
-static const char * const audio_uart2_grp0[] = { "audio_uart2_grp0", };
-static const char * const audio_uart2_grp1[] = { "audio_uart2_grp1", };
-static const char * const c_can_trnsvr_grp[] = { "c_can_trnsvr_grp", };
-static const char * const c0_can_grp0[] = { "c0_can_grp0", };
-static const char * const c0_can_grp1[] = { "c0_can_grp1", };
-static const char * const c1_can_grp0[] = { "c1_can_grp0", };
-static const char * const c1_can_grp1[] = { "c1_can_grp1", };
-static const char * const c1_can_grp2[] = { "c1_can_grp2", };
+static const char * const audio_spdif_out_grp0[] = { "audio_spdif_out_grp0", };
+static const char * const audio_spdif_out_grp1[] = { "audio_spdif_out_grp1", };
+static const char * const audio_spdif_out_grp2[] = { "audio_spdif_out_grp2", };
+static const char * const audio_uart0_basic_grp[] = {
+                               "audio_uart0_basic_grp", };
+static const char * const audio_uart0_urfs_grp0[] = {
+                               "audio_uart0_urfs_grp0", };
+static const char * const audio_uart0_urfs_grp1[] = {
+                               "audio_uart0_urfs_grp1", };
+static const char * const audio_uart0_urfs_grp2[] = {
+                               "audio_uart0_urfs_grp2", };
+static const char * const audio_uart0_urfs_grp3[] = {
+                               "audio_uart0_urfs_grp3", };
+static const char * const audio_uart1_basic_grp[] = {
+                               "audio_uart1_basic_grp", };
+static const char * const audio_uart1_urfs_grp0[] = {
+                               "audio_uart1_urfs_grp0", };
+static const char * const audio_uart1_urfs_grp1[] = {
+                               "audio_uart1_urfs_grp1", };
+static const char * const audio_uart1_urfs_grp2[] = {
+                               "audio_uart1_urfs_grp2", };
+static const char * const audio_uart2_urfs_grp0[] = {
+                               "audio_uart2_urfs_grp0", };
+static const char * const audio_uart2_urfs_grp1[] = {
+                               "audio_uart2_urfs_grp1", };
+static const char * const audio_uart2_urfs_grp2[] = {
+                               "audio_uart2_urfs_grp2", };
+static const char * const audio_uart2_urxd_grp0[] = {
+                               "audio_uart2_urxd_grp0", };
+static const char * const audio_uart2_urxd_grp1[] = {
+                               "audio_uart2_urxd_grp1", };
+static const char * const audio_uart2_urxd_grp2[] = {
+                               "audio_uart2_urxd_grp2", };
+static const char * const audio_uart2_usclk_grp0[] = {
+                               "audio_uart2_usclk_grp0", };
+static const char * const audio_uart2_usclk_grp1[] = {
+                               "audio_uart2_usclk_grp1", };
+static const char * const audio_uart2_usclk_grp2[] = {
+                               "audio_uart2_usclk_grp2", };
+static const char * const audio_uart2_utfs_grp0[] = {
+                               "audio_uart2_utfs_grp0", };
+static const char * const audio_uart2_utfs_grp1[] = {
+                               "audio_uart2_utfs_grp1", };
+static const char * const audio_uart2_utfs_grp2[] = {
+                               "audio_uart2_utfs_grp2", };
+static const char * const audio_uart2_utxd_grp0[] = {
+                               "audio_uart2_utxd_grp0", };
+static const char * const audio_uart2_utxd_grp1[] = {
+                               "audio_uart2_utxd_grp1", };
+static const char * const audio_uart2_utxd_grp2[] = {
+                               "audio_uart2_utxd_grp2", };
+static const char * const c_can_trnsvr_en_grp0[] = { "c_can_trnsvr_en_grp0", };
+static const char * const c_can_trnsvr_en_grp1[] = { "c_can_trnsvr_en_grp1", };
+static const char * const c_can_trnsvr_intr_grp[] = {
+                               "c_can_trnsvr_intr_grp", };
+static const char * const c_can_trnsvr_stb_n_grp[] = {
+                               "c_can_trnsvr_stb_n_grp", };
+static const char * const c0_can_rxd_trnsv0_grp[] = {
+                               "c0_can_rxd_trnsv0_grp", };
+static const char * const c0_can_rxd_trnsv1_grp[] = {
+                               "c0_can_rxd_trnsv1_grp", };
+static const char * const c0_can_txd_trnsv0_grp[] = {
+                               "c0_can_txd_trnsv0_grp", };
+static const char * const c0_can_txd_trnsv1_grp[] = {
+                               "c0_can_txd_trnsv1_grp", };
+static const char * const c1_can_rxd_grp0[] = { "c1_can_rxd_grp0", };
+static const char * const c1_can_rxd_grp1[] = { "c1_can_rxd_grp1", };
+static const char * const c1_can_rxd_grp2[] = { "c1_can_rxd_grp2", };
+static const char * const c1_can_rxd_grp3[] = { "c1_can_rxd_grp3", };
+static const char * const c1_can_txd_grp0[] = { "c1_can_txd_grp0", };
+static const char * const c1_can_txd_grp1[] = { "c1_can_txd_grp1", };
+static const char * const c1_can_txd_grp2[] = { "c1_can_txd_grp2", };
+static const char * const c1_can_txd_grp3[] = { "c1_can_txd_grp3", };
 static const char * const ca_audio_lpc_grp[] = { "ca_audio_lpc_grp", };
 static const char * const ca_bt_lpc_grp[] = { "ca_bt_lpc_grp", };
 static const char * const ca_coex_grp[] = { "ca_coex_grp", };
@@ -1135,7 +1390,30 @@ static const char * const gn_trg_shutdown_grp2[] = { "gn_trg_shutdown_grp2", };
 static const char * const gn_trg_shutdown_grp3[] = { "gn_trg_shutdown_grp3", };
 static const char * const i2c0_grp[] = { "i2c0_grp", };
 static const char * const i2c1_grp[] = { "i2c1_grp", };
-static const char * const jtag_grp0[] = { "jtag_grp0", };
+static const char * const i2s0_grp[] = { "i2s0_grp", };
+static const char * const i2s1_basic_grp[] = { "i2s1_basic_grp", };
+static const char * const i2s1_rxd0_grp0[] = { "i2s1_rxd0_grp0", };
+static const char * const i2s1_rxd0_grp1[] = { "i2s1_rxd0_grp1", };
+static const char * const i2s1_rxd0_grp2[] = { "i2s1_rxd0_grp2", };
+static const char * const i2s1_rxd0_grp3[] = { "i2s1_rxd0_grp3", };
+static const char * const i2s1_rxd0_grp4[] = { "i2s1_rxd0_grp4", };
+static const char * const i2s1_rxd1_grp0[] = { "i2s1_rxd1_grp0", };
+static const char * const i2s1_rxd1_grp1[] = { "i2s1_rxd1_grp1", };
+static const char * const i2s1_rxd1_grp2[] = { "i2s1_rxd1_grp2", };
+static const char * const i2s1_rxd1_grp3[] = { "i2s1_rxd1_grp3", };
+static const char * const i2s1_rxd1_grp4[] = { "i2s1_rxd1_grp4", };
+static const char * const jtag_jt_dbg_nsrst_grp[] = {
+                               "jtag_jt_dbg_nsrst_grp", };
+static const char * const jtag_ntrst_grp0[] = { "jtag_ntrst_grp0", };
+static const char * const jtag_ntrst_grp1[] = { "jtag_ntrst_grp1", };
+static const char * const jtag_swdiotms_grp0[] = { "jtag_swdiotms_grp0", };
+static const char * const jtag_swdiotms_grp1[] = { "jtag_swdiotms_grp1", };
+static const char * const jtag_tck_grp0[] = { "jtag_tck_grp0", };
+static const char * const jtag_tck_grp1[] = { "jtag_tck_grp1", };
+static const char * const jtag_tdi_grp0[] = { "jtag_tdi_grp0", };
+static const char * const jtag_tdi_grp1[] = { "jtag_tdi_grp1", };
+static const char * const jtag_tdo_grp0[] = { "jtag_tdo_grp0", };
+static const char * const jtag_tdo_grp1[] = { "jtag_tdo_grp1", };
 static const char * const ks_kas_spi_grp0[] = { "ks_kas_spi_grp0", };
 static const char * const ld_ldd_grp[] = { "ld_ldd_grp", };
 static const char * const ld_ldd_16bit_grp[] = { "ld_ldd_16bit_grp", };
@@ -1160,18 +1438,26 @@ static const char * const pwc_wakeup_src3_grp[] = { "pwc_wakeup_src3_grp", };
 static const char * const pw_cko0_grp0[] = { "pw_cko0_grp0", };
 static const char * const pw_cko0_grp1[] = { "pw_cko0_grp1", };
 static const char * const pw_cko0_grp2[] = { "pw_cko0_grp2", };
+static const char * const pw_cko0_grp3[] = { "pw_cko0_grp3", };
 static const char * const pw_cko1_grp0[] = { "pw_cko1_grp0", };
 static const char * const pw_cko1_grp1[] = { "pw_cko1_grp1", };
+static const char * const pw_cko1_grp2[] = { "pw_cko1_grp2", };
 static const char * const pw_i2s01_clk_grp0[] = { "pw_i2s01_clk_grp0", };
 static const char * const pw_i2s01_clk_grp1[] = { "pw_i2s01_clk_grp1", };
-static const char * const pw_pwm0_grp[] = { "pw_pwm0_grp", };
-static const char * const pw_pwm1_grp[] = { "pw_pwm1_grp", };
+static const char * const pw_i2s01_clk_grp2[] = { "pw_i2s01_clk_grp2", };
+static const char * const pw_pwm0_grp0[] = { "pw_pwm0_grp0", };
+static const char * const pw_pwm0_grp1[] = { "pw_pwm0_grp1", };
+static const char * const pw_pwm1_grp0[] = { "pw_pwm1_grp0", };
+static const char * const pw_pwm1_grp1[] = { "pw_pwm1_grp1", };
+static const char * const pw_pwm1_grp2[] = { "pw_pwm1_grp2", };
 static const char * const pw_pwm2_grp0[] = { "pw_pwm2_grp0", };
 static const char * const pw_pwm2_grp1[] = { "pw_pwm2_grp1", };
+static const char * const pw_pwm2_grp2[] = { "pw_pwm2_grp2", };
 static const char * const pw_pwm3_grp0[] = { "pw_pwm3_grp0", };
 static const char * const pw_pwm3_grp1[] = { "pw_pwm3_grp1", };
 static const char * const pw_pwm_cpu_vol_grp0[] = { "pw_pwm_cpu_vol_grp0", };
 static const char * const pw_pwm_cpu_vol_grp1[] = { "pw_pwm_cpu_vol_grp1", };
+static const char * const pw_pwm_cpu_vol_grp2[] = { "pw_pwm_cpu_vol_grp2", };
 static const char * const pw_backlight_grp0[] = { "pw_backlight_grp0", };
 static const char * const pw_backlight_grp1[] = { "pw_backlight_grp1", };
 static const char * const rg_eth_mac_grp[] = { "rg_eth_mac_grp", };
@@ -1187,8 +1473,11 @@ static const char * const sd0_4bit_grp[] = { "sd0_4bit_grp", };
 static const char * const sd1_grp[] = { "sd1_grp", };
 static const char * const sd1_4bit_grp0[] = { "sd1_4bit_grp0", };
 static const char * const sd1_4bit_grp1[] = { "sd1_4bit_grp1", };
-static const char * const sd2_grp0[] = { "sd2_grp0", };
-static const char * const sd2_no_cdb_grp0[] = { "sd2_no_cdb_grp0", };
+static const char * const sd2_basic_grp[] = { "sd2_basic_grp", };
+static const char * const sd2_cdb_grp0[] = { "sd2_cdb_grp0", };
+static const char * const sd2_cdb_grp1[] = { "sd2_cdb_grp1", };
+static const char * const sd2_wpb_grp0[] = { "sd2_wpb_grp0", };
+static const char * const sd2_wpb_grp1[] = { "sd2_wpb_grp1", };
 static const char * const sd3_grp[] = { "sd3_grp", };
 static const char * const sd5_grp[] = { "sd5_grp", };
 static const char * const sd6_grp0[] = { "sd6_grp0", };
@@ -1200,19 +1489,39 @@ static const char * const tpiu_trace_grp[] = { "tpiu_trace_grp", };
 static const char * const uart0_grp[] = { "uart0_grp", };
 static const char * const uart0_nopause_grp[] = { "uart0_nopause_grp", };
 static const char * const uart1_grp[] = { "uart1_grp", };
-static const char * const uart2_grp[] = { "uart2_grp", };
-static const char * const uart3_grp0[] = { "uart3_grp0", };
-static const char * const uart3_grp1[] = { "uart3_grp1", };
-static const char * const uart3_grp2[] = { "uart3_grp2", };
-static const char * const uart3_grp3[] = { "uart3_grp3", };
-static const char * const uart3_nopause_grp0[] = { "uart3_nopause_grp0", };
-static const char * const uart3_nopause_grp1[] = { "uart3_nopause_grp1", };
-static const char * const uart4_grp0[] = { "uart4_grp0", };
-static const char * const uart4_grp1[] = { "uart4_grp1", };
-static const char * const uart4_grp2[] = { "uart4_grp2", };
-static const char * const uart4_nopause_grp[] = { "uart4_nopause_grp", };
-static const char * const usb0_drvvbus_grp[] = { "usb0_drvvbus_grp", };
-static const char * const usb1_drvvbus_grp[] = { "usb1_drvvbus_grp", };
+static const char * const uart2_cts_grp0[] = { "uart2_cts_grp0", };
+static const char * const uart2_cts_grp1[] = { "uart2_cts_grp1", };
+static const char * const uart2_rts_grp0[] = { "uart2_rts_grp0", };
+static const char * const uart2_rts_grp1[] = { "uart2_rts_grp1", };
+static const char * const uart2_rxd_grp0[] = { "uart2_rxd_grp0", };
+static const char * const uart2_rxd_grp1[] = { "uart2_rxd_grp1", };
+static const char * const uart2_rxd_grp2[] = { "uart2_rxd_grp2", };
+static const char * const uart2_txd_grp0[] = { "uart2_txd_grp0", };
+static const char * const uart2_txd_grp1[] = { "uart2_txd_grp1", };
+static const char * const uart2_txd_grp2[] = { "uart2_txd_grp2", };
+static const char * const uart3_cts_grp0[] = { "uart3_cts_grp0", };
+static const char * const uart3_cts_grp1[] = { "uart3_cts_grp1", };
+static const char * const uart3_cts_grp2[] = { "uart3_cts_grp2", };
+static const char * const uart3_rts_grp0[] = { "uart3_rts_grp0", };
+static const char * const uart3_rts_grp1[] = { "uart3_rts_grp1", };
+static const char * const uart3_rts_grp2[] = { "uart3_rts_grp2", };
+static const char * const uart3_rxd_grp0[] = { "uart3_rxd_grp0", };
+static const char * const uart3_rxd_grp1[] = { "uart3_rxd_grp1", };
+static const char * const uart3_rxd_grp2[] = { "uart3_rxd_grp2", };
+static const char * const uart3_txd_grp0[] = { "uart3_txd_grp0", };
+static const char * const uart3_txd_grp1[] = { "uart3_txd_grp1", };
+static const char * const uart3_txd_grp2[] = { "uart3_txd_grp2", };
+static const char * const uart4_basic_grp[] = { "uart4_basic_grp", };
+static const char * const uart4_cts_grp0[] = { "uart4_cts_grp0", };
+static const char * const uart4_cts_grp1[] = { "uart4_cts_grp1", };
+static const char * const uart4_cts_grp2[] = { "uart4_cts_grp2", };
+static const char * const uart4_rts_grp0[] = { "uart4_rts_grp0", };
+static const char * const uart4_rts_grp1[] = { "uart4_rts_grp1", };
+static const char * const uart4_rts_grp2[] = { "uart4_rts_grp2", };
+static const char * const usb0_drvvbus_grp0[] = { "usb0_drvvbus_grp0", };
+static const char * const usb0_drvvbus_grp1[] = { "usb0_drvvbus_grp1", };
+static const char * const usb1_drvvbus_grp0[] = { "usb1_drvvbus_grp0", };
+static const char * const usb1_drvvbus_grp1[] = { "usb1_drvvbus_grp1", };
 static const char * const visbus_dout_grp[] = { "visbus_dout_grp", };
 static const char * const vi_vip1_grp[] = { "vi_vip1_grp", };
 static const char * const vi_vip1_ext_grp[] = { "vi_vip1_ext_grp", };
@@ -1376,7 +1685,7 @@ static struct atlas7_grp_mux lvds_gpio_grp_mux = {
        .pad_mux_list = lvds_gpio_grp_pad_mux,
 };
 
-static struct atlas7_pad_mux uart_nand_gpio_grp_pad_mux[] = {
+static struct atlas7_pad_mux jtag_uart_nand_gpio_grp_pad_mux[] = {
        MUX(1, 44, 0, N, N, N, N),
        MUX(1, 43, 0, N, N, N, N),
        MUX(1, 42, 0, N, N, N, N),
@@ -1401,11 +1710,16 @@ static struct atlas7_pad_mux uart_nand_gpio_grp_pad_mux[] = {
        MUX(1, 138, 0, N, N, N, N),
        MUX(1, 139, 0, N, N, N, N),
        MUX(1, 140, 0, N, N, N, N),
+       MUX(1, 159, 0, N, N, N, N),
+       MUX(1, 160, 0, N, N, N, N),
+       MUX(1, 161, 0, N, N, N, N),
+       MUX(1, 162, 0, N, N, N, N),
+       MUX(1, 163, 0, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart_nand_gpio_grp_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart_nand_gpio_grp_pad_mux),
-       .pad_mux_list = uart_nand_gpio_grp_pad_mux,
+static struct atlas7_grp_mux jtag_uart_nand_gpio_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_uart_nand_gpio_grp_pad_mux),
+       .pad_mux_list = jtag_uart_nand_gpio_grp_pad_mux,
 };
 
 static struct atlas7_pad_mux rtc_gpio_grp_pad_mux[] = {
@@ -1422,6 +1736,7 @@ static struct atlas7_pad_mux rtc_gpio_grp_pad_mux[] = {
        MUX(0, 15, 0, N, N, N, N),
        MUX(0, 16, 0, N, N, N, N),
        MUX(0, 17, 0, N, N, N, N),
+       MUX(0, 9, 0, N, N, N, N),
 };
 
 static struct atlas7_grp_mux rtc_gpio_grp_mux = {
@@ -1441,6 +1756,33 @@ static struct atlas7_grp_mux audio_ac97_grp_mux = {
        .pad_mux_list = audio_ac97_grp_pad_mux,
 };
 
+static struct atlas7_pad_mux audio_digmic_grp0_pad_mux[] = {
+       MUX(1, 51, 3, 0xa10, 20, 0xa90, 20),
+};
+
+static struct atlas7_grp_mux audio_digmic_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_digmic_grp0_pad_mux),
+       .pad_mux_list = audio_digmic_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_digmic_grp1_pad_mux[] = {
+       MUX(1, 122, 5, 0xa10, 20, 0xa90, 20),
+};
+
+static struct atlas7_grp_mux audio_digmic_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_digmic_grp1_pad_mux),
+       .pad_mux_list = audio_digmic_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_digmic_grp2_pad_mux[] = {
+       MUX(1, 161, 7, 0xa10, 20, 0xa90, 20),
+};
+
+static struct atlas7_grp_mux audio_digmic_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_digmic_grp2_pad_mux),
+       .pad_mux_list = audio_digmic_grp2_pad_mux,
+};
+
 static struct atlas7_pad_mux audio_func_dbg_grp_pad_mux[] = {
        MUX(1, 141, 4, N, N, N, N),
        MUX(1, 144, 4, N, N, N, N),
@@ -1512,111 +1854,397 @@ static struct atlas7_grp_mux audio_i2s_extclk_grp_mux = {
        .pad_mux_list = audio_i2s_extclk_grp_pad_mux,
 };
 
-static struct atlas7_pad_mux audio_uart0_grp_pad_mux[] = {
+static struct atlas7_pad_mux audio_spdif_out_grp0_pad_mux[] = {
+       MUX(1, 112, 3, N, N, N, N),
+};
+
+static struct atlas7_grp_mux audio_spdif_out_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_spdif_out_grp0_pad_mux),
+       .pad_mux_list = audio_spdif_out_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_spdif_out_grp1_pad_mux[] = {
+       MUX(1, 116, 3, N, N, N, N),
+};
+
+static struct atlas7_grp_mux audio_spdif_out_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_spdif_out_grp1_pad_mux),
+       .pad_mux_list = audio_spdif_out_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_spdif_out_grp2_pad_mux[] = {
+       MUX(1, 142, 3, N, N, N, N),
+};
+
+static struct atlas7_grp_mux audio_spdif_out_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_spdif_out_grp2_pad_mux),
+       .pad_mux_list = audio_spdif_out_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart0_basic_grp_pad_mux[] = {
        MUX(1, 143, 1, N, N, N, N),
        MUX(1, 142, 1, N, N, N, N),
        MUX(1, 141, 1, N, N, N, N),
        MUX(1, 144, 1, N, N, N, N),
 };
 
-static struct atlas7_grp_mux audio_uart0_grp_mux = {
-       .pad_mux_count = ARRAY_SIZE(audio_uart0_grp_pad_mux),
-       .pad_mux_list = audio_uart0_grp_pad_mux,
+static struct atlas7_grp_mux audio_uart0_basic_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart0_basic_grp_pad_mux),
+       .pad_mux_list = audio_uart0_basic_grp_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart0_urfs_grp0_pad_mux[] = {
+       MUX(1, 117, 5, 0xa10, 28, 0xa90, 28),
 };
 
-static struct atlas7_pad_mux audio_uart1_grp_pad_mux[] = {
-       MUX(1, 147, 1, N, N, N, N),
-       MUX(1, 146, 1, N, N, N, N),
-       MUX(1, 145, 1, N, N, N, N),
-       MUX(1, 148, 1, N, N, N, N),
+static struct atlas7_grp_mux audio_uart0_urfs_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart0_urfs_grp0_pad_mux),
+       .pad_mux_list = audio_uart0_urfs_grp0_pad_mux,
 };
 
-static struct atlas7_grp_mux audio_uart1_grp_mux = {
-       .pad_mux_count = ARRAY_SIZE(audio_uart1_grp_pad_mux),
-       .pad_mux_list = audio_uart1_grp_pad_mux,
+static struct atlas7_pad_mux audio_uart0_urfs_grp1_pad_mux[] = {
+       MUX(1, 139, 3, 0xa10, 28, 0xa90, 28),
 };
 
-static struct atlas7_pad_mux audio_uart2_grp0_pad_mux[] = {
+static struct atlas7_grp_mux audio_uart0_urfs_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart0_urfs_grp1_pad_mux),
+       .pad_mux_list = audio_uart0_urfs_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart0_urfs_grp2_pad_mux[] = {
+       MUX(1, 163, 3, 0xa10, 28, 0xa90, 28),
+};
+
+static struct atlas7_grp_mux audio_uart0_urfs_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart0_urfs_grp2_pad_mux),
+       .pad_mux_list = audio_uart0_urfs_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart0_urfs_grp3_pad_mux[] = {
+       MUX(1, 162, 6, 0xa10, 28, 0xa90, 28),
+};
+
+static struct atlas7_grp_mux audio_uart0_urfs_grp3_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart0_urfs_grp3_pad_mux),
+       .pad_mux_list = audio_uart0_urfs_grp3_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart1_basic_grp_pad_mux[] = {
+       MUX(1, 147, 1, 0xa10, 24, 0xa90, 24),
+       MUX(1, 146, 1, 0xa10, 25, 0xa90, 25),
+       MUX(1, 145, 1, 0xa10, 23, 0xa90, 23),
+       MUX(1, 148, 1, 0xa10, 22, 0xa90, 22),
+};
+
+static struct atlas7_grp_mux audio_uart1_basic_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart1_basic_grp_pad_mux),
+       .pad_mux_list = audio_uart1_basic_grp_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart1_urfs_grp0_pad_mux[] = {
+       MUX(1, 117, 6, 0xa10, 29, 0xa90, 29),
+};
+
+static struct atlas7_grp_mux audio_uart1_urfs_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart1_urfs_grp0_pad_mux),
+       .pad_mux_list = audio_uart1_urfs_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart1_urfs_grp1_pad_mux[] = {
+       MUX(1, 140, 3, 0xa10, 29, 0xa90, 29),
+};
+
+static struct atlas7_grp_mux audio_uart1_urfs_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart1_urfs_grp1_pad_mux),
+       .pad_mux_list = audio_uart1_urfs_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart1_urfs_grp2_pad_mux[] = {
+       MUX(1, 163, 4, 0xa10, 29, 0xa90, 29),
+};
+
+static struct atlas7_grp_mux audio_uart1_urfs_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart1_urfs_grp2_pad_mux),
+       .pad_mux_list = audio_uart1_urfs_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_urfs_grp0_pad_mux[] = {
+       MUX(1, 139, 4, 0xa10, 30, 0xa90, 30),
+};
+
+static struct atlas7_grp_mux audio_uart2_urfs_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_urfs_grp0_pad_mux),
+       .pad_mux_list = audio_uart2_urfs_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_urfs_grp1_pad_mux[] = {
+       MUX(1, 163, 6, 0xa10, 30, 0xa90, 30),
+};
+
+static struct atlas7_grp_mux audio_uart2_urfs_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_urfs_grp1_pad_mux),
+       .pad_mux_list = audio_uart2_urfs_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_urfs_grp2_pad_mux[] = {
+       MUX(1, 96, 3, 0xa10, 30, 0xa90, 30),
+};
+
+static struct atlas7_grp_mux audio_uart2_urfs_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_urfs_grp2_pad_mux),
+       .pad_mux_list = audio_uart2_urfs_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_urxd_grp0_pad_mux[] = {
        MUX(1, 20, 2, 0xa00, 24, 0xa80, 24),
-       MUX(1, 21, 2, 0xa00, 25, 0xa80, 25),
-       MUX(1, 19, 2, 0xa00, 23, 0xa80, 23),
-       MUX(1, 18, 2, 0xa00, 22, 0xa80, 22),
 };
 
-static struct atlas7_grp_mux audio_uart2_grp0_mux = {
-       .pad_mux_count = ARRAY_SIZE(audio_uart2_grp0_pad_mux),
-       .pad_mux_list = audio_uart2_grp0_pad_mux,
+static struct atlas7_grp_mux audio_uart2_urxd_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_urxd_grp0_pad_mux),
+       .pad_mux_list = audio_uart2_urxd_grp0_pad_mux,
 };
 
-static struct atlas7_pad_mux audio_uart2_grp1_pad_mux[] = {
+static struct atlas7_pad_mux audio_uart2_urxd_grp1_pad_mux[] = {
        MUX(1, 109, 2, 0xa00, 24, 0xa80, 24),
-       MUX(1, 110, 2, 0xa00, 25, 0xa80, 25),
+};
+
+static struct atlas7_grp_mux audio_uart2_urxd_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_urxd_grp1_pad_mux),
+       .pad_mux_list = audio_uart2_urxd_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_urxd_grp2_pad_mux[] = {
+       MUX(1, 93, 3, 0xa00, 24, 0xa80, 24),
+};
+
+static struct atlas7_grp_mux audio_uart2_urxd_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_urxd_grp2_pad_mux),
+       .pad_mux_list = audio_uart2_urxd_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_usclk_grp0_pad_mux[] = {
+       MUX(1, 19, 2, 0xa00, 23, 0xa80, 23),
+};
+
+static struct atlas7_grp_mux audio_uart2_usclk_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_usclk_grp0_pad_mux),
+       .pad_mux_list = audio_uart2_usclk_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_usclk_grp1_pad_mux[] = {
        MUX(1, 101, 2, 0xa00, 23, 0xa80, 23),
+};
+
+static struct atlas7_grp_mux audio_uart2_usclk_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_usclk_grp1_pad_mux),
+       .pad_mux_list = audio_uart2_usclk_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_usclk_grp2_pad_mux[] = {
+       MUX(1, 91, 3, 0xa00, 23, 0xa80, 23),
+};
+
+static struct atlas7_grp_mux audio_uart2_usclk_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_usclk_grp2_pad_mux),
+       .pad_mux_list = audio_uart2_usclk_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_utfs_grp0_pad_mux[] = {
+       MUX(1, 18, 2, 0xa00, 22, 0xa80, 22),
+};
+
+static struct atlas7_grp_mux audio_uart2_utfs_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_utfs_grp0_pad_mux),
+       .pad_mux_list = audio_uart2_utfs_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_utfs_grp1_pad_mux[] = {
        MUX(1, 111, 2, 0xa00, 22, 0xa80, 22),
 };
 
-static struct atlas7_grp_mux audio_uart2_grp1_mux = {
-       .pad_mux_count = ARRAY_SIZE(audio_uart2_grp1_pad_mux),
-       .pad_mux_list = audio_uart2_grp1_pad_mux,
+static struct atlas7_grp_mux audio_uart2_utfs_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_utfs_grp1_pad_mux),
+       .pad_mux_list = audio_uart2_utfs_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_utfs_grp2_pad_mux[] = {
+       MUX(1, 94, 3, 0xa00, 22, 0xa80, 22),
+};
+
+static struct atlas7_grp_mux audio_uart2_utfs_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_utfs_grp2_pad_mux),
+       .pad_mux_list = audio_uart2_utfs_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_utxd_grp0_pad_mux[] = {
+       MUX(1, 21, 2, 0xa00, 25, 0xa80, 25),
+};
+
+static struct atlas7_grp_mux audio_uart2_utxd_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_utxd_grp0_pad_mux),
+       .pad_mux_list = audio_uart2_utxd_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_utxd_grp1_pad_mux[] = {
+       MUX(1, 110, 2, 0xa00, 25, 0xa80, 25),
+};
+
+static struct atlas7_grp_mux audio_uart2_utxd_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_utxd_grp1_pad_mux),
+       .pad_mux_list = audio_uart2_utxd_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux audio_uart2_utxd_grp2_pad_mux[] = {
+       MUX(1, 92, 3, 0xa00, 25, 0xa80, 25),
+};
+
+static struct atlas7_grp_mux audio_uart2_utxd_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(audio_uart2_utxd_grp2_pad_mux),
+       .pad_mux_list = audio_uart2_utxd_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux c_can_trnsvr_en_grp0_pad_mux[] = {
+       MUX(0, 2, 6, N, N, N, N),
 };
 
-static struct atlas7_pad_mux c_can_trnsvr_grp_pad_mux[] = {
+static struct atlas7_grp_mux c_can_trnsvr_en_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(c_can_trnsvr_en_grp0_pad_mux),
+       .pad_mux_list = c_can_trnsvr_en_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux c_can_trnsvr_en_grp1_pad_mux[] = {
+       MUX(0, 0, 2, N, N, N, N),
+};
+
+static struct atlas7_grp_mux c_can_trnsvr_en_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(c_can_trnsvr_en_grp1_pad_mux),
+       .pad_mux_list = c_can_trnsvr_en_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux c_can_trnsvr_intr_grp_pad_mux[] = {
        MUX(0, 1, 2, N, N, N, N),
 };
 
-static struct atlas7_grp_mux c_can_trnsvr_grp_mux = {
-       .pad_mux_count = ARRAY_SIZE(c_can_trnsvr_grp_pad_mux),
-       .pad_mux_list = c_can_trnsvr_grp_pad_mux,
+static struct atlas7_grp_mux c_can_trnsvr_intr_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(c_can_trnsvr_intr_grp_pad_mux),
+       .pad_mux_list = c_can_trnsvr_intr_grp_pad_mux,
 };
 
-static struct atlas7_pad_mux c0_can_grp0_pad_mux[] = {
+static struct atlas7_pad_mux c_can_trnsvr_stb_n_grp_pad_mux[] = {
+       MUX(0, 3, 6, N, N, N, N),
+};
+
+static struct atlas7_grp_mux c_can_trnsvr_stb_n_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(c_can_trnsvr_stb_n_grp_pad_mux),
+       .pad_mux_list = c_can_trnsvr_stb_n_grp_pad_mux,
+};
+
+static struct atlas7_pad_mux c0_can_rxd_trnsv0_grp_pad_mux[] = {
        MUX(0, 11, 1, 0xa08, 9, 0xa88, 9),
+};
+
+static struct atlas7_grp_mux c0_can_rxd_trnsv0_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(c0_can_rxd_trnsv0_grp_pad_mux),
+       .pad_mux_list = c0_can_rxd_trnsv0_grp_pad_mux,
+};
+
+static struct atlas7_pad_mux c0_can_rxd_trnsv1_grp_pad_mux[] = {
+       MUX(0, 2, 5, 0xa10, 9, 0xa90, 9),
+};
+
+static struct atlas7_grp_mux c0_can_rxd_trnsv1_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(c0_can_rxd_trnsv1_grp_pad_mux),
+       .pad_mux_list = c0_can_rxd_trnsv1_grp_pad_mux,
+};
+
+static struct atlas7_pad_mux c0_can_txd_trnsv0_grp_pad_mux[] = {
        MUX(0, 10, 1, N, N, N, N),
 };
 
-static struct atlas7_grp_mux c0_can_grp0_mux = {
-       .pad_mux_count = ARRAY_SIZE(c0_can_grp0_pad_mux),
-       .pad_mux_list = c0_can_grp0_pad_mux,
+static struct atlas7_grp_mux c0_can_txd_trnsv0_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(c0_can_txd_trnsv0_grp_pad_mux),
+       .pad_mux_list = c0_can_txd_trnsv0_grp_pad_mux,
 };
 
-static struct atlas7_pad_mux c0_can_grp1_pad_mux[] = {
-       MUX(0, 2, 5, 0xa08, 9, 0xa88, 9),
+static struct atlas7_pad_mux c0_can_txd_trnsv1_grp_pad_mux[] = {
        MUX(0, 3, 5, N, N, N, N),
 };
 
-static struct atlas7_grp_mux c0_can_grp1_mux = {
-       .pad_mux_count = ARRAY_SIZE(c0_can_grp1_pad_mux),
-       .pad_mux_list = c0_can_grp1_pad_mux,
+static struct atlas7_grp_mux c0_can_txd_trnsv1_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(c0_can_txd_trnsv1_grp_pad_mux),
+       .pad_mux_list = c0_can_txd_trnsv1_grp_pad_mux,
 };
 
-static struct atlas7_pad_mux c1_can_grp0_pad_mux[] = {
+static struct atlas7_pad_mux c1_can_rxd_grp0_pad_mux[] = {
        MUX(1, 138, 2, 0xa00, 4, 0xa80, 4),
-       MUX(1, 137, 2, N, N, N, N),
 };
 
-static struct atlas7_grp_mux c1_can_grp0_mux = {
-       .pad_mux_count = ARRAY_SIZE(c1_can_grp0_pad_mux),
-       .pad_mux_list = c1_can_grp0_pad_mux,
+static struct atlas7_grp_mux c1_can_rxd_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(c1_can_rxd_grp0_pad_mux),
+       .pad_mux_list = c1_can_rxd_grp0_pad_mux,
 };
 
-static struct atlas7_pad_mux c1_can_grp1_pad_mux[] = {
+static struct atlas7_pad_mux c1_can_rxd_grp1_pad_mux[] = {
        MUX(1, 147, 2, 0xa00, 4, 0xa80, 4),
-       MUX(1, 146, 2, N, N, N, N),
 };
 
-static struct atlas7_grp_mux c1_can_grp1_mux = {
-       .pad_mux_count = ARRAY_SIZE(c1_can_grp1_pad_mux),
-       .pad_mux_list = c1_can_grp1_pad_mux,
+static struct atlas7_grp_mux c1_can_rxd_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(c1_can_rxd_grp1_pad_mux),
+       .pad_mux_list = c1_can_rxd_grp1_pad_mux,
 };
 
-static struct atlas7_pad_mux c1_can_grp2_pad_mux[] = {
+static struct atlas7_pad_mux c1_can_rxd_grp2_pad_mux[] = {
        MUX(0, 2, 2, 0xa00, 4, 0xa80, 4),
+};
+
+static struct atlas7_grp_mux c1_can_rxd_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(c1_can_rxd_grp2_pad_mux),
+       .pad_mux_list = c1_can_rxd_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux c1_can_rxd_grp3_pad_mux[] = {
+       MUX(1, 162, 4, 0xa00, 4, 0xa80, 4),
+};
+
+static struct atlas7_grp_mux c1_can_rxd_grp3_mux = {
+       .pad_mux_count = ARRAY_SIZE(c1_can_rxd_grp3_pad_mux),
+       .pad_mux_list = c1_can_rxd_grp3_pad_mux,
+};
+
+static struct atlas7_pad_mux c1_can_txd_grp0_pad_mux[] = {
+       MUX(1, 137, 2, N, N, N, N),
+};
+
+static struct atlas7_grp_mux c1_can_txd_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(c1_can_txd_grp0_pad_mux),
+       .pad_mux_list = c1_can_txd_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux c1_can_txd_grp1_pad_mux[] = {
+       MUX(1, 146, 2, N, N, N, N),
+};
+
+static struct atlas7_grp_mux c1_can_txd_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(c1_can_txd_grp1_pad_mux),
+       .pad_mux_list = c1_can_txd_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux c1_can_txd_grp2_pad_mux[] = {
        MUX(0, 3, 2, N, N, N, N),
 };
 
-static struct atlas7_grp_mux c1_can_grp2_mux = {
-       .pad_mux_count = ARRAY_SIZE(c1_can_grp2_pad_mux),
-       .pad_mux_list = c1_can_grp2_pad_mux,
+static struct atlas7_grp_mux c1_can_txd_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(c1_can_txd_grp2_pad_mux),
+       .pad_mux_list = c1_can_txd_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux c1_can_txd_grp3_pad_mux[] = {
+       MUX(1, 161, 4, N, N, N, N),
+};
+
+static struct atlas7_grp_mux c1_can_txd_grp3_mux = {
+       .pad_mux_count = ARRAY_SIZE(c1_can_txd_grp3_pad_mux),
+       .pad_mux_list = c1_can_txd_grp3_pad_mux,
 };
 
 static struct atlas7_pad_mux ca_audio_lpc_grp_pad_mux[] = {
@@ -2198,18 +2826,215 @@ static struct atlas7_grp_mux i2c1_grp_mux = {
        .pad_mux_list = i2c1_grp_pad_mux,
 };
 
-static struct atlas7_pad_mux jtag_grp0_pad_mux[] = {
+static struct atlas7_pad_mux i2s0_grp_pad_mux[] = {
+       MUX(1, 91, 2, 0xa10, 12, 0xa90, 12),
+       MUX(1, 93, 2, 0xa10, 13, 0xa90, 13),
+       MUX(1, 94, 2, 0xa10, 14, 0xa90, 14),
+       MUX(1, 92, 2, 0xa10, 15, 0xa90, 15),
+};
+
+static struct atlas7_grp_mux i2s0_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s0_grp_pad_mux),
+       .pad_mux_list = i2s0_grp_pad_mux,
+};
+
+static struct atlas7_pad_mux i2s1_basic_grp_pad_mux[] = {
+       MUX(1, 95, 2, 0xa10, 16, 0xa90, 16),
+       MUX(1, 96, 2, 0xa10, 19, 0xa90, 19),
+};
+
+static struct atlas7_grp_mux i2s1_basic_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s1_basic_grp_pad_mux),
+       .pad_mux_list = i2s1_basic_grp_pad_mux,
+};
+
+static struct atlas7_pad_mux i2s1_rxd0_grp0_pad_mux[] = {
+       MUX(1, 61, 4, 0xa10, 17, 0xa90, 17),
+};
+
+static struct atlas7_grp_mux i2s1_rxd0_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s1_rxd0_grp0_pad_mux),
+       .pad_mux_list = i2s1_rxd0_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux i2s1_rxd0_grp1_pad_mux[] = {
+       MUX(1, 131, 4, 0xa10, 17, 0xa90, 17),
+};
+
+static struct atlas7_grp_mux i2s1_rxd0_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s1_rxd0_grp1_pad_mux),
+       .pad_mux_list = i2s1_rxd0_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux i2s1_rxd0_grp2_pad_mux[] = {
+       MUX(1, 129, 2, 0xa10, 17, 0xa90, 17),
+};
+
+static struct atlas7_grp_mux i2s1_rxd0_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s1_rxd0_grp2_pad_mux),
+       .pad_mux_list = i2s1_rxd0_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux i2s1_rxd0_grp3_pad_mux[] = {
+       MUX(1, 117, 7, 0xa10, 17, 0xa90, 17),
+};
+
+static struct atlas7_grp_mux i2s1_rxd0_grp3_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s1_rxd0_grp3_pad_mux),
+       .pad_mux_list = i2s1_rxd0_grp3_pad_mux,
+};
+
+static struct atlas7_pad_mux i2s1_rxd0_grp4_pad_mux[] = {
+       MUX(1, 83, 4, 0xa10, 17, 0xa90, 17),
+};
+
+static struct atlas7_grp_mux i2s1_rxd0_grp4_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s1_rxd0_grp4_pad_mux),
+       .pad_mux_list = i2s1_rxd0_grp4_pad_mux,
+};
+
+static struct atlas7_pad_mux i2s1_rxd1_grp0_pad_mux[] = {
+       MUX(1, 72, 4, 0xa10, 18, 0xa90, 18),
+};
+
+static struct atlas7_grp_mux i2s1_rxd1_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s1_rxd1_grp0_pad_mux),
+       .pad_mux_list = i2s1_rxd1_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux i2s1_rxd1_grp1_pad_mux[] = {
+       MUX(1, 132, 4, 0xa10, 18, 0xa90, 18),
+};
+
+static struct atlas7_grp_mux i2s1_rxd1_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s1_rxd1_grp1_pad_mux),
+       .pad_mux_list = i2s1_rxd1_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux i2s1_rxd1_grp2_pad_mux[] = {
+       MUX(1, 130, 2, 0xa10, 18, 0xa90, 18),
+};
+
+static struct atlas7_grp_mux i2s1_rxd1_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s1_rxd1_grp2_pad_mux),
+       .pad_mux_list = i2s1_rxd1_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux i2s1_rxd1_grp3_pad_mux[] = {
+       MUX(1, 118, 7, 0xa10, 18, 0xa90, 18),
+};
+
+static struct atlas7_grp_mux i2s1_rxd1_grp3_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s1_rxd1_grp3_pad_mux),
+       .pad_mux_list = i2s1_rxd1_grp3_pad_mux,
+};
+
+static struct atlas7_pad_mux i2s1_rxd1_grp4_pad_mux[] = {
+       MUX(1, 84, 4, 0xa10, 18, 0xa90, 18),
+};
+
+static struct atlas7_grp_mux i2s1_rxd1_grp4_mux = {
+       .pad_mux_count = ARRAY_SIZE(i2s1_rxd1_grp4_pad_mux),
+       .pad_mux_list = i2s1_rxd1_grp4_pad_mux,
+};
+
+static struct atlas7_pad_mux jtag_jt_dbg_nsrst_grp_pad_mux[] = {
        MUX(1, 125, 5, 0xa08, 2, 0xa88, 2),
+};
+
+static struct atlas7_grp_mux jtag_jt_dbg_nsrst_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_jt_dbg_nsrst_grp_pad_mux),
+       .pad_mux_list = jtag_jt_dbg_nsrst_grp_pad_mux,
+};
+
+static struct atlas7_pad_mux jtag_ntrst_grp0_pad_mux[] = {
        MUX(0, 4, 3, 0xa08, 3, 0xa88, 3),
-       MUX(0, 2, 3, N, N, N, N),
-       MUX(0, 0, 3, N, N, N, N),
-       MUX(0, 1, 3, N, N, N, N),
+};
+
+static struct atlas7_grp_mux jtag_ntrst_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_ntrst_grp0_pad_mux),
+       .pad_mux_list = jtag_ntrst_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux jtag_ntrst_grp1_pad_mux[] = {
+       MUX(1, 163, 1, 0xa08, 3, 0xa88, 3),
+};
+
+static struct atlas7_grp_mux jtag_ntrst_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_ntrst_grp1_pad_mux),
+       .pad_mux_list = jtag_ntrst_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux jtag_swdiotms_grp0_pad_mux[] = {
+       MUX(0, 2, 3, 0xa10, 10, 0xa90, 10),
+};
+
+static struct atlas7_grp_mux jtag_swdiotms_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_swdiotms_grp0_pad_mux),
+       .pad_mux_list = jtag_swdiotms_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux jtag_swdiotms_grp1_pad_mux[] = {
+       MUX(1, 160, 1, 0xa10, 10, 0xa90, 10),
+};
+
+static struct atlas7_grp_mux jtag_swdiotms_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_swdiotms_grp1_pad_mux),
+       .pad_mux_list = jtag_swdiotms_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux jtag_tck_grp0_pad_mux[] = {
+       MUX(0, 0, 3, 0xa10, 11, 0xa90, 11),
+};
+
+static struct atlas7_grp_mux jtag_tck_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_tck_grp0_pad_mux),
+       .pad_mux_list = jtag_tck_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux jtag_tck_grp1_pad_mux[] = {
+       MUX(1, 161, 1, 0xa10, 11, 0xa90, 11),
+};
+
+static struct atlas7_grp_mux jtag_tck_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_tck_grp1_pad_mux),
+       .pad_mux_list = jtag_tck_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux jtag_tdi_grp0_pad_mux[] = {
+       MUX(0, 1, 3, 0xa10, 31, 0xa90, 31),
+};
+
+static struct atlas7_grp_mux jtag_tdi_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_tdi_grp0_pad_mux),
+       .pad_mux_list = jtag_tdi_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux jtag_tdi_grp1_pad_mux[] = {
+       MUX(1, 162, 1, 0xa10, 31, 0xa90, 31),
+};
+
+static struct atlas7_grp_mux jtag_tdi_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_tdi_grp1_pad_mux),
+       .pad_mux_list = jtag_tdi_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux jtag_tdo_grp0_pad_mux[] = {
        MUX(0, 3, 3, N, N, N, N),
 };
 
-static struct atlas7_grp_mux jtag_grp0_mux = {
-       .pad_mux_count = ARRAY_SIZE(jtag_grp0_pad_mux),
-       .pad_mux_list = jtag_grp0_pad_mux,
+static struct atlas7_grp_mux jtag_tdo_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_tdo_grp0_pad_mux),
+       .pad_mux_list = jtag_tdo_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux jtag_tdo_grp1_pad_mux[] = {
+       MUX(1, 159, 1, N, N, N, N),
+};
+
+static struct atlas7_grp_mux jtag_tdo_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(jtag_tdo_grp1_pad_mux),
+       .pad_mux_list = jtag_tdo_grp1_pad_mux,
 };
 
 static struct atlas7_pad_mux ks_kas_spi_grp0_pad_mux[] = {
@@ -2401,6 +3226,7 @@ static struct atlas7_grp_mux nd_df_nowp_grp_mux = {
 static struct atlas7_pad_mux ps_grp_pad_mux[] = {
        MUX(1, 120, 2, N, N, N, N),
        MUX(1, 119, 2, N, N, N, N),
+       MUX(1, 121, 5, N, N, N, N),
 };
 
 static struct atlas7_grp_mux ps_grp_mux = {
@@ -2534,6 +3360,15 @@ static struct atlas7_grp_mux pw_cko0_grp2_mux = {
        .pad_mux_list = pw_cko0_grp2_pad_mux,
 };
 
+static struct atlas7_pad_mux pw_cko0_grp3_pad_mux[] = {
+       MUX(1, 162, 5, N, N, N, N),
+};
+
+static struct atlas7_grp_mux pw_cko0_grp3_mux = {
+       .pad_mux_count = ARRAY_SIZE(pw_cko0_grp3_pad_mux),
+       .pad_mux_list = pw_cko0_grp3_pad_mux,
+};
+
 static struct atlas7_pad_mux pw_cko1_grp0_pad_mux[] = {
        MUX(1, 124, 3, N, N, N, N),
 };
@@ -2552,6 +3387,15 @@ static struct atlas7_grp_mux pw_cko1_grp1_mux = {
        .pad_mux_list = pw_cko1_grp1_pad_mux,
 };
 
+static struct atlas7_pad_mux pw_cko1_grp2_pad_mux[] = {
+       MUX(1, 163, 5, N, N, N, N),
+};
+
+static struct atlas7_grp_mux pw_cko1_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(pw_cko1_grp2_pad_mux),
+       .pad_mux_list = pw_cko1_grp2_pad_mux,
+};
+
 static struct atlas7_pad_mux pw_i2s01_clk_grp0_pad_mux[] = {
        MUX(1, 125, 3, N, N, N, N),
 };
@@ -2570,22 +3414,58 @@ static struct atlas7_grp_mux pw_i2s01_clk_grp1_mux = {
        .pad_mux_list = pw_i2s01_clk_grp1_pad_mux,
 };
 
-static struct atlas7_pad_mux pw_pwm0_grp_pad_mux[] = {
+static struct atlas7_pad_mux pw_i2s01_clk_grp2_pad_mux[] = {
+       MUX(1, 132, 2, N, N, N, N),
+};
+
+static struct atlas7_grp_mux pw_i2s01_clk_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(pw_i2s01_clk_grp2_pad_mux),
+       .pad_mux_list = pw_i2s01_clk_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux pw_pwm0_grp0_pad_mux[] = {
        MUX(1, 119, 3, N, N, N, N),
 };
 
-static struct atlas7_grp_mux pw_pwm0_grp_mux = {
-       .pad_mux_count = ARRAY_SIZE(pw_pwm0_grp_pad_mux),
-       .pad_mux_list = pw_pwm0_grp_pad_mux,
+static struct atlas7_grp_mux pw_pwm0_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(pw_pwm0_grp0_pad_mux),
+       .pad_mux_list = pw_pwm0_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux pw_pwm0_grp1_pad_mux[] = {
+       MUX(1, 159, 5, N, N, N, N),
+};
+
+static struct atlas7_grp_mux pw_pwm0_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(pw_pwm0_grp1_pad_mux),
+       .pad_mux_list = pw_pwm0_grp1_pad_mux,
 };
 
-static struct atlas7_pad_mux pw_pwm1_grp_pad_mux[] = {
+static struct atlas7_pad_mux pw_pwm1_grp0_pad_mux[] = {
        MUX(1, 120, 3, N, N, N, N),
 };
 
-static struct atlas7_grp_mux pw_pwm1_grp_mux = {
-       .pad_mux_count = ARRAY_SIZE(pw_pwm1_grp_pad_mux),
-       .pad_mux_list = pw_pwm1_grp_pad_mux,
+static struct atlas7_grp_mux pw_pwm1_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(pw_pwm1_grp0_pad_mux),
+       .pad_mux_list = pw_pwm1_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux pw_pwm1_grp1_pad_mux[] = {
+       MUX(1, 160, 5, N, N, N, N),
+};
+
+static struct atlas7_grp_mux pw_pwm1_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(pw_pwm1_grp1_pad_mux),
+       .pad_mux_list = pw_pwm1_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux pw_pwm1_grp2_pad_mux[] = {
+       MUX(1, 131, 2, N, N, N, N),
+};
+
+static struct atlas7_grp_mux pw_pwm1_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(pw_pwm1_grp2_pad_mux),
+       .pad_mux_list = pw_pwm1_grp2_pad_mux,
 };
 
 static struct atlas7_pad_mux pw_pwm2_grp0_pad_mux[] = {
@@ -2606,6 +3486,15 @@ static struct atlas7_grp_mux pw_pwm2_grp1_mux = {
        .pad_mux_list = pw_pwm2_grp1_pad_mux,
 };
 
+static struct atlas7_pad_mux pw_pwm2_grp2_pad_mux[] = {
+       MUX(1, 161, 5, N, N, N, N),
+};
+
+static struct atlas7_grp_mux pw_pwm2_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(pw_pwm2_grp2_pad_mux),
+       .pad_mux_list = pw_pwm2_grp2_pad_mux,
+};
+
 static struct atlas7_pad_mux pw_pwm3_grp0_pad_mux[] = {
        MUX(1, 122, 3, N, N, N, N),
 };
@@ -2642,6 +3531,15 @@ static struct atlas7_grp_mux pw_pwm_cpu_vol_grp1_mux = {
        .pad_mux_list = pw_pwm_cpu_vol_grp1_pad_mux,
 };
 
+static struct atlas7_pad_mux pw_pwm_cpu_vol_grp2_pad_mux[] = {
+       MUX(1, 161, 5, N, N, N, N),
+};
+
+static struct atlas7_grp_mux pw_pwm_cpu_vol_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(pw_pwm_cpu_vol_grp2_pad_mux),
+       .pad_mux_list = pw_pwm_cpu_vol_grp2_pad_mux,
+};
+
 static struct atlas7_pad_mux pw_backlight_grp0_pad_mux[] = {
        MUX(1, 122, 3, N, N, N, N),
 };
@@ -2795,35 +3693,54 @@ static struct atlas7_grp_mux sd1_4bit_grp1_mux = {
        .pad_mux_list = sd1_4bit_grp1_pad_mux,
 };
 
-static struct atlas7_pad_mux sd2_grp0_pad_mux[] = {
-       MUX(1, 124, 2, 0xa08, 7, 0xa88, 7),
+static struct atlas7_pad_mux sd2_basic_grp_pad_mux[] = {
        MUX(1, 31, 1, N, N, N, N),
        MUX(1, 32, 1, N, N, N, N),
        MUX(1, 33, 1, N, N, N, N),
        MUX(1, 34, 1, N, N, N, N),
        MUX(1, 35, 1, N, N, N, N),
        MUX(1, 36, 1, N, N, N, N),
-       MUX(1, 123, 2, N, N, N, N),
 };
 
-static struct atlas7_grp_mux sd2_grp0_mux = {
-       .pad_mux_count = ARRAY_SIZE(sd2_grp0_pad_mux),
-       .pad_mux_list = sd2_grp0_pad_mux,
+static struct atlas7_grp_mux sd2_basic_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(sd2_basic_grp_pad_mux),
+       .pad_mux_list = sd2_basic_grp_pad_mux,
 };
 
-static struct atlas7_pad_mux sd2_no_cdb_grp0_pad_mux[] = {
-       MUX(1, 31, 1, N, N, N, N),
-       MUX(1, 32, 1, N, N, N, N),
-       MUX(1, 33, 1, N, N, N, N),
-       MUX(1, 34, 1, N, N, N, N),
-       MUX(1, 35, 1, N, N, N, N),
-       MUX(1, 36, 1, N, N, N, N),
-       MUX(1, 123, 2, N, N, N, N),
+static struct atlas7_pad_mux sd2_cdb_grp0_pad_mux[] = {
+       MUX(1, 124, 2, 0xa08, 7, 0xa88, 7),
 };
 
-static struct atlas7_grp_mux sd2_no_cdb_grp0_mux = {
-       .pad_mux_count = ARRAY_SIZE(sd2_no_cdb_grp0_pad_mux),
-       .pad_mux_list = sd2_no_cdb_grp0_pad_mux,
+static struct atlas7_grp_mux sd2_cdb_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(sd2_cdb_grp0_pad_mux),
+       .pad_mux_list = sd2_cdb_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux sd2_cdb_grp1_pad_mux[] = {
+       MUX(1, 161, 6, 0xa08, 7, 0xa88, 7),
+};
+
+static struct atlas7_grp_mux sd2_cdb_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(sd2_cdb_grp1_pad_mux),
+       .pad_mux_list = sd2_cdb_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux sd2_wpb_grp0_pad_mux[] = {
+       MUX(1, 123, 2, 0xa10, 6, 0xa90, 6),
+};
+
+static struct atlas7_grp_mux sd2_wpb_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(sd2_wpb_grp0_pad_mux),
+       .pad_mux_list = sd2_wpb_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux sd2_wpb_grp1_pad_mux[] = {
+       MUX(1, 163, 7, 0xa10, 6, 0xa90, 6),
+};
+
+static struct atlas7_grp_mux sd2_wpb_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(sd2_wpb_grp1_pad_mux),
+       .pad_mux_list = sd2_wpb_grp1_pad_mux,
 };
 
 static struct atlas7_pad_mux sd3_grp_pad_mux[] = {
@@ -2975,146 +3892,302 @@ static struct atlas7_grp_mux uart1_grp_mux = {
        .pad_mux_list = uart1_grp_pad_mux,
 };
 
-static struct atlas7_pad_mux uart2_grp_pad_mux[] = {
-       MUX(0, 11, 2, N, N, N, N),
+static struct atlas7_pad_mux uart2_cts_grp0_pad_mux[] = {
+       MUX(1, 132, 3, 0xa10, 2, 0xa90, 2),
+};
+
+static struct atlas7_grp_mux uart2_cts_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart2_cts_grp0_pad_mux),
+       .pad_mux_list = uart2_cts_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux uart2_cts_grp1_pad_mux[] = {
+       MUX(1, 162, 2, 0xa10, 2, 0xa90, 2),
+};
+
+static struct atlas7_grp_mux uart2_cts_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart2_cts_grp1_pad_mux),
+       .pad_mux_list = uart2_cts_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux uart2_rts_grp0_pad_mux[] = {
+       MUX(1, 131, 3, N, N, N, N),
+};
+
+static struct atlas7_grp_mux uart2_rts_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart2_rts_grp0_pad_mux),
+       .pad_mux_list = uart2_rts_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux uart2_rts_grp1_pad_mux[] = {
+       MUX(1, 161, 2, N, N, N, N),
+};
+
+static struct atlas7_grp_mux uart2_rts_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart2_rts_grp1_pad_mux),
+       .pad_mux_list = uart2_rts_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux uart2_rxd_grp0_pad_mux[] = {
+       MUX(0, 11, 2, 0xa10, 5, 0xa90, 5),
+};
+
+static struct atlas7_grp_mux uart2_rxd_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart2_rxd_grp0_pad_mux),
+       .pad_mux_list = uart2_rxd_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux uart2_rxd_grp1_pad_mux[] = {
+       MUX(1, 160, 2, 0xa10, 5, 0xa90, 5),
+};
+
+static struct atlas7_grp_mux uart2_rxd_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart2_rxd_grp1_pad_mux),
+       .pad_mux_list = uart2_rxd_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux uart2_rxd_grp2_pad_mux[] = {
+       MUX(1, 130, 3, 0xa10, 5, 0xa90, 5),
+};
+
+static struct atlas7_grp_mux uart2_rxd_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart2_rxd_grp2_pad_mux),
+       .pad_mux_list = uart2_rxd_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux uart2_txd_grp0_pad_mux[] = {
        MUX(0, 10, 2, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart2_grp_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart2_grp_pad_mux),
-       .pad_mux_list = uart2_grp_pad_mux,
+static struct atlas7_grp_mux uart2_txd_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart2_txd_grp0_pad_mux),
+       .pad_mux_list = uart2_txd_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux uart2_txd_grp1_pad_mux[] = {
+       MUX(1, 159, 2, N, N, N, N),
 };
 
-static struct atlas7_pad_mux uart3_grp0_pad_mux[] = {
+static struct atlas7_grp_mux uart2_txd_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart2_txd_grp1_pad_mux),
+       .pad_mux_list = uart2_txd_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux uart2_txd_grp2_pad_mux[] = {
+       MUX(1, 129, 3, N, N, N, N),
+};
+
+static struct atlas7_grp_mux uart2_txd_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart2_txd_grp2_pad_mux),
+       .pad_mux_list = uart2_txd_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux uart3_cts_grp0_pad_mux[] = {
        MUX(1, 125, 2, 0xa08, 0, 0xa88, 0),
-       MUX(1, 126, 2, N, N, N, N),
-       MUX(1, 138, 1, 0xa00, 5, 0xa80, 5),
-       MUX(1, 137, 1, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart3_grp0_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart3_grp0_pad_mux),
-       .pad_mux_list = uart3_grp0_pad_mux,
+static struct atlas7_grp_mux uart3_cts_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_cts_grp0_pad_mux),
+       .pad_mux_list = uart3_cts_grp0_pad_mux,
 };
 
-static struct atlas7_pad_mux uart3_grp1_pad_mux[] = {
+static struct atlas7_pad_mux uart3_cts_grp1_pad_mux[] = {
        MUX(1, 111, 4, 0xa08, 0, 0xa88, 0),
-       MUX(1, 109, 4, N, N, N, N),
-       MUX(1, 84, 2, 0xa00, 5, 0xa80, 5),
-       MUX(1, 83, 2, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart3_grp1_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart3_grp1_pad_mux),
-       .pad_mux_list = uart3_grp1_pad_mux,
+static struct atlas7_grp_mux uart3_cts_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_cts_grp1_pad_mux),
+       .pad_mux_list = uart3_cts_grp1_pad_mux,
 };
 
-static struct atlas7_pad_mux uart3_grp2_pad_mux[] = {
+static struct atlas7_pad_mux uart3_cts_grp2_pad_mux[] = {
        MUX(1, 140, 2, 0xa08, 0, 0xa88, 0),
+};
+
+static struct atlas7_grp_mux uart3_cts_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_cts_grp2_pad_mux),
+       .pad_mux_list = uart3_cts_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux uart3_rts_grp0_pad_mux[] = {
+       MUX(1, 126, 2, N, N, N, N),
+};
+
+static struct atlas7_grp_mux uart3_rts_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_rts_grp0_pad_mux),
+       .pad_mux_list = uart3_rts_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux uart3_rts_grp1_pad_mux[] = {
+       MUX(1, 109, 4, N, N, N, N),
+};
+
+static struct atlas7_grp_mux uart3_rts_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_rts_grp1_pad_mux),
+       .pad_mux_list = uart3_rts_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux uart3_rts_grp2_pad_mux[] = {
        MUX(1, 139, 2, N, N, N, N),
+};
+
+static struct atlas7_grp_mux uart3_rts_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_rts_grp2_pad_mux),
+       .pad_mux_list = uart3_rts_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux uart3_rxd_grp0_pad_mux[] = {
        MUX(1, 138, 1, 0xa00, 5, 0xa80, 5),
-       MUX(1, 137, 1, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart3_grp2_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart3_grp2_pad_mux),
-       .pad_mux_list = uart3_grp2_pad_mux,
+static struct atlas7_grp_mux uart3_rxd_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_rxd_grp0_pad_mux),
+       .pad_mux_list = uart3_rxd_grp0_pad_mux,
 };
 
-static struct atlas7_pad_mux uart3_grp3_pad_mux[] = {
-       MUX(1, 139, 2, N, N, N, N),
-       MUX(1, 140, 2, 0xa08, 0, 0xa88, 0),
+static struct atlas7_pad_mux uart3_rxd_grp1_pad_mux[] = {
        MUX(1, 84, 2, 0xa00, 5, 0xa80, 5),
-       MUX(1, 83, 2, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart3_grp3_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart3_grp3_pad_mux),
-       .pad_mux_list = uart3_grp3_pad_mux,
+static struct atlas7_grp_mux uart3_rxd_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_rxd_grp1_pad_mux),
+       .pad_mux_list = uart3_rxd_grp1_pad_mux,
 };
 
-static struct atlas7_pad_mux uart3_nopause_grp0_pad_mux[] = {
-       MUX(1, 138, 1, 0xa00, 5, 0xa80, 5),
+static struct atlas7_pad_mux uart3_rxd_grp2_pad_mux[] = {
+       MUX(1, 162, 3, 0xa00, 5, 0xa80, 5),
+};
+
+static struct atlas7_grp_mux uart3_rxd_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_rxd_grp2_pad_mux),
+       .pad_mux_list = uart3_rxd_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux uart3_txd_grp0_pad_mux[] = {
        MUX(1, 137, 1, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart3_nopause_grp0_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart3_nopause_grp0_pad_mux),
-       .pad_mux_list = uart3_nopause_grp0_pad_mux,
+static struct atlas7_grp_mux uart3_txd_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_txd_grp0_pad_mux),
+       .pad_mux_list = uart3_txd_grp0_pad_mux,
 };
 
-static struct atlas7_pad_mux uart3_nopause_grp1_pad_mux[] = {
-       MUX(1, 84, 2, 0xa00, 5, 0xa80, 5),
+static struct atlas7_pad_mux uart3_txd_grp1_pad_mux[] = {
        MUX(1, 83, 2, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart3_nopause_grp1_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart3_nopause_grp1_pad_mux),
-       .pad_mux_list = uart3_nopause_grp1_pad_mux,
+static struct atlas7_grp_mux uart3_txd_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_txd_grp1_pad_mux),
+       .pad_mux_list = uart3_txd_grp1_pad_mux,
 };
 
-static struct atlas7_pad_mux uart4_grp0_pad_mux[] = {
-       MUX(1, 122, 4, 0xa08, 1, 0xa88, 1),
-       MUX(1, 123, 4, N, N, N, N),
+static struct atlas7_pad_mux uart3_txd_grp2_pad_mux[] = {
+       MUX(1, 161, 3, N, N, N, N),
+};
+
+static struct atlas7_grp_mux uart3_txd_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart3_txd_grp2_pad_mux),
+       .pad_mux_list = uart3_txd_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux uart4_basic_grp_pad_mux[] = {
        MUX(1, 140, 1, N, N, N, N),
        MUX(1, 139, 1, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart4_grp0_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart4_grp0_pad_mux),
-       .pad_mux_list = uart4_grp0_pad_mux,
+static struct atlas7_grp_mux uart4_basic_grp_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart4_basic_grp_pad_mux),
+       .pad_mux_list = uart4_basic_grp_pad_mux,
+};
+
+static struct atlas7_pad_mux uart4_cts_grp0_pad_mux[] = {
+       MUX(1, 122, 4, 0xa08, 1, 0xa88, 1),
+};
+
+static struct atlas7_grp_mux uart4_cts_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart4_cts_grp0_pad_mux),
+       .pad_mux_list = uart4_cts_grp0_pad_mux,
 };
 
-static struct atlas7_pad_mux uart4_grp1_pad_mux[] = {
+static struct atlas7_pad_mux uart4_cts_grp1_pad_mux[] = {
        MUX(1, 100, 4, 0xa08, 1, 0xa88, 1),
-       MUX(1, 99, 4, N, N, N, N),
-       MUX(1, 140, 1, N, N, N, N),
-       MUX(1, 139, 1, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart4_grp1_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart4_grp1_pad_mux),
-       .pad_mux_list = uart4_grp1_pad_mux,
+static struct atlas7_grp_mux uart4_cts_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart4_cts_grp1_pad_mux),
+       .pad_mux_list = uart4_cts_grp1_pad_mux,
 };
 
-static struct atlas7_pad_mux uart4_grp2_pad_mux[] = {
+static struct atlas7_pad_mux uart4_cts_grp2_pad_mux[] = {
        MUX(1, 117, 2, 0xa08, 1, 0xa88, 1),
-       MUX(1, 116, 2, N, N, N, N),
-       MUX(1, 140, 1, N, N, N, N),
-       MUX(1, 139, 1, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart4_grp2_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart4_grp2_pad_mux),
-       .pad_mux_list = uart4_grp2_pad_mux,
+static struct atlas7_grp_mux uart4_cts_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart4_cts_grp2_pad_mux),
+       .pad_mux_list = uart4_cts_grp2_pad_mux,
 };
 
-static struct atlas7_pad_mux uart4_nopause_grp_pad_mux[] = {
-       MUX(1, 140, 1, N, N, N, N),
-       MUX(1, 139, 1, N, N, N, N),
+static struct atlas7_pad_mux uart4_rts_grp0_pad_mux[] = {
+       MUX(1, 123, 4, N, N, N, N),
 };
 
-static struct atlas7_grp_mux uart4_nopause_grp_mux = {
-       .pad_mux_count = ARRAY_SIZE(uart4_nopause_grp_pad_mux),
-       .pad_mux_list = uart4_nopause_grp_pad_mux,
+static struct atlas7_grp_mux uart4_rts_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart4_rts_grp0_pad_mux),
+       .pad_mux_list = uart4_rts_grp0_pad_mux,
 };
 
-static struct atlas7_pad_mux usb0_drvvbus_grp_pad_mux[] = {
+static struct atlas7_pad_mux uart4_rts_grp1_pad_mux[] = {
+       MUX(1, 99, 4, N, N, N, N),
+};
+
+static struct atlas7_grp_mux uart4_rts_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart4_rts_grp1_pad_mux),
+       .pad_mux_list = uart4_rts_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux uart4_rts_grp2_pad_mux[] = {
+       MUX(1, 116, 2, N, N, N, N),
+};
+
+static struct atlas7_grp_mux uart4_rts_grp2_mux = {
+       .pad_mux_count = ARRAY_SIZE(uart4_rts_grp2_pad_mux),
+       .pad_mux_list = uart4_rts_grp2_pad_mux,
+};
+
+static struct atlas7_pad_mux usb0_drvvbus_grp0_pad_mux[] = {
        MUX(1, 51, 2, N, N, N, N),
 };
 
-static struct atlas7_grp_mux usb0_drvvbus_grp_mux = {
-       .pad_mux_count = ARRAY_SIZE(usb0_drvvbus_grp_pad_mux),
-       .pad_mux_list = usb0_drvvbus_grp_pad_mux,
+static struct atlas7_grp_mux usb0_drvvbus_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(usb0_drvvbus_grp0_pad_mux),
+       .pad_mux_list = usb0_drvvbus_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux usb0_drvvbus_grp1_pad_mux[] = {
+       MUX(1, 162, 7, N, N, N, N),
 };
 
-static struct atlas7_pad_mux usb1_drvvbus_grp_pad_mux[] = {
+static struct atlas7_grp_mux usb0_drvvbus_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(usb0_drvvbus_grp1_pad_mux),
+       .pad_mux_list = usb0_drvvbus_grp1_pad_mux,
+};
+
+static struct atlas7_pad_mux usb1_drvvbus_grp0_pad_mux[] = {
        MUX(1, 134, 2, N, N, N, N),
 };
 
-static struct atlas7_grp_mux usb1_drvvbus_grp_mux = {
-       .pad_mux_count = ARRAY_SIZE(usb1_drvvbus_grp_pad_mux),
-       .pad_mux_list = usb1_drvvbus_grp_pad_mux,
+static struct atlas7_grp_mux usb1_drvvbus_grp0_mux = {
+       .pad_mux_count = ARRAY_SIZE(usb1_drvvbus_grp0_pad_mux),
+       .pad_mux_list = usb1_drvvbus_grp0_pad_mux,
+};
+
+static struct atlas7_pad_mux usb1_drvvbus_grp1_pad_mux[] = {
+       MUX(1, 163, 2, N, N, N, N),
+};
+
+static struct atlas7_grp_mux usb1_drvvbus_grp1_mux = {
+       .pad_mux_count = ARRAY_SIZE(usb1_drvvbus_grp1_pad_mux),
+       .pad_mux_list = usb1_drvvbus_grp1_pad_mux,
 };
 
 static struct atlas7_pad_mux visbus_dout_grp_pad_mux[] = {
@@ -3252,11 +4325,20 @@ static struct atlas7_pmx_func atlas7_pmx_functions[] = {
        FUNCTION("sdio_i2s_gpio", sdio_i2s_gpio_grp, &sdio_i2s_gpio_grp_mux),
        FUNCTION("sp_rgmii_gpio", sp_rgmii_gpio_grp, &sp_rgmii_gpio_grp_mux),
        FUNCTION("lvds_gpio", lvds_gpio_grp, &lvds_gpio_grp_mux),
-       FUNCTION("uart_nand_gpio",
-                       uart_nand_gpio_grp,
-                       &uart_nand_gpio_grp_mux),
+       FUNCTION("jtag_uart_nand_gpio",
+                       jtag_uart_nand_gpio_grp,
+                       &jtag_uart_nand_gpio_grp_mux),
        FUNCTION("rtc_gpio", rtc_gpio_grp, &rtc_gpio_grp_mux),
        FUNCTION("audio_ac97", audio_ac97_grp, &audio_ac97_grp_mux),
+       FUNCTION("audio_digmic_m0",
+                       audio_digmic_grp0,
+                       &audio_digmic_grp0_mux),
+       FUNCTION("audio_digmic_m1",
+                       audio_digmic_grp1,
+                       &audio_digmic_grp1_mux),
+       FUNCTION("audio_digmic_m2",
+                       audio_digmic_grp2,
+                       &audio_digmic_grp2_mux),
        FUNCTION("audio_func_dbg",
                        audio_func_dbg_grp,
                        &audio_func_dbg_grp_mux),
@@ -3265,16 +4347,119 @@ static struct atlas7_pmx_func atlas7_pmx_functions[] = {
        FUNCTION("audio_i2s_extclk",
                        audio_i2s_extclk_grp,
                        &audio_i2s_extclk_grp_mux),
-       FUNCTION("audio_uart0", audio_uart0_grp, &audio_uart0_grp_mux),
-       FUNCTION("audio_uart1", audio_uart1_grp, &audio_uart1_grp_mux),
-       FUNCTION("audio_uart2_m0", audio_uart2_grp0, &audio_uart2_grp0_mux),
-       FUNCTION("audio_uart2_m1", audio_uart2_grp1, &audio_uart2_grp1_mux),
-       FUNCTION("c_can_trnsvr", c_can_trnsvr_grp, &c_can_trnsvr_grp_mux),
-       FUNCTION("c0_can_m0", c0_can_grp0, &c0_can_grp0_mux),
-       FUNCTION("c0_can_m1", c0_can_grp1, &c0_can_grp1_mux),
-       FUNCTION("c1_can_m0", c1_can_grp0, &c1_can_grp0_mux),
-       FUNCTION("c1_can_m1", c1_can_grp1, &c1_can_grp1_mux),
-       FUNCTION("c1_can_m2", c1_can_grp2, &c1_can_grp2_mux),
+       FUNCTION("audio_spdif_out_m0",
+                       audio_spdif_out_grp0,
+                       &audio_spdif_out_grp0_mux),
+       FUNCTION("audio_spdif_out_m1",
+                       audio_spdif_out_grp1,
+                       &audio_spdif_out_grp1_mux),
+       FUNCTION("audio_spdif_out_m2",
+                       audio_spdif_out_grp2,
+                       &audio_spdif_out_grp2_mux),
+       FUNCTION("audio_uart0_basic",
+                       audio_uart0_basic_grp,
+                       &audio_uart0_basic_grp_mux),
+       FUNCTION("audio_uart0_urfs_m0",
+                       audio_uart0_urfs_grp0,
+                       &audio_uart0_urfs_grp0_mux),
+       FUNCTION("audio_uart0_urfs_m1",
+                       audio_uart0_urfs_grp1,
+                       &audio_uart0_urfs_grp1_mux),
+       FUNCTION("audio_uart0_urfs_m2",
+                       audio_uart0_urfs_grp2,
+                       &audio_uart0_urfs_grp2_mux),
+       FUNCTION("audio_uart0_urfs_m3",
+                       audio_uart0_urfs_grp3,
+                       &audio_uart0_urfs_grp3_mux),
+       FUNCTION("audio_uart1_basic",
+                       audio_uart1_basic_grp,
+                       &audio_uart1_basic_grp_mux),
+       FUNCTION("audio_uart1_urfs_m0",
+                       audio_uart1_urfs_grp0,
+                       &audio_uart1_urfs_grp0_mux),
+       FUNCTION("audio_uart1_urfs_m1",
+                       audio_uart1_urfs_grp1,
+                       &audio_uart1_urfs_grp1_mux),
+       FUNCTION("audio_uart1_urfs_m2",
+                       audio_uart1_urfs_grp2,
+                       &audio_uart1_urfs_grp2_mux),
+       FUNCTION("audio_uart2_urfs_m0",
+                       audio_uart2_urfs_grp0,
+                       &audio_uart2_urfs_grp0_mux),
+       FUNCTION("audio_uart2_urfs_m1",
+                       audio_uart2_urfs_grp1,
+                       &audio_uart2_urfs_grp1_mux),
+       FUNCTION("audio_uart2_urfs_m2",
+                       audio_uart2_urfs_grp2,
+                       &audio_uart2_urfs_grp2_mux),
+       FUNCTION("audio_uart2_urxd_m0",
+                       audio_uart2_urxd_grp0,
+                       &audio_uart2_urxd_grp0_mux),
+       FUNCTION("audio_uart2_urxd_m1",
+                       audio_uart2_urxd_grp1,
+                       &audio_uart2_urxd_grp1_mux),
+       FUNCTION("audio_uart2_urxd_m2",
+                       audio_uart2_urxd_grp2,
+                       &audio_uart2_urxd_grp2_mux),
+       FUNCTION("audio_uart2_usclk_m0",
+                       audio_uart2_usclk_grp0,
+                       &audio_uart2_usclk_grp0_mux),
+       FUNCTION("audio_uart2_usclk_m1",
+                       audio_uart2_usclk_grp1,
+                       &audio_uart2_usclk_grp1_mux),
+       FUNCTION("audio_uart2_usclk_m2",
+                       audio_uart2_usclk_grp2,
+                       &audio_uart2_usclk_grp2_mux),
+       FUNCTION("audio_uart2_utfs_m0",
+                       audio_uart2_utfs_grp0,
+                       &audio_uart2_utfs_grp0_mux),
+       FUNCTION("audio_uart2_utfs_m1",
+                       audio_uart2_utfs_grp1,
+                       &audio_uart2_utfs_grp1_mux),
+       FUNCTION("audio_uart2_utfs_m2",
+                       audio_uart2_utfs_grp2,
+                       &audio_uart2_utfs_grp2_mux),
+       FUNCTION("audio_uart2_utxd_m0",
+                       audio_uart2_utxd_grp0,
+                       &audio_uart2_utxd_grp0_mux),
+       FUNCTION("audio_uart2_utxd_m1",
+                       audio_uart2_utxd_grp1,
+                       &audio_uart2_utxd_grp1_mux),
+       FUNCTION("audio_uart2_utxd_m2",
+                       audio_uart2_utxd_grp2,
+                       &audio_uart2_utxd_grp2_mux),
+       FUNCTION("c_can_trnsvr_en_m0",
+                       c_can_trnsvr_en_grp0,
+                       &c_can_trnsvr_en_grp0_mux),
+       FUNCTION("c_can_trnsvr_en_m1",
+                       c_can_trnsvr_en_grp1,
+                       &c_can_trnsvr_en_grp1_mux),
+       FUNCTION("c_can_trnsvr_intr",
+                       c_can_trnsvr_intr_grp,
+                       &c_can_trnsvr_intr_grp_mux),
+       FUNCTION("c_can_trnsvr_stb_n",
+                       c_can_trnsvr_stb_n_grp,
+                       &c_can_trnsvr_stb_n_grp_mux),
+       FUNCTION("c0_can_rxd_trnsv0",
+                       c0_can_rxd_trnsv0_grp,
+                       &c0_can_rxd_trnsv0_grp_mux),
+       FUNCTION("c0_can_rxd_trnsv1",
+                       c0_can_rxd_trnsv1_grp,
+                       &c0_can_rxd_trnsv1_grp_mux),
+       FUNCTION("c0_can_txd_trnsv0",
+                       c0_can_txd_trnsv0_grp,
+                       &c0_can_txd_trnsv0_grp_mux),
+       FUNCTION("c0_can_txd_trnsv1",
+                       c0_can_txd_trnsv1_grp,
+                       &c0_can_txd_trnsv1_grp_mux),
+       FUNCTION("c1_can_rxd_m0", c1_can_rxd_grp0, &c1_can_rxd_grp0_mux),
+       FUNCTION("c1_can_rxd_m1", c1_can_rxd_grp1, &c1_can_rxd_grp1_mux),
+       FUNCTION("c1_can_rxd_m2", c1_can_rxd_grp2, &c1_can_rxd_grp2_mux),
+       FUNCTION("c1_can_rxd_m3", c1_can_rxd_grp3, &c1_can_rxd_grp3_mux),
+       FUNCTION("c1_can_txd_m0", c1_can_txd_grp0, &c1_can_txd_grp0_mux),
+       FUNCTION("c1_can_txd_m1", c1_can_txd_grp1, &c1_can_txd_grp1_mux),
+       FUNCTION("c1_can_txd_m2", c1_can_txd_grp2, &c1_can_txd_grp2_mux),
+       FUNCTION("c1_can_txd_m3", c1_can_txd_grp3, &c1_can_txd_grp3_mux),
        FUNCTION("ca_audio_lpc", ca_audio_lpc_grp, &ca_audio_lpc_grp_mux),
        FUNCTION("ca_bt_lpc", ca_bt_lpc_grp, &ca_bt_lpc_grp_mux),
        FUNCTION("ca_coex", ca_coex_grp, &ca_coex_grp_mux),
@@ -3377,7 +4562,35 @@ static struct atlas7_pmx_func atlas7_pmx_functions[] = {
                        &gn_trg_shutdown_grp3_mux),
        FUNCTION("i2c0", i2c0_grp, &i2c0_grp_mux),
        FUNCTION("i2c1", i2c1_grp, &i2c1_grp_mux),
-       FUNCTION("jtag_m0", jtag_grp0, &jtag_grp0_mux),
+       FUNCTION("i2s0", i2s0_grp, &i2s0_grp_mux),
+       FUNCTION("i2s1_basic", i2s1_basic_grp, &i2s1_basic_grp_mux),
+       FUNCTION("i2s1_rxd0_m0", i2s1_rxd0_grp0, &i2s1_rxd0_grp0_mux),
+       FUNCTION("i2s1_rxd0_m1", i2s1_rxd0_grp1, &i2s1_rxd0_grp1_mux),
+       FUNCTION("i2s1_rxd0_m2", i2s1_rxd0_grp2, &i2s1_rxd0_grp2_mux),
+       FUNCTION("i2s1_rxd0_m3", i2s1_rxd0_grp3, &i2s1_rxd0_grp3_mux),
+       FUNCTION("i2s1_rxd0_m4", i2s1_rxd0_grp4, &i2s1_rxd0_grp4_mux),
+       FUNCTION("i2s1_rxd1_m0", i2s1_rxd1_grp0, &i2s1_rxd1_grp0_mux),
+       FUNCTION("i2s1_rxd1_m1", i2s1_rxd1_grp1, &i2s1_rxd1_grp1_mux),
+       FUNCTION("i2s1_rxd1_m2", i2s1_rxd1_grp2, &i2s1_rxd1_grp2_mux),
+       FUNCTION("i2s1_rxd1_m3", i2s1_rxd1_grp3, &i2s1_rxd1_grp3_mux),
+       FUNCTION("i2s1_rxd1_m4", i2s1_rxd1_grp4, &i2s1_rxd1_grp4_mux),
+       FUNCTION("jtag_jt_dbg_nsrst",
+                       jtag_jt_dbg_nsrst_grp,
+                       &jtag_jt_dbg_nsrst_grp_mux),
+       FUNCTION("jtag_ntrst_m0", jtag_ntrst_grp0, &jtag_ntrst_grp0_mux),
+       FUNCTION("jtag_ntrst_m1", jtag_ntrst_grp1, &jtag_ntrst_grp1_mux),
+       FUNCTION("jtag_swdiotms_m0",
+                       jtag_swdiotms_grp0,
+                       &jtag_swdiotms_grp0_mux),
+       FUNCTION("jtag_swdiotms_m1",
+                       jtag_swdiotms_grp1,
+                       &jtag_swdiotms_grp1_mux),
+       FUNCTION("jtag_tck_m0", jtag_tck_grp0, &jtag_tck_grp0_mux),
+       FUNCTION("jtag_tck_m1", jtag_tck_grp1, &jtag_tck_grp1_mux),
+       FUNCTION("jtag_tdi_m0", jtag_tdi_grp0, &jtag_tdi_grp0_mux),
+       FUNCTION("jtag_tdi_m1", jtag_tdi_grp1, &jtag_tdi_grp1_mux),
+       FUNCTION("jtag_tdo_m0", jtag_tdo_grp0, &jtag_tdo_grp0_mux),
+       FUNCTION("jtag_tdo_m1", jtag_tdo_grp1, &jtag_tdo_grp1_mux),
        FUNCTION("ks_kas_spi_m0", ks_kas_spi_grp0, &ks_kas_spi_grp0_mux),
        FUNCTION("ld_ldd", ld_ldd_grp, &ld_ldd_grp_mux),
        FUNCTION("ld_ldd_16bit", ld_ldd_16bit_grp, &ld_ldd_16bit_grp_mux),
@@ -3414,18 +4627,27 @@ static struct atlas7_pmx_func atlas7_pmx_functions[] = {
        FUNCTION("pw_cko0_m0", pw_cko0_grp0, &pw_cko0_grp0_mux),
        FUNCTION("pw_cko0_m1", pw_cko0_grp1, &pw_cko0_grp1_mux),
        FUNCTION("pw_cko0_m2", pw_cko0_grp2, &pw_cko0_grp2_mux),
+       FUNCTION("pw_cko0_m3", pw_cko0_grp3, &pw_cko0_grp3_mux),
        FUNCTION("pw_cko1_m0", pw_cko1_grp0, &pw_cko1_grp0_mux),
        FUNCTION("pw_cko1_m1", pw_cko1_grp1, &pw_cko1_grp1_mux),
+       FUNCTION("pw_cko1_m2", pw_cko1_grp2, &pw_cko1_grp2_mux),
        FUNCTION("pw_i2s01_clk_m0",
                        pw_i2s01_clk_grp0,
                        &pw_i2s01_clk_grp0_mux),
        FUNCTION("pw_i2s01_clk_m1",
                        pw_i2s01_clk_grp1,
                        &pw_i2s01_clk_grp1_mux),
-       FUNCTION("pw_pwm0", pw_pwm0_grp, &pw_pwm0_grp_mux),
-       FUNCTION("pw_pwm1", pw_pwm1_grp, &pw_pwm1_grp_mux),
+       FUNCTION("pw_i2s01_clk_m2",
+                       pw_i2s01_clk_grp2,
+                       &pw_i2s01_clk_grp2_mux),
+       FUNCTION("pw_pwm0_m0", pw_pwm0_grp0, &pw_pwm0_grp0_mux),
+       FUNCTION("pw_pwm0_m1", pw_pwm0_grp1, &pw_pwm0_grp1_mux),
+       FUNCTION("pw_pwm1_m0", pw_pwm1_grp0, &pw_pwm1_grp0_mux),
+       FUNCTION("pw_pwm1_m1", pw_pwm1_grp1, &pw_pwm1_grp1_mux),
+       FUNCTION("pw_pwm1_m2", pw_pwm1_grp2, &pw_pwm1_grp2_mux),
        FUNCTION("pw_pwm2_m0", pw_pwm2_grp0, &pw_pwm2_grp0_mux),
        FUNCTION("pw_pwm2_m1", pw_pwm2_grp1, &pw_pwm2_grp1_mux),
+       FUNCTION("pw_pwm2_m2", pw_pwm2_grp2, &pw_pwm2_grp2_mux),
        FUNCTION("pw_pwm3_m0", pw_pwm3_grp0, &pw_pwm3_grp0_mux),
        FUNCTION("pw_pwm3_m1", pw_pwm3_grp1, &pw_pwm3_grp1_mux),
        FUNCTION("pw_pwm_cpu_vol_m0",
@@ -3434,6 +4656,9 @@ static struct atlas7_pmx_func atlas7_pmx_functions[] = {
        FUNCTION("pw_pwm_cpu_vol_m1",
                        pw_pwm_cpu_vol_grp1,
                        &pw_pwm_cpu_vol_grp1_mux),
+       FUNCTION("pw_pwm_cpu_vol_m2",
+                       pw_pwm_cpu_vol_grp2,
+                       &pw_pwm_cpu_vol_grp2_mux),
        FUNCTION("pw_backlight_m0",
                        pw_backlight_grp0,
                        &pw_backlight_grp0_mux),
@@ -3456,8 +4681,11 @@ static struct atlas7_pmx_func atlas7_pmx_functions[] = {
        FUNCTION("sd1", sd1_grp, &sd1_grp_mux),
        FUNCTION("sd1_4bit_m0", sd1_4bit_grp0, &sd1_4bit_grp0_mux),
        FUNCTION("sd1_4bit_m1", sd1_4bit_grp1, &sd1_4bit_grp1_mux),
-       FUNCTION("sd2_m0", sd2_grp0, &sd2_grp0_mux),
-       FUNCTION("sd2_no_cdb_m0", sd2_no_cdb_grp0, &sd2_no_cdb_grp0_mux),
+       FUNCTION("sd2_basic", sd2_basic_grp, &sd2_basic_grp_mux),
+       FUNCTION("sd2_cdb_m0", sd2_cdb_grp0, &sd2_cdb_grp0_mux),
+       FUNCTION("sd2_cdb_m1", sd2_cdb_grp1, &sd2_cdb_grp1_mux),
+       FUNCTION("sd2_wpb_m0", sd2_wpb_grp0, &sd2_wpb_grp0_mux),
+       FUNCTION("sd2_wpb_m1", sd2_wpb_grp1, &sd2_wpb_grp1_mux),
        FUNCTION("sd3", sd3_grp, &sd3_grp_mux),
        FUNCTION("sd5", sd5_grp, &sd5_grp_mux),
        FUNCTION("sd6_m0", sd6_grp0, &sd6_grp0_mux),
@@ -3471,23 +4699,47 @@ static struct atlas7_pmx_func atlas7_pmx_functions[] = {
        FUNCTION("uart0", uart0_grp, &uart0_grp_mux),
        FUNCTION("uart0_nopause", uart0_nopause_grp, &uart0_nopause_grp_mux),
        FUNCTION("uart1", uart1_grp, &uart1_grp_mux),
-       FUNCTION("uart2", uart2_grp, &uart2_grp_mux),
-       FUNCTION("uart3_m0", uart3_grp0, &uart3_grp0_mux),
-       FUNCTION("uart3_m1", uart3_grp1, &uart3_grp1_mux),
-       FUNCTION("uart3_m2", uart3_grp2, &uart3_grp2_mux),
-       FUNCTION("uart3_m3", uart3_grp3, &uart3_grp3_mux),
-       FUNCTION("uart3_nopause_m0",
-                       uart3_nopause_grp0,
-                       &uart3_nopause_grp0_mux),
-       FUNCTION("uart3_nopause_m1",
-                       uart3_nopause_grp1,
-                       &uart3_nopause_grp1_mux),
-       FUNCTION("uart4_m0", uart4_grp0, &uart4_grp0_mux),
-       FUNCTION("uart4_m1", uart4_grp1, &uart4_grp1_mux),
-       FUNCTION("uart4_m2", uart4_grp2, &uart4_grp2_mux),
-       FUNCTION("uart4_nopause", uart4_nopause_grp, &uart4_nopause_grp_mux),
-       FUNCTION("usb0_drvvbus", usb0_drvvbus_grp, &usb0_drvvbus_grp_mux),
-       FUNCTION("usb1_drvvbus", usb1_drvvbus_grp, &usb1_drvvbus_grp_mux),
+       FUNCTION("uart2_cts_m0", uart2_cts_grp0, &uart2_cts_grp0_mux),
+       FUNCTION("uart2_cts_m1", uart2_cts_grp1, &uart2_cts_grp1_mux),
+       FUNCTION("uart2_rts_m0", uart2_rts_grp0, &uart2_rts_grp0_mux),
+       FUNCTION("uart2_rts_m1", uart2_rts_grp1, &uart2_rts_grp1_mux),
+       FUNCTION("uart2_rxd_m0", uart2_rxd_grp0, &uart2_rxd_grp0_mux),
+       FUNCTION("uart2_rxd_m1", uart2_rxd_grp1, &uart2_rxd_grp1_mux),
+       FUNCTION("uart2_rxd_m2", uart2_rxd_grp2, &uart2_rxd_grp2_mux),
+       FUNCTION("uart2_txd_m0", uart2_txd_grp0, &uart2_txd_grp0_mux),
+       FUNCTION("uart2_txd_m1", uart2_txd_grp1, &uart2_txd_grp1_mux),
+       FUNCTION("uart2_txd_m2", uart2_txd_grp2, &uart2_txd_grp2_mux),
+       FUNCTION("uart3_cts_m0", uart3_cts_grp0, &uart3_cts_grp0_mux),
+       FUNCTION("uart3_cts_m1", uart3_cts_grp1, &uart3_cts_grp1_mux),
+       FUNCTION("uart3_cts_m2", uart3_cts_grp2, &uart3_cts_grp2_mux),
+       FUNCTION("uart3_rts_m0", uart3_rts_grp0, &uart3_rts_grp0_mux),
+       FUNCTION("uart3_rts_m1", uart3_rts_grp1, &uart3_rts_grp1_mux),
+       FUNCTION("uart3_rts_m2", uart3_rts_grp2, &uart3_rts_grp2_mux),
+       FUNCTION("uart3_rxd_m0", uart3_rxd_grp0, &uart3_rxd_grp0_mux),
+       FUNCTION("uart3_rxd_m1", uart3_rxd_grp1, &uart3_rxd_grp1_mux),
+       FUNCTION("uart3_rxd_m2", uart3_rxd_grp2, &uart3_rxd_grp2_mux),
+       FUNCTION("uart3_txd_m0", uart3_txd_grp0, &uart3_txd_grp0_mux),
+       FUNCTION("uart3_txd_m1", uart3_txd_grp1, &uart3_txd_grp1_mux),
+       FUNCTION("uart3_txd_m2", uart3_txd_grp2, &uart3_txd_grp2_mux),
+       FUNCTION("uart4_basic", uart4_basic_grp, &uart4_basic_grp_mux),
+       FUNCTION("uart4_cts_m0", uart4_cts_grp0, &uart4_cts_grp0_mux),
+       FUNCTION("uart4_cts_m1", uart4_cts_grp1, &uart4_cts_grp1_mux),
+       FUNCTION("uart4_cts_m2", uart4_cts_grp2, &uart4_cts_grp2_mux),
+       FUNCTION("uart4_rts_m0", uart4_rts_grp0, &uart4_rts_grp0_mux),
+       FUNCTION("uart4_rts_m1", uart4_rts_grp1, &uart4_rts_grp1_mux),
+       FUNCTION("uart4_rts_m2", uart4_rts_grp2, &uart4_rts_grp2_mux),
+       FUNCTION("usb0_drvvbus_m0",
+                       usb0_drvvbus_grp0,
+                       &usb0_drvvbus_grp0_mux),
+       FUNCTION("usb0_drvvbus_m1",
+                       usb0_drvvbus_grp1,
+                       &usb0_drvvbus_grp1_mux),
+       FUNCTION("usb1_drvvbus_m0",
+                       usb1_drvvbus_grp0,
+                       &usb1_drvvbus_grp0_mux),
+       FUNCTION("usb1_drvvbus_m1",
+                       usb1_drvvbus_grp1,
+                       &usb1_drvvbus_grp1_mux),
        FUNCTION("visbus_dout", visbus_dout_grp, &visbus_dout_grp_mux),
        FUNCTION("vi_vip1", vi_vip1_grp, &vi_vip1_grp_mux),
        FUNCTION("vi_vip1_ext", vi_vip1_ext_grp, &vi_vip1_ext_grp_mux),
index ae27872ff3a61ae70108aec4e886e18c9d281460..e68fd951129a4df5557c4007776fb5aa5891bf2c 100644 (file)
@@ -42,6 +42,10 @@ config PINCTRL_SUN8I_A33
        def_bool MACH_SUN8I
        select PINCTRL_SUNXI_COMMON
 
+config PINCTRL_SUN8I_A83T
+       def_bool MACH_SUN8I
+       select PINCTRL_SUNXI_COMMON
+
 config PINCTRL_SUN8I_A23_R
        def_bool MACH_SUN8I
        depends on RESET_CONTROLLER
index 227a1213947c817726e18b66a6e8c0df04783670..e080290345107d60a52dea9ec054949879f2a5f2 100644 (file)
@@ -12,4 +12,5 @@ obj-$(CONFIG_PINCTRL_SUN7I_A20)               += pinctrl-sun7i-a20.o
 obj-$(CONFIG_PINCTRL_SUN8I_A23)                += pinctrl-sun8i-a23.o
 obj-$(CONFIG_PINCTRL_SUN8I_A23_R)      += pinctrl-sun8i-a23-r.o
 obj-$(CONFIG_PINCTRL_SUN8I_A33)                += pinctrl-sun8i-a33.o
+obj-$(CONFIG_PINCTRL_SUN8I_A83T)       += pinctrl-sun8i-a83t.o
 obj-$(CONFIG_PINCTRL_SUN9I_A80)                += pinctrl-sun9i-a80.o
index 9596b0a3df6be7f1b4cdfb238ae4d29f94538d0e..d4bc4f0e8be02814473b67bdcc724fa7676fe430 100644 (file)
@@ -47,45 +47,57 @@ static const struct sunxi_desc_pin sun6i_a31_r_pins[] = {
        SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 5),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
                  SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 0, 0),   /* PL_EINT0 */
                  SUNXI_FUNCTION(0x3, "s_jtag")),       /* MS */
        SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 6),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
                  SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 0, 1),   /* PL_EINT1 */
                  SUNXI_FUNCTION(0x3, "s_jtag")),       /* CK */
        SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 7),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
                  SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 0, 2),   /* PL_EINT2 */
                  SUNXI_FUNCTION(0x3, "s_jtag")),       /* DO */
        SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 8),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
                  SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 0, 3),   /* PL_EINT3 */
                  SUNXI_FUNCTION(0x3, "s_jtag")),       /* DI */
        /* Hole */
        SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 0),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
-                 SUNXI_FUNCTION(0x1, "gpio_out")),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 1, 0)),  /* PM_EINT0 */
        SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 1),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
-                 SUNXI_FUNCTION(0x1, "gpio_out")),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 1, 1)),  /* PM_EINT1 */
        SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 2),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
                  SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 1, 2),   /* PM_EINT2 */
                  SUNXI_FUNCTION(0x3, "1wire")),
        SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 3),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
-                 SUNXI_FUNCTION(0x1, "gpio_out")),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 1, 3)),  /* PM_EINT3 */
        SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 4),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
-                 SUNXI_FUNCTION(0x1, "gpio_out")),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 1, 4)),  /* PM_EINT4 */
        SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 5),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
-                 SUNXI_FUNCTION(0x1, "gpio_out")),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 1, 5)),  /* PM_EINT5 */
        SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 6),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
-                 SUNXI_FUNCTION(0x1, "gpio_out")),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 1, 6)),  /* PM_EINT6 */
        SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 7),
                  SUNXI_FUNCTION(0x0, "gpio_in"),
                  SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x2, 1, 7),   /* PM_EINT7 */
                  SUNXI_FUNCTION(0x3, "rtc")),          /* CLKO */
 };
 
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c
new file mode 100644 (file)
index 0000000..90b973e
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+ * Allwinner a83t SoCs pinctrl driver.
+ *
+ * Copyright (C) 2015 Vishnu Patekar <vishnupatekar0510@gmail.com>
+ *
+ * Based on pinctrl-sun8i-a23.c, which is:
+ * Copyright (C) 2014 Chen-Yu Tsai <wens@csie.org>
+ * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.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/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-sunxi.h"
+
+static const struct sunxi_desc_pin sun8i_a83t_pins[] = {
+       /* Hole */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "uart2"),         /* TX */
+                 SUNXI_FUNCTION(0x3, "jtag"),          /* MS0 */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),  /* PB_EINT0 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "uart2"),         /* RX */
+                 SUNXI_FUNCTION(0x3, "jtag"),          /* CK0 */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),  /* PB_EINT1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "uart2"),         /* RTS */
+                 SUNXI_FUNCTION(0x3, "jtag"),          /* DO0 */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),  /* PB_EINT2 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "uart2"),         /* CTS */
+                 SUNXI_FUNCTION(0x3, "jtag"),          /* DI0 */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),  /* PB_EINT3 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2s0"),          /* LRCK */
+                 SUNXI_FUNCTION(0x3, "tdm"),           /* LRCK */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),  /* PB_EINT4 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2s0"),          /* BCLK */
+                 SUNXI_FUNCTION(0x3, "tdm"),           /* BCLK */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),  /* PB_EINT5 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2s0"),          /* DOUT */
+                 SUNXI_FUNCTION(0x3, "tdm"),           /* DOUT */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),  /* PB_EINT6 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2s0"),          /* DIN */
+                 SUNXI_FUNCTION(0x3, "tdm"),           /* DIN */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),  /* PB_EINT7 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2s0"),          /* MCLK */
+                 SUNXI_FUNCTION(0x3, "tdm"),           /* MCLK */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),  /* PB_EINT8 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "uart0"),         /* TX */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),  /* PB_EINT9 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 10),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "uart0"),         /* RX */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 10)), /* PB_EINT10 */
+       /* Hole */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* WE */
+                 SUNXI_FUNCTION(0x3, "spi0")),         /* MOSI */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* ALE */
+                 SUNXI_FUNCTION(0x3, "spi0")),         /* MISO */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* CLE */
+                 SUNXI_FUNCTION(0x3, "spi0")),         /* CLK */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* CE1 */
+                 SUNXI_FUNCTION(0x3, "spi0")),         /* CS */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0")),        /* CE0 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* RE */
+                 SUNXI_FUNCTION(0x3, "mmc2")),         /* CLK */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* RB0 */
+                 SUNXI_FUNCTION(0x3, "mmc2")),         /* CMD */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0")),        /* RB1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 8),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* DQ0 */
+                 SUNXI_FUNCTION(0x3, "mmc2")),         /* D0 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 9),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* DQ1 */
+                 SUNXI_FUNCTION(0x3, "mmc2")),         /* D1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 10),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* DQ2 */
+                 SUNXI_FUNCTION(0x3, "mmc2")),         /* D2 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 11),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* DQ3 */
+                 SUNXI_FUNCTION(0x3, "mmc2")),         /* D3 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 12),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* DQ4 */
+                 SUNXI_FUNCTION(0x3, "mmc2")),         /* D4 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 13),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand0"),         /* DQ5 */
+                 SUNXI_FUNCTION(0x3, "mmc2")),         /* D5 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 14),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand"),          /* DQ6 */
+                 SUNXI_FUNCTION(0x3, "mmc2")),         /* D6 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 15),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand"),          /* DQ7 */
+                 SUNXI_FUNCTION(0x3, "mmc2")),         /* D7 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 16),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand"),          /* DQS */
+                 SUNXI_FUNCTION(0x3, "mmc2")),         /* RST */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 17),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand")),         /* CE2 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 18),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "nand")),         /* CE3 */
+       /* Hole */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D2 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* RGMII / MII RXD3 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D3 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* RGMII / MII RXD2 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D4 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* RGMII / MII RXD1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D5 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* RGMII / MII RXD0 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D6 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* RGMII / MII RXCK */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D7 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* RGMII / MII RXDV */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D10 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* RGMII / MII RXERR */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D11 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* RGMII / MII TXD3 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D12 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* RGMII / MII TXD2 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D13 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* RGMII / MII TXD1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D14 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* RGMII / MII TXD0 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D15 */
+                 SUNXI_FUNCTION(0x4, "gmac")), /* RGMII-NULL / MII-CRS */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D18 */
+                 SUNXI_FUNCTION(0x3, "lvds0"),         /* VP0 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* GTXCK / ETXCK */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D19 */
+                 SUNXI_FUNCTION(0x3, "lvds0"),         /* VN0 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* GTXCTL / ETXEL */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D20 */
+                 SUNXI_FUNCTION(0x3, "lvds0"),         /* VP1 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* GNULL / ETXERR */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D21 */
+                 SUNXI_FUNCTION(0x3, "lvds0"),         /* VN1 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* GCLKIN / ECOL */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D22 */
+                 SUNXI_FUNCTION(0x3, "lvds0"),         /* VP2 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* GMDC */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 23),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* D23 */
+                 SUNXI_FUNCTION(0x3, "lvds0"),         /* VN2 */
+                 SUNXI_FUNCTION(0x4, "gmac")),         /* GMDIO */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 24),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* CLK */
+                 SUNXI_FUNCTION(0x3, "lvds0")),        /* VPC */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 25),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* DE */
+                 SUNXI_FUNCTION(0x3, "lvds0")),        /* VNC */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 26),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* HSYNC */
+                 SUNXI_FUNCTION(0x3, "lvds0")),        /* VP3 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 27),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "lcd0"),          /* VSYNC */
+                 SUNXI_FUNCTION(0x3, "lvds0")),        /* VN3 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 28),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "pwm")),          /* PWM */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 29),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out")),
+       /* Hole */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* PCLK */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* CLK */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* MCLK */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* DE */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* HSYNC */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* HSYNC */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* VSYNC */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* VSYNC */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi")),          /* D0 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi")),          /* D1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* D2 */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* D0 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* D3 */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* D1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* D4 */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* D2 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* D5 */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* D3 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* D6 */
+                 SUNXI_FUNCTION(0x3, "uart4"),         /* TX */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* D4 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* D7 */
+                 SUNXI_FUNCTION(0x3, "uart4"),         /* RX */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* D5 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* D8 */
+                 SUNXI_FUNCTION(0x3, "uart4"),         /* RTS */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* D6 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* D9 */
+                 SUNXI_FUNCTION(0x3, "uart4"),         /* CTS */
+                 SUNXI_FUNCTION(0x4, "ccir")),         /* D7 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* SCK */
+                 SUNXI_FUNCTION(0x3, "i2c2")),         /* SCK */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "csi"),           /* SDA */
+                 SUNXI_FUNCTION(0x3, "i2c2")),         /* SDA */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out")),
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out")),
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 18),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x3, "owa")),          /* DOUT */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 19),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out")),
+       /* Hole */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc0"),          /* D1 */
+                 SUNXI_FUNCTION(0x3, "jtag")),         /* MS1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc0"),          /* D0 */
+                 SUNXI_FUNCTION(0x3, "jtag")),         /* DI1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc0"),          /* CLK */
+                 SUNXI_FUNCTION(0x3, "uart0")),        /* TX */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc0"),          /* CMD */
+                 SUNXI_FUNCTION(0x3, "jtag")),         /* DO1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc0"),          /* D3 */
+                 SUNXI_FUNCTION(0x3, "uart0")),        /* RX */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc0"),          /* D2 */
+                 SUNXI_FUNCTION(0x3, "jtag")),         /* CK1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out")),
+       /* Hole */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc1"),          /* CLK */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)),  /* PG_EINT0 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc1"),          /* CMD */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)),  /* PG_EINT1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc1"),          /* D0 */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)),  /* PG_EINT2 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc1"),          /* D1 */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)),  /* PG_EINT3 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc1"),          /* D2 */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)),  /* PG_EINT4 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "mmc1"),          /* D3 */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)),  /* PG_EINT5 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "uart1"),         /* TX */
+                 SUNXI_FUNCTION(0x3, "spi1"),          /* CS */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)),  /* PG_EINT6 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "uart1"),         /* RX */
+                 SUNXI_FUNCTION(0x3, "spi1"),          /* CLK */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)),  /* PG_EINT7 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "uart1"),         /* RTS */
+                 SUNXI_FUNCTION(0x3, "spi1"),          /* MOSI */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)),  /* PG_EINT8 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "uart1"),         /* CTS */
+                 SUNXI_FUNCTION(0x3, "spi1"),          /* MISO */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)),  /* PG_EINT9 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2s1"),          /* BCLK */
+                 SUNXI_FUNCTION(0x3, "uart3"),         /* TX */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)), /* PG_EINT10 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2s1"),          /* LRCK */
+                 SUNXI_FUNCTION(0x3, "uart3"),         /* RX */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)), /* PG_EINT11 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2s1"),          /* DOUT */
+                 SUNXI_FUNCTION(0x3, "uart3"),         /* RTS */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)), /* PG_EINT12 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2s1"),          /* DIN */
+                 SUNXI_FUNCTION(0x3, "uart3"),         /* CTS */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 13)), /* PG_EINT13 */
+       /* Hole */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 0),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2c0"),          /* SCK */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 0)),  /* PH_EINT0 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 1),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2c0"),          /* SDA */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)),  /* PH_EINT1 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 2),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2c1"),          /* SCK */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 2)),  /* PH_EINT2 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 3),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2c1"),          /* SDA */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 3)),  /* PH_EINT3 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 4),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2c2"),          /* SCK */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 4)),  /* PH_EINT4 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 5),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "i2c2"),          /* SDA */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 5)),  /* PH_EINT5 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 6),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "hdmi"),          /* HSCL */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 6)),  /* PH_EINT6 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 7),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "hdmi"),          /* HSDA */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 7)),  /* PH_EINT7 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 8),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION(0x2, "hdmi"),          /* HCEC */
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 8)),  /* PH_EINT8 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 9),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 9)),  /* PH_EINT9 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 10),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 10)), /* PH_EINT10 */
+       SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 11),
+                 SUNXI_FUNCTION(0x0, "gpio_in"),
+                 SUNXI_FUNCTION(0x1, "gpio_out"),
+                 SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)),  /* PH_EINT11 */
+};
+
+static const struct sunxi_pinctrl_desc sun8i_a83t_pinctrl_data = {
+       .pins = sun8i_a83t_pins,
+       .npins = ARRAY_SIZE(sun8i_a83t_pins),
+       .irq_banks = 3,
+};
+
+static int sun8i_a83t_pinctrl_probe(struct platform_device *pdev)
+{
+       return sunxi_pinctrl_init(pdev,
+                                 &sun8i_a83t_pinctrl_data);
+}
+
+static const struct of_device_id sun8i_a83t_pinctrl_match[] = {
+       { .compatible = "allwinner,sun8i-a83t-pinctrl", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, sun8i_a83t_pinctrl_match);
+
+static struct platform_driver sun8i_a83t_pinctrl_driver = {
+       .probe  = sun8i_a83t_pinctrl_probe,
+       .driver = {
+               .name           = "sun8i-a83t-pinctrl",
+               .of_match_table = sun8i_a83t_pinctrl_match,
+       },
+};
+module_platform_driver(sun8i_a83t_pinctrl_driver);
+
+MODULE_AUTHOR("Vishnu Patekar <vishnupatekar0510@gmail.com>");
+MODULE_DESCRIPTION("Allwinner a83t pinctrl driver");
+MODULE_LICENSE("GPL");
index 38e0c7bdd2ac456362a7e2b845c0250be5e60411..dead97daca35fe2e59a0e81f7898b4e66f27b0cb 100644 (file)
@@ -446,16 +446,6 @@ static const struct pinmux_ops sunxi_pmx_ops = {
        .gpio_set_direction     = sunxi_pmx_gpio_set_direction,
 };
 
-static int sunxi_pinctrl_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void sunxi_pinctrl_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static int sunxi_pinctrl_gpio_direction_input(struct gpio_chip *chip,
                                        unsigned offset)
 {
@@ -716,6 +706,7 @@ static int sunxi_pinctrl_irq_of_xlate(struct irq_domain *d,
                                      unsigned long *out_hwirq,
                                      unsigned int *out_type)
 {
+       struct sunxi_pinctrl *pctl = d->host_data;
        struct sunxi_desc_function *desc;
        int pin, base;
 
@@ -723,10 +714,9 @@ static int sunxi_pinctrl_irq_of_xlate(struct irq_domain *d,
                return -EINVAL;
 
        base = PINS_PER_BANK * intspec[0];
-       pin = base + intspec[1];
+       pin = pctl->desc->pin_base + base + intspec[1];
 
-       desc = sunxi_pinctrl_desc_find_function_by_pin(d->host_data,
-                                                      pin, "irq");
+       desc = sunxi_pinctrl_desc_find_function_by_pin(pctl, pin, "irq");
        if (!desc)
                return -EINVAL;
 
@@ -956,8 +946,8 @@ int sunxi_pinctrl_init(struct platform_device *pdev,
 
        last_pin = pctl->desc->pins[pctl->desc->npins - 1].pin.number;
        pctl->chip->owner = THIS_MODULE;
-       pctl->chip->request = sunxi_pinctrl_gpio_request,
-       pctl->chip->free = sunxi_pinctrl_gpio_free,
+       pctl->chip->request = gpiochip_generic_request,
+       pctl->chip->free = gpiochip_generic_free,
        pctl->chip->direction_input = sunxi_pinctrl_gpio_direction_input,
        pctl->chip->direction_output = sunxi_pinctrl_gpio_direction_output,
        pctl->chip->get = sunxi_pinctrl_gpio_get,
@@ -1029,7 +1019,7 @@ int sunxi_pinctrl_init(struct platform_device *pdev,
                irq_set_chip_and_handler(irqno, &sunxi_pinctrl_edge_irq_chip,
                                         handle_edge_irq);
                irq_set_chip_data(irqno, pctl);
-       };
+       }
 
        for (i = 0; i < pctl->desc->irq_banks; i++) {
                /* Mask and clear all IRQs before registering a handler */
index eab23ef9ddbf9a2b9a23ec1a826258eecd2fb065..ad907072e09fe191e92096e6fd3de6e88f0993e2 100644 (file)
@@ -1,32 +1,32 @@
 if ARCH_UNIPHIER
 
-config PINCTRL_UNIPHIER_CORE
+config PINCTRL_UNIPHIER
        bool
        select PINMUX
        select GENERIC_PINCONF
 
 config PINCTRL_UNIPHIER_PH1_LD4
        tristate "UniPhier PH1-LD4 SoC pinctrl driver"
-       select PINCTRL_UNIPHIER_CORE
+       select PINCTRL_UNIPHIER
 
 config PINCTRL_UNIPHIER_PH1_PRO4
        tristate "UniPhier PH1-Pro4 SoC pinctrl driver"
-       select PINCTRL_UNIPHIER_CORE
+       select PINCTRL_UNIPHIER
 
 config PINCTRL_UNIPHIER_PH1_SLD8
        tristate "UniPhier PH1-sLD8 SoC pinctrl driver"
-       select PINCTRL_UNIPHIER_CORE
+       select PINCTRL_UNIPHIER
 
 config PINCTRL_UNIPHIER_PH1_PRO5
        tristate "UniPhier PH1-Pro5 SoC pinctrl driver"
-       select PINCTRL_UNIPHIER_CORE
+       select PINCTRL_UNIPHIER
 
 config PINCTRL_UNIPHIER_PROXSTREAM2
        tristate "UniPhier ProXstream2 SoC pinctrl driver"
-       select PINCTRL_UNIPHIER_CORE
+       select PINCTRL_UNIPHIER
 
 config PINCTRL_UNIPHIER_PH1_LD6B
        tristate "UniPhier PH1-LD6b SoC pinctrl driver"
-       select PINCTRL_UNIPHIER_CORE
+       select PINCTRL_UNIPHIER
 
 endif
index e215b1097297a585298d855c2121af9222c0e1e9..e7ce9670306cb15399f7aee571cb63fd7c310c98 100644 (file)
@@ -1,4 +1,4 @@
-obj-$(CONFIG_PINCTRL_UNIPHIER_CORE)            += pinctrl-uniphier-core.o
+obj-y                                          += pinctrl-uniphier-core.o
 
 obj-$(CONFIG_PINCTRL_UNIPHIER_PH1_LD4)         += pinctrl-ph1-ld4.o
 obj-$(CONFIG_PINCTRL_UNIPHIER_PH1_PRO4)                += pinctrl-ph1-pro4.o
index 7beb87e8f499af55ce6afc267bbef91f9fe806bd..a7056dccfa5385a9854e475cc538a5642636f8dd 100644 (file)
@@ -537,6 +537,8 @@ static const unsigned nand_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                        0, 0};
 static const unsigned nand_cs1_pins[] = {22, 23};
 static const unsigned nand_cs1_muxvals[] = {0, 0};
+static const unsigned sd_pins[] = {44, 45, 46, 47, 48, 49, 50, 51, 52};
+static const unsigned sd_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 static const unsigned uart0_pins[] = {85, 88};
 static const unsigned uart0_muxvals[] = {1, 1};
 static const unsigned uart1_pins[] = {155, 156};
@@ -619,6 +621,7 @@ static const struct uniphier_pinctrl_group ph1_ld4_groups[] = {
        UNIPHIER_PINCTRL_GROUP(i2c3),
        UNIPHIER_PINCTRL_GROUP(nand),
        UNIPHIER_PINCTRL_GROUP(nand_cs1),
+       UNIPHIER_PINCTRL_GROUP(sd),
        UNIPHIER_PINCTRL_GROUP(uart0),
        UNIPHIER_PINCTRL_GROUP(uart1),
        UNIPHIER_PINCTRL_GROUP(uart1b),
@@ -776,6 +779,7 @@ static const char * const i2c1_groups[] = {"i2c1"};
 static const char * const i2c2_groups[] = {"i2c2"};
 static const char * const i2c3_groups[] = {"i2c3"};
 static const char * const nand_groups[] = {"nand", "nand_cs1"};
+static const char * const sd_groups[] = {"sd"};
 static const char * const uart0_groups[] = {"uart0"};
 static const char * const uart1_groups[] = {"uart1", "uart1b"};
 static const char * const uart2_groups[] = {"uart2"};
@@ -831,6 +835,7 @@ static const struct uniphier_pinmux_function ph1_ld4_functions[] = {
        UNIPHIER_PINMUX_FUNCTION(i2c2),
        UNIPHIER_PINMUX_FUNCTION(i2c3),
        UNIPHIER_PINMUX_FUNCTION(nand),
+       UNIPHIER_PINMUX_FUNCTION(sd),
        UNIPHIER_PINMUX_FUNCTION(uart0),
        UNIPHIER_PINMUX_FUNCTION(uart1),
        UNIPHIER_PINMUX_FUNCTION(uart2),
index 9720e697fbc1f94f019eac5533452f94576e297d..1824831bb4da6d7fc5d0d4a01b5a909130954ad7 100644 (file)
@@ -761,6 +761,8 @@ static const unsigned nand_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                        0, 0};
 static const unsigned nand_cs1_pins[] = {37, 38};
 static const unsigned nand_cs1_muxvals[] = {0, 0};
+static const unsigned sd_pins[] = {47, 48, 49, 50, 51, 52, 53, 54, 55};
+static const unsigned sd_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 static const unsigned uart0_pins[] = {135, 136};
 static const unsigned uart0_muxvals[] = {3, 3};
 static const unsigned uart0b_pins[] = {11, 12};
@@ -866,6 +868,7 @@ static const struct uniphier_pinctrl_group ph1_ld6b_groups[] = {
        UNIPHIER_PINCTRL_GROUP(i2c3),
        UNIPHIER_PINCTRL_GROUP(nand),
        UNIPHIER_PINCTRL_GROUP(nand_cs1),
+       UNIPHIER_PINCTRL_GROUP(sd),
        UNIPHIER_PINCTRL_GROUP(uart0),
        UNIPHIER_PINCTRL_GROUP(uart0b),
        UNIPHIER_PINCTRL_GROUP(uart1),
@@ -1136,6 +1139,7 @@ static const char * const i2c1_groups[] = {"i2c1"};
 static const char * const i2c2_groups[] = {"i2c2"};
 static const char * const i2c3_groups[] = {"i2c3"};
 static const char * const nand_groups[] = {"nand", "nand_cs1"};
+static const char * const sd_groups[] = {"sd"};
 static const char * const uart0_groups[] = {"uart0", "uart0b"};
 static const char * const uart1_groups[] = {"uart1", "uart1b"};
 static const char * const uart2_groups[] = {"uart2", "uart2b"};
@@ -1219,6 +1223,7 @@ static const struct uniphier_pinmux_function ph1_ld6b_functions[] = {
        UNIPHIER_PINMUX_FUNCTION(i2c2),
        UNIPHIER_PINMUX_FUNCTION(i2c3),
        UNIPHIER_PINMUX_FUNCTION(nand),
+       UNIPHIER_PINMUX_FUNCTION(sd),
        UNIPHIER_PINMUX_FUNCTION(uart0),
        UNIPHIER_PINMUX_FUNCTION(uart1),
        UNIPHIER_PINMUX_FUNCTION(uart2),
index 96921e40da5f9bc50f8f88b576569a5b18530256..ec8e92dfaf8c71394a658ec519856e72bc57f3ce 100644 (file)
@@ -1031,6 +1031,11 @@ static const unsigned nand_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                        0, 0};
 static const unsigned nand_cs1_pins[] = {131, 132};
 static const unsigned nand_cs1_muxvals[] = {1, 1};
+static const unsigned sd_pins[] = {150, 151, 152, 153, 154, 155, 156, 157, 158};
+static const unsigned sd_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const unsigned sd1_pins[] = {319, 320, 321, 322, 323, 324, 325, 326,
+                                   327};
+static const unsigned sd1_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 static const unsigned uart0_pins[] = {127, 128};
 static const unsigned uart0_muxvals[] = {0, 0};
 static const unsigned uart1_pins[] = {129, 130};
@@ -1140,6 +1145,8 @@ static const struct uniphier_pinctrl_group ph1_pro4_groups[] = {
        UNIPHIER_PINCTRL_GROUP(i2c6),
        UNIPHIER_PINCTRL_GROUP(nand),
        UNIPHIER_PINCTRL_GROUP(nand_cs1),
+       UNIPHIER_PINCTRL_GROUP(sd),
+       UNIPHIER_PINCTRL_GROUP(sd1),
        UNIPHIER_PINCTRL_GROUP(uart0),
        UNIPHIER_PINCTRL_GROUP(uart1),
        UNIPHIER_PINCTRL_GROUP(uart2),
@@ -1412,6 +1419,8 @@ static const char * const i2c2_groups[] = {"i2c2"};
 static const char * const i2c3_groups[] = {"i2c3"};
 static const char * const i2c6_groups[] = {"i2c6"};
 static const char * const nand_groups[] = {"nand", "nand_cs1"};
+static const char * const sd_groups[] = {"sd"};
+static const char * const sd1_groups[] = {"sd1"};
 static const char * const uart0_groups[] = {"uart0"};
 static const char * const uart1_groups[] = {"uart1"};
 static const char * const uart2_groups[] = {"uart2"};
@@ -1498,6 +1507,8 @@ static const struct uniphier_pinmux_function ph1_pro4_functions[] = {
        UNIPHIER_PINMUX_FUNCTION(i2c3),
        UNIPHIER_PINMUX_FUNCTION(i2c6),
        UNIPHIER_PINMUX_FUNCTION(nand),
+       UNIPHIER_PINMUX_FUNCTION(sd),
+       UNIPHIER_PINMUX_FUNCTION(sd1),
        UNIPHIER_PINMUX_FUNCTION(uart0),
        UNIPHIER_PINMUX_FUNCTION(uart1),
        UNIPHIER_PINMUX_FUNCTION(uart2),
index 9af455978058cde2231a6398b3057041eec357bf..e3d648eae85afe0e9f2a1022ada182b182557e53 100644 (file)
@@ -818,6 +818,8 @@ static const unsigned nand_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                        0, 0};
 static const unsigned nand_cs1_pins[] = {26, 27};
 static const unsigned nand_cs1_muxvals[] = {0, 0};
+static const unsigned sd_pins[] = {250, 251, 252, 253, 254, 255, 256, 257, 258};
+static const unsigned sd_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 static const unsigned uart0_pins[] = {47, 48};
 static const unsigned uart0_muxvals[] = {0, 0};
 static const unsigned uart0b_pins[] = {227, 228};
@@ -930,6 +932,7 @@ static const struct uniphier_pinctrl_group ph1_pro5_groups[] = {
        UNIPHIER_PINCTRL_GROUP(i2c5b),
        UNIPHIER_PINCTRL_GROUP(i2c5c),
        UNIPHIER_PINCTRL_GROUP(i2c6),
+       UNIPHIER_PINCTRL_GROUP(sd),
        UNIPHIER_PINCTRL_GROUP(uart0),
        UNIPHIER_PINCTRL_GROUP(uart0b),
        UNIPHIER_PINCTRL_GROUP(uart1),
@@ -1209,6 +1212,7 @@ static const char * const i2c3_groups[] = {"i2c3"};
 static const char * const i2c5_groups[] = {"i2c5", "i2c5b", "i2c5c"};
 static const char * const i2c6_groups[] = {"i2c6"};
 static const char * const nand_groups[] = {"nand", "nand_cs1"};
+static const char * const sd_groups[] = {"sd"};
 static const char * const uart0_groups[] = {"uart0", "uart0b"};
 static const char * const uart1_groups[] = {"uart1"};
 static const char * const uart2_groups[] = {"uart2"};
@@ -1296,6 +1300,7 @@ static const struct uniphier_pinmux_function ph1_pro5_functions[] = {
        UNIPHIER_PINMUX_FUNCTION(i2c5),
        UNIPHIER_PINMUX_FUNCTION(i2c6),
        UNIPHIER_PINMUX_FUNCTION(nand),
+       UNIPHIER_PINMUX_FUNCTION(sd),
        UNIPHIER_PINMUX_FUNCTION(uart0),
        UNIPHIER_PINMUX_FUNCTION(uart1),
        UNIPHIER_PINMUX_FUNCTION(uart2),
index 2df8bbecebfc4c5742e5652386186ce67a7aba7e..c3700a33a5da406541c3e3415ae075bedebd8cf2 100644 (file)
@@ -450,6 +450,8 @@ static const unsigned nand_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                        0, 0};
 static const unsigned nand_cs1_pins[] = {22, 23};
 static const unsigned nand_cs1_muxvals[] = {0, 0};
+static const unsigned sd_pins[] = {32, 33, 34, 35, 36, 37, 38, 39, 40};
+static const unsigned sd_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 static const unsigned uart0_pins[] = {70, 71};
 static const unsigned uart0_muxvals[] = {3, 3};
 static const unsigned uart1_pins[] = {114, 115};
@@ -536,6 +538,7 @@ static const struct uniphier_pinctrl_group ph1_sld8_groups[] = {
        UNIPHIER_PINCTRL_GROUP(i2c3),
        UNIPHIER_PINCTRL_GROUP(nand),
        UNIPHIER_PINCTRL_GROUP(nand_cs1),
+       UNIPHIER_PINCTRL_GROUP(sd),
        UNIPHIER_PINCTRL_GROUP(uart0),
        UNIPHIER_PINCTRL_GROUP(uart1),
        UNIPHIER_PINCTRL_GROUP(uart2),
@@ -684,6 +687,7 @@ static const char * const i2c1_groups[] = {"i2c1"};
 static const char * const i2c2_groups[] = {"i2c2"};
 static const char * const i2c3_groups[] = {"i2c3"};
 static const char * const nand_groups[] = {"nand", "nand_cs1"};
+static const char * const sd_groups[] = {"sd"};
 static const char * const uart0_groups[] = {"uart0"};
 static const char * const uart1_groups[] = {"uart1"};
 static const char * const uart2_groups[] = {"uart2"};
@@ -739,6 +743,7 @@ static const struct uniphier_pinmux_function ph1_sld8_functions[] = {
        UNIPHIER_PINMUX_FUNCTION(i2c2),
        UNIPHIER_PINMUX_FUNCTION(i2c3),
        UNIPHIER_PINMUX_FUNCTION(nand),
+       UNIPHIER_PINMUX_FUNCTION(sd),
        UNIPHIER_PINMUX_FUNCTION(uart0),
        UNIPHIER_PINMUX_FUNCTION(uart1),
        UNIPHIER_PINMUX_FUNCTION(uart2),
index 3f036e236ad90273f8dbd74c67ccdc76330d0cbb..bc00d7591c59b019ba77d55ae1fcd5c46f2e95d9 100644 (file)
@@ -751,6 +751,8 @@ static const unsigned nand_muxvals[] = {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
                                        8, 8};
 static const unsigned nand_cs1_pins[] = {37, 38};
 static const unsigned nand_cs1_muxvals[] = {8, 8};
+static const unsigned sd_pins[] = {47, 48, 49, 50, 51, 52, 53, 54, 55};
+static const unsigned sd_muxvals[] = {8, 8, 8, 8, 8, 8, 8, 8, 8};
 static const unsigned uart0_pins[] = {217, 218};
 static const unsigned uart0_muxvals[] = {8, 8};
 static const unsigned uart0b_pins[] = {179, 180};
@@ -857,6 +859,7 @@ static const struct uniphier_pinctrl_group proxstream2_groups[] = {
        UNIPHIER_PINCTRL_GROUP(i2c6),
        UNIPHIER_PINCTRL_GROUP(nand),
        UNIPHIER_PINCTRL_GROUP(nand_cs1),
+       UNIPHIER_PINCTRL_GROUP(sd),
        UNIPHIER_PINCTRL_GROUP(uart0),
        UNIPHIER_PINCTRL_GROUP(uart0b),
        UNIPHIER_PINCTRL_GROUP(uart1),
@@ -1128,6 +1131,7 @@ static const char * const i2c3_groups[] = {"i2c3"};
 static const char * const i2c5_groups[] = {"i2c5"};
 static const char * const i2c6_groups[] = {"i2c6"};
 static const char * const nand_groups[] = {"nand", "nand_cs1"};
+static const char * const sd_groups[] = {"sd"};
 static const char * const uart0_groups[] = {"uart0", "uart0b"};
 static const char * const uart1_groups[] = {"uart1"};
 static const char * const uart2_groups[] = {"uart2"};
@@ -1213,6 +1217,7 @@ static const struct uniphier_pinmux_function proxstream2_functions[] = {
        UNIPHIER_PINMUX_FUNCTION(i2c5),
        UNIPHIER_PINMUX_FUNCTION(i2c6),
        UNIPHIER_PINMUX_FUNCTION(nand),
+       UNIPHIER_PINMUX_FUNCTION(sd),
        UNIPHIER_PINMUX_FUNCTION(uart0),
        UNIPHIER_PINMUX_FUNCTION(uart1),
        UNIPHIER_PINMUX_FUNCTION(uart2),
index 918f3b643f1b00e15db2332a0417543621b11a60..589872cc8adbbd93bfd94a4b32c58f461b12b890 100644 (file)
@@ -539,6 +539,12 @@ static int uniphier_pmx_set_one_mux(struct pinctrl_dev *pctldev, unsigned pin,
        unsigned reg, reg_end, shift, mask;
        int ret;
 
+       /* some pins need input-enabling */
+       ret = uniphier_conf_pin_input_enable(pctldev,
+                                            &pctldev->desc->pins[pin], 1);
+       if (ret)
+               return ret;
+
        reg = UNIPHIER_PINCTRL_PINMUX_BASE + pin * mux_bits / 32 * reg_stride;
        reg_end = reg + reg_stride;
        shift = pin * mux_bits % 32;
@@ -563,9 +569,7 @@ static int uniphier_pmx_set_one_mux(struct pinctrl_dev *pctldev, unsigned pin,
                        return ret;
        }
 
-       /* some pins need input-enabling */
-       return uniphier_conf_pin_input_enable(pctldev,
-                                             &pctldev->desc->pins[pin], 1);
+       return 0;
 }
 
 static int uniphier_pmx_set_mux(struct pinctrl_dev *pctldev,
index c15316b003c578b430106f1a6f4a3c76b3b90d9b..fb22d3f62480d9f88f04cb4f93e7f0de45e5bf66 100644 (file)
@@ -486,16 +486,6 @@ static struct pinctrl_desc wmt_desc = {
        .confops = &wmt_pinconf_ops,
 };
 
-static int wmt_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
-       return pinctrl_request_gpio(chip->base + offset);
-}
-
-static void wmt_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
-       pinctrl_free_gpio(chip->base + offset);
-}
-
 static int wmt_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
 {
        struct wmt_pinctrl_data *data = dev_get_drvdata(chip->dev);
@@ -560,8 +550,8 @@ static int wmt_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
 static struct gpio_chip wmt_gpio_chip = {
        .label = "gpio-wmt",
        .owner = THIS_MODULE,
-       .request = wmt_gpio_request,
-       .free = wmt_gpio_free,
+       .request = gpiochip_generic_request,
+       .free = gpiochip_generic_free,
        .get_direction = wmt_gpio_get_direction,
        .direction_input = wmt_gpio_direction_input,
        .direction_output = wmt_gpio_direction_output,
index cdad4d95b20eed060e6bc961da9bb27ab23865fb..805c749ac1ad035975a36c00231a83395e55e102 100644 (file)
@@ -179,8 +179,8 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
        /* check event type */
        BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0);
 
-       dev_dbg(pps->dev, "PPS event at %ld.%09ld\n",
-                       ts->ts_real.tv_sec, ts->ts_real.tv_nsec);
+       dev_dbg(pps->dev, "PPS event at %lld.%09ld\n",
+                       (s64)ts->ts_real.tv_sec, ts->ts_real.tv_nsec);
 
        timespec_to_pps_ktime(&ts_real, ts->ts_real);
 
index b7d60306b0bcf185c683d5e29312738283c37a98..fc94bfdceb9537fa36ea5a83ef25844b57582ff8 100644 (file)
@@ -229,7 +229,7 @@ static struct mon_msg *mon_next_message(struct mon_private *monpriv)
 /******************************************************************************
  *                               IUCV handler                                 *
  *****************************************************************************/
-static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
+static void mon_iucv_path_complete(struct iucv_path *path, u8 *ipuser)
 {
        struct mon_private *monpriv = path->private;
 
@@ -237,7 +237,7 @@ static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
        wake_up(&mon_conn_wait_queue);
 }
 
-static void mon_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
+static void mon_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
 {
        struct mon_private *monpriv = path->private;
 
index 9bb48d70957cb254d6858e08d3db09c796df6c98..799c1524c779f282387b3ed79c5363f95b1fbd98 100644 (file)
@@ -99,8 +99,8 @@ static const struct file_operations vmlogrdr_fops = {
 };
 
 
-static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 ipuser[16]);
-static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 ipuser[16]);
+static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 *ipuser);
+static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 *ipuser);
 static void vmlogrdr_iucv_message_pending(struct iucv_path *,
                                          struct iucv_message *);
 
@@ -160,7 +160,7 @@ static struct cdev  *vmlogrdr_cdev = NULL;
 static int recording_class_AB;
 
 
-static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
+static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 *ipuser)
 {
        struct vmlogrdr_priv_t * logptr = path->private;
 
@@ -171,7 +171,7 @@ static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
 }
 
 
-static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
+static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
 {
        struct vmlogrdr_priv_t * logptr = path->private;
        u8 reason = (u8) ipuser[8];
index 848e3b64ea6e3004d8c2da9f4ed2868ff25305e0..4bb5262f7aee705e7bf3b74c5ee950632dfbfd5a 100644 (file)
@@ -319,6 +319,8 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit,
        int retries = 0, cc;
        unsigned long laob = 0;
 
+       WARN_ON_ONCE(aob && ((queue_type(q) != QDIO_IQDIO_QFMT) ||
+                            !q->u.out.use_cq));
        if (q->u.out.use_cq && aob != 0) {
                fc = QDIO_SIGA_WRITEQ;
                laob = aob;
@@ -329,8 +331,6 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit,
                fc |= QDIO_SIGA_QEBSM_FLAG;
        }
 again:
-       WARN_ON_ONCE((aob && queue_type(q) != QDIO_IQDIO_QFMT) ||
-               (aob && fc != QDIO_SIGA_WRITEQ));
        cc = do_siga_output(schid, q->mask, busy_bit, fc, laob);
 
        /* hipersocket busy condition */
index 33f7040d711d6f66aaf8dbc96987664bd197c134..0ba3a2f81750f6785bdd107613d9a98069e88662 100644 (file)
@@ -149,12 +149,11 @@ static struct device_driver netiucv_driver = {
        .pm = &netiucv_pm_ops,
 };
 
-static int netiucv_callback_connreq(struct iucv_path *,
-                                   u8 ipvmid[8], u8 ipuser[16]);
-static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
-static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
-static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
-static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
+static int netiucv_callback_connreq(struct iucv_path *, u8 *, u8 *);
+static void netiucv_callback_connack(struct iucv_path *, u8 *);
+static void netiucv_callback_connrej(struct iucv_path *, u8 *);
+static void netiucv_callback_connsusp(struct iucv_path *, u8 *);
+static void netiucv_callback_connres(struct iucv_path *, u8 *);
 static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
 static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
 
@@ -556,8 +555,8 @@ static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
        fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
 }
 
-static int netiucv_callback_connreq(struct iucv_path *path,
-                                   u8 ipvmid[8], u8 ipuser[16])
+static int netiucv_callback_connreq(struct iucv_path *path, u8 *ipvmid,
+                                   u8 *ipuser)
 {
        struct iucv_connection *conn = path->private;
        struct iucv_event ev;
@@ -587,21 +586,21 @@ static int netiucv_callback_connreq(struct iucv_path *path,
        return rc;
 }
 
-static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
+static void netiucv_callback_connrej(struct iucv_path *path, u8 *ipuser)
 {
        struct iucv_connection *conn = path->private;
 
        fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
 }
 
-static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
+static void netiucv_callback_connsusp(struct iucv_path *path, u8 *ipuser)
 {
        struct iucv_connection *conn = path->private;
 
        fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
 }
 
-static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
+static void netiucv_callback_connres(struct iucv_path *path, u8 *ipuser)
 {
        struct iucv_connection *conn = path->private;
 
index ba974a2e409fdc54363dba280951afb6bd373aa6..1766a20ebcb116d42a1b4173154da900260e0767 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/bitops.h>
 #include <linux/seq_file.h>
 #include <linux/ethtool.h>
+#include <linux/hashtable.h>
 
 #include <net/ipv6.h>
 #include <net/if_inet6.h>
@@ -663,9 +664,7 @@ struct qeth_card_info {
        char mcl_level[QETH_MCL_LENGTH + 1];
        int guestlan;
        int mac_bits;
-       int portname_required;
        int portno;
-       char portname[9];
        enum qeth_card_types type;
        enum qeth_link_types link_type;
        int is_multicast_different;
@@ -741,11 +740,17 @@ struct qeth_vlan_vid {
        unsigned short vid;
 };
 
-struct qeth_mc_mac {
-       struct list_head list;
-       __u8 mc_addr[MAX_ADDR_LEN];
-       unsigned char mc_addrlen;
-       int is_vmac;
+enum qeth_mac_disposition {
+       QETH_DISP_MAC_DELETE = 0,
+       QETH_DISP_MAC_DO_NOTHING = 1,
+       QETH_DISP_MAC_ADD = 2,
+};
+
+struct qeth_mac {
+       u8 mac_addr[OSA_ADDR_LEN];
+       u8 is_uc:1;
+       u8 disp_flag:2;
+       struct hlist_node hnode;
 };
 
 struct qeth_rx {
@@ -792,7 +797,7 @@ struct qeth_card {
        spinlock_t mclock;
        unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
        struct list_head vid_list;
-       struct list_head mc_list;
+       DECLARE_HASHTABLE(mac_htable, 4);
        struct work_struct kernel_thread_starter;
        spinlock_t thread_mask_lock;
        unsigned long thread_start_mask;
@@ -969,6 +974,15 @@ int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action);
 int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot);
 void qeth_trace_features(struct qeth_card *);
 void qeth_close_dev(struct qeth_card *);
+int qeth_send_simple_setassparms(struct qeth_card *, enum qeth_ipa_funcs,
+                                __u16, long);
+int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, __u16,
+                         long,
+                         int (*reply_cb)(struct qeth_card *,
+                                         struct qeth_reply *, unsigned long),
+                         void *);
+int qeth_start_ipa_tx_checksum(struct qeth_card *);
+int qeth_set_rx_csum(struct qeth_card *, int);
 
 /* exports for OSN */
 int qeth_osn_assist(struct net_device *, void *, int);
index 5e20fba37bfff268f23ea2e9ce214f71d7f7ff73..31ac53fa5cee9b81185bdba5c047d12cdf28c17f 100644 (file)
@@ -1982,14 +1982,6 @@ static void qeth_idx_read_cb(struct qeth_channel *channel,
                goto out;
        }
 
-/**
- *  * temporary fix for microcode bug
- *   * to revert it,replace OR by AND
- *    */
-       if ((!QETH_IDX_NO_PORTNAME_REQUIRED(iob->data)) ||
-            (card->info.type == QETH_CARD_TYPE_OSD))
-               card->info.portname_required = 1;
-
        memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
        if (temp != qeth_peer_func_level(card->info.func_level)) {
                QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function "
@@ -2360,8 +2352,6 @@ static int qeth_ulp_enable(struct qeth_card *card)
               &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
        memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data),
               &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH);
-       memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(iob->data),
-              card->info.portname, 9);
        rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob,
                                    qeth_ulp_enable_cb, NULL);
        return rc;
@@ -2680,48 +2670,6 @@ out_qdio:
        return rc;
 }
 
-static void qeth_print_status_with_portname(struct qeth_card *card)
-{
-       char dbf_text[15];
-       int i;
-
-       sprintf(dbf_text, "%s", card->info.portname + 1);
-       for (i = 0; i < 8; i++)
-               dbf_text[i] =
-                       (char) _ebcasc[(__u8) dbf_text[i]];
-       dbf_text[8] = 0;
-       dev_info(&card->gdev->dev, "Device is a%s card%s%s%s\n"
-              "with link type %s (portname: %s)\n",
-              qeth_get_cardname(card),
-              (card->info.mcl_level[0]) ? " (level: " : "",
-              (card->info.mcl_level[0]) ? card->info.mcl_level : "",
-              (card->info.mcl_level[0]) ? ")" : "",
-              qeth_get_cardname_short(card),
-              dbf_text);
-
-}
-
-static void qeth_print_status_no_portname(struct qeth_card *card)
-{
-       if (card->info.portname[0])
-               dev_info(&card->gdev->dev, "Device is a%s "
-                      "card%s%s%s\nwith link type %s "
-                      "(no portname needed by interface).\n",
-                      qeth_get_cardname(card),
-                      (card->info.mcl_level[0]) ? " (level: " : "",
-                      (card->info.mcl_level[0]) ? card->info.mcl_level : "",
-                      (card->info.mcl_level[0]) ? ")" : "",
-                      qeth_get_cardname_short(card));
-       else
-               dev_info(&card->gdev->dev, "Device is a%s "
-                      "card%s%s%s\nwith link type %s.\n",
-                      qeth_get_cardname(card),
-                      (card->info.mcl_level[0]) ? " (level: " : "",
-                      (card->info.mcl_level[0]) ? card->info.mcl_level : "",
-                      (card->info.mcl_level[0]) ? ")" : "",
-                      qeth_get_cardname_short(card));
-}
-
 void qeth_print_status_message(struct qeth_card *card)
 {
        switch (card->info.type) {
@@ -2758,10 +2706,13 @@ void qeth_print_status_message(struct qeth_card *card)
        default:
                memset(&card->info.mcl_level[0], 0, QETH_MCL_LENGTH + 1);
        }
-       if (card->info.portname_required)
-               qeth_print_status_with_portname(card);
-       else
-               qeth_print_status_no_portname(card);
+       dev_info(&card->gdev->dev,
+                "Device is a%s card%s%s%s\nwith link type %s.\n",
+                qeth_get_cardname(card),
+                (card->info.mcl_level[0]) ? " (level: " : "",
+                (card->info.mcl_level[0]) ? card->info.mcl_level : "",
+                (card->info.mcl_level[0]) ? ")" : "",
+                qeth_get_cardname_short(card));
 }
 EXPORT_SYMBOL_GPL(qeth_print_status_message);
 
@@ -5027,13 +4978,11 @@ static void qeth_core_free_card(struct qeth_card *card)
 void qeth_trace_features(struct qeth_card *card)
 {
        QETH_CARD_TEXT(card, 2, "features");
-       QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.supported_funcs);
-       QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.enabled_funcs);
-       QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.supported_funcs);
-       QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.enabled_funcs);
-       QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.supported_funcs);
-       QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.enabled_funcs);
-       QETH_CARD_TEXT_(card, 2, "%x", card->info.diagass_support);
+       QETH_CARD_HEX(card, 2, &card->options.ipa4, sizeof(card->options.ipa4));
+       QETH_CARD_HEX(card, 2, &card->options.ipa6, sizeof(card->options.ipa6));
+       QETH_CARD_HEX(card, 2, &card->options.adp, sizeof(card->options.adp));
+       QETH_CARD_HEX(card, 2, &card->info.diagass_support,
+                     sizeof(card->info.diagass_support));
 }
 EXPORT_SYMBOL_GPL(qeth_trace_features);
 
@@ -5132,6 +5081,7 @@ retriable:
        }
 
        card->options.ipa4.supported_funcs = 0;
+       card->options.ipa6.supported_funcs = 0;
        card->options.adp.supported_funcs = 0;
        card->options.sbp.supported_funcs = 0;
        card->info.diagass_support = 0;
@@ -5317,6 +5267,102 @@ no_mem:
 }
 EXPORT_SYMBOL_GPL(qeth_core_get_next_skb);
 
+static int qeth_setassparms_cb(struct qeth_card *card,
+                              struct qeth_reply *reply, unsigned long data)
+{
+       struct qeth_ipa_cmd *cmd;
+
+       QETH_CARD_TEXT(card, 4, "defadpcb");
+
+       cmd = (struct qeth_ipa_cmd *) data;
+       if (cmd->hdr.return_code == 0) {
+               cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
+               if (cmd->hdr.prot_version == QETH_PROT_IPV4)
+                       card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled;
+               if (cmd->hdr.prot_version == QETH_PROT_IPV6)
+                       card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
+       }
+       if (cmd->data.setassparms.hdr.assist_no == IPA_INBOUND_CHECKSUM &&
+           cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) {
+               card->info.csum_mask = cmd->data.setassparms.data.flags_32bit;
+               QETH_CARD_TEXT_(card, 3, "csum:%d", card->info.csum_mask);
+       }
+       if (cmd->data.setassparms.hdr.assist_no == IPA_OUTBOUND_CHECKSUM &&
+           cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) {
+               card->info.tx_csum_mask =
+                       cmd->data.setassparms.data.flags_32bit;
+               QETH_CARD_TEXT_(card, 3, "tcsu:%d", card->info.tx_csum_mask);
+       }
+
+       return 0;
+}
+
+static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
+                                                 enum qeth_ipa_funcs ipa_func,
+                                                 __u16 cmd_code, __u16 len,
+                                                 enum qeth_prot_versions prot)
+{
+       struct qeth_cmd_buffer *iob;
+       struct qeth_ipa_cmd *cmd;
+
+       QETH_CARD_TEXT(card, 4, "getasscm");
+       iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot);
+
+       if (iob) {
+               cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+               cmd->data.setassparms.hdr.assist_no = ipa_func;
+               cmd->data.setassparms.hdr.length = 8 + len;
+               cmd->data.setassparms.hdr.command_code = cmd_code;
+               cmd->data.setassparms.hdr.return_code = 0;
+               cmd->data.setassparms.hdr.seq_no = 0;
+       }
+
+       return iob;
+}
+
+int qeth_send_setassparms(struct qeth_card *card,
+                         struct qeth_cmd_buffer *iob, __u16 len, long data,
+                         int (*reply_cb)(struct qeth_card *,
+                                         struct qeth_reply *, unsigned long),
+                         void *reply_param)
+{
+       int rc;
+       struct qeth_ipa_cmd *cmd;
+
+       QETH_CARD_TEXT(card, 4, "sendassp");
+
+       cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+       if (len <= sizeof(__u32))
+               cmd->data.setassparms.data.flags_32bit = (__u32) data;
+       else   /* (len > sizeof(__u32)) */
+               memcpy(&cmd->data.setassparms.data, (void *) data, len);
+
+       rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_send_setassparms);
+
+int qeth_send_simple_setassparms(struct qeth_card *card,
+                                enum qeth_ipa_funcs ipa_func,
+                                __u16 cmd_code, long data)
+{
+       int rc;
+       int length = 0;
+       struct qeth_cmd_buffer *iob;
+
+       QETH_CARD_TEXT(card, 4, "simassp4");
+       if (data)
+               length = sizeof(__u32);
+       iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code,
+                                      length, QETH_PROT_IPV4);
+       if (!iob)
+               return -ENOMEM;
+       rc = qeth_send_setassparms(card, iob, length, data,
+                                  qeth_setassparms_cb, NULL);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_send_simple_setassparms);
+
 static void qeth_unregister_dbf_views(void)
 {
        int x;
@@ -6003,6 +6049,75 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,
 }
 EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings);
 
+static int qeth_send_checksum_command(struct qeth_card *card)
+{
+       int rc;
+
+       rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
+                                         IPA_CMD_ASS_START, 0);
+       if (rc) {
+               dev_warn(&card->gdev->dev, "Starting HW checksumming for %s "
+                       "failed, using SW checksumming\n",
+                       QETH_CARD_IFNAME(card));
+               return rc;
+       }
+       rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
+                                         IPA_CMD_ASS_ENABLE,
+                                         card->info.csum_mask);
+       if (rc) {
+               dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s "
+                       "failed, using SW checksumming\n",
+                       QETH_CARD_IFNAME(card));
+               return rc;
+       }
+       return 0;
+}
+
+int qeth_set_rx_csum(struct qeth_card *card, int on)
+{
+       int rc;
+
+       if (on) {
+               rc = qeth_send_checksum_command(card);
+               if (rc)
+                       return -EIO;
+               dev_info(&card->gdev->dev,
+                       "HW Checksumming (inbound) enabled\n");
+       } else {
+               rc = qeth_send_simple_setassparms(card,
+                       IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0);
+               if (rc)
+                       return -EIO;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(qeth_set_rx_csum);
+
+int qeth_start_ipa_tx_checksum(struct qeth_card *card)
+{
+       int rc = 0;
+
+       if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
+               return rc;
+       rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
+                                         IPA_CMD_ASS_START, 0);
+       if (rc)
+               goto err_out;
+       rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
+                                         IPA_CMD_ASS_ENABLE,
+                                         card->info.tx_csum_mask);
+       if (rc)
+               goto err_out;
+
+       dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n");
+       return rc;
+err_out:
+       dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s "
+               "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card));
+       return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_start_ipa_tx_checksum);
+
 static int __init qeth_core_init(void)
 {
        int rc;
index 423bec56cffa09115610b05b908307462774e909..e6e5b9671bf20808b734258a4c9c73c268438bdb 100644 (file)
@@ -153,52 +153,17 @@ static DEVICE_ATTR(portno, 0644, qeth_dev_portno_show, qeth_dev_portno_store);
 static ssize_t qeth_dev_portname_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
-       struct qeth_card *card = dev_get_drvdata(dev);
-       char portname[9] = {0, };
-
-       if (!card)
-               return -EINVAL;
-
-       if (card->info.portname_required) {
-               memcpy(portname, card->info.portname + 1, 8);
-               EBCASC(portname, 8);
-               return sprintf(buf, "%s\n", portname);
-       } else
-               return sprintf(buf, "no portname required\n");
+       return sprintf(buf, "no portname required\n");
 }
 
 static ssize_t qeth_dev_portname_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
        struct qeth_card *card = dev_get_drvdata(dev);
-       char *tmp;
-       int i, rc = 0;
-
-       if (!card)
-               return -EINVAL;
-
-       mutex_lock(&card->conf_mutex);
-       if ((card->state != CARD_STATE_DOWN) &&
-           (card->state != CARD_STATE_RECOVER)) {
-               rc = -EPERM;
-               goto out;
-       }
 
-       tmp = strsep((char **) &buf, "\n");
-       if ((strlen(tmp) > 8) || (strlen(tmp) == 0)) {
-               rc = -EINVAL;
-               goto out;
-       }
-
-       card->info.portname[0] = strlen(tmp);
-       /* for beauty reasons */
-       for (i = 1; i < 9; i++)
-               card->info.portname[i] = ' ';
-       strcpy(card->info.portname + 1, tmp);
-       ASCEBC(card->info.portname + 1, 8);
-out:
-       mutex_unlock(&card->conf_mutex);
-       return rc ? rc : count;
+       dev_warn_once(&card->gdev->dev,
+                     "portname is deprecated and is ignored\n");
+       return count;
 }
 
 static DEVICE_ATTR(portname, 0644, qeth_dev_portname_show,
index a8556692f632ec3000be0c8d8fd43d3b56396afb..8f1b091e173277a718c232bdbf707fb03db21945 100644 (file)
@@ -19,7 +19,9 @@
 #include <linux/mii.h>
 #include <linux/ip.h>
 #include <linux/list.h>
-
+#include <linux/hash.h>
+#include <linux/hashtable.h>
+#include <linux/string.h>
 #include "qeth_core.h"
 #include "qeth_l2.h"
 
@@ -28,7 +30,7 @@ static int qeth_l2_stop(struct net_device *);
 static int qeth_l2_send_delmac(struct qeth_card *, __u8 *);
 static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *,
                           enum qeth_ipa_cmds);
-static void qeth_l2_set_multicast_list(struct net_device *);
+static void qeth_l2_set_rx_mode(struct net_device *);
 static int qeth_l2_recover(void *);
 static void qeth_bridgeport_query_support(struct qeth_card *card);
 static void qeth_bridge_state_change(struct qeth_card *card,
@@ -193,49 +195,44 @@ static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
        return rc;
 }
 
-static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac)
+static inline u32 qeth_l2_mac_hash(const u8 *addr)
 {
-       struct qeth_mc_mac *mc;
-       int rc;
-
-       mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC);
+       return get_unaligned((u32 *)(&addr[2]));
+}
 
-       if (!mc)
-               return;
+static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac)
+{
 
-       memcpy(mc->mc_addr, mac, OSA_ADDR_LEN);
-       mc->mc_addrlen = OSA_ADDR_LEN;
-       mc->is_vmac = vmac;
+       int rc;
 
-       if (vmac) {
+       if (mac->is_uc) {
                rc = qeth_setdel_makerc(card,
-                       qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC));
+                               qeth_l2_send_setdelmac(card, mac->mac_addr,
+                                               IPA_CMD_SETVMAC));
        } else {
                rc = qeth_setdel_makerc(card,
-                       qeth_l2_send_setgroupmac(card, mac));
+                               qeth_l2_send_setgroupmac(card, mac->mac_addr));
        }
-
-       if (!rc)
-               list_add_tail(&mc->list, &card->mc_list);
-       else
-               kfree(mc);
+       return rc;
 }
 
-static void qeth_l2_del_all_mc(struct qeth_card *card, int del)
+static void qeth_l2_del_all_macs(struct qeth_card *card, int del)
 {
-       struct qeth_mc_mac *mc, *tmp;
+       struct qeth_mac *mac;
+       struct hlist_node *tmp;
+       int i;
 
        spin_lock_bh(&card->mclock);
-       list_for_each_entry_safe(mc, tmp, &card->mc_list, list) {
+       hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) {
                if (del) {
-                       if (mc->is_vmac)
-                               qeth_l2_send_setdelmac(card, mc->mc_addr,
-                                       IPA_CMD_DELVMAC);
+                       if (mac->is_uc)
+                               qeth_l2_send_setdelmac(card, mac->mac_addr,
+                                               IPA_CMD_DELVMAC);
                        else
-                               qeth_l2_send_delgroupmac(card, mc->mc_addr);
+                               qeth_l2_send_delgroupmac(card, mac->mac_addr);
                }
-               list_del(&mc->list);
-               kfree(mc);
+               hash_del(&mac->hnode);
+               kfree(mac);
        }
        spin_unlock_bh(&card->mclock);
 }
@@ -252,6 +249,23 @@ static inline int qeth_l2_get_cast_type(struct qeth_card *card,
        return RTN_UNSPEC;
 }
 
+static inline void qeth_l2_hdr_csum(struct qeth_card *card,
+                                   struct qeth_hdr *hdr, struct sk_buff *skb)
+{
+       struct iphdr *iph = ip_hdr(skb);
+
+       /* tcph->check contains already the pseudo hdr checksum
+        * so just set the header flags
+        */
+       if (iph->protocol == IPPROTO_UDP)
+               hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_UDP;
+       hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_CSUM_TRANSP_REQ |
+               QETH_HDR_EXT_CSUM_HDR_REQ;
+       iph->check = 0;
+       if (card->options.performance_stats)
+               card->perf_stats.tx_csum++;
+}
+
 static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                        struct sk_buff *skb, int cast_type)
 {
@@ -386,10 +400,42 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev,
                rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
                kfree(tmpid);
        }
-       qeth_l2_set_multicast_list(card->dev);
+       qeth_l2_set_rx_mode(card->dev);
        return rc;
 }
 
+static netdev_features_t qeth_l2_fix_features(struct net_device *dev,
+                                             netdev_features_t features)
+{
+       struct qeth_card *card = dev->ml_priv;
+
+       QETH_DBF_TEXT(SETUP, 2, "fixfeat");
+       if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
+               features &= ~NETIF_F_IP_CSUM;
+       if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
+               features &= ~NETIF_F_RXCSUM;
+       QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));
+       return features;
+}
+
+static int qeth_l2_set_features(struct net_device *dev,
+                               netdev_features_t features)
+{
+       struct qeth_card *card = dev->ml_priv;
+       netdev_features_t changed = dev->features ^ features;
+
+       QETH_DBF_TEXT(SETUP, 2, "setfeat");
+       QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));
+
+       if (card->state == CARD_STATE_DOWN ||
+           card->state == CARD_STATE_RECOVER)
+               return 0;
+
+       if (!(changed & NETIF_F_RXCSUM))
+               return 0;
+       return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0);
+}
+
 static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
 {
        QETH_DBF_TEXT(SETUP , 2, "stopcard");
@@ -411,7 +457,7 @@ static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
                card->state = CARD_STATE_SOFTSETUP;
        }
        if (card->state == CARD_STATE_SOFTSETUP) {
-               qeth_l2_del_all_mc(card, 0);
+               qeth_l2_del_all_macs(card, 0);
                qeth_clear_ipacmd_list(card);
                card->state = CARD_STATE_HARDSETUP;
        }
@@ -450,11 +496,19 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
                case QETH_HEADER_TYPE_LAYER2:
                        skb->pkt_type = PACKET_HOST;
                        skb->protocol = eth_type_trans(skb, skb->dev);
-                       skb->ip_summed = CHECKSUM_NONE;
+                       if ((card->dev->features & NETIF_F_RXCSUM)
+                          && ((hdr->hdr.l2.flags[1] &
+                               (QETH_HDR_EXT_CSUM_HDR_REQ |
+                                  QETH_HDR_EXT_CSUM_TRANSP_REQ)) ==
+                               (QETH_HDR_EXT_CSUM_HDR_REQ |
+                                  QETH_HDR_EXT_CSUM_TRANSP_REQ)))
+                               skb->ip_summed = CHECKSUM_UNNECESSARY;
+                       else
+                               skb->ip_summed = CHECKSUM_NONE;
                        if (skb->protocol == htons(ETH_P_802_2))
                                *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno;
                        len = skb->len;
-                       netif_receive_skb(skb);
+                       napi_gro_receive(&card->napi, skb);
                        break;
                case QETH_HEADER_TYPE_OSN:
                        if (card->info.type == QETH_CARD_TYPE_OSN) {
@@ -711,29 +765,91 @@ static void qeth_promisc_to_bridge(struct qeth_card *card)
                card->options.sbp.role = role;
                card->info.promisc_mode = promisc_mode;
        }
+
 }
+/* New MAC address is added to the hash table and marked to be written on card
+ * only if there is not in the hash table storage already
+ *
+*/
+static void
+qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha, u8 is_uc)
+{
+       struct qeth_mac *mac;
 
-static void qeth_l2_set_multicast_list(struct net_device *dev)
+       hash_for_each_possible(card->mac_htable, mac, hnode,
+                       qeth_l2_mac_hash(ha->addr)) {
+               if (is_uc == mac->is_uc &&
+                   !memcmp(ha->addr, mac->mac_addr, OSA_ADDR_LEN)) {
+                       mac->disp_flag = QETH_DISP_MAC_DO_NOTHING;
+                       return;
+               }
+       }
+
+       mac = kzalloc(sizeof(struct qeth_mac), GFP_ATOMIC);
+
+       if (!mac)
+               return;
+
+       memcpy(mac->mac_addr, ha->addr, OSA_ADDR_LEN);
+       mac->is_uc = is_uc;
+       mac->disp_flag = QETH_DISP_MAC_ADD;
+
+       hash_add(card->mac_htable, &mac->hnode,
+                       qeth_l2_mac_hash(mac->mac_addr));
+
+}
+
+static void qeth_l2_set_rx_mode(struct net_device *dev)
 {
        struct qeth_card *card = dev->ml_priv;
        struct netdev_hw_addr *ha;
+       struct qeth_mac *mac;
+       struct hlist_node *tmp;
+       int i;
+       int rc;
 
        if (card->info.type == QETH_CARD_TYPE_OSN)
-               return ;
+               return;
 
        QETH_CARD_TEXT(card, 3, "setmulti");
        if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
            (card->state != CARD_STATE_UP))
                return;
-       qeth_l2_del_all_mc(card, 1);
+
        spin_lock_bh(&card->mclock);
+
        netdev_for_each_mc_addr(ha, dev)
-               qeth_l2_add_mc(card, ha->addr, 0);
+               qeth_l2_add_mac(card, ha, 0);
 
        netdev_for_each_uc_addr(ha, dev)
-               qeth_l2_add_mc(card, ha->addr, 1);
+               qeth_l2_add_mac(card, ha, 1);
+
+       hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) {
+               if (mac->disp_flag == QETH_DISP_MAC_DELETE) {
+                       if (!mac->is_uc)
+                               rc = qeth_l2_send_delgroupmac(card,
+                                               mac->mac_addr);
+                       else {
+                               rc = qeth_l2_send_setdelmac(card, mac->mac_addr,
+                                               IPA_CMD_DELVMAC);
+                       }
+
+                       hash_del(&mac->hnode);
+                       kfree(mac);
+
+               } else if (mac->disp_flag == QETH_DISP_MAC_ADD) {
+                       rc = qeth_l2_write_mac(card, mac);
+                       if (rc) {
+                               hash_del(&mac->hnode);
+                               kfree(mac);
+                       } else
+                               mac->disp_flag = QETH_DISP_MAC_DELETE;
+               } else
+                       mac->disp_flag = QETH_DISP_MAC_DELETE;
+       }
 
        spin_unlock_bh(&card->mclock);
+
        if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
                qeth_setadp_promisc_mode(card);
        else
@@ -803,6 +919,8 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                                                sizeof(struct qeth_hdr));
                        skb_set_mac_header(new_skb, sizeof(struct qeth_hdr));
                        qeth_l2_fill_header(card, hdr, new_skb, cast_type);
+                       if (new_skb->ip_summed == CHECKSUM_PARTIAL)
+                               qeth_l2_hdr_csum(card, hdr, new_skb);
                }
        }
 
@@ -915,7 +1033,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
 
        qeth_l2_create_device_attributes(&gdev->dev);
        INIT_LIST_HEAD(&card->vid_list);
-       INIT_LIST_HEAD(&card->mc_list);
+       hash_init(card->mac_htable);
        card->options.layer2 = 1;
        card->info.hwtrap = 0;
        return 0;
@@ -961,13 +1079,15 @@ static const struct net_device_ops qeth_l2_netdev_ops = {
        .ndo_get_stats          = qeth_get_stats,
        .ndo_start_xmit         = qeth_l2_hard_start_xmit,
        .ndo_validate_addr      = eth_validate_addr,
-       .ndo_set_rx_mode        = qeth_l2_set_multicast_list,
+       .ndo_set_rx_mode        = qeth_l2_set_rx_mode,
        .ndo_do_ioctl           = qeth_l2_do_ioctl,
        .ndo_set_mac_address    = qeth_l2_set_mac_address,
        .ndo_change_mtu         = qeth_change_mtu,
        .ndo_vlan_rx_add_vid    = qeth_l2_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid   = qeth_l2_vlan_rx_kill_vid,
        .ndo_tx_timeout         = qeth_tx_timeout,
+       .ndo_fix_features       = qeth_l2_fix_features,
+       .ndo_set_features       = qeth_l2_set_features
 };
 
 static int qeth_l2_setup_netdev(struct qeth_card *card)
@@ -997,6 +1117,11 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
                (card->info.type != QETH_CARD_TYPE_OSN) ?
                &qeth_l2_ethtool_ops : &qeth_l2_osn_ops;
        card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+       if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) {
+               card->dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
+               /* Turn on RX offloading per default */
+               card->dev->features |= NETIF_F_RXCSUM;
+       }
        card->info.broadcast_capable = 1;
        qeth_l2_request_initial_mac(card);
        SET_NETDEV_DEV(card->dev, &card->gdev->dev);
@@ -1004,6 +1129,17 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
        return register_netdev(card->dev);
 }
 
+static int qeth_l2_start_ipassists(struct qeth_card *card)
+{
+       /* configure isolation level */
+       if (qeth_set_access_ctrl_online(card, 0))
+               return -ENODEV;
+       if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
+               qeth_set_rx_csum(card, 1);
+       qeth_start_ipa_tx_checksum(card);
+       return 0;
+}
+
 static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
 {
        struct qeth_card *card = dev_get_drvdata(&gdev->dev);
@@ -1069,12 +1205,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
 contin:
        if ((card->info.type == QETH_CARD_TYPE_OSD) ||
            (card->info.type == QETH_CARD_TYPE_OSX)) {
-               /* configure isolation level */
-               rc = qeth_set_access_ctrl_online(card, 0);
-               if (rc) {
-                       rc = -ENODEV;
+               if (qeth_l2_start_ipassists(card))
                        goto out_remove;
-               }
        }
 
        if (card->info.type != QETH_CARD_TYPE_OSN &&
@@ -1106,7 +1238,7 @@ contin:
                        rtnl_unlock();
                }
                /* this also sets saved unicast addresses */
-               qeth_l2_set_multicast_list(card->dev);
+               qeth_l2_set_rx_mode(card->dev);
        }
        /* let user_space know that device is online */
        kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
@@ -1452,8 +1584,8 @@ static void qeth_bridge_emit_host_event(struct qeth_card *card,
                        env[i] = str[i]; i++;
                }
                if (code & IPA_ADDR_CHANGE_CODE_MACADDR) {
-                       snprintf(str[i], sizeof(str[i]), "MAC=%pM6",
-                               &addr_lnid->mac);
+                       snprintf(str[i], sizeof(str[i]), "MAC=%pM",
+                               addr_lnid->mac);
                        env[i] = str[i]; i++;
                }
                snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x",
index 52673cd1db9952ab8548b1bad01fc1997b8c1638..692db49e3d2a368d36f76bfffe6200db6782627c 100644 (file)
@@ -109,7 +109,7 @@ static ssize_t qeth_bridge_port_state_show(struct device *dev,
        return qeth_bridge_port_role_state_show(dev, attr, buf, 1);
 }
 
-static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show,
+static DEVICE_ATTR(bridge_state, 0444, qeth_bridge_port_state_show,
                   NULL);
 
 static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev,
index a1aaa36e9ebb425d5c45933baa58e40b598a570a..543960e96b42b362af6ee7d68bafba057042cc32 100644 (file)
@@ -1065,27 +1065,6 @@ static struct qeth_cmd_buffer *qeth_l3_get_setassparms_cmd(
        return iob;
 }
 
-static int qeth_l3_send_setassparms(struct qeth_card *card,
-       struct qeth_cmd_buffer *iob, __u16 len, long data,
-       int (*reply_cb)(struct qeth_card *, struct qeth_reply *,
-               unsigned long),
-       void *reply_param)
-{
-       int rc;
-       struct qeth_ipa_cmd *cmd;
-
-       QETH_CARD_TEXT(card, 4, "sendassp");
-
-       cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
-       if (len <= sizeof(__u32))
-               cmd->data.setassparms.data.flags_32bit = (__u32) data;
-       else   /* (len > sizeof(__u32)) */
-               memcpy(&cmd->data.setassparms.data, (void *) data, len);
-
-       rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param);
-       return rc;
-}
-
 #ifdef CONFIG_QETH_IPV6
 static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card,
                enum qeth_ipa_funcs ipa_func, __u16 cmd_code)
@@ -1098,31 +1077,12 @@ static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card,
                                       0, QETH_PROT_IPV6);
        if (!iob)
                return -ENOMEM;
-       rc = qeth_l3_send_setassparms(card, iob, 0, 0,
+       rc = qeth_send_setassparms(card, iob, 0, 0,
                                   qeth_l3_default_setassparms_cb, NULL);
        return rc;
 }
 #endif
 
-static int qeth_l3_send_simple_setassparms(struct qeth_card *card,
-               enum qeth_ipa_funcs ipa_func, __u16 cmd_code, long data)
-{
-       int rc;
-       int length = 0;
-       struct qeth_cmd_buffer *iob;
-
-       QETH_CARD_TEXT(card, 4, "simassp4");
-       if (data)
-               length = sizeof(__u32);
-       iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code,
-                                      length, QETH_PROT_IPV4);
-       if (!iob)
-               return -ENOMEM;
-       rc = qeth_l3_send_setassparms(card, iob, length, data,
-                                  qeth_l3_default_setassparms_cb, NULL);
-       return rc;
-}
-
 static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
 {
        int rc;
@@ -1135,8 +1095,8 @@ static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
                        QETH_CARD_IFNAME(card));
                return 0;
        }
-       rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
-                                       IPA_CMD_ASS_START, 0);
+       rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+                                         IPA_CMD_ASS_START, 0);
        if (rc) {
                dev_warn(&card->gdev->dev,
                        "Starting ARP processing support for %s failed\n",
@@ -1158,7 +1118,7 @@ static int qeth_l3_start_ipa_ip_fragmentation(struct qeth_card *card)
                return  -EOPNOTSUPP;
        }
 
-       rc = qeth_l3_send_simple_setassparms(card, IPA_IP_FRAGMENTATION,
+       rc = qeth_send_simple_setassparms(card, IPA_IP_FRAGMENTATION,
                                          IPA_CMD_ASS_START, 0);
        if (rc) {
                dev_warn(&card->gdev->dev,
@@ -1183,7 +1143,7 @@ static int qeth_l3_start_ipa_source_mac(struct qeth_card *card)
                return -EOPNOTSUPP;
        }
 
-       rc = qeth_l3_send_simple_setassparms(card, IPA_SOURCE_MAC,
+       rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC,
                                          IPA_CMD_ASS_START, 0);
        if (rc)
                dev_warn(&card->gdev->dev,
@@ -1204,7 +1164,7 @@ static int qeth_l3_start_ipa_vlan(struct qeth_card *card)
                return -EOPNOTSUPP;
        }
 
-       rc = qeth_l3_send_simple_setassparms(card, IPA_VLAN_PRIO,
+       rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO,
                                          IPA_CMD_ASS_START, 0);
        if (rc) {
                dev_warn(&card->gdev->dev,
@@ -1229,7 +1189,7 @@ static int qeth_l3_start_ipa_multicast(struct qeth_card *card)
                return -EOPNOTSUPP;
        }
 
-       rc = qeth_l3_send_simple_setassparms(card, IPA_MULTICASTING,
+       rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING,
                                          IPA_CMD_ASS_START, 0);
        if (rc) {
                dev_warn(&card->gdev->dev,
@@ -1259,7 +1219,7 @@ static int qeth_l3_softsetup_ipv6(struct qeth_card *card)
                        QETH_CARD_IFNAME(card));
                return rc;
        }
-       rc = qeth_l3_send_simple_setassparms(card, IPA_IPV6,
+       rc = qeth_send_simple_setassparms(card, IPA_IPV6,
                                          IPA_CMD_ASS_START, 3);
        if (rc) {
                dev_err(&card->gdev->dev,
@@ -1319,7 +1279,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
                rc = -EOPNOTSUPP;
                goto out;
        }
-       rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+       rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
                                          IPA_CMD_ASS_START, 0);
        if (rc) {
                dev_warn(&card->gdev->dev, "Enabling broadcast filtering for "
@@ -1327,7 +1287,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
                goto out;
        }
 
-       rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+       rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
                                          IPA_CMD_ASS_CONFIGURE, 1);
        if (rc) {
                dev_warn(&card->gdev->dev,
@@ -1337,7 +1297,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
        }
        card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
        dev_info(&card->gdev->dev, "Broadcast enabled\n");
-       rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+       rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
                                          IPA_CMD_ASS_ENABLE, 1);
        if (rc) {
                dev_warn(&card->gdev->dev, "Setting up broadcast echo "
@@ -1353,84 +1313,18 @@ out:
        return rc;
 }
 
-static int qeth_l3_send_checksum_command(struct qeth_card *card)
-{
-       int rc;
-
-       rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
-                                         IPA_CMD_ASS_START, 0);
-       if (rc) {
-               dev_warn(&card->gdev->dev, "Starting HW checksumming for %s "
-                       "failed, using SW checksumming\n",
-                       QETH_CARD_IFNAME(card));
-               return rc;
-       }
-       rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
-                                         IPA_CMD_ASS_ENABLE,
-                                         card->info.csum_mask);
-       if (rc) {
-               dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s "
-                       "failed, using SW checksumming\n",
-                       QETH_CARD_IFNAME(card));
-               return rc;
-       }
-       return 0;
-}
-
-static int qeth_l3_set_rx_csum(struct qeth_card *card, int on)
-{
-       int rc = 0;
-
-       if (on) {
-               rc = qeth_l3_send_checksum_command(card);
-               if (rc)
-                       return -EIO;
-               dev_info(&card->gdev->dev,
-                       "HW Checksumming (inbound) enabled\n");
-       } else {
-               rc = qeth_l3_send_simple_setassparms(card,
-                       IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0);
-               if (rc)
-                       return -EIO;
-       }
-
-       return 0;
-}
-
-static int qeth_l3_start_ipa_checksum(struct qeth_card *card)
+static void qeth_l3_start_ipa_checksum(struct qeth_card *card)
 {
        QETH_CARD_TEXT(card, 3, "strtcsum");
-
-       if (card->dev->features & NETIF_F_RXCSUM) {
-               rtnl_lock();
-               /* force set_features call */
-               card->dev->features &= ~NETIF_F_RXCSUM;
-               netdev_update_features(card->dev);
-               rtnl_unlock();
-       }
-       return 0;
+       if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM)
+           && (card->dev->features & NETIF_F_RXCSUM))
+               qeth_set_rx_csum(card, 1);
 }
 
-static int qeth_l3_start_ipa_tx_checksum(struct qeth_card *card)
+static void qeth_l3_start_ipa_tx_checksum(struct qeth_card *card)
 {
-       int rc = 0;
-
-       if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
-               return rc;
-       rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
-                         IPA_CMD_ASS_START, 0);
-       if (rc)
-               goto err_out;
-       rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
-                         IPA_CMD_ASS_ENABLE, card->info.tx_csum_mask);
-       if (rc)
-               goto err_out;
-       dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n");
-       return rc;
-err_out:
-       dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s "
-               "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card));
-       return rc;
+       QETH_CARD_TEXT(card, 3, "strttxcs");
+       qeth_start_ipa_tx_checksum(card);
 }
 
 static int qeth_l3_start_ipa_tso(struct qeth_card *card)
@@ -1445,8 +1339,8 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card)
                        QETH_CARD_IFNAME(card));
                rc = -EOPNOTSUPP;
        } else {
-               rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
-                                               IPA_CMD_ASS_START, 0);
+               rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
+                                                 IPA_CMD_ASS_START, 0);
                if (rc)
                        dev_warn(&card->gdev->dev, "Starting outbound TCP "
                                "segmentation offload for %s failed\n",
@@ -1950,7 +1844,6 @@ static inline int qeth_l3_rebuild_skb(struct qeth_card *card,
                        skb->ip_summed = CHECKSUM_NONE;
        } else
                skb->ip_summed = CHECKSUM_NONE;
-
        return is_vlan;
 }
 
@@ -2287,7 +2180,7 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
        if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
                return -EOPNOTSUPP;
        }
-       rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+       rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
                                          IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
                                          no_entries);
        if (rc) {
@@ -2552,7 +2445,7 @@ static int qeth_l3_arp_add_entry(struct qeth_card *card,
                                       QETH_PROT_IPV4);
        if (!iob)
                return -ENOMEM;
-       rc = qeth_l3_send_setassparms(card, iob,
+       rc = qeth_send_setassparms(card, iob,
                                   sizeof(struct qeth_arp_cache_entry),
                                   (unsigned long) entry,
                                   qeth_l3_default_setassparms_cb, NULL);
@@ -2593,7 +2486,7 @@ static int qeth_l3_arp_remove_entry(struct qeth_card *card,
                                       QETH_PROT_IPV4);
        if (!iob)
                return -ENOMEM;
-       rc = qeth_l3_send_setassparms(card, iob,
+       rc = qeth_send_setassparms(card, iob,
                                   12, (unsigned long)buf,
                                   qeth_l3_default_setassparms_cb, NULL);
        if (rc) {
@@ -2624,7 +2517,7 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card)
        if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
                return -EOPNOTSUPP;
        }
-       rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+       rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
                                          IPA_CMD_ASS_ARP_FLUSH_CACHE, 0);
        if (rc) {
                tmp = rc;
@@ -3187,7 +3080,6 @@ static netdev_features_t qeth_l3_fix_features(struct net_device *dev,
                features &= ~NETIF_F_TSO;
        if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
                features &= ~NETIF_F_RXCSUM;
-
        return features;
 }
 
@@ -3204,7 +3096,7 @@ static int qeth_l3_set_features(struct net_device *dev,
            card->state == CARD_STATE_RECOVER)
                return 0;
 
-       return qeth_l3_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0);
+       return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0);
 }
 
 static const struct ethtool_ops qeth_l3_ethtool_ops = {
index d8f990b6b332c62f41c90b747347c71f56ef1709..a851d34c642b5d26866fafdde925eb48ddf61003 100644 (file)
@@ -49,7 +49,7 @@ static DEFINE_SPINLOCK(smsg_list_lock);
 static LIST_HEAD(smsg_list);
 static int iucv_path_connected;
 
-static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]);
+static int smsg_path_pending(struct iucv_path *, u8 *, u8 *);
 static void smsg_message_pending(struct iucv_path *, struct iucv_message *);
 
 static struct iucv_handler smsg_handler = {
@@ -57,8 +57,7 @@ static struct iucv_handler smsg_handler = {
        .message_pending = smsg_message_pending,
 };
 
-static int smsg_path_pending(struct iucv_path *path, u8 ipvmid[8],
-                            u8 ipuser[16])
+static int smsg_path_pending(struct iucv_path *path, u8 *ipvmid, u8 *ipuser)
 {
        if (strncmp(ipvmid, "*MSG    ", 8) != 0)
                return -EINVAL;
index de6feb8964c912e8f844010f9e6af32760481826..804806e1cbb4be79d3223a2d5684de4c6ff58afd 100644 (file)
@@ -160,7 +160,7 @@ static struct scsi_transport_template *cxgb4i_stt;
 
 #define DIV_ROUND_UP(n, d)     (((n) + (d) - 1) / (d))
 #define RCV_BUFSIZ_MASK                0x3FFU
-#define MAX_IMM_TX_PKT_LEN     128
+#define MAX_IMM_TX_PKT_LEN     256
 
 static int push_tx_frames(struct cxgbi_sock *, int);
 
index 454536c49315dd6f367051bfcb37ea59fb23e2bd..9c780740fb829db69d411c48717415f2ac10cb0e 100644 (file)
@@ -887,6 +887,8 @@ static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc)
 static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
                          struct mvs_slot_info *slot, u32 slot_idx)
 {
+       if (!slot)
+               return;
        if (!slot->task)
                return;
        if (!sas_protocol_ata(task->task_proto))
index 0a2168e69bbcd31c91a995dae5439a1485cc0a7d..e7649ed3f6677e69fe2e3644da24d99de2aa3181 100644 (file)
@@ -226,16 +226,20 @@ int scsi_dh_add_device(struct scsi_device *sdev)
 
        drv = scsi_dh_find_driver(sdev);
        if (drv)
-               devinfo = scsi_dh_lookup(drv);
+               devinfo = __scsi_dh_lookup(drv);
        if (devinfo)
                err = scsi_dh_handler_attach(sdev, devinfo);
        return err;
 }
 
-void scsi_dh_remove_device(struct scsi_device *sdev)
+void scsi_dh_release_device(struct scsi_device *sdev)
 {
        if (sdev->handler)
                scsi_dh_handler_detach(sdev);
+}
+
+void scsi_dh_remove_device(struct scsi_device *sdev)
+{
        device_remove_file(&sdev->sdev_gendev, &scsi_dh_state_attr);
 }
 
index 644bb7339b55bd89068e2a1c34a096dab682cb62..4d01cdb1b348306807663467bf56c254f16b94df 100644 (file)
@@ -173,9 +173,11 @@ extern struct async_domain scsi_sd_probe_domain;
 /* scsi_dh.c */
 #ifdef CONFIG_SCSI_DH
 int scsi_dh_add_device(struct scsi_device *sdev);
+void scsi_dh_release_device(struct scsi_device *sdev);
 void scsi_dh_remove_device(struct scsi_device *sdev);
 #else
 static inline int scsi_dh_add_device(struct scsi_device *sdev) { return 0; }
+static inline void scsi_dh_release_device(struct scsi_device *sdev) { }
 static inline void scsi_dh_remove_device(struct scsi_device *sdev) { }
 #endif
 
index b333389f248ffec291958014a39829156a188bd0..dff8fafb741c1bff625131e73e7fe4425375ced2 100644 (file)
@@ -399,6 +399,8 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work)
 
        sdev = container_of(work, struct scsi_device, ew.work);
 
+       scsi_dh_release_device(sdev);
+
        parent = sdev->sdev_gendev.parent;
 
        spin_lock_irqsave(sdev->host->host_lock, flags);
index 4a3cf9ba152f6b8060e3c7709550dfdc7ec9fe17..fb36810ae89a40a1aae2729e5767191c82840c50 100644 (file)
@@ -657,7 +657,7 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
                "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
                intspec[0], intspec[1], intspec[2]);
 
-       if (d->of_node != controller)
+       if (irq_domain_get_of_node(d) != controller)
                return -EINVAL;
        if (intsize != 4)
                return -EINVAL;
index f0d22cdb51cd8eee442b53c259aa7ee355443c4b..149214beeda920bef5c1263ad9305a370852f59a 100644 (file)
@@ -80,6 +80,15 @@ config SSB_SDIOHOST
 
          If unsure, say N
 
+config SSB_HOST_SOC
+       bool "Support for SSB bus on SoC"
+       depends on SSB
+       help
+         Host interface for a SSB directly mapped into memory. This is
+         for some Broadcom SoCs from the BCM47xx and BCM53xx lines.
+
+         If unsure, say N
+
 config SSB_SILENT
        bool "No SSB kernel messages"
        depends on SSB && EXPERT
index b1ddc116d387c47119408ed07d71d3274cce0691..64a09681cee0f1a1fe8aea4c1ce963dd60590d9f 100644 (file)
@@ -5,8 +5,9 @@ ssb-$(CONFIG_SSB_SPROM)                 += sprom.o
 
 # host support
 ssb-$(CONFIG_SSB_PCIHOST)              += pci.o pcihost_wrapper.o
-ssb-$(CONFIG_SSB_PCMCIAHOST)           += pcmcia.o
+ssb-$(CONFIG_SSB_PCMCIAHOST)           += pcmcia.o bridge_pcmcia_80211.o
 ssb-$(CONFIG_SSB_SDIOHOST)             += sdio.o
+ssb-$(CONFIG_SSB_HOST_SOC)             += host_soc.o
 
 # built-in drivers
 ssb-y                                  += driver_chipcommon.o
diff --git a/drivers/ssb/bridge_pcmcia_80211.c b/drivers/ssb/bridge_pcmcia_80211.c
new file mode 100644 (file)
index 0000000..d70568e
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Broadcom 43xx PCMCIA-SSB bridge module
+ *
+ * Copyright (c) 2007 Michael Buesch <m@bues.ch>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include "ssb_private.h"
+
+static const struct pcmcia_device_id ssb_host_pcmcia_tbl[] = {
+       PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448),
+       PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476),
+       PCMCIA_DEVICE_NULL,
+};
+
+MODULE_DEVICE_TABLE(pcmcia, ssb_host_pcmcia_tbl);
+
+static int ssb_host_pcmcia_probe(struct pcmcia_device *dev)
+{
+       struct ssb_bus *ssb;
+       int err = -ENOMEM;
+       int res = 0;
+
+       ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
+       if (!ssb)
+               goto out_error;
+
+       err = -ENODEV;
+
+       dev->config_flags |= CONF_ENABLE_IRQ;
+
+       dev->resource[2]->flags |=  WIN_ENABLE | WIN_DATA_WIDTH_16 |
+                        WIN_USE_WAIT;
+       dev->resource[2]->start = 0;
+       dev->resource[2]->end = SSB_CORE_SIZE;
+       res = pcmcia_request_window(dev, dev->resource[2], 250);
+       if (res != 0)
+               goto err_kfree_ssb;
+
+       res = pcmcia_map_mem_page(dev, dev->resource[2], 0);
+       if (res != 0)
+               goto err_disable;
+
+       if (!dev->irq)
+               goto err_disable;
+
+       res = pcmcia_enable_device(dev);
+       if (res != 0)
+               goto err_disable;
+
+       err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start);
+       if (err)
+               goto err_disable;
+       dev->priv = ssb;
+
+       return 0;
+
+err_disable:
+       pcmcia_disable_device(dev);
+err_kfree_ssb:
+       kfree(ssb);
+out_error:
+       ssb_err("Initialization failed (%d, %d)\n", res, err);
+       return err;
+}
+
+static void ssb_host_pcmcia_remove(struct pcmcia_device *dev)
+{
+       struct ssb_bus *ssb = dev->priv;
+
+       ssb_bus_unregister(ssb);
+       pcmcia_disable_device(dev);
+       kfree(ssb);
+       dev->priv = NULL;
+}
+
+#ifdef CONFIG_PM
+static int ssb_host_pcmcia_suspend(struct pcmcia_device *dev)
+{
+       struct ssb_bus *ssb = dev->priv;
+
+       return ssb_bus_suspend(ssb);
+}
+
+static int ssb_host_pcmcia_resume(struct pcmcia_device *dev)
+{
+       struct ssb_bus *ssb = dev->priv;
+
+       return ssb_bus_resume(ssb);
+}
+#else /* CONFIG_PM */
+# define ssb_host_pcmcia_suspend               NULL
+# define ssb_host_pcmcia_resume                NULL
+#endif /* CONFIG_PM */
+
+static struct pcmcia_driver ssb_host_pcmcia_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "ssb-pcmcia",
+       .id_table       = ssb_host_pcmcia_tbl,
+       .probe          = ssb_host_pcmcia_probe,
+       .remove         = ssb_host_pcmcia_remove,
+       .suspend        = ssb_host_pcmcia_suspend,
+       .resume         = ssb_host_pcmcia_resume,
+};
+
+/*
+ * These are not module init/exit functions!
+ * The module_pcmcia_driver() helper cannot be used here.
+ */
+int ssb_host_pcmcia_init(void)
+{
+       return pcmcia_register_driver(&ssb_host_pcmcia_driver);
+}
+
+void ssb_host_pcmcia_exit(void)
+{
+       pcmcia_unregister_driver(&ssb_host_pcmcia_driver);
+}
diff --git a/drivers/ssb/host_soc.c b/drivers/ssb/host_soc.c
new file mode 100644 (file)
index 0000000..c809f25
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Sonics Silicon Backplane SoC host related functions.
+ * Subsystem core
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+
+#include "ssb_private.h"
+
+static u8 ssb_host_soc_read8(struct ssb_device *dev, u16 offset)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       return readb(bus->mmio + offset);
+}
+
+static u16 ssb_host_soc_read16(struct ssb_device *dev, u16 offset)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       return readw(bus->mmio + offset);
+}
+
+static u32 ssb_host_soc_read32(struct ssb_device *dev, u16 offset)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       return readl(bus->mmio + offset);
+}
+
+#ifdef CONFIG_SSB_BLOCKIO
+static void ssb_host_soc_block_read(struct ssb_device *dev, void *buffer,
+                                   size_t count, u16 offset, u8 reg_width)
+{
+       struct ssb_bus *bus = dev->bus;
+       void __iomem *addr;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       addr = bus->mmio + offset;
+
+       switch (reg_width) {
+       case sizeof(u8): {
+               u8 *buf = buffer;
+
+               while (count) {
+                       *buf = __raw_readb(addr);
+                       buf++;
+                       count--;
+               }
+               break;
+       }
+       case sizeof(u16): {
+               __le16 *buf = buffer;
+
+               SSB_WARN_ON(count & 1);
+               while (count) {
+                       *buf = (__force __le16)__raw_readw(addr);
+                       buf++;
+                       count -= 2;
+               }
+               break;
+       }
+       case sizeof(u32): {
+               __le32 *buf = buffer;
+
+               SSB_WARN_ON(count & 3);
+               while (count) {
+                       *buf = (__force __le32)__raw_readl(addr);
+                       buf++;
+                       count -= 4;
+               }
+               break;
+       }
+       default:
+               SSB_WARN_ON(1);
+       }
+}
+#endif /* CONFIG_SSB_BLOCKIO */
+
+static void ssb_host_soc_write8(struct ssb_device *dev, u16 offset, u8 value)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       writeb(value, bus->mmio + offset);
+}
+
+static void ssb_host_soc_write16(struct ssb_device *dev, u16 offset, u16 value)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       writew(value, bus->mmio + offset);
+}
+
+static void ssb_host_soc_write32(struct ssb_device *dev, u16 offset, u32 value)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       writel(value, bus->mmio + offset);
+}
+
+#ifdef CONFIG_SSB_BLOCKIO
+static void ssb_host_soc_block_write(struct ssb_device *dev, const void *buffer,
+                                    size_t count, u16 offset, u8 reg_width)
+{
+       struct ssb_bus *bus = dev->bus;
+       void __iomem *addr;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       addr = bus->mmio + offset;
+
+       switch (reg_width) {
+       case sizeof(u8): {
+               const u8 *buf = buffer;
+
+               while (count) {
+                       __raw_writeb(*buf, addr);
+                       buf++;
+                       count--;
+               }
+               break;
+       }
+       case sizeof(u16): {
+               const __le16 *buf = buffer;
+
+               SSB_WARN_ON(count & 1);
+               while (count) {
+                       __raw_writew((__force u16)(*buf), addr);
+                       buf++;
+                       count -= 2;
+               }
+               break;
+       }
+       case sizeof(u32): {
+               const __le32 *buf = buffer;
+
+               SSB_WARN_ON(count & 3);
+               while (count) {
+                       __raw_writel((__force u32)(*buf), addr);
+                       buf++;
+                       count -= 4;
+               }
+               break;
+       }
+       default:
+               SSB_WARN_ON(1);
+       }
+}
+#endif /* CONFIG_SSB_BLOCKIO */
+
+/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */
+const struct ssb_bus_ops ssb_host_soc_ops = {
+       .read8          = ssb_host_soc_read8,
+       .read16         = ssb_host_soc_read16,
+       .read32         = ssb_host_soc_read32,
+       .write8         = ssb_host_soc_write8,
+       .write16        = ssb_host_soc_write16,
+       .write32        = ssb_host_soc_write32,
+#ifdef CONFIG_SSB_BLOCKIO
+       .block_read     = ssb_host_soc_block_read,
+       .block_write    = ssb_host_soc_block_write,
+#endif
+};
index a48a7439a2067d4311f81ce625f89ebcea6d1a24..5d1e9a0fc3893b75a4a87b8ea50df2e7899ba4e1 100644 (file)
@@ -596,166 +596,6 @@ error:
        return err;
 }
 
-static u8 ssb_ssb_read8(struct ssb_device *dev, u16 offset)
-{
-       struct ssb_bus *bus = dev->bus;
-
-       offset += dev->core_index * SSB_CORE_SIZE;
-       return readb(bus->mmio + offset);
-}
-
-static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset)
-{
-       struct ssb_bus *bus = dev->bus;
-
-       offset += dev->core_index * SSB_CORE_SIZE;
-       return readw(bus->mmio + offset);
-}
-
-static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset)
-{
-       struct ssb_bus *bus = dev->bus;
-
-       offset += dev->core_index * SSB_CORE_SIZE;
-       return readl(bus->mmio + offset);
-}
-
-#ifdef CONFIG_SSB_BLOCKIO
-static void ssb_ssb_block_read(struct ssb_device *dev, void *buffer,
-                              size_t count, u16 offset, u8 reg_width)
-{
-       struct ssb_bus *bus = dev->bus;
-       void __iomem *addr;
-
-       offset += dev->core_index * SSB_CORE_SIZE;
-       addr = bus->mmio + offset;
-
-       switch (reg_width) {
-       case sizeof(u8): {
-               u8 *buf = buffer;
-
-               while (count) {
-                       *buf = __raw_readb(addr);
-                       buf++;
-                       count--;
-               }
-               break;
-       }
-       case sizeof(u16): {
-               __le16 *buf = buffer;
-
-               SSB_WARN_ON(count & 1);
-               while (count) {
-                       *buf = (__force __le16)__raw_readw(addr);
-                       buf++;
-                       count -= 2;
-               }
-               break;
-       }
-       case sizeof(u32): {
-               __le32 *buf = buffer;
-
-               SSB_WARN_ON(count & 3);
-               while (count) {
-                       *buf = (__force __le32)__raw_readl(addr);
-                       buf++;
-                       count -= 4;
-               }
-               break;
-       }
-       default:
-               SSB_WARN_ON(1);
-       }
-}
-#endif /* CONFIG_SSB_BLOCKIO */
-
-static void ssb_ssb_write8(struct ssb_device *dev, u16 offset, u8 value)
-{
-       struct ssb_bus *bus = dev->bus;
-
-       offset += dev->core_index * SSB_CORE_SIZE;
-       writeb(value, bus->mmio + offset);
-}
-
-static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value)
-{
-       struct ssb_bus *bus = dev->bus;
-
-       offset += dev->core_index * SSB_CORE_SIZE;
-       writew(value, bus->mmio + offset);
-}
-
-static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value)
-{
-       struct ssb_bus *bus = dev->bus;
-
-       offset += dev->core_index * SSB_CORE_SIZE;
-       writel(value, bus->mmio + offset);
-}
-
-#ifdef CONFIG_SSB_BLOCKIO
-static void ssb_ssb_block_write(struct ssb_device *dev, const void *buffer,
-                               size_t count, u16 offset, u8 reg_width)
-{
-       struct ssb_bus *bus = dev->bus;
-       void __iomem *addr;
-
-       offset += dev->core_index * SSB_CORE_SIZE;
-       addr = bus->mmio + offset;
-
-       switch (reg_width) {
-       case sizeof(u8): {
-               const u8 *buf = buffer;
-
-               while (count) {
-                       __raw_writeb(*buf, addr);
-                       buf++;
-                       count--;
-               }
-               break;
-       }
-       case sizeof(u16): {
-               const __le16 *buf = buffer;
-
-               SSB_WARN_ON(count & 1);
-               while (count) {
-                       __raw_writew((__force u16)(*buf), addr);
-                       buf++;
-                       count -= 2;
-               }
-               break;
-       }
-       case sizeof(u32): {
-               const __le32 *buf = buffer;
-
-               SSB_WARN_ON(count & 3);
-               while (count) {
-                       __raw_writel((__force u32)(*buf), addr);
-                       buf++;
-                       count -= 4;
-               }
-               break;
-       }
-       default:
-               SSB_WARN_ON(1);
-       }
-}
-#endif /* CONFIG_SSB_BLOCKIO */
-
-/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */
-static const struct ssb_bus_ops ssb_ssb_ops = {
-       .read8          = ssb_ssb_read8,
-       .read16         = ssb_ssb_read16,
-       .read32         = ssb_ssb_read32,
-       .write8         = ssb_ssb_write8,
-       .write16        = ssb_ssb_write16,
-       .write32        = ssb_ssb_write32,
-#ifdef CONFIG_SSB_BLOCKIO
-       .block_read     = ssb_ssb_block_read,
-       .block_write    = ssb_ssb_block_write,
-#endif
-};
-
 static int ssb_fetch_invariants(struct ssb_bus *bus,
                                ssb_invariants_func_t get_invariants)
 {
@@ -876,7 +716,6 @@ int ssb_bus_pcibus_register(struct ssb_bus *bus, struct pci_dev *host_pci)
 
        return err;
 }
-EXPORT_SYMBOL(ssb_bus_pcibus_register);
 #endif /* CONFIG_SSB_PCIHOST */
 
 #ifdef CONFIG_SSB_PCMCIAHOST
@@ -898,7 +737,6 @@ int ssb_bus_pcmciabus_register(struct ssb_bus *bus,
 
        return err;
 }
-EXPORT_SYMBOL(ssb_bus_pcmciabus_register);
 #endif /* CONFIG_SSB_PCMCIAHOST */
 
 #ifdef CONFIG_SSB_SDIOHOST
@@ -923,13 +761,14 @@ int ssb_bus_sdiobus_register(struct ssb_bus *bus, struct sdio_func *func,
 EXPORT_SYMBOL(ssb_bus_sdiobus_register);
 #endif /* CONFIG_SSB_PCMCIAHOST */
 
+#ifdef CONFIG_SSB_HOST_SOC
 int ssb_bus_ssbbus_register(struct ssb_bus *bus, unsigned long baseaddr,
                            ssb_invariants_func_t get_invariants)
 {
        int err;
 
        bus->bustype = SSB_BUSTYPE_SSB;
-       bus->ops = &ssb_ssb_ops;
+       bus->ops = &ssb_host_soc_ops;
 
        err = ssb_bus_register(bus, get_invariants, baseaddr);
        if (!err) {
@@ -939,6 +778,7 @@ int ssb_bus_ssbbus_register(struct ssb_bus *bus, unsigned long baseaddr,
 
        return err;
 }
+#endif
 
 int __ssb_driver_register(struct ssb_driver *drv, struct module *owner)
 {
@@ -1465,6 +1305,12 @@ static int __init ssb_modinit(void)
                /* don't fail SSB init because of this */
                err = 0;
        }
+       err = ssb_host_pcmcia_init();
+       if (err) {
+               ssb_err("PCMCIA host initialization failed\n");
+               /* don't fail SSB init because of this */
+               err = 0;
+       }
        err = ssb_gige_init();
        if (err) {
                ssb_err("SSB Broadcom Gigabit Ethernet driver initialization failed\n");
@@ -1482,6 +1328,7 @@ fs_initcall(ssb_modinit);
 static void __exit ssb_modexit(void)
 {
        ssb_gige_exit();
+       ssb_host_pcmcia_exit();
        b43_pci_ssb_bridge_exit();
        bus_unregister(&ssb_bustype);
 }
index b413e01870878b51097456ec71112bf32b56f2b5..f03422bbf087f6cf7d0fbfae41db15d0d334cfaa 100644 (file)
@@ -147,8 +147,7 @@ error:
        return err;
 }
 
-int ssb_pcmcia_switch_core(struct ssb_bus *bus,
-                          struct ssb_device *dev)
+static int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
 {
        int err;
 
index b2d36f7736c537ef5ba6ec65d6144f965b75f971..2278e43614bd13c3c0c34afaa81f9a20f944f586 100644 (file)
@@ -200,7 +200,7 @@ out:
 }
 
 /* host must be already claimed */
-int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
+static int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
 {
        u8 coreidx = dev->core_index;
        u32 sbaddr;
index eb507a50a564dae01362b5cec6ae2ad25c766100..15bfd5c7d2d72eed45aba2560e023936afa67775 100644 (file)
@@ -85,8 +85,6 @@ static inline int ssb_pci_init(struct ssb_bus *bus)
 
 /* pcmcia.c */
 #ifdef CONFIG_SSB_PCMCIAHOST
-extern int ssb_pcmcia_switch_core(struct ssb_bus *bus,
-                                 struct ssb_device *dev);
 extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
                                     u8 coreidx);
 extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
@@ -96,13 +94,10 @@ extern int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
 extern int ssb_pcmcia_hardware_setup(struct ssb_bus *bus);
 extern void ssb_pcmcia_exit(struct ssb_bus *bus);
 extern int ssb_pcmcia_init(struct ssb_bus *bus);
+extern int ssb_host_pcmcia_init(void);
+extern void ssb_host_pcmcia_exit(void);
 extern const struct ssb_bus_ops ssb_pcmcia_ops;
 #else /* CONFIG_SSB_PCMCIAHOST */
-static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus,
-                                        struct ssb_device *dev)
-{
-       return 0;
-}
 static inline int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
                                            u8 coreidx)
 {
@@ -124,6 +119,13 @@ static inline int ssb_pcmcia_init(struct ssb_bus *bus)
 {
        return 0;
 }
+static inline int ssb_host_pcmcia_init(void)
+{
+       return 0;
+}
+static inline void ssb_host_pcmcia_exit(void)
+{
+}
 #endif /* CONFIG_SSB_PCMCIAHOST */
 
 /* sdio.c */
@@ -132,9 +134,7 @@ extern int ssb_sdio_get_invariants(struct ssb_bus *bus,
                                     struct ssb_init_invariants *iv);
 
 extern u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset);
-extern int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev);
 extern int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx);
-extern int ssb_sdio_hardware_setup(struct ssb_bus *bus);
 extern void ssb_sdio_exit(struct ssb_bus *bus);
 extern int ssb_sdio_init(struct ssb_bus *bus);
 
@@ -144,19 +144,10 @@ static inline u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset)
 {
        return 0;
 }
-static inline int ssb_sdio_switch_core(struct ssb_bus *bus,
-                                        struct ssb_device *dev)
-{
-       return 0;
-}
 static inline int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
 {
        return 0;
 }
-static inline int ssb_sdio_hardware_setup(struct ssb_bus *bus)
-{
-       return 0;
-}
 static inline void ssb_sdio_exit(struct ssb_bus *bus)
 {
 }
@@ -166,6 +157,13 @@ static inline int ssb_sdio_init(struct ssb_bus *bus)
 }
 #endif /* CONFIG_SSB_SDIOHOST */
 
+/**************************************************
+ * host_soc.c
+ **************************************************/
+
+#ifdef CONFIG_SSB_HOST_SOC
+extern const struct ssb_bus_ops ssb_host_soc_ops;
+#endif
 
 /* scan.c */
 extern const char *ssb_core_name(u16 coreid);
index 98af3b1f2d2a13efea585a11429bc00a48451582..aa5ab6c80ed4ffefc12aaca9c1337e403371d478 100644 (file)
@@ -7,7 +7,7 @@
 #include <linux/workqueue.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
-#include <asm/cmpxchg.h>
+#include <linux/atomic.h>
 
 #include "speakup.h"
 
index bb809cf3661770e63e0a50ceb19017a0ea087d0f..8b70a1627356d337c8bad36f6e29b65e84c2f148 100644 (file)
@@ -88,8 +88,8 @@ struct iucv_tty_buffer {
 };
 
 /* IUCV callback handler */
-static int hvc_iucv_path_pending(struct iucv_path *, u8[8], u8[16]);
-static void hvc_iucv_path_severed(struct iucv_path *, u8[16]);
+static int hvc_iucv_path_pending(struct iucv_path *, u8 *, u8 *);
+static void hvc_iucv_path_severed(struct iucv_path *, u8 *);
 static void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *);
 static void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *);
 
@@ -782,8 +782,8 @@ static int hvc_iucv_filter_connreq(u8 ipvmid[8])
  *
  * Locking:    struct hvc_iucv_private->lock
  */
-static int hvc_iucv_path_pending(struct iucv_path *path,
-                                 u8 ipvmid[8], u8 ipuser[16])
+static int hvc_iucv_path_pending(struct iucv_path *path, u8 *ipvmid,
+                                 u8 *ipuser)
 {
        struct hvc_iucv_private *priv, *tmp;
        u8 wildcard[9] = "lnxhvc  ";
@@ -881,7 +881,7 @@ out_path_handled:
  *
  * Locking:    struct hvc_iucv_private->lock
  */
-static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
+static void hvc_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
 {
        struct hvc_iucv_private *priv = path->private;
 
index 70f2b8a2e6cfe5d62d6509c507670aee8c981dd7..1bd9232ff76fcb774e6b1e9e1fb8acdb62ef267d 100644 (file)
@@ -391,9 +391,20 @@ static int omap2430_musb_init(struct musb *musb)
        }
        musb->isr = omap2430_musb_interrupt;
 
+       /*
+        * Enable runtime PM for musb parent (this driver). We can't
+        * do it earlier as struct musb is not yet allocated and we
+        * need to touch the musb registers for runtime PM.
+        */
+       pm_runtime_enable(glue->dev);
+       status = pm_runtime_get_sync(glue->dev);
+       if (status < 0)
+               goto err1;
+
        status = pm_runtime_get_sync(dev);
        if (status < 0) {
                dev_err(dev, "pm_runtime_get_sync FAILED %d\n", status);
+               pm_runtime_put_sync(glue->dev);
                goto err1;
        }
 
@@ -426,6 +437,7 @@ static int omap2430_musb_init(struct musb *musb)
        phy_power_on(musb->phy);
 
        pm_runtime_put_noidle(musb->controller);
+       pm_runtime_put_noidle(glue->dev);
        return 0;
 
 err1:
@@ -626,7 +638,11 @@ static int omap2430_probe(struct platform_device *pdev)
                goto err2;
        }
 
-       pm_runtime_enable(&pdev->dev);
+       /*
+        * Note that we cannot enable PM runtime yet for this
+        * driver as we need struct musb initialized first.
+        * See omap2430_musb_init above.
+        */
 
        ret = platform_device_add(musb);
        if (ret) {
@@ -675,11 +691,12 @@ static int omap2430_runtime_resume(struct device *dev)
        struct omap2430_glue            *glue = dev_get_drvdata(dev);
        struct musb                     *musb = glue_to_musb(glue);
 
-       if (musb) {
-               omap2430_low_level_init(musb);
-               musb_writel(musb->mregs, OTG_INTERFSEL,
-                               musb->context.otg_interfsel);
-       }
+       if (!musb)
+               return -EPROBE_DEFER;
+
+       omap2430_low_level_init(musb);
+       musb_writel(musb->mregs, OTG_INTERFSEL,
+                   musb->context.otg_interfsel);
 
        return 0;
 }
index 8fc15c0ba3399441959e2570bfb4ff131fa80948..277160bc6f2587325e75da7a36099c61ff37da6a 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
 #include <linux/phy/phy.h>
-#include <linux/platform_data/gpio-rcar.h>
 #include <linux/usb/phy.h>
 #include "common.h"
 #include "rcar2.h"
index 4772862b71a744091efef2216d9deaeca54567d1..d3f767448a72c75f468afe6b922e9306c35e5011 100644 (file)
@@ -183,10 +183,17 @@ static inline bool vhost_has_feature(struct vhost_virtqueue *vq, int bit)
        return vq->acked_features & (1ULL << bit);
 }
 
+#ifdef CONFIG_VHOST_CROSS_ENDIAN_LEGACY
 static inline bool vhost_is_little_endian(struct vhost_virtqueue *vq)
 {
        return vq->is_le;
 }
+#else
+static inline bool vhost_is_little_endian(struct vhost_virtqueue *vq)
+{
+       return virtio_legacy_is_little_endian() || vq->is_le;
+}
+#endif
 
 /* Memory accessors */
 static inline u16 vhost16_to_cpu(struct vhost_virtqueue *vq, __virtio16 val)
index 4bfff349b1fbb56f0b99e206f21fc2986ad4db0d..95d293b7445a83473bc2131cf657f5de14ef52b3 100644 (file)
@@ -114,6 +114,20 @@ static int efifb_setup(char *options)
        return 0;
 }
 
+static inline bool fb_base_is_valid(void)
+{
+       if (screen_info.lfb_base)
+               return true;
+
+       if (!(screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE))
+               return false;
+
+       if (screen_info.ext_lfb_base)
+               return true;
+
+       return false;
+}
+
 static int efifb_probe(struct platform_device *dev)
 {
        struct fb_info *info;
@@ -141,7 +155,7 @@ static int efifb_probe(struct platform_device *dev)
                screen_info.lfb_depth = 32;
        if (!screen_info.pages)
                screen_info.pages = 1;
-       if (!screen_info.lfb_base) {
+       if (!fb_base_is_valid()) {
                printk(KERN_DEBUG "efifb: invalid framebuffer address\n");
                return -ENODEV;
        }
@@ -160,6 +174,14 @@ static int efifb_probe(struct platform_device *dev)
        }
 
        efifb_fix.smem_start = screen_info.lfb_base;
+
+       if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) {
+               u64 ext_lfb_base;
+
+               ext_lfb_base = (u64)(unsigned long)screen_info.ext_lfb_base << 32;
+               efifb_fix.smem_start |= ext_lfb_base;
+       }
+
        efifb_defined.bits_per_pixel = screen_info.lfb_depth;
        efifb_defined.xres = screen_info.lfb_width;
        efifb_defined.yres = screen_info.lfb_height;
index 6c672ad329e9a8c6bf3b7925f9e344ea1c5991c4..c6986dce03348b3e0da89335c7853267e9efa8e8 100644 (file)
--- a/fs/file.c
+++ b/fs/file.c
@@ -56,6 +56,9 @@ static void free_fdtable_rcu(struct rcu_head *rcu)
        __free_fdtable(container_of(rcu, struct fdtable, rcu));
 }
 
+#define BITBIT_NR(nr)  BITS_TO_LONGS(BITS_TO_LONGS(nr))
+#define BITBIT_SIZE(nr)        (BITBIT_NR(nr) * sizeof(long))
+
 /*
  * Expand the fdset in the files_struct.  Called with the files spinlock
  * held for write.
@@ -77,6 +80,11 @@ static void copy_fdtable(struct fdtable *nfdt, struct fdtable *ofdt)
        memset((char *)(nfdt->open_fds) + cpy, 0, set);
        memcpy(nfdt->close_on_exec, ofdt->close_on_exec, cpy);
        memset((char *)(nfdt->close_on_exec) + cpy, 0, set);
+
+       cpy = BITBIT_SIZE(ofdt->max_fds);
+       set = BITBIT_SIZE(nfdt->max_fds) - cpy;
+       memcpy(nfdt->full_fds_bits, ofdt->full_fds_bits, cpy);
+       memset(cpy+(char *)nfdt->full_fds_bits, 0, set);
 }
 
 static struct fdtable * alloc_fdtable(unsigned int nr)
@@ -115,12 +123,14 @@ static struct fdtable * alloc_fdtable(unsigned int nr)
        fdt->fd = data;
 
        data = alloc_fdmem(max_t(size_t,
-                                2 * nr / BITS_PER_BYTE, L1_CACHE_BYTES));
+                                2 * nr / BITS_PER_BYTE + BITBIT_SIZE(nr), L1_CACHE_BYTES));
        if (!data)
                goto out_arr;
        fdt->open_fds = data;
        data += nr / BITS_PER_BYTE;
        fdt->close_on_exec = data;
+       data += nr / BITS_PER_BYTE;
+       fdt->full_fds_bits = data;
 
        return fdt;
 
@@ -226,17 +236,22 @@ static inline void __set_close_on_exec(int fd, struct fdtable *fdt)
 
 static inline void __clear_close_on_exec(int fd, struct fdtable *fdt)
 {
-       __clear_bit(fd, fdt->close_on_exec);
+       if (test_bit(fd, fdt->close_on_exec))
+               __clear_bit(fd, fdt->close_on_exec);
 }
 
-static inline void __set_open_fd(int fd, struct fdtable *fdt)
+static inline void __set_open_fd(unsigned int fd, struct fdtable *fdt)
 {
        __set_bit(fd, fdt->open_fds);
+       fd /= BITS_PER_LONG;
+       if (!~fdt->open_fds[fd])
+               __set_bit(fd, fdt->full_fds_bits);
 }
 
-static inline void __clear_open_fd(int fd, struct fdtable *fdt)
+static inline void __clear_open_fd(unsigned int fd, struct fdtable *fdt)
 {
        __clear_bit(fd, fdt->open_fds);
+       __clear_bit(fd / BITS_PER_LONG, fdt->full_fds_bits);
 }
 
 static int count_open_files(struct fdtable *fdt)
@@ -280,6 +295,7 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
        new_fdt->max_fds = NR_OPEN_DEFAULT;
        new_fdt->close_on_exec = newf->close_on_exec_init;
        new_fdt->open_fds = newf->open_fds_init;
+       new_fdt->full_fds_bits = newf->full_fds_bits_init;
        new_fdt->fd = &newf->fd_array[0];
 
        spin_lock(&oldf->file_lock);
@@ -323,6 +339,7 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
 
        memcpy(new_fdt->open_fds, old_fdt->open_fds, open_files / 8);
        memcpy(new_fdt->close_on_exec, old_fdt->close_on_exec, open_files / 8);
+       memcpy(new_fdt->full_fds_bits, old_fdt->full_fds_bits, BITBIT_SIZE(open_files));
 
        for (i = open_files; i != 0; i--) {
                struct file *f = *old_fds++;
@@ -454,10 +471,25 @@ struct files_struct init_files = {
                .fd             = &init_files.fd_array[0],
                .close_on_exec  = init_files.close_on_exec_init,
                .open_fds       = init_files.open_fds_init,
+               .full_fds_bits  = init_files.full_fds_bits_init,
        },
        .file_lock      = __SPIN_LOCK_UNLOCKED(init_files.file_lock),
 };
 
+static unsigned long find_next_fd(struct fdtable *fdt, unsigned long start)
+{
+       unsigned long maxfd = fdt->max_fds;
+       unsigned long maxbit = maxfd / BITS_PER_LONG;
+       unsigned long bitbit = start / BITS_PER_LONG;
+
+       bitbit = find_next_zero_bit(fdt->full_fds_bits, maxbit, bitbit) * BITS_PER_LONG;
+       if (bitbit > maxfd)
+               return maxfd;
+       if (bitbit > start)
+               start = bitbit;
+       return find_next_zero_bit(fdt->open_fds, maxfd, start);
+}
+
 /*
  * allocate a file descriptor, mark it busy.
  */
@@ -476,7 +508,7 @@ repeat:
                fd = files->next_fd;
 
        if (fd < fdt->max_fds)
-               fd = find_next_zero_bit(fdt->open_fds, fdt->max_fds, fd);
+               fd = find_next_fd(fdt, fd);
 
        /*
         * N.B. For clone tasks sharing a files structure, this test
index 29e4599f6fc1c20b73acb2c580ce8383e169c5f2..7378169e90be6ed485ac48d0cf633c8e37c4c3d2 100644 (file)
@@ -779,8 +779,8 @@ static void bdi_split_work_to_wbs(struct backing_dev_info *bdi,
                                  bool skip_if_busy)
 {
        struct bdi_writeback *last_wb = NULL;
-       struct bdi_writeback *wb = list_entry_rcu(&bdi->wb_list,
-                                               struct bdi_writeback, bdi_node);
+       struct bdi_writeback *wb = list_entry(&bdi->wb_list,
+                                             struct bdi_writeback, bdi_node);
 
        might_sleep();
 restart:
index 84d693d374284b580208fec3b8eb3c57bdd4195c..871fcb67be9741f2aab81f3d6552306dedf4c967 100644 (file)
@@ -81,11 +81,11 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
        if (len == 0)
                return 0;
 
-       old_file = ovl_path_open(old, O_RDONLY);
+       old_file = ovl_path_open(old, O_LARGEFILE | O_RDONLY);
        if (IS_ERR(old_file))
                return PTR_ERR(old_file);
 
-       new_file = ovl_path_open(new, O_WRONLY);
+       new_file = ovl_path_open(new, O_LARGEFILE | O_WRONLY);
        if (IS_ERR(new_file)) {
                error = PTR_ERR(new_file);
                goto out_fput;
@@ -267,7 +267,7 @@ out:
 
 out_cleanup:
        ovl_cleanup(wdir, newdentry);
-       goto out;
+       goto out2;
 }
 
 /*
index d9da5a4e93821ddbee9f2fb449666c207417e523..ec0c2a050043afbb3eff4c7451930dc6d86006ec 100644 (file)
@@ -363,6 +363,9 @@ struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags)
                ovl_path_upper(dentry, &realpath);
        }
 
+       if (realpath.dentry->d_flags & DCACHE_OP_SELECT_INODE)
+               return realpath.dentry->d_op->d_select_inode(realpath.dentry, file_flags);
+
        return d_backing_inode(realpath.dentry);
 }
 
index 79073d68b475d71b0f87902550b3eeef945885b5..e38ee0fed24a2f7a343f294a793b6abea93f6f97 100644 (file)
@@ -544,6 +544,7 @@ static void ovl_put_super(struct super_block *sb)
        mntput(ufs->upper_mnt);
        for (i = 0; i < ufs->numlower; i++)
                mntput(ufs->lower_mnt[i]);
+       kfree(ufs->lower_mnt);
 
        kfree(ufs->config.lowerdir);
        kfree(ufs->config.upperdir);
@@ -1048,6 +1049,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                oe->lowerstack[i].dentry = stack[i].dentry;
                oe->lowerstack[i].mnt = ufs->lower_mnt[i];
        }
+       kfree(stack);
 
        root_dentry->d_fsdata = oe;
 
index f60f0121e3319ec616d0a0151d3e4df146637c88..eed2050db9be9c7795acd2153f976d4742e2fe82 100644 (file)
@@ -375,7 +375,7 @@ int proc_pid_status(struct seq_file *m, struct pid_namespace *ns,
 static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
                        struct pid *pid, struct task_struct *task, int whole)
 {
-       unsigned long vsize, eip, esp, wchan = ~0UL;
+       unsigned long vsize, eip, esp, wchan = 0;
        int priority, nice;
        int tty_pgrp = -1, tty_nr = 0;
        sigset_t sigign, sigcatch;
@@ -507,7 +507,19 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
        seq_put_decimal_ull(m, ' ', task->blocked.sig[0] & 0x7fffffffUL);
        seq_put_decimal_ull(m, ' ', sigign.sig[0] & 0x7fffffffUL);
        seq_put_decimal_ull(m, ' ', sigcatch.sig[0] & 0x7fffffffUL);
-       seq_put_decimal_ull(m, ' ', wchan);
+
+       /*
+        * We used to output the absolute kernel address, but that's an
+        * information leak - so instead we show a 0/1 flag here, to signal
+        * to user-space whether there's a wchan field in /proc/PID/wchan.
+        *
+        * This works with older implementations of procps as well.
+        */
+       if (wchan)
+               seq_puts(m, " 1");
+       else
+               seq_puts(m, " 0");
+
        seq_put_decimal_ull(m, ' ', 0);
        seq_put_decimal_ull(m, ' ', 0);
        seq_put_decimal_ll(m, ' ', task->exit_signal);
index b25eee4cead5398b69889c95c29480ba9862c397..29595af328669543a70115a7613539a48f6e4226 100644 (file)
@@ -430,13 +430,10 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,
 
        wchan = get_wchan(task);
 
-       if (lookup_symbol_name(wchan, symname) < 0) {
-               if (!ptrace_may_access(task, PTRACE_MODE_READ))
-                       return 0;
-               seq_printf(m, "%lu", wchan);
-       } else {
+       if (wchan && ptrace_may_access(task, PTRACE_MODE_READ) && !lookup_symbol_name(wchan, symname))
                seq_printf(m, "%s", symname);
-       }
+       else
+               seq_putc(m, '0');
 
        return 0;
 }
index d3ebf2e618535bd2fa7daa0bfe948cb1a9bfc062..9155a5a0d3b9de6114e32d4108e67d7c038f0e20 100644 (file)
@@ -27,7 +27,6 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
 {
        struct sysinfo i;
        unsigned long committed;
-       struct vmalloc_info vmi;
        long cached;
        long available;
        unsigned long pagecache;
@@ -49,8 +48,6 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
        if (cached < 0)
                cached = 0;
 
-       get_vmalloc_info(&vmi);
-
        for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++)
                pages[lru] = global_page_state(NR_LRU_BASE + lru);
 
@@ -191,8 +188,8 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
                K(vm_commit_limit()),
                K(committed),
                (unsigned long)VMALLOC_TOTAL >> 10,
-               vmi.used >> 10,
-               vmi.largest_chunk >> 10
+               0ul, // used to be vmalloc 'used'
+               0ul  // used to be vmalloc 'largest_chunk'
 #ifdef CONFIG_MEMORY_FAILURE
                , atomic_long_read(&num_poisoned_pages) << (PAGE_SHIFT - 10)
 #endif
index a94cbebbc33df5dab756ce6fce0a739a13212784..eb1973bad80b11376c1f9dcbc0f9f2b7c3eff0cb 100644 (file)
@@ -35,7 +35,7 @@ typedef atomic_t atomic_long_t;
 #endif
 
 #define ATOMIC_LONG_READ_OP(mo)                                                \
-static inline long atomic_long_read##mo(atomic_long_t *l)              \
+static inline long atomic_long_read##mo(const atomic_long_t *l)                \
 {                                                                      \
        ATOMIC_LONG_PFX(_t) *v = (ATOMIC_LONG_PFX(_t) *)l;              \
                                                                        \
@@ -112,19 +112,23 @@ static inline void atomic_long_dec(atomic_long_t *l)
        ATOMIC_LONG_PFX(_dec)(v);
 }
 
-static inline void atomic_long_add(long i, atomic_long_t *l)
-{
-       ATOMIC_LONG_PFX(_t) *v = (ATOMIC_LONG_PFX(_t) *)l;
-
-       ATOMIC_LONG_PFX(_add)(i, v);
+#define ATOMIC_LONG_OP(op)                                             \
+static inline void                                                     \
+atomic_long_##op(long i, atomic_long_t *l)                             \
+{                                                                      \
+       ATOMIC_LONG_PFX(_t) *v = (ATOMIC_LONG_PFX(_t) *)l;              \
+                                                                       \
+       ATOMIC_LONG_PFX(_##op)(i, v);                                   \
 }
 
-static inline void atomic_long_sub(long i, atomic_long_t *l)
-{
-       ATOMIC_LONG_PFX(_t) *v = (ATOMIC_LONG_PFX(_t) *)l;
+ATOMIC_LONG_OP(add)
+ATOMIC_LONG_OP(sub)
+ATOMIC_LONG_OP(and)
+ATOMIC_LONG_OP(or)
+ATOMIC_LONG_OP(xor)
+ATOMIC_LONG_OP(andnot)
 
-       ATOMIC_LONG_PFX(_sub)(i, v);
-}
+#undef ATOMIC_LONG_OP
 
 static inline int atomic_long_sub_and_test(long i, atomic_long_t *l)
 {
@@ -154,19 +158,24 @@ static inline int atomic_long_add_negative(long i, atomic_long_t *l)
        return ATOMIC_LONG_PFX(_add_negative)(i, v);
 }
 
-static inline long atomic_long_inc_return(atomic_long_t *l)
-{
-       ATOMIC_LONG_PFX(_t) *v = (ATOMIC_LONG_PFX(_t) *)l;
-
-       return (long)ATOMIC_LONG_PFX(_inc_return)(v);
-}
-
-static inline long atomic_long_dec_return(atomic_long_t *l)
-{
-       ATOMIC_LONG_PFX(_t) *v = (ATOMIC_LONG_PFX(_t) *)l;
-
-       return (long)ATOMIC_LONG_PFX(_dec_return)(v);
+#define ATOMIC_LONG_INC_DEC_OP(op, mo)                                 \
+static inline long                                                     \
+atomic_long_##op##_return##mo(atomic_long_t *l)                                \
+{                                                                      \
+       ATOMIC_LONG_PFX(_t) *v = (ATOMIC_LONG_PFX(_t) *)l;              \
+                                                                       \
+       return (long)ATOMIC_LONG_PFX(_##op##_return##mo)(v);            \
 }
+ATOMIC_LONG_INC_DEC_OP(inc,)
+ATOMIC_LONG_INC_DEC_OP(inc, _relaxed)
+ATOMIC_LONG_INC_DEC_OP(inc, _acquire)
+ATOMIC_LONG_INC_DEC_OP(inc, _release)
+ATOMIC_LONG_INC_DEC_OP(dec,)
+ATOMIC_LONG_INC_DEC_OP(dec, _relaxed)
+ATOMIC_LONG_INC_DEC_OP(dec, _acquire)
+ATOMIC_LONG_INC_DEC_OP(dec, _release)
+
+#undef ATOMIC_LONG_INC_DEC_OP
 
 static inline long atomic_long_add_unless(atomic_long_t *l, long a, long u)
 {
index d4d7e337fdcb5d7bda73656c1e8b66fee6ff2cc3..74f1a3704d7a1ddf61707b6da03fc2348521e4a1 100644 (file)
@@ -127,7 +127,7 @@ ATOMIC_OP(xor, ^)
  * Atomically reads the value of @v.
  */
 #ifndef atomic_read
-#define atomic_read(v) ACCESS_ONCE((v)->counter)
+#define atomic_read(v) READ_ONCE((v)->counter)
 #endif
 
 /**
@@ -137,7 +137,7 @@ ATOMIC_OP(xor, ^)
  *
  * Atomically sets the value of @v to @i.
  */
-#define atomic_set(v, i) (((v)->counter) = (i))
+#define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
 
 #include <linux/irqflags.h>
 
index d4f9fb4e53dfa7ea2709bc63f5c98f7c72e64cf0..fd694cfd678af712b2c3ca1055331df24a8db8ba 100644 (file)
@@ -20,7 +20,7 @@
 static inline void
 __mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
 {
-       if (unlikely(atomic_dec_return(count) < 0))
+       if (unlikely(atomic_dec_return_acquire(count) < 0))
                fail_fn(count);
 }
 
@@ -35,7 +35,7 @@ __mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
 static inline int
 __mutex_fastpath_lock_retval(atomic_t *count)
 {
-       if (unlikely(atomic_dec_return(count) < 0))
+       if (unlikely(atomic_dec_return_acquire(count) < 0))
                return -1;
        return 0;
 }
@@ -56,7 +56,7 @@ __mutex_fastpath_lock_retval(atomic_t *count)
 static inline void
 __mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
 {
-       if (unlikely(atomic_inc_return(count) <= 0))
+       if (unlikely(atomic_inc_return_release(count) <= 0))
                fail_fn(count);
 }
 
@@ -80,7 +80,7 @@ __mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
 static inline int
 __mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
 {
-       if (likely(atomic_cmpxchg(count, 1, 0) == 1))
+       if (likely(atomic_cmpxchg_acquire(count, 1, 0) == 1))
                return 1;
        return 0;
 }
index f169ec064785beea6c8ce4f1bee1d193a9faf079..a6b4a7bd6ac9770356e066c51f295c6b9c33793f 100644 (file)
@@ -31,7 +31,7 @@ __mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
                 * to ensure that any waiting tasks are woken up by the
                 * unlock slow path.
                 */
-               if (likely(atomic_xchg(count, -1) != 1))
+               if (likely(atomic_xchg_acquire(count, -1) != 1))
                        fail_fn(count);
 }
 
@@ -46,7 +46,7 @@ __mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
 static inline int
 __mutex_fastpath_lock_retval(atomic_t *count)
 {
-       if (unlikely(atomic_xchg(count, 0) != 1))
+       if (unlikely(atomic_xchg_acquire(count, 0) != 1))
                if (likely(atomic_xchg(count, -1) != 1))
                        return -1;
        return 0;
@@ -67,7 +67,7 @@ __mutex_fastpath_lock_retval(atomic_t *count)
 static inline void
 __mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
 {
-       if (unlikely(atomic_xchg(count, 1) != 0))
+       if (unlikely(atomic_xchg_release(count, 1) != 0))
                fail_fn(count);
 }
 
@@ -91,7 +91,7 @@ __mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
 static inline int
 __mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
 {
-       int prev = atomic_xchg(count, 0);
+       int prev = atomic_xchg_acquire(count, 0);
 
        if (unlikely(prev < 0)) {
                /*
@@ -105,7 +105,7 @@ __mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
                 *   owner's unlock path needlessly, but that's not a problem
                 *   in practice. ]
                 */
-               prev = atomic_xchg(count, prev);
+               prev = atomic_xchg_acquire(count, prev);
                if (prev < 0)
                        prev = 0;
        }
index 29c57b2cb344dfc0bd73034927bfa50848d6709b..3eabbbbfd5780adf2e8c5fc76bf2632d7cbb2dc4 100644 (file)
@@ -30,9 +30,19 @@ extern int ptep_set_access_flags(struct vm_area_struct *vma,
 #endif
 
 #ifndef __HAVE_ARCH_PMDP_SET_ACCESS_FLAGS
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
 extern int pmdp_set_access_flags(struct vm_area_struct *vma,
                                 unsigned long address, pmd_t *pmdp,
                                 pmd_t entry, int dirty);
+#else
+static inline int pmdp_set_access_flags(struct vm_area_struct *vma,
+                                       unsigned long address, pmd_t *pmdp,
+                                       pmd_t entry, int dirty)
+{
+       BUILD_BUG();
+       return 0;
+}
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 #endif
 
 #ifndef __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
@@ -64,12 +74,12 @@ static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
                set_pmd_at(vma->vm_mm, address, pmdp, pmd_mkold(pmd));
        return r;
 }
-#else /* CONFIG_TRANSPARENT_HUGEPAGE */
+#else
 static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
                                            unsigned long address,
                                            pmd_t *pmdp)
 {
-       BUG();
+       BUILD_BUG();
        return 0;
 }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
@@ -81,8 +91,21 @@ int ptep_clear_flush_young(struct vm_area_struct *vma,
 #endif
 
 #ifndef __HAVE_ARCH_PMDP_CLEAR_YOUNG_FLUSH
-int pmdp_clear_flush_young(struct vm_area_struct *vma,
-                          unsigned long address, pmd_t *pmdp);
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+extern int pmdp_clear_flush_young(struct vm_area_struct *vma,
+                                 unsigned long address, pmd_t *pmdp);
+#else
+/*
+ * Despite relevant to THP only, this API is called from generic rmap code
+ * under PageTransHuge(), hence needs a dummy implementation for !THP
+ */
+static inline int pmdp_clear_flush_young(struct vm_area_struct *vma,
+                                        unsigned long address, pmd_t *pmdp)
+{
+       BUILD_BUG();
+       return 0;
+}
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 #endif
 
 #ifndef __HAVE_ARCH_PTEP_GET_AND_CLEAR
@@ -175,11 +198,11 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm,
        pmd_t old_pmd = *pmdp;
        set_pmd_at(mm, address, pmdp, pmd_wrprotect(old_pmd));
 }
-#else /* CONFIG_TRANSPARENT_HUGEPAGE */
+#else
 static inline void pmdp_set_wrprotect(struct mm_struct *mm,
                                      unsigned long address, pmd_t *pmdp)
 {
-       BUG();
+       BUILD_BUG();
 }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 #endif
@@ -248,7 +271,7 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b)
 #else /* CONFIG_TRANSPARENT_HUGEPAGE */
 static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b)
 {
-       BUG();
+       BUILD_BUG();
        return 0;
 }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
index 0bec580a48854f0a49b012b203663ebeddf98257..5d8ffa3e6f8c8a4e3715f3da4294abc860390386 100644 (file)
@@ -24,7 +24,7 @@ static __always_inline void preempt_count_set(int pc)
  * must be macros to avoid header recursion hell
  */
 #define init_task_preempt_count(p) do { \
-       task_thread_info(p)->preempt_count = PREEMPT_DISABLED; \
+       task_thread_info(p)->preempt_count = FORK_PREEMPT_COUNT; \
 } while (0)
 
 #define init_idle_preempt_count(p, cpu) do { \
index 4d76f24df5182d9a2736adb987ef7f95866050e6..0abc6b6062fbb1c203bed0f025e05e82ac3eea24 100644 (file)
 
 typedef struct qrwlock {
        atomic_t                cnts;
-       arch_spinlock_t         lock;
+       arch_spinlock_t         wait_lock;
 } arch_rwlock_t;
 
 #define        __ARCH_RW_LOCK_UNLOCKED {               \
        .cnts = ATOMIC_INIT(0),                 \
-       .lock = __ARCH_SPIN_LOCK_UNLOCKED,      \
+       .wait_lock = __ARCH_SPIN_LOCK_UNLOCKED, \
 }
 
 #endif /* __ASM_GENERIC_QRWLOCK_TYPES_H */
index d48bf5a95cc1bd6e2a3f93ebbe6f3022202c1b98..d6d5dc98d7da5509347e56737f3944a37c698490 100644 (file)
@@ -33,7 +33,7 @@
  */
 static inline void __down_read(struct rw_semaphore *sem)
 {
-       if (unlikely(atomic_long_inc_return((atomic_long_t *)&sem->count) <= 0))
+       if (unlikely(atomic_long_inc_return_acquire((atomic_long_t *)&sem->count) <= 0))
                rwsem_down_read_failed(sem);
 }
 
@@ -42,7 +42,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem)
        long tmp;
 
        while ((tmp = sem->count) >= 0) {
-               if (tmp == cmpxchg(&sem->count, tmp,
+               if (tmp == cmpxchg_acquire(&sem->count, tmp,
                                   tmp + RWSEM_ACTIVE_READ_BIAS)) {
                        return 1;
                }
@@ -57,7 +57,7 @@ static inline void __down_write_nested(struct rw_semaphore *sem, int subclass)
 {
        long tmp;
 
-       tmp = atomic_long_add_return(RWSEM_ACTIVE_WRITE_BIAS,
+       tmp = atomic_long_add_return_acquire(RWSEM_ACTIVE_WRITE_BIAS,
                                     (atomic_long_t *)&sem->count);
        if (unlikely(tmp != RWSEM_ACTIVE_WRITE_BIAS))
                rwsem_down_write_failed(sem);
@@ -72,7 +72,7 @@ static inline int __down_write_trylock(struct rw_semaphore *sem)
 {
        long tmp;
 
-       tmp = cmpxchg(&sem->count, RWSEM_UNLOCKED_VALUE,
+       tmp = cmpxchg_acquire(&sem->count, RWSEM_UNLOCKED_VALUE,
                      RWSEM_ACTIVE_WRITE_BIAS);
        return tmp == RWSEM_UNLOCKED_VALUE;
 }
@@ -84,7 +84,7 @@ static inline void __up_read(struct rw_semaphore *sem)
 {
        long tmp;
 
-       tmp = atomic_long_dec_return((atomic_long_t *)&sem->count);
+       tmp = atomic_long_dec_return_release((atomic_long_t *)&sem->count);
        if (unlikely(tmp < -1 && (tmp & RWSEM_ACTIVE_MASK) == 0))
                rwsem_wake(sem);
 }
@@ -94,7 +94,7 @@ static inline void __up_read(struct rw_semaphore *sem)
  */
 static inline void __up_write(struct rw_semaphore *sem)
 {
-       if (unlikely(atomic_long_sub_return(RWSEM_ACTIVE_WRITE_BIAS,
+       if (unlikely(atomic_long_sub_return_release(RWSEM_ACTIVE_WRITE_BIAS,
                                 (atomic_long_t *)&sem->count) < 0))
                rwsem_wake(sem);
 }
@@ -114,7 +114,14 @@ static inline void __downgrade_write(struct rw_semaphore *sem)
 {
        long tmp;
 
-       tmp = atomic_long_add_return(-RWSEM_WAITING_BIAS,
+       /*
+        * When downgrading from exclusive to shared ownership,
+        * anything inside the write-locked region cannot leak
+        * into the read side. In contrast, anything in the
+        * read-locked region is ok to be re-ordered into the
+        * write side. As such, rely on RELEASE semantics.
+        */
+       tmp = atomic_long_add_return_release(-RWSEM_WAITING_BIAS,
                                     (atomic_long_t *)&sem->count);
        if (tmp < 0)
                rwsem_downgrade_wake(sem);
index 69d163e39101bf46d6aae184d18e1938e109044c..45cd5b328040798a540dbda716b80c4d9a55a71d 100644 (file)
  * struct akcipher_request - public key request
  *
  * @base:      Common attributes for async crypto requests
- * @src:       Pointer to memory containing the input parameters
- *             The format of the parameter(s) is expeted to be Octet String
- * @dst:       Pointer to memory whare the result will be stored
- * @src_len:   Size of the input parameter
+ * @src:       Source data
+ * @dst:       Destination data
+ * @src_len:   Size of the input buffer
  * @dst_len:   Size of the output buffer. It needs to be at leaset
  *             as big as the expected result depending on the operation
  *             After operation it will be updated with the acctual size of the
- *             result. In case of error, where the dst_len was insufficient,
+ *             result.
+ *             In case of error where the dst sgl size was insufficient,
  *             it will be updated to the size required for the operation.
  * @__ctx:     Start of private context data
  */
 struct akcipher_request {
        struct crypto_async_request base;
-       void *src;
-       void *dst;
+       struct scatterlist *src;
+       struct scatterlist *dst;
        unsigned int src_len;
        unsigned int dst_len;
        void *__ctx[] CRYPTO_MINALIGN_ATTR;
@@ -67,8 +67,13 @@ struct crypto_akcipher {
  *             algorithm. In case of error, where the dst_len was insufficient,
  *             the req->dst_len will be updated to the size required for the
  *             operation
- * @setkey:    Function invokes the algorithm specific set key function, which
- *             knows how to decode and interpret the BER encoded key
+ * @set_pub_key: Function invokes the algorithm specific set public key
+ *             function, which knows how to decode and interpret
+ *             the BER encoded public key
+ * @set_priv_key: Function invokes the algorithm specific set private key
+ *             function, which knows how to decode and interpret
+ *             the BER encoded private key
+ * @max_size:  Function returns dest buffer size reqired for a given key.
  * @init:      Initialize the cryptographic transformation object.
  *             This function is used to initialize the cryptographic
  *             transformation object. This function is called only once at
@@ -89,8 +94,11 @@ struct akcipher_alg {
        int (*verify)(struct akcipher_request *req);
        int (*encrypt)(struct akcipher_request *req);
        int (*decrypt)(struct akcipher_request *req);
-       int (*setkey)(struct crypto_akcipher *tfm, const void *key,
-                     unsigned int keylen);
+       int (*set_pub_key)(struct crypto_akcipher *tfm, const void *key,
+                          unsigned int keylen);
+       int (*set_priv_key)(struct crypto_akcipher *tfm, const void *key,
+                           unsigned int keylen);
+       int (*max_size)(struct crypto_akcipher *tfm);
        int (*init)(struct crypto_akcipher *tfm);
        void (*exit)(struct crypto_akcipher *tfm);
 
@@ -229,14 +237,14 @@ static inline void akcipher_request_set_callback(struct akcipher_request *req,
  * Sets parameters required by crypto operation
  *
  * @req:       public key request
- * @src:       ptr to input parameter
- * @dst:       ptr of output parameter
- * @src_len:   size of the input buffer
- * @dst_len:   size of the output buffer. It will be updated by the
- *             implementation to reflect the acctual size of the result
+ * @src:       ptr to input scatter list
+ * @dst:       ptr to output scatter list
+ * @src_len:   size of the src input scatter list to be processed
+ * @dst_len:   size of the dst output scatter list
  */
 static inline void akcipher_request_set_crypt(struct akcipher_request *req,
-                                             void *src, void *dst,
+                                             struct scatterlist *src,
+                                             struct scatterlist *dst,
                                              unsigned int src_len,
                                              unsigned int dst_len)
 {
@@ -246,6 +254,22 @@ static inline void akcipher_request_set_crypt(struct akcipher_request *req,
        req->dst_len = dst_len;
 }
 
+/**
+ * crypto_akcipher_maxsize() -- Get len for output buffer
+ *
+ * Function returns the dest buffer size required for a given key
+ *
+ * @tfm:       AKCIPHER tfm handle allocated with crypto_alloc_akcipher()
+ *
+ * Return: minimum len for output buffer or error code in key hasn't been set
+ */
+static inline int crypto_akcipher_maxsize(struct crypto_akcipher *tfm)
+{
+       struct akcipher_alg *alg = crypto_akcipher_alg(tfm);
+
+       return alg->max_size(tfm);
+}
+
 /**
  * crypto_akcipher_encrypt() -- Invoke public key encrypt operation
  *
@@ -319,22 +343,44 @@ static inline int crypto_akcipher_verify(struct akcipher_request *req)
 }
 
 /**
- * crypto_akcipher_setkey() -- Invoke public key setkey operation
+ * crypto_akcipher_set_pub_key() -- Invoke set public key operation
+ *
+ * Function invokes the algorithm specific set key function, which knows
+ * how to decode and interpret the encoded key
+ *
+ * @tfm:       tfm handle
+ * @key:       BER encoded public key
+ * @keylen:    length of the key
+ *
+ * Return: zero on success; error code in case of error
+ */
+static inline int crypto_akcipher_set_pub_key(struct crypto_akcipher *tfm,
+                                             const void *key,
+                                             unsigned int keylen)
+{
+       struct akcipher_alg *alg = crypto_akcipher_alg(tfm);
+
+       return alg->set_pub_key(tfm, key, keylen);
+}
+
+/**
+ * crypto_akcipher_set_priv_key() -- Invoke set private key operation
  *
  * Function invokes the algorithm specific set key function, which knows
  * how to decode and interpret the encoded key
  *
  * @tfm:       tfm handle
- * @key:       BER encoded private or public key
+ * @key:       BER encoded private key
  * @keylen:    length of the key
  *
  * Return: zero on success; error code in case of error
  */
-static inline int crypto_akcipher_setkey(struct crypto_akcipher *tfm, void *key,
-                                        unsigned int keylen)
+static inline int crypto_akcipher_set_priv_key(struct crypto_akcipher *tfm,
+                                              const void *key,
+                                              unsigned int keylen)
 {
        struct akcipher_alg *alg = crypto_akcipher_alg(tfm);
 
-       return alg->setkey(tfm, key, keylen);
+       return alg->set_priv_key(tfm, key, keylen);
 }
 #endif
index 8e920b44c0ac4b14238ef0877812fab4eaf7f8b8..3d69c93d50e83d7b804f9de7260007ffc9903d69 100644 (file)
@@ -264,6 +264,20 @@ static inline unsigned int crypto_ahash_alignmask(
        return crypto_tfm_alg_alignmask(crypto_ahash_tfm(tfm));
 }
 
+/**
+ * crypto_ahash_blocksize() - obtain block size for cipher
+ * @tfm: cipher handle
+ *
+ * The block size for the message digest cipher referenced with the cipher
+ * handle is returned.
+ *
+ * Return: block size of cipher
+ */
+static inline unsigned int crypto_ahash_blocksize(struct crypto_ahash *tfm)
+{
+       return crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
+}
+
 static inline struct hash_alg_common *__crypto_hash_alg_common(
        struct crypto_alg *alg)
 {
index a8c86365439fb21447323a894461dd7c6d92dd58..f997e2d29b5a50a03cecf7aa9875a2a448466ae6 100644 (file)
@@ -20,8 +20,11 @@ struct rsa_key {
        MPI d;
 };
 
-int rsa_parse_key(struct rsa_key *rsa_key, const void *key,
-                 unsigned int key_len);
+int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key,
+                     unsigned int key_len);
+
+int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
+                      unsigned int key_len);
 
 void rsa_free_key(struct rsa_key *rsa_key);
 #endif
index e6b1e0a808aebf8c3f4659026df1c7dd54ebf126..c673d2c87c604ae86fc6bfd523a580308752148a 100644 (file)
@@ -9,7 +9,19 @@
 #ifndef _DT_BINDINGS_GPIO_GPIO_H
 #define _DT_BINDINGS_GPIO_GPIO_H
 
+/* Bit 0 express polarity */
 #define GPIO_ACTIVE_HIGH 0
 #define GPIO_ACTIVE_LOW 1
 
+/* Bit 1 express single-endedness */
+#define GPIO_PUSH_PULL 0
+#define GPIO_SINGLE_ENDED 2
+
+/*
+ * Open Drain/Collector is the combination of single-ended active low,
+ * Open Source/Emitter is the combination of single-ended active high.
+ */
+#define GPIO_OPEN_DRAIN (GPIO_SINGLE_ENDED | GPIO_ACTIVE_LOW)
+#define GPIO_OPEN_SOURCE (GPIO_SINGLE_ENDED | GPIO_ACTIVE_HIGH)
+
 #endif
diff --git a/include/dt-bindings/leds/leds-netxbig.h b/include/dt-bindings/leds/leds-netxbig.h
new file mode 100644 (file)
index 0000000..92658b0
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * This header provides constants for netxbig LED bindings.
+ *
+ * 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.
+ */
+
+#ifndef _DT_BINDINGS_LEDS_NETXBIG_H
+#define _DT_BINDINGS_LEDS_NETXBIG_H
+
+#define NETXBIG_LED_OFF                0
+#define NETXBIG_LED_ON         1
+#define NETXBIG_LED_SATA       2
+#define NETXBIG_LED_TIMER1     3
+#define NETXBIG_LED_TIMER2     4
+
+#endif /* _DT_BINDINGS_LEDS_NETXBIG_H */
index 4e14dac282bb6c963440593090bc060afecf8130..6a3538ef72753cd5ef70ac23605cf1484f852774 100644 (file)
@@ -282,7 +282,7 @@ struct vgic_v2_cpu_if {
 };
 
 struct vgic_v3_cpu_if {
-#ifdef CONFIG_ARM_GIC_V3
+#ifdef CONFIG_KVM_ARM_VGIC_V3
        u32             vgic_hcr;
        u32             vgic_vmcr;
        u32             vgic_sre;       /* Restored only, change ignored */
@@ -364,7 +364,7 @@ void kvm_vgic_set_phys_irq_active(struct irq_phys_map *map, bool active);
 int vgic_v2_probe(struct device_node *vgic_node,
                  const struct vgic_ops **ops,
                  const struct vgic_params **params);
-#ifdef CONFIG_ARM_GIC_V3
+#ifdef CONFIG_KVM_ARM_VGIC_V3
 int vgic_v3_probe(struct device_node *vgic_node,
                  const struct vgic_ops **ops,
                  const struct vgic_params **params);
index 43856d19cf4d8abc8942849202485fe76244135d..d863e12bbead6eb6161ffce64b791e73dca7fd27 100644 (file)
@@ -201,6 +201,9 @@ int acpi_register_gsi (struct device *dev, u32 gsi, int triggering, int polarity
 int acpi_gsi_to_irq (u32 gsi, unsigned int *irq);
 int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi);
 
+void acpi_set_irq_model(enum acpi_irq_model_id model,
+                       struct fwnode_handle *fwnode);
+
 #ifdef CONFIG_X86_IO_APIC
 extern int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity);
 #else
index 50fc66868402123dfee3d60738d3f16061e2a6a2..9006c4e75cf737a90335eadcd73e14d59b0f753e 100644 (file)
@@ -41,8 +41,6 @@ struct amba_driver {
        int                     (*probe)(struct amba_device *, const struct amba_id *);
        int                     (*remove)(struct amba_device *);
        void                    (*shutdown)(struct amba_device *);
-       int                     (*suspend)(struct amba_device *, pm_message_t);
-       int                     (*resume)(struct amba_device *);
        const struct amba_id    *id_table;
 };
 
index 00a5763e850e94b123880e8f10b53279bc6b78ba..301de78d65f75c194720cc5168359dbce6eab59f 100644 (file)
 #endif
 #endif /* atomic_add_return_relaxed */
 
+/* atomic_inc_return_relaxed */
+#ifndef atomic_inc_return_relaxed
+#define  atomic_inc_return_relaxed     atomic_inc_return
+#define  atomic_inc_return_acquire     atomic_inc_return
+#define  atomic_inc_return_release     atomic_inc_return
+
+#else /* atomic_inc_return_relaxed */
+
+#ifndef atomic_inc_return_acquire
+#define  atomic_inc_return_acquire(...)                                        \
+       __atomic_op_acquire(atomic_inc_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic_inc_return_release
+#define  atomic_inc_return_release(...)                                        \
+       __atomic_op_release(atomic_inc_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic_inc_return
+#define  atomic_inc_return(...)                                                \
+       __atomic_op_fence(atomic_inc_return, __VA_ARGS__)
+#endif
+#endif /* atomic_inc_return_relaxed */
+
 /* atomic_sub_return_relaxed */
 #ifndef atomic_sub_return_relaxed
 #define  atomic_sub_return_relaxed     atomic_sub_return
 #endif
 #endif /* atomic_sub_return_relaxed */
 
+/* atomic_dec_return_relaxed */
+#ifndef atomic_dec_return_relaxed
+#define  atomic_dec_return_relaxed     atomic_dec_return
+#define  atomic_dec_return_acquire     atomic_dec_return
+#define  atomic_dec_return_release     atomic_dec_return
+
+#else /* atomic_dec_return_relaxed */
+
+#ifndef atomic_dec_return_acquire
+#define  atomic_dec_return_acquire(...)                                        \
+       __atomic_op_acquire(atomic_dec_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic_dec_return_release
+#define  atomic_dec_return_release(...)                                        \
+       __atomic_op_release(atomic_dec_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic_dec_return
+#define  atomic_dec_return(...)                                                \
+       __atomic_op_fence(atomic_dec_return, __VA_ARGS__)
+#endif
+#endif /* atomic_dec_return_relaxed */
+
 /* atomic_xchg_relaxed */
 #ifndef atomic_xchg_relaxed
 #define  atomic_xchg_relaxed           atomic_xchg
 #endif
 #endif /* atomic64_add_return_relaxed */
 
+/* atomic64_inc_return_relaxed */
+#ifndef atomic64_inc_return_relaxed
+#define  atomic64_inc_return_relaxed   atomic64_inc_return
+#define  atomic64_inc_return_acquire   atomic64_inc_return
+#define  atomic64_inc_return_release   atomic64_inc_return
+
+#else /* atomic64_inc_return_relaxed */
+
+#ifndef atomic64_inc_return_acquire
+#define  atomic64_inc_return_acquire(...)                              \
+       __atomic_op_acquire(atomic64_inc_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_inc_return_release
+#define  atomic64_inc_return_release(...)                              \
+       __atomic_op_release(atomic64_inc_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_inc_return
+#define  atomic64_inc_return(...)                                      \
+       __atomic_op_fence(atomic64_inc_return, __VA_ARGS__)
+#endif
+#endif /* atomic64_inc_return_relaxed */
+
+
 /* atomic64_sub_return_relaxed */
 #ifndef atomic64_sub_return_relaxed
 #define  atomic64_sub_return_relaxed   atomic64_sub_return
 #endif
 #endif /* atomic64_sub_return_relaxed */
 
+/* atomic64_dec_return_relaxed */
+#ifndef atomic64_dec_return_relaxed
+#define  atomic64_dec_return_relaxed   atomic64_dec_return
+#define  atomic64_dec_return_acquire   atomic64_dec_return
+#define  atomic64_dec_return_release   atomic64_dec_return
+
+#else /* atomic64_dec_return_relaxed */
+
+#ifndef atomic64_dec_return_acquire
+#define  atomic64_dec_return_acquire(...)                              \
+       __atomic_op_acquire(atomic64_dec_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_dec_return_release
+#define  atomic64_dec_return_release(...)                              \
+       __atomic_op_release(atomic64_dec_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_dec_return
+#define  atomic64_dec_return(...)                                      \
+       __atomic_op_fence(atomic64_dec_return, __VA_ARGS__)
+#endif
+#endif /* atomic64_dec_return_relaxed */
+
 /* atomic64_xchg_relaxed */
 #ifndef atomic64_xchg_relaxed
 #define  atomic64_xchg_relaxed         atomic64_xchg
@@ -451,7 +548,6 @@ static inline int atomic_dec_if_positive(atomic_t *v)
 }
 #endif
 
-#include <asm-generic/atomic-long.h>
 #ifdef CONFIG_GENERIC_ATOMIC64
 #include <asm-generic/atomic64.h>
 #endif
@@ -463,4 +559,6 @@ static inline void atomic64_andnot(long long i, atomic64_t *v)
 }
 #endif
 
+#include <asm-generic/atomic-long.h>
+
 #endif /* _LINUX_ATOMIC_H */
index 2ff4a9961e1d5166741f98f80651a516d6ef8eb5..3feb1b2d75d87b83febb605a86720b06a8132d3f 100644 (file)
@@ -151,6 +151,8 @@ struct bcma_host_ops {
 #define BCMA_CORE_PCIE2                        0x83C   /* PCI Express Gen2 */
 #define BCMA_CORE_USB30_DEV            0x83D
 #define BCMA_CORE_ARM_CR4              0x83E
+#define BCMA_CORE_ARM_CA7              0x847
+#define BCMA_CORE_SYS_MEM              0x849
 #define BCMA_CORE_DEFAULT              0xFFF
 
 #define BCMA_MAX_NR_CORES              16
index 0a5cc7a1109b9b2c655020c6c849960506f1cbc6..c02e669945e9279bceb796eb9a1adb938ebf863b 100644 (file)
@@ -713,9 +713,9 @@ static inline bool blkcg_bio_issue_check(struct request_queue *q,
 
        if (!throtl) {
                blkg = blkg ?: q->root_blkg;
-               blkg_rwstat_add(&blkg->stat_bytes, bio->bi_flags,
+               blkg_rwstat_add(&blkg->stat_bytes, bio->bi_rw,
                                bio->bi_iter.bi_size);
-               blkg_rwstat_add(&blkg->stat_ios, bio->bi_flags, 1);
+               blkg_rwstat_add(&blkg->stat_ios, bio->bi_rw, 1);
        }
 
        rcu_read_unlock();
index f57d7fed9ec3f4554609a02538f44c5f950612b6..de464e6683b68f247492d69a02b463f6bfb4b3df 100644 (file)
@@ -10,7 +10,6 @@
 #include <uapi/linux/bpf.h>
 #include <linux/workqueue.h>
 #include <linux/file.h>
-#include <linux/perf_event.h>
 
 struct bpf_map;
 
@@ -37,6 +36,8 @@ struct bpf_map {
        u32 key_size;
        u32 value_size;
        u32 max_entries;
+       u32 pages;
+       struct user_struct *user;
        const struct bpf_map_ops *ops;
        struct work_struct work;
 };
@@ -101,6 +102,8 @@ enum bpf_access_type {
        BPF_WRITE = 2
 };
 
+struct bpf_prog;
+
 struct bpf_verifier_ops {
        /* return eBPF function prototype for verification */
        const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id);
@@ -112,7 +115,7 @@ struct bpf_verifier_ops {
 
        u32 (*convert_ctx_access)(enum bpf_access_type type, int dst_reg,
                                  int src_reg, int ctx_off,
-                                 struct bpf_insn *insn);
+                                 struct bpf_insn *insn, struct bpf_prog *prog);
 };
 
 struct bpf_prog_type_list {
@@ -121,14 +124,13 @@ struct bpf_prog_type_list {
        enum bpf_prog_type type;
 };
 
-struct bpf_prog;
-
 struct bpf_prog_aux {
        atomic_t refcnt;
        u32 used_map_cnt;
        const struct bpf_verifier_ops *ops;
        struct bpf_map **used_maps;
        struct bpf_prog *prog;
+       struct user_struct *user;
        union {
                struct work_struct work;
                struct rcu_head rcu;
@@ -165,9 +167,18 @@ struct bpf_prog *bpf_prog_get(u32 ufd);
 void bpf_prog_put(struct bpf_prog *prog);
 void bpf_prog_put_rcu(struct bpf_prog *prog);
 
-struct bpf_map *bpf_map_get(struct fd f);
+struct bpf_map *bpf_map_get(u32 ufd);
+struct bpf_map *__bpf_map_get(struct fd f);
 void bpf_map_put(struct bpf_map *map);
 
+extern int sysctl_unprivileged_bpf_disabled;
+
+int bpf_map_new_fd(struct bpf_map *map);
+int bpf_prog_new_fd(struct bpf_prog *prog);
+
+int bpf_obj_pin_user(u32 ufd, const char __user *pathname);
+int bpf_obj_get_user(const char __user *pathname);
+
 /* verify correctness of eBPF program */
 int bpf_check(struct bpf_prog **fp, union bpf_attr *attr);
 #else
@@ -190,7 +201,6 @@ extern const struct bpf_func_proto bpf_map_lookup_elem_proto;
 extern const struct bpf_func_proto bpf_map_update_elem_proto;
 extern const struct bpf_func_proto bpf_map_delete_elem_proto;
 
-extern const struct bpf_func_proto bpf_perf_event_read_proto;
 extern const struct bpf_func_proto bpf_get_prandom_u32_proto;
 extern const struct bpf_func_proto bpf_get_smp_processor_id_proto;
 extern const struct bpf_func_proto bpf_tail_call_proto;
@@ -201,4 +211,8 @@ extern const struct bpf_func_proto bpf_get_current_comm_proto;
 extern const struct bpf_func_proto bpf_skb_vlan_push_proto;
 extern const struct bpf_func_proto bpf_skb_vlan_pop_proto;
 
+/* Shared helpers among cBPF and eBPF. */
+void bpf_user_rnd_init_once(void);
+u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
+
 #endif /* _LINUX_BPF_H */
index 697ca7795bd9db6f2ac4fabdce77dc5bbc98f01b..59f4a73044199aa4fdafbd7e67578f3c000fd527 100644 (file)
@@ -30,6 +30,8 @@
 #define PHY_ID_BCM7439_2               0xae025080
 #define PHY_ID_BCM7445                 0x600d8510
 
+#define PHY_ID_BCM_CYGNUS              0xae025200
+
 #define PHY_BCM_OUI_MASK               0xfffffc00
 #define PHY_BCM_OUI_1                  0x00206000
 #define PHY_BCM_OUI_2                  0x0143bc00
 
 /* 01010: Auto Power-Down */
 #define BCM54XX_SHD_APD                        0x0a
+#define  BCM_APD_CLR_MASK              0xFE9F /* clear bits 5, 6 & 8 */
 #define  BCM54XX_SHD_APD_EN            0x0020
+#define  BCM_NO_ANEG_APD_EN            0x0060 /* bits 5 & 6 */
+#define  BCM_APD_SINGLELP_EN   0x0100 /* Bit 8 */
 
 #define BCM5482_SHD_LEDS1      0x0d    /* 01101: LED Selector 1 */
                                        /* LED3 / ~LINKSPD[2] selector */
 #define MII_BRCM_FET_SHDW_AUXSTAT2     0x1b    /* Auxiliary status 2 */
 #define MII_BRCM_FET_SHDW_AS2_APDE     0x0020  /* Auto power down enable */
 
-/*
- * Indirect register access functions for the 1000BASE-T/100BASE-TX/10BASE-T
- * 0x1c shadow registers.
- */
-static inline int bcm54xx_shadow_read(struct phy_device *phydev, u16 shadow)
-{
-       phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
-       return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
-}
-
-static inline int bcm54xx_shadow_write(struct phy_device *phydev, u16 shadow,
-                                      u16 val)
-{
-       return phy_write(phydev, MII_BCM54XX_SHD,
-                        MII_BCM54XX_SHD_WRITE |
-                        MII_BCM54XX_SHD_VAL(shadow) |
-                        MII_BCM54XX_SHD_DATA(val));
-}
-
 #define BRCM_CL45VEN_EEE_CONTROL       0x803d
 #define LPI_FEATURE_EN                 0x8000
 #define LPI_FEATURE_EN_DIG1000X                0x4000
 
+/* Core register definitions*/
+#define MII_BRCM_CORE_BASE1E   0x1E
+#define MII_BRCM_CORE_EXPB0    0xB0
+#define MII_BRCM_CORE_EXPB1    0xB1
+
 #endif /* _LINUX_BRCMPHY_H */
index c3a9c8fc60fa620395751b994e117dc1ac9f0712..735f9f8c4e43e5350f36bc4b39e9f8634778fd40 100644 (file)
 #define _CAN_DEV_H
 
 #include <linux/can.h>
-#include <linux/can/netlink.h>
 #include <linux/can/error.h>
 #include <linux/can/led.h>
+#include <linux/can/netlink.h>
+#include <linux/netdevice.h>
 
 /*
  * CAN mode
@@ -77,7 +78,7 @@ struct can_priv {
 #define get_canfd_dlc(i)       (min_t(__u8, (i), CANFD_MAX_DLC))
 
 /* Drop a given socketbuffer if it does not contain a valid CAN frame. */
-static inline int can_dropped_invalid_skb(struct net_device *dev,
+static inline bool can_dropped_invalid_skb(struct net_device *dev,
                                          struct sk_buff *skb)
 {
        const struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
@@ -93,12 +94,12 @@ static inline int can_dropped_invalid_skb(struct net_device *dev,
        } else
                goto inval_skb;
 
-       return 0;
+       return false;
 
 inval_skb:
        kfree_skb(skb);
        dev->stats.tx_dropped++;
-       return 1;
+       return true;
 }
 
 static inline bool can_is_canfd_skb(const struct sk_buff *skb)
index 146de4506d211285b82a5b3554154c4e6fc4a8a1..2746f7c2f87dd2f73d6312f254d367fed2b1957a 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/if.h>
 #include <linux/leds.h>
+#include <linux/netdevice.h>
 
 enum can_led_event {
        CAN_LED_EVENT_OPEN,
index 3d7810341b579795398e36cb15597feb344bb912..fe817432190c1cfb4c3108bbdfaf71b22916b8f8 100644 (file)
@@ -299,22 +299,6 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
        __u.__val;                                      \
 })
 
-/**
- * READ_ONCE_CTRL - Read a value heading a control dependency
- * @x: The value to be read, heading the control dependency
- *
- * Control dependencies are tricky.  See Documentation/memory-barriers.txt
- * for important information on how to use them.  Note that in many cases,
- * use of smp_load_acquire() will be much simpler.  Control dependencies
- * should be avoided except on the hottest of hotpaths.
- */
-#define READ_ONCE_CTRL(x) \
-({ \
-       typeof(x) __val = READ_ONCE(x); \
-       smp_read_barrier_depends(); /* Enforce control dependency. */ \
-       __val; \
-})
-
 #endif /* __KERNEL__ */
 
 #endif /* __ASSEMBLY__ */
index 23c30bdcca8631f80242d62d4b167d20e2814c75..d2ca8c38f9c45c0b279ef329c7abf130fcd0dc61 100644 (file)
@@ -228,7 +228,6 @@ extern struct bus_type cpu_subsys;
 extern void cpu_hotplug_begin(void);
 extern void cpu_hotplug_done(void);
 extern void get_online_cpus(void);
-extern bool try_get_online_cpus(void);
 extern void put_online_cpus(void);
 extern void cpu_hotplug_disable(void);
 extern void cpu_hotplug_enable(void);
@@ -246,7 +245,6 @@ int cpu_down(unsigned int cpu);
 static inline void cpu_hotplug_begin(void) {}
 static inline void cpu_hotplug_done(void) {}
 #define get_online_cpus()      do { } while (0)
-#define try_get_online_cpus()  true
 #define put_online_cpus()      do { } while (0)
 #define cpu_hotplug_disable()  do { } while (0)
 #define cpu_hotplug_enable()   do { } while (0)
index 221025423e6c993b70918e1186dabb7ff49a00e2..61d042bbbf607253033d9948b291cab2322814ba 100644 (file)
@@ -202,16 +202,16 @@ struct dccp_service_list {
 #define DCCP_SERVICE_INVALID_VALUE htonl((__u32)-1)
 #define DCCP_SERVICE_CODE_IS_ABSENT            0
 
-static inline int dccp_list_has_service(const struct dccp_service_list *sl,
+static inline bool dccp_list_has_service(const struct dccp_service_list *sl,
                                        const __be32 service)
 {
        if (likely(sl != NULL)) {
                u32 i = sl->dccpsl_nr;
                while (i--)
                        if (sl->dccpsl_list[i] == service)
-                               return 1;
+                               return true;
        }
-       return 0;
+       return false;
 }
 
 struct dccp_ackvec;
index da3b72e95db3cb0ba734ff736ad94aab6fd5b175..b3d87e5822f8aaba74dfe54a0730dffac499d990 100644 (file)
@@ -769,12 +769,10 @@ struct mem_ctl_info {
        /* the internal state of this controller instance */
        int op_state;
 
-#ifdef CONFIG_EDAC_DEBUG
        struct dentry *debugfs;
        u8 fake_inject_layer[EDAC_MAX_LAYERS];
        u32 fake_inject_ue;
        u16 fake_inject_count;
-#endif
 };
 
 /*
index 85ef051ac6fb43a64c38779c6bf754d2e492abcf..569b5a866bb1e6308bbc4c0a28a2da92d106d6b5 100644 (file)
@@ -99,6 +99,7 @@ typedef       struct {
 #define EFI_MEMORY_XP          ((u64)0x0000000000004000ULL)    /* execute-protect */
 #define EFI_MEMORY_MORE_RELIABLE \
                                ((u64)0x0000000000010000ULL)    /* higher reliability */
+#define EFI_MEMORY_RO          ((u64)0x0000000000020000ULL)    /* read-only */
 #define EFI_MEMORY_RUNTIME     ((u64)0x8000000000000000ULL)    /* range requires runtime mapping */
 #define EFI_MEMORY_DESCRIPTOR_VERSION  1
 
@@ -595,6 +596,9 @@ void efi_native_runtime_setup(void);
 #define DEVICE_TREE_GUID \
     EFI_GUID(  0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 )
 
+#define EFI_PROPERTIES_TABLE_GUID \
+    EFI_GUID(  0x880aaca3, 0x4adc, 0x4a04, 0x90, 0x79, 0xb7, 0x47, 0x34, 0x08, 0x25, 0xe5 )
+
 typedef struct {
        efi_guid_t guid;
        u64 table;
@@ -676,7 +680,7 @@ typedef struct {
 } efi_system_table_t;
 
 struct efi_memory_map {
-       void *phys_map;
+       phys_addr_t phys_map;
        void *map;
        void *map_end;
        int nr_map;
@@ -808,6 +812,15 @@ typedef struct _efi_file_io_interface {
 #define EFI_FILE_MODE_WRITE    0x0000000000000002
 #define EFI_FILE_MODE_CREATE   0x8000000000000000
 
+typedef struct {
+       u32 version;
+       u32 length;
+       u64 memory_protection_attribute;
+} efi_properties_table_t;
+
+#define EFI_PROPERTIES_TABLE_VERSION   0x00010000
+#define EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA        0x1
+
 #define EFI_INVALID_TABLE_ADDR         (~0UL)
 
 /*
@@ -830,6 +843,7 @@ extern struct efi {
        unsigned long runtime;          /* runtime table */
        unsigned long config_table;     /* config tables */
        unsigned long esrt;             /* ESRT table */
+       unsigned long properties_table; /* properties table */
        efi_get_time_t *get_time;
        efi_set_time_t *set_time;
        efi_get_wakeup_time_t *get_wakeup_time;
@@ -901,13 +915,19 @@ extern void efi_initialize_iomem_resources(struct resource *code_resource,
                struct resource *data_resource, struct resource *bss_resource);
 extern void efi_get_time(struct timespec *now);
 extern void efi_reserve_boot_services(void);
-extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
+extern int efi_get_fdt_params(struct efi_fdt_params *params);
 extern struct efi_memory_map memmap;
 extern struct kobject *efi_kobj;
 
 extern int efi_reboot_quirk_mode;
 extern bool efi_poweroff_required(void);
 
+#ifdef CONFIG_EFI_FAKE_MEMMAP
+extern void __init efi_fake_memmap(void);
+#else
+static inline void efi_fake_memmap(void) { }
+#endif
+
 /* Iterate through an efi_memory_map */
 #define for_each_efi_memory_desc(m, md)                                           \
        for ((md) = (m)->map;                                              \
@@ -959,6 +979,7 @@ extern int __init efi_setup_pcdp_console(char *);
 #define EFI_PARAVIRT           6       /* Access is via a paravirt interface */
 #define EFI_ARCH_1             7       /* First arch-specific bit */
 #define EFI_DBG                        8       /* Print additional debug info at runtime */
+#define EFI_NX_PE_DATA         9       /* Can runtime data regions be mapped non-executable? */
 
 #ifdef CONFIG_EFI
 /*
index 674e3e226465a0b21a283e068d77d665eca84052..5295535b60c60768ccb91034b3ba290dc1a57f1a 100644 (file)
@@ -26,6 +26,7 @@ struct fdtable {
        struct file __rcu **fd;      /* current fd array */
        unsigned long *close_on_exec;
        unsigned long *open_fds;
+       unsigned long *full_fds_bits;
        struct rcu_head rcu;
 };
 
@@ -59,6 +60,7 @@ struct files_struct {
        int next_fd;
        unsigned long close_on_exec_init[1];
        unsigned long open_fds_init[1];
+       unsigned long full_fds_bits_init[1];
        struct file __rcu * fd_array[NR_OPEN_DEFAULT];
 };
 
index fa2cab985e577681c801f8861c299ad938ec9ffc..4165e9ac9e36aa82735f40a790e25e0b7218c95b 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/printk.h>
 #include <linux/workqueue.h>
 #include <linux/sched.h>
+#include <net/sch_generic.h>
 
 #include <asm/cacheflush.h>
 
@@ -302,10 +303,6 @@ struct bpf_prog_aux;
        bpf_size;                                               \
 })
 
-/* Macro to invoke filter function. */
-#define SK_RUN_FILTER(filter, ctx) \
-       (*filter->prog->bpf_func)(ctx, filter->prog->insnsi)
-
 #ifdef CONFIG_COMPAT
 /* A struct sock_filter is architecture independent. */
 struct compat_sock_fprog {
@@ -326,8 +323,12 @@ struct bpf_binary_header {
 
 struct bpf_prog {
        u16                     pages;          /* Number of allocated pages */
-       bool                    jited;          /* Is our filter JIT'ed? */
-       bool                    gpl_compatible; /* Is our filter GPL compatible? */
+       kmemcheck_bitfield_begin(meta);
+       u16                     jited:1,        /* Is our filter JIT'ed? */
+                               gpl_compatible:1, /* Is filter GPL compatible? */
+                               cb_access:1,    /* Is control block accessed? */
+                               dst_needed:1;   /* Do we need dst entry? */
+       kmemcheck_bitfield_end(meta);
        u32                     len;            /* Number of filter blocks */
        enum bpf_prog_type      type;           /* Type of BPF program */
        struct bpf_prog_aux     *aux;           /* Auxiliary fields */
@@ -349,6 +350,39 @@ struct sk_filter {
 
 #define BPF_PROG_RUN(filter, ctx)  (*filter->bpf_func)(ctx, filter->insnsi)
 
+static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog,
+                                      struct sk_buff *skb)
+{
+       u8 *cb_data = qdisc_skb_cb(skb)->data;
+       u8 saved_cb[QDISC_CB_PRIV_LEN];
+       u32 res;
+
+       BUILD_BUG_ON(FIELD_SIZEOF(struct __sk_buff, cb) !=
+                    QDISC_CB_PRIV_LEN);
+
+       if (unlikely(prog->cb_access)) {
+               memcpy(saved_cb, cb_data, sizeof(saved_cb));
+               memset(cb_data, 0, sizeof(saved_cb));
+       }
+
+       res = BPF_PROG_RUN(prog, skb);
+
+       if (unlikely(prog->cb_access))
+               memcpy(cb_data, saved_cb, sizeof(saved_cb));
+
+       return res;
+}
+
+static inline u32 bpf_prog_run_clear_cb(const struct bpf_prog *prog,
+                                       struct sk_buff *skb)
+{
+       u8 *cb_data = qdisc_skb_cb(skb)->data;
+
+       if (unlikely(prog->cb_access))
+               memset(cb_data, 0, QDISC_CB_PRIV_LEN);
+       return BPF_PROG_RUN(prog, skb);
+}
+
 static inline unsigned int bpf_prog_size(unsigned int proglen)
 {
        return max(sizeof(struct bpf_prog),
@@ -408,7 +442,7 @@ typedef int (*bpf_aux_classic_check_t)(struct sock_filter *filter,
 
 int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog);
 int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
-                             bpf_aux_classic_check_t trans);
+                             bpf_aux_classic_check_t trans, bool save_orig);
 void bpf_prog_destroy(struct bpf_prog *fp);
 
 int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
index 0408545bce42403ecd50ecbeed8bffb78b336274..37ec668546ab21897393bcebc1ba548796db7994 100644 (file)
@@ -17,6 +17,7 @@ enum fwnode_type {
        FWNODE_OF,
        FWNODE_ACPI,
        FWNODE_PDATA,
+       FWNODE_IRQCHIP,
 };
 
 struct fwnode_handle {
index 09460d6d668208e3e4997b42f6dca05d0ef917a8..a4c61cbce777096a2d2171f8ec37a5f75f5f6bb8 100644 (file)
@@ -8,7 +8,7 @@
 extern void genl_lock(void);
 extern void genl_unlock(void);
 #ifdef CONFIG_LOCKDEP
-extern int lockdep_genl_is_held(void);
+extern bool lockdep_genl_is_held(void);
 #endif
 
 /* for synchronisation between af_netlink and genetlink */
index 14cac67c2012b6883d03445331337d139aba0307..fb0fde686cb1f80717c544ac18b380f6501afd39 100644 (file)
@@ -400,6 +400,7 @@ static inline struct gpio_desc *gpio_to_desc(unsigned gpio)
 {
        return ERR_PTR(-EINVAL);
 }
+
 static inline int desc_to_gpio(const struct gpio_desc *desc)
 {
        /* GPIO can never have been requested */
index 1aed31c5ffba33c3a122779b9feb151f24953168..d1baebf350d8109a30a6bbe3ff15dc25f3146e14 100644 (file)
@@ -206,6 +206,9 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
 
 #endif /* CONFIG_GPIOLIB_IRQCHIP */
 
+int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset);
+void gpiochip_generic_free(struct gpio_chip *chip, unsigned offset);
+
 #ifdef CONFIG_PINCTRL
 
 /**
index cfa906f28b7a277b480f5bf154bb8cf76e467ad5..452c0b0d2f3219dc0f11bec80878e5ea0b8e8803 100644 (file)
 #define IEEE80211_MAX_SN               IEEE80211_SN_MASK
 #define IEEE80211_SN_MODULO            (IEEE80211_MAX_SN + 1)
 
-static inline int ieee80211_sn_less(u16 sn1, u16 sn2)
+static inline bool ieee80211_sn_less(u16 sn1, u16 sn2)
 {
        return ((sn1 - sn2) & IEEE80211_SN_MASK) > (IEEE80211_SN_MODULO >> 1);
 }
@@ -250,7 +250,7 @@ struct ieee80211_qos_hdr {
  * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_has_tods(__le16 fc)
+static inline bool ieee80211_has_tods(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_TODS)) != 0;
 }
@@ -259,7 +259,7 @@ static inline int ieee80211_has_tods(__le16 fc)
  * ieee80211_has_fromds - check if IEEE80211_FCTL_FROMDS is set
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_has_fromds(__le16 fc)
+static inline bool ieee80211_has_fromds(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FROMDS)) != 0;
 }
@@ -268,7 +268,7 @@ static inline int ieee80211_has_fromds(__le16 fc)
  * ieee80211_has_a4 - check if IEEE80211_FCTL_TODS and IEEE80211_FCTL_FROMDS are set
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_has_a4(__le16 fc)
+static inline bool ieee80211_has_a4(__le16 fc)
 {
        __le16 tmp = cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS);
        return (fc & tmp) == tmp;
@@ -278,7 +278,7 @@ static inline int ieee80211_has_a4(__le16 fc)
  * ieee80211_has_morefrags - check if IEEE80211_FCTL_MOREFRAGS is set
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_has_morefrags(__le16 fc)
+static inline bool ieee80211_has_morefrags(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) != 0;
 }
@@ -287,7 +287,7 @@ static inline int ieee80211_has_morefrags(__le16 fc)
  * ieee80211_has_retry - check if IEEE80211_FCTL_RETRY is set
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_has_retry(__le16 fc)
+static inline bool ieee80211_has_retry(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_RETRY)) != 0;
 }
@@ -296,7 +296,7 @@ static inline int ieee80211_has_retry(__le16 fc)
  * ieee80211_has_pm - check if IEEE80211_FCTL_PM is set
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_has_pm(__le16 fc)
+static inline bool ieee80211_has_pm(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_PM)) != 0;
 }
@@ -305,7 +305,7 @@ static inline int ieee80211_has_pm(__le16 fc)
  * ieee80211_has_moredata - check if IEEE80211_FCTL_MOREDATA is set
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_has_moredata(__le16 fc)
+static inline bool ieee80211_has_moredata(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) != 0;
 }
@@ -314,7 +314,7 @@ static inline int ieee80211_has_moredata(__le16 fc)
  * ieee80211_has_protected - check if IEEE80211_FCTL_PROTECTED is set
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_has_protected(__le16 fc)
+static inline bool ieee80211_has_protected(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_PROTECTED)) != 0;
 }
@@ -323,7 +323,7 @@ static inline int ieee80211_has_protected(__le16 fc)
  * ieee80211_has_order - check if IEEE80211_FCTL_ORDER is set
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_has_order(__le16 fc)
+static inline bool ieee80211_has_order(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_ORDER)) != 0;
 }
@@ -332,7 +332,7 @@ static inline int ieee80211_has_order(__le16 fc)
  * ieee80211_is_mgmt - check if type is IEEE80211_FTYPE_MGMT
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_mgmt(__le16 fc)
+static inline bool ieee80211_is_mgmt(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT);
@@ -342,7 +342,7 @@ static inline int ieee80211_is_mgmt(__le16 fc)
  * ieee80211_is_ctl - check if type is IEEE80211_FTYPE_CTL
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_ctl(__le16 fc)
+static inline bool ieee80211_is_ctl(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_CTL);
@@ -352,7 +352,7 @@ static inline int ieee80211_is_ctl(__le16 fc)
  * ieee80211_is_data - check if type is IEEE80211_FTYPE_DATA
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_data(__le16 fc)
+static inline bool ieee80211_is_data(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_DATA);
@@ -362,7 +362,7 @@ static inline int ieee80211_is_data(__le16 fc)
  * ieee80211_is_data_qos - check if type is IEEE80211_FTYPE_DATA and IEEE80211_STYPE_QOS_DATA is set
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_data_qos(__le16 fc)
+static inline bool ieee80211_is_data_qos(__le16 fc)
 {
        /*
         * mask with QOS_DATA rather than IEEE80211_FCTL_STYPE as we just need
@@ -376,7 +376,7 @@ static inline int ieee80211_is_data_qos(__le16 fc)
  * ieee80211_is_data_present - check if type is IEEE80211_FTYPE_DATA and has data
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_data_present(__le16 fc)
+static inline bool ieee80211_is_data_present(__le16 fc)
 {
        /*
         * mask with 0x40 and test that that bit is clear to only return true
@@ -390,7 +390,7 @@ static inline int ieee80211_is_data_present(__le16 fc)
  * ieee80211_is_assoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_REQ
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_assoc_req(__le16 fc)
+static inline bool ieee80211_is_assoc_req(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ);
@@ -400,7 +400,7 @@ static inline int ieee80211_is_assoc_req(__le16 fc)
  * ieee80211_is_assoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_RESP
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_assoc_resp(__le16 fc)
+static inline bool ieee80211_is_assoc_resp(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_RESP);
@@ -410,7 +410,7 @@ static inline int ieee80211_is_assoc_resp(__le16 fc)
  * ieee80211_is_reassoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_REQ
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_reassoc_req(__le16 fc)
+static inline bool ieee80211_is_reassoc_req(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ);
@@ -420,7 +420,7 @@ static inline int ieee80211_is_reassoc_req(__le16 fc)
  * ieee80211_is_reassoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_RESP
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_reassoc_resp(__le16 fc)
+static inline bool ieee80211_is_reassoc_resp(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP);
@@ -430,7 +430,7 @@ static inline int ieee80211_is_reassoc_resp(__le16 fc)
  * ieee80211_is_probe_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_probe_req(__le16 fc)
+static inline bool ieee80211_is_probe_req(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ);
@@ -440,7 +440,7 @@ static inline int ieee80211_is_probe_req(__le16 fc)
  * ieee80211_is_probe_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_RESP
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_probe_resp(__le16 fc)
+static inline bool ieee80211_is_probe_resp(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP);
@@ -450,7 +450,7 @@ static inline int ieee80211_is_probe_resp(__le16 fc)
  * ieee80211_is_beacon - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_BEACON
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_beacon(__le16 fc)
+static inline bool ieee80211_is_beacon(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
@@ -460,7 +460,7 @@ static inline int ieee80211_is_beacon(__le16 fc)
  * ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_atim(__le16 fc)
+static inline bool ieee80211_is_atim(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ATIM);
@@ -470,7 +470,7 @@ static inline int ieee80211_is_atim(__le16 fc)
  * ieee80211_is_disassoc - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DISASSOC
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_disassoc(__le16 fc)
+static inline bool ieee80211_is_disassoc(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC);
@@ -480,7 +480,7 @@ static inline int ieee80211_is_disassoc(__le16 fc)
  * ieee80211_is_auth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_AUTH
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_auth(__le16 fc)
+static inline bool ieee80211_is_auth(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
@@ -490,7 +490,7 @@ static inline int ieee80211_is_auth(__le16 fc)
  * ieee80211_is_deauth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DEAUTH
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_deauth(__le16 fc)
+static inline bool ieee80211_is_deauth(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH);
@@ -500,7 +500,7 @@ static inline int ieee80211_is_deauth(__le16 fc)
  * ieee80211_is_action - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ACTION
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_action(__le16 fc)
+static inline bool ieee80211_is_action(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
@@ -510,7 +510,7 @@ static inline int ieee80211_is_action(__le16 fc)
  * ieee80211_is_back_req - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK_REQ
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_back_req(__le16 fc)
+static inline bool ieee80211_is_back_req(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ);
@@ -520,7 +520,7 @@ static inline int ieee80211_is_back_req(__le16 fc)
  * ieee80211_is_back - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_back(__le16 fc)
+static inline bool ieee80211_is_back(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK);
@@ -530,7 +530,7 @@ static inline int ieee80211_is_back(__le16 fc)
  * ieee80211_is_pspoll - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_PSPOLL
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_pspoll(__le16 fc)
+static inline bool ieee80211_is_pspoll(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
@@ -540,7 +540,7 @@ static inline int ieee80211_is_pspoll(__le16 fc)
  * ieee80211_is_rts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_RTS
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_rts(__le16 fc)
+static inline bool ieee80211_is_rts(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS);
@@ -550,7 +550,7 @@ static inline int ieee80211_is_rts(__le16 fc)
  * ieee80211_is_cts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CTS
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_cts(__le16 fc)
+static inline bool ieee80211_is_cts(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS);
@@ -560,7 +560,7 @@ static inline int ieee80211_is_cts(__le16 fc)
  * ieee80211_is_ack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_ACK
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_ack(__le16 fc)
+static inline bool ieee80211_is_ack(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK);
@@ -570,7 +570,7 @@ static inline int ieee80211_is_ack(__le16 fc)
  * ieee80211_is_cfend - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFEND
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_cfend(__le16 fc)
+static inline bool ieee80211_is_cfend(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFEND);
@@ -580,7 +580,7 @@ static inline int ieee80211_is_cfend(__le16 fc)
  * ieee80211_is_cfendack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFENDACK
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_cfendack(__le16 fc)
+static inline bool ieee80211_is_cfendack(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFENDACK);
@@ -590,7 +590,7 @@ static inline int ieee80211_is_cfendack(__le16 fc)
  * ieee80211_is_nullfunc - check if frame is a regular (non-QoS) nullfunc frame
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_nullfunc(__le16 fc)
+static inline bool ieee80211_is_nullfunc(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC);
@@ -600,7 +600,7 @@ static inline int ieee80211_is_nullfunc(__le16 fc)
  * ieee80211_is_qos_nullfunc - check if frame is a QoS nullfunc frame
  * @fc: frame control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_qos_nullfunc(__le16 fc)
+static inline bool ieee80211_is_qos_nullfunc(__le16 fc)
 {
        return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
               cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC);
@@ -624,7 +624,7 @@ static inline bool ieee80211_is_bufferable_mmpdu(__le16 fc)
  * ieee80211_is_first_frag - check if IEEE80211_SCTL_FRAG is not set
  * @seq_ctrl: frame sequence control bytes in little-endian byteorder
  */
-static inline int ieee80211_is_first_frag(__le16 seq_ctrl)
+static inline bool ieee80211_is_first_frag(__le16 seq_ctrl)
 {
        return (seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0;
 }
@@ -1379,6 +1379,7 @@ struct ieee80211_ht_operation {
 
 
 /* block-ack parameters */
+#define IEEE80211_ADDBA_PARAM_AMSDU_MASK 0x0001
 #define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
 #define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
 #define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFC0
@@ -1745,8 +1746,7 @@ enum ieee80211_eid {
        WLAN_EID_TIM = 5,
        WLAN_EID_IBSS_PARAMS = 6,
        WLAN_EID_COUNTRY = 7,
-       WLAN_EID_HP_PARAMS = 8,
-       WLAN_EID_HP_TABLE = 9,
+       /* 8, 9 reserved */
        WLAN_EID_REQUEST = 10,
        WLAN_EID_QBSS_LOAD = 11,
        WLAN_EID_EDCA_PARAM_SET = 12,
@@ -1932,6 +1932,8 @@ enum ieee80211_category {
        WLAN_CATEGORY_HT = 7,
        WLAN_CATEGORY_SA_QUERY = 8,
        WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9,
+       WLAN_CATEGORY_WNM = 10,
+       WLAN_CATEGORY_WNM_UNPROTECTED = 11,
        WLAN_CATEGORY_TDLS = 12,
        WLAN_CATEGORY_MESH_ACTION = 13,
        WLAN_CATEGORY_MULTIHOP_ACTION = 14,
@@ -2396,7 +2398,10 @@ static inline bool _ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr)
                category = ((u8 *) hdr) + 24;
                return *category != WLAN_CATEGORY_PUBLIC &&
                        *category != WLAN_CATEGORY_HT &&
+                       *category != WLAN_CATEGORY_WNM_UNPROTECTED &&
                        *category != WLAN_CATEGORY_SELF_PROTECTED &&
+                       *category != WLAN_CATEGORY_UNPROT_DMG &&
+                       *category != WLAN_CATEGORY_VHT &&
                        *category != WLAN_CATEGORY_VENDOR_SPECIFIC;
        }
 
index 1dc1f4ed40010bb8c1229a6f6b8cdfcf0bca9a9e..d3e415674dac4dfa34145c3f1d3a49685c70413c 100644 (file)
 
 #include <linux/types.h>
 #include <linux/random.h>
-#include <asm/byteorder.h>
 
 #define IEEE802154_MTU                 127
 #define IEEE802154_ACK_PSDU_LEN                5
 #define IEEE802154_MIN_PSDU_LEN                9
 #define IEEE802154_FCS_LEN             2
+#define IEEE802154_MAX_AUTH_TAG_LEN    16
+
+/*  General MAC frame format:
+ *  2 bytes: Frame Control
+ *  1 byte:  Sequence Number
+ * 20 bytes: Addressing fields
+ * 14 bytes: Auxiliary Security Header
+ */
+#define IEEE802154_MAX_HEADER_LEN      (2 + 1 + 20 + 14)
+#define IEEE802154_MIN_HEADER_LEN      (IEEE802154_ACK_PSDU_LEN - \
+                                        IEEE802154_FCS_LEN)
 
 #define IEEE802154_PAN_ID_BROADCAST    0xffff
 #define IEEE802154_ADDR_SHORT_BROADCAST        0xffff
@@ -205,6 +215,41 @@ enum {
        IEEE802154_SCAN_IN_PROGRESS = 0xfc,
 };
 
+/* frame control handling */
+#define IEEE802154_FCTL_FTYPE          0x0003
+#define IEEE802154_FCTL_ACKREQ         0x0020
+#define IEEE802154_FCTL_INTRA_PAN      0x0040
+
+#define IEEE802154_FTYPE_DATA          0x0001
+
+/*
+ * ieee802154_is_data - check if type is IEEE802154_FTYPE_DATA
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline int ieee802154_is_data(__le16 fc)
+{
+       return (fc & cpu_to_le16(IEEE802154_FCTL_FTYPE)) ==
+               cpu_to_le16(IEEE802154_FTYPE_DATA);
+}
+
+/**
+ * ieee802154_is_ackreq - check if acknowledgment request bit is set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee802154_is_ackreq(__le16 fc)
+{
+       return fc & cpu_to_le16(IEEE802154_FCTL_ACKREQ);
+}
+
+/**
+ * ieee802154_is_intra_pan - check if intra pan id communication
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee802154_is_intra_pan(__le16 fc)
+{
+       return fc & cpu_to_le16(IEEE802154_FCTL_INTRA_PAN);
+}
+
 /**
  * ieee802154_is_valid_psdu_len - check if psdu len is valid
  * available lengths:
index dad8b00beed27220856985c984e620d88cafd736..a338a688ee4a4237eb938ff71627bbd5aafa442a 100644 (file)
@@ -46,6 +46,12 @@ struct br_ip_list {
 #define BR_LEARNING_SYNC       BIT(9)
 #define BR_PROXYARP_WIFI       BIT(10)
 
+/* values as per ieee8021QBridgeFdbAgingTime */
+#define BR_MIN_AGEING_TIME     (10 * HZ)
+#define BR_MAX_AGEING_TIME     (1000000 * HZ)
+
+#define BR_DEFAULT_AGEING_TIME (300 * HZ)
+
 extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *));
 
 typedef int br_should_route_hook_t(struct sk_buff *skb);
index ae5d0d22955d8fdedba5b03d981dbcdd7585ee03..f923d15b432c75b831a57eb9d2f1f532684eb689 100644 (file)
@@ -24,5 +24,6 @@ struct ifla_vf_info {
        __u32 min_tx_rate;
        __u32 max_tx_rate;
        __u32 rss_query_en;
+       __u32 trusted;
 };
 #endif /* _LINUX_IF_LINK_H */
index 908429216d9f6e1264484a34ea35e05a0ad43cc6..9c9de11549a73047318ffc688e621bbea09f71c9 100644 (file)
@@ -110,7 +110,7 @@ struct ip_mc_list {
 #define IGMPV3_QQIC(value) IGMPV3_EXP(0x80, 4, 3, value)
 #define IGMPV3_MRC(value) IGMPV3_EXP(0x80, 4, 3, value)
 
-extern int ip_check_mc_rcu(struct in_device *dev, __be32 mc_addr, __be32 src_addr, u16 proto);
+extern int ip_check_mc_rcu(struct in_device *dev, __be32 mc_addr, __be32 src_addr, u8 proto);
 extern int igmp_rcv(struct sk_buff *);
 extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr);
 extern int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr);
index a4328cea376abfc9b11cea8c68403d6a96375f9c..ee971f335a8b659f04e5a3048ca70e5bc361ee5f 100644 (file)
@@ -171,7 +171,7 @@ __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, __be32 dst,
                         __be32 local, int scope);
 struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
                                    __be32 mask);
-static __inline__ int inet_ifa_match(__be32 addr, struct in_ifaddr *ifa)
+static __inline__ bool inet_ifa_match(__be32 addr, struct in_ifaddr *ifa)
 {
        return !((addr^ifa->ifa_address)&ifa->ifa_mask);
 }
@@ -180,15 +180,15 @@ static __inline__ int inet_ifa_match(__be32 addr, struct in_ifaddr *ifa)
  *     Check if a mask is acceptable.
  */
  
-static __inline__ int bad_mask(__be32 mask, __be32 addr)
+static __inline__ bool bad_mask(__be32 mask, __be32 addr)
 {
        __u32 hmask;
        if (addr & (mask = ~mask))
-               return 1;
+               return true;
        hmask = ntohl(mask);
        if (hmask & (hmask+1))
-               return 1;
-       return 0;
+               return true;
+       return false;
 }
 
 #define for_primary_ifa(in_dev)        { struct in_ifaddr *ifa; \
index e38681f4912d02f9c571dd7d232a4b4bb4911ea4..810a34f604247065d78c2820ed34b0bf8d3aabf6 100644 (file)
@@ -59,7 +59,8 @@ extern struct fs_struct init_fs;
        .rlim           = INIT_RLIMITS,                                 \
        .cputimer       = {                                             \
                .cputime_atomic = INIT_CPUTIME_ATOMIC,                  \
-               .running        = 0,                                    \
+               .running        = false,                                \
+               .checking_timer = false,                                \
        },                                                              \
        INIT_PREV_CPUTIME(sig)                                          \
        .cred_guard_mutex =                                             \
index be7e75c945e97b07d5f248ded5c1e0d9240756ad..ad16809c85961e16ebc804280e4a78d66cf77634 100644 (file)
@@ -102,6 +102,7 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
  * @flags:     flags (see IRQF_* above)
  * @thread_fn: interrupt handler function for threaded interrupts
  * @thread:    thread pointer for threaded interrupts
+ * @secondary: pointer to secondary irqaction (force threading)
  * @thread_flags:      flags related to @thread
  * @thread_mask:       bitmask for keeping track of @thread activity
  * @dir:       pointer to the proc/irq/NN/name entry
@@ -113,6 +114,7 @@ struct irqaction {
        struct irqaction        *next;
        irq_handler_t           thread_fn;
        struct task_struct      *thread;
+       struct irqaction        *secondary;
        unsigned int            irq;
        unsigned int            flags;
        unsigned long           thread_flags;
index bbced83b32ee1c45da17450f34fa16a26ec3092a..376a27c9cc6aa4901a1870c13749ce762f4fe69f 100644 (file)
@@ -7,6 +7,7 @@
 
 #define IOMMU_POOL_HASHBITS     4
 #define IOMMU_NR_POOLS          (1 << IOMMU_POOL_HASHBITS)
+#define IOMMU_ERROR_CODE       (~(unsigned long) 0)
 
 struct iommu_pool {
        unsigned long   start;
index f1f32af6d9b96c8d21f55fe6e875673c15e0eab2..0ef2a97ccdb50bc83baaf727f37d2fa034eea036 100644 (file)
@@ -264,9 +264,9 @@ struct tcp6_timewait_sock {
 };
 
 #if IS_ENABLED(CONFIG_IPV6)
-static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk)
+static inline struct ipv6_pinfo *inet6_sk(const struct sock *__sk)
 {
-       return inet_sk(__sk)->pinet6;
+       return sk_fullsock(__sk) ? inet_sk(__sk)->pinet6 : NULL;
 }
 
 static inline struct raw6_sock *raw6_sk(const struct sock *sk)
index 11bf09288ddb08ab277b6f213696c1216348155c..3c1c96786248cb02d2d9792cce455892323ef275 100644 (file)
@@ -67,11 +67,12 @@ enum irqchip_irq_state;
  *                               request/setup_irq()
  * IRQ_NO_BALANCING            - Interrupt cannot be balanced (affinity set)
  * IRQ_MOVE_PCNTXT             - Interrupt can be migrated from process context
- * IRQ_NESTED_TRHEAD           - Interrupt nests into another thread
+ * IRQ_NESTED_THREAD           - Interrupt nests into another thread
  * IRQ_PER_CPU_DEVID           - Dev_id is a per-cpu variable
  * IRQ_IS_POLLED               - Always polled by another interrupt. Exclude
  *                               it from the spurious interrupt detection
  *                               mechanism and from core side polling.
+ * IRQ_DISABLE_UNLAZY          - Disable lazy irq disable
  */
 enum {
        IRQ_TYPE_NONE           = 0x00000000,
@@ -97,13 +98,14 @@ enum {
        IRQ_NOTHREAD            = (1 << 16),
        IRQ_PER_CPU_DEVID       = (1 << 17),
        IRQ_IS_POLLED           = (1 << 18),
+       IRQ_DISABLE_UNLAZY      = (1 << 19),
 };
 
 #define IRQF_MODIFY_MASK       \
        (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
         IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \
         IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID | \
-        IRQ_IS_POLLED)
+        IRQ_IS_POLLED | IRQ_DISABLE_UNLAZY)
 
 #define IRQ_NO_BALANCING_MASK  (IRQ_PER_CPU | IRQ_NO_BALANCING)
 
@@ -297,21 +299,6 @@ static inline void irqd_clr_forwarded_to_vcpu(struct irq_data *d)
        __irqd_to_state(d) &= ~IRQD_FORWARDED_TO_VCPU;
 }
 
-/*
- * Functions for chained handlers which can be enabled/disabled by the
- * standard disable_irq/enable_irq calls. Must be called with
- * irq_desc->lock held.
- */
-static inline void irqd_set_chained_irq_inprogress(struct irq_data *d)
-{
-       __irqd_to_state(d) |= IRQD_IRQ_INPROGRESS;
-}
-
-static inline void irqd_clr_chained_irq_inprogress(struct irq_data *d)
-{
-       __irqd_to_state(d) &= ~IRQD_IRQ_INPROGRESS;
-}
-
 static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
 {
        return d->hwirq;
@@ -452,6 +439,8 @@ extern int irq_set_affinity_locked(struct irq_data *data,
                                   const struct cpumask *cpumask, bool force);
 extern int irq_set_vcpu_affinity(unsigned int irq, void *vcpu_info);
 
+extern void irq_migrate_all_off_this_cpu(void);
+
 #if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_PENDING_IRQ)
 void irq_move_irq(struct irq_data *data);
 void irq_move_masked_irq(struct irq_data *data);
index 9eeeb9589acfc35baed79a89d7d3f0c1be134cf3..c9ae0c6ec050569fc592b6f07b3ce1fa052bcc59 100644 (file)
@@ -18,8 +18,6 @@
 #ifndef __LINUX_IRQCHIP_ARM_GIC_V3_H
 #define __LINUX_IRQCHIP_ARM_GIC_V3_H
 
-#include <asm/sysreg.h>
-
 /*
  * Distributor registers. We assume we're running non-secure, with ARE
  * being set. Secure-only and non-ARE registers are not described.
 #define GITS_BASER_PAGE_SIZE_16K       (1UL << GITS_BASER_PAGE_SIZE_SHIFT)
 #define GITS_BASER_PAGE_SIZE_64K       (2UL << GITS_BASER_PAGE_SIZE_SHIFT)
 #define GITS_BASER_PAGE_SIZE_MASK      (3UL << GITS_BASER_PAGE_SIZE_SHIFT)
+#define GITS_BASER_PAGES_MAX           256
 
 #define GITS_BASER_TYPE_NONE           0
 #define GITS_BASER_TYPE_DEVICE         1
 /*
  * Hypervisor interface registers (SRE only)
  */
-#define ICH_LR_VIRTUAL_ID_MASK         ((1UL << 32) - 1)
-
-#define ICH_LR_EOI                     (1UL << 41)
-#define ICH_LR_GROUP                   (1UL << 60)
-#define ICH_LR_HW                      (1UL << 61)
-#define ICH_LR_STATE                   (3UL << 62)
-#define ICH_LR_PENDING_BIT             (1UL << 62)
-#define ICH_LR_ACTIVE_BIT              (1UL << 63)
+#define ICH_LR_VIRTUAL_ID_MASK         ((1ULL << 32) - 1)
+
+#define ICH_LR_EOI                     (1ULL << 41)
+#define ICH_LR_GROUP                   (1ULL << 60)
+#define ICH_LR_HW                      (1ULL << 61)
+#define ICH_LR_STATE                   (3ULL << 62)
+#define ICH_LR_PENDING_BIT             (1ULL << 62)
+#define ICH_LR_ACTIVE_BIT              (1ULL << 63)
 #define ICH_LR_PHYS_ID_SHIFT           32
-#define ICH_LR_PHYS_ID_MASK            (0x3ffUL << ICH_LR_PHYS_ID_SHIFT)
+#define ICH_LR_PHYS_ID_MASK            (0x3ffULL << ICH_LR_PHYS_ID_SHIFT)
 
 #define ICH_MISR_EOI                   (1 << 0)
 #define ICH_MISR_U                     (1 << 1)
 #define ICH_VMCR_PMR_SHIFT             24
 #define ICH_VMCR_PMR_MASK              (0xffUL << ICH_VMCR_PMR_SHIFT)
 
-#define ICC_EOIR1_EL1                  sys_reg(3, 0, 12, 12, 1)
-#define ICC_DIR_EL1                    sys_reg(3, 0, 12, 11, 1)
-#define ICC_IAR1_EL1                   sys_reg(3, 0, 12, 12, 0)
-#define ICC_SGI1R_EL1                  sys_reg(3, 0, 12, 11, 5)
-#define ICC_PMR_EL1                    sys_reg(3, 0, 4, 6, 0)
-#define ICC_CTLR_EL1                   sys_reg(3, 0, 12, 12, 4)
-#define ICC_SRE_EL1                    sys_reg(3, 0, 12, 12, 5)
-#define ICC_GRPEN1_EL1                 sys_reg(3, 0, 12, 12, 7)
-
 #define ICC_IAR1_EL1_SPURIOUS          0x3ff
 
-#define ICC_SRE_EL2                    sys_reg(3, 4, 12, 9, 5)
-
 #define ICC_SRE_EL2_SRE                        (1 << 0)
 #define ICC_SRE_EL2_ENABLE             (1 << 3)
 
 #define ICC_SGI1R_AFFINITY_3_SHIFT     48
 #define ICC_SGI1R_AFFINITY_3_MASK      (0xffULL << ICC_SGI1R_AFFINITY_1_SHIFT)
 
-/*
- * System register definitions
- */
-#define ICH_VSEIR_EL2                  sys_reg(3, 4, 12, 9, 4)
-#define ICH_HCR_EL2                    sys_reg(3, 4, 12, 11, 0)
-#define ICH_VTR_EL2                    sys_reg(3, 4, 12, 11, 1)
-#define ICH_MISR_EL2                   sys_reg(3, 4, 12, 11, 2)
-#define ICH_EISR_EL2                   sys_reg(3, 4, 12, 11, 3)
-#define ICH_ELSR_EL2                   sys_reg(3, 4, 12, 11, 5)
-#define ICH_VMCR_EL2                   sys_reg(3, 4, 12, 11, 7)
-
-#define __LR0_EL2(x)                   sys_reg(3, 4, 12, 12, x)
-#define __LR8_EL2(x)                   sys_reg(3, 4, 12, 13, x)
-
-#define ICH_LR0_EL2                    __LR0_EL2(0)
-#define ICH_LR1_EL2                    __LR0_EL2(1)
-#define ICH_LR2_EL2                    __LR0_EL2(2)
-#define ICH_LR3_EL2                    __LR0_EL2(3)
-#define ICH_LR4_EL2                    __LR0_EL2(4)
-#define ICH_LR5_EL2                    __LR0_EL2(5)
-#define ICH_LR6_EL2                    __LR0_EL2(6)
-#define ICH_LR7_EL2                    __LR0_EL2(7)
-#define ICH_LR8_EL2                    __LR8_EL2(0)
-#define ICH_LR9_EL2                    __LR8_EL2(1)
-#define ICH_LR10_EL2                   __LR8_EL2(2)
-#define ICH_LR11_EL2                   __LR8_EL2(3)
-#define ICH_LR12_EL2                   __LR8_EL2(4)
-#define ICH_LR13_EL2                   __LR8_EL2(5)
-#define ICH_LR14_EL2                   __LR8_EL2(6)
-#define ICH_LR15_EL2                   __LR8_EL2(7)
-
-#define __AP0Rx_EL2(x)                 sys_reg(3, 4, 12, 8, x)
-#define ICH_AP0R0_EL2                  __AP0Rx_EL2(0)
-#define ICH_AP0R1_EL2                  __AP0Rx_EL2(1)
-#define ICH_AP0R2_EL2                  __AP0Rx_EL2(2)
-#define ICH_AP0R3_EL2                  __AP0Rx_EL2(3)
-
-#define __AP1Rx_EL2(x)                 sys_reg(3, 4, 12, 9, x)
-#define ICH_AP1R0_EL2                  __AP1Rx_EL2(0)
-#define ICH_AP1R1_EL2                  __AP1Rx_EL2(1)
-#define ICH_AP1R2_EL2                  __AP1Rx_EL2(2)
-#define ICH_AP1R3_EL2                  __AP1Rx_EL2(3)
+#include <asm/arch_gicv3.h>
 
 #ifndef __ASSEMBLY__
 
-#include <linux/stringify.h>
-#include <asm/msi.h>
-
 /*
  * We need a value to serve as a irq-type for LPIs. Choose one that will
  * hopefully pique the interest of the reviewer.
@@ -385,23 +329,26 @@ struct rdists {
        u64                     flags;
 };
 
-static inline void gic_write_eoir(u64 irq)
-{
-       asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" (irq));
-       isb();
-}
-
-static inline void gic_write_dir(u64 irq)
-{
-       asm volatile("msr_s " __stringify(ICC_DIR_EL1) ", %0" : : "r" (irq));
-       isb();
-}
-
 struct irq_domain;
 int its_cpu_init(void);
 int its_init(struct device_node *node, struct rdists *rdists,
             struct irq_domain *domain);
 
+static inline bool gic_enable_sre(void)
+{
+       u32 val;
+
+       val = gic_read_sre();
+       if (val & ICC_SRE_EL1_SRE)
+               return true;
+
+       val |= ICC_SRE_EL1_SRE;
+       gic_write_sre(val);
+       val = gic_read_sre();
+
+       return !!(val & ICC_SRE_EL1_SRE);
+}
+
 #endif
 
 #endif
index b8901dfd9e9584ba06de5f585f14c6edc9da8fb5..bae69e5d693c3e60cea1a209fc06d2d33fdfb960 100644 (file)
 
 struct device_node;
 
-void gic_init_bases(unsigned int, int, void __iomem *, void __iomem *,
-                   u32 offset, struct device_node *);
 void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
 int gic_cpu_if_down(unsigned int gic_nr);
 
-static inline void gic_init(unsigned int nr, int start,
-                           void __iomem *dist , void __iomem *cpu)
-{
-       gic_init_bases(nr, start, dist, cpu, 0, NULL);
-}
+void gic_init(unsigned int nr, int start,
+             void __iomem *dist , void __iomem *cpu);
 
 int gicv2m_of_init(struct device_node *node, struct irq_domain *parent);
 
index f644fdb06dd691ba2d218384b1172791100f9aaf..d5e5c5bef28c45e29bff88b175d581c0595b73f9 100644 (file)
@@ -5,9 +5,10 @@
  * helpful for interrupt controllers to implement mapping between hardware
  * irq numbers and the Linux irq number space.
  *
- * irq_domains also have a hook for translating device tree interrupt
- * representation into a hardware irq number that can be mapped back to a
- * Linux irq number without any extra platform support code.
+ * irq_domains also have hooks for translating device tree or other
+ * firmware interrupt representations into a hardware irq number that
+ * can be mapped back to a Linux irq number without any extra platform
+ * support code.
  *
  * Interrupt controller "domain" data structure. This could be defined as a
  * irq domain controller. That is, it handles the mapping between hardware
  * model). It's the domain callbacks that are responsible for setting the
  * irq_chip on a given irq_desc after it's been mapped.
  *
- * The host code and data structures are agnostic to whether or not
- * we use an open firmware device-tree. We do have references to struct
- * device_node in two places: in irq_find_host() to find the host matching
- * a given interrupt controller node, and of course as an argument to its
- * counterpart domain->ops->match() callback. However, those are treated as
- * generic pointers by the core and the fact that it's actually a device-node
- * pointer is purely a convention between callers and implementation. This
- * code could thus be used on other architectures by replacing those two
- * by some sort of arch-specific void * "token" used to identify interrupt
- * controllers.
+ * The host code and data structures use a fwnode_handle pointer to
+ * identify the domain. In some cases, and in order to preserve source
+ * code compatibility, this fwnode pointer is "upgraded" to a DT
+ * device_node. For those firmware infrastructures that do not provide
+ * a unique identifier for an interrupt controller, the irq_domain
+ * code offers a fwnode allocator.
  */
 
 #ifndef _LINUX_IRQDOMAIN_H
@@ -34,6 +31,7 @@
 
 #include <linux/types.h>
 #include <linux/irqhandler.h>
+#include <linux/of.h>
 #include <linux/radix-tree.h>
 
 struct device_node;
@@ -45,6 +43,24 @@ struct irq_data;
 /* Number of irqs reserved for a legacy isa controller */
 #define NUM_ISA_INTERRUPTS     16
 
+#define IRQ_DOMAIN_IRQ_SPEC_PARAMS 16
+
+/**
+ * struct irq_fwspec - generic IRQ specifier structure
+ *
+ * @fwnode:            Pointer to a firmware-specific descriptor
+ * @param_count:       Number of device-specific parameters
+ * @param:             Device-specific parameters
+ *
+ * This structure, directly modeled after of_phandle_args, is used to
+ * pass a device-specific description of an interrupt.
+ */
+struct irq_fwspec {
+       struct fwnode_handle *fwnode;
+       int param_count;
+       u32 param[IRQ_DOMAIN_IRQ_SPEC_PARAMS];
+};
+
 /*
  * Should several domains have the same device node, but serve
  * different purposes (for example one domain is for PCI/MSI, and the
@@ -91,6 +107,8 @@ struct irq_domain_ops {
                     unsigned int nr_irqs);
        void (*activate)(struct irq_domain *d, struct irq_data *irq_data);
        void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);
+       int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec,
+                        unsigned long *out_hwirq, unsigned int *out_type);
 #endif
 };
 
@@ -130,7 +148,7 @@ struct irq_domain {
        unsigned int flags;
 
        /* Optional data */
-       struct device_node *of_node;
+       struct fwnode_handle *fwnode;
        enum irq_domain_bus_token bus_token;
        struct irq_domain_chip_generic *gc;
 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
@@ -163,11 +181,13 @@ enum {
 
 static inline struct device_node *irq_domain_get_of_node(struct irq_domain *d)
 {
-       return d->of_node;
+       return to_of_node(d->fwnode);
 }
 
 #ifdef CONFIG_IRQ_DOMAIN
-struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
+struct fwnode_handle *irq_domain_alloc_fwnode(void *data);
+void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
+struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
                                    irq_hw_number_t hwirq_max, int direct_max,
                                    const struct irq_domain_ops *ops,
                                    void *host_data);
@@ -182,10 +202,21 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
                                         irq_hw_number_t first_hwirq,
                                         const struct irq_domain_ops *ops,
                                         void *host_data);
-extern struct irq_domain *irq_find_matching_host(struct device_node *node,
-                                                enum irq_domain_bus_token bus_token);
+extern struct irq_domain *irq_find_matching_fwnode(struct fwnode_handle *fwnode,
+                                                  enum irq_domain_bus_token bus_token);
 extern void irq_set_default_host(struct irq_domain *host);
 
+static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node)
+{
+       return node ? &node->fwnode : NULL;
+}
+
+static inline struct irq_domain *irq_find_matching_host(struct device_node *node,
+                                                       enum irq_domain_bus_token bus_token)
+{
+       return irq_find_matching_fwnode(of_node_to_fwnode(node), bus_token);
+}
+
 static inline struct irq_domain *irq_find_host(struct device_node *node)
 {
        return irq_find_matching_host(node, DOMAIN_BUS_ANY);
@@ -203,14 +234,14 @@ static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_no
                                         const struct irq_domain_ops *ops,
                                         void *host_data)
 {
-       return __irq_domain_add(of_node, size, size, 0, ops, host_data);
+       return __irq_domain_add(of_node_to_fwnode(of_node), size, size, 0, ops, host_data);
 }
 static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
                                         unsigned int max_irq,
                                         const struct irq_domain_ops *ops,
                                         void *host_data)
 {
-       return __irq_domain_add(of_node, 0, max_irq, max_irq, ops, host_data);
+       return __irq_domain_add(of_node_to_fwnode(of_node), 0, max_irq, max_irq, ops, host_data);
 }
 static inline struct irq_domain *irq_domain_add_legacy_isa(
                                struct device_node *of_node,
@@ -224,7 +255,22 @@ static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node
                                         const struct irq_domain_ops *ops,
                                         void *host_data)
 {
-       return __irq_domain_add(of_node, 0, ~0, 0, ops, host_data);
+       return __irq_domain_add(of_node_to_fwnode(of_node), 0, ~0, 0, ops, host_data);
+}
+
+static inline struct irq_domain *irq_domain_create_linear(struct fwnode_handle *fwnode,
+                                        unsigned int size,
+                                        const struct irq_domain_ops *ops,
+                                        void *host_data)
+{
+       return __irq_domain_add(fwnode, size, size, 0, ops, host_data);
+}
+
+static inline struct irq_domain *irq_domain_create_tree(struct fwnode_handle *fwnode,
+                                        const struct irq_domain_ops *ops,
+                                        void *host_data)
+{
+       return __irq_domain_add(fwnode, 0, ~0, 0, ops, host_data);
 }
 
 extern void irq_domain_remove(struct irq_domain *host);
@@ -239,6 +285,7 @@ extern void irq_domain_disassociate(struct irq_domain *domain,
 
 extern unsigned int irq_create_mapping(struct irq_domain *host,
                                       irq_hw_number_t hwirq);
+extern unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec);
 extern void irq_dispose_mapping(unsigned int virq);
 
 /**
@@ -290,10 +337,23 @@ extern void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
                                void *chip_data, irq_flow_handler_t handler,
                                void *handler_data, const char *handler_name);
 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
-extern struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+extern struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
                        unsigned int flags, unsigned int size,
-                       struct device_node *node,
+                       struct fwnode_handle *fwnode,
                        const struct irq_domain_ops *ops, void *host_data);
+
+static inline struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+                                           unsigned int flags,
+                                           unsigned int size,
+                                           struct device_node *node,
+                                           const struct irq_domain_ops *ops,
+                                           void *host_data)
+{
+       return irq_domain_create_hierarchy(parent, flags, size,
+                                          of_node_to_fwnode(node),
+                                          ops, host_data);
+}
+
 extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
                                   unsigned int nr_irqs, int node, void *arg,
                                   bool realloc);
index e374e369fb2f4c9eb5ace48679376550d5ee633c..eb1bdcf95f2e0c931a3a5cb599ab06547ff34f3d 100644 (file)
@@ -3,7 +3,7 @@
 
 /**
  * enum irqreturn
- * @IRQ_NONE           interrupt was not from this device
+ * @IRQ_NONE           interrupt was not from this device or was not handled
  * @IRQ_HANDLED                interrupt was handled by this device
  * @IRQ_WAKE_THREAD    handler requests to wake the handler thread
  */
index b122eeafb5dc17b8a8b1a1852dc1c420ecf0f8d2..fa359c79c825e666789ec1ce65392b6ff184d93c 100644 (file)
@@ -283,6 +283,13 @@ static inline void led_trigger_register_simple(const char *name,
 static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {}
 static inline void led_trigger_event(struct led_trigger *trigger,
                                enum led_brightness event) {}
+static inline void led_trigger_blink(struct led_trigger *trigger,
+                                     unsigned long *delay_on,
+                                     unsigned long *delay_off) {}
+static inline void led_trigger_blink_oneshot(struct led_trigger *trigger,
+                                     unsigned long *delay_on,
+                                     unsigned long *delay_off,
+                                     int invert) {}
 static inline void led_trigger_set_default(struct led_classdev *led_cdev) {}
 static inline void led_trigger_set(struct led_classdev *led_cdev,
                                struct led_trigger *trigger) {}
index 3e3e64a6100241b328a82f39f77ab403583637c6..993395a2e55c5483c890b40e216193d95c85bd64 100644 (file)
@@ -87,7 +87,7 @@ static inline void list_add_tail(struct list_head *new, struct list_head *head)
 static inline void __list_del(struct list_head * prev, struct list_head * next)
 {
        next->prev = prev;
-       prev->next = next;
+       WRITE_ONCE(prev->next, next);
 }
 
 /**
@@ -615,7 +615,8 @@ static inline void __hlist_del(struct hlist_node *n)
 {
        struct hlist_node *next = n->next;
        struct hlist_node **pprev = n->pprev;
-       *pprev = next;
+
+       WRITE_ONCE(*pprev, next);
        if (next)
                next->pprev = pprev;
 }
index 2eb88556c5c5b0ee0cb0fc664e9d13dc852fea6e..8132214e8efd2930ff752fcc4c37da7d23b9643f 100644 (file)
@@ -93,9 +93,10 @@ static inline void __hlist_bl_del(struct hlist_bl_node *n)
        LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK);
 
        /* pprev may be `first`, so be careful not to lose the lock bit */
-       *pprev = (struct hlist_bl_node *)
+       WRITE_ONCE(*pprev,
+                  (struct hlist_bl_node *)
                        ((unsigned long)next |
-                        ((unsigned long)*pprev & LIST_BL_LOCKMASK));
+                        ((unsigned long)*pprev & LIST_BL_LOCKMASK)));
        if (next)
                next->pprev = pprev;
 }
index f266661d2666596d808bbe8756c91239f5b8e6dc..444d2b1313bda37647b1660e4582202a7e3b66e4 100644 (file)
@@ -76,7 +76,8 @@ static inline void __hlist_nulls_del(struct hlist_nulls_node *n)
 {
        struct hlist_nulls_node *next = n->next;
        struct hlist_nulls_node **pprev = n->pprev;
-       *pprev = next;
+
+       WRITE_ONCE(*pprev, next);
        if (!is_a_nulls(next))
                next->pprev = pprev;
 }
index baad4cb8e9b065fb76c8e2b66379d9415d0f4e47..5a8677bafe0408bad140320471e38c100e4bf33a 100644 (file)
@@ -833,6 +833,7 @@ struct mlx4_dev {
        struct mlx4_quotas      quotas;
        struct radix_tree_root  qp_table_tree;
        u8                      rev_id;
+       u8                      port_random_macs;
        char                    board_id[MLX4_BOARD_ID_LEN];
        int                     numa_node;
        int                     oper_log_mgm_entry_size;
index 250b1ff8b48d43c0f9f2479e388b405d8238eb41..0b473cbfa7ef10c875c1eed802c2a7dd74f415d4 100644 (file)
@@ -429,7 +429,7 @@ struct health_buffer {
        __be32          rsvd2;
        u8              irisc_index;
        u8              synd;
-       __be16          ext_sync;
+       __be16          ext_synd;
 };
 
 struct mlx5_init_seg {
@@ -439,7 +439,8 @@ struct mlx5_init_seg {
        __be32                  cmdq_addr_h;
        __be32                  cmdq_addr_l_sz;
        __be32                  cmd_dbell;
-       __be32                  rsvd1[121];
+       __be32                  rsvd1[120];
+       __be32                  initializing;
        struct health_buffer    health;
        __be32                  rsvd2[884];
        __be32                  health_counter;
index 8b6d6f2154a4eaab1cce3db487b92d8d1b36d4a9..5c857f2a20d7b5da77e2d50f0b95a53cc88a5107 100644 (file)
@@ -391,9 +391,11 @@ struct mlx5_core_health {
        struct health_buffer __iomem   *health;
        __be32 __iomem                 *health_counter;
        struct timer_list               timer;
-       struct list_head                list;
        u32                             prev;
        int                             miss_counter;
+       bool                            sick;
+       struct workqueue_struct        *wq;
+       struct work_struct              work;
 };
 
 struct mlx5_cq_table {
@@ -485,8 +487,26 @@ struct mlx5_priv {
        spinlock_t              ctx_lock;
 };
 
+enum mlx5_device_state {
+       MLX5_DEVICE_STATE_UP,
+       MLX5_DEVICE_STATE_INTERNAL_ERROR,
+};
+
+enum mlx5_interface_state {
+       MLX5_INTERFACE_STATE_DOWN,
+       MLX5_INTERFACE_STATE_UP,
+};
+
+enum mlx5_pci_status {
+       MLX5_PCI_STATUS_DISABLED,
+       MLX5_PCI_STATUS_ENABLED,
+};
+
 struct mlx5_core_dev {
        struct pci_dev         *pdev;
+       /* sync pci state */
+       struct mutex            pci_status_mutex;
+       enum mlx5_pci_status    pci_status;
        u8                      rev_id;
        char                    board_id[MLX5_BOARD_ID_LEN];
        struct mlx5_cmd         cmd;
@@ -495,6 +515,10 @@ struct mlx5_core_dev {
        u32 hca_caps_max[MLX5_CAP_NUM][MLX5_UN_SZ_DW(hca_cap_union)];
        phys_addr_t             iseg_base;
        struct mlx5_init_seg __iomem *iseg;
+       enum mlx5_device_state  state;
+       /* sync interface state */
+       struct mutex            intf_state_mutex;
+       enum mlx5_interface_state interface_state;
        void                    (*event) (struct mlx5_core_dev *dev,
                                          enum mlx5_dev_event event,
                                          unsigned long param);
@@ -676,8 +700,8 @@ int mlx5_alloc_uuars(struct mlx5_core_dev *dev, struct mlx5_uuar_info *uuari);
 int mlx5_free_uuars(struct mlx5_core_dev *dev, struct mlx5_uuar_info *uuari);
 int mlx5_alloc_map_uar(struct mlx5_core_dev *mdev, struct mlx5_uar *uar);
 void mlx5_unmap_free_uar(struct mlx5_core_dev *mdev, struct mlx5_uar *uar);
-void mlx5_health_cleanup(void);
-void  __init mlx5_health_init(void);
+void mlx5_health_cleanup(struct mlx5_core_dev *dev);
+int mlx5_health_init(struct mlx5_core_dev *dev);
 void mlx5_start_health_poll(struct mlx5_core_dev *dev);
 void mlx5_stop_health_poll(struct mlx5_core_dev *dev);
 int mlx5_buf_alloc_node(struct mlx5_core_dev *dev, int size,
@@ -731,7 +755,7 @@ void mlx5_eq_pagefault(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe);
 #endif
 void mlx5_srq_event(struct mlx5_core_dev *dev, u32 srqn, int event_type);
 struct mlx5_core_srq *mlx5_core_get_srq(struct mlx5_core_dev *dev, u32 srqn);
-void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector);
+void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec);
 void mlx5_cq_event(struct mlx5_core_dev *dev, u32 cqn, int event_type);
 int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
                       int nent, u64 mask, const char *name, struct mlx5_uar *uar);
@@ -802,6 +826,11 @@ void mlx5_core_put_rsc(struct mlx5_core_rsc_common *common);
 int mlx5_query_odp_caps(struct mlx5_core_dev *dev,
                        struct mlx5_odp_caps *odp_caps);
 
+static inline int fw_initializing(struct mlx5_core_dev *dev)
+{
+       return ioread32be(&dev->iseg->initializing) >> 31;
+}
+
 static inline u32 mlx5_mkey_to_idx(u32 mkey)
 {
        return mkey >> 8;
@@ -865,4 +894,8 @@ static inline int mlx5_get_gid_table_len(u16 param)
        return 8 * (1 << param);
 }
 
+enum {
+       MLX5_TRIGGERED_CMD_COMP = (u64)1 << 32,
+};
+
 #endif /* MLX5_DRIVER_H */
index fdd0779ccdfa52309f5e99aeeadd84b356c1a472..eb0151bac50c1fd796f479d017bc3c3d7017c9e9 100644 (file)
@@ -269,7 +269,6 @@ struct mmc_card {
                                                /* for byte mode */
 #define MMC_QUIRK_NONSTD_SDIO  (1<<2)          /* non-standard SDIO card attached */
                                                /* (missing CIA registers) */
-#define MMC_QUIRK_BROKEN_CLK_GATING (1<<3)     /* clock gating the sdio bus will make card fail */
 #define MMC_QUIRK_NONSTD_FUNC_IF (1<<4)                /* SDIO card has nonstd function interfaces */
 #define MMC_QUIRK_DISABLE_CD   (1<<5)          /* disconnect CD/DAT[3] resistor */
 #define MMC_QUIRK_INAND_CMD38  (1<<6)          /* iNAND devices have broken CMD38 */
index 258daf914c6df301330b70f00d95ac95a44ac545..37967b6da03cf542d7a5762342c2089d93054b7a 100644 (file)
@@ -152,10 +152,8 @@ extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
 extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
        struct mmc_command *, int);
 extern void mmc_start_bkops(struct mmc_card *card, bool from_exception);
-extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool,
-                       bool, bool);
 extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
-extern int mmc_send_tuning(struct mmc_host *host);
+extern int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
 extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
 
 #define MMC_ERASE_ARG          0x00000000
index 134c57422740944fb8d1d004c2f8126b2bdeb604..f67b2ec18e6d87c8df27b314f7320e49163850f5 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/scatterlist.h>
 #include <linux/mmc/core.h>
+#include <linux/dmaengine.h>
 
 #define MAX_MCI_SLOTS  2
 
@@ -40,6 +41,17 @@ enum {
 
 struct mmc_data;
 
+enum {
+       TRANS_MODE_PIO = 0,
+       TRANS_MODE_IDMAC,
+       TRANS_MODE_EDMAC
+};
+
+struct dw_mci_dma_slave {
+       struct dma_chan *ch;
+       enum dma_transfer_direction direction;
+};
+
 /**
  * struct dw_mci - MMC controller state shared between all slots
  * @lock: Spinlock protecting the queue and associated data.
@@ -154,7 +166,14 @@ struct dw_mci {
        dma_addr_t              sg_dma;
        void                    *sg_cpu;
        const struct dw_mci_dma_ops     *dma_ops;
+       /* For idmac */
        unsigned int            ring_size;
+
+       /* For edmac */
+       struct dw_mci_dma_slave *dms;
+       /* Registers's physical base address */
+       void                    *phy_regs;
+
        u32                     cmd_status;
        u32                     data_status;
        u32                     stop_cmdr;
@@ -208,8 +227,8 @@ struct dw_mci {
 struct dw_mci_dma_ops {
        /* DMA Ops */
        int (*init)(struct dw_mci *host);
-       void (*start)(struct dw_mci *host, unsigned int sg_len);
-       void (*complete)(struct dw_mci *host);
+       int (*start)(struct dw_mci *host, unsigned int sg_len);
+       void (*complete)(void *host);
        void (*stop)(struct dw_mci *host);
        void (*cleanup)(struct dw_mci *host);
        void (*exit)(struct dw_mci *host);
index 83b81fd865f3bba12e7bc7d4c0ac8091ca067c09..8673ffe3d86ef83fc657cde31a43058b94840f61 100644 (file)
@@ -292,18 +292,6 @@ struct mmc_host {
 
        mmc_pm_flag_t           pm_caps;        /* supported pm features */
 
-#ifdef CONFIG_MMC_CLKGATE
-       int                     clk_requests;   /* internal reference counter */
-       unsigned int            clk_delay;      /* number of MCI clk hold cycles */
-       bool                    clk_gated;      /* clock gated */
-       struct delayed_work     clk_gate_work; /* delayed clock gate */
-       unsigned int            clk_old;        /* old clock value cache */
-       spinlock_t              clk_lock;       /* lock for clk fields */
-       struct mutex            clk_gate_mutex; /* mutex for clock gating */
-       struct device_attribute clkgate_delay_attr;
-       unsigned long           clkgate_delay;
-#endif
-
        /* host specific block data */
        unsigned int            max_seg_size;   /* see blk_queue_max_segment_size */
        unsigned short          max_segs;       /* see blk_queue_max_segments */
@@ -423,6 +411,7 @@ int mmc_regulator_get_ocrmask(struct regulator *supply);
 int mmc_regulator_set_ocr(struct mmc_host *mmc,
                        struct regulator *supply,
                        unsigned short vdd_bit);
+int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios);
 #else
 static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
 {
@@ -435,6 +424,12 @@ static inline int mmc_regulator_set_ocr(struct mmc_host *mmc,
 {
        return 0;
 }
+
+static inline int mmc_regulator_set_vqmmc(struct mmc_host *mmc,
+                                         struct mmc_ios *ios)
+{
+       return -EINVAL;
+}
 #endif
 
 int mmc_regulator_get_supply(struct mmc_host *mmc);
@@ -479,26 +474,6 @@ static inline int mmc_host_packed_wr(struct mmc_host *host)
        return host->caps2 & MMC_CAP2_PACKED_WR;
 }
 
-#ifdef CONFIG_MMC_CLKGATE
-void mmc_host_clk_hold(struct mmc_host *host);
-void mmc_host_clk_release(struct mmc_host *host);
-unsigned int mmc_host_clk_rate(struct mmc_host *host);
-
-#else
-static inline void mmc_host_clk_hold(struct mmc_host *host)
-{
-}
-
-static inline void mmc_host_clk_release(struct mmc_host *host)
-{
-}
-
-static inline unsigned int mmc_host_clk_rate(struct mmc_host *host)
-{
-       return host->ios.clock;
-}
-#endif
-
 static inline int mmc_card_hs(struct mmc_card *card)
 {
        return card->host->ios.timing == MMC_TIMING_SD_HS ||
index 641b7d6fd0966dac7f49465dd0c0be9df2a560f8..3a5abe95affd381564b1b08b1996dd4e0ee52490 100644 (file)
 #define G10_MPI_H
 
 #include <linux/types.h>
-
-/* DSI defines */
-
-#define SHA1_DIGEST_LENGTH   20
-
-/*end of DSI defines */
+#include <linux/scatterlist.h>
 
 #define BYTES_PER_MPI_LIMB     (BITS_PER_LONG / 8)
 #define BITS_PER_MPI_LIMB      BITS_PER_LONG
@@ -78,6 +73,7 @@ void mpi_swap(MPI a, MPI b);
 MPI do_encode_md(const void *sha_buffer, unsigned nbits);
 MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes);
 MPI mpi_read_from_buffer(const void *buffer, unsigned *ret_nread);
+MPI mpi_read_raw_from_sgl(struct scatterlist *sgl, unsigned int len);
 int mpi_fromstr(MPI val, const char *str);
 u32 mpi_get_keyid(MPI a, u32 *keyid);
 void *mpi_get_buffer(MPI a, unsigned *nbytes, int *sign);
@@ -85,6 +81,8 @@ int mpi_read_buffer(MPI a, uint8_t *buf, unsigned buf_len, unsigned *nbytes,
                    int *sign);
 void *mpi_get_secure_buffer(MPI a, unsigned *nbytes, int *sign);
 int mpi_set_buffer(MPI a, const void *buffer, unsigned nbytes, int sign);
+int mpi_write_to_sgl(MPI a, struct scatterlist *sg, unsigned *nbytes,
+                    int *sign);
 
 #define log_mpidump g10_log_mpidump
 
index ad939d0ba816372b975d5c44d9582617b7b618e0..0b4460374020fd98e40248c990eb6110493a21be 100644 (file)
@@ -174,6 +174,7 @@ struct msi_controller {
 struct irq_domain;
 struct irq_chip;
 struct device_node;
+struct fwnode_handle;
 struct msi_domain_info;
 
 /**
@@ -262,7 +263,7 @@ enum {
 int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,
                            bool force);
 
-struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
+struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
                                         struct msi_domain_info *info,
                                         struct irq_domain *parent);
 int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
@@ -270,7 +271,7 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
 void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev);
 struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
 
-struct irq_domain *platform_msi_create_irq_domain(struct device_node *np,
+struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
                                                  struct msi_domain_info *info,
                                                  struct irq_domain *parent);
 int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
@@ -280,19 +281,26 @@ void platform_msi_domain_free_irqs(struct device *dev);
 
 #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
 void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg);
-struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
+struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
                                             struct msi_domain_info *info,
                                             struct irq_domain *parent);
 int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev,
                              int nvec, int type);
 void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev);
-struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node,
+struct irq_domain *pci_msi_create_default_irq_domain(struct fwnode_handle *fwnode,
                 struct msi_domain_info *info, struct irq_domain *parent);
 
 irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev,
                                          struct msi_desc *desc);
 int pci_msi_domain_check_cap(struct irq_domain *domain,
                             struct msi_domain_info *info, struct device *dev);
+u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev);
+struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev);
+#else
+static inline struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev)
+{
+       return NULL;
+}
 #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
 
 #endif /* LINUX_MSI_H */
index 049d4b03c4c4d50bb6292300818a0cf3d126c0ca..70ac5e28e6b737f7aac7b0b27d929adacf4aad12 100644 (file)
@@ -24,7 +24,8 @@
 #include <linux/fcntl.h>       /* For O_CLOEXEC and O_NONBLOCK */
 #include <linux/kmemcheck.h>
 #include <linux/rcupdate.h>
-#include <linux/jump_label.h>
+#include <linux/once.h>
+
 #include <uapi/linux/net.h>
 
 struct poll_table_struct;
@@ -250,22 +251,8 @@ do {                                                               \
        } while (0)
 #endif
 
-bool __net_get_random_once(void *buf, int nbytes, bool *done,
-                          struct static_key *done_key);
-
-#define net_get_random_once(buf, nbytes)                               \
-       ({                                                              \
-               bool ___ret = false;                                    \
-               static bool ___done = false;                            \
-               static struct static_key ___once_key =                  \
-                       STATIC_KEY_INIT_TRUE;                           \
-               if (static_key_true(&___once_key))                      \
-                       ___ret = __net_get_random_once(buf,             \
-                                                      nbytes,          \
-                                                      &___done,        \
-                                                      &___once_key);   \
-               ___ret;                                                 \
-       })
+#define net_get_random_once(buf, nbytes)                       \
+       get_random_once((buf), (nbytes))
 
 int kernel_sendmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec,
                   size_t num, size_t len);
index 9672781c593d4e2b654625d5769bce4671675647..f0d87347df19edd25d84d61dab931ebf8112117d 100644 (file)
@@ -125,6 +125,9 @@ enum {
 #define NETIF_F_HW_L2FW_DOFFLOAD       __NETIF_F(HW_L2FW_DOFFLOAD)
 #define NETIF_F_BUSY_POLL      __NETIF_F(BUSY_POLL)
 
+#define for_each_netdev_feature(mask_addr, bit)        \
+       for_each_set_bit(bit, (unsigned long *)mask_addr, NETDEV_FEATURE_COUNT)
+
 /* Features valid for ethtool to change */
 /* = all defined minus driver/device-class-related */
 #define NETIF_F_NEVER_CHANGE   (NETIF_F_VLAN_CHALLENGED | \
@@ -167,6 +170,12 @@ enum {
  */
 #define NETIF_F_ALL_FOR_ALL    (NETIF_F_NOCACHE_COPY | NETIF_F_FSO)
 
+/*
+ * If upper/master device has these features disabled, they must be disabled
+ * on all lower/slave devices as well.
+ */
+#define NETIF_F_UPPER_DISABLES NETIF_F_LRO
+
 /* changeable features with no special hardware requirements */
 #define NETIF_F_SOFT_FEATURES  (NETIF_F_GSO | NETIF_F_GRO)
 
index 2d15e383144038ca12d5e876bf4e5cbfe5f84feb..4ac653b7b8ace2a9a2f2ee0feae07aab1bbcf459 100644 (file)
@@ -881,6 +881,7 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
  * int (*ndo_set_vf_rate)(struct net_device *dev, int vf, int min_tx_rate,
  *                       int max_tx_rate);
  * int (*ndo_set_vf_spoofchk)(struct net_device *dev, int vf, bool setting);
+ * int (*ndo_set_vf_trust)(struct net_device *dev, int vf, bool setting);
  * int (*ndo_get_vf_config)(struct net_device *dev,
  *                         int vf, struct ifla_vf_info *ivf);
  * int (*ndo_set_vf_link_state)(struct net_device *dev, int vf, int link_state);
@@ -1054,6 +1055,10 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
  *     This function is used to pass protocol port error state information
  *     to the switch driver. The switch driver can react to the proto_down
  *      by doing a phys down on the associated switch port.
+ * int (*ndo_fill_metadata_dst)(struct net_device *dev, struct sk_buff *skb);
+ *     This function is used to get egress tunnel information for given skb.
+ *     This is useful for retrieving outer tunnel header parameters while
+ *     sampling packet.
  *
  */
 struct net_device_ops {
@@ -1109,6 +1114,8 @@ struct net_device_ops {
                                                   int max_tx_rate);
        int                     (*ndo_set_vf_spoofchk)(struct net_device *dev,
                                                       int vf, bool setting);
+       int                     (*ndo_set_vf_trust)(struct net_device *dev,
+                                                   int vf, bool setting);
        int                     (*ndo_get_vf_config)(struct net_device *dev,
                                                     int vf,
                                                     struct ifla_vf_info *ivf);
@@ -1227,6 +1234,8 @@ struct net_device_ops {
        int                     (*ndo_get_iflink)(const struct net_device *dev);
        int                     (*ndo_change_proto_down)(struct net_device *dev,
                                                         bool proto_down);
+       int                     (*ndo_fill_metadata_dst)(struct net_device *dev,
+                                                      struct sk_buff *skb);
 };
 
 /**
@@ -1258,9 +1267,10 @@ struct net_device_ops {
  * @IFF_LIVE_ADDR_CHANGE: device supports hardware address
  *     change when it's running
  * @IFF_MACVLAN: Macvlan device
- * @IFF_VRF_MASTER: device is a VRF master
+ * @IFF_L3MDEV_MASTER: device is an L3 master device
  * @IFF_NO_QUEUE: device can run without qdisc attached
  * @IFF_OPENVSWITCH: device is a Open vSwitch master
+ * @IFF_L3MDEV_SLAVE: device is enslaved to an L3 master device
  */
 enum netdev_priv_flags {
        IFF_802_1Q_VLAN                 = 1<<0,
@@ -1283,9 +1293,10 @@ enum netdev_priv_flags {
        IFF_XMIT_DST_RELEASE_PERM       = 1<<17,
        IFF_IPVLAN_MASTER               = 1<<18,
        IFF_IPVLAN_SLAVE                = 1<<19,
-       IFF_VRF_MASTER                  = 1<<20,
+       IFF_L3MDEV_MASTER               = 1<<20,
        IFF_NO_QUEUE                    = 1<<21,
        IFF_OPENVSWITCH                 = 1<<22,
+       IFF_L3MDEV_SLAVE                = 1<<23,
 };
 
 #define IFF_802_1Q_VLAN                        IFF_802_1Q_VLAN
@@ -1308,7 +1319,7 @@ enum netdev_priv_flags {
 #define IFF_XMIT_DST_RELEASE_PERM      IFF_XMIT_DST_RELEASE_PERM
 #define IFF_IPVLAN_MASTER              IFF_IPVLAN_MASTER
 #define IFF_IPVLAN_SLAVE               IFF_IPVLAN_SLAVE
-#define IFF_VRF_MASTER                 IFF_VRF_MASTER
+#define IFF_L3MDEV_MASTER              IFF_L3MDEV_MASTER
 #define IFF_NO_QUEUE                   IFF_NO_QUEUE
 #define IFF_OPENVSWITCH                        IFF_OPENVSWITCH
 
@@ -1427,7 +1438,6 @@ enum netdev_priv_flags {
  *     @dn_ptr:        DECnet specific data
  *     @ip6_ptr:       IPv6 specific data
  *     @ax25_ptr:      AX.25 specific data
- *     @vrf_ptr:       VRF specific data
  *     @ieee80211_ptr: IEEE 802.11 specific data, assign before registering
  *
  *     @last_rx:       Time of last Rx
@@ -1587,6 +1597,9 @@ struct net_device {
 #ifdef CONFIG_NET_SWITCHDEV
        const struct switchdev_ops *switchdev_ops;
 #endif
+#ifdef CONFIG_NET_L3_MASTER_DEV
+       const struct l3mdev_ops *l3mdev_ops;
+#endif
 
        const struct header_ops *header_ops;
 
@@ -1646,7 +1659,6 @@ struct net_device {
        struct dn_dev __rcu     *dn_ptr;
        struct inet6_dev __rcu  *ip6_ptr;
        void                    *ax25_ptr;
-       struct net_vrf_dev __rcu *vrf_ptr;
        struct wireless_dev     *ieee80211_ptr;
        struct wpan_dev         *ieee802154_ptr;
 #if IS_ENABLED(CONFIG_MPLS_ROUTING)
@@ -2103,6 +2115,7 @@ struct pcpu_sw_netstats {
 #define NETDEV_PRECHANGEMTU    0x0017 /* notify before mtu change happened */
 #define NETDEV_CHANGEINFODATA  0x0018
 #define NETDEV_BONDING_INFO    0x0019
+#define NETDEV_PRECHANGEUPPER  0x001A
 
 int register_netdevice_notifier(struct notifier_block *nb);
 int unregister_netdevice_notifier(struct notifier_block *nb);
@@ -2203,6 +2216,7 @@ void dev_add_offload(struct packet_offload *po);
 void dev_remove_offload(struct packet_offload *po);
 
 int dev_get_iflink(const struct net_device *dev);
+int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
 struct net_device *__dev_get_by_flags(struct net *net, unsigned short flags,
                                      unsigned short mask);
 struct net_device *dev_get_by_name(struct net *net, const char *name);
@@ -2213,12 +2227,8 @@ int dev_open(struct net_device *dev);
 int dev_close(struct net_device *dev);
 int dev_close_many(struct list_head *head, bool unlink);
 void dev_disable_lro(struct net_device *dev);
-int dev_loopback_xmit(struct sock *sk, struct sk_buff *newskb);
-int dev_queue_xmit_sk(struct sock *sk, struct sk_buff *skb);
-static inline int dev_queue_xmit(struct sk_buff *skb)
-{
-       return dev_queue_xmit_sk(skb->sk, skb);
-}
+int dev_loopback_xmit(struct net *net, struct sock *sk, struct sk_buff *newskb);
+int dev_queue_xmit(struct sk_buff *skb);
 int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv);
 int register_netdevice(struct net_device *dev);
 void unregister_netdevice_queue(struct net_device *dev, struct list_head *head);
@@ -2990,11 +3000,7 @@ static inline void dev_consume_skb_any(struct sk_buff *skb)
 
 int netif_rx(struct sk_buff *skb);
 int netif_rx_ni(struct sk_buff *skb);
-int netif_receive_skb_sk(struct sock *sk, struct sk_buff *skb);
-static inline int netif_receive_skb(struct sk_buff *skb)
-{
-       return netif_receive_skb_sk(skb->sk, skb);
-}
+int netif_receive_skb(struct sk_buff *skb);
 gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb);
 void napi_gro_flush(struct napi_struct *napi, bool flush_old);
 struct sk_buff *napi_get_frags(struct napi_struct *napi);
@@ -3832,9 +3838,14 @@ static inline bool netif_supports_nofcs(struct net_device *dev)
        return dev->priv_flags & IFF_SUPP_NOFCS;
 }
 
-static inline bool netif_is_vrf(const struct net_device *dev)
+static inline bool netif_is_l3_master(const struct net_device *dev)
 {
-       return dev->priv_flags & IFF_VRF_MASTER;
+       return dev->priv_flags & IFF_L3MDEV_MASTER;
+}
+
+static inline bool netif_is_l3_slave(const struct net_device *dev)
+{
+       return dev->priv_flags & IFF_L3MDEV_SLAVE;
 }
 
 static inline bool netif_is_bridge_master(const struct net_device *dev)
@@ -3847,27 +3858,6 @@ static inline bool netif_is_ovs_master(const struct net_device *dev)
        return dev->priv_flags & IFF_OPENVSWITCH;
 }
 
-static inline bool netif_index_is_vrf(struct net *net, int ifindex)
-{
-       bool rc = false;
-
-#if IS_ENABLED(CONFIG_NET_VRF)
-       struct net_device *dev;
-
-       if (ifindex == 0)
-               return false;
-
-       rcu_read_lock();
-
-       dev = dev_get_by_index_rcu(net, ifindex);
-       if (dev)
-               rc = netif_is_vrf(dev);
-
-       rcu_read_unlock();
-#endif
-       return rc;
-}
-
 /* This device needs to keep skb dst for qdisc enqueue or ndo_start_xmit() */
 static inline void netif_keep_dst(struct net_device *dev)
 {
index 36a652531791e7abfaf5fe55afada5a36425ea75..0ad556726181ada44d9f137502b65e6a10c242c7 100644 (file)
@@ -54,8 +54,9 @@ struct nf_hook_state {
        struct net_device *in;
        struct net_device *out;
        struct sock *sk;
+       struct net *net;
        struct list_head *hook_list;
-       int (*okfn)(struct sock *, struct sk_buff *);
+       int (*okfn)(struct net *, struct sock *, struct sk_buff *);
 };
 
 static inline void nf_hook_state_init(struct nf_hook_state *p,
@@ -65,7 +66,8 @@ static inline void nf_hook_state_init(struct nf_hook_state *p,
                                      struct net_device *indev,
                                      struct net_device *outdev,
                                      struct sock *sk,
-                                     int (*okfn)(struct sock *, struct sk_buff *))
+                                     struct net *net,
+                                     int (*okfn)(struct net *, struct sock *, struct sk_buff *))
 {
        p->hook = hook;
        p->thresh = thresh;
@@ -73,11 +75,12 @@ static inline void nf_hook_state_init(struct nf_hook_state *p,
        p->in = indev;
        p->out = outdev;
        p->sk = sk;
+       p->net = net;
        p->hook_list = hook_list;
        p->okfn = okfn;
 }
 
-typedef unsigned int nf_hookfn(const struct nf_hook_ops *ops,
+typedef unsigned int nf_hookfn(void *priv,
                               struct sk_buff *skb,
                               const struct nf_hook_state *state);
 
@@ -87,7 +90,6 @@ struct nf_hook_ops {
        /* User fills in from here down. */
        nf_hookfn               *hook;
        struct net_device       *dev;
-       struct module           *owner;
        void                    *priv;
        u_int8_t                pf;
        unsigned int            hooknum;
@@ -167,32 +169,32 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state);
  *     value indicates the packet has been consumed by the hook.
  */
 static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
+                                struct net *net,
                                 struct sock *sk,
                                 struct sk_buff *skb,
                                 struct net_device *indev,
                                 struct net_device *outdev,
-                                int (*okfn)(struct sock *, struct sk_buff *),
+                                int (*okfn)(struct net *, struct sock *, struct sk_buff *),
                                 int thresh)
 {
-       struct net *net = dev_net(indev ? indev : outdev);
        struct list_head *hook_list = &net->nf.hooks[pf][hook];
 
        if (nf_hook_list_active(hook_list, pf, hook)) {
                struct nf_hook_state state;
 
                nf_hook_state_init(&state, hook_list, hook, thresh,
-                                  pf, indev, outdev, sk, okfn);
+                                  pf, indev, outdev, sk, net, okfn);
                return nf_hook_slow(skb, &state);
        }
        return 1;
 }
 
-static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sock *sk,
-                         struct sk_buff *skb, struct net_device *indev,
-                         struct net_device *outdev,
-                         int (*okfn)(struct sock *, struct sk_buff *))
+static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
+                         struct sock *sk, struct sk_buff *skb,
+                         struct net_device *indev, struct net_device *outdev,
+                         int (*okfn)(struct net *, struct sock *, struct sk_buff *))
 {
-       return nf_hook_thresh(pf, hook, sk, skb, indev, outdev, okfn, INT_MIN);
+       return nf_hook_thresh(pf, hook, net, sk, skb, indev, outdev, okfn, INT_MIN);
 }
                    
 /* Activate hook; either okfn or kfree_skb called, unless a hook
@@ -213,36 +215,38 @@ static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sock *sk,
 */
 
 static inline int
-NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct sock *sk,
+NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,
               struct sk_buff *skb, struct net_device *in,
               struct net_device *out,
-              int (*okfn)(struct sock *, struct sk_buff *), int thresh)
+              int (*okfn)(struct net *, struct sock *, struct sk_buff *),
+              int thresh)
 {
-       int ret = nf_hook_thresh(pf, hook, sk, skb, in, out, okfn, thresh);
+       int ret = nf_hook_thresh(pf, hook, net, sk, skb, in, out, okfn, thresh);
        if (ret == 1)
-               ret = okfn(sk, skb);
+               ret = okfn(net, sk, skb);
        return ret;
 }
 
 static inline int
-NF_HOOK_COND(uint8_t pf, unsigned int hook, struct sock *sk,
+NF_HOOK_COND(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,
             struct sk_buff *skb, struct net_device *in, struct net_device *out,
-            int (*okfn)(struct sock *, struct sk_buff *), bool cond)
+            int (*okfn)(struct net *, struct sock *, struct sk_buff *),
+            bool cond)
 {
        int ret;
 
        if (!cond ||
-           ((ret = nf_hook_thresh(pf, hook, sk, skb, in, out, okfn, INT_MIN)) == 1))
-               ret = okfn(sk, skb);
+           ((ret = nf_hook_thresh(pf, hook, net, sk, skb, in, out, okfn, INT_MIN)) == 1))
+               ret = okfn(net, sk, skb);
        return ret;
 }
 
 static inline int
-NF_HOOK(uint8_t pf, unsigned int hook, struct sock *sk, struct sk_buff *skb,
+NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct sk_buff *skb,
        struct net_device *in, struct net_device *out,
-       int (*okfn)(struct sock *, struct sk_buff *))
+       int (*okfn)(struct net *, struct sock *, struct sk_buff *))
 {
-       return NF_HOOK_THRESH(pf, hook, sk, skb, in, out, okfn, INT_MIN);
+       return NF_HOOK_THRESH(pf, hook, net, sk, skb, in, out, okfn, INT_MIN);
 }
 
 /* Call setsockopt() */
@@ -278,7 +282,7 @@ struct nf_afinfo {
                                 struct flowi *fl, bool strict);
        void            (*saveroute)(const struct sk_buff *skb,
                                     struct nf_queue_entry *entry);
-       int             (*reroute)(struct sk_buff *skb,
+       int             (*reroute)(struct net *net, struct sk_buff *skb,
                                   const struct nf_queue_entry *entry);
        int             route_key_size;
 };
@@ -342,21 +346,27 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family)
 }
 
 #else /* !CONFIG_NETFILTER */
-#define NF_HOOK(pf, hook, sk, skb, indev, outdev, okfn) (okfn)(sk, skb)
-#define NF_HOOK_COND(pf, hook, sk, skb, indev, outdev, okfn, cond) (okfn)(sk, skb)
-static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
-                                struct sock *sk,
-                                struct sk_buff *skb,
-                                struct net_device *indev,
-                                struct net_device *outdev,
-                                int (*okfn)(struct sock *sk, struct sk_buff *), int thresh)
+static inline int
+NF_HOOK_COND(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,
+            struct sk_buff *skb, struct net_device *in, struct net_device *out,
+            int (*okfn)(struct net *, struct sock *, struct sk_buff *),
+            bool cond)
+{
+       return okfn(net, sk, skb);
+}
+
+static inline int
+NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,
+       struct sk_buff *skb, struct net_device *in, struct net_device *out,
+       int (*okfn)(struct net *, struct sock *, struct sk_buff *))
 {
-       return okfn(sk, skb);
+       return okfn(net, sk, skb);
 }
-static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sock *sk,
-                         struct sk_buff *skb, struct net_device *indev,
-                         struct net_device *outdev,
-                         int (*okfn)(struct sock *, struct sk_buff *))
+
+static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
+                         struct sock *sk, struct sk_buff *skb,
+                         struct net_device *indev, struct net_device *outdev,
+                         int (*okfn)(struct net *, struct sock *, struct sk_buff *))
 {
        return 1;
 }
@@ -373,24 +383,28 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family)
 extern void (*ip_ct_attach)(struct sk_buff *, const struct sk_buff *) __rcu;
 void nf_ct_attach(struct sk_buff *, const struct sk_buff *);
 extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu;
+#else
+static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
+#endif
 
 struct nf_conn;
 enum ip_conntrack_info;
 struct nlattr;
 
-struct nfq_ct_hook {
+struct nfnl_ct_hook {
+       struct nf_conn *(*get_ct)(const struct sk_buff *skb,
+                                 enum ip_conntrack_info *ctinfo);
        size_t (*build_size)(const struct nf_conn *ct);
-       int (*build)(struct sk_buff *skb, struct nf_conn *ct);
+       int (*build)(struct sk_buff *skb, struct nf_conn *ct,
+                    enum ip_conntrack_info ctinfo,
+                    u_int16_t ct_attr, u_int16_t ct_info_attr);
        int (*parse)(const struct nlattr *attr, struct nf_conn *ct);
        int (*attach_expect)(const struct nlattr *attr, struct nf_conn *ct,
                             u32 portid, u32 report);
        void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct,
                           enum ip_conntrack_info ctinfo, s32 off);
 };
-extern struct nfq_ct_hook __rcu *nfq_ct_hook;
-#else
-static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
-#endif
+extern struct nfnl_ct_hook __rcu *nfnl_ct_hook;
 
 /**
  * nf_skb_duplicated - TEE target has sent a packet
index e955d47306259c5867d80831bd78d99490849b0b..249d1bb01e03b0367f56f251c02269b24be0dac6 100644 (file)
@@ -45,11 +45,11 @@ int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid,
 void nfnl_lock(__u8 subsys_id);
 void nfnl_unlock(__u8 subsys_id);
 #ifdef CONFIG_PROVE_LOCKING
-int lockdep_nfnl_is_held(__u8 subsys_id);
+bool lockdep_nfnl_is_held(__u8 subsys_id);
 #else
-static inline int lockdep_nfnl_is_held(__u8 subsys_id)
+static inline bool lockdep_nfnl_is_held(__u8 subsys_id)
 {
-       return 1;
+       return true;
 }
 #endif /* CONFIG_PROVE_LOCKING */
 
index b006b719183fc40aef93d6eae1e90f302979a4dc..c5577410c25d3b6b3520811c994a1f3d20ed1488 100644 (file)
@@ -13,6 +13,7 @@
  * @target:    the target extension
  * @matchinfo: per-match data
  * @targetinfo:        per-target data
+ * @net                network namespace through which the action was invoked
  * @in:                input netdevice
  * @out:       output netdevice
  * @fragoff:   packet is a fragment, this is the data offset
@@ -24,7 +25,6 @@
  * Fields written to by extensions:
  *
  * @hotdrop:   drop packet if we had inspection problems
- * Network namespace obtainable using dev_net(in/out)
  */
 struct xt_action_param {
        union {
@@ -34,6 +34,7 @@ struct xt_action_param {
        union {
                const void *matchinfo, *targinfo;
        };
+       struct net *net;
        const struct net_device *in, *out;
        int fragoff;
        unsigned int thoff;
index c22a7fb8d0df08155857d8ca01ce4c67bfa01988..6f074db2f23def7befbf60dd41792e554503db6c 100644 (file)
@@ -53,7 +53,6 @@ extern struct xt_table *arpt_register_table(struct net *net,
                                            const struct arpt_replace *repl);
 extern void arpt_unregister_table(struct xt_table *table);
 extern unsigned int arpt_do_table(struct sk_buff *skb,
-                                 unsigned int hook,
                                  const struct nf_hook_state *state,
                                  struct xt_table *table);
 
index 2437b8a5d7a945d2a3ab5c4ff67dfd6f260bc95a..2ed40c402b5e446f9a06de983de8f9834d6c8b07 100644 (file)
@@ -17,7 +17,7 @@ enum nf_br_hook_priorities {
 
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 
-int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb);
+int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
 
 static inline void br_drop_fake_rtable(struct sk_buff *skb)
 {
index 8ca6d6464ea31fd7fa8c9faf53c9b5aa5c58980e..2ea517c7c6b945d7842633c7e20dc0c35ab8ffc2 100644 (file)
@@ -111,9 +111,9 @@ struct ebt_table {
 extern struct ebt_table *ebt_register_table(struct net *net,
                                            const struct ebt_table *table);
 extern void ebt_unregister_table(struct net *net, struct ebt_table *table);
-extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff *skb,
-   const struct net_device *in, const struct net_device *out,
-   struct ebt_table *table);
+extern unsigned int ebt_do_table(struct sk_buff *skb,
+                                const struct nf_hook_state *state,
+                                struct ebt_table *table);
 
 /* Used in the kernel match() functions */
 #define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
index cb0727fe2b3dbc5894b98867370615295edd3c87..187feabe557c379b2a78bb4467623722e7ed4996 100644 (file)
@@ -17,7 +17,7 @@ static inline int nf_hook_ingress(struct sk_buff *skb)
 
        nf_hook_state_init(&state, &skb->dev->nf_hooks_ingress,
                           NF_NETDEV_INGRESS, INT_MIN, NFPROTO_NETDEV, NULL,
-                          skb->dev, NULL, NULL);
+                          skb->dev, NULL, dev_net(skb->dev), NULL);
        return nf_hook_slow(skb, &state);
 }
 
index 6e4591bb54d495d2f4ee3f82058305be7539d025..98c03b2462b5d7c6f98982d16eb13ed8cde2cb13 100644 (file)
@@ -6,7 +6,7 @@
 
 #include <uapi/linux/netfilter_ipv4.h>
 
-int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type);
+int ip_route_me_harder(struct net *net, struct sk_buff *skb, unsigned addr_type);
 __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
                       unsigned int dataoff, u_int8_t protocol);
 #endif /*__LINUX_IP_NETFILTER_H*/
index 4073510da485fbad83ba5236d7ba5794b599653a..aa598f942c01a8d549302d570d17b6fa95198569 100644 (file)
@@ -64,7 +64,6 @@ struct ipt_error {
 
 extern void *ipt_alloc_initial_table(const struct xt_table *);
 extern unsigned int ipt_do_table(struct sk_buff *skb,
-                                unsigned int hook,
                                 const struct nf_hook_state *state,
                                 struct xt_table *table);
 
index 771574677e83136c82675559c6f160f6fd827eba..47c6b04c28c0cc463cf4b81a6148936bc750d95e 100644 (file)
@@ -17,12 +17,12 @@ struct nf_ipv6_ops {
        int (*chk_addr)(struct net *net, const struct in6_addr *addr,
                        const struct net_device *dev, int strict);
        void (*route_input)(struct sk_buff *skb);
-       int (*fragment)(struct sock *sk, struct sk_buff *skb,
-                       int (*output)(struct sock *, struct sk_buff *));
+       int (*fragment)(struct net *net, struct sock *sk, struct sk_buff *skb,
+                       int (*output)(struct net *, struct sock *, struct sk_buff *));
 };
 
 #ifdef CONFIG_NETFILTER
-int ip6_route_me_harder(struct sk_buff *skb);
+int ip6_route_me_harder(struct net *net, struct sk_buff *skb);
 __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
                        unsigned int dataoff, u_int8_t protocol);
 
index b40d2b635778372f46870d00fd06a10266359f67..0f76e5c674f9920ecc8ccb56635fa4353ea2136c 100644 (file)
@@ -30,7 +30,6 @@ extern struct xt_table *ip6t_register_table(struct net *net,
                                            const struct ip6t_replace *repl);
 extern void ip6t_unregister_table(struct net *net, struct xt_table *table);
 extern unsigned int ip6t_do_table(struct sk_buff *skb,
-                                 unsigned int hook,
                                  const struct nf_hook_state *state,
                                  struct xt_table *table);
 
index f3191828f037368a28b3fc1affc2c5d919b74001..87d6d1632dd4866064fb7319e7ed6f2bff260c49 100644 (file)
@@ -29,6 +29,7 @@ struct device_node;
  */
 enum of_gpio_flags {
        OF_GPIO_ACTIVE_LOW = 0x1,
+       OF_GPIO_SINGLE_ENDED = 0x2,
 };
 
 #ifdef CONFIG_OF_GPIO
index 4bcbd586a67296081fadb219ee96f32d7fcb65b2..65d969246a4d02e1ee8854451c923d5003941d31 100644 (file)
@@ -46,6 +46,11 @@ extern int of_irq_get(struct device_node *dev, int index);
 extern int of_irq_get_byname(struct device_node *dev, const char *name);
 extern int of_irq_to_resource_table(struct device_node *dev,
                struct resource *res, int nr_irqs);
+extern struct irq_domain *of_msi_get_domain(struct device *dev,
+                                           struct device_node *np,
+                                           enum irq_domain_bus_token token);
+extern struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
+                                                      u32 rid);
 #else
 static inline int of_irq_count(struct device_node *dev)
 {
@@ -64,6 +69,17 @@ static inline int of_irq_to_resource_table(struct device_node *dev,
 {
        return 0;
 }
+static inline struct irq_domain *of_msi_get_domain(struct device *dev,
+                                                  struct device_node *np,
+                                                  enum irq_domain_bus_token token)
+{
+       return NULL;
+}
+static inline struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
+                                                             u32 rid)
+{
+       return NULL;
+}
 #endif
 
 #if defined(CONFIG_OF)
@@ -75,6 +91,7 @@ static inline int of_irq_to_resource_table(struct device_node *dev,
 extern unsigned int irq_of_parse_and_map(struct device_node *node, int index);
 extern struct device_node *of_irq_find_parent(struct device_node *child);
 extern void of_msi_configure(struct device *dev, struct device_node *np);
+u32 of_msi_map_rid(struct device *dev, struct device_node *msi_np, u32 rid_in);
 
 #else /* !CONFIG_OF */
 static inline unsigned int irq_of_parse_and_map(struct device_node *dev,
@@ -87,6 +104,12 @@ static inline void *of_irq_find_parent(struct device_node *child)
 {
        return NULL;
 }
+
+static inline u32 of_msi_map_rid(struct device *dev,
+                                struct device_node *msi_np, u32 rid_in)
+{
+       return rid_in;
+}
 #endif /* !CONFIG_OF */
 
 #endif /* __OF_IRQ_H */
index e5a70132a240f66803a0c27a30eb2375111d97e0..88fa8af2b937c02a7402d5c8b2499009d4d0ff21 100644 (file)
@@ -17,7 +17,7 @@
 
 #include <linux/platform_device.h>
 
-#define INT_DMA_LCD                    25
+#define INT_DMA_LCD                    (NR_IRQS_LEGACY + 25)
 
 #define OMAP1_DMA_TOUT_IRQ             (1 << 0)
 #define OMAP_DMA_DROP_IRQ              (1 << 1)
diff --git a/include/linux/once.h b/include/linux/once.h
new file mode 100644 (file)
index 0000000..285f12c
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef _LINUX_ONCE_H
+#define _LINUX_ONCE_H
+
+#include <linux/types.h>
+#include <linux/jump_label.h>
+
+bool __do_once_start(bool *done, unsigned long *flags);
+void __do_once_done(bool *done, struct static_key *once_key,
+                   unsigned long *flags);
+
+/* Call a function exactly once. The idea of DO_ONCE() is to perform
+ * a function call such as initialization of random seeds, etc, only
+ * once, where DO_ONCE() can live in the fast-path. After @func has
+ * been called with the passed arguments, the static key will patch
+ * out the condition into a nop. DO_ONCE() guarantees type safety of
+ * arguments!
+ *
+ * Not that the following is not equivalent ...
+ *
+ *   DO_ONCE(func, arg);
+ *   DO_ONCE(func, arg);
+ *
+ * ... to this version:
+ *
+ *   void foo(void)
+ *   {
+ *     DO_ONCE(func, arg);
+ *   }
+ *
+ *   foo();
+ *   foo();
+ *
+ * In case the one-time invocation could be triggered from multiple
+ * places, then a common helper function must be defined, so that only
+ * a single static key will be placed there!
+ */
+#define DO_ONCE(func, ...)                                                  \
+       ({                                                                   \
+               bool ___ret = false;                                         \
+               static bool ___done = false;                                 \
+               static struct static_key ___once_key = STATIC_KEY_INIT_TRUE; \
+               if (static_key_true(&___once_key)) {                         \
+                       unsigned long ___flags;                              \
+                       ___ret = __do_once_start(&___done, &___flags);       \
+                       if (unlikely(___ret)) {                              \
+                               func(__VA_ARGS__);                           \
+                               __do_once_done(&___done, &___once_key,       \
+                                              &___flags);                   \
+                       }                                                    \
+               }                                                            \
+               ___ret;                                                      \
+       })
+
+#define get_random_once(buf, nbytes)                                        \
+       DO_ONCE(get_random_bytes, (buf), (nbytes))
+
+#endif /* _LINUX_ONCE_H */
index 834c4e52cb2d1cab57ba33a3e87c025bc7e4a7cc..c2fa3ecb0dce57dde9ad4a92a35dc4178d7a159e 100644 (file)
@@ -5,11 +5,12 @@
 #include <linux/rwsem.h>
 #include <linux/percpu.h>
 #include <linux/wait.h>
+#include <linux/rcu_sync.h>
 #include <linux/lockdep.h>
 
 struct percpu_rw_semaphore {
+       struct rcu_sync         rss;
        unsigned int __percpu   *fast_read_ctr;
-       atomic_t                write_ctr;
        struct rw_semaphore     rw_sem;
        atomic_t                slow_read_ctr;
        wait_queue_head_t       write_waitq;
index 092a0e8a479aa19569fa0704e050a853bc38883c..d841d33bcdc9c370742db6408e1f979594d37099 100644 (file)
@@ -140,33 +140,67 @@ struct hw_perf_event {
                };
 #endif
        };
+       /*
+        * If the event is a per task event, this will point to the task in
+        * question. See the comment in perf_event_alloc().
+        */
        struct task_struct              *target;
+
+/*
+ * hw_perf_event::state flags; used to track the PERF_EF_* state.
+ */
+#define PERF_HES_STOPPED       0x01 /* the counter is stopped */
+#define PERF_HES_UPTODATE      0x02 /* event->count up-to-date */
+#define PERF_HES_ARCH          0x04
+
        int                             state;
+
+       /*
+        * The last observed hardware counter value, updated with a
+        * local64_cmpxchg() such that pmu::read() can be called nested.
+        */
        local64_t                       prev_count;
+
+       /*
+        * The period to start the next sample with.
+        */
        u64                             sample_period;
+
+       /*
+        * The period we started this sample with.
+        */
        u64                             last_period;
+
+       /*
+        * However much is left of the current period; note that this is
+        * a full 64bit value and allows for generation of periods longer
+        * than hardware might allow.
+        */
        local64_t                       period_left;
+
+       /*
+        * State for throttling the event, see __perf_event_overflow() and
+        * perf_adjust_freq_unthr_context().
+        */
        u64                             interrupts_seq;
        u64                             interrupts;
 
+       /*
+        * State for freq target events, see __perf_event_overflow() and
+        * perf_adjust_freq_unthr_context().
+        */
        u64                             freq_time_stamp;
        u64                             freq_count_stamp;
 #endif
 };
 
-/*
- * hw_perf_event::state flags
- */
-#define PERF_HES_STOPPED       0x01 /* the counter is stopped */
-#define PERF_HES_UPTODATE      0x02 /* event->count up-to-date */
-#define PERF_HES_ARCH          0x04
-
 struct perf_event;
 
 /*
  * Common implementation detail of pmu::{start,commit,cancel}_txn
  */
-#define PERF_EVENT_TXN 0x1
+#define PERF_PMU_TXN_ADD  0x1          /* txn to add/schedule event on PMU */
+#define PERF_PMU_TXN_READ 0x2          /* txn to read event group from PMU */
 
 /**
  * pmu::capabilities flags
@@ -210,7 +244,19 @@ struct pmu {
 
        /*
         * Try and initialize the event for this PMU.
-        * Should return -ENOENT when the @event doesn't match this PMU.
+        *
+        * Returns:
+        *  -ENOENT     -- @event is not for this PMU
+        *
+        *  -ENODEV     -- @event is for this PMU but PMU not present
+        *  -EBUSY      -- @event is for this PMU but PMU temporarily unavailable
+        *  -EINVAL     -- @event is for this PMU but @event is not valid
+        *  -EOPNOTSUPP -- @event is for this PMU, @event is valid, but not supported
+        *  -EACCESS    -- @event is for this PMU, @event is valid, but no privilidges
+        *
+        *  0           -- @event is for this PMU and valid
+        *
+        * Other error return values are allowed.
         */
        int (*event_init)               (struct perf_event *event);
 
@@ -221,27 +267,61 @@ struct pmu {
        void (*event_mapped)            (struct perf_event *event); /*optional*/
        void (*event_unmapped)          (struct perf_event *event); /*optional*/
 
+       /*
+        * Flags for ->add()/->del()/ ->start()/->stop(). There are
+        * matching hw_perf_event::state flags.
+        */
 #define PERF_EF_START  0x01            /* start the counter when adding    */
 #define PERF_EF_RELOAD 0x02            /* reload the counter when starting */
 #define PERF_EF_UPDATE 0x04            /* update the counter when stopping */
 
        /*
-        * Adds/Removes a counter to/from the PMU, can be done inside
-        * a transaction, see the ->*_txn() methods.
+        * Adds/Removes a counter to/from the PMU, can be done inside a
+        * transaction, see the ->*_txn() methods.
+        *
+        * The add/del callbacks will reserve all hardware resources required
+        * to service the event, this includes any counter constraint
+        * scheduling etc.
+        *
+        * Called with IRQs disabled and the PMU disabled on the CPU the event
+        * is on.
+        *
+        * ->add() called without PERF_EF_START should result in the same state
+        *  as ->add() followed by ->stop().
+        *
+        * ->del() must always PERF_EF_UPDATE stop an event. If it calls
+        *  ->stop() that must deal with already being stopped without
+        *  PERF_EF_UPDATE.
         */
        int  (*add)                     (struct perf_event *event, int flags);
        void (*del)                     (struct perf_event *event, int flags);
 
        /*
-        * Starts/Stops a counter present on the PMU. The PMI handler
-        * should stop the counter when perf_event_overflow() returns
-        * !0. ->start() will be used to continue.
+        * Starts/Stops a counter present on the PMU.
+        *
+        * The PMI handler should stop the counter when perf_event_overflow()
+        * returns !0. ->start() will be used to continue.
+        *
+        * Also used to change the sample period.
+        *
+        * Called with IRQs disabled and the PMU disabled on the CPU the event
+        * is on -- will be called from NMI context with the PMU generates
+        * NMIs.
+        *
+        * ->stop() with PERF_EF_UPDATE will read the counter and update
+        *  period/count values like ->read() would.
+        *
+        * ->start() with PERF_EF_RELOAD will reprogram the the counter
+        *  value, must be preceded by a ->stop() with PERF_EF_UPDATE.
         */
        void (*start)                   (struct perf_event *event, int flags);
        void (*stop)                    (struct perf_event *event, int flags);
 
        /*
         * Updates the counter value of the event.
+        *
+        * For sampling capable PMUs this will also update the software period
+        * hw_perf_event::period_left field.
         */
        void (*read)                    (struct perf_event *event);
 
@@ -252,20 +332,26 @@ struct pmu {
         *
         * Start the transaction, after this ->add() doesn't need to
         * do schedulability tests.
+        *
+        * Optional.
         */
-       void (*start_txn)               (struct pmu *pmu); /* optional */
+       void (*start_txn)               (struct pmu *pmu, unsigned int txn_flags);
        /*
         * If ->start_txn() disabled the ->add() schedulability test
         * then ->commit_txn() is required to perform one. On success
         * the transaction is closed. On error the transaction is kept
         * open until ->cancel_txn() is called.
+        *
+        * Optional.
         */
-       int  (*commit_txn)              (struct pmu *pmu); /* optional */
+       int  (*commit_txn)              (struct pmu *pmu);
        /*
         * Will cancel the transaction, assumes ->del() is called
         * for each successful ->add() during the transaction.
+        *
+        * Optional.
         */
-       void (*cancel_txn)              (struct pmu *pmu); /* optional */
+       void (*cancel_txn)              (struct pmu *pmu);
 
        /*
         * Will return the value for perf_event_mmap_page::index for this event,
index 4a4e3a09233762ba0e2d3065a6fa951e352a2869..05fde31b6dc6dbe2f97356cea09a961dc1cd3af0 100644 (file)
@@ -213,7 +213,9 @@ static inline struct mii_bus *devm_mdiobus_alloc(struct device *dev)
 void devm_mdiobus_free(struct device *dev, struct mii_bus *bus);
 struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
 int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
+int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum);
 int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
+int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val);
 
 
 #define PHY_INTERRUPT_DISABLED 0x0
@@ -798,6 +800,7 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd);
 int phy_start_interrupts(struct phy_device *phydev);
 void phy_print_status(struct phy_device *phydev);
 void phy_device_free(struct phy_device *phydev);
+int phy_set_max_speed(struct phy_device *phydev, u32 max_speed);
 
 int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
                       int (*run)(struct phy_device *));
index 281cb91ddcf54c3a5275acbbc30abd9f0e9d11b1..05082e407c4a7c242250b05a08fbf028447e6ad6 100644 (file)
  * struct dev_pin_info - pin state container for devices
  * @p: pinctrl handle for the containing device
  * @default_state: the default state for the handle, if found
+ * @init_state: the state at probe time, if found
+ * @sleep_state: the state at suspend time, if found
+ * @idle_state: the state at idle (runtime suspend) time, if found
  */
 struct dev_pin_info {
        struct pinctrl *p;
        struct pinctrl_state *default_state;
+       struct pinctrl_state *init_state;
 #ifdef CONFIG_PM
        struct pinctrl_state *sleep_state;
        struct pinctrl_state *idle_state;
@@ -35,6 +39,7 @@ struct dev_pin_info {
 };
 
 extern int pinctrl_bind_pins(struct device *dev);
+extern int pinctrl_init_done(struct device *dev);
 
 #else
 
@@ -45,5 +50,10 @@ static inline int pinctrl_bind_pins(struct device *dev)
        return 0;
 }
 
+static inline int pinctrl_init_done(struct device *dev)
+{
+       return 0;
+}
+
 #endif /* CONFIG_PINCTRL */
 #endif /* PINCTRL_DEVINFO_H */
index fe65962b264ff6e14eb6b0a5c322bc11f055f56e..d921afd5f10907c8d8058c0136463f6fcf54f345 100644 (file)
 
 /**
  * enum pin_config_param - possible pin configuration parameters
+ * @PIN_CONFIG_BIAS_BUS_HOLD: the pin will be set to weakly latch so that it
+ *     weakly drives the last value on a tristate bus, also known as a "bus
+ *     holder", "bus keeper" or "repeater". This allows another device on the
+ *     bus to change the value by driving the bus high or low and switching to
+ *     tristate. The argument is ignored.
  * @PIN_CONFIG_BIAS_DISABLE: disable any pin bias on the pin, a
  *     transition from say pull-up to pull-down implies that you disable
  *     pull-up in the process, this setting disables all biasing.
  *     if for example some other pin is going to drive the signal connected
  *     to it for a while. Pins used for input are usually always high
  *     impedance.
- * @PIN_CONFIG_BIAS_BUS_HOLD: the pin will be set to weakly latch so that it
- *     weakly drives the last value on a tristate bus, also known as a "bus
- *     holder", "bus keeper" or "repeater". This allows another device on the
- *     bus to change the value by driving the bus high or low and switching to
- *     tristate. The argument is ignored.
- * @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high
- *     impedance to VDD). If the argument is != 0 pull-up is enabled,
- *     if it is 0, pull-up is total, i.e. the pin is connected to VDD.
  * @PIN_CONFIG_BIAS_PULL_DOWN: the pin will be pulled down (usually with high
  *     impedance to GROUND). If the argument is != 0 pull-down is enabled,
  *     if it is 0, pull-down is total, i.e. the pin is connected to GROUND.
  *     If the argument is != 0 pull up/down is enabled, if it is 0, the
  *     configuration is ignored. The proper way to disable it is to use
  *     @PIN_CONFIG_BIAS_DISABLE.
- * @PIN_CONFIG_DRIVE_PUSH_PULL: the pin will be driven actively high and
- *     low, this is the most typical case and is typically achieved with two
- *     active transistors on the output. Setting this config will enable
- *     push-pull mode, the argument is ignored.
+ * @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high
+ *     impedance to VDD). If the argument is != 0 pull-up is enabled,
+ *     if it is 0, pull-up is total, i.e. the pin is connected to VDD.
  * @PIN_CONFIG_DRIVE_OPEN_DRAIN: the pin will be driven with open drain (open
  *     collector) which means it is usually wired with other output ports
  *     which are then pulled up with an external resistor. Setting this
  * @PIN_CONFIG_DRIVE_OPEN_SOURCE: the pin will be driven with open source
  *     (open emitter). Setting this config will enable open source mode, the
  *     argument is ignored.
+ * @PIN_CONFIG_DRIVE_PUSH_PULL: the pin will be driven actively high and
+ *     low, this is the most typical case and is typically achieved with two
+ *     active transistors on the output. Setting this config will enable
+ *     push-pull mode, the argument is ignored.
  * @PIN_CONFIG_DRIVE_STRENGTH: the pin will sink or source at most the current
  *     passed as argument. The argument is in mA.
+ * @PIN_CONFIG_INPUT_DEBOUNCE: this will configure the pin to debounce mode,
+ *     which means it will wait for signals to settle when reading inputs. The
+ *     argument gives the debounce time in usecs. Setting the
+ *     argument to zero turns debouncing off.
  * @PIN_CONFIG_INPUT_ENABLE: enable the pin's input.  Note that this does not
  *     affect the pin's ability to drive output.  1 enables input, 0 disables
  *     input.
- * @PIN_CONFIG_INPUT_SCHMITT_ENABLE: control schmitt-trigger mode on the pin.
- *      If the argument != 0, schmitt-trigger mode is enabled. If it's 0,
- *      schmitt-trigger mode is disabled.
  * @PIN_CONFIG_INPUT_SCHMITT: this will configure an input pin to run in
  *     schmitt-trigger mode. If the schmitt-trigger has adjustable hysteresis,
  *     the threshold value is given on a custom format as argument when
  *     setting pins to this mode.
- * @PIN_CONFIG_INPUT_DEBOUNCE: this will configure the pin to debounce mode,
- *     which means it will wait for signals to settle when reading inputs. The
- *     argument gives the debounce time in usecs. Setting the
- *     argument to zero turns debouncing off.
- * @PIN_CONFIG_POWER_SOURCE: if the pin can select between different power
- *     supplies, the argument to this parameter (on a custom format) tells
- *     the driver which alternative power source to use.
- * @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to
- *     this parameter (on a custom format) tells the driver which alternative
- *     slew rate to use.
+ * @PIN_CONFIG_INPUT_SCHMITT_ENABLE: control schmitt-trigger mode on the pin.
+ *      If the argument != 0, schmitt-trigger mode is enabled. If it's 0,
+ *      schmitt-trigger mode is disabled.
  * @PIN_CONFIG_LOW_POWER_MODE: this will configure the pin for low power
  *     operation, if several modes of operation are supported these can be
  *     passed in the argument on a custom form, else just use argument 1
  *     1 to indicate high level, argument 0 to indicate low level. (Please
  *     see Documentation/pinctrl.txt, section "GPIO mode pitfalls" for a
  *     discussion around this parameter.)
+ * @PIN_CONFIG_POWER_SOURCE: if the pin can select between different power
+ *     supplies, the argument to this parameter (on a custom format) tells
+ *     the driver which alternative power source to use.
+ * @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to
+ *     this parameter (on a custom format) tells the driver which alternative
+ *     slew rate to use.
  * @PIN_CONFIG_END: this is the last enumerator for pin configurations, if
  *     you need to pass in custom configurations to the pin controller, use
  *     PIN_CONFIG_END+1 as the base offset.
  */
 enum pin_config_param {
+       PIN_CONFIG_BIAS_BUS_HOLD,
        PIN_CONFIG_BIAS_DISABLE,
        PIN_CONFIG_BIAS_HIGH_IMPEDANCE,
-       PIN_CONFIG_BIAS_BUS_HOLD,
-       PIN_CONFIG_BIAS_PULL_UP,
        PIN_CONFIG_BIAS_PULL_DOWN,
        PIN_CONFIG_BIAS_PULL_PIN_DEFAULT,
-       PIN_CONFIG_DRIVE_PUSH_PULL,
+       PIN_CONFIG_BIAS_PULL_UP,
        PIN_CONFIG_DRIVE_OPEN_DRAIN,
        PIN_CONFIG_DRIVE_OPEN_SOURCE,
+       PIN_CONFIG_DRIVE_PUSH_PULL,
        PIN_CONFIG_DRIVE_STRENGTH,
+       PIN_CONFIG_INPUT_DEBOUNCE,
        PIN_CONFIG_INPUT_ENABLE,
-       PIN_CONFIG_INPUT_SCHMITT_ENABLE,
        PIN_CONFIG_INPUT_SCHMITT,
-       PIN_CONFIG_INPUT_DEBOUNCE,
-       PIN_CONFIG_POWER_SOURCE,
-       PIN_CONFIG_SLEW_RATE,
+       PIN_CONFIG_INPUT_SCHMITT_ENABLE,
        PIN_CONFIG_LOW_POWER_MODE,
        PIN_CONFIG_OUTPUT,
+       PIN_CONFIG_POWER_SOURCE,
+       PIN_CONFIG_SLEW_RATE,
        PIN_CONFIG_END = 0x7FFF,
 };
 
index b5919f8e6d1ad87c618047cdcc7aa7a06324b354..23073519339f35e5bf42ccd72764e0e58cd67a6e 100644 (file)
@@ -9,6 +9,13 @@
  *     hogs to configure muxing and pins at boot, and also as a state
  *     to go into when returning from sleep and idle in
  *     .pm_runtime_resume() or ordinary .resume() for example.
+ * @PINCTRL_STATE_INIT: normally the pinctrl will be set to "default"
+ *     before the driver's probe() function is called.  There are some
+ *     drivers where that is not appropriate becausing doing so would
+ *     glitch the pins.  In those cases you can add an "init" pinctrl
+ *     which is the state of the pins before drive probe.  After probe
+ *     if the pins are still in "init" state they'll be moved to
+ *     "default".
  * @PINCTRL_STATE_IDLE: the state the pinctrl handle shall be put into
  *     when the pins are idle. This is a state where the system is relaxed
  *     but not fully sleeping - some power may be on but clocks gated for
@@ -20,5 +27,6 @@
  *     ordinary .suspend() function.
  */
 #define PINCTRL_STATE_DEFAULT "default"
+#define PINCTRL_STATE_INIT "init"
 #define PINCTRL_STATE_IDLE "idle"
 #define PINCTRL_STATE_SLEEP "sleep"
index 527a85c6192443a50ca8846f24fe40d7e726eca9..c121ddf74f7ff403696ecfe9521b84ab71bb4b0c 100644 (file)
@@ -74,11 +74,6 @@ struct atmel_uart_data {
        struct serial_rs485     rs485;          /* rs485 settings */
 };
 
-/* CAN */
-struct at91_can_data {
-       void (*transceiver_switch)(int on);
-};
-
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
index d2be19a51acde32102710cdf0db536fe4d364f01..3c85a735c380539e71c4094f2dd48ef33d3e6c6f 100644 (file)
@@ -40,6 +40,7 @@ struct netxbig_led {
        int             mode_addr;
        int             *mode_val;
        int             bright_addr;
+       int             bright_max;
 };
 
 struct netxbig_led_platform_data {
index ac91707dabcbb2e984938d41ccb985ec3f8e44a7..a6f9d633f5bedf8ca1c3ca302de283edb18bfaf2 100644 (file)
@@ -35,6 +35,14 @@ struct nfcmrvl_platform_data {
        unsigned int flow_control;
        /* Tell if firmware supports break control for power management */
        unsigned int break_control;
+
+
+       /*
+        * I2C specific
+        */
+
+       unsigned int irq;
+       unsigned int irq_polarity;
 };
 
 #endif /* _NFCMRVL_PTF_H_ */
index d9d400a297bde81e719ea61f30efedfdecc595d3..f6494b347c06a8988c6b745c29ffa1a56b44ea10 100644 (file)
@@ -24,6 +24,8 @@
 struct st_nci_nfc_platform_data {
        unsigned int gpio_reset;
        unsigned int irq_polarity;
+       bool is_ese_present;
+       bool is_uicc_present;
 };
 
 #endif /* _ST_NCI_H_ */
index 1d2cd21242e8a75c1ac6aeb191d9964d2f3388ea..54bf1484d41f0333cc860c84307d23eda76477e5 100644 (file)
@@ -48,9 +48,9 @@ struct pps_source_info {
 
 struct pps_event_time {
 #ifdef CONFIG_NTP_PPS
-       struct timespec ts_raw;
+       struct timespec64 ts_raw;
 #endif /* CONFIG_NTP_PPS */
-       struct timespec ts_real;
+       struct timespec64 ts_real;
 };
 
 /* The main struct */
@@ -105,7 +105,7 @@ extern void pps_event(struct pps_device *pps,
 struct pps_device *pps_lookup_dev(void const *cookie);
 
 static inline void timespec_to_pps_ktime(struct pps_ktime *kt,
-               struct timespec ts)
+               struct timespec64 ts)
 {
        kt->sec = ts.tv_sec;
        kt->nsec = ts.tv_nsec;
@@ -115,24 +115,24 @@ static inline void timespec_to_pps_ktime(struct pps_ktime *kt,
 
 static inline void pps_get_ts(struct pps_event_time *ts)
 {
-       getnstime_raw_and_real(&ts->ts_raw, &ts->ts_real);
+       ktime_get_raw_and_real_ts64(&ts->ts_raw, &ts->ts_real);
 }
 
 #else /* CONFIG_NTP_PPS */
 
 static inline void pps_get_ts(struct pps_event_time *ts)
 {
-       getnstimeofday(&ts->ts_real);
+       ktime_get_real_ts64(&ts->ts_real);
 }
 
 #endif /* CONFIG_NTP_PPS */
 
 /* Subtract known time delay from PPS event time(s) */
-static inline void pps_sub_ts(struct pps_event_time *ts, struct timespec delta)
+static inline void pps_sub_ts(struct pps_event_time *ts, struct timespec64 delta)
 {
-       ts->ts_real = timespec_sub(ts->ts_real, delta);
+       ts->ts_real = timespec64_sub(ts->ts_real, delta);
 #ifdef CONFIG_NTP_PPS
-       ts->ts_raw = timespec_sub(ts->ts_raw, delta);
+       ts->ts_raw = timespec64_sub(ts->ts_raw, delta);
 #endif
 }
 
index bea8dd8ff5e026f8fc3e3bc446af7aea7ec891e2..75e4e30677f1254fb2c9f076f71fc4e2df1fb9be 100644 (file)
@@ -26,7 +26,6 @@
  *         SOFTIRQ_MASK:       0x0000ff00
  *         HARDIRQ_MASK:       0x000f0000
  *             NMI_MASK:       0x00100000
- *       PREEMPT_ACTIVE:       0x00200000
  * PREEMPT_NEED_RESCHED:       0x80000000
  */
 #define PREEMPT_BITS   8
 
 #define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET)
 
-#define PREEMPT_ACTIVE_BITS    1
-#define PREEMPT_ACTIVE_SHIFT   (NMI_SHIFT + NMI_BITS)
-#define PREEMPT_ACTIVE (__IRQ_MASK(PREEMPT_ACTIVE_BITS) << PREEMPT_ACTIVE_SHIFT)
-
 /* We use the MSB mostly because its available */
 #define PREEMPT_NEED_RESCHED   0x80000000
 
  * Check whether we were atomic before we did preempt_disable():
  * (used by the scheduler)
  */
-#define in_atomic_preempt_off() \
-               ((preempt_count() & ~PREEMPT_ACTIVE) != PREEMPT_DISABLE_OFFSET)
+#define in_atomic_preempt_off() (preempt_count() != PREEMPT_DISABLE_OFFSET)
 
 #if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_PREEMPT_TRACER)
 extern void preempt_count_add(int val);
@@ -146,18 +140,6 @@ extern void preempt_count_sub(int val);
 #define preempt_count_inc() preempt_count_add(1)
 #define preempt_count_dec() preempt_count_sub(1)
 
-#define preempt_active_enter() \
-do { \
-       preempt_count_add(PREEMPT_ACTIVE + PREEMPT_DISABLE_OFFSET); \
-       barrier(); \
-} while (0)
-
-#define preempt_active_exit() \
-do { \
-       barrier(); \
-       preempt_count_sub(PREEMPT_ACTIVE + PREEMPT_DISABLE_OFFSET); \
-} while (0)
-
 #ifdef CONFIG_PREEMPT_COUNT
 
 #define preempt_disable() \
index 159c987b1853106fc13e36391f8534383c947d46..a079656b614cd725519249961b16fac739b8a888 100644 (file)
@@ -32,9 +32,9 @@
 #define PTP_CLASS_VMASK 0x0f /* max protocol version is 15 */
 #define PTP_CLASS_IPV4  0x10 /* event in an IPV4 UDP packet */
 #define PTP_CLASS_IPV6  0x20 /* event in an IPV6 UDP packet */
-#define PTP_CLASS_L2    0x30 /* event in a L2 packet */
-#define PTP_CLASS_PMASK 0x30 /* mask for the packet type field */
-#define PTP_CLASS_VLAN  0x40 /* event in a VLAN tagged packet */
+#define PTP_CLASS_L2    0x40 /* event in a L2 packet */
+#define PTP_CLASS_PMASK        0x70 /* mask for the packet type field */
+#define PTP_CLASS_VLAN 0x80 /* event in a VLAN tagged packet */
 
 #define PTP_CLASS_V1_IPV4 (PTP_CLASS_V1 | PTP_CLASS_IPV4)
 #define PTP_CLASS_V1_IPV6 (PTP_CLASS_V1 | PTP_CLASS_IPV6) /* probably DNE */
@@ -42,6 +42,7 @@
 #define PTP_CLASS_V2_IPV6 (PTP_CLASS_V2 | PTP_CLASS_IPV6)
 #define PTP_CLASS_V2_L2   (PTP_CLASS_V2 | PTP_CLASS_L2)
 #define PTP_CLASS_V2_VLAN (PTP_CLASS_V2 | PTP_CLASS_VLAN)
+#define PTP_CLASS_L4      (PTP_CLASS_IPV4 | PTP_CLASS_IPV6)
 
 #define PTP_EV_PORT 319
 #define PTP_GEN_BIT 0x08 /* indicates general message, if set in message type */
diff --git a/include/linux/qed/common_hsi.h b/include/linux/qed/common_hsi.h
new file mode 100644 (file)
index 0000000..6a43476
--- /dev/null
@@ -0,0 +1,607 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef __COMMON_HSI__
+#define __COMMON_HSI__
+
+#define FW_MAJOR_VERSION       8
+#define FW_MINOR_VERSION       4
+#define FW_REVISION_VERSION    2
+#define FW_ENGINEERING_VERSION 0
+
+/***********************/
+/* COMMON HW CONSTANTS */
+/***********************/
+
+/* PCI functions */
+#define MAX_NUM_PORTS_K2       (4)
+#define MAX_NUM_PORTS_BB       (2)
+#define MAX_NUM_PORTS          (MAX_NUM_PORTS_K2)
+
+#define MAX_NUM_PFS_K2 (16)
+#define MAX_NUM_PFS_BB (8)
+#define MAX_NUM_PFS    (MAX_NUM_PFS_K2)
+#define MAX_NUM_OF_PFS_IN_CHIP (16) /* On both engines */
+
+#define MAX_NUM_VFS_K2 (192)
+#define MAX_NUM_VFS_BB (120)
+#define MAX_NUM_VFS    (MAX_NUM_VFS_K2)
+
+#define MAX_NUM_FUNCTIONS_BB   (MAX_NUM_PFS_BB + MAX_NUM_VFS_BB)
+#define MAX_NUM_FUNCTIONS      (MAX_NUM_PFS + MAX_NUM_VFS)
+
+#define MAX_FUNCTION_NUMBER_BB (MAX_NUM_PFS + MAX_NUM_VFS_BB)
+#define MAX_FUNCTION_NUMBER    (MAX_NUM_PFS + MAX_NUM_VFS)
+
+#define MAX_NUM_VPORTS_K2      (208)
+#define MAX_NUM_VPORTS_BB      (160)
+#define MAX_NUM_VPORTS         (MAX_NUM_VPORTS_K2)
+
+#define MAX_NUM_L2_QUEUES_K2   (320)
+#define MAX_NUM_L2_QUEUES_BB   (256)
+#define MAX_NUM_L2_QUEUES      (MAX_NUM_L2_QUEUES_K2)
+
+/* Traffic classes in network-facing blocks (PBF, BTB, NIG, BRB, PRS and QM) */
+#define NUM_PHYS_TCS_4PORT_K2  (4)
+#define NUM_OF_PHYS_TCS                (8)
+
+#define NUM_TCS_4PORT_K2       (NUM_PHYS_TCS_4PORT_K2 + 1)
+#define NUM_OF_TCS             (NUM_OF_PHYS_TCS + 1)
+
+#define LB_TC                  (NUM_OF_PHYS_TCS)
+
+/* Num of possible traffic priority values */
+#define NUM_OF_PRIO            (8)
+
+#define MAX_NUM_VOQS_K2                (NUM_TCS_4PORT_K2 * MAX_NUM_PORTS_K2)
+#define MAX_NUM_VOQS_BB                (NUM_OF_TCS * MAX_NUM_PORTS_BB)
+#define MAX_NUM_VOQS           (MAX_NUM_VOQS_K2)
+#define MAX_PHYS_VOQS          (NUM_OF_PHYS_TCS * MAX_NUM_PORTS_BB)
+
+/* CIDs */
+#define NUM_OF_CONNECTION_TYPES        (8)
+#define NUM_OF_LCIDS           (320)
+#define NUM_OF_LTIDS           (320)
+
+/*****************/
+/* CDU CONSTANTS */
+/*****************/
+
+#define CDU_SEG_TYPE_OFFSET_REG_TYPE_SHIFT              (17)
+#define CDU_SEG_TYPE_OFFSET_REG_OFFSET_MASK             (0x1ffff)
+
+/*****************/
+/* DQ CONSTANTS  */
+/*****************/
+
+/* DEMS */
+#define DQ_DEMS_LEGACY                 0
+
+/* XCM agg val selection */
+#define DQ_XCM_AGG_VAL_SEL_WORD2  0
+#define DQ_XCM_AGG_VAL_SEL_WORD3  1
+#define DQ_XCM_AGG_VAL_SEL_WORD4  2
+#define DQ_XCM_AGG_VAL_SEL_WORD5  3
+#define DQ_XCM_AGG_VAL_SEL_REG3   4
+#define DQ_XCM_AGG_VAL_SEL_REG4   5
+#define DQ_XCM_AGG_VAL_SEL_REG5   6
+#define DQ_XCM_AGG_VAL_SEL_REG6   7
+
+/* XCM agg val selection */
+#define DQ_XCM_ETH_EDPM_NUM_BDS_CMD \
+       DQ_XCM_AGG_VAL_SEL_WORD2
+#define DQ_XCM_ETH_TX_BD_CONS_CMD \
+       DQ_XCM_AGG_VAL_SEL_WORD3
+#define DQ_XCM_CORE_TX_BD_CONS_CMD \
+       DQ_XCM_AGG_VAL_SEL_WORD3
+#define DQ_XCM_ETH_TX_BD_PROD_CMD \
+       DQ_XCM_AGG_VAL_SEL_WORD4
+#define DQ_XCM_CORE_TX_BD_PROD_CMD \
+       DQ_XCM_AGG_VAL_SEL_WORD4
+#define DQ_XCM_CORE_SPQ_PROD_CMD \
+       DQ_XCM_AGG_VAL_SEL_WORD4
+#define DQ_XCM_ETH_GO_TO_BD_CONS_CMD            DQ_XCM_AGG_VAL_SEL_WORD5
+
+/* XCM agg counter flag selection */
+#define DQ_XCM_AGG_FLG_SHIFT_BIT14  0
+#define DQ_XCM_AGG_FLG_SHIFT_BIT15  1
+#define DQ_XCM_AGG_FLG_SHIFT_CF12   2
+#define DQ_XCM_AGG_FLG_SHIFT_CF13   3
+#define DQ_XCM_AGG_FLG_SHIFT_CF18   4
+#define DQ_XCM_AGG_FLG_SHIFT_CF19   5
+#define DQ_XCM_AGG_FLG_SHIFT_CF22   6
+#define DQ_XCM_AGG_FLG_SHIFT_CF23   7
+
+/* XCM agg counter flag selection */
+#define DQ_XCM_ETH_DQ_CF_CMD           (1 << \
+                                       DQ_XCM_AGG_FLG_SHIFT_CF18)
+#define DQ_XCM_CORE_DQ_CF_CMD          (1 << \
+                                       DQ_XCM_AGG_FLG_SHIFT_CF18)
+#define DQ_XCM_ETH_TERMINATE_CMD       (1 << \
+                                       DQ_XCM_AGG_FLG_SHIFT_CF19)
+#define DQ_XCM_CORE_TERMINATE_CMD      (1 << \
+                                       DQ_XCM_AGG_FLG_SHIFT_CF19)
+#define DQ_XCM_ETH_SLOW_PATH_CMD       (1 << \
+                                       DQ_XCM_AGG_FLG_SHIFT_CF22)
+#define DQ_XCM_CORE_SLOW_PATH_CMD      (1 << \
+                                       DQ_XCM_AGG_FLG_SHIFT_CF22)
+#define DQ_XCM_ETH_TPH_EN_CMD          (1 << \
+                                       DQ_XCM_AGG_FLG_SHIFT_CF23)
+
+/*****************/
+/* QM CONSTANTS  */
+/*****************/
+
+/* number of TX queues in the QM */
+#define MAX_QM_TX_QUEUES_K2    512
+#define MAX_QM_TX_QUEUES_BB    448
+#define MAX_QM_TX_QUEUES       MAX_QM_TX_QUEUES_K2
+
+/* number of Other queues in the QM */
+#define MAX_QM_OTHER_QUEUES_BB 64
+#define MAX_QM_OTHER_QUEUES_K2 128
+#define MAX_QM_OTHER_QUEUES    MAX_QM_OTHER_QUEUES_K2
+
+/* number of queues in a PF queue group */
+#define QM_PF_QUEUE_GROUP_SIZE 8
+
+/* base number of Tx PQs in the CM PQ representation.
+ * should be used when storing PQ IDs in CM PQ registers and context
+ */
+#define CM_TX_PQ_BASE  0x200
+
+/* QM registers data */
+#define QM_LINE_CRD_REG_WIDTH          16
+#define QM_LINE_CRD_REG_SIGN_BIT       (1 << (QM_LINE_CRD_REG_WIDTH - 1))
+#define QM_BYTE_CRD_REG_WIDTH          24
+#define QM_BYTE_CRD_REG_SIGN_BIT       (1 << (QM_BYTE_CRD_REG_WIDTH - 1))
+#define QM_WFQ_CRD_REG_WIDTH           32
+#define QM_WFQ_CRD_REG_SIGN_BIT                (1 << (QM_WFQ_CRD_REG_WIDTH - 1))
+#define QM_RL_CRD_REG_WIDTH            32
+#define QM_RL_CRD_REG_SIGN_BIT         (1 << (QM_RL_CRD_REG_WIDTH - 1))
+
+/*****************/
+/* CAU CONSTANTS */
+/*****************/
+
+#define CAU_FSM_ETH_RX  0
+#define CAU_FSM_ETH_TX  1
+
+/* Number of Protocol Indices per Status Block */
+#define PIS_PER_SB    12
+
+#define CAU_HC_STOPPED_STATE   3
+#define CAU_HC_DISABLE_STATE   4
+#define CAU_HC_ENABLE_STATE    0
+
+/*****************/
+/* IGU CONSTANTS */
+/*****************/
+
+#define MAX_SB_PER_PATH_K2     (368)
+#define MAX_SB_PER_PATH_BB     (288)
+#define MAX_TOT_SB_PER_PATH \
+       MAX_SB_PER_PATH_K2
+
+#define MAX_SB_PER_PF_MIMD     129
+#define MAX_SB_PER_PF_SIMD     64
+#define MAX_SB_PER_VF          64
+
+/* Memory addresses on the BAR for the IGU Sub Block */
+#define IGU_MEM_BASE                   0x0000
+
+#define IGU_MEM_MSIX_BASE              0x0000
+#define IGU_MEM_MSIX_UPPER             0x0101
+#define IGU_MEM_MSIX_RESERVED_UPPER    0x01ff
+
+#define IGU_MEM_PBA_MSIX_BASE          0x0200
+#define IGU_MEM_PBA_MSIX_UPPER         0x0202
+#define IGU_MEM_PBA_MSIX_RESERVED_UPPER        0x03ff
+
+#define IGU_CMD_INT_ACK_BASE           0x0400
+#define IGU_CMD_INT_ACK_UPPER          (IGU_CMD_INT_ACK_BASE + \
+                                        MAX_TOT_SB_PER_PATH -  \
+                                        1)
+#define IGU_CMD_INT_ACK_RESERVED_UPPER 0x05ff
+
+#define IGU_CMD_ATTN_BIT_UPD_UPPER     0x05f0
+#define IGU_CMD_ATTN_BIT_SET_UPPER     0x05f1
+#define IGU_CMD_ATTN_BIT_CLR_UPPER     0x05f2
+
+#define IGU_REG_SISR_MDPC_WMASK_UPPER          0x05f3
+#define IGU_REG_SISR_MDPC_WMASK_LSB_UPPER      0x05f4
+#define IGU_REG_SISR_MDPC_WMASK_MSB_UPPER      0x05f5
+#define IGU_REG_SISR_MDPC_WOMASK_UPPER         0x05f6
+
+#define IGU_CMD_PROD_UPD_BASE                  0x0600
+#define IGU_CMD_PROD_UPD_UPPER                 (IGU_CMD_PROD_UPD_BASE +\
+                                                MAX_TOT_SB_PER_PATH - \
+                                                1)
+#define IGU_CMD_PROD_UPD_RESERVED_UPPER                0x07ff
+
+/*****************/
+/* PXP CONSTANTS */
+/*****************/
+
+/* PTT and GTT */
+#define PXP_NUM_PF_WINDOWS             12
+#define PXP_PER_PF_ENTRY_SIZE          8
+#define PXP_NUM_GLOBAL_WINDOWS         243
+#define PXP_GLOBAL_ENTRY_SIZE          4
+#define PXP_ADMIN_WINDOW_ALLOWED_LENGTH        4
+#define PXP_PF_WINDOW_ADMIN_START      0
+#define PXP_PF_WINDOW_ADMIN_LENGTH     0x1000
+#define PXP_PF_WINDOW_ADMIN_END                (PXP_PF_WINDOW_ADMIN_START + \
+                                        PXP_PF_WINDOW_ADMIN_LENGTH - 1)
+#define PXP_PF_WINDOW_ADMIN_PER_PF_START       0
+#define PXP_PF_WINDOW_ADMIN_PER_PF_LENGTH      (PXP_NUM_PF_WINDOWS * \
+                                                PXP_PER_PF_ENTRY_SIZE)
+#define PXP_PF_WINDOW_ADMIN_PER_PF_END (PXP_PF_WINDOW_ADMIN_PER_PF_START + \
+                                        PXP_PF_WINDOW_ADMIN_PER_PF_LENGTH - 1)
+#define PXP_PF_WINDOW_ADMIN_GLOBAL_START       0x200
+#define PXP_PF_WINDOW_ADMIN_GLOBAL_LENGTH      (PXP_NUM_GLOBAL_WINDOWS * \
+                                                PXP_GLOBAL_ENTRY_SIZE)
+#define PXP_PF_WINDOW_ADMIN_GLOBAL_END \
+               (PXP_PF_WINDOW_ADMIN_GLOBAL_START + \
+                PXP_PF_WINDOW_ADMIN_GLOBAL_LENGTH - 1)
+#define PXP_PF_GLOBAL_PRETEND_ADDR     0x1f0
+#define PXP_PF_ME_OPAQUE_MASK_ADDR     0xf4
+#define PXP_PF_ME_OPAQUE_ADDR          0x1f8
+#define PXP_PF_ME_CONCRETE_ADDR                0x1fc
+
+#define PXP_EXTERNAL_BAR_PF_WINDOW_START       0x1000
+#define PXP_EXTERNAL_BAR_PF_WINDOW_NUM         PXP_NUM_PF_WINDOWS
+#define PXP_EXTERNAL_BAR_PF_WINDOW_SINGLE_SIZE 0x1000
+#define PXP_EXTERNAL_BAR_PF_WINDOW_LENGTH \
+       (PXP_EXTERNAL_BAR_PF_WINDOW_NUM * \
+        PXP_EXTERNAL_BAR_PF_WINDOW_SINGLE_SIZE)
+#define PXP_EXTERNAL_BAR_PF_WINDOW_END \
+       (PXP_EXTERNAL_BAR_PF_WINDOW_START + \
+        PXP_EXTERNAL_BAR_PF_WINDOW_LENGTH - 1)
+
+#define PXP_EXTERNAL_BAR_GLOBAL_WINDOW_START \
+       (PXP_EXTERNAL_BAR_PF_WINDOW_END + 1)
+#define PXP_EXTERNAL_BAR_GLOBAL_WINDOW_NUM             PXP_NUM_GLOBAL_WINDOWS
+#define PXP_EXTERNAL_BAR_GLOBAL_WINDOW_SINGLE_SIZE     0x1000
+#define PXP_EXTERNAL_BAR_GLOBAL_WINDOW_LENGTH \
+       (PXP_EXTERNAL_BAR_GLOBAL_WINDOW_NUM * \
+        PXP_EXTERNAL_BAR_GLOBAL_WINDOW_SINGLE_SIZE)
+#define PXP_EXTERNAL_BAR_GLOBAL_WINDOW_END \
+       (PXP_EXTERNAL_BAR_GLOBAL_WINDOW_START + \
+        PXP_EXTERNAL_BAR_GLOBAL_WINDOW_LENGTH - 1)
+
+#define PXP_ILT_PAGE_SIZE_NUM_BITS_MIN 12
+#define PXP_ILT_BLOCK_FACTOR_MULTIPLIER        1024
+
+/* ILT Records */
+#define PXP_NUM_ILT_RECORDS_BB 7600
+#define PXP_NUM_ILT_RECORDS_K2 11000
+#define MAX_NUM_ILT_RECORDS MAX(PXP_NUM_ILT_RECORDS_BB, PXP_NUM_ILT_RECORDS_K2)
+
+/******************/
+/* PBF CONSTANTS  */
+/******************/
+
+/* Number of PBF command queue lines. Each line is 32B. */
+#define PBF_MAX_CMD_LINES 3328
+
+/* Number of BTB blocks. Each block is 256B. */
+#define BTB_MAX_BLOCKS 1440
+
+/*****************/
+/* PRS CONSTANTS */
+/*****************/
+
+/* Async data KCQ CQE */
+struct async_data {
+       __le32  cid;
+       __le16  itid;
+       u8      error_code;
+       u8      fw_debug_param;
+};
+
+struct regpair {
+       __le32  lo;
+       __le32  hi;
+};
+
+/* Event Data Union */
+union event_ring_data {
+       u8                              bytes[8];
+       struct async_data               async_info;
+};
+
+/* Event Ring Entry */
+struct event_ring_entry {
+       u8                      protocol_id;
+       u8                      opcode;
+       __le16                  reserved0;
+       __le16                  echo;
+       u8                      fw_return_code;
+       u8                      flags;
+#define EVENT_RING_ENTRY_ASYNC_MASK      0x1
+#define EVENT_RING_ENTRY_ASYNC_SHIFT     0
+#define EVENT_RING_ENTRY_RESERVED1_MASK  0x7F
+#define EVENT_RING_ENTRY_RESERVED1_SHIFT 1
+       union event_ring_data   data;
+};
+
+/* Multi function mode */
+enum mf_mode {
+       SF,
+       MF_OVLAN,
+       MF_NPAR,
+       MAX_MF_MODE
+};
+
+/* Per-protocol connection types */
+enum protocol_type {
+       PROTOCOLID_RESERVED1,
+       PROTOCOLID_RESERVED2,
+       PROTOCOLID_RESERVED3,
+       PROTOCOLID_CORE,
+       PROTOCOLID_ETH,
+       PROTOCOLID_RESERVED4,
+       PROTOCOLID_RESERVED5,
+       PROTOCOLID_PREROCE,
+       PROTOCOLID_COMMON,
+       PROTOCOLID_RESERVED6,
+       MAX_PROTOCOL_TYPE
+};
+
+/* status block structure */
+struct cau_pi_entry {
+       u32 prod;
+#define CAU_PI_ENTRY_PROD_VAL_MASK    0xFFFF
+#define CAU_PI_ENTRY_PROD_VAL_SHIFT   0
+#define CAU_PI_ENTRY_PI_TIMESET_MASK  0x7F
+#define CAU_PI_ENTRY_PI_TIMESET_SHIFT 16
+#define CAU_PI_ENTRY_FSM_SEL_MASK     0x1
+#define CAU_PI_ENTRY_FSM_SEL_SHIFT    23
+#define CAU_PI_ENTRY_RESERVED_MASK    0xFF
+#define CAU_PI_ENTRY_RESERVED_SHIFT   24
+};
+
+/* status block structure */
+struct cau_sb_entry {
+       u32 data;
+#define CAU_SB_ENTRY_SB_PROD_MASK      0xFFFFFF
+#define CAU_SB_ENTRY_SB_PROD_SHIFT     0
+#define CAU_SB_ENTRY_STATE0_MASK       0xF
+#define CAU_SB_ENTRY_STATE0_SHIFT      24
+#define CAU_SB_ENTRY_STATE1_MASK       0xF
+#define CAU_SB_ENTRY_STATE1_SHIFT      28
+       u32 params;
+#define CAU_SB_ENTRY_SB_TIMESET0_MASK  0x7F
+#define CAU_SB_ENTRY_SB_TIMESET0_SHIFT 0
+#define CAU_SB_ENTRY_SB_TIMESET1_MASK  0x7F
+#define CAU_SB_ENTRY_SB_TIMESET1_SHIFT 7
+#define CAU_SB_ENTRY_TIMER_RES0_MASK   0x3
+#define CAU_SB_ENTRY_TIMER_RES0_SHIFT  14
+#define CAU_SB_ENTRY_TIMER_RES1_MASK   0x3
+#define CAU_SB_ENTRY_TIMER_RES1_SHIFT  16
+#define CAU_SB_ENTRY_VF_NUMBER_MASK    0xFF
+#define CAU_SB_ENTRY_VF_NUMBER_SHIFT   18
+#define CAU_SB_ENTRY_VF_VALID_MASK     0x1
+#define CAU_SB_ENTRY_VF_VALID_SHIFT    26
+#define CAU_SB_ENTRY_PF_NUMBER_MASK    0xF
+#define CAU_SB_ENTRY_PF_NUMBER_SHIFT   27
+#define CAU_SB_ENTRY_TPH_MASK          0x1
+#define CAU_SB_ENTRY_TPH_SHIFT         31
+};
+
+/* core doorbell data */
+struct core_db_data {
+       u8 params;
+#define CORE_DB_DATA_DEST_MASK         0x3
+#define CORE_DB_DATA_DEST_SHIFT        0
+#define CORE_DB_DATA_AGG_CMD_MASK      0x3
+#define CORE_DB_DATA_AGG_CMD_SHIFT     2
+#define CORE_DB_DATA_BYPASS_EN_MASK    0x1
+#define CORE_DB_DATA_BYPASS_EN_SHIFT   4
+#define CORE_DB_DATA_RESERVED_MASK     0x1
+#define CORE_DB_DATA_RESERVED_SHIFT    5
+#define CORE_DB_DATA_AGG_VAL_SEL_MASK  0x3
+#define CORE_DB_DATA_AGG_VAL_SEL_SHIFT 6
+       u8      agg_flags;
+       __le16  spq_prod;
+};
+
+/* Enum of doorbell aggregative command selection */
+enum db_agg_cmd_sel {
+       DB_AGG_CMD_NOP,
+       DB_AGG_CMD_SET,
+       DB_AGG_CMD_ADD,
+       DB_AGG_CMD_MAX,
+       MAX_DB_AGG_CMD_SEL
+};
+
+/* Enum of doorbell destination */
+enum db_dest {
+       DB_DEST_XCM,
+       DB_DEST_UCM,
+       DB_DEST_TCM,
+       DB_NUM_DESTINATIONS,
+       MAX_DB_DEST
+};
+
+/* Structure for doorbell address, in legacy mode */
+struct db_legacy_addr {
+       __le32 addr;
+#define DB_LEGACY_ADDR_RESERVED0_MASK  0x3
+#define DB_LEGACY_ADDR_RESERVED0_SHIFT 0
+#define DB_LEGACY_ADDR_DEMS_MASK       0x7
+#define DB_LEGACY_ADDR_DEMS_SHIFT      2
+#define DB_LEGACY_ADDR_ICID_MASK       0x7FFFFFF
+#define DB_LEGACY_ADDR_ICID_SHIFT      5
+};
+
+/* Igu interrupt command */
+enum igu_int_cmd {
+       IGU_INT_ENABLE  = 0,
+       IGU_INT_DISABLE = 1,
+       IGU_INT_NOP     = 2,
+       IGU_INT_NOP2    = 3,
+       MAX_IGU_INT_CMD
+};
+
+/* IGU producer or consumer update command */
+struct igu_prod_cons_update {
+       u32 sb_id_and_flags;
+#define IGU_PROD_CONS_UPDATE_SB_INDEX_MASK        0xFFFFFF
+#define IGU_PROD_CONS_UPDATE_SB_INDEX_SHIFT       0
+#define IGU_PROD_CONS_UPDATE_UPDATE_FLAG_MASK     0x1
+#define IGU_PROD_CONS_UPDATE_UPDATE_FLAG_SHIFT    24
+#define IGU_PROD_CONS_UPDATE_ENABLE_INT_MASK      0x3
+#define IGU_PROD_CONS_UPDATE_ENABLE_INT_SHIFT     25
+#define IGU_PROD_CONS_UPDATE_SEGMENT_ACCESS_MASK  0x1
+#define IGU_PROD_CONS_UPDATE_SEGMENT_ACCESS_SHIFT 27
+#define IGU_PROD_CONS_UPDATE_TIMER_MASK_MASK      0x1
+#define IGU_PROD_CONS_UPDATE_TIMER_MASK_SHIFT     28
+#define IGU_PROD_CONS_UPDATE_RESERVED0_MASK       0x3
+#define IGU_PROD_CONS_UPDATE_RESERVED0_SHIFT      29
+#define IGU_PROD_CONS_UPDATE_COMMAND_TYPE_MASK    0x1
+#define IGU_PROD_CONS_UPDATE_COMMAND_TYPE_SHIFT   31
+       u32 reserved1;
+};
+
+/* Igu segments access for default status block only */
+enum igu_seg_access {
+       IGU_SEG_ACCESS_REG      = 0,
+       IGU_SEG_ACCESS_ATTN     = 1,
+       MAX_IGU_SEG_ACCESS
+};
+
+struct parsing_and_err_flags {
+       __le16 flags;
+#define PARSING_AND_ERR_FLAGS_L3TYPE_MASK                      0x3
+#define PARSING_AND_ERR_FLAGS_L3TYPE_SHIFT                     0
+#define PARSING_AND_ERR_FLAGS_L4PROTOCOL_MASK                  0x3
+#define PARSING_AND_ERR_FLAGS_L4PROTOCOL_SHIFT                 2
+#define PARSING_AND_ERR_FLAGS_IPV4FRAG_MASK                    0x1
+#define PARSING_AND_ERR_FLAGS_IPV4FRAG_SHIFT                   4
+#define PARSING_AND_ERR_FLAGS_TAG8021QEXIST_MASK               0x1
+#define PARSING_AND_ERR_FLAGS_TAG8021QEXIST_SHIFT              5
+#define PARSING_AND_ERR_FLAGS_L4CHKSMWASCALCULATED_MASK        0x1
+#define PARSING_AND_ERR_FLAGS_L4CHKSMWASCALCULATED_SHIFT       6
+#define PARSING_AND_ERR_FLAGS_TIMESYNCPKT_MASK                 0x1
+#define PARSING_AND_ERR_FLAGS_TIMESYNCPKT_SHIFT                7
+#define PARSING_AND_ERR_FLAGS_TIMESTAMPRECORDED_MASK           0x1
+#define PARSING_AND_ERR_FLAGS_TIMESTAMPRECORDED_SHIFT          8
+#define PARSING_AND_ERR_FLAGS_IPHDRERROR_MASK                  0x1
+#define PARSING_AND_ERR_FLAGS_IPHDRERROR_SHIFT                 9
+#define PARSING_AND_ERR_FLAGS_L4CHKSMERROR_MASK                0x1
+#define PARSING_AND_ERR_FLAGS_L4CHKSMERROR_SHIFT               10
+#define PARSING_AND_ERR_FLAGS_TUNNELEXIST_MASK                 0x1
+#define PARSING_AND_ERR_FLAGS_TUNNELEXIST_SHIFT                11
+#define PARSING_AND_ERR_FLAGS_TUNNEL8021QTAGEXIST_MASK         0x1
+#define PARSING_AND_ERR_FLAGS_TUNNEL8021QTAGEXIST_SHIFT        12
+#define PARSING_AND_ERR_FLAGS_TUNNELIPHDRERROR_MASK            0x1
+#define PARSING_AND_ERR_FLAGS_TUNNELIPHDRERROR_SHIFT           13
+#define PARSING_AND_ERR_FLAGS_TUNNELL4CHKSMWASCALCULATED_MASK  0x1
+#define PARSING_AND_ERR_FLAGS_TUNNELL4CHKSMWASCALCULATED_SHIFT 14
+#define PARSING_AND_ERR_FLAGS_TUNNELL4CHKSMERROR_MASK          0x1
+#define PARSING_AND_ERR_FLAGS_TUNNELL4CHKSMERROR_SHIFT         15
+};
+
+/* Concrete Function ID. */
+struct pxp_concrete_fid {
+       __le16 fid;
+#define PXP_CONCRETE_FID_PFID_MASK     0xF
+#define PXP_CONCRETE_FID_PFID_SHIFT    0
+#define PXP_CONCRETE_FID_PORT_MASK     0x3
+#define PXP_CONCRETE_FID_PORT_SHIFT    4
+#define PXP_CONCRETE_FID_PATH_MASK     0x1
+#define PXP_CONCRETE_FID_PATH_SHIFT    6
+#define PXP_CONCRETE_FID_VFVALID_MASK  0x1
+#define PXP_CONCRETE_FID_VFVALID_SHIFT 7
+#define PXP_CONCRETE_FID_VFID_MASK     0xFF
+#define PXP_CONCRETE_FID_VFID_SHIFT    8
+};
+
+struct pxp_pretend_concrete_fid {
+       __le16 fid;
+#define PXP_PRETEND_CONCRETE_FID_PFID_MASK      0xF
+#define PXP_PRETEND_CONCRETE_FID_PFID_SHIFT     0
+#define PXP_PRETEND_CONCRETE_FID_RESERVED_MASK  0x7
+#define PXP_PRETEND_CONCRETE_FID_RESERVED_SHIFT 4
+#define PXP_PRETEND_CONCRETE_FID_VFVALID_MASK   0x1
+#define PXP_PRETEND_CONCRETE_FID_VFVALID_SHIFT  7
+#define PXP_PRETEND_CONCRETE_FID_VFID_MASK      0xFF
+#define PXP_PRETEND_CONCRETE_FID_VFID_SHIFT     8
+};
+
+union pxp_pretend_fid {
+       struct pxp_pretend_concrete_fid concrete_fid;
+       __le16                          opaque_fid;
+};
+
+/* Pxp Pretend Command Register. */
+struct pxp_pretend_cmd {
+       union pxp_pretend_fid   fid;
+       __le16                  control;
+#define PXP_PRETEND_CMD_PATH_MASK              0x1
+#define PXP_PRETEND_CMD_PATH_SHIFT             0
+#define PXP_PRETEND_CMD_USE_PORT_MASK          0x1
+#define PXP_PRETEND_CMD_USE_PORT_SHIFT         1
+#define PXP_PRETEND_CMD_PORT_MASK              0x3
+#define PXP_PRETEND_CMD_PORT_SHIFT             2
+#define PXP_PRETEND_CMD_RESERVED0_MASK         0xF
+#define PXP_PRETEND_CMD_RESERVED0_SHIFT        4
+#define PXP_PRETEND_CMD_RESERVED1_MASK         0xF
+#define PXP_PRETEND_CMD_RESERVED1_SHIFT        8
+#define PXP_PRETEND_CMD_PRETEND_PATH_MASK      0x1
+#define PXP_PRETEND_CMD_PRETEND_PATH_SHIFT     12
+#define PXP_PRETEND_CMD_PRETEND_PORT_MASK      0x1
+#define PXP_PRETEND_CMD_PRETEND_PORT_SHIFT     13
+#define PXP_PRETEND_CMD_PRETEND_FUNCTION_MASK  0x1
+#define PXP_PRETEND_CMD_PRETEND_FUNCTION_SHIFT 14
+#define PXP_PRETEND_CMD_IS_CONCRETE_MASK       0x1
+#define PXP_PRETEND_CMD_IS_CONCRETE_SHIFT      15
+};
+
+/* PTT Record in PXP Admin Window. */
+struct pxp_ptt_entry {
+       __le32                  offset;
+#define PXP_PTT_ENTRY_OFFSET_MASK     0x7FFFFF
+#define PXP_PTT_ENTRY_OFFSET_SHIFT    0
+#define PXP_PTT_ENTRY_RESERVED0_MASK  0x1FF
+#define PXP_PTT_ENTRY_RESERVED0_SHIFT 23
+       struct pxp_pretend_cmd  pretend;
+};
+
+/* RSS hash type */
+enum rss_hash_type {
+       RSS_HASH_TYPE_DEFAULT   = 0,
+       RSS_HASH_TYPE_IPV4      = 1,
+       RSS_HASH_TYPE_TCP_IPV4  = 2,
+       RSS_HASH_TYPE_IPV6      = 3,
+       RSS_HASH_TYPE_TCP_IPV6  = 4,
+       RSS_HASH_TYPE_UDP_IPV4  = 5,
+       RSS_HASH_TYPE_UDP_IPV6  = 6,
+       MAX_RSS_HASH_TYPE
+};
+
+/* status block structure */
+struct status_block {
+       __le16  pi_array[PIS_PER_SB];
+       __le32  sb_num;
+#define STATUS_BLOCK_SB_NUM_MASK      0x1FF
+#define STATUS_BLOCK_SB_NUM_SHIFT     0
+#define STATUS_BLOCK_ZERO_PAD_MASK    0x7F
+#define STATUS_BLOCK_ZERO_PAD_SHIFT   9
+#define STATUS_BLOCK_ZERO_PAD2_MASK   0xFFFF
+#define STATUS_BLOCK_ZERO_PAD2_SHIFT  16
+       __le32 prod_index;
+#define STATUS_BLOCK_PROD_INDEX_MASK  0xFFFFFF
+#define STATUS_BLOCK_PROD_INDEX_SHIFT 0
+#define STATUS_BLOCK_ZERO_PAD3_MASK   0xFF
+#define STATUS_BLOCK_ZERO_PAD3_SHIFT  24
+};
+
+#endif /* __COMMON_HSI__ */
diff --git a/include/linux/qed/eth_common.h b/include/linux/qed/eth_common.h
new file mode 100644 (file)
index 0000000..320b337
--- /dev/null
@@ -0,0 +1,279 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef __ETH_COMMON__
+#define __ETH_COMMON__
+
+/********************/
+/* ETH FW CONSTANTS */
+/********************/
+#define ETH_CACHE_LINE_SIZE                 64
+
+#define ETH_MAX_RAMROD_PER_CON                          8
+#define ETH_TX_BD_PAGE_SIZE_BYTES                       4096
+#define ETH_RX_BD_PAGE_SIZE_BYTES                       4096
+#define ETH_RX_SGE_PAGE_SIZE_BYTES                      4096
+#define ETH_RX_CQE_PAGE_SIZE_BYTES                      4096
+#define ETH_RX_NUM_NEXT_PAGE_BDS                        2
+#define ETH_RX_NUM_NEXT_PAGE_SGES                       2
+
+#define ETH_TX_MIN_BDS_PER_NON_LSO_PKT                          1
+#define ETH_TX_MAX_BDS_PER_NON_LSO_PACKET                       18
+#define ETH_TX_MAX_LSO_HDR_NBD                                          4
+#define ETH_TX_MIN_BDS_PER_LSO_PKT                                      3
+#define ETH_TX_MIN_BDS_PER_TUNN_IPV6_WITH_EXT_PKT       3
+#define ETH_TX_MIN_BDS_PER_IPV6_WITH_EXT_PKT            2
+#define ETH_TX_MIN_BDS_PER_PKT_W_LOOPBACK_MODE          2
+#define ETH_TX_MAX_NON_LSO_PKT_LEN                  (9700 - (4 + 12 + 8))
+#define ETH_TX_MAX_LSO_HDR_BYTES                    510
+
+#define ETH_NUM_STATISTIC_COUNTERS                      MAX_NUM_VPORTS
+
+#define ETH_REG_CQE_PBL_SIZE                3
+
+/* num of MAC/VLAN filters */
+#define ETH_NUM_MAC_FILTERS                                     512
+#define ETH_NUM_VLAN_FILTERS                            512
+
+/* approx. multicast constants */
+#define ETH_MULTICAST_BIN_FROM_MAC_SEED     0
+#define ETH_MULTICAST_MAC_BINS                          256
+#define ETH_MULTICAST_MAC_BINS_IN_REGS          (ETH_MULTICAST_MAC_BINS / 32)
+
+/*  ethernet vport update constants */
+#define ETH_FILTER_RULES_COUNT                          10
+#define ETH_RSS_IND_TABLE_ENTRIES_NUM           128
+#define ETH_RSS_KEY_SIZE_REGS                       10
+#define ETH_RSS_ENGINE_NUM_K2               207
+#define ETH_RSS_ENGINE_NUM_BB               127
+
+/* TPA constants */
+#define ETH_TPA_MAX_AGGS_NUM              64
+#define ETH_TPA_CQE_START_SGL_SIZE        3
+#define ETH_TPA_CQE_CONT_SGL_SIZE         6
+#define ETH_TPA_CQE_END_SGL_SIZE          4
+
+/* Queue Zone sizes */
+#define TSTORM_QZONE_SIZE    0
+#define MSTORM_QZONE_SIZE    sizeof(struct mstorm_eth_queue_zone)
+#define USTORM_QZONE_SIZE    sizeof(struct ustorm_eth_queue_zone)
+#define XSTORM_QZONE_SIZE    0
+#define YSTORM_QZONE_SIZE    sizeof(struct ystorm_eth_queue_zone)
+#define PSTORM_QZONE_SIZE    0
+
+/* Interrupt coalescing TimeSet */
+struct coalescing_timeset {
+       u8      timeset;
+       u8      valid;
+};
+
+struct eth_tx_1st_bd_flags {
+       u8 bitfields;
+#define ETH_TX_1ST_BD_FLAGS_FORCE_VLAN_MODE_MASK  0x1
+#define ETH_TX_1ST_BD_FLAGS_FORCE_VLAN_MODE_SHIFT 0
+#define ETH_TX_1ST_BD_FLAGS_IP_CSUM_MASK          0x1
+#define ETH_TX_1ST_BD_FLAGS_IP_CSUM_SHIFT         1
+#define ETH_TX_1ST_BD_FLAGS_L4_CSUM_MASK          0x1
+#define ETH_TX_1ST_BD_FLAGS_L4_CSUM_SHIFT         2
+#define ETH_TX_1ST_BD_FLAGS_VLAN_INSERTION_MASK   0x1
+#define ETH_TX_1ST_BD_FLAGS_VLAN_INSERTION_SHIFT  3
+#define ETH_TX_1ST_BD_FLAGS_LSO_MASK              0x1
+#define ETH_TX_1ST_BD_FLAGS_LSO_SHIFT             4
+#define ETH_TX_1ST_BD_FLAGS_START_BD_MASK         0x1
+#define ETH_TX_1ST_BD_FLAGS_START_BD_SHIFT        5
+#define ETH_TX_1ST_BD_FLAGS_TUNN_IP_CSUM_MASK     0x1
+#define ETH_TX_1ST_BD_FLAGS_TUNN_IP_CSUM_SHIFT    6
+#define ETH_TX_1ST_BD_FLAGS_TUNN_L4_CSUM_MASK     0x1
+#define ETH_TX_1ST_BD_FLAGS_TUNN_L4_CSUM_SHIFT    7
+};
+
+/* The parsing information data fo rthe first tx bd of a given packet. */
+struct eth_tx_data_1st_bd {
+       __le16                          vlan;
+       u8                              nbds;
+       struct eth_tx_1st_bd_flags      bd_flags;
+       __le16                          fw_use_only;
+};
+
+/* The parsing information data for the second tx bd of a given packet. */
+struct eth_tx_data_2nd_bd {
+       __le16  tunn_ip_size;
+       __le16  bitfields;
+#define ETH_TX_DATA_2ND_BD_L4_HDR_START_OFFSET_W_MASK     0x1FFF
+#define ETH_TX_DATA_2ND_BD_L4_HDR_START_OFFSET_W_SHIFT    0
+#define ETH_TX_DATA_2ND_BD_RESERVED0_MASK                 0x7
+#define ETH_TX_DATA_2ND_BD_RESERVED0_SHIFT                13
+       __le16  bitfields2;
+#define ETH_TX_DATA_2ND_BD_TUNN_INNER_L2_HDR_SIZE_W_MASK  0xF
+#define ETH_TX_DATA_2ND_BD_TUNN_INNER_L2_HDR_SIZE_W_SHIFT 0
+#define ETH_TX_DATA_2ND_BD_TUNN_INNER_ETH_TYPE_MASK       0x3
+#define ETH_TX_DATA_2ND_BD_TUNN_INNER_ETH_TYPE_SHIFT      4
+#define ETH_TX_DATA_2ND_BD_DEST_PORT_MODE_MASK            0x3
+#define ETH_TX_DATA_2ND_BD_DEST_PORT_MODE_SHIFT           6
+#define ETH_TX_DATA_2ND_BD_TUNN_TYPE_MASK                 0x3
+#define ETH_TX_DATA_2ND_BD_TUNN_TYPE_SHIFT                8
+#define ETH_TX_DATA_2ND_BD_TUNN_INNER_IPV6_MASK           0x1
+#define ETH_TX_DATA_2ND_BD_TUNN_INNER_IPV6_SHIFT          10
+#define ETH_TX_DATA_2ND_BD_IPV6_EXT_MASK                  0x1
+#define ETH_TX_DATA_2ND_BD_IPV6_EXT_SHIFT                 11
+#define ETH_TX_DATA_2ND_BD_TUNN_IPV6_EXT_MASK             0x1
+#define ETH_TX_DATA_2ND_BD_TUNN_IPV6_EXT_SHIFT            12
+#define ETH_TX_DATA_2ND_BD_L4_UDP_MASK                    0x1
+#define ETH_TX_DATA_2ND_BD_L4_UDP_SHIFT                   13
+#define ETH_TX_DATA_2ND_BD_L4_PSEUDO_CSUM_MODE_MASK       0x1
+#define ETH_TX_DATA_2ND_BD_L4_PSEUDO_CSUM_MODE_SHIFT      14
+#define ETH_TX_DATA_2ND_BD_RESERVED1_MASK                 0x1
+#define ETH_TX_DATA_2ND_BD_RESERVED1_SHIFT                15
+};
+
+/* Regular ETH Rx FP CQE. */
+struct eth_fast_path_rx_reg_cqe {
+       u8      type;
+       u8      bitfields;
+#define ETH_FAST_PATH_RX_REG_CQE_RSS_HASH_TYPE_MASK  0x7
+#define ETH_FAST_PATH_RX_REG_CQE_RSS_HASH_TYPE_SHIFT 0
+#define ETH_FAST_PATH_RX_REG_CQE_TC_MASK             0xF
+#define ETH_FAST_PATH_RX_REG_CQE_TC_SHIFT            3
+#define ETH_FAST_PATH_RX_REG_CQE_RESERVED0_MASK      0x1
+#define ETH_FAST_PATH_RX_REG_CQE_RESERVED0_SHIFT     7
+       __le16                          pkt_len;
+       struct parsing_and_err_flags    pars_flags;
+       __le16                          vlan_tag;
+       __le32                          rss_hash;
+       __le16                          len_on_bd;
+       u8                              placement_offset;
+       u8                              reserved;
+       __le16                          pbl[ETH_REG_CQE_PBL_SIZE];
+       u8                              reserved1[10];
+};
+
+/* The L4 pseudo checksum mode for Ethernet */
+enum eth_l4_pseudo_checksum_mode {
+       ETH_L4_PSEUDO_CSUM_CORRECT_LENGTH,
+       ETH_L4_PSEUDO_CSUM_ZERO_LENGTH,
+       MAX_ETH_L4_PSEUDO_CHECKSUM_MODE
+};
+
+struct eth_rx_bd {
+       struct regpair addr;
+};
+
+/* regular ETH Rx SP CQE */
+struct eth_slow_path_rx_cqe {
+       u8      type;
+       u8      ramrod_cmd_id;
+       u8      error_flag;
+       u8      reserved[27];
+       __le16  echo;
+};
+
+/* union for all ETH Rx CQE types */
+union eth_rx_cqe {
+       struct eth_fast_path_rx_reg_cqe         fast_path_regular;
+       struct eth_slow_path_rx_cqe             slow_path;
+};
+
+/* ETH Rx CQE type */
+enum eth_rx_cqe_type {
+       ETH_RX_CQE_TYPE_UNUSED,
+       ETH_RX_CQE_TYPE_REGULAR,
+       ETH_RX_CQE_TYPE_SLOW_PATH,
+       MAX_ETH_RX_CQE_TYPE
+};
+
+/* ETH Rx producers data */
+struct eth_rx_prod_data {
+       __le16  bd_prod;
+       __le16  sge_prod;
+       __le16  cqe_prod;
+       __le16  reserved;
+};
+
+/* The first tx bd of a given packet */
+struct eth_tx_1st_bd {
+       struct regpair                  addr;
+       __le16                          nbytes;
+       struct eth_tx_data_1st_bd       data;
+};
+
+/* The second tx bd of a given packet */
+struct eth_tx_2nd_bd {
+       struct regpair                  addr;
+       __le16                          nbytes;
+       struct eth_tx_data_2nd_bd       data;
+};
+
+/* The parsing information data for the third tx bd of a given packet. */
+struct eth_tx_data_3rd_bd {
+       __le16  lso_mss;
+       u8      bitfields;
+#define ETH_TX_DATA_3RD_BD_TCP_HDR_LEN_DW_MASK  0xF
+#define ETH_TX_DATA_3RD_BD_TCP_HDR_LEN_DW_SHIFT 0
+#define ETH_TX_DATA_3RD_BD_HDR_NBD_MASK         0xF
+#define ETH_TX_DATA_3RD_BD_HDR_NBD_SHIFT        4
+       u8      resereved0[3];
+};
+
+/* The third tx bd of a given packet */
+struct eth_tx_3rd_bd {
+       struct regpair                  addr;
+       __le16                          nbytes;
+       struct eth_tx_data_3rd_bd       data;
+};
+
+/* The common non-special TX BD ring element */
+struct eth_tx_bd {
+       struct regpair  addr;
+       __le16          nbytes;
+       __le16          reserved0;
+       __le32          reserved1;
+};
+
+union eth_tx_bd_types {
+       struct eth_tx_1st_bd    first_bd;
+       struct eth_tx_2nd_bd    second_bd;
+       struct eth_tx_3rd_bd    third_bd;
+       struct eth_tx_bd        reg_bd;
+};
+
+/* Mstorm Queue Zone */
+struct mstorm_eth_queue_zone {
+       struct eth_rx_prod_data rx_producers;
+       __le32                  reserved[2];
+};
+
+/* Ustorm Queue Zone */
+struct ustorm_eth_queue_zone {
+       struct coalescing_timeset       int_coalescing_timeset;
+       __le16                          reserved[3];
+};
+
+/* Ystorm Queue Zone */
+struct ystorm_eth_queue_zone {
+       struct coalescing_timeset       int_coalescing_timeset;
+       __le16                          reserved[3];
+};
+
+/* ETH doorbell data */
+struct eth_db_data {
+       u8 params;
+#define ETH_DB_DATA_DEST_MASK         0x3
+#define ETH_DB_DATA_DEST_SHIFT        0
+#define ETH_DB_DATA_AGG_CMD_MASK      0x3
+#define ETH_DB_DATA_AGG_CMD_SHIFT     2
+#define ETH_DB_DATA_BYPASS_EN_MASK    0x1
+#define ETH_DB_DATA_BYPASS_EN_SHIFT   4
+#define ETH_DB_DATA_RESERVED_MASK     0x1
+#define ETH_DB_DATA_RESERVED_SHIFT    5
+#define ETH_DB_DATA_AGG_VAL_SEL_MASK  0x3
+#define ETH_DB_DATA_AGG_VAL_SEL_SHIFT 6
+       u8      agg_flags;
+       __le16  bd_prod;
+};
+
+#endif /* __ETH_COMMON__ */
diff --git a/include/linux/qed/qed_chain.h b/include/linux/qed/qed_chain.h
new file mode 100644 (file)
index 0000000..b920c36
--- /dev/null
@@ -0,0 +1,539 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_CHAIN_H
+#define _QED_CHAIN_H
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/qed/common_hsi.h>
+
+/* dma_addr_t manip */
+#define DMA_LO_LE(x)            cpu_to_le32(lower_32_bits(x))
+#define DMA_HI_LE(x)            cpu_to_le32(upper_32_bits(x))
+
+#define HILO_GEN(hi, lo, type)  ((((type)(hi)) << 32) + (lo))
+#define HILO_DMA(hi, lo)        HILO_GEN(hi, lo, dma_addr_t)
+#define HILO_64(hi, lo) HILO_GEN((le32_to_cpu(hi)), (le32_to_cpu(lo)), u64)
+#define HILO_DMA_REGPAIR(regpair)       (HILO_DMA(regpair.hi, regpair.lo))
+#define HILO_64_REGPAIR(regpair)        (HILO_64(regpair.hi, regpair.lo))
+
+enum qed_chain_mode {
+       /* Each Page contains a next pointer at its end */
+       QED_CHAIN_MODE_NEXT_PTR,
+
+       /* Chain is a single page (next ptr) is unrequired */
+       QED_CHAIN_MODE_SINGLE,
+
+       /* Page pointers are located in a side list */
+       QED_CHAIN_MODE_PBL,
+};
+
+enum qed_chain_use_mode {
+       QED_CHAIN_USE_TO_PRODUCE,               /* Chain starts empty */
+       QED_CHAIN_USE_TO_CONSUME,               /* Chain starts full */
+       QED_CHAIN_USE_TO_CONSUME_PRODUCE,       /* Chain starts empty */
+};
+
+struct qed_chain_next {
+       struct regpair  next_phys;
+       void            *next_virt;
+};
+
+struct qed_chain_pbl {
+       dma_addr_t      p_phys_table;
+       void            *p_virt_table;
+       u16             prod_page_idx;
+       u16             cons_page_idx;
+};
+
+struct qed_chain {
+       void                    *p_virt_addr;
+       dma_addr_t              p_phys_addr;
+       void                    *p_prod_elem;
+       void                    *p_cons_elem;
+       u16                     page_cnt;
+       enum qed_chain_mode     mode;
+       enum qed_chain_use_mode intended_use; /* used to produce/consume */
+       u16                     capacity; /*< number of _usable_ elements */
+       u16                     size; /* number of elements */
+       u16                     prod_idx;
+       u16                     cons_idx;
+       u16                     elem_per_page;
+       u16                     elem_per_page_mask;
+       u16                     elem_unusable;
+       u16                     usable_per_page;
+       u16                     elem_size;
+       u16                     next_page_mask;
+       struct qed_chain_pbl    pbl;
+};
+
+#define QED_CHAIN_PBL_ENTRY_SIZE        (8)
+#define QED_CHAIN_PAGE_SIZE             (0x1000)
+#define ELEMS_PER_PAGE(elem_size)       (QED_CHAIN_PAGE_SIZE / (elem_size))
+
+#define UNUSABLE_ELEMS_PER_PAGE(elem_size, mode)     \
+       ((mode == QED_CHAIN_MODE_NEXT_PTR) ?         \
+        (1 + ((sizeof(struct qed_chain_next) - 1) / \
+              (elem_size))) : 0)
+
+#define USABLE_ELEMS_PER_PAGE(elem_size, mode) \
+       ((u32)(ELEMS_PER_PAGE(elem_size) -     \
+              UNUSABLE_ELEMS_PER_PAGE(elem_size, mode)))
+
+#define QED_CHAIN_PAGE_CNT(elem_cnt, elem_size, mode) \
+       DIV_ROUND_UP(elem_cnt, USABLE_ELEMS_PER_PAGE(elem_size, mode))
+
+/* Accessors */
+static inline u16 qed_chain_get_prod_idx(struct qed_chain *p_chain)
+{
+       return p_chain->prod_idx;
+}
+
+static inline u16 qed_chain_get_cons_idx(struct qed_chain *p_chain)
+{
+       return p_chain->cons_idx;
+}
+
+static inline u16 qed_chain_get_elem_left(struct qed_chain *p_chain)
+{
+       u16 used;
+
+       /* we don't need to trancate upon assignmet, as we assign u32->u16 */
+       used = ((u32)0x10000u + (u32)(p_chain->prod_idx)) -
+               (u32)p_chain->cons_idx;
+       if (p_chain->mode == QED_CHAIN_MODE_NEXT_PTR)
+               used -= (used / p_chain->elem_per_page);
+
+       return p_chain->capacity - used;
+}
+
+static inline u8 qed_chain_is_full(struct qed_chain *p_chain)
+{
+       return qed_chain_get_elem_left(p_chain) == p_chain->capacity;
+}
+
+static inline u8 qed_chain_is_empty(struct qed_chain *p_chain)
+{
+       return qed_chain_get_elem_left(p_chain) == 0;
+}
+
+static inline u16 qed_chain_get_elem_per_page(
+       struct qed_chain *p_chain)
+{
+       return p_chain->elem_per_page;
+}
+
+static inline u16 qed_chain_get_usable_per_page(
+       struct qed_chain *p_chain)
+{
+       return p_chain->usable_per_page;
+}
+
+static inline u16 qed_chain_get_unusable_per_page(
+       struct qed_chain *p_chain)
+{
+       return p_chain->elem_unusable;
+}
+
+static inline u16 qed_chain_get_size(struct qed_chain *p_chain)
+{
+       return p_chain->size;
+}
+
+static inline dma_addr_t
+qed_chain_get_pbl_phys(struct qed_chain *p_chain)
+{
+       return p_chain->pbl.p_phys_table;
+}
+
+/**
+ * @brief qed_chain_advance_page -
+ *
+ * Advance the next element accros pages for a linked chain
+ *
+ * @param p_chain
+ * @param p_next_elem
+ * @param idx_to_inc
+ * @param page_to_inc
+ */
+static inline void
+qed_chain_advance_page(struct qed_chain *p_chain,
+                      void **p_next_elem,
+                      u16 *idx_to_inc,
+                      u16 *page_to_inc)
+
+{
+       switch (p_chain->mode) {
+       case QED_CHAIN_MODE_NEXT_PTR:
+       {
+               struct qed_chain_next *p_next = *p_next_elem;
+               *p_next_elem = p_next->next_virt;
+               *idx_to_inc += p_chain->elem_unusable;
+               break;
+       }
+       case QED_CHAIN_MODE_SINGLE:
+               *p_next_elem = p_chain->p_virt_addr;
+               break;
+
+       case QED_CHAIN_MODE_PBL:
+               /* It is assumed pages are sequential, next element needs
+                * to change only when passing going back to first from last.
+                */
+               if (++(*page_to_inc) == p_chain->page_cnt) {
+                       *page_to_inc = 0;
+                       *p_next_elem = p_chain->p_virt_addr;
+               }
+       }
+}
+
+#define is_unusable_idx(p, idx)        \
+       (((p)->idx & (p)->elem_per_page_mask) == (p)->usable_per_page)
+
+#define is_unusable_next_idx(p, idx) \
+       ((((p)->idx + 1) & (p)->elem_per_page_mask) == (p)->usable_per_page)
+
+#define test_ans_skip(p, idx)                          \
+       do {                                            \
+               if (is_unusable_idx(p, idx)) {          \
+                       (p)->idx += (p)->elem_unusable; \
+               }                                       \
+       } while (0)
+
+/**
+ * @brief qed_chain_return_multi_produced -
+ *
+ * A chain in which the driver "Produces" elements should use this API
+ * to indicate previous produced elements are now consumed.
+ *
+ * @param p_chain
+ * @param num
+ */
+static inline void
+qed_chain_return_multi_produced(struct qed_chain *p_chain,
+                               u16 num)
+{
+       p_chain->cons_idx += num;
+       test_ans_skip(p_chain, cons_idx);
+}
+
+/**
+ * @brief qed_chain_return_produced -
+ *
+ * A chain in which the driver "Produces" elements should use this API
+ * to indicate previous produced elements are now consumed.
+ *
+ * @param p_chain
+ */
+static inline void qed_chain_return_produced(struct qed_chain *p_chain)
+{
+       p_chain->cons_idx++;
+       test_ans_skip(p_chain, cons_idx);
+}
+
+/**
+ * @brief qed_chain_produce -
+ *
+ * A chain in which the driver "Produces" elements should use this to get
+ * a pointer to the next element which can be "Produced". It's driver
+ * responsibility to validate that the chain has room for new element.
+ *
+ * @param p_chain
+ *
+ * @return void*, a pointer to next element
+ */
+static inline void *qed_chain_produce(struct qed_chain *p_chain)
+{
+       void *ret = NULL;
+
+       if ((p_chain->prod_idx & p_chain->elem_per_page_mask) ==
+           p_chain->next_page_mask) {
+               qed_chain_advance_page(p_chain, &p_chain->p_prod_elem,
+                                      &p_chain->prod_idx,
+                                      &p_chain->pbl.prod_page_idx);
+       }
+
+       ret = p_chain->p_prod_elem;
+       p_chain->prod_idx++;
+       p_chain->p_prod_elem = (void *)(((u8 *)p_chain->p_prod_elem) +
+                                       p_chain->elem_size);
+
+       return ret;
+}
+
+/**
+ * @brief qed_chain_get_capacity -
+ *
+ * Get the maximum number of BDs in chain
+ *
+ * @param p_chain
+ * @param num
+ *
+ * @return u16, number of unusable BDs
+ */
+static inline u16 qed_chain_get_capacity(struct qed_chain *p_chain)
+{
+       return p_chain->capacity;
+}
+
+/**
+ * @brief qed_chain_recycle_consumed -
+ *
+ * Returns an element which was previously consumed;
+ * Increments producers so they could be written to FW.
+ *
+ * @param p_chain
+ */
+static inline void
+qed_chain_recycle_consumed(struct qed_chain *p_chain)
+{
+       test_ans_skip(p_chain, prod_idx);
+       p_chain->prod_idx++;
+}
+
+/**
+ * @brief qed_chain_consume -
+ *
+ * A Chain in which the driver utilizes data written by a different source
+ * (i.e., FW) should use this to access passed buffers.
+ *
+ * @param p_chain
+ *
+ * @return void*, a pointer to the next buffer written
+ */
+static inline void *qed_chain_consume(struct qed_chain *p_chain)
+{
+       void *ret = NULL;
+
+       if ((p_chain->cons_idx & p_chain->elem_per_page_mask) ==
+           p_chain->next_page_mask) {
+               qed_chain_advance_page(p_chain, &p_chain->p_cons_elem,
+                                      &p_chain->cons_idx,
+                                      &p_chain->pbl.cons_page_idx);
+       }
+
+       ret = p_chain->p_cons_elem;
+       p_chain->cons_idx++;
+       p_chain->p_cons_elem = (void *)(((u8 *)p_chain->p_cons_elem) +
+                                       p_chain->elem_size);
+
+       return ret;
+}
+
+/**
+ * @brief qed_chain_reset - Resets the chain to its start state
+ *
+ * @param p_chain pointer to a previously allocted chain
+ */
+static inline void qed_chain_reset(struct qed_chain *p_chain)
+{
+       int i;
+
+       p_chain->prod_idx       = 0;
+       p_chain->cons_idx       = 0;
+       p_chain->p_cons_elem    = p_chain->p_virt_addr;
+       p_chain->p_prod_elem    = p_chain->p_virt_addr;
+
+       if (p_chain->mode == QED_CHAIN_MODE_PBL) {
+               p_chain->pbl.prod_page_idx      = p_chain->page_cnt - 1;
+               p_chain->pbl.cons_page_idx      = p_chain->page_cnt - 1;
+       }
+
+       switch (p_chain->intended_use) {
+       case QED_CHAIN_USE_TO_CONSUME_PRODUCE:
+       case QED_CHAIN_USE_TO_PRODUCE:
+               /* Do nothing */
+               break;
+
+       case QED_CHAIN_USE_TO_CONSUME:
+               /* produce empty elements */
+               for (i = 0; i < p_chain->capacity; i++)
+                       qed_chain_recycle_consumed(p_chain);
+               break;
+       }
+}
+
+/**
+ * @brief qed_chain_init - Initalizes a basic chain struct
+ *
+ * @param p_chain
+ * @param p_virt_addr
+ * @param p_phys_addr  physical address of allocated buffer's beginning
+ * @param page_cnt     number of pages in the allocated buffer
+ * @param elem_size    size of each element in the chain
+ * @param intended_use
+ * @param mode
+ */
+static inline void qed_chain_init(struct qed_chain *p_chain,
+                                 void *p_virt_addr,
+                                 dma_addr_t p_phys_addr,
+                                 u16 page_cnt,
+                                 u8 elem_size,
+                                 enum qed_chain_use_mode intended_use,
+                                 enum qed_chain_mode mode)
+{
+       /* chain fixed parameters */
+       p_chain->p_virt_addr    = p_virt_addr;
+       p_chain->p_phys_addr    = p_phys_addr;
+       p_chain->elem_size      = elem_size;
+       p_chain->page_cnt       = page_cnt;
+       p_chain->mode           = mode;
+
+       p_chain->intended_use           = intended_use;
+       p_chain->elem_per_page          = ELEMS_PER_PAGE(elem_size);
+       p_chain->usable_per_page =
+               USABLE_ELEMS_PER_PAGE(elem_size, mode);
+       p_chain->capacity               = p_chain->usable_per_page * page_cnt;
+       p_chain->size                   = p_chain->elem_per_page * page_cnt;
+       p_chain->elem_per_page_mask     = p_chain->elem_per_page - 1;
+
+       p_chain->elem_unusable = UNUSABLE_ELEMS_PER_PAGE(elem_size, mode);
+
+       p_chain->next_page_mask = (p_chain->usable_per_page &
+                                  p_chain->elem_per_page_mask);
+
+       if (mode == QED_CHAIN_MODE_NEXT_PTR) {
+               struct qed_chain_next   *p_next;
+               u16                     i;
+
+               for (i = 0; i < page_cnt - 1; i++) {
+                       /* Increment mem_phy to the next page. */
+                       p_phys_addr += QED_CHAIN_PAGE_SIZE;
+
+                       /* Initialize the physical address of the next page. */
+                       p_next = (struct qed_chain_next *)((u8 *)p_virt_addr +
+                                                          elem_size *
+                                                          p_chain->
+                                                          usable_per_page);
+
+                       p_next->next_phys.lo    = DMA_LO_LE(p_phys_addr);
+                       p_next->next_phys.hi    = DMA_HI_LE(p_phys_addr);
+
+                       /* Initialize the virtual address of the next page. */
+                       p_next->next_virt = (void *)((u8 *)p_virt_addr +
+                                                    QED_CHAIN_PAGE_SIZE);
+
+                       /* Move to the next page. */
+                       p_virt_addr = p_next->next_virt;
+               }
+
+               /* Last page's next should point to beginning of the chain */
+               p_next = (struct qed_chain_next *)((u8 *)p_virt_addr +
+                                                  elem_size *
+                                                  p_chain->usable_per_page);
+
+               p_next->next_phys.lo    = DMA_LO_LE(p_chain->p_phys_addr);
+               p_next->next_phys.hi    = DMA_HI_LE(p_chain->p_phys_addr);
+               p_next->next_virt       = p_chain->p_virt_addr;
+       }
+       qed_chain_reset(p_chain);
+}
+
+/**
+ * @brief qed_chain_pbl_init - Initalizes a basic pbl chain
+ *        struct
+ * @param p_chain
+ * @param p_virt_addr  virtual address of allocated buffer's beginning
+ * @param p_phys_addr  physical address of allocated buffer's beginning
+ * @param page_cnt     number of pages in the allocated buffer
+ * @param elem_size    size of each element in the chain
+ * @param use_mode
+ * @param p_phys_pbl   pointer to a pre-allocated side table
+ *                      which will hold physical page addresses.
+ * @param p_virt_pbl   pointer to a pre allocated side table
+ *                      which will hold virtual page addresses.
+ */
+static inline void
+qed_chain_pbl_init(struct qed_chain *p_chain,
+                  void *p_virt_addr,
+                  dma_addr_t p_phys_addr,
+                  u16 page_cnt,
+                  u8 elem_size,
+                  enum qed_chain_use_mode use_mode,
+                  dma_addr_t p_phys_pbl,
+                  dma_addr_t *p_virt_pbl)
+{
+       dma_addr_t *p_pbl_dma = p_virt_pbl;
+       int i;
+
+       qed_chain_init(p_chain, p_virt_addr, p_phys_addr, page_cnt,
+                      elem_size, use_mode, QED_CHAIN_MODE_PBL);
+
+       p_chain->pbl.p_phys_table = p_phys_pbl;
+       p_chain->pbl.p_virt_table = p_virt_pbl;
+
+       /* Fill the PBL with physical addresses*/
+       for (i = 0; i < page_cnt; i++) {
+               *p_pbl_dma = p_phys_addr;
+               p_phys_addr += QED_CHAIN_PAGE_SIZE;
+               p_pbl_dma++;
+       }
+}
+
+/**
+ * @brief qed_chain_set_prod - sets the prod to the given
+ *        value
+ *
+ * @param prod_idx
+ * @param p_prod_elem
+ */
+static inline void qed_chain_set_prod(struct qed_chain *p_chain,
+                                     u16 prod_idx,
+                                     void *p_prod_elem)
+{
+       p_chain->prod_idx       = prod_idx;
+       p_chain->p_prod_elem    = p_prod_elem;
+}
+
+/**
+ * @brief qed_chain_get_elem -
+ *
+ * get a pointer to an element represented by absolute idx
+ *
+ * @param p_chain
+ * @assumption p_chain->size is a power of 2
+ *
+ * @return void*, a pointer to next element
+ */
+static inline void *qed_chain_sge_get_elem(struct qed_chain *p_chain,
+                                          u16 idx)
+{
+       void *ret = NULL;
+
+       if (idx >= p_chain->size)
+               return NULL;
+
+       ret = (u8 *)p_chain->p_virt_addr + p_chain->elem_size * idx;
+
+       return ret;
+}
+
+/**
+ * @brief qed_chain_sge_inc_cons_prod
+ *
+ * for sge chains, producer isn't increased serially, the ring
+ * is expected to be full at all times. Once elements are
+ * consumed, they are immediately produced.
+ *
+ * @param p_chain
+ * @param cnt
+ *
+ * @return inline void
+ */
+static inline void
+qed_chain_sge_inc_cons_prod(struct qed_chain *p_chain,
+                           u16 cnt)
+{
+       p_chain->prod_idx += cnt;
+       p_chain->cons_idx += cnt;
+}
+
+#endif
diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h
new file mode 100644 (file)
index 0000000..81ab178
--- /dev/null
@@ -0,0 +1,165 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_ETH_IF_H
+#define _QED_ETH_IF_H
+
+#include <linux/list.h>
+#include <linux/if_link.h>
+#include <linux/qed/eth_common.h>
+#include <linux/qed/qed_if.h>
+
+struct qed_dev_eth_info {
+       struct qed_dev_info common;
+
+       u8      num_queues;
+       u8      num_tc;
+
+       u8      port_mac[ETH_ALEN];
+       u8      num_vlan_filters;
+};
+
+struct qed_update_vport_rss_params {
+       u16     rss_ind_table[128];
+       u32     rss_key[10];
+};
+
+struct qed_update_vport_params {
+       u8 vport_id;
+       u8 update_vport_active_flg;
+       u8 vport_active_flg;
+       u8 update_rss_flg;
+       struct qed_update_vport_rss_params rss_params;
+};
+
+struct qed_stop_rxq_params {
+       u8 rss_id;
+       u8 rx_queue_id;
+       u8 vport_id;
+       bool eq_completion_only;
+};
+
+struct qed_stop_txq_params {
+       u8 rss_id;
+       u8 tx_queue_id;
+};
+
+enum qed_filter_rx_mode_type {
+       QED_FILTER_RX_MODE_TYPE_REGULAR,
+       QED_FILTER_RX_MODE_TYPE_MULTI_PROMISC,
+       QED_FILTER_RX_MODE_TYPE_PROMISC,
+};
+
+enum qed_filter_xcast_params_type {
+       QED_FILTER_XCAST_TYPE_ADD,
+       QED_FILTER_XCAST_TYPE_DEL,
+       QED_FILTER_XCAST_TYPE_REPLACE,
+};
+
+struct qed_filter_ucast_params {
+       enum qed_filter_xcast_params_type type;
+       u8 vlan_valid;
+       u16 vlan;
+       u8 mac_valid;
+       unsigned char mac[ETH_ALEN];
+};
+
+struct qed_filter_mcast_params {
+       enum qed_filter_xcast_params_type type;
+       u8 num;
+       unsigned char mac[64][ETH_ALEN];
+};
+
+union qed_filter_type_params {
+       enum qed_filter_rx_mode_type accept_flags;
+       struct qed_filter_ucast_params ucast;
+       struct qed_filter_mcast_params mcast;
+};
+
+enum qed_filter_type {
+       QED_FILTER_TYPE_UCAST,
+       QED_FILTER_TYPE_MCAST,
+       QED_FILTER_TYPE_RX_MODE,
+       QED_MAX_FILTER_TYPES,
+};
+
+struct qed_filter_params {
+       enum qed_filter_type type;
+       union qed_filter_type_params filter;
+};
+
+struct qed_queue_start_common_params {
+       u8 rss_id;
+       u8 queue_id;
+       u8 vport_id;
+       u16 sb;
+       u16 sb_idx;
+};
+
+struct qed_eth_cb_ops {
+       struct qed_common_cb_ops common;
+};
+
+struct qed_eth_ops {
+       const struct qed_common_ops *common;
+
+       int (*fill_dev_info)(struct qed_dev *cdev,
+                            struct qed_dev_eth_info *info);
+
+       void (*register_ops)(struct qed_dev *cdev,
+                            struct qed_eth_cb_ops *ops,
+                            void *cookie);
+
+       int (*vport_start)(struct qed_dev *cdev,
+                          u8 vport_id, u16 mtu,
+                          u8 drop_ttl0_flg,
+                          u8 inner_vlan_removal_en_flg);
+
+       int (*vport_stop)(struct qed_dev *cdev,
+                         u8 vport_id);
+
+       int (*vport_update)(struct qed_dev *cdev,
+                           struct qed_update_vport_params *params);
+
+       int (*q_rx_start)(struct qed_dev *cdev,
+                         struct qed_queue_start_common_params *params,
+                         u16 bd_max_bytes,
+                         dma_addr_t bd_chain_phys_addr,
+                         dma_addr_t cqe_pbl_addr,
+                         u16 cqe_pbl_size,
+                         void __iomem **pp_prod);
+
+       int (*q_rx_stop)(struct qed_dev *cdev,
+                        struct qed_stop_rxq_params *params);
+
+       int (*q_tx_start)(struct qed_dev *cdev,
+                         struct qed_queue_start_common_params *params,
+                         dma_addr_t pbl_addr,
+                         u16 pbl_size,
+                         void __iomem **pp_doorbell);
+
+       int (*q_tx_stop)(struct qed_dev *cdev,
+                        struct qed_stop_txq_params *params);
+
+       int (*filter_config)(struct qed_dev *cdev,
+                            struct qed_filter_params *params);
+
+       int (*fastpath_stop)(struct qed_dev *cdev);
+
+       int (*eth_cqe_completion)(struct qed_dev *cdev,
+                                 u8 rss_id,
+                                 struct eth_slow_path_rx_cqe *cqe);
+
+       void (*get_vport_stats)(struct qed_dev *cdev,
+                               struct qed_eth_stats *stats);
+};
+
+const struct qed_eth_ops *qed_get_eth_ops(u32 version);
+void qed_put_eth_ops(void);
+
+#endif
diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h
new file mode 100644 (file)
index 0000000..dc9a135
--- /dev/null
@@ -0,0 +1,498 @@
+/* QLogic qed NIC Driver
+ *
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available under the terms of the GNU General Public License
+ * (GPL) Version 2, available from the file COPYING in the main directory of
+ * this source tree.
+ */
+
+#ifndef _QED_IF_H
+#define _QED_IF_H
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/qed/common_hsi.h>
+#include <linux/qed/qed_chain.h>
+
+#define DIRECT_REG_WR(reg_addr, val) writel((u32)val, \
+                                           (void __iomem *)(reg_addr))
+
+#define DIRECT_REG_RD(reg_addr) readl((void __iomem *)(reg_addr))
+
+#define QED_COALESCE_MAX 0xFF
+
+/* forward */
+struct qed_dev;
+
+struct qed_eth_pf_params {
+       /* The following parameters are used during HW-init
+        * and these parameters need to be passed as arguments
+        * to update_pf_params routine invoked before slowpath start
+        */
+       u16 num_cons;
+};
+
+struct qed_pf_params {
+       struct qed_eth_pf_params eth_pf_params;
+};
+
+enum qed_int_mode {
+       QED_INT_MODE_INTA,
+       QED_INT_MODE_MSIX,
+       QED_INT_MODE_MSI,
+       QED_INT_MODE_POLL,
+};
+
+struct qed_sb_info {
+       struct status_block     *sb_virt;
+       dma_addr_t              sb_phys;
+       u32                     sb_ack; /* Last given ack */
+       u16                     igu_sb_id;
+       void __iomem            *igu_addr;
+       u8                      flags;
+#define QED_SB_INFO_INIT        0x1
+#define QED_SB_INFO_SETUP       0x2
+
+       struct qed_dev          *cdev;
+};
+
+struct qed_dev_info {
+       unsigned long   pci_mem_start;
+       unsigned long   pci_mem_end;
+       unsigned int    pci_irq;
+       u8              num_hwfns;
+
+       u8              hw_mac[ETH_ALEN];
+       bool            is_mf;
+
+       /* FW version */
+       u16             fw_major;
+       u16             fw_minor;
+       u16             fw_rev;
+       u16             fw_eng;
+
+       /* MFW version */
+       u32             mfw_rev;
+
+       u32             flash_size;
+       u8              mf_mode;
+};
+
+enum qed_sb_type {
+       QED_SB_TYPE_L2_QUEUE,
+};
+
+enum qed_protocol {
+       QED_PROTOCOL_ETH,
+};
+
+struct qed_link_params {
+       bool    link_up;
+
+#define QED_LINK_OVERRIDE_SPEED_AUTONEG         BIT(0)
+#define QED_LINK_OVERRIDE_SPEED_ADV_SPEEDS      BIT(1)
+#define QED_LINK_OVERRIDE_SPEED_FORCED_SPEED    BIT(2)
+#define QED_LINK_OVERRIDE_PAUSE_CONFIG          BIT(3)
+       u32     override_flags;
+       bool    autoneg;
+       u32     adv_speeds;
+       u32     forced_speed;
+#define QED_LINK_PAUSE_AUTONEG_ENABLE           BIT(0)
+#define QED_LINK_PAUSE_RX_ENABLE                BIT(1)
+#define QED_LINK_PAUSE_TX_ENABLE                BIT(2)
+       u32     pause_config;
+};
+
+struct qed_link_output {
+       bool    link_up;
+
+       u32     supported_caps;         /* In SUPPORTED defs */
+       u32     advertised_caps;        /* In ADVERTISED defs */
+       u32     lp_caps;                /* In ADVERTISED defs */
+       u32     speed;                  /* In Mb/s */
+       u8      duplex;                 /* In DUPLEX defs */
+       u8      port;                   /* In PORT defs */
+       bool    autoneg;
+       u32     pause_config;
+};
+
+#define QED_DRV_VER_STR_SIZE 12
+struct qed_slowpath_params {
+       u32     int_mode;
+       u8      drv_major;
+       u8      drv_minor;
+       u8      drv_rev;
+       u8      drv_eng;
+       u8      name[QED_DRV_VER_STR_SIZE];
+};
+
+#define ILT_PAGE_SIZE_TCFC 0x8000 /* 32KB */
+
+struct qed_int_info {
+       struct msix_entry       *msix;
+       u8                      msix_cnt;
+
+       /* This should be updated by the protocol driver */
+       u8                      used_cnt;
+};
+
+struct qed_common_cb_ops {
+       void    (*link_update)(void                     *dev,
+                              struct qed_link_output   *link);
+};
+
+struct qed_common_ops {
+       struct qed_dev* (*probe)(struct pci_dev *dev,
+                                enum qed_protocol protocol,
+                                u32 dp_module, u8 dp_level);
+
+       void            (*remove)(struct qed_dev *cdev);
+
+       int             (*set_power_state)(struct qed_dev *cdev,
+                                          pci_power_t state);
+
+       void            (*set_id)(struct qed_dev *cdev,
+                                 char name[],
+                                 char ver_str[]);
+
+       /* Client drivers need to make this call before slowpath_start.
+        * PF params required for the call before slowpath_start is
+        * documented within the qed_pf_params structure definition.
+        */
+       void            (*update_pf_params)(struct qed_dev *cdev,
+                                           struct qed_pf_params *params);
+       int             (*slowpath_start)(struct qed_dev *cdev,
+                                         struct qed_slowpath_params *params);
+
+       int             (*slowpath_stop)(struct qed_dev *cdev);
+
+       /* Requests to use `cnt' interrupts for fastpath.
+        * upon success, returns number of interrupts allocated for fastpath.
+        */
+       int             (*set_fp_int)(struct qed_dev *cdev,
+                                     u16 cnt);
+
+       /* Fills `info' with pointers required for utilizing interrupts */
+       int             (*get_fp_int)(struct qed_dev *cdev,
+                                     struct qed_int_info *info);
+
+       u32             (*sb_init)(struct qed_dev *cdev,
+                                  struct qed_sb_info *sb_info,
+                                  void *sb_virt_addr,
+                                  dma_addr_t sb_phy_addr,
+                                  u16 sb_id,
+                                  enum qed_sb_type type);
+
+       u32             (*sb_release)(struct qed_dev *cdev,
+                                     struct qed_sb_info *sb_info,
+                                     u16 sb_id);
+
+       void            (*simd_handler_config)(struct qed_dev *cdev,
+                                              void *token,
+                                              int index,
+                                              void (*handler)(void *));
+
+       void            (*simd_handler_clean)(struct qed_dev *cdev,
+                                             int index);
+/**
+ * @brief set_link - set links according to params
+ *
+ * @param cdev
+ * @param params - values used to override the default link configuration
+ *
+ * @return 0 on success, error otherwise.
+ */
+       int             (*set_link)(struct qed_dev *cdev,
+                                   struct qed_link_params *params);
+
+/**
+ * @brief get_link - returns the current link state.
+ *
+ * @param cdev
+ * @param if_link - structure to be filled with current link configuration.
+ */
+       void            (*get_link)(struct qed_dev *cdev,
+                                   struct qed_link_output *if_link);
+
+/**
+ * @brief - drains chip in case Tx completions fail to arrive due to pause.
+ *
+ * @param cdev
+ */
+       int             (*drain)(struct qed_dev *cdev);
+
+/**
+ * @brief update_msglvl - update module debug level
+ *
+ * @param cdev
+ * @param dp_module
+ * @param dp_level
+ */
+       void            (*update_msglvl)(struct qed_dev *cdev,
+                                        u32 dp_module,
+                                        u8 dp_level);
+
+       int             (*chain_alloc)(struct qed_dev *cdev,
+                                      enum qed_chain_use_mode intended_use,
+                                      enum qed_chain_mode mode,
+                                      u16 num_elems,
+                                      size_t elem_size,
+                                      struct qed_chain *p_chain);
+
+       void            (*chain_free)(struct qed_dev *cdev,
+                                     struct qed_chain *p_chain);
+};
+
+/**
+ * @brief qed_get_protocol_version
+ *
+ * @param protocol
+ *
+ * @return version supported by qed for given protocol driver
+ */
+u32 qed_get_protocol_version(enum qed_protocol protocol);
+
+#define MASK_FIELD(_name, _value) \
+       ((_value) &= (_name ## _MASK))
+
+#define FIELD_VALUE(_name, _value) \
+       ((_value & _name ## _MASK) << _name ## _SHIFT)
+
+#define SET_FIELD(value, name, flag)                          \
+       do {                                                   \
+               (value) &= ~(name ## _MASK << name ## _SHIFT); \
+               (value) |= (((u64)flag) << (name ## _SHIFT));  \
+       } while (0)
+
+#define GET_FIELD(value, name) \
+       (((value) >> (name ## _SHIFT)) & name ## _MASK)
+
+/* Debug print definitions */
+#define DP_ERR(cdev, fmt, ...)                                              \
+               pr_err("[%s:%d(%s)]" fmt,                                    \
+                      __func__, __LINE__,                                   \
+                      DP_NAME(cdev) ? DP_NAME(cdev) : "",                   \
+                      ## __VA_ARGS__)                                       \
+
+#define DP_NOTICE(cdev, fmt, ...)                                    \
+       do {                                                          \
+               if (unlikely((cdev)->dp_level <= QED_LEVEL_NOTICE)) { \
+                       pr_notice("[%s:%d(%s)]" fmt,                  \
+                                 __func__, __LINE__,                 \
+                                 DP_NAME(cdev) ? DP_NAME(cdev) : "", \
+                                 ## __VA_ARGS__);                    \
+                                                                     \
+               }                                                     \
+       } while (0)
+
+#define DP_INFO(cdev, fmt, ...)                                              \
+       do {                                                          \
+               if (unlikely((cdev)->dp_level <= QED_LEVEL_INFO)) {   \
+                       pr_notice("[%s:%d(%s)]" fmt,                  \
+                                 __func__, __LINE__,                 \
+                                 DP_NAME(cdev) ? DP_NAME(cdev) : "", \
+                                 ## __VA_ARGS__);                    \
+               }                                                     \
+       } while (0)
+
+#define DP_VERBOSE(cdev, module, fmt, ...)                             \
+       do {                                                            \
+               if (unlikely(((cdev)->dp_level <= QED_LEVEL_VERBOSE) && \
+                            ((cdev)->dp_module & module))) {           \
+                       pr_notice("[%s:%d(%s)]" fmt,                    \
+                                 __func__, __LINE__,                   \
+                                 DP_NAME(cdev) ? DP_NAME(cdev) : "",   \
+                                 ## __VA_ARGS__);                      \
+               }                                                       \
+       } while (0)
+
+enum DP_LEVEL {
+       QED_LEVEL_VERBOSE       = 0x0,
+       QED_LEVEL_INFO          = 0x1,
+       QED_LEVEL_NOTICE        = 0x2,
+       QED_LEVEL_ERR           = 0x3,
+};
+
+#define QED_LOG_LEVEL_SHIFT     (30)
+#define QED_LOG_VERBOSE_MASK    (0x3fffffff)
+#define QED_LOG_INFO_MASK       (0x40000000)
+#define QED_LOG_NOTICE_MASK     (0x80000000)
+
+enum DP_MODULE {
+       QED_MSG_SPQ     = 0x10000,
+       QED_MSG_STATS   = 0x20000,
+       QED_MSG_DCB     = 0x40000,
+       QED_MSG_IOV     = 0x80000,
+       QED_MSG_SP      = 0x100000,
+       QED_MSG_STORAGE = 0x200000,
+       QED_MSG_CXT     = 0x800000,
+       QED_MSG_ILT     = 0x2000000,
+       QED_MSG_ROCE    = 0x4000000,
+       QED_MSG_DEBUG   = 0x8000000,
+       /* to be added...up to 0x8000000 */
+};
+
+struct qed_eth_stats {
+       u64     no_buff_discards;
+       u64     packet_too_big_discard;
+       u64     ttl0_discard;
+       u64     rx_ucast_bytes;
+       u64     rx_mcast_bytes;
+       u64     rx_bcast_bytes;
+       u64     rx_ucast_pkts;
+       u64     rx_mcast_pkts;
+       u64     rx_bcast_pkts;
+       u64     mftag_filter_discards;
+       u64     mac_filter_discards;
+       u64     tx_ucast_bytes;
+       u64     tx_mcast_bytes;
+       u64     tx_bcast_bytes;
+       u64     tx_ucast_pkts;
+       u64     tx_mcast_pkts;
+       u64     tx_bcast_pkts;
+       u64     tx_err_drop_pkts;
+       u64     tpa_coalesced_pkts;
+       u64     tpa_coalesced_events;
+       u64     tpa_aborts_num;
+       u64     tpa_not_coalesced_pkts;
+       u64     tpa_coalesced_bytes;
+
+       /* port */
+       u64     rx_64_byte_packets;
+       u64     rx_127_byte_packets;
+       u64     rx_255_byte_packets;
+       u64     rx_511_byte_packets;
+       u64     rx_1023_byte_packets;
+       u64     rx_1518_byte_packets;
+       u64     rx_1522_byte_packets;
+       u64     rx_2047_byte_packets;
+       u64     rx_4095_byte_packets;
+       u64     rx_9216_byte_packets;
+       u64     rx_16383_byte_packets;
+       u64     rx_crc_errors;
+       u64     rx_mac_crtl_frames;
+       u64     rx_pause_frames;
+       u64     rx_pfc_frames;
+       u64     rx_align_errors;
+       u64     rx_carrier_errors;
+       u64     rx_oversize_packets;
+       u64     rx_jabbers;
+       u64     rx_undersize_packets;
+       u64     rx_fragments;
+       u64     tx_64_byte_packets;
+       u64     tx_65_to_127_byte_packets;
+       u64     tx_128_to_255_byte_packets;
+       u64     tx_256_to_511_byte_packets;
+       u64     tx_512_to_1023_byte_packets;
+       u64     tx_1024_to_1518_byte_packets;
+       u64     tx_1519_to_2047_byte_packets;
+       u64     tx_2048_to_4095_byte_packets;
+       u64     tx_4096_to_9216_byte_packets;
+       u64     tx_9217_to_16383_byte_packets;
+       u64     tx_pause_frames;
+       u64     tx_pfc_frames;
+       u64     tx_lpi_entry_count;
+       u64     tx_total_collisions;
+       u64     brb_truncates;
+       u64     brb_discards;
+       u64     rx_mac_bytes;
+       u64     rx_mac_uc_packets;
+       u64     rx_mac_mc_packets;
+       u64     rx_mac_bc_packets;
+       u64     rx_mac_frames_ok;
+       u64     tx_mac_bytes;
+       u64     tx_mac_uc_packets;
+       u64     tx_mac_mc_packets;
+       u64     tx_mac_bc_packets;
+       u64     tx_mac_ctrl_frames;
+};
+
+#define QED_SB_IDX              0x0002
+
+#define RX_PI           0
+#define TX_PI(tc)       (RX_PI + 1 + tc)
+
+static inline u16 qed_sb_update_sb_idx(struct qed_sb_info *sb_info)
+{
+       u32 prod = 0;
+       u16 rc = 0;
+
+       prod = le32_to_cpu(sb_info->sb_virt->prod_index) &
+              STATUS_BLOCK_PROD_INDEX_MASK;
+       if (sb_info->sb_ack != prod) {
+               sb_info->sb_ack = prod;
+               rc |= QED_SB_IDX;
+       }
+
+       /* Let SB update */
+       mmiowb();
+       return rc;
+}
+
+/**
+ *
+ * @brief This function creates an update command for interrupts that is
+ *        written to the IGU.
+ *
+ * @param sb_info       - This is the structure allocated and
+ *                 initialized per status block. Assumption is
+ *                 that it was initialized using qed_sb_init
+ * @param int_cmd       - Enable/Disable/Nop
+ * @param upd_flg       - whether igu consumer should be
+ *                 updated.
+ *
+ * @return inline void
+ */
+static inline void qed_sb_ack(struct qed_sb_info *sb_info,
+                             enum igu_int_cmd int_cmd,
+                             u8 upd_flg)
+{
+       struct igu_prod_cons_update igu_ack = { 0 };
+
+       igu_ack.sb_id_and_flags =
+               ((sb_info->sb_ack << IGU_PROD_CONS_UPDATE_SB_INDEX_SHIFT) |
+                (upd_flg << IGU_PROD_CONS_UPDATE_UPDATE_FLAG_SHIFT) |
+                (int_cmd << IGU_PROD_CONS_UPDATE_ENABLE_INT_SHIFT) |
+                (IGU_SEG_ACCESS_REG <<
+                 IGU_PROD_CONS_UPDATE_SEGMENT_ACCESS_SHIFT));
+
+       DIRECT_REG_WR(sb_info->igu_addr, igu_ack.sb_id_and_flags);
+
+       /* Both segments (interrupts & acks) are written to same place address;
+        * Need to guarantee all commands will be received (in-order) by HW.
+        */
+       mmiowb();
+       barrier();
+}
+
+static inline void __internal_ram_wr(void *p_hwfn,
+                                    void __iomem *addr,
+                                    int size,
+                                    u32 *data)
+
+{
+       unsigned int i;
+
+       for (i = 0; i < size / sizeof(*data); i++)
+               DIRECT_REG_WR(&((u32 __iomem *)addr)[i], data[i]);
+}
+
+static inline void internal_ram_wr(void __iomem *addr,
+                                  int size,
+                                  u32 *data)
+{
+       __internal_ram_wr(NULL, addr, size, data);
+}
+
+#endif
index e651874df2c9d7f7e45187900d4c5de84df03307..a75840c1aa71414acc43468da6cc8f83ec06c1b8 100644 (file)
@@ -7,6 +7,8 @@
 #define _LINUX_RANDOM_H
 
 #include <linux/list.h>
+#include <linux/once.h>
+
 #include <uapi/linux/random.h>
 
 struct random_ready_callback {
@@ -45,6 +47,10 @@ struct rnd_state {
 
 u32 prandom_u32_state(struct rnd_state *state);
 void prandom_bytes_state(struct rnd_state *state, void *buf, size_t nbytes);
+void prandom_seed_full_state(struct rnd_state __percpu *pcpu_state);
+
+#define prandom_init_once(pcpu_state)                  \
+       DO_ONCE(prandom_seed_full_state, (pcpu_state))
 
 /**
  * prandom_u32_max - returns a pseudo-random number in interval [0, ep_ro)
diff --git a/include/linux/rcu_sync.h b/include/linux/rcu_sync.h
new file mode 100644 (file)
index 0000000..a63a33e
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * RCU-based infrastructure for lightweight reader-writer locking
+ *
+ * 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, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * Copyright (c) 2015, Red Hat, Inc.
+ *
+ * Author: Oleg Nesterov <oleg@redhat.com>
+ */
+
+#ifndef _LINUX_RCU_SYNC_H_
+#define _LINUX_RCU_SYNC_H_
+
+#include <linux/wait.h>
+#include <linux/rcupdate.h>
+
+enum rcu_sync_type { RCU_SYNC, RCU_SCHED_SYNC, RCU_BH_SYNC };
+
+/* Structure to mediate between updaters and fastpath-using readers.  */
+struct rcu_sync {
+       int                     gp_state;
+       int                     gp_count;
+       wait_queue_head_t       gp_wait;
+
+       int                     cb_state;
+       struct rcu_head         cb_head;
+
+       enum rcu_sync_type      gp_type;
+};
+
+extern void rcu_sync_lockdep_assert(struct rcu_sync *);
+
+/**
+ * rcu_sync_is_idle() - Are readers permitted to use their fastpaths?
+ * @rsp: Pointer to rcu_sync structure to use for synchronization
+ *
+ * Returns true if readers are permitted to use their fastpaths.
+ * Must be invoked within an RCU read-side critical section whose
+ * flavor matches that of the rcu_sync struture.
+ */
+static inline bool rcu_sync_is_idle(struct rcu_sync *rsp)
+{
+#ifdef CONFIG_PROVE_RCU
+       rcu_sync_lockdep_assert(rsp);
+#endif
+       return !rsp->gp_state; /* GP_IDLE */
+}
+
+extern void rcu_sync_init(struct rcu_sync *, enum rcu_sync_type);
+extern void rcu_sync_enter(struct rcu_sync *);
+extern void rcu_sync_exit(struct rcu_sync *);
+extern void rcu_sync_dtor(struct rcu_sync *);
+
+#define __RCU_SYNC_INITIALIZER(name, type) {                           \
+               .gp_state = 0,                                          \
+               .gp_count = 0,                                          \
+               .gp_wait = __WAIT_QUEUE_HEAD_INITIALIZER(name.gp_wait), \
+               .cb_state = 0,                                          \
+               .gp_type = type,                                        \
+       }
+
+#define        __DEFINE_RCU_SYNC(name, type)   \
+       struct rcu_sync_struct name = __RCU_SYNC_INITIALIZER(name, type)
+
+#define DEFINE_RCU_SYNC(name)          \
+       __DEFINE_RCU_SYNC(name, RCU_SYNC)
+
+#define DEFINE_RCU_SCHED_SYNC(name)    \
+       __DEFINE_RCU_SYNC(name, RCU_SCHED_SYNC)
+
+#define DEFINE_RCU_BH_SYNC(name)       \
+       __DEFINE_RCU_SYNC(name, RCU_BH_SYNC)
+
+#endif /* _LINUX_RCU_SYNC_H_ */
index 17c6b1f84a77d3b3073bc272fffc6a6138b322b5..5ed540986019b910ddee9645aff3605d13cf4b3b 100644 (file)
@@ -247,10 +247,7 @@ static inline void list_splice_init_rcu(struct list_head *list,
  * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
  */
 #define list_entry_rcu(ptr, type, member) \
-({ \
-       typeof(*ptr) __rcu *__ptr = (typeof(*ptr) __rcu __force *)ptr; \
-       container_of((typeof(ptr))rcu_dereference_raw(__ptr), type, member); \
-})
+       container_of(lockless_dereference(ptr), type, member)
 
 /**
  * Where are list_empty_rcu() and list_first_entry_rcu()?
index 581abf84856691ad2e5ce39f77f9c9112170061c..a0189ba67fde721a824cc7c8db4b1f015197059e 100644 (file)
@@ -160,7 +160,7 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
  * more than one CPU).
  */
 void call_rcu(struct rcu_head *head,
-             void (*func)(struct rcu_head *head));
+             rcu_callback_t func);
 
 #else /* #ifdef CONFIG_PREEMPT_RCU */
 
@@ -191,7 +191,7 @@ void call_rcu(struct rcu_head *head,
  * memory ordering guarantees.
  */
 void call_rcu_bh(struct rcu_head *head,
-                void (*func)(struct rcu_head *head));
+                rcu_callback_t func);
 
 /**
  * call_rcu_sched() - Queue an RCU for invocation after sched grace period.
@@ -213,7 +213,7 @@ void call_rcu_bh(struct rcu_head *head,
  * memory ordering guarantees.
  */
 void call_rcu_sched(struct rcu_head *head,
-                   void (*func)(struct rcu_head *rcu));
+                   rcu_callback_t func);
 
 void synchronize_sched(void);
 
@@ -274,7 +274,7 @@ do {                                                                        \
  * See the description of call_rcu() for more detailed information on
  * memory ordering guarantees.
  */
-void call_rcu_tasks(struct rcu_head *head, void (*func)(struct rcu_head *head));
+void call_rcu_tasks(struct rcu_head *head, rcu_callback_t func);
 void synchronize_rcu_tasks(void);
 void rcu_barrier_tasks(void);
 
@@ -297,12 +297,14 @@ void synchronize_rcu(void);
 
 static inline void __rcu_read_lock(void)
 {
-       preempt_disable();
+       if (IS_ENABLED(CONFIG_PREEMPT_COUNT))
+               preempt_disable();
 }
 
 static inline void __rcu_read_unlock(void)
 {
-       preempt_enable();
+       if (IS_ENABLED(CONFIG_PREEMPT_COUNT))
+               preempt_enable();
 }
 
 static inline void synchronize_rcu(void)
@@ -535,28 +537,8 @@ static inline int rcu_read_lock_sched_held(void)
 
 #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
-/* Deprecate rcu_lockdep_assert():  Use RCU_LOCKDEP_WARN() instead. */
-static inline void __attribute((deprecated)) deprecate_rcu_lockdep_assert(void)
-{
-}
-
 #ifdef CONFIG_PROVE_RCU
 
-/**
- * rcu_lockdep_assert - emit lockdep splat if specified condition not met
- * @c: condition to check
- * @s: informative message
- */
-#define rcu_lockdep_assert(c, s)                                       \
-       do {                                                            \
-               static bool __section(.data.unlikely) __warned;         \
-               deprecate_rcu_lockdep_assert();                         \
-               if (debug_lockdep_rcu_enabled() && !__warned && !(c)) { \
-                       __warned = true;                                \
-                       lockdep_rcu_suspicious(__FILE__, __LINE__, s);  \
-               }                                                       \
-       } while (0)
-
 /**
  * RCU_LOCKDEP_WARN - emit lockdep splat if specified condition is met
  * @c: condition to check
@@ -594,7 +576,6 @@ static inline void rcu_preempt_sleep_check(void)
 
 #else /* #ifdef CONFIG_PROVE_RCU */
 
-#define rcu_lockdep_assert(c, s) deprecate_rcu_lockdep_assert()
 #define RCU_LOCKDEP_WARN(c, s) do { } while (0)
 #define rcu_sleep_check() do { } while (0)
 
@@ -810,6 +791,28 @@ static inline void rcu_preempt_sleep_check(void)
  */
 #define rcu_dereference_sched(p) rcu_dereference_sched_check(p, 0)
 
+/**
+ * rcu_pointer_handoff() - Hand off a pointer from RCU to other mechanism
+ * @p: The pointer to hand off
+ *
+ * This is simply an identity function, but it documents where a pointer
+ * is handed off from RCU to some other synchronization mechanism, for
+ * example, reference counting or locking.  In C11, it would map to
+ * kill_dependency().  It could be used as follows:
+ *
+ *     rcu_read_lock();
+ *     p = rcu_dereference(gp);
+ *     long_lived = is_long_lived(p);
+ *     if (long_lived) {
+ *             if (!atomic_inc_not_zero(p->refcnt))
+ *                     long_lived = false;
+ *             else
+ *                     p = rcu_pointer_handoff(p);
+ *     }
+ *     rcu_read_unlock();
+ */
+#define rcu_pointer_handoff(p) (p)
+
 /**
  * rcu_read_lock() - mark the beginning of an RCU read-side critical section
  *
@@ -1065,7 +1068,7 @@ static inline notrace void rcu_read_unlock_sched_notrace(void)
 #define __kfree_rcu(head, offset) \
        do { \
                BUILD_BUG_ON(!__is_kfree_rcu_offset(offset)); \
-               kfree_call_rcu(head, (void (*)(struct rcu_head *))(unsigned long)(offset)); \
+               kfree_call_rcu(head, (rcu_callback_t)(unsigned long)(offset)); \
        } while (0)
 
 /**
index ff968b7af3a4b61c9496f5222a7f600a42f5eca6..4c1aaf9cce7b3ac767c69d7849715d69089a9b48 100644 (file)
@@ -83,7 +83,7 @@ static inline void synchronize_sched_expedited(void)
 }
 
 static inline void kfree_call_rcu(struct rcu_head *head,
-                                 void (*func)(struct rcu_head *rcu))
+                                 rcu_callback_t func)
 {
        call_rcu(head, func);
 }
@@ -216,6 +216,7 @@ static inline bool rcu_is_watching(void)
 
 static inline void rcu_all_qs(void)
 {
+       barrier(); /* Avoid RCU read-side critical sections leaking across. */
 }
 
 #endif /* __LINUX_RCUTINY_H */
index 5abec82f325ecf5e2a0c06c21d566f94f5559721..60d15a080d7c3dcde45cf458df5632dfc5b36ac2 100644 (file)
@@ -48,7 +48,7 @@ void synchronize_rcu_bh(void);
 void synchronize_sched_expedited(void);
 void synchronize_rcu_expedited(void);
 
-void kfree_call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu));
+void kfree_call_rcu(struct rcu_head *head, rcu_callback_t func);
 
 /**
  * synchronize_rcu_bh_expedited - Brute-force RCU-bh grace period
index 8fc0bfd8edc4434fc79fda8591323d6be41f0645..d68bb402120e2c1a6d640720ef384663ceaefebe 100644 (file)
@@ -296,6 +296,8 @@ typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg,
                                  unsigned int *val);
 typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg,
                                   unsigned int val);
+typedef int (*regmap_hw_reg_update_bits)(void *context, unsigned int reg,
+                                        unsigned int mask, unsigned int val);
 typedef struct regmap_async *(*regmap_hw_async_alloc)(void);
 typedef void (*regmap_hw_free_context)(void *context);
 
@@ -335,6 +337,7 @@ struct regmap_bus {
        regmap_hw_gather_write gather_write;
        regmap_hw_async_write async_write;
        regmap_hw_reg_write reg_write;
+       regmap_hw_reg_update_bits reg_update_bits;
        regmap_hw_read read;
        regmap_hw_reg_read reg_read;
        regmap_hw_free_context free_context;
@@ -791,6 +794,9 @@ struct regmap_irq {
        unsigned int mask;
 };
 
+#define REGMAP_IRQ_REG(_irq, _off, _mask)              \
+       [_irq] = { .reg_offset = (_off), .mask = (_mask) }
+
 /**
  * Description of a generic regmap irq_chip.  This is not intended to
  * handle every possible interrupt controller, but it should handle a
@@ -800,6 +806,8 @@ struct regmap_irq {
  *
  * @status_base: Base status register address.
  * @mask_base:   Base mask register address.
+ * @unmask_base:  Base unmask register address. for chips who have
+ *                separate mask and unmask registers
  * @ack_base:    Base ack address. If zero then the chip is clear on read.
  *               Using zero value is possible with @use_ack bit.
  * @wake_base:   Base address for wake enables.  If zero unsupported.
@@ -807,6 +815,7 @@ struct regmap_irq {
  * @init_ack_masked: Ack all masked interrupts once during initalization.
  * @mask_invert: Inverted mask register: cleared bits are masked out.
  * @use_ack:     Use @ack register even if it is zero.
+ * @ack_invert:  Inverted ack register: cleared bits for ack.
  * @wake_invert: Inverted wake register: cleared bits are wake enabled.
  * @runtime_pm:  Hold a runtime PM lock on the device when accessing it.
  *
@@ -820,12 +829,14 @@ struct regmap_irq_chip {
 
        unsigned int status_base;
        unsigned int mask_base;
+       unsigned int unmask_base;
        unsigned int ack_base;
        unsigned int wake_base;
        unsigned int irq_reg_stride;
        bool init_ack_masked:1;
        bool mask_invert:1;
        bool use_ack:1;
+       bool ack_invert:1;
        bool wake_invert:1;
        bool runtime_pm:1;
 
index 39adaa9529eb328c049ad650021363bf83122088..4be5048b1fbe70b106d1802f6b400e5a8c259ba6 100644 (file)
@@ -33,11 +33,11 @@ extern wait_queue_head_t netdev_unregistering_wq;
 extern struct mutex net_mutex;
 
 #ifdef CONFIG_PROVE_LOCKING
-extern int lockdep_rtnl_is_held(void);
+extern bool lockdep_rtnl_is_held(void);
 #else
-static inline int lockdep_rtnl_is_held(void)
+static inline bool lockdep_rtnl_is_held(void)
 {
-       return 1;
+       return true;
 }
 #endif /* #ifdef CONFIG_PROVE_LOCKING */
 
index b7b9501b41af4eab6a096601c3baf85c85854807..c115d617739d8e6f1f388c13069ad32cfca1bb3b 100644 (file)
@@ -599,33 +599,42 @@ struct task_cputime_atomic {
                .sum_exec_runtime = ATOMIC64_INIT(0),           \
        }
 
-#ifdef CONFIG_PREEMPT_COUNT
-#define PREEMPT_DISABLED       (1 + PREEMPT_ENABLED)
-#else
-#define PREEMPT_DISABLED       PREEMPT_ENABLED
-#endif
+#define PREEMPT_DISABLED       (PREEMPT_DISABLE_OFFSET + PREEMPT_ENABLED)
 
 /*
- * Disable preemption until the scheduler is running.
- * Reset by start_kernel()->sched_init()->init_idle().
+ * Disable preemption until the scheduler is running -- use an unconditional
+ * value so that it also works on !PREEMPT_COUNT kernels.
  *
- * We include PREEMPT_ACTIVE to avoid cond_resched() from working
- * before the scheduler is active -- see should_resched().
+ * Reset by start_kernel()->sched_init()->init_idle()->init_idle_preempt_count().
  */
-#define INIT_PREEMPT_COUNT     (PREEMPT_DISABLED + PREEMPT_ACTIVE)
+#define INIT_PREEMPT_COUNT     PREEMPT_OFFSET
+
+/*
+ * Initial preempt_count value; reflects the preempt_count schedule invariant
+ * which states that during context switches:
+ *
+ *    preempt_count() == 2*PREEMPT_DISABLE_OFFSET
+ *
+ * Note: PREEMPT_DISABLE_OFFSET is 0 for !PREEMPT_COUNT kernels.
+ * Note: See finish_task_switch().
+ */
+#define FORK_PREEMPT_COUNT     (2*PREEMPT_DISABLE_OFFSET + PREEMPT_ENABLED)
 
 /**
  * struct thread_group_cputimer - thread group interval timer counts
  * @cputime_atomic:    atomic thread group interval timers.
- * @running:           non-zero when there are timers running and
- *                     @cputime receives updates.
+ * @running:           true when there are timers running and
+ *                     @cputime_atomic receives updates.
+ * @checking_timer:    true when a thread in the group is in the
+ *                     process of checking for thread group timers.
  *
  * This structure contains the version of task_cputime, above, that is
  * used for thread group CPU timer calculations.
  */
 struct thread_group_cputimer {
        struct task_cputime_atomic cputime_atomic;
-       int running;
+       bool running;
+       bool checking_timer;
 };
 
 #include <linux/rwsem.h>
@@ -840,7 +849,7 @@ struct user_struct {
        struct hlist_node uidhash_node;
        kuid_t uid;
 
-#ifdef CONFIG_PERF_EVENTS
+#if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL)
        atomic_long_t locked_vm;
 #endif
 };
@@ -1139,8 +1148,6 @@ struct sched_domain_topology_level {
 #endif
 };
 
-extern struct sched_domain_topology_level *sched_domain_topology;
-
 extern void set_sched_topology(struct sched_domain_topology_level *tl);
 extern void wake_up_if_idle(int cpu);
 
@@ -1189,10 +1196,10 @@ struct load_weight {
 
 /*
  * The load_avg/util_avg accumulates an infinite geometric series.
- * 1) load_avg factors the amount of time that a sched_entity is
- * runnable on a rq into its weight. For cfs_rq, it is the aggregated
- * such weights of all runnable and blocked sched_entities.
- * 2) util_avg factors frequency scaling into the amount of time
+ * 1) load_avg factors frequency scaling into the amount of time that a
+ * sched_entity is runnable on a rq into its weight. For cfs_rq, it is the
+ * aggregated such weights of all runnable and blocked sched_entities.
+ * 2) util_avg factors frequency and cpu scaling into the amount of time
  * that a sched_entity is running on a CPU, in the range [0..SCHED_LOAD_SCALE].
  * For cfs_rq, it is the aggregated such times of all runnable and
  * blocked sched_entities.
@@ -1342,10 +1349,12 @@ struct sched_dl_entity {
 
 union rcu_special {
        struct {
-               bool blocked;
-               bool need_qs;
-       } b;
-       short s;
+               u8 blocked;
+               u8 need_qs;
+               u8 exp_need_qs;
+               u8 pad; /* Otherwise the compiler can store garbage here. */
+       } b; /* Bits. */
+       u32 s; /* Set of bits. */
 };
 struct rcu_node;
 
index 9d303b8847df28e4851746515fd9ee388e1cf8e1..9089a2ae913ddf4d12f10a7bcff209bbd1894400 100644 (file)
@@ -21,4 +21,9 @@ static inline int dl_task(struct task_struct *p)
        return dl_prio(p->prio);
 }
 
+static inline bool dl_time_before(u64 a, u64 b)
+{
+       return (s64)(a - b) < 0;
+}
+
 #endif /* _SCHED_DEADLINE_H */
index f4265039a94c8f655a6a2d340bfbda547eb97704..2296e6b2f690760e5b1c2ab33091349be8d9ef58 100644 (file)
@@ -95,4 +95,15 @@ static inline void get_seccomp_filter(struct task_struct *tsk)
        return;
 }
 #endif /* CONFIG_SECCOMP_FILTER */
+
+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
+extern long seccomp_get_filter(struct task_struct *task,
+                              unsigned long filter_off, void __user *data);
+#else
+static inline long seccomp_get_filter(struct task_struct *task,
+                                     unsigned long n, void __user *data)
+{
+       return -EINVAL;
+}
+#endif /* CONFIG_SECCOMP_FILTER && CONFIG_CHECKPOINT_RESTORE */
 #endif /* _LINUX_SECCOMP_H */
index 4398411236f16c3f87691162909dc6197fb62b08..24f4dfd94c517b3b387682509180dee161e0912d 100644 (file)
@@ -463,6 +463,15 @@ static inline u32 skb_mstamp_us_delta(const struct skb_mstamp *t1,
        return delta_us;
 }
 
+static inline bool skb_mstamp_after(const struct skb_mstamp *t1,
+                                   const struct skb_mstamp *t0)
+{
+       s32 diff = t1->stamp_jiffies - t0->stamp_jiffies;
+
+       if (!diff)
+               diff = t1->stamp_us - t0->stamp_us;
+       return diff > 0;
+}
 
 /** 
  *     struct sk_buff - socket buffer
index e6109a6cd8f65eb779163d1a084a6e0256a11db0..12910cf19869c7db32d1ca34ddda0931e80570d1 100644 (file)
@@ -24,9 +24,6 @@ struct smpboot_thread_data;
  *                     parked (cpu offline)
  * @unpark:            Optional unpark function, called when the thread is
  *                     unparked (cpu online)
- * @pre_unpark:                Optional unpark function, called before the thread is
- *                     unparked (cpu online). This is not guaranteed to be
- *                     called on the target cpu of the thread. Careful!
  * @cpumask:           Internal state.  To update which threads are unparked,
  *                     call smpboot_update_cpumask_percpu_thread().
  * @selfparking:       Thread is not parked by the park function.
@@ -42,7 +39,6 @@ struct smp_hotplug_thread {
        void                            (*cleanup)(unsigned int cpu, bool online);
        void                            (*park)(unsigned int cpu);
        void                            (*unpark)(unsigned int cpu);
-       void                            (*pre_unpark)(unsigned int cpu);
        cpumask_var_t                   cpumask;
        bool                            selfparking;
        const char                      *thread_comm;
index bdeb4567b71e55edc785d5173f5ccc15ec0cda69..f5f80c5643ac5669abbb1e3e736ded2f49b5f3a0 100644 (file)
@@ -215,8 +215,11 @@ static inline int srcu_read_lock_held(struct srcu_struct *sp)
  */
 static inline int srcu_read_lock(struct srcu_struct *sp) __acquires(sp)
 {
-       int retval = __srcu_read_lock(sp);
+       int retval;
 
+       preempt_disable();
+       retval = __srcu_read_lock(sp);
+       preempt_enable();
        rcu_lock_acquire(&(sp)->dep_map);
        return retval;
 }
index 414d924318ce1ba57a448a485d8594a99a9012d5..0adedca24c5bfbd4ca7a25b80641e76e0e638738 100644 (file)
@@ -33,6 +33,8 @@ void stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg,
                         struct cpu_stop_work *work_buf);
 int stop_cpus(const struct cpumask *cpumask, cpu_stop_fn_t fn, void *arg);
 int try_stop_cpus(const struct cpumask *cpumask, cpu_stop_fn_t fn, void *arg);
+void stop_machine_park(int cpu);
+void stop_machine_unpark(int cpu);
 
 #else  /* CONFIG_SMP */
 
index 48c3696e86457172ff9e9a907db15e44b7e92bb5..c906f453458119324414f984ee60056194093988 100644 (file)
@@ -112,10 +112,11 @@ struct tcp_request_sock_ops;
 struct tcp_request_sock {
        struct inet_request_sock        req;
        const struct tcp_request_sock_ops *af_specific;
+       struct skb_mstamp               snt_synack; /* first SYNACK sent time */
        bool                            tfo_listener;
+       u32                             txhash;
        u32                             rcv_isn;
        u32                             snt_isn;
-       u32                             snt_synack; /* synack sent time */
        u32                             last_oow_ack_time; /* last SYNACK */
        u32                             rcv_nxt; /* the ack # by SYNACK. For
                                                  * FastOpen it's the seq#
@@ -193,6 +194,12 @@ struct tcp_sock {
        u32     window_clamp;   /* Maximal window to advertise          */
        u32     rcv_ssthresh;   /* Current window clamp                 */
 
+       /* Information of the most recently (s)acked skb */
+       struct tcp_rack {
+               struct skb_mstamp mstamp; /* (Re)sent time of the skb */
+               u8 advanced; /* mstamp advanced since last lost marking */
+               u8 reord;    /* reordering detected */
+       } rack;
        u16     advmss;         /* Advertised MSS                       */
        u8      unused;
        u8      nonagle     : 4,/* Disable Nagle algorithm?             */
@@ -216,6 +223,9 @@ struct tcp_sock {
        u32     mdev_max_us;    /* maximal mdev for the last rtt period */
        u32     rttvar_us;      /* smoothed mdev_max                    */
        u32     rtt_seq;        /* sequence number to update rttvar     */
+       struct rtt_meas {
+               u32 rtt, ts;    /* RTT in usec and sampling time in jiffies. */
+       } rtt_min[3];
 
        u32     packets_out;    /* Packets which are "in flight"        */
        u32     retrans_out;    /* Retransmitted packets out            */
@@ -279,8 +289,6 @@ struct tcp_sock {
        int     lost_cnt_hint;
        u32     retransmit_high;        /* L-bits may be on up to this seqno */
 
-       u32     lost_retrans_low;       /* Sent seq after any rxmit (lowest) */
-
        u32     prior_ssthresh; /* ssthresh saved at recovery start     */
        u32     high_seq;       /* snd_nxt at onset of congestion       */
 
@@ -355,8 +363,8 @@ static inline struct tcp_sock *tcp_sk(const struct sock *sk)
 
 struct tcp_timewait_sock {
        struct inet_timewait_sock tw_sk;
-       u32                       tw_rcv_nxt;
-       u32                       tw_snd_nxt;
+#define tw_rcv_nxt tw_sk.__tw_common.skc_tw_rcv_nxt
+#define tw_snd_nxt tw_sk.__tw_common.skc_tw_snd_nxt
        u32                       tw_rcv_wnd;
        u32                       tw_ts_offset;
        u32                       tw_ts_recent;
@@ -381,25 +389,12 @@ static inline bool tcp_passive_fastopen(const struct sock *sk)
                tcp_sk(sk)->fastopen_rsk != NULL);
 }
 
-extern void tcp_sock_destruct(struct sock *sk);
-
-static inline int fastopen_init_queue(struct sock *sk, int backlog)
+static inline void fastopen_queue_tune(struct sock *sk, int backlog)
 {
-       struct request_sock_queue *queue =
-           &inet_csk(sk)->icsk_accept_queue;
-
-       if (queue->fastopenq == NULL) {
-               queue->fastopenq = kzalloc(
-                   sizeof(struct fastopen_queue),
-                   sk->sk_allocation);
-               if (queue->fastopenq == NULL)
-                       return -ENOMEM;
-
-               sk->sk_destruct = tcp_sock_destruct;
-               spin_lock_init(&queue->fastopenq->lock);
-       }
-       queue->fastopenq->max_qlen = backlog;
-       return 0;
+       struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
+       int somaxconn = READ_ONCE(sock_net(sk)->core.sysctl_somaxconn);
+
+       queue->fastopenq.max_qlen = min_t(unsigned int, backlog, somaxconn);
 }
 
 static inline void tcp_saved_syn_free(struct tcp_sock *tp)
index ba0ae09cbb21f2038c71104a435f6782f70c1668..ec89d846324cac714807f7c002339636c497e4d8 100644 (file)
@@ -263,8 +263,8 @@ extern void timekeeping_inject_sleeptime64(struct timespec64 *delta);
 /*
  * PPS accessor
  */
-extern void getnstime_raw_and_real(struct timespec *ts_raw,
-                                  struct timespec *ts_real);
+extern void ktime_get_raw_and_real_ts64(struct timespec64 *ts_raw,
+                                       struct timespec64 *ts_real);
 
 /*
  * Persistent clock related interfaces
index 9d3f1a5b6178a9dd1aa3914b35051b96f5ceb279..39c25dbebfe8043b71892840918a384b14bf6e70 100644 (file)
@@ -152,7 +152,7 @@ extern unsigned long tick_nsec;             /* SHIFTED_HZ period (nsec) */
 #define NTP_INTERVAL_LENGTH (NSEC_PER_SEC/NTP_INTERVAL_FREQ)
 
 extern int do_adjtimex(struct timex *);
-extern void hardpps(const struct timespec *, const struct timespec *);
+extern void hardpps(const struct timespec64 *, const struct timespec64 *);
 
 int read_current_timer(unsigned long *timer_val);
 void ntp_notify_cmos_timer(void);
diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h
new file mode 100644 (file)
index 0000000..b5706f9
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * USB CDC common helpers
+ *
+ * Copyright (c) 2015 Oliver Neukum <oneukum@suse.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 __LINUX_USB_CDC_H
+#define __LINUX_USB_CDC_H
+
+#include <uapi/linux/usb/cdc.h>
+
+/*
+ * inofficial magic numbers
+ */
+
+#define CDC_PHONET_MAGIC_NUMBER                0xAB
+
+/*
+ * parsing CDC headers
+ */
+
+struct usb_cdc_parsed_header {
+       struct usb_cdc_union_desc *usb_cdc_union_desc;
+       struct usb_cdc_header_desc *usb_cdc_header_desc;
+
+       struct usb_cdc_call_mgmt_descriptor *usb_cdc_call_mgmt_descriptor;
+       struct usb_cdc_acm_descriptor *usb_cdc_acm_descriptor;
+       struct usb_cdc_country_functional_desc *usb_cdc_country_functional_desc;
+       struct usb_cdc_network_terminal_desc *usb_cdc_network_terminal_desc;
+       struct usb_cdc_ether_desc *usb_cdc_ether_desc;
+       struct usb_cdc_dmm_desc *usb_cdc_dmm_desc;
+       struct usb_cdc_mdlm_desc *usb_cdc_mdlm_desc;
+       struct usb_cdc_mdlm_detail_desc *usb_cdc_mdlm_detail_desc;
+       struct usb_cdc_obex_desc *usb_cdc_obex_desc;
+       struct usb_cdc_ncm_desc *usb_cdc_ncm_desc;
+       struct usb_cdc_mbim_desc *usb_cdc_mbim_desc;
+       struct usb_cdc_mbim_extended_desc *usb_cdc_mbim_extended_desc;
+
+       bool phonet_magic_present;
+};
+
+struct usb_interface;
+int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
+                               struct usb_interface *intf,
+                               u8 *buffer,
+                               int buflen);
+
+#endif /* __LINUX_USB_CDC_H */
index 0ec598381f9766182db52f246afc2f0a5f28b36f..3bff87a25a42fc2bcf2cff58cd201cdb10452fe1 100644 (file)
@@ -182,22 +182,10 @@ pcpu_free_vm_areas(struct vm_struct **vms, int nr_vms)
 # endif
 #endif
 
-struct vmalloc_info {
-       unsigned long   used;
-       unsigned long   largest_chunk;
-};
-
 #ifdef CONFIG_MMU
 #define VMALLOC_TOTAL (VMALLOC_END - VMALLOC_START)
-extern void get_vmalloc_info(struct vmalloc_info *vmi);
 #else
-
 #define VMALLOC_TOTAL 0UL
-#define get_vmalloc_info(vmi)                  \
-do {                                           \
-       (vmi)->used = 0;                        \
-       (vmi)->largest_chunk = 0;               \
-} while (0)
 #endif
 
 #endif /* _LINUX_VMALLOC_H */
index a2f59ec98d24a1af1e8de056305b563b2ff94df7..cf3bc564ac033f76773e9c6ec12599df1776da8e 100644 (file)
 #include <net/ipv6.h>
 #include <net/net_namespace.h>
 
-#define UIP_802154_SHORTADDR_LEN       2  /* compressed ipv6 address length */
-#define UIP_IPH_LEN                    40 /* ipv6 fixed header size */
-#define UIP_PROTO_UDP                  17 /* ipv6 next header value for UDP */
-#define UIP_FRAGH_LEN                  8  /* ipv6 fragment header size */
+#define EUI64_ADDR_LEN         8
 
-/*
- * ipv6 address based on mac
- * second bit-flip (Universe/Local) is done according RFC2464
+#define LOWPAN_NHC_MAX_ID_LEN  1
+/* Maximum next header compression length which we currently support inclusive
+ * possible inline data.
  */
-#define is_addr_mac_addr_based(a, m) \
-       ((((a)->s6_addr[8])  == (((m)[0]) ^ 0x02)) &&   \
-        (((a)->s6_addr[9])  == (m)[1]) &&              \
-        (((a)->s6_addr[10]) == (m)[2]) &&              \
-        (((a)->s6_addr[11]) == (m)[3]) &&              \
-        (((a)->s6_addr[12]) == (m)[4]) &&              \
-        (((a)->s6_addr[13]) == (m)[5]) &&              \
-        (((a)->s6_addr[14]) == (m)[6]) &&              \
-        (((a)->s6_addr[15]) == (m)[7]))
-
-/*
- * check whether we can compress the IID to 16 bits,
- * it's possible for unicast adresses with first 49 bits are zero only.
- */
-#define lowpan_is_iid_16_bit_compressable(a)   \
-       ((((a)->s6_addr16[4]) == 0) &&          \
-        (((a)->s6_addr[10]) == 0) &&           \
-        (((a)->s6_addr[11]) == 0xff) &&        \
-        (((a)->s6_addr[12]) == 0xfe) &&        \
-        (((a)->s6_addr[13]) == 0))
-
-/* check whether the 112-bit gid of the multicast address is mappable to: */
-
-/* 48 bits, FFXX::00XX:XXXX:XXXX */
-#define lowpan_is_mcast_addr_compressable48(a) \
-       ((((a)->s6_addr16[1]) == 0) &&          \
-        (((a)->s6_addr16[2]) == 0) &&          \
-        (((a)->s6_addr16[3]) == 0) &&          \
-        (((a)->s6_addr16[4]) == 0) &&          \
-        (((a)->s6_addr[10]) == 0))
-
-/* 32 bits, FFXX::00XX:XXXX */
-#define lowpan_is_mcast_addr_compressable32(a) \
-       ((((a)->s6_addr16[1]) == 0) &&          \
-        (((a)->s6_addr16[2]) == 0) &&          \
-        (((a)->s6_addr16[3]) == 0) &&          \
-        (((a)->s6_addr16[4]) == 0) &&          \
-        (((a)->s6_addr16[5]) == 0) &&          \
-        (((a)->s6_addr[12]) == 0))
-
-/* 8 bits, FF02::00XX */
-#define lowpan_is_mcast_addr_compressable8(a)  \
-       ((((a)->s6_addr[1])  == 2) &&           \
-        (((a)->s6_addr16[1]) == 0) &&          \
-        (((a)->s6_addr16[2]) == 0) &&          \
-        (((a)->s6_addr16[3]) == 0) &&          \
-        (((a)->s6_addr16[4]) == 0) &&          \
-        (((a)->s6_addr16[5]) == 0) &&          \
-        (((a)->s6_addr16[6]) == 0) &&          \
-        (((a)->s6_addr[14]) == 0))
-
-#define lowpan_is_addr_broadcast(a)    \
-       ((((a)[0]) == 0xFF) &&  \
-        (((a)[1]) == 0xFF) &&  \
-        (((a)[2]) == 0xFF) &&  \
-        (((a)[3]) == 0xFF) &&  \
-        (((a)[4]) == 0xFF) &&  \
-        (((a)[5]) == 0xFF) &&  \
-        (((a)[6]) == 0xFF) &&  \
-        (((a)[7]) == 0xFF))
-
-#define LOWPAN_DISPATCH_IPV6   0x41 /* 01000001 = 65 */
-#define LOWPAN_DISPATCH_HC1    0x42 /* 01000010 = 66 */
-#define LOWPAN_DISPATCH_IPHC   0x60 /* 011xxxxx = ... */
-#define LOWPAN_DISPATCH_FRAG1  0xc0 /* 11000xxx */
-#define LOWPAN_DISPATCH_FRAGN  0xe0 /* 11100xxx */
-
-#define LOWPAN_DISPATCH_MASK   0xf8 /* 11111000 */
-
-#define LOWPAN_FRAG_TIMEOUT    (HZ * 60)       /* time-out 60 sec */
-
-#define LOWPAN_FRAG1_HEAD_SIZE 0x4
-#define LOWPAN_FRAGN_HEAD_SIZE 0x5
-
-/*
- * Values of fields within the IPHC encoding first byte
- * (C stands for compressed and I for inline)
+#define LOWPAN_NHC_MAX_HDR_LEN (sizeof(struct udphdr))
+/* Max IPHC Header len without IPv6 hdr specific inline data.
+ * Useful for getting the "extra" bytes we need at worst case compression.
+ *
+ * LOWPAN_IPHC + CID + LOWPAN_NHC_MAX_ID_LEN
  */
-#define LOWPAN_IPHC_TF         0x18
-
-#define LOWPAN_IPHC_FL_C       0x10
-#define LOWPAN_IPHC_TC_C       0x08
-#define LOWPAN_IPHC_NH_C       0x04
-#define LOWPAN_IPHC_TTL_1      0x01
-#define LOWPAN_IPHC_TTL_64     0x02
-#define LOWPAN_IPHC_TTL_255    0x03
-#define LOWPAN_IPHC_TTL_I      0x00
-
+#define LOWPAN_IPHC_MAX_HEADER_LEN     (2 + 1 + LOWPAN_NHC_MAX_ID_LEN)
+/* Maximum worst case IPHC header buffer size */
+#define LOWPAN_IPHC_MAX_HC_BUF_LEN     (sizeof(struct ipv6hdr) +       \
+                                        LOWPAN_IPHC_MAX_HEADER_LEN +   \
+                                        LOWPAN_NHC_MAX_HDR_LEN)
 
-/* Values of fields within the IPHC encoding second byte */
-#define LOWPAN_IPHC_CID                0x80
+#define LOWPAN_DISPATCH_IPV6           0x41 /* 01000001 = 65 */
+#define LOWPAN_DISPATCH_IPHC           0x60 /* 011xxxxx = ... */
+#define LOWPAN_DISPATCH_IPHC_MASK      0xe0
 
-#define LOWPAN_IPHC_ADDR_00    0x00
-#define LOWPAN_IPHC_ADDR_01    0x01
-#define LOWPAN_IPHC_ADDR_02    0x02
-#define LOWPAN_IPHC_ADDR_03    0x03
-
-#define LOWPAN_IPHC_SAC                0x40
-#define LOWPAN_IPHC_SAM                0x30
-
-#define LOWPAN_IPHC_SAM_BIT    4
-
-#define LOWPAN_IPHC_M          0x08
-#define LOWPAN_IPHC_DAC                0x04
-#define LOWPAN_IPHC_DAM_00     0x00
-#define LOWPAN_IPHC_DAM_01     0x01
-#define LOWPAN_IPHC_DAM_10     0x02
-#define LOWPAN_IPHC_DAM_11     0x03
-
-#define LOWPAN_IPHC_DAM_BIT    0
-/*
- * LOWPAN_UDP encoding (works together with IPHC)
- */
-#define LOWPAN_NHC_UDP_MASK            0xF8
-#define LOWPAN_NHC_UDP_ID              0xF0
-#define LOWPAN_NHC_UDP_CHECKSUMC       0x04
-#define LOWPAN_NHC_UDP_CHECKSUMI       0x00
-
-#define LOWPAN_NHC_UDP_4BIT_PORT       0xF0B0
-#define LOWPAN_NHC_UDP_4BIT_MASK       0xFFF0
-#define LOWPAN_NHC_UDP_8BIT_PORT       0xF000
-#define LOWPAN_NHC_UDP_8BIT_MASK       0xFF00
+static inline bool lowpan_is_ipv6(u8 dispatch)
+{
+       return dispatch == LOWPAN_DISPATCH_IPV6;
+}
 
-/* values for port compression, _with checksum_ ie bit 5 set to 0 */
-#define LOWPAN_NHC_UDP_CS_P_00 0xF0 /* all inline */
-#define LOWPAN_NHC_UDP_CS_P_01 0xF1 /* source 16bit inline,
-                                       dest = 0xF0 + 8 bit inline */
-#define LOWPAN_NHC_UDP_CS_P_10 0xF2 /* source = 0xF0 + 8bit inline,
-                                       dest = 16 bit inline */
-#define LOWPAN_NHC_UDP_CS_P_11 0xF3 /* source & dest = 0xF0B + 4bit inline */
-#define LOWPAN_NHC_UDP_CS_C    0x04 /* checksum elided */
+static inline bool lowpan_is_iphc(u8 dispatch)
+{
+       return (dispatch & LOWPAN_DISPATCH_IPHC_MASK) == LOWPAN_DISPATCH_IPHC;
+}
 
 #define LOWPAN_PRIV_SIZE(llpriv_size)  \
        (sizeof(struct lowpan_priv) + llpriv_size)
@@ -218,10 +109,23 @@ struct lowpan_priv *lowpan_priv(const struct net_device *dev)
        return netdev_priv(dev);
 }
 
+struct lowpan_802154_cb {
+       u16 d_tag;
+       unsigned int d_size;
+       u8 d_offset;
+};
+
+static inline
+struct lowpan_802154_cb *lowpan_802154_cb(const struct sk_buff *skb)
+{
+       BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(skb->cb));
+       return (struct lowpan_802154_cb *)skb->cb;
+}
+
 #ifdef DEBUG
 /* print data in line */
 static inline void raw_dump_inline(const char *caller, char *msg,
-                                  unsigned char *buf, int len)
+                                  const unsigned char *buf, int len)
 {
        if (msg)
                pr_debug("%s():%s: ", caller, msg);
@@ -236,7 +140,7 @@ static inline void raw_dump_inline(const char *caller, char *msg,
  * ...
  */
 static inline void raw_dump_table(const char *caller, char *msg,
-                                 unsigned char *buf, int len)
+                                 const unsigned char *buf, int len)
 {
        if (msg)
                pr_debug("%s():%s:\n", caller, msg);
@@ -245,24 +149,25 @@ static inline void raw_dump_table(const char *caller, char *msg,
 }
 #else
 static inline void raw_dump_table(const char *caller, char *msg,
-                                 unsigned char *buf, int len) { }
+                                 const unsigned char *buf, int len) { }
 static inline void raw_dump_inline(const char *caller, char *msg,
-                                  unsigned char *buf, int len) { }
+                                  const unsigned char *buf, int len) { }
 #endif
 
-static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val)
-{
-       if (unlikely(!pskb_may_pull(skb, 1)))
-               return -EINVAL;
-
-       *val = skb->data[0];
-       skb_pull(skb, 1);
-
-       return 0;
-}
-
-static inline bool lowpan_fetch_skb(struct sk_buff *skb,
-               void *data, const unsigned int len)
+/**
+ * lowpan_fetch_skb - getting inline data from 6LoWPAN header
+ *
+ * This function will pull data from sk buffer and put it into data to
+ * remove the 6LoWPAN inline data. This function returns true if the
+ * sk buffer is too small to pull the amount of data which is specified
+ * by len.
+ *
+ * @skb: the buffer where the inline data should be pulled from.
+ * @data: destination buffer for the inline data.
+ * @len: amount of data which should be pulled in bytes.
+ */
+static inline bool lowpan_fetch_skb(struct sk_buff *skb, void *data,
+                                   unsigned int len)
 {
        if (unlikely(!pskb_may_pull(skb, len)))
                return true;
@@ -280,129 +185,44 @@ static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data,
        *hc_ptr += len;
 }
 
-static inline u8 lowpan_addr_mode_size(const u8 addr_mode)
-{
-       static const u8 addr_sizes[] = {
-               [LOWPAN_IPHC_ADDR_00] = 16,
-               [LOWPAN_IPHC_ADDR_01] = 8,
-               [LOWPAN_IPHC_ADDR_02] = 2,
-               [LOWPAN_IPHC_ADDR_03] = 0,
-       };
-       return addr_sizes[addr_mode];
-}
-
-static inline u8 lowpan_next_hdr_size(const u8 h_enc, u16 *uncomp_header)
-{
-       u8 ret = 1;
-
-       if ((h_enc & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
-               *uncomp_header += sizeof(struct udphdr);
-
-               switch (h_enc & LOWPAN_NHC_UDP_CS_P_11) {
-               case LOWPAN_NHC_UDP_CS_P_00:
-                       ret += 4;
-                       break;
-               case LOWPAN_NHC_UDP_CS_P_01:
-               case LOWPAN_NHC_UDP_CS_P_10:
-                       ret += 3;
-                       break;
-               case LOWPAN_NHC_UDP_CS_P_11:
-                       ret++;
-                       break;
-               default:
-                       break;
-               }
-
-               if (!(h_enc & LOWPAN_NHC_UDP_CS_C))
-                       ret += 2;
-       }
-
-       return ret;
-}
+void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype);
 
 /**
- *     lowpan_uncompress_size - returns skb->len size with uncompressed header
- *     @skb: sk_buff with 6lowpan header inside
- *     @datagram_offset: optional to get the datagram_offset value
+ * lowpan_header_decompress - replace 6LoWPAN header with IPv6 header
  *
- *     Returns the skb->len with uncompressed header
+ * This function replaces the IPHC 6LoWPAN header which should be pointed at
+ * skb->data and skb_network_header, with the IPv6 header.
+ * It would be nice that the caller have the necessary headroom of IPv6 header
+ * and greatest Transport layer header, this would reduce the overhead for
+ * reallocate headroom.
+ *
+ * @skb: the buffer which should be manipulate.
+ * @dev: the lowpan net device pointer.
+ * @daddr: destination lladdr of mac header which is used for compression
+ *     methods.
+ * @saddr: source lladdr of mac header which is used for compression
+ *     methods.
  */
-static inline u16
-lowpan_uncompress_size(const struct sk_buff *skb, u16 *dgram_offset)
-{
-       u16 ret = 2, uncomp_header = sizeof(struct ipv6hdr);
-       u8 iphc0, iphc1, h_enc;
-
-       iphc0 = skb_network_header(skb)[0];
-       iphc1 = skb_network_header(skb)[1];
-
-       switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
-       case 0:
-               ret += 4;
-               break;
-       case 1:
-               ret += 3;
-               break;
-       case 2:
-               ret++;
-               break;
-       default:
-               break;
-       }
-
-       if (!(iphc0 & LOWPAN_IPHC_NH_C))
-               ret++;
-
-       if (!(iphc0 & 0x03))
-               ret++;
-
-       ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_SAM) >>
-                                    LOWPAN_IPHC_SAM_BIT);
-
-       if (iphc1 & LOWPAN_IPHC_M) {
-               switch ((iphc1 & LOWPAN_IPHC_DAM_11) >>
-                       LOWPAN_IPHC_DAM_BIT) {
-               case LOWPAN_IPHC_DAM_00:
-                       ret += 16;
-                       break;
-               case LOWPAN_IPHC_DAM_01:
-                       ret += 6;
-                       break;
-               case LOWPAN_IPHC_DAM_10:
-                       ret += 4;
-                       break;
-               case LOWPAN_IPHC_DAM_11:
-                       ret++;
-                       break;
-               default:
-                       break;
-               }
-       } else {
-               ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_DAM_11) >>
-                                            LOWPAN_IPHC_DAM_BIT);
-       }
-
-       if (iphc0 & LOWPAN_IPHC_NH_C) {
-               h_enc = skb_network_header(skb)[ret];
-               ret += lowpan_next_hdr_size(h_enc, &uncomp_header);
-       }
+int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
+                            const void *daddr, const void *saddr);
 
-       if (dgram_offset)
-               *dgram_offset = uncomp_header;
-
-       return skb->len + uncomp_header - ret;
-}
-
-void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype);
-
-int
-lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
-                        const u8 *saddr, const u8 saddr_type,
-                        const u8 saddr_len, const u8 *daddr,
-                        const u8 daddr_type, const u8 daddr_len,
-                        u8 iphc0, u8 iphc1);
-int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
-                       unsigned short type, const void *_daddr,
-                       const void *_saddr, unsigned int len);
+/**
+ * lowpan_header_compress - replace IPv6 header with 6LoWPAN header
+ *
+ * This function replaces the IPv6 header which should be pointed at
+ * skb->data and skb_network_header, with the IPHC 6LoWPAN header.
+ * The caller need to be sure that the sk buffer is not shared and at have
+ * at least a headroom which is smaller or equal LOWPAN_IPHC_MAX_HEADER_LEN,
+ * which is the IPHC "more bytes than IPv6 header" at worst case.
+ *
+ * @skb: the buffer which should be manipulate.
+ * @dev: the lowpan net device pointer.
+ * @daddr: destination lladdr of mac header which is used for compression
+ *     methods.
+ * @saddr: source lladdr of mac header which is used for compression
+ *     methods.
+ */
+int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
+                          const void *daddr, const void *saddr);
 
 #endif /* __6LOWPAN_H__ */
index b5474b1fcd8399e667e5f807ee99c1e6150f517a..78003dfb8539bd42f75f8c2d04cbb7dce872e1bf 100644 (file)
@@ -192,8 +192,7 @@ struct ipv6_stub {
        int (*ipv6_dst_lookup)(struct net *net, struct sock *sk,
                               struct dst_entry **dst, struct flowi6 *fl6);
        void (*udpv6_encap_enable)(void);
-       void (*ndisc_send_na)(struct net_device *dev, struct neighbour *neigh,
-                             const struct in6_addr *daddr,
+       void (*ndisc_send_na)(struct net_device *dev, const struct in6_addr *daddr,
                              const struct in6_addr *solicited_addr,
                              bool router, bool solicited, bool override, bool inc_opt);
        struct neigh_table *nd_tbl;
index 7d38e2ffd2566b3077b1e6450a00ef4b771d9839..a5563d27a3eb046c6f070b2b82ae82d196f5450c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * IEEE 802.15.4 inteface for userspace
+ * IEEE 802.15.4 interface for userspace
  *
  * Copyright 2007, 2008 Siemens AG
  *
index db639a4c5ab84cab3339193010641f0b0187db48..e9eb2d6791b3bf8ac70644346e720c89b7f0d1af 100644 (file)
@@ -22,6 +22,9 @@
 
 #include "vsock_addr.h"
 
+/* vsock-specific sock->sk_state constants */
+#define VSOCK_SS_LISTEN 255
+
 #define LAST_RESERVED_PORT 1023
 
 #define vsock_sk(__sk)    ((struct vsock_sock *)__sk)
index 38d8a34d35894ec4f101b4437faa2dca3140e97f..42844d7b154af95a393ba6c7157323d3405099a1 100644 (file)
@@ -122,12 +122,28 @@ struct bt_voice {
 __printf(1, 2)
 void bt_info(const char *fmt, ...);
 __printf(1, 2)
+void bt_warn(const char *fmt, ...);
+__printf(1, 2)
 void bt_err(const char *fmt, ...);
+__printf(1, 2)
+void bt_err_ratelimited(const char *fmt, ...);
 
 #define BT_INFO(fmt, ...)      bt_info(fmt "\n", ##__VA_ARGS__)
+#define BT_WARN(fmt, ...)      bt_warn(fmt "\n", ##__VA_ARGS__)
 #define BT_ERR(fmt, ...)       bt_err(fmt "\n", ##__VA_ARGS__)
 #define BT_DBG(fmt, ...)       pr_debug(fmt "\n", ##__VA_ARGS__)
 
+#define BT_ERR_RATELIMITED(fmt, ...) bt_err_ratelimited(fmt "\n", ##__VA_ARGS__)
+
+#define bt_dev_info(hdev, fmt, ...)                            \
+       BT_INFO("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
+#define bt_dev_warn(hdev, fmt, ...)                            \
+       BT_WARN("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
+#define bt_dev_err(hdev, fmt, ...)                             \
+       BT_ERR("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
+#define bt_dev_dbg(hdev, fmt, ...)                             \
+       BT_DBG("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
+
 /* Connection and socket states */
 enum {
        BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
@@ -280,22 +296,22 @@ typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
 typedef void (*hci_req_complete_skb_t)(struct hci_dev *hdev, u8 status,
                                       u16 opcode, struct sk_buff *skb);
 
-struct req_ctrl {
-       bool start;
-       u8 event;
-       hci_req_complete_t complete;
-       hci_req_complete_skb_t complete_skb;
+struct hci_ctrl {
+       __u16 opcode;
+       bool req_start;
+       u8 req_event;
+       hci_req_complete_t req_complete;
+       hci_req_complete_skb_t req_complete_skb;
 };
 
 struct bt_skb_cb {
        __u8 pkt_type;
        __u8 force_active;
-       __u16 opcode;
        __u16 expect;
        __u8 incoming:1;
        union {
                struct l2cap_ctrl l2cap;
-               struct req_ctrl req;
+               struct hci_ctrl hci;
        };
 };
 #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
index 7ca6690355ea08acd54aa87b3302f78702c206c7..0205b80cc90b2d6d569c1ce517266fcb96ff2bf7 100644 (file)
@@ -44,6 +44,9 @@
 #define HCI_DEV_DOWN                   4
 #define HCI_DEV_SUSPEND                        5
 #define HCI_DEV_RESUME                 6
+#define HCI_DEV_OPEN                   7
+#define HCI_DEV_CLOSE                  8
+#define HCI_DEV_SETUP                  9
 
 /* HCI notify events */
 #define HCI_NOTIFY_CONN_ADD            1
@@ -168,6 +171,15 @@ enum {
         * during the hdev->setup vendor callback.
         */
        HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
+
+       /* When this quirk is set, the enabling of diagnostic mode is
+        * not persistent over HCI Reset. Every time the controller
+        * is brought up it needs to be reprogrammed.
+        *
+        * This quirk can be set before hci_register_dev is called or
+        * during the hdev->setup vendor callback.
+        */
+       HCI_QUIRK_NON_PERSISTENT_DIAG,
 };
 
 /* HCI device flags */
@@ -238,6 +250,7 @@ enum {
        HCI_LE_SCAN_INTERRUPTED,
 
        HCI_DUT_MODE,
+       HCI_VENDOR_DIAG,
        HCI_FORCE_BREDR_SMP,
        HCI_FORCE_STATIC_ADDR,
 
@@ -260,6 +273,7 @@ enum {
 #define HCI_ACLDATA_PKT                0x02
 #define HCI_SCODATA_PKT                0x03
 #define HCI_EVENT_PKT          0x04
+#define HCI_DIAG_PKT           0xf0
 #define HCI_VENDOR_PKT         0xff
 
 /* HCI packet types */
index 9e1a59e01fa2f6316ae950833974aa628761cf51..1878d0a96333cef013fbad0b71defa0bb274d58b 100644 (file)
@@ -398,6 +398,8 @@ struct hci_dev {
        int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
        void (*notify)(struct hci_dev *hdev, unsigned int evt);
        void (*hw_error)(struct hci_dev *hdev, u8 code);
+       int (*post_init)(struct hci_dev *hdev);
+       int (*set_diag)(struct hci_dev *hdev, bool enable);
        int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
 };
 
@@ -469,6 +471,7 @@ struct hci_conn {
        struct delayed_work auto_accept_work;
        struct delayed_work idle_work;
        struct delayed_work le_conn_timeout;
+       struct work_struct  le_scan_cleanup;
 
        struct device   dev;
        struct dentry   *debugfs;
@@ -791,6 +794,30 @@ static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev,
        return NULL;
 }
 
+static inline struct hci_conn *hci_conn_hash_lookup_le(struct hci_dev *hdev,
+                                                      bdaddr_t *ba,
+                                                      __u8 ba_type)
+{
+       struct hci_conn_hash *h = &hdev->conn_hash;
+       struct hci_conn  *c;
+
+       rcu_read_lock();
+
+       list_for_each_entry_rcu(c, &h->list, list) {
+               if (c->type != LE_LINK)
+                      continue;
+
+               if (ba_type == c->dst_type && !bacmp(&c->dst, ba)) {
+                       rcu_read_unlock();
+                       return c;
+               }
+       }
+
+       rcu_read_unlock();
+
+       return NULL;
+}
+
 static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
                                                        __u8 type, __u16 state)
 {
@@ -987,6 +1014,7 @@ int hci_resume_dev(struct hci_dev *hdev);
 int hci_reset_dev(struct hci_dev *hdev);
 int hci_dev_open(__u16 dev);
 int hci_dev_close(__u16 dev);
+int hci_dev_do_close(struct hci_dev *hdev);
 int hci_dev_reset(__u16 dev);
 int hci_dev_reset_stat(__u16 dev);
 int hci_dev_cmd(unsigned int cmd, void __user *arg);
@@ -1014,9 +1042,6 @@ void hci_conn_params_clear_disabled(struct hci_dev *hdev);
 struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
                                                  bdaddr_t *addr,
                                                  u8 addr_type);
-struct hci_conn_params *hci_explicit_connect_lookup(struct hci_dev *hdev,
-                                                   bdaddr_t *addr,
-                                                   u8 addr_type);
 
 void hci_uuids_clear(struct hci_dev *hdev);
 
@@ -1065,6 +1090,7 @@ int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance);
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
 
 int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
+int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb);
 
 void hci_init_sysfs(struct hci_dev *hdev);
 void hci_conn_init_sysfs(struct hci_conn *conn);
@@ -1348,6 +1374,9 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
 
 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
 
+struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
+                            const void *param, u32 timeout);
+
 /* ----- HCI Sockets ----- */
 void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
 void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
@@ -1452,7 +1481,7 @@ void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 void mgmt_discovering(struct hci_dev *hdev, u8 discovering);
 bool mgmt_powering_down(struct hci_dev *hdev);
 void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent);
-void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk);
+void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk, bool persistent);
 void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk,
                   bool persistent);
 void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr,
index 77d1e5764185acc0aea30c3cc4fc7eb2a3d79509..2b67567cf28d6d3c9b762ab0226a7818464428d2 100644 (file)
@@ -39,6 +39,10 @@ struct hci_mon_hdr {
 #define HCI_MON_ACL_RX_PKT     5
 #define HCI_MON_SCO_TX_PKT     6
 #define HCI_MON_SCO_RX_PKT     7
+#define HCI_MON_OPEN_INDEX     8
+#define HCI_MON_CLOSE_INDEX    9
+#define HCI_MON_INDEX_INFO     10
+#define HCI_MON_VENDOR_DIAG    11
 
 struct hci_mon_new_index {
        __u8            type;
@@ -48,4 +52,10 @@ struct hci_mon_new_index {
 } __packed;
 #define HCI_MON_NEW_INDEX_SIZE 16
 
+struct hci_mon_index_info {
+       bdaddr_t        bdaddr;
+       __le16          manufacturer;
+} __packed;
+#define HCI_MON_INDEX_INFO_SIZE 8
+
 #endif /* __HCI_MON_H */
index c2a40a172fcdde4f97dd4d66d6102f9a561eefb4..f1fbc3b119623de61becd9c45e44c05211b97ec3 100644 (file)
@@ -297,8 +297,7 @@ void bond_3ad_bind_slave(struct slave *slave);
 void bond_3ad_unbind_slave(struct slave *slave);
 void bond_3ad_state_machine_handler(struct work_struct *);
 void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout);
-void bond_3ad_adapter_speed_changed(struct slave *slave);
-void bond_3ad_adapter_duplex_changed(struct slave *slave);
+void bond_3ad_adapter_speed_duplex_changed(struct slave *slave);
 void bond_3ad_handle_link_change(struct slave *slave, char link);
 int  bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info);
 int  __bond_3ad_get_active_agg_info(struct bonding *bond,
index f0889a2476439553f055b31b6c577b6eb03e4d55..2c7bdb81d30c80725b9e7d704f908609a38da6b8 100644 (file)
@@ -5,6 +5,7 @@
  *
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright 2015      Intel Deutschland GmbH
  *
  * 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
@@ -858,6 +859,8 @@ struct station_del_parameters {
 /**
  * enum cfg80211_station_type - the type of station being modified
  * @CFG80211_STA_AP_CLIENT: client of an AP interface
+ * @CFG80211_STA_AP_CLIENT_UNASSOC: client of an AP interface that is still
+ *     unassociated (update properties for this type of client is permitted)
  * @CFG80211_STA_AP_MLME_CLIENT: client of an AP interface that has
  *     the AP MLME in the device
  * @CFG80211_STA_AP_STA: AP station on managed interface
@@ -873,6 +876,7 @@ struct station_del_parameters {
  */
 enum cfg80211_station_type {
        CFG80211_STA_AP_CLIENT,
+       CFG80211_STA_AP_CLIENT_UNASSOC,
        CFG80211_STA_AP_MLME_CLIENT,
        CFG80211_STA_AP_STA,
        CFG80211_STA_IBSS,
@@ -1497,6 +1501,20 @@ struct cfg80211_match_set {
        s32 rssi_thold;
 };
 
+/**
+ * struct cfg80211_sched_scan_plan - scan plan for scheduled scan
+ *
+ * @interval: interval between scheduled scan iterations. In seconds.
+ * @iterations: number of scan iterations in this scan plan. Zero means
+ *     infinite loop.
+ *     The last scan plan will always have this parameter set to zero,
+ *     all other scan plans will have a finite number of iterations.
+ */
+struct cfg80211_sched_scan_plan {
+       u32 interval;
+       u32 iterations;
+};
+
 /**
  * struct cfg80211_sched_scan_request - scheduled scan request description
  *
@@ -1504,7 +1522,6 @@ struct cfg80211_match_set {
  * @n_ssids: number of SSIDs
  * @n_channels: total number of channels to scan
  * @scan_width: channel width for scanning
- * @interval: interval between each scheduled scan cycle
  * @ie: optional information element(s) to add into Probe Request or %NULL
  * @ie_len: length of ie in octets
  * @flags: bit field of flags controlling operation
@@ -1523,6 +1540,9 @@ struct cfg80211_match_set {
  * @mac_addr_mask: MAC address mask used with randomisation, bits that
  *     are 0 in the mask should be randomised, bits that are 1 should
  *     be taken from the @mac_addr
+ * @scan_plans: scan plans to be executed in this scheduled scan. Lowest
+ *     index must be executed first.
+ * @n_scan_plans: number of scan plans, at least 1.
  * @rcu_head: RCU callback used to free the struct
  * @owner_nlportid: netlink portid of owner (if this should is a request
  *     owned by a particular socket)
@@ -1536,7 +1556,6 @@ struct cfg80211_sched_scan_request {
        int n_ssids;
        u32 n_channels;
        enum nl80211_bss_scan_width scan_width;
-       u32 interval;
        const u8 *ie;
        size_t ie_len;
        u32 flags;
@@ -1544,6 +1563,8 @@ struct cfg80211_sched_scan_request {
        int n_match_sets;
        s32 min_rssi_thold;
        u32 delay;
+       struct cfg80211_sched_scan_plan *scan_plans;
+       int n_scan_plans;
 
        u8 mac_addr[ETH_ALEN] __aligned(2);
        u8 mac_addr_mask[ETH_ALEN] __aligned(2);
@@ -1572,6 +1593,26 @@ enum cfg80211_signal_type {
        CFG80211_SIGNAL_TYPE_UNSPEC,
 };
 
+/**
+ * struct cfg80211_inform_bss - BSS inform data
+ * @chan: channel the frame was received on
+ * @scan_width: scan width that was used
+ * @signal: signal strength value, according to the wiphy's
+ *     signal type
+ * @boottime_ns: timestamp (CLOCK_BOOTTIME) when the information was
+ *     received; should match the time when the frame was actually
+ *     received by the device (not just by the host, in case it was
+ *     buffered on the device) and be accurate to about 10ms.
+ *     If the frame isn't buffered, just passing the return value of
+ *     ktime_get_boot_ns() is likely appropriate.
+ */
+struct cfg80211_inform_bss {
+       struct ieee80211_channel *chan;
+       enum nl80211_bss_scan_width scan_width;
+       s32 signal;
+       u64 boottime_ns;
+};
+
 /**
  * struct cfg80211_bss_ie_data - BSS entry IE data
  * @tsf: TSF contained in the frame that carried these IEs
@@ -2358,6 +2399,10 @@ struct cfg80211_qos_map {
  * @set_power_mgmt: Configure WLAN power management. A timeout value of -1
  *     allows the driver to adjust the dynamic ps timeout value.
  * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
+ *     After configuration, the driver should (soon) send an event indicating
+ *     the current level is above/below the configured threshold; this may
+ *     need some care when the configuration is changed (without first being
+ *     disabled.)
  * @set_cqm_txe_config: Configure connection quality monitor TX error
  *     thresholds.
  * @sched_scan_start: Tell the driver to start a scheduled scan.
@@ -2971,12 +3016,21 @@ enum wiphy_vendor_command_flags {
  * @doit: callback for the operation, note that wdev is %NULL if the
  *     flags didn't ask for a wdev and non-%NULL otherwise; the data
  *     pointer may be %NULL if userspace provided no data at all
+ * @dumpit: dump callback, for transferring bigger/multiple items. The
+ *     @storage points to cb->args[5], ie. is preserved over the multiple
+ *     dumpit calls.
+ * It's recommended to not have the same sub command with both @doit and
+ * @dumpit, so that userspace can assume certain ones are get and others
+ * are used with dump requests.
  */
 struct wiphy_vendor_command {
        struct nl80211_vendor_cmd_info info;
        u32 flags;
        int (*doit)(struct wiphy *wiphy, struct wireless_dev *wdev,
                    const void *data, int data_len);
+       int (*dumpit)(struct wiphy *wiphy, struct wireless_dev *wdev,
+                     struct sk_buff *skb, const void *data, int data_len,
+                     unsigned long *storage);
 };
 
 /**
@@ -3044,6 +3098,12 @@ struct wiphy_vendor_command {
  *     include fixed IEs like supported rates
  * @max_sched_scan_ie_len: same as max_scan_ie_len, but for scheduled
  *     scans
+ * @max_sched_scan_plans: maximum number of scan plans (scan interval and number
+ *     of iterations) for scheduled scan supported by the device.
+ * @max_sched_scan_plan_interval: maximum interval (in seconds) for a
+ *     single scan plan supported by the device.
+ * @max_sched_scan_plan_iterations: maximum number of iterations for a single
+ *     scan plan supported by the device.
  * @coverage_class: current coverage class
  * @fw_version: firmware version for ethtool reporting
  * @hw_version: hardware version for ethtool reporting
@@ -3151,6 +3211,9 @@ struct wiphy {
        u8 max_match_sets;
        u16 max_scan_ie_len;
        u16 max_sched_scan_ie_len;
+       u32 max_sched_scan_plans;
+       u32 max_sched_scan_plan_interval;
+       u32 max_sched_scan_plan_iterations;
 
        int n_cipher_suites;
        const u32 *cipher_suites;
@@ -3946,14 +4009,11 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
 void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy);
 
 /**
- * cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame
- *
+ * cfg80211_inform_bss_frame_data - inform cfg80211 of a received BSS frame
  * @wiphy: the wiphy reporting the BSS
- * @rx_channel: The channel the frame was received on
- * @scan_width: width of the control channel
+ * @data: the BSS metadata
  * @mgmt: the management frame (probe response or beacon)
  * @len: length of the management frame
- * @signal: the signal strength, type depends on the wiphy's signal_type
  * @gfp: context flags
  *
  * This informs cfg80211 that BSS information was found and
@@ -3963,11 +4023,26 @@ void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy);
  * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
+cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
+                              struct cfg80211_inform_bss *data,
+                              struct ieee80211_mgmt *mgmt, size_t len,
+                              gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
 cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
                                struct ieee80211_channel *rx_channel,
                                enum nl80211_bss_scan_width scan_width,
                                struct ieee80211_mgmt *mgmt, size_t len,
-                               s32 signal, gfp_t gfp);
+                               s32 signal, gfp_t gfp)
+{
+       struct cfg80211_inform_bss data = {
+               .chan = rx_channel,
+               .scan_width = scan_width,
+               .signal = signal,
+       };
+
+       return cfg80211_inform_bss_frame_data(wiphy, &data, mgmt, len, gfp);
+}
 
 static inline struct cfg80211_bss * __must_check
 cfg80211_inform_bss_frame(struct wiphy *wiphy,
@@ -3975,9 +4050,13 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
                          struct ieee80211_mgmt *mgmt, size_t len,
                          s32 signal, gfp_t gfp)
 {
-       return cfg80211_inform_bss_width_frame(wiphy, rx_channel,
-                                              NL80211_BSS_CHAN_WIDTH_20,
-                                              mgmt, len, signal, gfp);
+       struct cfg80211_inform_bss data = {
+               .chan = rx_channel,
+               .scan_width = NL80211_BSS_CHAN_WIDTH_20,
+               .signal = signal,
+       };
+
+       return cfg80211_inform_bss_frame_data(wiphy, &data, mgmt, len, gfp);
 }
 
 /**
@@ -3994,11 +4073,10 @@ enum cfg80211_bss_frame_type {
 };
 
 /**
- * cfg80211_inform_bss_width - inform cfg80211 of a new BSS
+ * cfg80211_inform_bss_data - inform cfg80211 of a new BSS
  *
  * @wiphy: the wiphy reporting the BSS
- * @rx_channel: The channel the frame was received on
- * @scan_width: width of the control channel
+ * @data: the BSS metadata
  * @ftype: frame type (if known)
  * @bssid: the BSSID of the BSS
  * @tsf: the TSF sent by the peer in the beacon/probe response (or 0)
@@ -4006,7 +4084,6 @@ enum cfg80211_bss_frame_type {
  * @beacon_interval: the beacon interval announced by the peer
  * @ie: additional IEs sent by the peer
  * @ielen: length of the additional IEs
- * @signal: the signal strength, type depends on the wiphy's signal_type
  * @gfp: context flags
  *
  * This informs cfg80211 that BSS information was found and
@@ -4016,13 +4093,32 @@ enum cfg80211_bss_frame_type {
  * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
+cfg80211_inform_bss_data(struct wiphy *wiphy,
+                        struct cfg80211_inform_bss *data,
+                        enum cfg80211_bss_frame_type ftype,
+                        const u8 *bssid, u64 tsf, u16 capability,
+                        u16 beacon_interval, const u8 *ie, size_t ielen,
+                        gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
 cfg80211_inform_bss_width(struct wiphy *wiphy,
                          struct ieee80211_channel *rx_channel,
                          enum nl80211_bss_scan_width scan_width,
                          enum cfg80211_bss_frame_type ftype,
                          const u8 *bssid, u64 tsf, u16 capability,
                          u16 beacon_interval, const u8 *ie, size_t ielen,
-                         s32 signal, gfp_t gfp);
+                         s32 signal, gfp_t gfp)
+{
+       struct cfg80211_inform_bss data = {
+               .chan = rx_channel,
+               .scan_width = scan_width,
+               .signal = signal,
+       };
+
+       return cfg80211_inform_bss_data(wiphy, &data, ftype, bssid, tsf,
+                                       capability, beacon_interval, ie, ielen,
+                                       gfp);
+}
 
 static inline struct cfg80211_bss * __must_check
 cfg80211_inform_bss(struct wiphy *wiphy,
@@ -4032,11 +4128,15 @@ cfg80211_inform_bss(struct wiphy *wiphy,
                    u16 beacon_interval, const u8 *ie, size_t ielen,
                    s32 signal, gfp_t gfp)
 {
-       return cfg80211_inform_bss_width(wiphy, rx_channel,
-                                        NL80211_BSS_CHAN_WIDTH_20, ftype,
-                                        bssid, tsf, capability,
-                                        beacon_interval, ie, ielen, signal,
-                                        gfp);
+       struct cfg80211_inform_bss data = {
+               .chan = rx_channel,
+               .scan_width = NL80211_BSS_CHAN_WIDTH_20,
+               .signal = signal,
+       };
+
+       return cfg80211_inform_bss_data(wiphy, &data, ftype, bssid, tsf,
+                                       capability, beacon_interval, ie, ielen,
+                                       gfp);
 }
 
 struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
index 76b1ffaea863600be09280cc4c2d168dc5bae41f..171cd76558fb8245fc267cbdddc7e9d675f28e39 100644 (file)
 struct wpan_phy;
 struct wpan_phy_cca;
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+struct ieee802154_llsec_device_key;
+struct ieee802154_llsec_seclevel;
+struct ieee802154_llsec_params;
+struct ieee802154_llsec_device;
+struct ieee802154_llsec_table;
+struct ieee802154_llsec_key_id;
+struct ieee802154_llsec_key;
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 struct cfg802154_ops {
        struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
                                                           const char *name,
@@ -65,6 +75,51 @@ struct cfg802154_ops {
                                struct wpan_dev *wpan_dev, bool mode);
        int     (*set_ackreq_default)(struct wpan_phy *wpan_phy,
                                      struct wpan_dev *wpan_dev, bool ackreq);
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+       void    (*get_llsec_table)(struct wpan_phy *wpan_phy,
+                                  struct wpan_dev *wpan_dev,
+                                  struct ieee802154_llsec_table **table);
+       void    (*lock_llsec_table)(struct wpan_phy *wpan_phy,
+                                   struct wpan_dev *wpan_dev);
+       void    (*unlock_llsec_table)(struct wpan_phy *wpan_phy,
+                                     struct wpan_dev *wpan_dev);
+       /* TODO remove locking/get table callbacks, this is part of the
+        * nl802154 interface and should be accessible from ieee802154 layer.
+        */
+       int     (*get_llsec_params)(struct wpan_phy *wpan_phy,
+                                   struct wpan_dev *wpan_dev,
+                                   struct ieee802154_llsec_params *params);
+       int     (*set_llsec_params)(struct wpan_phy *wpan_phy,
+                                   struct wpan_dev *wpan_dev,
+                                   const struct ieee802154_llsec_params *params,
+                                   int changed);
+       int     (*add_llsec_key)(struct wpan_phy *wpan_phy,
+                                struct wpan_dev *wpan_dev,
+                                const struct ieee802154_llsec_key_id *id,
+                                const struct ieee802154_llsec_key *key);
+       int     (*del_llsec_key)(struct wpan_phy *wpan_phy,
+                                struct wpan_dev *wpan_dev,
+                                const struct ieee802154_llsec_key_id *id);
+       int     (*add_seclevel)(struct wpan_phy *wpan_phy,
+                                struct wpan_dev *wpan_dev,
+                                const struct ieee802154_llsec_seclevel *sl);
+       int     (*del_seclevel)(struct wpan_phy *wpan_phy,
+                                struct wpan_dev *wpan_dev,
+                                const struct ieee802154_llsec_seclevel *sl);
+       int     (*add_device)(struct wpan_phy *wpan_phy,
+                             struct wpan_dev *wpan_dev,
+                             const struct ieee802154_llsec_device *dev);
+       int     (*del_device)(struct wpan_phy *wpan_phy,
+                             struct wpan_dev *wpan_dev, __le64 extended_addr);
+       int     (*add_devkey)(struct wpan_phy *wpan_phy,
+                             struct wpan_dev *wpan_dev,
+                             __le64 extended_addr,
+                             const struct ieee802154_llsec_device_key *key);
+       int     (*del_devkey)(struct wpan_phy *wpan_phy,
+                             struct wpan_dev *wpan_dev,
+                             __le64 extended_addr,
+                             const struct ieee802154_llsec_device_key *key);
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
 };
 
 static inline bool
@@ -167,6 +222,102 @@ struct wpan_phy {
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
+struct ieee802154_addr {
+       u8 mode;
+       __le16 pan_id;
+       union {
+               __le16 short_addr;
+               __le64 extended_addr;
+       };
+};
+
+struct ieee802154_llsec_key_id {
+       u8 mode;
+       u8 id;
+       union {
+               struct ieee802154_addr device_addr;
+               __le32 short_source;
+               __le64 extended_source;
+       };
+};
+
+#define IEEE802154_LLSEC_KEY_SIZE 16
+
+struct ieee802154_llsec_key {
+       u8 frame_types;
+       u32 cmd_frame_ids;
+       /* TODO replace with NL802154_KEY_SIZE */
+       u8 key[IEEE802154_LLSEC_KEY_SIZE];
+};
+
+struct ieee802154_llsec_key_entry {
+       struct list_head list;
+
+       struct ieee802154_llsec_key_id id;
+       struct ieee802154_llsec_key *key;
+};
+
+struct ieee802154_llsec_params {
+       bool enabled;
+
+       __be32 frame_counter;
+       u8 out_level;
+       struct ieee802154_llsec_key_id out_key;
+
+       __le64 default_key_source;
+
+       __le16 pan_id;
+       __le64 hwaddr;
+       __le64 coord_hwaddr;
+       __le16 coord_shortaddr;
+};
+
+struct ieee802154_llsec_table {
+       struct list_head keys;
+       struct list_head devices;
+       struct list_head security_levels;
+};
+
+struct ieee802154_llsec_seclevel {
+       struct list_head list;
+
+       u8 frame_type;
+       u8 cmd_frame_id;
+       bool device_override;
+       u32 sec_levels;
+};
+
+struct ieee802154_llsec_device {
+       struct list_head list;
+
+       __le16 pan_id;
+       __le16 short_addr;
+       __le64 hwaddr;
+       u32 frame_counter;
+       bool seclevel_exempt;
+
+       u8 key_mode;
+       struct list_head keys;
+};
+
+struct ieee802154_llsec_device_key {
+       struct list_head list;
+
+       struct ieee802154_llsec_key_id key_id;
+       u32 frame_counter;
+};
+
+struct wpan_dev_header_ops {
+       /* TODO create callback currently assumes ieee802154_mac_cb inside
+        * skb->cb. This should be changed to give these information as
+        * parameter.
+        */
+       int     (*create)(struct sk_buff *skb, struct net_device *dev,
+                         const struct ieee802154_addr *daddr,
+                         const struct ieee802154_addr *saddr,
+                         unsigned int len);
+};
+
 struct wpan_dev {
        struct wpan_phy *wpan_phy;
        int iftype;
@@ -175,6 +326,8 @@ struct wpan_dev {
        struct list_head list;
        struct net_device *netdev;
 
+       const struct wpan_dev_header_ops *header_ops;
+
        /* lowpan interface, set when the wpan_dev belongs to one lowpan_dev */
        struct net_device *lowpan_dev;
 
@@ -205,6 +358,17 @@ struct wpan_dev {
 
 #define to_phy(_dev)   container_of(_dev, struct wpan_phy, dev)
 
+static inline int
+wpan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
+                    const struct ieee802154_addr *daddr,
+                    const struct ieee802154_addr *saddr,
+                    unsigned int len)
+{
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+
+       return wpan_dev->header_ops->create(skb, dev, daddr, saddr, len);
+}
+
 struct wpan_phy *
 wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size);
 static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev)
index d0424269313fe2a84df0db71e07271e033756c9a..5e902fc3f4ebc54d67329cb1430e5d873b3e15de 100644 (file)
@@ -18,11 +18,11 @@ struct dn_neigh {
 
 void dn_neigh_init(void);
 void dn_neigh_cleanup(void);
-int dn_neigh_router_hello(struct sock *sk, struct sk_buff *skb);
-int dn_neigh_endnode_hello(struct sock *sk, struct sk_buff *skb);
+int dn_neigh_router_hello(struct net *net, struct sock *sk, struct sk_buff *skb);
+int dn_neigh_endnode_hello(struct net *net, struct sock *sk, struct sk_buff *skb);
 void dn_neigh_pointopoint_hello(struct sk_buff *skb);
 int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n);
-int dn_to_neigh_output(struct sock *sk, struct sk_buff *skb);
+int dn_to_neigh_output(struct net *net, struct sock *sk, struct sk_buff *skb);
 
 extern struct neigh_table dn_neigh_table;
 
index b34d812bc5d056d47b7d39181668572cde4f53ca..82a4c60111733e98310a12d569fb999c8e5ac412 100644 (file)
@@ -197,6 +197,11 @@ static inline u8 dsa_upstream_port(struct dsa_switch *ds)
                return ds->pd->rtable[dst->cpu_switch];
 }
 
+struct switchdev_trans;
+struct switchdev_obj;
+struct switchdev_obj_port_fdb;
+struct switchdev_obj_port_vlan;
+
 struct dsa_switch_driver {
        struct list_head        list;
 
@@ -305,24 +310,32 @@ struct dsa_switch_driver {
        /*
         * VLAN support
         */
+       int     (*port_vlan_prepare)(struct dsa_switch *ds, int port,
+                                    const struct switchdev_obj_port_vlan *vlan,
+                                    struct switchdev_trans *trans);
+       int     (*port_vlan_add)(struct dsa_switch *ds, int port,
+                                const struct switchdev_obj_port_vlan *vlan,
+                                struct switchdev_trans *trans);
+       int     (*port_vlan_del)(struct dsa_switch *ds, int port,
+                                const struct switchdev_obj_port_vlan *vlan);
        int     (*port_pvid_get)(struct dsa_switch *ds, int port, u16 *pvid);
-       int     (*port_pvid_set)(struct dsa_switch *ds, int port, u16 pvid);
-       int     (*port_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
-                                bool untagged);
-       int     (*port_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
        int     (*vlan_getnext)(struct dsa_switch *ds, u16 *vid,
                                unsigned long *ports, unsigned long *untagged);
 
        /*
         * Forwarding database
         */
+       int     (*port_fdb_prepare)(struct dsa_switch *ds, int port,
+                                   const struct switchdev_obj_port_fdb *fdb,
+                                   struct switchdev_trans *trans);
        int     (*port_fdb_add)(struct dsa_switch *ds, int port,
-                               const unsigned char *addr, u16 vid);
+                               const struct switchdev_obj_port_fdb *fdb,
+                               struct switchdev_trans *trans);
        int     (*port_fdb_del)(struct dsa_switch *ds, int port,
-                               const unsigned char *addr, u16 vid);
-       int     (*port_fdb_getnext)(struct dsa_switch *ds, int port,
-                                   unsigned char *addr, u16 *vid,
-                                   bool *is_static);
+                               const struct switchdev_obj_port_fdb *fdb);
+       int     (*port_fdb_dump)(struct dsa_switch *ds, int port,
+                                struct switchdev_obj_port_fdb *fdb,
+                                int (*cb)(struct switchdev_obj *obj));
 };
 
 void register_switch_driver(struct dsa_switch_driver *type);
index 9261d928303d475a8ef2772144f9f70e0febda49..1279f9b09791ace6885b0dd67c2c28d578b6cd8e 100644 (file)
@@ -45,7 +45,7 @@ struct dst_entry {
        void                    *__pad1;
 #endif
        int                     (*input)(struct sk_buff *);
-       int                     (*output)(struct sock *sk, struct sk_buff *skb);
+       int                     (*output)(struct net *net, struct sock *sk, struct sk_buff *skb);
 
        unsigned short          flags;
 #define DST_HOST               0x0001
@@ -365,10 +365,10 @@ static inline void skb_tunnel_rx(struct sk_buff *skb, struct net_device *dev,
        __skb_tunnel_rx(skb, dev, net);
 }
 
-int dst_discard_sk(struct sock *sk, struct sk_buff *skb);
+int dst_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
 static inline int dst_discard(struct sk_buff *skb)
 {
-       return dst_discard_sk(skb->sk, skb);
+       return dst_discard_out(&init_net, skb->sk, skb);
 }
 void *dst_alloc(struct dst_ops *ops, struct net_device *dev, int initial_ref,
                int initial_obsolete, unsigned short flags);
@@ -454,13 +454,9 @@ static inline void dst_set_expires(struct dst_entry *dst, int timeout)
 }
 
 /* Output packet to network from transport.  */
-static inline int dst_output_sk(struct sock *sk, struct sk_buff *skb)
+static inline int dst_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-       return skb_dst(skb)->output(sk, skb);
-}
-static inline int dst_output(struct sk_buff *skb)
-{
-       return dst_output_sk(skb->sk, skb);
+       return skb_dst(skb)->output(net, sk, skb);
 }
 
 /* Input packet from network to transport.  */
@@ -489,7 +485,8 @@ struct flowi;
 #ifndef CONFIG_XFRM
 static inline struct dst_entry *xfrm_lookup(struct net *net,
                                            struct dst_entry *dst_orig,
-                                           const struct flowi *fl, struct sock *sk,
+                                           const struct flowi *fl,
+                                           const struct sock *sk,
                                            int flags)
 {
        return dst_orig;
@@ -498,7 +495,7 @@ static inline struct dst_entry *xfrm_lookup(struct net *net,
 static inline struct dst_entry *xfrm_lookup_route(struct net *net,
                                                  struct dst_entry *dst_orig,
                                                  const struct flowi *fl,
-                                                 struct sock *sk,
+                                                 const struct sock *sk,
                                                  int flags)
 {
        return dst_orig;
@@ -511,11 +508,11 @@ static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst)
 
 #else
 struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
-                             const struct flowi *fl, struct sock *sk,
+                             const struct flowi *fl, const struct sock *sk,
                              int flags);
 
 struct dst_entry *xfrm_lookup_route(struct net *net, struct dst_entry *dst_orig,
-                                   const struct flowi *fl, struct sock *sk,
+                                   const struct flowi *fl, const struct sock *sk,
                                    int flags);
 
 /* skb attached with this dst needs transformation if dst->xfrm is valid */
index af9d5382f6cbae8c38d45106f5702bd1e66c5671..ce009710120ca8b541615b237a329ee089ec357b 100644 (file)
@@ -60,6 +60,38 @@ static inline struct metadata_dst *tun_rx_dst(int md_size)
        return tun_dst;
 }
 
+static inline struct metadata_dst *tun_dst_unclone(struct sk_buff *skb)
+{
+       struct metadata_dst *md_dst = skb_metadata_dst(skb);
+       int md_size = md_dst->u.tun_info.options_len;
+       struct metadata_dst *new_md;
+
+       if (!md_dst)
+               return ERR_PTR(-EINVAL);
+
+       new_md = metadata_dst_alloc(md_size, GFP_ATOMIC);
+       if (!new_md)
+               return ERR_PTR(-ENOMEM);
+
+       memcpy(&new_md->u.tun_info, &md_dst->u.tun_info,
+              sizeof(struct ip_tunnel_info) + md_size);
+       skb_dst_drop(skb);
+       dst_hold(&new_md->dst);
+       skb_dst_set(skb, &new_md->dst);
+       return new_md;
+}
+
+static inline struct ip_tunnel_info *skb_tunnel_info_unclone(struct sk_buff *skb)
+{
+       struct metadata_dst *dst;
+
+       dst = tun_dst_unclone(skb);
+       if (IS_ERR(dst))
+               return NULL;
+
+       return &dst->u.tun_info;
+}
+
 static inline struct metadata_dst *ip_tun_rx_dst(struct sk_buff *skb,
                                                 __be16 flags,
                                                 __be64 tunnel_id,
index d64253914a6ab6406d92becb4c44fbdec8af17e2..a0d443ca16fcca26b4925d477265935f5bdaf5bc 100644 (file)
@@ -9,6 +9,7 @@ struct kmem_cachep;
 struct net_device;
 struct sk_buff;
 struct sock;
+struct net;
 
 struct dst_ops {
        unsigned short          family;
@@ -28,7 +29,7 @@ struct dst_ops {
                                               struct sk_buff *skb, u32 mtu);
        void                    (*redirect)(struct dst_entry *dst, struct sock *sk,
                                            struct sk_buff *skb);
-       int                     (*local_out)(struct sk_buff *skb);
+       int                     (*local_out)(struct net *net, struct sock *sk, struct sk_buff *skb);
        struct neighbour *      (*neigh_lookup)(const struct dst_entry *dst,
                                                struct sk_buff *skb,
                                                const void *daddr);
index 2a2d6bb34eb8b5923ca8b951f2a5e10acd99be40..bb7f467da7fcd6957ada0111b9d412f298ebc729 100644 (file)
@@ -17,6 +17,7 @@ struct ethoc_platform_data {
        u8 hwaddr[IFHWADDRLEN];
        s8 phy_id;
        u32 eth_clkfreq;
+       bool big_endian;
 };
 
 #endif /* !LINUX_NET_ETHOC_H */
index 9b85db85f13cfc2c11713da810260d4da554bde3..83969eebebf3b458bc4160fe0aded6f5d8fdd30c 100644 (file)
@@ -34,7 +34,7 @@ struct flowi_common {
        __u8    flowic_flags;
 #define FLOWI_FLAG_ANYSRC              0x01
 #define FLOWI_FLAG_KNOWN_NH            0x02
-#define FLOWI_FLAG_VRFSRC              0x04
+#define FLOWI_FLAG_L3MDEV_SRC          0x04
 #define FLOWI_FLAG_SKIP_NH_OIF         0x08
        __u32   flowic_secid;
        struct flowi_tunnel flowic_tun_key;
index a9af1cc8c1bc6089d0facdf56001274cf7993a5a..1b6b6dcb018def094a36786bc5cd3d03e26fa453 100644 (file)
@@ -183,9 +183,8 @@ _genl_register_family_with_ops_grps(struct genl_family *family,
                                            (grps), ARRAY_SIZE(grps))
 
 int genl_unregister_family(struct genl_family *family);
-void genl_notify(struct genl_family *family,
-                struct sk_buff *skb, struct net *net, u32 portid,
-                u32 group, struct nlmsghdr *nlh, gfp_t flags);
+void genl_notify(struct genl_family *family, struct sk_buff *skb,
+                struct genl_info *info, u32 group, gfp_t flags);
 
 struct sk_buff *genlmsg_new_unicast(size_t payload, struct genl_info *info,
                                    gfp_t flags);
index 2c10a9f0c6d96ba51182abb60507d376ae543e96..a62a051a3a2facd78f57c60e120b9ea06bca9b11 100644 (file)
@@ -50,15 +50,6 @@ struct ieee802154_sechdr {
        };
 };
 
-struct ieee802154_addr {
-       u8 mode;
-       __le16 pan_id;
-       union {
-               __le16 short_addr;
-               __le64 extended_addr;
-       };
-};
-
 struct ieee802154_hdr_fc {
 #if defined(__LITTLE_ENDIAN_BITFIELD)
        u16 type:3,
@@ -99,7 +90,7 @@ struct ieee802154_hdr {
  * hdr->fc will be ignored. this includes the INTRA_PAN bit and the frame
  * version, if SECEN is set.
  */
-int ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr);
+int ieee802154_hdr_push(struct sk_buff *skb, struct ieee802154_hdr *hdr);
 
 /* pulls the entire 802.15.4 header off of the skb, including the security
  * header, and performs pan id decompression
@@ -243,38 +234,6 @@ static inline struct ieee802154_mac_cb *mac_cb_init(struct sk_buff *skb)
        return mac_cb(skb);
 }
 
-#define IEEE802154_LLSEC_KEY_SIZE 16
-
-struct ieee802154_llsec_key_id {
-       u8 mode;
-       u8 id;
-       union {
-               struct ieee802154_addr device_addr;
-               __le32 short_source;
-               __le64 extended_source;
-       };
-};
-
-struct ieee802154_llsec_key {
-       u8 frame_types;
-       u32 cmd_frame_ids;
-       u8 key[IEEE802154_LLSEC_KEY_SIZE];
-};
-
-struct ieee802154_llsec_key_entry {
-       struct list_head list;
-
-       struct ieee802154_llsec_key_id id;
-       struct ieee802154_llsec_key *key;
-};
-
-struct ieee802154_llsec_device_key {
-       struct list_head list;
-
-       struct ieee802154_llsec_key_id key_id;
-       u32 frame_counter;
-};
-
 enum {
        IEEE802154_LLSEC_DEVKEY_IGNORE,
        IEEE802154_LLSEC_DEVKEY_RESTRICT,
@@ -283,49 +242,6 @@ enum {
        __IEEE802154_LLSEC_DEVKEY_MAX,
 };
 
-struct ieee802154_llsec_device {
-       struct list_head list;
-
-       __le16 pan_id;
-       __le16 short_addr;
-       __le64 hwaddr;
-       u32 frame_counter;
-       bool seclevel_exempt;
-
-       u8 key_mode;
-       struct list_head keys;
-};
-
-struct ieee802154_llsec_seclevel {
-       struct list_head list;
-
-       u8 frame_type;
-       u8 cmd_frame_id;
-       bool device_override;
-       u32 sec_levels;
-};
-
-struct ieee802154_llsec_params {
-       bool enabled;
-
-       __be32 frame_counter;
-       u8 out_level;
-       struct ieee802154_llsec_key_id out_key;
-
-       __le64 default_key_source;
-
-       __le16 pan_id;
-       __le64 hwaddr;
-       __le64 coord_hwaddr;
-       __le16 coord_shortaddr;
-};
-
-struct ieee802154_llsec_table {
-       struct list_head keys;
-       struct list_head devices;
-       struct list_head security_levels;
-};
-
 #define IEEE802154_MAC_SCAN_ED         0
 #define IEEE802154_MAC_SCAN_ACTIVE     1
 #define IEEE802154_MAC_SCAN_PASSIVE    2
index 6d539e4e5ba731acb6ee949c0cc636fce93f435a..064cfbe639d0681f04b58aeb8c817ace9733e666 100644 (file)
@@ -25,17 +25,8 @@ struct sockaddr;
 int inet6_csk_bind_conflict(const struct sock *sk,
                            const struct inet_bind_bucket *tb, bool relax);
 
-struct dst_entry *inet6_csk_route_req(struct sock *sk, struct flowi6 *fl6,
-                                     const struct request_sock *req);
-
-struct request_sock *inet6_csk_search_req(struct sock *sk,
-                                         const __be16 rport,
-                                         const struct in6_addr *raddr,
-                                         const struct in6_addr *laddr,
-                                         const int iif);
-
-void inet6_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
-                                   const unsigned long timeout);
+struct dst_entry *inet6_csk_route_req(const struct sock *sk, struct flowi6 *fl6,
+                                     const struct request_sock *req, u8 proto);
 
 void inet6_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr);
 
index 279f83591971bd886e78e1b20e4a5c29051fc563..109e3ee9108c0e0219f6cb64a21dc68f1f519ff6 100644 (file)
@@ -41,7 +41,8 @@ int inet_recv_error(struct sock *sk, struct msghdr *msg, int len,
 
 static inline void inet_ctl_sock_destroy(struct sock *sk)
 {
-       sock_release(sk->sk_socket);
+       if (sk)
+               sock_release(sk->sk_socket);
 }
 
 #endif
index 0320bbb7d7b5a1987e7b85f6842cf9fa145d91c5..481fe1c9044cfd8b49585139e24df16b0716debf 100644 (file)
@@ -41,9 +41,11 @@ struct inet_connection_sock_af_ops {
        int         (*rebuild_header)(struct sock *sk);
        void        (*sk_rx_dst_set)(struct sock *sk, const struct sk_buff *skb);
        int         (*conn_request)(struct sock *sk, struct sk_buff *skb);
-       struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb,
+       struct sock *(*syn_recv_sock)(const struct sock *sk, struct sk_buff *skb,
                                      struct request_sock *req,
-                                     struct dst_entry *dst);
+                                     struct dst_entry *dst,
+                                     struct request_sock *req_unhash,
+                                     bool *own_req);
        u16         net_header_len;
        u16         net_frag_header_len;
        u16         sockaddr_len;
@@ -258,31 +260,25 @@ inet_csk_rto_backoff(const struct inet_connection_sock *icsk,
 
 struct sock *inet_csk_accept(struct sock *sk, int flags, int *err);
 
-struct request_sock *inet_csk_search_req(struct sock *sk,
-                                        const __be16 rport,
-                                        const __be32 raddr,
-                                        const __be32 laddr);
 int inet_csk_bind_conflict(const struct sock *sk,
                           const struct inet_bind_bucket *tb, bool relax);
 int inet_csk_get_port(struct sock *sk, unsigned short snum);
 
-struct dst_entry *inet_csk_route_req(struct sock *sk, struct flowi4 *fl4,
+struct dst_entry *inet_csk_route_req(const struct sock *sk, struct flowi4 *fl4,
                                     const struct request_sock *req);
-struct dst_entry *inet_csk_route_child_sock(struct sock *sk, struct sock *newsk,
+struct dst_entry *inet_csk_route_child_sock(const struct sock *sk,
+                                           struct sock *newsk,
                                            const struct request_sock *req);
 
-static inline void inet_csk_reqsk_queue_add(struct sock *sk,
-                                           struct request_sock *req,
-                                           struct sock *child)
-{
-       reqsk_queue_add(&inet_csk(sk)->icsk_accept_queue, req, sk, child);
-}
-
+void inet_csk_reqsk_queue_add(struct sock *sk, struct request_sock *req,
+                             struct sock *child);
 void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
                                   unsigned long timeout);
+struct sock *inet_csk_complete_hashdance(struct sock *sk, struct sock *child,
+                                        struct request_sock *req,
+                                        bool own_req);
 
-static inline void inet_csk_reqsk_queue_added(struct sock *sk,
-                                             const unsigned long timeout)
+static inline void inet_csk_reqsk_queue_added(struct sock *sk)
 {
        reqsk_queue_added(&inet_csk(sk)->icsk_accept_queue);
 }
@@ -299,10 +295,11 @@ static inline int inet_csk_reqsk_queue_young(const struct sock *sk)
 
 static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk)
 {
-       return reqsk_queue_is_full(&inet_csk(sk)->icsk_accept_queue);
+       return inet_csk_reqsk_queue_len(sk) >= sk->sk_max_ack_backlog;
 }
 
 void inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req);
+void inet_csk_reqsk_queue_drop_and_put(struct sock *sk, struct request_sock *req);
 
 void inet_csk_destroy_sock(struct sock *sk);
 void inet_csk_prepare_forced_close(struct sock *sk);
@@ -316,7 +313,7 @@ static inline unsigned int inet_csk_listen_poll(const struct sock *sk)
                        (POLLIN | POLLRDNORM) : 0;
 }
 
-int inet_csk_listen_start(struct sock *sk, const int nr_table_entries);
+int inet_csk_listen_start(struct sock *sk, int backlog);
 void inet_csk_listen_stop(struct sock *sk);
 
 void inet_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr);
index 53eead2da74324b47ef8c772663f9bcb4fd1d0e1..ac42bbb37b2dd5ca16bee092c3fc4481667f8bf2 100644 (file)
@@ -108,7 +108,15 @@ struct inet_frags {
 int inet_frags_init(struct inet_frags *);
 void inet_frags_fini(struct inet_frags *);
 
-void inet_frags_init_net(struct netns_frags *nf);
+static inline int inet_frags_init_net(struct netns_frags *nf)
+{
+       return percpu_counter_init(&nf->mem, 0, GFP_KERNEL);
+}
+static inline void inet_frags_uninit_net(struct netns_frags *nf)
+{
+       percpu_counter_destroy(&nf->mem);
+}
+
 void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f);
 
 void inet_frag_kill(struct inet_frag_queue *q, struct inet_frags *f);
@@ -154,11 +162,6 @@ static inline void add_frag_mem_limit(struct netns_frags *nf, int i)
        __percpu_counter_add(&nf->mem, i, frag_percpu_counter_batch);
 }
 
-static inline void init_frag_mem_limit(struct netns_frags *nf)
-{
-       percpu_counter_init(&nf->mem, 0, GFP_KERNEL);
-}
-
 static inline unsigned int sum_frag_mem_limit(struct netns_frags *nf)
 {
        unsigned int res;
index b07d126694a7aa5d5910e2d4126522aebd602a98..de2e3ade61028cc9937861a6218e3f26ff4a1321 100644 (file)
@@ -199,13 +199,14 @@ static inline int inet_sk_listen_hashfn(const struct sock *sk)
 }
 
 /* Caller must disable local BH processing. */
-int __inet_inherit_port(struct sock *sk, struct sock *child);
+int __inet_inherit_port(const struct sock *sk, struct sock *child);
 
 void inet_put_port(struct sock *sk);
 
 void inet_hashinfo_init(struct inet_hashinfo *h);
 
-void __inet_hash_nolisten(struct sock *sk, struct sock *osk);
+bool inet_ehash_insert(struct sock *sk, struct sock *osk);
+bool inet_ehash_nolisten(struct sock *sk, struct sock *osk);
 void __inet_hash(struct sock *sk, struct sock *osk);
 void inet_hash(struct sock *sk);
 void inet_unhash(struct sock *sk);
index 47eb67b08abdf28b185514cfc1a99685a8c8b8dd..f5bf7310e3343468bddf7e51940a77ed497ad7f1 100644 (file)
@@ -245,7 +245,8 @@ static inline unsigned int __inet_ehashfn(const __be32 laddr,
 }
 
 struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops,
-                                     struct sock *sk_listener);
+                                     struct sock *sk_listener,
+                                     bool attach_listener);
 
 static inline __u8 inet_sk_flowi_flags(const struct sock *sk)
 {
index fc19376986259de8faf9c2be4c5c3376780e54ff..c9b3eb70f340d48ffe60105622bee367c7ea848f 100644 (file)
@@ -70,6 +70,7 @@ struct inet_timewait_sock {
 #define tw_dport               __tw_common.skc_dport
 #define tw_num                 __tw_common.skc_num
 #define tw_cookie              __tw_common.skc_cookie
+#define tw_dr                  __tw_common.skc_tw_dr
 
        int                     tw_timeout;
        volatile unsigned char  tw_substate;
@@ -88,7 +89,6 @@ struct inet_timewait_sock {
        kmemcheck_bitfield_end(flags);
        struct timer_list       tw_timer;
        struct inet_bind_bucket *tw_tb;
-       struct inet_timewait_death_row *tw_dr;
 };
 #define tw_tclass tw_tos
 
index 9b9ca283939964ca12adc2f8b9e1e50b6af71e36..1a98f1ca16383d2b9cd0867091e323ab7c81f045 100644 (file)
@@ -100,24 +100,20 @@ int igmp_mc_init(void);
  *     Functions provided by ip.c
  */
 
-int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
+int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk,
                          __be32 saddr, __be32 daddr,
                          struct ip_options_rcu *opt);
 int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
           struct net_device *orig_dev);
 int ip_local_deliver(struct sk_buff *skb);
 int ip_mr_input(struct sk_buff *skb);
-int ip_output(struct sock *sk, struct sk_buff *skb);
-int ip_mc_output(struct sock *sk, struct sk_buff *skb);
-int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
-                  int (*output)(struct sock *, struct sk_buff *));
+int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb);
+int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb);
+int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
+                  int (*output)(struct net *, struct sock *, struct sk_buff *));
 void ip_send_check(struct iphdr *ip);
-int __ip_local_out(struct sk_buff *skb);
-int ip_local_out_sk(struct sock *sk, struct sk_buff *skb);
-static inline int ip_local_out(struct sk_buff *skb)
-{
-       return ip_local_out_sk(skb->sk, skb);
-}
+int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
+int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
 
 int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl);
 void ip_init(void);
@@ -282,10 +278,12 @@ int ip_decrease_ttl(struct iphdr *iph)
 }
 
 static inline
-int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
+int ip_dont_fragment(const struct sock *sk, const struct dst_entry *dst)
 {
-       return  inet_sk(sk)->pmtudisc == IP_PMTUDISC_DO ||
-               (inet_sk(sk)->pmtudisc == IP_PMTUDISC_WANT &&
+       u8 pmtudisc = READ_ONCE(inet_sk(sk)->pmtudisc);
+
+       return  pmtudisc == IP_PMTUDISC_DO ||
+               (pmtudisc == IP_PMTUDISC_WANT &&
                 !(dst_metric_locked(dst, RTAX_MTU)));
 }
 
@@ -321,12 +319,15 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
 
 static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb)
 {
-       if (!skb->sk || ip_sk_use_pmtu(skb->sk)) {
+       struct sock *sk = skb->sk;
+
+       if (!sk || !sk_fullsock(sk) || ip_sk_use_pmtu(sk)) {
                bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED;
+
                return ip_dst_mtu_maybe_forward(skb_dst(skb), forwarding);
-       } else {
-               return min(skb_dst(skb)->dev->mtu, IP_MAX_MTU);
        }
+
+       return min(skb_dst(skb)->dev->mtu, IP_MAX_MTU);
 }
 
 u32 ip_idents_reserve(u32 hash, int segs);
@@ -505,11 +506,11 @@ static inline bool ip_defrag_user_in_between(u32 user,
        return user >= lower_bond && user <= upper_bond;
 }
 
-int ip_defrag(struct sk_buff *skb, u32 user);
+int ip_defrag(struct net *net, struct sk_buff *skb, u32 user);
 #ifdef CONFIG_INET
-struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user);
+struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user);
 #else
-static inline struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user)
+static inline struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user)
 {
        return skb;
 }
index 297629aadb190d4cfcdec41c241efa2b0db76a71..2bfb2ad2fab1981696e52a914cd89a0dad495e76 100644 (file)
@@ -173,8 +173,8 @@ static inline bool ipv6_anycast_destination(const struct dst_entry *dst,
                 ipv6_addr_equal(&rt->rt6i_dst.addr, daddr));
 }
 
-int ip6_fragment(struct sock *sk, struct sk_buff *skb,
-                int (*output)(struct sock *, struct sk_buff *));
+int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
+                int (*output)(struct net *, struct sock *, struct sk_buff *));
 
 static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
 {
index fa915fa0f703dc03d21ae51b50cbd11c7fb74561..aaee6fa02cf1d2a756eb6b97559501c47af1c635 100644 (file)
@@ -87,7 +87,7 @@ static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb,
        int pkt_len, err;
 
        pkt_len = skb->len - skb_inner_network_offset(skb);
-       err = ip6_local_out_sk(sk, skb);
+       err = ip6_local_out(dev_net(skb_dst(skb)->dev), sk, skb);
 
        if (net_xmit_eval(err) == 0) {
                struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
index 727d6e9a96856b4fde184a188e1aec003ed4bfa9..9f4df68105ab28eecdeb9d799a83910b73142a06 100644 (file)
@@ -79,7 +79,7 @@ struct fib_nh {
        unsigned char           nh_scope;
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
        int                     nh_weight;
-       int                     nh_power;
+       atomic_t                nh_upper_bound;
 #endif
 #ifdef CONFIG_IP_ROUTE_CLASSID
        __u32                   nh_tclassid;
@@ -118,7 +118,7 @@ struct fib_info {
 #define fib_advmss fib_metrics[RTAX_ADVMSS-1]
        int                     fib_nhs;
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-       int                     fib_power;
+       int                     fib_weight;
 #endif
        struct rcu_head         rcu;
        struct fib_nh           fib_nh[0];
@@ -317,10 +317,20 @@ void fib_flush_external(struct net *net);
 
 /* Exported by fib_semantics.c */
 int ip_fib_check_default(__be32 gw, struct net_device *dev);
-int fib_sync_down_dev(struct net_device *dev, unsigned long event);
+int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force);
 int fib_sync_down_addr(struct net *net, __be32 local);
 int fib_sync_up(struct net_device *dev, unsigned int nh_flags);
-void fib_select_multipath(struct fib_result *res);
+
+extern u32 fib_multipath_secret __read_mostly;
+
+static inline int fib_multipath_hash(__be32 saddr, __be32 daddr)
+{
+       return jhash_2words(saddr, daddr, fib_multipath_secret) >> 1;
+}
+
+void fib_select_multipath(struct fib_result *res, int hash);
+void fib_select_path(struct net *net, struct fib_result *res,
+                    struct flowi4 *fl4, int mp_hash);
 
 /* Exported by fib_trie.c */
 void fib_trie_init(void);
index 9b9ca87a4210b9e83552f3dcb500f3ea54787fda..0816c872b68911077bd51bc41dee0e32e1c3213d 100644 (file)
 #endif
 #include <net/net_namespace.h>         /* Netw namespace */
 
+#define IP_VS_HDR_INVERSE      1
+#define IP_VS_HDR_ICMP         2
+
 /* Generic access of ipvs struct */
 static inline struct netns_ipvs *net_ipvs(struct net* net)
 {
        return net->ipvs;
 }
 
-/* Get net ptr from skb in traffic cases
- * use skb_sknet when call is from userland (ioctl or netlink)
- */
-static inline struct net *skb_net(const struct sk_buff *skb)
-{
-#ifdef CONFIG_NET_NS
-#ifdef CONFIG_IP_VS_DEBUG
-       /*
-        * This is used for debug only.
-        * Start with the most likely hit
-        * End with BUG
-        */
-       if (likely(skb->dev && dev_net(skb->dev)))
-               return dev_net(skb->dev);
-       if (skb_dst(skb) && skb_dst(skb)->dev)
-               return dev_net(skb_dst(skb)->dev);
-       WARN(skb->sk, "Maybe skb_sknet should be used in %s() at line:%d\n",
-                     __func__, __LINE__);
-       if (likely(skb->sk && sock_net(skb->sk)))
-               return sock_net(skb->sk);
-       pr_err("There is no net ptr to find in the skb in %s() line:%d\n",
-               __func__, __LINE__);
-       BUG();
-#else
-       return dev_net(skb->dev ? : skb_dst(skb)->dev);
-#endif
-#else
-       return &init_net;
-#endif
-}
-
-static inline struct net *skb_sknet(const struct sk_buff *skb)
-{
-#ifdef CONFIG_NET_NS
-#ifdef CONFIG_IP_VS_DEBUG
-       /* Start with the most likely hit */
-       if (likely(skb->sk && sock_net(skb->sk)))
-               return sock_net(skb->sk);
-       WARN(skb->dev, "Maybe skb_net should be used instead in %s() line:%d\n",
-                      __func__, __LINE__);
-       if (likely(skb->dev && dev_net(skb->dev)))
-               return dev_net(skb->dev);
-       pr_err("There is no net ptr to find in the skb in %s() line:%d\n",
-               __func__, __LINE__);
-       BUG();
-#else
-       return sock_net(skb->sk);
-#endif
-#else
-       return &init_net;
-#endif
-}
-
 /* This one needed for single_open_net since net is stored directly in
  * private not as a struct i.e. seq_file_net can't be used.
  */
@@ -104,6 +54,8 @@ static inline struct net *seq_file_single_net(struct seq_file *seq)
 extern int ip_vs_conn_tab_size;
 
 struct ip_vs_iphdr {
+       int hdr_flags;  /* ipvs flags */
+       __u32 off;      /* Where IP or IPv4 header starts */
        __u32 len;      /* IPv4 simply where L4 starts
                         * IPv6 where L4 Transport Header starts */
        __u16 fragoffs; /* IPv6 fragment offset, 0 if first frag (or not frag)*/
@@ -120,48 +72,89 @@ static inline void *frag_safe_skb_hp(const struct sk_buff *skb, int offset,
        return skb_header_pointer(skb, offset, len, buffer);
 }
 
-static inline void
-ip_vs_fill_ip4hdr(const void *nh, struct ip_vs_iphdr *iphdr)
-{
-       const struct iphdr *iph = nh;
-
-       iphdr->len      = iph->ihl * 4;
-       iphdr->fragoffs = 0;
-       iphdr->protocol = iph->protocol;
-       iphdr->saddr.ip = iph->saddr;
-       iphdr->daddr.ip = iph->daddr;
-}
-
 /* This function handles filling *ip_vs_iphdr, both for IPv4 and IPv6.
  * IPv6 requires some extra work, as finding proper header position,
  * depend on the IPv6 extension headers.
  */
-static inline void
-ip_vs_fill_iph_skb(int af, const struct sk_buff *skb, struct ip_vs_iphdr *iphdr)
+static inline int
+ip_vs_fill_iph_skb_off(int af, const struct sk_buff *skb, int offset,
+                      int hdr_flags, struct ip_vs_iphdr *iphdr)
 {
+       iphdr->hdr_flags = hdr_flags;
+       iphdr->off = offset;
+
 #ifdef CONFIG_IP_VS_IPV6
        if (af == AF_INET6) {
-               const struct ipv6hdr *iph =
-                       (struct ipv6hdr *)skb_network_header(skb);
+               struct ipv6hdr _iph;
+               const struct ipv6hdr *iph = skb_header_pointer(
+                       skb, offset, sizeof(_iph), &_iph);
+               if (!iph)
+                       return 0;
+
                iphdr->saddr.in6 = iph->saddr;
                iphdr->daddr.in6 = iph->daddr;
                /* ipv6_find_hdr() updates len, flags */
-               iphdr->len       = 0;
+               iphdr->len       = offset;
                iphdr->flags     = 0;
                iphdr->protocol  = ipv6_find_hdr(skb, &iphdr->len, -1,
                                                 &iphdr->fragoffs,
                                                 &iphdr->flags);
+               if (iphdr->protocol < 0)
+                       return 0;
        } else
 #endif
        {
-               const struct iphdr *iph =
-                       (struct iphdr *)skb_network_header(skb);
-               iphdr->len      = iph->ihl * 4;
+               struct iphdr _iph;
+               const struct iphdr *iph = skb_header_pointer(
+                       skb, offset, sizeof(_iph), &_iph);
+               if (!iph)
+                       return 0;
+
+               iphdr->len      = offset + iph->ihl * 4;
                iphdr->fragoffs = 0;
                iphdr->protocol = iph->protocol;
                iphdr->saddr.ip = iph->saddr;
                iphdr->daddr.ip = iph->daddr;
        }
+
+       return 1;
+}
+
+static inline int
+ip_vs_fill_iph_skb_icmp(int af, const struct sk_buff *skb, int offset,
+                       bool inverse, struct ip_vs_iphdr *iphdr)
+{
+       int hdr_flags = IP_VS_HDR_ICMP;
+
+       if (inverse)
+               hdr_flags |= IP_VS_HDR_INVERSE;
+
+       return ip_vs_fill_iph_skb_off(af, skb, offset, hdr_flags, iphdr);
+}
+
+static inline int
+ip_vs_fill_iph_skb(int af, const struct sk_buff *skb, bool inverse,
+                  struct ip_vs_iphdr *iphdr)
+{
+       int hdr_flags = 0;
+
+       if (inverse)
+               hdr_flags |= IP_VS_HDR_INVERSE;
+
+       return ip_vs_fill_iph_skb_off(af, skb, skb_network_offset(skb),
+                                     hdr_flags, iphdr);
+}
+
+static inline bool
+ip_vs_iph_inverse(const struct ip_vs_iphdr *iph)
+{
+       return !!(iph->hdr_flags & IP_VS_HDR_INVERSE);
+}
+
+static inline bool
+ip_vs_iph_icmp(const struct ip_vs_iphdr *iph)
+{
+       return !!(iph->hdr_flags & IP_VS_HDR_ICMP);
 }
 
 static inline void ip_vs_addr_copy(int af, union nf_inet_addr *dst,
@@ -437,26 +430,27 @@ struct ip_vs_protocol {
 
        void (*exit)(struct ip_vs_protocol *pp);
 
-       int (*init_netns)(struct net *net, struct ip_vs_proto_data *pd);
+       int (*init_netns)(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd);
 
-       void (*exit_netns)(struct net *net, struct ip_vs_proto_data *pd);
+       void (*exit_netns)(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd);
 
-       int (*conn_schedule)(int af, struct sk_buff *skb,
+       int (*conn_schedule)(struct netns_ipvs *ipvs,
+                            int af, struct sk_buff *skb,
                             struct ip_vs_proto_data *pd,
                             int *verdict, struct ip_vs_conn **cpp,
                             struct ip_vs_iphdr *iph);
 
        struct ip_vs_conn *
-       (*conn_in_get)(int af,
+       (*conn_in_get)(struct netns_ipvs *ipvs,
+                      int af,
                       const struct sk_buff *skb,
-                      const struct ip_vs_iphdr *iph,
-                      int inverse);
+                      const struct ip_vs_iphdr *iph);
 
        struct ip_vs_conn *
-       (*conn_out_get)(int af,
+       (*conn_out_get)(struct netns_ipvs *ipvs,
+                       int af,
                        const struct sk_buff *skb,
-                       const struct ip_vs_iphdr *iph,
-                       int inverse);
+                       const struct ip_vs_iphdr *iph);
 
        int (*snat_handler)(struct sk_buff *skb, struct ip_vs_protocol *pp,
                            struct ip_vs_conn *cp, struct ip_vs_iphdr *iph);
@@ -473,9 +467,9 @@ struct ip_vs_protocol {
                                 const struct sk_buff *skb,
                                 struct ip_vs_proto_data *pd);
 
-       int (*register_app)(struct net *net, struct ip_vs_app *inc);
+       int (*register_app)(struct netns_ipvs *ipvs, struct ip_vs_app *inc);
 
-       void (*unregister_app)(struct net *net, struct ip_vs_app *inc);
+       void (*unregister_app)(struct netns_ipvs *ipvs, struct ip_vs_app *inc);
 
        int (*app_conn_bind)(struct ip_vs_conn *cp);
 
@@ -497,11 +491,11 @@ struct ip_vs_proto_data {
 };
 
 struct ip_vs_protocol   *ip_vs_proto_get(unsigned short proto);
-struct ip_vs_proto_data *ip_vs_proto_data_get(struct net *net,
+struct ip_vs_proto_data *ip_vs_proto_data_get(struct netns_ipvs *ipvs,
                                              unsigned short proto);
 
 struct ip_vs_conn_param {
-       struct net                      *net;
+       struct netns_ipvs               *ipvs;
        const union nf_inet_addr        *caddr;
        const union nf_inet_addr        *vaddr;
        __be16                          cport;
@@ -528,9 +522,7 @@ struct ip_vs_conn {
        volatile __u32          flags;          /* status flags */
        __u16                   protocol;       /* Which protocol (TCP/UDP) */
        __u16                   daf;            /* Address family of the dest */
-#ifdef CONFIG_NET_NS
-       struct net              *net;           /* Name space */
-#endif
+       struct netns_ipvs       *ipvs;
 
        /* counter and timer */
        atomic_t                refcnt;         /* reference count */
@@ -577,33 +569,6 @@ struct ip_vs_conn {
        struct rcu_head         rcu_head;
 };
 
-/* To save some memory in conn table when name space is disabled. */
-static inline struct net *ip_vs_conn_net(const struct ip_vs_conn *cp)
-{
-#ifdef CONFIG_NET_NS
-       return cp->net;
-#else
-       return &init_net;
-#endif
-}
-
-static inline void ip_vs_conn_net_set(struct ip_vs_conn *cp, struct net *net)
-{
-#ifdef CONFIG_NET_NS
-       cp->net = net;
-#endif
-}
-
-static inline int ip_vs_conn_net_eq(const struct ip_vs_conn *cp,
-                                   struct net *net)
-{
-#ifdef CONFIG_NET_NS
-       return cp->net == net;
-#else
-       return 1;
-#endif
-}
-
 /* Extended internal versions of struct ip_vs_service_user and ip_vs_dest_user
  * for IPv6 support.
  *
@@ -663,7 +628,7 @@ struct ip_vs_service {
        unsigned int            flags;    /* service status flags */
        unsigned int            timeout;  /* persistent timeout in ticks */
        __be32                  netmask;  /* grouping granularity, mask/plen */
-       struct net              *net;
+       struct netns_ipvs       *ipvs;
 
        struct list_head        destinations;  /* real server d-linked list */
        __u32                   num_dests;     /* number of servers */
@@ -953,6 +918,8 @@ struct netns_ipvs {
        int                     sysctl_pmtu_disc;
        int                     sysctl_backup_only;
        int                     sysctl_conn_reuse_mode;
+       int                     sysctl_schedule_icmp;
+       int                     sysctl_ignore_tunneled;
 
        /* ip_vs_lblc */
        int                     sysctl_lblc_expiration;
@@ -1071,6 +1038,21 @@ static inline int sysctl_conn_reuse_mode(struct netns_ipvs *ipvs)
        return ipvs->sysctl_conn_reuse_mode;
 }
 
+static inline int sysctl_schedule_icmp(struct netns_ipvs *ipvs)
+{
+       return ipvs->sysctl_schedule_icmp;
+}
+
+static inline int sysctl_ignore_tunneled(struct netns_ipvs *ipvs)
+{
+       return ipvs->sysctl_ignore_tunneled;
+}
+
+static inline int sysctl_cache_bypass(struct netns_ipvs *ipvs)
+{
+       return ipvs->sysctl_cache_bypass;
+}
+
 #else
 
 static inline int sysctl_sync_threshold(struct netns_ipvs *ipvs)
@@ -1143,6 +1125,21 @@ static inline int sysctl_conn_reuse_mode(struct netns_ipvs *ipvs)
        return 1;
 }
 
+static inline int sysctl_schedule_icmp(struct netns_ipvs *ipvs)
+{
+       return 0;
+}
+
+static inline int sysctl_ignore_tunneled(struct netns_ipvs *ipvs)
+{
+       return 0;
+}
+
+static inline int sysctl_cache_bypass(struct netns_ipvs *ipvs)
+{
+       return 0;
+}
+
 #endif
 
 /* IPVS core functions
@@ -1164,14 +1161,14 @@ enum {
        IP_VS_DIR_LAST,
 };
 
-static inline void ip_vs_conn_fill_param(struct net *net, int af, int protocol,
+static inline void ip_vs_conn_fill_param(struct netns_ipvs *ipvs, int af, int protocol,
                                         const union nf_inet_addr *caddr,
                                         __be16 cport,
                                         const union nf_inet_addr *vaddr,
                                         __be16 vport,
                                         struct ip_vs_conn_param *p)
 {
-       p->net = net;
+       p->ipvs = ipvs;
        p->af = af;
        p->protocol = protocol;
        p->caddr = caddr;
@@ -1185,15 +1182,15 @@ static inline void ip_vs_conn_fill_param(struct net *net, int af, int protocol,
 struct ip_vs_conn *ip_vs_conn_in_get(const struct ip_vs_conn_param *p);
 struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p);
 
-struct ip_vs_conn * ip_vs_conn_in_get_proto(int af, const struct sk_buff *skb,
-                                           const struct ip_vs_iphdr *iph,
-                                           int inverse);
+struct ip_vs_conn * ip_vs_conn_in_get_proto(struct netns_ipvs *ipvs, int af,
+                                           const struct sk_buff *skb,
+                                           const struct ip_vs_iphdr *iph);
 
 struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p);
 
-struct ip_vs_conn * ip_vs_conn_out_get_proto(int af, const struct sk_buff *skb,
-                                            const struct ip_vs_iphdr *iph,
-                                            int inverse);
+struct ip_vs_conn * ip_vs_conn_out_get_proto(struct netns_ipvs *ipvs, int af,
+                                            const struct sk_buff *skb,
+                                            const struct ip_vs_iphdr *iph);
 
 /* Get reference to gain full access to conn.
  * By default, RCU read-side critical sections have access only to
@@ -1221,9 +1218,9 @@ void ip_vs_conn_expire_now(struct ip_vs_conn *cp);
 
 const char *ip_vs_state_name(__u16 proto, int state);
 
-void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp);
+void ip_vs_tcp_conn_listen(struct ip_vs_conn *cp);
 int ip_vs_check_template(struct ip_vs_conn *ct);
-void ip_vs_random_dropentry(struct net *net);
+void ip_vs_random_dropentry(struct netns_ipvs *ipvs);
 int ip_vs_conn_init(void);
 void ip_vs_conn_cleanup(void);
 
@@ -1288,29 +1285,29 @@ ip_vs_control_add(struct ip_vs_conn *cp, struct ip_vs_conn *ctl_cp)
 }
 
 /* IPVS netns init & cleanup functions */
-int ip_vs_estimator_net_init(struct net *net);
-int ip_vs_control_net_init(struct net *net);
-int ip_vs_protocol_net_init(struct net *net);
-int ip_vs_app_net_init(struct net *net);
-int ip_vs_conn_net_init(struct net *net);
-int ip_vs_sync_net_init(struct net *net);
-void ip_vs_conn_net_cleanup(struct net *net);
-void ip_vs_app_net_cleanup(struct net *net);
-void ip_vs_protocol_net_cleanup(struct net *net);
-void ip_vs_control_net_cleanup(struct net *net);
-void ip_vs_estimator_net_cleanup(struct net *net);
-void ip_vs_sync_net_cleanup(struct net *net);
-void ip_vs_service_net_cleanup(struct net *net);
+int ip_vs_estimator_net_init(struct netns_ipvs *ipvs);
+int ip_vs_control_net_init(struct netns_ipvs *ipvs);
+int ip_vs_protocol_net_init(struct netns_ipvs *ipvs);
+int ip_vs_app_net_init(struct netns_ipvs *ipvs);
+int ip_vs_conn_net_init(struct netns_ipvs *ipvs);
+int ip_vs_sync_net_init(struct netns_ipvs *ipvs);
+void ip_vs_conn_net_cleanup(struct netns_ipvs *ipvs);
+void ip_vs_app_net_cleanup(struct netns_ipvs *ipvs);
+void ip_vs_protocol_net_cleanup(struct netns_ipvs *ipvs);
+void ip_vs_control_net_cleanup(struct netns_ipvs *ipvs);
+void ip_vs_estimator_net_cleanup(struct netns_ipvs *ipvs);
+void ip_vs_sync_net_cleanup(struct netns_ipvs *ipvs);
+void ip_vs_service_net_cleanup(struct netns_ipvs *ipvs);
 
 /* IPVS application functions
  * (from ip_vs_app.c)
  */
 #define IP_VS_APP_MAX_PORTS  8
-struct ip_vs_app *register_ip_vs_app(struct net *net, struct ip_vs_app *app);
-void unregister_ip_vs_app(struct net *net, struct ip_vs_app *app);
+struct ip_vs_app *register_ip_vs_app(struct netns_ipvs *ipvs, struct ip_vs_app *app);
+void unregister_ip_vs_app(struct netns_ipvs *ipvs, struct ip_vs_app *app);
 int ip_vs_bind_app(struct ip_vs_conn *cp, struct ip_vs_protocol *pp);
 void ip_vs_unbind_app(struct ip_vs_conn *cp);
-int register_ip_vs_app_inc(struct net *net, struct ip_vs_app *app, __u16 proto,
+int register_ip_vs_app_inc(struct netns_ipvs *ipvs, struct ip_vs_app *app, __u16 proto,
                           __u16 port);
 int ip_vs_app_inc_get(struct ip_vs_app *inc);
 void ip_vs_app_inc_put(struct ip_vs_app *inc);
@@ -1375,10 +1372,10 @@ extern struct ip_vs_stats ip_vs_stats;
 extern int sysctl_ip_vs_sync_ver;
 
 struct ip_vs_service *
-ip_vs_service_find(struct net *net, int af, __u32 fwmark, __u16 protocol,
+ip_vs_service_find(struct netns_ipvs *ipvs, int af, __u32 fwmark, __u16 protocol,
                  const union nf_inet_addr *vaddr, __be16 vport);
 
-bool ip_vs_has_real_service(struct net *net, int af, __u16 protocol,
+bool ip_vs_has_real_service(struct netns_ipvs *ipvs, int af, __u16 protocol,
                            const union nf_inet_addr *daddr, __be16 dport);
 
 int ip_vs_use_count_inc(void);
@@ -1388,7 +1385,7 @@ void ip_vs_unregister_nl_ioctl(void);
 int ip_vs_control_init(void);
 void ip_vs_control_cleanup(void);
 struct ip_vs_dest *
-ip_vs_find_dest(struct net *net, int svc_af, int dest_af,
+ip_vs_find_dest(struct netns_ipvs *ipvs, int svc_af, int dest_af,
                const union nf_inet_addr *daddr, __be16 dport,
                const union nf_inet_addr *vaddr, __be16 vport,
                __u16 protocol, __u32 fwmark, __u32 flags);
@@ -1414,14 +1411,14 @@ static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest)
 /* IPVS sync daemon data and function prototypes
  * (from ip_vs_sync.c)
  */
-int start_sync_thread(struct net *net, struct ipvs_sync_daemon_cfg *cfg,
+int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *cfg,
                      int state);
-int stop_sync_thread(struct net *net, int state);
-void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts);
+int stop_sync_thread(struct netns_ipvs *ipvs, int state);
+void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts);
 
 /* IPVS rate estimator prototypes (from ip_vs_est.c) */
-void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats);
-void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats);
+void ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats);
+void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats);
 void ip_vs_zero_estimator(struct ip_vs_stats *stats);
 void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats);
 
index 711cca428cc8cd56b40de704265ad73262a6d2a4..e1a10b0ac0b027189732372e2c0040e5ea8350f2 100644 (file)
@@ -807,12 +807,12 @@ static inline u8 ip6_tclass(__be32 flowinfo)
 int ipv6_rcv(struct sk_buff *skb, struct net_device *dev,
             struct packet_type *pt, struct net_device *orig_dev);
 
-int ip6_rcv_finish(struct sock *sk, struct sk_buff *skb);
+int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
 
 /*
  *     upper-layer output functions
  */
-int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
+int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
             struct ipv6_txoptions *opt, int tclass);
 
 int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr);
@@ -849,7 +849,7 @@ static inline struct sk_buff *ip6_finish_skb(struct sock *sk)
 
 int ip6_dst_lookup(struct net *net, struct sock *sk, struct dst_entry **dst,
                   struct flowi6 *fl6);
-struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
+struct dst_entry *ip6_dst_lookup_flow(const struct sock *sk, struct flowi6 *fl6,
                                      const struct in6_addr *final_dst);
 struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
                                         const struct in6_addr *final_dst);
@@ -860,14 +860,13 @@ struct dst_entry *ip6_blackhole_route(struct net *net,
  *     skb processing functions
  */
 
-int ip6_output(struct sock *sk, struct sk_buff *skb);
+int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb);
 int ip6_forward(struct sk_buff *skb);
 int ip6_input(struct sk_buff *skb);
 int ip6_mc_input(struct sk_buff *skb);
 
-int __ip6_local_out(struct sk_buff *skb);
-int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb);
-int ip6_local_out(struct sk_buff *skb);
+int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
+int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
 
 /*
  *     Extension header (options) processing
index 0894ced31957cf64f992e4676fdd402056e2a39b..b867b0cf79e84fc2bb0106e0aa41438591bdb2dd 100644 (file)
@@ -141,14 +141,14 @@ struct iucv_handler {
          * called is the order of the registration of the iucv handlers
          * to the base code.
          */
-       int  (*path_pending)(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]);
+       int  (*path_pending)(struct iucv_path *, u8 *ipvmid, u8 *ipuser);
        /*
         * The path_complete function is called after an iucv interrupt
         * type 0x02 has been received for a path that has been established
         * for this handler with iucv_path_connect and got accepted by the
         * peer with iucv_path_accept.
         */
-       void (*path_complete)(struct iucv_path *, u8 ipuser[16]);
+       void (*path_complete)(struct iucv_path *, u8 *ipuser);
         /*
          * The path_severed function is called after an iucv interrupt
          * type 0x03 has been received. The communication peer shutdown
@@ -156,20 +156,20 @@ struct iucv_handler {
          * remaining messages can be received until a iucv_path_sever
          * shuts down the other end of the path as well.
          */
-       void (*path_severed)(struct iucv_path *, u8 ipuser[16]);
+       void (*path_severed)(struct iucv_path *, u8 *ipuser);
        /*
         * The path_quiesced function is called after an icuv interrupt
         * type 0x04 has been received. The communication peer has quiesced
         * the path. Delivery of messages is stopped until iucv_path_resume
         * has been called.
         */
-       void (*path_quiesced)(struct iucv_path *, u8 ipuser[16]);
+       void (*path_quiesced)(struct iucv_path *, u8 *ipuser);
        /*
         * The path_resumed function is called after an icuv interrupt
         * type 0x05 has been received. The communication peer has resumed
         * the path.
         */
-       void (*path_resumed)(struct iucv_path *, u8 ipuser[16]);
+       void (*path_resumed)(struct iucv_path *, u8 *ipuser);
        /*
         * The message_pending function is called after an icuv interrupt
         * type 0x06 or type 0x07 has been received. A new message is
@@ -256,7 +256,7 @@ static inline void iucv_path_free(struct iucv_path *path)
  * Returns the result of the CP IUCV call.
  */
 int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler,
-                    u8 userdata[16], void *private);
+                    u8 *userdata, void *private);
 
 /**
  * iucv_path_connect
@@ -274,7 +274,7 @@ int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler,
  * Returns the result of the CP IUCV call.
  */
 int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler,
-                     u8 userid[8], u8 system[8], u8 userdata[16],
+                     u8 *userid, u8 *system, u8 *userdata,
                      void *private);
 
 /**
@@ -287,7 +287,7 @@ int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler,
  *
  * Returns the result from the CP IUCV call.
  */
-int iucv_path_quiesce(struct iucv_path *path, u8 userdata[16]);
+int iucv_path_quiesce(struct iucv_path *path, u8 *userdata);
 
 /**
  * iucv_path_resume:
@@ -299,7 +299,7 @@ int iucv_path_quiesce(struct iucv_path *path, u8 userdata[16]);
  *
  * Returns the result from the CP IUCV call.
  */
-int iucv_path_resume(struct iucv_path *path, u8 userdata[16]);
+int iucv_path_resume(struct iucv_path *path, u8 *userdata);
 
 /**
  * iucv_path_sever
@@ -310,7 +310,7 @@ int iucv_path_resume(struct iucv_path *path, u8 userdata[16]);
  *
  * Returns the result from the CP IUCV call.
  */
-int iucv_path_sever(struct iucv_path *path, u8 userdata[16]);
+int iucv_path_sever(struct iucv_path *path, u8 *userdata);
 
 /**
  * iucv_message_purge
diff --git a/include/net/l3mdev.h b/include/net/l3mdev.h
new file mode 100644 (file)
index 0000000..774d85b
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * include/net/l3mdev.h - L3 master device API
+ * Copyright (c) 2015 Cumulus Networks
+ * Copyright (c) 2015 David Ahern <dsa@cumulusnetworks.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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _NET_L3MDEV_H_
+#define _NET_L3MDEV_H_
+
+/**
+ * struct l3mdev_ops - l3mdev operations
+ *
+ * @l3mdev_fib_table: Get FIB table id to use for lookups
+ *
+ * @l3mdev_get_rtable: Get cached IPv4 rtable (dst_entry) for device
+ *
+ * @l3mdev_get_saddr: Get source address for a flow
+ *
+ * @l3mdev_get_rt6_dst: Get cached IPv6 rt6_info (dst_entry) for device
+ */
+
+struct l3mdev_ops {
+       u32             (*l3mdev_fib_table)(const struct net_device *dev);
+
+       /* IPv4 ops */
+       struct rtable * (*l3mdev_get_rtable)(const struct net_device *dev,
+                                            const struct flowi4 *fl4);
+       void            (*l3mdev_get_saddr)(struct net_device *dev,
+                                           struct flowi4 *fl4);
+
+       /* IPv6 ops */
+       struct dst_entry * (*l3mdev_get_rt6_dst)(const struct net_device *dev,
+                                                const struct flowi6 *fl6);
+};
+
+#ifdef CONFIG_NET_L3_MASTER_DEV
+
+int l3mdev_master_ifindex_rcu(struct net_device *dev);
+static inline int l3mdev_master_ifindex(struct net_device *dev)
+{
+       int ifindex;
+
+       rcu_read_lock();
+       ifindex = l3mdev_master_ifindex_rcu(dev);
+       rcu_read_unlock();
+
+       return ifindex;
+}
+
+/* get index of an interface to use for FIB lookups. For devices
+ * enslaved to an L3 master device FIB lookups are based on the
+ * master index
+ */
+static inline int l3mdev_fib_oif_rcu(struct net_device *dev)
+{
+       return l3mdev_master_ifindex_rcu(dev) ? : dev->ifindex;
+}
+
+static inline int l3mdev_fib_oif(struct net_device *dev)
+{
+       int oif;
+
+       rcu_read_lock();
+       oif = l3mdev_fib_oif_rcu(dev);
+       rcu_read_unlock();
+
+       return oif;
+}
+
+u32 l3mdev_fib_table_rcu(const struct net_device *dev);
+u32 l3mdev_fib_table_by_index(struct net *net, int ifindex);
+static inline u32 l3mdev_fib_table(const struct net_device *dev)
+{
+       u32 tb_id;
+
+       rcu_read_lock();
+       tb_id = l3mdev_fib_table_rcu(dev);
+       rcu_read_unlock();
+
+       return tb_id;
+}
+
+static inline struct rtable *l3mdev_get_rtable(const struct net_device *dev,
+                                              const struct flowi4 *fl4)
+{
+       if (netif_is_l3_master(dev) && dev->l3mdev_ops->l3mdev_get_rtable)
+               return dev->l3mdev_ops->l3mdev_get_rtable(dev, fl4);
+
+       return NULL;
+}
+
+static inline bool netif_index_is_l3_master(struct net *net, int ifindex)
+{
+       struct net_device *dev;
+       bool rc = false;
+
+       if (ifindex == 0)
+               return false;
+
+       rcu_read_lock();
+
+       dev = dev_get_by_index_rcu(net, ifindex);
+       if (dev)
+               rc = netif_is_l3_master(dev);
+
+       rcu_read_unlock();
+
+       return rc;
+}
+
+static inline void l3mdev_get_saddr(struct net *net, int ifindex,
+                                   struct flowi4 *fl4)
+{
+       struct net_device *dev;
+
+       if (ifindex) {
+
+               rcu_read_lock();
+
+               dev = dev_get_by_index_rcu(net, ifindex);
+               if (dev && netif_is_l3_master(dev) &&
+                   dev->l3mdev_ops->l3mdev_get_saddr) {
+                       dev->l3mdev_ops->l3mdev_get_saddr(dev, fl4);
+               }
+
+               rcu_read_unlock();
+       }
+}
+
+static inline struct dst_entry *l3mdev_get_rt6_dst(const struct net_device *dev,
+                                                  const struct flowi6 *fl6)
+{
+       if (netif_is_l3_master(dev) && dev->l3mdev_ops->l3mdev_get_rt6_dst)
+               return dev->l3mdev_ops->l3mdev_get_rt6_dst(dev, fl6);
+
+       return NULL;
+}
+
+static inline
+struct dst_entry *l3mdev_rt6_dst_by_oif(struct net *net,
+                                       const struct flowi6 *fl6)
+{
+       struct dst_entry *dst = NULL;
+       struct net_device *dev;
+
+       dev = dev_get_by_index(net, fl6->flowi6_oif);
+       if (dev) {
+               dst = l3mdev_get_rt6_dst(dev, fl6);
+               dev_put(dev);
+       }
+
+       return dst;
+}
+
+#else
+
+static inline int l3mdev_master_ifindex_rcu(struct net_device *dev)
+{
+       return 0;
+}
+static inline int l3mdev_master_ifindex(struct net_device *dev)
+{
+       return 0;
+}
+
+static inline int l3mdev_fib_oif_rcu(struct net_device *dev)
+{
+       return dev ? dev->ifindex : 0;
+}
+static inline int l3mdev_fib_oif(struct net_device *dev)
+{
+       return dev ? dev->ifindex : 0;
+}
+
+static inline u32 l3mdev_fib_table_rcu(const struct net_device *dev)
+{
+       return 0;
+}
+static inline u32 l3mdev_fib_table(const struct net_device *dev)
+{
+       return 0;
+}
+static inline u32 l3mdev_fib_table_by_index(struct net *net, int ifindex)
+{
+       return 0;
+}
+
+static inline struct rtable *l3mdev_get_rtable(const struct net_device *dev,
+                                              const struct flowi4 *fl4)
+{
+       return NULL;
+}
+
+static inline bool netif_index_is_l3_master(struct net *net, int ifindex)
+{
+       return false;
+}
+
+static inline void l3mdev_get_saddr(struct net *net, int ifindex,
+                                   struct flowi4 *fl4)
+{
+}
+
+static inline
+struct dst_entry *l3mdev_get_rt6_dst(const struct net_device *dev,
+                                    const struct flowi6 *fl6)
+{
+       return NULL;
+}
+static inline
+struct dst_entry *l3mdev_rt6_dst_by_oif(struct net *net,
+                                       const struct flowi6 *fl6)
+{
+       return NULL;
+}
+#endif
+
+#endif /* _NET_L3MDEV_H_ */
index fce0e35e74d075cd77f23a84efc6c36885c93ba9..66350ce3e955330c167dd8ffbb5dc1abfb02a6eb 100644 (file)
@@ -18,7 +18,7 @@ struct lwtunnel_state {
        __u16           type;
        __u16           flags;
        atomic_t        refcnt;
-       int             (*orig_output)(struct sock *sk, struct sk_buff *skb);
+       int             (*orig_output)(struct net *net, struct sock *sk, struct sk_buff *skb);
        int             (*orig_input)(struct sk_buff *);
        int             len;
        __u8            data[0];
@@ -28,7 +28,7 @@ struct lwtunnel_encap_ops {
        int (*build_state)(struct net_device *dev, struct nlattr *encap,
                           unsigned int family, const void *cfg,
                           struct lwtunnel_state **ts);
-       int (*output)(struct sock *sk, struct sk_buff *skb);
+       int (*output)(struct net *net, struct sock *sk, struct sk_buff *skb);
        int (*input)(struct sk_buff *skb);
        int (*fill_encap)(struct sk_buff *skb,
                          struct lwtunnel_state *lwtstate);
@@ -88,7 +88,7 @@ int lwtunnel_fill_encap(struct sk_buff *skb,
 int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate);
 struct lwtunnel_state *lwtunnel_state_alloc(int hdr_len);
 int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b);
-int lwtunnel_output(struct sock *sk, struct sk_buff *skb);
+int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb);
 int lwtunnel_input(struct sk_buff *skb);
 
 #else
@@ -160,7 +160,7 @@ static inline int lwtunnel_cmp_encap(struct lwtunnel_state *a,
        return 0;
 }
 
-static inline int lwtunnel_output(struct sock *sk, struct sk_buff *skb)
+static inline int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        return -EOPNOTSUPP;
 }
index bfc569498bfa793013766e6e16158d29bb84c9ef..82045fca388b20a9cf28ad9112236668c31f46cb 100644 (file)
@@ -5,6 +5,7 @@
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright (C) 2015 Intel Deutschland GmbH
  *
  * 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
@@ -481,7 +482,9 @@ struct ieee80211_event {
  *     Note that with TDLS this can be the case (channel is HT, protection must
  *     be used from this field) even when the BSS association isn't using HT.
  * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value
- *     implies disabled
+ *     implies disabled. As with the cfg80211 callback, a change here should
+ *     cause an event to be sent indicating where the current value is in
+ *     relation to the newly configured threshold.
  * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis
  * @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The
  *     may filter ARP queries targeted for other addresses than listed here.
@@ -1240,11 +1243,6 @@ enum ieee80211_smps_mode {
  * @flags: configuration flags defined above
  *
  * @listen_interval: listen interval in units of beacon interval
- * @max_sleep_period: the maximum number of beacon intervals to sleep for
- *     before checking the beacon for a TIM bit (managed mode only); this
- *     value will be only achievable between DTIM frames, the hardware
- *     needs to check for the multicast traffic bit in DTIM beacons.
- *     This variable is valid only when the CONF_PS flag is set.
  * @ps_dtim_period: The DTIM period of the AP we're connected to, for use
  *     in power saving. Power saving will not be enabled until a beacon
  *     has been received and the DTIM period is known.
@@ -1274,7 +1272,6 @@ enum ieee80211_smps_mode {
 struct ieee80211_conf {
        u32 flags;
        int power_level, dynamic_ps_timeout;
-       int max_sleep_period;
 
        u16 listen_interval;
        u8 ps_dtim_period;
@@ -1360,6 +1357,8 @@ enum ieee80211_vif_flags {
  * @debugfs_dir: debugfs dentry, can be used by drivers to create own per
  *     interface debug files. Note that it will be NULL for the virtual
  *     monitor interface (if that is requested.)
+ * @probe_req_reg: probe requests should be reported to mac80211 for this
+ *     interface.
  * @drv_priv: data area for driver use, will always be aligned to
  *     sizeof(void *).
  * @txq: the multicast data TX queue (if driver uses the TXQ abstraction)
@@ -1384,6 +1383,8 @@ struct ieee80211_vif {
        struct dentry *debugfs_dir;
 #endif
 
+       unsigned int probe_req_reg;
+
        /* must be last */
        u8 drv_priv[0] __aligned(sizeof(void *));
 };
@@ -1494,10 +1495,8 @@ enum ieee80211_key_flags {
  *     - Temporal Authenticator Rx MIC Key (64 bits)
  * @icv_len: The ICV length for this key type
  * @iv_len: The IV length for this key type
- * @drv_priv: pointer for driver use
  */
 struct ieee80211_key_conf {
-       void *drv_priv;
        atomic64_t tx_pn;
        u32 cipher;
        u8 icv_len;
@@ -1680,6 +1679,7 @@ struct ieee80211_sta_rates {
  * @tdls: indicates whether the STA is a TDLS peer
  * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
  *     valid if the STA is a TDLS peer in the first place.
+ * @mfp: indicates whether the STA uses management frame protection or not.
  * @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
  */
 struct ieee80211_sta {
@@ -1697,6 +1697,7 @@ struct ieee80211_sta {
        struct ieee80211_sta_rates __rcu *rates;
        bool tdls;
        bool tdls_initiator;
+       bool mfp;
 
        struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
 
@@ -1894,6 +1895,12 @@ struct ieee80211_txq {
  * @IEEE80211_HW_TDLS_WIDER_BW: The device/driver supports wider bandwidth
  *     than then BSS bandwidth for a TDLS link on the base channel.
  *
+ * @IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU: The driver supports receiving A-MSDUs
+ *     within A-MPDU.
+ *
+ * @IEEE80211_HW_BEACON_TX_STATUS: The device/driver provides TX status
+ *     for sent beacons.
+ *
  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
  */
 enum ieee80211_hw_flags {
@@ -1927,6 +1934,8 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_SUPPORTS_CLONED_SKBS,
        IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS,
        IEEE80211_HW_TDLS_WIDER_BW,
+       IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU,
+       IEEE80211_HW_BEACON_TX_STATUS,
 
        /* keep last, obviously */
        NUM_IEEE80211_HW_FLAGS
@@ -2827,6 +2836,13 @@ enum ieee80211_reconfig_type {
  *     See the section "Frame filtering" for more information.
  *     This callback must be implemented and can sleep.
  *
+ * @config_iface_filter: Configure the interface's RX filter.
+ *     This callback is optional and is used to configure which frames
+ *     should be passed to mac80211. The filter_flags is the combination
+ *     of FIF_* flags. The changed_flags is a bit mask that indicates
+ *     which flags are changed.
+ *     This callback can sleep.
+ *
  * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit
  *     must be set or cleared for a given STA. Must be atomic.
  *
@@ -3016,6 +3032,9 @@ enum ieee80211_reconfig_type {
  *     buffer size of 8. Correct ways to retransmit #1 would be:
  *      - TX:       1 or 18 or 81
  *     Even "189" would be wrong since 1 could be lost again.
+ *     The @amsdu parameter is valid when the action is set to
+ *     %IEEE80211_AMPDU_TX_OPERATIONAL and indicates the peer's ability
+ *     to receive A-MSDU within A-MPDU.
  *
  *     Returns a negative error code on failure.
  *     The callback can sleep.
@@ -3153,18 +3172,24 @@ enum ieee80211_reconfig_type {
  *     The callback is optional and can sleep.
  *
  * @add_chanctx: Notifies device driver about new channel context creation.
+ *     This callback may sleep.
  * @remove_chanctx: Notifies device driver about channel context destruction.
+ *     This callback may sleep.
  * @change_chanctx: Notifies device driver about channel context changes that
  *     may happen when combining different virtual interfaces on the same
  *     channel context with different settings
+ *     This callback may sleep.
  * @assign_vif_chanctx: Notifies device driver about channel context being bound
  *     to vif. Possible use is for hw queue remapping.
+ *     This callback may sleep.
  * @unassign_vif_chanctx: Notifies device driver about channel context being
  *     unbound from vif.
+ *     This callback may sleep.
  * @switch_vif_chanctx: switch a number of vifs from one chanctx to
  *     another, as specified in the list of
  *     @ieee80211_vif_chanctx_switch passed to the driver, according
  *     to the mode defined in &ieee80211_chanctx_switch_mode.
+ *     This callback may sleep.
  *
  * @start_ap: Start operation on the AP interface, this is called after all the
  *     information in bss_conf is set and beacon can be retrieved. A channel
@@ -3266,6 +3291,10 @@ struct ieee80211_ops {
                                 unsigned int changed_flags,
                                 unsigned int *total_flags,
                                 u64 multicast);
+       void (*config_iface_filter)(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   unsigned int filter_flags,
+                                   unsigned int changed_flags);
        int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
                       bool set);
        int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -3349,7 +3378,7 @@ struct ieee80211_ops {
                            struct ieee80211_vif *vif,
                            enum ieee80211_ampdu_mlme_action action,
                            struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                           u8 buf_size);
+                           u8 buf_size, bool amsdu);
        int (*get_survey)(struct ieee80211_hw *hw, int idx,
                struct survey_info *survey);
        void (*rfkill_poll)(struct ieee80211_hw *hw);
index b7f99615224bd05d7e4cb926aca27f14f44aab81..da574bbdc33393fc927ba7297f7d67d0cf59b06e 100644 (file)
 
 #include <net/cfg802154.h>
 
-/* General MAC frame format:
- *  2 bytes: Frame Control
- *  1 byte:  Sequence Number
- * 20 bytes: Addressing fields
- * 14 bytes: Auxiliary Security Header
- */
-#define MAC802154_FRAME_HARD_HEADER_LEN                (2 + 1 + 20 + 14)
-
 /**
  * enum ieee802154_hw_addr_filt_flags - hardware address filtering flags
  *
@@ -249,6 +241,21 @@ struct ieee802154_ops {
                                                const bool on);
 };
 
+/**
+ * ieee802154_get_fc_from_skb - get the frame control field from an skb
+ * @skb: skb where the frame control field will be get from
+ */
+static inline __le16 ieee802154_get_fc_from_skb(const struct sk_buff *skb)
+{
+       /* return some invalid fc on failure */
+       if (unlikely(skb->len < 2)) {
+               WARN_ON(1);
+               return cpu_to_le16(0);
+       }
+
+       return (__force __le16)__get_unaligned_memmove16(skb_mac_header(skb));
+}
+
 /**
  * ieee802154_be64_to_le64 - copies and convert be64 to le64
  * @le64_dst: le64 destination pointer
@@ -269,6 +276,16 @@ static inline void ieee802154_le64_to_be64(void *be64_dst, const void *le64_src)
        __put_unaligned_memmove64(swab64p(le64_src), be64_dst);
 }
 
+/**
+ * ieee802154_le16_to_be16 - copies and convert le16 to be16
+ * @be16_dst: be16 destination pointer
+ * @le16_src: le16 source pointer
+ */
+static inline void ieee802154_le16_to_be16(void *be16_dst, const void *le16_src)
+{
+       __put_unaligned_memmove16(swab16p(le16_src), be16_dst);
+}
+
 /**
  * ieee802154_alloc_hw - Allocate a new hardware device
  *
index 4757997f76edf4f8871a1b2d8407954b5c17abb5..179253f9dcfd986ef806331044bc4973f1cc7d6e 100644 (file)
@@ -18,7 +18,7 @@
 
 struct mpls_iptunnel_encap {
        u32     label[MAX_NEW_LABELS];
-       u32     labels;
+       u     labels;
 };
 
 static inline struct mpls_iptunnel_encap *mpls_lwtunnel_encap(struct lwtunnel_state *lwtstate)
index aba5695fadb00df5fb83ff2439474c1f7ecda65a..bf39374310305d61e2948cbd1801eee056efc323 100644 (file)
@@ -180,15 +180,13 @@ void ndisc_cleanup(void);
 
 int ndisc_rcv(struct sk_buff *skb);
 
-void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
-                  const struct in6_addr *solicit,
+void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
                   const struct in6_addr *daddr, const struct in6_addr *saddr,
                   struct sk_buff *oskb);
 
 void ndisc_send_rs(struct net_device *dev,
                   const struct in6_addr *saddr, const struct in6_addr *daddr);
-void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
-                  const struct in6_addr *daddr,
+void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
                   const struct in6_addr *solicited_addr,
                   bool router, bool solicited, bool override, bool inc_opt);
 
index d4c6b5f30acd936d863b1a5a5e89ef2443ad3943..e8d1448425a717facd1a6cfb71d72a9dfacc3ed7 100644 (file)
@@ -31,7 +31,7 @@ static inline void nf_bridge_push_encap_header(struct sk_buff *skb)
        skb->network_header -= len;
 }
 
-int br_nf_pre_routing_finish_bridge(struct sock *sk, struct sk_buff *skb);
+int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_buff *skb);
 
 static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
 {
@@ -45,12 +45,12 @@ struct net_device *setup_pre_routing(struct sk_buff *skb);
 void br_netfilter_enable(void);
 
 #if IS_ENABLED(CONFIG_IPV6)
-int br_validate_ipv6(struct sk_buff *skb);
-unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
+int br_validate_ipv6(struct net *net, struct sk_buff *skb);
+unsigned int br_nf_pre_routing_ipv6(void *priv,
                                    struct sk_buff *skb,
                                    const struct nf_hook_state *state);
 #else
-static inline int br_validate_ipv6(struct sk_buff *skb)
+static inline int br_validate_ipv6(struct net *net, struct sk_buff *skb)
 {
        return -1;
 }
index 42008f10dfc40218af2850716d86a54be1261e7c..0a14733e8b82ce622f0f9ba375bc5aa95aed4986 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _NF_DUP_IPV4_H_
 #define _NF_DUP_IPV4_H_
 
-void nf_dup_ipv4(struct sk_buff *skb, unsigned int hooknum,
+void nf_dup_ipv4(struct net *net, struct sk_buff *skb, unsigned int hooknum,
                 const struct in_addr *gw, int oif);
 
 #endif /* _NF_DUP_IPV4_H_ */
index 77862c3645f07000070f94e4838ca8b8cd792110..df7ecd806abaf7d7d2d9417390636df178ae611e 100644 (file)
@@ -6,7 +6,7 @@
 #include <net/icmp.h>
 
 void nf_send_unreach(struct sk_buff *skb_in, int code, int hook);
-void nf_send_reset(struct sk_buff *oldskb, int hook);
+void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook);
 
 const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb,
                                             struct tcphdr *_oth, int hook);
index 27666d8a0bd07f38f644bf7c768678ade981155b..fb7da5bb76cc8a58716b59d1ed421868ce18c697 100644 (file)
@@ -5,7 +5,7 @@ void nf_defrag_ipv6_enable(void);
 
 int nf_ct_frag6_init(void);
 void nf_ct_frag6_cleanup(void);
-struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user);
+struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user);
 void nf_ct_frag6_consume_orig(struct sk_buff *skb);
 
 struct inet_frags_ctl;
index ed6bd66fa5a0e713714beda7a56ecc3c6a595350..fa6237b382a3efced6dcc6b315ebfbe72206fb0c 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _NF_DUP_IPV6_H_
 #define _NF_DUP_IPV6_H_
 
-void nf_dup_ipv6(struct sk_buff *skb, unsigned int hooknum,
+void nf_dup_ipv6(struct net *net, struct sk_buff *skb, unsigned int hooknum,
                 const struct in6_addr *gw, int oif);
 
 #endif /* _NF_DUP_IPV6_H_ */
index e8ad46834df87453e1335bccb7e80dda2ba9fbe5..fde4068eec0b2963ca7155503ecae631e925fba9 100644 (file)
@@ -183,15 +183,12 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls);
 
 void nf_ct_free_hashtable(void *hash, unsigned int size);
 
-struct nf_conntrack_tuple_hash *
-__nf_conntrack_find(struct net *net, u16 zone,
-                   const struct nf_conntrack_tuple *tuple);
-
 int nf_conntrack_hash_check_insert(struct nf_conn *ct);
 bool nf_ct_delete(struct nf_conn *ct, u32 pid, int report);
 
 bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff,
-                      u_int16_t l3num, struct nf_conntrack_tuple *tuple);
+                      u_int16_t l3num, struct net *net,
+                      struct nf_conntrack_tuple *tuple);
 bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse,
                          const struct nf_conntrack_tuple *orig);
 
index c03f9c42b3cd32be938e282e47cf66e63536a8c3..788ef58a66b9b78807d164bce85060cc760ee336 100644 (file)
@@ -41,6 +41,7 @@ void nf_conntrack_cleanup_end(void);
 
 bool nf_ct_get_tuple(const struct sk_buff *skb, unsigned int nhoff,
                     unsigned int dataoff, u_int16_t l3num, u_int8_t protonum,
+                    struct net *net,
                     struct nf_conntrack_tuple *tuple,
                     const struct nf_conntrack_l3proto *l3proto,
                     const struct nf_conntrack_l4proto *l4proto);
index 1f7061313d54620d11c1fb2f4e08d99be961989c..956d8a6ac06935bc151a74ead1090268378eff70 100644 (file)
@@ -26,7 +26,7 @@ struct nf_conntrack_l4proto {
        /* Try to fill in the third arg: dataoff is offset past network protocol
            hdr.  Return true if possible. */
        bool (*pkt_to_tuple)(const struct sk_buff *skb, unsigned int dataoff,
-                            struct nf_conntrack_tuple *tuple);
+                            struct net *net, struct nf_conntrack_tuple *tuple);
 
        /* Invert the per-proto part of the tuple: ie. turn xmit into reply.
         * Some packets can't be inverted: return 0 in that case.
index 62308713dd7fa1704409e7db22fca725cc0200b9..f72be38860a747f39c6f4166365ab6768e69b88f 100644 (file)
@@ -20,10 +20,20 @@ struct ctnl_timeout {
 };
 
 struct nf_conn_timeout {
-       struct ctnl_timeout     *timeout;
+       struct ctnl_timeout __rcu *timeout;
 };
 
-#define NF_CT_TIMEOUT_EXT_DATA(__t) (unsigned int *) &((__t)->timeout->data)
+static inline unsigned int *
+nf_ct_timeout_data(struct nf_conn_timeout *t)
+{
+       struct ctnl_timeout *timeout;
+
+       timeout = rcu_dereference(t->timeout);
+       if (timeout == NULL)
+               return NULL;
+
+       return (unsigned int *)timeout->data;
+}
 
 static inline
 struct nf_conn_timeout *nf_ct_timeout_find(const struct nf_conn *ct)
@@ -47,7 +57,7 @@ struct nf_conn_timeout *nf_ct_timeout_ext_add(struct nf_conn *ct,
        if (timeout_ext == NULL)
                return NULL;
 
-       timeout_ext->timeout = timeout;
+       rcu_assign_pointer(timeout_ext->timeout, timeout);
 
        return timeout_ext;
 #else
@@ -64,10 +74,13 @@ nf_ct_timeout_lookup(struct net *net, struct nf_conn *ct,
        unsigned int *timeouts;
 
        timeout_ext = nf_ct_timeout_find(ct);
-       if (timeout_ext)
-               timeouts = NF_CT_TIMEOUT_EXT_DATA(timeout_ext);
-       else
+       if (timeout_ext) {
+               timeouts = nf_ct_timeout_data(timeout_ext);
+               if (unlikely(!timeouts))
+                       timeouts = l4proto->get_timeouts(net);
+       } else {
                timeouts = l4proto->get_timeouts(net);
+       }
 
        return timeouts;
 #else
index fbfd1ba4254e0c356ca382626cae9d7653e8295e..186c54138f358261f66aee9a86ec9d365e0c33ad 100644 (file)
@@ -10,7 +10,7 @@
 unsigned int nf_nat_packet(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
                           unsigned int hooknum, struct sk_buff *skb);
 
-int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family);
+int nf_xfrm_me_harder(struct net *net, struct sk_buff *skb, unsigned int family);
 
 static inline int nf_nat_initialized(struct nf_conn *ct,
                                     enum nf_nat_manip_type manip)
index a3127325f624b9afd8cf160c63358e557f5cdf40..aef3e5fc9fd935970d3bfe7d6ac2c6c65bb58425 100644 (file)
@@ -43,31 +43,31 @@ int nf_nat_icmp_reply_translation(struct sk_buff *skb, struct nf_conn *ct,
                                  enum ip_conntrack_info ctinfo,
                                  unsigned int hooknum);
 
-unsigned int nf_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
+unsigned int nf_nat_ipv4_in(void *priv, struct sk_buff *skb,
                            const struct nf_hook_state *state,
-                           unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+                           unsigned int (*do_chain)(void *priv,
                                                     struct sk_buff *skb,
                                                     const struct nf_hook_state *state,
                                                     struct nf_conn *ct));
 
-unsigned int nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
+unsigned int nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
                             const struct nf_hook_state *state,
-                            unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+                            unsigned int (*do_chain)(void *priv,
                                                      struct sk_buff *skb,
                                                      const struct nf_hook_state *state,
                                                      struct nf_conn *ct));
 
-unsigned int nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops,
+unsigned int nf_nat_ipv4_local_fn(void *priv,
                                  struct sk_buff *skb,
                                  const struct nf_hook_state *state,
-                                 unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+                                 unsigned int (*do_chain)(void *priv,
                                                           struct sk_buff *skb,
                                                           const struct nf_hook_state *state,
                                                           struct nf_conn *ct));
 
-unsigned int nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
+unsigned int nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
                            const struct nf_hook_state *state,
-                           unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+                           unsigned int (*do_chain)(void *priv,
                                                     struct sk_buff *skb,
                                                     const struct nf_hook_state *state,
                                                     struct nf_conn *ct));
@@ -76,31 +76,31 @@ int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, struct nf_conn *ct,
                                    enum ip_conntrack_info ctinfo,
                                    unsigned int hooknum, unsigned int hdrlen);
 
-unsigned int nf_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
+unsigned int nf_nat_ipv6_in(void *priv, struct sk_buff *skb,
                            const struct nf_hook_state *state,
-                           unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+                           unsigned int (*do_chain)(void *priv,
                                                     struct sk_buff *skb,
                                                     const struct nf_hook_state *state,
                                                     struct nf_conn *ct));
 
-unsigned int nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
+unsigned int nf_nat_ipv6_out(void *priv, struct sk_buff *skb,
                             const struct nf_hook_state *state,
-                            unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+                            unsigned int (*do_chain)(void *priv,
                                                      struct sk_buff *skb,
                                                      const struct nf_hook_state *state,
                                                      struct nf_conn *ct));
 
-unsigned int nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops,
+unsigned int nf_nat_ipv6_local_fn(void *priv,
                                  struct sk_buff *skb,
                                  const struct nf_hook_state *state,
-                                 unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+                                 unsigned int (*do_chain)(void *priv,
                                                           struct sk_buff *skb,
                                                           const struct nf_hook_state *state,
                                                           struct nf_conn *ct));
 
-unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
+unsigned int nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
                            const struct nf_hook_state *state,
-                           unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+                           unsigned int (*do_chain)(void *priv,
                                                     struct sk_buff *skb,
                                                     const struct nf_hook_state *state,
                                                     struct nf_conn *ct));
index e8635854a55bd8771d4290adbcdd7e05417e8c69..9c5638ad872e39d2d01cca6f86c1620e499c916c 100644 (file)
@@ -32,7 +32,7 @@ void nf_register_queue_handler(const struct nf_queue_handler *qh);
 void nf_unregister_queue_handler(void);
 void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict);
 
-bool nf_queue_entry_get_refs(struct nf_queue_entry *entry);
+void nf_queue_entry_get_refs(struct nf_queue_entry *entry);
 void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
 
 static inline void init_hashrandom(u32 *jhash_initval)
index aa8bee72c9d34288cd121ebd5ddd1e3ad7a7bf40..c9149cc0a02d30683288de140bc3276d75e640ce 100644 (file)
 
 struct nft_pktinfo {
        struct sk_buff                  *skb;
+       struct net                      *net;
        const struct net_device         *in;
        const struct net_device         *out;
-       const struct nf_hook_ops        *ops;
+       u8                              pf;
+       u8                              hook;
        u8                              nhoff;
        u8                              thoff;
        u8                              tprot;
@@ -25,16 +27,15 @@ struct nft_pktinfo {
 };
 
 static inline void nft_set_pktinfo(struct nft_pktinfo *pkt,
-                                  const struct nf_hook_ops *ops,
                                   struct sk_buff *skb,
                                   const struct nf_hook_state *state)
 {
        pkt->skb = skb;
+       pkt->net = pkt->xt.net = state->net;
        pkt->in = pkt->xt.in = state->in;
        pkt->out = pkt->xt.out = state->out;
-       pkt->ops = ops;
-       pkt->xt.hooknum = ops->hooknum;
-       pkt->xt.family = ops->pf;
+       pkt->hook = pkt->xt.hooknum = state->hook;
+       pkt->pf = pkt->xt.family = state->pf;
 }
 
 /**
@@ -815,8 +816,7 @@ int nft_register_basechain(struct nft_base_chain *basechain,
 void nft_unregister_basechain(struct nft_base_chain *basechain,
                              unsigned int hook_nops);
 
-unsigned int nft_do_chain(struct nft_pktinfo *pkt,
-                         const struct nf_hook_ops *ops);
+unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv);
 
 /**
  *     struct nft_table - nf_tables table
index 2df7f96902ee96edaa9a85f7c96c254220b5e12d..ca6ef6bf775ef544bce93b892ee156d449a62f9e 100644 (file)
@@ -6,13 +6,12 @@
 
 static inline void
 nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
-                    const struct nf_hook_ops *ops,
                     struct sk_buff *skb,
                     const struct nf_hook_state *state)
 {
        struct iphdr *ip;
 
-       nft_set_pktinfo(pkt, ops, skb, state);
+       nft_set_pktinfo(pkt, skb, state);
 
        ip = ip_hdr(pkt->skb);
        pkt->tprot = ip->protocol;
index 97db2e3a5e657c4b67fdbcfd9b609aea390e1ec0..8ad39a6a5fe18703be7808b5ed4d92cf186bafd6 100644 (file)
@@ -6,14 +6,13 @@
 
 static inline int
 nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
-                    const struct nf_hook_ops *ops,
                     struct sk_buff *skb,
                     const struct nf_hook_state *state)
 {
        int protohdr, thoff = 0;
        unsigned short frag_off;
 
-       nft_set_pktinfo(pkt, ops, skb, state);
+       nft_set_pktinfo(pkt, skb, state);
 
        protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL);
        /* If malformed, drop it */
diff --git a/include/net/netfilter/nfnetlink_queue.h b/include/net/netfilter/nfnetlink_queue.h
deleted file mode 100644 (file)
index aff88ba..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#ifndef _NET_NFNL_QUEUE_H_
-#define _NET_NFNL_QUEUE_H_
-
-#include <linux/netfilter/nf_conntrack_common.h>
-
-struct nf_conn;
-
-#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT
-struct nf_conn *nfqnl_ct_get(struct sk_buff *entskb, size_t *size,
-                            enum ip_conntrack_info *ctinfo);
-struct nf_conn *nfqnl_ct_parse(const struct sk_buff *skb,
-                              const struct nlattr *attr,
-                              enum ip_conntrack_info *ctinfo);
-int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct,
-                enum ip_conntrack_info ctinfo);
-void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
-                        enum ip_conntrack_info ctinfo, int diff);
-int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr,
-                       u32 portid, u32 report);
-#else
-inline struct nf_conn *
-nfqnl_ct_get(struct sk_buff *entskb, size_t *size, enum ip_conntrack_info *ctinfo)
-{
-       return NULL;
-}
-
-inline struct nf_conn *nfqnl_ct_parse(const struct sk_buff *skb,
-                                     const struct nlattr *attr,
-                                     enum ip_conntrack_info *ctinfo)
-{
-       return NULL;
-}
-
-inline int
-nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo)
-{
-       return 0;
-}
-
-inline void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
-                               enum ip_conntrack_info ctinfo, int diff)
-{
-}
-
-inline int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr,
-                              u32 portid, u32 report)
-{
-       return 0;
-}
-#endif /* NF_CONNTRACK */
-#endif
index 2a5dbcc90d1cafc950a4eafd3fd18b4d1e342d47..0e31727517556dabdc929c496e9a5e6c995de068 100644 (file)
@@ -1003,6 +1003,15 @@ static inline __be32 nla_get_be32(const struct nlattr *nla)
        return *(__be32 *) nla_data(nla);
 }
 
+/**
+ * nla_get_le32 - return payload of __le32 attribute
+ * @nla: __le32 netlink attribute
+ */
+static inline __le32 nla_get_le32(const struct nlattr *nla)
+{
+       return *(__le32 *) nla_data(nla);
+}
+
 /**
  * nla_get_u16 - return payload of u16 attribute
  * @nla: u16 netlink attribute
@@ -1065,6 +1074,15 @@ static inline __be64 nla_get_be64(const struct nlattr *nla)
        return tmp;
 }
 
+/**
+ * nla_get_le64 - return payload of __le64 attribute
+ * @nla: __le64 netlink attribute
+ */
+static inline __le64 nla_get_le64(const struct nlattr *nla)
+{
+       return *(__le64 *) nla_data(nla);
+}
+
 /**
  * nla_get_s32 - return payload of s32 attribute
  * @nla: s32 netlink attribute
index 75d2e188005969a1931e25ffe95a7f07335571cf..707e3ab816c27348ebfab8f9d5e6a9329264c9fd 100644 (file)
@@ -35,6 +35,7 @@
 #define NCI_MAX_NUM_RF_CONFIGS                                 10
 #define NCI_MAX_NUM_CONN                                       10
 #define NCI_MAX_PARAM_LEN                                      251
+#define NCI_MAX_PAYLOAD_SIZE                                   255
 #define NCI_MAX_PACKET_SIZE                                    258
 
 /* NCI Status Codes */
@@ -315,6 +316,8 @@ struct nci_nfcee_mode_set_cmd {
        __u8    nfcee_mode;
 } __packed;
 
+#define NCI_OP_CORE_GET_CONFIG_CMD     nci_opcode_pack(NCI_GID_CORE, 0x03)
+
 /* ----------------------- */
 /* ---- NCI Responses ---- */
 /* ----------------------- */
@@ -375,6 +378,9 @@ struct nci_nfcee_discover_rsp {
 } __packed;
 
 #define NCI_OP_NFCEE_MODE_SET_RSP nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x01)
+
+#define NCI_OP_CORE_GET_CONFIG_RSP     nci_opcode_pack(NCI_GID_CORE, 0x03)
+
 /* --------------------------- */
 /* ---- NCI Notifications ---- */
 /* --------------------------- */
@@ -528,4 +534,6 @@ struct nci_nfcee_discover_ntf {
        struct nci_nfcee_information_tlv        information_tlv;
 } __packed;
 
+#define NCI_OP_CORE_RESET_NTF          nci_opcode_pack(NCI_GID_CORE, 0x00)
+
 #endif /* __NCI_H */
index d0d0f1e53bb95efccfa7e1f311957087ae7724c4..57ce24fb0047046d2e12839df1d30df7ce4999d7 100644 (file)
@@ -67,7 +67,7 @@ enum nci_state {
 
 struct nci_dev;
 
-struct nci_prop_ops {
+struct nci_driver_ops {
        __u16 opcode;
        int (*rsp)(struct nci_dev *dev, struct sk_buff *skb);
        int (*ntf)(struct nci_dev *dev, struct sk_buff *skb);
@@ -94,8 +94,11 @@ struct nci_ops {
        void  (*hci_cmd_received)(struct nci_dev *ndev, u8 pipe, u8 cmd,
                                  struct sk_buff *skb);
 
-       struct nci_prop_ops *prop_ops;
+       struct nci_driver_ops *prop_ops;
        size_t n_prop_ops;
+
+       struct nci_driver_ops *core_ops;
+       size_t n_core_ops;
 };
 
 #define NCI_MAX_SUPPORTED_RF_INTERFACES                4
@@ -125,6 +128,8 @@ struct nci_conn_info {
 
 /* Gates */
 #define NCI_HCI_ADMIN_GATE         0x00
+#define NCI_HCI_LOOPBACK_GATE     0x04
+#define NCI_HCI_IDENTITY_MGMT_GATE 0x05
 #define NCI_HCI_LINK_MGMT_GATE     0x06
 
 /* Pipes */
@@ -278,10 +283,12 @@ int nci_request(struct nci_dev *ndev,
                            unsigned long opt),
                unsigned long opt, __u32 timeout);
 int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload);
+int nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len, __u8 *payload);
 int nci_core_reset(struct nci_dev *ndev);
 int nci_core_init(struct nci_dev *ndev);
 
 int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
+int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb);
 int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val);
 
 int nci_nfcee_discover(struct nci_dev *ndev, u8 action);
@@ -305,6 +312,7 @@ int nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx,
                      const u8 *param, size_t param_len);
 int nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx,
                      struct sk_buff **skb);
+int nci_hci_clear_all_pipes(struct nci_dev *ndev);
 int nci_hci_dev_session_init(struct nci_dev *ndev);
 
 static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev,
@@ -348,9 +356,14 @@ int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode,
                        struct sk_buff *skb);
 int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode,
                        struct sk_buff *skb);
+int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode,
+                       struct sk_buff *skb);
+int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode,
+                       struct sk_buff *skb);
 void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb);
 int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload);
 int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb);
+int nci_conn_max_data_pkt_payload_size(struct nci_dev *ndev, __u8 conn_id);
 void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
                                __u8 conn_id, int err);
 void nci_hci_data_received_cb(void *context, struct sk_buff *skb, int err);
@@ -365,6 +378,7 @@ void nci_clear_target_list(struct nci_dev *ndev);
 void nci_req_complete(struct nci_dev *ndev, int result);
 struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev,
                                                   int conn_id);
+int nci_get_conn_info_by_id(struct nci_dev *ndev, u8 id);
 
 /* ----- NCI status code ----- */
 int nci_to_errno(__u8 code);
@@ -380,6 +394,12 @@ struct nci_spi {
 
        unsigned int            xfer_udelay;    /* microseconds delay between
                                                  transactions */
+
+       unsigned int            xfer_speed_hz; /*
+                                               * SPI clock frequency
+                                               * 0 => default clock
+                                               */
+
        u8                      acknowledge_mode;
 
        struct completion       req_completion;
index 30afc9a6718c01d7ef22ab6758a8a7c4a8019acf..dcfcfc9c00bf56a4cf1681333e56bb21c14b23d9 100644 (file)
@@ -68,7 +68,7 @@ struct nfc_ops {
        int (*activate_target)(struct nfc_dev *dev, struct nfc_target *target,
                               u32 protocol);
        void (*deactivate_target)(struct nfc_dev *dev,
-                                 struct nfc_target *target);
+                                 struct nfc_target *target, u8 mode);
        int (*im_transceive)(struct nfc_dev *dev, struct nfc_target *target,
                             struct sk_buff *skb, data_exchange_cb_t cb,
                             void *cb_context);
index cf2713d8b975f11c6f7ba6725af165a53b5e82bb..32cb3e591e07b6a936a14788952c737c9bb277bd 100644 (file)
@@ -56,6 +56,22 @@ enum nl802154_commands {
 
        /* add new commands above here */
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+       NL802154_CMD_SET_SEC_PARAMS,
+       NL802154_CMD_GET_SEC_KEY,               /* can dump */
+       NL802154_CMD_NEW_SEC_KEY,
+       NL802154_CMD_DEL_SEC_KEY,
+       NL802154_CMD_GET_SEC_DEV,               /* can dump */
+       NL802154_CMD_NEW_SEC_DEV,
+       NL802154_CMD_DEL_SEC_DEV,
+       NL802154_CMD_GET_SEC_DEVKEY,            /* can dump */
+       NL802154_CMD_NEW_SEC_DEVKEY,
+       NL802154_CMD_DEL_SEC_DEVKEY,
+       NL802154_CMD_GET_SEC_LEVEL,             /* can dump */
+       NL802154_CMD_NEW_SEC_LEVEL,
+       NL802154_CMD_DEL_SEC_LEVEL,
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
        /* used to define NL802154_CMD_MAX below */
        __NL802154_CMD_AFTER_LAST,
        NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1
@@ -110,6 +126,18 @@ enum nl802154_attrs {
 
        /* add attributes here, update the policy in nl802154.c */
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+       NL802154_ATTR_SEC_ENABLED,
+       NL802154_ATTR_SEC_OUT_LEVEL,
+       NL802154_ATTR_SEC_OUT_KEY_ID,
+       NL802154_ATTR_SEC_FRAME_COUNTER,
+
+       NL802154_ATTR_SEC_LEVEL,
+       NL802154_ATTR_SEC_DEVICE,
+       NL802154_ATTR_SEC_DEVKEY,
+       NL802154_ATTR_SEC_KEY,
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
        __NL802154_ATTR_AFTER_LAST,
        NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1
 };
@@ -247,4 +275,167 @@ enum nl802154_supported_bool_states {
        NL802154_SUPPORTED_BOOL_MAX = __NL802154_SUPPORTED_BOOL_AFTER_LAST - 1
 };
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+
+enum nl802154_dev_addr_modes {
+       NL802154_DEV_ADDR_NONE,
+       __NL802154_DEV_ADDR_INVALID,
+       NL802154_DEV_ADDR_SHORT,
+       NL802154_DEV_ADDR_EXTENDED,
+
+       /* keep last */
+       __NL802154_DEV_ADDR_AFTER_LAST,
+       NL802154_DEV_ADDR_MAX = __NL802154_DEV_ADDR_AFTER_LAST - 1
+};
+
+enum nl802154_dev_addr_attrs {
+       NL802154_DEV_ADDR_ATTR_UNSPEC,
+
+       NL802154_DEV_ADDR_ATTR_PAN_ID,
+       NL802154_DEV_ADDR_ATTR_MODE,
+       NL802154_DEV_ADDR_ATTR_SHORT,
+       NL802154_DEV_ADDR_ATTR_EXTENDED,
+
+       /* keep last */
+       __NL802154_DEV_ADDR_ATTR_AFTER_LAST,
+       NL802154_DEV_ADDR_ATTR_MAX = __NL802154_DEV_ADDR_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_key_id_modes {
+       NL802154_KEY_ID_MODE_IMPLICIT,
+       NL802154_KEY_ID_MODE_INDEX,
+       NL802154_KEY_ID_MODE_INDEX_SHORT,
+       NL802154_KEY_ID_MODE_INDEX_EXTENDED,
+
+       /* keep last */
+       __NL802154_KEY_ID_MODE_AFTER_LAST,
+       NL802154_KEY_ID_MODE_MAX = __NL802154_KEY_ID_MODE_AFTER_LAST - 1
+};
+
+enum nl802154_key_id_attrs {
+       NL802154_KEY_ID_ATTR_UNSPEC,
+
+       NL802154_KEY_ID_ATTR_MODE,
+       NL802154_KEY_ID_ATTR_INDEX,
+       NL802154_KEY_ID_ATTR_IMPLICIT,
+       NL802154_KEY_ID_ATTR_SOURCE_SHORT,
+       NL802154_KEY_ID_ATTR_SOURCE_EXTENDED,
+
+       /* keep last */
+       __NL802154_KEY_ID_ATTR_AFTER_LAST,
+       NL802154_KEY_ID_ATTR_MAX = __NL802154_KEY_ID_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_seclevels {
+       NL802154_SECLEVEL_NONE,
+       NL802154_SECLEVEL_MIC32,
+       NL802154_SECLEVEL_MIC64,
+       NL802154_SECLEVEL_MIC128,
+       NL802154_SECLEVEL_ENC,
+       NL802154_SECLEVEL_ENC_MIC32,
+       NL802154_SECLEVEL_ENC_MIC64,
+       NL802154_SECLEVEL_ENC_MIC128,
+
+       /* keep last */
+       __NL802154_SECLEVEL_AFTER_LAST,
+       NL802154_SECLEVEL_MAX = __NL802154_SECLEVEL_AFTER_LAST - 1
+};
+
+enum nl802154_frames {
+       NL802154_FRAME_BEACON,
+       NL802154_FRAME_DATA,
+       NL802154_FRAME_ACK,
+       NL802154_FRAME_CMD,
+
+       /* keep last */
+       __NL802154_FRAME_AFTER_LAST,
+       NL802154_FRAME_MAX = __NL802154_FRAME_AFTER_LAST - 1
+};
+
+enum nl802154_cmd_frames {
+       __NL802154_CMD_FRAME_INVALID,
+       NL802154_CMD_FRAME_ASSOC_REQUEST,
+       NL802154_CMD_FRAME_ASSOC_RESPONSE,
+       NL802154_CMD_FRAME_DISASSOC_NOTIFY,
+       NL802154_CMD_FRAME_DATA_REQUEST,
+       NL802154_CMD_FRAME_PAN_ID_CONFLICT_NOTIFY,
+       NL802154_CMD_FRAME_ORPHAN_NOTIFY,
+       NL802154_CMD_FRAME_BEACON_REQUEST,
+       NL802154_CMD_FRAME_COORD_REALIGNMENT,
+       NL802154_CMD_FRAME_GTS_REQUEST,
+
+       /* keep last */
+       __NL802154_CMD_FRAME_AFTER_LAST,
+       NL802154_CMD_FRAME_MAX = __NL802154_CMD_FRAME_AFTER_LAST - 1
+};
+
+enum nl802154_seclevel_attrs {
+       NL802154_SECLEVEL_ATTR_UNSPEC,
+
+       NL802154_SECLEVEL_ATTR_LEVELS,
+       NL802154_SECLEVEL_ATTR_FRAME,
+       NL802154_SECLEVEL_ATTR_CMD_FRAME,
+       NL802154_SECLEVEL_ATTR_DEV_OVERRIDE,
+
+       /* keep last */
+       __NL802154_SECLEVEL_ATTR_AFTER_LAST,
+       NL802154_SECLEVEL_ATTR_MAX = __NL802154_SECLEVEL_ATTR_AFTER_LAST - 1
+};
+
+/* TODO what is this? couldn't find in mib */
+enum {
+       NL802154_DEVKEY_IGNORE,
+       NL802154_DEVKEY_RESTRICT,
+       NL802154_DEVKEY_RECORD,
+
+       /* keep last */
+       __NL802154_DEVKEY_AFTER_LAST,
+       NL802154_DEVKEY_MAX = __NL802154_DEVKEY_AFTER_LAST - 1
+};
+
+enum nl802154_dev {
+       NL802154_DEV_ATTR_UNSPEC,
+
+       NL802154_DEV_ATTR_FRAME_COUNTER,
+       NL802154_DEV_ATTR_PAN_ID,
+       NL802154_DEV_ATTR_SHORT_ADDR,
+       NL802154_DEV_ATTR_EXTENDED_ADDR,
+       NL802154_DEV_ATTR_SECLEVEL_EXEMPT,
+       NL802154_DEV_ATTR_KEY_MODE,
+
+       /* keep last */
+       __NL802154_DEV_ATTR_AFTER_LAST,
+       NL802154_DEV_ATTR_MAX = __NL802154_DEV_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_devkey {
+       NL802154_DEVKEY_ATTR_UNSPEC,
+
+       NL802154_DEVKEY_ATTR_FRAME_COUNTER,
+       NL802154_DEVKEY_ATTR_EXTENDED_ADDR,
+       NL802154_DEVKEY_ATTR_ID,
+
+       /* keep last */
+       __NL802154_DEVKEY_ATTR_AFTER_LAST,
+       NL802154_DEVKEY_ATTR_MAX = __NL802154_DEVKEY_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_key {
+       NL802154_KEY_ATTR_UNSPEC,
+
+       NL802154_KEY_ATTR_ID,
+       NL802154_KEY_ATTR_USAGE_FRAMES,
+       NL802154_KEY_ATTR_USAGE_CMDS,
+       NL802154_KEY_ATTR_BYTES,
+
+       /* keep last */
+       __NL802154_KEY_ATTR_AFTER_LAST,
+       NL802154_KEY_ATTR_MAX = __NL802154_KEY_ATTR_AFTER_LAST - 1
+};
+
+#define NL802154_KEY_SIZE              16
+#define NL802154_CMD_FRAME_NR_IDS      256
+
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 #endif /* __NL802154_H */
index 87935cad2f7b37eadd01d30a3d39a9f73d0145b9..a0dde04eb178015ac97d17a6243cbe50cb83a885 100644 (file)
@@ -32,17 +32,17 @@ struct request_sock_ops {
        int             obj_size;
        struct kmem_cache       *slab;
        char            *slab_name;
-       int             (*rtx_syn_ack)(struct sock *sk,
+       int             (*rtx_syn_ack)(const struct sock *sk,
                                       struct request_sock *req);
-       void            (*send_ack)(struct sock *sk, struct sk_buff *skb,
+       void            (*send_ack)(const struct sock *sk, struct sk_buff *skb,
                                    struct request_sock *req);
-       void            (*send_reset)(struct sock *sk,
+       void            (*send_reset)(const struct sock *sk,
                                      struct sk_buff *skb);
        void            (*destructor)(struct request_sock *req);
        void            (*syn_ack_timeout)(const struct request_sock *req);
 };
 
-int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req);
+int inet_rtx_syn_ack(const struct sock *parent, struct request_sock *req);
 
 /* struct request_sock - mini sock to represent a connection request
  */
@@ -50,16 +50,15 @@ struct request_sock {
        struct sock_common              __req_common;
 #define rsk_refcnt                     __req_common.skc_refcnt
 #define rsk_hash                       __req_common.skc_hash
+#define rsk_listener                   __req_common.skc_listener
+#define rsk_window_clamp               __req_common.skc_window_clamp
+#define rsk_rcv_wnd                    __req_common.skc_rcv_wnd
 
        struct request_sock             *dl_next;
-       struct sock                     *rsk_listener;
        u16                             mss;
        u8                              num_retrans; /* number of retransmits */
        u8                              cookie_ts:1; /* syncookie: encode tcpopts in timestamp */
        u8                              num_timeout:7; /* number of timeouts */
-       /* The following two fields can be easily recomputed I think -AK */
-       u32                             window_clamp; /* window clamp at creation time */
-       u32                             rcv_wnd;          /* rcv_wnd offered first time */
        u32                             ts_recent;
        struct timer_list               rsk_timer;
        const struct request_sock_ops   *rsk_ops;
@@ -69,15 +68,35 @@ struct request_sock {
        u32                             peer_secid;
 };
 
+static inline struct request_sock *inet_reqsk(struct sock *sk)
+{
+       return (struct request_sock *)sk;
+}
+
+static inline struct sock *req_to_sk(struct request_sock *req)
+{
+       return (struct sock *)req;
+}
+
 static inline struct request_sock *
-reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener)
+reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener,
+           bool attach_listener)
 {
-       struct request_sock *req = kmem_cache_alloc(ops->slab, GFP_ATOMIC);
+       struct request_sock *req;
+
+       req = kmem_cache_alloc(ops->slab, GFP_ATOMIC | __GFP_NOWARN);
 
        if (req) {
                req->rsk_ops = ops;
-               sock_hold(sk_listener);
-               req->rsk_listener = sk_listener;
+               if (attach_listener) {
+                       sock_hold(sk_listener);
+                       req->rsk_listener = sk_listener;
+               } else {
+                       req->rsk_listener = NULL;
+               }
+               req_to_sk(req)->sk_prot = sk_listener->sk_prot;
+               sk_node_init(&req_to_sk(req)->sk_node);
+               sk_tx_queue_clear(req_to_sk(req));
                req->saved_syn = NULL;
                /* Following is temporary. It is coupled with debugging
                 * helpers in reqsk_put() & reqsk_free()
@@ -87,16 +106,6 @@ reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener)
        return req;
 }
 
-static inline struct request_sock *inet_reqsk(struct sock *sk)
-{
-       return (struct request_sock *)sk;
-}
-
-static inline struct sock *req_to_sk(struct request_sock *req)
-{
-       return (struct sock *)req;
-}
-
 static inline void reqsk_free(struct request_sock *req)
 {
        /* temporary debugging */
@@ -117,26 +126,6 @@ static inline void reqsk_put(struct request_sock *req)
 
 extern int sysctl_max_syn_backlog;
 
-/** struct listen_sock - listen state
- *
- * @max_qlen_log - log_2 of maximal queued SYNs/REQUESTs
- */
-struct listen_sock {
-       int                     qlen_inc; /* protected by listener lock */
-       int                     young_inc;/* protected by listener lock */
-
-       /* following fields can be updated by timer */
-       atomic_t                qlen_dec; /* qlen = qlen_inc - qlen_dec */
-       atomic_t                young_dec;
-
-       u8                      max_qlen_log ____cacheline_aligned_in_smp;
-       u8                      synflood_warned;
-       /* 2 bytes hole, try to use */
-       u32                     hash_rnd;
-       u32                     nr_table_entries;
-       struct request_sock     *syn_table[0];
-};
-
 /*
  * For a TCP Fast Open listener -
  *     lock - protects the access to all the reqsk, which is co-owned by
@@ -170,127 +159,72 @@ struct fastopen_queue {
  * @rskq_accept_head - FIFO head of established children
  * @rskq_accept_tail - FIFO tail of established children
  * @rskq_defer_accept - User waits for some data after accept()
- * @syn_wait_lock - serializer
- *
- * %syn_wait_lock is necessary only to avoid proc interface having to grab the main
- * lock sock while browsing the listening hash (otherwise it's deadlock prone).
  *
  */
 struct request_sock_queue {
+       spinlock_t              rskq_lock;
+       u8                      rskq_defer_accept;
+
+       u32                     synflood_warned;
+       atomic_t                qlen;
+       atomic_t                young;
+
        struct request_sock     *rskq_accept_head;
        struct request_sock     *rskq_accept_tail;
-       u8                      rskq_defer_accept;
-       struct listen_sock      *listen_opt;
-       struct fastopen_queue   *fastopenq; /* This is non-NULL iff TFO has been
-                                            * enabled on this listener. Check
-                                            * max_qlen != 0 in fastopen_queue
-                                            * to determine if TFO is enabled
-                                            * right at this moment.
+       struct fastopen_queue   fastopenq;  /* Check max_qlen != 0 to determine
+                                            * if TFO is enabled.
                                             */
-
-       /* temporary alignment, our goal is to get rid of this lock */
-       spinlock_t              syn_wait_lock ____cacheline_aligned_in_smp;
 };
 
-int reqsk_queue_alloc(struct request_sock_queue *queue,
-                     unsigned int nr_table_entries);
+void reqsk_queue_alloc(struct request_sock_queue *queue);
 
-void __reqsk_queue_destroy(struct request_sock_queue *queue);
-void reqsk_queue_destroy(struct request_sock_queue *queue);
 void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req,
                           bool reset);
 
-static inline struct request_sock *
-       reqsk_queue_yank_acceptq(struct request_sock_queue *queue)
-{
-       struct request_sock *req = queue->rskq_accept_head;
-
-       queue->rskq_accept_head = NULL;
-       return req;
-}
-
-static inline int reqsk_queue_empty(struct request_sock_queue *queue)
+static inline bool reqsk_queue_empty(const struct request_sock_queue *queue)
 {
        return queue->rskq_accept_head == NULL;
 }
 
-static inline void reqsk_queue_add(struct request_sock_queue *queue,
-                                  struct request_sock *req,
-                                  struct sock *parent,
-                                  struct sock *child)
+static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue *queue,
+                                                     struct sock *parent)
 {
-       req->sk = child;
-       sk_acceptq_added(parent);
-
-       if (queue->rskq_accept_head == NULL)
-               queue->rskq_accept_head = req;
-       else
-               queue->rskq_accept_tail->dl_next = req;
-
-       queue->rskq_accept_tail = req;
-       req->dl_next = NULL;
-}
-
-static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue *queue)
-{
-       struct request_sock *req = queue->rskq_accept_head;
-
-       WARN_ON(req == NULL);
-
-       queue->rskq_accept_head = req->dl_next;
-       if (queue->rskq_accept_head == NULL)
-               queue->rskq_accept_tail = NULL;
+       struct request_sock *req;
 
+       spin_lock_bh(&queue->rskq_lock);
+       req = queue->rskq_accept_head;
+       if (req) {
+               sk_acceptq_removed(parent);
+               queue->rskq_accept_head = req->dl_next;
+               if (queue->rskq_accept_head == NULL)
+                       queue->rskq_accept_tail = NULL;
+       }
+       spin_unlock_bh(&queue->rskq_lock);
        return req;
 }
 
 static inline void reqsk_queue_removed(struct request_sock_queue *queue,
                                       const struct request_sock *req)
 {
-       struct listen_sock *lopt = queue->listen_opt;
-
        if (req->num_timeout == 0)
-               atomic_inc(&lopt->young_dec);
-       atomic_inc(&lopt->qlen_dec);
+               atomic_dec(&queue->young);
+       atomic_dec(&queue->qlen);
 }
 
 static inline void reqsk_queue_added(struct request_sock_queue *queue)
 {
-       struct listen_sock *lopt = queue->listen_opt;
-
-       lopt->young_inc++;
-       lopt->qlen_inc++;
-}
-
-static inline int listen_sock_qlen(const struct listen_sock *lopt)
-{
-       return lopt->qlen_inc - atomic_read(&lopt->qlen_dec);
-}
-
-static inline int listen_sock_young(const struct listen_sock *lopt)
-{
-       return lopt->young_inc - atomic_read(&lopt->young_dec);
+       atomic_inc(&queue->young);
+       atomic_inc(&queue->qlen);
 }
 
 static inline int reqsk_queue_len(const struct request_sock_queue *queue)
 {
-       const struct listen_sock *lopt = queue->listen_opt;
-
-       return lopt ? listen_sock_qlen(lopt) : 0;
+       return atomic_read(&queue->qlen);
 }
 
 static inline int reqsk_queue_len_young(const struct request_sock_queue *queue)
 {
-       return listen_sock_young(queue->listen_opt);
+       return atomic_read(&queue->young);
 }
 
-static inline int reqsk_queue_is_full(const struct request_sock_queue *queue)
-{
-       return reqsk_queue_len(queue) >> queue->listen_opt->max_qlen_log;
-}
-
-void reqsk_queue_hash_req(struct request_sock_queue *queue,
-                         u32 hash, struct request_sock *req,
-                         unsigned long timeout);
-
 #endif /* _REQUEST_SOCK_H */
index f46af256880cd9ff0f9fccc503d7cdc83ef3cbd3..ee81307863d56329c097bb3ec30d06b07828b2d0 100644 (file)
@@ -28,6 +28,8 @@
 #include <net/inetpeer.h>
 #include <net/flow.h>
 #include <net/inet_sock.h>
+#include <net/ip_fib.h>
+#include <net/l3mdev.h>
 #include <linux/in_route.h>
 #include <linux/rtnetlink.h>
 #include <linux/rcupdate.h>
@@ -64,6 +66,8 @@ struct rtable {
        /* Miscellaneous cached information */
        u32                     rt_pmtu;
 
+       u32                     rt_table_id;
+
        struct list_head        rt_uncached;
        struct uncached_list    *rt_uncached_list;
 };
@@ -110,9 +114,17 @@ struct in_device;
 int ip_rt_init(void);
 void rt_cache_flush(struct net *net);
 void rt_flush_dev(struct net_device *dev);
-struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp);
+struct rtable *__ip_route_output_key_hash(struct net *, struct flowi4 *flp,
+                                         int mp_hash);
+
+static inline struct rtable *__ip_route_output_key(struct net *net,
+                                                  struct flowi4 *flp)
+{
+       return __ip_route_output_key_hash(net, flp, -1);
+}
+
 struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp,
-                                   struct sock *sk);
+                                   const struct sock *sk);
 struct dst_entry *ipv4_blackhole_route(struct net *net,
                                       struct dst_entry *dst_orig);
 
@@ -254,9 +266,6 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32
        if (inet_sk(sk)->transparent)
                flow_flags |= FLOWI_FLAG_ANYSRC;
 
-       if (netif_index_is_vrf(sock_net(sk), oif))
-               flow_flags |= FLOWI_FLAG_VRFSRC | FLOWI_FLAG_SKIP_NH_OIF;
-
        flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE,
                           protocol, flow_flags, dst, src, dport, sport);
 }
@@ -273,6 +282,10 @@ static inline struct rtable *ip_route_connect(struct flowi4 *fl4,
        ip_route_connect_init(fl4, dst, src, tos, oif, protocol,
                              sport, dport, sk);
 
+       if (!src && oif) {
+               l3mdev_get_saddr(net, oif, fl4);
+               src = fl4->saddr;
+       }
        if (!dst || !src) {
                rt = __ip_route_output_key(net, fl4);
                if (IS_ERR(rt))
index 18fdb98185ab36ae750a2d77078ea16ac87dd0e6..2f87c1ba13de639df7b733ad3905c7e47b8d901b 100644 (file)
@@ -122,8 +122,10 @@ struct rtnl_af_ops {
        int                     family;
 
        int                     (*fill_link_af)(struct sk_buff *skb,
-                                               const struct net_device *dev);
-       size_t                  (*get_link_af_size)(const struct net_device *dev);
+                                               const struct net_device *dev,
+                                               u32 ext_filter_mask);
+       size_t                  (*get_link_af_size)(const struct net_device *dev,
+                                                   u32 ext_filter_mask);
 
        int                     (*validate_link_af)(const struct net_device *dev,
                                                    const struct nlattr *attr);
index 444faa89a55fdd5e2b581d0ae93c3553268fa7e5..4c79ce8c1f92f2d47eb87ffacd55fa01292e7378 100644 (file)
@@ -251,7 +251,7 @@ struct tcf_proto {
 struct qdisc_skb_cb {
        unsigned int            pkt_len;
        u16                     slave_dev_queue_mapping;
-       u16                     _pad;
+       u16                     tc_classid;
 #define QDISC_CB_PRIV_LEN 20
        unsigned char           data[QDISC_CB_PRIV_LEN];
 };
@@ -402,6 +402,7 @@ void __qdisc_calculate_pkt_len(struct sk_buff *skb,
                               const struct qdisc_size_table *stab);
 bool tcf_destroy(struct tcf_proto *tp, bool force);
 void tcf_destroy_chain(struct tcf_proto __rcu **fl);
+int skb_do_redirect(struct sk_buff *);
 
 /* Reset all TX qdiscs greater then index of a device.  */
 static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i)
index e23717013a4e6cb1ef84e6b8ba6654b011ee8670..f570e75e3da927c54438340988f2a56762220060 100644 (file)
@@ -150,6 +150,10 @@ typedef __u64 __bitwise __addrpair;
  *     @skc_node: main hash linkage for various protocol lookup tables
  *     @skc_nulls_node: main hash linkage for TCP/UDP/UDP-Lite protocol
  *     @skc_tx_queue_mapping: tx queue number for this connection
+ *     @skc_flags: place holder for sk_flags
+ *             %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE,
+ *             %SO_OOBINLINE settings, %SO_TIMESTAMPING settings
+ *     @skc_incoming_cpu: record/match cpu processing incoming packets
  *     @skc_refcnt: reference count
  *
  *     This is the minimal network layer representation of sockets, the header
@@ -200,6 +204,16 @@ struct sock_common {
 
        atomic64_t              skc_cookie;
 
+       /* following fields are padding to force
+        * offset(struct sock, sk_refcnt) == 128 on 64bit arches
+        * assuming IPV6 is enabled. We use this padding differently
+        * for different kind of 'sockets'
+        */
+       union {
+               unsigned long   skc_flags;
+               struct sock     *skc_listener; /* request_sock */
+               struct inet_timewait_death_row *skc_tw_dr; /* inet_timewait_sock */
+       };
        /*
         * fields between dontcopy_begin/dontcopy_end
         * are not copied in sock_copy()
@@ -212,9 +226,20 @@ struct sock_common {
                struct hlist_nulls_node skc_nulls_node;
        };
        int                     skc_tx_queue_mapping;
+       union {
+               int             skc_incoming_cpu;
+               u32             skc_rcv_wnd;
+               u32             skc_tw_rcv_nxt; /* struct tcp_timewait_sock  */
+       };
+
        atomic_t                skc_refcnt;
        /* private: */
        int                     skc_dontcopy_end[0];
+       union {
+               u32             skc_rxhash;
+               u32             skc_window_clamp;
+               u32             skc_tw_snd_nxt; /* struct tcp_timewait_sock */
+       };
        /* public: */
 };
 
@@ -243,8 +268,6 @@ struct cg_proto;
   *    @sk_pacing_rate: Pacing rate (if supported by transport/packet scheduler)
   *    @sk_max_pacing_rate: Maximum pacing rate (%SO_MAX_PACING_RATE)
   *    @sk_sndbuf: size of send buffer in bytes
-  *    @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE,
-  *               %SO_OOBINLINE settings, %SO_TIMESTAMPING settings
   *    @sk_no_check_tx: %SO_NO_CHECK setting, set checksum in TX packets
   *    @sk_no_check_rx: allow zero checksum in RX packets
   *    @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO)
@@ -273,8 +296,6 @@ struct cg_proto;
   *    @sk_rcvlowat: %SO_RCVLOWAT setting
   *    @sk_rcvtimeo: %SO_RCVTIMEO setting
   *    @sk_sndtimeo: %SO_SNDTIMEO setting
-  *    @sk_rxhash: flow hash received from netif layer
-  *    @sk_incoming_cpu: record cpu processing incoming packets
   *    @sk_txhash: computed flow hash for use on transmit
   *    @sk_filter: socket filtering instructions
   *    @sk_timer: sock cleanup timer
@@ -331,6 +352,9 @@ struct sock {
 #define sk_v6_daddr            __sk_common.skc_v6_daddr
 #define sk_v6_rcv_saddr        __sk_common.skc_v6_rcv_saddr
 #define sk_cookie              __sk_common.skc_cookie
+#define sk_incoming_cpu                __sk_common.skc_incoming_cpu
+#define sk_flags               __sk_common.skc_flags
+#define sk_rxhash              __sk_common.skc_rxhash
 
        socket_lock_t           sk_lock;
        struct sk_buff_head     sk_receive_queue;
@@ -350,14 +374,6 @@ struct sock {
        } sk_backlog;
 #define sk_rmem_alloc sk_backlog.rmem_alloc
        int                     sk_forward_alloc;
-#ifdef CONFIG_RPS
-       __u32                   sk_rxhash;
-#endif
-       u16                     sk_incoming_cpu;
-       /* 16bit hole
-        * Warned : sk_incoming_cpu can be set from softirq,
-        * Do not use this hole without fully understanding possible issues.
-        */
 
        __u32                   sk_txhash;
 #ifdef CONFIG_NET_RX_BUSY_POLL
@@ -373,7 +389,6 @@ struct sock {
 #ifdef CONFIG_XFRM
        struct xfrm_policy      *sk_policy[2];
 #endif
-       unsigned long           sk_flags;
        struct dst_entry        *sk_rx_dst;
        struct dst_entry __rcu  *sk_dst_cache;
        spinlock_t              sk_dst_lock;
@@ -759,7 +774,7 @@ static inline int sk_memalloc_socks(void)
 
 #endif
 
-static inline gfp_t sk_gfp_atomic(struct sock *sk, gfp_t gfp_mask)
+static inline gfp_t sk_gfp_atomic(const struct sock *sk, gfp_t gfp_mask)
 {
        return GFP_ATOMIC | (sk->sk_allocation & __GFP_MEMALLOC);
 }
@@ -1522,6 +1537,13 @@ void sock_kfree_s(struct sock *sk, void *mem, int size);
 void sock_kzfree_s(struct sock *sk, void *mem, int size);
 void sk_send_sigurg(struct sock *sk);
 
+struct sockcm_cookie {
+       u32 mark;
+};
+
+int sock_cmsg_send(struct sock *sk, struct msghdr *msg,
+                  struct sockcm_cookie *sockc);
+
 /*
  * Functions to fill in entries in struct proto_ops when a protocol
  * does not implement a particular function.
@@ -1662,12 +1684,16 @@ static inline void sock_graft(struct sock *sk, struct socket *parent)
 kuid_t sock_i_uid(struct sock *sk);
 unsigned long sock_i_ino(struct sock *sk);
 
-static inline void sk_set_txhash(struct sock *sk)
+static inline u32 net_tx_rndhash(void)
 {
-       sk->sk_txhash = prandom_u32();
+       u32 v = prandom_u32();
+
+       return v ?: 1;
+}
 
-       if (unlikely(!sk->sk_txhash))
-               sk->sk_txhash = 1;
+static inline void sk_set_txhash(struct sock *sk)
+{
+       sk->sk_txhash = net_tx_rndhash();
 }
 
 static inline void sk_rethink_txhash(struct sock *sk)
@@ -1925,6 +1951,8 @@ static inline void skb_set_hash_from_sk(struct sk_buff *skb, struct sock *sk)
        }
 }
 
+void skb_set_owner_w(struct sk_buff *skb, struct sock *sk);
+
 /*
  *     Queue a received datagram if it will fit. Stream and sequenced
  *     protocols can't normally use this as they need to fit buffers in
@@ -1933,21 +1961,6 @@ static inline void skb_set_hash_from_sk(struct sk_buff *skb, struct sock *sk)
  *     Inlined as it's very short and called for pretty much every
  *     packet ever received.
  */
-
-static inline void skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
-{
-       skb_orphan(skb);
-       skb->sk = sk;
-       skb->destructor = sock_wfree;
-       skb_set_hash_from_sk(skb, sk);
-       /*
-        * We used to take a refcount on sk, but following operation
-        * is enough to guarantee sk_free() wont free this sock until
-        * all in-flight packets are completed
-        */
-       atomic_add(skb->truesize, &sk->sk_wmem_alloc);
-}
-
 static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
 {
        skb_orphan(skb);
@@ -2205,6 +2218,14 @@ static inline bool sk_fullsock(const struct sock *sk)
        return (1 << sk->sk_state) & ~(TCPF_TIME_WAIT | TCPF_NEW_SYN_RECV);
 }
 
+/* This helper checks if a socket is a LISTEN or NEW_SYN_RECV
+ * SYNACK messages can be attached to either ones (depending on SYNCOOKIE)
+ */
+static inline bool sk_listener(const struct sock *sk)
+{
+       return (1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV);
+}
+
 void sock_enable_timestamp(struct sock *sk, int flag);
 int sock_get_timestamp(struct sock *, struct timeval __user *);
 int sock_get_timestampns(struct sock *, struct timespec __user *);
index 319baab3b48efb9035a7987c6dbf96b9f8afe5b0..bc865e244efea0c4757a182274ad7ca9f9cc3266 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * include/net/switchdev.h - Switch device API
- * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ * Copyright (c) 2014-2015 Jiri Pirko <jiri@resnulli.us>
  * Copyright (c) 2014-2015 Scott Feldman <sfeldma@gmail.com>
  *
  * This program is free software; you can redistribute it and/or modify
 
 #include <linux/netdevice.h>
 #include <linux/notifier.h>
+#include <linux/list.h>
+#include <net/ip_fib.h>
 
 #define SWITCHDEV_F_NO_RECURSE         BIT(0)
+#define SWITCHDEV_F_SKIP_EOPNOTSUPP    BIT(1)
+#define SWITCHDEV_F_DEFER              BIT(2)
 
-enum switchdev_trans {
-       SWITCHDEV_TRANS_NONE,
-       SWITCHDEV_TRANS_PREPARE,
-       SWITCHDEV_TRANS_ABORT,
-       SWITCHDEV_TRANS_COMMIT,
+struct switchdev_trans_item {
+       struct list_head list;
+       void *data;
+       void (*destructor)(const void *data);
 };
 
+struct switchdev_trans {
+       struct list_head item_list;
+       bool ph_prepare;
+};
+
+static inline bool switchdev_trans_ph_prepare(struct switchdev_trans *trans)
+{
+       return trans && trans->ph_prepare;
+}
+
+static inline bool switchdev_trans_ph_commit(struct switchdev_trans *trans)
+{
+       return trans && !trans->ph_prepare;
+}
+
 enum switchdev_attr_id {
-       SWITCHDEV_ATTR_UNDEFINED,
-       SWITCHDEV_ATTR_PORT_PARENT_ID,
-       SWITCHDEV_ATTR_PORT_STP_STATE,
-       SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS,
+       SWITCHDEV_ATTR_ID_UNDEFINED,
+       SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
+       SWITCHDEV_ATTR_ID_PORT_STP_STATE,
+       SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
+       SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
 };
 
 struct switchdev_attr {
        enum switchdev_attr_id id;
-       enum switchdev_trans trans;
        u32 flags;
        union {
                struct netdev_phys_item_id ppid;        /* PORT_PARENT_ID */
                u8 stp_state;                           /* PORT_STP_STATE */
                unsigned long brport_flags;             /* PORT_BRIDGE_FLAGS */
+               u32 ageing_time;                        /* BRIDGE_AGEING_TIME */
        } u;
 };
 
-struct fib_info;
-
 enum switchdev_obj_id {
-       SWITCHDEV_OBJ_UNDEFINED,
-       SWITCHDEV_OBJ_PORT_VLAN,
-       SWITCHDEV_OBJ_IPV4_FIB,
-       SWITCHDEV_OBJ_PORT_FDB,
+       SWITCHDEV_OBJ_ID_UNDEFINED,
+       SWITCHDEV_OBJ_ID_PORT_VLAN,
+       SWITCHDEV_OBJ_ID_IPV4_FIB,
+       SWITCHDEV_OBJ_ID_PORT_FDB,
 };
 
 struct switchdev_obj {
        enum switchdev_obj_id id;
-       enum switchdev_trans trans;
-       int (*cb)(struct net_device *dev, struct switchdev_obj *obj);
-       union {
-               struct switchdev_obj_vlan {             /* PORT_VLAN */
-                       u16 flags;
-                       u16 vid_begin;
-                       u16 vid_end;
-               } vlan;
-               struct switchdev_obj_ipv4_fib {         /* IPV4_FIB */
-                       u32 dst;
-                       int dst_len;
-                       struct fib_info *fi;
-                       u8 tos;
-                       u8 type;
-                       u32 nlflags;
-                       u32 tb_id;
-               } ipv4_fib;
-               struct switchdev_obj_fdb {              /* PORT_FDB */
-                       const unsigned char *addr;
-                       u16 vid;
-                       u16 ndm_state;
-               } fdb;
-       } u;
+       u32 flags;
 };
 
+/* SWITCHDEV_OBJ_ID_PORT_VLAN */
+struct switchdev_obj_port_vlan {
+       struct switchdev_obj obj;
+       u16 flags;
+       u16 vid_begin;
+       u16 vid_end;
+};
+
+#define SWITCHDEV_OBJ_PORT_VLAN(obj) \
+       container_of(obj, struct switchdev_obj_port_vlan, obj)
+
+/* SWITCHDEV_OBJ_ID_IPV4_FIB */
+struct switchdev_obj_ipv4_fib {
+       struct switchdev_obj obj;
+       u32 dst;
+       int dst_len;
+       struct fib_info fi;
+       u8 tos;
+       u8 type;
+       u32 nlflags;
+       u32 tb_id;
+};
+
+#define SWITCHDEV_OBJ_IPV4_FIB(obj) \
+       container_of(obj, struct switchdev_obj_ipv4_fib, obj)
+
+/* SWITCHDEV_OBJ_ID_PORT_FDB */
+struct switchdev_obj_port_fdb {
+       struct switchdev_obj obj;
+       unsigned char addr[ETH_ALEN];
+       u16 vid;
+       u16 ndm_state;
+};
+
+#define SWITCHDEV_OBJ_PORT_FDB(obj) \
+       container_of(obj, struct switchdev_obj_port_fdb, obj)
+
+void switchdev_trans_item_enqueue(struct switchdev_trans *trans,
+                                 void *data, void (*destructor)(void const *),
+                                 struct switchdev_trans_item *tritem);
+void *switchdev_trans_item_dequeue(struct switchdev_trans *trans);
+
+typedef int switchdev_obj_dump_cb_t(struct switchdev_obj *obj);
+
 /**
  * struct switchdev_ops - switchdev operations
  *
@@ -84,23 +123,26 @@ struct switchdev_obj {
  *
  * @switchdev_port_attr_set: Set a port attribute (see switchdev_attr).
  *
- * @switchdev_port_obj_add: Add an object to port (see switchdev_obj).
+ * @switchdev_port_obj_add: Add an object to port (see switchdev_obj_*).
  *
- * @switchdev_port_obj_del: Delete an object from port (see switchdev_obj).
+ * @switchdev_port_obj_del: Delete an object from port (see switchdev_obj_*).
  *
- * @switchdev_port_obj_dump: Dump port objects (see switchdev_obj).
+ * @switchdev_port_obj_dump: Dump port objects (see switchdev_obj_*).
  */
 struct switchdev_ops {
        int     (*switchdev_port_attr_get)(struct net_device *dev,
                                           struct switchdev_attr *attr);
        int     (*switchdev_port_attr_set)(struct net_device *dev,
-                                          struct switchdev_attr *attr);
+                                          const struct switchdev_attr *attr,
+                                          struct switchdev_trans *trans);
        int     (*switchdev_port_obj_add)(struct net_device *dev,
-                                         struct switchdev_obj *obj);
+                                         const struct switchdev_obj *obj,
+                                         struct switchdev_trans *trans);
        int     (*switchdev_port_obj_del)(struct net_device *dev,
-                                         struct switchdev_obj *obj);
+                                         const struct switchdev_obj *obj);
        int     (*switchdev_port_obj_dump)(struct net_device *dev,
-                                         struct switchdev_obj *obj);
+                                          struct switchdev_obj *obj,
+                                          switchdev_obj_dump_cb_t *cb);
 };
 
 enum switchdev_notifier_type {
@@ -126,13 +168,17 @@ switchdev_notifier_info_to_dev(const struct switchdev_notifier_info *info)
 
 #ifdef CONFIG_NET_SWITCHDEV
 
+void switchdev_deferred_process(void);
 int switchdev_port_attr_get(struct net_device *dev,
                            struct switchdev_attr *attr);
 int switchdev_port_attr_set(struct net_device *dev,
-                           struct switchdev_attr *attr);
-int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj);
-int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj);
-int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj);
+                           const struct switchdev_attr *attr);
+int switchdev_port_obj_add(struct net_device *dev,
+                          const struct switchdev_obj *obj);
+int switchdev_port_obj_del(struct net_device *dev,
+                          const struct switchdev_obj *obj);
+int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj,
+                           switchdev_obj_dump_cb_t *cb);
 int register_switchdev_notifier(struct notifier_block *nb);
 int unregister_switchdev_notifier(struct notifier_block *nb);
 int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
@@ -164,6 +210,10 @@ void switchdev_port_fwd_mark_set(struct net_device *dev,
 
 #else
 
+static inline void switchdev_deferred_process(void)
+{
+}
+
 static inline int switchdev_port_attr_get(struct net_device *dev,
                                          struct switchdev_attr *attr)
 {
@@ -171,25 +221,26 @@ static inline int switchdev_port_attr_get(struct net_device *dev,
 }
 
 static inline int switchdev_port_attr_set(struct net_device *dev,
-                                         struct switchdev_attr *attr)
+                                         const struct switchdev_attr *attr)
 {
        return -EOPNOTSUPP;
 }
 
 static inline int switchdev_port_obj_add(struct net_device *dev,
-                                        struct switchdev_obj *obj)
+                                        const struct switchdev_obj *obj)
 {
        return -EOPNOTSUPP;
 }
 
 static inline int switchdev_port_obj_del(struct net_device *dev,
-                                        struct switchdev_obj *obj)
+                                        const struct switchdev_obj *obj)
 {
        return -EOPNOTSUPP;
 }
 
 static inline int switchdev_port_obj_dump(struct net_device *dev,
-                                         struct switchdev_obj *obj)
+                                         const struct switchdev_obj *obj,
+                                         switchdev_obj_dump_cb_t *cb)
 {
        return -EOPNOTSUPP;
 }
index 5c1104c2e24f655c08916d72e8ee6388127ac5a2..02caa406611b496e6e830c9fb85b467c55875732 100644 (file)
@@ -5,6 +5,7 @@
 
 struct tcf_connmark_info {
        struct tcf_common common;
+       struct net *net;
        u16 zone;
 };
 
index 0cab28cd43a9067e731af2d0b2557172e63fe5e9..f80e74c5ad18b22c274ecd7e75b6a23ffe7268b4 100644 (file)
@@ -279,6 +279,7 @@ extern int sysctl_tcp_limit_output_bytes;
 extern int sysctl_tcp_challenge_ack_limit;
 extern unsigned int sysctl_tcp_notsent_lowat;
 extern int sysctl_tcp_min_tso_segs;
+extern int sysctl_tcp_min_rtt_wlen;
 extern int sysctl_tcp_autocorking;
 extern int sysctl_tcp_invalid_ratelimit;
 extern int sysctl_tcp_pacing_ss_ratio;
@@ -365,8 +366,7 @@ void tcp_wfree(struct sk_buff *skb);
 void tcp_write_timer_handler(struct sock *sk);
 void tcp_delack_timer_handler(struct sock *sk);
 int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
-int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
-                         const struct tcphdr *th, unsigned int len);
+int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb);
 void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
                         const struct tcphdr *th, unsigned int len);
 void tcp_rcv_space_adjust(struct sock *sk);
@@ -451,19 +451,22 @@ void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb);
 void tcp_v4_mtu_reduced(struct sock *sk);
 void tcp_req_err(struct sock *sk, u32 seq);
 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb);
-struct sock *tcp_create_openreq_child(struct sock *sk,
+struct sock *tcp_create_openreq_child(const struct sock *sk,
                                      struct request_sock *req,
                                      struct sk_buff *skb);
 void tcp_ca_openreq_child(struct sock *sk, const struct dst_entry *dst);
-struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
+struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
                                  struct request_sock *req,
-                                 struct dst_entry *dst);
+                                 struct dst_entry *dst,
+                                 struct request_sock *req_unhash,
+                                 bool *own_req);
 int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
 int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
 int tcp_connect(struct sock *sk);
-struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
+struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
                                struct request_sock *req,
-                               struct tcp_fastopen_cookie *foc);
+                               struct tcp_fastopen_cookie *foc,
+                               bool attach_req);
 int tcp_disconnect(struct sock *sk, int flags);
 
 void tcp_finish_connect(struct sock *sk, struct sk_buff *skb);
@@ -492,8 +495,9 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb);
 
 /* syncookies: remember time of last synqueue overflow
  * But do not dirty this field too often (once per second is enough)
+ * It is racy as we do not hold a lock, but race is very minor.
  */
-static inline void tcp_synq_overflow(struct sock *sk)
+static inline void tcp_synq_overflow(const struct sock *sk)
 {
        unsigned long last_overflow = tcp_sk(sk)->rx_opt.ts_recent_stamp;
        unsigned long now = jiffies;
@@ -520,8 +524,7 @@ static inline u32 tcp_cookie_time(void)
 
 u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th,
                              u16 *mssp);
-__u32 cookie_v4_init_sequence(struct sock *sk, const struct sk_buff *skb,
-                             __u16 *mss);
+__u32 cookie_v4_init_sequence(const struct sk_buff *skb, __u16 *mss);
 __u32 cookie_init_timestamp(struct request_sock *req);
 bool cookie_timestamp_decode(struct tcp_options_received *opt);
 bool cookie_ecn_ok(const struct tcp_options_received *opt,
@@ -534,8 +537,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb);
 
 u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph,
                              const struct tcphdr *th, u16 *mssp);
-__u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb,
-                             __u16 *mss);
+__u32 cookie_v6_init_sequence(const struct sk_buff *skb, __u16 *mss);
 #endif
 /* tcp_output.c */
 
@@ -565,7 +567,9 @@ bool tcp_schedule_loss_probe(struct sock *sk);
 /* tcp_input.c */
 void tcp_resume_early_retransmit(struct sock *sk);
 void tcp_rearm_rto(struct sock *sk);
+void tcp_synack_rtt_meas(struct sock *sk, struct request_sock *req);
 void tcp_reset(struct sock *sk);
+void tcp_skb_mark_lost_uncond_verify(struct tcp_sock *tp, struct sk_buff *skb);
 
 /* tcp_timer.c */
 void tcp_init_xmit_timers(struct sock *);
@@ -671,6 +675,12 @@ static inline bool tcp_ca_dst_locked(const struct dst_entry *dst)
        return dst_metric_locked(dst, RTAX_CC_ALGO);
 }
 
+/* Minimum RTT in usec. ~0 means not available. */
+static inline u32 tcp_min_rtt(const struct tcp_sock *tp)
+{
+       return tp->rtt_min[0].rtt;
+}
+
 /* Compute the actual receive window we are currently advertising.
  * Rcv_nxt can be after the window if our peer push more data
  * than the offered window.
@@ -1206,7 +1216,8 @@ static inline int tcp_full_space(const struct sock *sk)
 }
 
 extern void tcp_openreq_init_rwin(struct request_sock *req,
-                                 struct sock *sk, struct dst_entry *dst);
+                                 const struct sock *sk_listener,
+                                 const struct dst_entry *dst);
 
 void tcp_enter_memory_pressure(struct sock *sk);
 
@@ -1370,16 +1381,16 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
                   int family, const u8 *newkey, u8 newkeylen, gfp_t gfp);
 int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr,
                   int family);
-struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
+struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
                                         const struct sock *addr_sk);
 
 #ifdef CONFIG_TCP_MD5SIG
-struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
+struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk,
                                         const union tcp_md5_addr *addr,
                                         int family);
 #define tcp_twsk_md5_key(twsk) ((twsk)->tw_md5_key)
 #else
-static inline struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
+static inline struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk,
                                         const union tcp_md5_addr *addr,
                                         int family)
 {
@@ -1420,10 +1431,10 @@ void tcp_free_fastopen_req(struct tcp_sock *tp);
 
 extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx;
 int tcp_fastopen_reset_cipher(void *key, unsigned int len);
-bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
-                     struct request_sock *req,
-                     struct tcp_fastopen_cookie *foc,
-                     struct dst_entry *dst);
+struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
+                             struct request_sock *req,
+                             struct tcp_fastopen_cookie *foc,
+                             struct dst_entry *dst);
 void tcp_fastopen_init_key_once(bool publish);
 #define TCP_FASTOPEN_KEY_LENGTH 16
 
@@ -1618,7 +1629,6 @@ static inline bool tcp_stream_is_thin(struct tcp_sock *tp)
 /* /proc */
 enum tcp_seq_states {
        TCP_SEQ_STATE_LISTENING,
-       TCP_SEQ_STATE_OPENREQ,
        TCP_SEQ_STATE_ESTABLISHED,
 };
 
@@ -1637,7 +1647,6 @@ struct tcp_iter_state {
        enum tcp_seq_states     state;
        struct sock             *syn_wait_sk;
        int                     bucket, offset, sbucket, num;
-       kuid_t                  uid;
        loff_t                  last_pos;
 };
 
@@ -1674,7 +1683,7 @@ int tcp4_proc_init(void);
 void tcp4_proc_exit(void);
 #endif
 
-int tcp_rtx_synack(struct sock *sk, struct request_sock *req);
+int tcp_rtx_synack(const struct sock *sk, struct request_sock *req);
 int tcp_conn_request(struct request_sock_ops *rsk_ops,
                     const struct tcp_request_sock_ops *af_ops,
                     struct sock *sk, struct sk_buff *skb);
@@ -1682,7 +1691,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
 /* TCP af-specific functions */
 struct tcp_sock_af_ops {
 #ifdef CONFIG_TCP_MD5SIG
-       struct tcp_md5sig_key   *(*md5_lookup) (struct sock *sk,
+       struct tcp_md5sig_key   *(*md5_lookup) (const struct sock *sk,
                                                const struct sock *addr_sk);
        int             (*calc_md5_hash)(char *location,
                                         const struct tcp_md5sig_key *md5,
@@ -1697,40 +1706,42 @@ struct tcp_sock_af_ops {
 struct tcp_request_sock_ops {
        u16 mss_clamp;
 #ifdef CONFIG_TCP_MD5SIG
-       struct tcp_md5sig_key *(*req_md5_lookup)(struct sock *sk,
+       struct tcp_md5sig_key *(*req_md5_lookup)(const struct sock *sk,
                                                 const struct sock *addr_sk);
        int             (*calc_md5_hash) (char *location,
                                          const struct tcp_md5sig_key *md5,
                                          const struct sock *sk,
                                          const struct sk_buff *skb);
 #endif
-       void (*init_req)(struct request_sock *req, struct sock *sk,
+       void (*init_req)(struct request_sock *req,
+                        const struct sock *sk_listener,
                         struct sk_buff *skb);
 #ifdef CONFIG_SYN_COOKIES
-       __u32 (*cookie_init_seq)(struct sock *sk, const struct sk_buff *skb,
+       __u32 (*cookie_init_seq)(const struct sk_buff *skb,
                                 __u16 *mss);
 #endif
-       struct dst_entry *(*route_req)(struct sock *sk, struct flowi *fl,
+       struct dst_entry *(*route_req)(const struct sock *sk, struct flowi *fl,
                                       const struct request_sock *req,
                                       bool *strict);
        __u32 (*init_seq)(const struct sk_buff *skb);
-       int (*send_synack)(struct sock *sk, struct dst_entry *dst,
+       int (*send_synack)(const struct sock *sk, struct dst_entry *dst,
                           struct flowi *fl, struct request_sock *req,
-                          u16 queue_mapping, struct tcp_fastopen_cookie *foc);
-       void (*queue_hash_add)(struct sock *sk, struct request_sock *req,
-                              const unsigned long timeout);
+                          struct tcp_fastopen_cookie *foc,
+                          bool attach_req);
 };
 
 #ifdef CONFIG_SYN_COOKIES
 static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops,
-                                        struct sock *sk, struct sk_buff *skb,
+                                        const struct sock *sk, struct sk_buff *skb,
                                         __u16 *mss)
 {
-       return ops->cookie_init_seq(sk, skb, mss);
+       tcp_synq_overflow(sk);
+       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT);
+       return ops->cookie_init_seq(skb, mss);
 }
 #else
 static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops,
-                                        struct sock *sk, struct sk_buff *skb,
+                                        const struct sock *sk, struct sk_buff *skb,
                                         __u16 *mss)
 {
        return 0;
@@ -1742,6 +1753,19 @@ int tcpv4_offload_init(void);
 void tcp_v4_init(void);
 void tcp_init(void);
 
+/* tcp_recovery.c */
+
+/* Flags to enable various loss recovery features. See below */
+extern int sysctl_tcp_recovery;
+
+/* Use TCP RACK to detect (some) tail and retransmit losses */
+#define TCP_RACK_LOST_RETRANS  0x1
+
+extern int tcp_rack_mark_lost(struct sock *sk);
+
+extern void tcp_rack_advance(struct tcp_sock *tp,
+                            const struct skb_mstamp *xmit_time, u8 sacked);
+
 /*
  * Save and compile IPv4 options, return a pointer to it
  */
index 47e5444f7d15bd971a337ad78960b094b771ec46..b7be852bfe9d5719aa86df7d788224a64fbce775 100644 (file)
@@ -8,6 +8,7 @@ struct tso_t {
        void *data;
        size_t size;
        u16 ip_id;
+       bool ipv6;
        u32 tcp_seq;
 };
 
diff --git a/include/net/vrf.h b/include/net/vrf.h
deleted file mode 100644 (file)
index 593e609..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * include/net/net_vrf.h - adds vrf dev structure definitions
- * Copyright (c) 2015 Cumulus Networks
- *
- * 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 __LINUX_NET_VRF_H
-#define __LINUX_NET_VRF_H
-
-struct net_vrf_dev {
-       struct rcu_head         rcu;
-       int                     ifindex; /* ifindex of master dev */
-       u32                     tb_id;   /* table id for VRF */
-};
-
-struct slave {
-       struct list_head        list;
-       struct net_device       *dev;
-};
-
-struct slave_queue {
-       struct list_head        all_slaves;
-};
-
-struct net_vrf {
-       struct slave_queue      queue;
-       struct rtable           *rth;
-       u32                     tb_id;
-};
-
-
-#if IS_ENABLED(CONFIG_NET_VRF)
-/* called with rcu_read_lock() */
-static inline int vrf_master_ifindex_rcu(const struct net_device *dev)
-{
-       struct net_vrf_dev *vrf_ptr;
-       int ifindex = 0;
-
-       if (!dev)
-               return 0;
-
-       if (netif_is_vrf(dev)) {
-               ifindex = dev->ifindex;
-       } else {
-               vrf_ptr = rcu_dereference(dev->vrf_ptr);
-               if (vrf_ptr)
-                       ifindex = vrf_ptr->ifindex;
-       }
-
-       return ifindex;
-}
-
-static inline int vrf_master_ifindex(const struct net_device *dev)
-{
-       int ifindex;
-
-       rcu_read_lock();
-       ifindex = vrf_master_ifindex_rcu(dev);
-       rcu_read_unlock();
-
-       return ifindex;
-}
-
-/* called with rcu_read_lock */
-static inline u32 vrf_dev_table_rcu(const struct net_device *dev)
-{
-       u32 tb_id = 0;
-
-       if (dev) {
-               struct net_vrf_dev *vrf_ptr;
-
-               vrf_ptr = rcu_dereference(dev->vrf_ptr);
-               if (vrf_ptr)
-                       tb_id = vrf_ptr->tb_id;
-       }
-       return tb_id;
-}
-
-static inline u32 vrf_dev_table(const struct net_device *dev)
-{
-       u32 tb_id;
-
-       rcu_read_lock();
-       tb_id = vrf_dev_table_rcu(dev);
-       rcu_read_unlock();
-
-       return tb_id;
-}
-
-static inline u32 vrf_dev_table_ifindex(struct net *net, int ifindex)
-{
-       struct net_device *dev;
-       u32 tb_id = 0;
-
-       if (!ifindex)
-               return 0;
-
-       rcu_read_lock();
-
-       dev = dev_get_by_index_rcu(net, ifindex);
-       if (dev)
-               tb_id = vrf_dev_table_rcu(dev);
-
-       rcu_read_unlock();
-
-       return tb_id;
-}
-
-/* called with rtnl */
-static inline u32 vrf_dev_table_rtnl(const struct net_device *dev)
-{
-       u32 tb_id = 0;
-
-       if (dev) {
-               struct net_vrf_dev *vrf_ptr;
-
-               vrf_ptr = rtnl_dereference(dev->vrf_ptr);
-               if (vrf_ptr)
-                       tb_id = vrf_ptr->tb_id;
-       }
-       return tb_id;
-}
-
-/* caller has already checked netif_is_vrf(dev) */
-static inline struct rtable *vrf_dev_get_rth(const struct net_device *dev)
-{
-       struct rtable *rth = ERR_PTR(-ENETUNREACH);
-       struct net_vrf *vrf = netdev_priv(dev);
-
-       if (vrf) {
-               rth = vrf->rth;
-               atomic_inc(&rth->dst.__refcnt);
-       }
-       return rth;
-}
-
-#else
-static inline int vrf_master_ifindex_rcu(const struct net_device *dev)
-{
-       return 0;
-}
-
-static inline int vrf_master_ifindex(const struct net_device *dev)
-{
-       return 0;
-}
-
-static inline u32 vrf_dev_table_rcu(const struct net_device *dev)
-{
-       return 0;
-}
-
-static inline u32 vrf_dev_table(const struct net_device *dev)
-{
-       return 0;
-}
-
-static inline u32 vrf_dev_table_ifindex(struct net *net, int ifindex)
-{
-       return 0;
-}
-
-static inline u32 vrf_dev_table_rtnl(const struct net_device *dev)
-{
-       return 0;
-}
-
-static inline struct rtable *vrf_dev_get_rth(const struct net_device *dev)
-{
-       return ERR_PTR(-ENETUNREACH);
-}
-#endif
-
-#endif /* __LINUX_NET_VRF_H */
index 480a319b4c925f3c98dd3f3dff99817adeb10355..c1c899c3a51be42f680fff5ce0f5c5e997652daa 100644 (file)
@@ -152,7 +152,10 @@ struct vxlan_config {
 struct vxlan_dev {
        struct hlist_node hlist;        /* vni hash table */
        struct list_head  next;         /* vxlan's per namespace list */
-       struct vxlan_sock *vn_sock;     /* listening socket */
+       struct vxlan_sock *vn4_sock;    /* listening socket for IPv4 */
+#if IS_ENABLED(CONFIG_IPV6)
+       struct vxlan_sock *vn6_sock;    /* listening socket for IPv6 */
+#endif
        struct net_device *dev;
        struct net        *net;         /* netns for packet i/o */
        struct vxlan_rdst default_dst;  /* default destination */
@@ -195,9 +198,14 @@ struct vxlan_dev {
 struct net_device *vxlan_dev_create(struct net *net, const char *name,
                                    u8 name_assign_type, struct vxlan_config *conf);
 
-static inline __be16 vxlan_dev_dst_port(struct vxlan_dev *vxlan)
+static inline __be16 vxlan_dev_dst_port(struct vxlan_dev *vxlan,
+                                       unsigned short family)
 {
-       return inet_sk(vxlan->vn_sock->sock->sk)->inet_sport;
+#if IS_ENABLED(CONFIG_IPV6)
+       if (family == AF_INET6)
+               return inet_sk(vxlan->vn6_sock->sock->sk)->inet_sport;
+#endif
+       return inet_sk(vxlan->vn4_sock->sock->sk)->inet_sport;
 }
 
 static inline netdev_features_t vxlan_features_check(struct sk_buff *skb,
index 312e3fee9ccfc098f70cd8ec6edb87881e1d1f99..4a9c21f9b4ea189075901a3f2c8c12ec6814f5fe 100644 (file)
@@ -296,8 +296,6 @@ struct xfrm_policy_afinfo {
                                                  struct flowi *fl,
                                                  int reverse);
        int                     (*get_tos)(const struct flowi *fl);
-       void                    (*init_dst)(struct net *net,
-                                           struct xfrm_dst *dst);
        int                     (*init_path)(struct xfrm_dst *path,
                                             struct dst_entry *dst,
                                             int nfheader_len);
@@ -335,7 +333,7 @@ struct xfrm_state_afinfo {
                                                const xfrm_address_t *saddr);
        int                     (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n);
        int                     (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n);
-       int                     (*output)(struct sock *sk, struct sk_buff *skb);
+       int                     (*output)(struct net *net, struct sock *sk, struct sk_buff *skb);
        int                     (*output_finish)(struct sock *sk, struct sk_buff *skb);
        int                     (*extract_input)(struct xfrm_state *x,
                                                 struct sk_buff *skb);
@@ -1529,7 +1527,7 @@ static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
 
 int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb);
 int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
-int xfrm4_output(struct sock *sk, struct sk_buff *skb);
+int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb);
 int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb);
 int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err);
 int xfrm4_protocol_register(struct xfrm4_protocol *handler, unsigned char protocol);
@@ -1554,7 +1552,7 @@ __be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr);
 __be32 xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr);
 int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb);
 int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
-int xfrm6_output(struct sock *sk, struct sk_buff *skb);
+int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb);
 int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb);
 int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
                          u8 **prevhdr);
index 539d6bc3216a3784f9ad5b4d1e3ef06e7a4cc223..9b90c57517a918687189933ae6920b80d251e98e 100644 (file)
@@ -104,22 +104,17 @@ DEFINE_EVENT(sched_wakeup_template, sched_wakeup_new,
             TP_ARGS(p));
 
 #ifdef CREATE_TRACE_POINTS
-static inline long __trace_sched_switch_state(struct task_struct *p)
+static inline long __trace_sched_switch_state(bool preempt, struct task_struct *p)
 {
-       long state = p->state;
-
-#ifdef CONFIG_PREEMPT
 #ifdef CONFIG_SCHED_DEBUG
        BUG_ON(p != current);
 #endif /* CONFIG_SCHED_DEBUG */
+
        /*
-        * For all intents and purposes a preempted task is a running task.
+        * Preemption ignores task state, therefore preempted tasks are always
+        * RUNNING (we will not have dequeued if state != RUNNING).
         */
-       if (preempt_count() & PREEMPT_ACTIVE)
-               state = TASK_RUNNING | TASK_STATE_MAX;
-#endif /* CONFIG_PREEMPT */
-
-       return state;
+       return preempt ? TASK_RUNNING | TASK_STATE_MAX : p->state;
 }
 #endif /* CREATE_TRACE_POINTS */
 
@@ -128,10 +123,11 @@ static inline long __trace_sched_switch_state(struct task_struct *p)
  */
 TRACE_EVENT(sched_switch,
 
-       TP_PROTO(struct task_struct *prev,
+       TP_PROTO(bool preempt,
+                struct task_struct *prev,
                 struct task_struct *next),
 
-       TP_ARGS(prev, next),
+       TP_ARGS(preempt, prev, next),
 
        TP_STRUCT__entry(
                __array(        char,   prev_comm,      TASK_COMM_LEN   )
@@ -147,7 +143,7 @@ TRACE_EVENT(sched_switch,
                memcpy(__entry->next_comm, next->comm, TASK_COMM_LEN);
                __entry->prev_pid       = prev->pid;
                __entry->prev_prio      = prev->prio;
-               __entry->prev_state     = __trace_sched_switch_state(prev);
+               __entry->prev_state     = __trace_sched_switch_state(preempt, prev);
                memcpy(__entry->prev_comm, prev->comm, TASK_COMM_LEN);
                __entry->next_pid       = next->pid;
                __entry->next_prio      = next->prio;
index f7b2db44eb4b07a910d0097e63a657c1e4a37816..70d89230b6416c6e59858d6936695adf71770e85 100644 (file)
@@ -263,6 +263,7 @@ header-y += minix_fs.h
 header-y += mman.h
 header-y += mmtimer.h
 header-y += mpls.h
+header-y += mpls_iptunnel.h
 header-y += mqueue.h
 header-y += mroute6.h
 header-y += mroute.h
index 10f0fa29454f5d06797882191ec6198e5e28a86a..9c9c6ad55f1487b9b4cead653fc996930603b259 100644 (file)
@@ -35,12 +35,6 @@ struct zatm_pool_req {
        struct zatm_pool_info info;     /* actual information */
 };
 
-struct zatm_t_hist {
-       struct timeval real;            /* real (wall-clock) time */
-       struct timeval expected;        /* expected real time */
-};
-
-
 #define ZATM_OAM_POOL          0       /* free buffer pool for OAM cells */
 #define ZATM_AAL0_POOL         1       /* free buffer pool for AAL0 cells */
 #define ZATM_AAL5_POOL_BASE    2       /* first AAL5 free buffer pool */
index 92a48e2d54619f80113c13f9fff45f98ddfabe30..9ea2d22fa2cb5af6c14ac09bb7e553eb1cbed87f 100644 (file)
@@ -63,50 +63,16 @@ struct bpf_insn {
        __s32   imm;            /* signed immediate constant */
 };
 
-/* BPF syscall commands */
+/* BPF syscall commands, see bpf(2) man-page for details. */
 enum bpf_cmd {
-       /* create a map with given type and attributes
-        * fd = bpf(BPF_MAP_CREATE, union bpf_attr *, u32 size)
-        * returns fd or negative error
-        * map is deleted when fd is closed
-        */
        BPF_MAP_CREATE,
-
-       /* lookup key in a given map
-        * err = bpf(BPF_MAP_LOOKUP_ELEM, union bpf_attr *attr, u32 size)
-        * Using attr->map_fd, attr->key, attr->value
-        * returns zero and stores found elem into value
-        * or negative error
-        */
        BPF_MAP_LOOKUP_ELEM,
-
-       /* create or update key/value pair in a given map
-        * err = bpf(BPF_MAP_UPDATE_ELEM, union bpf_attr *attr, u32 size)
-        * Using attr->map_fd, attr->key, attr->value, attr->flags
-        * returns zero or negative error
-        */
        BPF_MAP_UPDATE_ELEM,
-
-       /* find and delete elem by key in a given map
-        * err = bpf(BPF_MAP_DELETE_ELEM, union bpf_attr *attr, u32 size)
-        * Using attr->map_fd, attr->key
-        * returns zero or negative error
-        */
        BPF_MAP_DELETE_ELEM,
-
-       /* lookup key in a given map and return next key
-        * err = bpf(BPF_MAP_GET_NEXT_KEY, union bpf_attr *attr, u32 size)
-        * Using attr->map_fd, attr->key, attr->next_key
-        * returns zero and stores next key or negative error
-        */
        BPF_MAP_GET_NEXT_KEY,
-
-       /* verify and load eBPF program
-        * prog_fd = bpf(BPF_PROG_LOAD, union bpf_attr *attr, u32 size)
-        * Using attr->prog_type, attr->insns, attr->license
-        * returns fd or negative error
-        */
        BPF_PROG_LOAD,
+       BPF_OBJ_PIN,
+       BPF_OBJ_GET,
 };
 
 enum bpf_map_type {
@@ -160,6 +126,11 @@ union bpf_attr {
                __aligned_u64   log_buf;        /* user supplied buffer */
                __u32           kern_version;   /* checked when prog_type=kprobe */
        };
+
+       struct { /* anonymous struct used by BPF_OBJ_* commands */
+               __aligned_u64   pathname;
+               __u32           bpf_fd;
+       };
 } __attribute__((aligned(8)));
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
@@ -272,6 +243,32 @@ enum bpf_func_id {
        BPF_FUNC_skb_get_tunnel_key,
        BPF_FUNC_skb_set_tunnel_key,
        BPF_FUNC_perf_event_read,       /* u64 bpf_perf_event_read(&map, index) */
+       /**
+        * bpf_redirect(ifindex, flags) - redirect to another netdev
+        * @ifindex: ifindex of the net device
+        * @flags: bit 0 - if set, redirect to ingress instead of egress
+        *         other bits - reserved
+        * Return: TC_ACT_REDIRECT
+        */
+       BPF_FUNC_redirect,
+
+       /**
+        * bpf_get_route_realm(skb) - retrieve a dst's tclassid
+        * @skb: pointer to skb
+        * Return: realm if != 0
+        */
+       BPF_FUNC_get_route_realm,
+
+       /**
+        * bpf_perf_event_output(ctx, map, index, data, size) - output perf raw sample
+        * @ctx: struct pt_regs*
+        * @map: pointer to perf_event_array map
+        * @index: index of event in the map
+        * @data: data on stack to be output as raw data
+        * @size: size of data
+        * Return: 0 on success
+        */
+       BPF_FUNC_perf_event_output,
        __BPF_FUNC_MAX_ID,
 };
 
@@ -293,6 +290,7 @@ struct __sk_buff {
        __u32 tc_index;
        __u32 cb[5];
        __u32 hash;
+       __u32 tc_classid;
 };
 
 struct bpf_tunnel_key {
index 89ddb9dc9bdf7ca8bd191c9dedf7019f24573931..7a291dc1ff15a01bb97e304ca6dfeb27a9796794 100644 (file)
 #include <linux/types.h>
 #include <linux/can.h>
 
+struct bcm_timeval {
+       long tv_sec;
+       long tv_usec;
+};
+
 /**
  * struct bcm_msg_head - head of messages to/from the broadcast manager
  * @opcode:    opcode, see enum below.
@@ -62,7 +67,7 @@ struct bcm_msg_head {
        __u32 opcode;
        __u32 flags;
        __u32 count;
-       struct timeval ival1, ival2;
+       struct bcm_timeval ival1, ival2;
        canid_t can_id;
        __u32 nframes;
        struct can_frame frames[0];
index 46e34bd0e7832075c9446680d6b4183c6eda8a52..cfb642f8e7bd3b0eb0446fce2d5898eeb04bec4d 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/types.h>
 #include <linux/if_ether.h>
 
-
 /*
  *    These are the defined ARCnet Protocol ID's.
  */
  * The RFC1201-specific components of an arcnet packet header.
  */
 struct arc_rfc1201 {
-    __u8  proto;               /* protocol ID field - varies           */
-    __u8  split_flag;  /* for use with split packets           */
-    __be16   sequence;         /* sequence number                      */
-    __u8  payload[0];  /* space remaining in packet (504 bytes)*/
+       __u8  proto;            /* protocol ID field - varies           */
+       __u8  split_flag;       /* for use with split packets           */
+       __be16   sequence;      /* sequence number                      */
+       __u8  payload[0];       /* space remaining in packet (504 bytes)*/
 };
 #define RFC1201_HDR_SIZE 4
 
-
 /*
  * The RFC1051-specific components.
  */
 struct arc_rfc1051 {
-    __u8 proto;                /* ARC_P_RFC1051_ARP/RFC1051_IP */
-    __u8 payload[0];           /* 507 bytes                    */
+       __u8 proto;             /* ARC_P_RFC1051_ARP/RFC1051_IP */
+       __u8 payload[0];        /* 507 bytes                    */
 };
 #define RFC1051_HDR_SIZE 1
 
-
 /*
  * The ethernet-encap-specific components.  We have a real ethernet header
  * and some data.
  */
 struct arc_eth_encap {
-    __u8 proto;                /* Always ARC_P_ETHER                   */
-    struct ethhdr eth;         /* standard ethernet header (yuck!)     */
-    __u8 payload[0];           /* 493 bytes                            */
+       __u8 proto;             /* Always ARC_P_ETHER                   */
+       struct ethhdr eth;      /* standard ethernet header (yuck!)     */
+       __u8 payload[0];        /* 493 bytes                            */
 };
 #define ETH_ENCAP_HDR_SIZE 14
 
-
 struct arc_cap {
        __u8 proto;
-       __u8 cookie[sizeof(int)];   /* Actually NOT sent over the network */
+       __u8 cookie[sizeof(int)];
+                               /* Actually NOT sent over the network */
        union {
                __u8 ack;
-               __u8 raw[0];            /* 507 bytes */
+               __u8 raw[0];    /* 507 bytes */
        } mes;
 };
 
@@ -105,9 +102,9 @@ struct arc_cap {
  * driver.
  */
 struct arc_hardware {
-    __u8  source,              /* source ARCnet - filled in automagically */
-             dest,             /* destination ARCnet - 0 for broadcast    */
-            offset[2];         /* offset bytes (some weird semantics)     */
+       __u8 source;            /* source ARCnet - filled in automagically */
+       __u8 dest;              /* destination ARCnet - 0 for broadcast    */
+       __u8 offset[2];         /* offset bytes (some weird semantics)     */
 };
 #define ARC_HDR_SIZE 4
 
@@ -116,17 +113,17 @@ struct arc_hardware {
  * when you do a raw packet capture).
  */
 struct archdr {
-    /* hardware requirements */
-    struct arc_hardware hard;
-     
-    /* arcnet encapsulation-specific bits */
-    union {
-       struct arc_rfc1201   rfc1201;
-       struct arc_rfc1051   rfc1051;
-       struct arc_eth_encap eth_encap;
-       struct arc_cap       cap;
-       __u8 raw[0];            /* 508 bytes                            */
-    } soft;
+       /* hardware requirements */
+       struct arc_hardware hard;
+
+       /* arcnet encapsulation-specific bits */
+       union {
+               struct arc_rfc1201   rfc1201;
+               struct arc_rfc1051   rfc1051;
+               struct arc_eth_encap eth_encap;
+               struct arc_cap       cap;
+               __u8 raw[0];    /* 508 bytes                            */
+       } soft;
 };
 
 #endif                         /* _LINUX_IF_ARCNET_H */
index 3635b77975085a5801d9d4a5555beaf70a623441..18db14477bdda952fa946ccfba979950233f3130 100644 (file)
@@ -127,6 +127,7 @@ enum {
 #define BRIDGE_VLAN_INFO_UNTAGGED      (1<<2)  /* VLAN egresses untagged */
 #define BRIDGE_VLAN_INFO_RANGE_BEGIN   (1<<3) /* VLAN is start of vlan range */
 #define BRIDGE_VLAN_INFO_RANGE_END     (1<<4) /* VLAN is end of vlan range */
+#define BRIDGE_VLAN_INFO_BRENTRY       (1<<5) /* Global bridge VLAN entry */
 
 struct bridge_vlan_info {
        __u16 flags;
index 3a5f263cfc2ff9a5887195ebedd46de593c29cb3..5ad57375a99fdcfe8f8217fbbb6b52483fcd66a4 100644 (file)
@@ -232,11 +232,47 @@ enum {
        IFLA_BR_PRIORITY,
        IFLA_BR_VLAN_FILTERING,
        IFLA_BR_VLAN_PROTOCOL,
+       IFLA_BR_GROUP_FWD_MASK,
+       IFLA_BR_ROOT_ID,
+       IFLA_BR_BRIDGE_ID,
+       IFLA_BR_ROOT_PORT,
+       IFLA_BR_ROOT_PATH_COST,
+       IFLA_BR_TOPOLOGY_CHANGE,
+       IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+       IFLA_BR_HELLO_TIMER,
+       IFLA_BR_TCN_TIMER,
+       IFLA_BR_TOPOLOGY_CHANGE_TIMER,
+       IFLA_BR_GC_TIMER,
+       IFLA_BR_GROUP_ADDR,
+       IFLA_BR_FDB_FLUSH,
+       IFLA_BR_MCAST_ROUTER,
+       IFLA_BR_MCAST_SNOOPING,
+       IFLA_BR_MCAST_QUERY_USE_IFADDR,
+       IFLA_BR_MCAST_QUERIER,
+       IFLA_BR_MCAST_HASH_ELASTICITY,
+       IFLA_BR_MCAST_HASH_MAX,
+       IFLA_BR_MCAST_LAST_MEMBER_CNT,
+       IFLA_BR_MCAST_STARTUP_QUERY_CNT,
+       IFLA_BR_MCAST_LAST_MEMBER_INTVL,
+       IFLA_BR_MCAST_MEMBERSHIP_INTVL,
+       IFLA_BR_MCAST_QUERIER_INTVL,
+       IFLA_BR_MCAST_QUERY_INTVL,
+       IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
+       IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
+       IFLA_BR_NF_CALL_IPTABLES,
+       IFLA_BR_NF_CALL_IP6TABLES,
+       IFLA_BR_NF_CALL_ARPTABLES,
+       IFLA_BR_VLAN_DEFAULT_PVID,
        __IFLA_BR_MAX,
 };
 
 #define IFLA_BR_MAX    (__IFLA_BR_MAX - 1)
 
+struct ifla_bridge_id {
+       __u8    prio[2];
+       __u8    addr[6]; /* ETH_ALEN */
+};
+
 enum {
        BRIDGE_MODE_UNSPEC,
        BRIDGE_MODE_HAIRPIN,
@@ -256,6 +292,19 @@ enum {
        IFLA_BRPORT_PROXYARP,   /* proxy ARP */
        IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */
        IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */
+       IFLA_BRPORT_ROOT_ID,    /* designated root */
+       IFLA_BRPORT_BRIDGE_ID,  /* designated bridge */
+       IFLA_BRPORT_DESIGNATED_PORT,
+       IFLA_BRPORT_DESIGNATED_COST,
+       IFLA_BRPORT_ID,
+       IFLA_BRPORT_NO,
+       IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+       IFLA_BRPORT_CONFIG_PENDING,
+       IFLA_BRPORT_MESSAGE_AGE_TIMER,
+       IFLA_BRPORT_FORWARD_DELAY_TIMER,
+       IFLA_BRPORT_HOLD_TIMER,
+       IFLA_BRPORT_FLUSH,
+       IFLA_BRPORT_MULTICAST_ROUTER,
        __IFLA_BRPORT_MAX
 };
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
@@ -412,6 +461,7 @@ enum {
        IFLA_GENEVE_TOS,
        IFLA_GENEVE_PORT,       /* destination port */
        IFLA_GENEVE_COLLECT_METADATA,
+       IFLA_GENEVE_REMOTE6,
        __IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX        (__IFLA_GENEVE_MAX - 1)
@@ -501,6 +551,7 @@ enum {
                                 * on/off switch
                                 */
        IFLA_VF_STATS,          /* network device statistics */
+       IFLA_VF_TRUST,          /* Trust VF */
        __IFLA_VF_MAX,
 };
 
@@ -562,6 +613,11 @@ enum {
 
 #define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1)
 
+struct ifla_vf_trust {
+       __u32 vf;
+       __u32 setting;
+};
+
 /* VF ports management section
  *
  *     Nested layout of set/get msg is:
index 7b1425a6b370f878b7e08de24a5d901d6187d0e2..accb036bbc9c3621d9929dda225b6d0689348860 100644 (file)
@@ -75,5 +75,6 @@
 #define ANON_INODE_FS_MAGIC    0x09041934
 #define BTRFS_TEST_MAGIC       0x73727279
 #define NSFS_MAGIC             0x6e736673
+#define BPF_FS_MAGIC           0xcafe4a11
 
 #endif /* __LINUX_MAGIC_H__ */
index 1f5e6892392981bbd06a60ee6e1ff91de223e85d..7e385b83b9d82057ec79e6fbc76b4a258f0eec2a 100644 (file)
@@ -45,8 +45,24 @@ struct mmc_ioc_cmd {
 };
 #define mmc_ioc_cmd_set_data(ic, ptr) ic.data_ptr = (__u64)(unsigned long) ptr
 
-#define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd)
+/**
+ * struct mmc_ioc_multi_cmd - multi command information
+ * @num_of_cmds: Number of commands to send. Must be equal to or less than
+ *     MMC_IOC_MAX_CMDS.
+ * @cmds: Array of commands with length equal to 'num_of_cmds'
+ */
+struct mmc_ioc_multi_cmd {
+       __u64 num_of_cmds;
+       struct mmc_ioc_cmd cmds[0];
+};
 
+#define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd)
+/*
+ * MMC_IOC_MULTI_CMD: Used to send an array of MMC commands described by
+ *     the structure mmc_ioc_multi_cmd. The MMC driver will issue all
+ *     commands in array in sequence to card.
+ */
+#define MMC_IOC_MULTI_CMD _IOWR(MMC_BLOCK_MAJOR, 1, struct mmc_ioc_multi_cmd)
 /*
  * Since this ioctl is only meant to enhance (and not replace) normal access
  * to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES
@@ -54,4 +70,5 @@ struct mmc_ioc_cmd {
  * block device operations.
  */
 #define MMC_IOC_MAX_BYTES  (512L * 256)
+#define MMC_IOC_MAX_CMDS    255
 #endif /* LINUX_MMC_IOCTL_H */
index 90c2c9575bac371e95a69874c4b217d66a8b9ea6..fb21f0c717a12ef0dad7dc4ccbb33e2f10e8288b 100644 (file)
@@ -51,6 +51,8 @@ enum nfulnl_attr_type {
        NFULA_HWTYPE,                   /* hardware type */
        NFULA_HWHEADER,                 /* hardware header */
        NFULA_HWLEN,                    /* hardware header length */
+       NFULA_CT,                       /* nf_conntrack_netlink.h */
+       NFULA_CT_INFO,                  /* enum ip_conntrack_info */
 
        __NFULA_MAX
 };
@@ -93,5 +95,6 @@ enum nfulnl_attr_config {
 
 #define NFULNL_CFG_F_SEQ       0x0001
 #define NFULNL_CFG_F_SEQ_GLOBAL        0x0002
+#define NFULNL_CFG_F_CONNTRACK 0x0004
 
 #endif /* _NFNETLINK_LOG_H */
index 6f3fe16cd22a24149b144133d2a181d9d4448f96..f095155d87494b9cd68eca8f0c1458a683ad8479 100644 (file)
@@ -54,6 +54,7 @@ struct nlmsghdr {
 #define NLM_F_ACK              4       /* Reply with ack, with zero or error code */
 #define NLM_F_ECHO             8       /* Echo this request            */
 #define NLM_F_DUMP_INTR                16      /* Dump was inconsistent due to sequence change */
+#define NLM_F_DUMP_FILTERED    32      /* Dump was filtered as requested */
 
 /* Modifiers to GET request */
 #define NLM_F_ROOT     0x100   /* specify tree root    */
index dd3f7538907691fce2442ab55f1c202e7365e092..399f39ff8048da02844b0fa81a918714d043cdf5 100644 (file)
@@ -86,6 +86,7 @@
  *     for this event is the application ID (AID).
  * @NFC_CMD_GET_SE: Dump all discovered secure elements from an NFC controller.
  * @NFC_CMD_SE_IO: Send/Receive APDUs to/from the selected secure element.
+ * @NFC_CMD_ACTIVATE_TARGET: Request NFC controller to reactivate target.
  * @NFC_CMD_VENDOR: Vendor specific command, to be implemented directly
  *     from the driver in order to support hardware specific operations.
  */
@@ -156,6 +157,7 @@ enum nfc_commands {
  * @NFC_ATTR_APDU: Secure element APDU
  * @NFC_ATTR_TARGET_ISO15693_DSFID: ISO 15693 Data Storage Format Identifier
  * @NFC_ATTR_TARGET_ISO15693_UID: ISO 15693 Unique Identifier
+ * @NFC_ATTR_SE_PARAMS: Parameters data from an evt_transaction
  * @NFC_ATTR_VENDOR_ID: NFC manufacturer unique ID, typically an OUI
  * @NFC_ATTR_VENDOR_SUBCMD: Vendor specific sub command
  * @NFC_ATTR_VENDOR_DATA: Vendor specific data, to be optionally passed
index c0ab6b0a391941157a1dc96560de515774a5abde..1f0b4cf5dd03df822db7903ccddb3e9021520085 100644 (file)
@@ -10,6 +10,7 @@
  * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
  * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ * Copyright 2015      Intel Deutschland GmbH
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  *     partial scan results may be available
  *
  * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain
- *     intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL.
+ *     intervals and certain number of cycles, as specified by
+ *     %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
+ *     not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
+ *     scheduled scan will run in an infinite loop with the specified interval.
+ *     These attributes are mutually exculsive,
+ *     i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
+ *     NL80211_ATTR_SCHED_SCAN_PLANS is defined.
+ *     If for some reason scheduled scan is aborted by the driver, all scan
+ *     plans are canceled (including scan plans that did not start yet).
  *     Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS)
  *     are passed, they are used in the probe requests.  For
  *     broadcast, a broadcast SSID must be passed (ie. an empty
@@ -1761,6 +1770,19 @@ enum nl80211_commands {
  * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
  *      is operating in an indoor environment.
  *
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: maximum number of scan plans for
+ *     scheduled scan supported by the device (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: maximum interval (in seconds) for
+ *     a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: maximum number of iterations in
+ *     a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_SCHED_SCAN_PLANS: a list of scan plans for scheduled scan.
+ *     Each scan plan defines the number of scan iterations and the interval
+ *     between scans. The last scan plan will always run infinitely,
+ *     thus it must not specify the number of iterations, only the interval
+ *     between scans. The scan plans are executed sequentially.
+ *     Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2130,6 +2152,11 @@ enum nl80211_attrs {
 
        NL80211_ATTR_REG_INDOOR,
 
+       NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+       NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+       NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+       NL80211_ATTR_SCHED_SCAN_PLANS,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3364,6 +3391,9 @@ enum nl80211_bss_scan_width {
  *     (not present if no beacon frame has been received yet)
  * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
  *     @NL80211_BSS_TSF is known to be from a probe response (flag attribute)
+ * @NL80211_BSS_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry
+ *     was last updated by a received frame. The value is expected to be
+ *     accurate to about 10ms. (u64, nanoseconds)
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -3383,6 +3413,7 @@ enum nl80211_bss {
        NL80211_BSS_CHAN_WIDTH,
        NL80211_BSS_BEACON_TSF,
        NL80211_BSS_PRESP_DATA,
+       NL80211_BSS_LAST_SEEN_BOOTTIME,
 
        /* keep last */
        __NL80211_BSS_AFTER_LAST,
@@ -4589,4 +4620,28 @@ enum nl80211_tdls_peer_capability {
        NL80211_TDLS_PEER_WMM = 1<<2,
 };
 
+/**
+ * enum nl80211_sched_scan_plan - scanning plan for scheduled scan
+ * @__NL80211_SCHED_SCAN_PLAN_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_PLAN_INTERVAL: interval between scan iterations. In
+ *     seconds (u32).
+ * @NL80211_SCHED_SCAN_PLAN_ITERATIONS: number of scan iterations in this
+ *     scan plan (u32). The last scan plan must not specify this attribute
+ *     because it will run infinitely. A value of zero is invalid as it will
+ *     make the scan plan meaningless.
+ * @NL80211_SCHED_SCAN_PLAN_MAX: highest scheduled scan plan attribute number
+ *     currently defined
+ * @__NL80211_SCHED_SCAN_PLAN_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_plan {
+       __NL80211_SCHED_SCAN_PLAN_INVALID,
+       NL80211_SCHED_SCAN_PLAN_INTERVAL,
+       NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+
+       /* keep last */
+       __NL80211_SCHED_SCAN_PLAN_AFTER_LAST,
+       NL80211_SCHED_SCAN_PLAN_MAX =
+               __NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
+};
+
 #endif /* __LINUX_NL80211_H */
index 036f73bc54cd692763dc04954151d45a28f5e98d..28ccedd000f5720f6f41660ea6f049b9fd9f145c 100644 (file)
@@ -349,6 +349,8 @@ enum ovs_tunnel_key_attr {
        OVS_TUNNEL_KEY_ATTR_TP_SRC,             /* be16 src Transport Port. */
        OVS_TUNNEL_KEY_ATTR_TP_DST,             /* be16 dst Transport Port. */
        OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS,         /* Nested OVS_VXLAN_EXT_* */
+       OVS_TUNNEL_KEY_ATTR_IPV6_SRC,           /* struct in6_addr src IPv6 address. */
+       OVS_TUNNEL_KEY_ATTR_IPV6_DST,           /* struct in6_addr dst IPv6 address. */
        __OVS_TUNNEL_KEY_ATTR_MAX
 };
 
@@ -620,7 +622,8 @@ struct ovs_action_hash {
  * enum ovs_ct_attr - Attributes for %OVS_ACTION_ATTR_CT action.
  * @OVS_CT_ATTR_COMMIT: If present, commits the connection to the conntrack
  * table. This allows future packets for the same connection to be identified
- * as 'established' or 'related'.
+ * as 'established' or 'related'. The flow key for the current packet will
+ * retain the pre-commit connection state.
  * @OVS_CT_ATTR_ZONE: u16 connection tracking zone.
  * @OVS_CT_ATTR_MARK: u32 value followed by u32 mask. For each bit set in the
  * mask, the corresponding bit in the value is copied to the connection
index 2881145cda86cda91621da082ebec83ae490f3c5..d801bb0d9f6d2b08b6be600565716d045d8b85ca 100644 (file)
@@ -110,6 +110,7 @@ enum perf_sw_ids {
        PERF_COUNT_SW_ALIGNMENT_FAULTS          = 7,
        PERF_COUNT_SW_EMULATION_FAULTS          = 8,
        PERF_COUNT_SW_DUMMY                     = 9,
+       PERF_COUNT_SW_BPF_OUTPUT                = 10,
 
        PERF_COUNT_SW_MAX,                      /* non-ABI */
 };
@@ -168,6 +169,7 @@ enum perf_branch_sample_type_shift {
 
        PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT     = 11, /* call/ret stack */
        PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT       = 12, /* indirect jumps */
+       PERF_SAMPLE_BRANCH_CALL_SHIFT           = 13, /* direct call */
 
        PERF_SAMPLE_BRANCH_MAX_SHIFT            /* non-ABI */
 };
@@ -188,6 +190,7 @@ enum perf_branch_sample_type {
 
        PERF_SAMPLE_BRANCH_CALL_STACK   = 1U << PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT,
        PERF_SAMPLE_BRANCH_IND_JUMP     = 1U << PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT,
+       PERF_SAMPLE_BRANCH_CALL         = 1U << PERF_SAMPLE_BRANCH_CALL_SHIFT,
 
        PERF_SAMPLE_BRANCH_MAX          = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT,
 };
@@ -476,7 +479,7 @@ struct perf_event_mmap_page {
         *   u64 delta;
         *
         *   quot = (cyc >> time_shift);
-        *   rem = cyc & ((1 << time_shift) - 1);
+        *   rem = cyc & (((u64)1 << time_shift) - 1);
         *   delta = time_offset + quot * time_mult +
         *              ((rem * time_mult) >> time_shift);
         *
@@ -507,7 +510,7 @@ struct perf_event_mmap_page {
         * And vice versa:
         *
         *   quot = cyc >> time_shift;
-        *   rem  = cyc & ((1 << time_shift) - 1);
+        *   rem  = cyc & (((u64)1 << time_shift) - 1);
         *   timestamp = time_zero + quot * time_mult +
         *               ((rem * time_mult) >> time_shift);
         */
index 4f0d1bc3647dc266de5cf017efafe37a2f4f2e2a..439873775d49196a93b6f6b7dfaa820574fd462e 100644 (file)
@@ -87,6 +87,7 @@ enum {
 #define TC_ACT_STOLEN          4
 #define TC_ACT_QUEUED          5
 #define TC_ACT_REPEAT          6
+#define TC_ACT_REDIRECT                7
 #define TC_ACT_JUMP            0x10000000
 
 /* Action type identifiers*/
@@ -373,6 +374,8 @@ enum {
 
 /* BPF classifier */
 
+#define TCA_BPF_FLAG_ACT_DIRECT                (1 << 0)
+
 enum {
        TCA_BPF_UNSPEC,
        TCA_BPF_ACT,
@@ -382,6 +385,7 @@ enum {
        TCA_BPF_OPS,
        TCA_BPF_FD,
        TCA_BPF_NAME,
+       TCA_BPF_FLAGS,
        __TCA_BPF_MAX,
 };
 
index a7a69798661440b33f13c74e6560aabdb335ed1a..fb810650900029cf1a933a7eeec033e6dc7269d6 100644 (file)
@@ -64,6 +64,8 @@ struct ptrace_peeksiginfo_args {
 #define PTRACE_GETSIGMASK      0x420a
 #define PTRACE_SETSIGMASK      0x420b
 
+#define PTRACE_SECCOMP_GET_FILTER      0x420c
+
 /* Read signals from a shared (process wide) queue */
 #define PTRACE_PEEKSIGINFO_SHARED      (1 << 0)
 
index 9d8f5d10c1e553122be08d3407434ea24d19c136..123a5af4e8bb54b0cf33d9558d7fa0b1bb8a31f6 100644 (file)
@@ -270,6 +270,7 @@ enum rt_scope_t {
 #define RTM_F_CLONED           0x200   /* This route is cloned         */
 #define RTM_F_EQUALIZE         0x400   /* Multipath equalizer: NI      */
 #define RTM_F_PREFIX           0x800   /* Prefix addresses             */
+#define RTM_F_LOOKUP_TABLE     0x1000  /* set rtm_table to FIB lookup result */
 
 /* Reserved table identifiers */
 
@@ -666,6 +667,7 @@ struct tcamsg {
 #define RTEXT_FILTER_VF                (1 << 0)
 #define RTEXT_FILTER_BRVLAN    (1 << 1)
 #define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2)
+#define        RTEXT_FILTER_SKIP_STATS (1 << 3)
 
 /* End of information exported to user level */
 
index 7530e7447620ef8e4a1fee697c016e84da52cf0f..8b8d39dfb67f1992bc8e9fa95af34c8c4f05eb3a 100644 (file)
@@ -43,7 +43,8 @@ struct screen_info {
        __u16 pages;            /* 0x32 */
        __u16 vesa_attributes;  /* 0x34 */
        __u32 capabilities;     /* 0x36 */
-       __u8  _reserved[6];     /* 0x3a */
+       __u32 ext_lfb_base;     /* 0x3a */
+       __u8  _reserved[2];     /* 0x3e */
 } __attribute__((packed));
 
 #define VIDEO_TYPE_MDA         0x10    /* Monochrome Text Display      */
@@ -69,6 +70,6 @@ struct screen_info {
 #define VIDEO_FLAGS_NOCURSOR   (1 << 0) /* The video mode has no cursor set */
 
 #define VIDEO_CAPABILITY_SKIP_QUIRKS   (1 << 0)
-
+#define VIDEO_CAPABILITY_64BIT_BASE    (1 << 1)        /* Frame buffer base is 64-bit */
 
 #endif /* _UAPI_SCREEN_INFO_H */
index b6a9cdd6e096a26831bcb5c1cc69e6d41c8afe05..e2bc417b243bec21a8889e8397c9a1d19cc32955 100644 (file)
@@ -6,8 +6,8 @@
  * firmware based USB peripherals.
  */
 
-#ifndef __LINUX_USB_CDC_H
-#define __LINUX_USB_CDC_H
+#ifndef __UAPI_LINUX_USB_CDC_H
+#define __UAPI_LINUX_USB_CDC_H
 
 #include <linux/types.h>
 
@@ -444,4 +444,4 @@ struct usb_cdc_ncm_ndp_input_size {
 #define USB_CDC_NCM_CRC_NOT_APPENDED                   0x00
 #define USB_CDC_NCM_CRC_APPENDED                       0x01
 
-#endif /* __LINUX_USB_CDC_H */
+#endif /* __UAPI_LINUX_USB_CDC_H */
index e6983be12bd373dfc68eab347f9c6e764fbb6ce3..13272582eee00099a45179d82b47556ea0eb3cf9 100644 (file)
@@ -1,2 +1,4 @@
 obj-y := core.o
-obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o hashtab.o arraymap.o helpers.o
+
+obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o
+obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o
index 29ace107f2365c05b97f8f8aba54defeb59a6a78..3f4c99e06c6bbf8a5e28f882633e8161f3974929 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/filter.h>
+#include <linux/perf_event.h>
 
 /* Called from syscall */
 static struct bpf_map *array_map_alloc(union bpf_attr *attr)
@@ -48,7 +49,7 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
        array->map.key_size = attr->key_size;
        array->map.value_size = attr->value_size;
        array->map.max_entries = attr->max_entries;
-
+       array->map.pages = round_up(array_size, PAGE_SIZE) >> PAGE_SHIFT;
        array->elem_size = elem_size;
 
        return &array->map;
@@ -291,14 +292,23 @@ static void *perf_event_fd_array_get_ptr(struct bpf_map *map, int fd)
 
        attr = perf_event_attrs(event);
        if (IS_ERR(attr))
-               return (void *)attr;
+               goto err;
 
-       if (attr->type != PERF_TYPE_RAW &&
-           attr->type != PERF_TYPE_HARDWARE) {
-               perf_event_release_kernel(event);
-               return ERR_PTR(-EINVAL);
-       }
-       return event;
+       if (attr->inherit)
+               goto err;
+
+       if (attr->type == PERF_TYPE_RAW)
+               return event;
+
+       if (attr->type == PERF_TYPE_HARDWARE)
+               return event;
+
+       if (attr->type == PERF_TYPE_SOFTWARE &&
+           attr->config == PERF_COUNT_SW_BPF_OUTPUT)
+               return event;
+err:
+       perf_event_release_kernel(event);
+       return ERR_PTR(-EINVAL);
 }
 
 static void perf_event_fd_array_put_ptr(void *ptr)
index 67c380cfa9ca5b6ed8e48b69b38ba3c611d51a65..334b1bdd572c1e66007bd7163c9b98ee9c37deb5 100644 (file)
@@ -82,6 +82,8 @@ struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags)
        if (fp == NULL)
                return NULL;
 
+       kmemcheck_annotate_bitfield(fp, meta);
+
        aux = kzalloc(sizeof(*aux), GFP_KERNEL | gfp_extra_flags);
        if (aux == NULL) {
                vfree(fp);
@@ -90,6 +92,7 @@ struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags)
 
        fp->pages = size / PAGE_SIZE;
        fp->aux = aux;
+       fp->aux->prog = fp;
 
        return fp;
 }
@@ -110,8 +113,11 @@ struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
 
        fp = __vmalloc(size, gfp_flags, PAGE_KERNEL);
        if (fp != NULL) {
+               kmemcheck_annotate_bitfield(fp, meta);
+
                memcpy(fp, fp_old, fp_old->pages * PAGE_SIZE);
                fp->pages = size / PAGE_SIZE;
+               fp->aux->prog = fp;
 
                /* We keep fp->aux from fp_old around in the new
                 * reallocated structure.
@@ -722,11 +728,36 @@ void bpf_prog_free(struct bpf_prog *fp)
        struct bpf_prog_aux *aux = fp->aux;
 
        INIT_WORK(&aux->work, bpf_prog_free_deferred);
-       aux->prog = fp;
        schedule_work(&aux->work);
 }
 EXPORT_SYMBOL_GPL(bpf_prog_free);
 
+/* RNG for unpriviledged user space with separated state from prandom_u32(). */
+static DEFINE_PER_CPU(struct rnd_state, bpf_user_rnd_state);
+
+void bpf_user_rnd_init_once(void)
+{
+       prandom_init_once(&bpf_user_rnd_state);
+}
+
+u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
+{
+       /* Should someone ever have the rather unwise idea to use some
+        * of the registers passed into this function, then note that
+        * this function is called from native eBPF and classic-to-eBPF
+        * transformations. Register assignments from both sides are
+        * different, f.e. classic always sets fn(ctx, A, X) here.
+        */
+       struct rnd_state *state;
+       u32 res;
+
+       state = &get_cpu_var(bpf_user_rnd_state);
+       res = prandom_u32_state(state);
+       put_cpu_var(state);
+
+       return res;
+}
+
 /* Weak definitions of helper functions in case we don't have bpf syscall. */
 const struct bpf_func_proto bpf_map_lookup_elem_proto __weak;
 const struct bpf_func_proto bpf_map_update_elem_proto __weak;
index 83c209d9b17ae6b23eb16d0fd0d94b873897ffba..19909b22b4f8398d7373cc3838b835728265ed23 100644 (file)
@@ -17,7 +17,7 @@
 struct bpf_htab {
        struct bpf_map map;
        struct hlist_head *buckets;
-       spinlock_t lock;
+       raw_spinlock_t lock;
        u32 count;      /* number of elements in this hashtable */
        u32 n_buckets;  /* number of hash buckets */
        u32 elem_size;  /* size of each element in bytes */
@@ -82,12 +82,16 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
        for (i = 0; i < htab->n_buckets; i++)
                INIT_HLIST_HEAD(&htab->buckets[i]);
 
-       spin_lock_init(&htab->lock);
+       raw_spin_lock_init(&htab->lock);
        htab->count = 0;
 
        htab->elem_size = sizeof(struct htab_elem) +
                          round_up(htab->map.key_size, 8) +
                          htab->map.value_size;
+
+       htab->map.pages = round_up(htab->n_buckets * sizeof(struct hlist_head) +
+                                  htab->elem_size * htab->map.max_entries,
+                                  PAGE_SIZE) >> PAGE_SHIFT;
        return &htab->map;
 
 free_htab:
@@ -230,7 +234,7 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
        l_new->hash = htab_map_hash(l_new->key, key_size);
 
        /* bpf_map_update_elem() can be called in_irq() */
-       spin_lock_irqsave(&htab->lock, flags);
+       raw_spin_lock_irqsave(&htab->lock, flags);
 
        head = select_bucket(htab, l_new->hash);
 
@@ -266,11 +270,11 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
        } else {
                htab->count++;
        }
-       spin_unlock_irqrestore(&htab->lock, flags);
+       raw_spin_unlock_irqrestore(&htab->lock, flags);
 
        return 0;
 err:
-       spin_unlock_irqrestore(&htab->lock, flags);
+       raw_spin_unlock_irqrestore(&htab->lock, flags);
        kfree(l_new);
        return ret;
 }
@@ -291,7 +295,7 @@ static int htab_map_delete_elem(struct bpf_map *map, void *key)
 
        hash = htab_map_hash(key, key_size);
 
-       spin_lock_irqsave(&htab->lock, flags);
+       raw_spin_lock_irqsave(&htab->lock, flags);
 
        head = select_bucket(htab, hash);
 
@@ -304,7 +308,7 @@ static int htab_map_delete_elem(struct bpf_map *map, void *key)
                ret = 0;
        }
 
-       spin_unlock_irqrestore(&htab->lock, flags);
+       raw_spin_unlock_irqrestore(&htab->lock, flags);
        return ret;
 }
 
index 1447ec09421ebc4905c922f4f4fa2b65b52b474a..4504ca66118da0c0735bdb56d9204ba6cb79994b 100644 (file)
@@ -93,13 +93,8 @@ const struct bpf_func_proto bpf_map_delete_elem_proto = {
        .arg2_type      = ARG_PTR_TO_MAP_KEY,
 };
 
-static u64 bpf_get_prandom_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
-{
-       return prandom_u32();
-}
-
 const struct bpf_func_proto bpf_get_prandom_u32_proto = {
-       .func           = bpf_get_prandom_u32,
+       .func           = bpf_user_rnd_u32,
        .gpl_only       = false,
        .ret_type       = RET_INTEGER,
 };
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
new file mode 100644 (file)
index 0000000..be6d726
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * Minimal file system backend for holding eBPF maps and programs,
+ * used by bpf(2) object pinning.
+ *
+ * Authors:
+ *
+ *     Daniel Borkmann <daniel@iogearbox.net>
+ *
+ * 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>
+#include <linux/magic.h>
+#include <linux/major.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/fs.h>
+#include <linux/kdev_t.h>
+#include <linux/filter.h>
+#include <linux/bpf.h>
+
+enum bpf_type {
+       BPF_TYPE_UNSPEC = 0,
+       BPF_TYPE_PROG,
+       BPF_TYPE_MAP,
+};
+
+static void *bpf_any_get(void *raw, enum bpf_type type)
+{
+       switch (type) {
+       case BPF_TYPE_PROG:
+               atomic_inc(&((struct bpf_prog *)raw)->aux->refcnt);
+               break;
+       case BPF_TYPE_MAP:
+               atomic_inc(&((struct bpf_map *)raw)->refcnt);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               break;
+       }
+
+       return raw;
+}
+
+static void bpf_any_put(void *raw, enum bpf_type type)
+{
+       switch (type) {
+       case BPF_TYPE_PROG:
+               bpf_prog_put(raw);
+               break;
+       case BPF_TYPE_MAP:
+               bpf_map_put(raw);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               break;
+       }
+}
+
+static void *bpf_fd_probe_obj(u32 ufd, enum bpf_type *type)
+{
+       void *raw;
+
+       *type = BPF_TYPE_MAP;
+       raw = bpf_map_get(ufd);
+       if (IS_ERR(raw)) {
+               *type = BPF_TYPE_PROG;
+               raw = bpf_prog_get(ufd);
+       }
+
+       return raw;
+}
+
+static const struct inode_operations bpf_dir_iops;
+
+static const struct inode_operations bpf_prog_iops = { };
+static const struct inode_operations bpf_map_iops  = { };
+
+static struct inode *bpf_get_inode(struct super_block *sb,
+                                  const struct inode *dir,
+                                  umode_t mode)
+{
+       struct inode *inode;
+
+       switch (mode & S_IFMT) {
+       case S_IFDIR:
+       case S_IFREG:
+               break;
+       default:
+               return ERR_PTR(-EINVAL);
+       }
+
+       inode = new_inode(sb);
+       if (!inode)
+               return ERR_PTR(-ENOSPC);
+
+       inode->i_ino = get_next_ino();
+       inode->i_atime = CURRENT_TIME;
+       inode->i_mtime = inode->i_atime;
+       inode->i_ctime = inode->i_atime;
+
+       inode_init_owner(inode, dir, mode);
+
+       return inode;
+}
+
+static int bpf_inode_type(const struct inode *inode, enum bpf_type *type)
+{
+       *type = BPF_TYPE_UNSPEC;
+       if (inode->i_op == &bpf_prog_iops)
+               *type = BPF_TYPE_PROG;
+       else if (inode->i_op == &bpf_map_iops)
+               *type = BPF_TYPE_MAP;
+       else
+               return -EACCES;
+
+       return 0;
+}
+
+static bool bpf_dname_reserved(const struct dentry *dentry)
+{
+       return strchr(dentry->d_name.name, '.');
+}
+
+static int bpf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+       struct inode *inode;
+
+       if (bpf_dname_reserved(dentry))
+               return -EPERM;
+
+       inode = bpf_get_inode(dir->i_sb, dir, mode | S_IFDIR);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+
+       inode->i_op = &bpf_dir_iops;
+       inode->i_fop = &simple_dir_operations;
+
+       inc_nlink(inode);
+       inc_nlink(dir);
+
+       d_instantiate(dentry, inode);
+       dget(dentry);
+
+       return 0;
+}
+
+static int bpf_mkobj_ops(struct inode *dir, struct dentry *dentry,
+                        umode_t mode, const struct inode_operations *iops)
+{
+       struct inode *inode;
+
+       if (bpf_dname_reserved(dentry))
+               return -EPERM;
+
+       inode = bpf_get_inode(dir->i_sb, dir, mode | S_IFREG);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+
+       inode->i_op = iops;
+       inode->i_private = dentry->d_fsdata;
+
+       d_instantiate(dentry, inode);
+       dget(dentry);
+
+       return 0;
+}
+
+static int bpf_mkobj(struct inode *dir, struct dentry *dentry, umode_t mode,
+                    dev_t devt)
+{
+       enum bpf_type type = MINOR(devt);
+
+       if (MAJOR(devt) != UNNAMED_MAJOR || !S_ISREG(mode) ||
+           dentry->d_fsdata == NULL)
+               return -EPERM;
+
+       switch (type) {
+       case BPF_TYPE_PROG:
+               return bpf_mkobj_ops(dir, dentry, mode, &bpf_prog_iops);
+       case BPF_TYPE_MAP:
+               return bpf_mkobj_ops(dir, dentry, mode, &bpf_map_iops);
+       default:
+               return -EPERM;
+       }
+}
+
+static const struct inode_operations bpf_dir_iops = {
+       .lookup         = simple_lookup,
+       .mknod          = bpf_mkobj,
+       .mkdir          = bpf_mkdir,
+       .rmdir          = simple_rmdir,
+       .unlink         = simple_unlink,
+};
+
+static int bpf_obj_do_pin(const struct filename *pathname, void *raw,
+                         enum bpf_type type)
+{
+       struct dentry *dentry;
+       struct inode *dir;
+       struct path path;
+       umode_t mode;
+       dev_t devt;
+       int ret;
+
+       dentry = kern_path_create(AT_FDCWD, pathname->name, &path, 0);
+       if (IS_ERR(dentry))
+               return PTR_ERR(dentry);
+
+       mode = S_IFREG | ((S_IRUSR | S_IWUSR) & ~current_umask());
+       devt = MKDEV(UNNAMED_MAJOR, type);
+
+       ret = security_path_mknod(&path, dentry, mode, devt);
+       if (ret)
+               goto out;
+
+       dir = d_inode(path.dentry);
+       if (dir->i_op != &bpf_dir_iops) {
+               ret = -EPERM;
+               goto out;
+       }
+
+       dentry->d_fsdata = raw;
+       ret = vfs_mknod(dir, dentry, mode, devt);
+       dentry->d_fsdata = NULL;
+out:
+       done_path_create(&path, dentry);
+       return ret;
+}
+
+int bpf_obj_pin_user(u32 ufd, const char __user *pathname)
+{
+       struct filename *pname;
+       enum bpf_type type;
+       void *raw;
+       int ret;
+
+       pname = getname(pathname);
+       if (IS_ERR(pname))
+               return PTR_ERR(pname);
+
+       raw = bpf_fd_probe_obj(ufd, &type);
+       if (IS_ERR(raw)) {
+               ret = PTR_ERR(raw);
+               goto out;
+       }
+
+       ret = bpf_obj_do_pin(pname, raw, type);
+       if (ret != 0)
+               bpf_any_put(raw, type);
+out:
+       putname(pname);
+       return ret;
+}
+
+static void *bpf_obj_do_get(const struct filename *pathname,
+                           enum bpf_type *type)
+{
+       struct inode *inode;
+       struct path path;
+       void *raw;
+       int ret;
+
+       ret = kern_path(pathname->name, LOOKUP_FOLLOW, &path);
+       if (ret)
+               return ERR_PTR(ret);
+
+       inode = d_backing_inode(path.dentry);
+       ret = inode_permission(inode, MAY_WRITE);
+       if (ret)
+               goto out;
+
+       ret = bpf_inode_type(inode, type);
+       if (ret)
+               goto out;
+
+       raw = bpf_any_get(inode->i_private, *type);
+       touch_atime(&path);
+
+       path_put(&path);
+       return raw;
+out:
+       path_put(&path);
+       return ERR_PTR(ret);
+}
+
+int bpf_obj_get_user(const char __user *pathname)
+{
+       enum bpf_type type = BPF_TYPE_UNSPEC;
+       struct filename *pname;
+       int ret = -ENOENT;
+       void *raw;
+
+       pname = getname(pathname);
+       if (IS_ERR(pname))
+               return PTR_ERR(pname);
+
+       raw = bpf_obj_do_get(pname, &type);
+       if (IS_ERR(raw)) {
+               ret = PTR_ERR(raw);
+               goto out;
+       }
+
+       if (type == BPF_TYPE_PROG)
+               ret = bpf_prog_new_fd(raw);
+       else if (type == BPF_TYPE_MAP)
+               ret = bpf_map_new_fd(raw);
+       else
+               goto out;
+
+       if (ret < 0)
+               bpf_any_put(raw, type);
+out:
+       putname(pname);
+       return ret;
+}
+
+static void bpf_evict_inode(struct inode *inode)
+{
+       enum bpf_type type;
+
+       truncate_inode_pages_final(&inode->i_data);
+       clear_inode(inode);
+
+       if (!bpf_inode_type(inode, &type))
+               bpf_any_put(inode->i_private, type);
+}
+
+static const struct super_operations bpf_super_ops = {
+       .statfs         = simple_statfs,
+       .drop_inode     = generic_delete_inode,
+       .evict_inode    = bpf_evict_inode,
+};
+
+static int bpf_fill_super(struct super_block *sb, void *data, int silent)
+{
+       static struct tree_descr bpf_rfiles[] = { { "" } };
+       struct inode *inode;
+       int ret;
+
+       ret = simple_fill_super(sb, BPF_FS_MAGIC, bpf_rfiles);
+       if (ret)
+               return ret;
+
+       sb->s_op = &bpf_super_ops;
+
+       inode = sb->s_root->d_inode;
+       inode->i_op = &bpf_dir_iops;
+       inode->i_mode &= ~S_IALLUGO;
+       inode->i_mode |= S_ISVTX | S_IRWXUGO;
+
+       return 0;
+}
+
+static struct dentry *bpf_mount(struct file_system_type *type, int flags,
+                               const char *dev_name, void *data)
+{
+       return mount_ns(type, flags, current->nsproxy->mnt_ns, bpf_fill_super);
+}
+
+static struct file_system_type bpf_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "bpf",
+       .mount          = bpf_mount,
+       .kill_sb        = kill_litter_super,
+       .fs_flags       = FS_USERNS_MOUNT,
+};
+
+MODULE_ALIAS_FS("bpf");
+
+static int __init bpf_init(void)
+{
+       int ret;
+
+       ret = sysfs_create_mount_point(fs_kobj, "bpf");
+       if (ret)
+               return ret;
+
+       ret = register_filesystem(&bpf_fs_type);
+       if (ret)
+               sysfs_remove_mount_point(fs_kobj, "bpf");
+
+       return ret;
+}
+fs_initcall(bpf_init);
index 35bac8e8b071ae5aa57e3837f4363d8dcd741158..0d3313d02a7e512e1ca7f58fb52aa3e39bae60a3 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/filter.h>
 #include <linux/version.h>
 
+int sysctl_unprivileged_bpf_disabled __read_mostly;
+
 static LIST_HEAD(bpf_map_types);
 
 static struct bpf_map *find_and_alloc_map(union bpf_attr *attr)
@@ -44,11 +46,38 @@ void bpf_register_map_type(struct bpf_map_type_list *tl)
        list_add(&tl->list_node, &bpf_map_types);
 }
 
+static int bpf_map_charge_memlock(struct bpf_map *map)
+{
+       struct user_struct *user = get_current_user();
+       unsigned long memlock_limit;
+
+       memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+
+       atomic_long_add(map->pages, &user->locked_vm);
+
+       if (atomic_long_read(&user->locked_vm) > memlock_limit) {
+               atomic_long_sub(map->pages, &user->locked_vm);
+               free_uid(user);
+               return -EPERM;
+       }
+       map->user = user;
+       return 0;
+}
+
+static void bpf_map_uncharge_memlock(struct bpf_map *map)
+{
+       struct user_struct *user = map->user;
+
+       atomic_long_sub(map->pages, &user->locked_vm);
+       free_uid(user);
+}
+
 /* called from workqueue */
 static void bpf_map_free_deferred(struct work_struct *work)
 {
        struct bpf_map *map = container_of(work, struct bpf_map, work);
 
+       bpf_map_uncharge_memlock(map);
        /* implementation dependent freeing */
        map->ops->map_free(map);
 }
@@ -82,6 +111,12 @@ static const struct file_operations bpf_map_fops = {
        .release = bpf_map_release,
 };
 
+int bpf_map_new_fd(struct bpf_map *map)
+{
+       return anon_inode_getfd("bpf-map", &bpf_map_fops, map,
+                               O_RDWR | O_CLOEXEC);
+}
+
 /* helper macro to check that unused fields 'union bpf_attr' are zero */
 #define CHECK_ATTR(CMD) \
        memchr_inv((void *) &attr->CMD##_LAST_FIELD + \
@@ -108,8 +143,11 @@ static int map_create(union bpf_attr *attr)
 
        atomic_set(&map->refcnt, 1);
 
-       err = anon_inode_getfd("bpf-map", &bpf_map_fops, map, O_RDWR | O_CLOEXEC);
+       err = bpf_map_charge_memlock(map);
+       if (err)
+               goto free_map;
 
+       err = bpf_map_new_fd(map);
        if (err < 0)
                /* failed to allocate fd */
                goto free_map;
@@ -124,19 +162,29 @@ free_map:
 /* if error is returned, fd is released.
  * On success caller should complete fd access with matching fdput()
  */
-struct bpf_map *bpf_map_get(struct fd f)
+struct bpf_map *__bpf_map_get(struct fd f)
 {
-       struct bpf_map *map;
-
        if (!f.file)
                return ERR_PTR(-EBADF);
-
        if (f.file->f_op != &bpf_map_fops) {
                fdput(f);
                return ERR_PTR(-EINVAL);
        }
 
-       map = f.file->private_data;
+       return f.file->private_data;
+}
+
+struct bpf_map *bpf_map_get(u32 ufd)
+{
+       struct fd f = fdget(ufd);
+       struct bpf_map *map;
+
+       map = __bpf_map_get(f);
+       if (IS_ERR(map))
+               return map;
+
+       atomic_inc(&map->refcnt);
+       fdput(f);
 
        return map;
 }
@@ -164,7 +212,7 @@ static int map_lookup_elem(union bpf_attr *attr)
                return -EINVAL;
 
        f = fdget(ufd);
-       map = bpf_map_get(f);
+       map = __bpf_map_get(f);
        if (IS_ERR(map))
                return PTR_ERR(map);
 
@@ -223,7 +271,7 @@ static int map_update_elem(union bpf_attr *attr)
                return -EINVAL;
 
        f = fdget(ufd);
-       map = bpf_map_get(f);
+       map = __bpf_map_get(f);
        if (IS_ERR(map))
                return PTR_ERR(map);
 
@@ -276,7 +324,7 @@ static int map_delete_elem(union bpf_attr *attr)
                return -EINVAL;
 
        f = fdget(ufd);
-       map = bpf_map_get(f);
+       map = __bpf_map_get(f);
        if (IS_ERR(map))
                return PTR_ERR(map);
 
@@ -317,7 +365,7 @@ static int map_get_next_key(union bpf_attr *attr)
                return -EINVAL;
 
        f = fdget(ufd);
-       map = bpf_map_get(f);
+       map = __bpf_map_get(f);
        if (IS_ERR(map))
                return PTR_ERR(map);
 
@@ -402,6 +450,10 @@ static void fixup_bpf_calls(struct bpf_prog *prog)
                         */
                        BUG_ON(!prog->aux->ops->get_func_proto);
 
+                       if (insn->imm == BPF_FUNC_get_route_realm)
+                               prog->dst_needed = 1;
+                       if (insn->imm == BPF_FUNC_get_prandom_u32)
+                               bpf_user_rnd_init_once();
                        if (insn->imm == BPF_FUNC_tail_call) {
                                /* mark bpf_tail_call as different opcode
                                 * to avoid conditional branch in
@@ -436,29 +488,51 @@ static void free_used_maps(struct bpf_prog_aux *aux)
        kfree(aux->used_maps);
 }
 
-static void __prog_put_rcu(struct rcu_head *rcu)
+static int bpf_prog_charge_memlock(struct bpf_prog *prog)
+{
+       struct user_struct *user = get_current_user();
+       unsigned long memlock_limit;
+
+       memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+
+       atomic_long_add(prog->pages, &user->locked_vm);
+       if (atomic_long_read(&user->locked_vm) > memlock_limit) {
+               atomic_long_sub(prog->pages, &user->locked_vm);
+               free_uid(user);
+               return -EPERM;
+       }
+       prog->aux->user = user;
+       return 0;
+}
+
+static void bpf_prog_uncharge_memlock(struct bpf_prog *prog)
+{
+       struct user_struct *user = prog->aux->user;
+
+       atomic_long_sub(prog->pages, &user->locked_vm);
+       free_uid(user);
+}
+
+static void __prog_put_common(struct rcu_head *rcu)
 {
        struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu);
 
        free_used_maps(aux);
+       bpf_prog_uncharge_memlock(aux->prog);
        bpf_prog_free(aux->prog);
 }
 
 /* version of bpf_prog_put() that is called after a grace period */
 void bpf_prog_put_rcu(struct bpf_prog *prog)
 {
-       if (atomic_dec_and_test(&prog->aux->refcnt)) {
-               prog->aux->prog = prog;
-               call_rcu(&prog->aux->rcu, __prog_put_rcu);
-       }
+       if (atomic_dec_and_test(&prog->aux->refcnt))
+               call_rcu(&prog->aux->rcu, __prog_put_common);
 }
 
 void bpf_prog_put(struct bpf_prog *prog)
 {
-       if (atomic_dec_and_test(&prog->aux->refcnt)) {
-               free_used_maps(prog->aux);
-               bpf_prog_free(prog);
-       }
+       if (atomic_dec_and_test(&prog->aux->refcnt))
+               __prog_put_common(&prog->aux->rcu);
 }
 EXPORT_SYMBOL_GPL(bpf_prog_put);
 
@@ -474,21 +548,22 @@ static const struct file_operations bpf_prog_fops = {
         .release = bpf_prog_release,
 };
 
-static struct bpf_prog *get_prog(struct fd f)
+int bpf_prog_new_fd(struct bpf_prog *prog)
 {
-       struct bpf_prog *prog;
+       return anon_inode_getfd("bpf-prog", &bpf_prog_fops, prog,
+                               O_RDWR | O_CLOEXEC);
+}
 
+static struct bpf_prog *__bpf_prog_get(struct fd f)
+{
        if (!f.file)
                return ERR_PTR(-EBADF);
-
        if (f.file->f_op != &bpf_prog_fops) {
                fdput(f);
                return ERR_PTR(-EINVAL);
        }
 
-       prog = f.file->private_data;
-
-       return prog;
+       return f.file->private_data;
 }
 
 /* called by sockets/tracing/seccomp before attaching program to an event
@@ -499,13 +574,13 @@ struct bpf_prog *bpf_prog_get(u32 ufd)
        struct fd f = fdget(ufd);
        struct bpf_prog *prog;
 
-       prog = get_prog(f);
-
+       prog = __bpf_prog_get(f);
        if (IS_ERR(prog))
                return prog;
 
        atomic_inc(&prog->aux->refcnt);
        fdput(f);
+
        return prog;
 }
 EXPORT_SYMBOL_GPL(bpf_prog_get);
@@ -540,11 +615,18 @@ static int bpf_prog_load(union bpf_attr *attr)
            attr->kern_version != LINUX_VERSION_CODE)
                return -EINVAL;
 
+       if (type != BPF_PROG_TYPE_SOCKET_FILTER && !capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
        /* plain bpf_prog allocation */
        prog = bpf_prog_alloc(bpf_prog_size(attr->insn_cnt), GFP_USER);
        if (!prog)
                return -ENOMEM;
 
+       err = bpf_prog_charge_memlock(prog);
+       if (err)
+               goto free_prog_nouncharge;
+
        prog->len = attr->insn_cnt;
 
        err = -EFAULT;
@@ -553,10 +635,10 @@ static int bpf_prog_load(union bpf_attr *attr)
                goto free_prog;
 
        prog->orig_prog = NULL;
-       prog->jited = false;
+       prog->jited = 0;
 
        atomic_set(&prog->aux->refcnt, 1);
-       prog->gpl_compatible = is_gpl;
+       prog->gpl_compatible = is_gpl ? 1 : 0;
 
        /* find program type: socket_filter vs tracing_filter */
        err = find_prog_type(type, prog);
@@ -576,7 +658,7 @@ static int bpf_prog_load(union bpf_attr *attr)
        if (err < 0)
                goto free_used_maps;
 
-       err = anon_inode_getfd("bpf-prog", &bpf_prog_fops, prog, O_RDWR | O_CLOEXEC);
+       err = bpf_prog_new_fd(prog);
        if (err < 0)
                /* failed to allocate fd */
                goto free_used_maps;
@@ -586,20 +668,36 @@ static int bpf_prog_load(union bpf_attr *attr)
 free_used_maps:
        free_used_maps(prog->aux);
 free_prog:
+       bpf_prog_uncharge_memlock(prog);
+free_prog_nouncharge:
        bpf_prog_free(prog);
        return err;
 }
 
+#define BPF_OBJ_LAST_FIELD bpf_fd
+
+static int bpf_obj_pin(const union bpf_attr *attr)
+{
+       if (CHECK_ATTR(BPF_OBJ))
+               return -EINVAL;
+
+       return bpf_obj_pin_user(attr->bpf_fd, u64_to_ptr(attr->pathname));
+}
+
+static int bpf_obj_get(const union bpf_attr *attr)
+{
+       if (CHECK_ATTR(BPF_OBJ) || attr->bpf_fd != 0)
+               return -EINVAL;
+
+       return bpf_obj_get_user(u64_to_ptr(attr->pathname));
+}
+
 SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
 {
        union bpf_attr attr = {};
        int err;
 
-       /* the syscall is limited to root temporarily. This restriction will be
-        * lifted when security audit is clean. Note that eBPF+tracing must have
-        * this restriction, since it may pass kernel data to user space
-        */
-       if (!capable(CAP_SYS_ADMIN))
+       if (!capable(CAP_SYS_ADMIN) && sysctl_unprivileged_bpf_disabled)
                return -EPERM;
 
        if (!access_ok(VERIFY_READ, uattr, 1))
@@ -654,6 +752,12 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
        case BPF_PROG_LOAD:
                err = bpf_prog_load(&attr);
                break;
+       case BPF_OBJ_PIN:
+               err = bpf_obj_pin(&attr);
+               break;
+       case BPF_OBJ_GET:
+               err = bpf_obj_get(&attr);
+               break;
        default:
                err = -EINVAL;
                break;
index b074b23000d6e95792e7fcefe0cd29e5a649a6ea..c6073056badf02293c7e9d1e35dce9bb88536c67 100644 (file)
@@ -199,6 +199,7 @@ struct verifier_env {
        struct verifier_state_list **explored_states; /* search pruning optimization */
        struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by eBPF program */
        u32 used_map_cnt;               /* number of used maps */
+       bool allow_ptr_leaks;
 };
 
 /* verbose verifier prints what it's seeing
@@ -213,7 +214,7 @@ static DEFINE_MUTEX(bpf_verifier_lock);
  * verbose() is used to dump the verification trace to the log, so the user
  * can figure out what's wrong with the program
  */
-static void verbose(const char *fmt, ...)
+static __printf(1, 2) void verbose(const char *fmt, ...)
 {
        va_list args;
 
@@ -244,6 +245,7 @@ static const struct {
 } func_limit[] = {
        {BPF_MAP_TYPE_PROG_ARRAY, BPF_FUNC_tail_call},
        {BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_FUNC_perf_event_read},
+       {BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_FUNC_perf_event_output},
 };
 
 static void print_verifier_state(struct verifier_env *env)
@@ -538,6 +540,21 @@ static int bpf_size_to_bytes(int bpf_size)
                return -EINVAL;
 }
 
+static bool is_spillable_regtype(enum bpf_reg_type type)
+{
+       switch (type) {
+       case PTR_TO_MAP_VALUE:
+       case PTR_TO_MAP_VALUE_OR_NULL:
+       case PTR_TO_STACK:
+       case PTR_TO_CTX:
+       case FRAME_PTR:
+       case CONST_PTR_TO_MAP:
+               return true;
+       default:
+               return false;
+       }
+}
+
 /* check_stack_read/write functions track spill/fill of registers,
  * stack boundary and alignment are checked in check_mem_access()
  */
@@ -550,9 +567,7 @@ static int check_stack_write(struct verifier_state *state, int off, int size,
         */
 
        if (value_regno >= 0 &&
-           (state->regs[value_regno].type == PTR_TO_MAP_VALUE ||
-            state->regs[value_regno].type == PTR_TO_STACK ||
-            state->regs[value_regno].type == PTR_TO_CTX)) {
+           is_spillable_regtype(state->regs[value_regno].type)) {
 
                /* register containing pointer is being spilled into stack */
                if (size != BPF_REG_SIZE) {
@@ -643,6 +658,20 @@ static int check_ctx_access(struct verifier_env *env, int off, int size,
        return -EACCES;
 }
 
+static bool is_pointer_value(struct verifier_env *env, int regno)
+{
+       if (env->allow_ptr_leaks)
+               return false;
+
+       switch (env->cur_state.regs[regno].type) {
+       case UNKNOWN_VALUE:
+       case CONST_IMM:
+               return false;
+       default:
+               return true;
+       }
+}
+
 /* check whether memory at (regno + off) is accessible for t = (read | write)
  * if t==write, value_regno is a register which value is stored into memory
  * if t==read, value_regno is a register which will receive the value from memory
@@ -669,11 +698,21 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
        }
 
        if (state->regs[regno].type == PTR_TO_MAP_VALUE) {
+               if (t == BPF_WRITE && value_regno >= 0 &&
+                   is_pointer_value(env, value_regno)) {
+                       verbose("R%d leaks addr into map\n", value_regno);
+                       return -EACCES;
+               }
                err = check_map_access(env, regno, off, size);
                if (!err && t == BPF_READ && value_regno >= 0)
                        mark_reg_unknown_value(state->regs, value_regno);
 
        } else if (state->regs[regno].type == PTR_TO_CTX) {
+               if (t == BPF_WRITE && value_regno >= 0 &&
+                   is_pointer_value(env, value_regno)) {
+                       verbose("R%d leaks addr into ctx\n", value_regno);
+                       return -EACCES;
+               }
                err = check_ctx_access(env, off, size, t);
                if (!err && t == BPF_READ && value_regno >= 0)
                        mark_reg_unknown_value(state->regs, value_regno);
@@ -684,10 +723,17 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
                        verbose("invalid stack off=%d size=%d\n", off, size);
                        return -EACCES;
                }
-               if (t == BPF_WRITE)
+               if (t == BPF_WRITE) {
+                       if (!env->allow_ptr_leaks &&
+                           state->stack_slot_type[MAX_BPF_STACK + off] == STACK_SPILL &&
+                           size != BPF_REG_SIZE) {
+                               verbose("attempt to corrupt spilled pointer on stack\n");
+                               return -EACCES;
+                       }
                        err = check_stack_write(state, off, size, value_regno);
-               else
+               } else {
                        err = check_stack_read(state, off, size, value_regno);
+               }
        } else {
                verbose("R%d invalid mem access '%s'\n",
                        regno, reg_type_str[state->regs[regno].type]);
@@ -775,8 +821,13 @@ static int check_func_arg(struct verifier_env *env, u32 regno,
                return -EACCES;
        }
 
-       if (arg_type == ARG_ANYTHING)
+       if (arg_type == ARG_ANYTHING) {
+               if (is_pointer_value(env, regno)) {
+                       verbose("R%d leaks addr into helper function\n", regno);
+                       return -EACCES;
+               }
                return 0;
+       }
 
        if (arg_type == ARG_PTR_TO_STACK || arg_type == ARG_PTR_TO_MAP_KEY ||
            arg_type == ARG_PTR_TO_MAP_VALUE) {
@@ -860,7 +911,7 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id)
                 * don't allow any other map type to be passed into
                 * the special func;
                 */
-               if (bool_map != bool_func)
+               if (bool_func && bool_map != bool_func)
                        return -EINVAL;
        }
 
@@ -950,8 +1001,9 @@ static int check_call(struct verifier_env *env, int func_id)
 }
 
 /* check validity of 32-bit and 64-bit arithmetic operations */
-static int check_alu_op(struct reg_state *regs, struct bpf_insn *insn)
+static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
 {
+       struct reg_state *regs = env->cur_state.regs;
        u8 opcode = BPF_OP(insn->code);
        int err;
 
@@ -976,6 +1028,12 @@ static int check_alu_op(struct reg_state *regs, struct bpf_insn *insn)
                if (err)
                        return err;
 
+               if (is_pointer_value(env, insn->dst_reg)) {
+                       verbose("R%d pointer arithmetic prohibited\n",
+                               insn->dst_reg);
+                       return -EACCES;
+               }
+
                /* check dest operand */
                err = check_reg_arg(regs, insn->dst_reg, DST_OP);
                if (err)
@@ -1012,6 +1070,11 @@ static int check_alu_op(struct reg_state *regs, struct bpf_insn *insn)
                                 */
                                regs[insn->dst_reg] = regs[insn->src_reg];
                        } else {
+                               if (is_pointer_value(env, insn->src_reg)) {
+                                       verbose("R%d partial copy of pointer\n",
+                                               insn->src_reg);
+                                       return -EACCES;
+                               }
                                regs[insn->dst_reg].type = UNKNOWN_VALUE;
                                regs[insn->dst_reg].map_ptr = NULL;
                        }
@@ -1061,8 +1124,18 @@ static int check_alu_op(struct reg_state *regs, struct bpf_insn *insn)
                /* pattern match 'bpf_add Rx, imm' instruction */
                if (opcode == BPF_ADD && BPF_CLASS(insn->code) == BPF_ALU64 &&
                    regs[insn->dst_reg].type == FRAME_PTR &&
-                   BPF_SRC(insn->code) == BPF_K)
+                   BPF_SRC(insn->code) == BPF_K) {
                        stack_relative = true;
+               } else if (is_pointer_value(env, insn->dst_reg)) {
+                       verbose("R%d pointer arithmetic prohibited\n",
+                               insn->dst_reg);
+                       return -EACCES;
+               } else if (BPF_SRC(insn->code) == BPF_X &&
+                          is_pointer_value(env, insn->src_reg)) {
+                       verbose("R%d pointer arithmetic prohibited\n",
+                               insn->src_reg);
+                       return -EACCES;
+               }
 
                /* check dest operand */
                err = check_reg_arg(regs, insn->dst_reg, DST_OP);
@@ -1101,6 +1174,12 @@ static int check_cond_jmp_op(struct verifier_env *env,
                err = check_reg_arg(regs, insn->src_reg, SRC_OP);
                if (err)
                        return err;
+
+               if (is_pointer_value(env, insn->src_reg)) {
+                       verbose("R%d pointer comparison prohibited\n",
+                               insn->src_reg);
+                       return -EACCES;
+               }
        } else {
                if (insn->src_reg != BPF_REG_0) {
                        verbose("BPF_JMP uses reserved fields\n");
@@ -1155,6 +1234,9 @@ static int check_cond_jmp_op(struct verifier_env *env,
                        regs[insn->dst_reg].type = CONST_IMM;
                        regs[insn->dst_reg].imm = 0;
                }
+       } else if (is_pointer_value(env, insn->dst_reg)) {
+               verbose("R%d pointer comparison prohibited\n", insn->dst_reg);
+               return -EACCES;
        } else if (BPF_SRC(insn->code) == BPF_K &&
                   (opcode == BPF_JEQ || opcode == BPF_JNE)) {
 
@@ -1658,7 +1740,7 @@ static int do_check(struct verifier_env *env)
                }
 
                if (class == BPF_ALU || class == BPF_ALU64) {
-                       err = check_alu_op(regs, insn);
+                       err = check_alu_op(env, insn);
                        if (err)
                                return err;
 
@@ -1816,6 +1898,11 @@ static int do_check(struct verifier_env *env)
                                if (err)
                                        return err;
 
+                               if (is_pointer_value(env, BPF_REG_0)) {
+                                       verbose("R0 leaks addr as return value\n");
+                                       return -EACCES;
+                               }
+
 process_bpf_exit:
                                insn_idx = pop_stack(env, &prev_insn_idx);
                                if (insn_idx < 0) {
@@ -1902,8 +1989,7 @@ static int replace_map_fd_with_map_ptr(struct verifier_env *env)
                        }
 
                        f = fdget(insn->imm);
-
-                       map = bpf_map_get(f);
+                       map = __bpf_map_get(f);
                        if (IS_ERR(map)) {
                                verbose("fd %d is not pointing to valid bpf_map\n",
                                        insn->imm);
@@ -2024,7 +2110,7 @@ static int convert_ctx_accesses(struct verifier_env *env)
 
                cnt = env->prog->aux->ops->
                        convert_ctx_access(type, insn->dst_reg, insn->src_reg,
-                                          insn->off, insn_buf);
+                                          insn->off, insn_buf, env->prog);
                if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) {
                        verbose("bpf verifier is misconfigured\n");
                        return -EINVAL;
@@ -2144,6 +2230,8 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
        if (ret < 0)
                goto skip_full_check;
 
+       env->allow_ptr_leaks = capable(CAP_SYS_ADMIN);
+
        ret = do_check(env);
 
 skip_full_check:
index 82cf9dff4295eaa82305fe04a43cecb0c8a4c27e..85ff5e26e23b45b34201120c758082599f995b7e 100644 (file)
@@ -102,19 +102,6 @@ void get_online_cpus(void)
 }
 EXPORT_SYMBOL_GPL(get_online_cpus);
 
-bool try_get_online_cpus(void)
-{
-       if (cpu_hotplug.active_writer == current)
-               return true;
-       if (!mutex_trylock(&cpu_hotplug.lock))
-               return false;
-       cpuhp_lock_acquire_tryread();
-       atomic_inc(&cpu_hotplug.refcount);
-       mutex_unlock(&cpu_hotplug.lock);
-       return true;
-}
-EXPORT_SYMBOL_GPL(try_get_online_cpus);
-
 void put_online_cpus(void)
 {
        int refcount;
@@ -304,8 +291,8 @@ static inline void check_for_tasks(int dead_cpu)
 {
        struct task_struct *g, *p;
 
-       read_lock_irq(&tasklist_lock);
-       do_each_thread(g, p) {
+       read_lock(&tasklist_lock);
+       for_each_process_thread(g, p) {
                if (!p->on_rq)
                        continue;
                /*
@@ -320,8 +307,8 @@ static inline void check_for_tasks(int dead_cpu)
 
                pr_warn("Task %s (pid=%d) is on cpu %d (state=%ld, flags=%x)\n",
                        p->comm, task_pid_nr(p), dead_cpu, p->state, p->flags);
-       } while_each_thread(g, p);
-       read_unlock_irq(&tasklist_lock);
+       }
+       read_unlock(&tasklist_lock);
 }
 
 struct take_cpu_down_param {
@@ -344,7 +331,7 @@ static int take_cpu_down(void *_param)
        /* Give up timekeeping duties */
        tick_handover_do_timer();
        /* Park the stopper thread */
-       kthread_park(current);
+       stop_machine_park((long)param->hcpu);
        return 0;
 }
 
index b11756f9b6dcfdf2673b2a396ac0e0de5c980101..39db20c6248e47c940bd8721c41ade530e1eb5c9 100644 (file)
@@ -196,7 +196,7 @@ static int perf_sample_period_ns __read_mostly      = DEFAULT_SAMPLE_PERIOD_NS;
 static int perf_sample_allowed_ns __read_mostly =
        DEFAULT_SAMPLE_PERIOD_NS * DEFAULT_CPU_TIME_MAX_PERCENT / 100;
 
-void update_perf_cpu_limits(void)
+static void update_perf_cpu_limits(void)
 {
        u64 tmp = perf_sample_period_ns;
 
@@ -472,7 +472,7 @@ perf_cgroup_set_timestamp(struct task_struct *task,
  * mode SWOUT : schedule out everything
  * mode SWIN : schedule in based on cgroup for next
  */
-void perf_cgroup_switch(struct task_struct *task, int mode)
+static void perf_cgroup_switch(struct task_struct *task, int mode)
 {
        struct perf_cpu_context *cpuctx;
        struct pmu *pmu;
@@ -1939,7 +1939,7 @@ group_sched_in(struct perf_event *group_event,
        if (group_event->state == PERF_EVENT_STATE_OFF)
                return 0;
 
-       pmu->start_txn(pmu);
+       pmu->start_txn(pmu, PERF_PMU_TXN_ADD);
 
        if (event_sched_in(group_event, cpuctx, ctx)) {
                pmu->cancel_txn(pmu);
@@ -3209,14 +3209,22 @@ void perf_event_exec(void)
        rcu_read_unlock();
 }
 
+struct perf_read_data {
+       struct perf_event *event;
+       bool group;
+       int ret;
+};
+
 /*
  * Cross CPU call to read the hardware event
  */
 static void __perf_event_read(void *info)
 {
-       struct perf_event *event = info;
+       struct perf_read_data *data = info;
+       struct perf_event *sub, *event = data->event;
        struct perf_event_context *ctx = event->ctx;
        struct perf_cpu_context *cpuctx = __get_cpu_context(ctx);
+       struct pmu *pmu = event->pmu;
 
        /*
         * If this is a task context, we need to check whether it is
@@ -3233,9 +3241,35 @@ static void __perf_event_read(void *info)
                update_context_time(ctx);
                update_cgrp_time_from_event(event);
        }
+
        update_event_times(event);
-       if (event->state == PERF_EVENT_STATE_ACTIVE)
-               event->pmu->read(event);
+       if (event->state != PERF_EVENT_STATE_ACTIVE)
+               goto unlock;
+
+       if (!data->group) {
+               pmu->read(event);
+               data->ret = 0;
+               goto unlock;
+       }
+
+       pmu->start_txn(pmu, PERF_PMU_TXN_READ);
+
+       pmu->read(event);
+
+       list_for_each_entry(sub, &event->sibling_list, group_entry) {
+               update_event_times(sub);
+               if (sub->state == PERF_EVENT_STATE_ACTIVE) {
+                       /*
+                        * Use sibling's PMU rather than @event's since
+                        * sibling could be on different (eg: software) PMU.
+                        */
+                       sub->pmu->read(sub);
+               }
+       }
+
+       data->ret = pmu->commit_txn(pmu);
+
+unlock:
        raw_spin_unlock(&ctx->lock);
 }
 
@@ -3300,15 +3334,23 @@ u64 perf_event_read_local(struct perf_event *event)
        return val;
 }
 
-static u64 perf_event_read(struct perf_event *event)
+static int perf_event_read(struct perf_event *event, bool group)
 {
+       int ret = 0;
+
        /*
         * If event is enabled and currently active on a CPU, update the
         * value in the event structure:
         */
        if (event->state == PERF_EVENT_STATE_ACTIVE) {
+               struct perf_read_data data = {
+                       .event = event,
+                       .group = group,
+                       .ret = 0,
+               };
                smp_call_function_single(event->oncpu,
-                                        __perf_event_read, event, 1);
+                                        __perf_event_read, &data, 1);
+               ret = data.ret;
        } else if (event->state == PERF_EVENT_STATE_INACTIVE) {
                struct perf_event_context *ctx = event->ctx;
                unsigned long flags;
@@ -3323,11 +3365,14 @@ static u64 perf_event_read(struct perf_event *event)
                        update_context_time(ctx);
                        update_cgrp_time_from_event(event);
                }
-               update_event_times(event);
+               if (group)
+                       update_group_times(event);
+               else
+                       update_event_times(event);
                raw_spin_unlock_irqrestore(&ctx->lock, flags);
        }
 
-       return perf_event_count(event);
+       return ret;
 }
 
 /*
@@ -3769,7 +3814,7 @@ static void put_event(struct perf_event *event)
         *     see the comment there.
         *
         *  2) there is a lock-inversion with mmap_sem through
-        *     perf_event_read_group(), which takes faults while
+        *     perf_read_group(), which takes faults while
         *     holding ctx->mutex, however this is called after
         *     the last filedesc died, so there is no possibility
         *     to trigger the AB-BA case.
@@ -3843,14 +3888,18 @@ u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running)
        *running = 0;
 
        mutex_lock(&event->child_mutex);
-       total += perf_event_read(event);
+
+       (void)perf_event_read(event, false);
+       total += perf_event_count(event);
+
        *enabled += event->total_time_enabled +
                        atomic64_read(&event->child_total_time_enabled);
        *running += event->total_time_running +
                        atomic64_read(&event->child_total_time_running);
 
        list_for_each_entry(child, &event->child_list, child_list) {
-               total += perf_event_read(child);
+               (void)perf_event_read(child, false);
+               total += perf_event_count(child);
                *enabled += child->total_time_enabled;
                *running += child->total_time_running;
        }
@@ -3860,55 +3909,95 @@ u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running)
 }
 EXPORT_SYMBOL_GPL(perf_event_read_value);
 
-static int perf_event_read_group(struct perf_event *event,
-                                  u64 read_format, char __user *buf)
+static int __perf_read_group_add(struct perf_event *leader,
+                                       u64 read_format, u64 *values)
 {
-       struct perf_event *leader = event->group_leader, *sub;
-       struct perf_event_context *ctx = leader->ctx;
-       int n = 0, size = 0, ret;
-       u64 count, enabled, running;
-       u64 values[5];
+       struct perf_event *sub;
+       int n = 1; /* skip @nr */
+       int ret;
 
-       lockdep_assert_held(&ctx->mutex);
+       ret = perf_event_read(leader, true);
+       if (ret)
+               return ret;
 
-       count = perf_event_read_value(leader, &enabled, &running);
+       /*
+        * Since we co-schedule groups, {enabled,running} times of siblings
+        * will be identical to those of the leader, so we only publish one
+        * set.
+        */
+       if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
+               values[n++] += leader->total_time_enabled +
+                       atomic64_read(&leader->child_total_time_enabled);
+       }
 
-       values[n++] = 1 + leader->nr_siblings;
-       if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
-               values[n++] = enabled;
-       if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
-               values[n++] = running;
-       values[n++] = count;
+       if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
+               values[n++] += leader->total_time_running +
+                       atomic64_read(&leader->child_total_time_running);
+       }
+
+       /*
+        * Write {count,id} tuples for every sibling.
+        */
+       values[n++] += perf_event_count(leader);
        if (read_format & PERF_FORMAT_ID)
                values[n++] = primary_event_id(leader);
 
-       size = n * sizeof(u64);
+       list_for_each_entry(sub, &leader->sibling_list, group_entry) {
+               values[n++] += perf_event_count(sub);
+               if (read_format & PERF_FORMAT_ID)
+                       values[n++] = primary_event_id(sub);
+       }
 
-       if (copy_to_user(buf, values, size))
-               return -EFAULT;
+       return 0;
+}
 
-       ret = size;
+static int perf_read_group(struct perf_event *event,
+                                  u64 read_format, char __user *buf)
+{
+       struct perf_event *leader = event->group_leader, *child;
+       struct perf_event_context *ctx = leader->ctx;
+       int ret;
+       u64 *values;
 
-       list_for_each_entry(sub, &leader->sibling_list, group_entry) {
-               n = 0;
+       lockdep_assert_held(&ctx->mutex);
 
-               values[n++] = perf_event_read_value(sub, &enabled, &running);
-               if (read_format & PERF_FORMAT_ID)
-                       values[n++] = primary_event_id(sub);
+       values = kzalloc(event->read_size, GFP_KERNEL);
+       if (!values)
+               return -ENOMEM;
 
-               size = n * sizeof(u64);
+       values[0] = 1 + leader->nr_siblings;
 
-               if (copy_to_user(buf + ret, values, size)) {
-                       return -EFAULT;
-               }
+       /*
+        * By locking the child_mutex of the leader we effectively
+        * lock the child list of all siblings.. XXX explain how.
+        */
+       mutex_lock(&leader->child_mutex);
+
+       ret = __perf_read_group_add(leader, read_format, values);
+       if (ret)
+               goto unlock;
 
-               ret += size;
+       list_for_each_entry(child, &leader->child_list, child_list) {
+               ret = __perf_read_group_add(child, read_format, values);
+               if (ret)
+                       goto unlock;
        }
 
+       mutex_unlock(&leader->child_mutex);
+
+       ret = event->read_size;
+       if (copy_to_user(buf, values, event->read_size))
+               ret = -EFAULT;
+       goto out;
+
+unlock:
+       mutex_unlock(&leader->child_mutex);
+out:
+       kfree(values);
        return ret;
 }
 
-static int perf_event_read_one(struct perf_event *event,
+static int perf_read_one(struct perf_event *event,
                                 u64 read_format, char __user *buf)
 {
        u64 enabled, running;
@@ -3946,7 +4035,7 @@ static bool is_event_hup(struct perf_event *event)
  * Read the performance event - simple non blocking version for now
  */
 static ssize_t
-perf_read_hw(struct perf_event *event, char __user *buf, size_t count)
+__perf_read(struct perf_event *event, char __user *buf, size_t count)
 {
        u64 read_format = event->attr.read_format;
        int ret;
@@ -3964,9 +4053,9 @@ perf_read_hw(struct perf_event *event, char __user *buf, size_t count)
 
        WARN_ON_ONCE(event->ctx->parent_ctx);
        if (read_format & PERF_FORMAT_GROUP)
-               ret = perf_event_read_group(event, read_format, buf);
+               ret = perf_read_group(event, read_format, buf);
        else
-               ret = perf_event_read_one(event, read_format, buf);
+               ret = perf_read_one(event, read_format, buf);
 
        return ret;
 }
@@ -3979,7 +4068,7 @@ perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
        int ret;
 
        ctx = perf_event_ctx_lock(event);
-       ret = perf_read_hw(event, buf, count);
+       ret = __perf_read(event, buf, count);
        perf_event_ctx_unlock(event, ctx);
 
        return ret;
@@ -4010,7 +4099,7 @@ static unsigned int perf_poll(struct file *file, poll_table *wait)
 
 static void _perf_event_reset(struct perf_event *event)
 {
-       (void)perf_event_read(event);
+       (void)perf_event_read(event, false);
        local64_set(&event->count, 0);
        perf_event_update_userpage(event);
 }
@@ -5286,9 +5375,15 @@ void perf_output_sample(struct perf_output_handle *handle,
 
        if (sample_type & PERF_SAMPLE_RAW) {
                if (data->raw) {
-                       perf_output_put(handle, data->raw->size);
-                       __output_copy(handle, data->raw->data,
-                                          data->raw->size);
+                       u32 raw_size = data->raw->size;
+                       u32 real_size = round_up(raw_size + sizeof(u32),
+                                                sizeof(u64)) - sizeof(u32);
+                       u64 zero = 0;
+
+                       perf_output_put(handle, real_size);
+                       __output_copy(handle, data->raw->data, raw_size);
+                       if (real_size - raw_size)
+                               __output_copy(handle, &zero, real_size - raw_size);
                } else {
                        struct {
                                u32     size;
@@ -5420,8 +5515,7 @@ void perf_prepare_sample(struct perf_event_header *header,
                else
                        size += sizeof(u32);
 
-               WARN_ON_ONCE(size & (sizeof(u64)-1));
-               header->size += size;
+               header->size += round_up(size, sizeof(u64));
        }
 
        if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
@@ -7292,24 +7386,49 @@ static void perf_pmu_nop_void(struct pmu *pmu)
 {
 }
 
+static void perf_pmu_nop_txn(struct pmu *pmu, unsigned int flags)
+{
+}
+
 static int perf_pmu_nop_int(struct pmu *pmu)
 {
        return 0;
 }
 
-static void perf_pmu_start_txn(struct pmu *pmu)
+static DEFINE_PER_CPU(unsigned int, nop_txn_flags);
+
+static void perf_pmu_start_txn(struct pmu *pmu, unsigned int flags)
 {
+       __this_cpu_write(nop_txn_flags, flags);
+
+       if (flags & ~PERF_PMU_TXN_ADD)
+               return;
+
        perf_pmu_disable(pmu);
 }
 
 static int perf_pmu_commit_txn(struct pmu *pmu)
 {
+       unsigned int flags = __this_cpu_read(nop_txn_flags);
+
+       __this_cpu_write(nop_txn_flags, 0);
+
+       if (flags & ~PERF_PMU_TXN_ADD)
+               return 0;
+
        perf_pmu_enable(pmu);
        return 0;
 }
 
 static void perf_pmu_cancel_txn(struct pmu *pmu)
 {
+       unsigned int flags =  __this_cpu_read(nop_txn_flags);
+
+       __this_cpu_write(nop_txn_flags, 0);
+
+       if (flags & ~PERF_PMU_TXN_ADD)
+               return;
+
        perf_pmu_enable(pmu);
 }
 
@@ -7548,7 +7667,7 @@ got_cpu_context:
                        pmu->commit_txn = perf_pmu_commit_txn;
                        pmu->cancel_txn = perf_pmu_cancel_txn;
                } else {
-                       pmu->start_txn  = perf_pmu_nop_void;
+                       pmu->start_txn  = perf_pmu_nop_txn;
                        pmu->commit_txn = perf_pmu_nop_int;
                        pmu->cancel_txn = perf_pmu_nop_void;
                }
@@ -7636,7 +7755,7 @@ static int perf_try_init_event(struct pmu *pmu, struct perf_event *event)
        return ret;
 }
 
-struct pmu *perf_init_event(struct perf_event *event)
+static struct pmu *perf_init_event(struct perf_event *event)
 {
        struct pmu *pmu = NULL;
        int idx;
@@ -9345,14 +9464,6 @@ static void perf_cgroup_exit(struct cgroup_subsys_state *css,
                             struct cgroup_subsys_state *old_css,
                             struct task_struct *task)
 {
-       /*
-        * cgroup_exit() is called in the copy_process() failure path.
-        * Ignore this case since the task hasn't ran yet, this avoids
-        * trying to poke a half freed task state from generic code.
-        */
-       if (!(task->flags & PF_EXITING))
-               return;
-
        task_function_call(task, __perf_cgroup_move, task);
 }
 
index 182bc30899d5b095d6e317327065eb7f8409795d..b5d1ea79c5953e2a2c7a5ee843de5f76ad47077a 100644 (file)
@@ -141,7 +141,7 @@ int perf_output_begin(struct perf_output_handle *handle,
        perf_output_get_handle(handle);
 
        do {
-               tail = READ_ONCE_CTRL(rb->user_page->data_tail);
+               tail = READ_ONCE(rb->user_page->data_tail);
                offset = head = local_read(&rb->head);
                if (!rb->overwrite &&
                    unlikely(CIRC_SPACE(head, tail, perf_data_size(rb)) < size))
index ea95ee1b5ef7038ec1ade3f5cd5d98181177f22f..07110c6020a04ea37c04bc18bd0b9287cd0466dc 100644 (file)
@@ -706,10 +706,12 @@ void do_exit(long code)
        smp_mb();
        raw_spin_unlock_wait(&tsk->pi_lock);
 
-       if (unlikely(in_atomic()))
+       if (unlikely(in_atomic())) {
                pr_info("note: %s[%d] exited with preempt_count %d\n",
                        current->comm, task_pid_nr(current),
                        preempt_count());
+               preempt_count_set(PREEMPT_ENABLED);
+       }
 
        /* sync mm's RSS info before statistics gathering */
        if (tsk->mm)
@@ -761,7 +763,9 @@ void do_exit(long code)
         */
        flush_ptrace_hw_breakpoint(tsk);
 
+       TASKS_RCU(preempt_disable());
        TASKS_RCU(tasks_rcu_i = __srcu_read_lock(&tasks_rcu_exit_srcu));
+       TASKS_RCU(preempt_enable());
        exit_notify(tsk, group_dead);
        proc_exit_connector(tsk);
 #ifdef CONFIG_NUMA
index 2845623fb58264eec28a8b99b48d4511856e718d..6ac894244d3978fb800f7a1a02912bb2901e5e84 100644 (file)
@@ -1101,7 +1101,7 @@ static void posix_cpu_timers_init_group(struct signal_struct *sig)
        cpu_limit = READ_ONCE(sig->rlim[RLIMIT_CPU].rlim_cur);
        if (cpu_limit != RLIM_INFINITY) {
                sig->cputime_expires.prof_exp = secs_to_cputime(cpu_limit);
-               sig->cputimer.running = 1;
+               sig->cputimer.running = true;
        }
 
        /* The timer lists. */
index 6e443efc65f41e8cd1d0bdf2d31afc63207390c1..dfc86e93c31d4a3b8d5e9b0f70ae1c3720411f25 100644 (file)
@@ -255,9 +255,18 @@ struct futex_hash_bucket {
        struct plist_head chain;
 } ____cacheline_aligned_in_smp;
 
-static unsigned long __read_mostly futex_hashsize;
+/*
+ * The base of the bucket array and its size are always used together
+ * (after initialization only in hash_futex()), so ensure that they
+ * reside in the same cacheline.
+ */
+static struct {
+       struct futex_hash_bucket *queues;
+       unsigned long            hashsize;
+} __futex_data __read_mostly __aligned(2*sizeof(long));
+#define futex_queues   (__futex_data.queues)
+#define futex_hashsize (__futex_data.hashsize)
 
-static struct futex_hash_bucket *futex_queues;
 
 /*
  * Fault injections for futexes.
index 9a76e3beda5423b7743f1d67a0945755b771c977..3b48dab801648dc1b7ef9962b2dd8d8067d9adf8 100644 (file)
@@ -30,6 +30,10 @@ config GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
 config GENERIC_PENDING_IRQ
        bool
 
+# Support for generic irq migrating off cpu before the cpu is offline.
+config GENERIC_IRQ_MIGRATION
+       bool
+
 # Alpha specific irq affinity mechanism
 config AUTO_IRQ_AFFINITY
        bool
index d12123526e2b48b07dbf63274aa03f31dcd215e8..2fc9cbdf35b6221385ab1f8393098523cc4c43cd 100644 (file)
@@ -5,5 +5,6 @@ obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
 obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o
+obj-$(CONFIG_GENERIC_IRQ_MIGRATION) += cpuhotplug.o
 obj-$(CONFIG_PM_SLEEP) += pm.o
 obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o
index e28169dd1c36a51f92523208984ec4c7a4b5b251..15206453b12aab09cf96dd09cc3fa2da92fd9c14 100644 (file)
 
 #include "internals.h"
 
+static irqreturn_t bad_chained_irq(int irq, void *dev_id)
+{
+       WARN_ONCE(1, "Chained irq %d should not call an action\n", irq);
+       return IRQ_NONE;
+}
+
+/*
+ * Chained handlers should never call action on their IRQ. This default
+ * action will emit warning if such thing happens.
+ */
+struct irqaction chained_action = {
+       .handler = bad_chained_irq,
+};
+
 /**
  *     irq_set_chip - set the irq chip for an irq
  *     @irq:   irq number
@@ -227,6 +241,13 @@ void irq_enable(struct irq_desc *desc)
  * disabled. If an interrupt happens, then the interrupt flow
  * handler masks the line at the hardware level and marks it
  * pending.
+ *
+ * If the interrupt chip does not implement the irq_disable callback,
+ * a driver can disable the lazy approach for a particular irq line by
+ * calling 'irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY)'. This can
+ * be used for devices which cannot disable the interrupt at the
+ * device level under certain circumstances and have to use
+ * disable_irq[_nosync] instead.
  */
 void irq_disable(struct irq_desc *desc)
 {
@@ -234,6 +255,8 @@ void irq_disable(struct irq_desc *desc)
        if (desc->irq_data.chip->irq_disable) {
                desc->irq_data.chip->irq_disable(&desc->irq_data);
                irq_state_set_masked(desc);
+       } else if (irq_settings_disable_unlazy(desc)) {
+               mask_irq(desc);
        }
 }
 
@@ -669,7 +692,7 @@ void handle_percpu_irq(struct irq_desc *desc)
        if (chip->irq_ack)
                chip->irq_ack(&desc->irq_data);
 
-       handle_irq_event_percpu(desc, desc->action);
+       handle_irq_event_percpu(desc);
 
        if (chip->irq_eoi)
                chip->irq_eoi(&desc->irq_data);
@@ -746,6 +769,8 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
                if (desc->irq_data.chip != &no_irq_chip)
                        mask_ack_irq(desc);
                irq_state_set_disabled(desc);
+               if (is_chained)
+                       desc->action = NULL;
                desc->depth = 1;
        }
        desc->handle_irq = handle;
@@ -755,6 +780,7 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
                irq_settings_set_noprobe(desc);
                irq_settings_set_norequest(desc);
                irq_settings_set_nothread(desc);
+               desc->action = &chained_action;
                irq_startup(desc, true);
        }
 }
diff --git a/kernel/irq/cpuhotplug.c b/kernel/irq/cpuhotplug.c
new file mode 100644 (file)
index 0000000..80f4f4e
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Generic cpu hotunplug interrupt migration code copied from the
+ * arch/arm implementation
+ *
+ * Copyright (C) Russell King
+ *
+ * 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/interrupt.h>
+#include <linux/ratelimit.h>
+#include <linux/irq.h>
+
+#include "internals.h"
+
+static bool migrate_one_irq(struct irq_desc *desc)
+{
+       struct irq_data *d = irq_desc_get_irq_data(desc);
+       const struct cpumask *affinity = d->common->affinity;
+       struct irq_chip *c;
+       bool ret = false;
+
+       /*
+        * If this is a per-CPU interrupt, or the affinity does not
+        * include this CPU, then we have nothing to do.
+        */
+       if (irqd_is_per_cpu(d) ||
+           !cpumask_test_cpu(smp_processor_id(), affinity))
+               return false;
+
+       if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
+               affinity = cpu_online_mask;
+               ret = true;
+       }
+
+       c = irq_data_get_irq_chip(d);
+       if (!c->irq_set_affinity) {
+               pr_warn_ratelimited("IRQ%u: unable to set affinity\n", d->irq);
+       } else {
+               int r = irq_do_set_affinity(d, affinity, false);
+               if (r)
+                       pr_warn_ratelimited("IRQ%u: set affinity failed(%d).\n",
+                                           d->irq, r);
+       }
+
+       return ret;
+}
+
+/**
+ * irq_migrate_all_off_this_cpu - Migrate irqs away from offline cpu
+ *
+ * The current CPU has been marked offline.  Migrate IRQs off this CPU.
+ * If the affinity settings do not allow other CPUs, force them onto any
+ * available CPU.
+ *
+ * Note: we must iterate over all IRQs, whether they have an attached
+ * action structure or not, as we need to get chained interrupts too.
+ */
+void irq_migrate_all_off_this_cpu(void)
+{
+       unsigned int irq;
+       struct irq_desc *desc;
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+       for_each_active_irq(irq) {
+               bool affinity_broken;
+
+               desc = irq_to_desc(irq);
+               raw_spin_lock(&desc->lock);
+               affinity_broken = migrate_one_irq(desc);
+               raw_spin_unlock(&desc->lock);
+
+               if (affinity_broken)
+                       pr_warn_ratelimited("IRQ%u no longer affine to CPU%u\n",
+                                           irq, smp_processor_id());
+       }
+
+       local_irq_restore(flags);
+}
index e25a83b67ccea18568f932636b72bd0e9ba182d0..a302cf9a2126c8a911ff4da1a944c88c6d3b3f8a 100644 (file)
@@ -132,11 +132,11 @@ void __irq_wake_thread(struct irq_desc *desc, struct irqaction *action)
        wake_up_process(action->thread);
 }
 
-irqreturn_t
-handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
+irqreturn_t handle_irq_event_percpu(struct irq_desc *desc)
 {
        irqreturn_t retval = IRQ_NONE;
        unsigned int flags = 0, irq = desc->irq_data.irq;
+       struct irqaction *action = desc->action;
 
        do {
                irqreturn_t res;
@@ -184,14 +184,13 @@ handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
 
 irqreturn_t handle_irq_event(struct irq_desc *desc)
 {
-       struct irqaction *action = desc->action;
        irqreturn_t ret;
 
        desc->istate &= ~IRQS_PENDING;
        irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
        raw_spin_unlock(&desc->lock);
 
-       ret = handle_irq_event_percpu(desc, action);
+       ret = handle_irq_event_percpu(desc);
 
        raw_spin_lock(&desc->lock);
        irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
index 5ef0c2dbe9302a846e724d5106c01a8eec248fda..05c2188271b86730323c9bce053adc2d16e8d5a3 100644 (file)
@@ -18,6 +18,8 @@
 
 extern bool noirqdebug;
 
+extern struct irqaction chained_action;
+
 /*
  * Bits used by threaded handlers:
  * IRQTF_RUNTHREAD - signals that the interrupt handler thread should run
@@ -81,7 +83,7 @@ extern void irq_mark_irq(unsigned int irq);
 
 extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr);
 
-irqreturn_t handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action);
+irqreturn_t handle_irq_event_percpu(struct irq_desc *desc);
 irqreturn_t handle_irq_event(struct irq_desc *desc);
 
 /* Resending of interrupts :*/
index dc9d27c0c1589e58bb14a15e3442ed2ff0d091a4..22aa9612ef7ca98cd8796188a7021810cc86d05a 100644 (file)
@@ -27,6 +27,57 @@ static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
                                  irq_hw_number_t hwirq, int node);
 static void irq_domain_check_hierarchy(struct irq_domain *domain);
 
+struct irqchip_fwid {
+       struct fwnode_handle fwnode;
+       char *name;
+       void *data;
+};
+
+/**
+ * irq_domain_alloc_fwnode - Allocate a fwnode_handle suitable for
+ *                           identifying an irq domain
+ * @data: optional user-provided data
+ *
+ * Allocate a struct device_node, and return a poiner to the embedded
+ * fwnode_handle (or NULL on failure).
+ */
+struct fwnode_handle *irq_domain_alloc_fwnode(void *data)
+{
+       struct irqchip_fwid *fwid;
+       char *name;
+
+       fwid = kzalloc(sizeof(*fwid), GFP_KERNEL);
+       name = kasprintf(GFP_KERNEL, "irqchip@%p", data);
+
+       if (!fwid || !name) {
+               kfree(fwid);
+               kfree(name);
+               return NULL;
+       }
+
+       fwid->name = name;
+       fwid->data = data;
+       fwid->fwnode.type = FWNODE_IRQCHIP;
+       return &fwid->fwnode;
+}
+
+/**
+ * irq_domain_free_fwnode - Free a non-OF-backed fwnode_handle
+ *
+ * Free a fwnode_handle allocated with irq_domain_alloc_fwnode.
+ */
+void irq_domain_free_fwnode(struct fwnode_handle *fwnode)
+{
+       struct irqchip_fwid *fwid;
+
+       if (WARN_ON(fwnode->type != FWNODE_IRQCHIP))
+               return;
+
+       fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
+       kfree(fwid->name);
+       kfree(fwid);
+}
+
 /**
  * __irq_domain_add() - Allocate a new irq_domain data structure
  * @of_node: optional device-tree node of the interrupt controller
@@ -40,23 +91,28 @@ static void irq_domain_check_hierarchy(struct irq_domain *domain);
  * Allocates and initialize and irq_domain structure.
  * Returns pointer to IRQ domain, or NULL on failure.
  */
-struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
+struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
                                    irq_hw_number_t hwirq_max, int direct_max,
                                    const struct irq_domain_ops *ops,
                                    void *host_data)
 {
        struct irq_domain *domain;
+       struct device_node *of_node;
+
+       of_node = to_of_node(fwnode);
 
        domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
                              GFP_KERNEL, of_node_to_nid(of_node));
        if (WARN_ON(!domain))
                return NULL;
 
+       of_node_get(of_node);
+
        /* Fill structure */
        INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
        domain->ops = ops;
        domain->host_data = host_data;
-       domain->of_node = of_node_get(of_node);
+       domain->fwnode = fwnode;
        domain->hwirq_max = hwirq_max;
        domain->revmap_size = size;
        domain->revmap_direct_max_irq = direct_max;
@@ -102,7 +158,7 @@ void irq_domain_remove(struct irq_domain *domain)
 
        pr_debug("Removed domain %s\n", domain->name);
 
-       of_node_put(domain->of_node);
+       of_node_put(irq_domain_get_of_node(domain));
        kfree(domain);
 }
 EXPORT_SYMBOL_GPL(irq_domain_remove);
@@ -133,7 +189,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
 {
        struct irq_domain *domain;
 
-       domain = __irq_domain_add(of_node, size, size, 0, ops, host_data);
+       domain = __irq_domain_add(of_node_to_fwnode(of_node), size, size, 0, ops, host_data);
        if (!domain)
                return NULL;
 
@@ -177,7 +233,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
 {
        struct irq_domain *domain;
 
-       domain = __irq_domain_add(of_node, first_hwirq + size,
+       domain = __irq_domain_add(of_node_to_fwnode(of_node), first_hwirq + size,
                                  first_hwirq + size, 0, ops, host_data);
        if (domain)
                irq_domain_associate_many(domain, first_irq, first_hwirq, size);
@@ -187,12 +243,12 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
 EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
 
 /**
- * irq_find_matching_host() - Locates a domain for a given device node
- * @node: device-tree node of the interrupt controller
+ * irq_find_matching_fwnode() - Locates a domain for a given fwnode
+ * @fwnode: FW descriptor of the interrupt controller
  * @bus_token: domain-specific data
  */
-struct irq_domain *irq_find_matching_host(struct device_node *node,
-                                         enum irq_domain_bus_token bus_token)
+struct irq_domain *irq_find_matching_fwnode(struct fwnode_handle *fwnode,
+                                           enum irq_domain_bus_token bus_token)
 {
        struct irq_domain *h, *found = NULL;
        int rc;
@@ -209,9 +265,9 @@ struct irq_domain *irq_find_matching_host(struct device_node *node,
        mutex_lock(&irq_domain_mutex);
        list_for_each_entry(h, &irq_domain_list, link) {
                if (h->ops->match)
-                       rc = h->ops->match(h, node, bus_token);
+                       rc = h->ops->match(h, to_of_node(fwnode), bus_token);
                else
-                       rc = ((h->of_node != NULL) && (h->of_node == node) &&
+                       rc = ((fwnode != NULL) && (h->fwnode == fwnode) &&
                              ((bus_token == DOMAIN_BUS_ANY) ||
                               (h->bus_token == bus_token)));
 
@@ -223,7 +279,7 @@ struct irq_domain *irq_find_matching_host(struct device_node *node,
        mutex_unlock(&irq_domain_mutex);
        return found;
 }
-EXPORT_SYMBOL_GPL(irq_find_matching_host);
+EXPORT_SYMBOL_GPL(irq_find_matching_fwnode);
 
 /**
  * irq_set_default_host() - Set a "default" irq domain
@@ -336,10 +392,12 @@ EXPORT_SYMBOL_GPL(irq_domain_associate);
 void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
                               irq_hw_number_t hwirq_base, int count)
 {
+       struct device_node *of_node;
        int i;
 
+       of_node = irq_domain_get_of_node(domain);
        pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
-               of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
+               of_node_full_name(of_node), irq_base, (int)hwirq_base, count);
 
        for (i = 0; i < count; i++) {
                irq_domain_associate(domain, irq_base + i, hwirq_base + i);
@@ -359,12 +417,14 @@ EXPORT_SYMBOL_GPL(irq_domain_associate_many);
  */
 unsigned int irq_create_direct_mapping(struct irq_domain *domain)
 {
+       struct device_node *of_node;
        unsigned int virq;
 
        if (domain == NULL)
                domain = irq_default_domain;
 
-       virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
+       of_node = irq_domain_get_of_node(domain);
+       virq = irq_alloc_desc_from(1, of_node_to_nid(of_node));
        if (!virq) {
                pr_debug("create_direct virq allocation failed\n");
                return 0;
@@ -399,6 +459,7 @@ EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
 unsigned int irq_create_mapping(struct irq_domain *domain,
                                irq_hw_number_t hwirq)
 {
+       struct device_node *of_node;
        int virq;
 
        pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
@@ -412,6 +473,8 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
        }
        pr_debug("-> using domain @%p\n", domain);
 
+       of_node = irq_domain_get_of_node(domain);
+
        /* Check if mapping already exists */
        virq = irq_find_mapping(domain, hwirq);
        if (virq) {
@@ -420,8 +483,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
        }
 
        /* Allocate a virtual interrupt number */
-       virq = irq_domain_alloc_descs(-1, 1, hwirq,
-                                     of_node_to_nid(domain->of_node));
+       virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node));
        if (virq <= 0) {
                pr_debug("-> virq allocation failed\n");
                return 0;
@@ -433,7 +495,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
        }
 
        pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
-               hwirq, of_node_full_name(domain->of_node), virq);
+               hwirq, of_node_full_name(of_node), virq);
 
        return virq;
 }
@@ -460,10 +522,12 @@ EXPORT_SYMBOL_GPL(irq_create_mapping);
 int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
                               irq_hw_number_t hwirq_base, int count)
 {
+       struct device_node *of_node;
        int ret;
 
+       of_node = irq_domain_get_of_node(domain);
        ret = irq_alloc_descs(irq_base, irq_base, count,
-                             of_node_to_nid(domain->of_node));
+                             of_node_to_nid(of_node));
        if (unlikely(ret < 0))
                return ret;
 
@@ -472,28 +536,56 @@ int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
 }
 EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
 
-unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
+static int irq_domain_translate(struct irq_domain *d,
+                               struct irq_fwspec *fwspec,
+                               irq_hw_number_t *hwirq, unsigned int *type)
+{
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+       if (d->ops->translate)
+               return d->ops->translate(d, fwspec, hwirq, type);
+#endif
+       if (d->ops->xlate)
+               return d->ops->xlate(d, to_of_node(fwspec->fwnode),
+                                    fwspec->param, fwspec->param_count,
+                                    hwirq, type);
+
+       /* If domain has no translation, then we assume interrupt line */
+       *hwirq = fwspec->param[0];
+       return 0;
+}
+
+static void of_phandle_args_to_fwspec(struct of_phandle_args *irq_data,
+                                     struct irq_fwspec *fwspec)
+{
+       int i;
+
+       fwspec->fwnode = irq_data->np ? &irq_data->np->fwnode : NULL;
+       fwspec->param_count = irq_data->args_count;
+
+       for (i = 0; i < irq_data->args_count; i++)
+               fwspec->param[i] = irq_data->args[i];
+}
+
+unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
 {
        struct irq_domain *domain;
        irq_hw_number_t hwirq;
        unsigned int type = IRQ_TYPE_NONE;
        int virq;
 
-       domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
+       if (fwspec->fwnode)
+               domain = irq_find_matching_fwnode(fwspec->fwnode, DOMAIN_BUS_ANY);
+       else
+               domain = irq_default_domain;
+
        if (!domain) {
                pr_warn("no irq domain found for %s !\n",
-                       of_node_full_name(irq_data->np));
+                       of_node_full_name(to_of_node(fwspec->fwnode)));
                return 0;
        }
 
-       /* If domain has no translation, then we assume interrupt line */
-       if (domain->ops->xlate == NULL)
-               hwirq = irq_data->args[0];
-       else {
-               if (domain->ops->xlate(domain, irq_data->np, irq_data->args,
-                                       irq_data->args_count, &hwirq, &type))
-                       return 0;
-       }
+       if (irq_domain_translate(domain, fwspec, &hwirq, &type))
+               return 0;
 
        if (irq_domain_is_hierarchy(domain)) {
                /*
@@ -504,7 +596,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
                if (virq)
                        return virq;
 
-               virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data);
+               virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);
                if (virq <= 0)
                        return 0;
        } else {
@@ -520,6 +612,15 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
                irq_set_irq_type(virq, type);
        return virq;
 }
+EXPORT_SYMBOL_GPL(irq_create_fwspec_mapping);
+
+unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
+{
+       struct irq_fwspec fwspec;
+
+       of_phandle_args_to_fwspec(irq_data, &fwspec);
+       return irq_create_fwspec_mapping(&fwspec);
+}
 EXPORT_SYMBOL_GPL(irq_create_of_mapping);
 
 /**
@@ -590,14 +691,16 @@ static int virq_debug_show(struct seq_file *m, void *private)
                   "name", "mapped", "linear-max", "direct-max", "devtree-node");
        mutex_lock(&irq_domain_mutex);
        list_for_each_entry(domain, &irq_domain_list, link) {
+               struct device_node *of_node;
                int count = 0;
+               of_node = irq_domain_get_of_node(domain);
                radix_tree_for_each_slot(slot, &domain->revmap_tree, &iter, 0)
                        count++;
                seq_printf(m, "%c%-16s  %6u  %10u  %10u  %s\n",
                           domain == irq_default_domain ? '*' : ' ', domain->name,
                           domain->revmap_size + count, domain->revmap_size,
                           domain->revmap_direct_max_irq,
-                          domain->of_node ? of_node_full_name(domain->of_node) : "");
+                          of_node ? of_node_full_name(of_node) : "");
        }
        mutex_unlock(&irq_domain_mutex);
 
@@ -751,11 +854,11 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt,
 
 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
 /**
- * irq_domain_add_hierarchy - Add a irqdomain into the hierarchy
+ * irq_domain_create_hierarchy - Add a irqdomain into the hierarchy
  * @parent:    Parent irq domain to associate with the new domain
  * @flags:     Irq domain flags associated to the domain
  * @size:      Size of the domain. See below
- * @node:      Optional device-tree node of the interrupt controller
+ * @fwnode:    Optional fwnode of the interrupt controller
  * @ops:       Pointer to the interrupt domain callbacks
  * @host_data: Controller private data pointer
  *
@@ -765,19 +868,19 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt,
  * domain flags are set.
  * Returns pointer to IRQ domain, or NULL on failure.
  */
-struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
                                            unsigned int flags,
                                            unsigned int size,
-                                           struct device_node *node,
+                                           struct fwnode_handle *fwnode,
                                            const struct irq_domain_ops *ops,
                                            void *host_data)
 {
        struct irq_domain *domain;
 
        if (size)
-               domain = irq_domain_add_linear(node, size, ops, host_data);
+               domain = irq_domain_create_linear(fwnode, size, ops, host_data);
        else
-               domain = irq_domain_add_tree(node, ops, host_data);
+               domain = irq_domain_create_tree(fwnode, ops, host_data);
        if (domain) {
                domain->parent = parent;
                domain->flags |= flags;
index f9a59f6cabd2d3a96f8cd9bee179b6ab4214b83e..0eebaeef317bc990e3ee1b1bd1719b9e89211685 100644 (file)
@@ -258,37 +258,6 @@ int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m)
 }
 EXPORT_SYMBOL_GPL(irq_set_affinity_hint);
 
-/**
- *     irq_set_vcpu_affinity - Set vcpu affinity for the interrupt
- *     @irq: interrupt number to set affinity
- *     @vcpu_info: vCPU specific data
- *
- *     This function uses the vCPU specific data to set the vCPU
- *     affinity for an irq. The vCPU specific data is passed from
- *     outside, such as KVM. One example code path is as below:
- *     KVM -> IOMMU -> irq_set_vcpu_affinity().
- */
-int irq_set_vcpu_affinity(unsigned int irq, void *vcpu_info)
-{
-       unsigned long flags;
-       struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
-       struct irq_data *data;
-       struct irq_chip *chip;
-       int ret = -ENOSYS;
-
-       if (!desc)
-               return -EINVAL;
-
-       data = irq_desc_get_irq_data(desc);
-       chip = irq_data_get_irq_chip(data);
-       if (chip && chip->irq_set_vcpu_affinity)
-               ret = chip->irq_set_vcpu_affinity(data, vcpu_info);
-       irq_put_desc_unlock(desc, flags);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(irq_set_vcpu_affinity);
-
 static void irq_affinity_notify(struct work_struct *work)
 {
        struct irq_affinity_notify *notify =
@@ -424,6 +393,37 @@ setup_affinity(struct irq_desc *desc, struct cpumask *mask)
 }
 #endif
 
+/**
+ *     irq_set_vcpu_affinity - Set vcpu affinity for the interrupt
+ *     @irq: interrupt number to set affinity
+ *     @vcpu_info: vCPU specific data
+ *
+ *     This function uses the vCPU specific data to set the vCPU
+ *     affinity for an irq. The vCPU specific data is passed from
+ *     outside, such as KVM. One example code path is as below:
+ *     KVM -> IOMMU -> irq_set_vcpu_affinity().
+ */
+int irq_set_vcpu_affinity(unsigned int irq, void *vcpu_info)
+{
+       unsigned long flags;
+       struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
+       struct irq_data *data;
+       struct irq_chip *chip;
+       int ret = -ENOSYS;
+
+       if (!desc)
+               return -EINVAL;
+
+       data = irq_desc_get_irq_data(desc);
+       chip = irq_data_get_irq_chip(data);
+       if (chip && chip->irq_set_vcpu_affinity)
+               ret = chip->irq_set_vcpu_affinity(data, vcpu_info);
+       irq_put_desc_unlock(desc, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(irq_set_vcpu_affinity);
+
 void __disable_irq(struct irq_desc *desc)
 {
        if (!desc->depth++)
@@ -730,6 +730,12 @@ static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id)
        return IRQ_NONE;
 }
 
+static irqreturn_t irq_forced_secondary_handler(int irq, void *dev_id)
+{
+       WARN(1, "Secondary action handler called for irq %d\n", irq);
+       return IRQ_NONE;
+}
+
 static int irq_wait_for_interrupt(struct irqaction *action)
 {
        set_current_state(TASK_INTERRUPTIBLE);
@@ -756,7 +762,8 @@ static int irq_wait_for_interrupt(struct irqaction *action)
 static void irq_finalize_oneshot(struct irq_desc *desc,
                                 struct irqaction *action)
 {
-       if (!(desc->istate & IRQS_ONESHOT))
+       if (!(desc->istate & IRQS_ONESHOT) ||
+           action->handler == irq_forced_secondary_handler)
                return;
 again:
        chip_bus_lock(desc);
@@ -910,6 +917,18 @@ static void irq_thread_dtor(struct callback_head *unused)
        irq_finalize_oneshot(desc, action);
 }
 
+static void irq_wake_secondary(struct irq_desc *desc, struct irqaction *action)
+{
+       struct irqaction *secondary = action->secondary;
+
+       if (WARN_ON_ONCE(!secondary))
+               return;
+
+       raw_spin_lock_irq(&desc->lock);
+       __irq_wake_thread(desc, secondary);
+       raw_spin_unlock_irq(&desc->lock);
+}
+
 /*
  * Interrupt handler thread
  */
@@ -940,6 +959,8 @@ static int irq_thread(void *data)
                action_ret = handler_fn(desc, action);
                if (action_ret == IRQ_HANDLED)
                        atomic_inc(&desc->threads_handled);
+               if (action_ret == IRQ_WAKE_THREAD)
+                       irq_wake_secondary(desc, action);
 
                wake_threads_waitq(desc);
        }
@@ -984,20 +1005,36 @@ void irq_wake_thread(unsigned int irq, void *dev_id)
 }
 EXPORT_SYMBOL_GPL(irq_wake_thread);
 
-static void irq_setup_forced_threading(struct irqaction *new)
+static int irq_setup_forced_threading(struct irqaction *new)
 {
        if (!force_irqthreads)
-               return;
+               return 0;
        if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))
-               return;
+               return 0;
 
        new->flags |= IRQF_ONESHOT;
 
-       if (!new->thread_fn) {
-               set_bit(IRQTF_FORCED_THREAD, &new->thread_flags);
-               new->thread_fn = new->handler;
-               new->handler = irq_default_primary_handler;
+       /*
+        * Handle the case where we have a real primary handler and a
+        * thread handler. We force thread them as well by creating a
+        * secondary action.
+        */
+       if (new->handler != irq_default_primary_handler && new->thread_fn) {
+               /* Allocate the secondary action */
+               new->secondary = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
+               if (!new->secondary)
+                       return -ENOMEM;
+               new->secondary->handler = irq_forced_secondary_handler;
+               new->secondary->thread_fn = new->thread_fn;
+               new->secondary->dev_id = new->dev_id;
+               new->secondary->irq = new->irq;
+               new->secondary->name = new->name;
        }
+       /* Deal with the primary handler */
+       set_bit(IRQTF_FORCED_THREAD, &new->thread_flags);
+       new->thread_fn = new->handler;
+       new->handler = irq_default_primary_handler;
+       return 0;
 }
 
 static int irq_request_resources(struct irq_desc *desc)
@@ -1017,6 +1054,48 @@ static void irq_release_resources(struct irq_desc *desc)
                c->irq_release_resources(d);
 }
 
+static int
+setup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary)
+{
+       struct task_struct *t;
+       struct sched_param param = {
+               .sched_priority = MAX_USER_RT_PRIO/2,
+       };
+
+       if (!secondary) {
+               t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
+                                  new->name);
+       } else {
+               t = kthread_create(irq_thread, new, "irq/%d-s-%s", irq,
+                                  new->name);
+               param.sched_priority -= 1;
+       }
+
+       if (IS_ERR(t))
+               return PTR_ERR(t);
+
+       sched_setscheduler_nocheck(t, SCHED_FIFO, &param);
+
+       /*
+        * We keep the reference to the task struct even if
+        * the thread dies to avoid that the interrupt code
+        * references an already freed task_struct.
+        */
+       get_task_struct(t);
+       new->thread = t;
+       /*
+        * Tell the thread to set its affinity. This is
+        * important for shared interrupt handlers as we do
+        * not invoke setup_affinity() for the secondary
+        * handlers as everything is already set up. Even for
+        * interrupts marked with IRQF_NO_BALANCE this is
+        * correct as we want the thread to move to the cpu(s)
+        * on which the requesting code placed the interrupt.
+        */
+       set_bit(IRQTF_AFFINITY, &new->thread_flags);
+       return 0;
+}
+
 /*
  * Internal function to register an irqaction - typically used to
  * allocate special interrupts that are part of the architecture.
@@ -1037,6 +1116,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
        if (!try_module_get(desc->owner))
                return -ENODEV;
 
+       new->irq = irq;
+
        /*
         * Check whether the interrupt nests into another interrupt
         * thread.
@@ -1054,8 +1135,11 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                 */
                new->handler = irq_nested_primary_handler;
        } else {
-               if (irq_settings_can_thread(desc))
-                       irq_setup_forced_threading(new);
+               if (irq_settings_can_thread(desc)) {
+                       ret = irq_setup_forced_threading(new);
+                       if (ret)
+                               goto out_mput;
+               }
        }
 
        /*
@@ -1064,37 +1148,14 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
         * thread.
         */
        if (new->thread_fn && !nested) {
-               struct task_struct *t;
-               static const struct sched_param param = {
-                       .sched_priority = MAX_USER_RT_PRIO/2,
-               };
-
-               t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
-                                  new->name);
-               if (IS_ERR(t)) {
-                       ret = PTR_ERR(t);
+               ret = setup_irq_thread(new, irq, false);
+               if (ret)
                        goto out_mput;
+               if (new->secondary) {
+                       ret = setup_irq_thread(new->secondary, irq, true);
+                       if (ret)
+                               goto out_thread;
                }
-
-               sched_setscheduler_nocheck(t, SCHED_FIFO, &param);
-
-               /*
-                * We keep the reference to the task struct even if
-                * the thread dies to avoid that the interrupt code
-                * references an already freed task_struct.
-                */
-               get_task_struct(t);
-               new->thread = t;
-               /*
-                * Tell the thread to set its affinity. This is
-                * important for shared interrupt handlers as we do
-                * not invoke setup_affinity() for the secondary
-                * handlers as everything is already set up. Even for
-                * interrupts marked with IRQF_NO_BALANCE this is
-                * correct as we want the thread to move to the cpu(s)
-                * on which the requesting code placed the interrupt.
-                */
-               set_bit(IRQTF_AFFINITY, &new->thread_flags);
        }
 
        if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
@@ -1267,7 +1328,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                                   irq, nmsk, omsk);
        }
 
-       new->irq = irq;
        *old_ptr = new;
 
        irq_pm_install_action(desc, new);
@@ -1293,6 +1353,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
         */
        if (new->thread)
                wake_up_process(new->thread);
+       if (new->secondary)
+               wake_up_process(new->secondary->thread);
 
        register_irq_proc(irq, desc);
        new->dir = NULL;
@@ -1323,6 +1385,13 @@ out_thread:
                kthread_stop(t);
                put_task_struct(t);
        }
+       if (new->secondary && new->secondary->thread) {
+               struct task_struct *t = new->secondary->thread;
+
+               new->secondary->thread = NULL;
+               kthread_stop(t);
+               put_task_struct(t);
+       }
 out_mput:
        module_put(desc->owner);
        return ret;
@@ -1394,6 +1463,7 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
 
        /* If this was the last handler, shut down the IRQ line: */
        if (!desc->action) {
+               irq_settings_clr_disable_unlazy(desc);
                irq_shutdown(desc);
                irq_release_resources(desc);
        }
@@ -1430,9 +1500,14 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
        if (action->thread) {
                kthread_stop(action->thread);
                put_task_struct(action->thread);
+               if (action->secondary && action->secondary->thread) {
+                       kthread_stop(action->secondary->thread);
+                       put_task_struct(action->secondary->thread);
+               }
        }
 
        module_put(desc->owner);
+       kfree(action->secondary);
        return action;
 }
 
@@ -1576,8 +1651,10 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
        retval = __setup_irq(irq, desc, action);
        chip_bus_sync_unlock(desc);
 
-       if (retval)
+       if (retval) {
+               kfree(action->secondary);
                kfree(action);
+       }
 
 #ifdef CONFIG_DEBUG_SHIRQ_FIXME
        if (!retval && (irqflags & IRQF_SHARED)) {
@@ -1761,6 +1838,7 @@ void free_percpu_irq(unsigned int irq, void __percpu *dev_id)
        kfree(__free_percpu_irq(irq, dev_id));
        chip_bus_sync_unlock(desc);
 }
+EXPORT_SYMBOL_GPL(free_percpu_irq);
 
 /**
  *     setup_percpu_irq - setup a per-cpu interrupt
@@ -1790,9 +1868,10 @@ int setup_percpu_irq(unsigned int irq, struct irqaction *act)
  *     @devname: An ascii name for the claiming device
  *     @dev_id: A percpu cookie passed back to the handler function
  *
- *     This call allocates interrupt resources, but doesn't
- *     automatically enable the interrupt. It has to be done on each
- *     CPU using enable_percpu_irq().
+ *     This call allocates interrupt resources and enables the
+ *     interrupt on the local CPU. If the interrupt is supposed to be
+ *     enabled on other CPUs, it has to be done on each CPU using
+ *     enable_percpu_irq().
  *
  *     Dev_id must be globally unique. It is a per-cpu variable, and
  *     the handler gets called with the interrupted CPU's instance of
@@ -1831,6 +1910,7 @@ int request_percpu_irq(unsigned int irq, irq_handler_t handler,
 
        return retval;
 }
+EXPORT_SYMBOL_GPL(request_percpu_irq);
 
 /**
  *     irq_get_irqchip_state - returns the irqchip state of a interrupt.
index be9149f62eb86e63ac06194d4eeaa4065823ea62..6b0c0b74a2a1a88c0d3f81519fea7c520b290967 100644 (file)
@@ -235,11 +235,11 @@ static void msi_domain_update_chip_ops(struct msi_domain_info *info)
 
 /**
  * msi_create_irq_domain - Create a MSI interrupt domain
- * @of_node:   Optional device-tree node of the interrupt controller
+ * @fwnode:    Optional fwnode of the interrupt controller
  * @info:      MSI domain info
  * @parent:    Parent irq domain
  */
-struct irq_domain *msi_create_irq_domain(struct device_node *node,
+struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
                                         struct msi_domain_info *info,
                                         struct irq_domain *parent)
 {
@@ -248,8 +248,8 @@ struct irq_domain *msi_create_irq_domain(struct device_node *node,
        if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
                msi_domain_update_chip_ops(info);
 
-       return irq_domain_add_hierarchy(parent, 0, 0, node, &msi_domain_ops,
-                                       info);
+       return irq_domain_create_hierarchy(parent, 0, 0, fwnode,
+                                          &msi_domain_ops, info);
 }
 
 /**
index a50ddc9417ff5fbdccf837f9f43b716200ceb013..a916cf144b6550caf452fc304a1e848d40246b8a 100644 (file)
@@ -475,7 +475,7 @@ int show_interrupts(struct seq_file *p, void *v)
        for_each_online_cpu(j)
                any_count |= kstat_irqs_cpu(i, j);
        action = desc->action;
-       if (!action && !any_count)
+       if ((!action || action == &chained_action) && !any_count)
                goto out;
 
        seq_printf(p, "%*d: ", prec, i);
index 3320b84cc60f79ac09501d6f4d55acc01b0cebe6..320579d8909100d1ebb181510dd8ce4433ab5456 100644 (file)
@@ -15,6 +15,7 @@ enum {
        _IRQ_NESTED_THREAD      = IRQ_NESTED_THREAD,
        _IRQ_PER_CPU_DEVID      = IRQ_PER_CPU_DEVID,
        _IRQ_IS_POLLED          = IRQ_IS_POLLED,
+       _IRQ_DISABLE_UNLAZY     = IRQ_DISABLE_UNLAZY,
        _IRQF_MODIFY_MASK       = IRQF_MODIFY_MASK,
 };
 
@@ -28,6 +29,7 @@ enum {
 #define IRQ_NESTED_THREAD      GOT_YOU_MORON
 #define IRQ_PER_CPU_DEVID      GOT_YOU_MORON
 #define IRQ_IS_POLLED          GOT_YOU_MORON
+#define IRQ_DISABLE_UNLAZY     GOT_YOU_MORON
 #undef IRQF_MODIFY_MASK
 #define IRQF_MODIFY_MASK       GOT_YOU_MORON
 
@@ -154,3 +156,13 @@ static inline bool irq_settings_is_polled(struct irq_desc *desc)
 {
        return desc->status_use_accessors & _IRQ_IS_POLLED;
 }
+
+static inline bool irq_settings_disable_unlazy(struct irq_desc *desc)
+{
+       return desc->status_use_accessors & _IRQ_DISABLE_UNLAZY;
+}
+
+static inline void irq_settings_clr_disable_unlazy(struct irq_desc *desc)
+{
+       desc->status_use_accessors &= ~_IRQ_DISABLE_UNLAZY;
+}
index 201b45327804d230edc9017eef6c07805ae3f8d3..bd9f8a03cefa4ef05c08d54a357910286487afd8 100644 (file)
@@ -1149,7 +1149,7 @@ static int __init parse_crashkernel_simple(char *cmdline,
        if (*cur == '@')
                *crash_base = memparse(cur+1, &cur);
        else if (*cur != ' ' && *cur != '\0') {
-               pr_warn("crashkernel: unrecognized char\n");
+               pr_warn("crashkernel: unrecognized char: %c\n", *cur);
                return -EINVAL;
        }
 
@@ -1186,12 +1186,12 @@ static int __init parse_crashkernel_suffix(char *cmdline,
 
        /* check with suffix */
        if (strncmp(cur, suffix, strlen(suffix))) {
-               pr_warn("crashkernel: unrecognized char\n");
+               pr_warn("crashkernel: unrecognized char: %c\n", *cur);
                return -EINVAL;
        }
        cur += strlen(suffix);
        if (*cur != ' ' && *cur != '\0') {
-               pr_warn("crashkernel: unrecognized char\n");
+               pr_warn("crashkernel: unrecognized char: %c\n", *cur);
                return -EINVAL;
        }
 
index 32244186f1f2ae0e7a6343ad084f416aa0cda055..8ef1919d63b2401a4832404d92c598c3e656b090 100644 (file)
  *
  * Copyright (C) IBM Corporation, 2014
  *
- * Author: Paul E. McKenney <paulmck@us.ibm.com>
+ * Authors: Paul E. McKenney <paulmck@us.ibm.com>
+ *          Davidlohr Bueso <dave@stgolabs.net>
  *     Based on kernel/rcu/torture.c.
  */
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/kthread.h>
+#include <linux/sched/rt.h>
 #include <linux/spinlock.h>
 #include <linux/rwlock.h>
 #include <linux/mutex.h>
@@ -34,6 +36,7 @@
 #include <linux/moduleparam.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/percpu-rwsem.h>
 #include <linux/torture.h>
 
 MODULE_LICENSE("GPL");
@@ -91,11 +94,13 @@ struct lock_torture_ops {
        void (*init)(void);
        int (*writelock)(void);
        void (*write_delay)(struct torture_random_state *trsp);
+       void (*task_boost)(struct torture_random_state *trsp);
        void (*writeunlock)(void);
        int (*readlock)(void);
        void (*read_delay)(struct torture_random_state *trsp);
        void (*readunlock)(void);
-       unsigned long flags;
+
+       unsigned long flags; /* for irq spinlocks */
        const char *name;
 };
 
@@ -139,9 +144,15 @@ static void torture_lock_busted_write_unlock(void)
          /* BUGGY, do not use in real life!!! */
 }
 
+static void torture_boost_dummy(struct torture_random_state *trsp)
+{
+       /* Only rtmutexes care about priority */
+}
+
 static struct lock_torture_ops lock_busted_ops = {
        .writelock      = torture_lock_busted_write_lock,
        .write_delay    = torture_lock_busted_write_delay,
+       .task_boost     = torture_boost_dummy,
        .writeunlock    = torture_lock_busted_write_unlock,
        .readlock       = NULL,
        .read_delay     = NULL,
@@ -185,6 +196,7 @@ static void torture_spin_lock_write_unlock(void) __releases(torture_spinlock)
 static struct lock_torture_ops spin_lock_ops = {
        .writelock      = torture_spin_lock_write_lock,
        .write_delay    = torture_spin_lock_write_delay,
+       .task_boost     = torture_boost_dummy,
        .writeunlock    = torture_spin_lock_write_unlock,
        .readlock       = NULL,
        .read_delay     = NULL,
@@ -211,6 +223,7 @@ __releases(torture_spinlock)
 static struct lock_torture_ops spin_lock_irq_ops = {
        .writelock      = torture_spin_lock_write_lock_irq,
        .write_delay    = torture_spin_lock_write_delay,
+       .task_boost     = torture_boost_dummy,
        .writeunlock    = torture_lock_spin_write_unlock_irq,
        .readlock       = NULL,
        .read_delay     = NULL,
@@ -275,6 +288,7 @@ static void torture_rwlock_read_unlock(void) __releases(torture_rwlock)
 static struct lock_torture_ops rw_lock_ops = {
        .writelock      = torture_rwlock_write_lock,
        .write_delay    = torture_rwlock_write_delay,
+       .task_boost     = torture_boost_dummy,
        .writeunlock    = torture_rwlock_write_unlock,
        .readlock       = torture_rwlock_read_lock,
        .read_delay     = torture_rwlock_read_delay,
@@ -315,6 +329,7 @@ __releases(torture_rwlock)
 static struct lock_torture_ops rw_lock_irq_ops = {
        .writelock      = torture_rwlock_write_lock_irq,
        .write_delay    = torture_rwlock_write_delay,
+       .task_boost     = torture_boost_dummy,
        .writeunlock    = torture_rwlock_write_unlock_irq,
        .readlock       = torture_rwlock_read_lock_irq,
        .read_delay     = torture_rwlock_read_delay,
@@ -354,6 +369,7 @@ static void torture_mutex_unlock(void) __releases(torture_mutex)
 static struct lock_torture_ops mutex_lock_ops = {
        .writelock      = torture_mutex_lock,
        .write_delay    = torture_mutex_delay,
+       .task_boost     = torture_boost_dummy,
        .writeunlock    = torture_mutex_unlock,
        .readlock       = NULL,
        .read_delay     = NULL,
@@ -361,6 +377,90 @@ static struct lock_torture_ops mutex_lock_ops = {
        .name           = "mutex_lock"
 };
 
+#ifdef CONFIG_RT_MUTEXES
+static DEFINE_RT_MUTEX(torture_rtmutex);
+
+static int torture_rtmutex_lock(void) __acquires(torture_rtmutex)
+{
+       rt_mutex_lock(&torture_rtmutex);
+       return 0;
+}
+
+static void torture_rtmutex_boost(struct torture_random_state *trsp)
+{
+       int policy;
+       struct sched_param param;
+       const unsigned int factor = 50000; /* yes, quite arbitrary */
+
+       if (!rt_task(current)) {
+               /*
+                * (1) Boost priority once every ~50k operations. When the
+                * task tries to take the lock, the rtmutex it will account
+                * for the new priority, and do any corresponding pi-dance.
+                */
+               if (!(torture_random(trsp) %
+                     (cxt.nrealwriters_stress * factor))) {
+                       policy = SCHED_FIFO;
+                       param.sched_priority = MAX_RT_PRIO - 1;
+               } else /* common case, do nothing */
+                       return;
+       } else {
+               /*
+                * The task will remain boosted for another ~500k operations,
+                * then restored back to its original prio, and so forth.
+                *
+                * When @trsp is nil, we want to force-reset the task for
+                * stopping the kthread.
+                */
+               if (!trsp || !(torture_random(trsp) %
+                              (cxt.nrealwriters_stress * factor * 2))) {
+                       policy = SCHED_NORMAL;
+                       param.sched_priority = 0;
+               } else /* common case, do nothing */
+                       return;
+       }
+
+       sched_setscheduler_nocheck(current, policy, &param);
+}
+
+static void torture_rtmutex_delay(struct torture_random_state *trsp)
+{
+       const unsigned long shortdelay_us = 2;
+       const unsigned long longdelay_ms = 100;
+
+       /*
+        * We want a short delay mostly to emulate likely code, and
+        * we want a long delay occasionally to force massive contention.
+        */
+       if (!(torture_random(trsp) %
+             (cxt.nrealwriters_stress * 2000 * longdelay_ms)))
+               mdelay(longdelay_ms);
+       if (!(torture_random(trsp) %
+             (cxt.nrealwriters_stress * 2 * shortdelay_us)))
+               udelay(shortdelay_us);
+#ifdef CONFIG_PREEMPT
+       if (!(torture_random(trsp) % (cxt.nrealwriters_stress * 20000)))
+               preempt_schedule();  /* Allow test to be preempted. */
+#endif
+}
+
+static void torture_rtmutex_unlock(void) __releases(torture_rtmutex)
+{
+       rt_mutex_unlock(&torture_rtmutex);
+}
+
+static struct lock_torture_ops rtmutex_lock_ops = {
+       .writelock      = torture_rtmutex_lock,
+       .write_delay    = torture_rtmutex_delay,
+       .task_boost     = torture_rtmutex_boost,
+       .writeunlock    = torture_rtmutex_unlock,
+       .readlock       = NULL,
+       .read_delay     = NULL,
+       .readunlock     = NULL,
+       .name           = "rtmutex_lock"
+};
+#endif
+
 static DECLARE_RWSEM(torture_rwsem);
 static int torture_rwsem_down_write(void) __acquires(torture_rwsem)
 {
@@ -419,6 +519,7 @@ static void torture_rwsem_up_read(void) __releases(torture_rwsem)
 static struct lock_torture_ops rwsem_lock_ops = {
        .writelock      = torture_rwsem_down_write,
        .write_delay    = torture_rwsem_write_delay,
+       .task_boost     = torture_boost_dummy,
        .writeunlock    = torture_rwsem_up_write,
        .readlock       = torture_rwsem_down_read,
        .read_delay     = torture_rwsem_read_delay,
@@ -426,6 +527,48 @@ static struct lock_torture_ops rwsem_lock_ops = {
        .name           = "rwsem_lock"
 };
 
+#include <linux/percpu-rwsem.h>
+static struct percpu_rw_semaphore pcpu_rwsem;
+
+void torture_percpu_rwsem_init(void)
+{
+       BUG_ON(percpu_init_rwsem(&pcpu_rwsem));
+}
+
+static int torture_percpu_rwsem_down_write(void) __acquires(pcpu_rwsem)
+{
+       percpu_down_write(&pcpu_rwsem);
+       return 0;
+}
+
+static void torture_percpu_rwsem_up_write(void) __releases(pcpu_rwsem)
+{
+       percpu_up_write(&pcpu_rwsem);
+}
+
+static int torture_percpu_rwsem_down_read(void) __acquires(pcpu_rwsem)
+{
+       percpu_down_read(&pcpu_rwsem);
+       return 0;
+}
+
+static void torture_percpu_rwsem_up_read(void) __releases(pcpu_rwsem)
+{
+       percpu_up_read(&pcpu_rwsem);
+}
+
+static struct lock_torture_ops percpu_rwsem_lock_ops = {
+       .init           = torture_percpu_rwsem_init,
+       .writelock      = torture_percpu_rwsem_down_write,
+       .write_delay    = torture_rwsem_write_delay,
+       .task_boost     = torture_boost_dummy,
+       .writeunlock    = torture_percpu_rwsem_up_write,
+       .readlock       = torture_percpu_rwsem_down_read,
+       .read_delay     = torture_rwsem_read_delay,
+       .readunlock     = torture_percpu_rwsem_up_read,
+       .name           = "percpu_rwsem_lock"
+};
+
 /*
  * Lock torture writer kthread.  Repeatedly acquires and releases
  * the lock, checking for duplicate acquisitions.
@@ -442,6 +585,7 @@ static int lock_torture_writer(void *arg)
                if ((torture_random(&rand) & 0xfffff) == 0)
                        schedule_timeout_uninterruptible(1);
 
+               cxt.cur_ops->task_boost(&rand);
                cxt.cur_ops->writelock();
                if (WARN_ON_ONCE(lock_is_write_held))
                        lwsp->n_lock_fail++;
@@ -456,6 +600,8 @@ static int lock_torture_writer(void *arg)
 
                stutter_wait("lock_torture_writer");
        } while (!torture_must_stop());
+
+       cxt.cur_ops->task_boost(NULL); /* reset prio */
        torture_kthread_stopping("lock_torture_writer");
        return 0;
 }
@@ -642,7 +788,11 @@ static int __init lock_torture_init(void)
                &spin_lock_ops, &spin_lock_irq_ops,
                &rw_lock_ops, &rw_lock_irq_ops,
                &mutex_lock_ops,
+#ifdef CONFIG_RT_MUTEXES
+               &rtmutex_lock_ops,
+#endif
                &rwsem_lock_ops,
+               &percpu_rwsem_lock_ops,
        };
 
        if (!torture_init_begin(torture_type, verbose, &torture_runnable))
@@ -661,11 +811,11 @@ static int __init lock_torture_init(void)
                for (i = 0; i < ARRAY_SIZE(torture_ops); i++)
                        pr_alert(" %s", torture_ops[i]->name);
                pr_alert("\n");
-               torture_init_end();
-               return -EINVAL;
+               firsterr = -EINVAL;
+               goto unwind;
        }
        if (cxt.cur_ops->init)
-               cxt.cur_ops->init(); /* no "goto unwind" prior to this point!!! */
+               cxt.cur_ops->init();
 
        if (nwriters_stress >= 0)
                cxt.nrealwriters_stress = nwriters_stress;
@@ -676,6 +826,10 @@ static int __init lock_torture_init(void)
        if (strncmp(torture_type, "mutex", 5) == 0)
                cxt.debug_lock = true;
 #endif
+#ifdef CONFIG_DEBUG_RT_MUTEXES
+       if (strncmp(torture_type, "rtmutex", 7) == 0)
+               cxt.debug_lock = true;
+#endif
 #ifdef CONFIG_DEBUG_SPINLOCK
        if ((strncmp(torture_type, "spin", 4) == 0) ||
            (strncmp(torture_type, "rw_lock", 7) == 0))
index fd91aaa4554c8be6cf5b981d7ac3022a8046e77e..5b9102a47ea5209dd887b6dfbca16215b2bad922 100644 (file)
@@ -67,7 +67,7 @@ void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
        node->locked = 0;
        node->next   = NULL;
 
-       prev = xchg(lock, node);
+       prev = xchg_acquire(lock, node);
        if (likely(prev == NULL)) {
                /*
                 * Lock acquired, don't need to set node->locked to 1. Threads
@@ -98,7 +98,7 @@ void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
                /*
                 * Release the lock by setting it to NULL
                 */
-               if (likely(cmpxchg(lock, node, NULL) == node))
+               if (likely(cmpxchg_release(lock, node, NULL) == node))
                        return;
                /* Wait until the next pointer is set */
                while (!(next = READ_ONCE(node->next)))
index 4cccea6b8934f5697fa3dfa609ae4bd10db78b6f..0551c219c40e5bb8f8f87bfb0445100fe1b31cc9 100644 (file)
@@ -277,7 +277,7 @@ static inline int mutex_can_spin_on_owner(struct mutex *lock)
 static inline bool mutex_try_to_acquire(struct mutex *lock)
 {
        return !mutex_is_locked(lock) &&
-               (atomic_cmpxchg(&lock->count, 1, 0) == 1);
+               (atomic_cmpxchg_acquire(&lock->count, 1, 0) == 1);
 }
 
 /*
@@ -529,7 +529,8 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
         * Once more, try to acquire the lock. Only try-lock the mutex if
         * it is unlocked to reduce unnecessary xchg() operations.
         */
-       if (!mutex_is_locked(lock) && (atomic_xchg(&lock->count, 0) == 1))
+       if (!mutex_is_locked(lock) &&
+           (atomic_xchg_acquire(&lock->count, 0) == 1))
                goto skip_wait;
 
        debug_mutex_lock_common(lock, &waiter);
@@ -553,7 +554,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
                 * non-negative in order to avoid unnecessary xchg operations:
                 */
                if (atomic_read(&lock->count) >= 0 &&
-                   (atomic_xchg(&lock->count, -1) == 1))
+                   (atomic_xchg_acquire(&lock->count, -1) == 1))
                        break;
 
                /*
@@ -867,7 +868,7 @@ static inline int __mutex_trylock_slowpath(atomic_t *lock_count)
 
        spin_lock_mutex(&lock->wait_lock, flags);
 
-       prev = atomic_xchg(&lock->count, -1);
+       prev = atomic_xchg_acquire(&lock->count, -1);
        if (likely(prev == 1)) {
                mutex_set_owner(lock);
                mutex_acquire(&lock->dep_map, 0, 1, _RET_IP_);
index dc85ee23a26f79416a140241e3067a5a2ca24d0b..d092a0c9c2d4ed838004215cdf038691a14f4598 100644 (file)
@@ -50,7 +50,7 @@ osq_wait_next(struct optimistic_spin_queue *lock,
 
        for (;;) {
                if (atomic_read(&lock->tail) == curr &&
-                   atomic_cmpxchg(&lock->tail, curr, old) == curr) {
+                   atomic_cmpxchg_acquire(&lock->tail, curr, old) == curr) {
                        /*
                         * We were the last queued, we moved @lock back. @prev
                         * will now observe @lock and will complete its
@@ -92,7 +92,11 @@ bool osq_lock(struct optimistic_spin_queue *lock)
        node->next = NULL;
        node->cpu = curr;
 
-       old = atomic_xchg(&lock->tail, curr);
+       /*
+        * ACQUIRE semantics, pairs with corresponding RELEASE
+        * in unlock() uncontended, or fastpath.
+        */
+       old = atomic_xchg_acquire(&lock->tail, curr);
        if (old == OSQ_UNLOCKED_VAL)
                return true;
 
@@ -184,7 +188,8 @@ void osq_unlock(struct optimistic_spin_queue *lock)
        /*
         * Fast path for the uncontended case.
         */
-       if (likely(atomic_cmpxchg(&lock->tail, curr, OSQ_UNLOCKED_VAL) == curr))
+       if (likely(atomic_cmpxchg_release(&lock->tail, curr,
+                                         OSQ_UNLOCKED_VAL) == curr))
                return;
 
        /*
index f3256725486738878d0f640216a95d91d16359d4..f231e0bb311ce0827d281d34f737a3a06405c072 100644 (file)
@@ -17,50 +17,43 @@ int __percpu_init_rwsem(struct percpu_rw_semaphore *brw,
 
        /* ->rw_sem represents the whole percpu_rw_semaphore for lockdep */
        __init_rwsem(&brw->rw_sem, name, rwsem_key);
-       atomic_set(&brw->write_ctr, 0);
+       rcu_sync_init(&brw->rss, RCU_SCHED_SYNC);
        atomic_set(&brw->slow_read_ctr, 0);
        init_waitqueue_head(&brw->write_waitq);
        return 0;
 }
+EXPORT_SYMBOL_GPL(__percpu_init_rwsem);
 
 void percpu_free_rwsem(struct percpu_rw_semaphore *brw)
 {
+       /*
+        * XXX: temporary kludge. The error path in alloc_super()
+        * assumes that percpu_free_rwsem() is safe after kzalloc().
+        */
+       if (!brw->fast_read_ctr)
+               return;
+
+       rcu_sync_dtor(&brw->rss);
        free_percpu(brw->fast_read_ctr);
        brw->fast_read_ctr = NULL; /* catch use after free bugs */
 }
 
 /*
- * This is the fast-path for down_read/up_read, it only needs to ensure
- * there is no pending writer (atomic_read(write_ctr) == 0) and inc/dec the
- * fast per-cpu counter. The writer uses synchronize_sched_expedited() to
- * serialize with the preempt-disabled section below.
- *
- * The nontrivial part is that we should guarantee acquire/release semantics
- * in case when
- *
- *     R_W: down_write() comes after up_read(), the writer should see all
- *          changes done by the reader
- * or
- *     W_R: down_read() comes after up_write(), the reader should see all
- *          changes done by the writer
+ * This is the fast-path for down_read/up_read. If it succeeds we rely
+ * on the barriers provided by rcu_sync_enter/exit; see the comments in
+ * percpu_down_write() and percpu_up_write().
  *
  * If this helper fails the callers rely on the normal rw_semaphore and
  * atomic_dec_and_test(), so in this case we have the necessary barriers.
- *
- * But if it succeeds we do not have any barriers, atomic_read(write_ctr) or
- * __this_cpu_add() below can be reordered with any LOAD/STORE done by the
- * reader inside the critical section. See the comments in down_write and
- * up_write below.
  */
 static bool update_fast_ctr(struct percpu_rw_semaphore *brw, unsigned int val)
 {
-       bool success = false;
+       bool success;
 
        preempt_disable();
-       if (likely(!atomic_read(&brw->write_ctr))) {
+       success = rcu_sync_is_idle(&brw->rss);
+       if (likely(success))
                __this_cpu_add(*brw->fast_read_ctr, val);
-               success = true;
-       }
        preempt_enable();
 
        return success;
@@ -77,16 +70,17 @@ static bool update_fast_ctr(struct percpu_rw_semaphore *brw, unsigned int val)
 void percpu_down_read(struct percpu_rw_semaphore *brw)
 {
        might_sleep();
-       if (likely(update_fast_ctr(brw, +1))) {
-               rwsem_acquire_read(&brw->rw_sem.dep_map, 0, 0, _RET_IP_);
+       rwsem_acquire_read(&brw->rw_sem.dep_map, 0, 0, _RET_IP_);
+
+       if (likely(update_fast_ctr(brw, +1)))
                return;
-       }
 
-       down_read(&brw->rw_sem);
+       /* Avoid rwsem_acquire_read() and rwsem_release() */
+       __down_read(&brw->rw_sem);
        atomic_inc(&brw->slow_read_ctr);
-       /* avoid up_read()->rwsem_release() */
        __up_read(&brw->rw_sem);
 }
+EXPORT_SYMBOL_GPL(percpu_down_read);
 
 int percpu_down_read_trylock(struct percpu_rw_semaphore *brw)
 {
@@ -112,6 +106,7 @@ void percpu_up_read(struct percpu_rw_semaphore *brw)
        if (atomic_dec_and_test(&brw->slow_read_ctr))
                wake_up_all(&brw->write_waitq);
 }
+EXPORT_SYMBOL_GPL(percpu_up_read);
 
 static int clear_fast_ctr(struct percpu_rw_semaphore *brw)
 {
@@ -126,33 +121,17 @@ static int clear_fast_ctr(struct percpu_rw_semaphore *brw)
        return sum;
 }
 
-/*
- * A writer increments ->write_ctr to force the readers to switch to the
- * slow mode, note the atomic_read() check in update_fast_ctr().
- *
- * After that the readers can only inc/dec the slow ->slow_read_ctr counter,
- * ->fast_read_ctr is stable. Once the writer moves its sum into the slow
- * counter it represents the number of active readers.
- *
- * Finally the writer takes ->rw_sem for writing and blocks the new readers,
- * then waits until the slow counter becomes zero.
- */
 void percpu_down_write(struct percpu_rw_semaphore *brw)
 {
-       /* tell update_fast_ctr() there is a pending writer */
-       atomic_inc(&brw->write_ctr);
        /*
-        * 1. Ensures that write_ctr != 0 is visible to any down_read/up_read
-        *    so that update_fast_ctr() can't succeed.
-        *
-        * 2. Ensures we see the result of every previous this_cpu_add() in
-        *    update_fast_ctr().
+        * Make rcu_sync_is_idle() == F and thus disable the fast-path in
+        * percpu_down_read() and percpu_up_read(), and wait for gp pass.
         *
-        * 3. Ensures that if any reader has exited its critical section via
-        *    fast-path, it executes a full memory barrier before we return.
-        *    See R_W case in the comment above update_fast_ctr().
+        * The latter synchronises us with the preceding readers which used
+        * the fast-past, so we can not miss the result of __this_cpu_add()
+        * or anything else inside their criticial sections.
         */
-       synchronize_sched_expedited();
+       rcu_sync_enter(&brw->rss);
 
        /* exclude other writers, and block the new readers completely */
        down_write(&brw->rw_sem);
@@ -163,16 +142,17 @@ void percpu_down_write(struct percpu_rw_semaphore *brw)
        /* wait for all readers to complete their percpu_up_read() */
        wait_event(brw->write_waitq, !atomic_read(&brw->slow_read_ctr));
 }
+EXPORT_SYMBOL_GPL(percpu_down_write);
 
 void percpu_up_write(struct percpu_rw_semaphore *brw)
 {
        /* release the lock, but the readers can't use the fast-path */
        up_write(&brw->rw_sem);
        /*
-        * Insert the barrier before the next fast-path in down_read,
-        * see W_R case in the comment above update_fast_ctr().
+        * Enable the fast-path in percpu_down_read() and percpu_up_read()
+        * but only after another gp pass; this adds the necessary barrier
+        * to ensure the reader can't miss the changes done by us.
         */
-       synchronize_sched_expedited();
-       /* the last writer unblocks update_fast_ctr() */
-       atomic_dec(&brw->write_ctr);
+       rcu_sync_exit(&brw->rss);
 }
+EXPORT_SYMBOL_GPL(percpu_up_write);
index f17a3e3b355079b6658b55b2f21811a69e695190..fec08233866875ca1962d836b4e5d6f88d475092 100644 (file)
@@ -86,7 +86,7 @@ void queued_read_lock_slowpath(struct qrwlock *lock, u32 cnts)
        /*
         * Put the reader into the wait queue
         */
-       arch_spin_lock(&lock->lock);
+       arch_spin_lock(&lock->wait_lock);
 
        /*
         * The ACQUIRE semantics of the following spinning code ensure
@@ -99,7 +99,7 @@ void queued_read_lock_slowpath(struct qrwlock *lock, u32 cnts)
        /*
         * Signal the next one in queue to become queue head
         */
-       arch_spin_unlock(&lock->lock);
+       arch_spin_unlock(&lock->wait_lock);
 }
 EXPORT_SYMBOL(queued_read_lock_slowpath);
 
@@ -112,7 +112,7 @@ void queued_write_lock_slowpath(struct qrwlock *lock)
        u32 cnts;
 
        /* Put the writer into the wait queue */
-       arch_spin_lock(&lock->lock);
+       arch_spin_lock(&lock->wait_lock);
 
        /* Try to acquire the lock directly if no reader is present */
        if (!atomic_read(&lock->cnts) &&
@@ -144,6 +144,6 @@ void queued_write_lock_slowpath(struct qrwlock *lock)
                cpu_relax_lowlatency();
        }
 unlock:
-       arch_spin_unlock(&lock->lock);
+       arch_spin_unlock(&lock->wait_lock);
 }
 EXPORT_SYMBOL(queued_write_lock_slowpath);
index c8e6e9a596f513baa8a85af3cb0f2b9ff9116a91..f0450ff4829b6c1308d4768b2ae3a7c575b1cf51 100644 (file)
@@ -267,7 +267,6 @@ static void pv_wait_head(struct qspinlock *lock, struct mcs_spinlock *node)
                }
 
                if (!lp) { /* ONCE */
-                       WRITE_ONCE(pn->state, vcpu_hashed);
                        lp = pv_hash(lock, pn);
 
                        /*
@@ -275,11 +274,9 @@ static void pv_wait_head(struct qspinlock *lock, struct mcs_spinlock *node)
                         * when we observe _Q_SLOW_VAL in __pv_queued_spin_unlock()
                         * we'll be sure to be able to observe our hash entry.
                         *
-                        *   [S] pn->state
                         *   [S] <hash>                 [Rmw] l->locked == _Q_SLOW_VAL
                         *       MB                           RMB
                         * [RmW] l->locked = _Q_SLOW_VAL  [L] <unhash>
-                        *                                [L] pn->state
                         *
                         * Matches the smp_rmb() in __pv_queued_spin_unlock().
                         */
@@ -364,8 +361,7 @@ __visible void __pv_queued_spin_unlock(struct qspinlock *lock)
         * vCPU is harmless other than the additional latency in completing
         * the unlock.
         */
-       if (READ_ONCE(node->state) == vcpu_hashed)
-               pv_kick(node->cpu);
+       pv_kick(node->cpu);
 }
 /*
  * Include the architecture specific callee-save thunk of the
index 7781d801212fa32082593cf6bbb5dd44077cb78e..8251e75dd9c0bd67337754baf6f03fb2cf1f956f 100644 (file)
@@ -74,14 +74,23 @@ static void fixup_rt_mutex_waiters(struct rt_mutex *lock)
  * set up.
  */
 #ifndef CONFIG_DEBUG_RT_MUTEXES
-# define rt_mutex_cmpxchg(l,c,n)       (cmpxchg(&l->owner, c, n) == c)
+# define rt_mutex_cmpxchg_relaxed(l,c,n) (cmpxchg_relaxed(&l->owner, c, n) == c)
+# define rt_mutex_cmpxchg_acquire(l,c,n) (cmpxchg_acquire(&l->owner, c, n) == c)
+# define rt_mutex_cmpxchg_release(l,c,n) (cmpxchg_release(&l->owner, c, n) == c)
+
+/*
+ * Callers must hold the ->wait_lock -- which is the whole purpose as we force
+ * all future threads that attempt to [Rmw] the lock to the slowpath. As such
+ * relaxed semantics suffice.
+ */
 static inline void mark_rt_mutex_waiters(struct rt_mutex *lock)
 {
        unsigned long owner, *p = (unsigned long *) &lock->owner;
 
        do {
                owner = *p;
-       } while (cmpxchg(p, owner, owner | RT_MUTEX_HAS_WAITERS) != owner);
+       } while (cmpxchg_relaxed(p, owner,
+                                owner | RT_MUTEX_HAS_WAITERS) != owner);
 }
 
 /*
@@ -121,11 +130,14 @@ static inline bool unlock_rt_mutex_safe(struct rt_mutex *lock)
         *                                      lock(wait_lock);
         *                                      acquire(lock);
         */
-       return rt_mutex_cmpxchg(lock, owner, NULL);
+       return rt_mutex_cmpxchg_release(lock, owner, NULL);
 }
 
 #else
-# define rt_mutex_cmpxchg(l,c,n)       (0)
+# define rt_mutex_cmpxchg_relaxed(l,c,n)       (0)
+# define rt_mutex_cmpxchg_acquire(l,c,n)       (0)
+# define rt_mutex_cmpxchg_release(l,c,n)       (0)
+
 static inline void mark_rt_mutex_waiters(struct rt_mutex *lock)
 {
        lock->owner = (struct task_struct *)
@@ -158,7 +170,8 @@ rt_mutex_waiter_less(struct rt_mutex_waiter *left,
         * then right waiter has a dl_prio() too.
         */
        if (dl_prio(left->prio))
-               return (left->task->dl.deadline < right->task->dl.deadline);
+               return dl_time_before(left->task->dl.deadline,
+                                     right->task->dl.deadline);
 
        return 0;
 }
@@ -1321,7 +1334,7 @@ rt_mutex_fastlock(struct rt_mutex *lock, int state,
                                struct hrtimer_sleeper *timeout,
                                enum rtmutex_chainwalk chwalk))
 {
-       if (likely(rt_mutex_cmpxchg(lock, NULL, current))) {
+       if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) {
                rt_mutex_deadlock_account_lock(lock, current);
                return 0;
        } else
@@ -1337,7 +1350,7 @@ rt_mutex_timed_fastlock(struct rt_mutex *lock, int state,
                                      enum rtmutex_chainwalk chwalk))
 {
        if (chwalk == RT_MUTEX_MIN_CHAINWALK &&
-           likely(rt_mutex_cmpxchg(lock, NULL, current))) {
+           likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) {
                rt_mutex_deadlock_account_lock(lock, current);
                return 0;
        } else
@@ -1348,7 +1361,7 @@ static inline int
 rt_mutex_fasttrylock(struct rt_mutex *lock,
                     int (*slowfn)(struct rt_mutex *lock))
 {
-       if (likely(rt_mutex_cmpxchg(lock, NULL, current))) {
+       if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) {
                rt_mutex_deadlock_account_lock(lock, current);
                return 1;
        }
@@ -1362,7 +1375,7 @@ rt_mutex_fastunlock(struct rt_mutex *lock,
 {
        WAKE_Q(wake_q);
 
-       if (likely(rt_mutex_cmpxchg(lock, current, NULL))) {
+       if (likely(rt_mutex_cmpxchg_release(lock, current, NULL))) {
                rt_mutex_deadlock_account_unlock(current);
 
        } else {
@@ -1484,7 +1497,7 @@ EXPORT_SYMBOL_GPL(rt_mutex_unlock);
 bool __sched rt_mutex_futex_unlock(struct rt_mutex *lock,
                                   struct wake_q_head *wqh)
 {
-       if (likely(rt_mutex_cmpxchg(lock, current, NULL))) {
+       if (likely(rt_mutex_cmpxchg_release(lock, current, NULL))) {
                rt_mutex_deadlock_account_unlock(current);
                return false;
        }
index 0f189714e457016ba7801c788a44673907c7746b..a4d4de05b2d16707becf6179f8ae2ebed334bead 100644 (file)
@@ -262,7 +262,7 @@ static inline bool rwsem_try_write_lock(long count, struct rw_semaphore *sem)
         * to reduce unnecessary expensive cmpxchg() operations.
         */
        if (count == RWSEM_WAITING_BIAS &&
-           cmpxchg(&sem->count, RWSEM_WAITING_BIAS,
+           cmpxchg_acquire(&sem->count, RWSEM_WAITING_BIAS,
                    RWSEM_ACTIVE_WRITE_BIAS) == RWSEM_WAITING_BIAS) {
                if (!list_is_singular(&sem->wait_list))
                        rwsem_atomic_update(RWSEM_WAITING_BIAS, sem);
@@ -285,7 +285,8 @@ static inline bool rwsem_try_write_lock_unqueued(struct rw_semaphore *sem)
                if (!(count == 0 || count == RWSEM_WAITING_BIAS))
                        return false;
 
-               old = cmpxchg(&sem->count, count, count + RWSEM_ACTIVE_WRITE_BIAS);
+               old = cmpxchg_acquire(&sem->count, count,
+                                     count + RWSEM_ACTIVE_WRITE_BIAS);
                if (old == count) {
                        rwsem_set_owner(sem);
                        return true;
index 72b0c66628b6b3db589ad0cd6ed6533edb68a9ea..9d6b55587eaa59a25c38776e43aeff51ba682c40 100644 (file)
@@ -24,6 +24,16 @@ __weak void __iomem *ioremap_cache(resource_size_t offset, unsigned long size)
 }
 #endif
 
+static void *try_ram_remap(resource_size_t offset, size_t size)
+{
+       struct page *page = pfn_to_page(offset >> PAGE_SHIFT);
+
+       /* In the simple case just return the existing linear address */
+       if (!PageHighMem(page))
+               return __va(offset);
+       return NULL; /* fallback to ioremap_cache */
+}
+
 /**
  * memremap() - remap an iomem_resource as cacheable memory
  * @offset: iomem resource start address
@@ -66,8 +76,8 @@ void *memremap(resource_size_t offset, size_t size, unsigned long flags)
                 * the requested range is potentially in "System RAM"
                 */
                if (is_ram == REGION_INTERSECTS)
-                       addr = __va(offset);
-               else
+                       addr = try_ram_remap(offset, size);
+               if (!addr)
                        addr = ioremap_cache(offset, size);
        }
 
index b86b7bf1be388d72fe92fb6038b4a67b4710df1f..8f051a106676fb8f2d5457a9104340cce81ce37a 100644 (file)
@@ -1063,11 +1063,15 @@ void symbol_put_addr(void *addr)
        if (core_kernel_text(a))
                return;
 
-       /* module_text_address is safe here: we're supposed to have reference
-        * to module from symbol_get, so it can't go away. */
+       /*
+        * Even though we hold a reference on the module; we still need to
+        * disable preemption in order to safely traverse the data structure.
+        */
+       preempt_disable();
        modaddr = __module_text_address(a);
        BUG_ON(!modaddr);
        module_put(modaddr);
+       preempt_enable();
 }
 EXPORT_SYMBOL_GPL(symbol_put_addr);
 
index 787320de68e02425e8506363681b8875b6ee6d57..b760bae64cf123b16863b6180a68a099abca51d1 100644 (file)
@@ -1016,6 +1016,11 @@ int ptrace_request(struct task_struct *child, long request,
                break;
        }
 #endif
+
+       case PTRACE_SECCOMP_GET_FILTER:
+               ret = seccomp_get_filter(child, addr, datavp);
+               break;
+
        default:
                break;
        }
index 50a808424b06af45fdd0fd6ab3ae732e1917680d..61a16569ffbf53c2b19b9b67d908edcd225dc201 100644 (file)
@@ -1,4 +1,4 @@
-obj-y += update.o
+obj-y += update.o sync.o
 obj-$(CONFIG_SRCU) += srcu.o
 obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o
 obj-$(CONFIG_TREE_RCU) += tree.o
index 77192953dee5f5a54b10131ee70684fafb18c2c6..d89328e260df6f4f953b649fea98587aacf12318 100644 (file)
@@ -252,7 +252,7 @@ struct rcu_torture_ops {
        void (*exp_sync)(void);
        unsigned long (*get_state)(void);
        void (*cond_sync)(unsigned long oldstate);
-       void (*call)(struct rcu_head *head, void (*func)(struct rcu_head *rcu));
+       call_rcu_func_t call;
        void (*cb_barrier)(void);
        void (*fqs)(void);
        void (*stats)(void);
@@ -448,7 +448,7 @@ static void synchronize_rcu_busted(void)
 }
 
 static void
-call_rcu_busted(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
+call_rcu_busted(struct rcu_head *head, rcu_callback_t func)
 {
        /* This is a deliberate bug for testing purposes only! */
        func(head);
@@ -523,7 +523,7 @@ static void srcu_torture_synchronize(void)
 }
 
 static void srcu_torture_call(struct rcu_head *head,
-                             void (*func)(struct rcu_head *head))
+                             rcu_callback_t func)
 {
        call_srcu(srcu_ctlp, head, func);
 }
@@ -695,7 +695,7 @@ static bool __maybe_unused torturing_tasks(void)
 
 #define RCUTORTURE_TASKS_OPS
 
-static bool torturing_tasks(void)
+static bool __maybe_unused torturing_tasks(void)
 {
        return false;
 }
@@ -768,7 +768,6 @@ static int rcu_torture_boost(void *arg)
                                }
                                call_rcu_time = jiffies;
                        }
-                       cond_resched_rcu_qs();
                        stutter_wait("rcu_torture_boost");
                        if (torture_must_stop())
                                goto checkwait;
@@ -1208,7 +1207,6 @@ rcu_torture_reader(void *arg)
                __this_cpu_inc(rcu_torture_batch[completed]);
                preempt_enable();
                cur_ops->readunlock(idx);
-               cond_resched_rcu_qs();
                stutter_wait("rcu_torture_reader");
        } while (!torture_must_stop());
        if (irqreader && cur_ops->irq_capable) {
@@ -1742,15 +1740,15 @@ rcu_torture_init(void)
                for (i = 0; i < ARRAY_SIZE(torture_ops); i++)
                        pr_alert(" %s", torture_ops[i]->name);
                pr_alert("\n");
-               torture_init_end();
-               return -EINVAL;
+               firsterr = -EINVAL;
+               goto unwind;
        }
        if (cur_ops->fqs == NULL && fqs_duration != 0) {
                pr_alert("rcu-torture: ->fqs NULL and non-zero fqs_duration, fqs disabled.\n");
                fqs_duration = 0;
        }
        if (cur_ops->init)
-               cur_ops->init(); /* no "goto unwind" prior to this point!!! */
+               cur_ops->init();
 
        if (nreaders >= 0) {
                nrealreaders = nreaders;
index d3fcb2ec8536724615cbdd5ce5a891cbcc020934..a63a1ea5a41bf450f6b162674149a5928fca3530 100644 (file)
@@ -298,11 +298,9 @@ int __srcu_read_lock(struct srcu_struct *sp)
        int idx;
 
        idx = READ_ONCE(sp->completed) & 0x1;
-       preempt_disable();
        __this_cpu_inc(sp->per_cpu_ref->c[idx]);
        smp_mb(); /* B */  /* Avoid leaking the critical section. */
        __this_cpu_inc(sp->per_cpu_ref->seq[idx]);
-       preempt_enable();
        return idx;
 }
 EXPORT_SYMBOL_GPL(__srcu_read_lock);
@@ -387,7 +385,7 @@ static void srcu_flip(struct srcu_struct *sp)
  * srcu_struct structure.
  */
 void call_srcu(struct srcu_struct *sp, struct rcu_head *head,
-               void (*func)(struct rcu_head *head))
+              rcu_callback_t func)
 {
        unsigned long flags;
 
diff --git a/kernel/rcu/sync.c b/kernel/rcu/sync.c
new file mode 100644 (file)
index 0000000..be922c9
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * RCU-based infrastructure for lightweight reader-writer locking
+ *
+ * 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, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * Copyright (c) 2015, Red Hat, Inc.
+ *
+ * Author: Oleg Nesterov <oleg@redhat.com>
+ */
+
+#include <linux/rcu_sync.h>
+#include <linux/sched.h>
+
+#ifdef CONFIG_PROVE_RCU
+#define __INIT_HELD(func)      .held = func,
+#else
+#define __INIT_HELD(func)
+#endif
+
+static const struct {
+       void (*sync)(void);
+       void (*call)(struct rcu_head *, void (*)(struct rcu_head *));
+       void (*wait)(void);
+#ifdef CONFIG_PROVE_RCU
+       int  (*held)(void);
+#endif
+} gp_ops[] = {
+       [RCU_SYNC] = {
+               .sync = synchronize_rcu,
+               .call = call_rcu,
+               .wait = rcu_barrier,
+               __INIT_HELD(rcu_read_lock_held)
+       },
+       [RCU_SCHED_SYNC] = {
+               .sync = synchronize_sched,
+               .call = call_rcu_sched,
+               .wait = rcu_barrier_sched,
+               __INIT_HELD(rcu_read_lock_sched_held)
+       },
+       [RCU_BH_SYNC] = {
+               .sync = synchronize_rcu_bh,
+               .call = call_rcu_bh,
+               .wait = rcu_barrier_bh,
+               __INIT_HELD(rcu_read_lock_bh_held)
+       },
+};
+
+enum { GP_IDLE = 0, GP_PENDING, GP_PASSED };
+enum { CB_IDLE = 0, CB_PENDING, CB_REPLAY };
+
+#define        rss_lock        gp_wait.lock
+
+#ifdef CONFIG_PROVE_RCU
+void rcu_sync_lockdep_assert(struct rcu_sync *rsp)
+{
+       RCU_LOCKDEP_WARN(!gp_ops[rsp->gp_type].held(),
+                        "suspicious rcu_sync_is_idle() usage");
+}
+#endif
+
+/**
+ * rcu_sync_init() - Initialize an rcu_sync structure
+ * @rsp: Pointer to rcu_sync structure to be initialized
+ * @type: Flavor of RCU with which to synchronize rcu_sync structure
+ */
+void rcu_sync_init(struct rcu_sync *rsp, enum rcu_sync_type type)
+{
+       memset(rsp, 0, sizeof(*rsp));
+       init_waitqueue_head(&rsp->gp_wait);
+       rsp->gp_type = type;
+}
+
+/**
+ * rcu_sync_enter() - Force readers onto slowpath
+ * @rsp: Pointer to rcu_sync structure to use for synchronization
+ *
+ * This function is used by updaters who need readers to make use of
+ * a slowpath during the update.  After this function returns, all
+ * subsequent calls to rcu_sync_is_idle() will return false, which
+ * tells readers to stay off their fastpaths.  A later call to
+ * rcu_sync_exit() re-enables reader slowpaths.
+ *
+ * When called in isolation, rcu_sync_enter() must wait for a grace
+ * period, however, closely spaced calls to rcu_sync_enter() can
+ * optimize away the grace-period wait via a state machine implemented
+ * by rcu_sync_enter(), rcu_sync_exit(), and rcu_sync_func().
+ */
+void rcu_sync_enter(struct rcu_sync *rsp)
+{
+       bool need_wait, need_sync;
+
+       spin_lock_irq(&rsp->rss_lock);
+       need_wait = rsp->gp_count++;
+       need_sync = rsp->gp_state == GP_IDLE;
+       if (need_sync)
+               rsp->gp_state = GP_PENDING;
+       spin_unlock_irq(&rsp->rss_lock);
+
+       BUG_ON(need_wait && need_sync);
+
+       if (need_sync) {
+               gp_ops[rsp->gp_type].sync();
+               rsp->gp_state = GP_PASSED;
+               wake_up_all(&rsp->gp_wait);
+       } else if (need_wait) {
+               wait_event(rsp->gp_wait, rsp->gp_state == GP_PASSED);
+       } else {
+               /*
+                * Possible when there's a pending CB from a rcu_sync_exit().
+                * Nobody has yet been allowed the 'fast' path and thus we can
+                * avoid doing any sync(). The callback will get 'dropped'.
+                */
+               BUG_ON(rsp->gp_state != GP_PASSED);
+       }
+}
+
+/**
+ * rcu_sync_func() - Callback function managing reader access to fastpath
+ * @rsp: Pointer to rcu_sync structure to use for synchronization
+ *
+ * This function is passed to one of the call_rcu() functions by
+ * rcu_sync_exit(), so that it is invoked after a grace period following the
+ * that invocation of rcu_sync_exit().  It takes action based on events that
+ * have taken place in the meantime, so that closely spaced rcu_sync_enter()
+ * and rcu_sync_exit() pairs need not wait for a grace period.
+ *
+ * If another rcu_sync_enter() is invoked before the grace period
+ * ended, reset state to allow the next rcu_sync_exit() to let the
+ * readers back onto their fastpaths (after a grace period).  If both
+ * another rcu_sync_enter() and its matching rcu_sync_exit() are invoked
+ * before the grace period ended, re-invoke call_rcu() on behalf of that
+ * rcu_sync_exit().  Otherwise, set all state back to idle so that readers
+ * can again use their fastpaths.
+ */
+static void rcu_sync_func(struct rcu_head *rcu)
+{
+       struct rcu_sync *rsp = container_of(rcu, struct rcu_sync, cb_head);
+       unsigned long flags;
+
+       BUG_ON(rsp->gp_state != GP_PASSED);
+       BUG_ON(rsp->cb_state == CB_IDLE);
+
+       spin_lock_irqsave(&rsp->rss_lock, flags);
+       if (rsp->gp_count) {
+               /*
+                * A new rcu_sync_begin() has happened; drop the callback.
+                */
+               rsp->cb_state = CB_IDLE;
+       } else if (rsp->cb_state == CB_REPLAY) {
+               /*
+                * A new rcu_sync_exit() has happened; requeue the callback
+                * to catch a later GP.
+                */
+               rsp->cb_state = CB_PENDING;
+               gp_ops[rsp->gp_type].call(&rsp->cb_head, rcu_sync_func);
+       } else {
+               /*
+                * We're at least a GP after rcu_sync_exit(); eveybody will now
+                * have observed the write side critical section. Let 'em rip!.
+                */
+               rsp->cb_state = CB_IDLE;
+               rsp->gp_state = GP_IDLE;
+       }
+       spin_unlock_irqrestore(&rsp->rss_lock, flags);
+}
+
+/**
+ * rcu_sync_exit() - Allow readers back onto fast patch after grace period
+ * @rsp: Pointer to rcu_sync structure to use for synchronization
+ *
+ * This function is used by updaters who have completed, and can therefore
+ * now allow readers to make use of their fastpaths after a grace period
+ * has elapsed.  After this grace period has completed, all subsequent
+ * calls to rcu_sync_is_idle() will return true, which tells readers that
+ * they can once again use their fastpaths.
+ */
+void rcu_sync_exit(struct rcu_sync *rsp)
+{
+       spin_lock_irq(&rsp->rss_lock);
+       if (!--rsp->gp_count) {
+               if (rsp->cb_state == CB_IDLE) {
+                       rsp->cb_state = CB_PENDING;
+                       gp_ops[rsp->gp_type].call(&rsp->cb_head, rcu_sync_func);
+               } else if (rsp->cb_state == CB_PENDING) {
+                       rsp->cb_state = CB_REPLAY;
+               }
+       }
+       spin_unlock_irq(&rsp->rss_lock);
+}
+
+/**
+ * rcu_sync_dtor() - Clean up an rcu_sync structure
+ * @rsp: Pointer to rcu_sync structure to be cleaned up
+ */
+void rcu_sync_dtor(struct rcu_sync *rsp)
+{
+       int cb_state;
+
+       BUG_ON(rsp->gp_count);
+
+       spin_lock_irq(&rsp->rss_lock);
+       if (rsp->cb_state == CB_REPLAY)
+               rsp->cb_state = CB_PENDING;
+       cb_state = rsp->cb_state;
+       spin_unlock_irq(&rsp->rss_lock);
+
+       if (cb_state != CB_IDLE) {
+               gp_ops[rsp->gp_type].wait();
+               BUG_ON(rsp->cb_state != CB_IDLE);
+       }
+}
index d0471056d0afac18ed1739981aaa415495414791..944b1b491ed84b3d2d1cf977a9838a30cf405121 100644 (file)
@@ -44,7 +44,7 @@ struct rcu_ctrlblk;
 static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp);
 static void rcu_process_callbacks(struct softirq_action *unused);
 static void __call_rcu(struct rcu_head *head,
-                      void (*func)(struct rcu_head *rcu),
+                      rcu_callback_t func,
                       struct rcu_ctrlblk *rcp);
 
 #include "tiny_plugin.h"
@@ -203,7 +203,7 @@ EXPORT_SYMBOL_GPL(synchronize_sched);
  * Helper function for call_rcu() and call_rcu_bh().
  */
 static void __call_rcu(struct rcu_head *head,
-                      void (*func)(struct rcu_head *rcu),
+                      rcu_callback_t func,
                       struct rcu_ctrlblk *rcp)
 {
        unsigned long flags;
@@ -229,7 +229,7 @@ static void __call_rcu(struct rcu_head *head,
  * period.  But since we have but one CPU, that would be after any
  * quiescent state.
  */
-void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
+void call_rcu_sched(struct rcu_head *head, rcu_callback_t func)
 {
        __call_rcu(head, func, &rcu_sched_ctrlblk);
 }
@@ -239,7 +239,7 @@ EXPORT_SYMBOL_GPL(call_rcu_sched);
  * Post an RCU bottom-half callback to be invoked after any subsequent
  * quiescent state.
  */
-void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
+void call_rcu_bh(struct rcu_head *head, rcu_callback_t func)
 {
        __call_rcu(head, func, &rcu_bh_ctrlblk);
 }
index 775d36cc00506620829bd2ca14b5da21e4de79e3..f07343b54fe5a29d4fcabe7e78b9c3ea79029414 100644 (file)
@@ -71,7 +71,6 @@ MODULE_ALIAS("rcutree");
 static struct lock_class_key rcu_node_class[RCU_NUM_LVLS];
 static struct lock_class_key rcu_fqs_class[RCU_NUM_LVLS];
 static struct lock_class_key rcu_exp_class[RCU_NUM_LVLS];
-static struct lock_class_key rcu_exp_sched_class[RCU_NUM_LVLS];
 
 /*
  * In order to export the rcu_state name to the tracing tools, it
@@ -98,7 +97,7 @@ struct rcu_state sname##_state = { \
        .level = { &sname##_state.node[0] }, \
        .rda = &sname##_data, \
        .call = cr, \
-       .fqs_state = RCU_GP_IDLE, \
+       .gp_state = RCU_GP_IDLE, \
        .gpnum = 0UL - 300UL, \
        .completed = 0UL - 300UL, \
        .orphan_lock = __RAW_SPIN_LOCK_UNLOCKED(&sname##_state.orphan_lock), \
@@ -161,6 +160,8 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf);
 static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu);
 static void invoke_rcu_core(void);
 static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp);
+static void rcu_report_exp_rdp(struct rcu_state *rsp,
+                              struct rcu_data *rdp, bool wake);
 
 /* rcuc/rcub kthread realtime priority */
 #ifdef CONFIG_RCU_KTHREAD_PRIO
@@ -245,21 +246,33 @@ static int rcu_gp_in_progress(struct rcu_state *rsp)
  */
 void rcu_sched_qs(void)
 {
-       if (!__this_cpu_read(rcu_sched_data.passed_quiesce)) {
+       unsigned long flags;
+
+       if (__this_cpu_read(rcu_sched_data.cpu_no_qs.s)) {
                trace_rcu_grace_period(TPS("rcu_sched"),
                                       __this_cpu_read(rcu_sched_data.gpnum),
                                       TPS("cpuqs"));
-               __this_cpu_write(rcu_sched_data.passed_quiesce, 1);
+               __this_cpu_write(rcu_sched_data.cpu_no_qs.b.norm, false);
+               if (!__this_cpu_read(rcu_sched_data.cpu_no_qs.b.exp))
+                       return;
+               local_irq_save(flags);
+               if (__this_cpu_read(rcu_sched_data.cpu_no_qs.b.exp)) {
+                       __this_cpu_write(rcu_sched_data.cpu_no_qs.b.exp, false);
+                       rcu_report_exp_rdp(&rcu_sched_state,
+                                          this_cpu_ptr(&rcu_sched_data),
+                                          true);
+               }
+               local_irq_restore(flags);
        }
 }
 
 void rcu_bh_qs(void)
 {
-       if (!__this_cpu_read(rcu_bh_data.passed_quiesce)) {
+       if (__this_cpu_read(rcu_bh_data.cpu_no_qs.s)) {
                trace_rcu_grace_period(TPS("rcu_bh"),
                                       __this_cpu_read(rcu_bh_data.gpnum),
                                       TPS("cpuqs"));
-               __this_cpu_write(rcu_bh_data.passed_quiesce, 1);
+               __this_cpu_write(rcu_bh_data.cpu_no_qs.b.norm, false);
        }
 }
 
@@ -337,12 +350,14 @@ static void rcu_momentary_dyntick_idle(void)
  */
 void rcu_note_context_switch(void)
 {
+       barrier(); /* Avoid RCU read-side critical sections leaking down. */
        trace_rcu_utilization(TPS("Start context switch"));
        rcu_sched_qs();
        rcu_preempt_note_context_switch();
        if (unlikely(raw_cpu_read(rcu_sched_qs_mask)))
                rcu_momentary_dyntick_idle();
        trace_rcu_utilization(TPS("End context switch"));
+       barrier(); /* Avoid RCU read-side critical sections leaking up. */
 }
 EXPORT_SYMBOL_GPL(rcu_note_context_switch);
 
@@ -353,12 +368,19 @@ EXPORT_SYMBOL_GPL(rcu_note_context_switch);
  * RCU flavors in desperate need of a quiescent state, which will normally
  * be none of them).  Either way, do a lightweight quiescent state for
  * all RCU flavors.
+ *
+ * The barrier() calls are redundant in the common case when this is
+ * called externally, but just in case this is called from within this
+ * file.
+ *
  */
 void rcu_all_qs(void)
 {
+       barrier(); /* Avoid RCU read-side critical sections leaking down. */
        if (unlikely(raw_cpu_read(rcu_sched_qs_mask)))
                rcu_momentary_dyntick_idle();
        this_cpu_inc(rcu_qs_ctr);
+       barrier(); /* Avoid RCU read-side critical sections leaking up. */
 }
 EXPORT_SYMBOL_GPL(rcu_all_qs);
 
@@ -1744,9 +1766,9 @@ static bool __note_gp_changes(struct rcu_state *rsp, struct rcu_node *rnp,
                 */
                rdp->gpnum = rnp->gpnum;
                trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpustart"));
-               rdp->passed_quiesce = 0;
+               rdp->cpu_no_qs.b.norm = true;
                rdp->rcu_qs_ctr_snap = __this_cpu_read(rcu_qs_ctr);
-               rdp->qs_pending = !!(rnp->qsmask & rdp->grpmask);
+               rdp->core_needs_qs = !!(rnp->qsmask & rdp->grpmask);
                zero_cpu_stall_ticks(rdp);
                WRITE_ONCE(rdp->gpwrap, false);
        }
@@ -1927,16 +1949,15 @@ static bool rcu_gp_fqs_check_wake(struct rcu_state *rsp, int *gfp)
 /*
  * Do one round of quiescent-state forcing.
  */
-static int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in)
+static void rcu_gp_fqs(struct rcu_state *rsp, bool first_time)
 {
-       int fqs_state = fqs_state_in;
        bool isidle = false;
        unsigned long maxj;
        struct rcu_node *rnp = rcu_get_root(rsp);
 
        WRITE_ONCE(rsp->gp_activity, jiffies);
        rsp->n_force_qs++;
-       if (fqs_state == RCU_SAVE_DYNTICK) {
+       if (first_time) {
                /* Collect dyntick-idle snapshots. */
                if (is_sysidle_rcu_state(rsp)) {
                        isidle = true;
@@ -1945,7 +1966,6 @@ static int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in)
                force_qs_rnp(rsp, dyntick_save_progress_counter,
                             &isidle, &maxj);
                rcu_sysidle_report_gp(rsp, isidle, maxj);
-               fqs_state = RCU_FORCE_QS;
        } else {
                /* Handle dyntick-idle and offline CPUs. */
                isidle = true;
@@ -1959,7 +1979,6 @@ static int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in)
                           READ_ONCE(rsp->gp_flags) & ~RCU_GP_FLAG_FQS);
                raw_spin_unlock_irq(&rnp->lock);
        }
-       return fqs_state;
 }
 
 /*
@@ -2023,7 +2042,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)
        /* Declare grace period done. */
        WRITE_ONCE(rsp->completed, rsp->gpnum);
        trace_rcu_grace_period(rsp->name, rsp->completed, TPS("end"));
-       rsp->fqs_state = RCU_GP_IDLE;
+       rsp->gp_state = RCU_GP_IDLE;
        rdp = this_cpu_ptr(rsp->rda);
        /* Advance CBs to reduce false positives below. */
        needgp = rcu_advance_cbs(rsp, rnp, rdp) || needgp;
@@ -2041,7 +2060,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)
  */
 static int __noreturn rcu_gp_kthread(void *arg)
 {
-       int fqs_state;
+       bool first_gp_fqs;
        int gf;
        unsigned long j;
        int ret;
@@ -2073,7 +2092,7 @@ static int __noreturn rcu_gp_kthread(void *arg)
                }
 
                /* Handle quiescent-state forcing. */
-               fqs_state = RCU_SAVE_DYNTICK;
+               first_gp_fqs = true;
                j = jiffies_till_first_fqs;
                if (j > HZ) {
                        j = HZ;
@@ -2101,7 +2120,8 @@ static int __noreturn rcu_gp_kthread(void *arg)
                                trace_rcu_grace_period(rsp->name,
                                                       READ_ONCE(rsp->gpnum),
                                                       TPS("fqsstart"));
-                               fqs_state = rcu_gp_fqs(rsp, fqs_state);
+                               rcu_gp_fqs(rsp, first_gp_fqs);
+                               first_gp_fqs = false;
                                trace_rcu_grace_period(rsp->name,
                                                       READ_ONCE(rsp->gpnum),
                                                       TPS("fqsend"));
@@ -2337,7 +2357,7 @@ rcu_report_qs_rdp(int cpu, struct rcu_state *rsp, struct rcu_data *rdp)
        rnp = rdp->mynode;
        raw_spin_lock_irqsave(&rnp->lock, flags);
        smp_mb__after_unlock_lock();
-       if ((rdp->passed_quiesce == 0 &&
+       if ((rdp->cpu_no_qs.b.norm &&
             rdp->rcu_qs_ctr_snap == __this_cpu_read(rcu_qs_ctr)) ||
            rdp->gpnum != rnp->gpnum || rnp->completed == rnp->gpnum ||
            rdp->gpwrap) {
@@ -2348,7 +2368,7 @@ rcu_report_qs_rdp(int cpu, struct rcu_state *rsp, struct rcu_data *rdp)
                 * We will instead need a new quiescent state that lies
                 * within the current grace period.
                 */
-               rdp->passed_quiesce = 0;        /* need qs for new gp. */
+               rdp->cpu_no_qs.b.norm = true;   /* need qs for new gp. */
                rdp->rcu_qs_ctr_snap = __this_cpu_read(rcu_qs_ctr);
                raw_spin_unlock_irqrestore(&rnp->lock, flags);
                return;
@@ -2357,7 +2377,7 @@ rcu_report_qs_rdp(int cpu, struct rcu_state *rsp, struct rcu_data *rdp)
        if ((rnp->qsmask & mask) == 0) {
                raw_spin_unlock_irqrestore(&rnp->lock, flags);
        } else {
-               rdp->qs_pending = 0;
+               rdp->core_needs_qs = 0;
 
                /*
                 * This GP can't end until cpu checks in, so all of our
@@ -2388,14 +2408,14 @@ rcu_check_quiescent_state(struct rcu_state *rsp, struct rcu_data *rdp)
         * Does this CPU still need to do its part for current grace period?
         * If no, return and let the other CPUs do their part as well.
         */
-       if (!rdp->qs_pending)
+       if (!rdp->core_needs_qs)
                return;
 
        /*
         * Was there a quiescent state since the beginning of the grace
         * period? If no, then exit and wait for the next call.
         */
-       if (!rdp->passed_quiesce &&
+       if (rdp->cpu_no_qs.b.norm &&
            rdp->rcu_qs_ctr_snap == __this_cpu_read(rcu_qs_ctr))
                return;
 
@@ -3017,7 +3037,7 @@ static void rcu_leak_callback(struct rcu_head *rhp)
  * is expected to specify a CPU.
  */
 static void
-__call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
+__call_rcu(struct rcu_head *head, rcu_callback_t func,
           struct rcu_state *rsp, int cpu, bool lazy)
 {
        unsigned long flags;
@@ -3088,7 +3108,7 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
 /*
  * Queue an RCU-sched callback for invocation after a grace period.
  */
-void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
+void call_rcu_sched(struct rcu_head *head, rcu_callback_t func)
 {
        __call_rcu(head, func, &rcu_sched_state, -1, 0);
 }
@@ -3097,7 +3117,7 @@ EXPORT_SYMBOL_GPL(call_rcu_sched);
 /*
  * Queue an RCU callback for invocation after a quicker grace period.
  */
-void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
+void call_rcu_bh(struct rcu_head *head, rcu_callback_t func)
 {
        __call_rcu(head, func, &rcu_bh_state, -1, 0);
 }
@@ -3111,7 +3131,7 @@ EXPORT_SYMBOL_GPL(call_rcu_bh);
  * function may only be called from __kfree_rcu().
  */
 void kfree_call_rcu(struct rcu_head *head,
-                   void (*func)(struct rcu_head *rcu))
+                   rcu_callback_t func)
 {
        __call_rcu(head, func, rcu_state_p, -1, 1);
 }
@@ -3379,6 +3399,191 @@ static bool rcu_exp_gp_seq_done(struct rcu_state *rsp, unsigned long s)
        return rcu_seq_done(&rsp->expedited_sequence, s);
 }
 
+/*
+ * Reset the ->expmaskinit values in the rcu_node tree to reflect any
+ * recent CPU-online activity.  Note that these masks are not cleared
+ * when CPUs go offline, so they reflect the union of all CPUs that have
+ * ever been online.  This means that this function normally takes its
+ * no-work-to-do fastpath.
+ */
+static void sync_exp_reset_tree_hotplug(struct rcu_state *rsp)
+{
+       bool done;
+       unsigned long flags;
+       unsigned long mask;
+       unsigned long oldmask;
+       int ncpus = READ_ONCE(rsp->ncpus);
+       struct rcu_node *rnp;
+       struct rcu_node *rnp_up;
+
+       /* If no new CPUs onlined since last time, nothing to do. */
+       if (likely(ncpus == rsp->ncpus_snap))
+               return;
+       rsp->ncpus_snap = ncpus;
+
+       /*
+        * Each pass through the following loop propagates newly onlined
+        * CPUs for the current rcu_node structure up the rcu_node tree.
+        */
+       rcu_for_each_leaf_node(rsp, rnp) {
+               raw_spin_lock_irqsave(&rnp->lock, flags);
+               smp_mb__after_unlock_lock();
+               if (rnp->expmaskinit == rnp->expmaskinitnext) {
+                       raw_spin_unlock_irqrestore(&rnp->lock, flags);
+                       continue;  /* No new CPUs, nothing to do. */
+               }
+
+               /* Update this node's mask, track old value for propagation. */
+               oldmask = rnp->expmaskinit;
+               rnp->expmaskinit = rnp->expmaskinitnext;
+               raw_spin_unlock_irqrestore(&rnp->lock, flags);
+
+               /* If was already nonzero, nothing to propagate. */
+               if (oldmask)
+                       continue;
+
+               /* Propagate the new CPU up the tree. */
+               mask = rnp->grpmask;
+               rnp_up = rnp->parent;
+               done = false;
+               while (rnp_up) {
+                       raw_spin_lock_irqsave(&rnp_up->lock, flags);
+                       smp_mb__after_unlock_lock();
+                       if (rnp_up->expmaskinit)
+                               done = true;
+                       rnp_up->expmaskinit |= mask;
+                       raw_spin_unlock_irqrestore(&rnp_up->lock, flags);
+                       if (done)
+                               break;
+                       mask = rnp_up->grpmask;
+                       rnp_up = rnp_up->parent;
+               }
+       }
+}
+
+/*
+ * Reset the ->expmask values in the rcu_node tree in preparation for
+ * a new expedited grace period.
+ */
+static void __maybe_unused sync_exp_reset_tree(struct rcu_state *rsp)
+{
+       unsigned long flags;
+       struct rcu_node *rnp;
+
+       sync_exp_reset_tree_hotplug(rsp);
+       rcu_for_each_node_breadth_first(rsp, rnp) {
+               raw_spin_lock_irqsave(&rnp->lock, flags);
+               smp_mb__after_unlock_lock();
+               WARN_ON_ONCE(rnp->expmask);
+               rnp->expmask = rnp->expmaskinit;
+               raw_spin_unlock_irqrestore(&rnp->lock, flags);
+       }
+}
+
+/*
+ * Return non-zero if there is no RCU expedited grace period in progress
+ * for the specified rcu_node structure, in other words, if all CPUs and
+ * tasks covered by the specified rcu_node structure have done their bit
+ * for the current expedited grace period.  Works only for preemptible
+ * RCU -- other RCU implementation use other means.
+ *
+ * Caller must hold the root rcu_node's exp_funnel_mutex.
+ */
+static int sync_rcu_preempt_exp_done(struct rcu_node *rnp)
+{
+       return rnp->exp_tasks == NULL &&
+              READ_ONCE(rnp->expmask) == 0;
+}
+
+/*
+ * Report the exit from RCU read-side critical section for the last task
+ * that queued itself during or before the current expedited preemptible-RCU
+ * grace period.  This event is reported either to the rcu_node structure on
+ * which the task was queued or to one of that rcu_node structure's ancestors,
+ * recursively up the tree.  (Calm down, calm down, we do the recursion
+ * iteratively!)
+ *
+ * Caller must hold the root rcu_node's exp_funnel_mutex and the
+ * specified rcu_node structure's ->lock.
+ */
+static void __rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp,
+                                bool wake, unsigned long flags)
+       __releases(rnp->lock)
+{
+       unsigned long mask;
+
+       for (;;) {
+               if (!sync_rcu_preempt_exp_done(rnp)) {
+                       if (!rnp->expmask)
+                               rcu_initiate_boost(rnp, flags);
+                       else
+                               raw_spin_unlock_irqrestore(&rnp->lock, flags);
+                       break;
+               }
+               if (rnp->parent == NULL) {
+                       raw_spin_unlock_irqrestore(&rnp->lock, flags);
+                       if (wake) {
+                               smp_mb(); /* EGP done before wake_up(). */
+                               wake_up(&rsp->expedited_wq);
+                       }
+                       break;
+               }
+               mask = rnp->grpmask;
+               raw_spin_unlock(&rnp->lock); /* irqs remain disabled */
+               rnp = rnp->parent;
+               raw_spin_lock(&rnp->lock); /* irqs already disabled */
+               smp_mb__after_unlock_lock();
+               WARN_ON_ONCE(!(rnp->expmask & mask));
+               rnp->expmask &= ~mask;
+       }
+}
+
+/*
+ * Report expedited quiescent state for specified node.  This is a
+ * lock-acquisition wrapper function for __rcu_report_exp_rnp().
+ *
+ * Caller must hold the root rcu_node's exp_funnel_mutex.
+ */
+static void __maybe_unused rcu_report_exp_rnp(struct rcu_state *rsp,
+                                             struct rcu_node *rnp, bool wake)
+{
+       unsigned long flags;
+
+       raw_spin_lock_irqsave(&rnp->lock, flags);
+       smp_mb__after_unlock_lock();
+       __rcu_report_exp_rnp(rsp, rnp, wake, flags);
+}
+
+/*
+ * Report expedited quiescent state for multiple CPUs, all covered by the
+ * specified leaf rcu_node structure.  Caller must hold the root
+ * rcu_node's exp_funnel_mutex.
+ */
+static void rcu_report_exp_cpu_mult(struct rcu_state *rsp, struct rcu_node *rnp,
+                                   unsigned long mask, bool wake)
+{
+       unsigned long flags;
+
+       raw_spin_lock_irqsave(&rnp->lock, flags);
+       smp_mb__after_unlock_lock();
+       if (!(rnp->expmask & mask)) {
+               raw_spin_unlock_irqrestore(&rnp->lock, flags);
+               return;
+       }
+       rnp->expmask &= ~mask;
+       __rcu_report_exp_rnp(rsp, rnp, wake, flags); /* Releases rnp->lock. */
+}
+
+/*
+ * Report expedited quiescent state for specified rcu_data (CPU).
+ * Caller must hold the root rcu_node's exp_funnel_mutex.
+ */
+static void rcu_report_exp_rdp(struct rcu_state *rsp, struct rcu_data *rdp,
+                              bool wake)
+{
+       rcu_report_exp_cpu_mult(rsp, rdp->mynode, rdp->grpmask, wake);
+}
+
 /* Common code for synchronize_{rcu,sched}_expedited() work-done checking. */
 static bool sync_exp_work_done(struct rcu_state *rsp, struct rcu_node *rnp,
                               struct rcu_data *rdp,
@@ -3455,16 +3660,111 @@ static struct rcu_node *exp_funnel_lock(struct rcu_state *rsp, unsigned long s)
 }
 
 /* Invoked on each online non-idle CPU for expedited quiescent state. */
-static int synchronize_sched_expedited_cpu_stop(void *data)
+static void sync_sched_exp_handler(void *data)
 {
-       struct rcu_data *rdp = data;
-       struct rcu_state *rsp = rdp->rsp;
+       struct rcu_data *rdp;
+       struct rcu_node *rnp;
+       struct rcu_state *rsp = data;
 
-       /* We are here: If we are last, do the wakeup. */
-       rdp->exp_done = true;
-       if (atomic_dec_and_test(&rsp->expedited_need_qs))
-               wake_up(&rsp->expedited_wq);
-       return 0;
+       rdp = this_cpu_ptr(rsp->rda);
+       rnp = rdp->mynode;
+       if (!(READ_ONCE(rnp->expmask) & rdp->grpmask) ||
+           __this_cpu_read(rcu_sched_data.cpu_no_qs.b.exp))
+               return;
+       __this_cpu_write(rcu_sched_data.cpu_no_qs.b.exp, true);
+       resched_cpu(smp_processor_id());
+}
+
+/* Send IPI for expedited cleanup if needed at end of CPU-hotplug operation. */
+static void sync_sched_exp_online_cleanup(int cpu)
+{
+       struct rcu_data *rdp;
+       int ret;
+       struct rcu_node *rnp;
+       struct rcu_state *rsp = &rcu_sched_state;
+
+       rdp = per_cpu_ptr(rsp->rda, cpu);
+       rnp = rdp->mynode;
+       if (!(READ_ONCE(rnp->expmask) & rdp->grpmask))
+               return;
+       ret = smp_call_function_single(cpu, sync_sched_exp_handler, rsp, 0);
+       WARN_ON_ONCE(ret);
+}
+
+/*
+ * Select the nodes that the upcoming expedited grace period needs
+ * to wait for.
+ */
+static void sync_rcu_exp_select_cpus(struct rcu_state *rsp,
+                                    smp_call_func_t func)
+{
+       int cpu;
+       unsigned long flags;
+       unsigned long mask;
+       unsigned long mask_ofl_test;
+       unsigned long mask_ofl_ipi;
+       int ret;
+       struct rcu_node *rnp;
+
+       sync_exp_reset_tree(rsp);
+       rcu_for_each_leaf_node(rsp, rnp) {
+               raw_spin_lock_irqsave(&rnp->lock, flags);
+               smp_mb__after_unlock_lock();
+
+               /* Each pass checks a CPU for identity, offline, and idle. */
+               mask_ofl_test = 0;
+               for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++) {
+                       struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
+                       struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
+
+                       if (raw_smp_processor_id() == cpu ||
+                           !(atomic_add_return(0, &rdtp->dynticks) & 0x1))
+                               mask_ofl_test |= rdp->grpmask;
+               }
+               mask_ofl_ipi = rnp->expmask & ~mask_ofl_test;
+
+               /*
+                * Need to wait for any blocked tasks as well.  Note that
+                * additional blocking tasks will also block the expedited
+                * GP until such time as the ->expmask bits are cleared.
+                */
+               if (rcu_preempt_has_tasks(rnp))
+                       rnp->exp_tasks = rnp->blkd_tasks.next;
+               raw_spin_unlock_irqrestore(&rnp->lock, flags);
+
+               /* IPI the remaining CPUs for expedited quiescent state. */
+               mask = 1;
+               for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++, mask <<= 1) {
+                       if (!(mask_ofl_ipi & mask))
+                               continue;
+retry_ipi:
+                       ret = smp_call_function_single(cpu, func, rsp, 0);
+                       if (!ret) {
+                               mask_ofl_ipi &= ~mask;
+                       } else {
+                               /* Failed, raced with offline. */
+                               raw_spin_lock_irqsave(&rnp->lock, flags);
+                               if (cpu_online(cpu) &&
+                                   (rnp->expmask & mask)) {
+                                       raw_spin_unlock_irqrestore(&rnp->lock,
+                                                                  flags);
+                                       schedule_timeout_uninterruptible(1);
+                                       if (cpu_online(cpu) &&
+                                           (rnp->expmask & mask))
+                                               goto retry_ipi;
+                                       raw_spin_lock_irqsave(&rnp->lock,
+                                                             flags);
+                               }
+                               if (!(rnp->expmask & mask))
+                                       mask_ofl_ipi &= ~mask;
+                               raw_spin_unlock_irqrestore(&rnp->lock, flags);
+                       }
+               }
+               /* Report quiescent states for those that went offline. */
+               mask_ofl_test |= mask_ofl_ipi;
+               if (mask_ofl_test)
+                       rcu_report_exp_cpu_mult(rsp, rnp, mask_ofl_test, false);
+       }
 }
 
 static void synchronize_sched_expedited_wait(struct rcu_state *rsp)
@@ -3472,7 +3772,9 @@ static void synchronize_sched_expedited_wait(struct rcu_state *rsp)
        int cpu;
        unsigned long jiffies_stall;
        unsigned long jiffies_start;
-       struct rcu_data *rdp;
+       unsigned long mask;
+       struct rcu_node *rnp;
+       struct rcu_node *rnp_root = rcu_get_root(rsp);
        int ret;
 
        jiffies_stall = rcu_jiffies_till_stall_check();
@@ -3481,33 +3783,43 @@ static void synchronize_sched_expedited_wait(struct rcu_state *rsp)
        for (;;) {
                ret = wait_event_interruptible_timeout(
                                rsp->expedited_wq,
-                               !atomic_read(&rsp->expedited_need_qs),
+                               sync_rcu_preempt_exp_done(rnp_root),
                                jiffies_stall);
                if (ret > 0)
                        return;
                if (ret < 0) {
                        /* Hit a signal, disable CPU stall warnings. */
                        wait_event(rsp->expedited_wq,
-                                  !atomic_read(&rsp->expedited_need_qs));
+                                  sync_rcu_preempt_exp_done(rnp_root));
                        return;
                }
-               pr_err("INFO: %s detected expedited stalls on CPUs: {",
+               pr_err("INFO: %s detected expedited stalls on CPUs/tasks: {",
                       rsp->name);
-               for_each_online_cpu(cpu) {
-                       rdp = per_cpu_ptr(rsp->rda, cpu);
-
-                       if (rdp->exp_done)
-                               continue;
-                       pr_cont(" %d", cpu);
+               rcu_for_each_leaf_node(rsp, rnp) {
+                       (void)rcu_print_task_exp_stall(rnp);
+                       mask = 1;
+                       for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++, mask <<= 1) {
+                               struct rcu_data *rdp;
+
+                               if (!(rnp->expmask & mask))
+                                       continue;
+                               rdp = per_cpu_ptr(rsp->rda, cpu);
+                               pr_cont(" %d-%c%c%c", cpu,
+                                       "O."[cpu_online(cpu)],
+                                       "o."[!!(rdp->grpmask & rnp->expmaskinit)],
+                                       "N."[!!(rdp->grpmask & rnp->expmaskinitnext)]);
+                       }
+                       mask <<= 1;
                }
                pr_cont(" } %lu jiffies s: %lu\n",
                        jiffies - jiffies_start, rsp->expedited_sequence);
-               for_each_online_cpu(cpu) {
-                       rdp = per_cpu_ptr(rsp->rda, cpu);
-
-                       if (rdp->exp_done)
-                               continue;
-                       dump_cpu_task(cpu);
+               rcu_for_each_leaf_node(rsp, rnp) {
+                       mask = 1;
+                       for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++, mask <<= 1) {
+                               if (!(rnp->expmask & mask))
+                                       continue;
+                               dump_cpu_task(cpu);
+                       }
                }
                jiffies_stall = 3 * rcu_jiffies_till_stall_check() + 3;
        }
@@ -3531,7 +3843,6 @@ static void synchronize_sched_expedited_wait(struct rcu_state *rsp)
  */
 void synchronize_sched_expedited(void)
 {
-       int cpu;
        unsigned long s;
        struct rcu_node *rnp;
        struct rcu_state *rsp = &rcu_sched_state;
@@ -3539,48 +3850,16 @@ void synchronize_sched_expedited(void)
        /* Take a snapshot of the sequence number.  */
        s = rcu_exp_gp_seq_snap(rsp);
 
-       if (!try_get_online_cpus()) {
-               /* CPU hotplug operation in flight, fall back to normal GP. */
-               wait_rcu_gp(call_rcu_sched);
-               atomic_long_inc(&rsp->expedited_normal);
-               return;
-       }
-       WARN_ON_ONCE(cpu_is_offline(raw_smp_processor_id()));
-
        rnp = exp_funnel_lock(rsp, s);
-       if (rnp == NULL) {
-               put_online_cpus();
+       if (rnp == NULL)
                return;  /* Someone else did our work for us. */
-       }
 
        rcu_exp_gp_seq_start(rsp);
-
-       /* Stop each CPU that is online, non-idle, and not us. */
-       init_waitqueue_head(&rsp->expedited_wq);
-       atomic_set(&rsp->expedited_need_qs, 1); /* Extra count avoids race. */
-       for_each_online_cpu(cpu) {
-               struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
-               struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
-
-               rdp->exp_done = false;
-
-               /* Skip our CPU and any idle CPUs. */
-               if (raw_smp_processor_id() == cpu ||
-                   !(atomic_add_return(0, &rdtp->dynticks) & 0x1))
-                       continue;
-               atomic_inc(&rsp->expedited_need_qs);
-               stop_one_cpu_nowait(cpu, synchronize_sched_expedited_cpu_stop,
-                                   rdp, &rdp->exp_stop_work);
-       }
-
-       /* Remove extra count and, if necessary, wait for CPUs to stop. */
-       if (!atomic_dec_and_test(&rsp->expedited_need_qs))
-               synchronize_sched_expedited_wait(rsp);
+       sync_rcu_exp_select_cpus(rsp, sync_sched_exp_handler);
+       synchronize_sched_expedited_wait(rsp);
 
        rcu_exp_gp_seq_end(rsp);
        mutex_unlock(&rnp->exp_funnel_mutex);
-
-       put_online_cpus();
 }
 EXPORT_SYMBOL_GPL(synchronize_sched_expedited);
 
@@ -3606,11 +3885,11 @@ static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp)
 
        /* Is the RCU core waiting for a quiescent state from this CPU? */
        if (rcu_scheduler_fully_active &&
-           rdp->qs_pending && !rdp->passed_quiesce &&
+           rdp->core_needs_qs && rdp->cpu_no_qs.b.norm &&
            rdp->rcu_qs_ctr_snap == __this_cpu_read(rcu_qs_ctr)) {
-               rdp->n_rp_qs_pending++;
-       } else if (rdp->qs_pending &&
-                  (rdp->passed_quiesce ||
+               rdp->n_rp_core_needs_qs++;
+       } else if (rdp->core_needs_qs &&
+                  (!rdp->cpu_no_qs.b.norm ||
                    rdp->rcu_qs_ctr_snap != __this_cpu_read(rcu_qs_ctr))) {
                rdp->n_rp_report_qs++;
                return 1;
@@ -3868,7 +4147,6 @@ static void rcu_init_new_rnp(struct rcu_node *rnp_leaf)
 static void __init
 rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp)
 {
-       static struct lock_class_key rcu_exp_sched_rdp_class;
        unsigned long flags;
        struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
        struct rcu_node *rnp = rcu_get_root(rsp);
@@ -3884,10 +4162,6 @@ rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp)
        mutex_init(&rdp->exp_funnel_mutex);
        rcu_boot_init_nocb_percpu_data(rdp);
        raw_spin_unlock_irqrestore(&rnp->lock, flags);
-       if (rsp == &rcu_sched_state)
-               lockdep_set_class_and_name(&rdp->exp_funnel_mutex,
-                                          &rcu_exp_sched_rdp_class,
-                                          "rcu_data_exp_sched");
 }
 
 /*
@@ -3906,7 +4180,6 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
 
        /* Set up local state, ensuring consistent view of global state. */
        raw_spin_lock_irqsave(&rnp->lock, flags);
-       rdp->beenonline = 1;     /* We have now been online. */
        rdp->qlen_last_fqs_check = 0;
        rdp->n_force_qs_snap = rsp->n_force_qs;
        rdp->blimit = blimit;
@@ -3928,11 +4201,15 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
        raw_spin_lock(&rnp->lock);              /* irqs already disabled. */
        smp_mb__after_unlock_lock();
        rnp->qsmaskinitnext |= mask;
+       rnp->expmaskinitnext |= mask;
+       if (!rdp->beenonline)
+               WRITE_ONCE(rsp->ncpus, READ_ONCE(rsp->ncpus) + 1);
+       rdp->beenonline = true;  /* We have now been online. */
        rdp->gpnum = rnp->completed; /* Make CPU later note any new GP. */
        rdp->completed = rnp->completed;
-       rdp->passed_quiesce = false;
+       rdp->cpu_no_qs.b.norm = true;
        rdp->rcu_qs_ctr_snap = per_cpu(rcu_qs_ctr, cpu);
-       rdp->qs_pending = false;
+       rdp->core_needs_qs = false;
        trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpuonl"));
        raw_spin_unlock_irqrestore(&rnp->lock, flags);
 }
@@ -3965,6 +4242,7 @@ int rcu_cpu_notify(struct notifier_block *self,
                break;
        case CPU_ONLINE:
        case CPU_DOWN_FAILED:
+               sync_sched_exp_online_cleanup(cpu);
                rcu_boost_kthread_setaffinity(rnp, -1);
                break;
        case CPU_DOWN_PREPARE:
@@ -3976,6 +4254,12 @@ int rcu_cpu_notify(struct notifier_block *self,
                        rcu_cleanup_dying_cpu(rsp);
                break;
        case CPU_DYING_IDLE:
+               /* QS for any half-done expedited RCU-sched GP. */
+               preempt_disable();
+               rcu_report_exp_rdp(&rcu_sched_state,
+                                  this_cpu_ptr(rcu_sched_state.rda), true);
+               preempt_enable();
+
                for_each_rcu_flavor(rsp) {
                        rcu_cleanup_dying_idle_cpu(cpu, rsp);
                }
@@ -4107,7 +4391,6 @@ static void __init rcu_init_one(struct rcu_state *rsp,
        static const char * const buf[] = RCU_NODE_NAME_INIT;
        static const char * const fqs[] = RCU_FQS_NAME_INIT;
        static const char * const exp[] = RCU_EXP_NAME_INIT;
-       static const char * const exp_sched[] = RCU_EXP_SCHED_NAME_INIT;
        static u8 fl_mask = 0x1;
 
        int levelcnt[RCU_NUM_LVLS];             /* # nodes in each level. */
@@ -4167,18 +4450,13 @@ static void __init rcu_init_one(struct rcu_state *rsp,
                        INIT_LIST_HEAD(&rnp->blkd_tasks);
                        rcu_init_one_nocb(rnp);
                        mutex_init(&rnp->exp_funnel_mutex);
-                       if (rsp == &rcu_sched_state)
-                               lockdep_set_class_and_name(
-                                       &rnp->exp_funnel_mutex,
-                                       &rcu_exp_sched_class[i], exp_sched[i]);
-                       else
-                               lockdep_set_class_and_name(
-                                       &rnp->exp_funnel_mutex,
-                                       &rcu_exp_class[i], exp[i]);
+                       lockdep_set_class_and_name(&rnp->exp_funnel_mutex,
+                                                  &rcu_exp_class[i], exp[i]);
                }
        }
 
        init_waitqueue_head(&rsp->gp_wq);
+       init_waitqueue_head(&rsp->expedited_wq);
        rnp = rsp->level[rcu_num_lvls - 1];
        for_each_possible_cpu(i) {
                while (i > rnp->grphi)
@@ -4221,13 +4499,12 @@ static void __init rcu_init_geometry(void)
                rcu_fanout_leaf, nr_cpu_ids);
 
        /*
-        * The boot-time rcu_fanout_leaf parameter is only permitted
-        * to increase the leaf-level fanout, not decrease it.  Of course,
-        * the leaf-level fanout cannot exceed the number of bits in
-        * the rcu_node masks.  Complain and fall back to the compile-
-        * time values if these limits are exceeded.
+        * The boot-time rcu_fanout_leaf parameter must be at least two
+        * and cannot exceed the number of bits in the rcu_node masks.
+        * Complain and fall back to the compile-time values if this
+        * limit is exceeded.
         */
-       if (rcu_fanout_leaf < RCU_FANOUT_LEAF ||
+       if (rcu_fanout_leaf < 2 ||
            rcu_fanout_leaf > sizeof(unsigned long) * 8) {
                rcu_fanout_leaf = RCU_FANOUT_LEAF;
                WARN_ON(1);
@@ -4244,10 +4521,13 @@ static void __init rcu_init_geometry(void)
 
        /*
         * The tree must be able to accommodate the configured number of CPUs.
-        * If this limit is exceeded than we have a serious problem elsewhere.
+        * If this limit is exceeded, fall back to the compile-time values.
         */
-       if (nr_cpu_ids > rcu_capacity[RCU_NUM_LVLS - 1])
-               panic("rcu_init_geometry: rcu_capacity[] is too small");
+       if (nr_cpu_ids > rcu_capacity[RCU_NUM_LVLS - 1]) {
+               rcu_fanout_leaf = RCU_FANOUT_LEAF;
+               WARN_ON(1);
+               return;
+       }
 
        /* Calculate the number of levels in the tree. */
        for (i = 0; nr_cpu_ids > rcu_capacity[i]; i++) {
index 2e991f8361e4203a85d7b8e55afc6be370d7d7be..9fb4e238d4dcaaed1565dd7bf08a3c71a7f518bf 100644 (file)
@@ -70,8 +70,6 @@
 #  define RCU_NODE_NAME_INIT  { "rcu_node_0" }
 #  define RCU_FQS_NAME_INIT   { "rcu_node_fqs_0" }
 #  define RCU_EXP_NAME_INIT   { "rcu_node_exp_0" }
-#  define RCU_EXP_SCHED_NAME_INIT \
-                             { "rcu_node_exp_sched_0" }
 #elif NR_CPUS <= RCU_FANOUT_2
 #  define RCU_NUM_LVLS       2
 #  define NUM_RCU_LVL_0              1
@@ -81,8 +79,6 @@
 #  define RCU_NODE_NAME_INIT  { "rcu_node_0", "rcu_node_1" }
 #  define RCU_FQS_NAME_INIT   { "rcu_node_fqs_0", "rcu_node_fqs_1" }
 #  define RCU_EXP_NAME_INIT   { "rcu_node_exp_0", "rcu_node_exp_1" }
-#  define RCU_EXP_SCHED_NAME_INIT \
-                             { "rcu_node_exp_sched_0", "rcu_node_exp_sched_1" }
 #elif NR_CPUS <= RCU_FANOUT_3
 #  define RCU_NUM_LVLS       3
 #  define NUM_RCU_LVL_0              1
@@ -93,8 +89,6 @@
 #  define RCU_NODE_NAME_INIT  { "rcu_node_0", "rcu_node_1", "rcu_node_2" }
 #  define RCU_FQS_NAME_INIT   { "rcu_node_fqs_0", "rcu_node_fqs_1", "rcu_node_fqs_2" }
 #  define RCU_EXP_NAME_INIT   { "rcu_node_exp_0", "rcu_node_exp_1", "rcu_node_exp_2" }
-#  define RCU_EXP_SCHED_NAME_INIT \
-                             { "rcu_node_exp_sched_0", "rcu_node_exp_sched_1", "rcu_node_exp_sched_2" }
 #elif NR_CPUS <= RCU_FANOUT_4
 #  define RCU_NUM_LVLS       4
 #  define NUM_RCU_LVL_0              1
 #  define RCU_NODE_NAME_INIT  { "rcu_node_0", "rcu_node_1", "rcu_node_2", "rcu_node_3" }
 #  define RCU_FQS_NAME_INIT   { "rcu_node_fqs_0", "rcu_node_fqs_1", "rcu_node_fqs_2", "rcu_node_fqs_3" }
 #  define RCU_EXP_NAME_INIT   { "rcu_node_exp_0", "rcu_node_exp_1", "rcu_node_exp_2", "rcu_node_exp_3" }
-#  define RCU_EXP_SCHED_NAME_INIT \
-                             { "rcu_node_exp_sched_0", "rcu_node_exp_sched_1", "rcu_node_exp_sched_2", "rcu_node_exp_sched_3" }
 #else
 # error "CONFIG_RCU_FANOUT insufficient for NR_CPUS"
 #endif /* #if (NR_CPUS) <= RCU_FANOUT_1 */
@@ -171,16 +163,21 @@ struct rcu_node {
                                /*  an rcu_data structure, otherwise, each */
                                /*  bit corresponds to a child rcu_node */
                                /*  structure. */
-       unsigned long expmask;  /* Groups that have ->blkd_tasks */
-                               /*  elements that need to drain to allow the */
-                               /*  current expedited grace period to */
-                               /*  complete (only for PREEMPT_RCU). */
        unsigned long qsmaskinit;
-                               /* Per-GP initial value for qsmask & expmask. */
+                               /* Per-GP initial value for qsmask. */
                                /*  Initialized from ->qsmaskinitnext at the */
                                /*  beginning of each grace period. */
        unsigned long qsmaskinitnext;
                                /* Online CPUs for next grace period. */
+       unsigned long expmask;  /* CPUs or groups that need to check in */
+                               /*  to allow the current expedited GP */
+                               /*  to complete. */
+       unsigned long expmaskinit;
+                               /* Per-GP initial values for expmask. */
+                               /*  Initialized from ->expmaskinitnext at the */
+                               /*  beginning of each expedited GP. */
+       unsigned long expmaskinitnext;
+                               /* Online CPUs for next expedited GP. */
        unsigned long grpmask;  /* Mask to apply to parent qsmask. */
                                /*  Only one bit will be set in this mask. */
        int     grplo;          /* lowest-numbered CPU or group here. */
@@ -281,6 +278,18 @@ struct rcu_node {
        for ((rnp) = (rsp)->level[rcu_num_lvls - 1]; \
             (rnp) < &(rsp)->node[rcu_num_nodes]; (rnp)++)
 
+/*
+ * Union to allow "aggregate OR" operation on the need for a quiescent
+ * state by the normal and expedited grace periods.
+ */
+union rcu_noqs {
+       struct {
+               u8 norm;
+               u8 exp;
+       } b; /* Bits. */
+       u16 s; /* Set of bits, aggregate OR here. */
+};
+
 /* Index values for nxttail array in struct rcu_data. */
 #define RCU_DONE_TAIL          0       /* Also RCU_WAIT head. */
 #define RCU_WAIT_TAIL          1       /* Also RCU_NEXT_READY head. */
@@ -297,8 +306,8 @@ struct rcu_data {
                                        /*  is aware of having started. */
        unsigned long   rcu_qs_ctr_snap;/* Snapshot of rcu_qs_ctr to check */
                                        /*  for rcu_all_qs() invocations. */
-       bool            passed_quiesce; /* User-mode/idle loop etc. */
-       bool            qs_pending;     /* Core waits for quiesc state. */
+       union rcu_noqs  cpu_no_qs;      /* No QSes yet for this CPU. */
+       bool            core_needs_qs;  /* Core waits for quiesc state. */
        bool            beenonline;     /* CPU online at least once. */
        bool            gpwrap;         /* Possible gpnum/completed wrap. */
        struct rcu_node *mynode;        /* This CPU's leaf of hierarchy */
@@ -307,9 +316,6 @@ struct rcu_data {
                                        /*  ticks this CPU has handled */
                                        /*  during and after the last grace */
                                        /* period it is aware of. */
-       struct cpu_stop_work exp_stop_work;
-                                       /* Expedited grace-period control */
-                                       /*  for CPU stopping. */
 
        /* 2) batch handling */
        /*
@@ -363,7 +369,7 @@ struct rcu_data {
 
        /* 5) __rcu_pending() statistics. */
        unsigned long n_rcu_pending;    /* rcu_pending() calls since boot. */
-       unsigned long n_rp_qs_pending;
+       unsigned long n_rp_core_needs_qs;
        unsigned long n_rp_report_qs;
        unsigned long n_rp_cb_ready;
        unsigned long n_rp_cpu_needs_gp;
@@ -378,7 +384,6 @@ struct rcu_data {
        struct rcu_head oom_head;
 #endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */
        struct mutex exp_funnel_mutex;
-       bool exp_done;                  /* Expedited QS for this CPU? */
 
        /* 7) Callback offloading. */
 #ifdef CONFIG_RCU_NOCB_CPU
@@ -412,13 +417,6 @@ struct rcu_data {
        struct rcu_state *rsp;
 };
 
-/* Values for fqs_state field in struct rcu_state. */
-#define RCU_GP_IDLE            0       /* No grace period in progress. */
-#define RCU_GP_INIT            1       /* Grace period being initialized. */
-#define RCU_SAVE_DYNTICK       2       /* Need to scan dyntick state. */
-#define RCU_FORCE_QS           3       /* Need to force quiescent state. */
-#define RCU_SIGNAL_INIT                RCU_SAVE_DYNTICK
-
 /* Values for nocb_defer_wakeup field in struct rcu_data. */
 #define RCU_NOGP_WAKE_NOT      0
 #define RCU_NOGP_WAKE          1
@@ -464,14 +462,13 @@ struct rcu_state {
                                                /*  shut bogus gcc warning) */
        u8 flavor_mask;                         /* bit in flavor mask. */
        struct rcu_data __percpu *rda;          /* pointer of percu rcu_data. */
-       void (*call)(struct rcu_head *head,     /* call_rcu() flavor. */
-                    void (*func)(struct rcu_head *head));
+       call_rcu_func_t call;                   /* call_rcu() flavor. */
+       int ncpus;                              /* # CPUs seen so far. */
 
        /* The following fields are guarded by the root rcu_node's lock. */
 
-       u8      fqs_state ____cacheline_internodealigned_in_smp;
-                                               /* Force QS state. */
-       u8      boost;                          /* Subject to priority boost. */
+       u8      boost ____cacheline_internodealigned_in_smp;
+                                               /* Subject to priority boost. */
        unsigned long gpnum;                    /* Current gp number. */
        unsigned long completed;                /* # of last completed gp. */
        struct task_struct *gp_kthread;         /* Task for grace periods. */
@@ -508,6 +505,7 @@ struct rcu_state {
        atomic_long_t expedited_normal;         /* # fallbacks to normal. */
        atomic_t expedited_need_qs;             /* # CPUs left to check in. */
        wait_queue_head_t expedited_wq;         /* Wait for check-ins. */
+       int ncpus_snap;                         /* # CPUs seen last time. */
 
        unsigned long jiffies_force_qs;         /* Time at which to invoke */
                                                /*  force_quiescent_state(). */
@@ -538,8 +536,8 @@ struct rcu_state {
 #define RCU_GP_FLAG_INIT 0x1   /* Need grace-period initialization. */
 #define RCU_GP_FLAG_FQS  0x2   /* Need grace-period quiescent-state forcing. */
 
-/* Values for rcu_state structure's gp_flags field. */
-#define RCU_GP_WAIT_INIT 0     /* Initial state. */
+/* Values for rcu_state structure's gp_state field. */
+#define RCU_GP_IDLE     0      /* Initial state and no GP in progress. */
 #define RCU_GP_WAIT_GPS  1     /* Wait for grace-period start. */
 #define RCU_GP_DONE_GPS  2     /* Wait done for grace-period start. */
 #define RCU_GP_WAIT_FQS  3     /* Wait for force-quiescent-state time. */
@@ -582,9 +580,10 @@ static bool rcu_preempt_has_tasks(struct rcu_node *rnp);
 #endif /* #ifdef CONFIG_HOTPLUG_CPU */
 static void rcu_print_detail_task_stall(struct rcu_state *rsp);
 static int rcu_print_task_stall(struct rcu_node *rnp);
+static int rcu_print_task_exp_stall(struct rcu_node *rnp);
 static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp);
 static void rcu_preempt_check_callbacks(void);
-void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu));
+void call_rcu(struct rcu_head *head, rcu_callback_t func);
 static void __init __rcu_init_preempt(void);
 static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags);
 static void rcu_preempt_boost_start_gp(struct rcu_node *rnp);
index b2bf3963a0aee328d0d3bfc0d9e997b3a5ca106f..630c19772630cc0c2cac42e2cf751dec002e81e2 100644 (file)
@@ -101,7 +101,6 @@ RCU_STATE_INITIALIZER(rcu_preempt, 'p', call_rcu);
 static struct rcu_state *const rcu_state_p = &rcu_preempt_state;
 static struct rcu_data __percpu *const rcu_data_p = &rcu_preempt_data;
 
-static int rcu_preempted_readers_exp(struct rcu_node *rnp);
 static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp,
                               bool wake);
 
@@ -114,6 +113,147 @@ static void __init rcu_bootup_announce(void)
        rcu_bootup_announce_oddness();
 }
 
+/* Flags for rcu_preempt_ctxt_queue() decision table. */
+#define RCU_GP_TASKS   0x8
+#define RCU_EXP_TASKS  0x4
+#define RCU_GP_BLKD    0x2
+#define RCU_EXP_BLKD   0x1
+
+/*
+ * Queues a task preempted within an RCU-preempt read-side critical
+ * section into the appropriate location within the ->blkd_tasks list,
+ * depending on the states of any ongoing normal and expedited grace
+ * periods.  The ->gp_tasks pointer indicates which element the normal
+ * grace period is waiting on (NULL if none), and the ->exp_tasks pointer
+ * indicates which element the expedited grace period is waiting on (again,
+ * NULL if none).  If a grace period is waiting on a given element in the
+ * ->blkd_tasks list, it also waits on all subsequent elements.  Thus,
+ * adding a task to the tail of the list blocks any grace period that is
+ * already waiting on one of the elements.  In contrast, adding a task
+ * to the head of the list won't block any grace period that is already
+ * waiting on one of the elements.
+ *
+ * This queuing is imprecise, and can sometimes make an ongoing grace
+ * period wait for a task that is not strictly speaking blocking it.
+ * Given the choice, we needlessly block a normal grace period rather than
+ * blocking an expedited grace period.
+ *
+ * Note that an endless sequence of expedited grace periods still cannot
+ * indefinitely postpone a normal grace period.  Eventually, all of the
+ * fixed number of preempted tasks blocking the normal grace period that are
+ * not also blocking the expedited grace period will resume and complete
+ * their RCU read-side critical sections.  At that point, the ->gp_tasks
+ * pointer will equal the ->exp_tasks pointer, at which point the end of
+ * the corresponding expedited grace period will also be the end of the
+ * normal grace period.
+ */
+static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp,
+                                  unsigned long flags) __releases(rnp->lock)
+{
+       int blkd_state = (rnp->gp_tasks ? RCU_GP_TASKS : 0) +
+                        (rnp->exp_tasks ? RCU_EXP_TASKS : 0) +
+                        (rnp->qsmask & rdp->grpmask ? RCU_GP_BLKD : 0) +
+                        (rnp->expmask & rdp->grpmask ? RCU_EXP_BLKD : 0);
+       struct task_struct *t = current;
+
+       /*
+        * Decide where to queue the newly blocked task.  In theory,
+        * this could be an if-statement.  In practice, when I tried
+        * that, it was quite messy.
+        */
+       switch (blkd_state) {
+       case 0:
+       case                RCU_EXP_TASKS:
+       case                RCU_EXP_TASKS + RCU_GP_BLKD:
+       case RCU_GP_TASKS:
+       case RCU_GP_TASKS + RCU_EXP_TASKS:
+
+               /*
+                * Blocking neither GP, or first task blocking the normal
+                * GP but not blocking the already-waiting expedited GP.
+                * Queue at the head of the list to avoid unnecessarily
+                * blocking the already-waiting GPs.
+                */
+               list_add(&t->rcu_node_entry, &rnp->blkd_tasks);
+               break;
+
+       case                                              RCU_EXP_BLKD:
+       case                                RCU_GP_BLKD:
+       case                                RCU_GP_BLKD + RCU_EXP_BLKD:
+       case RCU_GP_TASKS +                               RCU_EXP_BLKD:
+       case RCU_GP_TASKS +                 RCU_GP_BLKD + RCU_EXP_BLKD:
+       case RCU_GP_TASKS + RCU_EXP_TASKS + RCU_GP_BLKD + RCU_EXP_BLKD:
+
+               /*
+                * First task arriving that blocks either GP, or first task
+                * arriving that blocks the expedited GP (with the normal
+                * GP already waiting), or a task arriving that blocks
+                * both GPs with both GPs already waiting.  Queue at the
+                * tail of the list to avoid any GP waiting on any of the
+                * already queued tasks that are not blocking it.
+                */
+               list_add_tail(&t->rcu_node_entry, &rnp->blkd_tasks);
+               break;
+
+       case                RCU_EXP_TASKS +               RCU_EXP_BLKD:
+       case                RCU_EXP_TASKS + RCU_GP_BLKD + RCU_EXP_BLKD:
+       case RCU_GP_TASKS + RCU_EXP_TASKS +               RCU_EXP_BLKD:
+
+               /*
+                * Second or subsequent task blocking the expedited GP.
+                * The task either does not block the normal GP, or is the
+                * first task blocking the normal GP.  Queue just after
+                * the first task blocking the expedited GP.
+                */
+               list_add(&t->rcu_node_entry, rnp->exp_tasks);
+               break;
+
+       case RCU_GP_TASKS +                 RCU_GP_BLKD:
+       case RCU_GP_TASKS + RCU_EXP_TASKS + RCU_GP_BLKD:
+
+               /*
+                * Second or subsequent task blocking the normal GP.
+                * The task does not block the expedited GP. Queue just
+                * after the first task blocking the normal GP.
+                */
+               list_add(&t->rcu_node_entry, rnp->gp_tasks);
+               break;
+
+       default:
+
+               /* Yet another exercise in excessive paranoia. */
+               WARN_ON_ONCE(1);
+               break;
+       }
+
+       /*
+        * We have now queued the task.  If it was the first one to
+        * block either grace period, update the ->gp_tasks and/or
+        * ->exp_tasks pointers, respectively, to reference the newly
+        * blocked tasks.
+        */
+       if (!rnp->gp_tasks && (blkd_state & RCU_GP_BLKD))
+               rnp->gp_tasks = &t->rcu_node_entry;
+       if (!rnp->exp_tasks && (blkd_state & RCU_EXP_BLKD))
+               rnp->exp_tasks = &t->rcu_node_entry;
+       raw_spin_unlock(&rnp->lock);
+
+       /*
+        * Report the quiescent state for the expedited GP.  This expedited
+        * GP should not be able to end until we report, so there should be
+        * no need to check for a subsequent expedited GP.  (Though we are
+        * still in a quiescent state in any case.)
+        */
+       if (blkd_state & RCU_EXP_BLKD &&
+           t->rcu_read_unlock_special.b.exp_need_qs) {
+               t->rcu_read_unlock_special.b.exp_need_qs = false;
+               rcu_report_exp_rdp(rdp->rsp, rdp, true);
+       } else {
+               WARN_ON_ONCE(t->rcu_read_unlock_special.b.exp_need_qs);
+       }
+       local_irq_restore(flags);
+}
+
 /*
  * Record a preemptible-RCU quiescent state for the specified CPU.  Note
  * that this just means that the task currently running on the CPU is
@@ -125,11 +265,11 @@ static void __init rcu_bootup_announce(void)
  */
 static void rcu_preempt_qs(void)
 {
-       if (!__this_cpu_read(rcu_data_p->passed_quiesce)) {
+       if (__this_cpu_read(rcu_data_p->cpu_no_qs.s)) {
                trace_rcu_grace_period(TPS("rcu_preempt"),
                                       __this_cpu_read(rcu_data_p->gpnum),
                                       TPS("cpuqs"));
-               __this_cpu_write(rcu_data_p->passed_quiesce, 1);
+               __this_cpu_write(rcu_data_p->cpu_no_qs.b.norm, false);
                barrier(); /* Coordinate with rcu_preempt_check_callbacks(). */
                current->rcu_read_unlock_special.b.need_qs = false;
        }
@@ -167,42 +307,18 @@ static void rcu_preempt_note_context_switch(void)
                t->rcu_blocked_node = rnp;
 
                /*
-                * If this CPU has already checked in, then this task
-                * will hold up the next grace period rather than the
-                * current grace period.  Queue the task accordingly.
-                * If the task is queued for the current grace period
-                * (i.e., this CPU has not yet passed through a quiescent
-                * state for the current grace period), then as long
-                * as that task remains queued, the current grace period
-                * cannot end.  Note that there is some uncertainty as
-                * to exactly when the current grace period started.
-                * We take a conservative approach, which can result
-                * in unnecessarily waiting on tasks that started very
-                * slightly after the current grace period began.  C'est
-                * la vie!!!
-                *
-                * But first, note that the current CPU must still be
-                * on line!
+                * Verify the CPU's sanity, trace the preemption, and
+                * then queue the task as required based on the states
+                * of any ongoing and expedited grace periods.
                 */
                WARN_ON_ONCE((rdp->grpmask & rcu_rnp_online_cpus(rnp)) == 0);
                WARN_ON_ONCE(!list_empty(&t->rcu_node_entry));
-               if ((rnp->qsmask & rdp->grpmask) && rnp->gp_tasks != NULL) {
-                       list_add(&t->rcu_node_entry, rnp->gp_tasks->prev);
-                       rnp->gp_tasks = &t->rcu_node_entry;
-                       if (IS_ENABLED(CONFIG_RCU_BOOST) &&
-                           rnp->boost_tasks != NULL)
-                               rnp->boost_tasks = rnp->gp_tasks;
-               } else {
-                       list_add(&t->rcu_node_entry, &rnp->blkd_tasks);
-                       if (rnp->qsmask & rdp->grpmask)
-                               rnp->gp_tasks = &t->rcu_node_entry;
-               }
                trace_rcu_preempt_task(rdp->rsp->name,
                                       t->pid,
                                       (rnp->qsmask & rdp->grpmask)
                                       ? rnp->gpnum
                                       : rnp->gpnum + 1);
-               raw_spin_unlock_irqrestore(&rnp->lock, flags);
+               rcu_preempt_ctxt_queue(rnp, rdp, flags);
        } else if (t->rcu_read_lock_nesting < 0 &&
                   t->rcu_read_unlock_special.s) {
 
@@ -272,6 +388,7 @@ void rcu_read_unlock_special(struct task_struct *t)
        unsigned long flags;
        struct list_head *np;
        bool drop_boost_mutex = false;
+       struct rcu_data *rdp;
        struct rcu_node *rnp;
        union rcu_special special;
 
@@ -282,8 +399,8 @@ void rcu_read_unlock_special(struct task_struct *t)
        local_irq_save(flags);
 
        /*
-        * If RCU core is waiting for this CPU to exit critical section,
-        * let it know that we have done so.  Because irqs are disabled,
+        * If RCU core is waiting for this CPU to exit its critical section,
+        * report the fact that it has exited.  Because irqs are disabled,
         * t->rcu_read_unlock_special cannot change.
         */
        special = t->rcu_read_unlock_special;
@@ -296,13 +413,32 @@ void rcu_read_unlock_special(struct task_struct *t)
                }
        }
 
+       /*
+        * Respond to a request for an expedited grace period, but only if
+        * we were not preempted, meaning that we were running on the same
+        * CPU throughout.  If we were preempted, the exp_need_qs flag
+        * would have been cleared at the time of the first preemption,
+        * and the quiescent state would be reported when we were dequeued.
+        */
+       if (special.b.exp_need_qs) {
+               WARN_ON_ONCE(special.b.blocked);
+               t->rcu_read_unlock_special.b.exp_need_qs = false;
+               rdp = this_cpu_ptr(rcu_state_p->rda);
+               rcu_report_exp_rdp(rcu_state_p, rdp, true);
+               if (!t->rcu_read_unlock_special.s) {
+                       local_irq_restore(flags);
+                       return;
+               }
+       }
+
        /* Hardware IRQ handlers cannot block, complain if they get here. */
        if (in_irq() || in_serving_softirq()) {
                lockdep_rcu_suspicious(__FILE__, __LINE__,
                                       "rcu_read_unlock() from irq or softirq with blocking in critical section!!!\n");
-               pr_alert("->rcu_read_unlock_special: %#x (b: %d, nq: %d)\n",
+               pr_alert("->rcu_read_unlock_special: %#x (b: %d, enq: %d nq: %d)\n",
                         t->rcu_read_unlock_special.s,
                         t->rcu_read_unlock_special.b.blocked,
+                        t->rcu_read_unlock_special.b.exp_need_qs,
                         t->rcu_read_unlock_special.b.need_qs);
                local_irq_restore(flags);
                return;
@@ -329,7 +465,7 @@ void rcu_read_unlock_special(struct task_struct *t)
                        raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
                }
                empty_norm = !rcu_preempt_blocked_readers_cgp(rnp);
-               empty_exp = !rcu_preempted_readers_exp(rnp);
+               empty_exp = sync_rcu_preempt_exp_done(rnp);
                smp_mb(); /* ensure expedited fastpath sees end of RCU c-s. */
                np = rcu_next_node_entry(t, rnp);
                list_del_init(&t->rcu_node_entry);
@@ -353,7 +489,7 @@ void rcu_read_unlock_special(struct task_struct *t)
                 * Note that rcu_report_unblock_qs_rnp() releases rnp->lock,
                 * so we must take a snapshot of the expedited state.
                 */
-               empty_exp_now = !rcu_preempted_readers_exp(rnp);
+               empty_exp_now = sync_rcu_preempt_exp_done(rnp);
                if (!empty_norm && !rcu_preempt_blocked_readers_cgp(rnp)) {
                        trace_rcu_quiescent_state_report(TPS("preempt_rcu"),
                                                         rnp->gpnum,
@@ -449,6 +585,27 @@ static int rcu_print_task_stall(struct rcu_node *rnp)
        return ndetected;
 }
 
+/*
+ * Scan the current list of tasks blocked within RCU read-side critical
+ * sections, printing out the tid of each that is blocking the current
+ * expedited grace period.
+ */
+static int rcu_print_task_exp_stall(struct rcu_node *rnp)
+{
+       struct task_struct *t;
+       int ndetected = 0;
+
+       if (!rnp->exp_tasks)
+               return 0;
+       t = list_entry(rnp->exp_tasks->prev,
+                      struct task_struct, rcu_node_entry);
+       list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) {
+               pr_cont(" P%d", t->pid);
+               ndetected++;
+       }
+       return ndetected;
+}
+
 /*
  * Check that the list of blocked tasks for the newly completed grace
  * period is in fact empty.  It is a serious bug to complete a grace
@@ -483,8 +640,8 @@ static void rcu_preempt_check_callbacks(void)
                return;
        }
        if (t->rcu_read_lock_nesting > 0 &&
-           __this_cpu_read(rcu_data_p->qs_pending) &&
-           !__this_cpu_read(rcu_data_p->passed_quiesce))
+           __this_cpu_read(rcu_data_p->core_needs_qs) &&
+           __this_cpu_read(rcu_data_p->cpu_no_qs.b.norm))
                t->rcu_read_unlock_special.b.need_qs = true;
 }
 
@@ -500,7 +657,7 @@ static void rcu_preempt_do_callbacks(void)
 /*
  * Queue a preemptible-RCU callback for invocation after a grace period.
  */
-void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
+void call_rcu(struct rcu_head *head, rcu_callback_t func)
 {
        __call_rcu(head, func, rcu_state_p, -1, 0);
 }
@@ -535,155 +692,41 @@ void synchronize_rcu(void)
 }
 EXPORT_SYMBOL_GPL(synchronize_rcu);
 
-static DECLARE_WAIT_QUEUE_HEAD(sync_rcu_preempt_exp_wq);
-
-/*
- * Return non-zero if there are any tasks in RCU read-side critical
- * sections blocking the current preemptible-RCU expedited grace period.
- * If there is no preemptible-RCU expedited grace period currently in
- * progress, returns zero unconditionally.
- */
-static int rcu_preempted_readers_exp(struct rcu_node *rnp)
-{
-       return rnp->exp_tasks != NULL;
-}
-
-/*
- * return non-zero if there is no RCU expedited grace period in progress
- * for the specified rcu_node structure, in other words, if all CPUs and
- * tasks covered by the specified rcu_node structure have done their bit
- * for the current expedited grace period.  Works only for preemptible
- * RCU -- other RCU implementation use other means.
- *
- * Caller must hold the root rcu_node's exp_funnel_mutex.
- */
-static int sync_rcu_preempt_exp_done(struct rcu_node *rnp)
-{
-       return !rcu_preempted_readers_exp(rnp) &&
-              READ_ONCE(rnp->expmask) == 0;
-}
-
-/*
- * Report the exit from RCU read-side critical section for the last task
- * that queued itself during or before the current expedited preemptible-RCU
- * grace period.  This event is reported either to the rcu_node structure on
- * which the task was queued or to one of that rcu_node structure's ancestors,
- * recursively up the tree.  (Calm down, calm down, we do the recursion
- * iteratively!)
- *
- * Caller must hold the root rcu_node's exp_funnel_mutex.
- */
-static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp,
-                              bool wake)
-{
-       unsigned long flags;
-       unsigned long mask;
-
-       raw_spin_lock_irqsave(&rnp->lock, flags);
-       smp_mb__after_unlock_lock();
-       for (;;) {
-               if (!sync_rcu_preempt_exp_done(rnp)) {
-                       raw_spin_unlock_irqrestore(&rnp->lock, flags);
-                       break;
-               }
-               if (rnp->parent == NULL) {
-                       raw_spin_unlock_irqrestore(&rnp->lock, flags);
-                       if (wake) {
-                               smp_mb(); /* EGP done before wake_up(). */
-                               wake_up(&sync_rcu_preempt_exp_wq);
-                       }
-                       break;
-               }
-               mask = rnp->grpmask;
-               raw_spin_unlock(&rnp->lock); /* irqs remain disabled */
-               rnp = rnp->parent;
-               raw_spin_lock(&rnp->lock); /* irqs already disabled */
-               smp_mb__after_unlock_lock();
-               rnp->expmask &= ~mask;
-       }
-}
-
 /*
- * Snapshot the tasks blocking the newly started preemptible-RCU expedited
- * grace period for the specified rcu_node structure, phase 1.  If there
- * are such tasks, set the ->expmask bits up the rcu_node tree and also
- * set the ->expmask bits on the leaf rcu_node structures to tell phase 2
- * that work is needed here.
- *
- * Caller must hold the root rcu_node's exp_funnel_mutex.
+ * Remote handler for smp_call_function_single().  If there is an
+ * RCU read-side critical section in effect, request that the
+ * next rcu_read_unlock() record the quiescent state up the
+ * ->expmask fields in the rcu_node tree.  Otherwise, immediately
+ * report the quiescent state.
  */
-static void
-sync_rcu_preempt_exp_init1(struct rcu_state *rsp, struct rcu_node *rnp)
+static void sync_rcu_exp_handler(void *info)
 {
-       unsigned long flags;
-       unsigned long mask;
-       struct rcu_node *rnp_up;
-
-       raw_spin_lock_irqsave(&rnp->lock, flags);
-       smp_mb__after_unlock_lock();
-       WARN_ON_ONCE(rnp->expmask);
-       WARN_ON_ONCE(rnp->exp_tasks);
-       if (!rcu_preempt_has_tasks(rnp)) {
-               /* No blocked tasks, nothing to do. */
-               raw_spin_unlock_irqrestore(&rnp->lock, flags);
-               return;
-       }
-       /* Call for Phase 2 and propagate ->expmask bits up the tree. */
-       rnp->expmask = 1;
-       rnp_up = rnp;
-       while (rnp_up->parent) {
-               mask = rnp_up->grpmask;
-               rnp_up = rnp_up->parent;
-               if (rnp_up->expmask & mask)
-                       break;
-               raw_spin_lock(&rnp_up->lock); /* irqs already off */
-               smp_mb__after_unlock_lock();
-               rnp_up->expmask |= mask;
-               raw_spin_unlock(&rnp_up->lock); /* irqs still off */
-       }
-       raw_spin_unlock_irqrestore(&rnp->lock, flags);
-}
-
-/*
- * Snapshot the tasks blocking the newly started preemptible-RCU expedited
- * grace period for the specified rcu_node structure, phase 2.  If the
- * leaf rcu_node structure has its ->expmask field set, check for tasks.
- * If there are some, clear ->expmask and set ->exp_tasks accordingly,
- * then initiate RCU priority boosting.  Otherwise, clear ->expmask and
- * invoke rcu_report_exp_rnp() to clear out the upper-level ->expmask bits,
- * enabling rcu_read_unlock_special() to do the bit-clearing.
- *
- * Caller must hold the root rcu_node's exp_funnel_mutex.
- */
-static void
-sync_rcu_preempt_exp_init2(struct rcu_state *rsp, struct rcu_node *rnp)
-{
-       unsigned long flags;
-
-       raw_spin_lock_irqsave(&rnp->lock, flags);
-       smp_mb__after_unlock_lock();
-       if (!rnp->expmask) {
-               /* Phase 1 didn't do anything, so Phase 2 doesn't either. */
-               raw_spin_unlock_irqrestore(&rnp->lock, flags);
-               return;
-       }
-
-       /* Phase 1 is over. */
-       rnp->expmask = 0;
+       struct rcu_data *rdp;
+       struct rcu_state *rsp = info;
+       struct task_struct *t = current;
 
        /*
-        * If there are still blocked tasks, set up ->exp_tasks so that
-        * rcu_read_unlock_special() will wake us and then boost them.
+        * Within an RCU read-side critical section, request that the next
+        * rcu_read_unlock() report.  Unless this RCU read-side critical
+        * section has already blocked, in which case it is already set
+        * up for the expedited grace period to wait on it.
         */
-       if (rcu_preempt_has_tasks(rnp)) {
-               rnp->exp_tasks = rnp->blkd_tasks.next;
-               rcu_initiate_boost(rnp, flags);  /* releases rnp->lock */
+       if (t->rcu_read_lock_nesting > 0 &&
+           !t->rcu_read_unlock_special.b.blocked) {
+               t->rcu_read_unlock_special.b.exp_need_qs = true;
                return;
        }
 
-       /* No longer any blocked tasks, so undo bit setting. */
-       raw_spin_unlock_irqrestore(&rnp->lock, flags);
-       rcu_report_exp_rnp(rsp, rnp, false);
+       /*
+        * We are either exiting an RCU read-side critical section (negative
+        * values of t->rcu_read_lock_nesting) or are not in one at all
+        * (zero value of t->rcu_read_lock_nesting).  Or we are in an RCU
+        * read-side critical section that blocked before this expedited
+        * grace period started.  Either way, we can immediately report
+        * the quiescent state.
+        */
+       rdp = this_cpu_ptr(rsp->rda);
+       rcu_report_exp_rdp(rsp, rdp, true);
 }
 
 /**
@@ -713,24 +756,12 @@ void synchronize_rcu_expedited(void)
 
        rcu_exp_gp_seq_start(rsp);
 
-       /* force all RCU readers onto ->blkd_tasks lists. */
-       synchronize_sched_expedited();
-
-       /*
-        * Snapshot current state of ->blkd_tasks lists into ->expmask.
-        * Phase 1 sets bits and phase 2 permits rcu_read_unlock_special()
-        * to start clearing them.  Doing this in one phase leads to
-        * strange races between setting and clearing bits, so just say "no"!
-        */
-       rcu_for_each_leaf_node(rsp, rnp)
-               sync_rcu_preempt_exp_init1(rsp, rnp);
-       rcu_for_each_leaf_node(rsp, rnp)
-               sync_rcu_preempt_exp_init2(rsp, rnp);
+       /* Initialize the rcu_node tree in preparation for the wait. */
+       sync_rcu_exp_select_cpus(rsp, sync_rcu_exp_handler);
 
        /* Wait for snapshotted ->blkd_tasks lists to drain. */
        rnp = rcu_get_root(rsp);
-       wait_event(sync_rcu_preempt_exp_wq,
-                  sync_rcu_preempt_exp_done(rnp));
+       synchronize_sched_expedited_wait(rsp);
 
        /* Clean up and exit. */
        rcu_exp_gp_seq_end(rsp);
@@ -834,6 +865,16 @@ static int rcu_print_task_stall(struct rcu_node *rnp)
        return 0;
 }
 
+/*
+ * Because preemptible RCU does not exist, we never have to check for
+ * tasks blocked within RCU read-side critical sections that are
+ * blocking the current expedited grace period.
+ */
+static int rcu_print_task_exp_stall(struct rcu_node *rnp)
+{
+       return 0;
+}
+
 /*
  * Because there is no preemptible RCU, there can be no readers blocked,
  * so there is no need to check for blocked tasks.  So check only for
@@ -1702,8 +1743,12 @@ static void print_cpu_stall_info(struct rcu_state *rsp, int cpu)
                ticks_value = rsp->gpnum - rdp->gpnum;
        }
        print_cpu_stall_fast_no_hz(fast_no_hz, cpu);
-       pr_err("\t%d: (%lu %s) idle=%03x/%llx/%d softirq=%u/%u fqs=%ld %s\n",
-              cpu, ticks_value, ticks_title,
+       pr_err("\t%d-%c%c%c: (%lu %s) idle=%03x/%llx/%d softirq=%u/%u fqs=%ld %s\n",
+              cpu,
+              "O."[!!cpu_online(cpu)],
+              "o."[!!(rdp->grpmask & rdp->mynode->qsmaskinit)],
+              "N."[!!(rdp->grpmask & rdp->mynode->qsmaskinitnext)],
+              ticks_value, ticks_title,
               atomic_read(&rdtp->dynticks) & 0xfff,
               rdtp->dynticks_nesting, rdtp->dynticks_nmi_nesting,
               rdp->softirq_snap, kstat_softirqs_cpu(RCU_SOFTIRQ, cpu),
index 6fc4c5ff3bb5cf55925a1da6ba73d269d3349d49..ef7093cc9b5cd86c3f9f6c6cff6a33f4e0b62ea8 100644 (file)
@@ -117,13 +117,13 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
 
        if (!rdp->beenonline)
                return;
-       seq_printf(m, "%3d%cc=%ld g=%ld pq=%d/%d qp=%d",
+       seq_printf(m, "%3d%cc=%ld g=%ld cnq=%d/%d:%d",
                   rdp->cpu,
                   cpu_is_offline(rdp->cpu) ? '!' : ' ',
                   ulong2long(rdp->completed), ulong2long(rdp->gpnum),
-                  rdp->passed_quiesce,
+                  rdp->cpu_no_qs.b.norm,
                   rdp->rcu_qs_ctr_snap == per_cpu(rcu_qs_ctr, rdp->cpu),
-                  rdp->qs_pending);
+                  rdp->core_needs_qs);
        seq_printf(m, " dt=%d/%llx/%d df=%lu",
                   atomic_read(&rdp->dynticks->dynticks),
                   rdp->dynticks->dynticks_nesting,
@@ -268,7 +268,7 @@ static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)
        gpnum = rsp->gpnum;
        seq_printf(m, "c=%ld g=%ld s=%d jfq=%ld j=%x ",
                   ulong2long(rsp->completed), ulong2long(gpnum),
-                  rsp->fqs_state,
+                  rsp->gp_state,
                   (long)(rsp->jiffies_force_qs - jiffies),
                   (int)(jiffies & 0xffff));
        seq_printf(m, "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld/%ld\n",
@@ -361,7 +361,7 @@ static void print_one_rcu_pending(struct seq_file *m, struct rcu_data *rdp)
                   cpu_is_offline(rdp->cpu) ? '!' : ' ',
                   rdp->n_rcu_pending);
        seq_printf(m, "qsp=%ld rpq=%ld cbr=%ld cng=%ld ",
-                  rdp->n_rp_qs_pending,
+                  rdp->n_rp_core_needs_qs,
                   rdp->n_rp_report_qs,
                   rdp->n_rp_cb_ready,
                   rdp->n_rp_cpu_needs_gp);
index 7a0b3bc7c5ed147162bec6a600e0bfbded04257d..5f748c5a40f0756b2d6c97fa615cde551649db4a 100644 (file)
@@ -534,7 +534,7 @@ static void rcu_spawn_tasks_kthread(void);
  * Post an RCU-tasks callback.  First call must be from process context
  * after the scheduler if fully operational.
  */
-void call_rcu_tasks(struct rcu_head *rhp, void (*func)(struct rcu_head *rhp))
+void call_rcu_tasks(struct rcu_head *rhp, rcu_callback_t func)
 {
        unsigned long flags;
        bool needwake;
index bcd214e4b4d630eb3304f6000a220dc881bafc24..aa5973220ad213a960092012bf4493f296dab90b 100644 (file)
@@ -817,7 +817,7 @@ static void set_load_weight(struct task_struct *p)
        /*
         * SCHED_IDLE tasks get minimal weight:
         */
-       if (p->policy == SCHED_IDLE) {
+       if (idle_policy(p->policy)) {
                load->weight = scale_load(WEIGHT_IDLEPRIO);
                load->inv_weight = WMULT_IDLEPRIO;
                return;
@@ -827,17 +827,19 @@ static void set_load_weight(struct task_struct *p)
        load->inv_weight = prio_to_wmult[prio];
 }
 
-static void enqueue_task(struct rq *rq, struct task_struct *p, int flags)
+static inline void enqueue_task(struct rq *rq, struct task_struct *p, int flags)
 {
        update_rq_clock(rq);
-       sched_info_queued(rq, p);
+       if (!(flags & ENQUEUE_RESTORE))
+               sched_info_queued(rq, p);
        p->sched_class->enqueue_task(rq, p, flags);
 }
 
-static void dequeue_task(struct rq *rq, struct task_struct *p, int flags)
+static inline void dequeue_task(struct rq *rq, struct task_struct *p, int flags)
 {
        update_rq_clock(rq);
-       sched_info_dequeued(rq, p);
+       if (!(flags & DEQUEUE_SAVE))
+               sched_info_dequeued(rq, p);
        p->sched_class->dequeue_task(rq, p, flags);
 }
 
@@ -1178,7 +1180,7 @@ void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask)
                 * holding rq->lock.
                 */
                lockdep_assert_held(&rq->lock);
-               dequeue_task(rq, p, 0);
+               dequeue_task(rq, p, DEQUEUE_SAVE);
        }
        if (running)
                put_prev_task(rq, p);
@@ -1188,7 +1190,7 @@ void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask)
        if (running)
                p->sched_class->set_curr_task(rq);
        if (queued)
-               enqueue_task(rq, p, 0);
+               enqueue_task(rq, p, ENQUEUE_RESTORE);
 }
 
 /*
@@ -1292,7 +1294,7 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
 
        if (task_cpu(p) != new_cpu) {
                if (p->sched_class->migrate_task_rq)
-                       p->sched_class->migrate_task_rq(p, new_cpu);
+                       p->sched_class->migrate_task_rq(p);
                p->se.nr_migrations++;
                perf_event_task_migrate(p);
        }
@@ -1333,12 +1335,16 @@ static int migrate_swap_stop(void *data)
        struct rq *src_rq, *dst_rq;
        int ret = -EAGAIN;
 
+       if (!cpu_active(arg->src_cpu) || !cpu_active(arg->dst_cpu))
+               return -EAGAIN;
+
        src_rq = cpu_rq(arg->src_cpu);
        dst_rq = cpu_rq(arg->dst_cpu);
 
        double_raw_lock(&arg->src_task->pi_lock,
                        &arg->dst_task->pi_lock);
        double_rq_lock(src_rq, dst_rq);
+
        if (task_cpu(arg->dst_task) != arg->dst_cpu)
                goto unlock;
 
@@ -1574,13 +1580,15 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
                        goto out;
                }
 
+               /* No more Mr. Nice Guy. */
                switch (state) {
                case cpuset:
-                       /* No more Mr. Nice Guy. */
-                       cpuset_cpus_allowed_fallback(p);
-                       state = possible;
-                       break;
-
+                       if (IS_ENABLED(CONFIG_CPUSETS)) {
+                               cpuset_cpus_allowed_fallback(p);
+                               state = possible;
+                               break;
+                       }
+                       /* fall-through */
                case possible:
                        do_set_cpus_allowed(p, cpu_possible_mask);
                        state = fail;
@@ -1692,7 +1700,7 @@ ttwu_stat(struct task_struct *p, int cpu, int wake_flags)
 #endif /* CONFIG_SCHEDSTATS */
 }
 
-static void ttwu_activate(struct rq *rq, struct task_struct *p, int en_flags)
+static inline void ttwu_activate(struct rq *rq, struct task_struct *p, int en_flags)
 {
        activate_task(rq, p, en_flags);
        p->on_rq = TASK_ON_RQ_QUEUED;
@@ -2114,23 +2122,17 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p)
 #endif /* CONFIG_NUMA_BALANCING */
 }
 
+DEFINE_STATIC_KEY_FALSE(sched_numa_balancing);
+
 #ifdef CONFIG_NUMA_BALANCING
-#ifdef CONFIG_SCHED_DEBUG
+
 void set_numabalancing_state(bool enabled)
 {
        if (enabled)
-               sched_feat_set("NUMA");
+               static_branch_enable(&sched_numa_balancing);
        else
-               sched_feat_set("NO_NUMA");
+               static_branch_disable(&sched_numa_balancing);
 }
-#else
-__read_mostly bool numabalancing_enabled;
-
-void set_numabalancing_state(bool enabled)
-{
-       numabalancing_enabled = enabled;
-}
-#endif /* CONFIG_SCHED_DEBUG */
 
 #ifdef CONFIG_PROC_SYSCTL
 int sysctl_numa_balancing(struct ctl_table *table, int write,
@@ -2138,7 +2140,7 @@ int sysctl_numa_balancing(struct ctl_table *table, int write,
 {
        struct ctl_table t;
        int err;
-       int state = numabalancing_enabled;
+       int state = static_branch_likely(&sched_numa_balancing);
 
        if (write && !capable(CAP_SYS_ADMIN))
                return -EPERM;
@@ -2349,6 +2351,8 @@ void wake_up_new_task(struct task_struct *p)
        struct rq *rq;
 
        raw_spin_lock_irqsave(&p->pi_lock, flags);
+       /* Initialize new task's runnable average */
+       init_entity_runnable_average(&p->se);
 #ifdef CONFIG_SMP
        /*
         * Fork balancing, do it here and not earlier because:
@@ -2358,8 +2362,6 @@ void wake_up_new_task(struct task_struct *p)
        set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0));
 #endif
 
-       /* Initialize new task's runnable average */
-       init_entity_runnable_average(&p->se);
        rq = __task_rq_lock(p);
        activate_task(rq, p, 0);
        p->on_rq = TASK_ON_RQ_QUEUED;
@@ -2483,7 +2485,6 @@ static inline void
 prepare_task_switch(struct rq *rq, struct task_struct *prev,
                    struct task_struct *next)
 {
-       trace_sched_switch(prev, next);
        sched_info_switch(rq, prev, next);
        perf_event_task_sched_out(prev, next);
        fire_sched_out_preempt_notifiers(prev, next);
@@ -2517,6 +2518,22 @@ static struct rq *finish_task_switch(struct task_struct *prev)
        struct mm_struct *mm = rq->prev_mm;
        long prev_state;
 
+       /*
+        * The previous task will have left us with a preempt_count of 2
+        * because it left us after:
+        *
+        *      schedule()
+        *        preempt_disable();                    // 1
+        *        __schedule()
+        *          raw_spin_lock_irq(&rq->lock)        // 2
+        *
+        * Also, see FORK_PREEMPT_COUNT.
+        */
+       if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
+                     "corrupted preempt_count: %s/%d/0x%x\n",
+                     current->comm, current->pid, preempt_count()))
+               preempt_count_set(FORK_PREEMPT_COUNT);
+
        rq->prev_mm = NULL;
 
        /*
@@ -2601,8 +2618,15 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev)
 {
        struct rq *rq;
 
-       /* finish_task_switch() drops rq->lock and enables preemtion */
-       preempt_disable();
+       /*
+        * New tasks start with FORK_PREEMPT_COUNT, see there and
+        * finish_task_switch() for details.
+        *
+        * finish_task_switch() will drop rq->lock() and lower preempt_count
+        * and the preempt_enable() will end up enabling preemption (on
+        * PREEMPT_COUNT kernels).
+        */
+
        rq = finish_task_switch(prev);
        balance_callback(rq);
        preempt_enable();
@@ -2960,15 +2984,13 @@ static noinline void __schedule_bug(struct task_struct *prev)
 static inline void schedule_debug(struct task_struct *prev)
 {
 #ifdef CONFIG_SCHED_STACK_END_CHECK
-       BUG_ON(unlikely(task_stack_end_corrupted(prev)));
+       BUG_ON(task_stack_end_corrupted(prev));
 #endif
-       /*
-        * Test if we are atomic. Since do_exit() needs to call into
-        * schedule() atomically, we ignore that path. Otherwise whine
-        * if we are scheduling when we should not.
-        */
-       if (unlikely(in_atomic_preempt_off() && prev->state != TASK_DEAD))
+
+       if (unlikely(in_atomic_preempt_off())) {
                __schedule_bug(prev);
+               preempt_count_set(PREEMPT_DISABLED);
+       }
        rcu_sleep_check();
 
        profile_hit(SCHED_PROFILING, __builtin_return_address(0));
@@ -3054,7 +3076,7 @@ again:
  *
  * WARNING: must be called with preemption disabled!
  */
-static void __sched __schedule(void)
+static void __sched notrace __schedule(bool preempt)
 {
        struct task_struct *prev, *next;
        unsigned long *switch_count;
@@ -3066,6 +3088,17 @@ static void __sched __schedule(void)
        rcu_note_context_switch();
        prev = rq->curr;
 
+       /*
+        * do_exit() calls schedule() with preemption disabled as an exception;
+        * however we must fix that up, otherwise the next task will see an
+        * inconsistent (higher) preempt count.
+        *
+        * It also avoids the below schedule_debug() test from complaining
+        * about this.
+        */
+       if (unlikely(prev->state == TASK_DEAD))
+               preempt_enable_no_resched_notrace();
+
        schedule_debug(prev);
 
        if (sched_feat(HRTICK))
@@ -3083,7 +3116,7 @@ static void __sched __schedule(void)
        rq->clock_skip_update <<= 1; /* promote REQ to ACT */
 
        switch_count = &prev->nivcsw;
-       if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
+       if (!preempt && prev->state) {
                if (unlikely(signal_pending_state(prev->state, prev))) {
                        prev->state = TASK_RUNNING;
                } else {
@@ -3119,6 +3152,7 @@ static void __sched __schedule(void)
                rq->curr = next;
                ++*switch_count;
 
+               trace_sched_switch(preempt, prev, next);
                rq = context_switch(rq, prev, next); /* unlocks the rq */
                cpu = cpu_of(rq);
        } else {
@@ -3148,7 +3182,7 @@ asmlinkage __visible void __sched schedule(void)
        sched_submit_work(tsk);
        do {
                preempt_disable();
-               __schedule();
+               __schedule(false);
                sched_preempt_enable_no_resched();
        } while (need_resched());
 }
@@ -3188,9 +3222,9 @@ void __sched schedule_preempt_disabled(void)
 static void __sched notrace preempt_schedule_common(void)
 {
        do {
-               preempt_active_enter();
-               __schedule();
-               preempt_active_exit();
+               preempt_disable_notrace();
+               __schedule(true);
+               preempt_enable_no_resched_notrace();
 
                /*
                 * Check again in case we missed a preemption opportunity
@@ -3241,24 +3275,17 @@ asmlinkage __visible void __sched notrace preempt_schedule_notrace(void)
                return;
 
        do {
-               /*
-                * Use raw __prempt_count() ops that don't call function.
-                * We can't call functions before disabling preemption which
-                * disarm preemption tracing recursions.
-                */
-               __preempt_count_add(PREEMPT_ACTIVE + PREEMPT_DISABLE_OFFSET);
-               barrier();
+               preempt_disable_notrace();
                /*
                 * Needs preempt disabled in case user_exit() is traced
                 * and the tracer calls preempt_enable_notrace() causing
                 * an infinite recursion.
                 */
                prev_ctx = exception_enter();
-               __schedule();
+               __schedule(true);
                exception_exit(prev_ctx);
 
-               barrier();
-               __preempt_count_sub(PREEMPT_ACTIVE + PREEMPT_DISABLE_OFFSET);
+               preempt_enable_no_resched_notrace();
        } while (need_resched());
 }
 EXPORT_SYMBOL_GPL(preempt_schedule_notrace);
@@ -3281,11 +3308,11 @@ asmlinkage __visible void __sched preempt_schedule_irq(void)
        prev_state = exception_enter();
 
        do {
-               preempt_active_enter();
+               preempt_disable();
                local_irq_enable();
-               __schedule();
+               __schedule(true);
                local_irq_disable();
-               preempt_active_exit();
+               sched_preempt_enable_no_resched();
        } while (need_resched());
 
        exception_exit(prev_state);
@@ -3313,7 +3340,7 @@ EXPORT_SYMBOL(default_wake_function);
  */
 void rt_mutex_setprio(struct task_struct *p, int prio)
 {
-       int oldprio, queued, running, enqueue_flag = 0;
+       int oldprio, queued, running, enqueue_flag = ENQUEUE_RESTORE;
        struct rq *rq;
        const struct sched_class *prev_class;
 
@@ -3345,7 +3372,7 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
        queued = task_on_rq_queued(p);
        running = task_current(rq, p);
        if (queued)
-               dequeue_task(rq, p, 0);
+               dequeue_task(rq, p, DEQUEUE_SAVE);
        if (running)
                put_prev_task(rq, p);
 
@@ -3363,7 +3390,7 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
                if (!dl_prio(p->normal_prio) ||
                    (pi_task && dl_entity_preempt(&pi_task->dl, &p->dl))) {
                        p->dl.dl_boosted = 1;
-                       enqueue_flag = ENQUEUE_REPLENISH;
+                       enqueue_flag |= ENQUEUE_REPLENISH;
                } else
                        p->dl.dl_boosted = 0;
                p->sched_class = &dl_sched_class;
@@ -3371,7 +3398,7 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
                if (dl_prio(oldprio))
                        p->dl.dl_boosted = 0;
                if (oldprio < prio)
-                       enqueue_flag = ENQUEUE_HEAD;
+                       enqueue_flag |= ENQUEUE_HEAD;
                p->sched_class = &rt_sched_class;
        } else {
                if (dl_prio(oldprio))
@@ -3423,7 +3450,7 @@ void set_user_nice(struct task_struct *p, long nice)
        }
        queued = task_on_rq_queued(p);
        if (queued)
-               dequeue_task(rq, p, 0);
+               dequeue_task(rq, p, DEQUEUE_SAVE);
 
        p->static_prio = NICE_TO_PRIO(nice);
        set_load_weight(p);
@@ -3432,7 +3459,7 @@ void set_user_nice(struct task_struct *p, long nice)
        delta = p->prio - old_prio;
 
        if (queued) {
-               enqueue_task(rq, p, 0);
+               enqueue_task(rq, p, ENQUEUE_RESTORE);
                /*
                 * If the task increased its priority or is running and
                 * lowered its priority, then reschedule its CPU:
@@ -3753,10 +3780,7 @@ recheck:
        } else {
                reset_on_fork = !!(attr->sched_flags & SCHED_FLAG_RESET_ON_FORK);
 
-               if (policy != SCHED_DEADLINE &&
-                               policy != SCHED_FIFO && policy != SCHED_RR &&
-                               policy != SCHED_NORMAL && policy != SCHED_BATCH &&
-                               policy != SCHED_IDLE)
+               if (!valid_policy(policy))
                        return -EINVAL;
        }
 
@@ -3812,7 +3836,7 @@ recheck:
                 * Treat SCHED_IDLE as nice 20. Only allow a switch to
                 * SCHED_NORMAL if the RLIMIT_NICE would normally permit it.
                 */
-               if (p->policy == SCHED_IDLE && policy != SCHED_IDLE) {
+               if (idle_policy(p->policy) && !idle_policy(policy)) {
                        if (!can_nice(p, task_nice(p)))
                                return -EPERM;
                }
@@ -3937,7 +3961,7 @@ change:
        queued = task_on_rq_queued(p);
        running = task_current(rq, p);
        if (queued)
-               dequeue_task(rq, p, 0);
+               dequeue_task(rq, p, DEQUEUE_SAVE);
        if (running)
                put_prev_task(rq, p);
 
@@ -3947,11 +3971,15 @@ change:
        if (running)
                p->sched_class->set_curr_task(rq);
        if (queued) {
+               int enqueue_flags = ENQUEUE_RESTORE;
                /*
                 * We enqueue to tail when the priority of a task is
                 * increased (user space view).
                 */
-               enqueue_task(rq, p, oldprio <= p->prio ? ENQUEUE_HEAD : 0);
+               if (oldprio <= p->prio)
+                       enqueue_flags |= ENQUEUE_HEAD;
+
+               enqueue_task(rq, p, enqueue_flags);
        }
 
        check_class_changed(rq, p, prev_class, oldprio);
@@ -4029,6 +4057,7 @@ int sched_setscheduler_nocheck(struct task_struct *p, int policy,
 {
        return _sched_setscheduler(p, policy, param, false);
 }
+EXPORT_SYMBOL_GPL(sched_setscheduler_nocheck);
 
 static int
 do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param)
@@ -5100,7 +5129,7 @@ void sched_setnuma(struct task_struct *p, int nid)
        running = task_current(rq, p);
 
        if (queued)
-               dequeue_task(rq, p, 0);
+               dequeue_task(rq, p, DEQUEUE_SAVE);
        if (running)
                put_prev_task(rq, p);
 
@@ -5109,7 +5138,7 @@ void sched_setnuma(struct task_struct *p, int nid)
        if (running)
                p->sched_class->set_curr_task(rq);
        if (queued)
-               enqueue_task(rq, p, 0);
+               enqueue_task(rq, p, ENQUEUE_RESTORE);
        task_rq_unlock(rq, p, &flags);
 }
 #endif /* CONFIG_NUMA_BALANCING */
@@ -5530,21 +5559,27 @@ static void set_cpu_rq_start_time(void)
 static int sched_cpu_active(struct notifier_block *nfb,
                                      unsigned long action, void *hcpu)
 {
+       int cpu = (long)hcpu;
+
        switch (action & ~CPU_TASKS_FROZEN) {
        case CPU_STARTING:
                set_cpu_rq_start_time();
                return NOTIFY_OK;
+
        case CPU_ONLINE:
                /*
                 * At this point a starting CPU has marked itself as online via
                 * set_cpu_online(). But it might not yet have marked itself
                 * as active, which is essential from here on.
-                *
-                * Thus, fall-through and help the starting CPU along.
                 */
+               set_cpu_active(cpu, true);
+               stop_machine_unpark(cpu);
+               return NOTIFY_OK;
+
        case CPU_DOWN_FAILED:
-               set_cpu_active((long)hcpu, true);
+               set_cpu_active(cpu, true);
                return NOTIFY_OK;
+
        default:
                return NOTIFY_DONE;
        }
@@ -6476,7 +6511,8 @@ static struct sched_domain_topology_level default_topology[] = {
        { NULL, },
 };
 
-struct sched_domain_topology_level *sched_domain_topology = default_topology;
+static struct sched_domain_topology_level *sched_domain_topology =
+       default_topology;
 
 #define for_each_sd_topology(tl)                       \
        for (tl = sched_domain_topology; tl->mask; tl++)
@@ -7477,7 +7513,7 @@ void __init sched_init(void)
 #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
 static inline int preempt_count_equals(int preempt_offset)
 {
-       int nested = (preempt_count() & ~PREEMPT_ACTIVE) + rcu_preempt_depth();
+       int nested = preempt_count() + rcu_preempt_depth();
 
        return (nested == preempt_offset);
 }
@@ -7724,7 +7760,7 @@ void sched_move_task(struct task_struct *tsk)
        queued = task_on_rq_queued(tsk);
 
        if (queued)
-               dequeue_task(rq, tsk, 0);
+               dequeue_task(rq, tsk, DEQUEUE_SAVE);
        if (unlikely(running))
                put_prev_task(rq, tsk);
 
@@ -7740,7 +7776,7 @@ void sched_move_task(struct task_struct *tsk)
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
        if (tsk->sched_class->task_move_group)
-               tsk->sched_class->task_move_group(tsk, queued);
+               tsk->sched_class->task_move_group(tsk);
        else
 #endif
                set_task_rq(tsk, task_cpu(tsk));
@@ -7748,7 +7784,7 @@ void sched_move_task(struct task_struct *tsk)
        if (unlikely(running))
                tsk->sched_class->set_curr_task(rq);
        if (queued)
-               enqueue_task(rq, tsk, 0);
+               enqueue_task(rq, tsk, ENQUEUE_RESTORE);
 
        task_rq_unlock(rq, tsk, &flags);
 }
@@ -8212,14 +8248,6 @@ static void cpu_cgroup_exit(struct cgroup_subsys_state *css,
                            struct cgroup_subsys_state *old_css,
                            struct task_struct *task)
 {
-       /*
-        * cgroup_exit() is called in the copy_process() failure path.
-        * Ignore this case since the task hasn't ran yet, this avoids
-        * trying to poke a half freed task state from generic code.
-        */
-       if (!(task->flags & PF_EXITING))
-               return;
-
        sched_move_task(task);
 }
 
index c6acb07466bb82b1143af4aba1da5e483f628e4f..5a75b08cfd8576d830adf9fc9df52d807c052be9 100644 (file)
@@ -31,11 +31,6 @@ static inline int right_child(int i)
        return (i << 1) + 2;
 }
 
-static inline int dl_time_before(u64 a, u64 b)
-{
-       return (s64)(a - b) < 0;
-}
-
 static void cpudl_exchange(struct cpudl *cp, int a, int b)
 {
        int cpu_a = cp->elements[a].cpu, cpu_b = cp->elements[b].cpu;
index 1a0a6ef2fbe1be030e32895571a1d267e26579e4..fcbdf83fed7e31afc4ee916f94d272d03117a2c2 100644 (file)
@@ -2,6 +2,7 @@
 #define _LINUX_CPUDL_H
 
 #include <linux/sched.h>
+#include <linux/sched/deadline.h>
 
 #define IDX_INVALID     -1
 
index 9a5e60fe721a5e7e8036508f61950eeb0c8dea31..824aa9f501a3b2a183a5a700dee57c984c51f89e 100644 (file)
@@ -661,11 +661,12 @@ static unsigned long task_h_load(struct task_struct *p);
 
 /*
  * We choose a half-life close to 1 scheduling period.
- * Note: The tables below are dependent on this value.
+ * Note: The tables runnable_avg_yN_inv and runnable_avg_yN_sum are
+ * dependent on this value.
  */
 #define LOAD_AVG_PERIOD 32
 #define LOAD_AVG_MAX 47742 /* maximum possible load avg */
-#define LOAD_AVG_MAX_N 345 /* number of full periods to produce LOAD_MAX_AVG */
+#define LOAD_AVG_MAX_N 345 /* number of full periods to produce LOAD_AVG_MAX */
 
 /* Give new sched_entity start runnable values to heavy its load in infant time */
 void init_entity_runnable_average(struct sched_entity *se)
@@ -682,7 +683,7 @@ void init_entity_runnable_average(struct sched_entity *se)
        sa->load_avg = scale_load_down(se->load.weight);
        sa->load_sum = sa->load_avg * LOAD_AVG_MAX;
        sa->util_avg = scale_load_down(SCHED_LOAD_SCALE);
-       sa->util_sum = LOAD_AVG_MAX;
+       sa->util_sum = sa->util_avg * LOAD_AVG_MAX;
        /* when this task enqueue'ed, it will contribute to its cfs_rq's load_avg */
 }
 
@@ -2069,7 +2070,7 @@ void task_numa_fault(int last_cpupid, int mem_node, int pages, int flags)
        int local = !!(flags & TNF_FAULT_LOCAL);
        int priv;
 
-       if (!numabalancing_enabled)
+       if (!static_branch_likely(&sched_numa_balancing))
                return;
 
        /* for example, ksmd faulting in a user's mm */
@@ -2157,7 +2158,7 @@ void task_numa_work(struct callback_head *work)
        struct vm_area_struct *vma;
        unsigned long start, end;
        unsigned long nr_pte_updates = 0;
-       long pages;
+       long pages, virtpages;
 
        WARN_ON_ONCE(p != container_of(work, struct task_struct, numa_work));
 
@@ -2203,9 +2204,11 @@ void task_numa_work(struct callback_head *work)
        start = mm->numa_scan_offset;
        pages = sysctl_numa_balancing_scan_size;
        pages <<= 20 - PAGE_SHIFT; /* MB in pages */
+       virtpages = pages * 8;     /* Scan up to this much virtual space */
        if (!pages)
                return;
 
+
        down_read(&mm->mmap_sem);
        vma = find_vma(mm, start);
        if (!vma) {
@@ -2240,18 +2243,22 @@ void task_numa_work(struct callback_head *work)
                        start = max(start, vma->vm_start);
                        end = ALIGN(start + (pages << PAGE_SHIFT), HPAGE_SIZE);
                        end = min(end, vma->vm_end);
-                       nr_pte_updates += change_prot_numa(vma, start, end);
+                       nr_pte_updates = change_prot_numa(vma, start, end);
 
                        /*
-                        * Scan sysctl_numa_balancing_scan_size but ensure that
-                        * at least one PTE is updated so that unused virtual
-                        * address space is quickly skipped.
+                        * Try to scan sysctl_numa_balancing_size worth of
+                        * hpages that have at least one present PTE that
+                        * is not already pte-numa. If the VMA contains
+                        * areas that are unused or already full of prot_numa
+                        * PTEs, scan up to virtpages, to skip through those
+                        * areas faster.
                         */
                        if (nr_pte_updates)
                                pages -= (end - start) >> PAGE_SHIFT;
+                       virtpages -= (end - start) >> PAGE_SHIFT;
 
                        start = end;
-                       if (pages <= 0)
+                       if (pages <= 0 || virtpages <= 0)
                                goto out;
 
                        cond_resched();
@@ -2515,6 +2522,12 @@ static u32 __compute_runnable_contrib(u64 n)
        return contrib + runnable_avg_yN_sum[n];
 }
 
+#if (SCHED_LOAD_SHIFT - SCHED_LOAD_RESOLUTION) != 10 || SCHED_CAPACITY_SHIFT != 10
+#error "load tracking assumes 2^10 as unit"
+#endif
+
+#define cap_scale(v, s) ((v)*(s) >> SCHED_CAPACITY_SHIFT)
+
 /*
  * We can represent the historical contribution to runnable average as the
  * coefficients of a geometric series.  To do this we sub-divide our runnable
@@ -2547,10 +2560,10 @@ static __always_inline int
 __update_load_avg(u64 now, int cpu, struct sched_avg *sa,
                  unsigned long weight, int running, struct cfs_rq *cfs_rq)
 {
-       u64 delta, periods;
+       u64 delta, scaled_delta, periods;
        u32 contrib;
-       int delta_w, decayed = 0;
-       unsigned long scale_freq = arch_scale_freq_capacity(NULL, cpu);
+       unsigned int delta_w, scaled_delta_w, decayed = 0;
+       unsigned long scale_freq, scale_cpu;
 
        delta = now - sa->last_update_time;
        /*
@@ -2571,6 +2584,9 @@ __update_load_avg(u64 now, int cpu, struct sched_avg *sa,
                return 0;
        sa->last_update_time = now;
 
+       scale_freq = arch_scale_freq_capacity(NULL, cpu);
+       scale_cpu = arch_scale_cpu_capacity(NULL, cpu);
+
        /* delta_w is the amount already accumulated against our next period */
        delta_w = sa->period_contrib;
        if (delta + delta_w >= 1024) {
@@ -2585,13 +2601,16 @@ __update_load_avg(u64 now, int cpu, struct sched_avg *sa,
                 * period and accrue it.
                 */
                delta_w = 1024 - delta_w;
+               scaled_delta_w = cap_scale(delta_w, scale_freq);
                if (weight) {
-                       sa->load_sum += weight * delta_w;
-                       if (cfs_rq)
-                               cfs_rq->runnable_load_sum += weight * delta_w;
+                       sa->load_sum += weight * scaled_delta_w;
+                       if (cfs_rq) {
+                               cfs_rq->runnable_load_sum +=
+                                               weight * scaled_delta_w;
+                       }
                }
                if (running)
-                       sa->util_sum += delta_w * scale_freq >> SCHED_CAPACITY_SHIFT;
+                       sa->util_sum += scaled_delta_w * scale_cpu;
 
                delta -= delta_w;
 
@@ -2608,23 +2627,25 @@ __update_load_avg(u64 now, int cpu, struct sched_avg *sa,
 
                /* Efficiently calculate \sum (1..n_period) 1024*y^i */
                contrib = __compute_runnable_contrib(periods);
+               contrib = cap_scale(contrib, scale_freq);
                if (weight) {
                        sa->load_sum += weight * contrib;
                        if (cfs_rq)
                                cfs_rq->runnable_load_sum += weight * contrib;
                }
                if (running)
-                       sa->util_sum += contrib * scale_freq >> SCHED_CAPACITY_SHIFT;
+                       sa->util_sum += contrib * scale_cpu;
        }
 
        /* Remainder of delta accrued against u_0` */
+       scaled_delta = cap_scale(delta, scale_freq);
        if (weight) {
-               sa->load_sum += weight * delta;
+               sa->load_sum += weight * scaled_delta;
                if (cfs_rq)
-                       cfs_rq->runnable_load_sum += weight * delta;
+                       cfs_rq->runnable_load_sum += weight * scaled_delta;
        }
        if (running)
-               sa->util_sum += delta * scale_freq >> SCHED_CAPACITY_SHIFT;
+               sa->util_sum += scaled_delta * scale_cpu;
 
        sa->period_contrib += delta;
 
@@ -2634,7 +2655,7 @@ __update_load_avg(u64 now, int cpu, struct sched_avg *sa,
                        cfs_rq->runnable_load_avg =
                                div_u64(cfs_rq->runnable_load_sum, LOAD_AVG_MAX);
                }
-               sa->util_avg = (sa->util_sum << SCHED_LOAD_SHIFT) / LOAD_AVG_MAX;
+               sa->util_avg = sa->util_sum / LOAD_AVG_MAX;
        }
 
        return decayed;
@@ -2677,8 +2698,7 @@ static inline int update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq)
        if (atomic_long_read(&cfs_rq->removed_util_avg)) {
                long r = atomic_long_xchg(&cfs_rq->removed_util_avg, 0);
                sa->util_avg = max_t(long, sa->util_avg - r, 0);
-               sa->util_sum = max_t(s32, sa->util_sum -
-                       ((r * LOAD_AVG_MAX) >> SCHED_LOAD_SHIFT), 0);
+               sa->util_sum = max_t(s32, sa->util_sum - r * LOAD_AVG_MAX, 0);
        }
 
        decayed = __update_load_avg(now, cpu_of(rq_of(cfs_rq)), sa,
@@ -2696,33 +2716,70 @@ static inline int update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq)
 static inline void update_load_avg(struct sched_entity *se, int update_tg)
 {
        struct cfs_rq *cfs_rq = cfs_rq_of(se);
-       int cpu = cpu_of(rq_of(cfs_rq));
        u64 now = cfs_rq_clock_task(cfs_rq);
+       int cpu = cpu_of(rq_of(cfs_rq));
 
        /*
         * Track task load average for carrying it to new CPU after migrated, and
         * track group sched_entity load average for task_h_load calc in migration
         */
        __update_load_avg(now, cpu, &se->avg,
-               se->on_rq * scale_load_down(se->load.weight), cfs_rq->curr == se, NULL);
+                         se->on_rq * scale_load_down(se->load.weight),
+                         cfs_rq->curr == se, NULL);
 
        if (update_cfs_rq_load_avg(now, cfs_rq) && update_tg)
                update_tg_load_avg(cfs_rq, 0);
 }
 
+static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)
+{
+       if (!sched_feat(ATTACH_AGE_LOAD))
+               goto skip_aging;
+
+       /*
+        * If we got migrated (either between CPUs or between cgroups) we'll
+        * have aged the average right before clearing @last_update_time.
+        */
+       if (se->avg.last_update_time) {
+               __update_load_avg(cfs_rq->avg.last_update_time, cpu_of(rq_of(cfs_rq)),
+                                 &se->avg, 0, 0, NULL);
+
+               /*
+                * XXX: we could have just aged the entire load away if we've been
+                * absent from the fair class for too long.
+                */
+       }
+
+skip_aging:
+       se->avg.last_update_time = cfs_rq->avg.last_update_time;
+       cfs_rq->avg.load_avg += se->avg.load_avg;
+       cfs_rq->avg.load_sum += se->avg.load_sum;
+       cfs_rq->avg.util_avg += se->avg.util_avg;
+       cfs_rq->avg.util_sum += se->avg.util_sum;
+}
+
+static void detach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)
+{
+       __update_load_avg(cfs_rq->avg.last_update_time, cpu_of(rq_of(cfs_rq)),
+                         &se->avg, se->on_rq * scale_load_down(se->load.weight),
+                         cfs_rq->curr == se, NULL);
+
+       cfs_rq->avg.load_avg = max_t(long, cfs_rq->avg.load_avg - se->avg.load_avg, 0);
+       cfs_rq->avg.load_sum = max_t(s64,  cfs_rq->avg.load_sum - se->avg.load_sum, 0);
+       cfs_rq->avg.util_avg = max_t(long, cfs_rq->avg.util_avg - se->avg.util_avg, 0);
+       cfs_rq->avg.util_sum = max_t(s32,  cfs_rq->avg.util_sum - se->avg.util_sum, 0);
+}
+
 /* Add the load generated by se into cfs_rq's load average */
 static inline void
 enqueue_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)
 {
        struct sched_avg *sa = &se->avg;
        u64 now = cfs_rq_clock_task(cfs_rq);
-       int migrated = 0, decayed;
+       int migrated, decayed;
 
-       if (sa->last_update_time == 0) {
-               sa->last_update_time = now;
-               migrated = 1;
-       }
-       else {
+       migrated = !sa->last_update_time;
+       if (!migrated) {
                __update_load_avg(now, cpu_of(rq_of(cfs_rq)), sa,
                        se->on_rq * scale_load_down(se->load.weight),
                        cfs_rq->curr == se, NULL);
@@ -2733,12 +2790,8 @@ enqueue_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)
        cfs_rq->runnable_load_avg += sa->load_avg;
        cfs_rq->runnable_load_sum += sa->load_sum;
 
-       if (migrated) {
-               cfs_rq->avg.load_avg += sa->load_avg;
-               cfs_rq->avg.load_sum += sa->load_sum;
-               cfs_rq->avg.util_avg += sa->util_avg;
-               cfs_rq->avg.util_sum += sa->util_sum;
-       }
+       if (migrated)
+               attach_entity_load_avg(cfs_rq, se);
 
        if (decayed || migrated)
                update_tg_load_avg(cfs_rq, 0);
@@ -2753,7 +2806,7 @@ dequeue_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)
        cfs_rq->runnable_load_avg =
                max_t(long, cfs_rq->runnable_load_avg - se->avg.load_avg, 0);
        cfs_rq->runnable_load_sum =
-               max_t(s64, cfs_rq->runnable_load_sum - se->avg.load_sum, 0);
+               max_t(s64,  cfs_rq->runnable_load_sum - se->avg.load_sum, 0);
 }
 
 /*
@@ -2821,6 +2874,11 @@ static inline void
 dequeue_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) {}
 static inline void remove_entity_load_avg(struct sched_entity *se) {}
 
+static inline void
+attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) {}
+static inline void
+detach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) {}
+
 static inline int idle_balance(struct rq *rq)
 {
        return 0;
@@ -4817,32 +4875,39 @@ next:
 done:
        return target;
 }
+
 /*
- * get_cpu_usage returns the amount of capacity of a CPU that is used by CFS
+ * cpu_util returns the amount of capacity of a CPU that is used by CFS
  * tasks. The unit of the return value must be the one of capacity so we can
- * compare the usage with the capacity of the CPU that is available for CFS
- * task (ie cpu_capacity).
- * cfs.avg.util_avg is the sum of running time of runnable tasks on a
- * CPU. It represents the amount of utilization of a CPU in the range
- * [0..SCHED_LOAD_SCALE].  The usage of a CPU can't be higher than the full
- * capacity of the CPU because it's about the running time on this CPU.
- * Nevertheless, cfs.avg.util_avg can be higher than SCHED_LOAD_SCALE
- * because of unfortunate rounding in util_avg or just
- * after migrating tasks until the average stabilizes with the new running
- * time. So we need to check that the usage stays into the range
- * [0..cpu_capacity_orig] and cap if necessary.
- * Without capping the usage, a group could be seen as overloaded (CPU0 usage
- * at 121% + CPU1 usage at 80%) whereas CPU1 has 20% of available capacity
+ * compare the utilization with the capacity of the CPU that is available for
+ * CFS task (ie cpu_capacity).
+ *
+ * cfs_rq.avg.util_avg is the sum of running time of runnable tasks plus the
+ * recent utilization of currently non-runnable tasks on a CPU. It represents
+ * the amount of utilization of a CPU in the range [0..capacity_orig] where
+ * capacity_orig is the cpu_capacity available at the highest frequency
+ * (arch_scale_freq_capacity()).
+ * The utilization of a CPU converges towards a sum equal to or less than the
+ * current capacity (capacity_curr <= capacity_orig) of the CPU because it is
+ * the running time on this CPU scaled by capacity_curr.
+ *
+ * Nevertheless, cfs_rq.avg.util_avg can be higher than capacity_curr or even
+ * higher than capacity_orig because of unfortunate rounding in
+ * cfs.avg.util_avg or just after migrating tasks and new task wakeups until
+ * the average stabilizes with the new running time. We need to check that the
+ * utilization stays within the range of [0..capacity_orig] and cap it if
+ * necessary. Without utilization capping, a group could be seen as overloaded
+ * (CPU0 utilization at 121% + CPU1 utilization at 80%) whereas CPU1 has 20% of
+ * available capacity. We allow utilization to overshoot capacity_curr (but not
+ * capacity_orig) as it useful for predicting the capacity required after task
+ * migrations (scheduler-driven DVFS).
  */
-static int get_cpu_usage(int cpu)
+static int cpu_util(int cpu)
 {
-       unsigned long usage = cpu_rq(cpu)->cfs.avg.util_avg;
+       unsigned long util = cpu_rq(cpu)->cfs.avg.util_avg;
        unsigned long capacity = capacity_orig_of(cpu);
 
-       if (usage >= SCHED_LOAD_SCALE)
-               return capacity;
-
-       return (usage * capacity) >> SCHED_LOAD_SHIFT;
+       return (util >= capacity) ? capacity : util;
 }
 
 /*
@@ -4945,7 +5010,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f
  * previous cpu.  However, the caller only guarantees p->pi_lock is held; no
  * other assumptions, including the state of rq->lock, should be made.
  */
-static void migrate_task_rq_fair(struct task_struct *p, int next_cpu)
+static void migrate_task_rq_fair(struct task_struct *p)
 {
        /*
         * We are supposed to update the task to "current" time, then its up to date
@@ -5525,10 +5590,10 @@ static int migrate_degrades_locality(struct task_struct *p, struct lb_env *env)
        unsigned long src_faults, dst_faults;
        int src_nid, dst_nid;
 
-       if (!p->numa_faults || !(env->sd->flags & SD_NUMA))
+       if (!static_branch_likely(&sched_numa_balancing))
                return -1;
 
-       if (!sched_feat(NUMA))
+       if (!p->numa_faults || !(env->sd->flags & SD_NUMA))
                return -1;
 
        src_nid = cpu_to_node(env->src_cpu);
@@ -5934,7 +5999,7 @@ struct sg_lb_stats {
        unsigned long sum_weighted_load; /* Weighted load of group's tasks */
        unsigned long load_per_task;
        unsigned long group_capacity;
-       unsigned long group_usage; /* Total usage of the group */
+       unsigned long group_util; /* Total utilization of the group */
        unsigned int sum_nr_running; /* Nr tasks running in the group */
        unsigned int idle_cpus;
        unsigned int group_weight;
@@ -6010,19 +6075,6 @@ static inline int get_sd_load_idx(struct sched_domain *sd,
        return load_idx;
 }
 
-static unsigned long default_scale_cpu_capacity(struct sched_domain *sd, int cpu)
-{
-       if ((sd->flags & SD_SHARE_CPUCAPACITY) && (sd->span_weight > 1))
-               return sd->smt_gain / sd->span_weight;
-
-       return SCHED_CAPACITY_SCALE;
-}
-
-unsigned long __weak arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
-{
-       return default_scale_cpu_capacity(sd, cpu);
-}
-
 static unsigned long scale_rt_capacity(int cpu)
 {
        struct rq *rq = cpu_rq(cpu);
@@ -6052,16 +6104,9 @@ static unsigned long scale_rt_capacity(int cpu)
 
 static void update_cpu_capacity(struct sched_domain *sd, int cpu)
 {
-       unsigned long capacity = SCHED_CAPACITY_SCALE;
+       unsigned long capacity = arch_scale_cpu_capacity(sd, cpu);
        struct sched_group *sdg = sd->groups;
 
-       if (sched_feat(ARCH_CAPACITY))
-               capacity *= arch_scale_cpu_capacity(sd, cpu);
-       else
-               capacity *= default_scale_cpu_capacity(sd, cpu);
-
-       capacity >>= SCHED_CAPACITY_SHIFT;
-
        cpu_rq(cpu)->cpu_capacity_orig = capacity;
 
        capacity *= scale_rt_capacity(cpu);
@@ -6187,8 +6232,8 @@ static inline int sg_imbalanced(struct sched_group *group)
  * group_has_capacity returns true if the group has spare capacity that could
  * be used by some tasks.
  * We consider that a group has spare capacity if the  * number of task is
- * smaller than the number of CPUs or if the usage is lower than the available
- * capacity for CFS tasks.
+ * smaller than the number of CPUs or if the utilization is lower than the
+ * available capacity for CFS tasks.
  * For the latter, we use a threshold to stabilize the state, to take into
  * account the variance of the tasks' load and to return true if the available
  * capacity in meaningful for the load balancer.
@@ -6202,7 +6247,7 @@ group_has_capacity(struct lb_env *env, struct sg_lb_stats *sgs)
                return true;
 
        if ((sgs->group_capacity * 100) >
-                       (sgs->group_usage * env->sd->imbalance_pct))
+                       (sgs->group_util * env->sd->imbalance_pct))
                return true;
 
        return false;
@@ -6223,15 +6268,15 @@ group_is_overloaded(struct lb_env *env, struct sg_lb_stats *sgs)
                return false;
 
        if ((sgs->group_capacity * 100) <
-                       (sgs->group_usage * env->sd->imbalance_pct))
+                       (sgs->group_util * env->sd->imbalance_pct))
                return true;
 
        return false;
 }
 
-static enum group_type group_classify(struct lb_env *env,
-               struct sched_group *group,
-               struct sg_lb_stats *sgs)
+static inline enum
+group_type group_classify(struct sched_group *group,
+                         struct sg_lb_stats *sgs)
 {
        if (sgs->group_no_capacity)
                return group_overloaded;
@@ -6271,7 +6316,7 @@ static inline void update_sg_lb_stats(struct lb_env *env,
                        load = source_load(i, load_idx);
 
                sgs->group_load += load;
-               sgs->group_usage += get_cpu_usage(i);
+               sgs->group_util += cpu_util(i);
                sgs->sum_nr_running += rq->cfs.h_nr_running;
 
                if (rq->nr_running > 1)
@@ -6296,7 +6341,7 @@ static inline void update_sg_lb_stats(struct lb_env *env,
        sgs->group_weight = group->group_weight;
 
        sgs->group_no_capacity = group_is_overloaded(env, sgs);
-       sgs->group_type = group_classify(env, group, sgs);
+       sgs->group_type = group_classify(group, sgs);
 }
 
 /**
@@ -6430,7 +6475,7 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd
                    group_has_capacity(env, &sds->local_stat) &&
                    (sgs->sum_nr_running > 1)) {
                        sgs->group_no_capacity = 1;
-                       sgs->group_type = group_overloaded;
+                       sgs->group_type = group_classify(sg, sgs);
                }
 
                if (update_sd_pick_busiest(env, sds, sg, sgs)) {
@@ -7610,8 +7655,22 @@ out:
         * When the cpu is attached to null domain for ex, it will not be
         * updated.
         */
-       if (likely(update_next_balance))
+       if (likely(update_next_balance)) {
                rq->next_balance = next_balance;
+
+#ifdef CONFIG_NO_HZ_COMMON
+               /*
+                * If this CPU has been elected to perform the nohz idle
+                * balance. Other idle CPUs have already rebalanced with
+                * nohz_idle_balance() and nohz.next_balance has been
+                * updated accordingly. This CPU is now running the idle load
+                * balance for itself and we need to update the
+                * nohz.next_balance accordingly.
+                */
+               if ((idle == CPU_IDLE) && time_after(nohz.next_balance, rq->next_balance))
+                       nohz.next_balance = rq->next_balance;
+#endif
+       }
 }
 
 #ifdef CONFIG_NO_HZ_COMMON
@@ -7624,6 +7683,9 @@ static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle)
        int this_cpu = this_rq->cpu;
        struct rq *rq;
        int balance_cpu;
+       /* Earliest time when we have to do rebalance again */
+       unsigned long next_balance = jiffies + 60*HZ;
+       int update_next_balance = 0;
 
        if (idle != CPU_IDLE ||
            !test_bit(NOHZ_BALANCE_KICK, nohz_flags(this_cpu)))
@@ -7655,10 +7717,19 @@ static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle)
                        rebalance_domains(rq, CPU_IDLE);
                }
 
-               if (time_after(this_rq->next_balance, rq->next_balance))
-                       this_rq->next_balance = rq->next_balance;
+               if (time_after(next_balance, rq->next_balance)) {
+                       next_balance = rq->next_balance;
+                       update_next_balance = 1;
+               }
        }
-       nohz.next_balance = this_rq->next_balance;
+
+       /*
+        * next_balance will be updated only when there is a need.
+        * When the CPU is attached to null domain for ex, it will not be
+        * updated.
+        */
+       if (likely(update_next_balance))
+               nohz.next_balance = next_balance;
 end:
        clear_bit(NOHZ_BALANCE_KICK, nohz_flags(this_cpu));
 }
@@ -7811,7 +7882,7 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
                entity_tick(cfs_rq, se, queued);
        }
 
-       if (numabalancing_enabled)
+       if (static_branch_unlikely(&sched_numa_balancing))
                task_tick_numa(rq, curr);
 }
 
@@ -7887,21 +7958,39 @@ prio_changed_fair(struct rq *rq, struct task_struct *p, int oldprio)
                check_preempt_curr(rq, p, 0);
 }
 
-static void switched_from_fair(struct rq *rq, struct task_struct *p)
+static inline bool vruntime_normalized(struct task_struct *p)
 {
        struct sched_entity *se = &p->se;
-       struct cfs_rq *cfs_rq = cfs_rq_of(se);
 
        /*
-        * Ensure the task's vruntime is normalized, so that when it's
-        * switched back to the fair class the enqueue_entity(.flags=0) will
-        * do the right thing.
+        * In both the TASK_ON_RQ_QUEUED and TASK_ON_RQ_MIGRATING cases,
+        * the dequeue_entity(.flags=0) will already have normalized the
+        * vruntime.
+        */
+       if (p->on_rq)
+               return true;
+
+       /*
+        * When !on_rq, vruntime of the task has usually NOT been normalized.
+        * But there are some cases where it has already been normalized:
         *
-        * If it's queued, then the dequeue_entity(.flags=0) will already
-        * have normalized the vruntime, if it's !queued, then only when
-        * the task is sleeping will it still have non-normalized vruntime.
+        * - A forked child which is waiting for being woken up by
+        *   wake_up_new_task().
+        * - A task which has been woken up by try_to_wake_up() and
+        *   waiting for actually being woken up by sched_ttwu_pending().
         */
-       if (!task_on_rq_queued(p) && p->state != TASK_RUNNING) {
+       if (!se->sum_exec_runtime || p->state == TASK_WAKING)
+               return true;
+
+       return false;
+}
+
+static void detach_task_cfs_rq(struct task_struct *p)
+{
+       struct sched_entity *se = &p->se;
+       struct cfs_rq *cfs_rq = cfs_rq_of(se);
+
+       if (!vruntime_normalized(p)) {
                /*
                 * Fix up our vruntime so that the current sleep doesn't
                 * cause 'unlimited' sleep bonus.
@@ -7910,28 +7999,14 @@ static void switched_from_fair(struct rq *rq, struct task_struct *p)
                se->vruntime -= cfs_rq->min_vruntime;
        }
 
-#ifdef CONFIG_SMP
        /* Catch up with the cfs_rq and remove our load when we leave */
-       __update_load_avg(cfs_rq->avg.last_update_time, cpu_of(rq), &se->avg,
-               se->on_rq * scale_load_down(se->load.weight), cfs_rq->curr == se, NULL);
-
-       cfs_rq->avg.load_avg =
-               max_t(long, cfs_rq->avg.load_avg - se->avg.load_avg, 0);
-       cfs_rq->avg.load_sum =
-               max_t(s64, cfs_rq->avg.load_sum - se->avg.load_sum, 0);
-       cfs_rq->avg.util_avg =
-               max_t(long, cfs_rq->avg.util_avg - se->avg.util_avg, 0);
-       cfs_rq->avg.util_sum =
-               max_t(s32, cfs_rq->avg.util_sum - se->avg.util_sum, 0);
-#endif
+       detach_entity_load_avg(cfs_rq, se);
 }
 
-/*
- * We switched to the sched_fair class.
- */
-static void switched_to_fair(struct rq *rq, struct task_struct *p)
+static void attach_task_cfs_rq(struct task_struct *p)
 {
        struct sched_entity *se = &p->se;
+       struct cfs_rq *cfs_rq = cfs_rq_of(se);
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
        /*
@@ -7941,31 +8016,33 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p)
        se->depth = se->parent ? se->parent->depth + 1 : 0;
 #endif
 
-       if (!task_on_rq_queued(p)) {
+       /* Synchronize task with its cfs_rq */
+       attach_entity_load_avg(cfs_rq, se);
 
+       if (!vruntime_normalized(p))
+               se->vruntime += cfs_rq->min_vruntime;
+}
+
+static void switched_from_fair(struct rq *rq, struct task_struct *p)
+{
+       detach_task_cfs_rq(p);
+}
+
+static void switched_to_fair(struct rq *rq, struct task_struct *p)
+{
+       attach_task_cfs_rq(p);
+
+       if (task_on_rq_queued(p)) {
                /*
-                * Ensure the task has a non-normalized vruntime when it is switched
-                * back to the fair class with !queued, so that enqueue_entity() at
-                * wake-up time will do the right thing.
-                *
-                * If it's queued, then the enqueue_entity(.flags=0) makes the task
-                * has non-normalized vruntime, if it's !queued, then it still has
-                * normalized vruntime.
+                * We were most likely switched from sched_rt, so
+                * kick off the schedule if running, otherwise just see
+                * if we can still preempt the current task.
                 */
-               if (p->state != TASK_RUNNING)
-                       se->vruntime += cfs_rq_of(se)->min_vruntime;
-               return;
+               if (rq->curr == p)
+                       resched_curr(rq);
+               else
+                       check_preempt_curr(rq, p, 0);
        }
-
-       /*
-        * We were most likely switched from sched_rt, so
-        * kick off the schedule if running, otherwise just see
-        * if we can still preempt the current task.
-        */
-       if (rq->curr == p)
-               resched_curr(rq);
-       else
-               check_preempt_curr(rq, p, 0);
 }
 
 /* Account for a task changing its policy or group.
@@ -8000,56 +8077,16 @@ void init_cfs_rq(struct cfs_rq *cfs_rq)
 }
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
-static void task_move_group_fair(struct task_struct *p, int queued)
+static void task_move_group_fair(struct task_struct *p)
 {
-       struct sched_entity *se = &p->se;
-       struct cfs_rq *cfs_rq;
-
-       /*
-        * If the task was not on the rq at the time of this cgroup movement
-        * it must have been asleep, sleeping tasks keep their ->vruntime
-        * absolute on their old rq until wakeup (needed for the fair sleeper
-        * bonus in place_entity()).
-        *
-        * If it was on the rq, we've just 'preempted' it, which does convert
-        * ->vruntime to a relative base.
-        *
-        * Make sure both cases convert their relative position when migrating
-        * to another cgroup's rq. This does somewhat interfere with the
-        * fair sleeper stuff for the first placement, but who cares.
-        */
-       /*
-        * When !queued, vruntime of the task has usually NOT been normalized.
-        * But there are some cases where it has already been normalized:
-        *
-        * - Moving a forked child which is waiting for being woken up by
-        *   wake_up_new_task().
-        * - Moving a task which has been woken up by try_to_wake_up() and
-        *   waiting for actually being woken up by sched_ttwu_pending().
-        *
-        * To prevent boost or penalty in the new cfs_rq caused by delta
-        * min_vruntime between the two cfs_rqs, we skip vruntime adjustment.
-        */
-       if (!queued && (!se->sum_exec_runtime || p->state == TASK_WAKING))
-               queued = 1;
-
-       if (!queued)
-               se->vruntime -= cfs_rq_of(se)->min_vruntime;
+       detach_task_cfs_rq(p);
        set_task_rq(p, task_cpu(p));
-       se->depth = se->parent ? se->parent->depth + 1 : 0;
-       if (!queued) {
-               cfs_rq = cfs_rq_of(se);
-               se->vruntime += cfs_rq->min_vruntime;
 
 #ifdef CONFIG_SMP
-               /* Virtually synchronize task with its new cfs_rq */
-               p->se.avg.last_update_time = cfs_rq->avg.last_update_time;
-               cfs_rq->avg.load_avg += p->se.avg.load_avg;
-               cfs_rq->avg.load_sum += p->se.avg.load_sum;
-               cfs_rq->avg.util_avg += p->se.avg.util_avg;
-               cfs_rq->avg.util_sum += p->se.avg.util_sum;
+       /* Tell se's cfs_rq has been changed -- migrated */
+       p->se.avg.last_update_time = 0;
 #endif
-       }
+       attach_task_cfs_rq(p);
 }
 
 void free_fair_sched_group(struct task_group *tg)
index 83a50e7ca53315695f779e2c5ac7177c423b646d..69631fa46c2f84fecd3e15599cba0e5935c1148e 100644 (file)
@@ -36,11 +36,6 @@ SCHED_FEAT(CACHE_HOT_BUDDY, true)
  */
 SCHED_FEAT(WAKEUP_PREEMPTION, true)
 
-/*
- * Use arch dependent cpu capacity functions
- */
-SCHED_FEAT(ARCH_CAPACITY, true)
-
 SCHED_FEAT(HRTICK, false)
 SCHED_FEAT(DOUBLE_TICK, false)
 SCHED_FEAT(LB_BIAS, true)
@@ -72,19 +67,5 @@ SCHED_FEAT(RT_PUSH_IPI, true)
 SCHED_FEAT(FORCE_SD_OVERLAP, false)
 SCHED_FEAT(RT_RUNTIME_SHARE, true)
 SCHED_FEAT(LB_MIN, false)
+SCHED_FEAT(ATTACH_AGE_LOAD, true)
 
-/*
- * Apply the automatic NUMA scheduling policy. Enabled automatically
- * at runtime if running on a NUMA machine. Can be controlled via
- * numa_balancing=
- */
-#ifdef CONFIG_NUMA_BALANCING
-
-/*
- * NUMA will favor moving tasks towards nodes where a higher number of
- * hinting faults are recorded during active load balancing. It will
- * resist moving tasks towards nodes where a lower number of hinting
- * faults have been recorded.
- */
-SCHED_FEAT(NUMA,       true)
-#endif
index d2ea59364a1c8f7e66d72e96c7d170088424eec0..e3cc16312046689fd04db8748304210c0d9fc8df 100644 (file)
@@ -635,11 +635,11 @@ bool sched_rt_bandwidth_account(struct rt_rq *rt_rq)
 /*
  * We ran out of runtime, see if we can borrow some from our neighbours.
  */
-static int do_balance_runtime(struct rt_rq *rt_rq)
+static void do_balance_runtime(struct rt_rq *rt_rq)
 {
        struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq);
        struct root_domain *rd = rq_of_rt_rq(rt_rq)->rd;
-       int i, weight, more = 0;
+       int i, weight;
        u64 rt_period;
 
        weight = cpumask_weight(rd->span);
@@ -673,7 +673,6 @@ static int do_balance_runtime(struct rt_rq *rt_rq)
                                diff = rt_period - rt_rq->rt_runtime;
                        iter->rt_runtime -= diff;
                        rt_rq->rt_runtime += diff;
-                       more = 1;
                        if (rt_rq->rt_runtime == rt_period) {
                                raw_spin_unlock(&iter->rt_runtime_lock);
                                break;
@@ -683,8 +682,6 @@ next:
                raw_spin_unlock(&iter->rt_runtime_lock);
        }
        raw_spin_unlock(&rt_b->rt_runtime_lock);
-
-       return more;
 }
 
 /*
@@ -796,26 +793,19 @@ static void __enable_runtime(struct rq *rq)
        }
 }
 
-static int balance_runtime(struct rt_rq *rt_rq)
+static void balance_runtime(struct rt_rq *rt_rq)
 {
-       int more = 0;
-
        if (!sched_feat(RT_RUNTIME_SHARE))
-               return more;
+               return;
 
        if (rt_rq->rt_time > rt_rq->rt_runtime) {
                raw_spin_unlock(&rt_rq->rt_runtime_lock);
-               more = do_balance_runtime(rt_rq);
+               do_balance_runtime(rt_rq);
                raw_spin_lock(&rt_rq->rt_runtime_lock);
        }
-
-       return more;
 }
 #else /* !CONFIG_SMP */
-static inline int balance_runtime(struct rt_rq *rt_rq)
-{
-       return 0;
-}
+static inline void balance_runtime(struct rt_rq *rt_rq) {}
 #endif /* CONFIG_SMP */
 
 static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun)
index 6d2a119c7ad9f63338ffb0e9e92efcbc269c2141..efd3bfc7e34722883e2f08ca82f91cffde812963 100644 (file)
@@ -84,6 +84,10 @@ static inline void update_cpu_load_active(struct rq *this_rq) { }
  */
 #define RUNTIME_INF    ((u64)~0ULL)
 
+static inline int idle_policy(int policy)
+{
+       return policy == SCHED_IDLE;
+}
 static inline int fair_policy(int policy)
 {
        return policy == SCHED_NORMAL || policy == SCHED_BATCH;
@@ -98,6 +102,11 @@ static inline int dl_policy(int policy)
 {
        return policy == SCHED_DEADLINE;
 }
+static inline bool valid_policy(int policy)
+{
+       return idle_policy(policy) || fair_policy(policy) ||
+               rt_policy(policy) || dl_policy(policy);
+}
 
 static inline int task_has_rt_policy(struct task_struct *p)
 {
@@ -109,11 +118,6 @@ static inline int task_has_dl_policy(struct task_struct *p)
        return dl_policy(p->policy);
 }
 
-static inline bool dl_time_before(u64 a, u64 b)
-{
-       return (s64)(a - b) < 0;
-}
-
 /*
  * Tells if entity @a should preempt entity @b.
  */
@@ -1003,17 +1007,7 @@ extern struct static_key sched_feat_keys[__SCHED_FEAT_NR];
 #define sched_feat(x) (sysctl_sched_features & (1UL << __SCHED_FEAT_##x))
 #endif /* SCHED_DEBUG && HAVE_JUMP_LABEL */
 
-#ifdef CONFIG_NUMA_BALANCING
-#define sched_feat_numa(x) sched_feat(x)
-#ifdef CONFIG_SCHED_DEBUG
-#define numabalancing_enabled sched_feat_numa(NUMA)
-#else
-extern bool numabalancing_enabled;
-#endif /* CONFIG_SCHED_DEBUG */
-#else
-#define sched_feat_numa(x) (0)
-#define numabalancing_enabled (0)
-#endif /* CONFIG_NUMA_BALANCING */
+extern struct static_key_false sched_numa_balancing;
 
 static inline u64 global_rt_period(void)
 {
@@ -1157,16 +1151,18 @@ static const u32 prio_to_wmult[40] = {
  /*  15 */ 119304647, 148102320, 186737708, 238609294, 286331153,
 };
 
-#define ENQUEUE_WAKEUP         1
-#define ENQUEUE_HEAD           2
+#define ENQUEUE_WAKEUP         0x01
+#define ENQUEUE_HEAD           0x02
 #ifdef CONFIG_SMP
-#define ENQUEUE_WAKING         4       /* sched_class::task_waking was called */
+#define ENQUEUE_WAKING         0x04    /* sched_class::task_waking was called */
 #else
-#define ENQUEUE_WAKING         0
+#define ENQUEUE_WAKING         0x00
 #endif
-#define ENQUEUE_REPLENISH      8
+#define ENQUEUE_REPLENISH      0x08
+#define ENQUEUE_RESTORE        0x10
 
-#define DEQUEUE_SLEEP          1
+#define DEQUEUE_SLEEP          0x01
+#define DEQUEUE_SAVE           0x02
 
 #define RETRY_TASK             ((void *)-1UL)
 
@@ -1194,7 +1190,7 @@ struct sched_class {
 
 #ifdef CONFIG_SMP
        int  (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags);
-       void (*migrate_task_rq)(struct task_struct *p, int next_cpu);
+       void (*migrate_task_rq)(struct task_struct *p);
 
        void (*task_waking) (struct task_struct *task);
        void (*task_woken) (struct rq *this_rq, struct task_struct *task);
@@ -1227,7 +1223,7 @@ struct sched_class {
        void (*update_curr) (struct rq *rq);
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
-       void (*task_move_group) (struct task_struct *p, int on_rq);
+       void (*task_move_group) (struct task_struct *p);
 #endif
 };
 
@@ -1405,6 +1401,17 @@ unsigned long arch_scale_freq_capacity(struct sched_domain *sd, int cpu)
 }
 #endif
 
+#ifndef arch_scale_cpu_capacity
+static __always_inline
+unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
+{
+       if (sd && (sd->flags & SD_SHARE_CPUCAPACITY) && (sd->span_weight > 1))
+               return sd->smt_gain / sd->span_weight;
+
+       return SCHED_CAPACITY_SCALE;
+}
+#endif
+
 static inline void sched_rt_avg_update(struct rq *rq, u64 rt_delta)
 {
        rq->rt_avg += rt_delta * arch_scale_freq_capacity(NULL, cpu_of(rq));
index 5bd4779282df00e8831d07ec09b80f9e07b73ad7..580ac2d4024ffbdb29960ccdd86424fa730b1e78 100644 (file)
@@ -347,6 +347,7 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
 {
        struct seccomp_filter *sfilter;
        int ret;
+       const bool save_orig = config_enabled(CONFIG_CHECKPOINT_RESTORE);
 
        if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
                return ERR_PTR(-EINVAL);
@@ -370,7 +371,7 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
                return ERR_PTR(-ENOMEM);
 
        ret = bpf_prog_create_from_user(&sfilter->prog, fprog,
-                                       seccomp_check_filter);
+                                       seccomp_check_filter, save_orig);
        if (ret < 0) {
                kfree(sfilter);
                return ERR_PTR(ret);
@@ -469,7 +470,7 @@ void get_seccomp_filter(struct task_struct *tsk)
 static inline void seccomp_filter_free(struct seccomp_filter *filter)
 {
        if (filter) {
-               bpf_prog_free(filter->prog);
+               bpf_prog_destroy(filter->prog);
                kfree(filter);
        }
 }
@@ -867,3 +868,76 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)
        /* prctl interface doesn't have flags, so they are always zero. */
        return do_seccomp(op, 0, uargs);
 }
+
+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
+long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
+                       void __user *data)
+{
+       struct seccomp_filter *filter;
+       struct sock_fprog_kern *fprog;
+       long ret;
+       unsigned long count = 0;
+
+       if (!capable(CAP_SYS_ADMIN) ||
+           current->seccomp.mode != SECCOMP_MODE_DISABLED) {
+               return -EACCES;
+       }
+
+       spin_lock_irq(&task->sighand->siglock);
+       if (task->seccomp.mode != SECCOMP_MODE_FILTER) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       filter = task->seccomp.filter;
+       while (filter) {
+               filter = filter->prev;
+               count++;
+       }
+
+       if (filter_off >= count) {
+               ret = -ENOENT;
+               goto out;
+       }
+       count -= filter_off;
+
+       filter = task->seccomp.filter;
+       while (filter && count > 1) {
+               filter = filter->prev;
+               count--;
+       }
+
+       if (WARN_ON(count != 1 || !filter)) {
+               /* The filter tree shouldn't shrink while we're using it. */
+               ret = -ENOENT;
+               goto out;
+       }
+
+       fprog = filter->prog->orig_prog;
+       if (!fprog) {
+               /* This must be a new non-cBPF filter, since we save every
+                * every cBPF filter's orig_prog above when
+                * CONFIG_CHECKPOINT_RESTORE is enabled.
+                */
+               ret = -EMEDIUMTYPE;
+               goto out;
+       }
+
+       ret = fprog->len;
+       if (!data)
+               goto out;
+
+       get_seccomp_filter(task);
+       spin_unlock_irq(&task->sighand->siglock);
+
+       if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
+               ret = -EFAULT;
+
+       put_seccomp_filter(task);
+       return ret;
+
+out:
+       spin_unlock_irq(&task->sighand->siglock);
+       return ret;
+}
+#endif
index a818cbc73e147382488cb0ea5bb5c490c6a46e15..d264f59bff56cb128edc68c5746280a2499752d5 100644 (file)
@@ -222,9 +222,8 @@ static void smpboot_unpark_thread(struct smp_hotplug_thread *ht, unsigned int cp
 {
        struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
 
-       if (ht->pre_unpark)
-               ht->pre_unpark(cpu);
-       kthread_unpark(tsk);
+       if (!ht->selfparking)
+               kthread_unpark(tsk);
 }
 
 void smpboot_unpark_threads(unsigned int cpu)
index 12484e5d5c88769058610aca529924ea9e882aff..867bc20e1ef142a63349c345932af24b26a1adfc 100644 (file)
@@ -73,21 +73,24 @@ static void cpu_stop_signal_done(struct cpu_stop_done *done, bool executed)
        }
 }
 
+static void __cpu_stop_queue_work(struct cpu_stopper *stopper,
+                                       struct cpu_stop_work *work)
+{
+       list_add_tail(&work->list, &stopper->works);
+       wake_up_process(stopper->thread);
+}
+
 /* queue @work to @stopper.  if offline, @work is completed immediately */
 static void cpu_stop_queue_work(unsigned int cpu, struct cpu_stop_work *work)
 {
        struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu);
-
        unsigned long flags;
 
        spin_lock_irqsave(&stopper->lock, flags);
-
-       if (stopper->enabled) {
-               list_add_tail(&work->list, &stopper->works);
-               wake_up_process(stopper->thread);
-       } else
+       if (stopper->enabled)
+               __cpu_stop_queue_work(stopper, work);
+       else
                cpu_stop_signal_done(work->done, false);
-
        spin_unlock_irqrestore(&stopper->lock, flags);
 }
 
@@ -213,6 +216,31 @@ static int multi_cpu_stop(void *data)
        return err;
 }
 
+static int cpu_stop_queue_two_works(int cpu1, struct cpu_stop_work *work1,
+                                   int cpu2, struct cpu_stop_work *work2)
+{
+       struct cpu_stopper *stopper1 = per_cpu_ptr(&cpu_stopper, cpu1);
+       struct cpu_stopper *stopper2 = per_cpu_ptr(&cpu_stopper, cpu2);
+       int err;
+
+       lg_double_lock(&stop_cpus_lock, cpu1, cpu2);
+       spin_lock_irq(&stopper1->lock);
+       spin_lock_nested(&stopper2->lock, SINGLE_DEPTH_NESTING);
+
+       err = -ENOENT;
+       if (!stopper1->enabled || !stopper2->enabled)
+               goto unlock;
+
+       err = 0;
+       __cpu_stop_queue_work(stopper1, work1);
+       __cpu_stop_queue_work(stopper2, work2);
+unlock:
+       spin_unlock(&stopper2->lock);
+       spin_unlock_irq(&stopper1->lock);
+       lg_double_unlock(&stop_cpus_lock, cpu1, cpu2);
+
+       return err;
+}
 /**
  * stop_two_cpus - stops two cpus
  * @cpu1: the cpu to stop
@@ -247,24 +275,13 @@ int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *
        cpu_stop_init_done(&done, 2);
        set_state(&msdata, MULTI_STOP_PREPARE);
 
-       /*
-        * If we observe both CPUs active we know _cpu_down() cannot yet have
-        * queued its stop_machine works and therefore ours will get executed
-        * first. Or its not either one of our CPUs that's getting unplugged,
-        * in which case we don't care.
-        *
-        * This relies on the stopper workqueues to be FIFO.
-        */
-       if (!cpu_active(cpu1) || !cpu_active(cpu2)) {
+       if (cpu1 > cpu2)
+               swap(cpu1, cpu2);
+       if (cpu_stop_queue_two_works(cpu1, &work1, cpu2, &work2)) {
                preempt_enable();
                return -ENOENT;
        }
 
-       lg_double_lock(&stop_cpus_lock, cpu1, cpu2);
-       cpu_stop_queue_work(cpu1, &work1);
-       cpu_stop_queue_work(cpu2, &work2);
-       lg_double_unlock(&stop_cpus_lock, cpu1, cpu2);
-
        preempt_enable();
 
        wait_for_completion(&done.completion);
@@ -452,6 +469,18 @@ repeat:
        }
 }
 
+void stop_machine_park(int cpu)
+{
+       struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu);
+       /*
+        * Lockless. cpu_stopper_thread() will take stopper->lock and flush
+        * the pending works before it parks, until then it is fine to queue
+        * the new works.
+        */
+       stopper->enabled = false;
+       kthread_park(stopper->thread);
+}
+
 extern void sched_set_stop_task(int cpu, struct task_struct *stop);
 
 static void cpu_stop_create(unsigned int cpu)
@@ -462,26 +491,16 @@ static void cpu_stop_create(unsigned int cpu)
 static void cpu_stop_park(unsigned int cpu)
 {
        struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu);
-       struct cpu_stop_work *work, *tmp;
-       unsigned long flags;
 
-       /* drain remaining works */
-       spin_lock_irqsave(&stopper->lock, flags);
-       list_for_each_entry_safe(work, tmp, &stopper->works, list) {
-               list_del_init(&work->list);
-               cpu_stop_signal_done(work->done, false);
-       }
-       stopper->enabled = false;
-       spin_unlock_irqrestore(&stopper->lock, flags);
+       WARN_ON(!list_empty(&stopper->works));
 }
 
-static void cpu_stop_unpark(unsigned int cpu)
+void stop_machine_unpark(int cpu)
 {
        struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu);
 
-       spin_lock_irq(&stopper->lock);
        stopper->enabled = true;
-       spin_unlock_irq(&stopper->lock);
+       kthread_unpark(stopper->thread);
 }
 
 static struct smp_hotplug_thread cpu_stop_threads = {
@@ -490,9 +509,7 @@ static struct smp_hotplug_thread cpu_stop_threads = {
        .thread_fn              = cpu_stopper_thread,
        .thread_comm            = "migration/%u",
        .create                 = cpu_stop_create,
-       .setup                  = cpu_stop_unpark,
        .park                   = cpu_stop_park,
-       .pre_unpark             = cpu_stop_unpark,
        .selfparking            = true,
 };
 
@@ -508,6 +525,7 @@ static int __init cpu_stop_init(void)
        }
 
        BUG_ON(smpboot_register_percpu_thread(&cpu_stop_threads));
+       stop_machine_unpark(raw_smp_processor_id());
        stop_machine_initialized = true;
        return 0;
 }
index e69201d8094eb8bed747329afc17528c4315a6b7..96c856b040819e30f5e9d4dad4ac396569f0eba0 100644 (file)
@@ -64,6 +64,7 @@
 #include <linux/binfmts.h>
 #include <linux/sched/sysctl.h>
 #include <linux/kexec.h>
+#include <linux/bpf.h>
 
 #include <asm/uaccess.h>
 #include <asm/processor.h>
@@ -1138,6 +1139,18 @@ static struct ctl_table kern_table[] = {
                .mode           = 0644,
                .proc_handler   = timer_migration_handler,
        },
+#endif
+#ifdef CONFIG_BPF_SYSCALL
+       {
+               .procname       = "unprivileged_bpf_disabled",
+               .data           = &sysctl_unprivileged_bpf_disabled,
+               .maxlen         = sizeof(sysctl_unprivileged_bpf_disabled),
+               .mode           = 0644,
+               /* only handle a transition from default "0" to "1" */
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &one,
+               .extra2         = &one,
+       },
 #endif
        { }
 };
index 3a38775b50c2243ea83341a9034a0eb4e3a502a3..0d8fe8b8f72772e0899662c29011d1d22c261c8f 100644 (file)
@@ -479,7 +479,7 @@ static u32 clocksource_max_adjustment(struct clocksource *cs)
  * return half the number of nanoseconds the hardware counter can technically
  * cover. This is done so that we can potentially detect problems caused by
  * delayed timers or bad hardware, which might result in time intervals that
- * are larger then what the math used can handle without overflows.
+ * are larger than what the math used can handle without overflows.
  */
 u64 clocks_calc_max_nsecs(u32 mult, u32 shift, u32 maxadj, u64 mask, u64 *max_cyc)
 {
@@ -595,16 +595,15 @@ static void __clocksource_select(bool skipcur)
  */
 static void clocksource_select(void)
 {
-       return __clocksource_select(false);
+       __clocksource_select(false);
 }
 
 static void clocksource_select_fallback(void)
 {
-       return __clocksource_select(true);
+       __clocksource_select(true);
 }
 
 #else /* !CONFIG_ARCH_USES_GETTIMEOFFSET */
-
 static inline void clocksource_select(void) { }
 static inline void clocksource_select_fallback(void) { }
 
index 457a373e21812be2d06d52e500e7b490da8a4c67..435b8850dd80a300c11ad128bc0cf51e3c23e15a 100644 (file)
@@ -59,7 +59,7 @@
 /*
  * The timer bases:
  *
- * There are more clockids then hrtimer bases. Thus, we index
+ * There are more clockids than hrtimer bases. Thus, we index
  * into the timer bases by the hrtimer_base_type enum. When trying
  * to reach a base using a clockid, hrtimer_clockid_to_base()
  * is used to convert from clockid to the proper hrtimer_base_type.
index df68cb87524885dd561b615300137a68d7ecfbdb..149cc8086aea16bbd811d3af37e187fd08bb366f 100644 (file)
@@ -99,7 +99,7 @@ static time64_t                       ntp_next_leap_sec = TIME64_MAX;
 static int pps_valid;          /* signal watchdog counter */
 static long pps_tf[3];         /* phase median filter */
 static long pps_jitter;                /* current jitter (ns) */
-static struct timespec pps_fbase; /* beginning of the last freq interval */
+static struct timespec64 pps_fbase; /* beginning of the last freq interval */
 static int pps_shift;          /* current interval duration (s) (shift) */
 static int pps_intcnt;         /* interval counter */
 static s64 pps_freq;           /* frequency offset (scaled ns/s) */
@@ -509,7 +509,7 @@ static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock);
 static void sync_cmos_clock(struct work_struct *work)
 {
        struct timespec64 now;
-       struct timespec next;
+       struct timespec64 next;
        int fail = 1;
 
        /*
@@ -559,7 +559,7 @@ static void sync_cmos_clock(struct work_struct *work)
                next.tv_nsec -= NSEC_PER_SEC;
        }
        queue_delayed_work(system_power_efficient_wq,
-                          &sync_cmos_work, timespec_to_jiffies(&next));
+                          &sync_cmos_work, timespec64_to_jiffies(&next));
 }
 
 void ntp_notify_cmos_timer(void)
@@ -773,13 +773,13 @@ int __do_adjtimex(struct timex *txc, struct timespec64 *ts, s32 *time_tai)
  * pps_normtime.nsec has a range of ( -NSEC_PER_SEC / 2, NSEC_PER_SEC / 2 ]
  * while timespec.tv_nsec has a range of [0, NSEC_PER_SEC) */
 struct pps_normtime {
-       __kernel_time_t sec;    /* seconds */
+       s64             sec;    /* seconds */
        long            nsec;   /* nanoseconds */
 };
 
 /* normalize the timestamp so that nsec is in the
    ( -NSEC_PER_SEC / 2, NSEC_PER_SEC / 2 ] interval */
-static inline struct pps_normtime pps_normalize_ts(struct timespec ts)
+static inline struct pps_normtime pps_normalize_ts(struct timespec64 ts)
 {
        struct pps_normtime norm = {
                .sec = ts.tv_sec,
@@ -861,7 +861,7 @@ static long hardpps_update_freq(struct pps_normtime freq_norm)
                pps_errcnt++;
                pps_dec_freq_interval();
                printk_deferred(KERN_ERR
-                       "hardpps: PPSERROR: interval too long - %ld s\n",
+                       "hardpps: PPSERROR: interval too long - %lld s\n",
                        freq_norm.sec);
                return 0;
        }
@@ -948,7 +948,7 @@ static void hardpps_update_phase(long error)
  * This code is based on David Mills's reference nanokernel
  * implementation. It was mostly rewritten but keeps the same idea.
  */
-void __hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts)
+void __hardpps(const struct timespec64 *phase_ts, const struct timespec64 *raw_ts)
 {
        struct pps_normtime pts_norm, freq_norm;
 
@@ -969,7 +969,7 @@ void __hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts)
        }
 
        /* ok, now we have a base for frequency calculation */
-       freq_norm = pps_normalize_ts(timespec_sub(*raw_ts, pps_fbase));
+       freq_norm = pps_normalize_ts(timespec64_sub(*raw_ts, pps_fbase));
 
        /* check that the signal is in the range
         * [1s - MAXFREQ us, 1s + MAXFREQ us], otherwise reject it */
index 65430504ca2630c31d185429e6a2573c817b43ae..af924470eac04c046e3481750a32833cef9acc13 100644 (file)
@@ -9,5 +9,5 @@ extern ktime_t ntp_get_next_leap(void);
 extern int second_overflow(unsigned long secs);
 extern int ntp_validate_timex(struct timex *);
 extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *);
-extern void __hardpps(const struct timespec *, const struct timespec *);
+extern void __hardpps(const struct timespec64 *, const struct timespec64 *);
 #endif /* _LINUX_NTP_INTERNAL_H */
index 892e3dae0aac41199e9ebbbdef8b73f6b2d57afd..f5e86d282d520a7881557de76096c1cf46f58fa7 100644 (file)
@@ -249,7 +249,7 @@ void thread_group_cputimer(struct task_struct *tsk, struct task_cputime *times)
                 * but barriers are not required because update_gt_cputime()
                 * can handle concurrent updates.
                 */
-               WRITE_ONCE(cputimer->running, 1);
+               WRITE_ONCE(cputimer->running, true);
        }
        sample_cputime_atomic(times, &cputimer->cputime_atomic);
 }
@@ -864,6 +864,13 @@ static void check_thread_timers(struct task_struct *tsk,
        unsigned long long expires;
        unsigned long soft;
 
+       /*
+        * If cputime_expires is zero, then there are no active
+        * per thread CPU timers.
+        */
+       if (task_cputime_zero(&tsk->cputime_expires))
+               return;
+
        expires = check_timers_list(timers, firing, prof_ticks(tsk));
        tsk_expires->prof_exp = expires_to_cputime(expires);
 
@@ -911,7 +918,7 @@ static inline void stop_process_timers(struct signal_struct *sig)
        struct thread_group_cputimer *cputimer = &sig->cputimer;
 
        /* Turn off cputimer->running. This is done without locking. */
-       WRITE_ONCE(cputimer->running, 0);
+       WRITE_ONCE(cputimer->running, false);
 }
 
 static u32 onecputick;
@@ -961,6 +968,19 @@ static void check_process_timers(struct task_struct *tsk,
        struct task_cputime cputime;
        unsigned long soft;
 
+       /*
+        * If cputimer is not running, then there are no active
+        * process wide timers (POSIX 1.b, itimers, RLIMIT_CPU).
+        */
+       if (!READ_ONCE(tsk->signal->cputimer.running))
+               return;
+
+        /*
+        * Signify that a thread is checking for process timers.
+        * Write access to this field is protected by the sighand lock.
+        */
+       sig->cputimer.checking_timer = true;
+
        /*
         * Collect the current process totals.
         */
@@ -1015,6 +1035,8 @@ static void check_process_timers(struct task_struct *tsk,
        sig->cputime_expires.sched_exp = sched_expires;
        if (task_cputime_zero(&sig->cputime_expires))
                stop_process_timers(sig);
+
+       sig->cputimer.checking_timer = false;
 }
 
 /*
@@ -1117,24 +1139,33 @@ static inline int task_cputime_expired(const struct task_cputime *sample,
 static inline int fastpath_timer_check(struct task_struct *tsk)
 {
        struct signal_struct *sig;
-       cputime_t utime, stime;
-
-       task_cputime(tsk, &utime, &stime);
 
        if (!task_cputime_zero(&tsk->cputime_expires)) {
-               struct task_cputime task_sample = {
-                       .utime = utime,
-                       .stime = stime,
-                       .sum_exec_runtime = tsk->se.sum_exec_runtime
-               };
+               struct task_cputime task_sample;
 
+               task_cputime(tsk, &task_sample.utime, &task_sample.stime);
+               task_sample.sum_exec_runtime = tsk->se.sum_exec_runtime;
                if (task_cputime_expired(&task_sample, &tsk->cputime_expires))
                        return 1;
        }
 
        sig = tsk->signal;
-       /* Check if cputimer is running. This is accessed without locking. */
-       if (READ_ONCE(sig->cputimer.running)) {
+       /*
+        * Check if thread group timers expired when the cputimer is
+        * running and no other thread in the group is already checking
+        * for thread group cputimers. These fields are read without the
+        * sighand lock. However, this is fine because this is meant to
+        * be a fastpath heuristic to determine whether we should try to
+        * acquire the sighand lock to check/handle timers.
+        *
+        * In the worst case scenario, if 'running' or 'checking_timer' gets
+        * set but the current thread doesn't see the change yet, we'll wait
+        * until the next thread in the group gets a scheduler interrupt to
+        * handle the timer. This isn't an issue in practice because these
+        * types of delays with signals actually getting sent are expected.
+        */
+       if (READ_ONCE(sig->cputimer.running) &&
+           !READ_ONCE(sig->cputimer.checking_timer)) {
                struct task_cputime group_sample;
 
                sample_cputime_atomic(&group_sample, &sig->cputimer.cputime_atomic);
@@ -1174,12 +1205,8 @@ void run_posix_cpu_timers(struct task_struct *tsk)
         * put them on the firing list.
         */
        check_thread_timers(tsk, &firing);
-       /*
-        * If there are any active process wide timers (POSIX 1.b, itimers,
-        * RLIMIT_CPU) cputimer must be running.
-        */
-       if (READ_ONCE(tsk->signal->cputimer.running))
-               check_process_timers(tsk, &firing);
+
+       check_process_timers(tsk, &firing);
 
        /*
         * We must release these locks before taking any timer's lock.
index c7388dee86358ae46967a20f39ffab6f44080d16..c48688904f9fec9dc033239e28c0bdcbc761e59d 100644 (file)
@@ -39,7 +39,7 @@ define fmuls(b,n,d) {
 }
 
 define timeconst(hz) {
-       print "/* Automatically generated by kernel/timeconst.bc */\n"
+       print "/* Automatically generated by kernel/time/timeconst.bc */\n"
        print "/* Time conversion constants for HZ == ", hz, " */\n"
        print "\n"
 
index 44d2cc0436f4968a32fb61772e19ca701fccbfb0..b1356b7ae57057139acf46c2c68cfa27688bcb63 100644 (file)
@@ -849,7 +849,7 @@ EXPORT_SYMBOL_GPL(ktime_get_real_seconds);
 #ifdef CONFIG_NTP_PPS
 
 /**
- * getnstime_raw_and_real - get day and raw monotonic time in timespec format
+ * ktime_get_raw_and_real_ts64 - get day and raw monotonic time in timespec format
  * @ts_raw:    pointer to the timespec to be set to raw monotonic time
  * @ts_real:   pointer to the timespec to be set to the time of day
  *
@@ -857,7 +857,7 @@ EXPORT_SYMBOL_GPL(ktime_get_real_seconds);
  * same time atomically and stores the resulting timestamps in timespec
  * format.
  */
-void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real)
+void ktime_get_raw_and_real_ts64(struct timespec64 *ts_raw, struct timespec64 *ts_real)
 {
        struct timekeeper *tk = &tk_core.timekeeper;
        unsigned long seq;
@@ -868,7 +868,7 @@ void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real)
        do {
                seq = read_seqcount_begin(&tk_core.seq);
 
-               *ts_raw = timespec64_to_timespec(tk->raw_time);
+               *ts_raw = tk->raw_time;
                ts_real->tv_sec = tk->xtime_sec;
                ts_real->tv_nsec = 0;
 
@@ -877,10 +877,10 @@ void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real)
 
        } while (read_seqcount_retry(&tk_core.seq, seq));
 
-       timespec_add_ns(ts_raw, nsecs_raw);
-       timespec_add_ns(ts_real, nsecs_real);
+       timespec64_add_ns(ts_raw, nsecs_raw);
+       timespec64_add_ns(ts_real, nsecs_real);
 }
-EXPORT_SYMBOL(getnstime_raw_and_real);
+EXPORT_SYMBOL(ktime_get_raw_and_real_ts64);
 
 #endif /* CONFIG_NTP_PPS */
 
@@ -1674,7 +1674,7 @@ static void timekeeping_adjust(struct timekeeper *tk, s64 offset)
 /**
  * accumulate_nsecs_to_secs - Accumulates nsecs into secs
  *
- * Helper function that accumulates a the nsecs greater then a second
+ * Helper function that accumulates the nsecs greater than a second
  * from the xtime_nsec field to the xtime_secs field.
  * It also calls into the NTP code to handle leapsecond processing.
  *
@@ -1726,7 +1726,7 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset,
        cycle_t interval = tk->cycle_interval << shift;
        u64 raw_nsecs;
 
-       /* If the offset is smaller then a shifted interval, do nothing */
+       /* If the offset is smaller than a shifted interval, do nothing */
        if (offset < interval)
                return offset;
 
@@ -2025,7 +2025,7 @@ int do_adjtimex(struct timex *txc)
 /**
  * hardpps() - Accessor function to NTP __hardpps function
  */
-void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts)
+void hardpps(const struct timespec64 *phase_ts, const struct timespec64 *raw_ts)
 {
        unsigned long flags;
 
index 84190f02b521c9fc77cee6e6a6722000939ee470..74591ba9474f4dff4e0c3d7b7acedbe14b5d57a8 100644 (file)
@@ -461,10 +461,17 @@ void __timer_stats_timer_set_start_info(struct timer_list *timer, void *addr)
 
 static void timer_stats_account_timer(struct timer_list *timer)
 {
-       if (likely(!timer->start_site))
+       void *site;
+
+       /*
+        * start_site can be concurrently reset by
+        * timer_stats_timer_clear_start_info()
+        */
+       site = READ_ONCE(timer->start_site);
+       if (likely(!site))
                return;
 
-       timer_stats_update_stats(timer, timer->start_pid, timer->start_site,
+       timer_stats_update_stats(timer, timer->start_pid, site,
                                 timer->function, timer->start_comm,
                                 timer->flags);
 }
@@ -867,7 +874,7 @@ unsigned long apply_slack(struct timer_list *timer, unsigned long expires)
        if (mask == 0)
                return expires;
 
-       bit = find_last_bit(&mask, BITS_PER_LONG);
+       bit = __fls(mask);
 
        mask = (1UL << bit) - 1;
 
index 3e4840633d3ee7bd926f1fe67f8b0a4b324514da..44aa462d033f700a86e1439d1b24053daca4094a 100644 (file)
@@ -523,6 +523,7 @@ static int stutter;
  */
 void stutter_wait(const char *title)
 {
+       cond_resched_rcu_qs();
        while (READ_ONCE(stutter_pause_test) ||
               (torture_runnable && !READ_ONCE(*torture_runnable))) {
                if (stutter_pause_test)
index 0fe96c7c8803c759b963411198fa18153a539f46..4228fd3682c3d45894f0f3bbc13a715331a24e7c 100644 (file)
@@ -199,6 +199,11 @@ static u64 bpf_perf_event_read(u64 r1, u64 index, u64 r3, u64 r4, u64 r5)
        if (!event)
                return -ENOENT;
 
+       /* make sure event is local and doesn't have pmu::count */
+       if (event->oncpu != smp_processor_id() ||
+           event->pmu->count)
+               return -EINVAL;
+
        /*
         * we don't know if the function is run successfully by the
         * return value. It can be judged in other places, such as
@@ -207,14 +212,58 @@ static u64 bpf_perf_event_read(u64 r1, u64 index, u64 r3, u64 r4, u64 r5)
        return perf_event_read_local(event);
 }
 
-const struct bpf_func_proto bpf_perf_event_read_proto = {
+static const struct bpf_func_proto bpf_perf_event_read_proto = {
        .func           = bpf_perf_event_read,
-       .gpl_only       = false,
+       .gpl_only       = true,
        .ret_type       = RET_INTEGER,
        .arg1_type      = ARG_CONST_MAP_PTR,
        .arg2_type      = ARG_ANYTHING,
 };
 
+static u64 bpf_perf_event_output(u64 r1, u64 r2, u64 index, u64 r4, u64 size)
+{
+       struct pt_regs *regs = (struct pt_regs *) (long) r1;
+       struct bpf_map *map = (struct bpf_map *) (long) r2;
+       struct bpf_array *array = container_of(map, struct bpf_array, map);
+       void *data = (void *) (long) r4;
+       struct perf_sample_data sample_data;
+       struct perf_event *event;
+       struct perf_raw_record raw = {
+               .size = size,
+               .data = data,
+       };
+
+       if (unlikely(index >= array->map.max_entries))
+               return -E2BIG;
+
+       event = (struct perf_event *)array->ptrs[index];
+       if (unlikely(!event))
+               return -ENOENT;
+
+       if (unlikely(event->attr.type != PERF_TYPE_SOFTWARE ||
+                    event->attr.config != PERF_COUNT_SW_BPF_OUTPUT))
+               return -EINVAL;
+
+       if (unlikely(event->oncpu != smp_processor_id()))
+               return -EOPNOTSUPP;
+
+       perf_sample_data_init(&sample_data, 0, 0);
+       sample_data.raw = &raw;
+       perf_event_output(event, &sample_data, regs);
+       return 0;
+}
+
+static const struct bpf_func_proto bpf_perf_event_output_proto = {
+       .func           = bpf_perf_event_output,
+       .gpl_only       = true,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_CONST_MAP_PTR,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_PTR_TO_STACK,
+       .arg5_type      = ARG_CONST_STACK_SIZE,
+};
+
 static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id)
 {
        switch (func_id) {
@@ -242,6 +291,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func
                return &bpf_get_smp_processor_id_proto;
        case BPF_FUNC_perf_event_read:
                return &bpf_perf_event_read_proto;
+       case BPF_FUNC_perf_event_output:
+               return &bpf_perf_event_output_proto;
        default:
                return NULL;
        }
index b0623ac785a22287526327a021d81fe3eaf5fafb..00611e95a8ee00bb91e7ccbd41c3ebcbc580e4f8 100644 (file)
@@ -5697,7 +5697,7 @@ free:
 }
 
 static void
-ftrace_graph_probe_sched_switch(void *ignore,
+ftrace_graph_probe_sched_switch(void *ignore, bool preempt,
                        struct task_struct *prev, struct task_struct *next)
 {
        unsigned long long timestamp;
index f270088e9929aa2e085a15960828929140ae1037..4c896a0101bdcb231581a3cced5f2ef4a3556d08 100644 (file)
@@ -16,7 +16,8 @@ static int                    sched_ref;
 static DEFINE_MUTEX(sched_register_mutex);
 
 static void
-probe_sched_switch(void *ignore, struct task_struct *prev, struct task_struct *next)
+probe_sched_switch(void *ignore, bool preempt,
+                  struct task_struct *prev, struct task_struct *next)
 {
        if (unlikely(!sched_ref))
                return;
index 12cbe77b413620cb80436ab55d3758237e31a5b9..4bcfbac289ff9e9e6ab4d39772ad2dffd89509a8 100644 (file)
@@ -420,7 +420,7 @@ tracing_sched_wakeup_trace(struct trace_array *tr,
 }
 
 static void notrace
-probe_wakeup_sched_switch(void *ignore,
+probe_wakeup_sched_switch(void *ignore, bool preempt,
                          struct task_struct *prev, struct task_struct *next)
 {
        struct trace_array_cpu *data;
index 7c200030acf77a76740fbe9288cefe10058e87a8..e0a122bc1cdb06d25c686d5e7fb68c7217169e96 100644 (file)
@@ -76,6 +76,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/bitops.h>
+#include <linux/crc32.h>
 #include <asm/unaligned.h>
 
 #include <linux/sw842.h>
@@ -98,6 +99,7 @@
 #define I2_BITS                (8)
 #define I4_BITS                (9)
 #define I8_BITS                (8)
+#define CRC_BITS       (32)
 
 #define REPEAT_BITS_MAX                (0x3f)
 #define SHORT_DATA_BITS_MAX    (0x7)
index 7ce68948e68c84d41c796c70a726895e65c68d78..4051339bdfbda6e6869d7f7c669672ba65f3d3f1 100644 (file)
@@ -490,6 +490,7 @@ int sw842_compress(const u8 *in, unsigned int ilen,
        int ret;
        u64 last, next, pad, total;
        u8 repeat_count = 0;
+       u32 crc;
 
        BUILD_BUG_ON(sizeof(*p) > SW842_MEM_COMPRESS);
 
@@ -580,6 +581,18 @@ skip_comp:
        if (ret)
                return ret;
 
+       /*
+        * crc(0:31) is appended to target data starting with the next
+        * bit after End of stream template.
+        * nx842 calculates CRC for data in big-endian format. So doing
+        * same here so that sw842 decompression can be used for both
+        * compressed data.
+        */
+       crc = crc32_be(0, in, ilen);
+       ret = add_bits(p, crc, CRC_BITS);
+       if (ret)
+               return ret;
+
        if (p->bit) {
                p->out++;
                p->olen--;
index 5446ff0c9ba0f186448cffc34942858dc1a7d0a9..8881dad2a6a086ac7ca7fdb86bf4d238ac21539a 100644 (file)
@@ -285,6 +285,7 @@ int sw842_decompress(const u8 *in, unsigned int ilen,
        struct sw842_param p;
        int ret;
        u64 op, rep, tmp, bytes, total;
+       u64 crc;
 
        p.in = (u8 *)in;
        p.bit = 0;
@@ -375,6 +376,22 @@ int sw842_decompress(const u8 *in, unsigned int ilen,
                }
        } while (op != OP_END);
 
+       /*
+        * crc(0:31) is saved in compressed data starting with the
+        * next bit after End of stream template.
+        */
+       ret = next_bits(&p, &crc, CRC_BITS);
+       if (ret)
+               return ret;
+
+       /*
+        * Validate CRC saved in compressed data.
+        */
+       if (crc != (u64)crc32_be(0, out, total - p.olen)) {
+               pr_debug("CRC mismatch for decompression\n");
+               return -EINVAL;
+       }
+
        if (unlikely((total - p.olen) > UINT_MAX))
                return -ENOSPC;
 
index 13a7c6ae3feca4b0e24bdd90281933a8ab09bf4f..8de3b012eac77ed2c14160022d0cc6b9f75773c6 100644 (file)
@@ -26,7 +26,8 @@ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
         bust_spinlocks.o kasprintf.o bitmap.o scatterlist.o \
         gcd.o lcm.o list_sort.o uuid.o flex_array.o iov_iter.o clz_ctz.o \
         bsearch.o find_bit.o llist.o memweight.o kfifo.o \
-        percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o
+        percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o \
+        once.o
 obj-y += string_helpers.o
 obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
 obj-y += hexdump.o
index b1c93e94ca7a430ea68c717e0e26d56057d75636..858dc1aae478bad9cb30275cb7fd7905d73f6ede 100644 (file)
 #include <linux/dma-mapping.h>
 #include <linux/hash.h>
 
-#ifndef        DMA_ERROR_CODE
-#define        DMA_ERROR_CODE (~(dma_addr_t)0x0)
-#endif
-
 static unsigned long iommu_large_alloc = 15;
 
 static DEFINE_PER_CPU(unsigned int, iommu_hash_common);
@@ -123,7 +119,7 @@ unsigned long iommu_tbl_range_alloc(struct device *dev,
        /* Sanity check */
        if (unlikely(npages == 0)) {
                WARN_ON_ONCE(1);
-               return DMA_ERROR_CODE;
+               return IOMMU_ERROR_CODE;
        }
 
        if (largealloc) {
@@ -206,7 +202,7 @@ unsigned long iommu_tbl_range_alloc(struct device *dev,
                        goto again;
                } else {
                        /* give up */
-                       n = DMA_ERROR_CODE;
+                       n = IOMMU_ERROR_CODE;
                        goto bail;
                }
        }
@@ -259,7 +255,7 @@ void iommu_tbl_range_free(struct iommu_map_table *iommu, u64 dma_addr,
        unsigned long flags;
        unsigned long shift = iommu->table_shift;
 
-       if (entry == DMA_ERROR_CODE) /* use default addr->entry mapping */
+       if (entry == IOMMU_ERROR_CODE) /* use default addr->entry mapping */
                entry = (dma_addr - iommu->table_map_base) >> shift;
        pool = get_pool(iommu, entry);
 
index 95c52a95259e89b6fc0ab279c4e51cfc5f35bf1f..c7e0a705eecfb1c389dcef39ee4f97c451728d44 100644 (file)
@@ -319,3 +319,202 @@ int mpi_set_buffer(MPI a, const void *xbuffer, unsigned nbytes, int sign)
        return 0;
 }
 EXPORT_SYMBOL_GPL(mpi_set_buffer);
+
+/**
+ * mpi_write_to_sgl() - Funnction exports MPI to an sgl (msb first)
+ *
+ * This function works in the same way as the mpi_read_buffer, but it
+ * takes an sgl instead of u8 * buf.
+ *
+ * @a:         a multi precision integer
+ * @sgl:       scatterlist to write to. Needs to be at least
+ *             mpi_get_size(a) long.
+ * @nbytes:    in/out param - it has the be set to the maximum number of
+ *             bytes that can be written to sgl. This has to be at least
+ *             the size of the integer a. On return it receives the actual
+ *             length of the data written.
+ * @sign:      if not NULL, it will be set to the sign of a.
+ *
+ * Return:     0 on success or error code in case of error
+ */
+int mpi_write_to_sgl(MPI a, struct scatterlist *sgl, unsigned *nbytes,
+                    int *sign)
+{
+       u8 *p, *p2;
+       mpi_limb_t alimb, alimb2;
+       unsigned int n = mpi_get_size(a);
+       int i, x, y = 0, lzeros = 0, buf_len;
+
+       if (!nbytes || *nbytes < n)
+               return -EINVAL;
+
+       if (sign)
+               *sign = a->sign;
+
+       p = (void *)&a->d[a->nlimbs] - 1;
+
+       for (i = a->nlimbs * sizeof(alimb) - 1; i >= 0; i--, p--) {
+               if (!*p)
+                       lzeros++;
+               else
+                       break;
+       }
+
+       *nbytes = n - lzeros;
+       buf_len = sgl->length;
+       p2 = sg_virt(sgl);
+
+       for (i = a->nlimbs - 1; i >= 0; i--) {
+               alimb = a->d[i];
+               p = (u8 *)&alimb2;
+#if BYTES_PER_MPI_LIMB == 4
+               *p++ = alimb >> 24;
+               *p++ = alimb >> 16;
+               *p++ = alimb >> 8;
+               *p++ = alimb;
+#elif BYTES_PER_MPI_LIMB == 8
+               *p++ = alimb >> 56;
+               *p++ = alimb >> 48;
+               *p++ = alimb >> 40;
+               *p++ = alimb >> 32;
+               *p++ = alimb >> 24;
+               *p++ = alimb >> 16;
+               *p++ = alimb >> 8;
+               *p++ = alimb;
+#else
+#error please implement for this limb size.
+#endif
+               if (lzeros > 0) {
+                       if (lzeros >= sizeof(alimb)) {
+                               p -= sizeof(alimb);
+                               continue;
+                       } else {
+                               mpi_limb_t *limb1 = (void *)p - sizeof(alimb);
+                               mpi_limb_t *limb2 = (void *)p - sizeof(alimb)
+                                                       + lzeros;
+                               *limb1 = *limb2;
+                               p -= lzeros;
+                               y = lzeros;
+                       }
+                       lzeros -= sizeof(alimb);
+               }
+
+               p = p - (sizeof(alimb) - y);
+
+               for (x = 0; x < sizeof(alimb) - y; x++) {
+                       if (!buf_len) {
+                               sgl = sg_next(sgl);
+                               if (!sgl)
+                                       return -EINVAL;
+                               buf_len = sgl->length;
+                               p2 = sg_virt(sgl);
+                       }
+                       *p2++ = *p++;
+                       buf_len--;
+               }
+               y = 0;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mpi_write_to_sgl);
+
+/*
+ * mpi_read_raw_from_sgl() - Function allocates an MPI and populates it with
+ *                          data from the sgl
+ *
+ * This function works in the same way as the mpi_read_raw_data, but it
+ * takes an sgl instead of void * buffer. i.e. it allocates
+ * a new MPI and reads the content of the sgl to the MPI.
+ *
+ * @sgl:       scatterlist to read from
+ * @len:       number of bytes to read
+ *
+ * Return:     Pointer to a new MPI or NULL on error
+ */
+MPI mpi_read_raw_from_sgl(struct scatterlist *sgl, unsigned int len)
+{
+       struct scatterlist *sg;
+       int x, i, j, z, lzeros, ents;
+       unsigned int nbits, nlimbs, nbytes;
+       mpi_limb_t a;
+       MPI val = NULL;
+
+       lzeros = 0;
+       ents = sg_nents(sgl);
+
+       for_each_sg(sgl, sg, ents, i) {
+               const u8 *buff = sg_virt(sg);
+               int len = sg->length;
+
+               while (len && !*buff) {
+                       lzeros++;
+                       len--;
+                       buff++;
+               }
+
+               if (len && *buff)
+                       break;
+
+               ents--;
+               lzeros = 0;
+       }
+
+       sgl = sg;
+
+       if (!ents)
+               nbytes = 0;
+       else
+               nbytes = len - lzeros;
+
+       nbits = nbytes * 8;
+       if (nbits > MAX_EXTERN_MPI_BITS) {
+               pr_info("MPI: mpi too large (%u bits)\n", nbits);
+               return NULL;
+       }
+
+       if (nbytes > 0)
+               nbits -= count_leading_zeros(*(u8 *)(sg_virt(sgl) + lzeros));
+       else
+               nbits = 0;
+
+       nlimbs = DIV_ROUND_UP(nbytes, BYTES_PER_MPI_LIMB);
+       val = mpi_alloc(nlimbs);
+       if (!val)
+               return NULL;
+
+       val->nbits = nbits;
+       val->sign = 0;
+       val->nlimbs = nlimbs;
+
+       if (nbytes == 0)
+               return val;
+
+       j = nlimbs - 1;
+       a = 0;
+       z = 0;
+       x = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB;
+       x %= BYTES_PER_MPI_LIMB;
+
+       for_each_sg(sgl, sg, ents, i) {
+               const u8 *buffer = sg_virt(sg) + lzeros;
+               int len = sg->length - lzeros;
+               int buf_shift = x;
+
+               if  (sg_is_last(sg) && (len % BYTES_PER_MPI_LIMB))
+                       len += BYTES_PER_MPI_LIMB - (len % BYTES_PER_MPI_LIMB);
+
+               for (; x < len + buf_shift; x++) {
+                       a <<= 8;
+                       a |= *buffer++;
+                       if (((z + x + 1) % BYTES_PER_MPI_LIMB) == 0) {
+                               val->d[j--] = a;
+                               a = 0;
+                       }
+               }
+               z += x;
+               x = 0;
+               lzeros = 0;
+       }
+       return val;
+}
+EXPORT_SYMBOL_GPL(mpi_read_raw_from_sgl);
index 88d3d32e59236bc1127d88c781bc014d70920652..6019c53c669e176bcf36cf3e8bffb35c9b893432 100644 (file)
@@ -43,6 +43,12 @@ static void print_seq_line(struct nmi_seq_buf *s, int start, int end)
        printk("%.*s", (end - start) + 1, buf);
 }
 
+/*
+ * When raise() is called it will be is passed a pointer to the
+ * backtrace_mask. Architectures that call nmi_cpu_backtrace()
+ * directly from their raise() functions may rely on the mask
+ * they are passed being updated as a side effect of this call.
+ */
 void nmi_trigger_all_cpu_backtrace(bool include_self,
                                   void (*raise)(cpumask_t *mask))
 {
@@ -149,7 +155,10 @@ bool nmi_cpu_backtrace(struct pt_regs *regs)
                /* Replace printk to write into the NMI seq */
                this_cpu_write(printk_func, nmi_vprintk);
                pr_warn("NMI backtrace for cpu %d\n", cpu);
-               show_regs(regs);
+               if (regs)
+                       show_regs(regs);
+               else
+                       dump_stack();
                this_cpu_write(printk_func, printk_func_save);
 
                cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
diff --git a/lib/once.c b/lib/once.c
new file mode 100644 (file)
index 0000000..05c8604
--- /dev/null
@@ -0,0 +1,62 @@
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/once.h>
+#include <linux/random.h>
+
+struct once_work {
+       struct work_struct work;
+       struct static_key *key;
+};
+
+static void once_deferred(struct work_struct *w)
+{
+       struct once_work *work;
+
+       work = container_of(w, struct once_work, work);
+       BUG_ON(!static_key_enabled(work->key));
+       static_key_slow_dec(work->key);
+       kfree(work);
+}
+
+static void once_disable_jump(struct static_key *key)
+{
+       struct once_work *w;
+
+       w = kmalloc(sizeof(*w), GFP_ATOMIC);
+       if (!w)
+               return;
+
+       INIT_WORK(&w->work, once_deferred);
+       w->key = key;
+       schedule_work(&w->work);
+}
+
+static DEFINE_SPINLOCK(once_lock);
+
+bool __do_once_start(bool *done, unsigned long *flags)
+       __acquires(once_lock)
+{
+       spin_lock_irqsave(&once_lock, *flags);
+       if (*done) {
+               spin_unlock_irqrestore(&once_lock, *flags);
+               /* Keep sparse happy by restoring an even lock count on
+                * this lock. In case we return here, we don't call into
+                * __do_once_done but return early in the DO_ONCE() macro.
+                */
+               __acquire(once_lock);
+               return false;
+       }
+
+       return true;
+}
+EXPORT_SYMBOL(__do_once_start);
+
+void __do_once_done(bool *done, struct static_key *once_key,
+                   unsigned long *flags)
+       __releases(once_lock)
+{
+       *done = true;
+       spin_unlock_irqrestore(&once_lock, *flags);
+       once_disable_jump(once_key);
+}
+EXPORT_SYMBOL(__do_once_done);
index 0bee183fa18faaf3314ac37244469f111408cd26..12111910ccd076aaa1d01cbb9a9623306514a6cd 100644 (file)
@@ -181,7 +181,7 @@ void prandom_seed(u32 entropy)
         * No locking on the CPUs, but then somewhat random results are, well,
         * expected.
         */
-       for_each_possible_cpu (i) {
+       for_each_possible_cpu(i) {
                struct rnd_state *state = &per_cpu(net_rand_state, i);
 
                state->s1 = __seed(state->s1 ^ entropy, 2U);
@@ -201,7 +201,7 @@ static int __init prandom_init(void)
        prandom_state_selftest();
 
        for_each_possible_cpu(i) {
-               struct rnd_state *state = &per_cpu(net_rand_state,i);
+               struct rnd_state *state = &per_cpu(net_rand_state, i);
                u32 weak_seed = (i + jiffies) ^ random_get_entropy();
 
                prandom_seed_early(state, weak_seed, true);
@@ -238,13 +238,30 @@ static void __init __prandom_start_seed_timer(void)
        add_timer(&seed_timer);
 }
 
+void prandom_seed_full_state(struct rnd_state __percpu *pcpu_state)
+{
+       int i;
+
+       for_each_possible_cpu(i) {
+               struct rnd_state *state = per_cpu_ptr(pcpu_state, i);
+               u32 seeds[4];
+
+               get_random_bytes(&seeds, sizeof(seeds));
+               state->s1 = __seed(seeds[0],   2U);
+               state->s2 = __seed(seeds[1],   8U);
+               state->s3 = __seed(seeds[2],  16U);
+               state->s4 = __seed(seeds[3], 128U);
+
+               prandom_warmup(state);
+       }
+}
+
 /*
  *     Generate better values after random number generator
  *     is fully initialized.
  */
 static void __prandom_reseed(bool late)
 {
-       int i;
        unsigned long flags;
        static bool latch = false;
        static DEFINE_SPINLOCK(lock);
@@ -266,19 +283,7 @@ static void __prandom_reseed(bool late)
                goto out;
 
        latch = true;
-
-       for_each_possible_cpu(i) {
-               struct rnd_state *state = &per_cpu(net_rand_state,i);
-               u32 seeds[4];
-
-               get_random_bytes(&seeds, sizeof(seeds));
-               state->s1 = __seed(seeds[0],   2U);
-               state->s2 = __seed(seeds[1],   8U);
-               state->s3 = __seed(seeds[2],  16U);
-               state->s4 = __seed(seeds[3], 128U);
-
-               prandom_warmup(state);
-       }
+       prandom_seed_full_state(&net_rand_state);
 out:
        spin_unlock_irqrestore(&lock, flags);
 }
index bbac913f96bc1662ac3d7ef37c82080d9c17562f..3fd0311c3ba70fa2e6238237c1afe58f0c47e75d 100644 (file)
@@ -1880,7 +1880,7 @@ static int __split_huge_page_map(struct page *page,
                 * here). But it is generally safer to never allow
                 * small and huge TLB entries for the same virtual
                 * address to be loaded simultaneously. So instead of
-                * doing "pmd_populate(); flush_tlb_range();" we first
+                * doing "pmd_populate(); flush_pmd_tlb_range();" we first
                 * mark the current pmd notpresent (atomically because
                 * here the pmd_trans_huge and pmd_trans_splitting
                 * must remain set at all times on the pmd until the
index 6b674e00153cea664ecadcec49f7c4c35bb843c0..7d3db0247983b22b121290c2203ba2c2fb544ec0 100644 (file)
@@ -57,35 +57,59 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
 }
 #endif
 
+#ifndef __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
+int ptep_clear_flush_young(struct vm_area_struct *vma,
+                          unsigned long address, pte_t *ptep)
+{
+       int young;
+       young = ptep_test_and_clear_young(vma, address, ptep);
+       if (young)
+               flush_tlb_page(vma, address);
+       return young;
+}
+#endif
+
+#ifndef __HAVE_ARCH_PTEP_CLEAR_FLUSH
+pte_t ptep_clear_flush(struct vm_area_struct *vma, unsigned long address,
+                      pte_t *ptep)
+{
+       struct mm_struct *mm = (vma)->vm_mm;
+       pte_t pte;
+       pte = ptep_get_and_clear(mm, address, ptep);
+       if (pte_accessible(mm, pte))
+               flush_tlb_page(vma, address);
+       return pte;
+}
+#endif
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+
+#ifndef __HAVE_ARCH_FLUSH_PMD_TLB_RANGE
+
+/*
+ * ARCHes with special requirements for evicting THP backing TLB entries can
+ * implement this. Otherwise also, it can help optimize normal TLB flush in
+ * THP regime. stock flush_tlb_range() typically has optimization to nuke the
+ * entire TLB TLB if flush span is greater than a threshhold, which will
+ * likely be true for a single huge page. Thus a single thp flush will
+ * invalidate the entire TLB which is not desitable.
+ * e.g. see arch/arc: flush_pmd_tlb_range
+ */
+#define flush_pmd_tlb_range(vma, addr, end)    flush_tlb_range(vma, addr, end)
+#endif
+
 #ifndef __HAVE_ARCH_PMDP_SET_ACCESS_FLAGS
 int pmdp_set_access_flags(struct vm_area_struct *vma,
                          unsigned long address, pmd_t *pmdp,
                          pmd_t entry, int dirty)
 {
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
        int changed = !pmd_same(*pmdp, entry);
        VM_BUG_ON(address & ~HPAGE_PMD_MASK);
        if (changed) {
                set_pmd_at(vma->vm_mm, address, pmdp, entry);
-               flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
+               flush_pmd_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
        }
        return changed;
-#else /* CONFIG_TRANSPARENT_HUGEPAGE */
-       BUG();
-       return 0;
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
-}
-#endif
-
-#ifndef __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
-int ptep_clear_flush_young(struct vm_area_struct *vma,
-                          unsigned long address, pte_t *ptep)
-{
-       int young;
-       young = ptep_test_and_clear_young(vma, address, ptep);
-       if (young)
-               flush_tlb_page(vma, address);
-       return young;
 }
 #endif
 
@@ -94,33 +118,15 @@ int pmdp_clear_flush_young(struct vm_area_struct *vma,
                           unsigned long address, pmd_t *pmdp)
 {
        int young;
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
        VM_BUG_ON(address & ~HPAGE_PMD_MASK);
-#else
-       BUG();
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
        young = pmdp_test_and_clear_young(vma, address, pmdp);
        if (young)
-               flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
+               flush_pmd_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
        return young;
 }
 #endif
 
-#ifndef __HAVE_ARCH_PTEP_CLEAR_FLUSH
-pte_t ptep_clear_flush(struct vm_area_struct *vma, unsigned long address,
-                      pte_t *ptep)
-{
-       struct mm_struct *mm = (vma)->vm_mm;
-       pte_t pte;
-       pte = ptep_get_and_clear(mm, address, ptep);
-       if (pte_accessible(mm, pte))
-               flush_tlb_page(vma, address);
-       return pte;
-}
-#endif
-
 #ifndef __HAVE_ARCH_PMDP_HUGE_CLEAR_FLUSH
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
 pmd_t pmdp_huge_clear_flush(struct vm_area_struct *vma, unsigned long address,
                            pmd_t *pmdp)
 {
@@ -128,14 +134,12 @@ pmd_t pmdp_huge_clear_flush(struct vm_area_struct *vma, unsigned long address,
        VM_BUG_ON(address & ~HPAGE_PMD_MASK);
        VM_BUG_ON(!pmd_trans_huge(*pmdp));
        pmd = pmdp_huge_get_and_clear(vma->vm_mm, address, pmdp);
-       flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
+       flush_pmd_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
        return pmd;
 }
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 #endif
 
 #ifndef __HAVE_ARCH_PMDP_SPLITTING_FLUSH
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
 void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address,
                          pmd_t *pmdp)
 {
@@ -143,13 +147,11 @@ void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address,
        VM_BUG_ON(address & ~HPAGE_PMD_MASK);
        set_pmd_at(vma->vm_mm, address, pmdp, pmd);
        /* tlb flush only to serialize against gup-fast */
-       flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
+       flush_pmd_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
 }
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 #endif
 
 #ifndef __HAVE_ARCH_PGTABLE_DEPOSIT
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
 void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
                                pgtable_t pgtable)
 {
@@ -162,11 +164,9 @@ void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
                list_add(&pgtable->lru, &pmd_huge_pte(mm, pmdp)->lru);
        pmd_huge_pte(mm, pmdp) = pgtable;
 }
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 #endif
 
 #ifndef __HAVE_ARCH_PGTABLE_WITHDRAW
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
 /* no "address" argument so destroys page coloring of some arch */
 pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
 {
@@ -185,23 +185,19 @@ pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
        }
        return pgtable;
 }
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 #endif
 
 #ifndef __HAVE_ARCH_PMDP_INVALIDATE
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
 void pmdp_invalidate(struct vm_area_struct *vma, unsigned long address,
                     pmd_t *pmdp)
 {
        pmd_t entry = *pmdp;
        set_pmd_at(vma->vm_mm, address, pmdp, pmd_mknotpresent(entry));
-       flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
+       flush_pmd_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
 }
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 #endif
 
 #ifndef pmdp_collapse_flush
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
 pmd_t pmdp_collapse_flush(struct vm_area_struct *vma, unsigned long address,
                          pmd_t *pmdp)
 {
@@ -214,8 +210,8 @@ pmd_t pmdp_collapse_flush(struct vm_area_struct *vma, unsigned long address,
        VM_BUG_ON(address & ~HPAGE_PMD_MASK);
        VM_BUG_ON(pmd_trans_huge(*pmdp));
        pmd = pmdp_huge_get_and_clear(vma->vm_mm, address, pmdp);
-       flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
+       flush_pmd_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
        return pmd;
 }
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 #endif
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
index 2faaa2976447a104ac5017ce7f4ad1e52b808b0b..af3a519e40c2ba9e08a0ff41eadeae945e813dbb 100644 (file)
@@ -2688,52 +2688,5 @@ static int __init proc_vmalloc_init(void)
 }
 module_init(proc_vmalloc_init);
 
-void get_vmalloc_info(struct vmalloc_info *vmi)
-{
-       struct vmap_area *va;
-       unsigned long free_area_size;
-       unsigned long prev_end;
-
-       vmi->used = 0;
-       vmi->largest_chunk = 0;
-
-       prev_end = VMALLOC_START;
-
-       rcu_read_lock();
-
-       if (list_empty(&vmap_area_list)) {
-               vmi->largest_chunk = VMALLOC_TOTAL;
-               goto out;
-       }
-
-       list_for_each_entry_rcu(va, &vmap_area_list, list) {
-               unsigned long addr = va->va_start;
-
-               /*
-                * Some archs keep another range for modules in vmalloc space
-                */
-               if (addr < VMALLOC_START)
-                       continue;
-               if (addr >= VMALLOC_END)
-                       break;
-
-               if (va->flags & (VM_LAZY_FREE | VM_LAZY_FREEING))
-                       continue;
-
-               vmi->used += (va->va_end - va->va_start);
-
-               free_area_size = addr - prev_end;
-               if (vmi->largest_chunk < free_area_size)
-                       vmi->largest_chunk = free_area_size;
-
-               prev_end = va->va_end;
-       }
-
-       if (VMALLOC_END - prev_end > vmi->largest_chunk)
-               vmi->largest_chunk = VMALLOC_END - prev_end;
-
-out:
-       rcu_read_unlock();
-}
 #endif
 
index ae1896fa45e2f891a89fd76cb8c4da5a1031f5e7..83b19e072224b031403d5904f1d9a8de7eb05ee2 100644 (file)
 
 void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype)
 {
+       dev->addr_len = EUI64_ADDR_LEN;
+       dev->type = ARPHRD_6LOWPAN;
+       dev->mtu = IPV6_MIN_MTU;
+       dev->priv_flags |= IFF_NO_QUEUE;
+
        lowpan_priv(dev)->lltype = lltype;
 }
 EXPORT_SYMBOL(lowpan_netdev_setup);
index 1e0071fdcf72a036d6697f2e453ba303b69abbe0..346b5c1a91851efd16b22695a7a1259c1cca9139 100644 (file)
 #include <linux/bitops.h>
 #include <linux/if_arp.h>
 #include <linux/netdevice.h>
+
 #include <net/6lowpan.h>
 #include <net/ipv6.h>
-#include <net/af_ieee802154.h>
+
+/* special link-layer handling */
+#include <net/mac802154.h>
 
 #include "nhc.h"
 
+/* Values of fields within the IPHC encoding first byte */
+#define LOWPAN_IPHC_TF_MASK    0x18
+#define LOWPAN_IPHC_TF_00      0x00
+#define LOWPAN_IPHC_TF_01      0x08
+#define LOWPAN_IPHC_TF_10      0x10
+#define LOWPAN_IPHC_TF_11      0x18
+
+#define LOWPAN_IPHC_NH         0x04
+
+#define LOWPAN_IPHC_HLIM_MASK  0x03
+#define LOWPAN_IPHC_HLIM_00    0x00
+#define LOWPAN_IPHC_HLIM_01    0x01
+#define LOWPAN_IPHC_HLIM_10    0x02
+#define LOWPAN_IPHC_HLIM_11    0x03
+
+/* Values of fields within the IPHC encoding second byte */
+#define LOWPAN_IPHC_CID                0x80
+
+#define LOWPAN_IPHC_SAC                0x40
+
+#define LOWPAN_IPHC_SAM_MASK   0x30
+#define LOWPAN_IPHC_SAM_00     0x00
+#define LOWPAN_IPHC_SAM_01     0x10
+#define LOWPAN_IPHC_SAM_10     0x20
+#define LOWPAN_IPHC_SAM_11     0x30
+
+#define LOWPAN_IPHC_M          0x08
+
+#define LOWPAN_IPHC_DAC                0x04
+
+#define LOWPAN_IPHC_DAM_MASK   0x03
+#define LOWPAN_IPHC_DAM_00     0x00
+#define LOWPAN_IPHC_DAM_01     0x01
+#define LOWPAN_IPHC_DAM_10     0x02
+#define LOWPAN_IPHC_DAM_11     0x03
+
+/* ipv6 address based on mac
+ * second bit-flip (Universe/Local) is done according RFC2464
+ */
+#define is_addr_mac_addr_based(a, m) \
+       ((((a)->s6_addr[8])  == (((m)[0]) ^ 0x02)) &&   \
+        (((a)->s6_addr[9])  == (m)[1]) &&              \
+        (((a)->s6_addr[10]) == (m)[2]) &&              \
+        (((a)->s6_addr[11]) == (m)[3]) &&              \
+        (((a)->s6_addr[12]) == (m)[4]) &&              \
+        (((a)->s6_addr[13]) == (m)[5]) &&              \
+        (((a)->s6_addr[14]) == (m)[6]) &&              \
+        (((a)->s6_addr[15]) == (m)[7]))
+
+/* check whether we can compress the IID to 16 bits,
+ * it's possible for unicast addresses with first 49 bits are zero only.
+ */
+#define lowpan_is_iid_16_bit_compressable(a)   \
+       ((((a)->s6_addr16[4]) == 0) &&          \
+        (((a)->s6_addr[10]) == 0) &&           \
+        (((a)->s6_addr[11]) == 0xff) &&        \
+        (((a)->s6_addr[12]) == 0xfe) &&        \
+        (((a)->s6_addr[13]) == 0))
+
+/* check whether the 112-bit gid of the multicast address is mappable to: */
+
+/* 48 bits, FFXX::00XX:XXXX:XXXX */
+#define lowpan_is_mcast_addr_compressable48(a) \
+       ((((a)->s6_addr16[1]) == 0) &&          \
+        (((a)->s6_addr16[2]) == 0) &&          \
+        (((a)->s6_addr16[3]) == 0) &&          \
+        (((a)->s6_addr16[4]) == 0) &&          \
+        (((a)->s6_addr[10]) == 0))
+
+/* 32 bits, FFXX::00XX:XXXX */
+#define lowpan_is_mcast_addr_compressable32(a) \
+       ((((a)->s6_addr16[1]) == 0) &&          \
+        (((a)->s6_addr16[2]) == 0) &&          \
+        (((a)->s6_addr16[3]) == 0) &&          \
+        (((a)->s6_addr16[4]) == 0) &&          \
+        (((a)->s6_addr16[5]) == 0) &&          \
+        (((a)->s6_addr[12]) == 0))
+
+/* 8 bits, FF02::00XX */
+#define lowpan_is_mcast_addr_compressable8(a)  \
+       ((((a)->s6_addr[1])  == 2) &&           \
+        (((a)->s6_addr16[1]) == 0) &&          \
+        (((a)->s6_addr16[2]) == 0) &&          \
+        (((a)->s6_addr16[3]) == 0) &&          \
+        (((a)->s6_addr16[4]) == 0) &&          \
+        (((a)->s6_addr16[5]) == 0) &&          \
+        (((a)->s6_addr16[6]) == 0) &&          \
+        (((a)->s6_addr[14]) == 0))
+
+static inline void iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr,
+                                               const void *lladdr)
+{
+       /* fe:80::XXXX:XXXX:XXXX:XXXX
+        *        \_________________/
+        *              hwaddr
+        */
+       ipaddr->s6_addr[0] = 0xFE;
+       ipaddr->s6_addr[1] = 0x80;
+       memcpy(&ipaddr->s6_addr[8], lladdr, EUI64_ADDR_LEN);
+       /* second bit-flip (Universe/Local)
+        * is done according RFC2464
+        */
+       ipaddr->s6_addr[8] ^= 0x02;
+}
+
+static inline void iphc_uncompress_802154_lladdr(struct in6_addr *ipaddr,
+                                                const void *lladdr)
+{
+       const struct ieee802154_addr *addr = lladdr;
+       u8 eui64[EUI64_ADDR_LEN] = { };
+
+       switch (addr->mode) {
+       case IEEE802154_ADDR_LONG:
+               ieee802154_le64_to_be64(eui64, &addr->extended_addr);
+               iphc_uncompress_eui64_lladdr(ipaddr, eui64);
+               break;
+       case IEEE802154_ADDR_SHORT:
+               /* fe:80::ff:fe00:XXXX
+                *                \__/
+                *             short_addr
+                *
+                * Universe/Local bit is zero.
+                */
+               ipaddr->s6_addr[0] = 0xFE;
+               ipaddr->s6_addr[1] = 0x80;
+               ipaddr->s6_addr[11] = 0xFF;
+               ipaddr->s6_addr[12] = 0xFE;
+               ieee802154_le16_to_be16(&ipaddr->s6_addr16[7],
+                                       &addr->short_addr);
+               break;
+       default:
+               /* should never handled and filtered by 802154 6lowpan */
+               WARN_ON_ONCE(1);
+               break;
+       }
+}
+
 /* Uncompress address function for source and
  * destination address(non-multicast).
  *
- * address_mode is sam value or dam value.
+ * address_mode is the masked value for sam or dam value
  */
-static int uncompress_addr(struct sk_buff *skb,
-                          struct in6_addr *ipaddr, const u8 address_mode,
-                          const u8 *lladdr, const u8 addr_type,
-                          const u8 addr_len)
+static int uncompress_addr(struct sk_buff *skb, const struct net_device *dev,
+                          struct in6_addr *ipaddr, u8 address_mode,
+                          const void *lladdr)
 {
        bool fail;
 
        switch (address_mode) {
-       case LOWPAN_IPHC_ADDR_00:
+       /* SAM and DAM are the same here */
+       case LOWPAN_IPHC_DAM_00:
                /* for global link addresses */
                fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
                break;
-       case LOWPAN_IPHC_ADDR_01:
+       case LOWPAN_IPHC_SAM_01:
+       case LOWPAN_IPHC_DAM_01:
                /* fe:80::XXXX:XXXX:XXXX:XXXX */
                ipaddr->s6_addr[0] = 0xFE;
                ipaddr->s6_addr[1] = 0x80;
                fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
                break;
-       case LOWPAN_IPHC_ADDR_02:
+       case LOWPAN_IPHC_SAM_10:
+       case LOWPAN_IPHC_DAM_10:
                /* fe:80::ff:fe00:XXXX */
                ipaddr->s6_addr[0] = 0xFE;
                ipaddr->s6_addr[1] = 0x80;
@@ -86,38 +228,16 @@ static int uncompress_addr(struct sk_buff *skb,
                ipaddr->s6_addr[12] = 0xFE;
                fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
                break;
-       case LOWPAN_IPHC_ADDR_03:
+       case LOWPAN_IPHC_SAM_11:
+       case LOWPAN_IPHC_DAM_11:
                fail = false;
-               switch (addr_type) {
-               case IEEE802154_ADDR_LONG:
-                       /* fe:80::XXXX:XXXX:XXXX:XXXX
-                        *        \_________________/
-                        *              hwaddr
-                        */
-                       ipaddr->s6_addr[0] = 0xFE;
-                       ipaddr->s6_addr[1] = 0x80;
-                       memcpy(&ipaddr->s6_addr[8], lladdr, addr_len);
-                       /* second bit-flip (Universe/Local)
-                        * is done according RFC2464
-                        */
-                       ipaddr->s6_addr[8] ^= 0x02;
-                       break;
-               case IEEE802154_ADDR_SHORT:
-                       /* fe:80::ff:fe00:XXXX
-                        *                \__/
-                        *             short_addr
-                        *
-                        * Universe/Local bit is zero.
-                        */
-                       ipaddr->s6_addr[0] = 0xFE;
-                       ipaddr->s6_addr[1] = 0x80;
-                       ipaddr->s6_addr[11] = 0xFF;
-                       ipaddr->s6_addr[12] = 0xFE;
-                       ipaddr->s6_addr16[7] = htons(*((u16 *)lladdr));
+               switch (lowpan_priv(dev)->lltype) {
+               case LOWPAN_LLTYPE_IEEE802154:
+                       iphc_uncompress_802154_lladdr(ipaddr, lladdr);
                        break;
                default:
-                       pr_debug("Invalid addr_type set\n");
-                       return -EINVAL;
+                       iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
+                       break;
                }
                break;
        default:
@@ -141,24 +261,25 @@ static int uncompress_addr(struct sk_buff *skb,
  */
 static int uncompress_context_based_src_addr(struct sk_buff *skb,
                                             struct in6_addr *ipaddr,
-                                            const u8 sam)
+                                            u8 address_mode)
 {
-       switch (sam) {
-       case LOWPAN_IPHC_ADDR_00:
+       switch (address_mode) {
+       case LOWPAN_IPHC_SAM_00:
                /* unspec address ::
                 * Do nothing, address is already ::
                 */
                break;
-       case LOWPAN_IPHC_ADDR_01:
+       case LOWPAN_IPHC_SAM_01:
                /* TODO */
-       case LOWPAN_IPHC_ADDR_02:
+       case LOWPAN_IPHC_SAM_10:
                /* TODO */
-       case LOWPAN_IPHC_ADDR_03:
+       case LOWPAN_IPHC_SAM_11:
                /* TODO */
-               netdev_warn(skb->dev, "SAM value 0x%x not supported\n", sam);
+               netdev_warn(skb->dev, "SAM value 0x%x not supported\n",
+                           address_mode);
                return -EINVAL;
        default:
-               pr_debug("Invalid sam value: 0x%x\n", sam);
+               pr_debug("Invalid sam value: 0x%x\n", address_mode);
                return -EINVAL;
        }
 
@@ -174,11 +295,11 @@ static int uncompress_context_based_src_addr(struct sk_buff *skb,
  */
 static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
                                             struct in6_addr *ipaddr,
-                                            const u8 dam)
+                                            u8 address_mode)
 {
        bool fail;
 
-       switch (dam) {
+       switch (address_mode) {
        case LOWPAN_IPHC_DAM_00:
                /* 00:  128 bits.  The full address
                 * is carried in-line.
@@ -210,7 +331,7 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
                fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[15], 1);
                break;
        default:
-               pr_debug("DAM value has a wrong value: 0x%x\n", dam);
+               pr_debug("DAM value has a wrong value: 0x%x\n", address_mode);
                return -EINVAL;
        }
 
@@ -225,77 +346,142 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
        return 0;
 }
 
-/* TTL uncompression values */
-static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
-
-int
-lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
-                        const u8 *saddr, const u8 saddr_type,
-                        const u8 saddr_len, const u8 *daddr,
-                        const u8 daddr_type, const u8 daddr_len,
-                        u8 iphc0, u8 iphc1)
+/* get the ecn values from iphc tf format and set it to ipv6hdr */
+static inline void lowpan_iphc_tf_set_ecn(struct ipv6hdr *hdr, const u8 *tf)
 {
-       struct ipv6hdr hdr = {};
-       u8 tmp, num_context = 0;
-       int err;
+       /* get the two higher bits which is ecn */
+       u8 ecn = tf[0] & 0xc0;
 
-       raw_dump_table(__func__, "raw skb data dump uncompressed",
-                      skb->data, skb->len);
+       /* ECN takes 0x30 in hdr->flow_lbl[0] */
+       hdr->flow_lbl[0] |= (ecn >> 2);
+}
 
-       /* another if the CID flag is set */
-       if (iphc1 & LOWPAN_IPHC_CID) {
-               pr_debug("CID flag is set, increase header with one\n");
-               if (lowpan_fetch_skb(skb, &num_context, sizeof(num_context)))
-                       return -EINVAL;
-       }
+/* get the dscp values from iphc tf format and set it to ipv6hdr */
+static inline void lowpan_iphc_tf_set_dscp(struct ipv6hdr *hdr, const u8 *tf)
+{
+       /* DSCP is at place after ECN */
+       u8 dscp = tf[0] & 0x3f;
 
-       hdr.version = 6;
+       /* The four highest bits need to be set at hdr->priority */
+       hdr->priority |= ((dscp & 0x3c) >> 2);
+       /* The two lower bits is part of hdr->flow_lbl[0] */
+       hdr->flow_lbl[0] |= ((dscp & 0x03) << 6);
+}
 
-       /* Traffic Class and Flow Label */
-       switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
-       /* Traffic Class and FLow Label carried in-line
-        * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
+/* get the flow label values from iphc tf format and set it to ipv6hdr */
+static inline void lowpan_iphc_tf_set_lbl(struct ipv6hdr *hdr, const u8 *lbl)
+{
+       /* flow label is always some array started with lower nibble of
+        * flow_lbl[0] and followed with two bytes afterwards. Inside inline
+        * data the flow_lbl position can be different, which will be handled
+        * by lbl pointer. E.g. case "01" vs "00" the traffic class is 8 bit
+        * shifted, the different lbl pointer will handle that.
+        *
+        * The flow label will started at lower nibble of flow_lbl[0], the
+        * higher nibbles are part of DSCP + ECN.
         */
-       case 0: /* 00b */
-               if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
+       hdr->flow_lbl[0] |= lbl[0] & 0x0f;
+       memcpy(&hdr->flow_lbl[1], &lbl[1], 2);
+}
+
+/* lowpan_iphc_tf_decompress - decompress the traffic class.
+ *     This function will return zero on success, a value lower than zero if
+ *     failed.
+ */
+static int lowpan_iphc_tf_decompress(struct sk_buff *skb, struct ipv6hdr *hdr,
+                                    u8 val)
+{
+       u8 tf[4];
+
+       /* Traffic Class and Flow Label */
+       switch (val) {
+       case LOWPAN_IPHC_TF_00:
+               /* ECN + DSCP + 4-bit Pad + Flow Label (4 bytes) */
+               if (lowpan_fetch_skb(skb, tf, 4))
                        return -EINVAL;
 
-               memcpy(&hdr.flow_lbl, &skb->data[0], 3);
-               skb_pull(skb, 3);
-               hdr.priority = ((tmp >> 2) & 0x0f);
-               hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) |
-                                       (hdr.flow_lbl[0] & 0x0f);
+               /*                      1                   2                   3
+                *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+                * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                * |ECN|   DSCP    |  rsv  |             Flow Label                |
+                * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                */
+               lowpan_iphc_tf_set_ecn(hdr, tf);
+               lowpan_iphc_tf_set_dscp(hdr, tf);
+               lowpan_iphc_tf_set_lbl(hdr, &tf[1]);
                break;
-       /* Traffic class carried in-line
-        * ECN + DSCP (1 byte), Flow Label is elided
-        */
-       case 2: /* 10b */
-               if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
+       case LOWPAN_IPHC_TF_01:
+               /* ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided. */
+               if (lowpan_fetch_skb(skb, tf, 3))
                        return -EINVAL;
 
-               hdr.priority = ((tmp >> 2) & 0x0f);
-               hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
+               /*                     1                   2
+                * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+                * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                * |ECN|rsv|             Flow Label                |
+                * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                */
+               lowpan_iphc_tf_set_ecn(hdr, tf);
+               lowpan_iphc_tf_set_lbl(hdr, &tf[0]);
                break;
-       /* Flow Label carried in-line
-        * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
-        */
-       case 1: /* 01b */
-               if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
+       case LOWPAN_IPHC_TF_10:
+               /* ECN + DSCP (1 byte), Flow Label is elided. */
+               if (lowpan_fetch_skb(skb, tf, 1))
                        return -EINVAL;
 
-               hdr.flow_lbl[0] = (tmp & 0x0F) | ((tmp >> 2) & 0x30);
-               memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
-               skb_pull(skb, 2);
+               /*  0 1 2 3 4 5 6 7
+                * +-+-+-+-+-+-+-+-+
+                * |ECN|   DSCP    |
+                * +-+-+-+-+-+-+-+-+
+                */
+               lowpan_iphc_tf_set_ecn(hdr, tf);
+               lowpan_iphc_tf_set_dscp(hdr, tf);
                break;
-       /* Traffic Class and Flow Label are elided */
-       case 3: /* 11b */
+       case LOWPAN_IPHC_TF_11:
+               /* Traffic Class and Flow Label are elided */
                break;
        default:
-               break;
+               WARN_ON_ONCE(1);
+               return -EINVAL;
        }
 
+       return 0;
+}
+
+/* TTL uncompression values */
+static const u8 lowpan_ttl_values[] = {
+       [LOWPAN_IPHC_HLIM_01] = 1,
+       [LOWPAN_IPHC_HLIM_10] = 64,
+       [LOWPAN_IPHC_HLIM_11] = 255,
+};
+
+int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
+                            const void *daddr, const void *saddr)
+{
+       struct ipv6hdr hdr = {};
+       u8 iphc0, iphc1;
+       int err;
+
+       raw_dump_table(__func__, "raw skb data dump uncompressed",
+                      skb->data, skb->len);
+
+       if (lowpan_fetch_skb(skb, &iphc0, sizeof(iphc0)) ||
+           lowpan_fetch_skb(skb, &iphc1, sizeof(iphc1)))
+               return -EINVAL;
+
+       /* another if the CID flag is set */
+       if (iphc1 & LOWPAN_IPHC_CID)
+               return -ENOTSUPP;
+
+       hdr.version = 6;
+
+       err = lowpan_iphc_tf_decompress(skb, &hdr,
+                                       iphc0 & LOWPAN_IPHC_TF_MASK);
+       if (err < 0)
+               return err;
+
        /* Next Header */
-       if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+       if (!(iphc0 & LOWPAN_IPHC_NH)) {
                /* Next header is carried inline */
                if (lowpan_fetch_skb(skb, &hdr.nexthdr, sizeof(hdr.nexthdr)))
                        return -EINVAL;
@@ -305,35 +491,30 @@ lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
        }
 
        /* Hop Limit */
-       if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I) {
-               hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
+       if ((iphc0 & LOWPAN_IPHC_HLIM_MASK) != LOWPAN_IPHC_HLIM_00) {
+               hdr.hop_limit = lowpan_ttl_values[iphc0 & LOWPAN_IPHC_HLIM_MASK];
        } else {
                if (lowpan_fetch_skb(skb, &hdr.hop_limit,
                                     sizeof(hdr.hop_limit)))
                        return -EINVAL;
        }
 
-       /* Extract SAM to the tmp variable */
-       tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03;
-
        if (iphc1 & LOWPAN_IPHC_SAC) {
                /* Source address context based uncompression */
                pr_debug("SAC bit is set. Handle context based source address.\n");
-               err = uncompress_context_based_src_addr(skb, &hdr.saddr, tmp);
+               err = uncompress_context_based_src_addr(skb, &hdr.saddr,
+                                                       iphc1 & LOWPAN_IPHC_SAM_MASK);
        } else {
                /* Source address uncompression */
                pr_debug("source address stateless compression\n");
-               err = uncompress_addr(skb, &hdr.saddr, tmp, saddr,
-                                     saddr_type, saddr_len);
+               err = uncompress_addr(skb, dev, &hdr.saddr,
+                                     iphc1 & LOWPAN_IPHC_SAM_MASK, saddr);
        }
 
        /* Check on error of previous branch */
        if (err)
                return -EINVAL;
 
-       /* Extract DAM to the tmp variable */
-       tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
-
        /* check for Multicast Compression */
        if (iphc1 & LOWPAN_IPHC_M) {
                if (iphc1 & LOWPAN_IPHC_DAC) {
@@ -341,22 +522,22 @@ lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
                        /* TODO: implement this */
                } else {
                        err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
-                                                               tmp);
+                                                               iphc1 & LOWPAN_IPHC_DAM_MASK);
 
                        if (err)
                                return -EINVAL;
                }
        } else {
-               err = uncompress_addr(skb, &hdr.daddr, tmp, daddr,
-                                     daddr_type, daddr_len);
+               err = uncompress_addr(skb, dev, &hdr.daddr,
+                                     iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
                pr_debug("dest: stateless compression mode %d dest %pI6c\n",
-                        tmp, &hdr.daddr);
+                        iphc1 & LOWPAN_IPHC_DAM_MASK, &hdr.daddr);
                if (err)
                        return -EINVAL;
        }
 
        /* Next header data uncompression */
-       if (iphc0 & LOWPAN_IPHC_NH_C) {
+       if (iphc0 & LOWPAN_IPHC_NH) {
                err = lowpan_nhc_do_uncompression(skb, dev, &hdr);
                if (err < 0)
                        return err;
@@ -366,7 +547,18 @@ lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
                        return err;
        }
 
-       hdr.payload_len = htons(skb->len);
+       switch (lowpan_priv(dev)->lltype) {
+       case LOWPAN_LLTYPE_IEEE802154:
+               if (lowpan_802154_cb(skb)->d_size)
+                       hdr.payload_len = htons(lowpan_802154_cb(skb)->d_size -
+                                               sizeof(struct ipv6hdr));
+               else
+                       hdr.payload_len = htons(skb->len);
+               break;
+       default:
+               hdr.payload_len = htons(skb->len);
+               break;
+       }
 
        pr_debug("skb headroom size = %d, data length = %d\n",
                 skb_headroom(skb), skb->len);
@@ -386,42 +578,176 @@ lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
 }
 EXPORT_SYMBOL_GPL(lowpan_header_decompress);
 
-static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift,
-                                 const struct in6_addr *ipaddr,
-                                 const unsigned char *lladdr)
+static const u8 lowpan_iphc_dam_to_sam_value[] = {
+       [LOWPAN_IPHC_DAM_00] = LOWPAN_IPHC_SAM_00,
+       [LOWPAN_IPHC_DAM_01] = LOWPAN_IPHC_SAM_01,
+       [LOWPAN_IPHC_DAM_10] = LOWPAN_IPHC_SAM_10,
+       [LOWPAN_IPHC_DAM_11] = LOWPAN_IPHC_SAM_11,
+};
+
+static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr *ipaddr,
+                                 const unsigned char *lladdr, bool sam)
 {
-       u8 val = 0;
+       u8 dam = LOWPAN_IPHC_DAM_00;
 
        if (is_addr_mac_addr_based(ipaddr, lladdr)) {
-               val = 3; /* 0-bits */
+               dam = LOWPAN_IPHC_DAM_11; /* 0-bits */
                pr_debug("address compression 0 bits\n");
        } else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
                /* compress IID to 16 bits xxxx::XXXX */
                lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[7], 2);
-               val = 2; /* 16-bits */
+               dam = LOWPAN_IPHC_DAM_10; /* 16-bits */
                raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)",
                                *hc_ptr - 2, 2);
        } else {
                /* do not compress IID => xxxx::IID */
                lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[4], 8);
-               val = 1; /* 64-bits */
+               dam = LOWPAN_IPHC_DAM_01; /* 64-bits */
                raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
                                *hc_ptr - 8, 8);
        }
 
-       return rol8(val, shift);
+       if (sam)
+               return lowpan_iphc_dam_to_sam_value[dam];
+       else
+               return dam;
+}
+
+/* lowpan_iphc_get_tc - get the ECN + DCSP fields in hc format */
+static inline u8 lowpan_iphc_get_tc(const struct ipv6hdr *hdr)
+{
+       u8 dscp, ecn;
+
+       /* hdr->priority contains the higher bits of dscp, lower are part of
+        * flow_lbl[0]. Note ECN, DCSP is swapped in ipv6 hdr.
+        */
+       dscp = (hdr->priority << 2) | ((hdr->flow_lbl[0] & 0xc0) >> 6);
+       /* ECN is at the two lower bits from first nibble of flow_lbl[0] */
+       ecn = (hdr->flow_lbl[0] & 0x30);
+       /* for pretty debug output, also shift ecn to get the ecn value */
+       pr_debug("ecn 0x%02x dscp 0x%02x\n", ecn >> 4, dscp);
+       /* ECN is at 0x30 now, shift it to have ECN + DCSP */
+       return (ecn << 2) | dscp;
 }
 
-int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
-                          unsigned short type, const void *_daddr,
-                          const void *_saddr, unsigned int len)
+/* lowpan_iphc_is_flow_lbl_zero - check if flow label is zero */
+static inline bool lowpan_iphc_is_flow_lbl_zero(const struct ipv6hdr *hdr)
 {
-       u8 tmp, iphc0, iphc1, *hc_ptr;
+       return ((!(hdr->flow_lbl[0] & 0x0f)) &&
+               !hdr->flow_lbl[1] && !hdr->flow_lbl[2]);
+}
+
+/* lowpan_iphc_tf_compress - compress the traffic class which is set by
+ *     ipv6hdr. Return the corresponding format identifier which is used.
+ */
+static u8 lowpan_iphc_tf_compress(u8 **hc_ptr, const struct ipv6hdr *hdr)
+{
+       /* get ecn dscp data in a byteformat as: ECN(hi) + DSCP(lo) */
+       u8 tc = lowpan_iphc_get_tc(hdr), tf[4], val;
+
+       /* printout the traffic class in hc format */
+       pr_debug("tc 0x%02x\n", tc);
+
+       if (lowpan_iphc_is_flow_lbl_zero(hdr)) {
+               if (!tc) {
+                       /* 11:  Traffic Class and Flow Label are elided. */
+                       val = LOWPAN_IPHC_TF_11;
+               } else {
+                       /* 10:  ECN + DSCP (1 byte), Flow Label is elided.
+                        *
+                        *  0 1 2 3 4 5 6 7
+                        * +-+-+-+-+-+-+-+-+
+                        * |ECN|   DSCP    |
+                        * +-+-+-+-+-+-+-+-+
+                        */
+                       lowpan_push_hc_data(hc_ptr, &tc, sizeof(tc));
+                       val = LOWPAN_IPHC_TF_10;
+               }
+       } else {
+               /* check if dscp is zero, it's after the first two bit */
+               if (!(tc & 0x3f)) {
+                       /* 01:  ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
+                        *
+                        *                     1                   2
+                        * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        * |ECN|rsv|             Flow Label                |
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        */
+                       memcpy(&tf[0], &hdr->flow_lbl[0], 3);
+                       /* zero the highest 4-bits, contains DCSP + ECN */
+                       tf[0] &= ~0xf0;
+                       /* set ECN */
+                       tf[0] |= (tc & 0xc0);
+
+                       lowpan_push_hc_data(hc_ptr, tf, 3);
+                       val = LOWPAN_IPHC_TF_01;
+               } else {
+                       /* 00:  ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
+                        *
+                        *                      1                   2                   3
+                        *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        * |ECN|   DSCP    |  rsv  |             Flow Label                |
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        */
+                       memcpy(&tf[0], &tc, sizeof(tc));
+                       /* highest nibble of flow_lbl[0] is part of DSCP + ECN
+                        * which will be the 4-bit pad and will be filled with
+                        * zeros afterwards.
+                        */
+                       memcpy(&tf[1], &hdr->flow_lbl[0], 3);
+                       /* zero the 4-bit pad, which is reserved */
+                       tf[1] &= ~0xf0;
+
+                       lowpan_push_hc_data(hc_ptr, tf, 4);
+                       val = LOWPAN_IPHC_TF_00;
+               }
+       }
+
+       return val;
+}
+
+static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
+                                         const struct in6_addr *ipaddr)
+{
+       u8 val;
+
+       if (lowpan_is_mcast_addr_compressable8(ipaddr)) {
+               pr_debug("compressed to 1 octet\n");
+               /* use last byte */
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[15], 1);
+               val = LOWPAN_IPHC_DAM_11;
+       } else if (lowpan_is_mcast_addr_compressable32(ipaddr)) {
+               pr_debug("compressed to 4 octets\n");
+               /* second byte + the last three */
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[1], 1);
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[13], 3);
+               val = LOWPAN_IPHC_DAM_10;
+       } else if (lowpan_is_mcast_addr_compressable48(ipaddr)) {
+               pr_debug("compressed to 6 octets\n");
+               /* second byte + the last five */
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[1], 1);
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[11], 5);
+               val = LOWPAN_IPHC_DAM_01;
+       } else {
+               pr_debug("using full address\n");
+               lowpan_push_hc_data(hc_ptr, ipaddr->s6_addr, 16);
+               val = LOWPAN_IPHC_DAM_00;
+       }
+
+       return val;
+}
+
+int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
+                          const void *daddr, const void *saddr)
+{
+       u8 iphc0, iphc1, *hc_ptr;
        struct ipv6hdr *hdr;
-       u8 head[100] = {};
+       u8 head[LOWPAN_IPHC_MAX_HC_BUF_LEN] = {};
        int ret, addr_type;
 
-       if (type != ETH_P_IPV6)
+       if (skb->protocol != htons(ETH_P_IPV6))
                return -EINVAL;
 
        hdr = ipv6_hdr(skb);
@@ -445,63 +771,26 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
 
        /* TODO: context lookup */
 
-       raw_dump_inline(__func__, "saddr",
-                       (unsigned char *)_saddr, IEEE802154_ADDR_LEN);
-       raw_dump_inline(__func__, "daddr",
-                       (unsigned char *)_daddr, IEEE802154_ADDR_LEN);
+       raw_dump_inline(__func__, "saddr", saddr, EUI64_ADDR_LEN);
+       raw_dump_inline(__func__, "daddr", daddr, EUI64_ADDR_LEN);
 
        raw_dump_table(__func__, "sending raw skb network uncompressed packet",
                       skb->data, skb->len);
 
-       /* Traffic class, flow label
-        * If flow label is 0, compress it. If traffic class is 0, compress it
-        * We have to process both in the same time as the offset of traffic
-        * class depends on the presence of version and flow label
-        */
-
-       /* hc format of TC is ECN | DSCP , original one is DSCP | ECN */
-       tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
-       tmp = ((tmp & 0x03) << 6) | (tmp >> 2);
-
-       if (((hdr->flow_lbl[0] & 0x0F) == 0) &&
-           (hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
-               /* flow label can be compressed */
-               iphc0 |= LOWPAN_IPHC_FL_C;
-               if ((hdr->priority == 0) &&
-                   ((hdr->flow_lbl[0] & 0xF0) == 0)) {
-                       /* compress (elide) all */
-                       iphc0 |= LOWPAN_IPHC_TC_C;
-               } else {
-                       /* compress only the flow label */
-                       *hc_ptr = tmp;
-                       hc_ptr += 1;
-               }
-       } else {
-               /* Flow label cannot be compressed */
-               if ((hdr->priority == 0) &&
-                   ((hdr->flow_lbl[0] & 0xF0) == 0)) {
-                       /* compress only traffic class */
-                       iphc0 |= LOWPAN_IPHC_TC_C;
-                       *hc_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
-                       memcpy(hc_ptr + 1, &hdr->flow_lbl[1], 2);
-                       hc_ptr += 3;
-               } else {
-                       /* compress nothing */
-                       memcpy(hc_ptr, hdr, 4);
-                       /* replace the top byte with new ECN | DSCP format */
-                       *hc_ptr = tmp;
-                       hc_ptr += 4;
-               }
-       }
+       /* Traffic Class, Flow Label compression */
+       iphc0 |= lowpan_iphc_tf_compress(&hc_ptr, hdr);
 
        /* NOTE: payload length is always compressed */
 
        /* Check if we provide the nhc format for nexthdr and compression
         * functionality. If not nexthdr is handled inline and not compressed.
         */
-       ret = lowpan_nhc_check_compression(skb, hdr, &hc_ptr, &iphc0);
-       if (ret < 0)
-               return ret;
+       ret = lowpan_nhc_check_compression(skb, hdr, &hc_ptr);
+       if (ret == -ENOENT)
+               lowpan_push_hc_data(&hc_ptr, &hdr->nexthdr,
+                                   sizeof(hdr->nexthdr));
+       else
+               iphc0 |= LOWPAN_IPHC_NH;
 
        /* Hop limit
         * if 1:   compress, encoding is 01
@@ -511,13 +800,13 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
         */
        switch (hdr->hop_limit) {
        case 1:
-               iphc0 |= LOWPAN_IPHC_TTL_1;
+               iphc0 |= LOWPAN_IPHC_HLIM_01;
                break;
        case 64:
-               iphc0 |= LOWPAN_IPHC_TTL_64;
+               iphc0 |= LOWPAN_IPHC_HLIM_10;
                break;
        case 255:
-               iphc0 |= LOWPAN_IPHC_TTL_255;
+               iphc0 |= LOWPAN_IPHC_HLIM_11;
                break;
        default:
                lowpan_push_hc_data(&hc_ptr, &hdr->hop_limit,
@@ -531,9 +820,8 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
                iphc1 |= LOWPAN_IPHC_SAC;
        } else {
                if (addr_type & IPV6_ADDR_LINKLOCAL) {
-                       iphc1 |= lowpan_compress_addr_64(&hc_ptr,
-                                                        LOWPAN_IPHC_SAM_BIT,
-                                                        &hdr->saddr, _saddr);
+                       iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->saddr,
+                                                        saddr, true);
                        pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
                                 &hdr->saddr, iphc1);
                } else {
@@ -547,38 +835,12 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
        if (addr_type & IPV6_ADDR_MULTICAST) {
                pr_debug("destination address is multicast: ");
                iphc1 |= LOWPAN_IPHC_M;
-               if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) {
-                       pr_debug("compressed to 1 octet\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_11;
-                       /* use last byte */
-                       lowpan_push_hc_data(&hc_ptr,
-                                           &hdr->daddr.s6_addr[15], 1);
-               } else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) {
-                       pr_debug("compressed to 4 octets\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_10;
-                       /* second byte + the last three */
-                       lowpan_push_hc_data(&hc_ptr,
-                                           &hdr->daddr.s6_addr[1], 1);
-                       lowpan_push_hc_data(&hc_ptr,
-                                           &hdr->daddr.s6_addr[13], 3);
-               } else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) {
-                       pr_debug("compressed to 6 octets\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_01;
-                       /* second byte + the last five */
-                       lowpan_push_hc_data(&hc_ptr,
-                                           &hdr->daddr.s6_addr[1], 1);
-                       lowpan_push_hc_data(&hc_ptr,
-                                           &hdr->daddr.s6_addr[11], 5);
-               } else {
-                       pr_debug("using full address\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_00;
-                       lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16);
-               }
+               iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr, &hdr->daddr);
        } else {
                if (addr_type & IPV6_ADDR_LINKLOCAL) {
                        /* TODO: context lookup */
-                       iphc1 |= lowpan_compress_addr_64(&hc_ptr,
-                               LOWPAN_IPHC_DAM_BIT, &hdr->daddr, _daddr);
+                       iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->daddr,
+                                                        daddr, false);
                        pr_debug("dest address unicast link-local %pI6c "
                                 "iphc1 0x%02x\n", &hdr->daddr, iphc1);
                } else {
@@ -588,7 +850,7 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
        }
 
        /* next header compression */
-       if (iphc0 & LOWPAN_IPHC_NH_C) {
+       if (iphc0 & LOWPAN_IPHC_NH) {
                ret = lowpan_nhc_do_compression(skb, hdr, &hc_ptr);
                if (ret < 0)
                        return ret;
index fd20fc51a7c49ffb9c2f0782973df08e1f8535c6..7008d53e455c5254db5879bb044b0a4b6d706775 100644 (file)
@@ -95,23 +95,20 @@ static struct lowpan_nhc *lowpan_nhc_by_nhcid(const struct sk_buff *skb)
 }
 
 int lowpan_nhc_check_compression(struct sk_buff *skb,
-                                const struct ipv6hdr *hdr, u8 **hc_ptr,
-                                u8 *iphc0)
+                                const struct ipv6hdr *hdr, u8 **hc_ptr)
 {
        struct lowpan_nhc *nhc;
+       int ret = 0;
 
        spin_lock_bh(&lowpan_nhc_lock);
 
        nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
-       if (nhc && nhc->compress)
-               *iphc0 |= LOWPAN_IPHC_NH_C;
-       else
-               lowpan_push_hc_data(hc_ptr, &hdr->nexthdr,
-                                   sizeof(hdr->nexthdr));
+       if (!(nhc && nhc->compress))
+               ret = -ENOENT;
 
        spin_unlock_bh(&lowpan_nhc_lock);
 
-       return 0;
+       return ret;
 }
 
 int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
@@ -157,7 +154,8 @@ out:
        return ret;
 }
 
-int lowpan_nhc_do_uncompression(struct sk_buff *skb, struct net_device *dev,
+int lowpan_nhc_do_uncompression(struct sk_buff *skb,
+                               const struct net_device *dev,
                                struct ipv6hdr *hdr)
 {
        struct lowpan_nhc *nhc;
index ed44938eb5dec6806ae18ad9cf316253eb37b2c3..8030414001361bbdd7e3bac62d468f1032f61fda 100644 (file)
@@ -8,8 +8,6 @@
 #include <net/6lowpan.h>
 #include <net/ipv6.h>
 
-#define LOWPAN_NHC_MAX_ID_LEN  1
-
 /**
  * LOWPAN_NHC - helper macro to generate nh id fields and lowpan_nhc struct
  *
@@ -88,19 +86,16 @@ struct lowpan_nhc *lowpan_nhc_by_nexthdr(u8 nexthdr);
 
 /**
  * lowpan_nhc_check_compression - checks if we support compression format. If
- *     we support the nhc by nexthdr field, the 6LoWPAN iphc NHC bit will be
- *     set. If we don't support nexthdr will be added as inline data to the
- *     6LoWPAN header.
+ *     we support the nhc by nexthdr field, the function will return 0. If we
+ *     don't support the nhc by nexthdr this function will return -ENOENT.
  *
  * @skb: skb of 6LoWPAN header to read nhc and replace header.
  * @hdr: ipv6hdr to check the nexthdr value
  * @hc_ptr: pointer for 6LoWPAN header which should increment at the end of
  *         replaced header.
- * @iphc0: iphc0 pointer to set the 6LoWPAN NHC bit
  */
 int lowpan_nhc_check_compression(struct sk_buff *skb,
-                                const struct ipv6hdr *hdr, u8 **hc_ptr,
-                                u8 *iphc0);
+                                const struct ipv6hdr *hdr, u8 **hc_ptr);
 
 /**
  * lowpan_nhc_do_compression - calling compress callback for nhc
@@ -121,7 +116,8 @@ int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
  * @dev: netdevice for print logging information.
  * @hdr: ipv6hdr for setting nexthdr value.
  */
-int lowpan_nhc_do_uncompression(struct sk_buff *skb, struct net_device *dev,
+int lowpan_nhc_do_uncompression(struct sk_buff *skb,
+                               const struct net_device *dev,
                                struct ipv6hdr *hdr);
 
 /**
index c6bcaeb428ae7737d1f61d2633f6d6d821bde788..69537a2eaab1317b33a535066a81c7c118aa3d96 100644 (file)
 
 #include "nhc.h"
 
-#define LOWPAN_NHC_UDP_IDLEN   1
+#define LOWPAN_NHC_UDP_MASK            0xF8
+#define LOWPAN_NHC_UDP_ID              0xF0
+#define LOWPAN_NHC_UDP_IDLEN           1
+
+#define LOWPAN_NHC_UDP_4BIT_PORT       0xF0B0
+#define LOWPAN_NHC_UDP_4BIT_MASK       0xFFF0
+#define LOWPAN_NHC_UDP_8BIT_PORT       0xF000
+#define LOWPAN_NHC_UDP_8BIT_MASK       0xFF00
+
+/* values for port compression, _with checksum_ ie bit 5 set to 0 */
+
+/* all inline */
+#define LOWPAN_NHC_UDP_CS_P_00 0xF0
+/* source 16bit inline, dest = 0xF0 + 8 bit inline */
+#define LOWPAN_NHC_UDP_CS_P_01 0xF1
+/* source = 0xF0 + 8bit inline, dest = 16 bit inline */
+#define LOWPAN_NHC_UDP_CS_P_10 0xF2
+/* source & dest = 0xF0B + 4bit inline */
+#define LOWPAN_NHC_UDP_CS_P_11 0xF3
+/* checksum elided */
+#define LOWPAN_NHC_UDP_CS_C    0x04
 
 static int udp_uncompress(struct sk_buff *skb, size_t needed)
 {
@@ -71,7 +91,18 @@ static int udp_uncompress(struct sk_buff *skb, size_t needed)
         * here, we obtain the hint from the remaining size of the
         * frame
         */
-       uh.len = htons(skb->len + sizeof(struct udphdr));
+       switch (lowpan_priv(skb->dev)->lltype) {
+       case LOWPAN_LLTYPE_IEEE802154:
+               if (lowpan_802154_cb(skb)->d_size)
+                       uh.len = htons(lowpan_802154_cb(skb)->d_size -
+                                      sizeof(struct ipv6hdr));
+               else
+                       uh.len = htons(skb->len + sizeof(struct udphdr));
+               break;
+       default:
+               uh.len = htons(skb->len + sizeof(struct udphdr));
+               break;
+       }
        pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
 
        /* replace the compressed UDP head by the uncompressed UDP
index 61bf2a06e85d62ce9d319b02f1bf1ebb569e3964..496b27588493ded6edb0bf408440ecaa56dcd59c 100644 (file)
@@ -206,7 +206,10 @@ static int __vlan_vid_add(struct vlan_info *vlan_info, __be16 proto, u16 vid,
                return -ENOMEM;
 
        if (vlan_hw_filter_capable(dev, vid_info)) {
-               err =  ops->ndo_vlan_rx_add_vid(dev, proto, vid);
+               if (netif_device_present(dev))
+                       err = ops->ndo_vlan_rx_add_vid(dev, proto, vid);
+               else
+                       err = -ENODEV;
                if (err) {
                        kfree(vid_info);
                        return err;
@@ -264,7 +267,10 @@ static void __vlan_vid_del(struct vlan_info *vlan_info,
        int err;
 
        if (vlan_hw_filter_capable(dev, vid_info)) {
-               err = ops->ndo_vlan_rx_kill_vid(dev, proto, vid);
+               if (netif_device_present(dev))
+                       err = ops->ndo_vlan_rx_kill_vid(dev, proto, vid);
+               else
+                       err = -ENODEV;
                if (err) {
                        pr_warn("failed to kill vid %04x/%d for device %s\n",
                                proto, vid, dev->name);
index 7021c1bf44d6ce949091cb287232e6cfefed6256..127da94ae25eb73e8ffd45e7e7dbc9e07d937033 100644 (file)
@@ -232,6 +232,7 @@ source "net/netlink/Kconfig"
 source "net/mpls/Kconfig"
 source "net/hsr/Kconfig"
 source "net/switchdev/Kconfig"
+source "net/l3mdev/Kconfig"
 
 config RPS
        bool
index 3995613e5510cfd6a05ebfe5cbbd9da3bcb5589f..a5d04098dfce8693c46785f6c0dfa90bd23f4187 100644 (file)
@@ -74,3 +74,6 @@ obj-$(CONFIG_HSR)             += hsr/
 ifneq ($(CONFIG_NET_SWITCHDEV),)
 obj-y                          += switchdev/
 endif
+ifneq ($(CONFIG_NET_L3_MASTER_DEV),)
+obj-y                          += l3mdev/
+endif
index 131e79cde3504f161e38f7bd5797f2de06eb63d1..9e9cca3689a08ab7c9038b9bab4c61d7214004af 100644 (file)
@@ -21,8 +21,6 @@
 #include <net/ip6_route.h>
 #include <net/addrconf.h>
 
-#include <net/af_ieee802154.h> /* to get the address type */
-
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
@@ -35,7 +33,6 @@ static struct dentry *lowpan_enable_debugfs;
 static struct dentry *lowpan_control_debugfs;
 
 #define IFACE_NAME_TEMPLATE "bt%d"
-#define EUI64_ADDR_LEN 8
 
 struct skb_cb {
        struct in6_addr addr;
@@ -266,14 +263,13 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
        if (!skb_cp)
                return NET_RX_DROP;
 
-       return netif_rx(skb_cp);
+       return netif_rx_ni(skb_cp);
 }
 
 static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev,
                           struct l2cap_chan *chan)
 {
        const u8 *saddr, *daddr;
-       u8 iphc0, iphc1;
        struct lowpan_dev *dev;
        struct lowpan_peer *peer;
 
@@ -288,22 +284,7 @@ static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev,
        saddr = peer->eui64_addr;
        daddr = dev->netdev->dev_addr;
 
-       /* at least two bytes will be used for the encoding */
-       if (skb->len < 2)
-               return -EINVAL;
-
-       if (lowpan_fetch_skb_u8(skb, &iphc0))
-               return -EINVAL;
-
-       if (lowpan_fetch_skb_u8(skb, &iphc1))
-               return -EINVAL;
-
-       return lowpan_header_decompress(skb, netdev,
-                                       saddr, IEEE802154_ADDR_LONG,
-                                       EUI64_ADDR_LEN, daddr,
-                                       IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
-                                       iphc0, iphc1);
-
+       return lowpan_header_decompress(skb, netdev, daddr, saddr);
 }
 
 static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
@@ -315,15 +296,17 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
        if (!netif_running(dev))
                goto drop;
 
-       if (dev->type != ARPHRD_6LOWPAN)
+       if (dev->type != ARPHRD_6LOWPAN || !skb->len)
                goto drop;
 
+       skb_reset_network_header(skb);
+
        skb = skb_share_check(skb, GFP_ATOMIC);
        if (!skb)
                goto drop;
 
        /* check that it's our buffer */
-       if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
+       if (lowpan_is_ipv6(*skb_network_header(skb))) {
                /* Copy the packet so that the IPv6 header is
                 * properly aligned.
                 */
@@ -335,7 +318,6 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
                local_skb->protocol = htons(ETH_P_IPV6);
                local_skb->pkt_type = PACKET_HOST;
 
-               skb_reset_network_header(local_skb);
                skb_set_transport_header(local_skb, sizeof(struct ipv6hdr));
 
                if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) {
@@ -348,38 +330,34 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
 
                consume_skb(local_skb);
                consume_skb(skb);
-       } else {
-               switch (skb->data[0] & 0xe0) {
-               case LOWPAN_DISPATCH_IPHC:      /* ipv6 datagram */
-                       local_skb = skb_clone(skb, GFP_ATOMIC);
-                       if (!local_skb)
-                               goto drop;
+       } else if (lowpan_is_iphc(*skb_network_header(skb))) {
+               local_skb = skb_clone(skb, GFP_ATOMIC);
+               if (!local_skb)
+                       goto drop;
 
-                       ret = iphc_decompress(local_skb, dev, chan);
-                       if (ret < 0) {
-                               kfree_skb(local_skb);
-                               goto drop;
-                       }
+               ret = iphc_decompress(local_skb, dev, chan);
+               if (ret < 0) {
+                       kfree_skb(local_skb);
+                       goto drop;
+               }
 
-                       local_skb->protocol = htons(ETH_P_IPV6);
-                       local_skb->pkt_type = PACKET_HOST;
-                       local_skb->dev = dev;
+               local_skb->protocol = htons(ETH_P_IPV6);
+               local_skb->pkt_type = PACKET_HOST;
+               local_skb->dev = dev;
 
-                       if (give_skb_to_upper(local_skb, dev)
-                                       != NET_RX_SUCCESS) {
-                               kfree_skb(local_skb);
-                               goto drop;
-                       }
+               if (give_skb_to_upper(local_skb, dev)
+                               != NET_RX_SUCCESS) {
+                       kfree_skb(local_skb);
+                       goto drop;
+               }
 
-                       dev->stats.rx_bytes += skb->len;
-                       dev->stats.rx_packets++;
+               dev->stats.rx_bytes += skb->len;
+               dev->stats.rx_packets++;
 
-                       consume_skb(local_skb);
-                       consume_skb(skb);
-                       break;
-               default:
-                       break;
-               }
+               consume_skb(local_skb);
+               consume_skb(skb);
+       } else {
+               goto drop;
        }
 
        return NET_RX_SUCCESS;
@@ -493,8 +471,7 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
                status = 1;
        }
 
-       lowpan_header_compress(skb, netdev, ETH_P_IPV6, daddr,
-                              dev->netdev->dev_addr, skb->len);
+       lowpan_header_compress(skb, netdev, daddr, dev->netdev->dev_addr);
 
        err = dev_hard_header(skb, netdev, ETH_P_IPV6, NULL, NULL, 0);
        if (err < 0)
@@ -674,13 +651,8 @@ static struct header_ops header_ops = {
 
 static void netdev_setup(struct net_device *dev)
 {
-       dev->addr_len           = EUI64_ADDR_LEN;
-       dev->type               = ARPHRD_6LOWPAN;
-
        dev->hard_header_len    = 0;
        dev->needed_tailroom    = 0;
-       dev->mtu                = IPV6_MIN_MTU;
-       dev->tx_queue_len       = 0;
        dev->flags              = IFF_RUNNING | IFF_POINTOPOINT |
                                  IFF_MULTICAST;
        dev->watchdog_timeo     = 0;
@@ -775,24 +747,7 @@ static struct l2cap_chan *chan_create(void)
 
        chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
        chan->mode = L2CAP_MODE_LE_FLOWCTL;
-       chan->omtu = 65535;
-       chan->imtu = chan->omtu;
-
-       return chan;
-}
-
-static struct l2cap_chan *chan_open(struct l2cap_chan *pchan)
-{
-       struct l2cap_chan *chan;
-
-       chan = chan_create();
-       if (!chan)
-               return NULL;
-
-       chan->remote_mps = chan->omtu;
-       chan->mps = chan->omtu;
-
-       chan->state = BT_CONNECTED;
+       chan->imtu = 1280;
 
        return chan;
 }
@@ -919,7 +874,10 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
 {
        struct l2cap_chan *chan;
 
-       chan = chan_open(pchan);
+       chan = chan_create();
+       if (!chan)
+               return NULL;
+
        chan->ops = pchan->ops;
 
        BT_DBG("chan %p pchan %p", chan, pchan);
@@ -1065,34 +1023,23 @@ static inline __u8 bdaddr_type(__u8 type)
                return BDADDR_LE_RANDOM;
 }
 
-static struct l2cap_chan *chan_get(void)
-{
-       struct l2cap_chan *pchan;
-
-       pchan = chan_create();
-       if (!pchan)
-               return NULL;
-
-       pchan->ops = &bt_6lowpan_chan_ops;
-
-       return pchan;
-}
-
 static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
 {
-       struct l2cap_chan *pchan;
+       struct l2cap_chan *chan;
        int err;
 
-       pchan = chan_get();
-       if (!pchan)
+       chan = chan_create();
+       if (!chan)
                return -EINVAL;
 
-       err = l2cap_chan_connect(pchan, cpu_to_le16(L2CAP_PSM_IPSP), 0,
+       chan->ops = &bt_6lowpan_chan_ops;
+
+       err = l2cap_chan_connect(chan, cpu_to_le16(L2CAP_PSM_IPSP), 0,
                                 addr, dst_type);
 
-       BT_DBG("chan %p err %d", pchan, err);
+       BT_DBG("chan %p err %d", chan, err);
        if (err < 0)
-               l2cap_chan_put(pchan);
+               l2cap_chan_put(chan);
 
        return err;
 }
@@ -1117,31 +1064,32 @@ static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
 static struct l2cap_chan *bt_6lowpan_listen(void)
 {
        bdaddr_t *addr = BDADDR_ANY;
-       struct l2cap_chan *pchan;
+       struct l2cap_chan *chan;
        int err;
 
        if (!enable_6lowpan)
                return NULL;
 
-       pchan = chan_get();
-       if (!pchan)
+       chan = chan_create();
+       if (!chan)
                return NULL;
 
-       pchan->state = BT_LISTEN;
-       pchan->src_type = BDADDR_LE_PUBLIC;
+       chan->ops = &bt_6lowpan_chan_ops;
+       chan->state = BT_LISTEN;
+       chan->src_type = BDADDR_LE_PUBLIC;
 
-       atomic_set(&pchan->nesting, L2CAP_NESTING_PARENT);
+       atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
 
-       BT_DBG("chan %p src type %d", pchan, pchan->src_type);
+       BT_DBG("chan %p src type %d", chan, chan->src_type);
 
-       err = l2cap_add_psm(pchan, addr, cpu_to_le16(L2CAP_PSM_IPSP));
+       err = l2cap_add_psm(chan, addr, cpu_to_le16(L2CAP_PSM_IPSP));
        if (err) {
-               l2cap_chan_put(pchan);
+               l2cap_chan_put(chan);
                BT_ERR("psm cannot be added err %d", err);
                return NULL;
        }
 
-       return pchan;
+       return chan;
 }
 
 static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
@@ -1165,7 +1113,7 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
                return -ENOENT;
 
        hci_dev_lock(hdev);
-       hcon = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
+       hcon = hci_conn_hash_lookup_le(hdev, addr, *addr_type);
        hci_dev_unlock(hdev);
 
        if (!hcon)
index 70f9d945faf7b439ce7a5283e94bc9687e8e67af..a3bffd1ec2b46adbc7a2130573ae991950789a77 100644 (file)
@@ -33,7 +33,7 @@
 
 #include "selftest.h"
 
-#define VERSION "2.20"
+#define VERSION "2.21"
 
 /* Bluetooth sockets */
 #define BT_MAX_PROTO   8
@@ -221,7 +221,7 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
 
        BT_DBG("sock %p sk %p len %zu", sock, sk, len);
 
-       if (flags & (MSG_OOB))
+       if (flags & MSG_OOB)
                return -EOPNOTSUPP;
 
        skb = skb_recv_datagram(sk, flags, noblock, &err);
index 2dda439c8cb83b7fa72b238dcf93023088762001..85b82f7adbd2dd96ec7163a6d3bc3c155dc2518e 100644 (file)
@@ -59,15 +59,11 @@ static const struct sco_param esco_param_msbc[] = {
        { EDR_ESCO_MASK | ESCO_EV3,   0x0008,   0x02 }, /* T1 */
 };
 
-static void hci_le_create_connection_cancel(struct hci_conn *conn)
-{
-       hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
-}
-
 /* This function requires the caller holds hdev->lock */
 static void hci_connect_le_scan_cleanup(struct hci_conn *conn)
 {
        struct hci_conn_params *params;
+       struct hci_dev *hdev = conn->hdev;
        struct smp_irk *irk;
        bdaddr_t *bdaddr;
        u8 bdaddr_type;
@@ -76,14 +72,15 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn)
        bdaddr_type = conn->dst_type;
 
        /* Check if we need to convert to identity address */
-       irk = hci_get_irk(conn->hdev, bdaddr, bdaddr_type);
+       irk = hci_get_irk(hdev, bdaddr, bdaddr_type);
        if (irk) {
                bdaddr = &irk->bdaddr;
                bdaddr_type = irk->addr_type;
        }
 
-       params = hci_explicit_connect_lookup(conn->hdev, bdaddr, bdaddr_type);
-       if (!params)
+       params = hci_pend_le_action_lookup(&hdev->pend_le_conns, bdaddr,
+                                          bdaddr_type);
+       if (!params || !params->explicit_connect)
                return;
 
        /* The connection attempt was doing scan for new RPA, and is
@@ -97,21 +94,21 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn)
 
        switch (params->auto_connect) {
        case HCI_AUTO_CONN_EXPLICIT:
-               hci_conn_params_del(conn->hdev, bdaddr, bdaddr_type);
+               hci_conn_params_del(hdev, bdaddr, bdaddr_type);
                /* return instead of break to avoid duplicate scan update */
                return;
        case HCI_AUTO_CONN_DIRECT:
        case HCI_AUTO_CONN_ALWAYS:
-               list_add(&params->action, &conn->hdev->pend_le_conns);
+               list_add(&params->action, &hdev->pend_le_conns);
                break;
        case HCI_AUTO_CONN_REPORT:
-               list_add(&params->action, &conn->hdev->pend_le_reports);
+               list_add(&params->action, &hdev->pend_le_reports);
                break;
        default:
                break;
        }
 
-       hci_update_background_scan(conn->hdev);
+       hci_update_background_scan(hdev);
 }
 
 static void hci_conn_cleanup(struct hci_conn *conn)
@@ -137,18 +134,51 @@ static void hci_conn_cleanup(struct hci_conn *conn)
        hci_conn_put(conn);
 }
 
-/* This function requires the caller holds hdev->lock */
-static void hci_connect_le_scan_remove(struct hci_conn *conn)
+static void le_scan_cleanup(struct work_struct *work)
 {
-       hci_connect_le_scan_cleanup(conn);
+       struct hci_conn *conn = container_of(work, struct hci_conn,
+                                            le_scan_cleanup);
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_conn *c = NULL;
 
-       /* We can't call hci_conn_del here since that would deadlock
-        * with trying to call cancel_delayed_work_sync(&conn->disc_work).
-        * Instead, call just hci_conn_cleanup() which contains the bare
-        * minimum cleanup operations needed for a connection in this
-        * state.
+       BT_DBG("%s hcon %p", hdev->name, conn);
+
+       hci_dev_lock(hdev);
+
+       /* Check that the hci_conn is still around */
+       rcu_read_lock();
+       list_for_each_entry_rcu(c, &hdev->conn_hash.list, list) {
+               if (c == conn)
+                       break;
+       }
+       rcu_read_unlock();
+
+       if (c == conn) {
+               hci_connect_le_scan_cleanup(conn);
+               hci_conn_cleanup(conn);
+       }
+
+       hci_dev_unlock(hdev);
+       hci_dev_put(hdev);
+       hci_conn_put(conn);
+}
+
+static void hci_connect_le_scan_remove(struct hci_conn *conn)
+{
+       BT_DBG("%s hcon %p", conn->hdev->name, conn);
+
+       /* We can't call hci_conn_del/hci_conn_cleanup here since that
+        * could deadlock with another hci_conn_del() call that's holding
+        * hci_dev_lock and doing cancel_delayed_work_sync(&conn->disc_work).
+        * Instead, grab temporary extra references to the hci_dev and
+        * hci_conn and perform the necessary cleanup in a separate work
+        * callback.
         */
-       hci_conn_cleanup(conn);
+
+       hci_dev_hold(conn->hdev);
+       hci_conn_get(conn);
+
+       schedule_work(&conn->le_scan_cleanup);
 }
 
 static void hci_acl_create_connection(struct hci_conn *conn)
@@ -194,33 +224,8 @@ static void hci_acl_create_connection(struct hci_conn *conn)
        hci_send_cmd(hdev, HCI_OP_CREATE_CONN, sizeof(cp), &cp);
 }
 
-static void hci_acl_create_connection_cancel(struct hci_conn *conn)
-{
-       struct hci_cp_create_conn_cancel cp;
-
-       BT_DBG("hcon %p", conn);
-
-       if (conn->hdev->hci_ver < BLUETOOTH_VER_1_2)
-               return;
-
-       bacpy(&cp.bdaddr, &conn->dst);
-       hci_send_cmd(conn->hdev, HCI_OP_CREATE_CONN_CANCEL, sizeof(cp), &cp);
-}
-
-static void hci_reject_sco(struct hci_conn *conn)
-{
-       struct hci_cp_reject_sync_conn_req cp;
-
-       cp.reason = HCI_ERROR_REJ_LIMITED_RESOURCES;
-       bacpy(&cp.bdaddr, &conn->dst);
-
-       hci_send_cmd(conn->hdev, HCI_OP_REJECT_SYNC_CONN_REQ, sizeof(cp), &cp);
-}
-
 int hci_disconnect(struct hci_conn *conn, __u8 reason)
 {
-       struct hci_cp_disconnect cp;
-
        BT_DBG("hcon %p", conn);
 
        /* When we are master of an established connection and it enters
@@ -228,7 +233,8 @@ int hci_disconnect(struct hci_conn *conn, __u8 reason)
         * current clock offset.  Processing of the result is done
         * within the event handling and hci_clock_offset_evt function.
         */
-       if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER) {
+       if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER &&
+           (conn->state == BT_CONNECTED || conn->state == BT_CONFIG)) {
                struct hci_dev *hdev = conn->hdev;
                struct hci_cp_read_clock_offset clkoff_cp;
 
@@ -237,25 +243,7 @@ int hci_disconnect(struct hci_conn *conn, __u8 reason)
                             &clkoff_cp);
        }
 
-       conn->state = BT_DISCONN;
-
-       cp.handle = cpu_to_le16(conn->handle);
-       cp.reason = reason;
-       return hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
-}
-
-static void hci_amp_disconn(struct hci_conn *conn)
-{
-       struct hci_cp_disconn_phy_link cp;
-
-       BT_DBG("hcon %p", conn);
-
-       conn->state = BT_DISCONN;
-
-       cp.phy_handle = HCI_PHY_HANDLE(conn->handle);
-       cp.reason = hci_proto_disconn_ind(conn);
-       hci_send_cmd(conn->hdev, HCI_OP_DISCONN_PHY_LINK,
-                    sizeof(cp), &cp);
+       return hci_abort_conn(conn, reason);
 }
 
 static void hci_add_sco(struct hci_conn *conn, __u16 handle)
@@ -421,35 +409,14 @@ static void hci_conn_timeout(struct work_struct *work)
        if (refcnt > 0)
                return;
 
-       switch (conn->state) {
-       case BT_CONNECT:
-       case BT_CONNECT2:
-               if (conn->out) {
-                       if (conn->type == ACL_LINK)
-                               hci_acl_create_connection_cancel(conn);
-                       else if (conn->type == LE_LINK) {
-                               if (test_bit(HCI_CONN_SCANNING, &conn->flags))
-                                       hci_connect_le_scan_remove(conn);
-                               else
-                                       hci_le_create_connection_cancel(conn);
-                       }
-               } else if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
-                       hci_reject_sco(conn);
-               }
-               break;
-       case BT_CONFIG:
-       case BT_CONNECTED:
-               if (conn->type == AMP_LINK) {
-                       hci_amp_disconn(conn);
-               } else {
-                       __u8 reason = hci_proto_disconn_ind(conn);
-                       hci_disconnect(conn, reason);
-               }
-               break;
-       default:
-               conn->state = BT_CLOSED;
-               break;
+       /* LE connections in scanning state need special handling */
+       if (conn->state == BT_CONNECT && conn->type == LE_LINK &&
+           test_bit(HCI_CONN_SCANNING, &conn->flags)) {
+               hci_connect_le_scan_remove(conn);
+               return;
        }
+
+       hci_abort_conn(conn, hci_proto_disconn_ind(conn));
 }
 
 /* Enter sniff mode */
@@ -517,7 +484,7 @@ static void le_conn_timeout(struct work_struct *work)
                return;
        }
 
-       hci_le_create_connection_cancel(conn);
+       hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
 }
 
 struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
@@ -580,6 +547,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
        INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept);
        INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
        INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
+       INIT_WORK(&conn->le_scan_cleanup, le_scan_cleanup);
 
        atomic_set(&conn->refcnt, 0);
 
@@ -835,7 +803,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
         * attempt, we simply update pending_sec_level and auth_type fields
         * and return the object found.
         */
-       conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
+       conn = hci_conn_hash_lookup_le(hdev, dst, dst_type);
        conn_unfinished = NULL;
        if (conn) {
                if (conn->state == BT_CONNECT &&
@@ -985,13 +953,10 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
 {
        struct hci_conn *conn;
 
-       conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
+       conn = hci_conn_hash_lookup_le(hdev, addr, type);
        if (!conn)
                return false;
 
-       if (conn->dst_type != type)
-               return false;
-
        if (conn->state != BT_CONNECTED)
                return false;
 
@@ -1064,7 +1029,7 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
         * attempt, we simply update pending_sec_level and auth_type fields
         * and return the object found.
         */
-       conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
+       conn = hci_conn_hash_lookup_le(hdev, dst, dst_type);
        if (conn) {
                if (conn->pending_sec_level < sec_level)
                        conn->pending_sec_level = sec_level;
index e837539452fb0e2880d8335da7769752a3b08110..83a6aacfab31cb136745001a03fcb6368d7b259f 100644 (file)
@@ -65,13 +65,6 @@ static DEFINE_IDA(hci_index_ida);
 #define hci_req_lock(d)                mutex_lock(&d->req_lock)
 #define hci_req_unlock(d)      mutex_unlock(&d->req_lock)
 
-/* ---- HCI notifications ---- */
-
-static void hci_notify(struct hci_dev *hdev, int event)
-{
-       hci_sock_dev_event(hdev, event);
-}
-
 /* ---- HCI debugfs entries ---- */
 
 static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
@@ -134,6 +127,77 @@ static const struct file_operations dut_mode_fops = {
        .llseek         = default_llseek,
 };
 
+static ssize_t vendor_diag_read(struct file *file, char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t vendor_diag_write(struct file *file, const char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+       bool enable;
+       int err;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       /* When the diagnostic flags are not persistent and the transport
+        * is not active, then there is no need for the vendor callback.
+        *
+        * Instead just store the desired value. If needed the setting
+        * will be programmed when the controller gets powered on.
+        */
+       if (test_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks) &&
+           !test_bit(HCI_RUNNING, &hdev->flags))
+               goto done;
+
+       hci_req_lock(hdev);
+       err = hdev->set_diag(hdev, enable);
+       hci_req_unlock(hdev);
+
+       if (err < 0)
+               return err;
+
+done:
+       if (enable)
+               hci_dev_set_flag(hdev, HCI_VENDOR_DIAG);
+       else
+               hci_dev_clear_flag(hdev, HCI_VENDOR_DIAG);
+
+       return count;
+}
+
+static const struct file_operations vendor_diag_fops = {
+       .open           = simple_open,
+       .read           = vendor_diag_read,
+       .write          = vendor_diag_write,
+       .llseek         = default_llseek,
+};
+
+static void hci_debugfs_create_basic(struct hci_dev *hdev)
+{
+       debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
+                           &dut_mode_fops);
+
+       if (hdev->set_diag)
+               debugfs_create_file("vendor_diag", 0644, hdev->debugfs, hdev,
+                                   &vendor_diag_fops);
+}
+
 /* ---- HCI requests ---- */
 
 static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
@@ -693,7 +757,8 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
 
        hci_setup_event_mask(req);
 
-       if (hdev->commands[6] & 0x20) {
+       if (hdev->commands[6] & 0x20 &&
+           !test_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks)) {
                struct hci_cp_read_stored_link_key cp;
 
                bacpy(&cp.bdaddr, BDADDR_ANY);
@@ -849,13 +914,8 @@ static int __hci_init(struct hci_dev *hdev)
        if (err < 0)
                return err;
 
-       /* The Device Under Test (DUT) mode is special and available for
-        * all controller types. So just create it early on.
-        */
-       if (hci_dev_test_flag(hdev, HCI_SETUP)) {
-               debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
-                                   &dut_mode_fops);
-       }
+       if (hci_dev_test_flag(hdev, HCI_SETUP))
+               hci_debugfs_create_basic(hdev);
 
        err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT);
        if (err < 0)
@@ -932,6 +992,9 @@ static int __hci_unconf_init(struct hci_dev *hdev)
        if (err < 0)
                return err;
 
+       if (hci_dev_test_flag(hdev, HCI_SETUP))
+               hci_debugfs_create_basic(hdev);
+
        return 0;
 }
 
@@ -1384,10 +1447,15 @@ static int hci_dev_do_open(struct hci_dev *hdev)
                goto done;
        }
 
+       set_bit(HCI_RUNNING, &hdev->flags);
+       hci_sock_dev_event(hdev, HCI_DEV_OPEN);
+
        atomic_set(&hdev->cmd_cnt, 1);
        set_bit(HCI_INIT, &hdev->flags);
 
        if (hci_dev_test_flag(hdev, HCI_SETUP)) {
+               hci_sock_dev_event(hdev, HCI_DEV_SETUP);
+
                if (hdev->setup)
                        ret = hdev->setup(hdev);
 
@@ -1428,17 +1496,28 @@ static int hci_dev_do_open(struct hci_dev *hdev)
 
        if (!ret) {
                if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
-                   !hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
+                   !hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
                        ret = __hci_init(hdev);
+                       if (!ret && hdev->post_init)
+                               ret = hdev->post_init(hdev);
+               }
        }
 
+       /* If the HCI Reset command is clearing all diagnostic settings,
+        * then they need to be reprogrammed after the init procedure
+        * completed.
+        */
+       if (test_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks) &&
+           hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) && hdev->set_diag)
+               ret = hdev->set_diag(hdev, true);
+
        clear_bit(HCI_INIT, &hdev->flags);
 
        if (!ret) {
                hci_dev_hold(hdev);
                hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
                set_bit(HCI_UP, &hdev->flags);
-               hci_notify(hdev, HCI_DEV_UP);
+               hci_sock_dev_event(hdev, HCI_DEV_UP);
                if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
                    !hci_dev_test_flag(hdev, HCI_CONFIG) &&
                    !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
@@ -1465,6 +1544,9 @@ static int hci_dev_do_open(struct hci_dev *hdev)
                        hdev->sent_cmd = NULL;
                }
 
+               clear_bit(HCI_RUNNING, &hdev->flags);
+               hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
+
                hdev->close(hdev);
                hdev->flags &= BIT(HCI_RAW);
        }
@@ -1548,8 +1630,10 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev)
        BT_DBG("All LE pending actions cleared");
 }
 
-static int hci_dev_do_close(struct hci_dev *hdev)
+int hci_dev_do_close(struct hci_dev *hdev)
 {
+       bool auto_off;
+
        BT_DBG("%s %p", hdev->name, hdev);
 
        if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
@@ -1605,10 +1689,10 @@ static int hci_dev_do_close(struct hci_dev *hdev)
 
        hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
 
-       if (!hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
-               if (hdev->dev_type == HCI_BREDR)
-                       mgmt_powered(hdev, 0);
-       }
+       auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF);
+
+       if (!auto_off && hdev->dev_type == HCI_BREDR)
+               mgmt_powered(hdev, 0);
 
        hci_inquiry_cache_flush(hdev);
        hci_pend_le_actions_clear(hdev);
@@ -1617,7 +1701,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
 
        smp_unregister(hdev);
 
-       hci_notify(hdev, HCI_DEV_DOWN);
+       hci_sock_dev_event(hdev, HCI_DEV_DOWN);
 
        if (hdev->flush)
                hdev->flush(hdev);
@@ -1625,9 +1709,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        /* Reset device */
        skb_queue_purge(&hdev->cmd_q);
        atomic_set(&hdev->cmd_cnt, 1);
-       if (!hci_dev_test_flag(hdev, HCI_AUTO_OFF) &&
-           !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
-           test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) {
+       if (test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks) &&
+           !auto_off && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
                set_bit(HCI_INIT, &hdev->flags);
                __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
                clear_bit(HCI_INIT, &hdev->flags);
@@ -1648,6 +1731,9 @@ static int hci_dev_do_close(struct hci_dev *hdev)
                hdev->sent_cmd = NULL;
        }
 
+       clear_bit(HCI_RUNNING, &hdev->flags);
+       hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
+
        /* After this point our queues are empty
         * and no tasks are scheduled. */
        hdev->close(hdev);
@@ -2847,23 +2933,6 @@ struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
        return NULL;
 }
 
-/* This function requires the caller holds hdev->lock */
-struct hci_conn_params *hci_explicit_connect_lookup(struct hci_dev *hdev,
-                                                   bdaddr_t *addr,
-                                                   u8 addr_type)
-{
-       struct hci_conn_params *param;
-
-       list_for_each_entry(param, &hdev->pend_le_conns, action) {
-               if (bacmp(&param->addr, addr) == 0 &&
-                   param->addr_type == addr_type &&
-                   param->explicit_connect)
-                       return param;
-       }
-
-       return NULL;
-}
-
 /* This function requires the caller holds hdev->lock */
 struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
                                            bdaddr_t *addr, u8 addr_type)
@@ -3338,7 +3407,7 @@ int hci_register_dev(struct hci_dev *hdev)
        if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
                hci_dev_set_flag(hdev, HCI_UNCONFIGURED);
 
-       hci_notify(hdev, HCI_DEV_REG);
+       hci_sock_dev_event(hdev, HCI_DEV_REG);
        hci_dev_hold(hdev);
 
        queue_work(hdev->req_workqueue, &hdev->power_on);
@@ -3386,7 +3455,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
         * pending list */
        BUG_ON(!list_empty(&hdev->mgmt_pending));
 
-       hci_notify(hdev, HCI_DEV_UNREG);
+       hci_sock_dev_event(hdev, HCI_DEV_UNREG);
 
        if (hdev->rfkill) {
                rfkill_unregister(hdev->rfkill);
@@ -3423,7 +3492,7 @@ EXPORT_SYMBOL(hci_unregister_dev);
 /* Suspend HCI device */
 int hci_suspend_dev(struct hci_dev *hdev)
 {
-       hci_notify(hdev, HCI_DEV_SUSPEND);
+       hci_sock_dev_event(hdev, HCI_DEV_SUSPEND);
        return 0;
 }
 EXPORT_SYMBOL(hci_suspend_dev);
@@ -3431,7 +3500,7 @@ EXPORT_SYMBOL(hci_suspend_dev);
 /* Resume HCI device */
 int hci_resume_dev(struct hci_dev *hdev)
 {
-       hci_notify(hdev, HCI_DEV_RESUME);
+       hci_sock_dev_event(hdev, HCI_DEV_RESUME);
        return 0;
 }
 EXPORT_SYMBOL(hci_resume_dev);
@@ -3463,6 +3532,13 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
                return -ENXIO;
        }
 
+       if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
+           bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
+           bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
        /* Incoming skb */
        bt_cb(skb)->incoming = 1;
 
@@ -3476,6 +3552,22 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(hci_recv_frame);
 
+/* Receive diagnostic message from HCI drivers */
+int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       /* Mark as diagnostic packet */
+       bt_cb(skb)->pkt_type = HCI_DIAG_PKT;
+
+       /* Time stamp */
+       __net_timestamp(skb);
+
+       skb_queue_tail(&hdev->rx_q, skb);
+       queue_work(hdev->workqueue, &hdev->rx_work);
+
+       return 0;
+}
+EXPORT_SYMBOL(hci_recv_diag);
+
 /* ---- Interface to upper protocols ---- */
 
 int hci_register_cb(struct hci_cb *cb)
@@ -3522,6 +3614,11 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
        /* Get rid of skb owner, prior to sending to the driver. */
        skb_orphan(skb);
 
+       if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+               kfree_skb(skb);
+               return;
+       }
+
        err = hdev->send(hdev, skb);
        if (err < 0) {
                BT_ERR("%s sending frame failed (%d)", hdev->name, err);
@@ -3546,7 +3643,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
        /* Stand-alone HCI commands must be flagged as
         * single-command requests.
         */
-       bt_cb(skb)->req.start = true;
+       bt_cb(skb)->hci.req_start = true;
 
        skb_queue_tail(&hdev->cmd_q, skb);
        queue_work(hdev->workqueue, &hdev->cmd_work);
@@ -3572,6 +3669,25 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
        return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
 }
 
+/* Send HCI command and wait for command commplete event */
+struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
+                            const void *param, u32 timeout)
+{
+       struct sk_buff *skb;
+
+       if (!test_bit(HCI_UP, &hdev->flags))
+               return ERR_PTR(-ENETDOWN);
+
+       bt_dev_dbg(hdev, "opcode 0x%4.4x plen %d", opcode, plen);
+
+       hci_req_lock(hdev);
+       skb = __hci_cmd_sync(hdev, opcode, plen, param, timeout);
+       hci_req_unlock(hdev);
+
+       return skb;
+}
+EXPORT_SYMBOL(hci_cmd_sync);
+
 /* Send ACL data */
 static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
 {
@@ -4224,7 +4340,7 @@ static bool hci_req_is_complete(struct hci_dev *hdev)
        if (!skb)
                return true;
 
-       return bt_cb(skb)->req.start;
+       return bt_cb(skb)->hci.req_start;
 }
 
 static void hci_resend_last(struct hci_dev *hdev)
@@ -4284,26 +4400,26 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
         * callback would be found in hdev->sent_cmd instead of the
         * command queue (hdev->cmd_q).
         */
-       if (bt_cb(hdev->sent_cmd)->req.complete) {
-               *req_complete = bt_cb(hdev->sent_cmd)->req.complete;
+       if (bt_cb(hdev->sent_cmd)->hci.req_complete) {
+               *req_complete = bt_cb(hdev->sent_cmd)->hci.req_complete;
                return;
        }
 
-       if (bt_cb(hdev->sent_cmd)->req.complete_skb) {
-               *req_complete_skb = bt_cb(hdev->sent_cmd)->req.complete_skb;
+       if (bt_cb(hdev->sent_cmd)->hci.req_complete_skb) {
+               *req_complete_skb = bt_cb(hdev->sent_cmd)->hci.req_complete_skb;
                return;
        }
 
        /* Remove all pending commands belonging to this request */
        spin_lock_irqsave(&hdev->cmd_q.lock, flags);
        while ((skb = __skb_dequeue(&hdev->cmd_q))) {
-               if (bt_cb(skb)->req.start) {
+               if (bt_cb(skb)->hci.req_start) {
                        __skb_queue_head(&hdev->cmd_q, skb);
                        break;
                }
 
-               *req_complete = bt_cb(skb)->req.complete;
-               *req_complete_skb = bt_cb(skb)->req.complete_skb;
+               *req_complete = bt_cb(skb)->hci.req_complete;
+               *req_complete_skb = bt_cb(skb)->hci.req_complete_skb;
                kfree_skb(skb);
        }
        spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
index bc31099d3b5bd113a8496b2ccad9e3e40364187f..d57c11c1c6b54758c698949b1b679d7029640d66 100644 (file)
@@ -1915,7 +1915,8 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status)
 
        hci_dev_lock(hdev);
 
-       conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr);
+       conn = hci_conn_hash_lookup_le(hdev, &cp->peer_addr,
+                                      cp->peer_addr_type);
        if (!conn)
                goto unlock;
 
@@ -3137,7 +3138,7 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
         * complete event).
         */
        if (ev->status ||
-           (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event))
+           (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->hci.req_event))
                hci_req_cmd_complete(hdev, *opcode, ev->status, req_complete,
                                     req_complete_skb);
 
@@ -4724,6 +4725,27 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
        struct hci_conn *conn;
        bool match;
        u32 flags;
+       u8 *ptr, real_len;
+
+       /* Find the end of the data in case the report contains padded zero
+        * bytes at the end causing an invalid length value.
+        *
+        * When data is NULL, len is 0 so there is no need for extra ptr
+        * check as 'ptr < data + 0' is already false in such case.
+        */
+       for (ptr = data; ptr < data + len && *ptr; ptr += *ptr + 1) {
+               if (ptr + 1 + *ptr > data + len)
+                       break;
+       }
+
+       real_len = ptr - data;
+
+       /* Adjust for actual length */
+       if (len != real_len) {
+               BT_ERR_RATELIMITED("%s advertising data length corrected",
+                                  hdev->name);
+               len = real_len;
+       }
 
        /* If the direct address is present, then this report is from
         * a LE Direct Advertising Report event. In that case it is
@@ -5187,7 +5209,7 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
        u8 status = 0, event = hdr->evt, req_evt = 0;
        u16 opcode = HCI_OP_NOP;
 
-       if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) {
+       if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->hci.req_event == event) {
                struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
                opcode = __le16_to_cpu(cmd_hdr->opcode);
                hci_req_cmd_complete(hdev, opcode, status, &req_complete,
index b7369220c9efff616d13f3ad3aeec44c809bd4e0..981f8a202c27d2298a8a9459fa2a671cb3b3197a 100644 (file)
@@ -56,8 +56,8 @@ static int req_run(struct hci_request *req, hci_req_complete_t complete,
                return -ENODATA;
 
        skb = skb_peek_tail(&req->cmd_q);
-       bt_cb(skb)->req.complete = complete;
-       bt_cb(skb)->req.complete_skb = complete_skb;
+       bt_cb(skb)->hci.req_complete = complete;
+       bt_cb(skb)->hci.req_complete_skb = complete_skb;
 
        spin_lock_irqsave(&hdev->cmd_q.lock, flags);
        skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
@@ -99,7 +99,7 @@ struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
        BT_DBG("skb len %d", skb->len);
 
        bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
-       bt_cb(skb)->opcode = opcode;
+       bt_cb(skb)->hci.opcode = opcode;
 
        return skb;
 }
@@ -128,9 +128,9 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
        }
 
        if (skb_queue_empty(&req->cmd_q))
-               bt_cb(skb)->req.start = true;
+               bt_cb(skb)->hci.req_start = true;
 
-       bt_cb(skb)->req.event = event;
+       bt_cb(skb)->hci.req_event = event;
 
        skb_queue_tail(&req->cmd_q, skb);
 }
@@ -564,3 +564,96 @@ void hci_update_background_scan(struct hci_dev *hdev)
        if (err && err != -ENODATA)
                BT_ERR("Failed to run HCI request: err %d", err);
 }
+
+void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
+                     u8 reason)
+{
+       switch (conn->state) {
+       case BT_CONNECTED:
+       case BT_CONFIG:
+               if (conn->type == AMP_LINK) {
+                       struct hci_cp_disconn_phy_link cp;
+
+                       cp.phy_handle = HCI_PHY_HANDLE(conn->handle);
+                       cp.reason = reason;
+                       hci_req_add(req, HCI_OP_DISCONN_PHY_LINK, sizeof(cp),
+                                   &cp);
+               } else {
+                       struct hci_cp_disconnect dc;
+
+                       dc.handle = cpu_to_le16(conn->handle);
+                       dc.reason = reason;
+                       hci_req_add(req, HCI_OP_DISCONNECT, sizeof(dc), &dc);
+               }
+
+               conn->state = BT_DISCONN;
+
+               break;
+       case BT_CONNECT:
+               if (conn->type == LE_LINK) {
+                       if (test_bit(HCI_CONN_SCANNING, &conn->flags))
+                               break;
+                       hci_req_add(req, HCI_OP_LE_CREATE_CONN_CANCEL,
+                                   0, NULL);
+               } else if (conn->type == ACL_LINK) {
+                       if (req->hdev->hci_ver < BLUETOOTH_VER_1_2)
+                               break;
+                       hci_req_add(req, HCI_OP_CREATE_CONN_CANCEL,
+                                   6, &conn->dst);
+               }
+               break;
+       case BT_CONNECT2:
+               if (conn->type == ACL_LINK) {
+                       struct hci_cp_reject_conn_req rej;
+
+                       bacpy(&rej.bdaddr, &conn->dst);
+                       rej.reason = reason;
+
+                       hci_req_add(req, HCI_OP_REJECT_CONN_REQ,
+                                   sizeof(rej), &rej);
+               } else if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
+                       struct hci_cp_reject_sync_conn_req rej;
+
+                       bacpy(&rej.bdaddr, &conn->dst);
+
+                       /* SCO rejection has its own limited set of
+                        * allowed error values (0x0D-0x0F) which isn't
+                        * compatible with most values passed to this
+                        * function. To be safe hard-code one of the
+                        * values that's suitable for SCO.
+                        */
+                       rej.reason = HCI_ERROR_REMOTE_LOW_RESOURCES;
+
+                       hci_req_add(req, HCI_OP_REJECT_SYNC_CONN_REQ,
+                                   sizeof(rej), &rej);
+               }
+               break;
+       default:
+               conn->state = BT_CLOSED;
+               break;
+       }
+}
+
+static void abort_conn_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+{
+       if (status)
+               BT_DBG("Failed to abort connection: status 0x%2.2x", status);
+}
+
+int hci_abort_conn(struct hci_conn *conn, u8 reason)
+{
+       struct hci_request req;
+       int err;
+
+       hci_req_init(&req, conn->hdev);
+
+       __hci_abort_conn(&req, conn, reason);
+
+       err = hci_req_run(&req, abort_conn_complete);
+       if (err && err != -ENODATA) {
+               BT_ERR("Failed to run HCI request: err %d", err);
+               return err;
+       }
+
+       return 0;
+}
index bf6df92f42dbf44be59134349f8e62ca953d9214..25c7f1305dcbcb273816c15c95097f2a1ed2c1f2 100644 (file)
@@ -55,3 +55,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy,
 
 void hci_update_background_scan(struct hci_dev *hdev);
 void __hci_update_background_scan(struct hci_request *req);
+
+int hci_abort_conn(struct hci_conn *conn, u8 reason);
+void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
+                     u8 reason);
index f2d30d1156c92ed7cb8c05dfbe737f1615ad50a7..b1eb8c09a66016c2cbcca79dcf3bb533bb85ec02 100644 (file)
@@ -120,10 +120,7 @@ static bool is_filtered_packet(struct sock *sk, struct sk_buff *skb)
        /* Apply filter */
        flt = &hci_pi(sk)->filter;
 
-       if (bt_cb(skb)->pkt_type == HCI_VENDOR_PKT)
-               flt_type = 0;
-       else
-               flt_type = bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS;
+       flt_type = bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS;
 
        if (!test_bit(flt_type, &flt->type_mask))
                return true;
@@ -173,6 +170,11 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
                        continue;
 
                if (hci_pi(sk)->channel == HCI_CHANNEL_RAW) {
+                       if (bt_cb(skb)->pkt_type != HCI_COMMAND_PKT &&
+                           bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
+                           bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
+                           bt_cb(skb)->pkt_type != HCI_SCODATA_PKT)
+                               continue;
                        if (is_filtered_packet(sk, skb))
                                continue;
                } else if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
@@ -279,6 +281,9 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
                else
                        opcode = cpu_to_le16(HCI_MON_SCO_TX_PKT);
                break;
+       case HCI_DIAG_PKT:
+               opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG);
+               break;
        default:
                return;
        }
@@ -303,6 +308,7 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
 {
        struct hci_mon_hdr *hdr;
        struct hci_mon_new_index *ni;
+       struct hci_mon_index_info *ii;
        struct sk_buff *skb;
        __le16 opcode;
 
@@ -312,7 +318,7 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
                if (!skb)
                        return NULL;
 
-               ni = (void *) skb_put(skb, HCI_MON_NEW_INDEX_SIZE);
+               ni = (void *)skb_put(skb, HCI_MON_NEW_INDEX_SIZE);
                ni->type = hdev->dev_type;
                ni->bus = hdev->bus;
                bacpy(&ni->bdaddr, &hdev->bdaddr);
@@ -329,6 +335,40 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
                opcode = cpu_to_le16(HCI_MON_DEL_INDEX);
                break;
 
+       case HCI_DEV_SETUP:
+               if (hdev->manufacturer == 0xffff)
+                       return NULL;
+
+               /* fall through */
+
+       case HCI_DEV_UP:
+               skb = bt_skb_alloc(HCI_MON_INDEX_INFO_SIZE, GFP_ATOMIC);
+               if (!skb)
+                       return NULL;
+
+               ii = (void *)skb_put(skb, HCI_MON_INDEX_INFO_SIZE);
+               bacpy(&ii->bdaddr, &hdev->bdaddr);
+               ii->manufacturer = cpu_to_le16(hdev->manufacturer);
+
+               opcode = cpu_to_le16(HCI_MON_INDEX_INFO);
+               break;
+
+       case HCI_DEV_OPEN:
+               skb = bt_skb_alloc(0, GFP_ATOMIC);
+               if (!skb)
+                       return NULL;
+
+               opcode = cpu_to_le16(HCI_MON_OPEN_INDEX);
+               break;
+
+       case HCI_DEV_CLOSE:
+               skb = bt_skb_alloc(0, GFP_ATOMIC);
+               if (!skb)
+                       return NULL;
+
+               opcode = cpu_to_le16(HCI_MON_CLOSE_INDEX);
+               break;
+
        default:
                return NULL;
        }
@@ -358,6 +398,28 @@ static void send_monitor_replay(struct sock *sk)
 
                if (sock_queue_rcv_skb(sk, skb))
                        kfree_skb(skb);
+
+               if (!test_bit(HCI_RUNNING, &hdev->flags))
+                       continue;
+
+               skb = create_monitor_event(hdev, HCI_DEV_OPEN);
+               if (!skb)
+                       continue;
+
+               if (sock_queue_rcv_skb(sk, skb))
+                       kfree_skb(skb);
+
+               if (test_bit(HCI_UP, &hdev->flags))
+                       skb = create_monitor_event(hdev, HCI_DEV_UP);
+               else if (hci_dev_test_flag(hdev, HCI_SETUP))
+                       skb = create_monitor_event(hdev, HCI_DEV_SETUP);
+               else
+                       skb = NULL;
+
+               if (skb) {
+                       if (sock_queue_rcv_skb(sk, skb))
+                               kfree_skb(skb);
+               }
        }
 
        read_unlock(&hci_dev_list_lock);
@@ -392,14 +454,12 @@ static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
 
 void hci_sock_dev_event(struct hci_dev *hdev, int event)
 {
-       struct hci_ev_si_device ev;
-
        BT_DBG("hdev %s event %d", hdev->name, event);
 
-       /* Send event to monitor */
        if (atomic_read(&monitor_promisc)) {
                struct sk_buff *skb;
 
+               /* Send event to monitor */
                skb = create_monitor_event(hdev, event);
                if (skb) {
                        hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
@@ -408,10 +468,14 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event)
                }
        }
 
-       /* Send event to sockets */
-       ev.event  = event;
-       ev.dev_id = hdev->id;
-       hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev);
+       if (event <= HCI_DEV_DOWN) {
+               struct hci_ev_si_device ev;
+
+               /* Send event to sockets */
+               ev.event  = event;
+               ev.dev_id = hdev->id;
+               hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev);
+       }
 
        if (event == HCI_DEV_UNREG) {
                struct sock *sk;
@@ -503,7 +567,16 @@ static int hci_sock_release(struct socket *sock)
 
        if (hdev) {
                if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
-                       hci_dev_close(hdev->id);
+                       /* When releasing an user channel exclusive access,
+                        * call hci_dev_do_close directly instead of calling
+                        * hci_dev_close to ensure the exclusive access will
+                        * be released and the controller brought back down.
+                        *
+                        * The checking of HCI_AUTO_OFF is not needed in this
+                        * case since it will have been cleared already when
+                        * opening the user channel.
+                        */
+                       hci_dev_do_close(hdev);
                        hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
                        mgmt_index_added(hdev);
                }
@@ -928,7 +1001,7 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
 
        BT_DBG("sock %p, sk %p", sock, sk);
 
-       if (flags & (MSG_OOB))
+       if (flags & MSG_OOB)
                return -EOPNOTSUPP;
 
        if (sk->sk_state == BT_CLOSED)
@@ -1176,7 +1249,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
                        /* Stand-alone HCI commands must be flagged as
                         * single-command requests.
                         */
-                       bt_cb(skb)->req.start = true;
+                       bt_cb(skb)->hci.req_start = true;
 
                        skb_queue_tail(&hdev->cmd_q, skb);
                        queue_work(hdev->workqueue, &hdev->cmd_work);
@@ -1187,6 +1260,12 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
                        goto drop;
                }
 
+               if (bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
+                   bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+                       err = -EINVAL;
+                       goto drop;
+               }
+
                skb_queue_tail(&hdev->raw_q, skb);
                queue_work(hdev->workqueue, &hdev->tx_work);
        }
index f1a117f8cad22a245044014eae118e09bcdfe271..0bec4588c3c8c3db45a564c81394c464a7faaf4a 100644 (file)
@@ -401,6 +401,20 @@ static void hidp_idle_timeout(unsigned long arg)
 {
        struct hidp_session *session = (struct hidp_session *) arg;
 
+       /* The HIDP user-space API only contains calls to add and remove
+        * devices. There is no way to forward events of any kind. Therefore,
+        * we have to forcefully disconnect a device on idle-timeouts. This is
+        * unfortunate and weird API design, but it is spec-compliant and
+        * required for backwards-compatibility. Hence, on idle-timeout, we
+        * signal driver-detach events, so poll() will be woken up with an
+        * error-condition on both sockets.
+        */
+
+       session->intr_sock->sk->sk_err = EUNATCH;
+       session->ctrl_sock->sk->sk_err = EUNATCH;
+       wake_up_interruptible(sk_sleep(session->intr_sock->sk));
+       wake_up_interruptible(sk_sleep(session->ctrl_sock->sk));
+
        hidp_session_terminate(session);
 }
 
index 586b3d580cfcba0422828cab1843363178dfe85c..1bb5515270449e8115a0c169ea5b6380594c40a4 100644 (file)
@@ -1111,53 +1111,76 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
        if (!sk)
                return 0;
 
+       lock_sock(sk);
+
+       if (sk->sk_shutdown)
+               goto shutdown_already;
+
+       BT_DBG("Handling sock shutdown");
+
        /* prevent sk structure from being freed whilst unlocked */
        sock_hold(sk);
 
        chan = l2cap_pi(sk)->chan;
        /* prevent chan structure from being freed whilst unlocked */
        l2cap_chan_hold(chan);
-       conn = chan->conn;
 
        BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
 
+       if (chan->mode == L2CAP_MODE_ERTM &&
+           chan->unacked_frames > 0 &&
+           chan->state == BT_CONNECTED) {
+               err = __l2cap_wait_ack(sk, chan);
+
+               /* After waiting for ACKs, check whether shutdown
+                * has already been actioned to close the L2CAP
+                * link such as by l2cap_disconnection_req().
+                */
+               if (sk->sk_shutdown)
+                       goto has_shutdown;
+       }
+
+       sk->sk_shutdown = SHUTDOWN_MASK;
+       release_sock(sk);
+
+       l2cap_chan_lock(chan);
+       conn = chan->conn;
+       if (conn)
+               /* prevent conn structure from being freed */
+               l2cap_conn_get(conn);
+       l2cap_chan_unlock(chan);
+
        if (conn)
+               /* mutex lock must be taken before l2cap_chan_lock() */
                mutex_lock(&conn->chan_lock);
 
        l2cap_chan_lock(chan);
-       lock_sock(sk);
+       l2cap_chan_close(chan, 0);
+       l2cap_chan_unlock(chan);
 
-       if (!sk->sk_shutdown) {
-               if (chan->mode == L2CAP_MODE_ERTM &&
-                   chan->unacked_frames > 0 &&
-                   chan->state == BT_CONNECTED)
-                       err = __l2cap_wait_ack(sk, chan);
+       if (conn) {
+               mutex_unlock(&conn->chan_lock);
+               l2cap_conn_put(conn);
+       }
 
-               sk->sk_shutdown = SHUTDOWN_MASK;
+       lock_sock(sk);
 
-               release_sock(sk);
-               l2cap_chan_close(chan, 0);
-               lock_sock(sk);
+       if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
+           !(current->flags & PF_EXITING))
+               err = bt_sock_wait_state(sk, BT_CLOSED,
+                                        sk->sk_lingertime);
 
-               if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
-                   !(current->flags & PF_EXITING))
-                       err = bt_sock_wait_state(sk, BT_CLOSED,
-                                                sk->sk_lingertime);
-       }
+has_shutdown:
+       l2cap_chan_put(chan);
+       sock_put(sk);
 
+shutdown_already:
        if (!err && sk->sk_err)
                err = -sk->sk_err;
 
        release_sock(sk);
-       l2cap_chan_unlock(chan);
-
-       if (conn)
-               mutex_unlock(&conn->chan_lock);
-
-       l2cap_chan_put(chan);
-       sock_put(sk);
 
-       BT_DBG("err: %d", err);
+       BT_DBG("Sock shutdown complete err: %d", err);
 
        return err;
 }
index b36bc041585425beed292ef9d499b4dc8e11397c..aa4cf64e32a67b9263d0c5729b8f2f73289f22f1 100644 (file)
@@ -151,6 +151,22 @@ void bt_info(const char *format, ...)
 }
 EXPORT_SYMBOL(bt_info);
 
+void bt_warn(const char *format, ...)
+{
+       struct va_format vaf;
+       va_list args;
+
+       va_start(args, format);
+
+       vaf.fmt = format;
+       vaf.va = &args;
+
+       pr_warn("%pV", &vaf);
+
+       va_end(args);
+}
+EXPORT_SYMBOL(bt_warn);
+
 void bt_err(const char *format, ...)
 {
        struct va_format vaf;
@@ -166,3 +182,19 @@ void bt_err(const char *format, ...)
        va_end(args);
 }
 EXPORT_SYMBOL(bt_err);
+
+void bt_err_ratelimited(const char *format, ...)
+{
+       struct va_format vaf;
+       va_list args;
+
+       va_start(args, format);
+
+       vaf.fmt = format;
+       vaf.va = &args;
+
+       pr_err_ratelimited("%pV", &vaf);
+
+       va_end(args);
+}
+EXPORT_SYMBOL(bt_err_ratelimited);
index c4fe2fee753fcfaa4233a4bb6675bbbdd29fc9d5..7f22119276f391067b424da490e1c667d7a6272b 100644 (file)
@@ -268,6 +268,14 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len,
                               HCI_SOCK_TRUSTED, skip_sk);
 }
 
+static u8 le_addr_type(u8 mgmt_addr_type)
+{
+       if (mgmt_addr_type == BDADDR_LE_PUBLIC)
+               return ADDR_LE_DEV_PUBLIC;
+       else
+               return ADDR_LE_DEV_RANDOM;
+}
+
 static int read_version(struct sock *sk, struct hci_dev *hdev, void *data,
                        u16 data_len)
 {
@@ -1631,35 +1639,8 @@ static int clean_up_hci_state(struct hci_dev *hdev)
        discov_stopped = hci_stop_discovery(&req);
 
        list_for_each_entry(conn, &hdev->conn_hash.list, list) {
-               struct hci_cp_disconnect dc;
-               struct hci_cp_reject_conn_req rej;
-
-               switch (conn->state) {
-               case BT_CONNECTED:
-               case BT_CONFIG:
-                       dc.handle = cpu_to_le16(conn->handle);
-                       dc.reason = 0x15; /* Terminated due to Power Off */
-                       hci_req_add(&req, HCI_OP_DISCONNECT, sizeof(dc), &dc);
-                       break;
-               case BT_CONNECT:
-                       if (conn->type == LE_LINK)
-                               hci_req_add(&req, HCI_OP_LE_CREATE_CONN_CANCEL,
-                                           0, NULL);
-                       else if (conn->type == ACL_LINK)
-                               hci_req_add(&req, HCI_OP_CREATE_CONN_CANCEL,
-                                           6, &conn->dst);
-                       break;
-               case BT_CONNECT2:
-                       bacpy(&rej.bdaddr, &conn->dst);
-                       rej.reason = 0x15; /* Terminated due to Power Off */
-                       if (conn->type == ACL_LINK)
-                               hci_req_add(&req, HCI_OP_REJECT_CONN_REQ,
-                                           sizeof(rej), &rej);
-                       else if (conn->type == SCO_LINK)
-                               hci_req_add(&req, HCI_OP_REJECT_SYNC_CONN_REQ,
-                                           sizeof(rej), &rej);
-                       break;
-               }
+               /* 0x15 == Terminated due to Power Off */
+               __hci_abort_conn(&req, conn, 0x15);
        }
 
        err = hci_req_run(&req, clean_up_hci_complete);
@@ -3044,9 +3025,10 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
 {
        struct mgmt_cp_unpair_device *cp = data;
        struct mgmt_rp_unpair_device rp;
-       struct hci_cp_disconnect dc;
+       struct hci_conn_params *params;
        struct mgmt_pending_cmd *cmd;
        struct hci_conn *conn;
+       u8 addr_type;
        int err;
 
        memset(&rp, 0, sizeof(rp));
@@ -3087,36 +3069,23 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                        conn = NULL;
 
                err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
-       } else {
-               u8 addr_type;
-
-               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
-                                              &cp->addr.bdaddr);
-               if (conn) {
-                       /* Defer clearing up the connection parameters
-                        * until closing to give a chance of keeping
-                        * them if a repairing happens.
-                        */
-                       set_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
-
-                       /* If disconnection is not requested, then
-                        * clear the connection variable so that the
-                        * link is not terminated.
-                        */
-                       if (!cp->disconnect)
-                               conn = NULL;
+               if (err < 0) {
+                       err = mgmt_cmd_complete(sk, hdev->id,
+                                               MGMT_OP_UNPAIR_DEVICE,
+                                               MGMT_STATUS_NOT_PAIRED, &rp,
+                                               sizeof(rp));
+                       goto unlock;
                }
 
-               if (cp->addr.type == BDADDR_LE_PUBLIC)
-                       addr_type = ADDR_LE_DEV_PUBLIC;
-               else
-                       addr_type = ADDR_LE_DEV_RANDOM;
+               goto done;
+       }
 
-               hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type);
+       /* LE address type */
+       addr_type = le_addr_type(cp->addr.type);
 
-               err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type);
-       }
+       hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type);
 
+       err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type);
        if (err < 0) {
                err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
                                        MGMT_STATUS_NOT_PAIRED, &rp,
@@ -3124,6 +3093,36 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
+       conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr, addr_type);
+       if (!conn) {
+               hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type);
+               goto done;
+       }
+
+       /* Abort any ongoing SMP pairing */
+       smp_cancel_pairing(conn);
+
+       /* Defer clearing up the connection parameters until closing to
+        * give a chance of keeping them if a repairing happens.
+        */
+       set_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
+
+       /* Disable auto-connection parameters if present */
+       params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, addr_type);
+       if (params) {
+               if (params->explicit_connect)
+                       params->auto_connect = HCI_AUTO_CONN_EXPLICIT;
+               else
+                       params->auto_connect = HCI_AUTO_CONN_DISABLED;
+       }
+
+       /* If disconnection is not requested, then clear the connection
+        * variable so that the link is not terminated.
+        */
+       if (!cp->disconnect)
+               conn = NULL;
+
+done:
        /* If the connection variable is set, then termination of the
         * link is requested.
         */
@@ -3143,9 +3142,7 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
 
        cmd->cmd_complete = addr_cmd_complete;
 
-       dc.handle = cpu_to_le16(conn->handle);
-       dc.reason = 0x13; /* Remote User Terminated Connection */
-       err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
+       err = hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
        if (err < 0)
                mgmt_pending_remove(cmd);
 
@@ -3193,7 +3190,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
                conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
                                               &cp->addr.bdaddr);
        else
-               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
+               conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
+                                              le_addr_type(cp->addr.type));
 
        if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) {
                err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
@@ -3544,16 +3542,9 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                conn = hci_connect_acl(hdev, &cp->addr.bdaddr, sec_level,
                                       auth_type);
        } else {
-               u8 addr_type;
+               u8 addr_type = le_addr_type(cp->addr.type);
                struct hci_conn_params *p;
 
-               /* Convert from L2CAP channel address type to HCI address type
-                */
-               if (cp->addr.type == BDADDR_LE_PUBLIC)
-                       addr_type = ADDR_LE_DEV_PUBLIC;
-               else
-                       addr_type = ADDR_LE_DEV_RANDOM;
-
                /* When pairing a new device, it is expected to remember
                 * this device for future connections. Adding the connection
                 * parameter information ahead of time allows tracking
@@ -3697,7 +3688,8 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
        if (addr->type == BDADDR_BREDR)
                conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &addr->bdaddr);
        else
-               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &addr->bdaddr);
+               conn = hci_conn_hash_lookup_le(hdev, &addr->bdaddr,
+                                              le_addr_type(addr->type));
 
        if (!conn) {
                err = mgmt_cmd_complete(sk, hdev->id, mgmt_op,
@@ -5600,14 +5592,9 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
 
        for (i = 0; i < irk_count; i++) {
                struct mgmt_irk_info *irk = &cp->irks[i];
-               u8 addr_type;
-
-               if (irk->addr.type == BDADDR_LE_PUBLIC)
-                       addr_type = ADDR_LE_DEV_PUBLIC;
-               else
-                       addr_type = ADDR_LE_DEV_RANDOM;
 
-               hci_add_irk(hdev, &irk->addr.bdaddr, addr_type, irk->val,
+               hci_add_irk(hdev, &irk->addr.bdaddr,
+                           le_addr_type(irk->addr.type), irk->val,
                            BDADDR_ANY);
        }
 
@@ -5687,12 +5674,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
 
        for (i = 0; i < key_count; i++) {
                struct mgmt_ltk_info *key = &cp->keys[i];
-               u8 type, addr_type, authenticated;
-
-               if (key->addr.type == BDADDR_LE_PUBLIC)
-                       addr_type = ADDR_LE_DEV_PUBLIC;
-               else
-                       addr_type = ADDR_LE_DEV_RANDOM;
+               u8 type, authenticated;
 
                switch (key->type) {
                case MGMT_LTK_UNAUTHENTICATED:
@@ -5718,9 +5700,9 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                        continue;
                }
 
-               hci_add_ltk(hdev, &key->addr.bdaddr, addr_type, type,
-                           authenticated, key->val, key->enc_size, key->ediv,
-                           key->rand);
+               hci_add_ltk(hdev, &key->addr.bdaddr,
+                           le_addr_type(key->addr.type), type, authenticated,
+                           key->val, key->enc_size, key->ediv, key->rand);
        }
 
        err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0,
@@ -6232,10 +6214,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
                goto added;
        }
 
-       if (cp->addr.type == BDADDR_LE_PUBLIC)
-               addr_type = ADDR_LE_DEV_PUBLIC;
-       else
-               addr_type = ADDR_LE_DEV_RANDOM;
+       addr_type = le_addr_type(cp->addr.type);
 
        if (cp->action == 0x02)
                auto_conn = HCI_AUTO_CONN_ALWAYS;
@@ -6364,10 +6343,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
                        goto complete;
                }
 
-               if (cp->addr.type == BDADDR_LE_PUBLIC)
-                       addr_type = ADDR_LE_DEV_PUBLIC;
-               else
-                       addr_type = ADDR_LE_DEV_RANDOM;
+               addr_type = le_addr_type(cp->addr.type);
 
                /* Kernel internally uses conn_params with resolvable private
                 * address, but Remove Device allows only identity addresses.
@@ -7873,27 +7849,13 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent)
        mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
 }
 
-void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk)
+void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk, bool persistent)
 {
        struct mgmt_ev_new_irk ev;
 
        memset(&ev, 0, sizeof(ev));
 
-       /* For identity resolving keys from devices that are already
-        * using a public address or static random address, do not
-        * ask for storing this key. The identity resolving key really
-        * is only mandatory for devices using resolvable random
-        * addresses.
-        *
-        * Storing all identity resolving keys has the downside that
-        * they will be also loaded on next boot of they system. More
-        * identity resolving keys, means more time during scanning is
-        * needed to actually resolve these addresses.
-        */
-       if (bacmp(&irk->rpa, BDADDR_ANY))
-               ev.store_hint = 0x01;
-       else
-               ev.store_hint = 0x00;
+       ev.store_hint = persistent;
 
        bacpy(&ev.rpa, &irk->rpa);
        bacpy(&ev.irk.addr.bdaddr, &irk->bdaddr);
index f315c8d0e43b594486cc3dd1be1433387601e7c3..fe129663bd3f7faeb39f20d02a8425d525da071d 100644 (file)
@@ -74,7 +74,7 @@ struct sco_pinfo {
 
 static void sco_sock_timeout(unsigned long arg)
 {
-       struct sock *sk = (struct sock *) arg;
+       struct sock *sk = (struct sock *)arg;
 
        BT_DBG("sock %p state %d", sk, sk->sk_state);
 
@@ -170,18 +170,21 @@ static void sco_conn_del(struct hci_conn *hcon, int err)
        sco_conn_unlock(conn);
 
        if (sk) {
+               sock_hold(sk);
                bh_lock_sock(sk);
                sco_sock_clear_timer(sk);
                sco_chan_del(sk, err);
                bh_unlock_sock(sk);
                sco_sock_kill(sk);
+               sock_put(sk);
        }
 
        hcon->sco_data = NULL;
        kfree(conn);
 }
 
-static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
+static void __sco_chan_add(struct sco_conn *conn, struct sock *sk,
+                          struct sock *parent)
 {
        BT_DBG("conn %p", conn);
 
@@ -414,8 +417,10 @@ static void __sco_sock_close(struct sock *sk)
                if (sco_pi(sk)->conn->hcon) {
                        sk->sk_state = BT_DISCONN;
                        sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT);
+                       sco_conn_lock(sco_pi(sk)->conn);
                        hci_conn_drop(sco_pi(sk)->conn->hcon);
                        sco_pi(sk)->conn->hcon = NULL;
+                       sco_conn_unlock(sco_pi(sk)->conn);
                } else
                        sco_chan_del(sk, ECONNRESET);
                break;
@@ -459,7 +464,8 @@ static struct proto sco_proto = {
        .obj_size       = sizeof(struct sco_pinfo)
 };
 
-static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio, int kern)
+static struct sock *sco_sock_alloc(struct net *net, struct socket *sock,
+                                  int proto, gfp_t prio, int kern)
 {
        struct sock *sk;
 
@@ -508,7 +514,8 @@ static int sco_sock_create(struct net *net, struct socket *sock, int protocol,
        return 0;
 }
 
-static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+static int sco_sock_bind(struct socket *sock, struct sockaddr *addr,
+                        int addr_len)
 {
        struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
        struct sock *sk = sock->sk;
@@ -615,7 +622,8 @@ done:
        return err;
 }
 
-static int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags)
+static int sco_sock_accept(struct socket *sock, struct socket *newsock,
+                          int flags)
 {
        DEFINE_WAIT_FUNC(wait, woken_wake_function);
        struct sock *sk = sock->sk, *ch;
@@ -669,7 +677,8 @@ done:
        return err;
 }
 
-static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
+static int sco_sock_getname(struct socket *sock, struct sockaddr *addr,
+                           int *len, int peer)
 {
        struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
        struct sock *sk = sock->sk;
@@ -779,7 +788,8 @@ static int sco_sock_recvmsg(struct socket *sock, struct msghdr *msg,
        return bt_sock_recvmsg(sock, msg, len, flags);
 }
 
-static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
+static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
+                              char __user *optval, unsigned int optlen)
 {
        struct sock *sk = sock->sk;
        int len, err = 0;
@@ -819,7 +829,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char
                voice.setting = sco_pi(sk)->setting;
 
                len = min_t(unsigned int, sizeof(voice), optlen);
-               if (copy_from_user((char *) &voice, optval, len)) {
+               if (copy_from_user((char *)&voice, optval, len)) {
                        err = -EFAULT;
                        break;
                }
@@ -843,7 +853,8 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char
        return err;
 }
 
-static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
+static int sco_sock_getsockopt_old(struct socket *sock, int optname,
+                                  char __user *optval, int __user *optlen)
 {
        struct sock *sk = sock->sk;
        struct sco_options opts;
@@ -903,7 +914,8 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user
        return err;
 }
 
-static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
+static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
+                              char __user *optval, int __user *optlen)
 {
        struct sock *sk = sock->sk;
        int len, err = 0;
@@ -928,7 +940,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
                }
 
                if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
-                            (u32 __user *) optval))
+                            (u32 __user *)optval))
                        err = -EFAULT;
 
                break;
@@ -961,7 +973,9 @@ static int sco_sock_shutdown(struct socket *sock, int how)
        if (!sk)
                return 0;
 
+       sock_hold(sk);
        lock_sock(sk);
+
        if (!sk->sk_shutdown) {
                sk->sk_shutdown = SHUTDOWN_MASK;
                sco_sock_clear_timer(sk);
@@ -972,7 +986,10 @@ static int sco_sock_shutdown(struct socket *sock, int how)
                        err = bt_sock_wait_state(sk, BT_CLOSED,
                                                 sk->sk_lingertime);
        }
+
        release_sock(sk);
+       sock_put(sk);
+
        return err;
 }
 
@@ -1016,6 +1033,11 @@ static void sco_conn_ready(struct sco_conn *conn)
        } else {
                sco_conn_lock(conn);
 
+               if (!conn->hcon) {
+                       sco_conn_unlock(conn);
+                       return;
+               }
+
                parent = sco_get_sock_listen(&conn->hcon->src);
                if (!parent) {
                        sco_conn_unlock(conn);
index 0510a577a7b58ef7dfbace4a68d712a3b8239891..c91353841e40500790c13d2ce894460e1cbbe9e3 100644 (file)
@@ -495,7 +495,7 @@ static int smp_ah(struct crypto_blkcipher *tfm, const u8 irk[16],
        }
 
        /* The output of the random address function ah is:
-        *      ah(h, r) = e(k, r') mod 2^24
+        *      ah(k, r) = e(k, r') mod 2^24
         * The output of the security function e is then truncated to 24 bits
         * by taking the least significant 24 bits of the output of e as the
         * result of ah.
@@ -811,7 +811,6 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason)
                smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
                             &reason);
 
-       clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags);
        mgmt_auth_failed(hcon, HCI_ERROR_AUTH_FAILURE);
 
        if (chan->data)
@@ -1046,8 +1045,24 @@ static void smp_notify_keys(struct l2cap_conn *conn)
        struct smp_cmd_pairing *rsp = (void *) &smp->prsp[1];
        bool persistent;
 
+       if (hcon->type == ACL_LINK) {
+               if (hcon->key_type == HCI_LK_DEBUG_COMBINATION)
+                       persistent = false;
+               else
+                       persistent = !test_bit(HCI_CONN_FLUSH_KEY,
+                                              &hcon->flags);
+       } else {
+               /* The LTKs, IRKs and CSRKs should be persistent only if
+                * both sides had the bonding bit set in their
+                * authentication requests.
+                */
+               persistent = !!((req->auth_req & rsp->auth_req) &
+                               SMP_AUTH_BONDING);
+       }
+
        if (smp->remote_irk) {
-               mgmt_new_irk(hdev, smp->remote_irk);
+               mgmt_new_irk(hdev, smp->remote_irk, persistent);
+
                /* Now that user space can be considered to know the
                 * identity address track the connection based on it
                 * from now on (assuming this is an LE link).
@@ -1075,21 +1090,6 @@ static void smp_notify_keys(struct l2cap_conn *conn)
                }
        }
 
-       if (hcon->type == ACL_LINK) {
-               if (hcon->key_type == HCI_LK_DEBUG_COMBINATION)
-                       persistent = false;
-               else
-                       persistent = !test_bit(HCI_CONN_FLUSH_KEY,
-                                              &hcon->flags);
-       } else {
-               /* The LTKs and CSRKs should be persistent only if both sides
-                * had the bonding bit set in their authentication requests.
-                */
-               persistent = !!((req->auth_req & rsp->auth_req) &
-                               SMP_AUTH_BONDING);
-       }
-
-
        if (smp->csrk) {
                smp->csrk->bdaddr_type = hcon->dst_type;
                bacpy(&smp->csrk->bdaddr, &hcon->dst);
@@ -2380,6 +2380,32 @@ unlock:
        return ret;
 }
 
+void smp_cancel_pairing(struct hci_conn *hcon)
+{
+       struct l2cap_conn *conn = hcon->l2cap_data;
+       struct l2cap_chan *chan;
+       struct smp_chan *smp;
+
+       if (!conn)
+               return;
+
+       chan = conn->smp;
+       if (!chan)
+               return;
+
+       l2cap_chan_lock(chan);
+
+       smp = chan->data;
+       if (smp) {
+               if (test_bit(SMP_FLAG_COMPLETE, &smp->flags))
+                       smp_failure(conn, 0);
+               else
+                       smp_failure(conn, SMP_UNSPECIFIED);
+       }
+
+       l2cap_chan_unlock(chan);
+}
+
 static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_encrypt_info *rp = (void *) skb->data;
index 6cf872563ea71d425dd1798314765edb56d39a51..ffcc70b6b1997d3d109aeab142738a77ee31ceb9 100644 (file)
@@ -180,6 +180,7 @@ enum smp_key_pref {
 };
 
 /* SMP Commands */
+void smp_cancel_pairing(struct hci_conn *hcon);
 bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
                             enum smp_key_pref key_pref);
 int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
index 6ed2feb51e3c7ae3d0f0fd629d6e147549b5fa54..5e88d3e17546618a728bb429fc61e20d5f8d79dc 100644 (file)
@@ -56,7 +56,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
        skb_reset_mac_header(skb);
        skb_pull(skb, ETH_HLEN);
 
-       if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid))
+       if (!br_allowed_ingress(br, br_vlan_group_rcu(br), skb, &vid))
                goto out;
 
        if (is_broadcast_ether_addr(dest))
@@ -391,7 +391,7 @@ void br_dev_setup(struct net_device *dev)
        br->bridge_max_age = br->max_age = 20 * HZ;
        br->bridge_hello_time = br->hello_time = 2 * HZ;
        br->bridge_forward_delay = br->forward_delay = 15 * HZ;
-       br->ageing_time = 300 * HZ;
+       br->ageing_time = BR_DEFAULT_AGEING_TIME;
 
        br_netfilter_rtable_init(br);
        br_stp_timer_init(br);
index 9e9875da0a4f979c5389f616b432d218b19fd776..a642bb829d09cfd473d5ad17a64b2ba6b76d912e 100644 (file)
@@ -133,15 +133,16 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr)
 
 static void fdb_del_external_learn(struct net_bridge_fdb_entry *f)
 {
-       struct switchdev_obj obj = {
-               .id = SWITCHDEV_OBJ_PORT_FDB,
-               .u.fdb = {
-                       .addr = f->addr.addr,
-                       .vid = f->vlan_id,
+       struct switchdev_obj_port_fdb fdb = {
+               .obj = {
+                       .id = SWITCHDEV_OBJ_ID_PORT_FDB,
+                       .flags = SWITCHDEV_F_DEFER,
                },
+               .vid = f->vlan_id,
        };
 
-       switchdev_port_obj_del(f->dst->dev, &obj);
+       ether_addr_copy(fdb.addr, f->addr.addr);
+       switchdev_port_obj_del(f->dst->dev, &fdb.obj);
 }
 
 static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
@@ -163,22 +164,27 @@ static void fdb_delete_local(struct net_bridge *br,
                             struct net_bridge_fdb_entry *f)
 {
        const unsigned char *addr = f->addr.addr;
-       u16 vid = f->vlan_id;
+       struct net_bridge_vlan_group *vg;
+       const struct net_bridge_vlan *v;
        struct net_bridge_port *op;
+       u16 vid = f->vlan_id;
 
        /* Maybe another port has same hw addr? */
        list_for_each_entry(op, &br->port_list, list) {
+               vg = nbp_vlan_group(op);
                if (op != p && ether_addr_equal(op->dev->dev_addr, addr) &&
-                   (!vid || nbp_vlan_find(op, vid))) {
+                   (!vid || br_vlan_find(vg, vid))) {
                        f->dst = op;
                        f->added_by_user = 0;
                        return;
                }
        }
 
+       vg = br_vlan_group(br);
+       v = br_vlan_find(vg, vid);
        /* Maybe bridge device has same hw addr? */
        if (p && ether_addr_equal(br->dev->dev_addr, addr) &&
-           (!vid || br_vlan_find(br, vid))) {
+           (!vid || (v && br_vlan_should_use(v)))) {
                f->dst = NULL;
                f->added_by_user = 0;
                return;
@@ -203,14 +209,14 @@ void br_fdb_find_delete_local(struct net_bridge *br,
 
 void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
 {
+       struct net_bridge_vlan_group *vg;
        struct net_bridge *br = p->br;
-       struct net_port_vlans *pv = nbp_get_vlan_info(p);
-       bool no_vlan = !pv;
+       struct net_bridge_vlan *v;
        int i;
-       u16 vid;
 
        spin_lock_bh(&br->hash_lock);
 
+       vg = nbp_vlan_group(p);
        /* Search all chains since old address/hash is unknown */
        for (i = 0; i < BR_HASH_SIZE; i++) {
                struct hlist_node *h;
@@ -226,7 +232,7 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
                                 * configured, we can safely be done at
                                 * this point.
                                 */
-                               if (no_vlan)
+                               if (!vg || !vg->num_vlans)
                                        goto insert;
                        }
                }
@@ -236,15 +242,15 @@ insert:
        /* insert new address,  may fail if invalid address or dup. */
        fdb_insert(br, p, newaddr, 0);
 
-       if (no_vlan)
+       if (!vg || !vg->num_vlans)
                goto done;
 
        /* Now add entries for every VLAN configured on the port.
         * This function runs under RTNL so the bitmap will not change
         * from under us.
         */
-       for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
-               fdb_insert(br, p, newaddr, vid);
+       list_for_each_entry(v, &vg->vlan_list, vlist)
+               fdb_insert(br, p, newaddr, v->vid);
 
 done:
        spin_unlock_bh(&br->hash_lock);
@@ -252,9 +258,9 @@ done:
 
 void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
 {
+       struct net_bridge_vlan_group *vg;
        struct net_bridge_fdb_entry *f;
-       struct net_port_vlans *pv;
-       u16 vid = 0;
+       struct net_bridge_vlan *v;
 
        spin_lock_bh(&br->hash_lock);
 
@@ -264,20 +270,18 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
                fdb_delete_local(br, NULL, f);
 
        fdb_insert(br, NULL, newaddr, 0);
-
+       vg = br_vlan_group(br);
+       if (!vg || !vg->num_vlans)
+               goto out;
        /* Now remove and add entries for every VLAN configured on the
         * bridge.  This function runs under RTNL so the bitmap will not
         * change from under us.
         */
-       pv = br_get_vlan_info(br);
-       if (!pv)
-               goto out;
-
-       for_each_set_bit_from(vid, pv->vlan_bitmap, VLAN_N_VID) {
-               f = __br_fdb_get(br, br->dev->dev_addr, vid);
+       list_for_each_entry(v, &vg->vlan_list, vlist) {
+               f = __br_fdb_get(br, br->dev->dev_addr, v->vid);
                if (f && f->is_local && !f->dst)
                        fdb_delete_local(br, NULL, f);
-               fdb_insert(br, NULL, newaddr, vid);
+               fdb_insert(br, NULL, newaddr, v->vid);
        }
 out:
        spin_unlock_bh(&br->hash_lock);
@@ -299,6 +303,8 @@ void br_fdb_cleanup(unsigned long _data)
                        unsigned long this_timer;
                        if (f->is_static)
                                continue;
+                       if (f->added_by_external_learn)
+                               continue;
                        this_timer = f->updated + delay;
                        if (time_before_eq(this_timer, jiffies))
                                fdb_delete(br, f);
@@ -489,7 +495,9 @@ static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head,
 static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
                                               struct net_bridge_port *source,
                                               const unsigned char *addr,
-                                              __u16 vid)
+                                              __u16 vid,
+                                              unsigned char is_local,
+                                              unsigned char is_static)
 {
        struct net_bridge_fdb_entry *fdb;
 
@@ -498,8 +506,8 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
                memcpy(fdb->addr.addr, addr, ETH_ALEN);
                fdb->dst = source;
                fdb->vlan_id = vid;
-               fdb->is_local = 0;
-               fdb->is_static = 0;
+               fdb->is_local = is_local;
+               fdb->is_static = is_static;
                fdb->added_by_user = 0;
                fdb->added_by_external_learn = 0;
                fdb->updated = fdb->used = jiffies;
@@ -530,11 +538,10 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
                fdb_delete(br, fdb);
        }
 
-       fdb = fdb_create(head, source, addr, vid);
+       fdb = fdb_create(head, source, addr, vid, 1, 1);
        if (!fdb)
                return -ENOMEM;
 
-       fdb->is_local = fdb->is_static = 1;
        fdb_add_hw_addr(br, addr);
        fdb_notify(br, fdb, RTM_NEWNEIGH);
        return 0;
@@ -591,7 +598,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
        } else {
                spin_lock(&br->hash_lock);
                if (likely(!fdb_find(head, addr, vid))) {
-                       fdb = fdb_create(head, source, addr, vid);
+                       fdb = fdb_create(head, source, addr, vid, 0, 0);
                        if (fdb) {
                                if (unlikely(added_by_user))
                                        fdb->added_by_user = 1;
@@ -605,13 +612,14 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
        }
 }
 
-static int fdb_to_nud(const struct net_bridge_fdb_entry *fdb)
+static int fdb_to_nud(const struct net_bridge *br,
+                     const struct net_bridge_fdb_entry *fdb)
 {
        if (fdb->is_local)
                return NUD_PERMANENT;
        else if (fdb->is_static)
                return NUD_NOARP;
-       else if (has_expired(fdb->dst->br, fdb))
+       else if (has_expired(br, fdb))
                return NUD_STALE;
        else
                return NUD_REACHABLE;
@@ -637,7 +645,7 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
        ndm->ndm_flags   = fdb->added_by_external_learn ? NTF_EXT_LEARNED : 0;
        ndm->ndm_type    = 0;
        ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex;
-       ndm->ndm_state   = fdb_to_nud(fdb);
+       ndm->ndm_state   = fdb_to_nud(br, fdb);
 
        if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr))
                goto nla_put_failure;
@@ -767,7 +775,7 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
                if (!(flags & NLM_F_CREATE))
                        return -ENOENT;
 
-               fdb = fdb_create(head, source, addr, vid);
+               fdb = fdb_create(head, source, addr, vid, 0, 0);
                if (!fdb)
                        return -ENOMEM;
 
@@ -782,7 +790,7 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
                }
        }
 
-       if (fdb_to_nud(fdb) != state) {
+       if (fdb_to_nud(br, fdb) != state) {
                if (state & NUD_PERMANENT) {
                        fdb->is_local = 1;
                        if (!fdb->is_static) {
@@ -842,9 +850,11 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
               struct net_device *dev,
               const unsigned char *addr, u16 vid, u16 nlh_flags)
 {
-       struct net_bridge_port *p;
+       struct net_bridge_vlan_group *vg;
+       struct net_bridge_port *p = NULL;
+       struct net_bridge_vlan *v;
+       struct net_bridge *br = NULL;
        int err = 0;
-       struct net_port_vlans *pv;
 
        if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) {
                pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state);
@@ -856,34 +866,51 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
                return -EINVAL;
        }
 
-       p = br_port_get_rtnl(dev);
-       if (p == NULL) {
-               pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n",
-                       dev->name);
-               return -EINVAL;
+       if (dev->priv_flags & IFF_EBRIDGE) {
+               br = netdev_priv(dev);
+               vg = br_vlan_group(br);
+       } else {
+               p = br_port_get_rtnl(dev);
+               if (!p) {
+                       pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n",
+                               dev->name);
+                       return -EINVAL;
+               }
+               vg = nbp_vlan_group(p);
        }
 
-       pv = nbp_get_vlan_info(p);
        if (vid) {
-               if (!pv || !test_bit(vid, pv->vlan_bitmap)) {
-                       pr_info("bridge: RTM_NEWNEIGH with unconfigured "
-                               "vlan %d on port %s\n", vid, dev->name);
+               v = br_vlan_find(vg, vid);
+               if (!v || !br_vlan_should_use(v)) {
+                       pr_info("bridge: RTM_NEWNEIGH with unconfigured vlan %d on %s\n", vid, dev->name);
                        return -EINVAL;
                }
 
                /* VID was specified, so use it. */
-               err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
+               if (dev->priv_flags & IFF_EBRIDGE)
+                       err = br_fdb_insert(br, NULL, addr, vid);
+               else
+                       err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
        } else {
-               err = __br_fdb_add(ndm, p, addr, nlh_flags, 0);
-               if (err || !pv)
+               if (dev->priv_flags & IFF_EBRIDGE)
+                       err = br_fdb_insert(br, NULL, addr, 0);
+               else
+                       err = __br_fdb_add(ndm, p, addr, nlh_flags, 0);
+               if (err || !vg || !vg->num_vlans)
                        goto out;
 
                /* We have vlans configured on this port and user didn't
                 * specify a VLAN.  To be nice, add/update entry for every
                 * vlan on this port.
                 */
-               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
-                       err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
+               list_for_each_entry(v, &vg->vlan_list, vlist) {
+                       if (!br_vlan_should_use(v))
+                               continue;
+                       if (dev->priv_flags & IFF_EBRIDGE)
+                               err = br_fdb_insert(br, NULL, addr, v->vid);
+                       else
+                               err = __br_fdb_add(ndm, p, addr, nlh_flags,
+                                                  v->vid);
                        if (err)
                                goto out;
                }
@@ -893,6 +920,32 @@ out:
        return err;
 }
 
+static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr,
+                             u16 vid)
+{
+       struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)];
+       struct net_bridge_fdb_entry *fdb;
+
+       fdb = fdb_find(head, addr, vid);
+       if (!fdb)
+               return -ENOENT;
+
+       fdb_delete(br, fdb);
+       return 0;
+}
+
+static int __br_fdb_delete_by_addr(struct net_bridge *br,
+                                  const unsigned char *addr, u16 vid)
+{
+       int err;
+
+       spin_lock_bh(&br->hash_lock);
+       err = fdb_delete_by_addr(br, addr, vid);
+       spin_unlock_bh(&br->hash_lock);
+
+       return err;
+}
+
 static int fdb_delete_by_addr_and_port(struct net_bridge_port *p,
                                       const u8 *addr, u16 vlan)
 {
@@ -925,38 +978,53 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
                  struct net_device *dev,
                  const unsigned char *addr, u16 vid)
 {
-       struct net_bridge_port *p;
+       struct net_bridge_vlan_group *vg;
+       struct net_bridge_port *p = NULL;
+       struct net_bridge_vlan *v;
+       struct net_bridge *br = NULL;
        int err;
-       struct net_port_vlans *pv;
 
-       p = br_port_get_rtnl(dev);
-       if (p == NULL) {
-               pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n",
-                       dev->name);
-               return -EINVAL;
+       if (dev->priv_flags & IFF_EBRIDGE) {
+               br = netdev_priv(dev);
+               vg = br_vlan_group(br);
+       } else {
+               p = br_port_get_rtnl(dev);
+               if (!p) {
+                       pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n",
+                               dev->name);
+                       return -EINVAL;
+               }
+               vg = nbp_vlan_group(p);
        }
 
-       pv = nbp_get_vlan_info(p);
        if (vid) {
-               if (!pv || !test_bit(vid, pv->vlan_bitmap)) {
-                       pr_info("bridge: RTM_DELNEIGH with unconfigured "
-                               "vlan %d on port %s\n", vid, dev->name);
+               v = br_vlan_find(vg, vid);
+               if (!v) {
+                       pr_info("bridge: RTM_DELNEIGH with unconfigured vlan %d on %s\n", vid, dev->name);
                        return -EINVAL;
                }
 
-               err = __br_fdb_delete(p, addr, vid);
+               if (dev->priv_flags & IFF_EBRIDGE)
+                       err = __br_fdb_delete_by_addr(br, addr, vid);
+               else
+                       err = __br_fdb_delete(p, addr, vid);
        } else {
                err = -ENOENT;
-               err &= __br_fdb_delete(p, addr, 0);
-               if (!pv)
+               if (dev->priv_flags & IFF_EBRIDGE)
+                       err = __br_fdb_delete_by_addr(br, addr, 0);
+               else
+                       err &= __br_fdb_delete(p, addr, 0);
+
+               if (!vg || !vg->num_vlans)
                        goto out;
 
-               /* We have vlans configured on this port and user didn't
-                * specify a VLAN.  To be nice, add/update entry for every
-                * vlan on this port.
-                */
-               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
-                       err &= __br_fdb_delete(p, addr, vid);
+               list_for_each_entry(v, &vg->vlan_list, vlist) {
+                       if (!br_vlan_should_use(v))
+                               continue;
+                       if (dev->priv_flags & IFF_EBRIDGE)
+                               err = __br_fdb_delete_by_addr(br, addr, v->vid);
+                       else
+                               err &= __br_fdb_delete(p, addr, v->vid);
                }
        }
 out:
@@ -1032,7 +1100,7 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
        head = &br->hash[br_mac_hash(addr, vid)];
        fdb = fdb_find(head, addr, vid);
        if (!fdb) {
-               fdb = fdb_create(head, p, addr, vid);
+               fdb = fdb_create(head, p, addr, vid, 0, 0);
                if (!fdb) {
                        err = -ENOMEM;
                        goto err_unlock;
index fa7bfced888ec0f3b7d8a5ceff17b597cbe9b79e..fcdb86dd5a239cceb0fb79d8e422f6fc56d14f0a 100644 (file)
@@ -30,12 +30,14 @@ static int deliver_clone(const struct net_bridge_port *prev,
 static inline int should_deliver(const struct net_bridge_port *p,
                                 const struct sk_buff *skb)
 {
+       struct net_bridge_vlan_group *vg;
+
+       vg = nbp_vlan_group_rcu(p);
        return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
-               br_allowed_egress(p->br, nbp_get_vlan_info(p), skb) &&
-               p->state == BR_STATE_FORWARDING;
+               br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING;
 }
 
-int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb)
+int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        if (!is_skb_forwardable(skb->dev, skb))
                goto drop;
@@ -65,10 +67,10 @@ drop:
 }
 EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit);
 
-int br_forward_finish(struct sock *sk, struct sk_buff *skb)
+int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-       return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, sk, skb,
-                      NULL, skb->dev,
+       return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING,
+                      net, sk, skb, NULL, skb->dev,
                       br_dev_queue_push_xmit);
 
 }
@@ -76,7 +78,10 @@ EXPORT_SYMBOL_GPL(br_forward_finish);
 
 static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
 {
-       skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb);
+       struct net_bridge_vlan_group *vg;
+
+       vg = nbp_vlan_group_rcu(to);
+       skb = br_handle_vlan(to->br, vg, skb);
        if (!skb)
                return;
 
@@ -92,13 +97,14 @@ static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
                return;
        }
 
-       NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb,
-               NULL, skb->dev,
+       NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
+               dev_net(skb->dev), NULL, skb,NULL, skb->dev,
                br_forward_finish);
 }
 
 static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
 {
+       struct net_bridge_vlan_group *vg;
        struct net_device *indev;
 
        if (skb_warn_if_lro(skb)) {
@@ -106,7 +112,8 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
                return;
        }
 
-       skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb);
+       vg = nbp_vlan_group_rcu(to);
+       skb = br_handle_vlan(to->br, vg, skb);
        if (!skb)
                return;
 
@@ -114,8 +121,8 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
        skb->dev = to->dev;
        skb_forward_csum(skb);
 
-       NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, NULL, skb,
-               indev, skb->dev,
+       NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD,
+               dev_net(indev), NULL, skb, indev, skb->dev,
                br_forward_finish);
 }
 
@@ -134,7 +141,7 @@ EXPORT_SYMBOL_GPL(br_deliver);
 /* called with rcu_read_lock */
 void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0)
 {
-       if (should_deliver(to, skb)) {
+       if (to && should_deliver(to, skb)) {
                if (skb0)
                        deliver_clone(to, skb, __br_forward);
                else
index 45e4757c6fd25ed05a14e24e2d026204c3e79506..ec02f5869a780246dd22030dfe66efa6d5e5f8da 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <net/sock.h>
 #include <linux/if_vlan.h>
+#include <net/switchdev.h>
 
 #include "br_private.h"
 
@@ -250,6 +251,8 @@ static void del_nbp(struct net_bridge_port *p)
 
        nbp_vlan_flush(p);
        br_fdb_delete_by_port(br, p, 0, 1);
+       switchdev_deferred_process();
+
        nbp_update_port_count(br);
 
        netdev_upper_dev_unlink(dev, br->dev);
index f921a5dce22dadf465b79dd93be733707d5d5d0d..f7fba74108a9377b12610c9cdfa033dc8c7c92a0 100644 (file)
 br_should_route_hook_t __rcu *br_should_route_hook __read_mostly;
 EXPORT_SYMBOL(br_should_route_hook);
 
+static int
+br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb)
+{
+       return netif_receive_skb(skb);
+}
+
 static int br_pass_frame_up(struct sk_buff *skb)
 {
        struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev;
        struct net_bridge *br = netdev_priv(brdev);
+       struct net_bridge_vlan_group *vg;
        struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats);
-       struct net_port_vlans *pv;
 
        u64_stats_update_begin(&brstats->syncp);
        brstats->rx_packets++;
        brstats->rx_bytes += skb->len;
        u64_stats_update_end(&brstats->syncp);
 
+       vg = br_vlan_group_rcu(br);
        /* Bridge is just like any other port.  Make sure the
         * packet is allowed except in promisc modue when someone
         * may be running packet capture.
         */
-       pv = br_get_vlan_info(br);
        if (!(brdev->flags & IFF_PROMISC) &&
-           !br_allowed_egress(br, pv, skb)) {
+           !br_allowed_egress(vg, skb)) {
                kfree_skb(skb);
                return NET_RX_DROP;
        }
 
        indev = skb->dev;
        skb->dev = brdev;
-       skb = br_handle_vlan(br, pv, skb);
+       skb = br_handle_vlan(br, vg, skb);
        if (!skb)
                return NET_RX_DROP;
 
-       return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, NULL, skb,
-                      indev, NULL,
-                      netif_receive_skb_sk);
+       return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
+                      dev_net(indev), NULL, skb, indev, NULL,
+                      br_netif_receive_skb);
 }
 
 static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br,
@@ -120,7 +126,7 @@ static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br,
 }
 
 /* note: already called with rcu_read_lock */
-int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb)
+int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        const unsigned char *dest = eth_hdr(skb)->h_dest;
        struct net_bridge_port *p = br_port_get_rcu(skb->dev);
@@ -134,7 +140,7 @@ int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb)
        if (!p || p->state == BR_STATE_DISABLED)
                goto drop;
 
-       if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid))
+       if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid))
                goto out;
 
        /* insert into forwarding database after filtering to avoid spoofing */
@@ -208,7 +214,7 @@ drop:
 EXPORT_SYMBOL_GPL(br_handle_frame_finish);
 
 /* note: already called with rcu_read_lock */
-static int br_handle_local_finish(struct sock *sk, struct sk_buff *skb)
+static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct net_bridge_port *p = br_port_get_rcu(skb->dev);
        u16 vid = 0;
@@ -278,8 +284,9 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
                }
 
                /* Deliver packet to local host only */
-               if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, NULL, skb,
-                           skb->dev, NULL, br_handle_local_finish)) {
+               if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
+                           dev_net(skb->dev), NULL, skb, skb->dev, NULL,
+                           br_handle_local_finish)) {
                        return RX_HANDLER_CONSUMED; /* consumed by filter */
                } else {
                        *pskb = skb;
@@ -303,8 +310,8 @@ forward:
                if (ether_addr_equal(p->br->dev->dev_addr, dest))
                        skb->pkt_type = PACKET_HOST;
 
-               NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, NULL, skb,
-                       skb->dev, NULL,
+               NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
+                       dev_net(skb->dev), NULL, skb, skb->dev, NULL,
                        br_handle_frame_finish);
                break;
        default:
index 8d423bc649b9c4a2829499dffb0ba88d952af128..263b4de4de57ccf4beddbfc48e62967236450b9b 100644 (file)
@@ -200,8 +200,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
                        return -EPERM;
 
-               br->ageing_time = clock_t_to_jiffies(args[1]);
-               return 0;
+               return br_set_ageing_time(br, args[1]);
 
        case BRCTL_GET_PORT_INFO:
        {
index d747275fad18826f96c604505f0583049c456f91..cd8deea2d074e6af6172968c60cdd4128d40b16c 100644 (file)
@@ -464,11 +464,11 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br,
 static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
-       unsigned short vid = VLAN_N_VID;
+       struct net_bridge_vlan_group *vg;
        struct net_device *dev, *pdev;
        struct br_mdb_entry *entry;
        struct net_bridge_port *p;
-       struct net_port_vlans *pv;
+       struct net_bridge_vlan *v;
        struct net_bridge *br;
        int err;
 
@@ -489,10 +489,10 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
        if (!p || p->br != br || p->state == BR_STATE_DISABLED)
                return -EINVAL;
 
-       pv = nbp_get_vlan_info(p);
-       if (br_vlan_enabled(br) && pv && entry->vid == 0) {
-               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
-                       entry->vid = vid;
+       vg = nbp_vlan_group(p);
+       if (br_vlan_enabled(br) && vg && entry->vid == 0) {
+               list_for_each_entry(v, &vg->vlan_list, vlist) {
+                       entry->vid = v->vid;
                        err = __br_mdb_add(net, br, entry);
                        if (err)
                                break;
@@ -566,11 +566,11 @@ unlock:
 static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
-       unsigned short vid = VLAN_N_VID;
+       struct net_bridge_vlan_group *vg;
        struct net_device *dev, *pdev;
        struct br_mdb_entry *entry;
        struct net_bridge_port *p;
-       struct net_port_vlans *pv;
+       struct net_bridge_vlan *v;
        struct net_bridge *br;
        int err;
 
@@ -591,10 +591,10 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
        if (!p || p->br != br || p->state == BR_STATE_DISABLED)
                return -EINVAL;
 
-       pv = nbp_get_vlan_info(p);
-       if (br_vlan_enabled(br) && pv && entry->vid == 0) {
-               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
-                       entry->vid = vid;
+       vg = nbp_vlan_group(p);
+       if (br_vlan_enabled(br) && vg && entry->vid == 0) {
+               list_for_each_entry(v, &vg->vlan_list, vlist) {
+                       entry->vid = v->vid;
                        err = __br_mdb_del(br, entry);
                        if (!err)
                                __br_mdb_notify(dev, entry, RTM_DELMDB);
index 480b3de1a0e3e158a0968355902a8da4764b5d4f..03661d97463c0a185b6c688ee281309682209528 100644 (file)
@@ -829,8 +829,8 @@ static void __br_multicast_send_query(struct net_bridge *br,
 
        if (port) {
                skb->dev = port->dev;
-               NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb,
-                       NULL, skb->dev,
+               NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
+                       dev_net(port->dev), NULL, skb, NULL, skb->dev,
                        br_dev_queue_push_xmit);
        } else {
                br_multicast_select_own_querier(br, ip, skb);
index 0a6f095bb0c9eef4cc6e6b543db0b0ba3967d67d..7ddbe7ec81d61d4971b919c5988e7bed93436dec 100644 (file)
@@ -111,7 +111,6 @@ static inline __be16 pppoe_proto(const struct sk_buff *skb)
 /* largest possible L2 header, see br_nf_dev_queue_xmit() */
 #define NF_BRIDGE_MAX_MAC_HEADER_LENGTH (PPPOE_SES_HLEN + ETH_HLEN)
 
-#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) || IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
 struct brnf_frag_data {
        char mac[NF_BRIDGE_MAX_MAC_HEADER_LENGTH];
        u8 encap_size;
@@ -121,7 +120,6 @@ struct brnf_frag_data {
 };
 
 static DEFINE_PER_CPU(struct brnf_frag_data, brnf_frag_data_storage);
-#endif
 
 static void nf_bridge_info_free(struct sk_buff *skb)
 {
@@ -189,10 +187,9 @@ static inline void nf_bridge_pull_encap_header_rcsum(struct sk_buff *skb)
  * expected format
  */
 
-static int br_validate_ipv4(struct sk_buff *skb)
+static int br_validate_ipv4(struct net *net, struct sk_buff *skb)
 {
        const struct iphdr *iph;
-       struct net_device *dev = skb->dev;
        u32 len;
 
        if (!pskb_may_pull(skb, sizeof(struct iphdr)))
@@ -213,13 +210,13 @@ static int br_validate_ipv4(struct sk_buff *skb)
 
        len = ntohs(iph->tot_len);
        if (skb->len < len) {
-               IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);
+               IP_INC_STATS_BH(net, IPSTATS_MIB_INTRUNCATEDPKTS);
                goto drop;
        } else if (len < (iph->ihl*4))
                goto inhdr_error;
 
        if (pskb_trim_rcsum(skb, len)) {
-               IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
+               IP_INC_STATS_BH(net, IPSTATS_MIB_INDISCARDS);
                goto drop;
        }
 
@@ -232,7 +229,7 @@ static int br_validate_ipv4(struct sk_buff *skb)
        return 0;
 
 inhdr_error:
-       IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);
+       IP_INC_STATS_BH(net, IPSTATS_MIB_INHDRERRORS);
 drop:
        return -1;
 }
@@ -256,7 +253,7 @@ void nf_bridge_update_protocol(struct sk_buff *skb)
  * don't, we use the neighbour framework to find out. In both cases, we make
  * sure that br_handle_frame_finish() is called afterwards.
  */
-int br_nf_pre_routing_finish_bridge(struct sock *sk, struct sk_buff *skb)
+int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct neighbour *neigh;
        struct dst_entry *dst;
@@ -273,7 +270,7 @@ int br_nf_pre_routing_finish_bridge(struct sock *sk, struct sk_buff *skb)
                if (neigh->hh.hh_len) {
                        neigh_hh_bridge(&neigh->hh, skb);
                        skb->dev = nf_bridge->physindev;
-                       ret = br_handle_frame_finish(sk, skb);
+                       ret = br_handle_frame_finish(net, sk, skb);
                } else {
                        /* the neighbour function below overwrites the complete
                         * MAC header, so we save the Ethernet source address and
@@ -342,7 +339,7 @@ br_nf_ipv4_daddr_was_changed(const struct sk_buff *skb,
  * device, we proceed as if ip_route_input() succeeded. If it differs from the
  * logical bridge port or if ip_route_output_key() fails we drop the packet.
  */
-static int br_nf_pre_routing_finish(struct sock *sk, struct sk_buff *skb)
+static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct net_device *dev = skb->dev;
        struct iphdr *iph = ip_hdr(skb);
@@ -371,7 +368,7 @@ static int br_nf_pre_routing_finish(struct sock *sk, struct sk_buff *skb)
                        if (err != -EHOSTUNREACH || !in_dev || IN_DEV_FORWARD(in_dev))
                                goto free_skb;
 
-                       rt = ip_route_output(dev_net(dev), iph->daddr, 0,
+                       rt = ip_route_output(net, iph->daddr, 0,
                                             RT_TOS(iph->tos), 0);
                        if (!IS_ERR(rt)) {
                                /* - Bridged-and-DNAT'ed traffic doesn't
@@ -393,7 +390,7 @@ bridged_dnat:
                                nf_bridge_push_encap_header(skb);
                                NF_HOOK_THRESH(NFPROTO_BRIDGE,
                                               NF_BR_PRE_ROUTING,
-                                              sk, skb, skb->dev, NULL,
+                                              net, sk, skb, skb->dev, NULL,
                                               br_nf_pre_routing_finish_bridge,
                                               1);
                                return 0;
@@ -413,7 +410,7 @@ bridged_dnat:
        skb->dev = nf_bridge->physindev;
        nf_bridge_update_protocol(skb);
        nf_bridge_push_encap_header(skb);
-       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
+       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, net, sk, skb,
                       skb->dev, NULL,
                       br_handle_frame_finish, 1);
 
@@ -464,7 +461,7 @@ struct net_device *setup_pre_routing(struct sk_buff *skb)
  * receiving device) to make netfilter happy, the REDIRECT
  * target in particular.  Save the original destination IP
  * address to be able to detect DNAT afterwards. */
-static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops,
+static unsigned int br_nf_pre_routing(void *priv,
                                      struct sk_buff *skb,
                                      const struct nf_hook_state *state)
 {
@@ -486,7 +483,7 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops,
                        return NF_ACCEPT;
 
                nf_bridge_pull_encap_header_rcsum(skb);
-               return br_nf_pre_routing_ipv6(ops, skb, state);
+               return br_nf_pre_routing_ipv6(priv, skb, state);
        }
 
        if (!brnf_call_iptables && !br->nf_call_iptables)
@@ -497,7 +494,7 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops,
 
        nf_bridge_pull_encap_header_rcsum(skb);
 
-       if (br_validate_ipv4(skb))
+       if (br_validate_ipv4(state->net, skb))
                return NF_DROP;
 
        nf_bridge_put(skb->nf_bridge);
@@ -511,7 +508,7 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops,
 
        skb->protocol = htons(ETH_P_IP);
 
-       NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, state->sk, skb,
+       NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, state->net, state->sk, skb,
                skb->dev, NULL,
                br_nf_pre_routing_finish);
 
@@ -526,7 +523,7 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops,
  * took place when the packet entered the bridge), but we
  * register an IPv4 PRE_ROUTING 'sabotage' hook that will
  * prevent this from happening. */
-static unsigned int br_nf_local_in(const struct nf_hook_ops *ops,
+static unsigned int br_nf_local_in(void *priv,
                                   struct sk_buff *skb,
                                   const struct nf_hook_state *state)
 {
@@ -535,7 +532,7 @@ static unsigned int br_nf_local_in(const struct nf_hook_ops *ops,
 }
 
 /* PF_BRIDGE/FORWARD *************************************************/
-static int br_nf_forward_finish(struct sock *sk, struct sk_buff *skb)
+static int br_nf_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
        struct net_device *in;
@@ -559,7 +556,7 @@ static int br_nf_forward_finish(struct sock *sk, struct sk_buff *skb)
        }
        nf_bridge_push_encap_header(skb);
 
-       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, sk, skb,
+       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, net, sk, skb,
                       in, skb->dev, br_forward_finish, 1);
        return 0;
 }
@@ -570,7 +567,7 @@ static int br_nf_forward_finish(struct sock *sk, struct sk_buff *skb)
  * but we are still able to filter on the 'real' indev/outdev
  * because of the physdev module. For ARP, indev and outdev are the
  * bridge ports. */
-static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
+static unsigned int br_nf_forward_ip(void *priv,
                                     struct sk_buff *skb,
                                     const struct nf_hook_state *state)
 {
@@ -609,13 +606,13 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
        }
 
        if (pf == NFPROTO_IPV4) {
-               if (br_validate_ipv4(skb))
+               if (br_validate_ipv4(state->net, skb))
                        return NF_DROP;
                IPCB(skb)->frag_max_size = nf_bridge->frag_max_size;
        }
 
        if (pf == NFPROTO_IPV6) {
-               if (br_validate_ipv6(skb))
+               if (br_validate_ipv6(state->net, skb))
                        return NF_DROP;
                IP6CB(skb)->frag_max_size = nf_bridge->frag_max_size;
        }
@@ -626,14 +623,14 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
        else
                skb->protocol = htons(ETH_P_IPV6);
 
-       NF_HOOK(pf, NF_INET_FORWARD, NULL, skb,
+       NF_HOOK(pf, NF_INET_FORWARD, state->net, NULL, skb,
                brnf_get_logical_dev(skb, state->in),
                parent, br_nf_forward_finish);
 
        return NF_STOLEN;
 }
 
-static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops,
+static unsigned int br_nf_forward_arp(void *priv,
                                      struct sk_buff *skb,
                                      const struct nf_hook_state *state)
 {
@@ -661,14 +658,13 @@ static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops,
                return NF_ACCEPT;
        }
        *d = state->in;
-       NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, state->sk, skb,
+       NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, state->net, state->sk, skb,
                state->in, state->out, br_nf_forward_finish);
 
        return NF_STOLEN;
 }
 
-#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) || IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
-static int br_nf_push_frag_xmit(struct sock *sk, struct sk_buff *skb)
+static int br_nf_push_frag_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct brnf_frag_data *data;
        int err;
@@ -690,30 +686,26 @@ static int br_nf_push_frag_xmit(struct sock *sk, struct sk_buff *skb)
        __skb_push(skb, data->encap_size);
 
        nf_bridge_info_free(skb);
-       return br_dev_queue_push_xmit(sk, skb);
+       return br_dev_queue_push_xmit(net, sk, skb);
 }
-#endif
 
-#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
-static int br_nf_ip_fragment(struct sock *sk, struct sk_buff *skb,
-                            int (*output)(struct sock *, struct sk_buff *))
+static int
+br_nf_ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
+                 int (*output)(struct net *, struct sock *, struct sk_buff *))
 {
        unsigned int mtu = ip_skb_dst_mtu(skb);
        struct iphdr *iph = ip_hdr(skb);
-       struct rtable *rt = skb_rtable(skb);
-       struct net_device *dev = rt->dst.dev;
 
        if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) ||
                     (IPCB(skb)->frag_max_size &&
                      IPCB(skb)->frag_max_size > mtu))) {
-               IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
                kfree_skb(skb);
                return -EMSGSIZE;
        }
 
-       return ip_do_fragment(sk, skb, output);
+       return ip_do_fragment(net, sk, skb, output);
 }
-#endif
 
 static unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb)
 {
@@ -722,7 +714,7 @@ static unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb)
        return 0;
 }
 
-static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
+static int br_nf_dev_queue_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct nf_bridge_info *nf_bridge;
        unsigned int mtu_reserved;
@@ -731,19 +723,19 @@ static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
 
        if (skb_is_gso(skb) || skb->len + mtu_reserved <= skb->dev->mtu) {
                nf_bridge_info_free(skb);
-               return br_dev_queue_push_xmit(sk, skb);
+               return br_dev_queue_push_xmit(net, sk, skb);
        }
 
        nf_bridge = nf_bridge_info_get(skb);
 
-#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
        /* This is wrong! We should preserve the original fragment
         * boundaries by preserving frag_list rather than refragmenting.
         */
-       if (skb->protocol == htons(ETH_P_IP)) {
+       if (IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) &&
+           skb->protocol == htons(ETH_P_IP)) {
                struct brnf_frag_data *data;
 
-               if (br_validate_ipv4(skb))
+               if (br_validate_ipv4(net, skb))
                        goto drop;
 
                IPCB(skb)->frag_max_size = nf_bridge->frag_max_size;
@@ -760,15 +752,14 @@ static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
                skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
                                                 data->size);
 
-               return br_nf_ip_fragment(sk, skb, br_nf_push_frag_xmit);
+               return br_nf_ip_fragment(net, sk, skb, br_nf_push_frag_xmit);
        }
-#endif
-#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
-       if (skb->protocol == htons(ETH_P_IPV6)) {
+       if (IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) &&
+           skb->protocol == htons(ETH_P_IPV6)) {
                const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops();
                struct brnf_frag_data *data;
 
-               if (br_validate_ipv6(skb))
+               if (br_validate_ipv6(net, skb))
                        goto drop;
 
                IP6CB(skb)->frag_max_size = nf_bridge->frag_max_size;
@@ -783,21 +774,20 @@ static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
                                                 data->size);
 
                if (v6ops)
-                       return v6ops->fragment(sk, skb, br_nf_push_frag_xmit);
+                       return v6ops->fragment(net, sk, skb, br_nf_push_frag_xmit);
 
                kfree_skb(skb);
                return -EMSGSIZE;
        }
-#endif
        nf_bridge_info_free(skb);
-       return br_dev_queue_push_xmit(sk, skb);
+       return br_dev_queue_push_xmit(net, sk, skb);
  drop:
        kfree_skb(skb);
        return 0;
 }
 
 /* PF_BRIDGE/POST_ROUTING ********************************************/
-static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops,
+static unsigned int br_nf_post_routing(void *priv,
                                       struct sk_buff *skb,
                                       const struct nf_hook_state *state)
 {
@@ -836,7 +826,7 @@ static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops,
        else
                skb->protocol = htons(ETH_P_IPV6);
 
-       NF_HOOK(pf, NF_INET_POST_ROUTING, state->sk, skb,
+       NF_HOOK(pf, NF_INET_POST_ROUTING, state->net, state->sk, skb,
                NULL, realoutdev,
                br_nf_dev_queue_xmit);
 
@@ -846,7 +836,7 @@ static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops,
 /* IP/SABOTAGE *****************************************************/
 /* Don't hand locally destined packets to PF_INET(6)/PRE_ROUTING
  * for the second time. */
-static unsigned int ip_sabotage_in(const struct nf_hook_ops *ops,
+static unsigned int ip_sabotage_in(void *priv,
                                   struct sk_buff *skb,
                                   const struct nf_hook_state *state)
 {
@@ -880,7 +870,7 @@ static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb)
        skb->dev = nf_bridge->physindev;
 
        nf_bridge->physoutdev = NULL;
-       br_handle_frame_finish(NULL, skb);
+       br_handle_frame_finish(dev_net(skb->dev), NULL, skb);
 }
 
 static int br_nf_dev_xmit(struct sk_buff *skb)
@@ -906,49 +896,42 @@ EXPORT_SYMBOL_GPL(br_netfilter_enable);
 static struct nf_hook_ops br_nf_ops[] __read_mostly = {
        {
                .hook = br_nf_pre_routing,
-               .owner = THIS_MODULE,
                .pf = NFPROTO_BRIDGE,
                .hooknum = NF_BR_PRE_ROUTING,
                .priority = NF_BR_PRI_BRNF,
        },
        {
                .hook = br_nf_local_in,
-               .owner = THIS_MODULE,
                .pf = NFPROTO_BRIDGE,
                .hooknum = NF_BR_LOCAL_IN,
                .priority = NF_BR_PRI_BRNF,
        },
        {
                .hook = br_nf_forward_ip,
-               .owner = THIS_MODULE,
                .pf = NFPROTO_BRIDGE,
                .hooknum = NF_BR_FORWARD,
                .priority = NF_BR_PRI_BRNF - 1,
        },
        {
                .hook = br_nf_forward_arp,
-               .owner = THIS_MODULE,
                .pf = NFPROTO_BRIDGE,
                .hooknum = NF_BR_FORWARD,
                .priority = NF_BR_PRI_BRNF,
        },
        {
                .hook = br_nf_post_routing,
-               .owner = THIS_MODULE,
                .pf = NFPROTO_BRIDGE,
                .hooknum = NF_BR_POST_ROUTING,
                .priority = NF_BR_PRI_LAST,
        },
        {
                .hook = ip_sabotage_in,
-               .owner = THIS_MODULE,
                .pf = NFPROTO_IPV4,
                .hooknum = NF_INET_PRE_ROUTING,
                .priority = NF_IP_PRI_FIRST,
        },
        {
                .hook = ip_sabotage_in,
-               .owner = THIS_MODULE,
                .pf = NFPROTO_IPV6,
                .hooknum = NF_INET_PRE_ROUTING,
                .priority = NF_IP6_PRI_FIRST,
index 77383bfe7ea38f888ce67619d03d1b7856bab84b..d61f56efc8dc3a2bc7ca440ae07f301a7cb6eaed 100644 (file)
@@ -100,10 +100,9 @@ bad:
        return -1;
 }
 
-int br_validate_ipv6(struct sk_buff *skb)
+int br_validate_ipv6(struct net *net, struct sk_buff *skb)
 {
        const struct ipv6hdr *hdr;
-       struct net_device *dev = skb->dev;
        struct inet6_dev *idev = __in6_dev_get(skb->dev);
        u32 pkt_len;
        u8 ip6h_len = sizeof(struct ipv6hdr);
@@ -123,12 +122,12 @@ int br_validate_ipv6(struct sk_buff *skb)
 
        if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
                if (pkt_len + ip6h_len > skb->len) {
-                       IP6_INC_STATS_BH(dev_net(dev), idev,
+                       IP6_INC_STATS_BH(net, idev,
                                         IPSTATS_MIB_INTRUNCATEDPKTS);
                        goto drop;
                }
                if (pskb_trim_rcsum(skb, pkt_len + ip6h_len)) {
-                       IP6_INC_STATS_BH(dev_net(dev), idev,
+                       IP6_INC_STATS_BH(net, idev,
                                         IPSTATS_MIB_INDISCARDS);
                        goto drop;
                }
@@ -143,7 +142,7 @@ int br_validate_ipv6(struct sk_buff *skb)
        return 0;
 
 inhdr_error:
-       IP6_INC_STATS_BH(dev_net(dev), idev, IPSTATS_MIB_INHDRERRORS);
+       IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS);
 drop:
        return -1;
 }
@@ -161,7 +160,7 @@ br_nf_ipv6_daddr_was_changed(const struct sk_buff *skb,
  * for br_nf_pre_routing_finish(), same logic is used here but
  * equivalent IPv6 function ip6_route_input() called indirectly.
  */
-static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
+static int br_nf_pre_routing_finish_ipv6(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
        struct rtable *rt;
@@ -189,7 +188,7 @@ static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
                        nf_bridge_update_protocol(skb);
                        nf_bridge_push_encap_header(skb);
                        NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
-                                      sk, skb, skb->dev, NULL,
+                                      net, sk, skb, skb->dev, NULL,
                                       br_nf_pre_routing_finish_bridge,
                                       1);
                        return 0;
@@ -208,7 +207,7 @@ static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
        skb->dev = nf_bridge->physindev;
        nf_bridge_update_protocol(skb);
        nf_bridge_push_encap_header(skb);
-       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
+       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, net, sk, skb,
                       skb->dev, NULL,
                       br_handle_frame_finish, 1);
 
@@ -218,13 +217,13 @@ static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
 /* Replicate the checks that IPv6 does on packet reception and pass the packet
  * to ip6tables.
  */
-unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
+unsigned int br_nf_pre_routing_ipv6(void *priv,
                                    struct sk_buff *skb,
                                    const struct nf_hook_state *state)
 {
        struct nf_bridge_info *nf_bridge;
 
-       if (br_validate_ipv6(skb))
+       if (br_validate_ipv6(state->net, skb))
                return NF_DROP;
 
        nf_bridge_put(skb->nf_bridge);
@@ -237,7 +236,7 @@ unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
        nf_bridge->ipv6_daddr = ipv6_hdr(skb)->daddr;
 
        skb->protocol = htons(ETH_P_IPV6);
-       NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->sk, skb,
+       NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->net, state->sk, skb,
                skb->dev, NULL,
                br_nf_pre_routing_finish_ipv6);
 
index ea748c93a07f1dcd988cc18130d629d6a3556d98..40197ff8918abab433c563dd92186479652ed84d 100644 (file)
 #include "br_private.h"
 #include "br_private_stp.h"
 
-static int br_get_num_vlan_infos(const struct net_port_vlans *pv,
-                                u32 filter_mask)
+static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
+                               u32 filter_mask)
 {
-       u16 vid_range_start = 0, vid_range_end = 0;
-       u16 vid_range_flags = 0;
-       u16 pvid, vid, flags;
+       struct net_bridge_vlan *v;
+       u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0;
+       u16 flags, pvid;
        int num_vlans = 0;
 
-       if (filter_mask & RTEXT_FILTER_BRVLAN)
-               return pv->num_vlans;
-
        if (!(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED))
                return 0;
 
-       /* Count number of vlan info's
-        */
-       pvid = br_get_pvid(pv);
-       for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
+       pvid = br_get_pvid(vg);
+       /* Count number of vlan infos */
+       list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
                flags = 0;
-               if (vid == pvid)
+               /* only a context, bridge vlan not activated */
+               if (!br_vlan_should_use(v))
+                       continue;
+               if (v->vid == pvid)
                        flags |= BRIDGE_VLAN_INFO_PVID;
 
-               if (test_bit(vid, pv->untagged_bitmap))
+               if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
                        flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 
                if (vid_range_start == 0) {
                        goto initvars;
-               } else if ((vid - vid_range_end) == 1 &&
+               } else if ((v->vid - vid_range_end) == 1 &&
                        flags == vid_range_flags) {
-                       vid_range_end = vid;
+                       vid_range_end = v->vid;
                        continue;
                } else {
                        if ((vid_range_end - vid_range_start) > 0)
@@ -59,8 +58,8 @@ static int br_get_num_vlan_infos(const struct net_port_vlans *pv,
                                num_vlans += 1;
                }
 initvars:
-               vid_range_start = vid;
-               vid_range_end = vid;
+               vid_range_start = v->vid;
+               vid_range_end = v->vid;
                vid_range_flags = flags;
        }
 
@@ -74,28 +73,43 @@ initvars:
        return num_vlans;
 }
 
+static int br_get_num_vlan_infos(struct net_bridge_vlan_group *vg,
+                                u32 filter_mask)
+{
+       int num_vlans;
+
+       if (!vg)
+               return 0;
+
+       if (filter_mask & RTEXT_FILTER_BRVLAN)
+               return vg->num_vlans;
+
+       rcu_read_lock();
+       num_vlans = __get_num_vlan_infos(vg, filter_mask);
+       rcu_read_unlock();
+
+       return num_vlans;
+}
+
 static size_t br_get_link_af_size_filtered(const struct net_device *dev,
                                           u32 filter_mask)
 {
-       struct net_port_vlans *pv;
+       struct net_bridge_vlan_group *vg = NULL;
+       struct net_bridge_port *p;
+       struct net_bridge *br;
        int num_vlan_infos;
 
        rcu_read_lock();
-       if (br_port_exists(dev))
-               pv = nbp_get_vlan_info(br_port_get_rcu(dev));
-       else if (dev->priv_flags & IFF_EBRIDGE)
-               pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev));
-       else
-               pv = NULL;
-       if (pv)
-               num_vlan_infos = br_get_num_vlan_infos(pv, filter_mask);
-       else
-               num_vlan_infos = 0;
+       if (br_port_exists(dev)) {
+               p = br_port_get_rcu(dev);
+               vg = nbp_vlan_group_rcu(p);
+       } else if (dev->priv_flags & IFF_EBRIDGE) {
+               br = netdev_priv(dev);
+               vg = br_vlan_group_rcu(br);
+       }
+       num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask);
        rcu_read_unlock();
 
-       if (!num_vlan_infos)
-               return 0;
-
        /* Each VLAN is returned in bridge_vlan_info along with flags */
        return num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info));
 }
@@ -113,6 +127,20 @@ static inline size_t br_port_info_size(void)
                + nla_total_size(1)     /* IFLA_BRPORT_UNICAST_FLOOD */
                + nla_total_size(1)     /* IFLA_BRPORT_PROXYARP */
                + nla_total_size(1)     /* IFLA_BRPORT_PROXYARP_WIFI */
+               + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */
+               + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */
+               + nla_total_size(sizeof(u16))   /* IFLA_BRPORT_DESIGNATED_PORT */
+               + nla_total_size(sizeof(u16))   /* IFLA_BRPORT_DESIGNATED_COST */
+               + nla_total_size(sizeof(u16))   /* IFLA_BRPORT_ID */
+               + nla_total_size(sizeof(u16))   /* IFLA_BRPORT_NO */
+               + nla_total_size(sizeof(u8))    /* IFLA_BRPORT_TOPOLOGY_CHANGE_ACK */
+               + nla_total_size(sizeof(u8))    /* IFLA_BRPORT_CONFIG_PENDING */
+               + nla_total_size(sizeof(u64))   /* IFLA_BRPORT_MESSAGE_AGE_TIMER */
+               + nla_total_size(sizeof(u64))   /* IFLA_BRPORT_FORWARD_DELAY_TIMER */
+               + nla_total_size(sizeof(u64))   /* IFLA_BRPORT_HOLD_TIMER */
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+               + nla_total_size(sizeof(u8))    /* IFLA_BRPORT_MULTICAST_ROUTER */
+#endif
                + 0;
 }
 
@@ -134,6 +162,7 @@ static int br_port_fill_attrs(struct sk_buff *skb,
                              const struct net_bridge_port *p)
 {
        u8 mode = !!(p->flags & BR_HAIRPIN_MODE);
+       u64 timerval;
 
        if (nla_put_u8(skb, IFLA_BRPORT_STATE, p->state) ||
            nla_put_u16(skb, IFLA_BRPORT_PRIORITY, p->priority) ||
@@ -146,9 +175,36 @@ static int br_port_fill_attrs(struct sk_buff *skb,
            nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD)) ||
            nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(p->flags & BR_PROXYARP)) ||
            nla_put_u8(skb, IFLA_BRPORT_PROXYARP_WIFI,
-                      !!(p->flags & BR_PROXYARP_WIFI)))
+                      !!(p->flags & BR_PROXYARP_WIFI)) ||
+           nla_put(skb, IFLA_BRPORT_ROOT_ID, sizeof(struct ifla_bridge_id),
+                   &p->designated_root) ||
+           nla_put(skb, IFLA_BRPORT_BRIDGE_ID, sizeof(struct ifla_bridge_id),
+                   &p->designated_bridge) ||
+           nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_PORT, p->designated_port) ||
+           nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_COST, p->designated_cost) ||
+           nla_put_u16(skb, IFLA_BRPORT_ID, p->port_id) ||
+           nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no) ||
+           nla_put_u8(skb, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+                      p->topology_change_ack) ||
+           nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending))
+               return -EMSGSIZE;
+
+       timerval = br_timer_value(&p->message_age_timer);
+       if (nla_put_u64(skb, IFLA_BRPORT_MESSAGE_AGE_TIMER, timerval))
+               return -EMSGSIZE;
+       timerval = br_timer_value(&p->forward_delay_timer);
+       if (nla_put_u64(skb, IFLA_BRPORT_FORWARD_DELAY_TIMER, timerval))
+               return -EMSGSIZE;
+       timerval = br_timer_value(&p->hold_timer);
+       if (nla_put_u64(skb, IFLA_BRPORT_HOLD_TIMER, timerval))
                return -EMSGSIZE;
 
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+       if (nla_put_u8(skb, IFLA_BRPORT_MULTICAST_ROUTER,
+                      p->multicast_router))
+               return -EMSGSIZE;
+#endif
+
        return 0;
 }
 
@@ -185,31 +241,33 @@ nla_put_failure:
 }
 
 static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
-                                        const struct net_port_vlans *pv)
+                                        struct net_bridge_vlan_group *vg)
 {
-       u16 vid_range_start = 0, vid_range_end = 0;
-       u16 vid_range_flags = 0;
-       u16 pvid, vid, flags;
+       struct net_bridge_vlan *v;
+       u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0;
+       u16 flags, pvid;
        int err = 0;
 
        /* Pack IFLA_BRIDGE_VLAN_INFO's for every vlan
         * and mark vlan info with begin and end flags
         * if vlaninfo represents a range
         */
-       pvid = br_get_pvid(pv);
-       for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
+       pvid = br_get_pvid(vg);
+       list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
                flags = 0;
-               if (vid == pvid)
+               if (!br_vlan_should_use(v))
+                       continue;
+               if (v->vid == pvid)
                        flags |= BRIDGE_VLAN_INFO_PVID;
 
-               if (test_bit(vid, pv->untagged_bitmap))
+               if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
                        flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 
                if (vid_range_start == 0) {
                        goto initvars;
-               } else if ((vid - vid_range_end) == 1 &&
+               } else if ((v->vid - vid_range_end) == 1 &&
                        flags == vid_range_flags) {
-                       vid_range_end = vid;
+                       vid_range_end = v->vid;
                        continue;
                } else {
                        err = br_fill_ifvlaninfo_range(skb, vid_range_start,
@@ -220,8 +278,8 @@ static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
                }
 
 initvars:
-               vid_range_start = vid;
-               vid_range_end = vid;
+               vid_range_start = v->vid;
+               vid_range_end = v->vid;
                vid_range_flags = flags;
        }
 
@@ -238,19 +296,23 @@ initvars:
 }
 
 static int br_fill_ifvlaninfo(struct sk_buff *skb,
-                             const struct net_port_vlans *pv)
+                             struct net_bridge_vlan_group *vg)
 {
        struct bridge_vlan_info vinfo;
-       u16 pvid, vid;
+       struct net_bridge_vlan *v;
+       u16 pvid;
+
+       pvid = br_get_pvid(vg);
+       list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
+               if (!br_vlan_should_use(v))
+                       continue;
 
-       pvid = br_get_pvid(pv);
-       for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
-               vinfo.vid = vid;
+               vinfo.vid = v->vid;
                vinfo.flags = 0;
-               if (vid == pvid)
+               if (v->vid == pvid)
                        vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
 
-               if (test_bit(vid, pv->untagged_bitmap))
+               if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
                        vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 
                if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
@@ -269,11 +331,11 @@ nla_put_failure:
  * Contains port and master info as well as carrier and bridge state.
  */
 static int br_fill_ifinfo(struct sk_buff *skb,
-                         const struct net_bridge_port *port,
+                         struct net_bridge_port *port,
                          u32 pid, u32 seq, int event, unsigned int flags,
                          u32 filter_mask, const struct net_device *dev)
 {
-       const struct net_bridge *br;
+       struct net_bridge *br;
        struct ifinfomsg *hdr;
        struct nlmsghdr *nlh;
        u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
@@ -320,26 +382,31 @@ static int br_fill_ifinfo(struct sk_buff *skb,
        /* Check if  the VID information is requested */
        if ((filter_mask & RTEXT_FILTER_BRVLAN) ||
            (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) {
-               const struct net_port_vlans *pv;
+               struct net_bridge_vlan_group *vg;
                struct nlattr *af;
                int err;
 
+               /* RCU needed because of the VLAN locking rules (rcu || rtnl) */
+               rcu_read_lock();
                if (port)
-                       pv = nbp_get_vlan_info(port);
+                       vg = nbp_vlan_group_rcu(port);
                else
-                       pv = br_get_vlan_info(br);
+                       vg = br_vlan_group_rcu(br);
 
-               if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID))
+               if (!vg || !vg->num_vlans) {
+                       rcu_read_unlock();
                        goto done;
-
+               }
                af = nla_nest_start(skb, IFLA_AF_SPEC);
-               if (!af)
+               if (!af) {
+                       rcu_read_unlock();
                        goto nla_put_failure;
-
+               }
                if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
-                       err = br_fill_ifvlaninfo_compressed(skb, pv);
+                       err = br_fill_ifvlaninfo_compressed(skb, vg);
                else
-                       err = br_fill_ifvlaninfo(skb, pv);
+                       err = br_fill_ifvlaninfo(skb, vg);
+               rcu_read_unlock();
                if (err)
                        goto nla_put_failure;
                nla_nest_end(skb, af);
@@ -413,14 +480,14 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
        switch (cmd) {
        case RTM_SETLINK:
                if (p) {
+                       /* if the MASTER flag is set this will act on the global
+                        * per-VLAN entry as well
+                        */
                        err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
                        if (err)
                                break;
-
-                       if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
-                               err = br_vlan_add(p->br, vinfo->vid,
-                                                 vinfo->flags);
                } else {
+                       vinfo->flags |= BRIDGE_VLAN_INFO_BRENTRY;
                        err = br_vlan_add(br, vinfo->vid, vinfo->flags);
                }
                break;
@@ -462,6 +529,9 @@ static int br_afspec(struct net_bridge *br,
                        if (vinfo_start)
                                return -EINVAL;
                        vinfo_start = vinfo;
+                       /* don't allow range of pvids */
+                       if (vinfo_start->flags & BRIDGE_VLAN_INFO_PVID)
+                               return -EINVAL;
                        continue;
                }
 
@@ -507,6 +577,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = {
        [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 },
        [IFLA_BRPORT_PROXYARP]  = { .type = NLA_U8 },
        [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NLA_U8 },
+       [IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NLA_U8 },
 };
 
 /* Change the state of the port and notify spanning tree */
@@ -578,6 +649,18 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
                        return err;
        }
 
+       if (tb[IFLA_BRPORT_FLUSH])
+               br_fdb_delete_by_port(p->br, p, 0, 0);
+
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+       if (tb[IFLA_BRPORT_MULTICAST_ROUTER]) {
+               u8 mcast_router = nla_get_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER]);
+
+               err = br_multicast_set_port_router(p, mcast_router);
+               if (err)
+                       return err;
+       }
+#endif
        br_port_flags_change(p, old_flags ^ p->flags);
        return 0;
 }
@@ -744,6 +827,27 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
        [IFLA_BR_PRIORITY] = { .type = NLA_U16 },
        [IFLA_BR_VLAN_FILTERING] = { .type = NLA_U8 },
        [IFLA_BR_VLAN_PROTOCOL] = { .type = NLA_U16 },
+       [IFLA_BR_GROUP_FWD_MASK] = { .type = NLA_U16 },
+       [IFLA_BR_GROUP_ADDR] = { .type = NLA_BINARY,
+                                .len  = ETH_ALEN },
+       [IFLA_BR_MCAST_ROUTER] = { .type = NLA_U8 },
+       [IFLA_BR_MCAST_SNOOPING] = { .type = NLA_U8 },
+       [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NLA_U8 },
+       [IFLA_BR_MCAST_QUERIER] = { .type = NLA_U8 },
+       [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NLA_U32 },
+       [IFLA_BR_MCAST_HASH_MAX] = { .type = NLA_U32 },
+       [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NLA_U32 },
+       [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NLA_U32 },
+       [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NLA_U64 },
+       [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NLA_U64 },
+       [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NLA_U64 },
+       [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NLA_U64 },
+       [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NLA_U64 },
+       [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NLA_U64 },
+       [IFLA_BR_NF_CALL_IPTABLES] = { .type = NLA_U8 },
+       [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NLA_U8 },
+       [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NLA_U8 },
+       [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NLA_U16 },
 };
 
 static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
@@ -774,9 +878,9 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
        }
 
        if (data[IFLA_BR_AGEING_TIME]) {
-               u32 ageing_time = nla_get_u32(data[IFLA_BR_AGEING_TIME]);
-
-               br->ageing_time = clock_t_to_jiffies(ageing_time);
+               err = br_set_ageing_time(br, nla_get_u32(data[IFLA_BR_AGEING_TIME]));
+               if (err)
+                       return err;
        }
 
        if (data[IFLA_BR_STP_STATE]) {
@@ -807,6 +911,158 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
                if (err)
                        return err;
        }
+
+       if (data[IFLA_BR_VLAN_DEFAULT_PVID]) {
+               __u16 defpvid = nla_get_u16(data[IFLA_BR_VLAN_DEFAULT_PVID]);
+
+               err = __br_vlan_set_default_pvid(br, defpvid);
+               if (err)
+                       return err;
+       }
+#endif
+
+       if (data[IFLA_BR_GROUP_FWD_MASK]) {
+               u16 fwd_mask = nla_get_u16(data[IFLA_BR_GROUP_FWD_MASK]);
+
+               if (fwd_mask & BR_GROUPFWD_RESTRICTED)
+                       return -EINVAL;
+               br->group_fwd_mask = fwd_mask;
+       }
+
+       if (data[IFLA_BR_GROUP_ADDR]) {
+               u8 new_addr[ETH_ALEN];
+
+               if (nla_len(data[IFLA_BR_GROUP_ADDR]) != ETH_ALEN)
+                       return -EINVAL;
+               memcpy(new_addr, nla_data(data[IFLA_BR_GROUP_ADDR]), ETH_ALEN);
+               if (!is_link_local_ether_addr(new_addr))
+                       return -EINVAL;
+               if (new_addr[5] == 1 ||         /* 802.3x Pause address */
+                   new_addr[5] == 2 ||         /* 802.3ad Slow protocols */
+                   new_addr[5] == 3)           /* 802.1X PAE address */
+                       return -EINVAL;
+               spin_lock_bh(&br->lock);
+               memcpy(br->group_addr, new_addr, sizeof(br->group_addr));
+               spin_unlock_bh(&br->lock);
+               br->group_addr_set = true;
+               br_recalculate_fwd_mask(br);
+       }
+
+       if (data[IFLA_BR_FDB_FLUSH])
+               br_fdb_flush(br);
+
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+       if (data[IFLA_BR_MCAST_ROUTER]) {
+               u8 multicast_router = nla_get_u8(data[IFLA_BR_MCAST_ROUTER]);
+
+               err = br_multicast_set_router(br, multicast_router);
+               if (err)
+                       return err;
+       }
+
+       if (data[IFLA_BR_MCAST_SNOOPING]) {
+               u8 mcast_snooping = nla_get_u8(data[IFLA_BR_MCAST_SNOOPING]);
+
+               err = br_multicast_toggle(br, mcast_snooping);
+               if (err)
+                       return err;
+       }
+
+       if (data[IFLA_BR_MCAST_QUERY_USE_IFADDR]) {
+               u8 val;
+
+               val = nla_get_u8(data[IFLA_BR_MCAST_QUERY_USE_IFADDR]);
+               br->multicast_query_use_ifaddr = !!val;
+       }
+
+       if (data[IFLA_BR_MCAST_QUERIER]) {
+               u8 mcast_querier = nla_get_u8(data[IFLA_BR_MCAST_QUERIER]);
+
+               err = br_multicast_set_querier(br, mcast_querier);
+               if (err)
+                       return err;
+       }
+
+       if (data[IFLA_BR_MCAST_HASH_ELASTICITY]) {
+               u32 val = nla_get_u32(data[IFLA_BR_MCAST_HASH_ELASTICITY]);
+
+               br->hash_elasticity = val;
+       }
+
+       if (data[IFLA_BR_MCAST_HASH_MAX]) {
+               u32 hash_max = nla_get_u32(data[IFLA_BR_MCAST_HASH_MAX]);
+
+               err = br_multicast_set_hash_max(br, hash_max);
+               if (err)
+                       return err;
+       }
+
+       if (data[IFLA_BR_MCAST_LAST_MEMBER_CNT]) {
+               u32 val = nla_get_u32(data[IFLA_BR_MCAST_LAST_MEMBER_CNT]);
+
+               br->multicast_last_member_count = val;
+       }
+
+       if (data[IFLA_BR_MCAST_STARTUP_QUERY_CNT]) {
+               u32 val = nla_get_u32(data[IFLA_BR_MCAST_STARTUP_QUERY_CNT]);
+
+               br->multicast_startup_query_count = val;
+       }
+
+       if (data[IFLA_BR_MCAST_LAST_MEMBER_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_LAST_MEMBER_INTVL]);
+
+               br->multicast_last_member_interval = clock_t_to_jiffies(val);
+       }
+
+       if (data[IFLA_BR_MCAST_MEMBERSHIP_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_MEMBERSHIP_INTVL]);
+
+               br->multicast_membership_interval = clock_t_to_jiffies(val);
+       }
+
+       if (data[IFLA_BR_MCAST_QUERIER_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERIER_INTVL]);
+
+               br->multicast_querier_interval = clock_t_to_jiffies(val);
+       }
+
+       if (data[IFLA_BR_MCAST_QUERY_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERY_INTVL]);
+
+               br->multicast_query_interval = clock_t_to_jiffies(val);
+       }
+
+       if (data[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]);
+
+               br->multicast_query_response_interval = clock_t_to_jiffies(val);
+       }
+
+       if (data[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]);
+
+               br->multicast_startup_query_interval = clock_t_to_jiffies(val);
+       }
+#endif
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+       if (data[IFLA_BR_NF_CALL_IPTABLES]) {
+               u8 val = nla_get_u8(data[IFLA_BR_NF_CALL_IPTABLES]);
+
+               br->nf_call_iptables = val ? true : false;
+       }
+
+       if (data[IFLA_BR_NF_CALL_IP6TABLES]) {
+               u8 val = nla_get_u8(data[IFLA_BR_NF_CALL_IP6TABLES]);
+
+               br->nf_call_ip6tables = val ? true : false;
+       }
+
+       if (data[IFLA_BR_NF_CALL_ARPTABLES]) {
+               u8 val = nla_get_u8(data[IFLA_BR_NF_CALL_ARPTABLES]);
+
+               br->nf_call_arptables = val ? true : false;
+       }
 #endif
 
        return 0;
@@ -823,6 +1079,40 @@ static size_t br_get_size(const struct net_device *brdev)
               nla_total_size(sizeof(u8)) +     /* IFLA_BR_VLAN_FILTERING */
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
               nla_total_size(sizeof(__be16)) + /* IFLA_BR_VLAN_PROTOCOL */
+              nla_total_size(sizeof(u16)) +    /* IFLA_BR_VLAN_DEFAULT_PVID */
+#endif
+              nla_total_size(sizeof(u16)) +    /* IFLA_BR_GROUP_FWD_MASK */
+              nla_total_size(sizeof(struct ifla_bridge_id)) +   /* IFLA_BR_ROOT_ID */
+              nla_total_size(sizeof(struct ifla_bridge_id)) +   /* IFLA_BR_BRIDGE_ID */
+              nla_total_size(sizeof(u16)) +    /* IFLA_BR_ROOT_PORT */
+              nla_total_size(sizeof(u32)) +    /* IFLA_BR_ROOT_PATH_COST */
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_TOPOLOGY_CHANGE */
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_TOPOLOGY_CHANGE_DETECTED */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_HELLO_TIMER */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_TCN_TIMER */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_TOPOLOGY_CHANGE_TIMER */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_GC_TIMER */
+              nla_total_size(ETH_ALEN) +       /* IFLA_BR_GROUP_ADDR */
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_ROUTER */
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_SNOOPING */
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_QUERY_USE_IFADDR */
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_QUERIER */
+              nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_HASH_ELASTICITY */
+              nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_HASH_MAX */
+              nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_LAST_MEMBER_CNT */
+              nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_STARTUP_QUERY_CNT */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_LAST_MEMBER_INTVL */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_MEMBERSHIP_INTVL */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_QUERIER_INTVL */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_QUERY_INTVL */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_QUERY_RESPONSE_INTVL */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_STARTUP_QUERY_INTVL */
+#endif
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_IPTABLES */
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_IP6TABLES */
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_ARPTABLES */
 #endif
               0;
 }
@@ -837,6 +1127,20 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
        u32 stp_enabled = br->stp_enabled;
        u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1];
        u8 vlan_enabled = br_vlan_enabled(br);
+       u64 clockval;
+
+       clockval = br_timer_value(&br->hello_timer);
+       if (nla_put_u64(skb, IFLA_BR_HELLO_TIMER, clockval))
+               return -EMSGSIZE;
+       clockval = br_timer_value(&br->tcn_timer);
+       if (nla_put_u64(skb, IFLA_BR_TCN_TIMER, clockval))
+               return -EMSGSIZE;
+       clockval = br_timer_value(&br->topology_change_timer);
+       if (nla_put_u64(skb, IFLA_BR_TOPOLOGY_CHANGE_TIMER, clockval))
+               return -EMSGSIZE;
+       clockval = br_timer_value(&br->gc_timer);
+       if (nla_put_u64(skb, IFLA_BR_GC_TIMER, clockval))
+               return -EMSGSIZE;
 
        if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) ||
            nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) ||
@@ -844,38 +1148,76 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
            nla_put_u32(skb, IFLA_BR_AGEING_TIME, ageing_time) ||
            nla_put_u32(skb, IFLA_BR_STP_STATE, stp_enabled) ||
            nla_put_u16(skb, IFLA_BR_PRIORITY, priority) ||
-           nla_put_u8(skb, IFLA_BR_VLAN_FILTERING, vlan_enabled))
+           nla_put_u8(skb, IFLA_BR_VLAN_FILTERING, vlan_enabled) ||
+           nla_put_u16(skb, IFLA_BR_GROUP_FWD_MASK, br->group_fwd_mask) ||
+           nla_put(skb, IFLA_BR_BRIDGE_ID, sizeof(struct ifla_bridge_id),
+                   &br->bridge_id) ||
+           nla_put(skb, IFLA_BR_ROOT_ID, sizeof(struct ifla_bridge_id),
+                   &br->designated_root) ||
+           nla_put_u16(skb, IFLA_BR_ROOT_PORT, br->root_port) ||
+           nla_put_u32(skb, IFLA_BR_ROOT_PATH_COST, br->root_path_cost) ||
+           nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE, br->topology_change) ||
+           nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+                      br->topology_change_detected) ||
+           nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr))
                return -EMSGSIZE;
 
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
-       if (nla_put_be16(skb, IFLA_BR_VLAN_PROTOCOL, br->vlan_proto))
+       if (nla_put_be16(skb, IFLA_BR_VLAN_PROTOCOL, br->vlan_proto) ||
+           nla_put_u16(skb, IFLA_BR_VLAN_DEFAULT_PVID, br->default_pvid))
+               return -EMSGSIZE;
+#endif
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+       if (nla_put_u8(skb, IFLA_BR_MCAST_ROUTER, br->multicast_router) ||
+           nla_put_u8(skb, IFLA_BR_MCAST_SNOOPING, !br->multicast_disabled) ||
+           nla_put_u8(skb, IFLA_BR_MCAST_QUERY_USE_IFADDR,
+                      br->multicast_query_use_ifaddr) ||
+           nla_put_u8(skb, IFLA_BR_MCAST_QUERIER, br->multicast_querier) ||
+           nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY,
+                       br->hash_elasticity) ||
+           nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max) ||
+           nla_put_u32(skb, IFLA_BR_MCAST_LAST_MEMBER_CNT,
+                       br->multicast_last_member_count) ||
+           nla_put_u32(skb, IFLA_BR_MCAST_STARTUP_QUERY_CNT,
+                       br->multicast_startup_query_count))
+               return -EMSGSIZE;
+
+       clockval = jiffies_to_clock_t(br->multicast_last_member_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_LAST_MEMBER_INTVL, clockval))
+               return -EMSGSIZE;
+       clockval = jiffies_to_clock_t(br->multicast_membership_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_MEMBERSHIP_INTVL, clockval))
+               return -EMSGSIZE;
+       clockval = jiffies_to_clock_t(br->multicast_querier_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_QUERIER_INTVL, clockval))
+               return -EMSGSIZE;
+       clockval = jiffies_to_clock_t(br->multicast_query_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_QUERY_INTVL, clockval))
+               return -EMSGSIZE;
+       clockval = jiffies_to_clock_t(br->multicast_query_response_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, clockval))
+               return -EMSGSIZE;
+       clockval = jiffies_to_clock_t(br->multicast_startup_query_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, clockval))
+               return -EMSGSIZE;
+#endif
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+       if (nla_put_u8(skb, IFLA_BR_NF_CALL_IPTABLES,
+                      br->nf_call_iptables ? 1 : 0) ||
+           nla_put_u8(skb, IFLA_BR_NF_CALL_IP6TABLES,
+                      br->nf_call_ip6tables ? 1 : 0) ||
+           nla_put_u8(skb, IFLA_BR_NF_CALL_ARPTABLES,
+                      br->nf_call_arptables ? 1 : 0))
                return -EMSGSIZE;
 #endif
 
        return 0;
 }
 
-static size_t br_get_link_af_size(const struct net_device *dev)
-{
-       struct net_port_vlans *pv;
-
-       if (br_port_exists(dev))
-               pv = nbp_get_vlan_info(br_port_get_rtnl(dev));
-       else if (dev->priv_flags & IFF_EBRIDGE)
-               pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev));
-       else
-               return 0;
-
-       if (!pv)
-               return 0;
-
-       /* Each VLAN is returned in bridge_vlan_info along with flags */
-       return pv->num_vlans * nla_total_size(sizeof(struct bridge_vlan_info));
-}
 
 static struct rtnl_af_ops br_af_ops __read_mostly = {
        .family                 = AF_BRIDGE,
-       .get_link_af_size       = br_get_link_af_size,
+       .get_link_af_size       = br_get_link_af_size_filtered,
 };
 
 struct rtnl_link_ops br_link_ops __read_mostly = {
index 213baf7aaa930b6c730aed2251e2a1051cab87b7..216018c760187db31e45206225ce8c7594a849d5 100644 (file)
@@ -20,6 +20,7 @@
 #include <net/route.h>
 #include <net/ip6_fib.h>
 #include <linux/if_vlan.h>
+#include <linux/rhashtable.h>
 
 #define BR_HASH_BITS 8
 #define BR_HASH_SIZE (1 << BR_HASH_BITS)
@@ -28,7 +29,6 @@
 
 #define BR_PORT_BITS   10
 #define BR_MAX_PORTS   (1<<BR_PORT_BITS)
-#define BR_VLAN_BITMAP_LEN     BITS_TO_LONGS(VLAN_N_VID)
 
 #define BR_VERSION     "2.3"
 
@@ -77,17 +77,61 @@ struct bridge_mcast_querier {
 };
 #endif
 
-struct net_port_vlans {
-       u16                             port_idx;
-       u16                             pvid;
+/**
+ * struct net_bridge_vlan - per-vlan entry
+ *
+ * @vnode: rhashtable member
+ * @vid: VLAN id
+ * @flags: bridge vlan flags
+ * @br: if MASTER flag set, this points to a bridge struct
+ * @port: if MASTER flag unset, this points to a port struct
+ * @refcnt: if MASTER flag set, this is bumped for each port referencing it
+ * @brvlan: if MASTER flag unset, this points to the global per-VLAN context
+ *          for this VLAN entry
+ * @vlist: sorted list of VLAN entries
+ * @rcu: used for entry destruction
+ *
+ * This structure is shared between the global per-VLAN entries contained in
+ * the bridge rhashtable and the local per-port per-VLAN entries contained in
+ * the port's rhashtable. The union entries should be interpreted depending on
+ * the entry flags that are set.
+ */
+struct net_bridge_vlan {
+       struct rhash_head               vnode;
+       u16                             vid;
+       u16                             flags;
        union {
-               struct net_bridge_port          *port;
-               struct net_bridge               *br;
-       }                               parent;
+               struct net_bridge       *br;
+               struct net_bridge_port  *port;
+       };
+       union {
+               atomic_t                refcnt;
+               struct net_bridge_vlan  *brvlan;
+       };
+       struct list_head                vlist;
+
        struct rcu_head                 rcu;
-       unsigned long                   vlan_bitmap[BR_VLAN_BITMAP_LEN];
-       unsigned long                   untagged_bitmap[BR_VLAN_BITMAP_LEN];
+};
+
+/**
+ * struct net_bridge_vlan_group
+ *
+ * @vlan_hash: VLAN entry rhashtable
+ * @vlan_list: sorted VLAN entry list
+ * @num_vlans: number of total VLAN entries
+ * @pvid: PVID VLAN id
+ *
+ * IMPORTANT: Be careful when checking if there're VLAN entries using list
+ *            primitives because the bridge can have entries in its list which
+ *            are just for global context but not for filtering, i.e. they have
+ *            the master flag set but not the brentry flag. If you have to check
+ *            if there're "real" entries in the bridge please test @num_vlans
+ */
+struct net_bridge_vlan_group {
+       struct rhashtable               vlan_hash;
+       struct list_head                vlan_list;
        u16                             num_vlans;
+       u16                             pvid;
 };
 
 struct net_bridge_fdb_entry
@@ -185,7 +229,7 @@ struct net_bridge_port
        struct netpoll                  *np;
 #endif
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
-       struct net_port_vlans __rcu     *vlan_info;
+       struct net_bridge_vlan_group    __rcu *vlgrp;
 #endif
 };
 
@@ -293,10 +337,10 @@ struct net_bridge
        struct kobject                  *ifobj;
        u32                             auto_cnt;
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
+       struct net_bridge_vlan_group    __rcu *vlgrp;
        u8                              vlan_enabled;
        __be16                          vlan_proto;
        u16                             default_pvid;
-       struct net_port_vlans __rcu     *vlan_info;
 #endif
 };
 
@@ -344,6 +388,31 @@ static inline int br_is_root_bridge(const struct net_bridge *br)
        return !memcmp(&br->bridge_id, &br->designated_root, 8);
 }
 
+/* check if a VLAN entry is global */
+static inline bool br_vlan_is_master(const struct net_bridge_vlan *v)
+{
+       return v->flags & BRIDGE_VLAN_INFO_MASTER;
+}
+
+/* check if a VLAN entry is used by the bridge */
+static inline bool br_vlan_is_brentry(const struct net_bridge_vlan *v)
+{
+       return v->flags & BRIDGE_VLAN_INFO_BRENTRY;
+}
+
+/* check if we should use the vlan entry, returns false if it's only context */
+static inline bool br_vlan_should_use(const struct net_bridge_vlan *v)
+{
+       if (br_vlan_is_master(v)) {
+               if (br_vlan_is_brentry(v))
+                       return true;
+               else
+                       return false;
+       }
+
+       return true;
+}
+
 /* br_device.c */
 void br_dev_setup(struct net_device *dev);
 void br_dev_delete(struct net_device *dev, struct list_head *list);
@@ -413,10 +482,10 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
 
 /* br_forward.c */
 void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
-int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb);
+int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb);
 void br_forward(const struct net_bridge_port *to,
                struct sk_buff *skb, struct sk_buff *skb0);
-int br_forward_finish(struct sock *sk, struct sk_buff *skb);
+int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
 void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, bool unicast);
 void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
                      struct sk_buff *skb2, bool unicast);
@@ -434,7 +503,7 @@ void br_port_flags_change(struct net_bridge_port *port, unsigned long mask);
 void br_manage_promisc(struct net_bridge *br);
 
 /* br_input.c */
-int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb);
+int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
 rx_handler_result_t br_handle_frame(struct sk_buff **pskb);
 
 static inline bool br_rx_handler_check_rcu(const struct net_device *dev)
@@ -601,18 +670,19 @@ static inline void br_mdb_uninit(void)
 
 /* br_vlan.c */
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
-bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
-                       struct sk_buff *skb, u16 *vid);
-bool br_allowed_egress(struct net_bridge *br, const struct net_port_vlans *v,
+bool br_allowed_ingress(const struct net_bridge *br,
+                       struct net_bridge_vlan_group *vg, struct sk_buff *skb,
+                       u16 *vid);
+bool br_allowed_egress(struct net_bridge_vlan_group *vg,
                       const struct sk_buff *skb);
 bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid);
 struct sk_buff *br_handle_vlan(struct net_bridge *br,
-                              const struct net_port_vlans *v,
+                              struct net_bridge_vlan_group *vg,
                               struct sk_buff *skb);
 int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags);
 int br_vlan_delete(struct net_bridge *br, u16 vid);
 void br_vlan_flush(struct net_bridge *br);
-bool br_vlan_find(struct net_bridge *br, u16 vid);
+struct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group *vg, u16 vid);
 void br_recalculate_fwd_mask(struct net_bridge *br);
 int __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
 int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
@@ -620,22 +690,35 @@ int __br_vlan_set_proto(struct net_bridge *br, __be16 proto);
 int br_vlan_set_proto(struct net_bridge *br, unsigned long val);
 int br_vlan_init(struct net_bridge *br);
 int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val);
+int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid);
 int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags);
 int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
 void nbp_vlan_flush(struct net_bridge_port *port);
-bool nbp_vlan_find(struct net_bridge_port *port, u16 vid);
 int nbp_vlan_init(struct net_bridge_port *port);
+int nbp_get_num_vlan_infos(struct net_bridge_port *p, u32 filter_mask);
+
+static inline struct net_bridge_vlan_group *br_vlan_group(
+                                       const struct net_bridge *br)
+{
+       return rtnl_dereference(br->vlgrp);
+}
 
-static inline struct net_port_vlans *br_get_vlan_info(
-                                               const struct net_bridge *br)
+static inline struct net_bridge_vlan_group *nbp_vlan_group(
+                                       const struct net_bridge_port *p)
 {
-       return rcu_dereference_rtnl(br->vlan_info);
+       return rtnl_dereference(p->vlgrp);
 }
 
-static inline struct net_port_vlans *nbp_get_vlan_info(
-                                               const struct net_bridge_port *p)
+static inline struct net_bridge_vlan_group *br_vlan_group_rcu(
+                                       const struct net_bridge *br)
 {
-       return rcu_dereference_rtnl(p->vlan_info);
+       return rcu_dereference(br->vlgrp);
+}
+
+static inline struct net_bridge_vlan_group *nbp_vlan_group_rcu(
+                                       const struct net_bridge_port *p)
+{
+       return rcu_dereference(p->vlgrp);
 }
 
 /* Since bridge now depends on 8021Q module, but the time bridge sees the
@@ -645,9 +728,9 @@ static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid)
 {
        int err = 0;
 
-       if (skb_vlan_tag_present(skb))
+       if (skb_vlan_tag_present(skb)) {
                *vid = skb_vlan_tag_get(skb) & VLAN_VID_MASK;
-       else {
+       else {
                *vid = 0;
                err = -EINVAL;
        }
@@ -655,13 +738,13 @@ static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid)
        return err;
 }
 
-static inline u16 br_get_pvid(const struct net_port_vlans *v)
+static inline u16 br_get_pvid(const struct net_bridge_vlan_group *vg)
 {
-       if (!v)
+       if (!vg)
                return 0;
 
        smp_rmb();
-       return v->pvid;
+       return vg->pvid;
 }
 
 static inline int br_vlan_enabled(struct net_bridge *br)
@@ -669,16 +752,15 @@ static inline int br_vlan_enabled(struct net_bridge *br)
        return br->vlan_enabled;
 }
 #else
-static inline bool br_allowed_ingress(struct net_bridge *br,
-                                     struct net_port_vlans *v,
+static inline bool br_allowed_ingress(const struct net_bridge *br,
+                                     struct net_bridge_vlan_group *vg,
                                      struct sk_buff *skb,
                                      u16 *vid)
 {
        return true;
 }
 
-static inline bool br_allowed_egress(struct net_bridge *br,
-                                    const struct net_port_vlans *v,
+static inline bool br_allowed_egress(struct net_bridge_vlan_group *vg,
                                     const struct sk_buff *skb)
 {
        return true;
@@ -691,7 +773,7 @@ static inline bool br_should_learn(struct net_bridge_port *p,
 }
 
 static inline struct sk_buff *br_handle_vlan(struct net_bridge *br,
-                                            const struct net_port_vlans *v,
+                                            struct net_bridge_vlan_group *vg,
                                             struct sk_buff *skb)
 {
        return skb;
@@ -711,11 +793,6 @@ static inline void br_vlan_flush(struct net_bridge *br)
 {
 }
 
-static inline bool br_vlan_find(struct net_bridge *br, u16 vid)
-{
-       return false;
-}
-
 static inline void br_recalculate_fwd_mask(struct net_bridge *br)
 {
 }
@@ -739,22 +816,12 @@ static inline void nbp_vlan_flush(struct net_bridge_port *port)
 {
 }
 
-static inline struct net_port_vlans *br_get_vlan_info(
-                                               const struct net_bridge *br)
-{
-       return NULL;
-}
-static inline struct net_port_vlans *nbp_get_vlan_info(
-                                               const struct net_bridge_port *p)
+static inline struct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group *vg,
+                                                  u16 vid)
 {
        return NULL;
 }
 
-static inline bool nbp_vlan_find(struct net_bridge_port *port, u16 vid)
-{
-       return false;
-}
-
 static inline int nbp_vlan_init(struct net_bridge_port *port)
 {
        return 0;
@@ -764,7 +831,8 @@ static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag)
 {
        return 0;
 }
-static inline u16 br_get_pvid(const struct net_port_vlans *v)
+
+static inline u16 br_get_pvid(const struct net_bridge_vlan_group *vg)
 {
        return 0;
 }
@@ -779,6 +847,37 @@ static inline int __br_vlan_filter_toggle(struct net_bridge *br,
 {
        return -EOPNOTSUPP;
 }
+
+static inline int nbp_get_num_vlan_infos(struct net_bridge_port *p,
+                                        u32 filter_mask)
+{
+       return 0;
+}
+
+static inline struct net_bridge_vlan_group *br_vlan_group(
+                                       const struct net_bridge *br)
+{
+       return NULL;
+}
+
+static inline struct net_bridge_vlan_group *nbp_vlan_group(
+                                       const struct net_bridge_port *p)
+{
+       return NULL;
+}
+
+static inline struct net_bridge_vlan_group *br_vlan_group_rcu(
+                                       const struct net_bridge *br)
+{
+       return NULL;
+}
+
+static inline struct net_bridge_vlan_group *nbp_vlan_group_rcu(
+                                       const struct net_bridge_port *p)
+{
+       return NULL;
+}
+
 #endif
 
 struct nf_br_ops {
@@ -808,6 +907,7 @@ void __br_set_forward_delay(struct net_bridge *br, unsigned long t);
 int br_set_forward_delay(struct net_bridge *br, unsigned long x);
 int br_set_hello_time(struct net_bridge *br, unsigned long x);
 int br_set_max_age(struct net_bridge *br, unsigned long x);
+int br_set_ageing_time(struct net_bridge *br, u32 ageing_time);
 
 
 /* br_stp_if.c */
index ed74ffaa851ff43d08c3edb4a158ab21af27e1a3..80c34d70218c0f9d2066016e3f5ba5fc56656490 100644 (file)
@@ -40,14 +40,15 @@ void br_log_state(const struct net_bridge_port *p)
 void br_set_state(struct net_bridge_port *p, unsigned int state)
 {
        struct switchdev_attr attr = {
-               .id = SWITCHDEV_ATTR_PORT_STP_STATE,
+               .id = SWITCHDEV_ATTR_ID_PORT_STP_STATE,
+               .flags = SWITCHDEV_F_DEFER,
                .u.stp_state = state,
        };
        int err;
 
        p->state = state;
        err = switchdev_port_attr_set(p->dev, &attr);
-       if (err && err != -EOPNOTSUPP)
+       if (err)
                br_warn(p->br, "error setting offload STP state on port %u(%s)\n",
                                (unsigned int) p->port_no, p->dev->name);
 }
@@ -566,6 +567,29 @@ int br_set_max_age(struct net_bridge *br, unsigned long val)
 
 }
 
+int br_set_ageing_time(struct net_bridge *br, u32 ageing_time)
+{
+       struct switchdev_attr attr = {
+               .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
+               .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
+               .u.ageing_time = ageing_time,
+       };
+       unsigned long t = clock_t_to_jiffies(ageing_time);
+       int err;
+
+       if (t < BR_MIN_AGEING_TIME || t > BR_MAX_AGEING_TIME)
+               return -ERANGE;
+
+       err = switchdev_port_attr_set(br->dev, &attr);
+       if (err)
+               return err;
+
+       br->ageing_time = t;
+       mod_timer(&br->gc_timer, jiffies);
+
+       return 0;
+}
+
 void __br_set_forward_delay(struct net_bridge *br, unsigned long t)
 {
        br->bridge_forward_delay = t;
@@ -576,17 +600,12 @@ void __br_set_forward_delay(struct net_bridge *br, unsigned long t)
 int br_set_forward_delay(struct net_bridge *br, unsigned long val)
 {
        unsigned long t = clock_t_to_jiffies(val);
-       int err = -ERANGE;
 
-       spin_lock_bh(&br->lock);
-       if (br->stp_enabled != BR_NO_STP &&
-           (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY))
-               goto unlock;
+       if (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY)
+               return -ERANGE;
 
+       spin_lock_bh(&br->lock);
        __br_set_forward_delay(br, t);
-       err = 0;
-
-unlock:
        spin_unlock_bh(&br->lock);
-       return err;
+       return 0;
 }
index 534fc4cd263ef21dd517402efde9585e6f720f83..5881fbc114a9ec88612fd08f35a73d2794d56890 100644 (file)
 
 #define LLC_RESERVE sizeof(struct llc_pdu_un)
 
+static int br_send_bpdu_finish(struct net *net, struct sock *sk,
+                              struct sk_buff *skb)
+{
+       return dev_queue_xmit(skb);
+}
+
 static void br_send_bpdu(struct net_bridge_port *p,
                         const unsigned char *data, int length)
 {
@@ -54,9 +60,9 @@ static void br_send_bpdu(struct net_bridge_port *p,
 
        skb_reset_mac_header(skb);
 
-       NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb,
-               NULL, skb->dev,
-               dev_queue_xmit_sk);
+       NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
+               dev_net(p->dev), NULL, skb, NULL, skb->dev,
+               br_send_bpdu_finish);
 }
 
 static inline void br_set_ticks(unsigned char *dest, int j)
index 4ca449a161320f7ef1c6f4864940e8557a7d18e3..fa53d7a89f485ac9039b6168db3063512c4d9d42 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/kmod.h>
 #include <linux/etherdevice.h>
 #include <linux/rtnetlink.h>
+#include <net/switchdev.h>
 
 #include "br_private.h"
 #include "br_private_stp.h"
@@ -35,11 +36,22 @@ static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
 /* called under bridge lock */
 void br_init_port(struct net_bridge_port *p)
 {
+       struct switchdev_attr attr = {
+               .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
+               .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER,
+               .u.ageing_time = p->br->ageing_time,
+       };
+       int err;
+
        p->port_id = br_make_port_id(p->priority, p->port_no);
        br_become_designated_port(p);
        br_set_state(p, BR_STATE_BLOCKING);
        p->topology_change_ack = 0;
        p->config_pending = 0;
+
+       err = switchdev_port_attr_set(p->dev, &attr);
+       if (err)
+               netdev_err(p->dev, "failed to set HW ageing time\n");
 }
 
 /* called under bridge lock */
index 4c97fc50fb704265bd2f51d96527cf3227a65118..8365bd53c42179dec15314fa99bf2346f729899f 100644 (file)
@@ -102,8 +102,15 @@ static ssize_t ageing_time_show(struct device *d,
 
 static int set_ageing_time(struct net_bridge *br, unsigned long val)
 {
-       br->ageing_time = clock_t_to_jiffies(val);
-       return 0;
+       int ret;
+
+       if (!rtnl_trylock())
+               return restart_syscall();
+
+       ret = br_set_ageing_time(br, val);
+       rtnl_unlock();
+
+       return ret;
 }
 
 static ssize_t ageing_time_store(struct device *d,
index 5f5a02b49a99617918c8f76ecf7858920152ba53..1394da63614a1d8a325ef44d5fa17849b5d82294 100644 (file)
 
 #include "br_private.h"
 
-static void __vlan_add_pvid(struct net_port_vlans *v, u16 vid)
+static inline int br_vlan_cmp(struct rhashtable_compare_arg *arg,
+                             const void *ptr)
 {
-       if (v->pvid == vid)
+       const struct net_bridge_vlan *vle = ptr;
+       u16 vid = *(u16 *)arg->key;
+
+       return vle->vid != vid;
+}
+
+static const struct rhashtable_params br_vlan_rht_params = {
+       .head_offset = offsetof(struct net_bridge_vlan, vnode),
+       .key_offset = offsetof(struct net_bridge_vlan, vid),
+       .key_len = sizeof(u16),
+       .nelem_hint = 3,
+       .locks_mul = 1,
+       .max_size = VLAN_N_VID,
+       .obj_cmpfn = br_vlan_cmp,
+       .automatic_shrinking = true,
+};
+
+static struct net_bridge_vlan *br_vlan_lookup(struct rhashtable *tbl, u16 vid)
+{
+       return rhashtable_lookup_fast(tbl, &vid, br_vlan_rht_params);
+}
+
+static void __vlan_add_pvid(struct net_bridge_vlan_group *vg, u16 vid)
+{
+       if (vg->pvid == vid)
                return;
 
        smp_wmb();
-       v->pvid = vid;
+       vg->pvid = vid;
 }
 
-static void __vlan_delete_pvid(struct net_port_vlans *v, u16 vid)
+static void __vlan_delete_pvid(struct net_bridge_vlan_group *vg, u16 vid)
 {
-       if (v->pvid != vid)
+       if (vg->pvid != vid)
                return;
 
        smp_wmb();
-       v->pvid = 0;
+       vg->pvid = 0;
 }
 
-static void __vlan_add_flags(struct net_port_vlans *v, u16 vid, u16 flags)
+static void __vlan_add_flags(struct net_bridge_vlan *v, u16 flags)
 {
+       struct net_bridge_vlan_group *vg;
+
+       if (br_vlan_is_master(v))
+               vg = br_vlan_group(v->br);
+       else
+               vg = nbp_vlan_group(v->port);
+
        if (flags & BRIDGE_VLAN_INFO_PVID)
-               __vlan_add_pvid(vvid);
+               __vlan_add_pvid(vg, v->vid);
        else
-               __vlan_delete_pvid(vvid);
+               __vlan_delete_pvid(vg, v->vid);
 
        if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
-               set_bit(vid, v->untagged_bitmap);
+               v->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
        else
-               clear_bit(vid, v->untagged_bitmap);
+               v->flags &= ~BRIDGE_VLAN_INFO_UNTAGGED;
 }
 
 static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br,
                          u16 vid, u16 flags)
 {
-       const struct net_device_ops *ops = dev->netdev_ops;
+       struct switchdev_obj_port_vlan v = {
+               .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
+               .flags = flags,
+               .vid_begin = vid,
+               .vid_end = vid,
+       };
        int err;
 
-       /* If driver uses VLAN ndo ops, use 8021q to install vid
-        * on device, otherwise try switchdev ops to install vid.
+       /* Try switchdev op first. In case it is not supported, fallback to
+        * 8021q add.
         */
+       err = switchdev_port_obj_add(dev, &v.obj);
+       if (err == -EOPNOTSUPP)
+               return vlan_vid_add(dev, br->vlan_proto, vid);
+       return err;
+}
 
-       if (ops->ndo_vlan_rx_add_vid) {
-               err = vlan_vid_add(dev, br->vlan_proto, vid);
-       } else {
-               struct switchdev_obj vlan_obj = {
-                       .id = SWITCHDEV_OBJ_PORT_VLAN,
-                       .u.vlan = {
-                               .flags = flags,
-                               .vid_begin = vid,
-                               .vid_end = vid,
-                       },
-               };
+static void __vlan_add_list(struct net_bridge_vlan *v)
+{
+       struct net_bridge_vlan_group *vg;
+       struct list_head *headp, *hpos;
+       struct net_bridge_vlan *vent;
 
-               err = switchdev_port_obj_add(dev, &vlan_obj);
-               if (err == -EOPNOTSUPP)
-                       err = 0;
+       if (br_vlan_is_master(v))
+               vg = br_vlan_group(v->br);
+       else
+               vg = nbp_vlan_group(v->port);
+
+       headp = &vg->vlan_list;
+       list_for_each_prev(hpos, headp) {
+               vent = list_entry(hpos, struct net_bridge_vlan, vlist);
+               if (v->vid < vent->vid)
+                       continue;
+               else
+                       break;
        }
+       list_add_rcu(&v->vlist, hpos);
+}
 
-       return err;
+static void __vlan_del_list(struct net_bridge_vlan *v)
+{
+       list_del_rcu(&v->vlist);
 }
 
-static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
+static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
+                         u16 vid)
 {
-       struct net_bridge_port *p = NULL;
-       struct net_bridge *br;
-       struct net_device *dev;
+       struct switchdev_obj_port_vlan v = {
+               .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
+               .vid_begin = vid,
+               .vid_end = vid,
+       };
        int err;
 
-       if (test_bit(vid, v->vlan_bitmap)) {
-               __vlan_add_flags(v, vid, flags);
+       /* Try switchdev op first. In case it is not supported, fallback to
+        * 8021q del.
+        */
+       err = switchdev_port_obj_del(dev, &v.obj);
+       if (err == -EOPNOTSUPP) {
+               vlan_vid_del(dev, br->vlan_proto, vid);
                return 0;
        }
+       return err;
+}
+
+/* Returns a master vlan, if it didn't exist it gets created. In all cases a
+ * a reference is taken to the master vlan before returning.
+ */
+static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid)
+{
+       struct net_bridge_vlan_group *vg;
+       struct net_bridge_vlan *masterv;
+
+       vg = br_vlan_group(br);
+       masterv = br_vlan_find(vg, vid);
+       if (!masterv) {
+               /* missing global ctx, create it now */
+               if (br_vlan_add(br, vid, 0))
+                       return NULL;
+               masterv = br_vlan_find(vg, vid);
+               if (WARN_ON(!masterv))
+                       return NULL;
+       }
+       atomic_inc(&masterv->refcnt);
+
+       return masterv;
+}
+
+static void br_vlan_put_master(struct net_bridge_vlan *masterv)
+{
+       struct net_bridge_vlan_group *vg;
+
+       if (!br_vlan_is_master(masterv))
+               return;
 
-       if (v->port_idx) {
-               p = v->parent.port;
+       vg = br_vlan_group(masterv->br);
+       if (atomic_dec_and_test(&masterv->refcnt)) {
+               rhashtable_remove_fast(&vg->vlan_hash,
+                                      &masterv->vnode, br_vlan_rht_params);
+               __vlan_del_list(masterv);
+               kfree_rcu(masterv, rcu);
+       }
+}
+
+/* This is the shared VLAN add function which works for both ports and bridge
+ * devices. There are four possible calls to this function in terms of the
+ * vlan entry type:
+ * 1. vlan is being added on a port (no master flags, global entry exists)
+ * 2. vlan is being added on a bridge (both master and brentry flags)
+ * 3. vlan is being added on a port, but a global entry didn't exist which
+ *    is being created right now (master flag set, brentry flag unset), the
+ *    global entry is used for global per-vlan features, but not for filtering
+ * 4. same as 3 but with both master and brentry flags set so the entry
+ *    will be used for filtering in both the port and the bridge
+ */
+static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
+{
+       struct net_bridge_vlan *masterv = NULL;
+       struct net_bridge_port *p = NULL;
+       struct net_bridge_vlan_group *vg;
+       struct net_device *dev;
+       struct net_bridge *br;
+       int err;
+
+       if (br_vlan_is_master(v)) {
+               br = v->br;
+               dev = br->dev;
+               vg = br_vlan_group(br);
+       } else {
+               p = v->port;
                br = p->br;
                dev = p->dev;
-       } else {
-               br = v->parent.br;
-               dev = br->dev;
+               vg = nbp_vlan_group(p);
        }
 
        if (p) {
@@ -93,116 +212,140 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
                 * This ensures tagged traffic enters the bridge when
                 * promiscuous mode is disabled by br_manage_promisc().
                 */
-               err = __vlan_vid_add(dev, br, vid, flags);
+               err = __vlan_vid_add(dev, br, v->vid, flags);
                if (err)
-                       return err;
-       }
+                       goto out;
+
+               /* need to work on the master vlan too */
+               if (flags & BRIDGE_VLAN_INFO_MASTER) {
+                       err = br_vlan_add(br, v->vid, flags |
+                                                     BRIDGE_VLAN_INFO_BRENTRY);
+                       if (err)
+                               goto out_filt;
+               }
 
-       err = br_fdb_insert(br, p, dev->dev_addr, vid);
-       if (err) {
-               br_err(br, "failed insert local address into bridge "
-                      "forwarding table\n");
-               goto out_filt;
+               masterv = br_vlan_get_master(br, v->vid);
+               if (!masterv)
+                       goto out_filt;
+               v->brvlan = masterv;
        }
 
-       set_bit(vid, v->vlan_bitmap);
-       v->num_vlans++;
-       __vlan_add_flags(v, vid, flags);
+       /* Add the dev mac and count the vlan only if it's usable */
+       if (br_vlan_should_use(v)) {
+               err = br_fdb_insert(br, p, dev->dev_addr, v->vid);
+               if (err) {
+                       br_err(br, "failed insert local address into bridge forwarding table\n");
+                       goto out_filt;
+               }
+               vg->num_vlans++;
+       }
 
-       return 0;
+       err = rhashtable_lookup_insert_fast(&vg->vlan_hash, &v->vnode,
+                                           br_vlan_rht_params);
+       if (err)
+               goto out_fdb_insert;
 
-out_filt:
-       if (p)
-               vlan_vid_del(dev, br->vlan_proto, vid);
+       __vlan_add_list(v);
+       __vlan_add_flags(v, flags);
+out:
        return err;
-}
 
-static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
-                         u16 vid)
-{
-       const struct net_device_ops *ops = dev->netdev_ops;
-       int err = 0;
-
-       /* If driver uses VLAN ndo ops, use 8021q to delete vid
-        * on device, otherwise try switchdev ops to delete vid.
-        */
-
-       if (ops->ndo_vlan_rx_kill_vid) {
-               vlan_vid_del(dev, br->vlan_proto, vid);
-       } else {
-               struct switchdev_obj vlan_obj = {
-                       .id = SWITCHDEV_OBJ_PORT_VLAN,
-                       .u.vlan = {
-                               .vid_begin = vid,
-                               .vid_end = vid,
-                       },
-               };
+out_fdb_insert:
+       if (br_vlan_should_use(v)) {
+               br_fdb_find_delete_local(br, p, dev->dev_addr, v->vid);
+               vg->num_vlans--;
+       }
 
-               err = switchdev_port_obj_del(dev, &vlan_obj);
-               if (err == -EOPNOTSUPP)
-                       err = 0;
+out_filt:
+       if (p) {
+               __vlan_vid_del(dev, br, v->vid);
+               if (masterv) {
+                       br_vlan_put_master(masterv);
+                       v->brvlan = NULL;
+               }
        }
 
-       return err;
+       goto out;
 }
 
-static int __vlan_del(struct net_port_vlans *v, u16 vid)
+static int __vlan_del(struct net_bridge_vlan *v)
 {
-       if (!test_bit(vid, v->vlan_bitmap))
-               return -EINVAL;
-
-       __vlan_delete_pvid(v, vid);
-       clear_bit(vid, v->untagged_bitmap);
+       struct net_bridge_vlan *masterv = v;
+       struct net_bridge_vlan_group *vg;
+       struct net_bridge_port *p = NULL;
+       int err = 0;
 
-       if (v->port_idx) {
-               struct net_bridge_port *p = v->parent.port;
-               int err;
+       if (br_vlan_is_master(v)) {
+               vg = br_vlan_group(v->br);
+       } else {
+               p = v->port;
+               vg = nbp_vlan_group(v->port);
+               masterv = v->brvlan;
+       }
 
-               err = __vlan_vid_del(p->dev, p->br, vid);
+       __vlan_delete_pvid(vg, v->vid);
+       if (p) {
+               err = __vlan_vid_del(p->dev, p->br, v->vid);
                if (err)
-                       return err;
+                       goto out;
        }
 
-       clear_bit(vid, v->vlan_bitmap);
-       v->num_vlans--;
-       if (bitmap_empty(v->vlan_bitmap, VLAN_N_VID)) {
-               if (v->port_idx)
-                       RCU_INIT_POINTER(v->parent.port->vlan_info, NULL);
-               else
-                       RCU_INIT_POINTER(v->parent.br->vlan_info, NULL);
+       if (br_vlan_should_use(v)) {
+               v->flags &= ~BRIDGE_VLAN_INFO_BRENTRY;
+               vg->num_vlans--;
+       }
+
+       if (masterv != v) {
+               rhashtable_remove_fast(&vg->vlan_hash, &v->vnode,
+                                      br_vlan_rht_params);
+               __vlan_del_list(v);
                kfree_rcu(v, rcu);
        }
-       return 0;
+
+       br_vlan_put_master(masterv);
+out:
+       return err;
 }
 
-static void __vlan_flush(struct net_port_vlans *v)
+static void __vlan_group_free(struct net_bridge_vlan_group *vg)
 {
-       smp_wmb();
-       v->pvid = 0;
-       bitmap_zero(v->vlan_bitmap, VLAN_N_VID);
-       if (v->port_idx)
-               RCU_INIT_POINTER(v->parent.port->vlan_info, NULL);
-       else
-               RCU_INIT_POINTER(v->parent.br->vlan_info, NULL);
-       kfree_rcu(v, rcu);
+       WARN_ON(!list_empty(&vg->vlan_list));
+       rhashtable_destroy(&vg->vlan_hash);
+       kfree(vg);
+}
+
+static void __vlan_flush(struct net_bridge_vlan_group *vg)
+{
+       struct net_bridge_vlan *vlan, *tmp;
+
+       __vlan_delete_pvid(vg, vg->pvid);
+       list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist)
+               __vlan_del(vlan);
 }
 
 struct sk_buff *br_handle_vlan(struct net_bridge *br,
-                              const struct net_port_vlans *pv,
+                              struct net_bridge_vlan_group *vg,
                               struct sk_buff *skb)
 {
+       struct net_bridge_vlan *v;
        u16 vid;
 
        /* If this packet was not filtered at input, let it pass */
        if (!BR_INPUT_SKB_CB(skb)->vlan_filtered)
                goto out;
 
-       /* Vlan filter table must be configured at this point.  The
+       /* At this point, we know that the frame was filtered and contains
+        * a valid vlan id.  If the vlan id has untagged flag set,
+        * send untagged; otherwise, send tagged.
+        */
+       br_vlan_get_tag(skb, &vid);
+       v = br_vlan_find(vg, vid);
+       /* Vlan entry must be configured at this point.  The
         * only exception is the bridge is set in promisc mode and the
         * packet is destined for the bridge device.  In this case
         * pass the packet as is.
         */
-       if (!pv) {
+       if (!v || !br_vlan_should_use(v)) {
                if ((br->dev->flags & IFF_PROMISC) && skb->dev == br->dev) {
                        goto out;
                } else {
@@ -210,13 +353,7 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
                        return NULL;
                }
        }
-
-       /* At this point, we know that the frame was filtered and contains
-        * a valid vlan id.  If the vlan id is set in the untagged bitmap,
-        * send untagged; otherwise, send tagged.
-        */
-       br_vlan_get_tag(skb, &vid);
-       if (test_bit(vid, pv->untagged_bitmap))
+       if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
                skb->vlan_tci = 0;
 
 out:
@@ -224,29 +361,13 @@ out:
 }
 
 /* Called under RCU */
-bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
-                       struct sk_buff *skb, u16 *vid)
+static bool __allowed_ingress(struct net_bridge_vlan_group *vg, __be16 proto,
+                             struct sk_buff *skb, u16 *vid)
 {
+       const struct net_bridge_vlan *v;
        bool tagged;
-       __be16 proto;
-
-       /* If VLAN filtering is disabled on the bridge, all packets are
-        * permitted.
-        */
-       if (!br->vlan_enabled) {
-               BR_INPUT_SKB_CB(skb)->vlan_filtered = false;
-               return true;
-       }
-
-       /* If there are no vlan in the permitted list, all packets are
-        * rejected.
-        */
-       if (!v)
-               goto drop;
 
        BR_INPUT_SKB_CB(skb)->vlan_filtered = true;
-       proto = br->vlan_proto;
-
        /* If vlan tx offload is disabled on bridge device and frame was
         * sent from vlan device on the bridge device, it does not have
         * HW accelerated vlan tag.
@@ -281,7 +402,7 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
        }
 
        if (!*vid) {
-               u16 pvid = br_get_pvid(v);
+               u16 pvid = br_get_pvid(vg);
 
                /* Frame had a tag with VID 0 or did not have a tag.
                 * See if pvid is set on this port.  That tells us which
@@ -309,29 +430,43 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
        }
 
        /* Frame had a valid vlan tag.  See if vlan is allowed */
-       if (test_bit(*vid, v->vlan_bitmap))
+       v = br_vlan_find(vg, *vid);
+       if (v && br_vlan_should_use(v))
                return true;
 drop:
        kfree_skb(skb);
        return false;
 }
 
+bool br_allowed_ingress(const struct net_bridge *br,
+                       struct net_bridge_vlan_group *vg, struct sk_buff *skb,
+                       u16 *vid)
+{
+       /* If VLAN filtering is disabled on the bridge, all packets are
+        * permitted.
+        */
+       if (!br->vlan_enabled) {
+               BR_INPUT_SKB_CB(skb)->vlan_filtered = false;
+               return true;
+       }
+
+       return __allowed_ingress(vg, br->vlan_proto, skb, vid);
+}
+
 /* Called under RCU. */
-bool br_allowed_egress(struct net_bridge *br,
-                      const struct net_port_vlans *v,
+bool br_allowed_egress(struct net_bridge_vlan_group *vg,
                       const struct sk_buff *skb)
 {
+       const struct net_bridge_vlan *v;
        u16 vid;
 
        /* If this packet was not filtered at input, let it pass */
        if (!BR_INPUT_SKB_CB(skb)->vlan_filtered)
                return true;
 
-       if (!v)
-               return false;
-
        br_vlan_get_tag(skb, &vid);
-       if (test_bit(vid, v->vlan_bitmap))
+       v = br_vlan_find(vg, vid);
+       if (v && br_vlan_should_use(v))
                return true;
 
        return false;
@@ -340,29 +475,29 @@ bool br_allowed_egress(struct net_bridge *br,
 /* Called under RCU */
 bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
 {
+       struct net_bridge_vlan_group *vg;
        struct net_bridge *br = p->br;
-       struct net_port_vlans *v;
 
        /* If filtering was disabled at input, let it pass. */
        if (!br->vlan_enabled)
                return true;
 
-       v = rcu_dereference(p->vlan_info);
-       if (!v)
+       vg = nbp_vlan_group_rcu(p);
+       if (!vg || !vg->num_vlans)
                return false;
 
        if (!br_vlan_get_tag(skb, vid) && skb->vlan_proto != br->vlan_proto)
                *vid = 0;
 
        if (!*vid) {
-               *vid = br_get_pvid(v);
+               *vid = br_get_pvid(vg);
                if (!*vid)
                        return false;
 
                return true;
        }
 
-       if (test_bit(*vid, v->vlan_bitmap))
+       if (br_vlan_find(vg, *vid))
                return true;
 
        return false;
@@ -373,31 +508,49 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
  */
 int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
 {
-       struct net_port_vlans *pv = NULL;
-       int err;
+       struct net_bridge_vlan_group *vg;
+       struct net_bridge_vlan *vlan;
+       int ret;
 
        ASSERT_RTNL();
 
-       pv = rtnl_dereference(br->vlan_info);
-       if (pv)
-               return __vlan_add(pv, vid, flags);
+       vg = br_vlan_group(br);
+       vlan = br_vlan_find(vg, vid);
+       if (vlan) {
+               if (!br_vlan_is_brentry(vlan)) {
+                       /* Trying to change flags of non-existent bridge vlan */
+                       if (!(flags & BRIDGE_VLAN_INFO_BRENTRY))
+                               return -EINVAL;
+                       /* It was only kept for port vlans, now make it real */
+                       ret = br_fdb_insert(br, NULL, br->dev->dev_addr,
+                                           vlan->vid);
+                       if (ret) {
+                               br_err(br, "failed insert local address into bridge forwarding table\n");
+                               return ret;
+                       }
+                       atomic_inc(&vlan->refcnt);
+                       vlan->flags |= BRIDGE_VLAN_INFO_BRENTRY;
+                       vg->num_vlans++;
+               }
+               __vlan_add_flags(vlan, flags);
+               return 0;
+       }
 
-       /* Create port vlan infomration
-        */
-       pv = kzalloc(sizeof(*pv), GFP_KERNEL);
-       if (!pv)
+       vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
+       if (!vlan)
                return -ENOMEM;
 
-       pv->parent.br = br;
-       err = __vlan_add(pv, vid, flags);
-       if (err)
-               goto out;
+       vlan->vid = vid;
+       vlan->flags = flags | BRIDGE_VLAN_INFO_MASTER;
+       vlan->flags &= ~BRIDGE_VLAN_INFO_PVID;
+       vlan->br = br;
+       if (flags & BRIDGE_VLAN_INFO_BRENTRY)
+               atomic_set(&vlan->refcnt, 1);
+       ret = __vlan_add(vlan, flags);
+       if (ret)
+               kfree(vlan);
 
-       rcu_assign_pointer(br->vlan_info, pv);
-       return 0;
-out:
-       kfree(pv);
-       return err;
+       return ret;
 }
 
 /* Must be protected by RTNL.
@@ -405,49 +558,41 @@ out:
  */
 int br_vlan_delete(struct net_bridge *br, u16 vid)
 {
-       struct net_port_vlans *pv;
+       struct net_bridge_vlan_group *vg;
+       struct net_bridge_vlan *v;
 
        ASSERT_RTNL();
 
-       pv = rtnl_dereference(br->vlan_info);
-       if (!pv)
-               return -EINVAL;
+       vg = br_vlan_group(br);
+       v = br_vlan_find(vg, vid);
+       if (!v || !br_vlan_is_brentry(v))
+               return -ENOENT;
 
        br_fdb_find_delete_local(br, NULL, br->dev->dev_addr, vid);
+       br_fdb_delete_by_port(br, NULL, vid, 0);
 
-       __vlan_del(pv, vid);
-       return 0;
+       return __vlan_del(v);
 }
 
 void br_vlan_flush(struct net_bridge *br)
 {
-       struct net_port_vlans *pv;
+       struct net_bridge_vlan_group *vg;
 
        ASSERT_RTNL();
-       pv = rtnl_dereference(br->vlan_info);
-       if (!pv)
-               return;
 
-       __vlan_flush(pv);
+       vg = br_vlan_group(br);
+       __vlan_flush(vg);
+       RCU_INIT_POINTER(br->vlgrp, NULL);
+       synchronize_rcu();
+       __vlan_group_free(vg);
 }
 
-bool br_vlan_find(struct net_bridge *br, u16 vid)
+struct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group *vg, u16 vid)
 {
-       struct net_port_vlans *pv;
-       bool found = false;
-
-       rcu_read_lock();
-       pv = rcu_dereference(br->vlan_info);
-
-       if (!pv)
-               goto out;
-
-       if (test_bit(vid, pv->vlan_bitmap))
-               found = true;
+       if (!vg)
+               return NULL;
 
-out:
-       rcu_read_unlock();
-       return found;
+       return br_vlan_lookup(&vg->vlan_hash, vid);
 }
 
 /* Must be protected by RTNL. */
@@ -505,21 +650,18 @@ int __br_vlan_set_proto(struct net_bridge *br, __be16 proto)
 {
        int err = 0;
        struct net_bridge_port *p;
-       struct net_port_vlans *pv;
+       struct net_bridge_vlan *vlan;
+       struct net_bridge_vlan_group *vg;
        __be16 oldproto;
-       u16 vid, errvid;
 
        if (br->vlan_proto == proto)
                return 0;
 
        /* Add VLANs for the new proto to the device filter. */
        list_for_each_entry(p, &br->port_list, list) {
-               pv = rtnl_dereference(p->vlan_info);
-               if (!pv)
-                       continue;
-
-               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
-                       err = vlan_vid_add(p->dev, proto, vid);
+               vg = nbp_vlan_group(p);
+               list_for_each_entry(vlan, &vg->vlan_list, vlist) {
+                       err = vlan_vid_add(p->dev, proto, vlan->vid);
                        if (err)
                                goto err_filt;
                }
@@ -533,28 +675,21 @@ int __br_vlan_set_proto(struct net_bridge *br, __be16 proto)
 
        /* Delete VLANs for the old proto from the device filter. */
        list_for_each_entry(p, &br->port_list, list) {
-               pv = rtnl_dereference(p->vlan_info);
-               if (!pv)
-                       continue;
-
-               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
-                       vlan_vid_del(p->dev, oldproto, vid);
+               vg = nbp_vlan_group(p);
+               list_for_each_entry(vlan, &vg->vlan_list, vlist)
+                       vlan_vid_del(p->dev, oldproto, vlan->vid);
        }
 
        return 0;
 
 err_filt:
-       errvid = vid;
-       for_each_set_bit(vid, pv->vlan_bitmap, errvid)
-               vlan_vid_del(p->dev, proto, vid);
+       list_for_each_entry_continue_reverse(vlan, &vg->vlan_list, vlist)
+               vlan_vid_del(p->dev, proto, vlan->vid);
 
        list_for_each_entry_continue_reverse(p, &br->port_list, list) {
-               pv = rtnl_dereference(p->vlan_info);
-               if (!pv)
-                       continue;
-
-               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
-                       vlan_vid_del(p->dev, proto, vid);
+               vg = nbp_vlan_group(p);
+               list_for_each_entry(vlan, &vg->vlan_list, vlist)
+                       vlan_vid_del(p->dev, proto, vlan->vid);
        }
 
        return err;
@@ -576,9 +711,19 @@ int br_vlan_set_proto(struct net_bridge *br, unsigned long val)
        return err;
 }
 
-static bool vlan_default_pvid(struct net_port_vlans *pv, u16 vid)
+static bool vlan_default_pvid(struct net_bridge_vlan_group *vg, u16 vid)
 {
-       return pv && vid == pv->pvid && test_bit(vid, pv->untagged_bitmap);
+       struct net_bridge_vlan *v;
+
+       if (vid != vg->pvid)
+               return false;
+
+       v = br_vlan_lookup(&vg->vlan_hash, vid);
+       if (v && br_vlan_should_use(v) &&
+           (v->flags & BRIDGE_VLAN_INFO_UNTAGGED))
+               return true;
+
+       return false;
 }
 
 static void br_vlan_disable_default_pvid(struct net_bridge *br)
@@ -589,24 +734,31 @@ static void br_vlan_disable_default_pvid(struct net_bridge *br)
        /* Disable default_pvid on all ports where it is still
         * configured.
         */
-       if (vlan_default_pvid(br_get_vlan_info(br), pvid))
+       if (vlan_default_pvid(br_vlan_group(br), pvid))
                br_vlan_delete(br, pvid);
 
        list_for_each_entry(p, &br->port_list, list) {
-               if (vlan_default_pvid(nbp_get_vlan_info(p), pvid))
+               if (vlan_default_pvid(nbp_vlan_group(p), pvid))
                        nbp_vlan_delete(p, pvid);
        }
 
        br->default_pvid = 0;
 }
 
-static int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
+int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
 {
+       const struct net_bridge_vlan *pvent;
+       struct net_bridge_vlan_group *vg;
        struct net_bridge_port *p;
        u16 old_pvid;
        int err = 0;
        unsigned long *changed;
 
+       if (!pvid) {
+               br_vlan_disable_default_pvid(br);
+               return 0;
+       }
+
        changed = kcalloc(BITS_TO_LONGS(BR_MAX_PORTS), sizeof(unsigned long),
                          GFP_KERNEL);
        if (!changed)
@@ -617,11 +769,14 @@ static int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
        /* Update default_pvid config only if we do not conflict with
         * user configuration.
         */
-       if ((!old_pvid || vlan_default_pvid(br_get_vlan_info(br), old_pvid)) &&
-           !br_vlan_find(br, pvid)) {
+       vg = br_vlan_group(br);
+       pvent = br_vlan_find(vg, pvid);
+       if ((!old_pvid || vlan_default_pvid(vg, old_pvid)) &&
+           (!pvent || !br_vlan_should_use(pvent))) {
                err = br_vlan_add(br, pvid,
                                  BRIDGE_VLAN_INFO_PVID |
-                                 BRIDGE_VLAN_INFO_UNTAGGED);
+                                 BRIDGE_VLAN_INFO_UNTAGGED |
+                                 BRIDGE_VLAN_INFO_BRENTRY);
                if (err)
                        goto out;
                br_vlan_delete(br, old_pvid);
@@ -632,9 +787,10 @@ static int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
                /* Update default_pvid config only if we do not conflict with
                 * user configuration.
                 */
+               vg = nbp_vlan_group(p);
                if ((old_pvid &&
-                    !vlan_default_pvid(nbp_get_vlan_info(p), old_pvid)) ||
-                   nbp_vlan_find(p, pvid))
+                    !vlan_default_pvid(vg, old_pvid)) ||
+                   br_vlan_find(vg, pvid))
                        continue;
 
                err = nbp_vlan_add(p, pvid,
@@ -668,7 +824,8 @@ err_port:
                if (old_pvid)
                        br_vlan_add(br, old_pvid,
                                    BRIDGE_VLAN_INFO_PVID |
-                                   BRIDGE_VLAN_INFO_UNTAGGED);
+                                   BRIDGE_VLAN_INFO_UNTAGGED |
+                                   BRIDGE_VLAN_INFO_BRENTRY);
                br_vlan_delete(br, pvid);
        }
        goto out;
@@ -694,12 +851,7 @@ int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val)
                err = -EPERM;
                goto unlock;
        }
-
-       if (!pvid)
-               br_vlan_disable_default_pvid(br);
-       else
-               err = __br_vlan_set_default_pvid(br, pvid);
-
+       err = __br_vlan_set_default_pvid(br, pvid);
 unlock:
        rtnl_unlock();
        return err;
@@ -707,10 +859,68 @@ unlock:
 
 int br_vlan_init(struct net_bridge *br)
 {
+       struct net_bridge_vlan_group *vg;
+       int ret = -ENOMEM;
+
+       vg = kzalloc(sizeof(*vg), GFP_KERNEL);
+       if (!vg)
+               goto out;
+       ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
+       if (ret)
+               goto err_rhtbl;
+       INIT_LIST_HEAD(&vg->vlan_list);
        br->vlan_proto = htons(ETH_P_8021Q);
        br->default_pvid = 1;
-       return br_vlan_add(br, 1,
-                          BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED);
+       rcu_assign_pointer(br->vlgrp, vg);
+       ret = br_vlan_add(br, 1,
+                         BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED |
+                         BRIDGE_VLAN_INFO_BRENTRY);
+       if (ret)
+               goto err_vlan_add;
+
+out:
+       return ret;
+
+err_vlan_add:
+       rhashtable_destroy(&vg->vlan_hash);
+err_rhtbl:
+       kfree(vg);
+
+       goto out;
+}
+
+int nbp_vlan_init(struct net_bridge_port *p)
+{
+       struct net_bridge_vlan_group *vg;
+       int ret = -ENOMEM;
+
+       vg = kzalloc(sizeof(struct net_bridge_vlan_group), GFP_KERNEL);
+       if (!vg)
+               goto out;
+
+       ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
+       if (ret)
+               goto err_rhtbl;
+       INIT_LIST_HEAD(&vg->vlan_list);
+       rcu_assign_pointer(p->vlgrp, vg);
+       if (p->br->default_pvid) {
+               ret = nbp_vlan_add(p, p->br->default_pvid,
+                                  BRIDGE_VLAN_INFO_PVID |
+                                  BRIDGE_VLAN_INFO_UNTAGGED);
+               if (ret)
+                       goto err_vlan_add;
+       }
+out:
+       return ret;
+
+err_vlan_add:
+       RCU_INIT_POINTER(p->vlgrp, NULL);
+       synchronize_rcu();
+       rhashtable_destroy(&vg->vlan_hash);
+err_rhtbl:
+       kfree(vg);
+
+       goto out;
 }
 
 /* Must be protected by RTNL.
@@ -718,35 +928,28 @@ int br_vlan_init(struct net_bridge *br)
  */
 int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
 {
-       struct net_port_vlans *pv = NULL;
-       int err;
+       struct net_bridge_vlan *vlan;
+       int ret;
 
        ASSERT_RTNL();
 
-       pv = rtnl_dereference(port->vlan_info);
-       if (pv)
-               return __vlan_add(pv, vid, flags);
-
-       /* Create port vlan infomration
-        */
-       pv = kzalloc(sizeof(*pv), GFP_KERNEL);
-       if (!pv) {
-               err = -ENOMEM;
-               goto clean_up;
+       vlan = br_vlan_find(nbp_vlan_group(port), vid);
+       if (vlan) {
+               __vlan_add_flags(vlan, flags);
+               return 0;
        }
 
-       pv->port_idx = port->port_no;
-       pv->parent.port = port;
-       err = __vlan_add(pv, vid, flags);
-       if (err)
-               goto clean_up;
+       vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
+       if (!vlan)
+               return -ENOMEM;
 
-       rcu_assign_pointer(port->vlan_info, pv);
-       return 0;
+       vlan->vid = vid;
+       vlan->port = port;
+       ret = __vlan_add(vlan, flags);
+       if (ret)
+               kfree(vlan);
 
-clean_up:
-       kfree(pv);
-       return err;
+       return ret;
 }
 
 /* Must be protected by RTNL.
@@ -754,61 +957,28 @@ clean_up:
  */
 int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
 {
-       struct net_port_vlans *pv;
+       struct net_bridge_vlan *v;
 
        ASSERT_RTNL();
 
-       pv = rtnl_dereference(port->vlan_info);
-       if (!pv)
-               return -EINVAL;
-
+       v = br_vlan_find(nbp_vlan_group(port), vid);
+       if (!v)
+               return -ENOENT;
        br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid);
        br_fdb_delete_by_port(port->br, port, vid, 0);
 
-       return __vlan_del(pv, vid);
+       return __vlan_del(v);
 }
 
 void nbp_vlan_flush(struct net_bridge_port *port)
 {
-       struct net_port_vlans *pv;
-       u16 vid;
+       struct net_bridge_vlan_group *vg;
 
        ASSERT_RTNL();
 
-       pv = rtnl_dereference(port->vlan_info);
-       if (!pv)
-               return;
-
-       for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
-               vlan_vid_del(port->dev, port->br->vlan_proto, vid);
-
-       __vlan_flush(pv);
-}
-
-bool nbp_vlan_find(struct net_bridge_port *port, u16 vid)
-{
-       struct net_port_vlans *pv;
-       bool found = false;
-
-       rcu_read_lock();
-       pv = rcu_dereference(port->vlan_info);
-
-       if (!pv)
-               goto out;
-
-       if (test_bit(vid, pv->vlan_bitmap))
-               found = true;
-
-out:
-       rcu_read_unlock();
-       return found;
-}
-
-int nbp_vlan_init(struct net_bridge_port *p)
-{
-       return p->br->default_pvid ?
-                       nbp_vlan_add(p, p->br->default_pvid,
-                                    BRIDGE_VLAN_INFO_PVID |
-                                    BRIDGE_VLAN_INFO_UNTAGGED) :
-                       0;
+       vg = nbp_vlan_group(port);
+       __vlan_flush(vg);
+       RCU_INIT_POINTER(port->vlgrp, NULL);
+       synchronize_rcu();
+       __vlan_group_free(vg);
 }
index 17f2e4bc2a29fcbb6d40dc408d5472fb67c5721c..0ad639a9614265f10ce76a2fe8777781dacfe798 100644 (file)
@@ -180,7 +180,7 @@ ebt_log_tg(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct ebt_log_info *info = par->targinfo;
        struct nf_loginfo li;
-       struct net *net = dev_net(par->in ? par->in : par->out);
+       struct net *net = par->net;
 
        li.type = NF_LOG_TYPE_LOG;
        li.u.log.level = info->loglevel;
index 59ac7952010de4b3f76901521fdd192ab1f748b0..54816150608e0e0f2fd45757a93d42f07a30a42d 100644 (file)
@@ -24,7 +24,7 @@ ebt_nflog_tg(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct ebt_nflog_info *info = par->targinfo;
        struct nf_loginfo li;
-       struct net *net = dev_net(par->in ? par->in : par->out);
+       struct net *net = par->net;
 
        li.type = NF_LOG_TYPE_ULOG;
        li.u.ulog.copy_len = info->len;
index d2cdf5d6e98cee057ebc838c64a7a1e66d6869c6..ec94c6f1ae881461bb1c72fc7a8965c335de73fe 100644 (file)
@@ -50,10 +50,14 @@ static const struct ebt_table broute_table = {
 
 static int ebt_broute(struct sk_buff *skb)
 {
+       struct nf_hook_state state;
        int ret;
 
-       ret = ebt_do_table(NF_BR_BROUTING, skb, skb->dev, NULL,
-                          dev_net(skb->dev)->xt.broute_table);
+       nf_hook_state_init(&state, NULL, NF_BR_BROUTING, INT_MIN,
+                          NFPROTO_BRIDGE, skb->dev, NULL, NULL,
+                          dev_net(skb->dev), NULL);
+
+       ret = ebt_do_table(skb, &state, state.net->xt.broute_table);
        if (ret == NF_DROP)
                return 1; /* route it */
        return 0; /* bridge it */
index 8a3f63b2e8073d8081df5fbaac3bf63c348c0447..32eccd101f2681971e14cacb758a847d421a02ff 100644 (file)
@@ -57,39 +57,34 @@ static const struct ebt_table frame_filter = {
 };
 
 static unsigned int
-ebt_in_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ebt_in_hook(void *priv, struct sk_buff *skb,
            const struct nf_hook_state *state)
 {
-       return ebt_do_table(ops->hooknum, skb, state->in, state->out,
-                           dev_net(state->in)->xt.frame_filter);
+       return ebt_do_table(skb, state, state->net->xt.frame_filter);
 }
 
 static unsigned int
-ebt_out_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ebt_out_hook(void *priv, struct sk_buff *skb,
             const struct nf_hook_state *state)
 {
-       return ebt_do_table(ops->hooknum, skb, state->in, state->out,
-                           dev_net(state->out)->xt.frame_filter);
+       return ebt_do_table(skb, state, state->net->xt.frame_filter);
 }
 
 static struct nf_hook_ops ebt_ops_filter[] __read_mostly = {
        {
                .hook           = ebt_in_hook,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_BRIDGE,
                .hooknum        = NF_BR_LOCAL_IN,
                .priority       = NF_BR_PRI_FILTER_BRIDGED,
        },
        {
                .hook           = ebt_in_hook,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_BRIDGE,
                .hooknum        = NF_BR_FORWARD,
                .priority       = NF_BR_PRI_FILTER_BRIDGED,
        },
        {
                .hook           = ebt_out_hook,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_BRIDGE,
                .hooknum        = NF_BR_LOCAL_OUT,
                .priority       = NF_BR_PRI_FILTER_OTHER,
index c5ef5b1ab6786814830983d76ef46c6fd0051f51..ec55358f00c8a9672b0465ed6dee9d5f03836b5f 100644 (file)
@@ -57,39 +57,34 @@ static struct ebt_table frame_nat = {
 };
 
 static unsigned int
-ebt_nat_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ebt_nat_in(void *priv, struct sk_buff *skb,
           const struct nf_hook_state *state)
 {
-       return ebt_do_table(ops->hooknum, skb, state->in, state->out,
-                           dev_net(state->in)->xt.frame_nat);
+       return ebt_do_table(skb, state, state->net->xt.frame_nat);
 }
 
 static unsigned int
-ebt_nat_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ebt_nat_out(void *priv, struct sk_buff *skb,
            const struct nf_hook_state *state)
 {
-       return ebt_do_table(ops->hooknum, skb, state->in, state->out,
-                           dev_net(state->out)->xt.frame_nat);
+       return ebt_do_table(skb, state, state->net->xt.frame_nat);
 }
 
 static struct nf_hook_ops ebt_ops_nat[] __read_mostly = {
        {
                .hook           = ebt_nat_out,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_BRIDGE,
                .hooknum        = NF_BR_LOCAL_OUT,
                .priority       = NF_BR_PRI_NAT_DST_OTHER,
        },
        {
                .hook           = ebt_nat_out,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_BRIDGE,
                .hooknum        = NF_BR_POST_ROUTING,
                .priority       = NF_BR_PRI_NAT_SRC,
        },
        {
                .hook           = ebt_nat_in,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_BRIDGE,
                .hooknum        = NF_BR_PRE_ROUTING,
                .priority       = NF_BR_PRI_NAT_DST_BRIDGED,
index 48b6b01295de4d39987f82a39cfd4c6503155f5a..f46ca417bf2d147118f8d37c67d9bffbee59724f 100644 (file)
@@ -183,10 +183,11 @@ struct ebt_entry *ebt_next_entry(const struct ebt_entry *entry)
 }
 
 /* Do some firewalling */
-unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
-   const struct net_device *in, const struct net_device *out,
-   struct ebt_table *table)
+unsigned int ebt_do_table(struct sk_buff *skb,
+                         const struct nf_hook_state *state,
+                         struct ebt_table *table)
 {
+       unsigned int hook = state->hook;
        int i, nentries;
        struct ebt_entry *point;
        struct ebt_counter *counter_base, *cb_base;
@@ -199,8 +200,9 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
        struct xt_action_param acpar;
 
        acpar.family  = NFPROTO_BRIDGE;
-       acpar.in      = in;
-       acpar.out     = out;
+       acpar.net     = state->net;
+       acpar.in      = state->in;
+       acpar.out     = state->out;
        acpar.hotdrop = false;
        acpar.hooknum = hook;
 
@@ -220,7 +222,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
        base = private->entries;
        i = 0;
        while (i < nentries) {
-               if (ebt_basic_match(point, skb, in, out))
+               if (ebt_basic_match(point, skb, state->in, state->out))
                        goto letscontinue;
 
                if (EBT_MATCH_ITERATE(point, ebt_do_match, skb, &acpar) != 0)
index a343e62442b1304eca4e23abeb2a3df92283552e..62f6b1b195897fd2d982b4849c7e139dc12ddfc2 100644 (file)
@@ -65,31 +65,29 @@ int nft_bridge_ip6hdr_validate(struct sk_buff *skb)
 EXPORT_SYMBOL_GPL(nft_bridge_ip6hdr_validate);
 
 static inline void nft_bridge_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
-                                              const struct nf_hook_ops *ops,
                                               struct sk_buff *skb,
                                               const struct nf_hook_state *state)
 {
        if (nft_bridge_iphdr_validate(skb))
-               nft_set_pktinfo_ipv4(pkt, ops, skb, state);
+               nft_set_pktinfo_ipv4(pkt, skb, state);
        else
-               nft_set_pktinfo(pkt, ops, skb, state);
+               nft_set_pktinfo(pkt, skb, state);
 }
 
 static inline void nft_bridge_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
-                                              const struct nf_hook_ops *ops,
                                               struct sk_buff *skb,
                                               const struct nf_hook_state *state)
 {
 #if IS_ENABLED(CONFIG_IPV6)
        if (nft_bridge_ip6hdr_validate(skb) &&
-           nft_set_pktinfo_ipv6(pkt, ops, skb, state) == 0)
+           nft_set_pktinfo_ipv6(pkt, skb, state) == 0)
                return;
 #endif
-       nft_set_pktinfo(pkt, ops, skb, state);
+       nft_set_pktinfo(pkt, skb, state);
 }
 
 static unsigned int
-nft_do_chain_bridge(const struct nf_hook_ops *ops,
+nft_do_chain_bridge(void *priv,
                    struct sk_buff *skb,
                    const struct nf_hook_state *state)
 {
@@ -97,17 +95,17 @@ nft_do_chain_bridge(const struct nf_hook_ops *ops,
 
        switch (eth_hdr(skb)->h_proto) {
        case htons(ETH_P_IP):
-               nft_bridge_set_pktinfo_ipv4(&pkt, ops, skb, state);
+               nft_bridge_set_pktinfo_ipv4(&pkt, skb, state);
                break;
        case htons(ETH_P_IPV6):
-               nft_bridge_set_pktinfo_ipv6(&pkt, ops, skb, state);
+               nft_bridge_set_pktinfo_ipv6(&pkt, skb, state);
                break;
        default:
-               nft_set_pktinfo(&pkt, ops, skb, state);
+               nft_set_pktinfo(&pkt, skb, state);
                break;
        }
 
-       return nft_do_chain(&pkt, ops);
+       return nft_do_chain(&pkt, priv);
 }
 
 static struct nft_af_info nft_af_bridge __read_mostly = {
index 858d848564ee93a4fa51534a8db512652e93c792..fdba3d9fbff3bac00b8acc68d1eaeda2e492e820 100644 (file)
@@ -261,7 +261,6 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr,
                                   const struct nft_pktinfo *pkt)
 {
        struct nft_reject *priv = nft_expr_priv(expr);
-       struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out);
        const unsigned char *dest = eth_hdr(pkt->skb)->h_dest;
 
        if (is_broadcast_ether_addr(dest) ||
@@ -273,16 +272,16 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr,
                switch (priv->type) {
                case NFT_REJECT_ICMP_UNREACH:
                        nft_reject_br_send_v4_unreach(pkt->skb, pkt->in,
-                                                     pkt->ops->hooknum,
+                                                     pkt->hook,
                                                      priv->icmp_code);
                        break;
                case NFT_REJECT_TCP_RST:
                        nft_reject_br_send_v4_tcp_reset(pkt->skb, pkt->in,
-                                                       pkt->ops->hooknum);
+                                                       pkt->hook);
                        break;
                case NFT_REJECT_ICMPX_UNREACH:
                        nft_reject_br_send_v4_unreach(pkt->skb, pkt->in,
-                                                     pkt->ops->hooknum,
+                                                     pkt->hook,
                                                      nft_reject_icmp_code(priv->icmp_code));
                        break;
                }
@@ -290,17 +289,17 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr,
        case htons(ETH_P_IPV6):
                switch (priv->type) {
                case NFT_REJECT_ICMP_UNREACH:
-                       nft_reject_br_send_v6_unreach(net, pkt->skb, pkt->in,
-                                                     pkt->ops->hooknum,
+                       nft_reject_br_send_v6_unreach(pkt->net, pkt->skb,
+                                                     pkt->in, pkt->hook,
                                                      priv->icmp_code);
                        break;
                case NFT_REJECT_TCP_RST:
-                       nft_reject_br_send_v6_tcp_reset(net, pkt->skb, pkt->in,
-                                                       pkt->ops->hooknum);
+                       nft_reject_br_send_v6_tcp_reset(pkt->net, pkt->skb,
+                                                       pkt->in, pkt->hook);
                        break;
                case NFT_REJECT_ICMPX_UNREACH:
-                       nft_reject_br_send_v6_unreach(net, pkt->skb, pkt->in,
-                                                     pkt->ops->hooknum,
+                       nft_reject_br_send_v6_unreach(pkt->net, pkt->skb,
+                                                     pkt->in, pkt->hook,
                                                      nft_reject_icmpv6_code(priv->icmp_code));
                        break;
                }
index a1ba6875c2a2073d55b6f797e16d88baed3af3d1..6863310d6973ba616323f33a5d57a04a583a5430 100644 (file)
@@ -96,7 +96,7 @@ struct bcm_op {
        canid_t can_id;
        u32 flags;
        unsigned long frames_abs, frames_filtered;
-       struct timeval ival1, ival2;
+       struct bcm_timeval ival1, ival2;
        struct hrtimer timer, thrtimer;
        struct tasklet_struct tsklet, thrtsklet;
        ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
@@ -131,6 +131,11 @@ static inline struct bcm_sock *bcm_sk(const struct sock *sk)
        return (struct bcm_sock *)sk;
 }
 
+static inline ktime_t bcm_timeval_to_ktime(struct bcm_timeval tv)
+{
+       return ktime_set(tv.tv_sec, tv.tv_usec * NSEC_PER_USEC);
+}
+
 #define CFSIZ sizeof(struct can_frame)
 #define OPSIZ sizeof(struct bcm_op)
 #define MHSIZ sizeof(struct bcm_msg_head)
@@ -953,8 +958,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
                op->count = msg_head->count;
                op->ival1 = msg_head->ival1;
                op->ival2 = msg_head->ival2;
-               op->kt_ival1 = timeval_to_ktime(msg_head->ival1);
-               op->kt_ival2 = timeval_to_ktime(msg_head->ival2);
+               op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
+               op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
 
                /* disable an active timer due to zero values? */
                if (!op->kt_ival1.tv64 && !op->kt_ival2.tv64)
@@ -1134,8 +1139,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
                        /* set timer value */
                        op->ival1 = msg_head->ival1;
                        op->ival2 = msg_head->ival2;
-                       op->kt_ival1 = timeval_to_ktime(msg_head->ival1);
-                       op->kt_ival2 = timeval_to_ktime(msg_head->ival2);
+                       op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
+                       op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
 
                        /* disable an active timer due to zero value? */
                        if (!op->kt_ival1.tv64)
index 6bb6470f5b7bbbfaa60de8e33277b63aadbdb641..8ce3f74cd6b99d68445065a96772a388c927fb7d 100644 (file)
@@ -99,6 +99,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/stat.h>
 #include <net/dst.h>
+#include <net/dst_metadata.h>
 #include <net/pkt_sched.h>
 #include <net/checksum.h>
 #include <net/xfrm.h>
@@ -681,6 +682,32 @@ int dev_get_iflink(const struct net_device *dev)
 }
 EXPORT_SYMBOL(dev_get_iflink);
 
+/**
+ *     dev_fill_metadata_dst - Retrieve tunnel egress information.
+ *     @dev: targeted interface
+ *     @skb: The packet.
+ *
+ *     For better visibility of tunnel traffic OVS needs to retrieve
+ *     egress tunnel information for a packet. Following API allows
+ *     user to get this info.
+ */
+int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
+{
+       struct ip_tunnel_info *info;
+
+       if (!dev->netdev_ops  || !dev->netdev_ops->ndo_fill_metadata_dst)
+               return -EINVAL;
+
+       info = skb_tunnel_info_unclone(skb);
+       if (!info)
+               return -ENOMEM;
+       if (unlikely(!(info->mode & IP_TUNNEL_INFO_TX)))
+               return -EINVAL;
+
+       return dev->netdev_ops->ndo_fill_metadata_dst(dev, skb);
+}
+EXPORT_SYMBOL_GPL(dev_fill_metadata_dst);
+
 /**
  *     __dev_get_by_name       - find a device by its name
  *     @net: the applicable net namespace
@@ -2915,9 +2942,11 @@ EXPORT_SYMBOL(xmit_recursion);
 
 /**
  *     dev_loopback_xmit - loop back @skb
+ *     @net: network namespace this loopback is happening in
+ *     @sk:  sk needed to be a netfilter okfn
  *     @skb: buffer to transmit
  */
-int dev_loopback_xmit(struct sock *sk, struct sk_buff *skb)
+int dev_loopback_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        skb_reset_mac_header(skb);
        __skb_pull(skb, skb_network_offset(skb));
@@ -2972,6 +3001,7 @@ static u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb)
                        new_index = skb_tx_hash(dev, skb);
 
                if (queue_index != new_index && sk &&
+                   sk_fullsock(sk) &&
                    rcu_access_pointer(sk->sk_dst_cache))
                        sk_tx_queue_set(sk, new_index);
 
@@ -3143,11 +3173,11 @@ out:
        return rc;
 }
 
-int dev_queue_xmit_sk(struct sock *sk, struct sk_buff *skb)
+int dev_queue_xmit(struct sk_buff *skb)
 {
        return __dev_queue_xmit(skb, NULL);
 }
-EXPORT_SYMBOL(dev_queue_xmit_sk);
+EXPORT_SYMBOL(dev_queue_xmit);
 
 int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv)
 {
@@ -3668,6 +3698,14 @@ static inline struct sk_buff *handle_ing(struct sk_buff *skb,
        case TC_ACT_QUEUED:
                kfree_skb(skb);
                return NULL;
+       case TC_ACT_REDIRECT:
+               /* skb_mac_header check was done by cls/act_bpf, so
+                * we can safely push the L2 header back before
+                * redirecting to another netdev
+                */
+               __skb_push(skb, skb->mac_len);
+               skb_do_redirect(skb);
+               return NULL;
        default:
                break;
        }
@@ -3982,13 +4020,13 @@ static int netif_receive_skb_internal(struct sk_buff *skb)
  *     NET_RX_SUCCESS: no congestion
  *     NET_RX_DROP: packet was dropped
  */
-int netif_receive_skb_sk(struct sock *sk, struct sk_buff *skb)
+int netif_receive_skb(struct sk_buff *skb)
 {
        trace_netif_receive_skb_entry(skb);
 
        return netif_receive_skb_internal(skb);
 }
-EXPORT_SYMBOL(netif_receive_skb_sk);
+EXPORT_SYMBOL(netif_receive_skb);
 
 /* Network device is going away, flush any packets still pending
  * Called with irqs disabled.
@@ -4857,8 +4895,7 @@ struct netdev_adjacent {
        struct rcu_head rcu;
 };
 
-static struct netdev_adjacent *__netdev_find_adj(struct net_device *dev,
-                                                struct net_device *adj_dev,
+static struct netdev_adjacent *__netdev_find_adj(struct net_device *adj_dev,
                                                 struct list_head *adj_list)
 {
        struct netdev_adjacent *adj;
@@ -4884,7 +4921,7 @@ bool netdev_has_upper_dev(struct net_device *dev,
 {
        ASSERT_RTNL();
 
-       return __netdev_find_adj(dev, upper_dev, &dev->all_adj_list.upper);
+       return __netdev_find_adj(upper_dev, &dev->all_adj_list.upper);
 }
 EXPORT_SYMBOL(netdev_has_upper_dev);
 
@@ -5146,7 +5183,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev,
        struct netdev_adjacent *adj;
        int ret;
 
-       adj = __netdev_find_adj(dev, adj_dev, dev_list);
+       adj = __netdev_find_adj(adj_dev, dev_list);
 
        if (adj) {
                adj->ref_nr++;
@@ -5202,7 +5239,7 @@ static void __netdev_adjacent_dev_remove(struct net_device *dev,
 {
        struct netdev_adjacent *adj;
 
-       adj = __netdev_find_adj(dev, adj_dev, dev_list);
+       adj = __netdev_find_adj(adj_dev, dev_list);
 
        if (!adj) {
                pr_err("tried to remove device %s from %s\n",
@@ -5323,10 +5360,10 @@ static int __netdev_upper_dev_link(struct net_device *dev,
                return -EBUSY;
 
        /* To prevent loops, check if dev is not upper device to upper_dev. */
-       if (__netdev_find_adj(upper_dev, dev, &upper_dev->all_adj_list.upper))
+       if (__netdev_find_adj(dev, &upper_dev->all_adj_list.upper))
                return -EBUSY;
 
-       if (__netdev_find_adj(dev, upper_dev, &dev->adj_list.upper))
+       if (__netdev_find_adj(upper_dev, &dev->adj_list.upper))
                return -EEXIST;
 
        if (master && netdev_master_upper_dev_get(dev))
@@ -5336,6 +5373,12 @@ static int __netdev_upper_dev_link(struct net_device *dev,
        changeupper_info.master = master;
        changeupper_info.linking = true;
 
+       ret = call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, dev,
+                                           &changeupper_info.info);
+       ret = notifier_to_errno(ret);
+       if (ret)
+               return ret;
+
        ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, private,
                                                   master);
        if (ret)
@@ -5478,6 +5521,9 @@ void netdev_upper_dev_unlink(struct net_device *dev,
        changeupper_info.master = netdev_master_upper_dev_get(dev) == upper_dev;
        changeupper_info.linking = false;
 
+       call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, dev,
+                                     &changeupper_info.info);
+
        __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev);
 
        /* Here is the tricky part. We must remove all dev's lower
@@ -5604,7 +5650,7 @@ void *netdev_lower_dev_get_private(struct net_device *dev,
 
        if (!lower_dev)
                return NULL;
-       lower = __netdev_find_adj(dev, lower_dev, &dev->adj_list.lower);
+       lower = __netdev_find_adj(lower_dev, &dev->adj_list.lower);
        if (!lower)
                return NULL;
 
@@ -6242,6 +6288,48 @@ static void rollback_registered(struct net_device *dev)
        list_del(&single);
 }
 
+static netdev_features_t netdev_sync_upper_features(struct net_device *lower,
+       struct net_device *upper, netdev_features_t features)
+{
+       netdev_features_t upper_disables = NETIF_F_UPPER_DISABLES;
+       netdev_features_t feature;
+       int feature_bit;
+
+       for_each_netdev_feature(&upper_disables, feature_bit) {
+               feature = __NETIF_F_BIT(feature_bit);
+               if (!(upper->wanted_features & feature)
+                   && (features & feature)) {
+                       netdev_dbg(lower, "Dropping feature %pNF, upper dev %s has it off.\n",
+                                  &feature, upper->name);
+                       features &= ~feature;
+               }
+       }
+
+       return features;
+}
+
+static void netdev_sync_lower_features(struct net_device *upper,
+       struct net_device *lower, netdev_features_t features)
+{
+       netdev_features_t upper_disables = NETIF_F_UPPER_DISABLES;
+       netdev_features_t feature;
+       int feature_bit;
+
+       for_each_netdev_feature(&upper_disables, feature_bit) {
+               feature = __NETIF_F_BIT(feature_bit);
+               if (!(features & feature) && (lower->features & feature)) {
+                       netdev_dbg(upper, "Disabling feature %pNF on lower dev %s.\n",
+                                  &feature, lower->name);
+                       lower->wanted_features &= ~feature;
+                       netdev_update_features(lower);
+
+                       if (unlikely(lower->features & feature))
+                               netdev_WARN(upper, "failed to disable %pNF on %s!\n",
+                                           &feature, lower->name);
+               }
+       }
+}
+
 static netdev_features_t netdev_fix_features(struct net_device *dev,
        netdev_features_t features)
 {
@@ -6311,7 +6399,9 @@ static netdev_features_t netdev_fix_features(struct net_device *dev,
 
 int __netdev_update_features(struct net_device *dev)
 {
+       struct net_device *upper, *lower;
        netdev_features_t features;
+       struct list_head *iter;
        int err = 0;
 
        ASSERT_RTNL();
@@ -6324,6 +6414,10 @@ int __netdev_update_features(struct net_device *dev)
        /* driver might be less strict about feature dependencies */
        features = netdev_fix_features(dev, features);
 
+       /* some features can't be enabled if they're off an an upper device */
+       netdev_for_each_upper_dev_rcu(dev, upper, iter)
+               features = netdev_sync_upper_features(dev, upper, features);
+
        if (dev->features == features)
                return 0;
 
@@ -6340,6 +6434,12 @@ int __netdev_update_features(struct net_device *dev)
                return -1;
        }
 
+       /* some features must be disabled on lower devices when disabled
+        * on an upper device (think: bonding master or bridge)
+        */
+       netdev_for_each_lower_dev(dev, lower, iter)
+               netdev_sync_lower_features(dev, lower, features);
+
        if (!err)
                dev->features = features;
 
index 0771c8cb9307c8c1b8edd3742fda766db5e59a97..2a1818065e126d4645076f391e4dd689051e9d43 100644 (file)
@@ -144,12 +144,12 @@ loop:
        mutex_unlock(&dst_gc_mutex);
 }
 
-int dst_discard_sk(struct sock *sk, struct sk_buff *skb)
+int dst_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        kfree_skb(skb);
        return 0;
 }
-EXPORT_SYMBOL(dst_discard_sk);
+EXPORT_SYMBOL(dst_discard_out);
 
 const u32 dst_default_metrics[RTAX_MAX + 1] = {
        /* This initializer is needed to force linker to place this variable
@@ -177,7 +177,7 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops,
        dst->xfrm = NULL;
 #endif
        dst->input = dst_discard;
-       dst->output = dst_discard_sk;
+       dst->output = dst_discard_out;
        dst->error = 0;
        dst->obsolete = initial_obsolete;
        dst->header_len = 0;
@@ -224,7 +224,7 @@ static void ___dst_free(struct dst_entry *dst)
         */
        if (dst->dev == NULL || !(dst->dev->flags&IFF_UP)) {
                dst->input = dst_discard;
-               dst->output = dst_discard_sk;
+               dst->output = dst_discard_out;
        }
        dst->obsolete = DST_OBSOLETE_DEAD;
 }
@@ -352,7 +352,7 @@ static struct dst_ops md_dst_ops = {
        .family =               AF_UNSPEC,
 };
 
-static int dst_md_discard_sk(struct sock *sk, struct sk_buff *skb)
+static int dst_md_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        WARN_ONCE(1, "Attempting to call output on metadata dst\n");
        kfree_skb(skb);
@@ -375,7 +375,7 @@ static void __metadata_dst_init(struct metadata_dst *md_dst, u8 optslen)
                 DST_METADATA | DST_NOCACHE | DST_NOCOUNT);
 
        dst->input = dst_md_discard;
-       dst->output = dst_md_discard_sk;
+       dst->output = dst_md_discard_out;
 
        memset(dst + 1, 0, sizeof(*md_dst) + optslen - sizeof(*dst));
 }
@@ -430,7 +430,7 @@ static void dst_ifdown(struct dst_entry *dst, struct net_device *dev,
 
        if (!unregister) {
                dst->input = dst_discard;
-               dst->output = dst_discard_sk;
+               dst->output = dst_discard_out;
        } else {
                dst->dev = dev_net(dst->dev)->loopback_dev;
                dev_hold(dst->dev);
index bb18c368000129ebea752a0a78785524848eaab8..672eefbfbe99fff2ade1bd2a095fb2366a2d2c0b 100644 (file)
 #include <net/sch_generic.h>
 #include <net/cls_cgroup.h>
 #include <net/dst_metadata.h>
+#include <net/dst.h>
 
 /**
  *     sk_filter - run a packet through a socket filter
  *     @sk: sock associated with &sk_buff
  *     @skb: buffer to filter
  *
- * Run the filter code and then cut skb->data to correct size returned by
- * SK_RUN_FILTER. If pkt_len is 0 we toss packet. If skb->len is smaller
+ * Run the eBPF program and then cut skb->data to correct size returned by
+ * the program. If pkt_len is 0 we toss packet. If skb->len is smaller
  * than pkt_len we keep whole skb->data. This is the socket level
- * wrapper to SK_RUN_FILTER. It returns 0 if the packet should
+ * wrapper to BPF_PROG_RUN. It returns 0 if the packet should
  * be accepted or -EPERM if the packet should be tossed.
  *
  */
@@ -82,7 +83,7 @@ int sk_filter(struct sock *sk, struct sk_buff *skb)
        rcu_read_lock();
        filter = rcu_dereference(sk->sk_filter);
        if (filter) {
-               unsigned int pkt_len = SK_RUN_FILTER(filter, skb);
+               unsigned int pkt_len = bpf_prog_run_save_cb(filter->prog, skb);
 
                err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM;
        }
@@ -148,12 +149,6 @@ static u64 __get_raw_cpu_id(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
        return raw_smp_processor_id();
 }
 
-/* note that this only generates 32-bit random numbers */
-static u64 __get_random_u32(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
-{
-       return prandom_u32();
-}
-
 static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg,
                              struct bpf_insn *insn_buf)
 {
@@ -312,7 +307,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
                        *insn = BPF_EMIT_CALL(__get_raw_cpu_id);
                        break;
                case SKF_AD_OFF + SKF_AD_RANDOM:
-                       *insn = BPF_EMIT_CALL(__get_random_u32);
+                       *insn = BPF_EMIT_CALL(bpf_user_rnd_u32);
+                       bpf_user_rnd_init_once();
                        break;
                }
                break;
@@ -1001,7 +997,7 @@ static struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp,
        int err;
 
        fp->bpf_func = NULL;
-       fp->jited = false;
+       fp->jited = 0;
 
        err = bpf_check_classic(fp->insns, fp->len);
        if (err) {
@@ -1083,16 +1079,18 @@ EXPORT_SYMBOL_GPL(bpf_prog_create);
  *     @pfp: the unattached filter that is created
  *     @fprog: the filter program
  *     @trans: post-classic verifier transformation handler
+ *     @save_orig: save classic BPF program
  *
  * This function effectively does the same as bpf_prog_create(), only
  * that it builds up its insns buffer from user space provided buffer.
  * It also allows for passing a bpf_aux_classic_check_t handler.
  */
 int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
-                             bpf_aux_classic_check_t trans)
+                             bpf_aux_classic_check_t trans, bool save_orig)
 {
        unsigned int fsize = bpf_classic_proglen(fprog);
        struct bpf_prog *fp;
+       int err;
 
        /* Make sure new filter is there and in the right amounts. */
        if (fprog->filter == NULL)
@@ -1108,12 +1106,16 @@ int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
        }
 
        fp->len = fprog->len;
-       /* Since unattached filters are not copied back to user
-        * space through sk_get_filter(), we do not need to hold
-        * a copy here, and can spare us the work.
-        */
        fp->orig_prog = NULL;
 
+       if (save_orig) {
+               err = bpf_prog_store_orig_filter(fp, fprog);
+               if (err) {
+                       __bpf_prog_free(fp);
+                       return -ENOMEM;
+               }
+       }
+
        /* bpf_prepare_filter() already takes care of freeing
         * memory in case something goes wrong.
         */
@@ -1404,9 +1406,6 @@ static u64 bpf_clone_redirect(u64 r1, u64 ifindex, u64 flags, u64 r4, u64 r5)
        if (unlikely(!dev))
                return -EINVAL;
 
-       if (unlikely(!(dev->flags & IFF_UP)))
-               return -EINVAL;
-
        skb2 = skb_clone(skb, GFP_ATOMIC);
        if (unlikely(!skb2))
                return -ENOMEM;
@@ -1428,6 +1427,49 @@ const struct bpf_func_proto bpf_clone_redirect_proto = {
        .arg3_type      = ARG_ANYTHING,
 };
 
+struct redirect_info {
+       u32 ifindex;
+       u32 flags;
+};
+
+static DEFINE_PER_CPU(struct redirect_info, redirect_info);
+static u64 bpf_redirect(u64 ifindex, u64 flags, u64 r3, u64 r4, u64 r5)
+{
+       struct redirect_info *ri = this_cpu_ptr(&redirect_info);
+
+       ri->ifindex = ifindex;
+       ri->flags = flags;
+       return TC_ACT_REDIRECT;
+}
+
+int skb_do_redirect(struct sk_buff *skb)
+{
+       struct redirect_info *ri = this_cpu_ptr(&redirect_info);
+       struct net_device *dev;
+
+       dev = dev_get_by_index_rcu(dev_net(skb->dev), ri->ifindex);
+       ri->ifindex = 0;
+       if (unlikely(!dev)) {
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       if (BPF_IS_REDIRECT_INGRESS(ri->flags))
+               return dev_forward_skb(dev, skb);
+
+       skb->dev = dev;
+       skb_sender_cpu_clear(skb);
+       return dev_queue_xmit(skb);
+}
+
+const struct bpf_func_proto bpf_redirect_proto = {
+       .func           = bpf_redirect,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_ANYTHING,
+       .arg2_type      = ARG_ANYTHING,
+};
+
 static u64 bpf_get_cgroup_classid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
 {
        return task_get_classid((struct sk_buff *) (unsigned long) r1);
@@ -1440,6 +1482,25 @@ static const struct bpf_func_proto bpf_get_cgroup_classid_proto = {
        .arg1_type      = ARG_PTR_TO_CTX,
 };
 
+static u64 bpf_get_route_realm(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
+{
+#ifdef CONFIG_IP_ROUTE_CLASSID
+       const struct dst_entry *dst;
+
+       dst = skb_dst((struct sk_buff *) (unsigned long) r1);
+       if (dst)
+               return dst->tclassid;
+#endif
+       return 0;
+}
+
+static const struct bpf_func_proto bpf_get_route_realm_proto = {
+       .func           = bpf_get_route_realm,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+};
+
 static u64 bpf_skb_vlan_push(u64 r1, u64 r2, u64 vlan_tci, u64 r4, u64 r5)
 {
        struct sk_buff *skb = (struct sk_buff *) (long) r1;
@@ -1580,7 +1641,8 @@ sk_filter_func_proto(enum bpf_func_id func_id)
        case BPF_FUNC_ktime_get_ns:
                return &bpf_ktime_get_ns_proto;
        case BPF_FUNC_trace_printk:
-               return bpf_get_trace_printk_proto();
+               if (capable(CAP_SYS_ADMIN))
+                       return bpf_get_trace_printk_proto();
        default:
                return NULL;
        }
@@ -1608,6 +1670,10 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
                return &bpf_skb_get_tunnel_key_proto;
        case BPF_FUNC_skb_set_tunnel_key:
                return bpf_get_skb_set_tunnel_key_proto();
+       case BPF_FUNC_redirect:
+               return &bpf_redirect_proto;
+       case BPF_FUNC_get_route_realm:
+               return &bpf_get_route_realm_proto;
        default:
                return sk_filter_func_proto(func_id);
        }
@@ -1633,6 +1699,9 @@ static bool __is_valid_access(int off, int size, enum bpf_access_type type)
 static bool sk_filter_is_valid_access(int off, int size,
                                      enum bpf_access_type type)
 {
+       if (off == offsetof(struct __sk_buff, tc_classid))
+               return false;
+
        if (type == BPF_WRITE) {
                switch (off) {
                case offsetof(struct __sk_buff, cb[0]) ...
@@ -1649,10 +1718,14 @@ static bool sk_filter_is_valid_access(int off, int size,
 static bool tc_cls_act_is_valid_access(int off, int size,
                                       enum bpf_access_type type)
 {
+       if (off == offsetof(struct __sk_buff, tc_classid))
+               return type == BPF_WRITE ? true : false;
+
        if (type == BPF_WRITE) {
                switch (off) {
                case offsetof(struct __sk_buff, mark):
                case offsetof(struct __sk_buff, tc_index):
+               case offsetof(struct __sk_buff, priority):
                case offsetof(struct __sk_buff, cb[0]) ...
                        offsetof(struct __sk_buff, cb[4]):
                        break;
@@ -1665,7 +1738,8 @@ static bool tc_cls_act_is_valid_access(int off, int size,
 
 static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
                                      int src_reg, int ctx_off,
-                                     struct bpf_insn *insn_buf)
+                                     struct bpf_insn *insn_buf,
+                                     struct bpf_prog *prog)
 {
        struct bpf_insn *insn = insn_buf;
 
@@ -1694,8 +1768,12 @@ static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
        case offsetof(struct __sk_buff, priority):
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, priority) != 4);
 
-               *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg,
-                                     offsetof(struct sk_buff, priority));
+               if (type == BPF_WRITE)
+                       *insn++ = BPF_STX_MEM(BPF_W, dst_reg, src_reg,
+                                             offsetof(struct sk_buff, priority));
+               else
+                       *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg,
+                                             offsetof(struct sk_buff, priority));
                break;
 
        case offsetof(struct __sk_buff, ingress_ifindex):
@@ -1752,6 +1830,7 @@ static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
                offsetof(struct __sk_buff, cb[4]):
                BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, data) < 20);
 
+               prog->cb_access = 1;
                ctx_off -= offsetof(struct __sk_buff, cb[0]);
                ctx_off += offsetof(struct sk_buff, cb);
                ctx_off += offsetof(struct qdisc_skb_cb, data);
@@ -1761,6 +1840,14 @@ static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
                        *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, ctx_off);
                break;
 
+       case offsetof(struct __sk_buff, tc_classid):
+               ctx_off -= offsetof(struct __sk_buff, tc_classid);
+               ctx_off += offsetof(struct sk_buff, cb);
+               ctx_off += offsetof(struct qdisc_skb_cb, tc_classid);
+               WARN_ON(type != BPF_WRITE);
+               *insn++ = BPF_STX_MEM(BPF_H, dst_reg, src_reg, ctx_off);
+               break;
+
        case offsetof(struct __sk_buff, tc_index):
 #ifdef CONFIG_NET_SCHED
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tc_index) != 2);
index dfb1a9ca08354fef353bf274415528d2d4ca269a..299cfc24d88832c3580450fcd824ae7b4fdd10c1 100644 (file)
@@ -180,7 +180,7 @@ int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b)
 }
 EXPORT_SYMBOL(lwtunnel_cmp_encap);
 
-int lwtunnel_output(struct sock *sk, struct sk_buff *skb)
+int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        const struct lwtunnel_encap_ops *ops;
@@ -199,7 +199,7 @@ int lwtunnel_output(struct sock *sk, struct sk_buff *skb)
        rcu_read_lock();
        ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
        if (likely(ops && ops->output))
-               ret = ops->output(sk, skb);
+               ret = ops->output(net, sk, skb);
        rcu_read_unlock();
 
        if (ret == -EOPNOTSUPP)
index 2b515ba7e94f4d1a15a3021a6e4a7732af1026c0..1aa8437ed6c4437d196ad513f0d3619198914416 100644 (file)
@@ -2235,14 +2235,53 @@ static void neigh_update_notify(struct neighbour *neigh)
        __neigh_notify(neigh, RTM_NEWNEIGH, 0);
 }
 
+static bool neigh_master_filtered(struct net_device *dev, int master_idx)
+{
+       struct net_device *master;
+
+       if (!master_idx)
+               return false;
+
+       master = netdev_master_upper_dev_get(dev);
+       if (!master || master->ifindex != master_idx)
+               return true;
+
+       return false;
+}
+
+static bool neigh_ifindex_filtered(struct net_device *dev, int filter_idx)
+{
+       if (filter_idx && dev->ifindex != filter_idx)
+               return true;
+
+       return false;
+}
+
 static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
                            struct netlink_callback *cb)
 {
        struct net *net = sock_net(skb->sk);
+       const struct nlmsghdr *nlh = cb->nlh;
+       struct nlattr *tb[NDA_MAX + 1];
        struct neighbour *n;
        int rc, h, s_h = cb->args[1];
        int idx, s_idx = idx = cb->args[2];
        struct neigh_hash_table *nht;
+       int filter_master_idx = 0, filter_idx = 0;
+       unsigned int flags = NLM_F_MULTI;
+       int err;
+
+       err = nlmsg_parse(nlh, sizeof(struct ndmsg), tb, NDA_MAX, NULL);
+       if (!err) {
+               if (tb[NDA_IFINDEX])
+                       filter_idx = nla_get_u32(tb[NDA_IFINDEX]);
+
+               if (tb[NDA_MASTER])
+                       filter_master_idx = nla_get_u32(tb[NDA_MASTER]);
+
+               if (filter_idx || filter_master_idx)
+                       flags |= NLM_F_DUMP_FILTERED;
+       }
 
        rcu_read_lock_bh();
        nht = rcu_dereference_bh(tbl->nht);
@@ -2255,12 +2294,16 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
                     n = rcu_dereference_bh(n->next)) {
                        if (!net_eq(dev_net(n->dev), net))
                                continue;
+                       if (neigh_ifindex_filtered(n->dev, filter_idx))
+                               continue;
+                       if (neigh_master_filtered(n->dev, filter_master_idx))
+                               continue;
                        if (idx < s_idx)
                                goto next;
                        if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid,
                                            cb->nlh->nlmsg_seq,
                                            RTM_NEWNEIGH,
-                                           NLM_F_MULTI) < 0) {
+                                           flags) < 0) {
                                rc = -1;
                                goto out;
                        }
index 830f8a7c1cb173caf3df0c9044bb81f9363338d8..f88a62ab019d25097efc67ee8e5239c9b39e8ca3 100644 (file)
@@ -471,7 +471,7 @@ static ssize_t phys_switch_id_show(struct device *dev,
 
        if (dev_isalive(netdev)) {
                struct switchdev_attr attr = {
-                       .id = SWITCHDEV_ATTR_PORT_PARENT_ID,
+                       .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
                        .flags = SWITCHDEV_F_NO_RECURSE,
                };
 
@@ -1003,15 +1003,12 @@ static ssize_t show_trans_timeout(struct netdev_queue *queue,
 }
 
 #ifdef CONFIG_XPS
-static inline unsigned int get_netdev_queue_index(struct netdev_queue *queue)
+static unsigned int get_netdev_queue_index(struct netdev_queue *queue)
 {
        struct net_device *dev = queue->dev;
-       int i;
-
-       for (i = 0; i < dev->num_tx_queues; i++)
-               if (queue == &dev->_tx[i])
-                       break;
+       unsigned int i;
 
+       i = queue - dev->_tx;
        BUG_ON(i >= dev->num_tx_queues);
 
        return i;
index 8bdada242a7d1266fe276f86aa02bd1691d56697..94acfc89ad976da245bed577511bd7a1c079f6f5 100644 (file)
@@ -140,7 +140,7 @@ static void queue_process(struct work_struct *work)
  * case. Further, we test the poll_owner to avoid recursion on UP
  * systems where the lock doesn't exist.
  */
-static int poll_one_napi(struct napi_struct *napi, int budget)
+static void poll_one_napi(struct napi_struct *napi)
 {
        int work = 0;
 
@@ -149,33 +149,33 @@ static int poll_one_napi(struct napi_struct *napi, int budget)
         * holding the napi->poll_lock.
         */
        if (!test_bit(NAPI_STATE_SCHED, &napi->state))
-               return budget;
+               return;
 
        /* If we set this bit but see that it has already been set,
         * that indicates that napi has been disabled and we need
         * to abort this operation
         */
        if (test_and_set_bit(NAPI_STATE_NPSVC, &napi->state))
-               goto out;
+               return;
 
-       work = napi->poll(napi, budget);
-       WARN_ONCE(work > budget, "%pF exceeded budget in poll\n", napi->poll);
+       /* We explicilty pass the polling call a budget of 0 to
+        * indicate that we are clearing the Tx path only.
+        */
+       work = napi->poll(napi, 0);
+       WARN_ONCE(work, "%pF exceeded budget in poll\n", napi->poll);
        trace_napi_poll(napi);
 
        clear_bit(NAPI_STATE_NPSVC, &napi->state);
-
-out:
-       return budget - work;
 }
 
-static void poll_napi(struct net_device *dev, int budget)
+static void poll_napi(struct net_device *dev)
 {
        struct napi_struct *napi;
 
        list_for_each_entry(napi, &dev->napi_list, dev_list) {
                if (napi->poll_owner != smp_processor_id() &&
                    spin_trylock(&napi->poll_lock)) {
-                       budget = poll_one_napi(napi, budget);
+                       poll_one_napi(napi);
                        spin_unlock(&napi->poll_lock);
                }
        }
@@ -185,7 +185,6 @@ static void netpoll_poll_dev(struct net_device *dev)
 {
        const struct net_device_ops *ops;
        struct netpoll_info *ni = rcu_dereference_bh(dev->npinfo);
-       int budget = 0;
 
        /* Don't do any rx activity if the dev_lock mutex is held
         * the dev_open/close paths use this to block netpoll activity
@@ -208,7 +207,7 @@ static void netpoll_poll_dev(struct net_device *dev)
        /* Process pending work on NIC */
        ops->ndo_poll_controller(dev);
 
-       poll_napi(dev, budget);
+       poll_napi(dev);
 
        up(&ni->dev_lock);
 
index 4eab4a94a59defa60d79988670681f2b3cdbcda5..703cf76aa7c2dee7c5b556f5f035c015780f55f0 100644 (file)
@@ -58,7 +58,7 @@
  *   jneq #0x0, drop_ieee1588      ; for PTP_GEN_BIT and drop these
  *   ldh [18]                      ; reload payload
  *   and #0xf                      ; mask PTP_CLASS_VMASK
- *   or #0x70                      ; PTP_CLASS_VLAN|PTP_CLASS_L2
+ *   or #0xc0                      ; PTP_CLASS_VLAN|PTP_CLASS_L2
  *   ret a                         ; return PTP class
  *
  * ; PTP over UDP over IPv4 over 802.1Q over Ethernet
@@ -73,7 +73,7 @@
  *   jneq #319, drop_8021q_ipv4    ; is port PTP_EV_PORT ?
  *   ldh [x + 26]                  ; load payload
  *   and #0xf                      ; mask PTP_CLASS_VMASK
- *   or #0x50                      ; PTP_CLASS_VLAN|PTP_CLASS_IPV4
+ *   or #0x90                      ; PTP_CLASS_VLAN|PTP_CLASS_IPV4
  *   ret a                         ; return PTP class
  *   drop_8021q_ipv4: ret #0x0     ; PTP_CLASS_NONE
  *
@@ -86,7 +86,7 @@
  *   jneq #319, drop_8021q_ipv6          ; is port PTP_EV_PORT ?
  *   ldh [66]                      ; load payload
  *   and #0xf                      ; mask PTP_CLASS_VMASK
- *   or #0x60                      ; PTP_CLASS_VLAN|PTP_CLASS_IPV6
+ *   or #0xa0                      ; PTP_CLASS_VLAN|PTP_CLASS_IPV6
  *   ret a                         ; return PTP class
  *   drop_8021q_ipv6: ret #0x0     ; PTP_CLASS_NONE
  *
@@ -98,7 +98,7 @@
  *   jneq #0x0, drop_ieee1588      ; for PTP_GEN_BIT and drop these
  *   ldh [14]                      ; reload payload
  *   and #0xf                      ; mask PTP_CLASS_VMASK
- *   or #0x30                      ; PTP_CLASS_L2
+ *   or #0x40                      ; PTP_CLASS_L2
  *   ret a                         ; return PTP class
  *   drop_ieee1588: ret #0x0       ; PTP_CLASS_NONE
  */
@@ -150,7 +150,7 @@ void __init ptp_classifier_init(void)
                { 0x15,  0, 35, 0x00000000 },
                { 0x28,  0,  0, 0x00000012 },
                { 0x54,  0,  0, 0x0000000f },
-               { 0x44,  0,  0, 0x00000070 },
+               { 0x44,  0,  0, 0x000000c0 },
                { 0x16,  0,  0, 0x00000000 },
                { 0x15,  0, 12, 0x00000800 },
                { 0x30,  0,  0, 0x0000001b },
@@ -162,7 +162,7 @@ void __init ptp_classifier_init(void)
                { 0x15,  0,  4, 0x0000013f },
                { 0x48,  0,  0, 0x0000001a },
                { 0x54,  0,  0, 0x0000000f },
-               { 0x44,  0,  0, 0x00000050 },
+               { 0x44,  0,  0, 0x00000090 },
                { 0x16,  0,  0, 0x00000000 },
                { 0x06,  0,  0, 0x00000000 },
                { 0x15,  0,  8, 0x000086dd },
@@ -172,7 +172,7 @@ void __init ptp_classifier_init(void)
                { 0x15,  0,  4, 0x0000013f },
                { 0x28,  0,  0, 0x00000042 },
                { 0x54,  0,  0, 0x0000000f },
-               { 0x44,  0,  0, 0x00000060 },
+               { 0x44,  0,  0, 0x000000a0 },
                { 0x16,  0,  0, 0x00000000 },
                { 0x06,  0,  0, 0x00000000 },
                { 0x15,  0,  7, 0x000088f7 },
@@ -181,7 +181,7 @@ void __init ptp_classifier_init(void)
                { 0x15,  0,  4, 0x00000000 },
                { 0x28,  0,  0, 0x0000000e },
                { 0x54,  0,  0, 0x0000000f },
-               { 0x44,  0,  0, 0x00000030 },
+               { 0x44,  0,  0, 0x00000040 },
                { 0x16,  0,  0, 0x00000000 },
                { 0x06,  0,  0, 0x00000000 },
        };
index b42f0e26f89e4cf2e37a8329da549eb5cd1200c5..5d26056b6d8f01f4db815423d2d78f6f7d25d55f 100644 (file)
 int sysctl_max_syn_backlog = 256;
 EXPORT_SYMBOL(sysctl_max_syn_backlog);
 
-int reqsk_queue_alloc(struct request_sock_queue *queue,
-                     unsigned int nr_table_entries)
+void reqsk_queue_alloc(struct request_sock_queue *queue)
 {
-       size_t lopt_size = sizeof(struct listen_sock);
-       struct listen_sock *lopt = NULL;
+       spin_lock_init(&queue->rskq_lock);
 
-       nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);
-       nr_table_entries = max_t(u32, nr_table_entries, 8);
-       nr_table_entries = roundup_pow_of_two(nr_table_entries + 1);
-       lopt_size += nr_table_entries * sizeof(struct request_sock *);
+       spin_lock_init(&queue->fastopenq.lock);
+       queue->fastopenq.rskq_rst_head = NULL;
+       queue->fastopenq.rskq_rst_tail = NULL;
+       queue->fastopenq.qlen = 0;
 
-       if (lopt_size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER))
-               lopt = kzalloc(lopt_size, GFP_KERNEL |
-                                         __GFP_NOWARN |
-                                         __GFP_NORETRY);
-       if (!lopt)
-               lopt = vzalloc(lopt_size);
-       if (!lopt)
-               return -ENOMEM;
-
-       get_random_bytes(&lopt->hash_rnd, sizeof(lopt->hash_rnd));
-       spin_lock_init(&queue->syn_wait_lock);
        queue->rskq_accept_head = NULL;
-       lopt->nr_table_entries = nr_table_entries;
-       lopt->max_qlen_log = ilog2(nr_table_entries);
-
-       spin_lock_bh(&queue->syn_wait_lock);
-       queue->listen_opt = lopt;
-       spin_unlock_bh(&queue->syn_wait_lock);
-
-       return 0;
-}
-
-void __reqsk_queue_destroy(struct request_sock_queue *queue)
-{
-       /* This is an error recovery path only, no locking needed */
-       kvfree(queue->listen_opt);
-}
-
-static inline struct listen_sock *reqsk_queue_yank_listen_sk(
-               struct request_sock_queue *queue)
-{
-       struct listen_sock *lopt;
-
-       spin_lock_bh(&queue->syn_wait_lock);
-       lopt = queue->listen_opt;
-       queue->listen_opt = NULL;
-       spin_unlock_bh(&queue->syn_wait_lock);
-
-       return lopt;
-}
-
-void reqsk_queue_destroy(struct request_sock_queue *queue)
-{
-       /* make all the listen_opt local to us */
-       struct listen_sock *lopt = reqsk_queue_yank_listen_sk(queue);
-
-       if (listen_sock_qlen(lopt) != 0) {
-               unsigned int i;
-
-               for (i = 0; i < lopt->nr_table_entries; i++) {
-                       struct request_sock *req;
-
-                       spin_lock_bh(&queue->syn_wait_lock);
-                       while ((req = lopt->syn_table[i]) != NULL) {
-                               lopt->syn_table[i] = req->dl_next;
-                               /* Because of following del_timer_sync(),
-                                * we must release the spinlock here
-                                * or risk a dead lock.
-                                */
-                               spin_unlock_bh(&queue->syn_wait_lock);
-                               atomic_inc(&lopt->qlen_dec);
-                               if (del_timer_sync(&req->rsk_timer))
-                                       reqsk_put(req);
-                               reqsk_put(req);
-                               spin_lock_bh(&queue->syn_wait_lock);
-                       }
-                       spin_unlock_bh(&queue->syn_wait_lock);
-               }
-       }
-
-       if (WARN_ON(listen_sock_qlen(lopt) != 0))
-               pr_err("qlen %u\n", listen_sock_qlen(lopt));
-       kvfree(lopt);
 }
 
 /*
@@ -174,7 +100,7 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req,
        struct sock *lsk = req->rsk_listener;
        struct fastopen_queue *fastopenq;
 
-       fastopenq = inet_csk(lsk)->icsk_accept_queue.fastopenq;
+       fastopenq = &inet_csk(lsk)->icsk_accept_queue.fastopenq;
 
        tcp_sk(sk)->fastopen_rsk = NULL;
        spin_lock_bh(&fastopenq->lock);
index 0ec48403ed68cc77b29b120665ed707bced816c4..504bd17b7456c3a16a0832ed28ede040c41ff316 100644 (file)
@@ -96,7 +96,7 @@ int rtnl_is_locked(void)
 EXPORT_SYMBOL(rtnl_is_locked);
 
 #ifdef CONFIG_PROVE_LOCKING
-int lockdep_rtnl_is_held(void)
+bool lockdep_rtnl_is_held(void)
 {
        return lockdep_is_held(&rtnl_mutex);
 }
@@ -497,7 +497,8 @@ void rtnl_af_unregister(struct rtnl_af_ops *ops)
 }
 EXPORT_SYMBOL_GPL(rtnl_af_unregister);
 
-static size_t rtnl_link_get_af_size(const struct net_device *dev)
+static size_t rtnl_link_get_af_size(const struct net_device *dev,
+                                   u32 ext_filter_mask)
 {
        struct rtnl_af_ops *af_ops;
        size_t size;
@@ -509,7 +510,7 @@ static size_t rtnl_link_get_af_size(const struct net_device *dev)
                if (af_ops->get_link_af_size) {
                        /* AF_* + nested data */
                        size += nla_total_size(sizeof(struct nlattr)) +
-                               af_ops->get_link_af_size(dev);
+                               af_ops->get_link_af_size(dev, ext_filter_mask);
                }
        }
 
@@ -837,7 +838,8 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev,
                         /* IFLA_VF_STATS_BROADCAST */
                         nla_total_size(sizeof(__u64)) +
                         /* IFLA_VF_STATS_MULTICAST */
-                        nla_total_size(sizeof(__u64)));
+                        nla_total_size(sizeof(__u64)) +
+                        nla_total_size(sizeof(struct ifla_vf_trust)));
                return size;
        } else
                return 0;
@@ -900,7 +902,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
               + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */
               + rtnl_port_size(dev, ext_filter_mask) /* IFLA_VF_PORTS + IFLA_PORT_SELF */
               + rtnl_link_get_size(dev) /* IFLA_LINKINFO */
-              + rtnl_link_get_af_size(dev) /* IFLA_AF_SPEC */
+              + rtnl_link_get_af_size(dev, ext_filter_mask) /* IFLA_AF_SPEC */
               + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_PORT_ID */
               + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_SWITCH_ID */
               + nla_total_size(1); /* IFLA_PROTO_DOWN */
@@ -1025,7 +1027,7 @@ static int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev)
 {
        int err;
        struct switchdev_attr attr = {
-               .id = SWITCHDEV_ATTR_PORT_PARENT_ID,
+               .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
                .flags = SWITCHDEV_F_NO_RECURSE,
        };
 
@@ -1160,6 +1162,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        struct ifla_vf_link_state vf_linkstate;
                        struct ifla_vf_rss_query_en vf_rss_query_en;
                        struct ifla_vf_stats vf_stats;
+                       struct ifla_vf_trust vf_trust;
 
                        /*
                         * Not all SR-IOV capable drivers support the
@@ -1169,6 +1172,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                         */
                        ivi.spoofchk = -1;
                        ivi.rss_query_en = -1;
+                       ivi.trusted = -1;
                        memset(ivi.mac, 0, sizeof(ivi.mac));
                        /* The default value for VF link state is "auto"
                         * IFLA_VF_LINK_STATE_AUTO which equals zero
@@ -1182,7 +1186,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                                vf_tx_rate.vf =
                                vf_spoofchk.vf =
                                vf_linkstate.vf =
-                               vf_rss_query_en.vf = ivi.vf;
+                               vf_rss_query_en.vf =
+                               vf_trust.vf = ivi.vf;
 
                        memcpy(vf_mac.mac, ivi.mac, sizeof(ivi.mac));
                        vf_vlan.vlan = ivi.vlan;
@@ -1193,6 +1198,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        vf_spoofchk.setting = ivi.spoofchk;
                        vf_linkstate.link_state = ivi.linkstate;
                        vf_rss_query_en.setting = ivi.rss_query_en;
+                       vf_trust.setting = ivi.trusted;
                        vf = nla_nest_start(skb, IFLA_VF_INFO);
                        if (!vf) {
                                nla_nest_cancel(skb, vfinfo);
@@ -1210,7 +1216,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                                    &vf_linkstate) ||
                            nla_put(skb, IFLA_VF_RSS_QUERY_EN,
                                    sizeof(vf_rss_query_en),
-                                   &vf_rss_query_en))
+                                   &vf_rss_query_en) ||
+                           nla_put(skb, IFLA_VF_TRUST,
+                                   sizeof(vf_trust), &vf_trust))
                                goto nla_put_failure;
                        memset(&vf_stats, 0, sizeof(vf_stats));
                        if (dev->netdev_ops->ndo_get_vf_stats)
@@ -1272,7 +1280,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        if (!(af = nla_nest_start(skb, af_ops->family)))
                                goto nla_put_failure;
 
-                       err = af_ops->fill_link_af(skb, dev);
+                       err = af_ops->fill_link_af(skb, dev, ext_filter_mask);
 
                        /*
                         * Caller may return ENODATA to indicate that there
@@ -1347,6 +1355,7 @@ static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
        [IFLA_VF_LINK_STATE]    = { .len = sizeof(struct ifla_vf_link_state) },
        [IFLA_VF_RSS_QUERY_EN]  = { .len = sizeof(struct ifla_vf_rss_query_en) },
        [IFLA_VF_STATS]         = { .type = NLA_NESTED },
+       [IFLA_VF_TRUST]         = { .len = sizeof(struct ifla_vf_trust) },
 };
 
 static const struct nla_policy ifla_vf_stats_policy[IFLA_VF_STATS_MAX + 1] = {
@@ -1586,6 +1595,16 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
                        return err;
        }
 
+       if (tb[IFLA_VF_TRUST]) {
+               struct ifla_vf_trust *ivt = nla_data(tb[IFLA_VF_TRUST]);
+
+               err = -EOPNOTSUPP;
+               if (ops->ndo_set_vf_trust)
+                       err = ops->ndo_set_vf_trust(dev, ivt->vf, ivt->setting);
+               if (err < 0)
+                       return err;
+       }
+
        return err;
 }
 
@@ -3443,4 +3462,3 @@ void __init rtnetlink_init(void)
        rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, NULL);
        rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL);
 }
-
index 3307c02244d39cfa3fbae969ea478fa0f195e595..7529eb9463be21a92dab4180adf5059cc0587807 100644 (file)
@@ -422,13 +422,25 @@ static void sock_warn_obsolete_bsdism(const char *name)
        }
 }
 
+static bool sock_needs_netstamp(const struct sock *sk)
+{
+       switch (sk->sk_family) {
+       case AF_UNSPEC:
+       case AF_UNIX:
+               return false;
+       default:
+               return true;
+       }
+}
+
 #define SK_FLAGS_TIMESTAMP ((1UL << SOCK_TIMESTAMP) | (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE))
 
 static void sock_disable_timestamp(struct sock *sk, unsigned long flags)
 {
        if (sk->sk_flags & flags) {
                sk->sk_flags &= ~flags;
-               if (!(sk->sk_flags & SK_FLAGS_TIMESTAMP))
+               if (sock_needs_netstamp(sk) &&
+                   !(sk->sk_flags & SK_FLAGS_TIMESTAMP))
                        net_disable_timestamp();
        }
 }
@@ -988,6 +1000,10 @@ set_rcvbuf:
                                         sk->sk_max_pacing_rate);
                break;
 
+       case SO_INCOMING_CPU:
+               sk->sk_incoming_cpu = val;
+               break;
+
        default:
                ret = -ENOPROTOOPT;
                break;
@@ -1578,7 +1594,8 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
                if (newsk->sk_prot->sockets_allocated)
                        sk_sockets_allocated_inc(newsk);
 
-               if (newsk->sk_flags & SK_FLAGS_TIMESTAMP)
+               if (sock_needs_netstamp(sk) &&
+                   newsk->sk_flags & SK_FLAGS_TIMESTAMP)
                        net_enable_timestamp();
        }
 out:
@@ -1639,6 +1656,28 @@ void sock_wfree(struct sk_buff *skb)
 }
 EXPORT_SYMBOL(sock_wfree);
 
+void skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
+{
+       skb_orphan(skb);
+       skb->sk = sk;
+#ifdef CONFIG_INET
+       if (unlikely(!sk_fullsock(sk))) {
+               skb->destructor = sock_edemux;
+               sock_hold(sk);
+               return;
+       }
+#endif
+       skb->destructor = sock_wfree;
+       skb_set_hash_from_sk(skb, sk);
+       /*
+        * We used to take a refcount on sk, but following operation
+        * is enough to guarantee sk_free() wont free this sock until
+        * all in-flight packets are completed
+        */
+       atomic_add(skb->truesize, &sk->sk_wmem_alloc);
+}
+EXPORT_SYMBOL(skb_set_owner_w);
+
 void skb_orphan_partial(struct sk_buff *skb)
 {
        /* TCP stack sets skb->ooo_okay based on sk_wmem_alloc,
@@ -1852,6 +1891,32 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
 }
 EXPORT_SYMBOL(sock_alloc_send_skb);
 
+int sock_cmsg_send(struct sock *sk, struct msghdr *msg,
+                  struct sockcm_cookie *sockc)
+{
+       struct cmsghdr *cmsg;
+
+       for_each_cmsghdr(cmsg, msg) {
+               if (!CMSG_OK(msg, cmsg))
+                       return -EINVAL;
+               if (cmsg->cmsg_level != SOL_SOCKET)
+                       continue;
+               switch (cmsg->cmsg_type) {
+               case SO_MARK:
+                       if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
+                               return -EPERM;
+                       if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32)))
+                               return -EINVAL;
+                       sockc->mark = *(u32 *)CMSG_DATA(cmsg);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL(sock_cmsg_send);
+
 /* On 32bit arches, an skb frag is limited to 2^15 */
 #define SKB_FRAG_PAGE_ORDER    get_order(32768)
 
@@ -2353,6 +2418,7 @@ void sock_init_data(struct socket *sock, struct sock *sk)
 
        sk->sk_max_pacing_rate = ~0U;
        sk->sk_pacing_rate = ~0U;
+       sk->sk_incoming_cpu = -1;
        /*
         * Before updating sk_refcnt, we must commit prior changes to memory
         * (Documentation/RCU/rculist_nulls.txt for details)
@@ -2479,7 +2545,8 @@ void sock_enable_timestamp(struct sock *sk, int flag)
                 * time stamping, but time stamping might have been on
                 * already because of the other one
                 */
-               if (!(previous_flags & SK_FLAGS_TIMESTAMP))
+               if (sock_needs_netstamp(sk) &&
+                   !(previous_flags & SK_FLAGS_TIMESTAMP))
                        net_enable_timestamp();
        }
 }
@@ -2758,7 +2825,7 @@ static int req_prot_init(const struct proto *prot)
 
        rsk_prot->slab = kmem_cache_create(rsk_prot->slab_name,
                                           rsk_prot->obj_size, 0,
-                                          0, NULL);
+                                          prot->slab_flags, NULL);
 
        if (!rsk_prot->slab) {
                pr_crit("%s: Can't create request sock SLAB cache!\n",
index 817622f3dbb79e3f94ec5257be032ec5574185d6..0c1d58d43f67c46c7a11081d0ecdd30cb5878529 100644 (file)
@@ -1,3 +1,5 @@
+/* License: GPL */
+
 #include <linux/mutex.h>
 #include <linux/socket.h>
 #include <linux/skbuff.h>
@@ -323,14 +325,4 @@ static int __init sock_diag_init(void)
        BUG_ON(!broadcast_wq);
        return register_pernet_subsys(&diag_net_ops);
 }
-
-static void __exit sock_diag_exit(void)
-{
-       unregister_pernet_subsys(&diag_net_ops);
-       destroy_workqueue(broadcast_wq);
-}
-
-module_init(sock_diag_init);
-module_exit(sock_diag_exit);
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_SOCK_DIAG);
+device_initcall(sock_diag_init);
index 630b30b4fb5368332428289e449b3247be0cd97b..5dca7ce8ee9f58619ba1ac6c0bc85c73f208fe9d 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/export.h>
+#include <linux/if_vlan.h>
 #include <net/ip.h>
 #include <net/tso.h>
 #include <asm/unaligned.h>
@@ -14,18 +15,24 @@ EXPORT_SYMBOL(tso_count_descs);
 void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso,
                   int size, bool is_last)
 {
-       struct iphdr *iph;
        struct tcphdr *tcph;
        int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
        int mac_hdr_len = skb_network_offset(skb);
 
        memcpy(hdr, skb->data, hdr_len);
-       iph = (struct iphdr *)(hdr + mac_hdr_len);
-       iph->id = htons(tso->ip_id);
-       iph->tot_len = htons(size + hdr_len - mac_hdr_len);
+       if (!tso->ipv6) {
+               struct iphdr *iph = (void *)(hdr + mac_hdr_len);
+
+               iph->id = htons(tso->ip_id);
+               iph->tot_len = htons(size + hdr_len - mac_hdr_len);
+               tso->ip_id++;
+       } else {
+               struct ipv6hdr *iph = (void *)(hdr + mac_hdr_len);
+
+               iph->payload_len = htons(size + tcp_hdrlen(skb));
+       }
        tcph = (struct tcphdr *)(hdr + skb_transport_offset(skb));
        put_unaligned_be32(tso->tcp_seq, &tcph->seq);
-       tso->ip_id++;
 
        if (!is_last) {
                /* Clear all special flags for not last packet */
@@ -61,6 +68,7 @@ void tso_start(struct sk_buff *skb, struct tso_t *tso)
        tso->ip_id = ntohs(ip_hdr(skb)->id);
        tso->tcp_seq = ntohl(tcp_hdr(skb)->seq);
        tso->next_frag_idx = 0;
+       tso->ipv6 = vlan_get_protocol(skb) == htons(ETH_P_IPV6);
 
        /* Build first data */
        tso->size = skb_headlen(skb) - hdr_len;
index 3dffce953c39fc0209e8d0f553817e972f854303..3d17ca8b4744168021e026afd78c9ec3c1a9a9cb 100644 (file)
@@ -348,52 +348,3 @@ void inet_proto_csum_replace_by_diff(__sum16 *sum, struct sk_buff *skb,
        }
 }
 EXPORT_SYMBOL(inet_proto_csum_replace_by_diff);
-
-struct __net_random_once_work {
-       struct work_struct work;
-       struct static_key *key;
-};
-
-static void __net_random_once_deferred(struct work_struct *w)
-{
-       struct __net_random_once_work *work =
-               container_of(w, struct __net_random_once_work, work);
-       BUG_ON(!static_key_enabled(work->key));
-       static_key_slow_dec(work->key);
-       kfree(work);
-}
-
-static void __net_random_once_disable_jump(struct static_key *key)
-{
-       struct __net_random_once_work *w;
-
-       w = kmalloc(sizeof(*w), GFP_ATOMIC);
-       if (!w)
-               return;
-
-       INIT_WORK(&w->work, __net_random_once_deferred);
-       w->key = key;
-       schedule_work(&w->work);
-}
-
-bool __net_get_random_once(void *buf, int nbytes, bool *done,
-                          struct static_key *once_key)
-{
-       static DEFINE_SPINLOCK(lock);
-       unsigned long flags;
-
-       spin_lock_irqsave(&lock, flags);
-       if (*done) {
-               spin_unlock_irqrestore(&lock, flags);
-               return false;
-       }
-
-       get_random_bytes(buf, nbytes);
-       *done = true;
-       spin_unlock_irqrestore(&lock, flags);
-
-       __net_random_once_disable_jump(once_key);
-
-       return true;
-}
-EXPORT_SYMBOL(__net_get_random_once);
index 5b21f6f88e9798b839a60a41c4e6ccda58bcf1f0..4f6c1862dfd25271303873acbc5c85b80b9f00c1 100644 (file)
@@ -13,6 +13,7 @@
  * You should have received a copy of the GNU General Public License along with
  * this program; if not, see <http://www.gnu.org/licenses/>.
  *
+ * Description: Data Center Bridging netlink interface
  * Author: Lucy Liu <lucy.liu@intel.com>
  */
 
@@ -24,7 +25,7 @@
 #include <linux/dcbnl.h>
 #include <net/dcbevent.h>
 #include <linux/rtnetlink.h>
-#include <linux/module.h>
+#include <linux/init.h>
 #include <net/sock.h>
 
 /* Data Center Bridging (DCB) is a collection of Ethernet enhancements
  * features for capable devices.
  */
 
-MODULE_AUTHOR("Lucy Liu, <lucy.liu@intel.com>");
-MODULE_DESCRIPTION("Data Center Bridging netlink interface");
-MODULE_LICENSE("GPL");
-
 /**************** DCB attribute policies *************************************/
 
 /* DCB netlink attributes policy */
@@ -1935,19 +1932,6 @@ int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del)
 }
 EXPORT_SYMBOL(dcb_ieee_delapp);
 
-static void dcb_flushapp(void)
-{
-       struct dcb_app_type *app;
-       struct dcb_app_type *tmp;
-
-       spin_lock_bh(&dcb_lock);
-       list_for_each_entry_safe(app, tmp, &dcb_app_list, list) {
-               list_del(&app->list);
-               kfree(app);
-       }
-       spin_unlock_bh(&dcb_lock);
-}
-
 static int __init dcbnl_init(void)
 {
        INIT_LIST_HEAD(&dcb_app_list);
@@ -1957,12 +1941,4 @@ static int __init dcbnl_init(void)
 
        return 0;
 }
-module_init(dcbnl_init);
-
-static void __exit dcbnl_exit(void)
-{
-       rtnl_unregister(PF_UNSPEC, RTM_GETDCB);
-       rtnl_unregister(PF_UNSPEC, RTM_SETDCB);
-       dcb_flushapp();
-}
-module_exit(dcbnl_exit);
+device_initcall(dcbnl_init);
index bebc735f5afc0fd9993a2a6ddc4074dcaa5b1559..b0e28d24e1a749ce1cfa7204a7cbdd201da8368f 100644 (file)
@@ -229,7 +229,7 @@ void dccp_v4_send_check(struct sock *sk, struct sk_buff *skb);
 int dccp_retransmit_skb(struct sock *sk);
 
 void dccp_send_ack(struct sock *sk);
-void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
+void dccp_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
                         struct request_sock *rsk);
 
 void dccp_send_sync(struct sock *sk, const u64 seq,
@@ -270,15 +270,17 @@ int dccp_reqsk_init(struct request_sock *rq, struct dccp_sock const *dp,
 
 int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb);
 
-struct sock *dccp_create_openreq_child(struct sock *sk,
+struct sock *dccp_create_openreq_child(const struct sock *sk,
                                       const struct request_sock *req,
                                       const struct sk_buff *skb);
 
 int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
 
-struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb,
+struct sock *dccp_v4_request_recv_sock(const struct sock *sk, struct sk_buff *skb,
                                       struct request_sock *req,
-                                      struct dst_entry *dst);
+                                      struct dst_entry *dst,
+                                      struct request_sock *req_unhash,
+                                      bool *own_req);
 struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
                            struct request_sock *req);
 
@@ -293,7 +295,7 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized);
 void dccp_destroy_sock(struct sock *sk);
 
 void dccp_close(struct sock *sk, long timeout);
-struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
+struct sk_buff *dccp_make_response(const struct sock *sk, struct dst_entry *dst,
                                   struct request_sock *req);
 
 int dccp_connect(struct sock *sk);
@@ -325,13 +327,13 @@ void dccp_send_close(struct sock *sk, const int active);
 int dccp_invalid_packet(struct sk_buff *skb);
 u32 dccp_sample_rtt(struct sock *sk, long delta);
 
-static inline int dccp_bad_service_code(const struct sock *sk,
+static inline bool dccp_bad_service_code(const struct sock *sk,
                                        const __be32 service)
 {
        const struct dccp_sock *dp = dccp_sk(sk);
 
        if (dp->dccps_service == service)
-               return 0;
+               return false;
        return !dccp_list_has_service(dp->dccps_service_list, service);
 }
 
index ccf4c5629b3c8672f348a3ca29b0b6bd46cc23e8..5684e14932bd47e97b9d547307bfc50230e7be7d 100644 (file)
@@ -208,7 +208,6 @@ void dccp_req_err(struct sock *sk, u64 seq)
 
        if (!between48(seq, dccp_rsk(req)->dreq_iss, dccp_rsk(req)->dreq_gss)) {
                NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
-               reqsk_put(req);
        } else {
                /*
                 * Still in RESPOND, just remove it silently.
@@ -218,6 +217,7 @@ void dccp_req_err(struct sock *sk, u64 seq)
                 */
                inet_csk_reqsk_queue_drop(req->rsk_listener, req);
        }
+       reqsk_put(req);
 }
 EXPORT_SYMBOL(dccp_req_err);
 
@@ -390,9 +390,12 @@ static inline u64 dccp_v4_init_sequence(const struct sk_buff *skb)
  *
  * This is the equivalent of TCP's tcp_v4_syn_recv_sock
  */
-struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb,
+struct sock *dccp_v4_request_recv_sock(const struct sock *sk,
+                                      struct sk_buff *skb,
                                       struct request_sock *req,
-                                      struct dst_entry *dst)
+                                      struct dst_entry *dst,
+                                      struct request_sock *req_unhash,
+                                      bool *own_req)
 {
        struct inet_request_sock *ireq;
        struct inet_sock *newinet;
@@ -425,7 +428,7 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb,
 
        if (__inet_inherit_port(sk, newsk) < 0)
                goto put_and_exit;
-       __inet_hash_nolisten(newsk, NULL);
+       *own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash));
 
        return newsk;
 
@@ -443,36 +446,6 @@ put_and_exit:
 }
 EXPORT_SYMBOL_GPL(dccp_v4_request_recv_sock);
 
-static struct sock *dccp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
-{
-       const struct dccp_hdr *dh = dccp_hdr(skb);
-       const struct iphdr *iph = ip_hdr(skb);
-       struct sock *nsk;
-       /* Find possible connection requests. */
-       struct request_sock *req = inet_csk_search_req(sk, dh->dccph_sport,
-                                                      iph->saddr, iph->daddr);
-       if (req) {
-               nsk = dccp_check_req(sk, skb, req);
-               if (!nsk)
-                       reqsk_put(req);
-               return nsk;
-       }
-       nsk = inet_lookup_established(sock_net(sk), &dccp_hashinfo,
-                                     iph->saddr, dh->dccph_sport,
-                                     iph->daddr, dh->dccph_dport,
-                                     inet_iif(skb));
-       if (nsk != NULL) {
-               if (nsk->sk_state != DCCP_TIME_WAIT) {
-                       bh_lock_sock(nsk);
-                       return nsk;
-               }
-               inet_twsk_put(inet_twsk(nsk));
-               return NULL;
-       }
-
-       return sk;
-}
-
 static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk,
                                           struct sk_buff *skb)
 {
@@ -498,7 +471,7 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk,
        return &rt->dst;
 }
 
-static int dccp_v4_send_response(struct sock *sk, struct request_sock *req)
+static int dccp_v4_send_response(const struct sock *sk, struct request_sock *req)
 {
        int err = -1;
        struct sk_buff *skb;
@@ -527,7 +500,7 @@ out:
        return err;
 }
 
-static void dccp_v4_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb)
+static void dccp_v4_ctl_send_reset(const struct sock *sk, struct sk_buff *rxskb)
 {
        int err;
        const struct iphdr *rxiph;
@@ -624,7 +597,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
                goto drop;
 
-       req = inet_reqsk_alloc(&dccp_request_sock_ops, sk);
+       req = inet_reqsk_alloc(&dccp_request_sock_ops, sk, true);
        if (req == NULL)
                goto drop;
 
@@ -704,18 +677,6 @@ int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
         * NOTE: the check for the packet types is done in
         *       dccp_rcv_state_process
         */
-       if (sk->sk_state == DCCP_LISTEN) {
-               struct sock *nsk = dccp_v4_hnd_req(sk, skb);
-
-               if (nsk == NULL)
-                       goto discard;
-
-               if (nsk != sk) {
-                       if (dccp_child_process(sk, nsk, skb))
-                               goto reset;
-                       return 0;
-               }
-       }
 
        if (dccp_rcv_state_process(sk, skb, dh, skb->len))
                goto reset;
@@ -723,7 +684,6 @@ int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
 
 reset:
        dccp_v4_ctl_send_reset(sk, skb);
-discard:
        kfree_skb(skb);
        return 0;
 }
@@ -841,15 +801,10 @@ static int dccp_v4_rcv(struct sk_buff *skb)
                                  DCCP_SKB_CB(skb)->dccpd_ack_seq);
        }
 
-       /* Step 2:
-        *      Look up flow ID in table and get corresponding socket */
+lookup:
        sk = __inet_lookup_skb(&dccp_hashinfo, skb,
                               dh->dccph_sport, dh->dccph_dport);
-       /*
-        * Step 2:
-        *      If no socket ...
-        */
-       if (sk == NULL) {
+       if (!sk) {
                dccp_pr_debug("failed to look up flow ID in table and "
                              "get corresponding socket\n");
                goto no_dccp_socket;
@@ -867,6 +822,31 @@ static int dccp_v4_rcv(struct sk_buff *skb)
                goto no_dccp_socket;
        }
 
+       if (sk->sk_state == DCCP_NEW_SYN_RECV) {
+               struct request_sock *req = inet_reqsk(sk);
+               struct sock *nsk = NULL;
+
+               sk = req->rsk_listener;
+               if (likely(sk->sk_state == DCCP_LISTEN)) {
+                       nsk = dccp_check_req(sk, skb, req);
+               } else {
+                       inet_csk_reqsk_queue_drop_and_put(sk, req);
+                       goto lookup;
+               }
+               if (!nsk) {
+                       reqsk_put(req);
+                       goto discard_it;
+               }
+               if (nsk == sk) {
+                       sock_hold(sk);
+                       reqsk_put(req);
+               } else if (dccp_child_process(sk, nsk, skb)) {
+                       dccp_v4_ctl_send_reset(sk, skb);
+                       goto discard_it;
+               } else {
+                       return 0;
+               }
+       }
        /*
         * RFC 4340, sec. 9.2.1: Minimum Checksum Coverage
         *      o if MinCsCov = 0, only packets with CsCov = 0 are accepted
index 5165571f397aa6d40da19d36287ca8339852ab54..db5fc2440a232c856bccfd8e0222a24bcf3d6d11 100644 (file)
@@ -181,7 +181,7 @@ out:
 }
 
 
-static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
+static int dccp_v6_send_response(const struct sock *sk, struct request_sock *req)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -234,7 +234,7 @@ static void dccp_v6_reqsk_destructor(struct request_sock *req)
        kfree_skb(inet_rsk(req)->pktopts);
 }
 
-static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb)
+static void dccp_v6_ctl_send_reset(const struct sock *sk, struct sk_buff *rxskb)
 {
        const struct ipv6hdr *rxip6h;
        struct sk_buff *skb;
@@ -290,37 +290,6 @@ static struct request_sock_ops dccp6_request_sock_ops = {
        .syn_ack_timeout = dccp_syn_ack_timeout,
 };
 
-static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
-{
-       const struct dccp_hdr *dh = dccp_hdr(skb);
-       const struct ipv6hdr *iph = ipv6_hdr(skb);
-       struct request_sock *req;
-       struct sock *nsk;
-
-       req = inet6_csk_search_req(sk, dh->dccph_sport, &iph->saddr,
-                                  &iph->daddr, inet6_iif(skb));
-       if (req) {
-               nsk = dccp_check_req(sk, skb, req);
-               if (!nsk)
-                       reqsk_put(req);
-               return nsk;
-       }
-       nsk = __inet6_lookup_established(sock_net(sk), &dccp_hashinfo,
-                                        &iph->saddr, dh->dccph_sport,
-                                        &iph->daddr, ntohs(dh->dccph_dport),
-                                        inet6_iif(skb));
-       if (nsk != NULL) {
-               if (nsk->sk_state != DCCP_TIME_WAIT) {
-                       bh_lock_sock(nsk);
-                       return nsk;
-               }
-               inet_twsk_put(inet_twsk(nsk));
-               return NULL;
-       }
-
-       return sk;
-}
-
 static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 {
        struct request_sock *req;
@@ -350,7 +319,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
                goto drop;
 
-       req = inet_reqsk_alloc(&dccp6_request_sock_ops, sk);
+       req = inet_reqsk_alloc(&dccp6_request_sock_ops, sk, true);
        if (req == NULL)
                goto drop;
 
@@ -398,7 +367,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        if (dccp_v6_send_response(sk, req))
                goto drop_and_free;
 
-       inet6_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
+       inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
        return 0;
 
 drop_and_free:
@@ -408,13 +377,16 @@ drop:
        return -1;
 }
 
-static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
+static struct sock *dccp_v6_request_recv_sock(const struct sock *sk,
                                              struct sk_buff *skb,
                                              struct request_sock *req,
-                                             struct dst_entry *dst)
+                                             struct dst_entry *dst,
+                                             struct request_sock *req_unhash,
+                                             bool *own_req)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
-       struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
+       struct ipv6_pinfo *newnp;
+       const struct ipv6_pinfo *np = inet6_sk(sk);
        struct inet_sock *newinet;
        struct dccp6_sock *newdp6;
        struct sock *newsk;
@@ -423,7 +395,8 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
                /*
                 *      v6 mapped
                 */
-               newsk = dccp_v4_request_recv_sock(sk, skb, req, dst);
+               newsk = dccp_v4_request_recv_sock(sk, skb, req, dst,
+                                                 req_unhash, own_req);
                if (newsk == NULL)
                        return NULL;
 
@@ -462,22 +435,11 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
        if (sk_acceptq_is_full(sk))
                goto out_overflow;
 
-       if (dst == NULL) {
-               struct in6_addr *final_p, final;
+       if (!dst) {
                struct flowi6 fl6;
 
-               memset(&fl6, 0, sizeof(fl6));
-               fl6.flowi6_proto = IPPROTO_DCCP;
-               fl6.daddr = ireq->ir_v6_rmt_addr;
-               final_p = fl6_update_dst(&fl6, np->opt, &final);
-               fl6.saddr = ireq->ir_v6_loc_addr;
-               fl6.flowi6_oif = sk->sk_bound_dev_if;
-               fl6.fl6_dport = ireq->ir_rmt_port;
-               fl6.fl6_sport = htons(ireq->ir_num);
-               security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
-
-               dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
-               if (IS_ERR(dst))
+               dst = inet6_csk_route_req(sk, &fl6, req, IPPROTO_DCCP);
+               if (!dst)
                        goto out;
        }
 
@@ -515,15 +477,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
        /* Clone RX bits */
        newnp->rxopt.all = np->rxopt.all;
 
-       /* Clone pktoptions received with SYN */
        newnp->pktoptions = NULL;
-       if (ireq->pktopts != NULL) {
-               newnp->pktoptions = skb_clone(ireq->pktopts, GFP_ATOMIC);
-               consume_skb(ireq->pktopts);
-               ireq->pktopts = NULL;
-               if (newnp->pktoptions)
-                       skb_set_owner_r(newnp->pktoptions, newsk);
-       }
        newnp->opt        = NULL;
        newnp->mcast_oif  = inet6_iif(skb);
        newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
@@ -552,7 +506,15 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
                dccp_done(newsk);
                goto out;
        }
-       __inet_hash(newsk, NULL);
+       *own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash));
+       /* Clone pktoptions received with SYN, if we own the req */
+       if (*own_req && ireq->pktopts) {
+               newnp->pktoptions = skb_clone(ireq->pktopts, GFP_ATOMIC);
+               consume_skb(ireq->pktopts);
+               ireq->pktopts = NULL;
+               if (newnp->pktoptions)
+                       skb_set_owner_r(newnp->pktoptions, newsk);
+       }
 
        return newsk;
 
@@ -651,24 +613,6 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
         * NOTE: the check for the packet types is done in
         *       dccp_rcv_state_process
         */
-       if (sk->sk_state == DCCP_LISTEN) {
-               struct sock *nsk = dccp_v6_hnd_req(sk, skb);
-
-               if (nsk == NULL)
-                       goto discard;
-               /*
-                * Queue it on the new socket if the new socket is active,
-                * otherwise we just shortcircuit this and continue with
-                * the new socket..
-                */
-               if (nsk != sk) {
-                       if (dccp_child_process(sk, nsk, skb))
-                               goto reset;
-                       if (opt_skb != NULL)
-                               __kfree_skb(opt_skb);
-                       return 0;
-               }
-       }
 
        if (dccp_rcv_state_process(sk, skb, dccp_hdr(skb), skb->len))
                goto reset;
@@ -715,16 +659,11 @@ static int dccp_v6_rcv(struct sk_buff *skb)
        else
                DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb);
 
-       /* Step 2:
-        *      Look up flow ID in table and get corresponding socket */
+lookup:
        sk = __inet6_lookup_skb(&dccp_hashinfo, skb,
                                dh->dccph_sport, dh->dccph_dport,
                                inet6_iif(skb));
-       /*
-        * Step 2:
-        *      If no socket ...
-        */
-       if (sk == NULL) {
+       if (!sk) {
                dccp_pr_debug("failed to look up flow ID in table and "
                              "get corresponding socket\n");
                goto no_dccp_socket;
@@ -742,6 +681,31 @@ static int dccp_v6_rcv(struct sk_buff *skb)
                goto no_dccp_socket;
        }
 
+       if (sk->sk_state == DCCP_NEW_SYN_RECV) {
+               struct request_sock *req = inet_reqsk(sk);
+               struct sock *nsk = NULL;
+
+               sk = req->rsk_listener;
+               if (likely(sk->sk_state == DCCP_LISTEN)) {
+                       nsk = dccp_check_req(sk, skb, req);
+               } else {
+                       inet_csk_reqsk_queue_drop_and_put(sk, req);
+                       goto lookup;
+               }
+               if (!nsk) {
+                       reqsk_put(req);
+                       goto discard_it;
+               }
+               if (nsk == sk) {
+                       sock_hold(sk);
+                       reqsk_put(req);
+               } else if (dccp_child_process(sk, nsk, skb)) {
+                       dccp_v6_ctl_send_reset(sk, skb);
+                       goto discard_it;
+               } else {
+                       return 0;
+               }
+       }
        /*
         * RFC 4340, sec. 9.2.1: Minimum Checksum Coverage
         *      o if MinCsCov = 0, only packets with CsCov = 0 are accepted
index 838f524cf11a177b473ca83e3d7f597ee2904d21..1994f8af646b15fe668c01b207567f9016865c4f 100644 (file)
@@ -72,7 +72,7 @@ void dccp_time_wait(struct sock *sk, int state, int timeo)
        dccp_done(sk);
 }
 
-struct sock *dccp_create_openreq_child(struct sock *sk,
+struct sock *dccp_create_openreq_child(const struct sock *sk,
                                       const struct request_sock *req,
                                       const struct sk_buff *skb)
 {
@@ -143,6 +143,7 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
 {
        struct sock *child = NULL;
        struct dccp_request_sock *dreq = dccp_rsk(req);
+       bool own_req;
 
        /* Check for retransmitted REQUEST */
        if (dccp_hdr(skb)->dccph_type == DCCP_PKT_REQUEST) {
@@ -182,14 +183,13 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
        if (dccp_parse_options(sk, dreq, skb))
                 goto drop;
 
-       child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
-       if (child == NULL)
+       child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
+                                                        req, &own_req);
+       if (!child)
                goto listen_overflow;
 
-       inet_csk_reqsk_queue_drop(sk, req);
-       inet_csk_reqsk_queue_add(sk, req, child);
-out:
-       return child;
+       return inet_csk_complete_hashdance(sk, child, req, own_req);
+
 listen_overflow:
        dccp_pr_debug("listen_overflow!\n");
        DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY;
@@ -198,7 +198,7 @@ drop:
                req->rsk_ops->send_reset(sk, skb);
 
        inet_csk_reqsk_queue_drop(sk, req);
-       goto out;
+       return NULL;
 }
 
 EXPORT_SYMBOL_GPL(dccp_check_req);
@@ -236,7 +236,7 @@ int dccp_child_process(struct sock *parent, struct sock *child,
 
 EXPORT_SYMBOL_GPL(dccp_child_process);
 
-void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
+void dccp_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
                         struct request_sock *rsk)
 {
        DCCP_BUG("DCCP-ACK packets are never sent in LISTEN/RESPOND state");
index 0248e8a3460c829bf8da8b47b8b7a525f0b85473..4ce912e691d03e925e3fef59d53b0724bd13ce83 100644 (file)
@@ -390,7 +390,7 @@ int dccp_retransmit_skb(struct sock *sk)
        return dccp_transmit_skb(sk, skb_clone(sk->sk_send_head, GFP_ATOMIC));
 }
 
-struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
+struct sk_buff *dccp_make_response(const struct sock *sk, struct dst_entry *dst,
                                   struct request_sock *req)
 {
        struct dccp_hdr *dh;
@@ -398,13 +398,18 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
        const u32 dccp_header_size = sizeof(struct dccp_hdr) +
                                     sizeof(struct dccp_hdr_ext) +
                                     sizeof(struct dccp_hdr_response);
-       struct sk_buff *skb = sock_wmalloc(sk, sk->sk_prot->max_header, 1,
-                                          GFP_ATOMIC);
-       if (skb == NULL)
+       struct sk_buff *skb;
+
+       /* sk is marked const to clearly express we dont hold socket lock.
+        * sock_wmalloc() will atomically change sk->sk_wmem_alloc,
+        * it is safe to promote sk to non const.
+        */
+       skb = sock_wmalloc((struct sock *)sk, MAX_DCCP_HEADER, 1,
+                          GFP_ATOMIC);
+       if (!skb)
                return NULL;
 
-       /* Reserve space for headers. */
-       skb_reserve(skb, sk->sk_prot->max_header);
+       skb_reserve(skb, MAX_DCCP_HEADER);
 
        skb_dst_set(skb, dst_clone(dst));
 
index d8346d0eadebf3018aa0358a98be65a2d111d984..3d3fda05b32d7fb6769a77ffe781d524f75e70d3 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/module.h>
 #include <linux/kfifo.h>
 #include <linux/vmalloc.h>
+#include <linux/time64.h>
 #include <linux/gfp.h>
 #include <net/net_namespace.h>
 
@@ -47,20 +48,20 @@ static struct {
        struct kfifo      fifo;
        spinlock_t        lock;
        wait_queue_head_t wait;
-       struct timespec   tstart;
+       struct timespec64 tstart;
 } dccpw;
 
 static void printl(const char *fmt, ...)
 {
        va_list args;
        int len;
-       struct timespec now;
+       struct timespec64 now;
        char tbuf[256];
 
        va_start(args, fmt);
-       getnstimeofday(&now);
+       getnstimeofday64(&now);
 
-       now = timespec_sub(now, dccpw.tstart);
+       now = timespec64_sub(now, dccpw.tstart);
 
        len = sprintf(tbuf, "%lu.%06lu ",
                      (unsigned long) now.tv_sec,
@@ -110,7 +111,7 @@ static struct jprobe dccp_send_probe = {
 static int dccpprobe_open(struct inode *inode, struct file *file)
 {
        kfifo_reset(&dccpw.fifo);
-       getnstimeofday(&dccpw.tstart);
+       getnstimeofday64(&dccpw.tstart);
        return 0;
 }
 
index 4507b188fc5109c6dced018c7b2159e9b1a8c3b2..482730cd8a562e048b08f275551361ca813a8792 100644 (file)
@@ -194,7 +194,7 @@ static int dn_neigh_output(struct neighbour *neigh, struct sk_buff *skb)
        return err;
 }
 
-static int dn_neigh_output_packet(struct sock *sk, struct sk_buff *skb)
+static int dn_neigh_output_packet(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct dn_route *rt = (struct dn_route *)dst;
@@ -246,8 +246,9 @@ static int dn_long_output(struct neighbour *neigh, struct sock *sk,
 
        skb_reset_network_header(skb);
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb,
-                      NULL, neigh->dev, dn_neigh_output_packet);
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING,
+                      &init_net, sk, skb, NULL, neigh->dev,
+                      dn_neigh_output_packet);
 }
 
 /*
@@ -286,8 +287,9 @@ static int dn_short_output(struct neighbour *neigh, struct sock *sk,
 
        skb_reset_network_header(skb);
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb,
-                      NULL, neigh->dev, dn_neigh_output_packet);
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING,
+                      &init_net, sk, skb, NULL, neigh->dev,
+                      dn_neigh_output_packet);
 }
 
 /*
@@ -327,11 +329,12 @@ static int dn_phase3_output(struct neighbour *neigh, struct sock *sk,
 
        skb_reset_network_header(skb);
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb,
-                      NULL, neigh->dev, dn_neigh_output_packet);
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING,
+                      &init_net, sk, skb, NULL, neigh->dev,
+                      dn_neigh_output_packet);
 }
 
-int dn_to_neigh_output(struct sock *sk, struct sk_buff *skb)
+int dn_to_neigh_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct dn_route *rt = (struct dn_route *) dst;
@@ -375,7 +378,7 @@ void dn_neigh_pointopoint_hello(struct sk_buff *skb)
 /*
  * Ethernet router hello message received
  */
-int dn_neigh_router_hello(struct sock *sk, struct sk_buff *skb)
+int dn_neigh_router_hello(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data;
 
@@ -437,7 +440,7 @@ int dn_neigh_router_hello(struct sock *sk, struct sk_buff *skb)
 /*
  * Endnode hello message received
  */
-int dn_neigh_endnode_hello(struct sock *sk, struct sk_buff *skb)
+int dn_neigh_endnode_hello(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data;
        struct neighbour *neigh;
index a321eac9fd0c5755f2d902b468137b413b092d46..7ac086d5c0c017a54534a1a986fefd88e0bcb12f 100644 (file)
@@ -714,7 +714,8 @@ out:
        return ret;
 }
 
-static int dn_nsp_rx_packet(struct sock *sk2, struct sk_buff *skb)
+static int dn_nsp_rx_packet(struct net *net, struct sock *sk2,
+                           struct sk_buff *skb)
 {
        struct dn_skb_cb *cb = DN_SKB_CB(skb);
        struct sock *sk = NULL;
@@ -814,8 +815,8 @@ free_out:
 
 int dn_nsp_rx(struct sk_buff *skb)
 {
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_IN, NULL, skb,
-                      skb->dev, NULL,
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_IN,
+                      &init_net, NULL, skb, skb->dev, NULL,
                       dn_nsp_rx_packet);
 }
 
index 1aaa51ebbda6ed1271b2a44820860f4d338b2f66..849805e7af523a0f1159c5e3b6914a4ccd99b353 100644 (file)
@@ -85,7 +85,7 @@ static void dn_nsp_send(struct sk_buff *skb)
        if (dst) {
 try_again:
                skb_dst_set(skb, dst);
-               dst_output(skb);
+               dst_output(&init_net, skb->sk, skb);
                return;
        }
 
@@ -582,7 +582,7 @@ static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg,
         * associations.
         */
        skb_dst_set(skb, dst_clone(dst));
-       dst_output(skb);
+       dst_output(&init_net, skb->sk, skb);
 }
 
 
index 03227ffd19ce02c1a506ebd606813edb853a3e75..607a14f20d88011e6de8540b21a69e6527d49df0 100644 (file)
@@ -512,7 +512,7 @@ static int dn_return_long(struct sk_buff *skb)
  *
  * Returns: result of input function if route is found, error code otherwise
  */
-static int dn_route_rx_packet(struct sock *sk, struct sk_buff *skb)
+static int dn_route_rx_packet(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dn_skb_cb *cb;
        int err;
@@ -573,8 +573,8 @@ static int dn_route_rx_long(struct sk_buff *skb)
        ptr++;
        cb->hops = *ptr++; /* Visit Count */
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, NULL, skb,
-                      skb->dev, NULL,
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING,
+                      &init_net, NULL, skb, skb->dev, NULL,
                       dn_route_rx_packet);
 
 drop_it:
@@ -601,8 +601,8 @@ static int dn_route_rx_short(struct sk_buff *skb)
        ptr += 2;
        cb->hops = *ptr & 0x3f;
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, NULL, skb,
-                      skb->dev, NULL,
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING,
+                      &init_net, NULL, skb, skb->dev, NULL,
                       dn_route_rx_packet);
 
 drop_it:
@@ -610,7 +610,7 @@ drop_it:
        return NET_RX_DROP;
 }
 
-static int dn_route_discard(struct sock *sk, struct sk_buff *skb)
+static int dn_route_discard(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        /*
         * I know we drop the packet here, but thats considered success in
@@ -620,7 +620,7 @@ static int dn_route_discard(struct sock *sk, struct sk_buff *skb)
        return NET_RX_SUCCESS;
 }
 
-static int dn_route_ptp_hello(struct sock *sk, struct sk_buff *skb)
+static int dn_route_ptp_hello(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        dn_dev_hello(skb);
        dn_neigh_pointopoint_hello(skb);
@@ -706,22 +706,22 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type
                switch (flags & DN_RT_CNTL_MSK) {
                case DN_RT_PKT_HELO:
                        return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
-                                      NULL, skb, skb->dev, NULL,
+                                      &init_net, NULL, skb, skb->dev, NULL,
                                       dn_route_ptp_hello);
 
                case DN_RT_PKT_L1RT:
                case DN_RT_PKT_L2RT:
                        return NF_HOOK(NFPROTO_DECNET, NF_DN_ROUTE,
-                                      NULL, skb, skb->dev, NULL,
+                                      &init_net, NULL, skb, skb->dev, NULL,
                                       dn_route_discard);
                case DN_RT_PKT_ERTH:
                        return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
-                                      NULL, skb, skb->dev, NULL,
+                                      &init_net, NULL, skb, skb->dev, NULL,
                                       dn_neigh_router_hello);
 
                case DN_RT_PKT_EEDH:
                        return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
-                                      NULL, skb, skb->dev, NULL,
+                                      &init_net, NULL, skb, skb->dev, NULL,
                                       dn_neigh_endnode_hello);
                }
        } else {
@@ -744,7 +744,7 @@ out:
        return NET_RX_DROP;
 }
 
-static int dn_output(struct sock *sk, struct sk_buff *skb)
+static int dn_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct dn_route *rt = (struct dn_route *)dst;
@@ -770,8 +770,8 @@ static int dn_output(struct sock *sk, struct sk_buff *skb)
        cb->rt_flags |= DN_RT_F_IE;
        cb->hops = 0;
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT, sk, skb,
-                      NULL, dev,
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT,
+                      &init_net, sk, skb, NULL, dev,
                       dn_to_neigh_output);
 
 error:
@@ -789,9 +789,7 @@ static int dn_forward(struct sk_buff *skb)
        struct dn_dev *dn_db = rcu_dereference(dst->dev->dn_ptr);
        struct dn_route *rt;
        int header_len;
-#ifdef CONFIG_NETFILTER
        struct net_device *dev = skb->dev;
-#endif
 
        if (skb->pkt_type != PACKET_HOST)
                goto drop;
@@ -819,8 +817,8 @@ static int dn_forward(struct sk_buff *skb)
        if (rt->rt_flags & RTCF_DOREDIRECT)
                cb->rt_flags |= DN_RT_F_IE;
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD, NULL, skb,
-                      dev, skb->dev,
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD,
+                      &init_net, NULL, skb, dev, skb->dev,
                       dn_to_neigh_output);
 
 drop:
@@ -832,7 +830,7 @@ drop:
  * Used to catch bugs. This should never normally get
  * called.
  */
-static int dn_rt_bug_sk(struct sock *sk, struct sk_buff *skb)
+static int dn_rt_bug_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dn_skb_cb *cb = DN_SKB_CB(skb);
 
@@ -1469,7 +1467,7 @@ make_route:
 
        rt->n = neigh;
        rt->dst.lastuse = jiffies;
-       rt->dst.output = dn_rt_bug_sk;
+       rt->dst.output = dn_rt_bug_out;
        switch (res.type) {
        case RTN_UNICAST:
                rt->dst.input = dn_forward;
index af34fc9bdf69768e45e3e772929410fa0eeee41c..85f2fdc360c27b21cb5b9c486e2d05ffb1d557d7 100644 (file)
@@ -87,7 +87,7 @@ static void dnrmg_send_peer(struct sk_buff *skb)
 }
 
 
-static unsigned int dnrmg_hook(const struct nf_hook_ops *ops,
+static unsigned int dnrmg_hook(void *priv,
                        struct sk_buff *skb,
                        const struct nf_hook_state *state)
 {
index adb5325f49348412efb50f0eeee5a5f0fa1e50ed..1eba07feb34adb451734e18e1c73031c9b7b2e35 100644 (file)
@@ -327,8 +327,8 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
 
                ret = dsa_slave_create(ds, parent, i, pd->port_names[i]);
                if (ret < 0) {
-                       netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s)\n",
-                                  index, i, pd->port_names[i]);
+                       netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s): %d\n",
+                                  index, i, pd->port_names[i], ret);
                        ret = 0;
                }
        }
index 7d91f4612ac07406cfffd90defb6aa1d7436cb36..7bc787b095c897922751c3eaa5c2b32d5bca95fa 100644 (file)
@@ -242,17 +242,15 @@ static int dsa_bridge_check_vlan_range(struct dsa_switch *ds,
 }
 
 static int dsa_slave_port_vlan_add(struct net_device *dev,
-                                  struct switchdev_obj *obj)
+                                  const struct switchdev_obj_port_vlan *vlan,
+                                  struct switchdev_trans *trans)
 {
-       struct switchdev_obj_vlan *vlan = &obj->u.vlan;
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
-       u16 vid;
        int err;
 
-       switch (obj->trans) {
-       case SWITCHDEV_TRANS_PREPARE:
-               if (!ds->drv->port_vlan_add || !ds->drv->port_pvid_set)
+       if (switchdev_trans_ph_prepare(trans)) {
+               if (!ds->drv->port_vlan_prepare || !ds->drv->port_vlan_add)
                        return -EOPNOTSUPP;
 
                /* If the requested port doesn't belong to the same bridge as
@@ -263,50 +261,35 @@ static int dsa_slave_port_vlan_add(struct net_device *dev,
                                                  vlan->vid_end);
                if (err)
                        return err;
-               break;
-       case SWITCHDEV_TRANS_COMMIT:
-               for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-                       err = ds->drv->port_vlan_add(ds, p->port, vid,
-                                                    vlan->flags &
-                                                    BRIDGE_VLAN_INFO_UNTAGGED);
-                       if (!err && vlan->flags & BRIDGE_VLAN_INFO_PVID)
-                               err = ds->drv->port_pvid_set(ds, p->port, vid);
-                       if (err)
-                               return err;
-               }
-               break;
-       default:
-               return -EOPNOTSUPP;
+
+               err = ds->drv->port_vlan_prepare(ds, p->port, vlan, trans);
+               if (err)
+                       return err;
+       } else {
+               err = ds->drv->port_vlan_add(ds, p->port, vlan, trans);
+               if (err)
+                       return err;
        }
 
        return 0;
 }
 
 static int dsa_slave_port_vlan_del(struct net_device *dev,
-                                  struct switchdev_obj *obj)
+                                  const struct switchdev_obj_port_vlan *vlan)
 {
-       struct switchdev_obj_vlan *vlan = &obj->u.vlan;
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
-       u16 vid;
-       int err;
 
        if (!ds->drv->port_vlan_del)
                return -EOPNOTSUPP;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               err = ds->drv->port_vlan_del(ds, p->port, vid);
-               if (err)
-                       return err;
-       }
-
-       return 0;
+       return ds->drv->port_vlan_del(ds, p->port, vlan);
 }
 
 static int dsa_slave_port_vlan_dump(struct net_device *dev,
-                                   struct switchdev_obj *obj)
+                                   struct switchdev_obj_port_vlan *vlan,
+                                   switchdev_obj_dump_cb_t *cb)
 {
-       struct switchdev_obj_vlan *vlan = &obj->u.vlan;
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
        DECLARE_BITMAP(members, DSA_MAX_PORTS);
@@ -338,7 +321,7 @@ static int dsa_slave_port_vlan_dump(struct net_device *dev,
                if (test_bit(p->port, untagged))
                        vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 
-               err = obj->cb(dev, obj);
+               err = cb(&vlan->obj);
                if (err)
                        break;
        }
@@ -347,65 +330,48 @@ static int dsa_slave_port_vlan_dump(struct net_device *dev,
 }
 
 static int dsa_slave_port_fdb_add(struct net_device *dev,
-                                 struct switchdev_obj *obj)
+                                 const struct switchdev_obj_port_fdb *fdb,
+                                 struct switchdev_trans *trans)
 {
-       struct switchdev_obj_fdb *fdb = &obj->u.fdb;
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
-       int ret = -EOPNOTSUPP;
+       int ret;
 
-       if (obj->trans == SWITCHDEV_TRANS_PREPARE)
-               ret = ds->drv->port_fdb_add ? 0 : -EOPNOTSUPP;
-       else if (obj->trans == SWITCHDEV_TRANS_COMMIT)
-               ret = ds->drv->port_fdb_add(ds, p->port, fdb->addr, fdb->vid);
+       if (!ds->drv->port_fdb_prepare || !ds->drv->port_fdb_add)
+               return -EOPNOTSUPP;
+
+       if (switchdev_trans_ph_prepare(trans))
+               ret = ds->drv->port_fdb_prepare(ds, p->port, fdb, trans);
+       else
+               ret = ds->drv->port_fdb_add(ds, p->port, fdb, trans);
 
        return ret;
 }
 
 static int dsa_slave_port_fdb_del(struct net_device *dev,
-                                 struct switchdev_obj *obj)
+                                 const struct switchdev_obj_port_fdb *fdb)
 {
-       struct switchdev_obj_fdb *fdb = &obj->u.fdb;
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
        int ret = -EOPNOTSUPP;
 
        if (ds->drv->port_fdb_del)
-               ret = ds->drv->port_fdb_del(ds, p->port, fdb->addr, fdb->vid);
+               ret = ds->drv->port_fdb_del(ds, p->port, fdb);
 
        return ret;
 }
 
 static int dsa_slave_port_fdb_dump(struct net_device *dev,
-                                  struct switchdev_obj *obj)
+                                  struct switchdev_obj_port_fdb *fdb,
+                                  switchdev_obj_dump_cb_t *cb)
 {
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
-       unsigned char addr[ETH_ALEN] = { 0 };
-       u16 vid = 0;
-       int ret;
-
-       if (!ds->drv->port_fdb_getnext)
-               return -EOPNOTSUPP;
-
-       for (;;) {
-               bool is_static;
 
-               ret = ds->drv->port_fdb_getnext(ds, p->port, addr, &vid,
-                                               &is_static);
-               if (ret < 0)
-                       break;
-
-               obj->u.fdb.addr = addr;
-               obj->u.fdb.vid = vid;
-               obj->u.fdb.ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
-
-               ret = obj->cb(dev, obj);
-               if (ret < 0)
-                       break;
-       }
+       if (ds->drv->port_fdb_dump)
+               return ds->drv->port_fdb_dump(ds, p->port, fdb, cb);
 
-       return ret == -ENOENT ? 0 : ret;
+       return -EOPNOTSUPP;
 }
 
 static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
@@ -456,15 +422,16 @@ static int dsa_slave_stp_update(struct net_device *dev, u8 state)
 }
 
 static int dsa_slave_port_attr_set(struct net_device *dev,
-                                  struct switchdev_attr *attr)
+                                  const struct switchdev_attr *attr,
+                                  struct switchdev_trans *trans)
 {
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct dsa_switch *ds = p->parent;
        int ret;
 
        switch (attr->id) {
-       case SWITCHDEV_ATTR_PORT_STP_STATE:
-               if (attr->trans == SWITCHDEV_TRANS_PREPARE)
+       case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+               if (switchdev_trans_ph_prepare(trans))
                        ret = ds->drv->port_stp_update ? 0 : -EOPNOTSUPP;
                else
                        ret = ds->drv->port_stp_update(ds, p->port,
@@ -479,7 +446,8 @@ static int dsa_slave_port_attr_set(struct net_device *dev,
 }
 
 static int dsa_slave_port_obj_add(struct net_device *dev,
-                                 struct switchdev_obj *obj)
+                                 const struct switchdev_obj *obj,
+                                 struct switchdev_trans *trans)
 {
        int err;
 
@@ -489,11 +457,15 @@ static int dsa_slave_port_obj_add(struct net_device *dev,
         */
 
        switch (obj->id) {
-       case SWITCHDEV_OBJ_PORT_FDB:
-               err = dsa_slave_port_fdb_add(dev, obj);
+       case SWITCHDEV_OBJ_ID_PORT_FDB:
+               err = dsa_slave_port_fdb_add(dev,
+                                            SWITCHDEV_OBJ_PORT_FDB(obj),
+                                            trans);
                break;
-       case SWITCHDEV_OBJ_PORT_VLAN:
-               err = dsa_slave_port_vlan_add(dev, obj);
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               err = dsa_slave_port_vlan_add(dev,
+                                             SWITCHDEV_OBJ_PORT_VLAN(obj),
+                                             trans);
                break;
        default:
                err = -EOPNOTSUPP;
@@ -504,16 +476,18 @@ static int dsa_slave_port_obj_add(struct net_device *dev,
 }
 
 static int dsa_slave_port_obj_del(struct net_device *dev,
-                                 struct switchdev_obj *obj)
+                                 const struct switchdev_obj *obj)
 {
        int err;
 
        switch (obj->id) {
-       case SWITCHDEV_OBJ_PORT_FDB:
-               err = dsa_slave_port_fdb_del(dev, obj);
+       case SWITCHDEV_OBJ_ID_PORT_FDB:
+               err = dsa_slave_port_fdb_del(dev,
+                                            SWITCHDEV_OBJ_PORT_FDB(obj));
                break;
-       case SWITCHDEV_OBJ_PORT_VLAN:
-               err = dsa_slave_port_vlan_del(dev, obj);
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               err = dsa_slave_port_vlan_del(dev,
+                                             SWITCHDEV_OBJ_PORT_VLAN(obj));
                break;
        default:
                err = -EOPNOTSUPP;
@@ -524,16 +498,21 @@ static int dsa_slave_port_obj_del(struct net_device *dev,
 }
 
 static int dsa_slave_port_obj_dump(struct net_device *dev,
-                                  struct switchdev_obj *obj)
+                                  struct switchdev_obj *obj,
+                                  switchdev_obj_dump_cb_t *cb)
 {
        int err;
 
        switch (obj->id) {
-       case SWITCHDEV_OBJ_PORT_FDB:
-               err = dsa_slave_port_fdb_dump(dev, obj);
+       case SWITCHDEV_OBJ_ID_PORT_FDB:
+               err = dsa_slave_port_fdb_dump(dev,
+                                             SWITCHDEV_OBJ_PORT_FDB(obj),
+                                             cb);
                break;
-       case SWITCHDEV_OBJ_PORT_VLAN:
-               err = dsa_slave_port_vlan_dump(dev, obj);
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               err = dsa_slave_port_vlan_dump(dev,
+                                              SWITCHDEV_OBJ_PORT_VLAN(obj),
+                                              cb);
                break;
        default:
                err = -EOPNOTSUPP;
@@ -587,7 +566,7 @@ static int dsa_slave_port_attr_get(struct net_device *dev,
        struct dsa_switch *ds = p->parent;
 
        switch (attr->id) {
-       case SWITCHDEV_ATTR_PORT_PARENT_ID:
+       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
                attr->u.ppid.id_len = sizeof(ds->index);
                memcpy(&attr->u.ppid.id, &ds->index, attr->u.ppid.id_len);
                break;
@@ -967,6 +946,10 @@ static const struct switchdev_ops dsa_slave_switchdev_ops = {
        .switchdev_port_obj_dump        = dsa_slave_port_obj_dump,
 };
 
+static struct device_type dsa_type = {
+       .name   = "dsa",
+};
+
 static void dsa_slave_adjust_link(struct net_device *dev)
 {
        struct dsa_slave_priv *p = netdev_priv(dev);
@@ -1015,8 +998,10 @@ static int dsa_slave_phy_connect(struct dsa_slave_priv *p,
        struct dsa_switch *ds = p->parent;
 
        p->phy = ds->slave_mii_bus->phy_map[addr];
-       if (!p->phy)
+       if (!p->phy) {
+               netdev_err(slave_dev, "no phy at %d\n", addr);
                return -ENODEV;
+       }
 
        /* Use already configured phy mode */
        if (p->phy_interface == PHY_INTERFACE_MODE_NA)
@@ -1050,7 +1035,7 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
                 */
                ret = of_phy_register_fixed_link(port_dn);
                if (ret) {
-                       netdev_err(slave_dev, "failed to register fixed PHY\n");
+                       netdev_err(slave_dev, "failed to register fixed PHY: %d\n", ret);
                        return ret;
                }
                phy_is_fixed = true;
@@ -1061,17 +1046,20 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
                phy_flags = ds->drv->get_phy_flags(ds, p->port);
 
        if (phy_dn) {
-               ret = of_mdio_parse_addr(&slave_dev->dev, phy_dn);
+               int phy_id = of_mdio_parse_addr(&slave_dev->dev, phy_dn);
+
                /* If this PHY address is part of phys_mii_mask, which means
                 * that we need to divert reads and writes to/from it, then we
                 * want to bind this device using the slave MII bus created by
                 * DSA to make that happen.
                 */
-               if (!phy_is_fixed && ret >= 0 &&
-                   (ds->phys_mii_mask & (1 << ret))) {
-                       ret = dsa_slave_phy_connect(p, slave_dev, ret);
-                       if (ret)
+               if (!phy_is_fixed && phy_id >= 0 &&
+                   (ds->phys_mii_mask & (1 << phy_id))) {
+                       ret = dsa_slave_phy_connect(p, slave_dev, phy_id);
+                       if (ret) {
+                               netdev_err(slave_dev, "failed to connect to phy%d: %d\n", phy_id, ret);
                                return ret;
+                       }
                } else {
                        p->phy = of_phy_connect(slave_dev, phy_dn,
                                                dsa_slave_adjust_link,
@@ -1088,8 +1076,10 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
         */
        if (!p->phy) {
                ret = dsa_slave_phy_connect(p, slave_dev, p->port);
-               if (ret)
+               if (ret) {
+                       netdev_err(slave_dev, "failed to connect to port %d: %d\n", p->port, ret);
                        return ret;
+               }
        } else {
                netdev_info(slave_dev, "attached PHY at address %d [%s]\n",
                            p->phy->addr, p->phy->drv->name);
@@ -1155,6 +1145,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
        slave_dev->priv_flags |= IFF_NO_QUEUE;
        slave_dev->netdev_ops = &dsa_slave_netdev_ops;
        slave_dev->switchdev_ops = &dsa_slave_switchdev_ops;
+       SET_NETDEV_DEVTYPE(slave_dev, &dsa_type);
 
        netdev_for_each_tx_queue(slave_dev, dsa_slave_set_lockdep_class_one,
                                 NULL);
@@ -1200,6 +1191,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
 
        ret = dsa_slave_phy_setup(p, slave_dev);
        if (ret) {
+               netdev_err(master, "error %d setting up slave phy\n", ret);
                free_netdev(slave_dev);
                return ret;
        }
@@ -1253,7 +1245,7 @@ int dsa_slave_netdevice_event(struct notifier_block *unused,
                        goto out;
 
                err = dsa_slave_master_changed(dev);
-               if (err)
+               if (err && err != -EOPNOTSUPP)
                        netdev_warn(dev, "failed to reflect master change\n");
 
                break;
index d850fdc828f932c20191741a75bf90acffb9f55f..9e63f252a89ead08fda157e716607938452c0b49 100644 (file)
@@ -127,7 +127,7 @@ u32 eth_get_headlen(void *data, unsigned int len)
        struct flow_keys keys;
 
        /* this should never happen, but better safe than sorry */
-       if (len < sizeof(*eth))
+       if (unlikely(len < sizeof(*eth)))
                return len;
 
        /* parse any remaining L2/L3 headers, check for L4 */
index ea339fa94c27c5006ca8121073c5d4b69a9b8e23..b4e17a7c0df0838f7aed6ac4fa4705a70699a7a8 100644 (file)
@@ -7,6 +7,15 @@
 #include <net/inet_frag.h>
 #include <net/6lowpan.h>
 
+typedef unsigned __bitwise__ lowpan_rx_result;
+#define RX_CONTINUE            ((__force lowpan_rx_result) 0u)
+#define RX_DROP_UNUSABLE       ((__force lowpan_rx_result) 1u)
+#define RX_DROP                        ((__force lowpan_rx_result) 2u)
+#define RX_QUEUED              ((__force lowpan_rx_result) 3u)
+
+#define LOWPAN_DISPATCH_FRAG1           0xc0
+#define LOWPAN_DISPATCH_FRAGN           0xe0
+
 struct lowpan_create_arg {
        u16 tag;
        u16 d_size;
@@ -40,7 +49,7 @@ static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a)
 
 /* private device info */
 struct lowpan_dev_info {
-       struct net_device       *real_dev; /* real WPAN device ptr */
+       struct net_device       *wdev; /* wpan device ptr */
        u16                     fragment_tag;
 };
 
@@ -62,4 +71,7 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
                         const void *_saddr, unsigned int len);
 netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev);
 
+int lowpan_iphc_decompress(struct sk_buff *skb);
+lowpan_rx_result lowpan_rx_h_ipv6(struct sk_buff *skb);
+
 #endif /* __IEEE802154_6LOWPAN_I_H__ */
index 953b1c49f5d1e42019752d86021fd7690272232e..20c49c724ba04a3707f038a17774012ca5ba0421 100644 (file)
@@ -61,7 +61,7 @@ static struct header_ops lowpan_header_ops = {
 static struct lock_class_key lowpan_tx_busylock;
 static struct lock_class_key lowpan_netdev_xmit_lock_key;
 
-static void lowpan_set_lockdep_class_one(struct net_device *dev,
+static void lowpan_set_lockdep_class_one(struct net_device *ldev,
                                         struct netdev_queue *txq,
                                         void *_unused)
 {
@@ -69,35 +69,47 @@ static void lowpan_set_lockdep_class_one(struct net_device *dev,
                          &lowpan_netdev_xmit_lock_key);
 }
 
-static int lowpan_dev_init(struct net_device *dev)
+static int lowpan_dev_init(struct net_device *ldev)
 {
-       netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL);
-       dev->qdisc_tx_busylock = &lowpan_tx_busylock;
+       netdev_for_each_tx_queue(ldev, lowpan_set_lockdep_class_one, NULL);
+       ldev->qdisc_tx_busylock = &lowpan_tx_busylock;
+       return 0;
+}
+
+static int lowpan_open(struct net_device *dev)
+{
+       if (!open_count)
+               lowpan_rx_init();
+       open_count++;
+       return 0;
+}
+
+static int lowpan_stop(struct net_device *dev)
+{
+       open_count--;
+       if (!open_count)
+               lowpan_rx_exit();
        return 0;
 }
 
 static const struct net_device_ops lowpan_netdev_ops = {
        .ndo_init               = lowpan_dev_init,
        .ndo_start_xmit         = lowpan_xmit,
+       .ndo_open               = lowpan_open,
+       .ndo_stop               = lowpan_stop,
 };
 
-static void lowpan_setup(struct net_device *dev)
+static void lowpan_setup(struct net_device *ldev)
 {
-       dev->addr_len           = IEEE802154_ADDR_LEN;
-       memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
-       dev->type               = ARPHRD_6LOWPAN;
-       /* Frame Control + Sequence Number + Address fields + Security Header */
-       dev->hard_header_len    = 2 + 1 + 20 + 14;
-       dev->needed_tailroom    = 2; /* FCS */
-       dev->mtu                = IPV6_MIN_MTU;
-       dev->priv_flags         |= IFF_NO_QUEUE;
-       dev->flags              = IFF_BROADCAST | IFF_MULTICAST;
-       dev->watchdog_timeo     = 0;
-
-       dev->netdev_ops         = &lowpan_netdev_ops;
-       dev->header_ops         = &lowpan_header_ops;
-       dev->destructor         = free_netdev;
-       dev->features           |= NETIF_F_NETNS_LOCAL;
+       memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN);
+       /* We need an ipv6hdr as minimum len when calling xmit */
+       ldev->hard_header_len   = sizeof(struct ipv6hdr);
+       ldev->flags             = IFF_BROADCAST | IFF_MULTICAST;
+
+       ldev->netdev_ops        = &lowpan_netdev_ops;
+       ldev->header_ops        = &lowpan_header_ops;
+       ldev->destructor        = free_netdev;
+       ldev->features          |= NETIF_F_NETNS_LOCAL;
 }
 
 static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[])
@@ -109,10 +121,10 @@ static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[])
        return 0;
 }
 
-static int lowpan_newlink(struct net *src_net, struct net_device *dev,
+static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
                          struct nlattr *tb[], struct nlattr *data[])
 {
-       struct net_device *real_dev;
+       struct net_device *wdev;
        int ret;
 
        ASSERT_RTNL();
@@ -120,58 +132,56 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
        pr_debug("adding new link\n");
 
        if (!tb[IFLA_LINK] ||
-           !net_eq(dev_net(dev), &init_net))
+           !net_eq(dev_net(ldev), &init_net))
                return -EINVAL;
-       /* find and hold real wpan device */
-       real_dev = dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK]));
-       if (!real_dev)
+       /* find and hold wpan device */
+       wdev = dev_get_by_index(dev_net(ldev), nla_get_u32(tb[IFLA_LINK]));
+       if (!wdev)
                return -ENODEV;
-       if (real_dev->type != ARPHRD_IEEE802154) {
-               dev_put(real_dev);
+       if (wdev->type != ARPHRD_IEEE802154) {
+               dev_put(wdev);
                return -EINVAL;
        }
 
-       if (real_dev->ieee802154_ptr->lowpan_dev) {
-               dev_put(real_dev);
+       if (wdev->ieee802154_ptr->lowpan_dev) {
+               dev_put(wdev);
                return -EBUSY;
        }
 
-       lowpan_dev_info(dev)->real_dev = real_dev;
+       lowpan_dev_info(ldev)->wdev = wdev;
        /* Set the lowpan hardware address to the wpan hardware address. */
-       memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN);
-
-       lowpan_netdev_setup(dev, LOWPAN_LLTYPE_IEEE802154);
-
-       ret = register_netdevice(dev);
+       memcpy(ldev->dev_addr, wdev->dev_addr, IEEE802154_ADDR_LEN);
+       /* We need headroom for possible wpan_dev_hard_header call and tailroom
+        * for encryption/fcs handling. The lowpan interface will replace
+        * the IPv6 header with 6LoWPAN header. At worst case the 6LoWPAN
+        * header has LOWPAN_IPHC_MAX_HEADER_LEN more bytes than the IPv6
+        * header.
+        */
+       ldev->needed_headroom = LOWPAN_IPHC_MAX_HEADER_LEN +
+                               wdev->needed_headroom;
+       ldev->needed_tailroom = wdev->needed_tailroom;
+
+       lowpan_netdev_setup(ldev, LOWPAN_LLTYPE_IEEE802154);
+
+       ret = register_netdevice(ldev);
        if (ret < 0) {
-               dev_put(real_dev);
+               dev_put(wdev);
                return ret;
        }
 
-       real_dev->ieee802154_ptr->lowpan_dev = dev;
-       if (!open_count)
-               lowpan_rx_init();
-
-       open_count++;
-
+       wdev->ieee802154_ptr->lowpan_dev = ldev;
        return 0;
 }
 
-static void lowpan_dellink(struct net_device *dev, struct list_head *head)
+static void lowpan_dellink(struct net_device *ldev, struct list_head *head)
 {
-       struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev);
-       struct net_device *real_dev = lowpan_dev->real_dev;
+       struct net_device *wdev = lowpan_dev_info(ldev)->wdev;
 
        ASSERT_RTNL();
 
-       open_count--;
-
-       if (!open_count)
-               lowpan_rx_exit();
-
-       real_dev->ieee802154_ptr->lowpan_dev = NULL;
-       unregister_netdevice(dev);
-       dev_put(real_dev);
+       wdev->ieee802154_ptr->lowpan_dev = NULL;
+       unregister_netdevice(ldev);
+       dev_put(wdev);
 }
 
 static struct rtnl_link_ops lowpan_link_ops __read_mostly = {
@@ -196,9 +206,9 @@ static inline void lowpan_netlink_fini(void)
 static int lowpan_device_event(struct notifier_block *unused,
                               unsigned long event, void *ptr)
 {
-       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct net_device *wdev = netdev_notifier_info_to_dev(ptr);
 
-       if (dev->type != ARPHRD_IEEE802154)
+       if (wdev->type != ARPHRD_IEEE802154)
                goto out;
 
        switch (event) {
@@ -207,8 +217,8 @@ static int lowpan_device_event(struct notifier_block *unused,
                 * also delete possible lowpan interfaces which belongs
                 * to the wpan interface.
                 */
-               if (dev->ieee802154_ptr && dev->ieee802154_ptr->lowpan_dev)
-                       lowpan_dellink(dev->ieee802154_ptr->lowpan_dev, NULL);
+               if (wdev->ieee802154_ptr->lowpan_dev)
+                       lowpan_dellink(wdev->ieee802154_ptr->lowpan_dev, NULL);
                break;
        default:
                break;
index 214d44aef35b5cd69ffe859138938e311c717cc3..6b437e8760d314c5314cc1deb2d5d13dff0f2ec8 100644 (file)
 
 static const char lowpan_frags_cache_name[] = "lowpan-frags";
 
-struct lowpan_frag_info {
-       u16 d_tag;
-       u16 d_size;
-       u8 d_offset;
-};
-
-static struct lowpan_frag_info *lowpan_cb(struct sk_buff *skb)
-{
-       return (struct lowpan_frag_info *)skb->cb;
-}
-
 static struct inet_frags lowpan_frags;
 
 static int lowpan_frag_reasm(struct lowpan_frag_queue *fq,
-                            struct sk_buff *prev, struct net_device *dev);
+                            struct sk_buff *prev, struct net_device *ldev);
 
 static unsigned int lowpan_hash_frag(u16 tag, u16 d_size,
                                     const struct ieee802154_addr *saddr,
@@ -111,7 +100,7 @@ out:
 }
 
 static inline struct lowpan_frag_queue *
-fq_find(struct net *net, const struct lowpan_frag_info *frag_info,
+fq_find(struct net *net, const struct lowpan_802154_cb *cb,
        const struct ieee802154_addr *src,
        const struct ieee802154_addr *dst)
 {
@@ -121,12 +110,12 @@ fq_find(struct net *net, const struct lowpan_frag_info *frag_info,
        struct netns_ieee802154_lowpan *ieee802154_lowpan =
                net_ieee802154_lowpan(net);
 
-       arg.tag = frag_info->d_tag;
-       arg.d_size = frag_info->d_size;
+       arg.tag = cb->d_tag;
+       arg.d_size = cb->d_size;
        arg.src = src;
        arg.dst = dst;
 
-       hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst);
+       hash = lowpan_hash_frag(cb->d_tag, cb->d_size, src, dst);
 
        q = inet_frag_find(&ieee802154_lowpan->frags,
                           &lowpan_frags, &arg, hash);
@@ -138,17 +127,17 @@ fq_find(struct net *net, const struct lowpan_frag_info *frag_info,
 }
 
 static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
-                            struct sk_buff *skb, const u8 frag_type)
+                            struct sk_buff *skb, u8 frag_type)
 {
        struct sk_buff *prev, *next;
-       struct net_device *dev;
+       struct net_device *ldev;
        int end, offset;
 
        if (fq->q.flags & INET_FRAG_COMPLETE)
                goto err;
 
-       offset = lowpan_cb(skb)->d_offset << 3;
-       end = lowpan_cb(skb)->d_size;
+       offset = lowpan_802154_cb(skb)->d_offset << 3;
+       end = lowpan_802154_cb(skb)->d_size;
 
        /* Is this the final fragment? */
        if (offset + skb->len == end) {
@@ -174,13 +163,16 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
         * this fragment, right?
         */
        prev = fq->q.fragments_tail;
-       if (!prev || lowpan_cb(prev)->d_offset < lowpan_cb(skb)->d_offset) {
+       if (!prev ||
+           lowpan_802154_cb(prev)->d_offset <
+           lowpan_802154_cb(skb)->d_offset) {
                next = NULL;
                goto found;
        }
        prev = NULL;
        for (next = fq->q.fragments; next != NULL; next = next->next) {
-               if (lowpan_cb(next)->d_offset >= lowpan_cb(skb)->d_offset)
+               if (lowpan_802154_cb(next)->d_offset >=
+                   lowpan_802154_cb(skb)->d_offset)
                        break;  /* bingo! */
                prev = next;
        }
@@ -195,18 +187,15 @@ found:
        else
                fq->q.fragments = skb;
 
-       dev = skb->dev;
-       if (dev)
+       ldev = skb->dev;
+       if (ldev)
                skb->dev = NULL;
 
        fq->q.stamp = skb->tstamp;
-       if (frag_type == LOWPAN_DISPATCH_FRAG1) {
-               /* Calculate uncomp. 6lowpan header to estimate full size */
-               fq->q.meat += lowpan_uncompress_size(skb, NULL);
+       if (frag_type == LOWPAN_DISPATCH_FRAG1)
                fq->q.flags |= INET_FRAG_FIRST_IN;
-       } else {
-               fq->q.meat += skb->len;
-       }
+
+       fq->q.meat += skb->len;
        add_frag_mem_limit(fq->q.net, skb->truesize);
 
        if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
@@ -215,7 +204,7 @@ found:
                unsigned long orefdst = skb->_skb_refdst;
 
                skb->_skb_refdst = 0UL;
-               res = lowpan_frag_reasm(fq, prev, dev);
+               res = lowpan_frag_reasm(fq, prev, ldev);
                skb->_skb_refdst = orefdst;
                return res;
        }
@@ -235,7 +224,7 @@ err:
  *     the last and the first frames arrived and all the bits are here.
  */
 static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev,
-                            struct net_device *dev)
+                            struct net_device *ldev)
 {
        struct sk_buff *fp, *head = fq->q.fragments;
        int sum_truesize;
@@ -313,7 +302,7 @@ static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev,
        sub_frag_mem_limit(fq->q.net, sum_truesize);
 
        head->next = NULL;
-       head->dev = dev;
+       head->dev = ldev;
        head->tstamp = fq->q.stamp;
 
        fq->q.fragments = NULL;
@@ -325,24 +314,87 @@ out_oom:
        return -1;
 }
 
-static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type,
-                               struct lowpan_frag_info *frag_info)
+static int lowpan_frag_rx_handlers_result(struct sk_buff *skb,
+                                         lowpan_rx_result res)
+{
+       switch (res) {
+       case RX_QUEUED:
+               return NET_RX_SUCCESS;
+       case RX_CONTINUE:
+               /* nobody cared about this packet */
+               net_warn_ratelimited("%s: received unknown dispatch\n",
+                                    __func__);
+
+               /* fall-through */
+       default:
+               /* all others failure */
+               return NET_RX_DROP;
+       }
+}
+
+static lowpan_rx_result lowpan_frag_rx_h_iphc(struct sk_buff *skb)
+{
+       int ret;
+
+       if (!lowpan_is_iphc(*skb_network_header(skb)))
+               return RX_CONTINUE;
+
+       ret = lowpan_iphc_decompress(skb);
+       if (ret < 0)
+               return RX_DROP;
+
+       return RX_QUEUED;
+}
+
+static int lowpan_invoke_frag_rx_handlers(struct sk_buff *skb)
+{
+       lowpan_rx_result res;
+
+#define CALL_RXH(rxh)                  \
+       do {                            \
+               res = rxh(skb); \
+               if (res != RX_CONTINUE) \
+                       goto rxh_next;  \
+       } while (0)
+
+       /* likely at first */
+       CALL_RXH(lowpan_frag_rx_h_iphc);
+       CALL_RXH(lowpan_rx_h_ipv6);
+
+rxh_next:
+       return lowpan_frag_rx_handlers_result(skb, res);
+#undef CALL_RXH
+}
+
+#define LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK       0x07
+#define LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT      8
+
+static int lowpan_get_cb(struct sk_buff *skb, u8 frag_type,
+                        struct lowpan_802154_cb *cb)
 {
        bool fail;
-       u8 pattern = 0, low = 0;
+       u8 high = 0, low = 0;
        __be16 d_tag = 0;
 
-       fail = lowpan_fetch_skb(skb, &pattern, 1);
+       fail = lowpan_fetch_skb(skb, &high, 1);
        fail |= lowpan_fetch_skb(skb, &low, 1);
-       frag_info->d_size = (pattern & 7) << 8 | low;
+       /* remove the dispatch value and use first three bits as high value
+        * for the datagram size
+        */
+       cb->d_size = (high & LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK) <<
+               LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT | low;
        fail |= lowpan_fetch_skb(skb, &d_tag, 2);
-       frag_info->d_tag = ntohs(d_tag);
+       cb->d_tag = ntohs(d_tag);
 
        if (frag_type == LOWPAN_DISPATCH_FRAGN) {
-               fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1);
+               fail |= lowpan_fetch_skb(skb, &cb->d_offset, 1);
        } else {
                skb_reset_network_header(skb);
-               frag_info->d_offset = 0;
+               cb->d_offset = 0;
+               /* check if datagram_size has ipv6hdr on FRAG1 */
+               fail |= cb->d_size < sizeof(struct ipv6hdr);
+               /* check if we can dereference the dispatch value */
+               fail |= !skb->len;
        }
 
        if (unlikely(fail))
@@ -351,27 +403,33 @@ static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type,
        return 0;
 }
 
-int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type)
+int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type)
 {
        struct lowpan_frag_queue *fq;
        struct net *net = dev_net(skb->dev);
-       struct lowpan_frag_info *frag_info = lowpan_cb(skb);
-       struct ieee802154_addr source, dest;
+       struct lowpan_802154_cb *cb = lowpan_802154_cb(skb);
+       struct ieee802154_hdr hdr;
        int err;
 
-       source = mac_cb(skb)->source;
-       dest = mac_cb(skb)->dest;
+       if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
+               goto err;
 
-       err = lowpan_get_frag_info(skb, frag_type, frag_info);
+       err = lowpan_get_cb(skb, frag_type, cb);
        if (err < 0)
                goto err;
 
-       if (frag_info->d_size > IPV6_MIN_MTU) {
+       if (frag_type == LOWPAN_DISPATCH_FRAG1) {
+               err = lowpan_invoke_frag_rx_handlers(skb);
+               if (err == NET_RX_DROP)
+                       goto err;
+       }
+
+       if (cb->d_size > IPV6_MIN_MTU) {
                net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n");
                goto err;
        }
 
-       fq = fq_find(net, frag_info, &source, &dest);
+       fq = fq_find(net, cb, &hdr.source, &hdr.dest);
        if (fq != NULL) {
                int ret;
 
@@ -387,7 +445,6 @@ err:
        kfree_skb(skb);
        return -1;
 }
-EXPORT_SYMBOL(lowpan_frag_rcv);
 
 #ifdef CONFIG_SYSCTL
 static int zero;
@@ -523,14 +580,19 @@ static int __net_init lowpan_frags_init_net(struct net *net)
 {
        struct netns_ieee802154_lowpan *ieee802154_lowpan =
                net_ieee802154_lowpan(net);
+       int res;
 
        ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
        ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH;
        ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT;
 
-       inet_frags_init_net(&ieee802154_lowpan->frags);
-
-       return lowpan_frags_ns_sysctl_register(net);
+       res = inet_frags_init_net(&ieee802154_lowpan->frags);
+       if (res)
+               return res;
+       res = lowpan_frags_ns_sysctl_register(net);
+       if (res)
+               inet_frags_uninit_net(&ieee802154_lowpan->frags);
+       return res;
 }
 
 static void __net_exit lowpan_frags_exit_net(struct net *net)
index 12e10201d263860d64cc7835bd947459e4d95873..ef185dd4110d74e44139e96dd6fdeec26aafb5fd 100644 (file)
 #include <linux/if_arp.h>
 
 #include <net/6lowpan.h>
+#include <net/mac802154.h>
 #include <net/ieee802154_netdev.h>
 
 #include "6lowpan_i.h"
 
-static int lowpan_give_skb_to_device(struct sk_buff *skb,
-                                    struct net_device *dev)
+#define LOWPAN_DISPATCH_FIRST          0xc0
+#define LOWPAN_DISPATCH_FRAG_MASK      0xf8
+
+#define LOWPAN_DISPATCH_NALP           0x00
+#define LOWPAN_DISPATCH_ESC            0x40
+#define LOWPAN_DISPATCH_HC1            0x42
+#define LOWPAN_DISPATCH_DFF            0x43
+#define LOWPAN_DISPATCH_BC0            0x50
+#define LOWPAN_DISPATCH_MESH           0x80
+
+static int lowpan_give_skb_to_device(struct sk_buff *skb)
 {
-       skb->dev = dev->ieee802154_ptr->lowpan_dev;
        skb->protocol = htons(ETH_P_IPV6);
-       skb->pkt_type = PACKET_HOST;
+       skb->dev->stats.rx_packets++;
+       skb->dev->stats.rx_bytes += skb->len;
 
        return netif_rx(skb);
 }
 
-static int
-iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
+static int lowpan_rx_handlers_result(struct sk_buff *skb, lowpan_rx_result res)
 {
-       u8 iphc0, iphc1;
-       struct ieee802154_addr_sa sa, da;
-       void *sap, *dap;
+       switch (res) {
+       case RX_CONTINUE:
+               /* nobody cared about this packet */
+               net_warn_ratelimited("%s: received unknown dispatch\n",
+                                    __func__);
 
-       raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len);
-       /* at least two bytes will be used for the encoding */
-       if (skb->len < 2)
-               return -EINVAL;
+               /* fall-through */
+       case RX_DROP_UNUSABLE:
+               kfree_skb(skb);
 
-       if (lowpan_fetch_skb_u8(skb, &iphc0))
-               return -EINVAL;
+               /* fall-through */
+       case RX_DROP:
+               return NET_RX_DROP;
+       case RX_QUEUED:
+               return lowpan_give_skb_to_device(skb);
+       default:
+               break;
+       }
 
-       if (lowpan_fetch_skb_u8(skb, &iphc1))
-               return -EINVAL;
+       return NET_RX_DROP;
+}
 
-       ieee802154_addr_to_sa(&sa, &hdr->source);
-       ieee802154_addr_to_sa(&da, &hdr->dest);
+static inline bool lowpan_is_frag1(u8 dispatch)
+{
+       return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAG1;
+}
 
-       if (sa.addr_type == IEEE802154_ADDR_SHORT)
-               sap = &sa.short_addr;
-       else
-               sap = &sa.hwaddr;
+static inline bool lowpan_is_fragn(u8 dispatch)
+{
+       return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAGN;
+}
 
-       if (da.addr_type == IEEE802154_ADDR_SHORT)
-               dap = &da.short_addr;
-       else
-               dap = &da.hwaddr;
+static lowpan_rx_result lowpan_rx_h_frag(struct sk_buff *skb)
+{
+       int ret;
 
-       return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type,
-                                       IEEE802154_ADDR_LEN, dap, da.addr_type,
-                                       IEEE802154_ADDR_LEN, iphc0, iphc1);
+       if (!(lowpan_is_frag1(*skb_network_header(skb)) ||
+             lowpan_is_fragn(*skb_network_header(skb))))
+               return RX_CONTINUE;
+
+       ret = lowpan_frag_rcv(skb, *skb_network_header(skb) &
+                             LOWPAN_DISPATCH_FRAG_MASK);
+       if (ret == 1)
+               return RX_QUEUED;
+
+       /* Packet is freed by lowpan_frag_rcv on error or put into the frag
+        * bucket.
+        */
+       return RX_DROP;
 }
 
-static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
-                     struct packet_type *pt, struct net_device *orig_dev)
+int lowpan_iphc_decompress(struct sk_buff *skb)
 {
        struct ieee802154_hdr hdr;
+
+       if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
+               return -EINVAL;
+
+       return lowpan_header_decompress(skb, skb->dev, &hdr.dest, &hdr.source);
+}
+
+static lowpan_rx_result lowpan_rx_h_iphc(struct sk_buff *skb)
+{
        int ret;
 
-       if (dev->type != ARPHRD_IEEE802154 ||
-           !dev->ieee802154_ptr->lowpan_dev)
-               goto drop;
+       if (!lowpan_is_iphc(*skb_network_header(skb)))
+               return RX_CONTINUE;
 
-       skb = skb_share_check(skb, GFP_ATOMIC);
-       if (!skb)
+       /* Setting datagram_offset to zero indicates non frag handling
+        * while doing lowpan_header_decompress.
+        */
+       lowpan_802154_cb(skb)->d_size = 0;
+
+       ret = lowpan_iphc_decompress(skb);
+       if (ret < 0)
+               return RX_DROP_UNUSABLE;
+
+       return RX_QUEUED;
+}
+
+lowpan_rx_result lowpan_rx_h_ipv6(struct sk_buff *skb)
+{
+       if (!lowpan_is_ipv6(*skb_network_header(skb)))
+               return RX_CONTINUE;
+
+       /* Pull off the 1-byte of 6lowpan header. */
+       skb_pull(skb, 1);
+       return RX_QUEUED;
+}
+
+static inline bool lowpan_is_esc(u8 dispatch)
+{
+       return dispatch == LOWPAN_DISPATCH_ESC;
+}
+
+static lowpan_rx_result lowpan_rx_h_esc(struct sk_buff *skb)
+{
+       if (!lowpan_is_esc(*skb_network_header(skb)))
+               return RX_CONTINUE;
+
+       net_warn_ratelimited("%s: %s\n", skb->dev->name,
+                            "6LoWPAN ESC not supported\n");
+
+       return RX_DROP_UNUSABLE;
+}
+
+static inline bool lowpan_is_hc1(u8 dispatch)
+{
+       return dispatch == LOWPAN_DISPATCH_HC1;
+}
+
+static lowpan_rx_result lowpan_rx_h_hc1(struct sk_buff *skb)
+{
+       if (!lowpan_is_hc1(*skb_network_header(skb)))
+               return RX_CONTINUE;
+
+       net_warn_ratelimited("%s: %s\n", skb->dev->name,
+                            "6LoWPAN HC1 not supported\n");
+
+       return RX_DROP_UNUSABLE;
+}
+
+static inline bool lowpan_is_dff(u8 dispatch)
+{
+       return dispatch == LOWPAN_DISPATCH_DFF;
+}
+
+static lowpan_rx_result lowpan_rx_h_dff(struct sk_buff *skb)
+{
+       if (!lowpan_is_dff(*skb_network_header(skb)))
+               return RX_CONTINUE;
+
+       net_warn_ratelimited("%s: %s\n", skb->dev->name,
+                            "6LoWPAN DFF not supported\n");
+
+       return RX_DROP_UNUSABLE;
+}
+
+static inline bool lowpan_is_bc0(u8 dispatch)
+{
+       return dispatch == LOWPAN_DISPATCH_BC0;
+}
+
+static lowpan_rx_result lowpan_rx_h_bc0(struct sk_buff *skb)
+{
+       if (!lowpan_is_bc0(*skb_network_header(skb)))
+               return RX_CONTINUE;
+
+       net_warn_ratelimited("%s: %s\n", skb->dev->name,
+                            "6LoWPAN BC0 not supported\n");
+
+       return RX_DROP_UNUSABLE;
+}
+
+static inline bool lowpan_is_mesh(u8 dispatch)
+{
+       return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_MESH;
+}
+
+static lowpan_rx_result lowpan_rx_h_mesh(struct sk_buff *skb)
+{
+       if (!lowpan_is_mesh(*skb_network_header(skb)))
+               return RX_CONTINUE;
+
+       net_warn_ratelimited("%s: %s\n", skb->dev->name,
+                            "6LoWPAN MESH not supported\n");
+
+       return RX_DROP_UNUSABLE;
+}
+
+static int lowpan_invoke_rx_handlers(struct sk_buff *skb)
+{
+       lowpan_rx_result res;
+
+#define CALL_RXH(rxh)                  \
+       do {                            \
+               res = rxh(skb); \
+               if (res != RX_CONTINUE) \
+                       goto rxh_next;  \
+       } while (0)
+
+       /* likely at first */
+       CALL_RXH(lowpan_rx_h_iphc);
+       CALL_RXH(lowpan_rx_h_frag);
+       CALL_RXH(lowpan_rx_h_ipv6);
+       CALL_RXH(lowpan_rx_h_esc);
+       CALL_RXH(lowpan_rx_h_hc1);
+       CALL_RXH(lowpan_rx_h_dff);
+       CALL_RXH(lowpan_rx_h_bc0);
+       CALL_RXH(lowpan_rx_h_mesh);
+
+rxh_next:
+       return lowpan_rx_handlers_result(skb, res);
+#undef CALL_RXH
+}
+
+static inline bool lowpan_is_nalp(u8 dispatch)
+{
+       return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_NALP;
+}
+
+/* Lookup for reserved dispatch values at:
+ * https://www.iana.org/assignments/_6lowpan-parameters/_6lowpan-parameters.xhtml#_6lowpan-parameters-1
+ *
+ * Last Updated: 2015-01-22
+ */
+static inline bool lowpan_is_reserved(u8 dispatch)
+{
+       return ((dispatch >= 0x44 && dispatch <= 0x4F) ||
+               (dispatch >= 0x51 && dispatch <= 0x5F) ||
+               (dispatch >= 0xc8 && dispatch <= 0xdf) ||
+               (dispatch >= 0xe8 && dispatch <= 0xff));
+}
+
+/* lowpan_rx_h_check checks on generic 6LoWPAN requirements
+ * in MAC and 6LoWPAN header.
+ *
+ * Don't manipulate the skb here, it could be shared buffer.
+ */
+static inline bool lowpan_rx_h_check(struct sk_buff *skb)
+{
+       __le16 fc = ieee802154_get_fc_from_skb(skb);
+
+       /* check on ieee802154 conform 6LoWPAN header */
+       if (!ieee802154_is_data(fc) ||
+           !ieee802154_is_intra_pan(fc))
+               return false;
+
+       /* check if we can dereference the dispatch */
+       if (unlikely(!skb->len))
+               return false;
+
+       if (lowpan_is_nalp(*skb_network_header(skb)) ||
+           lowpan_is_reserved(*skb_network_header(skb)))
+               return false;
+
+       return true;
+}
+
+static int lowpan_rcv(struct sk_buff *skb, struct net_device *wdev,
+                     struct packet_type *pt, struct net_device *orig_wdev)
+{
+       struct net_device *ldev;
+
+       if (wdev->type != ARPHRD_IEEE802154 ||
+           skb->pkt_type == PACKET_OTHERHOST ||
+           !lowpan_rx_h_check(skb))
                goto drop;
 
-       if (!netif_running(dev))
-               goto drop_skb;
+       ldev = wdev->ieee802154_ptr->lowpan_dev;
+       if (!ldev || !netif_running(ldev))
+               goto drop;
 
-       if (skb->pkt_type == PACKET_OTHERHOST)
-               goto drop_skb;
+       /* Replacing skb->dev and followed rx handlers will manipulate skb. */
+       skb = skb_share_check(skb, GFP_ATOMIC);
+       if (!skb)
+               goto out;
+       skb->dev = ldev;
 
-       if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
-               goto drop_skb;
-
-       /* check that it's our buffer */
-       if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
-               /* Pull off the 1-byte of 6lowpan header. */
-               skb_pull(skb, 1);
-               return lowpan_give_skb_to_device(skb, dev);
-       } else {
-               switch (skb->data[0] & 0xe0) {
-               case LOWPAN_DISPATCH_IPHC:      /* ipv6 datagram */
-                       ret = iphc_decompress(skb, &hdr);
-                       if (ret < 0)
-                               goto drop_skb;
-
-                       return lowpan_give_skb_to_device(skb, dev);
-               case LOWPAN_DISPATCH_FRAG1:     /* first fragment header */
-                       ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1);
-                       if (ret == 1) {
-                               ret = iphc_decompress(skb, &hdr);
-                               if (ret < 0)
-                                       goto drop_skb;
-
-                               return lowpan_give_skb_to_device(skb, dev);
-                       } else if (ret == -1) {
-                               return NET_RX_DROP;
-                       } else {
-                               return NET_RX_SUCCESS;
-                       }
-               case LOWPAN_DISPATCH_FRAGN:     /* next fragments headers */
-                       ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN);
-                       if (ret == 1) {
-                               ret = iphc_decompress(skb, &hdr);
-                               if (ret < 0)
-                                       goto drop_skb;
-
-                               return lowpan_give_skb_to_device(skb, dev);
-                       } else if (ret == -1) {
-                               return NET_RX_DROP;
-                       } else {
-                               return NET_RX_SUCCESS;
-                       }
-               default:
-                       break;
-               }
+       /* When receive frag1 it's likely that we manipulate the buffer.
+        * When recevie iphc we manipulate the data buffer. So we need
+        * to unshare the buffer.
+        */
+       if (lowpan_is_frag1(*skb_network_header(skb)) ||
+           lowpan_is_iphc(*skb_network_header(skb))) {
+               skb = skb_unshare(skb, GFP_ATOMIC);
+               if (!skb)
+                       goto out;
        }
 
-drop_skb:
-       kfree_skb(skb);
+       return lowpan_invoke_rx_handlers(skb);
+
 drop:
+       kfree_skb(skb);
+out:
        return NET_RX_DROP;
 }
 
index f6263fc1234056e89cd2c06046699c4fa1d75b39..d4353faced35cf64f98a68fe6a749120cff411d2 100644 (file)
 
 #include <net/6lowpan.h>
 #include <net/ieee802154_netdev.h>
+#include <net/mac802154.h>
 
 #include "6lowpan_i.h"
 
+#define LOWPAN_FRAG1_HEAD_SIZE 0x4
+#define LOWPAN_FRAGN_HEAD_SIZE 0x5
+
 /* don't save pan id, it's intra pan */
 struct lowpan_addr {
        u8 mode;
@@ -36,7 +40,14 @@ lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
                        sizeof(struct lowpan_addr_info));
 }
 
-int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
+/* This callback will be called from AF_PACKET and IPv6 stack, the AF_PACKET
+ * sockets gives an 8 byte array for addresses only!
+ *
+ * TODO I think AF_PACKET DGRAM (sending/receiving) RAW (sending) makes no
+ * sense here. We should disable it, the right use-case would be AF_INET6
+ * RAW/DGRAM sockets.
+ */
+int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
                         unsigned short type, const void *_daddr,
                         const void *_saddr, unsigned int len)
 {
@@ -51,7 +62,7 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
                return 0;
 
        if (!saddr)
-               saddr = dev->dev_addr;
+               saddr = ldev->dev_addr;
 
        raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8);
        raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8);
@@ -71,28 +82,33 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
 
 static struct sk_buff*
 lowpan_alloc_frag(struct sk_buff *skb, int size,
-                 const struct ieee802154_hdr *master_hdr)
+                 const struct ieee802154_hdr *master_hdr, bool frag1)
 {
-       struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev;
+       struct net_device *wdev = lowpan_dev_info(skb->dev)->wdev;
        struct sk_buff *frag;
        int rc;
 
-       frag = alloc_skb(real_dev->hard_header_len +
-                        real_dev->needed_tailroom + size,
+       frag = alloc_skb(wdev->needed_headroom + wdev->needed_tailroom + size,
                         GFP_ATOMIC);
 
        if (likely(frag)) {
-               frag->dev = real_dev;
+               frag->dev = wdev;
                frag->priority = skb->priority;
-               skb_reserve(frag, real_dev->hard_header_len);
+               skb_reserve(frag, wdev->needed_headroom);
                skb_reset_network_header(frag);
                *mac_cb(frag) = *mac_cb(skb);
 
-               rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest,
-                                    &master_hdr->source, size);
-               if (rc < 0) {
-                       kfree_skb(frag);
-                       return ERR_PTR(rc);
+               if (frag1) {
+                       memcpy(skb_put(frag, skb->mac_len),
+                              skb_mac_header(skb), skb->mac_len);
+               } else {
+                       rc = wpan_dev_hard_header(frag, wdev,
+                                                 &master_hdr->dest,
+                                                 &master_hdr->source, size);
+                       if (rc < 0) {
+                               kfree_skb(frag);
+                               return ERR_PTR(rc);
+                       }
                }
        } else {
                frag = ERR_PTR(-ENOMEM);
@@ -104,13 +120,13 @@ lowpan_alloc_frag(struct sk_buff *skb, int size,
 static int
 lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr,
                     u8 *frag_hdr, int frag_hdrlen,
-                    int offset, int len)
+                    int offset, int len, bool frag1)
 {
        struct sk_buff *frag;
 
        raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen);
 
-       frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr);
+       frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr, frag1);
        if (IS_ERR(frag))
                return PTR_ERR(frag);
 
@@ -123,19 +139,17 @@ lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr,
 }
 
 static int
-lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
-                      const struct ieee802154_hdr *wpan_hdr)
+lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev,
+                      const struct ieee802154_hdr *wpan_hdr, u16 dgram_size,
+                      u16 dgram_offset)
 {
-       u16 dgram_size, dgram_offset;
        __be16 frag_tag;
        u8 frag_hdr[5];
        int frag_cap, frag_len, payload_cap, rc;
        int skb_unprocessed, skb_offset;
 
-       dgram_size = lowpan_uncompress_size(skb, &dgram_offset) -
-                    skb->mac_len;
-       frag_tag = htons(lowpan_dev_info(dev)->fragment_tag);
-       lowpan_dev_info(dev)->fragment_tag++;
+       frag_tag = htons(lowpan_dev_info(ldev)->fragment_tag);
+       lowpan_dev_info(ldev)->fragment_tag++;
 
        frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07);
        frag_hdr[1] = dgram_size & 0xff;
@@ -151,7 +165,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
 
        rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
                                  LOWPAN_FRAG1_HEAD_SIZE, 0,
-                                 frag_len + skb_network_header_len(skb));
+                                 frag_len + skb_network_header_len(skb),
+                                 true);
        if (rc) {
                pr_debug("%s unable to send FRAG1 packet (tag: %d)",
                         __func__, ntohs(frag_tag));
@@ -172,7 +187,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
 
                rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
                                          LOWPAN_FRAGN_HEAD_SIZE, skb_offset,
-                                         frag_len);
+                                         frag_len, false);
                if (rc) {
                        pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
                                 __func__, ntohs(frag_tag), skb_offset);
@@ -180,6 +195,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
                }
        } while (skb_unprocessed > frag_cap);
 
+       ldev->stats.tx_packets++;
+       ldev->stats.tx_bytes += dgram_size;
        consume_skb(skb);
        return NET_XMIT_SUCCESS;
 
@@ -188,9 +205,10 @@ err:
        return rc;
 }
 
-static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
+static int lowpan_header(struct sk_buff *skb, struct net_device *ldev,
+                        u16 *dgram_size, u16 *dgram_offset)
 {
-       struct wpan_dev *wpan_dev = lowpan_dev_info(dev)->real_dev->ieee802154_ptr;
+       struct wpan_dev *wpan_dev = lowpan_dev_info(ldev)->wdev->ieee802154_ptr;
        struct ieee802154_addr sa, da;
        struct ieee802154_mac_cb *cb = mac_cb_init(skb);
        struct lowpan_addr_info info;
@@ -202,7 +220,10 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
        daddr = &info.daddr.u.extended_addr;
        saddr = &info.saddr.u.extended_addr;
 
-       lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len);
+       *dgram_size = skb->len;
+       lowpan_header_compress(skb, ldev, daddr, saddr);
+       /* dgram_offset = (saved bytes after compression) + lowpan header len */
+       *dgram_offset = (*dgram_size - skb->len) + skb_network_header_len(skb);
 
        cb->type = IEEE802154_FC_TYPE_DATA;
 
@@ -217,7 +238,7 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
        /* if the destination address is the broadcast address, use the
         * corresponding short address
         */
-       if (lowpan_is_addr_broadcast((const u8 *)daddr)) {
+       if (!memcmp(daddr, ldev->broadcast, EUI64_ADDR_LEN)) {
                da.mode = IEEE802154_ADDR_SHORT;
                da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
                cb->ackreq = false;
@@ -227,17 +248,20 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
                cb->ackreq = wpan_dev->ackreq;
        }
 
-       return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
-                       ETH_P_IPV6, (void *)&da, (void *)&sa, 0);
+       return wpan_dev_hard_header(skb, lowpan_dev_info(ldev)->wdev, &da, &sa,
+                                   0);
 }
 
-netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
+netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
 {
        struct ieee802154_hdr wpan_hdr;
        int max_single, ret;
+       u16 dgram_size, dgram_offset;
 
        pr_debug("package xmit\n");
 
+       WARN_ON_ONCE(skb->len > IPV6_MIN_MTU);
+
        /* We must take a copy of the skb before we modify/replace the ipv6
         * header as the header could be used elsewhere
         */
@@ -245,7 +269,7 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
        if (!skb)
                return NET_XMIT_DROP;
 
-       ret = lowpan_header(skb, dev);
+       ret = lowpan_header(skb, ldev, &dgram_size, &dgram_offset);
        if (ret < 0) {
                kfree_skb(skb);
                return NET_XMIT_DROP;
@@ -259,13 +283,16 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
        max_single = ieee802154_max_payload(&wpan_hdr);
 
        if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) {
-               skb->dev = lowpan_dev_info(dev)->real_dev;
+               skb->dev = lowpan_dev_info(ldev)->wdev;
+               ldev->stats.tx_packets++;
+               ldev->stats.tx_bytes += dgram_size;
                return dev_queue_xmit(skb);
        } else {
                netdev_tx_t rc;
 
                pr_debug("frame is too big, fragmentation is needed\n");
-               rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr);
+               rc = lowpan_xmit_fragmented(skb, ldev, &wpan_hdr, dgram_size,
+                                           dgram_offset);
 
                return rc < 0 ? NET_XMIT_DROP : rc;
        }
index 1370d5b0041b952cf74c272c2494f8c0150c893b..188135bcb803531b4ef24f68113a729122336390 100644 (file)
@@ -12,6 +12,11 @@ menuconfig IEEE802154
 
 if IEEE802154
 
+config IEEE802154_NL802154_EXPERIMENTAL
+       bool "IEEE 802.15.4 experimental netlink support"
+       ---help---
+         Adds experimental netlink support for nl802154.
+
 config IEEE802154_SOCKET
        tristate "IEEE 802.15.4 socket interface"
        default y
index b0248e934230d166c4e61a66a0ad18c268fa0a71..c35fdfa6d04efbfb8a1c6f524e5afcdc58d83130 100644 (file)
@@ -95,6 +95,18 @@ cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
        return result;
 }
 
+struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx)
+{
+       struct cfg802154_registered_device *rdev;
+
+       ASSERT_RTNL();
+
+       rdev = cfg802154_rdev_by_wpan_phy_idx(wpan_phy_idx);
+       if (!rdev)
+               return NULL;
+       return &rdev->wpan_phy;
+}
+
 struct wpan_phy *
 wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
 {
index f3e95580caee0e96ada8bee0bb2a2e72aa21eda3..231fade959f39c1f2e1ce6c174b98306a08faa35 100644 (file)
@@ -42,5 +42,6 @@ extern int cfg802154_rdev_list_generation;
 void cfg802154_dev_free(struct cfg802154_registered_device *rdev);
 struct cfg802154_registered_device *
 cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx);
+struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx);
 
 #endif /* __IEEE802154_CORE_H */
index a051b69931779438b33f61456203c451167fa591..c7439f0fbbdf817b7baadc08cd20d825a57eb298 100644 (file)
@@ -83,35 +83,35 @@ ieee802154_hdr_push_sechdr(u8 *buf, const struct ieee802154_sechdr *hdr)
 }
 
 int
-ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
+ieee802154_hdr_push(struct sk_buff *skb, struct ieee802154_hdr *hdr)
 {
-       u8 buf[MAC802154_FRAME_HARD_HEADER_LEN];
+       u8 buf[IEEE802154_MAX_HEADER_LEN];
        int pos = 2;
        int rc;
-       struct ieee802154_hdr_fc fc = hdr->fc;
+       struct ieee802154_hdr_fc *fc = &hdr->fc;
 
        buf[pos++] = hdr->seq;
 
-       fc.dest_addr_mode = hdr->dest.mode;
+       fc->dest_addr_mode = hdr->dest.mode;
 
        rc = ieee802154_hdr_push_addr(buf + pos, &hdr->dest, false);
        if (rc < 0)
                return -EINVAL;
        pos += rc;
 
-       fc.source_addr_mode = hdr->source.mode;
+       fc->source_addr_mode = hdr->source.mode;
 
        if (hdr->source.pan_id == hdr->dest.pan_id &&
            hdr->dest.mode != IEEE802154_ADDR_NONE)
-               fc.intra_pan = true;
+               fc->intra_pan = true;
 
-       rc = ieee802154_hdr_push_addr(buf + pos, &hdr->source, fc.intra_pan);
+       rc = ieee802154_hdr_push_addr(buf + pos, &hdr->source, fc->intra_pan);
        if (rc < 0)
                return -EINVAL;
        pos += rc;
 
-       if (fc.security_enabled) {
-               fc.version = 1;
+       if (fc->security_enabled) {
+               fc->version = 1;
 
                rc = ieee802154_hdr_push_sechdr(buf + pos, &hdr->sec);
                if (rc < 0)
@@ -120,7 +120,7 @@ ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
                pos += rc;
        }
 
-       memcpy(buf, &fc, 2);
+       memcpy(buf, fc, 2);
 
        memcpy(skb_push(skb, pos), buf, pos);
 
index 3f89c0abdab170485a4bc83867c0f9609fe69c64..16ef0d9f566e8436e55f0263c51585ae5ab751b9 100644 (file)
@@ -232,8 +232,86 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
        [NL802154_ATTR_SUPPORTED_COMMANDS] = { .type = NLA_NESTED },
 
        [NL802154_ATTR_ACKREQ_DEFAULT] = { .type = NLA_U8 },
+
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+       [NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, },
+       [NL802154_ATTR_SEC_OUT_LEVEL] = { .type = NLA_U32, },
+       [NL802154_ATTR_SEC_OUT_KEY_ID] = { .type = NLA_NESTED, },
+       [NL802154_ATTR_SEC_FRAME_COUNTER] = { .type = NLA_U32 },
+
+       [NL802154_ATTR_SEC_LEVEL] = { .type = NLA_NESTED },
+       [NL802154_ATTR_SEC_DEVICE] = { .type = NLA_NESTED },
+       [NL802154_ATTR_SEC_DEVKEY] = { .type = NLA_NESTED },
+       [NL802154_ATTR_SEC_KEY] = { .type = NLA_NESTED },
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
 };
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+static int
+nl802154_prepare_wpan_dev_dump(struct sk_buff *skb,
+                              struct netlink_callback *cb,
+                              struct cfg802154_registered_device **rdev,
+                              struct wpan_dev **wpan_dev)
+{
+       int err;
+
+       rtnl_lock();
+
+       if (!cb->args[0]) {
+               err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
+                                 nl802154_fam.attrbuf, nl802154_fam.maxattr,
+                                 nl802154_policy);
+               if (err)
+                       goto out_unlock;
+
+               *wpan_dev = __cfg802154_wpan_dev_from_attrs(sock_net(skb->sk),
+                                                           nl802154_fam.attrbuf);
+               if (IS_ERR(*wpan_dev)) {
+                       err = PTR_ERR(*wpan_dev);
+                       goto out_unlock;
+               }
+               *rdev = wpan_phy_to_rdev((*wpan_dev)->wpan_phy);
+               /* 0 is the first index - add 1 to parse only once */
+               cb->args[0] = (*rdev)->wpan_phy_idx + 1;
+               cb->args[1] = (*wpan_dev)->identifier;
+       } else {
+               /* subtract the 1 again here */
+               struct wpan_phy *wpan_phy = wpan_phy_idx_to_wpan_phy(cb->args[0] - 1);
+               struct wpan_dev *tmp;
+
+               if (!wpan_phy) {
+                       err = -ENODEV;
+                       goto out_unlock;
+               }
+               *rdev = wpan_phy_to_rdev(wpan_phy);
+               *wpan_dev = NULL;
+
+               list_for_each_entry(tmp, &(*rdev)->wpan_dev_list, list) {
+                       if (tmp->identifier == cb->args[1]) {
+                               *wpan_dev = tmp;
+                               break;
+                       }
+               }
+
+               if (!*wpan_dev) {
+                       err = -ENODEV;
+                       goto out_unlock;
+               }
+       }
+
+       return 0;
+ out_unlock:
+       rtnl_unlock();
+       return err;
+}
+
+static void
+nl802154_finish_wpan_dev_dump(struct cfg802154_registered_device *rdev)
+{
+       rtnl_unlock();
+}
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 /* message building helper */
 static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
                                    int flags, u8 cmd)
@@ -612,6 +690,107 @@ static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
               ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
 }
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+#include <net/ieee802154_netdev.h>
+
+static int
+ieee802154_llsec_send_key_id(struct sk_buff *msg,
+                            const struct ieee802154_llsec_key_id *desc)
+{
+       struct nlattr *nl_dev_addr;
+
+       if (nla_put_u32(msg, NL802154_KEY_ID_ATTR_MODE, desc->mode))
+               return -ENOBUFS;
+
+       switch (desc->mode) {
+       case NL802154_KEY_ID_MODE_IMPLICIT:
+               nl_dev_addr = nla_nest_start(msg, NL802154_KEY_ID_ATTR_IMPLICIT);
+               if (!nl_dev_addr)
+                       return -ENOBUFS;
+
+               if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_PAN_ID,
+                                desc->device_addr.pan_id) ||
+                   nla_put_u32(msg,  NL802154_DEV_ADDR_ATTR_MODE,
+                               desc->device_addr.mode))
+                       return -ENOBUFS;
+
+               switch (desc->device_addr.mode) {
+               case NL802154_DEV_ADDR_SHORT:
+                       if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_SHORT,
+                                        desc->device_addr.short_addr))
+                               return -ENOBUFS;
+                       break;
+               case NL802154_DEV_ADDR_EXTENDED:
+                       if (nla_put_le64(msg, NL802154_DEV_ADDR_ATTR_EXTENDED,
+                                        desc->device_addr.extended_addr))
+                               return -ENOBUFS;
+                       break;
+               default:
+                       /* userspace should handle unknown */
+                       break;
+               }
+
+               nla_nest_end(msg, nl_dev_addr);
+               break;
+       case NL802154_KEY_ID_MODE_INDEX:
+               break;
+       case NL802154_KEY_ID_MODE_INDEX_SHORT:
+               /* TODO renmae short_source? */
+               if (nla_put_le32(msg, NL802154_KEY_ID_ATTR_SOURCE_SHORT,
+                                desc->short_source))
+                       return -ENOBUFS;
+               break;
+       case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
+               if (nla_put_le64(msg, NL802154_KEY_ID_ATTR_SOURCE_EXTENDED,
+                                desc->extended_source))
+                       return -ENOBUFS;
+               break;
+       default:
+               /* userspace should handle unknown */
+               break;
+       }
+
+       /* TODO key_id to key_idx ? Check naming */
+       if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) {
+               if (nla_put_u8(msg, NL802154_KEY_ID_ATTR_INDEX, desc->id))
+                       return -ENOBUFS;
+       }
+
+       return 0;
+}
+
+static int nl802154_get_llsec_params(struct sk_buff *msg,
+                                    struct cfg802154_registered_device *rdev,
+                                    struct wpan_dev *wpan_dev)
+{
+       struct nlattr *nl_key_id;
+       struct ieee802154_llsec_params params;
+       int ret;
+
+       ret = rdev_get_llsec_params(rdev, wpan_dev, &params);
+       if (ret < 0)
+               return ret;
+
+       if (nla_put_u8(msg, NL802154_ATTR_SEC_ENABLED, params.enabled) ||
+           nla_put_u32(msg, NL802154_ATTR_SEC_OUT_LEVEL, params.out_level) ||
+           nla_put_be32(msg, NL802154_ATTR_SEC_FRAME_COUNTER,
+                        params.frame_counter))
+               return -ENOBUFS;
+
+       nl_key_id = nla_nest_start(msg, NL802154_ATTR_SEC_OUT_KEY_ID);
+       if (!nl_key_id)
+               return -ENOBUFS;
+
+       ret = ieee802154_llsec_send_key_id(msg, &params.out_key);
+       if (ret < 0)
+               return ret;
+
+       nla_nest_end(msg, nl_key_id);
+
+       return 0;
+}
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 static int
 nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
                    struct cfg802154_registered_device *rdev,
@@ -663,6 +842,11 @@ nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
        if (nla_put_u8(msg, NL802154_ATTR_ACKREQ_DEFAULT, wpan_dev->ackreq))
                goto nla_put_failure;
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+       if (nl802154_get_llsec_params(msg, rdev, wpan_dev) < 0)
+               goto nla_put_failure;
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
        genlmsg_end(msg, hdr);
        return 0;
 
@@ -753,10 +937,8 @@ static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
-       /* TODO add nla_get_le64 to netlink */
        if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
-               extended_addr = (__force __le64)nla_get_u64(
-                               info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
+               extended_addr = nla_get_le64(info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
 
        if (!rdev->ops->add_virtual_intf)
                return -EOPNOTSUPP;
@@ -1075,122 +1257,954 @@ nl802154_set_ackreq_default(struct sk_buff *skb, struct genl_info *info)
        return rdev_set_ackreq_default(rdev, wpan_dev, ackreq);
 }
 
-#define NL802154_FLAG_NEED_WPAN_PHY    0x01
-#define NL802154_FLAG_NEED_NETDEV      0x02
-#define NL802154_FLAG_NEED_RTNL                0x04
-#define NL802154_FLAG_CHECK_NETDEV_UP  0x08
-#define NL802154_FLAG_NEED_NETDEV_UP   (NL802154_FLAG_NEED_NETDEV |\
-                                        NL802154_FLAG_CHECK_NETDEV_UP)
-#define NL802154_FLAG_NEED_WPAN_DEV    0x10
-#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
-                                        NL802154_FLAG_CHECK_NETDEV_UP)
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+static const struct nla_policy nl802154_dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = {
+       [NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 },
+       [NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U32 },
+       [NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16 },
+       [NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64 },
+};
 
-static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
-                            struct genl_info *info)
+static int
+ieee802154_llsec_parse_dev_addr(struct nlattr *nla,
+                               struct ieee802154_addr *addr)
 {
-       struct cfg802154_registered_device *rdev;
-       struct wpan_dev *wpan_dev;
-       struct net_device *dev;
-       bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
+       struct nlattr *attrs[NL802154_DEV_ADDR_ATTR_MAX + 1];
 
-       if (rtnl)
-               rtnl_lock();
+       if (!nla || nla_parse_nested(attrs, NL802154_DEV_ADDR_ATTR_MAX, nla,
+                                    nl802154_dev_addr_policy))
+               return -EINVAL;
 
-       if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
-               rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
-               if (IS_ERR(rdev)) {
-                       if (rtnl)
-                               rtnl_unlock();
-                       return PTR_ERR(rdev);
-               }
-               info->user_ptr[0] = rdev;
-       } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
-                  ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
-               ASSERT_RTNL();
-               wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
-                                                          info->attrs);
-               if (IS_ERR(wpan_dev)) {
-                       if (rtnl)
-                               rtnl_unlock();
-                       return PTR_ERR(wpan_dev);
-               }
+       if (!attrs[NL802154_DEV_ADDR_ATTR_PAN_ID] &&
+           !attrs[NL802154_DEV_ADDR_ATTR_MODE] &&
+           !(attrs[NL802154_DEV_ADDR_ATTR_SHORT] ||
+             attrs[NL802154_DEV_ADDR_ATTR_EXTENDED]))
+               return -EINVAL;
 
-               dev = wpan_dev->netdev;
-               rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+       addr->pan_id = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_PAN_ID]);
+       addr->mode = nla_get_u32(attrs[NL802154_DEV_ADDR_ATTR_MODE]);
+       switch (addr->mode) {
+       case NL802154_DEV_ADDR_SHORT:
+               addr->short_addr = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_SHORT]);
+               break;
+       case NL802154_DEV_ADDR_EXTENDED:
+               addr->extended_addr = nla_get_le64(attrs[NL802154_DEV_ADDR_ATTR_EXTENDED]);
+               break;
+       default:
+               return -EINVAL;
+       }
 
-               if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
-                       if (!dev) {
-                               if (rtnl)
-                                       rtnl_unlock();
-                               return -EINVAL;
-                       }
+       return 0;
+}
 
-                       info->user_ptr[1] = dev;
-               } else {
-                       info->user_ptr[1] = wpan_dev;
-               }
+static const struct nla_policy nl802154_key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = {
+       [NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 },
+       [NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 },
+       [NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED },
+       [NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 },
+       [NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 },
+};
 
-               if (dev) {
-                       if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
-                           !netif_running(dev)) {
-                               if (rtnl)
-                                       rtnl_unlock();
-                               return -ENETDOWN;
-                       }
+static int
+ieee802154_llsec_parse_key_id(struct nlattr *nla,
+                             struct ieee802154_llsec_key_id *desc)
+{
+       struct nlattr *attrs[NL802154_KEY_ID_ATTR_MAX + 1];
 
-                       dev_hold(dev);
-               }
+       if (!nla || nla_parse_nested(attrs, NL802154_KEY_ID_ATTR_MAX, nla,
+                                    nl802154_key_id_policy))
+               return -EINVAL;
 
-               info->user_ptr[0] = rdev;
+       if (!attrs[NL802154_KEY_ID_ATTR_MODE])
+               return -EINVAL;
+
+       desc->mode = nla_get_u32(attrs[NL802154_KEY_ID_ATTR_MODE]);
+       switch (desc->mode) {
+       case NL802154_KEY_ID_MODE_IMPLICIT:
+               if (!attrs[NL802154_KEY_ID_ATTR_IMPLICIT])
+                       return -EINVAL;
+
+               if (ieee802154_llsec_parse_dev_addr(attrs[NL802154_KEY_ID_ATTR_IMPLICIT],
+                                                   &desc->device_addr) < 0)
+                       return -EINVAL;
+               break;
+       case NL802154_KEY_ID_MODE_INDEX:
+               break;
+       case NL802154_KEY_ID_MODE_INDEX_SHORT:
+               if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT])
+                       return -EINVAL;
+
+               desc->short_source = nla_get_le32(attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT]);
+               break;
+       case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
+               if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])
+                       return -EINVAL;
+
+               desc->extended_source = nla_get_le64(attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED]);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) {
+               if (!attrs[NL802154_KEY_ID_ATTR_INDEX])
+                       return -EINVAL;
+
+               /* TODO change id to idx */
+               desc->id = nla_get_u8(attrs[NL802154_KEY_ID_ATTR_INDEX]);
        }
 
        return 0;
 }
 
-static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
-                              struct genl_info *info)
+static int nl802154_set_llsec_params(struct sk_buff *skb,
+                                    struct genl_info *info)
 {
-       if (info->user_ptr[1]) {
-               if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
-                       struct wpan_dev *wpan_dev = info->user_ptr[1];
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       struct ieee802154_llsec_params params;
+       u32 changed = 0;
+       int ret;
 
-                       if (wpan_dev->netdev)
-                               dev_put(wpan_dev->netdev);
-               } else {
-                       dev_put(info->user_ptr[1]);
+       if (info->attrs[NL802154_ATTR_SEC_ENABLED]) {
+               u8 enabled;
+
+               enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]);
+               if (enabled != 0 && enabled != 1)
+                       return -EINVAL;
+
+               params.enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]);
+               changed |= IEEE802154_LLSEC_PARAM_ENABLED;
+       }
+
+       if (info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID]) {
+               ret = ieee802154_llsec_parse_key_id(info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID],
+                                                   &params.out_key);
+               if (ret < 0)
+                       return ret;
+
+               changed |= IEEE802154_LLSEC_PARAM_OUT_KEY;
+       }
+
+       if (info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]) {
+               params.out_level = nla_get_u32(info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]);
+               if (params.out_level > NL802154_SECLEVEL_MAX)
+                       return -EINVAL;
+
+               changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL;
+       }
+
+       if (info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]) {
+               params.frame_counter = nla_get_be32(info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]);
+               changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER;
+       }
+
+       return rdev_set_llsec_params(rdev, wpan_dev, &params, changed);
+}
+
+static int nl802154_send_key(struct sk_buff *msg, u32 cmd, u32 portid,
+                            u32 seq, int flags,
+                            struct cfg802154_registered_device *rdev,
+                            struct net_device *dev,
+                            const struct ieee802154_llsec_key_entry *key)
+{
+       void *hdr;
+       u32 commands[NL802154_CMD_FRAME_NR_IDS / 32];
+       struct nlattr *nl_key, *nl_key_id;
+
+       hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+       if (!hdr)
+               return -1;
+
+       if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
+               goto nla_put_failure;
+
+       nl_key = nla_nest_start(msg, NL802154_ATTR_SEC_KEY);
+       if (!nl_key)
+               goto nla_put_failure;
+
+       nl_key_id = nla_nest_start(msg, NL802154_KEY_ATTR_ID);
+       if (!nl_key_id)
+               goto nla_put_failure;
+
+       if (ieee802154_llsec_send_key_id(msg, &key->id) < 0)
+               goto nla_put_failure;
+
+       nla_nest_end(msg, nl_key_id);
+
+       if (nla_put_u8(msg, NL802154_KEY_ATTR_USAGE_FRAMES,
+                      key->key->frame_types))
+               goto nla_put_failure;
+
+       if (key->key->frame_types & BIT(NL802154_FRAME_CMD)) {
+               /* TODO for each nested */
+               memset(commands, 0, sizeof(commands));
+               commands[7] = key->key->cmd_frame_ids;
+               if (nla_put(msg, NL802154_KEY_ATTR_USAGE_CMDS,
+                           sizeof(commands), commands))
+                       goto nla_put_failure;
+       }
+
+       if (nla_put(msg, NL802154_KEY_ATTR_BYTES, NL802154_KEY_SIZE,
+                   key->key->key))
+               goto nla_put_failure;
+
+       nla_nest_end(msg, nl_key);
+       genlmsg_end(msg, hdr);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_llsec_key(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct cfg802154_registered_device *rdev = NULL;
+       struct ieee802154_llsec_key_entry *key;
+       struct ieee802154_llsec_table *table;
+       struct wpan_dev *wpan_dev;
+       int err;
+
+       err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
+       if (err)
+               return err;
+
+       if (!wpan_dev->netdev) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       rdev_lock_llsec_table(rdev, wpan_dev);
+       rdev_get_llsec_table(rdev, wpan_dev, &table);
+
+       /* TODO make it like station dump */
+       if (cb->args[2])
+               goto out;
+
+       list_for_each_entry(key, &table->keys, list) {
+               if (nl802154_send_key(skb, NL802154_CMD_NEW_SEC_KEY,
+                                     NETLINK_CB(cb->skb).portid,
+                                     cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                     rdev, wpan_dev->netdev, key) < 0) {
+                       /* TODO */
+                       err = -EIO;
+                       rdev_unlock_llsec_table(rdev, wpan_dev);
+                       goto out_err;
                }
        }
 
-       if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
-               rtnl_unlock();
+       cb->args[2] = 1;
+
+out:
+       rdev_unlock_llsec_table(rdev, wpan_dev);
+       err = skb->len;
+out_err:
+       nl802154_finish_wpan_dev_dump(rdev);
+
+       return err;
 }
 
-static const struct genl_ops nl802154_ops[] = {
-       {
-               .cmd = NL802154_CMD_GET_WPAN_PHY,
-               .doit = nl802154_get_wpan_phy,
-               .dumpit = nl802154_dump_wpan_phy,
-               .done = nl802154_dump_wpan_phy_done,
-               .policy = nl802154_policy,
-               /* can be retrieved by unprivileged users */
-               .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
-                                 NL802154_FLAG_NEED_RTNL,
-       },
-       {
-               .cmd = NL802154_CMD_GET_INTERFACE,
-               .doit = nl802154_get_interface,
-               .dumpit = nl802154_dump_interface,
-               .policy = nl802154_policy,
-               /* can be retrieved by unprivileged users */
-               .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
-                                 NL802154_FLAG_NEED_RTNL,
-       },
-       {
-               .cmd = NL802154_CMD_NEW_INTERFACE,
-               .doit = nl802154_new_interface,
-               .policy = nl802154_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+static const struct nla_policy nl802154_key_policy[NL802154_KEY_ATTR_MAX + 1] = {
+       [NL802154_KEY_ATTR_ID] = { NLA_NESTED },
+       /* TODO handle it as for_each_nested and NLA_FLAG? */
+       [NL802154_KEY_ATTR_USAGE_FRAMES] = { NLA_U8 },
+       /* TODO handle it as for_each_nested, not static array? */
+       [NL802154_KEY_ATTR_USAGE_CMDS] = { .len = NL802154_CMD_FRAME_NR_IDS / 8 },
+       [NL802154_KEY_ATTR_BYTES] = { .len = NL802154_KEY_SIZE },
+};
+
+static int nl802154_add_llsec_key(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1];
+       struct ieee802154_llsec_key key = { };
+       struct ieee802154_llsec_key_id id = { };
+       u32 commands[NL802154_CMD_FRAME_NR_IDS / 32] = { };
+
+       if (nla_parse_nested(attrs, NL802154_KEY_ATTR_MAX,
+                            info->attrs[NL802154_ATTR_SEC_KEY],
+                            nl802154_key_policy))
+               return -EINVAL;
+
+       if (!attrs[NL802154_KEY_ATTR_USAGE_FRAMES] ||
+           !attrs[NL802154_KEY_ATTR_BYTES])
+               return -EINVAL;
+
+       if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
+               return -ENOBUFS;
+
+       key.frame_types = nla_get_u8(attrs[NL802154_KEY_ATTR_USAGE_FRAMES]);
+       if (key.frame_types > BIT(NL802154_FRAME_MAX) ||
+           ((key.frame_types & BIT(NL802154_FRAME_CMD)) &&
+            !attrs[NL802154_KEY_ATTR_USAGE_CMDS]))
+               return -EINVAL;
+
+       if (attrs[NL802154_KEY_ATTR_USAGE_CMDS]) {
+               /* TODO for each nested */
+               nla_memcpy(commands, attrs[NL802154_KEY_ATTR_USAGE_CMDS],
+                          NL802154_CMD_FRAME_NR_IDS / 8);
+
+               /* TODO understand the -EINVAL logic here? last condition */
+               if (commands[0] || commands[1] || commands[2] || commands[3] ||
+                   commands[4] || commands[5] || commands[6] ||
+                   commands[7] > BIT(NL802154_CMD_FRAME_MAX))
+                       return -EINVAL;
+
+               key.cmd_frame_ids = commands[7];
+       } else {
+               key.cmd_frame_ids = 0;
+       }
+
+       nla_memcpy(key.key, attrs[NL802154_KEY_ATTR_BYTES], NL802154_KEY_SIZE);
+
+       if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
+               return -ENOBUFS;
+
+       return rdev_add_llsec_key(rdev, wpan_dev, &id, &key);
+}
+
+static int nl802154_del_llsec_key(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1];
+       struct ieee802154_llsec_key_id id;
+
+       if (nla_parse_nested(attrs, NL802154_KEY_ATTR_MAX,
+                            info->attrs[NL802154_ATTR_SEC_KEY],
+                            nl802154_key_policy))
+               return -EINVAL;
+
+       if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
+               return -ENOBUFS;
+
+       return rdev_del_llsec_key(rdev, wpan_dev, &id);
+}
+
+static int nl802154_send_device(struct sk_buff *msg, u32 cmd, u32 portid,
+                               u32 seq, int flags,
+                               struct cfg802154_registered_device *rdev,
+                               struct net_device *dev,
+                               const struct ieee802154_llsec_device *dev_desc)
+{
+       void *hdr;
+       struct nlattr *nl_device;
+
+       hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+       if (!hdr)
+               return -1;
+
+       if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
+               goto nla_put_failure;
+
+       nl_device = nla_nest_start(msg, NL802154_ATTR_SEC_DEVICE);
+       if (!nl_device)
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL802154_DEV_ATTR_FRAME_COUNTER,
+                       dev_desc->frame_counter) ||
+           nla_put_le16(msg, NL802154_DEV_ATTR_PAN_ID, dev_desc->pan_id) ||
+           nla_put_le16(msg, NL802154_DEV_ATTR_SHORT_ADDR,
+                        dev_desc->short_addr) ||
+           nla_put_le64(msg, NL802154_DEV_ATTR_EXTENDED_ADDR,
+                        dev_desc->hwaddr) ||
+           nla_put_u8(msg, NL802154_DEV_ATTR_SECLEVEL_EXEMPT,
+                      dev_desc->seclevel_exempt) ||
+           nla_put_u32(msg, NL802154_DEV_ATTR_KEY_MODE, dev_desc->key_mode))
+               goto nla_put_failure;
+
+       nla_nest_end(msg, nl_device);
+       genlmsg_end(msg, hdr);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_llsec_dev(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct cfg802154_registered_device *rdev = NULL;
+       struct ieee802154_llsec_device *dev;
+       struct ieee802154_llsec_table *table;
+       struct wpan_dev *wpan_dev;
+       int err;
+
+       err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
+       if (err)
+               return err;
+
+       if (!wpan_dev->netdev) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       rdev_lock_llsec_table(rdev, wpan_dev);
+       rdev_get_llsec_table(rdev, wpan_dev, &table);
+
+       /* TODO make it like station dump */
+       if (cb->args[2])
+               goto out;
+
+       list_for_each_entry(dev, &table->devices, list) {
+               if (nl802154_send_device(skb, NL802154_CMD_NEW_SEC_LEVEL,
+                                        NETLINK_CB(cb->skb).portid,
+                                        cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                        rdev, wpan_dev->netdev, dev) < 0) {
+                       /* TODO */
+                       err = -EIO;
+                       rdev_unlock_llsec_table(rdev, wpan_dev);
+                       goto out_err;
+               }
+       }
+
+       cb->args[2] = 1;
+
+out:
+       rdev_unlock_llsec_table(rdev, wpan_dev);
+       err = skb->len;
+out_err:
+       nl802154_finish_wpan_dev_dump(rdev);
+
+       return err;
+}
+
+static const struct nla_policy nl802154_dev_policy[NL802154_DEV_ATTR_MAX + 1] = {
+       [NL802154_DEV_ATTR_FRAME_COUNTER] = { NLA_U32 },
+       [NL802154_DEV_ATTR_PAN_ID] = { .type = NLA_U16 },
+       [NL802154_DEV_ATTR_SHORT_ADDR] = { .type = NLA_U16 },
+       [NL802154_DEV_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
+       [NL802154_DEV_ATTR_SECLEVEL_EXEMPT] = { NLA_U8 },
+       [NL802154_DEV_ATTR_KEY_MODE] = { NLA_U32 },
+};
+
+static int
+ieee802154_llsec_parse_device(struct nlattr *nla,
+                             struct ieee802154_llsec_device *dev)
+{
+       struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1];
+
+       if (!nla || nla_parse_nested(attrs, NL802154_DEV_ATTR_MAX, nla,
+                                    nl802154_dev_policy))
+               return -EINVAL;
+
+       memset(dev, 0, sizeof(*dev));
+
+       if (!attrs[NL802154_DEV_ATTR_FRAME_COUNTER] ||
+           !attrs[NL802154_DEV_ATTR_PAN_ID] ||
+           !attrs[NL802154_DEV_ATTR_SHORT_ADDR] ||
+           !attrs[NL802154_DEV_ATTR_EXTENDED_ADDR] ||
+           !attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] ||
+           !attrs[NL802154_DEV_ATTR_KEY_MODE])
+               return -EINVAL;
+
+       /* TODO be32 */
+       dev->frame_counter = nla_get_u32(attrs[NL802154_DEV_ATTR_FRAME_COUNTER]);
+       dev->pan_id = nla_get_le16(attrs[NL802154_DEV_ATTR_PAN_ID]);
+       dev->short_addr = nla_get_le16(attrs[NL802154_DEV_ATTR_SHORT_ADDR]);
+       /* TODO rename hwaddr to extended_addr */
+       dev->hwaddr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]);
+       dev->seclevel_exempt = nla_get_u8(attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT]);
+       dev->key_mode = nla_get_u32(attrs[NL802154_DEV_ATTR_KEY_MODE]);
+
+       if (dev->key_mode > NL802154_DEVKEY_MAX ||
+           (dev->seclevel_exempt != 0 && dev->seclevel_exempt != 1))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int nl802154_add_llsec_dev(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       struct ieee802154_llsec_device dev_desc;
+
+       if (ieee802154_llsec_parse_device(info->attrs[NL802154_ATTR_SEC_DEVICE],
+                                         &dev_desc) < 0)
+               return -EINVAL;
+
+       return rdev_add_device(rdev, wpan_dev, &dev_desc);
+}
+
+static int nl802154_del_llsec_dev(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1];
+       __le64 extended_addr;
+
+       if (nla_parse_nested(attrs, NL802154_DEV_ATTR_MAX,
+                            info->attrs[NL802154_ATTR_SEC_DEVICE],
+                            nl802154_dev_policy))
+               return -EINVAL;
+
+       if (!attrs[NL802154_DEV_ATTR_EXTENDED_ADDR])
+               return -EINVAL;
+
+       extended_addr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]);
+       return rdev_del_device(rdev, wpan_dev, extended_addr);
+}
+
+static int nl802154_send_devkey(struct sk_buff *msg, u32 cmd, u32 portid,
+                               u32 seq, int flags,
+                               struct cfg802154_registered_device *rdev,
+                               struct net_device *dev, __le64 extended_addr,
+                               const struct ieee802154_llsec_device_key *devkey)
+{
+       void *hdr;
+       struct nlattr *nl_devkey, *nl_key_id;
+
+       hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+       if (!hdr)
+               return -1;
+
+       if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
+               goto nla_put_failure;
+
+       nl_devkey = nla_nest_start(msg, NL802154_ATTR_SEC_DEVKEY);
+       if (!nl_devkey)
+               goto nla_put_failure;
+
+       if (nla_put_le64(msg, NL802154_DEVKEY_ATTR_EXTENDED_ADDR,
+                        extended_addr) ||
+           nla_put_u32(msg, NL802154_DEVKEY_ATTR_FRAME_COUNTER,
+                       devkey->frame_counter))
+               goto nla_put_failure;
+
+       nl_key_id = nla_nest_start(msg, NL802154_DEVKEY_ATTR_ID);
+       if (!nl_key_id)
+               goto nla_put_failure;
+
+       if (ieee802154_llsec_send_key_id(msg, &devkey->key_id) < 0)
+               goto nla_put_failure;
+
+       nla_nest_end(msg, nl_key_id);
+       nla_nest_end(msg, nl_devkey);
+       genlmsg_end(msg, hdr);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_llsec_devkey(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct cfg802154_registered_device *rdev = NULL;
+       struct ieee802154_llsec_device_key *kpos;
+       struct ieee802154_llsec_device *dpos;
+       struct ieee802154_llsec_table *table;
+       struct wpan_dev *wpan_dev;
+       int err;
+
+       err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
+       if (err)
+               return err;
+
+       if (!wpan_dev->netdev) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       rdev_lock_llsec_table(rdev, wpan_dev);
+       rdev_get_llsec_table(rdev, wpan_dev, &table);
+
+       /* TODO make it like station dump */
+       if (cb->args[2])
+               goto out;
+
+       /* TODO look if remove devkey and do some nested attribute */
+       list_for_each_entry(dpos, &table->devices, list) {
+               list_for_each_entry(kpos, &dpos->keys, list) {
+                       if (nl802154_send_devkey(skb,
+                                                NL802154_CMD_NEW_SEC_LEVEL,
+                                                NETLINK_CB(cb->skb).portid,
+                                                cb->nlh->nlmsg_seq,
+                                                NLM_F_MULTI, rdev,
+                                                wpan_dev->netdev,
+                                                dpos->hwaddr,
+                                                kpos) < 0) {
+                               /* TODO */
+                               err = -EIO;
+                               rdev_unlock_llsec_table(rdev, wpan_dev);
+                               goto out_err;
+                       }
+               }
+       }
+
+       cb->args[2] = 1;
+
+out:
+       rdev_unlock_llsec_table(rdev, wpan_dev);
+       err = skb->len;
+out_err:
+       nl802154_finish_wpan_dev_dump(rdev);
+
+       return err;
+}
+
+static const struct nla_policy nl802154_devkey_policy[NL802154_DEVKEY_ATTR_MAX + 1] = {
+       [NL802154_DEVKEY_ATTR_FRAME_COUNTER] = { NLA_U32 },
+       [NL802154_DEVKEY_ATTR_EXTENDED_ADDR] = { NLA_U64 },
+       [NL802154_DEVKEY_ATTR_ID] = { NLA_NESTED },
+};
+
+static int nl802154_add_llsec_devkey(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1];
+       struct ieee802154_llsec_device_key key;
+       __le64 extended_addr;
+
+       if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] ||
+           nla_parse_nested(attrs, NL802154_DEVKEY_ATTR_MAX,
+                            info->attrs[NL802154_ATTR_SEC_DEVKEY],
+                            nl802154_devkey_policy) < 0)
+               return -EINVAL;
+
+       if (!attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER] ||
+           !attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
+               return -EINVAL;
+
+       /* TODO change key.id ? */
+       if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID],
+                                         &key.key_id) < 0)
+               return -ENOBUFS;
+
+       /* TODO be32 */
+       key.frame_counter = nla_get_u32(attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER]);
+       /* TODO change naming hwaddr -> extended_addr
+        * check unique identifier short+pan OR extended_addr
+        */
+       extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]);
+       return rdev_add_devkey(rdev, wpan_dev, extended_addr, &key);
+}
+
+static int nl802154_del_llsec_devkey(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1];
+       struct ieee802154_llsec_device_key key;
+       __le64 extended_addr;
+
+       if (nla_parse_nested(attrs, NL802154_DEVKEY_ATTR_MAX,
+                            info->attrs[NL802154_ATTR_SEC_DEVKEY],
+                            nl802154_devkey_policy))
+               return -EINVAL;
+
+       if (!attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
+               return -EINVAL;
+
+       /* TODO change key.id ? */
+       if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID],
+                                         &key.key_id) < 0)
+               return -ENOBUFS;
+
+       /* TODO change naming hwaddr -> extended_addr
+        * check unique identifier short+pan OR extended_addr
+        */
+       extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]);
+       return rdev_del_devkey(rdev, wpan_dev, extended_addr, &key);
+}
+
+static int nl802154_send_seclevel(struct sk_buff *msg, u32 cmd, u32 portid,
+                                 u32 seq, int flags,
+                                 struct cfg802154_registered_device *rdev,
+                                 struct net_device *dev,
+                                 const struct ieee802154_llsec_seclevel *sl)
+{
+       void *hdr;
+       struct nlattr *nl_seclevel;
+
+       hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+       if (!hdr)
+               return -1;
+
+       if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
+               goto nla_put_failure;
+
+       nl_seclevel = nla_nest_start(msg, NL802154_ATTR_SEC_LEVEL);
+       if (!nl_seclevel)
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_FRAME, sl->frame_type) ||
+           nla_put_u32(msg, NL802154_SECLEVEL_ATTR_LEVELS, sl->sec_levels) ||
+           nla_put_u8(msg, NL802154_SECLEVEL_ATTR_DEV_OVERRIDE,
+                      sl->device_override))
+               goto nla_put_failure;
+
+       if (sl->frame_type == NL802154_FRAME_CMD) {
+               if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_CMD_FRAME,
+                               sl->cmd_frame_id))
+                       goto nla_put_failure;
+       }
+
+       nla_nest_end(msg, nl_seclevel);
+       genlmsg_end(msg, hdr);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_llsec_seclevel(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct cfg802154_registered_device *rdev = NULL;
+       struct ieee802154_llsec_seclevel *sl;
+       struct ieee802154_llsec_table *table;
+       struct wpan_dev *wpan_dev;
+       int err;
+
+       err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
+       if (err)
+               return err;
+
+       if (!wpan_dev->netdev) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       rdev_lock_llsec_table(rdev, wpan_dev);
+       rdev_get_llsec_table(rdev, wpan_dev, &table);
+
+       /* TODO make it like station dump */
+       if (cb->args[2])
+               goto out;
+
+       list_for_each_entry(sl, &table->security_levels, list) {
+               if (nl802154_send_seclevel(skb, NL802154_CMD_NEW_SEC_LEVEL,
+                                          NETLINK_CB(cb->skb).portid,
+                                          cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                          rdev, wpan_dev->netdev, sl) < 0) {
+                       /* TODO */
+                       err = -EIO;
+                       rdev_unlock_llsec_table(rdev, wpan_dev);
+                       goto out_err;
+               }
+       }
+
+       cb->args[2] = 1;
+
+out:
+       rdev_unlock_llsec_table(rdev, wpan_dev);
+       err = skb->len;
+out_err:
+       nl802154_finish_wpan_dev_dump(rdev);
+
+       return err;
+}
+
+static const struct nla_policy nl802154_seclevel_policy[NL802154_SECLEVEL_ATTR_MAX + 1] = {
+       [NL802154_SECLEVEL_ATTR_LEVELS] = { .type = NLA_U8 },
+       [NL802154_SECLEVEL_ATTR_FRAME] = { .type = NLA_U32 },
+       [NL802154_SECLEVEL_ATTR_CMD_FRAME] = { .type = NLA_U32 },
+       [NL802154_SECLEVEL_ATTR_DEV_OVERRIDE] = { .type = NLA_U8 },
+};
+
+static int
+llsec_parse_seclevel(struct nlattr *nla, struct ieee802154_llsec_seclevel *sl)
+{
+       struct nlattr *attrs[NL802154_SECLEVEL_ATTR_MAX + 1];
+
+       if (!nla || nla_parse_nested(attrs, NL802154_SECLEVEL_ATTR_MAX, nla,
+                                    nl802154_seclevel_policy))
+               return -EINVAL;
+
+       memset(sl, 0, sizeof(*sl));
+
+       if (!attrs[NL802154_SECLEVEL_ATTR_LEVELS] ||
+           !attrs[NL802154_SECLEVEL_ATTR_FRAME] ||
+           !attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE])
+               return -EINVAL;
+
+       sl->sec_levels = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_LEVELS]);
+       sl->frame_type = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_FRAME]);
+       sl->device_override = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE]);
+       if (sl->frame_type > NL802154_FRAME_MAX ||
+           (sl->device_override != 0 && sl->device_override != 1))
+               return -EINVAL;
+
+       if (sl->frame_type == NL802154_FRAME_CMD) {
+               if (!attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME])
+                       return -EINVAL;
+
+               sl->cmd_frame_id = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME]);
+               if (sl->cmd_frame_id > NL802154_CMD_FRAME_MAX)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int nl802154_add_llsec_seclevel(struct sk_buff *skb,
+                                      struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       struct ieee802154_llsec_seclevel sl;
+
+       if (llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL],
+                                &sl) < 0)
+               return -EINVAL;
+
+       return rdev_add_seclevel(rdev, wpan_dev, &sl);
+}
+
+static int nl802154_del_llsec_seclevel(struct sk_buff *skb,
+                                      struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       struct ieee802154_llsec_seclevel sl;
+
+       if (!info->attrs[NL802154_ATTR_SEC_LEVEL] ||
+           llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL],
+                                &sl) < 0)
+               return -EINVAL;
+
+       return rdev_del_seclevel(rdev, wpan_dev, &sl);
+}
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
+#define NL802154_FLAG_NEED_WPAN_PHY    0x01
+#define NL802154_FLAG_NEED_NETDEV      0x02
+#define NL802154_FLAG_NEED_RTNL                0x04
+#define NL802154_FLAG_CHECK_NETDEV_UP  0x08
+#define NL802154_FLAG_NEED_NETDEV_UP   (NL802154_FLAG_NEED_NETDEV |\
+                                        NL802154_FLAG_CHECK_NETDEV_UP)
+#define NL802154_FLAG_NEED_WPAN_DEV    0x10
+#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
+                                        NL802154_FLAG_CHECK_NETDEV_UP)
+
+static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+                            struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev;
+       struct wpan_dev *wpan_dev;
+       struct net_device *dev;
+       bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
+
+       if (rtnl)
+               rtnl_lock();
+
+       if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
+               rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
+               if (IS_ERR(rdev)) {
+                       if (rtnl)
+                               rtnl_unlock();
+                       return PTR_ERR(rdev);
+               }
+               info->user_ptr[0] = rdev;
+       } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
+                  ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+               ASSERT_RTNL();
+               wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
+                                                          info->attrs);
+               if (IS_ERR(wpan_dev)) {
+                       if (rtnl)
+                               rtnl_unlock();
+                       return PTR_ERR(wpan_dev);
+               }
+
+               dev = wpan_dev->netdev;
+               rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+
+               if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
+                       if (!dev) {
+                               if (rtnl)
+                                       rtnl_unlock();
+                               return -EINVAL;
+                       }
+
+                       info->user_ptr[1] = dev;
+               } else {
+                       info->user_ptr[1] = wpan_dev;
+               }
+
+               if (dev) {
+                       if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
+                           !netif_running(dev)) {
+                               if (rtnl)
+                                       rtnl_unlock();
+                               return -ENETDOWN;
+                       }
+
+                       dev_hold(dev);
+               }
+
+               info->user_ptr[0] = rdev;
+       }
+
+       return 0;
+}
+
+static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+                              struct genl_info *info)
+{
+       if (info->user_ptr[1]) {
+               if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+                       struct wpan_dev *wpan_dev = info->user_ptr[1];
+
+                       if (wpan_dev->netdev)
+                               dev_put(wpan_dev->netdev);
+               } else {
+                       dev_put(info->user_ptr[1]);
+               }
+       }
+
+       if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
+               rtnl_unlock();
+}
+
+static const struct genl_ops nl802154_ops[] = {
+       {
+               .cmd = NL802154_CMD_GET_WPAN_PHY,
+               .doit = nl802154_get_wpan_phy,
+               .dumpit = nl802154_dump_wpan_phy,
+               .done = nl802154_dump_wpan_phy_done,
+               .policy = nl802154_policy,
+               /* can be retrieved by unprivileged users */
+               .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_GET_INTERFACE,
+               .doit = nl802154_get_interface,
+               .dumpit = nl802154_dump_interface,
+               .policy = nl802154_policy,
+               /* can be retrieved by unprivileged users */
+               .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_NEW_INTERFACE,
+               .doit = nl802154_new_interface,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
                                  NL802154_FLAG_NEED_RTNL,
        },
        {
@@ -1289,6 +2303,119 @@ static const struct genl_ops nl802154_ops[] = {
                .internal_flags = NL802154_FLAG_NEED_NETDEV |
                                  NL802154_FLAG_NEED_RTNL,
        },
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+       {
+               .cmd = NL802154_CMD_SET_SEC_PARAMS,
+               .doit = nl802154_set_llsec_params,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_GET_SEC_KEY,
+               /* TODO .doit by matching key id? */
+               .dumpit = nl802154_dump_llsec_key,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_NEW_SEC_KEY,
+               .doit = nl802154_add_llsec_key,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_DEL_SEC_KEY,
+               .doit = nl802154_del_llsec_key,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       /* TODO unique identifier must short+pan OR extended_addr */
+       {
+               .cmd = NL802154_CMD_GET_SEC_DEV,
+               /* TODO .doit by matching extended_addr? */
+               .dumpit = nl802154_dump_llsec_dev,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_NEW_SEC_DEV,
+               .doit = nl802154_add_llsec_dev,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_DEL_SEC_DEV,
+               .doit = nl802154_del_llsec_dev,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       /* TODO remove complete devkey, put it as nested? */
+       {
+               .cmd = NL802154_CMD_GET_SEC_DEVKEY,
+               /* TODO doit by matching ??? */
+               .dumpit = nl802154_dump_llsec_devkey,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_NEW_SEC_DEVKEY,
+               .doit = nl802154_add_llsec_devkey,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_DEL_SEC_DEVKEY,
+               .doit = nl802154_del_llsec_devkey,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_GET_SEC_LEVEL,
+               /* TODO .doit by matching frame_type? */
+               .dumpit = nl802154_dump_llsec_seclevel,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_NEW_SEC_LEVEL,
+               .doit = nl802154_add_llsec_seclevel,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_DEL_SEC_LEVEL,
+               /* TODO match frame_type only? */
+               .doit = nl802154_del_llsec_seclevel,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
 };
 
 /* initialisation/exit functions */
index 03b357501cc55dc99c29a642d89975cbd2da87a0..4441c63b3ea64829d1236fab96bf601dfa15cb24 100644 (file)
@@ -208,4 +208,113 @@ rdev_set_ackreq_default(struct cfg802154_registered_device *rdev,
        return ret;
 }
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+/* TODO this is already a nl802154, so move into ieee802154 */
+static inline void
+rdev_get_llsec_table(struct cfg802154_registered_device *rdev,
+                    struct wpan_dev *wpan_dev,
+                    struct ieee802154_llsec_table **table)
+{
+       rdev->ops->get_llsec_table(&rdev->wpan_phy, wpan_dev, table);
+}
+
+static inline void
+rdev_lock_llsec_table(struct cfg802154_registered_device *rdev,
+                     struct wpan_dev *wpan_dev)
+{
+       rdev->ops->lock_llsec_table(&rdev->wpan_phy, wpan_dev);
+}
+
+static inline void
+rdev_unlock_llsec_table(struct cfg802154_registered_device *rdev,
+                       struct wpan_dev *wpan_dev)
+{
+       rdev->ops->unlock_llsec_table(&rdev->wpan_phy, wpan_dev);
+}
+
+static inline int
+rdev_get_llsec_params(struct cfg802154_registered_device *rdev,
+                     struct wpan_dev *wpan_dev,
+                     struct ieee802154_llsec_params *params)
+{
+       return rdev->ops->get_llsec_params(&rdev->wpan_phy, wpan_dev, params);
+}
+
+static inline int
+rdev_set_llsec_params(struct cfg802154_registered_device *rdev,
+                     struct wpan_dev *wpan_dev,
+                     const struct ieee802154_llsec_params *params,
+                     u32 changed)
+{
+       return rdev->ops->set_llsec_params(&rdev->wpan_phy, wpan_dev, params,
+                                          changed);
+}
+
+static inline int
+rdev_add_llsec_key(struct cfg802154_registered_device *rdev,
+                  struct wpan_dev *wpan_dev,
+                  const struct ieee802154_llsec_key_id *id,
+                  const struct ieee802154_llsec_key *key)
+{
+       return rdev->ops->add_llsec_key(&rdev->wpan_phy, wpan_dev, id, key);
+}
+
+static inline int
+rdev_del_llsec_key(struct cfg802154_registered_device *rdev,
+                  struct wpan_dev *wpan_dev,
+                  const struct ieee802154_llsec_key_id *id)
+{
+       return rdev->ops->del_llsec_key(&rdev->wpan_phy, wpan_dev, id);
+}
+
+static inline int
+rdev_add_seclevel(struct cfg802154_registered_device *rdev,
+                 struct wpan_dev *wpan_dev,
+                 const struct ieee802154_llsec_seclevel *sl)
+{
+       return rdev->ops->add_seclevel(&rdev->wpan_phy, wpan_dev, sl);
+}
+
+static inline int
+rdev_del_seclevel(struct cfg802154_registered_device *rdev,
+                 struct wpan_dev *wpan_dev,
+                 const struct ieee802154_llsec_seclevel *sl)
+{
+       return rdev->ops->del_seclevel(&rdev->wpan_phy, wpan_dev, sl);
+}
+
+static inline int
+rdev_add_device(struct cfg802154_registered_device *rdev,
+               struct wpan_dev *wpan_dev,
+               const struct ieee802154_llsec_device *dev_desc)
+{
+       return rdev->ops->add_device(&rdev->wpan_phy, wpan_dev, dev_desc);
+}
+
+static inline int
+rdev_del_device(struct cfg802154_registered_device *rdev,
+               struct wpan_dev *wpan_dev, __le64 extended_addr)
+{
+       return rdev->ops->del_device(&rdev->wpan_phy, wpan_dev, extended_addr);
+}
+
+static inline int
+rdev_add_devkey(struct cfg802154_registered_device *rdev,
+               struct wpan_dev *wpan_dev, __le64 extended_addr,
+               const struct ieee802154_llsec_device_key *devkey)
+{
+       return rdev->ops->add_devkey(&rdev->wpan_phy, wpan_dev, extended_addr,
+                                    devkey);
+}
+
+static inline int
+rdev_del_devkey(struct cfg802154_registered_device *rdev,
+               struct wpan_dev *wpan_dev, __le64 extended_addr,
+               const struct ieee802154_llsec_device_key *devkey)
+{
+       return rdev->ops->del_devkey(&rdev->wpan_phy, wpan_dev, extended_addr,
+                                    devkey);
+}
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 #endif /* __CFG802154_RDEV_OPS */
index b6eacf30ee7a619e10a682e7c3e5c2a5e9dfe408..a548be247e15d87c82cfda7a6cd3bde32a7159a7 100644 (file)
@@ -273,7 +273,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
                goto out;
        }
 
-       mtu = dev->mtu;
+       mtu = IEEE802154_MTU;
        pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
 
        if (size > mtu) {
@@ -637,7 +637,7 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
                err = -ENXIO;
                goto out;
        }
-       mtu = dev->mtu;
+       mtu = IEEE802154_MTU;
        pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
 
        if (size > mtu) {
@@ -676,8 +676,8 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
        cb->seclevel = ro->seclevel;
        cb->seclevel_override = ro->seclevel_override;
 
-       err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr,
-                             ro->bound ? &ro->src_addr : NULL, size);
+       err = wpan_dev_hard_header(skb, dev, &dst_addr,
+                                  ro->bound ? &ro->src_addr : NULL, size);
        if (err < 0)
                goto out_skb;
 
index 89aacb630a53362bb9de51c7381ae9b6a799bdc3..c29809f765dc5d4d95edd5d6ac3cc321fcb97c88 100644 (file)
@@ -8,6 +8,7 @@ obj-y     := route.o inetpeer.o protocol.o \
             inet_timewait_sock.o inet_connection_sock.o \
             tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \
             tcp_minisocks.o tcp_cong.o tcp_metrics.o tcp_fastopen.o \
+            tcp_recovery.o \
             tcp_offload.o datagram.o raw.o udp.o udplite.o \
             udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \
             fib_frontend.o fib_semantics.o fib_trie.o \
index 1d0c3adb6f349b1db579085fd15d9a2ff30360b6..11c4ca13ec3b0434090b3d5cbdd7a0aae784952f 100644 (file)
 #ifdef CONFIG_IP_MROUTE
 #include <linux/mroute.h>
 #endif
-#include <net/vrf.h>
+#include <net/l3mdev.h>
 
 
 /* The inetsw table contains everything that inet_create needs to
@@ -219,17 +219,13 @@ int inet_listen(struct socket *sock, int backlog)
                 * shutdown() (rather than close()).
                 */
                if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) != 0 &&
-                   !inet_csk(sk)->icsk_accept_queue.fastopenq) {
+                   !inet_csk(sk)->icsk_accept_queue.fastopenq.max_qlen) {
                        if ((sysctl_tcp_fastopen & TFO_SERVER_WO_SOCKOPT1) != 0)
-                               err = fastopen_init_queue(sk, backlog);
+                               fastopen_queue_tune(sk, backlog);
                        else if ((sysctl_tcp_fastopen &
                                  TFO_SERVER_WO_SOCKOPT2) != 0)
-                               err = fastopen_init_queue(sk,
+                               fastopen_queue_tune(sk,
                                    ((uint)sysctl_tcp_fastopen) >> 16);
-                       else
-                               err = 0;
-                       if (err)
-                               goto out;
 
                        tcp_fastopen_init_key_once(true);
                }
@@ -450,7 +446,7 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                        goto out;
        }
 
-       tb_id = vrf_dev_table_ifindex(net, sk->sk_bound_dev_if) ? : tb_id;
+       tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ? : tb_id;
        chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id);
 
        /* Not specified by any standard per-se, however it breaks too
@@ -1043,22 +1039,16 @@ void inet_register_protosw(struct inet_protosw *p)
                goto out_illegal;
 
        /* If we are trying to override a permanent protocol, bail. */
-       answer = NULL;
        last_perm = &inetsw[p->type];
        list_for_each(lh, &inetsw[p->type]) {
                answer = list_entry(lh, struct inet_protosw, list);
-
                /* Check only the non-wild match. */
-               if (INET_PROTOSW_PERMANENT & answer->flags) {
-                       if (protocol == answer->protocol)
-                               break;
-                       last_perm = lh;
-               }
-
-               answer = NULL;
+               if ((INET_PROTOSW_PERMANENT & answer->flags) == 0)
+                       break;
+               if (protocol == answer->protocol)
+                       goto out_permanent;
+               last_perm = lh;
        }
-       if (answer)
-               goto out_permanent;
 
        /* Add the new entry after the last permanent entry if any, so that
         * the new entry does not override a permanent entry when matched with
index 0c9c3482e41997a671c23be7dc0387edb1403916..59b3e0e8fd5110031eff0303b5f75622bdd7d22a 100644 (file)
@@ -624,14 +624,20 @@ out:
 }
 EXPORT_SYMBOL(arp_create);
 
+static int arp_xmit_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+{
+       return dev_queue_xmit(skb);
+}
+
 /*
  *     Send an arp packet.
  */
 void arp_xmit(struct sk_buff *skb)
 {
        /* Send it off, maybe filter it using firewalling first.  */
-       NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, NULL, skb,
-               NULL, skb->dev, dev_queue_xmit_sk);
+       NF_HOOK(NFPROTO_ARP, NF_ARP_OUT,
+               dev_net(skb->dev), NULL, skb, NULL, skb->dev,
+               arp_xmit_finish);
 }
 EXPORT_SYMBOL(arp_xmit);
 
@@ -639,7 +645,7 @@ EXPORT_SYMBOL(arp_xmit);
  *     Process an arp request.
  */
 
-static int arp_process(struct sock *sk, struct sk_buff *skb)
+static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct net_device *dev = skb->dev;
        struct in_device *in_dev = __in_dev_get_rcu(dev);
@@ -651,7 +657,6 @@ static int arp_process(struct sock *sk, struct sk_buff *skb)
        u16 dev_type = dev->type;
        int addr_type;
        struct neighbour *n;
-       struct net *net = dev_net(dev);
        struct dst_entry *reply_dst = NULL;
        bool is_garp = false;
 
@@ -872,7 +877,7 @@ out_free_dst:
 
 static void parp_redo(struct sk_buff *skb)
 {
-       arp_process(NULL, skb);
+       arp_process(dev_net(skb->dev), NULL, skb);
 }
 
 
@@ -905,8 +910,9 @@ static int arp_rcv(struct sk_buff *skb, struct net_device *dev,
 
        memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
 
-       return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, NULL, skb,
-                      dev, NULL, arp_process);
+       return NF_HOOK(NFPROTO_ARP, NF_ARP_IN,
+                      dev_net(dev), NULL, skb, dev, NULL,
+                      arp_process);
 
 consumeskb:
        consume_skb(skb);
index 2d9cb1748f8191c785567632faf0ee14eaca628b..cebd9d31e65a4a7539cab0bef71887736bc188f7 100644 (file)
@@ -1644,7 +1644,8 @@ errout:
                rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
 }
 
-static size_t inet_get_link_af_size(const struct net_device *dev)
+static size_t inet_get_link_af_size(const struct net_device *dev,
+                                   u32 ext_filter_mask)
 {
        struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
 
@@ -1654,7 +1655,8 @@ static size_t inet_get_link_af_size(const struct net_device *dev)
        return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
 }
 
-static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
+static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
+                            u32 ext_filter_mask)
 {
        struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
        struct nlattr *nla;
@@ -2397,4 +2399,3 @@ void __init devinet_init(void)
        rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
                      inet_netconf_dump_devconf, NULL);
 }
-
index 690bcbc59f26d1add82e9ebaa82e2c072b84ca47..cc8f3e506cded3fbcc9a64bbd369577c5f7cb52d 100644 (file)
@@ -45,7 +45,7 @@
 #include <net/ip_fib.h>
 #include <net/rtnetlink.h>
 #include <net/xfrm.h>
-#include <net/vrf.h>
+#include <net/l3mdev.h>
 #include <trace/events/fib.h>
 
 #ifndef CONFIG_IP_MULTIPLE_TABLES
@@ -255,7 +255,7 @@ EXPORT_SYMBOL(inet_addr_type);
 unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev,
                                __be32 addr)
 {
-       u32 rt_table = vrf_dev_table(dev) ? : RT_TABLE_LOCAL;
+       u32 rt_table = l3mdev_fib_table(dev) ? : RT_TABLE_LOCAL;
 
        return __inet_dev_addr_type(net, dev, addr, rt_table);
 }
@@ -268,7 +268,7 @@ unsigned int inet_addr_type_dev_table(struct net *net,
                                      const struct net_device *dev,
                                      __be32 addr)
 {
-       u32 rt_table = vrf_dev_table(dev) ? : RT_TABLE_LOCAL;
+       u32 rt_table = l3mdev_fib_table(dev) ? : RT_TABLE_LOCAL;
 
        return __inet_dev_addr_type(net, NULL, addr, rt_table);
 }
@@ -332,7 +332,7 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
        bool dev_match;
 
        fl4.flowi4_oif = 0;
-       fl4.flowi4_iif = vrf_master_ifindex_rcu(dev);
+       fl4.flowi4_iif = l3mdev_master_ifindex_rcu(dev);
        if (!fl4.flowi4_iif)
                fl4.flowi4_iif = oif ? : LOOPBACK_IFINDEX;
        fl4.daddr = src;
@@ -367,7 +367,7 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
                if (nh->nh_dev == dev) {
                        dev_match = true;
                        break;
-               } else if (vrf_master_ifindex_rcu(nh->nh_dev) == dev->ifindex) {
+               } else if (l3mdev_master_ifindex_rcu(nh->nh_dev) == dev->ifindex) {
                        dev_match = true;
                        break;
                }
@@ -804,7 +804,7 @@ out:
 static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifaddr *ifa)
 {
        struct net *net = dev_net(ifa->ifa_dev->dev);
-       u32 tb_id = vrf_dev_table_rtnl(ifa->ifa_dev->dev);
+       u32 tb_id = l3mdev_fib_table(ifa->ifa_dev->dev);
        struct fib_table *tb;
        struct fib_config cfg = {
                .fc_protocol = RTPROT_KERNEL,
@@ -867,9 +867,10 @@ void fib_add_ifaddr(struct in_ifaddr *ifa)
 
        if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) &&
            (prefix != addr || ifa->ifa_prefixlen < 32)) {
-               fib_magic(RTM_NEWROUTE,
-                         dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
-                         prefix, ifa->ifa_prefixlen, prim);
+               if (!(ifa->ifa_flags & IFA_F_NOPREFIXROUTE))
+                       fib_magic(RTM_NEWROUTE,
+                                 dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
+                                 prefix, ifa->ifa_prefixlen, prim);
 
                /* Add network specific broadcasts, when it takes a sense */
                if (ifa->ifa_prefixlen < 31) {
@@ -914,9 +915,10 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
                }
        } else if (!ipv4_is_zeronet(any) &&
                   (any != ifa->ifa_local || ifa->ifa_prefixlen < 32)) {
-               fib_magic(RTM_DELROUTE,
-                         dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
-                         any, ifa->ifa_prefixlen, prim);
+               if (!(ifa->ifa_flags & IFA_F_NOPREFIXROUTE))
+                       fib_magic(RTM_DELROUTE,
+                                 dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
+                                 any, ifa->ifa_prefixlen, prim);
                subnet = 1;
        }
 
@@ -1110,9 +1112,10 @@ static void nl_fib_lookup_exit(struct net *net)
        net->ipv4.fibnl = NULL;
 }
 
-static void fib_disable_ip(struct net_device *dev, unsigned long event)
+static void fib_disable_ip(struct net_device *dev, unsigned long event,
+                          bool force)
 {
-       if (fib_sync_down_dev(dev, event))
+       if (fib_sync_down_dev(dev, event, force))
                fib_flush(dev_net(dev));
        rt_cache_flush(dev_net(dev));
        arp_ifdown(dev);
@@ -1140,7 +1143,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
                        /* Last address was deleted from this interface.
                         * Disable IP.
                         */
-                       fib_disable_ip(dev, event);
+                       fib_disable_ip(dev, event, true);
                } else {
                        rt_cache_flush(dev_net(dev));
                }
@@ -1157,7 +1160,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
        unsigned int flags;
 
        if (event == NETDEV_UNREGISTER) {
-               fib_disable_ip(dev, event);
+               fib_disable_ip(dev, event, true);
                rt_flush_dev(dev);
                return NOTIFY_DONE;
        }
@@ -1178,14 +1181,14 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
                rt_cache_flush(net);
                break;
        case NETDEV_DOWN:
-               fib_disable_ip(dev, event);
+               fib_disable_ip(dev, event, false);
                break;
        case NETDEV_CHANGE:
                flags = dev_get_flags(dev);
                if (flags & (IFF_RUNNING | IFF_LOWER_UP))
                        fib_sync_up(dev, RTNH_F_LINKDOWN);
                else
-                       fib_sync_down_dev(dev, event);
+                       fib_sync_down_dev(dev, event, false);
                /* fall through */
        case NETDEV_CHANGEMTU:
                rt_cache_flush(net);
index 064bd3caaa4f1f8fda928d0c1e3ca2446d67c2ed..3e87447e65c777ff969d366136d2015e3cf9a8e2 100644 (file)
@@ -57,8 +57,7 @@ static unsigned int fib_info_cnt;
 static struct hlist_head fib_info_devhash[DEVINDEX_HASHSIZE];
 
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-
-static DEFINE_SPINLOCK(fib_multipath_lock);
+u32 fib_multipath_secret __read_mostly;
 
 #define for_nexthops(fi) {                                             \
        int nhsel; const struct fib_nh *nh;                             \
@@ -532,7 +531,67 @@ errout:
        return ret;
 }
 
-#endif
+static void fib_rebalance(struct fib_info *fi)
+{
+       int total;
+       int w;
+       struct in_device *in_dev;
+
+       if (fi->fib_nhs < 2)
+               return;
+
+       total = 0;
+       for_nexthops(fi) {
+               if (nh->nh_flags & RTNH_F_DEAD)
+                       continue;
+
+               in_dev = __in_dev_get_rtnl(nh->nh_dev);
+
+               if (in_dev &&
+                   IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
+                   nh->nh_flags & RTNH_F_LINKDOWN)
+                       continue;
+
+               total += nh->nh_weight;
+       } endfor_nexthops(fi);
+
+       w = 0;
+       change_nexthops(fi) {
+               int upper_bound;
+
+               in_dev = __in_dev_get_rtnl(nexthop_nh->nh_dev);
+
+               if (nexthop_nh->nh_flags & RTNH_F_DEAD) {
+                       upper_bound = -1;
+               } else if (in_dev &&
+                          IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
+                          nexthop_nh->nh_flags & RTNH_F_LINKDOWN) {
+                       upper_bound = -1;
+               } else {
+                       w += nexthop_nh->nh_weight;
+                       upper_bound = DIV_ROUND_CLOSEST_ULL((u64)w << 31,
+                                                           total) - 1;
+               }
+
+               atomic_set(&nexthop_nh->nh_upper_bound, upper_bound);
+       } endfor_nexthops(fi);
+
+       net_get_random_once(&fib_multipath_secret,
+                           sizeof(fib_multipath_secret));
+}
+
+static inline void fib_add_weight(struct fib_info *fi,
+                                 const struct fib_nh *nh)
+{
+       fi->fib_weight += nh->nh_weight;
+}
+
+#else /* CONFIG_IP_ROUTE_MULTIPATH */
+
+#define fib_rebalance(fi) do { } while (0)
+#define fib_add_weight(fi, nh) do { } while (0)
+
+#endif /* CONFIG_IP_ROUTE_MULTIPATH */
 
 static int fib_encap_match(struct net *net, u16 encap_type,
                           struct nlattr *encap,
@@ -1094,8 +1153,11 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
 
        change_nexthops(fi) {
                fib_info_update_nh_saddr(net, nexthop_nh);
+               fib_add_weight(fi, nexthop_nh);
        } endfor_nexthops(fi)
 
+       fib_rebalance(fi);
+
 link_it:
        ofi = fib_find_info(fi);
        if (ofi) {
@@ -1281,7 +1343,13 @@ int fib_sync_down_addr(struct net *net, __be32 local)
        return ret;
 }
 
-int fib_sync_down_dev(struct net_device *dev, unsigned long event)
+/* Event              force Flags           Description
+ * NETDEV_CHANGE      0     LINKDOWN        Carrier OFF, not for scope host
+ * NETDEV_DOWN        0     LINKDOWN|DEAD   Link down, not for scope host
+ * NETDEV_DOWN        1     LINKDOWN|DEAD   Last address removed
+ * NETDEV_UNREGISTER  1     LINKDOWN|DEAD   Device removed
+ */
+int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force)
 {
        int ret = 0;
        int scope = RT_SCOPE_NOWHERE;
@@ -1290,8 +1358,7 @@ int fib_sync_down_dev(struct net_device *dev, unsigned long event)
        struct hlist_head *head = &fib_info_devhash[hash];
        struct fib_nh *nh;
 
-       if (event == NETDEV_UNREGISTER ||
-           event == NETDEV_DOWN)
+       if (force)
                scope = -1;
 
        hlist_for_each_entry(nh, head, nh_hash) {
@@ -1317,12 +1384,6 @@ int fib_sync_down_dev(struct net_device *dev, unsigned long event)
                                        nexthop_nh->nh_flags |= RTNH_F_LINKDOWN;
                                        break;
                                }
-#ifdef CONFIG_IP_ROUTE_MULTIPATH
-                               spin_lock_bh(&fib_multipath_lock);
-                               fi->fib_power -= nexthop_nh->nh_power;
-                               nexthop_nh->nh_power = 0;
-                               spin_unlock_bh(&fib_multipath_lock);
-#endif
                                dead++;
                        }
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
@@ -1345,6 +1406,8 @@ int fib_sync_down_dev(struct net_device *dev, unsigned long event)
                        }
                        ret++;
                }
+
+               fib_rebalance(fi);
        }
 
        return ret;
@@ -1440,6 +1503,13 @@ int fib_sync_up(struct net_device *dev, unsigned int nh_flags)
        if (!(dev->flags & IFF_UP))
                return 0;
 
+       if (nh_flags & RTNH_F_DEAD) {
+               unsigned int flags = dev_get_flags(dev);
+
+               if (flags & (IFF_RUNNING | IFF_LOWER_UP))
+                       nh_flags |= RTNH_F_LINKDOWN;
+       }
+
        prev_fi = NULL;
        hash = fib_devindex_hashfn(dev->ifindex);
        head = &fib_info_devhash[hash];
@@ -1467,20 +1537,15 @@ int fib_sync_up(struct net_device *dev, unsigned int nh_flags)
                            !__in_dev_get_rtnl(dev))
                                continue;
                        alive++;
-#ifdef CONFIG_IP_ROUTE_MULTIPATH
-                       spin_lock_bh(&fib_multipath_lock);
-                       nexthop_nh->nh_power = 0;
-                       nexthop_nh->nh_flags &= ~nh_flags;
-                       spin_unlock_bh(&fib_multipath_lock);
-#else
                        nexthop_nh->nh_flags &= ~nh_flags;
-#endif
                } endfor_nexthops(fi)
 
                if (alive > 0) {
                        fi->fib_flags &= ~nh_flags;
                        ret++;
                }
+
+               fib_rebalance(fi);
        }
 
        return ret;
@@ -1488,62 +1553,41 @@ int fib_sync_up(struct net_device *dev, unsigned int nh_flags)
 
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 
-/*
- * The algorithm is suboptimal, but it provides really
- * fair weighted route distribution.
- */
-void fib_select_multipath(struct fib_result *res)
+void fib_select_multipath(struct fib_result *res, int hash)
 {
        struct fib_info *fi = res->fi;
-       struct in_device *in_dev;
-       int w;
-
-       spin_lock_bh(&fib_multipath_lock);
-       if (fi->fib_power <= 0) {
-               int power = 0;
-               change_nexthops(fi) {
-                       in_dev = __in_dev_get_rcu(nexthop_nh->nh_dev);
-                       if (nexthop_nh->nh_flags & RTNH_F_DEAD)
-                               continue;
-                       if (in_dev &&
-                           IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
-                           nexthop_nh->nh_flags & RTNH_F_LINKDOWN)
-                               continue;
-                       power += nexthop_nh->nh_weight;
-                       nexthop_nh->nh_power = nexthop_nh->nh_weight;
-               } endfor_nexthops(fi);
-               fi->fib_power = power;
-               if (power <= 0) {
-                       spin_unlock_bh(&fib_multipath_lock);
-                       /* Race condition: route has just become dead. */
-                       res->nh_sel = 0;
-                       return;
-               }
-       }
-
 
-       /* w should be random number [0..fi->fib_power-1],
-        * it is pretty bad approximation.
-        */
-
-       w = jiffies % fi->fib_power;
+       for_nexthops(fi) {
+               if (hash > atomic_read(&nh->nh_upper_bound))
+                       continue;
 
-       change_nexthops(fi) {
-               if (!(nexthop_nh->nh_flags & RTNH_F_DEAD) &&
-                   nexthop_nh->nh_power) {
-                       w -= nexthop_nh->nh_power;
-                       if (w <= 0) {
-                               nexthop_nh->nh_power--;
-                               fi->fib_power--;
-                               res->nh_sel = nhsel;
-                               spin_unlock_bh(&fib_multipath_lock);
-                               return;
-                       }
-               }
+               res->nh_sel = nhsel;
+               return;
        } endfor_nexthops(fi);
 
        /* Race condition: route has just become dead. */
        res->nh_sel = 0;
-       spin_unlock_bh(&fib_multipath_lock);
 }
 #endif
+
+void fib_select_path(struct net *net, struct fib_result *res,
+                    struct flowi4 *fl4, int mp_hash)
+{
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+       if (res->fi->fib_nhs > 1 && fl4->flowi4_oif == 0) {
+               if (mp_hash < 0)
+                       mp_hash = get_hash_from_flowi4(fl4) >> 1;
+
+               fib_select_multipath(res, mp_hash);
+       }
+       else
+#endif
+       if (!res->prefixlen &&
+           res->table->tb_num_default > 1 &&
+           res->type == RTN_UNICAST && !fl4->flowi4_oif)
+               fib_select_default(fl4, res);
+
+       if (!fl4->saddr)
+               fl4->saddr = FIB_RES_PREFSRC(net, *res);
+}
+EXPORT_SYMBOL_GPL(fib_select_path);
index 6c2af797f2f92b93cf4ea28d1d5deee4e725f757..744e5936c10d7ec555d1ca621f5bd4be57f1c72b 100644 (file)
@@ -1569,7 +1569,7 @@ static struct key_vector *leaf_walk_rcu(struct key_vector **tn, t_key key)
        do {
                /* record parent and next child index */
                pn = n;
-               cindex = key ? get_index(key, pn) : 0;
+               cindex = (key > pn->key) ? get_index(key, pn) : 0;
 
                if (cindex >> pn->bits)
                        break;
index 5aa46d4b44efb99702ccd89005528f20ae422a0e..5a8ee3282550880a7749b8d6a9086dc413661519 100644 (file)
@@ -36,7 +36,8 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
                                  SKB_GSO_TCP_ECN |
                                  SKB_GSO_GRE |
                                  SKB_GSO_GRE_CSUM |
-                                 SKB_GSO_IPIP)))
+                                 SKB_GSO_IPIP |
+                                 SKB_GSO_SIT)))
                goto out;
 
        if (!skb->encapsulation)
index e5eb8ac4089d88cc2875d8f991da51899360698a..36e26977c9088c1dbd09cd13e9a5e2c43369fe31 100644 (file)
@@ -96,7 +96,7 @@
 #include <net/xfrm.h>
 #include <net/inet_common.h>
 #include <net/ip_fib.h>
-#include <net/vrf.h>
+#include <net/l3mdev.h>
 
 /*
  *     Build xmit assembly blocks
@@ -309,7 +309,7 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
 
        rc = false;
        if (icmp_global_allow()) {
-               int vif = vrf_master_ifindex(dst->dev);
+               int vif = l3mdev_master_ifindex(dst->dev);
                struct inet_peer *peer;
 
                peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr, vif, 1);
@@ -427,7 +427,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        fl4.flowi4_mark = mark;
        fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
        fl4.flowi4_proto = IPPROTO_ICMP;
-       fl4.flowi4_oif = vrf_master_ifindex(skb->dev);
+       fl4.flowi4_oif = l3mdev_master_ifindex(skb->dev);
        security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
        rt = ip_route_output_key(net, &fl4);
        if (IS_ERR(rt))
@@ -440,6 +440,22 @@ out_unlock:
        icmp_xmit_unlock(sk);
 }
 
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+
+/* Source and destination is swapped. See ip_multipath_icmp_hash */
+static int icmp_multipath_hash_skb(const struct sk_buff *skb)
+{
+       const struct iphdr *iph = ip_hdr(skb);
+
+       return fib_multipath_hash(iph->daddr, iph->saddr);
+}
+
+#else
+
+#define icmp_multipath_hash_skb(skb) (-1)
+
+#endif
+
 static struct rtable *icmp_route_lookup(struct net *net,
                                        struct flowi4 *fl4,
                                        struct sk_buff *skb_in,
@@ -461,10 +477,11 @@ static struct rtable *icmp_route_lookup(struct net *net,
        fl4->flowi4_proto = IPPROTO_ICMP;
        fl4->fl4_icmp_type = type;
        fl4->fl4_icmp_code = code;
-       fl4->flowi4_oif = vrf_master_ifindex(skb_in->dev);
+       fl4->flowi4_oif = l3mdev_master_ifindex(skb_in->dev);
 
        security_skb_classify_flow(skb_in, flowi4_to_flowi(fl4));
-       rt = __ip_route_output_key(net, fl4);
+       rt = __ip_route_output_key_hash(net, fl4,
+                                       icmp_multipath_hash_skb(skb_in));
        if (IS_ERR(rt))
                return rt;
 
index d38b8b61eaeef2e79faa28091dafbee715f7f059..64aaf3522a59f30672689f7309987606773ccb0c 100644 (file)
@@ -397,7 +397,7 @@ static int igmpv3_sendpack(struct sk_buff *skb)
 
        pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen);
 
-       return ip_local_out(skb);
+       return ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb);
 }
 
 static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel)
@@ -739,7 +739,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
        ih->group = group;
        ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr));
 
-       return ip_local_out(skb);
+       return ip_local_out(net, skb->sk, skb);
 }
 
 static void igmp_gq_timer_expire(unsigned long data)
@@ -2569,7 +2569,7 @@ void ip_mc_drop_socket(struct sock *sk)
 }
 
 /* called with rcu_read_lock() */
-int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 proto)
+int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u8 proto)
 {
        struct ip_mc_list *im;
        struct ip_mc_list __rcu **mc_hash;
index 61b45a17fc738e17146a5e1f1cf97774f338179e..1feb15f23de8c4f673fd0fe713df2dd9195995cf 100644 (file)
@@ -330,14 +330,12 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
                if (error)
                        goto out_err;
        }
-       req = reqsk_queue_remove(queue);
+       req = reqsk_queue_remove(queue, sk);
        newsk = req->sk;
 
-       sk_acceptq_removed(sk);
        if (sk->sk_protocol == IPPROTO_TCP &&
-           tcp_rsk(req)->tfo_listener &&
-           queue->fastopenq) {
-               spin_lock_bh(&queue->fastopenq->lock);
+           tcp_rsk(req)->tfo_listener) {
+               spin_lock_bh(&queue->fastopenq.lock);
                if (tcp_rsk(req)->tfo_listener) {
                        /* We are still waiting for the final ACK from 3WHS
                         * so can't free req now. Instead, we set req->sk to
@@ -348,7 +346,7 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
                        req->sk = NULL;
                        req = NULL;
                }
-               spin_unlock_bh(&queue->fastopenq->lock);
+               spin_unlock_bh(&queue->fastopenq.lock);
        }
 out:
        release_sock(sk);
@@ -408,7 +406,7 @@ void inet_csk_reset_keepalive_timer(struct sock *sk, unsigned long len)
 }
 EXPORT_SYMBOL(inet_csk_reset_keepalive_timer);
 
-struct dst_entry *inet_csk_route_req(struct sock *sk,
+struct dst_entry *inet_csk_route_req(const struct sock *sk,
                                     struct flowi4 *fl4,
                                     const struct request_sock *req)
 {
@@ -439,7 +437,7 @@ no_route:
 }
 EXPORT_SYMBOL_GPL(inet_csk_route_req);
 
-struct dst_entry *inet_csk_route_child_sock(struct sock *sk,
+struct dst_entry *inet_csk_route_child_sock(const struct sock *sk,
                                            struct sock *newsk,
                                            const struct request_sock *req)
 {
@@ -478,65 +476,12 @@ no_route:
 }
 EXPORT_SYMBOL_GPL(inet_csk_route_child_sock);
 
-static inline u32 inet_synq_hash(const __be32 raddr, const __be16 rport,
-                                const u32 rnd, const u32 synq_hsize)
-{
-       return jhash_2words((__force u32)raddr, (__force u32)rport, rnd) & (synq_hsize - 1);
-}
-
 #if IS_ENABLED(CONFIG_IPV6)
 #define AF_INET_FAMILY(fam) ((fam) == AF_INET)
 #else
 #define AF_INET_FAMILY(fam) true
 #endif
 
-/* Note: this is temporary :
- * req sock will no longer be in listener hash table
-*/
-struct request_sock *inet_csk_search_req(struct sock *sk,
-                                        const __be16 rport,
-                                        const __be32 raddr,
-                                        const __be32 laddr)
-{
-       struct inet_connection_sock *icsk = inet_csk(sk);
-       struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
-       struct request_sock *req;
-       u32 hash = inet_synq_hash(raddr, rport, lopt->hash_rnd,
-                                 lopt->nr_table_entries);
-
-       spin_lock(&icsk->icsk_accept_queue.syn_wait_lock);
-       for (req = lopt->syn_table[hash]; req != NULL; req = req->dl_next) {
-               const struct inet_request_sock *ireq = inet_rsk(req);
-
-               if (ireq->ir_rmt_port == rport &&
-                   ireq->ir_rmt_addr == raddr &&
-                   ireq->ir_loc_addr == laddr &&
-                   AF_INET_FAMILY(req->rsk_ops->family)) {
-                       atomic_inc(&req->rsk_refcnt);
-                       WARN_ON(req->sk);
-                       break;
-               }
-       }
-       spin_unlock(&icsk->icsk_accept_queue.syn_wait_lock);
-
-       return req;
-}
-EXPORT_SYMBOL_GPL(inet_csk_search_req);
-
-void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
-                                  unsigned long timeout)
-{
-       struct inet_connection_sock *icsk = inet_csk(sk);
-       struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
-       const u32 h = inet_synq_hash(inet_rsk(req)->ir_rmt_addr,
-                                    inet_rsk(req)->ir_rmt_port,
-                                    lopt->hash_rnd, lopt->nr_table_entries);
-
-       reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, timeout);
-       inet_csk_reqsk_queue_added(sk, timeout);
-}
-EXPORT_SYMBOL_GPL(inet_csk_reqsk_queue_hash_add);
-
 /* Only thing we need from tcp.h */
 extern int sysctl_tcp_synack_retries;
 
@@ -563,7 +508,7 @@ static inline void syn_ack_recalc(struct request_sock *req, const int thresh,
                  req->num_timeout >= rskq_defer_accept - 1;
 }
 
-int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req)
+int inet_rtx_syn_ack(const struct sock *parent, struct request_sock *req)
 {
        int err = req->rsk_ops->rtx_syn_ack(parent, req);
 
@@ -573,27 +518,20 @@ int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req)
 }
 EXPORT_SYMBOL(inet_rtx_syn_ack);
 
-/* return true if req was found in the syn_table[] */
+/* return true if req was found in the ehash table */
 static bool reqsk_queue_unlink(struct request_sock_queue *queue,
                               struct request_sock *req)
 {
-       struct request_sock **prev;
-       struct listen_sock *lopt;
+       struct inet_hashinfo *hashinfo = req_to_sk(req)->sk_prot->h.hashinfo;
        bool found = false;
 
-       spin_lock(&queue->syn_wait_lock);
-       lopt = queue->listen_opt;
-       if (lopt) {
-               for (prev = &lopt->syn_table[req->rsk_hash]; *prev != NULL;
-                    prev = &(*prev)->dl_next) {
-                       if (*prev == req) {
-                               *prev = req->dl_next;
-                               found = true;
-                               break;
-                       }
-               }
+       if (sk_hashed(req_to_sk(req))) {
+               spinlock_t *lock = inet_ehash_lockp(hashinfo, req->rsk_hash);
+
+               spin_lock(lock);
+               found = __sk_nulls_del_node_init_rcu(req_to_sk(req));
+               spin_unlock(lock);
        }
-       spin_unlock(&queue->syn_wait_lock);
        if (timer_pending(&req->rsk_timer) && del_timer_sync(&req->rsk_timer))
                reqsk_put(req);
        return found;
@@ -608,21 +546,25 @@ void inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req)
 }
 EXPORT_SYMBOL(inet_csk_reqsk_queue_drop);
 
+void inet_csk_reqsk_queue_drop_and_put(struct sock *sk, struct request_sock *req)
+{
+       inet_csk_reqsk_queue_drop(sk, req);
+       reqsk_put(req);
+}
+EXPORT_SYMBOL(inet_csk_reqsk_queue_drop_and_put);
+
 static void reqsk_timer_handler(unsigned long data)
 {
        struct request_sock *req = (struct request_sock *)data;
        struct sock *sk_listener = req->rsk_listener;
        struct inet_connection_sock *icsk = inet_csk(sk_listener);
        struct request_sock_queue *queue = &icsk->icsk_accept_queue;
-       struct listen_sock *lopt = queue->listen_opt;
        int qlen, expire = 0, resend = 0;
        int max_retries, thresh;
        u8 defer_accept;
 
-       if (sk_listener->sk_state != TCP_LISTEN || !lopt) {
-               reqsk_put(req);
-               return;
-       }
+       if (sk_listener->sk_state != TCP_LISTEN)
+               goto drop;
 
        max_retries = icsk->icsk_syn_retries ? : sysctl_tcp_synack_retries;
        thresh = max_retries;
@@ -643,9 +585,9 @@ static void reqsk_timer_handler(unsigned long data)
         * embrions; and abort old ones without pity, if old
         * ones are about to clog our table.
         */
-       qlen = listen_sock_qlen(lopt);
-       if (qlen >> (lopt->max_qlen_log - 1)) {
-               int young = listen_sock_young(lopt) << 1;
+       qlen = reqsk_queue_len(queue);
+       if ((qlen << 1) > max(8U, sk_listener->sk_max_ack_backlog)) {
+               int young = reqsk_queue_len_young(queue) << 1;
 
                while (thresh > 2) {
                        if (qlen < young)
@@ -667,41 +609,40 @@ static void reqsk_timer_handler(unsigned long data)
                unsigned long timeo;
 
                if (req->num_timeout++ == 0)
-                       atomic_inc(&lopt->young_dec);
+                       atomic_dec(&queue->young);
                timeo = min(TCP_TIMEOUT_INIT << req->num_timeout, TCP_RTO_MAX);
                mod_timer_pinned(&req->rsk_timer, jiffies + timeo);
                return;
        }
-       inet_csk_reqsk_queue_drop(sk_listener, req);
-       reqsk_put(req);
+drop:
+       inet_csk_reqsk_queue_drop_and_put(sk_listener, req);
 }
 
-void reqsk_queue_hash_req(struct request_sock_queue *queue,
-                         u32 hash, struct request_sock *req,
-                         unsigned long timeout)
+static void reqsk_queue_hash_req(struct request_sock *req,
+                                unsigned long timeout)
 {
-       struct listen_sock *lopt = queue->listen_opt;
-
        req->num_retrans = 0;
        req->num_timeout = 0;
        req->sk = NULL;
 
        setup_timer(&req->rsk_timer, reqsk_timer_handler, (unsigned long)req);
        mod_timer_pinned(&req->rsk_timer, jiffies + timeout);
-       req->rsk_hash = hash;
 
+       inet_ehash_insert(req_to_sk(req), NULL);
        /* before letting lookups find us, make sure all req fields
         * are committed to memory and refcnt initialized.
         */
        smp_wmb();
-       atomic_set(&req->rsk_refcnt, 2);
+       atomic_set(&req->rsk_refcnt, 2 + 1);
+}
 
-       spin_lock(&queue->syn_wait_lock);
-       req->dl_next = lopt->syn_table[hash];
-       lopt->syn_table[hash] = req;
-       spin_unlock(&queue->syn_wait_lock);
+void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
+                                  unsigned long timeout)
+{
+       reqsk_queue_hash_req(req, timeout);
+       inet_csk_reqsk_queue_added(sk);
 }
-EXPORT_SYMBOL(reqsk_queue_hash_req);
+EXPORT_SYMBOL_GPL(inet_csk_reqsk_queue_hash_add);
 
 /**
  *     inet_csk_clone_lock - clone an inet socket, and lock its clone
@@ -792,16 +733,14 @@ void inet_csk_prepare_forced_close(struct sock *sk)
 }
 EXPORT_SYMBOL(inet_csk_prepare_forced_close);
 
-int inet_csk_listen_start(struct sock *sk, const int nr_table_entries)
+int inet_csk_listen_start(struct sock *sk, int backlog)
 {
-       struct inet_sock *inet = inet_sk(sk);
        struct inet_connection_sock *icsk = inet_csk(sk);
-       int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries);
+       struct inet_sock *inet = inet_sk(sk);
 
-       if (rc != 0)
-               return rc;
+       reqsk_queue_alloc(&icsk->icsk_accept_queue);
 
-       sk->sk_max_ack_backlog = 0;
+       sk->sk_max_ack_backlog = backlog;
        sk->sk_ack_backlog = 0;
        inet_csk_delack_init(sk);
 
@@ -821,11 +760,76 @@ int inet_csk_listen_start(struct sock *sk, const int nr_table_entries)
        }
 
        sk->sk_state = TCP_CLOSE;
-       __reqsk_queue_destroy(&icsk->icsk_accept_queue);
        return -EADDRINUSE;
 }
 EXPORT_SYMBOL_GPL(inet_csk_listen_start);
 
+static void inet_child_forget(struct sock *sk, struct request_sock *req,
+                             struct sock *child)
+{
+       sk->sk_prot->disconnect(child, O_NONBLOCK);
+
+       sock_orphan(child);
+
+       percpu_counter_inc(sk->sk_prot->orphan_count);
+
+       if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(req)->tfo_listener) {
+               BUG_ON(tcp_sk(child)->fastopen_rsk != req);
+               BUG_ON(sk != req->rsk_listener);
+
+               /* Paranoid, to prevent race condition if
+                * an inbound pkt destined for child is
+                * blocked by sock lock in tcp_v4_rcv().
+                * Also to satisfy an assertion in
+                * tcp_v4_destroy_sock().
+                */
+               tcp_sk(child)->fastopen_rsk = NULL;
+       }
+       inet_csk_destroy_sock(child);
+       reqsk_put(req);
+}
+
+void inet_csk_reqsk_queue_add(struct sock *sk, struct request_sock *req,
+                             struct sock *child)
+{
+       struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
+
+       spin_lock(&queue->rskq_lock);
+       if (unlikely(sk->sk_state != TCP_LISTEN)) {
+               inet_child_forget(sk, req, child);
+       } else {
+               req->sk = child;
+               req->dl_next = NULL;
+               if (queue->rskq_accept_head == NULL)
+                       queue->rskq_accept_head = req;
+               else
+                       queue->rskq_accept_tail->dl_next = req;
+               queue->rskq_accept_tail = req;
+               sk_acceptq_added(sk);
+       }
+       spin_unlock(&queue->rskq_lock);
+}
+EXPORT_SYMBOL(inet_csk_reqsk_queue_add);
+
+struct sock *inet_csk_complete_hashdance(struct sock *sk, struct sock *child,
+                                        struct request_sock *req, bool own_req)
+{
+       if (own_req) {
+               inet_csk_reqsk_queue_drop(sk, req);
+               reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req);
+               inet_csk_reqsk_queue_add(sk, req, child);
+               /* Warning: caller must not call reqsk_put(req);
+                * child stole last reference on it.
+                */
+               return child;
+       }
+       /* Too bad, another child took ownership of the request, undo. */
+       bh_unlock_sock(child);
+       sock_put(child);
+       return NULL;
+}
+EXPORT_SYMBOL(inet_csk_complete_hashdance);
+
 /*
  *     This routine closes sockets which have been at least partially
  *     opened, but not yet accepted.
@@ -834,11 +838,7 @@ void inet_csk_listen_stop(struct sock *sk)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct request_sock_queue *queue = &icsk->icsk_accept_queue;
-       struct request_sock *acc_req;
-       struct request_sock *req;
-
-       /* make all the listen_opt local to us */
-       acc_req = reqsk_queue_yank_acceptq(queue);
+       struct request_sock *next, *req;
 
        /* Following specs, it would be better either to send FIN
         * (and enter FIN-WAIT-1, it is normal close)
@@ -848,57 +848,34 @@ void inet_csk_listen_stop(struct sock *sk)
         * To be honest, we are not able to make either
         * of the variants now.                 --ANK
         */
-       reqsk_queue_destroy(queue);
-
-       while ((req = acc_req) != NULL) {
+       while ((req = reqsk_queue_remove(queue, sk)) != NULL) {
                struct sock *child = req->sk;
 
-               acc_req = req->dl_next;
-
                local_bh_disable();
                bh_lock_sock(child);
                WARN_ON(sock_owned_by_user(child));
                sock_hold(child);
 
-               sk->sk_prot->disconnect(child, O_NONBLOCK);
-
-               sock_orphan(child);
-
-               percpu_counter_inc(sk->sk_prot->orphan_count);
-
-               if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(req)->tfo_listener) {
-                       BUG_ON(tcp_sk(child)->fastopen_rsk != req);
-                       BUG_ON(sk != req->rsk_listener);
-
-                       /* Paranoid, to prevent race condition if
-                        * an inbound pkt destined for child is
-                        * blocked by sock lock in tcp_v4_rcv().
-                        * Also to satisfy an assertion in
-                        * tcp_v4_destroy_sock().
-                        */
-                       tcp_sk(child)->fastopen_rsk = NULL;
-               }
-               inet_csk_destroy_sock(child);
-
+               inet_child_forget(sk, req, child);
                bh_unlock_sock(child);
                local_bh_enable();
                sock_put(child);
 
-               sk_acceptq_removed(sk);
-               reqsk_put(req);
+               cond_resched();
        }
-       if (queue->fastopenq) {
+       if (queue->fastopenq.rskq_rst_head) {
                /* Free all the reqs queued in rskq_rst_head. */
-               spin_lock_bh(&queue->fastopenq->lock);
-               acc_req = queue->fastopenq->rskq_rst_head;
-               queue->fastopenq->rskq_rst_head = NULL;
-               spin_unlock_bh(&queue->fastopenq->lock);
-               while ((req = acc_req) != NULL) {
-                       acc_req = req->dl_next;
+               spin_lock_bh(&queue->fastopenq.lock);
+               req = queue->fastopenq.rskq_rst_head;
+               queue->fastopenq.rskq_rst_head = NULL;
+               spin_unlock_bh(&queue->fastopenq.lock);
+               while (req != NULL) {
+                       next = req->dl_next;
                        reqsk_put(req);
+                       req = next;
                }
        }
-       WARN_ON(sk->sk_ack_backlog);
+       WARN_ON_ONCE(sk->sk_ack_backlog);
 }
 EXPORT_SYMBOL_GPL(inet_csk_listen_stop);
 
index c3b1f3a0f4cf63df6c7e862f96e44c8653d3a476..ab9f8a66615d0872b586a0c2745196c58f6026be 100644 (file)
@@ -730,91 +730,21 @@ static void twsk_build_assert(void)
 #endif
 }
 
-static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
-                              struct netlink_callback *cb,
-                              const struct inet_diag_req_v2 *r,
-                              const struct nlattr *bc)
-{
-       struct inet_connection_sock *icsk = inet_csk(sk);
-       struct inet_sock *inet = inet_sk(sk);
-       struct inet_diag_entry entry;
-       int j, s_j, reqnum, s_reqnum;
-       struct listen_sock *lopt;
-       int err = 0;
-
-       s_j = cb->args[3];
-       s_reqnum = cb->args[4];
-
-       if (s_j > 0)
-               s_j--;
-
-       entry.family = sk->sk_family;
-
-       spin_lock(&icsk->icsk_accept_queue.syn_wait_lock);
-
-       lopt = icsk->icsk_accept_queue.listen_opt;
-       if (!lopt || !listen_sock_qlen(lopt))
-               goto out;
-
-       if (bc) {
-               entry.sport = inet->inet_num;
-               entry.userlocks = sk->sk_userlocks;
-       }
-
-       for (j = s_j; j < lopt->nr_table_entries; j++) {
-               struct request_sock *req, *head = lopt->syn_table[j];
-
-               reqnum = 0;
-               for (req = head; req; reqnum++, req = req->dl_next) {
-                       struct inet_request_sock *ireq = inet_rsk(req);
-
-                       if (reqnum < s_reqnum)
-                               continue;
-                       if (r->id.idiag_dport != ireq->ir_rmt_port &&
-                           r->id.idiag_dport)
-                               continue;
-
-                       if (bc) {
-                               /* Note: entry.sport and entry.userlocks are already set */
-                               entry_fill_addrs(&entry, req_to_sk(req));
-                               entry.dport = ntohs(ireq->ir_rmt_port);
-
-                               if (!inet_diag_bc_run(bc, &entry))
-                                       continue;
-                       }
-
-                       err = inet_req_diag_fill(req_to_sk(req), skb,
-                                                NETLINK_CB(cb->skb).portid,
-                                                cb->nlh->nlmsg_seq,
-                                                NLM_F_MULTI, cb->nlh);
-                       if (err < 0) {
-                               cb->args[3] = j + 1;
-                               cb->args[4] = reqnum;
-                               goto out;
-                       }
-               }
-
-               s_reqnum = 0;
-       }
-
-out:
-       spin_unlock(&icsk->icsk_accept_queue.syn_wait_lock);
-
-       return err;
-}
-
 void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
                         struct netlink_callback *cb,
                         const struct inet_diag_req_v2 *r, struct nlattr *bc)
 {
        struct net *net = sock_net(skb->sk);
        int i, num, s_i, s_num;
+       u32 idiag_states = r->idiag_states;
 
+       if (idiag_states & TCPF_SYN_RECV)
+               idiag_states |= TCPF_NEW_SYN_RECV;
        s_i = cb->args[1];
        s_num = num = cb->args[2];
 
        if (cb->args[0] == 0) {
-               if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
+               if (!(idiag_states & TCPF_LISTEN))
                        goto skip_listen_ht;
 
                for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
@@ -844,21 +774,11 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
                                    r->id.idiag_sport)
                                        goto next_listen;
 
-                               if (!(r->idiag_states & TCPF_LISTEN) ||
-                                   r->id.idiag_dport ||
+                               if (r->id.idiag_dport ||
                                    cb->args[3] > 0)
-                                       goto syn_recv;
-
-                               if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
-                                       spin_unlock_bh(&ilb->lock);
-                                       goto done;
-                               }
-
-syn_recv:
-                               if (!(r->idiag_states & TCPF_SYN_RECV))
                                        goto next_listen;
 
-                               if (inet_diag_dump_reqs(skb, sk, cb, r, bc) < 0) {
+                               if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
                                        spin_unlock_bh(&ilb->lock);
                                        goto done;
                                }
@@ -879,7 +799,7 @@ skip_listen_ht:
                s_i = num = s_num = 0;
        }
 
-       if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
+       if (!(idiag_states & ~TCPF_LISTEN))
                goto out;
 
        for (i = s_i; i <= hashinfo->ehash_mask; i++) {
@@ -906,7 +826,7 @@ skip_listen_ht:
                                goto next_normal;
                        state = (sk->sk_state == TCP_TIME_WAIT) ?
                                inet_twsk(sk)->tw_substate : sk->sk_state;
-                       if (!(r->idiag_states & (1 << state)))
+                       if (!(idiag_states & (1 << state)))
                                goto next_normal;
                        if (r->sdiag_family != AF_UNSPEC &&
                            sk->sk_family != r->sdiag_family)
index d0a7c0319e3d1b1b73f828717062b6fbbd3be27d..fe144dae7372dcfa3e0c5dc34f66ce9cfde2e7a3 100644 (file)
@@ -209,12 +209,6 @@ int inet_frags_init(struct inet_frags *f)
 }
 EXPORT_SYMBOL(inet_frags_init);
 
-void inet_frags_init_net(struct netns_frags *nf)
-{
-       init_frag_mem_limit(nf);
-}
-EXPORT_SYMBOL(inet_frags_init_net);
-
 void inet_frags_fini(struct inet_frags *f)
 {
        cancel_work_sync(&f->frags_work);
index 89120196a94934e3bb1f201eef9ad7f936ad828b..ccc5980797fcdb9ed3a1003db47e4e8cb7180279 100644 (file)
@@ -126,7 +126,7 @@ void inet_put_port(struct sock *sk)
 }
 EXPORT_SYMBOL(inet_put_port);
 
-int __inet_inherit_port(struct sock *sk, struct sock *child)
+int __inet_inherit_port(const struct sock *sk, struct sock *child)
 {
        struct inet_hashinfo *table = sk->sk_prot->h.hashinfo;
        unsigned short port = inet_sk(child)->inet_num;
@@ -137,6 +137,10 @@ int __inet_inherit_port(struct sock *sk, struct sock *child)
 
        spin_lock(&head->lock);
        tb = inet_csk(sk)->icsk_bind_hash;
+       if (unlikely(!tb)) {
+               spin_unlock(&head->lock);
+               return -ENOENT;
+       }
        if (tb->port != port) {
                /* NOTE: using tproxy and redirecting skbs to a proxy
                 * on a different listener port breaks the assumption
@@ -185,6 +189,8 @@ static inline int compute_score(struct sock *sk, struct net *net,
                                return -1;
                        score += 4;
                }
+               if (sk->sk_incoming_cpu == raw_smp_processor_id())
+                       score++;
        }
        return score;
 }
@@ -398,14 +404,18 @@ static u32 inet_sk_port_offset(const struct sock *sk)
                                          inet->inet_dport);
 }
 
-void __inet_hash_nolisten(struct sock *sk, struct sock *osk)
+/* insert a socket into ehash, and eventually remove another one
+ * (The another one can be a SYN_RECV or TIMEWAIT
+ */
+bool inet_ehash_insert(struct sock *sk, struct sock *osk)
 {
        struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
        struct hlist_nulls_head *list;
        struct inet_ehash_bucket *head;
        spinlock_t *lock;
+       bool ret = true;
 
-       WARN_ON(!sk_unhashed(sk));
+       WARN_ON_ONCE(!sk_unhashed(sk));
 
        sk->sk_hash = sk_ehashfn(sk);
        head = inet_ehash_bucket(hashinfo, sk->sk_hash);
@@ -413,24 +423,41 @@ void __inet_hash_nolisten(struct sock *sk, struct sock *osk)
        lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
 
        spin_lock(lock);
-       __sk_nulls_add_node_rcu(sk, list);
        if (osk) {
-               WARN_ON(sk->sk_hash != osk->sk_hash);
-               sk_nulls_del_node_init_rcu(osk);
+               WARN_ON_ONCE(sk->sk_hash != osk->sk_hash);
+               ret = sk_nulls_del_node_init_rcu(osk);
        }
+       if (ret)
+               __sk_nulls_add_node_rcu(sk, list);
        spin_unlock(lock);
-       sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+       return ret;
+}
+
+bool inet_ehash_nolisten(struct sock *sk, struct sock *osk)
+{
+       bool ok = inet_ehash_insert(sk, osk);
+
+       if (ok) {
+               sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+       } else {
+               percpu_counter_inc(sk->sk_prot->orphan_count);
+               sk->sk_state = TCP_CLOSE;
+               sock_set_flag(sk, SOCK_DEAD);
+               inet_csk_destroy_sock(sk);
+       }
+       return ok;
 }
-EXPORT_SYMBOL_GPL(__inet_hash_nolisten);
+EXPORT_SYMBOL_GPL(inet_ehash_nolisten);
 
 void __inet_hash(struct sock *sk, struct sock *osk)
 {
        struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
        struct inet_listen_hashbucket *ilb;
 
-       if (sk->sk_state != TCP_LISTEN)
-               return __inet_hash_nolisten(sk, osk);
-
+       if (sk->sk_state != TCP_LISTEN) {
+               inet_ehash_nolisten(sk, osk);
+               return;
+       }
        WARN_ON(!sk_unhashed(sk));
        ilb = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
 
@@ -551,7 +578,7 @@ ok:
                inet_bind_hash(sk, tb, port);
                if (sk_unhashed(sk)) {
                        inet_sk(sk)->inet_sport = htons(port);
-                       __inet_hash_nolisten(sk, (struct sock *)tw);
+                       inet_ehash_nolisten(sk, (struct sock *)tw);
                }
                if (tw)
                        inet_twsk_bind_unhash(tw, hinfo);
@@ -568,7 +595,7 @@ ok:
        tb  = inet_csk(sk)->icsk_bind_hash;
        spin_lock_bh(&head->lock);
        if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
-               __inet_hash_nolisten(sk, NULL);
+               inet_ehash_nolisten(sk, NULL);
                spin_unlock_bh(&head->lock);
                return 0;
        } else {
index 2d3aa408fbdca19230224269ebbcc7124dc7f5bb..da0d7ce85844a9ed0732883ac363ab2de62f3a9b 100644 (file)
@@ -61,18 +61,18 @@ static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
 }
 
 
-static int ip_forward_finish(struct sock *sk, struct sk_buff *skb)
+static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct ip_options *opt  = &(IPCB(skb)->opt);
 
-       IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
-       IP_ADD_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTOCTETS, skb->len);
+       IP_INC_STATS_BH(net, IPSTATS_MIB_OUTFORWDATAGRAMS);
+       IP_ADD_STATS_BH(net, IPSTATS_MIB_OUTOCTETS, skb->len);
 
        if (unlikely(opt->optlen))
                ip_forward_options(skb);
 
        skb_sender_cpu_clear(skb);
-       return dst_output_sk(sk, skb);
+       return dst_output(net, sk, skb);
 }
 
 int ip_forward(struct sk_buff *skb)
@@ -81,6 +81,7 @@ int ip_forward(struct sk_buff *skb)
        struct iphdr *iph;      /* Our header */
        struct rtable *rt;      /* Route we use */
        struct ip_options *opt  = &(IPCB(skb)->opt);
+       struct net *net;
 
        /* that should never happen */
        if (skb->pkt_type != PACKET_HOST)
@@ -99,6 +100,7 @@ int ip_forward(struct sk_buff *skb)
                return NET_RX_SUCCESS;
 
        skb_forward_csum(skb);
+       net = dev_net(skb->dev);
 
        /*
         *      According to the RFC, we must first decrease the TTL field. If
@@ -119,7 +121,7 @@ int ip_forward(struct sk_buff *skb)
        IPCB(skb)->flags |= IPSKB_FORWARDED;
        mtu = ip_dst_mtu_maybe_forward(&rt->dst, true);
        if (ip_exceeds_mtu(skb, mtu)) {
-               IP_INC_STATS(dev_net(rt->dst.dev), IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
                          htonl(mtu));
                goto drop;
@@ -143,8 +145,9 @@ int ip_forward(struct sk_buff *skb)
 
        skb->priority = rt_tos2priority(iph->tos);
 
-       return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, NULL, skb,
-                      skb->dev, rt->dst.dev, ip_forward_finish);
+       return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,
+                      net, NULL, skb, skb->dev, rt->dst.dev,
+                      ip_forward_finish);
 
 sr_failed:
        /*
@@ -155,7 +158,7 @@ sr_failed:
 
 too_many_hops:
        /* Tell the sender its packet died... */
-       IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_INHDRERRORS);
+       IP_INC_STATS_BH(net, IPSTATS_MIB_INHDRERRORS);
        icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
 drop:
        kfree_skb(skb);
index fa7f15305f9a2c1fa2f25d98047a161b6011ead1..1fe55ae817818abf9f10fa4c06a222476d3d3c45 100644 (file)
@@ -48,7 +48,7 @@
 #include <linux/inet.h>
 #include <linux/netfilter_ipv4.h>
 #include <net/inet_ecn.h>
-#include <net/vrf.h>
+#include <net/l3mdev.h>
 
 /* NOTE. Logic of IP defragmentation is parallel to corresponding IPv6
  * code now. If you change something here, _PLEASE_ update ipv6/reassembly.c
@@ -78,7 +78,7 @@ struct ipq {
        u8              ecn; /* RFC3168 support */
        u16             max_df_size; /* largest frag with DF set seen */
        int             iif;
-       int             vif;   /* VRF device index */
+       int             vif;   /* L3 master device index */
        unsigned int    rid;
        struct inet_peer *peer;
 };
@@ -654,11 +654,10 @@ out_fail:
 }
 
 /* Process an incoming IP datagram fragment. */
-int ip_defrag(struct sk_buff *skb, u32 user)
+int ip_defrag(struct net *net, struct sk_buff *skb, u32 user)
 {
        struct net_device *dev = skb->dev ? : skb_dst(skb)->dev;
-       int vif = vrf_master_ifindex_rcu(dev);
-       struct net *net = dev_net(dev);
+       int vif = l3mdev_master_ifindex_rcu(dev);
        struct ipq *qp;
 
        IP_INC_STATS_BH(net, IPSTATS_MIB_REASMREQDS);
@@ -683,7 +682,7 @@ int ip_defrag(struct sk_buff *skb, u32 user)
 }
 EXPORT_SYMBOL(ip_defrag);
 
-struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user)
+struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user)
 {
        struct iphdr iph;
        int netoff;
@@ -712,7 +711,7 @@ struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user)
                        if (pskb_trim_rcsum(skb, netoff + len))
                                return skb;
                        memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
-                       if (ip_defrag(skb, user))
+                       if (ip_defrag(net, skb, user))
                                return NULL;
                        skb_clear_hash(skb);
                }
@@ -840,6 +839,8 @@ static void __init ip4_frags_ctl_register(void)
 
 static int __net_init ipv4_frags_init_net(struct net *net)
 {
+       int res;
+
        /* Fragment cache limits.
         *
         * The fragment memory accounting code, (tries to) account for
@@ -863,9 +864,13 @@ static int __net_init ipv4_frags_init_net(struct net *net)
         */
        net->ipv4.frags.timeout = IP_FRAG_TIME;
 
-       inet_frags_init_net(&net->ipv4.frags);
-
-       return ip4_frags_ns_ctl_register(net);
+       res = inet_frags_init_net(&net->ipv4.frags);
+       if (res)
+               return res;
+       res = ip4_frags_ns_ctl_register(net);
+       if (res)
+               inet_frags_uninit_net(&net->ipv4.frags);
+       return res;
 }
 
 static void __net_exit ipv4_frags_exit_net(struct net *net)
index bd0679d90519b170dc98369e9b438e4c31b152b9..614521437e30159c5234e9b24fcc41bad7ea6c6b 100644 (file)
@@ -498,10 +498,26 @@ static struct sk_buff *gre_handle_offloads(struct sk_buff *skb,
                                        csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
 }
 
+static struct rtable *gre_get_rt(struct sk_buff *skb,
+                                struct net_device *dev,
+                                struct flowi4 *fl,
+                                const struct ip_tunnel_key *key)
+{
+       struct net *net = dev_net(dev);
+
+       memset(fl, 0, sizeof(*fl));
+       fl->daddr = key->u.ipv4.dst;
+       fl->saddr = key->u.ipv4.src;
+       fl->flowi4_tos = RT_TOS(key->tos);
+       fl->flowi4_mark = skb->mark;
+       fl->flowi4_proto = IPPROTO_GRE;
+
+       return ip_route_output_key(net, fl);
+}
+
 static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct ip_tunnel_info *tun_info;
-       struct net *net = dev_net(dev);
        const struct ip_tunnel_key *key;
        struct flowi4 fl;
        struct rtable *rt;
@@ -516,14 +532,7 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev)
                goto err_free_skb;
 
        key = &tun_info->key;
-       memset(&fl, 0, sizeof(fl));
-       fl.daddr = key->u.ipv4.dst;
-       fl.saddr = key->u.ipv4.src;
-       fl.flowi4_tos = RT_TOS(key->tos);
-       fl.flowi4_mark = skb->mark;
-       fl.flowi4_proto = IPPROTO_GRE;
-
-       rt = ip_route_output_key(net, &fl);
+       rt = gre_get_rt(skb, dev, &fl, key);
        if (IS_ERR(rt))
                goto err_free_skb;
 
@@ -566,6 +575,24 @@ err_free_skb:
        dev->stats.tx_dropped++;
 }
 
+static int gre_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
+{
+       struct ip_tunnel_info *info = skb_tunnel_info(skb);
+       struct rtable *rt;
+       struct flowi4 fl4;
+
+       if (ip_tunnel_info_af(info) != AF_INET)
+               return -EINVAL;
+
+       rt = gre_get_rt(skb, dev, &fl4, &info->key);
+       if (IS_ERR(rt))
+               return PTR_ERR(rt);
+
+       ip_rt_put(rt);
+       info->key.u.ipv4.src = fl4.saddr;
+       return 0;
+}
+
 static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
                              struct net_device *dev)
 {
@@ -1023,6 +1050,7 @@ static const struct net_device_ops gre_tap_netdev_ops = {
        .ndo_change_mtu         = ip_tunnel_change_mtu,
        .ndo_get_stats64        = ip_tunnel_get_stats64,
        .ndo_get_iflink         = ip_tunnel_get_iflink,
+       .ndo_fill_metadata_dst  = gre_fill_metadata_dst,
 };
 
 static void ipgre_tap_setup(struct net_device *dev)
index f4fc8a77aaa79dcb5156bfb2de84efdf24808713..b1209b63381f6f9ae81cf258ad3724e1b6a6b9d8 100644 (file)
@@ -157,6 +157,7 @@ bool ip_call_ra_chain(struct sk_buff *skb)
        u8 protocol = ip_hdr(skb)->protocol;
        struct sock *last = NULL;
        struct net_device *dev = skb->dev;
+       struct net *net = dev_net(dev);
 
        for (ra = rcu_dereference(ip_ra_chain); ra; ra = rcu_dereference(ra->next)) {
                struct sock *sk = ra->sk;
@@ -167,9 +168,9 @@ bool ip_call_ra_chain(struct sk_buff *skb)
                if (sk && inet_sk(sk)->inet_num == protocol &&
                    (!sk->sk_bound_dev_if ||
                     sk->sk_bound_dev_if == dev->ifindex) &&
-                   net_eq(sock_net(sk), dev_net(dev))) {
+                   net_eq(sock_net(sk), net)) {
                        if (ip_is_fragment(ip_hdr(skb))) {
-                               if (ip_defrag(skb, IP_DEFRAG_CALL_RA_CHAIN))
+                               if (ip_defrag(net, skb, IP_DEFRAG_CALL_RA_CHAIN))
                                        return true;
                        }
                        if (last) {
@@ -188,10 +189,8 @@ bool ip_call_ra_chain(struct sk_buff *skb)
        return false;
 }
 
-static int ip_local_deliver_finish(struct sock *sk, struct sk_buff *skb)
+static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-       struct net *net = dev_net(skb->dev);
-
        __skb_pull(skb, skb_network_header_len(skb));
 
        rcu_read_lock();
@@ -248,14 +247,15 @@ int ip_local_deliver(struct sk_buff *skb)
        /*
         *      Reassemble IP fragments.
         */
+       struct net *net = dev_net(skb->dev);
 
        if (ip_is_fragment(ip_hdr(skb))) {
-               if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))
+               if (ip_defrag(net, skb, IP_DEFRAG_LOCAL_DELIVER))
                        return 0;
        }
 
-       return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, NULL, skb,
-                      skb->dev, NULL,
+       return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
+                      net, NULL, skb, skb->dev, NULL,
                       ip_local_deliver_finish);
 }
 
@@ -311,7 +311,7 @@ drop:
 int sysctl_ip_early_demux __read_mostly = 1;
 EXPORT_SYMBOL(sysctl_ip_early_demux);
 
-static int ip_rcv_finish(struct sock *sk, struct sk_buff *skb)
+static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        const struct iphdr *iph = ip_hdr(skb);
        struct rtable *rt;
@@ -337,8 +337,7 @@ static int ip_rcv_finish(struct sock *sk, struct sk_buff *skb)
                                               iph->tos, skb->dev);
                if (unlikely(err)) {
                        if (err == -EXDEV)
-                               NET_INC_STATS_BH(dev_net(skb->dev),
-                                                LINUX_MIB_IPRPFILTER);
+                               NET_INC_STATS_BH(net, LINUX_MIB_IPRPFILTER);
                        goto drop;
                }
        }
@@ -359,11 +358,9 @@ static int ip_rcv_finish(struct sock *sk, struct sk_buff *skb)
 
        rt = skb_rtable(skb);
        if (rt->rt_type == RTN_MULTICAST) {
-               IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INMCAST,
-                               skb->len);
+               IP_UPD_PO_STATS_BH(net, IPSTATS_MIB_INMCAST, skb->len);
        } else if (rt->rt_type == RTN_BROADCAST)
-               IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INBCAST,
-                               skb->len);
+               IP_UPD_PO_STATS_BH(net, IPSTATS_MIB_INBCAST, skb->len);
 
        return dst_input(skb);
 
@@ -378,6 +375,7 @@ drop:
 int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
 {
        const struct iphdr *iph;
+       struct net *net;
        u32 len;
 
        /* When the interface is in promisc. mode, drop all the crap
@@ -387,11 +385,12 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
                goto drop;
 
 
-       IP_UPD_PO_STATS_BH(dev_net(dev), IPSTATS_MIB_IN, skb->len);
+       net = dev_net(dev);
+       IP_UPD_PO_STATS_BH(net, IPSTATS_MIB_IN, skb->len);
 
        skb = skb_share_check(skb, GFP_ATOMIC);
        if (!skb) {
-               IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
+               IP_INC_STATS_BH(net, IPSTATS_MIB_INDISCARDS);
                goto out;
        }
 
@@ -417,7 +416,7 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
        BUILD_BUG_ON(IPSTATS_MIB_ECT1PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_1);
        BUILD_BUG_ON(IPSTATS_MIB_ECT0PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_0);
        BUILD_BUG_ON(IPSTATS_MIB_CEPKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_CE);
-       IP_ADD_STATS_BH(dev_net(dev),
+       IP_ADD_STATS_BH(net,
                        IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK),
                        max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));
 
@@ -431,7 +430,7 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
 
        len = ntohs(iph->tot_len);
        if (skb->len < len) {
-               IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);
+               IP_INC_STATS_BH(net, IPSTATS_MIB_INTRUNCATEDPKTS);
                goto drop;
        } else if (len < (iph->ihl*4))
                goto inhdr_error;
@@ -441,7 +440,7 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
         * Note this now means skb->len holds ntohs(iph->tot_len).
         */
        if (pskb_trim_rcsum(skb, len)) {
-               IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
+               IP_INC_STATS_BH(net, IPSTATS_MIB_INDISCARDS);
                goto drop;
        }
 
@@ -453,14 +452,14 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
        /* Must drop socket now because of tproxy. */
        skb_orphan(skb);
 
-       return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, NULL, skb,
-                      dev, NULL,
+       return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
+                      net, NULL, skb, dev, NULL,
                       ip_rcv_finish);
 
 csum_error:
-       IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_CSUMERRORS);
+       IP_INC_STATS_BH(net, IPSTATS_MIB_CSUMERRORS);
 inhdr_error:
-       IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);
+       IP_INC_STATS_BH(net, IPSTATS_MIB_INHDRERRORS);
 drop:
        kfree_skb(skb);
 out:
index 0138fada0951b17b175be0b215c248fc7224dc5a..4233cbe47052d83d856908185945635433b8613c 100644 (file)
 int sysctl_ip_default_ttl __read_mostly = IPDEFTTL;
 EXPORT_SYMBOL(sysctl_ip_default_ttl);
 
-static int ip_fragment(struct sock *sk, struct sk_buff *skb,
-                      unsigned int mtu,
-                      int (*output)(struct sock *, struct sk_buff *));
+static int
+ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
+           unsigned int mtu,
+           int (*output)(struct net *, struct sock *, struct sk_buff *));
 
 /* Generate a checksum for an outgoing IP datagram. */
 void ip_send_check(struct iphdr *iph)
@@ -95,32 +96,28 @@ void ip_send_check(struct iphdr *iph)
 }
 EXPORT_SYMBOL(ip_send_check);
 
-static int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb)
+int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct iphdr *iph = ip_hdr(skb);
 
        iph->tot_len = htons(skb->len);
        ip_send_check(iph);
-       return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb, NULL,
-                      skb_dst(skb)->dev, dst_output_sk);
+       return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
+                      net, sk, skb, NULL, skb_dst(skb)->dev,
+                      dst_output);
 }
 
-int __ip_local_out(struct sk_buff *skb)
-{
-       return __ip_local_out_sk(skb->sk, skb);
-}
-
-int ip_local_out_sk(struct sock *sk, struct sk_buff *skb)
+int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        int err;
 
-       err = __ip_local_out(skb);
+       err = __ip_local_out(net, sk, skb);
        if (likely(err == 1))
-               err = dst_output_sk(sk, skb);
+               err = dst_output(net, sk, skb);
 
        return err;
 }
-EXPORT_SYMBOL_GPL(ip_local_out_sk);
+EXPORT_SYMBOL_GPL(ip_local_out);
 
 static inline int ip_select_ttl(struct inet_sock *inet, struct dst_entry *dst)
 {
@@ -135,11 +132,12 @@ static inline int ip_select_ttl(struct inet_sock *inet, struct dst_entry *dst)
  *             Add an ip header to a skbuff and send it out.
  *
  */
-int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
+int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk,
                          __be32 saddr, __be32 daddr, struct ip_options_rcu *opt)
 {
        struct inet_sock *inet = inet_sk(sk);
        struct rtable *rt = skb_rtable(skb);
+       struct net *net = sock_net(sk);
        struct iphdr *iph;
 
        /* Build the IP header. */
@@ -149,15 +147,17 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
        iph->version  = 4;
        iph->ihl      = 5;
        iph->tos      = inet->tos;
-       if (ip_dont_fragment(sk, &rt->dst))
-               iph->frag_off = htons(IP_DF);
-       else
-               iph->frag_off = 0;
        iph->ttl      = ip_select_ttl(inet, &rt->dst);
        iph->daddr    = (opt && opt->opt.srr ? opt->opt.faddr : daddr);
        iph->saddr    = saddr;
        iph->protocol = sk->sk_protocol;
-       ip_select_ident(sock_net(sk), skb, sk);
+       if (ip_dont_fragment(sk, &rt->dst)) {
+               iph->frag_off = htons(IP_DF);
+               iph->id = 0;
+       } else {
+               iph->frag_off = 0;
+               __ip_select_ident(net, iph, 1);
+       }
 
        if (opt && opt->opt.optlen) {
                iph->ihl += opt->opt.optlen>>2;
@@ -168,11 +168,11 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
        skb->mark = sk->sk_mark;
 
        /* Send it out. */
-       return ip_local_out(skb);
+       return ip_local_out(net, skb->sk, skb);
 }
 EXPORT_SYMBOL_GPL(ip_build_and_send_pkt);
 
-static int ip_finish_output2(struct sock *sk, struct sk_buff *skb)
+static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct rtable *rt = (struct rtable *)dst;
@@ -182,9 +182,9 @@ static int ip_finish_output2(struct sock *sk, struct sk_buff *skb)
        u32 nexthop;
 
        if (rt->rt_type == RTN_MULTICAST) {
-               IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len);
+               IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTMCAST, skb->len);
        } else if (rt->rt_type == RTN_BROADCAST)
-               IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTBCAST, skb->len);
+               IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTBCAST, skb->len);
 
        /* Be paranoid, rather than too clever. */
        if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
@@ -220,8 +220,8 @@ static int ip_finish_output2(struct sock *sk, struct sk_buff *skb)
        return -EINVAL;
 }
 
-static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb,
-                               unsigned int mtu)
+static int ip_finish_output_gso(struct net *net, struct sock *sk,
+                               struct sk_buff *skb, unsigned int mtu)
 {
        netdev_features_t features;
        struct sk_buff *segs;
@@ -230,7 +230,7 @@ static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb,
        /* common case: locally created skb or seglen is <= mtu */
        if (((IPCB(skb)->flags & IPSKB_FORWARDED) == 0) ||
              skb_gso_network_seglen(skb) <= mtu)
-               return ip_finish_output2(sk, skb);
+               return ip_finish_output2(net, sk, skb);
 
        /* Slowpath -  GSO segment length is exceeding the dst MTU.
         *
@@ -253,7 +253,7 @@ static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb,
                int err;
 
                segs->next = NULL;
-               err = ip_fragment(sk, segs, mtu, ip_finish_output2);
+               err = ip_fragment(net, sk, segs, mtu, ip_finish_output2);
 
                if (err && ret == 0)
                        ret = err;
@@ -263,7 +263,7 @@ static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb,
        return ret;
 }
 
-static int ip_finish_output(struct sock *sk, struct sk_buff *skb)
+static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        unsigned int mtu;
 
@@ -271,20 +271,20 @@ static int ip_finish_output(struct sock *sk, struct sk_buff *skb)
        /* Policy lookup after SNAT yielded a new policy */
        if (skb_dst(skb)->xfrm) {
                IPCB(skb)->flags |= IPSKB_REROUTED;
-               return dst_output_sk(sk, skb);
+               return dst_output(net, sk, skb);
        }
 #endif
        mtu = ip_skb_dst_mtu(skb);
        if (skb_is_gso(skb))
-               return ip_finish_output_gso(sk, skb, mtu);
+               return ip_finish_output_gso(net, sk, skb, mtu);
 
        if (skb->len > mtu || (IPCB(skb)->flags & IPSKB_FRAG_PMTU))
-               return ip_fragment(sk, skb, mtu, ip_finish_output2);
+               return ip_fragment(net, sk, skb, mtu, ip_finish_output2);
 
-       return ip_finish_output2(sk, skb);
+       return ip_finish_output2(net, sk, skb);
 }
 
-int ip_mc_output(struct sock *sk, struct sk_buff *skb)
+int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct rtable *rt = skb_rtable(skb);
        struct net_device *dev = rt->dst.dev;
@@ -292,7 +292,7 @@ int ip_mc_output(struct sock *sk, struct sk_buff *skb)
        /*
         *      If the indicated interface is up and running, send the packet.
         */
-       IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
+       IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
 
        skb->dev = dev;
        skb->protocol = htons(ETH_P_IP);
@@ -320,7 +320,7 @@ int ip_mc_output(struct sock *sk, struct sk_buff *skb)
                        struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
                        if (newskb)
                                NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING,
-                                       sk, newskb, NULL, newskb->dev,
+                                       net, sk, newskb, NULL, newskb->dev,
                                        dev_loopback_xmit);
                }
 
@@ -335,26 +335,28 @@ int ip_mc_output(struct sock *sk, struct sk_buff *skb)
        if (rt->rt_flags&RTCF_BROADCAST) {
                struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
                if (newskb)
-                       NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, newskb,
-                               NULL, newskb->dev, dev_loopback_xmit);
+                       NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING,
+                               net, sk, newskb, NULL, newskb->dev,
+                               dev_loopback_xmit);
        }
 
-       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb, NULL,
-                           skb->dev, ip_finish_output,
+       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
+                           net, sk, skb, NULL, skb->dev,
+                           ip_finish_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
 
-int ip_output(struct sock *sk, struct sk_buff *skb)
+int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct net_device *dev = skb_dst(skb)->dev;
 
-       IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
+       IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
 
        skb->dev = dev;
        skb->protocol = htons(ETH_P_IP);
 
-       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb,
-                           NULL, dev,
+       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
+                           net, sk, skb, NULL, dev,
                            ip_finish_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
@@ -377,6 +379,7 @@ static void ip_copy_addrs(struct iphdr *iph, const struct flowi4 *fl4)
 int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)
 {
        struct inet_sock *inet = inet_sk(sk);
+       struct net *net = sock_net(sk);
        struct ip_options_rcu *inet_opt;
        struct flowi4 *fl4;
        struct rtable *rt;
@@ -407,7 +410,7 @@ int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)
                 * keep trying until route appears or the connection times
                 * itself out.
                 */
-               rt = ip_route_output_ports(sock_net(sk), fl4, sk,
+               rt = ip_route_output_ports(net, fl4, sk,
                                           daddr, inet->inet_saddr,
                                           inet->inet_dport,
                                           inet->inet_sport,
@@ -444,20 +447,20 @@ packet_routed:
                ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0);
        }
 
-       ip_select_ident_segs(sock_net(sk), skb, sk,
+       ip_select_ident_segs(net, skb, sk,
                             skb_shinfo(skb)->gso_segs ?: 1);
 
        /* TODO : should we use skb->sk here instead of sk ? */
        skb->priority = sk->sk_priority;
        skb->mark = sk->sk_mark;
 
-       res = ip_local_out(skb);
+       res = ip_local_out(net, sk, skb);
        rcu_read_unlock();
        return res;
 
 no_route:
        rcu_read_unlock();
-       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
+       IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
        kfree_skb(skb);
        return -EHOSTUNREACH;
 }
@@ -486,29 +489,26 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        skb_copy_secmark(to, from);
 }
 
-static int ip_fragment(struct sock *sk, struct sk_buff *skb,
+static int ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
                       unsigned int mtu,
-                      int (*output)(struct sock *, struct sk_buff *))
+                      int (*output)(struct net *, struct sock *, struct sk_buff *))
 {
        struct iphdr *iph = ip_hdr(skb);
 
        if ((iph->frag_off & htons(IP_DF)) == 0)
-               return ip_do_fragment(sk, skb, output);
+               return ip_do_fragment(net, sk, skb, output);
 
        if (unlikely(!skb->ignore_df ||
                     (IPCB(skb)->frag_max_size &&
                      IPCB(skb)->frag_max_size > mtu))) {
-               struct rtable *rt = skb_rtable(skb);
-               struct net_device *dev = rt->dst.dev;
-
-               IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
                          htonl(mtu));
                kfree_skb(skb);
                return -EMSGSIZE;
        }
 
-       return ip_do_fragment(sk, skb, output);
+       return ip_do_fragment(net, sk, skb, output);
 }
 
 /*
@@ -518,8 +518,8 @@ static int ip_fragment(struct sock *sk, struct sk_buff *skb,
  *     single device frame, and queue such a frame for sending.
  */
 
-int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
-                  int (*output)(struct sock *, struct sk_buff *))
+int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
+                  int (*output)(struct net *, struct sock *, struct sk_buff *))
 {
        struct iphdr *iph;
        int ptr;
@@ -533,6 +533,11 @@ int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
 
        dev = rt->dst.dev;
 
+       /* for offloaded checksums cleanup checksum before fragmentation */
+       if (skb->ip_summed == CHECKSUM_PARTIAL &&
+           (err = skb_checksum_help(skb)))
+               goto fail;
+
        /*
         *      Point into the IP datagram header.
         */
@@ -621,10 +626,10 @@ int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
                                ip_send_check(iph);
                        }
 
-                       err = output(sk, skb);
+                       err = output(net, sk, skb);
 
                        if (!err)
-                               IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
+                               IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES);
                        if (err || !frag)
                                break;
 
@@ -634,7 +639,7 @@ int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
                }
 
                if (err == 0) {
-                       IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);
+                       IP_INC_STATS(net, IPSTATS_MIB_FRAGOKS);
                        return 0;
                }
 
@@ -643,7 +648,7 @@ int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
                        kfree_skb(frag);
                        frag = skb;
                }
-               IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
                return err;
 
 slow_path_clean:
@@ -657,9 +662,6 @@ slow_path_clean:
        }
 
 slow_path:
-       /* for offloaded checksums cleanup checksum before fragmentation */
-       if ((skb->ip_summed == CHECKSUM_PARTIAL) && skb_checksum_help(skb))
-               goto fail;
        iph = ip_hdr(skb);
 
        left = skb->len - hlen;         /* Space per frame */
@@ -761,19 +763,19 @@ slow_path:
 
                ip_send_check(iph);
 
-               err = output(sk, skb2);
+               err = output(net, sk, skb2);
                if (err)
                        goto fail;
 
-               IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
+               IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES);
        }
        consume_skb(skb);
-       IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);
+       IP_INC_STATS(net, IPSTATS_MIB_FRAGOKS);
        return err;
 
 fail:
        kfree_skb(skb);
-       IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
+       IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
        return err;
 }
 EXPORT_SYMBOL(ip_do_fragment);
@@ -911,6 +913,7 @@ static int __ip_append_data(struct sock *sk,
        if (transhdrlen &&
            length + fragheaderlen <= mtu &&
            rt->dst.dev->features & NETIF_F_V4_CSUM &&
+           !(flags & MSG_MORE) &&
            !exthdrlen)
                csummode = CHECKSUM_PARTIAL;
 
@@ -1434,7 +1437,7 @@ int ip_send_skb(struct net *net, struct sk_buff *skb)
 {
        int err;
 
-       err = ip_local_out(skb);
+       err = ip_local_out(net, skb->sk, skb);
        if (err) {
                if (err > 0)
                        err = net_xmit_errno(err);
@@ -1561,7 +1564,7 @@ void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb,
        }
 
        oif = arg->bound_dev_if;
-       if (!oif && netif_index_is_vrf(net, skb->skb_iif))
+       if (!oif && netif_index_is_l3_master(net, skb->skb_iif))
                oif = skb->skb_iif;
 
        flowi4_init_output(&fl4, oif,
@@ -1596,7 +1599,6 @@ void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb,
                          arg->csumoffset) = csum_fold(csum_add(nskb->csum,
                                                                arg->csum));
                nskb->ip_summed = CHECKSUM_NONE;
-               skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
                ip_push_pending_frames(sk, &fl4);
        }
 out:
index 84dce6a92f93bec35158b392477c536d6f2f1529..6cb9009c3d96785e566a3cb2cd065860e05980e1 100644 (file)
@@ -53,6 +53,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
                  __u8 tos, __u8 ttl, __be16 df, bool xnet)
 {
        int pkt_len = skb->len - skb_inner_network_offset(skb);
+       struct net *net = dev_net(rt->dst.dev);
        struct iphdr *iph;
        int err;
 
@@ -76,10 +77,9 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
        iph->daddr      =       dst;
        iph->saddr      =       src;
        iph->ttl        =       ttl;
-       __ip_select_ident(dev_net(rt->dst.dev), iph,
-                         skb_shinfo(skb)->gso_segs ?: 1);
+       __ip_select_ident(net, iph, skb_shinfo(skb)->gso_segs ?: 1);
 
-       err = ip_local_out_sk(sk, skb);
+       err = ip_local_out(net, sk, skb);
        if (unlikely(net_xmit_eval(err)))
                pkt_len = 0;
        return pkt_len;
index 0c152087ca15dd3f97548d3c7123d42bd6626f0e..4d8f0b6987777ab6c096fadbce538c53cbcad24b 100644 (file)
@@ -197,7 +197,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev,
        skb_dst_set(skb, dst);
        skb->dev = skb_dst(skb)->dev;
 
-       err = dst_output(skb);
+       err = dst_output(tunnel->net, skb->sk, skb);
        if (net_xmit_eval(err) == 0)
                err = skb->len;
        iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
index ed4ef09c2136ec34871fc240a8197bf694e2d9ef..0bc7412d9e14a3395ab9c877ee6683f33748a3b1 100644 (file)
@@ -146,6 +146,10 @@ u8 root_server_path[256] = { 0, }; /* Path to mount as root */
 /* vendor class identifier */
 static char vendor_class_identifier[253] __initdata;
 
+#if defined(CONFIG_IP_PNP_DHCP)
+static char dhcp_client_identifier[253] __initdata;
+#endif
+
 /* Persistent data: */
 
 static int ic_proto_used;                      /* Protocol used, if any */
@@ -728,6 +732,16 @@ ic_dhcp_init_options(u8 *options)
                        memcpy(e, vendor_class_identifier, len);
                        e += len;
                }
+               len = strlen(dhcp_client_identifier + 1);
+               /* the minimum length of identifier is 2, include 1 byte type,
+                * and can not be larger than the length of options
+                */
+               if (len >= 1 && len < 312 - (e - options) - 1) {
+                       *e++ = 61;
+                       *e++ = len + 1;
+                       memcpy(e, dhcp_client_identifier, len + 1);
+                       e += len + 1;
+               }
        }
 
        *e++ = 255;     /* End of the list */
@@ -1557,8 +1571,24 @@ static int __init ic_proto_name(char *name)
                return 0;
        }
 #ifdef CONFIG_IP_PNP_DHCP
-       else if (!strcmp(name, "dhcp")) {
+       else if (!strncmp(name, "dhcp", 4)) {
+               char *client_id;
+
                ic_proto_enabled &= ~IC_RARP;
+               client_id = strstr(name, "dhcp,");
+               if (client_id) {
+                       char *v;
+
+                       client_id = client_id + 5;
+                       v = strchr(client_id, ',');
+                       if (!v)
+                               return 1;
+                       *v = 0;
+                       if (kstrtou8(client_id, 0, dhcp_client_identifier))
+                               DBG("DHCP: Invalid client identifier type\n");
+                       strncpy(dhcp_client_identifier + 1, v + 1, 251);
+                       *v = ',';
+               }
                return 1;
        }
 #endif
index 866ee89f5254a4d6b401c4d9152d15640802b6b8..92dd4b74d513ab40ba4f6f3f2f5ce7a73085f379 100644 (file)
@@ -1678,17 +1678,18 @@ static void ip_encap(struct net *net, struct sk_buff *skb,
        nf_reset(skb);
 }
 
-static inline int ipmr_forward_finish(struct sock *sk, struct sk_buff *skb)
+static inline int ipmr_forward_finish(struct net *net, struct sock *sk,
+                                     struct sk_buff *skb)
 {
        struct ip_options *opt = &(IPCB(skb)->opt);
 
-       IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
-       IP_ADD_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTOCTETS, skb->len);
+       IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS);
+       IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len);
 
        if (unlikely(opt->optlen))
                ip_forward_options(skb);
 
-       return dst_output_sk(sk, skb);
+       return dst_output(net, sk, skb);
 }
 
 /*
@@ -1745,7 +1746,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
                 * to blackhole.
                 */
 
-               IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
                ip_rt_put(rt);
                goto out_free;
        }
@@ -1787,8 +1788,8 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
         * not mrouter) cannot join to more than one interface - it will
         * result in receiving multiple packets.
         */
-       NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, NULL, skb,
-               skb->dev, dev,
+       NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,
+               net, NULL, skb, skb->dev, dev,
                ipmr_forward_finish);
        return;
 
index 61eafc9b4545d5a9e1ea405e196753b3608c5fa0..c3776ff6749f18d1bf3c5c5084d3e7656291539d 100644 (file)
@@ -17,9 +17,8 @@
 #include <net/netfilter/nf_queue.h>
 
 /* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
-int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
+int ip_route_me_harder(struct net *net, struct sk_buff *skb, unsigned int addr_type)
 {
-       struct net *net = dev_net(skb_dst(skb)->dev);
        const struct iphdr *iph = ip_hdr(skb);
        struct rtable *rt;
        struct flowi4 fl4 = {};
@@ -104,7 +103,7 @@ static void nf_ip_saveroute(const struct sk_buff *skb,
        }
 }
 
-static int nf_ip_reroute(struct sk_buff *skb,
+static int nf_ip_reroute(struct net *net, struct sk_buff *skb,
                         const struct nf_queue_entry *entry)
 {
        const struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry);
@@ -116,7 +115,7 @@ static int nf_ip_reroute(struct sk_buff *skb,
                      skb->mark == rt_info->mark &&
                      iph->daddr == rt_info->daddr &&
                      iph->saddr == rt_info->saddr))
-                       return ip_route_me_harder(skb, RTN_UNSPEC);
+                       return ip_route_me_harder(net, skb, RTN_UNSPEC);
        }
        return 0;
 }
index 690d27d3f2f90d99612de8ed4a32dec0596a680a..a3558417653567ffe3a83d06515fb1b68ec36dcf 100644 (file)
@@ -75,6 +75,7 @@ endif # NF_TABLES
 
 config NF_DUP_IPV4
        tristate "Netfilter IPv4 packet duplication to alternate destination"
+       depends on !NF_CONNTRACK || NF_CONNTRACK
        help
          This option enables the nf_dup_ipv4 core, which duplicates an IPv4
          packet to be rerouted to another destination.
index 8f87fc38ccde3a4a55dc3b2ebb8f0ec5f0ecf4c9..11dccba474b7964fe7d9ee472c48d5eca435ae41 100644 (file)
@@ -186,7 +186,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
        if (FWINV(ret != 0, ARPT_INV_VIA_IN)) {
                dprintf("VIA in mismatch (%s vs %s).%s\n",
                        indev, arpinfo->iniface,
-                       arpinfo->invflags&ARPT_INV_VIA_IN ?" (INV)":"");
+                       arpinfo->invflags & ARPT_INV_VIA_IN ? " (INV)" : "");
                return 0;
        }
 
@@ -195,7 +195,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
        if (FWINV(ret != 0, ARPT_INV_VIA_OUT)) {
                dprintf("VIA out mismatch (%s vs %s).%s\n",
                        outdev, arpinfo->outiface,
-                       arpinfo->invflags&ARPT_INV_VIA_OUT ?" (INV)":"");
+                       arpinfo->invflags & ARPT_INV_VIA_OUT ? " (INV)" : "");
                return 0;
        }
 
@@ -247,10 +247,10 @@ struct arpt_entry *arpt_next_entry(const struct arpt_entry *entry)
 }
 
 unsigned int arpt_do_table(struct sk_buff *skb,
-                          unsigned int hook,
                           const struct nf_hook_state *state,
                           struct xt_table *table)
 {
+       unsigned int hook = state->hook;
        static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
        unsigned int verdict = NF_DROP;
        const struct arphdr *arp;
@@ -285,6 +285,7 @@ unsigned int arpt_do_table(struct sk_buff *skb,
         */
        e = get_entry(table_base, private->hook_entry[hook]);
 
+       acpar.net     = state->net;
        acpar.in      = state->in;
        acpar.out     = state->out;
        acpar.hooknum = hook;
@@ -467,7 +468,7 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
                                pos = newpos;
                        }
                }
-               next:
+next:
                duprintf("Finished chain %u\n", hook);
        }
        return 1;
@@ -631,7 +632,7 @@ static inline void cleanup_entry(struct arpt_entry *e)
  * newinfo).
  */
 static int translate_table(struct xt_table_info *newinfo, void *entry0,
-                           const struct arpt_replace *repl)
+                          const struct arpt_replace *repl)
 {
        struct arpt_entry *iter;
        unsigned int i;
@@ -891,7 +892,7 @@ static int compat_table_info(const struct xt_table_info *info,
 #endif
 
 static int get_info(struct net *net, void __user *user,
-                    const int *len, int compat)
+                   const int *len, int compat)
 {
        char name[XT_TABLE_MAXNAMELEN];
        struct xt_table *t;
@@ -1068,7 +1069,7 @@ static int __do_replace(struct net *net, const char *name,
 }
 
 static int do_replace(struct net *net, const void __user *user,
-                      unsigned int len)
+                     unsigned int len)
 {
        int ret;
        struct arpt_replace tmp;
index 93876d03120ca85fbc1e5aaa689d245d4508f01e..1897ee1609202f326a98f96536a00acd96204a13 100644 (file)
@@ -27,13 +27,10 @@ static const struct xt_table packet_filter = {
 
 /* The work comes in here from netfilter.c */
 static unsigned int
-arptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+arptable_filter_hook(void *priv, struct sk_buff *skb,
                     const struct nf_hook_state *state)
 {
-       const struct net *net = dev_net(state->in ? state->in : state->out);
-
-       return arpt_do_table(skb, ops->hooknum, state,
-                            net->ipv4.arptable_filter);
+       return arpt_do_table(skb, state, state->net->ipv4.arptable_filter);
 }
 
 static struct nf_hook_ops *arpfilter_ops __read_mostly;
index b0a86e73451c1f2ee99e209eb8a6556414b02e46..b99affad6ba1f4939e10f676a06f92cd27c32add 100644 (file)
@@ -102,7 +102,7 @@ ip_packet_match(const struct iphdr *ip,
        if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
                dprintf("VIA in mismatch (%s vs %s).%s\n",
                        indev, ipinfo->iniface,
-                       ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+                       ipinfo->invflags & IPT_INV_VIA_IN ? " (INV)" : "");
                return false;
        }
 
@@ -111,7 +111,7 @@ ip_packet_match(const struct iphdr *ip,
        if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
                dprintf("VIA out mismatch (%s vs %s).%s\n",
                        outdev, ipinfo->outiface,
-                       ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+                       ipinfo->invflags & IPT_INV_VIA_OUT ? " (INV)" : "");
                return false;
        }
 
@@ -120,7 +120,7 @@ ip_packet_match(const struct iphdr *ip,
            FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {
                dprintf("Packet protocol %hi does not match %hi.%s\n",
                        ip->protocol, ipinfo->proto,
-                       ipinfo->invflags&IPT_INV_PROTO ? " (INV)":"");
+                       ipinfo->invflags & IPT_INV_PROTO ? " (INV)" : "");
                return false;
        }
 
@@ -246,7 +246,8 @@ get_chainname_rulenum(const struct ipt_entry *s, const struct ipt_entry *e,
        return 0;
 }
 
-static void trace_packet(const struct sk_buff *skb,
+static void trace_packet(struct net *net,
+                        const struct sk_buff *skb,
                         unsigned int hook,
                         const struct net_device *in,
                         const struct net_device *out,
@@ -258,7 +259,6 @@ static void trace_packet(const struct sk_buff *skb,
        const char *hookname, *chainname, *comment;
        const struct ipt_entry *iter;
        unsigned int rulenum = 0;
-       struct net *net = dev_net(in ? in : out);
 
        root = get_entry(private->entries, private->hook_entry[hook]);
 
@@ -285,10 +285,10 @@ struct ipt_entry *ipt_next_entry(const struct ipt_entry *entry)
 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
 unsigned int
 ipt_do_table(struct sk_buff *skb,
-            unsigned int hook,
             const struct nf_hook_state *state,
             struct xt_table *table)
 {
+       unsigned int hook = state->hook;
        static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
        const struct iphdr *ip;
        /* Initializing verdict to NF_DROP keeps gcc happy. */
@@ -315,6 +315,7 @@ ipt_do_table(struct sk_buff *skb,
        acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
        acpar.thoff   = ip_hdrlen(skb);
        acpar.hotdrop = false;
+       acpar.net     = state->net;
        acpar.in      = state->in;
        acpar.out     = state->out;
        acpar.family  = NFPROTO_IPV4;
@@ -378,8 +379,8 @@ ipt_do_table(struct sk_buff *skb,
 #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
                /* The packet is traced: log it */
                if (unlikely(skb->nf_trace))
-                       trace_packet(skb, hook, state->in, state->out,
-                                    table->name, private, e);
+                       trace_packet(state->net, skb, hook, state->in,
+                                    state->out, table->name, private, e);
 #endif
                /* Standard target? */
                if (!t->u.kernel.target->target) {
@@ -430,8 +431,8 @@ ipt_do_table(struct sk_buff *skb,
        } while (!acpar.hotdrop);
        pr_debug("Exiting %s; sp at %u\n", __func__, stackidx);
 
-       xt_write_recseq_end(addend);
-       local_bh_enable();
+       xt_write_recseq_end(addend);
+       local_bh_enable();
 
 #ifdef DEBUG_ALLOW_ALL
        return NF_ACCEPT;
@@ -483,7 +484,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
                                unsigned int oldpos, size;
 
                                if ((strcmp(t->target.u.user.name,
-                                           XT_STANDARD_TARGET) == 0) &&
+                                           XT_STANDARD_TARGET) == 0) &&
                                    t->verdict < -NF_MAX_VERDICT - 1) {
                                        duprintf("mark_source_chains: bad "
                                                "negative verdict (%i)\n",
@@ -548,7 +549,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
                                pos = newpos;
                        }
                }
-               next:
+next:
                duprintf("Finished chain %u\n", hook);
        }
        return 1;
@@ -803,7 +804,7 @@ cleanup_entry(struct ipt_entry *e, struct net *net)
    newinfo) */
 static int
 translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
-                const struct ipt_replace *repl)
+               const struct ipt_replace *repl)
 {
        struct ipt_entry *iter;
        unsigned int i;
@@ -1077,7 +1078,7 @@ static int compat_table_info(const struct xt_table_info *info,
 #endif
 
 static int get_info(struct net *net, void __user *user,
-                    const int *len, int compat)
+                   const int *len, int compat)
 {
        char name[XT_TABLE_MAXNAMELEN];
        struct xt_table *t;
@@ -1303,7 +1304,7 @@ do_replace(struct net *net, const void __user *user, unsigned int len)
 
 static int
 do_add_counters(struct net *net, const void __user *user,
-                unsigned int len, int compat)
+               unsigned int len, int compat)
 {
        unsigned int i;
        struct xt_counters_info tmp;
index 45cb16a6a4a337c564d880485c45ca46229cb152..4a9e6db9df8d719a14b6aa129b78ba614587767f 100644 (file)
@@ -492,14 +492,14 @@ static void arp_print(struct arp_payload *payload)
 {
 #define HBUFFERLEN 30
        char hbuffer[HBUFFERLEN];
-       int j,k;
+       int j, k;
 
-       for (k=0, j=0; k < HBUFFERLEN-3 && j < ETH_ALEN; j++) {
+       for (k = 0, j = 0; k < HBUFFERLEN - 3 && j < ETH_ALEN; j++) {
                hbuffer[k++] = hex_asc_hi(payload->src_hw[j]);
                hbuffer[k++] = hex_asc_lo(payload->src_hw[j]);
-               hbuffer[k++]=':';
+               hbuffer[k++] = ':';
        }
-       hbuffer[--k]='\0';
+       hbuffer[--k] = '\0';
 
        pr_debug("src %pI4@%s, dst %pI4\n",
                 &payload->src_ip, hbuffer, &payload->dst_ip);
@@ -507,14 +507,14 @@ static void arp_print(struct arp_payload *payload)
 #endif
 
 static unsigned int
-arp_mangle(const struct nf_hook_ops *ops,
+arp_mangle(void *priv,
           struct sk_buff *skb,
           const struct nf_hook_state *state)
 {
        struct arphdr *arp = arp_hdr(skb);
        struct arp_payload *payload;
        struct clusterip_config *c;
-       struct net *net = dev_net(state->in ? state->in : state->out);
+       struct net *net = state->net;
 
        /* we don't care about non-ethernet and non-ipv4 ARP */
        if (arp->ar_hrd != htons(ARPHRD_ETHER) ||
index 87907d4bd259a725cfeea6e4cbccb77be7995972..1d16c0f28df00d17c38a52bfeff23c0a1bca0142 100644 (file)
@@ -59,7 +59,7 @@ reject_tg(struct sk_buff *skb, const struct xt_action_param *par)
                nf_send_unreach(skb, ICMP_PKT_FILTERED, hook);
                break;
        case IPT_TCP_RESET:
-               nf_send_reset(skb, hook);
+               nf_send_reset(par->net, skb, hook);
        case IPT_ICMP_ECHOREPLY:
                /* Doesn't happen. */
                break;
index 95ea633e8356eb9b419e4027f9954810194aa23c..5fdc556514bac3335f0c4f78b2c01c54f1c8b68f 100644 (file)
@@ -39,11 +39,14 @@ synproxy_build_ip(struct sk_buff *skb, __be32 saddr, __be32 daddr)
 }
 
 static void
-synproxy_send_tcp(const struct sk_buff *skb, struct sk_buff *nskb,
+synproxy_send_tcp(const struct synproxy_net *snet,
+                 const struct sk_buff *skb, struct sk_buff *nskb,
                  struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo,
                  struct iphdr *niph, struct tcphdr *nth,
                  unsigned int tcp_hdr_size)
 {
+       struct net *net = nf_ct_net(snet->tmpl);
+
        nth->check = ~tcp_v4_check(tcp_hdr_size, niph->saddr, niph->daddr, 0);
        nskb->ip_summed   = CHECKSUM_PARTIAL;
        nskb->csum_start  = (unsigned char *)nth - nskb->head;
@@ -51,7 +54,7 @@ synproxy_send_tcp(const struct sk_buff *skb, struct sk_buff *nskb,
 
        skb_dst_set_noref(nskb, skb_dst(skb));
        nskb->protocol = htons(ETH_P_IP);
-       if (ip_route_me_harder(nskb, RTN_UNSPEC))
+       if (ip_route_me_harder(net, nskb, RTN_UNSPEC))
                goto free_nskb;
 
        if (nfct) {
@@ -60,7 +63,7 @@ synproxy_send_tcp(const struct sk_buff *skb, struct sk_buff *nskb,
                nf_conntrack_get(nfct);
        }
 
-       ip_local_out(nskb);
+       ip_local_out(net, nskb->sk, nskb);
        return;
 
 free_nskb:
@@ -68,7 +71,8 @@ free_nskb:
 }
 
 static void
-synproxy_send_client_synack(const struct sk_buff *skb, const struct tcphdr *th,
+synproxy_send_client_synack(const struct synproxy_net *snet,
+                           const struct sk_buff *skb, const struct tcphdr *th,
                            const struct synproxy_options *opts)
 {
        struct sk_buff *nskb;
@@ -104,7 +108,7 @@ synproxy_send_client_synack(const struct sk_buff *skb, const struct tcphdr *th,
 
        synproxy_build_options(nth, opts);
 
-       synproxy_send_tcp(skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY,
+       synproxy_send_tcp(snet, skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY,
                          niph, nth, tcp_hdr_size);
 }
 
@@ -148,7 +152,7 @@ synproxy_send_server_syn(const struct synproxy_net *snet,
 
        synproxy_build_options(nth, opts);
 
-       synproxy_send_tcp(skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW,
+       synproxy_send_tcp(snet, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW,
                          niph, nth, tcp_hdr_size);
 }
 
@@ -188,7 +192,7 @@ synproxy_send_server_ack(const struct synproxy_net *snet,
 
        synproxy_build_options(nth, opts);
 
-       synproxy_send_tcp(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size);
+       synproxy_send_tcp(snet, skb, nskb, NULL, 0, niph, nth, tcp_hdr_size);
 }
 
 static void
@@ -226,8 +230,8 @@ synproxy_send_client_ack(const struct synproxy_net *snet,
 
        synproxy_build_options(nth, opts);
 
-       synproxy_send_tcp(skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY,
-                         niph, nth, tcp_hdr_size);
+       synproxy_send_tcp(snet, skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY,
+                         niph, nth, tcp_hdr_size);
 }
 
 static bool
@@ -258,7 +262,7 @@ static unsigned int
 synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_synproxy_info *info = par->targinfo;
-       struct synproxy_net *snet = synproxy_pernet(dev_net(par->in));
+       struct synproxy_net *snet = synproxy_pernet(par->net);
        struct synproxy_options opts = {};
        struct tcphdr *th, _th;
 
@@ -287,7 +291,7 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par)
                                          XT_SYNPROXY_OPT_SACK_PERM |
                                          XT_SYNPROXY_OPT_ECN);
 
-               synproxy_send_client_synack(skb, th, &opts);
+               synproxy_send_client_synack(snet, skb, th, &opts);
                return NF_DROP;
 
        } else if (th->ack && !(th->fin || th->rst || th->syn)) {
@@ -299,11 +303,11 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par)
        return XT_CONTINUE;
 }
 
-static unsigned int ipv4_synproxy_hook(const struct nf_hook_ops *ops,
+static unsigned int ipv4_synproxy_hook(void *priv,
                                       struct sk_buff *skb,
                                       const struct nf_hook_state *nhs)
 {
-       struct synproxy_net *snet = synproxy_pernet(dev_net(nhs->in ? : nhs->out));
+       struct synproxy_net *snet = synproxy_pernet(nhs->net);
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct;
        struct nf_conn_synproxy *synproxy;
@@ -433,14 +437,12 @@ static struct xt_target synproxy_tg4_reg __read_mostly = {
 static struct nf_hook_ops ipv4_synproxy_ops[] __read_mostly = {
        {
                .hook           = ipv4_synproxy_hook,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
        },
        {
                .hook           = ipv4_synproxy_hook,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_POST_ROUTING,
                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
index 14a2aa8b8a142502095e4da8a034eae6e78a8313..a787d07f6cb757b741fcd600f9972c3399c95c85 100644 (file)
@@ -25,7 +25,7 @@ spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, bool invert)
        bool r;
        pr_debug("spi_match:%c 0x%x <= 0x%x <= 0x%x\n",
                 invert ? '!' : ' ', min, spi, max);
-       r=(spi >= min && spi <= max) ^ invert;
+       r = (spi >= min && spi <= max) ^ invert;
        pr_debug(" result %s\n", r ? "PASS" : "FAILED");
        return r;
 }
index 8618fd150c965015ca2ee256499add4186f4810a..78cc64eddfc1855849652c563b3c931cd7cc72e6 100644 (file)
@@ -32,12 +32,11 @@ static __be32 rpfilter_get_saddr(__be32 addr)
        return addr;
 }
 
-static bool rpfilter_lookup_reverse(struct flowi4 *fl4,
+static bool rpfilter_lookup_reverse(struct net *net, struct flowi4 *fl4,
                                const struct net_device *dev, u8 flags)
 {
        struct fib_result res;
        bool dev_match;
-       struct net *net = dev_net(dev);
        int ret __maybe_unused;
 
        if (fib_lookup(net, fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE))
@@ -61,9 +60,7 @@ static bool rpfilter_lookup_reverse(struct flowi4 *fl4,
        if (FIB_RES_DEV(res) == dev)
                dev_match = true;
 #endif
-       if (dev_match || flags & XT_RPFILTER_LOOSE)
-               return FIB_RES_NH(res).nh_scope <= RT_SCOPE_HOST;
-       return dev_match;
+       return dev_match || flags & XT_RPFILTER_LOOSE;
 }
 
 static bool rpfilter_is_local(const struct sk_buff *skb)
@@ -98,7 +95,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)
        flow.flowi4_tos = RT_TOS(iph->tos);
        flow.flowi4_scope = RT_SCOPE_UNIVERSE;
 
-       return rpfilter_lookup_reverse(&flow, par->in, info->flags) ^ invert;
+       return rpfilter_lookup_reverse(par->net, &flow, par->in, info->flags) ^ invert;
 }
 
 static int rpfilter_check(const struct xt_mtchk_param *par)
index a0f3beca52d2107b12ae748a4328d5491e7553c3..397ef2dd133ed56f9edb3146e30ee46f99a3f50d 100644 (file)
@@ -33,19 +33,16 @@ static const struct xt_table packet_filter = {
 };
 
 static unsigned int
-iptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+iptable_filter_hook(void *priv, struct sk_buff *skb,
                    const struct nf_hook_state *state)
 {
-       const struct net *net;
-
-       if (ops->hooknum == NF_INET_LOCAL_OUT &&
+       if (state->hook == NF_INET_LOCAL_OUT &&
            (skb->len < sizeof(struct iphdr) ||
             ip_hdrlen(skb) < sizeof(struct iphdr)))
                /* root is playing with raw sockets. */
                return NF_ACCEPT;
 
-       net = dev_net(state->in ? state->in : state->out);
-       return ipt_do_table(skb, ops->hooknum, state, net->ipv4.iptable_filter);
+       return ipt_do_table(skb, state, state->net->ipv4.iptable_filter);
 }
 
 static struct nf_hook_ops *filter_ops __read_mostly;
index 62cbb8c5f4a8f246428599186e0e9b498ae460a6..ba5d392a13c41c74501cc2b76805d04a06b2675e 100644 (file)
@@ -39,7 +39,6 @@ static const struct xt_table packet_mangler = {
 static unsigned int
 ipt_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
 {
-       struct net_device *out = state->out;
        unsigned int ret;
        const struct iphdr *iph;
        u_int8_t tos;
@@ -59,8 +58,7 @@ ipt_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
        daddr = iph->daddr;
        tos = iph->tos;
 
-       ret = ipt_do_table(skb, NF_INET_LOCAL_OUT, state,
-                          dev_net(out)->ipv4.iptable_mangle);
+       ret = ipt_do_table(skb, state, state->net->ipv4.iptable_mangle);
        /* Reroute for ANY change. */
        if (ret != NF_DROP && ret != NF_STOLEN) {
                iph = ip_hdr(skb);
@@ -69,7 +67,7 @@ ipt_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
                    iph->daddr != daddr ||
                    skb->mark != mark ||
                    iph->tos != tos) {
-                       err = ip_route_me_harder(skb, RTN_UNSPEC);
+                       err = ip_route_me_harder(state->net, skb, RTN_UNSPEC);
                        if (err < 0)
                                ret = NF_DROP_ERR(err);
                }
@@ -80,18 +78,17 @@ ipt_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-iptable_mangle_hook(const struct nf_hook_ops *ops,
+iptable_mangle_hook(void *priv,
                     struct sk_buff *skb,
                     const struct nf_hook_state *state)
 {
-       if (ops->hooknum == NF_INET_LOCAL_OUT)
+       if (state->hook == NF_INET_LOCAL_OUT)
                return ipt_mangle_out(skb, state);
-       if (ops->hooknum == NF_INET_POST_ROUTING)
-               return ipt_do_table(skb, ops->hooknum, state,
-                                   dev_net(state->out)->ipv4.iptable_mangle);
+       if (state->hook == NF_INET_POST_ROUTING)
+               return ipt_do_table(skb, state,
+                                   state->net->ipv4.iptable_mangle);
        /* PREROUTING/INPUT/FORWARD: */
-       return ipt_do_table(skb, ops->hooknum, state,
-                           dev_net(state->in)->ipv4.iptable_mangle);
+       return ipt_do_table(skb, state, state->net->ipv4.iptable_mangle);
 }
 
 static struct nf_hook_ops *mangle_ops __read_mostly;
index 0d4d9cdf98a4c0dcb2da3fbebe9264efeb99d4c7..ae2cd275204643ebff64d7537dd617bd3a0eec62 100644 (file)
@@ -28,49 +28,46 @@ static const struct xt_table nf_nat_ipv4_table = {
        .af             = NFPROTO_IPV4,
 };
 
-static unsigned int iptable_nat_do_chain(const struct nf_hook_ops *ops,
+static unsigned int iptable_nat_do_chain(void *priv,
                                         struct sk_buff *skb,
                                         const struct nf_hook_state *state,
                                         struct nf_conn *ct)
 {
-       struct net *net = nf_ct_net(ct);
-
-       return ipt_do_table(skb, ops->hooknum, state, net->ipv4.nat_table);
+       return ipt_do_table(skb, state, state->net->ipv4.nat_table);
 }
 
-static unsigned int iptable_nat_ipv4_fn(const struct nf_hook_ops *ops,
+static unsigned int iptable_nat_ipv4_fn(void *priv,
                                        struct sk_buff *skb,
                                        const struct nf_hook_state *state)
 {
-       return nf_nat_ipv4_fn(ops, skb, state, iptable_nat_do_chain);
+       return nf_nat_ipv4_fn(priv, skb, state, iptable_nat_do_chain);
 }
 
-static unsigned int iptable_nat_ipv4_in(const struct nf_hook_ops *ops,
+static unsigned int iptable_nat_ipv4_in(void *priv,
                                        struct sk_buff *skb,
                                        const struct nf_hook_state *state)
 {
-       return nf_nat_ipv4_in(ops, skb, state, iptable_nat_do_chain);
+       return nf_nat_ipv4_in(priv, skb, state, iptable_nat_do_chain);
 }
 
-static unsigned int iptable_nat_ipv4_out(const struct nf_hook_ops *ops,
+static unsigned int iptable_nat_ipv4_out(void *priv,
                                         struct sk_buff *skb,
                                         const struct nf_hook_state *state)
 {
-       return nf_nat_ipv4_out(ops, skb, state, iptable_nat_do_chain);
+       return nf_nat_ipv4_out(priv, skb, state, iptable_nat_do_chain);
 }
 
-static unsigned int iptable_nat_ipv4_local_fn(const struct nf_hook_ops *ops,
+static unsigned int iptable_nat_ipv4_local_fn(void *priv,
                                              struct sk_buff *skb,
                                              const struct nf_hook_state *state)
 {
-       return nf_nat_ipv4_local_fn(ops, skb, state, iptable_nat_do_chain);
+       return nf_nat_ipv4_local_fn(priv, skb, state, iptable_nat_do_chain);
 }
 
 static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
        /* Before packet filtering, change destination */
        {
                .hook           = iptable_nat_ipv4_in,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_PRE_ROUTING,
                .priority       = NF_IP_PRI_NAT_DST,
@@ -78,7 +75,6 @@ static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
        /* After packet filtering, change source */
        {
                .hook           = iptable_nat_ipv4_out,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_POST_ROUTING,
                .priority       = NF_IP_PRI_NAT_SRC,
@@ -86,7 +82,6 @@ static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
        /* Before packet filtering, change destination */
        {
                .hook           = iptable_nat_ipv4_local_fn,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_OUT,
                .priority       = NF_IP_PRI_NAT_DST,
@@ -94,7 +89,6 @@ static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
        /* After packet filtering, change source */
        {
                .hook           = iptable_nat_ipv4_fn,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP_PRI_NAT_SRC,
index 0356e6da4bb749ba1dcfa07667dcac7b0aa92878..1ba02811acb0c3d380b2779dd61687b92be51ce8 100644 (file)
@@ -20,19 +20,16 @@ static const struct xt_table packet_raw = {
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-iptable_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+iptable_raw_hook(void *priv, struct sk_buff *skb,
                 const struct nf_hook_state *state)
 {
-       const struct net *net;
-
-       if (ops->hooknum == NF_INET_LOCAL_OUT &&
+       if (state->hook == NF_INET_LOCAL_OUT &&
            (skb->len < sizeof(struct iphdr) ||
             ip_hdrlen(skb) < sizeof(struct iphdr)))
                /* root is playing with raw sockets. */
                return NF_ACCEPT;
 
-       net = dev_net(state->in ? state->in : state->out);
-       return ipt_do_table(skb, ops->hooknum, state, net->ipv4.iptable_raw);
+       return ipt_do_table(skb, state, state->net->ipv4.iptable_raw);
 }
 
 static struct nf_hook_ops *rawtable_ops __read_mostly;
index 4bce3980ccd935f891c55329c127478dc031ed77..c2e23d5e9cd4a8412d38f20eb8ddedf2f32f8aa6 100644 (file)
@@ -37,20 +37,16 @@ static const struct xt_table security_table = {
 };
 
 static unsigned int
-iptable_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+iptable_security_hook(void *priv, struct sk_buff *skb,
                      const struct nf_hook_state *state)
 {
-       const struct net *net;
-
-       if (ops->hooknum == NF_INET_LOCAL_OUT &&
+       if (state->hook == NF_INET_LOCAL_OUT &&
            (skb->len < sizeof(struct iphdr) ||
             ip_hdrlen(skb) < sizeof(struct iphdr)))
                /* Somebody is playing with raw sockets. */
                return NF_ACCEPT;
 
-       net = dev_net(state->in ? state->in : state->out);
-       return ipt_do_table(skb, ops->hooknum, state,
-                           net->ipv4.iptable_security);
+       return ipt_do_table(skb, state, state->net->ipv4.iptable_security);
 }
 
 static struct nf_hook_ops *sectbl_ops __read_mostly;
@@ -83,7 +79,7 @@ static int __init iptable_security_init(void)
        int ret;
 
        ret = register_pernet_subsys(&iptable_security_net_ops);
-        if (ret < 0)
+       if (ret < 0)
                return ret;
 
        sectbl_ops = xt_hook_link(&security_table, iptable_security_hook);
index 8a2caaf3940bedaa9abba13352594a29341d287e..461ca926fd39408613f62a3de8bfc5ff4cbce7f6 100644 (file)
@@ -92,7 +92,7 @@ static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
        return NF_ACCEPT;
 }
 
-static unsigned int ipv4_helper(const struct nf_hook_ops *ops,
+static unsigned int ipv4_helper(void *priv,
                                struct sk_buff *skb,
                                const struct nf_hook_state *state)
 {
@@ -119,7 +119,7 @@ static unsigned int ipv4_helper(const struct nf_hook_ops *ops,
                            ct, ctinfo);
 }
 
-static unsigned int ipv4_confirm(const struct nf_hook_ops *ops,
+static unsigned int ipv4_confirm(void *priv,
                                 struct sk_buff *skb,
                                 const struct nf_hook_state *state)
 {
@@ -143,14 +143,14 @@ out:
        return nf_conntrack_confirm(skb);
 }
 
-static unsigned int ipv4_conntrack_in(const struct nf_hook_ops *ops,
+static unsigned int ipv4_conntrack_in(void *priv,
                                      struct sk_buff *skb,
                                      const struct nf_hook_state *state)
 {
-       return nf_conntrack_in(dev_net(state->in), PF_INET, ops->hooknum, skb);
+       return nf_conntrack_in(state->net, PF_INET, state->hook, skb);
 }
 
-static unsigned int ipv4_conntrack_local(const struct nf_hook_ops *ops,
+static unsigned int ipv4_conntrack_local(void *priv,
                                         struct sk_buff *skb,
                                         const struct nf_hook_state *state)
 {
@@ -158,7 +158,7 @@ static unsigned int ipv4_conntrack_local(const struct nf_hook_ops *ops,
        if (skb->len < sizeof(struct iphdr) ||
            ip_hdrlen(skb) < sizeof(struct iphdr))
                return NF_ACCEPT;
-       return nf_conntrack_in(dev_net(state->out), PF_INET, ops->hooknum, skb);
+       return nf_conntrack_in(state->net, PF_INET, state->hook, skb);
 }
 
 /* Connection tracking may drop packets, but never alters them, so
@@ -166,42 +166,36 @@ static unsigned int ipv4_conntrack_local(const struct nf_hook_ops *ops,
 static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
        {
                .hook           = ipv4_conntrack_in,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_PRE_ROUTING,
                .priority       = NF_IP_PRI_CONNTRACK,
        },
        {
                .hook           = ipv4_conntrack_local,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_OUT,
                .priority       = NF_IP_PRI_CONNTRACK,
        },
        {
                .hook           = ipv4_helper,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_POST_ROUTING,
                .priority       = NF_IP_PRI_CONNTRACK_HELPER,
        },
        {
                .hook           = ipv4_confirm,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_POST_ROUTING,
                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM,
        },
        {
                .hook           = ipv4_helper,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP_PRI_CONNTRACK_HELPER,
        },
        {
                .hook           = ipv4_confirm,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM,
index cdde3ec496e94321c424d3dd37b31cb305e05451..c567e1b5d7990d977383453d58b4ae80f387d25c 100644 (file)
@@ -30,7 +30,7 @@ static inline struct nf_icmp_net *icmp_pernet(struct net *net)
 }
 
 static bool icmp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
-                             struct nf_conntrack_tuple *tuple)
+                             struct net *net, struct nf_conntrack_tuple *tuple)
 {
        const struct icmphdr *hp;
        struct icmphdr _hdr;
@@ -144,7 +144,7 @@ icmp_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
        if (!nf_ct_get_tuplepr(skb,
                               skb_network_offset(skb) + ip_hdrlen(skb)
                                                       + sizeof(struct icmphdr),
-                              PF_INET, &origtuple)) {
+                              PF_INET, net, &origtuple)) {
                pr_debug("icmp_error_message: failed to get tuple\n");
                return -NF_ACCEPT;
        }
index 9306ec4fab41e9fa0c3c99fd6be78bcf8adfb397..0e5591c2ee9f6d66acb47ce2cbbf31403dc286f7 100644 (file)
 #endif
 #include <net/netfilter/nf_conntrack_zones.h>
 
-static int nf_ct_ipv4_gather_frags(struct sk_buff *skb, u_int32_t user)
+static int nf_ct_ipv4_gather_frags(struct net *net, struct sk_buff *skb,
+                                  u_int32_t user)
 {
        int err;
 
        skb_orphan(skb);
 
        local_bh_disable();
-       err = ip_defrag(skb, user);
+       err = ip_defrag(net, skb, user);
        local_bh_enable();
 
        if (!err) {
@@ -61,7 +62,7 @@ static enum ip_defrag_users nf_ct_defrag_user(unsigned int hooknum,
                return IP_DEFRAG_CONNTRACK_OUT + zone_id;
 }
 
-static unsigned int ipv4_conntrack_defrag(const struct nf_hook_ops *ops,
+static unsigned int ipv4_conntrack_defrag(void *priv,
                                          struct sk_buff *skb,
                                          const struct nf_hook_state *state)
 {
@@ -83,9 +84,9 @@ static unsigned int ipv4_conntrack_defrag(const struct nf_hook_ops *ops,
        /* Gather fragments. */
        if (ip_is_fragment(ip_hdr(skb))) {
                enum ip_defrag_users user =
-                       nf_ct_defrag_user(ops->hooknum, skb);
+                       nf_ct_defrag_user(state->hook, skb);
 
-               if (nf_ct_ipv4_gather_frags(skb, user))
+               if (nf_ct_ipv4_gather_frags(state->net, skb, user))
                        return NF_STOLEN;
        }
        return NF_ACCEPT;
@@ -94,14 +95,12 @@ static unsigned int ipv4_conntrack_defrag(const struct nf_hook_ops *ops,
 static struct nf_hook_ops ipv4_defrag_ops[] = {
        {
                .hook           = ipv4_conntrack_defrag,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_PRE_ROUTING,
                .priority       = NF_IP_PRI_CONNTRACK_DEFRAG,
        },
        {
                .hook           = ipv4_conntrack_defrag,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_OUT,
                .priority       = NF_IP_PRI_CONNTRACK_DEFRAG,
index 2d79e6e8d934dbd41080b2c6a658569e2ce00c53..ceb187308120675c6f5d51000e5e7112a48b3670 100644 (file)
 #include <net/netfilter/nf_conntrack.h>
 #endif
 
-static struct net *pick_net(struct sk_buff *skb)
-{
-#ifdef CONFIG_NET_NS
-       const struct dst_entry *dst;
-
-       if (skb->dev != NULL)
-               return dev_net(skb->dev);
-       dst = skb_dst(skb);
-       if (dst != NULL && dst->dev != NULL)
-               return dev_net(dst->dev);
-#endif
-       return &init_net;
-}
-
-static bool nf_dup_ipv4_route(struct sk_buff *skb, const struct in_addr *gw,
-                             int oif)
+static bool nf_dup_ipv4_route(struct net *net, struct sk_buff *skb,
+                             const struct in_addr *gw, int oif)
 {
        const struct iphdr *iph = ip_hdr(skb);
-       struct net *net = pick_net(skb);
        struct rtable *rt;
        struct flowi4 fl4;
 
@@ -65,7 +50,7 @@ static bool nf_dup_ipv4_route(struct sk_buff *skb, const struct in_addr *gw,
        return true;
 }
 
-void nf_dup_ipv4(struct sk_buff *skb, unsigned int hooknum,
+void nf_dup_ipv4(struct net *net, struct sk_buff *skb, unsigned int hooknum,
                 const struct in_addr *gw, int oif)
 {
        struct iphdr *iph;
@@ -105,9 +90,9 @@ void nf_dup_ipv4(struct sk_buff *skb, unsigned int hooknum,
                --iph->ttl;
        ip_send_check(iph);
 
-       if (nf_dup_ipv4_route(skb, gw, oif)) {
+       if (nf_dup_ipv4_route(net, skb, gw, oif)) {
                __this_cpu_write(nf_skb_duplicated, true);
-               ip_local_out(skb);
+               ip_local_out(net, skb->sk, skb);
                __this_cpu_write(nf_skb_duplicated, false);
        } else {
                kfree_skb(skb);
index 22f4579b0c2aeba3ad637e4cff756042e86e4244..5075b7ecd26d4a323f0549930d23bad0a5df8ab5 100644 (file)
@@ -255,9 +255,9 @@ int nf_nat_icmp_reply_translation(struct sk_buff *skb,
 EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation);
 
 unsigned int
-nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
+nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
               const struct nf_hook_state *state,
-              unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+              unsigned int (*do_chain)(void *priv,
                                        struct sk_buff *skb,
                                        const struct nf_hook_state *state,
                                        struct nf_conn *ct))
@@ -266,7 +266,7 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
        enum ip_conntrack_info ctinfo;
        struct nf_conn_nat *nat;
        /* maniptype == SRC for postrouting. */
-       enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
+       enum nf_nat_manip_type maniptype = HOOK2MANIP(state->hook);
 
        /* We never see fragments: conntrack defrags on pre-routing
         * and local-out, and nf_nat_out protects post-routing.
@@ -295,7 +295,7 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
        case IP_CT_RELATED_REPLY:
                if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
                        if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
-                                                          ops->hooknum))
+                                                          state->hook))
                                return NF_DROP;
                        else
                                return NF_ACCEPT;
@@ -308,21 +308,21 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
                if (!nf_nat_initialized(ct, maniptype)) {
                        unsigned int ret;
 
-                       ret = do_chain(ops, skb, state, ct);
+                       ret = do_chain(priv, skb, state, ct);
                        if (ret != NF_ACCEPT)
                                return ret;
 
-                       if (nf_nat_initialized(ct, HOOK2MANIP(ops->hooknum)))
+                       if (nf_nat_initialized(ct, HOOK2MANIP(state->hook)))
                                break;
 
-                       ret = nf_nat_alloc_null_binding(ct, ops->hooknum);
+                       ret = nf_nat_alloc_null_binding(ct, state->hook);
                        if (ret != NF_ACCEPT)
                                return ret;
                } else {
                        pr_debug("Already setup manip %s for ct %p\n",
                                 maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
                                 ct);
-                       if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat,
+                       if (nf_nat_oif_changed(state->hook, ctinfo, nat,
                                               state->out))
                                goto oif_changed;
                }
@@ -332,11 +332,11 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
                /* ESTABLISHED */
                NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
                             ctinfo == IP_CT_ESTABLISHED_REPLY);
-               if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, state->out))
+               if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out))
                        goto oif_changed;
        }
 
-       return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
+       return nf_nat_packet(ct, ctinfo, state->hook, skb);
 
 oif_changed:
        nf_ct_kill_acct(ct, ctinfo, skb);
@@ -345,9 +345,9 @@ oif_changed:
 EXPORT_SYMBOL_GPL(nf_nat_ipv4_fn);
 
 unsigned int
-nf_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
+nf_nat_ipv4_in(void *priv, struct sk_buff *skb,
               const struct nf_hook_state *state,
-              unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+              unsigned int (*do_chain)(void *priv,
                                         struct sk_buff *skb,
                                         const struct nf_hook_state *state,
                                         struct nf_conn *ct))
@@ -355,7 +355,7 @@ nf_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
        unsigned int ret;
        __be32 daddr = ip_hdr(skb)->daddr;
 
-       ret = nf_nat_ipv4_fn(ops, skb, state, do_chain);
+       ret = nf_nat_ipv4_fn(priv, skb, state, do_chain);
        if (ret != NF_DROP && ret != NF_STOLEN &&
            daddr != ip_hdr(skb)->daddr)
                skb_dst_drop(skb);
@@ -365,9 +365,9 @@ nf_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
 EXPORT_SYMBOL_GPL(nf_nat_ipv4_in);
 
 unsigned int
-nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
+nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
                const struct nf_hook_state *state,
-               unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+               unsigned int (*do_chain)(void *priv,
                                          struct sk_buff *skb,
                                          const struct nf_hook_state *state,
                                          struct nf_conn *ct))
@@ -384,7 +384,7 @@ nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
            ip_hdrlen(skb) < sizeof(struct iphdr))
                return NF_ACCEPT;
 
-       ret = nf_nat_ipv4_fn(ops, skb, state, do_chain);
+       ret = nf_nat_ipv4_fn(priv, skb, state, do_chain);
 #ifdef CONFIG_XFRM
        if (ret != NF_DROP && ret != NF_STOLEN &&
            !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
@@ -396,7 +396,7 @@ nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
                    (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
                     ct->tuplehash[dir].tuple.src.u.all !=
                     ct->tuplehash[!dir].tuple.dst.u.all)) {
-                       err = nf_xfrm_me_harder(skb, AF_INET);
+                       err = nf_xfrm_me_harder(state->net, skb, AF_INET);
                        if (err < 0)
                                ret = NF_DROP_ERR(err);
                }
@@ -407,9 +407,9 @@ nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
 EXPORT_SYMBOL_GPL(nf_nat_ipv4_out);
 
 unsigned int
-nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
+nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb,
                     const struct nf_hook_state *state,
-                    unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+                    unsigned int (*do_chain)(void *priv,
                                               struct sk_buff *skb,
                                               const struct nf_hook_state *state,
                                               struct nf_conn *ct))
@@ -424,14 +424,14 @@ nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
            ip_hdrlen(skb) < sizeof(struct iphdr))
                return NF_ACCEPT;
 
-       ret = nf_nat_ipv4_fn(ops, skb, state, do_chain);
+       ret = nf_nat_ipv4_fn(priv, skb, state, do_chain);
        if (ret != NF_DROP && ret != NF_STOLEN &&
            (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
                enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
 
                if (ct->tuplehash[dir].tuple.dst.u3.ip !=
                    ct->tuplehash[!dir].tuple.src.u3.ip) {
-                       err = ip_route_me_harder(skb, RTN_UNSPEC);
+                       err = ip_route_me_harder(state->net, skb, RTN_UNSPEC);
                        if (err < 0)
                                ret = NF_DROP_ERR(err);
                }
@@ -440,7 +440,7 @@ nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
                         ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
                         ct->tuplehash[dir].tuple.dst.u.all !=
                         ct->tuplehash[!dir].tuple.src.u.all) {
-                       err = nf_xfrm_me_harder(skb, AF_INET);
+                       err = nf_xfrm_me_harder(state->net, skb, AF_INET);
                        if (err < 0)
                                ret = NF_DROP_ERR(err);
                }
index 7c676671329d9432eb2392f6b4fc649ebcdae97f..ddb894ac1458ca40bf30d213e8b3b2c8a18880af 100644 (file)
@@ -1156,7 +1156,7 @@ static int snmp_parse_mangle(unsigned char *msg,
                }
 
                if (obj->type == SNMP_IPADDR)
-                       mangle_address(ctx.begin, ctx.pointer - 4 , map, check);
+                       mangle_address(ctx.begin, ctx.pointer - 4, map, check);
 
                kfree(obj->id);
                kfree(obj);
index 3262e41ff76f38a89db3fd7da8c771a51b273abf..c747b2d9eb7703388762c71a7222a567e2ce5cde 100644 (file)
@@ -99,7 +99,7 @@ void nf_reject_ip_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb,
 EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put);
 
 /* Send RST reply */
-void nf_send_reset(struct sk_buff *oldskb, int hook)
+void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook)
 {
        struct sk_buff *nskb;
        const struct iphdr *oiph;
@@ -129,7 +129,7 @@ void nf_send_reset(struct sk_buff *oldskb, int hook)
                                   ip4_dst_hoplimit(skb_dst(nskb)));
        nf_reject_ip_tcphdr_put(nskb, oldskb, oth);
 
-       if (ip_route_me_harder(nskb, RTN_UNSPEC))
+       if (ip_route_me_harder(net, nskb, RTN_UNSPEC))
                goto free_nskb;
 
        /* "Never happens" */
@@ -157,7 +157,7 @@ void nf_send_reset(struct sk_buff *oldskb, int hook)
                dev_queue_xmit(nskb);
        } else
 #endif
-               ip_local_out(nskb);
+               ip_local_out(net, nskb->sk, nskb);
 
        return;
 
index 8412268bbad1852851c6fc3d5337d463d1ff1c51..9d09d4f59545ccaaf0e9437bb36a8274a50bed4f 100644 (file)
 #include <net/netfilter/nf_tables.h>
 
 static unsigned int
-nft_do_chain_arp(const struct nf_hook_ops *ops,
+nft_do_chain_arp(void *priv,
                  struct sk_buff *skb,
                  const struct nf_hook_state *state)
 {
        struct nft_pktinfo pkt;
 
-       nft_set_pktinfo(&pkt, ops, skb, state);
+       nft_set_pktinfo(&pkt, skb, state);
 
-       return nft_do_chain(&pkt, ops);
+       return nft_do_chain(&pkt, priv);
 }
 
 static struct nft_af_info nft_af_arp __read_mostly = {
index aa180d3a69a5a196e65fdc46cad267944ef6c5cc..ca9dc3c46c4fd6e0af9ba01a0afc5e00f963374f 100644 (file)
 #include <net/ip.h>
 #include <net/netfilter/nf_tables_ipv4.h>
 
-static unsigned int nft_do_chain_ipv4(const struct nf_hook_ops *ops,
+static unsigned int nft_do_chain_ipv4(void *priv,
                                      struct sk_buff *skb,
                                      const struct nf_hook_state *state)
 {
        struct nft_pktinfo pkt;
 
-       nft_set_pktinfo_ipv4(&pkt, ops, skb, state);
+       nft_set_pktinfo_ipv4(&pkt, skb, state);
 
-       return nft_do_chain(&pkt, ops);
+       return nft_do_chain(&pkt, priv);
 }
 
-static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops,
+static unsigned int nft_ipv4_output(void *priv,
                                    struct sk_buff *skb,
                                    const struct nf_hook_state *state)
 {
@@ -41,7 +41,7 @@ static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops,
                return NF_ACCEPT;
        }
 
-       return nft_do_chain_ipv4(ops, skb, state);
+       return nft_do_chain_ipv4(priv, skb, state);
 }
 
 struct nft_af_info nft_af_ipv4 __read_mostly = {
index bf5c30ae14e4e768b61dd758be2acef6e8a0aa86..f5c66a7a4bf25fcdcd11edabc742c3520552550a 100644 (file)
 #include <net/netfilter/nf_nat_l3proto.h>
 #include <net/ip.h>
 
-static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops,
+static unsigned int nft_nat_do_chain(void *priv,
                                      struct sk_buff *skb,
                                      const struct nf_hook_state *state,
                                      struct nf_conn *ct)
 {
        struct nft_pktinfo pkt;
 
-       nft_set_pktinfo_ipv4(&pkt, ops, skb, state);
+       nft_set_pktinfo_ipv4(&pkt, skb, state);
 
-       return nft_do_chain(&pkt, ops);
+       return nft_do_chain(&pkt, priv);
 }
 
-static unsigned int nft_nat_ipv4_fn(const struct nf_hook_ops *ops,
+static unsigned int nft_nat_ipv4_fn(void *priv,
                                    struct sk_buff *skb,
                                    const struct nf_hook_state *state)
 {
-       return nf_nat_ipv4_fn(ops, skb, state, nft_nat_do_chain);
+       return nf_nat_ipv4_fn(priv, skb, state, nft_nat_do_chain);
 }
 
-static unsigned int nft_nat_ipv4_in(const struct nf_hook_ops *ops,
+static unsigned int nft_nat_ipv4_in(void *priv,
                                    struct sk_buff *skb,
                                    const struct nf_hook_state *state)
 {
-       return nf_nat_ipv4_in(ops, skb, state, nft_nat_do_chain);
+       return nf_nat_ipv4_in(priv, skb, state, nft_nat_do_chain);
 }
 
-static unsigned int nft_nat_ipv4_out(const struct nf_hook_ops *ops,
+static unsigned int nft_nat_ipv4_out(void *priv,
                                     struct sk_buff *skb,
                                     const struct nf_hook_state *state)
 {
-       return nf_nat_ipv4_out(ops, skb, state, nft_nat_do_chain);
+       return nf_nat_ipv4_out(priv, skb, state, nft_nat_do_chain);
 }
 
-static unsigned int nft_nat_ipv4_local_fn(const struct nf_hook_ops *ops,
+static unsigned int nft_nat_ipv4_local_fn(void *priv,
                                          struct sk_buff *skb,
                                          const struct nf_hook_state *state)
 {
-       return nf_nat_ipv4_local_fn(ops, skb, state, nft_nat_do_chain);
+       return nf_nat_ipv4_local_fn(priv, skb, state, nft_nat_do_chain);
 }
 
 static const struct nf_chain_type nft_chain_nat_ipv4 = {
index e335b0afdaf33405f05804e72bcadf48cac5c3ea..2375b0a8be4618941e1e4d10fb54089d4f82ea79 100644 (file)
@@ -21,7 +21,7 @@
 #include <net/route.h>
 #include <net/ip.h>
 
-static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
+static unsigned int nf_route_table_hook(void *priv,
                                        struct sk_buff *skb,
                                        const struct nf_hook_state *state)
 {
@@ -37,7 +37,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
            ip_hdrlen(skb) < sizeof(struct iphdr))
                return NF_ACCEPT;
 
-       nft_set_pktinfo_ipv4(&pkt, ops, skb, state);
+       nft_set_pktinfo_ipv4(&pkt, skb, state);
 
        mark = skb->mark;
        iph = ip_hdr(skb);
@@ -45,7 +45,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
        daddr = iph->daddr;
        tos = iph->tos;
 
-       ret = nft_do_chain(&pkt, ops);
+       ret = nft_do_chain(&pkt, priv);
        if (ret != NF_DROP && ret != NF_QUEUE) {
                iph = ip_hdr(skb);
 
@@ -53,7 +53,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
                    iph->daddr != daddr ||
                    skb->mark != mark ||
                    iph->tos != tos)
-                       if (ip_route_me_harder(skb, RTN_UNSPEC))
+                       if (ip_route_me_harder(state->net, skb, RTN_UNSPEC))
                                ret = NF_DROP;
        }
        return ret;
index b45932d43b69f31245886626f82635b1e7b53bbb..bf855e64fc45c86fbf286bc34b2bd96fbac17cff 100644 (file)
@@ -30,7 +30,7 @@ static void nft_dup_ipv4_eval(const struct nft_expr *expr,
        };
        int oif = regs->data[priv->sreg_dev];
 
-       nf_dup_ipv4(pkt->skb, pkt->ops->hooknum, &gw, oif);
+       nf_dup_ipv4(pkt->net, pkt->skb, pkt->hook, &gw, oif);
 }
 
 static int nft_dup_ipv4_init(const struct nft_ctx *ctx,
index 40e414c4ca56b49909fcde487dead284e5463990..b72ffc58e2556994eba4b79f69a1630c6177d248 100644 (file)
@@ -26,7 +26,7 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr,
        memset(&range, 0, sizeof(range));
        range.flags = priv->flags;
 
-       regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, pkt->ops->hooknum,
+       regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, pkt->hook,
                                                    &range, pkt->out);
 }
 
index d8d795df9c13c562880a6f1d074226984e5400de..c09d4381427ea91d522fc6fd6ff961541f24956a 100644 (file)
@@ -36,7 +36,7 @@ static void nft_redir_ipv4_eval(const struct nft_expr *expr,
        mr.range[0].flags |= priv->flags;
 
        regs->verdict.code = nf_nat_redirect_ipv4(pkt->skb, &mr,
-                                                 pkt->ops->hooknum);
+                                                 pkt->hook);
 }
 
 static struct nft_expr_type nft_redir_ipv4_type;
index b07e58b51158514496ce7b136e04eca3266058ce..c24f41c816b33f22b7e31ffb0b53e963b296f8c1 100644 (file)
@@ -27,11 +27,10 @@ static void nft_reject_ipv4_eval(const struct nft_expr *expr,
 
        switch (priv->type) {
        case NFT_REJECT_ICMP_UNREACH:
-               nf_send_unreach(pkt->skb, priv->icmp_code,
-                               pkt->ops->hooknum);
+               nf_send_unreach(pkt->skb, priv->icmp_code, pkt->hook);
                break;
        case NFT_REJECT_TCP_RST:
-               nf_send_reset(pkt->skb, pkt->ops->hooknum);
+               nf_send_reset(pkt->net, pkt->skb, pkt->hook);
                break;
        default:
                break;
index 561cd4b8fc6e07b49222d788651e93c42ee3adcb..8c0d0bdc2a7c59ad44752f2d378a07d505753a8b 100644 (file)
@@ -411,8 +411,9 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
                icmp_out_count(net, ((struct icmphdr *)
                        skb_transport_header(skb))->type);
 
-       err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb,
-                     NULL, rt->dst.dev, dst_output_sk);
+       err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
+                     net, sk, skb, NULL, rt->dst.dev,
+                     dst_output);
        if (err > 0)
                err = net_xmit_errno(err);
        if (err)
@@ -483,6 +484,7 @@ static int raw_getfrag(void *from, char *to, int offset, int len, int odd,
 static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 {
        struct inet_sock *inet = inet_sk(sk);
+       struct net *net = sock_net(sk);
        struct ipcm_cookie ipc;
        struct rtable *rt = NULL;
        struct flowi4 fl4;
@@ -542,7 +544,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        ipc.oif = sk->sk_bound_dev_if;
 
        if (msg->msg_controllen) {
-               err = ip_cmsg_send(sock_net(sk), msg, &ipc, false);
+               err = ip_cmsg_send(net, msg, &ipc, false);
                if (err)
                        goto out;
                if (ipc.opt)
@@ -597,6 +599,9 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
                            (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
                           daddr, saddr, 0, 0);
 
+       if (!saddr && ipc.oif)
+               l3mdev_get_saddr(net, ipc.oif, &fl4);
+
        if (!inet->hdrincl) {
                rfv.msg = msg;
                rfv.hlen = 0;
@@ -607,7 +612,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        }
 
        security_sk_classify_flow(sk, flowi4_to_flowi(&fl4));
-       rt = ip_route_output_flow(sock_net(sk), &fl4, sk);
+       rt = ip_route_output_flow(net, &fl4, sk);
        if (IS_ERR(rt)) {
                err = PTR_ERR(rt);
                rt = NULL;
index c81deb85acb4da84480f2f54555f4d7cdcbbb209..85f184e429c63c8c426f58c779dc3544cf2e2a54 100644 (file)
 #endif
 #include <net/secure_seq.h>
 #include <net/ip_tunnels.h>
-#include <net/vrf.h>
+#include <net/l3mdev.h>
 
 #define RT_FL_TOS(oldflp4) \
        ((oldflp4)->flowi4_tos & (IPTOS_RT_MASK | RTO_ONLINK))
@@ -847,7 +847,7 @@ void ip_rt_send_redirect(struct sk_buff *skb)
                return;
        }
        log_martians = IN_DEV_LOG_MARTIANS(in_dev);
-       vif = vrf_master_ifindex_rcu(rt->dst.dev);
+       vif = l3mdev_master_ifindex_rcu(rt->dst.dev);
        rcu_read_unlock();
 
        net = dev_net(rt->dst.dev);
@@ -941,7 +941,7 @@ static int ip_error(struct sk_buff *skb)
        }
 
        peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr,
-                              vrf_master_ifindex(skb->dev), 1);
+                              l3mdev_master_ifindex(skb->dev), 1);
 
        send = true;
        if (peer) {
@@ -1152,7 +1152,7 @@ static void ipv4_link_failure(struct sk_buff *skb)
                dst_set_expires(&rt->dst, 0);
 }
 
-static int ip_rt_bug(struct sock *sk, struct sk_buff *skb)
+static int ip_rt_bug(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        pr_debug("%s: %pI4 -> %pI4, %s\n",
                 __func__, &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr,
@@ -1438,12 +1438,34 @@ static void rt_set_nexthop(struct rtable *rt, __be32 daddr,
 }
 
 static struct rtable *rt_dst_alloc(struct net_device *dev,
+                                  unsigned int flags, u16 type,
                                   bool nopolicy, bool noxfrm, bool will_cache)
 {
-       return dst_alloc(&ipv4_dst_ops, dev, 1, DST_OBSOLETE_FORCE_CHK,
-                        (will_cache ? 0 : (DST_HOST | DST_NOCACHE)) |
-                        (nopolicy ? DST_NOPOLICY : 0) |
-                        (noxfrm ? DST_NOXFRM : 0));
+       struct rtable *rt;
+
+       rt = dst_alloc(&ipv4_dst_ops, dev, 1, DST_OBSOLETE_FORCE_CHK,
+                      (will_cache ? 0 : (DST_HOST | DST_NOCACHE)) |
+                      (nopolicy ? DST_NOPOLICY : 0) |
+                      (noxfrm ? DST_NOXFRM : 0));
+
+       if (rt) {
+               rt->rt_genid = rt_genid_ipv4(dev_net(dev));
+               rt->rt_flags = flags;
+               rt->rt_type = type;
+               rt->rt_is_input = 0;
+               rt->rt_iif = 0;
+               rt->rt_pmtu = 0;
+               rt->rt_gateway = 0;
+               rt->rt_uses_gateway = 0;
+               rt->rt_table_id = 0;
+               INIT_LIST_HEAD(&rt->rt_uncached);
+
+               rt->dst.output = ip_output;
+               if (flags & RTCF_LOCAL)
+                       rt->dst.input = ip_local_deliver;
+       }
+
+       return rt;
 }
 
 /* called in rcu_read_lock() section */
@@ -1452,6 +1474,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
 {
        struct rtable *rth;
        struct in_device *in_dev = __in_dev_get_rcu(dev);
+       unsigned int flags = RTCF_MULTICAST;
        u32 itag = 0;
        int err;
 
@@ -1464,9 +1487,8 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
            skb->protocol != htons(ETH_P_IP))
                goto e_inval;
 
-       if (likely(!IN_DEV_ROUTE_LOCALNET(in_dev)))
-               if (ipv4_is_loopback(saddr))
-                       goto e_inval;
+       if (ipv4_is_loopback(saddr) && !IN_DEV_ROUTE_LOCALNET(in_dev))
+               goto e_inval;
 
        if (ipv4_is_zeronet(saddr)) {
                if (!ipv4_is_local_multicast(daddr))
@@ -1477,7 +1499,10 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
                if (err < 0)
                        goto e_err;
        }
-       rth = rt_dst_alloc(dev_net(dev)->loopback_dev,
+       if (our)
+               flags |= RTCF_LOCAL;
+
+       rth = rt_dst_alloc(dev_net(dev)->loopback_dev, flags, RTN_MULTICAST,
                           IN_DEV_CONF_GET(in_dev, NOPOLICY), false, false);
        if (!rth)
                goto e_nobufs;
@@ -1486,20 +1511,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
        rth->dst.tclassid = itag;
 #endif
        rth->dst.output = ip_rt_bug;
-
-       rth->rt_genid   = rt_genid_ipv4(dev_net(dev));
-       rth->rt_flags   = RTCF_MULTICAST;
-       rth->rt_type    = RTN_MULTICAST;
        rth->rt_is_input= 1;
-       rth->rt_iif     = 0;
-       rth->rt_pmtu    = 0;
-       rth->rt_gateway = 0;
-       rth->rt_uses_gateway = 0;
-       INIT_LIST_HEAD(&rth->rt_uncached);
-       if (our) {
-               rth->dst.input= ip_local_deliver;
-               rth->rt_flags |= RTCF_LOCAL;
-       }
 
 #ifdef CONFIG_IP_MROUTE
        if (!ipv4_is_local_multicast(daddr) && IN_DEV_MFORWARD(in_dev))
@@ -1608,7 +1620,7 @@ static int __mkroute_input(struct sk_buff *skb,
                }
        }
 
-       rth = rt_dst_alloc(out_dev->dev,
+       rth = rt_dst_alloc(out_dev->dev, 0, res->type,
                           IN_DEV_CONF_GET(in_dev, NOPOLICY),
                           IN_DEV_CONF_GET(out_dev, NOXFRM), do_cache);
        if (!rth) {
@@ -1616,19 +1628,12 @@ static int __mkroute_input(struct sk_buff *skb,
                goto cleanup;
        }
 
-       rth->rt_genid = rt_genid_ipv4(dev_net(rth->dst.dev));
-       rth->rt_flags = 0;
-       rth->rt_type = res->type;
        rth->rt_is_input = 1;
-       rth->rt_iif     = 0;
-       rth->rt_pmtu    = 0;
-       rth->rt_gateway = 0;
-       rth->rt_uses_gateway = 0;
-       INIT_LIST_HEAD(&rth->rt_uncached);
+       if (res->table)
+               rth->rt_table_id = res->table->tb_id;
        RT_CACHE_STAT_INC(in_slow_tot);
 
        rth->dst.input = ip_forward;
-       rth->dst.output = ip_output;
 
        rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag);
        if (lwtunnel_output_redirect(rth->dst.lwtstate)) {
@@ -1646,6 +1651,48 @@ out:
        return err;
 }
 
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+
+/* To make ICMP packets follow the right flow, the multipath hash is
+ * calculated from the inner IP addresses in reverse order.
+ */
+static int ip_multipath_icmp_hash(struct sk_buff *skb)
+{
+       const struct iphdr *outer_iph = ip_hdr(skb);
+       struct icmphdr _icmph;
+       const struct icmphdr *icmph;
+       struct iphdr _inner_iph;
+       const struct iphdr *inner_iph;
+
+       if (unlikely((outer_iph->frag_off & htons(IP_OFFSET)) != 0))
+               goto standard_hash;
+
+       icmph = skb_header_pointer(skb, outer_iph->ihl * 4, sizeof(_icmph),
+                                  &_icmph);
+       if (!icmph)
+               goto standard_hash;
+
+       if (icmph->type != ICMP_DEST_UNREACH &&
+           icmph->type != ICMP_REDIRECT &&
+           icmph->type != ICMP_TIME_EXCEEDED &&
+           icmph->type != ICMP_PARAMETERPROB) {
+               goto standard_hash;
+       }
+
+       inner_iph = skb_header_pointer(skb,
+                                      outer_iph->ihl * 4 + sizeof(_icmph),
+                                      sizeof(_inner_iph), &_inner_iph);
+       if (!inner_iph)
+               goto standard_hash;
+
+       return fib_multipath_hash(inner_iph->daddr, inner_iph->saddr);
+
+standard_hash:
+       return fib_multipath_hash(outer_iph->saddr, outer_iph->daddr);
+}
+
+#endif /* CONFIG_IP_ROUTE_MULTIPATH */
+
 static int ip_mkroute_input(struct sk_buff *skb,
                            struct fib_result *res,
                            const struct flowi4 *fl4,
@@ -1653,8 +1700,15 @@ static int ip_mkroute_input(struct sk_buff *skb,
                            __be32 daddr, __be32 saddr, u32 tos)
 {
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-       if (res->fi && res->fi->fib_nhs > 1)
-               fib_select_multipath(res);
+       if (res->fi && res->fi->fib_nhs > 1) {
+               int h;
+
+               if (unlikely(ip_hdr(skb)->protocol == IPPROTO_ICMP))
+                       h = ip_multipath_icmp_hash(skb);
+               else
+                       h = fib_multipath_hash(saddr, daddr);
+               fib_select_multipath(res, h);
+       }
 #endif
 
        /* create a routing cache entry */
@@ -1706,6 +1760,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
                goto martian_source;
 
        res.fi = NULL;
+       res.table = NULL;
        if (ipv4_is_lbcast(daddr) || (saddr == 0 && daddr == 0))
                goto brd_input;
 
@@ -1733,7 +1788,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
         *      Now we are ready to route packet.
         */
        fl4.flowi4_oif = 0;
-       fl4.flowi4_iif = vrf_master_ifindex_rcu(dev) ? : dev->ifindex;
+       fl4.flowi4_iif = l3mdev_fib_oif_rcu(dev);
        fl4.flowi4_mark = skb->mark;
        fl4.flowi4_tos = tos;
        fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
@@ -1754,7 +1809,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
                err = fib_validate_source(skb, saddr, daddr, tos,
                                          0, dev, in_dev, &itag);
                if (err < 0)
-                       goto martian_source_keep_err;
+                       goto martian_source;
                goto local_input;
        }
 
@@ -1776,7 +1831,7 @@ brd_input:
                err = fib_validate_source(skb, saddr, 0, tos, 0, dev,
                                          in_dev, &itag);
                if (err < 0)
-                       goto martian_source_keep_err;
+                       goto martian_source;
        }
        flags |= RTCF_BROADCAST;
        res.type = RTN_BROADCAST;
@@ -1796,26 +1851,18 @@ local_input:
                }
        }
 
-       rth = rt_dst_alloc(net->loopback_dev,
+       rth = rt_dst_alloc(net->loopback_dev, flags | RTCF_LOCAL, res.type,
                           IN_DEV_CONF_GET(in_dev, NOPOLICY), false, do_cache);
        if (!rth)
                goto e_nobufs;
 
-       rth->dst.input= ip_local_deliver;
        rth->dst.output= ip_rt_bug;
 #ifdef CONFIG_IP_ROUTE_CLASSID
        rth->dst.tclassid = itag;
 #endif
-
-       rth->rt_genid = rt_genid_ipv4(net);
-       rth->rt_flags   = flags|RTCF_LOCAL;
-       rth->rt_type    = res.type;
        rth->rt_is_input = 1;
-       rth->rt_iif     = 0;
-       rth->rt_pmtu    = 0;
-       rth->rt_gateway = 0;
-       rth->rt_uses_gateway = 0;
-       INIT_LIST_HEAD(&rth->rt_uncached);
+       if (res.table)
+               rth->rt_table_id = res.table->tb_id;
 
        RT_CACHE_STAT_INC(in_slow_tot);
        if (res.type == RTN_UNREACHABLE) {
@@ -1837,6 +1884,7 @@ no_route:
        RT_CACHE_STAT_INC(in_no_route);
        res.type = RTN_UNREACHABLE;
        res.fi = NULL;
+       res.table = NULL;
        goto local_input;
 
        /*
@@ -1859,8 +1907,6 @@ e_nobufs:
        goto out;
 
 martian_source:
-       err = -EINVAL;
-martian_source_keep_err:
        ip_handle_martian_source(dev, in_dev, skb, daddr, saddr);
        goto out;
 }
@@ -1988,28 +2034,19 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
        }
 
 add:
-       rth = rt_dst_alloc(dev_out,
+       rth = rt_dst_alloc(dev_out, flags, type,
                           IN_DEV_CONF_GET(in_dev, NOPOLICY),
                           IN_DEV_CONF_GET(in_dev, NOXFRM),
                           do_cache);
        if (!rth)
                return ERR_PTR(-ENOBUFS);
 
-       rth->dst.output = ip_output;
-
-       rth->rt_genid = rt_genid_ipv4(dev_net(dev_out));
-       rth->rt_flags   = flags;
-       rth->rt_type    = type;
-       rth->rt_is_input = 0;
        rth->rt_iif     = orig_oif ? : 0;
-       rth->rt_pmtu    = 0;
-       rth->rt_gateway = 0;
-       rth->rt_uses_gateway = 0;
-       INIT_LIST_HEAD(&rth->rt_uncached);
+       if (res->table)
+               rth->rt_table_id = res->table->tb_id;
+
        RT_CACHE_STAT_INC(out_slow_tot);
 
-       if (flags & RTCF_LOCAL)
-               rth->dst.input = ip_local_deliver;
        if (flags & (RTCF_BROADCAST | RTCF_MULTICAST)) {
                if (flags & RTCF_LOCAL &&
                    !(dev_out->flags & IFF_LOOPBACK)) {
@@ -2038,7 +2075,8 @@ add:
  * Major route resolver routine.
  */
 
-struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
+struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4,
+                                         int mp_hash)
 {
        struct net_device *dev_out = NULL;
        __u8 tos = RT_FL_TOS(fl4);
@@ -2137,11 +2175,10 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
                                fl4->saddr = inet_select_addr(dev_out, 0,
                                                              RT_SCOPE_HOST);
                }
-               if (netif_is_vrf(dev_out) &&
-                   !(fl4->flowi4_flags & FLOWI_FLAG_VRFSRC)) {
-                       rth = vrf_dev_get_rth(dev_out);
+
+               rth = l3mdev_get_rtable(dev_out, fl4);
+               if (rth)
                        goto out;
-               }
        }
 
        if (!fl4->daddr) {
@@ -2159,7 +2196,8 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
        if (err) {
                res.fi = NULL;
                res.table = NULL;
-               if (fl4->flowi4_oif) {
+               if (fl4->flowi4_oif &&
+                   !netif_index_is_l3_master(net, fl4->flowi4_oif)) {
                        /* Apparently, routing tables are wrong. Assume,
                           that the destination is on link.
 
@@ -2201,18 +2239,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
                goto make_route;
        }
 
-#ifdef CONFIG_IP_ROUTE_MULTIPATH
-       if (res.fi->fib_nhs > 1 && fl4->flowi4_oif == 0)
-               fib_select_multipath(&res);
-       else
-#endif
-       if (!res.prefixlen &&
-           res.table->tb_num_default > 1 &&
-           res.type == RTN_UNICAST && !fl4->flowi4_oif)
-               fib_select_default(fl4, &res);
-
-       if (!fl4->saddr)
-               fl4->saddr = FIB_RES_PREFSRC(net, res);
+       fib_select_path(net, &res, fl4, mp_hash);
 
        dev_out = FIB_RES_DEV(res);
        fl4->flowi4_oif = dev_out->ifindex;
@@ -2225,7 +2252,7 @@ out:
        rcu_read_unlock();
        return rth;
 }
-EXPORT_SYMBOL_GPL(__ip_route_output_key);
+EXPORT_SYMBOL_GPL(__ip_route_output_key_hash);
 
 static struct dst_entry *ipv4_blackhole_dst_check(struct dst_entry *dst, u32 cookie)
 {
@@ -2277,7 +2304,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or
 
                new->__use = 1;
                new->input = dst_discard;
-               new->output = dst_discard_sk;
+               new->output = dst_discard_out;
 
                new->dev = ort->dst.dev;
                if (new->dev)
@@ -2303,7 +2330,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or
 }
 
 struct rtable *ip_route_output_flow(struct net *net, struct flowi4 *flp4,
-                                   struct sock *sk)
+                                   const struct sock *sk)
 {
        struct rtable *rt = __ip_route_output_key(net, flp4);
 
@@ -2319,7 +2346,7 @@ struct rtable *ip_route_output_flow(struct net *net, struct flowi4 *flp4,
 }
 EXPORT_SYMBOL_GPL(ip_route_output_flow);
 
-static int rt_fill_info(struct net *net,  __be32 dst, __be32 src,
+static int rt_fill_info(struct net *net,  __be32 dst, __be32 src, u32 table_id,
                        struct flowi4 *fl4, struct sk_buff *skb, u32 portid,
                        u32 seq, int event, int nowait, unsigned int flags)
 {
@@ -2339,8 +2366,8 @@ static int rt_fill_info(struct net *net,  __be32 dst, __be32 src,
        r->rtm_dst_len  = 32;
        r->rtm_src_len  = 0;
        r->rtm_tos      = fl4->flowi4_tos;
-       r->rtm_table    = RT_TABLE_MAIN;
-       if (nla_put_u32(skb, RTA_TABLE, RT_TABLE_MAIN))
+       r->rtm_table    = table_id;
+       if (nla_put_u32(skb, RTA_TABLE, table_id))
                goto nla_put_failure;
        r->rtm_type     = rt->rt_type;
        r->rtm_scope    = RT_SCOPE_UNIVERSE;
@@ -2445,6 +2472,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
        int err;
        int mark;
        struct sk_buff *skb;
+       u32 table_id = RT_TABLE_MAIN;
 
        err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy);
        if (err < 0)
@@ -2480,6 +2508,9 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
        fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0;
        fl4.flowi4_mark = mark;
 
+       if (netif_index_is_l3_master(net, fl4.flowi4_oif))
+               fl4.flowi4_flags = FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF;
+
        if (iif) {
                struct net_device *dev;
 
@@ -2514,7 +2545,10 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
        if (rtm->rtm_flags & RTM_F_NOTIFY)
                rt->rt_flags |= RTCF_NOTIFY;
 
-       err = rt_fill_info(net, dst, src, &fl4, skb,
+       if (rtm->rtm_flags & RTM_F_LOOKUP_TABLE)
+               table_id = rt->rt_table_id;
+
+       err = rt_fill_info(net, dst, src, table_id, &fl4, skb,
                           NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
                           RTM_NEWROUTE, 0, 0);
        if (err < 0)
index d70b1f603692554664c478ebb597ba73958ebd9a..4cbe9f0a428179d8c35fa5f0a05dd2b445498c11 100644 (file)
@@ -192,15 +192,11 @@ u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th,
 }
 EXPORT_SYMBOL_GPL(__cookie_v4_init_sequence);
 
-__u32 cookie_v4_init_sequence(struct sock *sk, const struct sk_buff *skb,
-                             __u16 *mssp)
+__u32 cookie_v4_init_sequence(const struct sk_buff *skb, __u16 *mssp)
 {
        const struct iphdr *iph = ip_hdr(skb);
        const struct tcphdr *th = tcp_hdr(skb);
 
-       tcp_synq_overflow(sk);
-       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT);
-
        return __cookie_v4_init_sequence(iph, th, mssp);
 }
 
@@ -225,10 +221,13 @@ struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct sock *child;
+       bool own_req;
 
-       child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst);
+       child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst,
+                                                NULL, &own_req);
        if (child) {
                atomic_set(&req->rsk_refcnt, 1);
+               sock_rps_save_rxhash(child, skb);
                inet_csk_reqsk_queue_add(sk, req, child);
        } else {
                reqsk_free(req);
@@ -288,6 +287,10 @@ bool cookie_ecn_ok(const struct tcp_options_received *tcp_opt,
 }
 EXPORT_SYMBOL(cookie_ecn_ok);
 
+/* On input, sk is a listener.
+ * Output is listener if incoming packet would not create a child
+ *           NULL if memory could not be allocated.
+ */
 struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
 {
        struct ip_options *opt = &TCP_SKB_CB(skb)->header.h4.opt;
@@ -326,7 +329,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
                goto out;
 
        ret = NULL;
-       req = inet_reqsk_alloc(&tcp_request_sock_ops, sk); /* for safety */
+       req = inet_reqsk_alloc(&tcp_request_sock_ops, sk, false); /* for safety */
        if (!req)
                goto out;
 
@@ -345,7 +348,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
        ireq->wscale_ok         = tcp_opt.wscale_ok;
        ireq->tstamp_ok         = tcp_opt.saw_tstamp;
        req->ts_recent          = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0;
-       treq->snt_synack        = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0;
+       treq->snt_synack.v64    = 0;
        treq->tfo_listener      = false;
 
        ireq->ir_iif = sk->sk_bound_dev_if;
@@ -381,10 +384,10 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
        }
 
        /* Try to redo what tcp_v4_send_synack did. */
-       req->window_clamp = tp->window_clamp ? :dst_metric(&rt->dst, RTAX_WINDOW);
+       req->rsk_window_clamp = tp->window_clamp ? :dst_metric(&rt->dst, RTAX_WINDOW);
 
        tcp_select_initial_window(tcp_full_space(sk), req->mss,
-                                 &req->rcv_wnd, &req->window_clamp,
+                                 &req->rsk_rcv_wnd, &req->rsk_window_clamp,
                                  ireq->wscale_ok, &rcv_wscale,
                                  dst_metric(&rt->dst, RTAX_INITRWND));
 
index 894da3a70aff98ac04c73f6f569f00113c042dd4..25300c5e283bc3879fa4400628d4a29141d52e3e 100644 (file)
@@ -495,6 +495,13 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
+       {
+               .procname       = "tcp_recovery",
+               .data           = &sysctl_tcp_recovery,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
        {
                .procname       = "tcp_reordering",
                .data           = &sysctl_tcp_reordering,
@@ -576,6 +583,13 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
+       {
+               .procname       = "tcp_min_rtt_wlen",
+               .data           = &sysctl_tcp_min_rtt_wlen,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec
+       },
        {
                .procname       = "tcp_low_latency",
                .data           = &sysctl_tcp_low_latency,
index b8b8fa184f7576d238b7c7c0b71b7f9c4acca4ee..0cfa7c0c1e80dae18b3aa51c3d71d021dd8b4f84 100644 (file)
@@ -388,6 +388,7 @@ void tcp_init_sock(struct sock *sk)
 
        icsk->icsk_rto = TCP_TIMEOUT_INIT;
        tp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT);
+       tp->rtt_min[0].rtt = ~0U;
 
        /* So many TCP implementations out there (incorrectly) count the
         * initial SYN frame in their delayed-ACK and congestion control
@@ -900,7 +901,8 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
         */
        if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) &&
            !tcp_passive_fastopen(sk)) {
-               if ((err = sk_stream_wait_connect(sk, &timeo)) != 0)
+               err = sk_stream_wait_connect(sk, &timeo);
+               if (err != 0)
                        goto out_err;
        }
 
@@ -967,7 +969,8 @@ new_segment:
 
                copied += copy;
                offset += copy;
-               if (!(size -= copy)) {
+               size -= copy;
+               if (!size) {
                        tcp_tx_timestamp(sk, skb);
                        goto out;
                }
@@ -988,7 +991,8 @@ wait_for_memory:
                tcp_push(sk, flags & ~MSG_MORE, mss_now,
                         TCP_NAGLE_PUSH, size_goal);
 
-               if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
+               err = sk_stream_wait_memory(sk, &timeo);
+               if (err != 0)
                        goto do_error;
 
                mss_now = tcp_send_mss(sk, &size_goal, flags);
@@ -1111,7 +1115,8 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
         */
        if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) &&
            !tcp_passive_fastopen(sk)) {
-               if ((err = sk_stream_wait_connect(sk, &timeo)) != 0)
+               err = sk_stream_wait_connect(sk, &timeo);
+               if (err != 0)
                        goto do_error;
        }
 
@@ -1267,7 +1272,8 @@ wait_for_memory:
                        tcp_push(sk, flags & ~MSG_MORE, mss_now,
                                 TCP_NAGLE_PUSH, size_goal);
 
-               if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
+               err = sk_stream_wait_memory(sk, &timeo);
+               if (err != 0)
                        goto do_error;
 
                mss_now = tcp_send_mss(sk, &size_goal, flags);
@@ -1767,7 +1773,8 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
 
                        /* __ Restore normal policy in scheduler __ */
 
-                       if ((chunk = len - tp->ucopy.len) != 0) {
+                       chunk = len - tp->ucopy.len;
+                       if (chunk != 0) {
                                NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG, chunk);
                                len -= chunk;
                                copied += chunk;
@@ -1778,7 +1785,8 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
 do_prequeue:
                                tcp_prequeue_process(sk);
 
-                               if ((chunk = len - tp->ucopy.len) != 0) {
+                               chunk = len - tp->ucopy.len;
+                               if (chunk != 0) {
                                        NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, chunk);
                                        len -= chunk;
                                        copied += chunk;
@@ -2230,7 +2238,8 @@ int tcp_disconnect(struct sock *sk, int flags)
        sk->sk_shutdown = 0;
        sock_reset_flag(sk, SOCK_DONE);
        tp->srtt_us = 0;
-       if ((tp->write_seq += tp->max_window + 2) == 0)
+       tp->write_seq += tp->max_window + 2;
+       if (tp->write_seq == 0)
                tp->write_seq = 1;
        icsk->icsk_backoff = 0;
        tp->snd_cwnd = 2;
@@ -2253,13 +2262,6 @@ int tcp_disconnect(struct sock *sk, int flags)
 }
 EXPORT_SYMBOL(tcp_disconnect);
 
-void tcp_sock_destruct(struct sock *sk)
-{
-       inet_sock_destruct(sk);
-
-       kfree(inet_csk(sk)->icsk_accept_queue.fastopenq);
-}
-
 static inline bool tcp_can_repair_sock(const struct sock *sk)
 {
        return ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN) &&
@@ -2581,7 +2583,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
                    TCPF_LISTEN))) {
                        tcp_fastopen_init_key_once(true);
 
-                       err = fastopen_init_queue(sk, val);
+                       fastopen_queue_tune(sk, val);
                } else {
                        err = -EINVAL;
                }
@@ -2849,10 +2851,7 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
                break;
 
        case TCP_FASTOPEN:
-               if (icsk->icsk_accept_queue.fastopenq)
-                       val = icsk->icsk_accept_queue.fastopenq->max_qlen;
-               else
-                       val = 0;
+               val = icsk->icsk_accept_queue.fastopenq.max_qlen;
                break;
 
        case TCP_TIMESTAMP:
index 93c4dc3ab23fdf224b48247584787f90916a1af5..882caa4e72bc25b478006f69c7fde2d8e7d4aa06 100644 (file)
@@ -173,6 +173,10 @@ out:
         */
        if (ca->get_info)
                memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
+       if (ca->flags & TCP_CONG_NEEDS_ECN)
+               INET_ECN_xmit(sk);
+       else
+               INET_ECN_dontxmit(sk);
 }
 
 void tcp_init_congestion_control(struct sock *sk)
@@ -181,6 +185,10 @@ void tcp_init_congestion_control(struct sock *sk)
 
        if (icsk->icsk_ca_ops->init)
                icsk->icsk_ca_ops->init(sk);
+       if (tcp_ca_needs_ecn(sk))
+               INET_ECN_xmit(sk);
+       else
+               INET_ECN_dontxmit(sk);
 }
 
 static void tcp_reinit_congestion_control(struct sock *sk,
@@ -192,8 +200,8 @@ static void tcp_reinit_congestion_control(struct sock *sk,
        icsk->icsk_ca_ops = ca;
        icsk->icsk_ca_setsockopt = 1;
 
-       if (sk->sk_state != TCP_CLOSE && icsk->icsk_ca_ops->init)
-               icsk->icsk_ca_ops->init(sk);
+       if (sk->sk_state != TCP_CLOSE)
+               tcp_init_congestion_control(sk);
 }
 
 /* Manage refcounts on socket close. */
index 7092a61c4dc8465fcf17ff71b289cf25bbb8b559..7e538f71f5fbae087c3e3e4367d60e08cd609ac5 100644 (file)
@@ -209,7 +209,7 @@ static void dctcp_update_alpha(struct sock *sk, u32 flags)
 
                /* alpha = (1 - g) * alpha + g * F */
 
-               alpha -= alpha >> dctcp_shift_g;
+               alpha -= min_not_zero(alpha, alpha >> dctcp_shift_g);
                if (bytes_ecn) {
                        /* If dctcp_shift_g == 1, a 32bit value would overflow
                         * after 8 Mbytes.
index f9c0fb84e4352be975c12067bbe2b0ccac36f1b0..55be6ac70cff3679cd7a80aa9aaac48ac156a203 100644 (file)
@@ -124,27 +124,29 @@ static bool tcp_fastopen_cookie_gen(struct request_sock *req,
        return false;
 }
 
-static bool tcp_fastopen_create_child(struct sock *sk,
-                                     struct sk_buff *skb,
-                                     struct dst_entry *dst,
-                                     struct request_sock *req)
+static struct sock *tcp_fastopen_create_child(struct sock *sk,
+                                             struct sk_buff *skb,
+                                             struct dst_entry *dst,
+                                             struct request_sock *req)
 {
        struct tcp_sock *tp;
        struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
        struct sock *child;
        u32 end_seq;
+       bool own_req;
 
        req->num_retrans = 0;
        req->num_timeout = 0;
        req->sk = NULL;
 
-       child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
+       child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
+                                                        NULL, &own_req);
        if (!child)
-               return false;
+               return NULL;
 
-       spin_lock(&queue->fastopenq->lock);
-       queue->fastopenq->qlen++;
-       spin_unlock(&queue->fastopenq->lock);
+       spin_lock(&queue->fastopenq.lock);
+       queue->fastopenq.qlen++;
+       spin_unlock(&queue->fastopenq.lock);
 
        /* Initialize the child socket. Have to fix some values to take
         * into account the child is a Fast Open socket and is created
@@ -161,15 +163,13 @@ static bool tcp_fastopen_create_child(struct sock *sk,
        tp->snd_wnd = ntohs(tcp_hdr(skb)->window);
 
        /* Activate the retrans timer so that SYNACK can be retransmitted.
-        * The request socket is not added to the SYN table of the parent
+        * The request socket is not added to the ehash
         * because it's been added to the accept queue directly.
         */
        inet_csk_reset_xmit_timer(child, ICSK_TIME_RETRANS,
                                  TCP_TIMEOUT_INIT, TCP_RTO_MAX);
 
-       atomic_set(&req->rsk_refcnt, 1);
-       /* Add the child socket directly into the accept queue */
-       inet_csk_reqsk_queue_add(sk, req, child);
+       atomic_set(&req->rsk_refcnt, 2);
 
        /* Now finish processing the fastopen child socket. */
        inet_csk(child)->icsk_af_ops->rebuild_header(child);
@@ -178,12 +178,10 @@ static bool tcp_fastopen_create_child(struct sock *sk,
        tcp_init_metrics(child);
        tcp_init_buffer_space(child);
 
-       /* Queue the data carried in the SYN packet. We need to first
-        * bump skb's refcnt because the caller will attempt to free it.
-        * Note that IPv6 might also have used skb_get() trick
-        * in tcp_v6_conn_request() to keep this SYN around (treq->pktopts)
-        * So we need to eventually get a clone of the packet,
-        * before inserting it in sk_receive_queue.
+       /* Queue the data carried in the SYN packet.
+        * We used to play tricky games with skb_get().
+        * With lockless listener, it is a dead end.
+        * Do not think about it.
         *
         * XXX (TFO) - we honor a zero-payload TFO request for now,
         * (any reason not to?) but no need to queue the skb since
@@ -191,12 +189,7 @@ static bool tcp_fastopen_create_child(struct sock *sk,
         */
        end_seq = TCP_SKB_CB(skb)->end_seq;
        if (end_seq != TCP_SKB_CB(skb)->seq + 1) {
-               struct sk_buff *skb2;
-
-               if (unlikely(skb_shared(skb)))
-                       skb2 = skb_clone(skb, GFP_ATOMIC);
-               else
-                       skb2 = skb_get(skb);
+               struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 
                if (likely(skb2)) {
                        skb_dst_drop(skb2);
@@ -214,11 +207,10 @@ static bool tcp_fastopen_create_child(struct sock *sk,
                }
        }
        tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = end_seq;
-       sk->sk_data_ready(sk);
-       bh_unlock_sock(child);
-       sock_put(child);
-       WARN_ON(!req->sk);
-       return true;
+       /* tcp_conn_request() is sending the SYNACK,
+        * and queues the child into listener accept queue.
+        */
+       return child;
 }
 
 static bool tcp_fastopen_queue_check(struct sock *sk)
@@ -235,8 +227,8 @@ static bool tcp_fastopen_queue_check(struct sock *sk)
         * between qlen overflow causing Fast Open to be disabled
         * temporarily vs a server not supporting Fast Open at all.
         */
-       fastopenq = inet_csk(sk)->icsk_accept_queue.fastopenq;
-       if (!fastopenq || fastopenq->max_qlen == 0)
+       fastopenq = &inet_csk(sk)->icsk_accept_queue.fastopenq;
+       if (fastopenq->max_qlen == 0)
                return false;
 
        if (fastopenq->qlen >= fastopenq->max_qlen) {
@@ -261,13 +253,14 @@ static bool tcp_fastopen_queue_check(struct sock *sk)
  * may be updated and return the client in the SYN-ACK later. E.g., Fast Open
  * cookie request (foc->len == 0).
  */
-bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
-                     struct request_sock *req,
-                     struct tcp_fastopen_cookie *foc,
-                     struct dst_entry *dst)
+struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
+                             struct request_sock *req,
+                             struct tcp_fastopen_cookie *foc,
+                             struct dst_entry *dst)
 {
        struct tcp_fastopen_cookie valid_foc = { .len = -1 };
        bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1;
+       struct sock *child;
 
        if (foc->len == 0) /* Client requests a cookie */
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENCOOKIEREQD);
@@ -276,7 +269,7 @@ bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
              (syn_data || foc->len >= 0) &&
              tcp_fastopen_queue_check(sk))) {
                foc->len = -1;
-               return false;
+               return NULL;
        }
 
        if (syn_data && (sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD))
@@ -296,11 +289,12 @@ bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
                 * data in SYN_RECV state.
                 */
 fastopen:
-               if (tcp_fastopen_create_child(sk, skb, dst, req)) {
+               child = tcp_fastopen_create_child(sk, skb, dst, req);
+               if (child) {
                        foc->len = -1;
                        NET_INC_STATS_BH(sock_net(sk),
                                         LINUX_MIB_TCPFASTOPENPASSIVE);
-                       return true;
+                       return child;
                }
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
        } else if (foc->len > 0) /* Client presents an invalid cookie */
@@ -308,6 +302,5 @@ fastopen:
 
        valid_foc.exp = foc->exp;
        *foc = valid_foc;
-       return false;
+       return NULL;
 }
-EXPORT_SYMBOL(tcp_try_fastopen);
index a8f515bb19c4bbd2d379c6f5c29f9a3d02e8bc96..fdd88c3803a673881053039cdc8ff44bc1b8aa4a 100644 (file)
@@ -95,6 +95,7 @@ int sysctl_tcp_stdurg __read_mostly;
 int sysctl_tcp_rfc1337 __read_mostly;
 int sysctl_tcp_max_orphans __read_mostly = NR_FILE;
 int sysctl_tcp_frto __read_mostly = 2;
+int sysctl_tcp_min_rtt_wlen __read_mostly = 300;
 
 int sysctl_tcp_thin_dupack __read_mostly;
 
@@ -880,6 +881,7 @@ static void tcp_update_reordering(struct sock *sk, const int metric,
 
        if (metric > 0)
                tcp_disable_early_retrans(tp);
+       tp->rack.reord = 1;
 }
 
 /* This must be called before lost_out is incremented */
@@ -905,8 +907,7 @@ static void tcp_skb_mark_lost(struct tcp_sock *tp, struct sk_buff *skb)
        }
 }
 
-static void tcp_skb_mark_lost_uncond_verify(struct tcp_sock *tp,
-                                           struct sk_buff *skb)
+void tcp_skb_mark_lost_uncond_verify(struct tcp_sock *tp, struct sk_buff *skb)
 {
        tcp_verify_retransmit_hint(tp, skb);
 
@@ -1047,70 +1048,6 @@ static bool tcp_is_sackblock_valid(struct tcp_sock *tp, bool is_dsack,
        return !before(start_seq, end_seq - tp->max_window);
 }
 
-/* Check for lost retransmit. This superb idea is borrowed from "ratehalving".
- * Event "B". Later note: FACK people cheated me again 8), we have to account
- * for reordering! Ugly, but should help.
- *
- * Search retransmitted skbs from write_queue that were sent when snd_nxt was
- * less than what is now known to be received by the other end (derived from
- * highest SACK block). Also calculate the lowest snd_nxt among the remaining
- * retransmitted skbs to avoid some costly processing per ACKs.
- */
-static void tcp_mark_lost_retrans(struct sock *sk, int *flag)
-{
-       const struct inet_connection_sock *icsk = inet_csk(sk);
-       struct tcp_sock *tp = tcp_sk(sk);
-       struct sk_buff *skb;
-       int cnt = 0;
-       u32 new_low_seq = tp->snd_nxt;
-       u32 received_upto = tcp_highest_sack_seq(tp);
-
-       if (!tcp_is_fack(tp) || !tp->retrans_out ||
-           !after(received_upto, tp->lost_retrans_low) ||
-           icsk->icsk_ca_state != TCP_CA_Recovery)
-               return;
-
-       tcp_for_write_queue(skb, sk) {
-               u32 ack_seq = TCP_SKB_CB(skb)->ack_seq;
-
-               if (skb == tcp_send_head(sk))
-                       break;
-               if (cnt == tp->retrans_out)
-                       break;
-               if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
-                       continue;
-
-               if (!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS))
-                       continue;
-
-               /* TODO: We would like to get rid of tcp_is_fack(tp) only
-                * constraint here (see above) but figuring out that at
-                * least tp->reordering SACK blocks reside between ack_seq
-                * and received_upto is not easy task to do cheaply with
-                * the available datastructures.
-                *
-                * Whether FACK should check here for tp->reordering segs
-                * in-between one could argue for either way (it would be
-                * rather simple to implement as we could count fack_count
-                * during the walk and do tp->fackets_out - fack_count).
-                */
-               if (after(received_upto, ack_seq)) {
-                       TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
-                       tp->retrans_out -= tcp_skb_pcount(skb);
-                       *flag |= FLAG_LOST_RETRANS;
-                       tcp_skb_mark_lost_uncond_verify(tp, skb);
-                       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPLOSTRETRANSMIT);
-               } else {
-                       if (before(ack_seq, new_low_seq))
-                               new_low_seq = ack_seq;
-                       cnt += tcp_skb_pcount(skb);
-               }
-       }
-
-       if (tp->retrans_out)
-               tp->lost_retrans_low = new_low_seq;
-}
-
 static bool tcp_check_dsack(struct sock *sk, const struct sk_buff *ack_skb,
                            struct tcp_sack_block_wire *sp, int num_sacks,
                            u32 prior_snd_una)
@@ -1236,6 +1173,8 @@ static u8 tcp_sacktag_one(struct sock *sk,
                return sacked;
 
        if (!(sacked & TCPCB_SACKED_ACKED)) {
+               tcp_rack_advance(tp, xmit_time, sacked);
+
                if (sacked & TCPCB_SACKED_RETRANS) {
                        /* If the segment is not tagged as lost,
                         * we do not clear RETRANS, believing
@@ -1837,7 +1776,6 @@ advance_sp:
            ((inet_csk(sk)->icsk_ca_state != TCP_CA_Loss) || tp->undo_marker))
                tcp_update_reordering(sk, tp->fackets_out - state->reord, 0);
 
-       tcp_mark_lost_retrans(sk, &state->flag);
        tcp_verify_left_out(tp);
 out:
 
@@ -2314,14 +2252,29 @@ static inline void tcp_moderate_cwnd(struct tcp_sock *tp)
        tp->snd_cwnd_stamp = tcp_time_stamp;
 }
 
+static bool tcp_tsopt_ecr_before(const struct tcp_sock *tp, u32 when)
+{
+       return tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
+              before(tp->rx_opt.rcv_tsecr, when);
+}
+
+/* skb is spurious retransmitted if the returned timestamp echo
+ * reply is prior to the skb transmission time
+ */
+static bool tcp_skb_spurious_retrans(const struct tcp_sock *tp,
+                                    const struct sk_buff *skb)
+{
+       return (TCP_SKB_CB(skb)->sacked & TCPCB_RETRANS) &&
+              tcp_tsopt_ecr_before(tp, tcp_skb_timestamp(skb));
+}
+
 /* Nothing was retransmitted or returned timestamp is less
  * than timestamp of the first retransmission.
  */
 static inline bool tcp_packet_delayed(const struct tcp_sock *tp)
 {
        return !tp->retrans_stamp ||
-               (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
-                before(tp->rx_opt.rcv_tsecr, tp->retrans_stamp));
+              tcp_tsopt_ecr_before(tp, tp->retrans_stamp);
 }
 
 /* Undo procedures. */
@@ -2853,6 +2806,11 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked,
                }
        }
 
+       /* Use RACK to detect loss */
+       if (sysctl_tcp_recovery & TCP_RACK_LOST_RETRANS &&
+           tcp_rack_mark_lost(sk))
+               flag |= FLAG_LOST_RETRANS;
+
        /* E. Process state. */
        switch (icsk->icsk_ca_state) {
        case TCP_CA_Recovery:
@@ -2915,8 +2873,69 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked,
        tcp_xmit_retransmit_queue(sk);
 }
 
+/* Kathleen Nichols' algorithm for tracking the minimum value of
+ * a data stream over some fixed time interval. (E.g., the minimum
+ * RTT over the past five minutes.) It uses constant space and constant
+ * time per update yet almost always delivers the same minimum as an
+ * implementation that has to keep all the data in the window.
+ *
+ * The algorithm keeps track of the best, 2nd best & 3rd best min
+ * values, maintaining an invariant that the measurement time of the
+ * n'th best >= n-1'th best. It also makes sure that the three values
+ * are widely separated in the time window since that bounds the worse
+ * case error when that data is monotonically increasing over the window.
+ *
+ * Upon getting a new min, we can forget everything earlier because it
+ * has no value - the new min is <= everything else in the window by
+ * definition and it's the most recent. So we restart fresh on every new min
+ * and overwrites 2nd & 3rd choices. The same property holds for 2nd & 3rd
+ * best.
+ */
+static void tcp_update_rtt_min(struct sock *sk, u32 rtt_us)
+{
+       const u32 now = tcp_time_stamp, wlen = sysctl_tcp_min_rtt_wlen * HZ;
+       struct rtt_meas *m = tcp_sk(sk)->rtt_min;
+       struct rtt_meas rttm = { .rtt = (rtt_us ? : 1), .ts = now };
+       u32 elapsed;
+
+       /* Check if the new measurement updates the 1st, 2nd, or 3rd choices */
+       if (unlikely(rttm.rtt <= m[0].rtt))
+               m[0] = m[1] = m[2] = rttm;
+       else if (rttm.rtt <= m[1].rtt)
+               m[1] = m[2] = rttm;
+       else if (rttm.rtt <= m[2].rtt)
+               m[2] = rttm;
+
+       elapsed = now - m[0].ts;
+       if (unlikely(elapsed > wlen)) {
+               /* Passed entire window without a new min so make 2nd choice
+                * the new min & 3rd choice the new 2nd. So forth and so on.
+                */
+               m[0] = m[1];
+               m[1] = m[2];
+               m[2] = rttm;
+               if (now - m[0].ts > wlen) {
+                       m[0] = m[1];
+                       m[1] = rttm;
+                       if (now - m[0].ts > wlen)
+                               m[0] = rttm;
+               }
+       } else if (m[1].ts == m[0].ts && elapsed > wlen / 4) {
+               /* Passed a quarter of the window without a new min so
+                * take 2nd choice from the 2nd quarter of the window.
+                */
+               m[2] = m[1] = rttm;
+       } else if (m[2].ts == m[1].ts && elapsed > wlen / 2) {
+               /* Passed half the window without a new min so take the 3rd
+                * choice from the last half of the window.
+                */
+               m[2] = rttm;
+       }
+}
+
 static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
-                                     long seq_rtt_us, long sack_rtt_us)
+                                     long seq_rtt_us, long sack_rtt_us,
+                                     long ca_rtt_us)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
 
@@ -2925,9 +2944,6 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
         * Karn's algorithm forbids taking RTT if some retransmitted data
         * is acked (RFC6298).
         */
-       if (flag & FLAG_RETRANS_DATA_ACKED)
-               seq_rtt_us = -1L;
-
        if (seq_rtt_us < 0)
                seq_rtt_us = sack_rtt_us;
 
@@ -2939,11 +2955,16 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
         */
        if (seq_rtt_us < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
            flag & FLAG_ACKED)
-               seq_rtt_us = jiffies_to_usecs(tcp_time_stamp - tp->rx_opt.rcv_tsecr);
-
+               seq_rtt_us = ca_rtt_us = jiffies_to_usecs(tcp_time_stamp -
+                                                         tp->rx_opt.rcv_tsecr);
        if (seq_rtt_us < 0)
                return false;
 
+       /* ca_rtt_us >= 0 is counting on the invariant that ca_rtt_us is
+        * always taken together with ACK, SACK, or TS-opts. Any negative
+        * values will be skipped with the seq_rtt_us < 0 check above.
+        */
+       tcp_update_rtt_min(sk, ca_rtt_us);
        tcp_rtt_estimator(sk, seq_rtt_us);
        tcp_set_rto(sk);
 
@@ -2953,21 +2974,21 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
 }
 
 /* Compute time elapsed between (last) SYNACK and the ACK completing 3WHS. */
-static void tcp_synack_rtt_meas(struct sock *sk, const u32 synack_stamp)
+void tcp_synack_rtt_meas(struct sock *sk, struct request_sock *req)
 {
-       struct tcp_sock *tp = tcp_sk(sk);
-       long seq_rtt_us = -1L;
+       long rtt_us = -1L;
 
-       if (synack_stamp && !tp->total_retrans)
-               seq_rtt_us = jiffies_to_usecs(tcp_time_stamp - synack_stamp);
+       if (req && !req->num_retrans && tcp_rsk(req)->snt_synack.v64) {
+               struct skb_mstamp now;
 
-       /* If the ACK acks both the SYNACK and the (Fast Open'd) data packets
-        * sent in SYN_RECV, SYNACK RTT is the smooth RTT computed in tcp_ack()
-        */
-       if (!tp->srtt_us)
-               tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt_us, -1L);
+               skb_mstamp_get(&now);
+               rtt_us = skb_mstamp_us_delta(&now, &tcp_rsk(req)->snt_synack);
+       }
+
+       tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, rtt_us, -1L, rtt_us);
 }
 
+
 static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        const struct inet_connection_sock *icsk = inet_csk(sk);
@@ -3131,6 +3152,8 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
 
                if (sacked & TCPCB_SACKED_ACKED)
                        tp->sacked_out -= acked_pcount;
+               else if (tcp_is_sack(tp) && !tcp_skb_spurious_retrans(tp, skb))
+                       tcp_rack_advance(tp, &skb->skb_mstamp, sacked);
                if (sacked & TCPCB_LOST)
                        tp->lost_out -= acked_pcount;
 
@@ -3169,7 +3192,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
                flag |= FLAG_SACK_RENEGING;
 
        skb_mstamp_get(&now);
-       if (likely(first_ackt.v64)) {
+       if (likely(first_ackt.v64) && !(flag & FLAG_RETRANS_DATA_ACKED)) {
                seq_rtt_us = skb_mstamp_us_delta(&now, &first_ackt);
                ca_rtt_us = skb_mstamp_us_delta(&now, &last_ackt);
        }
@@ -3178,7 +3201,8 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
                ca_rtt_us = skb_mstamp_us_delta(&now, &sack->last_sackt);
        }
 
-       rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt_us, sack_rtt_us);
+       rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt_us, sack_rtt_us,
+                                       ca_rtt_us);
 
        if (flag & FLAG_ACKED) {
                tcp_rearm_rto(sk);
@@ -5472,7 +5496,7 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
 }
 
 static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
-                                        const struct tcphdr *th, unsigned int len)
+                                        const struct tcphdr *th)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
@@ -5698,15 +5722,14 @@ reset_and_undo:
  *     address independent.
  */
 
-int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
-                         const struct tcphdr *th, unsigned int len)
+int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct inet_connection_sock *icsk = inet_csk(sk);
+       const struct tcphdr *th = tcp_hdr(skb);
        struct request_sock *req;
        int queued = 0;
        bool acceptable;
-       u32 synack_stamp;
 
        tp->rx_opt.saw_tstamp = 0;
 
@@ -5750,7 +5773,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                goto discard;
 
        case TCP_SYN_SENT:
-               queued = tcp_rcv_synsent_state_process(sk, skb, th, len);
+               queued = tcp_rcv_synsent_state_process(sk, skb, th);
                if (queued >= 0)
                        return queued;
 
@@ -5785,15 +5808,16 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                if (!acceptable)
                        return 1;
 
+               if (!tp->srtt_us)
+                       tcp_synack_rtt_meas(sk, req);
+
                /* Once we leave TCP_SYN_RECV, we no longer need req
                 * so release it.
                 */
                if (req) {
-                       synack_stamp = tcp_rsk(req)->snt_synack;
                        tp->total_retrans = req->num_retrans;
                        reqsk_fastopen_remove(sk, req, false);
                } else {
-                       synack_stamp = tp->lsndtime;
                        /* Make sure socket is routed, for correct metrics. */
                        icsk->icsk_af_ops->rebuild_header(sk);
                        tcp_init_congestion_control(sk);
@@ -5816,7 +5840,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
                tp->snd_wnd = ntohs(th->window) << tp->rx_opt.snd_wscale;
                tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);
-               tcp_synack_rtt_meas(sk, synack_stamp);
 
                if (tp->rx_opt.tstamp_ok)
                        tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
@@ -6023,11 +6046,11 @@ static void tcp_openreq_init(struct request_sock *req,
 {
        struct inet_request_sock *ireq = inet_rsk(req);
 
-       req->rcv_wnd = 0;               /* So that tcp_send_synack() knows! */
+       req->rsk_rcv_wnd = 0;           /* So that tcp_send_synack() knows! */
        req->cookie_ts = 0;
        tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq;
        tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
-       tcp_rsk(req)->snt_synack = tcp_time_stamp;
+       skb_mstamp_get(&tcp_rsk(req)->snt_synack);
        tcp_rsk(req)->last_oow_ack_time = 0;
        req->mss = rx_opt->mss_clamp;
        req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0;
@@ -6043,9 +6066,11 @@ static void tcp_openreq_init(struct request_sock *req,
 }
 
 struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops,
-                                     struct sock *sk_listener)
+                                     struct sock *sk_listener,
+                                     bool attach_listener)
 {
-       struct request_sock *req = reqsk_alloc(ops, sk_listener);
+       struct request_sock *req = reqsk_alloc(ops, sk_listener,
+                                              attach_listener);
 
        if (req) {
                struct inet_request_sock *ireq = inet_rsk(req);
@@ -6065,13 +6090,13 @@ EXPORT_SYMBOL(inet_reqsk_alloc);
 /*
  * Return true if a syncookie should be sent
  */
-static bool tcp_syn_flood_action(struct sock *sk,
+static bool tcp_syn_flood_action(const struct sock *sk,
                                 const struct sk_buff *skb,
                                 const char *proto)
 {
+       struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
        const char *msg = "Dropping request";
        bool want_cookie = false;
-       struct listen_sock *lopt;
 
 #ifdef CONFIG_SYN_COOKIES
        if (sysctl_tcp_syncookies) {
@@ -6082,12 +6107,12 @@ static bool tcp_syn_flood_action(struct sock *sk,
 #endif
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDROP);
 
-       lopt = inet_csk(sk)->icsk_accept_queue.listen_opt;
-       if (!lopt->synflood_warned && sysctl_tcp_syncookies != 2) {
-               lopt->synflood_warned = 1;
+       if (!queue->synflood_warned &&
+           sysctl_tcp_syncookies != 2 &&
+           xchg(&queue->synflood_warned, 1) == 0)
                pr_info("%s: Possible SYN flooding on port %d. %s.  Check SNMP counters.\n",
                        proto, ntohs(tcp_hdr(skb)->dest), msg);
-       }
+
        return want_cookie;
 }
 
@@ -6112,16 +6137,15 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
                     const struct tcp_request_sock_ops *af_ops,
                     struct sock *sk, struct sk_buff *skb)
 {
+       struct tcp_fastopen_cookie foc = { .len = -1 };
+       __u32 isn = TCP_SKB_CB(skb)->tcp_tw_isn;
        struct tcp_options_received tmp_opt;
-       struct request_sock *req;
        struct tcp_sock *tp = tcp_sk(sk);
+       struct sock *fastopen_sk = NULL;
        struct dst_entry *dst = NULL;
-       __u32 isn = TCP_SKB_CB(skb)->tcp_tw_isn;
-       bool want_cookie = false, fastopen;
+       struct request_sock *req;
+       bool want_cookie = false;
        struct flowi fl;
-       struct tcp_fastopen_cookie foc = { .len = -1 };
-       int err;
-
 
        /* TW buckets are converted to open requests without
         * limitations, they conserve resources and peer is
@@ -6145,7 +6169,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
                goto drop;
        }
 
-       req = inet_reqsk_alloc(rsk_ops, sk);
+       req = inet_reqsk_alloc(rsk_ops, sk, !want_cookie);
        if (!req)
                goto drop;
 
@@ -6228,20 +6252,30 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
        }
 
        tcp_rsk(req)->snt_isn = isn;
+       tcp_rsk(req)->txhash = net_tx_rndhash();
        tcp_openreq_init_rwin(req, sk, dst);
-       fastopen = !want_cookie &&
-                  tcp_try_fastopen(sk, skb, req, &foc, dst);
-       err = af_ops->send_synack(sk, dst, &fl, req,
-                                 skb_get_queue_mapping(skb), &foc);
-       if (!fastopen) {
-               if (err || want_cookie)
-                       goto drop_and_free;
-
+       if (!want_cookie) {
+               tcp_reqsk_record_syn(sk, req, skb);
+               fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc, dst);
+       }
+       if (fastopen_sk) {
+               af_ops->send_synack(fastopen_sk, dst, &fl, req,
+                                   &foc, false);
+               /* Add the child socket directly into the accept queue */
+               inet_csk_reqsk_queue_add(sk, req, fastopen_sk);
+               sk->sk_data_ready(sk);
+               bh_unlock_sock(fastopen_sk);
+               sock_put(fastopen_sk);
+       } else {
                tcp_rsk(req)->tfo_listener = false;
-               af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+               if (!want_cookie)
+                       inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+               af_ops->send_synack(sk, dst, &fl, req,
+                                   &foc, !want_cookie);
+               if (want_cookie)
+                       goto drop_and_free;
        }
-       tcp_reqsk_record_syn(sk, req, skb);
-
+       reqsk_put(req);
        return 0;
 
 drop_and_release:
index 93898e093d4e655537665602337de3c6c35cab70..1c2648bbac4b22b55739dde4d92dd2ca0533f77a 100644 (file)
@@ -324,7 +324,6 @@ void tcp_req_err(struct sock *sk, u32 seq)
 
        if (seq != tcp_rsk(req)->snt_isn) {
                NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
-               reqsk_put(req);
        } else {
                /*
                 * Still in SYN_RECV, just remove it silently.
@@ -332,9 +331,10 @@ void tcp_req_err(struct sock *sk, u32 seq)
                 * created socket, and POSIX does not want network
                 * errors returned from accept().
                 */
-               NET_INC_STATS_BH(net, LINUX_MIB_LISTENDROPS);
                inet_csk_reqsk_queue_drop(req->rsk_listener, req);
+               NET_INC_STATS_BH(net, LINUX_MIB_LISTENDROPS);
        }
+       reqsk_put(req);
 }
 EXPORT_SYMBOL(tcp_req_err);
 
@@ -576,7 +576,7 @@ EXPORT_SYMBOL(tcp_v4_send_check);
  *     Exception: precedence violation. We do not implement it in any case.
  */
 
-static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
+static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
 {
        const struct tcphdr *th = tcp_hdr(skb);
        struct {
@@ -795,7 +795,7 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
        inet_twsk_put(tw);
 }
 
-static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
+static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
                                  struct request_sock *req)
 {
        /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
@@ -803,7 +803,7 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
         */
        tcp_v4_send_ack(skb, (sk->sk_state == TCP_LISTEN) ?
                        tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt,
-                       tcp_rsk(req)->rcv_nxt, req->rcv_wnd,
+                       tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd,
                        tcp_time_stamp,
                        req->ts_recent,
                        0,
@@ -818,11 +818,11 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
  *     This still operates on a request_sock only, not on a big
  *     socket.
  */
-static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
+static int tcp_v4_send_synack(const struct sock *sk, struct dst_entry *dst,
                              struct flowi *fl,
                              struct request_sock *req,
-                             u16 queue_mapping,
-                             struct tcp_fastopen_cookie *foc)
+                             struct tcp_fastopen_cookie *foc,
+                                 bool attach_req)
 {
        const struct inet_request_sock *ireq = inet_rsk(req);
        struct flowi4 fl4;
@@ -833,12 +833,11 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
        if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
                return -1;
 
-       skb = tcp_make_synack(sk, dst, req, foc);
+       skb = tcp_make_synack(sk, dst, req, foc, attach_req);
 
        if (skb) {
                __tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr);
 
-               skb_set_queue_mapping(skb, queue_mapping);
                err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr,
                                            ireq->ir_rmt_addr,
                                            ireq->opt);
@@ -865,7 +864,7 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req)
  */
 
 /* Find the Key structure for an address.  */
-struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
+struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk,
                                         const union tcp_md5_addr *addr,
                                         int family)
 {
@@ -877,7 +876,7 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
        /* caller either holds rcu_read_lock() or socket lock */
        md5sig = rcu_dereference_check(tp->md5sig_info,
                                       sock_owned_by_user(sk) ||
-                                      lockdep_is_held(&sk->sk_lock.slock));
+                                      lockdep_is_held((spinlock_t *)&sk->sk_lock.slock));
        if (!md5sig)
                return NULL;
 #if IS_ENABLED(CONFIG_IPV6)
@@ -894,7 +893,7 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
 }
 EXPORT_SYMBOL(tcp_md5_do_lookup);
 
-struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
+struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
                                         const struct sock *addr_sk)
 {
        const union tcp_md5_addr *addr;
@@ -1112,10 +1111,13 @@ clear_hash_noput:
 }
 EXPORT_SYMBOL(tcp_v4_md5_hash_skb);
 
+#endif
+
 /* Called with rcu_read_lock() */
-static bool tcp_v4_inbound_md5_hash(struct sock *sk,
+static bool tcp_v4_inbound_md5_hash(const struct sock *sk,
                                    const struct sk_buff *skb)
 {
+#ifdef CONFIG_TCP_MD5SIG
        /*
         * This gets called for each TCP segment that arrives
         * so we want to be efficient.
@@ -1165,10 +1167,12 @@ static bool tcp_v4_inbound_md5_hash(struct sock *sk,
                return true;
        }
        return false;
-}
 #endif
+       return false;
+}
 
-static void tcp_v4_init_req(struct request_sock *req, struct sock *sk_listener,
+static void tcp_v4_init_req(struct request_sock *req,
+                           const struct sock *sk_listener,
                            struct sk_buff *skb)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
@@ -1179,7 +1183,8 @@ static void tcp_v4_init_req(struct request_sock *req, struct sock *sk_listener,
        ireq->opt = tcp_v4_save_options(skb);
 }
 
-static struct dst_entry *tcp_v4_route_req(struct sock *sk, struct flowi *fl,
+static struct dst_entry *tcp_v4_route_req(const struct sock *sk,
+                                         struct flowi *fl,
                                          const struct request_sock *req,
                                          bool *strict)
 {
@@ -1218,7 +1223,6 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
        .route_req      =       tcp_v4_route_req,
        .init_seq       =       tcp_v4_init_sequence,
        .send_synack    =       tcp_v4_send_synack,
-       .queue_hash_add =       inet_csk_reqsk_queue_hash_add,
 };
 
 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
@@ -1241,9 +1245,11 @@ EXPORT_SYMBOL(tcp_v4_conn_request);
  * The three way handshake has completed - we got a valid synack -
  * now create the new socket.
  */
-struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
+struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
                                  struct request_sock *req,
-                                 struct dst_entry *dst)
+                                 struct dst_entry *dst,
+                                 struct request_sock *req_unhash,
+                                 bool *own_req)
 {
        struct inet_request_sock *ireq;
        struct inet_sock *newinet;
@@ -1277,7 +1283,6 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        newinet->mc_ttl       = ip_hdr(skb)->ttl;
        newinet->rcv_tos      = ip_hdr(skb)->tos;
        inet_csk(newsk)->icsk_ext_hdr_len = 0;
-       sk_set_txhash(newsk);
        if (inet_opt)
                inet_csk(newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen;
        newinet->inet_id = newtp->write_seq ^ jiffies;
@@ -1320,7 +1325,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 
        if (__inet_inherit_port(sk, newsk) < 0)
                goto put_and_exit;
-       __inet_hash_nolisten(newsk, NULL);
+       *own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash));
 
        return newsk;
 
@@ -1338,34 +1343,11 @@ put_and_exit:
 }
 EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
 
-static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
+static struct sock *tcp_v4_cookie_check(struct sock *sk, struct sk_buff *skb)
 {
+#ifdef CONFIG_SYN_COOKIES
        const struct tcphdr *th = tcp_hdr(skb);
-       const struct iphdr *iph = ip_hdr(skb);
-       struct request_sock *req;
-       struct sock *nsk;
-
-       req = inet_csk_search_req(sk, th->source, iph->saddr, iph->daddr);
-       if (req) {
-               nsk = tcp_check_req(sk, skb, req, false);
-               if (!nsk || nsk == sk)
-                       reqsk_put(req);
-               return nsk;
-       }
-
-       nsk = inet_lookup_established(sock_net(sk), &tcp_hashinfo, iph->saddr,
-                       th->source, iph->daddr, th->dest, inet_iif(skb));
-
-       if (nsk) {
-               if (nsk->sk_state != TCP_TIME_WAIT) {
-                       bh_lock_sock(nsk);
-                       return nsk;
-               }
-               inet_twsk_put(inet_twsk(nsk));
-               return NULL;
-       }
 
-#ifdef CONFIG_SYN_COOKIES
        if (!th->syn)
                sk = cookie_v4_check(sk, skb);
 #endif
@@ -1373,7 +1355,7 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
 }
 
 /* The socket must have it's spinlock held when we get
- * here.
+ * here, unless it is a TCP_LISTEN socket.
  *
  * We have a potential double-lock case here, so even when
  * doing backlog processing we use the BH locking scheme.
@@ -1404,13 +1386,13 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
                goto csum_err;
 
        if (sk->sk_state == TCP_LISTEN) {
-               struct sock *nsk = tcp_v4_hnd_req(sk, skb);
+               struct sock *nsk = tcp_v4_cookie_check(sk, skb);
+
                if (!nsk)
                        goto discard;
-
                if (nsk != sk) {
                        sock_rps_save_rxhash(nsk, skb);
-                       sk_mark_napi_id(sk, skb);
+                       sk_mark_napi_id(nsk, skb);
                        if (tcp_child_process(sk, nsk, skb)) {
                                rsk = nsk;
                                goto reset;
@@ -1420,7 +1402,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
        } else
                sock_rps_save_rxhash(sk, skb);
 
-       if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
+       if (tcp_rcv_state_process(sk, skb)) {
                rsk = sk;
                goto reset;
        }
@@ -1590,6 +1572,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
        TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph);
        TCP_SKB_CB(skb)->sacked  = 0;
 
+lookup:
        sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
        if (!sk)
                goto no_tcp_socket;
@@ -1598,6 +1581,33 @@ process:
        if (sk->sk_state == TCP_TIME_WAIT)
                goto do_time_wait;
 
+       if (sk->sk_state == TCP_NEW_SYN_RECV) {
+               struct request_sock *req = inet_reqsk(sk);
+               struct sock *nsk = NULL;
+
+               sk = req->rsk_listener;
+               if (tcp_v4_inbound_md5_hash(sk, skb))
+                       goto discard_and_relse;
+               if (likely(sk->sk_state == TCP_LISTEN)) {
+                       nsk = tcp_check_req(sk, skb, req, false);
+               } else {
+                       inet_csk_reqsk_queue_drop_and_put(sk, req);
+                       goto lookup;
+               }
+               if (!nsk) {
+                       reqsk_put(req);
+                       goto discard_it;
+               }
+               if (nsk == sk) {
+                       sock_hold(sk);
+                       reqsk_put(req);
+               } else if (tcp_child_process(sk, nsk, skb)) {
+                       tcp_v4_send_reset(nsk, skb);
+                       goto discard_it;
+               } else {
+                       return 0;
+               }
+       }
        if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) {
                NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
                goto discard_and_relse;
@@ -1606,25 +1616,23 @@ process:
        if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
                goto discard_and_relse;
 
-#ifdef CONFIG_TCP_MD5SIG
-       /*
-        * We really want to reject the packet as early as possible
-        * if:
-        *  o We're expecting an MD5'd packet and this is no MD5 tcp option
-        *  o There is an MD5 option and we're not expecting one
-        */
        if (tcp_v4_inbound_md5_hash(sk, skb))
                goto discard_and_relse;
-#endif
 
        nf_reset(skb);
 
        if (sk_filter(sk, skb))
                goto discard_and_relse;
 
-       sk_incoming_cpu_update(sk);
        skb->dev = NULL;
 
+       if (sk->sk_state == TCP_LISTEN) {
+               ret = tcp_v4_do_rcv(sk, skb);
+               goto put_and_return;
+       }
+
+       sk_incoming_cpu_update(sk);
+
        bh_lock_sock_nested(sk);
        tcp_sk(sk)->segs_in += max_t(u16, 1, skb_shinfo(skb)->gso_segs);
        ret = 0;
@@ -1639,6 +1647,7 @@ process:
        }
        bh_unlock_sock(sk);
 
+put_and_return:
        sock_put(sk);
 
        return ret;
@@ -1833,35 +1842,7 @@ static void *listening_get_next(struct seq_file *seq, void *cur)
        ++st->num;
        ++st->offset;
 
-       if (st->state == TCP_SEQ_STATE_OPENREQ) {
-               struct request_sock *req = cur;
-
-               icsk = inet_csk(st->syn_wait_sk);
-               req = req->dl_next;
-               while (1) {
-                       while (req) {
-                               if (req->rsk_ops->family == st->family) {
-                                       cur = req;
-                                       goto out;
-                               }
-                               req = req->dl_next;
-                       }
-                       if (++st->sbucket >= icsk->icsk_accept_queue.listen_opt->nr_table_entries)
-                               break;
-get_req:
-                       req = icsk->icsk_accept_queue.listen_opt->syn_table[st->sbucket];
-               }
-               sk        = sk_nulls_next(st->syn_wait_sk);
-               st->state = TCP_SEQ_STATE_LISTENING;
-               spin_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
-       } else {
-               icsk = inet_csk(sk);
-               spin_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
-               if (reqsk_queue_len(&icsk->icsk_accept_queue))
-                       goto start_req;
-               spin_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
-               sk = sk_nulls_next(sk);
-       }
+       sk = sk_nulls_next(sk);
 get_sk:
        sk_nulls_for_each_from(sk, node) {
                if (!net_eq(sock_net(sk), net))
@@ -1871,16 +1852,6 @@ get_sk:
                        goto out;
                }
                icsk = inet_csk(sk);
-               spin_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
-               if (reqsk_queue_len(&icsk->icsk_accept_queue)) {
-start_req:
-                       st->uid         = sock_i_uid(sk);
-                       st->syn_wait_sk = sk;
-                       st->state       = TCP_SEQ_STATE_OPENREQ;
-                       st->sbucket     = 0;
-                       goto get_req;
-               }
-               spin_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
        }
        spin_unlock_bh(&ilb->lock);
        st->offset = 0;
@@ -2012,7 +1983,6 @@ static void *tcp_seek_last_pos(struct seq_file *seq)
        void *rc = NULL;
 
        switch (st->state) {
-       case TCP_SEQ_STATE_OPENREQ:
        case TCP_SEQ_STATE_LISTENING:
                if (st->bucket >= INET_LHTABLE_SIZE)
                        break;
@@ -2071,7 +2041,6 @@ static void *tcp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
        }
 
        switch (st->state) {
-       case TCP_SEQ_STATE_OPENREQ:
        case TCP_SEQ_STATE_LISTENING:
                rc = listening_get_next(seq, v);
                if (!rc) {
@@ -2096,11 +2065,6 @@ static void tcp_seq_stop(struct seq_file *seq, void *v)
        struct tcp_iter_state *st = seq->private;
 
        switch (st->state) {
-       case TCP_SEQ_STATE_OPENREQ:
-               if (v) {
-                       struct inet_connection_sock *icsk = inet_csk(st->syn_wait_sk);
-                       spin_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
-               }
        case TCP_SEQ_STATE_LISTENING:
                if (v != SEQ_START_TOKEN)
                        spin_unlock_bh(&tcp_hashinfo.listening_hash[st->bucket].lock);
@@ -2154,7 +2118,7 @@ void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo)
 EXPORT_SYMBOL(tcp_proc_unregister);
 
 static void get_openreq4(const struct request_sock *req,
-                        struct seq_file *f, int i, kuid_t uid)
+                        struct seq_file *f, int i)
 {
        const struct inet_request_sock *ireq = inet_rsk(req);
        long delta = req->rsk_timer.expires - jiffies;
@@ -2171,7 +2135,8 @@ static void get_openreq4(const struct request_sock *req,
                1,    /* timers active (only the expire timer) */
                jiffies_delta_to_clock_t(delta),
                req->num_timeout,
-               from_kuid_munged(seq_user_ns(f), uid),
+               from_kuid_munged(seq_user_ns(f),
+                                sock_i_uid(req->rsk_listener)),
                0,  /* non standard timer */
                0, /* open_requests have no inode */
                0,
@@ -2185,7 +2150,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
        const struct tcp_sock *tp = tcp_sk(sk);
        const struct inet_connection_sock *icsk = inet_csk(sk);
        const struct inet_sock *inet = inet_sk(sk);
-       struct fastopen_queue *fastopenq = icsk->icsk_accept_queue.fastopenq;
+       const struct fastopen_queue *fastopenq = &icsk->icsk_accept_queue.fastopenq;
        __be32 dest = inet->inet_daddr;
        __be32 src = inet->inet_rcv_saddr;
        __u16 destp = ntohs(inet->inet_dport);
@@ -2272,18 +2237,12 @@ static int tcp4_seq_show(struct seq_file *seq, void *v)
        }
        st = seq->private;
 
-       switch (st->state) {
-       case TCP_SEQ_STATE_LISTENING:
-       case TCP_SEQ_STATE_ESTABLISHED:
-               if (sk->sk_state == TCP_TIME_WAIT)
-                       get_timewait4_sock(v, seq, st->num);
-               else
-                       get_tcp4_sock(v, seq, st->num);
-               break;
-       case TCP_SEQ_STATE_OPENREQ:
-               get_openreq4(v, seq, st->num, st->uid);
-               break;
-       }
+       if (sk->sk_state == TCP_TIME_WAIT)
+               get_timewait4_sock(v, seq, st->num);
+       else if (sk->sk_state == TCP_NEW_SYN_RECV)
+               get_openreq4(v, seq, st->num);
+       else
+               get_tcp4_sock(v, seq, st->num);
 out:
        seq_pad(seq, '\n');
        return 0;
index def765911ff85dfefd2c73c41b6585b33c77bcea..3575dd1e5b6775ad8a35bb3ce0e951bc01e37e7c 100644 (file)
@@ -361,30 +361,38 @@ void tcp_twsk_destructor(struct sock *sk)
 }
 EXPORT_SYMBOL_GPL(tcp_twsk_destructor);
 
+/* Warning : This function is called without sk_listener being locked.
+ * Be sure to read socket fields once, as their value could change under us.
+ */
 void tcp_openreq_init_rwin(struct request_sock *req,
-                          struct sock *sk, struct dst_entry *dst)
+                          const struct sock *sk_listener,
+                          const struct dst_entry *dst)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
-       struct tcp_sock *tp = tcp_sk(sk);
-       __u8 rcv_wscale;
+       const struct tcp_sock *tp = tcp_sk(sk_listener);
+       u16 user_mss = READ_ONCE(tp->rx_opt.user_mss);
+       int full_space = tcp_full_space(sk_listener);
        int mss = dst_metric_advmss(dst);
+       u32 window_clamp;
+       __u8 rcv_wscale;
 
-       if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)
-               mss = tp->rx_opt.user_mss;
+       if (user_mss && user_mss < mss)
+               mss = user_mss;
 
+       window_clamp = READ_ONCE(tp->window_clamp);
        /* Set this up on the first call only */
-       req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);
+       req->rsk_window_clamp = window_clamp ? : dst_metric(dst, RTAX_WINDOW);
 
        /* limit the window selection if the user enforce a smaller rx buffer */
-       if (sk->sk_userlocks & SOCK_RCVBUF_LOCK &&
-           (req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0))
-               req->window_clamp = tcp_full_space(sk);
+       if (sk_listener->sk_userlocks & SOCK_RCVBUF_LOCK &&
+           (req->rsk_window_clamp > full_space || req->rsk_window_clamp == 0))
+               req->rsk_window_clamp = full_space;
 
        /* tcp_full_space because it is guaranteed to be the first packet */
-       tcp_select_initial_window(tcp_full_space(sk),
+       tcp_select_initial_window(full_space,
                mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),
-               &req->rcv_wnd,
-               &req->window_clamp,
+               &req->rsk_rcv_wnd,
+               &req->rsk_window_clamp,
                ireq->wscale_ok,
                &rcv_wscale,
                dst_metric(dst, RTAX_INITRWND));
@@ -433,7 +441,9 @@ EXPORT_SYMBOL_GPL(tcp_ca_openreq_child);
  * Actually, we could lots of memory writes here. tp of listening
  * socket contains all necessary default parameters.
  */
-struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, struct sk_buff *skb)
+struct sock *tcp_create_openreq_child(const struct sock *sk,
+                                     struct request_sock *req,
+                                     struct sk_buff *skb)
 {
        struct sock *newsk = inet_csk_clone_lock(sk, req, GFP_ATOMIC);
 
@@ -460,6 +470,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
 
                newtp->srtt_us = 0;
                newtp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT);
+               newtp->rtt_min[0].rtt = ~0U;
                newicsk->icsk_rto = TCP_TIMEOUT_INIT;
 
                newtp->packets_out = 0;
@@ -469,7 +480,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
                newtp->snd_ssthresh = TCP_INFINITE_SSTHRESH;
                tcp_enable_early_retrans(newtp);
                newtp->tlp_high_seq = 0;
-               newtp->lsndtime = treq->snt_synack;
+               newtp->lsndtime = treq->snt_synack.stamp_jiffies;
+               newsk->sk_txhash = treq->txhash;
                newtp->last_oow_ack_time = 0;
                newtp->total_retrans = req->num_retrans;
 
@@ -501,9 +513,9 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
                        if (sysctl_tcp_fack)
                                tcp_enable_fack(newtp);
                }
-               newtp->window_clamp = req->window_clamp;
-               newtp->rcv_ssthresh = req->rcv_wnd;
-               newtp->rcv_wnd = req->rcv_wnd;
+               newtp->window_clamp = req->rsk_window_clamp;
+               newtp->rcv_ssthresh = req->rsk_rcv_wnd;
+               newtp->rcv_wnd = req->rsk_rcv_wnd;
                newtp->rx_opt.wscale_ok = ireq->wscale_ok;
                if (newtp->rx_opt.wscale_ok) {
                        newtp->rx_opt.snd_wscale = ireq->snd_wscale;
@@ -536,6 +548,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
                tcp_ecn_openreq_child(newtp, req);
                newtp->fastopen_rsk = NULL;
                newtp->syn_data_acked = 0;
+               newtp->rack.mstamp.v64 = 0;
+               newtp->rack.advanced = 0;
 
                newtp->saved_syn = req->saved_syn;
                req->saved_syn = NULL;
@@ -566,8 +580,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
        const struct tcphdr *th = tcp_hdr(skb);
        __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK);
        bool paws_reject = false;
-
-       BUG_ON(fastopen == (sk->sk_state == TCP_LISTEN));
+       bool own_req;
 
        tmp_opt.saw_tstamp = 0;
        if (th->doff > (sizeof(struct tcphdr)>>2)) {
@@ -698,7 +711,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
        /* RFC793: "first check sequence number". */
 
        if (paws_reject || !tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
-                                         tcp_rsk(req)->rcv_nxt, tcp_rsk(req)->rcv_nxt + req->rcv_wnd)) {
+                                         tcp_rsk(req)->rcv_nxt, tcp_rsk(req)->rcv_nxt + req->rsk_rcv_wnd)) {
                /* Out of window: send ACK and drop. */
                if (!(flg & TCP_FLAG_RST))
                        req->rsk_ops->send_ack(sk, skb, req);
@@ -755,16 +768,14 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
         * ESTABLISHED STATE. If it will be dropped after
         * socket is created, wait for troubles.
         */
-       child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
+       child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
+                                                        req, &own_req);
        if (!child)
                goto listen_overflow;
 
-       inet_csk_reqsk_queue_drop(sk, req);
-       inet_csk_reqsk_queue_add(sk, req, child);
-       /* Warning: caller must not call reqsk_put(req);
-        * child stole last reference on it.
-        */
-       return child;
+       sock_rps_save_rxhash(child, skb);
+       tcp_synack_rtt_meas(child, req);
+       return inet_csk_complete_hashdance(sk, child, req, own_req);
 
 listen_overflow:
        if (!sysctl_tcp_abort_on_overflow) {
@@ -811,8 +822,7 @@ int tcp_child_process(struct sock *parent, struct sock *child,
        int state = child->sk_state;
 
        if (!sock_owned_by_user(child)) {
-               ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb),
-                                           skb->len);
+               ret = tcp_rcv_state_process(child, skb);
                /* Wakeup parent, send SIGIO */
                if (state == TCP_SYN_RECV && child->sk_state != state)
                        parent->sk_data_ready(parent);
index 1100ffe4a722d7bbae1ec30ee165ecc565cc2e72..cb7ca569052c5b69f78418e9c966cb285eff1d50 100644 (file)
@@ -357,14 +357,10 @@ static void tcp_ecn_clear_syn(struct sock *sk, struct sk_buff *skb)
 }
 
 static void
-tcp_ecn_make_synack(const struct request_sock *req, struct tcphdr *th,
-                   struct sock *sk)
+tcp_ecn_make_synack(const struct request_sock *req, struct tcphdr *th)
 {
-       if (inet_rsk(req)->ecn_ok) {
+       if (inet_rsk(req)->ecn_ok)
                th->ece = 1;
-               if (tcp_ca_needs_ecn(sk))
-                       INET_ECN_xmit(sk);
-       }
 }
 
 /* Set up ECN state for a packet on a ESTABLISHED socket that is about to
@@ -612,12 +608,11 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
 }
 
 /* Set up TCP options for SYN-ACKs. */
-static unsigned int tcp_synack_options(struct sock *sk,
-                                  struct request_sock *req,
-                                  unsigned int mss, struct sk_buff *skb,
-                                  struct tcp_out_options *opts,
-                                  const struct tcp_md5sig_key *md5,
-                                  struct tcp_fastopen_cookie *foc)
+static unsigned int tcp_synack_options(struct request_sock *req,
+                                      unsigned int mss, struct sk_buff *skb,
+                                      struct tcp_out_options *opts,
+                                      const struct tcp_md5sig_key *md5,
+                                      struct tcp_fastopen_cookie *foc)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
        unsigned int remaining = MAX_TCP_OPTION_SPACE;
@@ -1827,7 +1822,7 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
 
        /* Ok, it looks like it is advisable to defer. */
 
-       if (cong_win < send_win && cong_win < skb->len)
+       if (cong_win < send_win && cong_win <= skb->len)
                *is_cwnd_limited = true;
 
        return true;
@@ -2060,7 +2055,6 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
 
                cwnd_quota = tcp_cwnd_test(tp, skb);
                if (!cwnd_quota) {
-                       is_cwnd_limited = true;
                        if (push_one == 2)
                                /* Force out a loss probe pkt. */
                                cwnd_quota = 1;
@@ -2142,6 +2136,7 @@ repair:
                /* Send one loss probe per tail loss episode. */
                if (push_one != 2)
                        tcp_schedule_loss_probe(sk);
+               is_cwnd_limited |= (tcp_packets_in_flight(tp) >= tp->snd_cwnd);
                tcp_cwnd_validate(sk, is_cwnd_limited);
                return false;
        }
@@ -2165,7 +2160,7 @@ bool tcp_schedule_loss_probe(struct sock *sk)
        /* Don't do any loss probe on a Fast Open connection before 3WHS
         * finishes.
         */
-       if (sk->sk_state == TCP_SYN_RECV)
+       if (tp->fastopen_rsk)
                return false;
 
        /* TLP is only scheduled when next timer event is RTO. */
@@ -2175,7 +2170,7 @@ bool tcp_schedule_loss_probe(struct sock *sk)
        /* Schedule a loss probe in 2*RTT for SACK capable connections
         * in Open state, that are either limited by cwnd or application.
         */
-       if (sysctl_tcp_early_retrans < 3 || !tp->srtt_us || !tp->packets_out ||
+       if (sysctl_tcp_early_retrans < 3 || !tp->packets_out ||
            !tcp_is_sack(tp) || inet_csk(sk)->icsk_ca_state != TCP_CA_Open)
                return false;
 
@@ -2184,9 +2179,10 @@ bool tcp_schedule_loss_probe(struct sock *sk)
                return false;
 
        /* Probe timeout is at least 1.5*rtt + TCP_DELACK_MAX to account
-        * for delayed ack when there's one outstanding packet.
+        * for delayed ack when there's one outstanding packet. If no RTT
+        * sample is available then probe after TCP_TIMEOUT_INIT.
         */
-       timeout = rtt << 1;
+       timeout = rtt << 1 ? : TCP_TIMEOUT_INIT;
        if (tp->packets_out == 1)
                timeout = max_t(u32, timeout,
                                (rtt + (rtt >> 1) + TCP_DELACK_MAX));
@@ -2659,8 +2655,6 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
                        net_dbg_ratelimited("retrans_out leaked\n");
                }
 #endif
-               if (!tp->retrans_out)
-                       tp->lost_retrans_low = tp->snd_nxt;
                TCP_SKB_CB(skb)->sacked |= TCPCB_RETRANS;
                tp->retrans_out += tcp_skb_pcount(skb);
 
@@ -2668,10 +2662,6 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
                if (!tp->retrans_stamp)
                        tp->retrans_stamp = tcp_skb_timestamp(skb);
 
-               /* snd_nxt is stored to detect loss of retransmitted segment,
-                * see tcp_input.c tcp_sacktag_write_queue().
-                */
-               TCP_SKB_CB(skb)->ack_seq = tp->snd_nxt;
        } else if (err != -EBUSY) {
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL);
        }
@@ -2949,20 +2939,22 @@ int tcp_send_synack(struct sock *sk)
  * Allocate one skb and build a SYNACK packet.
  * @dst is consumed : Caller should not use it again.
  */
-struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
+struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
                                struct request_sock *req,
-                               struct tcp_fastopen_cookie *foc)
+                               struct tcp_fastopen_cookie *foc,
+                               bool attach_req)
 {
-       struct tcp_out_options opts;
        struct inet_request_sock *ireq = inet_rsk(req);
-       struct tcp_sock *tp = tcp_sk(sk);
-       struct tcphdr *th;
-       struct sk_buff *skb;
+       const struct tcp_sock *tp = tcp_sk(sk);
        struct tcp_md5sig_key *md5 = NULL;
+       struct tcp_out_options opts;
+       struct sk_buff *skb;
        int tcp_header_size;
+       struct tcphdr *th;
+       u16 user_mss;
        int mss;
 
-       skb = sock_wmalloc(sk, MAX_TCP_HEADER, 1, GFP_ATOMIC);
+       skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC);
        if (unlikely(!skb)) {
                dst_release(dst);
                return NULL;
@@ -2970,11 +2962,21 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        /* Reserve space for headers. */
        skb_reserve(skb, MAX_TCP_HEADER);
 
+       if (attach_req) {
+               skb_set_owner_w(skb, req_to_sk(req));
+       } else {
+               /* sk is a const pointer, because we want to express multiple
+                * cpu might call us concurrently.
+                * sk->sk_wmem_alloc in an atomic, we can promote to rw.
+                */
+               skb_set_owner_w(skb, (struct sock *)sk);
+       }
        skb_dst_set(skb, dst);
 
        mss = dst_metric_advmss(dst);
-       if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)
-               mss = tp->rx_opt.user_mss;
+       user_mss = READ_ONCE(tp->rx_opt.user_mss);
+       if (user_mss && user_mss < mss)
+               mss = user_mss;
 
        memset(&opts, 0, sizeof(opts));
 #ifdef CONFIG_SYN_COOKIES
@@ -2988,8 +2990,9 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        rcu_read_lock();
        md5 = tcp_rsk(req)->af_specific->req_md5_lookup(sk, req_to_sk(req));
 #endif
-       tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, md5,
-                                            foc) + sizeof(*th);
+       skb_set_hash(skb, tcp_rsk(req)->txhash, PKT_HASH_TYPE_L4);
+       tcp_header_size = tcp_synack_options(req, mss, skb, &opts, md5, foc) +
+                         sizeof(*th);
 
        skb_push(skb, tcp_header_size);
        skb_reset_transport_header(skb);
@@ -2998,7 +3001,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        memset(th, 0, sizeof(struct tcphdr));
        th->syn = 1;
        th->ack = 1;
-       tcp_ecn_make_synack(req, th, sk);
+       tcp_ecn_make_synack(req, th);
        th->source = htons(ireq->ir_num);
        th->dest = ireq->ir_rmt_port;
        /* Setting of flags are superfluous here for callers (and ECE is
@@ -3012,8 +3015,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        th->ack_seq = htonl(tcp_rsk(req)->rcv_nxt);
 
        /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
-       th->window = htons(min(req->rcv_wnd, 65535U));
-       tcp_options_write((__be32 *)(th + 1), tp, &opts);
+       th->window = htons(min(req->rsk_rcv_wnd, 65535U));
+       tcp_options_write((__be32 *)(th + 1), NULL, &opts);
        th->doff = (tcp_header_size >> 2);
        TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_OUTSEGS);
 
@@ -3405,7 +3408,7 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent, int mib)
         */
        tcp_init_nondata_skb(skb, tp->snd_una - !urgent, TCPHDR_ACK);
        skb_mstamp_get(&skb->skb_mstamp);
-       NET_INC_STATS_BH(sock_net(sk), mib);
+       NET_INC_STATS(sock_net(sk), mib);
        return tcp_transmit_skb(sk, skb, 0, GFP_ATOMIC);
 }
 
@@ -3500,13 +3503,14 @@ void tcp_send_probe0(struct sock *sk)
                                  TCP_RTO_MAX);
 }
 
-int tcp_rtx_synack(struct sock *sk, struct request_sock *req)
+int tcp_rtx_synack(const struct sock *sk, struct request_sock *req)
 {
        const struct tcp_request_sock_ops *af_ops = tcp_rsk(req)->af_specific;
        struct flowi fl;
        int res;
 
-       res = af_ops->send_synack(sk, NULL, &fl, req, 0, NULL);
+       tcp_rsk(req)->txhash = net_tx_rndhash();
+       res = af_ops->send_synack(sk, NULL, &fl, req, NULL, true);
        if (!res) {
                TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c
new file mode 100644 (file)
index 0000000..5353085
--- /dev/null
@@ -0,0 +1,109 @@
+#include <linux/tcp.h>
+#include <net/tcp.h>
+
+int sysctl_tcp_recovery __read_mostly = TCP_RACK_LOST_RETRANS;
+
+/* Marks a packet lost, if some packet sent later has been (s)acked.
+ * The underlying idea is similar to the traditional dupthresh and FACK
+ * but they look at different metrics:
+ *
+ * dupthresh: 3 OOO packets delivered (packet count)
+ * FACK: sequence delta to highest sacked sequence (sequence space)
+ * RACK: sent time delta to the latest delivered packet (time domain)
+ *
+ * The advantage of RACK is it applies to both original and retransmitted
+ * packet and therefore is robust against tail losses. Another advantage
+ * is being more resilient to reordering by simply allowing some
+ * "settling delay", instead of tweaking the dupthresh.
+ *
+ * The current version is only used after recovery starts but can be
+ * easily extended to detect the first loss.
+ */
+int tcp_rack_mark_lost(struct sock *sk)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       struct sk_buff *skb;
+       u32 reo_wnd, prior_retrans = tp->retrans_out;
+
+       if (inet_csk(sk)->icsk_ca_state < TCP_CA_Recovery || !tp->rack.advanced)
+               return 0;
+
+       /* Reset the advanced flag to avoid unnecessary queue scanning */
+       tp->rack.advanced = 0;
+
+       /* To be more reordering resilient, allow min_rtt/4 settling delay
+        * (lower-bounded to 1000uS). We use min_rtt instead of the smoothed
+        * RTT because reordering is often a path property and less related
+        * to queuing or delayed ACKs.
+        *
+        * TODO: measure and adapt to the observed reordering delay, and
+        * use a timer to retransmit like the delayed early retransmit.
+        */
+       reo_wnd = 1000;
+       if (tp->rack.reord && tcp_min_rtt(tp) != ~0U)
+               reo_wnd = max(tcp_min_rtt(tp) >> 2, reo_wnd);
+
+       tcp_for_write_queue(skb, sk) {
+               struct tcp_skb_cb *scb = TCP_SKB_CB(skb);
+
+               if (skb == tcp_send_head(sk))
+                       break;
+
+               /* Skip ones already (s)acked */
+               if (!after(scb->end_seq, tp->snd_una) ||
+                   scb->sacked & TCPCB_SACKED_ACKED)
+                       continue;
+
+               if (skb_mstamp_after(&tp->rack.mstamp, &skb->skb_mstamp)) {
+
+                       if (skb_mstamp_us_delta(&tp->rack.mstamp,
+                                               &skb->skb_mstamp) <= reo_wnd)
+                               continue;
+
+                       /* skb is lost if packet sent later is sacked */
+                       tcp_skb_mark_lost_uncond_verify(tp, skb);
+                       if (scb->sacked & TCPCB_SACKED_RETRANS) {
+                               scb->sacked &= ~TCPCB_SACKED_RETRANS;
+                               tp->retrans_out -= tcp_skb_pcount(skb);
+                               NET_INC_STATS_BH(sock_net(sk),
+                                                LINUX_MIB_TCPLOSTRETRANSMIT);
+                       }
+               } else if (!(scb->sacked & TCPCB_RETRANS)) {
+                       /* Original data are sent sequentially so stop early
+                        * b/c the rest are all sent after rack_sent
+                        */
+                       break;
+               }
+       }
+       return prior_retrans - tp->retrans_out;
+}
+
+/* Record the most recently (re)sent time among the (s)acked packets */
+void tcp_rack_advance(struct tcp_sock *tp,
+                     const struct skb_mstamp *xmit_time, u8 sacked)
+{
+       if (tp->rack.mstamp.v64 &&
+           !skb_mstamp_after(xmit_time, &tp->rack.mstamp))
+               return;
+
+       if (sacked & TCPCB_RETRANS) {
+               struct skb_mstamp now;
+
+               /* If the sacked packet was retransmitted, it's ambiguous
+                * whether the retransmission or the original (or the prior
+                * retransmission) was sacked.
+                *
+                * If the original is lost, there is no ambiguity. Otherwise
+                * we assume the original can be delayed up to aRTT + min_rtt.
+                * the aRTT term is bounded by the fast recovery or timeout,
+                * so it's at least one RTT (i.e., retransmission is at least
+                * an RTT later).
+                */
+               skb_mstamp_get(&now);
+               if (skb_mstamp_us_delta(&now, xmit_time) < tcp_min_rtt(tp))
+                       return;
+       }
+
+       tp->rack.mstamp = *xmit_time;
+       tp->rack.advanced = 1;
+}
index 7149ebc820c7d87afef856ce641ac63678abafa3..c9c716a483e457e8b4fe063776ae3d7a855889cc 100644 (file)
@@ -83,7 +83,7 @@ static int tcp_out_of_resources(struct sock *sk, bool do_reset)
 }
 
 /* Calculate maximal number or retries on an orphaned socket. */
-static int tcp_orphan_retries(struct sock *sk, int alive)
+static int tcp_orphan_retries(struct sock *sk, bool alive)
 {
        int retries = sysctl_tcp_orphan_retries; /* May be zero. */
 
@@ -184,7 +184,7 @@ static int tcp_write_timeout(struct sock *sk)
 
                retry_until = sysctl_tcp_retries2;
                if (sock_flag(sk, SOCK_DEAD)) {
-                       const int alive = icsk->icsk_rto < TCP_RTO_MAX;
+                       const bool alive = icsk->icsk_rto < TCP_RTO_MAX;
 
                        retry_until = tcp_orphan_retries(sk, alive);
                        do_reset = alive ||
@@ -298,7 +298,7 @@ static void tcp_probe_timer(struct sock *sk)
 
        max_probes = sysctl_tcp_retries2;
        if (sock_flag(sk, SOCK_DEAD)) {
-               const int alive = inet_csk_rto_backoff(icsk, TCP_RTO_MAX) < TCP_RTO_MAX;
+               const bool alive = inet_csk_rto_backoff(icsk, TCP_RTO_MAX) < TCP_RTO_MAX;
 
                max_probes = tcp_orphan_retries(sk, alive);
                if (!alive && icsk->icsk_backoff >= max_probes)
index f7d1d5e19e955563178fcddb84c4ffe9abb66036..24ec14f9825c20834eca20b820ec953ba108da52 100644 (file)
@@ -375,7 +375,8 @@ static inline int compute_score(struct sock *sk, struct net *net,
                        return -1;
                score += 4;
        }
-
+       if (sk->sk_incoming_cpu == raw_smp_processor_id())
+               score++;
        return score;
 }
 
@@ -419,6 +420,9 @@ static inline int compute_score2(struct sock *sk, struct net *net,
                score += 4;
        }
 
+       if (sk->sk_incoming_cpu == raw_smp_processor_id())
+               score++;
+
        return score;
 }
 
@@ -1017,30 +1021,14 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 
                fl4 = &fl4_stack;
 
-               /* unconnected socket. If output device is enslaved to a VRF
-                * device lookup source address from VRF table. This mimics
-                * behavior of ip_route_connect{_init}.
-                */
-               if (netif_index_is_vrf(net, ipc.oif)) {
-                       flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos,
-                                          RT_SCOPE_UNIVERSE, sk->sk_protocol,
-                                          (flow_flags | FLOWI_FLAG_VRFSRC |
-                                           FLOWI_FLAG_SKIP_NH_OIF),
-                                          faddr, saddr, dport,
-                                          inet->inet_sport);
-
-                       rt = ip_route_output_flow(net, fl4, sk);
-                       if (!IS_ERR(rt)) {
-                               saddr = fl4->saddr;
-                               ip_rt_put(rt);
-                       }
-               }
-
                flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos,
                                   RT_SCOPE_UNIVERSE, sk->sk_protocol,
                                   flow_flags,
                                   faddr, saddr, dport, inet->inet_sport);
 
+               if (!saddr && ipc.oif)
+                       l3mdev_get_saddr(net, ipc.oif, fl4);
+
                security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
                rt = ip_route_output_flow(net, fl4, sk);
                if (IS_ERR(rt)) {
index 60b032f58ccc9ffca8617de21c72288b919889b7..62e1e72db4612d0aa5c912ac7ec7aeea24123a35 100644 (file)
@@ -22,7 +22,8 @@ int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb)
        return xfrm4_extract_header(skb);
 }
 
-static inline int xfrm4_rcv_encap_finish(struct sock *sk, struct sk_buff *skb)
+static inline int xfrm4_rcv_encap_finish(struct net *net, struct sock *sk,
+                                        struct sk_buff *skb)
 {
        if (!skb_dst(skb)) {
                const struct iphdr *iph = ip_hdr(skb);
@@ -52,8 +53,8 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async)
        iph->tot_len = htons(skb->len);
        ip_send_check(iph);
 
-       NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, NULL, skb,
-               skb->dev, NULL,
+       NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
+               dev_net(skb->dev), NULL, skb, skb->dev, NULL,
                xfrm4_rcv_encap_finish);
        return 0;
 }
index 2878dbfffeb7e769a32079f1a6b80061136a7efc..7ee6518afa86ff785cacda0f115297ff6e5d0fa5 100644 (file)
@@ -30,6 +30,8 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
 
        mtu = dst_mtu(skb_dst(skb));
        if (skb->len > mtu) {
+               skb->protocol = htons(ETH_P_IP);
+
                if (skb->sk)
                        xfrm_local_error(skb, mtu);
                else
@@ -80,24 +82,25 @@ int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb)
        return xfrm_output(sk, skb);
 }
 
-static int __xfrm4_output(struct sock *sk, struct sk_buff *skb)
+static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct xfrm_state *x = skb_dst(skb)->xfrm;
 
 #ifdef CONFIG_NETFILTER
        if (!x) {
                IPCB(skb)->flags |= IPSKB_REROUTED;
-               return dst_output_sk(sk, skb);
+               return dst_output(net, sk, skb);
        }
 #endif
 
        return x->outer_mode->afinfo->output_finish(sk, skb);
 }
 
-int xfrm4_output(struct sock *sk, struct sk_buff *skb)
+int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb,
-                           NULL, skb_dst(skb)->dev, __xfrm4_output,
+       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
+                           net, sk, skb, NULL, skb_dst(skb)->dev,
+                           __xfrm4_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
 
index c10a9ee684337782fa96b71302c0f05695711f36..7b0edb37a1150427ef3b0d9a7f2be11421ef67b0 100644 (file)
@@ -15,7 +15,7 @@
 #include <net/dst.h>
 #include <net/xfrm.h>
 #include <net/ip.h>
-#include <net/vrf.h>
+#include <net/l3mdev.h>
 
 static struct xfrm_policy_afinfo xfrm4_policy_afinfo;
 
@@ -97,6 +97,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
        xdst->u.rt.rt_gateway = rt->rt_gateway;
        xdst->u.rt.rt_uses_gateway = rt->rt_uses_gateway;
        xdst->u.rt.rt_pmtu = rt->rt_pmtu;
+       xdst->u.rt.rt_table_id = rt->rt_table_id;
        INIT_LIST_HEAD(&xdst->u.rt.rt_uncached);
 
        return 0;
@@ -110,10 +111,8 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
        struct flowi4 *fl4 = &fl->u.ip4;
        int oif = 0;
 
-       if (skb_dst(skb)) {
-               oif = vrf_master_ifindex(skb_dst(skb)->dev) ?
-                       : skb_dst(skb)->dev->ifindex;
-       }
+       if (skb_dst(skb))
+               oif = l3mdev_fib_oif(skb_dst(skb)->dev);
 
        memset(fl4, 0, sizeof(struct flowi4));
        fl4->flowi4_mark = skb->mark;
@@ -128,7 +127,10 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
                case IPPROTO_DCCP:
                        if (xprth + 4 < skb->data ||
                            pskb_may_pull(skb, xprth + 4 - skb->data)) {
-                               __be16 *ports = (__be16 *)xprth;
+                               __be16 *ports;
+
+                               xprth = skb_network_header(skb) + iph->ihl * 4;
+                               ports = (__be16 *)xprth;
 
                                fl4->fl4_sport = ports[!!reverse];
                                fl4->fl4_dport = ports[!reverse];
@@ -136,8 +138,12 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
                        break;
 
                case IPPROTO_ICMP:
-                       if (pskb_may_pull(skb, xprth + 2 - skb->data)) {
-                               u8 *icmp = xprth;
+                       if (xprth + 2 < skb->data ||
+                           pskb_may_pull(skb, xprth + 2 - skb->data)) {
+                               u8 *icmp;
+
+                               xprth = skb_network_header(skb) + iph->ihl * 4;
+                               icmp = xprth;
 
                                fl4->fl4_icmp_type = icmp[0];
                                fl4->fl4_icmp_code = icmp[1];
@@ -145,33 +151,50 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
                        break;
 
                case IPPROTO_ESP:
-                       if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
-                               __be32 *ehdr = (__be32 *)xprth;
+                       if (xprth + 4 < skb->data ||
+                           pskb_may_pull(skb, xprth + 4 - skb->data)) {
+                               __be32 *ehdr;
+
+                               xprth = skb_network_header(skb) + iph->ihl * 4;
+                               ehdr = (__be32 *)xprth;
 
                                fl4->fl4_ipsec_spi = ehdr[0];
                        }
                        break;
 
                case IPPROTO_AH:
-                       if (pskb_may_pull(skb, xprth + 8 - skb->data)) {
-                               __be32 *ah_hdr = (__be32 *)xprth;
+                       if (xprth + 8 < skb->data ||
+                           pskb_may_pull(skb, xprth + 8 - skb->data)) {
+                               __be32 *ah_hdr;
+
+                               xprth = skb_network_header(skb) + iph->ihl * 4;
+                               ah_hdr = (__be32 *)xprth;
 
                                fl4->fl4_ipsec_spi = ah_hdr[1];
                        }
                        break;
 
                case IPPROTO_COMP:
-                       if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
-                               __be16 *ipcomp_hdr = (__be16 *)xprth;
+                       if (xprth + 4 < skb->data ||
+                           pskb_may_pull(skb, xprth + 4 - skb->data)) {
+                               __be16 *ipcomp_hdr;
+
+                               xprth = skb_network_header(skb) + iph->ihl * 4;
+                               ipcomp_hdr = (__be16 *)xprth;
 
                                fl4->fl4_ipsec_spi = htonl(ntohs(ipcomp_hdr[1]));
                        }
                        break;
 
                case IPPROTO_GRE:
-                       if (pskb_may_pull(skb, xprth + 12 - skb->data)) {
-                               __be16 *greflags = (__be16 *)xprth;
-                               __be32 *gre_hdr = (__be32 *)xprth;
+                       if (xprth + 12 < skb->data ||
+                           pskb_may_pull(skb, xprth + 12 - skb->data)) {
+                               __be16 *greflags;
+                               __be32 *gre_hdr;
+
+                               xprth = skb_network_header(skb) + iph->ihl * 4;
+                               greflags = (__be16 *)xprth;
+                               gre_hdr = (__be32 *)xprth;
 
                                if (greflags[0] & GRE_KEY) {
                                        if (greflags[0] & GRE_CSUM)
@@ -236,7 +259,7 @@ static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
        xfrm_dst_ifdown(dst, dev);
 }
 
-static struct dst_ops xfrm4_dst_ops = {
+static struct dst_ops xfrm4_dst_ops_template = {
        .family =               AF_INET,
        .gc =                   xfrm4_garbage_collect,
        .update_pmtu =          xfrm4_update_pmtu,
@@ -245,12 +268,12 @@ static struct dst_ops xfrm4_dst_ops = {
        .destroy =              xfrm4_dst_destroy,
        .ifdown =               xfrm4_dst_ifdown,
        .local_out =            __ip_local_out,
-       .gc_thresh =            32768,
+       .gc_thresh =            INT_MAX,
 };
 
 static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
        .family =               AF_INET,
-       .dst_ops =              &xfrm4_dst_ops,
+       .dst_ops =              &xfrm4_dst_ops_template,
        .dst_lookup =           xfrm4_dst_lookup,
        .get_saddr =            xfrm4_get_saddr,
        .decode_session =       _decode_session4,
@@ -272,7 +295,7 @@ static struct ctl_table xfrm4_policy_table[] = {
        { }
 };
 
-static int __net_init xfrm4_net_init(struct net *net)
+static int __net_init xfrm4_net_sysctl_init(struct net *net)
 {
        struct ctl_table *table;
        struct ctl_table_header *hdr;
@@ -300,7 +323,7 @@ err_alloc:
        return -ENOMEM;
 }
 
-static void __net_exit xfrm4_net_exit(struct net *net)
+static void __net_exit xfrm4_net_sysctl_exit(struct net *net)
 {
        struct ctl_table *table;
 
@@ -312,12 +335,44 @@ static void __net_exit xfrm4_net_exit(struct net *net)
        if (!net_eq(net, &init_net))
                kfree(table);
 }
+#else /* CONFIG_SYSCTL */
+static int inline xfrm4_net_sysctl_init(struct net *net)
+{
+       return 0;
+}
+
+static void inline xfrm4_net_sysctl_exit(struct net *net)
+{
+}
+#endif
+
+static int __net_init xfrm4_net_init(struct net *net)
+{
+       int ret;
+
+       memcpy(&net->xfrm.xfrm4_dst_ops, &xfrm4_dst_ops_template,
+              sizeof(xfrm4_dst_ops_template));
+       ret = dst_entries_init(&net->xfrm.xfrm4_dst_ops);
+       if (ret)
+               return ret;
+
+       ret = xfrm4_net_sysctl_init(net);
+       if (ret)
+               dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
+
+       return ret;
+}
+
+static void __net_exit xfrm4_net_exit(struct net *net)
+{
+       xfrm4_net_sysctl_exit(net);
+       dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
+}
 
 static struct pernet_operations __net_initdata xfrm4_net_ops = {
        .init   = xfrm4_net_init,
        .exit   = xfrm4_net_exit,
 };
-#endif
 
 static void __init xfrm4_policy_init(void)
 {
@@ -326,13 +381,9 @@ static void __init xfrm4_policy_init(void)
 
 void __init xfrm4_init(void)
 {
-       dst_entries_init(&xfrm4_dst_ops);
-
        xfrm4_state_init();
        xfrm4_policy_init();
        xfrm4_protocol_init();
-#ifdef CONFIG_SYSCTL
        register_pernet_subsys(&xfrm4_net_ops);
-#endif
 }
 
index 36b85bd05ac8a320b1b910c7d3454da1139ecd3c..d72fa90d6feb0122d15d68d6d862e5299d331008 100644 (file)
@@ -81,6 +81,7 @@
 #include <net/ip.h>
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
+#include <net/l3mdev.h>
 #include <linux/if_tunnel.h>
 #include <linux/rtnetlink.h>
 #include <linux/netconf.h>
@@ -2146,7 +2147,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev,
                      unsigned long expires, u32 flags)
 {
        struct fib6_config cfg = {
-               .fc_table = RT6_TABLE_PREFIX,
+               .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX,
                .fc_metric = IP6_RT_PRIO_ADDRCONF,
                .fc_ifindex = dev->ifindex,
                .fc_expires = expires,
@@ -2179,8 +2180,9 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
        struct fib6_node *fn;
        struct rt6_info *rt = NULL;
        struct fib6_table *table;
+       u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX;
 
-       table = fib6_get_table(dev_net(dev), RT6_TABLE_PREFIX);
+       table = fib6_get_table(dev_net(dev), tb_id);
        if (!table)
                return NULL;
 
@@ -2211,7 +2213,7 @@ out:
 static void addrconf_add_mroute(struct net_device *dev)
 {
        struct fib6_config cfg = {
-               .fc_table = RT6_TABLE_LOCAL,
+               .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_LOCAL,
                .fc_metric = IP6_RT_PRIO_ADDRCONF,
                .fc_ifindex = dev->ifindex,
                .fc_dst_len = 8,
@@ -3029,6 +3031,10 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
 {
        struct in6_addr addr;
 
+       /* no link local addresses on L3 master devices */
+       if (netif_is_l3_master(idev->dev))
+               return;
+
        ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
 
        if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
@@ -3141,6 +3147,32 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                }
                break;
 
+       case NETDEV_CHANGEMTU:
+               /* if MTU under IPV6_MIN_MTU stop IPv6 on this interface. */
+               if (dev->mtu < IPV6_MIN_MTU) {
+                       addrconf_ifdown(dev, 1);
+                       break;
+               }
+
+               if (idev) {
+                       rt6_mtu_change(dev, dev->mtu);
+                       idev->cnf.mtu6 = dev->mtu;
+                       break;
+               }
+
+               /* allocate new idev */
+               idev = ipv6_add_dev(dev);
+               if (IS_ERR(idev))
+                       break;
+
+               /* device is still not ready */
+               if (!(idev->if_flags & IF_READY))
+                       break;
+
+               run_pending = 1;
+
+               /* fall through */
+
        case NETDEV_UP:
        case NETDEV_CHANGE:
                if (dev->flags & IFF_SLAVE)
@@ -3164,7 +3196,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                                idev->if_flags |= IF_READY;
                                run_pending = 1;
                        }
-               } else {
+               } else if (event == NETDEV_CHANGE) {
                        if (!addrconf_qdisc_ok(dev)) {
                                /* device is still not ready. */
                                break;
@@ -3229,24 +3261,6 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                }
                break;
 
-       case NETDEV_CHANGEMTU:
-               if (idev && dev->mtu >= IPV6_MIN_MTU) {
-                       rt6_mtu_change(dev, dev->mtu);
-                       idev->cnf.mtu6 = dev->mtu;
-                       break;
-               }
-
-               if (!idev && dev->mtu >= IPV6_MIN_MTU) {
-                       idev = ipv6_add_dev(dev);
-                       if (!IS_ERR(idev))
-                               break;
-               }
-
-               /*
-                * if MTU under IPV6_MIN_MTU.
-                * Stop IPv6 on this interface.
-                */
-
        case NETDEV_DOWN:
        case NETDEV_UNREGISTER:
                /*
@@ -3627,7 +3641,7 @@ static void addrconf_dad_work(struct work_struct *w)
 
        /* send a neighbour solicitation for our addr */
        addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
-       ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &in6addr_any, NULL);
+       ndisc_send_ns(ifp->idev->dev, &ifp->addr, &mcaddr, &in6addr_any, NULL);
 out:
        in6_ifa_put(ifp);
        rtnl_unlock();
@@ -4731,7 +4745,8 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype,
        }
 }
 
-static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev)
+static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev,
+                                 u32 ext_filter_mask)
 {
        struct nlattr *nla;
        struct ifla_cacheinfo ci;
@@ -4751,6 +4766,9 @@ static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev)
 
        /* XXX - MC not implemented */
 
+       if (ext_filter_mask & RTEXT_FILTER_SKIP_STATS)
+               return 0;
+
        nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64));
        if (!nla)
                goto nla_put_failure;
@@ -4778,7 +4796,8 @@ nla_put_failure:
        return -EMSGSIZE;
 }
 
-static size_t inet6_get_link_af_size(const struct net_device *dev)
+static size_t inet6_get_link_af_size(const struct net_device *dev,
+                                    u32 ext_filter_mask)
 {
        if (!__in6_dev_get(dev))
                return 0;
@@ -4786,14 +4805,15 @@ static size_t inet6_get_link_af_size(const struct net_device *dev)
        return inet6_ifla6_size();
 }
 
-static int inet6_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
+static int inet6_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
+                             u32 ext_filter_mask)
 {
        struct inet6_dev *idev = __in6_dev_get(dev);
 
        if (!idev)
                return -ENODATA;
 
-       if (inet6_fill_ifla6_attrs(skb, idev) < 0)
+       if (inet6_fill_ifla6_attrs(skb, idev, ext_filter_mask) < 0)
                return -EMSGSIZE;
 
        return 0;
@@ -4948,7 +4968,7 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
        if (!protoinfo)
                goto nla_put_failure;
 
-       if (inet6_fill_ifla6_attrs(skb, idev) < 0)
+       if (inet6_fill_ifla6_attrs(skb, idev, 0) < 0)
                goto nla_put_failure;
 
        nla_nest_end(skb, protoinfo);
index 9aadd57808a515dda6edbf4b784aae2179604628..d70b0238f468f4e5602d09469eddc98a07a3e61c 100644 (file)
@@ -263,7 +263,7 @@ void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
 
 void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info)
 {
-       struct ipv6_pinfo *np = inet6_sk(sk);
+       const struct ipv6_pinfo *np = inet6_sk(sk);
        struct sock_exterr_skb *serr;
        struct ipv6hdr *iph;
        struct sk_buff *skb;
index 9f777ec59a59d24566d87643889a8c591dd52637..ed33abf57abd7d7ec71685a7180cf88ec132626c 100644 (file)
@@ -32,6 +32,7 @@ struct fib6_rule {
 struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
                                   int flags, pol_lookup_t lookup)
 {
+       struct rt6_info *rt;
        struct fib_lookup_arg arg = {
                .lookup_ptr = lookup,
                .flags = FIB_LOOKUP_NOREF,
@@ -40,11 +41,21 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
        fib_rules_lookup(net->ipv6.fib6_rules_ops,
                         flowi6_to_flowi(fl6), flags, &arg);
 
-       if (arg.result)
-               return arg.result;
+       rt = arg.result;
 
-       dst_hold(&net->ipv6.ip6_null_entry->dst);
-       return &net->ipv6.ip6_null_entry->dst;
+       if (!rt) {
+               dst_hold(&net->ipv6.ip6_null_entry->dst);
+               return &net->ipv6.ip6_null_entry->dst;
+       }
+
+       if (rt->rt6i_flags & RTF_REJECT &&
+           rt->dst.error == -EAGAIN) {
+               ip6_rt_put(rt);
+               rt = net->ipv6.ip6_null_entry;
+               dst_hold(&rt->dst);
+       }
+
+       return &rt->dst;
 }
 
 static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
index 6c2b2132c8d328e4d947c3b0b8216ea40f582f90..36c5a98b04727b220e9ea77e00d96410d4bb3f74 100644 (file)
@@ -68,6 +68,7 @@
 #include <net/xfrm.h>
 #include <net/inet_common.h>
 #include <net/dsfield.h>
+#include <net/l3mdev.h>
 
 #include <asm/uaccess.h>
 
@@ -452,7 +453,8 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
         *      and anycast addresses will be checked later.
         */
        if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
-               net_dbg_ratelimited("icmp6_send: addr_any/mcast source\n");
+               net_dbg_ratelimited("icmp6_send: addr_any/mcast source [%pI6c > %pI6c]\n",
+                                   &hdr->saddr, &hdr->daddr);
                return;
        }
 
@@ -460,7 +462,8 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
         *      Never answer to a ICMP packet.
         */
        if (is_ineligible(skb)) {
-               net_dbg_ratelimited("icmp6_send: no reply to icmp error\n");
+               net_dbg_ratelimited("icmp6_send: no reply to icmp error [%pI6c > %pI6c]\n",
+                                   &hdr->saddr, &hdr->daddr);
                return;
        }
 
@@ -496,6 +499,9 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        else if (!fl6.flowi6_oif)
                fl6.flowi6_oif = np->ucast_oif;
 
+       if (!fl6.flowi6_oif)
+               fl6.flowi6_oif = l3mdev_master_ifindex(skb->dev);
+
        dst = icmpv6_route_lookup(net, skb, sk, &fl6);
        if (IS_ERR(dst))
                goto out;
@@ -509,7 +515,8 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        len = skb->len - msg.offset;
        len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr));
        if (len < 0) {
-               net_dbg_ratelimited("icmp: len problem\n");
+               net_dbg_ratelimited("icmp: len problem [%pI6c > %pI6c]\n",
+                                   &hdr->saddr, &hdr->daddr);
                goto out_dst_release;
        }
 
@@ -575,7 +582,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
        fl6.daddr = ipv6_hdr(skb)->saddr;
        if (saddr)
                fl6.saddr = *saddr;
-       fl6.flowi6_oif = skb->dev->ifindex;
+       fl6.flowi6_oif = l3mdev_fib_oif(skb->dev);
        fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
        fl6.flowi6_mark = mark;
        security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
@@ -781,7 +788,8 @@ static int icmpv6_rcv(struct sk_buff *skb)
                if (type & ICMPV6_INFOMSG_MASK)
                        break;
 
-               net_dbg_ratelimited("icmpv6: msg of unknown type\n");
+               net_dbg_ratelimited("icmpv6: msg of unknown type [%pI6c > %pI6c]\n",
+                                   saddr, daddr);
 
                /*
                 * error of unknown type.
index 678d2df4b8d93915185bd14ca7fa8cf9931472e4..1a6852e1ac69e408de22e8df9131188542ad649a 100644 (file)
@@ -91,7 +91,7 @@ static void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
        *(__be64 *)&ip6h->daddr = p->locator;
 }
 
-static int ila_output(struct sock *sk, struct sk_buff *skb)
+static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
 
@@ -100,7 +100,7 @@ static int ila_output(struct sock *sk, struct sk_buff *skb)
 
        update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate));
 
-       return dst->lwtstate->orig_output(sk, skb);
+       return dst->lwtstate->orig_output(net, sk, skb);
 
 drop:
        kfree_skb(skb);
index 6927f3fb5597fd2013b885cddb35bed852b950d5..5d1c7cee2cb2bdc45b0889b4fe197c7af4db01e7 100644 (file)
@@ -65,17 +65,18 @@ int inet6_csk_bind_conflict(const struct sock *sk,
 }
 EXPORT_SYMBOL_GPL(inet6_csk_bind_conflict);
 
-struct dst_entry *inet6_csk_route_req(struct sock *sk,
+struct dst_entry *inet6_csk_route_req(const struct sock *sk,
                                      struct flowi6 *fl6,
-                                     const struct request_sock *req)
+                                     const struct request_sock *req,
+                                     u8 proto)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
-       struct ipv6_pinfo *np = inet6_sk(sk);
+       const struct ipv6_pinfo *np = inet6_sk(sk);
        struct in6_addr *final_p, final;
        struct dst_entry *dst;
 
        memset(fl6, 0, sizeof(*fl6));
-       fl6->flowi6_proto = IPPROTO_TCP;
+       fl6->flowi6_proto = proto;
        fl6->daddr = ireq->ir_v6_rmt_addr;
        final_p = fl6_update_dst(fl6, np->opt, &final);
        fl6->saddr = ireq->ir_v6_loc_addr;
@@ -91,73 +92,7 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk,
 
        return dst;
 }
-
-/*
- * request_sock (formerly open request) hash tables.
- */
-static u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport,
-                          const u32 rnd, const u32 synq_hsize)
-{
-       u32 c;
-
-       c = jhash_3words((__force u32)raddr->s6_addr32[0],
-                        (__force u32)raddr->s6_addr32[1],
-                        (__force u32)raddr->s6_addr32[2],
-                        rnd);
-
-       c = jhash_2words((__force u32)raddr->s6_addr32[3],
-                        (__force u32)rport,
-                        c);
-
-       return c & (synq_hsize - 1);
-}
-
-struct request_sock *inet6_csk_search_req(struct sock *sk,
-                                         const __be16 rport,
-                                         const struct in6_addr *raddr,
-                                         const struct in6_addr *laddr,
-                                         const int iif)
-{
-       struct inet_connection_sock *icsk = inet_csk(sk);
-       struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
-       struct request_sock *req;
-       u32 hash = inet6_synq_hash(raddr, rport, lopt->hash_rnd,
-                                  lopt->nr_table_entries);
-
-       spin_lock(&icsk->icsk_accept_queue.syn_wait_lock);
-       for (req = lopt->syn_table[hash]; req != NULL; req = req->dl_next) {
-               const struct inet_request_sock *ireq = inet_rsk(req);
-
-               if (ireq->ir_rmt_port == rport &&
-                   req->rsk_ops->family == AF_INET6 &&
-                   ipv6_addr_equal(&ireq->ir_v6_rmt_addr, raddr) &&
-                   ipv6_addr_equal(&ireq->ir_v6_loc_addr, laddr) &&
-                   (!ireq->ir_iif || ireq->ir_iif == iif)) {
-                       atomic_inc(&req->rsk_refcnt);
-                       WARN_ON(req->sk != NULL);
-                       break;
-               }
-       }
-       spin_unlock(&icsk->icsk_accept_queue.syn_wait_lock);
-
-       return req;
-}
-EXPORT_SYMBOL_GPL(inet6_csk_search_req);
-
-void inet6_csk_reqsk_queue_hash_add(struct sock *sk,
-                                   struct request_sock *req,
-                                   const unsigned long timeout)
-{
-       struct inet_connection_sock *icsk = inet_csk(sk);
-       struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
-       const u32 h = inet6_synq_hash(&inet_rsk(req)->ir_v6_rmt_addr,
-                                     inet_rsk(req)->ir_rmt_port,
-                                     lopt->hash_rnd, lopt->nr_table_entries);
-
-       reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, timeout);
-       inet_csk_reqsk_queue_added(sk, timeout);
-}
-EXPORT_SYMBOL_GPL(inet6_csk_reqsk_queue_hash_add);
+EXPORT_SYMBOL(inet6_csk_route_req);
 
 void inet6_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr)
 {
index 6ac8dad0138a6b41395f306bffc2d9b47d8d91bc..21ace5a2bf7c27f7e70a212cf0d94432d029112d 100644 (file)
@@ -114,6 +114,8 @@ static inline int compute_score(struct sock *sk, struct net *net,
                                return -1;
                        score++;
                }
+               if (sk->sk_incoming_cpu == raw_smp_processor_id())
+                       score++;
        }
        return score;
 }
index 7d2e0023c72dbe2e466b35ffb1c6f0c0446af6da..0c7e276c230e4ab2cd7c7ab0688e84920a41f69b 100644 (file)
@@ -264,6 +264,7 @@ struct fib6_table *fib6_get_table(struct net *net, u32 id)
 
        return NULL;
 }
+EXPORT_SYMBOL_GPL(fib6_get_table);
 
 static void __net_init fib6_tables_init(struct net *net)
 {
@@ -285,7 +286,17 @@ struct fib6_table *fib6_get_table(struct net *net, u32 id)
 struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
                                   int flags, pol_lookup_t lookup)
 {
-       return (struct dst_entry *) lookup(net, net->ipv6.fib6_main_tbl, fl6, flags);
+       struct rt6_info *rt;
+
+       rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, flags);
+       if (rt->rt6i_flags & RTF_REJECT &&
+           rt->dst.error == -EAGAIN) {
+               ip6_rt_put(rt);
+               rt = net->ipv6.ip6_null_entry;
+               dst_hold(&rt->dst);
+       }
+
+       return &rt->dst;
 }
 
 static void __net_init fib6_tables_init(struct net *net)
index adba03ac7ce9671f6dba419d163672b9520743bd..9075acf081dda32e30185886714f34f61b0b1f7d 100644 (file)
@@ -47,7 +47,7 @@
 #include <net/inet_ecn.h>
 #include <net/dst_metadata.h>
 
-int ip6_rcv_finish(struct sock *sk, struct sk_buff *skb)
+int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
                const struct inet6_protocol *ipprot;
@@ -109,7 +109,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
        if (hdr->version != 6)
                goto err;
 
-       IP6_ADD_STATS_BH(dev_net(dev), idev,
+       IP6_ADD_STATS_BH(net, idev,
                         IPSTATS_MIB_NOECTPKTS +
                                (ipv6_get_dsfield(hdr) & INET_ECN_MASK),
                         max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));
@@ -183,8 +183,8 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
        /* Must drop socket now because of tproxy. */
        skb_orphan(skb);
 
-       return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, NULL, skb,
-                      dev, NULL,
+       return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
+                      net, NULL, skb, dev, NULL,
                       ip6_rcv_finish);
 err:
        IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS);
@@ -199,9 +199,8 @@ drop:
  */
 
 
-static int ip6_input_finish(struct sock *sk, struct sk_buff *skb)
+static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-       struct net *net = dev_net(skb_dst(skb)->dev);
        const struct inet6_protocol *ipprot;
        struct inet6_dev *idev;
        unsigned int nhoff;
@@ -278,8 +277,8 @@ discard:
 
 int ip6_input(struct sk_buff *skb)
 {
-       return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, NULL, skb,
-                      skb->dev, NULL,
+       return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN,
+                      dev_net(skb->dev), NULL, skb, skb->dev, NULL,
                       ip6_input_finish);
 }
 
index 08b62047c67f311ca808533cb7a83b5caab0cfc8..eeca943f12dc083e195dde804c764c8732d11b9e 100644 (file)
@@ -264,6 +264,9 @@ static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
        struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + nhoff);
        int err = -ENOSYS;
 
+       if (skb->encapsulation)
+               skb_set_inner_network_header(skb, nhoff);
+
        iph->payload_len = htons(skb->len - nhoff - sizeof(*iph));
 
        rcu_read_lock();
@@ -280,6 +283,13 @@ out_unlock:
        return err;
 }
 
+static int sit_gro_complete(struct sk_buff *skb, int nhoff)
+{
+       skb->encapsulation = 1;
+       skb_shinfo(skb)->gso_type |= SKB_GSO_SIT;
+       return ipv6_gro_complete(skb, nhoff);
+}
+
 static struct packet_offload ipv6_packet_offload __read_mostly = {
        .type = cpu_to_be16(ETH_P_IPV6),
        .callbacks = {
@@ -292,6 +302,8 @@ static struct packet_offload ipv6_packet_offload __read_mostly = {
 static const struct net_offload sit_offload = {
        .callbacks = {
                .gso_segment    = ipv6_gso_segment,
+               .gro_receive    = ipv6_gro_receive,
+               .gro_complete   = sit_gro_complete,
        },
 };
 
index 61d403ee1031caa5536d9298abd4604606dff6f5..e6a7bd15b9b7115ce6a61e3157c637cdaf86ec18 100644 (file)
@@ -55,8 +55,9 @@
 #include <net/xfrm.h>
 #include <net/checksum.h>
 #include <linux/mroute6.h>
+#include <net/l3mdev.h>
 
-static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb)
+static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct net_device *dev = dst->dev;
@@ -71,7 +72,7 @@ static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb)
                struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
 
                if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) &&
-                   ((mroute6_socket(dev_net(dev), skb) &&
+                   ((mroute6_socket(net, skb) &&
                     !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
                     ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
                                         &ipv6_hdr(skb)->saddr))) {
@@ -82,19 +83,18 @@ static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb)
                         */
                        if (newskb)
                                NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING,
-                                       sk, newskb, NULL, newskb->dev,
+                                       net, sk, newskb, NULL, newskb->dev,
                                        dev_loopback_xmit);
 
                        if (ipv6_hdr(skb)->hop_limit == 0) {
-                               IP6_INC_STATS(dev_net(dev), idev,
+                               IP6_INC_STATS(net, idev,
                                              IPSTATS_MIB_OUTDISCARDS);
                                kfree_skb(skb);
                                return 0;
                        }
                }
 
-               IP6_UPD_PO_STATS(dev_net(dev), idev, IPSTATS_MIB_OUTMCAST,
-                               skb->len);
+               IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, skb->len);
 
                if (IPV6_ADDR_MC_SCOPE(&ipv6_hdr(skb)->daddr) <=
                    IPV6_ADDR_SCOPE_NODELOCAL &&
@@ -116,48 +116,49 @@ static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb)
        }
        rcu_read_unlock_bh();
 
-       IP6_INC_STATS(dev_net(dst->dev),
-                     ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
+       IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
        kfree_skb(skb);
        return -EINVAL;
 }
 
-static int ip6_finish_output(struct sock *sk, struct sk_buff *skb)
+static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
            dst_allfrag(skb_dst(skb)) ||
            (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size))
-               return ip6_fragment(sk, skb, ip6_finish_output2);
+               return ip6_fragment(net, sk, skb, ip6_finish_output2);
        else
-               return ip6_finish_output2(sk, skb);
+               return ip6_finish_output2(net, sk, skb);
 }
 
-int ip6_output(struct sock *sk, struct sk_buff *skb)
+int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct net_device *dev = skb_dst(skb)->dev;
        struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
+
        if (unlikely(idev->cnf.disable_ipv6)) {
-               IP6_INC_STATS(dev_net(dev), idev,
-                             IPSTATS_MIB_OUTDISCARDS);
+               IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
                kfree_skb(skb);
                return 0;
        }
 
-       return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, sk, skb,
-                           NULL, dev,
+       return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
+                           net, sk, skb, NULL, dev,
                            ip6_finish_output,
                            !(IP6CB(skb)->flags & IP6SKB_REROUTED));
 }
 
 /*
- *     xmit an sk_buff (used by TCP, SCTP and DCCP)
+ * xmit an sk_buff (used by TCP, SCTP and DCCP)
+ * Note : socket lock is not held for SYNACK packets, but might be modified
+ * by calls to skb_set_owner_w() and ipv6_local_error(),
+ * which are using proper atomic operations or spinlocks.
  */
-
-int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
+int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
             struct ipv6_txoptions *opt, int tclass)
 {
        struct net *net = sock_net(sk);
-       struct ipv6_pinfo *np = inet6_sk(sk);
+       const struct ipv6_pinfo *np = inet6_sk(sk);
        struct in6_addr *first_hop = &fl6->daddr;
        struct dst_entry *dst = skb_dst(skb);
        struct ipv6hdr *hdr;
@@ -186,7 +187,10 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
                        }
                        consume_skb(skb);
                        skb = skb2;
-                       skb_set_owner_w(skb, sk);
+                       /* skb_set_owner_w() changes sk->sk_wmem_alloc atomically,
+                        * it is safe to call in our context (socket lock not held)
+                        */
+                       skb_set_owner_w(skb, (struct sock *)sk);
                }
                if (opt->opt_flen)
                        ipv6_push_frag_opts(skb, opt, &proto);
@@ -224,12 +228,20 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
        if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) {
                IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)),
                              IPSTATS_MIB_OUT, skb->len);
-               return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
-                              NULL, dst->dev, dst_output_sk);
+               /* hooks should never assume socket lock is held.
+                * we promote our socket to non const
+                */
+               return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
+                              net, (struct sock *)sk, skb, NULL, dst->dev,
+                              dst_output);
        }
 
        skb->dev = dst->dev;
-       ipv6_local_error(sk, EMSGSIZE, fl6, mtu);
+       /* ipv6_local_error() does not require socket lock,
+        * we promote our socket to non const
+        */
+       ipv6_local_error((struct sock *)sk, EMSGSIZE, fl6, mtu);
+
        IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGFAILS);
        kfree_skb(skb);
        return -EMSGSIZE;
@@ -317,10 +329,11 @@ static int ip6_forward_proxy_check(struct sk_buff *skb)
        return 0;
 }
 
-static inline int ip6_forward_finish(struct sock *sk, struct sk_buff *skb)
+static inline int ip6_forward_finish(struct net *net, struct sock *sk,
+                                    struct sk_buff *skb)
 {
        skb_sender_cpu_clear(skb);
-       return dst_output_sk(sk, skb);
+       return dst_output(net, sk, skb);
 }
 
 static unsigned int ip6_dst_mtu_forward(const struct dst_entry *dst)
@@ -515,8 +528,8 @@ int ip6_forward(struct sk_buff *skb)
 
        IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
        IP6_ADD_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len);
-       return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, NULL, skb,
-                      skb->dev, dst->dev,
+       return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD,
+                      net, NULL, skb, skb->dev, dst->dev,
                       ip6_forward_finish);
 
 error:
@@ -543,8 +556,8 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        skb_copy_secmark(to, from);
 }
 
-int ip6_fragment(struct sock *sk, struct sk_buff *skb,
-                int (*output)(struct sock *, struct sk_buff *))
+int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
+                int (*output)(struct net *, struct sock *, struct sk_buff *))
 {
        struct sk_buff *frag;
        struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
@@ -557,7 +570,6 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb,
        __be32 frag_id;
        int ptr, offset = 0, err = 0;
        u8 *prevhdr, nexthdr = 0;
-       struct net *net = dev_net(skb_dst(skb)->dev);
 
        hlen = ip6_find_1stfragopt(skb, &prevhdr);
        nexthdr = *prevhdr;
@@ -584,11 +596,17 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb,
                if (np->frag_size)
                        mtu = np->frag_size;
        }
+       if (mtu < hlen + sizeof(struct frag_hdr) + 8)
+               goto fail_toobig;
        mtu -= hlen + sizeof(struct frag_hdr);
 
        frag_id = ipv6_select_ident(net, &ipv6_hdr(skb)->daddr,
                                    &ipv6_hdr(skb)->saddr);
 
+       if (skb->ip_summed == CHECKSUM_PARTIAL &&
+           (err = skb_checksum_help(skb)))
+               goto fail;
+
        hroom = LL_RESERVED_SPACE(rt->dst.dev);
        if (skb_has_frag_list(skb)) {
                int first_len = skb_pagelen(skb);
@@ -677,7 +695,7 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb,
                                ip6_copy_metadata(frag, skb);
                        }
 
-                       err = output(sk, skb);
+                       err = output(net, sk, skb);
                        if (!err)
                                IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
                                              IPSTATS_MIB_FRAGCREATES);
@@ -717,10 +735,6 @@ slow_path_clean:
        }
 
 slow_path:
-       if ((skb->ip_summed == CHECKSUM_PARTIAL) &&
-           skb_checksum_help(skb))
-               goto fail;
-
        left = skb->len - hlen;         /* Space per frame */
        ptr = hlen;                     /* Where to start from */
 
@@ -805,7 +819,7 @@ slow_path:
                /*
                 *      Put this fragment into the sending queue.
                 */
-               err = output(sk, frag);
+               err = output(net, sk, frag);
                if (err)
                        goto fail;
 
@@ -877,7 +891,8 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk,
 #ifdef CONFIG_IPV6_SUBTREES
            ip6_rt_check(&rt->rt6i_src, &fl6->saddr, np->saddr_cache) ||
 #endif
-           (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex)) {
+          (!(fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) &&
+             (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex))) {
                dst_release(dst);
                dst = NULL;
        }
@@ -886,7 +901,7 @@ out:
        return dst;
 }
 
-static int ip6_dst_lookup_tail(struct net *net, struct sock *sk,
+static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk,
                               struct dst_entry **dst, struct flowi6 *fl6)
 {
 #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
@@ -1017,7 +1032,7 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup);
  *     It returns a valid dst pointer on success, or a pointer encoded
  *     error code.
  */
-struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
+struct dst_entry *ip6_dst_lookup_flow(const struct sock *sk, struct flowi6 *fl6,
                                      const struct in6_addr *final_dst)
 {
        struct dst_entry *dst = NULL;
@@ -1029,7 +1044,7 @@ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
        if (final_dst)
                fl6->daddr = *final_dst;
        if (!fl6->flowi6_oif)
-               fl6->flowi6_oif = dst->dev->ifindex;
+               fl6->flowi6_oif = l3mdev_fib_oif(dst->dev);
 
        return xfrm_lookup_route(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0);
 }
@@ -1255,6 +1270,7 @@ static int __ip6_append_data(struct sock *sk,
        struct rt6_info *rt = (struct rt6_info *)cork->dst;
        struct ipv6_txoptions *opt = v6_cork->opt;
        int csummode = CHECKSUM_NONE;
+       unsigned int maxnonfragsize, headersize;
 
        skb = skb_peek_tail(queue);
        if (!skb) {
@@ -1272,38 +1288,43 @@ static int __ip6_append_data(struct sock *sk,
        maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen -
                     sizeof(struct frag_hdr);
 
-       if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {
-               unsigned int maxnonfragsize, headersize;
-
-               headersize = sizeof(struct ipv6hdr) +
-                            (opt ? opt->opt_flen + opt->opt_nflen : 0) +
-                            (dst_allfrag(&rt->dst) ?
-                             sizeof(struct frag_hdr) : 0) +
-                            rt->rt6i_nfheader_len;
-
-               if (ip6_sk_ignore_df(sk))
-                       maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN;
-               else
-                       maxnonfragsize = mtu;
+       headersize = sizeof(struct ipv6hdr) +
+                    (opt ? opt->opt_flen + opt->opt_nflen : 0) +
+                    (dst_allfrag(&rt->dst) ?
+                     sizeof(struct frag_hdr) : 0) +
+                    rt->rt6i_nfheader_len;
+
+       if (cork->length + length > mtu - headersize && dontfrag &&
+           (sk->sk_protocol == IPPROTO_UDP ||
+            sk->sk_protocol == IPPROTO_RAW)) {
+               ipv6_local_rxpmtu(sk, fl6, mtu - headersize +
+                               sizeof(struct ipv6hdr));
+               goto emsgsize;
+       }
 
-               /* dontfrag active */
-               if ((cork->length + length > mtu - headersize) && dontfrag &&
-                   (sk->sk_protocol == IPPROTO_UDP ||
-                    sk->sk_protocol == IPPROTO_RAW)) {
-                       ipv6_local_rxpmtu(sk, fl6, mtu - headersize +
-                                                  sizeof(struct ipv6hdr));
-                       goto emsgsize;
-               }
+       if (ip6_sk_ignore_df(sk))
+               maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN;
+       else
+               maxnonfragsize = mtu;
 
-               if (cork->length + length > maxnonfragsize - headersize) {
+       if (cork->length + length > maxnonfragsize - headersize) {
 emsgsize:
-                       ipv6_local_error(sk, EMSGSIZE, fl6,
-                                        mtu - headersize +
-                                        sizeof(struct ipv6hdr));
-                       return -EMSGSIZE;
-               }
+               ipv6_local_error(sk, EMSGSIZE, fl6,
+                                mtu - headersize +
+                                sizeof(struct ipv6hdr));
+               return -EMSGSIZE;
        }
 
+       /* CHECKSUM_PARTIAL only with no extension headers and when
+        * we are not going to fragment
+        */
+       if (transhdrlen && sk->sk_protocol == IPPROTO_UDP &&
+           headersize == sizeof(struct ipv6hdr) &&
+           length < mtu - headersize &&
+           !(flags & MSG_MORE) &&
+           rt->dst.dev->features & NETIF_F_V6_CSUM)
+               csummode = CHECKSUM_PARTIAL;
+
        if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_RAW) {
                sock_tx_timestamp(sk, &tx_flags);
                if (tx_flags & SKBTX_ANY_SW_TSTAMP &&
@@ -1311,16 +1332,6 @@ emsgsize:
                        tskey = sk->sk_tskey++;
        }
 
-       /* If this is the first and only packet and device
-        * supports checksum offloading, let's use it.
-        * Use transhdrlen, same as IPv4, because partial
-        * sums only work when transhdrlen is set.
-        */
-       if (transhdrlen && sk->sk_protocol == IPPROTO_UDP &&
-           length + fragheaderlen < mtu &&
-           rt->dst.dev->features & NETIF_F_V6_CSUM &&
-           !exthdrlen)
-               csummode = CHECKSUM_PARTIAL;
        /*
         * Let's try using as much space as possible.
         * Use MTU if total length of the message fits into the MTU.
@@ -1683,7 +1694,7 @@ int ip6_send_skb(struct sk_buff *skb)
        struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
        int err;
 
-       err = ip6_local_out(skb);
+       err = ip6_local_out(net, skb->sk, skb);
        if (err) {
                if (err > 0)
                        err = net_xmit_errno(err);
index 0224c032dca5dca98ea0146bcdf52c179fa23f6d..0a8610b33d7980e4b1d5290b4266669ddcdc9e0a 100644 (file)
@@ -482,7 +482,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
                return -EMSGSIZE;
        }
 
-       err = dst_output(skb);
+       err = dst_output(t->net, skb->sk, skb);
        if (net_xmit_eval(err) == 0) {
                struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
 
index 0e004cc42a22b1593fa308f3adac735568ddf2ee..ad19136086dd5e1ae4e362e3da0f0267fc755b02 100644 (file)
@@ -1985,13 +1985,13 @@ int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
 }
 #endif
 
-static inline int ip6mr_forward2_finish(struct sock *sk, struct sk_buff *skb)
+static inline int ip6mr_forward2_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-       IP6_INC_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)),
+       IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
                         IPSTATS_MIB_OUTFORWDATAGRAMS);
-       IP6_ADD_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)),
+       IP6_ADD_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
                         IPSTATS_MIB_OUTOCTETS, skb->len);
-       return dst_output_sk(sk, skb);
+       return dst_output(net, sk, skb);
 }
 
 /*
@@ -2063,8 +2063,8 @@ static int ip6mr_forward2(struct net *net, struct mr6_table *mrt,
 
        IP6CB(skb)->flags |= IP6SKB_FORWARDED;
 
-       return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, NULL, skb,
-                      skb->dev, dev,
+       return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD,
+                      net, NULL, skb, skb->dev, dev,
                       ip6mr_forward2_finish);
 
 out_free:
index 083b2927fc67aaa3939fff42a1c6ee9f1ca70afe..124338a39e29cb3e3a5e602fb706486f330fb529 100644 (file)
@@ -1645,8 +1645,8 @@ static void mld_sendpack(struct sk_buff *skb)
        payload_len = skb->len;
 
        err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
-                     net->ipv6.igmp_sk, skb, NULL, skb->dev,
-                     dst_output_sk);
+                     net, net->ipv6.igmp_sk, skb, NULL, skb->dev,
+                     dst_output);
 out:
        if (!err) {
                ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT);
@@ -2008,8 +2008,9 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
        }
 
        skb_dst_set(skb, dst);
-       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
-                     NULL, skb->dev, dst_output_sk);
+       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
+                     net, sk, skb, NULL, skb->dev,
+                     dst_output);
 out:
        if (!err) {
                ICMP6MSGOUT_INC_STATS(net, idev, type);
index b9779d441b1246906b40812fdf30a980a4673143..60c79a08e14a592006aabe8ac120afc32e81347d 100644 (file)
@@ -118,7 +118,7 @@ static int mip6_mh_filter(struct sock *sk, struct sk_buff *skb)
 
 struct mip6_report_rate_limiter {
        spinlock_t lock;
-       struct timeval stamp;
+       ktime_t stamp;
        int iif;
        struct in6_addr src;
        struct in6_addr dst;
@@ -184,20 +184,18 @@ static int mip6_destopt_output(struct xfrm_state *x, struct sk_buff *skb)
        return 0;
 }
 
-static inline int mip6_report_rl_allow(struct timeval *stamp,
+static inline int mip6_report_rl_allow(ktime_t stamp,
                                       const struct in6_addr *dst,
                                       const struct in6_addr *src, int iif)
 {
        int allow = 0;
 
        spin_lock_bh(&mip6_report_rl.lock);
-       if (mip6_report_rl.stamp.tv_sec != stamp->tv_sec ||
-           mip6_report_rl.stamp.tv_usec != stamp->tv_usec ||
+       if (!ktime_equal(mip6_report_rl.stamp, stamp) ||
            mip6_report_rl.iif != iif ||
            !ipv6_addr_equal(&mip6_report_rl.src, src) ||
            !ipv6_addr_equal(&mip6_report_rl.dst, dst)) {
-               mip6_report_rl.stamp.tv_sec = stamp->tv_sec;
-               mip6_report_rl.stamp.tv_usec = stamp->tv_usec;
+               mip6_report_rl.stamp = stamp;
                mip6_report_rl.iif = iif;
                mip6_report_rl.src = *src;
                mip6_report_rl.dst = *dst;
@@ -216,7 +214,7 @@ static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb,
        struct ipv6_destopt_hao *hao = NULL;
        struct xfrm_selector sel;
        int offset;
-       struct timeval stamp;
+       ktime_t stamp;
        int err = 0;
 
        if (unlikely(fl6->flowi6_proto == IPPROTO_MH &&
@@ -230,9 +228,9 @@ static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb,
                                        (skb_network_header(skb) + offset);
        }
 
-       skb_get_timestamp(skb, &stamp);
+       stamp = skb_get_ktime(skb);
 
-       if (!mip6_report_rl_allow(&stamp, &ipv6_hdr(skb)->daddr,
+       if (!mip6_report_rl_allow(stamp, &ipv6_hdr(skb)->daddr,
                                  hao ? &hao->addr : &ipv6_hdr(skb)->saddr,
                                  opt->iif))
                goto out;
index 64a71354b069dd816199d1b1e23fe78082f2406a..3e0f855e1bead049064a284494eda378f85ae47e 100644 (file)
@@ -67,6 +67,7 @@
 #include <net/flow.h>
 #include <net/ip6_checksum.h>
 #include <net/inet_common.h>
+#include <net/l3mdev.h>
 #include <linux/proc_fs.h>
 
 #include <linux/netfilter.h>
@@ -147,6 +148,7 @@ struct neigh_table nd_tbl = {
        .gc_thresh2 =    512,
        .gc_thresh3 =   1024,
 };
+EXPORT_SYMBOL_GPL(nd_tbl);
 
 static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
 {
@@ -441,8 +443,11 @@ static void ndisc_send_skb(struct sk_buff *skb,
 
        if (!dst) {
                struct flowi6 fl6;
+               int oif = l3mdev_fib_oif(skb->dev);
 
-               icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex);
+               icmpv6_flow_init(sk, &fl6, type, saddr, daddr, oif);
+               if (oif != skb->dev->ifindex)
+                       fl6.flowi6_flags |= FLOWI_FLAG_L3MDEV_SRC;
                dst = icmp6_dst_alloc(skb->dev, &fl6);
                if (IS_ERR(dst)) {
                        kfree_skb(skb);
@@ -463,9 +468,9 @@ static void ndisc_send_skb(struct sk_buff *skb,
        idev = __in6_dev_get(dst->dev);
        IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
 
-       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
-                     NULL, dst->dev,
-                     dst_output_sk);
+       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
+                     net, sk, skb, NULL, dst->dev,
+                     dst_output);
        if (!err) {
                ICMP6MSGOUT_INC_STATS(net, idev, type);
                ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
@@ -474,8 +479,7 @@ static void ndisc_send_skb(struct sk_buff *skb,
        rcu_read_unlock();
 }
 
-void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
-                  const struct in6_addr *daddr,
+void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
                   const struct in6_addr *solicited_addr,
                   bool router, bool solicited, bool override, bool inc_opt)
 {
@@ -541,7 +545,7 @@ static void ndisc_send_unsol_na(struct net_device *dev)
 
        read_lock_bh(&idev->lock);
        list_for_each_entry(ifa, &idev->addr_list, if_list) {
-               ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &ifa->addr,
+               ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifa->addr,
                              /*router=*/ !!idev->cnf.forwarding,
                              /*solicited=*/ false, /*override=*/ true,
                              /*inc_opt=*/ true);
@@ -551,8 +555,7 @@ static void ndisc_send_unsol_na(struct net_device *dev)
        in6_dev_put(idev);
 }
 
-void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
-                  const struct in6_addr *solicit,
+void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
                   const struct in6_addr *daddr, const struct in6_addr *saddr,
                   struct sk_buff *oskb)
 {
@@ -679,12 +682,12 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
                                  "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
                                  __func__, target);
                }
-               ndisc_send_ns(dev, neigh, target, target, saddr, skb);
+               ndisc_send_ns(dev, target, target, saddr, skb);
        } else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) {
                neigh_app_ns(neigh);
        } else {
                addrconf_addr_solict_mult(target, &mcaddr);
-               ndisc_send_ns(dev, NULL, target, &mcaddr, saddr, skb);
+               ndisc_send_ns(dev, target, &mcaddr, saddr, skb);
        }
 }
 
@@ -768,7 +771,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 
        ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
        if (ifp) {
-
+have_ifp:
                if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
                        if (dad) {
                                /*
@@ -794,6 +797,18 @@ static void ndisc_recv_ns(struct sk_buff *skb)
        } else {
                struct net *net = dev_net(dev);
 
+               /* perhaps an address on the master device */
+               if (netif_is_l3_slave(dev)) {
+                       struct net_device *mdev;
+
+                       mdev = netdev_master_upper_dev_get_rcu(dev);
+                       if (mdev) {
+                               ifp = ipv6_get_ifaddr(net, &msg->target, mdev, 1);
+                               if (ifp)
+                                       goto have_ifp;
+                       }
+               }
+
                idev = in6_dev_get(dev);
                if (!idev) {
                        /* XXX: count this drop? */
@@ -828,7 +843,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
                is_router = idev->cnf.forwarding;
 
        if (dad) {
-               ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
+               ndisc_send_na(dev, &in6addr_linklocal_allnodes, &msg->target,
                              !!is_router, false, (ifp != NULL), true);
                goto out;
        }
@@ -849,8 +864,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
                             NEIGH_UPDATE_F_WEAK_OVERRIDE|
                             NEIGH_UPDATE_F_OVERRIDE);
        if (neigh || !dev->header_ops) {
-               ndisc_send_na(dev, neigh, saddr, &msg->target,
-                             !!is_router,
+               ndisc_send_na(dev, saddr, &msg->target, !!is_router,
                              true, (ifp != NULL && inc), inc);
                if (neigh)
                        neigh_release(neigh);
@@ -1486,6 +1500,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
        struct flowi6 fl6;
        int rd_len;
        u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
+       int oif = l3mdev_fib_oif(dev);
        bool ret;
 
        if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
@@ -1502,7 +1517,10 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
        }
 
        icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
-                        &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
+                        &saddr_buf, &ipv6_hdr(skb)->saddr, oif);
+
+       if (oif != skb->dev->ifindex)
+               fl6.flowi6_flags |= FLOWI_FLAG_L3MDEV_SRC;
 
        dst = ip6_route_output(net, NULL, &fl6);
        if (dst->error) {
index b4de08a83e0b8da054fb7eb4f48d88a999795e6f..d11c46833d615b394797e193008f1cc8e4592935 100644 (file)
@@ -18,9 +18,8 @@
 #include <net/ip6_checksum.h>
 #include <net/netfilter/nf_queue.h>
 
-int ip6_route_me_harder(struct sk_buff *skb)
+int ip6_route_me_harder(struct net *net, struct sk_buff *skb)
 {
-       struct net *net = dev_net(skb_dst(skb)->dev);
        const struct ipv6hdr *iph = ipv6_hdr(skb);
        unsigned int hh_len;
        struct dst_entry *dst;
@@ -93,7 +92,7 @@ static void nf_ip6_saveroute(const struct sk_buff *skb,
        }
 }
 
-static int nf_ip6_reroute(struct sk_buff *skb,
+static int nf_ip6_reroute(struct net *net, struct sk_buff *skb,
                          const struct nf_queue_entry *entry)
 {
        struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry);
@@ -103,7 +102,7 @@ static int nf_ip6_reroute(struct sk_buff *skb,
                if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) ||
                    !ipv6_addr_equal(&iph->saddr, &rt_info->saddr) ||
                    skb->mark != rt_info->mark)
-                       return ip6_route_me_harder(skb);
+                       return ip6_route_me_harder(net, skb);
        }
        return 0;
 }
index 96833e4b31939a191eaf7de297ac438d4aa41fa4..f6a024e141e595541009cb24c172e0c52a8a879f 100644 (file)
@@ -58,6 +58,7 @@ endif # NF_TABLES
 
 config NF_DUP_IPV6
        tristate "Netfilter IPv6 packet duplication to alternate destination"
+       depends on !NF_CONNTRACK || NF_CONNTRACK
        help
          This option enables the nf_dup_ipv6 core, which duplicates an IPv6
          packet to be rerouted to another destination.
index 0771991ed812aebef403e60ace6e7df1dd6f550c..99425cf2819b83ceb33d49af65284cb16fd076ec 100644 (file)
@@ -117,7 +117,7 @@ ip6_packet_match(const struct sk_buff *skb,
        if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
                dprintf("VIA in mismatch (%s vs %s).%s\n",
                        indev, ip6info->iniface,
-                       ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
+                       ip6info->invflags & IP6T_INV_VIA_IN ? " (INV)" : "");
                return false;
        }
 
@@ -126,14 +126,14 @@ ip6_packet_match(const struct sk_buff *skb,
        if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
                dprintf("VIA out mismatch (%s vs %s).%s\n",
                        outdev, ip6info->outiface,
-                       ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
+                       ip6info->invflags & IP6T_INV_VIA_OUT ? " (INV)" : "");
                return false;
        }
 
 /* ... might want to do something with class and flowlabel here ... */
 
        /* look for the desired protocol header */
-       if((ip6info->flags & IP6T_F_PROTO)) {
+       if (ip6info->flags & IP6T_F_PROTO) {
                int protohdr;
                unsigned short _frag_off;
 
@@ -151,9 +151,9 @@ ip6_packet_match(const struct sk_buff *skb,
                                ip6info->proto);
 
                if (ip6info->proto == protohdr) {
-                       if(ip6info->invflags & IP6T_INV_PROTO) {
+                       if (ip6info->invflags & IP6T_INV_PROTO)
                                return false;
-                       }
+
                        return true;
                }
 
@@ -275,7 +275,8 @@ get_chainname_rulenum(const struct ip6t_entry *s, const struct ip6t_entry *e,
        return 0;
 }
 
-static void trace_packet(const struct sk_buff *skb,
+static void trace_packet(struct net *net,
+                        const struct sk_buff *skb,
                         unsigned int hook,
                         const struct net_device *in,
                         const struct net_device *out,
@@ -287,7 +288,6 @@ static void trace_packet(const struct sk_buff *skb,
        const char *hookname, *chainname, *comment;
        const struct ip6t_entry *iter;
        unsigned int rulenum = 0;
-       struct net *net = dev_net(in ? in : out);
 
        root = get_entry(private->entries, private->hook_entry[hook]);
 
@@ -314,10 +314,10 @@ ip6t_next_entry(const struct ip6t_entry *entry)
 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
 unsigned int
 ip6t_do_table(struct sk_buff *skb,
-             unsigned int hook,
              const struct nf_hook_state *state,
              struct xt_table *table)
 {
+       unsigned int hook = state->hook;
        static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
        /* Initializing verdict to NF_DROP keeps gcc happy. */
        unsigned int verdict = NF_DROP;
@@ -340,6 +340,7 @@ ip6t_do_table(struct sk_buff *skb,
         * rule is also a fragment-specific rule, non-fragments won't
         * match it. */
        acpar.hotdrop = false;
+       acpar.net     = state->net;
        acpar.in      = state->in;
        acpar.out     = state->out;
        acpar.family  = NFPROTO_IPV6;
@@ -401,8 +402,8 @@ ip6t_do_table(struct sk_buff *skb,
 #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
                /* The packet is traced: log it */
                if (unlikely(skb->nf_trace))
-                       trace_packet(skb, hook, state->in, state->out,
-                                    table->name, private, e);
+                       trace_packet(state->net, skb, hook, state->in,
+                                    state->out, table->name, private, e);
 #endif
                /* Standard target? */
                if (!t->u.kernel.target->target) {
@@ -442,8 +443,8 @@ ip6t_do_table(struct sk_buff *skb,
                        break;
        } while (!acpar.hotdrop);
 
-       xt_write_recseq_end(addend);
-       local_bh_enable();
+       xt_write_recseq_end(addend);
+       local_bh_enable();
 
 #ifdef DEBUG_ALLOW_ALL
        return NF_ACCEPT;
@@ -560,7 +561,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
                                pos = newpos;
                        }
                }
-               next:
+next:
                duprintf("Finished chain %u\n", hook);
        }
        return 1;
@@ -815,7 +816,7 @@ static void cleanup_entry(struct ip6t_entry *e, struct net *net)
    newinfo) */
 static int
 translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
-                const struct ip6t_replace *repl)
+               const struct ip6t_replace *repl)
 {
        struct ip6t_entry *iter;
        unsigned int i;
@@ -1089,7 +1090,7 @@ static int compat_table_info(const struct xt_table_info *info,
 #endif
 
 static int get_info(struct net *net, void __user *user,
-                    const int *len, int compat)
+                   const int *len, int compat)
 {
        char name[XT_TABLE_MAXNAMELEN];
        struct xt_table *t;
@@ -1151,7 +1152,7 @@ static int get_info(struct net *net, void __user *user,
 
 static int
 get_entries(struct net *net, struct ip6t_get_entries __user *uptr,
-            const int *len)
+           const int *len)
 {
        int ret;
        struct ip6t_get_entries get;
index 0ed841a3fa33ac7c5cc17fc823a2e33f537b5bfb..db29bbf41b5977d3228ea58ce3032725c175f989 100644 (file)
@@ -39,7 +39,7 @@ static unsigned int
 reject_tg6(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct ip6t_reject_info *reject = par->targinfo;
-       struct net *net = dev_net((par->in != NULL) ? par->in : par->out);
+       struct net *net = par->net;
 
        switch (reject->with) {
        case IP6T_ICMP6_NO_ROUTE:
index 1e4bf99ed16e68c68ca87e997895f66dbca4e789..3deed5860a42510078a2377260819aa269d0bc47 100644 (file)
@@ -76,7 +76,7 @@ synproxy_send_tcp(const struct synproxy_net *snet,
                nf_conntrack_get(nfct);
        }
 
-       ip6_local_out(nskb);
+       ip6_local_out(net, nskb->sk, nskb);
        return;
 
 free_nskb:
@@ -244,7 +244,7 @@ synproxy_send_client_ack(const struct synproxy_net *snet,
        synproxy_build_options(nth, opts);
 
        synproxy_send_tcp(snet, skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY,
-                         niph, nth, tcp_hdr_size);
+                         niph, nth, tcp_hdr_size);
 }
 
 static bool
@@ -275,7 +275,7 @@ static unsigned int
 synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_synproxy_info *info = par->targinfo;
-       struct synproxy_net *snet = synproxy_pernet(dev_net(par->in));
+       struct synproxy_net *snet = synproxy_pernet(par->net);
        struct synproxy_options opts = {};
        struct tcphdr *th, _th;
 
@@ -316,11 +316,11 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
        return XT_CONTINUE;
 }
 
-static unsigned int ipv6_synproxy_hook(const struct nf_hook_ops *ops,
+static unsigned int ipv6_synproxy_hook(void *priv,
                                       struct sk_buff *skb,
                                       const struct nf_hook_state *nhs)
 {
-       struct synproxy_net *snet = synproxy_pernet(dev_net(nhs->in ? : nhs->out));
+       struct synproxy_net *snet = synproxy_pernet(nhs->net);
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct;
        struct nf_conn_synproxy *synproxy;
@@ -458,14 +458,12 @@ static struct xt_target synproxy_tg6_reg __read_mostly = {
 static struct nf_hook_ops ipv6_synproxy_ops[] __read_mostly = {
        {
                .hook           = ipv6_synproxy_hook,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
        },
        {
                .hook           = ipv6_synproxy_hook,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_POST_ROUTING,
                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
index 790e0c6b19e1caa41b31c5f279de719581bd6065..1ee1b25df09679c32e0bd21526024cea9f7d26d1 100644 (file)
@@ -26,7 +26,7 @@ static bool rpfilter_addr_unicast(const struct in6_addr *addr)
        return addr_type & IPV6_ADDR_UNICAST;
 }
 
-static bool rpfilter_lookup_reverse6(const struct sk_buff *skb,
+static bool rpfilter_lookup_reverse6(struct net *net, const struct sk_buff *skb,
                                     const struct net_device *dev, u8 flags)
 {
        struct rt6_info *rt;
@@ -53,7 +53,7 @@ static bool rpfilter_lookup_reverse6(const struct sk_buff *skb,
                lookup_flags |= RT6_LOOKUP_F_IFACE;
        }
 
-       rt = (void *) ip6_route_lookup(dev_net(dev), &fl6, lookup_flags);
+       rt = (void *) ip6_route_lookup(net, &fl6, lookup_flags);
        if (rt->dst.error)
                goto out;
 
@@ -93,7 +93,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)
        if (unlikely(saddrtype == IPV6_ADDR_ANY))
                return true ^ invert; /* not routable: forward path will drop it */
 
-       return rpfilter_lookup_reverse6(skb, par->in, info->flags) ^ invert;
+       return rpfilter_lookup_reverse6(par->net, skb, par->in, info->flags) ^ invert;
 }
 
 static int rpfilter_check(const struct xt_mtchk_param *par)
index 5c33d8abc0774e52a99c20273f3349b0b5374fc7..8b277b983ca51886973ab62601d136a0b318f001 100644 (file)
@@ -32,12 +32,10 @@ static const struct xt_table packet_filter = {
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-ip6table_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip6table_filter_hook(void *priv, struct sk_buff *skb,
                     const struct nf_hook_state *state)
 {
-       const struct net *net = dev_net(state->in ? state->in : state->out);
-
-       return ip6t_do_table(skb, ops->hooknum, state, net->ipv6.ip6table_filter);
+       return ip6t_do_table(skb, state, state->net->ipv6.ip6table_filter);
 }
 
 static struct nf_hook_ops *filter_ops __read_mostly;
index b551f5b79fe2b7fa62278ae1f7d9327e82795253..abe278b079322cb929cc16e260722ec49da414d5 100644 (file)
@@ -57,8 +57,7 @@ ip6t_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
        /* flowlabel and prio (includes version, which shouldn't change either */
        flowlabel = *((u_int32_t *)ipv6_hdr(skb));
 
-       ret = ip6t_do_table(skb, NF_INET_LOCAL_OUT, state,
-                           dev_net(state->out)->ipv6.ip6table_mangle);
+       ret = ip6t_do_table(skb, state, state->net->ipv6.ip6table_mangle);
 
        if (ret != NF_DROP && ret != NF_STOLEN &&
            (!ipv6_addr_equal(&ipv6_hdr(skb)->saddr, &saddr) ||
@@ -66,7 +65,7 @@ ip6t_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
             skb->mark != mark ||
             ipv6_hdr(skb)->hop_limit != hop_limit ||
             flowlabel != *((u_int32_t *)ipv6_hdr(skb)))) {
-               err = ip6_route_me_harder(skb);
+               err = ip6_route_me_harder(state->net, skb);
                if (err < 0)
                        ret = NF_DROP_ERR(err);
        }
@@ -76,17 +75,16 @@ ip6t_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-ip6table_mangle_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip6table_mangle_hook(void *priv, struct sk_buff *skb,
                     const struct nf_hook_state *state)
 {
-       if (ops->hooknum == NF_INET_LOCAL_OUT)
+       if (state->hook == NF_INET_LOCAL_OUT)
                return ip6t_mangle_out(skb, state);
-       if (ops->hooknum == NF_INET_POST_ROUTING)
-               return ip6t_do_table(skb, ops->hooknum, state,
-                                    dev_net(state->out)->ipv6.ip6table_mangle);
+       if (state->hook == NF_INET_POST_ROUTING)
+               return ip6t_do_table(skb, state,
+                                    state->net->ipv6.ip6table_mangle);
        /* INPUT/FORWARD */
-       return ip6t_do_table(skb, ops->hooknum, state,
-                            dev_net(state->in)->ipv6.ip6table_mangle);
+       return ip6t_do_table(skb, state, state->net->ipv6.ip6table_mangle);
 }
 
 static struct nf_hook_ops *mangle_ops __read_mostly;
index c3a7f7af0ed4d183d00a5f50307f44fa44399460..de2a10a565f549bc4ae183d1c5af31d29fb106e4 100644 (file)
@@ -30,49 +30,46 @@ static const struct xt_table nf_nat_ipv6_table = {
        .af             = NFPROTO_IPV6,
 };
 
-static unsigned int ip6table_nat_do_chain(const struct nf_hook_ops *ops,
+static unsigned int ip6table_nat_do_chain(void *priv,
                                          struct sk_buff *skb,
                                          const struct nf_hook_state *state,
                                          struct nf_conn *ct)
 {
-       struct net *net = nf_ct_net(ct);
-
-       return ip6t_do_table(skb, ops->hooknum, state, net->ipv6.ip6table_nat);
+       return ip6t_do_table(skb, state, state->net->ipv6.ip6table_nat);
 }
 
-static unsigned int ip6table_nat_fn(const struct nf_hook_ops *ops,
+static unsigned int ip6table_nat_fn(void *priv,
                                    struct sk_buff *skb,
                                    const struct nf_hook_state *state)
 {
-       return nf_nat_ipv6_fn(ops, skb, state, ip6table_nat_do_chain);
+       return nf_nat_ipv6_fn(priv, skb, state, ip6table_nat_do_chain);
 }
 
-static unsigned int ip6table_nat_in(const struct nf_hook_ops *ops,
+static unsigned int ip6table_nat_in(void *priv,
                                    struct sk_buff *skb,
                                    const struct nf_hook_state *state)
 {
-       return nf_nat_ipv6_in(ops, skb, state, ip6table_nat_do_chain);
+       return nf_nat_ipv6_in(priv, skb, state, ip6table_nat_do_chain);
 }
 
-static unsigned int ip6table_nat_out(const struct nf_hook_ops *ops,
+static unsigned int ip6table_nat_out(void *priv,
                                     struct sk_buff *skb,
                                     const struct nf_hook_state *state)
 {
-       return nf_nat_ipv6_out(ops, skb, state, ip6table_nat_do_chain);
+       return nf_nat_ipv6_out(priv, skb, state, ip6table_nat_do_chain);
 }
 
-static unsigned int ip6table_nat_local_fn(const struct nf_hook_ops *ops,
+static unsigned int ip6table_nat_local_fn(void *priv,
                                          struct sk_buff *skb,
                                          const struct nf_hook_state *state)
 {
-       return nf_nat_ipv6_local_fn(ops, skb, state, ip6table_nat_do_chain);
+       return nf_nat_ipv6_local_fn(priv, skb, state, ip6table_nat_do_chain);
 }
 
 static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
        /* Before packet filtering, change destination */
        {
                .hook           = ip6table_nat_in,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_PRE_ROUTING,
                .priority       = NF_IP6_PRI_NAT_DST,
@@ -80,7 +77,6 @@ static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
        /* After packet filtering, change source */
        {
                .hook           = ip6table_nat_out,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_POST_ROUTING,
                .priority       = NF_IP6_PRI_NAT_SRC,
@@ -88,7 +84,6 @@ static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
        /* Before packet filtering, change destination */
        {
                .hook           = ip6table_nat_local_fn,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_LOCAL_OUT,
                .priority       = NF_IP6_PRI_NAT_DST,
@@ -96,7 +91,6 @@ static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
        /* After packet filtering, change source */
        {
                .hook           = ip6table_nat_fn,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP6_PRI_NAT_SRC,
index 0b33caad2b69254e29af5ff38484e37a0dc6c711..9021963565c37703ae478c2a39243a1725c92685 100644 (file)
@@ -19,12 +19,10 @@ static const struct xt_table packet_raw = {
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-ip6table_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip6table_raw_hook(void *priv, struct sk_buff *skb,
                  const struct nf_hook_state *state)
 {
-       const struct net *net = dev_net(state->in ? state->in : state->out);
-
-       return ip6t_do_table(skb, ops->hooknum, state, net->ipv6.ip6table_raw);
+       return ip6t_do_table(skb, state, state->net->ipv6.ip6table_raw);
 }
 
 static struct nf_hook_ops *rawtable_ops __read_mostly;
index fcef83c25f7b3281a92a2d5be27512e057dddfff..0d856fedfeb0c2d1ddc97cb5372fc33eda2f102b 100644 (file)
@@ -36,13 +36,10 @@ static const struct xt_table security_table = {
 };
 
 static unsigned int
-ip6table_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip6table_security_hook(void *priv, struct sk_buff *skb,
                       const struct nf_hook_state *state)
 {
-       const struct net *net = dev_net(state->in ? state->in : state->out);
-
-       return ip6t_do_table(skb, ops->hooknum, state,
-                            net->ipv6.ip6table_security);
+       return ip6t_do_table(skb, state, state->net->ipv6.ip6table_security);
 }
 
 static struct nf_hook_ops *sectbl_ops __read_mostly;
index 7302900c321aff58fcb7dc21794b50e04b1942d8..1aa5848764a78a1691e8bbb9af4240eb1733c73e 100644 (file)
@@ -95,7 +95,7 @@ static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
        return NF_ACCEPT;
 }
 
-static unsigned int ipv6_helper(const struct nf_hook_ops *ops,
+static unsigned int ipv6_helper(void *priv,
                                struct sk_buff *skb,
                                const struct nf_hook_state *state)
 {
@@ -131,7 +131,7 @@ static unsigned int ipv6_helper(const struct nf_hook_ops *ops,
        return helper->help(skb, protoff, ct, ctinfo);
 }
 
-static unsigned int ipv6_confirm(const struct nf_hook_ops *ops,
+static unsigned int ipv6_confirm(void *priv,
                                 struct sk_buff *skb,
                                 const struct nf_hook_state *state)
 {
@@ -165,14 +165,14 @@ out:
        return nf_conntrack_confirm(skb);
 }
 
-static unsigned int ipv6_conntrack_in(const struct nf_hook_ops *ops,
+static unsigned int ipv6_conntrack_in(void *priv,
                                      struct sk_buff *skb,
                                      const struct nf_hook_state *state)
 {
-       return nf_conntrack_in(dev_net(state->in), PF_INET6, ops->hooknum, skb);
+       return nf_conntrack_in(state->net, PF_INET6, state->hook, skb);
 }
 
-static unsigned int ipv6_conntrack_local(const struct nf_hook_ops *ops,
+static unsigned int ipv6_conntrack_local(void *priv,
                                         struct sk_buff *skb,
                                         const struct nf_hook_state *state)
 {
@@ -181,48 +181,42 @@ static unsigned int ipv6_conntrack_local(const struct nf_hook_ops *ops,
                net_notice_ratelimited("ipv6_conntrack_local: packet too short\n");
                return NF_ACCEPT;
        }
-       return nf_conntrack_in(dev_net(state->out), PF_INET6, ops->hooknum, skb);
+       return nf_conntrack_in(state->net, PF_INET6, state->hook, skb);
 }
 
 static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
        {
                .hook           = ipv6_conntrack_in,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_PRE_ROUTING,
                .priority       = NF_IP6_PRI_CONNTRACK,
        },
        {
                .hook           = ipv6_conntrack_local,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_LOCAL_OUT,
                .priority       = NF_IP6_PRI_CONNTRACK,
        },
        {
                .hook           = ipv6_helper,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_POST_ROUTING,
                .priority       = NF_IP6_PRI_CONNTRACK_HELPER,
        },
        {
                .hook           = ipv6_confirm,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_POST_ROUTING,
                .priority       = NF_IP6_PRI_LAST,
        },
        {
                .hook           = ipv6_helper,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP6_PRI_CONNTRACK_HELPER,
        },
        {
                .hook           = ipv6_confirm,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP6_PRI_LAST-1,
index 0e6fae103d33454f70fb5790b71d2529af969636..660bc10c7a9c178ae50c12e0ba6245cb7bb6202d 100644 (file)
@@ -36,6 +36,7 @@ static inline struct nf_icmp_net *icmpv6_pernet(struct net *net)
 
 static bool icmpv6_pkt_to_tuple(const struct sk_buff *skb,
                                unsigned int dataoff,
+                               struct net *net,
                                struct nf_conntrack_tuple *tuple)
 {
        const struct icmp6hdr *hp;
@@ -56,12 +57,12 @@ static const u_int8_t invmap[] = {
        [ICMPV6_ECHO_REQUEST - 128]     = ICMPV6_ECHO_REPLY + 1,
        [ICMPV6_ECHO_REPLY - 128]       = ICMPV6_ECHO_REQUEST + 1,
        [ICMPV6_NI_QUERY - 128]         = ICMPV6_NI_REPLY + 1,
-       [ICMPV6_NI_REPLY - 128]         = ICMPV6_NI_QUERY +1
+       [ICMPV6_NI_REPLY - 128]         = ICMPV6_NI_QUERY + 1
 };
 
 static const u_int8_t noct_valid_new[] = {
        [ICMPV6_MGM_QUERY - 130] = 1,
-       [ICMPV6_MGM_REPORT -130] = 1,
+       [ICMPV6_MGM_REPORT - 130] = 1,
        [ICMPV6_MGM_REDUCTION - 130] = 1,
        [NDISC_ROUTER_SOLICITATION - 130] = 1,
        [NDISC_ROUTER_ADVERTISEMENT - 130] = 1,
@@ -159,7 +160,7 @@ icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
                               skb_network_offset(skb)
                                + sizeof(struct ipv6hdr)
                                + sizeof(struct icmp6hdr),
-                              PF_INET6, &origtuple)) {
+                              PF_INET6, net, &origtuple)) {
                pr_debug("icmpv6_error: Can't get tuple\n");
                return -NF_ACCEPT;
        }
index 701cd2bae0a9224d56005f67d8b9f5e71f45825f..d5efeb87350e7fc8643962036c0b26cfe471cdc4 100644 (file)
@@ -59,7 +59,7 @@ struct nf_ct_frag6_skb_cb
        struct sk_buff          *orig;
 };
 
-#define NFCT_FRAG6_CB(skb)     ((struct nf_ct_frag6_skb_cb*)((skb)->cb))
+#define NFCT_FRAG6_CB(skb)     ((struct nf_ct_frag6_skb_cb *)((skb)->cb))
 
 static struct inet_frags nf_frags;
 
@@ -445,7 +445,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
        skb_reset_transport_header(head);
        skb_push(head, head->data - skb_network_header(head));
 
-       for (fp=head->next; fp; fp = fp->next) {
+       for (fp = head->next; fp; fp = fp->next) {
                head->data_len += fp->len;
                head->len += fp->len;
                if (head->ip_summed != fp->ip_summed)
@@ -563,12 +563,10 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
        return 0;
 }
 
-struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user)
+struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
 {
        struct sk_buff *clone;
        struct net_device *dev = skb->dev;
-       struct net *net = skb_dst(skb) ? dev_net(skb_dst(skb)->dev)
-                                      : dev_net(skb->dev);
        struct frag_hdr *fhdr;
        struct frag_queue *fq;
        struct ipv6hdr *hdr;
@@ -646,15 +644,22 @@ void nf_ct_frag6_consume_orig(struct sk_buff *skb)
                s = s2;
        }
 }
+EXPORT_SYMBOL_GPL(nf_ct_frag6_consume_orig);
 
 static int nf_ct_net_init(struct net *net)
 {
+       int res;
+
        net->nf_frag.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
        net->nf_frag.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
        net->nf_frag.frags.timeout = IPV6_FRAG_TIMEOUT;
-       inet_frags_init_net(&net->nf_frag.frags);
-
-       return nf_ct_frag6_sysctl_register(net);
+       res = inet_frags_init_net(&net->nf_frag.frags);
+       if (res)
+               return res;
+       res = nf_ct_frag6_sysctl_register(net);
+       if (res)
+               inet_frags_uninit_net(&net->nf_frag.frags);
+       return res;
 }
 
 static void nf_ct_net_exit(struct net *net)
index 6d9c0b3d5b8c49d111cca7bd70b9bc5229f0a263..4fdbed5ebfb6bbe92136fc545533914a40be7a2a 100644 (file)
@@ -51,7 +51,7 @@ static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum,
                return IP6_DEFRAG_CONNTRACK_OUT + zone_id;
 }
 
-static unsigned int ipv6_defrag(const struct nf_hook_ops *ops,
+static unsigned int ipv6_defrag(void *priv,
                                struct sk_buff *skb,
                                const struct nf_hook_state *state)
 {
@@ -63,7 +63,8 @@ static unsigned int ipv6_defrag(const struct nf_hook_ops *ops,
                return NF_ACCEPT;
 #endif
 
-       reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(ops->hooknum, skb));
+       reasm = nf_ct_frag6_gather(state->net, skb,
+                                  nf_ct6_defrag_user(state->hook, skb));
        /* queued */
        if (reasm == NULL)
                return NF_STOLEN;
@@ -74,7 +75,7 @@ static unsigned int ipv6_defrag(const struct nf_hook_ops *ops,
 
        nf_ct_frag6_consume_orig(reasm);
 
-       NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, state->sk, reasm,
+       NF_HOOK_THRESH(NFPROTO_IPV6, state->hook, state->net, state->sk, reasm,
                       state->in, state->out,
                       state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
 
@@ -84,14 +85,12 @@ static unsigned int ipv6_defrag(const struct nf_hook_ops *ops,
 static struct nf_hook_ops ipv6_defrag_ops[] = {
        {
                .hook           = ipv6_defrag,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_PRE_ROUTING,
                .priority       = NF_IP6_PRI_CONNTRACK_DEFRAG,
        },
        {
                .hook           = ipv6_defrag,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_LOCAL_OUT,
                .priority       = NF_IP6_PRI_CONNTRACK_DEFRAG,
index c8ab626556a02837506a30a26fdc1fbdf075480d..6989c70ae29f1541e0216d36d58ee142934a83c2 100644 (file)
 #include <net/netfilter/nf_conntrack.h>
 #endif
 
-static struct net *pick_net(struct sk_buff *skb)
-{
-#ifdef CONFIG_NET_NS
-       const struct dst_entry *dst;
-
-       if (skb->dev != NULL)
-               return dev_net(skb->dev);
-       dst = skb_dst(skb);
-       if (dst != NULL && dst->dev != NULL)
-               return dev_net(dst->dev);
-#endif
-       return &init_net;
-}
-
-static bool nf_dup_ipv6_route(struct sk_buff *skb, const struct in6_addr *gw,
-                             int oif)
+static bool nf_dup_ipv6_route(struct net *net, struct sk_buff *skb,
+                             const struct in6_addr *gw, int oif)
 {
        const struct ipv6hdr *iph = ipv6_hdr(skb);
-       struct net *net = pick_net(skb);
        struct dst_entry *dst;
        struct flowi6 fl6;
 
@@ -61,7 +46,7 @@ static bool nf_dup_ipv6_route(struct sk_buff *skb, const struct in6_addr *gw,
        return true;
 }
 
-void nf_dup_ipv6(struct sk_buff *skb, unsigned int hooknum,
+void nf_dup_ipv6(struct net *net, struct sk_buff *skb, unsigned int hooknum,
                 const struct in6_addr *gw, int oif)
 {
        if (this_cpu_read(nf_skb_duplicated))
@@ -81,9 +66,9 @@ void nf_dup_ipv6(struct sk_buff *skb, unsigned int hooknum,
                struct ipv6hdr *iph = ipv6_hdr(skb);
                --iph->hop_limit;
        }
-       if (nf_dup_ipv6_route(skb, gw, oif)) {
+       if (nf_dup_ipv6_route(net, skb, gw, oif)) {
                __this_cpu_write(nf_skb_duplicated, true);
-               ip6_local_out(skb);
+               ip6_local_out(net, skb->sk, skb);
                __this_cpu_write(nf_skb_duplicated, false);
        } else {
                kfree_skb(skb);
index 70fbaed49edbc5511d9327be5c9c0e003dc12e7d..238e70c3f7b7b432f017c592a6cbaf8258629324 100644 (file)
@@ -262,9 +262,9 @@ int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
 EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation);
 
 unsigned int
-nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
+nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
               const struct nf_hook_state *state,
-              unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+              unsigned int (*do_chain)(void *priv,
                                        struct sk_buff *skb,
                                        const struct nf_hook_state *state,
                                        struct nf_conn *ct))
@@ -272,7 +272,7 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
        struct nf_conn *ct;
        enum ip_conntrack_info ctinfo;
        struct nf_conn_nat *nat;
-       enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
+       enum nf_nat_manip_type maniptype = HOOK2MANIP(state->hook);
        __be16 frag_off;
        int hdrlen;
        u8 nexthdr;
@@ -303,7 +303,7 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
 
                if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
                        if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
-                                                            ops->hooknum,
+                                                            state->hook,
                                                             hdrlen))
                                return NF_DROP;
                        else
@@ -317,21 +317,21 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
                if (!nf_nat_initialized(ct, maniptype)) {
                        unsigned int ret;
 
-                       ret = do_chain(ops, skb, state, ct);
+                       ret = do_chain(priv, skb, state, ct);
                        if (ret != NF_ACCEPT)
                                return ret;
 
-                       if (nf_nat_initialized(ct, HOOK2MANIP(ops->hooknum)))
+                       if (nf_nat_initialized(ct, HOOK2MANIP(state->hook)))
                                break;
 
-                       ret = nf_nat_alloc_null_binding(ct, ops->hooknum);
+                       ret = nf_nat_alloc_null_binding(ct, state->hook);
                        if (ret != NF_ACCEPT)
                                return ret;
                } else {
                        pr_debug("Already setup manip %s for ct %p\n",
                                 maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
                                 ct);
-                       if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, state->out))
+                       if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out))
                                goto oif_changed;
                }
                break;
@@ -340,11 +340,11 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
                /* ESTABLISHED */
                NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
                             ctinfo == IP_CT_ESTABLISHED_REPLY);
-               if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, state->out))
+               if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out))
                        goto oif_changed;
        }
 
-       return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
+       return nf_nat_packet(ct, ctinfo, state->hook, skb);
 
 oif_changed:
        nf_ct_kill_acct(ct, ctinfo, skb);
@@ -353,9 +353,9 @@ oif_changed:
 EXPORT_SYMBOL_GPL(nf_nat_ipv6_fn);
 
 unsigned int
-nf_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
+nf_nat_ipv6_in(void *priv, struct sk_buff *skb,
               const struct nf_hook_state *state,
-              unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+              unsigned int (*do_chain)(void *priv,
                                        struct sk_buff *skb,
                                        const struct nf_hook_state *state,
                                        struct nf_conn *ct))
@@ -363,7 +363,7 @@ nf_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
        unsigned int ret;
        struct in6_addr daddr = ipv6_hdr(skb)->daddr;
 
-       ret = nf_nat_ipv6_fn(ops, skb, state, do_chain);
+       ret = nf_nat_ipv6_fn(priv, skb, state, do_chain);
        if (ret != NF_DROP && ret != NF_STOLEN &&
            ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
                skb_dst_drop(skb);
@@ -373,9 +373,9 @@ nf_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
 EXPORT_SYMBOL_GPL(nf_nat_ipv6_in);
 
 unsigned int
-nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
+nf_nat_ipv6_out(void *priv, struct sk_buff *skb,
                const struct nf_hook_state *state,
-               unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+               unsigned int (*do_chain)(void *priv,
                                         struct sk_buff *skb,
                                         const struct nf_hook_state *state,
                                         struct nf_conn *ct))
@@ -391,7 +391,7 @@ nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
        if (skb->len < sizeof(struct ipv6hdr))
                return NF_ACCEPT;
 
-       ret = nf_nat_ipv6_fn(ops, skb, state, do_chain);
+       ret = nf_nat_ipv6_fn(priv, skb, state, do_chain);
 #ifdef CONFIG_XFRM
        if (ret != NF_DROP && ret != NF_STOLEN &&
            !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
@@ -403,7 +403,7 @@ nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
                    (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
                     ct->tuplehash[dir].tuple.src.u.all !=
                     ct->tuplehash[!dir].tuple.dst.u.all)) {
-                       err = nf_xfrm_me_harder(skb, AF_INET6);
+                       err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
                        if (err < 0)
                                ret = NF_DROP_ERR(err);
                }
@@ -414,9 +414,9 @@ nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
 EXPORT_SYMBOL_GPL(nf_nat_ipv6_out);
 
 unsigned int
-nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
+nf_nat_ipv6_local_fn(void *priv, struct sk_buff *skb,
                     const struct nf_hook_state *state,
-                    unsigned int (*do_chain)(const struct nf_hook_ops *ops,
+                    unsigned int (*do_chain)(void *priv,
                                              struct sk_buff *skb,
                                              const struct nf_hook_state *state,
                                              struct nf_conn *ct))
@@ -430,14 +430,14 @@ nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
        if (skb->len < sizeof(struct ipv6hdr))
                return NF_ACCEPT;
 
-       ret = nf_nat_ipv6_fn(ops, skb, state, do_chain);
+       ret = nf_nat_ipv6_fn(priv, skb, state, do_chain);
        if (ret != NF_DROP && ret != NF_STOLEN &&
            (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
                enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
 
                if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
                                      &ct->tuplehash[!dir].tuple.src.u3)) {
-                       err = ip6_route_me_harder(skb);
+                       err = ip6_route_me_harder(state->net, skb);
                        if (err < 0)
                                ret = NF_DROP_ERR(err);
                }
@@ -446,7 +446,7 @@ nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
                         ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
                         ct->tuplehash[dir].tuple.dst.u.all !=
                         ct->tuplehash[!dir].tuple.src.u.all) {
-                       err = nf_xfrm_me_harder(skb, AF_INET6);
+                       err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
                        if (err < 0)
                                ret = NF_DROP_ERR(err);
                }
index 7745609665cd3418ba0b61e68a9a83caf9c3c6c8..31ba7ca19757083a1a83f8388b87be5695b1ba98 100644 (file)
@@ -34,7 +34,7 @@ nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range *range,
        NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
                            ctinfo == IP_CT_RELATED_REPLY));
 
-       if (ipv6_dev_get_saddr(dev_net(out), out,
+       if (ipv6_dev_get_saddr(nf_ct_net(ct), out,
                               &ipv6_hdr(skb)->daddr, 0, &src) < 0)
                return NF_DROP;
 
index 94b4c6dfb400c90b6c368acb7ecb83649309dce0..e0f922b777e3d9333ca4723e422182d083c6afba 100644 (file)
@@ -26,7 +26,7 @@ const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb,
        int tcphoff;
 
        proto = oip6h->nexthdr;
-       tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data),
+       tcphoff = ipv6_skip_exthdr(oldskb, ((u8 *)(oip6h + 1) - oldskb->data),
                                   &proto, &frag_off);
 
        if ((tcphoff < 0) || (tcphoff > oldskb->len)) {
@@ -206,7 +206,7 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
                dev_queue_xmit(nskb);
        } else
 #endif
-               ip6_local_out(nskb);
+               ip6_local_out(net, nskb->sk, nskb);
 }
 EXPORT_SYMBOL_GPL(nf_send_reset6);
 
@@ -224,7 +224,7 @@ static bool reject6_csum_ok(struct sk_buff *skb, int hook)
                return true;
 
        proto = ip6h->nexthdr;
-       thoff = ipv6_skip_exthdr(skb, ((u8*)(ip6h+1) - skb->data), &proto, &fo);
+       thoff = ipv6_skip_exthdr(skb, ((u8 *)(ip6h + 1) - skb->data), &proto, &fo);
 
        if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0)
                return false;
index c8148ba76d1a765e1ee2ba190961045fad033c6b..120ea9131be030d6849d54275179c0c8e36429f6 100644 (file)
 #include <net/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables_ipv6.h>
 
-static unsigned int nft_do_chain_ipv6(const struct nf_hook_ops *ops,
+static unsigned int nft_do_chain_ipv6(void *priv,
                                      struct sk_buff *skb,
                                      const struct nf_hook_state *state)
 {
        struct nft_pktinfo pkt;
 
        /* malformed packet, drop it */
-       if (nft_set_pktinfo_ipv6(&pkt, ops, skb, state) < 0)
+       if (nft_set_pktinfo_ipv6(&pkt, skb, state) < 0)
                return NF_DROP;
 
-       return nft_do_chain(&pkt, ops);
+       return nft_do_chain(&pkt, priv);
 }
 
-static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops,
+static unsigned int nft_ipv6_output(void *priv,
                                    struct sk_buff *skb,
                                    const struct nf_hook_state *state)
 {
@@ -40,7 +40,7 @@ static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops,
                return NF_ACCEPT;
        }
 
-       return nft_do_chain_ipv6(ops, skb, state);
+       return nft_do_chain_ipv6(priv, skb, state);
 }
 
 struct nft_af_info nft_af_ipv6 __read_mostly = {
index 951bb458b7bd53968f76b6e8431f12214a05b88a..443cd306c0b0695f4e60660468d59ef64c1104f8 100644 (file)
 #include <net/netfilter/nf_nat_l3proto.h>
 #include <net/ipv6.h>
 
-static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops,
+static unsigned int nft_nat_do_chain(void *priv,
                                     struct sk_buff *skb,
                                     const struct nf_hook_state *state,
                                     struct nf_conn *ct)
 {
        struct nft_pktinfo pkt;
 
-       nft_set_pktinfo_ipv6(&pkt, ops, skb, state);
+       nft_set_pktinfo_ipv6(&pkt, skb, state);
 
-       return nft_do_chain(&pkt, ops);
+       return nft_do_chain(&pkt, priv);
 }
 
-static unsigned int nft_nat_ipv6_fn(const struct nf_hook_ops *ops,
+static unsigned int nft_nat_ipv6_fn(void *priv,
                                    struct sk_buff *skb,
                                    const struct nf_hook_state *state)
 {
-       return nf_nat_ipv6_fn(ops, skb, state, nft_nat_do_chain);
+       return nf_nat_ipv6_fn(priv, skb, state, nft_nat_do_chain);
 }
 
-static unsigned int nft_nat_ipv6_in(const struct nf_hook_ops *ops,
+static unsigned int nft_nat_ipv6_in(void *priv,
                                    struct sk_buff *skb,
                                    const struct nf_hook_state *state)
 {
-       return nf_nat_ipv6_in(ops, skb, state, nft_nat_do_chain);
+       return nf_nat_ipv6_in(priv, skb, state, nft_nat_do_chain);
 }
 
-static unsigned int nft_nat_ipv6_out(const struct nf_hook_ops *ops,
+static unsigned int nft_nat_ipv6_out(void *priv,
                                     struct sk_buff *skb,
                                     const struct nf_hook_state *state)
 {
-       return nf_nat_ipv6_out(ops, skb, state, nft_nat_do_chain);
+       return nf_nat_ipv6_out(priv, skb, state, nft_nat_do_chain);
 }
 
-static unsigned int nft_nat_ipv6_local_fn(const struct nf_hook_ops *ops,
+static unsigned int nft_nat_ipv6_local_fn(void *priv,
                                          struct sk_buff *skb,
                                          const struct nf_hook_state *state)
 {
-       return nf_nat_ipv6_local_fn(ops, skb, state, nft_nat_do_chain);
+       return nf_nat_ipv6_local_fn(priv, skb, state, nft_nat_do_chain);
 }
 
 static const struct nf_chain_type nft_chain_nat_ipv6 = {
index 0dafdaac5e175062b8c81665834390a8aabe5171..71d995ff3108fe001fc2318aa0e7c76c3cd9b1f8 100644 (file)
@@ -22,7 +22,7 @@
 #include <net/netfilter/nf_tables_ipv6.h>
 #include <net/route.h>
 
-static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
+static unsigned int nf_route_table_hook(void *priv,
                                        struct sk_buff *skb,
                                        const struct nf_hook_state *state)
 {
@@ -33,7 +33,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
        u32 mark, flowlabel;
 
        /* malformed packet, drop it */
-       if (nft_set_pktinfo_ipv6(&pkt, ops, skb, state) < 0)
+       if (nft_set_pktinfo_ipv6(&pkt, skb, state) < 0)
                return NF_DROP;
 
        /* save source/dest address, mark, hoplimit, flowlabel, priority */
@@ -45,14 +45,14 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
        /* flowlabel and prio (includes version, which shouldn't change either */
        flowlabel = *((u32 *)ipv6_hdr(skb));
 
-       ret = nft_do_chain(&pkt, ops);
+       ret = nft_do_chain(&pkt, priv);
        if (ret != NF_DROP && ret != NF_QUEUE &&
            (memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) ||
             memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) ||
             skb->mark != mark ||
             ipv6_hdr(skb)->hop_limit != hop_limit ||
             flowlabel != *((u_int32_t *)ipv6_hdr(skb))))
-               return ip6_route_me_harder(skb) == 0 ? ret : NF_DROP;
+               return ip6_route_me_harder(state->net, skb) == 0 ? ret : NF_DROP;
 
        return ret;
 }
@@ -61,11 +61,11 @@ static const struct nf_chain_type nft_chain_route_ipv6 = {
        .name           = "route",
        .type           = NFT_CHAIN_T_ROUTE,
        .family         = NFPROTO_IPV6,
-        .owner         = THIS_MODULE,
+       .owner          = THIS_MODULE,
        .hook_mask      = (1 << NF_INET_LOCAL_OUT),
        .hooks          = {
-                [NF_INET_LOCAL_OUT]    = nf_route_table_hook,
-        },
+               [NF_INET_LOCAL_OUT]     = nf_route_table_hook,
+       },
 };
 
 static int __init nft_chain_route_init(void)
index 0eaa4f65fdea78d795ef3b841df8a35429fe3c82..8bfd470cbe726678c5da89dff4f24b9fa089b356 100644 (file)
@@ -28,7 +28,7 @@ static void nft_dup_ipv6_eval(const struct nft_expr *expr,
        struct in6_addr *gw = (struct in6_addr *)&regs->data[priv->sreg_addr];
        int oif = regs->data[priv->sreg_dev];
 
-       nf_dup_ipv6(pkt->skb, pkt->ops->hooknum, gw, oif);
+       nf_dup_ipv6(pkt->net, pkt->skb, pkt->hook, gw, oif);
 }
 
 static int nft_dup_ipv6_init(const struct nft_ctx *ctx,
index effd393bd51790ff09c241a4fe207b1fb5f8c19b..aca44e89a88133575b3435f82d71ed5c05e98649 100644 (file)
@@ -35,8 +35,7 @@ static void nft_redir_ipv6_eval(const struct nft_expr *expr,
 
        range.flags |= priv->flags;
 
-       regs->verdict.code = nf_nat_redirect_ipv6(pkt->skb, &range,
-                                                 pkt->ops->hooknum);
+       regs->verdict.code = nf_nat_redirect_ipv6(pkt->skb, &range, pkt->hook);
 }
 
 static struct nft_expr_type nft_redir_ipv6_type;
index d0d1540ecf870a83182354aa64bad36eac354e1b..533cd5719c594e7664c162b59037bb43a10c717c 100644 (file)
@@ -24,15 +24,14 @@ static void nft_reject_ipv6_eval(const struct nft_expr *expr,
                                 const struct nft_pktinfo *pkt)
 {
        struct nft_reject *priv = nft_expr_priv(expr);
-       struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out);
 
        switch (priv->type) {
        case NFT_REJECT_ICMP_UNREACH:
-               nf_send_unreach6(net, pkt->skb, priv->icmp_code,
-                                pkt->ops->hooknum);
+               nf_send_unreach6(pkt->net, pkt->skb, priv->icmp_code,
+                                pkt->hook);
                break;
        case NFT_REJECT_TCP_RST:
-               nf_send_reset6(net, pkt->skb, pkt->ops->hooknum);
+               nf_send_reset6(pkt->net, pkt->skb, pkt->hook);
                break;
        default:
                break;
index 928a0fb0b74406cdf32c89fe7db054b40476dc54..462f2a76b5c2270dba806e193a4c5bde57e14a39 100644 (file)
@@ -138,7 +138,7 @@ int ip6_dst_hoplimit(struct dst_entry *dst)
 EXPORT_SYMBOL(ip6_dst_hoplimit);
 #endif
 
-static int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb)
+int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        int len;
 
@@ -148,30 +148,20 @@ static int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb)
        ipv6_hdr(skb)->payload_len = htons(len);
        IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
 
-       return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
-                      NULL, skb_dst(skb)->dev, dst_output_sk);
-}
-
-int __ip6_local_out(struct sk_buff *skb)
-{
-       return __ip6_local_out_sk(skb->sk, skb);
+       return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
+                      net, sk, skb, NULL, skb_dst(skb)->dev,
+                      dst_output);
 }
 EXPORT_SYMBOL_GPL(__ip6_local_out);
 
-int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb)
+int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        int err;
 
-       err = __ip6_local_out_sk(sk, skb);
+       err = __ip6_local_out(net, sk, skb);
        if (likely(err == 1))
-               err = dst_output_sk(sk, skb);
+               err = dst_output(net, sk, skb);
 
        return err;
 }
-EXPORT_SYMBOL_GPL(ip6_local_out_sk);
-
-int ip6_local_out(struct sk_buff *skb)
-{
-       return ip6_local_out_sk(skb->sk, skb);
-}
 EXPORT_SYMBOL_GPL(ip6_local_out);
index fdbada1569a37348b47b60769f7d679741b21d0a..dc65ec198f7c3f7ae19176fdaa8752c7fb4e13a5 100644 (file)
@@ -614,6 +614,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
                        unsigned int flags)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
+       struct net *net = sock_net(sk);
        struct ipv6hdr *iph;
        struct sk_buff *skb;
        int err;
@@ -652,9 +653,9 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
        if (err)
                goto error_fault;
 
-       IP6_UPD_PO_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len);
-       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
-                     NULL, rt->dst.dev, dst_output_sk);
+       IP6_UPD_PO_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len);
+       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb,
+                     NULL, rt->dst.dev, dst_output);
        if (err > 0)
                err = net_xmit_errno(err);
        if (err)
@@ -666,7 +667,7 @@ error_fault:
        err = -EFAULT;
        kfree_skb(skb);
 error:
-       IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
+       IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
        if (err == -ENOBUFS && !np->recverr)
                err = 0;
        return err;
index f1159bb76e0a54fb55a3e8e382a6cb80c7a929f9..44e21a03cfc3fff395903d36e8e01a7a3e395afa 100644 (file)
@@ -706,13 +706,19 @@ static void ip6_frags_sysctl_unregister(void)
 
 static int __net_init ipv6_frags_init_net(struct net *net)
 {
+       int res;
+
        net->ipv6.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
        net->ipv6.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
        net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT;
 
-       inet_frags_init_net(&net->ipv6.frags);
-
-       return ip6_frags_ns_sysctl_register(net);
+       res = inet_frags_init_net(&net->ipv6.frags);
+       if (res)
+               return res;
+       res = ip6_frags_ns_sysctl_register(net);
+       if (res)
+               inet_frags_uninit_net(&net->ipv6.frags);
+       return res;
 }
 
 static void __net_exit ipv6_frags_exit_net(struct net *net)
index 968f31c01f89e8ccc6a4a13c056f8be3dcc0b7c6..c8bc9b4ac32835e7328025a8912f3627b365e38a 100644 (file)
@@ -61,6 +61,7 @@
 #include <net/nexthop.h>
 #include <net/lwtunnel.h>
 #include <net/ip_tunnels.h>
+#include <net/l3mdev.h>
 
 #include <asm/uaccess.h>
 
@@ -86,9 +87,9 @@ static void           ip6_dst_ifdown(struct dst_entry *,
 static int              ip6_dst_gc(struct dst_ops *ops);
 
 static int             ip6_pkt_discard(struct sk_buff *skb);
-static int             ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb);
+static int             ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
 static int             ip6_pkt_prohibit(struct sk_buff *skb);
-static int             ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb);
+static int             ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
 static void            ip6_link_failure(struct sk_buff *skb);
 static void            ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
                                           struct sk_buff *skb, u32 mtu);
@@ -303,7 +304,7 @@ static const struct rt6_info ip6_blk_hole_entry_template = {
                .obsolete       = DST_OBSOLETE_FORCE_CHK,
                .error          = -EINVAL,
                .input          = dst_discard,
-               .output         = dst_discard_sk,
+               .output         = dst_discard_out,
        },
        .rt6i_flags     = (RTF_REJECT | RTF_NONEXTHOP),
        .rt6i_protocol  = RTPROT_KERNEL,
@@ -421,31 +422,7 @@ static bool rt6_check_expired(const struct rt6_info *rt)
 static int rt6_info_hash_nhsfn(unsigned int candidate_count,
                               const struct flowi6 *fl6)
 {
-       unsigned int val = fl6->flowi6_proto;
-
-       val ^= ipv6_addr_hash(&fl6->daddr);
-       val ^= ipv6_addr_hash(&fl6->saddr);
-
-       /* Work only if this not encapsulated */
-       switch (fl6->flowi6_proto) {
-       case IPPROTO_UDP:
-       case IPPROTO_TCP:
-       case IPPROTO_SCTP:
-               val ^= (__force u16)fl6->fl6_sport;
-               val ^= (__force u16)fl6->fl6_dport;
-               break;
-
-       case IPPROTO_ICMPV6:
-               val ^= (__force u16)fl6->fl6_icmp_type;
-               val ^= (__force u16)fl6->fl6_icmp_code;
-               break;
-       }
-       /* RFC6438 recommands to use flowlabel */
-       val ^= (__force u32)fl6->flowlabel;
-
-       /* Perhaps, we need to tune, this function? */
-       val = val ^ (val >> 7) ^ (val >> 12);
-       return val % candidate_count;
+       return get_hash_from_flowi6(fl6) % candidate_count;
 }
 
 static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
@@ -498,10 +475,10 @@ static inline struct rt6_info *rt6_device_match(struct net *net,
                        if (dev->flags & IFF_LOOPBACK) {
                                if (!sprt->rt6i_idev ||
                                    sprt->rt6i_idev->dev->ifindex != oif) {
-                                       if (flags & RT6_LOOKUP_F_IFACE && oif)
+                                       if (flags & RT6_LOOKUP_F_IFACE)
                                                continue;
-                                       if (local && (!oif ||
-                                                     local->rt6i_idev->dev->ifindex == oif))
+                                       if (local &&
+                                           local->rt6i_idev->dev->ifindex == oif)
                                                continue;
                                }
                                local = sprt;
@@ -538,7 +515,7 @@ static void rt6_probe_deferred(struct work_struct *w)
                container_of(w, struct __rt6_probe_work, work);
 
        addrconf_addr_solict_mult(&work->target, &mcaddr);
-       ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL, NULL);
+       ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, NULL);
        dev_put(work->dev);
        kfree(work);
 }
@@ -1068,6 +1045,9 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
        fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
        saved_fn = fn;
 
+       if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
+               oif = 0;
+
 redo_rt6_select:
        rt = rt6_select(fn, oif, strict);
        if (rt->rt6i_nsiblings)
@@ -1165,7 +1145,7 @@ void ip6_route_input(struct sk_buff *skb)
        int flags = RT6_LOOKUP_F_HAS_SADDR;
        struct ip_tunnel_info *tun_info;
        struct flowi6 fl6 = {
-               .flowi6_iif = skb->dev->ifindex,
+               .flowi6_iif = l3mdev_fib_oif(skb->dev),
                .daddr = iph->daddr,
                .saddr = iph->saddr,
                .flowlabel = ip6_flowinfo(iph),
@@ -1189,15 +1169,22 @@ static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table
 struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
                                    struct flowi6 *fl6)
 {
+       struct dst_entry *dst;
        int flags = 0;
+       bool any_src;
+
+       dst = l3mdev_rt6_dst_by_oif(net, fl6);
+       if (dst)
+               return dst;
 
        fl6->flowi6_iif = LOOPBACK_IFINDEX;
 
+       any_src = ipv6_addr_any(&fl6->saddr);
        if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
-           fl6->flowi6_oif)
+           (fl6->flowi6_oif && any_src))
                flags |= RT6_LOOKUP_F_IFACE;
 
-       if (!ipv6_addr_any(&fl6->saddr))
+       if (!any_src)
                flags |= RT6_LOOKUP_F_HAS_SADDR;
        else if (sk)
                flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
@@ -1218,7 +1205,7 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori
                new = &rt->dst;
                new->__use = 1;
                new->input = dst_discard;
-               new->output = dst_discard_sk;
+               new->output = dst_discard_out;
 
                dst_copy_metrics(new, &ort->dst);
                rt->rt6i_idev = ort->rt6i_idev;
@@ -1744,21 +1731,21 @@ static int ip6_convert_metrics(struct mx6_config *mxc,
        return -EINVAL;
 }
 
-int ip6_route_info_create(struct fib6_config *cfg, struct rt6_info **rt_ret)
+static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg)
 {
-       int err;
        struct net *net = cfg->fc_nlinfo.nl_net;
        struct rt6_info *rt = NULL;
        struct net_device *dev = NULL;
        struct inet6_dev *idev = NULL;
        struct fib6_table *table;
        int addr_type;
+       int err = -EINVAL;
 
        if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
-               return -EINVAL;
+               goto out;
 #ifndef CONFIG_IPV6_SUBTREES
        if (cfg->fc_src_len)
-               return -EINVAL;
+               goto out;
 #endif
        if (cfg->fc_ifindex) {
                err = -ENODEV;
@@ -1873,7 +1860,7 @@ int ip6_route_info_create(struct fib6_config *cfg, struct rt6_info **rt_ret)
                switch (cfg->fc_type) {
                case RTN_BLACKHOLE:
                        rt->dst.error = -EINVAL;
-                       rt->dst.output = dst_discard_sk;
+                       rt->dst.output = dst_discard_out;
                        rt->dst.input = dst_discard;
                        break;
                case RTN_PROHIBIT:
@@ -1978,9 +1965,7 @@ install_route:
 
        cfg->fc_nlinfo.nl_net = dev_net(dev);
 
-       *rt_ret = rt;
-
-       return 0;
+       return rt;
 out:
        if (dev)
                dev_put(dev);
@@ -1989,20 +1974,21 @@ out:
        if (rt)
                dst_free(&rt->dst);
 
-       *rt_ret = NULL;
-
-       return err;
+       return ERR_PTR(err);
 }
 
 int ip6_route_add(struct fib6_config *cfg)
 {
        struct mx6_config mxc = { .mx = NULL, };
-       struct rt6_info *rt = NULL;
+       struct rt6_info *rt;
        int err;
 
-       err = ip6_route_info_create(cfg, &rt);
-       if (err)
+       rt = ip6_route_info_create(cfg);
+       if (IS_ERR(rt)) {
+               err = PTR_ERR(rt);
+               rt = NULL;
                goto out;
+       }
 
        err = ip6_convert_metrics(&mxc, cfg);
        if (err)
@@ -2094,7 +2080,6 @@ static int ip6_route_del(struct fib6_config *cfg)
 
 static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
 {
-       struct net *net = dev_net(skb->dev);
        struct netevent_redirect netevent;
        struct rt6_info *rt, *nrt = NULL;
        struct ndisc_options ndopts;
@@ -2155,7 +2140,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
        }
 
        rt = (struct rt6_info *) dst;
-       if (rt == net->ipv6.ip6_null_entry) {
+       if (rt->rt6i_flags & RTF_REJECT) {
                net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
                return;
        }
@@ -2284,7 +2269,6 @@ static struct rt6_info *rt6_add_route_info(struct net *net,
                                           unsigned int pref)
 {
        struct fib6_config cfg = {
-               .fc_table       = RT6_TABLE_INFO,
                .fc_metric      = IP6_RT_PRIO_USER,
                .fc_ifindex     = ifindex,
                .fc_dst_len     = prefixlen,
@@ -2295,6 +2279,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net,
                .fc_nlinfo.nl_net = net,
        };
 
+       cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO;
        cfg.fc_dst = *prefix;
        cfg.fc_gateway = *gwaddr;
 
@@ -2335,7 +2320,7 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
                                     unsigned int pref)
 {
        struct fib6_config cfg = {
-               .fc_table       = RT6_TABLE_DFLT,
+               .fc_table       = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
                .fc_metric      = IP6_RT_PRIO_USER,
                .fc_ifindex     = dev->ifindex,
                .fc_flags       = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
@@ -2382,7 +2367,8 @@ static void rtmsg_to_fib6_config(struct net *net,
 {
        memset(cfg, 0, sizeof(*cfg));
 
-       cfg->fc_table = RT6_TABLE_MAIN;
+       cfg->fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
+                        : RT6_TABLE_MAIN;
        cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
        cfg->fc_metric = rtmsg->rtmsg_metric;
        cfg->fc_expires = rtmsg->rtmsg_info;
@@ -2466,7 +2452,7 @@ static int ip6_pkt_discard(struct sk_buff *skb)
        return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
 }
 
-static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb)
+static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        skb->dev = skb_dst(skb)->dev;
        return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
@@ -2477,7 +2463,7 @@ static int ip6_pkt_prohibit(struct sk_buff *skb)
        return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
 }
 
-static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb)
+static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        skb->dev = skb_dst(skb)->dev;
        return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
@@ -2491,6 +2477,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
                                    const struct in6_addr *addr,
                                    bool anycast)
 {
+       u32 tb_id;
        struct net *net = dev_net(idev->dev);
        struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
                                            DST_NOCOUNT);
@@ -2513,7 +2500,8 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
        rt->rt6i_gateway  = *addr;
        rt->rt6i_dst.addr = *addr;
        rt->rt6i_dst.plen = 128;
-       rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
+       tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL;
+       rt->rt6i_table = fib6_get_table(net, tb_id);
        rt->dst.flags |= DST_NOCACHE;
 
        atomic_set(&rt->dst.__refcnt, 1);
@@ -2892,9 +2880,12 @@ static int ip6_route_multipath_add(struct fib6_config *cfg)
                                r_cfg.fc_encap_type = nla_get_u16(nla);
                }
 
-               err = ip6_route_info_create(&r_cfg, &rt);
-               if (err)
+               rt = ip6_route_info_create(&r_cfg);
+               if (IS_ERR(rt)) {
+                       err = PTR_ERR(rt);
+                       rt = NULL;
                        goto cleanup;
+               }
 
                err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg);
                if (err) {
@@ -3273,6 +3264,11 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
        } else {
                fl6.flowi6_oif = oif;
 
+               if (netif_index_is_l3_master(net, oif)) {
+                       fl6.flowi6_flags = FLOWI_FLAG_L3MDEV_SRC |
+                                          FLOWI_FLAG_SKIP_NH_OIF;
+               }
+
                rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
        }
 
index 94428fd85b2f9543c0e3015f785306ce203e73dc..dcccae86190f2a29c5ea8199320bbf399c7350b1 100644 (file)
@@ -1394,34 +1394,20 @@ static int ipip6_tunnel_init(struct net_device *dev)
        return 0;
 }
 
-static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
+static void __net_init ipip6_fb_tunnel_init(struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
        struct iphdr *iph = &tunnel->parms.iph;
        struct net *net = dev_net(dev);
        struct sit_net *sitn = net_generic(net, sit_net_id);
 
-       tunnel->dev = dev;
-       tunnel->net = dev_net(dev);
-
        iph->version            = 4;
        iph->protocol           = IPPROTO_IPV6;
        iph->ihl                = 5;
        iph->ttl                = 64;
 
-       dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
-       if (!dev->tstats)
-               return -ENOMEM;
-
-       tunnel->dst_cache = alloc_percpu(struct ip_tunnel_dst);
-       if (!tunnel->dst_cache) {
-               free_percpu(dev->tstats);
-               return -ENOMEM;
-       }
-
        dev_hold(dev);
        rcu_assign_pointer(sitn->tunnels_wc[0], tunnel);
-       return 0;
 }
 
 static int ipip6_validate(struct nlattr *tb[], struct nlattr *data[])
@@ -1831,23 +1817,19 @@ static int __net_init sit_init_net(struct net *net)
         */
        sitn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
 
-       err = ipip6_fb_tunnel_init(sitn->fb_tunnel_dev);
-       if (err)
-               goto err_dev_free;
-
-       ipip6_tunnel_clone_6rd(sitn->fb_tunnel_dev, sitn);
        err = register_netdev(sitn->fb_tunnel_dev);
        if (err)
                goto err_reg_dev;
 
+       ipip6_tunnel_clone_6rd(sitn->fb_tunnel_dev, sitn);
+       ipip6_fb_tunnel_init(sitn->fb_tunnel_dev);
+
        t = netdev_priv(sitn->fb_tunnel_dev);
 
        strcpy(t->parms.name, sitn->fb_tunnel_dev->name);
        return 0;
 
 err_reg_dev:
-       dev_put(sitn->fb_tunnel_dev);
-err_dev_free:
        ipip6_dev_free(sitn->fb_tunnel_dev);
 err_alloc_dev:
        return err;
index 0909f4e0d53c200f6643883df29682bca8e86fbb..bb8f2fa1c7fbbe1ac0936688ad684ae30679d917 100644 (file)
@@ -114,14 +114,11 @@ u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph,
 }
 EXPORT_SYMBOL_GPL(__cookie_v6_init_sequence);
 
-__u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb, __u16 *mssp)
+__u32 cookie_v6_init_sequence(const struct sk_buff *skb, __u16 *mssp)
 {
        const struct ipv6hdr *iph = ipv6_hdr(skb);
        const struct tcphdr *th = tcp_hdr(skb);
 
-       tcp_synq_overflow(sk);
-       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT);
-
        return __cookie_v6_init_sequence(iph, th, mssp);
 }
 
@@ -173,7 +170,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
                goto out;
 
        ret = NULL;
-       req = inet_reqsk_alloc(&tcp6_request_sock_ops, sk);
+       req = inet_reqsk_alloc(&tcp6_request_sock_ops, sk, false);
        if (!req)
                goto out;
 
@@ -210,7 +207,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
        ireq->wscale_ok         = tcp_opt.wscale_ok;
        ireq->tstamp_ok         = tcp_opt.saw_tstamp;
        req->ts_recent          = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0;
-       treq->snt_synack        = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0;
+       treq->snt_synack.v64    = 0;
        treq->rcv_isn = ntohl(th->seq) - 1;
        treq->snt_isn = cookie;
 
@@ -238,9 +235,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
                        goto out_free;
        }
 
-       req->window_clamp = tp->window_clamp ? :dst_metric(dst, RTAX_WINDOW);
+       req->rsk_window_clamp = tp->window_clamp ? :dst_metric(dst, RTAX_WINDOW);
        tcp_select_initial_window(tcp_full_space(sk), req->mss,
-                                 &req->rcv_wnd, &req->window_clamp,
+                                 &req->rsk_rcv_wnd, &req->rsk_window_clamp,
                                  ireq->wscale_ok, &rcv_wscale,
                                  dst_metric(dst, RTAX_INITRWND));
 
index 97d9314ea3611eeadd576d2fd8919ae60d468891..ea2f4d5440b58266f46525114e7bd9d025fb91d6 100644 (file)
@@ -70,8 +70,8 @@
 #include <linux/crypto.h>
 #include <linux/scatterlist.h>
 
-static void    tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb);
-static void    tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
+static void    tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb);
+static void    tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
                                      struct request_sock *req);
 
 static int     tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
@@ -82,7 +82,7 @@ static const struct inet_connection_sock_af_ops ipv6_specific;
 static const struct tcp_sock_af_ops tcp_sock_ipv6_specific;
 static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific;
 #else
-static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,
+static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(const struct sock *sk,
                                                   const struct in6_addr *addr)
 {
        return NULL;
@@ -434,11 +434,11 @@ out:
 }
 
 
-static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
+static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst,
                              struct flowi *fl,
                              struct request_sock *req,
-                             u16 queue_mapping,
-                             struct tcp_fastopen_cookie *foc)
+                             struct tcp_fastopen_cookie *foc,
+                             bool attach_req)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -447,10 +447,11 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
        int err = -ENOMEM;
 
        /* First, grab a route. */
-       if (!dst && (dst = inet6_csk_route_req(sk, fl6, req)) == NULL)
+       if (!dst && (dst = inet6_csk_route_req(sk, fl6, req,
+                                              IPPROTO_TCP)) == NULL)
                goto done;
 
-       skb = tcp_make_synack(sk, dst, req, foc);
+       skb = tcp_make_synack(sk, dst, req, foc, attach_req);
 
        if (skb) {
                __tcp_v6_send_check(skb, &ireq->ir_v6_loc_addr,
@@ -460,7 +461,6 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
                if (np->repflow && ireq->pktopts)
                        fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts));
 
-               skb_set_queue_mapping(skb, queue_mapping);
                err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass);
                err = net_xmit_eval(err);
        }
@@ -476,13 +476,13 @@ static void tcp_v6_reqsk_destructor(struct request_sock *req)
 }
 
 #ifdef CONFIG_TCP_MD5SIG
-static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,
+static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(const struct sock *sk,
                                                   const struct in6_addr *addr)
 {
        return tcp_md5_do_lookup(sk, (union tcp_md5_addr *)addr, AF_INET6);
 }
 
-static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk,
+static struct tcp_md5sig_key *tcp_v6_md5_lookup(const struct sock *sk,
                                                const struct sock *addr_sk)
 {
        return tcp_v6_md5_do_lookup(sk, &addr_sk->sk_v6_daddr);
@@ -621,8 +621,12 @@ clear_hash_noput:
        return 1;
 }
 
-static bool tcp_v6_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
+#endif
+
+static bool tcp_v6_inbound_md5_hash(const struct sock *sk,
+                                   const struct sk_buff *skb)
 {
+#ifdef CONFIG_TCP_MD5SIG
        const __u8 *hash_location = NULL;
        struct tcp_md5sig_key *hash_expected;
        const struct ipv6hdr *ip6h = ipv6_hdr(skb);
@@ -659,26 +663,27 @@ static bool tcp_v6_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
                                     &ip6h->daddr, ntohs(th->dest));
                return true;
        }
+#endif
        return false;
 }
-#endif
 
-static void tcp_v6_init_req(struct request_sock *req, struct sock *sk,
+static void tcp_v6_init_req(struct request_sock *req,
+                           const struct sock *sk_listener,
                            struct sk_buff *skb)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
-       struct ipv6_pinfo *np = inet6_sk(sk);
+       const struct ipv6_pinfo *np = inet6_sk(sk_listener);
 
        ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr;
        ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
 
        /* So that link locals have meaning */
-       if (!sk->sk_bound_dev_if &&
+       if (!sk_listener->sk_bound_dev_if &&
            ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
                ireq->ir_iif = tcp_v6_iif(skb);
 
        if (!TCP_SKB_CB(skb)->tcp_tw_isn &&
-           (ipv6_opt_accepted(sk, skb, &TCP_SKB_CB(skb)->header.h6) ||
+           (ipv6_opt_accepted(sk_listener, skb, &TCP_SKB_CB(skb)->header.h6) ||
             np->rxopt.bits.rxinfo ||
             np->rxopt.bits.rxoinfo || np->rxopt.bits.rxhlim ||
             np->rxopt.bits.rxohlim || np->repflow)) {
@@ -687,13 +692,14 @@ static void tcp_v6_init_req(struct request_sock *req, struct sock *sk,
        }
 }
 
-static struct dst_entry *tcp_v6_route_req(struct sock *sk, struct flowi *fl,
+static struct dst_entry *tcp_v6_route_req(const struct sock *sk,
+                                         struct flowi *fl,
                                          const struct request_sock *req,
                                          bool *strict)
 {
        if (strict)
                *strict = true;
-       return inet6_csk_route_req(sk, &fl->u.ip6, req);
+       return inet6_csk_route_req(sk, &fl->u.ip6, req, IPPROTO_TCP);
 }
 
 struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
@@ -720,10 +726,9 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
        .route_req      =       tcp_v6_route_req,
        .init_seq       =       tcp_v6_init_sequence,
        .send_synack    =       tcp_v6_send_synack,
-       .queue_hash_add =       inet6_csk_reqsk_queue_hash_add,
 };
 
-static void tcp_v6_send_response(struct sock *sk, struct sk_buff *skb, u32 seq,
+static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 seq,
                                 u32 ack, u32 win, u32 tsval, u32 tsecr,
                                 int oif, struct tcp_md5sig_key *key, int rst,
                                 u8 tclass, u32 label)
@@ -822,7 +827,7 @@ static void tcp_v6_send_response(struct sock *sk, struct sk_buff *skb, u32 seq,
        kfree_skb(buff);
 }
 
-static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
+static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 {
        const struct tcphdr *th = tcp_hdr(skb);
        u32 seq = 0, ack_seq = 0;
@@ -893,7 +898,7 @@ release_sk1:
 #endif
 }
 
-static void tcp_v6_send_ack(struct sock *sk, struct sk_buff *skb, u32 seq,
+static void tcp_v6_send_ack(const struct sock *sk, struct sk_buff *skb, u32 seq,
                            u32 ack, u32 win, u32 tsval, u32 tsecr, int oif,
                            struct tcp_md5sig_key *key, u8 tclass,
                            u32 label)
@@ -916,7 +921,7 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
        inet_twsk_put(tw);
 }
 
-static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
+static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
                                  struct request_sock *req)
 {
        /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
@@ -924,44 +929,18 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
         */
        tcp_v6_send_ack(sk, skb, (sk->sk_state == TCP_LISTEN) ?
                        tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt,
-                       tcp_rsk(req)->rcv_nxt, req->rcv_wnd,
+                       tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd,
                        tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if,
                        tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr),
                        0, 0);
 }
 
 
-static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb)
+static struct sock *tcp_v6_cookie_check(struct sock *sk, struct sk_buff *skb)
 {
+#ifdef CONFIG_SYN_COOKIES
        const struct tcphdr *th = tcp_hdr(skb);
-       struct request_sock *req;
-       struct sock *nsk;
-
-       /* Find possible connection requests. */
-       req = inet6_csk_search_req(sk, th->source,
-                                  &ipv6_hdr(skb)->saddr,
-                                  &ipv6_hdr(skb)->daddr, tcp_v6_iif(skb));
-       if (req) {
-               nsk = tcp_check_req(sk, skb, req, false);
-               if (!nsk || nsk == sk)
-                       reqsk_put(req);
-               return nsk;
-       }
-       nsk = __inet6_lookup_established(sock_net(sk), &tcp_hashinfo,
-                                        &ipv6_hdr(skb)->saddr, th->source,
-                                        &ipv6_hdr(skb)->daddr, ntohs(th->dest),
-                                        tcp_v6_iif(skb));
-
-       if (nsk) {
-               if (nsk->sk_state != TCP_TIME_WAIT) {
-                       bh_lock_sock(nsk);
-                       return nsk;
-               }
-               inet_twsk_put(inet_twsk(nsk));
-               return NULL;
-       }
 
-#ifdef CONFIG_SYN_COOKIES
        if (!th->syn)
                sk = cookie_v6_check(sk, skb);
 #endif
@@ -984,12 +963,15 @@ drop:
        return 0; /* don't send reset */
 }
 
-static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
+static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
                                         struct request_sock *req,
-                                        struct dst_entry *dst)
+                                        struct dst_entry *dst,
+                                        struct request_sock *req_unhash,
+                                        bool *own_req)
 {
        struct inet_request_sock *ireq;
-       struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
+       struct ipv6_pinfo *newnp;
+       const struct ipv6_pinfo *np = inet6_sk(sk);
        struct tcp6_sock *newtcp6sk;
        struct inet_sock *newinet;
        struct tcp_sock *newtp;
@@ -1004,7 +986,8 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                 *      v6 mapped
                 */
 
-               newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst);
+               newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst,
+                                            req_unhash, own_req);
 
                if (!newsk)
                        return NULL;
@@ -1057,7 +1040,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                goto out_overflow;
 
        if (!dst) {
-               dst = inet6_csk_route_req(sk, &fl6, req);
+               dst = inet6_csk_route_req(sk, &fl6, req, IPPROTO_TCP);
                if (!dst)
                        goto out;
        }
@@ -1090,8 +1073,6 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        newsk->sk_v6_rcv_saddr = ireq->ir_v6_loc_addr;
        newsk->sk_bound_dev_if = ireq->ir_iif;
 
-       sk_set_txhash(newsk);
-
        /* Now IPv6 options...
 
           First: no IPv4 options.
@@ -1103,16 +1084,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        /* Clone RX bits */
        newnp->rxopt.all = np->rxopt.all;
 
-       /* Clone pktoptions received with SYN */
        newnp->pktoptions = NULL;
-       if (ireq->pktopts) {
-               newnp->pktoptions = skb_clone(ireq->pktopts,
-                                             sk_gfp_atomic(sk, GFP_ATOMIC));
-               consume_skb(ireq->pktopts);
-               ireq->pktopts = NULL;
-               if (newnp->pktoptions)
-                       skb_set_owner_r(newnp->pktoptions, newsk);
-       }
        newnp->opt        = NULL;
        newnp->mcast_oif  = tcp_v6_iif(skb);
        newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
@@ -1167,7 +1139,16 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                tcp_done(newsk);
                goto out;
        }
-       __inet_hash(newsk, NULL);
+       *own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash));
+       /* Clone pktoptions received with SYN, if we own the req */
+       if (*own_req && ireq->pktopts) {
+               newnp->pktoptions = skb_clone(ireq->pktopts,
+                                             sk_gfp_atomic(sk, GFP_ATOMIC));
+               consume_skb(ireq->pktopts);
+               ireq->pktopts = NULL;
+               if (newnp->pktoptions)
+                       skb_set_owner_r(newnp->pktoptions, newsk);
+       }
 
        return newsk;
 
@@ -1181,7 +1162,7 @@ out:
 }
 
 /* The socket must have it's spinlock held when we get
- * here.
+ * here, unless it is a TCP_LISTEN socket.
  *
  * We have a potential double-lock case here, so even when
  * doing backlog processing we use the BH locking scheme.
@@ -1252,18 +1233,14 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
                goto csum_err;
 
        if (sk->sk_state == TCP_LISTEN) {
-               struct sock *nsk = tcp_v6_hnd_req(sk, skb);
+               struct sock *nsk = tcp_v6_cookie_check(sk, skb);
+
                if (!nsk)
                        goto discard;
 
-               /*
-                * Queue it on the new socket if the new socket is active,
-                * otherwise we just shortcircuit this and continue with
-                * the new socket..
-                */
                if (nsk != sk) {
                        sock_rps_save_rxhash(nsk, skb);
-                       sk_mark_napi_id(sk, skb);
+                       sk_mark_napi_id(nsk, skb);
                        if (tcp_child_process(sk, nsk, skb))
                                goto reset;
                        if (opt_skb)
@@ -1273,7 +1250,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
        } else
                sock_rps_save_rxhash(sk, skb);
 
-       if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len))
+       if (tcp_rcv_state_process(sk, skb))
                goto reset;
        if (opt_skb)
                goto ipv6_pktoptions;
@@ -1387,6 +1364,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)
        th = tcp_hdr(skb);
        hdr = ipv6_hdr(skb);
 
+lookup:
        sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest,
                                inet6_iif(skb));
        if (!sk)
@@ -1396,6 +1374,37 @@ process:
        if (sk->sk_state == TCP_TIME_WAIT)
                goto do_time_wait;
 
+       if (sk->sk_state == TCP_NEW_SYN_RECV) {
+               struct request_sock *req = inet_reqsk(sk);
+               struct sock *nsk = NULL;
+
+               sk = req->rsk_listener;
+               tcp_v6_fill_cb(skb, hdr, th);
+               if (tcp_v6_inbound_md5_hash(sk, skb)) {
+                       reqsk_put(req);
+                       goto discard_it;
+               }
+               if (likely(sk->sk_state == TCP_LISTEN)) {
+                       nsk = tcp_check_req(sk, skb, req, false);
+               } else {
+                       inet_csk_reqsk_queue_drop_and_put(sk, req);
+                       goto lookup;
+               }
+               if (!nsk) {
+                       reqsk_put(req);
+                       goto discard_it;
+               }
+               if (nsk == sk) {
+                       sock_hold(sk);
+                       reqsk_put(req);
+                       tcp_v6_restore_cb(skb);
+               } else if (tcp_child_process(sk, nsk, skb)) {
+                       tcp_v6_send_reset(nsk, skb);
+                       goto discard_it;
+               } else {
+                       return 0;
+               }
+       }
        if (hdr->hop_limit < inet6_sk(sk)->min_hopcount) {
                NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
                goto discard_and_relse;
@@ -1406,17 +1415,21 @@ process:
 
        tcp_v6_fill_cb(skb, hdr, th);
 
-#ifdef CONFIG_TCP_MD5SIG
        if (tcp_v6_inbound_md5_hash(sk, skb))
                goto discard_and_relse;
-#endif
 
        if (sk_filter(sk, skb))
                goto discard_and_relse;
 
-       sk_incoming_cpu_update(sk);
        skb->dev = NULL;
 
+       if (sk->sk_state == TCP_LISTEN) {
+               ret = tcp_v6_do_rcv(sk, skb);
+               goto put_and_return;
+       }
+
+       sk_incoming_cpu_update(sk);
+
        bh_lock_sock_nested(sk);
        tcp_sk(sk)->segs_in += max_t(u16, 1, skb_shinfo(skb)->gso_segs);
        ret = 0;
@@ -1431,6 +1444,7 @@ process:
        }
        bh_unlock_sock(sk);
 
+put_and_return:
        sock_put(sk);
        return ret ? -1 : 0;
 
@@ -1631,7 +1645,7 @@ static void tcp_v6_destroy_sock(struct sock *sk)
 #ifdef CONFIG_PROC_FS
 /* Proc filesystem TCPv6 sock list dumping. */
 static void get_openreq6(struct seq_file *seq,
-                        struct request_sock *req, int i, kuid_t uid)
+                        const struct request_sock *req, int i)
 {
        long ttd = req->rsk_timer.expires - jiffies;
        const struct in6_addr *src = &inet_rsk(req)->ir_v6_loc_addr;
@@ -1655,7 +1669,8 @@ static void get_openreq6(struct seq_file *seq,
                   1,   /* timers active (only the expire timer) */
                   jiffies_to_clock_t(ttd),
                   req->num_timeout,
-                  from_kuid_munged(seq_user_ns(seq), uid),
+                  from_kuid_munged(seq_user_ns(seq),
+                                   sock_i_uid(req->rsk_listener)),
                   0,  /* non standard timer */
                   0, /* open_requests have no inode */
                   0, req);
@@ -1670,7 +1685,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
        const struct inet_sock *inet = inet_sk(sp);
        const struct tcp_sock *tp = tcp_sk(sp);
        const struct inet_connection_sock *icsk = inet_csk(sp);
-       struct fastopen_queue *fastopenq = icsk->icsk_accept_queue.fastopenq;
+       const struct fastopen_queue *fastopenq = &icsk->icsk_accept_queue.fastopenq;
 
        dest  = &sp->sk_v6_daddr;
        src   = &sp->sk_v6_rcv_saddr;
@@ -1714,7 +1729,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
                   (icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong,
                   tp->snd_cwnd,
                   sp->sk_state == TCP_LISTEN ?
-                       (fastopenq ? fastopenq->max_qlen : 0) :
+                       fastopenq->max_qlen :
                        (tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh)
                   );
 }
@@ -1760,18 +1775,12 @@ static int tcp6_seq_show(struct seq_file *seq, void *v)
        }
        st = seq->private;
 
-       switch (st->state) {
-       case TCP_SEQ_STATE_LISTENING:
-       case TCP_SEQ_STATE_ESTABLISHED:
-               if (sk->sk_state == TCP_TIME_WAIT)
-                       get_timewait6_sock(seq, v, st->num);
-               else
-                       get_tcp6_sock(seq, v, st->num);
-               break;
-       case TCP_SEQ_STATE_OPENREQ:
-               get_openreq6(seq, v, st->num, st->uid);
-               break;
-       }
+       if (sk->sk_state == TCP_TIME_WAIT)
+               get_timewait6_sock(seq, v, st->num);
+       else if (sk->sk_state == TCP_NEW_SYN_RECV)
+               get_openreq6(seq, v, st->num);
+       else
+               get_tcp6_sock(seq, v, st->num);
 out:
        return 0;
 }
index 3c758007b327decd0f1d950ae6cca928af052b05..dae25cad05cd788146321016597a7badb53ef3d4 100644 (file)
@@ -144,6 +144,16 @@ static void tunnel6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                        break;
 }
 
+static void tunnel46_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+                        u8 type, u8 code, int offset, __be32 info)
+{
+       struct xfrm6_tunnel *handler;
+
+       for_each_tunnel_rcu(tunnel46_handlers, handler)
+               if (!handler->err_handler(skb, opt, type, code, offset, info))
+                       break;
+}
+
 static const struct inet6_protocol tunnel6_protocol = {
        .handler        = tunnel6_rcv,
        .err_handler    = tunnel6_err,
@@ -152,7 +162,7 @@ static const struct inet6_protocol tunnel6_protocol = {
 
 static const struct inet6_protocol tunnel46_protocol = {
        .handler        = tunnel46_rcv,
-       .err_handler    = tunnel6_err,
+       .err_handler    = tunnel46_err,
        .flags          = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
index 0aba654f5b91c198cce365a5dd619e30b566e798..01bcb49619ee6659aefbd198d40f25bab8319749 100644 (file)
@@ -182,10 +182,12 @@ static inline int compute_score(struct sock *sk, struct net *net,
                score++;
        }
 
+       if (sk->sk_incoming_cpu == raw_smp_processor_id())
+               score++;
+
        return score;
 }
 
-#define SCORE2_MAX (1 + 1 + 1)
 static inline int compute_score2(struct sock *sk, struct net *net,
                                 const struct in6_addr *saddr, __be16 sport,
                                 const struct in6_addr *daddr,
@@ -223,6 +225,9 @@ static inline int compute_score2(struct sock *sk, struct net *net,
                score++;
        }
 
+       if (sk->sk_incoming_cpu == raw_smp_processor_id())
+               score++;
+
        return score;
 }
 
@@ -251,8 +256,7 @@ begin:
                                hash = udp6_ehashfn(net, daddr, hnum,
                                                    saddr, sport);
                                matches = 1;
-                       } else if (score == SCORE2_MAX)
-                               goto exact_match;
+                       }
                } else if (score == badness && reuseport) {
                        matches++;
                        if (reciprocal_scale(hash, matches) == 0)
@@ -269,7 +273,6 @@ begin:
                goto begin;
 
        if (result) {
-exact_match:
                if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
                        result = NULL;
                else if (unlikely(compute_score2(result, net, saddr, sport,
index 74bd17882a2fe5126012fae7625254a56f14e20d..0eaab1fa6be5751734d98d900ae4d296ca12651e 100644 (file)
@@ -42,8 +42,8 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
        ipv6_hdr(skb)->payload_len = htons(skb->len);
        __skb_push(skb, skb->data - skb_network_header(skb));
 
-       NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, NULL, skb,
-               skb->dev, NULL,
+       NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
+               dev_net(skb->dev), NULL, skb, skb->dev, NULL,
                ip6_rcv_finish);
        return -1;
 }
index 09c76a7b474dbcb12cae8aeba6fcba375d0d329a..4d09ce6fa90e666bfdeda09cbdc8c7b0cb8b5824 100644 (file)
@@ -79,6 +79,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
 
        if (!skb->ignore_df && skb->len > mtu) {
                skb->dev = dst->dev;
+               skb->protocol = htons(ETH_P_IPV6);
 
                if (xfrm6_local_dontfrag(skb))
                        xfrm6_local_rxpmtu(skb, mtu);
@@ -131,44 +132,57 @@ int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb)
        return xfrm_output(sk, skb);
 }
 
-static int __xfrm6_output(struct sock *sk, struct sk_buff *skb)
+static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+{
+       struct xfrm_state *x = skb_dst(skb)->xfrm;
+
+       return x->outer_mode->afinfo->output_finish(sk, skb);
+}
+
+static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct xfrm_state *x = dst->xfrm;
        int mtu;
+       bool toobig;
 
 #ifdef CONFIG_NETFILTER
        if (!x) {
                IP6CB(skb)->flags |= IP6SKB_REROUTED;
-               return dst_output_sk(sk, skb);
+               return dst_output(net, sk, skb);
        }
 #endif
 
+       if (x->props.mode != XFRM_MODE_TUNNEL)
+               goto skip_frag;
+
        if (skb->protocol == htons(ETH_P_IPV6))
                mtu = ip6_skb_dst_mtu(skb);
        else
                mtu = dst_mtu(skb_dst(skb));
 
-       if (skb->len > mtu && xfrm6_local_dontfrag(skb)) {
+       toobig = skb->len > mtu && !skb_is_gso(skb);
+
+       if (toobig && xfrm6_local_dontfrag(skb)) {
                xfrm6_local_rxpmtu(skb, mtu);
                return -EMSGSIZE;
-       } else if (!skb->ignore_df && skb->len > mtu && skb->sk) {
+       } else if (!skb->ignore_df && toobig && skb->sk) {
                xfrm_local_error(skb, mtu);
                return -EMSGSIZE;
        }
 
-       if (x->props.mode == XFRM_MODE_TUNNEL &&
-           ((skb->len > mtu && !skb_is_gso(skb)) ||
-               dst_allfrag(skb_dst(skb)))) {
-               return ip6_fragment(sk, skb,
-                                   x->outer_mode->afinfo->output_finish);
-       }
+       if (toobig || dst_allfrag(skb_dst(skb)))
+               return ip6_fragment(net, sk, skb,
+                                   __xfrm6_output_finish);
+
+skip_frag:
        return x->outer_mode->afinfo->output_finish(sk, skb);
 }
 
-int xfrm6_output(struct sock *sk, struct sk_buff *skb)
+int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-       return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, sk, skb,
-                           NULL, skb_dst(skb)->dev, __xfrm6_output,
+       return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
+                           net, sk, skb,  NULL, skb_dst(skb)->dev,
+                           __xfrm6_output,
                            !(IP6CB(skb)->flags & IP6SKB_REROUTED));
 }
index 5cedfda4b241045e92fe89971836e549b88f5074..c074771a10f761253f99611de8294172bdb0e142 100644 (file)
@@ -20,7 +20,7 @@
 #include <net/ip.h>
 #include <net/ipv6.h>
 #include <net/ip6_route.h>
-#include <net/vrf.h>
+#include <net/l3mdev.h>
 #if IS_ENABLED(CONFIG_IPV6_MIP6)
 #include <net/mip6.h>
 #endif
@@ -133,10 +133,8 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
 
        nexthdr = nh[nhoff];
 
-       if (skb_dst(skb)) {
-               oif = vrf_master_ifindex(skb_dst(skb)->dev) ?
-                       : skb_dst(skb)->dev->ifindex;
-       }
+       if (skb_dst(skb))
+               oif = l3mdev_fib_oif(skb_dst(skb)->dev);
 
        memset(fl6, 0, sizeof(struct flowi6));
        fl6->flowi6_mark = skb->mark;
@@ -179,7 +177,8 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
                        return;
 
                case IPPROTO_ICMPV6:
-                       if (!onlyproto && pskb_may_pull(skb, nh + offset + 2 - skb->data)) {
+                       if (!onlyproto && (nh + offset + 2 < skb->data ||
+                           pskb_may_pull(skb, nh + offset + 2 - skb->data))) {
                                u8 *icmp;
 
                                nh = skb_network_header(skb);
@@ -193,7 +192,8 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
 #if IS_ENABLED(CONFIG_IPV6_MIP6)
                case IPPROTO_MH:
                        offset += ipv6_optlen(exthdr);
-                       if (!onlyproto && pskb_may_pull(skb, nh + offset + 3 - skb->data)) {
+                       if (!onlyproto && (nh + offset + 3 < skb->data ||
+                           pskb_may_pull(skb, nh + offset + 3 - skb->data))) {
                                struct ip6_mh *mh;
 
                                nh = skb_network_header(skb);
@@ -279,7 +279,7 @@ static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
        xfrm_dst_ifdown(dst, dev);
 }
 
-static struct dst_ops xfrm6_dst_ops = {
+static struct dst_ops xfrm6_dst_ops_template = {
        .family =               AF_INET6,
        .gc =                   xfrm6_garbage_collect,
        .update_pmtu =          xfrm6_update_pmtu,
@@ -288,12 +288,12 @@ static struct dst_ops xfrm6_dst_ops = {
        .destroy =              xfrm6_dst_destroy,
        .ifdown =               xfrm6_dst_ifdown,
        .local_out =            __ip6_local_out,
-       .gc_thresh =            32768,
+       .gc_thresh =            INT_MAX,
 };
 
 static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
        .family =               AF_INET6,
-       .dst_ops =              &xfrm6_dst_ops,
+       .dst_ops =              &xfrm6_dst_ops_template,
        .dst_lookup =           xfrm6_dst_lookup,
        .get_saddr =            xfrm6_get_saddr,
        .decode_session =       _decode_session6,
@@ -325,7 +325,7 @@ static struct ctl_table xfrm6_policy_table[] = {
        { }
 };
 
-static int __net_init xfrm6_net_init(struct net *net)
+static int __net_init xfrm6_net_sysctl_init(struct net *net)
 {
        struct ctl_table *table;
        struct ctl_table_header *hdr;
@@ -353,7 +353,7 @@ err_alloc:
        return -ENOMEM;
 }
 
-static void __net_exit xfrm6_net_exit(struct net *net)
+static void __net_exit xfrm6_net_sysctl_exit(struct net *net)
 {
        struct ctl_table *table;
 
@@ -365,24 +365,52 @@ static void __net_exit xfrm6_net_exit(struct net *net)
        if (!net_eq(net, &init_net))
                kfree(table);
 }
+#else /* CONFIG_SYSCTL */
+static int inline xfrm6_net_sysctl_init(struct net *net)
+{
+       return 0;
+}
+
+static void inline xfrm6_net_sysctl_exit(struct net *net)
+{
+}
+#endif
+
+static int __net_init xfrm6_net_init(struct net *net)
+{
+       int ret;
+
+       memcpy(&net->xfrm.xfrm6_dst_ops, &xfrm6_dst_ops_template,
+              sizeof(xfrm6_dst_ops_template));
+       ret = dst_entries_init(&net->xfrm.xfrm6_dst_ops);
+       if (ret)
+               return ret;
+
+       ret = xfrm6_net_sysctl_init(net);
+       if (ret)
+               dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
+
+       return ret;
+}
+
+static void __net_exit xfrm6_net_exit(struct net *net)
+{
+       xfrm6_net_sysctl_exit(net);
+       dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
+}
 
 static struct pernet_operations xfrm6_net_ops = {
        .init   = xfrm6_net_init,
        .exit   = xfrm6_net_exit,
 };
-#endif
 
 int __init xfrm6_init(void)
 {
        int ret;
 
-       dst_entries_init(&xfrm6_dst_ops);
-
        ret = xfrm6_policy_init();
-       if (ret) {
-               dst_entries_destroy(&xfrm6_dst_ops);
+       if (ret)
                goto out;
-       }
        ret = xfrm6_state_init();
        if (ret)
                goto out_policy;
@@ -391,9 +419,7 @@ int __init xfrm6_init(void)
        if (ret)
                goto out_state;
 
-#ifdef CONFIG_SYSCTL
        register_pernet_subsys(&xfrm6_net_ops);
-#endif
 out:
        return ret;
 out_state:
@@ -405,11 +431,8 @@ out_policy:
 
 void xfrm6_fini(void)
 {
-#ifdef CONFIG_SYSCTL
        unregister_pernet_subsys(&xfrm6_net_ops);
-#endif
        xfrm6_protocol_fini();
        xfrm6_policy_fini();
        xfrm6_state_fini();
-       dst_entries_destroy(&xfrm6_dst_ops);
 }
index fae6822cc367631ca403cc6207f800cf2571dfea..e6aa48b5395c67e2600eed9482c97b9a626d01bd 100644 (file)
@@ -2123,8 +2123,7 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
                }
 
                /* Unregister any old registration */
-               if (self->skey)
-                       irlmp_unregister_service(self->skey);
+               irlmp_unregister_service(self->skey);
 
                self->skey = irlmp_register_service((__u16) opt);
                break;
index a26c401ef4a4431b2957d5b46c05c0c2a35c90bd..43964594aa12d9864c00a3b778d77060084e6a14 100644 (file)
@@ -1839,7 +1839,7 @@ static void *irlmp_seq_hb_idx(struct irlmp_iter_state *iter, loff_t *off)
        for (element = hashbin_get_first(iter->hashbin);
             element != NULL;
             element = hashbin_get_next(iter->hashbin)) {
-               if (!off || *off-- == 0) {
+               if (!off || (*off)-- == 0) {
                        /* NB: hashbin left locked */
                        return element;
                }
index 918151c113480d6651c3f2c406fea7546ebddd22..fcb2752419c6635b06706d4cec542cb30ae3c85c 100644 (file)
@@ -95,11 +95,10 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *, enum iucv_tx_notify);
 /* Call Back functions */
 static void iucv_callback_rx(struct iucv_path *, struct iucv_message *);
 static void iucv_callback_txdone(struct iucv_path *, struct iucv_message *);
-static void iucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
-static int iucv_callback_connreq(struct iucv_path *, u8 ipvmid[8],
-                                u8 ipuser[16]);
-static void iucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
-static void iucv_callback_shutdown(struct iucv_path *, u8 ipuser[16]);
+static void iucv_callback_connack(struct iucv_path *, u8 *);
+static int iucv_callback_connreq(struct iucv_path *, u8 *, u8 *);
+static void iucv_callback_connrej(struct iucv_path *, u8 *);
+static void iucv_callback_shutdown(struct iucv_path *, u8 *);
 
 static struct iucv_sock_list iucv_sk_list = {
        .lock = __RW_LOCK_UNLOCKED(iucv_sk_list.lock),
index 2a6a1fdd62c059eb857312d10fee38fa1c6bc86f..7eaa000c9258fb1e5073c1371dbd9ae6e8fb0599 100644 (file)
@@ -713,7 +713,7 @@ static struct notifier_block __refdata iucv_cpu_notifier = {
  *
  * Sever an iucv path to free up the pathid. Used internally.
  */
-static int iucv_sever_pathid(u16 pathid, u8 userdata[16])
+static int iucv_sever_pathid(u16 pathid, u8 *userdata)
 {
        union iucv_param *parm;
 
@@ -876,7 +876,7 @@ static struct notifier_block iucv_reboot_notifier = {
  * Returns the result of the CP IUCV call.
  */
 int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler,
-                    u8 userdata[16], void *private)
+                    u8 *userdata, void *private)
 {
        union iucv_param *parm;
        int rc;
@@ -923,7 +923,7 @@ EXPORT_SYMBOL(iucv_path_accept);
  * Returns the result of the CP IUCV call.
  */
 int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler,
-                     u8 userid[8], u8 system[8], u8 userdata[16],
+                     u8 *userid, u8 *system, u8 *userdata,
                      void *private)
 {
        union iucv_param *parm;
@@ -985,7 +985,7 @@ EXPORT_SYMBOL(iucv_path_connect);
  *
  * Returns the result from the CP IUCV call.
  */
-int iucv_path_quiesce(struct iucv_path *path, u8 userdata[16])
+int iucv_path_quiesce(struct iucv_path *path, u8 *userdata)
 {
        union iucv_param *parm;
        int rc;
@@ -1017,7 +1017,7 @@ EXPORT_SYMBOL(iucv_path_quiesce);
  *
  * Returns the result from the CP IUCV call.
  */
-int iucv_path_resume(struct iucv_path *path, u8 userdata[16])
+int iucv_path_resume(struct iucv_path *path, u8 *userdata)
 {
        union iucv_param *parm;
        int rc;
@@ -1047,7 +1047,7 @@ out:
  *
  * Returns the result from the CP IUCV call.
  */
-int iucv_path_sever(struct iucv_path *path, u8 userdata[16])
+int iucv_path_sever(struct iucv_path *path, u8 *userdata)
 {
        int rc;
 
index 83a70688784b8449603e13f32ec26ff6fce06639..f9c9ecb0cdd3b3eea618538fda2e884583f9bc09 100644 (file)
@@ -261,7 +261,7 @@ static int pfkey_broadcast(struct sk_buff *skb,
 
                err2 = pfkey_broadcast_one(skb, &skb2, GFP_ATOMIC, sk);
 
-               /* Error is cleare after succecful sending to at least one
+               /* Error is cleared after successful sending to at least one
                 * registered KM */
                if ((broadcast_flags & BROADCAST_REGISTERED) && err)
                        err = err2;
index 68aa9ffd4ae4d972cdd57ea742ddf3b78ba497d6..5871537af387b2566c8ffaa79d107efaf3701fae 100644 (file)
@@ -321,4 +321,7 @@ do {                                                                        \
 #define l2tp_dbg(ptr, type, fmt, ...)                                  \
        l2tp_printk(ptr, type, pr_debug, fmt, ##__VA_ARGS__)
 
+#define MODULE_ALIAS_L2TP_PWTYPE(type) \
+       MODULE_ALIAS("net-l2tp-type-" __stringify(type))
+
 #endif /* _L2TP_CORE_H_ */
index 4b552873b55603a648f37bbd497efbb43bd869a4..e253c26f31ac378b644c925f1e44c1d4ef753e17 100644 (file)
@@ -358,3 +358,4 @@ MODULE_LICENSE("GPL");
 MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
 MODULE_DESCRIPTION("L2TP ethernet pseudowire driver");
 MODULE_VERSION("1.0");
+MODULE_ALIAS_L2TP_PWTYPE(5);
index 79649937ec71da6ffc70584b51ccbba6b73e391f..ec22078b0914ff7ce65c3b11801504e252102dc2 100644 (file)
@@ -655,3 +655,4 @@ MODULE_VERSION("1.0");
  * enums
  */
 MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 2, IPPROTO_L2TP);
+MODULE_ALIAS_NET_PF_PROTO(PF_INET, IPPROTO_L2TP);
index d1ded3777815e5b997db37cf4975422f4708dec1..aca38d8aed8e80b47ded2c81bbf346a39a678ce2 100644 (file)
@@ -801,3 +801,4 @@ MODULE_VERSION("1.0");
  * enums
  */
 MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 2, IPPROTO_L2TP);
+MODULE_ALIAS_NET_PF_PROTO(PF_INET6, IPPROTO_L2TP);
index 9e13c2ff878970fbbe355990f756684d9e868988..f93c5be612a7cb43611708ebb66c25b4db0d27cf 100644 (file)
@@ -576,6 +576,13 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf
        if (info->attrs[L2TP_ATTR_MRU])
                cfg.mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]);
 
+#ifdef CONFIG_MODULES
+       if (l2tp_nl_cmd_ops[cfg.pw_type] == NULL) {
+               genl_unlock();
+               request_module("net-l2tp-type-%u", cfg.pw_type);
+               genl_lock();
+       }
+#endif
        if ((l2tp_nl_cmd_ops[cfg.pw_type] == NULL) ||
            (l2tp_nl_cmd_ops[cfg.pw_type]->session_create == NULL)) {
                ret = -EPROTONOSUPPORT;
index f56c9f69e9f288b2d1f9da53257a5f64bf5dd067..1ad18c55064cafac516d2b719d9c1ba5f09c1b03 100644 (file)
@@ -1863,3 +1863,4 @@ MODULE_DESCRIPTION("PPP over L2TP over UDP");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(PPPOL2TP_DRV_VERSION);
 MODULE_ALIAS("pppox-proto-" __stringify(PX_PROTO_OL2TP));
+MODULE_ALIAS_L2TP_PWTYPE(11);
diff --git a/net/l3mdev/Kconfig b/net/l3mdev/Kconfig
new file mode 100644 (file)
index 0000000..5d47325
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Configuration for L3 master device support
+#
+
+config NET_L3_MASTER_DEV
+       bool "L3 Master device support"
+       depends on INET || IPV6
+       ---help---
+         This module provides glue between core networking code and device
+         drivers to support L3 master devices like VRF.
diff --git a/net/l3mdev/Makefile b/net/l3mdev/Makefile
new file mode 100644 (file)
index 0000000..84a53a6
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for the L3 device API
+#
+
+obj-$(CONFIG_NET_L3_MASTER_DEV) += l3mdev.o
diff --git a/net/l3mdev/l3mdev.c b/net/l3mdev/l3mdev.c
new file mode 100644 (file)
index 0000000..8e5ead3
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * net/l3mdev/l3mdev.c - L3 master device implementation
+ * Copyright (c) 2015 Cumulus Networks
+ * Copyright (c) 2015 David Ahern <dsa@cumulusnetworks.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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/netdevice.h>
+#include <net/l3mdev.h>
+
+/**
+ *     l3mdev_master_ifindex - get index of L3 master device
+ *     @dev: targeted interface
+ */
+
+int l3mdev_master_ifindex_rcu(struct net_device *dev)
+{
+       int ifindex = 0;
+
+       if (!dev)
+               return 0;
+
+       if (netif_is_l3_master(dev)) {
+               ifindex = dev->ifindex;
+       } else if (netif_is_l3_slave(dev)) {
+               struct net_device *master;
+
+               master = netdev_master_upper_dev_get_rcu(dev);
+               if (master)
+                       ifindex = master->ifindex;
+       }
+
+       return ifindex;
+}
+EXPORT_SYMBOL_GPL(l3mdev_master_ifindex_rcu);
+
+/**
+ *     l3mdev_fib_table - get FIB table id associated with an L3
+ *                             master interface
+ *     @dev: targeted interface
+ */
+
+u32 l3mdev_fib_table_rcu(const struct net_device *dev)
+{
+       u32 tb_id = 0;
+
+       if (!dev)
+               return 0;
+
+       if (netif_is_l3_master(dev)) {
+               if (dev->l3mdev_ops->l3mdev_fib_table)
+                       tb_id = dev->l3mdev_ops->l3mdev_fib_table(dev);
+       } else if (netif_is_l3_slave(dev)) {
+               /* Users of netdev_master_upper_dev_get_rcu need non-const,
+                * but current inet_*type functions take a const
+                */
+               struct net_device *_dev = (struct net_device *) dev;
+               const struct net_device *master;
+
+               master = netdev_master_upper_dev_get_rcu(_dev);
+               if (master &&
+                   master->l3mdev_ops->l3mdev_fib_table)
+                       tb_id = master->l3mdev_ops->l3mdev_fib_table(master);
+       }
+
+       return tb_id;
+}
+EXPORT_SYMBOL_GPL(l3mdev_fib_table_rcu);
+
+u32 l3mdev_fib_table_by_index(struct net *net, int ifindex)
+{
+       struct net_device *dev;
+       u32 tb_id = 0;
+
+       if (!ifindex)
+               return 0;
+
+       rcu_read_lock();
+
+       dev = dev_get_by_index_rcu(net, ifindex);
+       if (dev)
+               tb_id = l3mdev_fib_table_rcu(dev);
+
+       rcu_read_unlock();
+
+       return tb_id;
+}
+EXPORT_SYMBOL_GPL(l3mdev_fib_table_by_index);
index 783e891b7525c537bcab70b1be7c603e5f6d1f0d..f9137a8341f4db16b9dca579b0ed8f5300a59767 100644 (file)
@@ -27,7 +27,6 @@ mac80211-y := \
        key.o \
        util.o \
        wme.o \
-       event.o \
        chan.o \
        trace.o mlme.o \
        tdls.o \
index 5c564a68fb5088e21ccec11ffdde963bf8bb6cdf..10ad4ac1fa0ba8ebd04e793595fab47d326194ce 100644 (file)
@@ -79,7 +79,7 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
               (int)reason);
 
        if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP,
-                            &sta->sta, tid, NULL, 0))
+                            &sta->sta, tid, NULL, 0, false))
                sdata_info(sta->sdata,
                           "HW problem - can not stop rx aggregation for %pM tid %d\n",
                           sta->sta.addr, tid);
@@ -189,6 +189,7 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
+       bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
        u16 capab;
 
        skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
@@ -217,7 +218,8 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
        mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
        mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
 
-       capab = (u16)(policy << 1);     /* bit 1 aggregation policy */
+       capab = (u16)(amsdu << 0);      /* bit 0 A-MSDU support */
+       capab |= (u16)(policy << 1);    /* bit 1 aggregation policy */
        capab |= (u16)(tid << 2);       /* bit 5:2 TID number */
        capab |= (u16)(buf_size << 6);  /* bit 15:6 max size of aggregation */
 
@@ -321,7 +323,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
                __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
 
        ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
-                              &sta->sta, tid, &start_seq_num, 0);
+                              &sta->sta, tid, &start_seq_num, 0, false);
        ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
               sta->sta.addr, tid, ret);
        if (ret) {
index c8ba2e77737c3c00acbe840d8f572258912f39ce..a758eb84e8f057ac6518eef6c73bc8785dc7e746 100644 (file)
@@ -97,7 +97,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
        mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
 
        mgmt->u.action.u.addba_req.dialog_token = dialog_token;
-       capab = (u16)(1 << 1);          /* bit 1 aggregation policy */
+       capab = (u16)(1 << 0);          /* bit 0 A-MSDU support */
+       capab |= (u16)(1 << 1);         /* bit 1 aggregation policy */
        capab |= (u16)(tid << 2);       /* bit 5:2 TID number */
        capab |= (u16)(agg_size << 6);  /* bit 15:6 max size of aggergation */
 
@@ -331,7 +332,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                        return -EALREADY;
                ret = drv_ampdu_action(local, sta->sdata,
                                       IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
-                                      &sta->sta, tid, NULL, 0);
+                                      &sta->sta, tid, NULL, 0, false);
                WARN_ON_ONCE(ret);
                return 0;
        }
@@ -381,7 +382,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
        tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST;
 
        ret = drv_ampdu_action(local, sta->sdata, action,
-                              &sta->sta, tid, NULL, 0);
+                              &sta->sta, tid, NULL, 0, false);
 
        /* HW shall not deny going back to legacy */
        if (WARN_ON(ret)) {
@@ -469,7 +470,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
        start_seq_num = sta->tid_seq[tid] >> 4;
 
        ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START,
-                              &sta->sta, tid, &start_seq_num, 0);
+                              &sta->sta, tid, &start_seq_num, 0, false);
        if (ret) {
                ht_dbg(sdata,
                       "BA request denied - HW unavailable for %pM tid %d\n",
@@ -693,7 +694,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
 
        drv_ampdu_action(local, sta->sdata,
                         IEEE80211_AMPDU_TX_OPERATIONAL,
-                        &sta->sta, tid, NULL, tid_tx->buf_size);
+                        &sta->sta, tid, NULL, tid_tx->buf_size,
+                        tid_tx->amsdu);
 
        /*
         * synchronize with TX path, while splicing the TX path
@@ -918,8 +920,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
        struct tid_ampdu_tx *tid_tx;
        u16 capab, tid;
        u8 buf_size;
+       bool amsdu;
 
        capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
+       amsdu = capab & IEEE80211_ADDBA_PARAM_AMSDU_MASK;
        tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
        buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
 
@@ -968,6 +972,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
                }
 
                tid_tx->buf_size = buf_size;
+               tid_tx->amsdu = amsdu;
 
                if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))
                        ieee80211_agg_tx_operational(local, sta, tid);
index 7a77a1470f25bd26aa280eada69479da1f4e9dd9..c2bd1b6a69224e4e07b6247c71077e2a79939dac 100644 (file)
@@ -17,7 +17,6 @@
 #include <net/cfg80211.h>
 #include "ieee80211_i.h"
 #include "driver-ops.h"
-#include "cfg.h"
 #include "rate.h"
 #include "mesh.h"
 #include "wme.h"
@@ -469,45 +468,6 @@ void sta_set_rate_info_tx(struct sta_info *sta,
                rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
 }
 
-void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
-{
-       rinfo->flags = 0;
-
-       if (sta->last_rx_rate_flag & RX_FLAG_HT) {
-               rinfo->flags |= RATE_INFO_FLAGS_MCS;
-               rinfo->mcs = sta->last_rx_rate_idx;
-       } else if (sta->last_rx_rate_flag & RX_FLAG_VHT) {
-               rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
-               rinfo->nss = sta->last_rx_rate_vht_nss;
-               rinfo->mcs = sta->last_rx_rate_idx;
-       } else {
-               struct ieee80211_supported_band *sband;
-               int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
-               u16 brate;
-
-               sband = sta->local->hw.wiphy->bands[
-                               ieee80211_get_sdata_band(sta->sdata)];
-               brate = sband->bitrates[sta->last_rx_rate_idx].bitrate;
-               rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
-       }
-
-       if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
-               rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
-
-       if (sta->last_rx_rate_flag & RX_FLAG_5MHZ)
-               rinfo->bw = RATE_INFO_BW_5;
-       else if (sta->last_rx_rate_flag & RX_FLAG_10MHZ)
-               rinfo->bw = RATE_INFO_BW_10;
-       else if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
-               rinfo->bw = RATE_INFO_BW_40;
-       else if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ)
-               rinfo->bw = RATE_INFO_BW_80;
-       else if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ)
-               rinfo->bw = RATE_INFO_BW_160;
-       else
-               rinfo->bw = RATE_INFO_BW_20;
-}
-
 static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
                                  int idx, u8 *mac, struct station_info *sinfo)
 {
@@ -981,7 +941,7 @@ static int sta_apply_auth_flags(struct ieee80211_local *local,
                 * well. Some drivers require rate control initialized
                 * before drv_sta_state() is called.
                 */
-               if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+               if (!test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
                        rate_control_rate_init(sta);
 
                ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
@@ -1120,8 +1080,11 @@ static int sta_apply_parameters(struct ieee80211_local *local,
            local->hw.queues >= IEEE80211_NUM_ACS)
                sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
 
-       /* auth flags will be set later for TDLS stations */
-       if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+       /* auth flags will be set later for TDLS,
+        * and for unassociated stations that move to assocaited */
+       if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+           !((mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) &&
+             (set & BIT(NL80211_STA_FLAG_ASSOCIATED)))) {
                ret = sta_apply_auth_flags(local, sta, mask, set);
                if (ret)
                        return ret;
@@ -1135,6 +1098,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
        }
 
        if (mask & BIT(NL80211_STA_FLAG_MFP)) {
+               sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP));
                if (set & BIT(NL80211_STA_FLAG_MFP))
                        set_sta_flag(sta, WLAN_STA_MFP);
                else
@@ -1156,6 +1120,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
 
        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+           !sdata->u.mgd.tdls_wider_bw_prohibited &&
            ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
            params->ext_capab_len >= 8 &&
            params->ext_capab[7] & WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED)
@@ -1212,7 +1177,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                sta_apply_mesh_params(local, sta, params);
 
        /* set the STA state after all sta info from usermode has been set */
-       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
+           set & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
                ret = sta_apply_auth_flags(local, sta, mask, set);
                if (ret)
                        return ret;
@@ -1254,12 +1220,14 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
         * defaults -- if userspace wants something else we'll
         * change it accordingly in sta_apply_parameters()
         */
-       if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {
+       if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
+           !(params->sta_flags_set & (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                                       BIT(NL80211_STA_FLAG_ASSOCIATED)))) {
                sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
                sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
-       } else {
-               sta->sta.tdls = true;
        }
+       if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+               sta->sta.tdls = true;
 
        err = sta_apply_parameters(local, sta, params);
        if (err) {
@@ -1268,10 +1236,12 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        }
 
        /*
-        * for TDLS, rate control should be initialized only when
-        * rates are known and station is marked authorized
+        * for TDLS and for unassociated station, rate control should be
+        * initialized only when rates are known and station is marked
+        * authorized/associated
         */
-       if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+       if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+           test_sta_flag(sta, WLAN_STA_ASSOC))
                rate_control_rate_init(sta);
 
        layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
@@ -1346,7 +1316,10 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                break;
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_AP_VLAN:
-               statype = CFG80211_STA_AP_CLIENT;
+               if (test_sta_flag(sta, WLAN_STA_ASSOC))
+                       statype = CFG80211_STA_AP_CLIENT;
+               else
+                       statype = CFG80211_STA_AP_CLIENT_UNASSOC;
                break;
        default:
                err = -EOPNOTSUPP;
@@ -1415,7 +1388,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
            params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
-               ieee80211_recalc_ps(local, -1);
+               ieee80211_recalc_ps(local);
                ieee80211_recalc_ps_vif(sdata);
        }
 
@@ -2037,12 +2010,12 @@ ieee80211_sched_scan_start(struct wiphy *wiphy,
 static int
 ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev)
 {
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = wiphy_priv(wiphy);
 
-       if (!sdata->local->ops->sched_scan_stop)
+       if (!local->ops->sched_scan_stop)
                return -EOPNOTSUPP;
 
-       return ieee80211_request_sched_scan_stop(sdata);
+       return ieee80211_request_sched_scan_stop(local);
 }
 
 static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
@@ -2450,7 +2423,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
        if (ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS))
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
 
-       ieee80211_recalc_ps(local, -1);
+       ieee80211_recalc_ps(local);
        ieee80211_recalc_ps_vif(sdata);
 
        return 0;
@@ -3522,18 +3495,32 @@ static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
                                          u16 frame_type, bool reg)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
 
        switch (frame_type) {
        case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ:
-               if (reg)
+               if (reg) {
                        local->probe_req_reg++;
-               else
-                       local->probe_req_reg--;
+                       sdata->vif.probe_req_reg++;
+               } else {
+                       if (local->probe_req_reg)
+                               local->probe_req_reg--;
+
+                       if (sdata->vif.probe_req_reg)
+                               sdata->vif.probe_req_reg--;
+               }
 
                if (!local->open_count)
                        break;
 
-               ieee80211_queue_work(&local->hw, &local->reconfig_filter);
+               if (sdata->vif.probe_req_reg == 1)
+                       drv_config_iface_filter(local, sdata, FIF_PROBE_REQ,
+                                               FIF_PROBE_REQ);
+               else if (sdata->vif.probe_req_reg == 0)
+                       drv_config_iface_filter(local, sdata, 0,
+                                               FIF_PROBE_REQ);
+
+               ieee80211_configure_filter(local);
                break;
        default:
                break;
diff --git a/net/mac80211/cfg.h b/net/mac80211/cfg.h
deleted file mode 100644 (file)
index 2d51f62..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * mac80211 configuration hooks for cfg80211
- */
-#ifndef __CFG_H
-#define __CFG_H
-
-extern const struct cfg80211_ops mac80211_config_ops;
-
-#endif /* __CFG_H */
index 1560c8482bcb9fd587d1e278a499f99fd9958803..4d2aaebd4f97d8692758263e3d9759d4300abdb6 100644 (file)
@@ -123,6 +123,8 @@ static const char *hw_flag_names[NUM_IEEE80211_HW_FLAGS + 1] = {
        FLAG(SUPPORTS_CLONED_SKBS),
        FLAG(SINGLE_SCAN_ON_ALL_BANDS),
        FLAG(TDLS_WIDER_BW),
+       FLAG(SUPPORTS_AMSDU_IN_AMPDU),
+       FLAG(BEACON_TX_STATUS),
 
        /* keep last for the build bug below */
        (void *)0x1
index 702ca122c498938691842d95db7732d6d8c6d6bb..7961e7d0b61e1ee48700e9a7ef2db8feec84814f 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright 2003-2005 Devicescape Software, Inc.
  * Copyright (c) 2006  Jiri Benc <jbenc@suse.cz>
  * Copyright 2007      Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (C) 2015  Intel Deutschland GmbH
  *
  * 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
@@ -34,6 +35,14 @@ static const struct file_operations key_ ##name## _ops = {           \
        .llseek = generic_file_llseek,                                  \
 }
 
+#define KEY_OPS_W(name)                                                        \
+static const struct file_operations key_ ##name## _ops = {             \
+       .read = key_##name##_read,                                      \
+       .write = key_##name##_write,                                    \
+       .open = simple_open,                                            \
+       .llseek = generic_file_llseek,                                  \
+}
+
 #define KEY_FILE(name, format)                                         \
                 KEY_READ_##format(name)                                \
                 KEY_OPS(name)
@@ -74,6 +83,41 @@ static ssize_t key_algorithm_read(struct file *file,
 }
 KEY_OPS(algorithm);
 
+static ssize_t key_tx_spec_write(struct file *file, const char __user *userbuf,
+                                size_t count, loff_t *ppos)
+{
+       struct ieee80211_key *key = file->private_data;
+       u64 pn;
+       int ret;
+
+       switch (key->conf.cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               return -EINVAL;
+       case WLAN_CIPHER_SUITE_TKIP:
+               /* not supported yet */
+               return -EOPNOTSUPP;
+       case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               ret = kstrtou64_from_user(userbuf, count, 16, &pn);
+               if (ret)
+                       return ret;
+               /* PN is a 48-bit counter */
+               if (pn >= (1ULL << 48))
+                       return -ERANGE;
+               atomic64_set(&key->conf.tx_pn, pn);
+               return count;
+       default:
+               return 0;
+       }
+}
+
 static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
                                size_t count, loff_t *ppos)
 {
@@ -110,7 +154,7 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
        }
        return simple_read_from_buffer(userbuf, count, ppos, buf, len);
 }
-KEY_OPS(tx_spec);
+KEY_OPS_W(tx_spec);
 
 static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
                                size_t count, loff_t *ppos)
@@ -278,6 +322,9 @@ KEY_OPS(key);
 #define DEBUGFS_ADD(name) \
        debugfs_create_file(#name, 0400, key->debugfs.dir, \
                            key, &key_##name##_ops);
+#define DEBUGFS_ADD_W(name) \
+       debugfs_create_file(#name, 0600, key->debugfs.dir, \
+                           key, &key_##name##_ops);
 
 void ieee80211_debugfs_key_add(struct ieee80211_key *key)
 {
@@ -310,7 +357,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
        DEBUGFS_ADD(keyidx);
        DEBUGFS_ADD(hw_key_idx);
        DEBUGFS_ADD(algorithm);
-       DEBUGFS_ADD(tx_spec);
+       DEBUGFS_ADD_W(tx_spec);
        DEBUGFS_ADD(rx_spec);
        DEBUGFS_ADD(replays);
        DEBUGFS_ADD(icverrors);
index 1021e87c051f35168eef1274a38a4d720054359e..37ea30e0754c2b3d67b93f53fc9a582e2757dc3a 100644 (file)
@@ -114,14 +114,6 @@ static ssize_t ieee80211_if_fmt_##name(                                    \
        return scnprintf(buf, buflen, "%pM\n", sdata->field);           \
 }
 
-#define IEEE80211_IF_FMT_DEC_DIV_16(name, field)                       \
-static ssize_t ieee80211_if_fmt_##name(                                        \
-       const struct ieee80211_sub_if_data *sdata,                      \
-       char *buf, int buflen)                                          \
-{                                                                      \
-       return scnprintf(buf, buflen, "%d\n", sdata->field / 16);       \
-}
-
 #define IEEE80211_IF_FMT_JIFFIES_TO_MS(name, field)                    \
 static ssize_t ieee80211_if_fmt_##name(                                        \
        const struct ieee80211_sub_if_data *sdata,                      \
@@ -247,8 +239,6 @@ IEEE80211_IF_FILE_R(hw_queues);
 /* STA attributes */
 IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
 IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
-IEEE80211_IF_FILE(last_beacon, u.mgd.last_beacon_signal, DEC);
-IEEE80211_IF_FILE(ave_beacon, u.mgd.ave_beacon_signal, DEC_DIV_16);
 IEEE80211_IF_FILE(beacon_timeout, u.mgd.beacon_timeout, JIFFIES_TO_MS);
 
 static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
@@ -455,6 +445,34 @@ static ssize_t ieee80211_if_parse_uapsd_max_sp_len(
 }
 IEEE80211_IF_FILE_RW(uapsd_max_sp_len);
 
+static ssize_t ieee80211_if_fmt_tdls_wider_bw(
+       const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
+{
+       const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       bool tdls_wider_bw;
+
+       tdls_wider_bw = ieee80211_hw_check(&sdata->local->hw, TDLS_WIDER_BW) &&
+                       !ifmgd->tdls_wider_bw_prohibited;
+
+       return snprintf(buf, buflen, "%d\n", tdls_wider_bw);
+}
+
+static ssize_t ieee80211_if_parse_tdls_wider_bw(
+       struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
+{
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       u8 val;
+       int ret;
+
+       ret = kstrtou8(buf, 0, &val);
+       if (ret)
+               return ret;
+
+       ifmgd->tdls_wider_bw_prohibited = !val;
+       return buflen;
+}
+IEEE80211_IF_FILE_RW(tdls_wider_bw);
+
 /* AP attributes */
 IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC);
 IEEE80211_IF_FILE(num_sta_ps, u.ap.ps.num_sta_ps, ATOMIC);
@@ -606,14 +624,13 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
 {
        DEBUGFS_ADD(bssid);
        DEBUGFS_ADD(aid);
-       DEBUGFS_ADD(last_beacon);
-       DEBUGFS_ADD(ave_beacon);
        DEBUGFS_ADD(beacon_timeout);
        DEBUGFS_ADD_MODE(smps, 0600);
        DEBUGFS_ADD_MODE(tkip_mic_test, 0200);
        DEBUGFS_ADD_MODE(beacon_loss, 0200);
        DEBUGFS_ADD_MODE(uapsd_queues, 0600);
        DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600);
+       DEBUGFS_ADD_MODE(tdls_wider_bw, 0600);
 }
 
 static void add_ap_files(struct ieee80211_sub_if_data *sdata)
index 06d52935036dd0875868e3db79aaebebe08c5c11..a39512f09f9eb2992f825d479a609474414d9af5 100644 (file)
@@ -50,7 +50,6 @@ static const struct file_operations sta_ ##name## _ops = {            \
                STA_OPS(name)
 
 STA_FILE(aid, sta.aid, D);
-STA_FILE(last_ack_signal, last_ack_signal, D);
 
 static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
                              size_t count, loff_t *ppos)
@@ -366,11 +365,10 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
        DEBUGFS_ADD(agg_status);
        DEBUGFS_ADD(ht_capa);
        DEBUGFS_ADD(vht_capa);
-       DEBUGFS_ADD(last_ack_signal);
 
-       DEBUGFS_ADD_COUNTER(rx_duplicates, num_duplicates);
-       DEBUGFS_ADD_COUNTER(rx_fragments, rx_fragments);
-       DEBUGFS_ADD_COUNTER(tx_filtered, tx_filtered_count);
+       DEBUGFS_ADD_COUNTER(rx_duplicates, rx_stats.num_duplicates);
+       DEBUGFS_ADD_COUNTER(rx_fragments, rx_stats.fragments);
+       DEBUGFS_ADD_COUNTER(tx_filtered, status_stats.filtered);
 
        if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
                debugfs_create_x32("driver_buffered_tids", 0400,
index 267c3b1ca04758e30f3d5ab0fd7baa5aaee64a82..ca1fe5576103767c3a98b52e037c0034b949887f 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright 2015 Intel Deutschland GmbH
+ *
  * 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 "trace.h"
 #include "driver-ops.h"
 
+int drv_start(struct ieee80211_local *local)
+{
+       int ret;
+
+       might_sleep();
+
+       if (WARN_ON(local->started))
+               return -EALREADY;
+
+       trace_drv_start(local);
+       local->started = true;
+       /* allow rx frames */
+       smp_mb();
+       ret = local->ops->start(&local->hw);
+       trace_drv_return_int(local, ret);
+
+       if (ret)
+               local->started = false;
+
+       return ret;
+}
+
+void drv_stop(struct ieee80211_local *local)
+{
+       might_sleep();
+
+       if (WARN_ON(!local->started))
+               return;
+
+       trace_drv_stop(local);
+       local->ops->stop(&local->hw);
+       trace_drv_return_void(local);
+
+       /* sync away all work on the tasklet before clearing started */
+       tasklet_disable(&local->tasklet);
+       tasklet_enable(&local->tasklet);
+
+       barrier();
+
+       local->started = false;
+}
+
+int drv_add_interface(struct ieee80211_local *local,
+                     struct ieee80211_sub_if_data *sdata)
+{
+       int ret;
+
+       might_sleep();
+
+       if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+                   (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
+                    !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
+                    !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))))
+               return -EINVAL;
+
+       trace_drv_add_interface(local, sdata);
+       ret = local->ops->add_interface(&local->hw, &sdata->vif);
+       trace_drv_return_int(local, ret);
+
+       if (ret == 0)
+               sdata->flags |= IEEE80211_SDATA_IN_DRIVER;
+
+       return ret;
+}
+
+int drv_change_interface(struct ieee80211_local *local,
+                        struct ieee80211_sub_if_data *sdata,
+                        enum nl80211_iftype type, bool p2p)
+{
+       int ret;
+
+       might_sleep();
+
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
+
+       trace_drv_change_interface(local, sdata, type, p2p);
+       ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
+       trace_drv_return_int(local, ret);
+       return ret;
+}
+
+void drv_remove_interface(struct ieee80211_local *local,
+                         struct ieee80211_sub_if_data *sdata)
+{
+       might_sleep();
+
+       if (!check_sdata_in_driver(sdata))
+               return;
+
+       trace_drv_remove_interface(local, sdata);
+       local->ops->remove_interface(&local->hw, &sdata->vif);
+       sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
+       trace_drv_return_void(local);
+}
+
 __must_check
 int drv_sta_state(struct ieee80211_local *local,
                  struct ieee80211_sub_if_data *sdata,
@@ -39,3 +137,173 @@ int drv_sta_state(struct ieee80211_local *local,
        trace_drv_return_int(local, ret);
        return ret;
 }
+
+void drv_sta_rc_update(struct ieee80211_local *local,
+                      struct ieee80211_sub_if_data *sdata,
+                      struct ieee80211_sta *sta, u32 changed)
+{
+       sdata = get_bss_sdata(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
+
+       WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED &&
+               (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+                sdata->vif.type != NL80211_IFTYPE_MESH_POINT));
+
+       trace_drv_sta_rc_update(local, sdata, sta, changed);
+       if (local->ops->sta_rc_update)
+               local->ops->sta_rc_update(&local->hw, &sdata->vif,
+                                         sta, changed);
+
+       trace_drv_return_void(local);
+}
+
+int drv_conf_tx(struct ieee80211_local *local,
+               struct ieee80211_sub_if_data *sdata, u16 ac,
+               const struct ieee80211_tx_queue_params *params)
+{
+       int ret = -EOPNOTSUPP;
+
+       might_sleep();
+
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
+
+       if (WARN_ONCE(params->cw_min == 0 ||
+                     params->cw_min > params->cw_max,
+                     "%s: invalid CW_min/CW_max: %d/%d\n",
+                     sdata->name, params->cw_min, params->cw_max))
+               return -EINVAL;
+
+       trace_drv_conf_tx(local, sdata, ac, params);
+       if (local->ops->conf_tx)
+               ret = local->ops->conf_tx(&local->hw, &sdata->vif,
+                                         ac, params);
+       trace_drv_return_int(local, ret);
+       return ret;
+}
+
+u64 drv_get_tsf(struct ieee80211_local *local,
+               struct ieee80211_sub_if_data *sdata)
+{
+       u64 ret = -1ULL;
+
+       might_sleep();
+
+       if (!check_sdata_in_driver(sdata))
+               return ret;
+
+       trace_drv_get_tsf(local, sdata);
+       if (local->ops->get_tsf)
+               ret = local->ops->get_tsf(&local->hw, &sdata->vif);
+       trace_drv_return_u64(local, ret);
+       return ret;
+}
+
+void drv_set_tsf(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                u64 tsf)
+{
+       might_sleep();
+
+       if (!check_sdata_in_driver(sdata))
+               return;
+
+       trace_drv_set_tsf(local, sdata, tsf);
+       if (local->ops->set_tsf)
+               local->ops->set_tsf(&local->hw, &sdata->vif, tsf);
+       trace_drv_return_void(local);
+}
+
+void drv_reset_tsf(struct ieee80211_local *local,
+                  struct ieee80211_sub_if_data *sdata)
+{
+       might_sleep();
+
+       if (!check_sdata_in_driver(sdata))
+               return;
+
+       trace_drv_reset_tsf(local, sdata);
+       if (local->ops->reset_tsf)
+               local->ops->reset_tsf(&local->hw, &sdata->vif);
+       trace_drv_return_void(local);
+}
+
+int drv_switch_vif_chanctx(struct ieee80211_local *local,
+                          struct ieee80211_vif_chanctx_switch *vifs,
+                          int n_vifs, enum ieee80211_chanctx_switch_mode mode)
+{
+       int ret = 0;
+       int i;
+
+       might_sleep();
+
+       if (!local->ops->switch_vif_chanctx)
+               return -EOPNOTSUPP;
+
+       for (i = 0; i < n_vifs; i++) {
+               struct ieee80211_chanctx *new_ctx =
+                       container_of(vifs[i].new_ctx,
+                                    struct ieee80211_chanctx,
+                                    conf);
+               struct ieee80211_chanctx *old_ctx =
+                       container_of(vifs[i].old_ctx,
+                                    struct ieee80211_chanctx,
+                                    conf);
+
+               WARN_ON_ONCE(!old_ctx->driver_present);
+               WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
+                             new_ctx->driver_present) ||
+                            (mode == CHANCTX_SWMODE_REASSIGN_VIF &&
+                             !new_ctx->driver_present));
+       }
+
+       trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode);
+       ret = local->ops->switch_vif_chanctx(&local->hw,
+                                            vifs, n_vifs, mode);
+       trace_drv_return_int(local, ret);
+
+       if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) {
+               for (i = 0; i < n_vifs; i++) {
+                       struct ieee80211_chanctx *new_ctx =
+                               container_of(vifs[i].new_ctx,
+                                            struct ieee80211_chanctx,
+                                            conf);
+                       struct ieee80211_chanctx *old_ctx =
+                               container_of(vifs[i].old_ctx,
+                                            struct ieee80211_chanctx,
+                                            conf);
+
+                       new_ctx->driver_present = true;
+                       old_ctx->driver_present = false;
+               }
+       }
+
+       return ret;
+}
+
+int drv_ampdu_action(struct ieee80211_local *local,
+                    struct ieee80211_sub_if_data *sdata,
+                    enum ieee80211_ampdu_mlme_action action,
+                    struct ieee80211_sta *sta, u16 tid,
+                    u16 *ssn, u8 buf_size, bool amsdu)
+{
+       int ret = -EOPNOTSUPP;
+
+       might_sleep();
+
+       sdata = get_bss_sdata(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
+
+       trace_drv_ampdu_action(local, sdata, action, sta, tid,
+                              ssn, buf_size, amsdu);
+
+       if (local->ops->ampdu_action)
+               ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action,
+                                              sta, tid, ssn, buf_size, amsdu);
+
+       trace_drv_return_int(local, ret);
+
+       return ret;
+}
index 02d91332d7dddbe4459246308d605146be4430d7..154ce4b13406d5a31bac8797f8069184ca9cd6f5 100644 (file)
@@ -66,36 +66,8 @@ static inline int drv_get_et_sset_count(struct ieee80211_sub_if_data *sdata,
        return rv;
 }
 
-static inline int drv_start(struct ieee80211_local *local)
-{
-       int ret;
-
-       might_sleep();
-
-       trace_drv_start(local);
-       local->started = true;
-       smp_mb();
-       ret = local->ops->start(&local->hw);
-       trace_drv_return_int(local, ret);
-       return ret;
-}
-
-static inline void drv_stop(struct ieee80211_local *local)
-{
-       might_sleep();
-
-       trace_drv_stop(local);
-       local->ops->stop(&local->hw);
-       trace_drv_return_void(local);
-
-       /* sync away all work on the tasklet before clearing started */
-       tasklet_disable(&local->tasklet);
-       tasklet_enable(&local->tasklet);
-
-       barrier();
-
-       local->started = false;
-}
+int drv_start(struct ieee80211_local *local);
+void drv_stop(struct ieee80211_local *local);
 
 #ifdef CONFIG_PM
 static inline int drv_suspend(struct ieee80211_local *local,
@@ -137,59 +109,15 @@ static inline void drv_set_wakeup(struct ieee80211_local *local,
 }
 #endif
 
-static inline int drv_add_interface(struct ieee80211_local *local,
-                                   struct ieee80211_sub_if_data *sdata)
-{
-       int ret;
-
-       might_sleep();
-
-       if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
-                   (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
-                    !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
-                    !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))))
-               return -EINVAL;
-
-       trace_drv_add_interface(local, sdata);
-       ret = local->ops->add_interface(&local->hw, &sdata->vif);
-       trace_drv_return_int(local, ret);
-
-       if (ret == 0)
-               sdata->flags |= IEEE80211_SDATA_IN_DRIVER;
-
-       return ret;
-}
-
-static inline int drv_change_interface(struct ieee80211_local *local,
-                                      struct ieee80211_sub_if_data *sdata,
-                                      enum nl80211_iftype type, bool p2p)
-{
-       int ret;
-
-       might_sleep();
-
-       if (!check_sdata_in_driver(sdata))
-               return -EIO;
-
-       trace_drv_change_interface(local, sdata, type, p2p);
-       ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
-       trace_drv_return_int(local, ret);
-       return ret;
-}
+int drv_add_interface(struct ieee80211_local *local,
+                     struct ieee80211_sub_if_data *sdata);
 
-static inline void drv_remove_interface(struct ieee80211_local *local,
-                                       struct ieee80211_sub_if_data *sdata)
-{
-       might_sleep();
+int drv_change_interface(struct ieee80211_local *local,
+                        struct ieee80211_sub_if_data *sdata,
+                        enum nl80211_iftype type, bool p2p);
 
-       if (!check_sdata_in_driver(sdata))
-               return;
-
-       trace_drv_remove_interface(local, sdata);
-       local->ops->remove_interface(&local->hw, &sdata->vif);
-       sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
-       trace_drv_return_void(local);
-}
+void drv_remove_interface(struct ieee80211_local *local,
+                         struct ieee80211_sub_if_data *sdata);
 
 static inline int drv_config(struct ieee80211_local *local, u32 changed)
 {
@@ -260,6 +188,22 @@ static inline void drv_configure_filter(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+static inline void drv_config_iface_filter(struct ieee80211_local *local,
+                                          struct ieee80211_sub_if_data *sdata,
+                                          unsigned int filter_flags,
+                                          unsigned int changed_flags)
+{
+       might_sleep();
+
+       trace_drv_config_iface_filter(local, sdata, filter_flags,
+                                     changed_flags);
+       if (local->ops->config_iface_filter)
+               local->ops->config_iface_filter(&local->hw, &sdata->vif,
+                                               filter_flags,
+                                               changed_flags);
+       trace_drv_return_void(local);
+}
+
 static inline int drv_set_tim(struct ieee80211_local *local,
                              struct ieee80211_sta *sta, bool set)
 {
@@ -580,25 +524,9 @@ int drv_sta_state(struct ieee80211_local *local,
                  enum ieee80211_sta_state old_state,
                  enum ieee80211_sta_state new_state);
 
-static inline void drv_sta_rc_update(struct ieee80211_local *local,
-                                    struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_sta *sta, u32 changed)
-{
-       sdata = get_bss_sdata(sdata);
-       if (!check_sdata_in_driver(sdata))
-               return;
-
-       WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED &&
-               (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
-                sdata->vif.type != NL80211_IFTYPE_MESH_POINT));
-
-       trace_drv_sta_rc_update(local, sdata, sta, changed);
-       if (local->ops->sta_rc_update)
-               local->ops->sta_rc_update(&local->hw, &sdata->vif,
-                                         sta, changed);
-
-       trace_drv_return_void(local);
-}
+void drv_sta_rc_update(struct ieee80211_local *local,
+                      struct ieee80211_sub_if_data *sdata,
+                      struct ieee80211_sta *sta, u32 changed);
 
 static inline void drv_sta_rate_tbl_update(struct ieee80211_local *local,
                                           struct ieee80211_sub_if_data *sdata,
@@ -630,76 +558,17 @@ static inline void drv_sta_statistics(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
-static inline int drv_conf_tx(struct ieee80211_local *local,
-                             struct ieee80211_sub_if_data *sdata, u16 ac,
-                             const struct ieee80211_tx_queue_params *params)
-{
-       int ret = -EOPNOTSUPP;
+int drv_conf_tx(struct ieee80211_local *local,
+               struct ieee80211_sub_if_data *sdata, u16 ac,
+               const struct ieee80211_tx_queue_params *params);
 
-       might_sleep();
-
-       if (!check_sdata_in_driver(sdata))
-               return -EIO;
-
-       if (WARN_ONCE(params->cw_min == 0 ||
-                     params->cw_min > params->cw_max,
-                     "%s: invalid CW_min/CW_max: %d/%d\n",
-                     sdata->name, params->cw_min, params->cw_max))
-               return -EINVAL;
-
-       trace_drv_conf_tx(local, sdata, ac, params);
-       if (local->ops->conf_tx)
-               ret = local->ops->conf_tx(&local->hw, &sdata->vif,
-                                         ac, params);
-       trace_drv_return_int(local, ret);
-       return ret;
-}
-
-static inline u64 drv_get_tsf(struct ieee80211_local *local,
-                             struct ieee80211_sub_if_data *sdata)
-{
-       u64 ret = -1ULL;
-
-       might_sleep();
-
-       if (!check_sdata_in_driver(sdata))
-               return ret;
-
-       trace_drv_get_tsf(local, sdata);
-       if (local->ops->get_tsf)
-               ret = local->ops->get_tsf(&local->hw, &sdata->vif);
-       trace_drv_return_u64(local, ret);
-       return ret;
-}
-
-static inline void drv_set_tsf(struct ieee80211_local *local,
-                              struct ieee80211_sub_if_data *sdata,
-                              u64 tsf)
-{
-       might_sleep();
-
-       if (!check_sdata_in_driver(sdata))
-               return;
-
-       trace_drv_set_tsf(local, sdata, tsf);
-       if (local->ops->set_tsf)
-               local->ops->set_tsf(&local->hw, &sdata->vif, tsf);
-       trace_drv_return_void(local);
-}
-
-static inline void drv_reset_tsf(struct ieee80211_local *local,
-                                struct ieee80211_sub_if_data *sdata)
-{
-       might_sleep();
-
-       if (!check_sdata_in_driver(sdata))
-               return;
-
-       trace_drv_reset_tsf(local, sdata);
-       if (local->ops->reset_tsf)
-               local->ops->reset_tsf(&local->hw, &sdata->vif);
-       trace_drv_return_void(local);
-}
+u64 drv_get_tsf(struct ieee80211_local *local,
+               struct ieee80211_sub_if_data *sdata);
+void drv_set_tsf(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                u64 tsf);
+void drv_reset_tsf(struct ieee80211_local *local,
+                  struct ieee80211_sub_if_data *sdata);
 
 static inline int drv_tx_last_beacon(struct ieee80211_local *local)
 {
@@ -714,30 +583,11 @@ static inline int drv_tx_last_beacon(struct ieee80211_local *local)
        return ret;
 }
 
-static inline int drv_ampdu_action(struct ieee80211_local *local,
-                                  struct ieee80211_sub_if_data *sdata,
-                                  enum ieee80211_ampdu_mlme_action action,
-                                  struct ieee80211_sta *sta, u16 tid,
-                                  u16 *ssn, u8 buf_size)
-{
-       int ret = -EOPNOTSUPP;
-
-       might_sleep();
-
-       sdata = get_bss_sdata(sdata);
-       if (!check_sdata_in_driver(sdata))
-               return -EIO;
-
-       trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size);
-
-       if (local->ops->ampdu_action)
-               ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action,
-                                              sta, tid, ssn, buf_size);
-
-       trace_drv_return_int(local, ret);
-
-       return ret;
-}
+int drv_ampdu_action(struct ieee80211_local *local,
+                    struct ieee80211_sub_if_data *sdata,
+                    enum ieee80211_ampdu_mlme_action action,
+                    struct ieee80211_sta *sta, u16 tid,
+                    u16 *ssn, u8 buf_size, bool amsdu);
 
 static inline int drv_get_survey(struct ieee80211_local *local, int idx,
                                struct survey_info *survey)
@@ -993,6 +843,8 @@ static inline int drv_add_chanctx(struct ieee80211_local *local,
 {
        int ret = -EOPNOTSUPP;
 
+       might_sleep();
+
        trace_drv_add_chanctx(local, ctx);
        if (local->ops->add_chanctx)
                ret = local->ops->add_chanctx(&local->hw, &ctx->conf);
@@ -1006,6 +858,8 @@ static inline int drv_add_chanctx(struct ieee80211_local *local,
 static inline void drv_remove_chanctx(struct ieee80211_local *local,
                                      struct ieee80211_chanctx *ctx)
 {
+       might_sleep();
+
        if (WARN_ON(!ctx->driver_present))
                return;
 
@@ -1020,6 +874,8 @@ static inline void drv_change_chanctx(struct ieee80211_local *local,
                                      struct ieee80211_chanctx *ctx,
                                      u32 changed)
 {
+       might_sleep();
+
        trace_drv_change_chanctx(local, ctx, changed);
        if (local->ops->change_chanctx) {
                WARN_ON_ONCE(!ctx->driver_present);
@@ -1053,6 +909,8 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
                                            struct ieee80211_sub_if_data *sdata,
                                            struct ieee80211_chanctx *ctx)
 {
+       might_sleep();
+
        if (!check_sdata_in_driver(sdata))
                return;
 
@@ -1066,64 +924,17 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
-static inline int
-drv_switch_vif_chanctx(struct ieee80211_local *local,
-                      struct ieee80211_vif_chanctx_switch *vifs,
-                      int n_vifs,
-                      enum ieee80211_chanctx_switch_mode mode)
-{
-       int ret = 0;
-       int i;
-
-       if (!local->ops->switch_vif_chanctx)
-               return -EOPNOTSUPP;
-
-       for (i = 0; i < n_vifs; i++) {
-               struct ieee80211_chanctx *new_ctx =
-                       container_of(vifs[i].new_ctx,
-                                    struct ieee80211_chanctx,
-                                    conf);
-               struct ieee80211_chanctx *old_ctx =
-                       container_of(vifs[i].old_ctx,
-                                    struct ieee80211_chanctx,
-                                    conf);
-
-               WARN_ON_ONCE(!old_ctx->driver_present);
-               WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
-                             new_ctx->driver_present) ||
-                            (mode == CHANCTX_SWMODE_REASSIGN_VIF &&
-                             !new_ctx->driver_present));
-       }
-
-       trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode);
-       ret = local->ops->switch_vif_chanctx(&local->hw,
-                                            vifs, n_vifs, mode);
-       trace_drv_return_int(local, ret);
-
-       if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) {
-               for (i = 0; i < n_vifs; i++) {
-                       struct ieee80211_chanctx *new_ctx =
-                               container_of(vifs[i].new_ctx,
-                                            struct ieee80211_chanctx,
-                                            conf);
-                       struct ieee80211_chanctx *old_ctx =
-                               container_of(vifs[i].old_ctx,
-                                            struct ieee80211_chanctx,
-                                            conf);
-
-                       new_ctx->driver_present = true;
-                       old_ctx->driver_present = false;
-               }
-       }
-
-       return ret;
-}
+int drv_switch_vif_chanctx(struct ieee80211_local *local,
+                          struct ieee80211_vif_chanctx_switch *vifs,
+                          int n_vifs, enum ieee80211_chanctx_switch_mode mode);
 
 static inline int drv_start_ap(struct ieee80211_local *local,
                               struct ieee80211_sub_if_data *sdata)
 {
        int ret = 0;
 
+       might_sleep();
+
        if (!check_sdata_in_driver(sdata))
                return -EIO;
 
index 188faab11c24573397b16296af97568afcd206cd..9cc986deda617db0e27db5ab5acb8ae45e0ba77c 100644 (file)
@@ -40,7 +40,7 @@ static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
        "rx_duplicates", "rx_fragments", "rx_dropped",
        "tx_packets", "tx_bytes",
        "tx_filtered", "tx_retry_failed", "tx_retries",
-       "beacon_loss", "sta_state", "txrate", "rxrate", "signal",
+       "sta_state", "txrate", "rxrate", "signal",
        "channel", "noise", "ch_time", "ch_time_busy",
        "ch_time_ext_busy", "ch_time_rx", "ch_time_tx"
 };
@@ -77,20 +77,19 @@ static void ieee80211_get_stats(struct net_device *dev,
 
        memset(data, 0, sizeof(u64) * STA_STATS_LEN);
 
-#define ADD_STA_STATS(sta)                             \
-       do {                                            \
-               data[i++] += sta->rx_packets;           \
-               data[i++] += sta->rx_bytes;             \
-               data[i++] += sta->num_duplicates;       \
-               data[i++] += sta->rx_fragments;         \
-               data[i++] += sta->rx_dropped;           \
-                                                       \
-               data[i++] += sinfo.tx_packets;          \
-               data[i++] += sinfo.tx_bytes;            \
-               data[i++] += sta->tx_filtered_count;    \
-               data[i++] += sta->tx_retry_failed;      \
-               data[i++] += sta->tx_retry_count;       \
-               data[i++] += sta->beacon_loss_count;    \
+#define ADD_STA_STATS(sta)                                     \
+       do {                                                    \
+               data[i++] += sta->rx_stats.packets;             \
+               data[i++] += sta->rx_stats.bytes;               \
+               data[i++] += sta->rx_stats.num_duplicates;      \
+               data[i++] += sta->rx_stats.fragments;           \
+               data[i++] += sta->rx_stats.dropped;             \
+                                                               \
+               data[i++] += sinfo.tx_packets;                  \
+               data[i++] += sinfo.tx_bytes;                    \
+               data[i++] += sta->status_stats.filtered;        \
+               data[i++] += sta->status_stats.retry_failed;    \
+               data[i++] += sta->status_stats.retry_count;     \
        } while (0)
 
        /* For Managed stations, find the single station based on BSSID
diff --git a/net/mac80211/event.c b/net/mac80211/event.c
deleted file mode 100644 (file)
index 01ae759..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2007      Johannes Berg <johannes@sipsolutions.net>
- *
- * 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.
- *
- * mac80211 - events
- */
-#include <net/cfg80211.h>
-#include "ieee80211_i.h"
-
-/*
- * Indicate a failed Michael MIC to userspace. If the caller knows the TSC of
- * the frame that generated the MIC failure (i.e., if it was provided by the
- * driver or is still in the frame), it should provide that information.
- */
-void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
-                                    struct ieee80211_hdr *hdr, const u8 *tsc,
-                                    gfp_t gfp)
-{
-       cfg80211_michael_mic_failure(sdata->dev, hdr->addr2,
-                                    (hdr->addr1[0] & 0x01) ?
-                                    NL80211_KEYTYPE_GROUP :
-                                    NL80211_KEYTYPE_PAIRWISE,
-                                    keyidx, tsc, gfp);
-}
index 7f72bc9bae2e71470f7bd4bf43b1be04b4622024..337bb5d7800384b8acd52664951be429668e3f3c 100644 (file)
@@ -188,7 +188,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
                 * keep them at 0
                 */
                pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
-                                                chandef, 0);
+                                                chandef, 0, false);
 
                /* add VHT capability and information IEs */
                if (chandef->width != NL80211_CHAN_WIDTH_20 &&
@@ -229,7 +229,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        struct cfg80211_chan_def chandef;
        struct ieee80211_channel *chan;
        struct beacon_data *presp;
-       enum nl80211_bss_scan_width scan_width;
+       struct cfg80211_inform_bss bss_meta = {};
        bool have_higher_than_11mbit;
        bool radar_required;
        int err;
@@ -356,7 +356,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        else
                sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
 
-       ieee80211_set_wmm_default(sdata, true);
+       ieee80211_set_wmm_default(sdata, true, false);
 
        sdata->vif.bss_conf.ibss_joined = true;
        sdata->vif.bss_conf.ibss_creator = creator;
@@ -383,10 +383,11 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        mod_timer(&ifibss->timer,
                  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
 
-       scan_width = cfg80211_chandef_to_scan_width(&chandef);
-       bss = cfg80211_inform_bss_width_frame(local->hw.wiphy, chan,
-                                             scan_width, mgmt,
-                                             presp->head_len, 0, GFP_KERNEL);
+       bss_meta.chan = chan;
+       bss_meta.scan_width = cfg80211_chandef_to_scan_width(&chandef);
+       bss = cfg80211_inform_bss_frame_data(local->hw.wiphy, &bss_meta, mgmt,
+                                            presp->head_len, GFP_KERNEL);
+
        cfg80211_put_bss(local->hw.wiphy, bss);
        netif_carrier_on(sdata->dev);
        cfg80211_ibss_joined(sdata->dev, ifibss->bssid, chan, GFP_KERNEL);
@@ -646,7 +647,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
                return NULL;
        }
 
-       sta->last_rx = jiffies;
+       sta->rx_stats.last_rx = jiffies;
 
        /* make sure mandatory rates are always added */
        sband = local->hw.wiphy->bands[band];
@@ -668,7 +669,8 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
 
        list_for_each_entry_rcu(sta, &local->sta_list, list) {
                if (sta->sdata == sdata &&
-                   time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
+                   time_after(sta->rx_stats.last_rx +
+                              IEEE80211_IBSS_MERGE_INTERVAL,
                               jiffies)) {
                        active++;
                        break;
@@ -1234,7 +1236,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
        if (!sta)
                return;
 
-       sta->last_rx = jiffies;
+       sta->rx_stats.last_rx = jiffies;
 
        /* make sure mandatory rates are always added */
        sband = local->hw.wiphy->bands[band];
@@ -1252,7 +1254,7 @@ static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta, *tmp;
        unsigned long exp_time = IEEE80211_IBSS_INACTIVITY_LIMIT;
-       unsigned long exp_rsn_time = IEEE80211_IBSS_RSN_INACTIVITY_LIMIT;
+       unsigned long exp_rsn = IEEE80211_IBSS_RSN_INACTIVITY_LIMIT;
 
        mutex_lock(&local->sta_mtx);
 
@@ -1260,8 +1262,8 @@ static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
                if (sdata != sta->sdata)
                        continue;
 
-               if (time_after(jiffies, sta->last_rx + exp_time) ||
-                   (time_after(jiffies, sta->last_rx + exp_rsn_time) &&
+               if (time_after(jiffies, sta->rx_stats.last_rx + exp_time) ||
+                   (time_after(jiffies, sta->rx_stats.last_rx + exp_rsn) &&
                     sta->sta_state != IEEE80211_STA_AUTHORIZED)) {
                        sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n",
                                sta->sta_state != IEEE80211_STA_AUTHORIZED ?
index 6e52659f923f72a6ab451e73b75338e4f4e7de13..d832bd59236be15c5a6bb86dbb1efc4547025f51 100644 (file)
@@ -34,6 +34,8 @@
 #include "sta_info.h"
 #include "debug.h"
 
+extern const struct cfg80211_ops mac80211_config_ops;
+
 struct ieee80211_local;
 
 /* Maximum number of broadcast/multicast frames to buffer when some of the
@@ -419,6 +421,8 @@ struct ieee80211_sta_tx_tspec {
        bool downgraded;
 };
 
+DECLARE_EWMA(beacon_signal, 16, 4)
+
 struct ieee80211_if_managed {
        struct timer_list timer;
        struct timer_list conn_mon_timer;
@@ -490,16 +494,7 @@ struct ieee80211_if_managed {
 
        s16 p2p_noa_index;
 
-       /* Signal strength from the last Beacon frame in the current BSS. */
-       int last_beacon_signal;
-
-       /*
-        * Weighted average of the signal strength from Beacon frames in the
-        * current BSS. This is in units of 1/16 of the signal unit to maintain
-        * accuracy and to speed up calculations, i.e., the value need to be
-        * divided by 16 to get the actual value.
-        */
-       int ave_beacon_signal;
+       struct ewma_beacon_signal ave_beacon_signal;
 
        /*
         * Number of Beacon frames used in ave_beacon_signal. This can be used
@@ -508,6 +503,9 @@ struct ieee80211_if_managed {
         */
        unsigned int count_beacon_signal;
 
+       /* Number of times beacon loss was invoked. */
+       unsigned int beacon_loss_count;
+
        /*
         * Last Beacon frame signal strength average (ave_beacon_signal / 16)
         * that triggered a cqm event. 0 indicates that no event has been
@@ -535,6 +533,7 @@ struct ieee80211_if_managed {
        struct sk_buff *teardown_skb; /* A copy to send through the AP */
        spinlock_t teardown_lock; /* To lock changing teardown_skb */
        bool tdls_chan_switch_prohibited;
+       bool tdls_wider_bw_prohibited;
 
        /* WMM-AC TSPEC support */
        struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS];
@@ -1311,7 +1310,6 @@ struct ieee80211_local {
        struct work_struct dynamic_ps_enable_work;
        struct work_struct dynamic_ps_disable_work;
        struct timer_list dynamic_ps_timer;
-       struct notifier_block network_latency_notifier;
        struct notifier_block ifa_notifier;
        struct notifier_block ifa6_notifier;
 
@@ -1497,10 +1495,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
                           struct cfg80211_disassoc_request *req);
 void ieee80211_send_pspoll(struct ieee80211_local *local,
                           struct ieee80211_sub_if_data *sdata);
-void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency);
+void ieee80211_recalc_ps(struct ieee80211_local *local);
 void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata);
-int ieee80211_max_network_latency(struct notifier_block *nb,
-                                 unsigned long data, void *dummy);
 int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata);
 void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
@@ -1577,7 +1573,7 @@ __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                                     struct cfg80211_sched_scan_request *req);
 int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                                       struct cfg80211_sched_scan_request *req);
-int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata);
+int ieee80211_request_sched_scan_stop(struct ieee80211_local *local);
 void ieee80211_sched_scan_end(struct ieee80211_local *local);
 void ieee80211_sched_scan_stopped_work(struct work_struct *work);
 
@@ -1641,6 +1637,9 @@ void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
 struct sk_buff *
 ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
                              struct sk_buff *skb, u32 info_flags);
+void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,
+                         struct ieee80211_supported_band *sband,
+                         int retry_count, int shift, bool send_to_cooked);
 
 void ieee80211_check_fast_xmit(struct sta_info *sta);
 void ieee80211_check_fast_xmit_all(struct ieee80211_local *local);
@@ -1769,11 +1768,8 @@ extern const void *const mac80211_wiphy_privid; /* for wiphy privid */
 int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
                             int rate, int erp, int short_preamble,
                             int shift);
-void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
-                                    struct ieee80211_hdr *hdr, const u8 *tsc,
-                                    gfp_t gfp);
 void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
-                              bool bss_notify);
+                              bool bss_notify, bool enable_qos);
 void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
                    struct sta_info *sta, struct sk_buff *skb);
 
@@ -1853,7 +1849,7 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
 void ieee80211_dynamic_ps_timer(unsigned long data);
 void ieee80211_send_nullfunc(struct ieee80211_local *local,
                             struct ieee80211_sub_if_data *sdata,
-                            int powersave);
+                            bool powersave);
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
                             struct ieee80211_hdr *hdr);
 void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
@@ -1966,7 +1962,7 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
                              u16 cap);
 u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
                               const struct cfg80211_chan_def *chandef,
-                              u16 prot_mode);
+                              u16 prot_mode, bool rifs_mode);
 u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
                               u32 cap);
 u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
index 6964fc6a8ea2c7b46149e8be5e79c6f373f50d40..d0dc1bfaeec2d5440573d114b98e16099616f43d 100644 (file)
@@ -661,11 +661,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                }
 
                /*
-                * set default queue parameters so drivers don't
+                * Set default queue parameters so drivers don't
                 * need to initialise the hardware if the hardware
-                * doesn't start up with sane defaults
+                * doesn't start up with sane defaults.
+                * Enable QoS for anything but station interfaces.
                 */
-               ieee80211_set_wmm_default(sdata, true);
+               ieee80211_set_wmm_default(sdata, true,
+                       sdata->vif.type != NL80211_IFTYPE_STATION);
        }
 
        set_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -709,7 +711,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
        if (hw_reconf_flags)
                ieee80211_hw_config(local, hw_reconf_flags);
 
-       ieee80211_recalc_ps(local, -1);
+       ieee80211_recalc_ps(local);
 
        if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
            sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
@@ -1016,7 +1018,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                        drv_remove_interface(local, sdata);
        }
 
-       ieee80211_recalc_ps(local, -1);
+       ieee80211_recalc_ps(local);
 
        if (cancel_scan)
                flush_delayed_work(&local->scan_work);
@@ -1204,7 +1206,7 @@ static void ieee80211_iface_work(struct work_struct *work)
        if (!ieee80211_sdata_running(sdata))
                return;
 
-       if (local->scanning)
+       if (test_bit(SCAN_SW_SCANNING, &local->scanning))
                return;
 
        if (!ieee80211_can_run_worker(local))
index ff79a13d231db0d4197c80a67a9d119d4c870e68..858f6b1cb1494702bbc6e0d8d25f1b4d4b4d1470 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/if_arp.h>
 #include <linux/rtnetlink.h>
 #include <linux/bitmap.h>
-#include <linux/pm_qos.h>
 #include <linux/inetdevice.h>
 #include <net/net_namespace.h>
 #include <net/cfg80211.h>
@@ -32,7 +31,6 @@
 #include "mesh.h"
 #include "wep.h"
 #include "led.h"
-#include "cfg.h"
 #include "debugfs.h"
 
 void ieee80211_configure_filter(struct ieee80211_local *local)
@@ -283,7 +281,7 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
        local->in_reconfig = true;
        barrier();
 
-       schedule_work(&local->restart_work);
+       queue_work(system_freezable_wq, &local->restart_work);
 }
 EXPORT_SYMBOL(ieee80211_restart_hw);
 
@@ -543,7 +541,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
                           NL80211_FEATURE_HT_IBSS |
                           NL80211_FEATURE_VIF_TXPOWER |
                           NL80211_FEATURE_MAC_ON_CREATE |
-                          NL80211_FEATURE_USERSPACE_MPM;
+                          NL80211_FEATURE_USERSPACE_MPM |
+                          NL80211_FEATURE_FULL_AP_CLIENT_STATE;
 
        if (!ops->hw_scan)
                wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
@@ -1082,13 +1081,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
        rtnl_unlock();
 
-       local->network_latency_notifier.notifier_call =
-               ieee80211_max_network_latency;
-       result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY,
-                                    &local->network_latency_notifier);
-       if (result)
-               goto fail_pm_qos;
-
 #ifdef CONFIG_INET
        local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
        result = register_inetaddr_notifier(&local->ifa_notifier);
@@ -1113,10 +1105,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 #endif
 #if defined(CONFIG_INET) || defined(CONFIG_IPV6)
  fail_ifa:
-       pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
-                              &local->network_latency_notifier);
 #endif
- fail_pm_qos:
        rtnl_lock();
        rate_control_deinitialize(local);
        ieee80211_remove_interfaces(local);
@@ -1142,8 +1131,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
        tasklet_kill(&local->tx_pending_tasklet);
        tasklet_kill(&local->tasklet);
 
-       pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
-                              &local->network_latency_notifier);
 #ifdef CONFIG_INET
        unregister_inetaddr_notifier(&local->ifa_notifier);
 #endif
index e06a5ca7c9a996b311b524c37f93629ccc928a61..fa28500f28fd9fd8d452602b0407741589f12e36 100644 (file)
@@ -94,6 +94,9 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
        ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
                                     ie->ht_operation, &sta_chan_def);
 
+       ieee80211_vht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
+                                     ie->vht_operation, &sta_chan_def);
+
        if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef,
                                         &sta_chan_def))
                return false;
@@ -436,8 +439,6 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_channel *channel;
-       enum nl80211_channel_type channel_type =
-               cfg80211_get_chandef_type(&sdata->vif.bss_conf.chandef);
        struct ieee80211_supported_band *sband;
        struct ieee80211_sta_ht_cap *ht_cap;
        u8 *pos;
@@ -454,7 +455,10 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata,
        sband = local->hw.wiphy->bands[channel->band];
        ht_cap = &sband->ht_cap;
 
-       if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT)
+       if (!ht_cap->ht_supported ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
                return 0;
 
        if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_operation))
@@ -462,7 +466,70 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata,
 
        pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
        ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chandef,
-                                  sdata->vif.bss_conf.ht_operation_mode);
+                                  sdata->vif.bss_conf.ht_operation_mode,
+                                  false);
+
+       return 0;
+}
+
+int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata,
+                       struct sk_buff *skb)
+{
+       struct ieee80211_local *local = sdata->local;
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       struct ieee80211_supported_band *sband;
+       u8 *pos;
+
+       sband = local->hw.wiphy->bands[band];
+       if (!sband->vht_cap.vht_supported ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
+               return 0;
+
+       if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_cap))
+               return -ENOMEM;
+
+       pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_cap));
+       ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, sband->vht_cap.cap);
+
+       return 0;
+}
+
+int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata,
+                        struct sk_buff *skb)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_channel *channel;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_sta_vht_cap *vht_cap;
+       u8 *pos;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       if (WARN_ON(!chanctx_conf)) {
+               rcu_read_unlock();
+               return -EINVAL;
+       }
+       channel = chanctx_conf->def.chan;
+       rcu_read_unlock();
+
+       sband = local->hw.wiphy->bands[channel->band];
+       vht_cap = &sband->vht_cap;
+
+       if (!vht_cap->vht_supported ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
+               return 0;
+
+       if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_operation))
+               return -ENOMEM;
+
+       pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
+       ieee80211_ie_build_vht_oper(pos, vht_cap,
+                                   &sdata->vif.bss_conf.chandef);
 
        return 0;
 }
@@ -540,9 +607,9 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
  *
  * Return the header length.
  */
-int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata,
-                             struct ieee80211s_hdr *meshhdr,
-                             const char *addr4or5, const char *addr6)
+unsigned int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata,
+                                      struct ieee80211s_hdr *meshhdr,
+                                      const char *addr4or5, const char *addr6)
 {
        if (WARN_ON(!addr4or5 && addr6))
                return 0;
@@ -637,6 +704,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
                   2 + ifmsh->mesh_id_len +
                   2 + sizeof(struct ieee80211_meshconf_ie) +
                   2 + sizeof(__le16) + /* awake window */
+                  2 + sizeof(struct ieee80211_vht_cap) +
+                  2 + sizeof(struct ieee80211_vht_operation) +
                   ifmsh->ie_len;
 
        bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
@@ -718,6 +787,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
            mesh_add_meshid_ie(sdata, skb) ||
            mesh_add_meshconf_ie(sdata, skb) ||
            mesh_add_awake_window_ie(sdata, skb) ||
+           mesh_add_vht_cap_ie(sdata, skb) ||
+           mesh_add_vht_oper_ie(sdata, skb) ||
            mesh_add_vendor_ies(sdata, skb))
                goto out_free;
 
index 50c8473cf9dc5a8c75c7d963001f51390a68e247..a1596344c3ba1c5fe590767f3c0483310d63121a 100644 (file)
@@ -207,9 +207,9 @@ struct mesh_rmc {
 /* Various */
 int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
                                  const u8 *da, const u8 *sa);
-int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata,
-                             struct ieee80211s_hdr *meshhdr,
-                             const char *addr4or5, const char *addr6);
+unsigned int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata,
+                                      struct ieee80211s_hdr *meshhdr,
+                                      const char *addr4or5, const char *addr6);
 int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
                   const u8 *addr, struct ieee80211s_hdr *mesh_hdr);
 bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
@@ -227,6 +227,10 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata,
                       struct sk_buff *skb);
 int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata,
                        struct sk_buff *skb);
+int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata,
+                       struct sk_buff *skb);
+int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata,
+                        struct sk_buff *skb);
 void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);
 int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
 void ieee80211s_init(void);
index d80e0a4c16cf98eb386bb61c3513f20619693556..c6be0b4f405888489b21cee009b267d95f626c34 100644 (file)
@@ -329,7 +329,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
        if (sta->mesh->fail_avg >= 100)
                return MAX_METRIC;
 
-       sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo);
+       sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo);
        rate = cfg80211_calculate_bitrate(&rinfo);
        if (WARN_ON(!rate))
                return MAX_METRIC;
index 58384642e03c52bb6427fc809a9149769855e9c1..bd3d55eb21d4f8fb5d4cf7a0da75a506ecdf00eb 100644 (file)
@@ -60,7 +60,9 @@ static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
 {
        s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold;
        return rssi_threshold == 0 ||
-              (sta && (s8) -ewma_signal_read(&sta->avg_signal) > rssi_threshold);
+              (sta &&
+               (s8)-ewma_signal_read(&sta->rx_stats.avg_signal) >
+                                               rssi_threshold);
 }
 
 /**
@@ -226,6 +228,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
                            2 + sizeof(struct ieee80211_meshconf_ie) +
                            2 + sizeof(struct ieee80211_ht_cap) +
                            2 + sizeof(struct ieee80211_ht_operation) +
+                           2 + sizeof(struct ieee80211_vht_cap) +
+                           2 + sizeof(struct ieee80211_vht_operation) +
                            2 + 8 + /* peering IE */
                            sdata->u.mesh.ie_len);
        if (!skb)
@@ -306,7 +310,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
 
        if (action != WLAN_SP_MESH_PEERING_CLOSE) {
                if (mesh_add_ht_cap_ie(sdata, skb) ||
-                   mesh_add_ht_oper_ie(sdata, skb))
+                   mesh_add_ht_oper_ie(sdata, skb) ||
+                   mesh_add_vht_cap_ie(sdata, skb) ||
+                   mesh_add_vht_oper_ie(sdata, skb))
                        goto free;
        }
 
@@ -386,7 +392,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
        rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
 
        spin_lock_bh(&sta->mesh->plink_lock);
-       sta->last_rx = jiffies;
+       sta->rx_stats.last_rx = jiffies;
 
        /* rates and capabilities don't change during peering */
        if (sta->mesh->plink_state == NL80211_PLINK_ESTAB &&
@@ -402,6 +408,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
                                              elems->ht_cap_elem, sta))
                changed |= IEEE80211_RC_BW_CHANGED;
 
+       ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+                                           elems->vht_cap_elem, sta);
+
        if (bw != sta->sta.bandwidth)
                changed |= IEEE80211_RC_BW_CHANGED;
 
@@ -677,6 +686,9 @@ static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
 
        rcu_read_lock();
        list_for_each_entry_rcu(sta, &local->sta_list, list) {
+               if (sdata != sta->sdata)
+                       continue;
+
                if (!memcmp(&sta->mesh->llid, &llid, sizeof(llid))) {
                        in_use = true;
                        break;
index cd7e55e08a238c23f546ce7d6860759345eff371..b140cc6651f4a7a650380965c0901b60c800746c 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/etherdevice.h>
 #include <linux/moduleparam.h>
 #include <linux/rtnetlink.h>
-#include <linux/pm_qos.h>
 #include <linux/crc32.h>
 #include <linux/slab.h>
 #include <linux/export.h>
@@ -81,13 +80,6 @@ MODULE_PARM_DESC(probe_wait_ms,
                 "Maximum time(ms) to wait for probe response"
                 " before disconnecting (reason 4).");
 
-/*
- * Weight given to the latest Beacon frame when calculating average signal
- * strength for Beacon frames received in the current BSS. This must be
- * between 1 and 15.
- */
-#define IEEE80211_SIGNAL_AVE_WEIGHT    3
-
 /*
  * How many Beacon frames need to have been used in average signal strength
  * before starting to indicate signal change events.
@@ -943,7 +935,7 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
 
 void ieee80211_send_nullfunc(struct ieee80211_local *local,
                             struct ieee80211_sub_if_data *sdata,
-                            int powersave)
+                            bool powersave)
 {
        struct sk_buff *skb;
        struct ieee80211_hdr_3addr *nullfunc;
@@ -1427,7 +1419,7 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
                          msecs_to_jiffies(conf->dynamic_ps_timeout));
        } else {
                if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK))
-                       ieee80211_send_nullfunc(local, sdata, 1);
+                       ieee80211_send_nullfunc(local, sdata, true);
 
                if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
                    ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
@@ -1483,7 +1475,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
 }
 
 /* need to hold RTNL or interface lock */
-void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
+void ieee80211_recalc_ps(struct ieee80211_local *local)
 {
        struct ieee80211_sub_if_data *sdata, *found = NULL;
        int count = 0;
@@ -1512,48 +1504,23 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
        }
 
        if (count == 1 && ieee80211_powersave_allowed(found)) {
+               u8 dtimper = found->u.mgd.dtim_period;
                s32 beaconint_us;
 
-               if (latency < 0)
-                       latency = pm_qos_request(PM_QOS_NETWORK_LATENCY);
-
                beaconint_us = ieee80211_tu_to_usec(
                                        found->vif.bss_conf.beacon_int);
 
                timeout = local->dynamic_ps_forced_timeout;
-               if (timeout < 0) {
-                       /*
-                        * Go to full PSM if the user configures a very low
-                        * latency requirement.
-                        * The 2000 second value is there for compatibility
-                        * until the PM_QOS_NETWORK_LATENCY is configured
-                        * with real values.
-                        */
-                       if (latency > (1900 * USEC_PER_MSEC) &&
-                           latency != (2000 * USEC_PER_SEC))
-                               timeout = 0;
-                       else
-                               timeout = 100;
-               }
+               if (timeout < 0)
+                       timeout = 100;
                local->hw.conf.dynamic_ps_timeout = timeout;
 
-               if (beaconint_us > latency) {
-                       local->ps_sdata = NULL;
-               } else {
-                       int maxslp = 1;
-                       u8 dtimper = found->u.mgd.dtim_period;
-
-                       /* If the TIM IE is invalid, pretend the value is 1 */
-                       if (!dtimper)
-                               dtimper = 1;
-                       else if (dtimper > 1)
-                               maxslp = min_t(int, dtimper,
-                                                   latency / beaconint_us);
-
-                       local->hw.conf.max_sleep_period = maxslp;
-                       local->hw.conf.ps_dtim_period = dtimper;
-                       local->ps_sdata = found;
-               }
+               /* If the TIM IE is invalid, pretend the value is 1 */
+               if (!dtimper)
+                       dtimper = 1;
+
+               local->hw.conf.ps_dtim_period = dtimper;
+               local->ps_sdata = found;
        } else {
                local->ps_sdata = NULL;
        }
@@ -1642,7 +1609,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
                                  msecs_to_jiffies(
                                  local->hw.conf.dynamic_ps_timeout));
                } else {
-                       ieee80211_send_nullfunc(local, sdata, 1);
+                       ieee80211_send_nullfunc(local, sdata, true);
                        /* Flush to get the tx status of nullfunc frame */
                        ieee80211_flush_queues(local, sdata, false);
                }
@@ -1777,10 +1744,10 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                                     struct ieee80211_sub_if_data *sdata,
                                     const u8 *wmm_param, size_t wmm_param_len)
 {
-       struct ieee80211_tx_queue_params params;
+       struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS];
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        size_t left;
-       int count;
+       int count, ac;
        const u8 *pos;
        u8 uapsd_queues = 0;
 
@@ -1814,25 +1781,24 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                int aci = (pos[0] >> 5) & 0x03;
                int acm = (pos[0] >> 4) & 0x01;
                bool uapsd = false;
-               int queue;
 
                switch (aci) {
                case 1: /* AC_BK */
-                       queue = 3;
+                       ac = IEEE80211_AC_BK;
                        if (acm)
                                sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
                                uapsd = true;
                        break;
                case 2: /* AC_VI */
-                       queue = 1;
+                       ac = IEEE80211_AC_VI;
                        if (acm)
                                sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
                                uapsd = true;
                        break;
                case 3: /* AC_VO */
-                       queue = 0;
+                       ac = IEEE80211_AC_VO;
                        if (acm)
                                sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
@@ -1840,7 +1806,7 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                        break;
                case 0: /* AC_BE */
                default:
-                       queue = 2;
+                       ac = IEEE80211_AC_BE;
                        if (acm)
                                sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
@@ -1848,25 +1814,41 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                        break;
                }
 
-               params.aifs = pos[0] & 0x0f;
-               params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
-               params.cw_min = ecw2cw(pos[1] & 0x0f);
-               params.txop = get_unaligned_le16(pos + 2);
-               params.acm = acm;
-               params.uapsd = uapsd;
+               params[ac].aifs = pos[0] & 0x0f;
+
+               if (params[ac].aifs < 2) {
+                       sdata_info(sdata,
+                                  "AP has invalid WMM params (AIFSN=%d for ACI %d), will use 2\n",
+                                  params[ac].aifs, aci);
+                       params[ac].aifs = 2;
+               }
+               params[ac].cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
+               params[ac].cw_min = ecw2cw(pos[1] & 0x0f);
+               params[ac].txop = get_unaligned_le16(pos + 2);
+               params[ac].acm = acm;
+               params[ac].uapsd = uapsd;
 
+               if (params[ac].cw_min > params[ac].cw_max) {
+                       sdata_info(sdata,
+                                  "AP has invalid WMM params (CWmin/max=%d/%d for ACI %d), using defaults\n",
+                                  params[ac].cw_min, params[ac].cw_max, aci);
+                       return false;
+               }
+       }
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                mlme_dbg(sdata,
-                        "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
-                        queue, aci, acm,
-                        params.aifs, params.cw_min, params.cw_max,
-                        params.txop, params.uapsd,
-                        ifmgd->tx_tspec[queue].downgraded);
-               sdata->tx_conf[queue] = params;
-               if (!ifmgd->tx_tspec[queue].downgraded &&
-                   drv_conf_tx(local, sdata, queue, &params))
+                        "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
+                        ac, params[ac].acm,
+                        params[ac].aifs, params[ac].cw_min, params[ac].cw_max,
+                        params[ac].txop, params[ac].uapsd,
+                        ifmgd->tx_tspec[ac].downgraded);
+               sdata->tx_conf[ac] = params[ac];
+               if (!ifmgd->tx_tspec[ac].downgraded &&
+                   drv_conf_tx(local, sdata, ac, &params[ac]))
                        sdata_err(sdata,
-                                 "failed to set TX queue parameters for queue %d\n",
-                                 queue);
+                                 "failed to set TX queue parameters for AC %d\n",
+                                 ac);
        }
 
        /* enable WMM or activate new settings */
@@ -2004,7 +1986,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
        mutex_lock(&local->iflist_mtx);
-       ieee80211_recalc_ps(local, -1);
+       ieee80211_recalc_ps(local);
        mutex_unlock(&local->iflist_mtx);
 
        ieee80211_recalc_smps(sdata);
@@ -2110,7 +2092,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        ieee80211_bss_info_change_notify(sdata, changed);
 
        /* disassociated - set to defaults now */
-       ieee80211_set_wmm_default(sdata, false);
+       ieee80211_set_wmm_default(sdata, false, false);
 
        del_timer_sync(&sdata->u.mgd.conn_mon_timer);
        del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
@@ -2172,7 +2154,7 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
        __ieee80211_stop_poll(sdata);
 
        mutex_lock(&local->iflist_mtx);
-       ieee80211_recalc_ps(local, -1);
+       ieee80211_recalc_ps(local);
        mutex_unlock(&local->iflist_mtx);
 
        if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
@@ -2275,7 +2257,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
 
        if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
                ifmgd->nullfunc_failed = false;
-               ieee80211_send_nullfunc(sdata->local, sdata, 0);
+               ieee80211_send_nullfunc(sdata->local, sdata, false);
        } else {
                int ssid_len;
 
@@ -2348,7 +2330,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
                goto out;
 
        mutex_lock(&sdata->local->iflist_mtx);
-       ieee80211_recalc_ps(sdata->local, -1);
+       ieee80211_recalc_ps(sdata->local);
        mutex_unlock(&sdata->local->iflist_mtx);
 
        ifmgd->probe_send_count = 0;
@@ -2453,15 +2435,9 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data,
                             u.mgd.beacon_connection_loss_work);
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct sta_info *sta;
 
-       if (ifmgd->associated) {
-               rcu_read_lock();
-               sta = sta_info_get(sdata, ifmgd->bssid);
-               if (sta)
-                       sta->beacon_loss_count++;
-               rcu_read_unlock();
-       }
+       if (ifmgd->associated)
+               ifmgd->beacon_loss_count++;
 
        if (ifmgd->connection_loss) {
                sdata_info(sdata, "Connection to AP %pM lost\n",
@@ -3051,8 +3027,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
 
        rate_control_rate_init(sta);
 
-       if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
+       if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
                set_sta_flag(sta, WLAN_STA_MFP);
+               sta->sta.mfp = true;
+       } else {
+               sta->sta.mfp = false;
+       }
 
        sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;
 
@@ -3079,11 +3059,21 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
         */
        ifmgd->wmm_last_param_set = -1;
 
-       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param)
-               ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
-                                        elems.wmm_param_len);
-       else
-               ieee80211_set_wmm_default(sdata, false);
+       if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
+               ieee80211_set_wmm_default(sdata, false, false);
+       } else if (!ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
+                                            elems.wmm_param_len)) {
+               /* still enable QoS since we might have HT/VHT */
+               ieee80211_set_wmm_default(sdata, false, true);
+               /* set the disable-WMM flag in this case to disable
+                * tracking WMM parameter changes in the beacon if
+                * the parameters weren't actually valid. Doing so
+                * avoids changing parameters very strangely when
+                * the AP is going back and forth between valid and
+                * invalid parameters.
+                */
+               ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
+       }
        changed |= BSS_CHANGED_QOS;
 
        /* set AID and assoc capability,
@@ -3262,16 +3252,6 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->associated &&
            ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
                ieee80211_reset_ap_probe(sdata);
-
-       if (ifmgd->auth_data && !ifmgd->auth_data->bss->proberesp_ies &&
-           ether_addr_equal(mgmt->bssid, ifmgd->auth_data->bss->bssid)) {
-               /* got probe response, continue with auth */
-               sdata_info(sdata, "direct probe responded\n");
-               ifmgd->auth_data->tries = 0;
-               ifmgd->auth_data->timeout = jiffies;
-               ifmgd->auth_data->timeout_started = true;
-               run_again(sdata, ifmgd->auth_data->timeout);
-       }
 }
 
 /*
@@ -3374,24 +3354,21 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        bssid = ifmgd->associated->bssid;
 
        /* Track average RSSI from the Beacon frames of the current AP */
-       ifmgd->last_beacon_signal = rx_status->signal;
        if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) {
                ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE;
-               ifmgd->ave_beacon_signal = rx_status->signal * 16;
+               ewma_beacon_signal_init(&ifmgd->ave_beacon_signal);
                ifmgd->last_cqm_event_signal = 0;
                ifmgd->count_beacon_signal = 1;
                ifmgd->last_ave_beacon_signal = 0;
        } else {
-               ifmgd->ave_beacon_signal =
-                       (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 +
-                        (16 - IEEE80211_SIGNAL_AVE_WEIGHT) *
-                        ifmgd->ave_beacon_signal) / 16;
                ifmgd->count_beacon_signal++;
        }
 
+       ewma_beacon_signal_add(&ifmgd->ave_beacon_signal, -rx_status->signal);
+
        if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold &&
            ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
-               int sig = ifmgd->ave_beacon_signal;
+               int sig = -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal);
                int last_sig = ifmgd->last_ave_beacon_signal;
                struct ieee80211_event event = {
                        .type = RSSI_EVENT,
@@ -3418,10 +3395,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        if (bss_conf->cqm_rssi_thold &&
            ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT &&
            !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) {
-               int sig = ifmgd->ave_beacon_signal / 16;
+               int sig = -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal);
                int last_event = ifmgd->last_cqm_event_signal;
                int thold = bss_conf->cqm_rssi_thold;
                int hyst = bss_conf->cqm_rssi_hyst;
+
                if (sig < thold &&
                    (last_event == 0 || sig < last_event - hyst)) {
                        ifmgd->last_cqm_event_signal = sig;
@@ -3456,31 +3434,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                          len - baselen, false, &elems,
                                          care_about_ies, ncrc);
 
-       if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) {
-               bool directed_tim = ieee80211_check_tim(elems.tim,
-                                                       elems.tim_len,
-                                                       ifmgd->aid);
-               if (directed_tim) {
-                       if (local->hw.conf.dynamic_ps_timeout > 0) {
-                               if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-                                       local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-                                       ieee80211_hw_config(local,
-                                                           IEEE80211_CONF_CHANGE_PS);
-                               }
-                               ieee80211_send_nullfunc(local, sdata, 0);
-                       } else if (!local->pspolling && sdata->u.mgd.powersave) {
-                               local->pspolling = true;
-
-                               /*
-                                * Here is assumed that the driver will be
-                                * able to send ps-poll frame and receive a
-                                * response even though power save mode is
-                                * enabled, but some drivers might require
-                                * to disable power save here. This needs
-                                * to be investigated.
-                                */
-                               ieee80211_send_pspoll(local, sdata);
+       if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
+           ieee80211_check_tim(elems.tim, elems.tim_len, ifmgd->aid)) {
+               if (local->hw.conf.dynamic_ps_timeout > 0) {
+                       if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+                               local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+                               ieee80211_hw_config(local,
+                                                   IEEE80211_CONF_CHANGE_PS);
                        }
+                       ieee80211_send_nullfunc(local, sdata, false);
+               } else if (!local->pspolling && sdata->u.mgd.powersave) {
+                       local->pspolling = true;
+
+                       /*
+                        * Here is assumed that the driver will be
+                        * able to send ps-poll frame and receive a
+                        * response even though power save mode is
+                        * enabled, but some drivers might require
+                        * to disable power save here. This needs
+                        * to be investigated.
+                        */
+                       ieee80211_send_pspoll(local, sdata);
                }
        }
 
@@ -3567,7 +3541,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                ifmgd->have_beacon = true;
 
                mutex_lock(&local->iflist_mtx);
-               ieee80211_recalc_ps(local, -1);
+               ieee80211_recalc_ps(local);
                mutex_unlock(&local->iflist_mtx);
 
                ieee80211_recalc_ps_vif(sdata);
@@ -3717,12 +3691,14 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
                                    reason);
 }
 
-static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
+static int ieee80211_auth(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data;
        u32 tx_flags = 0;
+       u16 trans = 1;
+       u16 status = 0;
 
        sdata_assert_lock(sdata);
 
@@ -3746,54 +3722,27 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
 
        drv_mgd_prepare_tx(local, sdata);
 
-       if (auth_data->bss->proberesp_ies) {
-               u16 trans = 1;
-               u16 status = 0;
-
-               sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
-                          auth_data->bss->bssid, auth_data->tries,
-                          IEEE80211_AUTH_MAX_TRIES);
-
-               auth_data->expected_transaction = 2;
+       sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
+                  auth_data->bss->bssid, auth_data->tries,
+                  IEEE80211_AUTH_MAX_TRIES);
 
-               if (auth_data->algorithm == WLAN_AUTH_SAE) {
-                       trans = auth_data->sae_trans;
-                       status = auth_data->sae_status;
-                       auth_data->expected_transaction = trans;
-               }
+       auth_data->expected_transaction = 2;
 
-               if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
-                       tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
-                                  IEEE80211_TX_INTFL_MLME_CONN_TX;
-
-               ieee80211_send_auth(sdata, trans, auth_data->algorithm, status,
-                                   auth_data->data, auth_data->data_len,
-                                   auth_data->bss->bssid,
-                                   auth_data->bss->bssid, NULL, 0, 0,
-                                   tx_flags);
-       } else {
-               const u8 *ssidie;
+       if (auth_data->algorithm == WLAN_AUTH_SAE) {
+               trans = auth_data->sae_trans;
+               status = auth_data->sae_status;
+               auth_data->expected_transaction = trans;
+       }
 
-               sdata_info(sdata, "direct probe to %pM (try %d/%i)\n",
-                          auth_data->bss->bssid, auth_data->tries,
-                          IEEE80211_AUTH_MAX_TRIES);
+       if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
+               tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
+                          IEEE80211_TX_INTFL_MLME_CONN_TX;
 
-               rcu_read_lock();
-               ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID);
-               if (!ssidie) {
-                       rcu_read_unlock();
-                       return -EINVAL;
-               }
-               /*
-                * Direct probe is sent to broadcast address as some APs
-                * will not answer to direct packet in unassociated state.
-                */
-               ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
-                                        ssidie + 2, ssidie[1],
-                                        NULL, 0, (u32) -1, true, 0,
-                                        auth_data->bss->channel, false);
-               rcu_read_unlock();
-       }
+       ieee80211_send_auth(sdata, trans, auth_data->algorithm, status,
+                           auth_data->data, auth_data->data_len,
+                           auth_data->bss->bssid,
+                           auth_data->bss->bssid, NULL, 0, 0,
+                           tx_flags);
 
        if (tx_flags == 0) {
                auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
@@ -3874,8 +3823,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                bool status_acked = ifmgd->status_acked;
 
                ifmgd->status_received = false;
-               if (ifmgd->auth_data &&
-                   (ieee80211_is_probe_req(fc) || ieee80211_is_auth(fc))) {
+               if (ifmgd->auth_data && ieee80211_is_auth(fc)) {
                        if (status_acked) {
                                ifmgd->auth_data->timeout =
                                        jiffies + IEEE80211_AUTH_TIMEOUT_SHORT;
@@ -3906,7 +3854,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                         * so let's just kill the auth data
                         */
                        ieee80211_destroy_auth_data(sdata, false);
-               } else if (ieee80211_probe_auth(sdata)) {
+               } else if (ieee80211_auth(sdata)) {
                        u8 bssid[ETH_ALEN];
                        struct ieee80211_event event = {
                                .type = MLME_EVENT,
@@ -4197,21 +4145,6 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
        rcu_read_unlock();
 }
 
-int ieee80211_max_network_latency(struct notifier_block *nb,
-                                 unsigned long data, void *dummy)
-{
-       s32 latency_usec = (s32) data;
-       struct ieee80211_local *local =
-               container_of(nb, struct ieee80211_local,
-                            network_latency_notifier);
-
-       mutex_lock(&local->iflist_mtx);
-       ieee80211_recalc_ps(local, latency_usec);
-       mutex_unlock(&local->iflist_mtx);
-
-       return NOTIFY_OK;
-}
-
 static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
                                     struct cfg80211_bss *cbss)
 {
@@ -4613,7 +4546,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        if (err)
                goto err_clear;
 
-       err = ieee80211_probe_auth(sdata);
+       err = ieee80211_auth(sdata);
        if (err) {
                sta_info_destroy_addr(sdata, req->bss->bssid);
                goto err_clear;
@@ -4635,44 +4568,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        return err;
 }
 
-static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata,
-                                       const u8 *wmm_param, int len)
-{
-       const u8 *pos;
-       size_t left;
-
-       if (len < 8)
-               return false;
-
-       if (wmm_param[5] != 1 /* version */)
-               return false;
-
-       pos = wmm_param + 8;
-       left = len - 8;
-
-       for (; left >= 4; left -= 4, pos += 4) {
-               u8 aifsn = pos[0] & 0x0f;
-               u8 ecwmin = pos[1] & 0x0f;
-               u8 ecwmax = (pos[1] & 0xf0) >> 4;
-               int aci = (pos[0] >> 5) & 0x03;
-
-               if (aifsn < 2) {
-                       sdata_info(sdata,
-                                  "AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n",
-                                  aifsn, aci);
-                       return false;
-               }
-               if (ecwmin > ecwmax) {
-                       sdata_info(sdata,
-                                  "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n",
-                                  ecwmin, ecwmax, aci);
-                       return false;
-               }
-       }
-
-       return true;
-}
-
 int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_assoc_request *req)
 {
@@ -4737,39 +4632,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 
        assoc_data->wmm = bss->wmm_used &&
                          (local->hw.queues >= IEEE80211_NUM_ACS);
-       if (assoc_data->wmm) {
-               /* try to check validity of WMM params IE */
-               const struct cfg80211_bss_ies *ies;
-               const u8 *wp, *start, *end;
-
-               rcu_read_lock();
-               ies = rcu_dereference(req->bss->ies);
-               start = ies->data;
-               end = start + ies->len;
-
-               while (true) {
-                       wp = cfg80211_find_vendor_ie(
-                               WLAN_OUI_MICROSOFT,
-                               WLAN_OUI_TYPE_MICROSOFT_WMM,
-                               start, end - start);
-                       if (!wp)
-                               break;
-                       start = wp + wp[1] + 2;
-                       /* if this IE is too short, try the next */
-                       if (wp[1] <= 4)
-                               continue;
-                       /* if this IE is WMM params, we found what we wanted */
-                       if (wp[6] == 1)
-                               break;
-               }
-
-               if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2,
-                                                       wp[1] - 2)) {
-                       assoc_data->wmm = false;
-                       ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
-               }
-               rcu_read_unlock();
-       }
 
        /*
         * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
@@ -5028,6 +4890,25 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                return 0;
        }
 
+       if (ifmgd->assoc_data &&
+           ether_addr_equal(ifmgd->assoc_data->bss->bssid, req->bssid)) {
+               sdata_info(sdata,
+                          "aborting association with %pM by local choice (Reason: %u=%s)\n",
+                          req->bssid, req->reason_code,
+                          ieee80211_get_reason_code_string(req->reason_code));
+
+               drv_mgd_prepare_tx(sdata->local, sdata);
+               ieee80211_send_deauth_disassoc(sdata, req->bssid,
+                                              IEEE80211_STYPE_DEAUTH,
+                                              req->reason_code, tx,
+                                              frame_buf);
+               ieee80211_destroy_assoc_data(sdata, false);
+               ieee80211_report_disconnect(sdata, frame_buf,
+                                           sizeof(frame_buf), true,
+                                           req->reason_code);
+               return 0;
+       }
+
        if (ifmgd->associated &&
            ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
                sdata_info(sdata,
index 573b81a1fb2d882487f75ccc24493cc1b9deee5e..0be0aadfc55913621454c560de927b380076f059 100644 (file)
@@ -75,7 +75,7 @@ void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
        if (!sta)
                return;
 
-       sta->last_rx = jiffies;
+       sta->rx_stats.last_rx = jiffies;
 
        /* Add only mandatory rates for now */
        sband = local->hw.wiphy->bands[band];
index f2c75cf491fc019bdbc01d58feab49608cce9dca..04401037140ee85e7fcf5ad84a29193cb1e19240 100644 (file)
@@ -57,7 +57,7 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
                 * to send a new nullfunc frame to inform the AP that we
                 * are again sleeping.
                 */
-               ieee80211_send_nullfunc(local, sdata, 1);
+               ieee80211_send_nullfunc(local, sdata, true);
 }
 
 /* inform AP that we are awake again, unless power save is enabled */
@@ -66,7 +66,7 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_local *local = sdata->local;
 
        if (!local->ps_sdata)
-               ieee80211_send_nullfunc(local, sdata, 0);
+               ieee80211_send_nullfunc(local, sdata, false);
        else if (local->offchannel_ps_enabled) {
                /*
                 * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
@@ -93,7 +93,7 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
                 * restart the timer now and send a nullfunc frame to inform
                 * the AP that we are awake.
                 */
-               ieee80211_send_nullfunc(local, sdata, 0);
+               ieee80211_send_nullfunc(local, sdata, false);
                mod_timer(&local->dynamic_ps_timer, jiffies +
                          msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
        }
index b676b9fa707b3c8872a1065d8582b8ff1463e607..00a43a70e1fc30eedb09ab9dcdde8af2a9c89673 100644 (file)
@@ -6,6 +6,13 @@
 #include "driver-ops.h"
 #include "led.h"
 
+static void ieee80211_sched_scan_cancel(struct ieee80211_local *local)
+{
+       if (ieee80211_request_sched_scan_stop(local))
+               return;
+       cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy);
+}
+
 int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 {
        struct ieee80211_local *local = hw_to_local(hw);
@@ -23,7 +30,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 
        ieee80211_del_virtual_monitor(local);
 
-       if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) {
+       if (ieee80211_hw_check(hw, AMPDU_AGGREGATION) &&
+           !(wowlan && wowlan->any)) {
                mutex_lock(&local->sta_mtx);
                list_for_each_entry(sta, &local->sta_list, list) {
                        set_sta_flag(sta, WLAN_STA_BLOCK_BA);
@@ -33,6 +41,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                mutex_unlock(&local->sta_mtx);
        }
 
+       /* keep sched_scan only in case of 'any' trigger */
+       if (!(wowlan && wowlan->any))
+               ieee80211_sched_scan_cancel(local);
+
        ieee80211_stop_queues_by_reason(hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_SUSPEND,
index 9ce8883d5f449ed438f0a4a4a4807ede153c6725..a4e2f4e67f941084714fedab7b7e087df4fbb47f 100644 (file)
@@ -305,7 +305,10 @@ static void __rate_control_send_low(struct ieee80211_hw *hw,
                info->control.rates[0].idx = i;
                break;
        }
-       WARN_ON_ONCE(i == sband->n_bitrates);
+       WARN_ONCE(i == sband->n_bitrates,
+                 "no supported rates (0x%x) in rate_mask 0x%x with flags 0x%x\n",
+                 sta ? sta->supp_rates[sband->band] : -1,
+                 rate_mask, rate_flags);
 
        info->control.rates[0].count =
                (info->flags & IEEE80211_TX_CTL_NO_ACK) ?
index 1db5f7c3318ada6a1a1aede778dee49ccc99e824..820b0abc9c0d6f7662cf7a98cc497b310d1281e3 100644 (file)
@@ -85,12 +85,10 @@ minstrel_stats_open(struct inode *inode, struct file *file)
        file->private_data = ms;
        p = ms->buf;
        p += sprintf(p, "\n");
-       p += sprintf(p, "best   __________rate_________    ______"
-                       "statistics______    ________last_______    "
-                       "______sum-of________\n");
-       p += sprintf(p, "rate  [name idx airtime max_tp]  [ Ã¸(tp) Ã¸(prob) "
-                       "sd(prob)]  [prob.|retry|suc|att]  "
-                       "[#success | #attempts]\n");
+       p += sprintf(p,
+                    "best   __________rate_________    ________statistics________    ________last_______    ______sum-of________\n");
+       p += sprintf(p,
+                    "rate  [name idx airtime max_tp]  [avg(tp) avg(prob) sd(prob)]  [prob.|retry|suc|att]  [#success | #attempts]\n");
 
        for (i = 0; i < mi->n_rates; i++) {
                struct minstrel_rate *mr = &mi->r[i];
@@ -112,7 +110,7 @@ minstrel_stats_open(struct inode *inode, struct file *file)
                prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
                eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
 
-               p += sprintf(p, "%4u.%1u   %4u.%1u   %3u.%1u    %3u.%1u"
+               p += sprintf(p, "%4u.%1u    %4u.%1u     %3u.%1u    %3u.%1u"
                                "     %3u.%1u %3u   %3u %-3u   "
                                "%9llu   %-9llu\n",
                                tp_max / 10, tp_max % 10,
index 6822ce0f95e580641f782fb2f59dcf9f09e5e639..5320e35ed3d0f2dc899176b9e569b59877de9e38 100644 (file)
@@ -86,7 +86,7 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
                prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
                eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
 
-               p += sprintf(p, "%4u.%1u   %4u.%1u   %3u.%1u    %3u.%1u"
+               p += sprintf(p, "%4u.%1u    %4u.%1u     %3u.%1u    %3u.%1u"
                                "     %3u.%1u %3u   %3u %-3u   "
                                "%9llu   %-9llu\n",
                                tp_max / 10, tp_max % 10,
@@ -129,12 +129,10 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
        p = ms->buf;
 
        p += sprintf(p, "\n");
-       p += sprintf(p, "              best   ____________rate__________    "
-                       "______statistics______    ________last_______    "
-                       "______sum-of________\n");
-       p += sprintf(p, "mode guard #  rate  [name   idx airtime  max_tp]  "
-                       "[ Ã¸(tp) Ã¸(prob) sd(prob)]  [prob.|retry|suc|att]  [#success | "
-                       "#attempts]\n");
+       p += sprintf(p,
+                    "              best   ____________rate__________    ________statistics________    ________last_______    ______sum-of________\n");
+       p += sprintf(p,
+                    "mode guard #  rate  [name   idx airtime  max_tp]  [avg(tp) avg(prob) sd(prob)]  [prob.|retry|suc|att]  [#success | #attempts]\n");
 
        p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
        for (i = 0; i < MINSTREL_CCK_GROUP; i++)
index 5bc0b88d9eb1331a0dd47aab05ffa01c7a0bc845..8bae5de0dc44299146993df3581a951078eed733 100644 (file)
@@ -1113,16 +1113,16 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx)
            is_multicast_ether_addr(hdr->addr1))
                return RX_CONTINUE;
 
-       if (rx->sta) {
-               if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
-                            rx->sta->last_seq_ctrl[rx->seqno_idx] ==
-                            hdr->seq_ctrl)) {
-                       I802_DEBUG_INC(rx->local->dot11FrameDuplicateCount);
-                       rx->sta->num_duplicates++;
-                       return RX_DROP_UNUSABLE;
-               } else if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
-                       rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
-               }
+       if (!rx->sta)
+               return RX_CONTINUE;
+
+       if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
+                    rx->sta->last_seq_ctrl[rx->seqno_idx] == hdr->seq_ctrl)) {
+               I802_DEBUG_INC(rx->local->dot11FrameDuplicateCount);
+               rx->sta->rx_stats.num_duplicates++;
+               return RX_DROP_UNUSABLE;
+       } else if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
+               rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
        }
 
        return RX_CONTINUE;
@@ -1396,51 +1396,56 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
                                                NL80211_IFTYPE_ADHOC);
                if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid) &&
                    test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
-                       sta->last_rx = jiffies;
+                       sta->rx_stats.last_rx = jiffies;
                        if (ieee80211_is_data(hdr->frame_control) &&
                            !is_multicast_ether_addr(hdr->addr1)) {
-                               sta->last_rx_rate_idx = status->rate_idx;
-                               sta->last_rx_rate_flag = status->flag;
-                               sta->last_rx_rate_vht_flag = status->vht_flag;
-                               sta->last_rx_rate_vht_nss = status->vht_nss;
+                               sta->rx_stats.last_rate_idx =
+                                       status->rate_idx;
+                               sta->rx_stats.last_rate_flag =
+                                       status->flag;
+                               sta->rx_stats.last_rate_vht_flag =
+                                       status->vht_flag;
+                               sta->rx_stats.last_rate_vht_nss =
+                                       status->vht_nss;
                        }
                }
        } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
-               sta->last_rx = jiffies;
+               sta->rx_stats.last_rx = jiffies;
        } else if (!is_multicast_ether_addr(hdr->addr1)) {
                /*
                 * Mesh beacons will update last_rx when if they are found to
                 * match the current local configuration when processed.
                 */
-               sta->last_rx = jiffies;
+               sta->rx_stats.last_rx = jiffies;
                if (ieee80211_is_data(hdr->frame_control)) {
-                       sta->last_rx_rate_idx = status->rate_idx;
-                       sta->last_rx_rate_flag = status->flag;
-                       sta->last_rx_rate_vht_flag = status->vht_flag;
-                       sta->last_rx_rate_vht_nss = status->vht_nss;
+                       sta->rx_stats.last_rate_idx = status->rate_idx;
+                       sta->rx_stats.last_rate_flag = status->flag;
+                       sta->rx_stats.last_rate_vht_flag = status->vht_flag;
+                       sta->rx_stats.last_rate_vht_nss = status->vht_nss;
                }
        }
 
        if (rx->sdata->vif.type == NL80211_IFTYPE_STATION)
                ieee80211_sta_rx_notify(rx->sdata, hdr);
 
-       sta->rx_fragments++;
-       sta->rx_bytes += rx->skb->len;
+       sta->rx_stats.fragments++;
+       sta->rx_stats.bytes += rx->skb->len;
        if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
-               sta->last_signal = status->signal;
-               ewma_signal_add(&sta->avg_signal, -status->signal);
+               sta->rx_stats.last_signal = status->signal;
+               ewma_signal_add(&sta->rx_stats.avg_signal, -status->signal);
        }
 
        if (status->chains) {
-               sta->chains = status->chains;
+               sta->rx_stats.chains = status->chains;
                for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
                        int signal = status->chain_signal[i];
 
                        if (!(status->chains & BIT(i)))
                                continue;
 
-                       sta->chain_signal_last[i] = signal;
-                       ewma_signal_add(&sta->chain_signal_avg[i], -signal);
+                       sta->rx_stats.chain_signal_last[i] = signal;
+                       ewma_signal_add(&sta->rx_stats.chain_signal_avg[i],
+                                       -signal);
                }
        }
 
@@ -1500,7 +1505,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
                 * Update counter and free packet here to avoid
                 * counting this as a dropped packed.
                 */
-               sta->rx_packets++;
+               sta->rx_stats.packets++;
                dev_kfree_skb(rx->skb);
                return RX_QUEUED;
        }
@@ -1922,7 +1927,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
        ieee80211_led_rx(rx->local);
  out_no_led:
        if (rx->sta)
-               rx->sta->rx_packets++;
+               rx->sta->rx_stats.packets++;
        return RX_CONTINUE;
 }
 
@@ -2376,7 +2381,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
                 * for non-QoS-data frames. Here we know it's a data
                 * frame, so count MSDUs.
                 */
-               rx->sta->rx_msdu[rx->seqno_idx]++;
+               rx->sta->rx_stats.msdu[rx->seqno_idx]++;
        }
 
        /*
@@ -2413,7 +2418,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
                        skb_queue_tail(&local->skb_queue_tdls_chsw, rx->skb);
                        schedule_work(&local->tdls_chsw_work);
                        if (rx->sta)
-                               rx->sta->rx_packets++;
+                               rx->sta->rx_stats.packets++;
 
                        return RX_QUEUED;
                }
@@ -2875,7 +2880,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
 
  handled:
        if (rx->sta)
-               rx->sta->rx_packets++;
+               rx->sta->rx_stats.packets++;
        dev_kfree_skb(rx->skb);
        return RX_QUEUED;
 
@@ -2884,7 +2889,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
        skb_queue_tail(&sdata->skb_queue, rx->skb);
        ieee80211_queue_work(&local->hw, &sdata->work);
        if (rx->sta)
-               rx->sta->rx_packets++;
+               rx->sta->rx_stats.packets++;
        return RX_QUEUED;
 }
 
@@ -2911,7 +2916,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
        if (cfg80211_rx_mgmt(&rx->sdata->wdev, status->freq, sig,
                             rx->skb->data, rx->skb->len, 0)) {
                if (rx->sta)
-                       rx->sta->rx_packets++;
+                       rx->sta->rx_stats.packets++;
                dev_kfree_skb(rx->skb);
                return RX_QUEUED;
        }
@@ -3030,7 +3035,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
        skb_queue_tail(&sdata->skb_queue, rx->skb);
        ieee80211_queue_work(&rx->local->hw, &sdata->work);
        if (rx->sta)
-               rx->sta->rx_packets++;
+               rx->sta->rx_stats.packets++;
 
        return RX_QUEUED;
 }
@@ -3112,7 +3117,7 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
        case RX_DROP_MONITOR:
                I802_DEBUG_INC(rx->sdata->local->rx_handlers_drop);
                if (rx->sta)
-                       rx->sta->rx_dropped++;
+                       rx->sta->rx_stats.dropped++;
                /* fall through */
        case RX_CONTINUE: {
                struct ieee80211_rate *rate = NULL;
@@ -3132,7 +3137,7 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
        case RX_DROP_UNUSABLE:
                I802_DEBUG_INC(rx->sdata->local->rx_handlers_drop);
                if (rx->sta)
-                       rx->sta->rx_dropped++;
+                       rx->sta->rx_stats.dropped++;
                dev_kfree_skb(rx->skb);
                break;
        case RX_QUEUED:
index 11d0901ebb7b6e1015abae8534ee3aa6cc5c8b26..4aeca4b0c3cb426ba65b23d4a8ebc756e7a826a4 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/if_arp.h>
 #include <linux/etherdevice.h>
 #include <linux/rtnetlink.h>
-#include <linux/pm_qos.h>
 #include <net/sch_generic.h>
 #include <linux/slab.h>
 #include <linux/export.h>
@@ -67,24 +66,23 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        struct cfg80211_bss *cbss;
        struct ieee80211_bss *bss;
        int clen, srlen;
-       enum nl80211_bss_scan_width scan_width;
-       s32 signal = 0;
+       struct cfg80211_inform_bss bss_meta = {};
        bool signal_valid;
 
        if (ieee80211_hw_check(&local->hw, SIGNAL_DBM))
-               signal = rx_status->signal * 100;
+               bss_meta.signal = rx_status->signal * 100;
        else if (ieee80211_hw_check(&local->hw, SIGNAL_UNSPEC))
-               signal = (rx_status->signal * 100) / local->hw.max_signal;
+               bss_meta.signal = (rx_status->signal * 100) / local->hw.max_signal;
 
-       scan_width = NL80211_BSS_CHAN_WIDTH_20;
+       bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_20;
        if (rx_status->flag & RX_FLAG_5MHZ)
-               scan_width = NL80211_BSS_CHAN_WIDTH_5;
+               bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_5;
        if (rx_status->flag & RX_FLAG_10MHZ)
-               scan_width = NL80211_BSS_CHAN_WIDTH_10;
+               bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_10;
 
-       cbss = cfg80211_inform_bss_width_frame(local->hw.wiphy, channel,
-                                              scan_width, mgmt, len, signal,
-                                              GFP_ATOMIC);
+       bss_meta.chan = channel;
+       cbss = cfg80211_inform_bss_frame_data(local->hw.wiphy, &bss_meta,
+                                             mgmt, len, GFP_ATOMIC);
        if (!cbss)
                return NULL;
        /* In case the signal is invalid update the status */
@@ -1142,10 +1140,10 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
        return ret;
 }
 
-int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
+int ieee80211_request_sched_scan_stop(struct ieee80211_local *local)
 {
-       struct ieee80211_local *local = sdata->local;
-       int ret = 0;
+       struct ieee80211_sub_if_data *sched_scan_sdata;
+       int ret = -ENOENT;
 
        mutex_lock(&local->mtx);
 
@@ -1157,8 +1155,10 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
        /* We don't want to restart sched scan anymore. */
        RCU_INIT_POINTER(local->sched_scan_req, NULL);
 
-       if (rcu_access_pointer(local->sched_scan_sdata)) {
-               ret = drv_sched_scan_stop(local, sdata);
+       sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
+                                               lockdep_is_held(&local->mtx));
+       if (sched_scan_sdata) {
+               ret = drv_sched_scan_stop(local, sched_scan_sdata);
                if (!ret)
                        RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
        }
index 64f1936350c66e48fb076beb7b418d55f42c8753..f91d1873218c7e07a1a8aecb006e02026ecc42ce 100644 (file)
@@ -303,7 +303,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_hw *hw = &local->hw;
        struct sta_info *sta;
-       struct timespec uptime;
        int i;
 
        sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
@@ -332,18 +331,17 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        memcpy(sta->sta.addr, addr, ETH_ALEN);
        sta->local = local;
        sta->sdata = sdata;
-       sta->last_rx = jiffies;
+       sta->rx_stats.last_rx = jiffies;
 
        sta->sta_state = IEEE80211_STA_NONE;
 
        /* Mark TID as unreserved */
        sta->reserved_tid = IEEE80211_TID_UNRESERVED;
 
-       ktime_get_ts(&uptime);
-       sta->last_connected = uptime.tv_sec;
-       ewma_signal_init(&sta->avg_signal);
-       for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
-               ewma_signal_init(&sta->chain_signal_avg[i]);
+       sta->last_connected = ktime_get_seconds();
+       ewma_signal_init(&sta->rx_stats.avg_signal);
+       for (i = 0; i < ARRAY_SIZE(sta->rx_stats.chain_signal_avg); i++)
+               ewma_signal_init(&sta->rx_stats.chain_signal_avg[i]);
 
        if (local->ops->wake_tx_queue) {
                void *txq_data;
@@ -1068,7 +1066,7 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                if (sdata != sta->sdata)
                        continue;
 
-               if (time_after(jiffies, sta->last_rx + exp_time)) {
+               if (time_after(jiffies, sta->rx_stats.last_rx + exp_time)) {
                        sta_dbg(sta->sdata, "expiring inactive STA %pM\n",
                                sta->sta.addr);
 
@@ -1808,12 +1806,50 @@ u8 sta_info_tx_streams(struct sta_info *sta)
                        >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
 }
 
+static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
+{
+       rinfo->flags = 0;
+
+       if (sta->rx_stats.last_rate_flag & RX_FLAG_HT) {
+               rinfo->flags |= RATE_INFO_FLAGS_MCS;
+               rinfo->mcs = sta->rx_stats.last_rate_idx;
+       } else if (sta->rx_stats.last_rate_flag & RX_FLAG_VHT) {
+               rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
+               rinfo->nss = sta->rx_stats.last_rate_vht_nss;
+               rinfo->mcs = sta->rx_stats.last_rate_idx;
+       } else {
+               struct ieee80211_supported_band *sband;
+               int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+               u16 brate;
+
+               sband = sta->local->hw.wiphy->bands[
+                               ieee80211_get_sdata_band(sta->sdata)];
+               brate = sband->bitrates[sta->rx_stats.last_rate_idx].bitrate;
+               rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
+       }
+
+       if (sta->rx_stats.last_rate_flag & RX_FLAG_SHORT_GI)
+               rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
+
+       if (sta->rx_stats.last_rate_flag & RX_FLAG_5MHZ)
+               rinfo->bw = RATE_INFO_BW_5;
+       else if (sta->rx_stats.last_rate_flag & RX_FLAG_10MHZ)
+               rinfo->bw = RATE_INFO_BW_10;
+       else if (sta->rx_stats.last_rate_flag & RX_FLAG_40MHZ)
+               rinfo->bw = RATE_INFO_BW_40;
+       else if (sta->rx_stats.last_rate_vht_flag & RX_VHT_FLAG_80MHZ)
+               rinfo->bw = RATE_INFO_BW_80;
+       else if (sta->rx_stats.last_rate_vht_flag & RX_VHT_FLAG_160MHZ)
+               rinfo->bw = RATE_INFO_BW_160;
+       else
+               rinfo->bw = RATE_INFO_BW_20;
+}
+
 void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
        struct rate_control_ref *ref = NULL;
-       struct timespec uptime;
        u32 thr = 0;
        int i, ac;
 
@@ -1835,51 +1871,54 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                         BIT(NL80211_STA_INFO_STA_FLAGS) |
                         BIT(NL80211_STA_INFO_BSS_PARAM) |
                         BIT(NL80211_STA_INFO_CONNECTED_TIME) |
-                        BIT(NL80211_STA_INFO_RX_DROP_MISC) |
-                        BIT(NL80211_STA_INFO_BEACON_LOSS);
+                        BIT(NL80211_STA_INFO_RX_DROP_MISC);
+
+       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+               sinfo->beacon_loss_count = sdata->u.mgd.beacon_loss_count;
+               sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_LOSS);
+       }
 
-       ktime_get_ts(&uptime);
-       sinfo->connected_time = uptime.tv_sec - sta->last_connected;
-       sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
+       sinfo->connected_time = ktime_get_seconds() - sta->last_connected;
+       sinfo->inactive_time =
+               jiffies_to_msecs(jiffies - sta->rx_stats.last_rx);
 
        if (!(sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES64) |
                               BIT(NL80211_STA_INFO_TX_BYTES)))) {
                sinfo->tx_bytes = 0;
                for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
-                       sinfo->tx_bytes += sta->tx_bytes[ac];
+                       sinfo->tx_bytes += sta->tx_stats.bytes[ac];
                sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES64);
        }
 
        if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_PACKETS))) {
                sinfo->tx_packets = 0;
                for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
-                       sinfo->tx_packets += sta->tx_packets[ac];
+                       sinfo->tx_packets += sta->tx_stats.packets[ac];
                sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
        }
 
        if (!(sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES64) |
                               BIT(NL80211_STA_INFO_RX_BYTES)))) {
-               sinfo->rx_bytes = sta->rx_bytes;
+               sinfo->rx_bytes = sta->rx_stats.bytes;
                sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64);
        }
 
        if (!(sinfo->filled & BIT(NL80211_STA_INFO_RX_PACKETS))) {
-               sinfo->rx_packets = sta->rx_packets;
+               sinfo->rx_packets = sta->rx_stats.packets;
                sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
        }
 
        if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_RETRIES))) {
-               sinfo->tx_retries = sta->tx_retry_count;
+               sinfo->tx_retries = sta->status_stats.retry_count;
                sinfo->filled |= BIT(NL80211_STA_INFO_TX_RETRIES);
        }
 
        if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_FAILED))) {
-               sinfo->tx_failed = sta->tx_retry_failed;
+               sinfo->tx_failed = sta->status_stats.retry_failed;
                sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
        }
 
-       sinfo->rx_dropped_misc = sta->rx_dropped;
-       sinfo->beacon_loss_count = sta->beacon_loss_count;
+       sinfo->rx_dropped_misc = sta->rx_stats.dropped;
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
            !(sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)) {
@@ -1891,33 +1930,35 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
        if (ieee80211_hw_check(&sta->local->hw, SIGNAL_DBM) ||
            ieee80211_hw_check(&sta->local->hw, SIGNAL_UNSPEC)) {
                if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL))) {
-                       sinfo->signal = (s8)sta->last_signal;
+                       sinfo->signal = (s8)sta->rx_stats.last_signal;
                        sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
                }
 
                if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) {
                        sinfo->signal_avg =
-                               (s8) -ewma_signal_read(&sta->avg_signal);
+                               -ewma_signal_read(&sta->rx_stats.avg_signal);
                        sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
                }
        }
 
-       if (sta->chains &&
+       if (sta->rx_stats.chains &&
            !(sinfo->filled & (BIT(NL80211_STA_INFO_CHAIN_SIGNAL) |
                               BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)))) {
                sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL) |
                                 BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
 
-               sinfo->chains = sta->chains;
+               sinfo->chains = sta->rx_stats.chains;
                for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
-                       sinfo->chain_signal[i] = sta->chain_signal_last[i];
+                       sinfo->chain_signal[i] =
+                               sta->rx_stats.chain_signal_last[i];
                        sinfo->chain_signal_avg[i] =
-                               (s8) -ewma_signal_read(&sta->chain_signal_avg[i]);
+                               -ewma_signal_read(&sta->rx_stats.chain_signal_avg[i]);
                }
        }
 
        if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE))) {
-               sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
+               sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate,
+                                    &sinfo->txrate);
                sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
        }
 
@@ -1932,12 +1973,12 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 
                if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) {
                        tidstats->filled |= BIT(NL80211_TID_STATS_RX_MSDU);
-                       tidstats->rx_msdu = sta->rx_msdu[i];
+                       tidstats->rx_msdu = sta->rx_stats.msdu[i];
                }
 
                if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU))) {
                        tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU);
-                       tidstats->tx_msdu = sta->tx_msdu[i];
+                       tidstats->tx_msdu = sta->tx_stats.msdu[i];
                }
 
                if (!(tidstats->filled &
@@ -1945,7 +1986,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                    ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
                        tidstats->filled |=
                                BIT(NL80211_TID_STATS_TX_MSDU_RETRIES);
-                       tidstats->tx_msdu_retries = sta->tx_msdu_retries[i];
+                       tidstats->tx_msdu_retries =
+                               sta->status_stats.msdu_retries[i];
                }
 
                if (!(tidstats->filled &
@@ -1953,7 +1995,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                    ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
                        tidstats->filled |=
                                BIT(NL80211_TID_STATS_TX_MSDU_FAILED);
-                       tidstats->tx_msdu_failed = sta->tx_msdu_failed[i];
+                       tidstats->tx_msdu_failed =
+                               sta->status_stats.msdu_failed[i];
                }
        }
 
index b087c71ff7fe4ef99ed3869cc2c7eecc174b95c0..2cafb21b422fd152119489d4b4797dbfd2d3051b 100644 (file)
@@ -133,6 +133,7 @@ enum ieee80211_agg_stop_reason {
  * @buf_size: reorder buffer size at receiver
  * @failed_bar_ssn: ssn of the last failed BAR tx attempt
  * @bar_pending: BAR needs to be re-sent
+ * @amsdu: support A-MSDU withing A-MDPU
  *
  * This structure's lifetime is managed by RCU, assignments to
  * the array holding it must hold the aggregation mutex.
@@ -158,6 +159,7 @@ struct tid_ampdu_tx {
 
        u16 failed_bar_ssn;
        bool bar_pending;
+       bool amsdu;
 };
 
 /**
@@ -342,12 +344,6 @@ DECLARE_EWMA(signal, 1024, 8)
  * @rate_ctrl_lock: spinlock used to protect rate control data
  *     (data inside the algorithm, so serializes calls there)
  * @rate_ctrl_priv: rate control private per-STA pointer
- * @last_tx_rate: rate used for last transmit, to report to userspace as
- *     "the" transmit rate
- * @last_rx_rate_idx: rx status rate index of the last data packet
- * @last_rx_rate_flag: rx status flag of the last data packet
- * @last_rx_rate_vht_flag: rx status vht flag of the last data packet
- * @last_rx_rate_vht_nss: rx status nss of last data packet
  * @lock: used for locking all fields that require locking, see comments
  *     in the header file.
  * @drv_deliver_wk: used for delivering frames after driver PS unblocking
@@ -362,23 +358,9 @@ DECLARE_EWMA(signal, 1024, 8)
  *     the station when it leaves powersave or polls for frames
  * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on
  * @txq_buffered_tids: bitmap of TIDs that mac80211 has txq data buffered on
- * @rx_packets: Number of MSDUs received from this STA
- * @rx_bytes: Number of bytes received from this STA
- * @last_rx: time (in jiffies) when last frame was received from this STA
  * @last_connected: time (in seconds) when a station got connected
- * @num_duplicates: number of duplicate frames received from this STA
- * @rx_fragments: number of received MPDUs
- * @rx_dropped: number of dropped MPDUs from this STA
- * @last_signal: signal of last received frame from this STA
- * @avg_signal: moving average of signal of received frames from this STA
- * @last_ack_signal: signal of last received Ack frame from this STA
  * @last_seq_ctrl: last received seq/frag number from this STA (per TID
  *     plus one for non-QoS frames)
- * @tx_filtered_count: number of frames the hardware filtered for this STA
- * @tx_retry_failed: number of frames that failed retry
- * @tx_retry_count: total number of retries for frames to this STA
- * @tx_packets: number of RX/TX MSDUs
- * @tx_bytes: number of bytes transmitted to this STA
  * @tid_seq: per-TID sequence numbers for sending to this STA
  * @ampdu_mlme: A-MPDU state machine state
  * @timer_to_tid: identity mapping to ID timers
@@ -386,32 +368,22 @@ DECLARE_EWMA(signal, 1024, 8)
  * @debugfs: debug filesystem info
  * @dead: set to true when sta is unlinked
  * @uploaded: set to true when sta is uploaded to the driver
- * @lost_packets: number of consecutive lost packets
  * @sta: station information we share with the driver
  * @sta_state: duplicates information about station state (for debug)
  * @beacon_loss_count: number of times beacon loss has triggered
  * @rcu_head: RCU head used for freeing this station struct
  * @cur_max_bandwidth: maximum bandwidth to use for TX to the station,
  *     taken from HT/VHT capabilities or VHT operating mode notification
- * @chains: chains ever used for RX from this station
- * @chain_signal_last: last signal (per chain)
- * @chain_signal_avg: signal average (per chain)
  * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
  *     AP only.
  * @cipher_scheme: optional cipher scheme for this station
- * @last_tdls_pkt_time: holds the time in jiffies of last TDLS pkt ACKed
  * @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED)
- * @tx_msdu: MSDUs transmitted to this station, using IEEE80211_NUM_TID
- *     entry for non-QoS frames
- * @tx_msdu_retries: MSDU retries for transmissions to to this station,
- *     using IEEE80211_NUM_TID entry for non-QoS frames
- * @tx_msdu_failed: MSDU failures for transmissions to to this station,
- *     using IEEE80211_NUM_TID entry for non-QoS frames
- * @rx_msdu: MSDUs received from this station, using IEEE80211_NUM_TID
- *     entry for non-QoS frames
  * @fast_tx: TX fastpath information
  * @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
  *     the BSS one.
+ * @tx_stats: TX statistics
+ * @rx_stats: RX statistics
+ * @status_stats: TX status statistics
  */
 struct sta_info {
        /* General information, mostly static */
@@ -455,42 +427,49 @@ struct sta_info {
        unsigned long driver_buffered_tids;
        unsigned long txq_buffered_tids;
 
-       /* Updated from RX path only, no locking requirements */
-       unsigned long rx_packets;
-       u64 rx_bytes;
-       unsigned long last_rx;
        long last_connected;
-       unsigned long num_duplicates;
-       unsigned long rx_fragments;
-       unsigned long rx_dropped;
-       int last_signal;
-       struct ewma_signal avg_signal;
-       int last_ack_signal;
 
-       u8 chains;
-       s8 chain_signal_last[IEEE80211_MAX_CHAINS];
-       struct ewma_signal chain_signal_avg[IEEE80211_MAX_CHAINS];
+       /* Updated from RX path only, no locking requirements */
+       struct {
+               unsigned long packets;
+               u64 bytes;
+               unsigned long last_rx;
+               unsigned long num_duplicates;
+               unsigned long fragments;
+               unsigned long dropped;
+               int last_signal;
+               struct ewma_signal avg_signal;
+               u8 chains;
+               s8 chain_signal_last[IEEE80211_MAX_CHAINS];
+               struct ewma_signal chain_signal_avg[IEEE80211_MAX_CHAINS];
+               int last_rate_idx;
+               u32 last_rate_flag;
+               u32 last_rate_vht_flag;
+               u8 last_rate_vht_nss;
+               u64 msdu[IEEE80211_NUM_TIDS + 1];
+       } rx_stats;
 
        /* Plus 1 for non-QoS frames */
        __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
 
        /* Updated from TX status path only, no locking requirements */
-       unsigned long tx_filtered_count;
-       unsigned long tx_retry_failed, tx_retry_count;
+       struct {
+               unsigned long filtered;
+               unsigned long retry_failed, retry_count;
+               unsigned int lost_packets;
+               unsigned long last_tdls_pkt_time;
+               u64 msdu_retries[IEEE80211_NUM_TIDS + 1];
+               u64 msdu_failed[IEEE80211_NUM_TIDS + 1];
+       } status_stats;
 
        /* Updated from TX path only, no locking requirements */
-       u64 tx_packets[IEEE80211_NUM_ACS];
-       u64 tx_bytes[IEEE80211_NUM_ACS];
-       struct ieee80211_tx_rate last_tx_rate;
-       int last_rx_rate_idx;
-       u32 last_rx_rate_flag;
-       u32 last_rx_rate_vht_flag;
-       u8 last_rx_rate_vht_nss;
+       struct {
+               u64 packets[IEEE80211_NUM_ACS];
+               u64 bytes[IEEE80211_NUM_ACS];
+               struct ieee80211_tx_rate last_rate;
+               u64 msdu[IEEE80211_NUM_TIDS + 1];
+       } tx_stats;
        u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
-       u64 tx_msdu[IEEE80211_NUM_TIDS + 1];
-       u64 tx_msdu_retries[IEEE80211_NUM_TIDS + 1];
-       u64 tx_msdu_failed[IEEE80211_NUM_TIDS + 1];
-       u64 rx_msdu[IEEE80211_NUM_TIDS + 1];
 
        /*
         * Aggregation information, locked with lock.
@@ -507,15 +486,9 @@ struct sta_info {
 
        enum ieee80211_sta_rx_bandwidth cur_max_bandwidth;
 
-       unsigned int lost_packets;
-       unsigned int beacon_loss_count;
-
        enum ieee80211_smps_mode known_smps_mode;
        const struct ieee80211_cipher_scheme *cipher_scheme;
 
-       /* TDLS timeout data */
-       unsigned long last_tdls_pkt_time;
-
        u8 reserved_tid;
 
        struct cfg80211_chan_def tdls_chandef;
@@ -686,8 +659,6 @@ static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata)
 void sta_set_rate_info_tx(struct sta_info *sta,
                          const struct ieee80211_tx_rate *rate,
                          struct rate_info *rinfo);
-void sta_set_rate_info_rx(struct sta_info *sta,
-                         struct rate_info *rinfo);
 void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo);
 
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
index 3ed7ddfbf8e840d4c910b0e9452c06843031c91d..5bad05e9af90fc76201f4bd9a54b931ffd5c61bc 100644 (file)
@@ -67,7 +67,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
                       IEEE80211_TX_INTFL_RETRANSMISSION;
        info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
 
-       sta->tx_filtered_count++;
+       sta->status_stats.filtered++;
 
        /*
         * Clear more-data bit on filtered frames, it might be set
@@ -183,7 +183,7 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
        struct ieee80211_sub_if_data *sdata = sta->sdata;
 
        if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
-               sta->last_rx = jiffies;
+               sta->rx_stats.last_rx = jiffies;
 
        if (ieee80211_is_data_qos(mgmt->frame_control)) {
                struct ieee80211_hdr *hdr = (void *) skb->data;
@@ -557,8 +557,9 @@ static void ieee80211_lost_packet(struct sta_info *sta,
            !(info->flags & IEEE80211_TX_STAT_AMPDU))
                return;
 
-       sta->lost_packets++;
-       if (!sta->sta.tdls && sta->lost_packets < STA_LOST_PKT_THRESHOLD)
+       sta->status_stats.lost_packets++;
+       if (!sta->sta.tdls &&
+           sta->status_stats.lost_packets < STA_LOST_PKT_THRESHOLD)
                return;
 
        /*
@@ -568,14 +569,15 @@ static void ieee80211_lost_packet(struct sta_info *sta,
         * mechanism.
         */
        if (sta->sta.tdls &&
-           (sta->lost_packets < STA_LOST_TDLS_PKT_THRESHOLD ||
+           (sta->status_stats.lost_packets < STA_LOST_TDLS_PKT_THRESHOLD ||
             time_before(jiffies,
-                        sta->last_tdls_pkt_time + STA_LOST_TDLS_PKT_TIME)))
+                        sta->status_stats.last_tdls_pkt_time +
+                        STA_LOST_TDLS_PKT_TIME)))
                return;
 
        cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr,
-                                   sta->lost_packets, GFP_ATOMIC);
-       sta->lost_packets = 0;
+                                   sta->status_stats.lost_packets, GFP_ATOMIC);
+       sta->status_stats.lost_packets = 0;
 }
 
 static int ieee80211_tx_get_rates(struct ieee80211_hw *hw,
@@ -636,18 +638,18 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
                sta = container_of(pubsta, struct sta_info, sta);
 
                if (!acked)
-                       sta->tx_retry_failed++;
-               sta->tx_retry_count += retry_count;
+                       sta->status_stats.retry_failed++;
+               sta->status_stats.retry_count += retry_count;
 
                if (acked) {
-                       sta->last_rx = jiffies;
+                       sta->rx_stats.last_rx = jiffies;
 
-                       if (sta->lost_packets)
-                               sta->lost_packets = 0;
+                       if (sta->status_stats.lost_packets)
+                               sta->status_stats.lost_packets = 0;
 
                        /* Track when last TDLS packet was ACKed */
                        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
-                               sta->last_tdls_pkt_time = jiffies;
+                               sta->status_stats.last_tdls_pkt_time = jiffies;
                } else {
                        ieee80211_lost_packet(sta, info);
                }
@@ -669,16 +671,70 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(ieee80211_tx_status_noskb);
 
-void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,
+                         struct ieee80211_supported_band *sband,
+                         int retry_count, int shift, bool send_to_cooked)
 {
        struct sk_buff *skb2;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_sub_if_data *sdata;
+       struct net_device *prev_dev = NULL;
+       int rtap_len;
+
+       /* send frame to monitor interfaces now */
+       rtap_len = ieee80211_tx_radiotap_len(info);
+       if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) {
+               pr_err("ieee80211_tx_status: headroom too small\n");
+               dev_kfree_skb(skb);
+               return;
+       }
+       ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count,
+                                        rtap_len, shift);
+
+       /* XXX: is this sufficient for BPF? */
+       skb_set_mac_header(skb, 0);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       skb->pkt_type = PACKET_OTHERHOST;
+       skb->protocol = htons(ETH_P_802_2);
+       memset(skb->cb, 0, sizeof(skb->cb));
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+               if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
+                       if (!ieee80211_sdata_running(sdata))
+                               continue;
+
+                       if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) &&
+                           !send_to_cooked)
+                               continue;
+
+                       if (prev_dev) {
+                               skb2 = skb_clone(skb, GFP_ATOMIC);
+                               if (skb2) {
+                                       skb2->dev = prev_dev;
+                                       netif_rx(skb2);
+                               }
+                       }
+
+                       prev_dev = sdata->dev;
+               }
+       }
+       if (prev_dev) {
+               skb->dev = prev_dev;
+               netif_rx(skb);
+               skb = NULL;
+       }
+       rcu_read_unlock();
+       dev_kfree_skb(skb);
+}
+
+void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        __le16 fc;
        struct ieee80211_supported_band *sband;
-       struct ieee80211_sub_if_data *sdata;
-       struct net_device *prev_dev = NULL;
        struct sta_info *sta;
        struct rhash_head *tmp;
        int retry_count;
@@ -686,7 +742,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        bool send_to_cooked;
        bool acked;
        struct ieee80211_bar *bar;
-       int rtap_len;
        int shift = 0;
        int tid = IEEE80211_NUM_TIDS;
        const struct bucket_table *tbl;
@@ -731,7 +786,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL) &&
                    (ieee80211_is_data(hdr->frame_control)) &&
                    (rates_idx != -1))
-                       sta->last_tx_rate = info->status.rates[rates_idx];
+                       sta->tx_stats.last_rate =
+                               info->status.rates[rates_idx];
 
                if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
                    (ieee80211_is_data_qos(fc))) {
@@ -777,13 +833,15 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                        return;
                } else {
                        if (!acked)
-                               sta->tx_retry_failed++;
-                       sta->tx_retry_count += retry_count;
+                               sta->status_stats.retry_failed++;
+                       sta->status_stats.retry_count += retry_count;
 
                        if (ieee80211_is_data_present(fc)) {
                                if (!acked)
-                                       sta->tx_msdu_failed[tid]++;
-                               sta->tx_msdu_retries[tid] += retry_count;
+                                       sta->status_stats.msdu_failed[tid]++;
+
+                               sta->status_stats.msdu_retries[tid] +=
+                                       retry_count;
                        }
                }
 
@@ -801,19 +859,17 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 
                if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
                        if (info->flags & IEEE80211_TX_STAT_ACK) {
-                               if (sta->lost_packets)
-                                       sta->lost_packets = 0;
+                               if (sta->status_stats.lost_packets)
+                                       sta->status_stats.lost_packets = 0;
 
                                /* Track when last TDLS packet was ACKed */
                                if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
-                                       sta->last_tdls_pkt_time = jiffies;
+                                       sta->status_stats.last_tdls_pkt_time =
+                                               jiffies;
                        } else {
                                ieee80211_lost_packet(sta, info);
                        }
                }
-
-               if (acked)
-                       sta->last_ack_signal = info->status.ack_signal;
        }
 
        rcu_read_unlock();
@@ -879,51 +935,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                return;
        }
 
-       /* send frame to monitor interfaces now */
-       rtap_len = ieee80211_tx_radiotap_len(info);
-       if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) {
-               pr_err("ieee80211_tx_status: headroom too small\n");
-               dev_kfree_skb(skb);
-               return;
-       }
-       ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count,
-                                        rtap_len, shift);
-
-       /* XXX: is this sufficient for BPF? */
-       skb_set_mac_header(skb, 0);
-       skb->ip_summed = CHECKSUM_UNNECESSARY;
-       skb->pkt_type = PACKET_OTHERHOST;
-       skb->protocol = htons(ETH_P_802_2);
-       memset(skb->cb, 0, sizeof(skb->cb));
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
-                       if (!ieee80211_sdata_running(sdata))
-                               continue;
-
-                       if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) &&
-                           !send_to_cooked)
-                               continue;
-
-                       if (prev_dev) {
-                               skb2 = skb_clone(skb, GFP_ATOMIC);
-                               if (skb2) {
-                                       skb2->dev = prev_dev;
-                                       netif_rx(skb2);
-                               }
-                       }
-
-                       prev_dev = sdata->dev;
-               }
-       }
-       if (prev_dev) {
-               skb->dev = prev_dev;
-               netif_rx(skb);
-               skb = NULL;
-       }
-       rcu_read_unlock();
-       dev_kfree_skb(skb);
+       /* send to monitor interfaces */
+       ieee80211_tx_monitor(local, skb, sband, retry_count, shift, send_to_cooked);
 }
 EXPORT_SYMBOL(ieee80211_tx_status);
 
index 4e202d0679b26a0dfa0b89c5ae189f2ce7b56883..c9eeb3f1280852b6ec1c44eb82755390cf959629 100644 (file)
@@ -41,9 +41,11 @@ static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata,
                                         struct sk_buff *skb)
 {
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        bool chan_switch = local->hw.wiphy->features &
                           NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
-       bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW);
+       bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
+                         !ifmgd->tdls_wider_bw_prohibited;
        enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
        bool vht = sband && sband->vht_cap.vht_supported;
@@ -331,8 +333,8 @@ ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata,
 
        /* proceed to downgrade the chandef until usable or the same */
        while (uc.width > max_width &&
-              !cfg80211_reg_can_beacon(sdata->local->hw.wiphy,
-                                       &uc, sdata->wdev.iftype))
+              !cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &uc,
+                                             sdata->wdev.iftype))
                ieee80211_chandef_downgrade(&uc);
 
        if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) {
@@ -589,12 +591,19 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
                offset = noffset;
        }
 
-       /* if HT support is only added in TDLS, we need an HT-operation IE */
+       /*
+        * if HT support is only added in TDLS, we need an HT-operation IE.
+        * add the IE as required by IEEE802.11-2012 9.23.3.2.
+        */
        if (!ap_sta->sta.ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
+               u16 prot = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED |
+                          IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT |
+                          IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT;
+
                pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
-               /* send an empty HT operation IE */
                ieee80211_ie_build_ht_oper(pos, &sta->sta.ht_cap,
-                                          &sdata->vif.bss_conf.chandef, 0);
+                                          &sdata->vif.bss_conf.chandef, prot,
+                                          true);
        }
 
        ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
index 6f14591d8ca9eb7a356f54cf0a16e96f87480246..56c6d6cfa5a1a211818ef2b472d0900207fc0b6f 100644 (file)
                        __field(u32, chan_width)                                        \
                        __field(u32, center_freq1)                                      \
                        __field(u32, center_freq2)
-#define CHANDEF_ASSIGN(c)                                                              \
-                       __entry->control_freq = (c)->chan ? (c)->chan->center_freq : 0; \
-                       __entry->chan_width = (c)->width;                               \
-                       __entry->center_freq1 = (c)->center_freq1;                      \
-                       __entry->center_freq2 = (c)->center_freq2;
+#define CHANDEF_ASSIGN(c)                                                      \
+                       __entry->control_freq = (c) ? ((c)->chan ? (c)->chan->center_freq : 0) : 0;     \
+                       __entry->chan_width = (c) ? (c)->width : 0;                     \
+                       __entry->center_freq1 = (c) ? (c)->center_freq1 : 0;            \
+                       __entry->center_freq2 = (c) ? (c)->center_freq2 : 0;
 #define CHANDEF_PR_FMT " control:%d MHz width:%d center: %d/%d MHz"
 #define CHANDEF_PR_ARG __entry->control_freq, __entry->chan_width,                     \
                        __entry->center_freq1, __entry->center_freq2
@@ -325,7 +325,6 @@ TRACE_EVENT(drv_config,
                __field(u32, flags)
                __field(int, power_level)
                __field(int, dynamic_ps_timeout)
-               __field(int, max_sleep_period)
                __field(u16, listen_interval)
                __field(u8, long_frame_max_tx_count)
                __field(u8, short_frame_max_tx_count)
@@ -339,7 +338,6 @@ TRACE_EVENT(drv_config,
                __entry->flags = local->hw.conf.flags;
                __entry->power_level = local->hw.conf.power_level;
                __entry->dynamic_ps_timeout = local->hw.conf.dynamic_ps_timeout;
-               __entry->max_sleep_period = local->hw.conf.max_sleep_period;
                __entry->listen_interval = local->hw.conf.listen_interval;
                __entry->long_frame_max_tx_count =
                        local->hw.conf.long_frame_max_tx_count;
@@ -497,6 +495,36 @@ TRACE_EVENT(drv_configure_filter,
        )
 );
 
+TRACE_EVENT(drv_config_iface_filter,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                unsigned int filter_flags,
+                unsigned int changed_flags),
+
+       TP_ARGS(local, sdata, filter_flags, changed_flags),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               __field(unsigned int, filter_flags)
+               __field(unsigned int, changed_flags)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               __entry->filter_flags = filter_flags;
+               __entry->changed_flags = changed_flags;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT VIF_PR_FMT
+               " filter_flags: %#x changed_flags: %#x",
+               LOCAL_PR_ARG, VIF_PR_ARG, __entry->filter_flags,
+               __entry->changed_flags
+       )
+);
+
 TRACE_EVENT(drv_set_tim,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sta *sta, bool set),
@@ -944,9 +972,9 @@ TRACE_EVENT(drv_ampdu_action,
                 struct ieee80211_sub_if_data *sdata,
                 enum ieee80211_ampdu_mlme_action action,
                 struct ieee80211_sta *sta, u16 tid,
-                u16 *ssn, u8 buf_size),
+                u16 *ssn, u8 buf_size, bool amsdu),
 
-       TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size),
+       TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size, amsdu),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
@@ -955,6 +983,7 @@ TRACE_EVENT(drv_ampdu_action,
                __field(u16, tid)
                __field(u16, ssn)
                __field(u8, buf_size)
+               __field(bool, amsdu)
                VIF_ENTRY
        ),
 
@@ -966,12 +995,13 @@ TRACE_EVENT(drv_ampdu_action,
                __entry->tid = tid;
                __entry->ssn = ssn ? *ssn : 0;
                __entry->buf_size = buf_size;
+               __entry->amsdu = amsdu;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d",
+               LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d amsdu:%d",
                LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action,
-               __entry->tid, __entry->buf_size
+               __entry->tid, __entry->buf_size, __entry->amsdu
        )
 );
 
index 7892eb8ed4c8b1fa416ebcb297fab9f1cce91743..bdc224d5053ae3478a01565754ec6942c2d197a6 100644 (file)
@@ -757,9 +757,9 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
        if (txrc.reported_rate.idx < 0) {
                txrc.reported_rate = tx->rate;
                if (tx->sta && ieee80211_is_data(hdr->frame_control))
-                       tx->sta->last_tx_rate = txrc.reported_rate;
+                       tx->sta->tx_stats.last_rate = txrc.reported_rate;
        } else if (tx->sta)
-               tx->sta->last_tx_rate = txrc.reported_rate;
+               tx->sta->tx_stats.last_rate = txrc.reported_rate;
 
        if (ratetbl)
                return TX_CONTINUE;
@@ -824,7 +824,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
                hdr->seq_ctrl = cpu_to_le16(tx->sdata->sequence_number);
                tx->sdata->sequence_number += 0x10;
                if (tx->sta)
-                       tx->sta->tx_msdu[IEEE80211_NUM_TIDS]++;
+                       tx->sta->tx_stats.msdu[IEEE80211_NUM_TIDS]++;
                return TX_CONTINUE;
        }
 
@@ -840,7 +840,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
 
        qc = ieee80211_get_qos_ctl(hdr);
        tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
-       tx->sta->tx_msdu[tid]++;
+       tx->sta->tx_stats.msdu[tid]++;
 
        if (!tx->sta->sta.txq[0])
                hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
@@ -994,10 +994,10 @@ ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
 
        skb_queue_walk(&tx->skbs, skb) {
                ac = skb_get_queue_mapping(skb);
-               tx->sta->tx_bytes[ac] += skb->len;
+               tx->sta->tx_stats.bytes[ac] += skb->len;
        }
        if (ac >= 0)
-               tx->sta->tx_packets[ac]++;
+               tx->sta->tx_stats.packets[ac]++;
 
        return TX_CONTINUE;
 }
@@ -2770,7 +2770,8 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
 
        if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
                *ieee80211_get_qos_ctl(hdr) = tid;
-               hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
+               if (!sta->sta.txq[0])
+                       hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
        } else {
                info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
                hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number);
@@ -2778,10 +2779,10 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
        }
 
        if (skb_shinfo(skb)->gso_size)
-               sta->tx_msdu[tid] +=
+               sta->tx_stats.msdu[tid] +=
                        DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
        else
-               sta->tx_msdu[tid]++;
+               sta->tx_stats.msdu[tid]++;
 
        info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
 
@@ -2812,8 +2813,8 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
        /* statistics normally done by ieee80211_tx_h_stats (but that
         * has to consider fragmentation, so is more complex)
         */
-       sta->tx_bytes[skb_get_queue_mapping(skb)] += skb->len;
-       sta->tx_packets[skb_get_queue_mapping(skb)]++;
+       sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
+       sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
 
        if (fast_tx->pn_offs) {
                u64 pn;
@@ -3515,6 +3516,12 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
 {
        struct ieee80211_mutable_offsets offs = {};
        struct sk_buff *bcn = __ieee80211_beacon_get(hw, vif, &offs, false);
+       struct sk_buff *copy;
+       struct ieee80211_supported_band *sband;
+       int shift;
+
+       if (!bcn)
+               return bcn;
 
        if (tim_offset)
                *tim_offset = offs.tim_offset;
@@ -3522,6 +3529,19 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
        if (tim_length)
                *tim_length = offs.tim_length;
 
+       if (ieee80211_hw_check(hw, BEACON_TX_STATUS) ||
+           !hw_to_local(hw)->monitors)
+               return bcn;
+
+       /* send a copy to monitor interfaces */
+       copy = skb_copy(bcn, GFP_ATOMIC);
+       if (!copy)
+               return bcn;
+
+       shift = ieee80211_vif_get_shift(vif);
+       sband = hw->wiphy->bands[ieee80211_get_sdata_band(vif_to_sdata(vif))];
+       ieee80211_tx_monitor(hw_to_local(hw), copy, sband, 1, shift, false);
+
        return bcn;
 }
 EXPORT_SYMBOL(ieee80211_beacon_get_tim);
index 1104421bc525598aae491c587f29eb4b385fc220..74058020b7d6154c1ea1613e9a8a9775789736fa 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2007      Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright (C) 2015  Intel Deutschland GmbH
  *
  * 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
@@ -1104,13 +1105,13 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
 }
 
 void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
-                              bool bss_notify)
+                              bool bss_notify, bool enable_qos)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_tx_queue_params qparam;
        struct ieee80211_chanctx_conf *chanctx_conf;
        int ac;
-       bool use_11b, enable_qos;
+       bool use_11b;
        bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */
        int aCWmin, aCWmax;
 
@@ -1129,13 +1130,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
                 !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
        rcu_read_unlock();
 
-       /*
-        * By default disable QoS in STA mode for old access points, which do
-        * not support 802.11e. New APs will provide proper queue parameters,
-        * that we will configure later.
-        */
-       enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
-
        is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);
 
        /* Set defaults according to 802.11-2007 Table 7-37 */
@@ -1664,7 +1658,6 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
 
        local->resuming = false;
        local->suspended = false;
-       local->started = false;
        local->in_reconfig = false;
 
        /* scheduled scan clearly can't be running any more, but tell
@@ -1753,6 +1746,18 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        }
 #endif
 
+       /*
+        * In case of hw_restart during suspend (without wowlan),
+        * cancel restart work, as we are reconfiguring the device
+        * anyway.
+        * Note that restart_work is scheduled on a frozen workqueue,
+        * so we can't deadlock in this case.
+        */
+       if (suspended && local->in_reconfig && !reconfig_due_to_wowlan)
+               cancel_work_sync(&local->restart_work);
+
+       local->started = false;
+
        /*
         * Upon resume hardware can sometimes be goofy due to
         * various platform / driver / bus issues, so restarting
@@ -1951,7 +1956,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                }
        }
 
-       ieee80211_recalc_ps(local, -1);
+       ieee80211_recalc_ps(local);
 
        /*
         * The sta might be in psm against the ap (e.g. because
@@ -1966,7 +1971,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                        if (!sdata->u.mgd.associated)
                                continue;
 
-                       ieee80211_send_nullfunc(local, sdata, 0);
+                       ieee80211_send_nullfunc(local, sdata, false);
                }
        }
 
@@ -1996,6 +2001,29 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                if (ieee80211_sdata_running(sdata))
                        ieee80211_enable_keys(sdata);
 
+       /* Reconfigure sched scan if it was interrupted by FW restart */
+       mutex_lock(&local->mtx);
+       sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
+                                               lockdep_is_held(&local->mtx));
+       sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
+                                               lockdep_is_held(&local->mtx));
+       if (sched_scan_sdata && sched_scan_req)
+               /*
+                * Sched scan stopped, but we don't want to report it. Instead,
+                * we're trying to reschedule. However, if more than one scan
+                * plan was set, we cannot reschedule since we don't know which
+                * scan plan was currently running (and some scan plans may have
+                * already finished).
+                */
+               if (sched_scan_req->n_scan_plans > 1 ||
+                   __ieee80211_request_sched_scan_start(sched_scan_sdata,
+                                                        sched_scan_req))
+                       sched_scan_stopped = true;
+       mutex_unlock(&local->mtx);
+
+       if (sched_scan_stopped)
+               cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy);
+
  wake_up:
        local->in_reconfig = false;
        barrier();
@@ -2017,8 +2045,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                mutex_lock(&local->sta_mtx);
 
                list_for_each_entry(sta, &local->sta_list, list) {
-                       ieee80211_sta_tear_down_BA_sessions(
-                                       sta, AGG_STOP_LOCAL_REQUEST);
+                       if (!local->resuming)
+                               ieee80211_sta_tear_down_BA_sessions(
+                                               sta, AGG_STOP_LOCAL_REQUEST);
                        clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
                }
 
@@ -2029,28 +2058,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                                        IEEE80211_QUEUE_STOP_REASON_SUSPEND,
                                        false);
 
-       /*
-        * Reconfigure sched scan if it was interrupted by FW restart or
-        * suspend.
-        */
-       mutex_lock(&local->mtx);
-       sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
-                                               lockdep_is_held(&local->mtx));
-       sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
-                                               lockdep_is_held(&local->mtx));
-       if (sched_scan_sdata && sched_scan_req)
-               /*
-                * Sched scan stopped, but we don't want to report it. Instead,
-                * we're trying to reschedule.
-                */
-               if (__ieee80211_request_sched_scan_start(sched_scan_sdata,
-                                                        sched_scan_req))
-                       sched_scan_stopped = true;
-       mutex_unlock(&local->mtx);
-
-       if (sched_scan_stopped)
-               cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy);
-
        /*
         * If this is for hw restart things are still running.
         * We may want to change that later, however.
@@ -2135,7 +2142,13 @@ void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)
        chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
                                        lockdep_is_held(&local->chanctx_mtx));
 
-       if (WARN_ON_ONCE(!chanctx_conf))
+       /*
+        * This function can be called from a work, thus it may be possible
+        * that the chanctx_conf is removed (due to a disconnection, for
+        * example).
+        * So nothing should be done in such case.
+        */
+       if (!chanctx_conf)
                goto unlock;
 
        chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
@@ -2272,7 +2285,7 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
 
 u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
                               const struct cfg80211_chan_def *chandef,
-                              u16 prot_mode)
+                              u16 prot_mode, bool rifs_mode)
 {
        struct ieee80211_ht_operation *ht_oper;
        /* Build HT Information */
@@ -2300,6 +2313,9 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
            chandef->width != NL80211_CHAN_WIDTH_20)
                ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
 
+       if (rifs_mode)
+               ht_oper->ht_param |= IEEE80211_HT_PARAM_RIFS_MODE;
+
        ht_oper->operation_mode = cpu_to_le16(prot_mode);
        ht_oper->stbc_param = 0x0000;
 
@@ -2324,6 +2340,8 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
        if (chandef->center_freq2)
                vht_oper->center_freq_seg2_idx =
                        ieee80211_frequency_to_channel(chandef->center_freq2);
+       else
+               vht_oper->center_freq_seg2_idx = 0x00;
 
        switch (chandef->width) {
        case NL80211_CHAN_WIDTH_160:
@@ -2541,7 +2559,7 @@ int ieee80211_ave_rssi(struct ieee80211_vif *vif)
                /* non-managed type inferfaces */
                return 0;
        }
-       return ifmgd->ave_beacon_signal / 16;
+       return -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal);
 }
 EXPORT_SYMBOL_GPL(ieee80211_ave_rssi);
 
@@ -2951,6 +2969,13 @@ ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i)
        if (end > 0)
                return false;
 
+       /* One shot NOA  */
+       if (data->count[i] == 1)
+               return false;
+
+       if (data->desc[i].interval == 0)
+               return false;
+
        /* End time is in the past, check for repetitions */
        skip = DIV_ROUND_UP(-end, data->desc[i].interval);
        if (data->count[i] < 255) {
@@ -3298,9 +3323,11 @@ void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata,
        if (sta) {
                txqi->txq.sta = &sta->sta;
                sta->sta.txq[tid] = &txqi->txq;
+               txqi->txq.tid = tid;
                txqi->txq.ac = ieee802_1d_to_ac[tid & 7];
        } else {
                sdata->vif.txq = &txqi->txq;
+               txqi->txq.tid = 0;
                txqi->txq.ac = IEEE80211_AC_BE;
        }
 }
index feb547dc8643ab286fa0c64f19b01e2b4766898e..d824c38971ed53ff702669c9bce0f4a86495bdd0 100644 (file)
@@ -174,9 +174,12 @@ mic_fail_no_key:
         * a driver that supports HW encryption. Send up the key idx only if
         * the key is set.
         */
-       mac80211_ev_michael_mic_failure(rx->sdata,
-                                       rx->key ? rx->key->conf.keyidx : -1,
-                                       (void *) skb->data, NULL, GFP_ATOMIC);
+       cfg80211_michael_mic_failure(rx->sdata->dev, hdr->addr2,
+                                    is_multicast_ether_addr(hdr->addr1) ?
+                                    NL80211_KEYTYPE_GROUP :
+                                    NL80211_KEYTYPE_PAIRWISE,
+                                    rx->key ? rx->key->conf.keyidx : -1,
+                                    NULL, GFP_ATOMIC);
        return RX_DROP_UNUSABLE;
 }
 
index c865ebb2ace2b0b74cf3ba994c085790cde83da6..57b5e94471aff87b2581620c4d254163d2dcdea4 100644 (file)
@@ -266,6 +266,195 @@ ieee802154_set_ackreq_default(struct wpan_phy *wpan_phy,
        return 0;
 }
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+static void
+ieee802154_get_llsec_table(struct wpan_phy *wpan_phy,
+                          struct wpan_dev *wpan_dev,
+                          struct ieee802154_llsec_table **table)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+       *table = &sdata->sec.table;
+}
+
+static void
+ieee802154_lock_llsec_table(struct wpan_phy *wpan_phy,
+                           struct wpan_dev *wpan_dev)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+       mutex_lock(&sdata->sec_mtx);
+}
+
+static void
+ieee802154_unlock_llsec_table(struct wpan_phy *wpan_phy,
+                             struct wpan_dev *wpan_dev)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+       mutex_unlock(&sdata->sec_mtx);
+}
+
+static int
+ieee802154_set_llsec_params(struct wpan_phy *wpan_phy,
+                           struct wpan_dev *wpan_dev,
+                           const struct ieee802154_llsec_params *params,
+                           int changed)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       int res;
+
+       mutex_lock(&sdata->sec_mtx);
+       res = mac802154_llsec_set_params(&sdata->sec, params, changed);
+       mutex_unlock(&sdata->sec_mtx);
+
+       return res;
+}
+
+static int
+ieee802154_get_llsec_params(struct wpan_phy *wpan_phy,
+                           struct wpan_dev *wpan_dev,
+                           struct ieee802154_llsec_params *params)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       int res;
+
+       mutex_lock(&sdata->sec_mtx);
+       res = mac802154_llsec_get_params(&sdata->sec, params);
+       mutex_unlock(&sdata->sec_mtx);
+
+       return res;
+}
+
+static int
+ieee802154_add_llsec_key(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+                        const struct ieee802154_llsec_key_id *id,
+                        const struct ieee802154_llsec_key *key)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       int res;
+
+       mutex_lock(&sdata->sec_mtx);
+       res = mac802154_llsec_key_add(&sdata->sec, id, key);
+       mutex_unlock(&sdata->sec_mtx);
+
+       return res;
+}
+
+static int
+ieee802154_del_llsec_key(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+                        const struct ieee802154_llsec_key_id *id)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       int res;
+
+       mutex_lock(&sdata->sec_mtx);
+       res = mac802154_llsec_key_del(&sdata->sec, id);
+       mutex_unlock(&sdata->sec_mtx);
+
+       return res;
+}
+
+static int
+ieee802154_add_seclevel(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+                       const struct ieee802154_llsec_seclevel *sl)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       int res;
+
+       mutex_lock(&sdata->sec_mtx);
+       res = mac802154_llsec_seclevel_add(&sdata->sec, sl);
+       mutex_unlock(&sdata->sec_mtx);
+
+       return res;
+}
+
+static int
+ieee802154_del_seclevel(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+                       const struct ieee802154_llsec_seclevel *sl)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       int res;
+
+       mutex_lock(&sdata->sec_mtx);
+       res = mac802154_llsec_seclevel_del(&sdata->sec, sl);
+       mutex_unlock(&sdata->sec_mtx);
+
+       return res;
+}
+
+static int
+ieee802154_add_device(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+                     const struct ieee802154_llsec_device *dev_desc)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       int res;
+
+       mutex_lock(&sdata->sec_mtx);
+       res = mac802154_llsec_dev_add(&sdata->sec, dev_desc);
+       mutex_unlock(&sdata->sec_mtx);
+
+       return res;
+}
+
+static int
+ieee802154_del_device(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+                     __le64 extended_addr)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       int res;
+
+       mutex_lock(&sdata->sec_mtx);
+       res = mac802154_llsec_dev_del(&sdata->sec, extended_addr);
+       mutex_unlock(&sdata->sec_mtx);
+
+       return res;
+}
+
+static int
+ieee802154_add_devkey(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+                     __le64 extended_addr,
+                     const struct ieee802154_llsec_device_key *key)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       int res;
+
+       mutex_lock(&sdata->sec_mtx);
+       res = mac802154_llsec_devkey_add(&sdata->sec, extended_addr, key);
+       mutex_unlock(&sdata->sec_mtx);
+
+       return res;
+}
+
+static int
+ieee802154_del_devkey(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+                     __le64 extended_addr,
+                     const struct ieee802154_llsec_device_key *key)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       int res;
+
+       mutex_lock(&sdata->sec_mtx);
+       res = mac802154_llsec_devkey_del(&sdata->sec, extended_addr, key);
+       mutex_unlock(&sdata->sec_mtx);
+
+       return res;
+}
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 const struct cfg802154_ops mac802154_config_ops = {
        .add_virtual_intf_deprecated = ieee802154_add_iface_deprecated,
        .del_virtual_intf_deprecated = ieee802154_del_iface_deprecated,
@@ -284,4 +473,20 @@ const struct cfg802154_ops mac802154_config_ops = {
        .set_max_frame_retries = ieee802154_set_max_frame_retries,
        .set_lbt_mode = ieee802154_set_lbt_mode,
        .set_ackreq_default = ieee802154_set_ackreq_default,
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+       .get_llsec_table = ieee802154_get_llsec_table,
+       .lock_llsec_table = ieee802154_lock_llsec_table,
+       .unlock_llsec_table = ieee802154_unlock_llsec_table,
+       /* TODO above */
+       .set_llsec_params = ieee802154_set_llsec_params,
+       .get_llsec_params = ieee802154_get_llsec_params,
+       .add_llsec_key = ieee802154_add_llsec_key,
+       .del_llsec_key = ieee802154_del_llsec_key,
+       .add_seclevel = ieee802154_add_seclevel,
+       .del_seclevel = ieee802154_del_seclevel,
+       .add_device = ieee802154_add_device,
+       .del_device = ieee802154_del_device,
+       .add_devkey = ieee802154_add_devkey,
+       .del_devkey = ieee802154_del_devkey,
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
 };
index ed26952f9e143407723dca1ef45d8400424aca3e..7079cd32a7ad201265cf730027dda07254e622aa 100644 (file)
@@ -367,12 +367,11 @@ static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata,
        return 0;
 }
 
-static int mac802154_header_create(struct sk_buff *skb,
-                                  struct net_device *dev,
-                                  unsigned short type,
-                                  const void *daddr,
-                                  const void *saddr,
-                                  unsigned len)
+static int ieee802154_header_create(struct sk_buff *skb,
+                                   struct net_device *dev,
+                                   const struct ieee802154_addr *daddr,
+                                   const struct ieee802154_addr *saddr,
+                                   unsigned len)
 {
        struct ieee802154_hdr hdr;
        struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
@@ -423,24 +422,89 @@ static int mac802154_header_create(struct sk_buff *skb,
        return hlen;
 }
 
+static const struct wpan_dev_header_ops ieee802154_header_ops = {
+       .create         = ieee802154_header_create,
+};
+
+/* This header create functionality assumes a 8 byte array for
+ * source and destination pointer at maximum. To adapt this for
+ * the 802.15.4 dataframe header we use extended address handling
+ * here only and intra pan connection. fc fields are mostly fallback
+ * handling. For provide dev_hard_header for dgram sockets.
+ */
+static int mac802154_header_create(struct sk_buff *skb,
+                                  struct net_device *dev,
+                                  unsigned short type,
+                                  const void *daddr,
+                                  const void *saddr,
+                                  unsigned len)
+{
+       struct ieee802154_hdr hdr;
+       struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+       struct ieee802154_mac_cb cb = { };
+       int hlen;
+
+       if (!daddr)
+               return -EINVAL;
+
+       memset(&hdr.fc, 0, sizeof(hdr.fc));
+       hdr.fc.type = IEEE802154_FC_TYPE_DATA;
+       hdr.fc.ack_request = wpan_dev->ackreq;
+       hdr.seq = atomic_inc_return(&dev->ieee802154_ptr->dsn) & 0xFF;
+
+       /* TODO currently a workaround to give zero cb block to set
+        * security parameters defaults according MIB.
+        */
+       if (mac802154_set_header_security(sdata, &hdr, &cb) < 0)
+               return -EINVAL;
+
+       hdr.dest.pan_id = wpan_dev->pan_id;
+       hdr.dest.mode = IEEE802154_ADDR_LONG;
+       ieee802154_be64_to_le64(&hdr.dest.extended_addr, daddr);
+
+       hdr.source.pan_id = hdr.dest.pan_id;
+       hdr.source.mode = IEEE802154_ADDR_LONG;
+
+       if (!saddr)
+               hdr.source.extended_addr = wpan_dev->extended_addr;
+       else
+               ieee802154_be64_to_le64(&hdr.source.extended_addr, saddr);
+
+       hlen = ieee802154_hdr_push(skb, &hdr);
+       if (hlen < 0)
+               return -EINVAL;
+
+       skb_reset_mac_header(skb);
+       skb->mac_len = hlen;
+
+       if (len > ieee802154_max_payload(&hdr))
+               return -EMSGSIZE;
+
+       return hlen;
+}
+
 static int
 mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
 {
        struct ieee802154_hdr hdr;
-       struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
 
        if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
                pr_debug("malformed packet\n");
                return 0;
        }
 
-       *addr = hdr.source;
-       return sizeof(*addr);
+       if (hdr.source.mode == IEEE802154_ADDR_LONG) {
+               ieee802154_le64_to_be64(haddr, &hdr.source.extended_addr);
+               return IEEE802154_EXTENDED_ADDR_LEN;
+       }
+
+       return 0;
 }
 
-static struct header_ops mac802154_header_ops = {
-       .create         = mac802154_header_create,
-       .parse          = mac802154_header_parse,
+static const struct header_ops mac802154_header_ops = {
+       .create         = mac802154_header_create,
+       .parse          = mac802154_header_parse,
 };
 
 static const struct net_device_ops mac802154_wpan_ops = {
@@ -471,9 +535,29 @@ static void ieee802154_if_setup(struct net_device *dev)
        dev->addr_len           = IEEE802154_EXTENDED_ADDR_LEN;
        memset(dev->broadcast, 0xff, IEEE802154_EXTENDED_ADDR_LEN);
 
-       dev->hard_header_len    = MAC802154_FRAME_HARD_HEADER_LEN;
-       dev->needed_tailroom    = 2 + 16; /* FCS + MIC */
-       dev->mtu                = IEEE802154_MTU;
+       /* Let hard_header_len set to IEEE802154_MIN_HEADER_LEN. AF_PACKET
+        * will not send frames without any payload, but ack frames
+        * has no payload, so substract one that we can send a 3 bytes
+        * frame. The xmit callback assumes at least a hard header where two
+        * bytes fc and sequence field are set.
+        */
+       dev->hard_header_len    = IEEE802154_MIN_HEADER_LEN - 1;
+       /* The auth_tag header is for security and places in private payload
+        * room of mac frame which stucks between payload and FCS field.
+        */
+       dev->needed_tailroom    = IEEE802154_MAX_AUTH_TAG_LEN +
+                                 IEEE802154_FCS_LEN;
+       /* The mtu size is the payload without mac header in this case.
+        * We have a dynamic length header with a minimum header length
+        * which is hard_header_len. In this case we let mtu to the size
+        * of maximum payload which is IEEE802154_MTU - IEEE802154_FCS_LEN -
+        * hard_header_len. The FCS which is set by hardware or ndo_start_xmit
+        * and the minimum mac header which can be evaluated inside driver
+        * layer. The rest of mac header will be part of payload if greater
+        * than hard_header_len.
+        */
+       dev->mtu                = IEEE802154_MTU - IEEE802154_FCS_LEN -
+                                 dev->hard_header_len;
        dev->tx_queue_len       = 300;
        dev->flags              = IFF_NOARP | IFF_BROADCAST;
 }
@@ -513,6 +597,7 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
                sdata->dev->netdev_ops = &mac802154_wpan_ops;
                sdata->dev->ml_priv = &mac802154_mlme_wpan;
                wpan_dev->promiscuous_mode = false;
+               wpan_dev->header_ops = &ieee802154_header_ops;
 
                mutex_init(&sdata->sec_mtx);
 
@@ -550,7 +635,8 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
        if (!ndev)
                return ERR_PTR(-ENOMEM);
 
-       ndev->needed_headroom = local->hw.extra_tx_headroom;
+       ndev->needed_headroom = local->hw.extra_tx_headroom +
+                               IEEE802154_MAX_HEADER_LEN;
 
        ret = dev_alloc_name(ndev, ndev->name);
        if (ret < 0)
index 985e9394e2afa97c069a9c565859a04c7a2cff51..a13d02b7cee47401357f1fd7ff5e0dc74becc9ad 100644 (file)
@@ -55,7 +55,7 @@ void mac802154_llsec_destroy(struct mac802154_llsec *sec)
 
                msl = container_of(sl, struct mac802154_llsec_seclevel, level);
                list_del(&sl->list);
-               kfree(msl);
+               kzfree(msl);
        }
 
        list_for_each_entry_safe(dev, dn, &sec->table.devices, list) {
@@ -72,7 +72,7 @@ void mac802154_llsec_destroy(struct mac802154_llsec *sec)
                mkey = container_of(key->key, struct mac802154_llsec_key, key);
                list_del(&key->list);
                llsec_key_put(mkey);
-               kfree(key);
+               kzfree(key);
        }
 }
 
@@ -161,7 +161,7 @@ err_tfm:
                if (key->tfm[i])
                        crypto_free_aead(key->tfm[i]);
 
-       kfree(key);
+       kzfree(key);
        return NULL;
 }
 
@@ -176,7 +176,7 @@ static void llsec_key_release(struct kref *ref)
                crypto_free_aead(key->tfm[i]);
 
        crypto_free_blkcipher(key->tfm0);
-       kfree(key);
+       kzfree(key);
 }
 
 static struct mac802154_llsec_key*
@@ -267,7 +267,7 @@ int mac802154_llsec_key_add(struct mac802154_llsec *sec,
        return 0;
 
 fail:
-       kfree(new);
+       kzfree(new);
        return -ENOMEM;
 }
 
@@ -347,10 +347,10 @@ static void llsec_dev_free(struct mac802154_llsec_device *dev)
                                      devkey);
 
                list_del(&pos->list);
-               kfree(devkey);
+               kzfree(devkey);
        }
 
-       kfree(dev);
+       kzfree(dev);
 }
 
 int mac802154_llsec_dev_add(struct mac802154_llsec *sec,
@@ -401,6 +401,7 @@ int mac802154_llsec_dev_del(struct mac802154_llsec *sec, __le64 device_addr)
 
        hash_del_rcu(&pos->bucket_s);
        hash_del_rcu(&pos->bucket_hw);
+       list_del_rcu(&pos->dev.list);
        call_rcu(&pos->rcu, llsec_dev_free_rcu);
 
        return 0;
@@ -680,7 +681,7 @@ llsec_do_encrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
 
        rc = crypto_aead_encrypt(req);
 
-       kfree(req);
+       kzfree(req);
 
        return rc;
 }
@@ -880,7 +881,7 @@ llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
 
        rc = crypto_aead_decrypt(req);
 
-       kfree(req);
+       kzfree(req);
        skb_trim(skb, skb->len - authlen);
 
        return rc;
@@ -920,7 +921,7 @@ llsec_update_devkey_record(struct mac802154_llsec_device *dev,
                if (!devkey)
                        list_add_rcu(&next->devkey.list, &dev->dev.keys);
                else
-                       kfree(next);
+                       kzfree(next);
 
                spin_unlock_bh(&dev->lock);
        }
index d1c33c1d6b9b3dcd74dc077b5de5b5556a18901e..42e96729dae69aac0362e791f9e42be962ae38b2 100644 (file)
@@ -87,6 +87,10 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
 
        skb->dev = sdata->dev;
 
+       /* TODO this should be moved after netif_receive_skb call, otherwise
+        * wireshark will show a mac header with security fields and the
+        * payload is already decrypted.
+        */
        rc = mac802154_llsec_decrypt(&sdata->sec, skb);
        if (rc) {
                pr_debug("decryption failed: %i\n", rc);
index 7ed439172f30809d59fb5673131957ca8c25c56c..3827f359b336de356695635401820fc094344faf 100644 (file)
@@ -77,9 +77,6 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb)
                put_unaligned_le16(crc, skb_put(skb, 2));
        }
 
-       if (skb_cow_head(skb, local->hw.extra_tx_headroom))
-               goto err_tx;
-
        /* Stop the netif queue on each sub_if_data object. */
        ieee802154_stop_queue(&local->hw);
 
@@ -121,6 +118,10 @@ ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
        int rc;
 
+       /* TODO we should move it to wpan_dev_hard_header and dev_hard_header
+        * functions. The reason is wireshark will show a mac header which is
+        * with security fields but the payload is not encrypted.
+        */
        rc = mac802154_llsec_encrypt(&sdata->sec, skb);
        if (rc) {
                netdev_warn(dev, "encryption failed: %i\n", rc);
index bb185a28de9890d2f4b3c57d1ca7af7600f9b2aa..c70d750148b66759ce47525c6f6b348c2e69efaa 100644 (file)
 #include <net/ipv6.h>
 #include <net/addrconf.h>
 #endif
+#include <net/nexthop.h>
 #include "internal.h"
 
-#define LABEL_NOT_SPECIFIED (1<<20)
-#define MAX_NEW_LABELS 2
-
-/* This maximum ha length copied from the definition of struct neighbour */
-#define MAX_VIA_ALEN (ALIGN(MAX_ADDR_LEN, sizeof(unsigned long)))
-
-enum mpls_payload_type {
-       MPT_UNSPEC, /* IPv4 or IPv6 */
-       MPT_IPV4 = 4,
-       MPT_IPV6 = 6,
-
-       /* Other types not implemented:
-        *  - Pseudo-wire with or without control word (RFC4385)
-        *  - GAL (RFC5586)
-        */
-};
-
-struct mpls_route { /* next hop label forwarding entry */
-       struct net_device __rcu *rt_dev;
-       struct rcu_head         rt_rcu;
-       u32                     rt_label[MAX_NEW_LABELS];
-       u8                      rt_protocol; /* routing protocol that set this entry */
-       u8                      rt_payload_type;
-       u8                      rt_labels;
-       u8                      rt_via_alen;
-       u8                      rt_via_table;
-       u8                      rt_via[0];
-};
+/* Maximum number of labels to look ahead at when selecting a path of
+ * a multipath route
+ */
+#define MAX_MP_SELECT_LABELS 4
 
 static int zero = 0;
 static int label_limit = (1 << 20) - 1;
@@ -80,10 +57,24 @@ bool mpls_output_possible(const struct net_device *dev)
 }
 EXPORT_SYMBOL_GPL(mpls_output_possible);
 
-static unsigned int mpls_rt_header_size(const struct mpls_route *rt)
+static u8 *__mpls_nh_via(struct mpls_route *rt, struct mpls_nh *nh)
+{
+       u8 *nh0_via = PTR_ALIGN((u8 *)&rt->rt_nh[rt->rt_nhn], VIA_ALEN_ALIGN);
+       int nh_index = nh - rt->rt_nh;
+
+       return nh0_via + rt->rt_max_alen * nh_index;
+}
+
+static const u8 *mpls_nh_via(const struct mpls_route *rt,
+                            const struct mpls_nh *nh)
+{
+       return __mpls_nh_via((struct mpls_route *)rt, (struct mpls_nh *)nh);
+}
+
+static unsigned int mpls_nh_header_size(const struct mpls_nh *nh)
 {
        /* The size of the layer 2.5 labels to be added for this route */
-       return rt->rt_labels * sizeof(struct mpls_shim_hdr);
+       return nh->nh_labels * sizeof(struct mpls_shim_hdr);
 }
 
 unsigned int mpls_dev_mtu(const struct net_device *dev)
@@ -105,6 +96,80 @@ bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
 }
 EXPORT_SYMBOL_GPL(mpls_pkt_too_big);
 
+static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
+                                            struct sk_buff *skb, bool bos)
+{
+       struct mpls_entry_decoded dec;
+       struct mpls_shim_hdr *hdr;
+       bool eli_seen = false;
+       int label_index;
+       int nh_index = 0;
+       u32 hash = 0;
+
+       /* No need to look further into packet if there's only
+        * one path
+        */
+       if (rt->rt_nhn == 1)
+               goto out;
+
+       for (label_index = 0; label_index < MAX_MP_SELECT_LABELS && !bos;
+            label_index++) {
+               if (!pskb_may_pull(skb, sizeof(*hdr) * label_index))
+                       break;
+
+               /* Read and decode the current label */
+               hdr = mpls_hdr(skb) + label_index;
+               dec = mpls_entry_decode(hdr);
+
+               /* RFC6790 - reserved labels MUST NOT be used as keys
+                * for the load-balancing function
+                */
+               if (likely(dec.label >= MPLS_LABEL_FIRST_UNRESERVED)) {
+                       hash = jhash_1word(dec.label, hash);
+
+                       /* The entropy label follows the entropy label
+                        * indicator, so this means that the entropy
+                        * label was just added to the hash - no need to
+                        * go any deeper either in the label stack or in the
+                        * payload
+                        */
+                       if (eli_seen)
+                               break;
+               } else if (dec.label == MPLS_LABEL_ENTROPY) {
+                       eli_seen = true;
+               }
+
+               bos = dec.bos;
+               if (bos && pskb_may_pull(skb, sizeof(*hdr) * label_index +
+                                        sizeof(struct iphdr))) {
+                       const struct iphdr *v4hdr;
+
+                       v4hdr = (const struct iphdr *)(mpls_hdr(skb) +
+                                                      label_index);
+                       if (v4hdr->version == 4) {
+                               hash = jhash_3words(ntohl(v4hdr->saddr),
+                                                   ntohl(v4hdr->daddr),
+                                                   v4hdr->protocol, hash);
+                       } else if (v4hdr->version == 6 &&
+                               pskb_may_pull(skb, sizeof(*hdr) * label_index +
+                                             sizeof(struct ipv6hdr))) {
+                               const struct ipv6hdr *v6hdr;
+
+                               v6hdr = (const struct ipv6hdr *)(mpls_hdr(skb) +
+                                                               label_index);
+
+                               hash = __ipv6_addr_jhash(&v6hdr->saddr, hash);
+                               hash = __ipv6_addr_jhash(&v6hdr->daddr, hash);
+                               hash = jhash_1word(v6hdr->nexthdr, hash);
+                       }
+               }
+       }
+
+       nh_index = hash % rt->rt_nhn;
+out:
+       return &rt->rt_nh[nh_index];
+}
+
 static bool mpls_egress(struct mpls_route *rt, struct sk_buff *skb,
                        struct mpls_entry_decoded dec)
 {
@@ -159,6 +224,7 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev,
        struct net *net = dev_net(dev);
        struct mpls_shim_hdr *hdr;
        struct mpls_route *rt;
+       struct mpls_nh *nh;
        struct mpls_entry_decoded dec;
        struct net_device *out_dev;
        struct mpls_dev *mdev;
@@ -196,8 +262,12 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev,
        if (!rt)
                goto drop;
 
+       nh = mpls_select_multipath(rt, skb, dec.bos);
+       if (!nh)
+               goto drop;
+
        /* Find the output device */
-       out_dev = rcu_dereference(rt->rt_dev);
+       out_dev = rcu_dereference(nh->nh_dev);
        if (!mpls_output_possible(out_dev))
                goto drop;
 
@@ -212,7 +282,7 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev,
        dec.ttl -= 1;
 
        /* Verify the destination can hold the packet */
-       new_header_size = mpls_rt_header_size(rt);
+       new_header_size = mpls_nh_header_size(nh);
        mtu = mpls_dev_mtu(out_dev);
        if (mpls_pkt_too_big(skb, mtu - new_header_size))
                goto drop;
@@ -240,13 +310,14 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev,
                /* Push the new labels */
                hdr = mpls_hdr(skb);
                bos = dec.bos;
-               for (i = rt->rt_labels - 1; i >= 0; i--) {
-                       hdr[i] = mpls_entry_encode(rt->rt_label[i], dec.ttl, 0, bos);
+               for (i = nh->nh_labels - 1; i >= 0; i--) {
+                       hdr[i] = mpls_entry_encode(nh->nh_label[i],
+                                                  dec.ttl, 0, bos);
                        bos = false;
                }
        }
 
-       err = neigh_xmit(rt->rt_via_table, out_dev, rt->rt_via, skb);
+       err = neigh_xmit(nh->nh_via_table, out_dev, mpls_nh_via(rt, nh), skb);
        if (err)
                net_dbg_ratelimited("%s: packet transmission failed: %d\n",
                                    __func__, err);
@@ -270,24 +341,33 @@ static const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = {
 struct mpls_route_config {
        u32                     rc_protocol;
        u32                     rc_ifindex;
-       u16                     rc_via_table;
-       u16                     rc_via_alen;
+       u                     rc_via_table;
+       u                     rc_via_alen;
        u8                      rc_via[MAX_VIA_ALEN];
        u32                     rc_label;
-       u32                     rc_output_labels;
+       u                     rc_output_labels;
        u32                     rc_output_label[MAX_NEW_LABELS];
        u32                     rc_nlflags;
        enum mpls_payload_type  rc_payload_type;
        struct nl_info          rc_nlinfo;
+       struct rtnexthop        *rc_mp;
+       int                     rc_mp_len;
 };
 
-static struct mpls_route *mpls_rt_alloc(size_t alen)
+static struct mpls_route *mpls_rt_alloc(int num_nh, u8 max_alen)
 {
+       u8 max_alen_aligned = ALIGN(max_alen, VIA_ALEN_ALIGN);
        struct mpls_route *rt;
 
-       rt = kzalloc(sizeof(*rt) + alen, GFP_KERNEL);
-       if (rt)
-               rt->rt_via_alen = alen;
+       rt = kzalloc(ALIGN(sizeof(*rt) + num_nh * sizeof(*rt->rt_nh),
+                          VIA_ALEN_ALIGN) +
+                    num_nh * max_alen_aligned,
+                    GFP_KERNEL);
+       if (rt) {
+               rt->rt_nhn = num_nh;
+               rt->rt_max_alen = max_alen_aligned;
+       }
+
        return rt;
 }
 
@@ -312,25 +392,22 @@ static void mpls_notify_route(struct net *net, unsigned index,
 }
 
 static void mpls_route_update(struct net *net, unsigned index,
-                             struct net_device *dev, struct mpls_route *new,
+                             struct mpls_route *new,
                              const struct nl_info *info)
 {
        struct mpls_route __rcu **platform_label;
-       struct mpls_route *rt, *old = NULL;
+       struct mpls_route *rt;
 
        ASSERT_RTNL();
 
        platform_label = rtnl_dereference(net->mpls.platform_label);
        rt = rtnl_dereference(platform_label[index]);
-       if (!dev || (rt && (rtnl_dereference(rt->rt_dev) == dev))) {
-               rcu_assign_pointer(platform_label[index], new);
-               old = rt;
-       }
+       rcu_assign_pointer(platform_label[index], new);
 
-       mpls_notify_route(net, index, old, new, info);
+       mpls_notify_route(net, index, rt, new, info);
 
        /* If we removed a route free it now */
-       mpls_rt_free(old);
+       mpls_rt_free(rt);
 }
 
 static unsigned find_free_label(struct net *net)
@@ -350,7 +427,8 @@ static unsigned find_free_label(struct net *net)
 }
 
 #if IS_ENABLED(CONFIG_INET)
-static struct net_device *inet_fib_lookup_dev(struct net *net, void *addr)
+static struct net_device *inet_fib_lookup_dev(struct net *net,
+                                             const void *addr)
 {
        struct net_device *dev;
        struct rtable *rt;
@@ -369,14 +447,16 @@ static struct net_device *inet_fib_lookup_dev(struct net *net, void *addr)
        return dev;
 }
 #else
-static struct net_device *inet_fib_lookup_dev(struct net *net, void *addr)
+static struct net_device *inet_fib_lookup_dev(struct net *net,
+                                             const void *addr)
 {
        return ERR_PTR(-EAFNOSUPPORT);
 }
 #endif
 
 #if IS_ENABLED(CONFIG_IPV6)
-static struct net_device *inet6_fib_lookup_dev(struct net *net, void *addr)
+static struct net_device *inet6_fib_lookup_dev(struct net *net,
+                                              const void *addr)
 {
        struct net_device *dev;
        struct dst_entry *dst;
@@ -399,47 +479,234 @@ static struct net_device *inet6_fib_lookup_dev(struct net *net, void *addr)
        return dev;
 }
 #else
-static struct net_device *inet6_fib_lookup_dev(struct net *net, void *addr)
+static struct net_device *inet6_fib_lookup_dev(struct net *net,
+                                              const void *addr)
 {
        return ERR_PTR(-EAFNOSUPPORT);
 }
 #endif
 
 static struct net_device *find_outdev(struct net *net,
-                                     struct mpls_route_config *cfg)
+                                     struct mpls_route *rt,
+                                     struct mpls_nh *nh, int oif)
 {
        struct net_device *dev = NULL;
 
-       if (!cfg->rc_ifindex) {
-               switch (cfg->rc_via_table) {
+       if (!oif) {
+               switch (nh->nh_via_table) {
                case NEIGH_ARP_TABLE:
-                       dev = inet_fib_lookup_dev(net, cfg->rc_via);
+                       dev = inet_fib_lookup_dev(net, mpls_nh_via(rt, nh));
                        break;
                case NEIGH_ND_TABLE:
-                       dev = inet6_fib_lookup_dev(net, cfg->rc_via);
+                       dev = inet6_fib_lookup_dev(net, mpls_nh_via(rt, nh));
                        break;
                case NEIGH_LINK_TABLE:
                        break;
                }
        } else {
-               dev = dev_get_by_index(net, cfg->rc_ifindex);
+               dev = dev_get_by_index(net, oif);
        }
 
        if (!dev)
                return ERR_PTR(-ENODEV);
 
+       /* The caller is holding rtnl anyways, so release the dev reference */
+       dev_put(dev);
+
        return dev;
 }
 
+static int mpls_nh_assign_dev(struct net *net, struct mpls_route *rt,
+                             struct mpls_nh *nh, int oif)
+{
+       struct net_device *dev = NULL;
+       int err = -ENODEV;
+
+       dev = find_outdev(net, rt, nh, oif);
+       if (IS_ERR(dev)) {
+               err = PTR_ERR(dev);
+               dev = NULL;
+               goto errout;
+       }
+
+       /* Ensure this is a supported device */
+       err = -EINVAL;
+       if (!mpls_dev_get(dev))
+               goto errout;
+
+       RCU_INIT_POINTER(nh->nh_dev, dev);
+
+       return 0;
+
+errout:
+       return err;
+}
+
+static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg,
+                                 struct mpls_route *rt)
+{
+       struct net *net = cfg->rc_nlinfo.nl_net;
+       struct mpls_nh *nh = rt->rt_nh;
+       int err;
+       int i;
+
+       if (!nh)
+               return -ENOMEM;
+
+       err = -EINVAL;
+       /* Ensure only a supported number of labels are present */
+       if (cfg->rc_output_labels > MAX_NEW_LABELS)
+               goto errout;
+
+       nh->nh_labels = cfg->rc_output_labels;
+       for (i = 0; i < nh->nh_labels; i++)
+               nh->nh_label[i] = cfg->rc_output_label[i];
+
+       nh->nh_via_table = cfg->rc_via_table;
+       memcpy(__mpls_nh_via(rt, nh), cfg->rc_via, cfg->rc_via_alen);
+       nh->nh_via_alen = cfg->rc_via_alen;
+
+       err = mpls_nh_assign_dev(net, rt, nh, cfg->rc_ifindex);
+       if (err)
+               goto errout;
+
+       return 0;
+
+errout:
+       return err;
+}
+
+static int mpls_nh_build(struct net *net, struct mpls_route *rt,
+                        struct mpls_nh *nh, int oif,
+                        struct nlattr *via, struct nlattr *newdst)
+{
+       int err = -ENOMEM;
+
+       if (!nh)
+               goto errout;
+
+       if (newdst) {
+               err = nla_get_labels(newdst, MAX_NEW_LABELS,
+                                    &nh->nh_labels, nh->nh_label);
+               if (err)
+                       goto errout;
+       }
+
+       err = nla_get_via(via, &nh->nh_via_alen, &nh->nh_via_table,
+                         __mpls_nh_via(rt, nh));
+       if (err)
+               goto errout;
+
+       err = mpls_nh_assign_dev(net, rt, nh, oif);
+       if (err)
+               goto errout;
+
+       return 0;
+
+errout:
+       return err;
+}
+
+static int mpls_count_nexthops(struct rtnexthop *rtnh, int len,
+                              u8 cfg_via_alen, u8 *max_via_alen)
+{
+       int nhs = 0;
+       int remaining = len;
+
+       if (!rtnh) {
+               *max_via_alen = cfg_via_alen;
+               return 1;
+       }
+
+       *max_via_alen = 0;
+
+       while (rtnh_ok(rtnh, remaining)) {
+               struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
+               int attrlen;
+
+               attrlen = rtnh_attrlen(rtnh);
+               nla = nla_find(attrs, attrlen, RTA_VIA);
+               if (nla && nla_len(nla) >=
+                   offsetof(struct rtvia, rtvia_addr)) {
+                       int via_alen = nla_len(nla) -
+                               offsetof(struct rtvia, rtvia_addr);
+
+                       if (via_alen <= MAX_VIA_ALEN)
+                               *max_via_alen = max_t(u16, *max_via_alen,
+                                                     via_alen);
+               }
+
+               nhs++;
+               rtnh = rtnh_next(rtnh, &remaining);
+       }
+
+       /* leftover implies invalid nexthop configuration, discard it */
+       return remaining > 0 ? 0 : nhs;
+}
+
+static int mpls_nh_build_multi(struct mpls_route_config *cfg,
+                              struct mpls_route *rt)
+{
+       struct rtnexthop *rtnh = cfg->rc_mp;
+       struct nlattr *nla_via, *nla_newdst;
+       int remaining = cfg->rc_mp_len;
+       int nhs = 0;
+       int err = 0;
+
+       change_nexthops(rt) {
+               int attrlen;
+
+               nla_via = NULL;
+               nla_newdst = NULL;
+
+               err = -EINVAL;
+               if (!rtnh_ok(rtnh, remaining))
+                       goto errout;
+
+               /* neither weighted multipath nor any flags
+                * are supported
+                */
+               if (rtnh->rtnh_hops || rtnh->rtnh_flags)
+                       goto errout;
+
+               attrlen = rtnh_attrlen(rtnh);
+               if (attrlen > 0) {
+                       struct nlattr *attrs = rtnh_attrs(rtnh);
+
+                       nla_via = nla_find(attrs, attrlen, RTA_VIA);
+                       nla_newdst = nla_find(attrs, attrlen, RTA_NEWDST);
+               }
+
+               if (!nla_via)
+                       goto errout;
+
+               err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh,
+                                   rtnh->rtnh_ifindex, nla_via,
+                                   nla_newdst);
+               if (err)
+                       goto errout;
+
+               rtnh = rtnh_next(rtnh, &remaining);
+               nhs++;
+       } endfor_nexthops(rt);
+
+       rt->rt_nhn = nhs;
+
+       return 0;
+
+errout:
+       return err;
+}
+
 static int mpls_route_add(struct mpls_route_config *cfg)
 {
        struct mpls_route __rcu **platform_label;
        struct net *net = cfg->rc_nlinfo.nl_net;
-       struct net_device *dev = NULL;
        struct mpls_route *rt, *old;
-       unsigned index;
-       int i;
        int err = -EINVAL;
+       u8 max_via_alen;
+       unsigned index;
+       int nhs;
 
        index = cfg->rc_label;
 
@@ -457,27 +724,6 @@ static int mpls_route_add(struct mpls_route_config *cfg)
        if (index >= net->mpls.platform_labels)
                goto errout;
 
-       /* Ensure only a supported number of labels are present */
-       if (cfg->rc_output_labels > MAX_NEW_LABELS)
-               goto errout;
-
-       dev = find_outdev(net, cfg);
-       if (IS_ERR(dev)) {
-               err = PTR_ERR(dev);
-               dev = NULL;
-               goto errout;
-       }
-
-       /* Ensure this is a supported device */
-       err = -EINVAL;
-       if (!mpls_dev_get(dev))
-               goto errout;
-
-       err = -EINVAL;
-       if ((cfg->rc_via_table == NEIGH_LINK_TABLE) &&
-           (dev->addr_len != cfg->rc_via_alen))
-               goto errout;
-
        /* Append makes no sense with mpls */
        err = -EOPNOTSUPP;
        if (cfg->rc_nlflags & NLM_F_APPEND)
@@ -497,28 +743,34 @@ static int mpls_route_add(struct mpls_route_config *cfg)
        if (!(cfg->rc_nlflags & NLM_F_CREATE) && !old)
                goto errout;
 
+       err = -EINVAL;
+       nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len,
+                                 cfg->rc_via_alen, &max_via_alen);
+       if (nhs == 0)
+               goto errout;
+
        err = -ENOMEM;
-       rt = mpls_rt_alloc(cfg->rc_via_alen);
+       rt = mpls_rt_alloc(nhs, max_via_alen);
        if (!rt)
                goto errout;
 
-       rt->rt_labels = cfg->rc_output_labels;
-       for (i = 0; i < rt->rt_labels; i++)
-               rt->rt_label[i] = cfg->rc_output_label[i];
        rt->rt_protocol = cfg->rc_protocol;
-       RCU_INIT_POINTER(rt->rt_dev, dev);
        rt->rt_payload_type = cfg->rc_payload_type;
-       rt->rt_via_table = cfg->rc_via_table;
-       memcpy(rt->rt_via, cfg->rc_via, cfg->rc_via_alen);
 
-       mpls_route_update(net, index, NULL, rt, &cfg->rc_nlinfo);
+       if (cfg->rc_mp)
+               err = mpls_nh_build_multi(cfg, rt);
+       else
+               err = mpls_nh_build_from_cfg(cfg, rt);
+       if (err)
+               goto freert;
+
+       mpls_route_update(net, index, rt, &cfg->rc_nlinfo);
 
-       dev_put(dev);
        return 0;
 
+freert:
+       mpls_rt_free(rt);
 errout:
-       if (dev)
-               dev_put(dev);
        return err;
 }
 
@@ -538,7 +790,7 @@ static int mpls_route_del(struct mpls_route_config *cfg)
        if (index >= net->mpls.platform_labels)
                goto errout;
 
-       mpls_route_update(net, index, NULL, NULL, &cfg->rc_nlinfo);
+       mpls_route_update(net, index, NULL, &cfg->rc_nlinfo);
 
        err = 0;
 errout:
@@ -635,9 +887,11 @@ static void mpls_ifdown(struct net_device *dev)
                struct mpls_route *rt = rtnl_dereference(platform_label[index]);
                if (!rt)
                        continue;
-               if (rtnl_dereference(rt->rt_dev) != dev)
-                       continue;
-               rt->rt_dev = NULL;
+               for_nexthops(rt) {
+                       if (rtnl_dereference(nh->nh_dev) != dev)
+                               continue;
+                       nh->nh_dev = NULL;
+               } endfor_nexthops(rt);
        }
 
        mdev = mpls_dev_get(dev);
@@ -736,7 +990,7 @@ int nla_put_labels(struct sk_buff *skb, int attrtype,
 EXPORT_SYMBOL_GPL(nla_put_labels);
 
 int nla_get_labels(const struct nlattr *nla,
-                  u32 max_labels, u32 *labels, u32 label[])
+                  u32 max_labels, u8 *labels, u32 label[])
 {
        unsigned len = nla_len(nla);
        unsigned nla_labels;
@@ -781,6 +1035,48 @@ int nla_get_labels(const struct nlattr *nla,
 }
 EXPORT_SYMBOL_GPL(nla_get_labels);
 
+int nla_get_via(const struct nlattr *nla, u8 *via_alen,
+               u8 *via_table, u8 via_addr[])
+{
+       struct rtvia *via = nla_data(nla);
+       int err = -EINVAL;
+       int alen;
+
+       if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr))
+               goto errout;
+       alen = nla_len(nla) -
+                       offsetof(struct rtvia, rtvia_addr);
+       if (alen > MAX_VIA_ALEN)
+               goto errout;
+
+       /* Validate the address family */
+       switch (via->rtvia_family) {
+       case AF_PACKET:
+               *via_table = NEIGH_LINK_TABLE;
+               break;
+       case AF_INET:
+               *via_table = NEIGH_ARP_TABLE;
+               if (alen != 4)
+                       goto errout;
+               break;
+       case AF_INET6:
+               *via_table = NEIGH_ND_TABLE;
+               if (alen != 16)
+                       goto errout;
+               break;
+       default:
+               /* Unsupported address family */
+               goto errout;
+       }
+
+       memcpy(via_addr, via->rtvia_addr, alen);
+       *via_alen = alen;
+       err = 0;
+
+errout:
+       return err;
+}
+
 static int rtm_to_route_config(struct sk_buff *skb,  struct nlmsghdr *nlh,
                               struct mpls_route_config *cfg)
 {
@@ -844,7 +1140,7 @@ static int rtm_to_route_config(struct sk_buff *skb,  struct nlmsghdr *nlh,
                        break;
                case RTA_DST:
                {
-                       u32 label_count;
+                       u8 label_count;
                        if (nla_get_labels(nla, 1, &label_count,
                                           &cfg->rc_label))
                                goto errout;
@@ -857,35 +1153,15 @@ static int rtm_to_route_config(struct sk_buff *skb,  struct nlmsghdr *nlh,
                }
                case RTA_VIA:
                {
-                       struct rtvia *via = nla_data(nla);
-                       if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr))
-                               goto errout;
-                       cfg->rc_via_alen   = nla_len(nla) -
-                               offsetof(struct rtvia, rtvia_addr);
-                       if (cfg->rc_via_alen > MAX_VIA_ALEN)
+                       if (nla_get_via(nla, &cfg->rc_via_alen,
+                                       &cfg->rc_via_table, cfg->rc_via))
                                goto errout;
-
-                       /* Validate the address family */
-                       switch(via->rtvia_family) {
-                       case AF_PACKET:
-                               cfg->rc_via_table = NEIGH_LINK_TABLE;
-                               break;
-                       case AF_INET:
-                               cfg->rc_via_table = NEIGH_ARP_TABLE;
-                               if (cfg->rc_via_alen != 4)
-                                       goto errout;
-                               break;
-                       case AF_INET6:
-                               cfg->rc_via_table = NEIGH_ND_TABLE;
-                               if (cfg->rc_via_alen != 16)
-                                       goto errout;
-                               break;
-                       default:
-                               /* Unsupported address family */
-                               goto errout;
-                       }
-
-                       memcpy(cfg->rc_via, via->rtvia_addr, cfg->rc_via_alen);
+                       break;
+               }
+               case RTA_MULTIPATH:
+               {
+                       cfg->rc_mp = nla_data(nla);
+                       cfg->rc_mp_len = nla_len(nla);
                        break;
                }
                default:
@@ -946,16 +1222,52 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
        rtm->rtm_type = RTN_UNICAST;
        rtm->rtm_flags = 0;
 
-       if (rt->rt_labels &&
-           nla_put_labels(skb, RTA_NEWDST, rt->rt_labels, rt->rt_label))
-               goto nla_put_failure;
-       if (nla_put_via(skb, rt->rt_via_table, rt->rt_via, rt->rt_via_alen))
-               goto nla_put_failure;
-       dev = rtnl_dereference(rt->rt_dev);
-       if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex))
-               goto nla_put_failure;
        if (nla_put_labels(skb, RTA_DST, 1, &label))
                goto nla_put_failure;
+       if (rt->rt_nhn == 1) {
+               const struct mpls_nh *nh = rt->rt_nh;
+
+               if (nh->nh_labels &&
+                   nla_put_labels(skb, RTA_NEWDST, nh->nh_labels,
+                                  nh->nh_label))
+                       goto nla_put_failure;
+               if (nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh),
+                               nh->nh_via_alen))
+                       goto nla_put_failure;
+               dev = rtnl_dereference(nh->nh_dev);
+               if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex))
+                       goto nla_put_failure;
+       } else {
+               struct rtnexthop *rtnh;
+               struct nlattr *mp;
+
+               mp = nla_nest_start(skb, RTA_MULTIPATH);
+               if (!mp)
+                       goto nla_put_failure;
+
+               for_nexthops(rt) {
+                       rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh));
+                       if (!rtnh)
+                               goto nla_put_failure;
+
+                       dev = rtnl_dereference(nh->nh_dev);
+                       if (dev)
+                               rtnh->rtnh_ifindex = dev->ifindex;
+                       if (nh->nh_labels && nla_put_labels(skb, RTA_NEWDST,
+                                                           nh->nh_labels,
+                                                           nh->nh_label))
+                               goto nla_put_failure;
+                       if (nla_put_via(skb, nh->nh_via_table,
+                                       mpls_nh_via(rt, nh),
+                                       nh->nh_via_alen))
+                               goto nla_put_failure;
+
+                       /* length of rtnetlink header + attributes */
+                       rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh;
+               } endfor_nexthops(rt);
+
+               nla_nest_end(skb, mp);
+       }
 
        nlmsg_end(skb, nlh);
        return 0;
@@ -1000,12 +1312,30 @@ static inline size_t lfib_nlmsg_size(struct mpls_route *rt)
 {
        size_t payload =
                NLMSG_ALIGN(sizeof(struct rtmsg))
-               + nla_total_size(2 + rt->rt_via_alen)   /* RTA_VIA */
                + nla_total_size(4);                    /* RTA_DST */
-       if (rt->rt_labels)                              /* RTA_NEWDST */
-               payload += nla_total_size(rt->rt_labels * 4);
-       if (rt->rt_dev)                                 /* RTA_OIF */
-               payload += nla_total_size(4);
+
+       if (rt->rt_nhn == 1) {
+               struct mpls_nh *nh = rt->rt_nh;
+
+               if (nh->nh_dev)
+                       payload += nla_total_size(4); /* RTA_OIF */
+               payload += nla_total_size(2 + nh->nh_via_alen); /* RTA_VIA */
+               if (nh->nh_labels) /* RTA_NEWDST */
+                       payload += nla_total_size(nh->nh_labels * 4);
+       } else {
+               /* each nexthop is packed in an attribute */
+               size_t nhsize = 0;
+
+               for_nexthops(rt) {
+                       nhsize += nla_total_size(sizeof(struct rtnexthop));
+                       nhsize += nla_total_size(2 + nh->nh_via_alen);
+                       if (nh->nh_labels)
+                               nhsize += nla_total_size(nh->nh_labels * 4);
+               } endfor_nexthops(rt);
+               /* nested attribute */
+               payload += nla_total_size(nhsize);
+       }
+
        return payload;
 }
 
@@ -1057,25 +1387,29 @@ static int resize_platform_label_table(struct net *net, size_t limit)
        /* In case the predefined labels need to be populated */
        if (limit > MPLS_LABEL_IPV4NULL) {
                struct net_device *lo = net->loopback_dev;
-               rt0 = mpls_rt_alloc(lo->addr_len);
+               rt0 = mpls_rt_alloc(1, lo->addr_len);
                if (!rt0)
                        goto nort0;
-               RCU_INIT_POINTER(rt0->rt_dev, lo);
+               RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo);
                rt0->rt_protocol = RTPROT_KERNEL;
                rt0->rt_payload_type = MPT_IPV4;
-               rt0->rt_via_table = NEIGH_LINK_TABLE;
-               memcpy(rt0->rt_via, lo->dev_addr, lo->addr_len);
+               rt0->rt_nh->nh_via_table = NEIGH_LINK_TABLE;
+               rt0->rt_nh->nh_via_alen = lo->addr_len;
+               memcpy(__mpls_nh_via(rt0, rt0->rt_nh), lo->dev_addr,
+                      lo->addr_len);
        }
        if (limit > MPLS_LABEL_IPV6NULL) {
                struct net_device *lo = net->loopback_dev;
-               rt2 = mpls_rt_alloc(lo->addr_len);
+               rt2 = mpls_rt_alloc(1, lo->addr_len);
                if (!rt2)
                        goto nort2;
-               RCU_INIT_POINTER(rt2->rt_dev, lo);
+               RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo);
                rt2->rt_protocol = RTPROT_KERNEL;
                rt2->rt_payload_type = MPT_IPV6;
-               rt2->rt_via_table = NEIGH_LINK_TABLE;
-               memcpy(rt2->rt_via, lo->dev_addr, lo->addr_len);
+               rt2->rt_nh->nh_via_table = NEIGH_LINK_TABLE;
+               rt2->rt_nh->nh_via_alen = lo->addr_len;
+               memcpy(__mpls_nh_via(rt2, rt2->rt_nh), lo->dev_addr,
+                      lo->addr_len);
        }
 
        rtnl_lock();
@@ -1085,7 +1419,7 @@ static int resize_platform_label_table(struct net *net, size_t limit)
 
        /* Free any labels beyond the new table */
        for (index = limit; index < old_limit; index++)
-               mpls_route_update(net, index, NULL, NULL, NULL);
+               mpls_route_update(net, index, NULL, NULL);
 
        /* Copy over the old labels */
        cp_size = size;
index 2681a4ba6c375f3faf83498150350ddea7392ccc..bde52ce88c949e76083ec704ed010493b60d6466 100644 (file)
@@ -21,6 +21,76 @@ struct mpls_dev {
 
 struct sk_buff;
 
+#define LABEL_NOT_SPECIFIED (1 << 20)
+#define MAX_NEW_LABELS 2
+
+/* This maximum ha length copied from the definition of struct neighbour */
+#define VIA_ALEN_ALIGN sizeof(unsigned long)
+#define MAX_VIA_ALEN (ALIGN(MAX_ADDR_LEN, VIA_ALEN_ALIGN))
+
+enum mpls_payload_type {
+       MPT_UNSPEC, /* IPv4 or IPv6 */
+       MPT_IPV4 = 4,
+       MPT_IPV6 = 6,
+
+       /* Other types not implemented:
+        *  - Pseudo-wire with or without control word (RFC4385)
+        *  - GAL (RFC5586)
+        */
+};
+
+struct mpls_nh { /* next hop label forwarding entry */
+       struct net_device __rcu *nh_dev;
+       u32                     nh_label[MAX_NEW_LABELS];
+       u8                      nh_labels;
+       u8                      nh_via_alen;
+       u8                      nh_via_table;
+};
+
+/* The route, nexthops and vias are stored together in the same memory
+ * block:
+ *
+ * +----------------------+
+ * | mpls_route           |
+ * +----------------------+
+ * | mpls_nh 0            |
+ * +----------------------+
+ * | ...                  |
+ * +----------------------+
+ * | mpls_nh n-1          |
+ * +----------------------+
+ * | alignment padding    |
+ * +----------------------+
+ * | via[rt_max_alen] 0   |
+ * +----------------------+
+ * | ...                  |
+ * +----------------------+
+ * | via[rt_max_alen] n-1 |
+ * +----------------------+
+ */
+struct mpls_route { /* next hop label forwarding entry */
+       struct rcu_head         rt_rcu;
+       u8                      rt_protocol;
+       u8                      rt_payload_type;
+       u8                      rt_max_alen;
+       unsigned int            rt_nhn;
+       struct mpls_nh          rt_nh[0];
+};
+
+#define for_nexthops(rt) {                                             \
+       int nhsel; struct mpls_nh *nh;                  \
+       for (nhsel = 0, nh = (rt)->rt_nh;                               \
+            nhsel < (rt)->rt_nhn;                                      \
+            nh++, nhsel++)
+
+#define change_nexthops(rt) {                                          \
+       int nhsel; struct mpls_nh *nh;                          \
+       for (nhsel = 0, nh = (struct mpls_nh *)((rt)->rt_nh);   \
+            nhsel < (rt)->rt_nhn;                                      \
+            nh++, nhsel++)
+
+#define endfor_nexthops(rt) }
+
 static inline struct mpls_shim_hdr *mpls_hdr(const struct sk_buff *skb)
 {
        return (struct mpls_shim_hdr *)skb_network_header(skb);
@@ -52,8 +122,10 @@ static inline struct mpls_entry_decoded mpls_entry_decode(struct mpls_shim_hdr *
 
 int nla_put_labels(struct sk_buff *skb, int attrtype,  u8 labels,
                   const u32 label[]);
-int nla_get_labels(const struct nlattr *nla, u32 max_labels, u32 *labels,
+int nla_get_labels(const struct nlattr *nla, u32 max_labels, u8 *labels,
                   u32 label[]);
+int nla_get_via(const struct nlattr *nla, u8 *via_alen, u8 *via_table,
+               u8 via[]);
 bool mpls_output_possible(const struct net_device *dev);
 unsigned int mpls_dev_mtu(const struct net_device *dev);
 bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu);
index 21e70bc9af989355521f8fd99ea3bc07730626c8..67591aef9cae6fb203ef802272baad6e5b191000 100644 (file)
@@ -37,7 +37,7 @@ static unsigned int mpls_encap_size(struct mpls_iptunnel_encap *en)
        return en->labels * sizeof(struct mpls_shim_hdr);
 }
 
-int mpls_output(struct sock *sk, struct sk_buff *skb)
+int mpls_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct mpls_iptunnel_encap *tun_encap_info;
        struct mpls_shim_hdr *hdr;
index 3e1b4abf1897a5bdeca9e5fa061bd06d9b858263..e22349ea725605a05ce03cb3701142856daf420c 100644 (file)
@@ -354,7 +354,7 @@ config NF_CT_NETLINK_HELPER
        select NETFILTER_NETLINK
        depends on NF_CT_NETLINK
        depends on NETFILTER_NETLINK_QUEUE
-       depends on NETFILTER_NETLINK_QUEUE_CT
+       depends on NETFILTER_NETLINK_GLUE_CT
        depends on NETFILTER_ADVANCED
        help
          This option enables the user-space connection tracking helpers
@@ -362,13 +362,14 @@ config NF_CT_NETLINK_HELPER
 
          If unsure, say `N'.
 
-config NETFILTER_NETLINK_QUEUE_CT
-        bool "NFQUEUE integration with Connection Tracking"
-        default n
-        depends on NETFILTER_NETLINK_QUEUE
+config NETFILTER_NETLINK_GLUE_CT
+       bool "NFQUEUE and NFLOG integration with Connection Tracking"
+       default n
+       depends on (NETFILTER_NETLINK_QUEUE || NETFILTER_NETLINK_LOG) && NF_CT_NETLINK
        help
-         If this option is enabled, NFQUEUE can include Connection Tracking
-         information together with the packet is the enqueued via NFNETLINK.
+         If this option is enabled, NFQUEUE and NFLOG can include
+         Connection Tracking information together with the packet is
+         the enqueued via NFNETLINK.
 
 config NF_NAT
        tristate
index 70d026d46fe7d07a3ee1d942b7e877782bb37957..7638c36b498ccd00618bd073252331147912d125 100644 (file)
@@ -10,8 +10,6 @@ obj-$(CONFIG_NETFILTER) = netfilter.o
 
 obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
 obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o
-nfnetlink_queue-y := nfnetlink_queue_core.o
-nfnetlink_queue-$(CONFIG_NETFILTER_NETLINK_QUEUE_CT) += nfnetlink_queue_ct.o
 obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o
 obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
 
index 8e47f8113495739082572d269797e00160251751..f39276d1c2d76788dfc018f2ebdc07d2855f977d 100644 (file)
@@ -152,6 +152,8 @@ void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
 #endif
        synchronize_net();
        nf_queue_nf_hook_drop(net, &entry->ops);
+       /* other cpu might still process nfqueue verdict that used reg */
+       synchronize_net();
        kfree(entry);
 }
 EXPORT_SYMBOL(nf_unregister_net_hook);
@@ -269,7 +271,7 @@ unsigned int nf_iterate(struct list_head *head,
                /* Optimization: we don't need to hold module
                   reference here, since function can't sleep. --RR */
 repeat:
-               verdict = (*elemp)->hook(*elemp, skb, state);
+               verdict = (*elemp)->hook((*elemp)->priv, skb, state);
                if (verdict != NF_ACCEPT) {
 #ifdef CONFIG_NETFILTER_DEBUG
                        if (unlikely((verdict & NF_VERDICT_MASK)
@@ -313,8 +315,6 @@ next_hook:
                int err = nf_queue(skb, elem, state,
                                   verdict >> NF_VERDICT_QBITS);
                if (err < 0) {
-                       if (err == -ECANCELED)
-                               goto next_hook;
                        if (err == -ESRCH &&
                           (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS))
                                goto next_hook;
@@ -348,6 +348,12 @@ int skb_make_writable(struct sk_buff *skb, unsigned int writable_len)
 }
 EXPORT_SYMBOL(skb_make_writable);
 
+/* This needs to be compiled in any case to avoid dependencies between the
+ * nfnetlink_queue code and nf_conntrack.
+ */
+struct nfnl_ct_hook __rcu *nfnl_ct_hook __read_mostly;
+EXPORT_SYMBOL_GPL(nfnl_ct_hook);
+
 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
 /* This does not belong here, but locally generated errors need it if connection
    tracking in use: without this, connection may not be in hash table, and hence
@@ -385,9 +391,6 @@ void nf_conntrack_destroy(struct nf_conntrack *nfct)
 }
 EXPORT_SYMBOL(nf_conntrack_destroy);
 
-struct nfq_ct_hook __rcu *nfq_ct_hook __read_mostly;
-EXPORT_SYMBOL_GPL(nfq_ct_hook);
-
 /* Built-in default zone used e.g. by modules. */
 const struct nf_conntrack_zone nf_ct_zone_dflt = {
        .id     = NF_CT_DEFAULT_ZONE_ID,
index 338b4047776f7bb9c4fc1750bdcb122769fcc515..69ab9c2634e1566fac04e9509cf4c51e64ea36e8 100644 (file)
@@ -519,8 +519,7 @@ int
 ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
            const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
-       struct ip_set *set = ip_set_rcu_get(
-                       dev_net(par->in ? par->in : par->out), index);
+       struct ip_set *set = ip_set_rcu_get(par->net, index);
        int ret = 0;
 
        BUG_ON(!set);
@@ -558,8 +557,7 @@ int
 ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
           const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
-       struct ip_set *set = ip_set_rcu_get(
-                       dev_net(par->in ? par->in : par->out), index);
+       struct ip_set *set = ip_set_rcu_get(par->net, index);
        int ret;
 
        BUG_ON(!set);
@@ -581,8 +579,7 @@ int
 ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
           const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
-       struct ip_set *set = ip_set_rcu_get(
-                       dev_net(par->in ? par->in : par->out), index);
+       struct ip_set *set = ip_set_rcu_get(par->net, index);
        int ret = 0;
 
        BUG_ON(!set);
index a1fe5377a2b3376d29f29b18d77ebfe08fc988e1..5a30ce6e8c90d278ac37cb0115f45a4390f9a6d7 100644 (file)
@@ -297,7 +297,7 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
              ip_set_timeout_expired(ext_timeout(n, set))))
                n =  NULL;
 
-       e = kzalloc(set->dsize, GFP_KERNEL);
+       e = kzalloc(set->dsize, GFP_ATOMIC);
        if (!e)
                return -ENOMEM;
        e->id = d->id;
index dfd7b65b3d2a9165eed15c5e8a7b90c1ed30c050..0328f725069332d0c84bbb3108192a2a28beeb2a 100644 (file)
@@ -75,7 +75,7 @@ static void ip_vs_app_inc_rcu_free(struct rcu_head *head)
  *     Allocate/initialize app incarnation and register it in proto apps.
  */
 static int
-ip_vs_app_inc_new(struct net *net, struct ip_vs_app *app, __u16 proto,
+ip_vs_app_inc_new(struct netns_ipvs *ipvs, struct ip_vs_app *app, __u16 proto,
                  __u16 port)
 {
        struct ip_vs_protocol *pp;
@@ -107,7 +107,7 @@ ip_vs_app_inc_new(struct net *net, struct ip_vs_app *app, __u16 proto,
                }
        }
 
-       ret = pp->register_app(net, inc);
+       ret = pp->register_app(ipvs, inc);
        if (ret)
                goto out;
 
@@ -127,7 +127,7 @@ ip_vs_app_inc_new(struct net *net, struct ip_vs_app *app, __u16 proto,
  *     Release app incarnation
  */
 static void
-ip_vs_app_inc_release(struct net *net, struct ip_vs_app *inc)
+ip_vs_app_inc_release(struct netns_ipvs *ipvs, struct ip_vs_app *inc)
 {
        struct ip_vs_protocol *pp;
 
@@ -135,7 +135,7 @@ ip_vs_app_inc_release(struct net *net, struct ip_vs_app *inc)
                return;
 
        if (pp->unregister_app)
-               pp->unregister_app(net, inc);
+               pp->unregister_app(ipvs, inc);
 
        IP_VS_DBG(9, "%s App %s:%u unregistered\n",
                  pp->name, inc->name, ntohs(inc->port));
@@ -175,14 +175,14 @@ void ip_vs_app_inc_put(struct ip_vs_app *inc)
  *     Register an application incarnation in protocol applications
  */
 int
-register_ip_vs_app_inc(struct net *net, struct ip_vs_app *app, __u16 proto,
+register_ip_vs_app_inc(struct netns_ipvs *ipvs, struct ip_vs_app *app, __u16 proto,
                       __u16 port)
 {
        int result;
 
        mutex_lock(&__ip_vs_app_mutex);
 
-       result = ip_vs_app_inc_new(net, app, proto, port);
+       result = ip_vs_app_inc_new(ipvs, app, proto, port);
 
        mutex_unlock(&__ip_vs_app_mutex);
 
@@ -191,15 +191,11 @@ register_ip_vs_app_inc(struct net *net, struct ip_vs_app *app, __u16 proto,
 
 
 /* Register application for netns */
-struct ip_vs_app *register_ip_vs_app(struct net *net, struct ip_vs_app *app)
+struct ip_vs_app *register_ip_vs_app(struct netns_ipvs *ipvs, struct ip_vs_app *app)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_app *a;
        int err = 0;
 
-       if (!ipvs)
-               return ERR_PTR(-ENOENT);
-
        mutex_lock(&__ip_vs_app_mutex);
 
        list_for_each_entry(a, &ipvs->app_list, a_list) {
@@ -230,21 +226,17 @@ out_unlock:
  *     We are sure there are no app incarnations attached to services
  *     Caller should use synchronize_rcu() or rcu_barrier()
  */
-void unregister_ip_vs_app(struct net *net, struct ip_vs_app *app)
+void unregister_ip_vs_app(struct netns_ipvs *ipvs, struct ip_vs_app *app)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_app *a, *anxt, *inc, *nxt;
 
-       if (!ipvs)
-               return;
-
        mutex_lock(&__ip_vs_app_mutex);
 
        list_for_each_entry_safe(a, anxt, &ipvs->app_list, a_list) {
                if (app && strcmp(app->name, a->name))
                        continue;
                list_for_each_entry_safe(inc, nxt, &a->incs_list, a_list) {
-                       ip_vs_app_inc_release(net, inc);
+                       ip_vs_app_inc_release(ipvs, inc);
                }
 
                list_del(&a->a_list);
@@ -611,17 +603,19 @@ static const struct file_operations ip_vs_app_fops = {
 };
 #endif
 
-int __net_init ip_vs_app_net_init(struct net *net)
+int __net_init ip_vs_app_net_init(struct netns_ipvs *ipvs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
+       struct net *net = ipvs->net;
 
        INIT_LIST_HEAD(&ipvs->app_list);
        proc_create("ip_vs_app", 0, net->proc_net, &ip_vs_app_fops);
        return 0;
 }
 
-void __net_exit ip_vs_app_net_cleanup(struct net *net)
+void __net_exit ip_vs_app_net_cleanup(struct netns_ipvs *ipvs)
 {
-       unregister_ip_vs_app(net, NULL /* all */);
+       struct net *net = ipvs->net;
+
+       unregister_ip_vs_app(ipvs, NULL /* all */);
        remove_proc_entry("ip_vs_app", net->proc_net);
 }
index b0f7b626b56da755222c0a1bd9a3a7b276ba32c8..85ca189bdc3d2b01ee4f202348fdf606f2585cf1 100644 (file)
@@ -108,7 +108,7 @@ static inline void ct_write_unlock_bh(unsigned int key)
 /*
  *     Returns hash value for IPVS connection entry
  */
-static unsigned int ip_vs_conn_hashkey(struct net *net, int af, unsigned int proto,
+static unsigned int ip_vs_conn_hashkey(struct netns_ipvs *ipvs, int af, unsigned int proto,
                                       const union nf_inet_addr *addr,
                                       __be16 port)
 {
@@ -116,11 +116,11 @@ static unsigned int ip_vs_conn_hashkey(struct net *net, int af, unsigned int pro
        if (af == AF_INET6)
                return (jhash_3words(jhash(addr, 16, ip_vs_conn_rnd),
                                    (__force u32)port, proto, ip_vs_conn_rnd) ^
-                       ((size_t)net>>8)) & ip_vs_conn_tab_mask;
+                       ((size_t)ipvs>>8)) & ip_vs_conn_tab_mask;
 #endif
        return (jhash_3words((__force u32)addr->ip, (__force u32)port, proto,
                            ip_vs_conn_rnd) ^
-               ((size_t)net>>8)) & ip_vs_conn_tab_mask;
+               ((size_t)ipvs>>8)) & ip_vs_conn_tab_mask;
 }
 
 static unsigned int ip_vs_conn_hashkey_param(const struct ip_vs_conn_param *p,
@@ -141,14 +141,14 @@ static unsigned int ip_vs_conn_hashkey_param(const struct ip_vs_conn_param *p,
                port = p->vport;
        }
 
-       return ip_vs_conn_hashkey(p->net, p->af, p->protocol, addr, port);
+       return ip_vs_conn_hashkey(p->ipvs, p->af, p->protocol, addr, port);
 }
 
 static unsigned int ip_vs_conn_hashkey_conn(const struct ip_vs_conn *cp)
 {
        struct ip_vs_conn_param p;
 
-       ip_vs_conn_fill_param(ip_vs_conn_net(cp), cp->af, cp->protocol,
+       ip_vs_conn_fill_param(cp->ipvs, cp->af, cp->protocol,
                              &cp->caddr, cp->cport, NULL, 0, &p);
 
        if (cp->pe) {
@@ -279,7 +279,7 @@ __ip_vs_conn_in_get(const struct ip_vs_conn_param *p)
                    ip_vs_addr_equal(p->af, p->vaddr, &cp->vaddr) &&
                    ((!p->cport) ^ (!(cp->flags & IP_VS_CONN_F_NO_CPORT))) &&
                    p->protocol == cp->protocol &&
-                   ip_vs_conn_net_eq(cp, p->net)) {
+                   cp->ipvs == p->ipvs) {
                        if (!__ip_vs_conn_get(cp))
                                continue;
                        /* HIT */
@@ -314,33 +314,34 @@ struct ip_vs_conn *ip_vs_conn_in_get(const struct ip_vs_conn_param *p)
 }
 
 static int
-ip_vs_conn_fill_param_proto(int af, const struct sk_buff *skb,
+ip_vs_conn_fill_param_proto(struct netns_ipvs *ipvs,
+                           int af, const struct sk_buff *skb,
                            const struct ip_vs_iphdr *iph,
-                           int inverse, struct ip_vs_conn_param *p)
+                           struct ip_vs_conn_param *p)
 {
        __be16 _ports[2], *pptr;
-       struct net *net = skb_net(skb);
 
        pptr = frag_safe_skb_hp(skb, iph->len, sizeof(_ports), _ports, iph);
        if (pptr == NULL)
                return 1;
 
-       if (likely(!inverse))
-               ip_vs_conn_fill_param(net, af, iph->protocol, &iph->saddr,
+       if (likely(!ip_vs_iph_inverse(iph)))
+               ip_vs_conn_fill_param(ipvs, af, iph->protocol, &iph->saddr,
                                      pptr[0], &iph->daddr, pptr[1], p);
        else
-               ip_vs_conn_fill_param(net, af, iph->protocol, &iph->daddr,
+               ip_vs_conn_fill_param(ipvs, af, iph->protocol, &iph->daddr,
                                      pptr[1], &iph->saddr, pptr[0], p);
        return 0;
 }
 
 struct ip_vs_conn *
-ip_vs_conn_in_get_proto(int af, const struct sk_buff *skb,
-                       const struct ip_vs_iphdr *iph, int inverse)
+ip_vs_conn_in_get_proto(struct netns_ipvs *ipvs, int af,
+                       const struct sk_buff *skb,
+                       const struct ip_vs_iphdr *iph)
 {
        struct ip_vs_conn_param p;
 
-       if (ip_vs_conn_fill_param_proto(af, skb, iph, inverse, &p))
+       if (ip_vs_conn_fill_param_proto(ipvs, af, skb, iph, &p))
                return NULL;
 
        return ip_vs_conn_in_get(&p);
@@ -359,7 +360,7 @@ struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p)
 
        hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) {
                if (unlikely(p->pe_data && p->pe->ct_match)) {
-                       if (!ip_vs_conn_net_eq(cp, p->net))
+                       if (cp->ipvs != p->ipvs)
                                continue;
                        if (p->pe == cp->pe && p->pe->ct_match(p, cp)) {
                                if (__ip_vs_conn_get(cp))
@@ -377,7 +378,7 @@ struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p)
                    p->vport == cp->vport && p->cport == cp->cport &&
                    cp->flags & IP_VS_CONN_F_TEMPLATE &&
                    p->protocol == cp->protocol &&
-                   ip_vs_conn_net_eq(cp, p->net)) {
+                   cp->ipvs == p->ipvs) {
                        if (__ip_vs_conn_get(cp))
                                goto out;
                }
@@ -418,7 +419,7 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p)
                    ip_vs_addr_equal(p->af, p->vaddr, &cp->caddr) &&
                    ip_vs_addr_equal(p->af, p->caddr, &cp->daddr) &&
                    p->protocol == cp->protocol &&
-                   ip_vs_conn_net_eq(cp, p->net)) {
+                   cp->ipvs == p->ipvs) {
                        if (!__ip_vs_conn_get(cp))
                                continue;
                        /* HIT */
@@ -439,12 +440,13 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p)
 }
 
 struct ip_vs_conn *
-ip_vs_conn_out_get_proto(int af, const struct sk_buff *skb,
-                        const struct ip_vs_iphdr *iph, int inverse)
+ip_vs_conn_out_get_proto(struct netns_ipvs *ipvs, int af,
+                        const struct sk_buff *skb,
+                        const struct ip_vs_iphdr *iph)
 {
        struct ip_vs_conn_param p;
 
-       if (ip_vs_conn_fill_param_proto(af, skb, iph, inverse, &p))
+       if (ip_vs_conn_fill_param_proto(ipvs, af, skb, iph, &p))
                return NULL;
 
        return ip_vs_conn_out_get(&p);
@@ -638,7 +640,7 @@ void ip_vs_try_bind_dest(struct ip_vs_conn *cp)
         * so we can make the assumption that the svc_af is the same as the
         * dest_af
         */
-       dest = ip_vs_find_dest(ip_vs_conn_net(cp), cp->af, cp->af, &cp->daddr,
+       dest = ip_vs_find_dest(cp->ipvs, cp->af, cp->af, &cp->daddr,
                               cp->dport, &cp->vaddr, cp->vport,
                               cp->protocol, cp->fwmark, cp->flags);
        if (dest) {
@@ -668,7 +670,7 @@ void ip_vs_try_bind_dest(struct ip_vs_conn *cp)
 #endif
                        ip_vs_bind_xmit(cp);
 
-               pd = ip_vs_proto_data_get(ip_vs_conn_net(cp), cp->protocol);
+               pd = ip_vs_proto_data_get(cp->ipvs, cp->protocol);
                if (pd && atomic_read(&pd->appcnt))
                        ip_vs_bind_app(cp, pd->pp);
        }
@@ -746,7 +748,7 @@ static int expire_quiescent_template(struct netns_ipvs *ipvs,
 int ip_vs_check_template(struct ip_vs_conn *ct)
 {
        struct ip_vs_dest *dest = ct->dest;
-       struct netns_ipvs *ipvs = net_ipvs(ip_vs_conn_net(ct));
+       struct netns_ipvs *ipvs = ct->ipvs;
 
        /*
         * Checking the dest server status.
@@ -800,8 +802,7 @@ static void ip_vs_conn_rcu_free(struct rcu_head *head)
 static void ip_vs_conn_expire(unsigned long data)
 {
        struct ip_vs_conn *cp = (struct ip_vs_conn *)data;
-       struct net *net = ip_vs_conn_net(cp);
-       struct netns_ipvs *ipvs = net_ipvs(net);
+       struct netns_ipvs *ipvs = cp->ipvs;
 
        /*
         *      do I control anybody?
@@ -847,7 +848,7 @@ static void ip_vs_conn_expire(unsigned long data)
        cp->timeout = 60*HZ;
 
        if (ipvs->sync_state & IP_VS_STATE_MASTER)
-               ip_vs_sync_conn(net, cp, sysctl_sync_threshold(ipvs));
+               ip_vs_sync_conn(ipvs, cp, sysctl_sync_threshold(ipvs));
 
        ip_vs_conn_put(cp);
 }
@@ -875,8 +876,8 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af,
               struct ip_vs_dest *dest, __u32 fwmark)
 {
        struct ip_vs_conn *cp;
-       struct netns_ipvs *ipvs = net_ipvs(p->net);
-       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(p->net,
+       struct netns_ipvs *ipvs = p->ipvs;
+       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(p->ipvs,
                                                           p->protocol);
 
        cp = kmem_cache_alloc(ip_vs_conn_cachep, GFP_ATOMIC);
@@ -887,7 +888,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af,
 
        INIT_HLIST_NODE(&cp->c_list);
        setup_timer(&cp->timer, ip_vs_conn_expire, (unsigned long)cp);
-       ip_vs_conn_net_set(cp, p->net);
+       cp->ipvs           = ipvs;
        cp->af             = p->af;
        cp->daf            = dest_af;
        cp->protocol       = p->protocol;
@@ -1061,7 +1062,7 @@ static int ip_vs_conn_seq_show(struct seq_file *seq, void *v)
                size_t len = 0;
                char dbuf[IP_VS_ADDRSTRLEN];
 
-               if (!ip_vs_conn_net_eq(cp, net))
+               if (!net_eq(cp->ipvs->net, net))
                        return 0;
                if (cp->pe_data) {
                        pe_data[0] = ' ';
@@ -1146,7 +1147,7 @@ static int ip_vs_conn_sync_seq_show(struct seq_file *seq, void *v)
                const struct ip_vs_conn *cp = v;
                struct net *net = seq_file_net(seq);
 
-               if (!ip_vs_conn_net_eq(cp, net))
+               if (!net_eq(cp->ipvs->net, net))
                        return 0;
 
 #ifdef CONFIG_IP_VS_IPV6
@@ -1240,7 +1241,7 @@ static inline int todrop_entry(struct ip_vs_conn *cp)
 }
 
 /* Called from keventd and must protect itself from softirqs */
-void ip_vs_random_dropentry(struct net *net)
+void ip_vs_random_dropentry(struct netns_ipvs *ipvs)
 {
        int idx;
        struct ip_vs_conn *cp, *cp_c;
@@ -1256,7 +1257,7 @@ void ip_vs_random_dropentry(struct net *net)
                        if (cp->flags & IP_VS_CONN_F_TEMPLATE)
                                /* connection template */
                                continue;
-                       if (!ip_vs_conn_net_eq(cp, net))
+                       if (cp->ipvs != ipvs)
                                continue;
                        if (cp->protocol == IPPROTO_TCP) {
                                switch(cp->state) {
@@ -1308,18 +1309,17 @@ void ip_vs_random_dropentry(struct net *net)
 /*
  *      Flush all the connection entries in the ip_vs_conn_tab
  */
-static void ip_vs_conn_flush(struct net *net)
+static void ip_vs_conn_flush(struct netns_ipvs *ipvs)
 {
        int idx;
        struct ip_vs_conn *cp, *cp_c;
-       struct netns_ipvs *ipvs = net_ipvs(net);
 
 flush_again:
        rcu_read_lock();
        for (idx = 0; idx < ip_vs_conn_tab_size; idx++) {
 
                hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[idx], c_list) {
-                       if (!ip_vs_conn_net_eq(cp, net))
+                       if (cp->ipvs != ipvs)
                                continue;
                        IP_VS_DBG(4, "del connection\n");
                        ip_vs_conn_expire_now(cp);
@@ -1345,23 +1345,22 @@ flush_again:
 /*
  * per netns init and exit
  */
-int __net_init ip_vs_conn_net_init(struct net *net)
+int __net_init ip_vs_conn_net_init(struct netns_ipvs *ipvs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
-
        atomic_set(&ipvs->conn_count, 0);
 
-       proc_create("ip_vs_conn", 0, net->proc_net, &ip_vs_conn_fops);
-       proc_create("ip_vs_conn_sync", 0, net->proc_net, &ip_vs_conn_sync_fops);
+       proc_create("ip_vs_conn", 0, ipvs->net->proc_net, &ip_vs_conn_fops);
+       proc_create("ip_vs_conn_sync", 0, ipvs->net->proc_net,
+                   &ip_vs_conn_sync_fops);
        return 0;
 }
 
-void __net_exit ip_vs_conn_net_cleanup(struct net *net)
+void __net_exit ip_vs_conn_net_cleanup(struct netns_ipvs *ipvs)
 {
        /* flush all the connection entries first */
-       ip_vs_conn_flush(net);
-       remove_proc_entry("ip_vs_conn", net->proc_net);
-       remove_proc_entry("ip_vs_conn_sync", net->proc_net);
+       ip_vs_conn_flush(ipvs);
+       remove_proc_entry("ip_vs_conn", ipvs->net->proc_net);
+       remove_proc_entry("ip_vs_conn_sync", ipvs->net->proc_net);
 }
 
 int __init ip_vs_conn_init(void)
index 38fbc194b9cb72835c497439713d23f16d99ca8c..1e24fff53e4b5eef8fab776b2e3dc93a3ae4545c 100644 (file)
@@ -112,7 +112,7 @@ static inline void
 ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
 {
        struct ip_vs_dest *dest = cp->dest;
-       struct netns_ipvs *ipvs = net_ipvs(skb_net(skb));
+       struct netns_ipvs *ipvs = cp->ipvs;
 
        if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
                struct ip_vs_cpu_stats *s;
@@ -146,7 +146,7 @@ static inline void
 ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
 {
        struct ip_vs_dest *dest = cp->dest;
-       struct netns_ipvs *ipvs = net_ipvs(skb_net(skb));
+       struct netns_ipvs *ipvs = cp->ipvs;
 
        if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
                struct ip_vs_cpu_stats *s;
@@ -179,7 +179,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
 static inline void
 ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
 {
-       struct netns_ipvs *ipvs = net_ipvs(svc->net);
+       struct netns_ipvs *ipvs = svc->ipvs;
        struct ip_vs_cpu_stats *s;
 
        s = this_cpu_ptr(cp->dest->stats.cpustats);
@@ -215,7 +215,7 @@ ip_vs_conn_fill_param_persist(const struct ip_vs_service *svc,
                              const union nf_inet_addr *vaddr, __be16 vport,
                              struct ip_vs_conn_param *p)
 {
-       ip_vs_conn_fill_param(svc->net, svc->af, protocol, caddr, cport, vaddr,
+       ip_vs_conn_fill_param(svc->ipvs, svc->af, protocol, caddr, cport, vaddr,
                              vport, p);
        p->pe = rcu_dereference(svc->pe);
        if (p->pe && p->pe->fill_param)
@@ -245,20 +245,30 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
        const union nf_inet_addr fwmark = { .ip = htonl(svc->fwmark) };
        union nf_inet_addr snet;        /* source network of the client,
                                           after masking */
+       const union nf_inet_addr *src_addr, *dst_addr;
+
+       if (likely(!ip_vs_iph_inverse(iph))) {
+               src_addr = &iph->saddr;
+               dst_addr = &iph->daddr;
+       } else {
+               src_addr = &iph->daddr;
+               dst_addr = &iph->saddr;
+       }
+
 
        /* Mask saddr with the netmask to adjust template granularity */
 #ifdef CONFIG_IP_VS_IPV6
        if (svc->af == AF_INET6)
-               ipv6_addr_prefix(&snet.in6, &iph->saddr.in6,
+               ipv6_addr_prefix(&snet.in6, &src_addr->in6,
                                 (__force __u32) svc->netmask);
        else
 #endif
-               snet.ip = iph->saddr.ip & svc->netmask;
+               snet.ip = src_addr->ip & svc->netmask;
 
        IP_VS_DBG_BUF(6, "p-schedule: src %s:%u dest %s:%u "
                      "mnet %s\n",
-                     IP_VS_DBG_ADDR(svc->af, &iph->saddr), ntohs(src_port),
-                     IP_VS_DBG_ADDR(svc->af, &iph->daddr), ntohs(dst_port),
+                     IP_VS_DBG_ADDR(svc->af, src_addr), ntohs(src_port),
+                     IP_VS_DBG_ADDR(svc->af, dst_addr), ntohs(dst_port),
                      IP_VS_DBG_ADDR(svc->af, &snet));
 
        /*
@@ -276,7 +286,7 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
         */
        {
                int protocol = iph->protocol;
-               const union nf_inet_addr *vaddr = &iph->daddr;
+               const union nf_inet_addr *vaddr = dst_addr;
                __be16 vport = 0;
 
                if (dst_port == svc->port) {
@@ -366,8 +376,8 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
        /*
         *    Create a new connection according to the template
         */
-       ip_vs_conn_fill_param(svc->net, svc->af, iph->protocol, &iph->saddr,
-                             src_port, &iph->daddr, dst_port, &param);
+       ip_vs_conn_fill_param(svc->ipvs, svc->af, iph->protocol, src_addr,
+                             src_port, dst_addr, dst_port, &param);
 
        cp = ip_vs_conn_new(&param, dest->af, &dest->addr, dport, flags, dest,
                            skb->mark);
@@ -418,7 +428,8 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
        struct ip_vs_conn *cp = NULL;
        struct ip_vs_scheduler *sched;
        struct ip_vs_dest *dest;
-       __be16 _ports[2], *pptr;
+       __be16 _ports[2], *pptr, cport, vport;
+       const void *caddr, *vaddr;
        unsigned int flags;
 
        *ignored = 1;
@@ -429,14 +440,26 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
        if (pptr == NULL)
                return NULL;
 
+       if (likely(!ip_vs_iph_inverse(iph))) {
+               cport = pptr[0];
+               caddr = &iph->saddr;
+               vport = pptr[1];
+               vaddr = &iph->daddr;
+       } else {
+               cport = pptr[1];
+               caddr = &iph->daddr;
+               vport = pptr[0];
+               vaddr = &iph->saddr;
+       }
+
        /*
         * FTPDATA needs this check when using local real server.
         * Never schedule Active FTPDATA connections from real server.
         * For LVS-NAT they must be already created. For other methods
         * with persistence the connection is created on SYN+ACK.
         */
-       if (pptr[0] == FTPDATA) {
-               IP_VS_DBG_PKT(12, svc->af, pp, skb, 0,
+       if (cport == FTPDATA) {
+               IP_VS_DBG_PKT(12, svc->af, pp, skb, iph->off,
                              "Not scheduling FTPDATA");
                return NULL;
        }
@@ -444,19 +467,25 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
        /*
         *    Do not schedule replies from local real server.
         */
-       if ((!skb->dev || skb->dev->flags & IFF_LOOPBACK) &&
-           (cp = pp->conn_in_get(svc->af, skb, iph, 1))) {
-               IP_VS_DBG_PKT(12, svc->af, pp, skb, 0,
-                             "Not scheduling reply for existing connection");
-               __ip_vs_conn_put(cp);
-               return NULL;
+       if ((!skb->dev || skb->dev->flags & IFF_LOOPBACK)) {
+               iph->hdr_flags ^= IP_VS_HDR_INVERSE;
+               cp = pp->conn_in_get(svc->ipvs, svc->af, skb, iph);
+               iph->hdr_flags ^= IP_VS_HDR_INVERSE;
+
+               if (cp) {
+                       IP_VS_DBG_PKT(12, svc->af, pp, skb, iph->off,
+                                     "Not scheduling reply for existing"
+                                     " connection");
+                       __ip_vs_conn_put(cp);
+                       return NULL;
+               }
        }
 
        /*
         *    Persistent service
         */
        if (svc->flags & IP_VS_SVC_F_PERSISTENT)
-               return ip_vs_sched_persist(svc, skb, pptr[0], pptr[1], ignored,
+               return ip_vs_sched_persist(svc, skb, cport, vport, ignored,
                                           iph);
 
        *ignored = 0;
@@ -464,7 +493,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
        /*
         *    Non-persistent service
         */
-       if (!svc->fwmark && pptr[1] != svc->port) {
+       if (!svc->fwmark && vport != svc->port) {
                if (!svc->port)
                        pr_err("Schedule: port zero only supported "
                               "in persistent services, "
@@ -495,11 +524,10 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
        {
                struct ip_vs_conn_param p;
 
-               ip_vs_conn_fill_param(svc->net, svc->af, iph->protocol,
-                                     &iph->saddr, pptr[0], &iph->daddr,
-                                     pptr[1], &p);
+               ip_vs_conn_fill_param(svc->ipvs, svc->af, iph->protocol,
+                                     caddr, cport, vaddr, vport, &p);
                cp = ip_vs_conn_new(&p, dest->af, &dest->addr,
-                                   dest->port ? dest->port : pptr[1],
+                                   dest->port ? dest->port : vport,
                                    flags, dest, skb->mark);
                if (!cp) {
                        *ignored = -1;
@@ -519,6 +547,15 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
        return cp;
 }
 
+static inline int ip_vs_addr_is_unicast(struct net *net, int af,
+                                       union nf_inet_addr *addr)
+{
+#ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+               return ipv6_addr_type(&addr->in6) & IPV6_ADDR_UNICAST;
+#endif
+       return (inet_addr_type(net, addr->ip) == RTN_UNICAST);
+}
 
 /*
  *  Pass or drop the packet.
@@ -528,33 +565,21 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
 int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
                struct ip_vs_proto_data *pd, struct ip_vs_iphdr *iph)
 {
-       __be16 _ports[2], *pptr;
-#ifdef CONFIG_SYSCTL
-       struct net *net;
-       struct netns_ipvs *ipvs;
-       int unicast;
-#endif
+       __be16 _ports[2], *pptr, dport;
+       struct netns_ipvs *ipvs = svc->ipvs;
+       struct net *net = ipvs->net;
 
        pptr = frag_safe_skb_hp(skb, iph->len, sizeof(_ports), _ports, iph);
-       if (pptr == NULL) {
+       if (!pptr)
                return NF_DROP;
-       }
-
-#ifdef CONFIG_SYSCTL
-       net = skb_net(skb);
-
-#ifdef CONFIG_IP_VS_IPV6
-       if (svc->af == AF_INET6)
-               unicast = ipv6_addr_type(&iph->daddr.in6) & IPV6_ADDR_UNICAST;
-       else
-#endif
-               unicast = (inet_addr_type(net, iph->daddr.ip) == RTN_UNICAST);
+       dport = likely(!ip_vs_iph_inverse(iph)) ? pptr[1] : pptr[0];
 
        /* if it is fwmark-based service, the cache_bypass sysctl is up
           and the destination is a non-local unicast, then create
           a cache_bypass connection entry */
-       ipvs = net_ipvs(net);
-       if (ipvs->sysctl_cache_bypass && svc->fwmark && unicast) {
+       if (sysctl_cache_bypass(ipvs) && svc->fwmark &&
+           !(iph->hdr_flags & (IP_VS_HDR_INVERSE | IP_VS_HDR_ICMP)) &&
+           ip_vs_addr_is_unicast(net, svc->af, &iph->daddr)) {
                int ret;
                struct ip_vs_conn *cp;
                unsigned int flags = (svc->flags & IP_VS_SVC_F_ONEPACKET &&
@@ -566,7 +591,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
                IP_VS_DBG(6, "%s(): create a cache_bypass entry\n", __func__);
                {
                        struct ip_vs_conn_param p;
-                       ip_vs_conn_fill_param(svc->net, svc->af, iph->protocol,
+                       ip_vs_conn_fill_param(svc->ipvs, svc->af, iph->protocol,
                                              &iph->saddr, pptr[0],
                                              &iph->daddr, pptr[1], &p);
                        cp = ip_vs_conn_new(&p, svc->af, &daddr, 0,
@@ -590,7 +615,6 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
                ip_vs_conn_put(cp);
                return ret;
        }
-#endif
 
        /*
         * When the virtual ftp service is presented, packets destined
@@ -598,9 +622,12 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
         * listed in the ipvs table), pass the packets, because it is
         * not ipvs job to decide to drop the packets.
         */
-       if ((svc->port == FTPPORT) && (pptr[1] != FTPPORT))
+       if (svc->port == FTPPORT && dport != FTPPORT)
                return NF_ACCEPT;
 
+       if (unlikely(ip_vs_iph_icmp(iph)))
+               return NF_DROP;
+
        /*
         * Notify the client that the destination is unreachable, and
         * release the socket buffer.
@@ -610,11 +637,8 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
         */
 #ifdef CONFIG_IP_VS_IPV6
        if (svc->af == AF_INET6) {
-               if (!skb->dev) {
-                       struct net *net_ = dev_net(skb_dst(skb)->dev);
-
-                       skb->dev = net_->loopback_dev;
-               }
+               if (!skb->dev)
+                       skb->dev = net->loopback_dev;
                icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
        } else
 #endif
@@ -625,15 +649,13 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
 
 #ifdef CONFIG_SYSCTL
 
-static int sysctl_snat_reroute(struct sk_buff *skb)
+static int sysctl_snat_reroute(struct netns_ipvs *ipvs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(skb_net(skb));
        return ipvs->sysctl_snat_reroute;
 }
 
-static int sysctl_nat_icmp_send(struct net *net)
+static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        return ipvs->sysctl_nat_icmp_send;
 }
 
@@ -644,8 +666,8 @@ static int sysctl_expire_nodest_conn(struct netns_ipvs *ipvs)
 
 #else
 
-static int sysctl_snat_reroute(struct sk_buff *skb) { return 0; }
-static int sysctl_nat_icmp_send(struct net *net) { return 0; }
+static int sysctl_snat_reroute(struct netns_ipvs *ipvs) { return 0; }
+static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; }
 static int sysctl_expire_nodest_conn(struct netns_ipvs *ipvs) { return 0; }
 
 #endif
@@ -664,12 +686,13 @@ static inline enum ip_defrag_users ip_vs_defrag_user(unsigned int hooknum)
        return IP_DEFRAG_VS_OUT;
 }
 
-static inline int ip_vs_gather_frags(struct sk_buff *skb, u_int32_t user)
+static inline int ip_vs_gather_frags(struct netns_ipvs *ipvs,
+                                    struct sk_buff *skb, u_int32_t user)
 {
        int err;
 
        local_bh_disable();
-       err = ip_defrag(skb, user);
+       err = ip_defrag(ipvs->net, skb, user);
        local_bh_enable();
        if (!err)
                ip_send_check(ip_hdr(skb));
@@ -677,10 +700,10 @@ static inline int ip_vs_gather_frags(struct sk_buff *skb, u_int32_t user)
        return err;
 }
 
-static int ip_vs_route_me_harder(int af, struct sk_buff *skb,
-                                unsigned int hooknum)
+static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
+                                struct sk_buff *skb, unsigned int hooknum)
 {
-       if (!sysctl_snat_reroute(skb))
+       if (!sysctl_snat_reroute(ipvs))
                return 0;
        /* Reroute replies only to remote clients (FORWARD and LOCAL_OUT) */
        if (NF_INET_LOCAL_IN == hooknum)
@@ -690,12 +713,12 @@ static int ip_vs_route_me_harder(int af, struct sk_buff *skb,
                struct dst_entry *dst = skb_dst(skb);
 
                if (dst->dev && !(dst->dev->flags & IFF_LOOPBACK) &&
-                   ip6_route_me_harder(skb) != 0)
+                   ip6_route_me_harder(ipvs->net, skb) != 0)
                        return 1;
        } else
 #endif
                if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL) &&
-                   ip_route_me_harder(skb, RTN_LOCAL) != 0)
+                   ip_route_me_harder(ipvs->net, skb, RTN_LOCAL) != 0)
                        return 1;
 
        return 0;
@@ -848,7 +871,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
 #endif
                ip_vs_nat_icmp(skb, pp, cp, 1);
 
-       if (ip_vs_route_me_harder(af, skb, hooknum))
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
                goto out;
 
        /* do the statistics and put it back */
@@ -872,8 +895,8 @@ out:
  *     Find any that might be relevant, check against existing connections.
  *     Currently handles error types - unreachable, quench, ttl exceeded.
  */
-static int ip_vs_out_icmp(struct sk_buff *skb, int *related,
-                         unsigned int hooknum)
+static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+                         int *related, unsigned int hooknum)
 {
        struct iphdr *iph;
        struct icmphdr  _icmph, *ic;
@@ -888,7 +911,7 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related,
 
        /* reassemble IP fragments */
        if (ip_is_fragment(ip_hdr(skb))) {
-               if (ip_vs_gather_frags(skb, ip_vs_defrag_user(hooknum)))
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
                        return NF_STOLEN;
        }
 
@@ -934,10 +957,10 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related,
        IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
                      "Checking outgoing ICMP for");
 
-       ip_vs_fill_ip4hdr(cih, &ciph);
-       ciph.len += offset;
+       ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, true, &ciph);
+
        /* The embedded headers contain source and dest in reverse order */
-       cp = pp->conn_out_get(AF_INET, skb, &ciph, 1);
+       cp = pp->conn_out_get(ipvs, AF_INET, skb, &ciph);
        if (!cp)
                return NF_ACCEPT;
 
@@ -947,16 +970,16 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related,
 }
 
 #ifdef CONFIG_IP_VS_IPV6
-static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related,
-                            unsigned int hooknum, struct ip_vs_iphdr *ipvsh)
+static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+                            int *related,  unsigned int hooknum,
+                            struct ip_vs_iphdr *ipvsh)
 {
        struct icmp6hdr _icmph, *ic;
-       struct ipv6hdr _ip6h, *ip6h; /* The ip header contained within ICMP */
        struct ip_vs_iphdr ciph = {.flags = 0, .fragoffs = 0};/*Contained IP */
        struct ip_vs_conn *cp;
        struct ip_vs_protocol *pp;
        union nf_inet_addr snet;
-       unsigned int writable;
+       unsigned int offset;
 
        *related = 1;
        ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph, ipvsh);
@@ -984,31 +1007,23 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related,
                  ic->icmp6_type, ntohs(icmpv6_id(ic)),
                  &ipvsh->saddr, &ipvsh->daddr);
 
-       /* Now find the contained IP header */
-       ciph.len = ipvsh->len + sizeof(_icmph);
-       ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h);
-       if (ip6h == NULL)
+       if (!ip_vs_fill_iph_skb_icmp(AF_INET6, skb, ipvsh->len + sizeof(_icmph),
+                                    true, &ciph))
                return NF_ACCEPT; /* The packet looks wrong, ignore */
-       ciph.saddr.in6 = ip6h->saddr; /* conn_out_get() handles reverse order */
-       ciph.daddr.in6 = ip6h->daddr;
-       /* skip possible IPv6 exthdrs of contained IPv6 packet */
-       ciph.protocol = ipv6_find_hdr(skb, &ciph.len, -1, &ciph.fragoffs, NULL);
-       if (ciph.protocol < 0)
-               return NF_ACCEPT; /* Contained IPv6 hdr looks wrong, ignore */
 
        pp = ip_vs_proto_get(ciph.protocol);
        if (!pp)
                return NF_ACCEPT;
 
        /* The embedded headers contain source and dest in reverse order */
-       cp = pp->conn_out_get(AF_INET6, skb, &ciph, 1);
+       cp = pp->conn_out_get(ipvs, AF_INET6, skb, &ciph);
        if (!cp)
                return NF_ACCEPT;
 
        snet.in6 = ciph.saddr.in6;
-       writable = ciph.len;
+       offset = ciph.len;
        return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
-                                   pp, writable, sizeof(struct ipv6hdr),
+                                   pp, offset, sizeof(struct ipv6hdr),
                                    hooknum);
 }
 #endif
@@ -1093,7 +1108,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
 {
        struct ip_vs_protocol *pp = pd->pp;
 
-       IP_VS_DBG_PKT(11, af, pp, skb, 0, "Outgoing packet");
+       IP_VS_DBG_PKT(11, af, pp, skb, iph->off, "Outgoing packet");
 
        if (!skb_make_writable(skb, iph->len))
                goto drop;
@@ -1127,10 +1142,10 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
         * if it came from this machine itself.  So re-compute
         * the routing information.
         */
-       if (ip_vs_route_me_harder(af, skb, hooknum))
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
                goto drop;
 
-       IP_VS_DBG_PKT(10, af, pp, skb, 0, "After SNAT");
+       IP_VS_DBG_PKT(10, af, pp, skb, iph->off, "After SNAT");
 
        ip_vs_out_stats(cp, skb);
        ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd);
@@ -1155,9 +1170,8 @@ drop:
  *     Check if outgoing packet belongs to the established ip_vs_conn.
  */
 static unsigned int
-ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
+ip_vs_out(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int af)
 {
-       struct net *net = NULL;
        struct ip_vs_iphdr iph;
        struct ip_vs_protocol *pp;
        struct ip_vs_proto_data *pd;
@@ -1182,16 +1196,15 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
        if (unlikely(!skb_dst(skb)))
                return NF_ACCEPT;
 
-       net = skb_net(skb);
-       if (!net_ipvs(net)->enable)
+       if (!ipvs->enable)
                return NF_ACCEPT;
 
-       ip_vs_fill_iph_skb(af, skb, &iph);
+       ip_vs_fill_iph_skb(af, skb, false, &iph);
 #ifdef CONFIG_IP_VS_IPV6
        if (af == AF_INET6) {
                if (unlikely(iph.protocol == IPPROTO_ICMPV6)) {
                        int related;
-                       int verdict = ip_vs_out_icmp_v6(skb, &related,
+                       int verdict = ip_vs_out_icmp_v6(ipvs, skb, &related,
                                                        hooknum, &iph);
 
                        if (related)
@@ -1201,13 +1214,13 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
 #endif
                if (unlikely(iph.protocol == IPPROTO_ICMP)) {
                        int related;
-                       int verdict = ip_vs_out_icmp(skb, &related, hooknum);
+                       int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum);
 
                        if (related)
                                return verdict;
                }
 
-       pd = ip_vs_proto_data_get(net, iph.protocol);
+       pd = ip_vs_proto_data_get(ipvs, iph.protocol);
        if (unlikely(!pd))
                return NF_ACCEPT;
        pp = pd->pp;
@@ -1217,21 +1230,21 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
        if (af == AF_INET)
 #endif
                if (unlikely(ip_is_fragment(ip_hdr(skb)) && !pp->dont_defrag)) {
-                       if (ip_vs_gather_frags(skb,
+                       if (ip_vs_gather_frags(ipvs, skb,
                                               ip_vs_defrag_user(hooknum)))
                                return NF_STOLEN;
 
-                       ip_vs_fill_ip4hdr(skb_network_header(skb), &iph);
+                       ip_vs_fill_iph_skb(AF_INET, skb, false, &iph);
                }
 
        /*
         * Check if the packet belongs to an existing entry
         */
-       cp = pp->conn_out_get(af, skb, &iph, 0);
+       cp = pp->conn_out_get(ipvs, af, skb, &iph);
 
        if (likely(cp))
                return handle_response(af, skb, pd, cp, &iph, hooknum);
-       if (sysctl_nat_icmp_send(net) &&
+       if (sysctl_nat_icmp_send(ipvs) &&
            (pp->protocol == IPPROTO_TCP ||
             pp->protocol == IPPROTO_UDP ||
             pp->protocol == IPPROTO_SCTP)) {
@@ -1241,7 +1254,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
                                         sizeof(_ports), _ports, &iph);
                if (pptr == NULL)
                        return NF_ACCEPT;       /* Not for me */
-               if (ip_vs_has_real_service(net, af, iph.protocol, &iph.saddr,
+               if (ip_vs_has_real_service(ipvs, af, iph.protocol, &iph.saddr,
                                           pptr[0])) {
                        /*
                         * Notify the real server: there is no
@@ -1258,7 +1271,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
 #ifdef CONFIG_IP_VS_IPV6
                                if (af == AF_INET6) {
                                        if (!skb->dev)
-                                               skb->dev = net->loopback_dev;
+                                               skb->dev = ipvs->net->loopback_dev;
                                        icmpv6_send(skb,
                                                    ICMPV6_DEST_UNREACH,
                                                    ICMPV6_PORT_UNREACH,
@@ -1272,7 +1285,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
                        }
                }
        }
-       IP_VS_DBG_PKT(12, af, pp, skb, 0,
+       IP_VS_DBG_PKT(12, af, pp, skb, iph.off,
                      "ip_vs_out: packet continues traversal as normal");
        return NF_ACCEPT;
 }
@@ -1283,10 +1296,10 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
  *     Check if packet is reply for established ip_vs_conn.
  */
 static unsigned int
-ip_vs_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip_vs_reply4(void *priv, struct sk_buff *skb,
             const struct nf_hook_state *state)
 {
-       return ip_vs_out(ops->hooknum, skb, AF_INET);
+       return ip_vs_out(net_ipvs(state->net), state->hook, skb, AF_INET);
 }
 
 /*
@@ -1294,10 +1307,10 @@ ip_vs_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb,
  *     Check if packet is reply for established ip_vs_conn.
  */
 static unsigned int
-ip_vs_local_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip_vs_local_reply4(void *priv, struct sk_buff *skb,
                   const struct nf_hook_state *state)
 {
-       return ip_vs_out(ops->hooknum, skb, AF_INET);
+       return ip_vs_out(net_ipvs(state->net), state->hook, skb, AF_INET);
 }
 
 #ifdef CONFIG_IP_VS_IPV6
@@ -1308,10 +1321,10 @@ ip_vs_local_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb,
  *     Check if packet is reply for established ip_vs_conn.
  */
 static unsigned int
-ip_vs_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip_vs_reply6(void *priv, struct sk_buff *skb,
             const struct nf_hook_state *state)
 {
-       return ip_vs_out(ops->hooknum, skb, AF_INET6);
+       return ip_vs_out(net_ipvs(state->net), state->hook, skb, AF_INET6);
 }
 
 /*
@@ -1319,14 +1332,51 @@ ip_vs_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb,
  *     Check if packet is reply for established ip_vs_conn.
  */
 static unsigned int
-ip_vs_local_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip_vs_local_reply6(void *priv, struct sk_buff *skb,
                   const struct nf_hook_state *state)
 {
-       return ip_vs_out(ops->hooknum, skb, AF_INET6);
+       return ip_vs_out(net_ipvs(state->net), state->hook, skb, AF_INET6);
 }
 
 #endif
 
+static unsigned int
+ip_vs_try_to_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+                     struct ip_vs_proto_data *pd,
+                     int *verdict, struct ip_vs_conn **cpp,
+                     struct ip_vs_iphdr *iph)
+{
+       struct ip_vs_protocol *pp = pd->pp;
+
+       if (!iph->fragoffs) {
+               /* No (second) fragments need to enter here, as nf_defrag_ipv6
+                * replayed fragment zero will already have created the cp
+                */
+
+               /* Schedule and create new connection entry into cpp */
+               if (!pp->conn_schedule(ipvs, af, skb, pd, verdict, cpp, iph))
+                       return 0;
+       }
+
+       if (unlikely(!*cpp)) {
+               /* sorry, all this trouble for a no-hit :) */
+               IP_VS_DBG_PKT(12, af, pp, skb, iph->off,
+                             "ip_vs_in: packet continues traversal as normal");
+               if (iph->fragoffs) {
+                       /* Fragment that couldn't be mapped to a conn entry
+                        * is missing module nf_defrag_ipv6
+                        */
+                       IP_VS_DBG_RL("Unhandled frag, load nf_defrag_ipv6\n");
+                       IP_VS_DBG_PKT(7, af, pp, skb, iph->off,
+                                     "unhandled fragment");
+               }
+               *verdict = NF_ACCEPT;
+               return 0;
+       }
+
+       return 1;
+}
+
 /*
  *     Handle ICMP messages in the outside-to-inside direction (incoming).
  *     Find any that might be relevant, check against existing connections,
@@ -1334,9 +1384,9 @@ ip_vs_local_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb,
  *     Currently handles error types - unreachable, quench, ttl exceeded.
  */
 static int
-ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
+ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+             unsigned int hooknum)
 {
-       struct net *net = NULL;
        struct iphdr *iph;
        struct icmphdr  _icmph, *ic;
        struct iphdr    _ciph, *cih;    /* The ip header contained within the ICMP */
@@ -1345,13 +1395,13 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
        struct ip_vs_protocol *pp;
        struct ip_vs_proto_data *pd;
        unsigned int offset, offset2, ihl, verdict;
-       bool ipip;
+       bool ipip, new_cp = false;
 
        *related = 1;
 
        /* reassemble IP fragments */
        if (ip_is_fragment(ip_hdr(skb))) {
-               if (ip_vs_gather_frags(skb, ip_vs_defrag_user(hooknum)))
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
                        return NF_STOLEN;
        }
 
@@ -1385,8 +1435,6 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
        if (cih == NULL)
                return NF_ACCEPT; /* The packet looks wrong, ignore */
 
-       net = skb_net(skb);
-
        /* Special case for errors for IPIP packets */
        ipip = false;
        if (cih->protocol == IPPROTO_IPIP) {
@@ -1402,7 +1450,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
                ipip = true;
        }
 
-       pd = ip_vs_proto_data_get(net, cih->protocol);
+       pd = ip_vs_proto_data_get(ipvs, cih->protocol);
        if (!pd)
                return NF_ACCEPT;
        pp = pd->pp;
@@ -1416,15 +1464,24 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
                      "Checking incoming ICMP for");
 
        offset2 = offset;
-       ip_vs_fill_ip4hdr(cih, &ciph);
-       ciph.len += offset;
+       ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !ipip, &ciph);
        offset = ciph.len;
+
        /* The embedded headers contain source and dest in reverse order.
         * For IPIP this is error for request, not for reply.
         */
-       cp = pp->conn_in_get(AF_INET, skb, &ciph, ipip ? 0 : 1);
-       if (!cp)
-               return NF_ACCEPT;
+       cp = pp->conn_in_get(ipvs, AF_INET, skb, &ciph);
+
+       if (!cp) {
+               int v;
+
+               if (!sysctl_schedule_icmp(ipvs))
+                       return NF_ACCEPT;
+
+               if (!ip_vs_try_to_schedule(ipvs, AF_INET, skb, pd, &v, &cp, &ciph))
+                       return v;
+               new_cp = true;
+       }
 
        verdict = NF_DROP;
 
@@ -1455,7 +1512,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
                        skb_reset_network_header(skb);
                        IP_VS_DBG(12, "ICMP for IPIP %pI4->%pI4: mtu=%u\n",
                                &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, mtu);
-                       ipv4_update_pmtu(skb, dev_net(skb->dev),
+                       ipv4_update_pmtu(skb, ipvs->net,
                                         mtu, 0, 0, 0, 0);
                        /* Client uses PMTUD? */
                        if (!(frag_off & htons(IP_DF)))
@@ -1501,23 +1558,26 @@ ignore_ipip:
        verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph);
 
 out:
-       __ip_vs_conn_put(cp);
+       if (likely(!new_cp))
+               __ip_vs_conn_put(cp);
+       else
+               ip_vs_conn_put(cp);
 
        return verdict;
 }
 
 #ifdef CONFIG_IP_VS_IPV6
-static int ip_vs_in_icmp_v6(struct sk_buff *skb, int *related,
-                           unsigned int hooknum, struct ip_vs_iphdr *iph)
+static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+                           int *related, unsigned int hooknum,
+                           struct ip_vs_iphdr *iph)
 {
-       struct net *net = NULL;
-       struct ipv6hdr _ip6h, *ip6h;
        struct icmp6hdr _icmph, *ic;
        struct ip_vs_iphdr ciph = {.flags = 0, .fragoffs = 0};/*Contained IP */
        struct ip_vs_conn *cp;
        struct ip_vs_protocol *pp;
        struct ip_vs_proto_data *pd;
-       unsigned int offs_ciph, writable, verdict;
+       unsigned int offset, verdict;
+       bool new_cp = false;
 
        *related = 1;
 
@@ -1546,21 +1606,11 @@ static int ip_vs_in_icmp_v6(struct sk_buff *skb, int *related,
                  ic->icmp6_type, ntohs(icmpv6_id(ic)),
                  &iph->saddr, &iph->daddr);
 
-       /* Now find the contained IP header */
-       ciph.len = iph->len + sizeof(_icmph);
-       offs_ciph = ciph.len; /* Save ip header offset */
-       ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h);
-       if (ip6h == NULL)
-               return NF_ACCEPT; /* The packet looks wrong, ignore */
-       ciph.saddr.in6 = ip6h->saddr; /* conn_in_get() handles reverse order */
-       ciph.daddr.in6 = ip6h->daddr;
-       /* skip possible IPv6 exthdrs of contained IPv6 packet */
-       ciph.protocol = ipv6_find_hdr(skb, &ciph.len, -1, &ciph.fragoffs, NULL);
-       if (ciph.protocol < 0)
-               return NF_ACCEPT; /* Contained IPv6 hdr looks wrong, ignore */
-
-       net = skb_net(skb);
-       pd = ip_vs_proto_data_get(net, ciph.protocol);
+       offset = iph->len + sizeof(_icmph);
+       if (!ip_vs_fill_iph_skb_icmp(AF_INET6, skb, offset, true, &ciph))
+               return NF_ACCEPT;
+
+       pd = ip_vs_proto_data_get(ipvs, ciph.protocol);
        if (!pd)
                return NF_ACCEPT;
        pp = pd->pp;
@@ -1569,36 +1619,49 @@ static int ip_vs_in_icmp_v6(struct sk_buff *skb, int *related,
        if (ciph.fragoffs)
                return NF_ACCEPT;
 
-       IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offs_ciph,
+       IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
                      "Checking incoming ICMPv6 for");
 
        /* The embedded headers contain source and dest in reverse order
         * if not from localhost
         */
-       cp = pp->conn_in_get(AF_INET6, skb, &ciph,
-                            (hooknum == NF_INET_LOCAL_OUT) ? 0 : 1);
+       cp = pp->conn_in_get(ipvs, AF_INET6, skb, &ciph);
+
+       if (!cp) {
+               int v;
+
+               if (!sysctl_schedule_icmp(ipvs))
+                       return NF_ACCEPT;
+
+               if (!ip_vs_try_to_schedule(ipvs, AF_INET6, skb, pd, &v, &cp, &ciph))
+                       return v;
+
+               new_cp = true;
+       }
 
-       if (!cp)
-               return NF_ACCEPT;
        /* VS/TUN, VS/DR and LOCALNODE just let it go */
        if ((hooknum == NF_INET_LOCAL_OUT) &&
            (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) {
-               __ip_vs_conn_put(cp);
-               return NF_ACCEPT;
+               verdict = NF_ACCEPT;
+               goto out;
        }
 
        /* do the statistics and put it back */
        ip_vs_in_stats(cp, skb);
 
        /* Need to mangle contained IPv6 header in ICMPv6 packet */
-       writable = ciph.len;
+       offset = ciph.len;
        if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol ||
            IPPROTO_SCTP == ciph.protocol)
-               writable += 2 * sizeof(__u16); /* Also mangle ports */
+               offset += 2 * sizeof(__u16); /* Also mangle ports */
 
-       verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, writable, hooknum, &ciph);
+       verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph);
 
-       __ip_vs_conn_put(cp);
+out:
+       if (likely(!new_cp))
+               __ip_vs_conn_put(cp);
+       else
+               ip_vs_conn_put(cp);
 
        return verdict;
 }
@@ -1610,15 +1673,13 @@ static int ip_vs_in_icmp_v6(struct sk_buff *skb, int *related,
  *     and send it on its way...
  */
 static unsigned int
-ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
+ip_vs_in(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int af)
 {
-       struct net *net;
        struct ip_vs_iphdr iph;
        struct ip_vs_protocol *pp;
        struct ip_vs_proto_data *pd;
        struct ip_vs_conn *cp;
        int ret, pkts;
-       struct netns_ipvs *ipvs;
        int conn_reuse_mode;
 
        /* Already marked as IPVS request or reply? */
@@ -1633,7 +1694,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
        if (unlikely((skb->pkt_type != PACKET_HOST &&
                      hooknum != NF_INET_LOCAL_OUT) ||
                     !skb_dst(skb))) {
-               ip_vs_fill_iph_skb(af, skb, &iph);
+               ip_vs_fill_iph_skb(af, skb, false, &iph);
                IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s"
                              " ignored in hook %u\n",
                              skb->pkt_type, iph.protocol,
@@ -1641,12 +1702,10 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
                return NF_ACCEPT;
        }
        /* ipvs enabled in this netns ? */
-       net = skb_net(skb);
-       ipvs = net_ipvs(net);
        if (unlikely(sysctl_backup_only(ipvs) || !ipvs->enable))
                return NF_ACCEPT;
 
-       ip_vs_fill_iph_skb(af, skb, &iph);
+       ip_vs_fill_iph_skb(af, skb, false, &iph);
 
        /* Bad... Do not break raw sockets */
        if (unlikely(skb->sk != NULL && hooknum == NF_INET_LOCAL_OUT &&
@@ -1662,8 +1721,8 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
        if (af == AF_INET6) {
                if (unlikely(iph.protocol == IPPROTO_ICMPV6)) {
                        int related;
-                       int verdict = ip_vs_in_icmp_v6(skb, &related, hooknum,
-                                                      &iph);
+                       int verdict = ip_vs_in_icmp_v6(ipvs, skb, &related,
+                                                      hooknum, &iph);
 
                        if (related)
                                return verdict;
@@ -1672,21 +1731,30 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
 #endif
                if (unlikely(iph.protocol == IPPROTO_ICMP)) {
                        int related;
-                       int verdict = ip_vs_in_icmp(skb, &related, hooknum);
+                       int verdict = ip_vs_in_icmp(ipvs, skb, &related,
+                                                   hooknum);
 
                        if (related)
                                return verdict;
                }
 
        /* Protocol supported? */
-       pd = ip_vs_proto_data_get(net, iph.protocol);
-       if (unlikely(!pd))
+       pd = ip_vs_proto_data_get(ipvs, iph.protocol);
+       if (unlikely(!pd)) {
+               /* The only way we'll see this packet again is if it's
+                * encapsulated, so mark it with ipvs_property=1 so we
+                * skip it if we're ignoring tunneled packets
+                */
+               if (sysctl_ignore_tunneled(ipvs))
+                       skb->ipvs_property = 1;
+
                return NF_ACCEPT;
+       }
        pp = pd->pp;
        /*
         * Check if the packet belongs to an existing connection entry
         */
-       cp = pp->conn_in_get(af, skb, &iph, 0);
+       cp = pp->conn_in_get(ipvs, af, skb, &iph);
 
        conn_reuse_mode = sysctl_conn_reuse_mode(ipvs);
        if (conn_reuse_mode && !iph.fragoffs &&
@@ -1700,32 +1768,15 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
                cp = NULL;
        }
 
-       if (unlikely(!cp) && !iph.fragoffs) {
-               /* No (second) fragments need to enter here, as nf_defrag_ipv6
-                * replayed fragment zero will already have created the cp
-                */
+       if (unlikely(!cp)) {
                int v;
 
-               /* Schedule and create new connection entry into &cp */
-               if (!pp->conn_schedule(af, skb, pd, &v, &cp, &iph))
+               if (!ip_vs_try_to_schedule(ipvs, af, skb, pd, &v, &cp, &iph))
                        return v;
        }
 
-       if (unlikely(!cp)) {
-               /* sorry, all this trouble for a no-hit :) */
-               IP_VS_DBG_PKT(12, af, pp, skb, 0,
-                             "ip_vs_in: packet continues traversal as normal");
-               if (iph.fragoffs) {
-                       /* Fragment that couldn't be mapped to a conn entry
-                        * is missing module nf_defrag_ipv6
-                        */
-                       IP_VS_DBG_RL("Unhandled frag, load nf_defrag_ipv6\n");
-                       IP_VS_DBG_PKT(7, af, pp, skb, 0, "unhandled fragment");
-               }
-               return NF_ACCEPT;
-       }
+       IP_VS_DBG_PKT(11, af, pp, skb, iph.off, "Incoming packet");
 
-       IP_VS_DBG_PKT(11, af, pp, skb, 0, "Incoming packet");
        /* Check the server status */
        if (cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) {
                /* the destination server is not available */
@@ -1765,7 +1816,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
                pkts = atomic_add_return(1, &cp->in_pkts);
 
        if (ipvs->sync_state & IP_VS_STATE_MASTER)
-               ip_vs_sync_conn(net, cp, pkts);
+               ip_vs_sync_conn(ipvs, cp, pkts);
 
        ip_vs_conn_put(cp);
        return ret;
@@ -1776,10 +1827,10 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
  *     Schedule and forward packets from remote clients
  */
 static unsigned int
-ip_vs_remote_request4(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip_vs_remote_request4(void *priv, struct sk_buff *skb,
                      const struct nf_hook_state *state)
 {
-       return ip_vs_in(ops->hooknum, skb, AF_INET);
+       return ip_vs_in(net_ipvs(state->net), state->hook, skb, AF_INET);
 }
 
 /*
@@ -1787,10 +1838,10 @@ ip_vs_remote_request4(const struct nf_hook_ops *ops, struct sk_buff *skb,
  *     Schedule and forward packets from local clients
  */
 static unsigned int
-ip_vs_local_request4(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip_vs_local_request4(void *priv, struct sk_buff *skb,
                     const struct nf_hook_state *state)
 {
-       return ip_vs_in(ops->hooknum, skb, AF_INET);
+       return ip_vs_in(net_ipvs(state->net), state->hook, skb, AF_INET);
 }
 
 #ifdef CONFIG_IP_VS_IPV6
@@ -1800,10 +1851,10 @@ ip_vs_local_request4(const struct nf_hook_ops *ops, struct sk_buff *skb,
  *     Schedule and forward packets from remote clients
  */
 static unsigned int
-ip_vs_remote_request6(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip_vs_remote_request6(void *priv, struct sk_buff *skb,
                      const struct nf_hook_state *state)
 {
-       return ip_vs_in(ops->hooknum, skb, AF_INET6);
+       return ip_vs_in(net_ipvs(state->net), state->hook, skb, AF_INET6);
 }
 
 /*
@@ -1811,10 +1862,10 @@ ip_vs_remote_request6(const struct nf_hook_ops *ops, struct sk_buff *skb,
  *     Schedule and forward packets from local clients
  */
 static unsigned int
-ip_vs_local_request6(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip_vs_local_request6(void *priv, struct sk_buff *skb,
                     const struct nf_hook_state *state)
 {
-       return ip_vs_in(ops->hooknum, skb, AF_INET6);
+       return ip_vs_in(net_ipvs(state->net), state->hook, skb, AF_INET6);
 }
 
 #endif
@@ -1830,46 +1881,40 @@ ip_vs_local_request6(const struct nf_hook_ops *ops, struct sk_buff *skb,
  *      and send them to ip_vs_in_icmp.
  */
 static unsigned int
-ip_vs_forward_icmp(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
                   const struct nf_hook_state *state)
 {
        int r;
-       struct net *net;
-       struct netns_ipvs *ipvs;
+       struct netns_ipvs *ipvs = net_ipvs(state->net);
 
        if (ip_hdr(skb)->protocol != IPPROTO_ICMP)
                return NF_ACCEPT;
 
        /* ipvs enabled in this netns ? */
-       net = skb_net(skb);
-       ipvs = net_ipvs(net);
        if (unlikely(sysctl_backup_only(ipvs) || !ipvs->enable))
                return NF_ACCEPT;
 
-       return ip_vs_in_icmp(skb, &r, ops->hooknum);
+       return ip_vs_in_icmp(ipvs, skb, &r, state->hook);
 }
 
 #ifdef CONFIG_IP_VS_IPV6
 static unsigned int
-ip_vs_forward_icmp_v6(const struct nf_hook_ops *ops, struct sk_buff *skb,
+ip_vs_forward_icmp_v6(void *priv, struct sk_buff *skb,
                      const struct nf_hook_state *state)
 {
        int r;
-       struct net *net;
-       struct netns_ipvs *ipvs;
+       struct netns_ipvs *ipvs = net_ipvs(state->net);
        struct ip_vs_iphdr iphdr;
 
-       ip_vs_fill_iph_skb(AF_INET6, skb, &iphdr);
+       ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr);
        if (iphdr.protocol != IPPROTO_ICMPV6)
                return NF_ACCEPT;
 
        /* ipvs enabled in this netns ? */
-       net = skb_net(skb);
-       ipvs = net_ipvs(net);
        if (unlikely(sysctl_backup_only(ipvs) || !ipvs->enable))
                return NF_ACCEPT;
 
-       return ip_vs_in_icmp_v6(skb, &r, ops->hooknum, &iphdr);
+       return ip_vs_in_icmp_v6(ipvs, skb, &r, state->hook, &iphdr);
 }
 #endif
 
@@ -1878,7 +1923,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
        /* After packet filtering, change source only for VS/NAT */
        {
                .hook           = ip_vs_reply4,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP_PRI_NAT_SRC - 2,
@@ -1888,7 +1932,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
         * applied to IPVS. */
        {
                .hook           = ip_vs_remote_request4,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP_PRI_NAT_SRC - 1,
@@ -1896,7 +1939,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
        /* Before ip_vs_in, change source only for VS/NAT */
        {
                .hook           = ip_vs_local_reply4,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_OUT,
                .priority       = NF_IP_PRI_NAT_DST + 1,
@@ -1904,7 +1946,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
        /* After mangle, schedule and forward local requests */
        {
                .hook           = ip_vs_local_request4,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_OUT,
                .priority       = NF_IP_PRI_NAT_DST + 2,
@@ -1913,7 +1954,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
         * destined for 0.0.0.0/0, which is for incoming IPVS connections */
        {
                .hook           = ip_vs_forward_icmp,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_FORWARD,
                .priority       = 99,
@@ -1921,7 +1961,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
        /* After packet filtering, change source only for VS/NAT */
        {
                .hook           = ip_vs_reply4,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV4,
                .hooknum        = NF_INET_FORWARD,
                .priority       = 100,
@@ -1930,7 +1969,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
        /* After packet filtering, change source only for VS/NAT */
        {
                .hook           = ip_vs_reply6,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP6_PRI_NAT_SRC - 2,
@@ -1940,7 +1978,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
         * applied to IPVS. */
        {
                .hook           = ip_vs_remote_request6,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority       = NF_IP6_PRI_NAT_SRC - 1,
@@ -1948,7 +1985,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
        /* Before ip_vs_in, change source only for VS/NAT */
        {
                .hook           = ip_vs_local_reply6,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_LOCAL_OUT,
                .priority       = NF_IP6_PRI_NAT_DST + 1,
@@ -1956,7 +1992,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
        /* After mangle, schedule and forward local requests */
        {
                .hook           = ip_vs_local_request6,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_LOCAL_OUT,
                .priority       = NF_IP6_PRI_NAT_DST + 2,
@@ -1965,7 +2000,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
         * destined for 0.0.0.0/0, which is for incoming IPVS connections */
        {
                .hook           = ip_vs_forward_icmp_v6,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_FORWARD,
                .priority       = 99,
@@ -1973,7 +2007,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
        /* After packet filtering, change source only for VS/NAT */
        {
                .hook           = ip_vs_reply6,
-               .owner          = THIS_MODULE,
                .pf             = NFPROTO_IPV6,
                .hooknum        = NF_INET_FORWARD,
                .priority       = 100,
@@ -1999,22 +2032,22 @@ static int __net_init __ip_vs_init(struct net *net)
        atomic_inc(&ipvs_netns_cnt);
        net->ipvs = ipvs;
 
-       if (ip_vs_estimator_net_init(net) < 0)
+       if (ip_vs_estimator_net_init(ipvs) < 0)
                goto estimator_fail;
 
-       if (ip_vs_control_net_init(net) < 0)
+       if (ip_vs_control_net_init(ipvs) < 0)
                goto control_fail;
 
-       if (ip_vs_protocol_net_init(net) < 0)
+       if (ip_vs_protocol_net_init(ipvs) < 0)
                goto protocol_fail;
 
-       if (ip_vs_app_net_init(net) < 0)
+       if (ip_vs_app_net_init(ipvs) < 0)
                goto app_fail;
 
-       if (ip_vs_conn_net_init(net) < 0)
+       if (ip_vs_conn_net_init(ipvs) < 0)
                goto conn_fail;
 
-       if (ip_vs_sync_net_init(net) < 0)
+       if (ip_vs_sync_net_init(ipvs) < 0)
                goto sync_fail;
 
        printk(KERN_INFO "IPVS: Creating netns size=%zu id=%d\n",
@@ -2025,15 +2058,15 @@ static int __net_init __ip_vs_init(struct net *net)
  */
 
 sync_fail:
-       ip_vs_conn_net_cleanup(net);
+       ip_vs_conn_net_cleanup(ipvs);
 conn_fail:
-       ip_vs_app_net_cleanup(net);
+       ip_vs_app_net_cleanup(ipvs);
 app_fail:
-       ip_vs_protocol_net_cleanup(net);
+       ip_vs_protocol_net_cleanup(ipvs);
 protocol_fail:
-       ip_vs_control_net_cleanup(net);
+       ip_vs_control_net_cleanup(ipvs);
 control_fail:
-       ip_vs_estimator_net_cleanup(net);
+       ip_vs_estimator_net_cleanup(ipvs);
 estimator_fail:
        net->ipvs = NULL;
        return -ENOMEM;
@@ -2041,22 +2074,25 @@ estimator_fail:
 
 static void __net_exit __ip_vs_cleanup(struct net *net)
 {
-       ip_vs_service_net_cleanup(net); /* ip_vs_flush() with locks */
-       ip_vs_conn_net_cleanup(net);
-       ip_vs_app_net_cleanup(net);
-       ip_vs_protocol_net_cleanup(net);
-       ip_vs_control_net_cleanup(net);
-       ip_vs_estimator_net_cleanup(net);
-       IP_VS_DBG(2, "ipvs netns %d released\n", net_ipvs(net)->gen);
+       struct netns_ipvs *ipvs = net_ipvs(net);
+
+       ip_vs_service_net_cleanup(ipvs);        /* ip_vs_flush() with locks */
+       ip_vs_conn_net_cleanup(ipvs);
+       ip_vs_app_net_cleanup(ipvs);
+       ip_vs_protocol_net_cleanup(ipvs);
+       ip_vs_control_net_cleanup(ipvs);
+       ip_vs_estimator_net_cleanup(ipvs);
+       IP_VS_DBG(2, "ipvs netns %d released\n", ipvs->gen);
        net->ipvs = NULL;
 }
 
 static void __net_exit __ip_vs_dev_cleanup(struct net *net)
 {
+       struct netns_ipvs *ipvs = net_ipvs(net);
        EnterFunction(2);
-       net_ipvs(net)->enable = 0;      /* Disable packet reception */
+       ipvs->enable = 0;       /* Disable packet reception */
        smp_wmb();
-       ip_vs_sync_net_cleanup(net);
+       ip_vs_sync_net_cleanup(ipvs);
        LeaveFunction(2);
 }
 
index 1a23e91d50d84c9eb9e5d0e03db8dcadb5e0979e..e7c1b052c2a3ac9cccd54ed1e4558d36bde080c4 100644 (file)
@@ -228,7 +228,7 @@ static void defense_work_handler(struct work_struct *work)
 
        update_defense_level(ipvs);
        if (atomic_read(&ipvs->dropentry))
-               ip_vs_random_dropentry(ipvs->net);
+               ip_vs_random_dropentry(ipvs);
        schedule_delayed_work(&ipvs->defense_work, DEFENSE_TIMER_PERIOD);
 }
 #endif
@@ -263,7 +263,7 @@ static struct hlist_head ip_vs_svc_fwm_table[IP_VS_SVC_TAB_SIZE];
  *     Returns hash value for virtual service
  */
 static inline unsigned int
-ip_vs_svc_hashkey(struct net *net, int af, unsigned int proto,
+ip_vs_svc_hashkey(struct netns_ipvs *ipvs, int af, unsigned int proto,
                  const union nf_inet_addr *addr, __be16 port)
 {
        register unsigned int porth = ntohs(port);
@@ -276,7 +276,7 @@ ip_vs_svc_hashkey(struct net *net, int af, unsigned int proto,
                            addr->ip6[2]^addr->ip6[3];
 #endif
        ahash = ntohl(addr_fold);
-       ahash ^= ((size_t) net >> 8);
+       ahash ^= ((size_t) ipvs >> 8);
 
        return (proto ^ ahash ^ (porth >> IP_VS_SVC_TAB_BITS) ^ porth) &
               IP_VS_SVC_TAB_MASK;
@@ -285,9 +285,9 @@ ip_vs_svc_hashkey(struct net *net, int af, unsigned int proto,
 /*
  *     Returns hash value of fwmark for virtual service lookup
  */
-static inline unsigned int ip_vs_svc_fwm_hashkey(struct net *net, __u32 fwmark)
+static inline unsigned int ip_vs_svc_fwm_hashkey(struct netns_ipvs *ipvs, __u32 fwmark)
 {
-       return (((size_t)net>>8) ^ fwmark) & IP_VS_SVC_TAB_MASK;
+       return (((size_t)ipvs>>8) ^ fwmark) & IP_VS_SVC_TAB_MASK;
 }
 
 /*
@@ -309,14 +309,14 @@ static int ip_vs_svc_hash(struct ip_vs_service *svc)
                /*
                 *  Hash it by <netns,protocol,addr,port> in ip_vs_svc_table
                 */
-               hash = ip_vs_svc_hashkey(svc->net, svc->af, svc->protocol,
+               hash = ip_vs_svc_hashkey(svc->ipvs, svc->af, svc->protocol,
                                         &svc->addr, svc->port);
                hlist_add_head_rcu(&svc->s_list, &ip_vs_svc_table[hash]);
        } else {
                /*
                 *  Hash it by fwmark in svc_fwm_table
                 */
-               hash = ip_vs_svc_fwm_hashkey(svc->net, svc->fwmark);
+               hash = ip_vs_svc_fwm_hashkey(svc->ipvs, svc->fwmark);
                hlist_add_head_rcu(&svc->f_list, &ip_vs_svc_fwm_table[hash]);
        }
 
@@ -357,21 +357,21 @@ static int ip_vs_svc_unhash(struct ip_vs_service *svc)
  *     Get service by {netns, proto,addr,port} in the service table.
  */
 static inline struct ip_vs_service *
-__ip_vs_service_find(struct net *net, int af, __u16 protocol,
+__ip_vs_service_find(struct netns_ipvs *ipvs, int af, __u16 protocol,
                     const union nf_inet_addr *vaddr, __be16 vport)
 {
        unsigned int hash;
        struct ip_vs_service *svc;
 
        /* Check for "full" addressed entries */
-       hash = ip_vs_svc_hashkey(net, af, protocol, vaddr, vport);
+       hash = ip_vs_svc_hashkey(ipvs, af, protocol, vaddr, vport);
 
        hlist_for_each_entry_rcu(svc, &ip_vs_svc_table[hash], s_list) {
                if ((svc->af == af)
                    && ip_vs_addr_equal(af, &svc->addr, vaddr)
                    && (svc->port == vport)
                    && (svc->protocol == protocol)
-                   && net_eq(svc->net, net)) {
+                   && (svc->ipvs == ipvs)) {
                        /* HIT */
                        return svc;
                }
@@ -385,17 +385,17 @@ __ip_vs_service_find(struct net *net, int af, __u16 protocol,
  *     Get service by {fwmark} in the service table.
  */
 static inline struct ip_vs_service *
-__ip_vs_svc_fwm_find(struct net *net, int af, __u32 fwmark)
+__ip_vs_svc_fwm_find(struct netns_ipvs *ipvs, int af, __u32 fwmark)
 {
        unsigned int hash;
        struct ip_vs_service *svc;
 
        /* Check for fwmark addressed entries */
-       hash = ip_vs_svc_fwm_hashkey(net, fwmark);
+       hash = ip_vs_svc_fwm_hashkey(ipvs, fwmark);
 
        hlist_for_each_entry_rcu(svc, &ip_vs_svc_fwm_table[hash], f_list) {
                if (svc->fwmark == fwmark && svc->af == af
-                   && net_eq(svc->net, net)) {
+                   && (svc->ipvs == ipvs)) {
                        /* HIT */
                        return svc;
                }
@@ -406,17 +406,16 @@ __ip_vs_svc_fwm_find(struct net *net, int af, __u32 fwmark)
 
 /* Find service, called under RCU lock */
 struct ip_vs_service *
-ip_vs_service_find(struct net *net, int af, __u32 fwmark, __u16 protocol,
+ip_vs_service_find(struct netns_ipvs *ipvs, int af, __u32 fwmark, __u16 protocol,
                   const union nf_inet_addr *vaddr, __be16 vport)
 {
        struct ip_vs_service *svc;
-       struct netns_ipvs *ipvs = net_ipvs(net);
 
        /*
         *      Check the table hashed by fwmark first
         */
        if (fwmark) {
-               svc = __ip_vs_svc_fwm_find(net, af, fwmark);
+               svc = __ip_vs_svc_fwm_find(ipvs, af, fwmark);
                if (svc)
                        goto out;
        }
@@ -425,7 +424,7 @@ ip_vs_service_find(struct net *net, int af, __u32 fwmark, __u16 protocol,
         *      Check the table hashed by <protocol,addr,port>
         *      for "full" addressed entries
         */
-       svc = __ip_vs_service_find(net, af, protocol, vaddr, vport);
+       svc = __ip_vs_service_find(ipvs, af, protocol, vaddr, vport);
 
        if (svc == NULL
            && protocol == IPPROTO_TCP
@@ -435,7 +434,7 @@ ip_vs_service_find(struct net *net, int af, __u32 fwmark, __u16 protocol,
                 * Check if ftp service entry exists, the packet
                 * might belong to FTP data connections.
                 */
-               svc = __ip_vs_service_find(net, af, protocol, vaddr, FTPPORT);
+               svc = __ip_vs_service_find(ipvs, af, protocol, vaddr, FTPPORT);
        }
 
        if (svc == NULL
@@ -443,7 +442,7 @@ ip_vs_service_find(struct net *net, int af, __u32 fwmark, __u16 protocol,
                /*
                 * Check if the catch-all port (port zero) exists
                 */
-               svc = __ip_vs_service_find(net, af, protocol, vaddr, 0);
+               svc = __ip_vs_service_find(ipvs, af, protocol, vaddr, 0);
        }
 
   out:
@@ -543,10 +542,9 @@ static void ip_vs_rs_unhash(struct ip_vs_dest *dest)
 }
 
 /* Check if real service by <proto,addr,port> is present */
-bool ip_vs_has_real_service(struct net *net, int af, __u16 protocol,
+bool ip_vs_has_real_service(struct netns_ipvs *ipvs, int af, __u16 protocol,
                            const union nf_inet_addr *daddr, __be16 dport)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        unsigned int hash;
        struct ip_vs_dest *dest;
 
@@ -601,7 +599,7 @@ ip_vs_lookup_dest(struct ip_vs_service *svc, int dest_af,
  * on the backup.
  * Called under RCU lock, no refcnt is returned.
  */
-struct ip_vs_dest *ip_vs_find_dest(struct net  *net, int svc_af, int dest_af,
+struct ip_vs_dest *ip_vs_find_dest(struct netns_ipvs *ipvs, int svc_af, int dest_af,
                                   const union nf_inet_addr *daddr,
                                   __be16 dport,
                                   const union nf_inet_addr *vaddr,
@@ -612,7 +610,7 @@ struct ip_vs_dest *ip_vs_find_dest(struct net  *net, int svc_af, int dest_af,
        struct ip_vs_service *svc;
        __be16 port = dport;
 
-       svc = ip_vs_service_find(net, svc_af, fwmark, protocol, vaddr, vport);
+       svc = ip_vs_service_find(ipvs, svc_af, fwmark, protocol, vaddr, vport);
        if (!svc)
                return NULL;
        if (fwmark && (flags & IP_VS_CONN_F_FWD_MASK) != IP_VS_CONN_F_MASQ)
@@ -660,7 +658,7 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, int dest_af,
                     const union nf_inet_addr *daddr, __be16 dport)
 {
        struct ip_vs_dest *dest;
-       struct netns_ipvs *ipvs = net_ipvs(svc->net);
+       struct netns_ipvs *ipvs = svc->ipvs;
 
        /*
         * Find the destination in trash
@@ -715,10 +713,9 @@ static void ip_vs_dest_free(struct ip_vs_dest *dest)
  *  are expired, and the refcnt of each destination in the trash must
  *  be 0, so we simply release them here.
  */
-static void ip_vs_trash_cleanup(struct net *net)
+static void ip_vs_trash_cleanup(struct netns_ipvs *ipvs)
 {
        struct ip_vs_dest *dest, *nxt;
-       struct netns_ipvs *ipvs = net_ipvs(net);
 
        del_timer_sync(&ipvs->dest_trash_timer);
        /* No need to use dest_trash_lock */
@@ -788,7 +785,7 @@ static void
 __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
                    struct ip_vs_dest_user_kern *udest, int add)
 {
-       struct netns_ipvs *ipvs = net_ipvs(svc->net);
+       struct netns_ipvs *ipvs = svc->ipvs;
        struct ip_vs_service *old_svc;
        struct ip_vs_scheduler *sched;
        int conn_flags;
@@ -843,7 +840,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
        spin_unlock_bh(&dest->dst_lock);
 
        if (add) {
-               ip_vs_start_estimator(svc->net, &dest->stats);
+               ip_vs_start_estimator(svc->ipvs, &dest->stats);
                list_add_rcu(&dest->n_list, &svc->destinations);
                svc->num_dests++;
                sched = rcu_dereference_protected(svc->scheduler, 1);
@@ -874,12 +871,12 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest,
                atype = ipv6_addr_type(&udest->addr.in6);
                if ((!(atype & IPV6_ADDR_UNICAST) ||
                        atype & IPV6_ADDR_LINKLOCAL) &&
-                       !__ip_vs_addr_is_local_v6(svc->net, &udest->addr.in6))
+                       !__ip_vs_addr_is_local_v6(svc->ipvs->net, &udest->addr.in6))
                        return -EINVAL;
        } else
 #endif
        {
-               atype = inet_addr_type(svc->net, udest->addr.ip);
+               atype = inet_addr_type(svc->ipvs->net, udest->addr.ip);
                if (atype != RTN_LOCAL && atype != RTN_UNICAST)
                        return -EINVAL;
        }
@@ -1036,12 +1033,10 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
 /*
  *     Delete a destination (must be already unlinked from the service)
  */
-static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest,
+static void __ip_vs_del_dest(struct netns_ipvs *ipvs, struct ip_vs_dest *dest,
                             bool cleanup)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
-
-       ip_vs_stop_estimator(net, &dest->stats);
+       ip_vs_stop_estimator(ipvs, &dest->stats);
 
        /*
         *  Remove it from the d-linked list with the real services.
@@ -1079,7 +1074,7 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc,
        svc->num_dests--;
 
        if (dest->af != svc->af)
-               net_ipvs(svc->net)->mixed_address_family_dests--;
+               svc->ipvs->mixed_address_family_dests--;
 
        if (svcupd) {
                struct ip_vs_scheduler *sched;
@@ -1120,7 +1115,7 @@ ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
        /*
         *      Delete the destination
         */
-       __ip_vs_del_dest(svc->net, dest, false);
+       __ip_vs_del_dest(svc->ipvs, dest, false);
 
        LeaveFunction(2);
 
@@ -1129,8 +1124,7 @@ ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
 
 static void ip_vs_dest_trash_expire(unsigned long data)
 {
-       struct net *net = (struct net *) data;
-       struct netns_ipvs *ipvs = net_ipvs(net);
+       struct netns_ipvs *ipvs = (struct netns_ipvs *)data;
        struct ip_vs_dest *dest, *next;
        unsigned long now = jiffies;
 
@@ -1163,14 +1157,13 @@ static void ip_vs_dest_trash_expire(unsigned long data)
  *     Add a service into the service hash table
  */
 static int
-ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u,
+ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
                  struct ip_vs_service **svc_p)
 {
        int ret = 0, i;
        struct ip_vs_scheduler *sched = NULL;
        struct ip_vs_pe *pe = NULL;
        struct ip_vs_service *svc = NULL;
-       struct netns_ipvs *ipvs = net_ipvs(net);
 
        /* increase the module use count */
        ip_vs_use_count_inc();
@@ -1237,7 +1230,7 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u,
        svc->flags = u->flags;
        svc->timeout = u->timeout * HZ;
        svc->netmask = u->netmask;
-       svc->net = net;
+       svc->ipvs = ipvs;
 
        INIT_LIST_HEAD(&svc->destinations);
        spin_lock_init(&svc->sched_lock);
@@ -1261,7 +1254,7 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u,
        else if (svc->port == 0)
                atomic_inc(&ipvs->nullsvc_counter);
 
-       ip_vs_start_estimator(net, &svc->stats);
+       ip_vs_start_estimator(ipvs, &svc->stats);
 
        /* Count only IPv4 services for old get/setsockopt interface */
        if (svc->af == AF_INET)
@@ -1381,7 +1374,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup)
        struct ip_vs_dest *dest, *nxt;
        struct ip_vs_scheduler *old_sched;
        struct ip_vs_pe *old_pe;
-       struct netns_ipvs *ipvs = net_ipvs(svc->net);
+       struct netns_ipvs *ipvs = svc->ipvs;
 
        pr_info("%s: enter\n", __func__);
 
@@ -1389,7 +1382,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup)
        if (svc->af == AF_INET)
                ipvs->num_services--;
 
-       ip_vs_stop_estimator(svc->net, &svc->stats);
+       ip_vs_stop_estimator(svc->ipvs, &svc->stats);
 
        /* Unbind scheduler */
        old_sched = rcu_dereference_protected(svc->scheduler, 1);
@@ -1405,7 +1398,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup)
         */
        list_for_each_entry_safe(dest, nxt, &svc->destinations, n_list) {
                __ip_vs_unlink_dest(svc, dest, 0);
-               __ip_vs_del_dest(svc->net, dest, cleanup);
+               __ip_vs_del_dest(svc->ipvs, dest, cleanup);
        }
 
        /*
@@ -1456,7 +1449,7 @@ static int ip_vs_del_service(struct ip_vs_service *svc)
 /*
  *     Flush all the virtual services
  */
-static int ip_vs_flush(struct net *net, bool cleanup)
+static int ip_vs_flush(struct netns_ipvs *ipvs, bool cleanup)
 {
        int idx;
        struct ip_vs_service *svc;
@@ -1468,7 +1461,7 @@ static int ip_vs_flush(struct net *net, bool cleanup)
        for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
                hlist_for_each_entry_safe(svc, n, &ip_vs_svc_table[idx],
                                          s_list) {
-                       if (net_eq(svc->net, net))
+                       if (svc->ipvs == ipvs)
                                ip_vs_unlink_service(svc, cleanup);
                }
        }
@@ -1479,7 +1472,7 @@ static int ip_vs_flush(struct net *net, bool cleanup)
        for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
                hlist_for_each_entry_safe(svc, n, &ip_vs_svc_fwm_table[idx],
                                          f_list) {
-                       if (net_eq(svc->net, net))
+                       if (svc->ipvs == ipvs)
                                ip_vs_unlink_service(svc, cleanup);
                }
        }
@@ -1491,12 +1484,12 @@ static int ip_vs_flush(struct net *net, bool cleanup)
  *     Delete service by {netns} in the service table.
  *     Called by __ip_vs_cleanup()
  */
-void ip_vs_service_net_cleanup(struct net *net)
+void ip_vs_service_net_cleanup(struct netns_ipvs *ipvs)
 {
        EnterFunction(2);
        /* Check for "full" addressed entries */
        mutex_lock(&__ip_vs_mutex);
-       ip_vs_flush(net, true);
+       ip_vs_flush(ipvs, true);
        mutex_unlock(&__ip_vs_mutex);
        LeaveFunction(2);
 }
@@ -1540,7 +1533,7 @@ static int ip_vs_dst_event(struct notifier_block *this, unsigned long event,
        mutex_lock(&__ip_vs_mutex);
        for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
                hlist_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
-                       if (net_eq(svc->net, net)) {
+                       if (svc->ipvs == ipvs) {
                                list_for_each_entry(dest, &svc->destinations,
                                                    n_list) {
                                        ip_vs_forget_dev(dest, dev);
@@ -1549,7 +1542,7 @@ static int ip_vs_dst_event(struct notifier_block *this, unsigned long event,
                }
 
                hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
-                       if (net_eq(svc->net, net)) {
+                       if (svc->ipvs == ipvs) {
                                list_for_each_entry(dest, &svc->destinations,
                                                    n_list) {
                                        ip_vs_forget_dev(dest, dev);
@@ -1583,26 +1576,26 @@ static int ip_vs_zero_service(struct ip_vs_service *svc)
        return 0;
 }
 
-static int ip_vs_zero_all(struct net *net)
+static int ip_vs_zero_all(struct netns_ipvs *ipvs)
 {
        int idx;
        struct ip_vs_service *svc;
 
        for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
                hlist_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
-                       if (net_eq(svc->net, net))
+                       if (svc->ipvs == ipvs)
                                ip_vs_zero_service(svc);
                }
        }
 
        for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
                hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
-                       if (net_eq(svc->net, net))
+                       if (svc->ipvs == ipvs)
                                ip_vs_zero_service(svc);
                }
        }
 
-       ip_vs_zero_stats(&net_ipvs(net)->tot_stats);
+       ip_vs_zero_stats(&ipvs->tot_stats);
        return 0;
 }
 
@@ -1615,7 +1608,7 @@ static int
 proc_do_defense_mode(struct ctl_table *table, int write,
                     void __user *buffer, size_t *lenp, loff_t *ppos)
 {
-       struct net *net = current->nsproxy->net_ns;
+       struct netns_ipvs *ipvs = table->extra2;
        int *valp = table->data;
        int val = *valp;
        int rc;
@@ -1626,7 +1619,7 @@ proc_do_defense_mode(struct ctl_table *table, int write,
                        /* Restore the correct value */
                        *valp = val;
                } else {
-                       update_defense_level(net_ipvs(net));
+                       update_defense_level(ipvs);
                }
        }
        return rc;
@@ -1844,6 +1837,18 @@ static struct ctl_table vs_vars[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
+       {
+               .procname       = "schedule_icmp",
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "ignore_tunneled",
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
 #ifdef CONFIG_IP_VS_DEBUG
        {
                .procname       = "debug_level",
@@ -1889,6 +1894,7 @@ static inline const char *ip_vs_fwd_name(unsigned int flags)
 static struct ip_vs_service *ip_vs_info_array(struct seq_file *seq, loff_t pos)
 {
        struct net *net = seq_file_net(seq);
+       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_iter *iter = seq->private;
        int idx;
        struct ip_vs_service *svc;
@@ -1896,7 +1902,7 @@ static struct ip_vs_service *ip_vs_info_array(struct seq_file *seq, loff_t pos)
        /* look in hash by protocol */
        for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
                hlist_for_each_entry_rcu(svc, &ip_vs_svc_table[idx], s_list) {
-                       if (net_eq(svc->net, net) && pos-- == 0) {
+                       if ((svc->ipvs == ipvs) && pos-- == 0) {
                                iter->table = ip_vs_svc_table;
                                iter->bucket = idx;
                                return svc;
@@ -1908,7 +1914,7 @@ static struct ip_vs_service *ip_vs_info_array(struct seq_file *seq, loff_t pos)
        for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
                hlist_for_each_entry_rcu(svc, &ip_vs_svc_fwm_table[idx],
                                         f_list) {
-                       if (net_eq(svc->net, net) && pos-- == 0) {
+                       if ((svc->ipvs == ipvs) && pos-- == 0) {
                                iter->table = ip_vs_svc_fwm_table;
                                iter->bucket = idx;
                                return svc;
@@ -2196,7 +2202,7 @@ static const struct file_operations ip_vs_stats_percpu_fops = {
 /*
  *     Set timeout values for tcp tcpfin udp in the timeout_table.
  */
-static int ip_vs_set_timeout(struct net *net, struct ip_vs_timeout_user *u)
+static int ip_vs_set_timeout(struct netns_ipvs *ipvs, struct ip_vs_timeout_user *u)
 {
 #if defined(CONFIG_IP_VS_PROTO_TCP) || defined(CONFIG_IP_VS_PROTO_UDP)
        struct ip_vs_proto_data *pd;
@@ -2209,13 +2215,13 @@ static int ip_vs_set_timeout(struct net *net, struct ip_vs_timeout_user *u)
 
 #ifdef CONFIG_IP_VS_PROTO_TCP
        if (u->tcp_timeout) {
-               pd = ip_vs_proto_data_get(net, IPPROTO_TCP);
+               pd = ip_vs_proto_data_get(ipvs, IPPROTO_TCP);
                pd->timeout_table[IP_VS_TCP_S_ESTABLISHED]
                        = u->tcp_timeout * HZ;
        }
 
        if (u->tcp_fin_timeout) {
-               pd = ip_vs_proto_data_get(net, IPPROTO_TCP);
+               pd = ip_vs_proto_data_get(ipvs, IPPROTO_TCP);
                pd->timeout_table[IP_VS_TCP_S_FIN_WAIT]
                        = u->tcp_fin_timeout * HZ;
        }
@@ -2223,7 +2229,7 @@ static int ip_vs_set_timeout(struct net *net, struct ip_vs_timeout_user *u)
 
 #ifdef CONFIG_IP_VS_PROTO_UDP
        if (u->udp_timeout) {
-               pd = ip_vs_proto_data_get(net, IPPROTO_UDP);
+               pd = ip_vs_proto_data_get(ipvs, IPPROTO_UDP);
                pd->timeout_table[IP_VS_UDP_S_NORMAL]
                        = u->udp_timeout * HZ;
        }
@@ -2344,12 +2350,12 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
                        cfg.syncid = dm->syncid;
                        rtnl_lock();
                        mutex_lock(&ipvs->sync_mutex);
-                       ret = start_sync_thread(net, &cfg, dm->state);
+                       ret = start_sync_thread(ipvs, &cfg, dm->state);
                        mutex_unlock(&ipvs->sync_mutex);
                        rtnl_unlock();
                } else {
                        mutex_lock(&ipvs->sync_mutex);
-                       ret = stop_sync_thread(net, dm->state);
+                       ret = stop_sync_thread(ipvs, dm->state);
                        mutex_unlock(&ipvs->sync_mutex);
                }
                goto out_dec;
@@ -2358,11 +2364,11 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
        mutex_lock(&__ip_vs_mutex);
        if (cmd == IP_VS_SO_SET_FLUSH) {
                /* Flush the virtual service */
-               ret = ip_vs_flush(net, false);
+               ret = ip_vs_flush(ipvs, false);
                goto out_unlock;
        } else if (cmd == IP_VS_SO_SET_TIMEOUT) {
                /* Set timeout values for (tcp tcpfin udp) */
-               ret = ip_vs_set_timeout(net, (struct ip_vs_timeout_user *)arg);
+               ret = ip_vs_set_timeout(ipvs, (struct ip_vs_timeout_user *)arg);
                goto out_unlock;
        }
 
@@ -2377,7 +2383,7 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
        if (cmd == IP_VS_SO_SET_ZERO) {
                /* if no service address is set, zero counters in all */
                if (!usvc.fwmark && !usvc.addr.ip && !usvc.port) {
-                       ret = ip_vs_zero_all(net);
+                       ret = ip_vs_zero_all(ipvs);
                        goto out_unlock;
                }
        }
@@ -2395,10 +2401,10 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
        /* Lookup the exact service by <protocol, addr, port> or fwmark */
        rcu_read_lock();
        if (usvc.fwmark == 0)
-               svc = __ip_vs_service_find(net, usvc.af, usvc.protocol,
+               svc = __ip_vs_service_find(ipvs, usvc.af, usvc.protocol,
                                           &usvc.addr, usvc.port);
        else
-               svc = __ip_vs_svc_fwm_find(net, usvc.af, usvc.fwmark);
+               svc = __ip_vs_svc_fwm_find(ipvs, usvc.af, usvc.fwmark);
        rcu_read_unlock();
 
        if (cmd != IP_VS_SO_SET_ADD
@@ -2412,7 +2418,7 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
                if (svc != NULL)
                        ret = -EEXIST;
                else
-                       ret = ip_vs_add_service(net, &usvc, &svc);
+                       ret = ip_vs_add_service(ipvs, &usvc, &svc);
                break;
        case IP_VS_SO_SET_EDIT:
                ret = ip_vs_edit_service(svc, &usvc);
@@ -2471,7 +2477,7 @@ ip_vs_copy_service(struct ip_vs_service_entry *dst, struct ip_vs_service *src)
 }
 
 static inline int
-__ip_vs_get_service_entries(struct net *net,
+__ip_vs_get_service_entries(struct netns_ipvs *ipvs,
                            const struct ip_vs_get_services *get,
                            struct ip_vs_get_services __user *uptr)
 {
@@ -2483,7 +2489,7 @@ __ip_vs_get_service_entries(struct net *net,
        for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
                hlist_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
                        /* Only expose IPv4 entries to old interface */
-                       if (svc->af != AF_INET || !net_eq(svc->net, net))
+                       if (svc->af != AF_INET || (svc->ipvs != ipvs))
                                continue;
 
                        if (count >= get->num_services)
@@ -2502,7 +2508,7 @@ __ip_vs_get_service_entries(struct net *net,
        for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
                hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
                        /* Only expose IPv4 entries to old interface */
-                       if (svc->af != AF_INET || !net_eq(svc->net, net))
+                       if (svc->af != AF_INET || (svc->ipvs != ipvs))
                                continue;
 
                        if (count >= get->num_services)
@@ -2522,7 +2528,7 @@ out:
 }
 
 static inline int
-__ip_vs_get_dest_entries(struct net *net, const struct ip_vs_get_dests *get,
+__ip_vs_get_dest_entries(struct netns_ipvs *ipvs, const struct ip_vs_get_dests *get,
                         struct ip_vs_get_dests __user *uptr)
 {
        struct ip_vs_service *svc;
@@ -2531,9 +2537,9 @@ __ip_vs_get_dest_entries(struct net *net, const struct ip_vs_get_dests *get,
 
        rcu_read_lock();
        if (get->fwmark)
-               svc = __ip_vs_svc_fwm_find(net, AF_INET, get->fwmark);
+               svc = __ip_vs_svc_fwm_find(ipvs, AF_INET, get->fwmark);
        else
-               svc = __ip_vs_service_find(net, AF_INET, get->protocol, &addr,
+               svc = __ip_vs_service_find(ipvs, AF_INET, get->protocol, &addr,
                                           get->port);
        rcu_read_unlock();
 
@@ -2578,7 +2584,7 @@ __ip_vs_get_dest_entries(struct net *net, const struct ip_vs_get_dests *get,
 }
 
 static inline void
-__ip_vs_get_timeouts(struct net *net, struct ip_vs_timeout_user *u)
+__ip_vs_get_timeouts(struct netns_ipvs *ipvs, struct ip_vs_timeout_user *u)
 {
 #if defined(CONFIG_IP_VS_PROTO_TCP) || defined(CONFIG_IP_VS_PROTO_UDP)
        struct ip_vs_proto_data *pd;
@@ -2587,12 +2593,12 @@ __ip_vs_get_timeouts(struct net *net, struct ip_vs_timeout_user *u)
        memset(u, 0, sizeof (*u));
 
 #ifdef CONFIG_IP_VS_PROTO_TCP
-       pd = ip_vs_proto_data_get(net, IPPROTO_TCP);
+       pd = ip_vs_proto_data_get(ipvs, IPPROTO_TCP);
        u->tcp_timeout = pd->timeout_table[IP_VS_TCP_S_ESTABLISHED] / HZ;
        u->tcp_fin_timeout = pd->timeout_table[IP_VS_TCP_S_FIN_WAIT] / HZ;
 #endif
 #ifdef CONFIG_IP_VS_PROTO_UDP
-       pd = ip_vs_proto_data_get(net, IPPROTO_UDP);
+       pd = ip_vs_proto_data_get(ipvs, IPPROTO_UDP);
        u->udp_timeout =
                        pd->timeout_table[IP_VS_UDP_S_NORMAL] / HZ;
 #endif
@@ -2711,7 +2717,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
                        ret = -EINVAL;
                        goto out;
                }
-               ret = __ip_vs_get_service_entries(net, get, user);
+               ret = __ip_vs_get_service_entries(ipvs, get, user);
        }
        break;
 
@@ -2725,9 +2731,9 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
                addr.ip = entry->addr;
                rcu_read_lock();
                if (entry->fwmark)
-                       svc = __ip_vs_svc_fwm_find(net, AF_INET, entry->fwmark);
+                       svc = __ip_vs_svc_fwm_find(ipvs, AF_INET, entry->fwmark);
                else
-                       svc = __ip_vs_service_find(net, AF_INET,
+                       svc = __ip_vs_service_find(ipvs, AF_INET,
                                                   entry->protocol, &addr,
                                                   entry->port);
                rcu_read_unlock();
@@ -2753,7 +2759,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
                        ret = -EINVAL;
                        goto out;
                }
-               ret = __ip_vs_get_dest_entries(net, get, user);
+               ret = __ip_vs_get_dest_entries(ipvs, get, user);
        }
        break;
 
@@ -2761,7 +2767,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
        {
                struct ip_vs_timeout_user t;
 
-               __ip_vs_get_timeouts(net, &t);
+               __ip_vs_get_timeouts(ipvs, &t);
                if (copy_to_user(user, &t, sizeof(t)) != 0)
                        ret = -EFAULT;
        }
@@ -2996,12 +3002,13 @@ static int ip_vs_genl_dump_services(struct sk_buff *skb,
        int idx = 0, i;
        int start = cb->args[0];
        struct ip_vs_service *svc;
-       struct net *net = skb_sknet(skb);
+       struct net *net = sock_net(skb->sk);
+       struct netns_ipvs *ipvs = net_ipvs(net);
 
        mutex_lock(&__ip_vs_mutex);
        for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
                hlist_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
-                       if (++idx <= start || !net_eq(svc->net, net))
+                       if (++idx <= start || (svc->ipvs != ipvs))
                                continue;
                        if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
                                idx--;
@@ -3012,7 +3019,7 @@ static int ip_vs_genl_dump_services(struct sk_buff *skb,
 
        for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
                hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
-                       if (++idx <= start || !net_eq(svc->net, net))
+                       if (++idx <= start || (svc->ipvs != ipvs))
                                continue;
                        if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
                                idx--;
@@ -3028,7 +3035,7 @@ nla_put_failure:
        return skb->len;
 }
 
-static int ip_vs_genl_parse_service(struct net *net,
+static int ip_vs_genl_parse_service(struct netns_ipvs *ipvs,
                                    struct ip_vs_service_user_kern *usvc,
                                    struct nlattr *nla, int full_entry,
                                    struct ip_vs_service **ret_svc)
@@ -3073,9 +3080,9 @@ static int ip_vs_genl_parse_service(struct net *net,
 
        rcu_read_lock();
        if (usvc->fwmark)
-               svc = __ip_vs_svc_fwm_find(net, usvc->af, usvc->fwmark);
+               svc = __ip_vs_svc_fwm_find(ipvs, usvc->af, usvc->fwmark);
        else
-               svc = __ip_vs_service_find(net, usvc->af, usvc->protocol,
+               svc = __ip_vs_service_find(ipvs, usvc->af, usvc->protocol,
                                           &usvc->addr, usvc->port);
        rcu_read_unlock();
        *ret_svc = svc;
@@ -3113,14 +3120,14 @@ static int ip_vs_genl_parse_service(struct net *net,
        return 0;
 }
 
-static struct ip_vs_service *ip_vs_genl_find_service(struct net *net,
+static struct ip_vs_service *ip_vs_genl_find_service(struct netns_ipvs *ipvs,
                                                     struct nlattr *nla)
 {
        struct ip_vs_service_user_kern usvc;
        struct ip_vs_service *svc;
        int ret;
 
-       ret = ip_vs_genl_parse_service(net, &usvc, nla, 0, &svc);
+       ret = ip_vs_genl_parse_service(ipvs, &usvc, nla, 0, &svc);
        return ret ? ERR_PTR(ret) : svc;
 }
 
@@ -3195,7 +3202,8 @@ static int ip_vs_genl_dump_dests(struct sk_buff *skb,
        struct ip_vs_service *svc;
        struct ip_vs_dest *dest;
        struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
-       struct net *net = skb_sknet(skb);
+       struct net *net = sock_net(skb->sk);
+       struct netns_ipvs *ipvs = net_ipvs(net);
 
        mutex_lock(&__ip_vs_mutex);
 
@@ -3205,7 +3213,7 @@ static int ip_vs_genl_dump_dests(struct sk_buff *skb,
                goto out_err;
 
 
-       svc = ip_vs_genl_find_service(net, attrs[IPVS_CMD_ATTR_SERVICE]);
+       svc = ip_vs_genl_find_service(ipvs, attrs[IPVS_CMD_ATTR_SERVICE]);
        if (IS_ERR(svc) || svc == NULL)
                goto out_err;
 
@@ -3341,7 +3349,7 @@ nla_put_failure:
 static int ip_vs_genl_dump_daemons(struct sk_buff *skb,
                                   struct netlink_callback *cb)
 {
-       struct net *net = skb_sknet(skb);
+       struct net *net = sock_net(skb->sk);
        struct netns_ipvs *ipvs = net_ipvs(net);
 
        mutex_lock(&ipvs->sync_mutex);
@@ -3367,9 +3375,8 @@ nla_put_failure:
        return skb->len;
 }
 
-static int ip_vs_genl_new_daemon(struct net *net, struct nlattr **attrs)
+static int ip_vs_genl_new_daemon(struct netns_ipvs *ipvs, struct nlattr **attrs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ipvs_sync_daemon_cfg c;
        struct nlattr *a;
        int ret;
@@ -3426,33 +3433,32 @@ static int ip_vs_genl_new_daemon(struct net *net, struct nlattr **attrs)
 
        rtnl_lock();
        mutex_lock(&ipvs->sync_mutex);
-       ret = start_sync_thread(net, &c,
+       ret = start_sync_thread(ipvs, &c,
                                nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
        mutex_unlock(&ipvs->sync_mutex);
        rtnl_unlock();
        return ret;
 }
 
-static int ip_vs_genl_del_daemon(struct net *net, struct nlattr **attrs)
+static int ip_vs_genl_del_daemon(struct netns_ipvs *ipvs, struct nlattr **attrs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        int ret;
 
        if (!attrs[IPVS_DAEMON_ATTR_STATE])
                return -EINVAL;
 
        mutex_lock(&ipvs->sync_mutex);
-       ret = stop_sync_thread(net,
+       ret = stop_sync_thread(ipvs,
                               nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
        mutex_unlock(&ipvs->sync_mutex);
        return ret;
 }
 
-static int ip_vs_genl_set_config(struct net *net, struct nlattr **attrs)
+static int ip_vs_genl_set_config(struct netns_ipvs *ipvs, struct nlattr **attrs)
 {
        struct ip_vs_timeout_user t;
 
-       __ip_vs_get_timeouts(net, &t);
+       __ip_vs_get_timeouts(ipvs, &t);
 
        if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
                t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
@@ -3464,17 +3470,15 @@ static int ip_vs_genl_set_config(struct net *net, struct nlattr **attrs)
        if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
                t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
 
-       return ip_vs_set_timeout(net, &t);
+       return ip_vs_set_timeout(ipvs, &t);
 }
 
 static int ip_vs_genl_set_daemon(struct sk_buff *skb, struct genl_info *info)
 {
        int ret = -EINVAL, cmd;
-       struct net *net;
-       struct netns_ipvs *ipvs;
+       struct net *net = sock_net(skb->sk);
+       struct netns_ipvs *ipvs = net_ipvs(net);
 
-       net = skb_sknet(skb);
-       ipvs = net_ipvs(net);
        cmd = info->genlhdr->cmd;
 
        if (cmd == IPVS_CMD_NEW_DAEMON || cmd == IPVS_CMD_DEL_DAEMON) {
@@ -3487,9 +3491,9 @@ static int ip_vs_genl_set_daemon(struct sk_buff *skb, struct genl_info *info)
                        goto out;
 
                if (cmd == IPVS_CMD_NEW_DAEMON)
-                       ret = ip_vs_genl_new_daemon(net, daemon_attrs);
+                       ret = ip_vs_genl_new_daemon(ipvs, daemon_attrs);
                else
-                       ret = ip_vs_genl_del_daemon(net, daemon_attrs);
+                       ret = ip_vs_genl_del_daemon(ipvs, daemon_attrs);
        }
 
 out:
@@ -3503,22 +3507,22 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
        struct ip_vs_dest_user_kern udest;
        int ret = 0, cmd;
        int need_full_svc = 0, need_full_dest = 0;
-       struct net *net;
+       struct net *net = sock_net(skb->sk);
+       struct netns_ipvs *ipvs = net_ipvs(net);
 
-       net = skb_sknet(skb);
        cmd = info->genlhdr->cmd;
 
        mutex_lock(&__ip_vs_mutex);
 
        if (cmd == IPVS_CMD_FLUSH) {
-               ret = ip_vs_flush(net, false);
+               ret = ip_vs_flush(ipvs, false);
                goto out;
        } else if (cmd == IPVS_CMD_SET_CONFIG) {
-               ret = ip_vs_genl_set_config(net, info->attrs);
+               ret = ip_vs_genl_set_config(ipvs, info->attrs);
                goto out;
        } else if (cmd == IPVS_CMD_ZERO &&
                   !info->attrs[IPVS_CMD_ATTR_SERVICE]) {
-               ret = ip_vs_zero_all(net);
+               ret = ip_vs_zero_all(ipvs);
                goto out;
        }
 
@@ -3528,7 +3532,7 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
        if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE)
                need_full_svc = 1;
 
-       ret = ip_vs_genl_parse_service(net, &usvc,
+       ret = ip_vs_genl_parse_service(ipvs, &usvc,
                                       info->attrs[IPVS_CMD_ATTR_SERVICE],
                                       need_full_svc, &svc);
        if (ret)
@@ -3567,7 +3571,7 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
                        /* The synchronization protocol is incompatible
                         * with mixed family services
                         */
-                       if (net_ipvs(net)->sync_state) {
+                       if (ipvs->sync_state) {
                                ret = -EINVAL;
                                goto out;
                        }
@@ -3587,7 +3591,7 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
        switch (cmd) {
        case IPVS_CMD_NEW_SERVICE:
                if (svc == NULL)
-                       ret = ip_vs_add_service(net, &usvc, &svc);
+                       ret = ip_vs_add_service(ipvs, &usvc, &svc);
                else
                        ret = -EEXIST;
                break;
@@ -3625,9 +3629,9 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
        struct sk_buff *msg;
        void *reply;
        int ret, cmd, reply_cmd;
-       struct net *net;
+       struct net *net = sock_net(skb->sk);
+       struct netns_ipvs *ipvs = net_ipvs(net);
 
-       net = skb_sknet(skb);
        cmd = info->genlhdr->cmd;
 
        if (cmd == IPVS_CMD_GET_SERVICE)
@@ -3656,7 +3660,7 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
        {
                struct ip_vs_service *svc;
 
-               svc = ip_vs_genl_find_service(net,
+               svc = ip_vs_genl_find_service(ipvs,
                                              info->attrs[IPVS_CMD_ATTR_SERVICE]);
                if (IS_ERR(svc)) {
                        ret = PTR_ERR(svc);
@@ -3677,7 +3681,7 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
        {
                struct ip_vs_timeout_user t;
 
-               __ip_vs_get_timeouts(net, &t);
+               __ip_vs_get_timeouts(ipvs, &t);
 #ifdef CONFIG_IP_VS_PROTO_TCP
                if (nla_put_u32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP,
                                t.tcp_timeout) ||
@@ -3832,10 +3836,10 @@ static void ip_vs_genl_unregister(void)
  * per netns intit/exit func.
  */
 #ifdef CONFIG_SYSCTL
-static int __net_init ip_vs_control_net_init_sysctl(struct net *net)
+static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs)
 {
+       struct net *net = ipvs->net;
        int idx;
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ctl_table *tbl;
 
        atomic_set(&ipvs->dropentry, 0);
@@ -3854,6 +3858,10 @@ static int __net_init ip_vs_control_net_init_sysctl(struct net *net)
        } else
                tbl = vs_vars;
        /* Initialize sysctl defaults */
+       for (idx = 0; idx < ARRAY_SIZE(vs_vars); idx++) {
+               if (tbl[idx].proc_handler == proc_do_defense_mode)
+                       tbl[idx].extra2 = ipvs;
+       }
        idx = 0;
        ipvs->sysctl_amemthresh = 1024;
        tbl[idx++].data = &ipvs->sysctl_amemthresh;
@@ -3895,7 +3903,8 @@ static int __net_init ip_vs_control_net_init_sysctl(struct net *net)
        tbl[idx++].data = &ipvs->sysctl_backup_only;
        ipvs->sysctl_conn_reuse_mode = 1;
        tbl[idx++].data = &ipvs->sysctl_conn_reuse_mode;
-
+       tbl[idx++].data = &ipvs->sysctl_schedule_icmp;
+       tbl[idx++].data = &ipvs->sysctl_ignore_tunneled;
 
        ipvs->sysctl_hdr = register_net_sysctl(net, "net/ipv4/vs", tbl);
        if (ipvs->sysctl_hdr == NULL) {
@@ -3903,7 +3912,7 @@ static int __net_init ip_vs_control_net_init_sysctl(struct net *net)
                        kfree(tbl);
                return -ENOMEM;
        }
-       ip_vs_start_estimator(net, &ipvs->tot_stats);
+       ip_vs_start_estimator(ipvs, &ipvs->tot_stats);
        ipvs->sysctl_tbl = tbl;
        /* Schedule defense work */
        INIT_DELAYED_WORK(&ipvs->defense_work, defense_work_handler);
@@ -3912,14 +3921,14 @@ static int __net_init ip_vs_control_net_init_sysctl(struct net *net)
        return 0;
 }
 
-static void __net_exit ip_vs_control_net_cleanup_sysctl(struct net *net)
+static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
+       struct net *net = ipvs->net;
 
        cancel_delayed_work_sync(&ipvs->defense_work);
        cancel_work_sync(&ipvs->defense_work.work);
        unregister_net_sysctl_table(ipvs->sysctl_hdr);
-       ip_vs_stop_estimator(net, &ipvs->tot_stats);
+       ip_vs_stop_estimator(ipvs, &ipvs->tot_stats);
 
        if (!net_eq(net, &init_net))
                kfree(ipvs->sysctl_tbl);
@@ -3927,8 +3936,8 @@ static void __net_exit ip_vs_control_net_cleanup_sysctl(struct net *net)
 
 #else
 
-static int __net_init ip_vs_control_net_init_sysctl(struct net *net) { return 0; }
-static void __net_exit ip_vs_control_net_cleanup_sysctl(struct net *net) { }
+static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs) { return 0; }
+static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs) { }
 
 #endif
 
@@ -3936,10 +3945,10 @@ static struct notifier_block ip_vs_dst_notifier = {
        .notifier_call = ip_vs_dst_event,
 };
 
-int __net_init ip_vs_control_net_init(struct net *net)
+int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs)
 {
+       struct net *net = ipvs->net;
        int i, idx;
-       struct netns_ipvs *ipvs = net_ipvs(net);
 
        /* Initialize rs_table */
        for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++)
@@ -3948,7 +3957,7 @@ int __net_init ip_vs_control_net_init(struct net *net)
        INIT_LIST_HEAD(&ipvs->dest_trash);
        spin_lock_init(&ipvs->dest_trash_lock);
        setup_timer(&ipvs->dest_trash_timer, ip_vs_dest_trash_expire,
-                   (unsigned long) net);
+                   (unsigned long) ipvs);
        atomic_set(&ipvs->ftpsvc_counter, 0);
        atomic_set(&ipvs->nullsvc_counter, 0);
 
@@ -3970,7 +3979,7 @@ int __net_init ip_vs_control_net_init(struct net *net)
        proc_create("ip_vs_stats_percpu", 0, net->proc_net,
                    &ip_vs_stats_percpu_fops);
 
-       if (ip_vs_control_net_init_sysctl(net))
+       if (ip_vs_control_net_init_sysctl(ipvs))
                goto err;
 
        return 0;
@@ -3980,12 +3989,12 @@ err:
        return -ENOMEM;
 }
 
-void __net_exit ip_vs_control_net_cleanup(struct net *net)
+void __net_exit ip_vs_control_net_cleanup(struct netns_ipvs *ipvs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
+       struct net *net = ipvs->net;
 
-       ip_vs_trash_cleanup(net);
-       ip_vs_control_net_cleanup_sysctl(net);
+       ip_vs_trash_cleanup(ipvs);
+       ip_vs_control_net_cleanup_sysctl(ipvs);
        remove_proc_entry("ip_vs_stats_percpu", net->proc_net);
        remove_proc_entry("ip_vs_stats", net->proc_net);
        remove_proc_entry("ip_vs", net->proc_net);
index ef0eb0a8d552944c6149559848e73c9fdd821700..457c6c193e1332ab803b6f381f1329f85f1ebdf6 100644 (file)
@@ -102,10 +102,8 @@ static void estimation_timer(unsigned long arg)
        struct ip_vs_estimator *e;
        struct ip_vs_stats *s;
        u64 rate;
-       struct net *net = (struct net *)arg;
-       struct netns_ipvs *ipvs;
+       struct netns_ipvs *ipvs = (struct netns_ipvs *)arg;
 
-       ipvs = net_ipvs(net);
        spin_lock(&ipvs->est_lock);
        list_for_each_entry(e, &ipvs->est_list, list) {
                s = container_of(e, struct ip_vs_stats, est);
@@ -140,9 +138,8 @@ static void estimation_timer(unsigned long arg)
        mod_timer(&ipvs->est_timer, jiffies + 2*HZ);
 }
 
-void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats)
+void ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_estimator *est = &stats->est;
 
        INIT_LIST_HEAD(&est->list);
@@ -152,9 +149,8 @@ void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats)
        spin_unlock_bh(&ipvs->est_lock);
 }
 
-void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats)
+void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_estimator *est = &stats->est;
 
        spin_lock_bh(&ipvs->est_lock);
@@ -192,18 +188,16 @@ void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats)
        dst->outbps = (e->outbps + 0xF) >> 5;
 }
 
-int __net_init ip_vs_estimator_net_init(struct net *net)
+int __net_init ip_vs_estimator_net_init(struct netns_ipvs *ipvs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
-
        INIT_LIST_HEAD(&ipvs->est_list);
        spin_lock_init(&ipvs->est_lock);
-       setup_timer(&ipvs->est_timer, estimation_timer, (unsigned long)net);
+       setup_timer(&ipvs->est_timer, estimation_timer, (unsigned long)ipvs);
        mod_timer(&ipvs->est_timer, jiffies + 2 * HZ);
        return 0;
 }
 
-void __net_exit ip_vs_estimator_net_cleanup(struct net *net)
+void __net_exit ip_vs_estimator_net_cleanup(struct netns_ipvs *ipvs)
 {
-       del_timer_sync(&net_ipvs(net)->est_timer);
+       del_timer_sync(&ipvs->est_timer);
 }
index 5d3daae98bf0be1bc0fa699ec96208d2fa3da1d4..d30c327bb578981c0868086cfddb6c304419b2e8 100644 (file)
@@ -181,7 +181,6 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
        int ret = 0;
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct;
-       struct net *net;
 
        *diff = 0;
 
@@ -223,14 +222,14 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
                 */
                {
                        struct ip_vs_conn_param p;
-                       ip_vs_conn_fill_param(ip_vs_conn_net(cp), AF_INET,
+                       ip_vs_conn_fill_param(cp->ipvs, AF_INET,
                                              iph->protocol, &from, port,
                                              &cp->caddr, 0, &p);
                        n_cp = ip_vs_conn_out_get(&p);
                }
                if (!n_cp) {
                        struct ip_vs_conn_param p;
-                       ip_vs_conn_fill_param(ip_vs_conn_net(cp),
+                       ip_vs_conn_fill_param(cp->ipvs,
                                              AF_INET, IPPROTO_TCP, &cp->caddr,
                                              0, &cp->vaddr, port, &p);
                        /* As above, this is ipv4 only */
@@ -289,9 +288,8 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
                 * would be adjusted twice.
                 */
 
-               net = skb_net(skb);
                cp->app_data = NULL;
-               ip_vs_tcp_conn_listen(net, n_cp);
+               ip_vs_tcp_conn_listen(n_cp);
                ip_vs_conn_put(n_cp);
                return ret;
        }
@@ -320,7 +318,6 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
        union nf_inet_addr to;
        __be16 port;
        struct ip_vs_conn *n_cp;
-       struct net *net;
 
        /* no diff required for incoming packets */
        *diff = 0;
@@ -392,7 +389,7 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
 
        {
                struct ip_vs_conn_param p;
-               ip_vs_conn_fill_param(ip_vs_conn_net(cp), AF_INET,
+               ip_vs_conn_fill_param(cp->ipvs, AF_INET,
                                      iph->protocol, &to, port, &cp->vaddr,
                                      htons(ntohs(cp->vport)-1), &p);
                n_cp = ip_vs_conn_in_get(&p);
@@ -413,8 +410,7 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
        /*
         *      Move tunnel to listen state
         */
-       net = skb_net(skb);
-       ip_vs_tcp_conn_listen(net, n_cp);
+       ip_vs_tcp_conn_listen(n_cp);
        ip_vs_conn_put(n_cp);
 
        return 1;
@@ -447,14 +443,14 @@ static int __net_init __ip_vs_ftp_init(struct net *net)
        if (!ipvs)
                return -ENOENT;
 
-       app = register_ip_vs_app(net, &ip_vs_ftp);
+       app = register_ip_vs_app(ipvs, &ip_vs_ftp);
        if (IS_ERR(app))
                return PTR_ERR(app);
 
        for (i = 0; i < ports_count; i++) {
                if (!ports[i])
                        continue;
-               ret = register_ip_vs_app_inc(net, app, app->protocol, ports[i]);
+               ret = register_ip_vs_app_inc(ipvs, app, app->protocol, ports[i]);
                if (ret)
                        goto err_unreg;
                pr_info("%s: loaded support on port[%d] = %d\n",
@@ -463,7 +459,7 @@ static int __net_init __ip_vs_ftp_init(struct net *net)
        return 0;
 
 err_unreg:
-       unregister_ip_vs_app(net, &ip_vs_ftp);
+       unregister_ip_vs_app(ipvs, &ip_vs_ftp);
        return ret;
 }
 /*
@@ -471,7 +467,12 @@ err_unreg:
  */
 static void __ip_vs_ftp_exit(struct net *net)
 {
-       unregister_ip_vs_app(net, &ip_vs_ftp);
+       struct netns_ipvs *ipvs = net_ipvs(net);
+
+       if (!ipvs)
+               return;
+
+       unregister_ip_vs_app(ipvs, &ip_vs_ftp);
 }
 
 static struct pernet_operations ip_vs_ftp_ops = {
index 127f14046c519d9aa0d0cb7596b8bcf676b5a55b..cccf4d637412ea076d3150ca5f80afbb6ac1d880 100644 (file)
@@ -250,8 +250,7 @@ static void ip_vs_lblc_flush(struct ip_vs_service *svc)
 static int sysctl_lblc_expiration(struct ip_vs_service *svc)
 {
 #ifdef CONFIG_SYSCTL
-       struct netns_ipvs *ipvs = net_ipvs(svc->net);
-       return ipvs->sysctl_lblc_expiration;
+       return svc->ipvs->sysctl_lblc_expiration;
 #else
        return DEFAULT_EXPIRATION;
 #endif
index 2229d2d8bbe0afe97e4d8fa4c2bfc95a6a93ee72..796d70e47dddfcd7f291df239ddab8270819cbe5 100644 (file)
@@ -415,8 +415,7 @@ static void ip_vs_lblcr_flush(struct ip_vs_service *svc)
 static int sysctl_lblcr_expiration(struct ip_vs_service *svc)
 {
 #ifdef CONFIG_SYSCTL
-       struct netns_ipvs *ipvs = net_ipvs(svc->net);
-       return ipvs->sysctl_lblcr_expiration;
+       return svc->ipvs->sysctl_lblcr_expiration;
 #else
        return DEFAULT_EXPIRATION;
 #endif
index 136184572fc9d274a5ef9493852c57045119ce5c..30434fb133df6b7522818de4cffa7e5ab2c8acc6 100644 (file)
@@ -161,7 +161,7 @@ static void ip_vs_nfct_expect_callback(struct nf_conn *ct,
 
        /* RS->CLIENT */
        orig = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
-       ip_vs_conn_fill_param(net, exp->tuple.src.l3num, orig->dst.protonum,
+       ip_vs_conn_fill_param(net_ipvs(net), exp->tuple.src.l3num, orig->dst.protonum,
                              &orig->src.u3, orig->src.u.tcp.port,
                              &orig->dst.u3, orig->dst.u.tcp.port, &p);
        cp = ip_vs_conn_out_get(&p);
@@ -274,8 +274,7 @@ void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp)
                " for conn " FMT_CONN "\n",
                __func__, ARG_TUPLE(&tuple), ARG_CONN(cp));
 
-       h = nf_conntrack_find_get(ip_vs_conn_net(cp), &nf_ct_zone_dflt,
-                                 &tuple);
+       h = nf_conntrack_find_get(cp->ipvs->net, &nf_ct_zone_dflt, &tuple);
        if (h) {
                ct = nf_ct_tuplehash_to_ctrack(h);
                /* Show what happens instead of calling nf_ct_kill() */
index bed5f7042529de23ef209806c7ad5607ac363a74..1b8d594e493a32f0ea461ade2d2a85387054a78d 100644 (file)
@@ -70,7 +70,7 @@ ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb)
        const char *dptr;
        int retc;
 
-       ip_vs_fill_iph_skb(p->af, skb, &iph);
+       ip_vs_fill_iph_skb(p->af, skb, false, &iph);
 
        /* Only useful with UDP */
        if (iph.protocol != IPPROTO_UDP)
index 939f7fbe9b46d832ffab36569e7e4b67f3a0bc68..8ae480715ceae79ae07fd4dbfc9bacf424b429b3 100644 (file)
@@ -63,9 +63,8 @@ static int __used __init register_ip_vs_protocol(struct ip_vs_protocol *pp)
  *     register an ipvs protocols netns related data
  */
 static int
-register_ip_vs_proto_netns(struct net *net, struct ip_vs_protocol *pp)
+register_ip_vs_proto_netns(struct netns_ipvs *ipvs, struct ip_vs_protocol *pp)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        unsigned int hash = IP_VS_PROTO_HASH(pp->protocol);
        struct ip_vs_proto_data *pd =
                        kzalloc(sizeof(struct ip_vs_proto_data), GFP_KERNEL);
@@ -79,7 +78,7 @@ register_ip_vs_proto_netns(struct net *net, struct ip_vs_protocol *pp)
        atomic_set(&pd->appcnt, 0);     /* Init app counter */
 
        if (pp->init_netns != NULL) {
-               int ret = pp->init_netns(net, pd);
+               int ret = pp->init_netns(ipvs, pd);
                if (ret) {
                        /* unlink an free proto data */
                        ipvs->proto_data_table[hash] = pd->next;
@@ -116,9 +115,8 @@ static int unregister_ip_vs_protocol(struct ip_vs_protocol *pp)
  *     unregister an ipvs protocols netns data
  */
 static int
-unregister_ip_vs_proto_netns(struct net *net, struct ip_vs_proto_data *pd)
+unregister_ip_vs_proto_netns(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_proto_data **pd_p;
        unsigned int hash = IP_VS_PROTO_HASH(pd->pp->protocol);
 
@@ -127,7 +125,7 @@ unregister_ip_vs_proto_netns(struct net *net, struct ip_vs_proto_data *pd)
                if (*pd_p == pd) {
                        *pd_p = pd->next;
                        if (pd->pp->exit_netns != NULL)
-                               pd->pp->exit_netns(net, pd);
+                               pd->pp->exit_netns(ipvs, pd);
                        kfree(pd);
                        return 0;
                }
@@ -156,8 +154,8 @@ EXPORT_SYMBOL(ip_vs_proto_get);
 /*
  *     get ip_vs_protocol object data by netns and proto
  */
-static struct ip_vs_proto_data *
-__ipvs_proto_data_get(struct netns_ipvs *ipvs, unsigned short proto)
+struct ip_vs_proto_data *
+ip_vs_proto_data_get(struct netns_ipvs *ipvs, unsigned short proto)
 {
        struct ip_vs_proto_data *pd;
        unsigned int hash = IP_VS_PROTO_HASH(proto);
@@ -169,14 +167,6 @@ __ipvs_proto_data_get(struct netns_ipvs *ipvs, unsigned short proto)
 
        return NULL;
 }
-
-struct ip_vs_proto_data *
-ip_vs_proto_data_get(struct net *net, unsigned short proto)
-{
-       struct netns_ipvs *ipvs = net_ipvs(net);
-
-       return __ipvs_proto_data_get(ipvs, proto);
-}
 EXPORT_SYMBOL(ip_vs_proto_data_get);
 
 /*
@@ -317,7 +307,7 @@ ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp,
 /*
  * per network name-space init
  */
-int __net_init ip_vs_protocol_net_init(struct net *net)
+int __net_init ip_vs_protocol_net_init(struct netns_ipvs *ipvs)
 {
        int i, ret;
        static struct ip_vs_protocol *protos[] = {
@@ -339,27 +329,26 @@ int __net_init ip_vs_protocol_net_init(struct net *net)
        };
 
        for (i = 0; i < ARRAY_SIZE(protos); i++) {
-               ret = register_ip_vs_proto_netns(net, protos[i]);
+               ret = register_ip_vs_proto_netns(ipvs, protos[i]);
                if (ret < 0)
                        goto cleanup;
        }
        return 0;
 
 cleanup:
-       ip_vs_protocol_net_cleanup(net);
+       ip_vs_protocol_net_cleanup(ipvs);
        return ret;
 }
 
-void __net_exit ip_vs_protocol_net_cleanup(struct net *net)
+void __net_exit ip_vs_protocol_net_cleanup(struct netns_ipvs *ipvs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_proto_data *pd;
        int i;
 
        /* unregister all the ipvs proto data for this netns */
        for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) {
                while ((pd = ipvs->proto_data_table[i]) != NULL)
-                       unregister_ip_vs_proto_netns(net, pd);
+                       unregister_ip_vs_proto_netns(ipvs, pd);
        }
 }
 
index 5de3dd312c0f77ad787ea037378cf1a9eeb5536a..5320d39976e1ddc90b5d5f2d997b60226b4ea0d3 100644 (file)
@@ -41,30 +41,28 @@ struct isakmp_hdr {
 #define PORT_ISAKMP    500
 
 static void
-ah_esp_conn_fill_param_proto(struct net *net, int af,
-                            const struct ip_vs_iphdr *iph, int inverse,
+ah_esp_conn_fill_param_proto(struct netns_ipvs *ipvs, int af,
+                            const struct ip_vs_iphdr *iph,
                             struct ip_vs_conn_param *p)
 {
-       if (likely(!inverse))
-               ip_vs_conn_fill_param(net, af, IPPROTO_UDP,
+       if (likely(!ip_vs_iph_inverse(iph)))
+               ip_vs_conn_fill_param(ipvs, af, IPPROTO_UDP,
                                      &iph->saddr, htons(PORT_ISAKMP),
                                      &iph->daddr, htons(PORT_ISAKMP), p);
        else
-               ip_vs_conn_fill_param(net, af, IPPROTO_UDP,
+               ip_vs_conn_fill_param(ipvs, af, IPPROTO_UDP,
                                      &iph->daddr, htons(PORT_ISAKMP),
                                      &iph->saddr, htons(PORT_ISAKMP), p);
 }
 
 static struct ip_vs_conn *
-ah_esp_conn_in_get(int af, const struct sk_buff *skb,
-                  const struct ip_vs_iphdr *iph,
-                  int inverse)
+ah_esp_conn_in_get(struct netns_ipvs *ipvs, int af, const struct sk_buff *skb,
+                  const struct ip_vs_iphdr *iph)
 {
        struct ip_vs_conn *cp;
        struct ip_vs_conn_param p;
-       struct net *net = skb_net(skb);
 
-       ah_esp_conn_fill_param_proto(net, af, iph, inverse, &p);
+       ah_esp_conn_fill_param_proto(ipvs, af, iph, &p);
        cp = ip_vs_conn_in_get(&p);
        if (!cp) {
                /*
@@ -73,7 +71,7 @@ ah_esp_conn_in_get(int af, const struct sk_buff *skb,
                 */
                IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for outin packet "
                              "%s%s %s->%s\n",
-                             inverse ? "ICMP+" : "",
+                             ip_vs_iph_icmp(iph) ? "ICMP+" : "",
                              ip_vs_proto_get(iph->protocol)->name,
                              IP_VS_DBG_ADDR(af, &iph->saddr),
                              IP_VS_DBG_ADDR(af, &iph->daddr));
@@ -84,19 +82,18 @@ ah_esp_conn_in_get(int af, const struct sk_buff *skb,
 
 
 static struct ip_vs_conn *
-ah_esp_conn_out_get(int af, const struct sk_buff *skb,
-                   const struct ip_vs_iphdr *iph, int inverse)
+ah_esp_conn_out_get(struct netns_ipvs *ipvs, int af, const struct sk_buff *skb,
+                   const struct ip_vs_iphdr *iph)
 {
        struct ip_vs_conn *cp;
        struct ip_vs_conn_param p;
-       struct net *net = skb_net(skb);
 
-       ah_esp_conn_fill_param_proto(net, af, iph, inverse, &p);
+       ah_esp_conn_fill_param_proto(ipvs, af, iph, &p);
        cp = ip_vs_conn_out_get(&p);
        if (!cp) {
                IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for inout packet "
                              "%s%s %s->%s\n",
-                             inverse ? "ICMP+" : "",
+                             ip_vs_iph_icmp(iph) ? "ICMP+" : "",
                              ip_vs_proto_get(iph->protocol)->name,
                              IP_VS_DBG_ADDR(af, &iph->saddr),
                              IP_VS_DBG_ADDR(af, &iph->daddr));
@@ -107,7 +104,8 @@ ah_esp_conn_out_get(int af, const struct sk_buff *skb,
 
 
 static int
-ah_esp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
+ah_esp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+                    struct ip_vs_proto_data *pd,
                     int *verdict, struct ip_vs_conn **cpp,
                     struct ip_vs_iphdr *iph)
 {
index 5b84c0b566424dce498c7ff85ee3b1060df4047f..010ddeec135f50950d2f9a356bc7211c351dde9a 100644 (file)
@@ -9,35 +9,44 @@
 #include <net/ip_vs.h>
 
 static int
-sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
+sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+                  struct ip_vs_proto_data *pd,
                   int *verdict, struct ip_vs_conn **cpp,
                   struct ip_vs_iphdr *iph)
 {
-       struct net *net;
        struct ip_vs_service *svc;
-       struct netns_ipvs *ipvs;
        sctp_chunkhdr_t _schunkh, *sch;
        sctp_sctphdr_t *sh, _sctph;
-
-       sh = skb_header_pointer(skb, iph->len, sizeof(_sctph), &_sctph);
-       if (sh == NULL) {
-               *verdict = NF_DROP;
-               return 0;
+       __be16 _ports[2], *ports = NULL;
+
+       if (likely(!ip_vs_iph_icmp(iph))) {
+               sh = skb_header_pointer(skb, iph->len, sizeof(_sctph), &_sctph);
+               if (sh) {
+                       sch = skb_header_pointer(
+                               skb, iph->len + sizeof(sctp_sctphdr_t),
+                               sizeof(_schunkh), &_schunkh);
+                       if (sch && (sch->type == SCTP_CID_INIT ||
+                                   sysctl_sloppy_sctp(ipvs)))
+                               ports = &sh->source;
+               }
+       } else {
+               ports = skb_header_pointer(
+                       skb, iph->len, sizeof(_ports), &_ports);
        }
 
-       sch = skb_header_pointer(skb, iph->len + sizeof(sctp_sctphdr_t),
-                                sizeof(_schunkh), &_schunkh);
-       if (sch == NULL) {
+       if (!ports) {
                *verdict = NF_DROP;
                return 0;
        }
 
-       net = skb_net(skb);
-       ipvs = net_ipvs(net);
        rcu_read_lock();
-       if ((sch->type == SCTP_CID_INIT || sysctl_sloppy_sctp(ipvs)) &&
-           (svc = ip_vs_service_find(net, af, skb->mark, iph->protocol,
-                                     &iph->daddr, sh->dest))) {
+       if (likely(!ip_vs_iph_inverse(iph)))
+               svc = ip_vs_service_find(ipvs, af, skb->mark, iph->protocol,
+                                        &iph->daddr, ports[1]);
+       else
+               svc = ip_vs_service_find(ipvs, af, skb->mark, iph->protocol,
+                                        &iph->saddr, ports[0]);
+       if (svc) {
                int ignored;
 
                if (ip_vs_todrop(ipvs)) {
@@ -474,14 +483,13 @@ static inline __u16 sctp_app_hashkey(__be16 port)
                & SCTP_APP_TAB_MASK;
 }
 
-static int sctp_register_app(struct net *net, struct ip_vs_app *inc)
+static int sctp_register_app(struct netns_ipvs *ipvs, struct ip_vs_app *inc)
 {
        struct ip_vs_app *i;
        __u16 hash;
        __be16 port = inc->port;
        int ret = 0;
-       struct netns_ipvs *ipvs = net_ipvs(net);
-       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_SCTP);
+       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(ipvs, IPPROTO_SCTP);
 
        hash = sctp_app_hashkey(port);
 
@@ -498,9 +506,9 @@ out:
        return ret;
 }
 
-static void sctp_unregister_app(struct net *net, struct ip_vs_app *inc)
+static void sctp_unregister_app(struct netns_ipvs *ipvs, struct ip_vs_app *inc)
 {
-       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_SCTP);
+       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(ipvs, IPPROTO_SCTP);
 
        atomic_dec(&pd->appcnt);
        list_del_rcu(&inc->p_list);
@@ -508,7 +516,7 @@ static void sctp_unregister_app(struct net *net, struct ip_vs_app *inc)
 
 static int sctp_app_conn_bind(struct ip_vs_conn *cp)
 {
-       struct netns_ipvs *ipvs = net_ipvs(ip_vs_conn_net(cp));
+       struct netns_ipvs *ipvs = cp->ipvs;
        int hash;
        struct ip_vs_app *inc;
        int result = 0;
@@ -549,10 +557,8 @@ out:
  *   timeouts is netns related now.
  * ---------------------------------------------
  */
-static int __ip_vs_sctp_init(struct net *net, struct ip_vs_proto_data *pd)
+static int __ip_vs_sctp_init(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
-
        ip_vs_init_hash_table(ipvs->sctp_apps, SCTP_APP_TAB_SIZE);
        pd->timeout_table = ip_vs_create_timeout_table((int *)sctp_timeouts,
                                                        sizeof(sctp_timeouts));
@@ -561,7 +567,7 @@ static int __ip_vs_sctp_init(struct net *net, struct ip_vs_proto_data *pd)
        return 0;
 }
 
-static void __ip_vs_sctp_exit(struct net *net, struct ip_vs_proto_data *pd)
+static void __ip_vs_sctp_exit(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd)
 {
        kfree(pd->timeout_table);
 }
index 8e92beb0cca9920238421af8a5b5206869c357e0..d7024b2ed769a274ec8320ef9650c777a1d7dcd4 100644 (file)
 #include <net/ip_vs.h>
 
 static int
-tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
+tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+                 struct ip_vs_proto_data *pd,
                  int *verdict, struct ip_vs_conn **cpp,
                  struct ip_vs_iphdr *iph)
 {
-       struct net *net;
        struct ip_vs_service *svc;
        struct tcphdr _tcph, *th;
-       struct netns_ipvs *ipvs;
+       __be16 _ports[2], *ports = NULL;
 
-       th = skb_header_pointer(skb, iph->len, sizeof(_tcph), &_tcph);
-       if (th == NULL) {
+       /* In the event of icmp, we're only guaranteed to have the first 8
+        * bytes of the transport header, so we only check the rest of the
+        * TCP packet for non-ICMP packets
+        */
+       if (likely(!ip_vs_iph_icmp(iph))) {
+               th = skb_header_pointer(skb, iph->len, sizeof(_tcph), &_tcph);
+               if (th) {
+                       if (th->rst || !(sysctl_sloppy_tcp(ipvs) || th->syn))
+                               return 1;
+                       ports = &th->source;
+               }
+       } else {
+               ports = skb_header_pointer(
+                       skb, iph->len, sizeof(_ports), &_ports);
+       }
+
+       if (!ports) {
                *verdict = NF_DROP;
                return 0;
        }
-       net = skb_net(skb);
-       ipvs = net_ipvs(net);
+
        /* No !th->ack check to allow scheduling on SYN+ACK for Active FTP */
        rcu_read_lock();
-       if ((th->syn || sysctl_sloppy_tcp(ipvs)) && !th->rst &&
-           (svc = ip_vs_service_find(net, af, skb->mark, iph->protocol,
-                                     &iph->daddr, th->dest))) {
+
+       if (likely(!ip_vs_iph_inverse(iph)))
+               svc = ip_vs_service_find(ipvs, af, skb->mark, iph->protocol,
+                                        &iph->daddr, ports[1]);
+       else
+               svc = ip_vs_service_find(ipvs, af, skb->mark, iph->protocol,
+                                        &iph->saddr, ports[0]);
+
+       if (svc) {
                int ignored;
 
                if (ip_vs_todrop(ipvs)) {
@@ -571,14 +591,13 @@ static inline __u16 tcp_app_hashkey(__be16 port)
 }
 
 
-static int tcp_register_app(struct net *net, struct ip_vs_app *inc)
+static int tcp_register_app(struct netns_ipvs *ipvs, struct ip_vs_app *inc)
 {
        struct ip_vs_app *i;
        __u16 hash;
        __be16 port = inc->port;
        int ret = 0;
-       struct netns_ipvs *ipvs = net_ipvs(net);
-       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_TCP);
+       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(ipvs, IPPROTO_TCP);
 
        hash = tcp_app_hashkey(port);
 
@@ -597,9 +616,9 @@ static int tcp_register_app(struct net *net, struct ip_vs_app *inc)
 
 
 static void
-tcp_unregister_app(struct net *net, struct ip_vs_app *inc)
+tcp_unregister_app(struct netns_ipvs *ipvs, struct ip_vs_app *inc)
 {
-       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_TCP);
+       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(ipvs, IPPROTO_TCP);
 
        atomic_dec(&pd->appcnt);
        list_del_rcu(&inc->p_list);
@@ -609,7 +628,7 @@ tcp_unregister_app(struct net *net, struct ip_vs_app *inc)
 static int
 tcp_app_conn_bind(struct ip_vs_conn *cp)
 {
-       struct netns_ipvs *ipvs = net_ipvs(ip_vs_conn_net(cp));
+       struct netns_ipvs *ipvs = cp->ipvs;
        int hash;
        struct ip_vs_app *inc;
        int result = 0;
@@ -653,9 +672,9 @@ tcp_app_conn_bind(struct ip_vs_conn *cp)
 /*
  *     Set LISTEN timeout. (ip_vs_conn_put will setup timer)
  */
-void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp)
+void ip_vs_tcp_conn_listen(struct ip_vs_conn *cp)
 {
-       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_TCP);
+       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(cp->ipvs, IPPROTO_TCP);
 
        spin_lock_bh(&cp->lock);
        cp->state = IP_VS_TCP_S_LISTEN;
@@ -668,10 +687,8 @@ void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp)
  *   timeouts is netns related now.
  * ---------------------------------------------
  */
-static int __ip_vs_tcp_init(struct net *net, struct ip_vs_proto_data *pd)
+static int __ip_vs_tcp_init(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
-
        ip_vs_init_hash_table(ipvs->tcp_apps, TCP_APP_TAB_SIZE);
        pd->timeout_table = ip_vs_create_timeout_table((int *)tcp_timeouts,
                                                        sizeof(tcp_timeouts));
@@ -681,7 +698,7 @@ static int __ip_vs_tcp_init(struct net *net, struct ip_vs_proto_data *pd)
        return 0;
 }
 
-static void __ip_vs_tcp_exit(struct net *net, struct ip_vs_proto_data *pd)
+static void __ip_vs_tcp_exit(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd)
 {
        kfree(pd->timeout_table);
 }
index b62a3c0ff9bf400817b20ec1ea4056586ed5d1dc..e494e9a88c7fb4d089845727bbc63778d352de4f 100644 (file)
 #include <net/ip6_checksum.h>
 
 static int
-udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
+udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+                 struct ip_vs_proto_data *pd,
                  int *verdict, struct ip_vs_conn **cpp,
                  struct ip_vs_iphdr *iph)
 {
-       struct net *net;
        struct ip_vs_service *svc;
        struct udphdr _udph, *uh;
+       __be16 _ports[2], *ports = NULL;
 
-       /* IPv6 fragments, only first fragment will hit this */
-       uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
-       if (uh == NULL) {
+       if (likely(!ip_vs_iph_icmp(iph))) {
+               /* IPv6 fragments, only first fragment will hit this */
+               uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
+               if (uh)
+                       ports = &uh->source;
+       } else {
+               ports = skb_header_pointer(
+                       skb, iph->len, sizeof(_ports), &_ports);
+       }
+
+       if (!ports) {
                *verdict = NF_DROP;
                return 0;
        }
-       net = skb_net(skb);
+
        rcu_read_lock();
-       svc = ip_vs_service_find(net, af, skb->mark, iph->protocol,
-                                &iph->daddr, uh->dest);
+       if (likely(!ip_vs_iph_inverse(iph)))
+               svc = ip_vs_service_find(ipvs, af, skb->mark, iph->protocol,
+                                        &iph->daddr, ports[1]);
+       else
+               svc = ip_vs_service_find(ipvs, af, skb->mark, iph->protocol,
+                                        &iph->saddr, ports[0]);
+
        if (svc) {
                int ignored;
 
-               if (ip_vs_todrop(net_ipvs(net))) {
+               if (ip_vs_todrop(ipvs)) {
                        /*
                         * It seems that we are very loaded.
                         * We have to drop this packet :(
@@ -348,14 +362,13 @@ static inline __u16 udp_app_hashkey(__be16 port)
 }
 
 
-static int udp_register_app(struct net *net, struct ip_vs_app *inc)
+static int udp_register_app(struct netns_ipvs *ipvs, struct ip_vs_app *inc)
 {
        struct ip_vs_app *i;
        __u16 hash;
        __be16 port = inc->port;
        int ret = 0;
-       struct netns_ipvs *ipvs = net_ipvs(net);
-       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_UDP);
+       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(ipvs, IPPROTO_UDP);
 
        hash = udp_app_hashkey(port);
 
@@ -374,9 +387,9 @@ static int udp_register_app(struct net *net, struct ip_vs_app *inc)
 
 
 static void
-udp_unregister_app(struct net *net, struct ip_vs_app *inc)
+udp_unregister_app(struct netns_ipvs *ipvs, struct ip_vs_app *inc)
 {
-       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_UDP);
+       struct ip_vs_proto_data *pd = ip_vs_proto_data_get(ipvs, IPPROTO_UDP);
 
        atomic_dec(&pd->appcnt);
        list_del_rcu(&inc->p_list);
@@ -385,7 +398,7 @@ udp_unregister_app(struct net *net, struct ip_vs_app *inc)
 
 static int udp_app_conn_bind(struct ip_vs_conn *cp)
 {
-       struct netns_ipvs *ipvs = net_ipvs(ip_vs_conn_net(cp));
+       struct netns_ipvs *ipvs = cp->ipvs;
        int hash;
        struct ip_vs_app *inc;
        int result = 0;
@@ -456,10 +469,8 @@ udp_state_transition(struct ip_vs_conn *cp, int direction,
        cp->timeout = pd->timeout_table[IP_VS_UDP_S_NORMAL];
 }
 
-static int __udp_init(struct net *net, struct ip_vs_proto_data *pd)
+static int __udp_init(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
-
        ip_vs_init_hash_table(ipvs->udp_apps, UDP_APP_TAB_SIZE);
        pd->timeout_table = ip_vs_create_timeout_table((int *)udp_timeouts,
                                                        sizeof(udp_timeouts));
@@ -468,7 +479,7 @@ static int __udp_init(struct net *net, struct ip_vs_proto_data *pd)
        return 0;
 }
 
-static void __udp_exit(struct net *net, struct ip_vs_proto_data *pd)
+static void __udp_exit(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd)
 {
        kfree(pd->timeout_table);
 }
index 98a13433b68c226fee24e3421d5c46c5f0cc8a75..1e373a5e44e34cf172f3843b1fcc700ed3a57cbc 100644 (file)
@@ -280,35 +280,29 @@ static int ip_vs_sh_dest_changed(struct ip_vs_service *svc,
 static inline __be16
 ip_vs_sh_get_port(const struct sk_buff *skb, struct ip_vs_iphdr *iph)
 {
-       __be16 port;
-       struct tcphdr _tcph, *th;
-       struct udphdr _udph, *uh;
-       sctp_sctphdr_t _sctph, *sh;
+       __be16 _ports[2], *ports;
 
+       /* At this point we know that we have a valid packet of some kind.
+        * Because ICMP packets are only guaranteed to have the first 8
+        * bytes, let's just grab the ports.  Fortunately they're in the
+        * same position for all three of the protocols we care about.
+        */
        switch (iph->protocol) {
        case IPPROTO_TCP:
-               th = skb_header_pointer(skb, iph->len, sizeof(_tcph), &_tcph);
-               if (unlikely(th == NULL))
-                       return 0;
-               port = th->source;
-               break;
        case IPPROTO_UDP:
-               uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
-               if (unlikely(uh == NULL))
-                       return 0;
-               port = uh->source;
-               break;
        case IPPROTO_SCTP:
-               sh = skb_header_pointer(skb, iph->len, sizeof(_sctph), &_sctph);
-               if (unlikely(sh == NULL))
+               ports = skb_header_pointer(skb, iph->len, sizeof(_ports),
+                                          &_ports);
+               if (unlikely(!ports))
                        return 0;
-               port = sh->source;
-               break;
+
+               if (likely(!ip_vs_iph_inverse(iph)))
+                       return ports[0];
+               else
+                       return ports[1];
        default:
-               port = 0;
+               return 0;
        }
-
-       return port;
 }
 
 
@@ -322,6 +316,9 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
        struct ip_vs_dest *dest;
        struct ip_vs_sh_state *s;
        __be16 port = 0;
+       const union nf_inet_addr *hash_addr;
+
+       hash_addr = ip_vs_iph_inverse(iph) ? &iph->daddr : &iph->saddr;
 
        IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n");
 
@@ -331,9 +328,9 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
        s = (struct ip_vs_sh_state *) svc->sched_data;
 
        if (svc->flags & IP_VS_SVC_F_SCHED_SH_FALLBACK)
-               dest = ip_vs_sh_get_fallback(svc, s, &iph->saddr, port);
+               dest = ip_vs_sh_get_fallback(svc, s, hash_addr, port);
        else
-               dest = ip_vs_sh_get(svc, s, &iph->saddr, port);
+               dest = ip_vs_sh_get(svc, s, hash_addr, port);
 
        if (!dest) {
                ip_vs_scheduler_err(svc, "no destination available");
@@ -341,7 +338,7 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
        }
 
        IP_VS_DBG_BUF(6, "SH: source IP address %s --> server %s:%d\n",
-                     IP_VS_DBG_ADDR(svc->af, &iph->saddr),
+                     IP_VS_DBG_ADDR(svc->af, hash_addr),
                      IP_VS_DBG_ADDR(dest->af, &dest->addr),
                      ntohs(dest->port));
 
index 43f140950075f2a95d0be856482c92bfa9234ce7..803001a45aa16e6b5a372ab385dba8e9c09bd2f0 100644 (file)
@@ -193,7 +193,7 @@ union ip_vs_sync_conn {
 #define IPVS_OPT_F_PARAM       (1 << (IPVS_OPT_PARAM-1))
 
 struct ip_vs_sync_thread_data {
-       struct net *net;
+       struct netns_ipvs *ipvs;
        struct socket *sock;
        char *buf;
        int id;
@@ -533,10 +533,9 @@ set:
  *      Version 0 , could be switched in by sys_ctl.
  *      Add an ip_vs_conn information into the current sync_buff.
  */
-static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
+static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,
                               int pkts)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_sync_mesg_v0 *m;
        struct ip_vs_sync_conn_v0 *s;
        struct ip_vs_sync_buff *buff;
@@ -615,7 +614,7 @@ static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
                        pkts = atomic_add_return(1, &cp->in_pkts);
                else
                        pkts = sysctl_sync_threshold(ipvs);
-               ip_vs_sync_conn(net, cp, pkts);
+               ip_vs_sync_conn(ipvs, cp, pkts);
        }
 }
 
@@ -624,9 +623,8 @@ static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
  *      Called by ip_vs_in.
  *      Sending Version 1 messages
  */
-void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts)
+void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_sync_mesg *m;
        union ip_vs_sync_conn *s;
        struct ip_vs_sync_buff *buff;
@@ -637,7 +635,7 @@ void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts)
 
        /* Handle old version of the protocol */
        if (sysctl_sync_ver(ipvs) == 0) {
-               ip_vs_sync_conn_v0(net, cp, pkts);
+               ip_vs_sync_conn_v0(ipvs, cp, pkts);
                return;
        }
        /* Do not sync ONE PACKET */
@@ -784,21 +782,21 @@ control:
  *  fill_param used by version 1
  */
 static inline int
-ip_vs_conn_fill_param_sync(struct net *net, int af, union ip_vs_sync_conn *sc,
+ip_vs_conn_fill_param_sync(struct netns_ipvs *ipvs, int af, union ip_vs_sync_conn *sc,
                           struct ip_vs_conn_param *p,
                           __u8 *pe_data, unsigned int pe_data_len,
                           __u8 *pe_name, unsigned int pe_name_len)
 {
 #ifdef CONFIG_IP_VS_IPV6
        if (af == AF_INET6)
-               ip_vs_conn_fill_param(net, af, sc->v6.protocol,
+               ip_vs_conn_fill_param(ipvs, af, sc->v6.protocol,
                                      (const union nf_inet_addr *)&sc->v6.caddr,
                                      sc->v6.cport,
                                      (const union nf_inet_addr *)&sc->v6.vaddr,
                                      sc->v6.vport, p);
        else
 #endif
-               ip_vs_conn_fill_param(net, af, sc->v4.protocol,
+               ip_vs_conn_fill_param(ipvs, af, sc->v4.protocol,
                                      (const union nf_inet_addr *)&sc->v4.caddr,
                                      sc->v4.cport,
                                      (const union nf_inet_addr *)&sc->v4.vaddr,
@@ -837,7 +835,7 @@ ip_vs_conn_fill_param_sync(struct net *net, int af, union ip_vs_sync_conn *sc,
  *  Param: ...
  *         timeout is in sec.
  */
-static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
+static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *param,
                            unsigned int flags, unsigned int state,
                            unsigned int protocol, unsigned int type,
                            const union nf_inet_addr *daddr, __be16 dport,
@@ -846,7 +844,6 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
 {
        struct ip_vs_dest *dest;
        struct ip_vs_conn *cp;
-       struct netns_ipvs *ipvs = net_ipvs(net);
 
        if (!(flags & IP_VS_CONN_F_TEMPLATE)) {
                cp = ip_vs_conn_in_get(param);
@@ -904,7 +901,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
                 * with synchronization, so we can make the assumption that
                 * the svc_af is the same as the dest_af
                 */
-               dest = ip_vs_find_dest(net, type, type, daddr, dport,
+               dest = ip_vs_find_dest(ipvs, type, type, daddr, dport,
                                       param->vaddr, param->vport, protocol,
                                       fwmark, flags);
 
@@ -941,7 +938,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
        } else {
                struct ip_vs_proto_data *pd;
 
-               pd = ip_vs_proto_data_get(net, protocol);
+               pd = ip_vs_proto_data_get(ipvs, protocol);
                if (!(flags & IP_VS_CONN_F_TEMPLATE) && pd && pd->timeout_table)
                        cp->timeout = pd->timeout_table[state];
                else
@@ -953,7 +950,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
 /*
  *  Process received multicast message for Version 0
  */
-static void ip_vs_process_message_v0(struct net *net, const char *buffer,
+static void ip_vs_process_message_v0(struct netns_ipvs *ipvs, const char *buffer,
                                     const size_t buflen)
 {
        struct ip_vs_sync_mesg_v0 *m = (struct ip_vs_sync_mesg_v0 *)buffer;
@@ -1009,14 +1006,14 @@ static void ip_vs_process_message_v0(struct net *net, const char *buffer,
                        }
                }
 
-               ip_vs_conn_fill_param(net, AF_INET, s->protocol,
+               ip_vs_conn_fill_param(ipvs, AF_INET, s->protocol,
                                      (const union nf_inet_addr *)&s->caddr,
                                      s->cport,
                                      (const union nf_inet_addr *)&s->vaddr,
                                      s->vport, &param);
 
                /* Send timeout as Zero */
-               ip_vs_proc_conn(net, &param, flags, state, s->protocol, AF_INET,
+               ip_vs_proc_conn(ipvs, &param, flags, state, s->protocol, AF_INET,
                                (union nf_inet_addr *)&s->daddr, s->dport,
                                0, 0, opt);
        }
@@ -1067,7 +1064,7 @@ static int ip_vs_proc_str(__u8 *p, unsigned int plen, unsigned int *data_len,
 /*
  *   Process a Version 1 sync. connection
  */
-static inline int ip_vs_proc_sync_conn(struct net *net, __u8 *p, __u8 *msg_end)
+static inline int ip_vs_proc_sync_conn(struct netns_ipvs *ipvs, __u8 *p, __u8 *msg_end)
 {
        struct ip_vs_sync_conn_options opt;
        union  ip_vs_sync_conn *s;
@@ -1171,21 +1168,21 @@ static inline int ip_vs_proc_sync_conn(struct net *net, __u8 *p, __u8 *msg_end)
                        state = 0;
                }
        }
-       if (ip_vs_conn_fill_param_sync(net, af, s, &param, pe_data,
+       if (ip_vs_conn_fill_param_sync(ipvs, af, s, &param, pe_data,
                                       pe_data_len, pe_name, pe_name_len)) {
                retc = 50;
                goto out;
        }
        /* If only IPv4, just silent skip IPv6 */
        if (af == AF_INET)
-               ip_vs_proc_conn(net, &param, flags, state, s->v4.protocol, af,
+               ip_vs_proc_conn(ipvs, &param, flags, state, s->v4.protocol, af,
                                (union nf_inet_addr *)&s->v4.daddr, s->v4.dport,
                                ntohl(s->v4.timeout), ntohl(s->v4.fwmark),
                                (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL)
                                );
 #ifdef CONFIG_IP_VS_IPV6
        else
-               ip_vs_proc_conn(net, &param, flags, state, s->v6.protocol, af,
+               ip_vs_proc_conn(ipvs, &param, flags, state, s->v6.protocol, af,
                                (union nf_inet_addr *)&s->v6.daddr, s->v6.dport,
                                ntohl(s->v6.timeout), ntohl(s->v6.fwmark),
                                (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL)
@@ -1204,10 +1201,9 @@ out:
  *      ip_vs_conn entries.
  *      Handles Version 0 & 1
  */
-static void ip_vs_process_message(struct net *net, __u8 *buffer,
+static void ip_vs_process_message(struct netns_ipvs *ipvs, __u8 *buffer,
                                  const size_t buflen)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_sync_mesg *m2 = (struct ip_vs_sync_mesg *)buffer;
        __u8 *p, *msg_end;
        int i, nr_conns;
@@ -1257,7 +1253,7 @@ static void ip_vs_process_message(struct net *net, __u8 *buffer,
                                return;
                        }
                        /* Process a single sync_conn */
-                       retc = ip_vs_proc_sync_conn(net, p, msg_end);
+                       retc = ip_vs_proc_sync_conn(ipvs, p, msg_end);
                        if (retc < 0) {
                                IP_VS_ERR_RL("BACKUP, Dropping buffer, Err: %d in decoding\n",
                                             retc);
@@ -1268,7 +1264,7 @@ static void ip_vs_process_message(struct net *net, __u8 *buffer,
                }
        } else {
                /* Old type of message */
-               ip_vs_process_message_v0(net, buffer, buflen);
+               ip_vs_process_message_v0(ipvs, buffer, buflen);
                return;
        }
 }
@@ -1493,16 +1489,15 @@ static void get_mcast_sockaddr(union ipvs_sockaddr *sa, int *salen,
 /*
  *      Set up sending multicast socket over UDP
  */
-static struct socket *make_send_sock(struct net *net, int id)
+static struct socket *make_send_sock(struct netns_ipvs *ipvs, int id)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        /* multicast addr */
        union ipvs_sockaddr mcast_addr;
        struct socket *sock;
        int result, salen;
 
        /* First create a socket */
-       result = sock_create_kern(net, ipvs->mcfg.mcast_af, SOCK_DGRAM,
+       result = sock_create_kern(ipvs->net, ipvs->mcfg.mcast_af, SOCK_DGRAM,
                                  IPPROTO_UDP, &sock);
        if (result < 0) {
                pr_err("Error during creation of socket; terminating\n");
@@ -1550,16 +1545,15 @@ error:
 /*
  *      Set up receiving multicast socket over UDP
  */
-static struct socket *make_receive_sock(struct net *net, int id)
+static struct socket *make_receive_sock(struct netns_ipvs *ipvs, int id)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        /* multicast addr */
        union ipvs_sockaddr mcast_addr;
        struct socket *sock;
        int result, salen;
 
        /* First create a socket */
-       result = sock_create_kern(net, ipvs->bcfg.mcast_af, SOCK_DGRAM,
+       result = sock_create_kern(ipvs->net, ipvs->bcfg.mcast_af, SOCK_DGRAM,
                                  IPPROTO_UDP, &sock);
        if (result < 0) {
                pr_err("Error during creation of socket; terminating\n");
@@ -1687,7 +1681,7 @@ next_sync_buff(struct netns_ipvs *ipvs, struct ipvs_master_sync_state *ms)
 static int sync_thread_master(void *data)
 {
        struct ip_vs_sync_thread_data *tinfo = data;
-       struct netns_ipvs *ipvs = net_ipvs(tinfo->net);
+       struct netns_ipvs *ipvs = tinfo->ipvs;
        struct ipvs_master_sync_state *ms = &ipvs->ms[tinfo->id];
        struct sock *sk = tinfo->sock->sk;
        struct ip_vs_sync_buff *sb;
@@ -1743,7 +1737,7 @@ done:
 static int sync_thread_backup(void *data)
 {
        struct ip_vs_sync_thread_data *tinfo = data;
-       struct netns_ipvs *ipvs = net_ipvs(tinfo->net);
+       struct netns_ipvs *ipvs = tinfo->ipvs;
        int len;
 
        pr_info("sync thread started: state = BACKUP, mcast_ifn = %s, "
@@ -1765,7 +1759,7 @@ static int sync_thread_backup(void *data)
                                break;
                        }
 
-                       ip_vs_process_message(tinfo->net, tinfo->buf, len);
+                       ip_vs_process_message(ipvs, tinfo->buf, len);
                }
        }
 
@@ -1778,13 +1772,12 @@ static int sync_thread_backup(void *data)
 }
 
 
-int start_sync_thread(struct net *net, struct ipvs_sync_daemon_cfg *c,
+int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c,
                      int state)
 {
        struct ip_vs_sync_thread_data *tinfo;
        struct task_struct **array = NULL, *task;
        struct socket *sock;
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct net_device *dev;
        char *name;
        int (*threadfn)(void *data);
@@ -1811,7 +1804,7 @@ int start_sync_thread(struct net *net, struct ipvs_sync_daemon_cfg *c,
        if (!c->mcast_ttl)
                c->mcast_ttl = 1;
 
-       dev = __dev_get_by_name(net, c->mcast_ifn);
+       dev = __dev_get_by_name(ipvs->net, c->mcast_ifn);
        if (!dev) {
                pr_err("Unknown mcast interface: %s\n", c->mcast_ifn);
                return -ENODEV;
@@ -1873,9 +1866,9 @@ int start_sync_thread(struct net *net, struct ipvs_sync_daemon_cfg *c,
        tinfo = NULL;
        for (id = 0; id < count; id++) {
                if (state == IP_VS_STATE_MASTER)
-                       sock = make_send_sock(net, id);
+                       sock = make_send_sock(ipvs, id);
                else
-                       sock = make_receive_sock(net, id);
+                       sock = make_receive_sock(ipvs, id);
                if (IS_ERR(sock)) {
                        result = PTR_ERR(sock);
                        goto outtinfo;
@@ -1883,7 +1876,7 @@ int start_sync_thread(struct net *net, struct ipvs_sync_daemon_cfg *c,
                tinfo = kmalloc(sizeof(*tinfo), GFP_KERNEL);
                if (!tinfo)
                        goto outsocket;
-               tinfo->net = net;
+               tinfo->ipvs = ipvs;
                tinfo->sock = sock;
                if (state == IP_VS_STATE_BACKUP) {
                        tinfo->buf = kmalloc(ipvs->bcfg.sync_maxlen,
@@ -1947,9 +1940,8 @@ out:
 }
 
 
-int stop_sync_thread(struct net *net, int state)
+int stop_sync_thread(struct netns_ipvs *ipvs, int state)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct task_struct **array;
        int id;
        int retc = -EINVAL;
@@ -2015,27 +2007,24 @@ int stop_sync_thread(struct net *net, int state)
 /*
  * Initialize data struct for each netns
  */
-int __net_init ip_vs_sync_net_init(struct net *net)
+int __net_init ip_vs_sync_net_init(struct netns_ipvs *ipvs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
-
        __mutex_init(&ipvs->sync_mutex, "ipvs->sync_mutex", &__ipvs_sync_key);
        spin_lock_init(&ipvs->sync_lock);
        spin_lock_init(&ipvs->sync_buff_lock);
        return 0;
 }
 
-void ip_vs_sync_net_cleanup(struct net *net)
+void ip_vs_sync_net_cleanup(struct netns_ipvs *ipvs)
 {
        int retc;
-       struct netns_ipvs *ipvs = net_ipvs(net);
 
        mutex_lock(&ipvs->sync_mutex);
-       retc = stop_sync_thread(net, IP_VS_STATE_MASTER);
+       retc = stop_sync_thread(ipvs, IP_VS_STATE_MASTER);
        if (retc && retc != -ESRCH)
                pr_err("Failed to stop Master Daemon\n");
 
-       retc = stop_sync_thread(net, IP_VS_STATE_BACKUP);
+       retc = stop_sync_thread(ipvs, IP_VS_STATE_BACKUP);
        if (retc && retc != -ESRCH)
                pr_err("Failed to stop Backup Daemon\n");
        mutex_unlock(&ipvs->sync_mutex);
index 258a0b0e82a293db38533a114c5c3a0bb2c03b9f..3264cb49b333620d6013cf1f6686f09bf07f9b8a 100644 (file)
@@ -212,19 +212,20 @@ static inline void maybe_update_pmtu(int skb_af, struct sk_buff *skb, int mtu)
                ort->dst.ops->update_pmtu(&ort->dst, sk, NULL, mtu);
 }
 
-static inline bool ensure_mtu_is_adequate(int skb_af, int rt_mode,
+static inline bool ensure_mtu_is_adequate(struct netns_ipvs *ipvs, int skb_af,
+                                         int rt_mode,
                                          struct ip_vs_iphdr *ipvsh,
                                          struct sk_buff *skb, int mtu)
 {
 #ifdef CONFIG_IP_VS_IPV6
        if (skb_af == AF_INET6) {
-               struct net *net = dev_net(skb_dst(skb)->dev);
+               struct net *net = ipvs->net;
 
                if (unlikely(__mtu_check_toobig_v6(skb, mtu))) {
                        if (!skb->dev)
                                skb->dev = net->loopback_dev;
                        /* only send ICMP too big on first fragment */
-                       if (!ipvsh->fragoffs)
+                       if (!ipvsh->fragoffs && !ip_vs_iph_icmp(ipvsh))
                                icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
                        IP_VS_DBG(1, "frag needed for %pI6c\n",
                                  &ipv6_hdr(skb)->saddr);
@@ -233,8 +234,6 @@ static inline bool ensure_mtu_is_adequate(int skb_af, int rt_mode,
        } else
 #endif
        {
-               struct netns_ipvs *ipvs = net_ipvs(skb_net(skb));
-
                /* If we're going to tunnel the packet and pmtu discovery
                 * is disabled, we'll just fragment it anyway
                 */
@@ -242,7 +241,8 @@ static inline bool ensure_mtu_is_adequate(int skb_af, int rt_mode,
                        return true;
 
                if (unlikely(ip_hdr(skb)->frag_off & htons(IP_DF) &&
-                            skb->len > mtu && !skb_is_gso(skb))) {
+                            skb->len > mtu && !skb_is_gso(skb) &&
+                            !ip_vs_iph_icmp(ipvsh))) {
                        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
                                  htonl(mtu));
                        IP_VS_DBG(1, "frag needed for %pI4\n",
@@ -256,11 +256,12 @@ static inline bool ensure_mtu_is_adequate(int skb_af, int rt_mode,
 
 /* Get route to destination or remote server */
 static int
-__ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest,
+__ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
+                  struct ip_vs_dest *dest,
                   __be32 daddr, int rt_mode, __be32 *ret_saddr,
                   struct ip_vs_iphdr *ipvsh)
 {
-       struct net *net = dev_net(skb_dst(skb)->dev);
+       struct net *net = ipvs->net;
        struct ip_vs_dest_dst *dest_dst;
        struct rtable *rt;                      /* Route to the other host */
        int mtu;
@@ -336,7 +337,7 @@ __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest,
                maybe_update_pmtu(skb_af, skb, mtu);
        }
 
-       if (!ensure_mtu_is_adequate(skb_af, rt_mode, ipvsh, skb, mtu))
+       if (!ensure_mtu_is_adequate(ipvs, skb_af, rt_mode, ipvsh, skb, mtu))
                goto err_put;
 
        skb_dst_drop(skb);
@@ -402,11 +403,12 @@ out_err:
  * Get route to destination or remote server
  */
 static int
-__ip_vs_get_out_rt_v6(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest,
+__ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
+                     struct ip_vs_dest *dest,
                      struct in6_addr *daddr, struct in6_addr *ret_saddr,
                      struct ip_vs_iphdr *ipvsh, int do_xfrm, int rt_mode)
 {
-       struct net *net = dev_net(skb_dst(skb)->dev);
+       struct net *net = ipvs->net;
        struct ip_vs_dest_dst *dest_dst;
        struct rt6_info *rt;                    /* Route to the other host */
        struct dst_entry *dst;
@@ -484,7 +486,7 @@ __ip_vs_get_out_rt_v6(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest,
                maybe_update_pmtu(skb_af, skb, mtu);
        }
 
-       if (!ensure_mtu_is_adequate(skb_af, rt_mode, ipvsh, skb, mtu))
+       if (!ensure_mtu_is_adequate(ipvs, skb_af, rt_mode, ipvsh, skb, mtu))
                goto err_put;
 
        skb_dst_drop(skb);
@@ -573,8 +575,8 @@ static inline int ip_vs_nat_send_or_cont(int pf, struct sk_buff *skb,
                skb_forward_csum(skb);
                if (!skb->sk)
                        skb_sender_cpu_clear(skb);
-               NF_HOOK(pf, NF_INET_LOCAL_OUT, NULL, skb,
-                       NULL, skb_dst(skb)->dev, dst_output_sk);
+               NF_HOOK(pf, NF_INET_LOCAL_OUT, cp->ipvs->net, NULL, skb,
+                       NULL, skb_dst(skb)->dev, dst_output);
        } else
                ret = NF_ACCEPT;
 
@@ -595,8 +597,8 @@ static inline int ip_vs_send_or_cont(int pf, struct sk_buff *skb,
                skb_forward_csum(skb);
                if (!skb->sk)
                        skb_sender_cpu_clear(skb);
-               NF_HOOK(pf, NF_INET_LOCAL_OUT, NULL, skb,
-                       NULL, skb_dst(skb)->dev, dst_output_sk);
+               NF_HOOK(pf, NF_INET_LOCAL_OUT, cp->ipvs->net, NULL, skb,
+                       NULL, skb_dst(skb)->dev, dst_output);
        } else
                ret = NF_ACCEPT;
        return ret;
@@ -629,7 +631,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        EnterFunction(10);
 
        rcu_read_lock();
-       if (__ip_vs_get_out_rt(cp->af, skb, NULL, iph->daddr,
+       if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, iph->daddr,
                               IP_VS_RT_MODE_NON_LOCAL, NULL, ipvsh) < 0)
                goto tx_error;
 
@@ -656,10 +658,13 @@ int
 ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                     struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
 {
+       struct ipv6hdr *iph = ipv6_hdr(skb);
+
        EnterFunction(10);
 
        rcu_read_lock();
-       if (__ip_vs_get_out_rt_v6(cp->af, skb, NULL, &ipvsh->daddr.in6, NULL,
+       if (__ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, NULL,
+                                 &iph->daddr, NULL,
                                  ipvsh, 0, IP_VS_RT_MODE_NON_LOCAL) < 0)
                goto tx_error;
 
@@ -706,7 +711,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        }
 
        was_input = rt_is_input_route(skb_rtable(skb));
-       local = __ip_vs_get_out_rt(cp->af, skb, cp->dest, cp->daddr.ip,
+       local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip,
                                   IP_VS_RT_MODE_LOCAL |
                                   IP_VS_RT_MODE_NON_LOCAL |
                                   IP_VS_RT_MODE_RDR, NULL, ipvsh);
@@ -723,7 +728,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
                struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 
                if (ct && !nf_ct_is_untracked(ct)) {
-                       IP_VS_DBG_RL_PKT(10, AF_INET, pp, skb, 0,
+                       IP_VS_DBG_RL_PKT(10, AF_INET, pp, skb, ipvsh->off,
                                         "ip_vs_nat_xmit(): "
                                         "stopping DNAT to local address");
                        goto tx_error;
@@ -733,8 +738,9 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
 
        /* From world but DNAT to loopback address? */
        if (local && ipv4_is_loopback(cp->daddr.ip) && was_input) {
-               IP_VS_DBG_RL_PKT(1, AF_INET, pp, skb, 0, "ip_vs_nat_xmit(): "
-                                "stopping DNAT to loopback address");
+               IP_VS_DBG_RL_PKT(1, AF_INET, pp, skb, ipvsh->off,
+                                "ip_vs_nat_xmit(): stopping DNAT to loopback "
+                                "address");
                goto tx_error;
        }
 
@@ -751,7 +757,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        ip_hdr(skb)->daddr = cp->daddr.ip;
        ip_send_check(ip_hdr(skb));
 
-       IP_VS_DBG_PKT(10, AF_INET, pp, skb, 0, "After DNAT");
+       IP_VS_DBG_PKT(10, AF_INET, pp, skb, ipvsh->off, "After DNAT");
 
        /* FIXME: when application helper enlarges the packet and the length
           is larger than the MTU of outgoing device, there will be still
@@ -794,7 +800,8 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                IP_VS_DBG(10, "filled cport=%d\n", ntohs(*p));
        }
 
-       local = __ip_vs_get_out_rt_v6(cp->af, skb, cp->dest, &cp->daddr.in6,
+       local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest,
+                                     &cp->daddr.in6,
                                      NULL, ipvsh, 0,
                                      IP_VS_RT_MODE_LOCAL |
                                      IP_VS_RT_MODE_NON_LOCAL |
@@ -812,7 +819,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 
                if (ct && !nf_ct_is_untracked(ct)) {
-                       IP_VS_DBG_RL_PKT(10, AF_INET6, pp, skb, 0,
+                       IP_VS_DBG_RL_PKT(10, AF_INET6, pp, skb, ipvsh->off,
                                         "ip_vs_nat_xmit_v6(): "
                                         "stopping DNAT to local address");
                        goto tx_error;
@@ -823,7 +830,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        /* From world but DNAT to loopback address? */
        if (local && skb->dev && !(skb->dev->flags & IFF_LOOPBACK) &&
            ipv6_addr_type(&cp->daddr.in6) & IPV6_ADDR_LOOPBACK) {
-               IP_VS_DBG_RL_PKT(1, AF_INET6, pp, skb, 0,
+               IP_VS_DBG_RL_PKT(1, AF_INET6, pp, skb, ipvsh->off,
                                 "ip_vs_nat_xmit_v6(): "
                                 "stopping DNAT to loopback address");
                goto tx_error;
@@ -841,7 +848,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                goto tx_error;
        ipv6_hdr(skb)->daddr = cp->daddr.in6;
 
-       IP_VS_DBG_PKT(10, AF_INET6, pp, skb, 0, "After DNAT");
+       IP_VS_DBG_PKT(10, AF_INET6, pp, skb, ipvsh->off, "After DNAT");
 
        /* FIXME: when application helper enlarges the packet and the length
           is larger than the MTU of outgoing device, there will be still
@@ -967,8 +974,8 @@ int
 ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
                  struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
 {
-       struct net *net = skb_net(skb);
-       struct netns_ipvs *ipvs = net_ipvs(net);
+       struct netns_ipvs *ipvs = cp->ipvs;
+       struct net *net = ipvs->net;
        struct rtable *rt;                      /* Route to the other host */
        __be32 saddr;                           /* Source for tunnel */
        struct net_device *tdev;                /* Device to other host */
@@ -984,7 +991,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        EnterFunction(10);
 
        rcu_read_lock();
-       local = __ip_vs_get_out_rt(cp->af, skb, cp->dest, cp->daddr.ip,
+       local = __ip_vs_get_out_rt(ipvs, cp->af, skb, cp->dest, cp->daddr.ip,
                                   IP_VS_RT_MODE_LOCAL |
                                   IP_VS_RT_MODE_NON_LOCAL |
                                   IP_VS_RT_MODE_CONNECT |
@@ -1042,7 +1049,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
 
        ret = ip_vs_tunnel_xmit_prepare(skb, cp);
        if (ret == NF_ACCEPT)
-               ip_local_out(skb);
+               ip_local_out(net, skb->sk, skb);
        else if (ret == NF_DROP)
                kfree_skb(skb);
        rcu_read_unlock();
@@ -1078,7 +1085,8 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        EnterFunction(10);
 
        rcu_read_lock();
-       local = __ip_vs_get_out_rt_v6(cp->af, skb, cp->dest, &cp->daddr.in6,
+       local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest,
+                                     &cp->daddr.in6,
                                      &saddr, ipvsh, 1,
                                      IP_VS_RT_MODE_LOCAL |
                                      IP_VS_RT_MODE_NON_LOCAL |
@@ -1133,7 +1141,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
 
        ret = ip_vs_tunnel_xmit_prepare(skb, cp);
        if (ret == NF_ACCEPT)
-               ip6_local_out(skb);
+               ip6_local_out(cp->ipvs->net, skb->sk, skb);
        else if (ret == NF_DROP)
                kfree_skb(skb);
        rcu_read_unlock();
@@ -1165,7 +1173,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        EnterFunction(10);
 
        rcu_read_lock();
-       local = __ip_vs_get_out_rt(cp->af, skb, cp->dest, cp->daddr.ip,
+       local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip,
                                   IP_VS_RT_MODE_LOCAL |
                                   IP_VS_RT_MODE_NON_LOCAL |
                                   IP_VS_RT_MODE_KNOWN_NH, NULL, ipvsh);
@@ -1204,7 +1212,8 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        EnterFunction(10);
 
        rcu_read_lock();
-       local = __ip_vs_get_out_rt_v6(cp->af, skb, cp->dest, &cp->daddr.in6,
+       local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest,
+                                     &cp->daddr.in6,
                                      NULL, ipvsh, 0,
                                      IP_VS_RT_MODE_LOCAL |
                                      IP_VS_RT_MODE_NON_LOCAL |
@@ -1273,7 +1282,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
                  IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
                  IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
        rcu_read_lock();
-       local = __ip_vs_get_out_rt(cp->af, skb, cp->dest, cp->daddr.ip, rt_mode,
+       local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode,
                                   NULL, iph);
        if (local < 0)
                goto tx_error;
@@ -1365,8 +1374,8 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                  IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
                  IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
        rcu_read_lock();
-       local = __ip_vs_get_out_rt_v6(cp->af, skb, cp->dest, &cp->daddr.in6,
-                                     NULL, ipvsh, 0, rt_mode);
+       local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest,
+                                     &cp->daddr.in6, NULL, ipvsh, 0, rt_mode);
        if (local < 0)
                goto tx_error;
        rt = (struct rt6_info *) skb_dst(skb);
index c09d6c7198f60d809b36783ca1de43646025c876..3cb3cb831591ef79515b4bde66d5db825a732afb 100644 (file)
@@ -168,6 +168,7 @@ nf_ct_get_tuple(const struct sk_buff *skb,
                unsigned int dataoff,
                u_int16_t l3num,
                u_int8_t protonum,
+               struct net *net,
                struct nf_conntrack_tuple *tuple,
                const struct nf_conntrack_l3proto *l3proto,
                const struct nf_conntrack_l4proto *l4proto)
@@ -181,12 +182,13 @@ nf_ct_get_tuple(const struct sk_buff *skb,
        tuple->dst.protonum = protonum;
        tuple->dst.dir = IP_CT_DIR_ORIGINAL;
 
-       return l4proto->pkt_to_tuple(skb, dataoff, tuple);
+       return l4proto->pkt_to_tuple(skb, dataoff, net, tuple);
 }
 EXPORT_SYMBOL_GPL(nf_ct_get_tuple);
 
 bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff,
-                      u_int16_t l3num, struct nf_conntrack_tuple *tuple)
+                      u_int16_t l3num,
+                      struct net *net, struct nf_conntrack_tuple *tuple)
 {
        struct nf_conntrack_l3proto *l3proto;
        struct nf_conntrack_l4proto *l4proto;
@@ -205,7 +207,7 @@ bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff,
 
        l4proto = __nf_ct_l4proto_find(l3num, protonum);
 
-       ret = nf_ct_get_tuple(skb, nhoff, protoff, l3num, protonum, tuple,
+       ret = nf_ct_get_tuple(skb, nhoff, protoff, l3num, protonum, net, tuple,
                              l3proto, l4proto);
 
        rcu_read_unlock();
@@ -938,10 +940,13 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
        }
 
        timeout_ext = tmpl ? nf_ct_timeout_find(tmpl) : NULL;
-       if (timeout_ext)
-               timeouts = NF_CT_TIMEOUT_EXT_DATA(timeout_ext);
-       else
+       if (timeout_ext) {
+               timeouts = nf_ct_timeout_data(timeout_ext);
+               if (unlikely(!timeouts))
+                       timeouts = l4proto->get_timeouts(net);
+       } else {
                timeouts = l4proto->get_timeouts(net);
+       }
 
        if (!l4proto->new(ct, skb, dataoff, timeouts)) {
                nf_conntrack_free(ct);
@@ -950,7 +955,8 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
        }
 
        if (timeout_ext)
-               nf_ct_timeout_ext_add(ct, timeout_ext->timeout, GFP_ATOMIC);
+               nf_ct_timeout_ext_add(ct, rcu_dereference(timeout_ext->timeout),
+                                     GFP_ATOMIC);
 
        nf_ct_acct_ext_add(ct, GFP_ATOMIC);
        nf_ct_tstamp_ext_add(ct, GFP_ATOMIC);
@@ -1029,7 +1035,7 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
        u32 hash;
 
        if (!nf_ct_get_tuple(skb, skb_network_offset(skb),
-                            dataoff, l3num, protonum, &tuple, l3proto,
+                            dataoff, l3num, protonum, net, &tuple, l3proto,
                             l4proto)) {
                pr_debug("resolve_normal_ct: Can't get tuple\n");
                return NULL;
index 94a66541e0b76a1764ad6b2d73bf8f6cd1310a6b..9f5272968abb095cb3e758b3906cb6dcbd86ab24 100644 (file)
@@ -2133,9 +2133,9 @@ ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct,
                       struct nf_conntrack_tuple *tuple,
                       struct nf_conntrack_tuple *mask);
 
-#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT
+#ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT
 static size_t
-ctnetlink_nfqueue_build_size(const struct nf_conn *ct)
+ctnetlink_glue_build_size(const struct nf_conn *ct)
 {
        return 3 * nla_total_size(0) /* CTA_TUPLE_ORIG|REPL|MASTER */
               + 3 * nla_total_size(0) /* CTA_TUPLE_IP */
@@ -2162,8 +2162,19 @@ ctnetlink_nfqueue_build_size(const struct nf_conn *ct)
               ;
 }
 
-static int
-ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct)
+static struct nf_conn *ctnetlink_glue_get_ct(const struct sk_buff *skb,
+                                            enum ip_conntrack_info *ctinfo)
+{
+       struct nf_conn *ct;
+
+       ct = nf_ct_get(skb, ctinfo);
+       if (ct && nf_ct_is_untracked(ct))
+               ct = NULL;
+
+       return ct;
+}
+
+static int __ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct)
 {
        const struct nf_conntrack_zone *zone;
        struct nlattr *nest_parms;
@@ -2236,7 +2247,32 @@ nla_put_failure:
 }
 
 static int
-ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct)
+ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct,
+                    enum ip_conntrack_info ctinfo,
+                    u_int16_t ct_attr, u_int16_t ct_info_attr)
+{
+       struct nlattr *nest_parms;
+
+       nest_parms = nla_nest_start(skb, ct_attr | NLA_F_NESTED);
+       if (!nest_parms)
+               goto nla_put_failure;
+
+       if (__ctnetlink_glue_build(skb, ct) < 0)
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest_parms);
+
+       if (nla_put_be32(skb, ct_info_attr, htonl(ctinfo)))
+               goto nla_put_failure;
+
+       return 0;
+
+nla_put_failure:
+       return -ENOSPC;
+}
+
+static int
+ctnetlink_glue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct)
 {
        int err;
 
@@ -2276,7 +2312,7 @@ ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct)
 }
 
 static int
-ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct)
+ctnetlink_glue_parse(const struct nlattr *attr, struct nf_conn *ct)
 {
        struct nlattr *cda[CTA_MAX+1];
        int ret;
@@ -2286,16 +2322,16 @@ ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct)
                return ret;
 
        spin_lock_bh(&nf_conntrack_expect_lock);
-       ret = ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct);
+       ret = ctnetlink_glue_parse_ct((const struct nlattr **)cda, ct);
        spin_unlock_bh(&nf_conntrack_expect_lock);
 
        return ret;
 }
 
-static int ctnetlink_nfqueue_exp_parse(const struct nlattr * const *cda,
-                                      const struct nf_conn *ct,
-                                      struct nf_conntrack_tuple *tuple,
-                                      struct nf_conntrack_tuple *mask)
+static int ctnetlink_glue_exp_parse(const struct nlattr * const *cda,
+                                   const struct nf_conn *ct,
+                                   struct nf_conntrack_tuple *tuple,
+                                   struct nf_conntrack_tuple *mask)
 {
        int err;
 
@@ -2309,8 +2345,8 @@ static int ctnetlink_nfqueue_exp_parse(const struct nlattr * const *cda,
 }
 
 static int
-ctnetlink_nfqueue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
-                               u32 portid, u32 report)
+ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
+                            u32 portid, u32 report)
 {
        struct nlattr *cda[CTA_EXPECT_MAX+1];
        struct nf_conntrack_tuple tuple, mask;
@@ -2322,8 +2358,8 @@ ctnetlink_nfqueue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
        if (err < 0)
                return err;
 
-       err = ctnetlink_nfqueue_exp_parse((const struct nlattr * const *)cda,
-                                         ct, &tuple, &mask);
+       err = ctnetlink_glue_exp_parse((const struct nlattr * const *)cda,
+                                      ct, &tuple, &mask);
        if (err < 0)
                return err;
 
@@ -2350,14 +2386,24 @@ ctnetlink_nfqueue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
        return 0;
 }
 
-static struct nfq_ct_hook ctnetlink_nfqueue_hook = {
-       .build_size     = ctnetlink_nfqueue_build_size,
-       .build          = ctnetlink_nfqueue_build,
-       .parse          = ctnetlink_nfqueue_parse,
-       .attach_expect  = ctnetlink_nfqueue_attach_expect,
-       .seq_adjust     = nf_ct_tcp_seqadj_set,
+static void ctnetlink_glue_seqadj(struct sk_buff *skb, struct nf_conn *ct,
+                                 enum ip_conntrack_info ctinfo, int diff)
+{
+       if (!(ct->status & IPS_NAT_MASK))
+               return;
+
+       nf_ct_tcp_seqadj_set(skb, ct, ctinfo, diff);
+}
+
+static struct nfnl_ct_hook ctnetlink_glue_hook = {
+       .get_ct         = ctnetlink_glue_get_ct,
+       .build_size     = ctnetlink_glue_build_size,
+       .build          = ctnetlink_glue_build,
+       .parse          = ctnetlink_glue_parse,
+       .attach_expect  = ctnetlink_glue_attach_expect,
+       .seq_adjust     = ctnetlink_glue_seqadj,
 };
-#endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */
+#endif /* CONFIG_NETFILTER_NETLINK_GLUE_CT */
 
 /***********************************************************************
  * EXPECT
@@ -3341,9 +3387,9 @@ static int __init ctnetlink_init(void)
                pr_err("ctnetlink_init: cannot register pernet operations\n");
                goto err_unreg_exp_subsys;
        }
-#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT
+#ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT
        /* setup interaction between nf_queue and nf_conntrack_netlink. */
-       RCU_INIT_POINTER(nfq_ct_hook, &ctnetlink_nfqueue_hook);
+       RCU_INIT_POINTER(nfnl_ct_hook, &ctnetlink_glue_hook);
 #endif
        return 0;
 
@@ -3362,8 +3408,8 @@ static void __exit ctnetlink_exit(void)
        unregister_pernet_subsys(&ctnetlink_net_ops);
        nfnetlink_subsys_unregister(&ctnl_exp_subsys);
        nfnetlink_subsys_unregister(&ctnl_subsys);
-#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT
-       RCU_INIT_POINTER(nfq_ct_hook, NULL);
+#ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT
+       RCU_INIT_POINTER(nfnl_ct_hook, NULL);
 #endif
 }
 
index 6dd995c7c72b4400cdbd23da703467690f2e8cb4..fce1b1cca32d65c6241f0ee513db2fe5774a00b9 100644 (file)
@@ -398,7 +398,7 @@ static inline struct dccp_net *dccp_pernet(struct net *net)
 }
 
 static bool dccp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
-                             struct nf_conntrack_tuple *tuple)
+                             struct net *net, struct nf_conntrack_tuple *tuple)
 {
        struct dccp_hdr _hdr, *dh;
 
index 2281be419a74b6d8abe0fd7da8d7e8b35d304600..86dc752e534963b08c7fc4217db533c480042d17 100644 (file)
@@ -45,7 +45,7 @@ static inline struct nf_generic_net *generic_pernet(struct net *net)
 
 static bool generic_pkt_to_tuple(const struct sk_buff *skb,
                                 unsigned int dataoff,
-                                struct nf_conntrack_tuple *tuple)
+                                struct net *net, struct nf_conntrack_tuple *tuple)
 {
        tuple->src.u.all = 0;
        tuple->dst.u.all = 0;
index 7648674f29c3be28c1d3b3f91c5eab7400cedb51..a96451a7af20a8104c82711f31958b043de4bde7 100644 (file)
@@ -190,9 +190,8 @@ static bool gre_invert_tuple(struct nf_conntrack_tuple *tuple,
 
 /* gre hdr info to tuple */
 static bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
-                            struct nf_conntrack_tuple *tuple)
+                            struct net *net, struct nf_conntrack_tuple *tuple)
 {
-       struct net *net = dev_net(skb->dev ? skb->dev : skb_dst(skb)->dev);
        const struct gre_hdr_pptp *pgrehdr;
        struct gre_hdr_pptp _pgrehdr;
        __be16 srckey;
index 67197731eb6835ff944c688afb942b90c9dc63a4..9578a7c371ef2ce04f0a7b10c694b2533e29bd3a 100644 (file)
@@ -156,7 +156,7 @@ static inline struct sctp_net *sctp_pernet(struct net *net)
 }
 
 static bool sctp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
-                             struct nf_conntrack_tuple *tuple)
+                             struct net *net, struct nf_conntrack_tuple *tuple)
 {
        const struct sctphdr *hp;
        struct sctphdr _hdr;
index 70383de7205460a8ebdadd7fd1ba615fc7681296..278f3b9356efdcd37ca3d1aacb62f03e57db7e3c 100644 (file)
@@ -277,7 +277,7 @@ static inline struct nf_tcp_net *tcp_pernet(struct net *net)
 }
 
 static bool tcp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
-                            struct nf_conntrack_tuple *tuple)
+                            struct net *net, struct nf_conntrack_tuple *tuple)
 {
        const struct tcphdr *hp;
        struct tcphdr _hdr;
index 6957281ffee54fe43f22a73772b1f88a2e758a50..478f92f834b61e7e2007779dc9dd8cace98ffcd2 100644 (file)
@@ -38,6 +38,7 @@ static inline struct nf_udp_net *udp_pernet(struct net *net)
 
 static bool udp_pkt_to_tuple(const struct sk_buff *skb,
                             unsigned int dataoff,
+                            struct net *net,
                             struct nf_conntrack_tuple *tuple)
 {
        const struct udphdr *hp;
index c5903d1649f9a02ab1d59615a458be578e0a5f6d..1ac8ee13a873ea60dffb6d455c48fc11bf7e45c4 100644 (file)
@@ -48,6 +48,7 @@ static inline struct udplite_net *udplite_pernet(struct net *net)
 
 static bool udplite_pkt_to_tuple(const struct sk_buff *skb,
                                 unsigned int dataoff,
+                                struct net *net,
                                 struct nf_conntrack_tuple *tuple)
 {
        const struct udphdr *hp;
index 5113dfd39df929967f247ca644a96664e6d72f1a..06a9f45771ab613bdd529195f64583e6e4316a7c 100644 (file)
@@ -83,7 +83,7 @@ out:
        rcu_read_unlock();
 }
 
-int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family)
+int nf_xfrm_me_harder(struct net *net, struct sk_buff *skb, unsigned int family)
 {
        struct flowi fl;
        unsigned int hh_len;
@@ -99,7 +99,7 @@ int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family)
                dst = ((struct xfrm_dst *)dst)->route;
        dst_hold(dst);
 
-       dst = xfrm_lookup(dev_net(dst->dev), dst, &fl, skb->sk, 0);
+       dst = xfrm_lookup(net, dst, &fl, skb->sk, 0);
        if (IS_ERR(dst))
                return PTR_ERR(dst);
 
index 96777f9a9350b3a684ae56b50c77400fde3ad305..5baa8e24e6ac1b512250c676cf0caf115f0b17f9 100644 (file)
@@ -69,19 +69,14 @@ void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
                        dev_put(physdev);
        }
 #endif
-       /* Drop reference to owner of hook which queued us. */
-       module_put(entry->elem->owner);
 }
 EXPORT_SYMBOL_GPL(nf_queue_entry_release_refs);
 
 /* Bump dev refs so they don't vanish while packet is out */
-bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
+void nf_queue_entry_get_refs(struct nf_queue_entry *entry)
 {
        struct nf_hook_state *state = &entry->state;
 
-       if (!try_module_get(entry->elem->owner))
-               return false;
-
        if (state->in)
                dev_hold(state->in);
        if (state->out)
@@ -100,8 +95,6 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
                        dev_hold(physdev);
        }
 #endif
-
-       return true;
 }
 EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
 
@@ -131,22 +124,20 @@ int nf_queue(struct sk_buff *skb,
        const struct nf_queue_handler *qh;
 
        /* QUEUE == DROP if no one is waiting, to be safe. */
-       rcu_read_lock();
-
        qh = rcu_dereference(queue_handler);
        if (!qh) {
                status = -ESRCH;
-               goto err_unlock;
+               goto err;
        }
 
        afinfo = nf_get_afinfo(state->pf);
        if (!afinfo)
-               goto err_unlock;
+               goto err;
 
        entry = kmalloc(sizeof(*entry) + afinfo->route_key_size, GFP_ATOMIC);
        if (!entry) {
                status = -ENOMEM;
-               goto err_unlock;
+               goto err;
        }
 
        *entry = (struct nf_queue_entry) {
@@ -156,16 +147,11 @@ int nf_queue(struct sk_buff *skb,
                .size   = sizeof(*entry) + afinfo->route_key_size,
        };
 
-       if (!nf_queue_entry_get_refs(entry)) {
-               status = -ECANCELED;
-               goto err_unlock;
-       }
+       nf_queue_entry_get_refs(entry);
        skb_dst_force(skb);
        afinfo->saveroute(skb, entry);
        status = qh->outfn(entry, queuenum);
 
-       rcu_read_unlock();
-
        if (status < 0) {
                nf_queue_entry_release_refs(entry);
                goto err;
@@ -173,8 +159,6 @@ int nf_queue(struct sk_buff *skb,
 
        return 0;
 
-err_unlock:
-       rcu_read_unlock();
 err:
        kfree(entry);
        return status;
@@ -187,19 +171,15 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
        const struct nf_afinfo *afinfo;
        int err;
 
-       rcu_read_lock();
-
        nf_queue_entry_release_refs(entry);
 
        /* Continue traversal iff userspace said ok... */
-       if (verdict == NF_REPEAT) {
-               elem = list_entry(elem->list.prev, struct nf_hook_ops, list);
-               verdict = NF_ACCEPT;
-       }
+       if (verdict == NF_REPEAT)
+               verdict = elem->hook(elem->priv, skb, &entry->state);
 
        if (verdict == NF_ACCEPT) {
                afinfo = nf_get_afinfo(entry->state.pf);
-               if (!afinfo || afinfo->reroute(skb, entry) < 0)
+               if (!afinfo || afinfo->reroute(entry->state.net, skb, entry) < 0)
                        verdict = NF_DROP;
        }
 
@@ -215,15 +195,13 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
        case NF_ACCEPT:
        case NF_STOP:
                local_bh_disable();
-               entry->state.okfn(entry->state.sk, skb);
+               entry->state.okfn(entry->state.net, entry->state.sk, skb);
                local_bh_enable();
                break;
        case NF_QUEUE:
                err = nf_queue(skb, elem, &entry->state,
                               verdict >> NF_VERDICT_QBITS);
                if (err < 0) {
-                       if (err == -ECANCELED)
-                               goto next_hook;
                        if (err == -ESRCH &&
                           (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS))
                                goto next_hook;
@@ -235,7 +213,7 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
        default:
                kfree_skb(skb);
        }
-       rcu_read_unlock();
+
        kfree(entry);
 }
 EXPORT_SYMBOL(nf_reinject);
index 4a41eb92bcc0ab934f12305ad64a18c8804a52b2..93cc4737018fdf3d13a2896c19a9c65891355f9e 100644 (file)
@@ -1433,7 +1433,6 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
                for (i = 0; i < afi->nops; i++) {
                        ops = &basechain->ops[i];
                        ops->pf         = family;
-                       ops->owner      = afi->owner;
                        ops->hooknum    = hooknum;
                        ops->priority   = priority;
                        ops->priv       = chain;
index 05d0b03530f6b21418d69c0c0f51916228d26b94..f3695a4974086ff2f10acc8fcc0f2be746e248ba 100644 (file)
@@ -48,9 +48,7 @@ static void __nft_trace_packet(const struct nft_pktinfo *pkt,
                               const struct nft_chain *chain,
                               int rulenum, enum nft_trace type)
 {
-       struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
-
-       nf_log_trace(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in,
+       nf_log_trace(pkt->net, pkt->pf, pkt->hook, pkt->skb, pkt->in,
                     pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ",
                     chain->table->name, chain->name, comments[type],
                     rulenum);
@@ -111,10 +109,10 @@ struct nft_jumpstack {
 };
 
 unsigned int
-nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)
+nft_do_chain(struct nft_pktinfo *pkt, void *priv)
 {
-       const struct nft_chain *chain = ops->priv, *basechain = chain;
-       const struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
+       const struct nft_chain *chain = priv, *basechain = chain;
+       const struct net *net = pkt->net;
        const struct nft_rule *rule;
        const struct nft_expr *expr, *last;
        struct nft_regs regs;
index 2cae4d4a03b77c0a08b1d0f29900b5f06082572c..7b9c053ba75072276ee9227ea8d1e67ce3307715 100644 (file)
 
 static inline void
 nft_netdev_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
-                           const struct nf_hook_ops *ops, struct sk_buff *skb,
+                           struct sk_buff *skb,
                            const struct nf_hook_state *state)
 {
        struct iphdr *iph, _iph;
        u32 len, thoff;
 
-       nft_set_pktinfo(pkt, ops, skb, state);
+       nft_set_pktinfo(pkt, skb, state);
 
        iph = skb_header_pointer(skb, skb_network_offset(skb), sizeof(*iph),
                                 &_iph);
@@ -48,7 +48,6 @@ nft_netdev_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
 
 static inline void
 __nft_netdev_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
-                             const struct nf_hook_ops *ops,
                              struct sk_buff *skb,
                              const struct nf_hook_state *state)
 {
@@ -82,33 +81,32 @@ __nft_netdev_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
 }
 
 static inline void nft_netdev_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
-                                              const struct nf_hook_ops *ops,
                                               struct sk_buff *skb,
                                               const struct nf_hook_state *state)
 {
-       nft_set_pktinfo(pkt, ops, skb, state);
-       __nft_netdev_set_pktinfo_ipv6(pkt, ops, skb, state);
+       nft_set_pktinfo(pkt, skb, state);
+       __nft_netdev_set_pktinfo_ipv6(pkt, skb, state);
 }
 
 static unsigned int
-nft_do_chain_netdev(const struct nf_hook_ops *ops, struct sk_buff *skb,
+nft_do_chain_netdev(void *priv, struct sk_buff *skb,
                    const struct nf_hook_state *state)
 {
        struct nft_pktinfo pkt;
 
        switch (eth_hdr(skb)->h_proto) {
        case htons(ETH_P_IP):
-               nft_netdev_set_pktinfo_ipv4(&pkt, ops, skb, state);
+               nft_netdev_set_pktinfo_ipv4(&pkt, skb, state);
                break;
        case htons(ETH_P_IPV6):
-               nft_netdev_set_pktinfo_ipv6(&pkt, ops, skb, state);
+               nft_netdev_set_pktinfo_ipv6(&pkt, skb, state);
                break;
        default:
-               nft_set_pktinfo(&pkt, ops, skb, state);
+               nft_set_pktinfo(&pkt, skb, state);
                break;
        }
 
-       return nft_do_chain(&pkt, ops);
+       return nft_do_chain(&pkt, priv);
 }
 
 static struct nft_af_info nft_af_netdev __read_mostly = {
index 70277b11f742e8f0a2756c2ba54e1565419adc95..f1d9e887f5b157b58578a5a7244d65be788bb13b 100644 (file)
@@ -64,7 +64,7 @@ void nfnl_unlock(__u8 subsys_id)
 EXPORT_SYMBOL_GPL(nfnl_unlock);
 
 #ifdef CONFIG_PROVE_LOCKING
-int lockdep_nfnl_is_held(u8 subsys_id)
+bool lockdep_nfnl_is_held(u8 subsys_id)
 {
        return lockdep_is_held(&table[subsys_id].mutex);
 }
index 476accd171452fcfbfbe01018dcadd55fee41f67..c7a2d0e1c462cd9284ede6a0ea1b1d70db95c8c1 100644 (file)
@@ -291,6 +291,34 @@ cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb,
        return ret;
 }
 
+static void untimeout(struct nf_conntrack_tuple_hash *i,
+                     struct ctnl_timeout *timeout)
+{
+       struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(i);
+       struct nf_conn_timeout *timeout_ext = nf_ct_timeout_find(ct);
+
+       if (timeout_ext && (!timeout || timeout_ext->timeout == timeout))
+               RCU_INIT_POINTER(timeout_ext->timeout, NULL);
+}
+
+static void ctnl_untimeout(struct ctnl_timeout *timeout)
+{
+       struct nf_conntrack_tuple_hash *h;
+       const struct hlist_nulls_node *nn;
+       int i;
+
+       local_bh_disable();
+       for (i = 0; i < init_net.ct.htable_size; i++) {
+               spin_lock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]);
+               if (i < init_net.ct.htable_size) {
+                       hlist_nulls_for_each_entry(h, nn, &init_net.ct.hash[i], hnnode)
+                               untimeout(h, timeout);
+               }
+               spin_unlock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]);
+       }
+       local_bh_enable();
+}
+
 /* try to delete object, fail if it is still in use. */
 static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
 {
@@ -301,6 +329,7 @@ static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
                /* We are protected by nfnl mutex. */
                list_del_rcu(&timeout->head);
                nf_ct_l4proto_put(timeout->l4proto);
+               ctnl_untimeout(timeout);
                kfree_rcu(timeout, rcu_head);
        } else {
                /* still in use, restore reference counter. */
@@ -567,6 +596,10 @@ static void __exit cttimeout_exit(void)
        pr_info("cttimeout: unregistering from nfnetlink.\n");
 
        nfnetlink_subsys_unregister(&cttimeout_subsys);
+
+       /* Make sure no conntrack objects refer to custom timeouts anymore. */
+       ctnl_untimeout(NULL);
+
        list_for_each_entry_safe(cur, tmp, &cttimeout_list, head) {
                list_del_rcu(&cur->head);
                /* We are sure that our objects have no clients at this point,
@@ -579,6 +612,7 @@ static void __exit cttimeout_exit(void)
        RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL);
        RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL);
 #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
+       rcu_barrier();
 }
 
 module_init(cttimeout_init);
index 4670821b569d3270c6d47e51f1c10fca2a505970..06eb48fceb42e4c31dba2d91c0d182bfc82e5b19 100644 (file)
@@ -27,6 +27,7 @@
 #include <net/netlink.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nfnetlink_log.h>
+#include <linux/netfilter/nf_conntrack_common.h>
 #include <linux/spinlock.h>
 #include <linux/sysctl.h>
 #include <linux/proc_fs.h>
@@ -401,7 +402,9 @@ __build_packet_message(struct nfnl_log_net *log,
                        unsigned int hooknum,
                        const struct net_device *indev,
                        const struct net_device *outdev,
-                       const char *prefix, unsigned int plen)
+                       const char *prefix, unsigned int plen,
+                       const struct nfnl_ct_hook *nfnl_ct,
+                       struct nf_conn *ct, enum ip_conntrack_info ctinfo)
 {
        struct nfulnl_msg_packet_hdr pmsg;
        struct nlmsghdr *nlh;
@@ -538,9 +541,9 @@ __build_packet_message(struct nfnl_log_net *log,
 
        if (skb->tstamp.tv64) {
                struct nfulnl_msg_packet_timestamp ts;
-               struct timeval tv = ktime_to_timeval(skb->tstamp);
-               ts.sec = cpu_to_be64(tv.tv_sec);
-               ts.usec = cpu_to_be64(tv.tv_usec);
+               struct timespec64 kts = ktime_to_timespec64(skb->tstamp);
+               ts.sec = cpu_to_be64(kts.tv_sec);
+               ts.usec = cpu_to_be64(kts.tv_nsec / NSEC_PER_USEC);
 
                if (nla_put(inst->skb, NFULA_TIMESTAMP, sizeof(ts), &ts))
                        goto nla_put_failure;
@@ -575,6 +578,10 @@ __build_packet_message(struct nfnl_log_net *log,
                         htonl(atomic_inc_return(&log->global_seq))))
                goto nla_put_failure;
 
+       if (ct && nfnl_ct->build(inst->skb, ct, ctinfo,
+                                NFULA_CT, NFULA_CT_INFO) < 0)
+               goto nla_put_failure;
+
        if (data_len) {
                struct nlattr *nla;
                int size = nla_attr_size(data_len);
@@ -620,12 +627,16 @@ nfulnl_log_packet(struct net *net,
                  const struct nf_loginfo *li_user,
                  const char *prefix)
 {
-       unsigned int size, data_len;
+       size_t size;
+       unsigned int data_len;
        struct nfulnl_instance *inst;
        const struct nf_loginfo *li;
        unsigned int qthreshold;
        unsigned int plen;
        struct nfnl_log_net *log = nfnl_log_pernet(net);
+       const struct nfnl_ct_hook *nfnl_ct = NULL;
+       struct nf_conn *ct = NULL;
+       enum ip_conntrack_info uninitialized_var(ctinfo);
 
        if (li_user && li_user->type == NF_LOG_TYPE_ULOG)
                li = li_user;
@@ -671,6 +682,14 @@ nfulnl_log_packet(struct net *net,
                size += nla_total_size(sizeof(u_int32_t));
        if (inst->flags & NFULNL_CFG_F_SEQ_GLOBAL)
                size += nla_total_size(sizeof(u_int32_t));
+       if (inst->flags & NFULNL_CFG_F_CONNTRACK) {
+               nfnl_ct = rcu_dereference(nfnl_ct_hook);
+               if (nfnl_ct != NULL) {
+                       ct = nfnl_ct->get_ct(skb, &ctinfo);
+                       if (ct != NULL)
+                               size += nfnl_ct->build_size(ct);
+               }
+       }
 
        qthreshold = inst->qthreshold;
        /* per-rule qthreshold overrides per-instance */
@@ -715,7 +734,8 @@ nfulnl_log_packet(struct net *net,
        inst->qlen++;
 
        __build_packet_message(log, inst, skb, data_len, pf,
-                               hooknum, in, out, prefix, plen);
+                               hooknum, in, out, prefix, plen,
+                               nfnl_ct, ct, ctinfo);
 
        if (inst->qlen >= qthreshold)
                __nfulnl_flush(inst);
@@ -805,6 +825,7 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
        struct net *net = sock_net(ctnl);
        struct nfnl_log_net *log = nfnl_log_pernet(net);
        int ret = 0;
+       u16 flags;
 
        if (nfula[NFULA_CFG_CMD]) {
                u_int8_t pf = nfmsg->nfgen_family;
@@ -826,6 +847,28 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
                goto out_put;
        }
 
+       /* Check if we support these flags in first place, dependencies should
+        * be there too not to break atomicity.
+        */
+       if (nfula[NFULA_CFG_FLAGS]) {
+               flags = ntohs(nla_get_be16(nfula[NFULA_CFG_FLAGS]));
+
+               if ((flags & NFULNL_CFG_F_CONNTRACK) &&
+                   !rcu_access_pointer(nfnl_ct_hook)) {
+#ifdef CONFIG_MODULES
+                       nfnl_unlock(NFNL_SUBSYS_ULOG);
+                       request_module("ip_conntrack_netlink");
+                       nfnl_lock(NFNL_SUBSYS_ULOG);
+                       if (rcu_access_pointer(nfnl_ct_hook)) {
+                               ret = -EAGAIN;
+                               goto out_put;
+                       }
+#endif
+                       ret = -EOPNOTSUPP;
+                       goto out_put;
+               }
+       }
+
        if (cmd != NULL) {
                switch (cmd->command) {
                case NFULNL_CFG_CMD_BIND:
@@ -854,16 +897,15 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
                        ret = -ENOTSUPP;
                        break;
                }
+       } else if (!inst) {
+               ret = -ENODEV;
+               goto out;
        }
 
        if (nfula[NFULA_CFG_MODE]) {
-               struct nfulnl_msg_config_mode *params;
-               params = nla_data(nfula[NFULA_CFG_MODE]);
+               struct nfulnl_msg_config_mode *params =
+                       nla_data(nfula[NFULA_CFG_MODE]);
 
-               if (!inst) {
-                       ret = -ENODEV;
-                       goto out;
-               }
                nfulnl_set_mode(inst, params->copy_mode,
                                ntohl(params->copy_range));
        }
@@ -871,42 +913,23 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
        if (nfula[NFULA_CFG_TIMEOUT]) {
                __be32 timeout = nla_get_be32(nfula[NFULA_CFG_TIMEOUT]);
 
-               if (!inst) {
-                       ret = -ENODEV;
-                       goto out;
-               }
                nfulnl_set_timeout(inst, ntohl(timeout));
        }
 
        if (nfula[NFULA_CFG_NLBUFSIZ]) {
                __be32 nlbufsiz = nla_get_be32(nfula[NFULA_CFG_NLBUFSIZ]);
 
-               if (!inst) {
-                       ret = -ENODEV;
-                       goto out;
-               }
                nfulnl_set_nlbufsiz(inst, ntohl(nlbufsiz));
        }
 
        if (nfula[NFULA_CFG_QTHRESH]) {
                __be32 qthresh = nla_get_be32(nfula[NFULA_CFG_QTHRESH]);
 
-               if (!inst) {
-                       ret = -ENODEV;
-                       goto out;
-               }
                nfulnl_set_qthresh(inst, ntohl(qthresh));
        }
 
-       if (nfula[NFULA_CFG_FLAGS]) {
-               __be16 flags = nla_get_be16(nfula[NFULA_CFG_FLAGS]);
-
-               if (!inst) {
-                       ret = -ENODEV;
-                       goto out;
-               }
-               nfulnl_set_flags(inst, ntohs(flags));
-       }
+       if (nfula[NFULA_CFG_FLAGS])
+               nfulnl_set_flags(inst, flags);
 
 out_put:
        instance_put(inst);
similarity index 95%
rename from net/netfilter/nfnetlink_queue_core.c
rename to net/netfilter/nfnetlink_queue.c
index a5cd6d90b78b16ebd0c96c4d1d426ad7aea6e86b..7d81d280cb4ff3e8878af96bd644930f68a4340c 100644 (file)
 #include <linux/netfilter_bridge.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nfnetlink_queue.h>
+#include <linux/netfilter/nf_conntrack_common.h>
 #include <linux/list.h>
 #include <net/sock.h>
 #include <net/tcp_states.h>
 #include <net/netfilter/nf_queue.h>
 #include <net/netns/generic.h>
-#include <net/netfilter/nfnetlink_queue.h>
 
 #include <linux/atomic.h>
 
@@ -313,6 +313,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
        struct net_device *outdev;
        struct nf_conn *ct = NULL;
        enum ip_conntrack_info uninitialized_var(ctinfo);
+       struct nfnl_ct_hook *nfnl_ct;
        bool csum_verify;
        char *secdata = NULL;
        u32 seclen = 0;
@@ -364,8 +365,14 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
                break;
        }
 
-       if (queue->flags & NFQA_CFG_F_CONNTRACK)
-               ct = nfqnl_ct_get(entskb, &size, &ctinfo);
+       if (queue->flags & NFQA_CFG_F_CONNTRACK) {
+               nfnl_ct = rcu_dereference(nfnl_ct_hook);
+               if (nfnl_ct != NULL) {
+                       ct = nfnl_ct->get_ct(entskb, &ctinfo);
+                       if (ct != NULL)
+                               size += nfnl_ct->build_size(ct);
+               }
+       }
 
        if (queue->flags & NFQA_CFG_F_UID_GID) {
                size +=  (nla_total_size(sizeof(u_int32_t))     /* uid */
@@ -493,9 +500,10 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
 
        if (entskb->tstamp.tv64) {
                struct nfqnl_msg_packet_timestamp ts;
-               struct timeval tv = ktime_to_timeval(entskb->tstamp);
-               ts.sec = cpu_to_be64(tv.tv_sec);
-               ts.usec = cpu_to_be64(tv.tv_usec);
+               struct timespec64 kts = ktime_to_timespec64(skb->tstamp);
+
+               ts.sec = cpu_to_be64(kts.tv_sec);
+               ts.usec = cpu_to_be64(kts.tv_nsec / NSEC_PER_USEC);
 
                if (nla_put(skb, NFQA_TIMESTAMP, sizeof(ts), &ts))
                        goto nla_put_failure;
@@ -508,7 +516,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
        if (seclen && nla_put(skb, NFQA_SECCTX, seclen, secdata))
                goto nla_put_failure;
 
-       if (ct && nfqnl_ct_put(skb, ct, ctinfo) < 0)
+       if (ct && nfnl_ct->build(skb, ct, ctinfo, NFQA_CT, NFQA_CT_INFO) < 0)
                goto nla_put_failure;
 
        if (cap_len > data_len &&
@@ -598,12 +606,9 @@ static struct nf_queue_entry *
 nf_queue_entry_dup(struct nf_queue_entry *e)
 {
        struct nf_queue_entry *entry = kmemdup(e, e->size, GFP_ATOMIC);
-       if (entry) {
-               if (nf_queue_entry_get_refs(entry))
-                       return entry;
-               kfree(entry);
-       }
-       return NULL;
+       if (entry)
+               nf_queue_entry_get_refs(entry);
+       return entry;
 }
 
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
@@ -670,8 +675,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
        struct nfqnl_instance *queue;
        struct sk_buff *skb, *segs;
        int err = -ENOBUFS;
-       struct net *net = dev_net(entry->state.in ?
-                                 entry->state.in : entry->state.out);
+       struct net *net = entry->state.net;
        struct nfnl_queue_net *q = nfnl_queue_pernet(net);
 
        /* rcu_read_lock()ed by nf_hook_slow() */
@@ -699,7 +703,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
        nf_bridge_adjust_skb_data(skb);
        segs = skb_gso_segment(skb, 0);
        /* Does not use PTR_ERR to limit the number of error codes that can be
-        * returned by nf_queue.  For instance, callers rely on -ECANCELED to
+        * returned by nf_queue.  For instance, callers rely on -ESRCH to
         * mean 'ignore this hook'.
         */
        if (IS_ERR_OR_NULL(segs))
@@ -1002,6 +1006,28 @@ nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb,
        return 0;
 }
 
+static struct nf_conn *nfqnl_ct_parse(struct nfnl_ct_hook *nfnl_ct,
+                                     const struct nlmsghdr *nlh,
+                                     const struct nlattr * const nfqa[],
+                                     struct nf_queue_entry *entry,
+                                     enum ip_conntrack_info *ctinfo)
+{
+       struct nf_conn *ct;
+
+       ct = nfnl_ct->get_ct(entry->skb, ctinfo);
+       if (ct == NULL)
+               return NULL;
+
+       if (nfnl_ct->parse(nfqa[NFQA_CT], ct) < 0)
+               return NULL;
+
+       if (nfqa[NFQA_EXP])
+               nfnl_ct->attach_expect(nfqa[NFQA_EXP], ct,
+                                     NETLINK_CB(entry->skb).portid,
+                                     nlmsg_report(nlh));
+       return ct;
+}
+
 static int
 nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
                   const struct nlmsghdr *nlh,
@@ -1015,6 +1041,7 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
        unsigned int verdict;
        struct nf_queue_entry *entry;
        enum ip_conntrack_info uninitialized_var(ctinfo);
+       struct nfnl_ct_hook *nfnl_ct;
        struct nf_conn *ct = NULL;
 
        struct net *net = sock_net(ctnl);
@@ -1038,12 +1065,10 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
                return -ENOENT;
 
        if (nfqa[NFQA_CT]) {
-               ct = nfqnl_ct_parse(entry->skb, nfqa[NFQA_CT], &ctinfo);
-               if (ct && nfqa[NFQA_EXP]) {
-                       nfqnl_attach_expect(ct, nfqa[NFQA_EXP],
-                                           NETLINK_CB(skb).portid,
-                                           nlmsg_report(nlh));
-               }
+               /* rcu lock already held from nfnl->call_rcu. */
+               nfnl_ct = rcu_dereference(nfnl_ct_hook);
+               if (nfnl_ct != NULL)
+                       ct = nfqnl_ct_parse(nfnl_ct, nlh, nfqa, entry, &ctinfo);
        }
 
        if (nfqa[NFQA_PAYLOAD]) {
@@ -1054,8 +1079,8 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
                                 payload_len, entry, diff) < 0)
                        verdict = NF_DROP;
 
-               if (ct)
-                       nfqnl_ct_seq_adjust(entry->skb, ct, ctinfo, diff);
+               if (ct && diff)
+                       nfnl_ct->seq_adjust(entry->skb, ct, ctinfo, diff);
        }
 
        if (nfqa[NFQA_MARK])
diff --git a/net/netfilter/nfnetlink_queue_ct.c b/net/netfilter/nfnetlink_queue_ct.c
deleted file mode 100644 (file)
index 96cac50..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/skbuff.h>
-#include <linux/netfilter.h>
-#include <linux/netfilter/nfnetlink.h>
-#include <linux/netfilter/nfnetlink_queue.h>
-#include <net/netfilter/nf_conntrack.h>
-#include <net/netfilter/nfnetlink_queue.h>
-
-struct nf_conn *nfqnl_ct_get(struct sk_buff *entskb, size_t *size,
-                            enum ip_conntrack_info *ctinfo)
-{
-       struct nfq_ct_hook *nfq_ct;
-       struct nf_conn *ct;
-
-       /* rcu_read_lock()ed by __nf_queue already. */
-       nfq_ct = rcu_dereference(nfq_ct_hook);
-       if (nfq_ct == NULL)
-               return NULL;
-
-       ct = nf_ct_get(entskb, ctinfo);
-       if (ct) {
-               if (!nf_ct_is_untracked(ct))
-                       *size += nfq_ct->build_size(ct);
-               else
-                       ct = NULL;
-       }
-       return ct;
-}
-
-struct nf_conn *
-nfqnl_ct_parse(const struct sk_buff *skb, const struct nlattr *attr,
-              enum ip_conntrack_info *ctinfo)
-{
-       struct nfq_ct_hook *nfq_ct;
-       struct nf_conn *ct;
-
-       /* rcu_read_lock()ed by __nf_queue already. */
-       nfq_ct = rcu_dereference(nfq_ct_hook);
-       if (nfq_ct == NULL)
-               return NULL;
-
-       ct = nf_ct_get(skb, ctinfo);
-       if (ct && !nf_ct_is_untracked(ct))
-               nfq_ct->parse(attr, ct);
-
-       return ct;
-}
-
-int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct,
-                enum ip_conntrack_info ctinfo)
-{
-       struct nfq_ct_hook *nfq_ct;
-       struct nlattr *nest_parms;
-       u_int32_t tmp;
-
-       nfq_ct = rcu_dereference(nfq_ct_hook);
-       if (nfq_ct == NULL)
-               return 0;
-
-       nest_parms = nla_nest_start(skb, NFQA_CT | NLA_F_NESTED);
-       if (!nest_parms)
-               goto nla_put_failure;
-
-       if (nfq_ct->build(skb, ct) < 0)
-               goto nla_put_failure;
-
-       nla_nest_end(skb, nest_parms);
-
-       tmp = ctinfo;
-       if (nla_put_be32(skb, NFQA_CT_INFO, htonl(tmp)))
-               goto nla_put_failure;
-
-       return 0;
-
-nla_put_failure:
-       return -1;
-}
-
-void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
-                        enum ip_conntrack_info ctinfo, int diff)
-{
-       struct nfq_ct_hook *nfq_ct;
-
-       nfq_ct = rcu_dereference(nfq_ct_hook);
-       if (nfq_ct == NULL)
-               return;
-
-       if ((ct->status & IPS_NAT_MASK) && diff)
-               nfq_ct->seq_adjust(skb, ct, ctinfo, diff);
-}
-
-int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr,
-                       u32 portid, u32 report)
-{
-       struct nfq_ct_hook *nfq_ct;
-
-       if (nf_ct_is_untracked(ct))
-               return 0;
-
-       nfq_ct = rcu_dereference(nfq_ct_hook);
-       if (nfq_ct == NULL)
-               return -EOPNOTSUPP;
-
-       return nfq_ct->attach_expect(attr, ct, portid, report);
-}
index a13d6a386d635f00b5715d1b71ba9b0819203544..319c22b4bca2386c0ba6f95bca3e0aba104660d0 100644 (file)
@@ -31,9 +31,8 @@ static void nft_log_eval(const struct nft_expr *expr,
                         const struct nft_pktinfo *pkt)
 {
        const struct nft_log *priv = nft_expr_priv(expr);
-       struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
 
-       nf_log_packet(net, pkt->ops->pf, pkt->ops->hooknum, pkt->skb, pkt->in,
+       nf_log_packet(pkt->net, pkt->pf, pkt->hook, pkt->skb, pkt->in,
                      pkt->out, &priv->loginfo, "%s", priv->prefix);
 }
 
index cb2f13ebb5a66cdf5fd9498ddfe8949f38741803..e4ad2c24bc4122e6470966f75da7b92f8273ba51 100644 (file)
@@ -42,7 +42,7 @@ void nft_meta_get_eval(const struct nft_expr *expr,
                *(__be16 *)dest = skb->protocol;
                break;
        case NFT_META_NFPROTO:
-               *dest = pkt->ops->pf;
+               *dest = pkt->pf;
                break;
        case NFT_META_L4PROTO:
                *dest = pkt->tprot;
@@ -135,7 +135,7 @@ void nft_meta_get_eval(const struct nft_expr *expr,
                        break;
                }
 
-               switch (pkt->ops->pf) {
+               switch (pkt->pf) {
                case NFPROTO_IPV4:
                        if (ipv4_is_multicast(ip_hdr(skb)->daddr))
                                *dest = PACKET_MULTICAST;
index 96805d21d618b7be7f0b802e4738d4d2afa6de7f..61d216eb79175ac2a699190b04eb5d447ed66759 100644 (file)
@@ -42,7 +42,7 @@ static void nft_queue_eval(const struct nft_expr *expr,
                        queue = priv->queuenum + cpu % priv->queues_total;
                } else {
                        queue = nfqueue_hash(pkt->skb, queue,
-                                            priv->queues_total, pkt->ops->pf,
+                                            priv->queues_total, pkt->pf,
                                             jhash_initval);
                }
        }
index 635dbba93d013244038cbdbb48bbe9549f622304..759ca5248a3d22c20ce19dad1b7a7649a4ec0d6f 100644 (file)
@@ -22,38 +22,37 @@ static void nft_reject_inet_eval(const struct nft_expr *expr,
                                 const struct nft_pktinfo *pkt)
 {
        struct nft_reject *priv = nft_expr_priv(expr);
-       struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out);
 
-       switch (pkt->ops->pf) {
+       switch (pkt->pf) {
        case NFPROTO_IPV4:
                switch (priv->type) {
                case NFT_REJECT_ICMP_UNREACH:
                        nf_send_unreach(pkt->skb, priv->icmp_code,
-                                       pkt->ops->hooknum);
+                                       pkt->hook);
                        break;
                case NFT_REJECT_TCP_RST:
-                       nf_send_reset(pkt->skb, pkt->ops->hooknum);
+                       nf_send_reset(pkt->net, pkt->skb, pkt->hook);
                        break;
                case NFT_REJECT_ICMPX_UNREACH:
                        nf_send_unreach(pkt->skb,
                                        nft_reject_icmp_code(priv->icmp_code),
-                                       pkt->ops->hooknum);
+                                       pkt->hook);
                        break;
                }
                break;
        case NFPROTO_IPV6:
                switch (priv->type) {
                case NFT_REJECT_ICMP_UNREACH:
-                       nf_send_unreach6(net, pkt->skb, priv->icmp_code,
-                                        pkt->ops->hooknum);
+                       nf_send_unreach6(pkt->net, pkt->skb, priv->icmp_code,
+                                        pkt->hook);
                        break;
                case NFT_REJECT_TCP_RST:
-                       nf_send_reset6(net, pkt->skb, pkt->ops->hooknum);
+                       nf_send_reset6(pkt->net, pkt->skb, pkt->hook);
                        break;
                case NFT_REJECT_ICMPX_UNREACH:
-                       nf_send_unreach6(net, pkt->skb,
+                       nf_send_unreach6(pkt->net, pkt->skb,
                                         nft_reject_icmpv6_code(priv->icmp_code),
-                                        pkt->ops->hooknum);
+                                        pkt->hook);
                        break;
                }
                break;
index 9b42b5ea6dcd68c8398c501aa5af81b6dfa83ae8..d4aaad747ea99b0aa877016f437a5fd46ec768ee 100644 (file)
@@ -1193,7 +1193,6 @@ struct nf_hook_ops *xt_hook_link(const struct xt_table *table, nf_hookfn *fn)
                if (!(hook_mask & 1))
                        continue;
                ops[i].hook     = fn;
-               ops[i].owner    = table->me;
                ops[i].pf       = table->af;
                ops[i].hooknum  = hooknum;
                ops[i].priority = table->priority;
index faf32d888198a72a50c293312c014bcb63747654..e7ac07e53b5925d334d9e33fc8916768c3a3c1d4 100644 (file)
@@ -171,6 +171,9 @@ xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par,
        if (timeout_ext == NULL)
                ret = -ENOMEM;
 
+       rcu_read_unlock();
+       return ret;
+
 err_put_timeout:
        __xt_ct_tg_timeout_put(timeout);
 out:
@@ -318,8 +321,10 @@ static void xt_ct_destroy_timeout(struct nf_conn *ct)
 
        if (timeout_put) {
                timeout_ext = nf_ct_timeout_find(ct);
-               if (timeout_ext)
+               if (timeout_ext) {
                        timeout_put(timeout_ext->timeout);
+                       RCU_INIT_POINTER(timeout_ext->timeout, NULL);
+               }
        }
        rcu_read_unlock();
 #endif
index c13b79440ede6887588ed10144c7730cb8265b3d..1763ab82bcd75c343aebc53a26a621e434788d47 100644 (file)
@@ -33,7 +33,7 @@ log_tg(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_log_info *loginfo = par->targinfo;
        struct nf_loginfo li;
-       struct net *net = dev_net(par->in ? par->in : par->out);
+       struct net *net = par->net;
 
        li.type = NF_LOG_TYPE_LOG;
        li.u.log.level = loginfo->level;
index fb7497c928a0158675e377fe7b1f3ba4c043a8a9..a1fa2c800cb9122eeaf6cd477ea43ed409379795 100644 (file)
@@ -26,7 +26,7 @@ nflog_tg(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_nflog_info *info = par->targinfo;
        struct nf_loginfo li;
-       struct net *net = dev_net(par->in ? par->in : par->out);
+       struct net *net = par->net;
 
        li.type              = NF_LOG_TYPE_ULOG;
        li.u.ulog.copy_len   = info->len;
index 8c02501a530f4b481f9a6f4248957893801af2b2..b7c43def0dc69e1cd03db238e0174283d895003a 100644 (file)
@@ -108,7 +108,7 @@ tcpmss_mangle_packet(struct sk_buff *skb,
                return -1;
 
        if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
-               struct net *net = dev_net(par->in ? par->in : par->out);
+               struct net *net = par->net;
                unsigned int in_mtu = tcpmss_reverse_mtu(net, skb, family);
 
                if (dst_mtu(skb_dst(skb)) <= minlen) {
index fd980aa7715dc76d66b75bf53b158c07b3a147ed..899b06115fc53c87ea34eb1bf975666e1992ca98 100644 (file)
@@ -32,7 +32,7 @@ tee_tg4(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_tee_tginfo *info = par->targinfo;
 
-       nf_dup_ipv4(skb, par->hooknum, &info->gw.in, info->priv->oif);
+       nf_dup_ipv4(par->net, skb, par->hooknum, &info->gw.in, info->priv->oif);
 
        return XT_CONTINUE;
 }
@@ -43,7 +43,7 @@ tee_tg6(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_tee_tginfo *info = par->targinfo;
 
-       nf_dup_ipv6(skb, par->hooknum, &info->gw.in6, info->priv->oif);
+       nf_dup_ipv6(par->net, skb, par->hooknum, &info->gw.in6, info->priv->oif);
 
        return XT_CONTINUE;
 }
index d0c96c5ae29aa84057e93a5d9796014dca4c52e8..3ab591e73ec0f5823990f5ebf51aff29da7f598f 100644 (file)
@@ -250,8 +250,8 @@ nf_tproxy_get_sock_v6(struct net *net, const u8 protocol,
  * no such listener is found, or NULL if the TCP header is incomplete.
  */
 static struct sock *
-tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport,
-                       struct sock *sk)
+tproxy_handle_time_wait4(struct net *net, struct sk_buff *skb,
+                        __be32 laddr, __be16 lport, struct sock *sk)
 {
        const struct iphdr *iph = ip_hdr(skb);
        struct tcphdr _hdr, *hp;
@@ -267,7 +267,7 @@ tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport,
                 * to a listener socket if there's one */
                struct sock *sk2;
 
-               sk2 = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
+               sk2 = nf_tproxy_get_sock_v4(net, iph->protocol,
                                            iph->saddr, laddr ? laddr : iph->daddr,
                                            hp->source, lport ? lport : hp->dest,
                                            skb->dev, NFT_LOOKUP_LISTENER);
@@ -290,7 +290,7 @@ nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk)
 }
 
 static unsigned int
-tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
+tproxy_tg4(struct net *net, struct sk_buff *skb, __be32 laddr, __be16 lport,
           u_int32_t mark_mask, u_int32_t mark_value)
 {
        const struct iphdr *iph = ip_hdr(skb);
@@ -305,7 +305,7 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
         * addresses, this happens if the redirect already happened
         * and the current packet belongs to an already established
         * connection */
-       sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
+       sk = nf_tproxy_get_sock_v4(net, iph->protocol,
                                   iph->saddr, iph->daddr,
                                   hp->source, hp->dest,
                                   skb->dev, NFT_LOOKUP_ESTABLISHED);
@@ -317,11 +317,11 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
        /* UDP has no TCP_TIME_WAIT state, so we never enter here */
        if (sk && sk->sk_state == TCP_TIME_WAIT)
                /* reopening a TIME_WAIT connection needs special handling */
-               sk = tproxy_handle_time_wait4(skb, laddr, lport, sk);
+               sk = tproxy_handle_time_wait4(net, skb, laddr, lport, sk);
        else if (!sk)
                /* no, there's no established connection, check if
                 * there's a listener on the redirected addr/port */
-               sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
+               sk = nf_tproxy_get_sock_v4(net, iph->protocol,
                                           iph->saddr, laddr,
                                           hp->source, lport,
                                           skb->dev, NFT_LOOKUP_LISTENER);
@@ -351,7 +351,7 @@ tproxy_tg4_v0(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_tproxy_target_info *tgi = par->targinfo;
 
-       return tproxy_tg4(skb, tgi->laddr, tgi->lport, tgi->mark_mask, tgi->mark_value);
+       return tproxy_tg4(par->net, skb, tgi->laddr, tgi->lport, tgi->mark_mask, tgi->mark_value);
 }
 
 static unsigned int
@@ -359,7 +359,7 @@ tproxy_tg4_v1(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_tproxy_target_info_v1 *tgi = par->targinfo;
 
-       return tproxy_tg4(skb, tgi->laddr.ip, tgi->lport, tgi->mark_mask, tgi->mark_value);
+       return tproxy_tg4(par->net, skb, tgi->laddr.ip, tgi->lport, tgi->mark_mask, tgi->mark_value);
 }
 
 #ifdef XT_TPROXY_HAVE_IPV6
@@ -429,7 +429,7 @@ tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff,
                 * to a listener socket if there's one */
                struct sock *sk2;
 
-               sk2 = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
+               sk2 = nf_tproxy_get_sock_v6(par->net, tproto,
                                            &iph->saddr,
                                            tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr),
                                            hp->source,
@@ -472,7 +472,7 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
         * addresses, this happens if the redirect already happened
         * and the current packet belongs to an already established
         * connection */
-       sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
+       sk = nf_tproxy_get_sock_v6(par->net, tproto,
                                   &iph->saddr, &iph->daddr,
                                   hp->source, hp->dest,
                                   par->in, NFT_LOOKUP_ESTABLISHED);
@@ -487,7 +487,7 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
        else if (!sk)
                /* no there's no established connection, check if
                 * there's a listener on the redirected addr/port */
-               sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
+               sk = nf_tproxy_get_sock_v6(par->net, tproto,
                                           &iph->saddr, laddr,
                                           hp->source, lport,
                                           par->in, NFT_LOOKUP_LISTENER);
index 5b4743cc0436105f51cca8cc4ec71cf4a0ed730e..11d6091991a4ec569f6797023b75b241c3aa7575 100644 (file)
@@ -125,7 +125,7 @@ static inline bool match_type(struct net *net, const struct net_device *dev,
 static bool
 addrtype_mt_v0(const struct sk_buff *skb, struct xt_action_param *par)
 {
-       struct net *net = dev_net(par->in ? par->in : par->out);
+       struct net *net = par->net;
        const struct xt_addrtype_info *info = par->matchinfo;
        const struct iphdr *iph = ip_hdr(skb);
        bool ret = true;
@@ -143,7 +143,7 @@ addrtype_mt_v0(const struct sk_buff *skb, struct xt_action_param *par)
 static bool
 addrtype_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
 {
-       struct net *net = dev_net(par->in ? par->in : par->out);
+       struct net *net = par->net;
        const struct xt_addrtype_info_v1 *info = par->matchinfo;
        const struct iphdr *iph;
        const struct net_device *dev = NULL;
index 075d89d94d28f4deb87f473dd787f61395fa2681..99bbc829868d5042ab5dbcbc02dc70c8e57f6b85 100644 (file)
@@ -317,7 +317,7 @@ static int count_them(struct net *net,
 static bool
 connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
 {
-       struct net *net = dev_net(par->in ? par->in : par->out);
+       struct net *net = par->net;
        const struct xt_connlimit_info *info = par->matchinfo;
        union nf_inet_addr addr;
        struct nf_conntrack_tuple tuple;
@@ -332,7 +332,7 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
                tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
                zone = nf_ct_zone(ct);
        } else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
-                                   par->family, &tuple)) {
+                                     par->family, net, &tuple)) {
                goto hotdrop;
        }
 
index 8d47c3780fda8f98c1a16f6b6b396ec94c2bea34..71a9d95e0a81bbdd17d94f37c15cf612c1781dd4 100644 (file)
@@ -48,6 +48,7 @@ static bool
 ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
 {
        const struct xt_ipvs_mtinfo *data = par->matchinfo;
+       struct netns_ipvs *ipvs = net_ipvs(par->net);
        /* ipvs_mt_check ensures that family is only NFPROTO_IPV[46]. */
        const u_int8_t family = par->family;
        struct ip_vs_iphdr iph;
@@ -67,7 +68,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
                goto out;
        }
 
-       ip_vs_fill_iph_skb(family, skb, &iph);
+       ip_vs_fill_iph_skb(family, skb, true, &iph);
 
        if (data->bitmask & XT_IPVS_PROTO)
                if ((iph.protocol == data->l4proto) ^
@@ -85,7 +86,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
        /*
         * Check if the packet belongs to an existing entry
         */
-       cp = pp->conn_out_get(family, skb, &iph, 1 /* inverse */);
+       cp = pp->conn_out_get(ipvs, family, skb, &iph);
        if (unlikely(cp == NULL)) {
                match = false;
                goto out;
index 0778855ea5e75dff20f77f58a23c1f751c5ae45d..df8801e02a322fec2fac377d00d7a2bb8d389688 100644 (file)
@@ -200,7 +200,7 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p)
        unsigned char opts[MAX_IPOPTLEN];
        const struct xt_osf_finger *kf;
        const struct xt_osf_user_finger *f;
-       struct net *net = dev_net(p->in ? p->in : p->out);
+       struct net *net = p->net;
 
        if (!info)
                return false;
index 45e1b30e4fb214f850af2590425ab2a9748c6476..d725a27743a169fc80cc00f38dd2278696be3365 100644 (file)
@@ -237,7 +237,7 @@ static void recent_table_flush(struct recent_table *t)
 static bool
 recent_mt(const struct sk_buff *skb, struct xt_action_param *par)
 {
-       struct net *net = dev_net(par->in ? par->in : par->out);
+       struct net *net = par->net;
        struct recent_net *recent_net = recent_pernet(net);
        const struct xt_recent_mtinfo_v1 *info = par->matchinfo;
        struct recent_table *t;
index 43e26c8811007fc1736c16d60f8e0c647f171d3a..2ec08f04b816bc863e582fed000a2143d0b688a5 100644 (file)
@@ -143,7 +143,8 @@ static bool xt_socket_sk_is_transparent(struct sock *sk)
        }
 }
 
-static struct sock *xt_socket_lookup_slow_v4(const struct sk_buff *skb,
+static struct sock *xt_socket_lookup_slow_v4(struct net *net,
+                                            const struct sk_buff *skb,
                                             const struct net_device *indev)
 {
        const struct iphdr *iph = ip_hdr(skb);
@@ -197,7 +198,7 @@ static struct sock *xt_socket_lookup_slow_v4(const struct sk_buff *skb,
        }
 #endif
 
-       return xt_socket_get_sock_v4(dev_net(skb->dev), protocol, saddr, daddr,
+       return xt_socket_get_sock_v4(net, protocol, saddr, daddr,
                                     sport, dport, indev);
 }
 
@@ -209,7 +210,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
        struct sock *sk = skb->sk;
 
        if (!sk)
-               sk = xt_socket_lookup_slow_v4(skb, par->in);
+               sk = xt_socket_lookup_slow_v4(par->net, skb, par->in);
        if (sk) {
                bool wildcard;
                bool transparent = true;
@@ -335,7 +336,8 @@ xt_socket_get_sock_v6(struct net *net, const u8 protocol,
        return NULL;
 }
 
-static struct sock *xt_socket_lookup_slow_v6(const struct sk_buff *skb,
+static struct sock *xt_socket_lookup_slow_v6(struct net *net,
+                                            const struct sk_buff *skb,
                                             const struct net_device *indev)
 {
        __be16 uninitialized_var(dport), uninitialized_var(sport);
@@ -371,7 +373,7 @@ static struct sock *xt_socket_lookup_slow_v6(const struct sk_buff *skb,
                return NULL;
        }
 
-       return xt_socket_get_sock_v6(dev_net(skb->dev), tproto, saddr, daddr,
+       return xt_socket_get_sock_v6(net, tproto, saddr, daddr,
                                     sport, dport, indev);
 }
 
@@ -383,7 +385,7 @@ socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par)
        struct sock *sk = skb->sk;
 
        if (!sk)
-               sk = xt_socket_lookup_slow_v6(skb, par->in);
+               sk = xt_socket_lookup_slow_v6(par->net, skb, par->in);
        if (sk) {
                bool wildcard;
                bool transparent = true;
index 0a49a8c7c56432989de924e01563f692cf10e545..fafe33bdb61989e680dc4b26dbe99dcc1d4064b5 100644 (file)
@@ -2371,7 +2371,7 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname,
                int pos, idx, shift;
 
                err = 0;
-               netlink_table_grab();
+               netlink_lock_table();
                for (pos = 0; pos * 8 < nlk->ngroups; pos += sizeof(u32)) {
                        if (len - pos < sizeof(u32))
                                break;
@@ -2386,7 +2386,7 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname,
                }
                if (put_user(ALIGN(nlk->ngroups / 8, sizeof(u32)), optlen))
                        err = -EFAULT;
-               netlink_table_ungrab();
+               netlink_unlock_table();
                break;
        }
        case NETLINK_CAP_ACK:
index 2ed5f964772ee44fc693cf18bc6055308820bf78..bc0e504f33a68ee92897cb3c45241d4d3348deba 100644 (file)
@@ -39,7 +39,7 @@ void genl_unlock(void)
 EXPORT_SYMBOL(genl_unlock);
 
 #ifdef CONFIG_LOCKDEP
-int lockdep_genl_is_held(void)
+bool lockdep_genl_is_held(void)
 {
        return lockdep_is_held(&genl_mutex);
 }
@@ -1136,19 +1136,19 @@ int genlmsg_multicast_allns(struct genl_family *family, struct sk_buff *skb,
 }
 EXPORT_SYMBOL(genlmsg_multicast_allns);
 
-void genl_notify(struct genl_family *family,
-                struct sk_buff *skb, struct net *net, u32 portid, u32 group,
-                struct nlmsghdr *nlh, gfp_t flags)
+void genl_notify(struct genl_family *family, struct sk_buff *skb,
+                struct genl_info *info, u32 group, gfp_t flags)
 {
+       struct net *net = genl_info_net(info);
        struct sock *sk = net->genl_sock;
        int report = 0;
 
-       if (nlh)
-               report = nlmsg_report(nlh);
+       if (info->nlhdr)
+               report = nlmsg_report(info->nlhdr);
 
        if (WARN_ON_ONCE(group >= family->n_mcgrps))
                return;
        group = family->mcgrp_offset + group;
-       nlmsg_notify(sk, skb, portid, group, report, flags);
+       nlmsg_notify(sk, skb, info->snd_portid, group, report, flags);
 }
 EXPORT_SYMBOL(genl_notify);
index cff3f1614ad4557ff178804acb55bacbad328149..1fe3d3b362c0cdce72d8317488e8b1fc4d58ea1d 100644 (file)
@@ -449,7 +449,7 @@ error:
  * @dev: The nfc device that found the target
  * @target_idx: index of the target that must be deactivated
  */
-int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx)
+int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx, u8 mode)
 {
        int rc = 0;
 
@@ -476,7 +476,7 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx)
        if (dev->ops->check_presence)
                del_timer_sync(&dev->check_pres_timer);
 
-       dev->ops->deactivate_target(dev, dev->active_target);
+       dev->ops->deactivate_target(dev, dev->active_target, mode);
        dev->active_target = NULL;
 
 error:
index 009bcf317101003e4095fda37ed1b6b7e31565e1..23c2a118ac9f98c3cfd11c871a9126664f54838e 100644 (file)
@@ -631,7 +631,8 @@ static int digital_activate_target(struct nfc_dev *nfc_dev,
 }
 
 static void digital_deactivate_target(struct nfc_dev *nfc_dev,
-                                     struct nfc_target *target)
+                                     struct nfc_target *target,
+                                     u8 mode)
 {
        struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
 
index 6e061da2258a8bd32f80f585aaaf7e951180b7d3..2b0f0ac498d2c02c92a62d8e749a103f5b1849c1 100644 (file)
@@ -678,7 +678,8 @@ static int hci_activate_target(struct nfc_dev *nfc_dev,
 }
 
 static void hci_deactivate_target(struct nfc_dev *nfc_dev,
-                                 struct nfc_target *target)
+                                 struct nfc_target *target,
+                                 u8 mode)
 {
 }
 
index 1b90c05318521c76703db7ca86607decf006cbe8..1399a03fa6e66716cec38bd5d96df15d4226d0eb 100644 (file)
@@ -144,11 +144,13 @@ inline int nfc_llc_start(struct nfc_llc *llc)
 {
        return llc->ops->start(llc);
 }
+EXPORT_SYMBOL(nfc_llc_start);
 
 inline int nfc_llc_stop(struct nfc_llc *llc)
 {
        return llc->ops->stop(llc);
 }
+EXPORT_SYMBOL(nfc_llc_stop);
 
 inline void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
 {
index 901c1ddba841aa1816eda0c14f20972008623c42..85d4819ab657d85d250827813ec3d680f9f3d58a 100644 (file)
@@ -12,7 +12,7 @@ config NFC_NCI
 config NFC_NCI_SPI
        depends on NFC_NCI && SPI
        select CRC_CCITT
-       bool "NCI over SPI protocol support"
+       tristate "NCI over SPI protocol support"
        default n
        help
          NCI (NFC Controller Interface) is a communication protocol between
index b4b85b82e9882c7830a11dc7b6ce6c56f3157099..0ca31d9bf7413e8cadcc2171cb3727f7e26f7893 100644 (file)
@@ -6,7 +6,8 @@ obj-$(CONFIG_NFC_NCI) += nci.o
 
 nci-objs := core.o data.o lib.o ntf.o rsp.o hci.o
 
-nci-$(CONFIG_NFC_NCI_SPI) += spi.o
+nci_spi-y += spi.o
+obj-$(CONFIG_NFC_NCI_SPI) += nci_spi.o
 
 nci_uart-y += uart.o
 obj-$(CONFIG_NFC_NCI_UART) += nci_uart.o
index 943889b87a34bdd33e9c4a8c1b01df04808b3e00..10c99a5784212c92f7fa6ee69a43518ba89dc17c 100644 (file)
@@ -64,6 +64,19 @@ struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev,
        return NULL;
 }
 
+int nci_get_conn_info_by_id(struct nci_dev *ndev, u8 id)
+{
+       struct nci_conn_info *conn_info;
+
+       list_for_each_entry(conn_info, &ndev->conn_info_list, list) {
+               if (conn_info->id == id)
+                       return conn_info->conn_id;
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(nci_get_conn_info_by_id);
+
 /* ---- NCI requests ---- */
 
 void nci_req_complete(struct nci_dev *ndev, int result)
@@ -325,32 +338,46 @@ static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt)
                     sizeof(struct nci_rf_deactivate_cmd), &cmd);
 }
 
-struct nci_prop_cmd_param {
+struct nci_cmd_param {
        __u16 opcode;
        size_t len;
        __u8 *payload;
 };
 
-static void nci_prop_cmd_req(struct nci_dev *ndev, unsigned long opt)
+static void nci_generic_req(struct nci_dev *ndev, unsigned long opt)
 {
-       struct nci_prop_cmd_param *param = (struct nci_prop_cmd_param *)opt;
+       struct nci_cmd_param *param =
+               (struct nci_cmd_param *)opt;
 
        nci_send_cmd(ndev, param->opcode, param->len, param->payload);
 }
 
 int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload)
 {
-       struct nci_prop_cmd_param param;
+       struct nci_cmd_param param;
 
        param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid);
        param.len = len;
        param.payload = payload;
 
-       return __nci_request(ndev, nci_prop_cmd_req, (unsigned long)&param,
+       return __nci_request(ndev, nci_generic_req, (unsigned long)&param,
                             msecs_to_jiffies(NCI_CMD_TIMEOUT));
 }
 EXPORT_SYMBOL(nci_prop_cmd);
 
+int nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len, __u8 *payload)
+{
+       struct nci_cmd_param param;
+
+       param.opcode = opcode;
+       param.len = len;
+       param.payload = payload;
+
+       return __nci_request(ndev, nci_generic_req, (unsigned long)&param,
+                            msecs_to_jiffies(NCI_CMD_TIMEOUT));
+}
+EXPORT_SYMBOL(nci_core_cmd);
+
 int nci_core_reset(struct nci_dev *ndev)
 {
        return __nci_request(ndev, nci_reset_req, 0,
@@ -402,9 +429,8 @@ static int nci_open_device(struct nci_dev *ndev)
                                   msecs_to_jiffies(NCI_INIT_TIMEOUT));
        }
 
-       if (ndev->ops->post_setup) {
+       if (!rc && ndev->ops->post_setup)
                rc = ndev->ops->post_setup(ndev);
-       }
 
        if (!rc) {
                rc = __nci_request(ndev, nci_init_complete_req, 0,
@@ -540,7 +566,7 @@ static void nci_nfcee_discover_req(struct nci_dev *ndev, unsigned long opt)
 
 int nci_nfcee_discover(struct nci_dev *ndev, u8 action)
 {
-       return nci_request(ndev, nci_nfcee_discover_req, action,
+       return __nci_request(ndev, nci_nfcee_discover_req, action,
                                msecs_to_jiffies(NCI_CMD_TIMEOUT));
 }
 EXPORT_SYMBOL(nci_nfcee_discover);
@@ -561,8 +587,9 @@ int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode)
        cmd.nfcee_id = nfcee_id;
        cmd.nfcee_mode = nfcee_mode;
 
-       return nci_request(ndev, nci_nfcee_mode_set_req, (unsigned long)&cmd,
-                          msecs_to_jiffies(NCI_CMD_TIMEOUT));
+       return __nci_request(ndev, nci_nfcee_mode_set_req,
+                            (unsigned long)&cmd,
+                            msecs_to_jiffies(NCI_CMD_TIMEOUT));
 }
 EXPORT_SYMBOL(nci_nfcee_mode_set);
 
@@ -588,12 +615,19 @@ int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
        if (!cmd)
                return -ENOMEM;
 
+       if (!number_destination_params)
+               return -EINVAL;
+
        cmd->destination_type = destination_type;
        cmd->number_destination_params = number_destination_params;
        memcpy(cmd->params, params, params_len);
 
        data.cmd = cmd;
-       ndev->cur_id = params->value[DEST_SPEC_PARAMS_ID_INDEX];
+
+       if (params->length > 0)
+               ndev->cur_id = params->value[DEST_SPEC_PARAMS_ID_INDEX];
+       else
+               ndev->cur_id = 0;
 
        r = __nci_request(ndev, nci_core_conn_create_req,
                          (unsigned long)&data,
@@ -612,8 +646,8 @@ static void nci_core_conn_close_req(struct nci_dev *ndev, unsigned long opt)
 
 int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id)
 {
-       return nci_request(ndev, nci_core_conn_close_req, conn_id,
-                               msecs_to_jiffies(NCI_CMD_TIMEOUT));
+       return __nci_request(ndev, nci_core_conn_close_req, conn_id,
+                            msecs_to_jiffies(NCI_CMD_TIMEOUT));
 }
 EXPORT_SYMBOL(nci_core_conn_close);
 
@@ -801,9 +835,11 @@ static int nci_activate_target(struct nfc_dev *nfc_dev,
 }
 
 static void nci_deactivate_target(struct nfc_dev *nfc_dev,
-                                 struct nfc_target *target)
+                                 struct nfc_target *target,
+                                 __u8 mode)
 {
        struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+       u8 nci_mode = NCI_DEACTIVATE_TYPE_IDLE_MODE;
 
        pr_debug("entry\n");
 
@@ -814,9 +850,14 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
 
        ndev->target_active_prot = 0;
 
+       switch (mode) {
+       case NFC_TARGET_MODE_SLEEP:
+               nci_mode = NCI_DEACTIVATE_TYPE_SLEEP_MODE;
+               break;
+       }
+
        if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) {
-               nci_request(ndev, nci_rf_deactivate_req,
-                           NCI_DEACTIVATE_TYPE_IDLE_MODE,
+               nci_request(ndev, nci_rf_deactivate_req, nci_mode,
                            msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
        }
 }
@@ -850,7 +891,7 @@ static int nci_dep_link_down(struct nfc_dev *nfc_dev)
        pr_debug("entry\n");
 
        if (nfc_dev->rf_mode == NFC_RF_INITIATOR) {
-               nci_deactivate_target(nfc_dev, NULL);
+               nci_deactivate_target(nfc_dev, NULL, NCI_DEACTIVATE_TYPE_IDLE_MODE);
        } else {
                if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE ||
                    atomic_read(&ndev->state) == NCI_DISCOVERY) {
@@ -1177,7 +1218,7 @@ int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(nci_recv_frame);
 
-static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
+int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
 {
        pr_debug("len %d\n", skb->len);
 
@@ -1195,6 +1236,7 @@ static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
 
        return ndev->ops->send(ndev, skb);
 }
+EXPORT_SYMBOL(nci_send_frame);
 
 /* Send NCI command */
 int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload)
@@ -1226,48 +1268,80 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload)
 
        return 0;
 }
+EXPORT_SYMBOL(nci_send_cmd);
 
 /* Proprietary commands API */
-static struct nci_prop_ops *prop_cmd_lookup(struct nci_dev *ndev,
-                                           __u16 opcode)
+static struct nci_driver_ops *ops_cmd_lookup(struct nci_driver_ops *ops,
+                                            size_t n_ops,
+                                            __u16 opcode)
 {
        size_t i;
-       struct nci_prop_ops *prop_op;
+       struct nci_driver_ops *op;
 
-       if (!ndev->ops->prop_ops || !ndev->ops->n_prop_ops)
+       if (!ops || !n_ops)
                return NULL;
 
-       for (i = 0; i < ndev->ops->n_prop_ops; i++) {
-               prop_op = &ndev->ops->prop_ops[i];
-               if (prop_op->opcode == opcode)
-                       return prop_op;
+       for (i = 0; i < n_ops; i++) {
+               op = &ops[i];
+               if (op->opcode == opcode)
+                       return op;
        }
 
        return NULL;
 }
 
-int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode,
-                       struct sk_buff *skb)
+static int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode,
+                            struct sk_buff *skb, struct nci_driver_ops *ops,
+                            size_t n_ops)
 {
-       struct nci_prop_ops *prop_op;
+       struct nci_driver_ops *op;
 
-       prop_op = prop_cmd_lookup(ndev, rsp_opcode);
-       if (!prop_op || !prop_op->rsp)
+       op = ops_cmd_lookup(ops, n_ops, rsp_opcode);
+       if (!op || !op->rsp)
                return -ENOTSUPP;
 
-       return prop_op->rsp(ndev, skb);
+       return op->rsp(ndev, skb);
 }
 
-int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode,
-                       struct sk_buff *skb)
+static int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode,
+                            struct sk_buff *skb, struct nci_driver_ops *ops,
+                            size_t n_ops)
 {
-       struct nci_prop_ops *prop_op;
+       struct nci_driver_ops *op;
 
-       prop_op = prop_cmd_lookup(ndev, ntf_opcode);
-       if (!prop_op || !prop_op->ntf)
+       op = ops_cmd_lookup(ops, n_ops, ntf_opcode);
+       if (!op || !op->ntf)
                return -ENOTSUPP;
 
-       return prop_op->ntf(ndev, skb);
+       return op->ntf(ndev, skb);
+}
+
+int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode,
+                       struct sk_buff *skb)
+{
+       return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->prop_ops,
+                                ndev->ops->n_prop_ops);
+}
+
+int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode,
+                       struct sk_buff *skb)
+{
+       return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->prop_ops,
+                                ndev->ops->n_prop_ops);
+}
+
+int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode,
+                       struct sk_buff *skb)
+{
+       return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->core_ops,
+                                 ndev->ops->n_core_ops);
+}
+
+int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode,
+                       struct sk_buff *skb)
+{
+       return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->core_ops,
+                                ndev->ops->n_core_ops);
 }
 
 /* ---- NCI TX Data worker thread ---- */
index 566466d90048d92e3ac7c31fa73c32d398089126..dbd24254412a69d4c18c69a37d90608854a2ba9e 100644 (file)
@@ -90,6 +90,18 @@ static inline void nci_push_data_hdr(struct nci_dev *ndev,
        nci_pbf_set((__u8 *)hdr, pbf);
 }
 
+int nci_conn_max_data_pkt_payload_size(struct nci_dev *ndev, __u8 conn_id)
+{
+       struct nci_conn_info *conn_info;
+
+       conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
+       if (!conn_info)
+               return -EPROTO;
+
+       return conn_info->max_pkt_payload_len;
+}
+EXPORT_SYMBOL(nci_conn_max_data_pkt_payload_size);
+
 static int nci_queue_tx_data_frags(struct nci_dev *ndev,
                                   __u8 conn_id,
                                   struct sk_buff *skb) {
@@ -203,6 +215,7 @@ free_exit:
 exit:
        return rc;
 }
+EXPORT_SYMBOL(nci_send_data);
 
 /* ----------------- NCI RX Data ----------------- */
 
index 609f92283d1b74d118eb00d0fcedd6ece105e844..2aedac15cb592353de7bb2ab8ec3ebb91bd5325a 100644 (file)
@@ -70,6 +70,7 @@ struct nci_hcp_packet {
 #define NCI_HCI_ANY_SET_PARAMETER  0x01
 #define NCI_HCI_ANY_GET_PARAMETER  0x02
 #define NCI_HCI_ANY_CLOSE_PIPE     0x04
+#define NCI_HCI_ADM_CLEAR_ALL_PIPE 0x14
 
 #define NCI_HFP_NO_CHAINING        0x80
 
@@ -78,6 +79,8 @@ struct nci_hcp_packet {
 #define NCI_EVT_HOT_PLUG           0x03
 
 #define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY       0x01
+#define NCI_HCI_ADM_CREATE_PIPE                        0x10
+#define NCI_HCI_ADM_DELETE_PIPE                        0x11
 
 /* HCP headers */
 #define NCI_HCI_HCP_PACKET_HEADER_LEN      1
@@ -101,6 +104,20 @@ struct nci_hcp_packet {
 #define NCI_HCP_MSG_GET_CMD(header)  (header & 0x3f)
 #define NCI_HCP_MSG_GET_PIPE(header) (header & 0x7f)
 
+static int nci_hci_result_to_errno(u8 result)
+{
+       switch (result) {
+       case NCI_HCI_ANY_OK:
+               return 0;
+       case NCI_HCI_ANY_E_REG_PAR_UNKNOWN:
+               return -EOPNOTSUPP;
+       case NCI_HCI_ANY_E_TIMEOUT:
+               return -ETIME;
+       default:
+               return -1;
+       }
+}
+
 /* HCI core */
 static void nci_hci_reset_pipes(struct nci_hci_dev *hdev)
 {
@@ -146,18 +163,18 @@ static int nci_hci_send_data(struct nci_dev *ndev, u8 pipe,
        if (!conn_info)
                return -EPROTO;
 
-       skb = nci_skb_alloc(ndev, 2 + conn_info->max_pkt_payload_len +
+       i = 0;
+       skb = nci_skb_alloc(ndev, conn_info->max_pkt_payload_len +
                            NCI_DATA_HDR_SIZE, GFP_KERNEL);
        if (!skb)
                return -ENOMEM;
 
-       skb_reserve(skb, 2 + NCI_DATA_HDR_SIZE);
+       skb_reserve(skb, NCI_DATA_HDR_SIZE + 2);
        *skb_push(skb, 1) = data_type;
 
-       i = 0;
-       len = conn_info->max_pkt_payload_len;
-
        do {
+               len = conn_info->max_pkt_payload_len;
+
                /* If last packet add NCI_HFP_NO_CHAINING */
                if (i + conn_info->max_pkt_payload_len -
                    (skb->len + 1) >= data_len) {
@@ -177,9 +194,15 @@ static int nci_hci_send_data(struct nci_dev *ndev, u8 pipe,
                        return r;
 
                i += len;
+
                if (i < data_len) {
-                       skb_trim(skb, 0);
-                       skb_pull(skb, len);
+                       skb = nci_skb_alloc(ndev,
+                                           conn_info->max_pkt_payload_len +
+                                           NCI_DATA_HDR_SIZE, GFP_KERNEL);
+                       if (!skb)
+                               return -ENOMEM;
+
+                       skb_reserve(skb, NCI_DATA_HDR_SIZE + 1);
                }
        } while (i < data_len);
 
@@ -212,7 +235,8 @@ int nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd,
                     const u8 *param, size_t param_len,
                     struct sk_buff **skb)
 {
-       struct nci_conn_info    *conn_info;
+       struct nci_hcp_message *message;
+       struct nci_conn_info   *conn_info;
        struct nci_data data;
        int r;
        u8 pipe = ndev->hci_dev->gate2pipe[gate];
@@ -232,14 +256,34 @@ int nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd,
 
        r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data,
                        msecs_to_jiffies(NCI_DATA_TIMEOUT));
-
-       if (r == NCI_STATUS_OK && skb)
-               *skb = conn_info->rx_skb;
+       if (r == NCI_STATUS_OK) {
+               message = (struct nci_hcp_message *)conn_info->rx_skb->data;
+               r = nci_hci_result_to_errno(
+                       NCI_HCP_MSG_GET_CMD(message->header));
+               skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN);
+
+               if (!r && skb)
+                       *skb = conn_info->rx_skb;
+       }
 
        return r;
 }
 EXPORT_SYMBOL(nci_hci_send_cmd);
 
+int nci_hci_clear_all_pipes(struct nci_dev *ndev)
+{
+       int r;
+
+       r = nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE,
+                            NCI_HCI_ADM_CLEAR_ALL_PIPE, NULL, 0, NULL);
+       if (r < 0)
+               return r;
+
+       nci_hci_reset_pipes(ndev->hci_dev);
+       return r;
+}
+EXPORT_SYMBOL(nci_hci_clear_all_pipes);
+
 static void nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
                                   u8 event, struct sk_buff *skb)
 {
@@ -328,9 +372,6 @@ static void nci_hci_resp_received(struct nci_dev *ndev, u8 pipe,
        struct nci_conn_info    *conn_info;
        u8 status = result;
 
-       if (result != NCI_HCI_ANY_OK)
-               goto exit;
-
        conn_info = ndev->hci_dev->conn_info;
        if (!conn_info) {
                status = NCI_STATUS_REJECTED;
@@ -340,7 +381,7 @@ static void nci_hci_resp_received(struct nci_dev *ndev, u8 pipe,
        conn_info->rx_skb = skb;
 
 exit:
-       nci_req_complete(ndev, status);
+       nci_req_complete(ndev, NCI_STATUS_OK);
 }
 
 /* Receive hcp message for pipe, with type and cmd.
@@ -366,7 +407,7 @@ static void nci_hci_hcp_message_rx(struct nci_dev *ndev, u8 pipe,
                break;
        }
 
-       nci_req_complete(ndev, 0);
+       nci_req_complete(ndev, NCI_STATUS_OK);
 }
 
 static void nci_hci_msg_rx_work(struct work_struct *work)
@@ -378,7 +419,7 @@ static void nci_hci_msg_rx_work(struct work_struct *work)
        u8 pipe, type, instruction;
 
        while ((skb = skb_dequeue(&hdev->msg_rx_queue)) != NULL) {
-               pipe = skb->data[0];
+               pipe = NCI_HCP_MSG_GET_PIPE(skb->data[0]);
                skb_pull(skb, NCI_HCI_HCP_PACKET_HEADER_LEN);
                message = (struct nci_hcp_message *)skb->data;
                type = NCI_HCP_MSG_GET_TYPE(message->header);
@@ -395,7 +436,7 @@ void nci_hci_data_received_cb(void *context,
 {
        struct nci_dev *ndev = (struct nci_dev *)context;
        struct nci_hcp_packet *packet;
-       u8 pipe, type, instruction;
+       u8 pipe, type;
        struct sk_buff *hcp_skb;
        struct sk_buff *frag_skb;
        int msg_len;
@@ -415,7 +456,7 @@ void nci_hci_data_received_cb(void *context,
 
        /* it's the last fragment. Does it need re-aggregation? */
        if (skb_queue_len(&ndev->hci_dev->rx_hcp_frags)) {
-               pipe = packet->header & NCI_HCI_FRAGMENT;
+               pipe = NCI_HCP_MSG_GET_PIPE(packet->header);
                skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb);
 
                msg_len = 0;
@@ -434,7 +475,7 @@ void nci_hci_data_received_cb(void *context,
                *skb_put(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN) = pipe;
 
                skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) {
-                      msg_len = frag_skb->len - NCI_HCI_HCP_PACKET_HEADER_LEN;
+                       msg_len = frag_skb->len - NCI_HCI_HCP_PACKET_HEADER_LEN;
                        memcpy(skb_put(hcp_skb, msg_len), frag_skb->data +
                               NCI_HCI_HCP_PACKET_HEADER_LEN, msg_len);
                }
@@ -452,11 +493,10 @@ void nci_hci_data_received_cb(void *context,
        packet = (struct nci_hcp_packet *)hcp_skb->data;
        type = NCI_HCP_MSG_GET_TYPE(packet->message.header);
        if (type == NCI_HCI_HCP_RESPONSE) {
-               pipe = packet->header;
-               instruction = NCI_HCP_MSG_GET_CMD(packet->message.header);
-               skb_pull(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN +
-                        NCI_HCI_HCP_MESSAGE_HEADER_LEN);
-               nci_hci_hcp_message_rx(ndev, pipe, type, instruction, hcp_skb);
+               pipe = NCI_HCP_MSG_GET_PIPE(packet->header);
+               skb_pull(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN);
+               nci_hci_hcp_message_rx(ndev, pipe, type,
+                                      NCI_STATUS_OK, hcp_skb);
        } else {
                skb_queue_tail(&ndev->hci_dev->msg_rx_queue, hcp_skb);
                schedule_work(&ndev->hci_dev->msg_rx_work);
@@ -485,9 +525,47 @@ int nci_hci_open_pipe(struct nci_dev *ndev, u8 pipe)
 }
 EXPORT_SYMBOL(nci_hci_open_pipe);
 
+static u8 nci_hci_create_pipe(struct nci_dev *ndev, u8 dest_host,
+                             u8 dest_gate, int *result)
+{
+       u8 pipe;
+       struct sk_buff *skb;
+       struct nci_hci_create_pipe_params params;
+       struct nci_hci_create_pipe_resp *resp;
+
+       pr_debug("gate=%d\n", dest_gate);
+
+       params.src_gate = NCI_HCI_ADMIN_GATE;
+       params.dest_host = dest_host;
+       params.dest_gate = dest_gate;
+
+       *result = nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE,
+                                  NCI_HCI_ADM_CREATE_PIPE,
+                                  (u8 *)&params, sizeof(params), &skb);
+       if (*result < 0)
+               return NCI_HCI_INVALID_PIPE;
+
+       resp = (struct nci_hci_create_pipe_resp *)skb->data;
+       pipe = resp->pipe;
+       kfree_skb(skb);
+
+       pr_debug("pipe created=%d\n", pipe);
+
+       return pipe;
+}
+
+static int nci_hci_delete_pipe(struct nci_dev *ndev, u8 pipe)
+{
+       pr_debug("\n");
+
+       return nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE,
+                               NCI_HCI_ADM_DELETE_PIPE, &pipe, 1, NULL);
+}
+
 int nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx,
                      const u8 *param, size_t param_len)
 {
+       struct nci_hcp_message *message;
        struct nci_conn_info *conn_info;
        struct nci_data data;
        int r;
@@ -520,6 +598,12 @@ int nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx,
        r = nci_request(ndev, nci_hci_send_data_req,
                        (unsigned long)&data,
                        msecs_to_jiffies(NCI_DATA_TIMEOUT));
+       if (r == NCI_STATUS_OK) {
+               message = (struct nci_hcp_message *)conn_info->rx_skb->data;
+               r = nci_hci_result_to_errno(
+                       NCI_HCP_MSG_GET_CMD(message->header));
+               skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN);
+       }
 
        kfree(tmp);
        return r;
@@ -529,6 +613,7 @@ EXPORT_SYMBOL(nci_hci_set_param);
 int nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx,
                      struct sk_buff **skb)
 {
+       struct nci_hcp_message *message;
        struct nci_conn_info    *conn_info;
        struct nci_data data;
        int r;
@@ -553,8 +638,15 @@ int nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx,
        r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data,
                        msecs_to_jiffies(NCI_DATA_TIMEOUT));
 
-       if (r == NCI_STATUS_OK)
-               *skb = conn_info->rx_skb;
+       if (r == NCI_STATUS_OK) {
+               message = (struct nci_hcp_message *)conn_info->rx_skb->data;
+               r = nci_hci_result_to_errno(
+                       NCI_HCP_MSG_GET_CMD(message->header));
+               skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN);
+
+               if (!r && skb)
+                       *skb = conn_info->rx_skb;
+       }
 
        return r;
 }
@@ -563,6 +655,7 @@ EXPORT_SYMBOL(nci_hci_get_param);
 int nci_hci_connect_gate(struct nci_dev *ndev,
                         u8 dest_host, u8 dest_gate, u8 pipe)
 {
+       bool pipe_created = false;
        int r;
 
        if (pipe == NCI_HCI_DO_NOT_OPEN_PIPE)
@@ -581,12 +674,26 @@ int nci_hci_connect_gate(struct nci_dev *ndev,
        case NCI_HCI_ADMIN_GATE:
                pipe = NCI_HCI_ADMIN_PIPE;
        break;
+       default:
+               pipe = nci_hci_create_pipe(ndev, dest_host, dest_gate, &r);
+               if (pipe < 0)
+                       return r;
+               pipe_created = true;
+               break;
        }
 
 open_pipe:
        r = nci_hci_open_pipe(ndev, pipe);
-       if (r < 0)
+       if (r < 0) {
+               if (pipe_created) {
+                       if (nci_hci_delete_pipe(ndev, pipe) < 0) {
+                               /* TODO: Cannot clean by deleting pipe...
+                                * -> inconsistent state
+                                */
+                       }
+               }
                return r;
+       }
 
        ndev->hci_dev->pipes[pipe].gate = dest_gate;
        ndev->hci_dev->pipes[pipe].host = dest_host;
@@ -653,6 +760,10 @@ int nci_hci_dev_session_init(struct nci_dev *ndev)
                /* Restore gate<->pipe table from some proprietary location. */
                r = ndev->ops->hci_load_session(ndev);
        } else {
+               r = nci_hci_clear_all_pipes(ndev);
+               if (r < 0)
+                       goto exit;
+
                r = nci_hci_dev_connect_gates(ndev,
                                              ndev->hci_dev->init_data.gate_count,
                                              ndev->hci_dev->init_data.gates);
index 5d1c2e391c56a8caf3b457796bcfbc60bf8982a9..2ada2b39e355efa7fd0e4aa154115bec580aadd7 100644 (file)
@@ -759,7 +759,7 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
        skb_pull(skb, NCI_CTRL_HDR_SIZE);
 
        if (nci_opcode_gid(ntf_opcode) == NCI_GID_PROPRIETARY) {
-               if (nci_prop_ntf_packet(ndev, ntf_opcode, skb)) {
+               if (nci_prop_ntf_packet(ndev, ntf_opcode, skb) == -ENOTSUPP) {
                        pr_err("unsupported ntf opcode 0x%x\n",
                               ntf_opcode);
                }
@@ -805,6 +805,7 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
                break;
        }
 
+       nci_core_ntf_packet(ndev, ntf_opcode, skb);
 end:
        kfree_skb(skb);
 }
index 408bd8f857ab9fd506ad137dd0333db0b2556613..9b6eb913d801b000dc4f50eb3bf0bad7435e7e34 100644 (file)
@@ -355,6 +355,7 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
                break;
        }
 
+       nci_core_rsp_packet(ndev, rsp_opcode, skb);
 end:
        kfree_skb(skb);
 
index ec250e77763a1d1e23f0560c151972ce274f82bc..d904cd2f14423ce108e9d4bfc28b22be353d93f2 100644 (file)
@@ -18,6 +18,8 @@
 
 #define pr_fmt(fmt) "nci_spi: %s: " fmt, __func__
 
+#include <linux/module.h>
+
 #include <linux/export.h>
 #include <linux/spi/spi.h>
 #include <linux/crc-ccitt.h>
@@ -56,6 +58,7 @@ static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb,
        }
        t.cs_change = cs_change;
        t.delay_usecs = nspi->xfer_udelay;
+       t.speed_hz = nspi->xfer_speed_hz;
 
        spi_message_init(&m);
        spi_message_add_tail(&t, &m);
@@ -142,7 +145,8 @@ struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
 
        nspi->acknowledge_mode = acknowledge_mode;
        nspi->xfer_udelay = delay;
-
+       /* Use controller max SPI speed by default */
+       nspi->xfer_speed_hz = 0;
        nspi->spi = spi;
        nspi->ndev = ndev;
        init_completion(&nspi->req_completion);
@@ -195,12 +199,14 @@ static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
        tx.tx_buf = req;
        tx.len = 2;
        tx.cs_change = 0;
+       tx.speed_hz = nspi->xfer_speed_hz;
        spi_message_add_tail(&tx, &m);
 
        memset(&rx, 0, sizeof(struct spi_transfer));
        rx.rx_buf = resp_hdr;
        rx.len = 2;
        rx.cs_change = 1;
+       rx.speed_hz = nspi->xfer_speed_hz;
        spi_message_add_tail(&rx, &m);
 
        ret = spi_sync(nspi->spi, &m);
@@ -224,6 +230,7 @@ static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
        rx.len = rx_len;
        rx.cs_change = 0;
        rx.delay_usecs = nspi->xfer_udelay;
+       rx.speed_hz = nspi->xfer_speed_hz;
        spi_message_add_tail(&rx, &m);
 
        ret = spi_sync(nspi->spi, &m);
@@ -320,3 +327,5 @@ done:
        return skb;
 }
 EXPORT_SYMBOL_GPL(nci_spi_read);
+
+MODULE_LICENSE("GPL");
index 853172c27f68043f47ddd45a1e778e9e87efd163..f58c1fba10268b9c1336d205ff294e906dd04791 100644 (file)
@@ -885,7 +885,7 @@ static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info)
        target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]);
        protocol = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
 
-       nfc_deactivate_target(dev, target_idx);
+       nfc_deactivate_target(dev, target_idx, NFC_TARGET_MODE_SLEEP);
        rc = nfc_activate_target(dev, target_idx, protocol);
 
        nfc_put_device(dev);
@@ -1109,10 +1109,8 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info)
        idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
 
        dev = nfc_get_device(idx);
-       if (!dev) {
-               rc = -ENODEV;
-               goto exit;
-       }
+       if (!dev)
+               return -ENODEV;
 
        device_lock(&dev->dev);
 
index 5c93e8412a2639f3d1c77cf7ee5e15a95991a858..c20b784ad72069ae599bec30d8896ac693a8bda9 100644 (file)
@@ -25,6 +25,9 @@
 #include <net/nfc/nfc.h>
 #include <net/sock.h>
 
+#define NFC_TARGET_MODE_IDLE 0
+#define NFC_TARGET_MODE_SLEEP 1
+
 struct nfc_protocol {
        int id;
        struct proto *proto;
@@ -147,7 +150,7 @@ int nfc_dep_link_down(struct nfc_dev *dev);
 
 int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol);
 
-int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx);
+int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx, u8 mode);
 
 int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
                      data_exchange_cb_t cb, void *cb_context);
index e9a91488fe3d388d90f24b3a2e645bbf71cdb60a..e386e6c90b179f9494d25f412133a6c42fc29e4c 100644 (file)
@@ -321,7 +321,8 @@ static void rawsock_destruct(struct sock *sk)
 
        if (sk->sk_state == TCP_ESTABLISHED) {
                nfc_deactivate_target(nfc_rawsock(sk)->dev,
-                                     nfc_rawsock(sk)->target_idx);
+                                     nfc_rawsock(sk)->target_idx,
+                                     NFC_TARGET_MODE_IDLE);
                nfc_put_device(nfc_rawsock(sk)->dev);
        }
 
index c6a39bf2c3b954481a7217fa2b0f95778a2376b8..c88d0f2d3e019b2caff3c19742c9b4c0886e6298 100644 (file)
@@ -620,7 +620,7 @@ static int set_sctp(struct sk_buff *skb, struct sw_flow_key *flow_key,
        return 0;
 }
 
-static int ovs_vport_output(struct sock *sock, struct sk_buff *skb)
+static int ovs_vport_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct ovs_frag_data *data = this_cpu_ptr(&ovs_frag_data_storage);
        struct vport *vport = data->vport;
@@ -679,8 +679,8 @@ static void prepare_frag(struct vport *vport, struct sk_buff *skb)
        skb_pull(skb, hlen);
 }
 
-static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru,
-                        __be16 ethertype)
+static void ovs_fragment(struct net *net, struct vport *vport,
+                        struct sk_buff *skb, u16 mru, __be16 ethertype)
 {
        if (skb_network_offset(skb) > MAX_L2_LEN) {
                OVS_NLERR(1, "L2 header too long to fragment");
@@ -700,7 +700,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru,
                skb_dst_set_noref(skb, &ovs_dst);
                IPCB(skb)->frag_max_size = mru;
 
-               ip_do_fragment(skb->sk, skb, ovs_vport_output);
+               ip_do_fragment(net, skb->sk, skb, ovs_vport_output);
                refdst_drop(orig_dst);
        } else if (ethertype == htons(ETH_P_IPV6)) {
                const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops();
@@ -721,7 +721,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru,
                skb_dst_set_noref(skb, &ovs_rt.dst);
                IP6CB(skb)->frag_max_size = mru;
 
-               v6ops->fragment(skb->sk, skb, ovs_vport_output);
+               v6ops->fragment(net, skb->sk, skb, ovs_vport_output);
                refdst_drop(orig_dst);
        } else {
                WARN_ONCE(1, "Failed fragment ->%s: eth=%04x, MRU=%d, MTU=%d.",
@@ -746,6 +746,7 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
                if (likely(!mru || (skb->len <= mru + ETH_HLEN))) {
                        ovs_vport_send(vport, skb);
                } else if (mru <= vport->dev->mtu) {
+                       struct net *net = read_pnet(&dp->net);
                        __be16 ethertype = key->eth.type;
 
                        if (!is_flow_key_valid(key)) {
@@ -755,7 +756,7 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
                                        ethertype = vlan_get_protocol(skb);
                        }
 
-                       ovs_fragment(vport, skb, mru, ethertype);
+                       ovs_fragment(net, vport, skb, mru, ethertype);
                } else {
                        kfree_skb(skb);
                }
@@ -768,7 +769,6 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
                            struct sw_flow_key *key, const struct nlattr *attr,
                            const struct nlattr *actions, int actions_len)
 {
-       struct ip_tunnel_info info;
        struct dp_upcall_info upcall;
        const struct nlattr *a;
        int rem;
@@ -796,11 +796,9 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
                        if (vport) {
                                int err;
 
-                               upcall.egress_tun_info = &info;
-                               err = ovs_vport_get_egress_tun_info(vport, skb,
-                                                                   &upcall);
-                               if (err)
-                                       upcall.egress_tun_info = NULL;
+                               err = dev_fill_metadata_dst(vport->dev, skb);
+                               if (!err)
+                                       upcall.egress_tun_info = skb_tunnel_info(skb);
                        }
 
                        break;
@@ -1112,8 +1110,8 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
                                             nla_data(a));
 
                        /* Hide stolen IP fragments from user space. */
-                       if (err == -EINPROGRESS)
-                               return 0;
+                       if (err)
+                               return err == -EINPROGRESS ? 0 : err;
                        break;
                }
 
index 80bf702715bb3e8875ded486dbff63d86d7797f8..c2cc11168fd5e164fa122da5dd44f7aa47279a1e 100644 (file)
@@ -151,6 +151,8 @@ static void ovs_ct_update_key(const struct sk_buff *skb,
        ct = nf_ct_get(skb, &ctinfo);
        if (ct) {
                state = ovs_ct_get_state(ctinfo);
+               if (!nf_ct_is_confirmed(ct))
+                       state |= OVS_CS_F_NEW;
                if (ct->master)
                        state |= OVS_CS_F_RELATED;
                zone = nf_ct_zone(ct);
@@ -222,9 +224,6 @@ static int ovs_ct_set_labels(struct sk_buff *skb, struct sw_flow_key *key,
        struct nf_conn *ct;
        int err;
 
-       if (!IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS))
-               return -ENOTSUPP;
-
        /* The connection could be invalid, in which case set_label is no-op.*/
        ct = nf_ct_get(skb, &ctinfo);
        if (!ct)
@@ -294,6 +293,9 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto)
        return helper->help(skb, protoff, ct, ctinfo);
 }
 
+/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
+ * value if 'skb' is freed.
+ */
 static int handle_fragments(struct net *net, struct sw_flow_key *key,
                            u16 zone, struct sk_buff *skb)
 {
@@ -304,32 +306,40 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
                int err;
 
                memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
-               err = ip_defrag(skb, user);
+               err = ip_defrag(net, skb, user);
                if (err)
                        return err;
 
                ovs_cb.mru = IPCB(skb)->frag_max_size;
-       } else if (key->eth.type == htons(ETH_P_IPV6)) {
 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
+       } else if (key->eth.type == htons(ETH_P_IPV6)) {
                enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone;
                struct sk_buff *reasm;
 
                memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));
-               reasm = nf_ct_frag6_gather(skb, user);
+               reasm = nf_ct_frag6_gather(net, skb, user);
                if (!reasm)
                        return -EINPROGRESS;
 
-               if (skb == reasm)
+               if (skb == reasm) {
+                       kfree_skb(skb);
                        return -EINVAL;
+               }
+
+               /* Don't free 'skb' even though it is one of the original
+                * fragments, as we're going to morph it into the head.
+                */
+               skb_get(skb);
+               nf_ct_frag6_consume_orig(reasm);
 
                key->ip.proto = ipv6_hdr(reasm)->nexthdr;
                skb_morph(skb, reasm);
+               skb->next = reasm->next;
                consume_skb(reasm);
                ovs_cb.mru = IP6CB(skb)->frag_max_size;
-#else
-               return -EPFNOSUPPORT;
 #endif
        } else {
+               kfree_skb(skb);
                return -EPFNOSUPPORT;
        }
 
@@ -347,7 +357,7 @@ ovs_ct_expect_find(struct net *net, const struct nf_conntrack_zone *zone,
 {
        struct nf_conntrack_tuple tuple;
 
-       if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), proto, &tuple))
+       if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), proto, net, &tuple))
                return NULL;
        return __nf_ct_expect_find(net, zone, &tuple);
 }
@@ -377,7 +387,7 @@ static bool skb_nfct_cached(const struct net *net, const struct sk_buff *skb,
        return true;
 }
 
-static int __ovs_ct_lookup(struct net *net, const struct sw_flow_key *key,
+static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key,
                           const struct ovs_conntrack_info *info,
                           struct sk_buff *skb)
 {
@@ -408,6 +418,8 @@ static int __ovs_ct_lookup(struct net *net, const struct sw_flow_key *key,
                }
        }
 
+       ovs_ct_update_key(skb, key, true);
+
        return 0;
 }
 
@@ -430,8 +442,6 @@ static int ovs_ct_lookup(struct net *net, struct sw_flow_key *key,
                err = __ovs_ct_lookup(net, key, info, skb);
                if (err)
                        return err;
-
-               ovs_ct_update_key(skb, key, true);
        }
 
        return 0;
@@ -460,8 +470,6 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key,
        if (nf_conntrack_confirm(skb) != NF_ACCEPT)
                return -EINVAL;
 
-       ovs_ct_update_key(skb, key, true);
-
        return 0;
 }
 
@@ -476,6 +484,9 @@ static bool labels_nonzero(const struct ovs_key_ct_labels *labels)
        return false;
 }
 
+/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
+ * value if 'skb' is freed.
+ */
 int ovs_ct_execute(struct net *net, struct sk_buff *skb,
                   struct sw_flow_key *key,
                   const struct ovs_conntrack_info *info)
@@ -511,6 +522,8 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
                                        &info->labels.mask);
 err:
        skb_push(skb, nh_ofs);
+       if (err)
+               kfree_skb(skb);
        return err;
 }
 
@@ -587,6 +600,10 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
                case OVS_CT_ATTR_MARK: {
                        struct md_mark *mark = nla_data(a);
 
+                       if (!mark->mask) {
+                               OVS_NLERR(log, "ct_mark mask cannot be 0");
+                               return -EINVAL;
+                       }
                        info->mark = *mark;
                        break;
                }
@@ -595,6 +612,10 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
                case OVS_CT_ATTR_LABELS: {
                        struct md_labels *labels = nla_data(a);
 
+                       if (!labels_nonzero(&labels->mask)) {
+                               OVS_NLERR(log, "ct_labels mask cannot be 0");
+                               return -EINVAL;
+                       }
                        info->labels = *labels;
                        break;
                }
@@ -705,11 +726,12 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info,
        if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) &&
            nla_put_u16(skb, OVS_CT_ATTR_ZONE, ct_info->zone.id))
                return -EMSGSIZE;
-       if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) &&
+       if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && ct_info->mark.mask &&
            nla_put(skb, OVS_CT_ATTR_MARK, sizeof(ct_info->mark),
                    &ct_info->mark))
                return -EMSGSIZE;
        if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) &&
+           labels_nonzero(&ct_info->labels.mask) &&
            nla_put(skb, OVS_CT_ATTR_LABELS, sizeof(ct_info->labels),
                    &ct_info->labels))
                return -EMSGSIZE;
index da8714942c95f73803aa48a157484e898651bc8f..a7544f405c1626f6564075f4453832b311b1ac29 100644 (file)
@@ -35,12 +35,9 @@ void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key);
 int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb);
 void ovs_ct_free_action(const struct nlattr *a);
 
-static inline bool ovs_ct_state_supported(u32 state)
-{
-       return !(state & ~(OVS_CS_F_NEW | OVS_CS_F_ESTABLISHED |
-                        OVS_CS_F_RELATED | OVS_CS_F_REPLY_DIR |
-                        OVS_CS_F_INVALID | OVS_CS_F_TRACKED));
-}
+#define CT_SUPPORTED_MASK (OVS_CS_F_NEW | OVS_CS_F_ESTABLISHED | \
+                          OVS_CS_F_RELATED | OVS_CS_F_REPLY_DIR | \
+                          OVS_CS_F_INVALID | OVS_CS_F_TRACKED)
 #else
 #include <linux/errno.h>
 
@@ -53,11 +50,6 @@ static inline bool ovs_ct_verify(struct net *net, int attr)
        return false;
 }
 
-static inline bool ovs_ct_state_supported(u32 state)
-{
-       return false;
-}
-
 static inline int ovs_ct_copy_action(struct net *net, const struct nlattr *nla,
                                     const struct sw_flow_key *key,
                                     struct sw_flow_actions **acts, bool log)
@@ -75,6 +67,7 @@ static inline int ovs_ct_execute(struct net *net, struct sk_buff *skb,
                                 struct sw_flow_key *key,
                                 const struct ovs_conntrack_info *info)
 {
+       kfree_skb(skb);
        return -ENOTSUPP;
 }
 
@@ -94,5 +87,7 @@ static inline int ovs_ct_put_key(const struct sw_flow_key *key,
 }
 
 static inline void ovs_ct_free_action(const struct nlattr *a) { }
+
+#define CT_SUPPORTED_MASK 0
 #endif /* CONFIG_NF_CONNTRACK */
 #endif /* ovs_conntrack.h */
index b816ff87152834065841ff41e96d2f0d9ee4e307..5633172b791ab98e297ba1605c34bf42e684ccfb 100644 (file)
@@ -91,8 +91,7 @@ static bool ovs_must_notify(struct genl_family *family, struct genl_info *info,
 static void ovs_notify(struct genl_family *family,
                       struct sk_buff *skb, struct genl_info *info)
 {
-       genl_notify(family, skb, genl_info_net(info), info->snd_portid,
-                   0, info->nlhdr, GFP_KERNEL);
+       genl_notify(family, skb, info, 0, GFP_KERNEL);
 }
 
 /**
@@ -490,9 +489,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
 
        if (upcall_info->egress_tun_info) {
                nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_EGRESS_TUN_KEY);
-               err = ovs_nla_put_egress_tunnel_key(user_skb,
-                                                   upcall_info->egress_tun_info,
-                                                   upcall_info->egress_tun_opts);
+               err = ovs_nla_put_tunnel_info(user_skb,
+                                             upcall_info->egress_tun_info);
                BUG_ON(err);
                nla_nest_end(user_skb, nla);
        }
index f88038a99f4442bb753b4fc0cb3cc6c05c2bc2fc..67bdecd9fdc1f2b0544c2081aa1a557b16e0063a 100644 (file)
@@ -117,7 +117,6 @@ struct ovs_skb_cb {
  */
 struct dp_upcall_info {
        struct ip_tunnel_info *egress_tun_info;
-       const void *egress_tun_opts;
        const struct nlattr *userdata;
        const struct nlattr *actions;
        int actions_len;
index c8db44ab2ee780e135ec5585b11ba929d043d01f..0ea128eeeab2f835221b2068b1098a81fe1d731d 100644 (file)
@@ -698,8 +698,7 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
 {
        /* Extract metadata from packet. */
        if (tun_info) {
-               if (ip_tunnel_info_af(tun_info) != AF_INET)
-                       return -EINVAL;
+               key->tun_proto = ip_tunnel_info_af(tun_info);
                memcpy(&key->tun_key, &tun_info->key, sizeof(key->tun_key));
 
                if (tun_info->options_len) {
@@ -714,6 +713,7 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
                        key->tun_opts_len = 0;
                }
        } else  {
+               key->tun_proto = 0;
                key->tun_opts_len = 0;
                memset(&key->tun_key, 0, sizeof(key->tun_key));
        }
index 8cfa15a08668804a2a74c0047662694f23acbb80..1d055c559eafb118043ed21c66916d7313c41d8d 100644 (file)
@@ -63,6 +63,7 @@ struct sw_flow_key {
                u32     skb_mark;       /* SKB mark. */
                u16     in_port;        /* Input switch port (or DP_MAX_PORTS). */
        } __packed phy; /* Safe when right after 'tun_key'. */
+       u8 tun_proto;                   /* Protocol of encapsulating tunnel. */
        u32 ovs_flow_hash;              /* Datapath computed hash value.  */
        u32 recirc_id;                  /* Recirculation ID.  */
        struct {
index 171a691f1c3218ed1a63ac9151c95475ef7fcade..907d6fd28ede695cc1b876570c101883ed0b4b0e 100644 (file)
@@ -262,8 +262,8 @@ size_t ovs_tun_key_attr_size(void)
         * updating this function.
         */
        return    nla_total_size(8)    /* OVS_TUNNEL_KEY_ATTR_ID */
-               + nla_total_size(4)    /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */
-               + nla_total_size(4)    /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */
+               + nla_total_size(16)   /* OVS_TUNNEL_KEY_ATTR_IPV[46]_SRC */
+               + nla_total_size(16)   /* OVS_TUNNEL_KEY_ATTR_IPV[46]_DST */
                + nla_total_size(1)    /* OVS_TUNNEL_KEY_ATTR_TOS */
                + nla_total_size(1)    /* OVS_TUNNEL_KEY_ATTR_TTL */
                + nla_total_size(0)    /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
@@ -323,6 +323,8 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1]
        [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS]   = { .len = OVS_ATTR_VARIABLE },
        [OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS]    = { .len = OVS_ATTR_NESTED,
                                                .next = ovs_vxlan_ext_key_lens },
+       [OVS_TUNNEL_KEY_ATTR_IPV6_SRC]      = { .len = sizeof(struct in6_addr) },
+       [OVS_TUNNEL_KEY_ATTR_IPV6_DST]      = { .len = sizeof(struct in6_addr) },
 };
 
 /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute.  */
@@ -542,15 +544,15 @@ static int vxlan_tun_opt_from_nlattr(const struct nlattr *attr,
        return 0;
 }
 
-static int ipv4_tun_from_nlattr(const struct nlattr *attr,
-                               struct sw_flow_match *match, bool is_mask,
-                               bool log)
+static int ip_tun_from_nlattr(const struct nlattr *attr,
+                             struct sw_flow_match *match, bool is_mask,
+                             bool log)
 {
-       struct nlattr *a;
-       int rem;
-       bool ttl = false;
+       bool ttl = false, ipv4 = false, ipv6 = false;
        __be16 tun_flags = 0;
        int opts_type = 0;
+       struct nlattr *a;
+       int rem;
 
        nla_for_each_nested(a, attr, rem) {
                int type = nla_type(a);
@@ -578,10 +580,22 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
                case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
                        SW_FLOW_KEY_PUT(match, tun_key.u.ipv4.src,
                                        nla_get_in_addr(a), is_mask);
+                       ipv4 = true;
                        break;
                case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
                        SW_FLOW_KEY_PUT(match, tun_key.u.ipv4.dst,
                                        nla_get_in_addr(a), is_mask);
+                       ipv4 = true;
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_IPV6_SRC:
+                       SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.dst,
+                                       nla_get_in6_addr(a), is_mask);
+                       ipv6 = true;
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_IPV6_DST:
+                       SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.dst,
+                                       nla_get_in6_addr(a), is_mask);
+                       ipv6 = true;
                        break;
                case OVS_TUNNEL_KEY_ATTR_TOS:
                        SW_FLOW_KEY_PUT(match, tun_key.tos,
@@ -636,28 +650,46 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
                        opts_type = type;
                        break;
                default:
-                       OVS_NLERR(log, "Unknown IPv4 tunnel attribute %d",
+                       OVS_NLERR(log, "Unknown IP tunnel attribute %d",
                                  type);
                        return -EINVAL;
                }
        }
 
        SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask);
+       if (is_mask)
+               SW_FLOW_KEY_MEMSET_FIELD(match, tun_proto, 0xff, true);
+       else
+               SW_FLOW_KEY_PUT(match, tun_proto, ipv6 ? AF_INET6 : AF_INET,
+                               false);
 
        if (rem > 0) {
-               OVS_NLERR(log, "IPv4 tunnel attribute has %d unknown bytes.",
+               OVS_NLERR(log, "IP tunnel attribute has %d unknown bytes.",
                          rem);
                return -EINVAL;
        }
 
+       if (ipv4 && ipv6) {
+               OVS_NLERR(log, "Mixed IPv4 and IPv6 tunnel attributes");
+               return -EINVAL;
+       }
+
        if (!is_mask) {
-               if (!match->key->tun_key.u.ipv4.dst) {
+               if (!ipv4 && !ipv6) {
+                       OVS_NLERR(log, "IP tunnel dst address not specified");
+                       return -EINVAL;
+               }
+               if (ipv4 && !match->key->tun_key.u.ipv4.dst) {
                        OVS_NLERR(log, "IPv4 tunnel dst address is zero");
                        return -EINVAL;
                }
+               if (ipv6 && ipv6_addr_any(&match->key->tun_key.u.ipv6.dst)) {
+                       OVS_NLERR(log, "IPv6 tunnel dst address is zero");
+                       return -EINVAL;
+               }
 
                if (!ttl) {
-                       OVS_NLERR(log, "IPv4 tunnel TTL not specified.");
+                       OVS_NLERR(log, "IP tunnel TTL not specified.");
                        return -EINVAL;
                }
        }
@@ -682,21 +714,36 @@ static int vxlan_opt_to_nlattr(struct sk_buff *skb,
        return 0;
 }
 
-static int __ipv4_tun_to_nlattr(struct sk_buff *skb,
-                               const struct ip_tunnel_key *output,
-                               const void *tun_opts, int swkey_tun_opts_len)
+static int __ip_tun_to_nlattr(struct sk_buff *skb,
+                             const struct ip_tunnel_key *output,
+                             const void *tun_opts, int swkey_tun_opts_len,
+                             unsigned short tun_proto)
 {
        if (output->tun_flags & TUNNEL_KEY &&
            nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id))
                return -EMSGSIZE;
-       if (output->u.ipv4.src &&
-           nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC,
-                           output->u.ipv4.src))
-               return -EMSGSIZE;
-       if (output->u.ipv4.dst &&
-           nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST,
-                           output->u.ipv4.dst))
-               return -EMSGSIZE;
+       switch (tun_proto) {
+       case AF_INET:
+               if (output->u.ipv4.src &&
+                   nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC,
+                                   output->u.ipv4.src))
+                       return -EMSGSIZE;
+               if (output->u.ipv4.dst &&
+                   nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST,
+                                   output->u.ipv4.dst))
+                       return -EMSGSIZE;
+               break;
+       case AF_INET6:
+               if (!ipv6_addr_any(&output->u.ipv6.src) &&
+                   nla_put_in6_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV6_SRC,
+                                    &output->u.ipv6.src))
+                       return -EMSGSIZE;
+               if (!ipv6_addr_any(&output->u.ipv6.dst) &&
+                   nla_put_in6_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV6_DST,
+                                    &output->u.ipv6.dst))
+                       return -EMSGSIZE;
+               break;
+       }
        if (output->tos &&
            nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->tos))
                return -EMSGSIZE;
@@ -717,7 +764,7 @@ static int __ipv4_tun_to_nlattr(struct sk_buff *skb,
        if ((output->tun_flags & TUNNEL_OAM) &&
            nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_OAM))
                return -EMSGSIZE;
-       if (tun_opts) {
+       if (swkey_tun_opts_len) {
                if (output->tun_flags & TUNNEL_GENEVE_OPT &&
                    nla_put(skb, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS,
                            swkey_tun_opts_len, tun_opts))
@@ -730,9 +777,10 @@ static int __ipv4_tun_to_nlattr(struct sk_buff *skb,
        return 0;
 }
 
-static int ipv4_tun_to_nlattr(struct sk_buff *skb,
-                             const struct ip_tunnel_key *output,
-                             const void *tun_opts, int swkey_tun_opts_len)
+static int ip_tun_to_nlattr(struct sk_buff *skb,
+                           const struct ip_tunnel_key *output,
+                           const void *tun_opts, int swkey_tun_opts_len,
+                           unsigned short tun_proto)
 {
        struct nlattr *nla;
        int err;
@@ -741,7 +789,8 @@ static int ipv4_tun_to_nlattr(struct sk_buff *skb,
        if (!nla)
                return -EMSGSIZE;
 
-       err = __ipv4_tun_to_nlattr(skb, output, tun_opts, swkey_tun_opts_len);
+       err = __ip_tun_to_nlattr(skb, output, tun_opts, swkey_tun_opts_len,
+                                tun_proto);
        if (err)
                return err;
 
@@ -749,13 +798,13 @@ static int ipv4_tun_to_nlattr(struct sk_buff *skb,
        return 0;
 }
 
-int ovs_nla_put_egress_tunnel_key(struct sk_buff *skb,
-                                 const struct ip_tunnel_info *egress_tun_info,
-                                 const void *egress_tun_opts)
+int ovs_nla_put_tunnel_info(struct sk_buff *skb,
+                           struct ip_tunnel_info *tun_info)
 {
-       return __ipv4_tun_to_nlattr(skb, &egress_tun_info->key,
-                                   egress_tun_opts,
-                                   egress_tun_info->options_len);
+       return __ip_tun_to_nlattr(skb, &tun_info->key,
+                                 ip_tunnel_info_opts(tun_info),
+                                 tun_info->options_len,
+                                 ip_tunnel_info_af(tun_info));
 }
 
 static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
@@ -806,8 +855,8 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
                *attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK);
        }
        if (*attrs & (1 << OVS_KEY_ATTR_TUNNEL)) {
-               if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match,
-                                        is_mask, log) < 0)
+               if (ip_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match,
+                                      is_mask, log) < 0)
                        return -EINVAL;
                *attrs &= ~(1 << OVS_KEY_ATTR_TUNNEL);
        }
@@ -816,7 +865,7 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
            ovs_ct_verify(net, OVS_KEY_ATTR_CT_STATE)) {
                u32 ct_state = nla_get_u32(a[OVS_KEY_ATTR_CT_STATE]);
 
-               if (!is_mask && !ovs_ct_state_supported(ct_state)) {
+               if (ct_state & ~CT_SUPPORTED_MASK) {
                        OVS_NLERR(log, "ct_state flags %08x unsupported",
                                  ct_state);
                        return -EINVAL;
@@ -1099,6 +1148,9 @@ static void nlattr_set(struct nlattr *attr, u8 val,
                } else {
                        memset(nla_data(nla), val, nla_len(nla));
                }
+
+               if (nla_type(nla) == OVS_KEY_ATTR_CT_STATE)
+                       *(u32 *)nla_data(nla) &= CT_SUPPORTED_MASK;
        }
 }
 
@@ -1200,7 +1252,7 @@ int ovs_nla_get_match(struct net *net, struct sw_flow_match *match,
                        /* The userspace does not send tunnel attributes that
                         * are 0, but we should not wildcard them nonetheless.
                         */
-                       if (match->key->tun_key.u.ipv4.dst)
+                       if (match->key->tun_proto)
                                SW_FLOW_KEY_MEMSET_FIELD(match, tun_key,
                                                         0xff, true);
 
@@ -1373,14 +1425,14 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
        if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority))
                goto nla_put_failure;
 
-       if ((swkey->tun_key.u.ipv4.dst || is_mask)) {
+       if ((swkey->tun_proto || is_mask)) {
                const void *opts = NULL;
 
                if (output->tun_key.tun_flags & TUNNEL_OPTIONS_PRESENT)
                        opts = TUN_METADATA_OPTS(output, swkey->tun_opts_len);
 
-               if (ipv4_tun_to_nlattr(skb, &output->tun_key, opts,
-                                      swkey->tun_opts_len))
+               if (ip_tun_to_nlattr(skb, &output->tun_key, opts,
+                                    swkey->tun_opts_len, swkey->tun_proto))
                        goto nla_put_failure;
        }
 
@@ -1883,7 +1935,7 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
        int err = 0, start, opts_type;
 
        ovs_match_init(&match, &key, NULL);
-       opts_type = ipv4_tun_from_nlattr(nla_data(attr), &match, false, log);
+       opts_type = ip_tun_from_nlattr(nla_data(attr), &match, false, log);
        if (opts_type < 0)
                return opts_type;
 
@@ -1919,6 +1971,8 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
 
        tun_info = &tun_dst->u.tun_info;
        tun_info->mode = IP_TUNNEL_INFO_TX;
+       if (key.tun_proto == AF_INET6)
+               tun_info->mode |= IP_TUNNEL_INFO_IPV6;
        tun_info->key = key.tun_key;
 
        /* We need to store the options in the action itself since
@@ -2380,10 +2434,7 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
                if (!start)
                        return -EMSGSIZE;
 
-               err = ipv4_tun_to_nlattr(skb, &tun_info->key,
-                                        tun_info->options_len ?
-                                            ip_tunnel_info_opts(tun_info) : NULL,
-                                        tun_info->options_len);
+               err = ovs_nla_put_tunnel_info(skb, tun_info);
                if (err)
                        return err;
                nla_nest_end(skb, start);
index 6ca3f0baf449f05f82dd92f9796cd8cfa7abf141..47dd142eca1c0856c74406e1d75682fc822861f6 100644 (file)
@@ -55,9 +55,9 @@ int ovs_nla_put_mask(const struct sw_flow *flow, struct sk_buff *skb);
 int ovs_nla_get_match(struct net *, struct sw_flow_match *,
                      const struct nlattr *key, const struct nlattr *mask,
                      bool log);
-int ovs_nla_put_egress_tunnel_key(struct sk_buff *,
-                                 const struct ip_tunnel_info *,
-                                 const void *egress_tun_opts);
+
+int ovs_nla_put_tunnel_info(struct sk_buff *skb,
+                           struct ip_tunnel_info *tun_info);
 
 bool ovs_nla_get_ufid(struct sw_flow_id *, const struct nlattr *, bool log);
 int ovs_nla_get_identifier(struct sw_flow_id *sfid, const struct nlattr *ufid,
index c7f74aab34b9ef7d2befcd571d44290b76fbfbc1..d073fff82fdb8c6c8d39b57690d37eedeb12423a 100644 (file)
@@ -428,7 +428,7 @@ static u32 flow_hash(const struct sw_flow_key *key,
 
 static int flow_key_start(const struct sw_flow_key *key)
 {
-       if (key->tun_key.u.ipv4.dst)
+       if (key->tun_proto)
                return 0;
        else
                return rounddown(offsetof(struct sw_flow_key, phy),
index 2735e9c4a3b88586165ef5644e429cf28079974d..efb736bb685545a0cb6a323d3eca87fc54eeb9f4 100644 (file)
@@ -52,18 +52,6 @@ static int geneve_get_options(const struct vport *vport,
        return 0;
 }
 
-static int geneve_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-                                     struct dp_upcall_info *upcall)
-{
-       struct geneve_port *geneve_port = geneve_vport(vport);
-       struct net *net = ovs_dp_get_net(vport->dp);
-       __be16 dport = htons(geneve_port->port_no);
-       __be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true);
-
-       return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp),
-                                         skb, IPPROTO_UDP, sport, dport);
-}
-
 static struct vport *geneve_tnl_create(const struct vport_parms *parms)
 {
        struct net *net = ovs_dp_get_net(parms->dp);
@@ -128,9 +116,8 @@ static struct vport_ops ovs_geneve_vport_ops = {
        .create         = geneve_create,
        .destroy        = ovs_netdev_tunnel_destroy,
        .get_options    = geneve_get_options,
-       .send           = ovs_netdev_send,
+       .send           = dev_queue_xmit,
        .owner          = THIS_MODULE,
-       .get_egress_tun_info    = geneve_get_egress_tun_info,
 };
 
 static int __init ovs_geneve_tnl_init(void)
index 4d24481669c95197b06bb75d207b3e713b433508..c3257d78d3d28e6ed06e1be9c2e4a4f452c52886 100644 (file)
@@ -84,18 +84,10 @@ static struct vport *gre_create(const struct vport_parms *parms)
        return ovs_netdev_link(vport, parms->name);
 }
 
-static int gre_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-                                  struct dp_upcall_info *upcall)
-{
-       return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp),
-                                         skb, IPPROTO_GRE, 0, 0);
-}
-
 static struct vport_ops ovs_gre_vport_ops = {
        .type           = OVS_VPORT_TYPE_GRE,
        .create         = gre_create,
-       .send           = ovs_netdev_send,
-       .get_egress_tun_info    = gre_get_egress_tun_info,
+       .send           = dev_queue_xmit,
        .destroy        = ovs_netdev_tunnel_destroy,
        .owner          = THIS_MODULE,
 };
index 388b8a6bf112979f7f7291c5bb17fd6c7027e594..ec76398a792fbb7451c53b958304a2e001704604 100644 (file)
@@ -106,12 +106,45 @@ static void internal_dev_destructor(struct net_device *dev)
        free_netdev(dev);
 }
 
+static struct rtnl_link_stats64 *
+internal_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+       int i;
+
+       memset(stats, 0, sizeof(*stats));
+       stats->rx_errors  = dev->stats.rx_errors;
+       stats->tx_errors  = dev->stats.tx_errors;
+       stats->tx_dropped = dev->stats.tx_dropped;
+       stats->rx_dropped = dev->stats.rx_dropped;
+
+       for_each_possible_cpu(i) {
+               const struct pcpu_sw_netstats *percpu_stats;
+               struct pcpu_sw_netstats local_stats;
+               unsigned int start;
+
+               percpu_stats = per_cpu_ptr(dev->tstats, i);
+
+               do {
+                       start = u64_stats_fetch_begin_irq(&percpu_stats->syncp);
+                       local_stats = *percpu_stats;
+               } while (u64_stats_fetch_retry_irq(&percpu_stats->syncp, start));
+
+               stats->rx_bytes         += local_stats.rx_bytes;
+               stats->rx_packets       += local_stats.rx_packets;
+               stats->tx_bytes         += local_stats.tx_bytes;
+               stats->tx_packets       += local_stats.tx_packets;
+       }
+
+       return stats;
+}
+
 static const struct net_device_ops internal_dev_netdev_ops = {
        .ndo_open = internal_dev_open,
        .ndo_stop = internal_dev_stop,
        .ndo_start_xmit = internal_dev_xmit,
        .ndo_set_mac_address = eth_mac_addr,
        .ndo_change_mtu = internal_dev_change_mtu,
+       .ndo_get_stats64 = internal_get_stats,
 };
 
 static struct rtnl_link_ops internal_dev_link_ops __read_mostly = {
@@ -161,6 +194,11 @@ static struct vport *internal_dev_create(const struct vport_parms *parms)
                err = -ENOMEM;
                goto error_free_vport;
        }
+       vport->dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
+       if (!vport->dev->tstats) {
+               err = -ENOMEM;
+               goto error_free_netdev;
+       }
 
        dev_net_set(vport->dev, ovs_dp_get_net(vport->dp));
        internal_dev = internal_dev_priv(vport->dev);
@@ -173,7 +211,7 @@ static struct vport *internal_dev_create(const struct vport_parms *parms)
        rtnl_lock();
        err = register_netdevice(vport->dev);
        if (err)
-               goto error_free_netdev;
+               goto error_unlock;
 
        dev_set_promiscuity(vport->dev, 1);
        rtnl_unlock();
@@ -181,8 +219,10 @@ static struct vport *internal_dev_create(const struct vport_parms *parms)
 
        return vport;
 
-error_free_netdev:
+error_unlock:
        rtnl_unlock();
+       free_percpu(vport->dev->tstats);
+error_free_netdev:
        free_netdev(vport->dev);
 error_free_vport:
        ovs_vport_free(vport);
@@ -198,26 +238,25 @@ static void internal_dev_destroy(struct vport *vport)
 
        /* unregister_netdevice() waits for an RCU grace period. */
        unregister_netdevice(vport->dev);
-
+       free_percpu(vport->dev->tstats);
        rtnl_unlock();
 }
 
-static void internal_dev_recv(struct vport *vport, struct sk_buff *skb)
+static netdev_tx_t internal_dev_recv(struct sk_buff *skb)
 {
-       struct net_device *netdev = vport->dev;
+       struct net_device *netdev = skb->dev;
        struct pcpu_sw_netstats *stats;
 
        if (unlikely(!(netdev->flags & IFF_UP))) {
                kfree_skb(skb);
                netdev->stats.rx_dropped++;
-               return;
+               return NETDEV_TX_OK;
        }
 
        skb_dst_drop(skb);
        nf_reset(skb);
        secpath_reset(skb);
 
-       skb->dev = netdev;
        skb->pkt_type = PACKET_HOST;
        skb->protocol = eth_type_trans(skb, netdev);
        skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
@@ -229,6 +268,7 @@ static void internal_dev_recv(struct vport *vport, struct sk_buff *skb)
        u64_stats_update_end(&stats->syncp);
 
        netif_rx(skb);
+       return NETDEV_TX_OK;
 }
 
 static struct vport_ops ovs_internal_vport_ops = {
index f7e8dcce7adaec0e162db7f33f7b84e5e29d69cc..b327368a3848238013cf0f6f62445569d7e29251 100644 (file)
@@ -190,37 +190,6 @@ void ovs_netdev_tunnel_destroy(struct vport *vport)
 }
 EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy);
 
-static unsigned int packet_length(const struct sk_buff *skb)
-{
-       unsigned int length = skb->len - ETH_HLEN;
-
-       if (skb->protocol == htons(ETH_P_8021Q))
-               length -= VLAN_HLEN;
-
-       return length;
-}
-
-void ovs_netdev_send(struct vport *vport, struct sk_buff *skb)
-{
-       int mtu = vport->dev->mtu;
-
-       if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) {
-               net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",
-                                    vport->dev->name,
-                                    packet_length(skb), mtu);
-               vport->dev->stats.tx_errors++;
-               goto drop;
-       }
-
-       skb->dev = vport->dev;
-       dev_queue_xmit(skb);
-       return;
-
-drop:
-       kfree_skb(skb);
-}
-EXPORT_SYMBOL_GPL(ovs_netdev_send);
-
 /* Returns null if this device is not attached to a datapath. */
 struct vport *ovs_netdev_get_vport(struct net_device *dev)
 {
@@ -235,7 +204,7 @@ static struct vport_ops ovs_netdev_vport_ops = {
        .type           = OVS_VPORT_TYPE_NETDEV,
        .create         = netdev_create,
        .destroy        = netdev_destroy,
-       .send           = ovs_netdev_send,
+       .send           = dev_queue_xmit,
 };
 
 int __init ovs_netdev_init(void)
index bf22fcedbc69c3dd67312eef70251ef26c4d0925..19e29c12adcc562ed82b27563a46608ad1b95f33 100644 (file)
@@ -27,7 +27,6 @@
 struct vport *ovs_netdev_get_vport(struct net_device *dev);
 
 struct vport *ovs_netdev_link(struct vport *vport, const char *name);
-void ovs_netdev_send(struct vport *vport, struct sk_buff *skb);
 void ovs_netdev_detach_dev(struct vport *);
 
 int __init ovs_netdev_init(void);
index c11413d5075f882c2b360515285ba520a7303d0c..1605691d94144aee0fc50ffb17be05eca2b59675 100644 (file)
@@ -146,31 +146,12 @@ static struct vport *vxlan_create(const struct vport_parms *parms)
        return ovs_netdev_link(vport, parms->name);
 }
 
-static int vxlan_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-                                    struct dp_upcall_info *upcall)
-{
-       struct vxlan_dev *vxlan = netdev_priv(vport->dev);
-       struct net *net = ovs_dp_get_net(vport->dp);
-       __be16 dst_port = vxlan_dev_dst_port(vxlan);
-       __be16 src_port;
-       int port_min;
-       int port_max;
-
-       inet_get_local_port_range(net, &port_min, &port_max);
-       src_port = udp_flow_src_port(net, skb, 0, 0, true);
-
-       return ovs_tunnel_get_egress_info(upcall, net,
-                                         skb, IPPROTO_UDP,
-                                         src_port, dst_port);
-}
-
 static struct vport_ops ovs_vxlan_netdev_vport_ops = {
        .type                   = OVS_VPORT_TYPE_VXLAN,
        .create                 = vxlan_create,
        .destroy                = ovs_netdev_tunnel_destroy,
        .get_options            = vxlan_get_options,
-       .send                   = ovs_netdev_send,
-       .get_egress_tun_info    = vxlan_get_egress_tun_info,
+       .send                   = dev_queue_xmit,
 };
 
 static int __init ovs_vxlan_tnl_init(void)
index 12a36ac21edaaeddf50442408a1ea27aca975fa0..0ac0fd004d7ed885c009560d966da5b29b47f242 100644 (file)
@@ -480,60 +480,32 @@ void ovs_vport_deferred_free(struct vport *vport)
 }
 EXPORT_SYMBOL_GPL(ovs_vport_deferred_free);
 
-int ovs_tunnel_get_egress_info(struct dp_upcall_info *upcall,
-                              struct net *net,
-                              struct sk_buff *skb,
-                              u8 ipproto,
-                              __be16 tp_src,
-                              __be16 tp_dst)
+static unsigned int packet_length(const struct sk_buff *skb)
 {
-       struct ip_tunnel_info *egress_tun_info = upcall->egress_tun_info;
-       const struct ip_tunnel_info *tun_info = skb_tunnel_info(skb);
-       const struct ip_tunnel_key *tun_key;
-       u32 skb_mark = skb->mark;
-       struct rtable *rt;
-       struct flowi4 fl;
-
-       if (unlikely(!tun_info))
-               return -EINVAL;
-       if (ip_tunnel_info_af(tun_info) != AF_INET)
-               return -EINVAL;
-
-       tun_key = &tun_info->key;
-
-       /* Route lookup to get srouce IP address.
-        * The process may need to be changed if the corresponding process
-        * in vports ops changed.
-        */
-       rt = ovs_tunnel_route_lookup(net, tun_key, skb_mark, &fl, ipproto);
-       if (IS_ERR(rt))
-               return PTR_ERR(rt);
+       unsigned int length = skb->len - ETH_HLEN;
 
-       ip_rt_put(rt);
+       if (skb->protocol == htons(ETH_P_8021Q))
+               length -= VLAN_HLEN;
 
-       /* Generate egress_tun_info based on tun_info,
-        * saddr, tp_src and tp_dst
-        */
-       ip_tunnel_key_init(&egress_tun_info->key,
-                          fl.saddr, tun_key->u.ipv4.dst,
-                          tun_key->tos,
-                          tun_key->ttl,
-                          tp_src, tp_dst,
-                          tun_key->tun_id,
-                          tun_key->tun_flags);
-       egress_tun_info->options_len = tun_info->options_len;
-       egress_tun_info->mode = tun_info->mode;
-       upcall->egress_tun_opts = ip_tunnel_info_opts(egress_tun_info);
-       return 0;
+       return length;
 }
-EXPORT_SYMBOL_GPL(ovs_tunnel_get_egress_info);
 
-int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-                                 struct dp_upcall_info *upcall)
+void ovs_vport_send(struct vport *vport, struct sk_buff *skb)
 {
-       /* get_egress_tun_info() is only implemented on tunnel ports. */
-       if (unlikely(!vport->ops->get_egress_tun_info))
-               return -EINVAL;
+       int mtu = vport->dev->mtu;
+
+       if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) {
+               net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",
+                                    vport->dev->name,
+                                    packet_length(skb), mtu);
+               vport->dev->stats.tx_errors++;
+               goto drop;
+       }
+
+       skb->dev = vport->dev;
+       vport->ops->send(skb);
+       return;
 
-       return vport->ops->get_egress_tun_info(vport, skb, upcall);
+drop:
+       kfree_skb(skb);
 }
index a413f3ae6a7b540ed7b34fd4b31f69424caeb39f..bdfd82a7c064948dc1dc83acbc85b6534c1bcf9b 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
 #include <linux/u64_stats_sync.h>
-#include <net/route.h>
 
 #include "datapath.h"
 
@@ -53,16 +52,6 @@ int ovs_vport_set_upcall_portids(struct vport *, const struct nlattr *pids);
 int ovs_vport_get_upcall_portids(const struct vport *, struct sk_buff *);
 u32 ovs_vport_find_upcall_portid(const struct vport *, struct sk_buff *);
 
-int ovs_tunnel_get_egress_info(struct dp_upcall_info *upcall,
-                              struct net *net,
-                              struct sk_buff *,
-                              u8 ipproto,
-                              __be16 tp_src,
-                              __be16 tp_dst);
-
-int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-                                 struct dp_upcall_info *upcall);
-
 /**
  * struct vport_portids - array of netlink portids of a vport.
  *                        must be protected by rcu.
@@ -140,8 +129,6 @@ struct vport_parms {
  * have any configuration.
  * @send: Send a packet on the device.
  * zero for dropped packets or negative for error.
- * @get_egress_tun_info: Get the egress tunnel 5-tuple and other info for
- * a packet.
  */
 struct vport_ops {
        enum ovs_vport_type type;
@@ -153,10 +140,7 @@ struct vport_ops {
        int (*set_options)(struct vport *, struct nlattr *);
        int (*get_options)(const struct vport *, struct sk_buff *);
 
-       void (*send)(struct vport *, struct sk_buff *);
-       int (*get_egress_tun_info)(struct vport *, struct sk_buff *,
-                                  struct dp_upcall_info *upcall);
-
+       netdev_tx_t (*send) (struct sk_buff *skb);
        struct module *owner;
        struct list_head list;
 };
@@ -234,9 +218,6 @@ static inline struct rtable *ovs_tunnel_route_lookup(struct net *net,
        return rt;
 }
 
-static inline void ovs_vport_send(struct vport *vport, struct sk_buff *skb)
-{
-       vport->ops->send(vport, skb);
-}
+void ovs_vport_send(struct vport *vport, struct sk_buff *skb);
 
 #endif /* vport.h */
index aa4b15c3588445ae67568d35bddbeed6a3508cbd..691660b9b7effdc59d10517ebf099ca8736efe14 100644 (file)
@@ -1423,7 +1423,7 @@ static unsigned int fanout_demux_bpf(struct packet_fanout *f,
        rcu_read_lock();
        prog = rcu_dereference(f->bpf_prog);
        if (prog)
-               ret = BPF_PROG_RUN(prog, skb) % num;
+               ret = bpf_prog_run_clear_cb(prog, skb) % num;
        rcu_read_unlock();
 
        return ret;
@@ -1439,17 +1439,17 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev,
 {
        struct packet_fanout *f = pt->af_packet_priv;
        unsigned int num = READ_ONCE(f->num_members);
+       struct net *net = read_pnet(&f->net);
        struct packet_sock *po;
        unsigned int idx;
 
-       if (!net_eq(dev_net(dev), read_pnet(&f->net)) ||
-           !num) {
+       if (!net_eq(dev_net(dev), net) || !num) {
                kfree_skb(skb);
                return 0;
        }
 
        if (fanout_has_flag(f, PACKET_FANOUT_FLAG_DEFRAG)) {
-               skb = ip_check_defrag(skb, IP_DEFRAG_AF_PACKET);
+               skb = ip_check_defrag(net, skb, IP_DEFRAG_AF_PACKET);
                if (!skb)
                        return 0;
        }
@@ -1519,10 +1519,10 @@ static void __fanout_unlink(struct sock *sk, struct packet_sock *po)
 
 static bool match_fanout_group(struct packet_type *ptype, struct sock *sk)
 {
-       if (ptype->af_packet_priv == (void *)((struct packet_sock *)sk)->fanout)
-               return true;
+       if (sk->sk_family != PF_PACKET)
+               return false;
 
-       return false;
+       return ptype->af_packet_priv == pkt_sk(sk)->fanout;
 }
 
 static void fanout_init_data(struct packet_fanout *f)
@@ -1567,7 +1567,7 @@ static int fanout_set_data_cbpf(struct packet_sock *po, char __user *data,
        if (copy_from_user(&fprog, data, len))
                return -EFAULT;
 
-       ret = bpf_prog_create_from_user(&new, &fprog, NULL);
+       ret = bpf_prog_create_from_user(&new, &fprog, NULL, false);
        if (ret)
                return ret;
 
@@ -1939,16 +1939,16 @@ out_free:
        return err;
 }
 
-static unsigned int run_filter(const struct sk_buff *skb,
-                                     const struct sock *sk,
-                                     unsigned int res)
+static unsigned int run_filter(struct sk_buff *skb,
+                              const struct sock *sk,
+                              unsigned int res)
 {
        struct sk_filter *filter;
 
        rcu_read_lock();
        filter = rcu_dereference(sk->sk_filter);
        if (filter != NULL)
-               res = SK_RUN_FILTER(filter, skb);
+               res = bpf_prog_run_clear_cb(filter->prog, skb);
        rcu_read_unlock();
 
        return res;
@@ -2630,6 +2630,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
        __be16 proto;
        unsigned char *addr;
        int err, reserve = 0;
+       struct sockcm_cookie sockc;
        struct virtio_net_hdr vnet_hdr = { 0 };
        int offset = 0;
        int vnet_hdr_len;
@@ -2665,6 +2666,13 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
        if (unlikely(!(dev->flags & IFF_UP)))
                goto out_unlock;
 
+       sockc.mark = sk->sk_mark;
+       if (msg->msg_controllen) {
+               err = sock_cmsg_send(sk, msg, &sockc);
+               if (unlikely(err))
+                       goto out_unlock;
+       }
+
        if (sock->type == SOCK_RAW)
                reserve = dev->hard_header_len;
        if (po->has_vnet_hdr) {
@@ -2774,7 +2782,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
        skb->protocol = proto;
        skb->dev = dev;
        skb->priority = sk->sk_priority;
-       skb->mark = sk->sk_mark;
+       skb->mark = sockc.mark;
 
        packet_pick_tx_queue(dev, skb);
 
index a2f28a6d4dc579a6026e7506c80fd22d11e973a9..b5476aebd68d842e7e1b7ced45809a4c964e648e 100644 (file)
@@ -72,13 +72,7 @@ static int rds_release(struct socket *sock)
        rds_clear_recv_queue(rs);
        rds_cong_remove_socket(rs);
 
-       /*
-        * the binding lookup hash uses rcu, we need to
-        * make sure we synchronize_rcu before we free our
-        * entry
-        */
        rds_remove_bound(rs);
-       synchronize_rcu();
 
        rds_send_drop_to(rs, NULL);
        rds_rdma_drop_keys(rs);
@@ -579,6 +573,7 @@ static void rds_exit(void)
        rds_threads_exit();
        rds_stats_exit();
        rds_page_exit();
+       rds_bind_lock_destroy();
        rds_info_deregister_func(RDS_INFO_SOCKETS, rds_sock_info);
        rds_info_deregister_func(RDS_INFO_RECV_MESSAGES, rds_sock_inc_info);
 }
@@ -588,9 +583,14 @@ static int rds_init(void)
 {
        int ret;
 
-       ret = rds_conn_init();
+       ret = rds_bind_lock_init();
        if (ret)
                goto out;
+
+       ret = rds_conn_init();
+       if (ret)
+               goto out_bind;
+
        ret = rds_threads_init();
        if (ret)
                goto out_conn;
@@ -624,6 +624,8 @@ out_conn:
        rds_conn_exit();
        rds_cong_exit();
        rds_page_exit();
+out_bind:
+       rds_bind_lock_destroy();
 out:
        return ret;
 }
index dd666fb9b4e1761a50fcf1fdf91ef508b55c4aca..b22ea956522b420ca77e5c762f82957ee457f071 100644 (file)
 #include <linux/ratelimit.h>
 #include "rds.h"
 
-#define BIND_HASH_SIZE 1024
-static struct hlist_head bind_hash_table[BIND_HASH_SIZE];
-static DEFINE_SPINLOCK(rds_bind_lock);
+static struct rhashtable bind_hash_table;
 
-static struct hlist_head *hash_to_bucket(__be32 addr, __be16 port)
-{
-       return bind_hash_table + (jhash_2words((u32)addr, (u32)port, 0) &
-                                 (BIND_HASH_SIZE - 1));
-}
-
-static struct rds_sock *rds_bind_lookup(__be32 addr, __be16 port,
-                                       struct rds_sock *insert)
-{
-       struct rds_sock *rs;
-       struct hlist_head *head = hash_to_bucket(addr, port);
-       u64 cmp;
-       u64 needle = ((u64)be32_to_cpu(addr) << 32) | be16_to_cpu(port);
-
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(rs, head, rs_bound_node) {
-               cmp = ((u64)be32_to_cpu(rs->rs_bound_addr) << 32) |
-                     be16_to_cpu(rs->rs_bound_port);
-
-               if (cmp == needle) {
-                       rcu_read_unlock();
-                       return rs;
-               }
-       }
-       rcu_read_unlock();
-
-       if (insert) {
-               /*
-                * make sure our addr and port are set before
-                * we are added to the list, other people
-                * in rcu will find us as soon as the
-                * hlist_add_head_rcu is done
-                */
-               insert->rs_bound_addr = addr;
-               insert->rs_bound_port = port;
-               rds_sock_addref(insert);
-
-               hlist_add_head_rcu(&insert->rs_bound_node, head);
-       }
-       return NULL;
-}
+static struct rhashtable_params ht_parms = {
+       .nelem_hint = 768,
+       .key_len = sizeof(u64),
+       .key_offset = offsetof(struct rds_sock, rs_bound_key),
+       .head_offset = offsetof(struct rds_sock, rs_bound_node),
+       .max_size = 16384,
+       .min_size = 1024,
+};
 
 /*
  * Return the rds_sock bound at the given local address.
@@ -92,10 +57,10 @@ static struct rds_sock *rds_bind_lookup(__be32 addr, __be16 port,
  */
 struct rds_sock *rds_find_bound(__be32 addr, __be16 port)
 {
+       u64 key = ((u64)addr << 32) | port;
        struct rds_sock *rs;
 
-       rs = rds_bind_lookup(addr, port, NULL);
-
+       rs = rhashtable_lookup_fast(&bind_hash_table, &key, ht_parms);
        if (rs && !sock_flag(rds_rs_to_sk(rs), SOCK_DEAD))
                rds_sock_addref(rs);
        else
@@ -103,15 +68,16 @@ struct rds_sock *rds_find_bound(__be32 addr, __be16 port)
 
        rdsdebug("returning rs %p for %pI4:%u\n", rs, &addr,
                ntohs(port));
+
        return rs;
 }
 
 /* returns -ve errno or +ve port */
 static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port)
 {
-       unsigned long flags;
        int ret = -EADDRINUSE;
        u16 rover, last;
+       u64 key;
 
        if (*port != 0) {
                rover = be16_to_cpu(*port);
@@ -121,42 +87,49 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port)
                last = rover - 1;
        }
 
-       spin_lock_irqsave(&rds_bind_lock, flags);
-
        do {
                if (rover == 0)
                        rover++;
-               if (!rds_bind_lookup(addr, cpu_to_be16(rover), rs)) {
+
+               key = ((u64)addr << 32) | cpu_to_be16(rover);
+               if (rhashtable_lookup_fast(&bind_hash_table, &key, ht_parms))
+                       continue;
+
+               rs->rs_bound_key = key;
+               rs->rs_bound_addr = addr;
+               rs->rs_bound_port = cpu_to_be16(rover);
+               rs->rs_bound_node.next = NULL;
+               rds_sock_addref(rs);
+               if (!rhashtable_insert_fast(&bind_hash_table,
+                                           &rs->rs_bound_node, ht_parms)) {
                        *port = rs->rs_bound_port;
                        ret = 0;
                        rdsdebug("rs %p binding to %pI4:%d\n",
                          rs, &addr, (int)ntohs(*port));
                        break;
+               } else {
+                       rds_sock_put(rs);
+                       ret = -ENOMEM;
+                       break;
                }
        } while (rover++ != last);
 
-       spin_unlock_irqrestore(&rds_bind_lock, flags);
-
        return ret;
 }
 
 void rds_remove_bound(struct rds_sock *rs)
 {
-       unsigned long flags;
 
-       spin_lock_irqsave(&rds_bind_lock, flags);
+       if (!rs->rs_bound_addr)
+               return;
 
-       if (rs->rs_bound_addr) {
-               rdsdebug("rs %p unbinding from %pI4:%d\n",
-                 rs, &rs->rs_bound_addr,
-                 ntohs(rs->rs_bound_port));
-
-               hlist_del_init_rcu(&rs->rs_bound_node);
-               rds_sock_put(rs);
-               rs->rs_bound_addr = 0;
-       }
+       rdsdebug("rs %p unbinding from %pI4:%d\n",
+                rs, &rs->rs_bound_addr,
+                ntohs(rs->rs_bound_port));
 
-       spin_unlock_irqrestore(&rds_bind_lock, flags);
+       rhashtable_remove_fast(&bind_hash_table, &rs->rs_bound_node, ht_parms);
+       rds_sock_put(rs);
+       rs->rs_bound_addr = 0;
 }
 
 int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
@@ -182,7 +155,14 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                goto out;
 
        if (rs->rs_transport) { /* previously bound */
-               ret = 0;
+               trans = rs->rs_transport;
+               if (trans->laddr_check(sock_net(sock->sk),
+                                      sin->sin_addr.s_addr) != 0) {
+                       ret = -ENOPROTOOPT;
+                       rds_remove_bound(rs);
+               } else {
+                       ret = 0;
+               }
                goto out;
        }
        trans = rds_trans_get_preferred(sock_net(sock->sk),
@@ -200,9 +180,15 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 
 out:
        release_sock(sk);
-
-       /* we might have called rds_remove_bound on error */
-       if (ret)
-               synchronize_rcu();
        return ret;
 }
+
+void rds_bind_lock_destroy(void)
+{
+       rhashtable_destroy(&bind_hash_table);
+}
+
+int rds_bind_lock_init(void)
+{
+       return rhashtable_init(&bind_hash_table, &ht_parms);
+}
index 49adeef8090caea90be3e42702276314bca1d118..d4564036a339a04d31c65ec6c90cdc9e6a051efc 100644 (file)
@@ -128,10 +128,7 @@ static struct rds_connection *__rds_conn_create(struct net *net,
        struct rds_transport *loop_trans;
        unsigned long flags;
        int ret;
-       struct rds_transport *otrans = trans;
 
-       if (!is_outgoing && otrans->t_type == RDS_TRANS_TCP)
-               goto new_conn;
        rcu_read_lock();
        conn = rds_conn_lookup(net, head, laddr, faddr, trans);
        if (conn && conn->c_loopback && conn->c_trans != &rds_loop_transport &&
@@ -147,7 +144,6 @@ static struct rds_connection *__rds_conn_create(struct net *net,
        if (conn)
                goto out;
 
-new_conn:
        conn = kmem_cache_zalloc(rds_conn_slab, gfp);
        if (!conn) {
                conn = ERR_PTR(-ENOMEM);
@@ -207,6 +203,7 @@ new_conn:
 
        atomic_set(&conn->c_state, RDS_CONN_DOWN);
        conn->c_send_gen = 0;
+       conn->c_outgoing = (is_outgoing ? 1 : 0);
        conn->c_reconnect_jiffies = 0;
        INIT_DELAYED_WORK(&conn->c_send_w, rds_send_worker);
        INIT_DELAYED_WORK(&conn->c_recv_w, rds_recv_worker);
@@ -243,22 +240,13 @@ new_conn:
                /* Creating normal conn */
                struct rds_connection *found;
 
-               if (!is_outgoing && otrans->t_type == RDS_TRANS_TCP)
-                       found = NULL;
-               else
-                       found = rds_conn_lookup(net, head, laddr, faddr, trans);
+               found = rds_conn_lookup(net, head, laddr, faddr, trans);
                if (found) {
                        trans->conn_free(conn->c_transport_data);
                        kmem_cache_free(rds_conn_slab, conn);
                        conn = found;
                } else {
-                       if ((is_outgoing && otrans->t_type == RDS_TRANS_TCP) ||
-                           (otrans->t_type != RDS_TRANS_TCP)) {
-                               /* Only the active side should be added to
-                                * reconnect list for TCP.
-                                */
-                               hlist_add_head_rcu(&conn->c_hash_node, head);
-                       }
+                       hlist_add_head_rcu(&conn->c_hash_node, head);
                        rds_cong_add_conn(conn);
                        rds_conn_count++;
                }
@@ -337,7 +325,9 @@ void rds_conn_shutdown(struct rds_connection *conn)
        rcu_read_lock();
        if (!hlist_unhashed(&conn->c_hash_node)) {
                rcu_read_unlock();
-               rds_queue_reconnect(conn);
+               if (conn->c_trans->t_type != RDS_TRANS_TCP ||
+                   conn->c_outgoing == 1)
+                       rds_queue_reconnect(conn);
        } else {
                rcu_read_unlock();
        }
index 2d3f2ab475df8dc0b4329ca79ae59285f60df136..a833ab7898fe7306968e4ac20553afbee6955f8b 100644 (file)
 #include "rds.h"
 #include "ib.h"
 
-static unsigned int fmr_pool_size = RDS_FMR_POOL_SIZE;
-unsigned int fmr_message_size = RDS_FMR_SIZE + 1; /* +1 allows for unaligned MRs */
+unsigned int rds_ib_fmr_1m_pool_size = RDS_FMR_1M_POOL_SIZE;
+unsigned int rds_ib_fmr_8k_pool_size = RDS_FMR_8K_POOL_SIZE;
 unsigned int rds_ib_retry_count = RDS_IB_DEFAULT_RETRY_COUNT;
 
-module_param(fmr_pool_size, int, 0444);
-MODULE_PARM_DESC(fmr_pool_size, " Max number of fmr per HCA");
-module_param(fmr_message_size, int, 0444);
-MODULE_PARM_DESC(fmr_message_size, " Max size of a RDMA transfer");
+module_param(rds_ib_fmr_1m_pool_size, int, 0444);
+MODULE_PARM_DESC(rds_ib_fmr_1m_pool_size, " Max number of 1M fmr per HCA");
+module_param(rds_ib_fmr_8k_pool_size, int, 0444);
+MODULE_PARM_DESC(rds_ib_fmr_8k_pool_size, " Max number of 8K fmr per HCA");
 module_param(rds_ib_retry_count, int, 0444);
 MODULE_PARM_DESC(rds_ib_retry_count, " Number of hw retries before reporting an error");
 
@@ -97,8 +97,10 @@ static void rds_ib_dev_free(struct work_struct *work)
        struct rds_ib_device *rds_ibdev = container_of(work,
                                        struct rds_ib_device, free_work);
 
-       if (rds_ibdev->mr_pool)
-               rds_ib_destroy_mr_pool(rds_ibdev->mr_pool);
+       if (rds_ibdev->mr_8k_pool)
+               rds_ib_destroy_mr_pool(rds_ibdev->mr_8k_pool);
+       if (rds_ibdev->mr_1m_pool)
+               rds_ib_destroy_mr_pool(rds_ibdev->mr_1m_pool);
        if (rds_ibdev->pd)
                ib_dealloc_pd(rds_ibdev->pd);
 
@@ -148,9 +150,13 @@ static void rds_ib_add_one(struct ib_device *device)
        rds_ibdev->max_sge = min(dev_attr->max_sge, RDS_IB_MAX_SGE);
 
        rds_ibdev->fmr_max_remaps = dev_attr->max_map_per_fmr?: 32;
-       rds_ibdev->max_fmrs = dev_attr->max_fmr ?
-                       min_t(unsigned int, dev_attr->max_fmr, fmr_pool_size) :
-                       fmr_pool_size;
+       rds_ibdev->max_1m_fmrs = dev_attr->max_mr ?
+               min_t(unsigned int, (dev_attr->max_mr / 2),
+                     rds_ib_fmr_1m_pool_size) : rds_ib_fmr_1m_pool_size;
+
+       rds_ibdev->max_8k_fmrs = dev_attr->max_mr ?
+               min_t(unsigned int, ((dev_attr->max_mr / 2) * RDS_MR_8K_SCALE),
+                     rds_ib_fmr_8k_pool_size) : rds_ib_fmr_8k_pool_size;
 
        rds_ibdev->max_initiator_depth = dev_attr->max_qp_init_rd_atom;
        rds_ibdev->max_responder_resources = dev_attr->max_qp_rd_atom;
@@ -162,12 +168,25 @@ static void rds_ib_add_one(struct ib_device *device)
                goto put_dev;
        }
 
-       rds_ibdev->mr_pool = rds_ib_create_mr_pool(rds_ibdev);
-       if (IS_ERR(rds_ibdev->mr_pool)) {
-               rds_ibdev->mr_pool = NULL;
+       rds_ibdev->mr_1m_pool =
+               rds_ib_create_mr_pool(rds_ibdev, RDS_IB_MR_1M_POOL);
+       if (IS_ERR(rds_ibdev->mr_1m_pool)) {
+               rds_ibdev->mr_1m_pool = NULL;
                goto put_dev;
        }
 
+       rds_ibdev->mr_8k_pool =
+               rds_ib_create_mr_pool(rds_ibdev, RDS_IB_MR_8K_POOL);
+       if (IS_ERR(rds_ibdev->mr_8k_pool)) {
+               rds_ibdev->mr_8k_pool = NULL;
+               goto put_dev;
+       }
+
+       rdsdebug("RDS/IB: max_mr = %d, max_wrs = %d, max_sge = %d, fmr_max_remaps = %d, max_1m_fmrs = %d, max_8k_fmrs = %d\n",
+                dev_attr->max_fmr, rds_ibdev->max_wrs, rds_ibdev->max_sge,
+                rds_ibdev->fmr_max_remaps, rds_ibdev->max_1m_fmrs,
+                rds_ibdev->max_8k_fmrs);
+
        INIT_LIST_HEAD(&rds_ibdev->ipaddr_list);
        INIT_LIST_HEAD(&rds_ibdev->conn_list);
 
index aae60fda77f6d26d033a8c8b1fdb808eb6effaa0..f17d095678907b588c717b48e3d4602cab401199 100644 (file)
@@ -9,8 +9,11 @@
 #include "rds.h"
 #include "rdma_transport.h"
 
-#define RDS_FMR_SIZE                   256
-#define RDS_FMR_POOL_SIZE              8192
+#define RDS_FMR_1M_POOL_SIZE           (8192 / 2)
+#define RDS_FMR_1M_MSG_SIZE            256
+#define RDS_FMR_8K_MSG_SIZE            2
+#define RDS_MR_8K_SCALE                        (256 / (RDS_FMR_8K_MSG_SIZE + 1))
+#define RDS_FMR_8K_POOL_SIZE           (RDS_MR_8K_SCALE * (8192 / 2))
 
 #define RDS_IB_MAX_SGE                 8
 #define RDS_IB_RECV_SGE                2
@@ -24,6 +27,9 @@
 
 #define RDS_IB_RECYCLE_BATCH_COUNT     32
 
+#define RDS_IB_WC_MAX                  32
+#define RDS_IB_SEND_OP                 BIT_ULL(63)
+
 extern struct rw_semaphore rds_ib_devices_lock;
 extern struct list_head rds_ib_devices;
 
@@ -89,6 +95,20 @@ struct rds_ib_work_ring {
        atomic_t        w_free_ctr;
 };
 
+/* Rings are posted with all the allocations they'll need to queue the
+ * incoming message to the receiving socket so this can't fail.
+ * All fragments start with a header, so we can make sure we're not receiving
+ * garbage, and we can tell a small 8 byte fragment from an ACK frame.
+ */
+struct rds_ib_ack_state {
+       u64             ack_next;
+       u64             ack_recv;
+       unsigned int    ack_required:1;
+       unsigned int    ack_next_valid:1;
+       unsigned int    ack_recv_valid:1;
+};
+
+
 struct rds_ib_device;
 
 struct rds_ib_connection {
@@ -102,6 +122,12 @@ struct rds_ib_connection {
        struct ib_pd            *i_pd;
        struct ib_cq            *i_send_cq;
        struct ib_cq            *i_recv_cq;
+       struct ib_wc            i_send_wc[RDS_IB_WC_MAX];
+       struct ib_wc            i_recv_wc[RDS_IB_WC_MAX];
+
+       /* interrupt handling */
+       struct tasklet_struct   i_send_tasklet;
+       struct tasklet_struct   i_recv_tasklet;
 
        /* tx */
        struct rds_ib_work_ring i_send_ring;
@@ -112,7 +138,6 @@ struct rds_ib_connection {
        atomic_t                i_signaled_sends;
 
        /* rx */
-       struct tasklet_struct   i_recv_tasklet;
        struct mutex            i_recv_mutex;
        struct rds_ib_work_ring i_recv_ring;
        struct rds_ib_incoming  *i_ibinc;
@@ -164,6 +189,12 @@ struct rds_ib_connection {
 struct rds_ib_ipaddr {
        struct list_head        list;
        __be32                  ipaddr;
+       struct rcu_head         rcu;
+};
+
+enum {
+       RDS_IB_MR_8K_POOL,
+       RDS_IB_MR_1M_POOL,
 };
 
 struct rds_ib_device {
@@ -172,9 +203,12 @@ struct rds_ib_device {
        struct list_head        conn_list;
        struct ib_device        *dev;
        struct ib_pd            *pd;
-       struct rds_ib_mr_pool   *mr_pool;
-       unsigned int            fmr_max_remaps;
        unsigned int            max_fmrs;
+       struct rds_ib_mr_pool   *mr_1m_pool;
+       struct rds_ib_mr_pool   *mr_8k_pool;
+       unsigned int            fmr_max_remaps;
+       unsigned int            max_8k_fmrs;
+       unsigned int            max_1m_fmrs;
        int                     max_sge;
        unsigned int            max_wrs;
        unsigned int            max_initiator_depth;
@@ -197,14 +231,14 @@ struct rds_ib_device {
 struct rds_ib_statistics {
        uint64_t        s_ib_connect_raced;
        uint64_t        s_ib_listen_closed_stale;
-       uint64_t        s_ib_tx_cq_call;
+       uint64_t        s_ib_evt_handler_call;
+       uint64_t        s_ib_tasklet_call;
        uint64_t        s_ib_tx_cq_event;
        uint64_t        s_ib_tx_ring_full;
        uint64_t        s_ib_tx_throttle;
        uint64_t        s_ib_tx_sg_mapping_failure;
        uint64_t        s_ib_tx_stalled;
        uint64_t        s_ib_tx_credit_updates;
-       uint64_t        s_ib_rx_cq_call;
        uint64_t        s_ib_rx_cq_event;
        uint64_t        s_ib_rx_ring_empty;
        uint64_t        s_ib_rx_refill_from_cq;
@@ -216,12 +250,18 @@ struct rds_ib_statistics {
        uint64_t        s_ib_ack_send_delayed;
        uint64_t        s_ib_ack_send_piggybacked;
        uint64_t        s_ib_ack_received;
-       uint64_t        s_ib_rdma_mr_alloc;
-       uint64_t        s_ib_rdma_mr_free;
-       uint64_t        s_ib_rdma_mr_used;
-       uint64_t        s_ib_rdma_mr_pool_flush;
-       uint64_t        s_ib_rdma_mr_pool_wait;
-       uint64_t        s_ib_rdma_mr_pool_depleted;
+       uint64_t        s_ib_rdma_mr_8k_alloc;
+       uint64_t        s_ib_rdma_mr_8k_free;
+       uint64_t        s_ib_rdma_mr_8k_used;
+       uint64_t        s_ib_rdma_mr_8k_pool_flush;
+       uint64_t        s_ib_rdma_mr_8k_pool_wait;
+       uint64_t        s_ib_rdma_mr_8k_pool_depleted;
+       uint64_t        s_ib_rdma_mr_1m_alloc;
+       uint64_t        s_ib_rdma_mr_1m_free;
+       uint64_t        s_ib_rdma_mr_1m_used;
+       uint64_t        s_ib_rdma_mr_1m_pool_flush;
+       uint64_t        s_ib_rdma_mr_1m_pool_wait;
+       uint64_t        s_ib_rdma_mr_1m_pool_depleted;
        uint64_t        s_ib_atomic_cswp;
        uint64_t        s_ib_atomic_fadd;
 };
@@ -273,7 +313,8 @@ struct rds_ib_device *rds_ib_get_client_data(struct ib_device *device);
 void rds_ib_dev_put(struct rds_ib_device *rds_ibdev);
 extern struct ib_client rds_ib_client;
 
-extern unsigned int fmr_message_size;
+extern unsigned int rds_ib_fmr_1m_pool_size;
+extern unsigned int rds_ib_fmr_8k_pool_size;
 extern unsigned int rds_ib_retry_count;
 
 extern spinlock_t ib_nodev_conns_lock;
@@ -303,7 +344,8 @@ int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr);
 void rds_ib_add_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn);
 void rds_ib_remove_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn);
 void rds_ib_destroy_nodev_conns(void);
-struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *);
+struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_dev,
+                                            int npages);
 void rds_ib_get_mr_info(struct rds_ib_device *rds_ibdev, struct rds_info_rdma_connection *iinfo);
 void rds_ib_destroy_mr_pool(struct rds_ib_mr_pool *);
 void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents,
@@ -323,7 +365,8 @@ void rds_ib_recv_free_caches(struct rds_ib_connection *ic);
 void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp);
 void rds_ib_inc_free(struct rds_incoming *inc);
 int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to);
-void rds_ib_recv_cq_comp_handler(struct ib_cq *cq, void *context);
+void rds_ib_recv_cqe_handler(struct rds_ib_connection *ic, struct ib_wc *wc,
+                            struct rds_ib_ack_state *state);
 void rds_ib_recv_tasklet_fn(unsigned long data);
 void rds_ib_recv_init_ring(struct rds_ib_connection *ic);
 void rds_ib_recv_clear_ring(struct rds_ib_connection *ic);
@@ -331,6 +374,7 @@ void rds_ib_recv_init_ack(struct rds_ib_connection *ic);
 void rds_ib_attempt_ack(struct rds_ib_connection *ic);
 void rds_ib_ack_send_complete(struct rds_ib_connection *ic);
 u64 rds_ib_piggyb_ack(struct rds_ib_connection *ic);
+void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq, int ack_required);
 
 /* ib_ring.c */
 void rds_ib_ring_init(struct rds_ib_work_ring *ring, u32 nr);
@@ -348,7 +392,7 @@ extern wait_queue_head_t rds_ib_ring_empty_wait;
 void rds_ib_xmit_complete(struct rds_connection *conn);
 int rds_ib_xmit(struct rds_connection *conn, struct rds_message *rm,
                unsigned int hdr_off, unsigned int sg, unsigned int off);
-void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context);
+void rds_ib_send_cqe_handler(struct rds_ib_connection *ic, struct ib_wc *wc);
 void rds_ib_send_init_ring(struct rds_ib_connection *ic);
 void rds_ib_send_clear_ring(struct rds_ib_connection *ic);
 int rds_ib_xmit_rdma(struct rds_connection *conn, struct rm_rdma_op *op);
index 9043f5c04787216e3447813c026c284e30da2b5b..2b2370e7f356f5db4d23df09356796d0649ee063 100644 (file)
@@ -216,6 +216,96 @@ static void rds_ib_cq_event_handler(struct ib_event *event, void *data)
                 event->event, ib_event_msg(event->event), data);
 }
 
+/* Plucking the oldest entry from the ring can be done concurrently with
+ * the thread refilling the ring.  Each ring operation is protected by
+ * spinlocks and the transient state of refilling doesn't change the
+ * recording of which entry is oldest.
+ *
+ * This relies on IB only calling one cq comp_handler for each cq so that
+ * there will only be one caller of rds_recv_incoming() per RDS connection.
+ */
+static void rds_ib_cq_comp_handler_recv(struct ib_cq *cq, void *context)
+{
+       struct rds_connection *conn = context;
+       struct rds_ib_connection *ic = conn->c_transport_data;
+
+       rdsdebug("conn %p cq %p\n", conn, cq);
+
+       rds_ib_stats_inc(s_ib_evt_handler_call);
+
+       tasklet_schedule(&ic->i_recv_tasklet);
+}
+
+static void poll_cq(struct rds_ib_connection *ic, struct ib_cq *cq,
+                   struct ib_wc *wcs,
+                   struct rds_ib_ack_state *ack_state)
+{
+       int nr;
+       int i;
+       struct ib_wc *wc;
+
+       while ((nr = ib_poll_cq(cq, RDS_IB_WC_MAX, wcs)) > 0) {
+               for (i = 0; i < nr; i++) {
+                       wc = wcs + i;
+                       rdsdebug("wc wr_id 0x%llx status %u byte_len %u imm_data %u\n",
+                                (unsigned long long)wc->wr_id, wc->status,
+                                wc->byte_len, be32_to_cpu(wc->ex.imm_data));
+
+                       if (wc->wr_id & RDS_IB_SEND_OP)
+                               rds_ib_send_cqe_handler(ic, wc);
+                       else
+                               rds_ib_recv_cqe_handler(ic, wc, ack_state);
+               }
+       }
+}
+
+static void rds_ib_tasklet_fn_send(unsigned long data)
+{
+       struct rds_ib_connection *ic = (struct rds_ib_connection *)data;
+       struct rds_connection *conn = ic->conn;
+       struct rds_ib_ack_state state;
+
+       rds_ib_stats_inc(s_ib_tasklet_call);
+
+       memset(&state, 0, sizeof(state));
+       poll_cq(ic, ic->i_send_cq, ic->i_send_wc, &state);
+       ib_req_notify_cq(ic->i_send_cq, IB_CQ_NEXT_COMP);
+       poll_cq(ic, ic->i_send_cq, ic->i_send_wc, &state);
+
+       if (rds_conn_up(conn) &&
+           (!test_bit(RDS_LL_SEND_FULL, &conn->c_flags) ||
+           test_bit(0, &conn->c_map_queued)))
+               rds_send_xmit(ic->conn);
+}
+
+static void rds_ib_tasklet_fn_recv(unsigned long data)
+{
+       struct rds_ib_connection *ic = (struct rds_ib_connection *)data;
+       struct rds_connection *conn = ic->conn;
+       struct rds_ib_device *rds_ibdev = ic->rds_ibdev;
+       struct rds_ib_ack_state state;
+
+       if (!rds_ibdev)
+               rds_conn_drop(conn);
+
+       rds_ib_stats_inc(s_ib_tasklet_call);
+
+       memset(&state, 0, sizeof(state));
+       poll_cq(ic, ic->i_recv_cq, ic->i_recv_wc, &state);
+       ib_req_notify_cq(ic->i_recv_cq, IB_CQ_SOLICITED);
+       poll_cq(ic, ic->i_recv_cq, ic->i_recv_wc, &state);
+
+       if (state.ack_next_valid)
+               rds_ib_set_ack(ic, state.ack_next, state.ack_required);
+       if (state.ack_recv_valid && state.ack_recv > ic->i_ack_recv) {
+               rds_send_drop_acked(conn, state.ack_recv, NULL);
+               ic->i_ack_recv = state.ack_recv;
+       }
+
+       if (rds_conn_up(conn))
+               rds_ib_attempt_ack(ic);
+}
+
 static void rds_ib_qp_event_handler(struct ib_event *event, void *data)
 {
        struct rds_connection *conn = data;
@@ -238,6 +328,18 @@ static void rds_ib_qp_event_handler(struct ib_event *event, void *data)
        }
 }
 
+static void rds_ib_cq_comp_handler_send(struct ib_cq *cq, void *context)
+{
+       struct rds_connection *conn = context;
+       struct rds_ib_connection *ic = conn->c_transport_data;
+
+       rdsdebug("conn %p cq %p\n", conn, cq);
+
+       rds_ib_stats_inc(s_ib_evt_handler_call);
+
+       tasklet_schedule(&ic->i_send_tasklet);
+}
+
 /*
  * This needs to be very careful to not leave IS_ERR pointers around for
  * cleanup to trip over.
@@ -271,7 +373,8 @@ static int rds_ib_setup_qp(struct rds_connection *conn)
        ic->i_pd = rds_ibdev->pd;
 
        cq_attr.cqe = ic->i_send_ring.w_nr + 1;
-       ic->i_send_cq = ib_create_cq(dev, rds_ib_send_cq_comp_handler,
+
+       ic->i_send_cq = ib_create_cq(dev, rds_ib_cq_comp_handler_send,
                                     rds_ib_cq_event_handler, conn,
                                     &cq_attr);
        if (IS_ERR(ic->i_send_cq)) {
@@ -282,7 +385,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn)
        }
 
        cq_attr.cqe = ic->i_recv_ring.w_nr;
-       ic->i_recv_cq = ib_create_cq(dev, rds_ib_recv_cq_comp_handler,
+       ic->i_recv_cq = ib_create_cq(dev, rds_ib_cq_comp_handler_recv,
                                     rds_ib_cq_event_handler, conn,
                                     &cq_attr);
        if (IS_ERR(ic->i_recv_cq)) {
@@ -637,6 +740,7 @@ void rds_ib_conn_shutdown(struct rds_connection *conn)
                wait_event(rds_ib_ring_empty_wait,
                           rds_ib_ring_empty(&ic->i_recv_ring) &&
                           (atomic_read(&ic->i_signaled_sends) == 0));
+               tasklet_kill(&ic->i_send_tasklet);
                tasklet_kill(&ic->i_recv_tasklet);
 
                /* first destroy the ib state that generates callbacks */
@@ -743,8 +847,10 @@ int rds_ib_conn_alloc(struct rds_connection *conn, gfp_t gfp)
        }
 
        INIT_LIST_HEAD(&ic->ib_node);
-       tasklet_init(&ic->i_recv_tasklet, rds_ib_recv_tasklet_fn,
-                    (unsigned long) ic);
+       tasklet_init(&ic->i_send_tasklet, rds_ib_tasklet_fn_send,
+                    (unsigned long)ic);
+       tasklet_init(&ic->i_recv_tasklet, rds_ib_tasklet_fn_recv,
+                    (unsigned long)ic);
        mutex_init(&ic->i_recv_mutex);
 #ifndef KERNEL_HAS_ATOMIC64
        spin_lock_init(&ic->i_ack_lock);
index 251d1ce0b7c7a0da22fc445e778b2078559168f4..a2340748ec8673d478df5961329734a07d0b9ca6 100644 (file)
@@ -65,6 +65,7 @@ struct rds_ib_mr {
  * Our own little FMR pool
  */
 struct rds_ib_mr_pool {
+       unsigned int            pool_type;
        struct mutex            flush_lock;             /* serialize fmr invalidate */
        struct delayed_work     flush_worker;           /* flush worker */
 
@@ -83,7 +84,7 @@ struct rds_ib_mr_pool {
        struct ib_fmr_attr      fmr_attr;
 };
 
-struct workqueue_struct *rds_ib_fmr_wq;
+static struct workqueue_struct *rds_ib_fmr_wq;
 
 int rds_ib_fmr_init(void)
 {
@@ -159,10 +160,8 @@ static void rds_ib_remove_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr)
        }
        spin_unlock_irq(&rds_ibdev->spinlock);
 
-       if (to_free) {
-               synchronize_rcu();
-               kfree(to_free);
-       }
+       if (to_free)
+               kfree_rcu(to_free, rcu);
 }
 
 int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr)
@@ -236,7 +235,8 @@ void rds_ib_destroy_nodev_conns(void)
                rds_conn_destroy(ic->conn);
 }
 
-struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_ibdev)
+struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_ibdev,
+                                            int pool_type)
 {
        struct rds_ib_mr_pool *pool;
 
@@ -244,6 +244,7 @@ struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_ibdev)
        if (!pool)
                return ERR_PTR(-ENOMEM);
 
+       pool->pool_type = pool_type;
        init_llist_head(&pool->free_list);
        init_llist_head(&pool->drop_list);
        init_llist_head(&pool->clean_list);
@@ -251,28 +252,30 @@ struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_ibdev)
        init_waitqueue_head(&pool->flush_wait);
        INIT_DELAYED_WORK(&pool->flush_worker, rds_ib_mr_pool_flush_worker);
 
-       pool->fmr_attr.max_pages = fmr_message_size;
+       if (pool_type == RDS_IB_MR_1M_POOL) {
+               /* +1 allows for unaligned MRs */
+               pool->fmr_attr.max_pages = RDS_FMR_1M_MSG_SIZE + 1;
+               pool->max_items = RDS_FMR_1M_POOL_SIZE;
+       } else {
+               /* pool_type == RDS_IB_MR_8K_POOL */
+               pool->fmr_attr.max_pages = RDS_FMR_8K_MSG_SIZE + 1;
+               pool->max_items = RDS_FMR_8K_POOL_SIZE;
+       }
+
+       pool->max_free_pinned = pool->max_items * pool->fmr_attr.max_pages / 4;
        pool->fmr_attr.max_maps = rds_ibdev->fmr_max_remaps;
        pool->fmr_attr.page_shift = PAGE_SHIFT;
-       pool->max_free_pinned = rds_ibdev->max_fmrs * fmr_message_size / 4;
-
-       /* We never allow more than max_items MRs to be allocated.
-        * When we exceed more than max_items_soft, we start freeing
-        * items more aggressively.
-        * Make sure that max_items > max_items_soft > max_items / 2
-        */
        pool->max_items_soft = rds_ibdev->max_fmrs * 3 / 4;
-       pool->max_items = rds_ibdev->max_fmrs;
 
        return pool;
 }
 
 void rds_ib_get_mr_info(struct rds_ib_device *rds_ibdev, struct rds_info_rdma_connection *iinfo)
 {
-       struct rds_ib_mr_pool *pool = rds_ibdev->mr_pool;
+       struct rds_ib_mr_pool *pool_1m = rds_ibdev->mr_1m_pool;
 
-       iinfo->rdma_mr_max = pool->max_items;
-       iinfo->rdma_mr_size = pool->fmr_attr.max_pages;
+       iinfo->rdma_mr_max = pool_1m->max_items;
+       iinfo->rdma_mr_size = pool_1m->fmr_attr.max_pages;
 }
 
 void rds_ib_destroy_mr_pool(struct rds_ib_mr_pool *pool)
@@ -314,14 +317,28 @@ static inline void wait_clean_list_grace(void)
        }
 }
 
-static struct rds_ib_mr *rds_ib_alloc_fmr(struct rds_ib_device *rds_ibdev)
+static struct rds_ib_mr *rds_ib_alloc_fmr(struct rds_ib_device *rds_ibdev,
+                                         int npages)
 {
-       struct rds_ib_mr_pool *pool = rds_ibdev->mr_pool;
+       struct rds_ib_mr_pool *pool;
        struct rds_ib_mr *ibmr = NULL;
        int err = 0, iter = 0;
 
+       if (npages <= RDS_FMR_8K_MSG_SIZE)
+               pool = rds_ibdev->mr_8k_pool;
+       else
+               pool = rds_ibdev->mr_1m_pool;
+
        if (atomic_read(&pool->dirty_count) >= pool->max_items / 10)
-               schedule_delayed_work(&pool->flush_worker, 10);
+               queue_delayed_work(rds_ib_fmr_wq, &pool->flush_worker, 10);
+
+       /* Switch pools if one of the pool is reaching upper limit */
+       if (atomic_read(&pool->dirty_count) >=  pool->max_items * 9 / 10) {
+               if (pool->pool_type == RDS_IB_MR_8K_POOL)
+                       pool = rds_ibdev->mr_1m_pool;
+               else
+                       pool = rds_ibdev->mr_8k_pool;
+       }
 
        while (1) {
                ibmr = rds_ib_reuse_fmr(pool);
@@ -343,12 +360,18 @@ static struct rds_ib_mr *rds_ib_alloc_fmr(struct rds_ib_device *rds_ibdev)
                atomic_dec(&pool->item_count);
 
                if (++iter > 2) {
-                       rds_ib_stats_inc(s_ib_rdma_mr_pool_depleted);
+                       if (pool->pool_type == RDS_IB_MR_8K_POOL)
+                               rds_ib_stats_inc(s_ib_rdma_mr_8k_pool_depleted);
+                       else
+                               rds_ib_stats_inc(s_ib_rdma_mr_1m_pool_depleted);
                        return ERR_PTR(-EAGAIN);
                }
 
                /* We do have some empty MRs. Flush them out. */
-               rds_ib_stats_inc(s_ib_rdma_mr_pool_wait);
+               if (pool->pool_type == RDS_IB_MR_8K_POOL)
+                       rds_ib_stats_inc(s_ib_rdma_mr_8k_pool_wait);
+               else
+                       rds_ib_stats_inc(s_ib_rdma_mr_1m_pool_wait);
                rds_ib_flush_mr_pool(pool, 0, &ibmr);
                if (ibmr)
                        return ibmr;
@@ -373,7 +396,12 @@ static struct rds_ib_mr *rds_ib_alloc_fmr(struct rds_ib_device *rds_ibdev)
                goto out_no_cigar;
        }
 
-       rds_ib_stats_inc(s_ib_rdma_mr_alloc);
+       ibmr->pool = pool;
+       if (pool->pool_type == RDS_IB_MR_8K_POOL)
+               rds_ib_stats_inc(s_ib_rdma_mr_8k_alloc);
+       else
+               rds_ib_stats_inc(s_ib_rdma_mr_1m_alloc);
+
        return ibmr;
 
 out_no_cigar:
@@ -429,7 +457,7 @@ static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, struct rds_ib_mr *ibm
        }
 
        page_cnt += len >> PAGE_SHIFT;
-       if (page_cnt > fmr_message_size)
+       if (page_cnt > ibmr->pool->fmr_attr.max_pages)
                return -EINVAL;
 
        dma_pages = kmalloc_node(sizeof(u64) * page_cnt, GFP_ATOMIC,
@@ -461,7 +489,10 @@ static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, struct rds_ib_mr *ibm
        ibmr->sg_dma_len = sg_dma_len;
        ibmr->remap_count++;
 
-       rds_ib_stats_inc(s_ib_rdma_mr_used);
+       if (ibmr->pool->pool_type == RDS_IB_MR_8K_POOL)
+               rds_ib_stats_inc(s_ib_rdma_mr_8k_used);
+       else
+               rds_ib_stats_inc(s_ib_rdma_mr_1m_used);
        ret = 0;
 
 out:
@@ -524,8 +555,7 @@ static void rds_ib_teardown_mr(struct rds_ib_mr *ibmr)
 
        __rds_ib_teardown_mr(ibmr);
        if (pinned) {
-               struct rds_ib_device *rds_ibdev = ibmr->device;
-               struct rds_ib_mr_pool *pool = rds_ibdev->mr_pool;
+               struct rds_ib_mr_pool *pool = ibmr->pool;
 
                atomic_sub(pinned, &pool->free_pinned);
        }
@@ -594,7 +624,7 @@ static void list_to_llist_nodes(struct rds_ib_mr_pool *pool,
  * to free as many MRs as needed to get back to this limit.
  */
 static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool,
-                               int free_all, struct rds_ib_mr **ibmr_ret)
+                               int free_all, struct rds_ib_mr **ibmr_ret)
 {
        struct rds_ib_mr *ibmr, *next;
        struct llist_node *clean_nodes;
@@ -605,11 +635,14 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool,
        unsigned int nfreed = 0, dirty_to_clean = 0, free_goal;
        int ret = 0;
 
-       rds_ib_stats_inc(s_ib_rdma_mr_pool_flush);
+       if (pool->pool_type == RDS_IB_MR_8K_POOL)
+               rds_ib_stats_inc(s_ib_rdma_mr_8k_pool_flush);
+       else
+               rds_ib_stats_inc(s_ib_rdma_mr_1m_pool_flush);
 
        if (ibmr_ret) {
                DEFINE_WAIT(wait);
-               while(!mutex_trylock(&pool->flush_lock)) {
+               while (!mutex_trylock(&pool->flush_lock)) {
                        ibmr = rds_ib_reuse_fmr(pool);
                        if (ibmr) {
                                *ibmr_ret = ibmr;
@@ -666,8 +699,12 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool,
        list_for_each_entry_safe(ibmr, next, &unmap_list, unmap_list) {
                unpinned += ibmr->sg_len;
                __rds_ib_teardown_mr(ibmr);
-               if (nfreed < free_goal || ibmr->remap_count >= pool->fmr_attr.max_maps) {
-                       rds_ib_stats_inc(s_ib_rdma_mr_free);
+               if (nfreed < free_goal ||
+                   ibmr->remap_count >= pool->fmr_attr.max_maps) {
+                       if (ibmr->pool->pool_type == RDS_IB_MR_8K_POOL)
+                               rds_ib_stats_inc(s_ib_rdma_mr_8k_free);
+                       else
+                               rds_ib_stats_inc(s_ib_rdma_mr_1m_free);
                        list_del(&ibmr->unmap_list);
                        ib_dealloc_fmr(ibmr->fmr);
                        kfree(ibmr);
@@ -719,8 +756,8 @@ static void rds_ib_mr_pool_flush_worker(struct work_struct *work)
 void rds_ib_free_mr(void *trans_private, int invalidate)
 {
        struct rds_ib_mr *ibmr = trans_private;
+       struct rds_ib_mr_pool *pool = ibmr->pool;
        struct rds_ib_device *rds_ibdev = ibmr->device;
-       struct rds_ib_mr_pool *pool = rds_ibdev->mr_pool;
 
        rdsdebug("RDS/IB: free_mr nents %u\n", ibmr->sg_len);
 
@@ -759,10 +796,11 @@ void rds_ib_flush_mrs(void)
 
        down_read(&rds_ib_devices_lock);
        list_for_each_entry(rds_ibdev, &rds_ib_devices, list) {
-               struct rds_ib_mr_pool *pool = rds_ibdev->mr_pool;
+               if (rds_ibdev->mr_8k_pool)
+                       rds_ib_flush_mr_pool(rds_ibdev->mr_8k_pool, 0, NULL);
 
-               if (pool)
-                       rds_ib_flush_mr_pool(pool, 0, NULL);
+               if (rds_ibdev->mr_1m_pool)
+                       rds_ib_flush_mr_pool(rds_ibdev->mr_1m_pool, 0, NULL);
        }
        up_read(&rds_ib_devices_lock);
 }
@@ -780,12 +818,12 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents,
                goto out;
        }
 
-       if (!rds_ibdev->mr_pool) {
+       if (!rds_ibdev->mr_8k_pool || !rds_ibdev->mr_1m_pool) {
                ret = -ENODEV;
                goto out;
        }
 
-       ibmr = rds_ib_alloc_fmr(rds_ibdev);
+       ibmr = rds_ib_alloc_fmr(rds_ibdev, nents);
        if (IS_ERR(ibmr)) {
                rds_ib_dev_put(rds_ibdev);
                return ibmr;
index f43831e4186a3543af1b8cd8beba803c66a233d3..96744b75db9387aa2ef3b28d8ea103d81997a9ab 100644 (file)
@@ -596,8 +596,7 @@ void rds_ib_recv_init_ack(struct rds_ib_connection *ic)
  * wr_id and avoids working with the ring in that case.
  */
 #ifndef KERNEL_HAS_ATOMIC64
-static void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq,
-                               int ack_required)
+void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq, int ack_required)
 {
        unsigned long flags;
 
@@ -622,8 +621,7 @@ static u64 rds_ib_get_ack(struct rds_ib_connection *ic)
        return seq;
 }
 #else
-static void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq,
-                               int ack_required)
+void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq, int ack_required)
 {
        atomic64_set(&ic->i_ack_next, seq);
        if (ack_required) {
@@ -830,20 +828,6 @@ static void rds_ib_cong_recv(struct rds_connection *conn,
        rds_cong_map_updated(map, uncongested);
 }
 
-/*
- * Rings are posted with all the allocations they'll need to queue the
- * incoming message to the receiving socket so this can't fail.
- * All fragments start with a header, so we can make sure we're not receiving
- * garbage, and we can tell a small 8 byte fragment from an ACK frame.
- */
-struct rds_ib_ack_state {
-       u64             ack_next;
-       u64             ack_recv;
-       unsigned int    ack_required:1;
-       unsigned int    ack_next_valid:1;
-       unsigned int    ack_recv_valid:1;
-};
-
 static void rds_ib_process_recv(struct rds_connection *conn,
                                struct rds_ib_recv_work *recv, u32 data_len,
                                struct rds_ib_ack_state *state)
@@ -969,96 +953,50 @@ static void rds_ib_process_recv(struct rds_connection *conn,
        }
 }
 
-/*
- * Plucking the oldest entry from the ring can be done concurrently with
- * the thread refilling the ring.  Each ring operation is protected by
- * spinlocks and the transient state of refilling doesn't change the
- * recording of which entry is oldest.
- *
- * This relies on IB only calling one cq comp_handler for each cq so that
- * there will only be one caller of rds_recv_incoming() per RDS connection.
- */
-void rds_ib_recv_cq_comp_handler(struct ib_cq *cq, void *context)
-{
-       struct rds_connection *conn = context;
-       struct rds_ib_connection *ic = conn->c_transport_data;
-
-       rdsdebug("conn %p cq %p\n", conn, cq);
-
-       rds_ib_stats_inc(s_ib_rx_cq_call);
-
-       tasklet_schedule(&ic->i_recv_tasklet);
-}
-
-static inline void rds_poll_cq(struct rds_ib_connection *ic,
-                              struct rds_ib_ack_state *state)
+void rds_ib_recv_cqe_handler(struct rds_ib_connection *ic,
+                            struct ib_wc *wc,
+                            struct rds_ib_ack_state *state)
 {
        struct rds_connection *conn = ic->conn;
-       struct ib_wc wc;
        struct rds_ib_recv_work *recv;
 
-       while (ib_poll_cq(ic->i_recv_cq, 1, &wc) > 0) {
-               rdsdebug("wc wr_id 0x%llx status %u (%s) byte_len %u imm_data %u\n",
-                        (unsigned long long)wc.wr_id, wc.status,
-                        ib_wc_status_msg(wc.status), wc.byte_len,
-                        be32_to_cpu(wc.ex.imm_data));
-               rds_ib_stats_inc(s_ib_rx_cq_event);
+       rdsdebug("wc wr_id 0x%llx status %u (%s) byte_len %u imm_data %u\n",
+                (unsigned long long)wc->wr_id, wc->status,
+                ib_wc_status_msg(wc->status), wc->byte_len,
+                be32_to_cpu(wc->ex.imm_data));
 
-               recv = &ic->i_recvs[rds_ib_ring_oldest(&ic->i_recv_ring)];
-
-               ib_dma_unmap_sg(ic->i_cm_id->device, &recv->r_frag->f_sg, 1, DMA_FROM_DEVICE);
-
-               /*
-                * Also process recvs in connecting state because it is possible
-                * to get a recv completion _before_ the rdmacm ESTABLISHED
-                * event is processed.
-                */
-               if (wc.status == IB_WC_SUCCESS) {
-                       rds_ib_process_recv(conn, recv, wc.byte_len, state);
-               } else {
-                       /* We expect errors as the qp is drained during shutdown */
-                       if (rds_conn_up(conn) || rds_conn_connecting(conn))
-                               rds_ib_conn_error(conn, "recv completion on %pI4 had "
-                                                 "status %u (%s), disconnecting and "
-                                                 "reconnecting\n", &conn->c_faddr,
-                                                 wc.status,
-                                                 ib_wc_status_msg(wc.status));
-               }
+       rds_ib_stats_inc(s_ib_rx_cq_event);
+       recv = &ic->i_recvs[rds_ib_ring_oldest(&ic->i_recv_ring)];
+       ib_dma_unmap_sg(ic->i_cm_id->device, &recv->r_frag->f_sg, 1,
+                       DMA_FROM_DEVICE);
 
-               /*
-                * rds_ib_process_recv() doesn't always consume the frag, and
-                * we might not have called it at all if the wc didn't indicate
-                * success. We already unmapped the frag's pages, though, and
-                * the following rds_ib_ring_free() call tells the refill path
-                * that it will not find an allocated frag here. Make sure we
-                * keep that promise by freeing a frag that's still on the ring.
-                */
-               if (recv->r_frag) {
-                       rds_ib_frag_free(ic, recv->r_frag);
-                       recv->r_frag = NULL;
-               }
-               rds_ib_ring_free(&ic->i_recv_ring, 1);
+       /* Also process recvs in connecting state because it is possible
+        * to get a recv completion _before_ the rdmacm ESTABLISHED
+        * event is processed.
+        */
+       if (wc->status == IB_WC_SUCCESS) {
+               rds_ib_process_recv(conn, recv, wc->byte_len, state);
+       } else {
+               /* We expect errors as the qp is drained during shutdown */
+               if (rds_conn_up(conn) || rds_conn_connecting(conn))
+                       rds_ib_conn_error(conn, "recv completion on %pI4 had status %u (%s), disconnecting and reconnecting\n",
+                                         &conn->c_faddr,
+                                         wc->status,
+                                         ib_wc_status_msg(wc->status));
        }
-}
 
-void rds_ib_recv_tasklet_fn(unsigned long data)
-{
-       struct rds_ib_connection *ic = (struct rds_ib_connection *) data;
-       struct rds_connection *conn = ic->conn;
-       struct rds_ib_ack_state state = { 0, };
-
-       rds_poll_cq(ic, &state);
-       ib_req_notify_cq(ic->i_recv_cq, IB_CQ_SOLICITED);
-       rds_poll_cq(ic, &state);
-
-       if (state.ack_next_valid)
-               rds_ib_set_ack(ic, state.ack_next, state.ack_required);
-       if (state.ack_recv_valid && state.ack_recv > ic->i_ack_recv) {
-               rds_send_drop_acked(conn, state.ack_recv, NULL);
-               ic->i_ack_recv = state.ack_recv;
+       /* rds_ib_process_recv() doesn't always consume the frag, and
+        * we might not have called it at all if the wc didn't indicate
+        * success. We already unmapped the frag's pages, though, and
+        * the following rds_ib_ring_free() call tells the refill path
+        * that it will not find an allocated frag here. Make sure we
+        * keep that promise by freeing a frag that's still on the ring.
+        */
+       if (recv->r_frag) {
+               rds_ib_frag_free(ic, recv->r_frag);
+               recv->r_frag = NULL;
        }
-       if (rds_conn_up(conn))
-               rds_ib_attempt_ack(ic);
+       rds_ib_ring_free(&ic->i_recv_ring, 1);
 
        /* If we ever end up with a really empty receive ring, we're
         * in deep trouble, as the sender will definitely see RNR
index 4e88047086b6e10c62485f95b63f397900c32b28..670882c752e9470e6016fc51b0375006f4a94780 100644 (file)
@@ -195,7 +195,7 @@ void rds_ib_send_init_ring(struct rds_ib_connection *ic)
 
                send->s_op = NULL;
 
-               send->s_wr.wr_id = i;
+               send->s_wr.wr_id = i | RDS_IB_SEND_OP;
                send->s_wr.sg_list = send->s_sge;
                send->s_wr.ex.imm_data = 0;
 
@@ -237,81 +237,73 @@ static void rds_ib_sub_signaled(struct rds_ib_connection *ic, int nr)
  * unallocs the next free entry in the ring it doesn't alter which is
  * the next to be freed, which is what this is concerned with.
  */
-void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context)
+void rds_ib_send_cqe_handler(struct rds_ib_connection *ic, struct ib_wc *wc)
 {
-       struct rds_connection *conn = context;
-       struct rds_ib_connection *ic = conn->c_transport_data;
        struct rds_message *rm = NULL;
-       struct ib_wc wc;
+       struct rds_connection *conn = ic->conn;
        struct rds_ib_send_work *send;
        u32 completed;
        u32 oldest;
        u32 i = 0;
-       int ret;
        int nr_sig = 0;
 
-       rdsdebug("cq %p conn %p\n", cq, conn);
-       rds_ib_stats_inc(s_ib_tx_cq_call);
-       ret = ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
-       if (ret)
-               rdsdebug("ib_req_notify_cq send failed: %d\n", ret);
-
-       while (ib_poll_cq(cq, 1, &wc) > 0) {
-               rdsdebug("wc wr_id 0x%llx status %u (%s) byte_len %u imm_data %u\n",
-                        (unsigned long long)wc.wr_id, wc.status,
-                        ib_wc_status_msg(wc.status), wc.byte_len,
-                        be32_to_cpu(wc.ex.imm_data));
-               rds_ib_stats_inc(s_ib_tx_cq_event);
-
-               if (wc.wr_id == RDS_IB_ACK_WR_ID) {
-                       if (time_after(jiffies, ic->i_ack_queued + HZ/2))
-                               rds_ib_stats_inc(s_ib_tx_stalled);
-                       rds_ib_ack_send_complete(ic);
-                       continue;
-               }
 
-               oldest = rds_ib_ring_oldest(&ic->i_send_ring);
+       rdsdebug("wc wr_id 0x%llx status %u (%s) byte_len %u imm_data %u\n",
+                (unsigned long long)wc->wr_id, wc->status,
+                ib_wc_status_msg(wc->status), wc->byte_len,
+                be32_to_cpu(wc->ex.imm_data));
+       rds_ib_stats_inc(s_ib_tx_cq_event);
 
-               completed = rds_ib_ring_completed(&ic->i_send_ring, wc.wr_id, oldest);
+       if (wc->wr_id == RDS_IB_ACK_WR_ID) {
+               if (time_after(jiffies, ic->i_ack_queued + HZ / 2))
+                       rds_ib_stats_inc(s_ib_tx_stalled);
+               rds_ib_ack_send_complete(ic);
+               return;
+       }
 
-               for (i = 0; i < completed; i++) {
-                       send = &ic->i_sends[oldest];
-                       if (send->s_wr.send_flags & IB_SEND_SIGNALED)
-                               nr_sig++;
+       oldest = rds_ib_ring_oldest(&ic->i_send_ring);
 
-                       rm = rds_ib_send_unmap_op(ic, send, wc.status);
+       completed = rds_ib_ring_completed(&ic->i_send_ring,
+                                         (wc->wr_id & ~RDS_IB_SEND_OP),
+                                         oldest);
 
-                       if (time_after(jiffies, send->s_queued + HZ/2))
-                               rds_ib_stats_inc(s_ib_tx_stalled);
+       for (i = 0; i < completed; i++) {
+               send = &ic->i_sends[oldest];
+               if (send->s_wr.send_flags & IB_SEND_SIGNALED)
+                       nr_sig++;
 
-                       if (send->s_op) {
-                               if (send->s_op == rm->m_final_op) {
-                                       /* If anyone waited for this message to get flushed out, wake
-                                        * them up now */
-                                       rds_message_unmapped(rm);
-                               }
-                               rds_message_put(rm);
-                               send->s_op = NULL;
-                       }
+               rm = rds_ib_send_unmap_op(ic, send, wc->status);
 
-                       oldest = (oldest + 1) % ic->i_send_ring.w_nr;
-               }
+               if (time_after(jiffies, send->s_queued + HZ / 2))
+                       rds_ib_stats_inc(s_ib_tx_stalled);
 
-               rds_ib_ring_free(&ic->i_send_ring, completed);
-               rds_ib_sub_signaled(ic, nr_sig);
-               nr_sig = 0;
-
-               if (test_and_clear_bit(RDS_LL_SEND_FULL, &conn->c_flags) ||
-                   test_bit(0, &conn->c_map_queued))
-                       queue_delayed_work(rds_wq, &conn->c_send_w, 0);
-
-               /* We expect errors as the qp is drained during shutdown */
-               if (wc.status != IB_WC_SUCCESS && rds_conn_up(conn)) {
-                       rds_ib_conn_error(conn, "send completion on %pI4 had status "
-                                         "%u (%s), disconnecting and reconnecting\n",
-                                         &conn->c_faddr, wc.status,
-                                         ib_wc_status_msg(wc.status));
+               if (send->s_op) {
+                       if (send->s_op == rm->m_final_op) {
+                               /* If anyone waited for this message to get
+                                * flushed out, wake them up now
+                                */
+                               rds_message_unmapped(rm);
+                       }
+                       rds_message_put(rm);
+                       send->s_op = NULL;
                }
+
+               oldest = (oldest + 1) % ic->i_send_ring.w_nr;
+       }
+
+       rds_ib_ring_free(&ic->i_send_ring, completed);
+       rds_ib_sub_signaled(ic, nr_sig);
+       nr_sig = 0;
+
+       if (test_and_clear_bit(RDS_LL_SEND_FULL, &conn->c_flags) ||
+           test_bit(0, &conn->c_map_queued))
+               queue_delayed_work(rds_wq, &conn->c_send_w, 0);
+
+       /* We expect errors as the qp is drained during shutdown */
+       if (wc->status != IB_WC_SUCCESS && rds_conn_up(conn)) {
+               rds_ib_conn_error(conn, "send completion on %pI4 had status %u (%s), disconnecting and reconnecting\n",
+                                 &conn->c_faddr, wc->status,
+                                 ib_wc_status_msg(wc->status));
        }
 }
 
index 2d5965d6e97c039517d219bfdad3f28f7437b1d7..d77e0447305630d721760542ced368f86a9e15f9 100644 (file)
@@ -42,14 +42,14 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct rds_ib_statistics, rds_ib_stats);
 static const char *const rds_ib_stat_names[] = {
        "ib_connect_raced",
        "ib_listen_closed_stale",
-       "ib_tx_cq_call",
+       "s_ib_evt_handler_call",
+       "ib_tasklet_call",
        "ib_tx_cq_event",
        "ib_tx_ring_full",
        "ib_tx_throttle",
        "ib_tx_sg_mapping_failure",
        "ib_tx_stalled",
        "ib_tx_credit_updates",
-       "ib_rx_cq_call",
        "ib_rx_cq_event",
        "ib_rx_ring_empty",
        "ib_rx_refill_from_cq",
@@ -61,12 +61,18 @@ static const char *const rds_ib_stat_names[] = {
        "ib_ack_send_delayed",
        "ib_ack_send_piggybacked",
        "ib_ack_received",
-       "ib_rdma_mr_alloc",
-       "ib_rdma_mr_free",
-       "ib_rdma_mr_used",
-       "ib_rdma_mr_pool_flush",
-       "ib_rdma_mr_pool_wait",
-       "ib_rdma_mr_pool_depleted",
+       "ib_rdma_mr_8k_alloc",
+       "ib_rdma_mr_8k_free",
+       "ib_rdma_mr_8k_used",
+       "ib_rdma_mr_8k_pool_flush",
+       "ib_rdma_mr_8k_pool_wait",
+       "ib_rdma_mr_8k_pool_depleted",
+       "ib_rdma_mr_1m_alloc",
+       "ib_rdma_mr_1m_free",
+       "ib_rdma_mr_1m_used",
+       "ib_rdma_mr_1m_pool_flush",
+       "ib_rdma_mr_1m_pool_wait",
+       "ib_rdma_mr_1m_pool_depleted",
        "ib_atomic_cswp",
        "ib_atomic_fadd",
 };
index 6a8fbd6e69e7cf721c26aa742cc957e3be109fbd..d3d4454ffc84c6603397976e8c027b62c3300fbe 100644 (file)
@@ -75,7 +75,7 @@ struct rds_iw_mr_pool {
        int                     max_pages;
 };
 
-static int rds_iw_flush_mr_pool(struct rds_iw_mr_pool *pool, int free_all);
+static void rds_iw_flush_mr_pool(struct rds_iw_mr_pool *pool, int free_all);
 static void rds_iw_mr_pool_flush_worker(struct work_struct *work);
 static int rds_iw_init_fastreg(struct rds_iw_mr_pool *pool, struct rds_iw_mr *ibmr);
 static int rds_iw_map_fastreg(struct rds_iw_mr_pool *pool,
@@ -479,14 +479,13 @@ void rds_iw_sync_mr(void *trans_private, int direction)
  * If the number of MRs allocated exceeds the limit, we also try
  * to free as many MRs as needed to get back to this limit.
  */
-static int rds_iw_flush_mr_pool(struct rds_iw_mr_pool *pool, int free_all)
+static void rds_iw_flush_mr_pool(struct rds_iw_mr_pool *pool, int free_all)
 {
        struct rds_iw_mr *ibmr, *next;
        LIST_HEAD(unmap_list);
        LIST_HEAD(kill_list);
        unsigned long flags;
        unsigned int nfreed = 0, ncleaned = 0, unpinned = 0;
-       int ret = 0;
 
        rds_iw_stats_inc(s_iw_rdma_mr_pool_flush);
 
@@ -538,7 +537,6 @@ static int rds_iw_flush_mr_pool(struct rds_iw_mr_pool *pool, int free_all)
        atomic_sub(nfreed, &pool->item_count);
 
        mutex_unlock(&pool->flush_lock);
-       return ret;
 }
 
 static void rds_iw_mr_pool_flush_worker(struct work_struct *work)
index afb4048d0cfd2b530e8885cc9f2f28f96aac63f0..0e2797bdc3162b26995aeee2974187fdea62f5bf 100644 (file)
@@ -7,6 +7,7 @@
 #include <rdma/rdma_cm.h>
 #include <linux/mutex.h>
 #include <linux/rds.h>
+#include <linux/rhashtable.h>
 
 #include "info.h"
 
@@ -86,7 +87,9 @@ struct rds_connection {
        struct hlist_node       c_hash_node;
        __be32                  c_laddr;
        __be32                  c_faddr;
-       unsigned int            c_loopback:1;
+       unsigned int            c_loopback:1,
+                               c_outgoing:1,
+                               c_pad_to_32:30;
        struct rds_connection   *c_passive;
 
        struct rds_cong_map     *c_lcong;
@@ -472,7 +475,8 @@ struct rds_sock {
         * bound_addr used for both incoming and outgoing, no INADDR_ANY
         * support.
         */
-       struct hlist_node       rs_bound_node;
+       struct rhash_head       rs_bound_node;
+       u64                     rs_bound_key;
        __be32                  rs_bound_addr;
        __be32                  rs_conn_addr;
        __be16                  rs_bound_port;
@@ -603,6 +607,8 @@ extern wait_queue_head_t rds_poll_waitq;
 int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len);
 void rds_remove_bound(struct rds_sock *rs);
 struct rds_sock *rds_find_bound(__be32 addr, __be16 port);
+int rds_bind_lock_init(void);
+void rds_bind_lock_destroy(void);
 
 /* cong.c */
 int rds_cong_get_maps(struct rds_connection *conn);
index 4df61a515b83da02bd91fc5e247e17ad44246ec2..827155c2ead10376cb633c45c2f43917f5f5cd12 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/list.h>
 #include <linux/ratelimit.h>
 #include <linux/export.h>
+#include <linux/sizes.h>
 
 #include "rds.h"
 
@@ -51,7 +52,7 @@
  * it to 0 will restore the old behavior (where we looped until we had
  * drained the queue).
  */
-static int send_batch_count = 64;
+static int send_batch_count = SZ_1K;
 module_param(send_batch_count, int, 0444);
 MODULE_PARM_DESC(send_batch_count, " batch factor when working the send queue");
 
@@ -223,7 +224,7 @@ restart:
                         * through a lot of messages, lets back off and see
                         * if anyone else jumps in
                         */
-                       if (batch_count >= 1024)
+                       if (batch_count >= send_batch_count)
                                goto over_batch;
 
                        spin_lock_irqsave(&conn->c_lock, flags);
@@ -423,12 +424,15 @@ over_batch:
                     !list_empty(&conn->c_send_queue)) &&
                    send_gen == conn->c_send_gen) {
                        rds_stats_inc(s_send_lock_queue_raced);
-                       goto restart;
+                       if (batch_count < send_batch_count)
+                               goto restart;
+                       queue_delayed_work(rds_wq, &conn->c_send_w, 1);
                }
        }
 out:
        return ret;
 }
+EXPORT_SYMBOL_GPL(rds_send_xmit);
 
 static void rds_send_sndbuf_remove(struct rds_sock *rs, struct rds_message *rm)
 {
@@ -1120,8 +1124,9 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
         */
        rds_stats_inc(s_send_queued);
 
-       if (!test_bit(RDS_LL_SEND_FULL, &conn->c_flags))
-               rds_send_xmit(conn);
+       ret = rds_send_xmit(conn);
+       if (ret == -ENOMEM || ret == -EAGAIN)
+               queue_delayed_work(rds_wq, &conn->c_send_w, 1);
 
        rds_message_put(rm);
        return payload_len;
@@ -1177,8 +1182,8 @@ rds_send_pong(struct rds_connection *conn, __be16 dport)
        rds_stats_inc(s_send_queued);
        rds_stats_inc(s_send_pong);
 
-       if (!test_bit(RDS_LL_SEND_FULL, &conn->c_flags))
-               queue_delayed_work(rds_wq, &conn->c_send_w, 0);
+       /* schedule the send work on rds_wq */
+       queue_delayed_work(rds_wq, &conn->c_send_w, 1);
 
        rds_message_put(rm);
        return 0;
index c42b60bf4c68eb26f7364df7e045c40847ea3be3..9d6ddbacd8750e22b05986b37e40a8fe1bb95865 100644 (file)
@@ -67,21 +67,13 @@ void rds_tcp_nonagle(struct socket *sock)
        set_fs(oldfs);
 }
 
+/* All module specific customizations to the RDS-TCP socket should be done in
+ * rds_tcp_tune() and applied after socket creation. In general these
+ * customizations should be tunable via module_param()
+ */
 void rds_tcp_tune(struct socket *sock)
 {
-       struct sock *sk = sock->sk;
-
        rds_tcp_nonagle(sock);
-
-       /*
-        * We're trying to saturate gigabit with the default,
-        * see svc_sock_setbufsize().
-        */
-       lock_sock(sk);
-       sk->sk_sndbuf = RDS_TCP_DEFAULT_BUFSIZE;
-       sk->sk_rcvbuf = RDS_TCP_DEFAULT_BUFSIZE;
-       sk->sk_userlocks |= SOCK_SNDBUF_LOCK|SOCK_RCVBUF_LOCK;
-       release_sock(sk);
 }
 
 u32 rds_tcp_snd_nxt(struct rds_tcp_connection *tc)
index 444d78d0bd77c0ddc95daf99a22fc5983ea95bb6..0936a4a32b476fdde5c7208fc465ec3324bbcf09 100644 (file)
@@ -110,28 +110,27 @@ int rds_tcp_accept_one(struct socket *sock)
                goto out;
        }
        /* An incoming SYN request came in, and TCP just accepted it.
-        * We always create a new conn for listen side of TCP, and do not
-        * add it to the c_hash_list.
         *
         * If the client reboots, this conn will need to be cleaned up.
         * rds_tcp_state_change() will do that cleanup
         */
        rs_tcp = (struct rds_tcp_connection *)conn->c_transport_data;
-       WARN_ON(!rs_tcp || rs_tcp->t_sock);
-
-       /*
-        * see the comment above rds_queue_delayed_reconnect()
-        */
-       if (!rds_conn_transition(conn, RDS_CONN_DOWN, RDS_CONN_CONNECTING)) {
-               if (rds_conn_state(conn) == RDS_CONN_UP)
-                       rds_tcp_stats_inc(s_tcp_listen_closed_stale);
-               else
-                       rds_tcp_stats_inc(s_tcp_connect_raced);
-               rds_conn_drop(conn);
+       if (rs_tcp->t_sock &&
+           ntohl(inet->inet_saddr) < ntohl(inet->inet_daddr)) {
+               struct sock *nsk = new_sock->sk;
+
+               nsk->sk_user_data = NULL;
+               nsk->sk_prot->disconnect(nsk, 0);
+               tcp_done(nsk);
+               new_sock = NULL;
                ret = 0;
                goto out;
+       } else if (rs_tcp->t_sock) {
+               rds_tcp_restore_callbacks(rs_tcp->t_sock, rs_tcp);
+               conn->c_outgoing = 0;
        }
 
+       rds_conn_transition(conn, RDS_CONN_DOWN, RDS_CONN_CONNECTING);
        rds_tcp_set_callbacks(new_sock, conn);
        rds_connect_complete(conn);
        new_sock = NULL;
index fbc5ef88bc0e692ea4cf9cb59f6163905510928e..27a992154804c685d983206b599ef7a5bf96e8af 100644 (file)
@@ -214,8 +214,15 @@ static int rds_tcp_data_recv(read_descriptor_t *desc, struct sk_buff *skb,
                        }
 
                        to_copy = min(tc->t_tinc_data_rem, left);
-                       pskb_pull(clone, offset);
-                       pskb_trim(clone, to_copy);
+                       if (!pskb_pull(clone, offset) ||
+                           pskb_trim(clone, to_copy)) {
+                               pr_warn("rds_tcp_data_recv: pull/trim failed "
+                                       "left %zu data_rem %zu skb_len %d\n",
+                                       left, tc->t_tinc_data_rem, skb->len);
+                               kfree_skb(clone);
+                               desc->error = -ENOMEM;
+                               goto out;
+                       }
                        skb_queue_tail(&tinc->ti_skb_list, clone);
 
                        rdsdebug("skb %p data %p len %d off %u to_copy %zu -> "
index 53b17ca0dff5a5618d92b2f6aeb876284707959b..2894e6095e3bbd841c082c1d983bf9f949090d16 100644 (file)
@@ -83,6 +83,7 @@ int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm,
        struct rds_tcp_connection *tc = conn->c_transport_data;
        int done = 0;
        int ret = 0;
+       int more;
 
        if (hdr_off == 0) {
                /*
@@ -116,12 +117,15 @@ int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm,
                        goto out;
        }
 
+       more = rm->data.op_nents > 1 ? (MSG_MORE | MSG_SENDPAGE_NOTLAST) : 0;
        while (sg < rm->data.op_nents) {
+               int flags = MSG_DONTWAIT | MSG_NOSIGNAL | more;
+
                ret = tc->t_sock->ops->sendpage(tc->t_sock,
                                                sg_page(&rm->data.op_sg[sg]),
                                                rm->data.op_sg[sg].offset + off,
                                                rm->data.op_sg[sg].length - off,
-                                               MSG_DONTWAIT|MSG_NOSIGNAL);
+                                               flags);
                rdsdebug("tcp sendpage %p:%u:%u ret %d\n", (void *)sg_page(&rm->data.op_sg[sg]),
                         rm->data.op_sg[sg].offset + off, rm->data.op_sg[sg].length - off,
                         ret);
@@ -134,6 +138,8 @@ int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm,
                        off = 0;
                        sg++;
                }
+               if (sg == rm->data.op_nents - 1)
+                       more = 0;
        }
 
 out:
index dc2402e871fda52d10b0a67089c7c6e13832f72b..454aa6d23327b2a302bcfc72b6b3d857656e33ed 100644 (file)
@@ -162,7 +162,9 @@ void rds_send_worker(struct work_struct *work)
        int ret;
 
        if (rds_conn_state(conn) == RDS_CONN_UP) {
+               clear_bit(RDS_LL_SEND_FULL, &conn->c_flags);
                ret = rds_send_xmit(conn);
+               cond_resched();
                rdsdebug("conn %p ret %d\n", conn, ret);
                switch (ret) {
                case -EAGAIN:
index 6631f4f1e39be713029c8b9b504db4ea741fb3e6..692b3e67fb54418ffb143491b9e2f1dc82d8d503 100644 (file)
@@ -808,7 +808,7 @@ void rxrpc_put_connection(struct rxrpc_connection *conn)
 
        ASSERTCMP(atomic_read(&conn->usage), >, 0);
 
-       conn->put_time = get_seconds();
+       conn->put_time = ktime_get_seconds();
        if (atomic_dec_and_test(&conn->usage)) {
                _debug("zombie");
                rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0);
@@ -852,7 +852,7 @@ static void rxrpc_connection_reaper(struct work_struct *work)
 
        _enter("");
 
-       now = get_seconds();
+       now = ktime_get_seconds();
        earliest = ULONG_MAX;
 
        write_lock_bh(&rxrpc_connection_lock);
index aef1bd294e1796b68052936bdc8d0b62b52814fe..2934a73a5981ad154888904b67f8082fedac4a3c 100644 (file)
@@ -208,7 +208,7 @@ struct rxrpc_transport {
        struct rb_root          server_conns;   /* server connections on this transport */
        struct list_head        link;           /* link in master session list */
        struct sk_buff_head     error_queue;    /* error packets awaiting processing */
-       time_t                  put_time;       /* time at which to reap */
+       unsigned long           put_time;       /* time at which to reap */
        spinlock_t              client_lock;    /* client connection allocation lock */
        rwlock_t                conn_lock;      /* lock for active/dead connections */
        atomic_t                usage;
@@ -256,7 +256,7 @@ struct rxrpc_connection {
        struct rxrpc_crypt      csum_iv;        /* packet checksum base */
        unsigned long           events;
 #define RXRPC_CONN_CHALLENGE   0               /* send challenge packet */
-       time_t                  put_time;       /* time at which to reap */
+       unsigned long           put_time;       /* time at which to reap */
        rwlock_t                lock;           /* access lock */
        spinlock_t              state_lock;     /* state-change lock */
        atomic_t                usage;
index 1976dec84f297cfb126df6bcd53129f1d518001b..9946467f16b41a9f5acfc59865f085c04e007b77 100644 (file)
@@ -189,7 +189,7 @@ void rxrpc_put_transport(struct rxrpc_transport *trans)
 
        ASSERTCMP(atomic_read(&trans->usage), >, 0);
 
-       trans->put_time = get_seconds();
+       trans->put_time = ktime_get_seconds();
        if (unlikely(atomic_dec_and_test(&trans->usage))) {
                _debug("zombie");
                /* let the reaper determine the timeout to avoid a race with
@@ -226,7 +226,7 @@ static void rxrpc_transport_reaper(struct work_struct *work)
 
        _enter("");
 
-       now = get_seconds();
+       now = ktime_get_seconds();
        earliest = ULONG_MAX;
 
        /* extract all the transports that have been dead too long */
index 559bfa011bda7b7c6fed76ef7df8d4cd1d861bc3..0bc6f912f870297a6d91ef04acca76f6e1915339 100644 (file)
@@ -72,6 +72,7 @@ static int tcf_bpf(struct sk_buff *skb, const struct tc_action *act,
        case TC_ACT_PIPE:
        case TC_ACT_RECLASSIFY:
        case TC_ACT_OK:
+       case TC_ACT_REDIRECT:
                action = filter_res;
                break;
        case TC_ACT_SHOT:
index 5019a47b9270e758f65c346631becc99e933b0c6..bb41699c6c49eb4a40241d662a79ebd39cd6ff8f 100644 (file)
@@ -68,13 +68,13 @@ static int tcf_connmark(struct sk_buff *skb, const struct tc_action *a,
        }
 
        if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
-                              proto, &tuple))
+                              proto, ca->net, &tuple))
                goto out;
 
        zone.id = ca->zone;
        zone.dir = NF_CT_DEFAULT_ZONE_DIR;
 
-       thash = nf_conntrack_find_get(dev_net(skb->dev), &zone, &tuple);
+       thash = nf_conntrack_find_get(ca->net, &zone, &tuple);
        if (!thash)
                goto out;
 
@@ -119,6 +119,7 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla,
 
                ci = to_connmark(a);
                ci->tcf_action = parm->action;
+               ci->net = net;
                ci->zone = parm->zone;
 
                tcf_hash_insert(a);
index 99c9cc1c7af9240f9df444ae158df4fa7f7f8c73..d05869646515dbabeb352361c9d58b9ccfc687d5 100644 (file)
@@ -189,6 +189,7 @@ static int tcf_ipt(struct sk_buff *skb, const struct tc_action *a,
         * worry later - danger - this API seems to have changed
         * from earlier kernels
         */
+       par.net      = dev_net(skb->dev);
        par.in       = skb->dev;
        par.out      = NULL;
        par.hooknum  = ipt->tcfi_hook;
index e5168f8b9640964ce2dd95a896a24f6c986a959a..5faaa5425f7b72293fdd80e1b27405fcf479e207 100644 (file)
@@ -38,6 +38,7 @@ struct cls_bpf_prog {
        struct bpf_prog *filter;
        struct list_head link;
        struct tcf_result res;
+       bool exts_integrated;
        struct tcf_exts exts;
        u32 handle;
        union {
@@ -52,6 +53,7 @@ struct cls_bpf_prog {
 
 static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = {
        [TCA_BPF_CLASSID]       = { .type = NLA_U32 },
+       [TCA_BPF_FLAGS]         = { .type = NLA_U32 },
        [TCA_BPF_FD]            = { .type = NLA_U32 },
        [TCA_BPF_NAME]          = { .type = NLA_NUL_STRING, .len = CLS_BPF_NAME_LEN },
        [TCA_BPF_OPS_LEN]       = { .type = NLA_U16 },
@@ -59,6 +61,20 @@ static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = {
                                    .len = sizeof(struct sock_filter) * BPF_MAXINSNS },
 };
 
+static int cls_bpf_exec_opcode(int code)
+{
+       switch (code) {
+       case TC_ACT_OK:
+       case TC_ACT_SHOT:
+       case TC_ACT_STOLEN:
+       case TC_ACT_REDIRECT:
+       case TC_ACT_UNSPEC:
+               return code;
+       default:
+               return TC_ACT_UNSPEC;
+       }
+}
+
 static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
                            struct tcf_result *res)
 {
@@ -79,6 +95,8 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
        list_for_each_entry_rcu(prog, &head->plist, link) {
                int filter_res;
 
+               qdisc_skb_cb(skb)->tc_classid = prog->res.classid;
+
                if (at_ingress) {
                        /* It is safe to push/pull even if skb_shared() */
                        __skb_push(skb, skb->mac_len);
@@ -88,6 +106,16 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
                        filter_res = BPF_PROG_RUN(prog->filter, skb);
                }
 
+               if (prog->exts_integrated) {
+                       res->class = prog->res.class;
+                       res->classid = qdisc_skb_cb(skb)->tc_classid;
+
+                       ret = cls_bpf_exec_opcode(filter_res);
+                       if (ret == TC_ACT_UNSPEC)
+                               continue;
+                       break;
+               }
+
                if (filter_res == 0)
                        continue;
 
@@ -195,8 +223,7 @@ static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle)
        return ret;
 }
 
-static int cls_bpf_prog_from_ops(struct nlattr **tb,
-                                struct cls_bpf_prog *prog, u32 classid)
+static int cls_bpf_prog_from_ops(struct nlattr **tb, struct cls_bpf_prog *prog)
 {
        struct sock_filter *bpf_ops;
        struct sock_fprog_kern fprog_tmp;
@@ -230,15 +257,13 @@ static int cls_bpf_prog_from_ops(struct nlattr **tb,
        prog->bpf_ops = bpf_ops;
        prog->bpf_num_ops = bpf_num_ops;
        prog->bpf_name = NULL;
-
        prog->filter = fp;
-       prog->res.classid = classid;
 
        return 0;
 }
 
-static int cls_bpf_prog_from_efd(struct nlattr **tb,
-                                struct cls_bpf_prog *prog, u32 classid)
+static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog,
+                                const struct tcf_proto *tp)
 {
        struct bpf_prog *fp;
        char *name = NULL;
@@ -268,9 +293,10 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb,
        prog->bpf_ops = NULL;
        prog->bpf_fd = bpf_fd;
        prog->bpf_name = name;
-
        prog->filter = fp;
-       prog->res.classid = classid;
+
+       if (fp->dst_needed)
+               netif_keep_dst(qdisc_dev(tp->q));
 
        return 0;
 }
@@ -280,16 +306,13 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
                                   unsigned long base, struct nlattr **tb,
                                   struct nlattr *est, bool ovr)
 {
+       bool is_bpf, is_ebpf, have_exts = false;
        struct tcf_exts exts;
-       bool is_bpf, is_ebpf;
-       u32 classid;
        int ret;
 
        is_bpf = tb[TCA_BPF_OPS_LEN] && tb[TCA_BPF_OPS];
        is_ebpf = tb[TCA_BPF_FD];
-
-       if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf) ||
-           !tb[TCA_BPF_CLASSID])
+       if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf))
                return -EINVAL;
 
        tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE);
@@ -297,18 +320,32 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
        if (ret < 0)
                return ret;
 
-       classid = nla_get_u32(tb[TCA_BPF_CLASSID]);
+       if (tb[TCA_BPF_FLAGS]) {
+               u32 bpf_flags = nla_get_u32(tb[TCA_BPF_FLAGS]);
+
+               if (bpf_flags & ~TCA_BPF_FLAG_ACT_DIRECT) {
+                       tcf_exts_destroy(&exts);
+                       return -EINVAL;
+               }
+
+               have_exts = bpf_flags & TCA_BPF_FLAG_ACT_DIRECT;
+       }
+
+       prog->exts_integrated = have_exts;
 
-       ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog, classid) :
-                      cls_bpf_prog_from_efd(tb, prog, classid);
+       ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog) :
+                      cls_bpf_prog_from_efd(tb, prog, tp);
        if (ret < 0) {
                tcf_exts_destroy(&exts);
                return ret;
        }
 
-       tcf_bind_filter(tp, &prog->res, base);
-       tcf_exts_change(tp, &prog->exts, &exts);
+       if (tb[TCA_BPF_CLASSID]) {
+               prog->res.classid = nla_get_u32(tb[TCA_BPF_CLASSID]);
+               tcf_bind_filter(tp, &prog->res, base);
+       }
 
+       tcf_exts_change(tp, &prog->exts, &exts);
        return 0;
 }
 
@@ -429,6 +466,7 @@ static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
 {
        struct cls_bpf_prog *prog = (struct cls_bpf_prog *) fh;
        struct nlattr *nest;
+       u32 bpf_flags = 0;
        int ret;
 
        if (prog == NULL)
@@ -440,7 +478,8 @@ static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
        if (nest == NULL)
                goto nla_put_failure;
 
-       if (nla_put_u32(skb, TCA_BPF_CLASSID, prog->res.classid))
+       if (prog->res.classid &&
+           nla_put_u32(skb, TCA_BPF_CLASSID, prog->res.classid))
                goto nla_put_failure;
 
        if (cls_bpf_is_ebpf(prog))
@@ -453,6 +492,11 @@ static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
        if (tcf_exts_dump(skb, &prog->exts) < 0)
                goto nla_put_failure;
 
+       if (prog->exts_integrated)
+               bpf_flags |= TCA_BPF_FLAG_ACT_DIRECT;
+       if (bpf_flags && nla_put_u32(skb, TCA_BPF_FLAGS, bpf_flags))
+               goto nla_put_failure;
+
        nla_nest_end(skb, nest);
 
        if (tcf_exts_dump_stats(skb, &prog->exts) < 0)
index df0328ba6a48243056526fbc044f8c0e392f4f3c..c66ca9400ab4f03d96bc287381a935546abfe8fa 100644 (file)
@@ -95,6 +95,7 @@ static int em_ipset_match(struct sk_buff *skb, struct tcf_ematch *em,
        if (skb->skb_iif)
                indev = dev_get_by_index_rcu(em->net, skb->skb_iif);
 
+       acpar.net     = em->net;
        acpar.in      = indev ? indev : dev;
        acpar.out     = dev;
 
index 094a874b48bc43ca9337887bdd7cf462ee0ea63f..3fee70d9814f91f0e61d06aeecf6b1d5a6f94eeb 100644 (file)
@@ -11,7 +11,7 @@
  * Note: Quantum tunneling is not supported.
  */
 
-#include <linux/module.h>
+#include <linux/init.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/skbuff.h>
@@ -37,17 +37,8 @@ static struct Qdisc_ops blackhole_qdisc_ops __read_mostly = {
        .owner          = THIS_MODULE,
 };
 
-static int __init blackhole_module_init(void)
+static int __init blackhole_init(void)
 {
        return register_qdisc(&blackhole_qdisc_ops);
 }
-
-static void __exit blackhole_module_exit(void)
-{
-       unregister_qdisc(&blackhole_qdisc_ops);
-}
-
-module_init(blackhole_module_init)
-module_exit(blackhole_module_exit)
-
-MODULE_LICENSE("GPL");
+device_initcall(blackhole_init)
index 02bfd3d1c4f078e8b7da08791ea1069218095d1a..5ffb8b8337c7320368303e21a4bbd1fc0e4c5168 100644 (file)
@@ -553,65 +553,6 @@ static void choke_destroy(struct Qdisc *sch)
        choke_free(q->tab);
 }
 
-static struct Qdisc *choke_leaf(struct Qdisc *sch, unsigned long arg)
-{
-       return NULL;
-}
-
-static unsigned long choke_get(struct Qdisc *sch, u32 classid)
-{
-       return 0;
-}
-
-static void choke_put(struct Qdisc *q, unsigned long cl)
-{
-}
-
-static unsigned long choke_bind(struct Qdisc *sch, unsigned long parent,
-                               u32 classid)
-{
-       return 0;
-}
-
-static struct tcf_proto __rcu **choke_find_tcf(struct Qdisc *sch,
-                                              unsigned long cl)
-{
-       struct choke_sched_data *q = qdisc_priv(sch);
-
-       if (cl)
-               return NULL;
-       return &q->filter_list;
-}
-
-static int choke_dump_class(struct Qdisc *sch, unsigned long cl,
-                         struct sk_buff *skb, struct tcmsg *tcm)
-{
-       tcm->tcm_handle |= TC_H_MIN(cl);
-       return 0;
-}
-
-static void choke_walk(struct Qdisc *sch, struct qdisc_walker *arg)
-{
-       if (!arg->stop) {
-               if (arg->fn(sch, 1, arg) < 0) {
-                       arg->stop = 1;
-                       return;
-               }
-               arg->count++;
-       }
-}
-
-static const struct Qdisc_class_ops choke_class_ops = {
-       .leaf           =       choke_leaf,
-       .get            =       choke_get,
-       .put            =       choke_put,
-       .tcf_chain      =       choke_find_tcf,
-       .bind_tcf       =       choke_bind,
-       .unbind_tcf     =       choke_put,
-       .dump           =       choke_dump_class,
-       .walk           =       choke_walk,
-};
-
 static struct sk_buff *choke_peek_head(struct Qdisc *sch)
 {
        struct choke_sched_data *q = qdisc_priv(sch);
index c4d45fd8c551ef5b2c42910459b06c35667590ae..f357f34d02d2a5e9f4ead0ebe77ec44dca79f89a 100644 (file)
 
 #define NO_DEFAULT_INDEX       (1 << 16)
 
+struct mask_value {
+       u8                      mask;
+       u8                      value;
+};
+
 struct dsmark_qdisc_data {
        struct Qdisc            *q;
        struct tcf_proto __rcu  *filter_list;
-       u8                      *mask;  /* "owns" the array */
-       u8                      *value;
+       struct mask_value       *mv;
        u16                     indices;
+       u8                      set_tc_index;
        u32                     default_index;  /* index range is 0...0xffff */
-       int                     set_tc_index;
+#define DSMARK_EMBEDDED_SZ     16
+       struct mask_value       embedded[DSMARK_EMBEDDED_SZ];
 };
 
 static inline int dsmark_valid_index(struct dsmark_qdisc_data *p, u16 index)
@@ -116,7 +122,6 @@ static int dsmark_change(struct Qdisc *sch, u32 classid, u32 parent,
        struct nlattr *opt = tca[TCA_OPTIONS];
        struct nlattr *tb[TCA_DSMARK_MAX + 1];
        int err = -EINVAL;
-       u8 mask = 0;
 
        pr_debug("%s(sch %p,[qdisc %p],classid %x,parent %x), arg 0x%lx\n",
                 __func__, sch, p, classid, parent, *arg);
@@ -133,14 +138,11 @@ static int dsmark_change(struct Qdisc *sch, u32 classid, u32 parent,
        if (err < 0)
                goto errout;
 
-       if (tb[TCA_DSMARK_MASK])
-               mask = nla_get_u8(tb[TCA_DSMARK_MASK]);
-
        if (tb[TCA_DSMARK_VALUE])
-               p->value[*arg - 1] = nla_get_u8(tb[TCA_DSMARK_VALUE]);
+               p->mv[*arg - 1].value = nla_get_u8(tb[TCA_DSMARK_VALUE]);
 
        if (tb[TCA_DSMARK_MASK])
-               p->mask[*arg - 1] = mask;
+               p->mv[*arg - 1].mask = nla_get_u8(tb[TCA_DSMARK_MASK]);
 
        err = 0;
 
@@ -155,8 +157,8 @@ static int dsmark_delete(struct Qdisc *sch, unsigned long arg)
        if (!dsmark_valid_index(p, arg))
                return -EINVAL;
 
-       p->mask[arg - 1] = 0xff;
-       p->value[arg - 1] = 0;
+       p->mv[arg - 1].mask = 0xff;
+       p->mv[arg - 1].value = 0;
 
        return 0;
 }
@@ -173,7 +175,7 @@ static void dsmark_walk(struct Qdisc *sch, struct qdisc_walker *walker)
                return;
 
        for (i = 0; i < p->indices; i++) {
-               if (p->mask[i] == 0xff && !p->value[i])
+               if (p->mv[i].mask == 0xff && !p->mv[i].value)
                        goto ignore;
                if (walker->count >= walker->skip) {
                        if (walker->fn(sch, i + 1, walker) < 0) {
@@ -291,12 +293,12 @@ static struct sk_buff *dsmark_dequeue(struct Qdisc *sch)
 
        switch (tc_skb_protocol(skb)) {
        case htons(ETH_P_IP):
-               ipv4_change_dsfield(ip_hdr(skb), p->mask[index],
-                                   p->value[index]);
+               ipv4_change_dsfield(ip_hdr(skb), p->mv[index].mask,
+                                   p->mv[index].value);
                        break;
        case htons(ETH_P_IPV6):
-               ipv6_change_dsfield(ipv6_hdr(skb), p->mask[index],
-                                   p->value[index]);
+               ipv6_change_dsfield(ipv6_hdr(skb), p->mv[index].mask,
+                                   p->mv[index].value);
                        break;
        default:
                /*
@@ -304,7 +306,7 @@ static struct sk_buff *dsmark_dequeue(struct Qdisc *sch)
                 * This way, we can send non-IP traffic through dsmark
                 * and don't need yet another qdisc as a bypass.
                 */
-               if (p->mask[index] != 0xff || p->value[index])
+               if (p->mv[index].mask != 0xff || p->mv[index].value)
                        pr_warn("%s: unsupported protocol %d\n",
                                __func__, ntohs(tc_skb_protocol(skb)));
                break;
@@ -346,7 +348,7 @@ static int dsmark_init(struct Qdisc *sch, struct nlattr *opt)
        int err = -EINVAL;
        u32 default_index = NO_DEFAULT_INDEX;
        u16 indices;
-       u8 *mask;
+       int i;
 
        pr_debug("%s(sch %p,[qdisc %p],opt %p)\n", __func__, sch, p, opt);
 
@@ -366,18 +368,18 @@ static int dsmark_init(struct Qdisc *sch, struct nlattr *opt)
        if (tb[TCA_DSMARK_DEFAULT_INDEX])
                default_index = nla_get_u16(tb[TCA_DSMARK_DEFAULT_INDEX]);
 
-       mask = kmalloc(indices * 2, GFP_KERNEL);
-       if (mask == NULL) {
+       if (indices <= DSMARK_EMBEDDED_SZ)
+               p->mv = p->embedded;
+       else
+               p->mv = kmalloc_array(indices, sizeof(*p->mv), GFP_KERNEL);
+       if (!p->mv) {
                err = -ENOMEM;
                goto errout;
        }
-
-       p->mask = mask;
-       memset(p->mask, 0xff, indices);
-
-       p->value = p->mask + indices;
-       memset(p->value, 0, indices);
-
+       for (i = 0; i < indices; i++) {
+               p->mv[i].mask = 0xff;
+               p->mv[i].value = 0;
+       }
        p->indices = indices;
        p->default_index = default_index;
        p->set_tc_index = nla_get_flag(tb[TCA_DSMARK_SET_TC_INDEX]);
@@ -410,7 +412,8 @@ static void dsmark_destroy(struct Qdisc *sch)
 
        tcf_destroy_chain(&p->filter_list);
        qdisc_destroy(p->q);
-       kfree(p->mask);
+       if (p->mv != p->embedded)
+               kfree(p->mv);
 }
 
 static int dsmark_dump_class(struct Qdisc *sch, unsigned long cl,
@@ -430,8 +433,8 @@ static int dsmark_dump_class(struct Qdisc *sch, unsigned long cl,
        opts = nla_nest_start(skb, TCA_OPTIONS);
        if (opts == NULL)
                goto nla_put_failure;
-       if (nla_put_u8(skb, TCA_DSMARK_MASK, p->mask[cl - 1]) ||
-           nla_put_u8(skb, TCA_DSMARK_VALUE, p->value[cl - 1]))
+       if (nla_put_u8(skb, TCA_DSMARK_MASK, p->mv[cl - 1].mask) ||
+           nla_put_u8(skb, TCA_DSMARK_VALUE, p->mv[cl - 1].value))
                goto nla_put_failure;
 
        return nla_nest_end(skb, opts);
index f377702d4b9185762293a7251f2574a9e72515eb..109b2322778f2353d82ebfbc8e40578e80ae7099 100644 (file)
@@ -224,13 +224,16 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q)
        if (unlikely((skb->priority & TC_PRIO_MAX) == TC_PRIO_CONTROL))
                return &q->internal;
 
-       /* SYNACK messages are attached to a listener socket.
-        * 1) They are not part of a 'flow' yet
-        * 2) We do not want to rate limit them (eg SYNFLOOD attack),
+       /* SYNACK messages are attached to a TCP_NEW_SYN_RECV request socket
+        * or a listener (SYNCOOKIE mode)
+        * 1) request sockets are not full blown,
+        *    they do not contain sk_pacing_rate
+        * 2) They are not part of a 'flow' yet
+        * 3) We do not want to rate limit them (eg SYNFLOOD attack),
         *    especially if the listener set SO_MAX_PACING_RATE
-        * 3) We pretend they are orphaned
+        * 4) We pretend they are orphaned
         */
-       if (!sk || sk->sk_state == TCP_LISTEN) {
+       if (!sk || sk_listener(sk)) {
                unsigned long hash = skb_get_hash(skb) & q->orphan_mask;
 
                /* By forcing low order bit to 1, we make sure to not
index 7954c52e179442ab89151971787432af75d84383..763e06a55155b2a9e0a9d918ecc1fe2dd6d9e0c0 100644 (file)
@@ -2494,7 +2494,7 @@ static int sctp_process_param(struct sctp_association *asoc,
        __u16 sat;
        int retval = 1;
        sctp_scope_t scope;
-       time_t stale;
+       u32 stale;
        struct sctp_af *af;
        union sctp_addr_param *addr_param;
        struct sctp_transport *t;
index d7eaa7354cf76148d1a2c9ee3af4fff9a24990fb..6f46aa16cb76963de27cb87e5e8cedf617a259a9 100644 (file)
@@ -2306,7 +2306,7 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net,
                                                 sctp_cmd_seq_t *commands)
 {
        struct sctp_chunk *chunk = arg;
-       time_t stale;
+       u32 stale;
        sctp_cookie_preserve_param_t bht;
        sctp_errhdr_t *err;
        struct sctp_chunk *reply;
index 77f5d17e261230a6db4f261445311eafb37c7b06..f34e535e93bdf780d7093c84e32a6288029d5442 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * net/switchdev/switchdev.c - Switch device API
- * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ * Copyright (c) 2014-2015 Jiri Pirko <jiri@resnulli.us>
  * Copyright (c) 2014-2015 Scott Feldman <sfeldma@gmail.com>
  *
  * This program is free software; you can redistribute it and/or modify
 #include <linux/mutex.h>
 #include <linux/notifier.h>
 #include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include <linux/if_bridge.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
 #include <linux/if_vlan.h>
 #include <net/ip_fib.h>
 #include <net/switchdev.h>
 
+/**
+ *     switchdev_trans_item_enqueue - Enqueue data item to transaction queue
+ *
+ *     @trans: transaction
+ *     @data: pointer to data being queued
+ *     @destructor: data destructor
+ *     @tritem: transaction item being queued
+ *
+ *     Enqeueue data item to transaction queue. tritem is typically placed in
+ *     cointainter pointed at by data pointer. Destructor is called on
+ *     transaction abort and after successful commit phase in case
+ *     the caller did not dequeue the item before.
+ */
+void switchdev_trans_item_enqueue(struct switchdev_trans *trans,
+                                 void *data, void (*destructor)(void const *),
+                                 struct switchdev_trans_item *tritem)
+{
+       tritem->data = data;
+       tritem->destructor = destructor;
+       list_add_tail(&tritem->list, &trans->item_list);
+}
+EXPORT_SYMBOL_GPL(switchdev_trans_item_enqueue);
+
+static struct switchdev_trans_item *
+__switchdev_trans_item_dequeue(struct switchdev_trans *trans)
+{
+       struct switchdev_trans_item *tritem;
+
+       if (list_empty(&trans->item_list))
+               return NULL;
+       tritem = list_first_entry(&trans->item_list,
+                                 struct switchdev_trans_item, list);
+       list_del(&tritem->list);
+       return tritem;
+}
+
+/**
+ *     switchdev_trans_item_dequeue - Dequeue data item from transaction queue
+ *
+ *     @trans: transaction
+ */
+void *switchdev_trans_item_dequeue(struct switchdev_trans *trans)
+{
+       struct switchdev_trans_item *tritem;
+
+       tritem = __switchdev_trans_item_dequeue(trans);
+       BUG_ON(!tritem);
+       return tritem->data;
+}
+EXPORT_SYMBOL_GPL(switchdev_trans_item_dequeue);
+
+static void switchdev_trans_init(struct switchdev_trans *trans)
+{
+       INIT_LIST_HEAD(&trans->item_list);
+}
+
+static void switchdev_trans_items_destroy(struct switchdev_trans *trans)
+{
+       struct switchdev_trans_item *tritem;
+
+       while ((tritem = __switchdev_trans_item_dequeue(trans)))
+               tritem->destructor(tritem->data);
+}
+
+static void switchdev_trans_items_warn_destroy(struct net_device *dev,
+                                              struct switchdev_trans *trans)
+{
+       WARN(!list_empty(&trans->item_list), "%s: transaction item queue is not empty.\n",
+            dev->name);
+       switchdev_trans_items_destroy(trans);
+}
+
+static LIST_HEAD(deferred);
+static DEFINE_SPINLOCK(deferred_lock);
+
+typedef void switchdev_deferred_func_t(struct net_device *dev,
+                                      const void *data);
+
+struct switchdev_deferred_item {
+       struct list_head list;
+       struct net_device *dev;
+       switchdev_deferred_func_t *func;
+       unsigned long data[0];
+};
+
+static struct switchdev_deferred_item *switchdev_deferred_dequeue(void)
+{
+       struct switchdev_deferred_item *dfitem;
+
+       spin_lock_bh(&deferred_lock);
+       if (list_empty(&deferred)) {
+               dfitem = NULL;
+               goto unlock;
+       }
+       dfitem = list_first_entry(&deferred,
+                                 struct switchdev_deferred_item, list);
+       list_del(&dfitem->list);
+unlock:
+       spin_unlock_bh(&deferred_lock);
+       return dfitem;
+}
+
+/**
+ *     switchdev_deferred_process - Process ops in deferred queue
+ *
+ *     Called to flush the ops currently queued in deferred ops queue.
+ *     rtnl_lock must be held.
+ */
+void switchdev_deferred_process(void)
+{
+       struct switchdev_deferred_item *dfitem;
+
+       ASSERT_RTNL();
+
+       while ((dfitem = switchdev_deferred_dequeue())) {
+               dfitem->func(dfitem->dev, dfitem->data);
+               dev_put(dfitem->dev);
+               kfree(dfitem);
+       }
+}
+EXPORT_SYMBOL_GPL(switchdev_deferred_process);
+
+static void switchdev_deferred_process_work(struct work_struct *work)
+{
+       rtnl_lock();
+       switchdev_deferred_process();
+       rtnl_unlock();
+}
+
+static DECLARE_WORK(deferred_process_work, switchdev_deferred_process_work);
+
+static int switchdev_deferred_enqueue(struct net_device *dev,
+                                     const void *data, size_t data_len,
+                                     switchdev_deferred_func_t *func)
+{
+       struct switchdev_deferred_item *dfitem;
+
+       dfitem = kmalloc(sizeof(*dfitem) + data_len, GFP_ATOMIC);
+       if (!dfitem)
+               return -ENOMEM;
+       dfitem->dev = dev;
+       dfitem->func = func;
+       memcpy(dfitem->data, data, data_len);
+       dev_hold(dev);
+       spin_lock_bh(&deferred_lock);
+       list_add_tail(&dfitem->list, &deferred);
+       spin_unlock_bh(&deferred_lock);
+       schedule_work(&deferred_process_work);
+       return 0;
+}
+
 /**
  *     switchdev_port_attr_get - Get port attribute
  *
@@ -32,7 +186,7 @@ int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr)
        struct net_device *lower_dev;
        struct list_head *iter;
        struct switchdev_attr first = {
-               .id = SWITCHDEV_ATTR_UNDEFINED
+               .id = SWITCHDEV_ATTR_ID_UNDEFINED
        };
        int err = -EOPNOTSUPP;
 
@@ -52,7 +206,7 @@ int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr)
                err = switchdev_port_attr_get(lower_dev, attr);
                if (err)
                        break;
-               if (first.id == SWITCHDEV_ATTR_UNDEFINED)
+               if (first.id == SWITCHDEV_ATTR_ID_UNDEFINED)
                        first = *attr;
                else if (memcmp(&first, attr, sizeof(*attr)))
                        return -ENODATA;
@@ -63,18 +217,21 @@ int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr)
 EXPORT_SYMBOL_GPL(switchdev_port_attr_get);
 
 static int __switchdev_port_attr_set(struct net_device *dev,
-                                    struct switchdev_attr *attr)
+                                    const struct switchdev_attr *attr,
+                                    struct switchdev_trans *trans)
 {
        const struct switchdev_ops *ops = dev->switchdev_ops;
        struct net_device *lower_dev;
        struct list_head *iter;
        int err = -EOPNOTSUPP;
 
-       if (ops && ops->switchdev_port_attr_set)
-               return ops->switchdev_port_attr_set(dev, attr);
+       if (ops && ops->switchdev_port_attr_set) {
+               err = ops->switchdev_port_attr_set(dev, attr, trans);
+               goto done;
+       }
 
        if (attr->flags & SWITCHDEV_F_NO_RECURSE)
-               return err;
+               goto done;
 
        /* Switch device port(s) may be stacked under
         * bond/team/vlan dev, so recurse down to set attr on
@@ -82,80 +239,25 @@ static int __switchdev_port_attr_set(struct net_device *dev,
         */
 
        netdev_for_each_lower_dev(dev, lower_dev, iter) {
-               err = __switchdev_port_attr_set(lower_dev, attr);
+               err = __switchdev_port_attr_set(lower_dev, attr, trans);
                if (err)
                        break;
        }
 
-       return err;
-}
-
-struct switchdev_attr_set_work {
-       struct work_struct work;
-       struct net_device *dev;
-       struct switchdev_attr attr;
-};
-
-static void switchdev_port_attr_set_work(struct work_struct *work)
-{
-       struct switchdev_attr_set_work *asw =
-               container_of(work, struct switchdev_attr_set_work, work);
-       int err;
-
-       rtnl_lock();
-       err = switchdev_port_attr_set(asw->dev, &asw->attr);
-       if (err && err != -EOPNOTSUPP)
-               netdev_err(asw->dev, "failed (err=%d) to set attribute (id=%d)\n",
-                          err, asw->attr.id);
-       rtnl_unlock();
-
-       dev_put(asw->dev);
-       kfree(work);
-}
+done:
+       if (err == -EOPNOTSUPP && attr->flags & SWITCHDEV_F_SKIP_EOPNOTSUPP)
+               err = 0;
 
-static int switchdev_port_attr_set_defer(struct net_device *dev,
-                                        struct switchdev_attr *attr)
-{
-       struct switchdev_attr_set_work *asw;
-
-       asw = kmalloc(sizeof(*asw), GFP_ATOMIC);
-       if (!asw)
-               return -ENOMEM;
-
-       INIT_WORK(&asw->work, switchdev_port_attr_set_work);
-
-       dev_hold(dev);
-       asw->dev = dev;
-       memcpy(&asw->attr, attr, sizeof(asw->attr));
-
-       schedule_work(&asw->work);
-
-       return 0;
+       return err;
 }
 
-/**
- *     switchdev_port_attr_set - Set port attribute
- *
- *     @dev: port device
- *     @attr: attribute to set
- *
- *     Use a 2-phase prepare-commit transaction model to ensure
- *     system is not left in a partially updated state due to
- *     failure from driver/device.
- */
-int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr)
+static int switchdev_port_attr_set_now(struct net_device *dev,
+                                      const struct switchdev_attr *attr)
 {
+       struct switchdev_trans trans;
        int err;
 
-       if (!rtnl_is_locked()) {
-               /* Running prepare-commit transaction across stacked
-                * devices requires nothing moves, so if rtnl_lock is
-                * not held, schedule a worker thread to hold rtnl_lock
-                * while setting attr.
-                */
-
-               return switchdev_port_attr_set_defer(dev, attr);
-       }
+       switchdev_trans_init(&trans);
 
        /* Phase I: prepare for attr set. Driver/device should fail
         * here if there are going to be issues in the commit phase,
@@ -164,18 +266,16 @@ int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr)
         * but should not commit the attr.
         */
 
-       attr->trans = SWITCHDEV_TRANS_PREPARE;
-       err = __switchdev_port_attr_set(dev, attr);
+       trans.ph_prepare = true;
+       err = __switchdev_port_attr_set(dev, attr, &trans);
        if (err) {
                /* Prepare phase failed: abort the transaction.  Any
                 * resources reserved in the prepare phase are
                 * released.
                 */
 
-               if (err != -EOPNOTSUPP) {
-                       attr->trans = SWITCHDEV_TRANS_ABORT;
-                       __switchdev_port_attr_set(dev, attr);
-               }
+               if (err != -EOPNOTSUPP)
+                       switchdev_trans_items_destroy(&trans);
 
                return err;
        }
@@ -185,17 +285,75 @@ int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr)
         * because the driver said everythings was OK in phase I.
         */
 
-       attr->trans = SWITCHDEV_TRANS_COMMIT;
-       err = __switchdev_port_attr_set(dev, attr);
+       trans.ph_prepare = false;
+       err = __switchdev_port_attr_set(dev, attr, &trans);
        WARN(err, "%s: Commit of attribute (id=%d) failed.\n",
             dev->name, attr->id);
+       switchdev_trans_items_warn_destroy(dev, &trans);
 
        return err;
 }
+
+static void switchdev_port_attr_set_deferred(struct net_device *dev,
+                                            const void *data)
+{
+       const struct switchdev_attr *attr = data;
+       int err;
+
+       err = switchdev_port_attr_set_now(dev, attr);
+       if (err && err != -EOPNOTSUPP)
+               netdev_err(dev, "failed (err=%d) to set attribute (id=%d)\n",
+                          err, attr->id);
+}
+
+static int switchdev_port_attr_set_defer(struct net_device *dev,
+                                        const struct switchdev_attr *attr)
+{
+       return switchdev_deferred_enqueue(dev, attr, sizeof(*attr),
+                                         switchdev_port_attr_set_deferred);
+}
+
+/**
+ *     switchdev_port_attr_set - Set port attribute
+ *
+ *     @dev: port device
+ *     @attr: attribute to set
+ *
+ *     Use a 2-phase prepare-commit transaction model to ensure
+ *     system is not left in a partially updated state due to
+ *     failure from driver/device.
+ *
+ *     rtnl_lock must be held and must not be in atomic section,
+ *     in case SWITCHDEV_F_DEFER flag is not set.
+ */
+int switchdev_port_attr_set(struct net_device *dev,
+                           const struct switchdev_attr *attr)
+{
+       if (attr->flags & SWITCHDEV_F_DEFER)
+               return switchdev_port_attr_set_defer(dev, attr);
+       ASSERT_RTNL();
+       return switchdev_port_attr_set_now(dev, attr);
+}
 EXPORT_SYMBOL_GPL(switchdev_port_attr_set);
 
+static size_t switchdev_obj_size(const struct switchdev_obj *obj)
+{
+       switch (obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               return sizeof(struct switchdev_obj_port_vlan);
+       case SWITCHDEV_OBJ_ID_IPV4_FIB:
+               return sizeof(struct switchdev_obj_ipv4_fib);
+       case SWITCHDEV_OBJ_ID_PORT_FDB:
+               return sizeof(struct switchdev_obj_port_fdb);
+       default:
+               BUG();
+       }
+       return 0;
+}
+
 static int __switchdev_port_obj_add(struct net_device *dev,
-                                   struct switchdev_obj *obj)
+                                   const struct switchdev_obj *obj,
+                                   struct switchdev_trans *trans)
 {
        const struct switchdev_ops *ops = dev->switchdev_ops;
        struct net_device *lower_dev;
@@ -203,7 +361,7 @@ static int __switchdev_port_obj_add(struct net_device *dev,
        int err = -EOPNOTSUPP;
 
        if (ops && ops->switchdev_port_obj_add)
-               return ops->switchdev_port_obj_add(dev, obj);
+               return ops->switchdev_port_obj_add(dev, obj, trans);
 
        /* Switch device port(s) may be stacked under
         * bond/team/vlan dev, so recurse down to add object on
@@ -211,7 +369,7 @@ static int __switchdev_port_obj_add(struct net_device *dev,
         */
 
        netdev_for_each_lower_dev(dev, lower_dev, iter) {
-               err = __switchdev_port_obj_add(lower_dev, obj);
+               err = __switchdev_port_obj_add(lower_dev, obj, trans);
                if (err)
                        break;
        }
@@ -219,24 +377,16 @@ static int __switchdev_port_obj_add(struct net_device *dev,
        return err;
 }
 
-/**
- *     switchdev_port_obj_add - Add port object
- *
- *     @dev: port device
- *     @obj: object to add
- *
- *     Use a 2-phase prepare-commit transaction model to ensure
- *     system is not left in a partially updated state due to
- *     failure from driver/device.
- *
- *     rtnl_lock must be held.
- */
-int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj)
+static int switchdev_port_obj_add_now(struct net_device *dev,
+                                     const struct switchdev_obj *obj)
 {
+       struct switchdev_trans trans;
        int err;
 
        ASSERT_RTNL();
 
+       switchdev_trans_init(&trans);
+
        /* Phase I: prepare for obj add. Driver/device should fail
         * here if there are going to be issues in the commit phase,
         * such as lack of resources or support.  The driver/device
@@ -244,18 +394,16 @@ int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj)
         * but should not commit the obj.
         */
 
-       obj->trans = SWITCHDEV_TRANS_PREPARE;
-       err = __switchdev_port_obj_add(dev, obj);
+       trans.ph_prepare = true;
+       err = __switchdev_port_obj_add(dev, obj, &trans);
        if (err) {
                /* Prepare phase failed: abort the transaction.  Any
                 * resources reserved in the prepare phase are
                 * released.
                 */
 
-               if (err != -EOPNOTSUPP) {
-                       obj->trans = SWITCHDEV_TRANS_ABORT;
-                       __switchdev_port_obj_add(dev, obj);
-               }
+               if (err != -EOPNOTSUPP)
+                       switchdev_trans_items_destroy(&trans);
 
                return err;
        }
@@ -265,21 +413,59 @@ int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj)
         * because the driver said everythings was OK in phase I.
         */
 
-       obj->trans = SWITCHDEV_TRANS_COMMIT;
-       err = __switchdev_port_obj_add(dev, obj);
+       trans.ph_prepare = false;
+       err = __switchdev_port_obj_add(dev, obj, &trans);
        WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id);
+       switchdev_trans_items_warn_destroy(dev, &trans);
 
        return err;
 }
-EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
+
+static void switchdev_port_obj_add_deferred(struct net_device *dev,
+                                           const void *data)
+{
+       const struct switchdev_obj *obj = data;
+       int err;
+
+       err = switchdev_port_obj_add_now(dev, obj);
+       if (err && err != -EOPNOTSUPP)
+               netdev_err(dev, "failed (err=%d) to add object (id=%d)\n",
+                          err, obj->id);
+}
+
+static int switchdev_port_obj_add_defer(struct net_device *dev,
+                                       const struct switchdev_obj *obj)
+{
+       return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
+                                         switchdev_port_obj_add_deferred);
+}
 
 /**
- *     switchdev_port_obj_del - Delete port object
+ *     switchdev_port_obj_add - Add port object
  *
  *     @dev: port device
- *     @obj: object to delete
+ *     @id: object ID
+ *     @obj: object to add
+ *
+ *     Use a 2-phase prepare-commit transaction model to ensure
+ *     system is not left in a partially updated state due to
+ *     failure from driver/device.
+ *
+ *     rtnl_lock must be held and must not be in atomic section,
+ *     in case SWITCHDEV_F_DEFER flag is not set.
  */
-int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj)
+int switchdev_port_obj_add(struct net_device *dev,
+                          const struct switchdev_obj *obj)
+{
+       if (obj->flags & SWITCHDEV_F_DEFER)
+               return switchdev_port_obj_add_defer(dev, obj);
+       ASSERT_RTNL();
+       return switchdev_port_obj_add_now(dev, obj);
+}
+EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
+
+static int switchdev_port_obj_del_now(struct net_device *dev,
+                                     const struct switchdev_obj *obj)
 {
        const struct switchdev_ops *ops = dev->switchdev_ops;
        struct net_device *lower_dev;
@@ -295,30 +481,75 @@ int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj)
         */
 
        netdev_for_each_lower_dev(dev, lower_dev, iter) {
-               err = switchdev_port_obj_del(lower_dev, obj);
+               err = switchdev_port_obj_del_now(lower_dev, obj);
                if (err)
                        break;
        }
 
        return err;
 }
+
+static void switchdev_port_obj_del_deferred(struct net_device *dev,
+                                           const void *data)
+{
+       const struct switchdev_obj *obj = data;
+       int err;
+
+       err = switchdev_port_obj_del_now(dev, obj);
+       if (err && err != -EOPNOTSUPP)
+               netdev_err(dev, "failed (err=%d) to del object (id=%d)\n",
+                          err, obj->id);
+}
+
+static int switchdev_port_obj_del_defer(struct net_device *dev,
+                                       const struct switchdev_obj *obj)
+{
+       return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
+                                         switchdev_port_obj_del_deferred);
+}
+
+/**
+ *     switchdev_port_obj_del - Delete port object
+ *
+ *     @dev: port device
+ *     @id: object ID
+ *     @obj: object to delete
+ *
+ *     rtnl_lock must be held and must not be in atomic section,
+ *     in case SWITCHDEV_F_DEFER flag is not set.
+ */
+int switchdev_port_obj_del(struct net_device *dev,
+                          const struct switchdev_obj *obj)
+{
+       if (obj->flags & SWITCHDEV_F_DEFER)
+               return switchdev_port_obj_del_defer(dev, obj);
+       ASSERT_RTNL();
+       return switchdev_port_obj_del_now(dev, obj);
+}
 EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
 
 /**
  *     switchdev_port_obj_dump - Dump port objects
  *
  *     @dev: port device
+ *     @id: object ID
  *     @obj: object to dump
+ *     @cb: function to call with a filled object
+ *
+ *     rtnl_lock must be held.
  */
-int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj)
+int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj,
+                           switchdev_obj_dump_cb_t *cb)
 {
        const struct switchdev_ops *ops = dev->switchdev_ops;
        struct net_device *lower_dev;
        struct list_head *iter;
        int err = -EOPNOTSUPP;
 
+       ASSERT_RTNL();
+
        if (ops && ops->switchdev_port_obj_dump)
-               return ops->switchdev_port_obj_dump(dev, obj);
+               return ops->switchdev_port_obj_dump(dev, obj, cb);
 
        /* Switch device port(s) may be stacked under
         * bond/team/vlan dev, so recurse down to dump objects on
@@ -326,7 +557,7 @@ int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj)
         */
 
        netdev_for_each_lower_dev(dev, lower_dev, iter) {
-               err = switchdev_port_obj_dump(lower_dev, obj);
+               err = switchdev_port_obj_dump(lower_dev, obj, cb);
                break;
        }
 
@@ -398,7 +629,7 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
 EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
 
 struct switchdev_vlan_dump {
-       struct switchdev_obj obj;
+       struct switchdev_obj_port_vlan vlan;
        struct sk_buff *skb;
        u32 filter_mask;
        u16 flags;
@@ -406,8 +637,7 @@ struct switchdev_vlan_dump {
        u16 end;
 };
 
-static int switchdev_port_vlan_dump_put(struct net_device *dev,
-                                       struct switchdev_vlan_dump *dump)
+static int switchdev_port_vlan_dump_put(struct switchdev_vlan_dump *dump)
 {
        struct bridge_vlan_info vinfo;
 
@@ -437,12 +667,11 @@ static int switchdev_port_vlan_dump_put(struct net_device *dev,
        return 0;
 }
 
-static int switchdev_port_vlan_dump_cb(struct net_device *dev,
-                                      struct switchdev_obj *obj)
+static int switchdev_port_vlan_dump_cb(struct switchdev_obj *obj)
 {
+       struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
        struct switchdev_vlan_dump *dump =
-               container_of(obj, struct switchdev_vlan_dump, obj);
-       struct switchdev_obj_vlan *vlan = &dump->obj.u.vlan;
+               container_of(vlan, struct switchdev_vlan_dump, vlan);
        int err = 0;
 
        if (vlan->vid_begin > vlan->vid_end)
@@ -453,7 +682,7 @@ static int switchdev_port_vlan_dump_cb(struct net_device *dev,
                for (dump->begin = dump->end = vlan->vid_begin;
                     dump->begin <= vlan->vid_end;
                     dump->begin++, dump->end++) {
-                       err = switchdev_port_vlan_dump_put(dev, dump);
+                       err = switchdev_port_vlan_dump_put(dump);
                        if (err)
                                return err;
                }
@@ -465,7 +694,7 @@ static int switchdev_port_vlan_dump_cb(struct net_device *dev,
                                /* prepend */
                                dump->begin = vlan->vid_begin;
                        } else {
-                               err = switchdev_port_vlan_dump_put(dev, dump);
+                               err = switchdev_port_vlan_dump_put(dump);
                                dump->flags = vlan->flags;
                                dump->begin = vlan->vid_begin;
                                dump->end = vlan->vid_end;
@@ -477,7 +706,7 @@ static int switchdev_port_vlan_dump_cb(struct net_device *dev,
                                /* append */
                                dump->end = vlan->vid_end;
                        } else {
-                               err = switchdev_port_vlan_dump_put(dev, dump);
+                               err = switchdev_port_vlan_dump_put(dump);
                                dump->flags = vlan->flags;
                                dump->begin = vlan->vid_begin;
                                dump->end = vlan->vid_end;
@@ -494,10 +723,7 @@ static int switchdev_port_vlan_fill(struct sk_buff *skb, struct net_device *dev,
                                    u32 filter_mask)
 {
        struct switchdev_vlan_dump dump = {
-               .obj = {
-                       .id = SWITCHDEV_OBJ_PORT_VLAN,
-                       .cb = switchdev_port_vlan_dump_cb,
-               },
+               .vlan.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
                .skb = skb,
                .filter_mask = filter_mask,
        };
@@ -505,12 +731,13 @@ static int switchdev_port_vlan_fill(struct sk_buff *skb, struct net_device *dev,
 
        if ((filter_mask & RTEXT_FILTER_BRVLAN) ||
            (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) {
-               err = switchdev_port_obj_dump(dev, &dump.obj);
+               err = switchdev_port_obj_dump(dev, &dump.vlan.obj,
+                                             switchdev_port_vlan_dump_cb);
                if (err)
                        goto err_out;
                if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
                        /* last one */
-                       err = switchdev_port_vlan_dump_put(dev, &dump);
+                       err = switchdev_port_vlan_dump_put(&dump);
        }
 
 err_out:
@@ -530,10 +757,10 @@ int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
                                  int nlflags)
 {
        struct switchdev_attr attr = {
-               .id = SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS,
+               .id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
        };
        u16 mode = BRIDGE_MODE_UNDEF;
-       u32 mask = BR_LEARNING | BR_LEARNING_SYNC;
+       u32 mask = BR_LEARNING | BR_LEARNING_SYNC | BR_FLOOD;
        int err;
 
        err = switchdev_port_attr_get(dev, &attr);
@@ -551,7 +778,7 @@ static int switchdev_port_br_setflag(struct net_device *dev,
                                     unsigned long brport_flag)
 {
        struct switchdev_attr attr = {
-               .id = SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS,
+               .id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
        };
        u8 flag = nla_get_u8(nlattr);
        int err;
@@ -604,6 +831,9 @@ static int switchdev_port_br_setlink_protinfo(struct net_device *dev,
                        err = switchdev_port_br_setflag(dev, attr,
                                                        BR_LEARNING_SYNC);
                        break;
+               case IFLA_BRPORT_UNICAST_FLOOD:
+                       err = switchdev_port_br_setflag(dev, attr, BR_FLOOD);
+                       break;
                default:
                        err = -EOPNOTSUPP;
                        break;
@@ -618,14 +848,13 @@ static int switchdev_port_br_setlink_protinfo(struct net_device *dev,
 static int switchdev_port_br_afspec(struct net_device *dev,
                                    struct nlattr *afspec,
                                    int (*f)(struct net_device *dev,
-                                            struct switchdev_obj *obj))
+                                            const struct switchdev_obj *obj))
 {
        struct nlattr *attr;
        struct bridge_vlan_info *vinfo;
-       struct switchdev_obj obj = {
-               .id = SWITCHDEV_OBJ_PORT_VLAN,
+       struct switchdev_obj_port_vlan vlan = {
+               .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
        };
-       struct switchdev_obj_vlan *vlan = &obj.u.vlan;
        int rem;
        int err;
 
@@ -637,30 +866,33 @@ static int switchdev_port_br_afspec(struct net_device *dev,
                vinfo = nla_data(attr);
                if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK)
                        return -EINVAL;
-               vlan->flags = vinfo->flags;
+               vlan.flags = vinfo->flags;
                if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
-                       if (vlan->vid_begin)
+                       if (vlan.vid_begin)
+                               return -EINVAL;
+                       vlan.vid_begin = vinfo->vid;
+                       /* don't allow range of pvids */
+                       if (vlan.flags & BRIDGE_VLAN_INFO_PVID)
                                return -EINVAL;
-                       vlan->vid_begin = vinfo->vid;
                } else if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) {
-                       if (!vlan->vid_begin)
+                       if (!vlan.vid_begin)
                                return -EINVAL;
-                       vlan->vid_end = vinfo->vid;
-                       if (vlan->vid_end <= vlan->vid_begin)
+                       vlan.vid_end = vinfo->vid;
+                       if (vlan.vid_end <= vlan.vid_begin)
                                return -EINVAL;
-                       err = f(dev, &obj);
+                       err = f(dev, &vlan.obj);
                        if (err)
                                return err;
-                       memset(vlan, 0, sizeof(*vlan));
+                       vlan.vid_begin = 0;
                } else {
-                       if (vlan->vid_begin)
+                       if (vlan.vid_begin)
                                return -EINVAL;
-                       vlan->vid_begin = vinfo->vid;
-                       vlan->vid_end = vinfo->vid;
-                       err = f(dev, &obj);
+                       vlan.vid_begin = vinfo->vid;
+                       vlan.vid_end = vinfo->vid;
+                       err = f(dev, &vlan.obj);
                        if (err)
                                return err;
-                       memset(vlan, 0, sizeof(*vlan));
+                       vlan.vid_begin = 0;
                }
        }
 
@@ -742,15 +974,13 @@ int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
                           struct net_device *dev, const unsigned char *addr,
                           u16 vid, u16 nlm_flags)
 {
-       struct switchdev_obj obj = {
-               .id = SWITCHDEV_OBJ_PORT_FDB,
-               .u.fdb = {
-                       .addr = addr,
-                       .vid = vid,
-               },
+       struct switchdev_obj_port_fdb fdb = {
+               .obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
+               .vid = vid,
        };
 
-       return switchdev_port_obj_add(dev, &obj);
+       ether_addr_copy(fdb.addr, addr);
+       return switchdev_port_obj_add(dev, &fdb.obj);
 }
 EXPORT_SYMBOL_GPL(switchdev_port_fdb_add);
 
@@ -769,30 +999,29 @@ int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
                           struct net_device *dev, const unsigned char *addr,
                           u16 vid)
 {
-       struct switchdev_obj obj = {
-               .id = SWITCHDEV_OBJ_PORT_FDB,
-               .u.fdb = {
-                       .addr = addr,
-                       .vid = vid,
-               },
+       struct switchdev_obj_port_fdb fdb = {
+               .obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
+               .vid = vid,
        };
 
-       return switchdev_port_obj_del(dev, &obj);
+       ether_addr_copy(fdb.addr, addr);
+       return switchdev_port_obj_del(dev, &fdb.obj);
 }
 EXPORT_SYMBOL_GPL(switchdev_port_fdb_del);
 
 struct switchdev_fdb_dump {
-       struct switchdev_obj obj;
+       struct switchdev_obj_port_fdb fdb;
+       struct net_device *dev;
        struct sk_buff *skb;
        struct netlink_callback *cb;
        int idx;
 };
 
-static int switchdev_port_fdb_dump_cb(struct net_device *dev,
-                                     struct switchdev_obj *obj)
+static int switchdev_port_fdb_dump_cb(struct switchdev_obj *obj)
 {
+       struct switchdev_obj_port_fdb *fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
        struct switchdev_fdb_dump *dump =
-               container_of(obj, struct switchdev_fdb_dump, obj);
+               container_of(fdb, struct switchdev_fdb_dump, fdb);
        u32 portid = NETLINK_CB(dump->cb->skb).portid;
        u32 seq = dump->cb->nlh->nlmsg_seq;
        struct nlmsghdr *nlh;
@@ -812,13 +1041,13 @@ static int switchdev_port_fdb_dump_cb(struct net_device *dev,
        ndm->ndm_pad2    = 0;
        ndm->ndm_flags   = NTF_SELF;
        ndm->ndm_type    = 0;
-       ndm->ndm_ifindex = dev->ifindex;
-       ndm->ndm_state   = obj->u.fdb.ndm_state;
+       ndm->ndm_ifindex = dump->dev->ifindex;
+       ndm->ndm_state   = fdb->ndm_state;
 
-       if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, obj->u.fdb.addr))
+       if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, fdb->addr))
                goto nla_put_failure;
 
-       if (obj->u.fdb.vid && nla_put_u16(dump->skb, NDA_VLAN, obj->u.fdb.vid))
+       if (fdb->vid && nla_put_u16(dump->skb, NDA_VLAN, fdb->vid))
                goto nla_put_failure;
 
        nlmsg_end(dump->skb, nlh);
@@ -848,16 +1077,14 @@ int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
                            struct net_device *filter_dev, int idx)
 {
        struct switchdev_fdb_dump dump = {
-               .obj = {
-                       .id = SWITCHDEV_OBJ_PORT_FDB,
-                       .cb = switchdev_port_fdb_dump_cb,
-               },
+               .fdb.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
+               .dev = dev,
                .skb = skb,
                .cb = cb,
                .idx = idx,
        };
 
-       switchdev_port_obj_dump(dev, &dump.obj);
+       switchdev_port_obj_dump(dev, &dump.fdb.obj, switchdev_port_fdb_dump_cb);
        return dump.idx;
 }
 EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump);
@@ -888,12 +1115,14 @@ static struct net_device *switchdev_get_lowest_dev(struct net_device *dev)
 static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi)
 {
        struct switchdev_attr attr = {
-               .id = SWITCHDEV_ATTR_PORT_PARENT_ID,
+               .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
        };
        struct switchdev_attr prev_attr;
        struct net_device *dev = NULL;
        int nhsel;
 
+       ASSERT_RTNL();
+
        /* For this route, all nexthop devs must be on the same switch. */
 
        for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
@@ -935,21 +1164,20 @@ static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi)
 int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
                           u8 tos, u8 type, u32 nlflags, u32 tb_id)
 {
-       struct switchdev_obj fib_obj = {
-               .id = SWITCHDEV_OBJ_IPV4_FIB,
-               .u.ipv4_fib = {
-                       .dst = dst,
-                       .dst_len = dst_len,
-                       .fi = fi,
-                       .tos = tos,
-                       .type = type,
-                       .nlflags = nlflags,
-                       .tb_id = tb_id,
-               },
+       struct switchdev_obj_ipv4_fib ipv4_fib = {
+               .obj.id = SWITCHDEV_OBJ_ID_IPV4_FIB,
+               .dst = dst,
+               .dst_len = dst_len,
+               .tos = tos,
+               .type = type,
+               .nlflags = nlflags,
+               .tb_id = tb_id,
        };
        struct net_device *dev;
        int err = 0;
 
+       memcpy(&ipv4_fib.fi, fi, sizeof(ipv4_fib.fi));
+
        /* Don't offload route if using custom ip rules or if
         * IPv4 FIB offloading has been disabled completely.
         */
@@ -966,7 +1194,7 @@ int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
        if (!dev)
                return 0;
 
-       err = switchdev_port_obj_add(dev, &fib_obj);
+       err = switchdev_port_obj_add(dev, &ipv4_fib.obj);
        if (!err)
                fi->fib_flags |= RTNH_F_OFFLOAD;
 
@@ -989,21 +1217,20 @@ EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_add);
 int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
                           u8 tos, u8 type, u32 tb_id)
 {
-       struct switchdev_obj fib_obj = {
-               .id = SWITCHDEV_OBJ_IPV4_FIB,
-               .u.ipv4_fib = {
-                       .dst = dst,
-                       .dst_len = dst_len,
-                       .fi = fi,
-                       .tos = tos,
-                       .type = type,
-                       .nlflags = 0,
-                       .tb_id = tb_id,
-               },
+       struct switchdev_obj_ipv4_fib ipv4_fib = {
+               .obj.id = SWITCHDEV_OBJ_ID_IPV4_FIB,
+               .dst = dst,
+               .dst_len = dst_len,
+               .tos = tos,
+               .type = type,
+               .nlflags = 0,
+               .tb_id = tb_id,
        };
        struct net_device *dev;
        int err = 0;
 
+       memcpy(&ipv4_fib.fi, fi, sizeof(ipv4_fib.fi));
+
        if (!(fi->fib_flags & RTNH_F_OFFLOAD))
                return 0;
 
@@ -1011,7 +1238,7 @@ int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
        if (!dev)
                return 0;
 
-       err = switchdev_port_obj_del(dev, &fib_obj);
+       err = switchdev_port_obj_del(dev, &ipv4_fib.obj);
        if (!err)
                fi->fib_flags &= ~RTNH_F_OFFLOAD;
 
@@ -1043,11 +1270,11 @@ static bool switchdev_port_same_parent_id(struct net_device *a,
                                          struct net_device *b)
 {
        struct switchdev_attr a_attr = {
-               .id = SWITCHDEV_ATTR_PORT_PARENT_ID,
+               .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
                .flags = SWITCHDEV_F_NO_RECURSE,
        };
        struct switchdev_attr b_attr = {
-               .id = SWITCHDEV_ATTR_PORT_PARENT_ID,
+               .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
                .flags = SWITCHDEV_F_NO_RECURSE,
        };
 
@@ -1126,10 +1353,11 @@ void switchdev_port_fwd_mark_set(struct net_device *dev,
        u32 mark = dev->ifindex;
        u32 reset_mark = 0;
 
-       if (group_dev && joining) {
-               mark = switchdev_port_fwd_mark_get(dev, group_dev);
-       } else if (group_dev && !joining) {
-               if (dev->offload_fwd_mark == mark)
+       if (group_dev) {
+               ASSERT_RTNL();
+               if (joining)
+                       mark = switchdev_port_fwd_mark_get(dev, group_dev);
+               else if (dev->offload_fwd_mark == mark)
                        /* Ohoh, this port was the mark reference port,
                         * but it's leaving the group, so reset the
                         * mark for the remaining ports in the group.
index e7000be321b0148469264524ed6fce75c3952955..ed98c1fc3de1428560ea370413084102af9dff7f 100644 (file)
@@ -94,10 +94,14 @@ __init int net_sysctl_init(void)
                goto out;
        ret = register_pernet_subsys(&sysctl_pernet_ops);
        if (ret)
-               goto out;
+               goto out1;
        register_sysctl_root(&net_sysctl_root);
 out:
        return ret;
+out1:
+       unregister_sysctl_table(net_header);
+       net_header = NULL;
+       goto out;
 }
 
 struct ctl_table_header *register_net_sysctl(struct net *net,
index 41042de3ae9bcfad4504e0bcbb29d3bea4512bb5..9dc239dfe19211f2a6367f9581bf79527f73af31 100644 (file)
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <linux/tipc_config.h>
 #include "socket.h"
 #include "msg.h"
 #include "bcast.h"
 #include "name_distr.h"
-#include "core.h"
+#include "link.h"
+#include "node.h"
 
-#define        MAX_PKT_DEFAULT_MCAST   1500    /* bcast link max packet size (fixed) */
-#define        BCLINK_WIN_DEFAULT      20      /* bcast link window size (default) */
+#define        BCLINK_WIN_DEFAULT      50      /* bcast link window size (default) */
+#define        BCLINK_WIN_MIN          32      /* bcast minimum link window size */
 
 const char tipc_bclink_name[] = "broadcast-link";
 
-static void tipc_nmap_diff(struct tipc_node_map *nm_a,
-                          struct tipc_node_map *nm_b,
-                          struct tipc_node_map *nm_diff);
-static void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node);
-static void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node);
-
-static void tipc_bclink_lock(struct net *net)
-{
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-
-       spin_lock_bh(&tn->bclink->lock);
-}
-
-static void tipc_bclink_unlock(struct net *net)
-{
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-
-       spin_unlock_bh(&tn->bclink->lock);
-}
-
-void tipc_bclink_input(struct net *net)
-{
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-
-       tipc_sk_mcast_rcv(net, &tn->bclink->arrvq, &tn->bclink->inputq);
-}
-
-uint  tipc_bclink_get_mtu(void)
-{
-       return MAX_PKT_DEFAULT_MCAST;
-}
-
-static u32 bcbuf_acks(struct sk_buff *buf)
-{
-       return (u32)(unsigned long)TIPC_SKB_CB(buf)->handle;
-}
-
-static void bcbuf_set_acks(struct sk_buff *buf, u32 acks)
-{
-       TIPC_SKB_CB(buf)->handle = (void *)(unsigned long)acks;
-}
-
-static void bcbuf_decr_acks(struct sk_buff *buf)
-{
-       bcbuf_set_acks(buf, bcbuf_acks(buf) - 1);
-}
+/**
+ * struct tipc_bc_base - base structure for keeping broadcast send state
+ * @link: broadcast send link structure
+ * @inputq: data input queue; will only carry SOCK_WAKEUP messages
+ * @dest: array keeping number of reachable destinations per bearer
+ * @primary_bearer: a bearer having links to all broadcast destinations, if any
+ */
+struct tipc_bc_base {
+       struct tipc_link *link;
+       struct sk_buff_head inputq;
+       int dests[MAX_BEARERS];
+       int primary_bearer;
+};
 
-void tipc_bclink_add_node(struct net *net, u32 addr)
+static struct tipc_bc_base *tipc_bc_base(struct net *net)
 {
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-
-       tipc_bclink_lock(net);
-       tipc_nmap_add(&tn->bclink->bcast_nodes, addr);
-       tipc_bclink_unlock(net);
+       return tipc_net(net)->bcbase;
 }
 
-void tipc_bclink_remove_node(struct net *net, u32 addr)
+int tipc_bcast_get_mtu(struct net *net)
 {
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-
-       tipc_bclink_lock(net);
-       tipc_nmap_remove(&tn->bclink->bcast_nodes, addr);
-
-       /* Last node? => reset backlog queue */
-       if (!tn->bclink->bcast_nodes.count)
-               tipc_link_purge_backlog(&tn->bclink->link);
-
-       tipc_bclink_unlock(net);
+       return tipc_link_mtu(tipc_bc_sndlink(net));
 }
 
-static void bclink_set_last_sent(struct net *net)
+/* tipc_bcbase_select_primary(): find a bearer with links to all destinations,
+ *                               if any, and make it primary bearer
+ */
+static void tipc_bcbase_select_primary(struct net *net)
 {
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_link *bcl = tn->bcl;
+       struct tipc_bc_base *bb = tipc_bc_base(net);
+       int all_dests =  tipc_link_bc_peers(bb->link);
+       int i, mtu;
 
-       bcl->silent_intv_cnt = mod(bcl->snd_nxt - 1);
-}
+       bb->primary_bearer = INVALID_BEARER_ID;
 
-u32 tipc_bclink_get_last_sent(struct net *net)
-{
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
+       if (!all_dests)
+               return;
 
-       return tn->bcl->silent_intv_cnt;
-}
+       for (i = 0; i < MAX_BEARERS; i++) {
+               if (!bb->dests[i])
+                       continue;
 
-static void bclink_update_last_sent(struct tipc_node *node, u32 seqno)
-{
-       node->bclink.last_sent = less_eq(node->bclink.last_sent, seqno) ?
-                                               seqno : node->bclink.last_sent;
-}
+               mtu = tipc_bearer_mtu(net, i);
+               if (mtu < tipc_link_mtu(bb->link))
+                       tipc_link_set_mtu(bb->link, mtu);
 
-/**
- * tipc_bclink_retransmit_to - get most recent node to request retransmission
- *
- * Called with bclink_lock locked
- */
-struct tipc_node *tipc_bclink_retransmit_to(struct net *net)
-{
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-
-       return tn->bclink->retransmit_to;
-}
+               if (bb->dests[i] < all_dests)
+                       continue;
 
-/**
- * bclink_retransmit_pkt - retransmit broadcast packets
- * @after: sequence number of last packet to *not* retransmit
- * @to: sequence number of last packet to retransmit
- *
- * Called with bclink_lock locked
- */
-static void bclink_retransmit_pkt(struct tipc_net *tn, u32 after, u32 to)
-{
-       struct sk_buff *skb;
-       struct tipc_link *bcl = tn->bcl;
+               bb->primary_bearer = i;
 
-       skb_queue_walk(&bcl->transmq, skb) {
-               if (more(buf_seqno(skb), after)) {
-                       tipc_link_retransmit(bcl, skb, mod(to - after));
+               /* Reduce risk that all nodes select same primary */
+               if ((i ^ tipc_own_addr(net)) & 1)
                        break;
-               }
        }
 }
 
-/**
- * bclink_prepare_wakeup - prepare users for wakeup after congestion
- * @bcl: broadcast link
- * @resultq: queue for users which can be woken up
- * Move a number of waiting users, as permitted by available space in
- * the send queue, from link wait queue to specified queue for wakeup
- */
-static void bclink_prepare_wakeup(struct tipc_link *bcl, struct sk_buff_head *resultq)
+void tipc_bcast_inc_bearer_dst_cnt(struct net *net, int bearer_id)
 {
-       int pnd[TIPC_SYSTEM_IMPORTANCE + 1] = {0,};
-       int imp, lim;
-       struct sk_buff *skb, *tmp;
-
-       skb_queue_walk_safe(&bcl->wakeupq, skb, tmp) {
-               imp = TIPC_SKB_CB(skb)->chain_imp;
-               lim = bcl->window + bcl->backlog[imp].limit;
-               pnd[imp] += TIPC_SKB_CB(skb)->chain_sz;
-               if ((pnd[imp] + bcl->backlog[imp].len) >= lim)
-                       continue;
-               skb_unlink(skb, &bcl->wakeupq);
-               skb_queue_tail(resultq, skb);
-       }
-}
+       struct tipc_bc_base *bb = tipc_bc_base(net);
 
-/**
- * tipc_bclink_wakeup_users - wake up pending users
- *
- * Called with no locks taken
- */
-void tipc_bclink_wakeup_users(struct net *net)
-{
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_link *bcl = tn->bcl;
-       struct sk_buff_head resultq;
-
-       skb_queue_head_init(&resultq);
-       bclink_prepare_wakeup(bcl, &resultq);
-       tipc_sk_rcv(net, &resultq);
+       tipc_bcast_lock(net);
+       bb->dests[bearer_id]++;
+       tipc_bcbase_select_primary(net);
+       tipc_bcast_unlock(net);
 }
 
-/**
- * tipc_bclink_acknowledge - handle acknowledgement of broadcast packets
- * @n_ptr: node that sent acknowledgement info
- * @acked: broadcast sequence # that has been acknowledged
- *
- * Node is locked, bclink_lock unlocked.
- */
-void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)
+void tipc_bcast_dec_bearer_dst_cnt(struct net *net, int bearer_id)
 {
-       struct sk_buff *skb, *tmp;
-       unsigned int released = 0;
-       struct net *net = n_ptr->net;
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-
-       if (unlikely(!n_ptr->bclink.recv_permitted))
-               return;
-
-       tipc_bclink_lock(net);
-
-       /* Bail out if tx queue is empty (no clean up is required) */
-       skb = skb_peek(&tn->bcl->transmq);
-       if (!skb)
-               goto exit;
-
-       /* Determine which messages need to be acknowledged */
-       if (acked == INVALID_LINK_SEQ) {
-               /*
-                * Contact with specified node has been lost, so need to
-                * acknowledge sent messages only (if other nodes still exist)
-                * or both sent and unsent messages (otherwise)
-                */
-               if (tn->bclink->bcast_nodes.count)
-                       acked = tn->bcl->silent_intv_cnt;
-               else
-                       acked = tn->bcl->snd_nxt;
-       } else {
-               /*
-                * Bail out if specified sequence number does not correspond
-                * to a message that has been sent and not yet acknowledged
-                */
-               if (less(acked, buf_seqno(skb)) ||
-                   less(tn->bcl->silent_intv_cnt, acked) ||
-                   less_eq(acked, n_ptr->bclink.acked))
-                       goto exit;
-       }
-
-       /* Skip over packets that node has previously acknowledged */
-       skb_queue_walk(&tn->bcl->transmq, skb) {
-               if (more(buf_seqno(skb), n_ptr->bclink.acked))
-                       break;
-       }
-
-       /* Update packets that node is now acknowledging */
-       skb_queue_walk_from_safe(&tn->bcl->transmq, skb, tmp) {
-               if (more(buf_seqno(skb), acked))
-                       break;
-               bcbuf_decr_acks(skb);
-               bclink_set_last_sent(net);
-               if (bcbuf_acks(skb) == 0) {
-                       __skb_unlink(skb, &tn->bcl->transmq);
-                       kfree_skb(skb);
-                       released = 1;
-               }
-       }
-       n_ptr->bclink.acked = acked;
+       struct tipc_bc_base *bb = tipc_bc_base(net);
 
-       /* Try resolving broadcast link congestion, if necessary */
-       if (unlikely(skb_peek(&tn->bcl->backlogq))) {
-               tipc_link_push_packets(tn->bcl);
-               bclink_set_last_sent(net);
-       }
-       if (unlikely(released && !skb_queue_empty(&tn->bcl->wakeupq)))
-               n_ptr->action_flags |= TIPC_WAKEUP_BCAST_USERS;
-exit:
-       tipc_bclink_unlock(net);
+       tipc_bcast_lock(net);
+       bb->dests[bearer_id]--;
+       tipc_bcbase_select_primary(net);
+       tipc_bcast_unlock(net);
 }
 
-/**
- * tipc_bclink_update_link_state - update broadcast link state
+/* tipc_bcbase_xmit - broadcast a packet queue across one or more bearers
  *
- * RCU and node lock set
+ * Note that number of reachable destinations, as indicated in the dests[]
+ * array, may transitionally differ from the number of destinations indicated
+ * in each sent buffer. We can sustain this. Excess destination nodes will
+ * drop and never acknowledge the unexpected packets, and missing destinations
+ * will either require retransmission (if they are just about to be added to
+ * the bearer), or be removed from the buffer's 'ackers' counter (if they
+ * just went down)
  */
-void tipc_bclink_update_link_state(struct tipc_node *n_ptr,
-                                  u32 last_sent)
+static void tipc_bcbase_xmit(struct net *net, struct sk_buff_head *xmitq)
 {
-       struct sk_buff *buf;
-       struct net *net = n_ptr->net;
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
+       int bearer_id;
+       struct tipc_bc_base *bb = tipc_bc_base(net);
+       struct sk_buff *skb, *_skb;
+       struct sk_buff_head _xmitq;
 
-       /* Ignore "stale" link state info */
-       if (less_eq(last_sent, n_ptr->bclink.last_in))
+       if (skb_queue_empty(xmitq))
                return;
 
-       /* Update link synchronization state; quit if in sync */
-       bclink_update_last_sent(n_ptr, last_sent);
-
-       if (n_ptr->bclink.last_sent == n_ptr->bclink.last_in)
+       /* The typical case: at least one bearer has links to all nodes */
+       bearer_id = bb->primary_bearer;
+       if (bearer_id >= 0) {
+               tipc_bearer_bc_xmit(net, bearer_id, xmitq);
                return;
-
-       /* Update out-of-sync state; quit if loss is still unconfirmed */
-       if ((++n_ptr->bclink.oos_state) == 1) {
-               if (n_ptr->bclink.deferred_size < (TIPC_MIN_LINK_WIN / 2))
-                       return;
-               n_ptr->bclink.oos_state++;
        }
 
-       /* Don't NACK if one has been recently sent (or seen) */
-       if (n_ptr->bclink.oos_state & 0x1)
-               return;
-
-       /* Send NACK */
-       buf = tipc_buf_acquire(INT_H_SIZE);
-       if (buf) {
-               struct tipc_msg *msg = buf_msg(buf);
-               struct sk_buff *skb = skb_peek(&n_ptr->bclink.deferdq);
-               u32 to = skb ? buf_seqno(skb) - 1 : n_ptr->bclink.last_sent;
-
-               tipc_msg_init(tn->own_addr, msg, BCAST_PROTOCOL, STATE_MSG,
-                             INT_H_SIZE, n_ptr->addr);
-               msg_set_non_seq(msg, 1);
-               msg_set_mc_netid(msg, tn->net_id);
-               msg_set_bcast_ack(msg, n_ptr->bclink.last_in);
-               msg_set_bcgap_after(msg, n_ptr->bclink.last_in);
-               msg_set_bcgap_to(msg, to);
-
-               tipc_bclink_lock(net);
-               tipc_bearer_send(net, MAX_BEARERS, buf, NULL);
-               tn->bcl->stats.sent_nacks++;
-               tipc_bclink_unlock(net);
-               kfree_skb(buf);
-
-               n_ptr->bclink.oos_state++;
-       }
-}
-
-void tipc_bclink_sync_state(struct tipc_node *n, struct tipc_msg *hdr)
-{
-       u16 last = msg_last_bcast(hdr);
-       int mtyp = msg_type(hdr);
+       /* We have to transmit across all bearers */
+       skb_queue_head_init(&_xmitq);
+       for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) {
+               if (!bb->dests[bearer_id])
+                       continue;
 
-       if (unlikely(msg_user(hdr) != LINK_PROTOCOL))
-               return;
-       if (mtyp == STATE_MSG) {
-               tipc_bclink_update_link_state(n, last);
-               return;
+               skb_queue_walk(xmitq, skb) {
+                       _skb = pskb_copy_for_clone(skb, GFP_ATOMIC);
+                       if (!_skb)
+                               break;
+                       __skb_queue_tail(&_xmitq, _skb);
+               }
+               tipc_bearer_bc_xmit(net, bearer_id, &_xmitq);
        }
-       /* Compatibility: older nodes don't know BCAST_PROTOCOL synchronization,
-        * and transfer synch info in LINK_PROTOCOL messages.
-        */
-       if (tipc_node_is_up(n))
-               return;
-       if ((mtyp != RESET_MSG) && (mtyp != ACTIVATE_MSG))
-               return;
-       n->bclink.last_sent = last;
-       n->bclink.last_in = last;
-       n->bclink.oos_state = 0;
+       __skb_queue_purge(xmitq);
+       __skb_queue_purge(&_xmitq);
 }
 
-/**
- * bclink_peek_nack - monitor retransmission requests sent by other nodes
- *
- * Delay any upcoming NACK by this node if another node has already
- * requested the first message this node is going to ask for.
- */
-static void bclink_peek_nack(struct net *net, struct tipc_msg *msg)
-{
-       struct tipc_node *n_ptr = tipc_node_find(net, msg_destnode(msg));
-
-       if (unlikely(!n_ptr))
-               return;
-
-       tipc_node_lock(n_ptr);
-       if (n_ptr->bclink.recv_permitted &&
-           (n_ptr->bclink.last_in != n_ptr->bclink.last_sent) &&
-           (n_ptr->bclink.last_in == msg_bcgap_after(msg)))
-               n_ptr->bclink.oos_state = 2;
-       tipc_node_unlock(n_ptr);
-       tipc_node_put(n_ptr);
-}
-
-/* tipc_bclink_xmit - deliver buffer chain to all nodes in cluster
+/* tipc_bcast_xmit - deliver buffer chain to all nodes in cluster
  *                    and to identified node local sockets
  * @net: the applicable net namespace
  * @list: chain of buffers containing message
  * Consumes the buffer chain, except when returning -ELINKCONG
  * Returns 0 if success, otherwise errno: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE
  */
-int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list)
+int tipc_bcast_xmit(struct net *net, struct sk_buff_head *list)
 {
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_link *bcl = tn->bcl;
-       struct tipc_bclink *bclink = tn->bclink;
+       struct tipc_link *l = tipc_bc_sndlink(net);
+       struct sk_buff_head xmitq, inputq, rcvq;
        int rc = 0;
-       int bc = 0;
-       struct sk_buff *skb;
-       struct sk_buff_head arrvq;
-       struct sk_buff_head inputq;
 
-       /* Prepare clone of message for local node */
-       skb = tipc_msg_reassemble(list);
-       if (unlikely(!skb))
-               return -EHOSTUNREACH;
+       __skb_queue_head_init(&rcvq);
+       __skb_queue_head_init(&xmitq);
+       skb_queue_head_init(&inputq);
 
-       /* Broadcast to all nodes */
-       if (likely(bclink)) {
-               tipc_bclink_lock(net);
-               if (likely(bclink->bcast_nodes.count)) {
-                       rc = __tipc_link_xmit(net, bcl, list);
-                       if (likely(!rc)) {
-                               u32 len = skb_queue_len(&bcl->transmq);
-
-                               bclink_set_last_sent(net);
-                               bcl->stats.queue_sz_counts++;
-                               bcl->stats.accu_queue_sz += len;
-                       }
-                       bc = 1;
-               }
-               tipc_bclink_unlock(net);
-       }
+       /* Prepare message clone for local node */
+       if (unlikely(!tipc_msg_reassemble(list, &rcvq)))
+               return -EHOSTUNREACH;
 
-       if (unlikely(!bc))
-               __skb_queue_purge(list);
+       tipc_bcast_lock(net);
+       if (tipc_link_bc_peers(l))
+               rc = tipc_link_xmit(l, list, &xmitq);
+       tipc_bcast_unlock(net);
 
+       /* Don't send to local node if adding to link failed */
        if (unlikely(rc)) {
-               kfree_skb(skb);
+               __skb_queue_purge(&rcvq);
                return rc;
        }
-       /* Deliver message clone */
-       __skb_queue_head_init(&arrvq);
-       skb_queue_head_init(&inputq);
-       __skb_queue_tail(&arrvq, skb);
-       tipc_sk_mcast_rcv(net, &arrvq, &inputq);
-       return rc;
-}
 
-/**
- * bclink_accept_pkt - accept an incoming, in-sequence broadcast packet
- *
- * Called with both sending node's lock and bclink_lock taken.
- */
-static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)
-{
-       struct tipc_net *tn = net_generic(node->net, tipc_net_id);
-
-       bclink_update_last_sent(node, seqno);
-       node->bclink.last_in = seqno;
-       node->bclink.oos_state = 0;
-       tn->bcl->stats.recv_info++;
-
-       /*
-        * Unicast an ACK periodically, ensuring that
-        * all nodes in the cluster don't ACK at the same time
-        */
-       if (((seqno - tn->own_addr) % TIPC_MIN_LINK_WIN) == 0) {
-               tipc_link_proto_xmit(node_active_link(node, node->addr),
-                                    STATE_MSG, 0, 0, 0, 0);
-               tn->bcl->stats.sent_acks++;
-       }
+       /* Broadcast to all nodes, inluding local node */
+       tipc_bcbase_xmit(net, &xmitq);
+       tipc_sk_mcast_rcv(net, &rcvq, &inputq);
+       __skb_queue_purge(list);
+       return 0;
 }
 
-/**
- * tipc_bclink_rcv - receive a broadcast packet, and deliver upwards
+/* tipc_bcast_rcv - receive a broadcast packet, and deliver to rcv link
  *
  * RCU is locked, no other locks set
  */
-void tipc_bclink_rcv(struct net *net, struct sk_buff *buf)
+int tipc_bcast_rcv(struct net *net, struct tipc_link *l, struct sk_buff *skb)
 {
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_link *bcl = tn->bcl;
-       struct tipc_msg *msg = buf_msg(buf);
-       struct tipc_node *node;
-       u32 next_in;
-       u32 seqno;
-       int deferred = 0;
-       int pos = 0;
-       struct sk_buff *iskb;
-       struct sk_buff_head *arrvq, *inputq;
-
-       /* Screen out unwanted broadcast messages */
-       if (msg_mc_netid(msg) != tn->net_id)
-               goto exit;
-
-       node = tipc_node_find(net, msg_prevnode(msg));
-       if (unlikely(!node))
-               goto exit;
-
-       tipc_node_lock(node);
-       if (unlikely(!node->bclink.recv_permitted))
-               goto unlock;
-
-       /* Handle broadcast protocol message */
-       if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) {
-               if (msg_type(msg) != STATE_MSG)
-                       goto unlock;
-               if (msg_destnode(msg) == tn->own_addr) {
-                       tipc_bclink_acknowledge(node, msg_bcast_ack(msg));
-                       tipc_bclink_lock(net);
-                       bcl->stats.recv_nacks++;
-                       tn->bclink->retransmit_to = node;
-                       bclink_retransmit_pkt(tn, msg_bcgap_after(msg),
-                                             msg_bcgap_to(msg));
-                       tipc_bclink_unlock(net);
-                       tipc_node_unlock(node);
-               } else {
-                       tipc_node_unlock(node);
-                       bclink_peek_nack(net, msg);
-               }
-               tipc_node_put(node);
-               goto exit;
-       }
-
-       /* Handle in-sequence broadcast message */
-       seqno = msg_seqno(msg);
-       next_in = mod(node->bclink.last_in + 1);
-       arrvq = &tn->bclink->arrvq;
-       inputq = &tn->bclink->inputq;
-
-       if (likely(seqno == next_in)) {
-receive:
-               /* Deliver message to destination */
-               if (likely(msg_isdata(msg))) {
-                       tipc_bclink_lock(net);
-                       bclink_accept_pkt(node, seqno);
-                       spin_lock_bh(&inputq->lock);
-                       __skb_queue_tail(arrvq, buf);
-                       spin_unlock_bh(&inputq->lock);
-                       node->action_flags |= TIPC_BCAST_MSG_EVT;
-                       tipc_bclink_unlock(net);
-                       tipc_node_unlock(node);
-               } else if (msg_user(msg) == MSG_BUNDLER) {
-                       tipc_bclink_lock(net);
-                       bclink_accept_pkt(node, seqno);
-                       bcl->stats.recv_bundles++;
-                       bcl->stats.recv_bundled += msg_msgcnt(msg);
-                       pos = 0;
-                       while (tipc_msg_extract(buf, &iskb, &pos)) {
-                               spin_lock_bh(&inputq->lock);
-                               __skb_queue_tail(arrvq, iskb);
-                               spin_unlock_bh(&inputq->lock);
-                       }
-                       node->action_flags |= TIPC_BCAST_MSG_EVT;
-                       tipc_bclink_unlock(net);
-                       tipc_node_unlock(node);
-               } else if (msg_user(msg) == MSG_FRAGMENTER) {
-                       tipc_bclink_lock(net);
-                       bclink_accept_pkt(node, seqno);
-                       tipc_buf_append(&node->bclink.reasm_buf, &buf);
-                       if (unlikely(!buf && !node->bclink.reasm_buf)) {
-                               tipc_bclink_unlock(net);
-                               goto unlock;
-                       }
-                       bcl->stats.recv_fragments++;
-                       if (buf) {
-                               bcl->stats.recv_fragmented++;
-                               msg = buf_msg(buf);
-                               tipc_bclink_unlock(net);
-                               goto receive;
-                       }
-                       tipc_bclink_unlock(net);
-                       tipc_node_unlock(node);
-               } else {
-                       tipc_bclink_lock(net);
-                       bclink_accept_pkt(node, seqno);
-                       tipc_bclink_unlock(net);
-                       tipc_node_unlock(node);
-                       kfree_skb(buf);
-               }
-               buf = NULL;
-
-               /* Determine new synchronization state */
-               tipc_node_lock(node);
-               if (unlikely(!tipc_node_is_up(node)))
-                       goto unlock;
+       struct tipc_msg *hdr = buf_msg(skb);
+       struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq;
+       struct sk_buff_head xmitq;
+       int rc;
 
-               if (node->bclink.last_in == node->bclink.last_sent)
-                       goto unlock;
+       __skb_queue_head_init(&xmitq);
 
-               if (skb_queue_empty(&node->bclink.deferdq)) {
-                       node->bclink.oos_state = 1;
-                       goto unlock;
-               }
-
-               msg = buf_msg(skb_peek(&node->bclink.deferdq));
-               seqno = msg_seqno(msg);
-               next_in = mod(next_in + 1);
-               if (seqno != next_in)
-                       goto unlock;
-
-               /* Take in-sequence message from deferred queue & deliver it */
-               buf = __skb_dequeue(&node->bclink.deferdq);
-               goto receive;
-       }
-
-       /* Handle out-of-sequence broadcast message */
-       if (less(next_in, seqno)) {
-               deferred = tipc_link_defer_pkt(&node->bclink.deferdq,
-                                              buf);
-               bclink_update_last_sent(node, seqno);
-               buf = NULL;
+       if (msg_mc_netid(hdr) != tipc_netid(net) || !tipc_link_is_up(l)) {
+               kfree_skb(skb);
+               return 0;
        }
 
-       tipc_bclink_lock(net);
-
-       if (deferred)
-               bcl->stats.deferred_recv++;
+       tipc_bcast_lock(net);
+       if (msg_user(hdr) == BCAST_PROTOCOL)
+               rc = tipc_link_bc_nack_rcv(l, skb, &xmitq);
        else
-               bcl->stats.duplicates++;
+               rc = tipc_link_rcv(l, skb, NULL);
+       tipc_bcast_unlock(net);
 
-       tipc_bclink_unlock(net);
+       tipc_bcbase_xmit(net, &xmitq);
 
-unlock:
-       tipc_node_unlock(node);
-       tipc_node_put(node);
-exit:
-       kfree_skb(buf);
-}
+       /* Any socket wakeup messages ? */
+       if (!skb_queue_empty(inputq))
+               tipc_sk_rcv(net, inputq);
 
-u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr)
-{
-       return (n_ptr->bclink.recv_permitted &&
-               (tipc_bclink_get_last_sent(n_ptr->net) != n_ptr->bclink.acked));
+       return rc;
 }
 
-
-/**
- * tipc_bcbearer_send - send a packet through the broadcast pseudo-bearer
- *
- * Send packet over as many bearers as necessary to reach all nodes
- * that have joined the broadcast link.
+/* tipc_bcast_ack_rcv - receive and handle a broadcast acknowledge
  *
- * Returns 0 (packet sent successfully) under all circumstances,
- * since the broadcast link's pseudo-bearer never blocks
+ * RCU is locked, no other locks set
  */
-static int tipc_bcbearer_send(struct net *net, struct sk_buff *buf,
-                             struct tipc_bearer *unused1,
-                             struct tipc_media_addr *unused2)
+void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, u32 acked)
 {
-       int bp_index;
-       struct tipc_msg *msg = buf_msg(buf);
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_bcbearer *bcbearer = tn->bcbearer;
-       struct tipc_bclink *bclink = tn->bclink;
-
-       /* Prepare broadcast link message for reliable transmission,
-        * if first time trying to send it;
-        * preparation is skipped for broadcast link protocol messages
-        * since they are sent in an unreliable manner and don't need it
-        */
-       if (likely(!msg_non_seq(buf_msg(buf)))) {
-               bcbuf_set_acks(buf, bclink->bcast_nodes.count);
-               msg_set_non_seq(msg, 1);
-               msg_set_mc_netid(msg, tn->net_id);
-               tn->bcl->stats.sent_info++;
-               if (WARN_ON(!bclink->bcast_nodes.count)) {
-                       dump_stack();
-                       return 0;
-               }
-       }
+       struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq;
+       struct sk_buff_head xmitq;
 
-       /* Send buffer over bearers until all targets reached */
-       bcbearer->remains = bclink->bcast_nodes;
-
-       for (bp_index = 0; bp_index < MAX_BEARERS; bp_index++) {
-               struct tipc_bearer *p = bcbearer->bpairs[bp_index].primary;
-               struct tipc_bearer *s = bcbearer->bpairs[bp_index].secondary;
-               struct tipc_bearer *bp[2] = {p, s};
-               struct tipc_bearer *b = bp[msg_link_selector(msg)];
-               struct sk_buff *tbuf;
-
-               if (!p)
-                       break; /* No more bearers to try */
-               if (!b)
-                       b = p;
-               tipc_nmap_diff(&bcbearer->remains, &b->nodes,
-                              &bcbearer->remains_new);
-               if (bcbearer->remains_new.count == bcbearer->remains.count)
-                       continue; /* Nothing added by bearer pair */
-
-               if (bp_index == 0) {
-                       /* Use original buffer for first bearer */
-                       tipc_bearer_send(net, b->identity, buf, &b->bcast_addr);
-               } else {
-                       /* Avoid concurrent buffer access */
-                       tbuf = pskb_copy_for_clone(buf, GFP_ATOMIC);
-                       if (!tbuf)
-                               break;
-                       tipc_bearer_send(net, b->identity, tbuf,
-                                        &b->bcast_addr);
-                       kfree_skb(tbuf); /* Bearer keeps a clone */
-               }
-               if (bcbearer->remains_new.count == 0)
-                       break; /* All targets reached */
+       __skb_queue_head_init(&xmitq);
 
-               bcbearer->remains = bcbearer->remains_new;
-       }
+       tipc_bcast_lock(net);
+       tipc_link_bc_ack_rcv(l, acked, &xmitq);
+       tipc_bcast_unlock(net);
 
-       return 0;
+       tipc_bcbase_xmit(net, &xmitq);
+
+       /* Any socket wakeup messages ? */
+       if (!skb_queue_empty(inputq))
+               tipc_sk_rcv(net, inputq);
 }
 
-/**
- * tipc_bcbearer_sort - create sets of bearer pairs used by broadcast bearer
+/* tipc_bcast_synch_rcv -  check and update rcv link with peer's send state
+ *
+ * RCU is locked, no other locks set
  */
-void tipc_bcbearer_sort(struct net *net, struct tipc_node_map *nm_ptr,
-                       u32 node, bool action)
+void tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l,
+                        struct tipc_msg *hdr)
 {
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_bcbearer *bcbearer = tn->bcbearer;
-       struct tipc_bcbearer_pair *bp_temp = bcbearer->bpairs_temp;
-       struct tipc_bcbearer_pair *bp_curr;
-       struct tipc_bearer *b;
-       int b_index;
-       int pri;
+       struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq;
+       struct sk_buff_head xmitq;
 
-       tipc_bclink_lock(net);
+       __skb_queue_head_init(&xmitq);
 
-       if (action)
-               tipc_nmap_add(nm_ptr, node);
-       else
-               tipc_nmap_remove(nm_ptr, node);
+       tipc_bcast_lock(net);
+       if (msg_type(hdr) == STATE_MSG) {
+               tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), &xmitq);
+               tipc_link_bc_sync_rcv(l, hdr, &xmitq);
+       } else {
+               tipc_link_bc_init_rcv(l, hdr);
+       }
+       tipc_bcast_unlock(net);
 
-       /* Group bearers by priority (can assume max of two per priority) */
-       memset(bp_temp, 0, sizeof(bcbearer->bpairs_temp));
+       tipc_bcbase_xmit(net, &xmitq);
 
-       rcu_read_lock();
-       for (b_index = 0; b_index < MAX_BEARERS; b_index++) {
-               b = rcu_dereference_rtnl(tn->bearer_list[b_index]);
-               if (!b || !b->nodes.count)
-                       continue;
-
-               if (!bp_temp[b->priority].primary)
-                       bp_temp[b->priority].primary = b;
-               else
-                       bp_temp[b->priority].secondary = b;
-       }
-       rcu_read_unlock();
+       /* Any socket wakeup messages ? */
+       if (!skb_queue_empty(inputq))
+               tipc_sk_rcv(net, inputq);
+}
 
-       /* Create array of bearer pairs for broadcasting */
-       bp_curr = bcbearer->bpairs;
-       memset(bcbearer->bpairs, 0, sizeof(bcbearer->bpairs));
+/* tipc_bcast_add_peer - add a peer node to broadcast link and bearer
+ *
+ * RCU is locked, node lock is set
+ */
+void tipc_bcast_add_peer(struct net *net, struct tipc_link *uc_l,
+                        struct sk_buff_head *xmitq)
+{
+       struct tipc_link *snd_l = tipc_bc_sndlink(net);
 
-       for (pri = TIPC_MAX_LINK_PRI; pri >= 0; pri--) {
+       tipc_bcast_lock(net);
+       tipc_link_add_bc_peer(snd_l, uc_l, xmitq);
+       tipc_bcbase_select_primary(net);
+       tipc_bcast_unlock(net);
+}
 
-               if (!bp_temp[pri].primary)
-                       continue;
+/* tipc_bcast_remove_peer - remove a peer node from broadcast link and bearer
+ *
+ * RCU is locked, node lock is set
+ */
+void tipc_bcast_remove_peer(struct net *net, struct tipc_link *rcv_l)
+{
+       struct tipc_link *snd_l = tipc_bc_sndlink(net);
+       struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq;
+       struct sk_buff_head xmitq;
 
-               bp_curr->primary = bp_temp[pri].primary;
+       __skb_queue_head_init(&xmitq);
 
-               if (bp_temp[pri].secondary) {
-                       if (tipc_nmap_equal(&bp_temp[pri].primary->nodes,
-                                           &bp_temp[pri].secondary->nodes)) {
-                               bp_curr->secondary = bp_temp[pri].secondary;
-                       } else {
-                               bp_curr++;
-                               bp_curr->primary = bp_temp[pri].secondary;
-                       }
-               }
+       tipc_bcast_lock(net);
+       tipc_link_remove_bc_peer(snd_l, rcv_l, &xmitq);
+       tipc_bcbase_select_primary(net);
+       tipc_bcast_unlock(net);
 
-               bp_curr++;
-       }
+       tipc_bcbase_xmit(net, &xmitq);
 
-       tipc_bclink_unlock(net);
+       /* Any socket wakeup messages ? */
+       if (!skb_queue_empty(inputq))
+               tipc_sk_rcv(net, inputq);
 }
 
 static int __tipc_nl_add_bc_link_stat(struct sk_buff *skb,
@@ -835,7 +395,7 @@ int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg)
        if (!bcl)
                return 0;
 
-       tipc_bclink_lock(net);
+       tipc_bcast_lock(net);
 
        hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
                          NLM_F_MULTI, TIPC_NL_LINK_GET);
@@ -870,7 +430,7 @@ int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg)
        if (err)
                goto attr_msg_full;
 
-       tipc_bclink_unlock(net);
+       tipc_bcast_unlock(net);
        nla_nest_end(msg->skb, attrs);
        genlmsg_end(msg->skb, hdr);
 
@@ -881,7 +441,7 @@ prop_msg_full:
 attr_msg_full:
        nla_nest_cancel(msg->skb, attrs);
 msg_full:
-       tipc_bclink_unlock(net);
+       tipc_bcast_unlock(net);
        genlmsg_cancel(msg->skb, hdr);
 
        return -EMSGSIZE;
@@ -895,25 +455,25 @@ int tipc_bclink_reset_stats(struct net *net)
        if (!bcl)
                return -ENOPROTOOPT;
 
-       tipc_bclink_lock(net);
+       tipc_bcast_lock(net);
        memset(&bcl->stats, 0, sizeof(bcl->stats));
-       tipc_bclink_unlock(net);
+       tipc_bcast_unlock(net);
        return 0;
 }
 
-int tipc_bclink_set_queue_limits(struct net *net, u32 limit)
+static int tipc_bc_link_set_queue_limits(struct net *net, u32 limit)
 {
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_link *bcl = tn->bcl;
+       struct tipc_link *l = tipc_bc_sndlink(net);
 
-       if (!bcl)
+       if (!l)
                return -ENOPROTOOPT;
-       if ((limit < TIPC_MIN_LINK_WIN) || (limit > TIPC_MAX_LINK_WIN))
+       if (limit < BCLINK_WIN_MIN)
+               limit = BCLINK_WIN_MIN;
+       if (limit > TIPC_MAX_LINK_WIN)
                return -EINVAL;
-
-       tipc_bclink_lock(net);
-       tipc_link_set_queue_limits(bcl, limit);
-       tipc_bclink_unlock(net);
+       tipc_bcast_lock(net);
+       tipc_link_set_queue_limits(l, limit);
+       tipc_bcast_unlock(net);
        return 0;
 }
 
@@ -935,123 +495,51 @@ int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[])
 
        win = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
 
-       return tipc_bclink_set_queue_limits(net, win);
+       return tipc_bc_link_set_queue_limits(net, win);
 }
 
-int tipc_bclink_init(struct net *net)
+int tipc_bcast_init(struct net *net)
 {
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_bcbearer *bcbearer;
-       struct tipc_bclink *bclink;
-       struct tipc_link *bcl;
-
-       bcbearer = kzalloc(sizeof(*bcbearer), GFP_ATOMIC);
-       if (!bcbearer)
-               return -ENOMEM;
-
-       bclink = kzalloc(sizeof(*bclink), GFP_ATOMIC);
-       if (!bclink) {
-               kfree(bcbearer);
-               return -ENOMEM;
-       }
+       struct tipc_net *tn = tipc_net(net);
+       struct tipc_bc_base *bb = NULL;
+       struct tipc_link *l = NULL;
 
-       bcl = &bclink->link;
-       bcbearer->bearer.media = &bcbearer->media;
-       bcbearer->media.send_msg = tipc_bcbearer_send;
-       sprintf(bcbearer->media.name, "tipc-broadcast");
-
-       spin_lock_init(&bclink->lock);
-       __skb_queue_head_init(&bcl->transmq);
-       __skb_queue_head_init(&bcl->backlogq);
-       __skb_queue_head_init(&bcl->deferdq);
-       skb_queue_head_init(&bcl->wakeupq);
-       bcl->snd_nxt = 1;
-       spin_lock_init(&bclink->node.lock);
-       __skb_queue_head_init(&bclink->arrvq);
-       skb_queue_head_init(&bclink->inputq);
-       bcl->owner = &bclink->node;
-       bcl->owner->net = net;
-       bcl->mtu = MAX_PKT_DEFAULT_MCAST;
-       tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT);
-       bcl->bearer_id = MAX_BEARERS;
-       rcu_assign_pointer(tn->bearer_list[MAX_BEARERS], &bcbearer->bearer);
-       bcl->pmsg = (struct tipc_msg *)&bcl->proto_msg;
-       msg_set_prevnode(bcl->pmsg, tn->own_addr);
-       strlcpy(bcl->name, tipc_bclink_name, TIPC_MAX_LINK_NAME);
-       tn->bcbearer = bcbearer;
-       tn->bclink = bclink;
-       tn->bcl = bcl;
-       return 0;
-}
+       bb = kzalloc(sizeof(*bb), GFP_ATOMIC);
+       if (!bb)
+               goto enomem;
+       tn->bcbase = bb;
+       spin_lock_init(&tipc_net(net)->bclock);
 
-void tipc_bclink_stop(struct net *net)
-{
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-
-       tipc_bclink_lock(net);
-       tipc_link_purge_queues(tn->bcl);
-       tipc_bclink_unlock(net);
-
-       RCU_INIT_POINTER(tn->bearer_list[BCBEARER], NULL);
-       synchronize_net();
-       kfree(tn->bcbearer);
-       kfree(tn->bclink);
+       if (!tipc_link_bc_create(net, 0, 0,
+                                U16_MAX,
+                                BCLINK_WIN_DEFAULT,
+                                0,
+                                &bb->inputq,
+                                NULL,
+                                NULL,
+                                &l))
+               goto enomem;
+       bb->link = l;
+       tn->bcl = l;
+       return 0;
+enomem:
+       kfree(bb);
+       kfree(l);
+       return -ENOMEM;
 }
 
-/**
- * tipc_nmap_add - add a node to a node map
- */
-static void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node)
+void tipc_bcast_reinit(struct net *net)
 {
-       int n = tipc_node(node);
-       int w = n / WSIZE;
-       u32 mask = (1 << (n % WSIZE));
+       struct tipc_bc_base *b = tipc_bc_base(net);
 
-       if ((nm_ptr->map[w] & mask) == 0) {
-               nm_ptr->count++;
-               nm_ptr->map[w] |= mask;
-       }
+       msg_set_prevnode(b->link->pmsg, tipc_own_addr(net));
 }
 
-/**
- * tipc_nmap_remove - remove a node from a node map
- */
-static void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node)
+void tipc_bcast_stop(struct net *net)
 {
-       int n = tipc_node(node);
-       int w = n / WSIZE;
-       u32 mask = (1 << (n % WSIZE));
-
-       if ((nm_ptr->map[w] & mask) != 0) {
-               nm_ptr->map[w] &= ~mask;
-               nm_ptr->count--;
-       }
-}
+       struct tipc_net *tn = net_generic(net, tipc_net_id);
 
-/**
- * tipc_nmap_diff - find differences between node maps
- * @nm_a: input node map A
- * @nm_b: input node map B
- * @nm_diff: output node map A-B (i.e. nodes of A that are not in B)
- */
-static void tipc_nmap_diff(struct tipc_node_map *nm_a,
-                          struct tipc_node_map *nm_b,
-                          struct tipc_node_map *nm_diff)
-{
-       int stop = ARRAY_SIZE(nm_a->map);
-       int w;
-       int b;
-       u32 map;
-
-       memset(nm_diff, 0, sizeof(*nm_diff));
-       for (w = 0; w < stop; w++) {
-               map = nm_a->map[w] ^ (nm_a->map[w] & nm_b->map[w]);
-               nm_diff->map[w] = map;
-               if (map != 0) {
-                       for (b = 0 ; b < WSIZE; b++) {
-                               if (map & (1 << b))
-                                       nm_diff->count++;
-                       }
-               }
-       }
+       synchronize_net();
+       kfree(tn->bcbase);
+       kfree(tn->bcl);
 }
index d74c69bcf60bda5e04ddc61a988afd6fa94bee66..2855b9356a1527224d76186d001aad2a5a44e50d 100644 (file)
 #ifndef _TIPC_BCAST_H
 #define _TIPC_BCAST_H
 
-#include <linux/tipc_config.h>
-#include "link.h"
-#include "node.h"
+#include "core.h"
 
-/**
- * struct tipc_bcbearer_pair - a pair of bearers used by broadcast link
- * @primary: pointer to primary bearer
- * @secondary: pointer to secondary bearer
- *
- * Bearers must have same priority and same set of reachable destinations
- * to be paired.
- */
-
-struct tipc_bcbearer_pair {
-       struct tipc_bearer *primary;
-       struct tipc_bearer *secondary;
-};
-
-#define        BCBEARER                MAX_BEARERS
-
-/**
- * struct tipc_bcbearer - bearer used by broadcast link
- * @bearer: (non-standard) broadcast bearer structure
- * @media: (non-standard) broadcast media structure
- * @bpairs: array of bearer pairs
- * @bpairs_temp: temporary array of bearer pairs used by tipc_bcbearer_sort()
- * @remains: temporary node map used by tipc_bcbearer_send()
- * @remains_new: temporary node map used tipc_bcbearer_send()
- *
- * Note: The fields labelled "temporary" are incorporated into the bearer
- * to avoid consuming potentially limited stack space through the use of
- * large local variables within multicast routines.  Concurrent access is
- * prevented through use of the spinlock "bclink_lock".
- */
-struct tipc_bcbearer {
-       struct tipc_bearer bearer;
-       struct tipc_media media;
-       struct tipc_bcbearer_pair bpairs[MAX_BEARERS];
-       struct tipc_bcbearer_pair bpairs_temp[TIPC_MAX_LINK_PRI + 1];
-       struct tipc_node_map remains;
-       struct tipc_node_map remains_new;
-};
+struct tipc_node;
+struct tipc_msg;
+struct tipc_nl_msg;
+struct tipc_node_map;
 
-/**
- * struct tipc_bclink - link used for broadcast messages
- * @lock: spinlock governing access to structure
- * @link: (non-standard) broadcast link structure
- * @node: (non-standard) node structure representing b'cast link's peer node
- * @bcast_nodes: map of broadcast-capable nodes
- * @retransmit_to: node that most recently requested a retransmit
- *
- * Handles sequence numbering, fragmentation, bundling, etc.
- */
-struct tipc_bclink {
-       spinlock_t lock;
-       struct tipc_link link;
-       struct tipc_node node;
-       struct sk_buff_head arrvq;
-       struct sk_buff_head inputq;
-       struct tipc_node_map bcast_nodes;
-       struct tipc_node *retransmit_to;
-};
+int tipc_bcast_init(struct net *net);
+void tipc_bcast_reinit(struct net *net);
+void tipc_bcast_stop(struct net *net);
+void tipc_bcast_add_peer(struct net *net, struct tipc_link *l,
+                        struct sk_buff_head *xmitq);
+void tipc_bcast_remove_peer(struct net *net, struct tipc_link *rcv_bcl);
+void tipc_bcast_inc_bearer_dst_cnt(struct net *net, int bearer_id);
+void tipc_bcast_dec_bearer_dst_cnt(struct net *net, int bearer_id);
+int  tipc_bcast_get_mtu(struct net *net);
+int tipc_bcast_xmit(struct net *net, struct sk_buff_head *list);
+int tipc_bcast_rcv(struct net *net, struct tipc_link *l, struct sk_buff *skb);
+void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, u32 acked);
+void tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l,
+                        struct tipc_msg *hdr);
+int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg);
+int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[]);
+int tipc_bclink_reset_stats(struct net *net);
 
-struct tipc_node;
-extern const char tipc_bclink_name[];
+static inline void tipc_bcast_lock(struct net *net)
+{
+       spin_lock_bh(&tipc_net(net)->bclock);
+}
 
-/**
- * tipc_nmap_equal - test for equality of node maps
- */
-static inline int tipc_nmap_equal(struct tipc_node_map *nm_a,
-                                 struct tipc_node_map *nm_b)
+static inline void tipc_bcast_unlock(struct net *net)
 {
-       return !memcmp(nm_a, nm_b, sizeof(*nm_a));
+       spin_unlock_bh(&tipc_net(net)->bclock);
 }
 
-int tipc_bclink_init(struct net *net);
-void tipc_bclink_stop(struct net *net);
-void tipc_bclink_add_node(struct net *net, u32 addr);
-void tipc_bclink_remove_node(struct net *net, u32 addr);
-struct tipc_node *tipc_bclink_retransmit_to(struct net *tn);
-void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked);
-void tipc_bclink_rcv(struct net *net, struct sk_buff *buf);
-u32  tipc_bclink_get_last_sent(struct net *net);
-u32  tipc_bclink_acks_missing(struct tipc_node *n_ptr);
-void tipc_bclink_update_link_state(struct tipc_node *node,
-                                  u32 last_sent);
-int  tipc_bclink_reset_stats(struct net *net);
-int  tipc_bclink_set_queue_limits(struct net *net, u32 limit);
-void tipc_bcbearer_sort(struct net *net, struct tipc_node_map *nm_ptr,
-                       u32 node, bool action);
-uint  tipc_bclink_get_mtu(void);
-int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list);
-void tipc_bclink_wakeup_users(struct net *net);
-int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg);
-int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[]);
-void tipc_bclink_input(struct net *net);
-void tipc_bclink_sync_state(struct tipc_node *n, struct tipc_msg *msg);
+static inline struct tipc_link *tipc_bc_sndlink(struct net *net)
+{
+       return tipc_net(net)->bcl;
+}
 
 #endif
index ce9f7bfc0b92444950f51893e87abbc426151eb6..648f2a67f3148272dbbf7fd43e3a0536b1cdab3c 100644 (file)
@@ -193,10 +193,8 @@ void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest)
 
        rcu_read_lock();
        b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
-       if (b_ptr) {
-               tipc_bcbearer_sort(net, &b_ptr->nodes, dest, true);
+       if (b_ptr)
                tipc_disc_add_dest(b_ptr->link_req);
-       }
        rcu_read_unlock();
 }
 
@@ -207,10 +205,8 @@ void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest)
 
        rcu_read_lock();
        b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
-       if (b_ptr) {
-               tipc_bcbearer_sort(net, &b_ptr->nodes, dest, false);
+       if (b_ptr)
                tipc_disc_remove_dest(b_ptr->link_req);
-       }
        rcu_read_unlock();
 }
 
@@ -362,6 +358,7 @@ static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr)
        b_ptr->media->disable_media(b_ptr);
 
        tipc_node_delete_links(net, b_ptr->identity);
+       RCU_INIT_POINTER(b_ptr->media_ptr, NULL);
        if (b_ptr->link_req)
                tipc_disc_delete(b_ptr->link_req);
 
@@ -399,16 +396,13 @@ int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b,
 
 /* tipc_disable_l2_media - detach TIPC bearer from an L2 interface
  *
- * Mark L2 bearer as inactive so that incoming buffers are thrown away,
- * then get worker thread to complete bearer cleanup.  (Can't do cleanup
- * here because cleanup code needs to sleep and caller holds spinlocks.)
+ * Mark L2 bearer as inactive so that incoming buffers are thrown away
  */
 void tipc_disable_l2_media(struct tipc_bearer *b)
 {
        struct net_device *dev;
 
        dev = (struct net_device *)rtnl_dereference(b->media_ptr);
-       RCU_INIT_POINTER(b->media_ptr, NULL);
        RCU_INIT_POINTER(dev->tipc_ptr, NULL);
        synchronize_net();
        dev_put(dev);
@@ -420,10 +414,9 @@ void tipc_disable_l2_media(struct tipc_bearer *b)
  * @b_ptr: the bearer through which the packet is to be sent
  * @dest: peer destination address
  */
-int tipc_l2_send_msg(struct net *net, struct sk_buff *buf,
+int tipc_l2_send_msg(struct net *net, struct sk_buff *skb,
                     struct tipc_bearer *b, struct tipc_media_addr *dest)
 {
-       struct sk_buff *clone;
        struct net_device *dev;
        int delta;
 
@@ -431,42 +424,48 @@ int tipc_l2_send_msg(struct net *net, struct sk_buff *buf,
        if (!dev)
                return 0;
 
-       clone = skb_clone(buf, GFP_ATOMIC);
-       if (!clone)
-               return 0;
-
-       delta = dev->hard_header_len - skb_headroom(buf);
+       delta = dev->hard_header_len - skb_headroom(skb);
        if ((delta > 0) &&
-           pskb_expand_head(clone, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) {
-               kfree_skb(clone);
+           pskb_expand_head(skb, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) {
+               kfree_skb(skb);
                return 0;
        }
 
-       skb_reset_network_header(clone);
-       clone->dev = dev;
-       clone->protocol = htons(ETH_P_TIPC);
-       dev_hard_header(clone, dev, ETH_P_TIPC, dest->value,
-                       dev->dev_addr, clone->len);
-       dev_queue_xmit(clone);
+       skb_reset_network_header(skb);
+       skb->dev = dev;
+       skb->protocol = htons(ETH_P_TIPC);
+       dev_hard_header(skb, dev, ETH_P_TIPC, dest->value,
+                       dev->dev_addr, skb->len);
+       dev_queue_xmit(skb);
        return 0;
 }
 
-/* tipc_bearer_send- sends buffer to destination over bearer
- *
- * IMPORTANT:
- * The media send routine must not alter the buffer being passed in
- * as it may be needed for later retransmission!
+int tipc_bearer_mtu(struct net *net, u32 bearer_id)
+{
+       int mtu = 0;
+       struct tipc_bearer *b;
+
+       rcu_read_lock();
+       b = rcu_dereference_rtnl(tipc_net(net)->bearer_list[bearer_id]);
+       if (b)
+               mtu = b->mtu;
+       rcu_read_unlock();
+       return mtu;
+}
+
+/* tipc_bearer_xmit_skb - sends buffer to destination over bearer
  */
-void tipc_bearer_send(struct net *net, u32 bearer_id, struct sk_buff *buf,
-                     struct tipc_media_addr *dest)
+void tipc_bearer_xmit_skb(struct net *net, u32 bearer_id,
+                         struct sk_buff *skb,
+                         struct tipc_media_addr *dest)
 {
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_bearer *b_ptr;
+       struct tipc_net *tn = tipc_net(net);
+       struct tipc_bearer *b;
 
        rcu_read_lock();
-       b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
-       if (likely(b_ptr))
-               b_ptr->media->send_msg(net, buf, b_ptr, dest);
+       b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
+       if (likely(b))
+               b->media->send_msg(net, skb, b, dest);
        rcu_read_unlock();
 }
 
@@ -489,8 +488,31 @@ void tipc_bearer_xmit(struct net *net, u32 bearer_id,
                skb_queue_walk_safe(xmitq, skb, tmp) {
                        __skb_dequeue(xmitq);
                        b->media->send_msg(net, skb, b, dst);
-                       /* Until we remove cloning in tipc_l2_send_msg(): */
-                       kfree_skb(skb);
+               }
+       }
+       rcu_read_unlock();
+}
+
+/* tipc_bearer_bc_xmit() - broadcast buffers to all destinations
+ */
+void tipc_bearer_bc_xmit(struct net *net, u32 bearer_id,
+                        struct sk_buff_head *xmitq)
+{
+       struct tipc_net *tn = tipc_net(net);
+       int net_id = tn->net_id;
+       struct tipc_bearer *b;
+       struct sk_buff *skb, *tmp;
+       struct tipc_msg *hdr;
+
+       rcu_read_lock();
+       b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
+       if (likely(b)) {
+               skb_queue_walk_safe(xmitq, skb, tmp) {
+                       hdr = buf_msg(skb);
+                       msg_set_non_seq(hdr, 1);
+                       msg_set_mc_netid(hdr, net_id);
+                       __skb_dequeue(xmitq);
+                       b->media->send_msg(net, skb, b, &b->bcast_addr);
                }
        }
        rcu_read_unlock();
@@ -554,7 +576,7 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
        case NETDEV_CHANGE:
                if (netif_carrier_ok(dev))
                        break;
-       case NETDEV_DOWN:
+       case NETDEV_GOING_DOWN:
        case NETDEV_CHANGEMTU:
                tipc_reset_bearer(net, b_ptr);
                break;
index 6426f242f6262e80594cd1cdc438c4a94f4c7026..552185bc477327bc8a0ca4c638194d227268aace 100644 (file)
@@ -163,6 +163,7 @@ struct tipc_bearer {
        u32 identity;
        struct tipc_link_req *link_req;
        char net_plane;
+       int node_cnt;
        struct tipc_node_map nodes;
 };
 
@@ -215,10 +216,14 @@ struct tipc_media *tipc_media_find(const char *name);
 int tipc_bearer_setup(void);
 void tipc_bearer_cleanup(void);
 void tipc_bearer_stop(struct net *net);
-void tipc_bearer_send(struct net *net, u32 bearer_id, struct sk_buff *buf,
-                     struct tipc_media_addr *dest);
+int tipc_bearer_mtu(struct net *net, u32 bearer_id);
+void tipc_bearer_xmit_skb(struct net *net, u32 bearer_id,
+                         struct sk_buff *skb,
+                         struct tipc_media_addr *dest);
 void tipc_bearer_xmit(struct net *net, u32 bearer_id,
                      struct sk_buff_head *xmitq,
                      struct tipc_media_addr *dst);
+void tipc_bearer_bc_xmit(struct net *net, u32 bearer_id,
+                        struct sk_buff_head *xmitq);
 
 #endif /* _TIPC_BEARER_H */
index 005ba5eb0ea426a3a24cbfe0e74aa4c1901af903..03a842870c52d22ca5c8e72ce063f78ba8391521 100644 (file)
@@ -42,6 +42,7 @@
 #include "bearer.h"
 #include "net.h"
 #include "socket.h"
+#include "bcast.h"
 
 #include <linux/module.h>
 
@@ -71,8 +72,15 @@ static int __net_init tipc_init_net(struct net *net)
        err = tipc_topsrv_start(net);
        if (err)
                goto out_subscr;
+
+       err = tipc_bcast_init(net);
+       if (err)
+               goto out_bclink;
+
        return 0;
 
+out_bclink:
+       tipc_bcast_stop(net);
 out_subscr:
        tipc_nametbl_stop(net);
 out_nametbl:
@@ -85,6 +93,7 @@ static void __net_exit tipc_exit_net(struct net *net)
 {
        tipc_topsrv_stop(net);
        tipc_net_stop(net);
+       tipc_bcast_stop(net);
        tipc_nametbl_stop(net);
        tipc_sk_rht_destroy(net);
 }
index b96b41eabf121cc8577b65d0ad5bb0727ca5d3f7..18e95a8020cd48b4594fb4d00f1fc47e2ed6f6e2 100644 (file)
@@ -62,8 +62,7 @@
 
 struct tipc_node;
 struct tipc_bearer;
-struct tipc_bcbearer;
-struct tipc_bclink;
+struct tipc_bc_base;
 struct tipc_link;
 struct tipc_name_table;
 struct tipc_server;
@@ -93,8 +92,8 @@ struct tipc_net {
        struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1];
 
        /* Broadcast link */
-       struct tipc_bcbearer *bcbearer;
-       struct tipc_bclink *bclink;
+       spinlock_t bclock;
+       struct tipc_bc_base *bcbase;
        struct tipc_link *bcl;
 
        /* Socket hash table */
@@ -114,6 +113,11 @@ static inline struct tipc_net *tipc_net(struct net *net)
        return net_generic(net, tipc_net_id);
 }
 
+static inline int tipc_netid(struct net *net)
+{
+       return tipc_net(net)->net_id;
+}
+
 static inline u16 mod(u16 x)
 {
        return x & 0xffffu;
index d14e0a4aa9af900a7ace6855ab91eb2bcc901641..afe8c47c4085c7e01e6e8bfb3d9ed1e4f13eaa20 100644 (file)
@@ -89,7 +89,7 @@ static void tipc_disc_init_msg(struct net *net, struct sk_buff *buf, u32 type,
                      MAX_H_SIZE, dest_domain);
        msg_set_non_seq(msg, 1);
        msg_set_node_sig(msg, tn->random);
-       msg_set_node_capabilities(msg, 0);
+       msg_set_node_capabilities(msg, TIPC_NODE_CAPABILITIES);
        msg_set_dest_domain(msg, dest_domain);
        msg_set_bc_netid(msg, tn->net_id);
        b_ptr->media->addr2msg(msg_media_addr(msg), &b_ptr->addr);
@@ -167,11 +167,10 @@ void tipc_disc_rcv(struct net *net, struct sk_buff *skb,
        /* Send response, if necessary */
        if (respond && (mtyp == DSC_REQ_MSG)) {
                rskb = tipc_buf_acquire(MAX_H_SIZE);
-               if (rskb) {
-                       tipc_disc_init_msg(net, rskb, DSC_RESP_MSG, bearer);
-                       tipc_bearer_send(net, bearer->identity, rskb, &maddr);
-                       kfree_skb(rskb);
-               }
+               if (!rskb)
+                       return;
+               tipc_disc_init_msg(net, rskb, DSC_RESP_MSG, bearer);
+               tipc_bearer_xmit_skb(net, bearer->identity, rskb, &maddr);
        }
 }
 
@@ -225,6 +224,7 @@ void tipc_disc_remove_dest(struct tipc_link_req *req)
 static void disc_timeout(unsigned long data)
 {
        struct tipc_link_req *req = (struct tipc_link_req *)data;
+       struct sk_buff *skb;
        int max_delay;
 
        spin_lock_bh(&req->lock);
@@ -242,9 +242,9 @@ static void disc_timeout(unsigned long data)
         * hold at fast polling rate if don't have any associated nodes,
         * otherwise hold at slow polling rate
         */
-       tipc_bearer_send(req->net, req->bearer_id, req->buf, &req->dest);
-
-
+       skb = skb_clone(req->buf, GFP_ATOMIC);
+       if (skb)
+               tipc_bearer_xmit_skb(req->net, req->bearer_id, skb, &req->dest);
        req->timer_intv *= 2;
        if (req->num_nodes)
                max_delay = TIPC_LINK_REQ_SLOW;
@@ -271,6 +271,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr,
                     struct tipc_media_addr *dest)
 {
        struct tipc_link_req *req;
+       struct sk_buff *skb;
 
        req = kmalloc(sizeof(*req), GFP_ATOMIC);
        if (!req)
@@ -292,7 +293,9 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr,
        setup_timer(&req->timer, disc_timeout, (unsigned long)req);
        mod_timer(&req->timer, jiffies + req->timer_intv);
        b_ptr->link_req = req;
-       tipc_bearer_send(net, req->bearer_id, req->buf, &req->dest);
+       skb = skb_clone(req->buf, GFP_ATOMIC);
+       if (skb)
+               tipc_bearer_xmit_skb(net, req->bearer_id, skb, &req->dest);
        return 0;
 }
 
@@ -316,6 +319,7 @@ void tipc_disc_delete(struct tipc_link_req *req)
 void tipc_disc_reset(struct net *net, struct tipc_bearer *b_ptr)
 {
        struct tipc_link_req *req = b_ptr->link_req;
+       struct sk_buff *skb;
 
        spin_lock_bh(&req->lock);
        tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b_ptr);
@@ -325,6 +329,8 @@ void tipc_disc_reset(struct net *net, struct tipc_bearer *b_ptr)
        req->num_nodes = 0;
        req->timer_intv = TIPC_LINK_REQ_INIT;
        mod_timer(&req->timer, jiffies + req->timer_intv);
-       tipc_bearer_send(net, req->bearer_id, req->buf, &req->dest);
+       skb = skb_clone(req->buf, GFP_ATOMIC);
+       if (skb)
+               tipc_bearer_xmit_skb(net, req->bearer_id, skb, &req->dest);
        spin_unlock_bh(&req->lock);
 }
index 75db07c78a6900157c0b568491a5d4e8e694e274..9efbdbde2b0863542a08c91c136fadc9aa0cc6d8 100644 (file)
@@ -50,6 +50,7 @@
  */
 static const char *link_co_err = "Link tunneling error, ";
 static const char *link_rst_msg = "Resetting link ";
+static const char tipc_bclink_name[] = "broadcast-link";
 
 static const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = {
        [TIPC_NLA_LINK_UNSPEC]          = { .type = NLA_UNSPEC },
@@ -75,6 +76,14 @@ static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
        [TIPC_NLA_PROP_WIN]             = { .type = NLA_U32 }
 };
 
+/* Send states for broadcast NACKs
+ */
+enum {
+       BC_NACK_SND_CONDITIONAL,
+       BC_NACK_SND_UNCONDITIONAL,
+       BC_NACK_SND_SUPPRESS,
+};
+
 /*
  * Interval between NACKs when packets arrive out of order
  */
@@ -110,7 +119,11 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
                                      struct sk_buff_head *xmitq);
 static void link_reset_statistics(struct tipc_link *l_ptr);
 static void link_print(struct tipc_link *l_ptr, const char *str);
-static void tipc_link_sync_rcv(struct tipc_node *n, struct sk_buff *buf);
+static void tipc_link_build_nack_msg(struct tipc_link *l,
+                                    struct sk_buff_head *xmitq);
+static void tipc_link_build_bc_init_msg(struct tipc_link *l,
+                                       struct sk_buff_head *xmitq);
+static bool tipc_link_release_pkts(struct tipc_link *l, u16 to);
 
 /*
  *  Simple non-static link routines (i.e. referenced outside this file)
@@ -120,11 +133,21 @@ bool tipc_link_is_up(struct tipc_link *l)
        return link_is_up(l);
 }
 
+bool tipc_link_peer_is_down(struct tipc_link *l)
+{
+       return l->state == LINK_PEER_RESET;
+}
+
 bool tipc_link_is_reset(struct tipc_link *l)
 {
        return l->state & (LINK_RESET | LINK_FAILINGOVER | LINK_ESTABLISHING);
 }
 
+bool tipc_link_is_establishing(struct tipc_link *l)
+{
+       return l->state == LINK_ESTABLISHING;
+}
+
 bool tipc_link_is_synching(struct tipc_link *l)
 {
        return l->state == LINK_SYNCHING;
@@ -140,11 +163,66 @@ bool tipc_link_is_blocked(struct tipc_link *l)
        return l->state & (LINK_RESETTING | LINK_PEER_RESET | LINK_FAILINGOVER);
 }
 
+static bool link_is_bc_sndlink(struct tipc_link *l)
+{
+       return !l->bc_sndlink;
+}
+
+static bool link_is_bc_rcvlink(struct tipc_link *l)
+{
+       return ((l->bc_rcvlink == l) && !link_is_bc_sndlink(l));
+}
+
 int tipc_link_is_active(struct tipc_link *l)
 {
-       struct tipc_node *n = l->owner;
+       return l->active;
+}
+
+void tipc_link_set_active(struct tipc_link *l, bool active)
+{
+       l->active = active;
+}
+
+void tipc_link_add_bc_peer(struct tipc_link *snd_l,
+                          struct tipc_link *uc_l,
+                          struct sk_buff_head *xmitq)
+{
+       struct tipc_link *rcv_l = uc_l->bc_rcvlink;
+
+       snd_l->ackers++;
+       rcv_l->acked = snd_l->snd_nxt - 1;
+       tipc_link_build_bc_init_msg(uc_l, xmitq);
+}
+
+void tipc_link_remove_bc_peer(struct tipc_link *snd_l,
+                             struct tipc_link *rcv_l,
+                             struct sk_buff_head *xmitq)
+{
+       u16 ack = snd_l->snd_nxt - 1;
+
+       snd_l->ackers--;
+       tipc_link_bc_ack_rcv(rcv_l, ack, xmitq);
+       tipc_link_reset(rcv_l);
+       rcv_l->state = LINK_RESET;
+       if (!snd_l->ackers) {
+               tipc_link_reset(snd_l);
+               __skb_queue_purge(xmitq);
+       }
+}
+
+int tipc_link_bc_peers(struct tipc_link *l)
+{
+       return l->ackers;
+}
+
+void tipc_link_set_mtu(struct tipc_link *l, int mtu)
+{
+       l->mtu = mtu;
+}
 
-       return (node_active_link(n, 0) == l) || (node_active_link(n, 1) == l);
+int tipc_link_mtu(struct tipc_link *l)
+{
+       return l->mtu;
 }
 
 static u32 link_own_addr(struct tipc_link *l)
@@ -155,57 +233,72 @@ static u32 link_own_addr(struct tipc_link *l)
 /**
  * tipc_link_create - create a new link
  * @n: pointer to associated node
- * @b: pointer to associated bearer
+ * @if_name: associated interface name
+ * @bearer_id: id (index) of associated bearer
+ * @tolerance: link tolerance to be used by link
+ * @net_plane: network plane (A,B,c..) this link belongs to
+ * @mtu: mtu to be advertised by link
+ * @priority: priority to be used by link
+ * @window: send window to be used by link
+ * @session: session to be used by link
  * @ownnode: identity of own node
- * @peer: identity of peer node
- * @maddr: media address to be used
+ * @peer: node id of peer node
+ * @peer_caps: bitmap describing peer node capabilities
+ * @bc_sndlink: the namespace global link used for broadcast sending
+ * @bc_rcvlink: the peer specific link used for broadcast reception
  * @inputq: queue to put messages ready for delivery
  * @namedq: queue to put binding table update messages ready for delivery
  * @link: return value, pointer to put the created link
  *
  * Returns true if link was created, otherwise false
  */
-bool tipc_link_create(struct tipc_node *n, struct tipc_bearer *b, u32 session,
-                     u32 ownnode, u32 peer, struct tipc_media_addr *maddr,
-                     struct sk_buff_head *inputq, struct sk_buff_head *namedq,
+bool tipc_link_create(struct net *net, char *if_name, int bearer_id,
+                     int tolerance, char net_plane, u32 mtu, int priority,
+                     int window, u32 session, u32 ownnode, u32 peer,
+                     u16 peer_caps,
+                     struct tipc_link *bc_sndlink,
+                     struct tipc_link *bc_rcvlink,
+                     struct sk_buff_head *inputq,
+                     struct sk_buff_head *namedq,
                      struct tipc_link **link)
 {
        struct tipc_link *l;
        struct tipc_msg *hdr;
-       char *if_name;
 
        l = kzalloc(sizeof(*l), GFP_ATOMIC);
        if (!l)
                return false;
        *link = l;
+       l->pmsg = (struct tipc_msg *)&l->proto_msg;
+       hdr = l->pmsg;
+       tipc_msg_init(ownnode, hdr, LINK_PROTOCOL, RESET_MSG, INT_H_SIZE, peer);
+       msg_set_size(hdr, sizeof(l->proto_msg));
+       msg_set_session(hdr, session);
+       msg_set_bearer_id(hdr, l->bearer_id);
 
        /* Note: peer i/f name is completed by reset/activate message */
-       if_name = strchr(b->name, ':') + 1;
        sprintf(l->name, "%u.%u.%u:%s-%u.%u.%u:unknown",
                tipc_zone(ownnode), tipc_cluster(ownnode), tipc_node(ownnode),
                if_name, tipc_zone(peer), tipc_cluster(peer), tipc_node(peer));
+       strcpy((char *)msg_data(hdr), if_name);
 
        l->addr = peer;
-       l->media_addr = maddr;
-       l->owner = n;
+       l->peer_caps = peer_caps;
+       l->net = net;
        l->peer_session = WILDCARD_SESSION;
-       l->bearer_id = b->identity;
-       l->tolerance = b->tolerance;
-       l->net_plane = b->net_plane;
-       l->advertised_mtu = b->mtu;
-       l->mtu = b->mtu;
-       l->priority = b->priority;
-       tipc_link_set_queue_limits(l, b->window);
+       l->bearer_id = bearer_id;
+       l->tolerance = tolerance;
+       l->net_plane = net_plane;
+       l->advertised_mtu = mtu;
+       l->mtu = mtu;
+       l->priority = priority;
+       tipc_link_set_queue_limits(l, window);
+       l->ackers = 1;
+       l->bc_sndlink = bc_sndlink;
+       l->bc_rcvlink = bc_rcvlink;
        l->inputq = inputq;
        l->namedq = namedq;
        l->state = LINK_RESETTING;
-       l->pmsg = (struct tipc_msg *)&l->proto_msg;
-       hdr = l->pmsg;
-       tipc_msg_init(ownnode, hdr, LINK_PROTOCOL, RESET_MSG, INT_H_SIZE, peer);
-       msg_set_size(hdr, sizeof(l->proto_msg));
-       msg_set_session(hdr, session);
-       msg_set_bearer_id(hdr, l->bearer_id);
-       strcpy((char *)msg_data(hdr), if_name);
        __skb_queue_head_init(&l->transmq);
        __skb_queue_head_init(&l->backlogq);
        __skb_queue_head_init(&l->deferdq);
@@ -214,27 +307,43 @@ bool tipc_link_create(struct tipc_node *n, struct tipc_bearer *b, u32 session,
        return true;
 }
 
-/* tipc_link_build_bcast_sync_msg() - synchronize broadcast link endpoints.
+/**
+ * tipc_link_bc_create - create new link to be used for broadcast
+ * @n: pointer to associated node
+ * @mtu: mtu to be used
+ * @window: send window to be used
+ * @inputq: queue to put messages ready for delivery
+ * @namedq: queue to put binding table update messages ready for delivery
+ * @link: return value, pointer to put the created link
  *
- * Give a newly added peer node the sequence number where it should
- * start receiving and acking broadcast packets.
+ * Returns true if link was created, otherwise false
  */
-void tipc_link_build_bcast_sync_msg(struct tipc_link *l,
-                                   struct sk_buff_head *xmitq)
+bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer,
+                        int mtu, int window, u16 peer_caps,
+                        struct sk_buff_head *inputq,
+                        struct sk_buff_head *namedq,
+                        struct tipc_link *bc_sndlink,
+                        struct tipc_link **link)
 {
-       struct sk_buff *skb;
-       struct sk_buff_head list;
-       u16 last_sent;
+       struct tipc_link *l;
 
-       skb = tipc_msg_create(BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE,
-                             0, l->addr, link_own_addr(l), 0, 0, 0);
-       if (!skb)
-               return;
-       last_sent = tipc_bclink_get_last_sent(l->owner->net);
-       msg_set_last_bcast(buf_msg(skb), last_sent);
-       __skb_queue_head_init(&list);
-       __skb_queue_tail(&list, skb);
-       tipc_link_xmit(l, &list, xmitq);
+       if (!tipc_link_create(net, "", MAX_BEARERS, 0, 'Z', mtu, 0, window,
+                             0, ownnode, peer, peer_caps, bc_sndlink,
+                             NULL, inputq, namedq, link))
+               return false;
+
+       l = *link;
+       strcpy(l->name, tipc_bclink_name);
+       tipc_link_reset(l);
+       l->state = LINK_RESET;
+       l->ackers = 0;
+       l->bc_rcvlink = l;
+
+       /* Broadcast send link is always up */
+       if (link_is_bc_sndlink(l))
+               l->state = LINK_ESTABLISHED;
+
+       return true;
 }
 
 /**
@@ -321,14 +430,15 @@ int tipc_link_fsm_evt(struct tipc_link *l, int evt)
                switch (evt) {
                case LINK_ESTABLISH_EVT:
                        l->state = LINK_ESTABLISHED;
-                       rc |= TIPC_LINK_UP_EVT;
                        break;
                case LINK_FAILOVER_BEGIN_EVT:
                        l->state = LINK_FAILINGOVER;
                        break;
-               case LINK_PEER_RESET_EVT:
                case LINK_RESET_EVT:
+                       l->state = LINK_RESET;
+                       break;
                case LINK_FAILURE_EVT:
+               case LINK_PEER_RESET_EVT:
                case LINK_SYNCH_BEGIN_EVT:
                case LINK_FAILOVER_END_EVT:
                        break;
@@ -438,6 +548,8 @@ static void link_profile_stats(struct tipc_link *l)
                l->stats.msg_length_profile[6]++;
 }
 
+/* tipc_link_timeout - perform periodic task as instructed from node timeout
+ */
 /* tipc_link_timeout - perform periodic task as instructed from node timeout
  */
 int tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq)
@@ -446,6 +558,9 @@ int tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq)
        int mtyp = STATE_MSG;
        bool xmit = false;
        bool prb = false;
+       u16 bc_snt = l->bc_sndlink->snd_nxt - 1;
+       u16 bc_acked = l->bc_rcvlink->acked;
+       bool bc_up = link_is_up(l->bc_rcvlink);
 
        link_profile_stats(l);
 
@@ -453,7 +568,7 @@ int tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq)
        case LINK_ESTABLISHED:
        case LINK_SYNCHING:
                if (!l->silent_intv_cnt) {
-                       if (tipc_bclink_acks_missing(l->owner))
+                       if (bc_up && (bc_acked != bc_snt))
                                xmit = true;
                } else if (l->silent_intv_cnt <= l->abort_limit) {
                        xmit = true;
@@ -544,42 +659,8 @@ void link_prepare_wakeup(struct tipc_link *l)
        }
 }
 
-/**
- * tipc_link_reset_fragments - purge link's inbound message fragments queue
- * @l_ptr: pointer to link
- */
-void tipc_link_reset_fragments(struct tipc_link *l_ptr)
-{
-       kfree_skb(l_ptr->reasm_buf);
-       l_ptr->reasm_buf = NULL;
-}
-
-void tipc_link_purge_backlog(struct tipc_link *l)
-{
-       __skb_queue_purge(&l->backlogq);
-       l->backlog[TIPC_LOW_IMPORTANCE].len = 0;
-       l->backlog[TIPC_MEDIUM_IMPORTANCE].len = 0;
-       l->backlog[TIPC_HIGH_IMPORTANCE].len = 0;
-       l->backlog[TIPC_CRITICAL_IMPORTANCE].len = 0;
-       l->backlog[TIPC_SYSTEM_IMPORTANCE].len = 0;
-}
-
-/**
- * tipc_link_purge_queues - purge all pkt queues associated with link
- * @l_ptr: pointer to link
- */
-void tipc_link_purge_queues(struct tipc_link *l_ptr)
-{
-       __skb_queue_purge(&l_ptr->deferdq);
-       __skb_queue_purge(&l_ptr->transmq);
-       tipc_link_purge_backlog(l_ptr);
-       tipc_link_reset_fragments(l_ptr);
-}
-
 void tipc_link_reset(struct tipc_link *l)
 {
-       tipc_link_fsm_evt(l, LINK_RESET_EVT);
-
        /* Link is down, accept any session */
        l->peer_session = WILDCARD_SESSION;
 
@@ -589,12 +670,16 @@ void tipc_link_reset(struct tipc_link *l)
        /* Prepare for renewed mtu size negotiation */
        l->mtu = l->advertised_mtu;
 
-       /* Clean up all queues: */
+       /* Clean up all queues and counters: */
        __skb_queue_purge(&l->transmq);
        __skb_queue_purge(&l->deferdq);
        skb_queue_splice_init(&l->wakeupq, l->inputq);
-
-       tipc_link_purge_backlog(l);
+       __skb_queue_purge(&l->backlogq);
+       l->backlog[TIPC_LOW_IMPORTANCE].len = 0;
+       l->backlog[TIPC_MEDIUM_IMPORTANCE].len = 0;
+       l->backlog[TIPC_HIGH_IMPORTANCE].len = 0;
+       l->backlog[TIPC_CRITICAL_IMPORTANCE].len = 0;
+       l->backlog[TIPC_SYSTEM_IMPORTANCE].len = 0;
        kfree_skb(l->reasm_buf);
        kfree_skb(l->failover_reasm_skb);
        l->reasm_buf = NULL;
@@ -602,80 +687,14 @@ void tipc_link_reset(struct tipc_link *l)
        l->rcv_unacked = 0;
        l->snd_nxt = 1;
        l->rcv_nxt = 1;
+       l->acked = 0;
        l->silent_intv_cnt = 0;
        l->stats.recv_info = 0;
        l->stale_count = 0;
+       l->bc_peer_is_up = false;
        link_reset_statistics(l);
 }
 
-/**
- * __tipc_link_xmit(): same as tipc_link_xmit, but destlink is known & locked
- * @link: link to use
- * @list: chain of buffers containing message
- *
- * Consumes the buffer chain, except when returning an error code,
- * Returns 0 if success, or errno: -ELINKCONG, -EMSGSIZE or -ENOBUFS
- * Messages at TIPC_SYSTEM_IMPORTANCE are always accepted
- */
-int __tipc_link_xmit(struct net *net, struct tipc_link *link,
-                    struct sk_buff_head *list)
-{
-       struct tipc_msg *msg = buf_msg(skb_peek(list));
-       unsigned int maxwin = link->window;
-       unsigned int i, imp = msg_importance(msg);
-       uint mtu = link->mtu;
-       u16 ack = mod(link->rcv_nxt - 1);
-       u16 seqno = link->snd_nxt;
-       u16 bc_last_in = link->owner->bclink.last_in;
-       struct tipc_media_addr *addr = link->media_addr;
-       struct sk_buff_head *transmq = &link->transmq;
-       struct sk_buff_head *backlogq = &link->backlogq;
-       struct sk_buff *skb, *bskb;
-
-       /* Match msg importance against this and all higher backlog limits: */
-       for (i = imp; i <= TIPC_SYSTEM_IMPORTANCE; i++) {
-               if (unlikely(link->backlog[i].len >= link->backlog[i].limit))
-                       return link_schedule_user(link, list);
-       }
-       if (unlikely(msg_size(msg) > mtu))
-               return -EMSGSIZE;
-
-       /* Prepare each packet for sending, and add to relevant queue: */
-       while (skb_queue_len(list)) {
-               skb = skb_peek(list);
-               msg = buf_msg(skb);
-               msg_set_seqno(msg, seqno);
-               msg_set_ack(msg, ack);
-               msg_set_bcast_ack(msg, bc_last_in);
-
-               if (likely(skb_queue_len(transmq) < maxwin)) {
-                       __skb_dequeue(list);
-                       __skb_queue_tail(transmq, skb);
-                       tipc_bearer_send(net, link->bearer_id, skb, addr);
-                       link->rcv_unacked = 0;
-                       seqno++;
-                       continue;
-               }
-               if (tipc_msg_bundle(skb_peek_tail(backlogq), msg, mtu)) {
-                       kfree_skb(__skb_dequeue(list));
-                       link->stats.sent_bundled++;
-                       continue;
-               }
-               if (tipc_msg_make_bundle(&bskb, msg, mtu, link->addr)) {
-                       kfree_skb(__skb_dequeue(list));
-                       __skb_queue_tail(backlogq, bskb);
-                       link->backlog[msg_importance(buf_msg(bskb))].len++;
-                       link->stats.sent_bundled++;
-                       link->stats.sent_bundles++;
-                       continue;
-               }
-               link->backlog[imp].len += skb_queue_len(list);
-               skb_queue_splice_tail_init(list, backlogq);
-       }
-       link->snd_nxt = seqno;
-       return 0;
-}
-
 /**
  * tipc_link_xmit(): enqueue buffer list according to queue situation
  * @link: link to use
@@ -696,7 +715,7 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,
        unsigned int mtu = l->mtu;
        u16 ack = l->rcv_nxt - 1;
        u16 seqno = l->snd_nxt;
-       u16 bc_last_in = l->owner->bclink.last_in;
+       u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1;
        struct sk_buff_head *transmq = &l->transmq;
        struct sk_buff_head *backlogq = &l->backlogq;
        struct sk_buff *skb, *_skb, *bskb;
@@ -715,7 +734,7 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,
                hdr = buf_msg(skb);
                msg_set_seqno(hdr, seqno);
                msg_set_ack(hdr, ack);
-               msg_set_bcast_ack(hdr, bc_last_in);
+               msg_set_bcast_ack(hdr, bc_ack);
 
                if (likely(skb_queue_len(transmq) < maxwin)) {
                        _skb = skb_clone(skb, GFP_ATOMIC);
@@ -724,6 +743,7 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,
                        __skb_dequeue(list);
                        __skb_queue_tail(transmq, skb);
                        __skb_queue_tail(xmitq, _skb);
+                       TIPC_SKB_CB(skb)->ackers = l->ackers;
                        l->rcv_unacked = 0;
                        seqno++;
                        continue;
@@ -748,62 +768,13 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,
        return 0;
 }
 
-/*
- * tipc_link_sync_rcv - synchronize broadcast link endpoints.
- * Receive the sequence number where we should start receiving and
- * acking broadcast packets from a newly added peer node, and open
- * up for reception of such packets.
- *
- * Called with node locked
- */
-static void tipc_link_sync_rcv(struct tipc_node *n, struct sk_buff *buf)
-{
-       struct tipc_msg *msg = buf_msg(buf);
-
-       n->bclink.last_sent = n->bclink.last_in = msg_last_bcast(msg);
-       n->bclink.recv_permitted = true;
-       kfree_skb(buf);
-}
-
-/*
- * tipc_link_push_packets - push unsent packets to bearer
- *
- * Push out the unsent messages of a link where congestion
- * has abated. Node is locked.
- *
- * Called with node locked
- */
-void tipc_link_push_packets(struct tipc_link *link)
-{
-       struct sk_buff *skb;
-       struct tipc_msg *msg;
-       u16 seqno = link->snd_nxt;
-       u16 ack = mod(link->rcv_nxt - 1);
-
-       while (skb_queue_len(&link->transmq) < link->window) {
-               skb = __skb_dequeue(&link->backlogq);
-               if (!skb)
-                       break;
-               msg = buf_msg(skb);
-               link->backlog[msg_importance(msg)].len--;
-               msg_set_ack(msg, ack);
-               msg_set_seqno(msg, seqno);
-               seqno = mod(seqno + 1);
-               msg_set_bcast_ack(msg, link->owner->bclink.last_in);
-               link->rcv_unacked = 0;
-               __skb_queue_tail(&link->transmq, skb);
-               tipc_bearer_send(link->owner->net, link->bearer_id,
-                                skb, link->media_addr);
-       }
-       link->snd_nxt = seqno;
-}
-
 void tipc_link_advance_backlog(struct tipc_link *l, struct sk_buff_head *xmitq)
 {
        struct sk_buff *skb, *_skb;
        struct tipc_msg *hdr;
        u16 seqno = l->snd_nxt;
        u16 ack = l->rcv_nxt - 1;
+       u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1;
 
        while (skb_queue_len(&l->transmq) < l->window) {
                skb = skb_peek(&l->backlogq);
@@ -817,96 +788,35 @@ void tipc_link_advance_backlog(struct tipc_link *l, struct sk_buff_head *xmitq)
                l->backlog[msg_importance(hdr)].len--;
                __skb_queue_tail(&l->transmq, skb);
                __skb_queue_tail(xmitq, _skb);
-               msg_set_ack(hdr, ack);
+               TIPC_SKB_CB(skb)->ackers = l->ackers;
                msg_set_seqno(hdr, seqno);
-               msg_set_bcast_ack(hdr, l->owner->bclink.last_in);
+               msg_set_ack(hdr, ack);
+               msg_set_bcast_ack(hdr, bc_ack);
                l->rcv_unacked = 0;
                seqno++;
        }
        l->snd_nxt = seqno;
 }
 
-static void link_retransmit_failure(struct tipc_link *l_ptr,
-                                   struct sk_buff *buf)
-{
-       struct tipc_msg *msg = buf_msg(buf);
-       struct net *net = l_ptr->owner->net;
-
-       pr_warn("Retransmission failure on link <%s>\n", l_ptr->name);
-
-       if (l_ptr->addr) {
-               /* Handle failure on standard link */
-               link_print(l_ptr, "Resetting link ");
-               pr_info("Failed msg: usr %u, typ %u, len %u, err %u\n",
-                       msg_user(msg), msg_type(msg), msg_size(msg),
-                       msg_errcode(msg));
-               pr_info("sqno %u, prev: %x, src: %x\n",
-                       msg_seqno(msg), msg_prevnode(msg), msg_orignode(msg));
-       } else {
-               /* Handle failure on broadcast link */
-               struct tipc_node *n_ptr;
-               char addr_string[16];
-
-               pr_info("Msg seq number: %u,  ", msg_seqno(msg));
-               pr_cont("Outstanding acks: %lu\n",
-                       (unsigned long) TIPC_SKB_CB(buf)->handle);
-
-               n_ptr = tipc_bclink_retransmit_to(net);
-
-               tipc_addr_string_fill(addr_string, n_ptr->addr);
-               pr_info("Broadcast link info for %s\n", addr_string);
-               pr_info("Reception permitted: %d,  Acked: %u\n",
-                       n_ptr->bclink.recv_permitted,
-                       n_ptr->bclink.acked);
-               pr_info("Last in: %u,  Oos state: %u,  Last sent: %u\n",
-                       n_ptr->bclink.last_in,
-                       n_ptr->bclink.oos_state,
-                       n_ptr->bclink.last_sent);
-
-               n_ptr->action_flags |= TIPC_BCAST_RESET;
-               l_ptr->stale_count = 0;
-       }
-}
-
-void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *skb,
-                         u32 retransmits)
+static void link_retransmit_failure(struct tipc_link *l, struct sk_buff *skb)
 {
-       struct tipc_msg *msg;
-
-       if (!skb)
-               return;
-
-       msg = buf_msg(skb);
-
-       /* Detect repeated retransmit failures */
-       if (l_ptr->last_retransm == msg_seqno(msg)) {
-               if (++l_ptr->stale_count > 100) {
-                       link_retransmit_failure(l_ptr, skb);
-                       return;
-               }
-       } else {
-               l_ptr->last_retransm = msg_seqno(msg);
-               l_ptr->stale_count = 1;
-       }
+       struct tipc_msg *hdr = buf_msg(skb);
 
-       skb_queue_walk_from(&l_ptr->transmq, skb) {
-               if (!retransmits)
-                       break;
-               msg = buf_msg(skb);
-               msg_set_ack(msg, mod(l_ptr->rcv_nxt - 1));
-               msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
-               tipc_bearer_send(l_ptr->owner->net, l_ptr->bearer_id, skb,
-                                l_ptr->media_addr);
-               retransmits--;
-               l_ptr->stats.retransmitted++;
-       }
+       pr_warn("Retransmission failure on link <%s>\n", l->name);
+       link_print(l, "Resetting link ");
+       pr_info("Failed msg: usr %u, typ %u, len %u, err %u\n",
+               msg_user(hdr), msg_type(hdr), msg_size(hdr), msg_errcode(hdr));
+       pr_info("sqno %u, prev: %x, src: %x\n",
+               msg_seqno(hdr), msg_prevnode(hdr), msg_orignode(hdr));
 }
 
-static int tipc_link_retransm(struct tipc_link *l, int retransm,
-                             struct sk_buff_head *xmitq)
+int tipc_link_retrans(struct tipc_link *l, u16 from, u16 to,
+                     struct sk_buff_head *xmitq)
 {
        struct sk_buff *_skb, *skb = skb_peek(&l->transmq);
        struct tipc_msg *hdr;
+       u16 ack = l->rcv_nxt - 1;
+       u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1;
 
        if (!skb)
                return 0;
@@ -919,19 +829,25 @@ static int tipc_link_retransm(struct tipc_link *l, int retransm,
                link_retransmit_failure(l, skb);
                return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
        }
+
+       /* Move forward to where retransmission should start */
        skb_queue_walk(&l->transmq, skb) {
-               if (!retransm)
-                       return 0;
+               if (!less(buf_seqno(skb), from))
+                       break;
+       }
+
+       skb_queue_walk_from(&l->transmq, skb) {
+               if (more(buf_seqno(skb), to))
+                       break;
                hdr = buf_msg(skb);
                _skb = __pskb_copy(skb, MIN_H_SIZE, GFP_ATOMIC);
                if (!_skb)
                        return 0;
                hdr = buf_msg(_skb);
-               msg_set_ack(hdr, l->rcv_nxt - 1);
-               msg_set_bcast_ack(hdr, l->owner->bclink.last_in);
+               msg_set_ack(hdr, ack);
+               msg_set_bcast_ack(hdr, bc_ack);
                _skb->priority = TC_PRIO_CONTROL;
                __skb_queue_tail(xmitq, _skb);
-               retransm--;
                l->stats.retransmitted++;
        }
        return 0;
@@ -942,22 +858,20 @@ static int tipc_link_retransm(struct tipc_link *l, int retransm,
  * Consumes buffer if message is of right type
  * Node lock must be held
  */
-static bool tipc_data_input(struct tipc_link *link, struct sk_buff *skb,
+static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb,
                            struct sk_buff_head *inputq)
 {
-       struct tipc_node *node = link->owner;
-
        switch (msg_user(buf_msg(skb))) {
        case TIPC_LOW_IMPORTANCE:
        case TIPC_MEDIUM_IMPORTANCE:
        case TIPC_HIGH_IMPORTANCE:
        case TIPC_CRITICAL_IMPORTANCE:
        case CONN_MANAGER:
-               __skb_queue_tail(inputq, skb);
+               skb_queue_tail(inputq, skb);
                return true;
        case NAME_DISTRIBUTOR:
-               node->bclink.recv_permitted = true;
-               skb_queue_tail(link->namedq, skb);
+               l->bc_rcvlink->state = LINK_ESTABLISHED;
+               skb_queue_tail(l->namedq, skb);
                return true;
        case MSG_BUNDLER:
        case TUNNEL_PROTOCOL:
@@ -978,10 +892,10 @@ static bool tipc_data_input(struct tipc_link *link, struct sk_buff *skb,
 static int tipc_link_input(struct tipc_link *l, struct sk_buff *skb,
                           struct sk_buff_head *inputq)
 {
-       struct tipc_node *node = l->owner;
        struct tipc_msg *hdr = buf_msg(skb);
        struct sk_buff **reasm_skb = &l->reasm_buf;
        struct sk_buff *iskb;
+       struct sk_buff_head tmpq;
        int usr = msg_user(hdr);
        int rc = 0;
        int pos = 0;
@@ -1006,23 +920,27 @@ static int tipc_link_input(struct tipc_link *l, struct sk_buff *skb,
        }
 
        if (usr == MSG_BUNDLER) {
+               skb_queue_head_init(&tmpq);
                l->stats.recv_bundles++;
                l->stats.recv_bundled += msg_msgcnt(hdr);
                while (tipc_msg_extract(skb, &iskb, &pos))
-                       tipc_data_input(l, iskb, inputq);
+                       tipc_data_input(l, iskb, &tmpq);
+               tipc_skb_queue_splice_tail(&tmpq, inputq);
                return 0;
        } else if (usr == MSG_FRAGMENTER) {
                l->stats.recv_fragments++;
                if (tipc_buf_append(reasm_skb, &skb)) {
                        l->stats.recv_fragmented++;
                        tipc_data_input(l, skb, inputq);
-               } else if (!*reasm_skb) {
+               } else if (!*reasm_skb && !link_is_bc_rcvlink(l)) {
+                       pr_warn_ratelimited("Unable to build fragment list\n");
                        return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
                }
                return 0;
        } else if (usr == BCAST_PROTOCOL) {
-               tipc_link_sync_rcv(node, skb);
-               return 0;
+               tipc_bcast_lock(l->net);
+               tipc_link_bc_init_rcv(l->bc_rcvlink, hdr);
+               tipc_bcast_unlock(l->net);
        }
 drop:
        kfree_skb(skb);
@@ -1044,49 +962,95 @@ static bool tipc_link_release_pkts(struct tipc_link *l, u16 acked)
        return released;
 }
 
+/* tipc_link_build_ack_msg: prepare link acknowledge message for transmission
+ *
+ * Note that sending of broadcast ack is coordinated among nodes, to reduce
+ * risk of ack storms towards the sender
+ */
+int tipc_link_build_ack_msg(struct tipc_link *l, struct sk_buff_head *xmitq)
+{
+       if (!l)
+               return 0;
+
+       /* Broadcast ACK must be sent via a unicast link => defer to caller */
+       if (link_is_bc_rcvlink(l)) {
+               if (((l->rcv_nxt ^ link_own_addr(l)) & 0xf) != 0xf)
+                       return 0;
+               l->rcv_unacked = 0;
+               return TIPC_LINK_SND_BC_ACK;
+       }
+
+       /* Unicast ACK */
+       l->rcv_unacked = 0;
+       l->stats.sent_acks++;
+       tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, xmitq);
+       return 0;
+}
+
+/* tipc_link_build_reset_msg: prepare link RESET or ACTIVATE message
+ */
+void tipc_link_build_reset_msg(struct tipc_link *l, struct sk_buff_head *xmitq)
+{
+       int mtyp = RESET_MSG;
+
+       if (l->state == LINK_ESTABLISHING)
+               mtyp = ACTIVATE_MSG;
+
+       tipc_link_build_proto_msg(l, mtyp, 0, 0, 0, 0, xmitq);
+}
+
+/* tipc_link_build_nack_msg: prepare link nack message for transmission
+ */
+static void tipc_link_build_nack_msg(struct tipc_link *l,
+                                    struct sk_buff_head *xmitq)
+{
+       u32 def_cnt = ++l->stats.deferred_recv;
+
+       if (link_is_bc_rcvlink(l))
+               return;
+
+       if ((skb_queue_len(&l->deferdq) == 1) || !(def_cnt % TIPC_NACK_INTV))
+               tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, xmitq);
+}
+
 /* tipc_link_rcv - process TIPC packets/messages arriving from off-node
- * @link: the link that should handle the message
+ * @l: the link that should handle the message
  * @skb: TIPC packet
  * @xmitq: queue to place packets to be sent after this call
  */
 int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,
                  struct sk_buff_head *xmitq)
 {
-       struct sk_buff_head *arrvq = &l->deferdq;
-       struct sk_buff_head tmpq;
+       struct sk_buff_head *defq = &l->deferdq;
        struct tipc_msg *hdr;
-       u16 seqno, rcv_nxt;
+       u16 seqno, rcv_nxt, win_lim;
        int rc = 0;
 
-       __skb_queue_head_init(&tmpq);
-
-       if (unlikely(!__tipc_skb_queue_sorted(arrvq, skb))) {
-               if (!(skb_queue_len(arrvq) % TIPC_NACK_INTV))
-                       tipc_link_build_proto_msg(l, STATE_MSG, 0,
-                                                 0, 0, 0, xmitq);
-               return rc;
-       }
-
-       while ((skb = skb_peek(arrvq))) {
+       do {
                hdr = buf_msg(skb);
+               seqno = msg_seqno(hdr);
+               rcv_nxt = l->rcv_nxt;
+               win_lim = rcv_nxt + TIPC_MAX_LINK_WIN;
 
                /* Verify and update link state */
-               if (unlikely(msg_user(hdr) == LINK_PROTOCOL)) {
-                       __skb_dequeue(arrvq);
-                       rc = tipc_link_proto_rcv(l, skb, xmitq);
-                       continue;
-               }
+               if (unlikely(msg_user(hdr) == LINK_PROTOCOL))
+                       return tipc_link_proto_rcv(l, skb, xmitq);
 
                if (unlikely(!link_is_up(l))) {
-                       rc = tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT);
-                       if (!link_is_up(l)) {
-                               kfree_skb(__skb_dequeue(arrvq));
-                               goto exit;
-                       }
+                       if (l->state == LINK_ESTABLISHING)
+                               rc = TIPC_LINK_UP_EVT;
+                       goto drop;
                }
 
+               /* Don't send probe at next timeout expiration */
                l->silent_intv_cnt = 0;
 
+               /* Drop if outside receive window */
+               if (unlikely(less(seqno, rcv_nxt) || more(seqno, win_lim))) {
+                       l->stats.duplicates++;
+                       goto drop;
+               }
+
                /* Forward queues and wake up waiting users */
                if (likely(tipc_link_release_pkts(l, msg_ack(hdr)))) {
                        tipc_link_advance_backlog(l, xmitq);
@@ -1094,79 +1058,28 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,
                                link_prepare_wakeup(l);
                }
 
-               /* Defer reception if there is a gap in the sequence */
-               seqno = msg_seqno(hdr);
-               rcv_nxt = l->rcv_nxt;
-               if (unlikely(less(rcv_nxt, seqno))) {
-                       l->stats.deferred_recv++;
-                       goto exit;
-               }
-
-               __skb_dequeue(arrvq);
-
-               /* Drop if packet already received */
-               if (unlikely(more(rcv_nxt, seqno))) {
-                       l->stats.duplicates++;
-                       kfree_skb(skb);
-                       goto exit;
+               /* Defer delivery if sequence gap */
+               if (unlikely(seqno != rcv_nxt)) {
+                       __tipc_skb_queue_sorted(defq, seqno, skb);
+                       tipc_link_build_nack_msg(l, xmitq);
+                       break;
                }
 
-               /* Packet can be delivered */
+               /* Deliver packet */
                l->rcv_nxt++;
                l->stats.recv_info++;
-               if (unlikely(!tipc_data_input(l, skb, &tmpq)))
-                       rc = tipc_link_input(l, skb, &tmpq);
-
-               /* Ack at regular intervals */
-               if (unlikely(++l->rcv_unacked >= TIPC_MIN_LINK_WIN)) {
-                       l->rcv_unacked = 0;
-                       l->stats.sent_acks++;
-                       tipc_link_build_proto_msg(l, STATE_MSG,
-                                                 0, 0, 0, 0, xmitq);
-               }
-       }
-exit:
-       tipc_skb_queue_splice_tail(&tmpq, l->inputq);
-       return rc;
-}
-
-/**
- * tipc_link_defer_pkt - Add out-of-sequence message to deferred reception queue
- *
- * Returns increase in queue length (i.e. 0 or 1)
- */
-u32 tipc_link_defer_pkt(struct sk_buff_head *list, struct sk_buff *skb)
-{
-       struct sk_buff *skb1;
-       u16 seq_no = buf_seqno(skb);
-
-       /* Empty queue ? */
-       if (skb_queue_empty(list)) {
-               __skb_queue_tail(list, skb);
-               return 1;
-       }
-
-       /* Last ? */
-       if (less(buf_seqno(skb_peek_tail(list)), seq_no)) {
-               __skb_queue_tail(list, skb);
-               return 1;
-       }
-
-       /* Locate insertion point in queue, then insert; discard if duplicate */
-       skb_queue_walk(list, skb1) {
-               u16 curr_seqno = buf_seqno(skb1);
-
-               if (seq_no == curr_seqno) {
-                       kfree_skb(skb);
-                       return 0;
-               }
-
-               if (less(seq_no, curr_seqno))
+               if (!tipc_data_input(l, skb, l->inputq))
+                       rc |= tipc_link_input(l, skb, l->inputq);
+               if (unlikely(++l->rcv_unacked >= TIPC_MIN_LINK_WIN))
+                       rc |= tipc_link_build_ack_msg(l, xmitq);
+               if (unlikely(rc & ~TIPC_LINK_SND_BC_ACK))
                        break;
-       }
+       } while ((skb = __skb_dequeue(defq)));
 
-       __skb_queue_before(list, skb1, skb);
-       return 1;
+       return rc;
+drop:
+       kfree_skb(skb);
+       return rc;
 }
 
 /*
@@ -1184,23 +1097,17 @@ void tipc_link_proto_xmit(struct tipc_link *l, u32 msg_typ, int probe_msg,
        skb = __skb_dequeue(&xmitq);
        if (!skb)
                return;
-       tipc_bearer_send(l->owner->net, l->bearer_id, skb, l->media_addr);
+       tipc_bearer_xmit_skb(l->net, l->bearer_id, skb, l->media_addr);
        l->rcv_unacked = 0;
-       kfree_skb(skb);
 }
 
-/* tipc_link_build_proto_msg: prepare link protocol message for transmission
- */
 static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
                                      u16 rcvgap, int tolerance, int priority,
                                      struct sk_buff_head *xmitq)
 {
        struct sk_buff *skb = NULL;
        struct tipc_msg *hdr = l->pmsg;
-       u16 snd_nxt = l->snd_nxt;
-       u16 rcv_nxt = l->rcv_nxt;
-       u16 rcv_last = rcv_nxt - 1;
-       int node_up = l->owner->bclink.recv_permitted;
+       bool node_up = link_is_up(l->bc_rcvlink);
 
        /* Don't send protocol message during reset or link failover */
        if (tipc_link_is_blocked(l))
@@ -1208,33 +1115,34 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
 
        msg_set_type(hdr, mtyp);
        msg_set_net_plane(hdr, l->net_plane);
-       msg_set_bcast_ack(hdr, l->owner->bclink.last_in);
-       msg_set_last_bcast(hdr, tipc_bclink_get_last_sent(l->owner->net));
+       msg_set_next_sent(hdr, l->snd_nxt);
+       msg_set_ack(hdr, l->rcv_nxt - 1);
+       msg_set_bcast_ack(hdr, l->bc_rcvlink->rcv_nxt - 1);
+       msg_set_last_bcast(hdr, l->bc_sndlink->snd_nxt - 1);
        msg_set_link_tolerance(hdr, tolerance);
        msg_set_linkprio(hdr, priority);
        msg_set_redundant_link(hdr, node_up);
        msg_set_seq_gap(hdr, 0);
 
        /* Compatibility: created msg must not be in sequence with pkt flow */
-       msg_set_seqno(hdr, snd_nxt + U16_MAX / 2);
+       msg_set_seqno(hdr, l->snd_nxt + U16_MAX / 2);
 
        if (mtyp == STATE_MSG) {
                if (!tipc_link_is_up(l))
                        return;
-               msg_set_next_sent(hdr, snd_nxt);
 
                /* Override rcvgap if there are packets in deferred queue */
                if (!skb_queue_empty(&l->deferdq))
-                       rcvgap = buf_seqno(skb_peek(&l->deferdq)) - rcv_nxt;
+                       rcvgap = buf_seqno(skb_peek(&l->deferdq)) - l->rcv_nxt;
                if (rcvgap) {
                        msg_set_seq_gap(hdr, rcvgap);
                        l->stats.sent_nacks++;
                }
-               msg_set_ack(hdr, rcv_last);
                msg_set_probe(hdr, probe);
                if (probe)
                        l->stats.sent_probes++;
                l->stats.sent_states++;
+               l->rcv_unacked = 0;
        } else {
                /* RESET_MSG or ACTIVATE_MSG */
                msg_set_max_pkt(hdr, l->advertised_mtu);
@@ -1250,7 +1158,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
 }
 
 /* tipc_link_tnl_prepare(): prepare and return a list of tunnel packets
- * with contents of the link's tranmsit and backlog queues.
+ * with contents of the link's transmit and backlog queues.
  */
 void tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl,
                           int mtyp, struct sk_buff_head *xmitq)
@@ -1326,21 +1234,23 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
 {
        struct tipc_msg *hdr = buf_msg(skb);
        u16 rcvgap = 0;
-       u16 nacked_gap = msg_seq_gap(hdr);
+       u16 ack = msg_ack(hdr);
+       u16 gap = msg_seq_gap(hdr);
        u16 peers_snd_nxt =  msg_next_sent(hdr);
        u16 peers_tol = msg_link_tolerance(hdr);
        u16 peers_prio = msg_linkprio(hdr);
        u16 rcv_nxt = l->rcv_nxt;
+       int mtyp = msg_type(hdr);
        char *if_name;
        int rc = 0;
 
-       if (tipc_link_is_blocked(l))
+       if (tipc_link_is_blocked(l) || !xmitq)
                goto exit;
 
        if (link_own_addr(l) > msg_prevnode(hdr))
                l->net_plane = msg_net_plane(hdr);
 
-       switch (msg_type(hdr)) {
+       switch (mtyp) {
        case RESET_MSG:
 
                /* Ignore duplicate RESET with old session number */
@@ -1367,12 +1277,14 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
                if (in_range(peers_prio, l->priority + 1, TIPC_MAX_LINK_PRI))
                        l->priority = peers_prio;
 
-               if (msg_type(hdr) == RESET_MSG) {
-                       rc |= tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT);
-               } else if (!link_is_up(l)) {
-                       tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT);
-                       rc |= tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT);
-               }
+               /* ACTIVATE_MSG serves as PEER_RESET if link is already down */
+               if ((mtyp == RESET_MSG) || !link_is_up(l))
+                       rc = tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT);
+
+               /* ACTIVATE_MSG takes up link if it was already locally reset */
+               if ((mtyp == ACTIVATE_MSG) && (l->state == LINK_ESTABLISHING))
+                       rc = TIPC_LINK_UP_EVT;
+
                l->peer_session = msg_session(hdr);
                l->peer_bearer_id = msg_bearer_id(hdr);
                if (l->mtu > msg_max_pkt(hdr))
@@ -1389,9 +1301,12 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
                l->stats.recv_states++;
                if (msg_probe(hdr))
                        l->stats.recv_probes++;
-               rc = tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT);
-               if (!link_is_up(l))
+
+               if (!link_is_up(l)) {
+                       if (l->state == LINK_ESTABLISHING)
+                               rc = TIPC_LINK_UP_EVT;
                        break;
+               }
 
                /* Send NACK if peer has sent pkts we haven't received yet */
                if (more(peers_snd_nxt, rcv_nxt) && !tipc_link_is_synching(l))
@@ -1399,11 +1314,11 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
                if (rcvgap || (msg_probe(hdr)))
                        tipc_link_build_proto_msg(l, STATE_MSG, 0, rcvgap,
                                                  0, 0, xmitq);
-               tipc_link_release_pkts(l, msg_ack(hdr));
+               tipc_link_release_pkts(l, ack);
 
                /* If NACK, retransmit will now start at right position */
-               if (nacked_gap) {
-                       rc = tipc_link_retransm(l, nacked_gap, xmitq);
+               if (gap) {
+                       rc = tipc_link_retrans(l, ack + 1, ack + gap, xmitq);
                        l->stats.recv_nacks++;
                }
 
@@ -1416,6 +1331,188 @@ exit:
        return rc;
 }
 
+/* tipc_link_build_bc_proto_msg() - create broadcast protocol message
+ */
+static bool tipc_link_build_bc_proto_msg(struct tipc_link *l, bool bcast,
+                                        u16 peers_snd_nxt,
+                                        struct sk_buff_head *xmitq)
+{
+       struct sk_buff *skb;
+       struct tipc_msg *hdr;
+       struct sk_buff *dfrd_skb = skb_peek(&l->deferdq);
+       u16 ack = l->rcv_nxt - 1;
+       u16 gap_to = peers_snd_nxt - 1;
+
+       skb = tipc_msg_create(BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE,
+                             0, l->addr, link_own_addr(l), 0, 0, 0);
+       if (!skb)
+               return false;
+       hdr = buf_msg(skb);
+       msg_set_last_bcast(hdr, l->bc_sndlink->snd_nxt - 1);
+       msg_set_bcast_ack(hdr, ack);
+       msg_set_bcgap_after(hdr, ack);
+       if (dfrd_skb)
+               gap_to = buf_seqno(dfrd_skb) - 1;
+       msg_set_bcgap_to(hdr, gap_to);
+       msg_set_non_seq(hdr, bcast);
+       __skb_queue_tail(xmitq, skb);
+       return true;
+}
+
+/* tipc_link_build_bc_init_msg() - synchronize broadcast link endpoints.
+ *
+ * Give a newly added peer node the sequence number where it should
+ * start receiving and acking broadcast packets.
+ */
+static void tipc_link_build_bc_init_msg(struct tipc_link *l,
+                                       struct sk_buff_head *xmitq)
+{
+       struct sk_buff_head list;
+
+       __skb_queue_head_init(&list);
+       if (!tipc_link_build_bc_proto_msg(l->bc_rcvlink, false, 0, &list))
+               return;
+       tipc_link_xmit(l, &list, xmitq);
+}
+
+/* tipc_link_bc_init_rcv - receive initial broadcast synch data from peer
+ */
+void tipc_link_bc_init_rcv(struct tipc_link *l, struct tipc_msg *hdr)
+{
+       int mtyp = msg_type(hdr);
+       u16 peers_snd_nxt = msg_bc_snd_nxt(hdr);
+
+       if (link_is_up(l))
+               return;
+
+       if (msg_user(hdr) == BCAST_PROTOCOL) {
+               l->rcv_nxt = peers_snd_nxt;
+               l->state = LINK_ESTABLISHED;
+               return;
+       }
+
+       if (l->peer_caps & TIPC_BCAST_SYNCH)
+               return;
+
+       if (msg_peer_node_is_up(hdr))
+               return;
+
+       /* Compatibility: accept older, less safe initial synch data */
+       if ((mtyp == RESET_MSG) || (mtyp == ACTIVATE_MSG))
+               l->rcv_nxt = peers_snd_nxt;
+}
+
+/* tipc_link_bc_sync_rcv - update rcv link according to peer's send state
+ */
+void tipc_link_bc_sync_rcv(struct tipc_link *l, struct tipc_msg *hdr,
+                          struct sk_buff_head *xmitq)
+{
+       u16 peers_snd_nxt = msg_bc_snd_nxt(hdr);
+
+       if (!link_is_up(l))
+               return;
+
+       if (!msg_peer_node_is_up(hdr))
+               return;
+
+       l->bc_peer_is_up = true;
+
+       /* Ignore if peers_snd_nxt goes beyond receive window */
+       if (more(peers_snd_nxt, l->rcv_nxt + l->window))
+               return;
+
+       if (!more(peers_snd_nxt, l->rcv_nxt)) {
+               l->nack_state = BC_NACK_SND_CONDITIONAL;
+               return;
+       }
+
+       /* Don't NACK if one was recently sent or peeked */
+       if (l->nack_state == BC_NACK_SND_SUPPRESS) {
+               l->nack_state = BC_NACK_SND_UNCONDITIONAL;
+               return;
+       }
+
+       /* Conditionally delay NACK sending until next synch rcv */
+       if (l->nack_state == BC_NACK_SND_CONDITIONAL) {
+               l->nack_state = BC_NACK_SND_UNCONDITIONAL;
+               if ((peers_snd_nxt - l->rcv_nxt) < TIPC_MIN_LINK_WIN)
+                       return;
+       }
+
+       /* Send NACK now but suppress next one */
+       tipc_link_build_bc_proto_msg(l, true, peers_snd_nxt, xmitq);
+       l->nack_state = BC_NACK_SND_SUPPRESS;
+}
+
+void tipc_link_bc_ack_rcv(struct tipc_link *l, u16 acked,
+                         struct sk_buff_head *xmitq)
+{
+       struct sk_buff *skb, *tmp;
+       struct tipc_link *snd_l = l->bc_sndlink;
+
+       if (!link_is_up(l) || !l->bc_peer_is_up)
+               return;
+
+       if (!more(acked, l->acked))
+               return;
+
+       /* Skip over packets peer has already acked */
+       skb_queue_walk(&snd_l->transmq, skb) {
+               if (more(buf_seqno(skb), l->acked))
+                       break;
+       }
+
+       /* Update/release the packets peer is acking now */
+       skb_queue_walk_from_safe(&snd_l->transmq, skb, tmp) {
+               if (more(buf_seqno(skb), acked))
+                       break;
+               if (!--TIPC_SKB_CB(skb)->ackers) {
+                       __skb_unlink(skb, &snd_l->transmq);
+                       kfree_skb(skb);
+               }
+       }
+       l->acked = acked;
+       tipc_link_advance_backlog(snd_l, xmitq);
+       if (unlikely(!skb_queue_empty(&snd_l->wakeupq)))
+               link_prepare_wakeup(snd_l);
+}
+
+/* tipc_link_bc_nack_rcv(): receive broadcast nack message
+ */
+int tipc_link_bc_nack_rcv(struct tipc_link *l, struct sk_buff *skb,
+                         struct sk_buff_head *xmitq)
+{
+       struct tipc_msg *hdr = buf_msg(skb);
+       u32 dnode = msg_destnode(hdr);
+       int mtyp = msg_type(hdr);
+       u16 acked = msg_bcast_ack(hdr);
+       u16 from = acked + 1;
+       u16 to = msg_bcgap_to(hdr);
+       u16 peers_snd_nxt = to + 1;
+       int rc = 0;
+
+       kfree_skb(skb);
+
+       if (!tipc_link_is_up(l) || !l->bc_peer_is_up)
+               return 0;
+
+       if (mtyp != STATE_MSG)
+               return 0;
+
+       if (dnode == link_own_addr(l)) {
+               tipc_link_bc_ack_rcv(l, acked, xmitq);
+               rc = tipc_link_retrans(l->bc_sndlink, from, to, xmitq);
+               l->stats.recv_nacks++;
+               return rc;
+       }
+
+       /* Msg for other node => suppress own NACK at next sync if applicable */
+       if (more(peers_snd_nxt, l->rcv_nxt) && !less(l->rcv_nxt, from))
+               l->nack_state = BC_NACK_SND_SUPPRESS;
+
+       return 0;
+}
+
 void tipc_link_set_queue_limits(struct tipc_link *l, u32 win)
 {
        int max_bulk = TIPC_MAX_PUBLICATIONS / (l->mtu / ITEM_SIZE);
@@ -1480,7 +1577,7 @@ static void link_reset_statistics(struct tipc_link *l_ptr)
 static void link_print(struct tipc_link *l, const char *str)
 {
        struct sk_buff *hskb = skb_peek(&l->transmq);
-       u16 head = hskb ? msg_seqno(buf_msg(hskb)) : l->snd_nxt;
+       u16 head = hskb ? msg_seqno(buf_msg(hskb)) : l->snd_nxt - 1;
        u16 tail = l->snd_nxt - 1;
 
        pr_info("%s Link <%s> state %x\n", str, l->name, l->state);
@@ -1704,7 +1801,7 @@ static int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
        if (tipc_link_is_up(link))
                if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
                        goto attr_msg_full;
-       if (tipc_link_is_active(link))
+       if (link->active)
                if (nla_put_flag(msg->skb, TIPC_NLA_LINK_ACTIVE))
                        goto attr_msg_full;
 
index 39ff8b6919a4271d31892992098be0fcf98acd0f..66d859b66c84f3a21f7b9115f75a9114fd198744 100644 (file)
@@ -66,7 +66,8 @@ enum {
  */
 enum {
        TIPC_LINK_UP_EVT       = 1,
-       TIPC_LINK_DOWN_EVT     = (1 << 1)
+       TIPC_LINK_DOWN_EVT     = (1 << 1),
+       TIPC_LINK_SND_BC_ACK   = (1 << 2)
 };
 
 /* Starting value for maximum packet size negotiation on unicast links
@@ -110,7 +111,7 @@ struct tipc_stats {
  * @name: link name character string
  * @media_addr: media address to use when sending messages over link
  * @timer: link timer
- * @owner: pointer to peer node
+ * @net: pointer to namespace struct
  * @refcnt: reference counter for permanent references (owner node & timer)
  * @peer_session: link session # being used by peer end of link
  * @peer_bearer_id: bearer id used by link's peer endpoint
@@ -119,6 +120,7 @@ struct tipc_stats {
  * @keepalive_intv: link keepalive timer interval
  * @abort_limit: # of unacknowledged continuity probes needed to reset link
  * @state: current state of link FSM
+ * @peer_caps: bitmap describing capabilities of peer node
  * @silent_intv_cnt: # of timer intervals without any reception from peer
  * @proto_msg: template for control messages generated by link
  * @pmsg: convenience pointer to "proto_msg" field
@@ -134,6 +136,8 @@ struct tipc_stats {
  * @snt_nxt: next sequence number to use for outbound messages
  * @last_retransmitted: sequence number of most recently retransmitted message
  * @stale_count: # of identical retransmit requests made by peer
+ * @ackers: # of peers that needs to ack each packet before it can be released
+ * @acked: # last packet acked by a certain peer. Used for broadcast.
  * @rcv_nxt: next sequence number to expect for inbound messages
  * @deferred_queue: deferred queue saved OOS b'cast message received from node
  * @unacked_window: # of inbound messages rx'd without ack'ing back to peer
@@ -143,13 +147,14 @@ struct tipc_stats {
  * @wakeupq: linked list of wakeup msgs waiting for link congestion to abate
  * @long_msg_seq_no: next identifier to use for outbound fragmented messages
  * @reasm_buf: head of partially reassembled inbound message fragments
+ * @bc_rcvr: marks that this is a broadcast receiver link
  * @stats: collects statistics regarding link activity
  */
 struct tipc_link {
        u32 addr;
        char name[TIPC_MAX_LINK_NAME];
        struct tipc_media_addr *media_addr;
-       struct tipc_node *owner;
+       struct net *net;
 
        /* Management and link supervision data */
        u32 peer_session;
@@ -159,6 +164,8 @@ struct tipc_link {
        unsigned long keepalive_intv;
        u32 abort_limit;
        u32 state;
+       u16 peer_caps;
+       bool active;
        u32 silent_intv_cnt;
        struct {
                unchar hdr[INT_H_SIZE];
@@ -185,7 +192,7 @@ struct tipc_link {
        } backlog[5];
        u16 snd_nxt;
        u16 last_retransm;
-       u32 window;
+       u16 window;
        u32 stale_count;
 
        /* Reception */
@@ -201,42 +208,50 @@ struct tipc_link {
        /* Fragmentation/reassembly */
        struct sk_buff *reasm_buf;
 
+       /* Broadcast */
+       u16 ackers;
+       u16 acked;
+       struct tipc_link *bc_rcvlink;
+       struct tipc_link *bc_sndlink;
+       int nack_state;
+       bool bc_peer_is_up;
+
        /* Statistics */
        struct tipc_stats stats;
 };
 
-bool tipc_link_create(struct tipc_node *n, struct tipc_bearer *b, u32 session,
-                     u32 ownnode, u32 peer, struct tipc_media_addr *maddr,
-                     struct sk_buff_head *inputq, struct sk_buff_head *namedq,
+bool tipc_link_create(struct net *net, char *if_name, int bearer_id,
+                     int tolerance, char net_plane, u32 mtu, int priority,
+                     int window, u32 session, u32 ownnode, u32 peer,
+                     u16 peer_caps,
+                     struct tipc_link *bc_sndlink,
+                     struct tipc_link *bc_rcvlink,
+                     struct sk_buff_head *inputq,
+                     struct sk_buff_head *namedq,
                      struct tipc_link **link);
+bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer,
+                        int mtu, int window, u16 peer_caps,
+                        struct sk_buff_head *inputq,
+                        struct sk_buff_head *namedq,
+                        struct tipc_link *bc_sndlink,
+                        struct tipc_link **link);
 void tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl,
                           int mtyp, struct sk_buff_head *xmitq);
-void tipc_link_build_bcast_sync_msg(struct tipc_link *l,
-                                   struct sk_buff_head *xmitq);
+void tipc_link_build_reset_msg(struct tipc_link *l, struct sk_buff_head *xmitq);
 int tipc_link_fsm_evt(struct tipc_link *l, int evt);
 void tipc_link_reset_fragments(struct tipc_link *l_ptr);
 bool tipc_link_is_up(struct tipc_link *l);
+bool tipc_link_peer_is_down(struct tipc_link *l);
 bool tipc_link_is_reset(struct tipc_link *l);
+bool tipc_link_is_establishing(struct tipc_link *l);
 bool tipc_link_is_synching(struct tipc_link *l);
 bool tipc_link_is_failingover(struct tipc_link *l);
 bool tipc_link_is_blocked(struct tipc_link *l);
-int tipc_link_is_active(struct tipc_link *l_ptr);
-void tipc_link_purge_queues(struct tipc_link *l_ptr);
-void tipc_link_purge_backlog(struct tipc_link *l);
+void tipc_link_set_active(struct tipc_link *l, bool active);
 void tipc_link_reset(struct tipc_link *l_ptr);
-int __tipc_link_xmit(struct net *net, struct tipc_link *link,
-                    struct sk_buff_head *list);
 int tipc_link_xmit(struct tipc_link *link,     struct sk_buff_head *list,
                   struct sk_buff_head *xmitq);
-void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int prob,
-                         u32 gap, u32 tolerance, u32 priority);
-void tipc_link_push_packets(struct tipc_link *l_ptr);
-u32 tipc_link_defer_pkt(struct sk_buff_head *list, struct sk_buff *buf);
-void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window);
-void tipc_link_retransmit(struct tipc_link *l_ptr,
-                         struct sk_buff *start, u32 retransmits);
-struct sk_buff *tipc_skb_queue_next(const struct sk_buff_head *list,
-                                   const struct sk_buff *skb);
+void tipc_link_set_queue_limits(struct tipc_link *l, u32 window);
 
 int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb);
 int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info);
@@ -246,5 +261,23 @@ int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[]);
 int tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq);
 int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,
                  struct sk_buff_head *xmitq);
-
+int tipc_link_build_ack_msg(struct tipc_link *l, struct sk_buff_head *xmitq);
+void tipc_link_add_bc_peer(struct tipc_link *snd_l,
+                          struct tipc_link *uc_l,
+                          struct sk_buff_head *xmitq);
+void tipc_link_remove_bc_peer(struct tipc_link *snd_l,
+                             struct tipc_link *rcv_l,
+                             struct sk_buff_head *xmitq);
+int tipc_link_bc_peers(struct tipc_link *l);
+void tipc_link_set_mtu(struct tipc_link *l, int mtu);
+int tipc_link_mtu(struct tipc_link *l);
+void tipc_link_bc_ack_rcv(struct tipc_link *l, u16 acked,
+                         struct sk_buff_head *xmitq);
+void tipc_link_build_bc_sync_msg(struct tipc_link *l,
+                                struct sk_buff_head *xmitq);
+void tipc_link_bc_init_rcv(struct tipc_link *l, struct tipc_msg *hdr);
+void tipc_link_bc_sync_rcv(struct tipc_link *l,   struct tipc_msg *hdr,
+                          struct sk_buff_head *xmitq);
+int tipc_link_bc_nack_rcv(struct tipc_link *l, struct sk_buff *skb,
+                         struct sk_buff_head *xmitq);
 #endif
index c5ac436235e0823c016123394fef6a0cf321092c..8740930f07872ff1ec8ff9d6b712b2772ba48336 100644 (file)
@@ -121,7 +121,7 @@ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
 {
        struct sk_buff *head = *headbuf;
        struct sk_buff *frag = *buf;
-       struct sk_buff *tail;
+       struct sk_buff *tail = NULL;
        struct tipc_msg *msg;
        u32 fragid;
        int delta;
@@ -141,9 +141,15 @@ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
                if (unlikely(skb_unclone(frag, GFP_ATOMIC)))
                        goto err;
                head = *headbuf = frag;
-               skb_frag_list_init(head);
-               TIPC_SKB_CB(head)->tail = NULL;
                *buf = NULL;
+               TIPC_SKB_CB(head)->tail = NULL;
+               if (skb_is_nonlinear(head)) {
+                       skb_walk_frags(head, tail) {
+                               TIPC_SKB_CB(head)->tail = tail;
+                       }
+               } else {
+                       skb_frag_list_init(head);
+               }
                return 0;
        }
 
@@ -176,7 +182,6 @@ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
        *buf = NULL;
        return 0;
 err:
-       pr_warn_ratelimited("Unable to build fragment list\n");
        kfree_skb(*buf);
        kfree_skb(*headbuf);
        *buf = *headbuf = NULL;
@@ -559,18 +564,22 @@ bool tipc_msg_lookup_dest(struct net *net, struct sk_buff *skb, int *err)
 /* tipc_msg_reassemble() - clone a buffer chain of fragments and
  *                         reassemble the clones into one message
  */
-struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list)
+bool tipc_msg_reassemble(struct sk_buff_head *list, struct sk_buff_head *rcvq)
 {
-       struct sk_buff *skb;
+       struct sk_buff *skb, *_skb;
        struct sk_buff *frag = NULL;
        struct sk_buff *head = NULL;
-       int hdr_sz;
+       int hdr_len;
 
        /* Copy header if single buffer */
        if (skb_queue_len(list) == 1) {
                skb = skb_peek(list);
-               hdr_sz = skb_headroom(skb) + msg_hdr_sz(buf_msg(skb));
-               return __pskb_copy(skb, hdr_sz, GFP_ATOMIC);
+               hdr_len = skb_headroom(skb) + msg_hdr_sz(buf_msg(skb));
+               _skb = __pskb_copy(skb, hdr_len, GFP_ATOMIC);
+               if (!_skb)
+                       return false;
+               __skb_queue_tail(rcvq, _skb);
+               return true;
        }
 
        /* Clone all fragments and reassemble */
@@ -584,9 +593,41 @@ struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list)
                if (!head)
                        goto error;
        }
-       return frag;
+       __skb_queue_tail(rcvq, frag);
+       return true;
 error:
        pr_warn("Failed do clone local mcast rcv buffer\n");
        kfree_skb(head);
-       return NULL;
+       return false;
+}
+
+/* tipc_skb_queue_sorted(); sort pkt into list according to sequence number
+ * @list: list to be appended to
+ * @seqno: sequence number of buffer to add
+ * @skb: buffer to add
+ */
+void __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno,
+                            struct sk_buff *skb)
+{
+       struct sk_buff *_skb, *tmp;
+
+       if (skb_queue_empty(list) || less(seqno, buf_seqno(skb_peek(list)))) {
+               __skb_queue_head(list, skb);
+               return;
+       }
+
+       if (more(seqno, buf_seqno(skb_peek_tail(list)))) {
+               __skb_queue_tail(list, skb);
+               return;
+       }
+
+       skb_queue_walk_safe(list, _skb, tmp) {
+               if (more(seqno, buf_seqno(_skb)))
+                       continue;
+               if (seqno == buf_seqno(_skb))
+                       break;
+               __skb_queue_before(list, _skb, skb);
+               return;
+       }
+       kfree_skb(skb);
 }
index 5351a3f97e8ecf17e545459344a8b315f522045a..55778a0aebf3706f2c559528f6a482c4077197bb 100644 (file)
@@ -112,6 +112,7 @@ struct tipc_skb_cb {
        bool wakeup_pending;
        u16 chain_sz;
        u16 chain_imp;
+       u16 ackers;
 };
 
 #define TIPC_SKB_CB(__skb) ((struct tipc_skb_cb *)&((__skb)->cb[0]))
@@ -600,6 +601,11 @@ static inline u32 msg_last_bcast(struct tipc_msg *m)
        return msg_bits(m, 4, 16, 0xffff);
 }
 
+static inline u32 msg_bc_snd_nxt(struct tipc_msg *m)
+{
+       return msg_last_bcast(m) + 1;
+}
+
 static inline void msg_set_last_bcast(struct tipc_msg *m, u32 n)
 {
        msg_set_bits(m, 4, 16, 0xffff, n);
@@ -789,7 +795,9 @@ bool tipc_msg_extract(struct sk_buff *skb, struct sk_buff **iskb, int *pos);
 int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m,
                   int offset, int dsz, int mtu, struct sk_buff_head *list);
 bool tipc_msg_lookup_dest(struct net *net, struct sk_buff *skb, int *err);
-struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list);
+bool tipc_msg_reassemble(struct sk_buff_head *list, struct sk_buff_head *rcvq);
+void __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno,
+                            struct sk_buff *skb);
 
 static inline u16 buf_seqno(struct sk_buff *skb)
 {
@@ -862,38 +870,6 @@ static inline struct sk_buff *tipc_skb_dequeue(struct sk_buff_head *list,
        return skb;
 }
 
-/* tipc_skb_queue_sorted(); sort pkt into list according to sequence number
- * @list: list to be appended to
- * @skb: buffer to add
- * Returns true if queue should treated further, otherwise false
- */
-static inline bool __tipc_skb_queue_sorted(struct sk_buff_head *list,
-                                          struct sk_buff *skb)
-{
-       struct sk_buff *_skb, *tmp;
-       struct tipc_msg *hdr = buf_msg(skb);
-       u16 seqno = msg_seqno(hdr);
-
-       if (skb_queue_empty(list) || (msg_user(hdr) == LINK_PROTOCOL)) {
-               __skb_queue_head(list, skb);
-               return true;
-       }
-       if (likely(less(seqno, buf_seqno(skb_peek(list))))) {
-               __skb_queue_head(list, skb);
-               return true;
-       }
-       if (!more(seqno, buf_seqno(skb_peek_tail(list)))) {
-               skb_queue_walk_safe(list, _skb, tmp) {
-                       if (likely(less(seqno, buf_seqno(_skb)))) {
-                               __skb_queue_before(list, _skb, skb);
-                               return true;
-                       }
-               }
-       }
-       __skb_queue_tail(list, skb);
-       return false;
-}
-
 /* tipc_skb_queue_splice_tail - append an skb list to lock protected list
  * @list: the new list to append. Not lock protected
  * @head: target list. Lock protected.
index e6018b7eb1970dfc85bc7e0dc8945ccf45a72180..c07612bab95c0957b2e34434aa73b6ef02708fff 100644 (file)
@@ -102,7 +102,7 @@ void named_cluster_distribute(struct net *net, struct sk_buff *skb)
                if (!oskb)
                        break;
                msg_set_destnode(buf_msg(oskb), dnode);
-               tipc_node_xmit_skb(net, oskb, dnode, dnode);
+               tipc_node_xmit_skb(net, oskb, dnode, 0);
        }
        rcu_read_unlock();
 
@@ -223,7 +223,7 @@ void tipc_named_node_up(struct net *net, u32 dnode)
                         &tn->nametbl->publ_list[TIPC_ZONE_SCOPE]);
        rcu_read_unlock();
 
-       tipc_node_xmit(net, &head, dnode, dnode);
+       tipc_node_xmit(net, &head, dnode, 0);
 }
 
 static void tipc_publ_subscribe(struct net *net, struct publication *publ,
index d6d1399ae22922754ba24364e21e89a6a3497d22..77bf9113c7a76be7cfc5173bc41b84664eb658e6 100644 (file)
@@ -112,14 +112,11 @@ int tipc_net_start(struct net *net, u32 addr)
 {
        struct tipc_net *tn = net_generic(net, tipc_net_id);
        char addr_string[16];
-       int res;
 
        tn->own_addr = addr;
        tipc_named_reinit(net);
        tipc_sk_reinit(net);
-       res = tipc_bclink_init(net);
-       if (res)
-               return res;
+       tipc_bcast_reinit(net);
 
        tipc_nametbl_publish(net, TIPC_CFG_SRV, tn->own_addr, tn->own_addr,
                             TIPC_ZONE_SCOPE, 0, tn->own_addr);
@@ -142,7 +139,6 @@ void tipc_net_stop(struct net *net)
                              tn->own_addr);
        rtnl_lock();
        tipc_bearer_stop(net);
-       tipc_bclink_stop(net);
        tipc_node_stop(net);
        rtnl_unlock();
 
index 2c32a83037a3614ef09fb4b29c8f3337dcdf0f45..20cddec0a43c7227a6e3aff860286ec04f9cd423 100644 (file)
@@ -72,7 +72,6 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
 static void tipc_node_link_down(struct tipc_node *n, int bearer_id,
                                bool delete);
 static void node_lost_contact(struct tipc_node *n, struct sk_buff_head *inputq);
-static void node_established_contact(struct tipc_node *n_ptr);
 static void tipc_node_delete(struct tipc_node *node);
 static void tipc_node_timeout(unsigned long data);
 static void tipc_node_fsm_evt(struct tipc_node *n, int evt);
@@ -165,8 +164,10 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr, u16 capabilities)
        INIT_LIST_HEAD(&n_ptr->list);
        INIT_LIST_HEAD(&n_ptr->publ_list);
        INIT_LIST_HEAD(&n_ptr->conn_sks);
-       skb_queue_head_init(&n_ptr->bclink.namedq);
-       __skb_queue_head_init(&n_ptr->bclink.deferdq);
+       skb_queue_head_init(&n_ptr->bc_entry.namedq);
+       skb_queue_head_init(&n_ptr->bc_entry.inputq1);
+       __skb_queue_head_init(&n_ptr->bc_entry.arrvq);
+       skb_queue_head_init(&n_ptr->bc_entry.inputq2);
        hlist_add_head_rcu(&n_ptr->hash, &tn->node_htable[tipc_hashfn(addr)]);
        list_for_each_entry_rcu(temp_node, &tn->node_list, list) {
                if (n_ptr->addr < temp_node->addr)
@@ -177,6 +178,18 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr, u16 capabilities)
        n_ptr->signature = INVALID_NODE_SIG;
        n_ptr->active_links[0] = INVALID_BEARER_ID;
        n_ptr->active_links[1] = INVALID_BEARER_ID;
+       if (!tipc_link_bc_create(net, tipc_own_addr(net), n_ptr->addr,
+                                U16_MAX, tipc_bc_sndlink(net)->window,
+                                n_ptr->capabilities,
+                                &n_ptr->bc_entry.inputq1,
+                                &n_ptr->bc_entry.namedq,
+                                tipc_bc_sndlink(net),
+                                &n_ptr->bc_entry.link)) {
+               pr_warn("Broadcast rcv link creation failed, no memory\n");
+               kfree(n_ptr);
+               n_ptr = NULL;
+               goto exit;
+       }
        tipc_node_get(n_ptr);
        setup_timer(&n_ptr->timer, tipc_node_timeout, (unsigned long)n_ptr);
        n_ptr->keepalive_intv = U32_MAX;
@@ -203,6 +216,7 @@ static void tipc_node_delete(struct tipc_node *node)
 {
        list_del_rcu(&node->list);
        hlist_del_rcu(&node->hash);
+       kfree(node->bc_entry.link);
        kfree_rcu(node, rcu);
 }
 
@@ -317,7 +331,11 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
        struct tipc_link *ol = node_active_link(n, 0);
        struct tipc_link *nl = n->links[bearer_id].link;
 
-       if (!nl || !tipc_link_is_up(nl))
+       if (!nl)
+               return;
+
+       tipc_link_fsm_evt(nl, LINK_ESTABLISH_EVT);
+       if (!tipc_link_is_up(nl))
                return;
 
        n->working_links++;
@@ -328,6 +346,7 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
        n->links[bearer_id].mtu = nl->mtu - INT_H_SIZE;
 
        tipc_bearer_add_dest(n->net, bearer_id, n->addr);
+       tipc_bcast_inc_bearer_dst_cnt(n->net, bearer_id);
 
        pr_debug("Established link <%s> on network plane %c\n",
                 nl->name, nl->net_plane);
@@ -336,8 +355,9 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
        if (!ol) {
                *slot0 = bearer_id;
                *slot1 = bearer_id;
-               tipc_link_build_bcast_sync_msg(nl, xmitq);
-               node_established_contact(n);
+               tipc_node_fsm_evt(n, SELF_ESTABL_CONTACT_EVT);
+               n->action_flags |= TIPC_NOTIFY_NODE_UP;
+               tipc_bcast_add_peer(n->net, nl, xmitq);
                return;
        }
 
@@ -346,8 +366,11 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
                pr_debug("Old link <%s> becomes standby\n", ol->name);
                *slot0 = bearer_id;
                *slot1 = bearer_id;
+               tipc_link_set_active(nl, true);
+               tipc_link_set_active(ol, false);
        } else if (nl->priority == ol->priority) {
-               *slot0 = bearer_id;
+               tipc_link_set_active(nl, true);
+               *slot1 = bearer_id;
        } else {
                pr_debug("New link <%s> is standby\n", nl->name);
        }
@@ -416,10 +439,18 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
        }
 
        if (!tipc_node_is_up(n)) {
+               if (tipc_link_peer_is_down(l))
+                       tipc_node_fsm_evt(n, PEER_LOST_CONTACT_EVT);
+               tipc_node_fsm_evt(n, SELF_LOST_CONTACT_EVT);
+               tipc_link_fsm_evt(l, LINK_RESET_EVT);
                tipc_link_reset(l);
+               tipc_link_build_reset_msg(l, xmitq);
+               *maddr = &n->links[*bearer_id].maddr;
                node_lost_contact(n, &le->inputq);
+               tipc_bcast_dec_bearer_dst_cnt(n->net, *bearer_id);
                return;
        }
+       tipc_bcast_dec_bearer_dst_cnt(n->net, *bearer_id);
 
        /* There is still a working link => initiate failover */
        tnl = node_active_link(n, 0);
@@ -428,6 +459,7 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
        n->sync_point = tnl->rcv_nxt + (U16_MAX / 2 - 1);
        tipc_link_tnl_prepare(l, tnl, FAILOVER_MSG, xmitq);
        tipc_link_reset(l);
+       tipc_link_fsm_evt(l, LINK_RESET_EVT);
        tipc_link_fsm_evt(l, LINK_FAILOVER_BEGIN_EVT);
        tipc_node_fsm_evt(n, NODE_FAILOVER_BEGIN_EVT);
        *maddr = &n->links[tnl->bearer_id].maddr;
@@ -437,20 +469,28 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
 static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
 {
        struct tipc_link_entry *le = &n->links[bearer_id];
+       struct tipc_link *l = le->link;
        struct tipc_media_addr *maddr;
        struct sk_buff_head xmitq;
 
+       if (!l)
+               return;
+
        __skb_queue_head_init(&xmitq);
 
        tipc_node_lock(n);
-       __tipc_node_link_down(n, &bearer_id, &xmitq, &maddr);
-       if (delete && le->link) {
-               kfree(le->link);
-               le->link = NULL;
-               n->link_cnt--;
+       if (!tipc_link_is_establishing(l)) {
+               __tipc_node_link_down(n, &bearer_id, &xmitq, &maddr);
+               if (delete) {
+                       kfree(l);
+                       le->link = NULL;
+                       n->link_cnt--;
+               }
+       } else {
+               /* Defuse pending tipc_node_link_up() */
+               tipc_link_fsm_evt(l, LINK_RESET_EVT);
        }
        tipc_node_unlock(n);
-
        tipc_bearer_xmit(n->net, bearer_id, &xmitq, maddr);
        tipc_sk_rcv(n->net, &le->inputq);
 }
@@ -474,6 +514,7 @@ void tipc_node_check_dest(struct net *net, u32 onode,
        bool link_up = false;
        bool accept_addr = false;
        bool reset = true;
+       char *if_name;
 
        *dupl_addr = false;
        *respond = false;
@@ -560,13 +601,20 @@ void tipc_node_check_dest(struct net *net, u32 onode,
                        pr_warn("Cannot establish 3rd link to %x\n", n->addr);
                        goto exit;
                }
-               if (!tipc_link_create(n, b, mod(tipc_net(net)->random),
-                                     tipc_own_addr(net), onode, &le->maddr,
-                                     &le->inputq, &n->bclink.namedq, &l)) {
+               if_name = strchr(b->name, ':') + 1;
+               if (!tipc_link_create(net, if_name, b->identity, b->tolerance,
+                                     b->net_plane, b->mtu, b->priority,
+                                     b->window, mod(tipc_net(net)->random),
+                                     tipc_own_addr(net), onode,
+                                     n->capabilities,
+                                     tipc_bc_sndlink(n->net), n->bc_entry.link,
+                                     &le->inputq,
+                                     &n->bc_entry.namedq, &l)) {
                        *respond = false;
                        goto exit;
                }
                tipc_link_reset(l);
+               tipc_link_fsm_evt(l, LINK_RESET_EVT);
                if (n->state == NODE_FAILINGOVER)
                        tipc_link_fsm_evt(l, LINK_FAILOVER_BEGIN_EVT);
                le->link = l;
@@ -579,7 +627,7 @@ void tipc_node_check_dest(struct net *net, u32 onode,
        memcpy(&le->maddr, maddr, sizeof(*maddr));
 exit:
        tipc_node_unlock(n);
-       if (reset)
+       if (reset && !tipc_link_is_reset(l))
                tipc_node_link_down(n, b->identity, false);
        tipc_node_put(n);
 }
@@ -686,10 +734,10 @@ static void tipc_node_fsm_evt(struct tipc_node *n, int evt)
                        break;
                case SELF_ESTABL_CONTACT_EVT:
                case PEER_LOST_CONTACT_EVT:
-                       break;
                case NODE_SYNCH_END_EVT:
-               case NODE_SYNCH_BEGIN_EVT:
                case NODE_FAILOVER_BEGIN_EVT:
+                       break;
+               case NODE_SYNCH_BEGIN_EVT:
                case NODE_FAILOVER_END_EVT:
                default:
                        goto illegal_evt;
@@ -804,61 +852,36 @@ bool tipc_node_filter_pkt(struct tipc_node *n, struct tipc_msg *hdr)
        return true;
 }
 
-static void node_established_contact(struct tipc_node *n_ptr)
-{
-       tipc_node_fsm_evt(n_ptr, SELF_ESTABL_CONTACT_EVT);
-       n_ptr->action_flags |= TIPC_NOTIFY_NODE_UP;
-       n_ptr->bclink.oos_state = 0;
-       n_ptr->bclink.acked = tipc_bclink_get_last_sent(n_ptr->net);
-       tipc_bclink_add_node(n_ptr->net, n_ptr->addr);
-}
-
-static void node_lost_contact(struct tipc_node *n_ptr,
+static void node_lost_contact(struct tipc_node *n,
                              struct sk_buff_head *inputq)
 {
        char addr_string[16];
        struct tipc_sock_conn *conn, *safe;
        struct tipc_link *l;
-       struct list_head *conns = &n_ptr->conn_sks;
+       struct list_head *conns = &n->conn_sks;
        struct sk_buff *skb;
-       struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id);
        uint i;
 
        pr_debug("Lost contact with %s\n",
-                tipc_addr_string_fill(addr_string, n_ptr->addr));
-
-       /* Flush broadcast link info associated with lost node */
-       if (n_ptr->bclink.recv_permitted) {
-               __skb_queue_purge(&n_ptr->bclink.deferdq);
+                tipc_addr_string_fill(addr_string, n->addr));
 
-               if (n_ptr->bclink.reasm_buf) {
-                       kfree_skb(n_ptr->bclink.reasm_buf);
-                       n_ptr->bclink.reasm_buf = NULL;
-               }
-
-               tipc_bclink_remove_node(n_ptr->net, n_ptr->addr);
-               tipc_bclink_acknowledge(n_ptr, INVALID_LINK_SEQ);
-
-               n_ptr->bclink.recv_permitted = false;
-       }
+       /* Clean up broadcast state */
+       tipc_bcast_remove_peer(n->net, n->bc_entry.link);
 
        /* Abort any ongoing link failover */
        for (i = 0; i < MAX_BEARERS; i++) {
-               l = n_ptr->links[i].link;
+               l = n->links[i].link;
                if (l)
                        tipc_link_fsm_evt(l, LINK_FAILOVER_END_EVT);
        }
 
-       /* Prevent re-contact with node until cleanup is done */
-       tipc_node_fsm_evt(n_ptr, SELF_LOST_CONTACT_EVT);
-
        /* Notify publications from this node */
-       n_ptr->action_flags |= TIPC_NOTIFY_NODE_DOWN;
+       n->action_flags |= TIPC_NOTIFY_NODE_DOWN;
 
        /* Notify sockets connected to node */
        list_for_each_entry_safe(conn, safe, conns, list) {
                skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG,
-                                     SHORT_H_SIZE, 0, tn->own_addr,
+                                     SHORT_H_SIZE, 0, tipc_own_addr(n->net),
                                      conn->peer_node, conn->port,
                                      conn->peer_port, TIPC_ERR_NO_NODE);
                if (likely(skb))
@@ -920,18 +943,13 @@ void tipc_node_unlock(struct tipc_node *node)
        publ_list = &node->publ_list;
 
        node->action_flags &= ~(TIPC_NOTIFY_NODE_DOWN | TIPC_NOTIFY_NODE_UP |
-                               TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP |
-                               TIPC_WAKEUP_BCAST_USERS | TIPC_BCAST_MSG_EVT |
-                               TIPC_BCAST_RESET);
+                               TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP);
 
        spin_unlock_bh(&node->lock);
 
        if (flags & TIPC_NOTIFY_NODE_DOWN)
                tipc_publ_notify(net, publ_list, addr);
 
-       if (flags & TIPC_WAKEUP_BCAST_USERS)
-               tipc_bclink_wakeup_users(net);
-
        if (flags & TIPC_NOTIFY_NODE_UP)
                tipc_named_node_up(net, addr);
 
@@ -943,11 +961,6 @@ void tipc_node_unlock(struct tipc_node *node)
                tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr,
                                      link_id, addr);
 
-       if (flags & TIPC_BCAST_MSG_EVT)
-               tipc_bclink_input(net);
-
-       if (flags & TIPC_BCAST_RESET)
-               tipc_node_reset_links(node);
 }
 
 /* Caller should hold node lock for the passed node */
@@ -1062,6 +1075,67 @@ int tipc_node_xmit_skb(struct net *net, struct sk_buff *skb, u32 dnode,
        return 0;
 }
 
+/**
+ * tipc_node_bc_rcv - process TIPC broadcast packet arriving from off-node
+ * @net: the applicable net namespace
+ * @skb: TIPC packet
+ * @bearer_id: id of bearer message arrived on
+ *
+ * Invoked with no locks held.
+ */
+static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id)
+{
+       int rc;
+       struct sk_buff_head xmitq;
+       struct tipc_bclink_entry *be;
+       struct tipc_link_entry *le;
+       struct tipc_msg *hdr = buf_msg(skb);
+       int usr = msg_user(hdr);
+       u32 dnode = msg_destnode(hdr);
+       struct tipc_node *n;
+
+       __skb_queue_head_init(&xmitq);
+
+       /* If NACK for other node, let rcv link for that node peek into it */
+       if ((usr == BCAST_PROTOCOL) && (dnode != tipc_own_addr(net)))
+               n = tipc_node_find(net, dnode);
+       else
+               n = tipc_node_find(net, msg_prevnode(hdr));
+       if (!n) {
+               kfree_skb(skb);
+               return;
+       }
+       be = &n->bc_entry;
+       le = &n->links[bearer_id];
+
+       rc = tipc_bcast_rcv(net, be->link, skb);
+
+       /* Broadcast link reset may happen at reassembly failure */
+       if (rc & TIPC_LINK_DOWN_EVT)
+               tipc_node_reset_links(n);
+
+       /* Broadcast ACKs are sent on a unicast link */
+       if (rc & TIPC_LINK_SND_BC_ACK) {
+               tipc_node_lock(n);
+               tipc_link_build_ack_msg(le->link, &xmitq);
+               tipc_node_unlock(n);
+       }
+
+       if (!skb_queue_empty(&xmitq))
+               tipc_bearer_xmit(net, bearer_id, &xmitq, &le->maddr);
+
+       /* Deliver. 'arrvq' is under inputq2's lock protection */
+       if (!skb_queue_empty(&be->inputq1)) {
+               spin_lock_bh(&be->inputq2.lock);
+               spin_lock_bh(&be->inputq1.lock);
+               skb_queue_splice_tail_init(&be->inputq1, &be->arrvq);
+               spin_unlock_bh(&be->inputq1.lock);
+               spin_unlock_bh(&be->inputq2.lock);
+               tipc_sk_mcast_rcv(net, &be->arrvq, &be->inputq2);
+       }
+       tipc_node_put(n);
+}
+
 /**
  * tipc_node_check_state - check and if necessary update node state
  * @skb: TIPC packet
@@ -1204,6 +1278,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
        int usr = msg_user(hdr);
        int bearer_id = b->identity;
        struct tipc_link_entry *le;
+       u16 bc_ack = msg_bcast_ack(hdr);
        int rc = 0;
 
        __skb_queue_head_init(&xmitq);
@@ -1212,13 +1287,12 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
        if (unlikely(!tipc_msg_validate(skb)))
                goto discard;
 
-       /* Handle arrival of a non-unicast link packet */
+       /* Handle arrival of discovery or broadcast packet */
        if (unlikely(msg_non_seq(hdr))) {
-               if (usr ==  LINK_CONFIG)
-                       tipc_disc_rcv(net, skb, b);
+               if (unlikely(usr == LINK_CONFIG))
+                       return tipc_disc_rcv(net, skb, b);
                else
-                       tipc_bclink_rcv(net, skb);
-               return;
+                       return tipc_node_bc_rcv(net, skb, bearer_id);
        }
 
        /* Locate neighboring node that sent packet */
@@ -1227,19 +1301,18 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
                goto discard;
        le = &n->links[bearer_id];
 
+       /* Ensure broadcast reception is in synch with peer's send state */
+       if (unlikely(usr == LINK_PROTOCOL))
+               tipc_bcast_sync_rcv(net, n->bc_entry.link, hdr);
+       else if (unlikely(n->bc_entry.link->acked != bc_ack))
+               tipc_bcast_ack_rcv(net, n->bc_entry.link, bc_ack);
+
        tipc_node_lock(n);
 
        /* Is reception permitted at the moment ? */
        if (!tipc_node_filter_pkt(n, hdr))
                goto unlock;
 
-       if (unlikely(msg_user(hdr) == LINK_PROTOCOL))
-               tipc_bclink_sync_state(n, hdr);
-
-       /* Release acked broadcast packets */
-       if (unlikely(n->bclink.acked != msg_bcast_ack(hdr)))
-               tipc_bclink_acknowledge(n, msg_bcast_ack(hdr));
-
        /* Check and if necessary update node state */
        if (likely(tipc_node_check_state(n, skb, bearer_id, &xmitq))) {
                rc = tipc_link_rcv(le->link, skb, &xmitq);
@@ -1254,8 +1327,8 @@ unlock:
        if (unlikely(rc & TIPC_LINK_DOWN_EVT))
                tipc_node_link_down(n, bearer_id, false);
 
-       if (unlikely(!skb_queue_empty(&n->bclink.namedq)))
-               tipc_named_rcv(net, &n->bclink.namedq);
+       if (unlikely(!skb_queue_empty(&n->bc_entry.namedq)))
+               tipc_named_rcv(net, &n->bc_entry.namedq);
 
        if (!skb_queue_empty(&le->inputq))
                tipc_sk_rcv(net, &le->inputq);
index 344b3e7594fd0d59d8b83143181ffeac8c2df9a9..6734562d3c6e57092e005516429400a1f68c1d8c 100644 (file)
 enum {
        TIPC_NOTIFY_NODE_DOWN           = (1 << 3),
        TIPC_NOTIFY_NODE_UP             = (1 << 4),
-       TIPC_WAKEUP_BCAST_USERS         = (1 << 5),
        TIPC_NOTIFY_LINK_UP             = (1 << 6),
-       TIPC_NOTIFY_LINK_DOWN           = (1 << 7),
-       TIPC_BCAST_MSG_EVT              = (1 << 9),
-       TIPC_BCAST_RESET                = (1 << 10)
+       TIPC_NOTIFY_LINK_DOWN           = (1 << 7)
 };
 
-/**
- * struct tipc_node_bclink - TIPC node bclink structure
- * @acked: sequence # of last outbound b'cast message acknowledged by node
- * @last_in: sequence # of last in-sequence b'cast message received from node
- * @last_sent: sequence # of last b'cast message sent by node
- * @oos_state: state tracker for handling OOS b'cast messages
- * @deferred_queue: deferred queue saved OOS b'cast message received from node
- * @reasm_buf: broadcast reassembly queue head from node
- * @inputq_map: bitmap indicating which inqueues should be kicked
- * @recv_permitted: true if node is allowed to receive b'cast messages
+/* Optional capabilities supported by this code version
  */
-struct tipc_node_bclink {
-       u32 acked;
-       u32 last_in;
-       u32 last_sent;
-       u32 oos_state;
-       u32 deferred_size;
-       struct sk_buff_head deferdq;
-       struct sk_buff *reasm_buf;
-       struct sk_buff_head namedq;
-       bool recv_permitted;
+enum {
+       TIPC_BCAST_SYNCH = (1 << 1)
 };
 
+#define TIPC_NODE_CAPABILITIES TIPC_BCAST_SYNCH
+
 struct tipc_link_entry {
        struct tipc_link *link;
        u32 mtu;
@@ -92,6 +74,14 @@ struct tipc_link_entry {
        struct tipc_media_addr maddr;
 };
 
+struct tipc_bclink_entry {
+       struct tipc_link *link;
+       struct sk_buff_head inputq1;
+       struct sk_buff_head arrvq;
+       struct sk_buff_head inputq2;
+       struct sk_buff_head namedq;
+};
+
 /**
  * struct tipc_node - TIPC node structure
  * @addr: network address of node
@@ -104,7 +94,6 @@ struct tipc_link_entry {
  * @active_links: bearer ids of active links, used as index into links[] array
  * @links: array containing references to all links to node
  * @action_flags: bit mask of different types of node actions
- * @bclink: broadcast-related info
  * @state: connectivity state vs peer node
  * @sync_point: sequence number where synch/failover is finished
  * @list: links to adjacent nodes in sorted list of cluster's nodes
@@ -124,8 +113,8 @@ struct tipc_node {
        struct hlist_node hash;
        int active_links[2];
        struct tipc_link_entry links[MAX_BEARERS];
+       struct tipc_bclink_entry bc_entry;
        int action_flags;
-       struct tipc_node_bclink bclink;
        struct list_head list;
        int state;
        u16 sync_point;
index 1060d52ff23eb14f7b2ed2f732beacf9a698502c..552dbaba9cf386a07e6c4f499fda27ca1f8a8f4a 100644 (file)
@@ -689,13 +689,13 @@ static int tipc_sendmcast(struct  socket *sock, struct tipc_name_seq *seq,
        msg_set_hdr_sz(mhdr, MCAST_H_SIZE);
 
 new_mtu:
-       mtu = tipc_bclink_get_mtu();
+       mtu = tipc_bcast_get_mtu(net);
        rc = tipc_msg_build(mhdr, msg, 0, dsz, mtu, pktchain);
        if (unlikely(rc < 0))
                return rc;
 
        do {
-               rc = tipc_bclink_xmit(net, pktchain);
+               rc = tipc_bcast_xmit(net, pktchain);
                if (likely(!rc))
                        return dsz;
 
index c170d3138953a2361df5439aeffadd29afa52ad9..ad2719ad4c1baf38cd8e31b244fe703382da002a 100644 (file)
 #include <linux/tipc_netlink.h>
 #include "core.h"
 #include "bearer.h"
+#include "msg.h"
 
 /* IANA assigned UDP port */
 #define UDP_PORT_DEFAULT       6118
 
+#define UDP_MIN_HEADROOM        28
+
 static const struct nla_policy tipc_nl_udp_policy[TIPC_NLA_UDP_MAX + 1] = {
        [TIPC_NLA_UDP_UNSPEC]   = {.type = NLA_UNSPEC},
        [TIPC_NLA_UDP_LOCAL]    = {.type = NLA_BINARY,
@@ -153,11 +156,12 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
        struct udp_bearer *ub;
        struct udp_media_addr *dst = (struct udp_media_addr *)&dest->value;
        struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value;
-       struct sk_buff *clone;
        struct rtable *rt;
 
-       clone = skb_clone(skb, GFP_ATOMIC);
-       skb_set_inner_protocol(clone, htons(ETH_P_TIPC));
+       if (skb_headroom(skb) < UDP_MIN_HEADROOM)
+               pskb_expand_head(skb, UDP_MIN_HEADROOM, 0, GFP_ATOMIC);
+
+       skb_set_inner_protocol(skb, htons(ETH_P_TIPC));
        ub = rcu_dereference_rtnl(b->media_ptr);
        if (!ub) {
                err = -ENODEV;
@@ -167,7 +171,7 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
                struct flowi4 fl = {
                        .daddr = dst->ipv4.s_addr,
                        .saddr = src->ipv4.s_addr,
-                       .flowi4_mark = clone->mark,
+                       .flowi4_mark = skb->mark,
                        .flowi4_proto = IPPROTO_UDP
                };
                rt = ip_route_output_key(net, &fl);
@@ -176,7 +180,7 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
                        goto tx_error;
                }
                ttl = ip4_dst_hoplimit(&rt->dst);
-               err = udp_tunnel_xmit_skb(rt, ub->ubsock->sk, clone,
+               err = udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb,
                                          src->ipv4.s_addr,
                                          dst->ipv4.s_addr, 0, ttl, 0,
                                          src->udp_port, dst->udp_port,
@@ -199,7 +203,7 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
                if (err)
                        goto tx_error;
                ttl = ip6_dst_hoplimit(ndst);
-               err = udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, clone,
+               err = udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, skb,
                                           ndst->dev, &src->ipv6,
                                           &dst->ipv6, 0, ttl, src->udp_port,
                                           dst->udp_port, false);
@@ -208,7 +212,7 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
        return err;
 
 tx_error:
-       kfree_skb(clone);
+       kfree_skb(skb);
        return err;
 }
 
@@ -217,6 +221,10 @@ static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb)
 {
        struct udp_bearer *ub;
        struct tipc_bearer *b;
+       int usr = msg_user(buf_msg(skb));
+
+       if ((usr == LINK_PROTOCOL) || (usr == NAME_DISTRIBUTOR))
+               skb_linearize(skb);
 
        ub = rcu_dereference_sk_user_data(sk);
        if (!ub) {
@@ -425,7 +433,6 @@ static void tipc_udp_disable(struct tipc_bearer *b)
        }
        if (ub->ubsock)
                sock_set_flag(ub->ubsock->sk, SOCK_DEAD);
-       RCU_INIT_POINTER(b->media_ptr, NULL);
        RCU_INIT_POINTER(ub->bearer, NULL);
 
        /* sock_release need to be done outside of rtnl lock */
index 94f658235fb49a0c644a84867302cc00a75243d5..aaa0b58d6aba29816aa81c0f6ce444a0ab0be0fb 100644 (file)
@@ -326,9 +326,10 @@ found:
        return s;
 }
 
-static inline int unix_writable(struct sock *sk)
+static int unix_writable(const struct sock *sk)
 {
-       return (atomic_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf;
+       return sk->sk_state != TCP_LISTEN &&
+              (atomic_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf;
 }
 
 static void unix_write_space(struct sock *sk)
index df5fc6b340f1bbde621fbe84fa44e0af6e2e00af..7fd1220fbfa0bb2477e2b7e6edcf714a5ae6a097 100644 (file)
  * not support simultaneous connects (two "client" sockets connecting).
  *
  * - "Server" sockets are referred to as listener sockets throughout this
- * implementation because they are in the SS_LISTEN state.  When a connection
- * request is received (the second kind of socket mentioned above), we create a
- * new socket and refer to it as a pending socket.  These pending sockets are
- * placed on the pending connection list of the listener socket.  When future
- * packets are received for the address the listener socket is bound to, we
- * check if the source of the packet is from one that has an existing pending
- * connection.  If it does, we process the packet for the pending socket.  When
- * that socket reaches the connected state, it is removed from the listener
- * socket's pending list and enqueued in the listener socket's accept queue.
- * Callers of accept(2) will accept connected sockets from the listener socket's
- * accept queue.  If the socket cannot be accepted for some reason then it is
- * marked rejected.  Once the connection is accepted, it is owned by the user
- * process and the responsibility for cleanup falls with that user process.
+ * implementation because they are in the VSOCK_SS_LISTEN state.  When a
+ * connection request is received (the second kind of socket mentioned above),
+ * we create a new socket and refer to it as a pending socket.  These pending
+ * sockets are placed on the pending connection list of the listener socket.
+ * When future packets are received for the address the listener socket is
+ * bound to, we check if the source of the packet is from one that has an
+ * existing pending connection.  If it does, we process the packet for the
+ * pending socket.  When that socket reaches the connected state, it is removed
+ * from the listener socket's pending list and enqueued in the listener
+ * socket's accept queue.  Callers of accept(2) will accept connected sockets
+ * from the listener socket's accept queue.  If the socket cannot be accepted
+ * for some reason then it is marked rejected.  Once the connection is
+ * accepted, it is owned by the user process and the responsibility for cleanup
+ * falls with that user process.
  *
  * - It is possible that these pending sockets will never reach the connected
  * state; in fact, we may never receive another packet after the connection
@@ -114,8 +115,6 @@ static struct proto vsock_proto = {
  */
 #define VSOCK_DEFAULT_CONNECT_TIMEOUT (2 * HZ)
 
-#define SS_LISTEN 255
-
 static const struct vsock_transport *transport;
 static DEFINE_MUTEX(vsock_register_mutex);
 
@@ -887,7 +886,7 @@ static unsigned int vsock_poll(struct file *file, struct socket *sock,
                /* Listening sockets that have connections in their accept
                 * queue can be read.
                 */
-               if (sk->sk_state == SS_LISTEN
+               if (sk->sk_state == VSOCK_SS_LISTEN
                    && !vsock_is_accept_queue_empty(sk))
                        mask |= POLLIN | POLLRDNORM;
 
@@ -1144,7 +1143,7 @@ static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr,
                err = -EALREADY;
                break;
        default:
-               if ((sk->sk_state == SS_LISTEN) ||
+               if ((sk->sk_state == VSOCK_SS_LISTEN) ||
                    vsock_addr_cast(addr, addr_len, &remote_addr) != 0) {
                        err = -EINVAL;
                        goto out;
@@ -1256,7 +1255,7 @@ static int vsock_accept(struct socket *sock, struct socket *newsock, int flags)
                goto out;
        }
 
-       if (listener->sk_state != SS_LISTEN) {
+       if (listener->sk_state != VSOCK_SS_LISTEN) {
                err = -EINVAL;
                goto out;
        }
@@ -1348,7 +1347,7 @@ static int vsock_listen(struct socket *sock, int backlog)
        }
 
        sk->sk_max_ack_backlog = backlog;
-       sk->sk_state = SS_LISTEN;
+       sk->sk_state = VSOCK_SS_LISTEN;
 
        err = 0;
 
@@ -1948,13 +1947,13 @@ int __vsock_core_init(const struct vsock_transport *t, struct module *owner)
        err = misc_register(&vsock_device);
        if (err) {
                pr_err("Failed to register misc device\n");
-               return -ENOENT;
+               goto err_reset_transport;
        }
 
        err = proto_register(&vsock_proto, 1);  /* we want our slab */
        if (err) {
                pr_err("Cannot register vsock protocol\n");
-               goto err_misc_deregister;
+               goto err_deregister_misc;
        }
 
        err = sock_register(&vsock_family_ops);
@@ -1969,8 +1968,9 @@ int __vsock_core_init(const struct vsock_transport *t, struct module *owner)
 
 err_unregister_proto:
        proto_unregister(&vsock_proto);
-err_misc_deregister:
+err_deregister_misc:
        misc_deregister(&vsock_device);
+err_reset_transport:
        transport = NULL;
 err_busy:
        mutex_unlock(&vsock_register_mutex);
index 1f63daff39659e08561862cfd71220ffc6949291..400d87294de3f72ca343814525fcec67564d0b47 100644 (file)
 
 static int vmci_transport_recv_dgram_cb(void *data, struct vmci_datagram *dg);
 static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg);
-static void vmci_transport_peer_attach_cb(u32 sub_id,
-                                         const struct vmci_event_data *ed,
-                                         void *client_data);
 static void vmci_transport_peer_detach_cb(u32 sub_id,
                                          const struct vmci_event_data *ed,
                                          void *client_data);
 static void vmci_transport_recv_pkt_work(struct work_struct *work);
+static void vmci_transport_cleanup(struct work_struct *work);
 static int vmci_transport_recv_listen(struct sock *sk,
                                      struct vmci_transport_packet *pkt);
 static int vmci_transport_recv_connecting_server(
@@ -75,6 +73,10 @@ struct vmci_transport_recv_pkt_info {
        struct vmci_transport_packet pkt;
 };
 
+static LIST_HEAD(vmci_transport_cleanup_list);
+static DEFINE_SPINLOCK(vmci_transport_cleanup_lock);
+static DECLARE_WORK(vmci_transport_cleanup_work, vmci_transport_cleanup);
+
 static struct vmci_handle vmci_transport_stream_handle = { VMCI_INVALID_ID,
                                                           VMCI_INVALID_ID };
 static u32 vmci_transport_qp_resumed_sub_id = VMCI_INVALID_ID;
@@ -90,8 +92,6 @@ static int PROTOCOL_OVERRIDE = -1;
  */
 #define VSOCK_DEFAULT_CONNECT_TIMEOUT (2 * HZ)
 
-#define SS_LISTEN 255
-
 /* Helper function to convert from a VMCI error code to a VSock error code. */
 
 static s32 vmci_transport_error_to_vsock_error(s32 vmci_error)
@@ -791,44 +791,6 @@ out:
        return err;
 }
 
-static void vmci_transport_peer_attach_cb(u32 sub_id,
-                                         const struct vmci_event_data *e_data,
-                                         void *client_data)
-{
-       struct sock *sk = client_data;
-       const struct vmci_event_payload_qp *e_payload;
-       struct vsock_sock *vsk;
-
-       e_payload = vmci_event_data_const_payload(e_data);
-
-       vsk = vsock_sk(sk);
-
-       /* We don't ask for delayed CBs when we subscribe to this event (we
-        * pass 0 as flags to vmci_event_subscribe()).  VMCI makes no
-        * guarantees in that case about what context we might be running in,
-        * so it could be BH or process, blockable or non-blockable.  So we
-        * need to account for all possible contexts here.
-        */
-       local_bh_disable();
-       bh_lock_sock(sk);
-
-       /* XXX This is lame, we should provide a way to lookup sockets by
-        * qp_handle.
-        */
-       if (vmci_handle_is_equal(vmci_trans(vsk)->qp_handle,
-                                e_payload->handle)) {
-               /* XXX This doesn't do anything, but in the future we may want
-                * to set a flag here to verify the attach really did occur and
-                * we weren't just sent a datagram claiming it was.
-                */
-               goto out;
-       }
-
-out:
-       bh_unlock_sock(sk);
-       local_bh_enable();
-}
-
 static void vmci_transport_handle_detach(struct sock *sk)
 {
        struct vsock_sock *vsk;
@@ -871,28 +833,38 @@ static void vmci_transport_peer_detach_cb(u32 sub_id,
                                          const struct vmci_event_data *e_data,
                                          void *client_data)
 {
-       struct sock *sk = client_data;
+       struct vmci_transport *trans = client_data;
        const struct vmci_event_payload_qp *e_payload;
-       struct vsock_sock *vsk;
 
        e_payload = vmci_event_data_const_payload(e_data);
-       vsk = vsock_sk(sk);
-       if (vmci_handle_is_invalid(e_payload->handle))
-               return;
-
-       /* Same rules for locking as for peer_attach_cb(). */
-       local_bh_disable();
-       bh_lock_sock(sk);
 
        /* XXX This is lame, we should provide a way to lookup sockets by
         * qp_handle.
         */
-       if (vmci_handle_is_equal(vmci_trans(vsk)->qp_handle,
-                                e_payload->handle))
-               vmci_transport_handle_detach(sk);
+       if (vmci_handle_is_invalid(e_payload->handle) ||
+           vmci_handle_is_equal(trans->qp_handle, e_payload->handle))
+               return;
 
-       bh_unlock_sock(sk);
-       local_bh_enable();
+       /* We don't ask for delayed CBs when we subscribe to this event (we
+        * pass 0 as flags to vmci_event_subscribe()).  VMCI makes no
+        * guarantees in that case about what context we might be running in,
+        * so it could be BH or process, blockable or non-blockable.  So we
+        * need to account for all possible contexts here.
+        */
+       spin_lock_bh(&trans->lock);
+       if (!trans->sk)
+               goto out;
+
+       /* Apart from here, trans->lock is only grabbed as part of sk destruct,
+        * where trans->sk isn't locked.
+        */
+       bh_lock_sock(trans->sk);
+
+       vmci_transport_handle_detach(trans->sk);
+
+       bh_unlock_sock(trans->sk);
+ out:
+       spin_unlock_bh(&trans->lock);
 }
 
 static void vmci_transport_qp_resumed_cb(u32 sub_id,
@@ -919,7 +891,7 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
        vsock_sk(sk)->local_addr.svm_cid = pkt->dg.dst.context;
 
        switch (sk->sk_state) {
-       case SS_LISTEN:
+       case VSOCK_SS_LISTEN:
                vmci_transport_recv_listen(sk, pkt);
                break;
        case SS_CONNECTING:
@@ -1181,7 +1153,7 @@ vmci_transport_recv_connecting_server(struct sock *listener,
         */
        err = vmci_event_subscribe(VMCI_EVENT_QP_PEER_DETACH,
                                   vmci_transport_peer_detach_cb,
-                                  pending, &detach_sub_id);
+                                  vmci_trans(vpending), &detach_sub_id);
        if (err < VMCI_SUCCESS) {
                vmci_transport_send_reset(pending, pkt);
                err = vmci_transport_error_to_vsock_error(err);
@@ -1321,7 +1293,6 @@ vmci_transport_recv_connecting_client(struct sock *sk,
                    || vmci_trans(vsk)->qpair
                    || vmci_trans(vsk)->produce_size != 0
                    || vmci_trans(vsk)->consume_size != 0
-                   || vmci_trans(vsk)->attach_sub_id != VMCI_INVALID_ID
                    || vmci_trans(vsk)->detach_sub_id != VMCI_INVALID_ID) {
                        skerr = EPROTO;
                        err = -EINVAL;
@@ -1389,7 +1360,6 @@ static int vmci_transport_recv_connecting_client_negotiate(
        struct vsock_sock *vsk;
        struct vmci_handle handle;
        struct vmci_qp *qpair;
-       u32 attach_sub_id;
        u32 detach_sub_id;
        bool is_local;
        u32 flags;
@@ -1399,7 +1369,6 @@ static int vmci_transport_recv_connecting_client_negotiate(
 
        vsk = vsock_sk(sk);
        handle = VMCI_INVALID_HANDLE;
-       attach_sub_id = VMCI_INVALID_ID;
        detach_sub_id = VMCI_INVALID_ID;
 
        /* If we have gotten here then we should be past the point where old
@@ -1444,23 +1413,15 @@ static int vmci_transport_recv_connecting_client_negotiate(
                goto destroy;
        }
 
-       /* Subscribe to attach and detach events first.
+       /* Subscribe to detach events first.
         *
         * XXX We attach once for each queue pair created for now so it is easy
         * to find the socket (it's provided), but later we should only
         * subscribe once and add a way to lookup sockets by queue pair handle.
         */
-       err = vmci_event_subscribe(VMCI_EVENT_QP_PEER_ATTACH,
-                                  vmci_transport_peer_attach_cb,
-                                  sk, &attach_sub_id);
-       if (err < VMCI_SUCCESS) {
-               err = vmci_transport_error_to_vsock_error(err);
-               goto destroy;
-       }
-
        err = vmci_event_subscribe(VMCI_EVENT_QP_PEER_DETACH,
                                   vmci_transport_peer_detach_cb,
-                                  sk, &detach_sub_id);
+                                  vmci_trans(vsk), &detach_sub_id);
        if (err < VMCI_SUCCESS) {
                err = vmci_transport_error_to_vsock_error(err);
                goto destroy;
@@ -1496,7 +1457,6 @@ static int vmci_transport_recv_connecting_client_negotiate(
        vmci_trans(vsk)->produce_size = vmci_trans(vsk)->consume_size =
                pkt->u.size;
 
-       vmci_trans(vsk)->attach_sub_id = attach_sub_id;
        vmci_trans(vsk)->detach_sub_id = detach_sub_id;
 
        vmci_trans(vsk)->notify_ops->process_negotiate(sk);
@@ -1504,9 +1464,6 @@ static int vmci_transport_recv_connecting_client_negotiate(
        return 0;
 
 destroy:
-       if (attach_sub_id != VMCI_INVALID_ID)
-               vmci_event_unsubscribe(attach_sub_id);
-
        if (detach_sub_id != VMCI_INVALID_ID)
                vmci_event_unsubscribe(detach_sub_id);
 
@@ -1607,9 +1564,11 @@ static int vmci_transport_socket_init(struct vsock_sock *vsk,
        vmci_trans(vsk)->qp_handle = VMCI_INVALID_HANDLE;
        vmci_trans(vsk)->qpair = NULL;
        vmci_trans(vsk)->produce_size = vmci_trans(vsk)->consume_size = 0;
-       vmci_trans(vsk)->attach_sub_id = vmci_trans(vsk)->detach_sub_id =
-               VMCI_INVALID_ID;
+       vmci_trans(vsk)->detach_sub_id = VMCI_INVALID_ID;
        vmci_trans(vsk)->notify_ops = NULL;
+       INIT_LIST_HEAD(&vmci_trans(vsk)->elem);
+       vmci_trans(vsk)->sk = &vsk->sk;
+       spin_lock_init(&vmci_trans(vsk)->lock);
        if (psk) {
                vmci_trans(vsk)->queue_pair_size =
                        vmci_trans(psk)->queue_pair_size;
@@ -1629,29 +1588,57 @@ static int vmci_transport_socket_init(struct vsock_sock *vsk,
        return 0;
 }
 
-static void vmci_transport_destruct(struct vsock_sock *vsk)
+static void vmci_transport_free_resources(struct list_head *transport_list)
 {
-       if (vmci_trans(vsk)->attach_sub_id != VMCI_INVALID_ID) {
-               vmci_event_unsubscribe(vmci_trans(vsk)->attach_sub_id);
-               vmci_trans(vsk)->attach_sub_id = VMCI_INVALID_ID;
-       }
+       while (!list_empty(transport_list)) {
+               struct vmci_transport *transport =
+                   list_first_entry(transport_list, struct vmci_transport,
+                                    elem);
+               list_del(&transport->elem);
 
-       if (vmci_trans(vsk)->detach_sub_id != VMCI_INVALID_ID) {
-               vmci_event_unsubscribe(vmci_trans(vsk)->detach_sub_id);
-               vmci_trans(vsk)->detach_sub_id = VMCI_INVALID_ID;
-       }
+               if (transport->detach_sub_id != VMCI_INVALID_ID) {
+                       vmci_event_unsubscribe(transport->detach_sub_id);
+                       transport->detach_sub_id = VMCI_INVALID_ID;
+               }
 
-       if (!vmci_handle_is_invalid(vmci_trans(vsk)->qp_handle)) {
-               vmci_qpair_detach(&vmci_trans(vsk)->qpair);
-               vmci_trans(vsk)->qp_handle = VMCI_INVALID_HANDLE;
-               vmci_trans(vsk)->produce_size = 0;
-               vmci_trans(vsk)->consume_size = 0;
+               if (!vmci_handle_is_invalid(transport->qp_handle)) {
+                       vmci_qpair_detach(&transport->qpair);
+                       transport->qp_handle = VMCI_INVALID_HANDLE;
+                       transport->produce_size = 0;
+                       transport->consume_size = 0;
+               }
+
+               kfree(transport);
        }
+}
+
+static void vmci_transport_cleanup(struct work_struct *work)
+{
+       LIST_HEAD(pending);
+
+       spin_lock_bh(&vmci_transport_cleanup_lock);
+       list_replace_init(&vmci_transport_cleanup_list, &pending);
+       spin_unlock_bh(&vmci_transport_cleanup_lock);
+       vmci_transport_free_resources(&pending);
+}
+
+static void vmci_transport_destruct(struct vsock_sock *vsk)
+{
+       /* Ensure that the detach callback doesn't use the sk/vsk
+        * we are about to destruct.
+        */
+       spin_lock_bh(&vmci_trans(vsk)->lock);
+       vmci_trans(vsk)->sk = NULL;
+       spin_unlock_bh(&vmci_trans(vsk)->lock);
 
        if (vmci_trans(vsk)->notify_ops)
                vmci_trans(vsk)->notify_ops->socket_destruct(vsk);
 
-       kfree(vsk->trans);
+       spin_lock_bh(&vmci_transport_cleanup_lock);
+       list_add(&vmci_trans(vsk)->elem, &vmci_transport_cleanup_list);
+       spin_unlock_bh(&vmci_transport_cleanup_lock);
+       schedule_work(&vmci_transport_cleanup_work);
+
        vsk->trans = NULL;
 }
 
@@ -2146,6 +2133,9 @@ module_init(vmci_transport_init);
 
 static void __exit vmci_transport_exit(void)
 {
+       cancel_work_sync(&vmci_transport_cleanup_work);
+       vmci_transport_free_resources(&vmci_transport_cleanup_list);
+
        if (!vmci_handle_is_invalid(vmci_transport_stream_handle)) {
                if (vmci_datagram_destroy_handle(
                        vmci_transport_stream_handle) != VMCI_SUCCESS)
@@ -2164,6 +2154,7 @@ module_exit(vmci_transport_exit);
 
 MODULE_AUTHOR("VMware, Inc.");
 MODULE_DESCRIPTION("VMCI transport for Virtual Sockets");
+MODULE_VERSION("1.0.2.0-k");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("vmware_vsock");
 MODULE_ALIAS_NETPROTO(PF_VSOCK);
index ce6c9623d5f069029ce58294bcc7de9bc3728fcd..2ad46f39649f8130d9f19ce48ccf8a8b4309797d 100644 (file)
@@ -119,10 +119,12 @@ struct vmci_transport {
        u64 queue_pair_size;
        u64 queue_pair_min_size;
        u64 queue_pair_max_size;
-       u32 attach_sub_id;
        u32 detach_sub_id;
        union vmci_transport_notify notify;
        struct vmci_transport_notify_ops *notify_ops;
+       struct list_head elem;
+       struct sock *sk;
+       spinlock_t lock; /* protects sk. */
 };
 
 int vmci_transport_register(void);
index 4f5543dd25243e0c959dacb00327155cd4e27e2b..da72ed32f14385c3e218cda196e5b4c3511c896e 100644 (file)
@@ -174,6 +174,16 @@ config CFG80211_INTERNAL_REGDB
 
          Most distributions have a CRDA package.  So if unsure, say N.
 
+config CFG80211_CRDA_SUPPORT
+       bool "support CRDA" if CFG80211_INTERNAL_REGDB
+       default y
+       depends on CFG80211
+       help
+         You should enable this option unless you know for sure you have no
+         need for it, for example when using internal regdb (above.)
+
+         If unsure, say Y.
+
 config CFG80211_WEXT
        bool "cfg80211 wireless extensions compatibility" if !CFG80211_WEXT_EXPORT
        depends on CFG80211
index 3893409dee95b3ba23c39b369a57c48c6e838d9b..b0915515640efed1ff4795103c0572aba48c38f4 100644 (file)
@@ -419,6 +419,7 @@ use_default_name:
        device_initialize(&rdev->wiphy.dev);
        rdev->wiphy.dev.class = &ieee80211_class;
        rdev->wiphy.dev.platform_data = rdev;
+       device_enable_async_suspend(&rdev->wiphy.dev);
 
        INIT_LIST_HEAD(&rdev->destroy_list);
        spin_lock_init(&rdev->destroy_list_lock);
@@ -460,6 +461,9 @@ use_default_name:
 
        rdev->wiphy.max_num_csa_counters = 1;
 
+       rdev->wiphy.max_sched_scan_plans = 1;
+       rdev->wiphy.max_sched_scan_plan_interval = U32_MAX;
+
        return &rdev->wiphy;
 }
 EXPORT_SYMBOL(wiphy_new_nm);
@@ -635,7 +639,7 @@ int wiphy_register(struct wiphy *wiphy)
                if (WARN_ON(!sband->n_channels))
                        return -EINVAL;
                /*
-                * on 60gHz band, there are no legacy rates, so
+                * on 60GHz band, there are no legacy rates, so
                 * n_bitrates is 0
                 */
                if (WARN_ON(band != IEEE80211_BAND_60GHZ &&
index b9d5bc8c148d32ecb8156d19c2652c49824f41cd..a618b4b86fa4be96a6582f22d5c012fbddf78d44 100644 (file)
@@ -137,6 +137,7 @@ struct cfg80211_internal_bss {
        struct list_head list;
        struct list_head hidden_list;
        struct rb_node rbn;
+       u64 ts_boottime;
        unsigned long ts;
        unsigned long refcount;
        atomic_t hold;
index 5d8748b4c8a2d20f76c38a8394888c7fe56924bf..c71e274c810ac6610df652ec029838000ee7c69d 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright 2015      Intel Deutschland GmbH
  */
 
 #include <linux/if.h>
@@ -478,6 +479,12 @@ nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
        [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
 };
 
+static const struct nla_policy
+nl80211_plan_policy[NL80211_SCHED_SCAN_PLAN_MAX + 1] = {
+       [NL80211_SCHED_SCAN_PLAN_INTERVAL] = { .type = NLA_U32 },
+       [NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 },
+};
+
 static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
                                     struct netlink_callback *cb,
                                     struct cfg80211_registered_device **rdev,
@@ -1303,7 +1310,13 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                    nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
                                rdev->wiphy.max_sched_scan_ie_len) ||
                    nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
-                              rdev->wiphy.max_match_sets))
+                              rdev->wiphy.max_match_sets) ||
+                   nla_put_u32(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+                               rdev->wiphy.max_sched_scan_plans) ||
+                   nla_put_u32(msg, NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+                               rdev->wiphy.max_sched_scan_plan_interval) ||
+                   nla_put_u32(msg, NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+                               rdev->wiphy.max_sched_scan_plan_iterations))
                        goto nla_put_failure;
 
                if ((rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
@@ -2403,6 +2416,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
                }
        }
 
+       if (rdev->ops->get_tx_power) {
+               int dbm, ret;
+
+               ret = rdev_get_tx_power(rdev, wdev, &dbm);
+               if (ret == 0 &&
+                   nla_put_u32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
+                               DBM_TO_MBM(dbm)))
+                       goto nla_put_failure;
+       }
+
        if (wdev->ssid_len) {
                if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
                        goto nla_put_failure;
@@ -3409,12 +3432,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                                           wdev->iftype))
                return -EINVAL;
 
-       if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
-               params.acl = parse_acl_data(&rdev->wiphy, info);
-               if (IS_ERR(params.acl))
-                       return PTR_ERR(params.acl);
-       }
-
        if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
                params.smps_mode =
                        nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]);
@@ -3438,6 +3455,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                params.smps_mode = NL80211_SMPS_OFF;
        }
 
+       if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
+               params.acl = parse_acl_data(&rdev->wiphy, info);
+               if (IS_ERR(params.acl))
+                       return PTR_ERR(params.acl);
+       }
+
        wdev_lock(wdev);
        err = rdev_start_ap(rdev, dev, &params);
        if (!err) {
@@ -3945,10 +3968,13 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
                                  struct station_parameters *params,
                                  enum cfg80211_station_type statype)
 {
-       if (params->listen_interval != -1)
+       if (params->listen_interval != -1 &&
+           statype != CFG80211_STA_AP_CLIENT_UNASSOC)
                return -EINVAL;
+
        if (params->aid &&
-           !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+           !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
+           statype != CFG80211_STA_AP_CLIENT_UNASSOC)
                return -EINVAL;
 
        /* When you run into this, adjust the code below for the new flag */
@@ -3998,7 +4024,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
                params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
        }
 
-       if (statype != CFG80211_STA_TDLS_PEER_SETUP) {
+       if (statype != CFG80211_STA_TDLS_PEER_SETUP &&
+           statype != CFG80211_STA_AP_CLIENT_UNASSOC) {
                /* reject other things that can't change */
                if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD)
                        return -EINVAL;
@@ -4010,7 +4037,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
                        return -EINVAL;
        }
 
-       if (statype != CFG80211_STA_AP_CLIENT) {
+       if (statype != CFG80211_STA_AP_CLIENT &&
+           statype != CFG80211_STA_AP_CLIENT_UNASSOC) {
                if (params->vlan)
                        return -EINVAL;
        }
@@ -4022,6 +4050,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
                        return -EOPNOTSUPP;
                break;
        case CFG80211_STA_AP_CLIENT:
+       case CFG80211_STA_AP_CLIENT_UNASSOC:
                /* accept only the listed bits */
                if (params->sta_flags_mask &
                                ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
@@ -4219,13 +4248,22 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
 
        memset(&params, 0, sizeof(params));
 
-       params.listen_interval = -1;
-
        if (!rdev->ops->change_station)
                return -EOPNOTSUPP;
 
-       if (info->attrs[NL80211_ATTR_STA_AID])
-               return -EINVAL;
+       /*
+        * AID and listen_interval properties can be set only for unassociated
+        * station. Include these parameters here and will check them in
+        * cfg80211_check_station_change().
+        */
+       if (info->attrs[NL80211_ATTR_PEER_AID])
+               params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
+
+       if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
+               params.listen_interval =
+                    nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
+       else
+               params.listen_interval = -1;
 
        if (!info->attrs[NL80211_ATTR_MAC])
                return -EINVAL;
@@ -4252,9 +4290,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                        nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
        }
 
-       if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
-               return -EINVAL;
-
        if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
                return -EINVAL;
 
@@ -4918,56 +4953,6 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
-static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
-       [NL80211_ATTR_REG_RULE_FLAGS]           = { .type = NLA_U32 },
-       [NL80211_ATTR_FREQ_RANGE_START]         = { .type = NLA_U32 },
-       [NL80211_ATTR_FREQ_RANGE_END]           = { .type = NLA_U32 },
-       [NL80211_ATTR_FREQ_RANGE_MAX_BW]        = { .type = NLA_U32 },
-       [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]  = { .type = NLA_U32 },
-       [NL80211_ATTR_POWER_RULE_MAX_EIRP]      = { .type = NLA_U32 },
-       [NL80211_ATTR_DFS_CAC_TIME]             = { .type = NLA_U32 },
-};
-
-static int parse_reg_rule(struct nlattr *tb[],
-       struct ieee80211_reg_rule *reg_rule)
-{
-       struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
-       struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
-
-       if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
-               return -EINVAL;
-       if (!tb[NL80211_ATTR_FREQ_RANGE_START])
-               return -EINVAL;
-       if (!tb[NL80211_ATTR_FREQ_RANGE_END])
-               return -EINVAL;
-       if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
-               return -EINVAL;
-       if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
-               return -EINVAL;
-
-       reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
-
-       freq_range->start_freq_khz =
-               nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
-       freq_range->end_freq_khz =
-               nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
-       freq_range->max_bandwidth_khz =
-               nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
-
-       power_rule->max_eirp =
-               nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
-
-       if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
-               power_rule->max_antenna_gain =
-                       nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
-
-       if (tb[NL80211_ATTR_DFS_CAC_TIME])
-               reg_rule->dfs_cac_ms =
-                       nla_get_u32(tb[NL80211_ATTR_DFS_CAC_TIME]);
-
-       return 0;
-}
-
 static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
        char *data = NULL;
@@ -5599,6 +5584,57 @@ out_err:
        return err;
 }
 
+#ifdef CONFIG_CFG80211_CRDA_SUPPORT
+static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
+       [NL80211_ATTR_REG_RULE_FLAGS]           = { .type = NLA_U32 },
+       [NL80211_ATTR_FREQ_RANGE_START]         = { .type = NLA_U32 },
+       [NL80211_ATTR_FREQ_RANGE_END]           = { .type = NLA_U32 },
+       [NL80211_ATTR_FREQ_RANGE_MAX_BW]        = { .type = NLA_U32 },
+       [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]  = { .type = NLA_U32 },
+       [NL80211_ATTR_POWER_RULE_MAX_EIRP]      = { .type = NLA_U32 },
+       [NL80211_ATTR_DFS_CAC_TIME]             = { .type = NLA_U32 },
+};
+
+static int parse_reg_rule(struct nlattr *tb[],
+       struct ieee80211_reg_rule *reg_rule)
+{
+       struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
+       struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
+
+       if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
+               return -EINVAL;
+       if (!tb[NL80211_ATTR_FREQ_RANGE_START])
+               return -EINVAL;
+       if (!tb[NL80211_ATTR_FREQ_RANGE_END])
+               return -EINVAL;
+       if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
+               return -EINVAL;
+       if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+               return -EINVAL;
+
+       reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
+
+       freq_range->start_freq_khz =
+               nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
+       freq_range->end_freq_khz =
+               nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
+       freq_range->max_bandwidth_khz =
+               nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
+
+       power_rule->max_eirp =
+               nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
+
+       if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
+               power_rule->max_antenna_gain =
+                       nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
+
+       if (tb[NL80211_ATTR_DFS_CAC_TIME])
+               reg_rule->dfs_cac_ms =
+                       nla_get_u32(tb[NL80211_ATTR_DFS_CAC_TIME]);
+
+       return 0;
+}
+
 static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
@@ -5675,6 +5711,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
        kfree(rd);
        return r;
 }
+#endif /* CONFIG_CFG80211_CRDA_SUPPORT */
 
 static int validate_scan_freqs(struct nlattr *freqs)
 {
@@ -5960,14 +5997,100 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
+static int
+nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans,
+                              struct cfg80211_sched_scan_request *request,
+                              struct nlattr **attrs)
+{
+       int tmp, err, i = 0;
+       struct nlattr *attr;
+
+       if (!attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) {
+               u32 interval;
+
+               /*
+                * If scan plans are not specified,
+                * %NL80211_ATTR_SCHED_SCAN_INTERVAL must be specified. In this
+                * case one scan plan will be set with the specified scan
+                * interval and infinite number of iterations.
+                */
+               if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+                       return -EINVAL;
+
+               interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
+               if (!interval)
+                       return -EINVAL;
+
+               request->scan_plans[0].interval =
+                       DIV_ROUND_UP(interval, MSEC_PER_SEC);
+               if (!request->scan_plans[0].interval)
+                       return -EINVAL;
+
+               if (request->scan_plans[0].interval >
+                   wiphy->max_sched_scan_plan_interval)
+                       request->scan_plans[0].interval =
+                               wiphy->max_sched_scan_plan_interval;
+
+               return 0;
+       }
+
+       nla_for_each_nested(attr, attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp) {
+               struct nlattr *plan[NL80211_SCHED_SCAN_PLAN_MAX + 1];
+
+               if (WARN_ON(i >= n_plans))
+                       return -EINVAL;
+
+               err = nla_parse(plan, NL80211_SCHED_SCAN_PLAN_MAX,
+                               nla_data(attr), nla_len(attr),
+                               nl80211_plan_policy);
+               if (err)
+                       return err;
+
+               if (!plan[NL80211_SCHED_SCAN_PLAN_INTERVAL])
+                       return -EINVAL;
+
+               request->scan_plans[i].interval =
+                       nla_get_u32(plan[NL80211_SCHED_SCAN_PLAN_INTERVAL]);
+               if (!request->scan_plans[i].interval ||
+                   request->scan_plans[i].interval >
+                   wiphy->max_sched_scan_plan_interval)
+                       return -EINVAL;
+
+               if (plan[NL80211_SCHED_SCAN_PLAN_ITERATIONS]) {
+                       request->scan_plans[i].iterations =
+                               nla_get_u32(plan[NL80211_SCHED_SCAN_PLAN_ITERATIONS]);
+                       if (!request->scan_plans[i].iterations ||
+                           (request->scan_plans[i].iterations >
+                            wiphy->max_sched_scan_plan_iterations))
+                               return -EINVAL;
+               } else if (i < n_plans - 1) {
+                       /*
+                        * All scan plans but the last one must specify
+                        * a finite number of iterations
+                        */
+                       return -EINVAL;
+               }
+
+               i++;
+       }
+
+       /*
+        * The last scan plan must not specify the number of
+        * iterations, it is supposed to run infinitely
+        */
+       if (request->scan_plans[n_plans - 1].iterations)
+               return  -EINVAL;
+
+       return 0;
+}
+
 static struct cfg80211_sched_scan_request *
 nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
                         struct nlattr **attrs)
 {
        struct cfg80211_sched_scan_request *request;
        struct nlattr *attr;
-       int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
-       u32 interval;
+       int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i, n_plans = 0;
        enum ieee80211_band band;
        size_t ie_len;
        struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
@@ -5976,13 +6099,6 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
        if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE]))
                return ERR_PTR(-EINVAL);
 
-       if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
-               return ERR_PTR(-EINVAL);
-
-       interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
-       if (interval == 0)
-               return ERR_PTR(-EINVAL);
-
        if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
                n_channels = validate_scan_freqs(
                                attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
@@ -6046,9 +6162,37 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
        if (ie_len > wiphy->max_sched_scan_ie_len)
                return ERR_PTR(-EINVAL);
 
+       if (attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) {
+               /*
+                * NL80211_ATTR_SCHED_SCAN_INTERVAL must not be specified since
+                * each scan plan already specifies its own interval
+                */
+               if (attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+                       return ERR_PTR(-EINVAL);
+
+               nla_for_each_nested(attr,
+                                   attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp)
+                       n_plans++;
+       } else {
+               /*
+                * The scan interval attribute is kept for backward
+                * compatibility. If no scan plans are specified and sched scan
+                * interval is specified, one scan plan will be set with this
+                * scan interval and infinite number of iterations.
+                */
+               if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+                       return ERR_PTR(-EINVAL);
+
+               n_plans = 1;
+       }
+
+       if (!n_plans || n_plans > wiphy->max_sched_scan_plans)
+               return ERR_PTR(-EINVAL);
+
        request = kzalloc(sizeof(*request)
                        + sizeof(*request->ssids) * n_ssids
                        + sizeof(*request->match_sets) * n_match_sets
+                       + sizeof(*request->scan_plans) * n_plans
                        + sizeof(*request->channels) * n_channels
                        + ie_len, GFP_KERNEL);
        if (!request)
@@ -6076,6 +6220,18 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
        }
        request->n_match_sets = n_match_sets;
 
+       if (n_match_sets)
+               request->scan_plans = (void *)(request->match_sets +
+                                              n_match_sets);
+       else if (request->ie)
+               request->scan_plans = (void *)(request->ie + ie_len);
+       else if (n_ssids)
+               request->scan_plans = (void *)(request->ssids + n_ssids);
+       else
+               request->scan_plans = (void *)(request->channels + n_channels);
+
+       request->n_scan_plans = n_plans;
+
        i = 0;
        if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
                /* user specified, bail out if channel not found */
@@ -6238,7 +6394,10 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
                request->delay =
                        nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_DELAY]);
 
-       request->interval = interval;
+       err = nl80211_parse_sched_scan_plans(wiphy, n_plans, request, attrs);
+       if (err)
+               goto out_free;
+
        request->scan_start = jiffies;
 
        return request;
@@ -6591,6 +6750,11 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
                        jiffies_to_msecs(jiffies - intbss->ts)))
                goto nla_put_failure;
 
+       if (intbss->ts_boottime &&
+           nla_put_u64(msg, NL80211_BSS_LAST_SEEN_BOOTTIME,
+                       intbss->ts_boottime))
+               goto nla_put_failure;
+
        switch (rdev->wiphy.signal_type) {
        case CFG80211_SIGNAL_TYPE_MBM:
                if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
@@ -8831,7 +8995,7 @@ static int nl80211_send_wowlan_tcp(struct sk_buff *msg,
 static int nl80211_send_wowlan_nd(struct sk_buff *msg,
                                  struct cfg80211_sched_scan_request *req)
 {
-       struct nlattr *nd, *freqs, *matches, *match;
+       struct nlattr *nd, *freqs, *matches, *match, *scan_plans, *scan_plan;
        int i;
 
        if (!req)
@@ -8841,7 +9005,9 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
        if (!nd)
                return -ENOBUFS;
 
-       if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, req->interval))
+       if (req->n_scan_plans == 1 &&
+           nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL,
+                       req->scan_plans[0].interval * 1000))
                return -ENOBUFS;
 
        if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY, req->delay))
@@ -8868,6 +9034,23 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
                nla_nest_end(msg, matches);
        }
 
+       scan_plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS);
+       if (!scan_plans)
+               return -ENOBUFS;
+
+       for (i = 0; i < req->n_scan_plans; i++) {
+               scan_plan = nla_nest_start(msg, i + 1);
+               if (!scan_plan ||
+                   nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
+                               req->scan_plans[i].interval) ||
+                   (req->scan_plans[i].iterations &&
+                    nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+                                req->scan_plans[i].iterations)))
+                       return -ENOBUFS;
+               nla_nest_end(msg, scan_plan);
+       }
+       nla_nest_end(msg, scan_plans);
+
        nla_nest_end(msg, nd);
 
        return 0;
@@ -9938,6 +10121,9 @@ static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
                                if (!wdev->netdev && !wdev->p2p_started)
                                        return -ENETDOWN;
                        }
+
+                       if (!vcmd->doit)
+                               return -EOPNOTSUPP;
                } else {
                        wdev = NULL;
                }
@@ -9957,6 +10143,193 @@ static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
        return -EOPNOTSUPP;
 }
 
+static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
+                                      struct netlink_callback *cb,
+                                      struct cfg80211_registered_device **rdev,
+                                      struct wireless_dev **wdev)
+{
+       u32 vid, subcmd;
+       unsigned int i;
+       int vcmd_idx = -1;
+       int err;
+       void *data = NULL;
+       unsigned int data_len = 0;
+
+       rtnl_lock();
+
+       if (cb->args[0]) {
+               /* subtract the 1 again here */
+               struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
+               struct wireless_dev *tmp;
+
+               if (!wiphy) {
+                       err = -ENODEV;
+                       goto out_unlock;
+               }
+               *rdev = wiphy_to_rdev(wiphy);
+               *wdev = NULL;
+
+               if (cb->args[1]) {
+                       list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
+                               if (tmp->identifier == cb->args[1] - 1) {
+                                       *wdev = tmp;
+                                       break;
+                               }
+                       }
+               }
+
+               /* keep rtnl locked in successful case */
+               return 0;
+       }
+
+       err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+                         nl80211_fam.attrbuf, nl80211_fam.maxattr,
+                         nl80211_policy);
+       if (err)
+               goto out_unlock;
+
+       if (!nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID] ||
+           !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) {
+               err = -EINVAL;
+               goto out_unlock;
+       }
+
+       *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk),
+                                          nl80211_fam.attrbuf);
+       if (IS_ERR(*wdev))
+               *wdev = NULL;
+
+       *rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk),
+                                          nl80211_fam.attrbuf);
+       if (IS_ERR(*rdev)) {
+               err = PTR_ERR(*rdev);
+               goto out_unlock;
+       }
+
+       vid = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID]);
+       subcmd = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]);
+
+       for (i = 0; i < (*rdev)->wiphy.n_vendor_commands; i++) {
+               const struct wiphy_vendor_command *vcmd;
+
+               vcmd = &(*rdev)->wiphy.vendor_commands[i];
+
+               if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)
+                       continue;
+
+               if (!vcmd->dumpit) {
+                       err = -EOPNOTSUPP;
+                       goto out_unlock;
+               }
+
+               vcmd_idx = i;
+               break;
+       }
+
+       if (vcmd_idx < 0) {
+               err = -EOPNOTSUPP;
+               goto out_unlock;
+       }
+
+       if (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]) {
+               data = nla_data(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]);
+               data_len = nla_len(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]);
+       }
+
+       /* 0 is the first index - add 1 to parse only once */
+       cb->args[0] = (*rdev)->wiphy_idx + 1;
+       /* add 1 to know if it was NULL */
+       cb->args[1] = *wdev ? (*wdev)->identifier + 1 : 0;
+       cb->args[2] = vcmd_idx;
+       cb->args[3] = (unsigned long)data;
+       cb->args[4] = data_len;
+
+       /* keep rtnl locked in successful case */
+       return 0;
+ out_unlock:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_vendor_cmd_dump(struct sk_buff *skb,
+                                  struct netlink_callback *cb)
+{
+       struct cfg80211_registered_device *rdev;
+       struct wireless_dev *wdev;
+       unsigned int vcmd_idx;
+       const struct wiphy_vendor_command *vcmd;
+       void *data;
+       int data_len;
+       int err;
+       struct nlattr *vendor_data;
+
+       err = nl80211_prepare_vendor_dump(skb, cb, &rdev, &wdev);
+       if (err)
+               return err;
+
+       vcmd_idx = cb->args[2];
+       data = (void *)cb->args[3];
+       data_len = cb->args[4];
+       vcmd = &rdev->wiphy.vendor_commands[vcmd_idx];
+
+       if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV |
+                          WIPHY_VENDOR_CMD_NEED_NETDEV)) {
+               if (!wdev)
+                       return -EINVAL;
+               if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV &&
+                   !wdev->netdev)
+                       return -EINVAL;
+
+               if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) {
+                       if (wdev->netdev &&
+                           !netif_running(wdev->netdev))
+                               return -ENETDOWN;
+                       if (!wdev->netdev && !wdev->p2p_started)
+                               return -ENETDOWN;
+               }
+       }
+
+       while (1) {
+               void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid,
+                                          cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                          NL80211_CMD_VENDOR);
+               if (!hdr)
+                       break;
+
+               if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+                   (wdev && nla_put_u64(skb, NL80211_ATTR_WDEV,
+                                        wdev_id(wdev)))) {
+                       genlmsg_cancel(skb, hdr);
+                       break;
+               }
+
+               vendor_data = nla_nest_start(skb, NL80211_ATTR_VENDOR_DATA);
+               if (!vendor_data) {
+                       genlmsg_cancel(skb, hdr);
+                       break;
+               }
+
+               err = vcmd->dumpit(&rdev->wiphy, wdev, skb, data, data_len,
+                                  (unsigned long *)&cb->args[5]);
+               nla_nest_end(skb, vendor_data);
+
+               if (err == -ENOBUFS || err == -ENOENT) {
+                       genlmsg_cancel(skb, hdr);
+                       break;
+               } else if (err) {
+                       genlmsg_cancel(skb, hdr);
+                       goto out;
+               }
+
+               genlmsg_end(skb, hdr);
+       }
+
+       err = skb->len;
+ out:
+       rtnl_unlock();
+       return err;
+}
+
 struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
                                           enum nl80211_commands cmd,
                                           enum nl80211_attrs attr,
@@ -10533,6 +10906,7 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_RTNL,
                /* can be retrieved by unprivileged users */
        },
+#ifdef CONFIG_CFG80211_CRDA_SUPPORT
        {
                .cmd = NL80211_CMD_SET_REG,
                .doit = nl80211_set_reg,
@@ -10540,6 +10914,7 @@ static const struct genl_ops nl80211_ops[] = {
                .flags = GENL_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_RTNL,
        },
+#endif
        {
                .cmd = NL80211_CMD_REQ_SET_REG,
                .doit = nl80211_req_set_reg,
@@ -10994,6 +11369,7 @@ static const struct genl_ops nl80211_ops[] = {
        {
                .cmd = NL80211_CMD_VENDOR,
                .doit = nl80211_vendor_cmd,
+               .dumpit = nl80211_vendor_cmd_dump,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_WIPHY |
index 2510b231451ec8c7e0f0f33d523739dce3387ca3..2e8d6f39ed564ac7111be036a1f735da1d87d68b 100644 (file)
@@ -135,10 +135,7 @@ static spinlock_t reg_indoor_lock;
 /* Used to track the userspace process controlling the indoor setting */
 static u32 reg_is_indoor_portid;
 
-/* Max number of consecutive attempts to communicate with CRDA  */
-#define REG_MAX_CRDA_TIMEOUTS 10
-
-static u32 reg_crda_timeouts;
+static void restore_regulatory_settings(bool reset_user);
 
 static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
 {
@@ -226,9 +223,6 @@ static DECLARE_DELAYED_WORK(reg_check_chans, reg_check_chans_work);
 static void reg_todo(struct work_struct *work);
 static DECLARE_WORK(reg_work, reg_todo);
 
-static void reg_timeout_work(struct work_struct *work);
-static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work);
-
 /* We keep a static world regulatory domain in case of the absence of CRDA */
 static const struct ieee80211_regdomain world_regdom = {
        .n_reg_rules = 8,
@@ -262,7 +256,7 @@ static const struct ieee80211_regdomain world_regdom = {
                REG_RULE(5745-10, 5825+10, 80, 6, 20,
                        NL80211_RRF_NO_IR),
 
-               /* IEEE 802.11ad (60gHz), channels 1..3 */
+               /* IEEE 802.11ad (60GHz), channels 1..3 */
                REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0),
        }
 };
@@ -279,6 +273,9 @@ MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
 
 static void reg_free_request(struct regulatory_request *request)
 {
+       if (request == &core_request_world)
+               return;
+
        if (request != get_last_request())
                kfree(request);
 }
@@ -453,68 +450,70 @@ reg_copy_regd(const struct ieee80211_regdomain *src_regd)
 }
 
 #ifdef CONFIG_CFG80211_INTERNAL_REGDB
-struct reg_regdb_search_request {
-       char alpha2[2];
+struct reg_regdb_apply_request {
        struct list_head list;
+       const struct ieee80211_regdomain *regdom;
 };
 
-static LIST_HEAD(reg_regdb_search_list);
-static DEFINE_MUTEX(reg_regdb_search_mutex);
+static LIST_HEAD(reg_regdb_apply_list);
+static DEFINE_MUTEX(reg_regdb_apply_mutex);
 
-static void reg_regdb_search(struct work_struct *work)
+static void reg_regdb_apply(struct work_struct *work)
 {
-       struct reg_regdb_search_request *request;
-       const struct ieee80211_regdomain *curdom, *regdom = NULL;
-       int i;
+       struct reg_regdb_apply_request *request;
 
        rtnl_lock();
 
-       mutex_lock(&reg_regdb_search_mutex);
-       while (!list_empty(&reg_regdb_search_list)) {
-               request = list_first_entry(&reg_regdb_search_list,
-                                          struct reg_regdb_search_request,
+       mutex_lock(&reg_regdb_apply_mutex);
+       while (!list_empty(&reg_regdb_apply_list)) {
+               request = list_first_entry(&reg_regdb_apply_list,
+                                          struct reg_regdb_apply_request,
                                           list);
                list_del(&request->list);
 
-               for (i = 0; i < reg_regdb_size; i++) {
-                       curdom = reg_regdb[i];
-
-                       if (alpha2_equal(request->alpha2, curdom->alpha2)) {
-                               regdom = reg_copy_regd(curdom);
-                               break;
-                       }
-               }
-
+               set_regdom(request->regdom, REGD_SOURCE_INTERNAL_DB);
                kfree(request);
        }
-       mutex_unlock(&reg_regdb_search_mutex);
-
-       if (!IS_ERR_OR_NULL(regdom))
-               set_regdom(regdom, REGD_SOURCE_INTERNAL_DB);
+       mutex_unlock(&reg_regdb_apply_mutex);
 
        rtnl_unlock();
 }
 
-static DECLARE_WORK(reg_regdb_work, reg_regdb_search);
+static DECLARE_WORK(reg_regdb_work, reg_regdb_apply);
 
-static void reg_regdb_query(const char *alpha2)
+static int reg_query_builtin(const char *alpha2)
 {
-       struct reg_regdb_search_request *request;
+       const struct ieee80211_regdomain *regdom = NULL;
+       struct reg_regdb_apply_request *request;
+       unsigned int i;
 
-       if (!alpha2)
-               return;
+       for (i = 0; i < reg_regdb_size; i++) {
+               if (alpha2_equal(alpha2, reg_regdb[i]->alpha2)) {
+                       regdom = reg_regdb[i];
+                       break;
+               }
+       }
+
+       if (!regdom)
+               return -ENODATA;
 
-       request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL);
+       request = kzalloc(sizeof(struct reg_regdb_apply_request), GFP_KERNEL);
        if (!request)
-               return;
+               return -ENOMEM;
 
-       memcpy(request->alpha2, alpha2, 2);
+       request->regdom = reg_copy_regd(regdom);
+       if (IS_ERR_OR_NULL(request->regdom)) {
+               kfree(request);
+               return -ENOMEM;
+       }
 
-       mutex_lock(&reg_regdb_search_mutex);
-       list_add_tail(&request->list, &reg_regdb_search_list);
-       mutex_unlock(&reg_regdb_search_mutex);
+       mutex_lock(&reg_regdb_apply_mutex);
+       list_add_tail(&request->list, &reg_regdb_apply_list);
+       mutex_unlock(&reg_regdb_apply_mutex);
 
        schedule_work(&reg_regdb_work);
+
+       return 0;
 }
 
 /* Feel free to add any other sanity checks here */
@@ -525,9 +524,45 @@ static void reg_regdb_size_check(void)
 }
 #else
 static inline void reg_regdb_size_check(void) {}
-static inline void reg_regdb_query(const char *alpha2) {}
+static inline int reg_query_builtin(const char *alpha2)
+{
+       return -ENODATA;
+}
 #endif /* CONFIG_CFG80211_INTERNAL_REGDB */
 
+#ifdef CONFIG_CFG80211_CRDA_SUPPORT
+/* Max number of consecutive attempts to communicate with CRDA  */
+#define REG_MAX_CRDA_TIMEOUTS 10
+
+static u32 reg_crda_timeouts;
+
+static void crda_timeout_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(crda_timeout, crda_timeout_work);
+
+static void crda_timeout_work(struct work_struct *work)
+{
+       REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
+       rtnl_lock();
+       reg_crda_timeouts++;
+       restore_regulatory_settings(true);
+       rtnl_unlock();
+}
+
+static void cancel_crda_timeout(void)
+{
+       cancel_delayed_work(&crda_timeout);
+}
+
+static void cancel_crda_timeout_sync(void)
+{
+       cancel_delayed_work_sync(&crda_timeout);
+}
+
+static void reset_crda_timeouts(void)
+{
+       reg_crda_timeouts = 0;
+}
+
 /*
  * This lets us keep regulatory code which is updated on a regulatory
  * basis in userspace.
@@ -536,13 +571,11 @@ static int call_crda(const char *alpha2)
 {
        char country[12];
        char *env[] = { country, NULL };
+       int ret;
 
        snprintf(country, sizeof(country), "COUNTRY=%c%c",
                 alpha2[0], alpha2[1]);
 
-       /* query internal regulatory database (if it exists) */
-       reg_regdb_query(alpha2);
-
        if (reg_crda_timeouts > REG_MAX_CRDA_TIMEOUTS) {
                pr_debug("Exceeded CRDA call max attempts. Not calling CRDA\n");
                return -EINVAL;
@@ -554,18 +587,34 @@ static int call_crda(const char *alpha2)
        else
                pr_debug("Calling CRDA to update world regulatory domain\n");
 
-       return kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, env);
+       ret = kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, env);
+       if (ret)
+               return ret;
+
+       queue_delayed_work(system_power_efficient_wq,
+                          &crda_timeout, msecs_to_jiffies(3142));
+       return 0;
+}
+#else
+static inline void cancel_crda_timeout(void) {}
+static inline void cancel_crda_timeout_sync(void) {}
+static inline void reset_crda_timeouts(void) {}
+static inline int call_crda(const char *alpha2)
+{
+       return -ENODATA;
 }
+#endif /* CONFIG_CFG80211_CRDA_SUPPORT */
 
-static enum reg_request_treatment
-reg_call_crda(struct regulatory_request *request)
+static bool reg_query_database(struct regulatory_request *request)
 {
-       if (call_crda(request->alpha2))
-               return REG_REQ_IGNORE;
+       /* query internal regulatory database (if it exists) */
+       if (reg_query_builtin(request->alpha2) == 0)
+               return true;
 
-       queue_delayed_work(system_power_efficient_wq,
-                          &reg_timeout, msecs_to_jiffies(3142));
-       return REG_REQ_OK;
+       if (call_crda(request->alpha2) == 0)
+               return true;
+
+       return false;
 }
 
 bool reg_is_valid_request(const char *alpha2)
@@ -1040,8 +1089,8 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
        return ERR_PTR(-EINVAL);
 }
 
-const struct ieee80211_reg_rule *__freq_reg_info(struct wiphy *wiphy,
-                                                u32 center_freq, u32 min_bw)
+static const struct ieee80211_reg_rule *
+__freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 min_bw)
 {
        const struct ieee80211_regdomain *regd = reg_get_regdomain(wiphy);
        const struct ieee80211_reg_rule *reg_rule = NULL;
@@ -1081,11 +1130,11 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
 }
 EXPORT_SYMBOL(reg_initiator_name);
 
-#ifdef CONFIG_CFG80211_REG_DEBUG
 static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
                                    struct ieee80211_channel *chan,
                                    const struct ieee80211_reg_rule *reg_rule)
 {
+#ifdef CONFIG_CFG80211_REG_DEBUG
        const struct ieee80211_power_rule *power_rule;
        const struct ieee80211_freq_range *freq_range;
        char max_antenna_gain[32], bw[32];
@@ -1096,7 +1145,7 @@ static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
        if (!power_rule->max_antenna_gain)
                snprintf(max_antenna_gain, sizeof(max_antenna_gain), "N/A");
        else
-               snprintf(max_antenna_gain, sizeof(max_antenna_gain), "%d",
+               snprintf(max_antenna_gain, sizeof(max_antenna_gain), "%d mBi",
                         power_rule->max_antenna_gain);
 
        if (reg_rule->flags & NL80211_RRF_AUTO_BW)
@@ -1110,19 +1159,12 @@ static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
        REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n",
                      chan->center_freq);
 
-       REG_DBG_PRINT("%d KHz - %d KHz @ %s), (%s mBi, %d mBm)\n",
+       REG_DBG_PRINT("(%d KHz - %d KHz @ %s), (%s, %d mBm)\n",
                      freq_range->start_freq_khz, freq_range->end_freq_khz,
                      bw, max_antenna_gain,
                      power_rule->max_eirp);
-}
-#else
-static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
-                                   struct ieee80211_channel *chan,
-                                   const struct ieee80211_reg_rule *reg_rule)
-{
-       return;
-}
 #endif
+}
 
 /*
  * Note that right now we assume the desired channel bandwidth
@@ -1311,7 +1353,8 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
        return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS);
 }
 #else
-static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
+static enum reg_request_treatment
+reg_ignore_cell_hint(struct regulatory_request *pending_request)
 {
        return REG_REQ_IGNORE;
 }
@@ -1846,7 +1889,7 @@ static void reg_set_request_processed(void)
                need_more_processing = true;
        spin_unlock(&reg_requests_lock);
 
-       cancel_delayed_work(&reg_timeout);
+       cancel_crda_timeout();
 
        if (need_more_processing)
                schedule_work(&reg_work);
@@ -1858,19 +1901,18 @@ static void reg_set_request_processed(void)
  *
  * The wireless subsystem can use this function to process
  * a regulatory request issued by the regulatory core.
- *
- * Returns one of the different reg request treatment values.
  */
 static enum reg_request_treatment
 reg_process_hint_core(struct regulatory_request *core_request)
 {
+       if (reg_query_database(core_request)) {
+               core_request->intersect = false;
+               core_request->processed = false;
+               reg_update_last_request(core_request);
+               return REG_REQ_OK;
+       }
 
-       core_request->intersect = false;
-       core_request->processed = false;
-
-       reg_update_last_request(core_request);
-
-       return reg_call_crda(core_request);
+       return REG_REQ_IGNORE;
 }
 
 static enum reg_request_treatment
@@ -1915,8 +1957,6 @@ __reg_process_hint_user(struct regulatory_request *user_request)
  *
  * The wireless subsystem can use this function to process
  * a regulatory request initiated by userspace.
- *
- * Returns one of the different reg request treatment values.
  */
 static enum reg_request_treatment
 reg_process_hint_user(struct regulatory_request *user_request)
@@ -1925,20 +1965,20 @@ reg_process_hint_user(struct regulatory_request *user_request)
 
        treatment = __reg_process_hint_user(user_request);
        if (treatment == REG_REQ_IGNORE ||
-           treatment == REG_REQ_ALREADY_SET) {
-               reg_free_request(user_request);
-               return treatment;
-       }
+           treatment == REG_REQ_ALREADY_SET)
+               return REG_REQ_IGNORE;
 
        user_request->intersect = treatment == REG_REQ_INTERSECT;
        user_request->processed = false;
 
-       reg_update_last_request(user_request);
-
-       user_alpha2[0] = user_request->alpha2[0];
-       user_alpha2[1] = user_request->alpha2[1];
+       if (reg_query_database(user_request)) {
+               reg_update_last_request(user_request);
+               user_alpha2[0] = user_request->alpha2[0];
+               user_alpha2[1] = user_request->alpha2[1];
+               return REG_REQ_OK;
+       }
 
-       return reg_call_crda(user_request);
+       return REG_REQ_IGNORE;
 }
 
 static enum reg_request_treatment
@@ -1986,16 +2026,12 @@ reg_process_hint_driver(struct wiphy *wiphy,
        case REG_REQ_OK:
                break;
        case REG_REQ_IGNORE:
-               reg_free_request(driver_request);
-               return treatment;
+               return REG_REQ_IGNORE;
        case REG_REQ_INTERSECT:
-               /* fall through */
        case REG_REQ_ALREADY_SET:
                regd = reg_copy_regd(get_cfg80211_regdom());
-               if (IS_ERR(regd)) {
-                       reg_free_request(driver_request);
+               if (IS_ERR(regd))
                        return REG_REQ_IGNORE;
-               }
 
                tmp = get_wiphy_regdom(wiphy);
                rcu_assign_pointer(wiphy->regd, regd);
@@ -2006,8 +2042,6 @@ reg_process_hint_driver(struct wiphy *wiphy,
        driver_request->intersect = treatment == REG_REQ_INTERSECT;
        driver_request->processed = false;
 
-       reg_update_last_request(driver_request);
-
        /*
         * Since CRDA will not be called in this case as we already
         * have applied the requested regulatory domain before we just
@@ -2015,11 +2049,17 @@ reg_process_hint_driver(struct wiphy *wiphy,
         */
        if (treatment == REG_REQ_ALREADY_SET) {
                nl80211_send_reg_change_event(driver_request);
+               reg_update_last_request(driver_request);
                reg_set_request_processed();
-               return treatment;
+               return REG_REQ_ALREADY_SET;
        }
 
-       return reg_call_crda(driver_request);
+       if (reg_query_database(driver_request)) {
+               reg_update_last_request(driver_request);
+               return REG_REQ_OK;
+       }
+
+       return REG_REQ_IGNORE;
 }
 
 static enum reg_request_treatment
@@ -2085,12 +2125,11 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
        case REG_REQ_OK:
                break;
        case REG_REQ_IGNORE:
-               /* fall through */
+               return REG_REQ_IGNORE;
        case REG_REQ_ALREADY_SET:
                reg_free_request(country_ie_request);
-               return treatment;
+               return REG_REQ_ALREADY_SET;
        case REG_REQ_INTERSECT:
-               reg_free_request(country_ie_request);
                /*
                 * This doesn't happen yet, not sure we
                 * ever want to support it for this case.
@@ -2102,9 +2141,12 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
        country_ie_request->intersect = false;
        country_ie_request->processed = false;
 
-       reg_update_last_request(country_ie_request);
+       if (reg_query_database(country_ie_request)) {
+               reg_update_last_request(country_ie_request);
+               return REG_REQ_OK;
+       }
 
-       return reg_call_crda(country_ie_request);
+       return REG_REQ_IGNORE;
 }
 
 /* This processes *all* regulatory hints */
@@ -2118,11 +2160,11 @@ static void reg_process_hint(struct regulatory_request *reg_request)
 
        switch (reg_request->initiator) {
        case NL80211_REGDOM_SET_BY_CORE:
-               reg_process_hint_core(reg_request);
-               return;
+               treatment = reg_process_hint_core(reg_request);
+               break;
        case NL80211_REGDOM_SET_BY_USER:
-               reg_process_hint_user(reg_request);
-               return;
+               treatment = reg_process_hint_user(reg_request);
+               break;
        case NL80211_REGDOM_SET_BY_DRIVER:
                if (!wiphy)
                        goto out_free;
@@ -2138,6 +2180,12 @@ static void reg_process_hint(struct regulatory_request *reg_request)
                goto out_free;
        }
 
+       if (treatment == REG_REQ_IGNORE)
+               goto out_free;
+
+       WARN(treatment != REG_REQ_OK && treatment != REG_REQ_ALREADY_SET,
+            "unexpected treatment value %d\n", treatment);
+
        /* This is required so that the orig_* parameters are saved.
         * NOTE: treatment must be set for any case that reaches here!
         */
@@ -2345,7 +2393,7 @@ int regulatory_hint_user(const char *alpha2,
        request->user_reg_hint_type = user_reg_hint_type;
 
        /* Allow calling CRDA again */
-       reg_crda_timeouts = 0;
+       reset_crda_timeouts();
 
        queue_regulatory_request(request);
 
@@ -2417,7 +2465,7 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
        request->initiator = NL80211_REGDOM_SET_BY_DRIVER;
 
        /* Allow calling CRDA again */
-       reg_crda_timeouts = 0;
+       reset_crda_timeouts();
 
        queue_regulatory_request(request);
 
@@ -2473,7 +2521,7 @@ void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band,
        request->country_ie_env = env;
 
        /* Allow calling CRDA again */
-       reg_crda_timeouts = 0;
+       reset_crda_timeouts();
 
        queue_regulatory_request(request);
        request = NULL;
@@ -2874,11 +2922,8 @@ static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
        }
 
        request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx);
-       if (!request_wiphy) {
-               queue_delayed_work(system_power_efficient_wq,
-                                  &reg_timeout, 0);
+       if (!request_wiphy)
                return -ENODEV;
-       }
 
        if (!driver_request->intersect) {
                if (request_wiphy->regd)
@@ -2935,11 +2980,8 @@ static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd,
        }
 
        request_wiphy = wiphy_idx_to_wiphy(country_ie_request->wiphy_idx);
-       if (!request_wiphy) {
-               queue_delayed_work(system_power_efficient_wq,
-                                  &reg_timeout, 0);
+       if (!request_wiphy)
                return -ENODEV;
-       }
 
        if (country_ie_request->intersect)
                return -EINVAL;
@@ -2966,7 +3008,7 @@ int set_regdom(const struct ieee80211_regdomain *rd,
        }
 
        if (regd_src == REGD_SOURCE_CRDA)
-               reg_crda_timeouts = 0;
+               reset_crda_timeouts();
 
        lr = get_last_request();
 
@@ -3123,15 +3165,6 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy)
        lr->country_ie_env = ENVIRON_ANY;
 }
 
-static void reg_timeout_work(struct work_struct *work)
-{
-       REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
-       rtnl_lock();
-       reg_crda_timeouts++;
-       restore_regulatory_settings(true);
-       rtnl_unlock();
-}
-
 /*
  * See http://www.fcc.gov/document/5-ghz-unlicensed-spectrum-unii, for
  * UNII band definitions
@@ -3217,7 +3250,7 @@ void regulatory_exit(void)
        struct reg_beacon *reg_beacon, *btmp;
 
        cancel_work_sync(&reg_work);
-       cancel_delayed_work_sync(&reg_timeout);
+       cancel_crda_timeout_sync();
        cancel_delayed_work_sync(&reg_check_chans);
 
        /* Lock to suppress warnings */
index 3a50aa2553bfd777cce2657eee38f0c3a7b34081..14d5369eb778714fe0a97dc14e585cf3790b775c 100644 (file)
@@ -266,8 +266,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
                        spin_lock_bh(&rdev->bss_lock);
                        __cfg80211_bss_expire(rdev, request->scan_start);
                        spin_unlock_bh(&rdev->bss_lock);
-                       request->scan_start =
-                               jiffies + msecs_to_jiffies(request->interval);
+                       request->scan_start = jiffies;
                }
                nl80211_send_sched_scan_results(rdev, request->dev);
        }
@@ -839,6 +838,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
                        found->pub.signal = tmp->pub.signal;
                found->pub.capability = tmp->pub.capability;
                found->ts = tmp->ts;
+               found->ts_boottime = tmp->ts_boottime;
        } else {
                struct cfg80211_internal_bss *new;
                struct cfg80211_internal_bss *hidden;
@@ -938,14 +938,13 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
 }
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
-struct cfg80211_bss*
-cfg80211_inform_bss_width(struct wiphy *wiphy,
-                         struct ieee80211_channel *rx_channel,
-                         enum nl80211_bss_scan_width scan_width,
-                         enum cfg80211_bss_frame_type ftype,
-                         const u8 *bssid, u64 tsf, u16 capability,
-                         u16 beacon_interval, const u8 *ie, size_t ielen,
-                         s32 signal, gfp_t gfp)
+struct cfg80211_bss *
+cfg80211_inform_bss_data(struct wiphy *wiphy,
+                        struct cfg80211_inform_bss *data,
+                        enum cfg80211_bss_frame_type ftype,
+                        const u8 *bssid, u64 tsf, u16 capability,
+                        u16 beacon_interval, const u8 *ie, size_t ielen,
+                        gfp_t gfp)
 {
        struct cfg80211_bss_ies *ies;
        struct ieee80211_channel *channel;
@@ -957,19 +956,21 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
                return NULL;
 
        if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
-                       (signal < 0 || signal > 100)))
+                   (data->signal < 0 || data->signal > 100)))
                return NULL;
 
-       channel = cfg80211_get_bss_channel(wiphy, ie, ielen, rx_channel);
+       channel = cfg80211_get_bss_channel(wiphy, ie, ielen, data->chan);
        if (!channel)
                return NULL;
 
        memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
        tmp.pub.channel = channel;
-       tmp.pub.scan_width = scan_width;
-       tmp.pub.signal = signal;
+       tmp.pub.scan_width = data->scan_width;
+       tmp.pub.signal = data->signal;
        tmp.pub.beacon_interval = beacon_interval;
        tmp.pub.capability = capability;
+       tmp.ts_boottime = data->boottime_ns;
+
        /*
         * If we do not know here whether the IEs are from a Beacon or Probe
         * Response frame, we need to pick one of the options and only use it
@@ -999,7 +1000,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
        }
        rcu_assign_pointer(tmp.pub.ies, ies);
 
-       signal_valid = abs(rx_channel->center_freq - channel->center_freq) <=
+       signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
                wiphy->max_adj_channel_rssi_comp;
        res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
        if (!res)
@@ -1019,15 +1020,15 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
        /* cfg80211_bss_update gives us a referenced result */
        return &res->pub;
 }
-EXPORT_SYMBOL(cfg80211_inform_bss_width);
+EXPORT_SYMBOL(cfg80211_inform_bss_data);
 
-/* Returned bss is reference counted and must be cleaned up appropriately. */
+/* cfg80211_inform_bss_width_frame helper */
 struct cfg80211_bss *
-cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
-                               struct ieee80211_channel *rx_channel,
-                               enum nl80211_bss_scan_width scan_width,
-                               struct ieee80211_mgmt *mgmt, size_t len,
-                               s32 signal, gfp_t gfp)
+cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
+                              struct cfg80211_inform_bss *data,
+                              struct ieee80211_mgmt *mgmt, size_t len,
+                              gfp_t gfp)
+
 {
        struct cfg80211_internal_bss tmp = {}, *res;
        struct cfg80211_bss_ies *ies;
@@ -1040,8 +1041,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
        BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
                        offsetof(struct ieee80211_mgmt, u.beacon.variable));
 
-       trace_cfg80211_inform_bss_width_frame(wiphy, rx_channel, scan_width, mgmt,
-                                             len, signal);
+       trace_cfg80211_inform_bss_frame(wiphy, data, mgmt, len);
 
        if (WARN_ON(!mgmt))
                return NULL;
@@ -1050,14 +1050,14 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
                return NULL;
 
        if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
-                   (signal < 0 || signal > 100)))
+                   (data->signal < 0 || data->signal > 100)))
                return NULL;
 
        if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
                return NULL;
 
        channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
-                                          ielen, rx_channel);
+                                          ielen, data->chan);
        if (!channel)
                return NULL;
 
@@ -1077,12 +1077,13 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
        
        memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
        tmp.pub.channel = channel;
-       tmp.pub.scan_width = scan_width;
-       tmp.pub.signal = signal;
+       tmp.pub.scan_width = data->scan_width;
+       tmp.pub.signal = data->signal;
        tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
        tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
+       tmp.ts_boottime = data->boottime_ns;
 
-       signal_valid = abs(rx_channel->center_freq - channel->center_freq) <=
+       signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
                wiphy->max_adj_channel_rssi_comp;
        res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
        if (!res)
@@ -1102,7 +1103,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
        /* cfg80211_bss_update gives us a referenced result */
        return &res->pub;
 }
-EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);
+EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
 
 void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 {
index a808279a432a9ef1a23976ba169ac33ba377fe6d..0c392d36781b3915abee149399da4f2978ddef1f 100644 (file)
@@ -2670,30 +2670,30 @@ TRACE_EVENT(cfg80211_get_bss,
                  __entry->privacy)
 );
 
-TRACE_EVENT(cfg80211_inform_bss_width_frame,
-       TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
-                enum nl80211_bss_scan_width scan_width,
-                struct ieee80211_mgmt *mgmt, size_t len,
-                s32 signal),
-       TP_ARGS(wiphy, channel, scan_width, mgmt, len, signal),
+TRACE_EVENT(cfg80211_inform_bss_frame,
+       TP_PROTO(struct wiphy *wiphy, struct cfg80211_inform_bss *data,
+                struct ieee80211_mgmt *mgmt, size_t len),
+       TP_ARGS(wiphy, data, mgmt, len),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                CHAN_ENTRY
                __field(enum nl80211_bss_scan_width, scan_width)
                __dynamic_array(u8, mgmt, len)
                __field(s32, signal)
+               __field(u64, ts_boottime)
        ),
        TP_fast_assign(
                WIPHY_ASSIGN;
-               CHAN_ASSIGN(channel);
-               __entry->scan_width = scan_width;
+               CHAN_ASSIGN(data->chan);
+               __entry->scan_width = data->scan_width;
                if (mgmt)
                        memcpy(__get_dynamic_array(mgmt), mgmt, len);
-               __entry->signal = signal;
+               __entry->signal = data->signal;
+               __entry->ts_boottime = data->boottime_ns;
        ),
-       TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d",
+       TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d, tsb:%llu",
                  WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width,
-                 __entry->signal)
+                 __entry->signal, (unsigned long long)__entry->ts_boottime)
 );
 
 DECLARE_EVENT_CLASS(cfg80211_bss_evt,
index 60ce7014e1b094ef0013f27330ab8c0bec24e284..ad7f5b3f9b619ff22cb22da32b7f259abe82ca3d 100644 (file)
@@ -330,8 +330,10 @@ resume:
 
                if (x->sel.family == AF_UNSPEC) {
                        inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
-                       if (inner_mode == NULL)
+                       if (inner_mode == NULL) {
+                               XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
                                goto drop;
+                       }
                }
 
                if (inner_mode->input(x, skb)) {
index 68ada2ca4b60707ac70b45aa6f0a9ea142a9dd16..cc3676eb6239707909e41c34841abbd4ea8958dd 100644 (file)
@@ -19,7 +19,7 @@
 #include <net/dst.h>
 #include <net/xfrm.h>
 
-static int xfrm_output2(struct sock *sk, struct sk_buff *skb);
+static int xfrm_output2(struct net *net, struct sock *sk, struct sk_buff *skb);
 
 static int xfrm_skb_check_space(struct sk_buff *skb)
 {
@@ -131,18 +131,20 @@ out:
 
 int xfrm_output_resume(struct sk_buff *skb, int err)
 {
+       struct net *net = xs_net(skb_dst(skb)->xfrm);
+
        while (likely((err = xfrm_output_one(skb, err)) == 0)) {
                nf_reset(skb);
 
-               err = skb_dst(skb)->ops->local_out(skb);
+               err = skb_dst(skb)->ops->local_out(net, skb->sk, skb);
                if (unlikely(err != 1))
                        goto out;
 
                if (!skb_dst(skb)->xfrm)
-                       return dst_output(skb);
+                       return dst_output(net, skb->sk, skb);
 
                err = nf_hook(skb_dst(skb)->ops->family,
-                             NF_INET_POST_ROUTING, skb->sk, skb,
+                             NF_INET_POST_ROUTING, net, skb->sk, skb,
                              NULL, skb_dst(skb)->dev, xfrm_output2);
                if (unlikely(err != 1))
                        goto out;
@@ -156,12 +158,12 @@ out:
 }
 EXPORT_SYMBOL_GPL(xfrm_output_resume);
 
-static int xfrm_output2(struct sock *sk, struct sk_buff *skb)
+static int xfrm_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        return xfrm_output_resume(skb, 1);
 }
 
-static int xfrm_output_gso(struct sock *sk, struct sk_buff *skb)
+static int xfrm_output_gso(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct sk_buff *segs;
 
@@ -177,7 +179,7 @@ static int xfrm_output_gso(struct sock *sk, struct sk_buff *skb)
                int err;
 
                segs->next = NULL;
-               err = xfrm_output2(sk, segs);
+               err = xfrm_output2(net, sk, segs);
 
                if (unlikely(err)) {
                        kfree_skb_list(nskb);
@@ -196,7 +198,7 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb)
        int err;
 
        if (skb_is_gso(skb))
-               return xfrm_output_gso(sk, skb);
+               return xfrm_output_gso(net, sk, skb);
 
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
                err = skb_checksum_help(skb);
@@ -207,7 +209,7 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb)
                }
        }
 
-       return xfrm_output2(sk, skb);
+       return xfrm_output2(net, sk, skb);
 }
 EXPORT_SYMBOL_GPL(xfrm_output);
 
index 94af3d0657859e98c61d3f9b2babebcab722aec8..aed6a84cf28e7900a120ad8a6cb408a29e5caaa9 100644 (file)
@@ -1208,7 +1208,7 @@ static inline int policy_to_flow_dir(int dir)
        }
 }
 
-static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir,
+static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
                                                 const struct flowi *fl)
 {
        struct xfrm_policy *pol;
@@ -1583,8 +1583,6 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family)
 
                memset(dst + 1, 0, sizeof(*xdst) - sizeof(*dst));
                xdst->flo.ops = &xfrm_bundle_fc_ops;
-               if (afinfo->init_dst)
-                       afinfo->init_dst(net, xdst);
        } else
                xdst = ERR_PTR(-ENOBUFS);
 
@@ -1889,6 +1887,7 @@ static void xfrm_policy_queue_process(unsigned long arg)
        struct sock *sk;
        struct dst_entry *dst;
        struct xfrm_policy *pol = (struct xfrm_policy *)arg;
+       struct net *net = xp_net(pol);
        struct xfrm_policy_queue *pq = &pol->polq;
        struct flowi fl;
        struct sk_buff_head list;
@@ -1905,8 +1904,7 @@ static void xfrm_policy_queue_process(unsigned long arg)
        spin_unlock(&pq->hold_queue.lock);
 
        dst_hold(dst->path);
-       dst = xfrm_lookup(xp_net(pol), dst->path, &fl,
-                         sk, 0);
+       dst = xfrm_lookup(net, dst->path, &fl, sk, 0);
        if (IS_ERR(dst))
                goto purge_queue;
 
@@ -1936,8 +1934,7 @@ static void xfrm_policy_queue_process(unsigned long arg)
 
                xfrm_decode_session(skb, &fl, skb_dst(skb)->ops->family);
                dst_hold(skb_dst(skb)->path);
-               dst = xfrm_lookup(xp_net(pol), skb_dst(skb)->path,
-                                 &fl, skb->sk, 0);
+               dst = xfrm_lookup(net, skb_dst(skb)->path, &fl, skb->sk, 0);
                if (IS_ERR(dst)) {
                        kfree_skb(skb);
                        continue;
@@ -1947,7 +1944,7 @@ static void xfrm_policy_queue_process(unsigned long arg)
                skb_dst_drop(skb);
                skb_dst_set(skb, dst);
 
-               dst_output(skb);
+               dst_output(net, skb->sk, skb);
        }
 
 out:
@@ -1960,7 +1957,7 @@ purge_queue:
        xfrm_pol_put(pol);
 }
 
-static int xdst_queue_output(struct sock *sk, struct sk_buff *skb)
+static int xdst_queue_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        unsigned long sched_next;
        struct dst_entry *dst = skb_dst(skb);
@@ -2187,7 +2184,7 @@ static struct dst_entry *make_blackhole(struct net *net, u16 family,
  */
 struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
                              const struct flowi *fl,
-                             struct sock *sk, int flags)
+                             const struct sock *sk, int flags)
 {
        struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
        struct flow_cache_object *flo;
@@ -2335,7 +2332,7 @@ EXPORT_SYMBOL(xfrm_lookup);
  */
 struct dst_entry *xfrm_lookup_route(struct net *net, struct dst_entry *dst_orig,
                                    const struct flowi *fl,
-                                   struct sock *sk, int flags)
+                                   const struct sock *sk, int flags)
 {
        struct dst_entry *dst = xfrm_lookup(net, dst_orig, fl, sk,
                                            flags | XFRM_LOOKUP_QUEUE |
@@ -2807,7 +2804,6 @@ static struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst,
 
 int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
 {
-       struct net *net;
        int err = 0;
        if (unlikely(afinfo == NULL))
                return -EINVAL;
@@ -2838,26 +2834,6 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
        }
        spin_unlock(&xfrm_policy_afinfo_lock);
 
-       rtnl_lock();
-       for_each_net(net) {
-               struct dst_ops *xfrm_dst_ops;
-
-               switch (afinfo->family) {
-               case AF_INET:
-                       xfrm_dst_ops = &net->xfrm.xfrm4_dst_ops;
-                       break;
-#if IS_ENABLED(CONFIG_IPV6)
-               case AF_INET6:
-                       xfrm_dst_ops = &net->xfrm.xfrm6_dst_ops;
-                       break;
-#endif
-               default:
-                       BUG();
-               }
-               *xfrm_dst_ops = *afinfo->dst_ops;
-       }
-       rtnl_unlock();
-
        return err;
 }
 EXPORT_SYMBOL(xfrm_policy_register_afinfo);
@@ -2893,22 +2869,6 @@ int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
 }
 EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
 
-static void __net_init xfrm_dst_ops_init(struct net *net)
-{
-       struct xfrm_policy_afinfo *afinfo;
-
-       rcu_read_lock();
-       afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET]);
-       if (afinfo)
-               net->xfrm.xfrm4_dst_ops = *afinfo->dst_ops;
-#if IS_ENABLED(CONFIG_IPV6)
-       afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET6]);
-       if (afinfo)
-               net->xfrm.xfrm6_dst_ops = *afinfo->dst_ops;
-#endif
-       rcu_read_unlock();
-}
-
 static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
@@ -3057,7 +3017,6 @@ static int __net_init xfrm_net_init(struct net *net)
        rv = xfrm_policy_init(net);
        if (rv < 0)
                goto out_policy;
-       xfrm_dst_ops_init(net);
        rv = xfrm_sysctl_init(net);
        if (rv < 0)
                goto out_sysctl;
index a8de9e3002000d7eaa76f6764797e5b231d187ff..805681a7d356a7f4984f5dcd30c666291d3ae59a 100644 (file)
@@ -31,6 +31,7 @@
 #if IS_ENABLED(CONFIG_IPV6)
 #include <linux/in6.h>
 #endif
+#include <asm/unaligned.h>
 
 static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
 {
@@ -728,7 +729,9 @@ static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
        memcpy(&p->sel, &x->sel, sizeof(p->sel));
        memcpy(&p->lft, &x->lft, sizeof(p->lft));
        memcpy(&p->curlft, &x->curlft, sizeof(p->curlft));
-       memcpy(&p->stats, &x->stats, sizeof(p->stats));
+       put_unaligned(x->stats.replay_window, &p->stats.replay_window);
+       put_unaligned(x->stats.replay, &p->stats.replay);
+       put_unaligned(x->stats.integrity_failed, &p->stats.integrity_failed);
        memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr));
        p->mode = x->props.mode;
        p->replay_window = x->props.replay_window;
@@ -1928,8 +1931,10 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
        struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL];
        struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
+       struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
+       struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
 
-       if (!lt && !rp && !re)
+       if (!lt && !rp && !re && !et && !rt)
                return err;
 
        /* pedantic mode - thou shalt sayeth replaceth */
index 63e7d50e6a4fe3ca46cbd3b7ae064600bf6696ff..79b4596b5f9a45758776d91affe855c59d6fa39d 100644 (file)
@@ -4,6 +4,7 @@ obj- := dummy.o
 # List of programs to build
 hostprogs-y := test_verifier test_maps
 hostprogs-y += sock_example
+hostprogs-y += fds_example
 hostprogs-y += sockex1
 hostprogs-y += sockex2
 hostprogs-y += sockex3
@@ -13,11 +14,13 @@ hostprogs-y += tracex3
 hostprogs-y += tracex4
 hostprogs-y += tracex5
 hostprogs-y += tracex6
+hostprogs-y += trace_output
 hostprogs-y += lathist
 
 test_verifier-objs := test_verifier.o libbpf.o
 test_maps-objs := test_maps.o libbpf.o
 sock_example-objs := sock_example.o libbpf.o
+fds_example-objs := bpf_load.o libbpf.o fds_example.o
 sockex1-objs := bpf_load.o libbpf.o sockex1_user.o
 sockex2-objs := bpf_load.o libbpf.o sockex2_user.o
 sockex3-objs := bpf_load.o libbpf.o sockex3_user.o
@@ -27,6 +30,7 @@ tracex3-objs := bpf_load.o libbpf.o tracex3_user.o
 tracex4-objs := bpf_load.o libbpf.o tracex4_user.o
 tracex5-objs := bpf_load.o libbpf.o tracex5_user.o
 tracex6-objs := bpf_load.o libbpf.o tracex6_user.o
+trace_output-objs := bpf_load.o libbpf.o trace_output_user.o
 lathist-objs := bpf_load.o libbpf.o lathist_user.o
 
 # Tell kbuild to always build the programs
@@ -40,12 +44,14 @@ always += tracex3_kern.o
 always += tracex4_kern.o
 always += tracex5_kern.o
 always += tracex6_kern.o
+always += trace_output_kern.o
 always += tcbpf1_kern.o
 always += lathist_kern.o
 
 HOSTCFLAGS += -I$(objtree)/usr/include
 
 HOSTCFLAGS_bpf_load.o += -I$(objtree)/usr/include -Wno-unused-variable
+HOSTLOADLIBES_fds_example += -lelf
 HOSTLOADLIBES_sockex1 += -lelf
 HOSTLOADLIBES_sockex2 += -lelf
 HOSTLOADLIBES_sockex3 += -lelf
@@ -55,6 +61,7 @@ HOSTLOADLIBES_tracex3 += -lelf
 HOSTLOADLIBES_tracex4 += -lelf -lrt
 HOSTLOADLIBES_tracex5 += -lelf
 HOSTLOADLIBES_tracex6 += -lelf
+HOSTLOADLIBES_trace_output += -lelf -lrt
 HOSTLOADLIBES_lathist += -lelf
 
 # point this to your LLVM backend with bpf support
@@ -64,3 +71,6 @@ $(obj)/%.o: $(src)/%.c
        clang $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) \
                -D__KERNEL__ -Wno-unused-value -Wno-pointer-sign \
                -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@
+       clang $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) \
+               -D__KERNEL__ -Wno-unused-value -Wno-pointer-sign \
+               -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=asm -o $@.s
index 3a44d3a272af40119b379fe1930244909499fd8d..7ad19e1dbaf45fc6f1951a896c2ebdb427ae6805 100644 (file)
@@ -33,6 +33,12 @@ static int (*bpf_get_current_comm)(void *buf, int buf_size) =
        (void *) BPF_FUNC_get_current_comm;
 static int (*bpf_perf_event_read)(void *map, int index) =
        (void *) BPF_FUNC_perf_event_read;
+static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) =
+       (void *) BPF_FUNC_clone_redirect;
+static int (*bpf_redirect)(int ifindex, int flags) =
+       (void *) BPF_FUNC_redirect;
+static int (*bpf_perf_event_output)(void *ctx, void *map, int index, void *data, int size) =
+       (void *) BPF_FUNC_perf_event_output;
 
 /* llvm builtin functions that eBPF C program may use to
  * emit BPF_LD_ABS and BPF_LD_IND instructions
@@ -86,5 +92,17 @@ static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flag
 #define PT_REGS_RC(x) ((x)->gprs[2])
 #define PT_REGS_SP(x) ((x)->gprs[15])
 
+#elif defined(__aarch64__)
+
+#define PT_REGS_PARM1(x) ((x)->regs[0])
+#define PT_REGS_PARM2(x) ((x)->regs[1])
+#define PT_REGS_PARM3(x) ((x)->regs[2])
+#define PT_REGS_PARM4(x) ((x)->regs[3])
+#define PT_REGS_PARM5(x) ((x)->regs[4])
+#define PT_REGS_RET(x) ((x)->regs[30])
+#define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */
+#define PT_REGS_RC(x) ((x)->regs[0])
+#define PT_REGS_SP(x) ((x)->sp)
+
 #endif
 #endif
diff --git a/samples/bpf/fds_example.c b/samples/bpf/fds_example.c
new file mode 100644 (file)
index 0000000..e2fd16c
--- /dev/null
@@ -0,0 +1,183 @@
+#include <linux/unistd.h>
+#include <linux/bpf.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "bpf_load.h"
+#include "libbpf.h"
+
+#define BPF_F_PIN      (1 << 0)
+#define BPF_F_GET      (1 << 1)
+#define BPF_F_PIN_GET  (BPF_F_PIN | BPF_F_GET)
+
+#define BPF_F_KEY      (1 << 2)
+#define BPF_F_VAL      (1 << 3)
+#define BPF_F_KEY_VAL  (BPF_F_KEY | BPF_F_VAL)
+
+#define BPF_M_UNSPEC   0
+#define BPF_M_MAP      1
+#define BPF_M_PROG     2
+
+static void usage(void)
+{
+       printf("Usage: fds_example [...]\n");
+       printf("       -F <file>   File to pin/get object\n");
+       printf("       -P          |- pin object\n");
+       printf("       -G          `- get object\n");
+       printf("       -m          eBPF map mode\n");
+       printf("       -k <key>    |- map key\n");
+       printf("       -v <value>  `- map value\n");
+       printf("       -p          eBPF prog mode\n");
+       printf("       -o <object> `- object file\n");
+       printf("       -h          Display this help.\n");
+}
+
+static int bpf_map_create(void)
+{
+       return bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(uint32_t),
+                             sizeof(uint32_t), 1024);
+}
+
+static int bpf_prog_create(const char *object)
+{
+       static const struct bpf_insn insns[] = {
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       };
+
+       if (object) {
+               assert(!load_bpf_file((char *)object));
+               return prog_fd[0];
+       } else {
+               return bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER,
+                                    insns, sizeof(insns), "GPL", 0);
+       }
+}
+
+static int bpf_do_map(const char *file, uint32_t flags, uint32_t key,
+                     uint32_t value)
+{
+       int fd, ret;
+
+       if (flags & BPF_F_PIN) {
+               fd = bpf_map_create();
+               printf("bpf: map fd:%d (%s)\n", fd, strerror(errno));
+               assert(fd > 0);
+
+               ret = bpf_obj_pin(fd, file);
+               printf("bpf: pin ret:(%d,%s)\n", ret, strerror(errno));
+               assert(ret == 0);
+       } else {
+               fd = bpf_obj_get(file);
+               printf("bpf: get fd:%d (%s)\n", fd, strerror(errno));
+               assert(fd > 0);
+       }
+
+       if ((flags & BPF_F_KEY_VAL) == BPF_F_KEY_VAL) {
+               ret = bpf_update_elem(fd, &key, &value, 0);
+               printf("bpf: fd:%d u->(%u:%u) ret:(%d,%s)\n", fd, key, value,
+                      ret, strerror(errno));
+               assert(ret == 0);
+       } else if (flags & BPF_F_KEY) {
+               ret = bpf_lookup_elem(fd, &key, &value);
+               printf("bpf: fd:%d l->(%u):%u ret:(%d,%s)\n", fd, key, value,
+                      ret, strerror(errno));
+               assert(ret == 0);
+       }
+
+       return 0;
+}
+
+static int bpf_do_prog(const char *file, uint32_t flags, const char *object)
+{
+       int fd, sock, ret;
+
+       if (flags & BPF_F_PIN) {
+               fd = bpf_prog_create(object);
+               printf("bpf: prog fd:%d (%s)\n", fd, strerror(errno));
+               assert(fd > 0);
+
+               ret = bpf_obj_pin(fd, file);
+               printf("bpf: pin ret:(%d,%s)\n", ret, strerror(errno));
+               assert(ret == 0);
+       } else {
+               fd = bpf_obj_get(file);
+               printf("bpf: get fd:%d (%s)\n", fd, strerror(errno));
+               assert(fd > 0);
+       }
+
+       sock = open_raw_sock("lo");
+       assert(sock > 0);
+
+       ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &fd, sizeof(fd));
+       printf("bpf: sock:%d <- fd:%d attached ret:(%d,%s)\n", sock, fd,
+              ret, strerror(errno));
+       assert(ret == 0);
+
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       const char *file = NULL, *object = NULL;
+       uint32_t key = 0, value = 0, flags = 0;
+       int opt, mode = BPF_M_UNSPEC;
+
+       while ((opt = getopt(argc, argv, "F:PGmk:v:po:")) != -1) {
+               switch (opt) {
+               /* General args */
+               case 'F':
+                       file = optarg;
+                       break;
+               case 'P':
+                       flags |= BPF_F_PIN;
+                       break;
+               case 'G':
+                       flags |= BPF_F_GET;
+                       break;
+               /* Map-related args */
+               case 'm':
+                       mode = BPF_M_MAP;
+                       break;
+               case 'k':
+                       key = strtoul(optarg, NULL, 0);
+                       flags |= BPF_F_KEY;
+                       break;
+               case 'v':
+                       value = strtoul(optarg, NULL, 0);
+                       flags |= BPF_F_VAL;
+                       break;
+               /* Prog-related args */
+               case 'p':
+                       mode = BPF_M_PROG;
+                       break;
+               case 'o':
+                       object = optarg;
+                       break;
+               default:
+                       goto out;
+               }
+       }
+
+       if (!(flags & BPF_F_PIN_GET) || !file)
+               goto out;
+
+       switch (mode) {
+       case BPF_M_MAP:
+               return bpf_do_map(file, flags, key, value);
+       case BPF_M_PROG:
+               return bpf_do_prog(file, flags, object);
+       }
+out:
+       usage();
+       return -1;
+}
index 7e1efa7e2ed7f5b761c7c9bf7ab8a7b2c3c3ab27..65a8d48d2799e43a1cfbd57ba13e353b1fa3856a 100644 (file)
@@ -103,6 +103,25 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
        return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
 }
 
+int bpf_obj_pin(int fd, const char *pathname)
+{
+       union bpf_attr attr = {
+               .pathname       = ptr_to_u64((void *)pathname),
+               .bpf_fd         = fd,
+       };
+
+       return syscall(__NR_bpf, BPF_OBJ_PIN, &attr, sizeof(attr));
+}
+
+int bpf_obj_get(const char *pathname)
+{
+       union bpf_attr attr = {
+               .pathname       = ptr_to_u64((void *)pathname),
+       };
+
+       return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
+}
+
 int open_raw_sock(const char *name)
 {
        struct sockaddr_ll sll;
index 7235e292a03b67417523950797595b8f6fe7de03..014aacf916e4063c28227da658e38e7c011c8c46 100644 (file)
@@ -15,6 +15,9 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
                  const struct bpf_insn *insns, int insn_len,
                  const char *license, int kern_version);
 
+int bpf_obj_pin(int fd, const char *pathname);
+int bpf_obj_get(const char *pathname);
+
 #define LOG_BUF_SIZE 65536
 extern char bpf_log_buf[LOG_BUF_SIZE];
 
@@ -64,6 +67,14 @@ extern char bpf_log_buf[LOG_BUF_SIZE];
                .off   = 0,                                     \
                .imm   = 0 })
 
+#define BPF_MOV32_REG(DST, SRC)                                        \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU | BPF_MOV | BPF_X,             \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
 /* Short form of mov, dst_reg = imm32 */
 
 #define BPF_MOV64_IMM(DST, IMM)                                        \
index 9bfb2eb34563d0158dae21c97c1b37d640e8d62a..fa051b3d53ee0a8f18da0b0701e04d2962c3e4b6 100644 (file)
@@ -5,7 +5,7 @@
 #include <uapi/linux/in.h>
 #include <uapi/linux/tcp.h>
 #include <uapi/linux/filter.h>
-
+#include <uapi/linux/pkt_cls.h>
 #include "bpf_helpers.h"
 
 /* compiler workaround */
@@ -64,4 +64,26 @@ int bpf_prog1(struct __sk_buff *skb)
 
        return 0;
 }
+SEC("redirect_xmit")
+int _redirect_xmit(struct __sk_buff *skb)
+{
+       return bpf_redirect(skb->ifindex + 1, 0);
+}
+SEC("redirect_recv")
+int _redirect_recv(struct __sk_buff *skb)
+{
+       return bpf_redirect(skb->ifindex + 1, 1);
+}
+SEC("clone_redirect_xmit")
+int _clone_redirect_xmit(struct __sk_buff *skb)
+{
+       bpf_clone_redirect(skb, skb->ifindex + 1, 0);
+       return TC_ACT_SHOT;
+}
+SEC("clone_redirect_recv")
+int _clone_redirect_recv(struct __sk_buff *skb)
+{
+       bpf_clone_redirect(skb, skb->ifindex + 1, 1);
+       return TC_ACT_SHOT;
+}
 char _license[] SEC("license") = "GPL";
index ee0f110c9c543b54fd8593607d161598fb29e472..563c507c0a09f3868827ce6ce1e0e88669803016 100644 (file)
 #include <string.h>
 #include <linux/filter.h>
 #include <stddef.h>
+#include <stdbool.h>
+#include <sys/resource.h>
 #include "libbpf.h"
 
 #define MAX_INSNS 512
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
 
+#define MAX_FIXUPS 8
+
 struct bpf_test {
        const char *descr;
        struct bpf_insn insns[MAX_INSNS];
-       int fixup[32];
+       int fixup[MAX_FIXUPS];
+       int prog_array_fixup[MAX_FIXUPS];
        const char *errstr;
+       const char *errstr_unpriv;
        enum {
+               UNDEF,
                ACCEPT,
                REJECT
-       } result;
+       } result, result_unpriv;
        enum bpf_prog_type prog_type;
 };
 
@@ -96,6 +103,7 @@ static struct bpf_test tests[] = {
                        BPF_EXIT_INSN(),
                },
                .errstr = "invalid BPF_LD_IMM insn",
+               .errstr_unpriv = "R1 pointer comparison",
                .result = REJECT,
        },
        {
@@ -109,6 +117,7 @@ static struct bpf_test tests[] = {
                        BPF_EXIT_INSN(),
                },
                .errstr = "invalid BPF_LD_IMM insn",
+               .errstr_unpriv = "R1 pointer comparison",
                .result = REJECT,
        },
        {
@@ -219,6 +228,7 @@ static struct bpf_test tests[] = {
                        BPF_EXIT_INSN(),
                },
                .errstr = "R0 !read_ok",
+               .errstr_unpriv = "R1 pointer comparison",
                .result = REJECT,
        },
        {
@@ -294,7 +304,9 @@ static struct bpf_test tests[] = {
                        BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
                        BPF_EXIT_INSN(),
                },
+               .errstr_unpriv = "R0 leaks addr",
                .result = ACCEPT,
+               .result_unpriv = REJECT,
        },
        {
                "check corrupted spill/fill",
@@ -310,6 +322,7 @@ static struct bpf_test tests[] = {
 
                        BPF_EXIT_INSN(),
                },
+               .errstr_unpriv = "attempt to corrupt spilled",
                .errstr = "corrupted spill",
                .result = REJECT,
        },
@@ -473,6 +486,7 @@ static struct bpf_test tests[] = {
                },
                .fixup = {3},
                .errstr = "R0 invalid mem access",
+               .errstr_unpriv = "R0 leaks addr",
                .result = REJECT,
        },
        {
@@ -495,6 +509,8 @@ static struct bpf_test tests[] = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
                        BPF_EXIT_INSN(),
                },
+               .errstr_unpriv = "R1 pointer comparison",
+               .result_unpriv = REJECT,
                .result = ACCEPT,
        },
        {
@@ -521,6 +537,8 @@ static struct bpf_test tests[] = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
                        BPF_EXIT_INSN(),
                },
+               .errstr_unpriv = "R1 pointer comparison",
+               .result_unpriv = REJECT,
                .result = ACCEPT,
        },
        {
@@ -555,6 +573,8 @@ static struct bpf_test tests[] = {
                        BPF_EXIT_INSN(),
                },
                .fixup = {24},
+               .errstr_unpriv = "R1 pointer comparison",
+               .result_unpriv = REJECT,
                .result = ACCEPT,
        },
        {
@@ -603,6 +623,8 @@ static struct bpf_test tests[] = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
                        BPF_EXIT_INSN(),
                },
+               .errstr_unpriv = "R1 pointer comparison",
+               .result_unpriv = REJECT,
                .result = ACCEPT,
        },
        {
@@ -642,6 +664,8 @@ static struct bpf_test tests[] = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
                        BPF_EXIT_INSN(),
                },
+               .errstr_unpriv = "R1 pointer comparison",
+               .result_unpriv = REJECT,
                .result = ACCEPT,
        },
        {
@@ -699,6 +723,7 @@ static struct bpf_test tests[] = {
                },
                .fixup = {4},
                .errstr = "different pointers",
+               .errstr_unpriv = "R1 pointer comparison",
                .result = REJECT,
        },
        {
@@ -720,6 +745,7 @@ static struct bpf_test tests[] = {
                },
                .fixup = {6},
                .errstr = "different pointers",
+               .errstr_unpriv = "R1 pointer comparison",
                .result = REJECT,
        },
        {
@@ -742,6 +768,7 @@ static struct bpf_test tests[] = {
                },
                .fixup = {7},
                .errstr = "different pointers",
+               .errstr_unpriv = "R1 pointer comparison",
                .result = REJECT,
        },
        {
@@ -752,6 +779,7 @@ static struct bpf_test tests[] = {
                        BPF_EXIT_INSN(),
                },
                .errstr = "invalid bpf_context access",
+               .errstr_unpriv = "R1 leaks addr",
                .result = REJECT,
        },
        {
@@ -762,6 +790,7 @@ static struct bpf_test tests[] = {
                        BPF_EXIT_INSN(),
                },
                .errstr = "invalid bpf_context access",
+               .errstr_unpriv = "R1 leaks addr",
                .result = REJECT,
        },
        {
@@ -772,16 +801,18 @@ static struct bpf_test tests[] = {
                        BPF_EXIT_INSN(),
                },
                .errstr = "invalid bpf_context access",
+               .errstr_unpriv = "R1 leaks addr",
                .result = REJECT,
        },
        {
                "check out of range skb->cb access",
                .insns = {
                        BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
-                                   offsetof(struct __sk_buff, cb[60])),
+                                   offsetof(struct __sk_buff, cb[0]) + 256),
                        BPF_EXIT_INSN(),
                },
                .errstr = "invalid bpf_context access",
+               .errstr_unpriv = "",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_SCHED_ACT,
        },
@@ -803,6 +834,8 @@ static struct bpf_test tests[] = {
                        BPF_EXIT_INSN(),
                },
                .result = ACCEPT,
+               .errstr_unpriv = "R1 leaks addr",
+               .result_unpriv = REJECT,
        },
        {
                "write skb fields from tc_cls_act prog",
@@ -819,6 +852,8 @@ static struct bpf_test tests[] = {
                                    offsetof(struct __sk_buff, cb[3])),
                        BPF_EXIT_INSN(),
                },
+               .errstr_unpriv = "",
+               .result_unpriv = REJECT,
                .result = ACCEPT,
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
        },
@@ -881,6 +916,270 @@ static struct bpf_test tests[] = {
                .result = REJECT,
                .errstr = "invalid stack off=0 size=8",
        },
+       {
+               "unpriv: return pointer",
+               .insns = {
+                       BPF_MOV64_REG(BPF_REG_0, BPF_REG_10),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .result_unpriv = REJECT,
+               .errstr_unpriv = "R0 leaks addr",
+       },
+       {
+               "unpriv: add const to pointer",
+               .insns = {
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .result_unpriv = REJECT,
+               .errstr_unpriv = "R1 pointer arithmetic",
+       },
+       {
+               "unpriv: add pointer to pointer",
+               .insns = {
+                       BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_10),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .result_unpriv = REJECT,
+               .errstr_unpriv = "R1 pointer arithmetic",
+       },
+       {
+               "unpriv: neg pointer",
+               .insns = {
+                       BPF_ALU64_IMM(BPF_NEG, BPF_REG_1, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .result_unpriv = REJECT,
+               .errstr_unpriv = "R1 pointer arithmetic",
+       },
+       {
+               "unpriv: cmp pointer with const",
+               .insns = {
+                       BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .result_unpriv = REJECT,
+               .errstr_unpriv = "R1 pointer comparison",
+       },
+       {
+               "unpriv: cmp pointer with pointer",
+               .insns = {
+                       BPF_JMP_REG(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .result_unpriv = REJECT,
+               .errstr_unpriv = "R10 pointer comparison",
+       },
+       {
+               "unpriv: check that printk is disallowed",
+               .insns = {
+                       BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
+                       BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),
+                       BPF_MOV64_IMM(BPF_REG_2, 8),
+                       BPF_MOV64_REG(BPF_REG_3, BPF_REG_1),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_trace_printk),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "unknown func 6",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
+       {
+               "unpriv: pass pointer to helper function",
+               .insns = {
+                       BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
+                       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+                       BPF_LD_MAP_FD(BPF_REG_1, 0),
+                       BPF_MOV64_REG(BPF_REG_3, BPF_REG_2),
+                       BPF_MOV64_REG(BPF_REG_4, BPF_REG_2),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_update_elem),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .fixup = {3},
+               .errstr_unpriv = "R4 leaks addr",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
+       {
+               "unpriv: indirectly pass pointer on stack to helper function",
+               .insns = {
+                       BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8),
+                       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+                       BPF_LD_MAP_FD(BPF_REG_1, 0),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .fixup = {3},
+               .errstr = "invalid indirect read from stack off -8+0 size 8",
+               .result = REJECT,
+       },
+       {
+               "unpriv: mangle pointer on stack 1",
+               .insns = {
+                       BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8),
+                       BPF_ST_MEM(BPF_W, BPF_REG_10, -8, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "attempt to corrupt spilled",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
+       {
+               "unpriv: mangle pointer on stack 2",
+               .insns = {
+                       BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8),
+                       BPF_ST_MEM(BPF_B, BPF_REG_10, -1, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "attempt to corrupt spilled",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
+       {
+               "unpriv: read pointer from stack in small chunks",
+               .insns = {
+                       BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -8),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr = "invalid size",
+               .result = REJECT,
+       },
+       {
+               "unpriv: write pointer into ctx",
+               .insns = {
+                       BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "R1 leaks addr",
+               .result_unpriv = REJECT,
+               .errstr = "invalid bpf_context access",
+               .result = REJECT,
+       },
+       {
+               "unpriv: write pointer into map elem value",
+               .insns = {
+                       BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
+                       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+                       BPF_LD_MAP_FD(BPF_REG_1, 0),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+                       BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
+                       BPF_STX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .fixup = {3},
+               .errstr_unpriv = "R0 leaks addr",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
+       {
+               "unpriv: partial copy of pointer",
+               .insns = {
+                       BPF_MOV32_REG(BPF_REG_1, BPF_REG_10),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "R10 partial copy",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
+       {
+               "unpriv: pass pointer to tail_call",
+               .insns = {
+                       BPF_MOV64_REG(BPF_REG_3, BPF_REG_1),
+                       BPF_LD_MAP_FD(BPF_REG_2, 0),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_array_fixup = {1},
+               .errstr_unpriv = "R3 leaks addr into helper",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
+       {
+               "unpriv: cmp map pointer with zero",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_1, 0),
+                       BPF_LD_MAP_FD(BPF_REG_1, 0),
+                       BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .fixup = {1},
+               .errstr_unpriv = "R1 pointer comparison",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
+       {
+               "unpriv: write into frame pointer",
+               .insns = {
+                       BPF_MOV64_REG(BPF_REG_10, BPF_REG_1),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr = "frame pointer is read only",
+               .result = REJECT,
+       },
+       {
+               "unpriv: cmp of frame pointer",
+               .insns = {
+                       BPF_JMP_IMM(BPF_JEQ, BPF_REG_10, 0, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "R10 pointer comparison",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
+       {
+               "unpriv: cmp of stack pointer",
+               .insns = {
+                       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+                       BPF_JMP_IMM(BPF_JEQ, BPF_REG_2, 0, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "R2 pointer comparison",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
+       {
+               "unpriv: obfuscate stack pointer",
+               .insns = {
+                       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "R2 pointer arithmetic",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+       },
 };
 
 static int probe_filter_length(struct bpf_insn *fp)
@@ -896,13 +1195,24 @@ static int probe_filter_length(struct bpf_insn *fp)
 
 static int create_map(void)
 {
-       long long key, value = 0;
        int map_fd;
 
-       map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), 1024);
-       if (map_fd < 0) {
+       map_fd = bpf_create_map(BPF_MAP_TYPE_HASH,
+                               sizeof(long long), sizeof(long long), 1024);
+       if (map_fd < 0)
                printf("failed to create map '%s'\n", strerror(errno));
-       }
+
+       return map_fd;
+}
+
+static int create_prog_array(void)
+{
+       int map_fd;
+
+       map_fd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY,
+                               sizeof(int), sizeof(int), 4);
+       if (map_fd < 0)
+               printf("failed to create prog_array '%s'\n", strerror(errno));
 
        return map_fd;
 }
@@ -910,13 +1220,17 @@ static int create_map(void)
 static int test(void)
 {
        int prog_fd, i, pass_cnt = 0, err_cnt = 0;
+       bool unpriv = geteuid() != 0;
 
        for (i = 0; i < ARRAY_SIZE(tests); i++) {
                struct bpf_insn *prog = tests[i].insns;
                int prog_type = tests[i].prog_type;
                int prog_len = probe_filter_length(prog);
                int *fixup = tests[i].fixup;
-               int map_fd = -1;
+               int *prog_array_fixup = tests[i].prog_array_fixup;
+               int expected_result;
+               const char *expected_errstr;
+               int map_fd = -1, prog_array_fd = -1;
 
                if (*fixup) {
                        map_fd = create_map();
@@ -926,13 +1240,31 @@ static int test(void)
                                fixup++;
                        } while (*fixup);
                }
+               if (*prog_array_fixup) {
+                       prog_array_fd = create_prog_array();
+
+                       do {
+                               prog[*prog_array_fixup].imm = prog_array_fd;
+                               prog_array_fixup++;
+                       } while (*prog_array_fixup);
+               }
                printf("#%d %s ", i, tests[i].descr);
 
                prog_fd = bpf_prog_load(prog_type ?: BPF_PROG_TYPE_SOCKET_FILTER,
                                        prog, prog_len * sizeof(struct bpf_insn),
                                        "GPL", 0);
 
-               if (tests[i].result == ACCEPT) {
+               if (unpriv && tests[i].result_unpriv != UNDEF)
+                       expected_result = tests[i].result_unpriv;
+               else
+                       expected_result = tests[i].result;
+
+               if (unpriv && tests[i].errstr_unpriv)
+                       expected_errstr = tests[i].errstr_unpriv;
+               else
+                       expected_errstr = tests[i].errstr;
+
+               if (expected_result == ACCEPT) {
                        if (prog_fd < 0) {
                                printf("FAIL\nfailed to load prog '%s'\n",
                                       strerror(errno));
@@ -947,7 +1279,7 @@ static int test(void)
                                err_cnt++;
                                goto fail;
                        }
-                       if (strstr(bpf_log_buf, tests[i].errstr) == 0) {
+                       if (strstr(bpf_log_buf, expected_errstr) == 0) {
                                printf("FAIL\nunexpected error message: %s",
                                       bpf_log_buf);
                                err_cnt++;
@@ -960,6 +1292,8 @@ static int test(void)
 fail:
                if (map_fd >= 0)
                        close(map_fd);
+               if (prog_array_fd >= 0)
+                       close(prog_array_fd);
                close(prog_fd);
 
        }
@@ -970,5 +1304,8 @@ fail:
 
 int main(void)
 {
+       struct rlimit r = {1 << 20, 1 << 20};
+
+       setrlimit(RLIMIT_MEMLOCK, &r);
        return test();
 }
diff --git a/samples/bpf/trace_output_kern.c b/samples/bpf/trace_output_kern.c
new file mode 100644 (file)
index 0000000..8d8d1ec
--- /dev/null
@@ -0,0 +1,31 @@
+#include <linux/ptrace.h>
+#include <linux/version.h>
+#include <uapi/linux/bpf.h>
+#include "bpf_helpers.h"
+
+struct bpf_map_def SEC("maps") my_map = {
+       .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(u32),
+       .max_entries = 2,
+};
+
+SEC("kprobe/sys_write")
+int bpf_prog1(struct pt_regs *ctx)
+{
+       struct S {
+               u64 pid;
+               u64 cookie;
+       } data;
+
+       memset(&data, 0, sizeof(data));
+       data.pid = bpf_get_current_pid_tgid();
+       data.cookie = 0x12345678;
+
+       bpf_perf_event_output(ctx, &my_map, 0, &data, sizeof(data));
+
+       return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+u32 _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/samples/bpf/trace_output_user.c b/samples/bpf/trace_output_user.c
new file mode 100644 (file)
index 0000000..661a7d0
--- /dev/null
@@ -0,0 +1,196 @@
+/* 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.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <linux/perf_event.h>
+#include <linux/bpf.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/syscall.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <signal.h>
+#include "libbpf.h"
+#include "bpf_load.h"
+
+static int pmu_fd;
+
+int page_size;
+int page_cnt = 8;
+volatile struct perf_event_mmap_page *header;
+
+typedef void (*print_fn)(void *data, int size);
+
+static int perf_event_mmap(int fd)
+{
+       void *base;
+       int mmap_size;
+
+       page_size = getpagesize();
+       mmap_size = page_size * (page_cnt + 1);
+
+       base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+       if (base == MAP_FAILED) {
+               printf("mmap err\n");
+               return -1;
+       }
+
+       header = base;
+       return 0;
+}
+
+static int perf_event_poll(int fd)
+{
+       struct pollfd pfd = { .fd = fd, .events = POLLIN };
+
+       return poll(&pfd, 1, 1000);
+}
+
+struct perf_event_sample {
+       struct perf_event_header header;
+       __u32 size;
+       char data[];
+};
+
+void perf_event_read(print_fn fn)
+{
+       __u64 data_tail = header->data_tail;
+       __u64 data_head = header->data_head;
+       __u64 buffer_size = page_cnt * page_size;
+       void *base, *begin, *end;
+       char buf[256];
+
+       asm volatile("" ::: "memory"); /* in real code it should be smp_rmb() */
+       if (data_head == data_tail)
+               return;
+
+       base = ((char *)header) + page_size;
+
+       begin = base + data_tail % buffer_size;
+       end = base + data_head % buffer_size;
+
+       while (begin != end) {
+               struct perf_event_sample *e;
+
+               e = begin;
+               if (begin + e->header.size > base + buffer_size) {
+                       long len = base + buffer_size - begin;
+
+                       assert(len < e->header.size);
+                       memcpy(buf, begin, len);
+                       memcpy(buf + len, base, e->header.size - len);
+                       e = (void *) buf;
+                       begin = base + e->header.size - len;
+               } else if (begin + e->header.size == base + buffer_size) {
+                       begin = base;
+               } else {
+                       begin += e->header.size;
+               }
+
+               if (e->header.type == PERF_RECORD_SAMPLE) {
+                       fn(e->data, e->size);
+               } else if (e->header.type == PERF_RECORD_LOST) {
+                       struct {
+                               struct perf_event_header header;
+                               __u64 id;
+                               __u64 lost;
+                       } *lost = (void *) e;
+                       printf("lost %lld events\n", lost->lost);
+               } else {
+                       printf("unknown event type=%d size=%d\n",
+                              e->header.type, e->header.size);
+               }
+       }
+
+       __sync_synchronize(); /* smp_mb() */
+       header->data_tail = data_head;
+}
+
+static __u64 time_get_ns(void)
+{
+       struct timespec ts;
+
+       clock_gettime(CLOCK_MONOTONIC, &ts);
+       return ts.tv_sec * 1000000000ull + ts.tv_nsec;
+}
+
+static __u64 start_time;
+
+#define MAX_CNT 100000ll
+
+static void print_bpf_output(void *data, int size)
+{
+       static __u64 cnt;
+       struct {
+               __u64 pid;
+               __u64 cookie;
+       } *e = data;
+
+       if (e->cookie != 0x12345678) {
+               printf("BUG pid %llx cookie %llx sized %d\n",
+                      e->pid, e->cookie, size);
+               kill(0, SIGINT);
+       }
+
+       cnt++;
+
+       if (cnt == MAX_CNT) {
+               printf("recv %lld events per sec\n",
+                      MAX_CNT * 1000000000ll / (time_get_ns() - start_time));
+               kill(0, SIGINT);
+       }
+}
+
+static void test_bpf_perf_event(void)
+{
+       struct perf_event_attr attr = {
+               .sample_type = PERF_SAMPLE_RAW,
+               .type = PERF_TYPE_SOFTWARE,
+               .config = PERF_COUNT_SW_BPF_OUTPUT,
+       };
+       int key = 0;
+
+       pmu_fd = perf_event_open(&attr, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/, 0);
+
+       assert(pmu_fd >= 0);
+       assert(bpf_update_elem(map_fd[0], &key, &pmu_fd, BPF_ANY) == 0);
+       ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
+}
+
+int main(int argc, char **argv)
+{
+       char filename[256];
+       FILE *f;
+
+       snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+
+       if (load_bpf_file(filename)) {
+               printf("%s", bpf_log_buf);
+               return 1;
+       }
+
+       test_bpf_perf_event();
+
+       if (perf_event_mmap(pmu_fd) < 0)
+               return 1;
+
+       f = popen("taskset 1 dd if=/dev/zero of=/dev/null", "r");
+       (void) f;
+
+       start_time = time_get_ns();
+       for (;;) {
+               perf_event_poll(pmu_fd);
+               perf_event_read(print_bpf_output);
+       }
+
+       return 0;
+}
index e4369d86e5885d9b00e97cd7cd901810f2c1c237..26f4039d54b8f6bd8dd0aba99837835ba13c871b 100644 (file)
@@ -4866,7 +4866,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb,
        return NF_ACCEPT;
 }
 
-static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,
+static unsigned int selinux_ipv4_forward(void *priv,
                                         struct sk_buff *skb,
                                         const struct nf_hook_state *state)
 {
@@ -4874,7 +4874,7 @@ static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops,
+static unsigned int selinux_ipv6_forward(void *priv,
                                         struct sk_buff *skb,
                                         const struct nf_hook_state *state)
 {
@@ -4898,7 +4898,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,
        if (sk) {
                struct sk_security_struct *sksec;
 
-               if (sk->sk_state == TCP_LISTEN)
+               if (sk_listener(sk))
                        /* if the socket is the listening state then this
                         * packet is a SYN-ACK packet which means it needs to
                         * be labeled based on the connection/request_sock and
@@ -4924,7 +4924,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,
        return NF_ACCEPT;
 }
 
-static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops,
+static unsigned int selinux_ipv4_output(void *priv,
                                        struct sk_buff *skb,
                                        const struct nf_hook_state *state)
 {
@@ -5005,7 +5005,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
         *       unfortunately, this means more work, but it is only once per
         *       connection. */
        if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL &&
-           !(sk != NULL && sk->sk_state == TCP_LISTEN))
+           !(sk && sk_listener(sk)))
                return NF_ACCEPT;
 #endif
 
@@ -5022,7 +5022,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
                        secmark_perm = PACKET__SEND;
                        peer_sid = SECINITSID_KERNEL;
                }
-       } else if (sk->sk_state == TCP_LISTEN) {
+       } else if (sk_listener(sk)) {
                /* Locally generated packet but the associated socket is in the
                 * listening state which means this is a SYN-ACK packet.  In
                 * this particular case the correct security label is assigned
@@ -5033,7 +5033,11 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
                 * selinux_inet_conn_request().  See also selinux_ip_output()
                 * for similar problems. */
                u32 skb_sid;
-               struct sk_security_struct *sksec = sk->sk_security;
+               struct sk_security_struct *sksec;
+
+               if (sk->sk_state == TCP_NEW_SYN_RECV)
+                       sk = inet_reqsk(sk)->rsk_listener;
+               sksec = sk->sk_security;
                if (selinux_skb_peerlbl_sid(skb, family, &skb_sid))
                        return NF_DROP;
                /* At this point, if the returned skb peerlbl is SECSID_NULL
@@ -5099,7 +5103,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
        return NF_ACCEPT;
 }
 
-static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,
+static unsigned int selinux_ipv4_postroute(void *priv,
                                           struct sk_buff *skb,
                                           const struct nf_hook_state *state)
 {
@@ -5107,7 +5111,7 @@ static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops,
+static unsigned int selinux_ipv6_postroute(void *priv,
                                           struct sk_buff *skb,
                                           const struct nf_hook_state *state)
 {
@@ -6127,21 +6131,18 @@ security_initcall(selinux_init);
 static struct nf_hook_ops selinux_nf_ops[] = {
        {
                .hook =         selinux_ipv4_postroute,
-               .owner =        THIS_MODULE,
                .pf =           NFPROTO_IPV4,
                .hooknum =      NF_INET_POST_ROUTING,
                .priority =     NF_IP_PRI_SELINUX_LAST,
        },
        {
                .hook =         selinux_ipv4_forward,
-               .owner =        THIS_MODULE,
                .pf =           NFPROTO_IPV4,
                .hooknum =      NF_INET_FORWARD,
                .priority =     NF_IP_PRI_SELINUX_FIRST,
        },
        {
                .hook =         selinux_ipv4_output,
-               .owner =        THIS_MODULE,
                .pf =           NFPROTO_IPV4,
                .hooknum =      NF_INET_LOCAL_OUT,
                .priority =     NF_IP_PRI_SELINUX_FIRST,
@@ -6149,14 +6150,12 @@ static struct nf_hook_ops selinux_nf_ops[] = {
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        {
                .hook =         selinux_ipv6_postroute,
-               .owner =        THIS_MODULE,
                .pf =           NFPROTO_IPV6,
                .hooknum =      NF_INET_POST_ROUTING,
                .priority =     NF_IP6_PRI_SELINUX_LAST,
        },
        {
                .hook =         selinux_ipv6_forward,
-               .owner =        THIS_MODULE,
                .pf =           NFPROTO_IPV6,
                .hooknum =      NF_INET_FORWARD,
                .priority =     NF_IP6_PRI_SELINUX_FIRST,
index a455cfc9ec1f614851aba10693800a6acd476049..6d1706c9777e64fc69ca305e6ef55a2d563bb29d 100644 (file)
@@ -21,7 +21,7 @@
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 
-static unsigned int smack_ipv6_output(const struct nf_hook_ops *ops,
+static unsigned int smack_ipv6_output(void *priv,
                                        struct sk_buff *skb,
                                        const struct nf_hook_state *state)
 {
@@ -38,7 +38,7 @@ static unsigned int smack_ipv6_output(const struct nf_hook_ops *ops,
 }
 #endif /* IPV6 */
 
-static unsigned int smack_ipv4_output(const struct nf_hook_ops *ops,
+static unsigned int smack_ipv4_output(void *priv,
                                        struct sk_buff *skb,
                                        const struct nf_hook_state *state)
 {
@@ -57,7 +57,6 @@ static unsigned int smack_ipv4_output(const struct nf_hook_ops *ops,
 static struct nf_hook_ops smack_nf_ops[] = {
        {
                .hook =         smack_ipv4_output,
-               .owner =        THIS_MODULE,
                .pf =           NFPROTO_IPV4,
                .hooknum =      NF_INET_LOCAL_OUT,
                .priority =     NF_IP_PRI_SELINUX_FIRST,
@@ -65,7 +64,6 @@ static struct nf_hook_ops smack_nf_ops[] = {
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        {
                .hook =         smack_ipv6_output,
-               .owner =        THIS_MODULE,
                .pf =           NFPROTO_IPV6,
                .hooknum =      NF_INET_LOCAL_OUT,
                .priority =     NF_IP6_PRI_SELINUX_FIRST,
diff --git a/tools/build/.gitignore b/tools/build/.gitignore
new file mode 100644 (file)
index 0000000..a776371
--- /dev/null
@@ -0,0 +1 @@
+fixdep
diff --git a/tools/build/Build b/tools/build/Build
new file mode 100644 (file)
index 0000000..63a6c34
--- /dev/null
@@ -0,0 +1 @@
+fixdep-y := fixdep.o
index 4c8daaccb82a90565a5aac52afb9c497e8d8580d..4d000bc959b495b695686281912709bc53d10ca6 100644 (file)
@@ -54,15 +54,26 @@ make-cmd = $(call escsq,$(subst \#,\\\#,$(subst $$,$$$$,$(cmd_$(1)))))
 # PHONY targets skipped in both cases.
 any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^)
 
+###
+# Copy dependency data into .cmd file
+#  - gcc -M dependency info
+#  - command line to create object 'cmd_object :='
+dep-cmd = $(if $(wildcard $(fixdep)),                                           \
+           $(fixdep) $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;           \
+           rm -f $(depfile);                                                    \
+           mv -f $(dot-target).tmp $(dot-target).cmd,                           \
+           printf '\# cannot find fixdep (%s)\n' $(fixdep) > $(dot-target).cmd; \
+           printf '\# using basic dep data\n\n' >> $(dot-target).cmd;           \
+           cat $(depfile) >> $(dot-target).cmd;                                 \
+           printf '%s\n' 'cmd_$@ := $(make-cmd)' >> $(dot-target).cmd)
+
 ###
 # if_changed_dep  - execute command if any prerequisite is newer than
 #                   target, or command line has changed and update
 #                   dependencies in the cmd file
 if_changed_dep = $(if $(strip $(any-prereq) $(arg-check)),         \
        @set -e;                                                   \
-       $(echo-cmd) $(cmd_$(1));                                   \
-       cat $(depfile) > $(dot-target).cmd;                        \
-       printf '%s\n' 'cmd_$@ := $(make-cmd)' >> $(dot-target).cmd)
+       $(echo-cmd) $(cmd_$(1)) && $(dep-cmd))
 
 # if_changed      - execute command if any prerequisite is newer than
 #                   target, or command line has changed
index aa5e092c4352bad50660f3c262ce9819b70120fc..a47bffbae1595bdb8d9fc111ddf102f868e217e5 100644 (file)
@@ -11,8 +11,9 @@ Unlike the kernel we don't have a single build object 'obj-y' list that where
 we setup source objects, but we support more. This allows one 'Build' file to
 carry a sources list for multiple build objects.
 
-a) Build framework makefiles
-----------------------------
+
+Build framework makefiles
+-------------------------
 
 The build framework consists of 2 Makefiles:
 
@@ -23,7 +24,7 @@ While the 'Build.include' file contains just some generic definitions, the
 'Makefile.build' file is the makefile used from the outside. It's
 interface/usage is following:
 
-  $ make -f tools/build/Makefile srctree=$(KSRC) dir=$(DIR) obj=$(OBJECT)
+  $ make -f tools/build/Makefile.build srctree=$(KSRC) dir=$(DIR) obj=$(OBJECT)
 
 where:
 
@@ -38,8 +39,9 @@ called $(OBJECT)-in.o:
 
 which includes all compiled sources described in 'Build' makefiles.
 
-a) Build makefiles
-------------------
+
+Build makefiles
+---------------
 
 The user supplies 'Build' makefiles that contains a objects list, and connects
 the build to nested directories.
@@ -95,8 +97,31 @@ It's only a matter of 2 single commands to create the final binaries:
 
 You can check the 'ex' example in 'tools/build/tests/ex' for more details.
 
-b) Rules
---------
+
+Makefile.include
+----------------
+
+The tools/build/Makefile.include makefile could be included
+via user makefiles to get usefull definitions.
+
+It defines following interface:
+
+  - build macro definition:
+      build := -f $(srctree)/tools/build/Makefile.build dir=. obj
+
+    to make it easier to invoke build like:
+      make $(build)=ex
+
+
+Fixdep
+------
+It is necessary to build the fixdep helper before invoking the build.
+The Makefile.include file adds the fixdep target, that could be
+invoked by the user.
+
+
+Rules
+-----
 
 The build framework provides standard compilation rules to handle .S and .c
 compilation.
@@ -104,8 +129,9 @@ compilation.
 It's possible to include special rule if needed (like we do for flex or bison
 code generation).
 
-c) CFLAGS
----------
+
+CFLAGS
+------
 
 It's possible to alter the standard object C flags in the following way:
 
@@ -115,8 +141,8 @@ It's possible to alter the standard object C flags in the following way:
 This C flags changes has the scope of the Build makefile they are defined in.
 
 
-d) Dependencies
----------------
+Dependencies
+------------
 
 For each built object file 'a.o' the '.a.cmd' is created and holds:
 
@@ -130,8 +156,8 @@ All existing '.cmd' files are included in the Build process to follow properly
 the dependencies and trigger a rebuild when necessary.
 
 
-e) Single rules
----------------
+Single rules
+------------
 
 It's possible to build single object file by choice, like:
 
diff --git a/tools/build/Makefile b/tools/build/Makefile
new file mode 100644 (file)
index 0000000..a930362
--- /dev/null
@@ -0,0 +1,43 @@
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(shell pwd)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+endif
+
+include $(srctree)/tools//scripts/Makefile.include
+
+define allow-override
+  $(if $(or $(findstring environment,$(origin $(1))),\
+            $(findstring command line,$(origin $(1)))),,\
+    $(eval $(1) = $(2)))
+endef
+
+$(call allow-override,CC,$(CROSS_COMPILE)gcc)
+$(call allow-override,LD,$(CROSS_COMPILE)ld)
+
+ifeq ($(V),1)
+  Q =
+else
+  Q = @
+endif
+
+export Q srctree CC LD
+
+MAKEFLAGS := --no-print-directory
+build     := -f $(srctree)/tools/build/Makefile.build dir=. obj
+
+all: fixdep
+
+clean:
+       $(call QUIET_CLEAN, fixdep)
+       $(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
+       $(Q)rm -f fixdep
+
+$(OUTPUT)fixdep-in.o: FORCE
+       $(Q)$(MAKE) $(build)=fixdep
+
+$(OUTPUT)fixdep: $(OUTPUT)fixdep-in.o
+       $(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $<
+
+FORCE:
+
+.PHONY: FORCE
index 0c5f485521d6707786188652cb42a3fb7d23c8e6..4a96473b180f938b038d5e698f3578876eb7c70c 100644 (file)
@@ -21,6 +21,13 @@ endif
 
 build-dir := $(srctree)/tools/build
 
+# Define $(fixdep) for dep-cmd function
+ifeq ($(OUTPUT),)
+  fixdep := $(build-dir)/fixdep
+else
+  fixdep := $(OUTPUT)/fixdep
+endif
+
 # Generic definitions
 include $(build-dir)/Build.include
 
index c8fe6d17711915e50978b3b2cbba6804af5cf9fe..37ff4c9f92f1b5d8c17f8df034f0dad567d146fa 100644 (file)
@@ -53,7 +53,8 @@ FEATURE_TESTS ?=                      \
        libdw-dwarf-unwind              \
        zlib                            \
        lzma                            \
-       get_cpuid
+       get_cpuid                       \
+       bpf
 
 FEATURE_DISPLAY ?=                     \
        dwarf                           \
@@ -71,7 +72,8 @@ FEATURE_DISPLAY ?=                    \
        libdw-dwarf-unwind              \
        zlib                            \
        lzma                            \
-       get_cpuid
+       get_cpuid                       \
+       bpf
 
 # Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features.
 # If in the future we need per-feature checks/flags for features not
@@ -121,8 +123,9 @@ define feature_print_text_code
     MSG = $(shell printf '...%30s: %s' $(1) $(2))
 endef
 
+FEATURE_DUMP_FILENAME = $(OUTPUT)FEATURE-DUMP$(FEATURE_USER)
 FEATURE_DUMP := $(foreach feat,$(FEATURE_DISPLAY),feature-$(feat)($(feature-$(feat))))
-FEATURE_DUMP_FILE := $(shell touch $(OUTPUT)FEATURE-DUMP; cat $(OUTPUT)FEATURE-DUMP)
+FEATURE_DUMP_FILE := $(shell touch $(FEATURE_DUMP_FILENAME); cat $(FEATURE_DUMP_FILENAME))
 
 ifeq ($(dwarf-post-unwind),1)
   FEATURE_DUMP += dwarf-post-unwind($(dwarf-post-unwind-text))
@@ -131,16 +134,16 @@ endif
 # The $(feature_display) controls the default detection message
 # output. It's set if:
 # - detected features differes from stored features from
-#   last build (in FEATURE-DUMP file)
+#   last build (in $(FEATURE_DUMP_FILENAME) file)
 # - one of the $(FEATURE_DISPLAY) is not detected
 # - VF is enabled
 
 ifneq ("$(FEATURE_DUMP)","$(FEATURE_DUMP_FILE)")
-  $(shell echo "$(FEATURE_DUMP)" > $(OUTPUT)FEATURE-DUMP)
+  $(shell echo "$(FEATURE_DUMP)" > $(FEATURE_DUMP_FILENAME))
   feature_display := 1
 endif
 
-feature_display_check = $(eval $(feature_check_code))
+feature_display_check = $(eval $(feature_check_display_code))
 define feature_display_check_code
   ifneq ($(feature-$(1)), 1)
     feature_display := 1
diff --git a/tools/build/Makefile.include b/tools/build/Makefile.include
new file mode 100644 (file)
index 0000000..4e09ad6
--- /dev/null
@@ -0,0 +1,10 @@
+build := -f $(srctree)/tools/build/Makefile.build dir=. obj
+
+ifdef CROSS_COMPILE
+fixdep:
+else
+fixdep:
+       $(Q)$(MAKE) -C $(srctree)/tools/build fixdep
+endif
+
+.PHONY: fixdep
index e43a2971bf5669a8cae7722e8d5058fb9ada56ea..cea04ce9f35cdfb7e5b217077d60b214894b6c48 100644 (file)
@@ -132,10 +132,10 @@ test-libbfd.bin:
        $(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl
 
 test-liberty.bin:
-       $(CC) -Wall -Werror -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' -lbfd -ldl -liberty
+       $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty
 
 test-liberty-z.bin:
-       $(CC) -Wall -Werror -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' -lbfd -ldl -liberty -lz
+       $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty -lz
 
 test-cplus-demangle.bin:
        $(BUILD) -liberty
diff --git a/tools/build/fixdep.c b/tools/build/fixdep.c
new file mode 100644 (file)
index 0000000..1521d36
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * "Optimize" a list of dependencies as spit out by gcc -MD
+ * for the build framework.
+ *
+ * Original author:
+ *   Copyright    2002 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *
+ * This code has been borrowed from kbuild's fixdep (scripts/basic/fixdep.c),
+ * Please check it for detailed explanation. This fixdep borow only the
+ * base transformation of dependecies without the CONFIG mangle.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+char *target;
+char *depfile;
+char *cmdline;
+
+static void usage(void)
+{
+       fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n");
+       exit(1);
+}
+
+/*
+ * Print out the commandline prefixed with cmd_<target filename> :=
+ */
+static void print_cmdline(void)
+{
+       printf("cmd_%s := %s\n\n", target, cmdline);
+}
+
+/*
+ * Important: The below generated source_foo.o and deps_foo.o variable
+ * assignments are parsed not only by make, but also by the rather simple
+ * parser in scripts/mod/sumversion.c.
+ */
+static void parse_dep_file(void *map, size_t len)
+{
+       char *m = map;
+       char *end = m + len;
+       char *p;
+       char s[PATH_MAX];
+       int is_target;
+       int saw_any_target = 0;
+       int is_first_dep = 0;
+
+       while (m < end) {
+               /* Skip any "white space" */
+               while (m < end && (*m == ' ' || *m == '\\' || *m == '\n'))
+                       m++;
+               /* Find next "white space" */
+               p = m;
+               while (p < end && *p != ' ' && *p != '\\' && *p != '\n')
+                       p++;
+               /* Is the token we found a target name? */
+               is_target = (*(p-1) == ':');
+               /* Don't write any target names into the dependency file */
+               if (is_target) {
+                       /* The /next/ file is the first dependency */
+                       is_first_dep = 1;
+               } else {
+                       /* Save this token/filename */
+                       memcpy(s, m, p-m);
+                       s[p - m] = 0;
+
+                       /*
+                        * Do not list the source file as dependency,
+                        * so that kbuild is not confused if a .c file
+                        * is rewritten into .S or vice versa. Storing
+                        * it in source_* is needed for modpost to
+                        * compute srcversions.
+                        */
+                       if (is_first_dep) {
+                               /*
+                                * If processing the concatenation of
+                                * multiple dependency files, only
+                                * process the first target name, which
+                                * will be the original source name,
+                                * and ignore any other target names,
+                                * which will be intermediate temporary
+                                * files.
+                                */
+                               if (!saw_any_target) {
+                                       saw_any_target = 1;
+                                       printf("source_%s := %s\n\n",
+                                               target, s);
+                                       printf("deps_%s := \\\n",
+                                               target);
+                               }
+                               is_first_dep = 0;
+                       } else
+                               printf("  %s \\\n", s);
+               }
+               /*
+                * Start searching for next token immediately after the first
+                * "whitespace" character that follows this token.
+                */
+               m = p + 1;
+       }
+
+       if (!saw_any_target) {
+               fprintf(stderr, "fixdep: parse error; no targets found\n");
+               exit(1);
+       }
+
+       printf("\n%s: $(deps_%s)\n\n", target, target);
+       printf("$(deps_%s):\n", target);
+}
+
+static void print_deps(void)
+{
+       struct stat st;
+       int fd;
+       void *map;
+
+       fd = open(depfile, O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "fixdep: error opening depfile: ");
+               perror(depfile);
+               exit(2);
+       }
+       if (fstat(fd, &st) < 0) {
+               fprintf(stderr, "fixdep: error fstat'ing depfile: ");
+               perror(depfile);
+               exit(2);
+       }
+       if (st.st_size == 0) {
+               fprintf(stderr, "fixdep: %s is empty\n", depfile);
+               close(fd);
+               return;
+       }
+       map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if ((long) map == -1) {
+               perror("fixdep: mmap");
+               close(fd);
+               return;
+       }
+
+       parse_dep_file(map, st.st_size);
+
+       munmap(map, st.st_size);
+
+       close(fd);
+}
+
+int main(int argc, char **argv)
+{
+       if (argc != 4)
+               usage();
+
+       depfile = argv[1];
+       target  = argv[2];
+       cmdline = argv[3];
+
+       print_cmdline();
+       print_deps();
+
+       return 0;
+}
index 429c7d4521016283ef81bc1dbd1e9ae93e8f2581..4d502f9b1a50e5c55e8ddd4b745a77acf72f6a87 100644 (file)
@@ -4,6 +4,7 @@ ex-y += b.o
 ex-y += b.o
 ex-y += empty/
 ex-y += empty2/
+ex-y += inc.o
 
 libex-y += c.o
 libex-y += d.o
index 52d2476073a34c6a8b788bdeec4627806c4864aa..c50d5782ad5a967472a75f84a9d3a81fee19eecf 100644 (file)
@@ -1,19 +1,22 @@
-export srctree := ../../../..
+export srctree := $(abspath ../../../..)
 export CC      := gcc
 export LD      := ld
 export AR      := ar
 
-build := -f $(srctree)/tools/build/Makefile.build dir=. obj
+ex:
+
+include $(srctree)/tools/build/Makefile.include
+
 ex: ex-in.o libex-in.o
        gcc -o $@ $^
 
-ex.%: FORCE
+ex.%: fixdep FORCE
        make -f $(srctree)/tools/build/Makefile.build dir=. $@
 
-ex-in.o: FORCE
+ex-in.o: fixdep FORCE
        make $(build)=ex
 
-libex-in.o: FORCE
+libex-in.o: fixdep FORCE
        make $(build)=libex
 
 clean:
index dc42eb2e1a677414b32001de061eb1868acd5827..57de6074d252857fdbaf4c115a25c254b3bd2a94 100644 (file)
@@ -5,6 +5,7 @@ int c(void);
 int d(void);
 int e(void);
 int f(void);
+int inc(void);
 
 int main(void)
 {
@@ -14,6 +15,7 @@ int main(void)
        d();
        e();
        f();
+       inc();
 
        return 0;
 }
diff --git a/tools/build/tests/ex/inc.c b/tools/build/tests/ex/inc.c
new file mode 100644 (file)
index 0000000..c20f1e9
--- /dev/null
@@ -0,0 +1,8 @@
+#ifdef INCLUDE
+#include "krava.h"
+#endif
+
+int inc(void)
+{
+       return 0;
+}
index 5494f8ea75670f91a77034959b32db9a88ceb203..44d2a0fade677b9a456647120d1bb60f3b711aa1 100755 (executable)
@@ -34,9 +34,36 @@ function test_ex_suffix {
        make -C ex V=1 clean > /dev/null 2>&1
        rm -f ex.out
 }
+
+function test_ex_include {
+       make -C ex V=1 clean > ex.out 2>&1
+
+       # build with krava.h include
+       touch ex/krava.h
+       make -C ex V=1 CFLAGS=-DINCLUDE >> ex.out 2>&1
+
+       if [ ! -x ./ex/ex ]; then
+         echo FAILED
+         exit -1
+       fi
+
+       # build without the include
+       rm -f ex/krava.h ex/ex
+       make -C ex V=1 >> ex.out 2>&1
+
+       if [ ! -x ./ex/ex ]; then
+         echo FAILED
+         exit -1
+       fi
+
+       make -C ex V=1 clean > /dev/null 2>&1
+       rm -f ex.out
+}
+
 echo -n Testing..
 
 test_ex
 test_ex_suffix
+test_ex_include
 
 echo OK
index 9098083869c85a62bc4df5bcf8df96f5ffcc7e00..fa7208a32d763d0ae35e953ccff82c69c772a5c2 100644 (file)
 
 #include <linux/types.h>
 
+/*
+ * Following functions are taken from kernel sources and
+ * break aliasing rules in their original form.
+ *
+ * While kernel is compiled with -fno-strict-aliasing,
+ * perf uses -Wstrict-aliasing=3 which makes build fail
+ * under gcc 4.4.
+ *
+ * Using extra __may_alias__ type to allow aliasing
+ * in this case.
+ */
+typedef __u8  __attribute__((__may_alias__))  __u8_alias_t;
+typedef __u16 __attribute__((__may_alias__)) __u16_alias_t;
+typedef __u32 __attribute__((__may_alias__)) __u32_alias_t;
+typedef __u64 __attribute__((__may_alias__)) __u64_alias_t;
+
 static __always_inline void __read_once_size(const volatile void *p, void *res, int size)
 {
        switch (size) {
-       case 1: *(__u8 *)res = *(volatile __u8 *)p; break;
-       case 2: *(__u16 *)res = *(volatile __u16 *)p; break;
-       case 4: *(__u32 *)res = *(volatile __u32 *)p; break;
-       case 8: *(__u64 *)res = *(volatile __u64 *)p; break;
+       case 1: *(__u8_alias_t  *) res = *(volatile __u8_alias_t  *) p; break;
+       case 2: *(__u16_alias_t *) res = *(volatile __u16_alias_t *) p; break;
+       case 4: *(__u32_alias_t *) res = *(volatile __u32_alias_t *) p; break;
+       case 8: *(__u64_alias_t *) res = *(volatile __u64_alias_t *) p; break;
        default:
                barrier();
                __builtin_memcpy((void *)res, (const void *)p, size);
@@ -60,10 +76,10 @@ static __always_inline void __read_once_size(const volatile void *p, void *res,
 static __always_inline void __write_once_size(volatile void *p, void *res, int size)
 {
        switch (size) {
-       case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
-       case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
-       case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
-       case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
+       case 1: *(volatile  __u8_alias_t *) p = *(__u8_alias_t  *) res; break;
+       case 2: *(volatile __u16_alias_t *) p = *(__u16_alias_t *) res; break;
+       case 4: *(volatile __u32_alias_t *) p = *(__u32_alias_t *) res; break;
+       case 8: *(volatile __u64_alias_t *) p = *(__u64_alias_t *) res; break;
        default:
                barrier();
                __builtin_memcpy((void *)p, (const void *)res, size);
diff --git a/tools/include/linux/err.h b/tools/include/linux/err.h
new file mode 100644 (file)
index 0000000..bdc3dd8
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef __TOOLS_LINUX_ERR_H
+#define __TOOLS_LINUX_ERR_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+#include <asm/errno.h>
+
+/*
+ * Original kernel header comment:
+ *
+ * Kernel pointers have redundant information, so we can use a
+ * scheme where we can return either an error code or a normal
+ * pointer with the same return value.
+ *
+ * This should be a per-architecture thing, to allow different
+ * error and pointer decisions.
+ *
+ * Userspace note:
+ * The same principle works for userspace, because 'error' pointers
+ * fall down to the unused hole far from user space, as described
+ * in Documentation/x86/x86_64/mm.txt for x86_64 arch:
+ *
+ * 0000000000000000 - 00007fffffffffff (=47 bits) user space, different per mm hole caused by [48:63] sign extension
+ * ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole
+ *
+ * It should be the same case for other architectures, because
+ * this code is used in generic kernel code.
+ */
+#define MAX_ERRNO      4095
+
+#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
+
+static inline void * __must_check ERR_PTR(long error_)
+{
+       return (void *) error_;
+}
+
+static inline long __must_check PTR_ERR(__force const void *ptr)
+{
+       return (long) ptr;
+}
+
+static inline bool __must_check IS_ERR(__force const void *ptr)
+{
+       return IS_ERR_VALUE((unsigned long)ptr);
+}
+
+#endif /* _LINUX_ERR_H */
diff --git a/tools/include/linux/filter.h b/tools/include/linux/filter.h
new file mode 100644 (file)
index 0000000..3276625
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Linux Socket Filter Data Structures
+ */
+#ifndef __TOOLS_LINUX_FILTER_H
+#define __TOOLS_LINUX_FILTER_H
+
+#include <linux/bpf.h>
+
+/* ArgX, context and stack frame pointer register positions. Note,
+ * Arg1, Arg2, Arg3, etc are used as argument mappings of function
+ * calls in BPF_CALL instruction.
+ */
+#define BPF_REG_ARG1   BPF_REG_1
+#define BPF_REG_ARG2   BPF_REG_2
+#define BPF_REG_ARG3   BPF_REG_3
+#define BPF_REG_ARG4   BPF_REG_4
+#define BPF_REG_ARG5   BPF_REG_5
+#define BPF_REG_CTX    BPF_REG_6
+#define BPF_REG_FP     BPF_REG_10
+
+/* Additional register mappings for converted user programs. */
+#define BPF_REG_A      BPF_REG_0
+#define BPF_REG_X      BPF_REG_7
+#define BPF_REG_TMP    BPF_REG_8
+
+/* BPF program can access up to 512 bytes of stack space. */
+#define MAX_BPF_STACK  512
+
+/* Helper macros for filter block array initializers. */
+
+/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
+
+#define BPF_ALU64_REG(OP, DST, SRC)                            \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU64 | BPF_OP(OP) | BPF_X,        \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#define BPF_ALU32_REG(OP, DST, SRC)                            \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU | BPF_OP(OP) | BPF_X,          \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
+
+#define BPF_ALU64_IMM(OP, DST, IMM)                            \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU64 | BPF_OP(OP) | BPF_K,        \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+#define BPF_ALU32_IMM(OP, DST, IMM)                            \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU | BPF_OP(OP) | BPF_K,          \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Endianess conversion, cpu_to_{l,b}e(), {l,b}e_to_cpu() */
+
+#define BPF_ENDIAN(TYPE, DST, LEN)                             \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU | BPF_END | BPF_SRC(TYPE),     \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = LEN })
+
+/* Short form of mov, dst_reg = src_reg */
+
+#define BPF_MOV64_REG(DST, SRC)                                        \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU64 | BPF_MOV | BPF_X,           \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#define BPF_MOV32_REG(DST, SRC)                                        \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU | BPF_MOV | BPF_X,             \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+/* Short form of mov, dst_reg = imm32 */
+
+#define BPF_MOV64_IMM(DST, IMM)                                        \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU64 | BPF_MOV | BPF_K,           \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+#define BPF_MOV32_IMM(DST, IMM)                                        \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU | BPF_MOV | BPF_K,             \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Short form of mov based on type,  BPF_X: dst_reg = src_reg, BPF_K: dst_reg = imm32 */
+
+#define BPF_MOV64_RAW(TYPE, DST, SRC, IMM)                     \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE),   \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+#define BPF_MOV32_RAW(TYPE, DST, SRC, IMM)                     \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU | BPF_MOV | BPF_SRC(TYPE),     \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */
+
+#define BPF_LD_ABS(SIZE, IMM)                                  \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS,     \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Indirect packet access, R0 = *(uint *) (skb->data + src_reg + imm32) */
+
+#define BPF_LD_IND(SIZE, SRC, IMM)                             \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_LD | BPF_SIZE(SIZE) | BPF_IND,     \
+               .dst_reg = 0,                                   \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
+
+#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)                       \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,    \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
+
+#define BPF_STX_MEM(SIZE, DST, SRC, OFF)                       \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,    \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
+
+#define BPF_ST_MEM(SIZE, DST, OFF, IMM)                                \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM,     \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */
+
+#define BPF_JMP_REG(OP, DST, SRC, OFF)                         \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_JMP | BPF_OP(OP) | BPF_X,          \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
+
+#define BPF_JMP_IMM(OP, DST, IMM, OFF)                         \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_JMP | BPF_OP(OP) | BPF_K,          \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Function call */
+
+#define BPF_EMIT_CALL(FUNC)                                    \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_JMP | BPF_CALL,                    \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = ((FUNC) - BPF_FUNC_unspec) })
+
+/* Raw code statement block */
+
+#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM)                 \
+       ((struct bpf_insn) {                                    \
+               .code  = CODE,                                  \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Program exit */
+
+#define BPF_EXIT_INSN()                                                \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_JMP | BPF_EXIT,                    \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#endif /* __TOOLS_LINUX_FILTER_H */
index 3653965cf48185a9950bb81aafb12660b306cba6..e8b8a23b9bf4cdac8ccfcf95690f8dfd93e1d6e6 100644 (file)
@@ -1,2 +1,3 @@
 libapi-y += fd/
 libapi-y += fs/
+libapi-y += cpu.o
index fe1b02c2c95bbe1274376f1b20ffebfac7358cc7..d85904dc9b38747dc0feda60f819cf93ceabbdcf 100644 (file)
@@ -21,12 +21,14 @@ CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
 
 RM = rm -f
 
-build  := -f $(srctree)/tools/build/Makefile.build dir=. obj
 API_IN := $(OUTPUT)libapi-in.o
 
+all:
+
 export srctree OUTPUT CC LD CFLAGS V
+include $(srctree)/tools/build/Makefile.include
 
-all: $(LIBFILE)
+all: fixdep $(LIBFILE)
 
 $(API_IN): FORCE
        @$(MAKE) $(build)=libapi
diff --git a/tools/lib/api/cpu.c b/tools/lib/api/cpu.c
new file mode 100644 (file)
index 0000000..8c64893
--- /dev/null
@@ -0,0 +1,18 @@
+#include <stdio.h>
+
+#include "cpu.h"
+#include "fs/fs.h"
+
+int cpu__get_max_freq(unsigned long long *freq)
+{
+       char entry[PATH_MAX];
+       int cpu;
+
+       if (sysfs__read_int("devices/system/cpu/online", &cpu) < 0)
+               return -1;
+
+       snprintf(entry, sizeof(entry),
+                "devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu);
+
+       return sysfs__read_ull(entry, freq);
+}
diff --git a/tools/lib/api/cpu.h b/tools/lib/api/cpu.h
new file mode 100644 (file)
index 0000000..81e9d39
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __API_CPU__
+#define __API_CPU__
+
+int cpu__get_max_freq(unsigned long long *freq);
+
+#endif /* __API_CPU__ */
index 6de5a4f0b501ef33244ef356a63fcec9ac208a18..f4ed9629ae85dcc16b53faf90f41defb960476db 100644 (file)
@@ -1,4 +1,2 @@
 libapi-y += fs.o
-libapi-y += debugfs.o
-libapi-y += findfs.o
-libapi-y += tracefs.o
+libapi-y += tracing_path.o
diff --git a/tools/lib/api/fs/debugfs.c b/tools/lib/api/fs/debugfs.c
deleted file mode 100644 (file)
index eb7cf4d..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-#define _GNU_SOURCE
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <sys/vfs.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mount.h>
-#include <linux/kernel.h>
-
-#include "debugfs.h"
-#include "tracefs.h"
-
-#ifndef DEBUGFS_DEFAULT_PATH
-#define DEBUGFS_DEFAULT_PATH           "/sys/kernel/debug"
-#endif
-
-char debugfs_mountpoint[PATH_MAX + 1] = DEBUGFS_DEFAULT_PATH;
-
-static const char * const debugfs_known_mountpoints[] = {
-       DEBUGFS_DEFAULT_PATH,
-       "/debug",
-       0,
-};
-
-static bool debugfs_found;
-
-bool debugfs_configured(void)
-{
-       return debugfs_find_mountpoint() != NULL;
-}
-
-/* find the path to the mounted debugfs */
-const char *debugfs_find_mountpoint(void)
-{
-       const char *ret;
-
-       if (debugfs_found)
-               return (const char *)debugfs_mountpoint;
-
-       ret = find_mountpoint("debugfs", (long) DEBUGFS_MAGIC,
-                             debugfs_mountpoint, PATH_MAX + 1,
-                             debugfs_known_mountpoints);
-       if (ret)
-               debugfs_found = true;
-
-       return ret;
-}
-
-/* mount the debugfs somewhere if it's not mounted */
-char *debugfs_mount(const char *mountpoint)
-{
-       /* see if it's already mounted */
-       if (debugfs_find_mountpoint())
-               goto out;
-
-       /* if not mounted and no argument */
-       if (mountpoint == NULL) {
-               /* see if environment variable set */
-               mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT);
-               /* if no environment variable, use default */
-               if (mountpoint == NULL)
-                       mountpoint = DEBUGFS_DEFAULT_PATH;
-       }
-
-       if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0)
-               return NULL;
-
-       /* save the mountpoint */
-       debugfs_found = true;
-       strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint));
-out:
-       return debugfs_mountpoint;
-}
-
-int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename)
-{
-       char sbuf[128];
-
-       switch (err) {
-       case ENOENT:
-               if (debugfs_found) {
-                       snprintf(buf, size,
-                                "Error:\tFile %s/%s not found.\n"
-                                "Hint:\tPerhaps this kernel misses some CONFIG_ setting to enable this feature?.\n",
-                                debugfs_mountpoint, filename);
-                       break;
-               }
-               snprintf(buf, size, "%s",
-                        "Error:\tUnable to find debugfs\n"
-                        "Hint:\tWas your kernel compiled with debugfs support?\n"
-                        "Hint:\tIs the debugfs filesystem mounted?\n"
-                        "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'");
-               break;
-       case EACCES: {
-               const char *mountpoint = debugfs_mountpoint;
-
-               if (!access(debugfs_mountpoint, R_OK) && strncmp(filename, "tracing/", 8) == 0) {
-                       const char *tracefs_mntpoint = tracefs_find_mountpoint();
-
-                       if (tracefs_mntpoint)
-                               mountpoint = tracefs_mntpoint;
-               }
-
-               snprintf(buf, size,
-                        "Error:\tNo permissions to read %s/%s\n"
-                        "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
-                        debugfs_mountpoint, filename, mountpoint);
-       }
-               break;
-       default:
-               snprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf)));
-               break;
-       }
-
-       return 0;
-}
-
-int debugfs__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name)
-{
-       char path[PATH_MAX];
-
-       snprintf(path, PATH_MAX, "tracing/events/%s/%s", sys, name ?: "*");
-
-       return debugfs__strerror_open(err, buf, size, path);
-}
diff --git a/tools/lib/api/fs/debugfs.h b/tools/lib/api/fs/debugfs.h
deleted file mode 100644 (file)
index 4550236..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef __API_DEBUGFS_H__
-#define __API_DEBUGFS_H__
-
-#include "findfs.h"
-
-#ifndef DEBUGFS_MAGIC
-#define DEBUGFS_MAGIC          0x64626720
-#endif
-
-#ifndef PERF_DEBUGFS_ENVIRONMENT
-#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
-#endif
-
-bool debugfs_configured(void);
-const char *debugfs_find_mountpoint(void);
-char *debugfs_mount(const char *mountpoint);
-
-extern char debugfs_mountpoint[];
-
-int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename);
-int debugfs__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name);
-
-#endif /* __API_DEBUGFS_H__ */
diff --git a/tools/lib/api/fs/findfs.c b/tools/lib/api/fs/findfs.c
deleted file mode 100644 (file)
index 49946cb..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <sys/vfs.h>
-
-#include "findfs.h"
-
-/* verify that a mountpoint is actually the type we want */
-
-int valid_mountpoint(const char *mount, long magic)
-{
-       struct statfs st_fs;
-
-       if (statfs(mount, &st_fs) < 0)
-               return -ENOENT;
-       else if ((long)st_fs.f_type != magic)
-               return -ENOENT;
-
-       return 0;
-}
-
-/* find the path to a mounted file system */
-const char *find_mountpoint(const char *fstype, long magic,
-                           char *mountpoint, int len,
-                           const char * const *known_mountpoints)
-{
-       const char * const *ptr;
-       char format[128];
-       char type[100];
-       FILE *fp;
-
-       if (known_mountpoints) {
-               ptr = known_mountpoints;
-               while (*ptr) {
-                       if (valid_mountpoint(*ptr, magic) == 0) {
-                               strncpy(mountpoint, *ptr, len - 1);
-                               mountpoint[len-1] = 0;
-                               return mountpoint;
-                       }
-                       ptr++;
-               }
-       }
-
-       /* give up and parse /proc/mounts */
-       fp = fopen("/proc/mounts", "r");
-       if (fp == NULL)
-               return NULL;
-
-       snprintf(format, 128, "%%*s %%%ds %%99s %%*s %%*d %%*d\n", len);
-
-       while (fscanf(fp, format, mountpoint, type) == 2) {
-               if (strcmp(type, fstype) == 0)
-                       break;
-       }
-       fclose(fp);
-
-       if (strcmp(type, fstype) != 0)
-               return NULL;
-
-       return mountpoint;
-}
diff --git a/tools/lib/api/fs/findfs.h b/tools/lib/api/fs/findfs.h
deleted file mode 100644 (file)
index b6f5d05..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef __API_FINDFS_H__
-#define __API_FINDFS_H__
-
-#include <stdbool.h>
-
-#define _STR(x) #x
-#define STR(x) _STR(x)
-
-/*
- * On most systems <limits.h> would have given us this, but  not on some systems
- * (e.g. GNU/Hurd).
- */
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-const char *find_mountpoint(const char *fstype, long magic,
-                           char *mountpoint, int len,
-                           const char * const *known_mountpoints);
-
-int valid_mountpoint(const char *mount, long magic);
-
-#endif /* __API_FINDFS_H__ */
index 128ef6332a6bd89c0ddbeef283c4dfccbf5f8417..459599d1b6c410b7b41333c13f248ee685c2dd4a 100644 (file)
@@ -1,7 +1,6 @@
-/* TODO merge/factor in debugfs.c here */
-
 #include <ctype.h>
 #include <errno.h>
+#include <limits.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <sys/mount.h>
 
-#include "debugfs.h"
 #include "fs.h"
 
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+#ifndef SYSFS_MAGIC
+#define SYSFS_MAGIC            0x62656572
+#endif
+
+#ifndef PROC_SUPER_MAGIC
+#define PROC_SUPER_MAGIC       0x9fa0
+#endif
+
+#ifndef DEBUGFS_MAGIC
+#define DEBUGFS_MAGIC          0x64626720
+#endif
+
+#ifndef TRACEFS_MAGIC
+#define TRACEFS_MAGIC          0x74726163
+#endif
+
 static const char * const sysfs__fs_known_mountpoints[] = {
        "/sys",
        0,
@@ -25,19 +43,48 @@ static const char * const procfs__known_mountpoints[] = {
        0,
 };
 
+#ifndef DEBUGFS_DEFAULT_PATH
+#define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug"
+#endif
+
+static const char * const debugfs__known_mountpoints[] = {
+       DEBUGFS_DEFAULT_PATH,
+       "/debug",
+       0,
+};
+
+
+#ifndef TRACEFS_DEFAULT_PATH
+#define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing"
+#endif
+
+static const char * const tracefs__known_mountpoints[] = {
+       TRACEFS_DEFAULT_PATH,
+       "/sys/kernel/debug/tracing",
+       "/tracing",
+       "/trace",
+       0,
+};
+
 struct fs {
        const char              *name;
        const char * const      *mounts;
-       char                     path[PATH_MAX + 1];
+       char                     path[PATH_MAX];
        bool                     found;
        long                     magic;
 };
 
 enum {
-       FS__SYSFS  = 0,
-       FS__PROCFS = 1,
+       FS__SYSFS   = 0,
+       FS__PROCFS  = 1,
+       FS__DEBUGFS = 2,
+       FS__TRACEFS = 3,
 };
 
+#ifndef TRACEFS_MAGIC
+#define TRACEFS_MAGIC 0x74726163
+#endif
+
 static struct fs fs__entries[] = {
        [FS__SYSFS] = {
                .name   = "sysfs",
@@ -49,6 +96,16 @@ static struct fs fs__entries[] = {
                .mounts = procfs__known_mountpoints,
                .magic  = PROC_SUPER_MAGIC,
        },
+       [FS__DEBUGFS] = {
+               .name   = "debugfs",
+               .mounts = debugfs__known_mountpoints,
+               .magic  = DEBUGFS_MAGIC,
+       },
+       [FS__TRACEFS] = {
+               .name   = "tracefs",
+               .mounts = tracefs__known_mountpoints,
+               .magic  = TRACEFS_MAGIC,
+       },
 };
 
 static bool fs__read_mounts(struct fs *fs)
@@ -159,14 +216,54 @@ static const char *fs__mountpoint(int idx)
        return fs__get_mountpoint(fs);
 }
 
-#define FS__MOUNTPOINT(name, idx)      \
-const char *name##__mountpoint(void)   \
-{                                      \
-       return fs__mountpoint(idx);     \
+static const char *mount_overload(struct fs *fs)
+{
+       size_t name_len = strlen(fs->name);
+       /* "PERF_" + name + "_ENVIRONMENT" + '\0' */
+       char upper_name[5 + name_len + 12 + 1];
+
+       snprintf(upper_name, name_len, "PERF_%s_ENVIRONMENT", fs->name);
+       mem_toupper(upper_name, name_len);
+
+       return getenv(upper_name) ?: *fs->mounts;
+}
+
+static const char *fs__mount(int idx)
+{
+       struct fs *fs = &fs__entries[idx];
+       const char *mountpoint;
+
+       if (fs__mountpoint(idx))
+               return (const char *)fs->path;
+
+       mountpoint = mount_overload(fs);
+
+       if (mount(NULL, mountpoint, fs->name, 0, NULL) < 0)
+               return NULL;
+
+       return fs__check_mounts(fs) ? fs->path : NULL;
+}
+
+#define FS(name, idx)                          \
+const char *name##__mountpoint(void)           \
+{                                              \
+       return fs__mountpoint(idx);             \
+}                                              \
+                                               \
+const char *name##__mount(void)                        \
+{                                              \
+       return fs__mount(idx);                  \
+}                                              \
+                                               \
+bool name##__configured(void)                  \
+{                                              \
+       return name##__mountpoint() != NULL;    \
 }
 
-FS__MOUNTPOINT(sysfs,  FS__SYSFS);
-FS__MOUNTPOINT(procfs, FS__PROCFS);
+FS(sysfs,   FS__SYSFS);
+FS(procfs,  FS__PROCFS);
+FS(debugfs, FS__DEBUGFS);
+FS(tracefs, FS__TRACEFS);
 
 int filename__read_int(const char *filename, int *value)
 {
@@ -185,6 +282,50 @@ int filename__read_int(const char *filename, int *value)
        return err;
 }
 
+int filename__read_ull(const char *filename, unsigned long long *value)
+{
+       char line[64];
+       int fd = open(filename, O_RDONLY), err = -1;
+
+       if (fd < 0)
+               return -1;
+
+       if (read(fd, line, sizeof(line)) > 0) {
+               *value = strtoull(line, NULL, 10);
+               if (*value != ULLONG_MAX)
+                       err = 0;
+       }
+
+       close(fd);
+       return err;
+}
+
+int sysfs__read_ull(const char *entry, unsigned long long *value)
+{
+       char path[PATH_MAX];
+       const char *sysfs = sysfs__mountpoint();
+
+       if (!sysfs)
+               return -1;
+
+       snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
+
+       return filename__read_ull(path, value);
+}
+
+int sysfs__read_int(const char *entry, int *value)
+{
+       char path[PATH_MAX];
+       const char *sysfs = sysfs__mountpoint();
+
+       if (!sysfs)
+               return -1;
+
+       snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
+
+       return filename__read_int(path, value);
+}
+
 int sysctl__read_int(const char *sysctl, int *value)
 {
        char path[PATH_MAX];
index 6caa2bbc6cecdc2f7fa39b69fa1cf30f8653f15e..d024a7f682f69b27a4bba1aed7b8f1894d07b50f 100644 (file)
@@ -1,17 +1,33 @@
 #ifndef __API_FS__
 #define __API_FS__
 
-#ifndef SYSFS_MAGIC
-#define SYSFS_MAGIC            0x62656572
-#endif
+#include <stdbool.h>
 
-#ifndef PROC_SUPER_MAGIC
-#define PROC_SUPER_MAGIC       0x9fa0
+/*
+ * On most systems <limits.h> would have given us this, but  not on some systems
+ * (e.g. GNU/Hurd).
+ */
+#ifndef PATH_MAX
+#define PATH_MAX 4096
 #endif
 
-const char *sysfs__mountpoint(void);
-const char *procfs__mountpoint(void);
+#define FS(name)                               \
+       const char *name##__mountpoint(void);   \
+       const char *name##__mount(void);        \
+       bool name##__configured(void);          \
+
+FS(sysfs)
+FS(procfs)
+FS(debugfs)
+FS(tracefs)
+
+#undef FS
+
 
 int filename__read_int(const char *filename, int *value);
+int filename__read_ull(const char *filename, unsigned long long *value);
+
 int sysctl__read_int(const char *sysctl, int *value);
+int sysfs__read_int(const char *entry, int *value);
+int sysfs__read_ull(const char *entry, unsigned long long *value);
 #endif /* __API_FS__ */
diff --git a/tools/lib/api/fs/tracefs.c b/tools/lib/api/fs/tracefs.c
deleted file mode 100644 (file)
index e4aa968..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <sys/vfs.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mount.h>
-#include <linux/kernel.h>
-
-#include "tracefs.h"
-
-#ifndef TRACEFS_DEFAULT_PATH
-#define TRACEFS_DEFAULT_PATH           "/sys/kernel/tracing"
-#endif
-
-char tracefs_mountpoint[PATH_MAX + 1] = TRACEFS_DEFAULT_PATH;
-
-static const char * const tracefs_known_mountpoints[] = {
-       TRACEFS_DEFAULT_PATH,
-       "/sys/kernel/debug/tracing",
-       "/tracing",
-       "/trace",
-       0,
-};
-
-static bool tracefs_found;
-
-bool tracefs_configured(void)
-{
-       return tracefs_find_mountpoint() != NULL;
-}
-
-/* find the path to the mounted tracefs */
-const char *tracefs_find_mountpoint(void)
-{
-       const char *ret;
-
-       if (tracefs_found)
-               return (const char *)tracefs_mountpoint;
-
-       ret = find_mountpoint("tracefs", (long) TRACEFS_MAGIC,
-                             tracefs_mountpoint, PATH_MAX + 1,
-                             tracefs_known_mountpoints);
-
-       if (ret)
-               tracefs_found = true;
-
-       return ret;
-}
-
-/* mount the tracefs somewhere if it's not mounted */
-char *tracefs_mount(const char *mountpoint)
-{
-       /* see if it's already mounted */
-       if (tracefs_find_mountpoint())
-               goto out;
-
-       /* if not mounted and no argument */
-       if (mountpoint == NULL) {
-               /* see if environment variable set */
-               mountpoint = getenv(PERF_TRACEFS_ENVIRONMENT);
-               /* if no environment variable, use default */
-               if (mountpoint == NULL)
-                       mountpoint = TRACEFS_DEFAULT_PATH;
-       }
-
-       if (mount(NULL, mountpoint, "tracefs", 0, NULL) < 0)
-               return NULL;
-
-       /* save the mountpoint */
-       tracefs_found = true;
-       strncpy(tracefs_mountpoint, mountpoint, sizeof(tracefs_mountpoint));
-out:
-       return tracefs_mountpoint;
-}
diff --git a/tools/lib/api/fs/tracefs.h b/tools/lib/api/fs/tracefs.h
deleted file mode 100644 (file)
index da780ac..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef __API_TRACEFS_H__
-#define __API_TRACEFS_H__
-
-#include "findfs.h"
-
-#ifndef TRACEFS_MAGIC
-#define TRACEFS_MAGIC          0x74726163
-#endif
-
-#ifndef PERF_TRACEFS_ENVIRONMENT
-#define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR"
-#endif
-
-bool tracefs_configured(void);
-const char *tracefs_find_mountpoint(void);
-int tracefs_valid_mountpoint(const char *debugfs);
-char *tracefs_mount(const char *mountpoint);
-
-extern char tracefs_mountpoint[];
-
-#endif /* __API_DEBUGFS_H__ */
diff --git a/tools/lib/api/fs/tracing_path.c b/tools/lib/api/fs/tracing_path.c
new file mode 100644 (file)
index 0000000..a26bb5e
--- /dev/null
@@ -0,0 +1,135 @@
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include "fs.h"
+
+#include "tracing_path.h"
+
+
+char tracing_mnt[PATH_MAX]         = "/sys/kernel/debug";
+char tracing_path[PATH_MAX]        = "/sys/kernel/debug/tracing";
+char tracing_events_path[PATH_MAX] = "/sys/kernel/debug/tracing/events";
+
+
+static void __tracing_path_set(const char *tracing, const char *mountpoint)
+{
+       snprintf(tracing_mnt, sizeof(tracing_mnt), "%s", mountpoint);
+       snprintf(tracing_path, sizeof(tracing_path), "%s/%s",
+                mountpoint, tracing);
+       snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s%s",
+                mountpoint, tracing, "events");
+}
+
+static const char *tracing_path_tracefs_mount(void)
+{
+       const char *mnt;
+
+       mnt = tracefs__mount();
+       if (!mnt)
+               return NULL;
+
+       __tracing_path_set("", mnt);
+
+       return mnt;
+}
+
+static const char *tracing_path_debugfs_mount(void)
+{
+       const char *mnt;
+
+       mnt = debugfs__mount();
+       if (!mnt)
+               return NULL;
+
+       __tracing_path_set("tracing/", mnt);
+
+       return mnt;
+}
+
+const char *tracing_path_mount(void)
+{
+       const char *mnt;
+
+       mnt = tracing_path_tracefs_mount();
+       if (mnt)
+               return mnt;
+
+       mnt = tracing_path_debugfs_mount();
+
+       return mnt;
+}
+
+void tracing_path_set(const char *mntpt)
+{
+       __tracing_path_set("tracing/", mntpt);
+}
+
+char *get_tracing_file(const char *name)
+{
+       char *file;
+
+       if (asprintf(&file, "%s/%s", tracing_path, name) < 0)
+               return NULL;
+
+       return file;
+}
+
+void put_tracing_file(char *file)
+{
+       free(file);
+}
+
+static int strerror_open(int err, char *buf, size_t size, const char *filename)
+{
+       char sbuf[128];
+
+       switch (err) {
+       case ENOENT:
+               /*
+                * We will get here if we can't find the tracepoint, but one of
+                * debugfs or tracefs is configured, which means you probably
+                * want some tracepoint which wasn't compiled in your kernel.
+                * - jirka
+                */
+               if (debugfs__configured() || tracefs__configured()) {
+                       snprintf(buf, size,
+                                "Error:\tFile %s/%s not found.\n"
+                                "Hint:\tPerhaps this kernel misses some CONFIG_ setting to enable this feature?.\n",
+                                tracing_events_path, filename);
+                       break;
+               }
+               snprintf(buf, size, "%s",
+                        "Error:\tUnable to find debugfs/tracefs\n"
+                        "Hint:\tWas your kernel compiled with debugfs/tracefs support?\n"
+                        "Hint:\tIs the debugfs/tracefs filesystem mounted?\n"
+                        "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'");
+               break;
+       case EACCES: {
+               snprintf(buf, size,
+                        "Error:\tNo permissions to read %s/%s\n"
+                        "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
+                        tracing_events_path, filename, tracing_mnt);
+       }
+               break;
+       default:
+               snprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf)));
+               break;
+       }
+
+       return 0;
+}
+
+int tracing_path__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name)
+{
+       char path[PATH_MAX];
+
+       snprintf(path, PATH_MAX, "%s/%s", sys, name ?: "*");
+
+       return strerror_open(err, buf, size, path);
+}
diff --git a/tools/lib/api/fs/tracing_path.h b/tools/lib/api/fs/tracing_path.h
new file mode 100644 (file)
index 0000000..3f233ac
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __API_FS_TRACING_PATH_H
+#define __API_FS_TRACING_PATH_H
+
+#include <linux/types.h>
+
+extern char tracing_path[];
+extern char tracing_events_path[];
+
+void tracing_path_set(const char *mountpoint);
+const char *tracing_path_mount(void);
+
+char *get_tracing_file(const char *name);
+void put_tracing_file(char *file);
+
+int tracing_path__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name);
+#endif /* __API_FS_TRACING_PATH_H */
index f68d23a0b4877342175d82866acdcaf4963f36e4..fc9af57b666e244b6b1c06cd163738068190b6f8 100644 (file)
@@ -64,8 +64,9 @@ srctree := $(patsubst %/,%,$(dir $(srctree)))
 #$(info Determined 'srctree' to be $(srctree))
 endif
 
-FEATURE_DISPLAY = libelf libelf-getphdrnum libelf-mmap bpf
-FEATURE_TESTS = libelf bpf
+FEATURE_USER = .libbpf
+FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf
+FEATURE_DISPLAY = libelf bpf
 
 INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
 FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES)
@@ -122,8 +123,10 @@ endif
 # the same command line setup.
 MAKEOVERRIDES=
 
+all:
+
 export srctree OUTPUT CC LD CFLAGS V
-build := -f $(srctree)/tools/build/Makefile.build dir=. obj
+include $(srctree)/tools/build/Makefile.include
 
 BPF_IN    := $(OUTPUT)libbpf-in.o
 LIB_FILE := $(addprefix $(OUTPUT),$(LIB_FILE))
@@ -132,7 +135,7 @@ CMD_TARGETS = $(LIB_FILE)
 
 TARGETS = $(CMD_TARGETS)
 
-all: $(VERSION_FILES) all_cmd
+all: fixdep $(VERSION_FILES) all_cmd
 
 all_cmd: $(CMD_TARGETS)
 
index 18ffccf004264d202632990d40227c27c41d27ad..7e319afac78a0763967b0cb8c2d38842e69c9302 100644 (file)
@@ -93,8 +93,10 @@ else
   print_install =              echo '  INSTALL  '$1'   to      $(DESTDIR_SQ)$2';
 endif
 
+all:
+
 export srctree OUTPUT CC LD CFLAGS V
-build := -f $(srctree)/tools/build/Makefile.build dir=. obj
+include $(srctree)/tools/build/Makefile.include
 
 do_compile_shared_library =                    \
        ($(print_shared_lib_compile)            \
@@ -109,7 +111,7 @@ CMD_TARGETS = $(LIB_FILE)
 TARGETS = $(CMD_TARGETS)
 
 
-all: all_cmd
+all: fixdep all_cmd
 
 all_cmd: $(CMD_TARGETS)
 
index 18bc271a4bbc7fe4232e6cc6f5fd2b2bf392846a..5e431077fcd6784ac9567a785bc6fb90a40bc7bb 100644 (file)
@@ -2,6 +2,12 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+u8 kallsyms2elf_type(char type)
+{
+       type = tolower(type);
+       return (type == 't' || type == 'w') ? STT_FUNC : STT_OBJECT;
+}
+
 int kallsyms__parse(const char *filename, void *arg,
                    int (*process_symbol)(void *arg, const char *name,
                                          char type, u64 start))
index 6084f5e18b3c34d354c38a56dea258fd9769e596..4071316a766ef95023853f61ed1a27c9477aa898 100644 (file)
@@ -9,7 +9,7 @@
 #define KSYM_NAME_LEN 256
 #endif
 
-static inline u8 kallsyms2elf_type(char type)
+static inline u8 kallsyms2elf_binding(char type)
 {
        if (type == 'W')
                return STB_WEAK;
@@ -17,6 +17,8 @@ static inline u8 kallsyms2elf_type(char type)
        return isupper(type) ? STB_GLOBAL : STB_LOCAL;
 }
 
+u8 kallsyms2elf_type(char type);
+
 int kallsyms__parse(const char *filename, void *arg,
                    int (*process_symbol)(void *arg, const char *name,
                                          char type, u64 start));
index cf42b090477b9795ac1f48dff6c1c7b93bed7f65..2a912df6771bf5c6abd880667fbece1dcf1e1005 100644 (file)
@@ -848,6 +848,7 @@ static void free_arg(struct print_arg *arg)
                free(arg->bitmask.bitmask);
                break;
        case PRINT_DYNAMIC_ARRAY:
+       case PRINT_DYNAMIC_ARRAY_LEN:
                free(arg->dynarray.index);
                break;
        case PRINT_OP:
@@ -2728,6 +2729,42 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char **
        return EVENT_ERROR;
 }
 
+static enum event_type
+process_dynamic_array_len(struct event_format *event, struct print_arg *arg,
+                         char **tok)
+{
+       struct format_field *field;
+       enum event_type type;
+       char *token;
+
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto out_free;
+
+       arg->type = PRINT_DYNAMIC_ARRAY_LEN;
+
+       /* Find the field */
+       field = pevent_find_field(event, token);
+       if (!field)
+               goto out_free;
+
+       arg->dynarray.field = field;
+       arg->dynarray.index = 0;
+
+       if (read_expected(EVENT_DELIM, ")") < 0)
+               goto out_err;
+
+       type = read_token(&token);
+       *tok = token;
+
+       return type;
+
+ out_free:
+       free_token(token);
+ out_err:
+       *tok = NULL;
+       return EVENT_ERROR;
+}
+
 static enum event_type
 process_paren(struct event_format *event, struct print_arg *arg, char **tok)
 {
@@ -2975,6 +3012,10 @@ process_function(struct event_format *event, struct print_arg *arg,
                free_token(token);
                return process_dynamic_array(event, arg, tok);
        }
+       if (strcmp(token, "__get_dynamic_array_len") == 0) {
+               free_token(token);
+               return process_dynamic_array_len(event, arg, tok);
+       }
 
        func = find_func_handler(event->pevent, token);
        if (func) {
@@ -3655,14 +3696,25 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
                        goto out_warning_op;
                }
                break;
+       case PRINT_DYNAMIC_ARRAY_LEN:
+               offset = pevent_read_number(pevent,
+                                           data + arg->dynarray.field->offset,
+                                           arg->dynarray.field->size);
+               /*
+                * The total allocated length of the dynamic array is
+                * stored in the top half of the field, and the offset
+                * is in the bottom half of the 32 bit field.
+                */
+               val = (unsigned long long)(offset >> 16);
+               break;
        case PRINT_DYNAMIC_ARRAY:
                /* Without [], we pass the address to the dynamic data */
                offset = pevent_read_number(pevent,
                                            data + arg->dynarray.field->offset,
                                            arg->dynarray.field->size);
                /*
-                * The actual length of the dynamic array is stored
-                * in the top half of the field, and the offset
+                * The total allocated length of the dynamic array is
+                * stored in the top half of the field, and the offset
                 * is in the bottom half of the 32 bit field.
                 */
                offset &= 0xffff;
@@ -4853,8 +4905,8 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
                                else
                                        ls = 2;
 
-                               if (*(ptr+1) == 'F' ||
-                                   *(ptr+1) == 'f') {
+                               if (*(ptr+1) == 'F' || *(ptr+1) == 'f' ||
+                                   *(ptr+1) == 'S' || *(ptr+1) == 's') {
                                        ptr++;
                                        show_func = *ptr;
                                } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') {
index 204befb05a173754869f4dc00f58c48a87331091..6fc83c7edbe918b01be04667699ae56a87d8676e 100644 (file)
@@ -294,6 +294,7 @@ enum print_arg_type {
        PRINT_OP,
        PRINT_FUNC,
        PRINT_BITMASK,
+       PRINT_DYNAMIC_ARRAY_LEN,
 };
 
 struct print_arg {
index 88fe83dff7cdf4fb957a23670324138225affa86..18536f7565773b84dd29cb7a65b5df851dd4b005 100644 (file)
@@ -124,7 +124,10 @@ static const char *disassemble(unsigned char *insn, int len, uint64_t rip,
        _ER(WBINVD,              54)            \
        _ER(XSETBV,              55)            \
        _ER(APIC_WRITE,          56)            \
-       _ER(INVPCID,             58)
+       _ER(INVPCID,             58)            \
+       _ER(PML_FULL,            62)            \
+       _ER(XSAVES,              63)            \
+       _ER(XRSTORS,             64)
 
 #define SVM_EXIT_REASONS \
        _ER(EXIT_READ_CR0,      0x000)          \
@@ -352,15 +355,18 @@ static int kvm_nested_vmexit_handler(struct trace_seq *s, struct pevent_record *
 union kvm_mmu_page_role {
        unsigned word;
        struct {
-               unsigned glevels:4;
                unsigned level:4;
+               unsigned cr4_pae:1;
                unsigned quadrant:2;
-               unsigned pad_for_nice_hex_output:6;
                unsigned direct:1;
                unsigned access:3;
                unsigned invalid:1;
-               unsigned cr4_pge:1;
                unsigned nxe:1;
+               unsigned cr0_wp:1;
+               unsigned smep_and_not_wp:1;
+               unsigned smap_and_not_wp:1;
+               unsigned pad_for_nice_hex_output:8;
+               unsigned smm:8;
        };
 };
 
@@ -385,15 +391,18 @@ static int kvm_mmu_print_role(struct trace_seq *s, struct pevent_record *record,
        if (pevent_is_file_bigendian(event->pevent) ==
            pevent_is_host_bigendian(event->pevent)) {
 
-               trace_seq_printf(s, "%u/%u q%u%s %s%s %spge %snxe",
+               trace_seq_printf(s, "%u q%u%s %s%s %spae %snxe %swp%s%s%s",
                                 role.level,
-                                role.glevels,
                                 role.quadrant,
                                 role.direct ? " direct" : "",
                                 access_str[role.access],
                                 role.invalid ? " invalid" : "",
-                                role.cr4_pge ? "" : "!",
-                                role.nxe ? "" : "!");
+                                role.cr4_pae ? "" : "!",
+                                role.nxe ? "" : "!",
+                                role.cr0_wp ? "" : "!",
+                                role.smep_and_not_wp ? " smep" : "",
+                                role.smap_and_not_wp ? " smap" : "",
+                                role.smm ? " smm" : "");
        } else
                trace_seq_printf(s, "WORD: %08x", role.word);
 
index 2cd3d4c997383af3fde9ed090eb5d9ab32a7e467..5b32413409459ce165e2f56990dbc0f52aa8bd31 100644 (file)
@@ -156,8 +156,8 @@ static void put_log_buff(char *buff)
        free(buff);
 }
 
-static int get_last_jit_image(char *haystack, size_t hlen,
-                             uint8_t *image, size_t ilen)
+static unsigned int get_last_jit_image(char *haystack, size_t hlen,
+                                      uint8_t *image, size_t ilen)
 {
        char *ptr, *pptr, *tmp;
        off_t off = 0;
index c94c9de3173ee187f87be72c0ffa128885115c89..be764f9ec7691a3d2357214cbe1af9c6c333ad92 100644 (file)
@@ -671,6 +671,7 @@ The letters are:
        e       synthesize tracing error events
        d       create a debug log
        g       synthesize a call chain (use with i or x)
+       l       synthesize last branch entries (use with i or x)
 
 "Instructions" events look like they were recorded by "perf record -e
 instructions".
@@ -707,12 +708,26 @@ on the sample is *not* adjusted and reflects the last known value of TSC.
 
 For Intel PT, the default period is 100us.
 
+Setting it to a zero period means "as often as possible".
+
+In the case of Intel PT that is the same as a period of 1 and a unit of
+'instructions' (i.e. --itrace=i1i).
+
 Also the call chain size (default 16, max. 1024) for instructions or
 transactions events can be specified. e.g.
 
        --itrace=ig32
        --itrace=xg32
 
+Also the number of last branch entries (default 64, max. 1024) for instructions or
+transactions events can be specified. e.g.
+
+       --itrace=il10
+       --itrace=xl10
+
+Note that last branch entries are cleared for each sample, so there is no overlap
+from one sample to the next.
+
 To disable trace decoding entirely, use the option --no-itrace.
 
 
@@ -749,3 +764,32 @@ perf inject also accepts the --itrace option in which case tracing data is
 removed and replaced with the synthesized events. e.g.
 
        perf inject --itrace -i perf.data -o perf.data.new
+
+Below is an example of using Intel PT with autofdo.  It requires autofdo
+(https://github.com/google/autofdo) and gcc version 5.  The bubble
+sort example is from the AutoFDO tutorial (https://gcc.gnu.org/wiki/AutoFDO/Tutorial)
+amended to take the number of elements as a parameter.
+
+       $ gcc-5 -O3 sort.c -o sort_optimized
+       $ ./sort_optimized 30000
+       Bubble sorting array of 30000 elements
+       2254 ms
+
+       $ cat ~/.perfconfig
+       [intel-pt]
+               mispred-all
+
+       $ perf record -e intel_pt//u ./sort 3000
+       Bubble sorting array of 3000 elements
+       58 ms
+       [ perf record: Woken up 2 times to write data ]
+       [ perf record: Captured and wrote 3.939 MB perf.data ]
+       $ perf inject -i perf.data -o inj --itrace=i100usle --strip
+       $ ./create_gcov --binary=./sort --profile=inj --gcov=sort.gcov -gcov_version=1
+       $ gcc-5 -O3 -fauto-profile=sort.gcov sort.c -o sort_autofdo
+       $ ./sort_autofdo 30000
+       Bubble sorting array of 30000 elements
+       2155 ms
+
+Note there is currently no advantage to using Intel PT instead of LBR, but
+that may change in the future if greater use is made of the data.
index 2ff946677e3b81b15be4d0d3a4782327d80daccc..65453f4c700604f8a259df384c6e52988ce81b14 100644 (file)
@@ -6,6 +6,7 @@
                e       synthesize error events
                d       create a debug log
                g       synthesize a call chain (use with i or x)
+               l       synthesize last branch entries (use with i or x)
 
        The default is all events i.e. the same as --itrace=ibxe
 
@@ -20,3 +21,6 @@
 
        Also the call chain size (default 16, max. 1024) for instructions or
        transactions events can be specified.
+
+       Also the number of last branch entries (default 64, max. 1024) for
+       instructions or transactions events can be specified.
index ab632d9fbd7d09c8947f824dddddcfd1d1cd0837..34750fc32714caeb18ba0fdcb55cb4f4928c1648 100644 (file)
@@ -82,7 +82,7 @@ Be multi thread instead of multi process
 Specify number of groups
 
 -l::
---loop=::
+--nr_loops=::
 Specify number of loops
 
 Example of *messaging*
@@ -139,64 +139,48 @@ Suite for evaluating performance of simple memory copy in various ways.
 Options of *memcpy*
 ^^^^^^^^^^^^^^^^^^^
 -l::
---length::
-Specify length of memory to copy (default: 1MB).
+--size::
+Specify size of memory to copy (default: 1MB).
 Available units are B, KB, MB, GB and TB (case insensitive).
 
--r::
---routine::
-Specify routine to copy (default: default).
-Available routines are depend on the architecture.
+-f::
+--function::
+Specify function to copy (default: default).
+Available functions are depend on the architecture.
 On x86-64, x86-64-unrolled, x86-64-movsq and x86-64-movsb are supported.
 
--i::
---iterations::
+-l::
+--nr_loops::
 Repeat memcpy invocation this number of times.
 
 -c::
---cycle::
+--cycles::
 Use perf's cpu-cycles event instead of gettimeofday syscall.
 
--o::
---only-prefault::
-Show only the result with page faults before memcpy.
-
--n::
---no-prefault::
-Show only the result without page faults before memcpy.
-
 *memset*::
 Suite for evaluating performance of simple memory set in various ways.
 
 Options of *memset*
 ^^^^^^^^^^^^^^^^^^^
 -l::
---length::
-Specify length of memory to set (default: 1MB).
+--size::
+Specify size of memory to set (default: 1MB).
 Available units are B, KB, MB, GB and TB (case insensitive).
 
--r::
---routine::
-Specify routine to set (default: default).
-Available routines are depend on the architecture.
+-f::
+--function::
+Specify function to set (default: default).
+Available functions are depend on the architecture.
 On x86-64, x86-64-unrolled, x86-64-stosq and x86-64-stosb are supported.
 
--i::
---iterations::
+-l::
+--nr_loops::
 Repeat memset invocation this number of times.
 
 -c::
---cycle::
+--cycles::
 Use perf's cpu-cycles event instead of gettimeofday syscall.
 
--o::
---only-prefault::
-Show only the result with page faults before memset.
-
--n::
---no-prefault::
-Show only the result without page faults before memset.
-
 SUITES FOR 'numa'
 ~~~~~~~~~~~~~~~~~
 *mem*::
index 0c721c3e37e1131e3546b2c0440bbcdef289a54e..0b1cedeef895369c9c60aa0a1e6cbb718c65c509 100644 (file)
@@ -50,6 +50,9 @@ OPTIONS
 
 include::itrace.txt[]
 
+--strip::
+       Use with --itrace to strip out non-synthesized events.
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1]
index bada8933fdd4798a2890a13b2fb64d5bf75d9743..79483f40e991eaa80ffd7889f628d5e4d8ea3ce0 100644 (file)
@@ -30,6 +30,7 @@ counted. The following modifiers exist:
  G - guest counting (in KVM guests)
  H - host counting (not in KVM guests)
  p - precise level
+ P - use maximum detected precise level
  S - read sample value (PERF_SAMPLE_READ)
  D - pin the event to the PMU
 
@@ -125,6 +126,8 @@ To limit the list use:
 . If none of the above is matched, it will apply the supplied glob to all
   events, printing the ones that match.
 
+. As a last resort, it will do a substring search in all event names.
+
 One or more types can be used at the same time, listing the events for the
 types specified.
 
index 2e9ce77b5e144a720a01a01661d3482661fb4f88..e630a7d2c3483cc6b11f33190d289b19c2d43b85 100644 (file)
@@ -144,7 +144,7 @@ OPTIONS
 
 --call-graph::
        Setup and enable call-graph (stack chain/backtrace) recording,
-       implies -g.
+       implies -g.  Default is "fp".
 
        Allows specifying "fp" (frame pointer) or "dwarf"
        (DWARF's CFI - Call Frame Information) or "lbr"
@@ -154,13 +154,18 @@ OPTIONS
        In some systems, where binaries are build with gcc
        --fomit-frame-pointer, using the "fp" method will produce bogus
        call graphs, using "dwarf", if available (perf tools linked to
-       the libunwind library) should be used instead.
+       the libunwind or libdw library) should be used instead.
        Using the "lbr" method doesn't require any compiler options. It
        will produce call graphs from the hardware LBR registers. The
        main limition is that it is only available on new Intel
        platforms, such as Haswell. It can only get user call chain. It
        doesn't work with branch stack sampling at the same time.
 
+       When "dwarf" recording is used, perf also records (user) stack dump
+       when sampled.  Default size of the stack dump is 8192 (bytes).
+       User can change the size by passing the size after comma like
+       "--call-graph dwarf,4096".
+
 -q::
 --quiet::
        Don't print any message, useful for scripting.
@@ -236,6 +241,7 @@ following filters are defined:
         - any_call: any function call or system call
         - any_ret: any function return or system call return
         - ind_call: any indirect branch
+        - call: direct calls, including far (to/from kernel) calls
         - u:  only when the branch target is at the user level
         - k: only when the branch target is in the kernel
         - hv: only when the target is at the hypervisor level
@@ -308,6 +314,12 @@ This option sets the time out limit. The default value is 500 ms.
 Record context switch events i.e. events of type PERF_RECORD_SWITCH or
 PERF_RECORD_SWITCH_CPU_WIDE.
 
+--clang-path::
+Path to clang binary to use for compiling BPF scriptlets.
+
+--clang-opt::
+Options passed to clang when compiling BPF scriptlets.
+
 SEE ALSO
 --------
 linkperf:perf-stat[1], linkperf:perf-list[1]
index 9c7981bfddad4ca92f3580169156f15b8645f8ef..5ce8da1e1256f2295c0b3273c48516463db8ce23 100644 (file)
@@ -29,7 +29,7 @@ OPTIONS
 --show-nr-samples::
        Show the number of samples for each symbol
 
---showcpuutilization::
+--show-cpu-utilization::
         Show sample percentage for different cpu modes.
 
 -T::
@@ -68,7 +68,7 @@ OPTIONS
 --sort=::
        Sort histogram entries by given key(s) - multiple keys can be specified
        in CSV format.  Following sort keys are available:
-       pid, comm, dso, symbol, parent, cpu, srcline, weight, local_weight.
+       pid, comm, dso, symbol, parent, cpu, socket, srcline, weight, local_weight.
 
        Each key has following meaning:
 
@@ -79,6 +79,7 @@ OPTIONS
        - parent: name of function matched to the parent regex filter. Unmatched
        entries are displayed as "[other]".
        - cpu: cpu number the task ran at the time of sample
+       - socket: processor socket number the task ran at the time of sample
        - srcline: filename and line number executed at the time of sample.  The
        DWARF debugging info must be provided.
        - srcfile: file name of the source file of the same. Requires dwarf
@@ -168,30 +169,40 @@ OPTIONS
 --dump-raw-trace::
         Dump raw trace in ASCII.
 
--g [type,min[,limit],order[,key][,branch]]::
---call-graph::
-        Display call chains using type, min percent threshold, optional print
-       limit and order.
-       type can be either:
+-g::
+--call-graph=<print_type,threshold[,print_limit],order,sort_key,branch>::
+        Display call chains using type, min percent threshold, print limit,
+       call order, sort key and branch.  Note that ordering of parameters is not
+       fixed so any parement can be given in an arbitraty order.  One exception
+       is the print_limit which should be preceded by threshold.
+
+       print_type can be either:
        - flat: single column, linear exposure of call chains.
-       - graph: use a graph tree, displaying absolute overhead rates.
+       - graph: use a graph tree, displaying absolute overhead rates. (default)
        - fractal: like graph, but displays relative rates. Each branch of
-                the tree is considered as a new profiled object. +
+                the tree is considered as a new profiled object.
+       - none: disable call chain display.
+
+       threshold is a percentage value which specifies a minimum percent to be
+       included in the output call graph.  Default is 0.5 (%).
+
+       print_limit is only applied when stdio interface is used.  It's to limit
+       number of call graph entries in a single hist entry.  Note that it needs
+       to be given after threshold (but not necessarily consecutive).
+       Default is 0 (unlimited).
 
        order can be either:
        - callee: callee based call graph.
        - caller: inverted caller based call graph.
+       Default is 'caller' when --children is used, otherwise 'callee'.
 
-       key can be:
-       - function: compare on functions
+       sort_key can be:
+       - function: compare on functions (default)
        - address: compare on individual code addresses
 
        branch can be:
-       - branch: include last branch information in callgraph
-       when available. Usually more convenient to use --branch-history
-       for this.
-
-       Default: fractal,0.5,callee,function.
+       - branch: include last branch information in callgraph when available.
+                 Usually more convenient to use --branch-history for this.
 
 --children::
        Accumulate callchain of children to parent entry so that then can
@@ -204,6 +215,8 @@ OPTIONS
        beyond the specified depth will be ignored. This is a trade-off
        between information loss and faster processing especially for
        workloads that can have a very long callchain stack.
+       Note that when using the --itrace option the synthesized callchain size
+       will override this value if the synthesized callchain size is bigger.
 
        Default: 127
 
@@ -349,6 +362,9 @@ include::itrace.txt[]
        This option extends the perf report to show reference callgraphs,
        which collected by reference event, in no callgraph event.
 
+--socket-filter::
+       Only report the samples on the processor socket that match with this filter
+
 include::callchain-overhead-calculation.txt[]
 
 SEE ALSO
index dc3ec783b7bd50973da5eecb0a5ba728a1a72361..382ddfb45d1dbbb4a65bfe464c29096923b64bd3 100644 (file)
@@ -112,11 +112,11 @@ OPTIONS
 --debug-mode::
         Do various checks like samples ordering and lost events.
 
--f::
+-F::
 --fields::
         Comma separated list of fields to print. Options are:
         comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff,
-       srcline, period, iregs, flags.
+       srcline, period, iregs, brstack, brstacksym, flags.
         Field list can be prepended with the type, trace, sw or hw,
         to indicate to which event type the field list applies.
         e.g., -f sw:comm,tid,time,ip,sym  and -f trace:time,cpu,trace
@@ -175,6 +175,16 @@ OPTIONS
        Finally, a user may not set fields to none for all event types.
        i.e., -f "" is not allowed.
 
+       The brstack output includes branch related information with raw addresses using the
+       /v/v/v/v/ syntax in the following order:
+       FROM: branch source instruction
+       TO  : branch target instruction
+        M/P/-: M=branch target mispredicted or branch direction was mispredicted, P=target predicted or direction predicted, -=not supported
+       X/- : X=branch inside a transactional region, -=not in transaction region or not supported
+       A/- : A=TSX abort entry, -=not aborted region or not supported
+
+       The brstacksym is identical to brstack, except that the FROM and TO addresses are printed in a symbolic form if possible.
+
 -k::
 --vmlinux=<file>::
         vmlinux pathname
@@ -249,6 +259,9 @@ include::itrace.txt[]
 --full-source-path::
        Show the full path for source files for srcline output.
 
+--ns::
+       Use 9 decimal places when displaying time (i.e. show the nanoseconds)
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-script-perl[1],
index 47469abdcc1c11e2256effc86845232f78785075..4e074a6608269793d52a2ea4f28061532bb9798b 100644 (file)
@@ -128,8 +128,9 @@ perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- m
 
 -I msecs::
 --interval-print msecs::
-       Print count deltas every N milliseconds (minimum: 100ms)
-       example: perf stat -I 1000 -e cycles -a sleep 5
+Print count deltas every N milliseconds (minimum: 10ms)
+The overhead percentage could be high in some cases, for instance with small, sub 100ms intervals.  Use with caution.
+       example: 'perf stat -I 1000 -e cycles -a sleep 5'
 
 --per-socket::
 Aggregate counts per processor socket for system-wide mode measurements.  This
index f6a23eb294e77ad2f364f7275d4916336ca60809..556cec09bf50cc4d5d9a50104d8efabd7879e2c7 100644 (file)
@@ -160,9 +160,10 @@ Default is to monitor all CPUS.
 -g::
        Enables call-graph (stack chain/backtrace) recording.
 
---call-graph::
+--call-graph [mode,type,min[,limit],order[,key][,branch]]::
        Setup and enable call-graph (stack chain/backtrace) recording,
-       implies -g.
+       implies -g.  See `--call-graph` section in perf-record and
+       perf-report man pages for details.
 
 --children::
        Accumulate callchain of children to parent entry so that then can
index 2b131776363e3a2498ce1b721440c985d22df7fd..864e37597252b3b0449cc1ae0eb5147f79b6e81b 100644 (file)
@@ -27,6 +27,14 @@ OPTIONS
        Setup buildid cache directory. It has higher priority than
        buildid.dir config file option.
 
+-v::
+--version::
+  Display perf version.
+
+-h::
+--help::
+  Run perf help command.
+
 DESCRIPTION
 -----------
 Performance counters for Linux are a new kernel-based subsystem
index af009bd6e6b7cfcc6b451838447f16922ef8bc3c..39c38cb45b00f8e3e478bfebab6a47a9bf4c3dd7 100644 (file)
@@ -17,6 +17,7 @@ tools/build
 tools/arch/x86/include/asm/atomic.h
 tools/arch/x86/include/asm/rmwcc.h
 tools/lib/traceevent
+tools/lib/bpf
 tools/lib/api
 tools/lib/bpf
 tools/lib/hweight.c
@@ -41,6 +42,7 @@ tools/include/asm-generic/bitops.h
 tools/include/linux/atomic.h
 tools/include/linux/bitops.h
 tools/include/linux/compiler.h
+tools/include/linux/filter.h
 tools/include/linux/hash.h
 tools/include/linux/kernel.h
 tools/include/linux/list.h
@@ -49,6 +51,7 @@ tools/include/linux/poison.h
 tools/include/linux/rbtree.h
 tools/include/linux/rbtree_augmented.h
 tools/include/linux/types.h
+tools/include/linux/err.h
 include/asm-generic/bitops/arch_hweight.h
 include/asm-generic/bitops/const_hweight.h
 include/asm-generic/bitops/fls64.h
@@ -67,6 +70,8 @@ arch/*/lib/memset*.S
 include/linux/poison.h
 include/linux/hw_breakpoint.h
 include/uapi/linux/perf_event.h
+include/uapi/linux/bpf.h
+include/uapi/linux/bpf_common.h
 include/uapi/linux/const.h
 include/uapi/linux/swab.h
 include/uapi/linux/hw_breakpoint.h
index d9863cb96f59b8c7bed554dd57e4bc275379eac7..0d19d5447d6c721bba185422ee32b45ce6fff3e9 100644 (file)
@@ -75,6 +75,8 @@ include config/utilities.mak
 # Define NO_LZMA if you do not want to support compressed (xz) kernel modules
 #
 # Define NO_AUXTRACE if you do not want AUX area tracing support
+#
+# Define NO_LIBBPF if you do not want BPF support
 
 # As per kernel Makefile, avoid funny character set dependencies
 unexport LC_ALL
@@ -145,6 +147,7 @@ AWK     = awk
 
 LIB_DIR          = $(srctree)/tools/lib/api/
 TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/
+BPF_DIR = $(srctree)/tools/lib/bpf/
 
 # include config/Makefile by default and rule out
 # non-config cases
@@ -180,6 +183,7 @@ strip-libs = $(filter-out -l%,$(1))
 
 ifneq ($(OUTPUT),)
   TE_PATH=$(OUTPUT)
+  BPF_PATH=$(OUTPUT)
 ifneq ($(subdir),)
   LIB_PATH=$(OUTPUT)/../lib/api/
 else
@@ -188,6 +192,7 @@ endif
 else
   TE_PATH=$(TRACE_EVENT_DIR)
   LIB_PATH=$(LIB_DIR)
+  BPF_PATH=$(BPF_DIR)
 endif
 
 LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
@@ -199,6 +204,8 @@ LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS = -Xlinker --dynamic-list=$(LIBTRACEEVENT_DYN
 LIBAPI = $(LIB_PATH)libapi.a
 export LIBAPI
 
+LIBBPF = $(BPF_PATH)libbpf.a
+
 # python extension build directories
 PYTHON_EXTBUILD     := $(OUTPUT)python_ext_build/
 PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
@@ -251,6 +258,9 @@ export PERL_PATH
 LIB_FILE=$(OUTPUT)libperf.a
 
 PERFLIBS = $(LIB_FILE) $(LIBAPI) $(LIBTRACEEVENT)
+ifndef NO_LIBBPF
+  PERFLIBS += $(LIBBPF)
+endif
 
 # We choose to avoid "if .. else if .. else .. endif endif"
 # because maintaining the nesting to match is a pain.  If
@@ -297,16 +307,16 @@ strip: $(PROGRAMS) $(OUTPUT)perf
 PERF_IN := $(OUTPUT)perf-in.o
 
 export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK
-build := -f $(srctree)/tools/build/Makefile.build dir=. obj
+include $(srctree)/tools/build/Makefile.include
 
-$(PERF_IN): $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h FORCE
+$(PERF_IN): prepare FORCE
        $(Q)$(MAKE) $(build)=perf
 
 $(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(LIBTRACEEVENT_DYNAMIC_LIST)
        $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \
                $(PERF_IN) $(LIBS) -o $@
 
-$(GTK_IN): FORCE
+$(GTK_IN): fixdep FORCE
        $(Q)$(MAKE) $(build)=gtk
 
 $(OUTPUT)libperf-gtk.so: $(GTK_IN) $(PERFLIBS)
@@ -349,27 +359,27 @@ endif
 __build-dir = $(subst $(OUTPUT),,$(dir $@))
 build-dir   = $(if $(__build-dir),$(__build-dir),.)
 
-single_dep: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h
+prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h fixdep
 
-$(OUTPUT)%.o: %.c single_dep FORCE
+$(OUTPUT)%.o: %.c prepare FORCE
        $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=$(build-dir) $@
 
-$(OUTPUT)%.i: %.c single_dep FORCE
+$(OUTPUT)%.i: %.c prepare FORCE
        $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=$(build-dir) $@
 
-$(OUTPUT)%.s: %.c single_dep FORCE
+$(OUTPUT)%.s: %.c prepare FORCE
        $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=$(build-dir) $@
 
-$(OUTPUT)%-bison.o: %.c single_dep FORCE
+$(OUTPUT)%-bison.o: %.c prepare FORCE
        $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=$(build-dir) $@
 
-$(OUTPUT)%-flex.o: %.c single_dep FORCE
+$(OUTPUT)%-flex.o: %.c prepare FORCE
        $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=$(build-dir) $@
 
-$(OUTPUT)%.o: %.S single_dep FORCE
+$(OUTPUT)%.o: %.S prepare FORCE
        $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=$(build-dir) $@
 
-$(OUTPUT)%.i: %.S single_dep FORCE
+$(OUTPUT)%.i: %.S prepare FORCE
        $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=$(build-dir) $@
 
 $(OUTPUT)perf-%: %.o $(PERFLIBS)
@@ -389,7 +399,7 @@ $(patsubst perf-%,%.o,$(PROGRAMS)): $(wildcard */*.h)
 
 LIBPERF_IN := $(OUTPUT)libperf-in.o
 
-$(LIBPERF_IN): FORCE
+$(LIBPERF_IN): fixdep FORCE
        $(Q)$(MAKE) $(build)=libperf
 
 $(LIB_FILE): $(LIBPERF_IN)
@@ -397,10 +407,10 @@ $(LIB_FILE): $(LIBPERF_IN)
 
 LIBTRACEEVENT_FLAGS += plugin_dir=$(plugindir_SQ)
 
-$(LIBTRACEEVENT): FORCE
+$(LIBTRACEEVENT): fixdep FORCE
        $(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) $(OUTPUT)libtraceevent.a
 
-libtraceevent_plugins: FORCE
+libtraceevent_plugins: fixdep FORCE
        $(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) plugins
 
 $(LIBTRACEEVENT_DYNAMIC_LIST): libtraceevent_plugins
@@ -413,13 +423,20 @@ $(LIBTRACEEVENT)-clean:
 install-traceevent-plugins: $(LIBTRACEEVENT)
        $(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) install_plugins
 
-$(LIBAPI): FORCE
+$(LIBAPI): fixdep FORCE
        $(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) $(OUTPUT)libapi.a
 
 $(LIBAPI)-clean:
        $(call QUIET_CLEAN, libapi)
        $(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null
 
+$(LIBBPF): fixdep FORCE
+       $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) $(OUTPUT)libbpf.a
+
+$(LIBBPF)-clean:
+       $(call QUIET_CLEAN, libbpf)
+       $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) clean >/dev/null
+
 help:
        @echo 'Perf make targets:'
        @echo '  doc            - make *all* documentation (see below)'
@@ -459,7 +476,7 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
 $(DOC_TARGETS):
        $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all)
 
-TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol
+TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol ../include ../lib/bpf
 TAG_FILES= ../../include/uapi/linux/perf_event.h
 
 TAGS:
@@ -567,7 +584,7 @@ config-clean:
        $(call QUIET_CLEAN, config)
        $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
 
-clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean config-clean
+clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean config-clean
        $(call QUIET_CLEAN, core-objs)  $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
        $(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
        $(Q)$(RM) $(OUTPUT).config-detected
@@ -591,6 +608,6 @@ FORCE:
 
 .PHONY: all install clean config-clean strip install-gtk
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
-.PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope FORCE single_dep
+.PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope FORCE prepare
 .PHONY: libtraceevent_plugins
 
index b00dfd92ea731fe3c6319b703d2780207fe4cd6e..e83c8ce243039c12591781c1eea645fd2e341b2d 100644 (file)
@@ -128,9 +128,8 @@ static const char *normalize_arch(char *arch)
        return arch;
 }
 
-static int perf_session_env__lookup_binutils_path(struct perf_env *env,
-                                                 const char *name,
-                                                 const char **path)
+static int perf_env__lookup_binutils_path(struct perf_env *env,
+                                         const char *name, const char **path)
 {
        int idx;
        const char *arch, *cross_env;
@@ -206,7 +205,7 @@ out_error:
        return -1;
 }
 
-int perf_session_env__lookup_objdump(struct perf_env *env)
+int perf_env__lookup_objdump(struct perf_env *env)
 {
        /*
         * For live mode, env->arch will be NULL and we can use
@@ -215,6 +214,5 @@ int perf_session_env__lookup_objdump(struct perf_env *env)
        if (env->arch == NULL)
                return 0;
 
-       return perf_session_env__lookup_binutils_path(env, "objdump",
-                                                     &objdump_path);
+       return perf_env__lookup_binutils_path(env, "objdump", &objdump_path);
 }
index 20176df69fc83fcf31d3c38817b367723b0bed06..7529cfb143cecf0aaf74dbaaf277f3fe58e871e4 100644 (file)
@@ -1,10 +1,10 @@
 #ifndef ARCH_PERF_COMMON_H
 #define ARCH_PERF_COMMON_H
 
-#include "../util/session.h"
+#include "../util/env.h"
 
 extern const char *objdump_path;
 
-int perf_session_env__lookup_objdump(struct perf_env *env);
+int perf_env__lookup_objdump(struct perf_env *env);
 
 #endif /* ARCH_PERF_COMMON_H */
index 41bf61da476a4ce3150fc0184c84718d807be894..db52fa22d3a1d64a6fe62cffd4b34cd9ffe066f9 100644 (file)
@@ -1,2 +1,2 @@
 libperf-y += util/
-libperf-$(CONFIG_DWARF_UNWIND) += tests/
+libperf-y += tests/
index 21322e0385b886667d7bbd9a17edddc1ad1b3c8f..09ba923debe86810f8380f7df54504dee4232ec8 100644 (file)
@@ -2,3 +2,4 @@ ifndef NO_DWARF
 PERF_HAVE_DWARF_REGS := 1
 endif
 HAVE_KVM_STAT_SUPPORT := 1
+PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h
new file mode 100644 (file)
index 0000000..7ed00f4
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef ARCH_TESTS_H
+#define ARCH_TESTS_H
+
+/* Tests */
+int test__rdpmc(void);
+int test__perf_time_to_tsc(void);
+int test__insn_x86(void);
+int test__intel_cqm_count_nmi_context(void);
+
+#ifdef HAVE_DWARF_UNWIND_SUPPORT
+struct thread;
+struct perf_sample;
+int test__arch_unwind_sample(struct perf_sample *sample,
+                            struct thread *thread);
+#endif
+
+extern struct test arch_tests[];
+
+#endif
index b30eff9bcc83fd14026100f8626e913fd8f954e9..cbb7e978166bcc67209f31a9600149c6a5fe30bc 100644 (file)
@@ -1,2 +1,8 @@
-libperf-y += regs_load.o
-libperf-y += dwarf-unwind.o
+libperf-$(CONFIG_DWARF_UNWIND) += regs_load.o
+libperf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
+
+libperf-y += arch-tests.o
+libperf-y += rdpmc.o
+libperf-y += perf-time-to-tsc.o
+libperf-$(CONFIG_AUXTRACE) += insn-x86.o
+libperf-y += intel-cqm.o
diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c
new file mode 100644 (file)
index 0000000..2218cb6
--- /dev/null
@@ -0,0 +1,34 @@
+#include <string.h>
+#include "tests/tests.h"
+#include "arch-tests.h"
+
+struct test arch_tests[] = {
+       {
+               .desc = "x86 rdpmc test",
+               .func = test__rdpmc,
+       },
+       {
+               .desc = "Test converting perf time to TSC",
+               .func = test__perf_time_to_tsc,
+       },
+#ifdef HAVE_DWARF_UNWIND_SUPPORT
+       {
+               .desc = "Test dwarf unwind",
+               .func = test__dwarf_unwind,
+       },
+#endif
+#ifdef HAVE_AUXTRACE_SUPPORT
+       {
+               .desc = "Test x86 instruction decoder - new instructions",
+               .func = test__insn_x86,
+       },
+#endif
+       {
+               .desc = "Test intel cqm nmi context read",
+               .func = test__intel_cqm_count_nmi_context,
+       },
+       {
+               .func = NULL,
+       },
+
+};
index d8bbf7ad1681f6e5afb4a49374a3f0dbd625f3ef..7f209ce827bf5aa9e0f8ca5ee137c93ec69f451d 100644 (file)
@@ -5,6 +5,7 @@
 #include "event.h"
 #include "debug.h"
 #include "tests/tests.h"
+#include "arch-tests.h"
 
 #define STACK_SIZE 8192
 
diff --git a/tools/perf/arch/x86/tests/gen-insn-x86-dat.awk b/tools/perf/arch/x86/tests/gen-insn-x86-dat.awk
new file mode 100644 (file)
index 0000000..a214548
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/awk -f
+# gen-insn-x86-dat.awk: script to convert data for the insn-x86 test
+# Copyright (c) 2015, 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.
+
+BEGIN {
+       print "/*"
+       print " * Generated by gen-insn-x86-dat.sh and gen-insn-x86-dat.awk"
+       print " * from insn-x86-dat-src.c for inclusion by insn-x86.c"
+       print " * Do not change this code."
+       print "*/\n"
+       op = ""
+       branch = ""
+       rel = 0
+       going = 0
+}
+
+/ Start here / {
+       going = 1
+}
+
+/ Stop here / {
+       going = 0
+}
+
+/^\s*[0-9a-fA-F]+\:/ {
+       if (going) {
+               colon_pos = index($0, ":")
+               useful_line = substr($0, colon_pos + 1)
+               first_pos = match(useful_line, "[0-9a-fA-F]")
+               useful_line = substr(useful_line, first_pos)
+               gsub("\t", "\\t", useful_line)
+               printf "{{"
+               len = 0
+               for (i = 2; i <= NF; i++) {
+                       if (match($i, "^[0-9a-fA-F][0-9a-fA-F]$")) {
+                               printf "0x%s, ", $i
+                               len += 1
+                       } else {
+                               break
+                       }
+               }
+               printf "}, %d, %s, \"%s\", \"%s\",", len, rel, op, branch
+               printf "\n\"%s\",},\n", useful_line
+               op = ""
+               branch = ""
+               rel = 0
+       }
+}
+
+/ Expecting: / {
+       expecting_str = " Expecting: "
+       expecting_len = length(expecting_str)
+       expecting_pos = index($0, expecting_str)
+       useful_line = substr($0, expecting_pos + expecting_len)
+       for (i = 1; i <= NF; i++) {
+               if ($i == "Expecting:") {
+                       i++
+                       op = $i
+                       i++
+                       branch = $i
+                       i++
+                       rel = $i
+                       break
+               }
+       }
+}
diff --git a/tools/perf/arch/x86/tests/gen-insn-x86-dat.sh b/tools/perf/arch/x86/tests/gen-insn-x86-dat.sh
new file mode 100755 (executable)
index 0000000..2d4ef94
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh
+# gen-insn-x86-dat: generate data for the insn-x86 test
+# Copyright (c) 2015, 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.
+
+set -e
+
+if [ "$(uname -m)" != "x86_64" ]; then
+       echo "ERROR: This script only works on x86_64"
+       exit 1
+fi
+
+cd $(dirname $0)
+
+trap 'echo "Might need a more recent version of binutils"' EXIT
+
+echo "Compiling insn-x86-dat-src.c to 64-bit object"
+
+gcc -g -c insn-x86-dat-src.c
+
+objdump -dSw insn-x86-dat-src.o | awk -f gen-insn-x86-dat.awk > insn-x86-dat-64.c
+
+rm -f insn-x86-dat-src.o
+
+echo "Compiling insn-x86-dat-src.c to 32-bit object"
+
+gcc -g -c -m32 insn-x86-dat-src.c
+
+objdump -dSw insn-x86-dat-src.o | awk -f gen-insn-x86-dat.awk > insn-x86-dat-32.c
+
+rm -f insn-x86-dat-src.o
+
+trap - EXIT
+
+echo "Done (use git diff to see the changes)"
diff --git a/tools/perf/arch/x86/tests/insn-x86-dat-32.c b/tools/perf/arch/x86/tests/insn-x86-dat-32.c
new file mode 100644 (file)
index 0000000..3b491cf
--- /dev/null
@@ -0,0 +1,658 @@
+/*
+ * Generated by gen-insn-x86-dat.sh and gen-insn-x86-dat.awk
+ * from insn-x86-dat-src.c for inclusion by insn-x86.c
+ * Do not change this code.
+*/
+
+{{0x0f, 0x31, }, 2, 0, "", "",
+"0f 31                \trdtsc  ",},
+{{0xf3, 0x0f, 0x1b, 0x00, }, 4, 0, "", "",
+"f3 0f 1b 00          \tbndmk  (%eax),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f3 0f 1b 05 78 56 34 12 \tbndmk  0x12345678,%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x18, }, 4, 0, "", "",
+"f3 0f 1b 18          \tbndmk  (%eax),%bnd3",},
+{{0xf3, 0x0f, 0x1b, 0x04, 0x01, }, 5, 0, "", "",
+"f3 0f 1b 04 01       \tbndmk  (%ecx,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1b 04 05 78 56 34 12 \tbndmk  0x12345678(,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x04, 0x08, }, 5, 0, "", "",
+"f3 0f 1b 04 08       \tbndmk  (%eax,%ecx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x04, 0xc8, }, 5, 0, "", "",
+"f3 0f 1b 04 c8       \tbndmk  (%eax,%ecx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x40, 0x12, }, 5, 0, "", "",
+"f3 0f 1b 40 12       \tbndmk  0x12(%eax),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x45, 0x12, }, 5, 0, "", "",
+"f3 0f 1b 45 12       \tbndmk  0x12(%ebp),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"f3 0f 1b 44 01 12    \tbndmk  0x12(%ecx,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"f3 0f 1b 44 05 12    \tbndmk  0x12(%ebp,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"f3 0f 1b 44 08 12    \tbndmk  0x12(%eax,%ecx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"f3 0f 1b 44 c8 12    \tbndmk  0x12(%eax,%ecx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f3 0f 1b 80 78 56 34 12 \tbndmk  0x12345678(%eax),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f3 0f 1b 85 78 56 34 12 \tbndmk  0x12345678(%ebp),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1b 84 01 78 56 34 12 \tbndmk  0x12345678(%ecx,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1b 84 05 78 56 34 12 \tbndmk  0x12345678(%ebp,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1b 84 08 78 56 34 12 \tbndmk  0x12345678(%eax,%ecx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1b 84 c8 78 56 34 12 \tbndmk  0x12345678(%eax,%ecx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x00, }, 4, 0, "", "",
+"f3 0f 1a 00          \tbndcl  (%eax),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f3 0f 1a 05 78 56 34 12 \tbndcl  0x12345678,%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x18, }, 4, 0, "", "",
+"f3 0f 1a 18          \tbndcl  (%eax),%bnd3",},
+{{0xf3, 0x0f, 0x1a, 0x04, 0x01, }, 5, 0, "", "",
+"f3 0f 1a 04 01       \tbndcl  (%ecx,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1a 04 05 78 56 34 12 \tbndcl  0x12345678(,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x04, 0x08, }, 5, 0, "", "",
+"f3 0f 1a 04 08       \tbndcl  (%eax,%ecx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x04, 0xc8, }, 5, 0, "", "",
+"f3 0f 1a 04 c8       \tbndcl  (%eax,%ecx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x40, 0x12, }, 5, 0, "", "",
+"f3 0f 1a 40 12       \tbndcl  0x12(%eax),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x45, 0x12, }, 5, 0, "", "",
+"f3 0f 1a 45 12       \tbndcl  0x12(%ebp),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"f3 0f 1a 44 01 12    \tbndcl  0x12(%ecx,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"f3 0f 1a 44 05 12    \tbndcl  0x12(%ebp,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"f3 0f 1a 44 08 12    \tbndcl  0x12(%eax,%ecx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"f3 0f 1a 44 c8 12    \tbndcl  0x12(%eax,%ecx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f3 0f 1a 80 78 56 34 12 \tbndcl  0x12345678(%eax),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f3 0f 1a 85 78 56 34 12 \tbndcl  0x12345678(%ebp),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1a 84 01 78 56 34 12 \tbndcl  0x12345678(%ecx,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1a 84 05 78 56 34 12 \tbndcl  0x12345678(%ebp,%eax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1a 84 08 78 56 34 12 \tbndcl  0x12345678(%eax,%ecx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1a 84 c8 78 56 34 12 \tbndcl  0x12345678(%eax,%ecx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0xc0, }, 4, 0, "", "",
+"f3 0f 1a c0          \tbndcl  %eax,%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x00, }, 4, 0, "", "",
+"f2 0f 1a 00          \tbndcu  (%eax),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f2 0f 1a 05 78 56 34 12 \tbndcu  0x12345678,%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x18, }, 4, 0, "", "",
+"f2 0f 1a 18          \tbndcu  (%eax),%bnd3",},
+{{0xf2, 0x0f, 0x1a, 0x04, 0x01, }, 5, 0, "", "",
+"f2 0f 1a 04 01       \tbndcu  (%ecx,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1a 04 05 78 56 34 12 \tbndcu  0x12345678(,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x04, 0x08, }, 5, 0, "", "",
+"f2 0f 1a 04 08       \tbndcu  (%eax,%ecx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x04, 0xc8, }, 5, 0, "", "",
+"f2 0f 1a 04 c8       \tbndcu  (%eax,%ecx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x40, 0x12, }, 5, 0, "", "",
+"f2 0f 1a 40 12       \tbndcu  0x12(%eax),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x45, 0x12, }, 5, 0, "", "",
+"f2 0f 1a 45 12       \tbndcu  0x12(%ebp),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"f2 0f 1a 44 01 12    \tbndcu  0x12(%ecx,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"f2 0f 1a 44 05 12    \tbndcu  0x12(%ebp,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"f2 0f 1a 44 08 12    \tbndcu  0x12(%eax,%ecx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"f2 0f 1a 44 c8 12    \tbndcu  0x12(%eax,%ecx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f2 0f 1a 80 78 56 34 12 \tbndcu  0x12345678(%eax),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f2 0f 1a 85 78 56 34 12 \tbndcu  0x12345678(%ebp),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1a 84 01 78 56 34 12 \tbndcu  0x12345678(%ecx,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1a 84 05 78 56 34 12 \tbndcu  0x12345678(%ebp,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1a 84 08 78 56 34 12 \tbndcu  0x12345678(%eax,%ecx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1a 84 c8 78 56 34 12 \tbndcu  0x12345678(%eax,%ecx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0xc0, }, 4, 0, "", "",
+"f2 0f 1a c0          \tbndcu  %eax,%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x00, }, 4, 0, "", "",
+"f2 0f 1b 00          \tbndcn  (%eax),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f2 0f 1b 05 78 56 34 12 \tbndcn  0x12345678,%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x18, }, 4, 0, "", "",
+"f2 0f 1b 18          \tbndcn  (%eax),%bnd3",},
+{{0xf2, 0x0f, 0x1b, 0x04, 0x01, }, 5, 0, "", "",
+"f2 0f 1b 04 01       \tbndcn  (%ecx,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1b 04 05 78 56 34 12 \tbndcn  0x12345678(,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x04, 0x08, }, 5, 0, "", "",
+"f2 0f 1b 04 08       \tbndcn  (%eax,%ecx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x04, 0xc8, }, 5, 0, "", "",
+"f2 0f 1b 04 c8       \tbndcn  (%eax,%ecx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x40, 0x12, }, 5, 0, "", "",
+"f2 0f 1b 40 12       \tbndcn  0x12(%eax),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x45, 0x12, }, 5, 0, "", "",
+"f2 0f 1b 45 12       \tbndcn  0x12(%ebp),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"f2 0f 1b 44 01 12    \tbndcn  0x12(%ecx,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"f2 0f 1b 44 05 12    \tbndcn  0x12(%ebp,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"f2 0f 1b 44 08 12    \tbndcn  0x12(%eax,%ecx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"f2 0f 1b 44 c8 12    \tbndcn  0x12(%eax,%ecx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f2 0f 1b 80 78 56 34 12 \tbndcn  0x12345678(%eax),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f2 0f 1b 85 78 56 34 12 \tbndcn  0x12345678(%ebp),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1b 84 01 78 56 34 12 \tbndcn  0x12345678(%ecx,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1b 84 05 78 56 34 12 \tbndcn  0x12345678(%ebp,%eax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1b 84 08 78 56 34 12 \tbndcn  0x12345678(%eax,%ecx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1b 84 c8 78 56 34 12 \tbndcn  0x12345678(%eax,%ecx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0xc0, }, 4, 0, "", "",
+"f2 0f 1b c0          \tbndcn  %eax,%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x00, }, 4, 0, "", "",
+"66 0f 1a 00          \tbndmov (%eax),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f 1a 05 78 56 34 12 \tbndmov 0x12345678,%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x18, }, 4, 0, "", "",
+"66 0f 1a 18          \tbndmov (%eax),%bnd3",},
+{{0x66, 0x0f, 0x1a, 0x04, 0x01, }, 5, 0, "", "",
+"66 0f 1a 04 01       \tbndmov (%ecx,%eax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1a 04 05 78 56 34 12 \tbndmov 0x12345678(,%eax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x04, 0x08, }, 5, 0, "", "",
+"66 0f 1a 04 08       \tbndmov (%eax,%ecx,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x04, 0xc8, }, 5, 0, "", "",
+"66 0f 1a 04 c8       \tbndmov (%eax,%ecx,8),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x40, 0x12, }, 5, 0, "", "",
+"66 0f 1a 40 12       \tbndmov 0x12(%eax),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x45, 0x12, }, 5, 0, "", "",
+"66 0f 1a 45 12       \tbndmov 0x12(%ebp),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"66 0f 1a 44 01 12    \tbndmov 0x12(%ecx,%eax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"66 0f 1a 44 05 12    \tbndmov 0x12(%ebp,%eax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"66 0f 1a 44 08 12    \tbndmov 0x12(%eax,%ecx,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"66 0f 1a 44 c8 12    \tbndmov 0x12(%eax,%ecx,8),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f 1a 80 78 56 34 12 \tbndmov 0x12345678(%eax),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f 1a 85 78 56 34 12 \tbndmov 0x12345678(%ebp),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1a 84 01 78 56 34 12 \tbndmov 0x12345678(%ecx,%eax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1a 84 05 78 56 34 12 \tbndmov 0x12345678(%ebp,%eax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1a 84 08 78 56 34 12 \tbndmov 0x12345678(%eax,%ecx,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1a 84 c8 78 56 34 12 \tbndmov 0x12345678(%eax,%ecx,8),%bnd0",},
+{{0x66, 0x0f, 0x1b, 0x00, }, 4, 0, "", "",
+"66 0f 1b 00          \tbndmov %bnd0,(%eax)",},
+{{0x66, 0x0f, 0x1b, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f 1b 05 78 56 34 12 \tbndmov %bnd0,0x12345678",},
+{{0x66, 0x0f, 0x1b, 0x18, }, 4, 0, "", "",
+"66 0f 1b 18          \tbndmov %bnd3,(%eax)",},
+{{0x66, 0x0f, 0x1b, 0x04, 0x01, }, 5, 0, "", "",
+"66 0f 1b 04 01       \tbndmov %bnd0,(%ecx,%eax,1)",},
+{{0x66, 0x0f, 0x1b, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1b 04 05 78 56 34 12 \tbndmov %bnd0,0x12345678(,%eax,1)",},
+{{0x66, 0x0f, 0x1b, 0x04, 0x08, }, 5, 0, "", "",
+"66 0f 1b 04 08       \tbndmov %bnd0,(%eax,%ecx,1)",},
+{{0x66, 0x0f, 0x1b, 0x04, 0xc8, }, 5, 0, "", "",
+"66 0f 1b 04 c8       \tbndmov %bnd0,(%eax,%ecx,8)",},
+{{0x66, 0x0f, 0x1b, 0x40, 0x12, }, 5, 0, "", "",
+"66 0f 1b 40 12       \tbndmov %bnd0,0x12(%eax)",},
+{{0x66, 0x0f, 0x1b, 0x45, 0x12, }, 5, 0, "", "",
+"66 0f 1b 45 12       \tbndmov %bnd0,0x12(%ebp)",},
+{{0x66, 0x0f, 0x1b, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"66 0f 1b 44 01 12    \tbndmov %bnd0,0x12(%ecx,%eax,1)",},
+{{0x66, 0x0f, 0x1b, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"66 0f 1b 44 05 12    \tbndmov %bnd0,0x12(%ebp,%eax,1)",},
+{{0x66, 0x0f, 0x1b, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"66 0f 1b 44 08 12    \tbndmov %bnd0,0x12(%eax,%ecx,1)",},
+{{0x66, 0x0f, 0x1b, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"66 0f 1b 44 c8 12    \tbndmov %bnd0,0x12(%eax,%ecx,8)",},
+{{0x66, 0x0f, 0x1b, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f 1b 80 78 56 34 12 \tbndmov %bnd0,0x12345678(%eax)",},
+{{0x66, 0x0f, 0x1b, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f 1b 85 78 56 34 12 \tbndmov %bnd0,0x12345678(%ebp)",},
+{{0x66, 0x0f, 0x1b, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1b 84 01 78 56 34 12 \tbndmov %bnd0,0x12345678(%ecx,%eax,1)",},
+{{0x66, 0x0f, 0x1b, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1b 84 05 78 56 34 12 \tbndmov %bnd0,0x12345678(%ebp,%eax,1)",},
+{{0x66, 0x0f, 0x1b, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1b 84 08 78 56 34 12 \tbndmov %bnd0,0x12345678(%eax,%ecx,1)",},
+{{0x66, 0x0f, 0x1b, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1b 84 c8 78 56 34 12 \tbndmov %bnd0,0x12345678(%eax,%ecx,8)",},
+{{0x66, 0x0f, 0x1a, 0xc8, }, 4, 0, "", "",
+"66 0f 1a c8          \tbndmov %bnd0,%bnd1",},
+{{0x66, 0x0f, 0x1a, 0xc1, }, 4, 0, "", "",
+"66 0f 1a c1          \tbndmov %bnd1,%bnd0",},
+{{0x0f, 0x1a, 0x00, }, 3, 0, "", "",
+"0f 1a 00             \tbndldx (%eax),%bnd0",},
+{{0x0f, 0x1a, 0x05, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f 1a 05 78 56 34 12 \tbndldx 0x12345678,%bnd0",},
+{{0x0f, 0x1a, 0x18, }, 3, 0, "", "",
+"0f 1a 18             \tbndldx (%eax),%bnd3",},
+{{0x0f, 0x1a, 0x04, 0x01, }, 4, 0, "", "",
+"0f 1a 04 01          \tbndldx (%ecx,%eax,1),%bnd0",},
+{{0x0f, 0x1a, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1a 04 05 78 56 34 12 \tbndldx 0x12345678(,%eax,1),%bnd0",},
+{{0x0f, 0x1a, 0x04, 0x08, }, 4, 0, "", "",
+"0f 1a 04 08          \tbndldx (%eax,%ecx,1),%bnd0",},
+{{0x0f, 0x1a, 0x40, 0x12, }, 4, 0, "", "",
+"0f 1a 40 12          \tbndldx 0x12(%eax),%bnd0",},
+{{0x0f, 0x1a, 0x45, 0x12, }, 4, 0, "", "",
+"0f 1a 45 12          \tbndldx 0x12(%ebp),%bnd0",},
+{{0x0f, 0x1a, 0x44, 0x01, 0x12, }, 5, 0, "", "",
+"0f 1a 44 01 12       \tbndldx 0x12(%ecx,%eax,1),%bnd0",},
+{{0x0f, 0x1a, 0x44, 0x05, 0x12, }, 5, 0, "", "",
+"0f 1a 44 05 12       \tbndldx 0x12(%ebp,%eax,1),%bnd0",},
+{{0x0f, 0x1a, 0x44, 0x08, 0x12, }, 5, 0, "", "",
+"0f 1a 44 08 12       \tbndldx 0x12(%eax,%ecx,1),%bnd0",},
+{{0x0f, 0x1a, 0x80, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f 1a 80 78 56 34 12 \tbndldx 0x12345678(%eax),%bnd0",},
+{{0x0f, 0x1a, 0x85, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f 1a 85 78 56 34 12 \tbndldx 0x12345678(%ebp),%bnd0",},
+{{0x0f, 0x1a, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1a 84 01 78 56 34 12 \tbndldx 0x12345678(%ecx,%eax,1),%bnd0",},
+{{0x0f, 0x1a, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1a 84 05 78 56 34 12 \tbndldx 0x12345678(%ebp,%eax,1),%bnd0",},
+{{0x0f, 0x1a, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1a 84 08 78 56 34 12 \tbndldx 0x12345678(%eax,%ecx,1),%bnd0",},
+{{0x0f, 0x1b, 0x00, }, 3, 0, "", "",
+"0f 1b 00             \tbndstx %bnd0,(%eax)",},
+{{0x0f, 0x1b, 0x05, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f 1b 05 78 56 34 12 \tbndstx %bnd0,0x12345678",},
+{{0x0f, 0x1b, 0x18, }, 3, 0, "", "",
+"0f 1b 18             \tbndstx %bnd3,(%eax)",},
+{{0x0f, 0x1b, 0x04, 0x01, }, 4, 0, "", "",
+"0f 1b 04 01          \tbndstx %bnd0,(%ecx,%eax,1)",},
+{{0x0f, 0x1b, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1b 04 05 78 56 34 12 \tbndstx %bnd0,0x12345678(,%eax,1)",},
+{{0x0f, 0x1b, 0x04, 0x08, }, 4, 0, "", "",
+"0f 1b 04 08          \tbndstx %bnd0,(%eax,%ecx,1)",},
+{{0x0f, 0x1b, 0x40, 0x12, }, 4, 0, "", "",
+"0f 1b 40 12          \tbndstx %bnd0,0x12(%eax)",},
+{{0x0f, 0x1b, 0x45, 0x12, }, 4, 0, "", "",
+"0f 1b 45 12          \tbndstx %bnd0,0x12(%ebp)",},
+{{0x0f, 0x1b, 0x44, 0x01, 0x12, }, 5, 0, "", "",
+"0f 1b 44 01 12       \tbndstx %bnd0,0x12(%ecx,%eax,1)",},
+{{0x0f, 0x1b, 0x44, 0x05, 0x12, }, 5, 0, "", "",
+"0f 1b 44 05 12       \tbndstx %bnd0,0x12(%ebp,%eax,1)",},
+{{0x0f, 0x1b, 0x44, 0x08, 0x12, }, 5, 0, "", "",
+"0f 1b 44 08 12       \tbndstx %bnd0,0x12(%eax,%ecx,1)",},
+{{0x0f, 0x1b, 0x80, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f 1b 80 78 56 34 12 \tbndstx %bnd0,0x12345678(%eax)",},
+{{0x0f, 0x1b, 0x85, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f 1b 85 78 56 34 12 \tbndstx %bnd0,0x12345678(%ebp)",},
+{{0x0f, 0x1b, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1b 84 01 78 56 34 12 \tbndstx %bnd0,0x12345678(%ecx,%eax,1)",},
+{{0x0f, 0x1b, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1b 84 05 78 56 34 12 \tbndstx %bnd0,0x12345678(%ebp,%eax,1)",},
+{{0x0f, 0x1b, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1b 84 08 78 56 34 12 \tbndstx %bnd0,0x12345678(%eax,%ecx,1)",},
+{{0xf2, 0xe8, 0xfc, 0xff, 0xff, 0xff, }, 6, 0xfffffffc, "call", "unconditional",
+"f2 e8 fc ff ff ff    \tbnd call 3c3 <main+0x3c3>",},
+{{0xf2, 0xff, 0x10, }, 3, 0, "call", "indirect",
+"f2 ff 10             \tbnd call *(%eax)",},
+{{0xf2, 0xc3, }, 2, 0, "ret", "indirect",
+"f2 c3                \tbnd ret ",},
+{{0xf2, 0xe9, 0xfc, 0xff, 0xff, 0xff, }, 6, 0xfffffffc, "jmp", "unconditional",
+"f2 e9 fc ff ff ff    \tbnd jmp 3ce <main+0x3ce>",},
+{{0xf2, 0xe9, 0xfc, 0xff, 0xff, 0xff, }, 6, 0xfffffffc, "jmp", "unconditional",
+"f2 e9 fc ff ff ff    \tbnd jmp 3d4 <main+0x3d4>",},
+{{0xf2, 0xff, 0x21, }, 3, 0, "jmp", "indirect",
+"f2 ff 21             \tbnd jmp *(%ecx)",},
+{{0xf2, 0x0f, 0x85, 0xfc, 0xff, 0xff, 0xff, }, 7, 0xfffffffc, "jcc", "conditional",
+"f2 0f 85 fc ff ff ff \tbnd jne 3de <main+0x3de>",},
+{{0x0f, 0x3a, 0xcc, 0xc1, 0x00, }, 5, 0, "", "",
+"0f 3a cc c1 00       \tsha1rnds4 $0x0,%xmm1,%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0xd7, 0x91, }, 5, 0, "", "",
+"0f 3a cc d7 91       \tsha1rnds4 $0x91,%xmm7,%xmm2",},
+{{0x0f, 0x3a, 0xcc, 0x00, 0x91, }, 5, 0, "", "",
+"0f 3a cc 00 91       \tsha1rnds4 $0x91,(%eax),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x05, 0x78, 0x56, 0x34, 0x12, 0x91, }, 9, 0, "", "",
+"0f 3a cc 05 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678,%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x18, 0x91, }, 5, 0, "", "",
+"0f 3a cc 18 91       \tsha1rnds4 $0x91,(%eax),%xmm3",},
+{{0x0f, 0x3a, 0xcc, 0x04, 0x01, 0x91, }, 6, 0, "", "",
+"0f 3a cc 04 01 91    \tsha1rnds4 $0x91,(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, 0x91, }, 10, 0, "", "",
+"0f 3a cc 04 05 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(,%eax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x04, 0x08, 0x91, }, 6, 0, "", "",
+"0f 3a cc 04 08 91    \tsha1rnds4 $0x91,(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x04, 0xc8, 0x91, }, 6, 0, "", "",
+"0f 3a cc 04 c8 91    \tsha1rnds4 $0x91,(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x40, 0x12, 0x91, }, 6, 0, "", "",
+"0f 3a cc 40 12 91    \tsha1rnds4 $0x91,0x12(%eax),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x45, 0x12, 0x91, }, 6, 0, "", "",
+"0f 3a cc 45 12 91    \tsha1rnds4 $0x91,0x12(%ebp),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x44, 0x01, 0x12, 0x91, }, 7, 0, "", "",
+"0f 3a cc 44 01 12 91 \tsha1rnds4 $0x91,0x12(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x44, 0x05, 0x12, 0x91, }, 7, 0, "", "",
+"0f 3a cc 44 05 12 91 \tsha1rnds4 $0x91,0x12(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x44, 0x08, 0x12, 0x91, }, 7, 0, "", "",
+"0f 3a cc 44 08 12 91 \tsha1rnds4 $0x91,0x12(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x44, 0xc8, 0x12, 0x91, }, 7, 0, "", "",
+"0f 3a cc 44 c8 12 91 \tsha1rnds4 $0x91,0x12(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x80, 0x78, 0x56, 0x34, 0x12, 0x91, }, 9, 0, "", "",
+"0f 3a cc 80 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%eax),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x85, 0x78, 0x56, 0x34, 0x12, 0x91, }, 9, 0, "", "",
+"0f 3a cc 85 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%ebp),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, 0x91, }, 10, 0, "", "",
+"0f 3a cc 84 01 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, 0x91, }, 10, 0, "", "",
+"0f 3a cc 84 05 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, 0x91, }, 10, 0, "", "",
+"0f 3a cc 84 08 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, 0x91, }, 10, 0, "", "",
+"0f 3a cc 84 c8 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0xc1, }, 4, 0, "", "",
+"0f 38 c8 c1          \tsha1nexte %xmm1,%xmm0",},
+{{0x0f, 0x38, 0xc8, 0xd7, }, 4, 0, "", "",
+"0f 38 c8 d7          \tsha1nexte %xmm7,%xmm2",},
+{{0x0f, 0x38, 0xc8, 0x00, }, 4, 0, "", "",
+"0f 38 c8 00          \tsha1nexte (%eax),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 c8 05 78 56 34 12 \tsha1nexte 0x12345678,%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x18, }, 4, 0, "", "",
+"0f 38 c8 18          \tsha1nexte (%eax),%xmm3",},
+{{0x0f, 0x38, 0xc8, 0x04, 0x01, }, 5, 0, "", "",
+"0f 38 c8 04 01       \tsha1nexte (%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c8 04 05 78 56 34 12 \tsha1nexte 0x12345678(,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x04, 0x08, }, 5, 0, "", "",
+"0f 38 c8 04 08       \tsha1nexte (%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x04, 0xc8, }, 5, 0, "", "",
+"0f 38 c8 04 c8       \tsha1nexte (%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x40, 0x12, }, 5, 0, "", "",
+"0f 38 c8 40 12       \tsha1nexte 0x12(%eax),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x45, 0x12, }, 5, 0, "", "",
+"0f 38 c8 45 12       \tsha1nexte 0x12(%ebp),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 c8 44 01 12    \tsha1nexte 0x12(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 c8 44 05 12    \tsha1nexte 0x12(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 c8 44 08 12    \tsha1nexte 0x12(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 c8 44 c8 12    \tsha1nexte 0x12(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 c8 80 78 56 34 12 \tsha1nexte 0x12345678(%eax),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 c8 85 78 56 34 12 \tsha1nexte 0x12345678(%ebp),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c8 84 01 78 56 34 12 \tsha1nexte 0x12345678(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c8 84 05 78 56 34 12 \tsha1nexte 0x12345678(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c8 84 08 78 56 34 12 \tsha1nexte 0x12345678(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c8 84 c8 78 56 34 12 \tsha1nexte 0x12345678(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0xc1, }, 4, 0, "", "",
+"0f 38 c9 c1          \tsha1msg1 %xmm1,%xmm0",},
+{{0x0f, 0x38, 0xc9, 0xd7, }, 4, 0, "", "",
+"0f 38 c9 d7          \tsha1msg1 %xmm7,%xmm2",},
+{{0x0f, 0x38, 0xc9, 0x00, }, 4, 0, "", "",
+"0f 38 c9 00          \tsha1msg1 (%eax),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 c9 05 78 56 34 12 \tsha1msg1 0x12345678,%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x18, }, 4, 0, "", "",
+"0f 38 c9 18          \tsha1msg1 (%eax),%xmm3",},
+{{0x0f, 0x38, 0xc9, 0x04, 0x01, }, 5, 0, "", "",
+"0f 38 c9 04 01       \tsha1msg1 (%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c9 04 05 78 56 34 12 \tsha1msg1 0x12345678(,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x04, 0x08, }, 5, 0, "", "",
+"0f 38 c9 04 08       \tsha1msg1 (%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x04, 0xc8, }, 5, 0, "", "",
+"0f 38 c9 04 c8       \tsha1msg1 (%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x40, 0x12, }, 5, 0, "", "",
+"0f 38 c9 40 12       \tsha1msg1 0x12(%eax),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x45, 0x12, }, 5, 0, "", "",
+"0f 38 c9 45 12       \tsha1msg1 0x12(%ebp),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 c9 44 01 12    \tsha1msg1 0x12(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 c9 44 05 12    \tsha1msg1 0x12(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 c9 44 08 12    \tsha1msg1 0x12(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 c9 44 c8 12    \tsha1msg1 0x12(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 c9 80 78 56 34 12 \tsha1msg1 0x12345678(%eax),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 c9 85 78 56 34 12 \tsha1msg1 0x12345678(%ebp),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c9 84 01 78 56 34 12 \tsha1msg1 0x12345678(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c9 84 05 78 56 34 12 \tsha1msg1 0x12345678(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c9 84 08 78 56 34 12 \tsha1msg1 0x12345678(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c9 84 c8 78 56 34 12 \tsha1msg1 0x12345678(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xca, 0xc1, }, 4, 0, "", "",
+"0f 38 ca c1          \tsha1msg2 %xmm1,%xmm0",},
+{{0x0f, 0x38, 0xca, 0xd7, }, 4, 0, "", "",
+"0f 38 ca d7          \tsha1msg2 %xmm7,%xmm2",},
+{{0x0f, 0x38, 0xca, 0x00, }, 4, 0, "", "",
+"0f 38 ca 00          \tsha1msg2 (%eax),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 ca 05 78 56 34 12 \tsha1msg2 0x12345678,%xmm0",},
+{{0x0f, 0x38, 0xca, 0x18, }, 4, 0, "", "",
+"0f 38 ca 18          \tsha1msg2 (%eax),%xmm3",},
+{{0x0f, 0x38, 0xca, 0x04, 0x01, }, 5, 0, "", "",
+"0f 38 ca 04 01       \tsha1msg2 (%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 ca 04 05 78 56 34 12 \tsha1msg2 0x12345678(,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x04, 0x08, }, 5, 0, "", "",
+"0f 38 ca 04 08       \tsha1msg2 (%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x04, 0xc8, }, 5, 0, "", "",
+"0f 38 ca 04 c8       \tsha1msg2 (%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x40, 0x12, }, 5, 0, "", "",
+"0f 38 ca 40 12       \tsha1msg2 0x12(%eax),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x45, 0x12, }, 5, 0, "", "",
+"0f 38 ca 45 12       \tsha1msg2 0x12(%ebp),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 ca 44 01 12    \tsha1msg2 0x12(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 ca 44 05 12    \tsha1msg2 0x12(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 ca 44 08 12    \tsha1msg2 0x12(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 ca 44 c8 12    \tsha1msg2 0x12(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 ca 80 78 56 34 12 \tsha1msg2 0x12345678(%eax),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 ca 85 78 56 34 12 \tsha1msg2 0x12345678(%ebp),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 ca 84 01 78 56 34 12 \tsha1msg2 0x12345678(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 ca 84 05 78 56 34 12 \tsha1msg2 0x12345678(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 ca 84 08 78 56 34 12 \tsha1msg2 0x12345678(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 ca 84 c8 78 56 34 12 \tsha1msg2 0x12345678(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xcb, 0xcc, }, 4, 0, "", "",
+"0f 38 cb cc          \tsha256rnds2 %xmm0,%xmm4,%xmm1",},
+{{0x0f, 0x38, 0xcb, 0xd7, }, 4, 0, "", "",
+"0f 38 cb d7          \tsha256rnds2 %xmm0,%xmm7,%xmm2",},
+{{0x0f, 0x38, 0xcb, 0x08, }, 4, 0, "", "",
+"0f 38 cb 08          \tsha256rnds2 %xmm0,(%eax),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x0d, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cb 0d 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678,%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x18, }, 4, 0, "", "",
+"0f 38 cb 18          \tsha256rnds2 %xmm0,(%eax),%xmm3",},
+{{0x0f, 0x38, 0xcb, 0x0c, 0x01, }, 5, 0, "", "",
+"0f 38 cb 0c 01       \tsha256rnds2 %xmm0,(%ecx,%eax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x0c, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cb 0c 05 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(,%eax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x0c, 0x08, }, 5, 0, "", "",
+"0f 38 cb 0c 08       \tsha256rnds2 %xmm0,(%eax,%ecx,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x0c, 0xc8, }, 5, 0, "", "",
+"0f 38 cb 0c c8       \tsha256rnds2 %xmm0,(%eax,%ecx,8),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x48, 0x12, }, 5, 0, "", "",
+"0f 38 cb 48 12       \tsha256rnds2 %xmm0,0x12(%eax),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x4d, 0x12, }, 5, 0, "", "",
+"0f 38 cb 4d 12       \tsha256rnds2 %xmm0,0x12(%ebp),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x4c, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 cb 4c 01 12    \tsha256rnds2 %xmm0,0x12(%ecx,%eax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x4c, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 cb 4c 05 12    \tsha256rnds2 %xmm0,0x12(%ebp,%eax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x4c, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 cb 4c 08 12    \tsha256rnds2 %xmm0,0x12(%eax,%ecx,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x4c, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 cb 4c c8 12    \tsha256rnds2 %xmm0,0x12(%eax,%ecx,8),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x88, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cb 88 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%eax),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x8d, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cb 8d 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%ebp),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x8c, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cb 8c 01 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%ecx,%eax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x8c, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cb 8c 05 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%ebp,%eax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x8c, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cb 8c 08 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%eax,%ecx,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x8c, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cb 8c c8 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%eax,%ecx,8),%xmm1",},
+{{0x0f, 0x38, 0xcc, 0xc1, }, 4, 0, "", "",
+"0f 38 cc c1          \tsha256msg1 %xmm1,%xmm0",},
+{{0x0f, 0x38, 0xcc, 0xd7, }, 4, 0, "", "",
+"0f 38 cc d7          \tsha256msg1 %xmm7,%xmm2",},
+{{0x0f, 0x38, 0xcc, 0x00, }, 4, 0, "", "",
+"0f 38 cc 00          \tsha256msg1 (%eax),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cc 05 78 56 34 12 \tsha256msg1 0x12345678,%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x18, }, 4, 0, "", "",
+"0f 38 cc 18          \tsha256msg1 (%eax),%xmm3",},
+{{0x0f, 0x38, 0xcc, 0x04, 0x01, }, 5, 0, "", "",
+"0f 38 cc 04 01       \tsha256msg1 (%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cc 04 05 78 56 34 12 \tsha256msg1 0x12345678(,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x04, 0x08, }, 5, 0, "", "",
+"0f 38 cc 04 08       \tsha256msg1 (%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x04, 0xc8, }, 5, 0, "", "",
+"0f 38 cc 04 c8       \tsha256msg1 (%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x40, 0x12, }, 5, 0, "", "",
+"0f 38 cc 40 12       \tsha256msg1 0x12(%eax),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x45, 0x12, }, 5, 0, "", "",
+"0f 38 cc 45 12       \tsha256msg1 0x12(%ebp),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 cc 44 01 12    \tsha256msg1 0x12(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 cc 44 05 12    \tsha256msg1 0x12(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 cc 44 08 12    \tsha256msg1 0x12(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 cc 44 c8 12    \tsha256msg1 0x12(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cc 80 78 56 34 12 \tsha256msg1 0x12345678(%eax),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cc 85 78 56 34 12 \tsha256msg1 0x12345678(%ebp),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cc 84 01 78 56 34 12 \tsha256msg1 0x12345678(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cc 84 05 78 56 34 12 \tsha256msg1 0x12345678(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cc 84 08 78 56 34 12 \tsha256msg1 0x12345678(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cc 84 c8 78 56 34 12 \tsha256msg1 0x12345678(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0xc1, }, 4, 0, "", "",
+"0f 38 cd c1          \tsha256msg2 %xmm1,%xmm0",},
+{{0x0f, 0x38, 0xcd, 0xd7, }, 4, 0, "", "",
+"0f 38 cd d7          \tsha256msg2 %xmm7,%xmm2",},
+{{0x0f, 0x38, 0xcd, 0x00, }, 4, 0, "", "",
+"0f 38 cd 00          \tsha256msg2 (%eax),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cd 05 78 56 34 12 \tsha256msg2 0x12345678,%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x18, }, 4, 0, "", "",
+"0f 38 cd 18          \tsha256msg2 (%eax),%xmm3",},
+{{0x0f, 0x38, 0xcd, 0x04, 0x01, }, 5, 0, "", "",
+"0f 38 cd 04 01       \tsha256msg2 (%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cd 04 05 78 56 34 12 \tsha256msg2 0x12345678(,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x04, 0x08, }, 5, 0, "", "",
+"0f 38 cd 04 08       \tsha256msg2 (%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x04, 0xc8, }, 5, 0, "", "",
+"0f 38 cd 04 c8       \tsha256msg2 (%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x40, 0x12, }, 5, 0, "", "",
+"0f 38 cd 40 12       \tsha256msg2 0x12(%eax),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x45, 0x12, }, 5, 0, "", "",
+"0f 38 cd 45 12       \tsha256msg2 0x12(%ebp),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 cd 44 01 12    \tsha256msg2 0x12(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 cd 44 05 12    \tsha256msg2 0x12(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 cd 44 08 12    \tsha256msg2 0x12(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 cd 44 c8 12    \tsha256msg2 0x12(%eax,%ecx,8),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cd 80 78 56 34 12 \tsha256msg2 0x12345678(%eax),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cd 85 78 56 34 12 \tsha256msg2 0x12345678(%ebp),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cd 84 01 78 56 34 12 \tsha256msg2 0x12345678(%ecx,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cd 84 05 78 56 34 12 \tsha256msg2 0x12345678(%ebp,%eax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cd 84 08 78 56 34 12 \tsha256msg2 0x12345678(%eax,%ecx,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cd 84 c8 78 56 34 12 \tsha256msg2 0x12345678(%eax,%ecx,8),%xmm0",},
+{{0x66, 0x0f, 0xae, 0x38, }, 4, 0, "", "",
+"66 0f ae 38          \tclflushopt (%eax)",},
+{{0x66, 0x0f, 0xae, 0x3d, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f ae 3d 78 56 34 12 \tclflushopt 0x12345678",},
+{{0x66, 0x0f, 0xae, 0xbc, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f ae bc c8 78 56 34 12 \tclflushopt 0x12345678(%eax,%ecx,8)",},
+{{0x0f, 0xae, 0x38, }, 3, 0, "", "",
+"0f ae 38             \tclflush (%eax)",},
+{{0x0f, 0xae, 0xf8, }, 3, 0, "", "",
+"0f ae f8             \tsfence ",},
+{{0x66, 0x0f, 0xae, 0x30, }, 4, 0, "", "",
+"66 0f ae 30          \tclwb   (%eax)",},
+{{0x66, 0x0f, 0xae, 0x35, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f ae 35 78 56 34 12 \tclwb   0x12345678",},
+{{0x66, 0x0f, 0xae, 0xb4, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f ae b4 c8 78 56 34 12 \tclwb   0x12345678(%eax,%ecx,8)",},
+{{0x0f, 0xae, 0x30, }, 3, 0, "", "",
+"0f ae 30             \txsaveopt (%eax)",},
+{{0x0f, 0xae, 0xf0, }, 3, 0, "", "",
+"0f ae f0             \tmfence ",},
+{{0x0f, 0xc7, 0x20, }, 3, 0, "", "",
+"0f c7 20             \txsavec (%eax)",},
+{{0x0f, 0xc7, 0x25, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f c7 25 78 56 34 12 \txsavec 0x12345678",},
+{{0x0f, 0xc7, 0xa4, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f c7 a4 c8 78 56 34 12 \txsavec 0x12345678(%eax,%ecx,8)",},
+{{0x0f, 0xc7, 0x28, }, 3, 0, "", "",
+"0f c7 28             \txsaves (%eax)",},
+{{0x0f, 0xc7, 0x2d, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f c7 2d 78 56 34 12 \txsaves 0x12345678",},
+{{0x0f, 0xc7, 0xac, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f c7 ac c8 78 56 34 12 \txsaves 0x12345678(%eax,%ecx,8)",},
+{{0x0f, 0xc7, 0x18, }, 3, 0, "", "",
+"0f c7 18             \txrstors (%eax)",},
+{{0x0f, 0xc7, 0x1d, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f c7 1d 78 56 34 12 \txrstors 0x12345678",},
+{{0x0f, 0xc7, 0x9c, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f c7 9c c8 78 56 34 12 \txrstors 0x12345678(%eax,%ecx,8)",},
+{{0x66, 0x0f, 0xae, 0xf8, }, 4, 0, "", "",
+"66 0f ae f8          \tpcommit ",},
diff --git a/tools/perf/arch/x86/tests/insn-x86-dat-64.c b/tools/perf/arch/x86/tests/insn-x86-dat-64.c
new file mode 100644 (file)
index 0000000..4fe7cce
--- /dev/null
@@ -0,0 +1,768 @@
+/*
+ * Generated by gen-insn-x86-dat.sh and gen-insn-x86-dat.awk
+ * from insn-x86-dat-src.c for inclusion by insn-x86.c
+ * Do not change this code.
+*/
+
+{{0x0f, 0x31, }, 2, 0, "", "",
+"0f 31                \trdtsc  ",},
+{{0xf3, 0x0f, 0x1b, 0x00, }, 4, 0, "", "",
+"f3 0f 1b 00          \tbndmk  (%rax),%bnd0",},
+{{0xf3, 0x41, 0x0f, 0x1b, 0x00, }, 5, 0, "", "",
+"f3 41 0f 1b 00       \tbndmk  (%r8),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1b 04 25 78 56 34 12 \tbndmk  0x12345678,%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x18, }, 4, 0, "", "",
+"f3 0f 1b 18          \tbndmk  (%rax),%bnd3",},
+{{0xf3, 0x0f, 0x1b, 0x04, 0x01, }, 5, 0, "", "",
+"f3 0f 1b 04 01       \tbndmk  (%rcx,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1b 04 05 78 56 34 12 \tbndmk  0x12345678(,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x04, 0x08, }, 5, 0, "", "",
+"f3 0f 1b 04 08       \tbndmk  (%rax,%rcx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x04, 0xc8, }, 5, 0, "", "",
+"f3 0f 1b 04 c8       \tbndmk  (%rax,%rcx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x40, 0x12, }, 5, 0, "", "",
+"f3 0f 1b 40 12       \tbndmk  0x12(%rax),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x45, 0x12, }, 5, 0, "", "",
+"f3 0f 1b 45 12       \tbndmk  0x12(%rbp),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"f3 0f 1b 44 01 12    \tbndmk  0x12(%rcx,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"f3 0f 1b 44 05 12    \tbndmk  0x12(%rbp,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"f3 0f 1b 44 08 12    \tbndmk  0x12(%rax,%rcx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"f3 0f 1b 44 c8 12    \tbndmk  0x12(%rax,%rcx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f3 0f 1b 80 78 56 34 12 \tbndmk  0x12345678(%rax),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f3 0f 1b 85 78 56 34 12 \tbndmk  0x12345678(%rbp),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1b 84 01 78 56 34 12 \tbndmk  0x12345678(%rcx,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1b 84 05 78 56 34 12 \tbndmk  0x12345678(%rbp,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1b 84 08 78 56 34 12 \tbndmk  0x12345678(%rax,%rcx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1b, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1b 84 c8 78 56 34 12 \tbndmk  0x12345678(%rax,%rcx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x00, }, 4, 0, "", "",
+"f3 0f 1a 00          \tbndcl  (%rax),%bnd0",},
+{{0xf3, 0x41, 0x0f, 0x1a, 0x00, }, 5, 0, "", "",
+"f3 41 0f 1a 00       \tbndcl  (%r8),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1a 04 25 78 56 34 12 \tbndcl  0x12345678,%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x18, }, 4, 0, "", "",
+"f3 0f 1a 18          \tbndcl  (%rax),%bnd3",},
+{{0xf3, 0x0f, 0x1a, 0x04, 0x01, }, 5, 0, "", "",
+"f3 0f 1a 04 01       \tbndcl  (%rcx,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1a 04 05 78 56 34 12 \tbndcl  0x12345678(,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x04, 0x08, }, 5, 0, "", "",
+"f3 0f 1a 04 08       \tbndcl  (%rax,%rcx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x04, 0xc8, }, 5, 0, "", "",
+"f3 0f 1a 04 c8       \tbndcl  (%rax,%rcx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x40, 0x12, }, 5, 0, "", "",
+"f3 0f 1a 40 12       \tbndcl  0x12(%rax),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x45, 0x12, }, 5, 0, "", "",
+"f3 0f 1a 45 12       \tbndcl  0x12(%rbp),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"f3 0f 1a 44 01 12    \tbndcl  0x12(%rcx,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"f3 0f 1a 44 05 12    \tbndcl  0x12(%rbp,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"f3 0f 1a 44 08 12    \tbndcl  0x12(%rax,%rcx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"f3 0f 1a 44 c8 12    \tbndcl  0x12(%rax,%rcx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f3 0f 1a 80 78 56 34 12 \tbndcl  0x12345678(%rax),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f3 0f 1a 85 78 56 34 12 \tbndcl  0x12345678(%rbp),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1a 84 01 78 56 34 12 \tbndcl  0x12345678(%rcx,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1a 84 05 78 56 34 12 \tbndcl  0x12345678(%rbp,%rax,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1a 84 08 78 56 34 12 \tbndcl  0x12345678(%rax,%rcx,1),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f3 0f 1a 84 c8 78 56 34 12 \tbndcl  0x12345678(%rax,%rcx,8),%bnd0",},
+{{0xf3, 0x0f, 0x1a, 0xc0, }, 4, 0, "", "",
+"f3 0f 1a c0          \tbndcl  %rax,%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x00, }, 4, 0, "", "",
+"f2 0f 1a 00          \tbndcu  (%rax),%bnd0",},
+{{0xf2, 0x41, 0x0f, 0x1a, 0x00, }, 5, 0, "", "",
+"f2 41 0f 1a 00       \tbndcu  (%r8),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1a 04 25 78 56 34 12 \tbndcu  0x12345678,%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x18, }, 4, 0, "", "",
+"f2 0f 1a 18          \tbndcu  (%rax),%bnd3",},
+{{0xf2, 0x0f, 0x1a, 0x04, 0x01, }, 5, 0, "", "",
+"f2 0f 1a 04 01       \tbndcu  (%rcx,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1a 04 05 78 56 34 12 \tbndcu  0x12345678(,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x04, 0x08, }, 5, 0, "", "",
+"f2 0f 1a 04 08       \tbndcu  (%rax,%rcx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x04, 0xc8, }, 5, 0, "", "",
+"f2 0f 1a 04 c8       \tbndcu  (%rax,%rcx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x40, 0x12, }, 5, 0, "", "",
+"f2 0f 1a 40 12       \tbndcu  0x12(%rax),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x45, 0x12, }, 5, 0, "", "",
+"f2 0f 1a 45 12       \tbndcu  0x12(%rbp),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"f2 0f 1a 44 01 12    \tbndcu  0x12(%rcx,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"f2 0f 1a 44 05 12    \tbndcu  0x12(%rbp,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"f2 0f 1a 44 08 12    \tbndcu  0x12(%rax,%rcx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"f2 0f 1a 44 c8 12    \tbndcu  0x12(%rax,%rcx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f2 0f 1a 80 78 56 34 12 \tbndcu  0x12345678(%rax),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f2 0f 1a 85 78 56 34 12 \tbndcu  0x12345678(%rbp),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1a 84 01 78 56 34 12 \tbndcu  0x12345678(%rcx,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1a 84 05 78 56 34 12 \tbndcu  0x12345678(%rbp,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1a 84 08 78 56 34 12 \tbndcu  0x12345678(%rax,%rcx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1a 84 c8 78 56 34 12 \tbndcu  0x12345678(%rax,%rcx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1a, 0xc0, }, 4, 0, "", "",
+"f2 0f 1a c0          \tbndcu  %rax,%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x00, }, 4, 0, "", "",
+"f2 0f 1b 00          \tbndcn  (%rax),%bnd0",},
+{{0xf2, 0x41, 0x0f, 0x1b, 0x00, }, 5, 0, "", "",
+"f2 41 0f 1b 00       \tbndcn  (%r8),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1b 04 25 78 56 34 12 \tbndcn  0x12345678,%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x18, }, 4, 0, "", "",
+"f2 0f 1b 18          \tbndcn  (%rax),%bnd3",},
+{{0xf2, 0x0f, 0x1b, 0x04, 0x01, }, 5, 0, "", "",
+"f2 0f 1b 04 01       \tbndcn  (%rcx,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1b 04 05 78 56 34 12 \tbndcn  0x12345678(,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x04, 0x08, }, 5, 0, "", "",
+"f2 0f 1b 04 08       \tbndcn  (%rax,%rcx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x04, 0xc8, }, 5, 0, "", "",
+"f2 0f 1b 04 c8       \tbndcn  (%rax,%rcx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x40, 0x12, }, 5, 0, "", "",
+"f2 0f 1b 40 12       \tbndcn  0x12(%rax),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x45, 0x12, }, 5, 0, "", "",
+"f2 0f 1b 45 12       \tbndcn  0x12(%rbp),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"f2 0f 1b 44 01 12    \tbndcn  0x12(%rcx,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"f2 0f 1b 44 05 12    \tbndcn  0x12(%rbp,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"f2 0f 1b 44 08 12    \tbndcn  0x12(%rax,%rcx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"f2 0f 1b 44 c8 12    \tbndcn  0x12(%rax,%rcx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f2 0f 1b 80 78 56 34 12 \tbndcn  0x12345678(%rax),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"f2 0f 1b 85 78 56 34 12 \tbndcn  0x12345678(%rbp),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1b 84 01 78 56 34 12 \tbndcn  0x12345678(%rcx,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1b 84 05 78 56 34 12 \tbndcn  0x12345678(%rbp,%rax,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1b 84 08 78 56 34 12 \tbndcn  0x12345678(%rax,%rcx,1),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"f2 0f 1b 84 c8 78 56 34 12 \tbndcn  0x12345678(%rax,%rcx,8),%bnd0",},
+{{0xf2, 0x0f, 0x1b, 0xc0, }, 4, 0, "", "",
+"f2 0f 1b c0          \tbndcn  %rax,%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x00, }, 4, 0, "", "",
+"66 0f 1a 00          \tbndmov (%rax),%bnd0",},
+{{0x66, 0x41, 0x0f, 0x1a, 0x00, }, 5, 0, "", "",
+"66 41 0f 1a 00       \tbndmov (%r8),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1a 04 25 78 56 34 12 \tbndmov 0x12345678,%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x18, }, 4, 0, "", "",
+"66 0f 1a 18          \tbndmov (%rax),%bnd3",},
+{{0x66, 0x0f, 0x1a, 0x04, 0x01, }, 5, 0, "", "",
+"66 0f 1a 04 01       \tbndmov (%rcx,%rax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1a 04 05 78 56 34 12 \tbndmov 0x12345678(,%rax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x04, 0x08, }, 5, 0, "", "",
+"66 0f 1a 04 08       \tbndmov (%rax,%rcx,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x04, 0xc8, }, 5, 0, "", "",
+"66 0f 1a 04 c8       \tbndmov (%rax,%rcx,8),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x40, 0x12, }, 5, 0, "", "",
+"66 0f 1a 40 12       \tbndmov 0x12(%rax),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x45, 0x12, }, 5, 0, "", "",
+"66 0f 1a 45 12       \tbndmov 0x12(%rbp),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"66 0f 1a 44 01 12    \tbndmov 0x12(%rcx,%rax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"66 0f 1a 44 05 12    \tbndmov 0x12(%rbp,%rax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"66 0f 1a 44 08 12    \tbndmov 0x12(%rax,%rcx,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"66 0f 1a 44 c8 12    \tbndmov 0x12(%rax,%rcx,8),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f 1a 80 78 56 34 12 \tbndmov 0x12345678(%rax),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f 1a 85 78 56 34 12 \tbndmov 0x12345678(%rbp),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1a 84 01 78 56 34 12 \tbndmov 0x12345678(%rcx,%rax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1a 84 05 78 56 34 12 \tbndmov 0x12345678(%rbp,%rax,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1a 84 08 78 56 34 12 \tbndmov 0x12345678(%rax,%rcx,1),%bnd0",},
+{{0x66, 0x0f, 0x1a, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1a 84 c8 78 56 34 12 \tbndmov 0x12345678(%rax,%rcx,8),%bnd0",},
+{{0x66, 0x0f, 0x1b, 0x00, }, 4, 0, "", "",
+"66 0f 1b 00          \tbndmov %bnd0,(%rax)",},
+{{0x66, 0x41, 0x0f, 0x1b, 0x00, }, 5, 0, "", "",
+"66 41 0f 1b 00       \tbndmov %bnd0,(%r8)",},
+{{0x66, 0x0f, 0x1b, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1b 04 25 78 56 34 12 \tbndmov %bnd0,0x12345678",},
+{{0x66, 0x0f, 0x1b, 0x18, }, 4, 0, "", "",
+"66 0f 1b 18          \tbndmov %bnd3,(%rax)",},
+{{0x66, 0x0f, 0x1b, 0x04, 0x01, }, 5, 0, "", "",
+"66 0f 1b 04 01       \tbndmov %bnd0,(%rcx,%rax,1)",},
+{{0x66, 0x0f, 0x1b, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1b 04 05 78 56 34 12 \tbndmov %bnd0,0x12345678(,%rax,1)",},
+{{0x66, 0x0f, 0x1b, 0x04, 0x08, }, 5, 0, "", "",
+"66 0f 1b 04 08       \tbndmov %bnd0,(%rax,%rcx,1)",},
+{{0x66, 0x0f, 0x1b, 0x04, 0xc8, }, 5, 0, "", "",
+"66 0f 1b 04 c8       \tbndmov %bnd0,(%rax,%rcx,8)",},
+{{0x66, 0x0f, 0x1b, 0x40, 0x12, }, 5, 0, "", "",
+"66 0f 1b 40 12       \tbndmov %bnd0,0x12(%rax)",},
+{{0x66, 0x0f, 0x1b, 0x45, 0x12, }, 5, 0, "", "",
+"66 0f 1b 45 12       \tbndmov %bnd0,0x12(%rbp)",},
+{{0x66, 0x0f, 0x1b, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"66 0f 1b 44 01 12    \tbndmov %bnd0,0x12(%rcx,%rax,1)",},
+{{0x66, 0x0f, 0x1b, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"66 0f 1b 44 05 12    \tbndmov %bnd0,0x12(%rbp,%rax,1)",},
+{{0x66, 0x0f, 0x1b, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"66 0f 1b 44 08 12    \tbndmov %bnd0,0x12(%rax,%rcx,1)",},
+{{0x66, 0x0f, 0x1b, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"66 0f 1b 44 c8 12    \tbndmov %bnd0,0x12(%rax,%rcx,8)",},
+{{0x66, 0x0f, 0x1b, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f 1b 80 78 56 34 12 \tbndmov %bnd0,0x12345678(%rax)",},
+{{0x66, 0x0f, 0x1b, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"66 0f 1b 85 78 56 34 12 \tbndmov %bnd0,0x12345678(%rbp)",},
+{{0x66, 0x0f, 0x1b, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1b 84 01 78 56 34 12 \tbndmov %bnd0,0x12345678(%rcx,%rax,1)",},
+{{0x66, 0x0f, 0x1b, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1b 84 05 78 56 34 12 \tbndmov %bnd0,0x12345678(%rbp,%rax,1)",},
+{{0x66, 0x0f, 0x1b, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1b 84 08 78 56 34 12 \tbndmov %bnd0,0x12345678(%rax,%rcx,1)",},
+{{0x66, 0x0f, 0x1b, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f 1b 84 c8 78 56 34 12 \tbndmov %bnd0,0x12345678(%rax,%rcx,8)",},
+{{0x66, 0x0f, 0x1a, 0xc8, }, 4, 0, "", "",
+"66 0f 1a c8          \tbndmov %bnd0,%bnd1",},
+{{0x66, 0x0f, 0x1a, 0xc1, }, 4, 0, "", "",
+"66 0f 1a c1          \tbndmov %bnd1,%bnd0",},
+{{0x0f, 0x1a, 0x00, }, 3, 0, "", "",
+"0f 1a 00             \tbndldx (%rax),%bnd0",},
+{{0x41, 0x0f, 0x1a, 0x00, }, 4, 0, "", "",
+"41 0f 1a 00          \tbndldx (%r8),%bnd0",},
+{{0x0f, 0x1a, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1a 04 25 78 56 34 12 \tbndldx 0x12345678,%bnd0",},
+{{0x0f, 0x1a, 0x18, }, 3, 0, "", "",
+"0f 1a 18             \tbndldx (%rax),%bnd3",},
+{{0x0f, 0x1a, 0x04, 0x01, }, 4, 0, "", "",
+"0f 1a 04 01          \tbndldx (%rcx,%rax,1),%bnd0",},
+{{0x0f, 0x1a, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1a 04 05 78 56 34 12 \tbndldx 0x12345678(,%rax,1),%bnd0",},
+{{0x0f, 0x1a, 0x04, 0x08, }, 4, 0, "", "",
+"0f 1a 04 08          \tbndldx (%rax,%rcx,1),%bnd0",},
+{{0x0f, 0x1a, 0x40, 0x12, }, 4, 0, "", "",
+"0f 1a 40 12          \tbndldx 0x12(%rax),%bnd0",},
+{{0x0f, 0x1a, 0x45, 0x12, }, 4, 0, "", "",
+"0f 1a 45 12          \tbndldx 0x12(%rbp),%bnd0",},
+{{0x0f, 0x1a, 0x44, 0x01, 0x12, }, 5, 0, "", "",
+"0f 1a 44 01 12       \tbndldx 0x12(%rcx,%rax,1),%bnd0",},
+{{0x0f, 0x1a, 0x44, 0x05, 0x12, }, 5, 0, "", "",
+"0f 1a 44 05 12       \tbndldx 0x12(%rbp,%rax,1),%bnd0",},
+{{0x0f, 0x1a, 0x44, 0x08, 0x12, }, 5, 0, "", "",
+"0f 1a 44 08 12       \tbndldx 0x12(%rax,%rcx,1),%bnd0",},
+{{0x0f, 0x1a, 0x80, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f 1a 80 78 56 34 12 \tbndldx 0x12345678(%rax),%bnd0",},
+{{0x0f, 0x1a, 0x85, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f 1a 85 78 56 34 12 \tbndldx 0x12345678(%rbp),%bnd0",},
+{{0x0f, 0x1a, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1a 84 01 78 56 34 12 \tbndldx 0x12345678(%rcx,%rax,1),%bnd0",},
+{{0x0f, 0x1a, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1a 84 05 78 56 34 12 \tbndldx 0x12345678(%rbp,%rax,1),%bnd0",},
+{{0x0f, 0x1a, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1a 84 08 78 56 34 12 \tbndldx 0x12345678(%rax,%rcx,1),%bnd0",},
+{{0x0f, 0x1b, 0x00, }, 3, 0, "", "",
+"0f 1b 00             \tbndstx %bnd0,(%rax)",},
+{{0x41, 0x0f, 0x1b, 0x00, }, 4, 0, "", "",
+"41 0f 1b 00          \tbndstx %bnd0,(%r8)",},
+{{0x0f, 0x1b, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1b 04 25 78 56 34 12 \tbndstx %bnd0,0x12345678",},
+{{0x0f, 0x1b, 0x18, }, 3, 0, "", "",
+"0f 1b 18             \tbndstx %bnd3,(%rax)",},
+{{0x0f, 0x1b, 0x04, 0x01, }, 4, 0, "", "",
+"0f 1b 04 01          \tbndstx %bnd0,(%rcx,%rax,1)",},
+{{0x0f, 0x1b, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1b 04 05 78 56 34 12 \tbndstx %bnd0,0x12345678(,%rax,1)",},
+{{0x0f, 0x1b, 0x04, 0x08, }, 4, 0, "", "",
+"0f 1b 04 08          \tbndstx %bnd0,(%rax,%rcx,1)",},
+{{0x0f, 0x1b, 0x40, 0x12, }, 4, 0, "", "",
+"0f 1b 40 12          \tbndstx %bnd0,0x12(%rax)",},
+{{0x0f, 0x1b, 0x45, 0x12, }, 4, 0, "", "",
+"0f 1b 45 12          \tbndstx %bnd0,0x12(%rbp)",},
+{{0x0f, 0x1b, 0x44, 0x01, 0x12, }, 5, 0, "", "",
+"0f 1b 44 01 12       \tbndstx %bnd0,0x12(%rcx,%rax,1)",},
+{{0x0f, 0x1b, 0x44, 0x05, 0x12, }, 5, 0, "", "",
+"0f 1b 44 05 12       \tbndstx %bnd0,0x12(%rbp,%rax,1)",},
+{{0x0f, 0x1b, 0x44, 0x08, 0x12, }, 5, 0, "", "",
+"0f 1b 44 08 12       \tbndstx %bnd0,0x12(%rax,%rcx,1)",},
+{{0x0f, 0x1b, 0x80, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f 1b 80 78 56 34 12 \tbndstx %bnd0,0x12345678(%rax)",},
+{{0x0f, 0x1b, 0x85, 0x78, 0x56, 0x34, 0x12, }, 7, 0, "", "",
+"0f 1b 85 78 56 34 12 \tbndstx %bnd0,0x12345678(%rbp)",},
+{{0x0f, 0x1b, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1b 84 01 78 56 34 12 \tbndstx %bnd0,0x12345678(%rcx,%rax,1)",},
+{{0x0f, 0x1b, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1b 84 05 78 56 34 12 \tbndstx %bnd0,0x12345678(%rbp,%rax,1)",},
+{{0x0f, 0x1b, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 1b 84 08 78 56 34 12 \tbndstx %bnd0,0x12345678(%rax,%rcx,1)",},
+{{0xf2, 0xe8, 0x00, 0x00, 0x00, 0x00, }, 6, 0, "call", "unconditional",
+"f2 e8 00 00 00 00    \tbnd callq 3f6 <main+0x3f6>",},
+{{0x67, 0xf2, 0xff, 0x10, }, 4, 0, "call", "indirect",
+"67 f2 ff 10          \tbnd callq *(%eax)",},
+{{0xf2, 0xc3, }, 2, 0, "ret", "indirect",
+"f2 c3                \tbnd retq ",},
+{{0xf2, 0xe9, 0x00, 0x00, 0x00, 0x00, }, 6, 0, "jmp", "unconditional",
+"f2 e9 00 00 00 00    \tbnd jmpq 402 <main+0x402>",},
+{{0xf2, 0xe9, 0x00, 0x00, 0x00, 0x00, }, 6, 0, "jmp", "unconditional",
+"f2 e9 00 00 00 00    \tbnd jmpq 408 <main+0x408>",},
+{{0x67, 0xf2, 0xff, 0x21, }, 4, 0, "jmp", "indirect",
+"67 f2 ff 21          \tbnd jmpq *(%ecx)",},
+{{0xf2, 0x0f, 0x85, 0x00, 0x00, 0x00, 0x00, }, 7, 0, "jcc", "conditional",
+"f2 0f 85 00 00 00 00 \tbnd jne 413 <main+0x413>",},
+{{0x0f, 0x3a, 0xcc, 0xc1, 0x00, }, 5, 0, "", "",
+"0f 3a cc c1 00       \tsha1rnds4 $0x0,%xmm1,%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0xd7, 0x91, }, 5, 0, "", "",
+"0f 3a cc d7 91       \tsha1rnds4 $0x91,%xmm7,%xmm2",},
+{{0x41, 0x0f, 0x3a, 0xcc, 0xc0, 0x91, }, 6, 0, "", "",
+"41 0f 3a cc c0 91    \tsha1rnds4 $0x91,%xmm8,%xmm0",},
+{{0x44, 0x0f, 0x3a, 0xcc, 0xc7, 0x91, }, 6, 0, "", "",
+"44 0f 3a cc c7 91    \tsha1rnds4 $0x91,%xmm7,%xmm8",},
+{{0x45, 0x0f, 0x3a, 0xcc, 0xc7, 0x91, }, 6, 0, "", "",
+"45 0f 3a cc c7 91    \tsha1rnds4 $0x91,%xmm15,%xmm8",},
+{{0x0f, 0x3a, 0xcc, 0x00, 0x91, }, 5, 0, "", "",
+"0f 3a cc 00 91       \tsha1rnds4 $0x91,(%rax),%xmm0",},
+{{0x41, 0x0f, 0x3a, 0xcc, 0x00, 0x91, }, 6, 0, "", "",
+"41 0f 3a cc 00 91    \tsha1rnds4 $0x91,(%r8),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, 0x91, }, 10, 0, "", "",
+"0f 3a cc 04 25 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678,%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x18, 0x91, }, 5, 0, "", "",
+"0f 3a cc 18 91       \tsha1rnds4 $0x91,(%rax),%xmm3",},
+{{0x0f, 0x3a, 0xcc, 0x04, 0x01, 0x91, }, 6, 0, "", "",
+"0f 3a cc 04 01 91    \tsha1rnds4 $0x91,(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, 0x91, }, 10, 0, "", "",
+"0f 3a cc 04 05 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(,%rax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x04, 0x08, 0x91, }, 6, 0, "", "",
+"0f 3a cc 04 08 91    \tsha1rnds4 $0x91,(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x04, 0xc8, 0x91, }, 6, 0, "", "",
+"0f 3a cc 04 c8 91    \tsha1rnds4 $0x91,(%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x40, 0x12, 0x91, }, 6, 0, "", "",
+"0f 3a cc 40 12 91    \tsha1rnds4 $0x91,0x12(%rax),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x45, 0x12, 0x91, }, 6, 0, "", "",
+"0f 3a cc 45 12 91    \tsha1rnds4 $0x91,0x12(%rbp),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x44, 0x01, 0x12, 0x91, }, 7, 0, "", "",
+"0f 3a cc 44 01 12 91 \tsha1rnds4 $0x91,0x12(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x44, 0x05, 0x12, 0x91, }, 7, 0, "", "",
+"0f 3a cc 44 05 12 91 \tsha1rnds4 $0x91,0x12(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x44, 0x08, 0x12, 0x91, }, 7, 0, "", "",
+"0f 3a cc 44 08 12 91 \tsha1rnds4 $0x91,0x12(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x44, 0xc8, 0x12, 0x91, }, 7, 0, "", "",
+"0f 3a cc 44 c8 12 91 \tsha1rnds4 $0x91,0x12(%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x80, 0x78, 0x56, 0x34, 0x12, 0x91, }, 9, 0, "", "",
+"0f 3a cc 80 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%rax),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x85, 0x78, 0x56, 0x34, 0x12, 0x91, }, 9, 0, "", "",
+"0f 3a cc 85 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%rbp),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, 0x91, }, 10, 0, "", "",
+"0f 3a cc 84 01 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, 0x91, }, 10, 0, "", "",
+"0f 3a cc 84 05 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, 0x91, }, 10, 0, "", "",
+"0f 3a cc 84 08 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x3a, 0xcc, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, 0x91, }, 10, 0, "", "",
+"0f 3a cc 84 c8 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%rax,%rcx,8),%xmm0",},
+{{0x44, 0x0f, 0x3a, 0xcc, 0xbc, 0xc8, 0x78, 0x56, 0x34, 0x12, 0x91, }, 11, 0, "", "",
+"44 0f 3a cc bc c8 78 56 34 12 91 \tsha1rnds4 $0x91,0x12345678(%rax,%rcx,8),%xmm15",},
+{{0x0f, 0x38, 0xc8, 0xc1, }, 4, 0, "", "",
+"0f 38 c8 c1          \tsha1nexte %xmm1,%xmm0",},
+{{0x0f, 0x38, 0xc8, 0xd7, }, 4, 0, "", "",
+"0f 38 c8 d7          \tsha1nexte %xmm7,%xmm2",},
+{{0x41, 0x0f, 0x38, 0xc8, 0xc0, }, 5, 0, "", "",
+"41 0f 38 c8 c0       \tsha1nexte %xmm8,%xmm0",},
+{{0x44, 0x0f, 0x38, 0xc8, 0xc7, }, 5, 0, "", "",
+"44 0f 38 c8 c7       \tsha1nexte %xmm7,%xmm8",},
+{{0x45, 0x0f, 0x38, 0xc8, 0xc7, }, 5, 0, "", "",
+"45 0f 38 c8 c7       \tsha1nexte %xmm15,%xmm8",},
+{{0x0f, 0x38, 0xc8, 0x00, }, 4, 0, "", "",
+"0f 38 c8 00          \tsha1nexte (%rax),%xmm0",},
+{{0x41, 0x0f, 0x38, 0xc8, 0x00, }, 5, 0, "", "",
+"41 0f 38 c8 00       \tsha1nexte (%r8),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c8 04 25 78 56 34 12 \tsha1nexte 0x12345678,%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x18, }, 4, 0, "", "",
+"0f 38 c8 18          \tsha1nexte (%rax),%xmm3",},
+{{0x0f, 0x38, 0xc8, 0x04, 0x01, }, 5, 0, "", "",
+"0f 38 c8 04 01       \tsha1nexte (%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c8 04 05 78 56 34 12 \tsha1nexte 0x12345678(,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x04, 0x08, }, 5, 0, "", "",
+"0f 38 c8 04 08       \tsha1nexte (%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x04, 0xc8, }, 5, 0, "", "",
+"0f 38 c8 04 c8       \tsha1nexte (%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x40, 0x12, }, 5, 0, "", "",
+"0f 38 c8 40 12       \tsha1nexte 0x12(%rax),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x45, 0x12, }, 5, 0, "", "",
+"0f 38 c8 45 12       \tsha1nexte 0x12(%rbp),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 c8 44 01 12    \tsha1nexte 0x12(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 c8 44 05 12    \tsha1nexte 0x12(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 c8 44 08 12    \tsha1nexte 0x12(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 c8 44 c8 12    \tsha1nexte 0x12(%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 c8 80 78 56 34 12 \tsha1nexte 0x12345678(%rax),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 c8 85 78 56 34 12 \tsha1nexte 0x12345678(%rbp),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c8 84 01 78 56 34 12 \tsha1nexte 0x12345678(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c8 84 05 78 56 34 12 \tsha1nexte 0x12345678(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c8 84 08 78 56 34 12 \tsha1nexte 0x12345678(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xc8, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c8 84 c8 78 56 34 12 \tsha1nexte 0x12345678(%rax,%rcx,8),%xmm0",},
+{{0x44, 0x0f, 0x38, 0xc8, 0xbc, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 10, 0, "", "",
+"44 0f 38 c8 bc c8 78 56 34 12 \tsha1nexte 0x12345678(%rax,%rcx,8),%xmm15",},
+{{0x0f, 0x38, 0xc9, 0xc1, }, 4, 0, "", "",
+"0f 38 c9 c1          \tsha1msg1 %xmm1,%xmm0",},
+{{0x0f, 0x38, 0xc9, 0xd7, }, 4, 0, "", "",
+"0f 38 c9 d7          \tsha1msg1 %xmm7,%xmm2",},
+{{0x41, 0x0f, 0x38, 0xc9, 0xc0, }, 5, 0, "", "",
+"41 0f 38 c9 c0       \tsha1msg1 %xmm8,%xmm0",},
+{{0x44, 0x0f, 0x38, 0xc9, 0xc7, }, 5, 0, "", "",
+"44 0f 38 c9 c7       \tsha1msg1 %xmm7,%xmm8",},
+{{0x45, 0x0f, 0x38, 0xc9, 0xc7, }, 5, 0, "", "",
+"45 0f 38 c9 c7       \tsha1msg1 %xmm15,%xmm8",},
+{{0x0f, 0x38, 0xc9, 0x00, }, 4, 0, "", "",
+"0f 38 c9 00          \tsha1msg1 (%rax),%xmm0",},
+{{0x41, 0x0f, 0x38, 0xc9, 0x00, }, 5, 0, "", "",
+"41 0f 38 c9 00       \tsha1msg1 (%r8),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c9 04 25 78 56 34 12 \tsha1msg1 0x12345678,%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x18, }, 4, 0, "", "",
+"0f 38 c9 18          \tsha1msg1 (%rax),%xmm3",},
+{{0x0f, 0x38, 0xc9, 0x04, 0x01, }, 5, 0, "", "",
+"0f 38 c9 04 01       \tsha1msg1 (%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c9 04 05 78 56 34 12 \tsha1msg1 0x12345678(,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x04, 0x08, }, 5, 0, "", "",
+"0f 38 c9 04 08       \tsha1msg1 (%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x04, 0xc8, }, 5, 0, "", "",
+"0f 38 c9 04 c8       \tsha1msg1 (%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x40, 0x12, }, 5, 0, "", "",
+"0f 38 c9 40 12       \tsha1msg1 0x12(%rax),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x45, 0x12, }, 5, 0, "", "",
+"0f 38 c9 45 12       \tsha1msg1 0x12(%rbp),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 c9 44 01 12    \tsha1msg1 0x12(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 c9 44 05 12    \tsha1msg1 0x12(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 c9 44 08 12    \tsha1msg1 0x12(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 c9 44 c8 12    \tsha1msg1 0x12(%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 c9 80 78 56 34 12 \tsha1msg1 0x12345678(%rax),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 c9 85 78 56 34 12 \tsha1msg1 0x12345678(%rbp),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c9 84 01 78 56 34 12 \tsha1msg1 0x12345678(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c9 84 05 78 56 34 12 \tsha1msg1 0x12345678(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c9 84 08 78 56 34 12 \tsha1msg1 0x12345678(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xc9, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 c9 84 c8 78 56 34 12 \tsha1msg1 0x12345678(%rax,%rcx,8),%xmm0",},
+{{0x44, 0x0f, 0x38, 0xc9, 0xbc, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 10, 0, "", "",
+"44 0f 38 c9 bc c8 78 56 34 12 \tsha1msg1 0x12345678(%rax,%rcx,8),%xmm15",},
+{{0x0f, 0x38, 0xca, 0xc1, }, 4, 0, "", "",
+"0f 38 ca c1          \tsha1msg2 %xmm1,%xmm0",},
+{{0x0f, 0x38, 0xca, 0xd7, }, 4, 0, "", "",
+"0f 38 ca d7          \tsha1msg2 %xmm7,%xmm2",},
+{{0x41, 0x0f, 0x38, 0xca, 0xc0, }, 5, 0, "", "",
+"41 0f 38 ca c0       \tsha1msg2 %xmm8,%xmm0",},
+{{0x44, 0x0f, 0x38, 0xca, 0xc7, }, 5, 0, "", "",
+"44 0f 38 ca c7       \tsha1msg2 %xmm7,%xmm8",},
+{{0x45, 0x0f, 0x38, 0xca, 0xc7, }, 5, 0, "", "",
+"45 0f 38 ca c7       \tsha1msg2 %xmm15,%xmm8",},
+{{0x0f, 0x38, 0xca, 0x00, }, 4, 0, "", "",
+"0f 38 ca 00          \tsha1msg2 (%rax),%xmm0",},
+{{0x41, 0x0f, 0x38, 0xca, 0x00, }, 5, 0, "", "",
+"41 0f 38 ca 00       \tsha1msg2 (%r8),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 ca 04 25 78 56 34 12 \tsha1msg2 0x12345678,%xmm0",},
+{{0x0f, 0x38, 0xca, 0x18, }, 4, 0, "", "",
+"0f 38 ca 18          \tsha1msg2 (%rax),%xmm3",},
+{{0x0f, 0x38, 0xca, 0x04, 0x01, }, 5, 0, "", "",
+"0f 38 ca 04 01       \tsha1msg2 (%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 ca 04 05 78 56 34 12 \tsha1msg2 0x12345678(,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x04, 0x08, }, 5, 0, "", "",
+"0f 38 ca 04 08       \tsha1msg2 (%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x04, 0xc8, }, 5, 0, "", "",
+"0f 38 ca 04 c8       \tsha1msg2 (%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x40, 0x12, }, 5, 0, "", "",
+"0f 38 ca 40 12       \tsha1msg2 0x12(%rax),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x45, 0x12, }, 5, 0, "", "",
+"0f 38 ca 45 12       \tsha1msg2 0x12(%rbp),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 ca 44 01 12    \tsha1msg2 0x12(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 ca 44 05 12    \tsha1msg2 0x12(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 ca 44 08 12    \tsha1msg2 0x12(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 ca 44 c8 12    \tsha1msg2 0x12(%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 ca 80 78 56 34 12 \tsha1msg2 0x12345678(%rax),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 ca 85 78 56 34 12 \tsha1msg2 0x12345678(%rbp),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 ca 84 01 78 56 34 12 \tsha1msg2 0x12345678(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 ca 84 05 78 56 34 12 \tsha1msg2 0x12345678(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 ca 84 08 78 56 34 12 \tsha1msg2 0x12345678(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xca, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 ca 84 c8 78 56 34 12 \tsha1msg2 0x12345678(%rax,%rcx,8),%xmm0",},
+{{0x44, 0x0f, 0x38, 0xca, 0xbc, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 10, 0, "", "",
+"44 0f 38 ca bc c8 78 56 34 12 \tsha1msg2 0x12345678(%rax,%rcx,8),%xmm15",},
+{{0x0f, 0x38, 0xcb, 0xcc, }, 4, 0, "", "",
+"0f 38 cb cc          \tsha256rnds2 %xmm0,%xmm4,%xmm1",},
+{{0x0f, 0x38, 0xcb, 0xd7, }, 4, 0, "", "",
+"0f 38 cb d7          \tsha256rnds2 %xmm0,%xmm7,%xmm2",},
+{{0x41, 0x0f, 0x38, 0xcb, 0xc8, }, 5, 0, "", "",
+"41 0f 38 cb c8       \tsha256rnds2 %xmm0,%xmm8,%xmm1",},
+{{0x44, 0x0f, 0x38, 0xcb, 0xc7, }, 5, 0, "", "",
+"44 0f 38 cb c7       \tsha256rnds2 %xmm0,%xmm7,%xmm8",},
+{{0x45, 0x0f, 0x38, 0xcb, 0xc7, }, 5, 0, "", "",
+"45 0f 38 cb c7       \tsha256rnds2 %xmm0,%xmm15,%xmm8",},
+{{0x0f, 0x38, 0xcb, 0x08, }, 4, 0, "", "",
+"0f 38 cb 08          \tsha256rnds2 %xmm0,(%rax),%xmm1",},
+{{0x41, 0x0f, 0x38, 0xcb, 0x08, }, 5, 0, "", "",
+"41 0f 38 cb 08       \tsha256rnds2 %xmm0,(%r8),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x0c, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cb 0c 25 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678,%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x18, }, 4, 0, "", "",
+"0f 38 cb 18          \tsha256rnds2 %xmm0,(%rax),%xmm3",},
+{{0x0f, 0x38, 0xcb, 0x0c, 0x01, }, 5, 0, "", "",
+"0f 38 cb 0c 01       \tsha256rnds2 %xmm0,(%rcx,%rax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x0c, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cb 0c 05 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(,%rax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x0c, 0x08, }, 5, 0, "", "",
+"0f 38 cb 0c 08       \tsha256rnds2 %xmm0,(%rax,%rcx,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x0c, 0xc8, }, 5, 0, "", "",
+"0f 38 cb 0c c8       \tsha256rnds2 %xmm0,(%rax,%rcx,8),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x48, 0x12, }, 5, 0, "", "",
+"0f 38 cb 48 12       \tsha256rnds2 %xmm0,0x12(%rax),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x4d, 0x12, }, 5, 0, "", "",
+"0f 38 cb 4d 12       \tsha256rnds2 %xmm0,0x12(%rbp),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x4c, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 cb 4c 01 12    \tsha256rnds2 %xmm0,0x12(%rcx,%rax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x4c, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 cb 4c 05 12    \tsha256rnds2 %xmm0,0x12(%rbp,%rax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x4c, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 cb 4c 08 12    \tsha256rnds2 %xmm0,0x12(%rax,%rcx,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x4c, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 cb 4c c8 12    \tsha256rnds2 %xmm0,0x12(%rax,%rcx,8),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x88, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cb 88 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%rax),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x8d, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cb 8d 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%rbp),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x8c, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cb 8c 01 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%rcx,%rax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x8c, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cb 8c 05 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%rbp,%rax,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x8c, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cb 8c 08 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%rax,%rcx,1),%xmm1",},
+{{0x0f, 0x38, 0xcb, 0x8c, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cb 8c c8 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%rax,%rcx,8),%xmm1",},
+{{0x44, 0x0f, 0x38, 0xcb, 0xbc, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 10, 0, "", "",
+"44 0f 38 cb bc c8 78 56 34 12 \tsha256rnds2 %xmm0,0x12345678(%rax,%rcx,8),%xmm15",},
+{{0x0f, 0x38, 0xcc, 0xc1, }, 4, 0, "", "",
+"0f 38 cc c1          \tsha256msg1 %xmm1,%xmm0",},
+{{0x0f, 0x38, 0xcc, 0xd7, }, 4, 0, "", "",
+"0f 38 cc d7          \tsha256msg1 %xmm7,%xmm2",},
+{{0x41, 0x0f, 0x38, 0xcc, 0xc0, }, 5, 0, "", "",
+"41 0f 38 cc c0       \tsha256msg1 %xmm8,%xmm0",},
+{{0x44, 0x0f, 0x38, 0xcc, 0xc7, }, 5, 0, "", "",
+"44 0f 38 cc c7       \tsha256msg1 %xmm7,%xmm8",},
+{{0x45, 0x0f, 0x38, 0xcc, 0xc7, }, 5, 0, "", "",
+"45 0f 38 cc c7       \tsha256msg1 %xmm15,%xmm8",},
+{{0x0f, 0x38, 0xcc, 0x00, }, 4, 0, "", "",
+"0f 38 cc 00          \tsha256msg1 (%rax),%xmm0",},
+{{0x41, 0x0f, 0x38, 0xcc, 0x00, }, 5, 0, "", "",
+"41 0f 38 cc 00       \tsha256msg1 (%r8),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cc 04 25 78 56 34 12 \tsha256msg1 0x12345678,%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x18, }, 4, 0, "", "",
+"0f 38 cc 18          \tsha256msg1 (%rax),%xmm3",},
+{{0x0f, 0x38, 0xcc, 0x04, 0x01, }, 5, 0, "", "",
+"0f 38 cc 04 01       \tsha256msg1 (%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cc 04 05 78 56 34 12 \tsha256msg1 0x12345678(,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x04, 0x08, }, 5, 0, "", "",
+"0f 38 cc 04 08       \tsha256msg1 (%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x04, 0xc8, }, 5, 0, "", "",
+"0f 38 cc 04 c8       \tsha256msg1 (%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x40, 0x12, }, 5, 0, "", "",
+"0f 38 cc 40 12       \tsha256msg1 0x12(%rax),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x45, 0x12, }, 5, 0, "", "",
+"0f 38 cc 45 12       \tsha256msg1 0x12(%rbp),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 cc 44 01 12    \tsha256msg1 0x12(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 cc 44 05 12    \tsha256msg1 0x12(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 cc 44 08 12    \tsha256msg1 0x12(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 cc 44 c8 12    \tsha256msg1 0x12(%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cc 80 78 56 34 12 \tsha256msg1 0x12345678(%rax),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cc 85 78 56 34 12 \tsha256msg1 0x12345678(%rbp),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cc 84 01 78 56 34 12 \tsha256msg1 0x12345678(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cc 84 05 78 56 34 12 \tsha256msg1 0x12345678(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cc 84 08 78 56 34 12 \tsha256msg1 0x12345678(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xcc, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cc 84 c8 78 56 34 12 \tsha256msg1 0x12345678(%rax,%rcx,8),%xmm0",},
+{{0x44, 0x0f, 0x38, 0xcc, 0xbc, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 10, 0, "", "",
+"44 0f 38 cc bc c8 78 56 34 12 \tsha256msg1 0x12345678(%rax,%rcx,8),%xmm15",},
+{{0x0f, 0x38, 0xcd, 0xc1, }, 4, 0, "", "",
+"0f 38 cd c1          \tsha256msg2 %xmm1,%xmm0",},
+{{0x0f, 0x38, 0xcd, 0xd7, }, 4, 0, "", "",
+"0f 38 cd d7          \tsha256msg2 %xmm7,%xmm2",},
+{{0x41, 0x0f, 0x38, 0xcd, 0xc0, }, 5, 0, "", "",
+"41 0f 38 cd c0       \tsha256msg2 %xmm8,%xmm0",},
+{{0x44, 0x0f, 0x38, 0xcd, 0xc7, }, 5, 0, "", "",
+"44 0f 38 cd c7       \tsha256msg2 %xmm7,%xmm8",},
+{{0x45, 0x0f, 0x38, 0xcd, 0xc7, }, 5, 0, "", "",
+"45 0f 38 cd c7       \tsha256msg2 %xmm15,%xmm8",},
+{{0x0f, 0x38, 0xcd, 0x00, }, 4, 0, "", "",
+"0f 38 cd 00          \tsha256msg2 (%rax),%xmm0",},
+{{0x41, 0x0f, 0x38, 0xcd, 0x00, }, 5, 0, "", "",
+"41 0f 38 cd 00       \tsha256msg2 (%r8),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x04, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cd 04 25 78 56 34 12 \tsha256msg2 0x12345678,%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x18, }, 4, 0, "", "",
+"0f 38 cd 18          \tsha256msg2 (%rax),%xmm3",},
+{{0x0f, 0x38, 0xcd, 0x04, 0x01, }, 5, 0, "", "",
+"0f 38 cd 04 01       \tsha256msg2 (%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x04, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cd 04 05 78 56 34 12 \tsha256msg2 0x12345678(,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x04, 0x08, }, 5, 0, "", "",
+"0f 38 cd 04 08       \tsha256msg2 (%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x04, 0xc8, }, 5, 0, "", "",
+"0f 38 cd 04 c8       \tsha256msg2 (%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x40, 0x12, }, 5, 0, "", "",
+"0f 38 cd 40 12       \tsha256msg2 0x12(%rax),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x45, 0x12, }, 5, 0, "", "",
+"0f 38 cd 45 12       \tsha256msg2 0x12(%rbp),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x44, 0x01, 0x12, }, 6, 0, "", "",
+"0f 38 cd 44 01 12    \tsha256msg2 0x12(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x44, 0x05, 0x12, }, 6, 0, "", "",
+"0f 38 cd 44 05 12    \tsha256msg2 0x12(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x44, 0x08, 0x12, }, 6, 0, "", "",
+"0f 38 cd 44 08 12    \tsha256msg2 0x12(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x44, 0xc8, 0x12, }, 6, 0, "", "",
+"0f 38 cd 44 c8 12    \tsha256msg2 0x12(%rax,%rcx,8),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x80, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cd 80 78 56 34 12 \tsha256msg2 0x12345678(%rax),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x85, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f 38 cd 85 78 56 34 12 \tsha256msg2 0x12345678(%rbp),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x84, 0x01, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cd 84 01 78 56 34 12 \tsha256msg2 0x12345678(%rcx,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x84, 0x05, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cd 84 05 78 56 34 12 \tsha256msg2 0x12345678(%rbp,%rax,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x84, 0x08, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cd 84 08 78 56 34 12 \tsha256msg2 0x12345678(%rax,%rcx,1),%xmm0",},
+{{0x0f, 0x38, 0xcd, 0x84, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"0f 38 cd 84 c8 78 56 34 12 \tsha256msg2 0x12345678(%rax,%rcx,8),%xmm0",},
+{{0x44, 0x0f, 0x38, 0xcd, 0xbc, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 10, 0, "", "",
+"44 0f 38 cd bc c8 78 56 34 12 \tsha256msg2 0x12345678(%rax,%rcx,8),%xmm15",},
+{{0x66, 0x0f, 0xae, 0x38, }, 4, 0, "", "",
+"66 0f ae 38          \tclflushopt (%rax)",},
+{{0x66, 0x41, 0x0f, 0xae, 0x38, }, 5, 0, "", "",
+"66 41 0f ae 38       \tclflushopt (%r8)",},
+{{0x66, 0x0f, 0xae, 0x3c, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f ae 3c 25 78 56 34 12 \tclflushopt 0x12345678",},
+{{0x66, 0x0f, 0xae, 0xbc, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f ae bc c8 78 56 34 12 \tclflushopt 0x12345678(%rax,%rcx,8)",},
+{{0x66, 0x41, 0x0f, 0xae, 0xbc, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 10, 0, "", "",
+"66 41 0f ae bc c8 78 56 34 12 \tclflushopt 0x12345678(%r8,%rcx,8)",},
+{{0x0f, 0xae, 0x38, }, 3, 0, "", "",
+"0f ae 38             \tclflush (%rax)",},
+{{0x41, 0x0f, 0xae, 0x38, }, 4, 0, "", "",
+"41 0f ae 38          \tclflush (%r8)",},
+{{0x0f, 0xae, 0xf8, }, 3, 0, "", "",
+"0f ae f8             \tsfence ",},
+{{0x66, 0x0f, 0xae, 0x30, }, 4, 0, "", "",
+"66 0f ae 30          \tclwb   (%rax)",},
+{{0x66, 0x41, 0x0f, 0xae, 0x30, }, 5, 0, "", "",
+"66 41 0f ae 30       \tclwb   (%r8)",},
+{{0x66, 0x0f, 0xae, 0x34, 0x25, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f ae 34 25 78 56 34 12 \tclwb   0x12345678",},
+{{0x66, 0x0f, 0xae, 0xb4, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"66 0f ae b4 c8 78 56 34 12 \tclwb   0x12345678(%rax,%rcx,8)",},
+{{0x66, 0x41, 0x0f, 0xae, 0xb4, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 10, 0, "", "",
+"66 41 0f ae b4 c8 78 56 34 12 \tclwb   0x12345678(%r8,%rcx,8)",},
+{{0x0f, 0xae, 0x30, }, 3, 0, "", "",
+"0f ae 30             \txsaveopt (%rax)",},
+{{0x41, 0x0f, 0xae, 0x30, }, 4, 0, "", "",
+"41 0f ae 30          \txsaveopt (%r8)",},
+{{0x0f, 0xae, 0xf0, }, 3, 0, "", "",
+"0f ae f0             \tmfence ",},
+{{0x0f, 0xc7, 0x20, }, 3, 0, "", "",
+"0f c7 20             \txsavec (%rax)",},
+{{0x41, 0x0f, 0xc7, 0x20, }, 4, 0, "", "",
+"41 0f c7 20          \txsavec (%r8)",},
+{{0x0f, 0xc7, 0x24, 0x25, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f c7 24 25 78 56 34 12 \txsavec 0x12345678",},
+{{0x0f, 0xc7, 0xa4, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f c7 a4 c8 78 56 34 12 \txsavec 0x12345678(%rax,%rcx,8)",},
+{{0x41, 0x0f, 0xc7, 0xa4, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"41 0f c7 a4 c8 78 56 34 12 \txsavec 0x12345678(%r8,%rcx,8)",},
+{{0x0f, 0xc7, 0x28, }, 3, 0, "", "",
+"0f c7 28             \txsaves (%rax)",},
+{{0x41, 0x0f, 0xc7, 0x28, }, 4, 0, "", "",
+"41 0f c7 28          \txsaves (%r8)",},
+{{0x0f, 0xc7, 0x2c, 0x25, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f c7 2c 25 78 56 34 12 \txsaves 0x12345678",},
+{{0x0f, 0xc7, 0xac, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f c7 ac c8 78 56 34 12 \txsaves 0x12345678(%rax,%rcx,8)",},
+{{0x41, 0x0f, 0xc7, 0xac, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"41 0f c7 ac c8 78 56 34 12 \txsaves 0x12345678(%r8,%rcx,8)",},
+{{0x0f, 0xc7, 0x18, }, 3, 0, "", "",
+"0f c7 18             \txrstors (%rax)",},
+{{0x41, 0x0f, 0xc7, 0x18, }, 4, 0, "", "",
+"41 0f c7 18          \txrstors (%r8)",},
+{{0x0f, 0xc7, 0x1c, 0x25, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f c7 1c 25 78 56 34 12 \txrstors 0x12345678",},
+{{0x0f, 0xc7, 0x9c, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 8, 0, "", "",
+"0f c7 9c c8 78 56 34 12 \txrstors 0x12345678(%rax,%rcx,8)",},
+{{0x41, 0x0f, 0xc7, 0x9c, 0xc8, 0x78, 0x56, 0x34, 0x12, }, 9, 0, "", "",
+"41 0f c7 9c c8 78 56 34 12 \txrstors 0x12345678(%r8,%rcx,8)",},
+{{0x66, 0x0f, 0xae, 0xf8, }, 4, 0, "", "",
+"66 0f ae f8          \tpcommit ",},
diff --git a/tools/perf/arch/x86/tests/insn-x86-dat-src.c b/tools/perf/arch/x86/tests/insn-x86-dat-src.c
new file mode 100644 (file)
index 0000000..41b1b1c
--- /dev/null
@@ -0,0 +1,877 @@
+/*
+ * This file contains instructions for testing by the test titled:
+ *
+ *         "Test x86 instruction decoder - new instructions"
+ *
+ * Note that the 'Expecting' comment lines are consumed by the
+ * gen-insn-x86-dat.awk script and have the format:
+ *
+ *         Expecting: <op> <branch> <rel>
+ *
+ * If this file is changed, remember to run the gen-insn-x86-dat.sh
+ * script and commit the result.
+ *
+ * Refer to insn-x86.c for more details.
+ */
+
+int main(void)
+{
+       /* Following line is a marker for the awk script - do not change */
+       asm volatile("rdtsc"); /* Start here */
+
+#ifdef __x86_64__
+
+       /* bndmk m64, bnd */
+
+       asm volatile("bndmk (%rax), %bnd0");
+       asm volatile("bndmk (%r8), %bnd0");
+       asm volatile("bndmk (0x12345678), %bnd0");
+       asm volatile("bndmk (%rax), %bnd3");
+       asm volatile("bndmk (%rcx,%rax,1), %bnd0");
+       asm volatile("bndmk 0x12345678(,%rax,1), %bnd0");
+       asm volatile("bndmk (%rax,%rcx,1), %bnd0");
+       asm volatile("bndmk (%rax,%rcx,8), %bnd0");
+       asm volatile("bndmk 0x12(%rax), %bnd0");
+       asm volatile("bndmk 0x12(%rbp), %bnd0");
+       asm volatile("bndmk 0x12(%rcx,%rax,1), %bnd0");
+       asm volatile("bndmk 0x12(%rbp,%rax,1), %bnd0");
+       asm volatile("bndmk 0x12(%rax,%rcx,1), %bnd0");
+       asm volatile("bndmk 0x12(%rax,%rcx,8), %bnd0");
+       asm volatile("bndmk 0x12345678(%rax), %bnd0");
+       asm volatile("bndmk 0x12345678(%rbp), %bnd0");
+       asm volatile("bndmk 0x12345678(%rcx,%rax,1), %bnd0");
+       asm volatile("bndmk 0x12345678(%rbp,%rax,1), %bnd0");
+       asm volatile("bndmk 0x12345678(%rax,%rcx,1), %bnd0");
+       asm volatile("bndmk 0x12345678(%rax,%rcx,8), %bnd0");
+
+       /* bndcl r/m64, bnd */
+
+       asm volatile("bndcl (%rax), %bnd0");
+       asm volatile("bndcl (%r8), %bnd0");
+       asm volatile("bndcl (0x12345678), %bnd0");
+       asm volatile("bndcl (%rax), %bnd3");
+       asm volatile("bndcl (%rcx,%rax,1), %bnd0");
+       asm volatile("bndcl 0x12345678(,%rax,1), %bnd0");
+       asm volatile("bndcl (%rax,%rcx,1), %bnd0");
+       asm volatile("bndcl (%rax,%rcx,8), %bnd0");
+       asm volatile("bndcl 0x12(%rax), %bnd0");
+       asm volatile("bndcl 0x12(%rbp), %bnd0");
+       asm volatile("bndcl 0x12(%rcx,%rax,1), %bnd0");
+       asm volatile("bndcl 0x12(%rbp,%rax,1), %bnd0");
+       asm volatile("bndcl 0x12(%rax,%rcx,1), %bnd0");
+       asm volatile("bndcl 0x12(%rax,%rcx,8), %bnd0");
+       asm volatile("bndcl 0x12345678(%rax), %bnd0");
+       asm volatile("bndcl 0x12345678(%rbp), %bnd0");
+       asm volatile("bndcl 0x12345678(%rcx,%rax,1), %bnd0");
+       asm volatile("bndcl 0x12345678(%rbp,%rax,1), %bnd0");
+       asm volatile("bndcl 0x12345678(%rax,%rcx,1), %bnd0");
+       asm volatile("bndcl 0x12345678(%rax,%rcx,8), %bnd0");
+       asm volatile("bndcl %rax, %bnd0");
+
+       /* bndcu r/m64, bnd */
+
+       asm volatile("bndcu (%rax), %bnd0");
+       asm volatile("bndcu (%r8), %bnd0");
+       asm volatile("bndcu (0x12345678), %bnd0");
+       asm volatile("bndcu (%rax), %bnd3");
+       asm volatile("bndcu (%rcx,%rax,1), %bnd0");
+       asm volatile("bndcu 0x12345678(,%rax,1), %bnd0");
+       asm volatile("bndcu (%rax,%rcx,1), %bnd0");
+       asm volatile("bndcu (%rax,%rcx,8), %bnd0");
+       asm volatile("bndcu 0x12(%rax), %bnd0");
+       asm volatile("bndcu 0x12(%rbp), %bnd0");
+       asm volatile("bndcu 0x12(%rcx,%rax,1), %bnd0");
+       asm volatile("bndcu 0x12(%rbp,%rax,1), %bnd0");
+       asm volatile("bndcu 0x12(%rax,%rcx,1), %bnd0");
+       asm volatile("bndcu 0x12(%rax,%rcx,8), %bnd0");
+       asm volatile("bndcu 0x12345678(%rax), %bnd0");
+       asm volatile("bndcu 0x12345678(%rbp), %bnd0");
+       asm volatile("bndcu 0x12345678(%rcx,%rax,1), %bnd0");
+       asm volatile("bndcu 0x12345678(%rbp,%rax,1), %bnd0");
+       asm volatile("bndcu 0x12345678(%rax,%rcx,1), %bnd0");
+       asm volatile("bndcu 0x12345678(%rax,%rcx,8), %bnd0");
+       asm volatile("bndcu %rax, %bnd0");
+
+       /* bndcn r/m64, bnd */
+
+       asm volatile("bndcn (%rax), %bnd0");
+       asm volatile("bndcn (%r8), %bnd0");
+       asm volatile("bndcn (0x12345678), %bnd0");
+       asm volatile("bndcn (%rax), %bnd3");
+       asm volatile("bndcn (%rcx,%rax,1), %bnd0");
+       asm volatile("bndcn 0x12345678(,%rax,1), %bnd0");
+       asm volatile("bndcn (%rax,%rcx,1), %bnd0");
+       asm volatile("bndcn (%rax,%rcx,8), %bnd0");
+       asm volatile("bndcn 0x12(%rax), %bnd0");
+       asm volatile("bndcn 0x12(%rbp), %bnd0");
+       asm volatile("bndcn 0x12(%rcx,%rax,1), %bnd0");
+       asm volatile("bndcn 0x12(%rbp,%rax,1), %bnd0");
+       asm volatile("bndcn 0x12(%rax,%rcx,1), %bnd0");
+       asm volatile("bndcn 0x12(%rax,%rcx,8), %bnd0");
+       asm volatile("bndcn 0x12345678(%rax), %bnd0");
+       asm volatile("bndcn 0x12345678(%rbp), %bnd0");
+       asm volatile("bndcn 0x12345678(%rcx,%rax,1), %bnd0");
+       asm volatile("bndcn 0x12345678(%rbp,%rax,1), %bnd0");
+       asm volatile("bndcn 0x12345678(%rax,%rcx,1), %bnd0");
+       asm volatile("bndcn 0x12345678(%rax,%rcx,8), %bnd0");
+       asm volatile("bndcn %rax, %bnd0");
+
+       /* bndmov m128, bnd */
+
+       asm volatile("bndmov (%rax), %bnd0");
+       asm volatile("bndmov (%r8), %bnd0");
+       asm volatile("bndmov (0x12345678), %bnd0");
+       asm volatile("bndmov (%rax), %bnd3");
+       asm volatile("bndmov (%rcx,%rax,1), %bnd0");
+       asm volatile("bndmov 0x12345678(,%rax,1), %bnd0");
+       asm volatile("bndmov (%rax,%rcx,1), %bnd0");
+       asm volatile("bndmov (%rax,%rcx,8), %bnd0");
+       asm volatile("bndmov 0x12(%rax), %bnd0");
+       asm volatile("bndmov 0x12(%rbp), %bnd0");
+       asm volatile("bndmov 0x12(%rcx,%rax,1), %bnd0");
+       asm volatile("bndmov 0x12(%rbp,%rax,1), %bnd0");
+       asm volatile("bndmov 0x12(%rax,%rcx,1), %bnd0");
+       asm volatile("bndmov 0x12(%rax,%rcx,8), %bnd0");
+       asm volatile("bndmov 0x12345678(%rax), %bnd0");
+       asm volatile("bndmov 0x12345678(%rbp), %bnd0");
+       asm volatile("bndmov 0x12345678(%rcx,%rax,1), %bnd0");
+       asm volatile("bndmov 0x12345678(%rbp,%rax,1), %bnd0");
+       asm volatile("bndmov 0x12345678(%rax,%rcx,1), %bnd0");
+       asm volatile("bndmov 0x12345678(%rax,%rcx,8), %bnd0");
+
+       /* bndmov bnd, m128 */
+
+       asm volatile("bndmov %bnd0, (%rax)");
+       asm volatile("bndmov %bnd0, (%r8)");
+       asm volatile("bndmov %bnd0, (0x12345678)");
+       asm volatile("bndmov %bnd3, (%rax)");
+       asm volatile("bndmov %bnd0, (%rcx,%rax,1)");
+       asm volatile("bndmov %bnd0, 0x12345678(,%rax,1)");
+       asm volatile("bndmov %bnd0, (%rax,%rcx,1)");
+       asm volatile("bndmov %bnd0, (%rax,%rcx,8)");
+       asm volatile("bndmov %bnd0, 0x12(%rax)");
+       asm volatile("bndmov %bnd0, 0x12(%rbp)");
+       asm volatile("bndmov %bnd0, 0x12(%rcx,%rax,1)");
+       asm volatile("bndmov %bnd0, 0x12(%rbp,%rax,1)");
+       asm volatile("bndmov %bnd0, 0x12(%rax,%rcx,1)");
+       asm volatile("bndmov %bnd0, 0x12(%rax,%rcx,8)");
+       asm volatile("bndmov %bnd0, 0x12345678(%rax)");
+       asm volatile("bndmov %bnd0, 0x12345678(%rbp)");
+       asm volatile("bndmov %bnd0, 0x12345678(%rcx,%rax,1)");
+       asm volatile("bndmov %bnd0, 0x12345678(%rbp,%rax,1)");
+       asm volatile("bndmov %bnd0, 0x12345678(%rax,%rcx,1)");
+       asm volatile("bndmov %bnd0, 0x12345678(%rax,%rcx,8)");
+
+       /* bndmov bnd2, bnd1 */
+
+       asm volatile("bndmov %bnd0, %bnd1");
+       asm volatile("bndmov %bnd1, %bnd0");
+
+       /* bndldx mib, bnd */
+
+       asm volatile("bndldx (%rax), %bnd0");
+       asm volatile("bndldx (%r8), %bnd0");
+       asm volatile("bndldx (0x12345678), %bnd0");
+       asm volatile("bndldx (%rax), %bnd3");
+       asm volatile("bndldx (%rcx,%rax,1), %bnd0");
+       asm volatile("bndldx 0x12345678(,%rax,1), %bnd0");
+       asm volatile("bndldx (%rax,%rcx,1), %bnd0");
+       asm volatile("bndldx 0x12(%rax), %bnd0");
+       asm volatile("bndldx 0x12(%rbp), %bnd0");
+       asm volatile("bndldx 0x12(%rcx,%rax,1), %bnd0");
+       asm volatile("bndldx 0x12(%rbp,%rax,1), %bnd0");
+       asm volatile("bndldx 0x12(%rax,%rcx,1), %bnd0");
+       asm volatile("bndldx 0x12345678(%rax), %bnd0");
+       asm volatile("bndldx 0x12345678(%rbp), %bnd0");
+       asm volatile("bndldx 0x12345678(%rcx,%rax,1), %bnd0");
+       asm volatile("bndldx 0x12345678(%rbp,%rax,1), %bnd0");
+       asm volatile("bndldx 0x12345678(%rax,%rcx,1), %bnd0");
+
+       /* bndstx bnd, mib */
+
+       asm volatile("bndstx %bnd0, (%rax)");
+       asm volatile("bndstx %bnd0, (%r8)");
+       asm volatile("bndstx %bnd0, (0x12345678)");
+       asm volatile("bndstx %bnd3, (%rax)");
+       asm volatile("bndstx %bnd0, (%rcx,%rax,1)");
+       asm volatile("bndstx %bnd0, 0x12345678(,%rax,1)");
+       asm volatile("bndstx %bnd0, (%rax,%rcx,1)");
+       asm volatile("bndstx %bnd0, 0x12(%rax)");
+       asm volatile("bndstx %bnd0, 0x12(%rbp)");
+       asm volatile("bndstx %bnd0, 0x12(%rcx,%rax,1)");
+       asm volatile("bndstx %bnd0, 0x12(%rbp,%rax,1)");
+       asm volatile("bndstx %bnd0, 0x12(%rax,%rcx,1)");
+       asm volatile("bndstx %bnd0, 0x12345678(%rax)");
+       asm volatile("bndstx %bnd0, 0x12345678(%rbp)");
+       asm volatile("bndstx %bnd0, 0x12345678(%rcx,%rax,1)");
+       asm volatile("bndstx %bnd0, 0x12345678(%rbp,%rax,1)");
+       asm volatile("bndstx %bnd0, 0x12345678(%rax,%rcx,1)");
+
+       /* bnd prefix on call, ret, jmp and all jcc */
+
+       asm volatile("bnd call label1");  /* Expecting: call unconditional 0 */
+       asm volatile("bnd call *(%eax)"); /* Expecting: call indirect      0 */
+       asm volatile("bnd ret");          /* Expecting: ret  indirect      0 */
+       asm volatile("bnd jmp label1");   /* Expecting: jmp  unconditional 0 */
+       asm volatile("bnd jmp label1");   /* Expecting: jmp  unconditional 0 */
+       asm volatile("bnd jmp *(%ecx)");  /* Expecting: jmp  indirect      0 */
+       asm volatile("bnd jne label1");   /* Expecting: jcc  conditional   0 */
+
+       /* sha1rnds4 imm8, xmm2/m128, xmm1 */
+
+       asm volatile("sha1rnds4 $0x0, %xmm1, %xmm0");
+       asm volatile("sha1rnds4 $0x91, %xmm7, %xmm2");
+       asm volatile("sha1rnds4 $0x91, %xmm8, %xmm0");
+       asm volatile("sha1rnds4 $0x91, %xmm7, %xmm8");
+       asm volatile("sha1rnds4 $0x91, %xmm15, %xmm8");
+       asm volatile("sha1rnds4 $0x91, (%rax), %xmm0");
+       asm volatile("sha1rnds4 $0x91, (%r8), %xmm0");
+       asm volatile("sha1rnds4 $0x91, (0x12345678), %xmm0");
+       asm volatile("sha1rnds4 $0x91, (%rax), %xmm3");
+       asm volatile("sha1rnds4 $0x91, (%rcx,%rax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(,%rax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, (%rax,%rcx,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, (%rax,%rcx,8), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%rax), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%rbp), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%rcx,%rax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%rbp,%rax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%rax,%rcx,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%rax,%rcx,8), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%rax), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%rbp), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%rcx,%rax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%rbp,%rax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%rax,%rcx,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%rax,%rcx,8), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%rax,%rcx,8), %xmm15");
+
+       /* sha1nexte xmm2/m128, xmm1 */
+
+       asm volatile("sha1nexte %xmm1, %xmm0");
+       asm volatile("sha1nexte %xmm7, %xmm2");
+       asm volatile("sha1nexte %xmm8, %xmm0");
+       asm volatile("sha1nexte %xmm7, %xmm8");
+       asm volatile("sha1nexte %xmm15, %xmm8");
+       asm volatile("sha1nexte (%rax), %xmm0");
+       asm volatile("sha1nexte (%r8), %xmm0");
+       asm volatile("sha1nexte (0x12345678), %xmm0");
+       asm volatile("sha1nexte (%rax), %xmm3");
+       asm volatile("sha1nexte (%rcx,%rax,1), %xmm0");
+       asm volatile("sha1nexte 0x12345678(,%rax,1), %xmm0");
+       asm volatile("sha1nexte (%rax,%rcx,1), %xmm0");
+       asm volatile("sha1nexte (%rax,%rcx,8), %xmm0");
+       asm volatile("sha1nexte 0x12(%rax), %xmm0");
+       asm volatile("sha1nexte 0x12(%rbp), %xmm0");
+       asm volatile("sha1nexte 0x12(%rcx,%rax,1), %xmm0");
+       asm volatile("sha1nexte 0x12(%rbp,%rax,1), %xmm0");
+       asm volatile("sha1nexte 0x12(%rax,%rcx,1), %xmm0");
+       asm volatile("sha1nexte 0x12(%rax,%rcx,8), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%rax), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%rbp), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%rcx,%rax,1), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%rbp,%rax,1), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%rax,%rcx,1), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%rax,%rcx,8), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%rax,%rcx,8), %xmm15");
+
+       /* sha1msg1 xmm2/m128, xmm1 */
+
+       asm volatile("sha1msg1 %xmm1, %xmm0");
+       asm volatile("sha1msg1 %xmm7, %xmm2");
+       asm volatile("sha1msg1 %xmm8, %xmm0");
+       asm volatile("sha1msg1 %xmm7, %xmm8");
+       asm volatile("sha1msg1 %xmm15, %xmm8");
+       asm volatile("sha1msg1 (%rax), %xmm0");
+       asm volatile("sha1msg1 (%r8), %xmm0");
+       asm volatile("sha1msg1 (0x12345678), %xmm0");
+       asm volatile("sha1msg1 (%rax), %xmm3");
+       asm volatile("sha1msg1 (%rcx,%rax,1), %xmm0");
+       asm volatile("sha1msg1 0x12345678(,%rax,1), %xmm0");
+       asm volatile("sha1msg1 (%rax,%rcx,1), %xmm0");
+       asm volatile("sha1msg1 (%rax,%rcx,8), %xmm0");
+       asm volatile("sha1msg1 0x12(%rax), %xmm0");
+       asm volatile("sha1msg1 0x12(%rbp), %xmm0");
+       asm volatile("sha1msg1 0x12(%rcx,%rax,1), %xmm0");
+       asm volatile("sha1msg1 0x12(%rbp,%rax,1), %xmm0");
+       asm volatile("sha1msg1 0x12(%rax,%rcx,1), %xmm0");
+       asm volatile("sha1msg1 0x12(%rax,%rcx,8), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%rax), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%rbp), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%rcx,%rax,1), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%rbp,%rax,1), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%rax,%rcx,1), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%rax,%rcx,8), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%rax,%rcx,8), %xmm15");
+
+       /* sha1msg2 xmm2/m128, xmm1 */
+
+       asm volatile("sha1msg2 %xmm1, %xmm0");
+       asm volatile("sha1msg2 %xmm7, %xmm2");
+       asm volatile("sha1msg2 %xmm8, %xmm0");
+       asm volatile("sha1msg2 %xmm7, %xmm8");
+       asm volatile("sha1msg2 %xmm15, %xmm8");
+       asm volatile("sha1msg2 (%rax), %xmm0");
+       asm volatile("sha1msg2 (%r8), %xmm0");
+       asm volatile("sha1msg2 (0x12345678), %xmm0");
+       asm volatile("sha1msg2 (%rax), %xmm3");
+       asm volatile("sha1msg2 (%rcx,%rax,1), %xmm0");
+       asm volatile("sha1msg2 0x12345678(,%rax,1), %xmm0");
+       asm volatile("sha1msg2 (%rax,%rcx,1), %xmm0");
+       asm volatile("sha1msg2 (%rax,%rcx,8), %xmm0");
+       asm volatile("sha1msg2 0x12(%rax), %xmm0");
+       asm volatile("sha1msg2 0x12(%rbp), %xmm0");
+       asm volatile("sha1msg2 0x12(%rcx,%rax,1), %xmm0");
+       asm volatile("sha1msg2 0x12(%rbp,%rax,1), %xmm0");
+       asm volatile("sha1msg2 0x12(%rax,%rcx,1), %xmm0");
+       asm volatile("sha1msg2 0x12(%rax,%rcx,8), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%rax), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%rbp), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%rcx,%rax,1), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%rbp,%rax,1), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%rax,%rcx,1), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%rax,%rcx,8), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%rax,%rcx,8), %xmm15");
+
+       /* sha256rnds2 <XMM0>, xmm2/m128, xmm1 */
+       /* Note sha256rnds2 has an implicit operand 'xmm0' */
+
+       asm volatile("sha256rnds2 %xmm4, %xmm1");
+       asm volatile("sha256rnds2 %xmm7, %xmm2");
+       asm volatile("sha256rnds2 %xmm8, %xmm1");
+       asm volatile("sha256rnds2 %xmm7, %xmm8");
+       asm volatile("sha256rnds2 %xmm15, %xmm8");
+       asm volatile("sha256rnds2 (%rax), %xmm1");
+       asm volatile("sha256rnds2 (%r8), %xmm1");
+       asm volatile("sha256rnds2 (0x12345678), %xmm1");
+       asm volatile("sha256rnds2 (%rax), %xmm3");
+       asm volatile("sha256rnds2 (%rcx,%rax,1), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(,%rax,1), %xmm1");
+       asm volatile("sha256rnds2 (%rax,%rcx,1), %xmm1");
+       asm volatile("sha256rnds2 (%rax,%rcx,8), %xmm1");
+       asm volatile("sha256rnds2 0x12(%rax), %xmm1");
+       asm volatile("sha256rnds2 0x12(%rbp), %xmm1");
+       asm volatile("sha256rnds2 0x12(%rcx,%rax,1), %xmm1");
+       asm volatile("sha256rnds2 0x12(%rbp,%rax,1), %xmm1");
+       asm volatile("sha256rnds2 0x12(%rax,%rcx,1), %xmm1");
+       asm volatile("sha256rnds2 0x12(%rax,%rcx,8), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%rax), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%rbp), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%rcx,%rax,1), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%rbp,%rax,1), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%rax,%rcx,1), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%rax,%rcx,8), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%rax,%rcx,8), %xmm15");
+
+       /* sha256msg1 xmm2/m128, xmm1 */
+
+       asm volatile("sha256msg1 %xmm1, %xmm0");
+       asm volatile("sha256msg1 %xmm7, %xmm2");
+       asm volatile("sha256msg1 %xmm8, %xmm0");
+       asm volatile("sha256msg1 %xmm7, %xmm8");
+       asm volatile("sha256msg1 %xmm15, %xmm8");
+       asm volatile("sha256msg1 (%rax), %xmm0");
+       asm volatile("sha256msg1 (%r8), %xmm0");
+       asm volatile("sha256msg1 (0x12345678), %xmm0");
+       asm volatile("sha256msg1 (%rax), %xmm3");
+       asm volatile("sha256msg1 (%rcx,%rax,1), %xmm0");
+       asm volatile("sha256msg1 0x12345678(,%rax,1), %xmm0");
+       asm volatile("sha256msg1 (%rax,%rcx,1), %xmm0");
+       asm volatile("sha256msg1 (%rax,%rcx,8), %xmm0");
+       asm volatile("sha256msg1 0x12(%rax), %xmm0");
+       asm volatile("sha256msg1 0x12(%rbp), %xmm0");
+       asm volatile("sha256msg1 0x12(%rcx,%rax,1), %xmm0");
+       asm volatile("sha256msg1 0x12(%rbp,%rax,1), %xmm0");
+       asm volatile("sha256msg1 0x12(%rax,%rcx,1), %xmm0");
+       asm volatile("sha256msg1 0x12(%rax,%rcx,8), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%rax), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%rbp), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%rcx,%rax,1), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%rbp,%rax,1), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%rax,%rcx,1), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%rax,%rcx,8), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%rax,%rcx,8), %xmm15");
+
+       /* sha256msg2 xmm2/m128, xmm1 */
+
+       asm volatile("sha256msg2 %xmm1, %xmm0");
+       asm volatile("sha256msg2 %xmm7, %xmm2");
+       asm volatile("sha256msg2 %xmm8, %xmm0");
+       asm volatile("sha256msg2 %xmm7, %xmm8");
+       asm volatile("sha256msg2 %xmm15, %xmm8");
+       asm volatile("sha256msg2 (%rax), %xmm0");
+       asm volatile("sha256msg2 (%r8), %xmm0");
+       asm volatile("sha256msg2 (0x12345678), %xmm0");
+       asm volatile("sha256msg2 (%rax), %xmm3");
+       asm volatile("sha256msg2 (%rcx,%rax,1), %xmm0");
+       asm volatile("sha256msg2 0x12345678(,%rax,1), %xmm0");
+       asm volatile("sha256msg2 (%rax,%rcx,1), %xmm0");
+       asm volatile("sha256msg2 (%rax,%rcx,8), %xmm0");
+       asm volatile("sha256msg2 0x12(%rax), %xmm0");
+       asm volatile("sha256msg2 0x12(%rbp), %xmm0");
+       asm volatile("sha256msg2 0x12(%rcx,%rax,1), %xmm0");
+       asm volatile("sha256msg2 0x12(%rbp,%rax,1), %xmm0");
+       asm volatile("sha256msg2 0x12(%rax,%rcx,1), %xmm0");
+       asm volatile("sha256msg2 0x12(%rax,%rcx,8), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%rax), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%rbp), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%rcx,%rax,1), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%rbp,%rax,1), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%rax,%rcx,1), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%rax,%rcx,8), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%rax,%rcx,8), %xmm15");
+
+       /* clflushopt m8 */
+
+       asm volatile("clflushopt (%rax)");
+       asm volatile("clflushopt (%r8)");
+       asm volatile("clflushopt (0x12345678)");
+       asm volatile("clflushopt 0x12345678(%rax,%rcx,8)");
+       asm volatile("clflushopt 0x12345678(%r8,%rcx,8)");
+       /* Also check instructions in the same group encoding as clflushopt */
+       asm volatile("clflush (%rax)");
+       asm volatile("clflush (%r8)");
+       asm volatile("sfence");
+
+       /* clwb m8 */
+
+       asm volatile("clwb (%rax)");
+       asm volatile("clwb (%r8)");
+       asm volatile("clwb (0x12345678)");
+       asm volatile("clwb 0x12345678(%rax,%rcx,8)");
+       asm volatile("clwb 0x12345678(%r8,%rcx,8)");
+       /* Also check instructions in the same group encoding as clwb */
+       asm volatile("xsaveopt (%rax)");
+       asm volatile("xsaveopt (%r8)");
+       asm volatile("mfence");
+
+       /* xsavec mem */
+
+       asm volatile("xsavec (%rax)");
+       asm volatile("xsavec (%r8)");
+       asm volatile("xsavec (0x12345678)");
+       asm volatile("xsavec 0x12345678(%rax,%rcx,8)");
+       asm volatile("xsavec 0x12345678(%r8,%rcx,8)");
+
+       /* xsaves mem */
+
+       asm volatile("xsaves (%rax)");
+       asm volatile("xsaves (%r8)");
+       asm volatile("xsaves (0x12345678)");
+       asm volatile("xsaves 0x12345678(%rax,%rcx,8)");
+       asm volatile("xsaves 0x12345678(%r8,%rcx,8)");
+
+       /* xrstors mem */
+
+       asm volatile("xrstors (%rax)");
+       asm volatile("xrstors (%r8)");
+       asm volatile("xrstors (0x12345678)");
+       asm volatile("xrstors 0x12345678(%rax,%rcx,8)");
+       asm volatile("xrstors 0x12345678(%r8,%rcx,8)");
+
+#else  /* #ifdef __x86_64__ */
+
+       /* bndmk m32, bnd */
+
+       asm volatile("bndmk (%eax), %bnd0");
+       asm volatile("bndmk (0x12345678), %bnd0");
+       asm volatile("bndmk (%eax), %bnd3");
+       asm volatile("bndmk (%ecx,%eax,1), %bnd0");
+       asm volatile("bndmk 0x12345678(,%eax,1), %bnd0");
+       asm volatile("bndmk (%eax,%ecx,1), %bnd0");
+       asm volatile("bndmk (%eax,%ecx,8), %bnd0");
+       asm volatile("bndmk 0x12(%eax), %bnd0");
+       asm volatile("bndmk 0x12(%ebp), %bnd0");
+       asm volatile("bndmk 0x12(%ecx,%eax,1), %bnd0");
+       asm volatile("bndmk 0x12(%ebp,%eax,1), %bnd0");
+       asm volatile("bndmk 0x12(%eax,%ecx,1), %bnd0");
+       asm volatile("bndmk 0x12(%eax,%ecx,8), %bnd0");
+       asm volatile("bndmk 0x12345678(%eax), %bnd0");
+       asm volatile("bndmk 0x12345678(%ebp), %bnd0");
+       asm volatile("bndmk 0x12345678(%ecx,%eax,1), %bnd0");
+       asm volatile("bndmk 0x12345678(%ebp,%eax,1), %bnd0");
+       asm volatile("bndmk 0x12345678(%eax,%ecx,1), %bnd0");
+       asm volatile("bndmk 0x12345678(%eax,%ecx,8), %bnd0");
+
+       /* bndcl r/m32, bnd */
+
+       asm volatile("bndcl (%eax), %bnd0");
+       asm volatile("bndcl (0x12345678), %bnd0");
+       asm volatile("bndcl (%eax), %bnd3");
+       asm volatile("bndcl (%ecx,%eax,1), %bnd0");
+       asm volatile("bndcl 0x12345678(,%eax,1), %bnd0");
+       asm volatile("bndcl (%eax,%ecx,1), %bnd0");
+       asm volatile("bndcl (%eax,%ecx,8), %bnd0");
+       asm volatile("bndcl 0x12(%eax), %bnd0");
+       asm volatile("bndcl 0x12(%ebp), %bnd0");
+       asm volatile("bndcl 0x12(%ecx,%eax,1), %bnd0");
+       asm volatile("bndcl 0x12(%ebp,%eax,1), %bnd0");
+       asm volatile("bndcl 0x12(%eax,%ecx,1), %bnd0");
+       asm volatile("bndcl 0x12(%eax,%ecx,8), %bnd0");
+       asm volatile("bndcl 0x12345678(%eax), %bnd0");
+       asm volatile("bndcl 0x12345678(%ebp), %bnd0");
+       asm volatile("bndcl 0x12345678(%ecx,%eax,1), %bnd0");
+       asm volatile("bndcl 0x12345678(%ebp,%eax,1), %bnd0");
+       asm volatile("bndcl 0x12345678(%eax,%ecx,1), %bnd0");
+       asm volatile("bndcl 0x12345678(%eax,%ecx,8), %bnd0");
+       asm volatile("bndcl %eax, %bnd0");
+
+       /* bndcu r/m32, bnd */
+
+       asm volatile("bndcu (%eax), %bnd0");
+       asm volatile("bndcu (0x12345678), %bnd0");
+       asm volatile("bndcu (%eax), %bnd3");
+       asm volatile("bndcu (%ecx,%eax,1), %bnd0");
+       asm volatile("bndcu 0x12345678(,%eax,1), %bnd0");
+       asm volatile("bndcu (%eax,%ecx,1), %bnd0");
+       asm volatile("bndcu (%eax,%ecx,8), %bnd0");
+       asm volatile("bndcu 0x12(%eax), %bnd0");
+       asm volatile("bndcu 0x12(%ebp), %bnd0");
+       asm volatile("bndcu 0x12(%ecx,%eax,1), %bnd0");
+       asm volatile("bndcu 0x12(%ebp,%eax,1), %bnd0");
+       asm volatile("bndcu 0x12(%eax,%ecx,1), %bnd0");
+       asm volatile("bndcu 0x12(%eax,%ecx,8), %bnd0");
+       asm volatile("bndcu 0x12345678(%eax), %bnd0");
+       asm volatile("bndcu 0x12345678(%ebp), %bnd0");
+       asm volatile("bndcu 0x12345678(%ecx,%eax,1), %bnd0");
+       asm volatile("bndcu 0x12345678(%ebp,%eax,1), %bnd0");
+       asm volatile("bndcu 0x12345678(%eax,%ecx,1), %bnd0");
+       asm volatile("bndcu 0x12345678(%eax,%ecx,8), %bnd0");
+       asm volatile("bndcu %eax, %bnd0");
+
+       /* bndcn r/m32, bnd */
+
+       asm volatile("bndcn (%eax), %bnd0");
+       asm volatile("bndcn (0x12345678), %bnd0");
+       asm volatile("bndcn (%eax), %bnd3");
+       asm volatile("bndcn (%ecx,%eax,1), %bnd0");
+       asm volatile("bndcn 0x12345678(,%eax,1), %bnd0");
+       asm volatile("bndcn (%eax,%ecx,1), %bnd0");
+       asm volatile("bndcn (%eax,%ecx,8), %bnd0");
+       asm volatile("bndcn 0x12(%eax), %bnd0");
+       asm volatile("bndcn 0x12(%ebp), %bnd0");
+       asm volatile("bndcn 0x12(%ecx,%eax,1), %bnd0");
+       asm volatile("bndcn 0x12(%ebp,%eax,1), %bnd0");
+       asm volatile("bndcn 0x12(%eax,%ecx,1), %bnd0");
+       asm volatile("bndcn 0x12(%eax,%ecx,8), %bnd0");
+       asm volatile("bndcn 0x12345678(%eax), %bnd0");
+       asm volatile("bndcn 0x12345678(%ebp), %bnd0");
+       asm volatile("bndcn 0x12345678(%ecx,%eax,1), %bnd0");
+       asm volatile("bndcn 0x12345678(%ebp,%eax,1), %bnd0");
+       asm volatile("bndcn 0x12345678(%eax,%ecx,1), %bnd0");
+       asm volatile("bndcn 0x12345678(%eax,%ecx,8), %bnd0");
+       asm volatile("bndcn %eax, %bnd0");
+
+       /* bndmov m64, bnd */
+
+       asm volatile("bndmov (%eax), %bnd0");
+       asm volatile("bndmov (0x12345678), %bnd0");
+       asm volatile("bndmov (%eax), %bnd3");
+       asm volatile("bndmov (%ecx,%eax,1), %bnd0");
+       asm volatile("bndmov 0x12345678(,%eax,1), %bnd0");
+       asm volatile("bndmov (%eax,%ecx,1), %bnd0");
+       asm volatile("bndmov (%eax,%ecx,8), %bnd0");
+       asm volatile("bndmov 0x12(%eax), %bnd0");
+       asm volatile("bndmov 0x12(%ebp), %bnd0");
+       asm volatile("bndmov 0x12(%ecx,%eax,1), %bnd0");
+       asm volatile("bndmov 0x12(%ebp,%eax,1), %bnd0");
+       asm volatile("bndmov 0x12(%eax,%ecx,1), %bnd0");
+       asm volatile("bndmov 0x12(%eax,%ecx,8), %bnd0");
+       asm volatile("bndmov 0x12345678(%eax), %bnd0");
+       asm volatile("bndmov 0x12345678(%ebp), %bnd0");
+       asm volatile("bndmov 0x12345678(%ecx,%eax,1), %bnd0");
+       asm volatile("bndmov 0x12345678(%ebp,%eax,1), %bnd0");
+       asm volatile("bndmov 0x12345678(%eax,%ecx,1), %bnd0");
+       asm volatile("bndmov 0x12345678(%eax,%ecx,8), %bnd0");
+
+       /* bndmov bnd, m64 */
+
+       asm volatile("bndmov %bnd0, (%eax)");
+       asm volatile("bndmov %bnd0, (0x12345678)");
+       asm volatile("bndmov %bnd3, (%eax)");
+       asm volatile("bndmov %bnd0, (%ecx,%eax,1)");
+       asm volatile("bndmov %bnd0, 0x12345678(,%eax,1)");
+       asm volatile("bndmov %bnd0, (%eax,%ecx,1)");
+       asm volatile("bndmov %bnd0, (%eax,%ecx,8)");
+       asm volatile("bndmov %bnd0, 0x12(%eax)");
+       asm volatile("bndmov %bnd0, 0x12(%ebp)");
+       asm volatile("bndmov %bnd0, 0x12(%ecx,%eax,1)");
+       asm volatile("bndmov %bnd0, 0x12(%ebp,%eax,1)");
+       asm volatile("bndmov %bnd0, 0x12(%eax,%ecx,1)");
+       asm volatile("bndmov %bnd0, 0x12(%eax,%ecx,8)");
+       asm volatile("bndmov %bnd0, 0x12345678(%eax)");
+       asm volatile("bndmov %bnd0, 0x12345678(%ebp)");
+       asm volatile("bndmov %bnd0, 0x12345678(%ecx,%eax,1)");
+       asm volatile("bndmov %bnd0, 0x12345678(%ebp,%eax,1)");
+       asm volatile("bndmov %bnd0, 0x12345678(%eax,%ecx,1)");
+       asm volatile("bndmov %bnd0, 0x12345678(%eax,%ecx,8)");
+
+       /* bndmov bnd2, bnd1 */
+
+       asm volatile("bndmov %bnd0, %bnd1");
+       asm volatile("bndmov %bnd1, %bnd0");
+
+       /* bndldx mib, bnd */
+
+       asm volatile("bndldx (%eax), %bnd0");
+       asm volatile("bndldx (0x12345678), %bnd0");
+       asm volatile("bndldx (%eax), %bnd3");
+       asm volatile("bndldx (%ecx,%eax,1), %bnd0");
+       asm volatile("bndldx 0x12345678(,%eax,1), %bnd0");
+       asm volatile("bndldx (%eax,%ecx,1), %bnd0");
+       asm volatile("bndldx 0x12(%eax), %bnd0");
+       asm volatile("bndldx 0x12(%ebp), %bnd0");
+       asm volatile("bndldx 0x12(%ecx,%eax,1), %bnd0");
+       asm volatile("bndldx 0x12(%ebp,%eax,1), %bnd0");
+       asm volatile("bndldx 0x12(%eax,%ecx,1), %bnd0");
+       asm volatile("bndldx 0x12345678(%eax), %bnd0");
+       asm volatile("bndldx 0x12345678(%ebp), %bnd0");
+       asm volatile("bndldx 0x12345678(%ecx,%eax,1), %bnd0");
+       asm volatile("bndldx 0x12345678(%ebp,%eax,1), %bnd0");
+       asm volatile("bndldx 0x12345678(%eax,%ecx,1), %bnd0");
+
+       /* bndstx bnd, mib */
+
+       asm volatile("bndstx %bnd0, (%eax)");
+       asm volatile("bndstx %bnd0, (0x12345678)");
+       asm volatile("bndstx %bnd3, (%eax)");
+       asm volatile("bndstx %bnd0, (%ecx,%eax,1)");
+       asm volatile("bndstx %bnd0, 0x12345678(,%eax,1)");
+       asm volatile("bndstx %bnd0, (%eax,%ecx,1)");
+       asm volatile("bndstx %bnd0, 0x12(%eax)");
+       asm volatile("bndstx %bnd0, 0x12(%ebp)");
+       asm volatile("bndstx %bnd0, 0x12(%ecx,%eax,1)");
+       asm volatile("bndstx %bnd0, 0x12(%ebp,%eax,1)");
+       asm volatile("bndstx %bnd0, 0x12(%eax,%ecx,1)");
+       asm volatile("bndstx %bnd0, 0x12345678(%eax)");
+       asm volatile("bndstx %bnd0, 0x12345678(%ebp)");
+       asm volatile("bndstx %bnd0, 0x12345678(%ecx,%eax,1)");
+       asm volatile("bndstx %bnd0, 0x12345678(%ebp,%eax,1)");
+       asm volatile("bndstx %bnd0, 0x12345678(%eax,%ecx,1)");
+
+       /* bnd prefix on call, ret, jmp and all jcc */
+
+       asm volatile("bnd call label1");  /* Expecting: call unconditional 0xfffffffc */
+       asm volatile("bnd call *(%eax)"); /* Expecting: call indirect      0 */
+       asm volatile("bnd ret");          /* Expecting: ret  indirect      0 */
+       asm volatile("bnd jmp label1");   /* Expecting: jmp  unconditional 0xfffffffc */
+       asm volatile("bnd jmp label1");   /* Expecting: jmp  unconditional 0xfffffffc */
+       asm volatile("bnd jmp *(%ecx)");  /* Expecting: jmp  indirect      0 */
+       asm volatile("bnd jne label1");   /* Expecting: jcc  conditional   0xfffffffc */
+
+       /* sha1rnds4 imm8, xmm2/m128, xmm1 */
+
+       asm volatile("sha1rnds4 $0x0, %xmm1, %xmm0");
+       asm volatile("sha1rnds4 $0x91, %xmm7, %xmm2");
+       asm volatile("sha1rnds4 $0x91, (%eax), %xmm0");
+       asm volatile("sha1rnds4 $0x91, (0x12345678), %xmm0");
+       asm volatile("sha1rnds4 $0x91, (%eax), %xmm3");
+       asm volatile("sha1rnds4 $0x91, (%ecx,%eax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(,%eax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, (%eax,%ecx,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, (%eax,%ecx,8), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%eax), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%ebp), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%ecx,%eax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%ebp,%eax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%eax,%ecx,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12(%eax,%ecx,8), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%eax), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%ebp), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%ecx,%eax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%ebp,%eax,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%eax,%ecx,1), %xmm0");
+       asm volatile("sha1rnds4 $0x91, 0x12345678(%eax,%ecx,8), %xmm0");
+
+       /* sha1nexte xmm2/m128, xmm1 */
+
+       asm volatile("sha1nexte %xmm1, %xmm0");
+       asm volatile("sha1nexte %xmm7, %xmm2");
+       asm volatile("sha1nexte (%eax), %xmm0");
+       asm volatile("sha1nexte (0x12345678), %xmm0");
+       asm volatile("sha1nexte (%eax), %xmm3");
+       asm volatile("sha1nexte (%ecx,%eax,1), %xmm0");
+       asm volatile("sha1nexte 0x12345678(,%eax,1), %xmm0");
+       asm volatile("sha1nexte (%eax,%ecx,1), %xmm0");
+       asm volatile("sha1nexte (%eax,%ecx,8), %xmm0");
+       asm volatile("sha1nexte 0x12(%eax), %xmm0");
+       asm volatile("sha1nexte 0x12(%ebp), %xmm0");
+       asm volatile("sha1nexte 0x12(%ecx,%eax,1), %xmm0");
+       asm volatile("sha1nexte 0x12(%ebp,%eax,1), %xmm0");
+       asm volatile("sha1nexte 0x12(%eax,%ecx,1), %xmm0");
+       asm volatile("sha1nexte 0x12(%eax,%ecx,8), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%eax), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%ebp), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%ecx,%eax,1), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%ebp,%eax,1), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%eax,%ecx,1), %xmm0");
+       asm volatile("sha1nexte 0x12345678(%eax,%ecx,8), %xmm0");
+
+       /* sha1msg1 xmm2/m128, xmm1 */
+
+       asm volatile("sha1msg1 %xmm1, %xmm0");
+       asm volatile("sha1msg1 %xmm7, %xmm2");
+       asm volatile("sha1msg1 (%eax), %xmm0");
+       asm volatile("sha1msg1 (0x12345678), %xmm0");
+       asm volatile("sha1msg1 (%eax), %xmm3");
+       asm volatile("sha1msg1 (%ecx,%eax,1), %xmm0");
+       asm volatile("sha1msg1 0x12345678(,%eax,1), %xmm0");
+       asm volatile("sha1msg1 (%eax,%ecx,1), %xmm0");
+       asm volatile("sha1msg1 (%eax,%ecx,8), %xmm0");
+       asm volatile("sha1msg1 0x12(%eax), %xmm0");
+       asm volatile("sha1msg1 0x12(%ebp), %xmm0");
+       asm volatile("sha1msg1 0x12(%ecx,%eax,1), %xmm0");
+       asm volatile("sha1msg1 0x12(%ebp,%eax,1), %xmm0");
+       asm volatile("sha1msg1 0x12(%eax,%ecx,1), %xmm0");
+       asm volatile("sha1msg1 0x12(%eax,%ecx,8), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%eax), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%ebp), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%ecx,%eax,1), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%ebp,%eax,1), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%eax,%ecx,1), %xmm0");
+       asm volatile("sha1msg1 0x12345678(%eax,%ecx,8), %xmm0");
+
+       /* sha1msg2 xmm2/m128, xmm1 */
+
+       asm volatile("sha1msg2 %xmm1, %xmm0");
+       asm volatile("sha1msg2 %xmm7, %xmm2");
+       asm volatile("sha1msg2 (%eax), %xmm0");
+       asm volatile("sha1msg2 (0x12345678), %xmm0");
+       asm volatile("sha1msg2 (%eax), %xmm3");
+       asm volatile("sha1msg2 (%ecx,%eax,1), %xmm0");
+       asm volatile("sha1msg2 0x12345678(,%eax,1), %xmm0");
+       asm volatile("sha1msg2 (%eax,%ecx,1), %xmm0");
+       asm volatile("sha1msg2 (%eax,%ecx,8), %xmm0");
+       asm volatile("sha1msg2 0x12(%eax), %xmm0");
+       asm volatile("sha1msg2 0x12(%ebp), %xmm0");
+       asm volatile("sha1msg2 0x12(%ecx,%eax,1), %xmm0");
+       asm volatile("sha1msg2 0x12(%ebp,%eax,1), %xmm0");
+       asm volatile("sha1msg2 0x12(%eax,%ecx,1), %xmm0");
+       asm volatile("sha1msg2 0x12(%eax,%ecx,8), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%eax), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%ebp), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%ecx,%eax,1), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%ebp,%eax,1), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%eax,%ecx,1), %xmm0");
+       asm volatile("sha1msg2 0x12345678(%eax,%ecx,8), %xmm0");
+
+       /* sha256rnds2 <XMM0>, xmm2/m128, xmm1 */
+       /* Note sha256rnds2 has an implicit operand 'xmm0' */
+
+       asm volatile("sha256rnds2 %xmm4, %xmm1");
+       asm volatile("sha256rnds2 %xmm7, %xmm2");
+       asm volatile("sha256rnds2 (%eax), %xmm1");
+       asm volatile("sha256rnds2 (0x12345678), %xmm1");
+       asm volatile("sha256rnds2 (%eax), %xmm3");
+       asm volatile("sha256rnds2 (%ecx,%eax,1), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(,%eax,1), %xmm1");
+       asm volatile("sha256rnds2 (%eax,%ecx,1), %xmm1");
+       asm volatile("sha256rnds2 (%eax,%ecx,8), %xmm1");
+       asm volatile("sha256rnds2 0x12(%eax), %xmm1");
+       asm volatile("sha256rnds2 0x12(%ebp), %xmm1");
+       asm volatile("sha256rnds2 0x12(%ecx,%eax,1), %xmm1");
+       asm volatile("sha256rnds2 0x12(%ebp,%eax,1), %xmm1");
+       asm volatile("sha256rnds2 0x12(%eax,%ecx,1), %xmm1");
+       asm volatile("sha256rnds2 0x12(%eax,%ecx,8), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%eax), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%ebp), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%ecx,%eax,1), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%ebp,%eax,1), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%eax,%ecx,1), %xmm1");
+       asm volatile("sha256rnds2 0x12345678(%eax,%ecx,8), %xmm1");
+
+       /* sha256msg1 xmm2/m128, xmm1 */
+
+       asm volatile("sha256msg1 %xmm1, %xmm0");
+       asm volatile("sha256msg1 %xmm7, %xmm2");
+       asm volatile("sha256msg1 (%eax), %xmm0");
+       asm volatile("sha256msg1 (0x12345678), %xmm0");
+       asm volatile("sha256msg1 (%eax), %xmm3");
+       asm volatile("sha256msg1 (%ecx,%eax,1), %xmm0");
+       asm volatile("sha256msg1 0x12345678(,%eax,1), %xmm0");
+       asm volatile("sha256msg1 (%eax,%ecx,1), %xmm0");
+       asm volatile("sha256msg1 (%eax,%ecx,8), %xmm0");
+       asm volatile("sha256msg1 0x12(%eax), %xmm0");
+       asm volatile("sha256msg1 0x12(%ebp), %xmm0");
+       asm volatile("sha256msg1 0x12(%ecx,%eax,1), %xmm0");
+       asm volatile("sha256msg1 0x12(%ebp,%eax,1), %xmm0");
+       asm volatile("sha256msg1 0x12(%eax,%ecx,1), %xmm0");
+       asm volatile("sha256msg1 0x12(%eax,%ecx,8), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%eax), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%ebp), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%ecx,%eax,1), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%ebp,%eax,1), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%eax,%ecx,1), %xmm0");
+       asm volatile("sha256msg1 0x12345678(%eax,%ecx,8), %xmm0");
+
+       /* sha256msg2 xmm2/m128, xmm1 */
+
+       asm volatile("sha256msg2 %xmm1, %xmm0");
+       asm volatile("sha256msg2 %xmm7, %xmm2");
+       asm volatile("sha256msg2 (%eax), %xmm0");
+       asm volatile("sha256msg2 (0x12345678), %xmm0");
+       asm volatile("sha256msg2 (%eax), %xmm3");
+       asm volatile("sha256msg2 (%ecx,%eax,1), %xmm0");
+       asm volatile("sha256msg2 0x12345678(,%eax,1), %xmm0");
+       asm volatile("sha256msg2 (%eax,%ecx,1), %xmm0");
+       asm volatile("sha256msg2 (%eax,%ecx,8), %xmm0");
+       asm volatile("sha256msg2 0x12(%eax), %xmm0");
+       asm volatile("sha256msg2 0x12(%ebp), %xmm0");
+       asm volatile("sha256msg2 0x12(%ecx,%eax,1), %xmm0");
+       asm volatile("sha256msg2 0x12(%ebp,%eax,1), %xmm0");
+       asm volatile("sha256msg2 0x12(%eax,%ecx,1), %xmm0");
+       asm volatile("sha256msg2 0x12(%eax,%ecx,8), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%eax), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%ebp), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%ecx,%eax,1), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%ebp,%eax,1), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%eax,%ecx,1), %xmm0");
+       asm volatile("sha256msg2 0x12345678(%eax,%ecx,8), %xmm0");
+
+       /* clflushopt m8 */
+
+       asm volatile("clflushopt (%eax)");
+       asm volatile("clflushopt (0x12345678)");
+       asm volatile("clflushopt 0x12345678(%eax,%ecx,8)");
+       /* Also check instructions in the same group encoding as clflushopt */
+       asm volatile("clflush (%eax)");
+       asm volatile("sfence");
+
+       /* clwb m8 */
+
+       asm volatile("clwb (%eax)");
+       asm volatile("clwb (0x12345678)");
+       asm volatile("clwb 0x12345678(%eax,%ecx,8)");
+       /* Also check instructions in the same group encoding as clwb */
+       asm volatile("xsaveopt (%eax)");
+       asm volatile("mfence");
+
+       /* xsavec mem */
+
+       asm volatile("xsavec (%eax)");
+       asm volatile("xsavec (0x12345678)");
+       asm volatile("xsavec 0x12345678(%eax,%ecx,8)");
+
+       /* xsaves mem */
+
+       asm volatile("xsaves (%eax)");
+       asm volatile("xsaves (0x12345678)");
+       asm volatile("xsaves 0x12345678(%eax,%ecx,8)");
+
+       /* xrstors mem */
+
+       asm volatile("xrstors (%eax)");
+       asm volatile("xrstors (0x12345678)");
+       asm volatile("xrstors 0x12345678(%eax,%ecx,8)");
+
+#endif /* #ifndef __x86_64__ */
+
+       /* pcommit */
+
+       asm volatile("pcommit");
+
+       /* Following line is a marker for the awk script - do not change */
+       asm volatile("rdtsc"); /* Stop here */
+
+       return 0;
+}
diff --git a/tools/perf/arch/x86/tests/insn-x86.c b/tools/perf/arch/x86/tests/insn-x86.c
new file mode 100644 (file)
index 0000000..b6115df
--- /dev/null
@@ -0,0 +1,185 @@
+#include <linux/types.h>
+
+#include "debug.h"
+#include "tests/tests.h"
+#include "arch-tests.h"
+
+#include "intel-pt-decoder/insn.h"
+#include "intel-pt-decoder/intel-pt-insn-decoder.h"
+
+struct test_data {
+       u8 data[MAX_INSN_SIZE];
+       int expected_length;
+       int expected_rel;
+       const char *expected_op_str;
+       const char *expected_branch_str;
+       const char *asm_rep;
+};
+
+struct test_data test_data_32[] = {
+#include "insn-x86-dat-32.c"
+       {{0x0f, 0x01, 0xee}, 3, 0, NULL, NULL, "0f 01 ee             \trdpkru"},
+       {{0x0f, 0x01, 0xef}, 3, 0, NULL, NULL, "0f 01 ef             \twrpkru"},
+       {{0}, 0, 0, NULL, NULL, NULL},
+};
+
+struct test_data test_data_64[] = {
+#include "insn-x86-dat-64.c"
+       {{0x0f, 0x01, 0xee}, 3, 0, NULL, NULL, "0f 01 ee             \trdpkru"},
+       {{0x0f, 0x01, 0xef}, 3, 0, NULL, NULL, "0f 01 ef             \twrpkru"},
+       {{0}, 0, 0, NULL, NULL, NULL},
+};
+
+static int get_op(const char *op_str)
+{
+       struct val_data {
+               const char *name;
+               int val;
+       } vals[] = {
+               {"other",   INTEL_PT_OP_OTHER},
+               {"call",    INTEL_PT_OP_CALL},
+               {"ret",     INTEL_PT_OP_RET},
+               {"jcc",     INTEL_PT_OP_JCC},
+               {"jmp",     INTEL_PT_OP_JMP},
+               {"loop",    INTEL_PT_OP_LOOP},
+               {"iret",    INTEL_PT_OP_IRET},
+               {"int",     INTEL_PT_OP_INT},
+               {"syscall", INTEL_PT_OP_SYSCALL},
+               {"sysret",  INTEL_PT_OP_SYSRET},
+               {NULL, 0},
+       };
+       struct val_data *val;
+
+       if (!op_str || !strlen(op_str))
+               return 0;
+
+       for (val = vals; val->name; val++) {
+               if (!strcmp(val->name, op_str))
+                       return val->val;
+       }
+
+       pr_debug("Failed to get op\n");
+
+       return -1;
+}
+
+static int get_branch(const char *branch_str)
+{
+       struct val_data {
+               const char *name;
+               int val;
+       } vals[] = {
+               {"no_branch",     INTEL_PT_BR_NO_BRANCH},
+               {"indirect",      INTEL_PT_BR_INDIRECT},
+               {"conditional",   INTEL_PT_BR_CONDITIONAL},
+               {"unconditional", INTEL_PT_BR_UNCONDITIONAL},
+               {NULL, 0},
+       };
+       struct val_data *val;
+
+       if (!branch_str || !strlen(branch_str))
+               return 0;
+
+       for (val = vals; val->name; val++) {
+               if (!strcmp(val->name, branch_str))
+                       return val->val;
+       }
+
+       pr_debug("Failed to get branch\n");
+
+       return -1;
+}
+
+static int test_data_item(struct test_data *dat, int x86_64)
+{
+       struct intel_pt_insn intel_pt_insn;
+       struct insn insn;
+       int op, branch;
+
+       insn_init(&insn, dat->data, MAX_INSN_SIZE, x86_64);
+       insn_get_length(&insn);
+
+       if (!insn_complete(&insn)) {
+               pr_debug("Failed to decode: %s\n", dat->asm_rep);
+               return -1;
+       }
+
+       if (insn.length != dat->expected_length) {
+               pr_debug("Failed to decode length (%d vs expected %d): %s\n",
+                        insn.length, dat->expected_length, dat->asm_rep);
+               return -1;
+       }
+
+       op = get_op(dat->expected_op_str);
+       branch = get_branch(dat->expected_branch_str);
+
+       if (intel_pt_get_insn(dat->data, MAX_INSN_SIZE, x86_64, &intel_pt_insn)) {
+               pr_debug("Intel PT failed to decode: %s\n", dat->asm_rep);
+               return -1;
+       }
+
+       if ((int)intel_pt_insn.op != op) {
+               pr_debug("Failed to decode 'op' value (%d vs expected %d): %s\n",
+                        intel_pt_insn.op, op, dat->asm_rep);
+               return -1;
+       }
+
+       if ((int)intel_pt_insn.branch != branch) {
+               pr_debug("Failed to decode 'branch' value (%d vs expected %d): %s\n",
+                        intel_pt_insn.branch, branch, dat->asm_rep);
+               return -1;
+       }
+
+       if (intel_pt_insn.rel != dat->expected_rel) {
+               pr_debug("Failed to decode 'rel' value (%#x vs expected %#x): %s\n",
+                        intel_pt_insn.rel, dat->expected_rel, dat->asm_rep);
+               return -1;
+       }
+
+       pr_debug("Decoded ok: %s\n", dat->asm_rep);
+
+       return 0;
+}
+
+static int test_data_set(struct test_data *dat_set, int x86_64)
+{
+       struct test_data *dat;
+       int ret = 0;
+
+       for (dat = dat_set; dat->expected_length; dat++) {
+               if (test_data_item(dat, x86_64))
+                       ret = -1;
+       }
+
+       return ret;
+}
+
+/**
+ * test__insn_x86 - test x86 instruction decoder - new instructions.
+ *
+ * This function implements a test that decodes a selection of instructions and
+ * checks the results.  The Intel PT function that further categorizes
+ * instructions (i.e. intel_pt_get_insn()) is also checked.
+ *
+ * The instructions are originally in insn-x86-dat-src.c which has been
+ * processed by scripts gen-insn-x86-dat.sh and gen-insn-x86-dat.awk to produce
+ * insn-x86-dat-32.c and insn-x86-dat-64.c which are included into this program.
+ * i.e. to add new instructions to the test, edit insn-x86-dat-src.c, run the
+ * gen-insn-x86-dat.sh script, make perf, and then run the test.
+ *
+ * If the test passes %0 is returned, otherwise %-1 is returned.  Use the
+ * verbose (-v) option to see all the instructions and whether or not they
+ * decoded successfuly.
+ */
+int test__insn_x86(void)
+{
+       int ret = 0;
+
+       if (test_data_set(test_data_32, 0))
+               ret = -1;
+
+       if (test_data_set(test_data_64, 1))
+               ret = -1;
+
+       return ret;
+}
diff --git a/tools/perf/arch/x86/tests/intel-cqm.c b/tools/perf/arch/x86/tests/intel-cqm.c
new file mode 100644 (file)
index 0000000..d28c1b6
--- /dev/null
@@ -0,0 +1,124 @@
+#include "tests/tests.h"
+#include "perf.h"
+#include "cloexec.h"
+#include "debug.h"
+#include "evlist.h"
+#include "evsel.h"
+#include "arch-tests.h"
+
+#include <sys/mman.h>
+#include <string.h>
+
+static pid_t spawn(void)
+{
+       pid_t pid;
+
+       pid = fork();
+       if (pid)
+               return pid;
+
+       while(1);
+               sleep(5);
+       return 0;
+}
+
+/*
+ * Create an event group that contains both a sampled hardware
+ * (cpu-cycles) and software (intel_cqm/llc_occupancy/) event. We then
+ * wait for the hardware perf counter to overflow and generate a PMI,
+ * which triggers an event read for both of the events in the group.
+ *
+ * Since reading Intel CQM event counters requires sending SMP IPIs, the
+ * CQM pmu needs to handle the above situation gracefully, and return
+ * the last read counter value to avoid triggering a WARN_ON_ONCE() in
+ * smp_call_function_many() caused by sending IPIs from NMI context.
+ */
+int test__intel_cqm_count_nmi_context(void)
+{
+       struct perf_evlist *evlist = NULL;
+       struct perf_evsel *evsel = NULL;
+       struct perf_event_attr pe;
+       int i, fd[2], flag, ret;
+       size_t mmap_len;
+       void *event;
+       pid_t pid;
+       int err = TEST_FAIL;
+
+       flag = perf_event_open_cloexec_flag();
+
+       evlist = perf_evlist__new();
+       if (!evlist) {
+               pr_debug("perf_evlist__new failed\n");
+               return TEST_FAIL;
+       }
+
+       ret = parse_events(evlist, "intel_cqm/llc_occupancy/", NULL);
+       if (ret) {
+               pr_debug("parse_events failed\n");
+               err = TEST_SKIP;
+               goto out;
+       }
+
+       evsel = perf_evlist__first(evlist);
+       if (!evsel) {
+               pr_debug("perf_evlist__first failed\n");
+               goto out;
+       }
+
+       memset(&pe, 0, sizeof(pe));
+       pe.size = sizeof(pe);
+
+       pe.type = PERF_TYPE_HARDWARE;
+       pe.config = PERF_COUNT_HW_CPU_CYCLES;
+       pe.read_format = PERF_FORMAT_GROUP;
+
+       pe.sample_period = 128;
+       pe.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_READ;
+
+       pid = spawn();
+
+       fd[0] = sys_perf_event_open(&pe, pid, -1, -1, flag);
+       if (fd[0] < 0) {
+               pr_debug("failed to open event\n");
+               goto out;
+       }
+
+       memset(&pe, 0, sizeof(pe));
+       pe.size = sizeof(pe);
+
+       pe.type = evsel->attr.type;
+       pe.config = evsel->attr.config;
+
+       fd[1] = sys_perf_event_open(&pe, pid, -1, fd[0], flag);
+       if (fd[1] < 0) {
+               pr_debug("failed to open event\n");
+               goto out;
+       }
+
+       /*
+        * Pick a power-of-two number of pages + 1 for the meta-data
+        * page (struct perf_event_mmap_page). See tools/perf/design.txt.
+        */
+       mmap_len = page_size * 65;
+
+       event = mmap(NULL, mmap_len, PROT_READ, MAP_SHARED, fd[0], 0);
+       if (event == (void *)(-1)) {
+               pr_debug("failed to mmap %d\n", errno);
+               goto out;
+       }
+
+       sleep(1);
+
+       err = TEST_OK;
+
+       munmap(event, mmap_len);
+
+       for (i = 0; i < 2; i++)
+               close(fd[i]);
+
+       kill(pid, SIGKILL);
+       wait(NULL);
+out:
+       perf_evlist__delete(evlist);
+       return err;
+}
similarity index 98%
rename from tools/perf/tests/perf-time-to-tsc.c
rename to tools/perf/arch/x86/tests/perf-time-to-tsc.c
index 5f49484f1abc03bed80d035c508501914a577caf..658cd200af74dca1a1f5165626b0cc51ab517087 100644 (file)
@@ -9,7 +9,9 @@
 #include "thread_map.h"
 #include "cpumap.h"
 #include "tsc.h"
-#include "tests.h"
+#include "tests/tests.h"
+
+#include "arch-tests.h"
 
 #define CHECK__(x) {                           \
        while ((x) < 0) {                       \
similarity index 97%
rename from tools/perf/tests/rdpmc.c
rename to tools/perf/arch/x86/tests/rdpmc.c
index d31f2c4d9f6491c62893f00cfbf46cdeff12ec90..e7688214c7cf19fc040170b2590d19d8e011b6e8 100644 (file)
@@ -5,10 +5,9 @@
 #include <linux/types.h>
 #include "perf.h"
 #include "debug.h"
-#include "tests.h"
+#include "tests/tests.h"
 #include "cloexec.h"
-
-#if defined(__x86_64__) || defined(__i386__)
+#include "arch-tests.h"
 
 static u64 rdpmc(unsigned int counter)
 {
@@ -173,5 +172,3 @@ int test__rdpmc(void)
 
        return 0;
 }
-
-#endif
index a08de0a35b8384ffd4cad3d6c8e08522c0276f59..9223c164e545d869267b9b7a17d409b774dd7904 100644 (file)
  */
 
 #include <stddef.h>
+#include <errno.h> /* for EINVAL */
+#include <string.h> /* for strcmp */
+#include <linux/ptrace.h> /* for struct pt_regs */
+#include <linux/kernel.h> /* for offsetof */
 #include <dwarf-regs.h>
 
 /*
- * Generic dwarf analysis helpers
+ * See arch/x86/kernel/ptrace.c.
+ * Different from it:
+ *
+ *  - Since struct pt_regs is defined differently for user and kernel,
+ *    but we want to use 'ax, bx' instead of 'rax, rbx' (which is struct
+ *    field name of user's pt_regs), we make REG_OFFSET_NAME to accept
+ *    both string name and reg field name.
+ *
+ *  - Since accessing x86_32's pt_regs from x86_64 building is difficult
+ *    and vise versa, we simply fill offset with -1, so
+ *    get_arch_regstr() still works but regs_query_register_offset()
+ *    returns error.
+ *    The only inconvenience caused by it now is that we are not allowed
+ *    to generate BPF prologue for a x86_64 kernel if perf is built for
+ *    x86_32. This is really a rare usecase.
+ *
+ *  - Order is different from kernel's ptrace.c for get_arch_regstr(). Use
+ *    the order defined by dwarf.
  */
 
-#define X86_32_MAX_REGS 8
-const char *x86_32_regs_table[X86_32_MAX_REGS] = {
-       "%ax",
-       "%cx",
-       "%dx",
-       "%bx",
-       "$stack",       /* Stack address instead of %sp */
-       "%bp",
-       "%si",
-       "%di",
+struct pt_regs_offset {
+       const char *name;
+       int offset;
+};
+
+#define REG_OFFSET_END {.name = NULL, .offset = 0}
+
+#ifdef __x86_64__
+# define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)}
+# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = -1}
+#else
+# define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = -1}
+# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)}
+#endif
+
+static const struct pt_regs_offset x86_32_regoffset_table[] = {
+       REG_OFFSET_NAME_32("%ax",       eax),
+       REG_OFFSET_NAME_32("%cx",       ecx),
+       REG_OFFSET_NAME_32("%dx",       edx),
+       REG_OFFSET_NAME_32("%bx",       ebx),
+       REG_OFFSET_NAME_32("$stack",    esp),   /* Stack address instead of %sp */
+       REG_OFFSET_NAME_32("%bp",       ebp),
+       REG_OFFSET_NAME_32("%si",       esi),
+       REG_OFFSET_NAME_32("%di",       edi),
+       REG_OFFSET_END,
 };
 
-#define X86_64_MAX_REGS 16
-const char *x86_64_regs_table[X86_64_MAX_REGS] = {
-       "%ax",
-       "%dx",
-       "%cx",
-       "%bx",
-       "%si",
-       "%di",
-       "%bp",
-       "%sp",
-       "%r8",
-       "%r9",
-       "%r10",
-       "%r11",
-       "%r12",
-       "%r13",
-       "%r14",
-       "%r15",
+static const struct pt_regs_offset x86_64_regoffset_table[] = {
+       REG_OFFSET_NAME_64("%ax",       rax),
+       REG_OFFSET_NAME_64("%dx",       rdx),
+       REG_OFFSET_NAME_64("%cx",       rcx),
+       REG_OFFSET_NAME_64("%bx",       rbx),
+       REG_OFFSET_NAME_64("%si",       rsi),
+       REG_OFFSET_NAME_64("%di",       rdi),
+       REG_OFFSET_NAME_64("%bp",       rbp),
+       REG_OFFSET_NAME_64("%sp",       rsp),
+       REG_OFFSET_NAME_64("%r8",       r8),
+       REG_OFFSET_NAME_64("%r9",       r9),
+       REG_OFFSET_NAME_64("%r10",      r10),
+       REG_OFFSET_NAME_64("%r11",      r11),
+       REG_OFFSET_NAME_64("%r12",      r12),
+       REG_OFFSET_NAME_64("%r13",      r13),
+       REG_OFFSET_NAME_64("%r14",      r14),
+       REG_OFFSET_NAME_64("%r15",      r15),
+       REG_OFFSET_END,
 };
 
 /* TODO: switching by dwarf address size */
 #ifdef __x86_64__
-#define ARCH_MAX_REGS X86_64_MAX_REGS
-#define arch_regs_table x86_64_regs_table
+#define regoffset_table x86_64_regoffset_table
 #else
-#define ARCH_MAX_REGS X86_32_MAX_REGS
-#define arch_regs_table x86_32_regs_table
+#define regoffset_table x86_32_regoffset_table
 #endif
 
+/* Minus 1 for the ending REG_OFFSET_END */
+#define ARCH_MAX_REGS ((sizeof(regoffset_table) / sizeof(regoffset_table[0])) - 1)
+
 /* Return architecture dependent register string (for kprobe-tracer) */
 const char *get_arch_regstr(unsigned int n)
 {
-       return (n < ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
+       return (n < ARCH_MAX_REGS) ? regoffset_table[n].name : NULL;
+}
+
+/* Reuse code from arch/x86/kernel/ptrace.c */
+/**
+ * regs_query_register_offset() - query register offset from its name
+ * @name:      the name of a register
+ *
+ * regs_query_register_offset() returns the offset of a register in struct
+ * pt_regs from its name. If the name is invalid, this returns -EINVAL;
+ */
+int regs_query_register_offset(const char *name)
+{
+       const struct pt_regs_offset *roff;
+       for (roff = regoffset_table; roff->name != NULL; roff++)
+               if (!strcmp(roff->name, name))
+                       return roff->offset;
+       return -EINVAL;
 }
index 2ca10d796c0bb5bf0b591a070a5835dfd76fa700..b02af064f0f98333b6f90a5d6dd8778e6a7a6bb4 100644 (file)
@@ -624,13 +624,49 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
         * threads.
         */
        if (have_timing_info && !cpu_map__empty(cpus)) {
-               err = intel_pt_track_switches(evlist);
-               if (err == -EPERM)
-                       pr_debug2("Unable to select sched:sched_switch\n");
-               else if (err)
-                       return err;
-               else
-                       ptr->have_sched_switch = 1;
+               if (perf_can_record_switch_events()) {
+                       bool cpu_wide = !target__none(&opts->target) &&
+                                       !target__has_task(&opts->target);
+
+                       if (!cpu_wide && perf_can_record_cpu_wide()) {
+                               struct perf_evsel *switch_evsel;
+
+                               err = parse_events(evlist, "dummy:u", NULL);
+                               if (err)
+                                       return err;
+
+                               switch_evsel = perf_evlist__last(evlist);
+
+                               switch_evsel->attr.freq = 0;
+                               switch_evsel->attr.sample_period = 1;
+                               switch_evsel->attr.context_switch = 1;
+
+                               switch_evsel->system_wide = true;
+                               switch_evsel->no_aux_samples = true;
+                               switch_evsel->immediate = true;
+
+                               perf_evsel__set_sample_bit(switch_evsel, TID);
+                               perf_evsel__set_sample_bit(switch_evsel, TIME);
+                               perf_evsel__set_sample_bit(switch_evsel, CPU);
+
+                               opts->record_switch_events = false;
+                               ptr->have_sched_switch = 3;
+                       } else {
+                               opts->record_switch_events = true;
+                               if (cpu_wide)
+                                       ptr->have_sched_switch = 3;
+                               else
+                                       ptr->have_sched_switch = 2;
+                       }
+               } else {
+                       err = intel_pt_track_switches(evlist);
+                       if (err == -EPERM)
+                               pr_debug2("Unable to select sched:sched_switch\n");
+                       else if (err)
+                               return err;
+                       else
+                               ptr->have_sched_switch = 1;
+               }
        }
 
        if (intel_pt_evsel) {
@@ -663,8 +699,11 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
                tracking_evsel->attr.sample_period = 1;
 
                /* In per-cpu case, always need the time of mmap events etc */
-               if (!cpu_map__empty(cpus))
+               if (!cpu_map__empty(cpus)) {
                        perf_evsel__set_sample_bit(tracking_evsel, TIME);
+                       /* And the CPU for switch events */
+                       perf_evsel__set_sample_bit(tracking_evsel, CPU);
+               }
        }
 
        /*
index 573e28896038d2f9045eb1eb700e77cbe5c66cbe..60bf119430479272a80c17e4231de4633bad2b6e 100644 (file)
@@ -1,6 +1,6 @@
 perf-y += sched-messaging.o
 perf-y += sched-pipe.o
-perf-y += mem-memcpy.o
+perf-y += mem-functions.o
 perf-y += futex-hash.o
 perf-y += futex-wake.o
 perf-y += futex-wake-parallel.o
diff --git a/tools/perf/bench/mem-functions.c b/tools/perf/bench/mem-functions.c
new file mode 100644 (file)
index 0000000..9419b94
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * mem-memcpy.c
+ *
+ * Simple memcpy() and memset() benchmarks
+ *
+ * Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ */
+
+#include "../perf.h"
+#include "../util/util.h"
+#include "../util/parse-options.h"
+#include "../util/header.h"
+#include "../util/cloexec.h"
+#include "bench.h"
+#include "mem-memcpy-arch.h"
+#include "mem-memset-arch.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#define K 1024
+
+static const char      *size_str       = "1MB";
+static const char      *function_str   = "all";
+static int             nr_loops        = 1;
+static bool            use_cycles;
+static int             cycles_fd;
+
+static const struct option options[] = {
+       OPT_STRING('s', "size", &size_str, "1MB",
+                   "Specify the size of the memory buffers. "
+                   "Available units: B, KB, MB, GB and TB (case insensitive)"),
+
+       OPT_STRING('f', "function", &function_str, "all",
+                   "Specify the function to run, \"all\" runs all available functions, \"help\" lists them"),
+
+       OPT_INTEGER('l', "nr_loops", &nr_loops,
+                   "Specify the number of loops to run. (default: 1)"),
+
+       OPT_BOOLEAN('c', "cycles", &use_cycles,
+                   "Use a cycles event instead of gettimeofday() to measure performance"),
+
+       OPT_END()
+};
+
+typedef void *(*memcpy_t)(void *, const void *, size_t);
+typedef void *(*memset_t)(void *, int, size_t);
+
+struct function {
+       const char *name;
+       const char *desc;
+       union {
+               memcpy_t memcpy;
+               memset_t memset;
+       } fn;
+};
+
+static struct perf_event_attr cycle_attr = {
+       .type           = PERF_TYPE_HARDWARE,
+       .config         = PERF_COUNT_HW_CPU_CYCLES
+};
+
+static void init_cycles(void)
+{
+       cycles_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, perf_event_open_cloexec_flag());
+
+       if (cycles_fd < 0 && errno == ENOSYS)
+               die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
+       else
+               BUG_ON(cycles_fd < 0);
+}
+
+static u64 get_cycles(void)
+{
+       int ret;
+       u64 clk;
+
+       ret = read(cycles_fd, &clk, sizeof(u64));
+       BUG_ON(ret != sizeof(u64));
+
+       return clk;
+}
+
+static double timeval2double(struct timeval *ts)
+{
+       return (double)ts->tv_sec + (double)ts->tv_usec / (double)1000000;
+}
+
+#define print_bps(x) do {                                              \
+               if (x < K)                                              \
+                       printf(" %14lf bytes/sec\n", x);                \
+               else if (x < K * K)                                     \
+                       printf(" %14lfd KB/sec\n", x / K);              \
+               else if (x < K * K * K)                                 \
+                       printf(" %14lf MB/sec\n", x / K / K);           \
+               else                                                    \
+                       printf(" %14lf GB/sec\n", x / K / K / K);       \
+       } while (0)
+
+struct bench_mem_info {
+       const struct function *functions;
+       u64 (*do_cycles)(const struct function *r, size_t size);
+       double (*do_gettimeofday)(const struct function *r, size_t size);
+       const char *const *usage;
+};
+
+static void __bench_mem_function(struct bench_mem_info *info, int r_idx, size_t size, double size_total)
+{
+       const struct function *r = &info->functions[r_idx];
+       double result_bps = 0.0;
+       u64 result_cycles = 0;
+
+       printf("# function '%s' (%s)\n", r->name, r->desc);
+
+       if (bench_format == BENCH_FORMAT_DEFAULT)
+               printf("# Copying %s bytes ...\n\n", size_str);
+
+       if (use_cycles) {
+               result_cycles = info->do_cycles(r, size);
+       } else {
+               result_bps = info->do_gettimeofday(r, size);
+       }
+
+       switch (bench_format) {
+       case BENCH_FORMAT_DEFAULT:
+               if (use_cycles) {
+                       printf(" %14lf cycles/byte\n", (double)result_cycles/size_total);
+               } else {
+                       print_bps(result_bps);
+               }
+               break;
+
+       case BENCH_FORMAT_SIMPLE:
+               if (use_cycles) {
+                       printf("%lf\n", (double)result_cycles/size_total);
+               } else {
+                       printf("%lf\n", result_bps);
+               }
+               break;
+
+       default:
+               BUG_ON(1);
+               break;
+       }
+}
+
+static int bench_mem_common(int argc, const char **argv, struct bench_mem_info *info)
+{
+       int i;
+       size_t size;
+       double size_total;
+
+       argc = parse_options(argc, argv, options, info->usage, 0);
+
+       if (use_cycles)
+               init_cycles();
+
+       size = (size_t)perf_atoll((char *)size_str);
+       size_total = (double)size * nr_loops;
+
+       if ((s64)size <= 0) {
+               fprintf(stderr, "Invalid size:%s\n", size_str);
+               return 1;
+       }
+
+       if (!strncmp(function_str, "all", 3)) {
+               for (i = 0; info->functions[i].name; i++)
+                       __bench_mem_function(info, i, size, size_total);
+               return 0;
+       }
+
+       for (i = 0; info->functions[i].name; i++) {
+               if (!strcmp(info->functions[i].name, function_str))
+                       break;
+       }
+       if (!info->functions[i].name) {
+               if (strcmp(function_str, "help") && strcmp(function_str, "h"))
+                       printf("Unknown function: %s\n", function_str);
+               printf("Available functions:\n");
+               for (i = 0; info->functions[i].name; i++) {
+                       printf("\t%s ... %s\n",
+                              info->functions[i].name, info->functions[i].desc);
+               }
+               return 1;
+       }
+
+       __bench_mem_function(info, i, size, size_total);
+
+       return 0;
+}
+
+static void memcpy_alloc_mem(void **dst, void **src, size_t size)
+{
+       *dst = zalloc(size);
+       if (!*dst)
+               die("memory allocation failed - maybe size is too large?\n");
+
+       *src = zalloc(size);
+       if (!*src)
+               die("memory allocation failed - maybe size is too large?\n");
+
+       /* Make sure to always prefault zero pages even if MMAP_THRESH is crossed: */
+       memset(*src, 0, size);
+}
+
+static u64 do_memcpy_cycles(const struct function *r, size_t size)
+{
+       u64 cycle_start = 0ULL, cycle_end = 0ULL;
+       void *src = NULL, *dst = NULL;
+       memcpy_t fn = r->fn.memcpy;
+       int i;
+
+       memcpy_alloc_mem(&dst, &src, size);
+
+       /*
+        * We prefault the freshly allocated memory range here,
+        * to not measure page fault overhead:
+        */
+       fn(dst, src, size);
+
+       cycle_start = get_cycles();
+       for (i = 0; i < nr_loops; ++i)
+               fn(dst, src, size);
+       cycle_end = get_cycles();
+
+       free(src);
+       free(dst);
+       return cycle_end - cycle_start;
+}
+
+static double do_memcpy_gettimeofday(const struct function *r, size_t size)
+{
+       struct timeval tv_start, tv_end, tv_diff;
+       memcpy_t fn = r->fn.memcpy;
+       void *src = NULL, *dst = NULL;
+       int i;
+
+       memcpy_alloc_mem(&dst, &src, size);
+
+       /*
+        * We prefault the freshly allocated memory range here,
+        * to not measure page fault overhead:
+        */
+       fn(dst, src, size);
+
+       BUG_ON(gettimeofday(&tv_start, NULL));
+       for (i = 0; i < nr_loops; ++i)
+               fn(dst, src, size);
+       BUG_ON(gettimeofday(&tv_end, NULL));
+
+       timersub(&tv_end, &tv_start, &tv_diff);
+
+       free(src);
+       free(dst);
+
+       return (double)(((double)size * nr_loops) / timeval2double(&tv_diff));
+}
+
+struct function memcpy_functions[] = {
+       { .name         = "default",
+         .desc         = "Default memcpy() provided by glibc",
+         .fn.memcpy    = memcpy },
+
+#ifdef HAVE_ARCH_X86_64_SUPPORT
+# define MEMCPY_FN(_fn, _name, _desc) {.name = _name, .desc = _desc, .fn.memcpy = _fn},
+# include "mem-memcpy-x86-64-asm-def.h"
+# undef MEMCPY_FN
+#endif
+
+       { .name = NULL, }
+};
+
+static const char * const bench_mem_memcpy_usage[] = {
+       "perf bench mem memcpy <options>",
+       NULL
+};
+
+int bench_mem_memcpy(int argc, const char **argv, const char *prefix __maybe_unused)
+{
+       struct bench_mem_info info = {
+               .functions              = memcpy_functions,
+               .do_cycles              = do_memcpy_cycles,
+               .do_gettimeofday        = do_memcpy_gettimeofday,
+               .usage                  = bench_mem_memcpy_usage,
+       };
+
+       return bench_mem_common(argc, argv, &info);
+}
+
+static void memset_alloc_mem(void **dst, size_t size)
+{
+       *dst = zalloc(size);
+       if (!*dst)
+               die("memory allocation failed - maybe size is too large?\n");
+}
+
+static u64 do_memset_cycles(const struct function *r, size_t size)
+{
+       u64 cycle_start = 0ULL, cycle_end = 0ULL;
+       memset_t fn = r->fn.memset;
+       void *dst = NULL;
+       int i;
+
+       memset_alloc_mem(&dst, size);
+
+       /*
+        * We prefault the freshly allocated memory range here,
+        * to not measure page fault overhead:
+        */
+       fn(dst, -1, size);
+
+       cycle_start = get_cycles();
+       for (i = 0; i < nr_loops; ++i)
+               fn(dst, i, size);
+       cycle_end = get_cycles();
+
+       free(dst);
+       return cycle_end - cycle_start;
+}
+
+static double do_memset_gettimeofday(const struct function *r, size_t size)
+{
+       struct timeval tv_start, tv_end, tv_diff;
+       memset_t fn = r->fn.memset;
+       void *dst = NULL;
+       int i;
+
+       memset_alloc_mem(&dst, size);
+
+       /*
+        * We prefault the freshly allocated memory range here,
+        * to not measure page fault overhead:
+        */
+       fn(dst, -1, size);
+
+       BUG_ON(gettimeofday(&tv_start, NULL));
+       for (i = 0; i < nr_loops; ++i)
+               fn(dst, i, size);
+       BUG_ON(gettimeofday(&tv_end, NULL));
+
+       timersub(&tv_end, &tv_start, &tv_diff);
+
+       free(dst);
+       return (double)(((double)size * nr_loops) / timeval2double(&tv_diff));
+}
+
+static const char * const bench_mem_memset_usage[] = {
+       "perf bench mem memset <options>",
+       NULL
+};
+
+static const struct function memset_functions[] = {
+       { .name         = "default",
+         .desc         = "Default memset() provided by glibc",
+         .fn.memset    = memset },
+
+#ifdef HAVE_ARCH_X86_64_SUPPORT
+# define MEMSET_FN(_fn, _name, _desc) { .name = _name, .desc = _desc, .fn.memset = _fn },
+# include "mem-memset-x86-64-asm-def.h"
+# undef MEMSET_FN
+#endif
+
+       { .name = NULL, }
+};
+
+int bench_mem_memset(int argc, const char **argv, const char *prefix __maybe_unused)
+{
+       struct bench_mem_info info = {
+               .functions              = memset_functions,
+               .do_cycles              = do_memset_cycles,
+               .do_gettimeofday        = do_memset_gettimeofday,
+               .usage                  = bench_mem_memset_usage,
+       };
+
+       return bench_mem_common(argc, argv, &info);
+}
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c
deleted file mode 100644 (file)
index d3dfb79..0000000
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * mem-memcpy.c
- *
- * memcpy: Simple memory copy in various ways
- *
- * Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
- */
-
-#include "../perf.h"
-#include "../util/util.h"
-#include "../util/parse-options.h"
-#include "../util/header.h"
-#include "../util/cloexec.h"
-#include "bench.h"
-#include "mem-memcpy-arch.h"
-#include "mem-memset-arch.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-#include <errno.h>
-
-#define K 1024
-
-static const char      *length_str     = "1MB";
-static const char      *routine        = "default";
-static int             iterations      = 1;
-static bool            use_cycle;
-static int             cycle_fd;
-static bool            only_prefault;
-static bool            no_prefault;
-
-static const struct option options[] = {
-       OPT_STRING('l', "length", &length_str, "1MB",
-                   "Specify length of memory to copy. "
-                   "Available units: B, KB, MB, GB and TB (upper and lower)"),
-       OPT_STRING('r', "routine", &routine, "default",
-                   "Specify routine to copy, \"all\" runs all available routines"),
-       OPT_INTEGER('i', "iterations", &iterations,
-                   "repeat memcpy() invocation this number of times"),
-       OPT_BOOLEAN('c', "cycle", &use_cycle,
-                   "Use cycles event instead of gettimeofday() for measuring"),
-       OPT_BOOLEAN('o', "only-prefault", &only_prefault,
-                   "Show only the result with page faults before memcpy()"),
-       OPT_BOOLEAN('n', "no-prefault", &no_prefault,
-                   "Show only the result without page faults before memcpy()"),
-       OPT_END()
-};
-
-typedef void *(*memcpy_t)(void *, const void *, size_t);
-typedef void *(*memset_t)(void *, int, size_t);
-
-struct routine {
-       const char *name;
-       const char *desc;
-       union {
-               memcpy_t memcpy;
-               memset_t memset;
-       } fn;
-};
-
-struct routine memcpy_routines[] = {
-       { .name = "default",
-         .desc = "Default memcpy() provided by glibc",
-         .fn.memcpy = memcpy },
-#ifdef HAVE_ARCH_X86_64_SUPPORT
-
-#define MEMCPY_FN(_fn, _name, _desc) {.name = _name, .desc = _desc, .fn.memcpy = _fn},
-#include "mem-memcpy-x86-64-asm-def.h"
-#undef MEMCPY_FN
-
-#endif
-
-       { NULL,
-         NULL,
-         {NULL}   }
-};
-
-static const char * const bench_mem_memcpy_usage[] = {
-       "perf bench mem memcpy <options>",
-       NULL
-};
-
-static struct perf_event_attr cycle_attr = {
-       .type           = PERF_TYPE_HARDWARE,
-       .config         = PERF_COUNT_HW_CPU_CYCLES
-};
-
-static void init_cycle(void)
-{
-       cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1,
-                                      perf_event_open_cloexec_flag());
-
-       if (cycle_fd < 0 && errno == ENOSYS)
-               die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
-       else
-               BUG_ON(cycle_fd < 0);
-}
-
-static u64 get_cycle(void)
-{
-       int ret;
-       u64 clk;
-
-       ret = read(cycle_fd, &clk, sizeof(u64));
-       BUG_ON(ret != sizeof(u64));
-
-       return clk;
-}
-
-static double timeval2double(struct timeval *ts)
-{
-       return (double)ts->tv_sec +
-               (double)ts->tv_usec / (double)1000000;
-}
-
-#define pf (no_prefault ? 0 : 1)
-
-#define print_bps(x) do {                                      \
-               if (x < K)                                      \
-                       printf(" %14lf B/Sec", x);              \
-               else if (x < K * K)                             \
-                       printf(" %14lfd KB/Sec", x / K);        \
-               else if (x < K * K * K)                         \
-                       printf(" %14lf MB/Sec", x / K / K);     \
-               else                                            \
-                       printf(" %14lf GB/Sec", x / K / K / K); \
-       } while (0)
-
-struct bench_mem_info {
-       const struct routine *routines;
-       u64 (*do_cycle)(const struct routine *r, size_t len, bool prefault);
-       double (*do_gettimeofday)(const struct routine *r, size_t len, bool prefault);
-       const char *const *usage;
-};
-
-static void __bench_mem_routine(struct bench_mem_info *info, int r_idx, size_t len, double totallen)
-{
-       const struct routine *r = &info->routines[r_idx];
-       double result_bps[2];
-       u64 result_cycle[2];
-
-       result_cycle[0] = result_cycle[1] = 0ULL;
-       result_bps[0] = result_bps[1] = 0.0;
-
-       printf("Routine %s (%s)\n", r->name, r->desc);
-
-       if (bench_format == BENCH_FORMAT_DEFAULT)
-               printf("# Copying %s Bytes ...\n\n", length_str);
-
-       if (!only_prefault && !no_prefault) {
-               /* show both of results */
-               if (use_cycle) {
-                       result_cycle[0] = info->do_cycle(r, len, false);
-                       result_cycle[1] = info->do_cycle(r, len, true);
-               } else {
-                       result_bps[0]   = info->do_gettimeofday(r, len, false);
-                       result_bps[1]   = info->do_gettimeofday(r, len, true);
-               }
-       } else {
-               if (use_cycle)
-                       result_cycle[pf] = info->do_cycle(r, len, only_prefault);
-               else
-                       result_bps[pf] = info->do_gettimeofday(r, len, only_prefault);
-       }
-
-       switch (bench_format) {
-       case BENCH_FORMAT_DEFAULT:
-               if (!only_prefault && !no_prefault) {
-                       if (use_cycle) {
-                               printf(" %14lf Cycle/Byte\n",
-                                       (double)result_cycle[0]
-                                       / totallen);
-                               printf(" %14lf Cycle/Byte (with prefault)\n",
-                                       (double)result_cycle[1]
-                                       / totallen);
-                       } else {
-                               print_bps(result_bps[0]);
-                               printf("\n");
-                               print_bps(result_bps[1]);
-                               printf(" (with prefault)\n");
-                       }
-               } else {
-                       if (use_cycle) {
-                               printf(" %14lf Cycle/Byte",
-                                       (double)result_cycle[pf]
-                                       / totallen);
-                       } else
-                               print_bps(result_bps[pf]);
-
-                       printf("%s\n", only_prefault ? " (with prefault)" : "");
-               }
-               break;
-       case BENCH_FORMAT_SIMPLE:
-               if (!only_prefault && !no_prefault) {
-                       if (use_cycle) {
-                               printf("%lf %lf\n",
-                                       (double)result_cycle[0] / totallen,
-                                       (double)result_cycle[1] / totallen);
-                       } else {
-                               printf("%lf %lf\n",
-                                       result_bps[0], result_bps[1]);
-                       }
-               } else {
-                       if (use_cycle) {
-                               printf("%lf\n", (double)result_cycle[pf]
-                                       / totallen);
-                       } else
-                               printf("%lf\n", result_bps[pf]);
-               }
-               break;
-       default:
-               /* reaching this means there's some disaster: */
-               die("unknown format: %d\n", bench_format);
-               break;
-       }
-}
-
-static int bench_mem_common(int argc, const char **argv,
-                    const char *prefix __maybe_unused,
-                    struct bench_mem_info *info)
-{
-       int i;
-       size_t len;
-       double totallen;
-
-       argc = parse_options(argc, argv, options,
-                            info->usage, 0);
-
-       if (no_prefault && only_prefault) {
-               fprintf(stderr, "Invalid options: -o and -n are mutually exclusive\n");
-               return 1;
-       }
-
-       if (use_cycle)
-               init_cycle();
-
-       len = (size_t)perf_atoll((char *)length_str);
-       totallen = (double)len * iterations;
-
-       if ((s64)len <= 0) {
-               fprintf(stderr, "Invalid length:%s\n", length_str);
-               return 1;
-       }
-
-       /* same to without specifying either of prefault and no-prefault */
-       if (only_prefault && no_prefault)
-               only_prefault = no_prefault = false;
-
-       if (!strncmp(routine, "all", 3)) {
-               for (i = 0; info->routines[i].name; i++)
-                       __bench_mem_routine(info, i, len, totallen);
-               return 0;
-       }
-
-       for (i = 0; info->routines[i].name; i++) {
-               if (!strcmp(info->routines[i].name, routine))
-                       break;
-       }
-       if (!info->routines[i].name) {
-               printf("Unknown routine:%s\n", routine);
-               printf("Available routines...\n");
-               for (i = 0; info->routines[i].name; i++) {
-                       printf("\t%s ... %s\n",
-                              info->routines[i].name, info->routines[i].desc);
-               }
-               return 1;
-       }
-
-       __bench_mem_routine(info, i, len, totallen);
-
-       return 0;
-}
-
-static void memcpy_alloc_mem(void **dst, void **src, size_t length)
-{
-       *dst = zalloc(length);
-       if (!*dst)
-               die("memory allocation failed - maybe length is too large?\n");
-
-       *src = zalloc(length);
-       if (!*src)
-               die("memory allocation failed - maybe length is too large?\n");
-       /* Make sure to always replace the zero pages even if MMAP_THRESH is crossed */
-       memset(*src, 0, length);
-}
-
-static u64 do_memcpy_cycle(const struct routine *r, size_t len, bool prefault)
-{
-       u64 cycle_start = 0ULL, cycle_end = 0ULL;
-       void *src = NULL, *dst = NULL;
-       memcpy_t fn = r->fn.memcpy;
-       int i;
-
-       memcpy_alloc_mem(&dst, &src, len);
-
-       if (prefault)
-               fn(dst, src, len);
-
-       cycle_start = get_cycle();
-       for (i = 0; i < iterations; ++i)
-               fn(dst, src, len);
-       cycle_end = get_cycle();
-
-       free(src);
-       free(dst);
-       return cycle_end - cycle_start;
-}
-
-static double do_memcpy_gettimeofday(const struct routine *r, size_t len,
-                                    bool prefault)
-{
-       struct timeval tv_start, tv_end, tv_diff;
-       memcpy_t fn = r->fn.memcpy;
-       void *src = NULL, *dst = NULL;
-       int i;
-
-       memcpy_alloc_mem(&dst, &src, len);
-
-       if (prefault)
-               fn(dst, src, len);
-
-       BUG_ON(gettimeofday(&tv_start, NULL));
-       for (i = 0; i < iterations; ++i)
-               fn(dst, src, len);
-       BUG_ON(gettimeofday(&tv_end, NULL));
-
-       timersub(&tv_end, &tv_start, &tv_diff);
-
-       free(src);
-       free(dst);
-       return (double)(((double)len * iterations) / timeval2double(&tv_diff));
-}
-
-int bench_mem_memcpy(int argc, const char **argv,
-                    const char *prefix __maybe_unused)
-{
-       struct bench_mem_info info = {
-               .routines = memcpy_routines,
-               .do_cycle = do_memcpy_cycle,
-               .do_gettimeofday = do_memcpy_gettimeofday,
-               .usage = bench_mem_memcpy_usage,
-       };
-
-       return bench_mem_common(argc, argv, prefix, &info);
-}
-
-static void memset_alloc_mem(void **dst, size_t length)
-{
-       *dst = zalloc(length);
-       if (!*dst)
-               die("memory allocation failed - maybe length is too large?\n");
-}
-
-static u64 do_memset_cycle(const struct routine *r, size_t len, bool prefault)
-{
-       u64 cycle_start = 0ULL, cycle_end = 0ULL;
-       memset_t fn = r->fn.memset;
-       void *dst = NULL;
-       int i;
-
-       memset_alloc_mem(&dst, len);
-
-       if (prefault)
-               fn(dst, -1, len);
-
-       cycle_start = get_cycle();
-       for (i = 0; i < iterations; ++i)
-               fn(dst, i, len);
-       cycle_end = get_cycle();
-
-       free(dst);
-       return cycle_end - cycle_start;
-}
-
-static double do_memset_gettimeofday(const struct routine *r, size_t len,
-                                    bool prefault)
-{
-       struct timeval tv_start, tv_end, tv_diff;
-       memset_t fn = r->fn.memset;
-       void *dst = NULL;
-       int i;
-
-       memset_alloc_mem(&dst, len);
-
-       if (prefault)
-               fn(dst, -1, len);
-
-       BUG_ON(gettimeofday(&tv_start, NULL));
-       for (i = 0; i < iterations; ++i)
-               fn(dst, i, len);
-       BUG_ON(gettimeofday(&tv_end, NULL));
-
-       timersub(&tv_end, &tv_start, &tv_diff);
-
-       free(dst);
-       return (double)(((double)len * iterations) / timeval2double(&tv_diff));
-}
-
-static const char * const bench_mem_memset_usage[] = {
-       "perf bench mem memset <options>",
-       NULL
-};
-
-static const struct routine memset_routines[] = {
-       { .name ="default",
-         .desc = "Default memset() provided by glibc",
-         .fn.memset = memset },
-#ifdef HAVE_ARCH_X86_64_SUPPORT
-
-#define MEMSET_FN(_fn, _name, _desc) { .name = _name, .desc = _desc, .fn.memset = _fn },
-#include "mem-memset-x86-64-asm-def.h"
-#undef MEMSET_FN
-
-#endif
-
-       { .name = NULL,
-         .desc = NULL,
-         .fn.memset = NULL   }
-};
-
-int bench_mem_memset(int argc, const char **argv,
-                    const char *prefix __maybe_unused)
-{
-       struct bench_mem_info info = {
-               .routines = memset_routines,
-               .do_cycle = do_memset_cycle,
-               .do_gettimeofday = do_memset_gettimeofday,
-               .usage = bench_mem_memset_usage,
-       };
-
-       return bench_mem_common(argc, argv, prefix, &info);
-}
index 870b7e665a203264c1b7b27684a860cbe147c450..492df2752a2d1057bbde642538f84da93aa9df8b 100644 (file)
@@ -164,8 +164,8 @@ static const struct option options[] = {
        OPT_STRING('L', "mb_proc_locked", &p0.mb_proc_locked_str,"MB", "process serialized/locked memory access (MBs), <= process_memory"),
        OPT_STRING('T', "mb_thread"     , &p0.mb_thread_str,    "MB", "thread  memory (MBs)"),
 
-       OPT_UINTEGER('l', "nr_loops"    , &p0.nr_loops,         "max number of loops to run"),
-       OPT_UINTEGER('s', "nr_secs"     , &p0.nr_secs,          "max number of seconds to run"),
+       OPT_UINTEGER('l', "nr_loops"    , &p0.nr_loops,         "max number of loops to run (default: unlimited)"),
+       OPT_UINTEGER('s', "nr_secs"     , &p0.nr_secs,          "max number of seconds to run (default: 5 secs)"),
        OPT_UINTEGER('u', "usleep"      , &p0.sleep_usecs,      "usecs to sleep per loop iteration"),
 
        OPT_BOOLEAN('R', "data_reads"   , &p0.data_reads,       "access the data via writes (can be mixed with -W)"),
index d7f281c2828d97e35bb1f007f2f792666e89b78b..d4ff1b539cfd2727e0488858cf241aa83fe6c037 100644 (file)
@@ -33,7 +33,7 @@
 #define DATASIZE 100
 
 static bool use_pipes = false;
-static unsigned int loops = 100;
+static unsigned int nr_loops = 100;
 static bool thread_mode = false;
 static unsigned int num_groups = 10;
 
@@ -79,7 +79,7 @@ static void ready(int ready_out, int wakefd)
                err(EXIT_FAILURE, "poll");
 }
 
-/* Sender sprays loops messages down each file descriptor */
+/* Sender sprays nr_loops messages down each file descriptor */
 static void *sender(struct sender_context *ctx)
 {
        char data[DATASIZE];
@@ -88,7 +88,7 @@ static void *sender(struct sender_context *ctx)
        ready(ctx->ready_out, ctx->wakefd);
 
        /* Now pump to every receiver. */
-       for (i = 0; i < loops; i++) {
+       for (i = 0; i < nr_loops; i++) {
                for (j = 0; j < ctx->num_fds; j++) {
                        int ret, done = 0;
 
@@ -213,7 +213,7 @@ static unsigned int group(pthread_t *pth,
                /* Create the pipe between client and server */
                fdpair(fds);
 
-               ctx->num_packets = num_fds * loops;
+               ctx->num_packets = num_fds * nr_loops;
                ctx->in_fds[0] = fds[0];
                ctx->in_fds[1] = fds[1];
                ctx->ready_out = ready_out;
@@ -250,7 +250,7 @@ static const struct option options[] = {
        OPT_BOOLEAN('t', "thread", &thread_mode,
                    "Be multi thread instead of multi process"),
        OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"),
-       OPT_UINTEGER('l', "loop", &loops, "Specify number of loops"),
+       OPT_UINTEGER('l', "nr_loops", &nr_loops, "Specify the number of loops to run (default: 100)"),
        OPT_END()
 };
 
index 8edc205ff9a7fe41097f8ff72338079516f286e2..2bf9b3fd9e61546fb8fd58ff9a43f7c5106e2778 100644 (file)
@@ -211,7 +211,7 @@ static int __cmd_annotate(struct perf_annotate *ann)
        }
 
        if (!objdump_path) {
-               ret = perf_session_env__lookup_objdump(&session->header.env);
+               ret = perf_env__lookup_objdump(&session->header.env);
                if (ret)
                        goto out;
        }
index f67934d46d4098773a746f0039f1c3d1f1097097..b17aed36ca16207915ab935ecceea426e49a9ec6 100644 (file)
@@ -36,7 +36,7 @@ struct bench {
 #ifdef HAVE_LIBNUMA_SUPPORT
 static struct bench numa_benchmarks[] = {
        { "mem",        "Benchmark for NUMA workloads",                 bench_numa              },
-       { "all",        "Test all NUMA benchmarks",                     NULL                    },
+       { "all",        "Run all NUMA benchmarks",                      NULL                    },
        { NULL,         NULL,                                           NULL                    }
 };
 #endif
@@ -44,14 +44,14 @@ static struct bench numa_benchmarks[] = {
 static struct bench sched_benchmarks[] = {
        { "messaging",  "Benchmark for scheduling and IPC",             bench_sched_messaging   },
        { "pipe",       "Benchmark for pipe() between two processes",   bench_sched_pipe        },
-       { "all",        "Test all scheduler benchmarks",                NULL                    },
+       { "all",        "Run all scheduler benchmarks",         NULL                    },
        { NULL,         NULL,                                           NULL                    }
 };
 
 static struct bench mem_benchmarks[] = {
-       { "memcpy",     "Benchmark for memcpy()",                       bench_mem_memcpy        },
-       { "memset",     "Benchmark for memset() tests",                 bench_mem_memset        },
-       { "all",        "Test all memory benchmarks",                   NULL                    },
+       { "memcpy",     "Benchmark for memcpy() functions",             bench_mem_memcpy        },
+       { "memset",     "Benchmark for memset() functions",             bench_mem_memset        },
+       { "all",        "Run all memory access benchmarks",             NULL                    },
        { NULL,         NULL,                                           NULL                    }
 };
 
@@ -62,7 +62,7 @@ static struct bench futex_benchmarks[] = {
        { "requeue",    "Benchmark for futex requeue calls",            bench_futex_requeue     },
        /* pi-futexes */
        { "lock-pi",    "Benchmark for futex lock_pi calls",            bench_futex_lock_pi     },
-       { "all",        "Test all futex benchmarks",                    NULL                    },
+       { "all",        "Run all futex benchmarks",                     NULL                    },
        { NULL,         NULL,                                           NULL                    }
 };
 
@@ -110,7 +110,7 @@ int bench_format = BENCH_FORMAT_DEFAULT;
 unsigned int bench_repeat = 10; /* default number of times to repeat the run */
 
 static const struct option bench_options[] = {
-       OPT_STRING('f', "format", &bench_format_str, "default", "Specify format style"),
+       OPT_STRING('f', "format", &bench_format_str, "default|simple", "Specify the output formatting style"),
        OPT_UINTEGER('r', "repeat",  &bench_repeat,   "Specify amount of times to repeat the run"),
        OPT_END()
 };
index 695ec5a50cf22c56e72961ae98ae9580a98f2512..f4d62510acbbb5603ad246bea89a7dfb95c07f43 100644 (file)
@@ -61,8 +61,8 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
                usage_with_options(evlist_usage, options);
 
        if (details.event_group && (details.verbose || details.freq)) {
-               pr_err("--group option is not compatible with other options\n");
-               usage_with_options(evlist_usage, options);
+               usage_with_options_msg(evlist_usage, options,
+                       "--group option is not compatible with other options\n");
        }
 
        return __cmd_evlist(input_name, &details);
index 36486eade1ef4f2d30736c99d7471b86d7dd0ada..a7d588bf3cdd345233042131373666af2f9d7598 100644 (file)
@@ -463,7 +463,7 @@ int cmd_help(int argc, const char **argv, const char *prefix __maybe_unused)
                        builtin_help_subcommands, builtin_help_usage, 0);
 
        if (show_all) {
-               printf("\n usage: %s\n\n", perf_usage_string);
+               printf("\n Usage: %s\n\n", perf_usage_string);
                list_commands("perf commands", &main_cmds, &other_cmds);
                printf(" %s\n\n", perf_more_info_string);
                return 0;
index f62c49b35be04ec698fc51e2b73ded6bb9dfbccf..0a945d2e8ca5b1bd319fa18537e7a4c57e3b7291 100644 (file)
@@ -28,9 +28,11 @@ struct perf_inject {
        bool                    build_ids;
        bool                    sched_stat;
        bool                    have_auxtrace;
+       bool                    strip;
        const char              *input_name;
        struct perf_data_file   output;
        u64                     bytes_written;
+       u64                     aux_id;
        struct list_head        samples;
        struct itrace_synth_opts itrace_synth_opts;
 };
@@ -176,6 +178,27 @@ static int perf_event__repipe(struct perf_tool *tool,
        return perf_event__repipe_synth(tool, event);
 }
 
+static int perf_event__drop(struct perf_tool *tool __maybe_unused,
+                           union perf_event *event __maybe_unused,
+                           struct perf_sample *sample __maybe_unused,
+                           struct machine *machine __maybe_unused)
+{
+       return 0;
+}
+
+static int perf_event__drop_aux(struct perf_tool *tool,
+                               union perf_event *event __maybe_unused,
+                               struct perf_sample *sample,
+                               struct machine *machine __maybe_unused)
+{
+       struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
+
+       if (!inject->aux_id)
+               inject->aux_id = sample->id;
+
+       return 0;
+}
+
 typedef int (*inject_handler)(struct perf_tool *tool,
                              union perf_event *event,
                              struct perf_sample *sample,
@@ -466,6 +489,78 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel,
        return 0;
 }
 
+static int drop_sample(struct perf_tool *tool __maybe_unused,
+                      union perf_event *event __maybe_unused,
+                      struct perf_sample *sample __maybe_unused,
+                      struct perf_evsel *evsel __maybe_unused,
+                      struct machine *machine __maybe_unused)
+{
+       return 0;
+}
+
+static void strip_init(struct perf_inject *inject)
+{
+       struct perf_evlist *evlist = inject->session->evlist;
+       struct perf_evsel *evsel;
+
+       inject->tool.context_switch = perf_event__drop;
+
+       evlist__for_each(evlist, evsel)
+               evsel->handler = drop_sample;
+}
+
+static bool has_tracking(struct perf_evsel *evsel)
+{
+       return evsel->attr.mmap || evsel->attr.mmap2 || evsel->attr.comm ||
+              evsel->attr.task;
+}
+
+#define COMPAT_MASK (PERF_SAMPLE_ID | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | \
+                    PERF_SAMPLE_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_IDENTIFIER)
+
+/*
+ * In order that the perf.data file is parsable, tracking events like MMAP need
+ * their selected event to exist, except if there is only 1 selected event left
+ * and it has a compatible sample type.
+ */
+static bool ok_to_remove(struct perf_evlist *evlist,
+                        struct perf_evsel *evsel_to_remove)
+{
+       struct perf_evsel *evsel;
+       int cnt = 0;
+       bool ok = false;
+
+       if (!has_tracking(evsel_to_remove))
+               return true;
+
+       evlist__for_each(evlist, evsel) {
+               if (evsel->handler != drop_sample) {
+                       cnt += 1;
+                       if ((evsel->attr.sample_type & COMPAT_MASK) ==
+                           (evsel_to_remove->attr.sample_type & COMPAT_MASK))
+                               ok = true;
+               }
+       }
+
+       return ok && cnt == 1;
+}
+
+static void strip_fini(struct perf_inject *inject)
+{
+       struct perf_evlist *evlist = inject->session->evlist;
+       struct perf_evsel *evsel, *tmp;
+
+       /* Remove non-synthesized evsels if possible */
+       evlist__for_each_safe(evlist, tmp, evsel) {
+               if (evsel->handler == drop_sample &&
+                   ok_to_remove(evlist, evsel)) {
+                       pr_debug("Deleting %s\n", perf_evsel__name(evsel));
+                       perf_evlist__remove(evlist, evsel);
+                       perf_evsel__delete(evsel);
+               }
+       }
+}
+
 static int __cmd_inject(struct perf_inject *inject)
 {
        int ret = -EINVAL;
@@ -512,10 +607,14 @@ static int __cmd_inject(struct perf_inject *inject)
                inject->tool.id_index       = perf_event__repipe_id_index;
                inject->tool.auxtrace_info  = perf_event__process_auxtrace_info;
                inject->tool.auxtrace       = perf_event__process_auxtrace;
+               inject->tool.aux            = perf_event__drop_aux;
+               inject->tool.itrace_start   = perf_event__drop_aux,
                inject->tool.ordered_events = true;
                inject->tool.ordering_requires_timestamps = true;
                /* Allow space in the header for new attributes */
                output_data_offset = 4096;
+               if (inject->strip)
+                       strip_init(inject);
        }
 
        if (!inject->itrace_synth_opts.set)
@@ -535,11 +634,28 @@ static int __cmd_inject(struct perf_inject *inject)
                }
                /*
                 * The AUX areas have been removed and replaced with
-                * synthesized hardware events, so clear the feature flag.
+                * synthesized hardware events, so clear the feature flag and
+                * remove the evsel.
                 */
-               if (inject->itrace_synth_opts.set)
+               if (inject->itrace_synth_opts.set) {
+                       struct perf_evsel *evsel;
+
                        perf_header__clear_feat(&session->header,
                                                HEADER_AUXTRACE);
+                       if (inject->itrace_synth_opts.last_branch)
+                               perf_header__set_feat(&session->header,
+                                                     HEADER_BRANCH_STACK);
+                       evsel = perf_evlist__id2evsel_strict(session->evlist,
+                                                            inject->aux_id);
+                       if (evsel) {
+                               pr_debug("Deleting %s\n",
+                                        perf_evsel__name(evsel));
+                               perf_evlist__remove(session->evlist, evsel);
+                               perf_evsel__delete(evsel);
+                       }
+                       if (inject->strip)
+                               strip_fini(inject);
+               }
                session->header.data_offset = output_data_offset;
                session->header.data_size = inject->bytes_written;
                perf_session__write_header(session, session->evlist, fd, true);
@@ -604,6 +720,8 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
                OPT_CALLBACK_OPTARG(0, "itrace", &inject.itrace_synth_opts,
                                    NULL, "opts", "Instruction Tracing options",
                                    itrace_parse_synth_opts),
+               OPT_BOOLEAN(0, "strip", &inject.strip,
+                           "strip non-synthesized events (use with --itrace)"),
                OPT_END()
        };
        const char * const inject_usage[] = {
@@ -619,6 +737,11 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
        if (argc)
                usage_with_options(inject_usage, options);
 
+       if (inject.strip && !inject.itrace_synth_opts.set) {
+               pr_err("--strip option requires --itrace option\n");
+               return -1;
+       }
+
        if (perf_data_file__open(&inject.output)) {
                perror("failed to create output file");
                return -1;
index 23b1faaaa4cc5f83c263ffbc9a0f14733c0dcae2..93ce665f976f65a4a1889ba01441bb1f1cce2786 100644 (file)
@@ -329,7 +329,7 @@ static int build_alloc_func_list(void)
                return -EINVAL;
        }
 
-       kernel_map = machine->vmlinux_maps[MAP__FUNCTION];
+       kernel_map = machine__kernel_map(machine);
        if (map__load(kernel_map, NULL) < 0) {
                pr_err("cannot load kernel map\n");
                return -ENOENT;
index fc1cffb1b7a28c9b0d9856770530a0e8b3bf9606..dd94b4ca22131a211729082925bbbf082d19c73e 100644 (file)
@@ -13,7 +13,6 @@
 #include "util/parse-options.h"
 #include "util/trace-event.h"
 #include "util/debug.h"
-#include <api/fs/debugfs.h>
 #include "util/tool.h"
 #include "util/stat.h"
 #include "util/top.h"
index af5bd05141088469d29d2fc7112927be96f2f610..bf679e2c978bdef9b7943271987e4b7e4e465981 100644 (file)
@@ -36,7 +36,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
 
        setup_pager();
 
-       if (!raw_dump)
+       if (!raw_dump && pager_in_use())
                printf("\nList of pre-defined events (to be used in -e):\n\n");
 
        if (argc == 0) {
@@ -45,6 +45,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
        }
 
        for (i = 0; i < argc; ++i) {
+               char *sep, *s;
+
                if (strcmp(argv[i], "tracepoint") == 0)
                        print_tracepoint_events(NULL, NULL, raw_dump);
                else if (strcmp(argv[i], "hw") == 0 ||
@@ -60,8 +62,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
                        print_hwcache_events(NULL, raw_dump);
                else if (strcmp(argv[i], "pmu") == 0)
                        print_pmu_events(NULL, raw_dump);
-               else {
-                       char *sep = strchr(argv[i], ':'), *s;
+               else if ((sep = strchr(argv[i], ':')) != NULL) {
                        int sep_idx;
 
                        if (sep == NULL) {
@@ -76,6 +77,19 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
                        s[sep_idx] = '\0';
                        print_tracepoint_events(s, s + sep_idx + 1, raw_dump);
                        free(s);
+               } else {
+                       if (asprintf(&s, "*%s*", argv[i]) < 0) {
+                               printf("Critical: Not enough memory! Trying to continue...\n");
+                               continue;
+                       }
+                       print_symbol_events(s, PERF_TYPE_HARDWARE,
+                                           event_symbols_hw, PERF_COUNT_HW_MAX, raw_dump);
+                       print_symbol_events(s, PERF_TYPE_SOFTWARE,
+                                           event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump);
+                       print_hwcache_events(s, raw_dump);
+                       print_pmu_events(s, raw_dump);
+                       print_tracepoint_events(NULL, s, raw_dump);
+                       free(s);
                }
        }
        return 0;
index b81cec33b4b2e4e07cac866a514e6369499b0c16..132afc97676c1861d7be03bbbd4f5cea99eb9e57 100644 (file)
 #include "util/strfilter.h"
 #include "util/symbol.h"
 #include "util/debug.h"
-#include <api/fs/debugfs.h>
 #include "util/parse-options.h"
 #include "util/probe-finder.h"
 #include "util/probe-event.h"
+#include "util/probe-file.h"
 
 #define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*"
 #define DEFAULT_FUNC_FILTER "!_*"
@@ -182,10 +182,8 @@ static int opt_set_target(const struct option *opt, const char *str,
        if  (str) {
                if (!strcmp(opt->long_name, "exec"))
                        params.uprobes = true;
-#ifdef HAVE_DWARF_SUPPORT
                else if (!strcmp(opt->long_name, "module"))
                        params.uprobes = false;
-#endif
                else
                        return ret;
 
@@ -311,6 +309,119 @@ static void pr_err_with_code(const char *msg, int err)
        pr_err("\n");
 }
 
+static int perf_add_probe_events(struct perf_probe_event *pevs, int npevs)
+{
+       int ret;
+       int i, k;
+       const char *event = NULL, *group = NULL;
+
+       ret = init_probe_symbol_maps(pevs->uprobes);
+       if (ret < 0)
+               return ret;
+
+       ret = convert_perf_probe_events(pevs, npevs);
+       if (ret < 0)
+               goto out_cleanup;
+
+       ret = apply_perf_probe_events(pevs, npevs);
+       if (ret < 0)
+               goto out_cleanup;
+
+       for (i = k = 0; i < npevs; i++)
+               k += pevs[i].ntevs;
+
+       pr_info("Added new event%s\n", (k > 1) ? "s:" : ":");
+       for (i = 0; i < npevs; i++) {
+               struct perf_probe_event *pev = &pevs[i];
+
+               for (k = 0; k < pev->ntevs; k++) {
+                       struct probe_trace_event *tev = &pev->tevs[k];
+
+                       /* We use tev's name for showing new events */
+                       show_perf_probe_event(tev->group, tev->event, pev,
+                                             tev->point.module, false);
+
+                       /* Save the last valid name */
+                       event = tev->event;
+                       group = tev->group;
+               }
+       }
+
+       /* Note that it is possible to skip all events because of blacklist */
+       if (event) {
+               /* Show how to use the event. */
+               pr_info("\nYou can now use it in all perf tools, such as:\n\n");
+               pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", group, event);
+       }
+
+out_cleanup:
+       cleanup_perf_probe_events(pevs, npevs);
+       exit_probe_symbol_maps();
+       return ret;
+}
+
+static int perf_del_probe_events(struct strfilter *filter)
+{
+       int ret, ret2, ufd = -1, kfd = -1;
+       char *str = strfilter__string(filter);
+       struct strlist *klist = NULL, *ulist = NULL;
+       struct str_node *ent;
+
+       if (!str)
+               return -EINVAL;
+
+       pr_debug("Delete filter: \'%s\'\n", str);
+
+       /* Get current event names */
+       ret = probe_file__open_both(&kfd, &ufd, PF_FL_RW);
+       if (ret < 0)
+               goto out;
+
+       klist = strlist__new(NULL, NULL);
+       ulist = strlist__new(NULL, NULL);
+       if (!klist || !ulist) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = probe_file__get_events(kfd, filter, klist);
+       if (ret == 0) {
+               strlist__for_each(ent, klist)
+                       pr_info("Removed event: %s\n", ent->s);
+
+               ret = probe_file__del_strlist(kfd, klist);
+               if (ret < 0)
+                       goto error;
+       }
+
+       ret2 = probe_file__get_events(ufd, filter, ulist);
+       if (ret2 == 0) {
+               strlist__for_each(ent, ulist)
+                       pr_info("Removed event: %s\n", ent->s);
+
+               ret2 = probe_file__del_strlist(ufd, ulist);
+               if (ret2 < 0)
+                       goto error;
+       }
+
+       if (ret == -ENOENT && ret2 == -ENOENT)
+               pr_debug("\"%s\" does not hit any event.\n", str);
+               /* Note that this is silently ignored */
+       ret = 0;
+
+error:
+       if (kfd >= 0)
+               close(kfd);
+       if (ufd >= 0)
+               close(ufd);
+out:
+       strlist__delete(klist);
+       strlist__delete(ulist);
+       free(str);
+
+       return ret;
+}
+
 static int
 __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
 {
@@ -377,9 +488,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
                   "file", "vmlinux pathname"),
        OPT_STRING('s', "source", &symbol_conf.source_prefix,
                   "directory", "path to kernel source"),
-       OPT_CALLBACK('m', "module", NULL, "modname|path",
-               "target module name (for online) or path (for offline)",
-               opt_set_target),
        OPT_BOOLEAN('\0', "no-inlines", &probe_conf.no_inlines,
                "Don't search inlined functions"),
 #endif
@@ -396,6 +504,9 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
                     opt_set_filter),
        OPT_CALLBACK('x', "exec", NULL, "executable|path",
                        "target executable name or path", opt_set_target),
+       OPT_CALLBACK('m', "module", NULL, "modname|path",
+               "target module name (for online) or path (for offline)",
+               opt_set_target),
        OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
                    "Enable symbol demangling"),
        OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
@@ -417,12 +528,12 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
                             PARSE_OPT_STOP_AT_NON_OPTION);
        if (argc > 0) {
                if (strcmp(argv[0], "-") == 0) {
-                       pr_warning("  Error: '-' is not supported.\n");
-                       usage_with_options(probe_usage, options);
+                       usage_with_options_msg(probe_usage, options,
+                               "'-' is not supported.\n");
                }
                if (params.command && params.command != 'a') {
-                       pr_warning("  Error: another command except --add is set.\n");
-                       usage_with_options(probe_usage, options);
+                       usage_with_options_msg(probe_usage, options,
+                               "another command except --add is set.\n");
                }
                ret = parse_probe_event_argv(argc, argv);
                if (ret < 0) {
@@ -451,8 +562,10 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
        switch (params.command) {
        case 'l':
                if (params.uprobes) {
-                       pr_warning("  Error: Don't use --list with --exec.\n");
-                       usage_with_options(probe_usage, options);
+                       pr_err("  Error: Don't use --list with --exec.\n");
+                       parse_options_usage(probe_usage, options, "l", true);
+                       parse_options_usage(NULL, options, "x", true);
+                       return -EINVAL;
                }
                ret = show_perf_probe_events(params.filter);
                if (ret < 0)
@@ -483,7 +596,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
                return ret;
 #endif
        case 'd':
-               ret = del_perf_probe_events(params.filter);
+               ret = perf_del_probe_events(params.filter);
                if (ret < 0) {
                        pr_err_with_code("  Error: Failed to delete events.", ret);
                        return ret;
@@ -492,11 +605,13 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
        case 'a':
                /* Ensure the last given target is used */
                if (params.target && !params.target_used) {
-                       pr_warning("  Error: -x/-m must follow the probe definitions.\n");
-                       usage_with_options(probe_usage, options);
+                       pr_err("  Error: -x/-m must follow the probe definitions.\n");
+                       parse_options_usage(probe_usage, options, "m", true);
+                       parse_options_usage(NULL, options, "x", true);
+                       return -EINVAL;
                }
 
-               ret = add_perf_probe_events(params.events, params.nevents);
+               ret = perf_add_probe_events(params.events, params.nevents);
                if (ret < 0) {
                        pr_err_with_code("  Error: Failed to add events.", ret);
                        return ret;
index 142eeb341b295a722aec214d3346790221031ff1..199fc31e3919c5743ca305f9eef5ef8c8fc86a70 100644 (file)
@@ -31,6 +31,7 @@
 #include "util/auxtrace.h"
 #include "util/parse-branch-options.h"
 #include "util/parse-regs-options.h"
+#include "util/llvm-utils.h"
 
 #include <unistd.h>
 #include <sched.h>
@@ -49,7 +50,7 @@ struct record {
        int                     realtime_prio;
        bool                    no_buildid;
        bool                    no_buildid_cache;
-       long                    samples;
+       unsigned long long      samples;
 };
 
 static int record__write(struct record *rec, void *bf, size_t size)
@@ -636,8 +637,29 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
        /*
         * Let the child rip
         */
-       if (forks)
+       if (forks) {
+               union perf_event *event;
+
+               event = malloc(sizeof(event->comm) + machine->id_hdr_size);
+               if (event == NULL) {
+                       err = -ENOMEM;
+                       goto out_child;
+               }
+
+               /*
+                * Some H/W events are generated before COMM event
+                * which is emitted during exec(), so perf script
+                * cannot see a correct process name for those events.
+                * Synthesize COMM event to prevent it.
+                */
+               perf_event__synthesize_comm(tool, event,
+                                           rec->evlist->workload.pid,
+                                           process_synthesized_event,
+                                           machine);
+               free(event);
+
                perf_evlist__start_workload(rec->evlist);
+       }
 
        if (opts->initial_delay) {
                usleep(opts->initial_delay * 1000);
@@ -646,7 +668,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 
        auxtrace_snapshot_enabled = 1;
        for (;;) {
-               int hits = rec->samples;
+               unsigned long long hits = rec->samples;
 
                if (record__mmap_read_all(rec) < 0) {
                        auxtrace_snapshot_enabled = 0;
@@ -989,13 +1011,8 @@ static struct record record = {
        },
 };
 
-#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: "
-
-#ifdef HAVE_DWARF_UNWIND_SUPPORT
-const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf lbr";
-#else
-const char record_callchain_help[] = CALLCHAIN_HELP "fp lbr";
-#endif
+const char record_callchain_help[] = CALLCHAIN_RECORD_HELP
+       "\n\t\t\t\tDefault: fp";
 
 /*
  * XXX Will stay a global variable till we fix builtin-script.c to stop messing
@@ -1043,7 +1060,7 @@ struct option __record_options[] = {
                           NULL, "enables call-graph recording" ,
                           &record_callchain_opt),
        OPT_CALLBACK(0, "call-graph", &record.opts,
-                    "mode[,dump_size]", record_callchain_help,
+                    "record_mode[,record_size]", record_callchain_help,
                     &record_parse_callchain_opt),
        OPT_INCR('v', "verbose", &verbose,
                    "be more verbose (show counter open errors, etc)"),
@@ -1096,6 +1113,12 @@ struct option __record_options[] = {
                        "per thread proc mmap processing timeout in ms"),
        OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events,
                    "Record context switch events"),
+#ifdef HAVE_LIBBPF_SUPPORT
+       OPT_STRING(0, "clang-path", &llvm_param.clang_path, "clang path",
+                  "clang binary to use for compiling BPF scriptlets"),
+       OPT_STRING(0, "clang-opt", &llvm_param.clang_opt, "clang options",
+                  "options passed to clang when compiling BPF scriptlets"),
+#endif
        OPT_END()
 };
 
@@ -1119,14 +1142,15 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
                usage_with_options(record_usage, record_options);
 
        if (nr_cgroups && !rec->opts.target.system_wide) {
-               ui__error("cgroup monitoring only available in"
-                         " system-wide mode\n");
-               usage_with_options(record_usage, record_options);
+               usage_with_options_msg(record_usage, record_options,
+                       "cgroup monitoring only available in system-wide mode");
+
        }
        if (rec->opts.record_switch_events &&
            !perf_can_record_switch_events()) {
-               ui__error("kernel does not support recording context switch events (--switch-events option)\n");
-               usage_with_options(record_usage, record_options);
+               ui__error("kernel does not support recording context switch events\n");
+               parse_options_usage(record_usage, record_options, "switch-events", 0);
+               return -EINVAL;
        }
 
        if (!rec->itr) {
index 62b285e32aa551093d9869798827a5be2cf3e1e5..2853ad2bd43541b1291292f94a5c6f8a6804dd03 100644 (file)
@@ -62,6 +62,7 @@ struct report {
        float                   min_percent;
        u64                     nr_entries;
        u64                     queue_size;
+       int                     socket_filter;
        DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
 };
 
@@ -162,14 +163,21 @@ static int process_sample_event(struct perf_tool *tool,
        if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
                goto out_put;
 
-       if (sort__mode == SORT_MODE__BRANCH)
+       if (sort__mode == SORT_MODE__BRANCH) {
+               /*
+                * A non-synthesized event might not have a branch stack if
+                * branch stacks have been synthesized (using itrace options).
+                */
+               if (!sample->branch_stack)
+                       goto out_put;
                iter.ops = &hist_iter_branch;
-       else if (rep->mem_mode)
+       } else if (rep->mem_mode) {
                iter.ops = &hist_iter_mem;
-       else if (symbol_conf.cumulate_callchain)
+       } else if (symbol_conf.cumulate_callchain) {
                iter.ops = &hist_iter_cumulative;
-       else
+       } else {
                iter.ops = &hist_iter_normal;
+       }
 
        if (al.map != NULL)
                al.map->dso->hit = 1;
@@ -213,6 +221,15 @@ static int report__setup_sample_type(struct report *rep)
        u64 sample_type = perf_evlist__combined_sample_type(session->evlist);
        bool is_pipe = perf_data_file__is_pipe(session->file);
 
+       if (session->itrace_synth_opts->callchain ||
+           (!is_pipe &&
+            perf_header__has_feat(&session->header, HEADER_AUXTRACE) &&
+            !session->itrace_synth_opts->set))
+               sample_type |= PERF_SAMPLE_CALLCHAIN;
+
+       if (session->itrace_synth_opts->last_branch)
+               sample_type |= PERF_SAMPLE_BRANCH_STACK;
+
        if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
                if (sort__has_parent) {
                        ui__error("Selected --sort parent, but no "
@@ -286,6 +303,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
        struct perf_evsel *evsel = hists_to_evsel(hists);
        char buf[512];
        size_t size = sizeof(buf);
+       int socked_id = hists->socket_filter;
 
        if (symbol_conf.filter_relative) {
                nr_samples = hists->stats.nr_non_filtered_samples;
@@ -326,6 +344,10 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
                ret += fprintf(fp, "\n# Sort order   : %s", sort_order ? : default_mem_sort_order);
        } else
                ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events);
+
+       if (socked_id > -1)
+               ret += fprintf(fp, "\n# Processor Socket: %d", socked_id);
+
        return ret + fprintf(fp, "\n#\n");
 }
 
@@ -365,7 +387,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
 
 static void report__warn_kptr_restrict(const struct report *rep)
 {
-       struct map *kernel_map = rep->session->machines.host.vmlinux_maps[MAP__FUNCTION];
+       struct map *kernel_map = machine__kernel_map(&rep->session->machines.host);
        struct kmap *kernel_kmap = kernel_map ? map__kmap(kernel_map) : NULL;
 
        if (kernel_map == NULL ||
@@ -450,6 +472,8 @@ static void report__collapse_hists(struct report *rep)
                if (pos->idx == 0)
                        hists->symbol_filter_str = rep->symbol_filter_str;
 
+               hists->socket_filter = rep->socket_filter;
+
                hists__collapse_resort(hists, &prog);
 
                /* Non-group events are considered as leader */
@@ -601,6 +625,12 @@ parse_percent_limit(const struct option *opt, const char *str,
        return 0;
 }
 
+#define CALLCHAIN_DEFAULT_OPT  "graph,0.5,caller,function"
+
+const char report_callchain_help[] = "Display call graph (stack chain/backtrace):\n\n"
+                                    CALLCHAIN_REPORT_HELP
+                                    "\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT;
+
 int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
 {
        struct perf_session *session;
@@ -609,7 +639,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
        bool has_br_stack = false;
        int branch_mode = -1;
        bool branch_call_mode = false;
-       char callchain_default_opt[] = "fractal,0.5,callee";
+       char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT;
        const char * const report_usage[] = {
                "perf report [<options>]",
                NULL
@@ -635,6 +665,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
                },
                .max_stack               = PERF_MAX_STACK_DEPTH,
                .pretty_printing_style   = "normal",
+               .socket_filter           = -1,
        };
        const struct option options[] = {
        OPT_STRING('i', "input", &input_name, "file",
@@ -668,15 +699,18 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
                   " Please refer the man page for the complete list."),
        OPT_STRING('F', "fields", &field_order, "key[,keys...]",
                   "output field(s): overhead, period, sample plus all of sort keys"),
-       OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
+       OPT_BOOLEAN(0, "show-cpu-utilization", &symbol_conf.show_cpu_utilization,
                    "Show sample percentage for different cpu modes"),
+       OPT_BOOLEAN_FLAG(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
+                   "Show sample percentage for different cpu modes", PARSE_OPT_HIDDEN),
        OPT_STRING('p', "parent", &parent_pattern, "regex",
                   "regex filter to identify parent, see: '--sort parent'"),
        OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
                    "Only display entries with parent-match"),
-       OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order[,branch]",
-                    "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address), add branches. "
-                    "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt),
+       OPT_CALLBACK_DEFAULT('g', "call-graph", &report,
+                            "print_type,threshold[,print_limit],order,sort_key[,branch]",
+                            report_callchain_help, &report_parse_callchain_opt,
+                            callchain_default_opt),
        OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
                    "Accumulate callchains of children and show total overhead as well"),
        OPT_INTEGER(0, "max-stack", &report.max_stack,
@@ -747,6 +781,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
                        "Show full source file name path for source lines"),
        OPT_BOOLEAN(0, "show-ref-call-graph", &symbol_conf.show_ref_callgraph,
                    "Show callgraph from reference event"),
+       OPT_INTEGER(0, "socket-filter", &report.socket_filter,
+                   "only show processor socket that match with this filter"),
        OPT_END()
        };
        struct perf_data_file file = {
@@ -781,6 +817,12 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
 
        if (report.inverted_callchain)
                callchain_param.order = ORDER_CALLER;
+       if (symbol_conf.cumulate_callchain && !callchain_param.order_set)
+               callchain_param.order = ORDER_CALLER;
+
+       if (itrace_synth_opts.callchain &&
+           (int)itrace_synth_opts.callchain_sz > report.max_stack)
+               report.max_stack = itrace_synth_opts.callchain_sz;
 
        if (!input_name || !strlen(input_name)) {
                if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
@@ -809,6 +851,9 @@ repeat:
        has_br_stack = perf_header__has_feat(&session->header,
                                             HEADER_BRANCH_STACK);
 
+       if (itrace_synth_opts.last_branch)
+               has_br_stack = true;
+
        /*
         * Branch mode is a tristate:
         * -1 means default, so decide based on the file having branch data.
index 33962612a5e9035ae42c83e15497a5713922556b..0ee6d900e100a29ceeb3cacf7e97d81dce8f8d17 100644 (file)
@@ -1728,8 +1728,8 @@ static void setup_sorting(struct perf_sched *sched, const struct option *options
        for (tok = strtok_r(str, ", ", &tmp);
                        tok; tok = strtok_r(NULL, ", ", &tmp)) {
                if (sort_dimension__add(tok, &sched->sort_list) < 0) {
-                       error("Unknown --sort key: `%s'", tok);
-                       usage_with_options(usage_msg, options);
+                       usage_with_options_msg(usage_msg, options,
+                                       "Unknown --sort key: `%s'", tok);
                }
        }
 
index 284a76e046284983cd769da89cb45604b114a41a..72b5deb4bd7961bc4fdb86c50689253b23660b0e 100644 (file)
@@ -29,9 +29,12 @@ static bool                  no_callchain;
 static bool                    latency_format;
 static bool                    system_wide;
 static bool                    print_flags;
+static bool                    nanosecs;
 static const char              *cpu_list;
 static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
 
+unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH;
+
 enum perf_output_field {
        PERF_OUTPUT_COMM            = 1U << 0,
        PERF_OUTPUT_TID             = 1U << 1,
@@ -48,6 +51,8 @@ enum perf_output_field {
        PERF_OUTPUT_SRCLINE         = 1U << 12,
        PERF_OUTPUT_PERIOD          = 1U << 13,
        PERF_OUTPUT_IREGS           = 1U << 14,
+       PERF_OUTPUT_BRSTACK         = 1U << 15,
+       PERF_OUTPUT_BRSTACKSYM      = 1U << 16,
 };
 
 struct output_option {
@@ -69,6 +74,8 @@ struct output_option {
        {.str = "srcline", .field = PERF_OUTPUT_SRCLINE},
        {.str = "period", .field = PERF_OUTPUT_PERIOD},
        {.str = "iregs", .field = PERF_OUTPUT_IREGS},
+       {.str = "brstack", .field = PERF_OUTPUT_BRSTACK},
+       {.str = "brstacksym", .field = PERF_OUTPUT_BRSTACKSYM},
 };
 
 /* default set to maintain compatibility with current format */
@@ -415,10 +422,84 @@ static void print_sample_start(struct perf_sample *sample,
                secs = nsecs / NSECS_PER_SEC;
                nsecs -= secs * NSECS_PER_SEC;
                usecs = nsecs / NSECS_PER_USEC;
-               printf("%5lu.%06lu: ", secs, usecs);
+               if (nanosecs)
+                       printf("%5lu.%09llu: ", secs, nsecs);
+               else
+                       printf("%5lu.%06lu: ", secs, usecs);
        }
 }
 
+static inline char
+mispred_str(struct branch_entry *br)
+{
+       if (!(br->flags.mispred  || br->flags.predicted))
+               return '-';
+
+       return br->flags.predicted ? 'P' : 'M';
+}
+
+static void print_sample_brstack(union perf_event *event __maybe_unused,
+                         struct perf_sample *sample,
+                         struct thread *thread __maybe_unused,
+                         struct perf_event_attr *attr __maybe_unused)
+{
+       struct branch_stack *br = sample->branch_stack;
+       u64 i;
+
+       if (!(br && br->nr))
+               return;
+
+       for (i = 0; i < br->nr; i++) {
+               printf(" 0x%"PRIx64"/0x%"PRIx64"/%c/%c/%c/%d ",
+                       br->entries[i].from,
+                       br->entries[i].to,
+                       mispred_str( br->entries + i),
+                       br->entries[i].flags.in_tx? 'X' : '-',
+                       br->entries[i].flags.abort? 'A' : '-',
+                       br->entries[i].flags.cycles);
+       }
+}
+
+static void print_sample_brstacksym(union perf_event *event __maybe_unused,
+                         struct perf_sample *sample,
+                         struct thread *thread __maybe_unused,
+                         struct perf_event_attr *attr __maybe_unused)
+{
+       struct branch_stack *br = sample->branch_stack;
+       struct addr_location alf, alt;
+       u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+       u64 i, from, to;
+
+       if (!(br && br->nr))
+               return;
+
+       for (i = 0; i < br->nr; i++) {
+
+               memset(&alf, 0, sizeof(alf));
+               memset(&alt, 0, sizeof(alt));
+               from = br->entries[i].from;
+               to   = br->entries[i].to;
+
+               thread__find_addr_map(thread, cpumode, MAP__FUNCTION, from, &alf);
+               if (alf.map)
+                       alf.sym = map__find_symbol(alf.map, alf.addr, NULL);
+
+               thread__find_addr_map(thread, cpumode, MAP__FUNCTION, to, &alt);
+               if (alt.map)
+                       alt.sym = map__find_symbol(alt.map, alt.addr, NULL);
+
+               symbol__fprintf_symname_offs(alf.sym, &alf, stdout);
+               putchar('/');
+               symbol__fprintf_symname_offs(alt.sym, &alt, stdout);
+               printf("/%c/%c/%c/%d ",
+                       mispred_str( br->entries + i),
+                       br->entries[i].flags.in_tx? 'X' : '-',
+                       br->entries[i].flags.abort? 'A' : '-',
+                       br->entries[i].flags.cycles);
+       }
+}
+
+
 static void print_sample_addr(union perf_event *event,
                          struct perf_sample *sample,
                          struct thread *thread,
@@ -471,7 +552,7 @@ static void print_sample_bts(union perf_event *event,
                        }
                }
                perf_evsel__print_ip(evsel, sample, al, print_opts,
-                                    PERF_MAX_STACK_DEPTH);
+                                    scripting_max_stack);
        }
 
        /* print branch_to information */
@@ -548,12 +629,17 @@ static void process_event(union perf_event *event, struct perf_sample *sample,
 
                perf_evsel__print_ip(evsel, sample, al,
                                     output[attr->type].print_ip_opts,
-                                    PERF_MAX_STACK_DEPTH);
+                                    scripting_max_stack);
        }
 
        if (PRINT_FIELD(IREGS))
                print_sample_iregs(event, sample, thread, attr);
 
+       if (PRINT_FIELD(BRSTACK))
+               print_sample_brstack(event, sample, thread, attr);
+       else if (PRINT_FIELD(BRSTACKSYM))
+               print_sample_brstacksym(event, sample, thread, attr);
+
        printf("\n");
 }
 
@@ -680,7 +766,10 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
 
        set_print_ip_opts(&evsel->attr);
 
-       return perf_evsel__check_attr(evsel, scr->session);
+       if (evsel->attr.sample_type)
+               err = perf_evsel__check_attr(evsel, scr->session);
+
+       return err;
 }
 
 static int process_comm_event(struct perf_tool *tool,
@@ -1672,7 +1761,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
                     "comma separated output fields prepend with 'type:'. "
                     "Valid types: hw,sw,trace,raw. "
                     "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,"
-                    "addr,symoff,period,iregs,flags", parse_output_fields),
+                    "addr,symoff,period,iregs,brstack,brstacksym,flags", parse_output_fields),
        OPT_BOOLEAN('a', "all-cpus", &system_wide,
                    "system-wide collection from all CPUs"),
        OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
@@ -1695,6 +1784,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_BOOLEAN('\0', "show-switch-events", &script.show_switch_events,
                    "Show context switch events (if recorded)"),
        OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
+       OPT_BOOLEAN(0, "ns", &nanosecs,
+                   "Use 9 decimal places when displaying time"),
        OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts",
                            "Instruction Tracing options",
                            itrace_parse_synth_opts),
@@ -1740,6 +1831,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
                }
        }
 
+       if (itrace_synth_opts.callchain &&
+           itrace_synth_opts.callchain_sz > scripting_max_stack)
+               scripting_max_stack = itrace_synth_opts.callchain_sz;
+
        /* make sure PERF_EXEC_PATH is set for scripts */
        perf_set_argv_exec_path(perf_exec_path());
 
@@ -1752,9 +1847,9 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
                rep_script_path = get_script_path(argv[0], REPORT_SUFFIX);
 
                if (!rec_script_path && !rep_script_path) {
-                       fprintf(stderr, " Couldn't find script %s\n\n See perf"
+                       usage_with_options_msg(script_usage, options,
+                               "Couldn't find script `%s'\n\n See perf"
                                " script -l for available scripts.\n", argv[0]);
-                       usage_with_options(script_usage, options);
                }
 
                if (is_top_script(argv[0])) {
@@ -1765,10 +1860,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
                        rep_args = has_required_arg(rep_script_path);
                        rec_args = (argc - 1) - rep_args;
                        if (rec_args < 0) {
-                               fprintf(stderr, " %s script requires options."
+                               usage_with_options_msg(script_usage, options,
+                                       "`%s' script requires options."
                                        "\n\n See perf script -l for available "
                                        "scripts and options.\n", argv[0]);
-                               usage_with_options(script_usage, options);
                        }
                }
 
index d46dbb1bc65d95980b13ec7f473f793c8262a05b..2f438f76cceb2cb1681c30d62324e6a03c4b2108 100644 (file)
@@ -100,6 +100,8 @@ static struct target target = {
        .uid    = UINT_MAX,
 };
 
+typedef int (*aggr_get_id_t)(struct cpu_map *m, int cpu);
+
 static int                     run_count                       =  1;
 static bool                    no_inherit                      = false;
 static volatile pid_t          child_pid                       = -1;
@@ -119,7 +121,7 @@ static unsigned int         unit_width                      = 4; /* strlen("unit") */
 static bool                    forever                         = false;
 static struct timespec         ref_time;
 static struct cpu_map          *aggr_map;
-static int                     (*aggr_get_id)(struct cpu_map *m, int cpu);
+static aggr_get_id_t           aggr_get_id;
 
 static volatile int done = 0;
 
@@ -215,7 +217,7 @@ static void read_counters(bool close_counters)
 
        evlist__for_each(evsel_list, counter) {
                if (read_counter(counter))
-                       pr_warning("failed to read counter %s\n", counter->name);
+                       pr_debug("failed to read counter %s\n", counter->name);
 
                if (perf_stat_process_counter(&stat_config, counter))
                        pr_warning("failed to process counter %s\n", counter->name);
@@ -434,7 +436,7 @@ static void print_noise_pct(double total, double avg)
 
 static void print_noise(struct perf_evsel *evsel, double avg)
 {
-       struct perf_stat *ps;
+       struct perf_stat_evsel *ps;
 
        if (run_count == 1)
                return;
@@ -479,6 +481,7 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr)
                        csv_sep);
                break;
        case AGGR_GLOBAL:
+       case AGGR_UNSET:
        default:
                break;
        }
@@ -671,7 +674,7 @@ static void print_aggr_thread(struct perf_evsel *counter, char *prefix)
 static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
 {
        FILE *output = stat_config.output;
-       struct perf_stat *ps = counter->priv;
+       struct perf_stat_evsel *ps = counter->priv;
        double avg = avg_stats(&ps->res_stats[0]);
        int scaled = counter->counts->scaled;
        double uval;
@@ -799,6 +802,8 @@ static void print_interval(char *prefix, struct timespec *ts)
                case AGGR_GLOBAL:
                default:
                        fprintf(output, "#           time             counts %*s events\n", unit_width, "unit");
+               case AGGR_UNSET:
+                       break;
                }
        }
 
@@ -880,6 +885,7 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)
                evlist__for_each(evsel_list, counter)
                        print_counter(counter, prefix);
                break;
+       case AGGR_UNSET:
        default:
                break;
        }
@@ -940,30 +946,90 @@ static int stat__set_big_num(const struct option *opt __maybe_unused,
        return 0;
 }
 
+static int perf_stat__get_socket(struct cpu_map *map, int cpu)
+{
+       return cpu_map__get_socket(map, cpu, NULL);
+}
+
+static int perf_stat__get_core(struct cpu_map *map, int cpu)
+{
+       return cpu_map__get_core(map, cpu, NULL);
+}
+
+static int cpu_map__get_max(struct cpu_map *map)
+{
+       int i, max = -1;
+
+       for (i = 0; i < map->nr; i++) {
+               if (map->map[i] > max)
+                       max = map->map[i];
+       }
+
+       return max;
+}
+
+static struct cpu_map *cpus_aggr_map;
+
+static int perf_stat__get_aggr(aggr_get_id_t get_id, struct cpu_map *map, int idx)
+{
+       int cpu;
+
+       if (idx >= map->nr)
+               return -1;
+
+       cpu = map->map[idx];
+
+       if (cpus_aggr_map->map[cpu] == -1)
+               cpus_aggr_map->map[cpu] = get_id(map, idx);
+
+       return cpus_aggr_map->map[cpu];
+}
+
+static int perf_stat__get_socket_cached(struct cpu_map *map, int idx)
+{
+       return perf_stat__get_aggr(perf_stat__get_socket, map, idx);
+}
+
+static int perf_stat__get_core_cached(struct cpu_map *map, int idx)
+{
+       return perf_stat__get_aggr(perf_stat__get_core, map, idx);
+}
+
 static int perf_stat_init_aggr_mode(void)
 {
+       int nr;
+
        switch (stat_config.aggr_mode) {
        case AGGR_SOCKET:
                if (cpu_map__build_socket_map(evsel_list->cpus, &aggr_map)) {
                        perror("cannot build socket map");
                        return -1;
                }
-               aggr_get_id = cpu_map__get_socket;
+               aggr_get_id = perf_stat__get_socket_cached;
                break;
        case AGGR_CORE:
                if (cpu_map__build_core_map(evsel_list->cpus, &aggr_map)) {
                        perror("cannot build core map");
                        return -1;
                }
-               aggr_get_id = cpu_map__get_core;
+               aggr_get_id = perf_stat__get_core_cached;
                break;
        case AGGR_NONE:
        case AGGR_GLOBAL:
        case AGGR_THREAD:
+       case AGGR_UNSET:
        default:
                break;
        }
-       return 0;
+
+       /*
+        * The evsel_list->cpus is the base we operate on,
+        * taking the highest cpu number to be the size of
+        * the aggregation translate cpumap.
+        */
+       nr = cpu_map__get_max(evsel_list->cpus);
+       cpus_aggr_map = cpu_map__empty_new(nr + 1);
+       return cpus_aggr_map ? 0 : -ENOMEM;
 }
 
 /*
@@ -1179,7 +1245,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_STRING(0, "post", &post_cmd, "command",
                        "command to run after to the measured command"),
        OPT_UINTEGER('I', "interval-print", &stat_config.interval,
-                   "print counts at regular interval in ms (>= 100)"),
+                   "print counts at regular interval in ms (>= 10)"),
        OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode,
                     "aggregate counts per processor socket", AGGR_SOCKET),
        OPT_SET_UINT(0, "per-core", &stat_config.aggr_mode,
@@ -1332,9 +1398,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
                thread_map__read_comms(evsel_list->threads);
 
        if (interval && interval < 100) {
-               pr_err("print interval must be >= 100ms\n");
-               parse_options_usage(stat_usage, options, "I", 1);
-               goto out;
+               if (interval < 10) {
+                       pr_err("print interval must be >= 10ms\n");
+                       parse_options_usage(stat_usage, options, "I", 1);
+                       goto out;
+               } else
+                       pr_warning("print interval < 100ms. "
+                                  "The overhead percentage could be high in some cases. "
+                                  "Please proceed with caution.\n");
        }
 
        if (perf_evlist__alloc_stats(evsel_list, interval))
index 8c465c83aabf31bfd4ce5e600c4084b6d2a62746..7e2e72e6d9d16323c3c448986372fa13234d368a 100644 (file)
@@ -655,7 +655,7 @@ static int symbol_filter(struct map *map, struct symbol *sym)
 {
        const char *name = sym->name;
 
-       if (!map->dso->kernel)
+       if (!__map__is_kernel(map))
                return 0;
        /*
         * ppc64 uses function descriptors and appends a '.' to the
@@ -857,9 +857,12 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
                         * TODO: we don't process guest user from host side
                         * except simple counting.
                         */
-                       /* Fall thru */
-               default:
                        goto next_event;
+               default:
+                       if (event->header.type == PERF_RECORD_SAMPLE)
+                               goto next_event;
+                       machine = &session->machines.host;
+                       break;
                }
 
 
@@ -952,7 +955,7 @@ static int __cmd_top(struct perf_top *top)
        machines__set_symbol_filter(&top->session->machines, symbol_filter);
 
        if (!objdump_path) {
-               ret = perf_session_env__lookup_objdump(&top->session->header.env);
+               ret = perf_env__lookup_objdump(&top->session->header.env);
                if (ret)
                        goto out_delete;
        }
@@ -961,8 +964,18 @@ static int __cmd_top(struct perf_top *top)
        if (ret)
                goto out_delete;
 
+       if (perf_session__register_idle_thread(top->session) == NULL)
+               goto out_delete;
+
        machine__synthesize_threads(&top->session->machines.host, &opts->target,
                                    top->evlist->threads, false, opts->proc_map_timeout);
+
+       if (sort__has_socket) {
+               ret = perf_env__read_cpu_topology_map(&perf_env);
+               if (ret < 0)
+                       goto out_err_cpu_topo;
+       }
+
        ret = perf_top__start_counters(top);
        if (ret)
                goto out_delete;
@@ -1020,6 +1033,14 @@ out_delete:
        top->session = NULL;
 
        return ret;
+
+out_err_cpu_topo: {
+       char errbuf[BUFSIZ];
+       const char *err = strerror_r(-ret, errbuf, sizeof(errbuf));
+
+       ui__error("Could not read the CPU topology map: %s\n", err);
+       goto out_delete;
+}
 }
 
 static int
@@ -1032,8 +1053,22 @@ callchain_opt(const struct option *opt, const char *arg, int unset)
 static int
 parse_callchain_opt(const struct option *opt, const char *arg, int unset)
 {
-       symbol_conf.use_callchain = true;
-       return record_parse_callchain_opt(opt, arg, unset);
+       struct record_opts *record = (struct record_opts *)opt->value;
+
+       record->callgraph_set = true;
+       callchain_param.enabled = !unset;
+       callchain_param.record_mode = CALLCHAIN_FP;
+
+       /*
+        * --no-call-graph
+        */
+       if (unset) {
+               symbol_conf.use_callchain = false;
+               callchain_param.record_mode = CALLCHAIN_NONE;
+               return 0;
+       }
+
+       return parse_callchain_top_opt(arg);
 }
 
 static int perf_top_config(const char *var, const char *value, void *cb)
@@ -1058,6 +1093,9 @@ parse_percent_limit(const struct option *opt, const char *arg,
        return 0;
 }
 
+const char top_callchain_help[] = CALLCHAIN_RECORD_HELP CALLCHAIN_REPORT_HELP
+       "\n\t\t\t\tDefault: fp,graph,0.5,caller,function";
+
 int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
 {
        char errbuf[BUFSIZ];
@@ -1133,11 +1171,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
                    "Show a column with the number of samples"),
        OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts,
-                          NULL, "enables call-graph recording",
+                          NULL, "enables call-graph recording and display",
                           &callchain_opt),
        OPT_CALLBACK(0, "call-graph", &top.record_opts,
-                    "mode[,dump_size]", record_callchain_help,
-                    &parse_callchain_opt),
+                    "record_mode[,record_size],print_type,threshold[,print_limit],order,sort_key[,branch]",
+                    top_callchain_help, &parse_callchain_opt),
        OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
                    "Accumulate callchains of children and show total overhead as well"),
        OPT_INTEGER(0, "max-stack", &top.max_stack,
@@ -1267,6 +1305,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
                perf_hpp__cancel_cumulate();
        }
 
+       if (symbol_conf.cumulate_callchain && !callchain_param.order_set)
+               callchain_param.order = ORDER_CALLER;
+
        symbol_conf.priv_size = sizeof(struct annotation);
 
        symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
index 4e3abba03062f3e84cfbdea5ca2c07adc641b35b..c783d8fd3a80fb4ae29ab2cb81d3a7d8b54a0123 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <traceevent/event-parse.h>
+#include <api/fs/tracing_path.h>
 #include "builtin.h"
 #include "util/color.h"
 #include "util/debug.h"
@@ -37,6 +38,7 @@
 #include <stdlib.h>
 #include <sys/mman.h>
 #include <linux/futex.h>
+#include <linux/err.h>
 
 /* For older distros: */
 #ifndef MAP_STACK
@@ -244,13 +246,14 @@ static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void
        struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction);
 
        /* older kernel (e.g., RHEL6) use syscalls:{enter,exit} */
-       if (evsel == NULL)
+       if (IS_ERR(evsel))
                evsel = perf_evsel__newtp("syscalls", direction);
 
-       if (evsel) {
-               if (perf_evsel__init_syscall_tp(evsel, handler))
-                       goto out_delete;
-       }
+       if (IS_ERR(evsel))
+               return NULL;
+
+       if (perf_evsel__init_syscall_tp(evsel, handler))
+               goto out_delete;
 
        return evsel;
 
@@ -582,6 +585,12 @@ static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct sysc
 
 #define SCA_FUTEX_OP  syscall_arg__scnprintf_futex_op
 
+static const char *bpf_cmd[] = {
+       "MAP_CREATE", "MAP_LOOKUP_ELEM", "MAP_UPDATE_ELEM", "MAP_DELETE_ELEM",
+       "MAP_GET_NEXT_KEY", "PROG_LOAD",
+};
+static DEFINE_STRARRAY(bpf_cmd);
+
 static const char *epoll_ctl_ops[] = { "ADD", "DEL", "MOD", };
 static DEFINE_STRARRAY_OFFSET(epoll_ctl_ops, 1);
 
@@ -1008,6 +1017,7 @@ static struct syscall_fmt {
          .arg_scnprintf = { [0] = SCA_FILENAME, /* filename */
                             [1] = SCA_ACCMODE,  /* mode */ }, },
        { .name     = "arch_prctl", .errmsg = true, .alias = "prctl", },
+       { .name     = "bpf",        .errmsg = true, STRARRAY(0, cmd, bpf_cmd), },
        { .name     = "brk",        .hexret = true,
          .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, },
        { .name     = "chdir",      .errmsg = true,
@@ -1704,12 +1714,12 @@ static int trace__read_syscall_info(struct trace *trace, int id)
        snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
        sc->tp_format = trace_event__tp_format("syscalls", tp_name);
 
-       if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) {
+       if (IS_ERR(sc->tp_format) && sc->fmt && sc->fmt->alias) {
                snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias);
                sc->tp_format = trace_event__tp_format("syscalls", tp_name);
        }
 
-       if (sc->tp_format == NULL)
+       if (IS_ERR(sc->tp_format))
                return -1;
 
        sc->args = sc->tp_format->format.fields;
@@ -2389,7 +2399,8 @@ static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp);
 static bool perf_evlist__add_vfs_getname(struct perf_evlist *evlist)
 {
        struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname");
-       if (evsel == NULL)
+
+       if (IS_ERR(evsel))
                return false;
 
        if (perf_evsel__field(evsel, "pathname") == NULL) {
@@ -2686,11 +2697,11 @@ out_delete_evlist:
        char errbuf[BUFSIZ];
 
 out_error_sched_stat_runtime:
-       debugfs__strerror_open_tp(errno, errbuf, sizeof(errbuf), "sched", "sched_stat_runtime");
+       tracing_path__strerror_open_tp(errno, errbuf, sizeof(errbuf), "sched", "sched_stat_runtime");
        goto out_error;
 
 out_error_raw_syscalls:
-       debugfs__strerror_open_tp(errno, errbuf, sizeof(errbuf), "raw_syscalls", "sys_(enter|exit)");
+       tracing_path__strerror_open_tp(errno, errbuf, sizeof(errbuf), "raw_syscalls", "sys_(enter|exit)");
        goto out_error;
 
 out_error_mmap:
index 38a08539f4bfc0405000dfd1ce543053409241a8..de89ec57436171ef74e5aa48135db8c2e9b4c0a0 100644 (file)
@@ -106,9 +106,14 @@ ifdef LIBBABELTRACE
   FEATURE_CHECK_LDFLAGS-libbabeltrace := $(LIBBABELTRACE_LDFLAGS) -lbabeltrace-ctf
 endif
 
+FEATURE_CHECK_CFLAGS-bpf = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
 # include ARCH specific config
 -include $(src-perf)/arch/$(ARCH)/Makefile
 
+ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
+  CFLAGS += -DHAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
+endif
+
 include $(src-perf)/config/utilities.mak
 
 ifeq ($(call get-executable,$(FLEX)),)
@@ -233,6 +238,7 @@ ifdef NO_LIBELF
   NO_DEMANGLE := 1
   NO_LIBUNWIND := 1
   NO_LIBDW_DWARF_UNWIND := 1
+  NO_LIBBPF := 1
 else
   ifeq ($(feature-libelf), 0)
     ifeq ($(feature-glibc), 1)
@@ -242,13 +248,14 @@ else
       LIBC_SUPPORT := 1
     endif
     ifeq ($(LIBC_SUPPORT),1)
-      msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev);
+      msg := $(warning No libelf found, disables 'probe' tool and BPF support in 'perf record', please install elfutils-libelf-devel/libelf-dev);
 
       NO_LIBELF := 1
       NO_DWARF := 1
       NO_DEMANGLE := 1
       NO_LIBUNWIND := 1
       NO_LIBDW_DWARF_UNWIND := 1
+      NO_LIBBPF := 1
     else
       ifneq ($(filter s% -static%,$(LDFLAGS),),)
         msg := $(error No static glibc found, please install glibc-static);
@@ -305,6 +312,13 @@ ifndef NO_LIBELF
       $(call detected,CONFIG_DWARF)
     endif # PERF_HAVE_DWARF_REGS
   endif # NO_DWARF
+
+  ifndef NO_LIBBPF
+    ifeq ($(feature-bpf), 1)
+      CFLAGS += -DHAVE_LIBBPF_SUPPORT
+      $(call detected,CONFIG_LIBBPF)
+    endif
+  endif # NO_LIBBPF
 endif # NO_LIBELF
 
 ifeq ($(ARCH),powerpc)
@@ -320,6 +334,13 @@ ifndef NO_LIBUNWIND
   endif
 endif
 
+ifndef NO_LIBBPF
+  ifneq ($(feature-bpf), 1)
+    msg := $(warning BPF API too old. Please install recent kernel headers. BPF support in 'perf record' is disabled.)
+    NO_LIBBPF := 1
+  endif
+endif
+
 dwarf-post-unwind := 1
 dwarf-post-unwind-text := BUG
 
index 07dbff5c0e60ea10b0c330b194e234666b84f6c5..3d4c7c09adeae9afeeccf69161882b2fc65f9e41 100644 (file)
@@ -8,14 +8,16 @@
  */
 #include "builtin.h"
 
+#include "util/env.h"
 #include "util/exec_cmd.h"
 #include "util/cache.h"
 #include "util/quote.h"
 #include "util/run-command.h"
 #include "util/parse-events.h"
 #include "util/parse-options.h"
+#include "util/bpf-loader.h"
 #include "util/debug.h"
-#include <api/fs/debugfs.h>
+#include <api/fs/tracing_path.h>
 #include <pthread.h>
 
 const char perf_usage_string[] =
@@ -160,6 +162,20 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
                        break;
 
+               /*
+                * Shortcut for '-h' and '-v' options to invoke help
+                * and version command.
+                */
+               if (!strcmp(cmd, "-h")) {
+                       (*argv)[0] = "--help";
+                       break;
+               }
+
+               if (!strcmp(cmd, "-v")) {
+                       (*argv)[0] = "--version";
+                       break;
+               }
+
                /*
                 * Check remaining flags.
                 */
@@ -214,7 +230,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                fprintf(stderr, "No directory given for --debugfs-dir.\n");
                                usage(perf_usage_string);
                        }
-                       perf_debugfs_set_path((*argv)[1]);
+                       tracing_path_set((*argv)[1]);
                        if (envchanged)
                                *envchanged = 1;
                        (*argv)++;
@@ -230,7 +246,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        (*argv)++;
                        (*argc)--;
                } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) {
-                       perf_debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR));
+                       tracing_path_set(cmd + strlen(CMD_DEBUGFS_DIR));
                        fprintf(stderr, "dir: %s\n", tracing_path);
                        if (envchanged)
                                *envchanged = 1;
@@ -369,6 +385,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 
        status = p->fn(argc, argv, prefix);
        exit_browser(status);
+       perf_env__exit(&perf_env);
+       bpf__clear();
 
        if (status)
                return status & 0xff;
@@ -517,8 +535,10 @@ int main(int argc, const char **argv)
        cmd = perf_extract_argv0_path(argv[0]);
        if (!cmd)
                cmd = "perf-help";
-       /* get debugfs mount point from /proc/mounts */
-       perf_debugfs_mount(NULL);
+
+       /* get debugfs/tracefs mount point from /proc/mounts */
+       tracing_path_mount();
+
        /*
         * "perf-xxxx" is the same as "perf xxxx", but we obviously:
         *
index b9d508336ae6ff0f39de4bee8a98083503ab4dca..c235c22b107ab5531d3f03de1f328f5fc2758a4c 100755 (executable)
 
 import perf
 
-def main():
+def main(context_switch = 0, thread = -1):
        cpus = perf.cpu_map()
-       threads = perf.thread_map()
+       threads = perf.thread_map(thread)
        evsel = perf.evsel(type   = perf.TYPE_SOFTWARE,
                           config = perf.COUNT_SW_DUMMY,
                           task = 1, comm = 1, mmap = 0, freq = 0,
                           wakeup_events = 1, watermark = 1,
-                          sample_id_all = 1,
+                          sample_id_all = 1, context_switch = context_switch,
                           sample_type = perf.SAMPLE_PERIOD | perf.SAMPLE_TID | perf.SAMPLE_CPU)
 
        """What we want are just the PERF_RECORD_ lifetime events for threads,
@@ -48,4 +48,21 @@ def main():
                        print event
 
 if __name__ == '__main__':
+    """
+       To test the PERF_RECORD_SWITCH record, pick a pid and replace
+       in the following line.
+
+       Example output:
+
+cpu: 3, pid: 31463, tid: 31593 { type: context_switch, next_prev_pid: 31463, next_prev_tid: 31593, switch_out: 1 }
+cpu: 1, pid: 31463, tid: 31489 { type: context_switch, next_prev_pid: 31463, next_prev_tid: 31489, switch_out: 1 }
+cpu: 2, pid: 31463, tid: 31496 { type: context_switch, next_prev_pid: 31463, next_prev_tid: 31496, switch_out: 1 }
+cpu: 3, pid: 31463, tid: 31491 { type: context_switch, next_prev_pid: 31463, next_prev_tid: 31491, switch_out: 0 }
+
+       It is possible as well to use event.misc & perf.PERF_RECORD_MISC_SWITCH_OUT
+       to figure out if this is a context switch in or out of the monitored threads.
+
+       If bored, please add command line option parsing support for these options :-)
+    """
+    # main(context_switch = 1, thread = 31463)
     main()
index 84a32037a80fff854108b21cc66eb5ebadab2d21..1b02cdc0cab69b53f0aa97ed544d6383fba56d3d 100644 (file)
@@ -61,6 +61,142 @@ import datetime
 #
 # An example of using the database is provided by the script
 # call-graph-from-postgresql.py.  Refer to that script for details.
+#
+# Tables:
+#
+#      The tables largely correspond to perf tools' data structures.  They are largely self-explanatory.
+#
+#      samples
+#
+#              'samples' is the main table. It represents what instruction was executing at a point in time
+#              when something (a selected event) happened.  The memory address is the instruction pointer or 'ip'.
+#
+#      calls
+#
+#              'calls' represents function calls and is related to 'samples' by 'call_id' and 'return_id'.
+#              'calls' is only created when the 'calls' option to this script is specified.
+#
+#      call_paths
+#
+#              'call_paths' represents all the call stacks.  Each 'call' has an associated record in 'call_paths'.
+#              'calls_paths' is only created when the 'calls' option to this script is specified.
+#
+#      branch_types
+#
+#              'branch_types' provides descriptions for each type of branch.
+#
+#      comm_threads
+#
+#              'comm_threads' shows how 'comms' relates to 'threads'.
+#
+#      comms
+#
+#              'comms' contains a record for each 'comm' - the name given to the executable that is running.
+#
+#      dsos
+#
+#              'dsos' contains a record for each executable file or library.
+#
+#      machines
+#
+#              'machines' can be used to distinguish virtual machines if virtualization is supported.
+#
+#      selected_events
+#
+#              'selected_events' contains a record for each kind of event that has been sampled.
+#
+#      symbols
+#
+#              'symbols' contains a record for each symbol.  Only symbols that have samples are present.
+#
+#      threads
+#
+#              'threads' contains a record for each thread.
+#
+# Views:
+#
+#      Most of the tables have views for more friendly display.  The views are:
+#
+#              calls_view
+#              call_paths_view
+#              comm_threads_view
+#              dsos_view
+#              machines_view
+#              samples_view
+#              symbols_view
+#              threads_view
+#
+# More examples of browsing the database with psql:
+#   Note that some of the examples are not the most optimal SQL query.
+#   Note that call information is only available if the script's 'calls' option has been used.
+#
+#      Top 10 function calls (not aggregated by symbol):
+#
+#              SELECT * FROM calls_view ORDER BY elapsed_time DESC LIMIT 10;
+#
+#      Top 10 function calls (aggregated by symbol):
+#
+#              SELECT symbol_id,(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,
+#                      SUM(elapsed_time) AS tot_elapsed_time,SUM(branch_count) AS tot_branch_count
+#                      FROM calls_view GROUP BY symbol_id ORDER BY tot_elapsed_time DESC LIMIT 10;
+#
+#              Note that the branch count gives a rough estimation of cpu usage, so functions
+#              that took a long time but have a relatively low branch count must have spent time
+#              waiting.
+#
+#      Find symbols by pattern matching on part of the name (e.g. names containing 'alloc'):
+#
+#              SELECT * FROM symbols_view WHERE name LIKE '%alloc%';
+#
+#      Top 10 function calls for a specific symbol (e.g. whose symbol_id is 187):
+#
+#              SELECT * FROM calls_view WHERE symbol_id = 187 ORDER BY elapsed_time DESC LIMIT 10;
+#
+#      Show function calls made by function in the same context (i.e. same call path) (e.g. one with call_path_id 254):
+#
+#              SELECT * FROM calls_view WHERE parent_call_path_id = 254;
+#
+#      Show branches made during a function call (e.g. where call_id is 29357 and return_id is 29370 and tid is 29670)
+#
+#              SELECT * FROM samples_view WHERE id >= 29357 AND id <= 29370 AND tid = 29670 AND event LIKE 'branches%';
+#
+#      Show transactions:
+#
+#              SELECT * FROM samples_view WHERE event = 'transactions';
+#
+#              Note transaction start has 'in_tx' true whereas, transaction end has 'in_tx' false.
+#              Transaction aborts have branch_type_name 'transaction abort'
+#
+#      Show transaction aborts:
+#
+#              SELECT * FROM samples_view WHERE event = 'transactions' AND branch_type_name = 'transaction abort';
+#
+# To print a call stack requires walking the call_paths table.  For example this python script:
+#   #!/usr/bin/python2
+#
+#   import sys
+#   from PySide.QtSql import *
+#
+#   if __name__ == '__main__':
+#           if (len(sys.argv) < 3):
+#                   print >> sys.stderr, "Usage is: printcallstack.py <database name> <call_path_id>"
+#                   raise Exception("Too few arguments")
+#           dbname = sys.argv[1]
+#           call_path_id = sys.argv[2]
+#           db = QSqlDatabase.addDatabase('QPSQL')
+#           db.setDatabaseName(dbname)
+#           if not db.open():
+#                   raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
+#           query = QSqlQuery(db)
+#           print "    id          ip  symbol_id  symbol                          dso_id  dso_short_name"
+#           while call_path_id != 0 and call_path_id != 1:
+#                   ret = query.exec_('SELECT * FROM call_paths_view WHERE id = ' + str(call_path_id))
+#                   if not ret:
+#                           raise Exception("Query failed: " + query.lastError().text())
+#                   if not query.next():
+#                           raise Exception("Query failed")
+#                   print "{0:>6}  {1:>10}  {2:>9}  {3:<30}  {4:>6}  {5:<30}".format(query.value(0), query.value(1), query.value(2), query.value(3), query.value(4), query.value(5))
+#                   call_path_id = query.value(6)
 
 from PySide.QtSql import *
 
@@ -244,6 +380,91 @@ if perf_db_export_calls:
                'parent_call_path_id    bigint,'
                'flags          integer)')
 
+do_query(query, 'CREATE VIEW machines_view AS '
+       'SELECT '
+               'id,'
+               'pid,'
+               'root_dir,'
+               'CASE WHEN id=0 THEN \'unknown\' WHEN pid=-1 THEN \'host\' ELSE \'guest\' END AS host_or_guest'
+       ' FROM machines')
+
+do_query(query, 'CREATE VIEW dsos_view AS '
+       'SELECT '
+               'id,'
+               'machine_id,'
+               '(SELECT host_or_guest FROM machines_view WHERE id = machine_id) AS host_or_guest,'
+               'short_name,'
+               'long_name,'
+               'build_id'
+       ' FROM dsos')
+
+do_query(query, 'CREATE VIEW symbols_view AS '
+       'SELECT '
+               'id,'
+               'name,'
+               '(SELECT short_name FROM dsos WHERE id=dso_id) AS dso,'
+               'dso_id,'
+               'sym_start,'
+               'sym_end,'
+               'CASE WHEN binding=0 THEN \'local\' WHEN binding=1 THEN \'global\' ELSE \'weak\' END AS binding'
+       ' FROM symbols')
+
+do_query(query, 'CREATE VIEW threads_view AS '
+       'SELECT '
+               'id,'
+               'machine_id,'
+               '(SELECT host_or_guest FROM machines_view WHERE id = machine_id) AS host_or_guest,'
+               'process_id,'
+               'pid,'
+               'tid'
+       ' FROM threads')
+
+do_query(query, 'CREATE VIEW comm_threads_view AS '
+       'SELECT '
+               'comm_id,'
+               '(SELECT comm FROM comms WHERE id = comm_id) AS command,'
+               'thread_id,'
+               '(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
+               '(SELECT tid FROM threads WHERE id = thread_id) AS tid'
+       ' FROM comm_threads')
+
+if perf_db_export_calls:
+       do_query(query, 'CREATE VIEW call_paths_view AS '
+               'SELECT '
+                       'c.id,'
+                       'to_hex(c.ip) AS ip,'
+                       'c.symbol_id,'
+                       '(SELECT name FROM symbols WHERE id = c.symbol_id) AS symbol,'
+                       '(SELECT dso_id FROM symbols WHERE id = c.symbol_id) AS dso_id,'
+                       '(SELECT dso FROM symbols_view  WHERE id = c.symbol_id) AS dso_short_name,'
+                       'c.parent_id,'
+                       'to_hex(p.ip) AS parent_ip,'
+                       'p.symbol_id AS parent_symbol_id,'
+                       '(SELECT name FROM symbols WHERE id = p.symbol_id) AS parent_symbol,'
+                       '(SELECT dso_id FROM symbols WHERE id = p.symbol_id) AS parent_dso_id,'
+                       '(SELECT dso FROM symbols_view  WHERE id = p.symbol_id) AS parent_dso_short_name'
+               ' FROM call_paths c INNER JOIN call_paths p ON p.id = c.parent_id')
+       do_query(query, 'CREATE VIEW calls_view AS '
+               'SELECT '
+                       'calls.id,'
+                       'thread_id,'
+                       '(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
+                       '(SELECT tid FROM threads WHERE id = thread_id) AS tid,'
+                       '(SELECT comm FROM comms WHERE id = comm_id) AS command,'
+                       'call_path_id,'
+                       'to_hex(ip) AS ip,'
+                       'symbol_id,'
+                       '(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,'
+                       'call_time,'
+                       'return_time,'
+                       'return_time - call_time AS elapsed_time,'
+                       'branch_count,'
+                       'call_id,'
+                       'return_id,'
+                       'CASE WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' ELSE \'\' END AS flags,'
+                       'parent_call_path_id'
+               ' FROM calls INNER JOIN call_paths ON call_paths.id = call_path_id')
+
 do_query(query, 'CREATE VIEW samples_view AS '
        'SELECT '
                'id,'
index c1518bdd0f1b626242dd101698b13304106c200e..50de2253cff6c64450a23d0332d22c43c802dd8f 100644 (file)
@@ -8,7 +8,6 @@ perf-y += openat-syscall-all-cpus.o
 perf-y += openat-syscall-tp-fields.o
 perf-y += mmap-basic.o
 perf-y += perf-record.o
-perf-y += rdpmc.o
 perf-y += evsel-roundtrip-name.o
 perf-y += evsel-tp-sched.o
 perf-y += fdarray.o
@@ -33,8 +32,7 @@ perf-y += parse-no-sample-id-all.o
 perf-y += kmod-path.o
 perf-y += thread-map.o
 perf-y += llvm.o
-
-perf-$(CONFIG_X86) += perf-time-to-tsc.o
+perf-y += topology.o
 
 ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
 perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
diff --git a/tools/perf/tests/bpf-script-example.c b/tools/perf/tests/bpf-script-example.c
new file mode 100644 (file)
index 0000000..410a70b
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef LINUX_VERSION_CODE
+# error Need LINUX_VERSION_CODE
+# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig'
+#endif
+#define BPF_ANY 0
+#define BPF_MAP_TYPE_ARRAY 2
+#define BPF_FUNC_map_lookup_elem 1
+#define BPF_FUNC_map_update_elem 2
+
+static void *(*bpf_map_lookup_elem)(void *map, void *key) =
+       (void *) BPF_FUNC_map_lookup_elem;
+static void *(*bpf_map_update_elem)(void *map, void *key, void *value, int flags) =
+       (void *) BPF_FUNC_map_update_elem;
+
+struct bpf_map_def {
+       unsigned int type;
+       unsigned int key_size;
+       unsigned int value_size;
+       unsigned int max_entries;
+};
+
+#define SEC(NAME) __attribute__((section(NAME), used))
+struct bpf_map_def SEC("maps") flip_table = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .max_entries = 1,
+};
+
+SEC("func=sys_epoll_pwait")
+int bpf_func__sys_epoll_pwait(void *ctx)
+{
+       int ind =0;
+       int *flag = bpf_map_lookup_elem(&flip_table, &ind);
+       int new_flag;
+       if (!flag)
+               return 0;
+       /* flip flag and store back */
+       new_flag = !*flag;
+       bpf_map_update_elem(&flip_table, &ind, &new_flag, BPF_ANY);
+       return new_flag;
+}
+char _license[] SEC("license") = "GPL";
+int _version SEC("version") = LINUX_VERSION_CODE;
index 136cd934be66a862baa5300397d6b7254155d069..66f72d3d66771ab4c9ad1cec7a53b92d8bcb8f1b 100644 (file)
 #include "parse-options.h"
 #include "symbol.h"
 
-static struct test {
-       const char *desc;
-       int (*func)(void);
-} tests[] = {
+struct test __weak arch_tests[] = {
+       {
+               .func = NULL,
+       },
+};
+
+static struct test generic_tests[] = {
        {
                .desc = "vmlinux symtab matches kallsyms",
                .func = test__vmlinux_matches_kallsyms,
@@ -38,12 +41,6 @@ static struct test {
                .desc = "parse events tests",
                .func = test__parse_events,
        },
-#if defined(__x86_64__) || defined(__i386__)
-       {
-               .desc = "x86 rdpmc test",
-               .func = test__rdpmc,
-       },
-#endif
        {
                .desc = "Validate PERF_RECORD_* events & perf_sample fields",
                .func = test__PERF_RECORD,
@@ -104,12 +101,6 @@ static struct test {
                .desc = "Test software clock events have valid period values",
                .func = test__sw_clock_freq,
        },
-#if defined(__x86_64__) || defined(__i386__)
-       {
-               .desc = "Test converting perf time to TSC",
-               .func = test__perf_time_to_tsc,
-       },
-#endif
        {
                .desc = "Test object code reading",
                .func = test__code_reading,
@@ -126,14 +117,6 @@ static struct test {
                .desc = "Test parsing with no sample_id_all bit set",
                .func = test__parse_no_sample_id_all,
        },
-#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
-#ifdef HAVE_DWARF_UNWIND_SUPPORT
-       {
-               .desc = "Test dwarf unwind",
-               .func = test__dwarf_unwind,
-       },
-#endif
-#endif
        {
                .desc = "Test filtering hist entries",
                .func = test__hists_filter,
@@ -178,12 +161,21 @@ static struct test {
                .desc = "Test LLVM searching and compiling",
                .func = test__llvm,
        },
+       {
+               .desc = "Test topology in session",
+               .func = test_session_topology,
+       },
        {
                .func = NULL,
        },
 };
 
-static bool perf_test__matches(int curr, int argc, const char *argv[])
+static struct test *tests[] = {
+       generic_tests,
+       arch_tests,
+};
+
+static bool perf_test__matches(struct test *test, int curr, int argc, const char *argv[])
 {
        int i;
 
@@ -200,7 +192,7 @@ static bool perf_test__matches(int curr, int argc, const char *argv[])
                        continue;
                }
 
-               if (strstr(tests[curr].desc, argv[i]))
+               if (strstr(test->desc, argv[i]))
                        return true;
        }
 
@@ -237,27 +229,31 @@ static int run_test(struct test *test)
        return err;
 }
 
+#define for_each_test(j, t)                                    \
+       for (j = 0; j < ARRAY_SIZE(tests); j++) \
+               for (t = &tests[j][0]; t->func; t++)
+
 static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
 {
+       struct test *t;
+       unsigned int j;
        int i = 0;
        int width = 0;
 
-       while (tests[i].func) {
-               int len = strlen(tests[i].desc);
+       for_each_test(j, t) {
+               int len = strlen(t->desc);
 
                if (width < len)
                        width = len;
-               ++i;
        }
 
-       i = 0;
-       while (tests[i].func) {
+       for_each_test(j, t) {
                int curr = i++, err;
 
-               if (!perf_test__matches(curr, argc, argv))
+               if (!perf_test__matches(t, curr, argc, argv))
                        continue;
 
-               pr_info("%2d: %-*s:", i, width, tests[curr].desc);
+               pr_info("%2d: %-*s:", i, width, t->desc);
 
                if (intlist__find(skiplist, i)) {
                        color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n");
@@ -265,8 +261,8 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
                }
 
                pr_debug("\n--- start ---\n");
-               err = run_test(&tests[curr]);
-               pr_debug("---- end ----\n%s:", tests[curr].desc);
+               err = run_test(t);
+               pr_debug("---- end ----\n%s:", t->desc);
 
                switch (err) {
                case TEST_OK:
@@ -287,15 +283,15 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
 
 static int perf_test__list(int argc, const char **argv)
 {
+       unsigned int j;
+       struct test *t;
        int i = 0;
 
-       while (tests[i].func) {
-               int curr = i++;
-
-               if (argc > 1 && !strstr(tests[curr].desc, argv[1]))
+       for_each_test(j, t) {
+               if (argc > 1 && !strstr(t->desc, argv[1]))
                        continue;
 
-               pr_info("%2d: %s\n", i, tests[curr].desc);
+               pr_info("%2d: %s\n", ++i, t->desc);
        }
 
        return 0;
index 39c784a100a955143e401fc46cbcb93a2a3d8136..49b1959dda41f0ceb5bc248f3c3e90b558ce9169 100644 (file)
@@ -33,20 +33,20 @@ static unsigned int hex(char c)
        return c - 'A' + 10;
 }
 
-static void read_objdump_line(const char *line, size_t line_len, void **buf,
-                             size_t *len)
+static size_t read_objdump_line(const char *line, size_t line_len, void *buf,
+                             size_t len)
 {
        const char *p;
-       size_t i;
+       size_t i, j = 0;
 
        /* Skip to a colon */
        p = strchr(line, ':');
        if (!p)
-               return;
+               return 0;
        i = p + 1 - line;
 
        /* Read bytes */
-       while (*len) {
+       while (j < len) {
                char c1, c2;
 
                /* Skip spaces */
@@ -65,20 +65,26 @@ static void read_objdump_line(const char *line, size_t line_len, void **buf,
                if (i < line_len && line[i] && !isspace(line[i]))
                        break;
                /* Store byte */
-               *(unsigned char *)*buf = (hex(c1) << 4) | hex(c2);
-               *buf += 1;
-               *len -= 1;
+               *(unsigned char *)buf = (hex(c1) << 4) | hex(c2);
+               buf += 1;
+               j++;
        }
+       /* return number of successfully read bytes */
+       return j;
 }
 
-static int read_objdump_output(FILE *f, void **buf, size_t *len)
+static int read_objdump_output(FILE *f, void *buf, size_t *len, u64 start_addr)
 {
        char *line = NULL;
-       size_t line_len;
+       size_t line_len, off_last = 0;
        ssize_t ret;
        int err = 0;
+       u64 addr, last_addr = start_addr;
+
+       while (off_last < *len) {
+               size_t off, read_bytes, written_bytes;
+               unsigned char tmp[BUFSZ];
 
-       while (1) {
                ret = getline(&line, &line_len, f);
                if (feof(f))
                        break;
@@ -87,9 +93,33 @@ static int read_objdump_output(FILE *f, void **buf, size_t *len)
                        err = -1;
                        break;
                }
-               read_objdump_line(line, ret, buf, len);
+
+               /* read objdump data into temporary buffer */
+               read_bytes = read_objdump_line(line, ret, tmp, sizeof(tmp));
+               if (!read_bytes)
+                       continue;
+
+               if (sscanf(line, "%"PRIx64, &addr) != 1)
+                       continue;
+               if (addr < last_addr) {
+                       pr_debug("addr going backwards, read beyond section?\n");
+                       break;
+               }
+               last_addr = addr;
+
+               /* copy it from temporary buffer to 'buf' according
+                * to address on current objdump line */
+               off = addr - start_addr;
+               if (off >= *len)
+                       break;
+               written_bytes = MIN(read_bytes, *len - off);
+               memcpy(buf + off, tmp, written_bytes);
+               off_last = off + written_bytes;
        }
 
+       /* len returns number of bytes that could not be read */
+       *len -= off_last;
+
        free(line);
 
        return err;
@@ -103,7 +133,7 @@ static int read_via_objdump(const char *filename, u64 addr, void *buf,
        FILE *f;
        int ret;
 
-       fmt = "%s -d --start-address=0x%"PRIx64" --stop-address=0x%"PRIx64" %s";
+       fmt = "%s -z -d --start-address=0x%"PRIx64" --stop-address=0x%"PRIx64" %s";
        ret = snprintf(cmd, sizeof(cmd), fmt, "objdump", addr, addr + len,
                       filename);
        if (ret <= 0 || (size_t)ret >= sizeof(cmd))
@@ -120,7 +150,7 @@ static int read_via_objdump(const char *filename, u64 addr, void *buf,
                return -1;
        }
 
-       ret = read_objdump_output(f, &buf, &len);
+       ret = read_objdump_output(f, buf, &len, addr);
        if (len) {
                pr_debug("objdump read too few bytes\n");
                if (!ret)
@@ -132,6 +162,18 @@ static int read_via_objdump(const char *filename, u64 addr, void *buf,
        return ret;
 }
 
+static void dump_buf(unsigned char *buf, size_t len)
+{
+       size_t i;
+
+       for (i = 0; i < len; i++) {
+               pr_debug("0x%02x ", buf[i]);
+               if (i % 16 == 15)
+                       pr_debug("\n");
+       }
+       pr_debug("\n");
+}
+
 static int read_object_code(u64 addr, size_t len, u8 cpumode,
                            struct thread *thread, struct state *state)
 {
@@ -234,6 +276,10 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode,
        /* The results should be identical */
        if (memcmp(buf1, buf2, len)) {
                pr_debug("Bytes read differ from those read by objdump\n");
+               pr_debug("buf1 (dso):\n");
+               dump_buf(buf1, len);
+               pr_debug("buf2 (objdump):\n");
+               dump_buf(buf2, len);
                return -1;
        }
        pr_debug("Bytes read match those read by objdump\n");
@@ -427,7 +473,7 @@ static int do_test_code_reading(bool try_kcore)
                symbol_conf.kallsyms_name = "/proc/kallsyms";
 
        /* Load kernel map */
-       map = machine->vmlinux_maps[MAP__FUNCTION];
+       map = machine__kernel_map(machine);
        ret = map__load(map, NULL);
        if (ret < 0) {
                pr_debug("map__load failed\n");
index 40b36c4624275a360d4a0f3226eaf850d6d0e825..07221793a3acec65d083f68548d4bf5e287b4628 100644 (file)
 #include "thread.h"
 #include "callchain.h"
 
+#if defined (__x86_64__) || defined (__i386__)
+#include "arch-tests.h"
+#endif
+
 /* For bsearch. We try to unwind functions in shared object. */
 #include <stdlib.h>
 
index 52162425c969ef2b29596658931ec0c85e822182..790e413d9a1f39c1945bb4519a4391ded30560e9 100644 (file)
@@ -1,3 +1,4 @@
+#include <linux/err.h>
 #include <traceevent/event-parse.h>
 #include "evsel.h"
 #include "tests.h"
@@ -36,8 +37,8 @@ int test__perf_evsel__tp_sched_test(void)
        struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch");
        int ret = 0;
 
-       if (evsel == NULL) {
-               pr_debug("perf_evsel__new\n");
+       if (IS_ERR(evsel)) {
+               pr_debug("perf_evsel__newtp failed with %ld\n", PTR_ERR(evsel));
                return -1;
        }
 
@@ -66,6 +67,11 @@ int test__perf_evsel__tp_sched_test(void)
 
        evsel = perf_evsel__newtp("sched", "sched_wakeup");
 
+       if (IS_ERR(evsel)) {
+               pr_debug("perf_evsel__newtp failed with %ld\n", PTR_ERR(evsel));
+               return -1;
+       }
+
        if (perf_evsel__test_field(evsel, "comm", 16, true))
                ret = -1;
 
index ce48775e6ada13886000013183908f3f6b63f26d..818acf875dd0bb4afc7e4e60873125c4b4448a5c 100644 (file)
@@ -16,30 +16,31 @@ struct sample {
        struct thread *thread;
        struct map *map;
        struct symbol *sym;
+       int socket;
 };
 
 /* For the numbers, see hists_common.c */
 static struct sample fake_samples[] = {
        /* perf [kernel] schedule() */
-       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, .socket = 0 },
        /* perf [perf]   main() */
-       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, .socket = 0 },
        /* perf [libc]   malloc() */
-       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, .socket = 0 },
        /* perf [perf]   main() */
-       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, }, /* will be merged */
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, .socket = 0 }, /* will be merged */
        /* perf [perf]   cmd_record() */
-       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, },
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, .socket = 1 },
        /* perf [kernel] page_fault() */
-       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, .socket = 1 },
        /* bash [bash]   main() */
-       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_MAIN, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_MAIN, .socket = 2 },
        /* bash [bash]   xmalloc() */
-       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, .socket = 2 },
        /* bash [libc]   malloc() */
-       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_MALLOC, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_MALLOC, .socket = 3 },
        /* bash [kernel] page_fault() */
-       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, .socket = 3 },
 };
 
 static int add_hist_entries(struct perf_evlist *evlist,
@@ -83,6 +84,7 @@ static int add_hist_entries(struct perf_evlist *evlist,
                                                          &sample) < 0)
                                goto out;
 
+                       al.socket = fake_samples[i].socket;
                        if (hist_entry_iter__add(&iter, &al,
                                                 PERF_MAX_STACK_DEPTH, NULL) < 0) {
                                addr_location__put(&al);
@@ -253,6 +255,39 @@ int test__hists_filter(void)
                TEST_ASSERT_VAL("Unmatched total period for symbol filter",
                                hists->stats.total_non_filtered_period == 300);
 
+               /* remove symbol filter first */
+               hists->symbol_filter_str = NULL;
+               hists__filter_by_symbol(hists);
+
+               /* now applying socket filters */
+               hists->socket_filter = 2;
+               hists__filter_by_socket(hists);
+
+               if (verbose > 2) {
+                       pr_info("Histogram for socket filters\n");
+                       print_hists_out(hists);
+               }
+
+               /* normal stats should be invariant */
+               TEST_ASSERT_VAL("Invalid nr samples",
+                               hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10);
+               TEST_ASSERT_VAL("Invalid nr hist entries",
+                               hists->nr_entries == 9);
+               TEST_ASSERT_VAL("Invalid total period",
+                               hists->stats.total_period == 1000);
+
+               /* but filter stats are changed */
+               TEST_ASSERT_VAL("Unmatched nr samples for socket filter",
+                               hists->stats.nr_non_filtered_samples == 2);
+               TEST_ASSERT_VAL("Unmatched nr hist entries for socket filter",
+                               hists->nr_non_filtered_entries == 2);
+               TEST_ASSERT_VAL("Unmatched total period for socket filter",
+                               hists->stats.total_non_filtered_period == 200);
+
+               /* remove socket filter first */
+               hists->socket_filter = -1;
+               hists__filter_by_socket(hists);
+
                /* now applying all filters at once. */
                hists->thread_filter = fake_samples[1].thread;
                hists->dso_filter = fake_samples[1].map->dso;
index ba31c4bd441d72195fbc48fa72673ba7dad0c337..2cbd0c6901e35ff3954bf06892bb02b3c9f92cef 100644 (file)
@@ -44,6 +44,7 @@ make_no_libnuma     := NO_LIBNUMA=1
 make_no_libaudit    := NO_LIBAUDIT=1
 make_no_libbionic   := NO_LIBBIONIC=1
 make_no_auxtrace    := NO_AUXTRACE=1
+make_no_libbpf     := NO_LIBBPF=1
 make_tags           := tags
 make_cscope         := cscope
 make_help           := help
@@ -66,7 +67,7 @@ make_static         := LDFLAGS=-static
 make_minimal        := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1
 make_minimal        += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1
 make_minimal        += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1
-make_minimal        += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1
+make_minimal        += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1
 
 # $(run) contains all available tests
 run := make_pure
@@ -94,6 +95,7 @@ run += make_no_libnuma
 run += make_no_libaudit
 run += make_no_libbionic
 run += make_no_auxtrace
+run += make_no_libbpf
 run += make_help
 run += make_doc
 run += make_perf_o
index 666b67a4df9dd0d8ea8d41736af4355008c11464..4495493c943111aa6c76fd492d7ca6ee88b74e37 100644 (file)
@@ -3,6 +3,7 @@
 #include "thread_map.h"
 #include "cpumap.h"
 #include "tests.h"
+#include <linux/err.h>
 
 /*
  * This test will generate random numbers of calls to some getpid syscalls,
@@ -65,7 +66,7 @@ int test__basic_mmap(void)
 
                snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]);
                evsels[i] = perf_evsel__newtp("syscalls", name);
-               if (evsels[i] == NULL) {
+               if (IS_ERR(evsels[i])) {
                        pr_debug("perf_evsel__new\n");
                        goto out_delete_evlist;
                }
index a572f87e9c8d8e25665bf75e1c45b1f86ff8a6e9..2006485a2859ba450ddbe6da6c005ff58b5b47d8 100644 (file)
@@ -1,3 +1,5 @@
+#include <api/fs/fs.h>
+#include <linux/err.h>
 #include "evsel.h"
 #include "tests.h"
 #include "thread_map.h"
@@ -14,6 +16,7 @@ int test__openat_syscall_event_on_all_cpus(void)
        cpu_set_t cpu_set;
        struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
        char sbuf[STRERR_BUFSIZE];
+       char errbuf[BUFSIZ];
 
        if (threads == NULL) {
                pr_debug("thread_map__new\n");
@@ -29,13 +32,9 @@ int test__openat_syscall_event_on_all_cpus(void)
        CPU_ZERO(&cpu_set);
 
        evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
-       if (evsel == NULL) {
-               if (tracefs_configured())
-                       pr_debug("is tracefs mounted on /sys/kernel/tracing?\n");
-               else if (debugfs_configured())
-                       pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
-               else
-                       pr_debug("Neither tracefs or debugfs is enabled in this kernel\n");
+       if (IS_ERR(evsel)) {
+               tracing_path__strerror_open_tp(errno, errbuf, sizeof(errbuf), "syscalls", "sys_enter_openat");
+               pr_debug("%s\n", errbuf);
                goto out_thread_map_delete;
        }
 
index 01a19626c84624136d909e1c3d3e58ef877d1b39..5e811cd8f1c3e14567ef38e9da9595f502946c5d 100644 (file)
@@ -1,3 +1,4 @@
+#include <linux/err.h>
 #include "perf.h"
 #include "evlist.h"
 #include "evsel.h"
@@ -30,7 +31,7 @@ int test__syscall_openat_tp_fields(void)
        }
 
        evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
-       if (evsel == NULL) {
+       if (IS_ERR(evsel)) {
                pr_debug("%s: perf_evsel__newtp\n", __func__);
                goto out_delete_evlist;
        }
@@ -88,7 +89,7 @@ int test__syscall_openat_tp_fields(void)
 
                                err = perf_evsel__parse_sample(evsel, event, &sample);
                                if (err) {
-                                       pr_err("Can't parse sample, err = %d\n", err);
+                                       pr_debug("Can't parse sample, err = %d\n", err);
                                        goto out_delete_evlist;
                                }
 
index c9a37bc6b33ac336a128a4948623a1a93a5b388f..033b54797b8a021fe5f5c7ed25459a23d9cb5b6c 100644 (file)
@@ -1,3 +1,5 @@
+#include <api/fs/tracing_path.h>
+#include <linux/err.h>
 #include "thread_map.h"
 #include "evsel.h"
 #include "debug.h"
@@ -10,6 +12,7 @@ int test__openat_syscall_event(void)
        unsigned int nr_openat_calls = 111, i;
        struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
        char sbuf[STRERR_BUFSIZE];
+       char errbuf[BUFSIZ];
 
        if (threads == NULL) {
                pr_debug("thread_map__new\n");
@@ -17,13 +20,9 @@ int test__openat_syscall_event(void)
        }
 
        evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
-       if (evsel == NULL) {
-               if (tracefs_configured())
-                       pr_debug("is tracefs mounted on /sys/kernel/tracing?\n");
-               else if (debugfs_configured())
-                       pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
-               else
-                       pr_debug("Neither tracefs or debugfs is enabled in this kernel\n");
+       if (IS_ERR(evsel)) {
+               tracing_path__strerror_open_tp(errno, errbuf, sizeof(errbuf), "syscalls", "sys_enter_openat");
+               pr_debug("%s\n", errbuf);
                goto out_thread_map_delete;
        }
 
index 9b6b2b6324a1d479835af199e4f02e129c949075..636d7b42d8447f93917ce64b0aff528fd3fceed6 100644 (file)
@@ -3,11 +3,11 @@
 #include "evsel.h"
 #include "evlist.h"
 #include <api/fs/fs.h>
-#include <api/fs/tracefs.h>
-#include <api/fs/debugfs.h>
 #include "tests.h"
 #include "debug.h"
+#include "util.h"
 #include <linux/hw_breakpoint.h>
+#include <api/fs/fs.h>
 
 #define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \
                             PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD)
@@ -1260,25 +1260,24 @@ test__checkevent_breakpoint_len_rw_modifier(struct perf_evlist *evlist)
        return test__checkevent_breakpoint_rw(evlist);
 }
 
+static int test__checkevent_precise_max_modifier(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel = perf_evlist__first(evlist);
+
+       TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config",
+                       PERF_COUNT_SW_TASK_CLOCK == evsel->attr.config);
+       return 0;
+}
+
 static int count_tracepoints(void)
 {
-       char events_path[PATH_MAX];
        struct dirent *events_ent;
-       const char *mountpoint;
        DIR *events_dir;
        int cnt = 0;
 
-       mountpoint = tracefs_find_mountpoint();
-       if (mountpoint) {
-               scnprintf(events_path, PATH_MAX, "%s/events",
-                         mountpoint);
-       } else {
-               mountpoint = debugfs_find_mountpoint();
-               scnprintf(events_path, PATH_MAX, "%s/tracing/events",
-                         mountpoint);
-       }
-
-       events_dir = opendir(events_path);
+       events_dir = opendir(tracing_events_path);
 
        TEST_ASSERT_VAL("Can't open events dir", events_dir);
 
@@ -1295,7 +1294,7 @@ static int count_tracepoints(void)
                        continue;
 
                scnprintf(sys_path, PATH_MAX, "%s/%s",
-                         events_path, events_ent->d_name);
+                         tracing_events_path, events_ent->d_name);
 
                sys_dir = opendir(sys_path);
                TEST_ASSERT_VAL("Can't open sys dir", sys_dir);
@@ -1575,6 +1574,11 @@ static struct evlist_test test__events[] = {
                .check = test__checkevent_exclude_idle_modifier_1,
                .id    = 46,
        },
+       {
+               .name  = "task-clock:P,cycles",
+               .check = test__checkevent_precise_max_modifier,
+               .id    = 47,
+       },
 };
 
 static struct evlist_test test__events_pmu[] = {
@@ -1750,6 +1754,17 @@ static int test_pmu_events(void)
        return ret;
 }
 
+static void debug_warn(const char *warn, va_list params)
+{
+       char msg[1024];
+
+       if (!verbose)
+               return;
+
+       vsnprintf(msg, sizeof(msg), warn, params);
+       fprintf(stderr, " Warning: %s\n", msg);
+}
+
 int test__parse_events(void)
 {
        int ret1, ret2 = 0;
@@ -1761,6 +1776,8 @@ do {                                                      \
                ret2 = ret1;                            \
 } while (0)
 
+       set_warning_routine(debug_warn);
+
        TEST_EVENTS(test__events);
 
        if (test_pmu())
index bf113a247987e7beb271f25da5343f821e5c12da..c80486969f83412621336a33ee95edef87d2fb31 100644 (file)
@@ -24,13 +24,17 @@ enum {
        TEST_SKIP = -2,
 };
 
+struct test {
+       const char *desc;
+       int (*func)(void);
+};
+
 /* Tests */
 int test__vmlinux_matches_kallsyms(void);
 int test__openat_syscall_event(void);
 int test__openat_syscall_event_on_all_cpus(void);
 int test__basic_mmap(void);
 int test__PERF_RECORD(void);
-int test__rdpmc(void);
 int test__perf_evsel__roundtrip_name_test(void);
 int test__perf_evsel__tp_sched_test(void);
 int test__syscall_openat_tp_fields(void);
@@ -46,7 +50,6 @@ int test__bp_signal(void);
 int test__bp_signal_overflow(void);
 int test__task_exit(void);
 int test__sw_clock_freq(void);
-int test__perf_time_to_tsc(void);
 int test__code_reading(void);
 int test__sample_parsing(void);
 int test__keep_tracking(void);
@@ -63,8 +66,9 @@ int test__fdarray__add(void);
 int test__kmod_path__parse(void);
 int test__thread_map(void);
 int test__llvm(void);
+int test_session_topology(void);
 
-#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
+#if defined(__arm__) || defined(__aarch64__)
 #ifdef HAVE_DWARF_UNWIND_SUPPORT
 struct thread;
 struct perf_sample;
diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c
new file mode 100644 (file)
index 0000000..f5bb096
--- /dev/null
@@ -0,0 +1,115 @@
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "tests.h"
+#include "util.h"
+#include "session.h"
+#include "evlist.h"
+#include "debug.h"
+
+#define TEMPL "/tmp/perf-test-XXXXXX"
+#define DATA_SIZE      10
+
+static int get_temp(char *path)
+{
+       int fd;
+
+       strcpy(path, TEMPL);
+
+       fd = mkstemp(path);
+       if (fd < 0) {
+               perror("mkstemp failed");
+               return -1;
+       }
+
+       close(fd);
+       return 0;
+}
+
+static int session_write_header(char *path)
+{
+       struct perf_session *session;
+       struct perf_data_file file = {
+               .path = path,
+               .mode = PERF_DATA_MODE_WRITE,
+       };
+
+       session = perf_session__new(&file, false, NULL);
+       TEST_ASSERT_VAL("can't get session", session);
+
+       session->evlist = perf_evlist__new_default();
+       TEST_ASSERT_VAL("can't get evlist", session->evlist);
+
+       perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY);
+       perf_header__set_feat(&session->header, HEADER_NRCPUS);
+
+       session->header.data_size += DATA_SIZE;
+
+       TEST_ASSERT_VAL("failed to write header",
+                       !perf_session__write_header(session, session->evlist, file.fd, true));
+
+       perf_session__delete(session);
+
+       return 0;
+}
+
+static int check_cpu_topology(char *path, struct cpu_map *map)
+{
+       struct perf_session *session;
+       struct perf_data_file file = {
+               .path = path,
+               .mode = PERF_DATA_MODE_READ,
+       };
+       int i;
+
+       session = perf_session__new(&file, false, NULL);
+       TEST_ASSERT_VAL("can't get session", session);
+
+       for (i = 0; i < session->header.env.nr_cpus_online; i++) {
+               pr_debug("CPU %d, core %d, socket %d\n", i,
+                        session->header.env.cpu[i].core_id,
+                        session->header.env.cpu[i].socket_id);
+       }
+
+       for (i = 0; i < map->nr; i++) {
+               TEST_ASSERT_VAL("Core ID doesn't match",
+                       (session->header.env.cpu[map->map[i]].core_id == (cpu_map__get_core(map, i, NULL) & 0xffff)));
+
+               TEST_ASSERT_VAL("Socket ID doesn't match",
+                       (session->header.env.cpu[map->map[i]].socket_id == cpu_map__get_socket(map, i, NULL)));
+       }
+
+       perf_session__delete(session);
+
+       return 0;
+}
+
+int test_session_topology(void)
+{
+       char path[PATH_MAX];
+       struct cpu_map *map;
+       int ret = -1;
+
+       TEST_ASSERT_VAL("can't get templ file", !get_temp(path));
+
+       pr_debug("templ file: %s\n", path);
+
+       if (session_write_header(path))
+               goto free_path;
+
+       map = cpu_map__new(NULL);
+       if (map == NULL) {
+               pr_debug("failed to get system cpumap\n");
+               goto free_path;
+       }
+
+       if (check_cpu_topology(path, map))
+               goto free_map;
+       ret = 0;
+
+free_map:
+       cpu_map__put(map);
+free_path:
+       unlink(path);
+       return ret;
+}
index b34c5fc829ae2b0da7389bd31649dd0389e40604..d677e018e50426a8b2c2b417a63b8dfbe36949b7 100644 (file)
@@ -68,7 +68,7 @@ int test__vmlinux_matches_kallsyms(void)
         * to see if the running kernel was relocated by checking if it has the
         * same value in the vmlinux file we load.
         */
-       kallsyms_map = machine__kernel_map(&kallsyms, type);
+       kallsyms_map = machine__kernel_map(&kallsyms);
 
        /*
         * Step 5:
@@ -80,7 +80,7 @@ int test__vmlinux_matches_kallsyms(void)
                goto out;
        }
 
-       vmlinux_map = machine__kernel_map(&vmlinux, type);
+       vmlinux_map = machine__kernel_map(&vmlinux);
 
        /*
         * Step 6:
index 62378a899d79b116d53102c6a8a082ef80080365..722e25d200bf9f9861232d499ddb75b41dccd14d 100644 (file)
@@ -9,6 +9,7 @@ mkdir
 open
 openat
 quotactl
+read
 readlink
 rename
 rmdir
@@ -16,3 +17,4 @@ stat
 statfs
 symlink
 unlink
+write
index c6c7e5189214fb7f8df1c3fb3659a673d80e8ffa..e9703c0829f104bf9d7819d04831306cfd31dbe8 100644 (file)
@@ -393,6 +393,7 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs)
 
                if (browser->use_navkeypressed && !browser->navkeypressed) {
                        if (key == K_DOWN || key == K_UP ||
+                           (browser->columns && (key == K_LEFT || key == K_RIGHT)) ||
                            key == K_PGDN || key == K_PGUP ||
                            key == K_HOME || key == K_END ||
                            key == ' ') {
@@ -421,6 +422,18 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs)
                                browser->seek(browser, -1, SEEK_CUR);
                        }
                        break;
+               case K_RIGHT:
+                       if (!browser->columns)
+                               goto out;
+                       if (browser->horiz_scroll < browser->columns - 1)
+                               ++browser->horiz_scroll;
+                       break;
+               case K_LEFT:
+                       if (!browser->columns)
+                               goto out;
+                       if (browser->horiz_scroll != 0)
+                               --browser->horiz_scroll;
+                       break;
                case K_PGDN:
                case ' ':
                        if (browser->top_idx + browser->rows > browser->nr_entries - 1)
@@ -459,6 +472,7 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs)
                        browser->seek(browser, -offset, SEEK_END);
                        break;
                default:
+               out:
                        return key;
                }
        }
index f3cef564de02e7262c820d52b3063543adf9c2fc..01781de59532ce9c9fd1ff7f8c7fdf97d45fa105 100644 (file)
@@ -14,7 +14,7 @@
 struct ui_browser {
        u64           index, top_idx;
        void          *top, *entries;
-       u16           y, x, width, height, rows;
+       u16           y, x, width, height, rows, columns, horiz_scroll;
        int           current_color;
        void          *priv;
        const char    *title;
index 29739b34759923f6a7c099f070c5689fd80a03d3..d4d7cc27252f1184bf2d678b6460f138099efd28 100644 (file)
@@ -768,8 +768,8 @@ static int annotate_browser__run(struct annotate_browser *browser,
                "UP/DOWN/PGUP\n"
                "PGDN/SPACE    Navigate\n"
                "q/ESC/CTRL+C  Exit\n\n"
-               "->            Go to target\n"
-               "<-            Exit\n"
+               "ENTER         Go to target\n"
+               "ESC           Exit\n"
                "H             Cycle thru hottest instructions\n"
                "j             Toggle showing jump to target arrows\n"
                "J             Toggle showing number of jump sources on targets\n"
@@ -1056,7 +1056,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map,
                goto out_free_offsets;
        }
 
-       ui_helpline__push("Press <- or ESC to exit");
+       ui_helpline__push("Press ESC to exit");
 
        notes = symbol__annotation(sym);
        browser.start = map__rip_2objdump(map, sym->start);
@@ -1125,8 +1125,8 @@ static struct annotate_config {
        ANNOTATE_CFG(jump_arrows),
        ANNOTATE_CFG(show_linenr),
        ANNOTATE_CFG(show_nr_jumps),
-       ANNOTATE_CFG(use_offset),
        ANNOTATE_CFG(show_total_period),
+       ANNOTATE_CFG(use_offset),
 };
 
 #undef ANNOTATE_CFG
@@ -1152,9 +1152,9 @@ static int annotate__config(const char *var, const char *value,
                      sizeof(struct annotate_config), annotate_config__cmp);
 
        if (cfg == NULL)
-               return -1;
-
-       *cfg->value = perf_config_bool(name, value);
+               ui__warning("%s variable unknown, ignoring...", var);
+       else
+               *cfg->value = perf_config_bool(name, value);
        return 0;
 }
 
index c04c60d4863ce71a08014c396aa7342c8af2fa16..e5afb893604044f189da5a0f50962f990362e0da 100644 (file)
@@ -784,11 +784,12 @@ static int hist_browser__show_entry(struct hist_browser *browser,
                        .size           = sizeof(s),
                        .ptr            = &arg,
                };
+               int column = 0;
 
                hist_browser__gotorc(browser, row, 0);
 
                perf_hpp__for_each_format(fmt) {
-                       if (perf_hpp__should_skip(fmt))
+                       if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
                                continue;
 
                        if (current_entry && browser->b.navkeypressed) {
@@ -861,14 +862,16 @@ static int advance_hpp_check(struct perf_hpp *hpp, int inc)
        return hpp->size <= 0;
 }
 
-static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
+static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
 {
+       struct hists *hists = browser->hists;
        struct perf_hpp dummy_hpp = {
                .buf    = buf,
                .size   = size,
        };
        struct perf_hpp_fmt *fmt;
        size_t ret = 0;
+       int column = 0;
 
        if (symbol_conf.use_callchain) {
                ret = scnprintf(buf, size, "  ");
@@ -877,7 +880,7 @@ static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
        }
 
        perf_hpp__for_each_format(fmt) {
-               if (perf_hpp__should_skip(fmt))
+               if (perf_hpp__should_skip(fmt)  || column++ < browser->b.horiz_scroll)
                        continue;
 
                ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
@@ -896,7 +899,7 @@ static void hist_browser__show_headers(struct hist_browser *browser)
 {
        char headers[1024];
 
-       hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
+       hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
        ui_browser__gotorc(&browser->b, 0, 0);
        ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
        ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
@@ -1261,6 +1264,7 @@ static int hists__browser_title(struct hists *hists,
        int printed;
        const struct dso *dso = hists->dso_filter;
        const struct thread *thread = hists->thread_filter;
+       int socket_id = hists->socket_filter;
        unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
        u64 nr_events = hists->stats.total_period;
        struct perf_evsel *evsel = hists_to_evsel(hists);
@@ -1314,6 +1318,9 @@ static int hists__browser_title(struct hists *hists,
        if (dso)
                printed += scnprintf(bf + printed, size - printed,
                                    ", DSO: %s", dso->short_name);
+       if (socket_id > -1)
+               printed += scnprintf(bf + printed, size - printed,
+                                   ", Processor Socket: %d", socket_id);
        if (!is_report_browser(hbt)) {
                struct perf_top *top = hbt->arg;
 
@@ -1425,6 +1432,7 @@ struct popup_action {
        struct thread           *thread;
        struct dso              *dso;
        struct map_symbol       ms;
+       int                     socket;
 
        int (*fn)(struct hist_browser *browser, struct popup_action *act);
 };
@@ -1437,7 +1445,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act)
        struct hist_entry *he;
        int err;
 
-       if (!objdump_path && perf_session_env__lookup_objdump(browser->env))
+       if (!objdump_path && perf_env__lookup_objdump(browser->env))
                return 0;
 
        notes = symbol__annotation(act->ms.sym);
@@ -1488,7 +1496,7 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
                thread__zput(browser->hists->thread_filter);
                ui_helpline__pop();
        } else {
-               ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
+               ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
                                   thread->comm_set ? thread__comm_str(thread) : "",
                                   thread->tid);
                browser->hists->thread_filter = thread__get(thread);
@@ -1522,7 +1530,7 @@ add_thread_opt(struct hist_browser *browser, struct popup_action *act,
 static int
 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
 {
-       struct dso *dso = act->dso;
+       struct map *map = act->ms.map;
 
        if (browser->hists->dso_filter) {
                pstack__remove(browser->pstack, &browser->hists->dso_filter);
@@ -1530,11 +1538,11 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
                browser->hists->dso_filter = NULL;
                ui_helpline__pop();
        } else {
-               if (dso == NULL)
+               if (map == NULL)
                        return 0;
-               ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
-                                  dso->kernel ? "the Kernel" : dso->short_name);
-               browser->hists->dso_filter = dso;
+               ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
+                                  __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
+               browser->hists->dso_filter = map->dso;
                perf_hpp__set_elide(HISTC_DSO, true);
                pstack__push(browser->pstack, &browser->hists->dso_filter);
        }
@@ -1546,17 +1554,18 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
 
 static int
 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
-           char **optstr, struct dso *dso)
+           char **optstr, struct map *map)
 {
-       if (dso == NULL)
+       if (map == NULL)
                return 0;
 
        if (asprintf(optstr, "Zoom %s %s DSO",
                     browser->hists->dso_filter ? "out of" : "into",
-                    dso->kernel ? "the Kernel" : dso->short_name) < 0)
+                    __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
                return 0;
 
-       act->dso = dso;
+       act->ms.map = map;
+       act->dso = map->dso;
        act->fn = do_zoom_dso;
        return 1;
 }
@@ -1672,6 +1681,41 @@ add_exit_opt(struct hist_browser *browser __maybe_unused,
        return 1;
 }
 
+static int
+do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
+{
+       if (browser->hists->socket_filter > -1) {
+               pstack__remove(browser->pstack, &browser->hists->socket_filter);
+               browser->hists->socket_filter = -1;
+               perf_hpp__set_elide(HISTC_SOCKET, false);
+       } else {
+               browser->hists->socket_filter = act->socket;
+               perf_hpp__set_elide(HISTC_SOCKET, true);
+               pstack__push(browser->pstack, &browser->hists->socket_filter);
+       }
+
+       hists__filter_by_socket(browser->hists);
+       hist_browser__reset(browser);
+       return 0;
+}
+
+static int
+add_socket_opt(struct hist_browser *browser, struct popup_action *act,
+              char **optstr, int socket_id)
+{
+       if (socket_id < 0)
+               return 0;
+
+       if (asprintf(optstr, "Zoom %s Processor Socket %d",
+                    (browser->hists->socket_filter > -1) ? "out of" : "into",
+                    socket_id) < 0)
+               return 0;
+
+       act->socket = socket_id;
+       act->fn = do_zoom_socket;
+       return 1;
+}
+
 static void hist_browser__update_nr_entries(struct hist_browser *hb)
 {
        u64 nr_entries = 0;
@@ -1717,14 +1761,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
        "For multiple event sessions:\n\n"                              \
        "TAB/UNTAB     Switch events\n\n"                               \
        "For symbolic views (--sort has sym):\n\n"                      \
-       "->            Zoom into DSO/Threads & Annotate current symbol\n" \
-       "<-            Zoom out\n"                                      \
+       "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
+       "ESC           Zoom out\n"                                      \
        "a             Annotate current symbol\n"                       \
        "C             Collapse all callchains\n"                       \
        "d             Zoom into current DSO\n"                         \
        "E             Expand all callchains\n"                         \
        "F             Toggle percentage of filtered entries\n"         \
        "H             Display column headers\n"                        \
+       "m             Display context menu\n"                          \
+       "S             Zoom into current Processor Socket\n"            \
 
        /* help messages are sorted by lexical order of the hotkey */
        const char report_help[] = HIST_BROWSER_HELP_COMMON
@@ -1755,7 +1801,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                hist_browser__update_nr_entries(browser);
        }
 
-       browser->pstack = pstack__new(2);
+       browser->pstack = pstack__new(3);
        if (browser->pstack == NULL)
                goto out;
 
@@ -1764,8 +1810,17 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
        memset(options, 0, sizeof(options));
        memset(actions, 0, sizeof(actions));
 
-       perf_hpp__for_each_format(fmt)
+       perf_hpp__for_each_format(fmt) {
                perf_hpp__reset_width(fmt, hists);
+               /*
+                * This is done just once, and activates the horizontal scrolling
+                * code in the ui_browser code, it would be better to have a the
+                * counter in the perf_hpp code, but I couldn't find doing it here
+                * works, FIXME by setting this in hist_browser__new, for now, be
+                * clever 8-)
+                */
+               ++browser->b.columns;
+       }
 
        if (symbol_conf.col_width_list_str)
                perf_hpp__set_user_width(symbol_conf.col_width_list_str);
@@ -1773,7 +1828,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
        while (1) {
                struct thread *thread = NULL;
                struct dso *dso = NULL;
+               struct map *map = NULL;
                int choice = 0;
+               int socked_id = -1;
 
                nr_options = 0;
 
@@ -1781,7 +1838,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
 
                if (browser->he_selection != NULL) {
                        thread = hist_browser__selected_thread(browser);
-                       dso = browser->selection->map ? browser->selection->map->dso : NULL;
+                       map = browser->selection->map;
+                       if (map)
+                               dso = map->dso;
+                       socked_id = browser->he_selection->socket;
                }
                switch (key) {
                case K_TAB:
@@ -1824,9 +1884,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                        actions->thread = thread;
                        do_zoom_thread(browser, actions);
                        continue;
+               case 'S':
+                       actions->socket = socked_id;
+                       do_zoom_socket(browser, actions);
+                       continue;
                case '/':
                        if (ui_browser__input_window("Symbol to show",
-                                       "Please enter the name of symbol you want to see",
+                                       "Please enter the name of symbol you want to see.\n"
+                                       "To remove the filter later, press / + ENTER.",
                                        buf, "ENTER: OK, ESC: Cancel",
                                        delay_secs * 2) == K_ENTER) {
                                hists->symbol_filter_str = *buf ? buf : NULL;
@@ -1871,6 +1936,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                        continue;
                case K_ENTER:
                case K_RIGHT:
+               case 'm':
                        /* menu */
                        break;
                case K_ESC:
@@ -1899,9 +1965,11 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                                 * Ditto for thread below.
                                 */
                                do_zoom_dso(browser, actions);
-                       }
-                       if (top == &browser->hists->thread_filter)
+                       } else if (top == &browser->hists->thread_filter) {
                                do_zoom_thread(browser, actions);
+                       } else if (top == &browser->hists->socket_filter) {
+                               do_zoom_socket(browser, actions);
+                       }
                        continue;
                }
                case 'q':
@@ -1965,12 +2033,14 @@ skip_annotation:
                nr_options += add_thread_opt(browser, &actions[nr_options],
                                             &options[nr_options], thread);
                nr_options += add_dso_opt(browser, &actions[nr_options],
-                                         &options[nr_options], dso);
+                                         &options[nr_options], map);
                nr_options += add_map_opt(browser, &actions[nr_options],
                                          &options[nr_options],
                                          browser->selection ?
                                                browser->selection->map : NULL);
-
+               nr_options += add_socket_opt(browser, &actions[nr_options],
+                                            &options[nr_options],
+                                            socked_id);
                /* perf script support */
                if (browser->he_selection) {
                        nr_options += add_script_opt(browser,
index 8c154c7d46697455c6fb8da8e8362f8c24f85953..80912778bb6d5623bd6555fd7f843a84acb1d51a 100644 (file)
@@ -72,7 +72,7 @@ static int map_browser__run(struct map_browser *browser)
        int key;
 
        if (ui_browser__show(&browser->b, browser->map->dso->long_name,
-                            "Press <- or ESC to exit, %s / to search",
+                            "Press ESC to exit, %s / to search",
                             verbose ? "" : "restart with -v to use") < 0)
                return -1;
 
index e13b48d705ef454a2ebe8be5708ca6eabf0f85d5..ad6b6ee3770e436339fa3d68b2c016e1a7e9dfb7 100644 (file)
@@ -89,7 +89,7 @@ static int script_browser__run(struct perf_script_browser *browser)
        int key;
 
        if (ui_browser__show(&browser->b, browser->script_name,
-                            "Press <- or ESC to exit") < 0)
+                            "Press ESC to exit") < 0)
                return -1;
 
        while (1) {
index 25d608394d746fcb7435517915bed787f20aa674..5029ba2b55af0cb20daaeec875d1f493dc807e62 100644 (file)
@@ -463,27 +463,27 @@ void perf_hpp__init(void)
                return;
 
        if (symbol_conf.cumulate_callchain) {
-               perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC);
+               hpp_dimension__add_output(PERF_HPP__OVERHEAD_ACC);
                perf_hpp__format[PERF_HPP__OVERHEAD].name = "Self";
        }
 
-       perf_hpp__column_enable(PERF_HPP__OVERHEAD);
+       hpp_dimension__add_output(PERF_HPP__OVERHEAD);
 
        if (symbol_conf.show_cpu_utilization) {
-               perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS);
-               perf_hpp__column_enable(PERF_HPP__OVERHEAD_US);
+               hpp_dimension__add_output(PERF_HPP__OVERHEAD_SYS);
+               hpp_dimension__add_output(PERF_HPP__OVERHEAD_US);
 
                if (perf_guest) {
-                       perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS);
-                       perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US);
+                       hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_SYS);
+                       hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_US);
                }
        }
 
        if (symbol_conf.show_nr_samples)
-               perf_hpp__column_enable(PERF_HPP__SAMPLES);
+               hpp_dimension__add_output(PERF_HPP__SAMPLES);
 
        if (symbol_conf.show_total_period)
-               perf_hpp__column_enable(PERF_HPP__PERIOD);
+               hpp_dimension__add_output(PERF_HPP__PERIOD);
 
        /* prepend overhead field for backward compatiblity.  */
        list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list;
index 60d1f29b4b50a9fedf0a163855056edfce1ed22b..7dfeba0a91f37c33c87b826e9ed79ec4ad59869c 100644 (file)
@@ -141,10 +141,6 @@ int ui__init(void)
 
        SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB);
 
-       ui_helpline__init();
-       ui_browser__init();
-       tui_progress__init();
-
        signal(SIGSEGV, ui__signal_backtrace);
        signal(SIGFPE, ui__signal_backtrace);
        signal(SIGINT, ui__signal);
@@ -153,6 +149,10 @@ int ui__init(void)
 
        perf_error__register(&perf_tui_eops);
 
+       ui_helpline__init();
+       ui_browser__init();
+       tui_progress__init();
+
        hist_browser__init_hpp();
 out:
        return err;
index e5f18a288b7489a93e880657401dd1f99014deda..591b3fe3ed49acd8b31f701a78b3302a84786546 100644 (file)
@@ -5,6 +5,7 @@ libperf-y += build-id.o
 libperf-y += config.o
 libperf-y += ctype.o
 libperf-y += db-export.o
+libperf-y += env.o
 libperf-y += environment.o
 libperf-y += event.o
 libperf-y += evlist.o
@@ -86,6 +87,7 @@ libperf-$(CONFIG_AUXTRACE) += intel-bts.o
 libperf-y += parse-branch-options.o
 libperf-y += parse-regs-options.o
 
+libperf-$(CONFIG_LIBBPF) += bpf-loader.o
 libperf-$(CONFIG_LIBELF) += symbol-elf.o
 libperf-$(CONFIG_LIBELF) += probe-file.o
 libperf-$(CONFIG_LIBELF) += probe-event.o
index d1eece70b84d6e00c527ccd6027362517013aba8..0fc8d7a2fea5f2ddee03789c3c0b442278f0fb0a 100644 (file)
@@ -548,8 +548,11 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map,
 
        pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));
 
-       if (addr < sym->start || addr >= sym->end)
+       if (addr < sym->start || addr >= sym->end) {
+               pr_debug("%s(%d): ERANGE! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 "\n",
+                      __func__, __LINE__, sym->name, sym->start, addr, sym->end);
                return -ERANGE;
+       }
 
        offset = addr - sym->start;
        h = annotation__histogram(notes, evidx);
index e9996092a093d7ef6af5aa72ac78fdd5cab61d81..cea323d9ee7eabbd2101eff85be5e0f564520b91 100644 (file)
@@ -122,7 +122,7 @@ struct annotated_source {
        struct list_head   source;
        struct source_line *lines;
        int                nr_histograms;
-       int                sizeof_sym_hist;
+       size_t             sizeof_sym_hist;
        struct cyc_hist    *cycles_hist;
        struct sym_hist    histograms[0];
 };
index a980e7c50ee078c47f6579e454572c67616bdc0d..7f10430af39c3ac9e47f4da1aca93e37a8f9cf87 100644 (file)
@@ -926,6 +926,8 @@ s64 perf_event__process_auxtrace(struct perf_tool *tool,
 #define PERF_ITRACE_DEFAULT_PERIOD             100000
 #define PERF_ITRACE_DEFAULT_CALLCHAIN_SZ       16
 #define PERF_ITRACE_MAX_CALLCHAIN_SZ           1024
+#define PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ     64
+#define PERF_ITRACE_MAX_LAST_BRANCH_SZ         1024
 
 void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts)
 {
@@ -936,6 +938,7 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts)
        synth_opts->period_type = PERF_ITRACE_DEFAULT_PERIOD_TYPE;
        synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD;
        synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ;
+       synth_opts->last_branch_sz = PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ;
 }
 
 /*
@@ -950,6 +953,7 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
        const char *p;
        char *endptr;
        bool period_type_set = false;
+       bool period_set = false;
 
        synth_opts->set = true;
 
@@ -971,6 +975,7 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
                                p += 1;
                        if (isdigit(*p)) {
                                synth_opts->period = strtoull(p, &endptr, 10);
+                               period_set = true;
                                p = endptr;
                                while (*p == ' ' || *p == ',')
                                        p += 1;
@@ -1041,6 +1046,23 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
                                synth_opts->callchain_sz = val;
                        }
                        break;
+               case 'l':
+                       synth_opts->last_branch = true;
+                       synth_opts->last_branch_sz =
+                                       PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ;
+                       while (*p == ' ' || *p == ',')
+                               p += 1;
+                       if (isdigit(*p)) {
+                               unsigned int val;
+
+                               val = strtoul(p, &endptr, 10);
+                               p = endptr;
+                               if (!val ||
+                                   val > PERF_ITRACE_MAX_LAST_BRANCH_SZ)
+                                       goto out_err;
+                               synth_opts->last_branch_sz = val;
+                       }
+                       break;
                case ' ':
                case ',':
                        break;
@@ -1053,7 +1075,7 @@ out:
                if (!period_type_set)
                        synth_opts->period_type =
                                        PERF_ITRACE_DEFAULT_PERIOD_TYPE;
-               if (!synth_opts->period)
+               if (!period_set)
                        synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD;
        }
 
index bf72b77a588a60b330a8af18622ba8cd9a371bf5..b86f90db1352a6c8635e3ea5d02aa3c21bccc323 100644 (file)
@@ -63,7 +63,9 @@ enum itrace_period_type {
  * @calls: limit branch samples to calls (can be combined with @returns)
  * @returns: limit branch samples to returns (can be combined with @calls)
  * @callchain: add callchain to 'instructions' events
+ * @last_branch: add branch context to 'instruction' events
  * @callchain_sz: maximum callchain size
+ * @last_branch_sz: branch context size
  * @period: 'instructions' events period
  * @period_type: 'instructions' events period type
  */
@@ -79,7 +81,9 @@ struct itrace_synth_opts {
        bool                    calls;
        bool                    returns;
        bool                    callchain;
+       bool                    last_branch;
        unsigned int            callchain_sz;
+       unsigned int            last_branch_sz;
        unsigned long long      period;
        enum itrace_period_type period_type;
 };
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
new file mode 100644 (file)
index 0000000..ba6f752
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * bpf-loader.c
+ *
+ * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+
+#include <bpf/libbpf.h>
+#include <linux/err.h>
+#include "perf.h"
+#include "debug.h"
+#include "bpf-loader.h"
+#include "probe-event.h"
+#include "probe-finder.h" // for MAX_PROBES
+#include "llvm-utils.h"
+
+#define DEFINE_PRINT_FN(name, level) \
+static int libbpf_##name(const char *fmt, ...) \
+{                                              \
+       va_list args;                           \
+       int ret;                                \
+                                               \
+       va_start(args, fmt);                    \
+       ret = veprintf(level, verbose, pr_fmt(fmt), args);\
+       va_end(args);                           \
+       return ret;                             \
+}
+
+DEFINE_PRINT_FN(warning, 0)
+DEFINE_PRINT_FN(info, 0)
+DEFINE_PRINT_FN(debug, 1)
+
+struct bpf_prog_priv {
+       struct perf_probe_event pev;
+};
+
+struct bpf_object *bpf__prepare_load(const char *filename, bool source)
+{
+       struct bpf_object *obj;
+       static bool libbpf_initialized;
+
+       if (!libbpf_initialized) {
+               libbpf_set_print(libbpf_warning,
+                                libbpf_info,
+                                libbpf_debug);
+               libbpf_initialized = true;
+       }
+
+       if (source) {
+               int err;
+               void *obj_buf;
+               size_t obj_buf_sz;
+
+               err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz);
+               if (err)
+                       return ERR_PTR(err);
+               obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename);
+               free(obj_buf);
+       } else
+               obj = bpf_object__open(filename);
+
+       if (!obj) {
+               pr_debug("bpf: failed to load %s\n", filename);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return obj;
+}
+
+void bpf__clear(void)
+{
+       struct bpf_object *obj, *tmp;
+
+       bpf_object__for_each_safe(obj, tmp) {
+               bpf__unprobe(obj);
+               bpf_object__close(obj);
+       }
+}
+
+static void
+bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
+                    void *_priv)
+{
+       struct bpf_prog_priv *priv = _priv;
+
+       cleanup_perf_probe_events(&priv->pev, 1);
+       free(priv);
+}
+
+static int
+config_bpf_program(struct bpf_program *prog)
+{
+       struct perf_probe_event *pev = NULL;
+       struct bpf_prog_priv *priv = NULL;
+       const char *config_str;
+       int err;
+
+       config_str = bpf_program__title(prog, false);
+       if (!config_str) {
+               pr_debug("bpf: unable to get title for program\n");
+               return -EINVAL;
+       }
+
+       priv = calloc(sizeof(*priv), 1);
+       if (!priv) {
+               pr_debug("bpf: failed to alloc priv\n");
+               return -ENOMEM;
+       }
+       pev = &priv->pev;
+
+       pr_debug("bpf: config program '%s'\n", config_str);
+       err = parse_perf_probe_command(config_str, pev);
+       if (err < 0) {
+               pr_debug("bpf: '%s' is not a valid config string\n",
+                        config_str);
+               err = -EINVAL;
+               goto errout;
+       }
+
+       if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
+               pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
+                        config_str, PERF_BPF_PROBE_GROUP);
+               err = -EINVAL;
+               goto errout;
+       } else if (!pev->group)
+               pev->group = strdup(PERF_BPF_PROBE_GROUP);
+
+       if (!pev->group) {
+               pr_debug("bpf: strdup failed\n");
+               err = -ENOMEM;
+               goto errout;
+       }
+
+       if (!pev->event) {
+               pr_debug("bpf: '%s': event name is missing\n",
+                        config_str);
+               err = -EINVAL;
+               goto errout;
+       }
+       pr_debug("bpf: config '%s' is ok\n", config_str);
+
+       err = bpf_program__set_private(prog, priv, bpf_prog_priv__clear);
+       if (err) {
+               pr_debug("Failed to set priv for program '%s'\n", config_str);
+               goto errout;
+       }
+
+       return 0;
+
+errout:
+       if (pev)
+               clear_perf_probe_event(pev);
+       free(priv);
+       return err;
+}
+
+static int bpf__prepare_probe(void)
+{
+       static int err = 0;
+       static bool initialized = false;
+
+       /*
+        * Make err static, so if init failed the first, bpf__prepare_probe()
+        * fails each time without calling init_probe_symbol_maps multiple
+        * times.
+        */
+       if (initialized)
+               return err;
+
+       initialized = true;
+       err = init_probe_symbol_maps(false);
+       if (err < 0)
+               pr_debug("Failed to init_probe_symbol_maps\n");
+       probe_conf.max_probes = MAX_PROBES;
+       return err;
+}
+
+int bpf__probe(struct bpf_object *obj)
+{
+       int err = 0;
+       struct bpf_program *prog;
+       struct bpf_prog_priv *priv;
+       struct perf_probe_event *pev;
+
+       err = bpf__prepare_probe();
+       if (err) {
+               pr_debug("bpf__prepare_probe failed\n");
+               return err;
+       }
+
+       bpf_object__for_each_program(prog, obj) {
+               err = config_bpf_program(prog);
+               if (err)
+                       goto out;
+
+               err = bpf_program__get_private(prog, (void **)&priv);
+               if (err || !priv)
+                       goto out;
+               pev = &priv->pev;
+
+               err = convert_perf_probe_events(pev, 1);
+               if (err < 0) {
+                       pr_debug("bpf_probe: failed to convert perf probe events");
+                       goto out;
+               }
+
+               err = apply_perf_probe_events(pev, 1);
+               if (err < 0) {
+                       pr_debug("bpf_probe: failed to apply perf probe events");
+                       goto out;
+               }
+       }
+out:
+       return err < 0 ? err : 0;
+}
+
+#define EVENTS_WRITE_BUFSIZE  4096
+int bpf__unprobe(struct bpf_object *obj)
+{
+       int err, ret = 0;
+       struct bpf_program *prog;
+       struct bpf_prog_priv *priv;
+
+       bpf_object__for_each_program(prog, obj) {
+               int i;
+
+               err = bpf_program__get_private(prog, (void **)&priv);
+               if (err || !priv)
+                       continue;
+
+               for (i = 0; i < priv->pev.ntevs; i++) {
+                       struct probe_trace_event *tev = &priv->pev.tevs[i];
+                       char name_buf[EVENTS_WRITE_BUFSIZE];
+                       struct strfilter *delfilter;
+
+                       snprintf(name_buf, EVENTS_WRITE_BUFSIZE,
+                                "%s:%s", tev->group, tev->event);
+                       name_buf[EVENTS_WRITE_BUFSIZE - 1] = '\0';
+
+                       delfilter = strfilter__new(name_buf, NULL);
+                       if (!delfilter) {
+                               pr_debug("Failed to create filter for unprobing\n");
+                               ret = -ENOMEM;
+                               continue;
+                       }
+
+                       err = del_perf_probe_events(delfilter);
+                       strfilter__delete(delfilter);
+                       if (err) {
+                               pr_debug("Failed to delete %s\n", name_buf);
+                               ret = err;
+                               continue;
+                       }
+               }
+       }
+       return ret;
+}
+
+int bpf__load(struct bpf_object *obj)
+{
+       int err;
+
+       err = bpf_object__load(obj);
+       if (err) {
+               pr_debug("bpf: load objects failed\n");
+               return err;
+       }
+       return 0;
+}
+
+int bpf__foreach_tev(struct bpf_object *obj,
+                    bpf_prog_iter_callback_t func,
+                    void *arg)
+{
+       struct bpf_program *prog;
+       int err;
+
+       bpf_object__for_each_program(prog, obj) {
+               struct probe_trace_event *tev;
+               struct perf_probe_event *pev;
+               struct bpf_prog_priv *priv;
+               int i, fd;
+
+               err = bpf_program__get_private(prog,
+                               (void **)&priv);
+               if (err || !priv) {
+                       pr_debug("bpf: failed to get private field\n");
+                       return -EINVAL;
+               }
+
+               pev = &priv->pev;
+               for (i = 0; i < pev->ntevs; i++) {
+                       tev = &pev->tevs[i];
+
+                       fd = bpf_program__fd(prog);
+                       if (fd < 0) {
+                               pr_debug("bpf: failed to get file descriptor\n");
+                               return fd;
+                       }
+
+                       err = (*func)(tev, fd, arg);
+                       if (err) {
+                               pr_debug("bpf: call back failed, stop iterate\n");
+                               return err;
+                       }
+               }
+       }
+       return 0;
+}
+
+#define bpf__strerror_head(err, buf, size) \
+       char sbuf[STRERR_BUFSIZE], *emsg;\
+       if (!size)\
+               return 0;\
+       if (err < 0)\
+               err = -err;\
+       emsg = strerror_r(err, sbuf, sizeof(sbuf));\
+       switch (err) {\
+       default:\
+               scnprintf(buf, size, "%s", emsg);\
+               break;
+
+#define bpf__strerror_entry(val, fmt...)\
+       case val: {\
+               scnprintf(buf, size, fmt);\
+               break;\
+       }
+
+#define bpf__strerror_end(buf, size)\
+       }\
+       buf[size - 1] = '\0';
+
+int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
+                       int err, char *buf, size_t size)
+{
+       bpf__strerror_head(err, buf, size);
+       bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'");
+       bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0\n");
+       bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file\n");
+       bpf__strerror_end(buf, size);
+       return 0;
+}
+
+int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
+                      int err, char *buf, size_t size)
+{
+       bpf__strerror_head(err, buf, size);
+       bpf__strerror_entry(EINVAL, "%s: Are you root and runing a CONFIG_BPF_SYSCALL kernel?",
+                           emsg)
+       bpf__strerror_end(buf, size);
+       return 0;
+}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
new file mode 100644 (file)
index 0000000..ccd8d7f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+#ifndef __BPF_LOADER_H
+#define __BPF_LOADER_H
+
+#include <linux/compiler.h>
+#include <linux/err.h>
+#include <string.h>
+#include "probe-event.h"
+#include "debug.h"
+
+struct bpf_object;
+#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
+
+typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev,
+                                       int fd, void *arg);
+
+#ifdef HAVE_LIBBPF_SUPPORT
+struct bpf_object *bpf__prepare_load(const char *filename, bool source);
+
+void bpf__clear(void);
+
+int bpf__probe(struct bpf_object *obj);
+int bpf__unprobe(struct bpf_object *obj);
+int bpf__strerror_probe(struct bpf_object *obj, int err,
+                       char *buf, size_t size);
+
+int bpf__load(struct bpf_object *obj);
+int bpf__strerror_load(struct bpf_object *obj, int err,
+                      char *buf, size_t size);
+int bpf__foreach_tev(struct bpf_object *obj,
+                    bpf_prog_iter_callback_t func, void *arg);
+#else
+static inline struct bpf_object *
+bpf__prepare_load(const char *filename __maybe_unused,
+                 bool source __maybe_unused)
+{
+       pr_debug("ERROR: eBPF object loading is disabled during compiling.\n");
+       return ERR_PTR(-ENOTSUP);
+}
+
+static inline void bpf__clear(void) { }
+
+static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;}
+static inline int bpf__unprobe(struct bpf_object *obj __maybe_unused) { return 0;}
+static inline int bpf__load(struct bpf_object *obj __maybe_unused) { return 0; }
+
+static inline int
+bpf__foreach_tev(struct bpf_object *obj __maybe_unused,
+                bpf_prog_iter_callback_t func __maybe_unused,
+                void *arg __maybe_unused)
+{
+       return 0;
+}
+
+static inline int
+__bpf_strerror(char *buf, size_t size)
+{
+       if (!size)
+               return 0;
+       strncpy(buf,
+               "ERROR: eBPF object loading is disabled during compiling.\n",
+               size);
+       buf[size - 1] = '\0';
+       return 0;
+}
+
+static inline int
+bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
+                   int err __maybe_unused,
+                   char *buf, size_t size)
+{
+       return __bpf_strerror(buf, size);
+}
+
+static inline int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
+                                    int err __maybe_unused,
+                                    char *buf, size_t size)
+{
+       return __bpf_strerror(buf, size);
+}
+#endif
+#endif
index 773fe13ce6271b192bc9482ac8496578a9f41a81..735ad48e1858b0382c9aacc84e0b912e9329c86a 100644 (file)
@@ -51,10 +51,12 @@ static int parse_callchain_order(const char *value)
 {
        if (!strncmp(value, "caller", strlen(value))) {
                callchain_param.order = ORDER_CALLER;
+               callchain_param.order_set = true;
                return 0;
        }
        if (!strncmp(value, "callee", strlen(value))) {
                callchain_param.order = ORDER_CALLEE;
+               callchain_param.order_set = true;
                return 0;
        }
        return -1;
@@ -77,12 +79,14 @@ static int parse_callchain_sort_key(const char *value)
        return -1;
 }
 
-int
-parse_callchain_report_opt(const char *arg)
+static int
+__parse_callchain_report_opt(const char *arg, bool allow_record_opt)
 {
        char *tok;
        char *endptr;
        bool minpcnt_set = false;
+       bool record_opt_set = false;
+       bool try_stack_size = false;
 
        symbol_conf.use_callchain = true;
 
@@ -100,6 +104,28 @@ parse_callchain_report_opt(const char *arg)
                    !parse_callchain_order(tok) ||
                    !parse_callchain_sort_key(tok)) {
                        /* parsing ok - move on to the next */
+                       try_stack_size = false;
+                       goto next;
+               } else if (allow_record_opt && !record_opt_set) {
+                       if (parse_callchain_record(tok, &callchain_param))
+                               goto try_numbers;
+
+                       /* assume that number followed by 'dwarf' is stack size */
+                       if (callchain_param.record_mode == CALLCHAIN_DWARF)
+                               try_stack_size = true;
+
+                       record_opt_set = true;
+                       goto next;
+               }
+
+try_numbers:
+               if (try_stack_size) {
+                       unsigned long size = 0;
+
+                       if (get_stack_size(tok, &size) < 0)
+                               return -1;
+                       callchain_param.dump_size = size;
+                       try_stack_size = false;
                } else if (!minpcnt_set) {
                        /* try to get the min percent */
                        callchain_param.min_percent = strtod(tok, &endptr);
@@ -112,7 +138,7 @@ parse_callchain_report_opt(const char *arg)
                        if (tok == endptr)
                                return -1;
                }
-
+next:
                arg = NULL;
        }
 
@@ -123,6 +149,16 @@ parse_callchain_report_opt(const char *arg)
        return 0;
 }
 
+int parse_callchain_report_opt(const char *arg)
+{
+       return __parse_callchain_report_opt(arg, false);
+}
+
+int parse_callchain_top_opt(const char *arg)
+{
+       return __parse_callchain_report_opt(arg, true);
+}
+
 int perf_callchain_config(const char *var, const char *value)
 {
        char *endptr;
index acee2b3cd801737985fc7ca44443027a03ad4d54..fce8161e54dbbbae16f0667c0cd86cb50d7b58a2 100644 (file)
@@ -7,6 +7,30 @@
 #include "event.h"
 #include "symbol.h"
 
+#define HELP_PAD "\t\t\t\t"
+
+#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace):\n\n"
+
+#ifdef HAVE_DWARF_UNWIND_SUPPORT
+# define RECORD_MODE_HELP  HELP_PAD "record_mode:\tcall graph recording mode (fp|dwarf|lbr)\n"
+#else
+# define RECORD_MODE_HELP  HELP_PAD "record_mode:\tcall graph recording mode (fp|lbr)\n"
+#endif
+
+#define RECORD_SIZE_HELP                                               \
+       HELP_PAD "record_size:\tif record_mode is 'dwarf', max size of stack recording (<bytes>)\n" \
+       HELP_PAD "\t\tdefault: 8192 (bytes)\n"
+
+#define CALLCHAIN_RECORD_HELP  CALLCHAIN_HELP RECORD_MODE_HELP RECORD_SIZE_HELP
+
+#define CALLCHAIN_REPORT_HELP                                          \
+       HELP_PAD "print_type:\tcall graph printing style (graph|flat|fractal|none)\n" \
+       HELP_PAD "threshold:\tminimum call graph inclusion threshold (<percent>)\n" \
+       HELP_PAD "print_limit:\tmaximum number of call graph entry (<number>)\n" \
+       HELP_PAD "order:\t\tcall graph order (caller|callee)\n" \
+       HELP_PAD "sort_key:\tcall graph sort key (function|address)\n"  \
+       HELP_PAD "branch:\t\tinclude last branch info to call graph (branch)\n"
+
 enum perf_call_graph_mode {
        CALLCHAIN_NONE,
        CALLCHAIN_FP,
@@ -63,6 +87,7 @@ struct callchain_param {
        double                  min_percent;
        sort_chain_func_t       sort;
        enum chain_order        order;
+       bool                    order_set;
        enum chain_key          key;
        bool                    branch_callstack;
 };
@@ -180,6 +205,7 @@ extern const char record_callchain_help[];
 extern int parse_callchain_record(const char *arg, struct callchain_param *param);
 int parse_callchain_record_opt(const char *arg, struct callchain_param *param);
 int parse_callchain_report_opt(const char *arg);
+int parse_callchain_top_opt(const char *arg);
 int perf_callchain_config(const char *var, const char *value);
 
 static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
index 3667e2123e5b44414a483c23b239e61b955dd7f7..10af1e7524fbd24de791c38fa23c7d730d54a193 100644 (file)
@@ -203,6 +203,23 @@ struct cpu_map *cpu_map__dummy_new(void)
        return cpus;
 }
 
+struct cpu_map *cpu_map__empty_new(int nr)
+{
+       struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int) * nr);
+
+       if (cpus != NULL) {
+               int i;
+
+               cpus->nr = nr;
+               for (i = 0; i < nr; i++)
+                       cpus->map[i] = -1;
+
+               atomic_set(&cpus->refcnt, 1);
+       }
+
+       return cpus;
+}
+
 static void cpu_map__delete(struct cpu_map *map)
 {
        if (map) {
@@ -225,32 +242,32 @@ void cpu_map__put(struct cpu_map *map)
                cpu_map__delete(map);
 }
 
-int cpu_map__get_socket(struct cpu_map *map, int idx)
+static int cpu__get_topology_int(int cpu, const char *name, int *value)
 {
-       FILE *fp;
-       const char *mnt;
        char path[PATH_MAX];
-       int cpu, ret;
 
-       if (idx > map->nr)
-               return -1;
+       snprintf(path, PATH_MAX,
+               "devices/system/cpu/cpu%d/topology/%s", cpu, name);
 
-       cpu = map->map[idx];
+       return sysfs__read_int(path, value);
+}
 
-       mnt = sysfs__mountpoint();
-       if (!mnt)
-               return -1;
+int cpu_map__get_socket_id(int cpu)
+{
+       int value, ret = cpu__get_topology_int(cpu, "physical_package_id", &value);
+       return ret ?: value;
+}
 
-       snprintf(path, PATH_MAX,
-               "%s/devices/system/cpu/cpu%d/topology/physical_package_id",
-               mnt, cpu);
+int cpu_map__get_socket(struct cpu_map *map, int idx, void *data __maybe_unused)
+{
+       int cpu;
 
-       fp = fopen(path, "r");
-       if (!fp)
+       if (idx > map->nr)
                return -1;
-       ret = fscanf(fp, "%d", &cpu);
-       fclose(fp);
-       return ret == 1 ? cpu : -1;
+
+       cpu = map->map[idx];
+
+       return cpu_map__get_socket_id(cpu);
 }
 
 static int cmp_ids(const void *a, const void *b)
@@ -258,8 +275,9 @@ static int cmp_ids(const void *a, const void *b)
        return *(int *)a - *(int *)b;
 }
 
-static int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
-                             int (*f)(struct cpu_map *map, int cpu))
+int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
+                      int (*f)(struct cpu_map *map, int cpu, void *data),
+                      void *data)
 {
        struct cpu_map *c;
        int nr = cpus->nr;
@@ -271,7 +289,7 @@ static int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
                return -1;
 
        for (cpu = 0; cpu < nr; cpu++) {
-               s1 = f(cpus, cpu);
+               s1 = f(cpus, cpu, data);
                for (s2 = 0; s2 < c->nr; s2++) {
                        if (s1 == c->map[s2])
                                break;
@@ -284,40 +302,29 @@ static int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
        /* ensure we process id in increasing order */
        qsort(c->map, c->nr, sizeof(int), cmp_ids);
 
-       atomic_set(&cpus->refcnt, 1);
+       atomic_set(&c->refcnt, 1);
        *res = c;
        return 0;
 }
 
-int cpu_map__get_core(struct cpu_map *map, int idx)
+int cpu_map__get_core_id(int cpu)
 {
-       FILE *fp;
-       const char *mnt;
-       char path[PATH_MAX];
-       int cpu, ret, s;
+       int value, ret = cpu__get_topology_int(cpu, "core_id", &value);
+       return ret ?: value;
+}
+
+int cpu_map__get_core(struct cpu_map *map, int idx, void *data)
+{
+       int cpu, s;
 
        if (idx > map->nr)
                return -1;
 
        cpu = map->map[idx];
 
-       mnt = sysfs__mountpoint();
-       if (!mnt)
-               return -1;
-
-       snprintf(path, PATH_MAX,
-               "%s/devices/system/cpu/cpu%d/topology/core_id",
-               mnt, cpu);
-
-       fp = fopen(path, "r");
-       if (!fp)
-               return -1;
-       ret = fscanf(fp, "%d", &cpu);
-       fclose(fp);
-       if (ret != 1)
-               return -1;
+       cpu = cpu_map__get_core_id(cpu);
 
-       s = cpu_map__get_socket(map, idx);
+       s = cpu_map__get_socket(map, idx, data);
        if (s == -1)
                return -1;
 
@@ -332,12 +339,12 @@ int cpu_map__get_core(struct cpu_map *map, int idx)
 
 int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp)
 {
-       return cpu_map__build_map(cpus, sockp, cpu_map__get_socket);
+       return cpu_map__build_map(cpus, sockp, cpu_map__get_socket, NULL);
 }
 
 int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep)
 {
-       return cpu_map__build_map(cpus, corep, cpu_map__get_core);
+       return cpu_map__build_map(cpus, corep, cpu_map__get_core, NULL);
 }
 
 /* setup simple routines to easily access node numbers given a cpu number */
index 0af9cecb4c519d44da13d392baa9abca37ebe6ec..85f7772457fa091655d62212067f2edddf6e55ae 100644 (file)
@@ -15,11 +15,14 @@ struct cpu_map {
 };
 
 struct cpu_map *cpu_map__new(const char *cpu_list);
+struct cpu_map *cpu_map__empty_new(int nr);
 struct cpu_map *cpu_map__dummy_new(void);
 struct cpu_map *cpu_map__read(FILE *file);
 size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
-int cpu_map__get_socket(struct cpu_map *map, int idx);
-int cpu_map__get_core(struct cpu_map *map, int idx);
+int cpu_map__get_socket_id(int cpu);
+int cpu_map__get_socket(struct cpu_map *map, int idx, void *data);
+int cpu_map__get_core_id(int cpu);
+int cpu_map__get_core(struct cpu_map *map, int idx, void *data);
 int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp);
 int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep);
 
@@ -85,4 +88,7 @@ static inline int cpu__get_node(int cpu)
        return cpunode_map[cpu];
 }
 
+int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
+                      int (*f)(struct cpu_map *map, int cpu, void *data),
+                      void *data);
 #endif /* __PERF_CPUMAP_H */
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
new file mode 100644 (file)
index 0000000..6af4f7c
--- /dev/null
@@ -0,0 +1,86 @@
+#include "cpumap.h"
+#include "env.h"
+#include "util.h"
+
+struct perf_env perf_env;
+
+void perf_env__exit(struct perf_env *env)
+{
+       zfree(&env->hostname);
+       zfree(&env->os_release);
+       zfree(&env->version);
+       zfree(&env->arch);
+       zfree(&env->cpu_desc);
+       zfree(&env->cpuid);
+       zfree(&env->cmdline);
+       zfree(&env->cmdline_argv);
+       zfree(&env->sibling_cores);
+       zfree(&env->sibling_threads);
+       zfree(&env->numa_nodes);
+       zfree(&env->pmu_mappings);
+       zfree(&env->cpu);
+}
+
+int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
+{
+       int i;
+
+       /*
+        * If env->cmdline_argv has already been set, do not override it.  This allows
+        * a command to set the cmdline, parse args and then call another
+        * builtin function that implements a command -- e.g, cmd_kvm calling
+        * cmd_record.
+        */
+       if (env->cmdline_argv != NULL)
+               return 0;
+
+       /* do not include NULL termination */
+       env->cmdline_argv = calloc(argc, sizeof(char *));
+       if (env->cmdline_argv == NULL)
+               goto out_enomem;
+
+       /*
+        * Must copy argv contents because it gets moved around during option
+        * parsing:
+        */
+       for (i = 0; i < argc ; i++) {
+               env->cmdline_argv[i] = argv[i];
+               if (env->cmdline_argv[i] == NULL)
+                       goto out_free;
+       }
+
+       env->nr_cmdline = argc;
+
+       return 0;
+out_free:
+       zfree(&env->cmdline_argv);
+out_enomem:
+       return -ENOMEM;
+}
+
+int perf_env__read_cpu_topology_map(struct perf_env *env)
+{
+       int cpu, nr_cpus;
+
+       if (env->cpu != NULL)
+               return 0;
+
+       if (env->nr_cpus_avail == 0)
+               env->nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF);
+
+       nr_cpus = env->nr_cpus_avail;
+       if (nr_cpus == -1)
+               return -EINVAL;
+
+       env->cpu = calloc(nr_cpus, sizeof(env->cpu[0]));
+       if (env->cpu == NULL)
+               return -ENOMEM;
+
+       for (cpu = 0; cpu < nr_cpus; ++cpu) {
+               env->cpu[cpu].core_id   = cpu_map__get_core_id(cpu);
+               env->cpu[cpu].socket_id = cpu_map__get_socket_id(cpu);
+       }
+
+       env->nr_cpus_avail = nr_cpus;
+       return 0;
+}
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
new file mode 100644 (file)
index 0000000..0132b95
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef __PERF_ENV_H
+#define __PERF_ENV_H
+
+struct cpu_topology_map {
+       int     socket_id;
+       int     core_id;
+};
+
+struct perf_env {
+       char                    *hostname;
+       char                    *os_release;
+       char                    *version;
+       char                    *arch;
+       int                     nr_cpus_online;
+       int                     nr_cpus_avail;
+       char                    *cpu_desc;
+       char                    *cpuid;
+       unsigned long long      total_mem;
+       unsigned int            msr_pmu_type;
+
+       int                     nr_cmdline;
+       int                     nr_sibling_cores;
+       int                     nr_sibling_threads;
+       int                     nr_numa_nodes;
+       int                     nr_pmu_mappings;
+       int                     nr_groups;
+       char                    *cmdline;
+       const char              **cmdline_argv;
+       char                    *sibling_cores;
+       char                    *sibling_threads;
+       char                    *numa_nodes;
+       char                    *pmu_mappings;
+       struct cpu_topology_map *cpu;
+};
+
+extern struct perf_env perf_env;
+
+void perf_env__exit(struct perf_env *env);
+
+int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]);
+
+int perf_env__read_cpu_topology_map(struct perf_env *env);
+
+#endif /* __PERF_ENV_H */
index 7ff61274ed57f5219416c69767fac9acca21e091..8b10621b415c684564a6dd5af130ebc9d3ab33c7 100644 (file)
@@ -67,7 +67,8 @@ static int perf_event__get_comm_ids(pid_t pid, char *comm, size_t len,
        char filename[PATH_MAX];
        char bf[4096];
        int fd;
-       size_t size = 0, n;
+       size_t size = 0;
+       ssize_t n;
        char *nl, *name, *tgids, *ppids;
 
        *tgid = -1;
@@ -167,7 +168,7 @@ static int perf_event__prepare_comm(union perf_event *event, pid_t pid,
        return 0;
 }
 
-static pid_t perf_event__synthesize_comm(struct perf_tool *tool,
+pid_t perf_event__synthesize_comm(struct perf_tool *tool,
                                         union perf_event *event, pid_t pid,
                                         perf_event__handler_t process,
                                         struct machine *machine)
@@ -378,7 +379,7 @@ int perf_event__synthesize_modules(struct perf_tool *tool,
        for (pos = maps__first(maps); pos; pos = map__next(pos)) {
                size_t size;
 
-               if (pos->dso->kernel)
+               if (__map__is_kernel(pos))
                        continue;
 
                size = PERF_ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
@@ -649,12 +650,12 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
        size_t size;
        const char *mmap_name;
        char name_buff[PATH_MAX];
-       struct map *map;
+       struct map *map = machine__kernel_map(machine);
        struct kmap *kmap;
        int err;
        union perf_event *event;
 
-       if (machine->vmlinux_maps[0] == NULL)
+       if (map == NULL)
                return -1;
 
        /*
@@ -680,7 +681,6 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
                event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
        }
 
-       map = machine->vmlinux_maps[MAP__FUNCTION];
        kmap = map__kmap(map);
        size = snprintf(event->mmap.filename, sizeof(event->mmap.filename),
                        "%s%s", mmap_name, kmap->ref_reloc_sym->name) + 1;
@@ -1008,7 +1008,7 @@ int perf_event__preprocess_sample(const union perf_event *event,
         * it now.
         */
        if (cpumode == PERF_RECORD_MISC_KERNEL &&
-           machine->vmlinux_maps[MAP__FUNCTION] == NULL)
+           machine__kernel_map(machine) == NULL)
                machine__create_kernel_maps(machine);
 
        thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, al);
@@ -1021,6 +1021,14 @@ int perf_event__preprocess_sample(const union perf_event *event,
 
        al->sym = NULL;
        al->cpu = sample->cpu;
+       al->socket = -1;
+
+       if (al->cpu >= 0) {
+               struct perf_env *env = machine->env;
+
+               if (env && env->cpu)
+                       al->socket = env->cpu[al->cpu].socket_id;
+       }
 
        if (al->map) {
                struct dso *dso = al->map->dso;
index f729df5e25e634607f6b4c2f2cb4cc4e1ec933a3..a0dbcbd4f6d82d6564d56e48c0672ea56cba45bc 100644 (file)
@@ -257,6 +257,7 @@ struct events_stats {
        u64 total_non_filtered_period;
        u64 total_lost;
        u64 total_lost_samples;
+       u64 total_aux_lost;
        u64 total_invalid_chains;
        u32 nr_events[PERF_RECORD_HEADER_MAX];
        u32 nr_non_filtered_samples;
@@ -478,6 +479,11 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
                                  const struct perf_sample *sample,
                                  bool swapped);
 
+pid_t perf_event__synthesize_comm(struct perf_tool *tool,
+                                 union perf_event *event, pid_t pid,
+                                 perf_event__handler_t process,
+                                 struct machine *machine);
+
 int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                                       union perf_event *event,
                                       pid_t pid, pid_t tgid,
index c8fc8a258f4265c42c636d045b195a612cfae3a4..d1392194a9a951bd3e1dd5c9b72fd383ede98439 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/bitops.h>
 #include <linux/hash.h>
 #include <linux/log2.h>
+#include <linux/err.h>
 
 static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx);
 static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx);
@@ -164,6 +165,13 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
        __perf_evlist__propagate_maps(evlist, entry);
 }
 
+void perf_evlist__remove(struct perf_evlist *evlist, struct perf_evsel *evsel)
+{
+       evsel->evlist = NULL;
+       list_del_init(&evsel->node);
+       evlist->nr_entries -= 1;
+}
+
 void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
                                   struct list_head *list)
 {
@@ -197,6 +205,20 @@ void perf_evlist__set_leader(struct perf_evlist *evlist)
        }
 }
 
+void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr)
+{
+       attr->precise_ip = 3;
+
+       while (attr->precise_ip != 0) {
+               int fd = sys_perf_event_open(attr, 0, -1, -1, 0);
+               if (fd != -1) {
+                       close(fd);
+                       break;
+               }
+               --attr->precise_ip;
+       }
+}
+
 int perf_evlist__add_default(struct perf_evlist *evlist)
 {
        struct perf_event_attr attr = {
@@ -207,13 +229,15 @@ int perf_evlist__add_default(struct perf_evlist *evlist)
 
        event_attr_init(&attr);
 
+       perf_event_attr__set_max_precise_ip(&attr);
+
        evsel = perf_evsel__new(&attr);
        if (evsel == NULL)
                goto error;
 
-       /* use strdup() because free(evsel) assumes name is allocated */
-       evsel->name = strdup("cycles");
-       if (!evsel->name)
+       /* use asprintf() because free(evsel) assumes name is allocated */
+       if (asprintf(&evsel->name, "cycles%.*s",
+                    attr.precise_ip ? attr.precise_ip + 1 : 0, ":ppp") < 0)
                goto error_free;
 
        perf_evlist__add(evlist, evsel);
@@ -293,7 +317,7 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist,
 {
        struct perf_evsel *evsel = perf_evsel__newtp(sys, name);
 
-       if (evsel == NULL)
+       if (IS_ERR(evsel))
                return -1;
 
        evsel->handler = handler;
@@ -616,6 +640,21 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
        return NULL;
 }
 
+struct perf_evsel *perf_evlist__id2evsel_strict(struct perf_evlist *evlist,
+                                               u64 id)
+{
+       struct perf_sample_id *sid;
+
+       if (!id)
+               return NULL;
+
+       sid = perf_evlist__id2sid(evlist, id);
+       if (sid)
+               return sid->evsel;
+
+       return NULL;
+}
+
 static int perf_evlist__event2id(struct perf_evlist *evlist,
                                 union perf_event *event, u64 *id)
 {
index 115d8b53c6010a5a1d466b04535245d9fd8f8579..a459fe71b452e0b721d798cc73cc6287ae87dc1c 100644 (file)
@@ -73,6 +73,7 @@ void perf_evlist__exit(struct perf_evlist *evlist);
 void perf_evlist__delete(struct perf_evlist *evlist);
 
 void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry);
+void perf_evlist__remove(struct perf_evlist *evlist, struct perf_evsel *evsel);
 int perf_evlist__add_default(struct perf_evlist *evlist);
 int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,
                                     struct perf_event_attr *attrs, size_t nr_attrs);
@@ -104,6 +105,8 @@ int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mas
 int perf_evlist__poll(struct perf_evlist *evlist, int timeout);
 
 struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);
+struct perf_evsel *perf_evlist__id2evsel_strict(struct perf_evlist *evlist,
+                                               u64 id);
 
 struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id);
 
@@ -287,4 +290,6 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
 
 void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
                                     struct perf_evsel *tracking_evsel);
+
+void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr);
 #endif /* __PERF_EVLIST_H */
index 5410483d52198c5909ec5a502c61567c5119c8ef..397fb4ed3c97b6deffeffd8f69bbceb886ac58ea 100644 (file)
@@ -9,10 +9,11 @@
 
 #include <byteswap.h>
 #include <linux/bitops.h>
-#include <api/fs/debugfs.h>
+#include <api/fs/tracing_path.h>
 #include <traceevent/event-parse.h>
 #include <linux/hw_breakpoint.h>
 #include <linux/perf_event.h>
+#include <linux/err.h>
 #include <sys/resource.h>
 #include "asm/bug.h"
 #include "callchain.h"
@@ -207,6 +208,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
        evsel->unit        = "";
        evsel->scale       = 1.0;
        evsel->evlist      = NULL;
+       evsel->bpf_fd      = -1;
        INIT_LIST_HEAD(&evsel->node);
        INIT_LIST_HEAD(&evsel->config_terms);
        perf_evsel__object.init(evsel);
@@ -225,11 +227,17 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
        return evsel;
 }
 
+/*
+ * Returns pointer with encoded error via <linux/err.h> interface.
+ */
 struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx)
 {
        struct perf_evsel *evsel = zalloc(perf_evsel__object.size);
+       int err = -ENOMEM;
 
-       if (evsel != NULL) {
+       if (evsel == NULL) {
+               goto out_err;
+       } else {
                struct perf_event_attr attr = {
                        .type          = PERF_TYPE_TRACEPOINT,
                        .sample_type   = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
@@ -240,8 +248,10 @@ struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int
                        goto out_free;
 
                evsel->tp_format = trace_event__tp_format(sys, name);
-               if (evsel->tp_format == NULL)
+               if (IS_ERR(evsel->tp_format)) {
+                       err = PTR_ERR(evsel->tp_format);
                        goto out_free;
+               }
 
                event_attr_init(&attr);
                attr.config = evsel->tp_format->id;
@@ -254,7 +264,8 @@ struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int
 out_free:
        zfree(&evsel->name);
        free(evsel);
-       return NULL;
+out_err:
+       return ERR_PTR(err);
 }
 
 const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
@@ -642,6 +653,15 @@ static void apply_config_terms(struct perf_evsel *evsel,
                case PERF_EVSEL__CONFIG_TERM_STACK_USER:
                        dump_size = term->val.stack_user;
                        break;
+               case PERF_EVSEL__CONFIG_TERM_INHERIT:
+                       /*
+                        * attr->inherit should has already been set by
+                        * perf_evsel__config. If user explicitly set
+                        * inherit using config terms, override global
+                        * opt->no_inherit setting.
+                        */
+                       attr->inherit = term->val.inherit ? 1 : 0;
+                       break;
                default:
                        break;
                }
@@ -872,6 +892,9 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
                attr->clockid = opts->clockid;
        }
 
+       if (evsel->precise_max)
+               perf_event_attr__set_max_precise_ip(attr);
+
        /*
         * Apply event specific term settings,
         * it overloads any global configuration.
@@ -1168,7 +1191,7 @@ static void __p_sample_type(char *buf, size_t size, u64 value)
                bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU),
                bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW),
                bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER),
-               bit_name(IDENTIFIER), bit_name(REGS_INTR),
+               bit_name(IDENTIFIER), bit_name(REGS_INTR), bit_name(DATA_SRC),
                { .name = NULL, }
        };
 #undef bit_name
@@ -1249,6 +1272,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
        PRINT_ATTRf(bp_type, p_unsigned);
        PRINT_ATTRn("{ bp_addr, config1 }", bp_addr, p_hex);
        PRINT_ATTRn("{ bp_len, config2 }", bp_len, p_hex);
+       PRINT_ATTRf(branch_sample_type, p_unsigned);
        PRINT_ATTRf(sample_regs_user, p_hex);
        PRINT_ATTRf(sample_stack_user, p_unsigned);
        PRINT_ATTRf(clockid, p_signed);
@@ -1333,6 +1357,22 @@ retry_open:
                                          err);
                                goto try_fallback;
                        }
+
+                       if (evsel->bpf_fd >= 0) {
+                               int evt_fd = FD(evsel, cpu, thread);
+                               int bpf_fd = evsel->bpf_fd;
+
+                               err = ioctl(evt_fd,
+                                           PERF_EVENT_IOC_SET_BPF,
+                                           bpf_fd);
+                               if (err && errno != EEXIST) {
+                                       pr_err("failed to attach bpf fd %d: %s\n",
+                                              bpf_fd, strerror(errno));
+                                       err = -EINVAL;
+                                       goto out_close;
+                               }
+                       }
+
                        set_rlimit = NO_CHANGE;
 
                        /*
index ef8925f7211a4a311c927e6919d83817745eb1cf..0e49bd742c639c02d1aef18f421c0204faa95823 100644 (file)
@@ -43,6 +43,7 @@ enum {
        PERF_EVSEL__CONFIG_TERM_TIME,
        PERF_EVSEL__CONFIG_TERM_CALLGRAPH,
        PERF_EVSEL__CONFIG_TERM_STACK_USER,
+       PERF_EVSEL__CONFIG_TERM_INHERIT,
        PERF_EVSEL__CONFIG_TERM_MAX,
 };
 
@@ -55,6 +56,7 @@ struct perf_evsel_config_term {
                bool    time;
                char    *callgraph;
                u64     stack_user;
+               bool    inherit;
        } val;
 };
 
@@ -90,9 +92,9 @@ struct perf_evsel {
        double                  scale;
        const char              *unit;
        struct event_format     *tp_format;
+       off_t                   id_offset;
        union {
                void            *priv;
-               off_t           id_offset;
                u64             db_id;
        };
        struct cgroup_sel       *cgrp;
@@ -111,6 +113,7 @@ struct perf_evsel {
        bool                    system_wide;
        bool                    tracking;
        bool                    per_pkg;
+       bool                    precise_max;
        /* parse modifier helper */
        int                     exclude_GH;
        int                     nr_members;
@@ -120,6 +123,7 @@ struct perf_evsel {
        char                    *group_name;
        bool                    cmdline_group_boundary;
        struct list_head        config_terms;
+       int                     bpf_fd;
 };
 
 union u64_swap {
@@ -130,7 +134,6 @@ union u64_swap {
 struct cpu_map;
 struct target;
 struct thread_map;
-struct perf_evlist;
 struct record_opts;
 
 static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
@@ -162,6 +165,9 @@ static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr)
 
 struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx);
 
+/*
+ * Returns pointer with encoded error via <linux/err.h> interface.
+ */
 static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name)
 {
        return perf_evsel__newtp_idx(sys, name, 0);
index fce6634aebe25d19d126120ec1e6e4dd323be46a..43838003c1a160ff56a0ae72a70dcef68631fe2f 100644 (file)
@@ -24,9 +24,6 @@
 #include "build-id.h"
 #include "data.h"
 
-static u32 header_argc;
-static const char **header_argv;
-
 /*
  * magic2 = "PERFILE2"
  * must be a numerical value to let the endianness
@@ -88,6 +85,9 @@ int write_padded(int fd, const void *bf, size_t count, size_t count_aligned)
        return err;
 }
 
+#define string_size(str)                                               \
+       (PERF_ALIGN((strlen(str) + 1), NAME_ALIGN) + sizeof(u32))
+
 static int do_write_string(int fd, const char *str)
 {
        u32 len, olen;
@@ -135,37 +135,6 @@ static char *do_read_string(int fd, struct perf_header *ph)
        return NULL;
 }
 
-int
-perf_header__set_cmdline(int argc, const char **argv)
-{
-       int i;
-
-       /*
-        * If header_argv has already been set, do not override it.
-        * This allows a command to set the cmdline, parse args and
-        * then call another builtin function that implements a
-        * command -- e.g, cmd_kvm calling cmd_record.
-        */
-       if (header_argv)
-               return 0;
-
-       header_argc = (u32)argc;
-
-       /* do not include NULL termination */
-       header_argv = calloc(argc, sizeof(char *));
-       if (!header_argv)
-               return -ENOMEM;
-
-       /*
-        * must copy argv contents because it gets moved
-        * around during option parsing
-        */
-       for (i = 0; i < argc ; i++)
-               header_argv[i] = argv[i];
-
-       return 0;
-}
-
 static int write_tracing_data(int fd, struct perf_header *h __maybe_unused,
                            struct perf_evlist *evlist)
 {
@@ -402,8 +371,8 @@ static int write_cmdline(int fd, struct perf_header *h __maybe_unused,
 {
        char buf[MAXPATHLEN];
        char proc[32];
-       u32 i, n;
-       int ret;
+       u32 n;
+       int i, ret;
 
        /*
         * actual atual path to perf binary
@@ -417,7 +386,7 @@ static int write_cmdline(int fd, struct perf_header *h __maybe_unused,
        buf[ret] = '\0';
 
        /* account for binary path */
-       n = header_argc + 1;
+       n = perf_env.nr_cmdline + 1;
 
        ret = do_write(fd, &n, sizeof(n));
        if (ret < 0)
@@ -427,8 +396,8 @@ static int write_cmdline(int fd, struct perf_header *h __maybe_unused,
        if (ret < 0)
                return ret;
 
-       for (i = 0 ; i < header_argc; i++) {
-               ret = do_write_string(fd, header_argv[i]);
+       for (i = 0 ; i < perf_env.nr_cmdline; i++) {
+               ret = do_write_string(fd, perf_env.cmdline_argv[i]);
                if (ret < 0)
                        return ret;
        }
@@ -441,6 +410,7 @@ static int write_cmdline(int fd, struct perf_header *h __maybe_unused,
        "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list"
 
 struct cpu_topo {
+       u32 cpu_nr;
        u32 core_sib;
        u32 thread_sib;
        char **core_siblings;
@@ -551,7 +521,7 @@ static struct cpu_topo *build_cpu_topology(void)
                return NULL;
 
        tp = addr;
-
+       tp->cpu_nr = nr;
        addr += sizeof(*tp);
        tp->core_siblings = addr;
        addr += sz;
@@ -574,7 +544,7 @@ static int write_cpu_topology(int fd, struct perf_header *h __maybe_unused,
 {
        struct cpu_topo *tp;
        u32 i;
-       int ret;
+       int ret, j;
 
        tp = build_cpu_topology();
        if (!tp)
@@ -598,6 +568,21 @@ static int write_cpu_topology(int fd, struct perf_header *h __maybe_unused,
                if (ret < 0)
                        break;
        }
+
+       ret = perf_env__read_cpu_topology_map(&perf_env);
+       if (ret < 0)
+               goto done;
+
+       for (j = 0; j < perf_env.nr_cpus_avail; j++) {
+               ret = do_write(fd, &perf_env.cpu[j].core_id,
+                              sizeof(perf_env.cpu[j].core_id));
+               if (ret < 0)
+                       return ret;
+               ret = do_write(fd, &perf_env.cpu[j].socket_id,
+                              sizeof(perf_env.cpu[j].socket_id));
+               if (ret < 0)
+                       return ret;
+       }
 done:
        free_cpu_topo(tp);
        return ret;
@@ -938,6 +923,7 @@ static void print_cpu_topology(struct perf_header *ph, int fd __maybe_unused,
 {
        int nr, i;
        char *str;
+       int cpu_nr = ph->env.nr_cpus_online;
 
        nr = ph->env.nr_sibling_cores;
        str = ph->env.sibling_cores;
@@ -954,6 +940,13 @@ static void print_cpu_topology(struct perf_header *ph, int fd __maybe_unused,
                fprintf(fp, "# sibling threads : %s\n", str);
                str += strlen(str) + 1;
        }
+
+       if (ph->env.cpu != NULL) {
+               for (i = 0; i < cpu_nr; i++)
+                       fprintf(fp, "# CPU %d: Core ID %d, Socket ID %d\n", i,
+                               ph->env.cpu[i].core_id, ph->env.cpu[i].socket_id);
+       } else
+               fprintf(fp, "# Core ID and Socket ID information is not available\n");
 }
 
 static void free_event_desc(struct perf_evsel *events)
@@ -1582,7 +1575,7 @@ error:
        return -1;
 }
 
-static int process_cpu_topology(struct perf_file_section *section __maybe_unused,
+static int process_cpu_topology(struct perf_file_section *section,
                                struct perf_header *ph, int fd,
                                void *data __maybe_unused)
 {
@@ -1590,15 +1583,22 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused
        u32 nr, i;
        char *str;
        struct strbuf sb;
+       int cpu_nr = ph->env.nr_cpus_online;
+       u64 size = 0;
+
+       ph->env.cpu = calloc(cpu_nr, sizeof(*ph->env.cpu));
+       if (!ph->env.cpu)
+               return -1;
 
        ret = readn(fd, &nr, sizeof(nr));
        if (ret != sizeof(nr))
-               return -1;
+               goto free_cpu;
 
        if (ph->needs_swap)
                nr = bswap_32(nr);
 
        ph->env.nr_sibling_cores = nr;
+       size += sizeof(u32);
        strbuf_init(&sb, 128);
 
        for (i = 0; i < nr; i++) {
@@ -1608,6 +1608,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused
 
                /* include a NULL character at the end */
                strbuf_add(&sb, str, strlen(str) + 1);
+               size += string_size(str);
                free(str);
        }
        ph->env.sibling_cores = strbuf_detach(&sb, NULL);
@@ -1620,6 +1621,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused
                nr = bswap_32(nr);
 
        ph->env.nr_sibling_threads = nr;
+       size += sizeof(u32);
 
        for (i = 0; i < nr; i++) {
                str = do_read_string(fd, ph);
@@ -1628,13 +1630,57 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused
 
                /* include a NULL character at the end */
                strbuf_add(&sb, str, strlen(str) + 1);
+               size += string_size(str);
                free(str);
        }
        ph->env.sibling_threads = strbuf_detach(&sb, NULL);
+
+       /*
+        * The header may be from old perf,
+        * which doesn't include core id and socket id information.
+        */
+       if (section->size <= size) {
+               zfree(&ph->env.cpu);
+               return 0;
+       }
+
+       for (i = 0; i < (u32)cpu_nr; i++) {
+               ret = readn(fd, &nr, sizeof(nr));
+               if (ret != sizeof(nr))
+                       goto free_cpu;
+
+               if (ph->needs_swap)
+                       nr = bswap_32(nr);
+
+               if (nr > (u32)cpu_nr) {
+                       pr_debug("core_id number is too big."
+                                "You may need to upgrade the perf tool.\n");
+                       goto free_cpu;
+               }
+               ph->env.cpu[i].core_id = nr;
+
+               ret = readn(fd, &nr, sizeof(nr));
+               if (ret != sizeof(nr))
+                       goto free_cpu;
+
+               if (ph->needs_swap)
+                       nr = bswap_32(nr);
+
+               if (nr > (u32)cpu_nr) {
+                       pr_debug("socket_id number is too big."
+                                "You may need to upgrade the perf tool.\n");
+                       goto free_cpu;
+               }
+
+               ph->env.cpu[i].socket_id = nr;
+       }
+
        return 0;
 
 error:
        strbuf_release(&sb);
+free_cpu:
+       zfree(&ph->env.cpu);
        return -1;
 }
 
@@ -1737,6 +1783,9 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused
                /* include a NULL character at the end */
                strbuf_add(&sb, "", 1);
 
+               if (!strcmp(name, "msr"))
+                       ph->env.msr_pmu_type = type;
+
                free(name);
                pmu_num--;
        }
@@ -2515,6 +2564,7 @@ int perf_session__read_header(struct perf_session *session)
                return -ENOMEM;
 
        session->evlist->env = &header->env;
+       session->machines.host.env = &header->env;
        if (perf_data_file__is_pipe(file))
                return perf_header__read_pipe(session);
 
index 396e4965f0c97469056324e55183883a58b151b5..05f27cb6b7e36a2e663f7c18fb22513bc5280af2 100644 (file)
@@ -7,7 +7,7 @@
 #include <linux/bitmap.h>
 #include <linux/types.h>
 #include "event.h"
-
+#include "env.h"
 
 enum {
        HEADER_RESERVED         = 0,    /* always cleared */
@@ -66,31 +66,6 @@ struct perf_header;
 int perf_file_header__read(struct perf_file_header *header,
                           struct perf_header *ph, int fd);
 
-struct perf_env {
-       char                    *hostname;
-       char                    *os_release;
-       char                    *version;
-       char                    *arch;
-       int                     nr_cpus_online;
-       int                     nr_cpus_avail;
-       char                    *cpu_desc;
-       char                    *cpuid;
-       unsigned long long      total_mem;
-
-       int                     nr_cmdline;
-       int                     nr_sibling_cores;
-       int                     nr_sibling_threads;
-       int                     nr_numa_nodes;
-       int                     nr_pmu_mappings;
-       int                     nr_groups;
-       char                    *cmdline;
-       const char              **cmdline_argv;
-       char                    *sibling_cores;
-       char                    *sibling_threads;
-       char                    *numa_nodes;
-       char                    *pmu_mappings;
-};
-
 struct perf_header {
        enum perf_header_version        version;
        bool                            needs_swap;
index 08b6cd945f1ece736249318835c044a735c6e29c..4fd37d6708cb973d2653c1ec4f6f17b086c712d0 100644 (file)
@@ -15,6 +15,8 @@ static bool hists__filter_entry_by_thread(struct hists *hists,
                                          struct hist_entry *he);
 static bool hists__filter_entry_by_symbol(struct hists *hists,
                                          struct hist_entry *he);
+static bool hists__filter_entry_by_socket(struct hists *hists,
+                                         struct hist_entry *he);
 
 u16 hists__col_len(struct hists *hists, enum hist_column col)
 {
@@ -130,6 +132,18 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
                        hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
                                           symlen);
                }
+
+               if (h->mem_info->iaddr.sym) {
+                       symlen = (int)h->mem_info->iaddr.sym->namelen + 4
+                              + unresolved_col_width + 2;
+                       hists__new_col_len(hists, HISTC_MEM_IADDR_SYMBOL,
+                                          symlen);
+               } else {
+                       symlen = unresolved_col_width + 4 + 2;
+                       hists__new_col_len(hists, HISTC_MEM_IADDR_SYMBOL,
+                                          symlen);
+               }
+
                if (h->mem_info->daddr.map) {
                        symlen = dso__name_len(h->mem_info->daddr.map->dso);
                        hists__new_col_len(hists, HISTC_MEM_DADDR_DSO,
@@ -141,9 +155,12 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
        } else {
                symlen = unresolved_col_width + 4 + 2;
                hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, symlen);
+               hists__new_col_len(hists, HISTC_MEM_IADDR_SYMBOL, symlen);
                hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO);
        }
 
+       hists__new_col_len(hists, HISTC_CPU, 3);
+       hists__new_col_len(hists, HISTC_SOCKET, 6);
        hists__new_col_len(hists, HISTC_MEM_LOCKED, 6);
        hists__new_col_len(hists, HISTC_MEM_TLB, 22);
        hists__new_col_len(hists, HISTC_MEM_SNOOP, 12);
@@ -452,6 +469,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
                        .map    = al->map,
                        .sym    = al->sym,
                },
+               .socket  = al->socket,
                .cpu     = al->cpu,
                .cpumode = al->cpumode,
                .ip      = al->addr,
@@ -690,7 +708,7 @@ iter_finish_normal_entry(struct hist_entry_iter *iter,
 }
 
 static int
-iter_prepare_cumulative_entry(struct hist_entry_iter *iter __maybe_unused,
+iter_prepare_cumulative_entry(struct hist_entry_iter *iter,
                              struct addr_location *al __maybe_unused)
 {
        struct hist_entry **he_cache;
@@ -702,7 +720,7 @@ iter_prepare_cumulative_entry(struct hist_entry_iter *iter __maybe_unused,
         * cumulated only one time to prevent entries more than 100%
         * overhead.
         */
-       he_cache = malloc(sizeof(*he_cache) * (PERF_MAX_STACK_DEPTH + 1));
+       he_cache = malloc(sizeof(*he_cache) * (iter->max_stack + 1));
        if (he_cache == NULL)
                return -ENOMEM;
 
@@ -863,6 +881,8 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
        if (err)
                return err;
 
+       iter->max_stack = max_stack_depth;
+
        err = iter->ops->prepare_entry(iter, al);
        if (err)
                goto out;
@@ -1024,6 +1044,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
        hists__filter_entry_by_dso(hists, he);
        hists__filter_entry_by_thread(hists, he);
        hists__filter_entry_by_symbol(hists, he);
+       hists__filter_entry_by_socket(hists, he);
 }
 
 void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
@@ -1143,7 +1164,7 @@ void hists__output_resort(struct hists *hists, struct ui_progress *prog)
        struct perf_evsel *evsel = hists_to_evsel(hists);
        bool use_callchain;
 
-       if (evsel && !symbol_conf.show_ref_callgraph)
+       if (evsel && symbol_conf.use_callchain && !symbol_conf.show_ref_callgraph)
                use_callchain = evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN;
        else
                use_callchain = symbol_conf.use_callchain;
@@ -1292,6 +1313,37 @@ void hists__filter_by_symbol(struct hists *hists)
        }
 }
 
+static bool hists__filter_entry_by_socket(struct hists *hists,
+                                         struct hist_entry *he)
+{
+       if ((hists->socket_filter > -1) &&
+           (he->socket != hists->socket_filter)) {
+               he->filtered |= (1 << HIST_FILTER__SOCKET);
+               return true;
+       }
+
+       return false;
+}
+
+void hists__filter_by_socket(struct hists *hists)
+{
+       struct rb_node *nd;
+
+       hists->stats.nr_non_filtered_samples = 0;
+
+       hists__reset_filter_stats(hists);
+       hists__reset_col_len(hists);
+
+       for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
+               struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+               if (hists__filter_entry_by_socket(hists, h))
+                       continue;
+
+               hists__remove_entry_filter(hists, h, HIST_FILTER__SOCKET);
+       }
+}
+
 void events_stats__inc(struct events_stats *stats, u32 type)
 {
        ++stats->nr_events[0];
@@ -1517,6 +1569,7 @@ static int hists_evsel__init(struct perf_evsel *evsel)
        hists->entries_collapsed = RB_ROOT;
        hists->entries = RB_ROOT;
        pthread_mutex_init(&hists->lock, NULL);
+       hists->socket_filter = -1;
        return 0;
 }
 
index de6d58e7f0d561db9e204939453a5ca8b6a9cc11..a48a2078d288f01b9edf10814571509803b674d1 100644 (file)
@@ -20,6 +20,7 @@ enum hist_filter {
        HIST_FILTER__SYMBOL,
        HIST_FILTER__GUEST,
        HIST_FILTER__HOST,
+       HIST_FILTER__SOCKET,
 };
 
 enum hist_column {
@@ -29,6 +30,7 @@ enum hist_column {
        HISTC_COMM,
        HISTC_PARENT,
        HISTC_CPU,
+       HISTC_SOCKET,
        HISTC_SRCLINE,
        HISTC_SRCFILE,
        HISTC_MISPREDICT,
@@ -47,6 +49,7 @@ enum hist_column {
        HISTC_MEM_LVL,
        HISTC_MEM_SNOOP,
        HISTC_MEM_DCACHELINE,
+       HISTC_MEM_IADDR_SYMBOL,
        HISTC_TRANSACTION,
        HISTC_CYCLES,
        HISTC_NR_COLS, /* Last entry */
@@ -70,6 +73,7 @@ struct hists {
        struct events_stats     stats;
        u64                     event_stream;
        u16                     col_len[HISTC_NR_COLS];
+       int                     socket_filter;
 };
 
 struct hist_entry_iter;
@@ -87,6 +91,7 @@ struct hist_entry_iter {
        int curr;
 
        bool hide_unresolved;
+       int max_stack;
 
        struct perf_evsel *evsel;
        struct perf_sample *sample;
@@ -144,11 +149,12 @@ size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp);
 void hists__filter_by_dso(struct hists *hists);
 void hists__filter_by_thread(struct hists *hists);
 void hists__filter_by_symbol(struct hists *hists);
+void hists__filter_by_socket(struct hists *hists);
 
 static inline bool hists__has_filter(struct hists *hists)
 {
        return hists->thread_filter || hists->dso_filter ||
-               hists->symbol_filter_str;
+               hists->symbol_filter_str || (hists->socket_filter > -1);
 }
 
 u16 hists__col_len(struct hists *hists, enum hist_column col);
index 8f149655f497302f39b5352cf099fbd71d4d02d7..07c644ed64c4ecd9809bf322297e5a3876e83aad 100644 (file)
@@ -5,4 +5,12 @@
 const char *get_arch_regstr(unsigned int n);
 #endif
 
+#ifdef HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
+/*
+ * Arch should support fetching the offset of a register in pt_regs
+ * by its name. See kernel's regs_query_register_offset in
+ * arch/xxx/kernel/ptrace.c.
+ */
+int regs_query_register_offset(const char *name);
+#endif
 #endif
index 2386322ece4fc8af0851fd2768cc20d1bf454fd4..0611d619a42e645baa4dfbbecca099b38ee23f21 100644 (file)
@@ -7,6 +7,17 @@ $(OUTPUT)util/intel-pt-decoder/inat-tables.c: $(inat_tables_script) $(inat_table
        $(call rule_mkdir)
        @$(call echo-cmd,gen)$(AWK) -f $(inat_tables_script) $(inat_tables_maps) > $@ || rm -f $@
 
-$(OUTPUT)util/intel-pt-decoder/intel-pt-insn-decoder.o: util/intel-pt-decoder/inat.c $(OUTPUT)util/intel-pt-decoder/inat-tables.c
+$(OUTPUT)util/intel-pt-decoder/intel-pt-insn-decoder.o: util/intel-pt-decoder/intel-pt-insn-decoder.c util/intel-pt-decoder/inat.c $(OUTPUT)util/intel-pt-decoder/inat-tables.c
+       @(test -d ../../kernel -a -d ../../tools -a -d ../perf && (( \
+       diff -B -I'^#include' util/intel-pt-decoder/insn.c ../../arch/x86/lib/insn.c >/dev/null && \
+       diff -B -I'^#include' util/intel-pt-decoder/inat.c ../../arch/x86/lib/inat.c >/dev/null && \
+       diff -B util/intel-pt-decoder/x86-opcode-map.txt ../../arch/x86/lib/x86-opcode-map.txt >/dev/null && \
+       diff -B util/intel-pt-decoder/gen-insn-attr-x86.awk ../../arch/x86/tools/gen-insn-attr-x86.awk >/dev/null && \
+       diff -B -I'^#include' util/intel-pt-decoder/insn.h ../../arch/x86/include/asm/insn.h >/dev/null && \
+       diff -B -I'^#include' util/intel-pt-decoder/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \
+       diff -B -I'^#include' util/intel-pt-decoder/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \
+       || echo "Warning: Intel PT: x86 instruction decoder differs from kernel" >&2 )) || true
+       $(call rule_mkdir)
+       $(call if_changed_dep,cc_o_c)
 
 CFLAGS_intel-pt-insn-decoder.o += -I$(OUTPUT)util/intel-pt-decoder -Wno-override-init
index 22ba5022431958d82d5ade0fe4a1b3cbecfc63d8..9409d014b46c713de02df828cf9ad7ff726881e8 100644 (file)
@@ -650,7 +650,7 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info)
                if (data->from_mtc && timestamp < data->timestamp &&
                    data->timestamp - timestamp < decoder->tsc_slip)
                        return 1;
-               while (timestamp < data->timestamp)
+               if (timestamp < data->timestamp)
                        timestamp += (1ULL << 56);
                if (pkt_info->last_packet_type != INTEL_PT_CYC) {
                        if (data->from_mtc)
@@ -1191,7 +1191,7 @@ static void intel_pt_calc_tsc_timestamp(struct intel_pt_decoder *decoder)
                                        timestamp);
                        timestamp = decoder->timestamp;
                }
-               while (timestamp < decoder->timestamp) {
+               if (timestamp < decoder->timestamp) {
                        intel_pt_log_to("Wraparound timestamp", timestamp);
                        timestamp += (1ULL << 56);
                        decoder->tsc_timestamp = timestamp;
index d09c7d9f905002c8f07e833428dd897858826eb6..319bef33a64b2104ab7b17436e6c222f4d656d01 100644 (file)
 
 static FILE *f;
 static char log_name[MAX_LOG_NAME];
-static bool enable_logging;
+bool intel_pt_enable_logging;
 
 void intel_pt_log_enable(void)
 {
-       enable_logging = true;
+       intel_pt_enable_logging = true;
 }
 
 void intel_pt_log_disable(void)
 {
        if (f)
                fflush(f);
-       enable_logging = false;
+       intel_pt_enable_logging = false;
 }
 
 void intel_pt_log_set_name(const char *name)
@@ -80,7 +80,7 @@ static void intel_pt_print_no_data(uint64_t pos, int indent)
 
 static int intel_pt_log_open(void)
 {
-       if (!enable_logging)
+       if (!intel_pt_enable_logging)
                return -1;
 
        if (f)
@@ -91,15 +91,15 @@ static int intel_pt_log_open(void)
 
        f = fopen(log_name, "w+");
        if (!f) {
-               enable_logging = false;
+               intel_pt_enable_logging = false;
                return -1;
        }
 
        return 0;
 }
 
-void intel_pt_log_packet(const struct intel_pt_pkt *packet, int pkt_len,
-                        uint64_t pos, const unsigned char *buf)
+void __intel_pt_log_packet(const struct intel_pt_pkt *packet, int pkt_len,
+                          uint64_t pos, const unsigned char *buf)
 {
        char desc[INTEL_PT_PKT_DESC_MAX];
 
@@ -111,7 +111,7 @@ void intel_pt_log_packet(const struct intel_pt_pkt *packet, int pkt_len,
        fprintf(f, "%s\n", desc);
 }
 
-void intel_pt_log_insn(struct intel_pt_insn *intel_pt_insn, uint64_t ip)
+void __intel_pt_log_insn(struct intel_pt_insn *intel_pt_insn, uint64_t ip)
 {
        char desc[INTEL_PT_INSN_DESC_MAX];
        size_t len = intel_pt_insn->length;
@@ -128,7 +128,8 @@ void intel_pt_log_insn(struct intel_pt_insn *intel_pt_insn, uint64_t ip)
                fprintf(f, "Bad instruction!\n");
 }
 
-void intel_pt_log_insn_no_data(struct intel_pt_insn *intel_pt_insn, uint64_t ip)
+void __intel_pt_log_insn_no_data(struct intel_pt_insn *intel_pt_insn,
+                                uint64_t ip)
 {
        char desc[INTEL_PT_INSN_DESC_MAX];
 
@@ -142,7 +143,7 @@ void intel_pt_log_insn_no_data(struct intel_pt_insn *intel_pt_insn, uint64_t ip)
                fprintf(f, "Bad instruction!\n");
 }
 
-void intel_pt_log(const char *fmt, ...)
+void __intel_pt_log(const char *fmt, ...)
 {
        va_list args;
 
index db3942f83677eb51c2f8681e1811cb7521683759..debe751dc3d68403b31c9b89349657c400648d49 100644 (file)
@@ -25,20 +25,46 @@ void intel_pt_log_enable(void);
 void intel_pt_log_disable(void);
 void intel_pt_log_set_name(const char *name);
 
-void intel_pt_log_packet(const struct intel_pt_pkt *packet, int pkt_len,
-                        uint64_t pos, const unsigned char *buf);
+void __intel_pt_log_packet(const struct intel_pt_pkt *packet, int pkt_len,
+                          uint64_t pos, const unsigned char *buf);
 
 struct intel_pt_insn;
 
-void intel_pt_log_insn(struct intel_pt_insn *intel_pt_insn, uint64_t ip);
-void intel_pt_log_insn_no_data(struct intel_pt_insn *intel_pt_insn,
-                              uint64_t ip);
+void __intel_pt_log_insn(struct intel_pt_insn *intel_pt_insn, uint64_t ip);
+void __intel_pt_log_insn_no_data(struct intel_pt_insn *intel_pt_insn,
+                                uint64_t ip);
 
 __attribute__((format(printf, 1, 2)))
-void intel_pt_log(const char *fmt, ...);
+void __intel_pt_log(const char *fmt, ...);
+
+#define intel_pt_log(fmt, ...) \
+       do { \
+               if (intel_pt_enable_logging) \
+                       __intel_pt_log(fmt, ##__VA_ARGS__); \
+       } while (0)
+
+#define intel_pt_log_packet(arg, ...) \
+       do { \
+               if (intel_pt_enable_logging) \
+                       __intel_pt_log_packet(arg, ##__VA_ARGS__); \
+       } while (0)
+
+#define intel_pt_log_insn(arg, ...) \
+       do { \
+               if (intel_pt_enable_logging) \
+                       __intel_pt_log_insn(arg, ##__VA_ARGS__); \
+       } while (0)
+
+#define intel_pt_log_insn_no_data(arg, ...) \
+       do { \
+               if (intel_pt_enable_logging) \
+                       __intel_pt_log_insn_no_data(arg, ##__VA_ARGS__); \
+       } while (0)
 
 #define x64_fmt "0x%" PRIx64
 
+extern bool intel_pt_enable_logging;
+
 static inline void intel_pt_log_at(const char *msg, uint64_t u)
 {
        intel_pt_log("%s at " x64_fmt "\n", msg, u);
index 816488c0b97e3540a59af5c5f4c001fed1f5ba41..d388de72eacaa353ba14e3340a30ef64b63bcd97 100644 (file)
@@ -353,8 +353,12 @@ AVXcode: 1
 17: vmovhps Mq,Vq (v1) | vmovhpd Mq,Vq (66),(v1)
 18: Grp16 (1A)
 19:
-1a: BNDCL Ev,Gv | BNDCU Ev,Gv | BNDMOV Gv,Ev | BNDLDX Gv,Ev,Gv
-1b: BNDCN Ev,Gv | BNDMOV Ev,Gv | BNDMK Gv,Ev | BNDSTX Ev,GV,Gv
+# Intel SDM opcode map does not list MPX instructions. For now using Gv for
+# bnd registers and Ev for everything else is OK because the instruction
+# decoder does not use the information except as an indication that there is
+# a ModR/M byte.
+1a: BNDCL Gv,Ev (F3) | BNDCU Gv,Ev (F2) | BNDMOV Gv,Ev (66) | BNDLDX Gv,Ev
+1b: BNDCN Gv,Ev (F2) | BNDMOV Ev,Gv (66) | BNDMK Gv,Ev (F3) | BNDSTX Ev,Gv
 1c:
 1d:
 1e:
@@ -732,6 +736,12 @@ bd: vfnmadd231ss/d Vx,Hx,Wx (66),(v),(v1)
 be: vfnmsub231ps/d Vx,Hx,Wx (66),(v)
 bf: vfnmsub231ss/d Vx,Hx,Wx (66),(v),(v1)
 # 0x0f 0x38 0xc0-0xff
+c8: sha1nexte Vdq,Wdq
+c9: sha1msg1 Vdq,Wdq
+ca: sha1msg2 Vdq,Wdq
+cb: sha256rnds2 Vdq,Wdq
+cc: sha256msg1 Vdq,Wdq
+cd: sha256msg2 Vdq,Wdq
 db: VAESIMC Vdq,Wdq (66),(v1)
 dc: VAESENC Vdq,Hdq,Wdq (66),(v1)
 dd: VAESENCLAST Vdq,Hdq,Wdq (66),(v1)
@@ -790,6 +800,7 @@ AVXcode: 3
 61: vpcmpestri Vdq,Wdq,Ib (66),(v1)
 62: vpcmpistrm Vdq,Wdq,Ib (66),(v1)
 63: vpcmpistri Vdq,Wdq,Ib (66),(v1)
+cc: sha1rnds4 Vdq,Wdq,Ib
 df: VAESKEYGEN Vdq,Wdq,Ib (66),(v1)
 f0: RORX Gy,Ey,Ib (F2),(v)
 EndTable
@@ -874,7 +885,7 @@ GrpTable: Grp7
 2: LGDT Ms | XGETBV (000),(11B) | XSETBV (001),(11B) | VMFUNC (100),(11B) | XEND (101)(11B) | XTEST (110)(11B)
 3: LIDT Ms
 4: SMSW Mw/Rv
-5:
+5: rdpkru (110),(11B) | wrpkru (111),(11B)
 6: LMSW Ew
 7: INVLPG Mb | SWAPGS (o64),(000),(11B) | RDTSCP (001),(11B)
 EndTable
@@ -888,6 +899,9 @@ EndTable
 
 GrpTable: Grp9
 1: CMPXCHG8B/16B Mq/Mdq
+3: xrstors
+4: xsavec
+5: xsaves
 6: VMPTRLD Mq | VMCLEAR Mq (66) | VMXON Mq (F3) | RDRAND Rv (11B)
 7: VMPTRST Mq | VMPTRST Mq (F3) | RDSEED Rv (11B)
 EndTable
@@ -932,8 +946,8 @@ GrpTable: Grp15
 3: vstmxcsr Md (v1) | WRGSBASE Ry (F3),(11B)
 4: XSAVE
 5: XRSTOR | lfence (11B)
-6: XSAVEOPT | mfence (11B)
-7: clflush | sfence (11B)
+6: XSAVEOPT | clwb (66) | mfence (11B)
+7: clflush | clflushopt (66) | sfence (11B) | pcommit (66),(11B)
 EndTable
 
 GrpTable: Grp16
index 535d86f8e4d17b802a4c0473a7bf7ff937a10b15..97f963a3dcb95157f8dcaf3e568d3ac487989608 100644 (file)
@@ -22,6 +22,7 @@
 #include "../perf.h"
 #include "session.h"
 #include "machine.h"
+#include "sort.h"
 #include "tool.h"
 #include "event.h"
 #include "evlist.h"
@@ -63,6 +64,7 @@ struct intel_pt {
        bool data_queued;
        bool est_tsc;
        bool sync_switch;
+       bool mispred_all;
        int have_sched_switch;
        u32 pmu_type;
        u64 kernel_start;
@@ -115,6 +117,9 @@ struct intel_pt_queue {
        void *decoder;
        const struct intel_pt_state *state;
        struct ip_callchain *chain;
+       struct branch_stack *last_branch;
+       struct branch_stack *last_branch_rb;
+       size_t last_branch_pos;
        union perf_event *event_buf;
        bool on_heap;
        bool stop;
@@ -675,6 +680,19 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
                        goto out_free;
        }
 
+       if (pt->synth_opts.last_branch) {
+               size_t sz = sizeof(struct branch_stack);
+
+               sz += pt->synth_opts.last_branch_sz *
+                     sizeof(struct branch_entry);
+               ptq->last_branch = zalloc(sz);
+               if (!ptq->last_branch)
+                       goto out_free;
+               ptq->last_branch_rb = zalloc(sz);
+               if (!ptq->last_branch_rb)
+                       goto out_free;
+       }
+
        ptq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
        if (!ptq->event_buf)
                goto out_free;
@@ -720,7 +738,7 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
 
                if (!params.period) {
                        params.period_type = INTEL_PT_PERIOD_INSTRUCTIONS;
-                       params.period = 1000;
+                       params.period = 1;
                }
        }
 
@@ -732,6 +750,8 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
 
 out_free:
        zfree(&ptq->event_buf);
+       zfree(&ptq->last_branch);
+       zfree(&ptq->last_branch_rb);
        zfree(&ptq->chain);
        free(ptq);
        return NULL;
@@ -746,6 +766,8 @@ static void intel_pt_free_queue(void *priv)
        thread__zput(ptq->thread);
        intel_pt_decoder_free(ptq->decoder);
        zfree(&ptq->event_buf);
+       zfree(&ptq->last_branch);
+       zfree(&ptq->last_branch_rb);
        zfree(&ptq->chain);
        free(ptq);
 }
@@ -876,6 +898,58 @@ static int intel_pt_setup_queues(struct intel_pt *pt)
        return 0;
 }
 
+static inline void intel_pt_copy_last_branch_rb(struct intel_pt_queue *ptq)
+{
+       struct branch_stack *bs_src = ptq->last_branch_rb;
+       struct branch_stack *bs_dst = ptq->last_branch;
+       size_t nr = 0;
+
+       bs_dst->nr = bs_src->nr;
+
+       if (!bs_src->nr)
+               return;
+
+       nr = ptq->pt->synth_opts.last_branch_sz - ptq->last_branch_pos;
+       memcpy(&bs_dst->entries[0],
+              &bs_src->entries[ptq->last_branch_pos],
+              sizeof(struct branch_entry) * nr);
+
+       if (bs_src->nr >= ptq->pt->synth_opts.last_branch_sz) {
+               memcpy(&bs_dst->entries[nr],
+                      &bs_src->entries[0],
+                      sizeof(struct branch_entry) * ptq->last_branch_pos);
+       }
+}
+
+static inline void intel_pt_reset_last_branch_rb(struct intel_pt_queue *ptq)
+{
+       ptq->last_branch_pos = 0;
+       ptq->last_branch_rb->nr = 0;
+}
+
+static void intel_pt_update_last_branch_rb(struct intel_pt_queue *ptq)
+{
+       const struct intel_pt_state *state = ptq->state;
+       struct branch_stack *bs = ptq->last_branch_rb;
+       struct branch_entry *be;
+
+       if (!ptq->last_branch_pos)
+               ptq->last_branch_pos = ptq->pt->synth_opts.last_branch_sz;
+
+       ptq->last_branch_pos -= 1;
+
+       be              = &bs->entries[ptq->last_branch_pos];
+       be->from        = state->from_ip;
+       be->to          = state->to_ip;
+       be->flags.abort = !!(state->flags & INTEL_PT_ABORT_TX);
+       be->flags.in_tx = !!(state->flags & INTEL_PT_IN_TX);
+       /* No support for mispredict */
+       be->flags.mispred = ptq->pt->mispred_all;
+
+       if (bs->nr < ptq->pt->synth_opts.last_branch_sz)
+               bs->nr += 1;
+}
+
 static int intel_pt_inject_event(union perf_event *event,
                                 struct perf_sample *sample, u64 type,
                                 bool swapped)
@@ -890,6 +964,13 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq)
        struct intel_pt *pt = ptq->pt;
        union perf_event *event = ptq->event_buf;
        struct perf_sample sample = { .ip = 0, };
+       struct dummy_branch_stack {
+               u64                     nr;
+               struct branch_entry     entries;
+       } dummy_bs;
+
+       if (pt->branches_filter && !(pt->branches_filter & ptq->flags))
+               return 0;
 
        event->sample.header.type = PERF_RECORD_SAMPLE;
        event->sample.header.misc = PERF_RECORD_MISC_USER;
@@ -909,8 +990,20 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq)
        sample.flags = ptq->flags;
        sample.insn_len = ptq->insn_len;
 
-       if (pt->branches_filter && !(pt->branches_filter & ptq->flags))
-               return 0;
+       /*
+        * perf report cannot handle events without a branch stack when using
+        * SORT_MODE__BRANCH so make a dummy one.
+        */
+       if (pt->synth_opts.last_branch && sort__mode == SORT_MODE__BRANCH) {
+               dummy_bs = (struct dummy_branch_stack){
+                       .nr = 1,
+                       .entries = {
+                               .from = sample.ip,
+                               .to = sample.addr,
+                       },
+               };
+               sample.branch_stack = (struct branch_stack *)&dummy_bs;
+       }
 
        if (pt->synth_opts.inject) {
                ret = intel_pt_inject_event(event, &sample,
@@ -961,6 +1054,11 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq)
                sample.callchain = ptq->chain;
        }
 
+       if (pt->synth_opts.last_branch) {
+               intel_pt_copy_last_branch_rb(ptq);
+               sample.branch_stack = ptq->last_branch;
+       }
+
        if (pt->synth_opts.inject) {
                ret = intel_pt_inject_event(event, &sample,
                                            pt->instructions_sample_type,
@@ -974,6 +1072,9 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq)
                pr_err("Intel Processor Trace: failed to deliver instruction event, error %d\n",
                       ret);
 
+       if (pt->synth_opts.last_branch)
+               intel_pt_reset_last_branch_rb(ptq);
+
        return ret;
 }
 
@@ -1008,6 +1109,11 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq)
                sample.callchain = ptq->chain;
        }
 
+       if (pt->synth_opts.last_branch) {
+               intel_pt_copy_last_branch_rb(ptq);
+               sample.branch_stack = ptq->last_branch;
+       }
+
        if (pt->synth_opts.inject) {
                ret = intel_pt_inject_event(event, &sample,
                                            pt->transactions_sample_type,
@@ -1021,6 +1127,9 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq)
                pr_err("Intel Processor Trace: failed to deliver transaction event, error %d\n",
                       ret);
 
+       if (pt->synth_opts.callchain)
+               intel_pt_reset_last_branch_rb(ptq);
+
        return ret;
 }
 
@@ -1116,6 +1225,9 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
                        return err;
        }
 
+       if (pt->synth_opts.last_branch)
+               intel_pt_update_last_branch_rb(ptq);
+
        if (!pt->sync_switch)
                return 0;
 
@@ -1145,16 +1257,18 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
        return 0;
 }
 
-static u64 intel_pt_switch_ip(struct machine *machine, u64 *ptss_ip)
+static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
 {
+       struct machine *machine = pt->machine;
        struct map *map;
        struct symbol *sym, *start;
        u64 ip, switch_ip = 0;
+       const char *ptss;
 
        if (ptss_ip)
                *ptss_ip = 0;
 
-       map = machine__kernel_map(machine, MAP__FUNCTION);
+       map = machine__kernel_map(machine);
        if (!map)
                return 0;
 
@@ -1177,8 +1291,13 @@ static u64 intel_pt_switch_ip(struct machine *machine, u64 *ptss_ip)
        if (!switch_ip || !ptss_ip)
                return 0;
 
+       if (pt->have_sched_switch == 1)
+               ptss = "perf_trace_sched_switch";
+       else
+               ptss = "__perf_event_task_sched_out";
+
        for (sym = start; sym; sym = dso__next_symbol(sym)) {
-               if (!strcmp(sym->name, "perf_trace_sched_switch")) {
+               if (!strcmp(sym->name, ptss)) {
                        ip = map->unmap_ip(map, sym->start);
                        if (ip >= map->start && ip < map->end) {
                                *ptss_ip = ip;
@@ -1198,11 +1317,11 @@ static int intel_pt_run_decoder(struct intel_pt_queue *ptq, u64 *timestamp)
 
        if (!pt->kernel_start) {
                pt->kernel_start = machine__kernel_start(pt->machine);
-               if (pt->per_cpu_mmaps && pt->have_sched_switch &&
+               if (pt->per_cpu_mmaps &&
+                   (pt->have_sched_switch == 1 || pt->have_sched_switch == 3) &&
                    !pt->timeless_decoding && intel_pt_tracing_kernel(pt) &&
                    !pt->sampling_mode) {
-                       pt->switch_ip = intel_pt_switch_ip(pt->machine,
-                                                          &pt->ptss_ip);
+                       pt->switch_ip = intel_pt_switch_ip(pt, &pt->ptss_ip);
                        if (pt->switch_ip) {
                                intel_pt_log("switch_ip: %"PRIx64" ptss_ip: %"PRIx64"\n",
                                             pt->switch_ip, pt->ptss_ip);
@@ -1387,31 +1506,18 @@ static struct intel_pt_queue *intel_pt_cpu_to_ptq(struct intel_pt *pt, int cpu)
        return NULL;
 }
 
-static int intel_pt_process_switch(struct intel_pt *pt,
-                                  struct perf_sample *sample)
+static int intel_pt_sync_switch(struct intel_pt *pt, int cpu, pid_t tid,
+                               u64 timestamp)
 {
        struct intel_pt_queue *ptq;
-       struct perf_evsel *evsel;
-       pid_t tid;
-       int cpu, err;
-
-       evsel = perf_evlist__id2evsel(pt->session->evlist, sample->id);
-       if (evsel != pt->switch_evsel)
-               return 0;
-
-       tid = perf_evsel__intval(evsel, sample, "next_pid");
-       cpu = sample->cpu;
-
-       intel_pt_log("sched_switch: cpu %d tid %d time %"PRIu64" tsc %#"PRIx64"\n",
-                    cpu, tid, sample->time, perf_time_to_tsc(sample->time,
-                    &pt->tc));
+       int err;
 
        if (!pt->sync_switch)
-               goto out;
+               return 1;
 
        ptq = intel_pt_cpu_to_ptq(pt, cpu);
        if (!ptq)
-               goto out;
+               return 1;
 
        switch (ptq->switch_state) {
        case INTEL_PT_SS_NOT_TRACING:
@@ -1424,7 +1530,7 @@ static int intel_pt_process_switch(struct intel_pt *pt,
                return 0;
        case INTEL_PT_SS_EXPECTING_SWITCH_EVENT:
                if (!ptq->on_heap) {
-                       ptq->timestamp = perf_time_to_tsc(sample->time,
+                       ptq->timestamp = perf_time_to_tsc(timestamp,
                                                          &pt->tc);
                        err = auxtrace_heap__add(&pt->heap, ptq->queue_nr,
                                                 ptq->timestamp);
@@ -1441,10 +1547,76 @@ static int intel_pt_process_switch(struct intel_pt *pt,
        default:
                break;
        }
-out:
+
+       return 1;
+}
+
+static int intel_pt_process_switch(struct intel_pt *pt,
+                                  struct perf_sample *sample)
+{
+       struct perf_evsel *evsel;
+       pid_t tid;
+       int cpu, ret;
+
+       evsel = perf_evlist__id2evsel(pt->session->evlist, sample->id);
+       if (evsel != pt->switch_evsel)
+               return 0;
+
+       tid = perf_evsel__intval(evsel, sample, "next_pid");
+       cpu = sample->cpu;
+
+       intel_pt_log("sched_switch: cpu %d tid %d time %"PRIu64" tsc %#"PRIx64"\n",
+                    cpu, tid, sample->time, perf_time_to_tsc(sample->time,
+                    &pt->tc));
+
+       ret = intel_pt_sync_switch(pt, cpu, tid, sample->time);
+       if (ret <= 0)
+               return ret;
+
        return machine__set_current_tid(pt->machine, cpu, -1, tid);
 }
 
+static int intel_pt_context_switch(struct intel_pt *pt, union perf_event *event,
+                                  struct perf_sample *sample)
+{
+       bool out = event->header.misc & PERF_RECORD_MISC_SWITCH_OUT;
+       pid_t pid, tid;
+       int cpu, ret;
+
+       cpu = sample->cpu;
+
+       if (pt->have_sched_switch == 3) {
+               if (!out)
+                       return 0;
+               if (event->header.type != PERF_RECORD_SWITCH_CPU_WIDE) {
+                       pr_err("Expecting CPU-wide context switch event\n");
+                       return -EINVAL;
+               }
+               pid = event->context_switch.next_prev_pid;
+               tid = event->context_switch.next_prev_tid;
+       } else {
+               if (out)
+                       return 0;
+               pid = sample->pid;
+               tid = sample->tid;
+       }
+
+       if (tid == -1) {
+               pr_err("context_switch event has no tid\n");
+               return -EINVAL;
+       }
+
+       intel_pt_log("context_switch: cpu %d pid %d tid %d time %"PRIu64" tsc %#"PRIx64"\n",
+                    cpu, pid, tid, sample->time, perf_time_to_tsc(sample->time,
+                    &pt->tc));
+
+       ret = intel_pt_sync_switch(pt, cpu, tid, sample->time);
+       if (ret <= 0)
+               return ret;
+
+       return machine__set_current_tid(pt->machine, cpu, pid, tid);
+}
+
 static int intel_pt_process_itrace_start(struct intel_pt *pt,
                                         union perf_event *event,
                                         struct perf_sample *sample)
@@ -1515,6 +1687,9 @@ static int intel_pt_process_event(struct perf_session *session,
                err = intel_pt_process_switch(pt, sample);
        else if (event->header.type == PERF_RECORD_ITRACE_START)
                err = intel_pt_process_itrace_start(pt, event, sample);
+       else if (event->header.type == PERF_RECORD_SWITCH ||
+                event->header.type == PERF_RECORD_SWITCH_CPU_WIDE)
+               err = intel_pt_context_switch(pt, event, sample);
 
        intel_pt_log("event %s (%u): cpu %d time %"PRIu64" tsc %#"PRIx64"\n",
                     perf_event__name(event->header.type), event->header.type,
@@ -1700,6 +1875,8 @@ static int intel_pt_synth_events(struct intel_pt *pt,
                pt->instructions_sample_period = attr.sample_period;
                if (pt->synth_opts.callchain)
                        attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
+               if (pt->synth_opts.last_branch)
+                       attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
                pr_debug("Synthesizing 'instructions' event with id %" PRIu64 " sample type %#" PRIx64 "\n",
                         id, (u64)attr.sample_type);
                err = intel_pt_synth_event(session, &attr, id);
@@ -1719,6 +1896,8 @@ static int intel_pt_synth_events(struct intel_pt *pt,
                attr.sample_period = 1;
                if (pt->synth_opts.callchain)
                        attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
+               if (pt->synth_opts.last_branch)
+                       attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
                pr_debug("Synthesizing 'transactions' event with id %" PRIu64 " sample type %#" PRIx64 "\n",
                         id, (u64)attr.sample_type);
                err = intel_pt_synth_event(session, &attr, id);
@@ -1745,6 +1924,7 @@ static int intel_pt_synth_events(struct intel_pt *pt,
                attr.sample_period = 1;
                attr.sample_type |= PERF_SAMPLE_ADDR;
                attr.sample_type &= ~(u64)PERF_SAMPLE_CALLCHAIN;
+               attr.sample_type &= ~(u64)PERF_SAMPLE_BRANCH_STACK;
                pr_debug("Synthesizing 'branches' event with id %" PRIu64 " sample type %#" PRIx64 "\n",
                         id, (u64)attr.sample_type);
                err = intel_pt_synth_event(session, &attr, id);
@@ -1777,6 +1957,28 @@ static struct perf_evsel *intel_pt_find_sched_switch(struct perf_evlist *evlist)
        return NULL;
 }
 
+static bool intel_pt_find_switch(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel;
+
+       evlist__for_each(evlist, evsel) {
+               if (evsel->attr.context_switch)
+                       return true;
+       }
+
+       return false;
+}
+
+static int intel_pt_perf_config(const char *var, const char *value, void *data)
+{
+       struct intel_pt *pt = data;
+
+       if (!strcmp(var, "intel-pt.mispred-all"))
+               pt->mispred_all = perf_config_bool(var, value);
+
+       return 0;
+}
+
 static const char * const intel_pt_info_fmts[] = {
        [INTEL_PT_PMU_TYPE]             = "  PMU Type            %"PRId64"\n",
        [INTEL_PT_TIME_SHIFT]           = "  Time Shift          %"PRIu64"\n",
@@ -1821,6 +2023,8 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
        if (!pt)
                return -ENOMEM;
 
+       perf_config(intel_pt_perf_config, pt);
+
        err = auxtrace_queues__init(&pt->queues);
        if (err)
                goto err_free;
@@ -1888,6 +2092,10 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
                        pr_err("%s: missing sched_switch event\n", __func__);
                        goto err_delete_thread;
                }
+       } else if (pt->have_sched_switch == 2 &&
+                  !intel_pt_find_switch(session->evlist)) {
+               pr_err("%s: missing context_switch attribute flag\n", __func__);
+               goto err_delete_thread;
        }
 
        if (session->itrace_synth_opts && session->itrace_synth_opts->set) {
index 6309f7ceb08f14cfbf0ce0b3cd23d82d4603a60e..5ef90be2a2497a05731a5fb8d24af33a192f2011 100644 (file)
@@ -35,6 +35,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
        machine->last_match = NULL;
 
        machine->vdso_info = NULL;
+       machine->env = NULL;
 
        machine->pid = pid;
 
@@ -624,7 +625,7 @@ size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp)
 {
        int i;
        size_t printed = 0;
-       struct dso *kdso = machine->vmlinux_maps[MAP__FUNCTION]->dso;
+       struct dso *kdso = machine__kernel_map(machine)->dso;
 
        if (kdso->has_build_id) {
                char filename[PATH_MAX];
@@ -740,6 +741,7 @@ int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)
 
        for (type = 0; type < MAP__NR_TYPES; ++type) {
                struct kmap *kmap;
+               struct map *map;
 
                machine->vmlinux_maps[type] = map__new2(start, kernel, type);
                if (machine->vmlinux_maps[type] == NULL)
@@ -748,13 +750,13 @@ int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)
                machine->vmlinux_maps[type]->map_ip =
                        machine->vmlinux_maps[type]->unmap_ip =
                                identity__map_ip;
-               kmap = map__kmap(machine->vmlinux_maps[type]);
+               map = __machine__kernel_map(machine, type);
+               kmap = map__kmap(map);
                if (!kmap)
                        return -1;
 
                kmap->kmaps = &machine->kmaps;
-               map_groups__insert(&machine->kmaps,
-                                  machine->vmlinux_maps[type]);
+               map_groups__insert(&machine->kmaps, map);
        }
 
        return 0;
@@ -766,13 +768,13 @@ void machine__destroy_kernel_maps(struct machine *machine)
 
        for (type = 0; type < MAP__NR_TYPES; ++type) {
                struct kmap *kmap;
+               struct map *map = __machine__kernel_map(machine, type);
 
-               if (machine->vmlinux_maps[type] == NULL)
+               if (map == NULL)
                        continue;
 
-               kmap = map__kmap(machine->vmlinux_maps[type]);
-               map_groups__remove(&machine->kmaps,
-                                  machine->vmlinux_maps[type]);
+               kmap = map__kmap(map);
+               map_groups__remove(&machine->kmaps, map);
                if (kmap && kmap->ref_reloc_sym) {
                        /*
                         * ref_reloc_sym is shared among all maps, so free just
@@ -866,7 +868,7 @@ int machines__create_kernel_maps(struct machines *machines, pid_t pid)
 int machine__load_kallsyms(struct machine *machine, const char *filename,
                           enum map_type type, symbol_filter_t filter)
 {
-       struct map *map = machine->vmlinux_maps[type];
+       struct map *map = machine__kernel_map(machine);
        int ret = dso__load_kallsyms(map->dso, filename, map, filter);
 
        if (ret > 0) {
@@ -885,7 +887,7 @@ int machine__load_kallsyms(struct machine *machine, const char *filename,
 int machine__load_vmlinux_path(struct machine *machine, enum map_type type,
                               symbol_filter_t filter)
 {
-       struct map *map = machine->vmlinux_maps[type];
+       struct map *map = machine__kernel_map(machine);
        int ret = dso__load_vmlinux_path(map->dso, map, filter);
 
        if (ret > 0)
@@ -1243,8 +1245,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
                        /*
                         * preload dso of guest kernel and modules
                         */
-                       dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION],
-                                 NULL);
+                       dso__load(kernel, machine__kernel_map(machine), NULL);
                }
        }
        return 0;
@@ -1830,7 +1831,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
        }
 
 check_calls:
-       if (chain->nr > PERF_MAX_STACK_DEPTH) {
+       if (chain->nr > PERF_MAX_STACK_DEPTH && (int)chain->nr > max_stack) {
                pr_warning("corrupted callchain. skipping...\n");
                return 0;
        }
@@ -1996,7 +1997,7 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
 
 int machine__get_kernel_start(struct machine *machine)
 {
-       struct map *map = machine__kernel_map(machine, MAP__FUNCTION);
+       struct map *map = machine__kernel_map(machine);
        int err = 0;
 
        /*
index ea5cb4a621db80056f008d3e3f71ddecbda761d8..2c2b443df5ba796c43ee12c6c0f2061b5d7c7b80 100644 (file)
@@ -34,6 +34,7 @@ struct machine {
        struct list_head  dead_threads;
        struct thread     *last_match;
        struct vdso_info  *vdso_info;
+       struct perf_env   *env;
        struct dsos       dsos;
        struct map_groups kmaps;
        struct map        *vmlinux_maps[MAP__NR_TYPES];
@@ -47,11 +48,17 @@ struct machine {
 };
 
 static inline
-struct map *machine__kernel_map(struct machine *machine, enum map_type type)
+struct map *__machine__kernel_map(struct machine *machine, enum map_type type)
 {
        return machine->vmlinux_maps[type];
 }
 
+static inline
+struct map *machine__kernel_map(struct machine *machine)
+{
+       return __machine__kernel_map(machine, MAP__FUNCTION);
+}
+
 int machine__get_kernel_start(struct machine *machine);
 
 static inline u64 machine__kernel_start(struct machine *machine)
index b1c475d9b240393e73614bc249e9e4b3dc2eedfc..4e38c396a897ad9599d1addb1fb464b5cdfa4129 100644 (file)
@@ -235,7 +235,7 @@ struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
  */
 bool __map__is_kernel(const struct map *map)
 {
-       return map->groups->machine->vmlinux_maps[map->type] == map;
+       return __machine__kernel_map(map->groups->machine, map->type) == map;
 }
 
 static void map__exit(struct map *map)
@@ -553,13 +553,9 @@ struct symbol *map_groups__find_symbol(struct map_groups *mg,
        return NULL;
 }
 
-struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,
-                                              enum map_type type,
-                                              const char *name,
-                                              struct map **mapp,
-                                              symbol_filter_t filter)
+struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name,
+                                        struct map **mapp, symbol_filter_t filter)
 {
-       struct maps *maps = &mg->maps[type];
        struct symbol *sym;
        struct rb_node *nd;
 
@@ -583,6 +579,17 @@ out:
        return sym;
 }
 
+struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,
+                                              enum map_type type,
+                                              const char *name,
+                                              struct map **mapp,
+                                              symbol_filter_t filter)
+{
+       struct symbol *sym = maps__find_symbol_by_name(&mg->maps[type], name, mapp, filter);
+
+       return sym;
+}
+
 int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter)
 {
        if (ams->addr < ams->map->start || ams->addr >= ams->map->end) {
index 57829e89b78b2f22e1977bb7c2ca2c315bcfa5f0..7309d64ce39e17d74416a1af04d57249c0cf5295 100644 (file)
@@ -190,6 +190,8 @@ void maps__remove(struct maps *maps, struct map *map);
 struct map *maps__find(struct maps *maps, u64 addr);
 struct map *maps__first(struct maps *maps);
 struct map *map__next(struct map *map);
+struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name,
+                                         struct map **mapp, symbol_filter_t filter);
 void map_groups__init(struct map_groups *mg, struct machine *machine);
 void map_groups__exit(struct map_groups *mg);
 int map_groups__clone(struct map_groups *mg,
index a3b1e13a05c0dd493e5bbb8a394705bff6e73fd2..355eecf6bf598ba051dde4df6368747e55fb141a 100644 (file)
@@ -27,6 +27,7 @@ static const struct branch_mode branch_modes[] = {
        BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX),
        BRANCH_OPT("cond", PERF_SAMPLE_BRANCH_COND),
        BRANCH_OPT("ind_jmp", PERF_SAMPLE_BRANCH_IND_JUMP),
+       BRANCH_OPT("call", PERF_SAMPLE_BRANCH_CALL),
        BRANCH_END
 };
 
index 21ed6ee63da9747d1215ac638f936357c8df2e8c..bee60583839a7cdae0dd44f5c48dd818d53cb5e4 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/hw_breakpoint.h>
+#include <linux/err.h>
 #include "util.h"
 #include "../perf.h"
 #include "evlist.h"
@@ -10,8 +11,9 @@
 #include "symbol.h"
 #include "cache.h"
 #include "header.h"
+#include "bpf-loader.h"
 #include "debug.h"
-#include <api/fs/debugfs.h>
+#include <api/fs/tracing_path.h>
 #include "parse-events-bison.h"
 #define YY_EXTRA_TYPE int
 #include "parse-events-flex.h"
@@ -26,6 +28,8 @@
 extern int parse_events_debug;
 #endif
 int parse_events_parse(void *data, void *scanner);
+static int get_config_terms(struct list_head *head_config,
+                           struct list_head *head_terms __maybe_unused);
 
 static struct perf_pmu_event_symbol *perf_pmu_events_list;
 /*
@@ -386,32 +390,72 @@ int parse_events_add_cache(struct list_head *list, int *idx,
        return add_event(list, idx, &attr, name, NULL);
 }
 
+static void tracepoint_error(struct parse_events_error *e, int err,
+                            char *sys, char *name)
+{
+       char help[BUFSIZ];
+
+       /*
+        * We get error directly from syscall errno ( > 0),
+        * or from encoded pointer's error ( < 0).
+        */
+       err = abs(err);
+
+       switch (err) {
+       case EACCES:
+               e->str = strdup("can't access trace events");
+               break;
+       case ENOENT:
+               e->str = strdup("unknown tracepoint");
+               break;
+       default:
+               e->str = strdup("failed to add tracepoint");
+               break;
+       }
+
+       tracing_path__strerror_open_tp(err, help, sizeof(help), sys, name);
+       e->help = strdup(help);
+}
+
 static int add_tracepoint(struct list_head *list, int *idx,
-                         char *sys_name, char *evt_name)
+                         char *sys_name, char *evt_name,
+                         struct parse_events_error *err,
+                         struct list_head *head_config)
 {
        struct perf_evsel *evsel;
 
        evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++);
-       if (!evsel)
-               return -ENOMEM;
+       if (IS_ERR(evsel)) {
+               tracepoint_error(err, PTR_ERR(evsel), sys_name, evt_name);
+               return PTR_ERR(evsel);
+       }
 
-       list_add_tail(&evsel->node, list);
+       if (head_config) {
+               LIST_HEAD(config_terms);
 
+               if (get_config_terms(head_config, &config_terms))
+                       return -ENOMEM;
+               list_splice(&config_terms, &evsel->config_terms);
+       }
+
+       list_add_tail(&evsel->node, list);
        return 0;
 }
 
 static int add_tracepoint_multi_event(struct list_head *list, int *idx,
-                                     char *sys_name, char *evt_name)
+                                     char *sys_name, char *evt_name,
+                                     struct parse_events_error *err,
+                                     struct list_head *head_config)
 {
        char evt_path[MAXPATHLEN];
        struct dirent *evt_ent;
        DIR *evt_dir;
-       int ret = 0;
+       int ret = 0, found = 0;
 
        snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);
        evt_dir = opendir(evt_path);
        if (!evt_dir) {
-               perror("Can't open event dir");
+               tracepoint_error(err, errno, sys_name, evt_name);
                return -1;
        }
 
@@ -425,7 +469,15 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx,
                if (!strglobmatch(evt_ent->d_name, evt_name))
                        continue;
 
-               ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name);
+               found++;
+
+               ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name,
+                                    err, head_config);
+       }
+
+       if (!found) {
+               tracepoint_error(err, ENOENT, sys_name, evt_name);
+               ret = -1;
        }
 
        closedir(evt_dir);
@@ -433,15 +485,21 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx,
 }
 
 static int add_tracepoint_event(struct list_head *list, int *idx,
-                               char *sys_name, char *evt_name)
+                               char *sys_name, char *evt_name,
+                               struct parse_events_error *err,
+                               struct list_head *head_config)
 {
        return strpbrk(evt_name, "*?") ?
-              add_tracepoint_multi_event(list, idx, sys_name, evt_name) :
-              add_tracepoint(list, idx, sys_name, evt_name);
+              add_tracepoint_multi_event(list, idx, sys_name, evt_name,
+                                         err, head_config) :
+              add_tracepoint(list, idx, sys_name, evt_name,
+                             err, head_config);
 }
 
 static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
-                                   char *sys_name, char *evt_name)
+                                   char *sys_name, char *evt_name,
+                                   struct parse_events_error *err,
+                                   struct list_head *head_config)
 {
        struct dirent *events_ent;
        DIR *events_dir;
@@ -449,7 +507,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
 
        events_dir = opendir(tracing_events_path);
        if (!events_dir) {
-               perror("Can't open event dir");
+               tracepoint_error(err, errno, sys_name, evt_name);
                return -1;
        }
 
@@ -465,20 +523,135 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
                        continue;
 
                ret = add_tracepoint_event(list, idx, events_ent->d_name,
-                                          evt_name);
+                                          evt_name, err, head_config);
        }
 
        closedir(events_dir);
        return ret;
 }
 
-int parse_events_add_tracepoint(struct list_head *list, int *idx,
-                               char *sys, char *event)
+struct __add_bpf_event_param {
+       struct parse_events_evlist *data;
+       struct list_head *list;
+};
+
+static int add_bpf_event(struct probe_trace_event *tev, int fd,
+                        void *_param)
 {
-       if (strpbrk(sys, "*?"))
-               return add_tracepoint_multi_sys(list, idx, sys, event);
-       else
-               return add_tracepoint_event(list, idx, sys, event);
+       LIST_HEAD(new_evsels);
+       struct __add_bpf_event_param *param = _param;
+       struct parse_events_evlist *evlist = param->data;
+       struct list_head *list = param->list;
+       struct perf_evsel *pos;
+       int err;
+
+       pr_debug("add bpf event %s:%s and attach bpf program %d\n",
+                tev->group, tev->event, fd);
+
+       err = parse_events_add_tracepoint(&new_evsels, &evlist->idx, tev->group,
+                                         tev->event, evlist->error, NULL);
+       if (err) {
+               struct perf_evsel *evsel, *tmp;
+
+               pr_debug("Failed to add BPF event %s:%s\n",
+                        tev->group, tev->event);
+               list_for_each_entry_safe(evsel, tmp, &new_evsels, node) {
+                       list_del(&evsel->node);
+                       perf_evsel__delete(evsel);
+               }
+               return err;
+       }
+       pr_debug("adding %s:%s\n", tev->group, tev->event);
+
+       list_for_each_entry(pos, &new_evsels, node) {
+               pr_debug("adding %s:%s to %p\n",
+                        tev->group, tev->event, pos);
+               pos->bpf_fd = fd;
+       }
+       list_splice(&new_evsels, list);
+       return 0;
+}
+
+int parse_events_load_bpf_obj(struct parse_events_evlist *data,
+                             struct list_head *list,
+                             struct bpf_object *obj)
+{
+       int err;
+       char errbuf[BUFSIZ];
+       struct __add_bpf_event_param param = {data, list};
+       static bool registered_unprobe_atexit = false;
+
+       if (IS_ERR(obj) || !obj) {
+               snprintf(errbuf, sizeof(errbuf),
+                        "Internal error: load bpf obj with NULL");
+               err = -EINVAL;
+               goto errout;
+       }
+
+       /*
+        * Register atexit handler before calling bpf__probe() so
+        * bpf__probe() don't need to unprobe probe points its already
+        * created when failure.
+        */
+       if (!registered_unprobe_atexit) {
+               atexit(bpf__clear);
+               registered_unprobe_atexit = true;
+       }
+
+       err = bpf__probe(obj);
+       if (err) {
+               bpf__strerror_probe(obj, err, errbuf, sizeof(errbuf));
+               goto errout;
+       }
+
+       err = bpf__load(obj);
+       if (err) {
+               bpf__strerror_load(obj, err, errbuf, sizeof(errbuf));
+               goto errout;
+       }
+
+       err = bpf__foreach_tev(obj, add_bpf_event, &param);
+       if (err) {
+               snprintf(errbuf, sizeof(errbuf),
+                        "Attach events in BPF object failed");
+               goto errout;
+       }
+
+       return 0;
+errout:
+       data->error->help = strdup("(add -v to see detail)");
+       data->error->str = strdup(errbuf);
+       return err;
+}
+
+int parse_events_load_bpf(struct parse_events_evlist *data,
+                         struct list_head *list,
+                         char *bpf_file_name,
+                         bool source)
+{
+       struct bpf_object *obj;
+
+       obj = bpf__prepare_load(bpf_file_name, source);
+       if (IS_ERR(obj) || !obj) {
+               char errbuf[BUFSIZ];
+               int err;
+
+               err = obj ? PTR_ERR(obj) : -EINVAL;
+
+               if (err == -ENOTSUP)
+                       snprintf(errbuf, sizeof(errbuf),
+                                "BPF support is not compiled");
+               else
+                       snprintf(errbuf, sizeof(errbuf),
+                                "BPF object file '%s' is invalid",
+                                bpf_file_name);
+
+               data->error->help = strdup("(add -v to see detail)");
+               data->error->str = strdup(errbuf);
+               return err;
+       }
+
+       return parse_events_load_bpf_obj(data, list, obj);
 }
 
 static int
@@ -565,9 +738,13 @@ static int check_type_val(struct parse_events_term *term,
        return -EINVAL;
 }
 
-static int config_term(struct perf_event_attr *attr,
-                      struct parse_events_term *term,
-                      struct parse_events_error *err)
+typedef int config_term_func_t(struct perf_event_attr *attr,
+                              struct parse_events_term *term,
+                              struct parse_events_error *err);
+
+static int config_term_common(struct perf_event_attr *attr,
+                             struct parse_events_term *term,
+                             struct parse_events_error *err)
 {
 #define CHECK_TYPE_VAL(type)                                              \
 do {                                                                      \
@@ -576,12 +753,6 @@ do {                                                                          \
 } while (0)
 
        switch (term->type_term) {
-       case PARSE_EVENTS__TERM_TYPE_USER:
-               /*
-                * Always succeed for sysfs terms, as we dont know
-                * at this point what type they need to have.
-                */
-               return 0;
        case PARSE_EVENTS__TERM_TYPE_CONFIG:
                CHECK_TYPE_VAL(NUM);
                attr->config = term->val.num;
@@ -620,10 +791,19 @@ do {                                                                         \
        case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
                CHECK_TYPE_VAL(NUM);
                break;
+       case PARSE_EVENTS__TERM_TYPE_INHERIT:
+               CHECK_TYPE_VAL(NUM);
+               break;
+       case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
+               CHECK_TYPE_VAL(NUM);
+               break;
        case PARSE_EVENTS__TERM_TYPE_NAME:
                CHECK_TYPE_VAL(STR);
                break;
        default:
+               err->str = strdup("unknown term");
+               err->idx = term->err_term;
+               err->help = parse_events_formats_error_string(NULL);
                return -EINVAL;
        }
 
@@ -631,9 +811,46 @@ do {                                                                          \
 #undef CHECK_TYPE_VAL
 }
 
+static int config_term_pmu(struct perf_event_attr *attr,
+                          struct parse_events_term *term,
+                          struct parse_events_error *err)
+{
+       if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER)
+               /*
+                * Always succeed for sysfs terms, as we dont know
+                * at this point what type they need to have.
+                */
+               return 0;
+       else
+               return config_term_common(attr, term, err);
+}
+
+static int config_term_tracepoint(struct perf_event_attr *attr,
+                                 struct parse_events_term *term,
+                                 struct parse_events_error *err)
+{
+       switch (term->type_term) {
+       case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
+       case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
+       case PARSE_EVENTS__TERM_TYPE_INHERIT:
+       case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
+               return config_term_common(attr, term, err);
+       default:
+               if (err) {
+                       err->idx = term->err_term;
+                       err->str = strdup("unknown term");
+                       err->help = strdup("valid terms: call-graph,stack-size\n");
+               }
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int config_attr(struct perf_event_attr *attr,
                       struct list_head *head,
-                      struct parse_events_error *err)
+                      struct parse_events_error *err,
+                      config_term_func_t config_term)
 {
        struct parse_events_term *term;
 
@@ -680,6 +897,12 @@ do {                                                               \
                case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
                        ADD_CONFIG_TERM(STACK_USER, stack_user, term->val.num);
                        break;
+               case PARSE_EVENTS__TERM_TYPE_INHERIT:
+                       ADD_CONFIG_TERM(INHERIT, inherit, term->val.num ? 1 : 0);
+                       break;
+               case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
+                       ADD_CONFIG_TERM(INHERIT, inherit, term->val.num ? 0 : 1);
+                       break;
                default:
                        break;
                }
@@ -688,6 +911,27 @@ do {                                                               \
        return 0;
 }
 
+int parse_events_add_tracepoint(struct list_head *list, int *idx,
+                               char *sys, char *event,
+                               struct parse_events_error *err,
+                               struct list_head *head_config)
+{
+       if (head_config) {
+               struct perf_event_attr attr;
+
+               if (config_attr(&attr, head_config, err,
+                               config_term_tracepoint))
+                       return -EINVAL;
+       }
+
+       if (strpbrk(sys, "*?"))
+               return add_tracepoint_multi_sys(list, idx, sys, event,
+                                               err, head_config);
+       else
+               return add_tracepoint_event(list, idx, sys, event,
+                                           err, head_config);
+}
+
 int parse_events_add_numeric(struct parse_events_evlist *data,
                             struct list_head *list,
                             u32 type, u64 config,
@@ -701,7 +945,8 @@ int parse_events_add_numeric(struct parse_events_evlist *data,
        attr.config = config;
 
        if (head_config) {
-               if (config_attr(&attr, head_config, data->error))
+               if (config_attr(&attr, head_config, data->error,
+                               config_term_common))
                        return -EINVAL;
 
                if (get_config_terms(head_config, &config_terms))
@@ -761,7 +1006,7 @@ int parse_events_add_pmu(struct parse_events_evlist *data,
         * Configure hardcoded terms first, no need to check
         * return value when called with fail == 0 ;)
         */
-       if (config_attr(&attr, head_config, data->error))
+       if (config_attr(&attr, head_config, data->error, config_term_pmu))
                return -EINVAL;
 
        if (get_config_terms(head_config, &config_terms))
@@ -793,6 +1038,11 @@ void parse_events__set_leader(char *name, struct list_head *list)
 {
        struct perf_evsel *leader;
 
+       if (list_empty(list)) {
+               WARN_ONCE(true, "WARNING: failed to set leader: empty list");
+               return;
+       }
+
        __perf_evlist__set_leader(list);
        leader = list_entry(list->next, struct perf_evsel, node);
        leader->group_name = name ? strdup(name) : NULL;
@@ -819,6 +1069,7 @@ struct event_modifier {
        int eG;
        int eI;
        int precise;
+       int precise_max;
        int exclude_GH;
        int sample_read;
        int pinned;
@@ -834,6 +1085,7 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
        int eG = evsel ? evsel->attr.exclude_guest : 0;
        int eI = evsel ? evsel->attr.exclude_idle : 0;
        int precise = evsel ? evsel->attr.precise_ip : 0;
+       int precise_max = 0;
        int sample_read = 0;
        int pinned = evsel ? evsel->attr.pinned : 0;
 
@@ -870,6 +1122,8 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
                        /* use of precise requires exclude_guest */
                        if (!exclude_GH)
                                eG = 1;
+               } else if (*str == 'P') {
+                       precise_max = 1;
                } else if (*str == 'S') {
                        sample_read = 1;
                } else if (*str == 'D') {
@@ -900,6 +1154,7 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
        mod->eG = eG;
        mod->eI = eI;
        mod->precise = precise;
+       mod->precise_max = precise_max;
        mod->exclude_GH = exclude_GH;
        mod->sample_read = sample_read;
        mod->pinned = pinned;
@@ -916,7 +1171,7 @@ static int check_modifier(char *str)
        char *p = str;
 
        /* The sizeof includes 0 byte as well. */
-       if (strlen(str) > (sizeof("ukhGHpppSDI") - 1))
+       if (strlen(str) > (sizeof("ukhGHpppPSDI") - 1))
                return -1;
 
        while (*p) {
@@ -955,6 +1210,7 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add)
                evsel->attr.exclude_idle   = mod.eI;
                evsel->exclude_GH          = mod.exclude_GH;
                evsel->sample_read         = mod.sample_read;
+               evsel->precise_max         = mod.precise_max;
 
                if (perf_evsel__is_group_leader(evsel))
                        evsel->attr.pinned = mod.pinned;
@@ -1142,6 +1398,11 @@ int parse_events(struct perf_evlist *evlist, const char *str,
        if (!ret) {
                struct perf_evsel *last;
 
+               if (list_empty(&data.list)) {
+                       WARN_ONCE(true, "WARNING: event parser found nothing");
+                       return -1;
+               }
+
                perf_evlist__splice_list_tail(evlist, &data.list);
                evlist->nr_groups += data.nr_groups;
                last = perf_evlist__last(evlist);
@@ -1251,6 +1512,12 @@ foreach_evsel_in_last_glob(struct perf_evlist *evlist,
        struct perf_evsel *last = NULL;
        int err;
 
+       /*
+        * Don't return when list_empty, give func a chance to report
+        * error when it found last == NULL.
+        *
+        * So no need to WARN here, let *func do this.
+        */
        if (evlist->nr_entries > 0)
                last = perf_evlist__last(evlist);
 
@@ -1419,7 +1686,7 @@ restart:
                printf("  %-50s [%s]\n", evt_list[evt_i++],
                                event_type_descriptors[PERF_TYPE_TRACEPOINT]);
        }
-       if (evt_num)
+       if (evt_num && pager_in_use())
                printf("\n");
 
 out_free:
@@ -1575,7 +1842,7 @@ restart:
                printf("  %-50s [%s]\n", evt_list[evt_i++],
                                event_type_descriptors[PERF_TYPE_HW_CACHE]);
        }
-       if (evt_num)
+       if (evt_num && pager_in_use())
                printf("\n");
 
 out_free:
@@ -1648,7 +1915,7 @@ restart:
                }
                printf("  %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[type]);
        }
-       if (evt_num)
+       if (evt_num && pager_in_use())
                printf("\n");
 
 out_free:
@@ -1689,13 +1956,14 @@ void print_events(const char *event_glob, bool name_only)
                printf("  %-50s [%s]\n",
                       "cpu/t1=v1[,t2=v2,t3 ...]/modifier",
                       event_type_descriptors[PERF_TYPE_RAW]);
-               printf("   (see 'man perf-list' on how to encode it)\n");
-               printf("\n");
+               if (pager_in_use())
+                       printf("   (see 'man perf-list' on how to encode it)\n\n");
 
                printf("  %-50s [%s]\n",
                       "mem:<addr>[/len][:access]",
                        event_type_descriptors[PERF_TYPE_BREAKPOINT]);
-               printf("\n");
+               if (pager_in_use())
+                       printf("\n");
        }
 
        print_tracepoint_events(NULL, NULL, name_only);
@@ -1811,3 +2079,29 @@ void parse_events_evlist_error(struct parse_events_evlist *data,
        err->str = strdup(str);
        WARN_ONCE(!err->str, "WARNING: failed to allocate error string");
 }
+
+/*
+ * Return string contains valid config terms of an event.
+ * @additional_terms: For terms such as PMU sysfs terms.
+ */
+char *parse_events_formats_error_string(char *additional_terms)
+{
+       char *str;
+       static const char *static_terms = "config,config1,config2,name,"
+                                         "period,freq,branch_type,time,"
+                                         "call-graph,stack-size\n";
+
+       /* valid terms */
+       if (additional_terms) {
+               if (!asprintf(&str, "valid terms: %s,%s",
+                             additional_terms, static_terms))
+                       goto fail;
+       } else {
+               if (!asprintf(&str, "valid terms: %s", static_terms))
+                       goto fail;
+       }
+       return str;
+
+fail:
+       return NULL;
+}
index a09b0e2109973395b5375832ec99a65c707ac630..f1a6db107241b1c8ffaf03a3514ba1549df6fd1b 100644 (file)
@@ -67,6 +67,8 @@ enum {
        PARSE_EVENTS__TERM_TYPE_TIME,
        PARSE_EVENTS__TERM_TYPE_CALLGRAPH,
        PARSE_EVENTS__TERM_TYPE_STACKSIZE,
+       PARSE_EVENTS__TERM_TYPE_NOINHERIT,
+       PARSE_EVENTS__TERM_TYPE_INHERIT
 };
 
 struct parse_events_term {
@@ -118,7 +120,18 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add);
 int parse_events__modifier_group(struct list_head *list, char *event_mod);
 int parse_events_name(struct list_head *list, char *name);
 int parse_events_add_tracepoint(struct list_head *list, int *idx,
-                               char *sys, char *event);
+                               char *sys, char *event,
+                               struct parse_events_error *error,
+                               struct list_head *head_config);
+int parse_events_load_bpf(struct parse_events_evlist *data,
+                         struct list_head *list,
+                         char *bpf_file_name,
+                         bool source);
+/* Provide this function for perf test */
+struct bpf_object;
+int parse_events_load_bpf_obj(struct parse_events_evlist *data,
+                             struct list_head *list,
+                             struct bpf_object *obj);
 int parse_events_add_numeric(struct parse_events_evlist *data,
                             struct list_head *list,
                             u32 type, u64 config,
@@ -155,5 +168,6 @@ int print_hwcache_events(const char *event_glob, bool name_only);
 extern int is_valid_tracepoint(const char *event_string);
 
 int valid_event_mount(const char *eventfs);
+char *parse_events_formats_error_string(char *additional_terms);
 
 #endif /* __PERF_PARSE_EVENTS_H */
index 936d566f48d8df39062848fbca9630eadbee3ca0..58c5831ffd5c22133f48a4c1a3a07721c71362fa 100644 (file)
@@ -115,6 +115,8 @@ do {                                                        \
 group          [^,{}/]*[{][^}]*[}][^,{}/]*
 event_pmu      [^,{}/]+[/][^/]*[/][^,{}/]*
 event          [^,{}/]+
+bpf_object     .*\.(o|bpf)
+bpf_source     .*\.c
 
 num_dec                [0-9]+
 num_hex                0x[a-fA-F0-9]+
@@ -122,7 +124,7 @@ num_raw_hex [a-fA-F0-9]+
 name           [a-zA-Z_*?][a-zA-Z0-9_*?.]*
 name_minus     [a-zA-Z_*?][a-zA-Z0-9\-_*?.]*
 /* If you add a modifier you need to update check_modifier() */
-modifier_event [ukhpGHSDI]+
+modifier_event [ukhpPGHSDI]+
 modifier_bp    [rwx]{1,3}
 
 %%
@@ -159,6 +161,8 @@ modifier_bp [rwx]{1,3}
                }
 
 {event_pmu}    |
+{bpf_object}   |
+{bpf_source}   |
 {event}                {
                        BEGIN(INITIAL);
                        REWIND(1);
@@ -174,7 +178,7 @@ modifier_bp [rwx]{1,3}
 
 <config>{
        /*
-        * Please update formats_error_string any time
+        * Please update parse_events_formats_error_string any time
         * new static term is added.
         */
 config                 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); }
@@ -187,6 +191,8 @@ branch_type         { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE
 time                   { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_TIME); }
 call-graph             { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CALLGRAPH); }
 stack-size             { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_STACKSIZE); }
+inherit                        { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_INHERIT); }
+no-inherit             { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); }
 ,                      { return ','; }
 "/"                    { BEGIN(INITIAL); return '/'; }
 {name_minus}           { return str(yyscanner, PE_NAME); }
@@ -264,6 +270,8 @@ r{num_raw_hex}              { return raw(yyscanner); }
 {num_hex}              { return value(yyscanner, 16); }
 
 {modifier_event}       { return str(yyscanner, PE_MODIFIER_EVENT); }
+{bpf_object}           { return str(yyscanner, PE_BPF_OBJECT); }
+{bpf_source}           { return str(yyscanner, PE_BPF_SOURCE); }
 {name}                 { return pmu_str_check(yyscanner); }
 "/"                    { BEGIN(config); return '/'; }
 -                      { return '-'; }
index 9cd70819c7950e2de1aaae29a17b0f951b593bda..ad379968d4c10c0fb7bb2ddc3bacce3dc1166f43 100644 (file)
@@ -42,6 +42,7 @@ static inc_group_count(struct list_head *list,
 %token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
 %token PE_EVENT_NAME
 %token PE_NAME
+%token PE_BPF_OBJECT PE_BPF_SOURCE
 %token PE_MODIFIER_EVENT PE_MODIFIER_BP
 %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
 %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
@@ -53,6 +54,8 @@ static inc_group_count(struct list_head *list,
 %type <num> PE_RAW
 %type <num> PE_TERM
 %type <str> PE_NAME
+%type <str> PE_BPF_OBJECT
+%type <str> PE_BPF_SOURCE
 %type <str> PE_NAME_CACHE_TYPE
 %type <str> PE_NAME_CACHE_OP_RESULT
 %type <str> PE_MODIFIER_EVENT
@@ -67,8 +70,10 @@ static inc_group_count(struct list_head *list,
 %type <head> event_legacy_cache
 %type <head> event_legacy_mem
 %type <head> event_legacy_tracepoint
+%type <tracepoint_name> tracepoint_name
 %type <head> event_legacy_numeric
 %type <head> event_legacy_raw
+%type <head> event_bpf_file
 %type <head> event_def
 %type <head> event_mod
 %type <head> event_name
@@ -84,6 +89,10 @@ static inc_group_count(struct list_head *list,
        u64 num;
        struct list_head *head;
        struct parse_events_term *term;
+       struct tracepoint_name {
+               char *sys;
+               char *event;
+       } tracepoint_name;
 }
 %%
 
@@ -198,7 +207,8 @@ event_def: event_pmu |
           event_legacy_mem |
           event_legacy_tracepoint sep_dc |
           event_legacy_numeric sep_dc |
-          event_legacy_raw sep_dc
+          event_legacy_raw sep_dc |
+          event_bpf_file
 
 event_pmu:
 PE_NAME '/' event_config '/'
@@ -368,36 +378,60 @@ PE_PREFIX_MEM PE_VALUE sep_dc
 }
 
 event_legacy_tracepoint:
-PE_NAME '-' PE_NAME ':' PE_NAME
+tracepoint_name
 {
        struct parse_events_evlist *data = _data;
+       struct parse_events_error *error = data->error;
        struct list_head *list;
-       char sys_name[128];
-       snprintf(&sys_name, 128, "%s-%s", $1, $3);
 
        ALLOC_LIST(list);
-       ABORT_ON(parse_events_add_tracepoint(list, &data->idx, &sys_name, $5));
+       if (error)
+               error->idx = @1.first_column;
+
+       if (parse_events_add_tracepoint(list, &data->idx, $1.sys, $1.event,
+                                       error, NULL))
+               return -1;
+
        $$ = list;
 }
 |
-PE_NAME ':' PE_NAME
+tracepoint_name '/' event_config '/'
 {
        struct parse_events_evlist *data = _data;
+       struct parse_events_error *error = data->error;
        struct list_head *list;
 
        ALLOC_LIST(list);
-       if (parse_events_add_tracepoint(list, &data->idx, $1, $3)) {
-               struct parse_events_error *error = data->error;
+       if (error)
+               error->idx = @1.first_column;
 
-               if (error) {
-                       error->idx = @1.first_column;
-                       error->str = strdup("unknown tracepoint");
-               }
+       if (parse_events_add_tracepoint(list, &data->idx, $1.sys, $1.event,
+                                       error, $3))
                return -1;
-       }
+
        $$ = list;
 }
 
+tracepoint_name:
+PE_NAME '-' PE_NAME ':' PE_NAME
+{
+       char sys_name[128];
+       struct tracepoint_name tracepoint;
+
+       snprintf(&sys_name, 128, "%s-%s", $1, $3);
+       tracepoint.sys = &sys_name;
+       tracepoint.event = $5;
+
+       $$ = tracepoint;
+}
+|
+PE_NAME ':' PE_NAME
+{
+       struct tracepoint_name tracepoint = {$1, $3};
+
+       $$ = tracepoint;
+}
+
 event_legacy_numeric:
 PE_VALUE ':' PE_VALUE
 {
@@ -420,6 +454,28 @@ PE_RAW
        $$ = list;
 }
 
+event_bpf_file:
+PE_BPF_OBJECT
+{
+       struct parse_events_evlist *data = _data;
+       struct parse_events_error *error = data->error;
+       struct list_head *list;
+
+       ALLOC_LIST(list);
+       ABORT_ON(parse_events_load_bpf(data, list, $1, false));
+       $$ = list;
+}
+|
+PE_BPF_SOURCE
+{
+       struct parse_events_evlist *data = _data;
+       struct list_head *list;
+
+       ALLOC_LIST(list);
+       ABORT_ON(parse_events_load_bpf(data, list, $1, true));
+       $$ = list;
+}
+
 start_terms: event_config
 {
        struct parse_events_terms *data = _data;
index 01626be2a8eb8043135591fcca302213af501e0d..9fca09296eb3472ba2fbe06af0ac1a3f4a1f829b 100644 (file)
@@ -2,10 +2,13 @@
 #include "parse-options.h"
 #include "cache.h"
 #include "header.h"
+#include <linux/string.h>
 
 #define OPT_SHORT 1
 #define OPT_UNSET 2
 
+static struct strbuf error_buf = STRBUF_INIT;
+
 static int opterror(const struct option *opt, const char *reason, int flags)
 {
        if (flags & OPT_SHORT)
@@ -372,7 +375,8 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
 }
 
 static int usage_with_options_internal(const char * const *,
-                                      const struct option *, int);
+                                      const struct option *, int,
+                                      struct parse_opt_ctx_t *);
 
 int parse_options_step(struct parse_opt_ctx_t *ctx,
                       const struct option *options,
@@ -396,8 +400,9 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 
                if (arg[1] != '-') {
                        ctx->opt = ++arg;
-                       if (internal_help && *ctx->opt == 'h')
-                               return usage_with_options_internal(usagestr, options, 0);
+                       if (internal_help && *ctx->opt == 'h') {
+                               return usage_with_options_internal(usagestr, options, 0, ctx);
+                       }
                        switch (parse_short_opt(ctx, options)) {
                        case -1:
                                return parse_options_usage(usagestr, options, arg, 1);
@@ -412,7 +417,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                                check_typos(arg, options);
                        while (ctx->opt) {
                                if (internal_help && *ctx->opt == 'h')
-                                       return usage_with_options_internal(usagestr, options, 0);
+                                       return usage_with_options_internal(usagestr, options, 0, ctx);
                                arg = ctx->opt;
                                switch (parse_short_opt(ctx, options)) {
                                case -1:
@@ -445,9 +450,9 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 
                arg += 2;
                if (internal_help && !strcmp(arg, "help-all"))
-                       return usage_with_options_internal(usagestr, options, 1);
+                       return usage_with_options_internal(usagestr, options, 1, ctx);
                if (internal_help && !strcmp(arg, "help"))
-                       return usage_with_options_internal(usagestr, options, 0);
+                       return usage_with_options_internal(usagestr, options, 0, ctx);
                if (!strcmp(arg, "list-opts"))
                        return PARSE_OPT_LIST_OPTS;
                if (!strcmp(arg, "list-cmds"))
@@ -496,7 +501,7 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
 {
        struct parse_opt_ctx_t ctx;
 
-       perf_header__set_cmdline(argc, argv);
+       perf_env__set_cmdline(&perf_env, argc, argv);
 
        /* build usage string if it's not provided */
        if (subcommands && !usagestr[0]) {
@@ -537,9 +542,11 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
                exit(130);
        default: /* PARSE_OPT_UNKNOWN */
                if (ctx.argv[0][1] == '-') {
-                       error("unknown option `%s'", ctx.argv[0] + 2);
+                       strbuf_addf(&error_buf, "unknown option `%s'",
+                                   ctx.argv[0] + 2);
                } else {
-                       error("unknown switch `%c'", *ctx.opt);
+                       strbuf_addf(&error_buf, "unknown switch `%c'",
+                                   *ctx.opt);
                }
                usage_with_options(usagestr, options);
        }
@@ -642,13 +649,93 @@ static void print_option_help(const struct option *opts, int full)
        fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
 }
 
+static int option__cmp(const void *va, const void *vb)
+{
+       const struct option *a = va, *b = vb;
+       int sa = tolower(a->short_name), sb = tolower(b->short_name), ret;
+
+       if (sa == 0)
+               sa = 'z' + 1;
+       if (sb == 0)
+               sb = 'z' + 1;
+
+       ret = sa - sb;
+
+       if (ret == 0) {
+               const char *la = a->long_name ?: "",
+                          *lb = b->long_name ?: "";
+               ret = strcmp(la, lb);
+       }
+
+       return ret;
+}
+
+static struct option *options__order(const struct option *opts)
+{
+       int nr_opts = 0;
+       const struct option *o = opts;
+       struct option *ordered;
+
+       for (o = opts; o->type != OPTION_END; o++)
+               ++nr_opts;
+
+       ordered = memdup(opts, sizeof(*o) * (nr_opts + 1));
+       if (ordered == NULL)
+               goto out;
+
+       qsort(ordered, nr_opts, sizeof(*o), option__cmp);
+out:
+       return ordered;
+}
+
+static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx)
+{
+       int i;
+
+       for (i = 1; i < ctx->argc; ++i) {
+               const char *arg = ctx->argv[i];
+
+               if (arg[0] != '-') {
+                       if (arg[1] == '\0') {
+                               if (arg[0] == opt->short_name)
+                                       return true;
+                               continue;
+                       }
+
+                       if (opt->long_name && strcmp(opt->long_name, arg) == 0)
+                               return true;
+
+                       if (opt->help && strcasestr(opt->help, arg) != NULL)
+                               return true;
+
+                       continue;
+               }
+
+               if (arg[1] == opt->short_name ||
+                   (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0))
+                       return true;
+       }
+
+       return false;
+}
+
 int usage_with_options_internal(const char * const *usagestr,
-                               const struct option *opts, int full)
+                               const struct option *opts, int full,
+                               struct parse_opt_ctx_t *ctx)
 {
+       struct option *ordered;
+
        if (!usagestr)
                return PARSE_OPT_HELP;
 
-       fprintf(stderr, "\n usage: %s\n", *usagestr++);
+       setup_pager();
+
+       if (strbuf_avail(&error_buf)) {
+               fprintf(stderr, "  Error: %s\n", error_buf.buf);
+               strbuf_release(&error_buf);
+       }
+
+       fprintf(stderr, "\n Usage: %s\n", *usagestr++);
        while (*usagestr && **usagestr)
                fprintf(stderr, "    or: %s\n", *usagestr++);
        while (*usagestr) {
@@ -661,11 +748,20 @@ int usage_with_options_internal(const char * const *usagestr,
        if (opts->type != OPTION_GROUP)
                fputc('\n', stderr);
 
-       for (  ; opts->type != OPTION_END; opts++)
+       ordered = options__order(opts);
+       if (ordered)
+               opts = ordered;
+
+       for (  ; opts->type != OPTION_END; opts++) {
+               if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx))
+                       continue;
                print_option_help(opts, full);
+       }
 
        fputc('\n', stderr);
 
+       free(ordered);
+
        return PARSE_OPT_HELP;
 }
 
@@ -673,7 +769,22 @@ void usage_with_options(const char * const *usagestr,
                        const struct option *opts)
 {
        exit_browser(false);
-       usage_with_options_internal(usagestr, opts, 0);
+       usage_with_options_internal(usagestr, opts, 0, NULL);
+       exit(129);
+}
+
+void usage_with_options_msg(const char * const *usagestr,
+                           const struct option *opts, const char *fmt, ...)
+{
+       va_list ap;
+
+       exit_browser(false);
+
+       va_start(ap, fmt);
+       strbuf_addv(&error_buf, fmt, ap);
+       va_end(ap);
+
+       usage_with_options_internal(usagestr, opts, 0, NULL);
        exit(129);
 }
 
@@ -684,7 +795,7 @@ int parse_options_usage(const char * const *usagestr,
        if (!usagestr)
                goto opt;
 
-       fprintf(stderr, "\n usage: %s\n", *usagestr++);
+       fprintf(stderr, "\n Usage: %s\n", *usagestr++);
        while (*usagestr && **usagestr)
                fprintf(stderr, "    or: %s\n", *usagestr++);
        while (*usagestr) {
@@ -698,24 +809,23 @@ int parse_options_usage(const char * const *usagestr,
 opt:
        for (  ; opts->type != OPTION_END; opts++) {
                if (short_opt) {
-                       if (opts->short_name == *optstr)
+                       if (opts->short_name == *optstr) {
+                               print_option_help(opts, 0);
                                break;
+                       }
                        continue;
                }
 
                if (opts->long_name == NULL)
                        continue;
 
-               if (!prefixcmp(optstr, opts->long_name))
-                       break;
-               if (!prefixcmp(optstr, "no-") &&
-                   !prefixcmp(optstr + 3, opts->long_name))
-                       break;
+               if (!prefixcmp(opts->long_name, optstr))
+                       print_option_help(opts, 0);
+               if (!prefixcmp("no-", optstr) &&
+                   !prefixcmp(opts->long_name, optstr + 3))
+                       print_option_help(opts, 0);
        }
 
-       if (opts->type != OPTION_END)
-               print_option_help(opts, 0);
-
        return PARSE_OPT_HELP;
 }
 
index 367d8b816cc7e7ae0a99bb8200e0bd81b7999546..a8e407bc251e418df2e04623bec08c9de9f5a6cb 100644 (file)
@@ -111,6 +111,7 @@ struct option {
 #define OPT_GROUP(h)                { .type = OPTION_GROUP, .help = (h) }
 #define OPT_BIT(s, l, v, h, b)      { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }
 #define OPT_BOOLEAN(s, l, v, h)     { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) }
+#define OPT_BOOLEAN_FLAG(s, l, v, h, f)     { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h), .flags = (f) }
 #define OPT_BOOLEAN_SET(s, l, v, os, h) \
        { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \
        .value = check_vtype(v, bool *), .help = (h), \
@@ -160,6 +161,10 @@ extern int parse_options_subcommand(int argc, const char **argv,
 
 extern NORETURN void usage_with_options(const char * const *usagestr,
                                         const struct option *options);
+extern NORETURN __attribute__((format(printf,3,4)))
+void usage_with_options_msg(const char * const *usagestr,
+                           const struct option *options,
+                           const char *fmt, ...);
 
 /*----- incremantal advanced APIs -----*/
 
index 89c91a1a67e760fc5b82198179f9ecdc4b337df5..e4b173dec4b9978cb3d1eb53c3569277c5110714 100644 (file)
@@ -626,38 +626,26 @@ static int pmu_resolve_param_term(struct parse_events_term *term,
        return -1;
 }
 
-static char *formats_error_string(struct list_head *formats)
+static char *pmu_formats_string(struct list_head *formats)
 {
        struct perf_pmu_format *format;
-       char *err, *str;
-       static const char *static_terms = "config,config1,config2,name,"
-                                         "period,freq,branch_type,time,"
-                                         "call-graph,stack-size\n";
+       char *str;
+       struct strbuf buf;
        unsigned i = 0;
 
-       if (!asprintf(&str, "valid terms:"))
+       if (!formats)
                return NULL;
 
+       strbuf_init(&buf, 0);
        /* sysfs exported terms */
-       list_for_each_entry(format, formats, list) {
-               char c = i++ ? ',' : ' ';
-
-               err = str;
-               if (!asprintf(&str, "%s%c%s", err, c, format->name))
-                       goto fail;
-               free(err);
-       }
+       list_for_each_entry(format, formats, list)
+               strbuf_addf(&buf, i++ ? ",%s" : "%s",
+                           format->name);
 
-       /* static terms */
-       err = str;
-       if (!asprintf(&str, "%s,%s", err, static_terms))
-               goto fail;
+       str = strbuf_detach(&buf, NULL);
+       strbuf_release(&buf);
 
-       free(err);
        return str;
-fail:
-       free(err);
-       return NULL;
 }
 
 /*
@@ -693,9 +681,12 @@ static int pmu_config_term(struct list_head *formats,
                if (verbose)
                        printf("Invalid event/parameter '%s'\n", term->config);
                if (err) {
+                       char *pmu_term = pmu_formats_string(formats);
+
                        err->idx  = term->err_term;
                        err->str  = strdup("unknown term");
-                       err->help = formats_error_string(formats);
+                       err->help = parse_events_formats_error_string(pmu_term);
+                       free(pmu_term);
                }
                return -EINVAL;
        }
@@ -1017,7 +1008,8 @@ void print_pmu_events(const char *event_glob, bool name_only)
                                goto out_enomem;
                        j++;
                }
-               if (pmu->selectable) {
+               if (pmu->selectable &&
+                   (event_glob == NULL || strglobmatch(pmu->name, event_glob))) {
                        char *s;
                        if (asprintf(&s, "%s//", pmu->name) < 0)
                                goto out_enomem;
@@ -1035,7 +1027,7 @@ void print_pmu_events(const char *event_glob, bool name_only)
                printf("  %-50s [Kernel PMU event]\n", aliases[j]);
                printed++;
        }
-       if (printed)
+       if (printed && pager_in_use())
                printf("\n");
 out_free:
        for (j = 0; j < len; j++)
index c6f9af78f6f5f9651c6339bd3140221b9b951b6d..b51a8bfb40f92cd861d0954ed84e71e15ee50784 100644 (file)
@@ -40,8 +40,7 @@
 #include "color.h"
 #include "symbol.h"
 #include "thread.h"
-#include <api/fs/debugfs.h>
-#include <api/fs/tracefs.h>
+#include <api/fs/fs.h>
 #include "trace-event.h"       /* For __maybe_unused */
 #include "probe-event.h"
 #include "probe-finder.h"
@@ -72,7 +71,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
 static struct machine *host_machine;
 
 /* Initialize symbol maps and path of vmlinux/modules */
-static int init_symbol_maps(bool user_only)
+int init_probe_symbol_maps(bool user_only)
 {
        int ret;
 
@@ -102,7 +101,7 @@ out:
        return ret;
 }
 
-static void exit_symbol_maps(void)
+void exit_probe_symbol_maps(void)
 {
        if (host_machine) {
                machine__delete(host_machine);
@@ -127,17 +126,19 @@ static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void)
 {
        /* kmap->ref_reloc_sym should be set if host_machine is initialized */
        struct kmap *kmap;
+       struct map *map = machine__kernel_map(host_machine);
 
-       if (map__load(host_machine->vmlinux_maps[MAP__FUNCTION], NULL) < 0)
+       if (map__load(map, NULL) < 0)
                return NULL;
 
-       kmap = map__kmap(host_machine->vmlinux_maps[MAP__FUNCTION]);
+       kmap = map__kmap(map);
        if (!kmap)
                return NULL;
        return kmap->ref_reloc_sym;
 }
 
-static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc)
+static int kernel_get_symbol_address_by_name(const char *name, u64 *addr,
+                                            bool reloc, bool reladdr)
 {
        struct ref_reloc_sym *reloc_sym;
        struct symbol *sym;
@@ -146,12 +147,14 @@ static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc)
        /* ref_reloc_sym is just a label. Need a special fix*/
        reloc_sym = kernel_get_ref_reloc_sym();
        if (reloc_sym && strcmp(name, reloc_sym->name) == 0)
-               return (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr;
+               *addr = (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr;
        else {
                sym = __find_kernel_function_by_name(name, &map);
-               if (sym)
-                       return map->unmap_ip(map, sym->start) -
-                               ((reloc) ? 0 : map->reloc);
+               if (!sym)
+                       return -ENOENT;
+               *addr = map->unmap_ip(map, sym->start) -
+                       ((reloc) ? 0 : map->reloc) -
+                       ((reladdr) ? map->start : 0);
        }
        return 0;
 }
@@ -245,12 +248,14 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
 static bool kprobe_blacklist__listed(unsigned long address);
 static bool kprobe_warn_out_range(const char *symbol, unsigned long address)
 {
-       u64 etext_addr;
+       u64 etext_addr = 0;
+       int ret;
 
        /* Get the address of _etext for checking non-probable text symbol */
-       etext_addr = kernel_get_symbol_address_by_name("_etext", false);
+       ret = kernel_get_symbol_address_by_name("_etext", &etext_addr,
+                                               false, false);
 
-       if (etext_addr != 0 && etext_addr < address)
+       if (ret == 0 && etext_addr < address)
                pr_warning("%s is out of .text, skip it.\n", symbol);
        else if (kprobe_blacklist__listed(address))
                pr_warning("%s is blacklisted function, skip it.\n", symbol);
@@ -282,7 +287,7 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso)
                return -ENOENT;
        }
 
-       map = host_machine->vmlinux_maps[MAP__FUNCTION];
+       map = machine__kernel_map(host_machine);
        dso = map->dso;
 
        vmlinux_name = symbol_conf.vmlinux_name;
@@ -436,19 +441,22 @@ static char *debuginfo_cache_path;
 
 static struct debuginfo *debuginfo_cache__open(const char *module, bool silent)
 {
-       if ((debuginfo_cache_path && !strcmp(debuginfo_cache_path, module)) ||
-           (!debuginfo_cache_path && !module && debuginfo_cache))
+       const char *path = module;
+
+       /* If the module is NULL, it should be the kernel. */
+       if (!module)
+               path = "kernel";
+
+       if (debuginfo_cache_path && !strcmp(debuginfo_cache_path, path))
                goto out;
 
        /* Copy module path */
        free(debuginfo_cache_path);
-       if (module) {
-               debuginfo_cache_path = strdup(module);
-               if (!debuginfo_cache_path) {
-                       debuginfo__delete(debuginfo_cache);
-                       debuginfo_cache = NULL;
-                       goto out;
-               }
+       debuginfo_cache_path = strdup(path);
+       if (!debuginfo_cache_path) {
+               debuginfo__delete(debuginfo_cache);
+               debuginfo_cache = NULL;
+               goto out;
        }
 
        debuginfo_cache = open_debuginfo(module, silent);
@@ -517,8 +525,10 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
                        goto error;
                addr += stext;
        } else if (tp->symbol) {
-               addr = kernel_get_symbol_address_by_name(tp->symbol, false);
-               if (addr == 0)
+               /* If the module is given, this returns relative address */
+               ret = kernel_get_symbol_address_by_name(tp->symbol, &addr,
+                                                       false, !!tp->module);
+               if (ret != 0)
                        goto error;
                addr += tp->offset;
        }
@@ -861,11 +871,11 @@ int show_line_range(struct line_range *lr, const char *module, bool user)
 {
        int ret;
 
-       ret = init_symbol_maps(user);
+       ret = init_probe_symbol_maps(user);
        if (ret < 0)
                return ret;
        ret = __show_line_range(lr, module, user);
-       exit_symbol_maps();
+       exit_probe_symbol_maps();
 
        return ret;
 }
@@ -943,7 +953,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
        int i, ret = 0;
        struct debuginfo *dinfo;
 
-       ret = init_symbol_maps(pevs->uprobes);
+       ret = init_probe_symbol_maps(pevs->uprobes);
        if (ret < 0)
                return ret;
 
@@ -960,7 +970,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
 
        debuginfo__delete(dinfo);
 out:
-       exit_symbol_maps();
+       exit_probe_symbol_maps();
        return ret;
 }
 
@@ -1884,8 +1894,12 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
                        goto out;
                sym = map__find_symbol(map, addr, NULL);
        } else {
-               if (tp->symbol)
-                       addr = kernel_get_symbol_address_by_name(tp->symbol, true);
+               if (tp->symbol && !addr) {
+                       ret = kernel_get_symbol_address_by_name(tp->symbol,
+                                                       &addr, true, false);
+                       if (ret < 0)
+                               goto out;
+               }
                if (addr) {
                        addr += tp->offset;
                        sym = __find_kernel_function(addr, &map);
@@ -2055,7 +2069,7 @@ static void kprobe_blacklist__delete(struct list_head *blacklist)
 static int kprobe_blacklist__load(struct list_head *blacklist)
 {
        struct kprobe_blacklist_node *node;
-       const char *__debugfs = debugfs_find_mountpoint();
+       const char *__debugfs = debugfs__mountpoint();
        char buf[PATH_MAX], *p;
        FILE *fp;
        int ret;
@@ -2181,9 +2195,9 @@ out:
 }
 
 /* Show an event */
-static int show_perf_probe_event(const char *group, const char *event,
-                                struct perf_probe_event *pev,
-                                const char *module, bool use_stdout)
+int show_perf_probe_event(const char *group, const char *event,
+                         struct perf_probe_event *pev,
+                         const char *module, bool use_stdout)
 {
        struct strbuf buf = STRBUF_INIT;
        int ret;
@@ -2264,7 +2278,7 @@ int show_perf_probe_events(struct strfilter *filter)
 
        setup_pager();
 
-       ret = init_symbol_maps(false);
+       ret = init_probe_symbol_maps(false);
        if (ret < 0)
                return ret;
 
@@ -2280,7 +2294,7 @@ int show_perf_probe_events(struct strfilter *filter)
                close(kp_fd);
        if (up_fd > 0)
                close(up_fd);
-       exit_symbol_maps();
+       exit_probe_symbol_maps();
 
        return ret;
 }
@@ -2289,36 +2303,41 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
                              struct strlist *namelist, bool allow_suffix)
 {
        int i, ret;
-       char *p;
+       char *p, *nbase;
 
        if (*base == '.')
                base++;
+       nbase = strdup(base);
+       if (!nbase)
+               return -ENOMEM;
+
+       /* Cut off the dot suffixes (e.g. .const, .isra)*/
+       p = strchr(nbase, '.');
+       if (p && p != nbase)
+               *p = '\0';
 
-       /* Try no suffix */
-       ret = e_snprintf(buf, len, "%s", base);
+       /* Try no suffix number */
+       ret = e_snprintf(buf, len, "%s", nbase);
        if (ret < 0) {
                pr_debug("snprintf() failed: %d\n", ret);
-               return ret;
+               goto out;
        }
-       /* Cut off the postfixes (e.g. .const, .isra)*/
-       p = strchr(buf, '.');
-       if (p && p != buf)
-               *p = '\0';
        if (!strlist__has_entry(namelist, buf))
-               return 0;
+               goto out;
 
        if (!allow_suffix) {
                pr_warning("Error: event \"%s\" already exists. "
-                          "(Use -f to force duplicates.)\n", base);
-               return -EEXIST;
+                          "(Use -f to force duplicates.)\n", buf);
+               ret = -EEXIST;
+               goto out;
        }
 
        /* Try to add suffix */
        for (i = 1; i < MAX_EVENT_INDEX; i++) {
-               ret = e_snprintf(buf, len, "%s_%d", base, i);
+               ret = e_snprintf(buf, len, "%s_%d", nbase, i);
                if (ret < 0) {
                        pr_debug("snprintf() failed: %d\n", ret);
-                       return ret;
+                       goto out;
                }
                if (!strlist__has_entry(namelist, buf))
                        break;
@@ -2328,6 +2347,8 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
                ret = -ERANGE;
        }
 
+out:
+       free(nbase);
        return ret;
 }
 
@@ -2400,7 +2421,6 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 {
        int i, fd, ret;
        struct probe_trace_event *tev = NULL;
-       const char *event = NULL, *group = NULL;
        struct strlist *namelist;
 
        fd = probe_file__open(PF_FL_RW | (pev->uprobes ? PF_FL_UPROBE : 0));
@@ -2416,7 +2436,6 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
        }
 
        ret = 0;
-       pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
        for (i = 0; i < ntevs; i++) {
                tev = &tevs[i];
                /* Skip if the symbol is out of .text or blacklisted */
@@ -2433,13 +2452,6 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
                if (ret < 0)
                        break;
 
-               /* We use tev's name for showing new events */
-               show_perf_probe_event(tev->group, tev->event, pev,
-                                     tev->point.module, false);
-               /* Save the last valid name */
-               event = tev->event;
-               group = tev->group;
-
                /*
                 * Probes after the first probe which comes from same
                 * user input are always allowed to add suffix, because
@@ -2451,13 +2463,6 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
        if (ret == -EINVAL && pev->uprobes)
                warn_uprobe_event_compat(tev);
 
-       /* Note that it is possible to skip all events because of blacklist */
-       if (ret >= 0 && event) {
-               /* Show how to use the event. */
-               pr_info("\nYou can now use it in all perf tools, such as:\n\n");
-               pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", group, event);
-       }
-
        strlist__delete(namelist);
 close_out:
        close(fd);
@@ -2538,7 +2543,8 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
                goto out;
        }
 
-       if (!pev->uprobes && !pp->retprobe) {
+       /* Note that the symbols in the kmodule are not relocated */
+       if (!pev->uprobes && !pp->retprobe && !pev->target) {
                reloc_sym = kernel_get_ref_reloc_sym();
                if (!reloc_sym) {
                        pr_warning("Relocated base symbol is not found!\n");
@@ -2575,8 +2581,9 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
                }
                /* Add one probe point */
                tp->address = map->unmap_ip(map, sym->start) + pp->offset;
-               /* If we found a wrong one, mark it by NULL symbol */
-               if (!pev->uprobes &&
+
+               /* Check the kprobe (not in module) is within .text  */
+               if (!pev->uprobes && !pev->target &&
                    kprobe_warn_out_range(sym->name, tp->address)) {
                        tp->symbol = NULL;      /* Skip it */
                        skipped++;
@@ -2760,63 +2767,71 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
        return find_probe_trace_events_from_map(pev, tevs);
 }
 
-struct __event_package {
-       struct perf_probe_event         *pev;
-       struct probe_trace_event        *tevs;
-       int                             ntevs;
-};
-
-int add_perf_probe_events(struct perf_probe_event *pevs, int npevs)
+int convert_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 {
-       int i, j, ret;
-       struct __event_package *pkgs;
-
-       ret = 0;
-       pkgs = zalloc(sizeof(struct __event_package) * npevs);
-
-       if (pkgs == NULL)
-               return -ENOMEM;
-
-       ret = init_symbol_maps(pevs->uprobes);
-       if (ret < 0) {
-               free(pkgs);
-               return ret;
-       }
+       int i, ret;
 
        /* Loop 1: convert all events */
        for (i = 0; i < npevs; i++) {
-               pkgs[i].pev = &pevs[i];
                /* Init kprobe blacklist if needed */
-               if (!pkgs[i].pev->uprobes)
+               if (!pevs[i].uprobes)
                        kprobe_blacklist__init();
                /* Convert with or without debuginfo */
-               ret  = convert_to_probe_trace_events(pkgs[i].pev,
-                                                    &pkgs[i].tevs);
+               ret  = convert_to_probe_trace_events(&pevs[i], &pevs[i].tevs);
                if (ret < 0)
-                       goto end;
-               pkgs[i].ntevs = ret;
+                       return ret;
+               pevs[i].ntevs = ret;
        }
        /* This just release blacklist only if allocated */
        kprobe_blacklist__release();
 
+       return 0;
+}
+
+int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs)
+{
+       int i, ret = 0;
+
        /* Loop 2: add all events */
        for (i = 0; i < npevs; i++) {
-               ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
-                                              pkgs[i].ntevs,
+               ret = __add_probe_trace_events(&pevs[i], pevs[i].tevs,
+                                              pevs[i].ntevs,
                                               probe_conf.force_add);
                if (ret < 0)
                        break;
        }
-end:
+       return ret;
+}
+
+void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs)
+{
+       int i, j;
+
        /* Loop 3: cleanup and free trace events  */
        for (i = 0; i < npevs; i++) {
-               for (j = 0; j < pkgs[i].ntevs; j++)
-                       clear_probe_trace_event(&pkgs[i].tevs[j]);
-               zfree(&pkgs[i].tevs);
+               for (j = 0; j < pevs[i].ntevs; j++)
+                       clear_probe_trace_event(&pevs[i].tevs[j]);
+               zfree(&pevs[i].tevs);
+               pevs[i].ntevs = 0;
+               clear_perf_probe_event(&pevs[i]);
        }
-       free(pkgs);
-       exit_symbol_maps();
+}
 
+int add_perf_probe_events(struct perf_probe_event *pevs, int npevs)
+{
+       int ret;
+
+       ret = init_probe_symbol_maps(pevs->uprobes);
+       if (ret < 0)
+               return ret;
+
+       ret = convert_perf_probe_events(pevs, npevs);
+       if (ret == 0)
+               ret = apply_perf_probe_events(pevs, npevs);
+
+       cleanup_perf_probe_events(pevs, npevs);
+
+       exit_probe_symbol_maps();
        return ret;
 }
 
@@ -2828,8 +2843,6 @@ int del_perf_probe_events(struct strfilter *filter)
        if (!str)
                return -EINVAL;
 
-       pr_debug("Delete filter: \'%s\'\n", str);
-
        /* Get current event names */
        ret = probe_file__open_both(&kfd, &ufd, PF_FL_RW);
        if (ret < 0)
@@ -2844,9 +2857,6 @@ int del_perf_probe_events(struct strfilter *filter)
                ret = ret2;
                goto error;
        }
-       if (ret == -ENOENT && ret2 == -ENOENT)
-               pr_debug("\"%s\" does not hit any event.\n", str);
-               /* Note that this is silently ignored */
        ret = 0;
 
 error:
@@ -2881,7 +2891,7 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
        struct map *map;
        int ret;
 
-       ret = init_symbol_maps(user);
+       ret = init_probe_symbol_maps(user);
        if (ret < 0)
                return ret;
 
@@ -2911,7 +2921,7 @@ end:
        if (user) {
                map__put(map);
        }
-       exit_symbol_maps();
+       exit_probe_symbol_maps();
 
        return ret;
 }
index 6e7ec68a4aa84ce0818c52e1b8d2a488c32e4b31..ba926c30f8cdcc3718f75cb51ef57f7f3f5c5c95 100644 (file)
@@ -87,6 +87,8 @@ struct perf_probe_event {
        bool                    uprobes;        /* Uprobe event flag */
        char                    *target;        /* Target binary */
        struct perf_probe_arg   *args;  /* Arguments */
+       struct probe_trace_event *tevs;
+       int                     ntevs;
 };
 
 /* Line range */
@@ -108,6 +110,8 @@ struct variable_list {
 };
 
 struct map;
+int init_probe_symbol_maps(bool user_only);
+void exit_probe_symbol_maps(void);
 
 /* Command string to events */
 extern int parse_perf_probe_command(const char *cmd,
@@ -138,7 +142,14 @@ extern void line_range__clear(struct line_range *lr);
 extern int line_range__init(struct line_range *lr);
 
 extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs);
+extern int convert_perf_probe_events(struct perf_probe_event *pevs, int npevs);
+extern int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs);
+extern void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs);
 extern int del_perf_probe_events(struct strfilter *filter);
+
+extern int show_perf_probe_event(const char *group, const char *event,
+                                struct perf_probe_event *pev,
+                                const char *module, bool use_stdout);
 extern int show_perf_probe_events(struct strfilter *filter);
 extern int show_line_range(struct line_range *lr, const char *module,
                           bool user);
index bbb243717ec834f0548e9d5bf8a3ac0ff48f8c4b..89dbeb92c68e9de1bc001edddd902940700ab4f2 100644 (file)
@@ -22,8 +22,7 @@
 #include "color.h"
 #include "symbol.h"
 #include "thread.h"
-#include <api/fs/debugfs.h>
-#include <api/fs/tracefs.h>
+#include <api/fs/tracing_path.h>
 #include "probe-event.h"
 #include "probe-file.h"
 #include "session.h"
@@ -73,21 +72,11 @@ static void print_both_open_warning(int kerr, int uerr)
 static int open_probe_events(const char *trace_file, bool readwrite)
 {
        char buf[PATH_MAX];
-       const char *__debugfs;
        const char *tracing_dir = "";
        int ret;
 
-       __debugfs = tracefs_find_mountpoint();
-       if (__debugfs == NULL) {
-               tracing_dir = "tracing/";
-
-               __debugfs = debugfs_find_mountpoint();
-               if (__debugfs == NULL)
-                       return -ENOTSUP;
-       }
-
        ret = e_snprintf(buf, PATH_MAX, "%s/%s%s",
-                        __debugfs, tracing_dir, trace_file);
+                        tracing_path, tracing_dir, trace_file);
        if (ret >= 0) {
                pr_debug("Opening %s write=%d\n", buf, readwrite);
                if (readwrite && !probe_event_dry_run)
@@ -267,7 +256,6 @@ static int __del_trace_probe_event(int fd, struct str_node *ent)
                goto error;
        }
 
-       pr_info("Removed event: %s\n", ent->s);
        return 0;
 error:
        pr_warning("Failed to delete event: %s\n",
@@ -275,7 +263,8 @@ error:
        return ret;
 }
 
-int probe_file__del_events(int fd, struct strfilter *filter)
+int probe_file__get_events(int fd, struct strfilter *filter,
+                          struct strlist *plist)
 {
        struct strlist *namelist;
        struct str_node *ent;
@@ -290,12 +279,43 @@ int probe_file__del_events(int fd, struct strfilter *filter)
                p = strchr(ent->s, ':');
                if ((p && strfilter__compare(filter, p + 1)) ||
                    strfilter__compare(filter, ent->s)) {
-                       ret = __del_trace_probe_event(fd, ent);
-                       if (ret < 0)
-                               break;
+                       strlist__add(plist, ent->s);
+                       ret = 0;
                }
        }
        strlist__delete(namelist);
 
        return ret;
 }
+
+int probe_file__del_strlist(int fd, struct strlist *namelist)
+{
+       int ret = 0;
+       struct str_node *ent;
+
+       strlist__for_each(ent, namelist) {
+               ret = __del_trace_probe_event(fd, ent);
+               if (ret < 0)
+                       break;
+       }
+       return ret;
+}
+
+int probe_file__del_events(int fd, struct strfilter *filter)
+{
+       struct strlist *namelist;
+       int ret;
+
+       namelist = strlist__new(NULL, NULL);
+       if (!namelist)
+               return -ENOMEM;
+
+       ret = probe_file__get_events(fd, filter, namelist);
+       if (ret < 0)
+               return ret;
+
+       ret = probe_file__del_strlist(fd, namelist);
+       strlist__delete(namelist);
+
+       return ret;
+}
index ada94a242a17c80edb8f53c82f31145b70993653..18ac9cf51c3433438eb3036d340f34e5975161bc 100644 (file)
@@ -14,5 +14,9 @@ struct strlist *probe_file__get_namelist(int fd);
 struct strlist *probe_file__get_rawlist(int fd);
 int probe_file__add_event(int fd, struct probe_trace_event *tev);
 int probe_file__del_events(int fd, struct strfilter *filter);
+int probe_file__get_events(int fd, struct strfilter *filter,
+                                 struct strlist *plist);
+int probe_file__del_strlist(int fd, struct strlist *namelist);
+
 
 #endif
index 29c43c0680a8fc0dbefbd9d35b5e6488d6807f40..bd8f03de5e408293a642478a795c6436143edbcf 100644 (file)
@@ -70,6 +70,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg,
        if (!dbg->dwfl)
                goto error;
 
+       dwfl_report_begin(dbg->dwfl);
        dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd);
        if (!dbg->mod)
                goto error;
@@ -78,6 +79,8 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg,
        if (!dbg->dbg)
                goto error;
 
+       dwfl_report_end(dbg->dwfl, NULL, NULL);
+
        return 0;
 error:
        if (dbg->dwfl)
@@ -591,6 +594,7 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)
 /* Convert subprogram DIE to trace point */
 static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod,
                                  Dwarf_Addr paddr, bool retprobe,
+                                 const char *function,
                                  struct probe_trace_point *tp)
 {
        Dwarf_Addr eaddr, highaddr;
@@ -634,8 +638,10 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod,
        /* Return probe must be on the head of a subprogram */
        if (retprobe) {
                if (eaddr != paddr) {
-                       pr_warning("Return probe must be on the head of"
-                                  " a real function.\n");
+                       pr_warning("Failed to find \"%s%%return\",\n"
+                                  " because %s is an inlined function and"
+                                  " has no return point.\n", function,
+                                  function);
                        return -EINVAL;
                }
                tp->retprobe = true;
@@ -1175,6 +1181,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
 {
        struct trace_event_finder *tf =
                        container_of(pf, struct trace_event_finder, pf);
+       struct perf_probe_point *pp = &pf->pev->point;
        struct probe_trace_event *tev;
        struct perf_probe_arg *args;
        int ret, i;
@@ -1189,7 +1196,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
 
        /* Trace point should be converted from subprogram DIE */
        ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr,
-                                    pf->pev->point.retprobe, &tev->point);
+                                    pp->retprobe, pp->function, &tev->point);
        if (ret < 0)
                return ret;
 
@@ -1319,6 +1326,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
 {
        struct available_var_finder *af =
                        container_of(pf, struct available_var_finder, pf);
+       struct perf_probe_point *pp = &pf->pev->point;
        struct variable_list *vl;
        Dwarf_Die die_mem;
        int ret;
@@ -1332,7 +1340,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
 
        /* Trace point should be converted from subprogram DIE */
        ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr,
-                                    pf->pev->point.retprobe, &vl->point);
+                                    pp->retprobe, pp->function, &vl->point);
        if (ret < 0)
                return ret;
 
@@ -1399,6 +1407,41 @@ int debuginfo__find_available_vars_at(struct debuginfo *dbg,
        return (ret < 0) ? ret : af.nvls;
 }
 
+/* For the kernel module, we need a special code to get a DIE */
+static int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs)
+{
+       int n, i;
+       Elf32_Word shndx;
+       Elf_Scn *scn;
+       Elf *elf;
+       GElf_Shdr mem, *shdr;
+       const char *p;
+
+       elf = dwfl_module_getelf(dbg->mod, &dbg->bias);
+       if (!elf)
+               return -EINVAL;
+
+       /* Get the number of relocations */
+       n = dwfl_module_relocations(dbg->mod);
+       if (n < 0)
+               return -ENOENT;
+       /* Search the relocation related .text section */
+       for (i = 0; i < n; i++) {
+               p = dwfl_module_relocation_info(dbg->mod, i, &shndx);
+               if (strcmp(p, ".text") == 0) {
+                       /* OK, get the section header */
+                       scn = elf_getscn(elf, shndx);
+                       if (!scn)
+                               return -ENOENT;
+                       shdr = gelf_getshdr(scn, &mem);
+                       if (!shdr)
+                               return -ENOENT;
+                       *offs = shdr->sh_addr;
+               }
+       }
+       return 0;
+}
+
 /* Reverse search */
 int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,
                                struct perf_probe_point *ppt)
@@ -1407,9 +1450,16 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,
        Dwarf_Addr _addr = 0, baseaddr = 0;
        const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp;
        int baseline = 0, lineno = 0, ret = 0;
+       bool reloc = false;
 
+retry:
        /* Find cu die */
        if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) {
+               if (!reloc && debuginfo__get_text_offset(dbg, &baseaddr) == 0) {
+                       addr += baseaddr;
+                       reloc = true;
+                       goto retry;
+               }
                pr_warning("Failed to find debug information for address %lx\n",
                           addr);
                ret = -EINVAL;
index 6324fe6b161e8f45a60fdde0573e76e6fc174298..98f127abfa42a2d9b8d06b24b6c17090781c87fb 100644 (file)
@@ -67,6 +67,7 @@ static char pyrf_mmap_event__doc[] = PyDoc_STR("perf mmap event object.");
 static PyMemberDef pyrf_mmap_event__members[] = {
        sample_members
        member_def(perf_event_header, type, T_UINT, "event type"),
+       member_def(perf_event_header, misc, T_UINT, "event misc"),
        member_def(mmap_event, pid, T_UINT, "event pid"),
        member_def(mmap_event, tid, T_UINT, "event tid"),
        member_def(mmap_event, start, T_ULONGLONG, "start of the map"),
@@ -297,6 +298,43 @@ static PyTypeObject pyrf_sample_event__type = {
        .tp_repr        = (reprfunc)pyrf_sample_event__repr,
 };
 
+static char pyrf_context_switch_event__doc[] = PyDoc_STR("perf context_switch event object.");
+
+static PyMemberDef pyrf_context_switch_event__members[] = {
+       sample_members
+       member_def(perf_event_header, type, T_UINT, "event type"),
+       member_def(context_switch_event, next_prev_pid, T_UINT, "next/prev pid"),
+       member_def(context_switch_event, next_prev_tid, T_UINT, "next/prev tid"),
+       { .name = NULL, },
+};
+
+static PyObject *pyrf_context_switch_event__repr(struct pyrf_event *pevent)
+{
+       PyObject *ret;
+       char *s;
+
+       if (asprintf(&s, "{ type: context_switch, next_prev_pid: %u, next_prev_tid: %u, switch_out: %u }",
+                    pevent->event.context_switch.next_prev_pid,
+                    pevent->event.context_switch.next_prev_tid,
+                    !!(pevent->event.header.misc & PERF_RECORD_MISC_SWITCH_OUT)) < 0) {
+               ret = PyErr_NoMemory();
+       } else {
+               ret = PyString_FromString(s);
+               free(s);
+       }
+       return ret;
+}
+
+static PyTypeObject pyrf_context_switch_event__type = {
+       PyVarObject_HEAD_INIT(NULL, 0)
+       .tp_name        = "perf.context_switch_event",
+       .tp_basicsize   = sizeof(struct pyrf_event),
+       .tp_flags       = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+       .tp_doc         = pyrf_context_switch_event__doc,
+       .tp_members     = pyrf_context_switch_event__members,
+       .tp_repr        = (reprfunc)pyrf_context_switch_event__repr,
+};
+
 static int pyrf_event__setup_types(void)
 {
        int err;
@@ -306,6 +344,7 @@ static int pyrf_event__setup_types(void)
        pyrf_lost_event__type.tp_new =
        pyrf_read_event__type.tp_new =
        pyrf_sample_event__type.tp_new =
+       pyrf_context_switch_event__type.tp_new =
        pyrf_throttle_event__type.tp_new = PyType_GenericNew;
        err = PyType_Ready(&pyrf_mmap_event__type);
        if (err < 0)
@@ -328,6 +367,9 @@ static int pyrf_event__setup_types(void)
        err = PyType_Ready(&pyrf_sample_event__type);
        if (err < 0)
                goto out;
+       err = PyType_Ready(&pyrf_context_switch_event__type);
+       if (err < 0)
+               goto out;
 out:
        return err;
 }
@@ -342,6 +384,8 @@ static PyTypeObject *pyrf_event__type[] = {
        [PERF_RECORD_FORK]       = &pyrf_task_event__type,
        [PERF_RECORD_READ]       = &pyrf_read_event__type,
        [PERF_RECORD_SAMPLE]     = &pyrf_sample_event__type,
+       [PERF_RECORD_SWITCH]     = &pyrf_context_switch_event__type,
+       [PERF_RECORD_SWITCH_CPU_WIDE]  = &pyrf_context_switch_event__type,
 };
 
 static PyObject *pyrf_event__new(union perf_event *event)
@@ -349,8 +393,10 @@ static PyObject *pyrf_event__new(union perf_event *event)
        struct pyrf_event *pevent;
        PyTypeObject *ptype;
 
-       if (event->header.type < PERF_RECORD_MMAP ||
-           event->header.type > PERF_RECORD_SAMPLE)
+       if ((event->header.type < PERF_RECORD_MMAP ||
+            event->header.type > PERF_RECORD_SAMPLE) &&
+           !(event->header.type == PERF_RECORD_SWITCH ||
+             event->header.type == PERF_RECORD_SWITCH_CPU_WIDE))
                return NULL;
 
        ptype = pyrf_event__type[event->header.type];
@@ -528,6 +574,7 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel,
                "exclude_hv",
                "exclude_idle",
                "mmap",
+               "context_switch",
                "comm",
                "freq",
                "inherit_stat",
@@ -553,6 +600,7 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel,
            exclude_hv = 0,
            exclude_idle = 0,
            mmap = 0,
+           context_switch = 0,
            comm = 0,
            freq = 1,
            inherit_stat = 0,
@@ -565,13 +613,13 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel,
        int idx = 0;
 
        if (!PyArg_ParseTupleAndKeywords(args, kwargs,
-                                        "|iKiKKiiiiiiiiiiiiiiiiiiiiiKK", kwlist,
+                                        "|iKiKKiiiiiiiiiiiiiiiiiiiiiiKK", kwlist,
                                         &attr.type, &attr.config, &attr.sample_freq,
                                         &sample_period, &attr.sample_type,
                                         &attr.read_format, &disabled, &inherit,
                                         &pinned, &exclusive, &exclude_user,
                                         &exclude_kernel, &exclude_hv, &exclude_idle,
-                                        &mmap, &comm, &freq, &inherit_stat,
+                                        &mmap, &context_switch, &comm, &freq, &inherit_stat,
                                         &enable_on_exec, &task, &watermark,
                                         &precise_ip, &mmap_data, &sample_id_all,
                                         &attr.wakeup_events, &attr.bp_type,
@@ -595,6 +643,7 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel,
        attr.exclude_hv     = exclude_hv;
        attr.exclude_idle   = exclude_idle;
        attr.mmap           = mmap;
+       attr.context_switch = context_switch;
        attr.comm           = comm;
        attr.freq           = freq;
        attr.inherit_stat   = inherit_stat;
@@ -1019,6 +1068,8 @@ static struct {
        PERF_CONST(RECORD_LOST_SAMPLES),
        PERF_CONST(RECORD_SWITCH),
        PERF_CONST(RECORD_SWITCH_CPU_WIDE),
+
+       PERF_CONST(RECORD_MISC_SWITCH_OUT),
        { .name = NULL, },
 };
 
index 1bd593bbf7a56c1ddd5a6a9b6466f27f3eeafd53..544509c159cec4111e5865007b513e1197c576c4 100644 (file)
@@ -221,6 +221,7 @@ static void define_event_symbols(struct event_format *event,
                break;
        case PRINT_BSTRING:
        case PRINT_DYNAMIC_ARRAY:
+       case PRINT_DYNAMIC_ARRAY_LEN:
        case PRINT_STRING:
        case PRINT_BITMASK:
                break;
index ace2484985cb45c298529523d791736a25a5c12b..a8e825fca42af9aeab46de300855e5e22ea8ffc0 100644 (file)
@@ -251,6 +251,7 @@ static void define_event_symbols(struct event_format *event,
                /* gcc warns for these? */
        case PRINT_BSTRING:
        case PRINT_DYNAMIC_ARRAY:
+       case PRINT_DYNAMIC_ARRAY_LEN:
        case PRINT_FUNC:
        case PRINT_BITMASK:
                /* we should warn... */
@@ -318,7 +319,7 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
 
        if (thread__resolve_callchain(al->thread, evsel,
                                      sample, NULL, NULL,
-                                     PERF_MAX_STACK_DEPTH) != 0) {
+                                     scripting_max_stack) != 0) {
                pr_err("Failed to resolve callchain. Skipping\n");
                goto exit;
        }
index fc3f7c922f99abf57246e0446b154299a1e6085d..428149bc64d23c6a12e9c141fb3070809090ad36 100644 (file)
@@ -138,6 +138,8 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
                        perf_session__set_id_hdr_size(session);
                        perf_session__set_comm_exec(session);
                }
+       } else  {
+               session->machines.host.env = &perf_env;
        }
 
        if (!file || perf_data_file__is_write(file)) {
@@ -170,30 +172,13 @@ static void perf_session__delete_threads(struct perf_session *session)
        machine__delete_threads(&session->machines.host);
 }
 
-static void perf_session_env__exit(struct perf_env *env)
-{
-       zfree(&env->hostname);
-       zfree(&env->os_release);
-       zfree(&env->version);
-       zfree(&env->arch);
-       zfree(&env->cpu_desc);
-       zfree(&env->cpuid);
-
-       zfree(&env->cmdline);
-       zfree(&env->cmdline_argv);
-       zfree(&env->sibling_cores);
-       zfree(&env->sibling_threads);
-       zfree(&env->numa_nodes);
-       zfree(&env->pmu_mappings);
-}
-
 void perf_session__delete(struct perf_session *session)
 {
        auxtrace__free(session);
        auxtrace_index__free(&session->auxtrace_index);
        perf_session__destroy_kernel_maps(session);
        perf_session__delete_threads(session);
-       perf_session_env__exit(&session->header.env);
+       perf_env__exit(&session->header.env);
        machines__exit(&session->machines);
        if (session->file)
                perf_data_file__close(session->file);
@@ -1079,11 +1064,11 @@ static int machines__deliver_event(struct machines *machines,
 
        switch (event->header.type) {
        case PERF_RECORD_SAMPLE:
-               dump_sample(evsel, event, sample);
                if (evsel == NULL) {
                        ++evlist->stats.nr_unknown_id;
                        return 0;
                }
+               dump_sample(evsel, event, sample);
                if (machine == NULL) {
                        ++evlist->stats.nr_unprocessable_samples;
                        return 0;
@@ -1116,6 +1101,9 @@ static int machines__deliver_event(struct machines *machines,
        case PERF_RECORD_UNTHROTTLE:
                return tool->unthrottle(tool, event, sample, machine);
        case PERF_RECORD_AUX:
+               if (tool->aux == perf_event__process_aux &&
+                   (event->aux.flags & PERF_AUX_FLAG_TRUNCATED))
+                       evlist->stats.total_aux_lost += 1;
                return tool->aux(tool, event, sample, machine);
        case PERF_RECORD_ITRACE_START:
                return tool->itrace_start(tool, event, sample, machine);
@@ -1323,7 +1311,7 @@ struct thread *perf_session__findnew(struct perf_session *session, pid_t pid)
        return machine__findnew_thread(&session->machines.host, -1, pid);
 }
 
-static struct thread *perf_session__register_idle_thread(struct perf_session *session)
+struct thread *perf_session__register_idle_thread(struct perf_session *session)
 {
        struct thread *thread;
 
@@ -1361,6 +1349,13 @@ static void perf_session__warn_about_errors(const struct perf_session *session)
                }
        }
 
+       if (session->tool->aux == perf_event__process_aux &&
+           stats->total_aux_lost != 0) {
+               ui__warning("AUX data lost %" PRIu64 " times out of %u!\n\n",
+                           stats->total_aux_lost,
+                           stats->nr_events[PERF_RECORD_AUX]);
+       }
+
        if (stats->nr_unknown_events != 0) {
                ui__warning("Found %u unknown events!\n\n"
                            "Is this an older tool processing a perf.data "
@@ -1805,7 +1800,7 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample,
 
                if (thread__resolve_callchain(al->thread, evsel,
                                              sample, NULL, NULL,
-                                             PERF_MAX_STACK_DEPTH) != 0) {
+                                             stack_depth) != 0) {
                        if (verbose)
                                error("Failed to resolve callchain. Skipping\n");
                        return;
index b44afc75d1cc51feb05f943973d0813e8556122c..3e900c0efc734a19188ff2e6658e0321f9b6b843 100644 (file)
@@ -89,6 +89,8 @@ struct machine *perf_session__findnew_machine(struct perf_session *session, pid_
 }
 
 struct thread *perf_session__findnew(struct perf_session *session, pid_t pid);
+struct thread *perf_session__register_idle_thread(struct perf_session *session);
+
 size_t perf_session__fprintf(struct perf_session *session, FILE *fp);
 
 size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp);
index 7e3871606df3a9911fe150105942d61e4c2a5848..2d8ccd4d9e1b7bd5231d35c2afaaec9078c86573 100644 (file)
@@ -21,6 +21,7 @@ int           sort__need_collapse = 0;
 int            sort__has_parent = 0;
 int            sort__has_sym = 0;
 int            sort__has_dso = 0;
+int            sort__has_socket = 0;
 enum sort_mode sort__mode = SORT_MODE__NORMAL;
 
 
@@ -328,8 +329,8 @@ static char *get_srcfile(struct hist_entry *e)
        char *sf, *p;
        struct map *map = e->ms.map;
 
-       sf = get_srcline(map->dso, map__rip_2objdump(map, e->ip),
-                        e->ms.sym, true);
+       sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip),
+                        e->ms.sym, false, true);
        if (!strcmp(sf, SRCLINE_UNKNOWN))
                return no_srcfile;
        p = strchr(sf, ':');
@@ -421,6 +422,27 @@ struct sort_entry sort_cpu = {
        .se_width_idx   = HISTC_CPU,
 };
 
+/* --sort socket */
+
+static int64_t
+sort__socket_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+       return right->socket - left->socket;
+}
+
+static int hist_entry__socket_snprintf(struct hist_entry *he, char *bf,
+                                   size_t size, unsigned int width)
+{
+       return repsep_snprintf(bf, size, "%*.*d", width, width-3, he->socket);
+}
+
+struct sort_entry sort_socket = {
+       .se_header      = "Socket",
+       .se_cmp         = sort__socket_cmp,
+       .se_snprintf    = hist_entry__socket_snprintf,
+       .se_width_idx   = HISTC_SOCKET,
+};
+
 /* sort keys for branch stacks */
 
 static int64_t
@@ -632,6 +654,35 @@ static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
                                         width);
 }
 
+static int64_t
+sort__iaddr_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+       uint64_t l = 0, r = 0;
+
+       if (left->mem_info)
+               l = left->mem_info->iaddr.addr;
+       if (right->mem_info)
+               r = right->mem_info->iaddr.addr;
+
+       return (int64_t)(r - l);
+}
+
+static int hist_entry__iaddr_snprintf(struct hist_entry *he, char *bf,
+                                   size_t size, unsigned int width)
+{
+       uint64_t addr = 0;
+       struct map *map = NULL;
+       struct symbol *sym = NULL;
+
+       if (he->mem_info) {
+               addr = he->mem_info->iaddr.addr;
+               map  = he->mem_info->iaddr.map;
+               sym  = he->mem_info->iaddr.sym;
+       }
+       return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
+                                        width);
+}
+
 static int64_t
 sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
 {
@@ -1055,6 +1106,13 @@ struct sort_entry sort_mem_daddr_sym = {
        .se_width_idx   = HISTC_MEM_DADDR_SYMBOL,
 };
 
+struct sort_entry sort_mem_iaddr_sym = {
+       .se_header      = "Code Symbol",
+       .se_cmp         = sort__iaddr_cmp,
+       .se_snprintf    = hist_entry__iaddr_snprintf,
+       .se_width_idx   = HISTC_MEM_IADDR_SYMBOL,
+};
+
 struct sort_entry sort_mem_daddr_dso = {
        .se_header      = "Data Object",
        .se_cmp         = sort__dso_daddr_cmp,
@@ -1248,6 +1306,7 @@ static struct sort_dimension common_sort_dimensions[] = {
        DIM(SORT_SYM, "symbol", sort_sym),
        DIM(SORT_PARENT, "parent", sort_parent),
        DIM(SORT_CPU, "cpu", sort_cpu),
+       DIM(SORT_SOCKET, "socket", sort_socket),
        DIM(SORT_SRCLINE, "srcline", sort_srcline),
        DIM(SORT_SRCFILE, "srcfile", sort_srcfile),
        DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
@@ -1276,6 +1335,7 @@ static struct sort_dimension bstack_sort_dimensions[] = {
 
 static struct sort_dimension memory_sort_dimensions[] = {
        DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
+       DIM(SORT_MEM_IADDR_SYMBOL, "symbol_iaddr", sort_mem_iaddr_sym),
        DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
        DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
        DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
@@ -1517,6 +1577,12 @@ static int __hpp_dimension__add_output(struct hpp_dimension *hd)
        return 0;
 }
 
+int hpp_dimension__add_output(unsigned col)
+{
+       BUG_ON(col >= PERF_HPP__MAX_INDEX);
+       return __hpp_dimension__add_output(&hpp_sort_dimensions[col]);
+}
+
 int sort_dimension__add(const char *tok)
 {
        unsigned int i;
@@ -1550,6 +1616,8 @@ int sort_dimension__add(const char *tok)
 
                } else if (sd->entry == &sort_dso) {
                        sort__has_dso = 1;
+               } else if (sd->entry == &sort_socket) {
+                       sort__has_socket = 1;
                }
 
                return __sort_dimension__add(sd);
@@ -1855,8 +1923,6 @@ static int __setup_output_field(void)
        if (field_order == NULL)
                return 0;
 
-       reset_dimensions();
-
        strp = str = strdup(field_order);
        if (str == NULL) {
                error("Not enough memory to setup output fields");
index 3c2a399f8f5b9ca093efe7afac24e9034376310b..31228851e397770d6b7567be00e7feb27435c8c0 100644 (file)
@@ -34,6 +34,7 @@ extern int have_ignore_callees;
 extern int sort__need_collapse;
 extern int sort__has_parent;
 extern int sort__has_sym;
+extern int sort__has_socket;
 extern enum sort_mode sort__mode;
 extern struct sort_entry sort_comm;
 extern struct sort_entry sort_dso;
@@ -90,6 +91,7 @@ struct hist_entry {
        struct comm             *comm;
        u64                     ip;
        u64                     transaction;
+       s32                     socket;
        s32                     cpu;
        u8                      cpumode;
 
@@ -172,6 +174,7 @@ enum sort_type {
        SORT_SYM,
        SORT_PARENT,
        SORT_CPU,
+       SORT_SOCKET,
        SORT_SRCLINE,
        SORT_SRCFILE,
        SORT_LOCAL_WEIGHT,
@@ -198,6 +201,7 @@ enum sort_type {
        SORT_MEM_LVL,
        SORT_MEM_SNOOP,
        SORT_MEM_DCACHELINE,
+       SORT_MEM_IADDR_SYMBOL,
 };
 
 /*
@@ -230,4 +234,6 @@ void perf_hpp__set_elide(int idx, bool elide);
 int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset);
 
 bool is_strict_order(const char *order);
+
+int hpp_dimension__add_output(unsigned col);
 #endif /* __PERF_SORT_H */
index fc08248f08ca703b0a4330169535264854d5478c..b4db3f48e3b09784f00d610c8cbffea8c26e38c6 100644 (file)
@@ -149,8 +149,11 @@ static void addr2line_cleanup(struct a2l_data *a2l)
        free(a2l);
 }
 
+#define MAX_INLINE_NEST 1024
+
 static int addr2line(const char *dso_name, u64 addr,
-                    char **file, unsigned int *line, struct dso *dso)
+                    char **file, unsigned int *line, struct dso *dso,
+                    bool unwind_inlines)
 {
        int ret = 0;
        struct a2l_data *a2l = dso->a2l;
@@ -170,6 +173,15 @@ static int addr2line(const char *dso_name, u64 addr,
 
        bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l);
 
+       if (a2l->found && unwind_inlines) {
+               int cnt = 0;
+
+               while (bfd_find_inliner_info(a2l->abfd, &a2l->filename,
+                                            &a2l->funcname, &a2l->line) &&
+                      cnt++ < MAX_INLINE_NEST)
+                       ;
+       }
+
        if (a2l->found && a2l->filename) {
                *file = strdup(a2l->filename);
                *line = a2l->line;
@@ -197,7 +209,8 @@ void dso__free_a2l(struct dso *dso)
 
 static int addr2line(const char *dso_name, u64 addr,
                     char **file, unsigned int *line_nr,
-                    struct dso *dso __maybe_unused)
+                    struct dso *dso __maybe_unused,
+                    bool unwind_inlines __maybe_unused)
 {
        FILE *fp;
        char cmd[PATH_MAX];
@@ -254,8 +267,8 @@ void dso__free_a2l(struct dso *dso __maybe_unused)
  */
 #define A2L_FAIL_LIMIT 123
 
-char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
-                 bool show_sym)
+char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
+                 bool show_sym, bool unwind_inlines)
 {
        char *file = NULL;
        unsigned line = 0;
@@ -276,7 +289,7 @@ char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
        if (!strncmp(dso_name, "/tmp/perf-", 10))
                goto out;
 
-       if (!addr2line(dso_name, addr, &file, &line, dso))
+       if (!addr2line(dso_name, addr, &file, &line, dso, unwind_inlines))
                goto out;
 
        if (asprintf(&srcline, "%s:%u",
@@ -310,3 +323,9 @@ void free_srcline(char *srcline)
        if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0)
                free(srcline);
 }
+
+char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
+                 bool show_sym)
+{
+       return __get_srcline(dso, addr, sym, show_sym, false);
+}
index 2d065d065b676232eecbe8ea94348f4cf3a77f33..2d9d8306dbd3f97f3d1f9734ace17b25d9ed583a 100644 (file)
@@ -67,7 +67,7 @@ double rel_stddev_stats(double stddev, double avg)
 bool __perf_evsel_stat__is(struct perf_evsel *evsel,
                           enum perf_stat_evsel_id id)
 {
-       struct perf_stat *ps = evsel->priv;
+       struct perf_stat_evsel *ps = evsel->priv;
 
        return ps->id == id;
 }
@@ -84,7 +84,7 @@ static const char *id_str[PERF_STAT_EVSEL_ID__MAX] = {
 
 void perf_stat_evsel_id_init(struct perf_evsel *evsel)
 {
-       struct perf_stat *ps = evsel->priv;
+       struct perf_stat_evsel *ps = evsel->priv;
        int i;
 
        /* ps->id is 0 hence PERF_STAT_EVSEL_ID__NONE by default */
@@ -100,7 +100,7 @@ void perf_stat_evsel_id_init(struct perf_evsel *evsel)
 void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
 {
        int i;
-       struct perf_stat *ps = evsel->priv;
+       struct perf_stat_evsel *ps = evsel->priv;
 
        for (i = 0; i < 3; i++)
                init_stats(&ps->res_stats[i]);
@@ -110,7 +110,7 @@ void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
 
 int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
 {
-       evsel->priv = zalloc(sizeof(struct perf_stat));
+       evsel->priv = zalloc(sizeof(struct perf_stat_evsel));
        if (evsel->priv == NULL)
                return -ENOMEM;
        perf_evsel__reset_stat_priv(evsel);
@@ -230,7 +230,7 @@ static int check_per_pkg(struct perf_evsel *counter,
        if (!(vals->run && vals->ena))
                return 0;
 
-       s = cpu_map__get_socket(cpus, cpu);
+       s = cpu_map__get_socket(cpus, cpu, NULL);
        if (s < 0)
                return -1;
 
@@ -272,6 +272,7 @@ process_counter_values(struct perf_stat_config *config, struct perf_evsel *evsel
                        aggr->ena += count->ena;
                        aggr->run += count->run;
                }
+       case AGGR_UNSET:
        default:
                break;
        }
@@ -304,7 +305,7 @@ int perf_stat_process_counter(struct perf_stat_config *config,
                              struct perf_evsel *counter)
 {
        struct perf_counts_values *aggr = &counter->counts->aggr;
-       struct perf_stat *ps = counter->priv;
+       struct perf_stat_evsel *ps = counter->priv;
        u64 *count = counter->counts->aggr.values;
        int i, ret;
 
index 62448c8175d3de587eb6b9546df7bc4088b2bfa1..da1d11c4f8c193cff476fec76868277b12f7c623 100644 (file)
@@ -20,7 +20,7 @@ enum perf_stat_evsel_id {
        PERF_STAT_EVSEL_ID__MAX,
 };
 
-struct perf_stat {
+struct perf_stat_evsel {
        struct stats            res_stats[3];
        enum perf_stat_evsel_id id;
 };
@@ -31,6 +31,7 @@ enum aggr_mode {
        AGGR_SOCKET,
        AGGR_CORE,
        AGGR_THREAD,
+       AGGR_UNSET,
 };
 
 struct perf_stat_config {
index 4abe23550c73808b16586ac4121bcde244a7fba8..25671fa166188413c66758a978082e89a1ba08d2 100644 (file)
@@ -82,23 +82,22 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len)
        strbuf_setlen(sb, sb->len + len);
 }
 
-void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
+void strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap)
 {
        int len;
-       va_list ap;
+       va_list ap_saved;
 
        if (!strbuf_avail(sb))
                strbuf_grow(sb, 64);
-       va_start(ap, fmt);
+
+       va_copy(ap_saved, ap);
        len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
-       va_end(ap);
        if (len < 0)
                die("your vsnprintf is broken");
        if (len > strbuf_avail(sb)) {
                strbuf_grow(sb, len);
-               va_start(ap, fmt);
-               len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
-               va_end(ap);
+               len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap_saved);
+               va_end(ap_saved);
                if (len > strbuf_avail(sb)) {
                        die("this should not happen, your vsnprintf is broken");
                }
@@ -106,6 +105,15 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
        strbuf_setlen(sb, sb->len + len);
 }
 
+void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       strbuf_addv(sb, fmt, ap);
+       va_end(ap);
+}
+
 ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
 {
        size_t oldlen = sb->len;
index 436ac319f6c76bdf2a2be4b19e60e06dc481f245..529f2f03524915ab9cae7c5608de444fd875812d 100644 (file)
@@ -39,6 +39,7 @@
  */
 
 #include <assert.h>
+#include <stdarg.h>
 
 extern char strbuf_slopbuf[];
 struct strbuf {
@@ -85,6 +86,7 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
 
 __attribute__((format(printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
+extern void strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap);
 
 /* XXX: if read fails, any partial read is undone */
 extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint);
index fd8477cacf888f2bdac70fbc71a4f0461fbfc9b5..48906333a858c06b41991f33cb0f5f2cdd4f68d9 100644 (file)
@@ -337,7 +337,7 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused,
                  symbol_filter_t filter __maybe_unused,
                  int kmodule __maybe_unused)
 {
-       unsigned char *build_id[BUILD_ID_SIZE];
+       unsigned char build_id[BUILD_ID_SIZE];
        int ret;
 
        ret = fd__is_64_bit(ss->fd);
index 1f97ffb158a6af43e95cd1428ae0339d5fed3386..b4cc7662677ec858cddf6dd90e906f75c5a14200 100644 (file)
@@ -624,7 +624,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
         * symbols, setting length to 0, and rely on
         * symbols__fixup_end() to fix it up.
         */
-       sym = symbol__new(start, 0, kallsyms2elf_type(type), name);
+       sym = symbol__new(start, 0, kallsyms2elf_binding(type), name);
        if (sym == NULL)
                return -ENOMEM;
        /*
@@ -680,7 +680,7 @@ static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map,
                        pos->start -= curr_map->start - curr_map->pgoff;
                        if (pos->end)
                                pos->end -= curr_map->start - curr_map->pgoff;
-                       if (curr_map != map) {
+                       if (curr_map->dso != map->dso) {
                                rb_erase_init(&pos->rb_node, root);
                                symbols__insert(
                                        &curr_map->dso->symbols[curr_map->type],
@@ -1406,6 +1406,7 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
        struct symsrc ss_[2];
        struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
        bool kmod;
+       unsigned char build_id[BUILD_ID_SIZE];
 
        pthread_mutex_lock(&dso->lock);
 
@@ -1461,6 +1462,14 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
                dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE ||
                dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP;
 
+
+       /*
+        * Read the build id if possible. This is required for
+        * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work
+        */
+       if (filename__read_build_id(dso->name, build_id, BUILD_ID_SIZE) > 0)
+               dso__set_build_id(dso, build_id);
+
        /*
         * Iterate over candidate debug images.
         * Keep track of "interesting" ones (those which have a symtab, dynsym,
@@ -1607,6 +1616,15 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,
        int i, err = 0;
        char *filename = NULL;
 
+       pr_debug("Looking at the vmlinux_path (%d entries long)\n",
+                vmlinux_path__nr_entries + 1);
+
+       for (i = 0; i < vmlinux_path__nr_entries; ++i) {
+               err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter);
+               if (err > 0)
+                       goto out;
+       }
+
        if (!symbol_conf.ignore_vmlinux_buildid)
                filename = dso__build_id_filename(dso, NULL, 0);
        if (filename != NULL) {
@@ -1615,15 +1633,6 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,
                        goto out;
                free(filename);
        }
-
-       pr_debug("Looking at the vmlinux_path (%d entries long)\n",
-                vmlinux_path__nr_entries + 1);
-
-       for (i = 0; i < vmlinux_path__nr_entries; ++i) {
-               err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter);
-               if (err > 0)
-                       break;
-       }
 out:
        return err;
 }
index 440ba8ae888f0a2fcbc91cb67a85a206bb7ca1c1..40073c60b83d6b7e91fe2e3208f50210cf61f6ce 100644 (file)
@@ -191,6 +191,7 @@ struct addr_location {
        u8            filtered;
        u8            cpumode;
        s32           cpu;
+       s32           socket;
 };
 
 struct symsrc {
index 22245986e59e45632d4998285dbc3b6557ed370d..d995743cb673e77524f0cd539d0cf0042b3fc363 100644 (file)
@@ -38,7 +38,7 @@
 
 #include "../perf.h"
 #include "trace-event.h"
-#include <api/fs/debugfs.h>
+#include <api/fs/tracing_path.h>
 #include "evsel.h"
 #include "debug.h"
 
index b90e646c7a91c399ea23f1f23c49991e5e8511d3..802bb868d446cafa7c5383982193ad13d87b785a 100644 (file)
@@ -7,7 +7,9 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <linux/kernel.h>
+#include <linux/err.h>
 #include <traceevent/event-parse.h>
+#include <api/fs/tracing_path.h>
 #include "trace-event.h"
 #include "machine.h"
 #include "util.h"
@@ -65,6 +67,9 @@ void trace_event__cleanup(struct trace_event *t)
        pevent_free(t->pevent);
 }
 
+/*
+ * Returns pointer with encoded error via <linux/err.h> interface.
+ */
 static struct event_format*
 tp_format(const char *sys, const char *name)
 {
@@ -73,12 +78,14 @@ tp_format(const char *sys, const char *name)
        char path[PATH_MAX];
        size_t size;
        char *data;
+       int err;
 
        scnprintf(path, PATH_MAX, "%s/%s/%s/format",
                  tracing_events_path, sys, name);
 
-       if (filename__read_str(path, &data, &size))
-               return NULL;
+       err = filename__read_str(path, &data, &size);
+       if (err)
+               return ERR_PTR(err);
 
        pevent_parse_format(pevent, &event, data, size, sys);
 
@@ -86,11 +93,14 @@ tp_format(const char *sys, const char *name)
        return event;
 }
 
+/*
+ * Returns pointer with encoded error via <linux/err.h> interface.
+ */
 struct event_format*
 trace_event__tp_format(const char *sys, const char *name)
 {
        if (!tevent_initialized && trace_event__init2())
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        return tp_format(sys, name);
 }
index da6cc4cc2a4f48eebfa0e59a3001e9e61d8484ee..b85ee55cca0cc943c75e2ea348b932236ff0618f 100644 (file)
@@ -78,6 +78,8 @@ struct scripting_ops {
        int (*generate_script) (struct pevent *pevent, const char *outfile);
 };
 
+extern unsigned int scripting_max_stack;
+
 int script_spec_register(const char *spec, struct scripting_ops *ops);
 
 void setup_perl_scripting(void);
index 4c00507ee3fd2ad488642def35226711cba821fe..c83832b555e580a771c1a831e1d18b3b5df135ac 100644 (file)
@@ -330,6 +330,7 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
        struct map *map;
        unw_dyn_info_t di;
        u64 table_data, segbase, fde_count;
+       int ret = -EINVAL;
 
        map = find_map(ip, ui);
        if (!map || !map->dso)
@@ -348,29 +349,33 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
                di.u.rti.table_data = map->start + table_data;
                di.u.rti.table_len  = fde_count * sizeof(struct table_entry)
                                      / sizeof(unw_word_t);
-               return dwarf_search_unwind_table(as, ip, &di, pi,
-                                                need_unwind_info, arg);
+               ret = dwarf_search_unwind_table(as, ip, &di, pi,
+                                               need_unwind_info, arg);
        }
 
 #ifndef NO_LIBUNWIND_DEBUG_FRAME
        /* Check the .debug_frame section for unwinding info */
-       if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) {
+       if (ret < 0 &&
+           !read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) {
                int fd = dso__data_get_fd(map->dso, ui->machine);
                int is_exec = elf_is_exec(fd, map->dso->name);
                unw_word_t base = is_exec ? 0 : map->start;
+               const char *symfile;
 
                if (fd >= 0)
                        dso__data_put_fd(map->dso);
 
+               symfile = map->dso->symsrc_filename ?: map->dso->name;
+
                memset(&di, 0, sizeof(di));
-               if (dwarf_find_debug_frame(0, &di, ip, base, map->dso->name,
+               if (dwarf_find_debug_frame(0, &di, ip, base, symfile,
                                           map->start, map->end))
                        return dwarf_search_unwind_table(as, ip, &di, pi,
                                                         need_unwind_info, arg);
        }
 #endif
 
-       return -EINVAL;
+       return ret;
 }
 
 static int access_fpreg(unw_addr_space_t __maybe_unused as,
@@ -461,7 +466,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as,
                if (ret) {
                        pr_debug("unwind: access_mem %p not inside range"
                                 " 0x%" PRIx64 "-0x%" PRIx64 "\n",
-                                (void *) addr, start, end);
+                                (void *) (uintptr_t) addr, start, end);
                        *valp = 0;
                        return ret;
                }
@@ -471,7 +476,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as,
        offset = addr - start;
        *valp  = *(unw_word_t *)&stack->data[offset];
        pr_debug("unwind: access_mem addr %p val %lx, offset %d\n",
-                (void *) addr, (unsigned long)*valp, offset);
+                (void *) (uintptr_t) addr, (unsigned long)*valp, offset);
        return 0;
 }
 
index 4007aca8e0caa550a3fd551accf66810f2e19be9..6adfa18cdd4e0289a83bfca59a5f2901cdeb63b8 100644 (file)
@@ -50,6 +50,11 @@ void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN)
        die_routine = routine;
 }
 
+void set_warning_routine(void (*routine)(const char *err, va_list params))
+{
+       warn_routine = routine;
+}
+
 void usage(const char *err)
 {
        usage_routine(err);
index c2cd9bf2348b5eb8e68603c32b7a3bf220c41aad..cd12c25e4ea408b1b873d8368f5081d3e2a72113 100644 (file)
@@ -17,7 +17,7 @@
 #include "callchain.h"
 
 struct callchain_param callchain_param = {
-       .mode   = CHAIN_GRAPH_REL,
+       .mode   = CHAIN_GRAPH_ABS,
        .min_percent = 0.5,
        .order  = ORDER_CALLEE,
        .key    = CCKEY_FUNCTION
@@ -34,9 +34,6 @@ bool test_attr__enabled;
 bool perf_host  = true;
 bool perf_guest = false;
 
-char tracing_path[PATH_MAX + 1]        = "/sys/kernel/debug/tracing";
-char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events";
-
 void event_attr_init(struct perf_event_attr *attr)
 {
        if (!perf_host)
@@ -390,73 +387,6 @@ void set_term_quiet_input(struct termios *old)
        tcsetattr(0, TCSANOW, &tc);
 }
 
-static void set_tracing_events_path(const char *tracing, const char *mountpoint)
-{
-       snprintf(tracing_path, sizeof(tracing_path), "%s/%s",
-                mountpoint, tracing);
-       snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s%s",
-                mountpoint, tracing, "events");
-}
-
-static const char *__perf_tracefs_mount(const char *mountpoint)
-{
-       const char *mnt;
-
-       mnt = tracefs_mount(mountpoint);
-       if (!mnt)
-               return NULL;
-
-       set_tracing_events_path("", mnt);
-
-       return mnt;
-}
-
-static const char *__perf_debugfs_mount(const char *mountpoint)
-{
-       const char *mnt;
-
-       mnt = debugfs_mount(mountpoint);
-       if (!mnt)
-               return NULL;
-
-       set_tracing_events_path("tracing/", mnt);
-
-       return mnt;
-}
-
-const char *perf_debugfs_mount(const char *mountpoint)
-{
-       const char *mnt;
-
-       mnt = __perf_tracefs_mount(mountpoint);
-       if (mnt)
-               return mnt;
-
-       mnt = __perf_debugfs_mount(mountpoint);
-
-       return mnt;
-}
-
-void perf_debugfs_set_path(const char *mntpt)
-{
-       set_tracing_events_path("tracing/", mntpt);
-}
-
-char *get_tracing_file(const char *name)
-{
-       char *file;
-
-       if (asprintf(&file, "%s/%s", tracing_path, name) < 0)
-               return NULL;
-
-       return file;
-}
-
-void put_tracing_file(char *file)
-{
-       free(file);
-}
-
 int parse_nsec_time(const char *str, u64 *ptime)
 {
        u64 time_sec, time_nsec;
index 291be1d84bc3e679ef1d3c4974309f566bd9a652..4cfb913aa9e04bffd94286ac426bac4c1221e9c4 100644 (file)
@@ -74,8 +74,7 @@
 #include <linux/magic.h>
 #include <linux/types.h>
 #include <sys/ttydefaults.h>
-#include <api/fs/debugfs.h>
-#include <api/fs/tracefs.h>
+#include <api/fs/tracing_path.h>
 #include <termios.h>
 #include <linux/bitops.h>
 #include <termios.h>
 extern const char *graph_line;
 extern const char *graph_dotted_line;
 extern char buildid_dir[];
-extern char tracing_path[];
-extern char tracing_events_path[];
-extern void perf_debugfs_set_path(const char *mountpoint);
-const char *perf_debugfs_mount(const char *mountpoint);
-char *get_tracing_file(const char *name);
-void put_tracing_file(char *file);
 
 /* On most systems <limits.h> would have given us this, but
  * not on some systems (e.g. GNU/Hurd).
@@ -152,6 +145,7 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
 
 
 extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
+extern void set_warning_routine(void (*routine)(const char *err, va_list params));
 
 extern int prefixcmp(const char *str, const char *prefix);
 extern void set_buildid_dir(const char *dir);
@@ -321,6 +315,8 @@ struct symbol;
 extern bool srcline_full_filename;
 char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
                  bool show_sym);
+char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
+                 bool show_sym, bool unwind_inlines);
 void free_srcline(char *srcline);
 
 int filename__read_str(const char *filename, char **buf, size_t *sizep);
index fbe2dbff1e210c2f4711d2f581823d6b14c85799..f6483609ebc246f38d268dc97a9ddfa4072cbf7b 100755 (executable)
@@ -75,7 +75,7 @@ usage () {
 while test $# -gt 0
 do
        case "$1" in
-       --bootargs)
+       --bootargs|--bootarg)
                checkarg --bootargs "(list of kernel boot arguments)" "$#" "$2" '.*' '^--'
                TORTURE_BOOTARGS="$2"
                shift
@@ -88,7 +88,7 @@ do
        --buildonly)
                TORTURE_BUILDONLY=1
                ;;
-       --configs)
+       --configs|--config)
                checkarg --configs "(list of config files)" "$#" "$2" '^[^/]*$' '^--'
                configs="$2"
                shift
@@ -134,7 +134,7 @@ do
        --no-initrd)
                TORTURE_INITRD=""; export TORTURE_INITRD
                ;;
-       --qemu-args)
+       --qemu-args|--qemu-arg)
                checkarg --qemu-args "-qemu args" $# "$2" '^-' '^error'
                TORTURE_QEMU_ARG="$2"
                shift
index 6910b73707617cc2ddf855be82e488420a35bed1..b9611c523723982e726548742b700d29ed0be7c4 100644 (file)
@@ -1,4 +1,6 @@
 LOCK01
 LOCK02
 LOCK03
-LOCK04
\ No newline at end of file
+LOCK04
+LOCK05
+LOCK06
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK05 b/tools/testing/selftests/rcutorture/configs/lock/LOCK05
new file mode 100644 (file)
index 0000000..1d1da14
--- /dev/null
@@ -0,0 +1,6 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=4
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK05.boot b/tools/testing/selftests/rcutorture/configs/lock/LOCK05.boot
new file mode 100644 (file)
index 0000000..8ac3730
--- /dev/null
@@ -0,0 +1 @@
+locktorture.torture_type=rtmutex_lock
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK06 b/tools/testing/selftests/rcutorture/configs/lock/LOCK06
new file mode 100644 (file)
index 0000000..1d1da14
--- /dev/null
@@ -0,0 +1,6 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=4
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK06.boot b/tools/testing/selftests/rcutorture/configs/lock/LOCK06.boot
new file mode 100644 (file)
index 0000000..f92219c
--- /dev/null
@@ -0,0 +1 @@
+locktorture.torture_type=percpu_rwsem_lock
index 89a3f44bf355d65f72ad279c298920d4c0dec260..4a1be1b75a7fe6ca66c8788d62740622fd795d42 100644 (file)
@@ -8,7 +8,7 @@ LDFLAGS += -lrt -lpthread
 TEST_PROGS = posix_timers nanosleep nsleep-lat set-timer-lat mqueue-lat \
             inconsistency-check raw_skew threadtest rtctest
 
-TEST_PROGS_EXTENDED = alarmtimer-suspend valid-adjtimex change_skew \
+TEST_PROGS_EXTENDED = alarmtimer-suspend valid-adjtimex adjtick change_skew \
                      skew_consistency clocksource-switch leap-a-day \
                      leapcrash set-tai set-2038
 
@@ -24,6 +24,7 @@ include ../lib.mk
 run_destructive_tests: run_tests
        ./alarmtimer-suspend
        ./valid-adjtimex
+       ./adjtick
        ./change_skew
        ./skew_consistency
        ./clocksource-switch
diff --git a/tools/testing/selftests/timers/adjtick.c b/tools/testing/selftests/timers/adjtick.c
new file mode 100644 (file)
index 0000000..9887fd5
--- /dev/null
@@ -0,0 +1,221 @@
+/* adjtimex() tick adjustment test
+ *             by:   John Stultz <john.stultz@linaro.org>
+ *             (C) Copyright Linaro Limited 2015
+ *             Licensed under the GPLv2
+ *
+ *  To build:
+ *     $ gcc adjtick.c -o adjtick -lrt
+ *
+ *   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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+#include <time.h>
+
+#ifdef KTEST
+#include "../kselftest.h"
+#else
+static inline int ksft_exit_pass(void)
+{
+       exit(0);
+}
+static inline int ksft_exit_fail(void)
+{
+       exit(1);
+}
+#endif
+
+#define CLOCK_MONOTONIC_RAW    4
+
+#define NSEC_PER_SEC           1000000000LL
+#define USEC_PER_SEC           1000000
+
+#define MILLION                        1000000
+
+long systick;
+
+long long llabs(long long val)
+{
+       if (val < 0)
+               val = -val;
+       return val;
+}
+
+unsigned long long ts_to_nsec(struct timespec ts)
+{
+       return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
+}
+
+struct timespec nsec_to_ts(long long ns)
+{
+       struct timespec ts;
+
+       ts.tv_sec = ns/NSEC_PER_SEC;
+       ts.tv_nsec = ns%NSEC_PER_SEC;
+
+       return ts;
+}
+
+long long diff_timespec(struct timespec start, struct timespec end)
+{
+       long long start_ns, end_ns;
+
+       start_ns = ts_to_nsec(start);
+       end_ns = ts_to_nsec(end);
+
+       return end_ns - start_ns;
+}
+
+void get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)
+{
+       struct timespec start, mid, end;
+       long long diff = 0, tmp;
+       int i;
+
+       clock_gettime(CLOCK_MONOTONIC, mon);
+       clock_gettime(CLOCK_MONOTONIC_RAW, raw);
+
+       /* Try to get a more tightly bound pairing */
+       for (i = 0; i < 3; i++) {
+               long long newdiff;
+
+               clock_gettime(CLOCK_MONOTONIC, &start);
+               clock_gettime(CLOCK_MONOTONIC_RAW, &mid);
+               clock_gettime(CLOCK_MONOTONIC, &end);
+
+               newdiff = diff_timespec(start, end);
+               if (diff == 0 || newdiff < diff) {
+                       diff = newdiff;
+                       *raw = mid;
+                       tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;
+                       *mon = nsec_to_ts(tmp);
+               }
+       }
+}
+
+long long get_ppm_drift(void)
+{
+       struct timespec mon_start, raw_start, mon_end, raw_end;
+       long long delta1, delta2, eppm;
+
+       get_monotonic_and_raw(&mon_start, &raw_start);
+
+       sleep(15);
+
+       get_monotonic_and_raw(&mon_end, &raw_end);
+
+       delta1 = diff_timespec(mon_start, mon_end);
+       delta2 = diff_timespec(raw_start, raw_end);
+
+       eppm = (delta1*MILLION)/delta2 - MILLION;
+
+       return eppm;
+}
+
+int check_tick_adj(long tickval)
+{
+       long long eppm, ppm;
+       struct timex tx1;
+
+       tx1.modes        = ADJ_TICK;
+       tx1.modes       |= ADJ_OFFSET;
+       tx1.modes       |= ADJ_FREQUENCY;
+       tx1.modes       |= ADJ_STATUS;
+
+       tx1.status      = STA_PLL;
+       tx1.offset      = 0;
+       tx1.freq        = 0;
+       tx1.tick        = tickval;
+
+       adjtimex(&tx1);
+
+       sleep(1);
+
+       ppm = ((long long)tickval * MILLION)/systick - MILLION;
+       printf("Estimating tick (act: %ld usec, %lld ppm): ", tickval, ppm);
+
+       eppm = get_ppm_drift();
+       printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm);
+
+       tx1.modes = 0;
+       adjtimex(&tx1);
+
+       if (tx1.offset || tx1.freq || tx1.tick != tickval) {
+               printf("        [ERROR]\n");
+               printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n");
+               return -1;
+       }
+
+       /*
+        * Here we use 100ppm difference as an error bound.
+        * We likely should see better, but some coarse clocksources
+        * cannot match the HZ tick size accurately, so we have a
+        * internal correction factor that doesn't scale exactly
+        * with the adjustment, resulting in > 10ppm error during
+        * a 10% adjustment. 100ppm also gives us more breathing
+        * room for interruptions during the measurement.
+        */
+       if (llabs(eppm - ppm) > 100) {
+               printf("        [FAILED]\n");
+               return -1;
+       }
+       printf("        [OK]\n");
+
+       return  0;
+}
+
+int main(int argv, char **argc)
+{
+       struct timespec raw;
+       long tick, max, interval, err;
+       struct timex tx1;
+
+       err = 0;
+       setbuf(stdout, NULL);
+
+       if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {
+               printf("ERR: NO CLOCK_MONOTONIC_RAW\n");
+               return -1;
+       }
+
+       printf("Each iteration takes about 15 seconds\n");
+
+       systick = sysconf(_SC_CLK_TCK);
+       systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK);
+       max = systick/10; /* +/- 10% */
+       interval = max/4; /* in 4 steps each side */
+
+       for (tick = (systick - max); tick < (systick + max); tick += interval) {
+               if (check_tick_adj(tick)) {
+                       err = 1;
+                       break;
+               }
+       }
+
+       /* Reset things to zero */
+       tx1.modes        = ADJ_TICK;
+       tx1.modes       |= ADJ_OFFSET;
+       tx1.modes       |= ADJ_FREQUENCY;
+
+       tx1.offset       = 0;
+       tx1.freq         = 0;
+       tx1.tick         = systick;
+
+       adjtimex(&tx1);
+
+       if (err)
+               return ksft_exit_fail();
+
+       return ksft_exit_pass();
+}
index 29089b24d18bca1bd3b4a3b8ab92a65cf6316053..eabcff4119840aaf663da3bbd0a8c4154b072881 100644 (file)
@@ -4,8 +4,9 @@ include ../lib.mk
 
 .PHONY: all all_32 all_64 warn_32bit_failure clean
 
-TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs ldt_gdt syscall_nt
-TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault sigreturn
+TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs ldt_gdt syscall_nt ptrace_syscall
+TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault sigreturn test_syscall_vdso unwind_vdso \
+                       test_FCMOV test_FCOMI test_FISTTP
 
 TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY)
 BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32)
@@ -35,7 +36,7 @@ clean:
        $(RM) $(BINARIES_32) $(BINARIES_64)
 
 $(TARGETS_C_32BIT_ALL:%=%_32): %_32: %.c
-       $(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl
+       $(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl -lm
 
 $(TARGETS_C_BOTHBITS:%=%_64): %_64: %.c
        $(CC) -m64 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl
@@ -60,3 +61,5 @@ endif
 
 # Some tests have additional dependencies.
 sysret_ss_attrs_64: thunks.S
+ptrace_syscall_32: raw_syscall_helper_32.S
+test_syscall_vdso_32: thunks_32.S
index 421c607a8856887d0f9f6048c2ab26bd9367efdd..d075ea0e5ca1ac0a42ce0d48e25b30a1a059de48 100644 (file)
@@ -230,5 +230,9 @@ int main(void)
        }
        clearhandler(SIGSEGV);
 
+       /* Make sure nothing explodes if we fork. */
+       if (fork() > 0)
+               return 0;
+
        return (nerrs == 0 ? 0 : 1);
 }
diff --git a/tools/testing/selftests/x86/ptrace_syscall.c b/tools/testing/selftests/x86/ptrace_syscall.c
new file mode 100644 (file)
index 0000000..5105b49
--- /dev/null
@@ -0,0 +1,294 @@
+#define _GNU_SOURCE
+
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include <sys/user.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <asm/ptrace-abi.h>
+#include <sys/auxv.h>
+
+/* Bitness-agnostic defines for user_regs_struct fields. */
+#ifdef __x86_64__
+# define user_syscall_nr       orig_rax
+# define user_arg0             rdi
+# define user_arg1             rsi
+# define user_arg2             rdx
+# define user_arg3             r10
+# define user_arg4             r8
+# define user_arg5             r9
+# define user_ip               rip
+# define user_ax               rax
+#else
+# define user_syscall_nr       orig_eax
+# define user_arg0             ebx
+# define user_arg1             ecx
+# define user_arg2             edx
+# define user_arg3             esi
+# define user_arg4             edi
+# define user_arg5             ebp
+# define user_ip               eip
+# define user_ax               eax
+#endif
+
+static int nerrs = 0;
+
+struct syscall_args32 {
+       uint32_t nr, arg0, arg1, arg2, arg3, arg4, arg5;
+};
+
+#ifdef __i386__
+extern void sys32_helper(struct syscall_args32 *, void *);
+extern void int80_and_ret(void);
+#endif
+
+/*
+ * Helper to invoke int80 with controlled regs and capture the final regs.
+ */
+static void do_full_int80(struct syscall_args32 *args)
+{
+#ifdef __x86_64__
+       register unsigned long bp asm("bp") = args->arg5;
+       asm volatile ("int $0x80"
+                     : "+a" (args->nr),
+                       "+b" (args->arg0), "+c" (args->arg1), "+d" (args->arg2),
+                       "+S" (args->arg3), "+D" (args->arg4), "+r" (bp));
+       args->arg5 = bp;
+#else
+       sys32_helper(args, int80_and_ret);
+#endif
+}
+
+#ifdef __i386__
+static void (*vsyscall32)(void);
+
+/*
+ * Nasty helper to invoke AT_SYSINFO (i.e. __kernel_vsyscall) with
+ * controlled regs and capture the final regs.  This is so nasty that it
+ * crashes my copy of gdb :)
+ */
+static void do_full_vsyscall32(struct syscall_args32 *args)
+{
+       sys32_helper(args, vsyscall32);
+}
+#endif
+
+static siginfo_t wait_trap(pid_t chld)
+{
+       siginfo_t si;
+       if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0)
+               err(1, "waitid");
+       if (si.si_pid != chld)
+               errx(1, "got unexpected pid in event\n");
+       if (si.si_code != CLD_TRAPPED)
+               errx(1, "got unexpected event type %d\n", si.si_code);
+       return si;
+}
+
+static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+                      int flags)
+{
+       struct sigaction sa;
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_sigaction = handler;
+       sa.sa_flags = SA_SIGINFO | flags;
+       sigemptyset(&sa.sa_mask);
+       if (sigaction(sig, &sa, 0))
+               err(1, "sigaction");
+}
+
+static void clearhandler(int sig)
+{
+       struct sigaction sa;
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = SIG_DFL;
+       sigemptyset(&sa.sa_mask);
+       if (sigaction(sig, &sa, 0))
+               err(1, "sigaction");
+}
+
+#ifdef __x86_64__
+# define REG_BP REG_RBP
+#else
+# define REG_BP REG_EBP
+#endif
+
+static void empty_handler(int sig, siginfo_t *si, void *ctx_void)
+{
+}
+
+static void test_sys32_regs(void (*do_syscall)(struct syscall_args32 *))
+{
+       struct syscall_args32 args = {
+               .nr = 224,      /* gettid */
+               .arg0 = 10, .arg1 = 11, .arg2 = 12,
+               .arg3 = 13, .arg4 = 14, .arg5 = 15,
+       };
+
+       do_syscall(&args);
+
+       if (args.nr != getpid() ||
+           args.arg0 != 10 || args.arg1 != 11 || args.arg2 != 12 ||
+           args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) {
+               printf("[FAIL]\tgetpid() failed to preseve regs\n");
+               nerrs++;
+       } else {
+               printf("[OK]\tgetpid() preserves regs\n");
+       }
+
+       sethandler(SIGUSR1, empty_handler, 0);
+
+       args.nr = 37;   /* kill */
+       args.arg0 = getpid();
+       args.arg1 = SIGUSR1;
+       do_syscall(&args);
+       if (args.nr != 0 ||
+           args.arg0 != getpid() || args.arg1 != SIGUSR1 || args.arg2 != 12 ||
+           args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) {
+               printf("[FAIL]\tkill(getpid(), SIGUSR1) failed to preseve regs\n");
+               nerrs++;
+       } else {
+               printf("[OK]\tkill(getpid(), SIGUSR1) preserves regs\n");
+       }
+       clearhandler(SIGUSR1);
+}
+
+static void test_ptrace_syscall_restart(void)
+{
+       printf("[RUN]\tptrace-induced syscall restart\n");
+       pid_t chld = fork();
+       if (chld < 0)
+               err(1, "fork");
+
+       if (chld == 0) {
+               if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
+                       err(1, "PTRACE_TRACEME");
+
+               printf("\tChild will make one syscall\n");
+               raise(SIGSTOP);
+
+               syscall(SYS_gettid, 10, 11, 12, 13, 14, 15);
+               _exit(0);
+       }
+
+       int status;
+
+       /* Wait for SIGSTOP. */
+       if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
+               err(1, "waitpid");
+
+       struct user_regs_struct regs;
+
+       printf("[RUN]\tSYSEMU\n");
+       if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
+               err(1, "PTRACE_SYSCALL");
+       wait_trap(chld);
+
+       if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
+               err(1, "PTRACE_GETREGS");
+
+       if (regs.user_syscall_nr != SYS_gettid ||
+           regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
+           regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
+           regs.user_arg4 != 14 || regs.user_arg5 != 15) {
+               printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
+               nerrs++;
+       } else {
+               printf("[OK]\tInitial nr and args are correct\n");
+       }
+
+       printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n",
+              (unsigned long)regs.user_ip);
+
+       /*
+        * This does exactly what it appears to do if syscall is int80 or
+        * SYSCALL64.  For SYSCALL32 or SYSENTER, though, this is highly
+        * magical.  It needs to work so that ptrace and syscall restart
+        * work as expected.
+        */
+       regs.user_ax = regs.user_syscall_nr;
+       regs.user_ip -= 2;
+       if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
+               err(1, "PTRACE_SETREGS");
+
+       if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
+               err(1, "PTRACE_SYSCALL");
+       wait_trap(chld);
+
+       if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
+               err(1, "PTRACE_GETREGS");
+
+       if (regs.user_syscall_nr != SYS_gettid ||
+           regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
+           regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
+           regs.user_arg4 != 14 || regs.user_arg5 != 15) {
+               printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
+               nerrs++;
+       } else {
+               printf("[OK]\tRestarted nr and args are correct\n");
+       }
+
+       printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n",
+              (unsigned long)regs.user_ip);
+
+       regs.user_ax = SYS_getpid;
+       regs.user_arg0 = 20;
+       regs.user_arg1 = 21;
+       regs.user_arg2 = 22;
+       regs.user_arg3 = 23;
+       regs.user_arg4 = 24;
+       regs.user_arg5 = 25;
+       regs.user_ip -= 2;
+
+       if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
+               err(1, "PTRACE_SETREGS");
+
+       if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
+               err(1, "PTRACE_SYSCALL");
+       wait_trap(chld);
+
+       if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
+               err(1, "PTRACE_GETREGS");
+
+       if (regs.user_syscall_nr != SYS_getpid ||
+           regs.user_arg0 != 20 || regs.user_arg1 != 21 || regs.user_arg2 != 22 ||
+           regs.user_arg3 != 23 || regs.user_arg4 != 24 || regs.user_arg5 != 25) {
+               printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
+               nerrs++;
+       } else {
+               printf("[OK]\tReplacement nr and args are correct\n");
+       }
+
+       if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
+               err(1, "PTRACE_CONT");
+       if (waitpid(chld, &status, 0) != chld)
+               err(1, "waitpid");
+       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+               printf("[FAIL]\tChild failed\n");
+               nerrs++;
+       } else {
+               printf("[OK]\tChild exited cleanly\n");
+       }
+}
+
+int main()
+{
+       printf("[RUN]\tCheck int80 return regs\n");
+       test_sys32_regs(do_full_int80);
+
+#if defined(__i386__) && (!defined(__GLIBC__) || __GLIBC__ > 2 || __GLIBC_MINOR__ >= 16)
+       vsyscall32 = (void *)getauxval(AT_SYSINFO);
+       printf("[RUN]\tCheck AT_SYSINFO return regs\n");
+       test_sys32_regs(do_full_vsyscall32);
+#endif
+
+       test_ptrace_syscall_restart();
+
+       return 0;
+}
diff --git a/tools/testing/selftests/x86/raw_syscall_helper_32.S b/tools/testing/selftests/x86/raw_syscall_helper_32.S
new file mode 100644 (file)
index 0000000..534e71e
--- /dev/null
@@ -0,0 +1,46 @@
+.global sys32_helper
+sys32_helper:
+       /* Args: syscall_args_32*, function pointer */
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %esi
+       pushl   %edi
+       movl    5*4(%esp), %eax /* pointer to args struct */
+
+       movl    1*4(%eax), %ebx
+       movl    2*4(%eax), %ecx
+       movl    3*4(%eax), %edx
+       movl    4*4(%eax), %esi
+       movl    5*4(%eax), %edi
+       movl    6*4(%eax), %ebp
+       movl    0*4(%eax), %eax
+
+       call    *(6*4)(%esp)    /* Do the syscall */
+
+       /* Now we need to recover without losing any reg values */
+       pushl   %eax
+       movl    6*4(%esp), %eax
+       popl    0*4(%eax)
+       movl    %ebx, 1*4(%eax)
+       movl    %ecx, 2*4(%eax)
+       movl    %edx, 3*4(%eax)
+       movl    %esi, 4*4(%eax)
+       movl    %edi, 5*4(%eax)
+       movl    %ebp, 6*4(%eax)
+
+       popl    %edi
+       popl    %esi
+       popl    %ebx
+       popl    %ebp
+       ret
+
+       .type sys32_helper, @function
+       .size sys32_helper, .-sys32_helper
+
+.global int80_and_ret
+int80_and_ret:
+       int     $0x80
+       ret
+
+       .type int80_and_ret, @function
+       .size int80_and_ret, .-int80_and_ret
diff --git a/tools/testing/selftests/x86/test_FCMOV.c b/tools/testing/selftests/x86/test_FCMOV.c
new file mode 100644 (file)
index 0000000..4adcca0
--- /dev/null
@@ -0,0 +1,93 @@
+#undef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#undef __USE_GNU
+#define __USE_GNU 1
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#define TEST(insn) \
+long double __attribute__((noinline)) insn(long flags) \
+{                                              \
+       long double out;                        \
+       asm ("\n"                               \
+       "       push    %1""\n"                 \
+       "       popf""\n"                       \
+       "       fldpi""\n"                      \
+       "       fld1""\n"                       \
+       "       " #insn " %%st(1), %%st" "\n"   \
+       "       ffree   %%st(1)" "\n"           \
+       : "=t" (out)                            \
+       : "r" (flags)                           \
+       );                                      \
+       return out;                             \
+}
+
+TEST(fcmovb)
+TEST(fcmove)
+TEST(fcmovbe)
+TEST(fcmovu)
+TEST(fcmovnb)
+TEST(fcmovne)
+TEST(fcmovnbe)
+TEST(fcmovnu)
+
+enum {
+       CF = 1 << 0,
+       PF = 1 << 2,
+       ZF = 1 << 6,
+};
+
+void sighandler(int sig)
+{
+       printf("[FAIL]\tGot signal %d, exiting\n", sig);
+       exit(1);
+}
+
+int main(int argc, char **argv, char **envp)
+{
+       int err = 0;
+
+       /* SIGILL triggers on 32-bit kernels w/o fcomi emulation
+        * when run with "no387 nofxsr". Other signals are caught
+        * just in case.
+        */
+       signal(SIGILL, sighandler);
+       signal(SIGFPE, sighandler);
+       signal(SIGSEGV, sighandler);
+
+       printf("[RUN]\tTesting fcmovCC instructions\n");
+       /* If fcmovCC() returns 1.0, the move wasn't done */
+       err |= !(fcmovb(0)   == 1.0); err |= !(fcmovnb(0)  != 1.0);
+       err |= !(fcmove(0)   == 1.0); err |= !(fcmovne(0)  != 1.0);
+       err |= !(fcmovbe(0)  == 1.0); err |= !(fcmovnbe(0) != 1.0);
+       err |= !(fcmovu(0)   == 1.0); err |= !(fcmovnu(0)  != 1.0);
+
+       err |= !(fcmovb(CF)  != 1.0); err |= !(fcmovnb(CF)  == 1.0);
+       err |= !(fcmove(CF)  == 1.0); err |= !(fcmovne(CF)  != 1.0);
+       err |= !(fcmovbe(CF) != 1.0); err |= !(fcmovnbe(CF) == 1.0);
+       err |= !(fcmovu(CF)  == 1.0); err |= !(fcmovnu(CF)  != 1.0);
+
+       err |= !(fcmovb(ZF)  == 1.0); err |= !(fcmovnb(ZF)  != 1.0);
+       err |= !(fcmove(ZF)  != 1.0); err |= !(fcmovne(ZF)  == 1.0);
+       err |= !(fcmovbe(ZF) != 1.0); err |= !(fcmovnbe(ZF) == 1.0);
+       err |= !(fcmovu(ZF)  == 1.0); err |= !(fcmovnu(ZF)  != 1.0);
+
+       err |= !(fcmovb(PF)  == 1.0); err |= !(fcmovnb(PF)  != 1.0);
+       err |= !(fcmove(PF)  == 1.0); err |= !(fcmovne(PF)  != 1.0);
+       err |= !(fcmovbe(PF) == 1.0); err |= !(fcmovnbe(PF) != 1.0);
+       err |= !(fcmovu(PF)  != 1.0); err |= !(fcmovnu(PF)  == 1.0);
+
+        if (!err)
+                printf("[OK]\tfcmovCC\n");
+       else
+               printf("[FAIL]\tfcmovCC errors: %d\n", err);
+
+       return err;
+}
diff --git a/tools/testing/selftests/x86/test_FCOMI.c b/tools/testing/selftests/x86/test_FCOMI.c
new file mode 100644 (file)
index 0000000..db4933e
--- /dev/null
@@ -0,0 +1,331 @@
+#undef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#undef __USE_GNU
+#define __USE_GNU 1
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <fenv.h>
+
+enum {
+       CF = 1 << 0,
+       PF = 1 << 2,
+       ZF = 1 << 6,
+       ARITH = CF | PF | ZF,
+};
+
+long res_fcomi_pi_1;
+long res_fcomi_1_pi;
+long res_fcomi_1_1;
+long res_fcomi_nan_1;
+/* sNaN is s|111 1111 1|1xx xxxx xxxx xxxx xxxx xxxx */
+/* qNaN is s|111 1111 1|0xx xxxx xxxx xxxx xxxx xxxx (some x must be nonzero) */
+int snan = 0x7fc11111;
+int qnan = 0x7f811111;
+unsigned short snan1[5];
+/* sNaN80 is s|111 1111 1111 1111 |10xx xx...xx (some x must be nonzero) */
+unsigned short snan80[5] = { 0x1111, 0x1111, 0x1111, 0x8111, 0x7fff };
+
+int test(long flags)
+{
+       feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+
+       asm ("\n"
+
+       "       push    %0""\n"
+       "       popf""\n"
+       "       fld1""\n"
+       "       fldpi""\n"
+       "       fcomi   %%st(1), %%st" "\n"
+       "       ffree   %%st(0)" "\n"
+       "       ffree   %%st(1)" "\n"
+       "       pushf""\n"
+       "       pop     res_fcomi_1_pi""\n"
+
+       "       push    %0""\n"
+       "       popf""\n"
+       "       fldpi""\n"
+       "       fld1""\n"
+       "       fcomi   %%st(1), %%st" "\n"
+       "       ffree   %%st(0)" "\n"
+       "       ffree   %%st(1)" "\n"
+       "       pushf""\n"
+       "       pop     res_fcomi_pi_1""\n"
+
+       "       push    %0""\n"
+       "       popf""\n"
+       "       fld1""\n"
+       "       fld1""\n"
+       "       fcomi   %%st(1), %%st" "\n"
+       "       ffree   %%st(0)" "\n"
+       "       ffree   %%st(1)" "\n"
+       "       pushf""\n"
+       "       pop     res_fcomi_1_1""\n"
+       :
+       : "r" (flags)
+       );
+       if ((res_fcomi_1_pi & ARITH) != (0)) {
+               printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags);
+               return 1;
+       }
+       if ((res_fcomi_pi_1 & ARITH) != (CF)) {
+               printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH);
+               return 1;
+       }
+       if ((res_fcomi_1_1 & ARITH) != (ZF)) {
+               printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags);
+               return 1;
+       }
+       if (fetestexcept(FE_INVALID) != 0) {
+               printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
+               return 1;
+       }
+       return 0;
+}
+
+int test_qnan(long flags)
+{
+       feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+
+       asm ("\n"
+       "       push    %0""\n"
+       "       popf""\n"
+       "       flds    qnan""\n"
+       "       fld1""\n"
+       "       fnclex""\n"             // fld of a qnan raised FE_INVALID, clear it
+       "       fcomi   %%st(1), %%st" "\n"
+       "       ffree   %%st(0)" "\n"
+       "       ffree   %%st(1)" "\n"
+       "       pushf""\n"
+       "       pop     res_fcomi_nan_1""\n"
+       :
+       : "r" (flags)
+       );
+       if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
+               printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
+               return 1;
+       }
+       if (fetestexcept(FE_INVALID) != FE_INVALID) {
+               printf("[BAD]\tFE_INVALID is not set in %s\n", __func__);
+               return 1;
+       }
+       return 0;
+}
+
+int testu_qnan(long flags)
+{
+       feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+
+       asm ("\n"
+       "       push    %0""\n"
+       "       popf""\n"
+       "       flds    qnan""\n"
+       "       fld1""\n"
+       "       fnclex""\n"             // fld of a qnan raised FE_INVALID, clear it
+       "       fucomi  %%st(1), %%st" "\n"
+       "       ffree   %%st(0)" "\n"
+       "       ffree   %%st(1)" "\n"
+       "       pushf""\n"
+       "       pop     res_fcomi_nan_1""\n"
+       :
+       : "r" (flags)
+       );
+       if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
+               printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
+               return 1;
+       }
+       if (fetestexcept(FE_INVALID) != 0) {
+               printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
+               return 1;
+       }
+       return 0;
+}
+
+int testu_snan(long flags)
+{
+       feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+
+       asm ("\n"
+       "       push    %0""\n"
+       "       popf""\n"
+//     "       flds    snan""\n"       // WRONG, this will convert 32-bit fp snan to a *qnan* in 80-bit fp register!
+//     "       fstpt   snan1""\n"      // if uncommented, it prints "snan1:7fff c111 1100 0000 0000" - c111, not 8111!
+//     "       fnclex""\n"             // flds of a snan raised FE_INVALID, clear it
+       "       fldt    snan80""\n"     // fldt never raise FE_INVALID
+       "       fld1""\n"
+       "       fucomi  %%st(1), %%st" "\n"
+       "       ffree   %%st(0)" "\n"
+       "       ffree   %%st(1)" "\n"
+       "       pushf""\n"
+       "       pop     res_fcomi_nan_1""\n"
+       :
+       : "r" (flags)
+       );
+       if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
+               printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
+               return 1;
+       }
+//     printf("snan:%x snan1:%04x %04x %04x %04x %04x\n", snan, snan1[4], snan1[3], snan1[2], snan1[1], snan1[0]);
+       if (fetestexcept(FE_INVALID) != FE_INVALID) {
+               printf("[BAD]\tFE_INVALID is not set in %s\n", __func__);
+               return 1;
+       }
+       return 0;
+}
+
+int testp(long flags)
+{
+       feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+
+       asm ("\n"
+
+       "       push    %0""\n"
+       "       popf""\n"
+       "       fld1""\n"
+       "       fldpi""\n"
+       "       fcomip  %%st(1), %%st" "\n"
+       "       ffree   %%st(0)" "\n"
+       "       pushf""\n"
+       "       pop     res_fcomi_1_pi""\n"
+
+       "       push    %0""\n"
+       "       popf""\n"
+       "       fldpi""\n"
+       "       fld1""\n"
+       "       fcomip  %%st(1), %%st" "\n"
+       "       ffree   %%st(0)" "\n"
+       "       pushf""\n"
+       "       pop     res_fcomi_pi_1""\n"
+
+       "       push    %0""\n"
+       "       popf""\n"
+       "       fld1""\n"
+       "       fld1""\n"
+       "       fcomip  %%st(1), %%st" "\n"
+       "       ffree   %%st(0)" "\n"
+       "       pushf""\n"
+       "       pop     res_fcomi_1_1""\n"
+       :
+       : "r" (flags)
+       );
+       if ((res_fcomi_1_pi & ARITH) != (0)) {
+               printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags);
+               return 1;
+       }
+       if ((res_fcomi_pi_1 & ARITH) != (CF)) {
+               printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH);
+               return 1;
+       }
+       if ((res_fcomi_1_1 & ARITH) != (ZF)) {
+               printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags);
+               return 1;
+       }
+       if (fetestexcept(FE_INVALID) != 0) {
+               printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
+               return 1;
+       }
+       return 0;
+}
+
+int testp_qnan(long flags)
+{
+       feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+
+       asm ("\n"
+       "       push    %0""\n"
+       "       popf""\n"
+       "       flds    qnan""\n"
+       "       fld1""\n"
+       "       fnclex""\n"             // fld of a qnan raised FE_INVALID, clear it
+       "       fcomip  %%st(1), %%st" "\n"
+       "       ffree   %%st(0)" "\n"
+       "       pushf""\n"
+       "       pop     res_fcomi_nan_1""\n"
+       :
+       : "r" (flags)
+       );
+       if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
+               printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
+               return 1;
+       }
+       if (fetestexcept(FE_INVALID) != FE_INVALID) {
+               printf("[BAD]\tFE_INVALID is not set in %s\n", __func__);
+               return 1;
+       }
+       return 0;
+}
+
+int testup_qnan(long flags)
+{
+       feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+
+       asm ("\n"
+       "       push    %0""\n"
+       "       popf""\n"
+       "       flds    qnan""\n"
+       "       fld1""\n"
+       "       fnclex""\n"             // fld of a qnan raised FE_INVALID, clear it
+       "       fucomip %%st(1), %%st" "\n"
+       "       ffree   %%st(0)" "\n"
+       "       pushf""\n"
+       "       pop     res_fcomi_nan_1""\n"
+       :
+       : "r" (flags)
+       );
+       if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
+               printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
+               return 1;
+       }
+       if (fetestexcept(FE_INVALID) != 0) {
+               printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
+               return 1;
+       }
+       return 0;
+}
+
+void sighandler(int sig)
+{
+       printf("[FAIL]\tGot signal %d, exiting\n", sig);
+       exit(1);
+}
+
+int main(int argc, char **argv, char **envp)
+{
+       int err = 0;
+
+       /* SIGILL triggers on 32-bit kernels w/o fcomi emulation
+        * when run with "no387 nofxsr". Other signals are caught
+        * just in case.
+        */
+       signal(SIGILL, sighandler);
+       signal(SIGFPE, sighandler);
+       signal(SIGSEGV, sighandler);
+
+       printf("[RUN]\tTesting f[u]comi[p] instructions\n");
+       err |= test(0);
+       err |= test_qnan(0);
+       err |= testu_qnan(0);
+       err |= testu_snan(0);
+       err |= test(CF|ZF|PF);
+       err |= test_qnan(CF|ZF|PF);
+       err |= testu_qnan(CF|ZF|PF);
+       err |= testu_snan(CF|ZF|PF);
+       err |= testp(0);
+       err |= testp_qnan(0);
+       err |= testup_qnan(0);
+       err |= testp(CF|ZF|PF);
+       err |= testp_qnan(CF|ZF|PF);
+       err |= testup_qnan(CF|ZF|PF);
+       if (!err)
+               printf("[OK]\tf[u]comi[p]\n");
+       else
+               printf("[FAIL]\tf[u]comi[p] errors: %d\n", err);
+
+       return err;
+}
diff --git a/tools/testing/selftests/x86/test_FISTTP.c b/tools/testing/selftests/x86/test_FISTTP.c
new file mode 100644 (file)
index 0000000..b8e61a0
--- /dev/null
@@ -0,0 +1,137 @@
+#undef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#undef __USE_GNU
+#define __USE_GNU 1
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <fenv.h>
+
+unsigned long long res64 = -1;
+unsigned int res32 = -1;
+unsigned short res16 = -1;
+
+int test(void)
+{
+       int ex;
+
+       feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+       asm volatile ("\n"
+       "       fld1""\n"
+       "       fisttp  res16""\n"
+       "       fld1""\n"
+       "       fisttpl res32""\n"
+       "       fld1""\n"
+       "       fisttpll res64""\n"
+       : : : "memory"
+       );
+       if (res16 != 1 || res32 != 1 || res64 != 1) {
+               printf("[BAD]\tfisttp 1\n");
+               return 1;
+       }
+       ex = fetestexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+       if (ex != 0) {
+               printf("[BAD]\tfisttp 1: wrong exception state\n");
+               return 1;
+       }
+
+       feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+       asm volatile ("\n"
+       "       fldpi""\n"
+       "       fisttp  res16""\n"
+       "       fldpi""\n"
+       "       fisttpl res32""\n"
+       "       fldpi""\n"
+       "       fisttpll res64""\n"
+       : : : "memory"
+       );
+       if (res16 != 3 || res32 != 3 || res64 != 3) {
+               printf("[BAD]\tfisttp pi\n");
+               return 1;
+       }
+       ex = fetestexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+       if (ex != FE_INEXACT) {
+               printf("[BAD]\tfisttp pi: wrong exception state\n");
+               return 1;
+       }
+
+       feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+       asm volatile ("\n"
+       "       fldpi""\n"
+       "       fchs""\n"
+       "       fisttp  res16""\n"
+       "       fldpi""\n"
+       "       fchs""\n"
+       "       fisttpl res32""\n"
+       "       fldpi""\n"
+       "       fchs""\n"
+       "       fisttpll res64""\n"
+       : : : "memory"
+       );
+       if (res16 != 0xfffd || res32 != 0xfffffffd || res64 != 0xfffffffffffffffdULL) {
+               printf("[BAD]\tfisttp -pi\n");
+               return 1;
+       }
+       ex = fetestexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+       if (ex != FE_INEXACT) {
+               printf("[BAD]\tfisttp -pi: wrong exception state\n");
+               return 1;
+       }
+
+       feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+       asm volatile ("\n"
+       "       fldln2""\n"
+       "       fisttp  res16""\n"
+       "       fldln2""\n"
+       "       fisttpl res32""\n"
+       "       fldln2""\n"
+       "       fisttpll res64""\n"
+       : : : "memory"
+       );
+       /* Test truncation to zero (round-to-nearest would give 1 here) */
+       if (res16 != 0 || res32 != 0 || res64 != 0) {
+               printf("[BAD]\tfisttp ln2\n");
+               return 1;
+       }
+       ex = fetestexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
+       if (ex != FE_INEXACT) {
+               printf("[BAD]\tfisttp ln2: wrong exception state\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+void sighandler(int sig)
+{
+       printf("[FAIL]\tGot signal %d, exiting\n", sig);
+       exit(1);
+}
+
+int main(int argc, char **argv, char **envp)
+{
+       int err = 0;
+
+       /* SIGILL triggers on 32-bit kernels w/o fisttp emulation
+        * when run with "no387 nofxsr". Other signals are caught
+        * just in case.
+        */
+       signal(SIGILL, sighandler);
+       signal(SIGFPE, sighandler);
+       signal(SIGSEGV, sighandler);
+
+       printf("[RUN]\tTesting fisttp instructions\n");
+       err |= test();
+       if (!err)
+               printf("[OK]\tfisttp\n");
+       else
+               printf("[FAIL]\tfisttp errors: %d\n", err);
+
+       return err;
+}
diff --git a/tools/testing/selftests/x86/test_syscall_vdso.c b/tools/testing/selftests/x86/test_syscall_vdso.c
new file mode 100644 (file)
index 0000000..4037035
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * 32-bit syscall ABI conformance test.
+ *
+ * Copyright (c) 2015 Denys Vlasenko
+ *
+ * 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.
+ */
+/*
+ * Can be built statically:
+ * gcc -Os -Wall -static -m32 test_syscall_vdso.c thunks_32.S
+ */
+#undef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#undef __USE_GNU
+#define __USE_GNU 1
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <elf.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+
+#if !defined(__i386__)
+int main(int argc, char **argv, char **envp)
+{
+       printf("[SKIP]\tNot a 32-bit x86 userspace\n");
+       return 0;
+}
+#else
+
+long syscall_addr;
+long get_syscall(char **envp)
+{
+       Elf32_auxv_t *auxv;
+       while (*envp++ != NULL)
+               continue;
+       for (auxv = (void *)envp; auxv->a_type != AT_NULL; auxv++)
+               if (auxv->a_type == AT_SYSINFO)
+                       return auxv->a_un.a_val;
+       printf("[WARN]\tAT_SYSINFO not supplied\n");
+       return 0;
+}
+
+asm (
+       "       .pushsection .text\n"
+       "       .global int80\n"
+       "int80:\n"
+       "       int     $0x80\n"
+       "       ret\n"
+       "       .popsection\n"
+);
+extern char int80;
+
+struct regs64 {
+       uint64_t rax, rbx, rcx, rdx;
+       uint64_t rsi, rdi, rbp, rsp;
+       uint64_t r8,  r9,  r10, r11;
+       uint64_t r12, r13, r14, r15;
+};
+struct regs64 regs64;
+int kernel_is_64bit;
+
+asm (
+       "       .pushsection .text\n"
+       "       .code64\n"
+       "get_regs64:\n"
+       "       push    %rax\n"
+       "       mov     $regs64, %eax\n"
+       "       pop     0*8(%rax)\n"
+       "       movq    %rbx, 1*8(%rax)\n"
+       "       movq    %rcx, 2*8(%rax)\n"
+       "       movq    %rdx, 3*8(%rax)\n"
+       "       movq    %rsi, 4*8(%rax)\n"
+       "       movq    %rdi, 5*8(%rax)\n"
+       "       movq    %rbp, 6*8(%rax)\n"
+       "       movq    %rsp, 7*8(%rax)\n"
+       "       movq    %r8,  8*8(%rax)\n"
+       "       movq    %r9,  9*8(%rax)\n"
+       "       movq    %r10, 10*8(%rax)\n"
+       "       movq    %r11, 11*8(%rax)\n"
+       "       movq    %r12, 12*8(%rax)\n"
+       "       movq    %r13, 13*8(%rax)\n"
+       "       movq    %r14, 14*8(%rax)\n"
+       "       movq    %r15, 15*8(%rax)\n"
+       "       ret\n"
+       "poison_regs64:\n"
+       "       movq    $0x7f7f7f7f, %r8\n"
+       "       shl     $32, %r8\n"
+       "       orq     $0x7f7f7f7f, %r8\n"
+       "       movq    %r8, %r9\n"
+       "       movq    %r8, %r10\n"
+       "       movq    %r8, %r11\n"
+       "       movq    %r8, %r12\n"
+       "       movq    %r8, %r13\n"
+       "       movq    %r8, %r14\n"
+       "       movq    %r8, %r15\n"
+       "       ret\n"
+       "       .code32\n"
+       "       .popsection\n"
+);
+extern void get_regs64(void);
+extern void poison_regs64(void);
+extern unsigned long call64_from_32(void (*function)(void));
+void print_regs64(void)
+{
+       if (!kernel_is_64bit)
+               return;
+       printf("ax:%016llx bx:%016llx cx:%016llx dx:%016llx\n", regs64.rax,  regs64.rbx,  regs64.rcx,  regs64.rdx);
+       printf("si:%016llx di:%016llx bp:%016llx sp:%016llx\n", regs64.rsi,  regs64.rdi,  regs64.rbp,  regs64.rsp);
+       printf(" 8:%016llx  9:%016llx 10:%016llx 11:%016llx\n", regs64.r8 ,  regs64.r9 ,  regs64.r10,  regs64.r11);
+       printf("12:%016llx 13:%016llx 14:%016llx 15:%016llx\n", regs64.r12,  regs64.r13,  regs64.r14,  regs64.r15);
+}
+
+int check_regs64(void)
+{
+       int err = 0;
+       int num = 8;
+       uint64_t *r64 = &regs64.r8;
+
+       if (!kernel_is_64bit)
+               return 0;
+
+       do {
+               if (*r64 == 0x7f7f7f7f7f7f7f7fULL)
+                       continue; /* register did not change */
+               if (syscall_addr != (long)&int80) {
+                       /*
+                        * Non-INT80 syscall entrypoints are allowed to clobber R8+ regs:
+                        * either clear them to 0, or for R11, load EFLAGS.
+                        */
+                       if (*r64 == 0)
+                               continue;
+                       if (num == 11) {
+                               printf("[NOTE]\tR11 has changed:%016llx - assuming clobbered by SYSRET insn\n", *r64);
+                               continue;
+                       }
+               } else {
+                       /* INT80 syscall entrypoint can be used by
+                        * 64-bit programs too, unlike SYSCALL/SYSENTER.
+                        * Therefore it must preserve R12+
+                        * (they are callee-saved registers in 64-bit C ABI).
+                        *
+                        * This was probably historically not intended,
+                        * but R8..11 are clobbered (cleared to 0).
+                        * IOW: they are the only registers which aren't
+                        * preserved across INT80 syscall.
+                        */
+                       if (*r64 == 0 && num <= 11)
+                               continue;
+               }
+               printf("[FAIL]\tR%d has changed:%016llx\n", num, *r64);
+               err++;
+       } while (r64++, ++num < 16);
+
+       if (!err)
+               printf("[OK]\tR8..R15 did not leak kernel data\n");
+       return err;
+}
+
+int nfds;
+fd_set rfds;
+fd_set wfds;
+fd_set efds;
+struct timespec timeout;
+sigset_t sigmask;
+struct {
+       sigset_t *sp;
+       int sz;
+} sigmask_desc;
+
+void prep_args()
+{
+       nfds = 42;
+       FD_ZERO(&rfds);
+       FD_ZERO(&wfds);
+       FD_ZERO(&efds);
+       FD_SET(0, &rfds);
+       FD_SET(1, &wfds);
+       FD_SET(2, &efds);
+       timeout.tv_sec = 0;
+       timeout.tv_nsec = 123;
+       sigemptyset(&sigmask);
+       sigaddset(&sigmask, SIGINT);
+       sigaddset(&sigmask, SIGUSR2);
+       sigaddset(&sigmask, SIGRTMAX);
+       sigmask_desc.sp = &sigmask;
+       sigmask_desc.sz = 8; /* bytes */
+}
+
+static void print_flags(const char *name, unsigned long r)
+{
+       static const char *bitarray[] = {
+       "\n" ,"c\n" ,/* Carry Flag */
+       "0 " ,"1 "  ,/* Bit 1 - always on */
+       ""   ,"p "  ,/* Parity Flag */
+       "0 " ,"3? " ,
+       ""   ,"a "  ,/* Auxiliary carry Flag */
+       "0 " ,"5? " ,
+       ""   ,"z "  ,/* Zero Flag */
+       ""   ,"s "  ,/* Sign Flag */
+       ""   ,"t "  ,/* Trap Flag */
+       ""   ,"i "  ,/* Interrupt Flag */
+       ""   ,"d "  ,/* Direction Flag */
+       ""   ,"o "  ,/* Overflow Flag */
+       "0 " ,"1 "  ,/* I/O Privilege Level (2 bits) */
+       "0"  ,"1"   ,/* I/O Privilege Level (2 bits) */
+       ""   ,"n "  ,/* Nested Task */
+       "0 " ,"15? ",
+       ""   ,"r "  ,/* Resume Flag */
+       ""   ,"v "  ,/* Virtual Mode */
+       ""   ,"ac " ,/* Alignment Check/Access Control */
+       ""   ,"vif ",/* Virtual Interrupt Flag */
+       ""   ,"vip ",/* Virtual Interrupt Pending */
+       ""   ,"id " ,/* CPUID detection */
+       NULL
+       };
+       const char **bitstr;
+       int bit;
+
+       printf("%s=%016lx ", name, r);
+       bitstr = bitarray + 42;
+       bit = 21;
+       if ((r >> 22) != 0)
+               printf("(extra bits are set) ");
+       do {
+               if (bitstr[(r >> bit) & 1][0])
+                       fputs(bitstr[(r >> bit) & 1], stdout);
+               bitstr -= 2;
+               bit--;
+       } while (bit >= 0);
+}
+
+int run_syscall(void)
+{
+       long flags, bad_arg;
+
+       prep_args();
+
+       if (kernel_is_64bit)
+               call64_from_32(poison_regs64);
+       /*print_regs64();*/
+
+       asm("\n"
+       /* Try 6-arg syscall: pselect. It should return quickly */
+       "       push    %%ebp\n"
+       "       mov     $308, %%eax\n"     /* PSELECT */
+       "       mov     nfds, %%ebx\n"     /* ebx  arg1 */
+       "       mov     $rfds, %%ecx\n"    /* ecx  arg2 */
+       "       mov     $wfds, %%edx\n"    /* edx  arg3 */
+       "       mov     $efds, %%esi\n"    /* esi  arg4 */
+       "       mov     $timeout, %%edi\n" /* edi  arg5 */
+       "       mov     $sigmask_desc, %%ebp\n" /* %ebp arg6 */
+       "       push    $0x200ed7\n"      /* set almost all flags */
+       "       popf\n"         /* except TF, IOPL, NT, RF, VM, AC, VIF, VIP */
+       "       call    *syscall_addr\n"
+       /* Check that registers are not clobbered */
+       "       pushf\n"
+       "       pop     %%eax\n"
+       "       cld\n"
+       "       cmp     nfds, %%ebx\n"     /* ebx  arg1 */
+       "       mov     $1, %%ebx\n"
+       "       jne     1f\n"
+       "       cmp     $rfds, %%ecx\n"    /* ecx  arg2 */
+       "       mov     $2, %%ebx\n"
+       "       jne     1f\n"
+       "       cmp     $wfds, %%edx\n"    /* edx  arg3 */
+       "       mov     $3, %%ebx\n"
+       "       jne     1f\n"
+       "       cmp     $efds, %%esi\n"    /* esi  arg4 */
+       "       mov     $4, %%ebx\n"
+       "       jne     1f\n"
+       "       cmp     $timeout, %%edi\n" /* edi  arg5 */
+       "       mov     $5, %%ebx\n"
+       "       jne     1f\n"
+       "       cmpl    $sigmask_desc, %%ebp\n" /* %ebp arg6 */
+       "       mov     $6, %%ebx\n"
+       "       jne     1f\n"
+       "       mov     $0, %%ebx\n"
+       "1:\n"
+       "       pop     %%ebp\n"
+       : "=a" (flags), "=b" (bad_arg)
+       :
+       : "cx", "dx", "si", "di"
+       );
+
+       if (kernel_is_64bit) {
+               memset(&regs64, 0x77, sizeof(regs64));
+               call64_from_32(get_regs64);
+               /*print_regs64();*/
+       }
+
+       /*
+        * On paravirt kernels, flags are not preserved across syscalls.
+        * Thus, we do not consider it a bug if some are changed.
+        * We just show ones which do.
+        */
+       if ((0x200ed7 ^ flags) != 0) {
+               print_flags("[WARN]\tFlags before", 0x200ed7);
+               print_flags("[WARN]\tFlags  after", flags);
+               print_flags("[WARN]\tFlags change", (0x200ed7 ^ flags));
+       }
+
+       if (bad_arg) {
+               printf("[FAIL]\targ#%ld clobbered\n", bad_arg);
+               return 1;
+       }
+       printf("[OK]\tArguments are preserved across syscall\n");
+
+       return check_regs64();
+}
+
+int run_syscall_twice()
+{
+       int exitcode = 0;
+       long sv;
+
+       if (syscall_addr) {
+               printf("[RUN]\tExecuting 6-argument 32-bit syscall via VDSO\n");
+               exitcode = run_syscall();
+       }
+       sv = syscall_addr;
+       syscall_addr = (long)&int80;
+       printf("[RUN]\tExecuting 6-argument 32-bit syscall via INT 80\n");
+       exitcode += run_syscall();
+       syscall_addr = sv;
+       return exitcode;
+}
+
+void ptrace_me()
+{
+       pid_t pid;
+
+       fflush(NULL);
+       pid = fork();
+       if (pid < 0)
+               exit(1);
+       if (pid == 0) {
+               /* child */
+               if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) != 0)
+                       exit(0);
+               raise(SIGSTOP);
+               return;
+       }
+       /* parent */
+       printf("[RUN]\tRunning tests under ptrace\n");
+       while (1) {
+               int status;
+               pid = waitpid(-1, &status, __WALL);
+               if (WIFEXITED(status))
+                       exit(WEXITSTATUS(status));
+               if (WIFSIGNALED(status))
+                       exit(WTERMSIG(status));
+               if (pid <= 0 || !WIFSTOPPED(status)) /* paranoia */
+                       exit(255);
+               /*
+                * Note: we do not inject sig = WSTOPSIG(status).
+                * We probably should, but careful: do not inject SIGTRAP
+                * generated by syscall entry/exit stops.
+                * That kills the child.
+                */
+               ptrace(PTRACE_SYSCALL, pid, 0L, 0L /*sig*/);
+       }
+}
+
+int main(int argc, char **argv, char **envp)
+{
+       int exitcode = 0;
+       int cs;
+
+       asm("\n"
+       "       movl    %%cs, %%eax\n"
+       : "=a" (cs)
+       );
+       kernel_is_64bit = (cs == 0x23);
+       if (!kernel_is_64bit)
+               printf("[NOTE]\tNot a 64-bit kernel, won't test R8..R15 leaks\n");
+
+       /* This only works for non-static builds:
+        * syscall_addr = dlsym(dlopen("linux-gate.so.1", RTLD_NOW), "__kernel_vsyscall");
+        */
+       syscall_addr = get_syscall(envp);
+
+       exitcode += run_syscall_twice();
+       ptrace_me();
+       exitcode += run_syscall_twice();
+
+       return exitcode;
+}
+#endif
diff --git a/tools/testing/selftests/x86/thunks_32.S b/tools/testing/selftests/x86/thunks_32.S
new file mode 100644 (file)
index 0000000..29b644b
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * thunks_32.S - assembly helpers for mixed-bitness code
+ * Copyright (c) 2015 Denys Vlasenko
+ *
+ * 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.
+ *
+ * These are little helpers that make it easier to switch bitness on
+ * the fly.
+ */
+
+       .text
+       .code32
+
+       .global call64_from_32
+       .type call32_from_64, @function
+
+       // 4(%esp): function to call
+call64_from_32:
+       // Fetch function address
+       mov     4(%esp), %eax
+
+       // Save registers which are callee-clobbered by 64-bit ABI
+       push    %ecx
+       push    %edx
+       push    %esi
+       push    %edi
+
+       // Switch to long mode
+       jmp     $0x33,$1f
+1:     .code64
+
+       // Call the function
+       call    *%rax
+
+       // Switch to compatibility mode
+       push    $0x23  /* USER32_CS */
+       .code32; push $1f; .code64 /* hack: can't have X86_64_32S relocation in 32-bit ELF */
+       lretq
+1:     .code32
+
+       pop     %edi
+       pop     %esi
+       pop     %edx
+       pop     %ecx
+
+       ret
+
+.size call64_from_32, .-call64_from_32
diff --git a/tools/testing/selftests/x86/unwind_vdso.c b/tools/testing/selftests/x86/unwind_vdso.c
new file mode 100644 (file)
index 0000000..00a26a8
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * unwind_vdso.c - tests unwind info for AT_SYSINFO in the vDSO
+ * Copyright (c) 2014-2015 Andrew Lutomirski
+ *
+ * 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.
+ *
+ * This tests __kernel_vsyscall's unwind info.
+ */
+
+#define _GNU_SOURCE
+
+#include <features.h>
+#include <stdio.h>
+
+#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16
+
+int main()
+{
+       /* We need getauxval(). */
+       printf("[SKIP]\tGLIBC before 2.16 cannot compile this test\n");
+       return 0;
+}
+
+#else
+
+#include <sys/time.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <sys/ucontext.h>
+#include <err.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+#include <sys/ucontext.h>
+#include <link.h>
+#include <sys/auxv.h>
+#include <dlfcn.h>
+#include <unwind.h>
+
+static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+                      int flags)
+{
+       struct sigaction sa;
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_sigaction = handler;
+       sa.sa_flags = SA_SIGINFO | flags;
+       sigemptyset(&sa.sa_mask);
+       if (sigaction(sig, &sa, 0))
+               err(1, "sigaction");
+}
+
+#ifdef __x86_64__
+# define WIDTH "q"
+#else
+# define WIDTH "l"
+#endif
+
+static unsigned long get_eflags(void)
+{
+       unsigned long eflags;
+       asm volatile ("pushf" WIDTH "\n\tpop" WIDTH " %0" : "=rm" (eflags));
+       return eflags;
+}
+
+static void set_eflags(unsigned long eflags)
+{
+       asm volatile ("push" WIDTH " %0\n\tpopf" WIDTH
+                     : : "rm" (eflags) : "flags");
+}
+
+#define X86_EFLAGS_TF (1UL << 8)
+
+static volatile sig_atomic_t nerrs;
+static unsigned long sysinfo;
+static bool got_sysinfo = false;
+static unsigned long return_address;
+
+struct unwind_state {
+       unsigned long ip;       /* trap source */
+       int depth;              /* -1 until we hit the trap source */
+};
+
+_Unwind_Reason_Code trace_fn(struct _Unwind_Context * ctx, void *opaque)
+{
+       struct unwind_state *state = opaque;
+       unsigned long ip = _Unwind_GetIP(ctx);
+
+       if (state->depth == -1) {
+               if (ip == state->ip)
+                       state->depth = 0;
+               else
+                       return _URC_NO_REASON;  /* Not there yet */
+       }
+       printf("\t  0x%lx\n", ip);
+
+       if (ip == return_address) {
+               /* Here we are. */
+               unsigned long eax = _Unwind_GetGR(ctx, 0);
+               unsigned long ecx = _Unwind_GetGR(ctx, 1);
+               unsigned long edx = _Unwind_GetGR(ctx, 2);
+               unsigned long ebx = _Unwind_GetGR(ctx, 3);
+               unsigned long ebp = _Unwind_GetGR(ctx, 5);
+               unsigned long esi = _Unwind_GetGR(ctx, 6);
+               unsigned long edi = _Unwind_GetGR(ctx, 7);
+               bool ok = (eax == SYS_getpid || eax == getpid()) &&
+                       ebx == 1 && ecx == 2 && edx == 3 &&
+                       esi == 4 && edi == 5 && ebp == 6;
+
+               if (!ok)
+                       nerrs++;
+               printf("[%s]\t  NR = %ld, args = %ld, %ld, %ld, %ld, %ld, %ld\n",
+                      (ok ? "OK" : "FAIL"),
+                      eax, ebx, ecx, edx, esi, edi, ebp);
+
+               return _URC_NORMAL_STOP;
+       } else {
+               state->depth++;
+               return _URC_NO_REASON;
+       }
+}
+
+static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
+{
+       ucontext_t *ctx = (ucontext_t *)ctx_void;
+       struct unwind_state state;
+       unsigned long ip = ctx->uc_mcontext.gregs[REG_EIP];
+
+       if (!got_sysinfo && ip == sysinfo) {
+               got_sysinfo = true;
+
+               /* Find the return address. */
+               return_address = *(unsigned long *)(unsigned long)ctx->uc_mcontext.gregs[REG_ESP];
+
+               printf("\tIn vsyscall at 0x%lx, returning to 0x%lx\n",
+                      ip, return_address);
+       }
+
+       if (!got_sysinfo)
+               return;         /* Not there yet */
+
+       if (ip == return_address) {
+               ctx->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF;
+               printf("\tVsyscall is done\n");
+               return;
+       }
+
+       printf("\tSIGTRAP at 0x%lx\n", ip);
+
+       state.ip = ip;
+       state.depth = -1;
+       _Unwind_Backtrace(trace_fn, &state);
+}
+
+int main()
+{
+       sysinfo = getauxval(AT_SYSINFO);
+       printf("\tAT_SYSINFO is 0x%lx\n", sysinfo);
+
+       Dl_info info;
+       if (!dladdr((void *)sysinfo, &info)) {
+               printf("[WARN]\tdladdr failed on AT_SYSINFO\n");
+       } else {
+               printf("[OK]\tAT_SYSINFO maps to %s, loaded at 0x%p\n",
+                      info.dli_fname, info.dli_fbase);
+       }
+
+       sethandler(SIGTRAP, sigtrap, 0);
+
+       syscall(SYS_getpid);  /* Force symbol binding without TF set. */
+       printf("[RUN]\tSet TF and check a fast syscall\n");
+       set_eflags(get_eflags() | X86_EFLAGS_TF);
+       syscall(SYS_getpid, 1, 2, 3, 4, 5, 6);
+       if (!got_sysinfo) {
+               set_eflags(get_eflags() & ~X86_EFLAGS_TF);
+
+               /*
+                * The most likely cause of this is that you're on Debian or
+                * a Debian-based distro, you're missing libc6-i686, and you're
+                * affected by libc/19006 (https://sourceware.org/PR19006).
+                */
+               printf("[WARN]\tsyscall(2) didn't enter AT_SYSINFO\n");
+       }
+
+       if (get_eflags() & X86_EFLAGS_TF) {
+               printf("[FAIL]\tTF is still set\n");
+               nerrs++;
+       }
+
+       if (nerrs) {
+               printf("[FAIL]\tThere were errors\n");
+               return 1;
+       } else {
+               printf("[OK]\tAll is well\n");
+               return 0;
+       }
+}
+
+#endif /* New enough libc */
index 7f73fa32a590b5f5177affb4b2e6df93dc3e2f2f..bcf5ec760eb9287f5a0b5f99e95518eaed78f6a0 100644 (file)
@@ -42,7 +42,7 @@
 #include <sys/mman.h>
 #include "../../include/uapi/linux/magic.h"
 #include "../../include/uapi/linux/kernel-page-flags.h"
-#include <api/fs/debugfs.h>
+#include <api/fs/fs.h>
 
 #ifndef MAX_PATH
 # define MAX_PATH 256
@@ -188,7 +188,7 @@ static int          kpageflags_fd;
 static int             opt_hwpoison;
 static int             opt_unpoison;
 
-static char            *hwpoison_debug_fs;
+static const char      *hwpoison_debug_fs;
 static int             hwpoison_inject_fd;
 static int             hwpoison_forget_fd;
 
@@ -487,7 +487,7 @@ static void prepare_hwpoison_fd(void)
 {
        char buf[MAX_PATH + 1];
 
-       hwpoison_debug_fs = debugfs_mount(NULL);
+       hwpoison_debug_fs = debugfs__mount();
        if (!hwpoison_debug_fs) {
                perror("mount debugfs");
                exit(EXIT_FAILURE);
index 66c66165e712d743ed3da1a501c03a087f442378..30489181922d28d9749feefb552e7b8f0fa97f52 100644 (file)
@@ -2137,7 +2137,7 @@ static int init_vgic_model(struct kvm *kvm, int type)
        case KVM_DEV_TYPE_ARM_VGIC_V2:
                vgic_v2_init_emulation(kvm);
                break;
-#ifdef CONFIG_ARM_GIC_V3
+#ifdef CONFIG_KVM_ARM_VGIC_V3
        case KVM_DEV_TYPE_ARM_VGIC_V3:
                vgic_v3_init_emulation(kvm);
                break;
@@ -2299,7 +2299,7 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write)
                block_size = KVM_VGIC_V2_CPU_SIZE;
                alignment = SZ_4K;
                break;
-#ifdef CONFIG_ARM_GIC_V3
+#ifdef CONFIG_KVM_ARM_VGIC_V3
        case KVM_VGIC_V3_ADDR_TYPE_DIST:
                type_needed = KVM_DEV_TYPE_ARM_VGIC_V3;
                addr_ptr = &vgic->vgic_dist_base;